]> git.street.me.uk Git - andy/viking.git/blame - src/viktrwlayer.c
News feature: external tools
[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
3e7553ae
GB
27#ifdef HAVE_CONFIG_H
28#include "config.h"
29#endif
30
50a14534 31#include "viking.h"
7114e879 32#include "vikmapslayer.h"
50a14534
EB
33#include "viktrwlayer_tpwin.h"
34#include "viktrwlayer_propwin.h"
acaf7113 35#include "garminsymbols.h"
50a14534
EB
36#include "thumbnails.h"
37#include "background.h"
911400b5 38#include "gpx.h"
e0b0b9c1 39#include "babel.h"
ad0a8c2d
EB
40#include "dem.h"
41#include "dems.h"
7567b54c 42#include "googlesearch.h"
3e7553ae
GB
43#ifdef VIK_CONFIG_OPENSTREETMAP
44#include "osm-traces.h"
45#endif
28c82d8b 46#include "acquire.h"
7d02a0b0 47#include "util.h"
50a14534 48
bce3a7b0
EB
49#include "icons/icons.h"
50
8c00358d 51#ifdef HAVE_MATH_H
50a14534 52#include <math.h>
8c00358d
GB
53#endif
54#ifdef HAVE_STRING_H
50a14534 55#include <string.h>
8c00358d
GB
56#endif
57#ifdef HAVE_STDLIB_H
50a14534 58#include <stdlib.h>
8c00358d 59#endif
8c060406 60#include <stdio.h>
50a14534
EB
61#include <ctype.h>
62
777e2d4d 63#include <gdk/gdkkeysyms.h>
8c060406
MA
64#include <glib.h>
65#include <glib/gstdio.h>
4c77d5e0 66#include <glib/gi18n.h>
777e2d4d 67
090cae39
GB
68/* Relax some dependencies */
69#if ! GLIB_CHECK_VERSION(2,12,0)
70static gboolean return_true (gpointer a, gpointer b, gpointer c) { return TRUE; }
71static g_hash_table_remove_all (GHashTable *ght) { g_hash_table_foreach_remove ( ght, (GHRFunc) return_true, FALSE ); }
72#endif
73
533bbf34 74#define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=js"
50a14534
EB
75#define VIK_TRW_LAYER_TRACK_GC 13
76#define VIK_TRW_LAYER_TRACK_GC_RATES 10
77#define VIK_TRW_LAYER_TRACK_GC_MIN 0
78#define VIK_TRW_LAYER_TRACK_GC_MAX 11
79#define VIK_TRW_LAYER_TRACK_GC_BLACK 12
80
81#define DRAWMODE_BY_TRACK 0
82#define DRAWMODE_BY_VELOCITY 1
83#define DRAWMODE_ALL_BLACK 2
84
85#define POINTS 1
86#define LINES 2
87
88/* this is how it knows when you click if you are clicking close to a trackpoint. */
89#define TRACKPOINT_SIZE_APPROX 5
90#define WAYPOINT_SIZE_APPROX 5
91
b42a25ba
EB
92#define MIN_STOP_LENGTH 15
93#define MAX_STOP_LENGTH 86400
94#define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
95 /* this is multiplied by user-inputted value from 1-100. */
50a14534
EB
96enum {
97VIK_TRW_LAYER_SUBLAYER_TRACKS,
98VIK_TRW_LAYER_SUBLAYER_WAYPOINTS,
99VIK_TRW_LAYER_SUBLAYER_TRACK,
100VIK_TRW_LAYER_SUBLAYER_WAYPOINT
101};
102
103enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
104
105struct _VikTrwLayer {
106 VikLayer vl;
107 GHashTable *tracks;
108 GHashTable *tracks_iters;
109 GHashTable *waypoints_iters;
110 GHashTable *waypoints;
111 GtkTreeIter waypoints_iter, tracks_iter;
112 gboolean tracks_visible, waypoints_visible;
113 guint8 drawmode;
114 guint8 drawpoints;
b42a25ba
EB
115 guint8 drawelevation;
116 guint8 elevation_factor;
117 guint8 drawstops;
118 guint32 stop_length;
50a14534
EB
119 guint8 drawlines;
120 guint8 line_thickness;
121 guint8 bg_line_thickness;
122
123 guint8 wp_symbol;
124 guint8 wp_size;
ea3933fc 125 gboolean wp_draw_symbols;
50a14534
EB
126
127 gdouble velocity_min, velocity_max;
128 GArray *track_gc;
129 guint16 track_gc_iter;
8e9c992d 130 GdkGC *current_track_gc;
50a14534
EB
131 GdkGC *track_bg_gc;
132 GdkGC *waypoint_gc;
133 GdkGC *waypoint_text_gc;
134 GdkGC *waypoint_bg_gc;
135 GdkFont *waypoint_font;
136 VikTrack *current_track;
137 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
7b203521
EB
138 gboolean ct_sync_done;
139
50a14534
EB
140
141 VikCoordMode coord_mode;
142
143 /* wp editing tool */
144 VikWaypoint *current_wp;
145 gchar *current_wp_name;
146 gboolean moving_wp;
147 gboolean waypoint_rightclick;
148
149 /* track editing tool */
150 GList *current_tpl;
151 gchar *current_tp_track_name;
152 VikTrwLayerTpwin *tpwin;
153
154 /* weird hack for joining tracks */
155 GList *last_tpl;
156 gchar *last_tp_track_name;
157
158 /* track editing tool -- more specifically, moving tps */
159 gboolean moving_tp;
160
1eef1bde
QT
161 /* magic scissors tool */
162 gboolean magic_scissors_started;
163 VikCoord magic_scissors_coord;
bddd2056
EB
164 gboolean magic_scissors_check_added_track;
165 gchar *magic_scissors_added_track_name;
166 VikTrack *magic_scissors_current_track;
167 gboolean magic_scissors_append;
1eef1bde 168
50a14534
EB
169 gboolean drawlabels;
170 gboolean drawimages;
171 guint8 image_alpha;
172 GQueue *image_cache;
173 guint8 image_size;
174 guint16 image_cache_size;
175
176 /* for waypoint text */
177 PangoLayout *wplabellayout;
178
179 gboolean has_verified_thumbnails;
180
181 GtkMenu *wp_right_click_menu;
182
20c7a3a0
QT
183 /* menu */
184 VikStdLayerMenuItem menu_selection;
185
a8fe53f8 186 gint highest_wp_number;
50a14534
EB
187};
188
189/* A caached waypoint image. */
190typedef struct {
191 GdkPixbuf *pixbuf;
192 gchar *image; /* filename */
193} CachedPixbuf;
194
195struct DrawingParams {
196 VikViewport *vp;
197 VikTrwLayer *vtl;
198 gdouble xmpp, ympp;
199 guint16 width, height;
200 const VikCoord *center;
201 gint track_gc_iter;
202 gboolean one_zone, lat_lon;
203 gdouble ce1, ce2, cn1, cn2;
204};
205
2cebc318 206static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16);
20c7a3a0
QT
207static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl);
208
33534cd8 209static void trw_layer_delete_item ( gpointer *pass_along );
2cebc318
QT
210static void trw_layer_copy_item_cb( gpointer *pass_along);
211static void trw_layer_cut_item_cb( gpointer *pass_along);
33534cd8 212
50a14534
EB
213static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
214static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] );
215
216static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
217static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
218
219static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 );
220static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
221static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
222
223static void goto_coord ( VikLayersPanel *vlp, const VikCoord *coord );
224static void trw_layer_goto_track_startpoint ( gpointer pass_along[5] );
225static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
111fa174
AF
226static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
227static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
7114e879 228static void trw_layer_download_map_along_track_cb(gpointer pass_along[6]);
50a14534
EB
229static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
230static void trw_layer_export ( gpointer layer_and_vlp[2], guint file_type );
231static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
232static void trw_layer_new_wp ( gpointer lav[2] );
291edcab 233static void trw_layer_merge_with_other ( gpointer pass_along[6] );
50a14534
EB
234
235/* pop-up items */
236static void trw_layer_properties_item ( gpointer pass_along[5] );
237static void trw_layer_goto_waypoint ( gpointer pass_along[5] );
238static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[5] );
239
240static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[4] );
241static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[4] );
242static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
243
50a14534 244
911400b5
AF
245static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len );
246static VikTrwLayer *trw_layer_unmarshall( gpointer data, gint len, VikViewport *vvp );
247
50a14534
EB
248static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp );
249static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id );
250
33534cd8 251static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
ddc47a46
AF
252static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
253static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
50a14534 254static void trw_layer_free_copied_item ( gint subtype, gpointer item );
70a23263 255static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
50a14534 256
50a14534
EB
257static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
258static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
259static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
260static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
50a14534 261
941aa6e9 262static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
33534cd8 263static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
dc2c040e 264static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
33534cd8 265static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
941aa6e9
AF
266static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
267static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
268static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
7432fddf 269static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
dc2c040e 270static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
7432fddf 271static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
98f5364d
EB
272static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
273static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
941aa6e9
AF
274static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
275static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
dc2c040e 276static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
777e2d4d 277static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
941aa6e9
AF
278static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
279static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
1eef1bde
QT
280static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp);
281static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
282
50a14534 283
50a14534
EB
284static void cached_pixbuf_free ( CachedPixbuf *cp );
285static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
286static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp );
287
288static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
289static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
290
291static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
292
e890a6e6 293static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
ddc47a46
AF
294static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode );
295static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
e890a6e6 296
a8fe53f8
EB
297static gchar *highest_wp_number_get(VikTrwLayer *vtl);
298static void highest_wp_number_reset(VikTrwLayer *vtl);
299static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
300static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
301
302
50a14534 303static VikToolInterface trw_layer_tools[] = {
4c77d5e0 304 { N_("Create Waypoint"), (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
5bfafde9 305 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
941aa6e9 306
4c77d5e0 307 { N_("Create Track"), (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
dc2c040e 308 (VikToolMouseFunc) tool_new_track_click, (VikToolMouseMoveFunc) tool_new_track_move, NULL,
5bfafde9 309 (VikToolKeyFunc) tool_new_track_key_press, GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
50a14534 310
4c77d5e0 311 { N_("Begin Track"), (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
5bfafde9 312 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
98f5364d 313
4c77d5e0 314 { N_("Edit Waypoint"), (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
7432fddf 315 (VikToolMouseFunc) tool_edit_waypoint_click,
dc2c040e 316 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
5bfafde9 317 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
941aa6e9 318
4c77d5e0 319 { N_("Edit Trackpoint"), (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
33534cd8 320 (VikToolMouseFunc) tool_edit_trackpoint_click,
dc2c040e 321 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
5bfafde9 322 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
941aa6e9 323
4c77d5e0 324 { N_("Show Picture"), (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
5bfafde9 325 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
1eef1bde 326
4c77d5e0 327 { N_("Magic Scissors"), (VikToolConstructorFunc) tool_magic_scissors_create, NULL, NULL, NULL,
5bfafde9 328 (VikToolMouseFunc) tool_magic_scissors_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_iscissors_pixbuf },
941aa6e9 329};
8e9c992d 330enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
50a14534
EB
331
332/****** PARAMETERS ******/
333
4c77d5e0 334static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
55906514 335enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
50a14534 336
4c77d5e0
GB
337static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Velocity"), N_("All Tracks Black"), 0 };
338static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
50a14534 339
b42a25ba 340
50a14534
EB
341static VikLayerParamScale params_scales[] = {
342 /* min max step digits */
343 { 1, 10, 1, 0 }, /* line_thickness */
344 { 0.0, 99.0, 1, 2 }, /* velocity_min */
345 { 1.0, 100.0, 1.0, 2 }, /* velocity_max */
346 /* 5 * step == how much to turn */
347 { 16, 128, 3.2, 0 }, /* image_size */
348 { 0, 255, 5, 0 }, /* image alpha */
349 { 5, 500, 5, 0 }, /* image cache_size */
350 { 0, 8, 1, 0 }, /* image cache_size */
351 { 1, 64, 1, 0 }, /* wpsize */
b42a25ba
EB
352 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
353 { 1, 100, 1, 0 }, /* stop_length */
50a14534
EB
354};
355
356VikLayerParam trw_layer_params[] = {
357 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
358 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
359
4c77d5e0
GB
360 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
361 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
362 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
363 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
364 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
365
366 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
367 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
368
369 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
370 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
371 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
372 { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Min Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
373 { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Max Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 },
374
375 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
376 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
377 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
378 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
379 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
380 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
381 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
382 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
383
384 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
385 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
386 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
387 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
50a14534
EB
388};
389
55906514 390enum { 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 };
ad0a8c2d
EB
391
392/*** TO ADD A PARAM:
393 *** 1) Add to trw_layer_params and enumeration
394 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
ad0a8c2d 395 ***/
50a14534
EB
396
397/****** END PARAMETERS ******/
398
399VikLayerInterface vik_trw_layer_interface = {
400 "TrackWaypoint",
5bfafde9 401 &viktrwlayer_pixbuf,
50a14534
EB
402
403 trw_layer_tools,
404 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
405
406 trw_layer_params,
407 NUM_PARAMS,
408 params_groups, /* params_groups */
409 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
410
5a4a28bf
QT
411 VIK_MENU_ITEM_ALL,
412
50a14534
EB
413 (VikLayerFuncCreate) vik_trw_layer_create,
414 (VikLayerFuncRealize) vik_trw_layer_realize,
415 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
416 (VikLayerFuncFree) vik_trw_layer_free,
417
418 (VikLayerFuncProperties) NULL,
419 (VikLayerFuncDraw) vik_trw_layer_draw,
420 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
421
20c7a3a0
QT
422 (VikLayerFuncSetMenuItemsSelection) vik_trw_layer_set_menu_selection,
423 (VikLayerFuncGetMenuItemsSelection) vik_trw_layer_get_menu_selection,
424
50a14534
EB
425 (VikLayerFuncAddMenuItems) vik_trw_layer_add_menu_items,
426 (VikLayerFuncSublayerAddMenuItems) vik_trw_layer_sublayer_add_menu_items,
427
428 (VikLayerFuncSublayerRenameRequest) vik_trw_layer_sublayer_rename_request,
429 (VikLayerFuncSublayerToggleVisible) vik_trw_layer_sublayer_toggle_visible,
430
911400b5
AF
431 (VikLayerFuncMarshall) trw_layer_marshall,
432 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
50a14534
EB
433
434 (VikLayerFuncSetParam) trw_layer_set_param,
435 (VikLayerFuncGetParam) trw_layer_get_param,
436
437 (VikLayerFuncReadFileData) a_gpspoint_read_file,
438 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
439
33534cd8 440 (VikLayerFuncDeleteItem) trw_layer_del_item,
50a14534
EB
441 (VikLayerFuncCopyItem) trw_layer_copy_item,
442 (VikLayerFuncPasteItem) trw_layer_paste_item,
443 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
70a23263
AF
444
445 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
50a14534
EB
446};
447
448/* for copy & paste (I think?) */
449typedef struct {
ddc47a46
AF
450 guint len;
451 guint8 data[0];
452 // gchar *name;
453 // VikWaypoint *wp;
454} FlatItem;
50a14534
EB
455
456GType vik_trw_layer_get_type ()
457{
458 static GType vtl_type = 0;
459
460 if (!vtl_type)
461 {
462 static const GTypeInfo vtl_info =
463 {
464 sizeof (VikTrwLayerClass),
465 NULL, /* base_init */
466 NULL, /* base_finalize */
467 NULL, /* class init */
468 NULL, /* class_finalize */
469 NULL, /* class_data */
470 sizeof (VikTrwLayer),
471 0,
472 NULL /* instance init */
473 };
474 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
475 }
476
477 return vtl_type;
478}
479
33534cd8
AF
480static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
481{
482 static gpointer pass_along[5];
483 if (!sublayer) {
484 return;
485 }
486
487 pass_along[0] = vtl;
488 pass_along[1] = NULL;
dc2c040e 489 pass_along[2] = GINT_TO_POINTER (subtype);
33534cd8
AF
490 pass_along[3] = sublayer;
491 pass_along[4] = NULL;
492
493 trw_layer_delete_item ( pass_along );
494}
50a14534 495
2cebc318
QT
496static void trw_layer_copy_item_cb( gpointer pass_along[5])
497{
498 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
dc2c040e 499 gint subtype = GPOINTER_TO_INT (pass_along[2]);
2cebc318
QT
500 gpointer * sublayer = pass_along[3];
501 guint8 *data = NULL;
502 guint len;
503
504 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
505
506 if (data) {
507 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
508 subtype, len, data);
509 }
510}
511
512static void trw_layer_cut_item_cb( gpointer pass_along[5])
513{
514 trw_layer_copy_item_cb(pass_along);
515 trw_layer_delete_item(pass_along);
516}
517
ddc47a46 518static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
50a14534 519{
ddc47a46
AF
520 FlatItem *fi;
521 guint8 *id;
522 guint il;
523
524 if (!sublayer) {
525 *item = NULL;
526 return;
50a14534 527 }
ddc47a46
AF
528
529 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 530 {
ddc47a46
AF
531 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
532 } else {
533 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
50a14534 534 }
ddc47a46
AF
535
536 *len = sizeof(FlatItem) + strlen(sublayer) + 1 + il;
537 fi = g_malloc ( *len );
538 fi->len = strlen(sublayer) + 1;
539 memcpy(fi->data, sublayer, fi->len);
540 memcpy(fi->data + fi->len, id, il);
541 g_free(id);
542 *item = (guint8 *)fi;
50a14534
EB
543}
544
ddc47a46 545static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
50a14534 546{
ddc47a46
AF
547 FlatItem *fi = (FlatItem *) item;
548
549 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
50a14534 550 {
ddc47a46
AF
551 VikWaypoint *w;
552 gchar *name;
553
554 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, (gchar *)fi->data);
555 w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
556 vik_trw_layer_add_waypoint ( vtl, name, w );
557 waypoint_convert(name, w, &vtl->coord_mode);
50a14534
EB
558 return TRUE;
559 }
ddc47a46 560 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
50a14534 561 {
ddc47a46
AF
562 VikTrack *t;
563 gchar *name;
564 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, (gchar *)fi->data);
565 t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
566 vik_trw_layer_add_track ( vtl, name, t );
567 track_convert(name, t, &vtl->coord_mode);
50a14534
EB
568 return TRUE;
569 }
570 return FALSE;
571}
572
573static void trw_layer_free_copied_item ( gint subtype, gpointer item )
574{
ddc47a46
AF
575 if (item) {
576 g_free(item);
50a14534
EB
577 }
578}
579
50a14534
EB
580static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp )
581{
582 switch ( id )
583 {
584 case PARAM_TV: vtl->tracks_visible = data.b; break;
585 case PARAM_WV: vtl->waypoints_visible = data.b; break;
586 case PARAM_DM: vtl->drawmode = data.u; break;
587 case PARAM_DP: vtl->drawpoints = data.b; break;
b42a25ba
EB
588 case PARAM_DE: vtl->drawelevation = data.b; break;
589 case PARAM_DS: vtl->drawstops = data.b; break;
50a14534 590 case PARAM_DL: vtl->drawlines = data.b; break;
b42a25ba
EB
591 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
592 vtl->stop_length = data.u;
593 break;
594 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
595 vtl->elevation_factor = data.u;
596 break;
50a14534
EB
597 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
598 {
599 vtl->line_thickness = data.u;
600 trw_layer_new_track_gcs ( vtl, vp );
601 }
602 break;
603 case PARAM_BLT: if ( data.u > 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
604 {
605 vtl->bg_line_thickness = data.u;
606 trw_layer_new_track_gcs ( vtl, vp );
607 }
608 break;
609 case PARAM_VMIN: vtl->velocity_min = data.d; break;
610 case PARAM_VMAX: vtl->velocity_max = data.d; break;
611 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
612 case PARAM_DLA: vtl->drawlabels = data.b; break;
613 case PARAM_DI: vtl->drawimages = data.b; break;
614 case PARAM_IS: if ( data.u != vtl->image_size )
615 {
616 vtl->image_size = data.u;
617 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
618 g_queue_free ( vtl->image_cache );
619 vtl->image_cache = g_queue_new ();
620 }
621 break;
622 case PARAM_IA: vtl->image_alpha = data.u; break;
623 case PARAM_ICS: vtl->image_cache_size = data.u;
624 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
625 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
626 break;
627 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
628 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
629 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
630 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
631 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
632 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
ea3933fc 633 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
50a14534
EB
634 }
635 return TRUE;
636}
637
638static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id )
639{
640 VikLayerParamData rv;
641 switch ( id )
642 {
643 case PARAM_TV: rv.b = vtl->tracks_visible; break;
644 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
645 case PARAM_DM: rv.u = vtl->drawmode; break;
646 case PARAM_DP: rv.b = vtl->drawpoints; break;
b42a25ba
EB
647 case PARAM_DE: rv.b = vtl->drawelevation; break;
648 case PARAM_EF: rv.u = vtl->elevation_factor; break;
649 case PARAM_DS: rv.b = vtl->drawstops; break;
650 case PARAM_SL: rv.u = vtl->stop_length; break;
50a14534
EB
651 case PARAM_DL: rv.b = vtl->drawlines; break;
652 case PARAM_LT: rv.u = vtl->line_thickness; break;
653 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
654 case PARAM_VMIN: rv.d = vtl->velocity_min; break;
655 case PARAM_VMAX: rv.d = vtl->velocity_max; break;
656 case PARAM_DLA: rv.b = vtl->drawlabels; break;
657 case PARAM_DI: rv.b = vtl->drawimages; break;
658 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
659 case PARAM_IS: rv.u = vtl->image_size; break;
660 case PARAM_IA: rv.u = vtl->image_alpha; break;
661 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
662 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
663 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
664 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
665 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
666 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
667 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
ea3933fc 668 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
50a14534
EB
669 }
670 return rv;
671}
672
911400b5
AF
673static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
674{
675 guint8 *pd, *dd;
676 gint pl, dl;
677 gchar *tmpname;
678 FILE *f;
679
680 *data = NULL;
681
682 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
683 a_gpx_write_file(vtl, f);
684 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
685 fclose(f);
8c060406 686 f = NULL;
941aa6e9 687 g_file_get_contents(tmpname, (void *)&dd, (void *)&dl, NULL);
911400b5
AF
688 *len = sizeof(pl) + pl + dl;
689 *data = g_malloc(*len);
690 memcpy(*data, &pl, sizeof(pl));
691 memcpy(*data + sizeof(pl), pd, pl);
692 memcpy(*data + sizeof(pl) + pl, dd, dl);
693
694 g_free(pd);
695 g_free(dd);
8c060406 696 g_remove(tmpname);
911400b5
AF
697 g_free(tmpname);
698 }
699}
700
701static VikTrwLayer *trw_layer_unmarshall( gpointer data, gint len, VikViewport *vvp )
702{
703 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
704 guint pl;
705 gchar *tmpname;
706 FILE *f;
707
708
709 memcpy(&pl, data, sizeof(pl));
710 data += sizeof(pl);
711 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
712 data += pl;
713
714 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
4258f4e2 715 g_critical("couldn't open temp file");
911400b5
AF
716 exit(1);
717 }
718 fwrite(data, len - pl - sizeof(pl), 1, f);
719 rewind(f);
720 a_gpx_read_file(rv, f);
721 fclose(f);
8c060406
MA
722 f = NULL;
723 g_remove(tmpname);
911400b5
AF
724 g_free(tmpname);
725 return rv;
726}
727
753ec753 728static GList * str_array_to_glist(gchar* data[])
267b6db5
QT
729{
730 GList *gl = NULL;
731 gpointer * p;
0654760a 732 for (p = (gpointer)data; *p; p++)
267b6db5
QT
733 gl = g_list_prepend(gl, *p);
734 return(g_list_reverse(gl));
735}
736
8499a412
QT
737static gboolean strcase_equal(gconstpointer s1, gconstpointer s2)
738{
739 return (strcasecmp(s1, s2) == 0);
740}
741
742static guint strcase_hash(gconstpointer v)
743{
744 /* 31 bit hash function */
745 int i;
746 const gchar *t = v;
747 gchar s[128]; /* malloc is too slow for reading big files */
748 gchar *p = s;
749
750 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
751 p[i] = toupper(t[i]);
752 p[i] = '\0';
753
754 p = s;
755 guint32 h = *p;
756 if (h) {
757 for (p += 1; *p != '\0'; p++)
758 h = (h << 5) - h + *p;
759 }
760
761 return h;
762}
763
50a14534
EB
764VikTrwLayer *vik_trw_layer_new ( gint drawmode )
765{
267b6db5 766 if (trw_layer_params[PARAM_DM].widget_data == NULL)
753ec753 767 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
267b6db5 768 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
753ec753 769 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
267b6db5 770
50a14534
EB
771 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
772 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
773
8499a412 774 rv->waypoints = g_hash_table_new_full ( strcase_hash, strcase_equal, g_free, (GDestroyNotify) vik_waypoint_free );
50a14534
EB
775 rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free );
776 rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
8499a412 777 rv->waypoints_iters = g_hash_table_new_full ( strcase_hash, strcase_equal, NULL, g_free );
50a14534
EB
778
779 /* TODO: constants at top */
780 rv->waypoints_visible = rv->tracks_visible = TRUE;
781 rv->drawmode = drawmode;
782 rv->drawpoints = TRUE;
b42a25ba
EB
783 rv->drawstops = FALSE;
784 rv->drawelevation = FALSE;
785 rv->elevation_factor = 30;
786 rv->stop_length = 60;
50a14534
EB
787 rv->drawlines = TRUE;
788 rv->wplabellayout = NULL;
789 rv->wp_right_click_menu = NULL;
790 rv->waypoint_gc = NULL;
791 rv->waypoint_text_gc = NULL;
792 rv->waypoint_bg_gc = NULL;
793 rv->track_gc = NULL;
794 rv->velocity_max = 5.0;
795 rv->velocity_min = 0.0;
796 rv->line_thickness = 1;
b42a25ba 797 rv->bg_line_thickness = 0;
50a14534
EB
798 rv->current_wp = NULL;
799 rv->current_wp_name = NULL;
800 rv->current_track = NULL;
801 rv->current_tpl = NULL;
802 rv->current_tp_track_name = NULL;
803 rv->moving_tp = FALSE;
804 rv->moving_wp = FALSE;
1eef1bde 805
7b203521
EB
806 rv->ct_sync_done = TRUE;
807
1eef1bde 808 rv->magic_scissors_started = FALSE;
bddd2056
EB
809 rv->magic_scissors_check_added_track = FALSE;
810 rv->magic_scissors_added_track_name = NULL;
811 rv->magic_scissors_current_track = NULL;
812 rv->magic_scissors_append = FALSE;
1eef1bde 813
50a14534
EB
814 rv->waypoint_rightclick = FALSE;
815 rv->last_tpl = NULL;
816 rv->last_tp_track_name = NULL;
817 rv->tpwin = NULL;
818 rv->image_cache = g_queue_new();
819 rv->image_size = 64;
820 rv->image_alpha = 255;
821 rv->image_cache_size = 300;
822 rv->drawimages = TRUE;
823 rv->drawlabels = TRUE;
824 return rv;
825}
826
827
828void vik_trw_layer_free ( VikTrwLayer *trwlayer )
829{
830 g_hash_table_destroy(trwlayer->waypoints);
831 g_hash_table_destroy(trwlayer->tracks);
832
833 /* ODC: replace with GArray */
834 trw_layer_free_track_gcs ( trwlayer );
835
836 if ( trwlayer->wp_right_click_menu )
837 gtk_object_sink ( GTK_OBJECT(trwlayer->wp_right_click_menu) );
838
839 if ( trwlayer->wplabellayout != NULL)
840 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
841
842 if ( trwlayer->waypoint_gc != NULL )
843 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
844
845 if ( trwlayer->waypoint_text_gc != NULL )
846 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
847
848 if ( trwlayer->waypoint_bg_gc != NULL )
849 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
850
851 if ( trwlayer->waypoint_font != NULL )
852 gdk_font_unref ( trwlayer->waypoint_font );
853
854 if ( trwlayer->tpwin != NULL )
855 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
856
857 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
858 g_queue_free ( trwlayer->image_cache );
859}
860
861static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
862{
863 dp->vp = vp;
864 dp->xmpp = vik_viewport_get_xmpp ( vp );
865 dp->ympp = vik_viewport_get_ympp ( vp );
866 dp->width = vik_viewport_get_width ( vp );
867 dp->height = vik_viewport_get_height ( vp );
868 dp->center = vik_viewport_get_center ( vp );
869 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
870 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
871
872 if ( dp->one_zone )
873 {
874 gint w2, h2;
875 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
876 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
877 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
878
879 dp->ce1 = dp->center->east_west-w2;
880 dp->ce2 = dp->center->east_west+w2;
881 dp->cn1 = dp->center->north_south-h2;
882 dp->cn2 = dp->center->north_south+h2;
883 } else if ( dp->lat_lon ) {
884 VikCoord upperleft, bottomright;
885 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
886 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
887 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
888 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
889 dp->ce1 = upperleft.east_west;
890 dp->ce2 = bottomright.east_west;
891 dp->cn1 = bottomright.north_south;
892 dp->cn2 = upperleft.north_south;
893 }
894
895 dp->track_gc_iter = 0;
896}
897
898static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
899{
900 static gdouble rv = 0;
901 if ( tp1->has_timestamp && tp2->has_timestamp )
902 {
903 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
904 / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
905
906 if ( rv < 0 )
907 return VIK_TRW_LAYER_TRACK_GC_MIN;
908 else if ( vtl->velocity_min >= vtl->velocity_max )
909 return VIK_TRW_LAYER_TRACK_GC_MAX;
910
911 rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
912
913 if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
914 return VIK_TRW_LAYER_TRACK_GC_MAX;
915 return (gint) rv;
916 }
917 else
918 return VIK_TRW_LAYER_TRACK_GC_BLACK;
919}
920
921void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
922{
923 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
924 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
925 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
926 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
927}
928
929static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
930{
931 /* TODO: this function is a mess, get rid of any redundancy */
932 GList *list = track->trackpoints;
8e9c992d 933 GdkGC *main_gc;
50a14534
EB
934 gboolean useoldvals = TRUE;
935
936 gboolean drawpoints;
b42a25ba
EB
937 gboolean drawstops;
938 gboolean drawelevation;
941aa6e9 939 gdouble min_alt, max_alt, alt_diff = 0;
50a14534
EB
940
941 const guint8 tp_size_reg = 2;
942 const guint8 tp_size_cur = 4;
943 guint8 tp_size;
944
b42a25ba
EB
945 if ( dp->vtl->drawelevation )
946 {
947 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
948 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
949 alt_diff = max_alt - min_alt;
950 }
951
50a14534
EB
952 if ( ! track->visible )
953 return;
954
955 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
956 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
957 trw_layer_draw_track ( name, track, dp, TRUE );
958
959 if ( drawing_white_background )
b42a25ba
EB
960 drawpoints = drawstops = FALSE;
961 else {
50a14534 962 drawpoints = dp->vtl->drawpoints;
b42a25ba
EB
963 drawstops = dp->vtl->drawstops;
964 }
50a14534 965
8e9c992d
EB
966 if ( track == dp->vtl->current_track )
967 main_gc = dp->vtl->current_track_gc;
968 else
969 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
970
50a14534
EB
971 if (list) {
972 int x, y, oldx, oldy;
973 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
974
975 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
976
977 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
978
979 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
980 {
981 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
8e9c992d 982 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
50a14534
EB
983 }
984
985 oldx = x;
986 oldy = y;
987
988 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
989 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_MAX + 1;
990
991 while ((list = g_list_next(list)))
992 {
993 tp = VIK_TRACKPOINT(list->data);
994 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
995
996 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
997 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
998 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
999 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1000 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1001 {
1002 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1003
1004 if ( drawpoints && ! drawing_white_background )
1005 {
1006 if ( list->next ) {
8e9c992d 1007 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
50a14534 1008
8e9c992d 1009 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
734652bf 1010
50a14534 1011 /* stops */
b42a25ba 1012 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
50a14534
EB
1013 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 );
1014 }
1015 else
8e9c992d 1016 vik_viewport_draw_arc ( dp->vp, main_gc, TRUE, x-(2*tp_size), y-(2*tp_size), 4*tp_size, 4*tp_size, 0, 360*64 );
50a14534
EB
1017 }
1018
1019 if ((!tp->newsegment) && (dp->vtl->drawlines))
1020 {
1021 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1022
1023 /* UTM only: zone check */
1024 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
8e9c992d 1025 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
50a14534
EB
1026
1027 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY )
1028 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1029
1030 if (!useoldvals)
1031 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1032
8c4f1350 1033 if ( drawing_white_background ) {
50a14534 1034 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
8c4f1350
EB
1035 }
1036 else {
8c4f1350 1037
8e9c992d 1038 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
b42a25ba
EB
1039 if ( dp->vtl->drawelevation && list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1040 GdkPoint tmp[4];
1041 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1042 if ( list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1043 tmp[0].x = oldx;
1044 tmp[0].y = oldy;
1045 tmp[1].x = oldx;
1046 tmp[1].y = oldy-FIXALTITUDE(list->data);
1047 tmp[2].x = x;
1048 tmp[2].y = y-FIXALTITUDE(list->next->data);
1049 tmp[3].x = x;
1050 tmp[3].y = y;
1051
1052 GdkGC *tmp_gc;
1053 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1054 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1055 else
1056 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1057 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1058 }
8e9c992d 1059 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
8c4f1350
EB
1060 }
1061 }
50a14534
EB
1062 }
1063
1064 oldx = x;
1065 oldy = y;
1066 useoldvals = TRUE;
1067 }
1068 else {
1069 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1070 {
1071 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1072 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1073 {
1074 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1075 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY )
1076 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1077
1078 if ( drawing_white_background )
1079 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1080 else
8e9c992d 1081 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
50a14534
EB
1082 }
1083 else
1084 {
1085 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
8e9c992d 1086 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
50a14534
EB
1087 }
1088 }
1089 useoldvals = FALSE;
1090 }
1091 }
1092 }
1093 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1094 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
1095 dp->track_gc_iter = 0;
1096}
1097
1098/* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1099static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1100{
1101 trw_layer_draw_track ( name, track, dp, FALSE );
1102}
1103
1104static void cached_pixbuf_free ( CachedPixbuf *cp )
1105{
1106 g_object_unref ( G_OBJECT(cp->pixbuf) );
1107 g_free ( cp->image );
1108}
1109
1110static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1111{
1112 return strcmp ( cp->image, name );
1113}
1114
1115static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1116{
1117 if ( wp->visible )
51f0884d 1118 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
50a14534
EB
1119 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1120 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1121 {
1122 gint x, y;
a7f9c01e 1123 GdkPixbuf *sym = NULL;
50a14534
EB
1124 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1125
1126 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1127
1128 if ( wp->image && dp->vtl->drawimages )
1129 {
1130 GdkPixbuf *pixbuf = NULL;
1131 GList *l;
1132
1133 if ( dp->vtl->image_alpha == 0)
1134 return;
1135
1136 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1137 if ( l )
1138 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1139 else
1140 {
1141 gchar *image = wp->image;
1142 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1143 if ( ! regularthumb )
1144 {
1145 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1146 image = "\x12\x00"; /* this shouldn't occur naturally. */
1147 }
1148 if ( regularthumb )
1149 {
1150 CachedPixbuf *cp = NULL;
1151 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1152 if ( dp->vtl->image_size == 128 )
1153 cp->pixbuf = regularthumb;
1154 else
1155 {
1156 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1157 g_assert ( cp->pixbuf );
1158 g_object_unref ( G_OBJECT(regularthumb) );
1159 }
1160 cp->image = g_strdup ( image );
1161
1162 /* needed so 'click picture' tool knows how big the pic is; we don't
1163 * store it in cp because they may have been freed already. */
1164 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1165 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1166
1167 g_queue_push_head ( dp->vtl->image_cache, cp );
1168 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1169 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1170
1171 pixbuf = cp->pixbuf;
1172 }
1173 else
1174 {
1175 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1176 }
1177 }
1178 if ( pixbuf )
1179 {
1180 gint w, h;
1181 w = gdk_pixbuf_get_width ( pixbuf );
1182 h = gdk_pixbuf_get_height ( pixbuf );
1183
1184 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1185 {
1186 if ( dp->vtl->image_alpha == 255 )
1187 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1188 else
1189 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1190 }
1191 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1192 }
1193 }
1194
1195 /* DRAW ACTUAL DOT */
ea3933fc 1196 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
acaf7113
AF
1197 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 );
1198 }
1199 else if ( wp == dp->vtl->current_wp ) {
50a14534
EB
1200 switch ( dp->vtl->wp_symbol ) {
1201 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;
1202 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;
1203 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;
1204 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 );
1205 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 );
1206 }
1207 }
1208 else {
1209 switch ( dp->vtl->wp_symbol ) {
1210 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;
1211 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;
1212 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;
1213 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 );
1214 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;
1215 }
1216 }
1217
1218 if ( dp->vtl->drawlabels )
1219 {
1220 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
a7f9c01e 1221 gint label_x, label_y;
50a14534
EB
1222 gint width, height;
1223 pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 );
1224 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
a7f9c01e
QT
1225 label_x = x - width/2;
1226 if (sym)
1227 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1228 else
1229 label_y = y - dp->vtl->wp_size - height - 2;
1230
1231 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1232 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
50a14534
EB
1233 }
1234 }
1235}
1236
1237void vik_trw_layer_draw ( VikTrwLayer *l, gpointer data )
1238{
1239 static struct DrawingParams dp;
1240 g_assert ( l != NULL );
1241
1242 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1243 dp.vtl = l;
1244
1245 if ( l->tracks_visible )
1246 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1247
1248 if (l->waypoints_visible)
1249 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1250}
1251
1252static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1253{
1254 int i;
1255 if ( vtl->track_bg_gc )
1256 {
1257 g_object_unref ( vtl->track_bg_gc );
1258 vtl->track_bg_gc = NULL;
1259 }
8e9c992d
EB
1260 if ( vtl->current_track_gc )
1261 {
1262 g_object_unref ( vtl->current_track_gc );
1263 vtl->current_track_gc = NULL;
1264 }
50a14534
EB
1265
1266 if ( ! vtl->track_gc )
1267 return;
1268 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1269 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1270 g_array_free ( vtl->track_gc, TRUE );
1271 vtl->track_gc = NULL;
1272}
1273
1274static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1275{
1276 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1277 gint width = vtl->line_thickness;
1278
1279 if ( vtl->track_gc )
1280 trw_layer_free_track_gcs ( vtl );
1281
1282 if ( vtl->track_bg_gc )
1283 g_object_unref ( vtl->track_bg_gc );
1284 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1285
8e9c992d
EB
1286 if ( vtl->current_track_gc )
1287 g_object_unref ( vtl->current_track_gc );
1288 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 );
1289 gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1290
50a14534
EB
1291 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1292
1293 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1294
1295 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1296 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1297 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1298 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1299 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1300 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1301 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1302 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1303 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1304 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1305
1306 gc[11] = vik_viewport_new_gc ( vp, "#ff0000", width ); /* above range */
1307
1308 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1309
1310 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1311}
1312
1313VikTrwLayer *vik_trw_layer_create ( VikViewport *vp )
1314{
1315 VikTrwLayer *rv = vik_trw_layer_new ( 0 );
1316 PangoFontDescription *pfd;
1317 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1318 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1319 pango_layout_set_font_description (rv->wplabellayout, pfd);
1320 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1321 pango_font_description_free (pfd);
1322
1323 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1324
1325 trw_layer_new_track_gcs ( rv, vp );
1326
1327 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1328 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1329 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1330 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1331
1332 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1333
1334 rv->has_verified_thumbnails = FALSE;
1335 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1336 rv->wp_size = 4;
ea3933fc 1337 rv->wp_draw_symbols = TRUE;
50a14534
EB
1338
1339 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1340
20c7a3a0
QT
1341 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1342
50a14534
EB
1343 return rv;
1344}
1345
1346static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[4] )
1347{
1348 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1349
1350#ifdef VIK_CONFIG_ALPHABETIZED_TRW
dc2c040e 1351 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
50a14534
EB
1352#else
1353 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 );
1354#endif
1355
1356 *new_iter = *((GtkTreeIter *) pass_along[1]);
1357 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1358
1359 if ( ! track->visible )
1360 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1361}
1362
1363static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[4] )
1364{
1365 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1366#ifdef VIK_CONFIG_ALPHABETIZED_TRW
dc2c040e 1367 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
50a14534
EB
1368#else
1369 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 );
1370#endif
1371
1372 *new_iter = *((GtkTreeIter *) pass_along[1]);
1373 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1374
1375 if ( ! wp->visible )
1376 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1377}
1378
1379
1380void vik_trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1381{
1382 GtkTreeIter iter2;
1383 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1384
1385#ifdef VIK_CONFIG_ALPHABETIZED_TRW
4c77d5e0 1386 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
50a14534 1387#else
4c77d5e0 1388 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
50a14534
EB
1389#endif
1390 if ( ! vtl->tracks_visible )
1391 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1392
1393 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1394
1395#ifdef VIK_CONFIG_ALPHABETIZED_TRW
4c77d5e0 1396 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
50a14534 1397#else
4c77d5e0 1398 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
50a14534
EB
1399#endif
1400
1401 if ( ! vtl->waypoints_visible )
1402 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1403
1404 pass_along[0] = &(vtl->waypoints_iter);
1405 pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1406
1407 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1408
1409}
1410
1411gboolean vik_trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1412{
1413 switch ( subtype )
1414 {
1415 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1416 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1417 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1418 {
1419 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1420 if (t)
1421 return (t->visible ^= 1);
1422 else
1423 return TRUE;
1424 }
1425 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1426 {
1427 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1428 if (t)
1429 return (t->visible ^= 1);
1430 else
1431 return TRUE;
1432 }
1433 }
1434 return TRUE;
1435}
1436
1437GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
1438{
1439 return l->tracks;
1440}
1441
1442GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
1443{
1444 return l->waypoints;
1445}
1446
1447static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
1448{
1449 static VikCoord fixme;
1450 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
1451 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1452 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1453 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1454 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1455 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1456 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1457 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1458 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1459}
1460
1461static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
1462{
1463 GList *tr = *t;
1464 static VikCoord fixme;
1465
1466 while ( tr )
1467 {
1468 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
1469 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1470 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1471 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1472 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1473 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1474 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1475 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1476 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1477 tr = tr->next;
1478 }
1479}
1480
1481
1482gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
1483{
1484 /* 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... */
1485 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1486 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
1487 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
1488 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1489 return FALSE;
1490 else
1491 {
1492 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1493 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
1494 return TRUE;
1495 }
1496}
1497
1498static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
1499{
1500 VikCoord coord;
1501 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
1502 goto_coord ( VIK_LAYERS_PANEL(layer_and_vlp[1]), &coord );
1503 else
4c77d5e0 1504 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
50a14534
EB
1505}
1506
1507static void trw_layer_export ( gpointer layer_and_vlp[2], guint file_type )
1508{
1509 GtkWidget *file_selector;
1510 const gchar *fn;
1511 gboolean failed = FALSE;
6e4a49aa
MA
1512 file_selector = gtk_file_chooser_dialog_new (_("Export Layer"),
1513 NULL,
1514 GTK_FILE_CHOOSER_ACTION_SAVE,
1515 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1516 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1517 NULL);
1518 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER(file_selector), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])));
1519
1520 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
50a14534 1521 {
6e4a49aa 1522 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
45acf79e 1523 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
50a14534
EB
1524 {
1525 gtk_widget_hide ( file_selector );
1526 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type );
1527 break;
1528 }
1529 else
1530 {
1e80f7a4 1531 if ( a_dialog_overwrite ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
50a14534
EB
1532 {
1533 gtk_widget_hide ( file_selector );
1534 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type );
1535 break;
1536 }
1537 }
1538 }
1539 gtk_widget_destroy ( file_selector );
1540 if ( failed )
4c77d5e0 1541 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
50a14534
EB
1542}
1543
1544static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
1545{
1546 trw_layer_export ( layer_and_vlp, FILE_TYPE_GPSPOINT );
1547}
1548
1549static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
1550{
1551 trw_layer_export ( layer_and_vlp, FILE_TYPE_GPSMAPPER );
1552}
1553
561e6ad0
EB
1554static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
1555{
1556 trw_layer_export ( layer_and_vlp, FILE_TYPE_GPX );
1557}
1558
50a14534
EB
1559static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
1560{
1561 GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
4c77d5e0 1562 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Create"),
50a14534
EB
1563 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
1564 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1565 GTK_STOCK_CANCEL,
1566 GTK_RESPONSE_REJECT,
1567 GTK_STOCK_OK,
1568 GTK_RESPONSE_ACCEPT,
1569 NULL);
1570
1571 GtkWidget *label, *entry;
4c77d5e0 1572 label = gtk_label_new(_("Waypoint Name:"));
50a14534
EB
1573 entry = gtk_entry_new();
1574
1575 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
1576 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
1577 gtk_widget_show_all ( label );
1578 gtk_widget_show_all ( entry );
1579
1580 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
1581 {
1582 VikWaypoint *wp;
1583 gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
1584 int i;
1585
1586 for ( i = strlen(upname)-1; i >= 0; i-- )
1587 upname[i] = toupper(upname[i]);
1588
1589 wp = g_hash_table_lookup ( wps, upname );
1590
1591 if (!wp)
4c77d5e0 1592 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
50a14534
EB
1593 else
1594 {
1595 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
1596 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
1597 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 ) );
1598 break;
1599 }
1600
1601 g_free ( upname );
1602
1603 }
1604 gtk_widget_destroy ( dia );
1605}
1606
1607gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
1608{
a8fe53f8 1609 gchar *name = highest_wp_number_get(vtl);
acaf7113
AF
1610 VikWaypoint *wp = vik_waypoint_new();
1611 wp->coord = *def_coord;
1612 wp->altitude = VIK_DEFAULT_ALTITUDE;
50a14534 1613
acaf7113 1614 if ( a_dialog_new_waypoint ( w, &name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode ) )
50a14534 1615 {
805d282e 1616 wp->visible = TRUE;
50a14534
EB
1617 vik_trw_layer_add_waypoint ( vtl, name, wp );
1618 return TRUE;
1619 }
acaf7113 1620 vik_waypoint_free(wp);
50a14534
EB
1621 return FALSE;
1622}
1623
1624static void trw_layer_new_wp ( gpointer lav[2] )
1625{
1626 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
1627 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
1628 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
1629 instead return true if you want to update. */
1630 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 )
1631 vik_layers_panel_emit_update ( vlp );
1632}
1633
1634void vik_trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
1635{
1636 static gpointer pass_along[2];
1637 GtkWidget *item;
98fcbbdb 1638 GtkWidget *export_submenu;
50a14534
EB
1639 pass_along[0] = vtl;
1640 pass_along[1] = vlp;
1641
1642 item = gtk_menu_item_new();
1643 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1644 gtk_widget_show ( item );
1645
4c77d5e0 1646 item = gtk_menu_item_new_with_label ( _("Goto Center of Layer") );
50a14534
EB
1647 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
1648 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1649 gtk_widget_show ( item );
1650
4c77d5e0 1651 item = gtk_menu_item_new_with_label ( _("Goto Waypoint") );
50a14534
EB
1652 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
1653 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1654 gtk_widget_show ( item );
1655
1bd88e66 1656 export_submenu = gtk_menu_new ();
4c77d5e0 1657 item = gtk_menu_item_new_with_label ( _("Export layer") );
50a14534
EB
1658 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1659 gtk_widget_show ( item );
98fcbbdb 1660 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
1bd88e66 1661
4c77d5e0 1662 item = gtk_menu_item_new_with_label ( _("Export as GPSPoint") );
1bd88e66
GB
1663 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
1664 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
1665 gtk_widget_show ( item );
50a14534 1666
4c77d5e0 1667 item = gtk_menu_item_new_with_label ( _("Export as GPSMapper") );
50a14534 1668 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
1bd88e66 1669 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
50a14534
EB
1670 gtk_widget_show ( item );
1671
4c77d5e0 1672 item = gtk_menu_item_new_with_label ( _("Export as GPX") );
561e6ad0 1673 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
1bd88e66 1674 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
561e6ad0
EB
1675 gtk_widget_show ( item );
1676
4c77d5e0 1677 item = gtk_menu_item_new_with_label ( _("New Waypoint") );
50a14534
EB
1678 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
1679 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1680 gtk_widget_show ( item );
3e7553ae
GB
1681
1682#ifdef VIK_CONFIG_OPENSTREETMAP
4c77d5e0 1683 item = gtk_menu_item_new_with_label ( _("Upload to OSM") );
3e7553ae
GB
1684 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
1685 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1686 gtk_widget_show ( item );
1687#endif
28c82d8b
EB
1688
1689 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
1690 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
1691 if ( item ) {
1692 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1693 gtk_widget_show ( item );
1694 }
1695
1696 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
1697 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
1698 if ( item ) {
1699 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1700 gtk_widget_show ( item );
1701 }
50a14534
EB
1702}
1703
1704void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
1705{
1706 if ( VIK_LAYER(vtl)->realized )
1707 {
1708 VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
1709 if ( oldwp )
1710 wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
1711 else
1712 {
1713 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
1714#ifdef VIK_CONFIG_ALPHABETIZED_TRW
1715 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
1716#else
1717 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
1718#endif
1719 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
1720 g_hash_table_insert ( vtl->waypoints_iters, name, iter );
1721 wp->visible = TRUE;
1722 }
1723 }
1724 else
1725 wp->visible = TRUE;
1726
a8fe53f8 1727 highest_wp_number_add_wp(vtl, name);
50a14534
EB
1728 g_hash_table_insert ( vtl->waypoints, name, wp );
1729
1730}
1731
1732void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
1733{
1734 if ( VIK_LAYER(vtl)->realized )
1735 {
1736 VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
1737 if ( oldt )
1738 t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
1739 else
1740 {
1741 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
1742#ifdef VIK_CONFIG_ALPHABETIZED_TRW
1743 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
1744#else
1745 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
1746#endif
1747 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
1748 g_hash_table_insert ( vtl->tracks_iters, name, iter );
805d282e 1749 /* t->visible = TRUE; */
50a14534
EB
1750 }
1751 }
1752 else
805d282e 1753 ; /* t->visible = TRUE; // this is now used by file input functions */
50a14534
EB
1754
1755 g_hash_table_insert ( vtl->tracks, name, t );
1756
1757}
1758
50a14534 1759/* to be called whenever a track has been deleted or may have been changed. */
21700912 1760void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
50a14534
EB
1761{
1762 if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
1763 trw_layer_cancel_current_tp ( vtl, FALSE );
1764 else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
1765 trw_layer_cancel_last_tp ( vtl );
1766}
e890a6e6
EB
1767
1768static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
1769{
1770 gint i = 2;
1771 gchar *newname = g_strdup(name);
1772 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
941aa6e9 1773 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
e890a6e6
EB
1774 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
1775 g_free(newname);
1776 newname = new_newname;
1777 i++;
1778 }
1779 return newname;
1780}
50a14534 1781
805d282e
EB
1782void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
1783{
1784 vik_trw_layer_add_waypoint ( vtl,
1785 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
1786 wp );
1787}
1788void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
1789{
bddd2056 1790 if ( vtl->magic_scissors_append && vtl->magic_scissors_current_track ) {
c3deba01 1791 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
bddd2056
EB
1792 vik_track_steal_and_append_trackpoints ( vtl->magic_scissors_current_track, tr );
1793 vik_track_free ( tr );
1794 vtl->magic_scissors_append = FALSE; /* this means we have added it */
1795 } else {
1796 gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name);
1797 vik_trw_layer_add_track ( vtl, new_name, tr );
1798
1799 if ( vtl->magic_scissors_check_added_track ) {
c3deba01 1800 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
bddd2056
EB
1801 if ( vtl->magic_scissors_added_track_name ) /* for google routes */
1802 g_free ( vtl->magic_scissors_added_track_name );
1803 vtl->magic_scissors_added_track_name = g_strdup(new_name);
1804 }
1805 }
805d282e
EB
1806}
1807
c48517ad
AF
1808static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
1809{
1810 *l = g_list_append(*l, (gpointer)name);
1811}
1812
1813static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
1814{
1815 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
1816 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
1817 VikTrack *t;
1818 t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
1819 vik_trw_layer_delete_track(vtl_src, name);
1820 vik_trw_layer_add_track(vtl_dest, newname, t);
1821 }
1822 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
1823 VikWaypoint *w;
1824 w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
1825 vik_trw_layer_delete_waypoint(vtl_src, name);
1826 vik_trw_layer_add_waypoint(vtl_dest, newname, w);
1827 }
1828}
1829
70a23263 1830static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
e4afc73a
EB
1831{
1832 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
c48517ad
AF
1833 gint type = vik_treeview_item_get_data(vt, src_item_iter);
1834
e4afc73a 1835 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
b637058e
EB
1836 GList *items = NULL;
1837 GList *iter;
e4afc73a 1838
c48517ad
AF
1839 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
1840 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
1841 }
1842 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
1843 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
1844 }
1845
1846 iter = items;
1847 while (iter) {
1848 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
1849 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
1850 } else {
1851 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1852 }
1853 iter = iter->next;
e4afc73a 1854 }
c48517ad 1855 if (items)
b637058e 1856 g_list_free(items);
c48517ad
AF
1857 } else {
1858 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
1859 trw_layer_move_item(vtl_src, vtl_dest, name, type);
e4afc73a
EB
1860 }
1861}
1862
1863
1864gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
50a14534
EB
1865{
1866 VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
1867 gboolean was_visible = FALSE;
1868 if ( t )
1869 {
1870 GtkTreeIter *it;
1871 was_visible = t->visible;
1872 if ( t == vtl->current_track )
1873 vtl->current_track = NULL;
bddd2056
EB
1874 if ( t == vtl->magic_scissors_current_track )
1875 vtl->magic_scissors_current_track = NULL;
50a14534
EB
1876
1877 /* could be current_tp, so we have to check */
1878 trw_layer_cancel_tps_of_track ( vtl, trk_name );
1879
1880 g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
1881 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
1882 g_hash_table_remove ( vtl->tracks_iters, trk_name );
1883
1884 /* do this last because trk_name may be pointing to actual orig key */
1885 g_hash_table_remove ( vtl->tracks, trk_name );
1886 }
1887 return was_visible;
1888}
1889
e4afc73a
EB
1890gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
1891{
1892 gboolean was_visible = FALSE;
1893 VikWaypoint *wp;
1894
1895 wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
1896 if ( wp ) {
1897 GtkTreeIter *it;
1898
1899 if ( wp == vtl->current_wp ) {
1900 vtl->current_wp = NULL;
1901 vtl->current_wp_name = NULL;
1902 vtl->moving_wp = FALSE;
1903 }
1904
1905 was_visible = wp->visible;
1906 g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
1907 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
1908 g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
a8fe53f8
EB
1909
1910 highest_wp_number_remove_wp(vtl, wp_name);
e4afc73a
EB
1911 g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
1912 }
1913
1914 return was_visible;
1915}
1916
700b0908
QT
1917static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
1918{
1919 vik_treeview_item_delete (vt, it );
1920}
1921
1922void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
1923{
1924
1925 vtl->current_track = NULL;
bddd2056 1926 vtl->magic_scissors_current_track = NULL;
700b0908
QT
1927 if (vtl->current_tp_track_name)
1928 trw_layer_cancel_current_tp(vtl, FALSE);
1929 if (vtl->last_tp_track_name)
1930 trw_layer_cancel_last_tp ( vtl );
1931
1932 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
1933 g_hash_table_remove_all(vtl->tracks_iters);
1934 g_hash_table_remove_all(vtl->tracks);
1935
1936 /* TODO: only update if the layer is visible (ticked) */
1937 vik_layer_emit_update ( VIK_LAYER(vtl) );
1938}
1939
1940void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
1941{
1942 vtl->current_wp = NULL;
1943 vtl->current_wp_name = NULL;
1944 vtl->moving_wp = FALSE;
1945
a8fe53f8
EB
1946 highest_wp_number_reset(vtl);
1947
700b0908
QT
1948 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
1949 g_hash_table_remove_all(vtl->waypoints_iters);
1950 g_hash_table_remove_all(vtl->waypoints);
1951
1952 /* TODO: only update if the layer is visible (ticked) */
1953 vik_layer_emit_update ( VIK_LAYER(vtl) );
1954}
1955
50a14534
EB
1956static void trw_layer_delete_item ( gpointer pass_along[5] )
1957{
1958 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
1959 gboolean was_visible = FALSE;
dc2c040e 1960 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 1961 {
e4afc73a 1962 was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
50a14534
EB
1963 }
1964 else
1965 {
e4afc73a 1966 was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
50a14534 1967 }
50a14534
EB
1968 if ( was_visible )
1969 vik_layer_emit_update ( VIK_LAYER(vtl) );
1970}
1971
1972
1973static void trw_layer_properties_item ( gpointer pass_along[5] )
1974{
1975 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
dc2c040e 1976 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534
EB
1977 {
1978 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
1979 if ( wp )
1980 {
1981 if ( a_dialog_new_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), NULL, wp, NULL, vtl->coord_mode ) )
1982
1983 if ( VIK_LAYER(vtl)->visible )
1984 vik_layer_emit_update ( VIK_LAYER(vtl) );
1985 }
1986 }
1987 else
1988 {
1989 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
1990 if ( tr )
1991 {
21700912
QT
1992 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
1993 vtl, tr,
1994 pass_along[1], /* vlp */
1995 pass_along[3] /* track name */);
50a14534
EB
1996 }
1997 }
1998}
1999
2000static void goto_coord ( VikLayersPanel *vlp, const VikCoord *coord )
2001{
2002 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), coord );
2003 vik_layers_panel_emit_update ( vlp );
2004}
2005
2006static void trw_layer_goto_track_startpoint ( gpointer pass_along[5] )
2007{
2008 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2009 if ( trps && trps->data )
2010 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord));
2011}
2012
2013static void trw_layer_goto_track_center ( gpointer pass_along[5] )
2014{
8fb71d6c 2015 /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
50a14534
EB
2016 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2017 if ( trps && *trps )
2018 {
2019 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
2020 VikCoord coord;
2021 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2022 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
2023 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
2024 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
2025 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &coord);
2026 }
2027}
2028
8fb71d6c
EB
2029static void trw_layer_extend_track_end ( gpointer pass_along[6] )
2030{
2031 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2032 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2033
2034 vtl->current_track = track;
a7955c1d 2035 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
8fb71d6c
EB
2036
2037 if ( track->trackpoints )
2038 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
2039}
2040
e3154bef
GB
2041/**
2042 * extend a track using magic scissors
2043 */
a7955c1d
AM
2044static void trw_layer_extend_track_end_ms ( gpointer pass_along[6] )
2045{
2046 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2047 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2048 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
2049
2050 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
2051 vtl->magic_scissors_coord = last_coord;
2052 vtl->magic_scissors_current_track = track;
2053
2054 if ( track->trackpoints )
2055 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &last_coord) ;
2056
2057}
a7955c1d 2058
ad0a8c2d
EB
2059static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
2060{
2061 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
2062 /* Also warn if overwrite old elevation data */
2063 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2064
55906514 2065 vik_track_apply_dem_data ( track );
ad0a8c2d
EB
2066}
2067
50a14534
EB
2068static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
2069{
2070 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2071 if ( !trps )
2072 return;
111fa174 2073 trps = g_list_last(trps);
50a14534
EB
2074 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord));
2075}
2076
111fa174
AF
2077
2078/*************************************
2079 * merge/split by time routines
2080 *************************************/
2081
15f45edc
QT
2082/* called for each key in track hash table.
2083 * If the current track has time stamp, add it to the result,
2084 * except the one pointed by "exclude".
2085 * set exclude to NULL if there is no exclude to check.
2086 * Not that result is in reverse (for performance reason).
2087 */
2088typedef struct {
2089 GList **result;
2090 GList *exclude;
2091} twt_udata;
2092static void find_tracks_with_timestamp(gpointer key, gpointer value, gpointer udata)
2093{
2094 twt_udata *user_data = udata;
2095 VikTrackpoint *p1, *p2;
2096
2097 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
2098 return;
2099 }
2100
2101 if (VIK_TRACK(value)->trackpoints) {
2102 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2103 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2104
2105 if (!p1->has_timestamp || !p2->has_timestamp) {
2106 g_print("no timestamp\n");
2107 return;
2108 }
2109
2110 }
2111
2112 *(user_data->result) = g_list_prepend(*(user_data->result), key);
2113}
2114
111fa174
AF
2115/* called for each key in track hash table. if original track user_data[1] is close enough
2116 * to the passed one, add it to list in user_data[0]
2117 */
2118static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
2119{
2120 time_t t1, t2;
2121 VikTrackpoint *p1, *p2;
2122
2123 GList **nearby_tracks = ((gpointer *)user_data)[0];
2124 GList *orig_track = ((gpointer *)user_data)[1];
dc2c040e 2125 guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
111fa174 2126
a61b2619
AF
2127 /* outline:
2128 * detect reasons for not merging, and return
2129 * if no reason is found not to merge, then do it.
2130 */
111fa174
AF
2131
2132 if (VIK_TRACK(value)->trackpoints == orig_track) {
2133 return;
2134 }
2135
a61b2619
AF
2136 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
2137 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
111fa174 2138
a61b2619
AF
2139 if (VIK_TRACK(value)->trackpoints) {
2140 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2141 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2142
2143 if (!p1->has_timestamp || !p2->has_timestamp) {
2144 g_print("no timestamp\n");
2145 return;
2146 }
111fa174 2147
a61b2619
AF
2148 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
2149 if (! (abs(t1 - p2->timestamp) < thr*60 ||
2150 /* p1 p2 t1 t2 */
2151 abs(p1->timestamp - t2) < thr*60)
2152 /* t1 t2 p1 p2 */
2153 ) {
2154 return;
2155 }
111fa174 2156 }
a61b2619
AF
2157
2158 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
111fa174
AF
2159}
2160
2161/* comparison function used to sort tracks; a and b are hash table keys */
2162static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
2163{
2164 GHashTable *tracks = user_data;
2165 time_t t1, t2;
2166
2167 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
2168 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
2169
2170 if (t1 < t2) return -1;
2171 if (t1 > t2) return 1;
2172 return 0;
2173}
2174
2175/* comparison function used to sort trackpoints */
2176static gint trackpoint_compare(gconstpointer a, gconstpointer b)
2177{
2178 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
2179
2180 if (t1 < t2) return -1;
2181 if (t1 > t2) return 1;
2182 return 0;
2183}
2184
291edcab
HR
2185static void trw_layer_merge_with_other ( gpointer pass_along[6] )
2186{
2187 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
15f45edc 2188 gchar *orig_track_name = pass_along[3];
15f45edc
QT
2189 GList *tracks_with_timestamp = NULL;
2190 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
2191
2192 if (track->trackpoints &&
2193 !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
2194 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
2195 return;
2196 }
2197
2198 if (1) {
2199
2200 twt_udata udata;
2201 udata.result = &tracks_with_timestamp;
2202 udata.exclude = track->trackpoints;
2203 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp, (gpointer)&udata);
2204 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
2205 }
291edcab 2206
15f45edc
QT
2207 if (!tracks_with_timestamp) {
2208 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
2209 return;
2210 }
2211
7767aa02
QT
2212 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
2213 vtl->tracks, tracks_with_timestamp, TRUE,
2214 _("Merge with..."), _("Select track to merge with"));
15f45edc 2215 g_list_free(tracks_with_timestamp);
291edcab 2216
7767aa02 2217 if (merge_list)
291edcab 2218 {
7767aa02
QT
2219 GList *l;
2220 for (l = merge_list; l != NULL; l = g_list_next(l)) {
2221 VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
2222 if (merge_track) {
2223 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
2224 merge_track->trackpoints = NULL;
2225 vik_trw_layer_delete_track(vtl, l->data);
2226 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
2227 }
291edcab 2228 }
7767aa02
QT
2229 /* TODO: free data before free merge_list */
2230 for (l = merge_list; l != NULL; l = g_list_next(l))
2231 g_free(l->data);
2232 g_list_free(merge_list);
15f45edc 2233 vik_layer_emit_update( VIK_LAYER(vtl) );
291edcab 2234 }
291edcab
HR
2235}
2236
111fa174
AF
2237/* merge by time routine */
2238static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
2239{
a61b2619
AF
2240 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
2241 gchar *orig_track_name = strdup(pass_along[3]);
2242
111fa174 2243 time_t t1, t2;
a61b2619 2244 GList *nearby_tracks;
111fa174
AF
2245 VikTrack *track;
2246 GList *trps;
2247 static guint thr = 1;
2248 guint track_count = 0;
111fa174 2249
a61b2619 2250 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4c77d5e0 2251 _("Merge Threshold..."),
a61b2619 2252 _("Merge when time between tracks less than:"),
111fa174
AF
2253 &thr)) {
2254 return;
2255 }
2256
2257 /* merge tracks until we can't */
a61b2619 2258 nearby_tracks = NULL;
111fa174
AF
2259 do {
2260 gpointer params[3];
2261
a61b2619 2262 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
111fa174
AF
2263 trps = track->trackpoints;
2264 if ( !trps )
2265 return;
2266
2267
2268 if (nearby_tracks) {
2269 g_list_free(nearby_tracks);
2270 nearby_tracks = NULL;
2271 }
2272
2273 t1 = ((VikTrackpoint *)trps->data)->timestamp;
2274 t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
2275
70a23263 2276 /* g_print("Original track times: %d and %d\n", t1, t2); */
111fa174
AF
2277 params[0] = &nearby_tracks;
2278 params[1] = trps;
dc2c040e 2279 params[2] = GUINT_TO_POINTER (thr);
111fa174
AF
2280
2281 /* get a list of adjacent-in-time tracks */
a61b2619 2282 g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
111fa174
AF
2283
2284 /* add original track */
2285 nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
2286
111fa174
AF
2287 /* merge them */
2288 {
a61b2619 2289#define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
111fa174
AF
2290#define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
2291#define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
2292 GList *l = nearby_tracks;
2293 VikTrack *tr = vik_track_new();
2294 tr->visible = track->visible;
2295 track_count = 0;
2296 while (l) {
2297 /*
2298 time_t t1, t2;
2299 t1 = get_first_trackpoint(l)->timestamp;
2300 t2 = get_last_trackpoint(l)->timestamp;
70a23263 2301 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
111fa174
AF
2302 */
2303
2304
2305 /* remove trackpoints from merged track, delete track */
2306 tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
2307 get_track(l)->trackpoints = NULL;
a61b2619 2308 vik_trw_layer_delete_track(vtl, l->data);
111fa174
AF
2309
2310 track_count ++;
2311 l = g_list_next(l);
2312 }
2313 tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
a61b2619 2314 vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
111fa174
AF
2315
2316#undef get_first_trackpoint
2317#undef get_last_trackpoint
2318#undef get_track
2319 }
2320 } while (track_count > 1);
2321 g_list_free(nearby_tracks);
2322 free(orig_track_name);
0654760a 2323 vik_layer_emit_update( VIK_LAYER(vtl) );
111fa174
AF
2324}
2325
2326/* split by time routine */
2327static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
2328{
2329 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2330 GList *trps = track->trackpoints;
2331 GList *iter;
2332 GList *newlists = NULL;
2333 GList *newtps = NULL;
2334 guint i;
2335 static guint thr = 1;
2336
2337 time_t ts, prev_ts;
2338
2339 if ( !trps )
2340 return;
2341
2342 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4c77d5e0
GB
2343 _("Split Threshold..."),
2344 _("Split when time between trackpoints exceeds:"),
111fa174
AF
2345 &thr)) {
2346 return;
2347 }
2348
2349 /* iterate through trackpoints, and copy them into new lists without touching original list */
2350 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
2351 iter = trps;
2352
2353 while (iter) {
2354 ts = VIK_TRACKPOINT(iter->data)->timestamp;
2355 if (ts < prev_ts) {
70a23263 2356 g_print("panic: ts < prev_ts: this should never happen!\n");
111fa174
AF
2357 return;
2358 }
2359 if (ts - prev_ts > thr*60) {
2360 /* flush accumulated trackpoints into new list */
2361 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
2362 newtps = NULL;
2363 }
2364
2365 /* accumulate trackpoint copies in newtps, in reverse order */
2366 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
2367 prev_ts = ts;
2368 iter = g_list_next(iter);
2369 }
2370 if (newtps) {
2371 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
2372 }
2373
2374 /* put lists of trackpoints into tracks */
2375 iter = newlists;
2376 i = 1;
2377 while (iter) {
2378 gchar *new_tr_name;
2379 VikTrack *tr;
2380
2381 tr = vik_track_new();
2382 tr->visible = track->visible;
2383 tr->trackpoints = (GList *)(iter->data);
2384
2385 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
2386 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
70a23263 2387 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
111fa174
AF
2388 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
2389
2390 iter = g_list_next(iter);
2391 }
2392 g_list_free(newlists);
e4afc73a 2393 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
111fa174
AF
2394 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
2395}
2396
2397/* end of split/merge routines */
2398
2399
50a14534
EB
2400static void trw_layer_goto_waypoint ( gpointer pass_along[5] )
2401{
2402 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2403 if ( wp )
2404 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(wp->coord) );
2405}
2406
2407static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[5] )
2408{
2409 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
7d02a0b0 2410 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
50a14534
EB
2411 g_free ( webpage );
2412}
2413
2414const gchar *vik_trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
2415{
2416 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2417 {
50a14534
EB
2418 gchar *rv;
2419 VikWaypoint *wp;
2420
8499a412 2421 if (strcmp(newname, sublayer) == 0 )
50a14534
EB
2422 return NULL;
2423
8499a412
QT
2424 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
2425 if (g_hash_table_lookup( l->waypoints, newname))
2426 {
2427 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
2428 return NULL;
2429 }
50a14534
EB
2430 }
2431
50a14534
EB
2432 iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
2433 g_hash_table_steal ( l->waypoints_iters, sublayer );
2434
e4afc73a 2435 wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
a8fe53f8 2436 highest_wp_number_remove_wp(l, sublayer);
e4afc73a
EB
2437 g_hash_table_remove ( l->waypoints, sublayer );
2438
50a14534 2439 rv = g_strdup(newname);
50a14534
EB
2440
2441 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
2442
a8fe53f8 2443 highest_wp_number_add_wp(l, rv);
50a14534
EB
2444 g_hash_table_insert ( l->waypoints, rv, wp );
2445 g_hash_table_insert ( l->waypoints_iters, rv, iter );
2446
2447 /* it hasn't been updated yet so we pass new name */
2448#ifdef VIK_CONFIG_ALPHABETIZED_TRW
2449 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
2450#endif
2451
2452 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2453 return rv;
2454 }
2455 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2456 {
50a14534
EB
2457 gchar *rv;
2458 VikTrack *tr;
2459 GtkTreeIter *iter;
2460 gchar *orig_key;
2461
8499a412 2462 if (strcmp(newname, sublayer) == 0)
50a14534
EB
2463 return NULL;
2464
8499a412
QT
2465 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
2466 if (g_hash_table_lookup( l->waypoints, newname))
2467 {
2468 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
2469 return NULL;
2470 }
50a14534
EB
2471 }
2472
886031df 2473 g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
50a14534
EB
2474 g_hash_table_steal ( l->tracks, sublayer );
2475
2476 iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
2477 g_hash_table_steal ( l->tracks_iters, sublayer );
2478
2479 rv = g_strdup(newname);
50a14534
EB
2480
2481 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
2482
2483 g_hash_table_insert ( l->tracks, rv, tr );
2484 g_hash_table_insert ( l->tracks_iters, rv, iter );
2485
2486 /* don't forget about current_tp_track_name, update that too */
2487 if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
2488 {
2489 l->current_tp_track_name = rv;
2490 if ( l->tpwin )
2491 vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
2492 }
2493 else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
2494 l->last_tp_track_name = rv;
2495
2496 g_free ( orig_key );
2497
2498#ifdef VIK_CONFIG_ALPHABETIZED_TRW
2499 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
2500#endif
2501
2502 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2503 return rv;
2504 }
2505 return NULL;
2506}
2507
2508static gboolean is_valid_geocache_name ( gchar *str )
2509{
2510 gint len = strlen ( str );
0c1044e9 2511 return len >= 3 && len <= 7 && str[0] == 'G' && str[1] == 'C' && isalnum(str[2]) && (len < 4 || isalnum(str[3])) && (len < 5 || isalnum(str[4])) && (len < 6 || isalnum(str[5])) && (len < 7 || isalnum(str[6]));
50a14534
EB
2512}
2513
28c82d8b
EB
2514static void trw_layer_track_use_with_filter ( gpointer *pass_along )
2515{
2516 gchar *track_name = (gchar *) pass_along[3];
2517 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
2518 a_acquire_set_filter_track ( tr, track_name );
2519}
2520
bddd2056
EB
2521static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
2522{
2523 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
2524 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
2525}
2526
2527static void trw_layer_track_google_route_webpage( gpointer *pass_along )
2528{
2529 gchar *track_name = (gchar *) pass_along[3];
2530 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
2531 if ( tr ) {
2532 gchar *escaped = uri_escape ( tr->comment );
2533 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7d02a0b0 2534 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
bddd2056 2535 g_free ( escaped );
bddd2056
EB
2536 g_free ( webpage );
2537 }
2538}
2539
50a14534
EB
2540/* vlp can be NULL if necessary - i.e. right-click from a tool -- but be careful, some functions may try to use it */
2541gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
2542{
2543 static GtkTreeIter staticiter;
2544 static gpointer pass_along[5];
2545 GtkWidget *item;
2546 gboolean rv = FALSE;
2547
2548 pass_along[0] = l;
2549 pass_along[1] = vlp;
dc2c040e 2550 pass_along[2] = GINT_TO_POINTER (subtype);
50a14534
EB
2551 pass_along[3] = sublayer;
2552 staticiter = *iter; /* will exist after function has ended */
2553 pass_along[4] = &staticiter;
2554
2555 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2556 {
2557 rv = TRUE;
2558
2559 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
2560 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
2561 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2562 gtk_widget_show ( item );
2563
21700912
QT
2564 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2565 VikTrwLayer *vtl = l;
2566 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
2567 if (tr && tr->property_dialog)
2568 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
2569 }
2570
2cebc318
QT
2571 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
2572 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
2573 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2574 gtk_widget_show ( item );
2575
2576 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
2577 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
2578 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2579 gtk_widget_show ( item );
2580
50a14534
EB
2581 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
2582 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
2583 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2584 gtk_widget_show ( item );
2585
2586 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2587 {
2588 /* could be a right-click using the tool */
2589 if ( vlp != NULL ) {
4c77d5e0 2590 item = gtk_menu_item_new_with_label ( _("Goto") );
50a14534
EB
2591 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
2592 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2593 gtk_widget_show ( item );
2594 }
2595
2596 if ( is_valid_geocache_name ( (gchar *) sublayer ) )
2597 {
4c77d5e0 2598 item = gtk_menu_item_new_with_label ( _("Visit Geocache Webpage") );
50a14534
EB
2599 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
2600 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2601 gtk_widget_show ( item );
2602 }
2603
2604 }
2605 }
2606
2607 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2608 {
2609 item = gtk_menu_item_new ();
2610 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2611 gtk_widget_show ( item );
2612
4c77d5e0 2613 item = gtk_menu_item_new_with_label ( _("Goto Startpoint") );
50a14534
EB
2614 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
2615 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2616 gtk_widget_show ( item );
2617
4c77d5e0 2618 item = gtk_menu_item_new_with_label ( _("Goto \"Center\"") );
50a14534
EB
2619 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
2620 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2621 gtk_widget_show ( item );
2622
4c77d5e0 2623 item = gtk_menu_item_new_with_label ( _("Goto Endpoint") );
50a14534
EB
2624 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
2625 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2626 gtk_widget_show ( item );
111fa174 2627
4c77d5e0 2628 item = gtk_menu_item_new_with_label ( _("Merge By Time") );
111fa174
AF
2629 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
2630 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2631 gtk_widget_show ( item );
2632
7767aa02 2633 item = gtk_menu_item_new_with_label ( _("Merge With Other Tracks...") );
291edcab
HR
2634 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
2635 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2636 gtk_widget_show ( item );
2637
4c77d5e0 2638 item = gtk_menu_item_new_with_label ( _("Split By Time") );
111fa174
AF
2639 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
2640 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2641 gtk_widget_show ( item );
7114e879 2642
4c77d5e0 2643 item = gtk_menu_item_new_with_label ( _("Download maps along track...") );
7114e879
QT
2644 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
2645 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2646 gtk_widget_show ( item );
ad0a8c2d 2647
4c77d5e0 2648 item = gtk_menu_item_new_with_label ( _("Apply DEM Data") );
ad0a8c2d
EB
2649 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
2650 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8fb71d6c
EB
2651 gtk_widget_show ( item );
2652
e3154bef 2653 item = gtk_menu_item_new_with_label ( _("Extend track end") );
8fb71d6c
EB
2654 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
2655 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
ad0a8c2d 2656 gtk_widget_show ( item );
5092de80 2657
e3154bef 2658 item = gtk_menu_item_new_with_label ( _("Extend using magic scissors") );
a7955c1d
AM
2659 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_ms), pass_along );
2660 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2661 gtk_widget_show ( item );
2662
5092de80 2663#ifdef VIK_CONFIG_OPENSTREETMAP
4c77d5e0 2664 item = gtk_menu_item_new_with_label ( _("Upload to OSM") );
5092de80
GB
2665 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
2666 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2667 gtk_widget_show ( item );
2668#endif
bddd2056
EB
2669
2670 if ( is_valid_google_route ( l, (gchar *) sublayer ) )
2671 {
e3154bef 2672 item = gtk_menu_item_new_with_label ( _("View Google Directions") );
bddd2056
EB
2673 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
2674 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2675 gtk_widget_show ( item );
2676 }
2677
e3154bef 2678 item = gtk_menu_item_new_with_label ( _("Use with filter") );
28c82d8b
EB
2679 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
2680 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2681 gtk_widget_show ( item );
2682
2683 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
2684 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
2685 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
2686 if ( item ) {
2687 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2688 gtk_widget_show ( item );
2689 }
50a14534
EB
2690 }
2691
2692 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
2693 {
2694 item = gtk_menu_item_new ();
2695 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2696 gtk_widget_show ( item );
2697
4c77d5e0 2698 item = gtk_menu_item_new_with_label ( _("New Waypoint") );
50a14534
EB
2699 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2700 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2701 gtk_widget_show ( item );
2702 }
2703
2704 return rv;
2705}
2706
50a14534
EB
2707
2708/* to be called when last_tpl no long exists. */
2709static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
2710{
2711 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
2712 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
2713 vtl->last_tpl = NULL;
2714 vtl->last_tp_track_name = NULL;
2715}
2716
2717static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
2718{
2719 if ( vtl->tpwin )
2720 {
2721 if ( destroy)
2722 {
2723 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
2724 vtl->tpwin = NULL;
2725 }
2726 else
2727 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
2728 }
2729 if ( vtl->current_tpl )
2730 {
2731 vtl->current_tpl = NULL;
2732 vtl->current_tp_track_name = NULL;
2733 vik_layer_emit_update(VIK_LAYER(vtl));
2734 }
2735}
2736
2737static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
2738{
2739 g_assert ( vtl->tpwin != NULL );
2740 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
2741 trw_layer_cancel_current_tp ( vtl, TRUE );
2742 else if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
2743 {
2744 gchar *name;
2745 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks ) ) )
2746 {
2747 VikTrack *tr = vik_track_new ();
2748 GList *newglist = g_list_alloc ();
2749 newglist->prev = NULL;
2750 newglist->next = vtl->current_tpl->next;
2751 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
2752 tr->trackpoints = newglist;
2753
2754 vtl->current_tpl->next->prev = newglist; /* end old track here */
2755 vtl->current_tpl->next = NULL;
2756
2757 vtl->current_tpl = newglist; /* change tp to first of new track. */
2758 vtl->current_tp_track_name = name;
2759
2760 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
2761
2762 vik_trw_layer_add_track ( vtl, name, tr );
2763 vik_layer_emit_update(VIK_LAYER(vtl));
2764 }
2765 }
2766 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
2767 {
2768 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
2769 GList *new_tpl;
2770 g_assert(tr != NULL);
2771
2772 /* can't join with a non-existent trackpoint */
2773 vtl->last_tpl = NULL;
2774 vtl->last_tp_track_name = NULL;
2775
2776 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
2777 {
2778 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
2779 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
2780
2781 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
2782
2783 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
2784 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
2785
2786 trw_layer_cancel_last_tp ( vtl );
2787
2788 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
2789 g_list_free_1 ( vtl->current_tpl );
2790 vtl->current_tpl = new_tpl;
2791 vik_layer_emit_update(VIK_LAYER(vtl));
2792 }
2793 else
2794 {
2795 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
2796 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
2797 g_list_free_1 ( vtl->current_tpl );
2798 trw_layer_cancel_current_tp ( vtl, FALSE );
2799 }
2800 }
2801 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
2802 {
2803 vtl->last_tpl = vtl->current_tpl;
2804 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
2805 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
2806 }
2807 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
2808 {
2809 vtl->last_tpl = vtl->current_tpl;
2810 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
2811 vik_layer_emit_update(VIK_LAYER(vtl));
2812 }
2813 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
2814 {
2815 VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
2816 VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
2817
2818 VikTrack *tr_first = tr1, *tr_last = tr2;
2819
2820 gchar *tmp;
2821
2822 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
2823 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
2824 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
2825 vik_track_reverse ( tr1 );
2826 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
2827 {
2828 tr_first = tr2;
2829 tr_last = tr1;
2830 }
2831 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
2832
2833 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. */
2834 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
2835 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
2836 tr2->trackpoints = NULL;
2837
2838 tmp = vtl->current_tp_track_name;
2839
2840 vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
2841 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
2842
2843 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
2844 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
e4afc73a 2845 vik_trw_layer_delete_track ( vtl, tmp );
50a14534
EB
2846
2847 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
2848 vik_layer_emit_update(VIK_LAYER(vtl));
2849 }
2850 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
2851 vik_layer_emit_update (VIK_LAYER(vtl));
2852}
2853
2854static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
2855{
2856 if ( ! vtl->tpwin )
2857 {
2858 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
2859 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
2860 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
2861 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
2862 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
2863 }
2864 if ( vtl->current_tpl )
2865 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
2866 /* set layer name and TP data */
2867}
2868
941aa6e9
AF
2869/***************************************************************************
2870 ** Tool code
2871 ***************************************************************************/
50a14534 2872
941aa6e9 2873/*** Utility data structures and functions ****/
50a14534
EB
2874
2875typedef struct {
2876 gint x, y;
2877 gint closest_x, closest_y;
2878 gchar *closest_wp_name;
2879 VikWaypoint *closest_wp;
2880 VikViewport *vvp;
2881} WPSearchParams;
2882
941aa6e9
AF
2883typedef struct {
2884 gint x, y;
2885 gint closest_x, closest_y;
2886 gchar *closest_track_name;
2887 VikTrackpoint *closest_tp;
2888 VikViewport *vvp;
2889 GList *closest_tpl;
2890} TPSearchParams;
2891
50a14534
EB
2892static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
2893{
2894 gint x, y;
2895 if ( !wp->visible )
2896 return;
2897
2898 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
2899
2900 if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
2901 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
2902 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
2903 {
2904 params->closest_wp_name = name;
2905 params->closest_wp = wp;
2906 params->closest_x = x;
2907 params->closest_y = y;
2908 }
2909}
2910
941aa6e9 2911static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
50a14534 2912{
941aa6e9
AF
2913 GList *tpl = t->trackpoints;
2914 VikTrackpoint *tp;
50a14534 2915
941aa6e9
AF
2916 if ( !t->visible )
2917 return;
50a14534 2918
941aa6e9
AF
2919 while (tpl)
2920 {
2921 gint x, y;
2922 tp = VIK_TRACKPOINT(tpl->data);
2923
2924 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
2925
2926 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
2927 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
2928 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
50a14534 2929 {
941aa6e9
AF
2930 params->closest_track_name = name;
2931 params->closest_tp = tp;
2932 params->closest_tpl = tpl;
2933 params->closest_x = x;
2934 params->closest_y = y;
50a14534 2935 }
941aa6e9 2936 tpl = tpl->next;
50a14534 2937 }
941aa6e9
AF
2938}
2939
2940static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
2941{
2942 TPSearchParams params;
2943 params.x = x;
2944 params.y = y;
2945 params.vvp = vvp;
2946 params.closest_track_name = NULL;
2947 params.closest_tp = NULL;
2948 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
2949 return params.closest_tp;
50a14534
EB
2950}
2951
2952static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
2953{
2954 WPSearchParams params;
2955 params.x = x;
2956 params.y = y;
2957 params.vvp = vvp;
2958 params.closest_wp = NULL;
2959 params.closest_wp_name = NULL;
2960 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
2961 return params.closest_wp;
2962}
2963
7432fddf
AF
2964/* background drawing hook, to be passed the viewport */
2965static gboolean tool_sync_done = TRUE;
2966
2967static gboolean tool_sync(gpointer data)
2968{
2969 VikViewport *vvp = data;
2970 gdk_threads_enter();
2971 vik_viewport_sync(vvp);
2972 tool_sync_done = TRUE;
2973 gdk_threads_leave();
2974 return FALSE;
2975}
2976
2977typedef struct {
2978 VikViewport *vvp;
2979 gboolean holding;
2980 GdkGC *gc;
2981 int oldx, oldy;
2982} tool_ed_t;
2983
2984static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
2985{
2986 t->holding = TRUE;
2987 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
2988 gdk_gc_set_function ( t->gc, GDK_INVERT );
2989 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
2990 vik_viewport_sync(t->vvp);
2991 t->oldx = x;
2992 t->oldy = y;
2993}
2994
2995static void marker_moveto ( tool_ed_t *t, gint x, gint y )
2996{
2997 VikViewport *vvp = t->vvp;
2998 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
2999 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
3000 t->oldx = x;
3001 t->oldy = y;
7b203521 3002
7432fddf
AF
3003 if (tool_sync_done) {
3004 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
3005 tool_sync_done = FALSE;
3006 }
3007}
3008
3009static void marker_end_move ( tool_ed_t *t )
3010{
3011 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
3012 g_object_unref ( t->gc );
3013 t->holding = FALSE;
3014}
3015
941aa6e9
AF
3016/*** Edit waypoint ****/
3017
3018static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
3019{
7432fddf
AF
3020 tool_ed_t *t = g_new(tool_ed_t, 1);
3021 t->vvp = vvp;
3022 t->holding = FALSE;
3023 return t;
941aa6e9
AF
3024}
3025
7432fddf 3026static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
50a14534
EB
3027{
3028 WPSearchParams params;
7432fddf
AF
3029 tool_ed_t *t = data;
3030 VikViewport *vvp = t->vvp;
50a14534 3031
941aa6e9
AF
3032 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3033 return FALSE;
7432fddf
AF
3034
3035 if ( t->holding ) {
3036 return TRUE;
3037 }
3038
50a14534
EB
3039 if ( vtl->current_wp && vtl->current_wp->visible )
3040 {
3041 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
3042 gint x, y;
3043 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
3044
c75d78d7
AF
3045 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
3046 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
50a14534
EB
3047 {
3048 if ( event->button == 3 )
3049 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7432fddf
AF
3050 else {
3051 marker_begin_move(t, event->x, event->y);
3052 }
50a14534
EB
3053 return TRUE;
3054 }
3055 }
3056
3057 params.vvp = vvp;
3058 params.x = event->x;
3059 params.y = event->y;
3060 params.closest_wp_name = NULL;
3061 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
3062 params.closest_wp = NULL;
3063 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
3064 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
3065 {
7432fddf
AF
3066 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
3067 marker_begin_move(t, event->x, event->y);
7742da66 3068 g_critical("shouldn't be here");
7432fddf 3069 exit(1);
50a14534
EB
3070 }
3071 else if ( params.closest_wp )
3072 {
3073 if ( event->button == 3 )
3074 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
3075 else
3076 vtl->waypoint_rightclick = FALSE;
3077
3078 vtl->current_wp = params.closest_wp;
3079 vtl->current_wp_name = params.closest_wp_name;
50a14534
EB
3080
3081 if ( params.closest_wp )
3082 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ) );
3083
3084 /* could make it so don't update if old WP is off screen and new is null but oh well */
3085 vik_layer_emit_update ( VIK_LAYER(vtl) );
3086 return TRUE;
3087 }
3088
3089 vtl->current_wp = NULL;
3090 vtl->current_wp_name = NULL;
50a14534 3091 vtl->waypoint_rightclick = FALSE;
7432fddf
AF
3092 vik_layer_emit_update ( VIK_LAYER(vtl) );
3093 return FALSE;
3094}
3095
dc2c040e 3096static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7432fddf
AF
3097{
3098 tool_ed_t *t = data;
3099 VikViewport *vvp = t->vvp;
3100
3101 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3102 return FALSE;
3103
3104 if ( t->holding ) {
3105 VikCoord new_coord;
3106 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3107
3108 /* snap to TP */
3109 if ( event->state & GDK_CONTROL_MASK )
3110 {
3111 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3112 if ( tp )
3113 new_coord = tp->coord;
3114 }
3115
3116 /* snap to WP */
3117 if ( event->state & GDK_SHIFT_MASK )
3118 {
3119 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3120 if ( wp && wp != vtl->current_wp )
3121 new_coord = wp->coord;
3122 }
3123
3124 {
3125 gint x, y;
3126 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7b203521 3127
7432fddf
AF
3128 marker_moveto ( t, x, y );
3129 }
3130 return TRUE;
3131 }
50a14534
EB
3132 return FALSE;
3133}
3134
7432fddf 3135static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 3136{
7432fddf
AF
3137 tool_ed_t *t = data;
3138 VikViewport *vvp = t->vvp;
3139
941aa6e9
AF
3140 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3141 return FALSE;
7432fddf
AF
3142
3143 if ( t->holding && event->button == 1 )
941aa6e9
AF
3144 {
3145 VikCoord new_coord;
941aa6e9
AF
3146 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3147
3148 /* snap to TP */
3149 if ( event->state & GDK_CONTROL_MASK )
3150 {
3151 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3152 if ( tp )
3153 new_coord = tp->coord;
3154 }
3155
3156 /* snap to WP */
3157 if ( event->state & GDK_SHIFT_MASK )
3158 {
3159 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3160 if ( wp && wp != vtl->current_wp )
3161 new_coord = wp->coord;
3162 }
3163
7432fddf
AF
3164 marker_end_move ( t );
3165
941aa6e9
AF
3166 vtl->current_wp->coord = new_coord;
3167 vik_layer_emit_update ( VIK_LAYER(vtl) );
3168 return TRUE;
3169 }
3170 /* PUT IN RIGHT PLACE!!! */
7432fddf 3171 if ( event->button == 3 && vtl->waypoint_rightclick )
941aa6e9
AF
3172 {
3173 if ( vtl->wp_right_click_menu )
3174 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
3175 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
3176 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 ) );
3177 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
3178 vtl->waypoint_rightclick = FALSE;
3179 }
3180 return FALSE;
3181}
3182
98f5364d
EB
3183/**** Begin track ***/
3184static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
3185{
3186 return vvp;
3187}
3188
3189static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3190{
3191 vtl->current_track = NULL;
3192 return tool_new_track_click ( vtl, event, vvp );
3193}
3194
941aa6e9
AF
3195/*** New track ****/
3196
3197static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
3198{
3199 return vvp;
3200}
3201
7b203521
EB
3202typedef struct {
3203 VikTrwLayer *vtl;
3204 VikViewport *vvp;
3205 gint x1,y1,x2,y2;
3206} new_track_move_passalong_t;
3207
3208/* sync and undraw, but only when we have time */
3209static gboolean ct_sync ( gpointer passalong )
3210{
3211 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
3212 vik_viewport_sync ( p->vvp );
3213 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
3214 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
3215 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
3216 p->vtl->ct_sync_done = TRUE;
3217 g_free ( p );
3218 return FALSE;
3219}
3220
dc2c040e 3221static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7b203521
EB
3222{
3223 /* if we haven't sync'ed yet, we don't have time to do more. */
3224 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
3225 GList *iter = vtl->current_track->trackpoints;
3226 new_track_move_passalong_t *passalong;
3227 gint x1, y1;
3228
3229 while ( iter->next )
3230 iter = iter->next;
3231 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
3232 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
3233 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
3234 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
3235
3236 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
3237 passalong->vtl = vtl;
3238 passalong->vvp = vvp;
3239 passalong->x1 = x1;
3240 passalong->y1 = y1;
3241 passalong->x2 = event->x;
3242 passalong->y2 = event->y;
3243
3244 /* this will sync and undraw when we have time to */
3245 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
3246 vtl->ct_sync_done = FALSE;
165d30aa 3247 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7b203521 3248 }
165d30aa 3249 return VIK_LAYER_TOOL_ACK;
7b203521
EB
3250}
3251
777e2d4d
EB
3252static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
3253{
3254 if ( vtl->current_track && event->keyval == GDK_Escape ) {
3255 vtl->current_track = NULL;
3256 vik_layer_emit_update ( VIK_LAYER(vtl) );
3257 return TRUE;
3258 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
3259 /* undo */
3260 if ( vtl->current_track->trackpoints )
3261 {
3262 GList *last = g_list_last(vtl->current_track->trackpoints);
3263 g_free ( last->data );
3264 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
3265 }
3266 vik_layer_emit_update ( VIK_LAYER(vtl) );
3267 return TRUE;
3268 }
3269 return FALSE;
3270}
3271
941aa6e9 3272static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
50a14534
EB
3273{
3274 VikTrackpoint *tp;
3275
941aa6e9
AF
3276 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3277 return FALSE;
3278
50a14534
EB
3279 if ( event->button == 3 && vtl->current_track )
3280 {
3281 /* undo */
3282 if ( vtl->current_track->trackpoints )
3283 {
3284 GList *last = g_list_last(vtl->current_track->trackpoints);
3285 g_free ( last->data );
3286 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
3287 }
3288 vik_layer_emit_update ( VIK_LAYER(vtl) );
3289 return TRUE;
3290 }
3291
3292 if ( event->type == GDK_2BUTTON_PRESS )
3293 {
3294 /* subtract last (duplicate from double click) tp then end */
3295 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
3296 {
3297 GList *last = g_list_last(vtl->current_track->trackpoints);
3298 g_free ( last->data );
3299 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
3300 /* undo last, then end */
3301 vtl->current_track = NULL;
3302 }
8e9c992d 3303 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
3304 return TRUE;
3305 }
3306
3307 if ( ! vtl->current_track )
3308 {
3309 gchar *name;
3310 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks ) ) )
3311 {
3312 vtl->current_track = vik_track_new();
3313 vtl->current_track->visible = TRUE;
3314 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
98f5364d
EB
3315
3316 /* incase it was created by begin track */
3317 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
50a14534
EB
3318 }
3319 else
3320 return TRUE;
3321 }
3322 tp = vik_trackpoint_new();
3323 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
3324
3325 /* snap to other TP */
3326 if ( event->state & GDK_CONTROL_MASK )
3327 {
3328 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3329 if ( other_tp )
3330 tp->coord = other_tp->coord;
3331 }
3332
3333 tp->newsegment = FALSE;
3334 tp->has_timestamp = FALSE;
3335 tp->timestamp = 0;
3336 tp->altitude = VIK_DEFAULT_ALTITUDE;
3337 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
3338
3339 vtl->ct_x1 = vtl->ct_x2;
3340 vtl->ct_y1 = vtl->ct_y2;
3341 vtl->ct_x2 = event->x;
3342 vtl->ct_y2 = event->y;
3343
3344 vik_layer_emit_update ( VIK_LAYER(vtl) );
3345 return TRUE;
3346}
3347
941aa6e9
AF
3348
3349/*** New waypoint ****/
3350
3351static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
3352{
3353 return vvp;
3354}
3355
3356static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3357{
3358 VikCoord coord;
3359 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3360 return FALSE;
3361 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
3362 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
3363 vik_layer_emit_update ( VIK_LAYER(vtl) );
3364 return TRUE;
3365}
3366
3367
3368/*** Edit trackpoint ****/
3369
3370static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
3371{
7432fddf 3372 tool_ed_t *t = g_new(tool_ed_t, 1);
33534cd8
AF
3373 t->vvp = vvp;
3374 t->holding = FALSE;
3375 return t;
941aa6e9
AF
3376}
3377
33534cd8 3378static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 3379{
7432fddf 3380 tool_ed_t *t = data;
33534cd8
AF
3381 VikViewport *vvp = t->vvp;
3382 TPSearchParams params;
941aa6e9
AF
3383 /* OUTDATED DOCUMENTATION:
3384 find 5 pixel range on each side. then put these UTM, and a pointer
3385 to the winning track name (and maybe the winning track itself), and a
3386 pointer to the winning trackpoint, inside an array or struct. pass
3387 this along, do a foreach on the tracks which will do a foreach on the
3388 trackpoints. */
3389 params.vvp = vvp;
3390 params.x = event->x;
3391 params.y = event->y;
3392 params.closest_track_name = NULL;
3393 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
3394 params.closest_tp = NULL;
3395
7432fddf
AF
3396 if ( event->button != 1 )
3397 return FALSE;
3398
941aa6e9
AF
3399 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3400 return FALSE;
3401
3402 if ( vtl->current_tpl )
3403 {
3404 /* 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.) */
3405 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
3406 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
3407 gint x, y;
3408 g_assert ( current_tr );
3409
3410 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
3411
3412 if ( current_tr->visible &&
3413 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
7432fddf
AF
3414 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
3415 marker_begin_move ( t, event->x, event->y );
941aa6e9
AF
3416 return TRUE;
3417 }
3418
3419 vtl->last_tpl = vtl->current_tpl;
3420 vtl->last_tp_track_name = vtl->current_tp_track_name;
3421 }
3422
3423 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
3424
3425 if ( params.closest_tp )
3426 {
3427 vtl->current_tpl = params.closest_tpl;
3428 vtl->current_tp_track_name = params.closest_track_name;
3429 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, vtl->current_tp_track_name ) );
3430 trw_layer_tpwin_init ( vtl );
3431 vik_layer_emit_update ( VIK_LAYER(vtl) );
3432 return TRUE;
3433 }
3434
3435 /* these aren't the droids you're looking for */
3436 return FALSE;
3437}
3438
dc2c040e 3439static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
33534cd8 3440{
7432fddf 3441 tool_ed_t *t = data;
33534cd8
AF
3442 VikViewport *vvp = t->vvp;
3443
3444 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3445 return FALSE;
3446
3447 if ( t->holding )
3448 {
3449 VikCoord new_coord;
33534cd8
AF
3450 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3451
3452 /* snap to TP */
3453 if ( event->state & GDK_CONTROL_MASK )
3454 {
3455 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3456 if ( tp && tp != vtl->current_tpl->data )
3457 new_coord = tp->coord;
3458 }
3459 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7432fddf
AF
3460 {
3461 gint x, y;
3462 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
3463 marker_moveto ( t, x, y );
3464 }
33534cd8
AF
3465
3466 return TRUE;
3467 }
3468 return FALSE;
3469}
3470
33534cd8 3471static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 3472{
7432fddf 3473 tool_ed_t *t = data;
33534cd8
AF
3474 VikViewport *vvp = t->vvp;
3475
941aa6e9
AF
3476 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3477 return FALSE;
7432fddf
AF
3478 if ( event->button != 1)
3479 return FALSE;
33534cd8 3480
7432fddf 3481 if ( t->holding ) {
941aa6e9 3482 VikCoord new_coord;
941aa6e9
AF
3483 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3484
3485 /* snap to TP */
3486 if ( event->state & GDK_CONTROL_MASK )
3487 {
3488 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3489 if ( tp && tp != vtl->current_tpl->data )
3490 new_coord = tp->coord;
3491 }
3492
3493 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
3494
7432fddf 3495 marker_end_move ( t );
33534cd8 3496
941aa6e9
AF
3497 /* diff dist is diff from orig */
3498 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3499 /* can't join with itself! */
3500 trw_layer_cancel_last_tp ( vtl );
3501
3502 vik_layer_emit_update ( VIK_LAYER(vtl) );
3503 return TRUE;
3504 }
3505 return FALSE;
3506}
3507
3508
1eef1bde
QT
3509/*** Magic Scissors ***/
3510static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp)
3511{
3512 return vvp;
3513}
3514
3515static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3516{
3517 VikCoord tmp;
0c1044e9 3518 if ( !vtl ) return FALSE;
1eef1bde 3519 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
c3deba01
EB
3520 if ( event->button == 3 && vtl->magic_scissors_current_track ) {
3521 VikCoord *new_end;
3522 new_end = vik_track_cut_back_to_double_point ( vtl->magic_scissors_current_track );
3523 if ( new_end ) {
3524 vtl->magic_scissors_coord = *new_end;
3525 g_free ( new_end );
3526 vik_layer_emit_update ( VIK_LAYER(vtl) );
3527 /* remove last ' to:...' */
3528 if ( vtl->magic_scissors_current_track->comment ) {
3529 gchar *last_to = strrchr ( vtl->magic_scissors_current_track->comment, 't' );
3530 if ( last_to && (last_to - vtl->magic_scissors_current_track->comment > 1) ) {
3531 gchar *new_comment = g_strndup ( vtl->magic_scissors_current_track->comment,
3532 last_to - vtl->magic_scissors_current_track->comment - 1);
3533 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
3534 }
3535 }
3536 }
3537 }
3538 else if ( vtl->magic_scissors_started || (event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track) ) {
1eef1bde 3539 struct LatLon start, end;
533bbf34
MA
3540 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
3541 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
3542 gchar *url;
bddd2056 3543
1eef1bde
QT
3544 vik_coord_to_latlon ( &(vtl->magic_scissors_coord), &start );
3545 vik_coord_to_latlon ( &(tmp), &end );
bddd2056
EB
3546 vtl->magic_scissors_coord = tmp; /* for continuations */
3547
3548 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
3549 if ( event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track ) {
3550 vtl->magic_scissors_append = TRUE; // merge tracks. keep started true.
3551 } else {
3552 vtl->magic_scissors_check_added_track = TRUE;
3553 vtl->magic_scissors_started = FALSE;
3554 }
3555
533bbf34
MA
3556 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
3557 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
3558 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
3559 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
3560 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
3561 a_babel_convert_from_url ( vtl, url, "google", NULL, NULL );
3562 g_free ( url );
bddd2056
EB
3563
3564 /* see if anything was done -- a track was added or appended to */
3565 if ( vtl->magic_scissors_check_added_track && vtl->magic_scissors_added_track_name ) {
3566 VikTrack *tr;
3567
3568 tr = g_hash_table_lookup ( vtl->tracks, vtl->magic_scissors_added_track_name );
3569
3570 if ( tr )
3571 vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f%f", start.lat, start.lon, end.lat, end.lon ) );
3572
3573 vtl->magic_scissors_current_track = tr;
3574
3575 g_free ( vtl->magic_scissors_added_track_name );
3576 vtl->magic_scissors_added_track_name = NULL;
3577 } else if ( vtl->magic_scissors_append == FALSE && vtl->magic_scissors_current_track ) {
3578 /* magic_scissors_append was originally TRUE but set to FALSE by filein_add_track */
3579 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->magic_scissors_current_track->comment, end.lat, end.lon );
3580 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
3581 }
3582 vtl->magic_scissors_check_added_track = FALSE;
3583 vtl->magic_scissors_append = FALSE;
3584
1eef1bde
QT
3585 vik_layer_emit_update ( VIK_LAYER(vtl) );
3586 } else {
bddd2056 3587 vtl->magic_scissors_started = TRUE;
1eef1bde 3588 vtl->magic_scissors_coord = tmp;
bddd2056 3589 vtl->magic_scissors_current_track = NULL;
1eef1bde 3590 }
1eef1bde
QT
3591 return TRUE;
3592}
3593
941aa6e9
AF
3594/*** Show picture ****/
3595
3596static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
3597{
3598 return vvp;
3599}
3600
3601/* Params are: vvp, event, last match found or NULL */
3602static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] )
3603{
3604 if ( wp->image && wp->visible )
3605 {
3606 gint x, y, slackx, slacky;
3607 GdkEventButton *event = (GdkEventButton *) params[1];
3608
3609 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
3610 slackx = wp->image_width / 2;
3611 slacky = wp->image_height / 2;
3612 if ( x <= event->x + slackx && x >= event->x - slackx
3613 && y <= event->y + slacky && y >= event->y - slacky )
3614 {
3615 params[2] = wp->image; /* we've found a match. however continue searching
3616 * since we want to find the last match -- that
3617 * is, the match that was drawn last. */
3618 }
3619 }
3620}
3621
3622static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3623{
3624 gpointer params[3] = { vvp, event, NULL };
3625 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3626 return FALSE;
3627 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
3628 if ( params[2] )
3629 {
3630 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
3631#ifdef WINDOWS
3632 ShellExecute(NULL, NULL, (char *) params[2], NULL, ".\\", 0);
3633#else /* WINDOWS */
3634 GError *err = NULL;
3635 gchar *quoted_file = g_shell_quote ( (gchar *) params[2] );
3636 gchar *cmd = g_strdup_printf ( "eog %s", quoted_file );
3637 g_free ( quoted_file );
3638 if ( ! g_spawn_command_line_async ( cmd, &err ) )
3639 {
4c77d5e0 3640 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch eog to open file.") );
941aa6e9
AF
3641 g_error_free ( err );
3642 }
3643 g_free ( cmd );
3644#endif /* WINDOWS */
3645 return TRUE; /* found a match */
3646 }
3647 else
3648 return FALSE; /* go through other layers, searching for a match */
3649}
3650
3651/***************************************************************************
3652 ** End tool code
3653 ***************************************************************************/
3654
3655
3656
3657
3658
50a14534
EB
3659static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
3660{
3661 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
3662 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
3663}
3664
3665static void create_thumbnails_thread ( GSList *pics, gpointer threaddata )
3666{
3667 guint total = g_slist_length(pics), done = 0;
3668 while ( pics )
3669 {
3670 a_thumbnails_create ( (gchar *) pics->data );
3671 a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
3672 pics = pics->next;
3673 }
3674}
3675
3676static void free_pics_slist ( GSList *pics )
3677{
3678 while ( pics )
3679 {
3680 g_free ( pics->data );
3681 pics = g_slist_delete_link ( pics, pics );
3682 }
3683}
3684
3685static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
3686{
3687 if ( ! vtl->has_verified_thumbnails )
3688 {
3689 GSList *pics = NULL;
3690 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
3691 if ( pics )
3692 {
3693 gint len = g_slist_length ( pics );
4c77d5e0 3694 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
50a14534
EB
3695 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 );
3696 g_free ( tmp );
3697 }
3698 }
3699}
3700
3701VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
3702{
3703 return vtl->coord_mode;
3704}
3705
3706
3707
3708static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
3709{
3710 vik_coord_convert ( &(wp->coord), *dest_mode );
3711}
3712
3713static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
3714{
3715 vik_track_convert ( tr, *dest_mode );
3716}
3717
3718static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
3719{
3720 if ( vtl->coord_mode != dest_mode )
3721 {
3722 vtl->coord_mode = dest_mode;
3723 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
3724 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
3725 }
3726}
e4afc73a
EB
3727
3728VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
3729{
3730 return g_hash_table_lookup ( vtl->waypoints, name );
3731}
3732
3733VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, gchar *name )
3734{
3735 return g_hash_table_lookup ( vtl->tracks, name );
3736}
20c7a3a0 3737
2cebc318 3738static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16 selection)
20c7a3a0
QT
3739{
3740 vtl->menu_selection = selection;
3741}
3742
3743static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl)
3744{
3745 return(vtl->menu_selection);
3746}
3747
7114e879
QT
3748/* ----------- Downloading maps along tracks --------------- */
3749
3750static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
3751{
3752 /* TODO: calculating based on current size of viewport */
3753 const gdouble w_at_zoom_0_125 = 0.0013;
3754 const gdouble h_at_zoom_0_125 = 0.0011;
3755 gdouble zoom_factor = zoom_level/0.125;
3756
3757 wh->lat = h_at_zoom_0_125 * zoom_factor;
3758 wh->lon = w_at_zoom_0_125 * zoom_factor;
3759
3760 return 0; /* all OK */
3761}
3762
35e22ed8
QT
3763static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
3764{
3765 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
3766 (dist->lat >= ABS(to->north_south - from->north_south)))
3767 return NULL;
3768
3769 VikCoord *coord = g_malloc(sizeof(VikCoord));
3770 coord->mode = VIK_COORD_LATLON;
3771
3772 if (ABS(gradient) < 1) {
3773 if (from->east_west > to->east_west)
3774 coord->east_west = from->east_west - dist->lon;
3775 else
3776 coord->east_west = from->east_west + dist->lon;
3777 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
3778 } else {
3779 if (from->north_south > to->north_south)
3780 coord->north_south = from->north_south - dist->lat;
3781 else
3782 coord->north_south = from->north_south + dist->lat;
3783 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
3784 }
3785
3786 return coord;
3787}
3788
3789static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
3790{
3791 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
3792 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
3793
3794 VikCoord *next = from;
3795 while (TRUE) {
3796 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
3797 break;
3798 list = g_list_prepend(list, next);
3799 }
3800
3801 return list;
3802}
3803
7114e879
QT
3804void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
3805{
3806 typedef struct _Rect {
3807 VikCoord tl;
3808 VikCoord br;
35e22ed8 3809 VikCoord center;
7114e879 3810 } Rect;
35e22ed8 3811#define GLRECT(iter) ((Rect *)((iter)->data))
7114e879
QT
3812
3813 struct LatLon wh;
35e22ed8 3814 GList *rects_to_download = NULL;
7114e879
QT
3815 GList *rect_iter;
3816
3817 if (get_download_area_width(vvp, zoom_level, &wh))
3818 return;
3819
3820 GList *iter = tr->trackpoints;
35e22ed8
QT
3821 if (!iter)
3822 return;
7114e879
QT
3823
3824 gboolean new_map = TRUE;
3825 VikCoord *cur_coord, tl, br;
3826 Rect *rect;
7114e879
QT
3827 while (iter) {
3828 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
3829 if (new_map) {
3830 vik_coord_set_area(cur_coord, &wh, &tl, &br);
3831 rect = g_malloc(sizeof(Rect));
3832 rect->tl = tl;
3833 rect->br = br;
35e22ed8
QT
3834 rect->center = *cur_coord;
3835 rects_to_download = g_list_prepend(rects_to_download, rect);
7114e879
QT
3836 new_map = FALSE;
3837 iter = iter->next;
3838 continue;
3839 }
3840 gboolean found = FALSE;
35e22ed8
QT
3841 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
3842 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
7114e879
QT
3843 found = TRUE;
3844 break;
3845 }
3846 }
3847 if (found)
3848 iter = iter->next;
3849 else
3850 new_map = TRUE;
3851 }
35e22ed8
QT
3852
3853 /* fill-ins for far apart points */
3854 GList *cur_rect, *next_rect;
3855 GList *fillins = NULL;
3856 for (cur_rect = rects_to_download;
3857 (next_rect = cur_rect->next) != NULL;
3858 cur_rect = cur_rect->next) {
3859 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
3860 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
3861 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
3862 }
3863 }
3864
3865 if (fillins) {
3866 GList *iter = fillins;
3867 while (iter) {
3868 cur_coord = (VikCoord *)(iter->data);
3869 vik_coord_set_area(cur_coord, &wh, &tl, &br);
3870 rect = g_malloc(sizeof(Rect));
3871 rect->tl = tl;
3872 rect->br = br;
3873 rect->center = *cur_coord;
3874 rects_to_download = g_list_prepend(rects_to_download, rect);
3875 iter = iter->next;
3876 }
3877 }
3878
3879 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
7114e879
QT
3880 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
3881 }
3882
35e22ed8
QT
3883 if (fillins) {
3884 for (iter = fillins; iter; iter = iter->next)
3885 g_free(iter->data);
3886 g_list_free(fillins);
3887 }
3888 if (rects_to_download) {
3889 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
3890 g_free(rect_iter->data);
3891 g_list_free(rects_to_download);
3892 }
7114e879
QT
3893}
3894
7114e879
QT
3895static void trw_layer_download_map_along_track_cb(gpointer pass_along[6])
3896{
3897 VikMapsLayer *vml;
3898 gint selected_map, default_map;
3899 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
3900 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
3901 gint selected_zoom, default_zoom;
3902 int i,j;
3903
3904
3905 VikTrwLayer *vtl = pass_along[0];
3906 VikLayersPanel *vlp = pass_along[1];
3907 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3908 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
3909
3910 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS);
3911 int num_maps = g_list_length(vmls);
3912
3913 if (!num_maps) {
4c77d5e0 3914 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
7114e879
QT
3915 return;
3916 }
3917
3918 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
3919 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
3920
3921 gchar **np = map_names;
3922 VikMapsLayer **lp = map_layers;
3923 for (i = 0; i < num_maps; i++) {
3924 gboolean dup = FALSE;
3925 vml = (VikMapsLayer *)(vmls->data);
3926 for (j = 0; j < i; j++) { /* no duplicate allowed */
3927 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
3928 dup = TRUE;
3929 break;
3930 }
3931 }
3932 if (!dup) {
3933 *lp++ = vml;
3934 *np++ = vik_maps_layer_get_map_label(vml);
3935 }
3936 vmls = vmls->next;
3937 }
3938 *lp = NULL;
3939 *np = NULL;
3940 num_maps = lp - map_layers;
3941
3942 for (default_map = 0; default_map < num_maps; default_map++) {
3943 /* TODO: check for parent layer's visibility */
3944 if (VIK_LAYER(map_layers[default_map])->visible)
3945 break;
3946 }
3947 default_map = (default_map == num_maps) ? 0 : default_map;
3948
3949 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
3950 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
3951 if (cur_zoom == zoom_vals[default_zoom])
3952 break;
3953 }
3954 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
3955
3956 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
3957 goto done;
3958
3959 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
3960
3961done:
3962 for (i = 0; i < num_maps; i++)
3963 g_free(map_names[i]);
3964 g_free(map_names);
3965 g_free(map_layers);
3966
3967 g_list_free(vmls);
3968
3969}
0c1044e9 3970
a8fe53f8
EB
3971/**** lowest waypoint number calculation ***/
3972static gint highest_wp_number_name_to_number(const gchar *name) {
3973 if ( strlen(name) == 3 ) {
3974 int n = atoi(name);
3975 if ( n < 100 && name[0] != '0' )
3976 return -1;
3977 if ( n < 10 && name[0] != '0' )
3978 return -1;
3979 return n;
3980 }
3981 return -1;
3982}
3983
3984
3985static void highest_wp_number_reset(VikTrwLayer *vtl)
3986{
3987 vtl->highest_wp_number = -1;
3988}
3989
3990static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
3991{
3992 /* if is bigger that top, add it */
3993 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
3994 if ( new_wp_num > vtl->highest_wp_number )
3995 vtl->highest_wp_number = new_wp_num;
3996}
3997
3998static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
3999{
4000 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
4001 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
4002 if ( vtl->highest_wp_number == old_wp_num ) {
4003 gchar buf[4];
4004 vtl->highest_wp_number --;
4005
4006 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
4007 /* search down until we find something that *does* exist */
4008
3ce17b61 4009 while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
a8fe53f8
EB
4010 vtl->highest_wp_number --;
4011 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
4012 }
4013 }
4014}
4015
4016/* get lowest unused number */
4017static gchar *highest_wp_number_get(VikTrwLayer *vtl)
4018{
4019 gchar buf[4];
4020 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
3ce17b61 4021 return NULL;
a8fe53f8
EB
4022 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
4023 return g_strdup(buf);
4024}