]> git.street.me.uk Git - andy/viking.git/blame - src/viktrwlayer.c
(no commit message)
[andy/viking.git] / src / viktrwlayer.c
CommitLineData
50a14534
EB
1/*
2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3 *
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 *
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.
10 *
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.
15 *
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
19 *
20 */
21
22#define WAYPOINT_FONT "Sans 8"
23
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 */
26
27#include "viking.h"
28#include "viktrwlayer_pixmap.h"
29#include "viktrwlayer_tpwin.h"
30#include "viktrwlayer_propwin.h"
acaf7113 31#include "garminsymbols.h"
50a14534
EB
32#include "thumbnails.h"
33#include "background.h"
911400b5 34#include "gpx.h"
50a14534
EB
35
36#include <math.h>
37#include <string.h>
38#include <stdlib.h>
39#include <ctype.h>
40
41#define VIK_TRW_LAYER_TRACK_GC 13
42#define VIK_TRW_LAYER_TRACK_GC_RATES 10
43#define VIK_TRW_LAYER_TRACK_GC_MIN 0
44#define VIK_TRW_LAYER_TRACK_GC_MAX 11
45#define VIK_TRW_LAYER_TRACK_GC_BLACK 12
46
47#define DRAWMODE_BY_TRACK 0
48#define DRAWMODE_BY_VELOCITY 1
49#define DRAWMODE_ALL_BLACK 2
50
51#define POINTS 1
52#define LINES 2
53
54/* this is how it knows when you click if you are clicking close to a trackpoint. */
55#define TRACKPOINT_SIZE_APPROX 5
56#define WAYPOINT_SIZE_APPROX 5
57
b42a25ba
EB
58#define MIN_STOP_LENGTH 15
59#define MAX_STOP_LENGTH 86400
60#define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
61 /* this is multiplied by user-inputted value from 1-100. */
50a14534
EB
62enum {
63VIK_TRW_LAYER_SUBLAYER_TRACKS,
64VIK_TRW_LAYER_SUBLAYER_WAYPOINTS,
65VIK_TRW_LAYER_SUBLAYER_TRACK,
66VIK_TRW_LAYER_SUBLAYER_WAYPOINT
67};
68
69enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
70
71struct _VikTrwLayer {
72 VikLayer vl;
73 GHashTable *tracks;
74 GHashTable *tracks_iters;
75 GHashTable *waypoints_iters;
76 GHashTable *waypoints;
77 GtkTreeIter waypoints_iter, tracks_iter;
78 gboolean tracks_visible, waypoints_visible;
79 guint8 drawmode;
80 guint8 drawpoints;
b42a25ba
EB
81 guint8 drawelevation;
82 guint8 elevation_factor;
83 guint8 drawstops;
84 guint32 stop_length;
50a14534
EB
85 guint8 drawlines;
86 guint8 line_thickness;
87 guint8 bg_line_thickness;
88
89 guint8 wp_symbol;
90 guint8 wp_size;
ea3933fc 91 gboolean wp_draw_symbols;
50a14534
EB
92
93 gdouble velocity_min, velocity_max;
94 GArray *track_gc;
95 guint16 track_gc_iter;
96 GdkGC *track_bg_gc;
97 GdkGC *waypoint_gc;
98 GdkGC *waypoint_text_gc;
99 GdkGC *waypoint_bg_gc;
100 GdkFont *waypoint_font;
101 VikTrack *current_track;
102 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
103
104 VikCoordMode coord_mode;
105
106 /* wp editing tool */
107 VikWaypoint *current_wp;
108 gchar *current_wp_name;
109 gboolean moving_wp;
110 gboolean waypoint_rightclick;
111
112 /* track editing tool */
113 GList *current_tpl;
114 gchar *current_tp_track_name;
115 VikTrwLayerTpwin *tpwin;
116
117 /* weird hack for joining tracks */
118 GList *last_tpl;
119 gchar *last_tp_track_name;
120
121 /* track editing tool -- more specifically, moving tps */
122 gboolean moving_tp;
123
124 gboolean drawlabels;
125 gboolean drawimages;
126 guint8 image_alpha;
127 GQueue *image_cache;
128 guint8 image_size;
129 guint16 image_cache_size;
130
131 /* for waypoint text */
132 PangoLayout *wplabellayout;
133
134 gboolean has_verified_thumbnails;
135
136 GtkMenu *wp_right_click_menu;
137
138};
139
140/* A caached waypoint image. */
141typedef struct {
142 GdkPixbuf *pixbuf;
143 gchar *image; /* filename */
144} CachedPixbuf;
145
146struct DrawingParams {
147 VikViewport *vp;
148 VikTrwLayer *vtl;
149 gdouble xmpp, ympp;
150 guint16 width, height;
151 const VikCoord *center;
152 gint track_gc_iter;
153 gboolean one_zone, lat_lon;
154 gdouble ce1, ce2, cn1, cn2;
155};
156
33534cd8
AF
157static void trw_layer_delete_item ( gpointer *pass_along );
158
50a14534
EB
159static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
160static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] );
161
162static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
163static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
164
165static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 );
166static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
167static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
168
169static void goto_coord ( VikLayersPanel *vlp, const VikCoord *coord );
170static void trw_layer_goto_track_startpoint ( gpointer pass_along[5] );
171static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
111fa174
AF
172static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
173static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
50a14534
EB
174static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
175static void trw_layer_export ( gpointer layer_and_vlp[2], guint file_type );
176static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
177static void trw_layer_new_wp ( gpointer lav[2] );
178
179/* pop-up items */
180static void trw_layer_properties_item ( gpointer pass_along[5] );
181static void trw_layer_goto_waypoint ( gpointer pass_along[5] );
182static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[5] );
183
184static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[4] );
185static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[4] );
186static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
187
50a14534
EB
188
189static VikTrwLayer *trw_layer_copy ( VikTrwLayer *vtl, gpointer vp );
911400b5
AF
190static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len );
191static VikTrwLayer *trw_layer_unmarshall( gpointer data, gint len, VikViewport *vvp );
192
50a14534
EB
193static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp );
194static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id );
195
33534cd8 196static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
ddc47a46
AF
197static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
198static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
50a14534 199static void trw_layer_free_copied_item ( gint subtype, gpointer item );
70a23263 200static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
50a14534
EB
201
202static void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name );
203static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
204static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
205static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
206static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
50a14534 207
941aa6e9 208static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
33534cd8
AF
209static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
210static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
211static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
941aa6e9
AF
212static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
213static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
214static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
215static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
50a14534 216static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
941aa6e9
AF
217static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
218static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
219static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
220static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
50a14534
EB
221
222static gboolean uppercase_exists_in_hash ( GHashTable *hash, const gchar *str );
223
224static void cached_pixbuf_free ( CachedPixbuf *cp );
225static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
226static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp );
227
228static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
229static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
230
231static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
232
e890a6e6 233static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
ddc47a46
AF
234static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode );
235static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
e890a6e6 236
50a14534 237static VikToolInterface trw_layer_tools[] = {
941aa6e9
AF
238 { "Create Waypoint", (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
239 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL },
240
241 { "Create Track", (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
242 (VikToolMouseFunc) tool_new_track_click, NULL, NULL },
50a14534 243
941aa6e9
AF
244 { "Edit Waypoint", (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
245 (VikToolMouseFunc) tool_edit_waypoint_click, NULL, (VikToolMouseFunc) tool_edit_waypoint_release },
246
247 { "Edit Trackpoint", (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
33534cd8
AF
248 (VikToolMouseFunc) tool_edit_trackpoint_click,
249 (VikToolMouseFunc) tool_edit_trackpoint_move,
250 (VikToolMouseFunc) tool_edit_trackpoint_release },
941aa6e9
AF
251
252 { "Show Picture", (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
253 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL },
254};
50a14534
EB
255
256/****** PARAMETERS ******/
257
258static gchar *params_groups[] = { "Waypoints", "Tracks", "Waypoint Images" };
259enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
260
261static gchar *params_drawmodes[] = { "Draw by Track", "Draw by Velocity", "All Tracks Black", 0 };
262static gchar *params_wpsymbols[] = { "Filled Square", "Square", "Circle", "X", 0 };
263
b42a25ba 264
50a14534
EB
265static VikLayerParamScale params_scales[] = {
266 /* min max step digits */
267 { 1, 10, 1, 0 }, /* line_thickness */
268 { 0.0, 99.0, 1, 2 }, /* velocity_min */
269 { 1.0, 100.0, 1.0, 2 }, /* velocity_max */
270 /* 5 * step == how much to turn */
271 { 16, 128, 3.2, 0 }, /* image_size */
272 { 0, 255, 5, 0 }, /* image alpha */
273 { 5, 500, 5, 0 }, /* image cache_size */
274 { 0, 8, 1, 0 }, /* image cache_size */
275 { 1, 64, 1, 0 }, /* wpsize */
b42a25ba
EB
276 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
277 { 1, 100, 1, 0 }, /* stop_length */
50a14534
EB
278};
279
280VikLayerParam trw_layer_params[] = {
281 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
282 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
283
284 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, "Track Drawing Mode:", VIK_LAYER_WIDGET_RADIOGROUP, params_drawmodes },
285 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, "Draw Track Lines", VIK_LAYER_WIDGET_CHECKBUTTON },
286 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, "Draw Trackpoints", VIK_LAYER_WIDGET_CHECKBUTTON },
b42a25ba
EB
287 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, "Draw Elevation", VIK_LAYER_WIDGET_CHECKBUTTON },
288 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, "Draw Elevation Height %:", VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
289
290 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, "Draw Stops", VIK_LAYER_WIDGET_CHECKBUTTON },
291 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, "Min Stop Length (seconds):", VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
292
50a14534
EB
293 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, "Track Thickness:", VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
294 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, "Track BG Thickness:", VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
295 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, "Track Background Color", VIK_LAYER_WIDGET_COLOR, 0 },
296 { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, "Min Track Velocity:", VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
297 { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, "Max Track Velocity:", VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 },
298
299 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, "Draw Labels", VIK_LAYER_WIDGET_CHECKBUTTON },
300 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, "Waypoint Color:", VIK_LAYER_WIDGET_COLOR, 0 },
301 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, "Waypoint Text:", VIK_LAYER_WIDGET_COLOR, 0 },
302 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, "Background:", VIK_LAYER_WIDGET_COLOR, 0 },
303 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, "Fake BG Color Translucency:", VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
ea3933fc 304 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, "Waypoint marker:", VIK_LAYER_WIDGET_RADIOGROUP, params_wpsymbols },
50a14534 305 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, "Waypoint size:", VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
ea3933fc 306 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, "Draw Waypoint Symbols:", VIK_LAYER_WIDGET_CHECKBUTTON },
50a14534
EB
307
308 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, "Draw Waypoint Images", VIK_LAYER_WIDGET_CHECKBUTTON },
309 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, "Image Size (pixels):", VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
310 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, "Image Alpha:", VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
311 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, "Image Memory Cache Size:", VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
312};
313
ea3933fc 314enum { 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 };
50a14534
EB
315
316/****** END PARAMETERS ******/
317
318VikLayerInterface vik_trw_layer_interface = {
319 "TrackWaypoint",
320 &trwlayer_pixbuf,
321
322 trw_layer_tools,
323 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
324
325 trw_layer_params,
326 NUM_PARAMS,
327 params_groups, /* params_groups */
328 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
329
330 (VikLayerFuncCreate) vik_trw_layer_create,
331 (VikLayerFuncRealize) vik_trw_layer_realize,
332 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
333 (VikLayerFuncFree) vik_trw_layer_free,
334
335 (VikLayerFuncProperties) NULL,
336 (VikLayerFuncDraw) vik_trw_layer_draw,
337 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
338
339 (VikLayerFuncAddMenuItems) vik_trw_layer_add_menu_items,
340 (VikLayerFuncSublayerAddMenuItems) vik_trw_layer_sublayer_add_menu_items,
341
342 (VikLayerFuncSublayerRenameRequest) vik_trw_layer_sublayer_rename_request,
343 (VikLayerFuncSublayerToggleVisible) vik_trw_layer_sublayer_toggle_visible,
344
345 (VikLayerFuncCopy) trw_layer_copy,
911400b5
AF
346 (VikLayerFuncMarshall) trw_layer_marshall,
347 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
50a14534
EB
348
349 (VikLayerFuncSetParam) trw_layer_set_param,
350 (VikLayerFuncGetParam) trw_layer_get_param,
351
352 (VikLayerFuncReadFileData) a_gpspoint_read_file,
353 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
354
33534cd8 355 (VikLayerFuncDeleteItem) trw_layer_del_item,
50a14534
EB
356 (VikLayerFuncCopyItem) trw_layer_copy_item,
357 (VikLayerFuncPasteItem) trw_layer_paste_item,
358 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
70a23263
AF
359
360 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
50a14534
EB
361};
362
363/* for copy & paste (I think?) */
364typedef struct {
ddc47a46
AF
365 guint len;
366 guint8 data[0];
367 // gchar *name;
368 // VikWaypoint *wp;
369} FlatItem;
50a14534
EB
370
371GType vik_trw_layer_get_type ()
372{
373 static GType vtl_type = 0;
374
375 if (!vtl_type)
376 {
377 static const GTypeInfo vtl_info =
378 {
379 sizeof (VikTrwLayerClass),
380 NULL, /* base_init */
381 NULL, /* base_finalize */
382 NULL, /* class init */
383 NULL, /* class_finalize */
384 NULL, /* class_data */
385 sizeof (VikTrwLayer),
386 0,
387 NULL /* instance init */
388 };
389 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
390 }
391
392 return vtl_type;
393}
394
33534cd8
AF
395static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
396{
397 static gpointer pass_along[5];
398 if (!sublayer) {
399 return;
400 }
401
402 pass_along[0] = vtl;
403 pass_along[1] = NULL;
404 pass_along[2] = (gpointer) subtype;
405 pass_along[3] = sublayer;
406 pass_along[4] = NULL;
407
408 trw_layer_delete_item ( pass_along );
409}
50a14534 410
ddc47a46 411static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
50a14534 412{
ddc47a46
AF
413 FlatItem *fi;
414 guint8 *id;
415 guint il;
416
417 if (!sublayer) {
418 *item = NULL;
419 return;
50a14534 420 }
ddc47a46
AF
421
422 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 423 {
ddc47a46
AF
424 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
425 } else {
426 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
50a14534 427 }
ddc47a46
AF
428
429 *len = sizeof(FlatItem) + strlen(sublayer) + 1 + il;
430 fi = g_malloc ( *len );
431 fi->len = strlen(sublayer) + 1;
432 memcpy(fi->data, sublayer, fi->len);
433 memcpy(fi->data + fi->len, id, il);
434 g_free(id);
435 *item = (guint8 *)fi;
50a14534
EB
436}
437
ddc47a46 438static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
50a14534 439{
ddc47a46
AF
440 FlatItem *fi = (FlatItem *) item;
441
442 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
50a14534 443 {
ddc47a46
AF
444 VikWaypoint *w;
445 gchar *name;
446
447 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, (gchar *)fi->data);
448 w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
449 vik_trw_layer_add_waypoint ( vtl, name, w );
450 waypoint_convert(name, w, &vtl->coord_mode);
50a14534
EB
451 return TRUE;
452 }
ddc47a46 453 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
50a14534 454 {
ddc47a46
AF
455 VikTrack *t;
456 gchar *name;
457 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, (gchar *)fi->data);
458 t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
459 vik_trw_layer_add_track ( vtl, name, t );
460 track_convert(name, t, &vtl->coord_mode);
50a14534
EB
461 return TRUE;
462 }
463 return FALSE;
464}
465
466static void trw_layer_free_copied_item ( gint subtype, gpointer item )
467{
ddc47a46
AF
468 if (item) {
469 g_free(item);
50a14534
EB
470 }
471}
472
473static void waypoint_copy ( const gchar *name, VikWaypoint *wp, GHashTable *dest )
474{
475 g_hash_table_insert ( dest, g_strdup(name), vik_waypoint_copy(wp) );
476}
477
478static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp )
479{
480 switch ( id )
481 {
482 case PARAM_TV: vtl->tracks_visible = data.b; break;
483 case PARAM_WV: vtl->waypoints_visible = data.b; break;
484 case PARAM_DM: vtl->drawmode = data.u; break;
485 case PARAM_DP: vtl->drawpoints = data.b; break;
b42a25ba
EB
486 case PARAM_DE: vtl->drawelevation = data.b; break;
487 case PARAM_DS: vtl->drawstops = data.b; break;
50a14534 488 case PARAM_DL: vtl->drawlines = data.b; break;
b42a25ba
EB
489 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
490 vtl->stop_length = data.u;
491 break;
492 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
493 vtl->elevation_factor = data.u;
494 break;
50a14534
EB
495 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
496 {
497 vtl->line_thickness = data.u;
498 trw_layer_new_track_gcs ( vtl, vp );
499 }
500 break;
501 case PARAM_BLT: if ( data.u > 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
502 {
503 vtl->bg_line_thickness = data.u;
504 trw_layer_new_track_gcs ( vtl, vp );
505 }
506 break;
507 case PARAM_VMIN: vtl->velocity_min = data.d; break;
508 case PARAM_VMAX: vtl->velocity_max = data.d; break;
509 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
510 case PARAM_DLA: vtl->drawlabels = data.b; break;
511 case PARAM_DI: vtl->drawimages = data.b; break;
512 case PARAM_IS: if ( data.u != vtl->image_size )
513 {
514 vtl->image_size = data.u;
515 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
516 g_queue_free ( vtl->image_cache );
517 vtl->image_cache = g_queue_new ();
518 }
519 break;
520 case PARAM_IA: vtl->image_alpha = data.u; break;
521 case PARAM_ICS: vtl->image_cache_size = data.u;
522 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
523 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
524 break;
525 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
526 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
527 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
528 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
529 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
530 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
ea3933fc 531 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
50a14534
EB
532 }
533 return TRUE;
534}
535
536static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id )
537{
538 VikLayerParamData rv;
539 switch ( id )
540 {
541 case PARAM_TV: rv.b = vtl->tracks_visible; break;
542 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
543 case PARAM_DM: rv.u = vtl->drawmode; break;
544 case PARAM_DP: rv.b = vtl->drawpoints; break;
b42a25ba
EB
545 case PARAM_DE: rv.b = vtl->drawelevation; break;
546 case PARAM_EF: rv.u = vtl->elevation_factor; break;
547 case PARAM_DS: rv.b = vtl->drawstops; break;
548 case PARAM_SL: rv.u = vtl->stop_length; break;
50a14534
EB
549 case PARAM_DL: rv.b = vtl->drawlines; break;
550 case PARAM_LT: rv.u = vtl->line_thickness; break;
551 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
552 case PARAM_VMIN: rv.d = vtl->velocity_min; break;
553 case PARAM_VMAX: rv.d = vtl->velocity_max; break;
554 case PARAM_DLA: rv.b = vtl->drawlabels; break;
555 case PARAM_DI: rv.b = vtl->drawimages; break;
556 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
557 case PARAM_IS: rv.u = vtl->image_size; break;
558 case PARAM_IA: rv.u = vtl->image_alpha; break;
559 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
560 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
561 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
562 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
563 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
564 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
565 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
ea3933fc 566 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
50a14534
EB
567 }
568 return rv;
569}
570
571static void track_copy ( const gchar *name, VikTrack *tr, GHashTable *dest )
572{
573 g_hash_table_insert ( dest, g_strdup ( name ), vik_track_copy(tr) );
574}
575
911400b5
AF
576static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
577{
578 guint8 *pd, *dd;
579 gint pl, dl;
580 gchar *tmpname;
581 FILE *f;
582
583 *data = NULL;
584
585 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
586 a_gpx_write_file(vtl, f);
587 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
588 fclose(f);
941aa6e9 589 g_file_get_contents(tmpname, (void *)&dd, (void *)&dl, NULL);
911400b5
AF
590 *len = sizeof(pl) + pl + dl;
591 *data = g_malloc(*len);
592 memcpy(*data, &pl, sizeof(pl));
593 memcpy(*data + sizeof(pl), pd, pl);
594 memcpy(*data + sizeof(pl) + pl, dd, dl);
595
596 g_free(pd);
597 g_free(dd);
598 remove(tmpname);
599 g_free(tmpname);
600 }
601}
602
603static VikTrwLayer *trw_layer_unmarshall( gpointer data, gint len, VikViewport *vvp )
604{
605 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
606 guint pl;
607 gchar *tmpname;
608 FILE *f;
609
610
611 memcpy(&pl, data, sizeof(pl));
612 data += sizeof(pl);
613 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
614 data += pl;
615
616 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
617 g_critical("couldn't open temp file\n");
618 exit(1);
619 }
620 fwrite(data, len - pl - sizeof(pl), 1, f);
621 rewind(f);
622 a_gpx_read_file(rv, f);
623 fclose(f);
624 remove(tmpname);
625 g_free(tmpname);
626 return rv;
627}
628
50a14534
EB
629static VikTrwLayer *trw_layer_copy ( VikTrwLayer *vtl, gpointer vp )
630{
631 VikTrwLayer *rv = vik_trw_layer_new ( vtl->drawmode );
632 PangoFontDescription *pfd;
633 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
634 pfd = pango_font_description_from_string (WAYPOINT_FONT);
635 pango_layout_set_font_description (rv->wplabellayout, pfd);
636 /* freeing PangoFontDescription, cause it has been copied by prev. call */
637 pango_font_description_free (pfd);
638
639 rv->tracks_visible = vtl->tracks_visible;
640 rv->waypoints_visible = vtl->waypoints_visible;
641 rv->drawpoints = vtl->drawpoints;
b42a25ba
EB
642 rv->drawstops = vtl->drawstops;
643 rv->drawelevation = vtl->drawelevation;
644 rv->elevation_factor = vtl->elevation_factor;
50a14534 645 rv->drawlines = vtl->drawlines;
b42a25ba 646 rv->stop_length = vtl->stop_length;
50a14534
EB
647 rv->line_thickness = vtl->line_thickness;
648 rv->bg_line_thickness = vtl->bg_line_thickness;
649 rv->velocity_min = vtl->velocity_min;
650 rv->velocity_max = vtl->velocity_max;
651 rv->drawlabels = vtl->drawlabels;
652 rv->drawimages = vtl->drawimages;
653 rv->image_size = vtl->image_size;
654 rv->image_alpha = vtl->image_alpha;
655 rv->image_cache_size = vtl->image_cache_size;
656 rv->has_verified_thumbnails = TRUE;
657 rv->coord_mode = vtl->coord_mode;
658 rv->wp_symbol = vtl->wp_symbol;
659 rv->wp_size = vtl->wp_size;
ea3933fc 660 rv->wp_draw_symbols = vtl->wp_draw_symbols;
50a14534
EB
661
662 trw_layer_new_track_gcs ( rv, VIK_VIEWPORT(vp) );
663
664 rv->waypoint_gc = gdk_gc_new ( GTK_WIDGET(vp)->window );
665 gdk_gc_set_line_attributes ( rv->waypoint_gc, 2, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND );
666
667 rv->waypoint_text_gc = gdk_gc_new ( GTK_WIDGET(vp)->window );
668 rv->waypoint_bg_gc = gdk_gc_new ( GTK_WIDGET(vp)->window );
669 gdk_gc_copy ( rv->waypoint_gc, vtl->waypoint_gc );
670 gdk_gc_copy ( rv->waypoint_text_gc, vtl->waypoint_text_gc );
671 gdk_gc_copy ( rv->waypoint_bg_gc, vtl->waypoint_bg_gc );
672
673 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
674
675 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_copy, rv->waypoints );
676 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_copy, rv->tracks );
677
678 return rv;
679}
680
681VikTrwLayer *vik_trw_layer_new ( gint drawmode )
682{
683 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
684 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
685
686 rv->waypoints = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_waypoint_free );
687 rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free );
688 rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
689 rv->waypoints_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
690
691 /* TODO: constants at top */
692 rv->waypoints_visible = rv->tracks_visible = TRUE;
693 rv->drawmode = drawmode;
694 rv->drawpoints = TRUE;
b42a25ba
EB
695 rv->drawstops = FALSE;
696 rv->drawelevation = FALSE;
697 rv->elevation_factor = 30;
698 rv->stop_length = 60;
50a14534
EB
699 rv->drawlines = TRUE;
700 rv->wplabellayout = NULL;
701 rv->wp_right_click_menu = NULL;
702 rv->waypoint_gc = NULL;
703 rv->waypoint_text_gc = NULL;
704 rv->waypoint_bg_gc = NULL;
705 rv->track_gc = NULL;
706 rv->velocity_max = 5.0;
707 rv->velocity_min = 0.0;
708 rv->line_thickness = 1;
b42a25ba 709 rv->bg_line_thickness = 0;
50a14534
EB
710 rv->current_wp = NULL;
711 rv->current_wp_name = NULL;
712 rv->current_track = NULL;
713 rv->current_tpl = NULL;
714 rv->current_tp_track_name = NULL;
715 rv->moving_tp = FALSE;
716 rv->moving_wp = FALSE;
717 rv->waypoint_rightclick = FALSE;
718 rv->last_tpl = NULL;
719 rv->last_tp_track_name = NULL;
720 rv->tpwin = NULL;
721 rv->image_cache = g_queue_new();
722 rv->image_size = 64;
723 rv->image_alpha = 255;
724 rv->image_cache_size = 300;
725 rv->drawimages = TRUE;
726 rv->drawlabels = TRUE;
727 return rv;
728}
729
730
731void vik_trw_layer_free ( VikTrwLayer *trwlayer )
732{
733 g_hash_table_destroy(trwlayer->waypoints);
734 g_hash_table_destroy(trwlayer->tracks);
735
736 /* ODC: replace with GArray */
737 trw_layer_free_track_gcs ( trwlayer );
738
739 if ( trwlayer->wp_right_click_menu )
740 gtk_object_sink ( GTK_OBJECT(trwlayer->wp_right_click_menu) );
741
742 if ( trwlayer->wplabellayout != NULL)
743 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
744
745 if ( trwlayer->waypoint_gc != NULL )
746 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
747
748 if ( trwlayer->waypoint_text_gc != NULL )
749 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
750
751 if ( trwlayer->waypoint_bg_gc != NULL )
752 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
753
754 if ( trwlayer->waypoint_font != NULL )
755 gdk_font_unref ( trwlayer->waypoint_font );
756
757 if ( trwlayer->tpwin != NULL )
758 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
759
760 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
761 g_queue_free ( trwlayer->image_cache );
762}
763
764static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
765{
766 dp->vp = vp;
767 dp->xmpp = vik_viewport_get_xmpp ( vp );
768 dp->ympp = vik_viewport_get_ympp ( vp );
769 dp->width = vik_viewport_get_width ( vp );
770 dp->height = vik_viewport_get_height ( vp );
771 dp->center = vik_viewport_get_center ( vp );
772 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
773 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
774
775 if ( dp->one_zone )
776 {
777 gint w2, h2;
778 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
779 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
780 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
781
782 dp->ce1 = dp->center->east_west-w2;
783 dp->ce2 = dp->center->east_west+w2;
784 dp->cn1 = dp->center->north_south-h2;
785 dp->cn2 = dp->center->north_south+h2;
786 } else if ( dp->lat_lon ) {
787 VikCoord upperleft, bottomright;
788 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
789 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
790 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
791 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
792 dp->ce1 = upperleft.east_west;
793 dp->ce2 = bottomright.east_west;
794 dp->cn1 = bottomright.north_south;
795 dp->cn2 = upperleft.north_south;
796 }
797
798 dp->track_gc_iter = 0;
799}
800
801static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
802{
803 static gdouble rv = 0;
804 if ( tp1->has_timestamp && tp2->has_timestamp )
805 {
806 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
807 / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
808
809 if ( rv < 0 )
810 return VIK_TRW_LAYER_TRACK_GC_MIN;
811 else if ( vtl->velocity_min >= vtl->velocity_max )
812 return VIK_TRW_LAYER_TRACK_GC_MAX;
813
814 rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
815
816 if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
817 return VIK_TRW_LAYER_TRACK_GC_MAX;
818 return (gint) rv;
819 }
820 else
821 return VIK_TRW_LAYER_TRACK_GC_BLACK;
822}
823
824void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
825{
826 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
827 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
828 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
829 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
830}
831
832static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
833{
834 /* TODO: this function is a mess, get rid of any redundancy */
835 GList *list = track->trackpoints;
836 gboolean useoldvals = TRUE;
837
838 gboolean drawpoints;
b42a25ba
EB
839 gboolean drawstops;
840 gboolean drawelevation;
941aa6e9 841 gdouble min_alt, max_alt, alt_diff = 0;
50a14534
EB
842
843 const guint8 tp_size_reg = 2;
844 const guint8 tp_size_cur = 4;
845 guint8 tp_size;
846
b42a25ba
EB
847 if ( dp->vtl->drawelevation )
848 {
849 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
850 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
851 alt_diff = max_alt - min_alt;
852 }
853
50a14534
EB
854 if ( ! track->visible )
855 return;
856
857 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
858 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
859 trw_layer_draw_track ( name, track, dp, TRUE );
860
861 if ( drawing_white_background )
b42a25ba
EB
862 drawpoints = drawstops = FALSE;
863 else {
50a14534 864 drawpoints = dp->vtl->drawpoints;
b42a25ba
EB
865 drawstops = dp->vtl->drawstops;
866 }
50a14534
EB
867
868 if (list) {
869 int x, y, oldx, oldy;
870 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
871
872 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
873
874 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
875
876 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
877 {
878 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
879 vik_viewport_draw_polygon ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), TRUE, trian, 3 );
880 }
881
882 oldx = x;
883 oldy = y;
884
885 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
886 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_MAX + 1;
887
888 while ((list = g_list_next(list)))
889 {
890 tp = VIK_TRACKPOINT(list->data);
891 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
892
893 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
894 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
895 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
896 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
897 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
898 {
899 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
900
901 if ( drawpoints && ! drawing_white_background )
902 {
903 if ( list->next ) {
904 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 );
905
734652bf
EB
906 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 );
907
50a14534 908 /* stops */
b42a25ba 909 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
50a14534
EB
910 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 );
911 }
912 else
913 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 );
914 }
915
916 if ((!tp->newsegment) && (dp->vtl->drawlines))
917 {
918 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
919
920 /* UTM only: zone check */
921 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
922 draw_utm_skip_insignia ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), x, y);
923
924 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY )
925 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
926
927 if (!useoldvals)
928 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
929
8c4f1350 930 if ( drawing_white_background ) {
50a14534 931 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
8c4f1350
EB
932 }
933 else {
8c4f1350 934
50a14534 935 vik_viewport_draw_line ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), oldx, oldy, x, y);
b42a25ba
EB
936 if ( dp->vtl->drawelevation && list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
937 GdkPoint tmp[4];
938 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
939 if ( list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
940 tmp[0].x = oldx;
941 tmp[0].y = oldy;
942 tmp[1].x = oldx;
943 tmp[1].y = oldy-FIXALTITUDE(list->data);
944 tmp[2].x = x;
945 tmp[2].y = y-FIXALTITUDE(list->next->data);
946 tmp[3].x = x;
947 tmp[3].y = y;
948
949 GdkGC *tmp_gc;
950 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
951 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
952 else
953 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
954 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
955 }
8c4f1350
EB
956 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));
957 }
958 }
50a14534
EB
959 }
960
961 oldx = x;
962 oldy = y;
963 useoldvals = TRUE;
964 }
965 else {
966 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
967 {
968 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
969 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
970 {
971 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
972 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY )
973 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
974
975 if ( drawing_white_background )
976 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
977 else
978 vik_viewport_draw_line ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), oldx, oldy, x, y);
979 }
980 else
981 {
982 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
983 draw_utm_skip_insignia ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), x, y );
984 }
985 }
986 useoldvals = FALSE;
987 }
988 }
989 }
990 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
991 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
992 dp->track_gc_iter = 0;
993}
994
995/* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
996static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
997{
998 trw_layer_draw_track ( name, track, dp, FALSE );
999}
1000
1001static void cached_pixbuf_free ( CachedPixbuf *cp )
1002{
1003 g_object_unref ( G_OBJECT(cp->pixbuf) );
1004 g_free ( cp->image );
1005}
1006
1007static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1008{
1009 return strcmp ( cp->image, name );
1010}
1011
1012static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1013{
1014 if ( wp->visible )
1015 if ( (!dp->one_zone) || ( wp->coord.utm_zone == dp->center->utm_zone &&
1016 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1017 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1018 {
1019 gint x, y;
acaf7113 1020 GdkPixbuf *sym;
50a14534
EB
1021 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1022
1023 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1024
1025 if ( wp->image && dp->vtl->drawimages )
1026 {
1027 GdkPixbuf *pixbuf = NULL;
1028 GList *l;
1029
1030 if ( dp->vtl->image_alpha == 0)
1031 return;
1032
1033 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1034 if ( l )
1035 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1036 else
1037 {
1038 gchar *image = wp->image;
1039 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1040 if ( ! regularthumb )
1041 {
1042 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1043 image = "\x12\x00"; /* this shouldn't occur naturally. */
1044 }
1045 if ( regularthumb )
1046 {
1047 CachedPixbuf *cp = NULL;
1048 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1049 if ( dp->vtl->image_size == 128 )
1050 cp->pixbuf = regularthumb;
1051 else
1052 {
1053 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1054 g_assert ( cp->pixbuf );
1055 g_object_unref ( G_OBJECT(regularthumb) );
1056 }
1057 cp->image = g_strdup ( image );
1058
1059 /* needed so 'click picture' tool knows how big the pic is; we don't
1060 * store it in cp because they may have been freed already. */
1061 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1062 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1063
1064 g_queue_push_head ( dp->vtl->image_cache, cp );
1065 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1066 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1067
1068 pixbuf = cp->pixbuf;
1069 }
1070 else
1071 {
1072 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1073 }
1074 }
1075 if ( pixbuf )
1076 {
1077 gint w, h;
1078 w = gdk_pixbuf_get_width ( pixbuf );
1079 h = gdk_pixbuf_get_height ( pixbuf );
1080
1081 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1082 {
1083 if ( dp->vtl->image_alpha == 255 )
1084 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1085 else
1086 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1087 }
1088 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1089 }
1090 }
1091
1092 /* DRAW ACTUAL DOT */
ea3933fc 1093 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
acaf7113
AF
1094 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 );
1095 }
1096 else if ( wp == dp->vtl->current_wp ) {
50a14534
EB
1097 switch ( dp->vtl->wp_symbol ) {
1098 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;
1099 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;
1100 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;
1101 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 );
1102 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 );
1103 }
1104 }
1105 else {
1106 switch ( dp->vtl->wp_symbol ) {
1107 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;
1108 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;
1109 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;
1110 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 );
1111 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;
1112 }
1113 }
1114
1115 if ( dp->vtl->drawlabels )
1116 {
1117 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1118 gint width, height;
1119 pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 );
1120 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1121 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, x + dp->vtl->wp_size - 1, y-1,width+1,height-1);
1122 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, x + dp->vtl->wp_size, y, dp->vtl->wplabellayout );
1123 }
1124 }
1125}
1126
1127void vik_trw_layer_draw ( VikTrwLayer *l, gpointer data )
1128{
1129 static struct DrawingParams dp;
1130 g_assert ( l != NULL );
1131
1132 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1133 dp.vtl = l;
1134
1135 if ( l->tracks_visible )
1136 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1137
1138 if (l->waypoints_visible)
1139 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1140}
1141
1142static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1143{
1144 int i;
1145 if ( vtl->track_bg_gc )
1146 {
1147 g_object_unref ( vtl->track_bg_gc );
1148 vtl->track_bg_gc = NULL;
1149 }
1150
1151 if ( ! vtl->track_gc )
1152 return;
1153 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1154 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1155 g_array_free ( vtl->track_gc, TRUE );
1156 vtl->track_gc = NULL;
1157}
1158
1159static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1160{
1161 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1162 gint width = vtl->line_thickness;
1163
1164 if ( vtl->track_gc )
1165 trw_layer_free_track_gcs ( vtl );
1166
1167 if ( vtl->track_bg_gc )
1168 g_object_unref ( vtl->track_bg_gc );
1169 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1170
1171 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1172
1173 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1174
1175 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1176 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1177 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1178 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1179 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1180 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1181 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1182 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1183 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1184 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1185
1186 gc[11] = vik_viewport_new_gc ( vp, "#ff0000", width ); /* above range */
1187
1188 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1189
1190 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1191}
1192
1193VikTrwLayer *vik_trw_layer_create ( VikViewport *vp )
1194{
1195 VikTrwLayer *rv = vik_trw_layer_new ( 0 );
1196 PangoFontDescription *pfd;
1197 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1198 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1199 pango_layout_set_font_description (rv->wplabellayout, pfd);
1200 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1201 pango_font_description_free (pfd);
1202
1203 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1204
1205 trw_layer_new_track_gcs ( rv, vp );
1206
1207 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1208 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1209 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1210 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1211
1212 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1213
1214 rv->has_verified_thumbnails = FALSE;
1215 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1216 rv->wp_size = 4;
ea3933fc 1217 rv->wp_draw_symbols = TRUE;
50a14534
EB
1218
1219 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1220
1221 return rv;
1222}
1223
1224static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[4] )
1225{
1226 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1227
1228#ifdef VIK_CONFIG_ALPHABETIZED_TRW
1229 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 );
1230#else
1231 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 );
1232#endif
1233
1234 *new_iter = *((GtkTreeIter *) pass_along[1]);
1235 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1236
1237 if ( ! track->visible )
1238 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1239}
1240
1241static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[4] )
1242{
1243 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1244#ifdef VIK_CONFIG_ALPHABETIZED_TRW
1245 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 );
1246#else
1247 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 );
1248#endif
1249
1250 *new_iter = *((GtkTreeIter *) pass_along[1]);
1251 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1252
1253 if ( ! wp->visible )
1254 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1255}
1256
1257
1258void vik_trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1259{
1260 GtkTreeIter iter2;
1261 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1262
1263#ifdef VIK_CONFIG_ALPHABETIZED_TRW
1264 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), "Tracks", vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1265#else
1266 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), "Tracks", vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1267#endif
1268 if ( ! vtl->tracks_visible )
1269 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1270
1271 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1272
1273#ifdef VIK_CONFIG_ALPHABETIZED_TRW
1274 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), "Waypoints", vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1275#else
1276 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), "Waypoints", vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1277#endif
1278
1279 if ( ! vtl->waypoints_visible )
1280 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1281
1282 pass_along[0] = &(vtl->waypoints_iter);
1283 pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1284
1285 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1286
1287}
1288
1289gboolean vik_trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1290{
1291 switch ( subtype )
1292 {
1293 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1294 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1295 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1296 {
1297 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1298 if (t)
1299 return (t->visible ^= 1);
1300 else
1301 return TRUE;
1302 }
1303 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1304 {
1305 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1306 if (t)
1307 return (t->visible ^= 1);
1308 else
1309 return TRUE;
1310 }
1311 }
1312 return TRUE;
1313}
1314
1315GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
1316{
1317 return l->tracks;
1318}
1319
1320GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
1321{
1322 return l->waypoints;
1323}
1324
1325static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
1326{
1327 static VikCoord fixme;
1328 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
1329 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1330 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1331 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1332 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1333 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1334 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1335 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1336 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1337}
1338
1339static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
1340{
1341 GList *tr = *t;
1342 static VikCoord fixme;
1343
1344 while ( tr )
1345 {
1346 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
1347 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1348 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1349 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1350 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1351 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1352 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1353 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1354 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1355 tr = tr->next;
1356 }
1357}
1358
1359
1360gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
1361{
1362 /* 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... */
1363 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1364 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
1365 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
1366 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1367 return FALSE;
1368 else
1369 {
1370 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1371 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
1372 return TRUE;
1373 }
1374}
1375
1376static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
1377{
1378 VikCoord coord;
1379 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
1380 goto_coord ( VIK_LAYERS_PANEL(layer_and_vlp[1]), &coord );
1381 else
1382 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), "This layer has no waypoints or trackpoints." );
1383}
1384
1385static void trw_layer_export ( gpointer layer_and_vlp[2], guint file_type )
1386{
1387 GtkWidget *file_selector;
1388 const gchar *fn;
1389 gboolean failed = FALSE;
1390 file_selector = gtk_file_selection_new ("Export Layer");
1391
1392 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_OK )
1393 {
1394 fn = gtk_file_selection_get_filename (GTK_FILE_SELECTION(file_selector) );
1395 if ( access ( fn, F_OK ) != 0 )
1396 {
1397 gtk_widget_hide ( file_selector );
1398 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type );
1399 break;
1400 }
1401 else
1402 {
1403 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 ) ) )
1404 {
1405 gtk_widget_hide ( file_selector );
1406 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type );
1407 break;
1408 }
1409 }
1410 }
1411 gtk_widget_destroy ( file_selector );
1412 if ( failed )
1413 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), "The filename you requested could not be opened for writing." );
1414}
1415
1416static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
1417{
1418 trw_layer_export ( layer_and_vlp, FILE_TYPE_GPSPOINT );
1419}
1420
1421static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
1422{
1423 trw_layer_export ( layer_and_vlp, FILE_TYPE_GPSMAPPER );
1424}
1425
561e6ad0
EB
1426static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
1427{
1428 trw_layer_export ( layer_and_vlp, FILE_TYPE_GPX );
1429}
1430
50a14534
EB
1431static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
1432{
1433 GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
1434 GtkWidget *dia = gtk_dialog_new_with_buttons ("Create",
1435 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
1436 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1437 GTK_STOCK_CANCEL,
1438 GTK_RESPONSE_REJECT,
1439 GTK_STOCK_OK,
1440 GTK_RESPONSE_ACCEPT,
1441 NULL);
1442
1443 GtkWidget *label, *entry;
1444 label = gtk_label_new("Waypoint Name:");
1445 entry = gtk_entry_new();
1446
1447 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
1448 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
1449 gtk_widget_show_all ( label );
1450 gtk_widget_show_all ( entry );
1451
1452 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
1453 {
1454 VikWaypoint *wp;
1455 gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
1456 int i;
1457
1458 for ( i = strlen(upname)-1; i >= 0; i-- )
1459 upname[i] = toupper(upname[i]);
1460
1461 wp = g_hash_table_lookup ( wps, upname );
1462
1463 if (!wp)
1464 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), "Waypoint not found in this layer." );
1465 else
1466 {
1467 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
1468 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
1469 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 ) );
1470 break;
1471 }
1472
1473 g_free ( upname );
1474
1475 }
1476 gtk_widget_destroy ( dia );
1477}
1478
1479gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
1480{
1481 gchar *name;
acaf7113
AF
1482 VikWaypoint *wp = vik_waypoint_new();
1483 wp->coord = *def_coord;
1484 wp->altitude = VIK_DEFAULT_ALTITUDE;
50a14534 1485
acaf7113 1486 if ( a_dialog_new_waypoint ( w, &name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode ) )
50a14534 1487 {
805d282e 1488 wp->visible = TRUE;
50a14534
EB
1489 vik_trw_layer_add_waypoint ( vtl, name, wp );
1490 return TRUE;
1491 }
acaf7113 1492 vik_waypoint_free(wp);
50a14534
EB
1493 return FALSE;
1494}
1495
1496static void trw_layer_new_wp ( gpointer lav[2] )
1497{
1498 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
1499 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
1500 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
1501 instead return true if you want to update. */
1502 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 )
1503 vik_layers_panel_emit_update ( vlp );
1504}
1505
1506void vik_trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
1507{
1508 static gpointer pass_along[2];
1509 GtkWidget *item;
1510 pass_along[0] = vtl;
1511 pass_along[1] = vlp;
1512
1513 item = gtk_menu_item_new();
1514 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1515 gtk_widget_show ( item );
1516
1517 item = gtk_menu_item_new_with_label ( "Goto Center of Layer" );
1518 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
1519 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1520 gtk_widget_show ( item );
1521
1522 item = gtk_menu_item_new_with_label ( "Goto Waypoint" );
1523 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
1524 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1525 gtk_widget_show ( item );
1526
1527 item = gtk_menu_item_new_with_label ( "Export Layer as GPSPoint" );
1528 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
1529 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1530 gtk_widget_show ( item );
1531
1532 item = gtk_menu_item_new_with_label ( "Export Layer as GPSMapper" );
1533 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
1534 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1535 gtk_widget_show ( item );
1536
561e6ad0
EB
1537 item = gtk_menu_item_new_with_label ( "Export Layer as GPX" );
1538 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
1539 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1540 gtk_widget_show ( item );
1541
50a14534
EB
1542 item = gtk_menu_item_new_with_label ( "New Waypoint" );
1543 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
1544 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1545 gtk_widget_show ( item );
1546}
1547
1548void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
1549{
1550 if ( VIK_LAYER(vtl)->realized )
1551 {
1552 VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
1553 if ( oldwp )
1554 wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
1555 else
1556 {
1557 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
1558#ifdef VIK_CONFIG_ALPHABETIZED_TRW
1559 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
1560#else
1561 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
1562#endif
1563 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
1564 g_hash_table_insert ( vtl->waypoints_iters, name, iter );
1565 wp->visible = TRUE;
1566 }
1567 }
1568 else
1569 wp->visible = TRUE;
1570
1571 g_hash_table_insert ( vtl->waypoints, name, wp );
1572
1573}
1574
1575void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
1576{
1577 if ( VIK_LAYER(vtl)->realized )
1578 {
1579 VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
1580 if ( oldt )
1581 t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
1582 else
1583 {
1584 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
1585#ifdef VIK_CONFIG_ALPHABETIZED_TRW
1586 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
1587#else
1588 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
1589#endif
1590 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
1591 g_hash_table_insert ( vtl->tracks_iters, name, iter );
805d282e 1592 /* t->visible = TRUE; */
50a14534
EB
1593 }
1594 }
1595 else
805d282e 1596 ; /* t->visible = TRUE; // this is now used by file input functions */
50a14534
EB
1597
1598 g_hash_table_insert ( vtl->tracks, name, t );
1599
1600}
1601
1602static gboolean uppercase_exists_in_hash ( GHashTable *hash, const gchar *str )
1603{
1604 gchar *upp = g_strdup ( str );
1605 gboolean rv;
1606 char *tmp = upp;
1607 while ( *tmp )
1608 {
1609 *tmp = toupper(*tmp);
1610 tmp++;
1611 }
1612 rv = g_hash_table_lookup ( hash, upp ) ? TRUE : FALSE;
1613 g_free (upp);
1614 return rv;
1615}
1616
1617/* to be called whenever a track has been deleted or may have been changed. */
1618static void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
1619{
1620 if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
1621 trw_layer_cancel_current_tp ( vtl, FALSE );
1622 else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
1623 trw_layer_cancel_last_tp ( vtl );
1624}
e890a6e6
EB
1625
1626static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
1627{
1628 gint i = 2;
1629 gchar *newname = g_strdup(name);
1630 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
941aa6e9 1631 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
e890a6e6
EB
1632 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
1633 g_free(newname);
1634 newname = new_newname;
1635 i++;
1636 }
1637 return newname;
1638}
50a14534 1639
805d282e
EB
1640void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
1641{
1642 vik_trw_layer_add_waypoint ( vtl,
1643 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
1644 wp );
1645}
1646void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
1647{
1648 vik_trw_layer_add_track ( vtl,
1649 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name),
1650 tr );
1651}
1652
c48517ad
AF
1653static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
1654{
1655 *l = g_list_append(*l, (gpointer)name);
1656}
1657
1658static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
1659{
1660 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
1661 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
1662 VikTrack *t;
1663 t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
1664 vik_trw_layer_delete_track(vtl_src, name);
1665 vik_trw_layer_add_track(vtl_dest, newname, t);
1666 }
1667 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
1668 VikWaypoint *w;
1669 w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
1670 vik_trw_layer_delete_waypoint(vtl_src, name);
1671 vik_trw_layer_add_waypoint(vtl_dest, newname, w);
1672 }
1673}
1674
70a23263 1675static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
e4afc73a
EB
1676{
1677 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
c48517ad
AF
1678 gint type = vik_treeview_item_get_data(vt, src_item_iter);
1679
e4afc73a 1680 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
b637058e
EB
1681 GList *items = NULL;
1682 GList *iter;
e4afc73a 1683
c48517ad
AF
1684 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
1685 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
1686 }
1687 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
1688 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
1689 }
1690
1691 iter = items;
1692 while (iter) {
1693 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
1694 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
1695 } else {
1696 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1697 }
1698 iter = iter->next;
e4afc73a 1699 }
c48517ad 1700 if (items)
b637058e 1701 g_list_free(items);
c48517ad
AF
1702 } else {
1703 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
1704 trw_layer_move_item(vtl_src, vtl_dest, name, type);
e4afc73a
EB
1705 }
1706}
1707
1708
1709gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
50a14534
EB
1710{
1711 VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
1712 gboolean was_visible = FALSE;
1713 if ( t )
1714 {
1715 GtkTreeIter *it;
1716 was_visible = t->visible;
1717 if ( t == vtl->current_track )
1718 vtl->current_track = NULL;
1719
1720 /* could be current_tp, so we have to check */
1721 trw_layer_cancel_tps_of_track ( vtl, trk_name );
1722
1723 g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
1724 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
1725 g_hash_table_remove ( vtl->tracks_iters, trk_name );
1726
1727 /* do this last because trk_name may be pointing to actual orig key */
1728 g_hash_table_remove ( vtl->tracks, trk_name );
1729 }
1730 return was_visible;
1731}
1732
e4afc73a
EB
1733gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
1734{
1735 gboolean was_visible = FALSE;
1736 VikWaypoint *wp;
1737
1738 wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
1739 if ( wp ) {
1740 GtkTreeIter *it;
1741
1742 if ( wp == vtl->current_wp ) {
1743 vtl->current_wp = NULL;
1744 vtl->current_wp_name = NULL;
1745 vtl->moving_wp = FALSE;
1746 }
1747
1748 was_visible = wp->visible;
1749 g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
1750 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
1751 g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
1752 g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
1753 }
1754
1755 return was_visible;
1756}
1757
50a14534
EB
1758static void trw_layer_delete_item ( gpointer pass_along[5] )
1759{
1760 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
1761 gboolean was_visible = FALSE;
1762 if ( (gint) pass_along[2] == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
1763 {
e4afc73a 1764 was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
50a14534
EB
1765 }
1766 else
1767 {
e4afc73a 1768 was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
50a14534 1769 }
50a14534
EB
1770 if ( was_visible )
1771 vik_layer_emit_update ( VIK_LAYER(vtl) );
1772}
1773
1774
1775static void trw_layer_properties_item ( gpointer pass_along[5] )
1776{
1777 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
1778 if ( (gint) pass_along[2] == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
1779 {
1780 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
1781 if ( wp )
1782 {
1783 if ( a_dialog_new_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), NULL, wp, NULL, vtl->coord_mode ) )
1784
1785 if ( VIK_LAYER(vtl)->visible )
1786 vik_layer_emit_update ( VIK_LAYER(vtl) );
1787 }
1788 }
1789 else
1790 {
1791 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
1792 if ( tr )
1793 {
24d5c7e2 1794 gint resp = vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl), tr, pass_along[1] /* vlp */ );
50a14534
EB
1795 if ( resp == VIK_TRW_LAYER_PROPWIN_DEL_DUP )
1796 {
1797 vik_track_remove_dup_points(tr);
1798 /* above operation could have deleted current_tp or last_tp */
1799 trw_layer_cancel_tps_of_track ( vtl, (gchar *) pass_along[3] );
1800 vik_layer_emit_update ( VIK_LAYER(vtl) );
1801 }
1802 if ( resp == VIK_TRW_LAYER_PROPWIN_REVERSE )
1803 {
1804 vik_track_reverse(tr);
1805 vik_layer_emit_update ( VIK_LAYER(vtl) );
1806 }
1807 else if ( resp == VIK_TRW_LAYER_PROPWIN_SPLIT )
1808 {
1809 /* get new tracks, add them, resolve naming conflicts (free if cancel), and delete old. old can still exist on clipboard. */
1810 guint ntracks;
1811 VikTrack **tracks = vik_track_split_into_segments(tr, &ntracks);
1812 gchar *new_tr_name;
1813 guint i;
1814 for ( i = 0; i < ntracks; i++ )
1815 {
1816 g_assert ( tracks[i] );
1817 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i+1);
1818 /* if ( (wp_exists) && (! overwrite) ) */
1819 /* don't need to upper case new_tr_name because old tr name was uppercase */
1820 if ( g_hash_table_lookup ( vtl->tracks, new_tr_name ) &&
1821 ( ! a_dialog_overwrite ( VIK_GTK_WINDOW_FROM_LAYER(vtl), "The track \"%s\" exists, do you wish to overwrite it?", new_tr_name ) ) )
1822 {
1823 gchar *new_new_tr_name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks );
1824 g_free ( new_tr_name );
1825 if (new_new_tr_name)
1826 new_tr_name = new_new_tr_name;
1827 else
1828 {
1829 new_tr_name = NULL;
1830 vik_track_free ( tracks[i] );
1831 }
1832 }
1833 if ( new_tr_name )
1834 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
50a14534
EB
1835 }
1836 if ( tracks )
1837 {
1838 g_free ( tracks );
e4afc73a 1839 vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
50a14534
EB
1840 vik_layer_emit_update ( VIK_LAYER(vtl) ); /* chase thru the hoops */
1841 }
1842 }
1843 }
1844 }
1845}
1846
1847static void goto_coord ( VikLayersPanel *vlp, const VikCoord *coord )
1848{
1849 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), coord );
1850 vik_layers_panel_emit_update ( vlp );
1851}
1852
1853static void trw_layer_goto_track_startpoint ( gpointer pass_along[5] )
1854{
1855 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
1856 if ( trps && trps->data )
1857 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord));
1858}
1859
1860static void trw_layer_goto_track_center ( gpointer pass_along[5] )
1861{
1862 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
1863 if ( trps && *trps )
1864 {
1865 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1866 VikCoord coord;
1867 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
1868 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1869 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1870 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
1871 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &coord);
1872 }
1873}
1874
1875static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
1876{
1877 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
1878 if ( !trps )
1879 return;
111fa174 1880 trps = g_list_last(trps);
50a14534
EB
1881 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord));
1882}
1883
111fa174
AF
1884
1885/*************************************
1886 * merge/split by time routines
1887 *************************************/
1888
1889/* called for each key in track hash table. if original track user_data[1] is close enough
1890 * to the passed one, add it to list in user_data[0]
1891 */
1892static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
1893{
1894 time_t t1, t2;
1895 VikTrackpoint *p1, *p2;
1896
1897 GList **nearby_tracks = ((gpointer *)user_data)[0];
1898 GList *orig_track = ((gpointer *)user_data)[1];
1899 guint thr = (guint)((gpointer *)user_data)[2];
1900
1901 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
1902 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
1903
1904 if (VIK_TRACK(value)->trackpoints == orig_track) {
1905 return;
1906 }
1907
1908 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
1909 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
1910
1911 if (!p1->has_timestamp || !p2->has_timestamp) {
70a23263 1912 g_print("no timestamp\n");
111fa174
AF
1913 return;
1914 }
1915
70a23263 1916 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
111fa174
AF
1917 if (abs(t1 - p2->timestamp) < thr*60 ||
1918 /* p1 p2 t1 t2 */
1919 abs(p1->timestamp - t2) < thr*60
1920 /* t1 t2 p1 p2 */
1921 ) {
1922 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
1923 }
1924}
1925
1926/* comparison function used to sort tracks; a and b are hash table keys */
1927static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
1928{
1929 GHashTable *tracks = user_data;
1930 time_t t1, t2;
1931
1932 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
1933 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
1934
1935 if (t1 < t2) return -1;
1936 if (t1 > t2) return 1;
1937 return 0;
1938}
1939
1940/* comparison function used to sort trackpoints */
1941static gint trackpoint_compare(gconstpointer a, gconstpointer b)
1942{
1943 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
1944
1945 if (t1 < t2) return -1;
1946 if (t1 > t2) return 1;
1947 return 0;
1948}
1949
1950/* merge by time routine */
1951static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
1952{
1953 time_t t1, t2;
1954 GList *nearby_tracks = NULL;
1955 VikTrack *track;
1956 GList *trps;
1957 static guint thr = 1;
1958 guint track_count = 0;
1959 gchar *orig_track_name = strdup(pass_along[3]);
1960
1961 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
1962 "Merge Threshold...",
1963 "Merge when time between trackpoints less than:",
1964 &thr)) {
1965 return;
1966 }
1967
1968 /* merge tracks until we can't */
1969 do {
1970 gpointer params[3];
1971
1972 track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, orig_track_name );
1973 trps = track->trackpoints;
1974 if ( !trps )
1975 return;
1976
1977
1978 if (nearby_tracks) {
1979 g_list_free(nearby_tracks);
1980 nearby_tracks = NULL;
1981 }
1982
1983 t1 = ((VikTrackpoint *)trps->data)->timestamp;
1984 t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
1985
70a23263 1986 /* g_print("Original track times: %d and %d\n", t1, t2); */
111fa174
AF
1987 params[0] = &nearby_tracks;
1988 params[1] = trps;
1989 params[2] = (gpointer)thr;
1990
1991 /* get a list of adjacent-in-time tracks */
1992 g_hash_table_foreach(VIK_TRW_LAYER(pass_along[0])->tracks, find_nearby_track, (gpointer)params);
1993
1994 /* add original track */
1995 nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
1996
1997 /* sort by first trackpoint; assumes they don't overlap */
1998 nearby_tracks = g_list_sort_with_data(nearby_tracks, track_compare, VIK_TRW_LAYER(pass_along[0])->tracks);
1999
2000 /* merge them */
2001 {
2002#define get_track(x) VIK_TRACK(g_hash_table_lookup(VIK_TRW_LAYER(pass_along[0])->tracks, (gchar *)((x)->data)))
2003#define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
2004#define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
2005 GList *l = nearby_tracks;
2006 VikTrack *tr = vik_track_new();
2007 tr->visible = track->visible;
2008 track_count = 0;
2009 while (l) {
2010 /*
2011 time_t t1, t2;
2012 t1 = get_first_trackpoint(l)->timestamp;
2013 t2 = get_last_trackpoint(l)->timestamp;
70a23263 2014 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
111fa174
AF
2015 */
2016
2017
2018 /* remove trackpoints from merged track, delete track */
2019 tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
2020 get_track(l)->trackpoints = NULL;
e4afc73a 2021 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), l->data);
111fa174
AF
2022
2023 track_count ++;
2024 l = g_list_next(l);
2025 }
2026 tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
2027 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), strdup(orig_track_name), tr);
2028
2029#undef get_first_trackpoint
2030#undef get_last_trackpoint
2031#undef get_track
2032 }
2033 } while (track_count > 1);
2034 g_list_free(nearby_tracks);
2035 free(orig_track_name);
2036 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
2037}
2038
2039/* split by time routine */
2040static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
2041{
2042 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2043 GList *trps = track->trackpoints;
2044 GList *iter;
2045 GList *newlists = NULL;
2046 GList *newtps = NULL;
2047 guint i;
2048 static guint thr = 1;
2049
2050 time_t ts, prev_ts;
2051
2052 if ( !trps )
2053 return;
2054
2055 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
2056 "Split Threshold...",
2057 "Split when time between trackpoints exceeds:",
2058 &thr)) {
2059 return;
2060 }
2061
2062 /* iterate through trackpoints, and copy them into new lists without touching original list */
2063 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
2064 iter = trps;
2065
2066 while (iter) {
2067 ts = VIK_TRACKPOINT(iter->data)->timestamp;
2068 if (ts < prev_ts) {
70a23263 2069 g_print("panic: ts < prev_ts: this should never happen!\n");
111fa174
AF
2070 return;
2071 }
2072 if (ts - prev_ts > thr*60) {
2073 /* flush accumulated trackpoints into new list */
2074 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
2075 newtps = NULL;
2076 }
2077
2078 /* accumulate trackpoint copies in newtps, in reverse order */
2079 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
2080 prev_ts = ts;
2081 iter = g_list_next(iter);
2082 }
2083 if (newtps) {
2084 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
2085 }
2086
2087 /* put lists of trackpoints into tracks */
2088 iter = newlists;
2089 i = 1;
2090 while (iter) {
2091 gchar *new_tr_name;
2092 VikTrack *tr;
2093
2094 tr = vik_track_new();
2095 tr->visible = track->visible;
2096 tr->trackpoints = (GList *)(iter->data);
2097
2098 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
2099 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
70a23263 2100 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
111fa174
AF
2101 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
2102
2103 iter = g_list_next(iter);
2104 }
2105 g_list_free(newlists);
e4afc73a 2106 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
111fa174
AF
2107 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
2108}
2109
2110/* end of split/merge routines */
2111
2112
50a14534
EB
2113static void trw_layer_goto_waypoint ( gpointer pass_along[5] )
2114{
2115 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2116 if ( wp )
2117 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(wp->coord) );
2118}
2119
2120static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[5] )
2121{
2122 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
2123#ifdef WINDOWS
2124 ShellExecute(NULL, NULL, (char *) webpage, NULL, ".\\", 0);
2125#else /* WINDOWS */
2126 GError *err = NULL;
2127 gchar *cmd = g_strdup_printf ( "%s %s", UNIX_WEB_BROWSER, webpage );
2128 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2129 {
2130 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), "Could not launch web browser." );
2131 g_error_free ( err );
2132 }
2133 g_free ( cmd );
2134#endif /* WINDOWS */
2135 g_free ( webpage );
2136}
2137
2138const gchar *vik_trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
2139{
2140 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2141 {
2142 int i;
2143 gchar *rv;
2144 VikWaypoint *wp;
2145
2146 if ( strcasecmp ( newname, sublayer ) == 0 )
2147 return NULL;
2148
2149 if ( uppercase_exists_in_hash ( l->waypoints, newname ) )
2150 {
2151 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), "Waypoint Already Exists" );
2152 return NULL;
2153 }
2154
50a14534
EB
2155 iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
2156 g_hash_table_steal ( l->waypoints_iters, sublayer );
2157
e4afc73a
EB
2158 wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
2159 g_hash_table_remove ( l->waypoints, sublayer );
2160
50a14534
EB
2161 rv = g_strdup(newname);
2162 for ( i = strlen(rv) - 1; i >= 0; i-- )
2163 rv[i] = toupper(rv[i]);
2164
2165 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
2166
2167 g_hash_table_insert ( l->waypoints, rv, wp );
2168 g_hash_table_insert ( l->waypoints_iters, rv, iter );
2169
2170 /* it hasn't been updated yet so we pass new name */
2171#ifdef VIK_CONFIG_ALPHABETIZED_TRW
2172 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
2173#endif
2174
2175 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2176 return rv;
2177 }
2178 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2179 {
2180 int i;
2181 gchar *rv;
2182 VikTrack *tr;
2183 GtkTreeIter *iter;
2184 gchar *orig_key;
2185
2186 if ( strcasecmp ( newname, sublayer ) == 0 )
2187 return NULL;
2188
2189 if ( uppercase_exists_in_hash ( l->tracks, newname ) )
2190 {
2191 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), "Track Already Exists" );
2192 return NULL;
2193 }
2194
886031df 2195 g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
50a14534
EB
2196 g_hash_table_steal ( l->tracks, sublayer );
2197
2198 iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
2199 g_hash_table_steal ( l->tracks_iters, sublayer );
2200
2201 rv = g_strdup(newname);
2202 for ( i = strlen(rv) - 1; i >= 0; i-- )
2203 rv[i] = toupper(rv[i]);
2204
2205 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
2206
2207 g_hash_table_insert ( l->tracks, rv, tr );
2208 g_hash_table_insert ( l->tracks_iters, rv, iter );
2209
2210 /* don't forget about current_tp_track_name, update that too */
2211 if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
2212 {
2213 l->current_tp_track_name = rv;
2214 if ( l->tpwin )
2215 vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
2216 }
2217 else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
2218 l->last_tp_track_name = rv;
2219
2220 g_free ( orig_key );
2221
2222#ifdef VIK_CONFIG_ALPHABETIZED_TRW
2223 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
2224#endif
2225
2226 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2227 return rv;
2228 }
2229 return NULL;
2230}
2231
2232static gboolean is_valid_geocache_name ( gchar *str )
2233{
2234 gint len = strlen ( str );
2235 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]));
2236}
2237
2238/* vlp can be NULL if necessary - i.e. right-click from a tool -- but be careful, some functions may try to use it */
2239gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
2240{
2241 static GtkTreeIter staticiter;
2242 static gpointer pass_along[5];
2243 GtkWidget *item;
2244 gboolean rv = FALSE;
2245
2246 pass_along[0] = l;
2247 pass_along[1] = vlp;
2248 pass_along[2] = (gpointer) subtype;
2249 pass_along[3] = sublayer;
2250 staticiter = *iter; /* will exist after function has ended */
2251 pass_along[4] = &staticiter;
2252
2253 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2254 {
2255 rv = TRUE;
2256
2257 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
2258 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
2259 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2260 gtk_widget_show ( item );
2261
2262 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
2263 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
2264 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2265 gtk_widget_show ( item );
2266
2267 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2268 {
2269 /* could be a right-click using the tool */
2270 if ( vlp != NULL ) {
2271 item = gtk_menu_item_new_with_label ( "Goto" );
2272 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
2273 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2274 gtk_widget_show ( item );
2275 }
2276
2277 if ( is_valid_geocache_name ( (gchar *) sublayer ) )
2278 {
2279 item = gtk_menu_item_new_with_label ( "Visit Geocache Webpage" );
2280 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
2281 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2282 gtk_widget_show ( item );
2283 }
2284
2285 }
2286 }
2287
2288 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2289 {
2290 item = gtk_menu_item_new ();
2291 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2292 gtk_widget_show ( item );
2293
2294 item = gtk_menu_item_new_with_label ( "Goto Startpoint" );
2295 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
2296 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2297 gtk_widget_show ( item );
2298
2299 item = gtk_menu_item_new_with_label ( "Goto \"Center\"" );
2300 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
2301 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2302 gtk_widget_show ( item );
2303
2304 item = gtk_menu_item_new_with_label ( "Goto Endpoint" );
2305 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
2306 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2307 gtk_widget_show ( item );
111fa174
AF
2308
2309 item = gtk_menu_item_new_with_label ( "Merge By Time" );
2310 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
2311 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2312 gtk_widget_show ( item );
2313
2314 item = gtk_menu_item_new_with_label ( "Split By Time" );
2315 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
2316 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2317 gtk_widget_show ( item );
50a14534
EB
2318 }
2319
2320 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
2321 {
2322 item = gtk_menu_item_new ();
2323 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2324 gtk_widget_show ( item );
2325
2326 item = gtk_menu_item_new_with_label ( "New Waypoint" );
2327 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2328 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2329 gtk_widget_show ( item );
2330 }
2331
2332 return rv;
2333}
2334
50a14534
EB
2335
2336/* to be called when last_tpl no long exists. */
2337static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
2338{
2339 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
2340 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
2341 vtl->last_tpl = NULL;
2342 vtl->last_tp_track_name = NULL;
2343}
2344
2345static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
2346{
2347 if ( vtl->tpwin )
2348 {
2349 if ( destroy)
2350 {
2351 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
2352 vtl->tpwin = NULL;
2353 }
2354 else
2355 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
2356 }
2357 if ( vtl->current_tpl )
2358 {
2359 vtl->current_tpl = NULL;
2360 vtl->current_tp_track_name = NULL;
2361 vik_layer_emit_update(VIK_LAYER(vtl));
2362 }
2363}
2364
2365static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
2366{
2367 g_assert ( vtl->tpwin != NULL );
2368 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
2369 trw_layer_cancel_current_tp ( vtl, TRUE );
2370 else if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
2371 {
2372 gchar *name;
2373 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks ) ) )
2374 {
2375 VikTrack *tr = vik_track_new ();
2376 GList *newglist = g_list_alloc ();
2377 newglist->prev = NULL;
2378 newglist->next = vtl->current_tpl->next;
2379 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
2380 tr->trackpoints = newglist;
2381
2382 vtl->current_tpl->next->prev = newglist; /* end old track here */
2383 vtl->current_tpl->next = NULL;
2384
2385 vtl->current_tpl = newglist; /* change tp to first of new track. */
2386 vtl->current_tp_track_name = name;
2387
2388 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
2389
2390 vik_trw_layer_add_track ( vtl, name, tr );
2391 vik_layer_emit_update(VIK_LAYER(vtl));
2392 }
2393 }
2394 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
2395 {
2396 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
2397 GList *new_tpl;
2398 g_assert(tr != NULL);
2399
2400 /* can't join with a non-existent trackpoint */
2401 vtl->last_tpl = NULL;
2402 vtl->last_tp_track_name = NULL;
2403
2404 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
2405 {
2406 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
2407 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
2408
2409 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
2410
2411 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
2412 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
2413
2414 trw_layer_cancel_last_tp ( vtl );
2415
2416 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
2417 g_list_free_1 ( vtl->current_tpl );
2418 vtl->current_tpl = new_tpl;
2419 vik_layer_emit_update(VIK_LAYER(vtl));
2420 }
2421 else
2422 {
2423 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
2424 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
2425 g_list_free_1 ( vtl->current_tpl );
2426 trw_layer_cancel_current_tp ( vtl, FALSE );
2427 }
2428 }
2429 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
2430 {
2431 vtl->last_tpl = vtl->current_tpl;
2432 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
2433 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
2434 }
2435 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
2436 {
2437 vtl->last_tpl = vtl->current_tpl;
2438 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
2439 vik_layer_emit_update(VIK_LAYER(vtl));
2440 }
2441 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
2442 {
2443 VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
2444 VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
2445
2446 VikTrack *tr_first = tr1, *tr_last = tr2;
2447
2448 gchar *tmp;
2449
2450 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
2451 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
2452 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
2453 vik_track_reverse ( tr1 );
2454 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
2455 {
2456 tr_first = tr2;
2457 tr_last = tr1;
2458 }
2459 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
2460
2461 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. */
2462 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
2463 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
2464 tr2->trackpoints = NULL;
2465
2466 tmp = vtl->current_tp_track_name;
2467
2468 vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
2469 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
2470
2471 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
2472 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
e4afc73a 2473 vik_trw_layer_delete_track ( vtl, tmp );
50a14534
EB
2474
2475 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
2476 vik_layer_emit_update(VIK_LAYER(vtl));
2477 }
2478 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
2479 vik_layer_emit_update (VIK_LAYER(vtl));
2480}
2481
2482static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
2483{
2484 if ( ! vtl->tpwin )
2485 {
2486 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
2487 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
2488 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
2489 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
2490 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
2491 }
2492 if ( vtl->current_tpl )
2493 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
2494 /* set layer name and TP data */
2495}
2496
941aa6e9
AF
2497/***************************************************************************
2498 ** Tool code
2499 ***************************************************************************/
50a14534 2500
941aa6e9 2501/*** Utility data structures and functions ****/
50a14534
EB
2502
2503typedef struct {
2504 gint x, y;
2505 gint closest_x, closest_y;
2506 gchar *closest_wp_name;
2507 VikWaypoint *closest_wp;
2508 VikViewport *vvp;
2509} WPSearchParams;
2510
941aa6e9
AF
2511typedef struct {
2512 gint x, y;
2513 gint closest_x, closest_y;
2514 gchar *closest_track_name;
2515 VikTrackpoint *closest_tp;
2516 VikViewport *vvp;
2517 GList *closest_tpl;
2518} TPSearchParams;
2519
50a14534
EB
2520static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
2521{
2522 gint x, y;
2523 if ( !wp->visible )
2524 return;
2525
2526 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
2527
2528 if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
2529 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
2530 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
2531 {
2532 params->closest_wp_name = name;
2533 params->closest_wp = wp;
2534 params->closest_x = x;
2535 params->closest_y = y;
2536 }
2537}
2538
941aa6e9 2539static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
50a14534 2540{
941aa6e9
AF
2541 GList *tpl = t->trackpoints;
2542 VikTrackpoint *tp;
50a14534 2543
941aa6e9
AF
2544 if ( !t->visible )
2545 return;
50a14534 2546
941aa6e9
AF
2547 while (tpl)
2548 {
2549 gint x, y;
2550 tp = VIK_TRACKPOINT(tpl->data);
2551
2552 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
2553
2554 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
2555 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
2556 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
50a14534 2557 {
941aa6e9
AF
2558 params->closest_track_name = name;
2559 params->closest_tp = tp;
2560 params->closest_tpl = tpl;
2561 params->closest_x = x;
2562 params->closest_y = y;
50a14534 2563 }
941aa6e9 2564 tpl = tpl->next;
50a14534 2565 }
941aa6e9
AF
2566}
2567
2568static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
2569{
2570 TPSearchParams params;
2571 params.x = x;
2572 params.y = y;
2573 params.vvp = vvp;
2574 params.closest_track_name = NULL;
2575 params.closest_tp = NULL;
2576 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
2577 return params.closest_tp;
50a14534
EB
2578}
2579
2580static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
2581{
2582 WPSearchParams params;
2583 params.x = x;
2584 params.y = y;
2585 params.vvp = vvp;
2586 params.closest_wp = NULL;
2587 params.closest_wp_name = NULL;
2588 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
2589 return params.closest_wp;
2590}
2591
941aa6e9
AF
2592/*** Edit waypoint ****/
2593
2594static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
2595{
2596 return vvp;
2597}
2598
2599static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
50a14534
EB
2600{
2601 WPSearchParams params;
2602
941aa6e9
AF
2603 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
2604 return FALSE;
50a14534
EB
2605 if ( vtl->current_wp && vtl->current_wp->visible )
2606 {
2607 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
2608 gint x, y;
2609 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
2610
2611 if ( abs(x - event->x) < WAYPOINT_SIZE_APPROX &&
2612 abs(y - event->y) < WAYPOINT_SIZE_APPROX )
2613 {
2614 if ( event->button == 3 )
2615 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
2616 else
2617 vtl->moving_wp = TRUE;
2618 return TRUE;
2619 }
2620 }
2621
2622 params.vvp = vvp;
2623 params.x = event->x;
2624 params.y = event->y;
2625 params.closest_wp_name = NULL;
2626 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
2627 params.closest_wp = NULL;
2628 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
2629 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
2630 {
2631 vtl->moving_wp = TRUE;
2632 }
2633 else if ( params.closest_wp )
2634 {
2635 if ( event->button == 3 )
2636 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
2637 else
2638 vtl->waypoint_rightclick = FALSE;
2639
2640 vtl->current_wp = params.closest_wp;
2641 vtl->current_wp_name = params.closest_wp_name;
2642 vtl->moving_wp = FALSE;
2643
2644 if ( params.closest_wp )
2645 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ) );
2646
2647 /* could make it so don't update if old WP is off screen and new is null but oh well */
2648 vik_layer_emit_update ( VIK_LAYER(vtl) );
2649 return TRUE;
2650 }
2651
2652 vtl->current_wp = NULL;
2653 vtl->current_wp_name = NULL;
2654 vtl->moving_wp = FALSE;
2655 vtl->waypoint_rightclick = FALSE;
2656 return FALSE;
2657}
2658
941aa6e9
AF
2659static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
2660{
2661 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
2662 return FALSE;
2663 if ( vtl->moving_wp )
2664 {
2665 VikCoord new_coord;
2666 vtl->moving_wp = FALSE;
2667 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
2668
2669 /* snap to TP */
2670 if ( event->state & GDK_CONTROL_MASK )
2671 {
2672 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
2673 if ( tp )
2674 new_coord = tp->coord;
2675 }
2676
2677 /* snap to WP */
2678 if ( event->state & GDK_SHIFT_MASK )
2679 {
2680 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
2681 if ( wp && wp != vtl->current_wp )
2682 new_coord = wp->coord;
2683 }
2684
2685 vtl->current_wp->coord = new_coord;
2686 vik_layer_emit_update ( VIK_LAYER(vtl) );
2687 return TRUE;
2688 }
2689 /* PUT IN RIGHT PLACE!!! */
2690 if ( vtl->waypoint_rightclick )
2691 {
2692 if ( vtl->wp_right_click_menu )
2693 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
2694 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
2695 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 ) );
2696 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
2697 vtl->waypoint_rightclick = FALSE;
2698 }
2699 return FALSE;
2700}
2701
2702/*** New track ****/
2703
2704static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
2705{
2706 return vvp;
2707}
2708
2709static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
50a14534
EB
2710{
2711 VikTrackpoint *tp;
2712
941aa6e9
AF
2713 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
2714 return FALSE;
2715
50a14534
EB
2716 if ( event->button == 3 && vtl->current_track )
2717 {
2718 /* undo */
2719 if ( vtl->current_track->trackpoints )
2720 {
2721 GList *last = g_list_last(vtl->current_track->trackpoints);
2722 g_free ( last->data );
2723 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
2724 }
2725 vik_layer_emit_update ( VIK_LAYER(vtl) );
2726 return TRUE;
2727 }
2728
2729 if ( event->type == GDK_2BUTTON_PRESS )
2730 {
2731 /* subtract last (duplicate from double click) tp then end */
2732 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
2733 {
2734 GList *last = g_list_last(vtl->current_track->trackpoints);
2735 g_free ( last->data );
2736 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
2737 /* undo last, then end */
2738 vtl->current_track = NULL;
2739 }
2740 return TRUE;
2741 }
2742
2743 if ( ! vtl->current_track )
2744 {
2745 gchar *name;
2746 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks ) ) )
2747 {
2748 vtl->current_track = vik_track_new();
2749 vtl->current_track->visible = TRUE;
2750 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
2751 }
2752 else
2753 return TRUE;
2754 }
2755 tp = vik_trackpoint_new();
2756 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
2757
2758 /* snap to other TP */
2759 if ( event->state & GDK_CONTROL_MASK )
2760 {
2761 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
2762 if ( other_tp )
2763 tp->coord = other_tp->coord;
2764 }
2765
2766 tp->newsegment = FALSE;
2767 tp->has_timestamp = FALSE;
2768 tp->timestamp = 0;
2769 tp->altitude = VIK_DEFAULT_ALTITUDE;
2770 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
2771
2772 vtl->ct_x1 = vtl->ct_x2;
2773 vtl->ct_y1 = vtl->ct_y2;
2774 vtl->ct_x2 = event->x;
2775 vtl->ct_y2 = event->y;
2776
2777 vik_layer_emit_update ( VIK_LAYER(vtl) );
2778 return TRUE;
2779}
2780
941aa6e9
AF
2781
2782/*** New waypoint ****/
2783
2784static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
2785{
2786 return vvp;
2787}
2788
2789static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
2790{
2791 VikCoord coord;
2792 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
2793 return FALSE;
2794 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
2795 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
2796 vik_layer_emit_update ( VIK_LAYER(vtl) );
2797 return TRUE;
2798}
2799
2800
2801/*** Edit trackpoint ****/
2802
33534cd8
AF
2803typedef struct {
2804 VikViewport *vvp;
2805 gboolean holding;
2806 GdkGC *gc;
2807 int oldx, oldy;
2808} tool_edtr_t;
2809
941aa6e9
AF
2810static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
2811{
33534cd8
AF
2812 tool_edtr_t *t = g_new(tool_edtr_t, 1);
2813 t->vvp = vvp;
2814 t->holding = FALSE;
2815 return t;
941aa6e9
AF
2816}
2817
33534cd8 2818static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 2819{
33534cd8
AF
2820 tool_edtr_t *t = data;
2821 VikViewport *vvp = t->vvp;
2822 TPSearchParams params;
941aa6e9
AF
2823 /* OUTDATED DOCUMENTATION:
2824 find 5 pixel range on each side. then put these UTM, and a pointer
2825 to the winning track name (and maybe the winning track itself), and a
2826 pointer to the winning trackpoint, inside an array or struct. pass
2827 this along, do a foreach on the tracks which will do a foreach on the
2828 trackpoints. */
2829 params.vvp = vvp;
2830 params.x = event->x;
2831 params.y = event->y;
2832 params.closest_track_name = NULL;
2833 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
2834 params.closest_tp = NULL;
2835
2836 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
2837 return FALSE;
2838
2839 if ( vtl->current_tpl )
2840 {
2841 /* 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.) */
2842 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
2843 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
2844 gint x, y;
2845 g_assert ( current_tr );
2846
2847 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
2848
2849 if ( current_tr->visible &&
2850 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
2851 abs(y - event->y) < TRACKPOINT_SIZE_APPROX )
2852 {
2853 vtl->moving_tp = TRUE;
33534cd8
AF
2854 t->holding = TRUE;
2855 t->gc = vik_viewport_new_gc (vvp, "black", 2);
2856 gdk_gc_set_function ( t->gc, GDK_INVERT );
2857 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, event->x-3, event->y-3, 6, 6 );
2858 vik_viewport_sync(vvp);
2859 t->oldx = event->x;
2860 t->oldy = event->y;
941aa6e9
AF
2861 return TRUE;
2862 }
2863
2864 vtl->last_tpl = vtl->current_tpl;
2865 vtl->last_tp_track_name = vtl->current_tp_track_name;
2866 }
2867
2868 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
2869
2870 if ( params.closest_tp )
2871 {
2872 vtl->current_tpl = params.closest_tpl;
2873 vtl->current_tp_track_name = params.closest_track_name;
2874 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, vtl->current_tp_track_name ) );
2875 trw_layer_tpwin_init ( vtl );
2876 vik_layer_emit_update ( VIK_LAYER(vtl) );
2877 return TRUE;
2878 }
2879
2880 /* these aren't the droids you're looking for */
2881 return FALSE;
2882}
2883
33534cd8
AF
2884static gboolean edtr_sync_done = TRUE;
2885
2886static gboolean edtr_sync(gpointer data)
2887{
2888 VikViewport *vvp = data;
2889 gdk_threads_enter();
2890 vik_viewport_sync(vvp);
2891 edtr_sync_done = TRUE;
2892 gdk_threads_leave();
2893 return FALSE;
2894}
2895
2896static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
2897{
2898 tool_edtr_t *t = data;
2899 VikViewport *vvp = t->vvp;
2900
2901 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
2902 return FALSE;
2903
2904 if ( t->holding )
2905 {
2906 VikCoord new_coord;
2907 GdkGC *gc;
2908 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
2909
2910 /* snap to TP */
2911 if ( event->state & GDK_CONTROL_MASK )
2912 {
2913 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
2914 if ( tp && tp != vtl->current_tpl->data )
2915 new_coord = tp->coord;
2916 }
2917 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
2918 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
2919 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, event->x-3, event->y-3, 6, 6 );
2920 t->oldx = event->x;
2921 t->oldy = event->y;
2922 if (edtr_sync_done) {
2923 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, edtr_sync, vvp, NULL);
2924 edtr_sync_done = FALSE;
2925 }
2926
2927 return TRUE;
2928 }
2929 return FALSE;
2930}
2931
941aa6e9 2932
33534cd8 2933static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 2934{
33534cd8
AF
2935 tool_edtr_t *t = data;
2936 VikViewport *vvp = t->vvp;
2937
941aa6e9
AF
2938 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
2939 return FALSE;
33534cd8
AF
2940
2941 /* xxx: this state is now in the tool instance... fix this */
941aa6e9
AF
2942 if ( vtl->moving_tp )
2943 {
2944 /* vtl->moving_tp_x, vtl->moving_tp_y, etc. */
2945 VikCoord new_coord;
2946 vtl->moving_tp = FALSE;
33534cd8 2947 t->holding = FALSE;
941aa6e9
AF
2948 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
2949
2950 /* snap to TP */
2951 if ( event->state & GDK_CONTROL_MASK )
2952 {
2953 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
2954 if ( tp && tp != vtl->current_tpl->data )
2955 new_coord = tp->coord;
2956 }
2957
2958 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
2959
33534cd8
AF
2960 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
2961
941aa6e9
AF
2962 /* diff dist is diff from orig */
2963 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
2964 /* can't join with itself! */
2965 trw_layer_cancel_last_tp ( vtl );
2966
2967 vik_layer_emit_update ( VIK_LAYER(vtl) );
2968 return TRUE;
2969 }
2970 return FALSE;
2971}
2972
2973
2974/*** Show picture ****/
2975
2976static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
2977{
2978 return vvp;
2979}
2980
2981/* Params are: vvp, event, last match found or NULL */
2982static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] )
2983{
2984 if ( wp->image && wp->visible )
2985 {
2986 gint x, y, slackx, slacky;
2987 GdkEventButton *event = (GdkEventButton *) params[1];
2988
2989 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
2990 slackx = wp->image_width / 2;
2991 slacky = wp->image_height / 2;
2992 if ( x <= event->x + slackx && x >= event->x - slackx
2993 && y <= event->y + slacky && y >= event->y - slacky )
2994 {
2995 params[2] = wp->image; /* we've found a match. however continue searching
2996 * since we want to find the last match -- that
2997 * is, the match that was drawn last. */
2998 }
2999 }
3000}
3001
3002static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3003{
3004 gpointer params[3] = { vvp, event, NULL };
3005 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3006 return FALSE;
3007 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
3008 if ( params[2] )
3009 {
3010 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
3011#ifdef WINDOWS
3012 ShellExecute(NULL, NULL, (char *) params[2], NULL, ".\\", 0);
3013#else /* WINDOWS */
3014 GError *err = NULL;
3015 gchar *quoted_file = g_shell_quote ( (gchar *) params[2] );
3016 gchar *cmd = g_strdup_printf ( "eog %s", quoted_file );
3017 g_free ( quoted_file );
3018 if ( ! g_spawn_command_line_async ( cmd, &err ) )
3019 {
3020 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), "Could not launch eog to open file." );
3021 g_error_free ( err );
3022 }
3023 g_free ( cmd );
3024#endif /* WINDOWS */
3025 return TRUE; /* found a match */
3026 }
3027 else
3028 return FALSE; /* go through other layers, searching for a match */
3029}
3030
3031/***************************************************************************
3032 ** End tool code
3033 ***************************************************************************/
3034
3035
3036
3037
3038
50a14534
EB
3039static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
3040{
3041 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
3042 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
3043}
3044
3045static void create_thumbnails_thread ( GSList *pics, gpointer threaddata )
3046{
3047 guint total = g_slist_length(pics), done = 0;
3048 while ( pics )
3049 {
3050 a_thumbnails_create ( (gchar *) pics->data );
3051 a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
3052 pics = pics->next;
3053 }
3054}
3055
3056static void free_pics_slist ( GSList *pics )
3057{
3058 while ( pics )
3059 {
3060 g_free ( pics->data );
3061 pics = g_slist_delete_link ( pics, pics );
3062 }
3063}
3064
3065static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
3066{
3067 if ( ! vtl->has_verified_thumbnails )
3068 {
3069 GSList *pics = NULL;
3070 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
3071 if ( pics )
3072 {
3073 gint len = g_slist_length ( pics );
3074 gchar *tmp = g_strdup_printf ( "Creating %d Image Thumbnails...", len );
3075 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 );
3076 g_free ( tmp );
3077 }
3078 }
3079}
3080
3081VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
3082{
3083 return vtl->coord_mode;
3084}
3085
3086
3087
3088static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
3089{
3090 vik_coord_convert ( &(wp->coord), *dest_mode );
3091}
3092
3093static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
3094{
3095 vik_track_convert ( tr, *dest_mode );
3096}
3097
3098static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
3099{
3100 if ( vtl->coord_mode != dest_mode )
3101 {
3102 vtl->coord_mode = dest_mode;
3103 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
3104 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
3105 }
3106}
e4afc73a
EB
3107
3108VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
3109{
3110 return g_hash_table_lookup ( vtl->waypoints, name );
3111}
3112
3113VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, gchar *name )
3114{
3115 return g_hash_table_lookup ( vtl->tracks, name );
3116}