Converting printf calls
[viking/gosmore.git] / src / viktrwlayer.c
blob44a4a4c6abe8e3044a402a111411c589750224dc
1 /*
2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #define WAYPOINT_FONT "Sans 8"
24 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
25 /* viktrwlayer.c -- 2200 lines can make a difference in the state of things */
27 #include "viking.h"
28 #include "vikmapslayer.h"
29 #include "viktrwlayer_pixmap.h"
30 #include "viktrwlayer_tpwin.h"
31 #include "viktrwlayer_propwin.h"
32 #include "garminsymbols.h"
33 #include "thumbnails.h"
34 #include "background.h"
35 #include "gpx.h"
36 #include "babel.h"
37 #include "dem.h"
38 #include "dems.h"
41 #include <math.h>
42 #include <string.h>
43 #include <stdlib.h>
44 #include <ctype.h>
46 #define GOOGLE_DIRECTIONS_STRING "(wget -O - \"http://maps.google.com/maps?q=%f,%f to %f,%f&output=js\" 2>/dev/null)"
47 #define VIK_TRW_LAYER_TRACK_GC 13
48 #define VIK_TRW_LAYER_TRACK_GC_RATES 10
49 #define VIK_TRW_LAYER_TRACK_GC_MIN 0
50 #define VIK_TRW_LAYER_TRACK_GC_MAX 11
51 #define VIK_TRW_LAYER_TRACK_GC_BLACK 12
53 #define DRAWMODE_BY_TRACK 0
54 #define DRAWMODE_BY_VELOCITY 1
55 #define DRAWMODE_ALL_BLACK 2
57 #define POINTS 1
58 #define LINES 2
60 /* this is how it knows when you click if you are clicking close to a trackpoint. */
61 #define TRACKPOINT_SIZE_APPROX 5
62 #define WAYPOINT_SIZE_APPROX 5
64 #define MIN_STOP_LENGTH 15
65 #define MAX_STOP_LENGTH 86400
66 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
67 /* this is multiplied by user-inputted value from 1-100. */
68 enum {
69 VIK_TRW_LAYER_SUBLAYER_TRACKS,
70 VIK_TRW_LAYER_SUBLAYER_WAYPOINTS,
71 VIK_TRW_LAYER_SUBLAYER_TRACK,
72 VIK_TRW_LAYER_SUBLAYER_WAYPOINT
75 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
77 struct _VikTrwLayer {
78 VikLayer vl;
79 GHashTable *tracks;
80 GHashTable *tracks_iters;
81 GHashTable *waypoints_iters;
82 GHashTable *waypoints;
83 GtkTreeIter waypoints_iter, tracks_iter;
84 gboolean tracks_visible, waypoints_visible;
85 guint8 drawmode;
86 guint8 drawpoints;
87 guint8 drawelevation;
88 guint8 elevation_factor;
89 guint8 drawstops;
90 guint32 stop_length;
91 guint8 drawlines;
92 guint8 line_thickness;
93 guint8 bg_line_thickness;
95 guint8 wp_symbol;
96 guint8 wp_size;
97 gboolean wp_draw_symbols;
99 gdouble velocity_min, velocity_max;
100 GArray *track_gc;
101 guint16 track_gc_iter;
102 GdkGC *track_bg_gc;
103 GdkGC *waypoint_gc;
104 GdkGC *waypoint_text_gc;
105 GdkGC *waypoint_bg_gc;
106 GdkFont *waypoint_font;
107 VikTrack *current_track;
108 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
110 VikCoordMode coord_mode;
112 /* wp editing tool */
113 VikWaypoint *current_wp;
114 gchar *current_wp_name;
115 gboolean moving_wp;
116 gboolean waypoint_rightclick;
118 /* track editing tool */
119 GList *current_tpl;
120 gchar *current_tp_track_name;
121 VikTrwLayerTpwin *tpwin;
123 /* weird hack for joining tracks */
124 GList *last_tpl;
125 gchar *last_tp_track_name;
127 /* track editing tool -- more specifically, moving tps */
128 gboolean moving_tp;
130 /* magic scissors tool */
131 gboolean magic_scissors_started;
132 VikCoord magic_scissors_coord;
134 gboolean drawlabels;
135 gboolean drawimages;
136 guint8 image_alpha;
137 GQueue *image_cache;
138 guint8 image_size;
139 guint16 image_cache_size;
141 /* for waypoint text */
142 PangoLayout *wplabellayout;
144 gboolean has_verified_thumbnails;
146 GtkMenu *wp_right_click_menu;
148 /* menu */
149 VikStdLayerMenuItem menu_selection;
153 /* A caached waypoint image. */
154 typedef struct {
155 GdkPixbuf *pixbuf;
156 gchar *image; /* filename */
157 } CachedPixbuf;
159 struct DrawingParams {
160 VikViewport *vp;
161 VikTrwLayer *vtl;
162 gdouble xmpp, ympp;
163 guint16 width, height;
164 const VikCoord *center;
165 gint track_gc_iter;
166 gboolean one_zone, lat_lon;
167 gdouble ce1, ce2, cn1, cn2;
170 static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16);
171 static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl);
173 static void trw_layer_delete_item ( gpointer *pass_along );
174 static void trw_layer_copy_item_cb( gpointer *pass_along);
175 static void trw_layer_cut_item_cb( gpointer *pass_along);
177 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
178 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] );
180 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
181 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
183 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 );
184 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
185 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
187 static void goto_coord ( VikLayersPanel *vlp, const VikCoord *coord );
188 static void trw_layer_goto_track_startpoint ( gpointer pass_along[5] );
189 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
190 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
191 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
192 static void trw_layer_download_map_along_track_cb(gpointer pass_along[6]);
193 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
194 static void trw_layer_export ( gpointer layer_and_vlp[2], guint file_type );
195 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
196 static void trw_layer_new_wp ( gpointer lav[2] );
198 /* pop-up items */
199 static void trw_layer_properties_item ( gpointer pass_along[5] );
200 static void trw_layer_goto_waypoint ( gpointer pass_along[5] );
201 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[5] );
203 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[4] );
204 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[4] );
205 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
208 static VikTrwLayer *trw_layer_copy ( VikTrwLayer *vtl, gpointer vp );
209 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len );
210 static VikTrwLayer *trw_layer_unmarshall( gpointer data, gint len, VikViewport *vvp );
212 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp );
213 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id );
215 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
216 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
217 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
218 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
219 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
221 static void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name );
222 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
223 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
224 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
225 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
227 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
228 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
229 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
230 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
231 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
232 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
233 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
234 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
235 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
236 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
237 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
238 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
239 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
240 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
241 static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp);
242 static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
245 static gboolean uppercase_exists_in_hash ( GHashTable *hash, const gchar *str );
247 static void cached_pixbuf_free ( CachedPixbuf *cp );
248 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
249 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp );
251 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
252 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
254 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
256 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
257 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode );
258 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
260 static VikToolInterface trw_layer_tools[] = {
261 { "Create Waypoint", (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
262 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL },
264 { "Create Track", (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
265 (VikToolMouseFunc) tool_new_track_click, NULL, NULL },
267 { "Edit Waypoint", (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
268 (VikToolMouseFunc) tool_edit_waypoint_click,
269 (VikToolMouseFunc) tool_edit_waypoint_move,
270 (VikToolMouseFunc) tool_edit_waypoint_release },
272 { "Edit Trackpoint", (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
273 (VikToolMouseFunc) tool_edit_trackpoint_click,
274 (VikToolMouseFunc) tool_edit_trackpoint_move,
275 (VikToolMouseFunc) tool_edit_trackpoint_release },
277 { "Show Picture", (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
278 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL },
280 { "Magic Scissors", (VikToolConstructorFunc) tool_magic_scissors_create, NULL, NULL, NULL,
281 (VikToolMouseFunc) tool_magic_scissors_click, NULL, NULL },
284 /****** PARAMETERS ******/
286 static gchar *params_groups[] = { "Waypoints", "Tracks", "Waypoint Images" };
287 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
289 static gchar *params_drawmodes[] = { "Draw by Track", "Draw by Velocity", "All Tracks Black", 0 };
290 static gchar *params_wpsymbols[] = { "Filled Square", "Square", "Circle", "X", 0 };
293 static VikLayerParamScale params_scales[] = {
294 /* min max step digits */
295 { 1, 10, 1, 0 }, /* line_thickness */
296 { 0.0, 99.0, 1, 2 }, /* velocity_min */
297 { 1.0, 100.0, 1.0, 2 }, /* velocity_max */
298 /* 5 * step == how much to turn */
299 { 16, 128, 3.2, 0 }, /* image_size */
300 { 0, 255, 5, 0 }, /* image alpha */
301 { 5, 500, 5, 0 }, /* image cache_size */
302 { 0, 8, 1, 0 }, /* image cache_size */
303 { 1, 64, 1, 0 }, /* wpsize */
304 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
305 { 1, 100, 1, 0 }, /* stop_length */
308 VikLayerParam trw_layer_params[] = {
309 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
310 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
312 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, "Track Drawing Mode:", VIK_LAYER_WIDGET_RADIOGROUP, NULL },
313 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, "Draw Track Lines", VIK_LAYER_WIDGET_CHECKBUTTON },
314 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, "Draw Trackpoints", VIK_LAYER_WIDGET_CHECKBUTTON },
315 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, "Draw Elevation", VIK_LAYER_WIDGET_CHECKBUTTON },
316 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, "Draw Elevation Height %:", VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
318 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, "Draw Stops", VIK_LAYER_WIDGET_CHECKBUTTON },
319 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, "Min Stop Length (seconds):", VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
321 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, "Track Thickness:", VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
322 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, "Track BG Thickness:", VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
323 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, "Track Background Color", VIK_LAYER_WIDGET_COLOR, 0 },
324 { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, "Min Track Velocity:", VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
325 { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, "Max Track Velocity:", VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 },
327 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, "Draw Labels", VIK_LAYER_WIDGET_CHECKBUTTON },
328 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, "Waypoint Color:", VIK_LAYER_WIDGET_COLOR, 0 },
329 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, "Waypoint Text:", VIK_LAYER_WIDGET_COLOR, 0 },
330 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, "Background:", VIK_LAYER_WIDGET_COLOR, 0 },
331 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, "Fake BG Color Translucency:", VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
332 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, "Waypoint marker:", VIK_LAYER_WIDGET_RADIOGROUP, NULL },
333 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, "Waypoint size:", VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
334 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, "Draw Waypoint Symbols:", VIK_LAYER_WIDGET_CHECKBUTTON },
336 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, "Draw Waypoint Images", VIK_LAYER_WIDGET_CHECKBUTTON },
337 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, "Image Size (pixels):", VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
338 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, "Image Alpha:", VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
339 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, "Image Memory Cache Size:", VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
342 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 };
344 /*** TO ADD A PARAM:
345 *** 1) Add to trw_layer_params and enumeration
346 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
347 ***/
349 /****** END PARAMETERS ******/
351 VikLayerInterface vik_trw_layer_interface = {
352 "TrackWaypoint",
353 &trwlayer_pixbuf,
355 trw_layer_tools,
356 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
358 trw_layer_params,
359 NUM_PARAMS,
360 params_groups, /* params_groups */
361 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
363 VIK_MENU_ITEM_ALL,
365 (VikLayerFuncCreate) vik_trw_layer_create,
366 (VikLayerFuncRealize) vik_trw_layer_realize,
367 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
368 (VikLayerFuncFree) vik_trw_layer_free,
370 (VikLayerFuncProperties) NULL,
371 (VikLayerFuncDraw) vik_trw_layer_draw,
372 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
374 (VikLayerFuncSetMenuItemsSelection) vik_trw_layer_set_menu_selection,
375 (VikLayerFuncGetMenuItemsSelection) vik_trw_layer_get_menu_selection,
377 (VikLayerFuncAddMenuItems) vik_trw_layer_add_menu_items,
378 (VikLayerFuncSublayerAddMenuItems) vik_trw_layer_sublayer_add_menu_items,
380 (VikLayerFuncSublayerRenameRequest) vik_trw_layer_sublayer_rename_request,
381 (VikLayerFuncSublayerToggleVisible) vik_trw_layer_sublayer_toggle_visible,
383 (VikLayerFuncCopy) trw_layer_copy,
384 (VikLayerFuncMarshall) trw_layer_marshall,
385 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
387 (VikLayerFuncSetParam) trw_layer_set_param,
388 (VikLayerFuncGetParam) trw_layer_get_param,
390 (VikLayerFuncReadFileData) a_gpspoint_read_file,
391 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
393 (VikLayerFuncDeleteItem) trw_layer_del_item,
394 (VikLayerFuncCopyItem) trw_layer_copy_item,
395 (VikLayerFuncPasteItem) trw_layer_paste_item,
396 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
398 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
401 /* for copy & paste (I think?) */
402 typedef struct {
403 guint len;
404 guint8 data[0];
405 // gchar *name;
406 // VikWaypoint *wp;
407 } FlatItem;
409 GType vik_trw_layer_get_type ()
411 static GType vtl_type = 0;
413 if (!vtl_type)
415 static const GTypeInfo vtl_info =
417 sizeof (VikTrwLayerClass),
418 NULL, /* base_init */
419 NULL, /* base_finalize */
420 NULL, /* class init */
421 NULL, /* class_finalize */
422 NULL, /* class_data */
423 sizeof (VikTrwLayer),
425 NULL /* instance init */
427 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
430 return vtl_type;
433 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
435 static gpointer pass_along[5];
436 if (!sublayer) {
437 return;
440 pass_along[0] = vtl;
441 pass_along[1] = NULL;
442 pass_along[2] = (gpointer) subtype;
443 pass_along[3] = sublayer;
444 pass_along[4] = NULL;
446 trw_layer_delete_item ( pass_along );
449 static void trw_layer_copy_item_cb( gpointer pass_along[5])
451 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
452 gint subtype = (gint)pass_along[2];
453 gpointer * sublayer = pass_along[3];
454 guint8 *data = NULL;
455 guint len;
457 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
459 if (data) {
460 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
461 subtype, len, data);
465 static void trw_layer_cut_item_cb( gpointer pass_along[5])
467 trw_layer_copy_item_cb(pass_along);
468 trw_layer_delete_item(pass_along);
471 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
473 FlatItem *fi;
474 guint8 *id;
475 guint il;
477 g_debug("%s:%s() called\n", __FILE__, __PRETTY_FUNCTION__);
478 if (!sublayer) {
479 *item = NULL;
480 return;
483 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
485 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
486 } else {
487 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
490 *len = sizeof(FlatItem) + strlen(sublayer) + 1 + il;
491 fi = g_malloc ( *len );
492 fi->len = strlen(sublayer) + 1;
493 memcpy(fi->data, sublayer, fi->len);
494 memcpy(fi->data + fi->len, id, il);
495 g_free(id);
496 *item = (guint8 *)fi;
499 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
501 FlatItem *fi = (FlatItem *) item;
503 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
505 VikWaypoint *w;
506 gchar *name;
508 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, (gchar *)fi->data);
509 w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
510 vik_trw_layer_add_waypoint ( vtl, name, w );
511 waypoint_convert(name, w, &vtl->coord_mode);
512 return TRUE;
514 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
516 VikTrack *t;
517 gchar *name;
518 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, (gchar *)fi->data);
519 t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
520 vik_trw_layer_add_track ( vtl, name, t );
521 track_convert(name, t, &vtl->coord_mode);
522 return TRUE;
524 return FALSE;
527 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
529 if (item) {
530 g_free(item);
534 static void waypoint_copy ( const gchar *name, VikWaypoint *wp, GHashTable *dest )
536 g_hash_table_insert ( dest, g_strdup(name), vik_waypoint_copy(wp) );
539 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp )
541 switch ( id )
543 case PARAM_TV: vtl->tracks_visible = data.b; break;
544 case PARAM_WV: vtl->waypoints_visible = data.b; break;
545 case PARAM_DM: vtl->drawmode = data.u; break;
546 case PARAM_DP: vtl->drawpoints = data.b; break;
547 case PARAM_DE: vtl->drawelevation = data.b; break;
548 case PARAM_DS: vtl->drawstops = data.b; break;
549 case PARAM_DL: vtl->drawlines = data.b; break;
550 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
551 vtl->stop_length = data.u;
552 break;
553 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
554 vtl->elevation_factor = data.u;
555 break;
556 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
558 vtl->line_thickness = data.u;
559 trw_layer_new_track_gcs ( vtl, vp );
561 break;
562 case PARAM_BLT: if ( data.u > 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
564 vtl->bg_line_thickness = data.u;
565 trw_layer_new_track_gcs ( vtl, vp );
567 break;
568 case PARAM_VMIN: vtl->velocity_min = data.d; break;
569 case PARAM_VMAX: vtl->velocity_max = data.d; break;
570 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
571 case PARAM_DLA: vtl->drawlabels = data.b; break;
572 case PARAM_DI: vtl->drawimages = data.b; break;
573 case PARAM_IS: if ( data.u != vtl->image_size )
575 vtl->image_size = data.u;
576 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
577 g_queue_free ( vtl->image_cache );
578 vtl->image_cache = g_queue_new ();
580 break;
581 case PARAM_IA: vtl->image_alpha = data.u; break;
582 case PARAM_ICS: vtl->image_cache_size = data.u;
583 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
584 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
585 break;
586 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
587 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
588 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
589 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
590 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
591 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
592 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
594 return TRUE;
597 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id )
599 VikLayerParamData rv;
600 switch ( id )
602 case PARAM_TV: rv.b = vtl->tracks_visible; break;
603 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
604 case PARAM_DM: rv.u = vtl->drawmode; break;
605 case PARAM_DP: rv.b = vtl->drawpoints; break;
606 case PARAM_DE: rv.b = vtl->drawelevation; break;
607 case PARAM_EF: rv.u = vtl->elevation_factor; break;
608 case PARAM_DS: rv.b = vtl->drawstops; break;
609 case PARAM_SL: rv.u = vtl->stop_length; break;
610 case PARAM_DL: rv.b = vtl->drawlines; break;
611 case PARAM_LT: rv.u = vtl->line_thickness; break;
612 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
613 case PARAM_VMIN: rv.d = vtl->velocity_min; break;
614 case PARAM_VMAX: rv.d = vtl->velocity_max; break;
615 case PARAM_DLA: rv.b = vtl->drawlabels; break;
616 case PARAM_DI: rv.b = vtl->drawimages; break;
617 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
618 case PARAM_IS: rv.u = vtl->image_size; break;
619 case PARAM_IA: rv.u = vtl->image_alpha; break;
620 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
621 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
622 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
623 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
624 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
625 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
626 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
627 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
629 return rv;
632 static void track_copy ( const gchar *name, VikTrack *tr, GHashTable *dest )
634 g_hash_table_insert ( dest, g_strdup ( name ), vik_track_copy(tr) );
637 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
639 guint8 *pd, *dd;
640 gint pl, dl;
641 gchar *tmpname;
642 FILE *f;
644 *data = NULL;
646 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
647 a_gpx_write_file(vtl, f);
648 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
649 fclose(f);
650 g_file_get_contents(tmpname, (void *)&dd, (void *)&dl, NULL);
651 *len = sizeof(pl) + pl + dl;
652 *data = g_malloc(*len);
653 memcpy(*data, &pl, sizeof(pl));
654 memcpy(*data + sizeof(pl), pd, pl);
655 memcpy(*data + sizeof(pl) + pl, dd, dl);
657 g_free(pd);
658 g_free(dd);
659 remove(tmpname);
660 g_free(tmpname);
664 static VikTrwLayer *trw_layer_unmarshall( gpointer data, gint len, VikViewport *vvp )
666 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
667 guint pl;
668 gchar *tmpname;
669 FILE *f;
672 memcpy(&pl, data, sizeof(pl));
673 data += sizeof(pl);
674 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
675 data += pl;
677 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
678 g_critical("couldn't open temp file\n");
679 exit(1);
681 fwrite(data, len - pl - sizeof(pl), 1, f);
682 rewind(f);
683 a_gpx_read_file(rv, f);
684 fclose(f);
685 remove(tmpname);
686 g_free(tmpname);
687 return rv;
690 static VikTrwLayer *trw_layer_copy ( VikTrwLayer *vtl, gpointer vp )
692 VikTrwLayer *rv = vik_trw_layer_new ( vtl->drawmode );
693 PangoFontDescription *pfd;
694 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
695 pfd = pango_font_description_from_string (WAYPOINT_FONT);
696 pango_layout_set_font_description (rv->wplabellayout, pfd);
697 /* freeing PangoFontDescription, cause it has been copied by prev. call */
698 pango_font_description_free (pfd);
700 rv->tracks_visible = vtl->tracks_visible;
701 rv->waypoints_visible = vtl->waypoints_visible;
702 rv->drawpoints = vtl->drawpoints;
703 rv->drawstops = vtl->drawstops;
704 rv->drawelevation = vtl->drawelevation;
705 rv->elevation_factor = vtl->elevation_factor;
706 rv->drawlines = vtl->drawlines;
707 rv->stop_length = vtl->stop_length;
708 rv->line_thickness = vtl->line_thickness;
709 rv->bg_line_thickness = vtl->bg_line_thickness;
710 rv->velocity_min = vtl->velocity_min;
711 rv->velocity_max = vtl->velocity_max;
712 rv->drawlabels = vtl->drawlabels;
713 rv->drawimages = vtl->drawimages;
714 rv->image_size = vtl->image_size;
715 rv->image_alpha = vtl->image_alpha;
716 rv->image_cache_size = vtl->image_cache_size;
717 rv->has_verified_thumbnails = TRUE;
718 rv->coord_mode = vtl->coord_mode;
719 rv->wp_symbol = vtl->wp_symbol;
720 rv->wp_size = vtl->wp_size;
721 rv->wp_draw_symbols = vtl->wp_draw_symbols;
723 trw_layer_new_track_gcs ( rv, VIK_VIEWPORT(vp) );
725 rv->waypoint_gc = gdk_gc_new ( GTK_WIDGET(vp)->window );
726 gdk_gc_set_line_attributes ( rv->waypoint_gc, 2, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND );
728 rv->waypoint_text_gc = gdk_gc_new ( GTK_WIDGET(vp)->window );
729 rv->waypoint_bg_gc = gdk_gc_new ( GTK_WIDGET(vp)->window );
730 gdk_gc_copy ( rv->waypoint_gc, vtl->waypoint_gc );
731 gdk_gc_copy ( rv->waypoint_text_gc, vtl->waypoint_text_gc );
732 gdk_gc_copy ( rv->waypoint_bg_gc, vtl->waypoint_bg_gc );
734 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
736 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_copy, rv->waypoints );
737 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_copy, rv->tracks );
739 return rv;
742 static GList * a_array_to_glist(gpointer data[])
744 GList *gl = NULL;
745 gpointer * p;
746 for (p = data; *p; p++)
747 gl = g_list_prepend(gl, *p);
748 return(g_list_reverse(gl));
751 VikTrwLayer *vik_trw_layer_new ( gint drawmode )
753 if (trw_layer_params[PARAM_DM].widget_data == NULL)
754 trw_layer_params[PARAM_DM].widget_data = a_array_to_glist(params_drawmodes);
755 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
756 trw_layer_params[PARAM_WPSYM].widget_data = a_array_to_glist(params_wpsymbols);
758 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
759 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
761 rv->waypoints = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_waypoint_free );
762 rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free );
763 rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
764 rv->waypoints_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
766 /* TODO: constants at top */
767 rv->waypoints_visible = rv->tracks_visible = TRUE;
768 rv->drawmode = drawmode;
769 rv->drawpoints = TRUE;
770 rv->drawstops = FALSE;
771 rv->drawelevation = FALSE;
772 rv->elevation_factor = 30;
773 rv->stop_length = 60;
774 rv->drawlines = TRUE;
775 rv->wplabellayout = NULL;
776 rv->wp_right_click_menu = NULL;
777 rv->waypoint_gc = NULL;
778 rv->waypoint_text_gc = NULL;
779 rv->waypoint_bg_gc = NULL;
780 rv->track_gc = NULL;
781 rv->velocity_max = 5.0;
782 rv->velocity_min = 0.0;
783 rv->line_thickness = 1;
784 rv->bg_line_thickness = 0;
785 rv->current_wp = NULL;
786 rv->current_wp_name = NULL;
787 rv->current_track = NULL;
788 rv->current_tpl = NULL;
789 rv->current_tp_track_name = NULL;
790 rv->moving_tp = FALSE;
791 rv->moving_wp = FALSE;
793 rv->magic_scissors_started = FALSE;
795 rv->waypoint_rightclick = FALSE;
796 rv->last_tpl = NULL;
797 rv->last_tp_track_name = NULL;
798 rv->tpwin = NULL;
799 rv->image_cache = g_queue_new();
800 rv->image_size = 64;
801 rv->image_alpha = 255;
802 rv->image_cache_size = 300;
803 rv->drawimages = TRUE;
804 rv->drawlabels = TRUE;
805 return rv;
809 void vik_trw_layer_free ( VikTrwLayer *trwlayer )
811 g_hash_table_destroy(trwlayer->waypoints);
812 g_hash_table_destroy(trwlayer->tracks);
814 /* ODC: replace with GArray */
815 trw_layer_free_track_gcs ( trwlayer );
817 if ( trwlayer->wp_right_click_menu )
818 gtk_object_sink ( GTK_OBJECT(trwlayer->wp_right_click_menu) );
820 if ( trwlayer->wplabellayout != NULL)
821 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
823 if ( trwlayer->waypoint_gc != NULL )
824 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
826 if ( trwlayer->waypoint_text_gc != NULL )
827 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
829 if ( trwlayer->waypoint_bg_gc != NULL )
830 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
832 if ( trwlayer->waypoint_font != NULL )
833 gdk_font_unref ( trwlayer->waypoint_font );
835 if ( trwlayer->tpwin != NULL )
836 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
838 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
839 g_queue_free ( trwlayer->image_cache );
842 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
844 dp->vp = vp;
845 dp->xmpp = vik_viewport_get_xmpp ( vp );
846 dp->ympp = vik_viewport_get_ympp ( vp );
847 dp->width = vik_viewport_get_width ( vp );
848 dp->height = vik_viewport_get_height ( vp );
849 dp->center = vik_viewport_get_center ( vp );
850 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
851 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
853 if ( dp->one_zone )
855 gint w2, h2;
856 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
857 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
858 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
860 dp->ce1 = dp->center->east_west-w2;
861 dp->ce2 = dp->center->east_west+w2;
862 dp->cn1 = dp->center->north_south-h2;
863 dp->cn2 = dp->center->north_south+h2;
864 } else if ( dp->lat_lon ) {
865 VikCoord upperleft, bottomright;
866 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
867 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
868 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
869 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
870 dp->ce1 = upperleft.east_west;
871 dp->ce2 = bottomright.east_west;
872 dp->cn1 = bottomright.north_south;
873 dp->cn2 = upperleft.north_south;
876 dp->track_gc_iter = 0;
879 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
881 static gdouble rv = 0;
882 if ( tp1->has_timestamp && tp2->has_timestamp )
884 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
885 / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
887 if ( rv < 0 )
888 return VIK_TRW_LAYER_TRACK_GC_MIN;
889 else if ( vtl->velocity_min >= vtl->velocity_max )
890 return VIK_TRW_LAYER_TRACK_GC_MAX;
892 rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
894 if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
895 return VIK_TRW_LAYER_TRACK_GC_MAX;
896 return (gint) rv;
898 else
899 return VIK_TRW_LAYER_TRACK_GC_BLACK;
902 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
904 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
905 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
906 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
907 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
910 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
912 /* TODO: this function is a mess, get rid of any redundancy */
913 GList *list = track->trackpoints;
914 gboolean useoldvals = TRUE;
916 gboolean drawpoints;
917 gboolean drawstops;
918 gboolean drawelevation;
919 gdouble min_alt, max_alt, alt_diff = 0;
921 const guint8 tp_size_reg = 2;
922 const guint8 tp_size_cur = 4;
923 guint8 tp_size;
925 if ( dp->vtl->drawelevation )
927 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
928 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
929 alt_diff = max_alt - min_alt;
932 if ( ! track->visible )
933 return;
935 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
936 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
937 trw_layer_draw_track ( name, track, dp, TRUE );
939 if ( drawing_white_background )
940 drawpoints = drawstops = FALSE;
941 else {
942 drawpoints = dp->vtl->drawpoints;
943 drawstops = dp->vtl->drawstops;
946 if (list) {
947 int x, y, oldx, oldy;
948 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
950 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
952 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
954 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
956 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
957 vik_viewport_draw_polygon ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), TRUE, trian, 3 );
960 oldx = x;
961 oldy = y;
963 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
964 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_MAX + 1;
966 while ((list = g_list_next(list)))
968 tp = VIK_TRACKPOINT(list->data);
969 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
971 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
972 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
973 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
974 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
975 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
977 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
979 if ( drawpoints && ! drawing_white_background )
981 if ( list->next ) {
982 vik_viewport_draw_rectangle ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
984 vik_viewport_draw_rectangle ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
986 /* stops */
987 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
988 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 );
990 else
991 vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), TRUE, x-(2*tp_size), y-(2*tp_size), 4*tp_size, 4*tp_size, 0, 360*64 );
994 if ((!tp->newsegment) && (dp->vtl->drawlines))
996 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
998 /* UTM only: zone check */
999 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1000 draw_utm_skip_insignia ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), x, y);
1002 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY )
1003 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1005 if (!useoldvals)
1006 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1008 if ( drawing_white_background ) {
1009 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1011 else {
1013 vik_viewport_draw_line ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), oldx, oldy, x, y);
1014 if ( dp->vtl->drawelevation && list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1015 GdkPoint tmp[4];
1016 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1017 if ( list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1018 tmp[0].x = oldx;
1019 tmp[0].y = oldy;
1020 tmp[1].x = oldx;
1021 tmp[1].y = oldy-FIXALTITUDE(list->data);
1022 tmp[2].x = x;
1023 tmp[2].y = y-FIXALTITUDE(list->next->data);
1024 tmp[3].x = x;
1025 tmp[3].y = y;
1027 GdkGC *tmp_gc;
1028 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1029 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1030 else
1031 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1032 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1034 vik_viewport_draw_line ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1039 oldx = x;
1040 oldy = y;
1041 useoldvals = TRUE;
1043 else {
1044 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1046 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1047 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1049 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1050 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY )
1051 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1053 if ( drawing_white_background )
1054 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1055 else
1056 vik_viewport_draw_line ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), oldx, oldy, x, y);
1058 else
1060 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1061 draw_utm_skip_insignia ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), x, y );
1064 useoldvals = FALSE;
1068 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1069 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
1070 dp->track_gc_iter = 0;
1073 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1074 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1076 trw_layer_draw_track ( name, track, dp, FALSE );
1079 static void cached_pixbuf_free ( CachedPixbuf *cp )
1081 g_object_unref ( G_OBJECT(cp->pixbuf) );
1082 g_free ( cp->image );
1085 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1087 return strcmp ( cp->image, name );
1090 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1092 if ( wp->visible )
1093 if ( (!dp->one_zone) || ( wp->coord.utm_zone == dp->center->utm_zone &&
1094 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1095 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1097 gint x, y;
1098 GdkPixbuf *sym;
1099 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1101 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1103 if ( wp->image && dp->vtl->drawimages )
1105 GdkPixbuf *pixbuf = NULL;
1106 GList *l;
1108 if ( dp->vtl->image_alpha == 0)
1109 return;
1111 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1112 if ( l )
1113 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1114 else
1116 gchar *image = wp->image;
1117 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1118 if ( ! regularthumb )
1120 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1121 image = "\x12\x00"; /* this shouldn't occur naturally. */
1123 if ( regularthumb )
1125 CachedPixbuf *cp = NULL;
1126 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1127 if ( dp->vtl->image_size == 128 )
1128 cp->pixbuf = regularthumb;
1129 else
1131 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1132 g_assert ( cp->pixbuf );
1133 g_object_unref ( G_OBJECT(regularthumb) );
1135 cp->image = g_strdup ( image );
1137 /* needed so 'click picture' tool knows how big the pic is; we don't
1138 * store it in cp because they may have been freed already. */
1139 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1140 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1142 g_queue_push_head ( dp->vtl->image_cache, cp );
1143 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1144 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1146 pixbuf = cp->pixbuf;
1148 else
1150 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1153 if ( pixbuf )
1155 gint w, h;
1156 w = gdk_pixbuf_get_width ( pixbuf );
1157 h = gdk_pixbuf_get_height ( pixbuf );
1159 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1161 if ( dp->vtl->image_alpha == 255 )
1162 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1163 else
1164 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1166 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1170 /* DRAW ACTUAL DOT */
1171 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1172 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 );
1174 else if ( wp == dp->vtl->current_wp ) {
1175 switch ( dp->vtl->wp_symbol ) {
1176 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;
1177 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;
1178 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;
1179 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 );
1180 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 );
1183 else {
1184 switch ( dp->vtl->wp_symbol ) {
1185 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;
1186 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;
1187 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;
1188 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 );
1189 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;
1193 if ( dp->vtl->drawlabels )
1195 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1196 gint width, height;
1197 pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 );
1198 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1199 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, x + dp->vtl->wp_size - 1, y-1,width+1,height-1);
1200 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, x + dp->vtl->wp_size, y, dp->vtl->wplabellayout );
1205 void vik_trw_layer_draw ( VikTrwLayer *l, gpointer data )
1207 static struct DrawingParams dp;
1208 g_assert ( l != NULL );
1210 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1211 dp.vtl = l;
1213 if ( l->tracks_visible )
1214 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1216 if (l->waypoints_visible)
1217 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1220 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1222 int i;
1223 if ( vtl->track_bg_gc )
1225 g_object_unref ( vtl->track_bg_gc );
1226 vtl->track_bg_gc = NULL;
1229 if ( ! vtl->track_gc )
1230 return;
1231 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1232 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1233 g_array_free ( vtl->track_gc, TRUE );
1234 vtl->track_gc = NULL;
1237 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1239 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1240 gint width = vtl->line_thickness;
1242 if ( vtl->track_gc )
1243 trw_layer_free_track_gcs ( vtl );
1245 if ( vtl->track_bg_gc )
1246 g_object_unref ( vtl->track_bg_gc );
1247 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1249 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1251 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1253 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1254 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1255 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1256 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1257 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1258 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1259 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1260 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1261 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1262 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1264 gc[11] = vik_viewport_new_gc ( vp, "#ff0000", width ); /* above range */
1266 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1268 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1271 VikTrwLayer *vik_trw_layer_create ( VikViewport *vp )
1273 VikTrwLayer *rv = vik_trw_layer_new ( 0 );
1274 PangoFontDescription *pfd;
1275 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1276 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1277 pango_layout_set_font_description (rv->wplabellayout, pfd);
1278 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1279 pango_font_description_free (pfd);
1281 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1283 trw_layer_new_track_gcs ( rv, vp );
1285 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1286 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1287 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1288 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1290 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1292 rv->has_verified_thumbnails = FALSE;
1293 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1294 rv->wp_size = 4;
1295 rv->wp_draw_symbols = TRUE;
1297 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1299 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1301 return rv;
1304 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[4] )
1306 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1308 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1309 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, (gint) pass_along[4], NULL, TRUE, TRUE );
1310 #else
1311 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, (gint) pass_along[4], NULL, TRUE, TRUE );
1312 #endif
1314 *new_iter = *((GtkTreeIter *) pass_along[1]);
1315 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1317 if ( ! track->visible )
1318 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1321 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[4] )
1323 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1324 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1325 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, (gint) pass_along[4], NULL, TRUE, TRUE );
1326 #else
1327 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, (gint) pass_along[4], NULL, TRUE, TRUE );
1328 #endif
1330 *new_iter = *((GtkTreeIter *) pass_along[1]);
1331 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1333 if ( ! wp->visible )
1334 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1338 void vik_trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1340 GtkTreeIter iter2;
1341 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1343 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1344 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), "Tracks", vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1345 #else
1346 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), "Tracks", vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1347 #endif
1348 if ( ! vtl->tracks_visible )
1349 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1351 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1353 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1354 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), "Waypoints", vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1355 #else
1356 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), "Waypoints", vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1357 #endif
1359 if ( ! vtl->waypoints_visible )
1360 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1362 pass_along[0] = &(vtl->waypoints_iter);
1363 pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1365 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1369 gboolean vik_trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1371 switch ( subtype )
1373 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1374 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1375 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1377 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1378 if (t)
1379 return (t->visible ^= 1);
1380 else
1381 return TRUE;
1383 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1385 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1386 if (t)
1387 return (t->visible ^= 1);
1388 else
1389 return TRUE;
1392 return TRUE;
1395 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
1397 return l->tracks;
1400 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
1402 return l->waypoints;
1405 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
1407 static VikCoord fixme;
1408 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
1409 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1410 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1411 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1412 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1413 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1414 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1415 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1416 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1419 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
1421 GList *tr = *t;
1422 static VikCoord fixme;
1424 while ( tr )
1426 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
1427 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1428 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1429 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1430 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1431 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1432 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1433 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1434 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1435 tr = tr->next;
1440 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
1442 /* 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... */
1443 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1444 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
1445 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
1446 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1447 return FALSE;
1448 else
1450 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1451 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
1452 return TRUE;
1456 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
1458 VikCoord coord;
1459 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
1460 goto_coord ( VIK_LAYERS_PANEL(layer_and_vlp[1]), &coord );
1461 else
1462 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), "This layer has no waypoints or trackpoints." );
1465 static void trw_layer_export ( gpointer layer_and_vlp[2], guint file_type )
1467 GtkWidget *file_selector;
1468 const gchar *fn;
1469 gboolean failed = FALSE;
1470 file_selector = gtk_file_selection_new ("Export Layer");
1472 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_OK )
1474 fn = gtk_file_selection_get_filename (GTK_FILE_SELECTION(file_selector) );
1475 if ( access ( fn, F_OK ) != 0 )
1477 gtk_widget_hide ( file_selector );
1478 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type );
1479 break;
1481 else
1483 if ( a_dialog_overwrite ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), "The file \"%s\" exists, do you wish to overwrite it?", a_file_basename ( fn ) ) )
1485 gtk_widget_hide ( file_selector );
1486 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type );
1487 break;
1491 gtk_widget_destroy ( file_selector );
1492 if ( failed )
1493 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), "The filename you requested could not be opened for writing." );
1496 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
1498 trw_layer_export ( layer_and_vlp, FILE_TYPE_GPSPOINT );
1501 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
1503 trw_layer_export ( layer_and_vlp, FILE_TYPE_GPSMAPPER );
1506 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
1508 trw_layer_export ( layer_and_vlp, FILE_TYPE_GPX );
1511 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
1513 GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
1514 GtkWidget *dia = gtk_dialog_new_with_buttons ("Create",
1515 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
1516 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1517 GTK_STOCK_CANCEL,
1518 GTK_RESPONSE_REJECT,
1519 GTK_STOCK_OK,
1520 GTK_RESPONSE_ACCEPT,
1521 NULL);
1523 GtkWidget *label, *entry;
1524 label = gtk_label_new("Waypoint Name:");
1525 entry = gtk_entry_new();
1527 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
1528 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
1529 gtk_widget_show_all ( label );
1530 gtk_widget_show_all ( entry );
1532 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
1534 VikWaypoint *wp;
1535 gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
1536 int i;
1538 for ( i = strlen(upname)-1; i >= 0; i-- )
1539 upname[i] = toupper(upname[i]);
1541 wp = g_hash_table_lookup ( wps, upname );
1543 if (!wp)
1544 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), "Waypoint not found in this layer." );
1545 else
1547 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
1548 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
1549 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 ) );
1550 break;
1553 g_free ( upname );
1556 gtk_widget_destroy ( dia );
1559 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
1561 gchar *name;
1562 VikWaypoint *wp = vik_waypoint_new();
1563 wp->coord = *def_coord;
1564 wp->altitude = VIK_DEFAULT_ALTITUDE;
1566 if ( a_dialog_new_waypoint ( w, &name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode ) )
1568 wp->visible = TRUE;
1569 vik_trw_layer_add_waypoint ( vtl, name, wp );
1570 return TRUE;
1572 vik_waypoint_free(wp);
1573 return FALSE;
1576 static void trw_layer_new_wp ( gpointer lav[2] )
1578 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
1579 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
1580 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
1581 instead return true if you want to update. */
1582 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 )
1583 vik_layers_panel_emit_update ( vlp );
1586 void vik_trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
1588 static gpointer pass_along[2];
1589 GtkWidget *item;
1590 pass_along[0] = vtl;
1591 pass_along[1] = vlp;
1593 item = gtk_menu_item_new();
1594 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1595 gtk_widget_show ( item );
1597 item = gtk_menu_item_new_with_label ( "Goto Center of Layer" );
1598 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
1599 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1600 gtk_widget_show ( item );
1602 item = gtk_menu_item_new_with_label ( "Goto Waypoint" );
1603 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
1604 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1605 gtk_widget_show ( item );
1607 item = gtk_menu_item_new_with_label ( "Export Layer as GPSPoint" );
1608 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
1609 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1610 gtk_widget_show ( item );
1612 item = gtk_menu_item_new_with_label ( "Export Layer as GPSMapper" );
1613 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
1614 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1615 gtk_widget_show ( item );
1617 item = gtk_menu_item_new_with_label ( "Export Layer as GPX" );
1618 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
1619 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1620 gtk_widget_show ( item );
1622 item = gtk_menu_item_new_with_label ( "New Waypoint" );
1623 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
1624 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1625 gtk_widget_show ( item );
1628 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
1630 if ( VIK_LAYER(vtl)->realized )
1632 VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
1633 if ( oldwp )
1634 wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
1635 else
1637 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
1638 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1639 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
1640 #else
1641 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
1642 #endif
1643 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
1644 g_hash_table_insert ( vtl->waypoints_iters, name, iter );
1645 wp->visible = TRUE;
1648 else
1649 wp->visible = TRUE;
1651 g_hash_table_insert ( vtl->waypoints, name, wp );
1655 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
1657 if ( VIK_LAYER(vtl)->realized )
1659 VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
1660 if ( oldt )
1661 t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
1662 else
1664 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
1665 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1666 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
1667 #else
1668 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
1669 #endif
1670 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
1671 g_hash_table_insert ( vtl->tracks_iters, name, iter );
1672 /* t->visible = TRUE; */
1675 else
1676 ; /* t->visible = TRUE; // this is now used by file input functions */
1678 g_hash_table_insert ( vtl->tracks, name, t );
1682 static gboolean uppercase_exists_in_hash ( GHashTable *hash, const gchar *str )
1684 gchar *upp = g_strdup ( str );
1685 gboolean rv;
1686 char *tmp = upp;
1687 while ( *tmp )
1689 *tmp = toupper(*tmp);
1690 tmp++;
1692 rv = g_hash_table_lookup ( hash, upp ) ? TRUE : FALSE;
1693 g_free (upp);
1694 return rv;
1697 /* to be called whenever a track has been deleted or may have been changed. */
1698 static void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
1700 if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
1701 trw_layer_cancel_current_tp ( vtl, FALSE );
1702 else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
1703 trw_layer_cancel_last_tp ( vtl );
1706 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
1708 gint i = 2;
1709 gchar *newname = g_strdup(name);
1710 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
1711 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
1712 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
1713 g_free(newname);
1714 newname = new_newname;
1715 i++;
1717 return newname;
1720 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
1722 vik_trw_layer_add_waypoint ( vtl,
1723 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
1724 wp );
1726 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
1728 vik_trw_layer_add_track ( vtl,
1729 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name),
1730 tr );
1733 static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
1735 *l = g_list_append(*l, (gpointer)name);
1738 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
1740 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
1741 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
1742 VikTrack *t;
1743 t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
1744 vik_trw_layer_delete_track(vtl_src, name);
1745 vik_trw_layer_add_track(vtl_dest, newname, t);
1747 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
1748 VikWaypoint *w;
1749 w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
1750 vik_trw_layer_delete_waypoint(vtl_src, name);
1751 vik_trw_layer_add_waypoint(vtl_dest, newname, w);
1755 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
1757 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
1758 gint type = vik_treeview_item_get_data(vt, src_item_iter);
1760 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
1761 GList *items = NULL;
1762 GList *iter;
1764 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
1765 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
1767 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
1768 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
1771 iter = items;
1772 while (iter) {
1773 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
1774 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
1775 } else {
1776 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1778 iter = iter->next;
1780 if (items)
1781 g_list_free(items);
1782 } else {
1783 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
1784 trw_layer_move_item(vtl_src, vtl_dest, name, type);
1789 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
1791 VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
1792 gboolean was_visible = FALSE;
1793 if ( t )
1795 GtkTreeIter *it;
1796 was_visible = t->visible;
1797 if ( t == vtl->current_track )
1798 vtl->current_track = NULL;
1800 /* could be current_tp, so we have to check */
1801 trw_layer_cancel_tps_of_track ( vtl, trk_name );
1803 g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
1804 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
1805 g_hash_table_remove ( vtl->tracks_iters, trk_name );
1807 /* do this last because trk_name may be pointing to actual orig key */
1808 g_hash_table_remove ( vtl->tracks, trk_name );
1810 return was_visible;
1813 gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
1815 gboolean was_visible = FALSE;
1816 VikWaypoint *wp;
1818 wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
1819 if ( wp ) {
1820 GtkTreeIter *it;
1822 if ( wp == vtl->current_wp ) {
1823 vtl->current_wp = NULL;
1824 vtl->current_wp_name = NULL;
1825 vtl->moving_wp = FALSE;
1828 was_visible = wp->visible;
1829 g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
1830 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
1831 g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
1832 g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
1835 return was_visible;
1838 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
1840 vik_treeview_item_delete (vt, it );
1843 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
1846 vtl->current_track = NULL;
1847 if (vtl->current_tp_track_name)
1848 trw_layer_cancel_current_tp(vtl, FALSE);
1849 if (vtl->last_tp_track_name)
1850 trw_layer_cancel_last_tp ( vtl );
1852 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
1853 g_hash_table_remove_all(vtl->tracks_iters);
1854 g_hash_table_remove_all(vtl->tracks);
1856 /* TODO: only update if the layer is visible (ticked) */
1857 vik_layer_emit_update ( VIK_LAYER(vtl) );
1860 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
1862 vtl->current_wp = NULL;
1863 vtl->current_wp_name = NULL;
1864 vtl->moving_wp = FALSE;
1866 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
1867 g_hash_table_remove_all(vtl->waypoints_iters);
1868 g_hash_table_remove_all(vtl->waypoints);
1870 /* TODO: only update if the layer is visible (ticked) */
1871 vik_layer_emit_update ( VIK_LAYER(vtl) );
1874 static void trw_layer_delete_item ( gpointer pass_along[5] )
1876 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
1877 gboolean was_visible = FALSE;
1878 if ( (gint) pass_along[2] == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
1880 was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
1882 else
1884 was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
1886 if ( was_visible )
1887 vik_layer_emit_update ( VIK_LAYER(vtl) );
1891 static void trw_layer_properties_item ( gpointer pass_along[5] )
1893 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
1894 if ( (gint) pass_along[2] == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
1896 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
1897 if ( wp )
1899 if ( a_dialog_new_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), NULL, wp, NULL, vtl->coord_mode ) )
1901 if ( VIK_LAYER(vtl)->visible )
1902 vik_layer_emit_update ( VIK_LAYER(vtl) );
1905 else
1907 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
1908 if ( tr )
1910 gint resp = vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl), tr, pass_along[1] /* vlp */ );
1911 if ( resp == VIK_TRW_LAYER_PROPWIN_DEL_DUP )
1913 vik_track_remove_dup_points(tr);
1914 /* above operation could have deleted current_tp or last_tp */
1915 trw_layer_cancel_tps_of_track ( vtl, (gchar *) pass_along[3] );
1916 vik_layer_emit_update ( VIK_LAYER(vtl) );
1918 if ( resp == VIK_TRW_LAYER_PROPWIN_REVERSE )
1920 vik_track_reverse(tr);
1921 vik_layer_emit_update ( VIK_LAYER(vtl) );
1923 else if ( resp == VIK_TRW_LAYER_PROPWIN_SPLIT )
1925 /* get new tracks, add them, resolve naming conflicts (free if cancel), and delete old. old can still exist on clipboard. */
1926 guint ntracks;
1927 VikTrack **tracks = vik_track_split_into_segments(tr, &ntracks);
1928 gchar *new_tr_name;
1929 guint i;
1930 for ( i = 0; i < ntracks; i++ )
1932 g_assert ( tracks[i] );
1933 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i+1);
1934 /* if ( (wp_exists) && (! overwrite) ) */
1935 /* don't need to upper case new_tr_name because old tr name was uppercase */
1936 if ( g_hash_table_lookup ( vtl->tracks, new_tr_name ) &&
1937 ( ! a_dialog_overwrite ( VIK_GTK_WINDOW_FROM_LAYER(vtl), "The track \"%s\" exists, do you wish to overwrite it?", new_tr_name ) ) )
1939 gchar *new_new_tr_name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks );
1940 g_free ( new_tr_name );
1941 if (new_new_tr_name)
1942 new_tr_name = new_new_tr_name;
1943 else
1945 new_tr_name = NULL;
1946 vik_track_free ( tracks[i] );
1949 if ( new_tr_name )
1950 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
1952 if ( tracks )
1954 g_free ( tracks );
1955 vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
1956 vik_layer_emit_update ( VIK_LAYER(vtl) ); /* chase thru the hoops */
1963 static void goto_coord ( VikLayersPanel *vlp, const VikCoord *coord )
1965 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), coord );
1966 vik_layers_panel_emit_update ( vlp );
1969 static void trw_layer_goto_track_startpoint ( gpointer pass_along[5] )
1971 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
1972 if ( trps && trps->data )
1973 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord));
1976 static void trw_layer_goto_track_center ( gpointer pass_along[5] )
1978 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
1979 if ( trps && *trps )
1981 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1982 VikCoord coord;
1983 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
1984 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1985 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1986 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
1987 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &coord);
1991 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
1993 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
1994 /* Also warn if overwrite old elevation data */
1995 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
1997 vik_track_apply_dem_data ( track );
2001 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
2003 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2004 if ( !trps )
2005 return;
2006 trps = g_list_last(trps);
2007 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord));
2011 /*************************************
2012 * merge/split by time routines
2013 *************************************/
2015 /* called for each key in track hash table. if original track user_data[1] is close enough
2016 * to the passed one, add it to list in user_data[0]
2018 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
2020 time_t t1, t2;
2021 VikTrackpoint *p1, *p2;
2023 GList **nearby_tracks = ((gpointer *)user_data)[0];
2024 GList *orig_track = ((gpointer *)user_data)[1];
2025 guint thr = (guint)((gpointer *)user_data)[2];
2027 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
2028 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
2030 if (VIK_TRACK(value)->trackpoints == orig_track) {
2031 return;
2034 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2035 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2037 if (!p1->has_timestamp || !p2->has_timestamp) {
2038 g_print("no timestamp\n");
2039 return;
2042 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
2043 if (abs(t1 - p2->timestamp) < thr*60 ||
2044 /* p1 p2 t1 t2 */
2045 abs(p1->timestamp - t2) < thr*60
2046 /* t1 t2 p1 p2 */
2048 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
2052 /* comparison function used to sort tracks; a and b are hash table keys */
2053 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
2055 GHashTable *tracks = user_data;
2056 time_t t1, t2;
2058 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
2059 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
2061 if (t1 < t2) return -1;
2062 if (t1 > t2) return 1;
2063 return 0;
2066 /* comparison function used to sort trackpoints */
2067 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
2069 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
2071 if (t1 < t2) return -1;
2072 if (t1 > t2) return 1;
2073 return 0;
2076 /* merge by time routine */
2077 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
2079 time_t t1, t2;
2080 GList *nearby_tracks = NULL;
2081 VikTrack *track;
2082 GList *trps;
2083 static guint thr = 1;
2084 guint track_count = 0;
2085 gchar *orig_track_name = strdup(pass_along[3]);
2087 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
2088 "Merge Threshold...",
2089 "Merge when time between trackpoints less than:",
2090 &thr)) {
2091 return;
2094 /* merge tracks until we can't */
2095 do {
2096 gpointer params[3];
2098 track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, orig_track_name );
2099 trps = track->trackpoints;
2100 if ( !trps )
2101 return;
2104 if (nearby_tracks) {
2105 g_list_free(nearby_tracks);
2106 nearby_tracks = NULL;
2109 t1 = ((VikTrackpoint *)trps->data)->timestamp;
2110 t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
2112 /* g_print("Original track times: %d and %d\n", t1, t2); */
2113 params[0] = &nearby_tracks;
2114 params[1] = trps;
2115 params[2] = (gpointer)thr;
2117 /* get a list of adjacent-in-time tracks */
2118 g_hash_table_foreach(VIK_TRW_LAYER(pass_along[0])->tracks, find_nearby_track, (gpointer)params);
2120 /* add original track */
2121 nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
2123 /* sort by first trackpoint; assumes they don't overlap */
2124 nearby_tracks = g_list_sort_with_data(nearby_tracks, track_compare, VIK_TRW_LAYER(pass_along[0])->tracks);
2126 /* merge them */
2128 #define get_track(x) VIK_TRACK(g_hash_table_lookup(VIK_TRW_LAYER(pass_along[0])->tracks, (gchar *)((x)->data)))
2129 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
2130 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
2131 GList *l = nearby_tracks;
2132 VikTrack *tr = vik_track_new();
2133 tr->visible = track->visible;
2134 track_count = 0;
2135 while (l) {
2137 time_t t1, t2;
2138 t1 = get_first_trackpoint(l)->timestamp;
2139 t2 = get_last_trackpoint(l)->timestamp;
2140 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
2144 /* remove trackpoints from merged track, delete track */
2145 tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
2146 get_track(l)->trackpoints = NULL;
2147 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), l->data);
2149 track_count ++;
2150 l = g_list_next(l);
2152 tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
2153 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), strdup(orig_track_name), tr);
2155 #undef get_first_trackpoint
2156 #undef get_last_trackpoint
2157 #undef get_track
2159 } while (track_count > 1);
2160 g_list_free(nearby_tracks);
2161 free(orig_track_name);
2162 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
2165 /* split by time routine */
2166 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
2168 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2169 GList *trps = track->trackpoints;
2170 GList *iter;
2171 GList *newlists = NULL;
2172 GList *newtps = NULL;
2173 guint i;
2174 static guint thr = 1;
2176 time_t ts, prev_ts;
2178 if ( !trps )
2179 return;
2181 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
2182 "Split Threshold...",
2183 "Split when time between trackpoints exceeds:",
2184 &thr)) {
2185 return;
2188 /* iterate through trackpoints, and copy them into new lists without touching original list */
2189 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
2190 iter = trps;
2192 while (iter) {
2193 ts = VIK_TRACKPOINT(iter->data)->timestamp;
2194 if (ts < prev_ts) {
2195 g_print("panic: ts < prev_ts: this should never happen!\n");
2196 return;
2198 if (ts - prev_ts > thr*60) {
2199 /* flush accumulated trackpoints into new list */
2200 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
2201 newtps = NULL;
2204 /* accumulate trackpoint copies in newtps, in reverse order */
2205 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
2206 prev_ts = ts;
2207 iter = g_list_next(iter);
2209 if (newtps) {
2210 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
2213 /* put lists of trackpoints into tracks */
2214 iter = newlists;
2215 i = 1;
2216 while (iter) {
2217 gchar *new_tr_name;
2218 VikTrack *tr;
2220 tr = vik_track_new();
2221 tr->visible = track->visible;
2222 tr->trackpoints = (GList *)(iter->data);
2224 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
2225 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
2226 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
2227 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
2229 iter = g_list_next(iter);
2231 g_list_free(newlists);
2232 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
2233 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
2236 /* end of split/merge routines */
2239 static void trw_layer_goto_waypoint ( gpointer pass_along[5] )
2241 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2242 if ( wp )
2243 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(wp->coord) );
2246 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[5] )
2248 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
2249 #ifdef WINDOWS
2250 ShellExecute(NULL, NULL, (char *) webpage, NULL, ".\\", 0);
2251 #else /* WINDOWS */
2252 GError *err = NULL;
2253 gchar *cmd = g_strdup_printf ( "%s %s", UNIX_WEB_BROWSER, webpage );
2254 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2256 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), "Could not launch web browser." );
2257 g_error_free ( err );
2259 g_free ( cmd );
2260 #endif /* WINDOWS */
2261 g_free ( webpage );
2264 const gchar *vik_trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
2266 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2268 int i;
2269 gchar *rv;
2270 VikWaypoint *wp;
2272 if ( strcasecmp ( newname, sublayer ) == 0 )
2273 return NULL;
2275 if ( uppercase_exists_in_hash ( l->waypoints, newname ) )
2277 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), "Waypoint Already Exists" );
2278 return NULL;
2281 iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
2282 g_hash_table_steal ( l->waypoints_iters, sublayer );
2284 wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
2285 g_hash_table_remove ( l->waypoints, sublayer );
2287 rv = g_strdup(newname);
2288 for ( i = strlen(rv) - 1; i >= 0; i-- )
2289 rv[i] = toupper(rv[i]);
2291 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
2293 g_hash_table_insert ( l->waypoints, rv, wp );
2294 g_hash_table_insert ( l->waypoints_iters, rv, iter );
2296 /* it hasn't been updated yet so we pass new name */
2297 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2298 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
2299 #endif
2301 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2302 return rv;
2304 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2306 int i;
2307 gchar *rv;
2308 VikTrack *tr;
2309 GtkTreeIter *iter;
2310 gchar *orig_key;
2312 if ( strcasecmp ( newname, sublayer ) == 0 )
2313 return NULL;
2315 if ( uppercase_exists_in_hash ( l->tracks, newname ) )
2317 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), "Track Already Exists" );
2318 return NULL;
2321 g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
2322 g_hash_table_steal ( l->tracks, sublayer );
2324 iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
2325 g_hash_table_steal ( l->tracks_iters, sublayer );
2327 rv = g_strdup(newname);
2328 for ( i = strlen(rv) - 1; i >= 0; i-- )
2329 rv[i] = toupper(rv[i]);
2331 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
2333 g_hash_table_insert ( l->tracks, rv, tr );
2334 g_hash_table_insert ( l->tracks_iters, rv, iter );
2336 /* don't forget about current_tp_track_name, update that too */
2337 if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
2339 l->current_tp_track_name = rv;
2340 if ( l->tpwin )
2341 vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
2343 else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
2344 l->last_tp_track_name = rv;
2346 g_free ( orig_key );
2348 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2349 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
2350 #endif
2352 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2353 return rv;
2355 return NULL;
2358 static gboolean is_valid_geocache_name ( gchar *str )
2360 gint len = strlen ( str );
2361 return len >= 3 && len <= 6 && str[0] == 'G' && str[1] == 'C' && isalnum(str[2]) && (len < 4 || isalnum(str[3])) && (len < 5 || isalnum(str[4])) && (len < 6 || isalnum(str[5]));
2364 /* vlp can be NULL if necessary - i.e. right-click from a tool -- but be careful, some functions may try to use it */
2365 gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
2367 static GtkTreeIter staticiter;
2368 static gpointer pass_along[5];
2369 GtkWidget *item;
2370 gboolean rv = FALSE;
2372 pass_along[0] = l;
2373 pass_along[1] = vlp;
2374 pass_along[2] = (gpointer) subtype;
2375 pass_along[3] = sublayer;
2376 staticiter = *iter; /* will exist after function has ended */
2377 pass_along[4] = &staticiter;
2379 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2381 rv = TRUE;
2383 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
2384 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
2385 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2386 gtk_widget_show ( item );
2388 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
2389 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
2390 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2391 gtk_widget_show ( item );
2393 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
2394 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
2395 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2396 gtk_widget_show ( item );
2398 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
2399 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
2400 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2401 gtk_widget_show ( item );
2403 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2405 /* could be a right-click using the tool */
2406 if ( vlp != NULL ) {
2407 item = gtk_menu_item_new_with_label ( "Goto" );
2408 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
2409 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2410 gtk_widget_show ( item );
2413 if ( is_valid_geocache_name ( (gchar *) sublayer ) )
2415 item = gtk_menu_item_new_with_label ( "Visit Geocache Webpage" );
2416 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
2417 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2418 gtk_widget_show ( item );
2424 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2426 item = gtk_menu_item_new ();
2427 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2428 gtk_widget_show ( item );
2430 item = gtk_menu_item_new_with_label ( "Goto Startpoint" );
2431 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
2432 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2433 gtk_widget_show ( item );
2435 item = gtk_menu_item_new_with_label ( "Goto \"Center\"" );
2436 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
2437 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2438 gtk_widget_show ( item );
2440 item = gtk_menu_item_new_with_label ( "Goto Endpoint" );
2441 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
2442 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2443 gtk_widget_show ( item );
2445 item = gtk_menu_item_new_with_label ( "Merge By Time" );
2446 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
2447 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2448 gtk_widget_show ( item );
2450 item = gtk_menu_item_new_with_label ( "Split By Time" );
2451 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
2452 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2453 gtk_widget_show ( item );
2455 item = gtk_menu_item_new_with_label ( "Download maps along track..." );
2456 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
2457 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2458 gtk_widget_show ( item );
2460 item = gtk_menu_item_new_with_label ( "Apply DEM Data" );
2461 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
2462 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2463 gtk_widget_show ( item );
2466 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
2468 item = gtk_menu_item_new ();
2469 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2470 gtk_widget_show ( item );
2472 item = gtk_menu_item_new_with_label ( "New Waypoint" );
2473 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2474 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2475 gtk_widget_show ( item );
2478 return rv;
2482 /* to be called when last_tpl no long exists. */
2483 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
2485 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
2486 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
2487 vtl->last_tpl = NULL;
2488 vtl->last_tp_track_name = NULL;
2491 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
2493 if ( vtl->tpwin )
2495 if ( destroy)
2497 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
2498 vtl->tpwin = NULL;
2500 else
2501 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
2503 if ( vtl->current_tpl )
2505 vtl->current_tpl = NULL;
2506 vtl->current_tp_track_name = NULL;
2507 vik_layer_emit_update(VIK_LAYER(vtl));
2511 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
2513 g_assert ( vtl->tpwin != NULL );
2514 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
2515 trw_layer_cancel_current_tp ( vtl, TRUE );
2516 else if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
2518 gchar *name;
2519 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks ) ) )
2521 VikTrack *tr = vik_track_new ();
2522 GList *newglist = g_list_alloc ();
2523 newglist->prev = NULL;
2524 newglist->next = vtl->current_tpl->next;
2525 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
2526 tr->trackpoints = newglist;
2528 vtl->current_tpl->next->prev = newglist; /* end old track here */
2529 vtl->current_tpl->next = NULL;
2531 vtl->current_tpl = newglist; /* change tp to first of new track. */
2532 vtl->current_tp_track_name = name;
2534 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
2536 vik_trw_layer_add_track ( vtl, name, tr );
2537 vik_layer_emit_update(VIK_LAYER(vtl));
2540 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
2542 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
2543 GList *new_tpl;
2544 g_assert(tr != NULL);
2546 /* can't join with a non-existent trackpoint */
2547 vtl->last_tpl = NULL;
2548 vtl->last_tp_track_name = NULL;
2550 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
2552 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
2553 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
2555 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
2557 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
2558 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
2560 trw_layer_cancel_last_tp ( vtl );
2562 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
2563 g_list_free_1 ( vtl->current_tpl );
2564 vtl->current_tpl = new_tpl;
2565 vik_layer_emit_update(VIK_LAYER(vtl));
2567 else
2569 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
2570 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
2571 g_list_free_1 ( vtl->current_tpl );
2572 trw_layer_cancel_current_tp ( vtl, FALSE );
2575 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
2577 vtl->last_tpl = vtl->current_tpl;
2578 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
2579 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
2581 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
2583 vtl->last_tpl = vtl->current_tpl;
2584 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
2585 vik_layer_emit_update(VIK_LAYER(vtl));
2587 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
2589 VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
2590 VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
2592 VikTrack *tr_first = tr1, *tr_last = tr2;
2594 gchar *tmp;
2596 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
2597 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
2598 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
2599 vik_track_reverse ( tr1 );
2600 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
2602 tr_first = tr2;
2603 tr_last = tr1;
2605 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
2607 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. */
2608 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
2609 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
2610 tr2->trackpoints = NULL;
2612 tmp = vtl->current_tp_track_name;
2614 vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
2615 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
2617 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
2618 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
2619 vik_trw_layer_delete_track ( vtl, tmp );
2621 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
2622 vik_layer_emit_update(VIK_LAYER(vtl));
2624 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
2625 vik_layer_emit_update (VIK_LAYER(vtl));
2628 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
2630 if ( ! vtl->tpwin )
2632 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
2633 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
2634 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
2635 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
2636 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
2638 if ( vtl->current_tpl )
2639 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
2640 /* set layer name and TP data */
2643 /***************************************************************************
2644 ** Tool code
2645 ***************************************************************************/
2647 /*** Utility data structures and functions ****/
2649 typedef struct {
2650 gint x, y;
2651 gint closest_x, closest_y;
2652 gchar *closest_wp_name;
2653 VikWaypoint *closest_wp;
2654 VikViewport *vvp;
2655 } WPSearchParams;
2657 typedef struct {
2658 gint x, y;
2659 gint closest_x, closest_y;
2660 gchar *closest_track_name;
2661 VikTrackpoint *closest_tp;
2662 VikViewport *vvp;
2663 GList *closest_tpl;
2664 } TPSearchParams;
2666 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
2668 gint x, y;
2669 if ( !wp->visible )
2670 return;
2672 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
2674 if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
2675 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
2676 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
2678 params->closest_wp_name = name;
2679 params->closest_wp = wp;
2680 params->closest_x = x;
2681 params->closest_y = y;
2685 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
2687 GList *tpl = t->trackpoints;
2688 VikTrackpoint *tp;
2690 if ( !t->visible )
2691 return;
2693 while (tpl)
2695 gint x, y;
2696 tp = VIK_TRACKPOINT(tpl->data);
2698 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
2700 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
2701 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
2702 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
2704 params->closest_track_name = name;
2705 params->closest_tp = tp;
2706 params->closest_tpl = tpl;
2707 params->closest_x = x;
2708 params->closest_y = y;
2710 tpl = tpl->next;
2714 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
2716 TPSearchParams params;
2717 params.x = x;
2718 params.y = y;
2719 params.vvp = vvp;
2720 params.closest_track_name = NULL;
2721 params.closest_tp = NULL;
2722 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
2723 return params.closest_tp;
2726 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
2728 WPSearchParams params;
2729 params.x = x;
2730 params.y = y;
2731 params.vvp = vvp;
2732 params.closest_wp = NULL;
2733 params.closest_wp_name = NULL;
2734 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
2735 return params.closest_wp;
2738 /* background drawing hook, to be passed the viewport */
2739 static gboolean tool_sync_done = TRUE;
2741 static gboolean tool_sync(gpointer data)
2743 VikViewport *vvp = data;
2744 gdk_threads_enter();
2745 vik_viewport_sync(vvp);
2746 tool_sync_done = TRUE;
2747 gdk_threads_leave();
2748 return FALSE;
2751 typedef struct {
2752 VikViewport *vvp;
2753 gboolean holding;
2754 GdkGC *gc;
2755 int oldx, oldy;
2756 } tool_ed_t;
2758 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
2760 t->holding = TRUE;
2761 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
2762 gdk_gc_set_function ( t->gc, GDK_INVERT );
2763 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
2764 vik_viewport_sync(t->vvp);
2765 t->oldx = x;
2766 t->oldy = y;
2769 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
2771 VikViewport *vvp = t->vvp;
2772 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
2773 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
2774 t->oldx = x;
2775 t->oldy = y;
2776 if (tool_sync_done) {
2777 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
2778 tool_sync_done = FALSE;
2782 static void marker_end_move ( tool_ed_t *t )
2784 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
2785 g_object_unref ( t->gc );
2786 t->holding = FALSE;
2789 /*** Edit waypoint ****/
2791 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
2793 tool_ed_t *t = g_new(tool_ed_t, 1);
2794 t->vvp = vvp;
2795 t->holding = FALSE;
2796 return t;
2799 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
2801 WPSearchParams params;
2802 tool_ed_t *t = data;
2803 VikViewport *vvp = t->vvp;
2805 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
2806 return FALSE;
2808 if ( t->holding ) {
2809 return TRUE;
2812 if ( vtl->current_wp && vtl->current_wp->visible )
2814 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
2815 gint x, y;
2816 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
2818 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
2819 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
2821 if ( event->button == 3 )
2822 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
2823 else {
2824 marker_begin_move(t, event->x, event->y);
2826 return TRUE;
2830 params.vvp = vvp;
2831 params.x = event->x;
2832 params.y = event->y;
2833 params.closest_wp_name = NULL;
2834 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
2835 params.closest_wp = NULL;
2836 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
2837 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
2839 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
2840 marker_begin_move(t, event->x, event->y);
2841 g_critical("shouldn't be here");
2842 exit(1);
2844 else if ( params.closest_wp )
2846 if ( event->button == 3 )
2847 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
2848 else
2849 vtl->waypoint_rightclick = FALSE;
2851 vtl->current_wp = params.closest_wp;
2852 vtl->current_wp_name = params.closest_wp_name;
2854 if ( params.closest_wp )
2855 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ) );
2857 /* could make it so don't update if old WP is off screen and new is null but oh well */
2858 vik_layer_emit_update ( VIK_LAYER(vtl) );
2859 return TRUE;
2862 vtl->current_wp = NULL;
2863 vtl->current_wp_name = NULL;
2864 vtl->waypoint_rightclick = FALSE;
2865 vik_layer_emit_update ( VIK_LAYER(vtl) );
2866 return FALSE;
2869 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
2871 tool_ed_t *t = data;
2872 VikViewport *vvp = t->vvp;
2874 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
2875 return FALSE;
2877 if ( t->holding ) {
2878 VikCoord new_coord;
2879 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
2881 /* snap to TP */
2882 if ( event->state & GDK_CONTROL_MASK )
2884 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
2885 if ( tp )
2886 new_coord = tp->coord;
2889 /* snap to WP */
2890 if ( event->state & GDK_SHIFT_MASK )
2892 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
2893 if ( wp && wp != vtl->current_wp )
2894 new_coord = wp->coord;
2898 gint x, y;
2899 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
2900 marker_moveto ( t, x, y );
2902 return TRUE;
2904 return FALSE;
2907 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
2909 tool_ed_t *t = data;
2910 VikViewport *vvp = t->vvp;
2912 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
2913 return FALSE;
2915 if ( t->holding && event->button == 1 )
2917 VikCoord new_coord;
2918 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
2920 /* snap to TP */
2921 if ( event->state & GDK_CONTROL_MASK )
2923 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
2924 if ( tp )
2925 new_coord = tp->coord;
2928 /* snap to WP */
2929 if ( event->state & GDK_SHIFT_MASK )
2931 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
2932 if ( wp && wp != vtl->current_wp )
2933 new_coord = wp->coord;
2936 marker_end_move ( t );
2938 vtl->current_wp->coord = new_coord;
2939 vik_layer_emit_update ( VIK_LAYER(vtl) );
2940 return TRUE;
2942 /* PUT IN RIGHT PLACE!!! */
2943 if ( event->button == 3 && vtl->waypoint_rightclick )
2945 if ( vtl->wp_right_click_menu )
2946 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
2947 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
2948 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 ) );
2949 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
2950 vtl->waypoint_rightclick = FALSE;
2952 return FALSE;
2955 /*** New track ****/
2957 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
2959 return vvp;
2962 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
2964 VikTrackpoint *tp;
2966 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
2967 return FALSE;
2969 if ( event->button == 3 && vtl->current_track )
2971 /* undo */
2972 if ( vtl->current_track->trackpoints )
2974 GList *last = g_list_last(vtl->current_track->trackpoints);
2975 g_free ( last->data );
2976 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
2978 vik_layer_emit_update ( VIK_LAYER(vtl) );
2979 return TRUE;
2982 if ( event->type == GDK_2BUTTON_PRESS )
2984 /* subtract last (duplicate from double click) tp then end */
2985 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
2987 GList *last = g_list_last(vtl->current_track->trackpoints);
2988 g_free ( last->data );
2989 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
2990 /* undo last, then end */
2991 vtl->current_track = NULL;
2993 return TRUE;
2996 if ( ! vtl->current_track )
2998 gchar *name;
2999 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks ) ) )
3001 vtl->current_track = vik_track_new();
3002 vtl->current_track->visible = TRUE;
3003 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3005 else
3006 return TRUE;
3008 tp = vik_trackpoint_new();
3009 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
3011 /* snap to other TP */
3012 if ( event->state & GDK_CONTROL_MASK )
3014 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3015 if ( other_tp )
3016 tp->coord = other_tp->coord;
3019 tp->newsegment = FALSE;
3020 tp->has_timestamp = FALSE;
3021 tp->timestamp = 0;
3022 tp->altitude = VIK_DEFAULT_ALTITUDE;
3023 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
3025 vtl->ct_x1 = vtl->ct_x2;
3026 vtl->ct_y1 = vtl->ct_y2;
3027 vtl->ct_x2 = event->x;
3028 vtl->ct_y2 = event->y;
3030 vik_layer_emit_update ( VIK_LAYER(vtl) );
3031 return TRUE;
3035 /*** New waypoint ****/
3037 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
3039 return vvp;
3042 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3044 VikCoord coord;
3045 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3046 return FALSE;
3047 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
3048 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
3049 vik_layer_emit_update ( VIK_LAYER(vtl) );
3050 return TRUE;
3054 /*** Edit trackpoint ****/
3056 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
3058 tool_ed_t *t = g_new(tool_ed_t, 1);
3059 t->vvp = vvp;
3060 t->holding = FALSE;
3061 return t;
3064 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3066 tool_ed_t *t = data;
3067 VikViewport *vvp = t->vvp;
3068 TPSearchParams params;
3069 /* OUTDATED DOCUMENTATION:
3070 find 5 pixel range on each side. then put these UTM, and a pointer
3071 to the winning track name (and maybe the winning track itself), and a
3072 pointer to the winning trackpoint, inside an array or struct. pass
3073 this along, do a foreach on the tracks which will do a foreach on the
3074 trackpoints. */
3075 params.vvp = vvp;
3076 params.x = event->x;
3077 params.y = event->y;
3078 params.closest_track_name = NULL;
3079 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
3080 params.closest_tp = NULL;
3082 if ( event->button != 1 )
3083 return FALSE;
3085 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3086 return FALSE;
3088 if ( vtl->current_tpl )
3090 /* 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.) */
3091 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
3092 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
3093 gint x, y;
3094 g_assert ( current_tr );
3096 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
3098 if ( current_tr->visible &&
3099 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
3100 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
3101 marker_begin_move ( t, event->x, event->y );
3102 return TRUE;
3105 vtl->last_tpl = vtl->current_tpl;
3106 vtl->last_tp_track_name = vtl->current_tp_track_name;
3109 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
3111 if ( params.closest_tp )
3113 vtl->current_tpl = params.closest_tpl;
3114 vtl->current_tp_track_name = params.closest_track_name;
3115 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, vtl->current_tp_track_name ) );
3116 trw_layer_tpwin_init ( vtl );
3117 vik_layer_emit_update ( VIK_LAYER(vtl) );
3118 return TRUE;
3121 /* these aren't the droids you're looking for */
3122 return FALSE;
3125 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3127 tool_ed_t *t = data;
3128 VikViewport *vvp = t->vvp;
3130 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3131 return FALSE;
3133 if ( t->holding )
3135 VikCoord new_coord;
3136 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3138 /* snap to TP */
3139 if ( event->state & GDK_CONTROL_MASK )
3141 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3142 if ( tp && tp != vtl->current_tpl->data )
3143 new_coord = tp->coord;
3145 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
3147 gint x, y;
3148 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
3149 marker_moveto ( t, x, y );
3152 return TRUE;
3154 return FALSE;
3157 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3159 tool_ed_t *t = data;
3160 VikViewport *vvp = t->vvp;
3162 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3163 return FALSE;
3164 if ( event->button != 1)
3165 return FALSE;
3167 if ( t->holding ) {
3168 VikCoord new_coord;
3169 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3171 /* snap to TP */
3172 if ( event->state & GDK_CONTROL_MASK )
3174 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3175 if ( tp && tp != vtl->current_tpl->data )
3176 new_coord = tp->coord;
3179 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
3181 marker_end_move ( t );
3183 /* diff dist is diff from orig */
3184 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3185 /* can't join with itself! */
3186 trw_layer_cancel_last_tp ( vtl );
3188 vik_layer_emit_update ( VIK_LAYER(vtl) );
3189 return TRUE;
3191 return FALSE;
3195 /*** Magic Scissors ***/
3196 static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp)
3198 return vvp;
3201 static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3203 VikCoord tmp;
3204 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
3205 if ( vtl->magic_scissors_started ) {
3206 struct LatLon start, end;
3207 gchar *cmd;
3208 vik_coord_to_latlon ( &(vtl->magic_scissors_coord), &start );
3209 vik_coord_to_latlon ( &(tmp), &end );
3210 cmd = g_strdup_printf(GOOGLE_DIRECTIONS_STRING, start.lat, start.lon, end.lat, end.lon );
3211 a_babel_convert_from_shellcommand ( vtl, cmd, "google", NULL, NULL );
3212 g_free ( cmd );
3213 vik_layer_emit_update ( VIK_LAYER(vtl) );
3214 } else {
3215 vtl->magic_scissors_coord = tmp;
3217 vtl->magic_scissors_started = !vtl->magic_scissors_started;
3218 return TRUE;
3221 /*** Show picture ****/
3223 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
3225 return vvp;
3228 /* Params are: vvp, event, last match found or NULL */
3229 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] )
3231 if ( wp->image && wp->visible )
3233 gint x, y, slackx, slacky;
3234 GdkEventButton *event = (GdkEventButton *) params[1];
3236 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
3237 slackx = wp->image_width / 2;
3238 slacky = wp->image_height / 2;
3239 if ( x <= event->x + slackx && x >= event->x - slackx
3240 && y <= event->y + slacky && y >= event->y - slacky )
3242 params[2] = wp->image; /* we've found a match. however continue searching
3243 * since we want to find the last match -- that
3244 * is, the match that was drawn last. */
3249 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3251 gpointer params[3] = { vvp, event, NULL };
3252 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3253 return FALSE;
3254 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
3255 if ( params[2] )
3257 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
3258 #ifdef WINDOWS
3259 ShellExecute(NULL, NULL, (char *) params[2], NULL, ".\\", 0);
3260 #else /* WINDOWS */
3261 GError *err = NULL;
3262 gchar *quoted_file = g_shell_quote ( (gchar *) params[2] );
3263 gchar *cmd = g_strdup_printf ( "eog %s", quoted_file );
3264 g_free ( quoted_file );
3265 if ( ! g_spawn_command_line_async ( cmd, &err ) )
3267 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), "Could not launch eog to open file." );
3268 g_error_free ( err );
3270 g_free ( cmd );
3271 #endif /* WINDOWS */
3272 return TRUE; /* found a match */
3274 else
3275 return FALSE; /* go through other layers, searching for a match */
3278 /***************************************************************************
3279 ** End tool code
3280 ***************************************************************************/
3286 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
3288 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
3289 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
3292 static void create_thumbnails_thread ( GSList *pics, gpointer threaddata )
3294 guint total = g_slist_length(pics), done = 0;
3295 while ( pics )
3297 a_thumbnails_create ( (gchar *) pics->data );
3298 a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
3299 pics = pics->next;
3303 static void free_pics_slist ( GSList *pics )
3305 while ( pics )
3307 g_free ( pics->data );
3308 pics = g_slist_delete_link ( pics, pics );
3312 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
3314 if ( ! vtl->has_verified_thumbnails )
3316 GSList *pics = NULL;
3317 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
3318 if ( pics )
3320 gint len = g_slist_length ( pics );
3321 gchar *tmp = g_strdup_printf ( "Creating %d Image Thumbnails...", len );
3322 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 );
3323 g_free ( tmp );
3328 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
3330 return vtl->coord_mode;
3335 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
3337 vik_coord_convert ( &(wp->coord), *dest_mode );
3340 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
3342 vik_track_convert ( tr, *dest_mode );
3345 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
3347 if ( vtl->coord_mode != dest_mode )
3349 vtl->coord_mode = dest_mode;
3350 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
3351 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
3355 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
3357 return g_hash_table_lookup ( vtl->waypoints, name );
3360 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, gchar *name )
3362 return g_hash_table_lookup ( vtl->tracks, name );
3365 static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16 selection)
3367 vtl->menu_selection = selection;
3370 static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl)
3372 return(vtl->menu_selection);
3375 /* ----------- Downloading maps along tracks --------------- */
3377 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
3379 /* TODO: calculating based on current size of viewport */
3380 const gdouble w_at_zoom_0_125 = 0.0013;
3381 const gdouble h_at_zoom_0_125 = 0.0011;
3382 gdouble zoom_factor = zoom_level/0.125;
3384 wh->lat = h_at_zoom_0_125 * zoom_factor;
3385 wh->lon = w_at_zoom_0_125 * zoom_factor;
3387 return 0; /* all OK */
3390 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
3392 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
3393 (dist->lat >= ABS(to->north_south - from->north_south)))
3394 return NULL;
3396 VikCoord *coord = g_malloc(sizeof(VikCoord));
3397 coord->mode = VIK_COORD_LATLON;
3399 if (ABS(gradient) < 1) {
3400 if (from->east_west > to->east_west)
3401 coord->east_west = from->east_west - dist->lon;
3402 else
3403 coord->east_west = from->east_west + dist->lon;
3404 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
3405 } else {
3406 if (from->north_south > to->north_south)
3407 coord->north_south = from->north_south - dist->lat;
3408 else
3409 coord->north_south = from->north_south + dist->lat;
3410 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
3413 return coord;
3416 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
3418 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
3419 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
3421 VikCoord *next = from;
3422 while (TRUE) {
3423 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
3424 break;
3425 list = g_list_prepend(list, next);
3428 return list;
3431 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
3433 typedef struct _Rect {
3434 VikCoord tl;
3435 VikCoord br;
3436 VikCoord center;
3437 } Rect;
3438 #define GLRECT(iter) ((Rect *)((iter)->data))
3440 struct LatLon wh;
3441 GList *rects_to_download = NULL;
3442 GList *rect_iter;
3444 if (get_download_area_width(vvp, zoom_level, &wh))
3445 return;
3447 GList *iter = tr->trackpoints;
3448 if (!iter)
3449 return;
3451 gboolean new_map = TRUE;
3452 VikCoord *cur_coord, tl, br;
3453 Rect *rect;
3454 while (iter) {
3455 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
3456 if (new_map) {
3457 vik_coord_set_area(cur_coord, &wh, &tl, &br);
3458 rect = g_malloc(sizeof(Rect));
3459 rect->tl = tl;
3460 rect->br = br;
3461 rect->center = *cur_coord;
3462 rects_to_download = g_list_prepend(rects_to_download, rect);
3463 new_map = FALSE;
3464 iter = iter->next;
3465 continue;
3467 gboolean found = FALSE;
3468 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
3469 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
3470 found = TRUE;
3471 break;
3474 if (found)
3475 iter = iter->next;
3476 else
3477 new_map = TRUE;
3480 /* fill-ins for far apart points */
3481 GList *cur_rect, *next_rect;
3482 GList *fillins = NULL;
3483 for (cur_rect = rects_to_download;
3484 (next_rect = cur_rect->next) != NULL;
3485 cur_rect = cur_rect->next) {
3486 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
3487 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
3488 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
3492 if (fillins) {
3493 GList *iter = fillins;
3494 while (iter) {
3495 cur_coord = (VikCoord *)(iter->data);
3496 vik_coord_set_area(cur_coord, &wh, &tl, &br);
3497 rect = g_malloc(sizeof(Rect));
3498 rect->tl = tl;
3499 rect->br = br;
3500 rect->center = *cur_coord;
3501 rects_to_download = g_list_prepend(rects_to_download, rect);
3502 iter = iter->next;
3506 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
3507 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
3510 if (fillins) {
3511 for (iter = fillins; iter; iter = iter->next)
3512 g_free(iter->data);
3513 g_list_free(fillins);
3515 if (rects_to_download) {
3516 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
3517 g_free(rect_iter->data);
3518 g_list_free(rects_to_download);
3522 static void trw_layer_download_map_along_track_cb(gpointer pass_along[6])
3524 VikMapsLayer *vml;
3525 gint selected_map, default_map;
3526 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
3527 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
3528 gint selected_zoom, default_zoom;
3529 int i,j;
3532 VikTrwLayer *vtl = pass_along[0];
3533 VikLayersPanel *vlp = pass_along[1];
3534 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3535 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
3537 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS);
3538 int num_maps = g_list_length(vmls);
3540 if (!num_maps) {
3541 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR,"No map layer in use. Create one first", NULL);
3542 return;
3545 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
3546 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
3548 gchar **np = map_names;
3549 VikMapsLayer **lp = map_layers;
3550 for (i = 0; i < num_maps; i++) {
3551 gboolean dup = FALSE;
3552 vml = (VikMapsLayer *)(vmls->data);
3553 for (j = 0; j < i; j++) { /* no duplicate allowed */
3554 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
3555 dup = TRUE;
3556 break;
3559 if (!dup) {
3560 *lp++ = vml;
3561 *np++ = vik_maps_layer_get_map_label(vml);
3563 vmls = vmls->next;
3565 *lp = NULL;
3566 *np = NULL;
3567 num_maps = lp - map_layers;
3569 for (default_map = 0; default_map < num_maps; default_map++) {
3570 /* TODO: check for parent layer's visibility */
3571 if (VIK_LAYER(map_layers[default_map])->visible)
3572 break;
3574 default_map = (default_map == num_maps) ? 0 : default_map;
3576 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
3577 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
3578 if (cur_zoom == zoom_vals[default_zoom])
3579 break;
3581 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
3583 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
3584 goto done;
3586 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
3588 done:
3589 for (i = 0; i < num_maps; i++)
3590 g_free(map_names[i]);
3591 g_free(map_names);
3592 g_free(map_layers);
3594 g_list_free(vmls);