]> git.street.me.uk Git - andy/viking.git/blame - src/viktrwlayer_propwin.c
SF Features#120: Add Nautical Miles preference for distance units.
[andy/viking.git] / src / viktrwlayer_propwin.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>
a482007a
GB
5 * Copyright (C) 2005-2007, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2007-2008, Quy Tonthat <qtonthat@gmail.com>
1b40910b 7 * Copyright (C) 2012-2014, Rob Norris <rw_norris@hotmail.com>
50a14534
EB
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
24
4c77d5e0
GB
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
8c00358d 29#ifdef HAVE_MATH_H
07596bf4 30#include <math.h>
8c00358d 31#endif
4c77d5e0 32
50a14534 33#include <gtk/gtk.h>
4c77d5e0 34#include <glib/gi18n.h>
50a14534 35#include <time.h>
8c00358d 36#ifdef HAVE_STRING_H
f583082b 37#include <string.h>
8c00358d 38#endif
21700912 39#include "viktrwlayer.h"
50a14534 40#include "viktrwlayer_propwin.h"
07596bf4 41#include "dems.h"
972fc895 42#include "viking.h"
24d5c7e2 43#include "vikviewport.h" /* ugh */
8ada46de 44#include "vikutils.h"
24d5c7e2 45#include <gdk-pixbuf/gdk-pixdata.h>
950d8a07 46
926c8140
RN
47typedef enum {
48 PROPWIN_GRAPH_TYPE_ELEVATION_DISTANCE,
0ba33e1d 49 PROPWIN_GRAPH_TYPE_GRADIENT_DISTANCE,
926c8140
RN
50 PROPWIN_GRAPH_TYPE_SPEED_TIME,
51 PROPWIN_GRAPH_TYPE_DISTANCE_TIME,
8de26632 52 PROPWIN_GRAPH_TYPE_ELEVATION_TIME,
7b624086 53 PROPWIN_GRAPH_TYPE_SPEED_DISTANCE,
926c8140
RN
54 PROPWIN_GRAPH_TYPE_END,
55} VikPropWinGraphType_t;
56
c6bc2190
RN
57/* (Hopefully!) Human friendly altitude grid sizes - note no fixed 'ratio' just numbers that look nice...*/
58static const gdouble chunksa[] = {2.0, 5.0, 10.0, 15.0, 20.0,
2501f239 59 25.0, 40.0, 50.0, 75.0, 100.0,
c6bc2190
RN
60 150.0, 200.0, 250.0, 375.0, 500.0,
61 750.0, 1000.0, 2000.0, 5000.0, 10000.0, 100000.0};
62
0ba33e1d
FA
63/* (Hopefully!) Human friendly gradient grid sizes - note no fixed 'ratio' just numbers that look nice...*/
64static const gdouble chunksg[] = {1.0, 2.0, 3.0, 4.0, 5.0, 8.0, 10.0,
65 12.0, 15.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 75.0,
66 100.0, 150.0, 200.0, 250.0, 375.0, 500.0,
f25c1cf8
RN
67 750.0, 1000.0, 10000.0, 100000.0};
68// Normally gradients should range up to couple hundred precent at most,
69// however there are possibilities of having points with no altitude after a point with a big altitude
70// (such as places with invalid DEM values in otherwise mountainous regions) - thus giving huge negative gradients.
0ba33e1d 71
976c7df6
RN
72/* (Hopefully!) Human friendly grid sizes - note no fixed 'ratio' just numbers that look nice...*/
73/* As need to cover walking speeds - have many low numbers (but also may go up to airplane speeds!) */
74static const gdouble chunkss[] = {1.0, 2.0, 3.0, 4.0, 5.0, 8.0, 10.0,
75 15.0, 20.0, 25.0, 40.0, 50.0, 75.0,
76 100.0, 150.0, 200.0, 250.0, 375.0, 500.0,
77 750.0, 1000.0, 10000.0};
78
926c8140 79/* (Hopefully!) Human friendly distance grid sizes - note no fixed 'ratio' just numbers that look nice...*/
87819e20 80static const gdouble chunksd[] = {0.1, 0.2, 0.5, 1.0, 2.0, 3.0, 4.0, 5.0, 8.0, 10.0,
926c8140
RN
81 15.0, 20.0, 25.0, 40.0, 50.0, 75.0,
82 100.0, 150.0, 200.0, 250.0, 375.0, 500.0,
83 750.0, 1000.0, 10000.0};
84
7f44287f
RN
85// Time chunks in seconds
86static const time_t chunkst[] = {
87 60, // 1 minute
88 120, // 2 minutes
89 300, // 5 minutes
90 900, // 15 minutes
91 1800, // half hour
92 3600, // 1 hour
93 10800, // 3 hours
94 21600, // 6 hours
95 43200, // 12 hours
96 86400, // 1 day
97 172800, // 2 days
98 604800, // 1 week
99 1209600,// 2 weeks
100 2419200,// 4 weeks
101};
102
932b4a7a
RN
103// Local show settings to restore on dialog opening
104static gboolean show_dem = TRUE;
105static gboolean show_alt_gps_speed = TRUE;
106static gboolean show_gps_speed = TRUE;
107static gboolean show_gradient_gps_speed = TRUE;
108static gboolean show_dist_speed = FALSE;
109static gboolean show_elev_speed = FALSE;
110static gboolean show_elev_dem = FALSE;
111static gboolean show_sd_gps_speed = TRUE;
112
ca7e67ef
QT
113typedef struct _propsaved {
114 gboolean saved;
ca7e67ef
QT
115 GdkImage *img;
116} PropSaved;
117
21700912 118typedef struct _propwidgets {
950d8a07 119 gboolean configure_dialog;
21700912
QT
120 VikTrwLayer *vtl;
121 VikTrack *tr;
2cf168ac
RN
122 VikViewport *vvp;
123 VikLayersPanel *vlp;
b66b0b38
RN
124 gint profile_width;
125 gint profile_height;
2f078f56
RN
126 gint profile_width_old;
127 gint profile_height_old;
128 gint profile_width_offset;
129 gint profile_height_offset;
8dcb3ba4 130 GtkWidget *dialog;
21700912 131 GtkWidget *w_comment;
6b2f262e 132 GtkWidget *w_description;
21700912
QT
133 GtkWidget *w_track_length;
134 GtkWidget *w_tp_count;
135 GtkWidget *w_segment_count;
136 GtkWidget *w_duptp_count;
137 GtkWidget *w_max_speed;
138 GtkWidget *w_avg_speed;
a68ddd9c 139 GtkWidget *w_mvg_speed;
21700912
QT
140 GtkWidget *w_avg_dist;
141 GtkWidget *w_elev_range;
142 GtkWidget *w_elev_gain;
143 GtkWidget *w_time_start;
144 GtkWidget *w_time_end;
145 GtkWidget *w_time_dur;
b1453c16 146 GtkWidget *w_color;
387ff7ac
RN
147 GtkWidget *w_namelabel;
148 GtkWidget *w_number_distlabels;
065f60f1 149 GtkWidget *w_cur_dist; /*< Current distance */
780d3771 150 GtkWidget *w_cur_elevation;
0ba33e1d
FA
151 GtkWidget *w_cur_gradient_dist; /*< Current distance on gradient graph */
152 GtkWidget *w_cur_gradient_gradient; /*< Current gradient on gradient graph */
1b40910b
RN
153 GtkWidget *w_cur_time; /*< Current track time */
154 GtkWidget *w_cur_time_real; /*< Actual time as on a clock */
780d3771 155 GtkWidget *w_cur_speed;
926c8140 156 GtkWidget *w_cur_dist_dist; /*< Current distance on distance graph */
1b40910b
RN
157 GtkWidget *w_cur_dist_time; /*< Current track time on distance graph */
158 GtkWidget *w_cur_dist_time_real; // Clock time
8de26632 159 GtkWidget *w_cur_elev_elev;
1b40910b
RN
160 GtkWidget *w_cur_elev_time; // Track time
161 GtkWidget *w_cur_elev_time_real; // Clock time
7b624086
RN
162 GtkWidget *w_cur_speed_dist;
163 GtkWidget *w_cur_speed_speed;
ec32b567
RN
164 GtkWidget *w_show_dem;
165 GtkWidget *w_show_alt_gps_speed;
166 GtkWidget *w_show_gps_speed;
0ba33e1d 167 GtkWidget *w_show_gradient_gps_speed;
926c8140 168 GtkWidget *w_show_dist_speed;
8de26632 169 GtkWidget *w_show_elev_speed;
3772b8c2 170 GtkWidget *w_show_elev_dem;
7b624086 171 GtkWidget *w_show_sd_gps_speed;
e60fc2c9 172 gdouble track_length;
f9744e8f 173 gdouble track_length_inc_gaps;
ca7e67ef 174 PropSaved elev_graph_saved_img;
0ba33e1d 175 PropSaved gradient_graph_saved_img;
ca7e67ef 176 PropSaved speed_graph_saved_img;
926c8140 177 PropSaved dist_graph_saved_img;
8de26632 178 PropSaved elev_time_graph_saved_img;
7b624086 179 PropSaved speed_dist_graph_saved_img;
e60fc2c9 180 GtkWidget *elev_box;
0ba33e1d 181 GtkWidget *gradient_box;
e60fc2c9 182 GtkWidget *speed_box;
926c8140 183 GtkWidget *dist_box;
8de26632 184 GtkWidget *elev_time_box;
7b624086 185 GtkWidget *speed_dist_box;
0b2bfa08 186 gdouble *altitudes;
8de26632 187 gdouble *ats; // altitudes in time
83cd69cb
RN
188 gdouble min_altitude;
189 gdouble max_altitude;
c6bc2190 190 gdouble draw_min_altitude;
d2bae53c 191 gdouble draw_min_altitude_time;
c6bc2190 192 gint cia; // Chunk size Index into Altitudes
d2bae53c
RN
193 gint ciat; // Chunk size Index into Altitudes / Time
194 // NB cia & ciat are normally same value but sometimes not due to differing methods of altitude array creation
195 // thus also have draw_min_altitude for each altitude graph type
0ba33e1d
FA
196 gdouble *gradients;
197 gdouble min_gradient;
198 gdouble max_gradient;
199 gdouble draw_min_gradient;
200 gint cig; // Chunk size Index into Gradients
0b2bfa08 201 gdouble *speeds;
7b624086 202 gdouble *speeds_dist;
83cd69cb
RN
203 gdouble min_speed;
204 gdouble max_speed;
976c7df6 205 gdouble draw_min_speed;
7b624086 206 gdouble max_speed_dist;
976c7df6 207 gint cis; // Chunk size Index into Speeds
7b624086 208 gint cisd; // Chunk size Index into Speed/Distance
926c8140
RN
209 gdouble *distances;
210 gint cid; // Chunk size Index into Distance
8dcb3ba4 211 VikTrackpoint *marker_tp;
7ee55449 212 gboolean is_marker_drawn;
50391e88
RN
213 VikTrackpoint *blob_tp;
214 gboolean is_blob_drawn;
7f44287f 215 time_t duration;
74562734 216 gchar *tz; // TimeZone at track's location
21700912
QT
217} PropWidgets;
218
ca7e67ef
QT
219static PropWidgets *prop_widgets_new()
220{
221 PropWidgets *widgets = g_malloc0(sizeof(PropWidgets));
222
223 return widgets;
224}
225
226static void prop_widgets_free(PropWidgets *widgets)
227{
ca7e67ef
QT
228 if (widgets->elev_graph_saved_img.img)
229 g_object_unref(widgets->elev_graph_saved_img.img);
0ba33e1d
FA
230 if (widgets->gradient_graph_saved_img.img)
231 g_object_unref(widgets->gradient_graph_saved_img.img);
ca7e67ef
QT
232 if (widgets->speed_graph_saved_img.img)
233 g_object_unref(widgets->speed_graph_saved_img.img);
926c8140
RN
234 if (widgets->dist_graph_saved_img.img)
235 g_object_unref(widgets->dist_graph_saved_img.img);
8de26632
RN
236 if (widgets->elev_time_graph_saved_img.img)
237 g_object_unref(widgets->elev_time_graph_saved_img.img);
7b624086
RN
238 if (widgets->speed_dist_graph_saved_img.img)
239 g_object_unref(widgets->speed_dist_graph_saved_img.img);
0b2bfa08
RN
240 if (widgets->altitudes)
241 g_free(widgets->altitudes);
0ba33e1d
FA
242 if (widgets->gradients)
243 g_free(widgets->gradients);
0b2bfa08
RN
244 if (widgets->speeds)
245 g_free(widgets->speeds);
926c8140
RN
246 if (widgets->distances)
247 g_free(widgets->distances);
8de26632
RN
248 if (widgets->ats)
249 g_free(widgets->ats);
7b624086
RN
250 if (widgets->speeds_dist)
251 g_free(widgets->speeds_dist);
ca7e67ef
QT
252 g_free(widgets);
253}
254
b66b0b38 255static void minmax_array(const gdouble *array, gdouble *min, gdouble *max, gboolean NO_ALT_TEST, gint PROFILE_WIDTH)
50a14534
EB
256{
257 *max = -1000;
258 *min = 20000;
259 guint i;
260 for ( i=0; i < PROFILE_WIDTH; i++ ) {
93a85f58
RN
261 if ( NO_ALT_TEST || (array[i] != VIK_DEFAULT_ALTITUDE) ) {
262 if ( array[i] > *max )
263 *max = array[i];
264 if ( array[i] < *min )
265 *min = array[i];
50a14534
EB
266 }
267 }
50a14534
EB
268}
269
206157df 270#define MARGIN_X 70
7f44287f 271#define MARGIN_Y 20
25e44eac 272#define LINES 5
c6bc2190 273/**
f25c1cf8 274 * get_new_min_and_chunk_index:
c6bc2190 275 * Returns via pointers:
f25c1cf8
RN
276 * the new minimum value to be used for the graph
277 * the index in to the chunk sizes array (ci = Chunk Index)
c6bc2190 278 */
f25c1cf8 279static void get_new_min_and_chunk_index (gdouble mina, gdouble maxa, const gdouble *chunks, size_t chunky, gdouble *new_min, gint *ci)
c6bc2190
RN
280{
281 /* Get unitized chunk */
282 /* Find suitable chunk index */
283 *ci = 0;
f25c1cf8 284 gdouble diff_chunk = (maxa - mina)/LINES;
c6bc2190
RN
285
286 /* Loop through to find best match */
f25c1cf8 287 while (diff_chunk > chunks[*ci]) {
c6bc2190
RN
288 (*ci)++;
289 /* Last Resort Check */
f25c1cf8
RN
290 if ( *ci == chunky ) {
291 // Use previous value and exit loop
292 (*ci)--;
c6bc2190 293 break;
c6bc2190
RN
294 }
295 }
0ba33e1d
FA
296
297 /* Ensure adjusted minimum .. maximum covers mina->maxa */
298
299 // Now work out adjusted minimum point to the nearest lowest chunk divisor value
300 // When negative ensure logic uses lowest value
301 if ( mina < 0 )
f25c1cf8 302 *new_min = (gdouble) ( (gint)((mina - chunks[*ci]) / chunks[*ci]) * chunks[*ci] );
0ba33e1d 303 else
f25c1cf8 304 *new_min = (gdouble) ( (gint)(mina / chunks[*ci]) * chunks[*ci] );
0ba33e1d
FA
305
306 // Range not big enough - as new minimum has lowered
f25c1cf8 307 if ((*new_min + (chunks[*ci] * LINES) < maxa)) {
0ba33e1d 308 // Next chunk should cover it
f25c1cf8 309 if ( *ci < chunky-1 ) {
0ba33e1d
FA
310 (*ci)++;
311 // Remember to adjust the minimum too...
312 if ( mina < 0 )
f25c1cf8 313 *new_min = (gdouble) ( (gint)((mina - chunks[*ci]) / chunks[*ci]) * chunks[*ci] );
0ba33e1d 314 else
f25c1cf8 315 *new_min = (gdouble) ( (gint)(mina / chunks[*ci]) * chunks[*ci] );
0ba33e1d
FA
316 }
317 }
318}
319
7f44287f
RN
320static guint get_time_chunk_index (time_t duration)
321{
322 // Grid split
323 time_t myduration = duration / LINES;
324
325 // Search nearest chunk index
326 guint ci = 0;
327 guint last_chunk = G_N_ELEMENTS(chunkst);
328
329 // Loop through to find best match
330 while (myduration > chunkst[ci]) {
331 ci++;
332 // Last Resort Check
333 if ( ci == last_chunk )
334 break;
335 }
336 // Use previous value
337 if ( ci != 0 )
338 ci--;
339
340 return ci;
341}
342
87819e20
RN
343/**
344 *
345 */
346static guint get_distance_chunk_index (gdouble length)
347{
348 // Grid split
349 gdouble mylength = length / LINES;
350
351 // Search nearest chunk index
352 guint ci = 0;
353 guint last_chunk = G_N_ELEMENTS(chunksd);
354
355 // Loop through to find best match
356 while (mylength > chunksd[ci]) {
357 ci++;
358 // Last Resort Check
359 if ( ci == last_chunk )
360 break;
361 }
362 // Use previous value
363 if ( ci != 0 )
364 ci--;
365
366 return ci;
367}
368
b66b0b38
RN
369static VikTrackpoint *set_center_at_graph_position(gdouble event_x,
370 gint img_width,
371 VikTrwLayer *vtl,
372 VikLayersPanel *vlp,
373 VikViewport *vvp,
374 VikTrack *tr,
375 gboolean time_base,
376 gint PROFILE_WIDTH)
24d5c7e2 377{
32e48121 378 VikTrackpoint *trackpoint;
206157df 379 gdouble x = event_x - img_width / 2 + PROFILE_WIDTH / 2 - MARGIN_X / 2;
e1e2f2c6
JJ
380 if (x < 0)
381 x = 0;
382 if (x > PROFILE_WIDTH)
383 x = PROFILE_WIDTH;
32e48121
QT
384
385 if (time_base)
ddc2372e 386 trackpoint = vik_track_get_closest_tp_by_percentage_time ( tr, (gdouble) x / PROFILE_WIDTH, NULL );
32e48121 387 else
ddc2372e 388 trackpoint = vik_track_get_closest_tp_by_percentage_dist ( tr, (gdouble) x / PROFILE_WIDTH, NULL );
32e48121 389
e1e2f2c6
JJ
390 if ( trackpoint ) {
391 VikCoord coord = trackpoint->coord;
6bb72350 392 if ( vlp ) {
be5554c5 393 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &coord, TRUE );
6bb72350
RN
394 vik_layers_panel_emit_update ( vlp );
395 }
396 else {
397 /* since vlp not set, vvp should be valid instead! */
398 if ( vvp )
be5554c5 399 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
da121f9b 400 vik_layer_emit_update ( VIK_LAYER(vtl) );
6bb72350 401 }
e1e2f2c6 402 }
e60fc2c9 403 return trackpoint;
e1e2f2c6 404}
ca7e67ef 405
7ee55449 406/**
50391e88 407 * Returns whether the marker was drawn or not and whether the blob was drawn or not
7ee55449 408 */
50391e88
RN
409static void save_image_and_draw_graph_marks (GtkWidget *image,
410 gdouble marker_x,
411 GdkGC *gc,
412 gint blob_x,
413 gint blob_y,
414 PropSaved *saved_img,
415 gint PROFILE_WIDTH,
416 gint PROFILE_HEIGHT,
417 gboolean *marker_drawn,
418 gboolean *blob_drawn)
32e48121 419{
d973878f 420 GdkPixmap *pix = NULL;
9dc30292 421 /* the pixmap = margin + graph area */
ca7e67ef 422 gtk_image_get_pixmap(GTK_IMAGE(image), &pix, NULL);
50391e88 423
7ee55449 424 /* Restore previously saved image */
ca7e67ef 425 if (saved_img->saved) {
7f44287f 426 gdk_draw_image(GDK_DRAWABLE(pix), gc, saved_img->img, 0, 0, 0, 0, MARGIN_X+PROFILE_WIDTH, MARGIN_Y+PROFILE_HEIGHT);
ca7e67ef 427 saved_img->saved = FALSE;
ca7e67ef 428 }
50391e88
RN
429
430 // ATM always save whole image - as anywhere could have changed
431 if (saved_img->img)
7f44287f 432 gdk_drawable_copy_to_image(GDK_DRAWABLE(pix), saved_img->img, 0, 0, 0, 0, MARGIN_X+PROFILE_WIDTH, MARGIN_Y+PROFILE_HEIGHT);
50391e88 433 else
7f44287f 434 saved_img->img = gdk_drawable_copy_to_image(GDK_DRAWABLE(pix), saved_img->img, 0, 0, 0, 0, MARGIN_X+PROFILE_WIDTH, MARGIN_Y+PROFILE_HEIGHT);
50391e88
RN
435 saved_img->saved = TRUE;
436
206157df 437 if ((marker_x >= MARGIN_X) && (marker_x < (PROFILE_WIDTH + MARGIN_X))) {
7f44287f 438 gdk_draw_line (GDK_DRAWABLE(pix), gc, marker_x, MARGIN_Y, marker_x, PROFILE_HEIGHT + MARGIN_Y);
7ee55449 439 *marker_drawn = TRUE;
ca7e67ef 440 }
7ee55449
RN
441 else
442 *marker_drawn = FALSE;
50391e88
RN
443
444 // Draw a square blob to indicate where we are on track for this graph
7f44287f 445 if ( (blob_x >= MARGIN_X) && (blob_x < (PROFILE_WIDTH + MARGIN_X)) && (blob_y < PROFILE_HEIGHT+MARGIN_Y) ) {
50391e88
RN
446 gdk_draw_rectangle (GDK_DRAWABLE(pix), gc, TRUE, blob_x-3, blob_y-3, 6, 6);
447 *blob_drawn = TRUE;
448 }
449 else
450 *blob_drawn = FALSE;
451
452 // Anywhere on image could have changed
453 if (*marker_drawn || *blob_drawn)
454 gtk_widget_queue_draw(image);
32e48121
QT
455}
456
fb822443
RN
457/**
458 * Return the percentage of how far a trackpoint is a long a track via the time method
459 */
460static gdouble tp_percentage_by_time ( VikTrack *tr, VikTrackpoint *trackpoint )
461{
462 gdouble pc = NAN;
463 if (trackpoint == NULL)
464 return pc;
465 time_t t_start, t_end, t_total;
466 t_start = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
467 t_end = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
468 t_total = t_end - t_start;
469 pc = (gdouble)(trackpoint->timestamp - t_start)/t_total;
470 return pc;
471}
472
473/**
474 * Return the percentage of how far a trackpoint is a long a track via the distance method
475 */
476static gdouble tp_percentage_by_distance ( VikTrack *tr, VikTrackpoint *trackpoint, gdouble track_length )
477{
478 gdouble pc = NAN;
479 if (trackpoint == NULL)
480 return pc;
481 gdouble dist = 0.0;
482 GList *iter;
483 for (iter = tr->trackpoints->next; iter != NULL; iter = iter->next) {
484 dist += vik_coord_diff(&(VIK_TRACKPOINT(iter->data)->coord),
485 &(VIK_TRACKPOINT(iter->prev->data)->coord));
486 /* Assuming trackpoint is not a copy */
487 if (trackpoint == VIK_TRACKPOINT(iter->data))
488 break;
489 }
490 if (iter != NULL)
491 pc = dist/track_length;
492 return pc;
493}
494
2cf168ac 495static void track_graph_click( GtkWidget *event_box, GdkEventButton *event, PropWidgets *widgets, VikPropWinGraphType_t graph_type )
32e48121 496{
7b624086
RN
497 gboolean is_time_graph =
498 ( graph_type == PROPWIN_GRAPH_TYPE_SPEED_TIME ||
499 graph_type == PROPWIN_GRAPH_TYPE_DISTANCE_TIME ||
500 graph_type == PROPWIN_GRAPH_TYPE_ELEVATION_TIME );
ca7e67ef 501
90ba111c
RN
502 GtkAllocation allocation;
503 gtk_widget_get_allocation ( event_box, &allocation );
504
505 VikTrackpoint *trackpoint = set_center_at_graph_position(event->x, allocation.width, widgets->vtl, widgets->vlp, widgets->vvp, widgets->tr, is_time_graph, widgets->profile_width);
926c8140
RN
506 // Unable to get the point so give up
507 if ( trackpoint == NULL ) {
508 gtk_dialog_set_response_sensitive(GTK_DIALOG(widgets->dialog), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER, FALSE);
e60fc2c9 509 return;
926c8140
RN
510 }
511
512 widgets->marker_tp = trackpoint;
e60fc2c9 513
926c8140
RN
514 GList *child;
515 GtkWidget *image;
516 GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(event_box));
517 GtkWidget *graph_box;
518 PropSaved *graph_saved_img;
e60fc2c9 519 gdouble pc = NAN;
e60fc2c9 520
926c8140
RN
521 // Attempt to redraw marker on all graph types
522 gint graphite;
523 for ( graphite = PROPWIN_GRAPH_TYPE_ELEVATION_DISTANCE;
524 graphite < PROPWIN_GRAPH_TYPE_END;
525 graphite++ ) {
526
527 // Switch commonal variables to particular graph type
528 switch (graphite) {
529 default:
530 case PROPWIN_GRAPH_TYPE_ELEVATION_DISTANCE:
531 graph_box = widgets->elev_box;
532 graph_saved_img = &widgets->elev_graph_saved_img;
533 is_time_graph = FALSE;
534 break;
0ba33e1d
FA
535 case PROPWIN_GRAPH_TYPE_GRADIENT_DISTANCE:
536 graph_box = widgets->gradient_box;
537 graph_saved_img = &widgets->gradient_graph_saved_img;
538 is_time_graph = FALSE;
539 break;
926c8140
RN
540 case PROPWIN_GRAPH_TYPE_SPEED_TIME:
541 graph_box = widgets->speed_box;
542 graph_saved_img = &widgets->speed_graph_saved_img;
543 is_time_graph = TRUE;
544 break;
545 case PROPWIN_GRAPH_TYPE_DISTANCE_TIME:
546 graph_box = widgets->dist_box;
547 graph_saved_img = &widgets->dist_graph_saved_img;
548 is_time_graph = TRUE;
549 break;
8de26632
RN
550 case PROPWIN_GRAPH_TYPE_ELEVATION_TIME:
551 graph_box = widgets->elev_time_box;
552 graph_saved_img = &widgets->elev_time_graph_saved_img;
553 is_time_graph = TRUE;
554 break;
7b624086
RN
555 case PROPWIN_GRAPH_TYPE_SPEED_DISTANCE:
556 graph_box = widgets->speed_dist_box;
557 graph_saved_img = &widgets->speed_dist_graph_saved_img;
558 is_time_graph = FALSE;
559 break;
926c8140
RN
560 }
561
562 // Commonal method of redrawing marker
563 if ( graph_box ) {
e60fc2c9 564
926c8140
RN
565 child = gtk_container_get_children(GTK_CONTAINER(graph_box));
566 image = GTK_WIDGET(child->data);
567
568 if (is_time_graph)
2cf168ac 569 pc = tp_percentage_by_time ( widgets->tr, trackpoint );
926c8140 570 else
2cf168ac 571 pc = tp_percentage_by_distance ( widgets->tr, trackpoint, widgets->track_length_inc_gaps );
926c8140
RN
572
573 if (!isnan(pc)) {
7f44287f 574 gdouble marker_x = (pc * widgets->profile_width) + MARGIN_X;
926c8140
RN
575 save_image_and_draw_graph_marks(image,
576 marker_x,
ff37db21 577 gtk_widget_get_style(window)->black_gc,
926c8140
RN
578 -1, // Don't draw blob on clicks
579 0,
580 graph_saved_img,
581 widgets->profile_width,
582 widgets->profile_height,
583 &widgets->is_marker_drawn,
584 &widgets->is_blob_drawn);
585 }
586 g_list_free(child);
587 }
588 }
589
590 gtk_dialog_set_response_sensitive(GTK_DIALOG(widgets->dialog), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER, widgets->is_marker_drawn);
ca7e67ef
QT
591}
592
2cf168ac 593static gboolean track_profile_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
ca7e67ef 594{
2cf168ac 595 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_ELEVATION_DISTANCE);
ca7e67ef
QT
596 return TRUE; /* don't call other (further) callbacks */
597}
598
2cf168ac 599static gboolean track_gradient_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
0ba33e1d 600{
2cf168ac 601 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_GRADIENT_DISTANCE);
0ba33e1d
FA
602 return TRUE; /* don't call other (further) callbacks */
603}
604
2cf168ac 605static gboolean track_vt_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
ca7e67ef 606{
2cf168ac 607 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_SPEED_TIME);
926c8140
RN
608 return TRUE; /* don't call other (further) callbacks */
609}
610
2cf168ac 611static gboolean track_dt_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
926c8140 612{
2cf168ac 613 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_DISTANCE_TIME);
ca7e67ef 614 return TRUE; /* don't call other (further) callbacks */
32e48121
QT
615}
616
2cf168ac 617static gboolean track_et_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
8de26632 618{
2cf168ac 619 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_ELEVATION_TIME);
8de26632
RN
620 return TRUE; /* don't call other (further) callbacks */
621}
622
2cf168ac 623static gboolean track_sd_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
7b624086 624{
2cf168ac 625 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_SPEED_DISTANCE);
7b624086
RN
626 return TRUE; /* don't call other (further) callbacks */
627}
628
50391e88
RN
629/**
630 * Calculate y position for blob on elevation graph
631 */
632static gint blobby_altitude ( gdouble x_blob, PropWidgets *widgets )
633{
634 gint ix = (gint)x_blob;
635 // Ensure ix is inbounds
636 if (ix == widgets->profile_width)
637 ix--;
638
c6bc2190 639 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->altitudes[ix]-widgets->draw_min_altitude)/(chunksa[widgets->cia]*LINES);
50391e88
RN
640
641 return y_blob;
642}
643
0ba33e1d
FA
644/**
645 * Calculate y position for blob on gradient graph
646 */
647static gint blobby_gradient ( gdouble x_blob, PropWidgets *widgets )
648{
649 gint ix = (gint)x_blob;
650 // Ensure ix is inbounds
651 if (ix == widgets->profile_width)
652 ix--;
653
654 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->gradients[ix]-widgets->draw_min_gradient)/(chunksg[widgets->cig]*LINES);
655
656 return y_blob;
657}
658
50391e88
RN
659/**
660 * Calculate y position for blob on speed graph
661 */
662static gint blobby_speed ( gdouble x_blob, PropWidgets *widgets )
663{
664 gint ix = (gint)x_blob;
665 // Ensure ix is inbounds
666 if (ix == widgets->profile_width)
667 ix--;
668
976c7df6 669 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->speeds[ix]-widgets->draw_min_speed)/(chunkss[widgets->cis]*LINES);
50391e88
RN
670
671 return y_blob;
672}
673
926c8140
RN
674/**
675 * Calculate y position for blob on distance graph
676 */
677static gint blobby_distance ( gdouble x_blob, PropWidgets *widgets )
678{
679 gint ix = (gint)x_blob;
680 // Ensure ix is inbounds
681 if (ix == widgets->profile_width)
682 ix--;
683
684 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->distances[ix])/(chunksd[widgets->cid]*LINES);
685 //NB min distance is always 0, so no need to subtract that from this ______/
686
687 return y_blob;
688}
689
8de26632
RN
690/**
691 * Calculate y position for blob on elevation/time graph
692 */
693static gint blobby_altitude_time ( gdouble x_blob, PropWidgets *widgets )
694{
695 gint ix = (gint)x_blob;
696 // Ensure ix is inbounds
697 if (ix == widgets->profile_width)
698 ix--;
699
d2bae53c 700 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->ats[ix]-widgets->draw_min_altitude_time)/(chunksa[widgets->ciat]*LINES);
8de26632
RN
701 return y_blob;
702}
703
7b624086
RN
704/**
705 * Calculate y position for blob on speed/dist graph
706 */
707static gint blobby_speed_dist ( gdouble x_blob, PropWidgets *widgets )
708{
709 gint ix = (gint)x_blob;
710 // Ensure ix is inbounds
711 if (ix == widgets->profile_width)
712 ix--;
713
714 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->speeds_dist[ix]-widgets->draw_min_speed)/(chunkss[widgets->cisd]*LINES);
715
716 return y_blob;
717}
718
8de26632 719
2cf168ac 720void track_profile_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
e1e2f2c6 721{
e1e2f2c6
JJ
722 int mouse_x, mouse_y;
723 GdkModifierType state;
724
725 if (event->is_hint)
726 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
727 else
728 mouse_x = event->x;
729
90ba111c
RN
730 GtkAllocation allocation;
731 gtk_widget_get_allocation ( event_box, &allocation );
732
206157df 733 gdouble x = mouse_x - allocation.width / 2 + widgets->profile_width / 2 - MARGIN_X / 2;
e1e2f2c6
JJ
734 if (x < 0)
735 x = 0;
b66b0b38
RN
736 if (x > widgets->profile_width)
737 x = widgets->profile_width;
e1e2f2c6 738
ddc2372e 739 gdouble meters_from_start;
2cf168ac 740 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_dist ( widgets->tr, (gdouble) x / widgets->profile_width, &meters_from_start );
065f60f1 741 if (trackpoint && widgets->w_cur_dist) {
ddc2372e 742 static gchar tmp_buf[20];
6f9336aa
RN
743 vik_units_distance_t dist_units = a_vik_get_units_distance ();
744 switch (dist_units) {
745 case VIK_UNITS_DISTANCE_KILOMETRES:
746 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km", meters_from_start/1000.0);
747 break;
748 case VIK_UNITS_DISTANCE_MILES:
433b3f7f 749 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f miles", VIK_METERS_TO_MILES(meters_from_start) );
6f9336aa 750 break;
b22233bd
RN
751 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
752 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f NM", VIK_METERS_TO_NAUTICAL_MILES(meters_from_start) );
753 break;
6f9336aa
RN
754 default:
755 g_critical("Houston, we've had a problem. distance=%d", dist_units);
756 }
065f60f1 757 gtk_label_set_text(GTK_LABEL(widgets->w_cur_dist), tmp_buf);
ddc2372e 758 }
780d3771
RN
759
760 // Show track elevation for this position - to the nearest whole number
761 if (trackpoint && widgets->w_cur_elevation) {
762 static gchar tmp_buf[20];
763 if (a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET)
764 g_snprintf(tmp_buf, sizeof(tmp_buf), "%d ft", (int)VIK_METERS_TO_FEET(trackpoint->altitude));
765 else
766 g_snprintf(tmp_buf, sizeof(tmp_buf), "%d m", (int)trackpoint->altitude);
767 gtk_label_set_text(GTK_LABEL(widgets->w_cur_elevation), tmp_buf);
768 }
50391e88
RN
769
770 widgets->blob_tp = trackpoint;
771
772 if ( widgets->altitudes == NULL )
773 return;
774
775 GtkWidget *window = gtk_widget_get_toplevel (event_box);
776 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
777 GtkWidget *image = GTK_WIDGET(child->data);
778
779 gint y_blob = blobby_altitude (x, widgets);
780
781 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
782 if (widgets->is_marker_drawn) {
2cf168ac 783 gdouble pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
50391e88 784 if (!isnan(pc)) {
206157df 785 marker_x = (pc * widgets->profile_width) + MARGIN_X;
50391e88
RN
786 }
787 }
788
789 save_image_and_draw_graph_marks (image,
790 marker_x,
ff37db21 791 gtk_widget_get_style(window)->black_gc,
206157df 792 MARGIN_X+x,
7f44287f 793 MARGIN_Y+y_blob,
50391e88
RN
794 &widgets->elev_graph_saved_img,
795 widgets->profile_width,
796 widgets->profile_height,
797 &widgets->is_marker_drawn,
798 &widgets->is_blob_drawn);
799
800 g_list_free(child);
ddc2372e
QT
801}
802
2cf168ac 803void track_gradient_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
0ba33e1d 804{
0ba33e1d
FA
805 int mouse_x, mouse_y;
806 GdkModifierType state;
807
808 if (event->is_hint)
809 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
810 else
811 mouse_x = event->x;
812
90ba111c
RN
813 GtkAllocation allocation;
814 gtk_widget_get_allocation ( event_box, &allocation );
815
206157df 816 gdouble x = mouse_x - allocation.width / 2 + widgets->profile_width / 2 - MARGIN_X / 2;
0ba33e1d
FA
817 if (x < 0)
818 x = 0;
819 if (x > widgets->profile_width)
820 x = widgets->profile_width;
821
822 gdouble meters_from_start;
2cf168ac 823 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_dist ( widgets->tr, (gdouble) x / widgets->profile_width, &meters_from_start );
0ba33e1d
FA
824 if (trackpoint && widgets->w_cur_gradient_dist) {
825 static gchar tmp_buf[20];
826 vik_units_distance_t dist_units = a_vik_get_units_distance ();
827 switch (dist_units) {
828 case VIK_UNITS_DISTANCE_KILOMETRES:
829 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km", meters_from_start/1000.0);
830 break;
831 case VIK_UNITS_DISTANCE_MILES:
832 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f miles", VIK_METERS_TO_MILES(meters_from_start) );
833 break;
b22233bd
RN
834 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
835 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f NM", VIK_METERS_TO_NAUTICAL_MILES(meters_from_start) );
836 break;
0ba33e1d
FA
837 default:
838 g_critical("Houston, we've had a problem. distance=%d", dist_units);
839 }
840 gtk_label_set_text(GTK_LABEL(widgets->w_cur_gradient_dist), tmp_buf);
841 }
842
843 // Show track gradient for this position - to the nearest whole number
844 if (trackpoint && widgets->w_cur_gradient_gradient) {
845 static gchar tmp_buf[20];
846
847 double gradient = widgets->gradients[(int) x];
848
849 g_snprintf(tmp_buf, sizeof(tmp_buf), "%d%%", (int)gradient);
850 gtk_label_set_text(GTK_LABEL(widgets->w_cur_gradient_gradient), tmp_buf);
851 }
852
853 widgets->blob_tp = trackpoint;
854
855 if ( widgets->gradients == NULL )
856 return;
857
858 GtkWidget *window = gtk_widget_get_toplevel (event_box);
859 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
860 GtkWidget *image = GTK_WIDGET(child->data);
861
862 gint y_blob = blobby_gradient (x, widgets);
863
864 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
865 if (widgets->is_marker_drawn) {
2cf168ac 866 gdouble pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
0ba33e1d 867 if (!isnan(pc)) {
206157df 868 marker_x = (pc * widgets->profile_width) + MARGIN_X;
0ba33e1d
FA
869 }
870 }
871
872 save_image_and_draw_graph_marks (image,
873 marker_x,
ff37db21 874 gtk_widget_get_style(window)->black_gc,
206157df 875 MARGIN_X+x,
7f44287f 876 MARGIN_Y+y_blob,
0ba33e1d
FA
877 &widgets->gradient_graph_saved_img,
878 widgets->profile_width,
879 widgets->profile_height,
880 &widgets->is_marker_drawn,
881 &widgets->is_blob_drawn);
882
883 g_list_free(child);
884}
885
f6e1cd6b
RN
886//
887static void time_label_update (GtkWidget *widget, time_t seconds_from_start)
888{
889 static gchar tmp_buf[20];
890 guint h = seconds_from_start/3600;
891 guint m = (seconds_from_start - h*3600)/60;
892 guint s = seconds_from_start - (3600*h) - (60*m);
893 g_snprintf(tmp_buf, sizeof(tmp_buf), "%02d:%02d:%02d", h, m, s);
894 gtk_label_set_text(GTK_LABEL(widget), tmp_buf);
895}
896
1b40910b 897//
74562734 898static void real_time_label_update ( PropWidgets *widgets, GtkWidget *widget, VikTrackpoint *trackpoint)
1b40910b
RN
899{
900 static gchar tmp_buf[64];
8ada46de 901 if ( trackpoint->has_timestamp ) {
1b40910b
RN
902 // Alternatively could use %c format but I prefer a slightly more compact form here
903 // The full date can of course be seen on the Statistics tab
9e405703 904 strftime (tmp_buf, sizeof(tmp_buf), "%X %x %Z", localtime(&(trackpoint->timestamp)));
8ada46de 905 }
1b40910b
RN
906 else
907 g_snprintf (tmp_buf, sizeof(tmp_buf), _("No Data"));
908 gtk_label_set_text(GTK_LABEL(widget), tmp_buf);
909}
910
2cf168ac 911void track_vt_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
ddc2372e 912{
ddc2372e
QT
913 int mouse_x, mouse_y;
914 GdkModifierType state;
915
916 if (event->is_hint)
917 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
918 else
919 mouse_x = event->x;
920
90ba111c
RN
921 GtkAllocation allocation;
922 gtk_widget_get_allocation ( event_box, &allocation );
206157df 923 gdouble x = mouse_x - allocation.width / 2 + widgets->profile_width / 2 - MARGIN_X / 2;
ddc2372e
QT
924 if (x < 0)
925 x = 0;
b66b0b38
RN
926 if (x > widgets->profile_width)
927 x = widgets->profile_width;
ddc2372e
QT
928
929 time_t seconds_from_start;
2cf168ac 930 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_time ( widgets->tr, (gdouble) x / widgets->profile_width, &seconds_from_start );
065f60f1 931 if (trackpoint && widgets->w_cur_time) {
f6e1cd6b 932 time_label_update ( widgets->w_cur_time, seconds_from_start );
24d5c7e2 933 }
780d3771 934
1b40910b 935 if (trackpoint && widgets->w_cur_time_real) {
74562734 936 real_time_label_update ( widgets, widgets->w_cur_time_real, trackpoint );
1b40910b
RN
937 }
938
50391e88
RN
939 gint ix = (gint)x;
940 // Ensure ix is inbounds
941 if (ix == widgets->profile_width)
942 ix--;
943
780d3771
RN
944 // Show track speed for this position
945 if (trackpoint && widgets->w_cur_speed) {
946 static gchar tmp_buf[20];
947 // Even if GPS speed available (trackpoint->speed), the text will correspond to the speed map shown
976c7df6 948 // No conversions needed as already in appropriate units
780d3771
RN
949 vik_units_speed_t speed_units = a_vik_get_units_speed ();
950 switch (speed_units) {
951 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
976c7df6 952 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f kph"), widgets->speeds[ix]);
780d3771
RN
953 break;
954 case VIK_UNITS_SPEED_MILES_PER_HOUR:
976c7df6 955 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f mph"), widgets->speeds[ix]);
780d3771
RN
956 break;
957 case VIK_UNITS_SPEED_KNOTS:
976c7df6 958 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f knots"), widgets->speeds[ix]);
780d3771
RN
959 break;
960 default:
961 // VIK_UNITS_SPEED_METRES_PER_SECOND:
780d3771
RN
962 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f m/s"), widgets->speeds[ix]);
963 break;
964 }
965 gtk_label_set_text(GTK_LABEL(widgets->w_cur_speed), tmp_buf);
966 }
50391e88
RN
967
968 widgets->blob_tp = trackpoint;
969
970 if ( widgets->speeds == NULL )
971 return;
972
973 GtkWidget *window = gtk_widget_get_toplevel (event_box);
974 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
975 GtkWidget *image = GTK_WIDGET(child->data);
976
977 gint y_blob = blobby_speed (x, widgets);
978
979 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
980 if (widgets->is_marker_drawn) {
2cf168ac 981 gdouble pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
50391e88 982 if (!isnan(pc)) {
206157df 983 marker_x = (pc * widgets->profile_width) + MARGIN_X;
50391e88
RN
984 }
985 }
986
987 save_image_and_draw_graph_marks (image,
988 marker_x,
ff37db21 989 gtk_widget_get_style(window)->black_gc,
206157df 990 MARGIN_X+x,
7f44287f 991 MARGIN_Y+y_blob,
50391e88
RN
992 &widgets->speed_graph_saved_img,
993 widgets->profile_width,
994 widgets->profile_height,
995 &widgets->is_marker_drawn,
996 &widgets->is_blob_drawn);
997
998 g_list_free(child);
24d5c7e2
EB
999}
1000
926c8140
RN
1001/**
1002 * Update labels and blob marker on mouse moves in the distance/time graph
1003 */
2cf168ac 1004void track_dt_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
926c8140 1005{
926c8140
RN
1006 int mouse_x, mouse_y;
1007 GdkModifierType state;
1008
1009 if (event->is_hint)
1010 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
1011 else
1012 mouse_x = event->x;
1013
90ba111c
RN
1014 GtkAllocation allocation;
1015 gtk_widget_get_allocation ( event_box, &allocation );
1016
206157df 1017 gdouble x = mouse_x - allocation.width / 2 + widgets->profile_width / 2 - MARGIN_X / 2;
926c8140
RN
1018 if (x < 0)
1019 x = 0;
1020 if (x > widgets->profile_width)
1021 x = widgets->profile_width;
1022
1023 time_t seconds_from_start;
2cf168ac 1024 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_time ( widgets->tr, (gdouble) x / widgets->profile_width, &seconds_from_start );
926c8140 1025 if (trackpoint && widgets->w_cur_dist_time) {
f6e1cd6b 1026 time_label_update ( widgets->w_cur_dist_time, seconds_from_start );
926c8140
RN
1027 }
1028
1b40910b 1029 if (trackpoint && widgets->w_cur_dist_time_real) {
74562734 1030 real_time_label_update ( widgets, widgets->w_cur_dist_time_real, trackpoint );
1b40910b
RN
1031 }
1032
926c8140
RN
1033 gint ix = (gint)x;
1034 // Ensure ix is inbounds
1035 if (ix == widgets->profile_width)
1036 ix--;
1037
1038 if (trackpoint && widgets->w_cur_dist_dist) {
1039 static gchar tmp_buf[20];
b22233bd
RN
1040 switch ( a_vik_get_units_distance () ) {
1041 case VIK_UNITS_DISTANCE_MILES:
926c8140 1042 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f miles", widgets->distances[ix]);
b22233bd
RN
1043 break;
1044 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1045 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f NM", widgets->distances[ix]);
1046 break;
1047 default:
926c8140 1048 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km", widgets->distances[ix]);
b22233bd
RN
1049 break;
1050 }
926c8140
RN
1051 gtk_label_set_text(GTK_LABEL(widgets->w_cur_dist_dist), tmp_buf);
1052 }
1053
1054 widgets->blob_tp = trackpoint;
1055
1056 if ( widgets->distances == NULL )
1057 return;
1058
1059 GtkWidget *window = gtk_widget_get_toplevel (event_box);
1060 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
1061 GtkWidget *image = GTK_WIDGET(child->data);
1062
1063 gint y_blob = blobby_distance (x, widgets);
1064
1065 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
1066 if (widgets->is_marker_drawn) {
2cf168ac 1067 gdouble pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
926c8140 1068 if (!isnan(pc)) {
206157df 1069 marker_x = (pc * widgets->profile_width) + MARGIN_X;
926c8140
RN
1070 }
1071 }
1072
1073 save_image_and_draw_graph_marks (image,
1074 marker_x,
ff37db21 1075 gtk_widget_get_style(window)->black_gc,
206157df 1076 MARGIN_X+x,
7f44287f 1077 MARGIN_Y+y_blob,
926c8140
RN
1078 &widgets->dist_graph_saved_img,
1079 widgets->profile_width,
1080 widgets->profile_height,
1081 &widgets->is_marker_drawn,
1082 &widgets->is_blob_drawn);
1083
1084 g_list_free(child);
1085}
1086
8de26632
RN
1087/**
1088 * Update labels and blob marker on mouse moves in the elevation/time graph
1089 */
2cf168ac 1090void track_et_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
8de26632 1091{
8de26632
RN
1092 int mouse_x, mouse_y;
1093 GdkModifierType state;
1094
1095 if (event->is_hint)
1096 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
1097 else
1098 mouse_x = event->x;
1099
90ba111c
RN
1100 GtkAllocation allocation;
1101 gtk_widget_get_allocation ( event_box, &allocation );
1102
206157df 1103 gdouble x = mouse_x - allocation.width / 2 + widgets->profile_width / 2 - MARGIN_X / 2;
8de26632
RN
1104 if (x < 0)
1105 x = 0;
1106 if (x > widgets->profile_width)
1107 x = widgets->profile_width;
1108
1109 time_t seconds_from_start;
2cf168ac 1110 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_time ( widgets->tr, (gdouble) x / widgets->profile_width, &seconds_from_start );
8de26632 1111 if (trackpoint && widgets->w_cur_elev_time) {
f6e1cd6b 1112 time_label_update ( widgets->w_cur_elev_time, seconds_from_start );
8de26632
RN
1113 }
1114
1b40910b 1115 if (trackpoint && widgets->w_cur_elev_time_real) {
74562734 1116 real_time_label_update ( widgets, widgets->w_cur_elev_time_real, trackpoint );
1b40910b
RN
1117 }
1118
8de26632
RN
1119 gint ix = (gint)x;
1120 // Ensure ix is inbounds
1121 if (ix == widgets->profile_width)
1122 ix--;
1123
1124 if (trackpoint && widgets->w_cur_elev_elev) {
1125 static gchar tmp_buf[20];
1126 if (a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET)
1127 g_snprintf(tmp_buf, sizeof(tmp_buf), "%d ft", (int)VIK_METERS_TO_FEET(trackpoint->altitude));
1128 else
1129 g_snprintf(tmp_buf, sizeof(tmp_buf), "%d m", (int)trackpoint->altitude);
1130 gtk_label_set_text(GTK_LABEL(widgets->w_cur_elev_elev), tmp_buf);
1131 }
1132
1133 widgets->blob_tp = trackpoint;
1134
1135 if ( widgets->ats == NULL )
1136 return;
1137
1138 GtkWidget *window = gtk_widget_get_toplevel (event_box);
1139 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
1140 GtkWidget *image = GTK_WIDGET(child->data);
1141
1142 gint y_blob = blobby_altitude_time (x, widgets);
1143
1144 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
1145 if (widgets->is_marker_drawn) {
2cf168ac 1146 gdouble pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
8de26632 1147 if (!isnan(pc)) {
206157df 1148 marker_x = (pc * widgets->profile_width) + MARGIN_X;
8de26632
RN
1149 }
1150 }
1151
1152 save_image_and_draw_graph_marks (image,
1153 marker_x,
ff37db21 1154 gtk_widget_get_style(window)->black_gc,
206157df 1155 MARGIN_X+x,
7f44287f 1156 MARGIN_Y+y_blob,
8de26632
RN
1157 &widgets->elev_time_graph_saved_img,
1158 widgets->profile_width,
1159 widgets->profile_height,
1160 &widgets->is_marker_drawn,
1161 &widgets->is_blob_drawn);
1162
1163 g_list_free(child);
1164}
1165
2cf168ac 1166void track_sd_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
7b624086 1167{
7b624086
RN
1168 int mouse_x, mouse_y;
1169 GdkModifierType state;
1170
1171 if (event->is_hint)
1172 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
1173 else
1174 mouse_x = event->x;
1175
90ba111c
RN
1176 GtkAllocation allocation;
1177 gtk_widget_get_allocation ( event_box, &allocation );
1178
206157df 1179 gdouble x = mouse_x - allocation.width / 2 + widgets->profile_width / 2 - MARGIN_X / 2;
7b624086
RN
1180 if (x < 0)
1181 x = 0;
1182 if (x > widgets->profile_width)
1183 x = widgets->profile_width;
1184
1185 gdouble meters_from_start;
2cf168ac 1186 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_dist ( widgets->tr, (gdouble) x / widgets->profile_width, &meters_from_start );
7b624086
RN
1187 if (trackpoint && widgets->w_cur_speed_dist) {
1188 static gchar tmp_buf[20];
1189 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1190 switch (dist_units) {
1191 case VIK_UNITS_DISTANCE_KILOMETRES:
1192 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km", meters_from_start/1000.0);
1193 break;
1194 case VIK_UNITS_DISTANCE_MILES:
1195 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f miles", VIK_METERS_TO_MILES(meters_from_start) );
1196 break;
b22233bd
RN
1197 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1198 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f NM", VIK_METERS_TO_NAUTICAL_MILES(meters_from_start) );
1199 break;
7b624086
RN
1200 default:
1201 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1202 }
1203 gtk_label_set_text(GTK_LABEL(widgets->w_cur_speed_dist), tmp_buf);
1204 }
1205
1206 gint ix = (gint)x;
1207 // Ensure ix is inbounds
1208 if (ix == widgets->profile_width)
1209 ix--;
1210
1211 if ( widgets->speeds_dist == NULL )
1212 return;
1213
1214 // Show track speed for this position
1215 if (widgets->w_cur_speed_speed) {
1216 static gchar tmp_buf[20];
1217 // Even if GPS speed available (trackpoint->speed), the text will correspond to the speed map shown
1218 // No conversions needed as already in appropriate units
1219 vik_units_speed_t speed_units = a_vik_get_units_speed ();
1220 switch (speed_units) {
1221 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
1222 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f kph"), widgets->speeds_dist[ix]);
1223 break;
1224 case VIK_UNITS_SPEED_MILES_PER_HOUR:
1225 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f mph"), widgets->speeds_dist[ix]);
1226 break;
1227 case VIK_UNITS_SPEED_KNOTS:
1228 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f knots"), widgets->speeds_dist[ix]);
1229 break;
1230 default:
1231 // VIK_UNITS_SPEED_METRES_PER_SECOND:
1232 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f m/s"), widgets->speeds_dist[ix]);
1233 break;
1234 }
1235 gtk_label_set_text(GTK_LABEL(widgets->w_cur_speed_speed), tmp_buf);
1236 }
1237
1238 widgets->blob_tp = trackpoint;
1239
1240 GtkWidget *window = gtk_widget_get_toplevel (event_box);
1241 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
1242 GtkWidget *image = GTK_WIDGET(child->data);
1243
1244 gint y_blob = blobby_speed_dist (x, widgets);
1245
1246 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
1247 if (widgets->is_marker_drawn) {
2cf168ac 1248 gdouble pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
7b624086 1249 if (!isnan(pc)) {
206157df 1250 marker_x = (pc * widgets->profile_width) + MARGIN_X;
7b624086
RN
1251 }
1252 }
1253
1254 save_image_and_draw_graph_marks (image,
1255 marker_x,
ff37db21 1256 gtk_widget_get_style(window)->black_gc,
206157df 1257 MARGIN_X+x,
7f44287f 1258 MARGIN_Y+y_blob,
7b624086
RN
1259 &widgets->speed_dist_graph_saved_img,
1260 widgets->profile_width,
1261 widgets->profile_height,
1262 &widgets->is_marker_drawn,
1263 &widgets->is_blob_drawn);
1264
1265 g_list_free(child);
1266}
1267
6bca6a7d
RN
1268/**
1269 * Draws DEM points and a respresentative speed on the supplied pixmap
1270 * (which is the elevations graph)
1271 */
1272static void draw_dem_alt_speed_dist(VikTrack *tr,
1273 GdkDrawable *pix,
1274 GdkGC *alt_gc,
1275 GdkGC *speed_gc,
1276 gdouble alt_offset,
1277 gdouble alt_diff,
83cd69cb 1278 gdouble max_speed_in,
c6bc2190 1279 gint cia,
6bca6a7d
RN
1280 gint width,
1281 gint height,
1282 gint margin,
1283 gboolean do_dem,
1284 gboolean do_speed)
07596bf4
QT
1285{
1286 GList *iter;
07596bf4
QT
1287 gdouble max_speed = 0;
1288 gdouble total_length = vik_track_get_length_including_gaps(tr);
1289
83cd69cb
RN
1290 // Calculate the max speed factor
1291 if (do_speed)
1292 max_speed = max_speed_in * 110 / 100;
07596bf4 1293
6bca6a7d 1294 gdouble dist = 0;
a92612bc
RN
1295 gint h2 = height + MARGIN_Y; // Adjust height for x axis labelling offset
1296 gint achunk = chunksa[cia]*LINES;
1297
07596bf4 1298 for (iter = tr->trackpoints->next; iter; iter = iter->next) {
6bca6a7d 1299 int x;
07596bf4 1300 dist += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
6bca6a7d 1301 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
07596bf4 1302 x = (width * dist)/total_length + margin;
6bca6a7d
RN
1303 if (do_dem) {
1304 gint16 elev = a_dems_get_elev_by_coord(&(VIK_TRACKPOINT(iter->data)->coord), VIK_DEM_INTERPOL_BEST);
1305 elev -= alt_offset;
1306 if ( elev != VIK_DEM_INVALID_ELEVATION ) {
c6bc2190
RN
1307 // Convert into height units
1308 if (a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET)
1309 elev = VIK_METERS_TO_FEET(elev);
1310 // No conversion needed if already in metres
1311
a92612bc
RN
1312 // consider chunk size
1313 int y_alt = h2 - ((height * elev)/achunk );
1314 gdk_draw_rectangle(GDK_DRAWABLE(pix), alt_gc, TRUE, x-2, y_alt-2, 4, 4);
6bca6a7d 1315 }
07596bf4 1316 }
6bca6a7d
RN
1317 if (do_speed) {
1318 // This is just a speed indicator - no actual values can be inferred by user
1319 if (!isnan(VIK_TRACKPOINT(iter->data)->speed)) {
a92612bc
RN
1320 int y_speed = h2 - (height * VIK_TRACKPOINT(iter->data)->speed)/max_speed;
1321 gdk_draw_rectangle(GDK_DRAWABLE(pix), speed_gc, TRUE, x-2, y_speed-2, 4, 4);
6bca6a7d 1322 }
07596bf4
QT
1323 }
1324 }
1325}
1326
2299976d 1327/**
206157df 1328 * draw_grid_y:
2299976d
RN
1329 *
1330 * A common way to draw the grid with y axis labels
1331 *
1332 */
206157df 1333static void draw_grid_y ( GtkWidget *window, GtkWidget *image, PropWidgets *widgets, GdkPixmap *pix, gchar *ss, gint i )
2299976d
RN
1334{
1335 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(image), NULL);
1336
1337 pango_layout_set_alignment (pl, PANGO_ALIGN_RIGHT);
1338 pango_layout_set_font_description (pl, gtk_widget_get_style(window)->font_desc);
1339
1340 gchar *label_markup = g_strdup_printf ( "<span size=\"small\">%s</span>", ss );
1341 pango_layout_set_markup ( pl, label_markup, -1 );
1342 g_free ( label_markup );
1343
1344 int w, h;
1345 pango_layout_get_pixel_size ( pl, &w, &h );
1346
206157df
RN
1347 gdk_draw_layout ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->fg_gc[0],
1348 MARGIN_X-w-3,
7f44287f
RN
1349 CLAMP((int)i*widgets->profile_height/LINES - h/2 + MARGIN_Y, 0, widgets->profile_height-h+MARGIN_Y),
1350 pl );
1351 g_object_unref ( G_OBJECT ( pl ) );
2299976d
RN
1352
1353 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[0],
7f44287f
RN
1354 MARGIN_X, MARGIN_Y + widgets->profile_height/LINES * i,
1355 MARGIN_X + widgets->profile_width, MARGIN_Y + widgets->profile_height/LINES * i );
1356}
1357
1358/**
1359 * draw_grid_x_time:
1360 *
1361 * A common way to draw the grid with x axis labels for time graphs
1362 *
1363 */
1364static void draw_grid_x_time ( GtkWidget *window, GtkWidget *image, PropWidgets *widgets, GdkPixmap *pix, guint ii, guint tt, guint xx )
1365{
1366 gchar *label_markup = NULL;
1367 switch (ii) {
1368 case 0:
1369 case 1:
1370 case 2:
1371 case 3:
1372 // Minutes
1373 label_markup = g_strdup_printf ( "<span size=\"small\">%d %s</span>", tt/60, _("mins") );
1374 break;
1375 case 4:
1376 case 5:
1377 case 6:
1378 case 7:
1379 // Hours
1380 label_markup = g_strdup_printf ( "<span size=\"small\">%.1f %s</span>", (gdouble)tt/(60*60), _("h") );
1381 break;
1382 case 8:
1383 case 9:
1384 case 10:
1385 // Days
1386 label_markup = g_strdup_printf ( "<span size=\"small\">%.1f %s</span>", (gdouble)tt/(60*60*24), _("d") );
1387 break;
1388 case 11:
1389 case 12:
1390 // Weeks
1391 label_markup = g_strdup_printf ( "<span size=\"small\">%.1f %s</span>", (gdouble)tt/(60*60*24*7), _("w") );
1392 break;
1393 case 13:
1394 // 'Months'
1395 label_markup = g_strdup_printf ( "<span size=\"small\">%.1f %s</span>", (gdouble)tt/(60*60*24*28), _("M") );
1396 break;
1397 default:
1398 break;
1399 }
1400 if ( label_markup ) {
1401
1402 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(image), NULL);
1403 pango_layout_set_font_description (pl, gtk_widget_get_style(window)->font_desc);
1404
1405 pango_layout_set_markup ( pl, label_markup, -1 );
1406 g_free ( label_markup );
1407 int ww, hh;
1408 pango_layout_get_pixel_size ( pl, &ww, &hh );
1409
1410 gdk_draw_layout ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->fg_gc[0],
1411 MARGIN_X+xx-ww/2, MARGIN_Y/2-hh/2, pl );
1412 g_object_unref ( G_OBJECT ( pl ) );
1413 }
1414
1415 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[0],
1416 MARGIN_X+xx, MARGIN_Y, MARGIN_X+xx, MARGIN_Y+widgets->profile_height );
2299976d
RN
1417}
1418
87819e20
RN
1419/**
1420 * draw_grid_x_distance:
1421 *
1422 * A common way to draw the grid with x axis labels for distance graphs
1423 *
1424 */
1425static void draw_grid_x_distance ( GtkWidget *window, GtkWidget *image, PropWidgets *widgets, GdkPixmap *pix, guint ii, gdouble dd, guint xx, vik_units_distance_t dist_units )
1426{
1427 gchar *label_markup = NULL;
b22233bd
RN
1428 switch ( dist_units ) {
1429 case VIK_UNITS_DISTANCE_MILES:
87819e20
RN
1430 if ( ii > 4 )
1431 label_markup = g_strdup_printf ( "<span size=\"small\">%d %s</span>", (guint)dd, _("miles") );
1432 else
1433 label_markup = g_strdup_printf ( "<span size=\"small\">%.1f %s</span>", dd, _("miles") );
b22233bd
RN
1434 break;
1435 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1436 if ( ii > 4 )
1437 label_markup = g_strdup_printf ( "<span size=\"small\">%d %s</span>", (guint)dd, _("NM") );
1438 else
1439 label_markup = g_strdup_printf ( "<span size=\"small\">%.1f %s</span>", dd, _("NM") );
1440 break;
1441 default:
1442 // VIK_UNITS_DISTANCE_KILOMETRES:
87819e20
RN
1443 if ( ii > 4 )
1444 label_markup = g_strdup_printf ( "<span size=\"small\">%d %s</span>", (guint)dd, _("km") );
1445 else
1446 label_markup = g_strdup_printf ( "<span size=\"small\">%.1f %s</span>", dd, _("km") );
b22233bd
RN
1447 break;
1448 }
87819e20
RN
1449
1450 if ( label_markup ) {
1451 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(image), NULL);
1452 pango_layout_set_font_description (pl, gtk_widget_get_style(window)->font_desc);
1453
1454 pango_layout_set_markup ( pl, label_markup, -1 );
1455 g_free ( label_markup );
1456 int ww, hh;
1457 pango_layout_get_pixel_size ( pl, &ww, &hh );
1458
1459 gdk_draw_layout ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->fg_gc[0],
1460 MARGIN_X+xx-ww/2, MARGIN_Y/2-hh/2, pl );
1461 g_object_unref ( G_OBJECT ( pl ) );
1462 }
1463
1464 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[0],
1465 MARGIN_X+xx, MARGIN_Y, MARGIN_X+xx, MARGIN_Y+widgets->profile_height );
1466}
1467
206157df
RN
1468/**
1469 * clear the images (scale texts & actual graph)
1470 */
1471static void clear_images (GdkPixmap *pix, GtkWidget *window, PropWidgets *widgets)
1472{
1473 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->bg_gc[0],
7f44287f 1474 TRUE, 0, 0, widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y);
206157df 1475 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->mid_gc[0],
7f44287f 1476 TRUE, 0, 0, widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y);
206157df
RN
1477}
1478
87819e20
RN
1479/**
1480 *
1481 */
1482static void draw_distance_divisions ( GtkWidget *window, GtkWidget *image, GdkPixmap *pix, PropWidgets *widgets, vik_units_distance_t dist_units )
1483{
1484 // Set to display units from length in metres.
1485 gdouble length = widgets->track_length_inc_gaps;
b22233bd
RN
1486 switch (dist_units) {
1487 case VIK_UNITS_DISTANCE_MILES:
1488 length = VIK_METERS_TO_MILES(length);
1489 break;
1490 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1491 length = VIK_METERS_TO_NAUTICAL_MILES(length);
1492 break;
1493 default:
1494 // KM
1495 length = length/1000.0;
1496 break;
1497 }
87819e20
RN
1498 guint index = get_distance_chunk_index ( length );
1499 gdouble dist_per_pixel = length/widgets->profile_width;
1500
1501 for (guint i=1; chunksd[index]*i <= length; i++) {
1502 draw_grid_x_distance ( window, image, widgets, pix, index, chunksd[index]*i, (guint)(chunksd[index]*i/dist_per_pixel), dist_units );
1503 }
1504}
1505
950d8a07
RN
1506/**
1507 * Draw just the height profile image
1508 */
1509static void draw_elevations (GtkWidget *image, VikTrack *tr, PropWidgets *widgets )
50a14534 1510{
50a14534
EB
1511 guint i;
1512
950d8a07 1513 GdkGC *no_alt_info;
950d8a07
RN
1514 GdkColor color;
1515
0b2bfa08
RN
1516 // Free previous allocation
1517 if ( widgets->altitudes )
1518 g_free ( widgets->altitudes );
1519
b66b0b38 1520 widgets->altitudes = vik_track_make_elevation_map ( tr, widgets->profile_width );
950d8a07 1521
0b2bfa08 1522 if ( widgets->altitudes == NULL )
950d8a07
RN
1523 return;
1524
c6bc2190
RN
1525 // Convert into appropriate units
1526 vik_units_height_t height_units = a_vik_get_units_height ();
1527 if ( height_units == VIK_UNITS_HEIGHT_FEET ) {
1528 // Convert altitudes into feet units
1529 for ( i = 0; i < widgets->profile_width; i++ ) {
1530 widgets->altitudes[i] = VIK_METERS_TO_FEET(widgets->altitudes[i]);
1531 }
1532 }
1533 // Otherwise leave in metres
1534
83cd69cb 1535 minmax_array(widgets->altitudes, &widgets->min_altitude, &widgets->max_altitude, TRUE, widgets->profile_width);
c6bc2190 1536
f25c1cf8 1537 get_new_min_and_chunk_index (widgets->min_altitude, widgets->max_altitude, chunksa, G_N_ELEMENTS(chunksa), &widgets->draw_min_altitude, &widgets->cia);
c6bc2190 1538
83cd69cb 1539 // Assign locally
7f44287f 1540 gdouble mina = widgets->draw_min_altitude;
c79f0206 1541
7f44287f
RN
1542 GtkWidget *window = gtk_widget_get_toplevel (widgets->elev_box);
1543 GdkPixmap *pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
c79f0206 1544
950d8a07 1545 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
50a14534 1546
9b082b39 1547 no_alt_info = gdk_gc_new ( gtk_widget_get_window(window) );
07596bf4 1548 gdk_color_parse ( "yellow", &color );
50a14534 1549 gdk_gc_set_rgb_fg_color ( no_alt_info, &color);
950d8a07 1550
206157df
RN
1551 // Reset before redrawing
1552 clear_images (pix, window, widgets);
1553
25e44eac 1554 /* draw grid */
25e44eac 1555 for (i=0; i<=LINES; i++) {
25e44eac 1556 gchar s[32];
56cb1807 1557
6027a9c5
RN
1558 switch (height_units) {
1559 case VIK_UNITS_HEIGHT_METRES:
c6bc2190 1560 sprintf(s, "%8dm", (int)(mina + (LINES-i)*chunksa[widgets->cia]));
6027a9c5
RN
1561 break;
1562 case VIK_UNITS_HEIGHT_FEET:
c6bc2190
RN
1563 // NB values already converted into feet
1564 sprintf(s, "%8dft", (int)(mina + (LINES-i)*chunksa[widgets->cia]));
6027a9c5
RN
1565 break;
1566 default:
1567 sprintf(s, "--");
1568 g_critical("Houston, we've had a problem. height=%d", height_units);
1569 }
2299976d 1570
206157df 1571 draw_grid_y ( window, image, widgets, pix, s, i );
25e44eac
AF
1572 }
1573
87819e20
RN
1574 draw_distance_divisions ( window, image, pix, widgets, a_vik_get_units_distance() );
1575
25e44eac 1576 /* draw elevations */
7f44287f 1577 guint height = MARGIN_Y+widgets->profile_height;
b66b0b38 1578 for ( i = 0; i < widgets->profile_width; i++ )
0b2bfa08 1579 if ( widgets->altitudes[i] == VIK_DEFAULT_ALTITUDE )
25e44eac 1580 gdk_draw_line ( GDK_DRAWABLE(pix), no_alt_info,
7f44287f 1581 i + MARGIN_X, MARGIN_Y, i + MARGIN_X, height );
bb71de8b 1582 else
ff37db21 1583 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
7f44287f 1584 i + MARGIN_X, height, i + MARGIN_X, height-widgets->profile_height*(widgets->altitudes[i]-mina)/(chunksa[widgets->cia]*LINES) );
734652bf 1585
2280c34f
RN
1586 if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_dem)) ||
1587 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_alt_gps_speed)) ) {
1588
9b082b39
RN
1589 GdkGC *dem_alt_gc = gdk_gc_new ( gtk_widget_get_window(window) );
1590 GdkGC *gps_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
2280c34f
RN
1591
1592 gdk_color_parse ( "green", &color );
1593 gdk_gc_set_rgb_fg_color ( dem_alt_gc, &color);
1594
1595 gdk_color_parse ( "red", &color );
1596 gdk_gc_set_rgb_fg_color ( gps_speed_gc, &color);
1597
1598 // Ensure somekind of max speed when not set
1599 if ( widgets->max_speed < 0.01 )
1600 widgets->max_speed = vik_track_get_max_speed(tr);
1601
1602 draw_dem_alt_speed_dist(tr,
1603 GDK_DRAWABLE(pix),
1604 dem_alt_gc,
1605 gps_speed_gc,
1606 mina,
3772b8c2 1607 widgets->max_altitude - mina,
2280c34f
RN
1608 widgets->max_speed,
1609 widgets->cia,
1610 widgets->profile_width,
a92612bc 1611 widgets->profile_height,
206157df 1612 MARGIN_X,
2280c34f
RN
1613 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_dem)),
1614 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_alt_gps_speed)));
1615
1616 g_object_unref ( G_OBJECT(dem_alt_gc) );
1617 g_object_unref ( G_OBJECT(gps_speed_gc) );
1618 }
07596bf4 1619
25e44eac 1620 /* draw border */
7f44287f 1621 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN_X, MARGIN_Y, widgets->profile_width-1, widgets->profile_height-1);
25e44eac 1622
50a14534 1623 g_object_unref ( G_OBJECT(pix) );
50a14534 1624 g_object_unref ( G_OBJECT(no_alt_info) );
50a14534 1625}
24d5c7e2 1626
0ba33e1d
FA
1627/**
1628 * Draws representative speed on the supplied pixmap
1629 * (which is the gradients graph)
1630 */
1631static void draw_speed_dist(VikTrack *tr,
1632 GdkDrawable *pix,
1633 GdkGC *speed_gc,
1634 gdouble max_speed_in,
1635 gint width,
1636 gint height,
1637 gint margin,
1638 gboolean do_speed)
1639{
1640 GList *iter;
1641 gdouble max_speed = 0;
1642 gdouble total_length = vik_track_get_length_including_gaps(tr);
1643
1644 // Calculate the max speed factor
1645 if (do_speed)
1646 max_speed = max_speed_in * 110 / 100;
1647
1648 gdouble dist = 0;
1649 for (iter = tr->trackpoints->next; iter; iter = iter->next) {
1650 int x;
1651 dist += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
1652 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
7f44287f 1653 x = (width * dist)/total_length + MARGIN_X;
0ba33e1d
FA
1654 if (do_speed) {
1655 // This is just a speed indicator - no actual values can be inferred by user
1656 if (!isnan(VIK_TRACKPOINT(iter->data)->speed)) {
1657 int y_speed = height - (height * VIK_TRACKPOINT(iter->data)->speed)/max_speed;
1658 gdk_draw_rectangle(GDK_DRAWABLE(pix), speed_gc, TRUE, x-2, y_speed-2, 4, 4);
1659 }
1660 }
1661 }
1662}
1663
1664/**
1665 * Draw just the gradient image
1666 */
1667static void draw_gradients (GtkWidget *image, VikTrack *tr, PropWidgets *widgets )
1668{
0ba33e1d
FA
1669 guint i;
1670
0ba33e1d
FA
1671 // Free previous allocation
1672 if ( widgets->gradients )
1673 g_free ( widgets->gradients );
1674
1675 widgets->gradients = vik_track_make_gradient_map ( tr, widgets->profile_width );
1676
1677 if ( widgets->gradients == NULL )
1678 return;
1679
1680 minmax_array(widgets->gradients, &widgets->min_gradient, &widgets->max_gradient, TRUE, widgets->profile_width);
1681
f25c1cf8 1682 get_new_min_and_chunk_index (widgets->min_gradient, widgets->max_gradient, chunksg, G_N_ELEMENTS(chunksg), &widgets->draw_min_gradient, &widgets->cig);
0ba33e1d
FA
1683
1684 // Assign locally
7f44287f 1685 gdouble mina = widgets->draw_min_gradient;
0ba33e1d 1686
7f44287f
RN
1687 GtkWidget *window = gtk_widget_get_toplevel (widgets->gradient_box);
1688 GdkPixmap *pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
0ba33e1d
FA
1689
1690 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
1691
206157df
RN
1692 // Reset before redrawing
1693 clear_images (pix, window, widgets);
1694
0ba33e1d
FA
1695 /* draw grid */
1696 for (i=0; i<=LINES; i++) {
0ba33e1d 1697 gchar s[32];
56cb1807 1698
0ba33e1d 1699 sprintf(s, "%8d%%", (int)(mina + (LINES-i)*chunksg[widgets->cig]));
2299976d 1700
206157df 1701 draw_grid_y ( window, image, widgets, pix, s, i );
0ba33e1d
FA
1702 }
1703
87819e20
RN
1704 draw_distance_divisions ( window, image, pix, widgets, a_vik_get_units_distance() );
1705
0ba33e1d 1706 /* draw gradients */
7f44287f 1707 guint height = widgets->profile_height + MARGIN_Y;
0ba33e1d 1708 for ( i = 0; i < widgets->profile_width; i++ )
ff37db21 1709 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
7f44287f 1710 i + MARGIN_X, height, i + MARGIN_X, height - widgets->profile_height*(widgets->gradients[i]-mina)/(chunksg[widgets->cig]*LINES) );
0ba33e1d
FA
1711
1712 if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_gradient_gps_speed)) ) {
9b082b39 1713 GdkGC *gps_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
0ba33e1d 1714
7f44287f 1715 GdkColor color;
0ba33e1d
FA
1716 gdk_color_parse ( "red", &color );
1717 gdk_gc_set_rgb_fg_color ( gps_speed_gc, &color);
1718
1719 // Ensure somekind of max speed when not set
1720 if ( widgets->max_speed < 0.01 )
1721 widgets->max_speed = vik_track_get_max_speed(tr);
1722
1723 draw_speed_dist(tr,
1724 GDK_DRAWABLE(pix),
1725 gps_speed_gc,
1726 widgets->max_speed,
1727 widgets->profile_width,
1728 widgets->profile_height,
206157df 1729 MARGIN_X,
0ba33e1d
FA
1730 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_alt_gps_speed)));
1731
1732 g_object_unref ( G_OBJECT(gps_speed_gc) );
1733 }
1734
1735 /* draw border */
7f44287f 1736 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN_X, MARGIN_Y, widgets->profile_width-1, widgets->profile_height-1);
0ba33e1d
FA
1737
1738 g_object_unref ( G_OBJECT(pix) );
1739}
1740
7f44287f
RN
1741static void draw_time_lines ( GtkWidget *window, GtkWidget *image, GdkPixmap *pix, PropWidgets *widgets )
1742{
1743 guint index = get_time_chunk_index ( widgets->duration );
1744 gdouble time_per_pixel = (gdouble)(widgets->duration)/widgets->profile_width;
1745
1746 // If stupidly long track in time - don't bother trying to draw grid lines
1747 if ( widgets->duration > chunkst[G_N_ELEMENTS(chunkst)-1]*LINES*LINES )
1748 return;
1749
1750 for (guint i=1; chunkst[index]*i <= widgets->duration; i++) {
1751 draw_grid_x_time ( window, image, widgets, pix, index, chunkst[index]*i, (guint)(chunkst[index]*i/time_per_pixel) );
1752 }
1753}
1754
950d8a07
RN
1755/**
1756 * Draw just the speed (velocity)/time image
1757 */
1758static void draw_vt ( GtkWidget *image, VikTrack *tr, PropWidgets *widgets)
25e44eac 1759{
25e44eac 1760 guint i;
c79f0206 1761
0b2bfa08
RN
1762 // Free previous allocation
1763 if ( widgets->speeds )
1764 g_free ( widgets->speeds );
1765
b66b0b38 1766 widgets->speeds = vik_track_make_speed_map ( tr, widgets->profile_width );
0b2bfa08 1767 if ( widgets->speeds == NULL )
950d8a07
RN
1768 return;
1769
7f44287f
RN
1770 widgets->duration = vik_track_get_duration ( tr );
1771 // Negative time or other problem
1772 if ( widgets->duration <= 0 )
1773 return;
1774
976c7df6
RN
1775 // Convert into appropriate units
1776 vik_units_speed_t speed_units = a_vik_get_units_speed ();
1777 switch (speed_units) {
1778 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
1779 for ( i = 0; i < widgets->profile_width; i++ ) {
1780 widgets->speeds[i] = VIK_MPS_TO_KPH(widgets->speeds[i]);
1781 }
1782 break;
1783 case VIK_UNITS_SPEED_MILES_PER_HOUR:
1784 for ( i = 0; i < widgets->profile_width; i++ ) {
1785 widgets->speeds[i] = VIK_MPS_TO_MPH(widgets->speeds[i]);
1786 }
1787 break;
1788 case VIK_UNITS_SPEED_KNOTS:
1789 for ( i = 0; i < widgets->profile_width; i++ ) {
1790 widgets->speeds[i] = VIK_MPS_TO_KNOTS(widgets->speeds[i]);
1791 }
1792 break;
1793 default:
1794 // VIK_UNITS_SPEED_METRES_PER_SECOND:
1795 // No need to convert as already in m/s
1796 break;
1797 }
1798
7f44287f
RN
1799 GtkWidget *window = gtk_widget_get_toplevel (widgets->speed_box);
1800 GdkPixmap *pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
950d8a07
RN
1801
1802 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
1803
83cd69cb
RN
1804 minmax_array(widgets->speeds, &widgets->min_speed, &widgets->max_speed, FALSE, widgets->profile_width);
1805 if (widgets->min_speed < 0.0)
1806 widgets->min_speed = 0; /* splines sometimes give negative speeds */
976c7df6
RN
1807
1808 /* Find suitable chunk index */
f25c1cf8 1809 get_new_min_and_chunk_index (widgets->min_speed, widgets->max_speed, chunkss, G_N_ELEMENTS(chunkss), &widgets->draw_min_speed, &widgets->cis);
976c7df6 1810
83cd69cb 1811 // Assign locally
7f44287f
RN
1812 gdouble mins = widgets->draw_min_speed;
1813
206157df
RN
1814 // Reset before redrawing
1815 clear_images (pix, window, widgets);
25e44eac 1816
25e44eac 1817 /* draw grid */
25e44eac 1818 for (i=0; i<=LINES; i++) {
25e44eac 1819 gchar s[32];
56cb1807 1820
976c7df6 1821 // NB: No need to convert here anymore as numbers are in the appropriate units
13bdea80
RN
1822 switch (speed_units) {
1823 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
976c7df6 1824 sprintf(s, "%8dkm/h", (int)(mins + (LINES-i)*chunkss[widgets->cis]));
13bdea80
RN
1825 break;
1826 case VIK_UNITS_SPEED_MILES_PER_HOUR:
976c7df6 1827 sprintf(s, "%8dmph", (int)(mins + (LINES-i)*chunkss[widgets->cis]));
13bdea80
RN
1828 break;
1829 case VIK_UNITS_SPEED_METRES_PER_SECOND:
976c7df6 1830 sprintf(s, "%8dm/s", (int)(mins + (LINES-i)*chunkss[widgets->cis]));
13bdea80 1831 break;
a4c92313 1832 case VIK_UNITS_SPEED_KNOTS:
976c7df6 1833 sprintf(s, "%8dknots", (int)(mins + (LINES-i)*chunkss[widgets->cis]));
a4c92313 1834 break;
13bdea80
RN
1835 default:
1836 sprintf(s, "--");
1837 g_critical("Houston, we've had a problem. speed=%d", speed_units);
1838 }
1839
206157df 1840 draw_grid_y ( window, image, widgets, pix, s, i );
25e44eac 1841 }
7f44287f
RN
1842
1843 draw_time_lines ( window, image, pix, widgets );
25e44eac
AF
1844
1845 /* draw speeds */
7f44287f 1846 guint height = widgets->profile_height + MARGIN_Y;
b66b0b38 1847 for ( i = 0; i < widgets->profile_width; i++ )
ff37db21 1848 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
7f44287f 1849 i + MARGIN_X, height, i + MARGIN_X, height - widgets->profile_height*(widgets->speeds[i]-mins)/(chunkss[widgets->cis]*LINES) );
07596bf4 1850
ec32b567 1851 if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->w_show_gps_speed)) ) {
2280c34f 1852
9b082b39 1853 GdkGC *gps_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
2280c34f
RN
1854 GdkColor color;
1855 gdk_color_parse ( "red", &color );
1856 gdk_gc_set_rgb_fg_color ( gps_speed_gc, &color);
1857
1858 time_t beg_time = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1859 time_t dur = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp - beg_time;
1860
ec32b567
RN
1861 GList *iter;
1862 for (iter = tr->trackpoints; iter; iter = iter->next) {
1863 gdouble gps_speed = VIK_TRACKPOINT(iter->data)->speed;
1864 if (isnan(gps_speed))
07596bf4 1865 continue;
976c7df6
RN
1866 switch (speed_units) {
1867 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
1868 gps_speed = VIK_MPS_TO_KPH(gps_speed);
1869 break;
1870 case VIK_UNITS_SPEED_MILES_PER_HOUR:
1871 gps_speed = VIK_MPS_TO_MPH(gps_speed);
1872 break;
1873 case VIK_UNITS_SPEED_KNOTS:
1874 gps_speed = VIK_MPS_TO_KNOTS(gps_speed);
1875 break;
1876 default:
1877 // VIK_UNITS_SPEED_METRES_PER_SECOND:
1878 // No need to convert as already in m/s
1879 break;
1880 }
206157df 1881 int x = MARGIN_X + widgets->profile_width * (VIK_TRACKPOINT(iter->data)->timestamp - beg_time) / dur;
7f44287f 1882 int y = height - widgets->profile_height*(gps_speed - mins)/(chunkss[widgets->cis]*LINES);
ec32b567
RN
1883 gdk_draw_rectangle(GDK_DRAWABLE(pix), gps_speed_gc, TRUE, x-2, y-2, 4, 4);
1884 }
2280c34f 1885 g_object_unref ( G_OBJECT(gps_speed_gc) );
07596bf4
QT
1886 }
1887
25e44eac 1888 /* draw border */
7f44287f 1889 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN_X, MARGIN_Y, widgets->profile_width-1, widgets->profile_height-1);
25e44eac
AF
1890
1891 g_object_unref ( G_OBJECT(pix) );
926c8140
RN
1892}
1893
1894/**
1895 * Draw just the distance/time image
1896 */
1897static void draw_dt ( GtkWidget *image, VikTrack *tr, PropWidgets *widgets )
1898{
926c8140
RN
1899 guint i;
1900
1901 // Free previous allocation
1902 if ( widgets->distances )
1903 g_free ( widgets->distances );
1904
1905 widgets->distances = vik_track_make_distance_map ( tr, widgets->profile_width );
1906 if ( widgets->distances == NULL )
1907 return;
1908
1909 // Convert into appropriate units
1910 vik_units_distance_t dist_units = a_vik_get_units_distance ();
b22233bd
RN
1911 switch ( dist_units ) {
1912 case VIK_UNITS_DISTANCE_MILES:
1913 for ( i = 0; i < widgets->profile_width; i++ ) {
1914 widgets->distances[i] = VIK_METERS_TO_MILES(widgets->distances[i]);
1915 }
1916 break;
1917 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1918 for ( i = 0; i < widgets->profile_width; i++ ) {
1919 widgets->distances[i] = VIK_METERS_TO_NAUTICAL_MILES(widgets->distances[i]);
1920 }
1921 break;
1922 default:
1923 // Metres - but want in kms
1924 for ( i = 0; i < widgets->profile_width; i++ ) {
1925 widgets->distances[i] = widgets->distances[i]/1000.0;
1926 }
1927 break;
926c8140
RN
1928 }
1929
7f44287f
RN
1930 widgets->duration = vik_track_get_duration ( widgets->tr );
1931 // Negative time or other problem
1932 if ( widgets->duration <= 0 )
1933 return;
926c8140 1934
7f44287f
RN
1935 GtkWidget *window = gtk_widget_get_toplevel (widgets->dist_box);
1936 GdkPixmap *pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
926c8140
RN
1937
1938 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
1939
1940 // easy to work out min / max of distance!
1941 // Assign locally
1942 // mind = 0.0; - Thus not used
7f44287f 1943 gdouble maxd;
b22233bd
RN
1944 switch ( dist_units ) {
1945 case VIK_UNITS_DISTANCE_MILES:
926c8140 1946 maxd = VIK_METERS_TO_MILES(vik_track_get_length_including_gaps (tr));
b22233bd
RN
1947 break;
1948 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1949 maxd = VIK_METERS_TO_NAUTICAL_MILES(vik_track_get_length_including_gaps (tr));
1950 break;
1951 default:
926c8140 1952 maxd = vik_track_get_length_including_gaps (tr) / 1000.0;
b22233bd
RN
1953 break;
1954 }
926c8140
RN
1955
1956 /* Find suitable chunk index */
1957 gdouble dummy = 0.0; // expect this to remain the same! (not that it's used)
f25c1cf8 1958 get_new_min_and_chunk_index (0, maxd, chunksd, G_N_ELEMENTS(chunksd), &dummy, &widgets->cid);
926c8140 1959
206157df
RN
1960 // Reset before redrawing
1961 clear_images (pix, window, widgets);
926c8140
RN
1962
1963 /* draw grid */
1964 for (i=0; i<=LINES; i++) {
926c8140 1965 gchar s[32];
56cb1807 1966
b22233bd
RN
1967 switch ( dist_units ) {
1968 case VIK_UNITS_DISTANCE_MILES:
926c8140 1969 sprintf(s, _("%.1f miles"), ((LINES-i)*chunksd[widgets->cid]));
b22233bd
RN
1970 break;
1971 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1972 sprintf(s, _("%.1f NM"), ((LINES-i)*chunksd[widgets->cid]));
1973 break;
1974 default:
926c8140 1975 sprintf(s, _("%.1f km"), ((LINES-i)*chunksd[widgets->cid]));
b22233bd
RN
1976 break;
1977 }
926c8140 1978
206157df 1979 draw_grid_y ( window, image, widgets, pix, s, i );
926c8140
RN
1980 }
1981
7f44287f
RN
1982 draw_time_lines ( window, image, pix, widgets );
1983
926c8140 1984 /* draw distance */
7f44287f 1985 guint height = widgets->profile_height + MARGIN_Y;
926c8140 1986 for ( i = 0; i < widgets->profile_width; i++ )
ff37db21 1987 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
7f44287f 1988 i + MARGIN_X, height, i + MARGIN_X, height - widgets->profile_height*(widgets->distances[i])/(chunksd[widgets->cid]*LINES) );
926c8140
RN
1989
1990 // Show speed indicator
1991 if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->w_show_dist_speed)) ) {
9b082b39 1992 GdkGC *dist_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
926c8140
RN
1993 GdkColor color;
1994 gdk_color_parse ( "red", &color );
1995 gdk_gc_set_rgb_fg_color ( dist_speed_gc, &color);
1996
1997 gdouble max_speed = 0;
1998 max_speed = widgets->max_speed * 110 / 100;
1999
2000 // This is just an indicator - no actual values can be inferred by user
926c8140
RN
2001 for ( i = 0; i < widgets->profile_width; i++ ) {
2002 int y_speed = widgets->profile_height - (widgets->profile_height * widgets->speeds[i])/max_speed;
206157df 2003 gdk_draw_rectangle(GDK_DRAWABLE(pix), dist_speed_gc, TRUE, i+MARGIN_X-2, y_speed-2, 4, 4);
926c8140
RN
2004 }
2005 g_object_unref ( G_OBJECT(dist_speed_gc) );
2006 }
2007
2008 /* draw border */
7f44287f 2009 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN_X, MARGIN_Y, widgets->profile_width-1, widgets->profile_height-1);
926c8140
RN
2010
2011 g_object_unref ( G_OBJECT(pix) );
2012
8de26632
RN
2013}
2014
2015/**
2016 * Draw just the elevation/time image
2017 */
2018static void draw_et ( GtkWidget *image, VikTrack *tr, PropWidgets *widgets )
2019{
8de26632
RN
2020 guint i;
2021
2022 // Free previous allocation
2023 if ( widgets->ats )
2024 g_free ( widgets->ats );
2025
2026 widgets->ats = vik_track_make_elevation_time_map ( tr, widgets->profile_width );
2027
2028 if ( widgets->ats == NULL )
2029 return;
2030
2031 // Convert into appropriate units
2032 vik_units_height_t height_units = a_vik_get_units_height ();
2033 if ( height_units == VIK_UNITS_HEIGHT_FEET ) {
2034 // Convert altitudes into feet units
2035 for ( i = 0; i < widgets->profile_width; i++ ) {
2036 widgets->ats[i] = VIK_METERS_TO_FEET(widgets->ats[i]);
2037 }
2038 }
2039 // Otherwise leave in metres
2040
2041 minmax_array(widgets->ats, &widgets->min_altitude, &widgets->max_altitude, TRUE, widgets->profile_width);
2042
f25c1cf8 2043 get_new_min_and_chunk_index (widgets->min_altitude, widgets->max_altitude, chunksa, G_N_ELEMENTS(chunksa), &widgets->draw_min_altitude_time, &widgets->ciat);
8de26632
RN
2044
2045 // Assign locally
7f44287f 2046 gdouble mina = widgets->draw_min_altitude_time;
8de26632 2047
7f44287f
RN
2048 widgets->duration = vik_track_get_duration ( widgets->tr );
2049 // Negative time or other problem
2050 if ( widgets->duration <= 0 )
2051 return;
8de26632 2052
7f44287f
RN
2053 GtkWidget *window = gtk_widget_get_toplevel (widgets->elev_time_box);
2054 GdkPixmap *pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
8de26632
RN
2055
2056 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
2057
206157df
RN
2058 // Reset before redrawing
2059 clear_images (pix, window, widgets);
8de26632
RN
2060
2061 /* draw grid */
2062 for (i=0; i<=LINES; i++) {
8de26632 2063 gchar s[32];
56cb1807 2064
8de26632
RN
2065 switch (height_units) {
2066 case VIK_UNITS_HEIGHT_METRES:
d2bae53c 2067 sprintf(s, "%8dm", (int)(mina + (LINES-i)*chunksa[widgets->ciat]));
8de26632
RN
2068 break;
2069 case VIK_UNITS_HEIGHT_FEET:
2070 // NB values already converted into feet
d2bae53c 2071 sprintf(s, "%8dft", (int)(mina + (LINES-i)*chunksa[widgets->ciat]));
8de26632
RN
2072 break;
2073 default:
2074 sprintf(s, "--");
2075 g_critical("Houston, we've had a problem. height=%d", height_units);
2076 }
2299976d 2077
206157df 2078 draw_grid_y ( window, image, widgets, pix, s, i );
8de26632
RN
2079 }
2080
7f44287f
RN
2081 draw_time_lines ( window, image, pix, widgets );
2082
8de26632 2083 /* draw elevations */
7f44287f 2084 guint height = widgets->profile_height + MARGIN_Y;
8de26632 2085 for ( i = 0; i < widgets->profile_width; i++ )
ff37db21 2086 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
7f44287f 2087 i + MARGIN_X, height, i + MARGIN_X, height-widgets->profile_height*(widgets->ats[i]-mina)/(chunksa[widgets->ciat]*LINES) );
8de26632 2088
3772b8c2
RN
2089 // Show DEMS
2090 if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->w_show_elev_dem)) ) {
2091 GdkColor color;
2092 GdkGC *dem_alt_gc = gdk_gc_new ( gtk_widget_get_window(window) );
2093 gdk_color_parse ( "green", &color );
2094 gdk_gc_set_rgb_fg_color ( dem_alt_gc, &color);
2095
2096 gint h2 = widgets->profile_height + MARGIN_Y; // Adjust height for x axis labelling offset
2097 gint achunk = chunksa[widgets->ciat]*LINES;
2098
2099 for ( i = 0; i < widgets->profile_width; i++ ) {
2100 // This could be slow doing this each time...
2101 VikTrackpoint *tp = vik_track_get_closest_tp_by_percentage_time ( widgets->tr, ((gdouble)i/(gdouble)widgets->profile_width), NULL );
2102 if ( tp ) {
2103 gint16 elev = a_dems_get_elev_by_coord(&(tp->coord), VIK_DEM_INTERPOL_SIMPLE);
2104 elev -= mina; //?
2105 if ( elev != VIK_DEM_INVALID_ELEVATION ) {
2106 // Convert into height units
2107 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
2108 elev = VIK_METERS_TO_FEET(elev);
2109 // No conversion needed if already in metres
2110
2111 // consider chunk size
2112 int y_alt = h2 - ((widgets->profile_height * elev)/achunk );
2113 gdk_draw_rectangle(GDK_DRAWABLE(pix), dem_alt_gc, TRUE, i+MARGIN_X-2, y_alt-2, 4, 4);
2114 }
2115 }
2116 }
2117 g_object_unref ( G_OBJECT(dem_alt_gc) );
2118 }
2119
2120 // Show speeds
8de26632 2121 if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->w_show_elev_speed)) ) {
8de26632 2122 GdkColor color;
3772b8c2
RN
2123 // This is just an indicator - no actual values can be inferred by user
2124 GdkGC *elev_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
8de26632
RN
2125 gdk_color_parse ( "red", &color );
2126 gdk_gc_set_rgb_fg_color ( elev_speed_gc, &color);
2127
3772b8c2 2128 gdouble max_speed = widgets->max_speed * 110 / 100;
8de26632 2129
8de26632
RN
2130 for ( i = 0; i < widgets->profile_width; i++ ) {
2131 int y_speed = widgets->profile_height - (widgets->profile_height * widgets->speeds[i])/max_speed;
206157df 2132 gdk_draw_rectangle(GDK_DRAWABLE(pix), elev_speed_gc, TRUE, i+MARGIN_X-2, y_speed-2, 4, 4);
8de26632 2133 }
3772b8c2 2134
8de26632
RN
2135 g_object_unref ( G_OBJECT(elev_speed_gc) );
2136 }
2137
2138 /* draw border */
7f44287f 2139 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN_X, MARGIN_Y, widgets->profile_width-1, widgets->profile_height-1);
8de26632
RN
2140
2141 g_object_unref ( G_OBJECT(pix) );
950d8a07 2142}
7b624086
RN
2143
2144/**
2145 * Draw just the speed/distance image
2146 */
2147static void draw_sd ( GtkWidget *image, VikTrack *tr, PropWidgets *widgets)
2148{
7169b863 2149 gdouble mins;
7b624086
RN
2150 guint i;
2151
2152 // Free previous allocation
2153 if ( widgets->speeds_dist )
2154 g_free ( widgets->speeds_dist );
2155
2156 widgets->speeds_dist = vik_track_make_speed_dist_map ( tr, widgets->profile_width );
2157 if ( widgets->speeds_dist == NULL )
2158 return;
2159
2160 // Convert into appropriate units
2161 vik_units_speed_t speed_units = a_vik_get_units_speed ();
2162 switch (speed_units) {
2163 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
2164 for ( i = 0; i < widgets->profile_width; i++ ) {
2165 widgets->speeds_dist[i] = VIK_MPS_TO_KPH(widgets->speeds_dist[i]);
2166 }
2167 break;
2168 case VIK_UNITS_SPEED_MILES_PER_HOUR:
2169 for ( i = 0; i < widgets->profile_width; i++ ) {
2170 widgets->speeds_dist[i] = VIK_MPS_TO_MPH(widgets->speeds_dist[i]);
2171 }
2172 break;
2173 case VIK_UNITS_SPEED_KNOTS:
2174 for ( i = 0; i < widgets->profile_width; i++ ) {
2175 widgets->speeds_dist[i] = VIK_MPS_TO_KNOTS(widgets->speeds_dist[i]);
2176 }
2177 break;
2178 default:
2179 // VIK_UNITS_SPEED_METRES_PER_SECOND:
2180 // No need to convert as already in m/s
2181 break;
2182 }
2183
7f44287f
RN
2184 GtkWidget *window = gtk_widget_get_toplevel (widgets->speed_dist_box);
2185 GdkPixmap *pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
7b624086
RN
2186
2187 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
2188
2189 // OK to resuse min_speed here
2190 minmax_array(widgets->speeds_dist, &widgets->min_speed, &widgets->max_speed_dist, FALSE, widgets->profile_width);
2191 if (widgets->min_speed < 0.0)
2192 widgets->min_speed = 0; /* splines sometimes give negative speeds */
2193
2194 /* Find suitable chunk index */
f25c1cf8 2195 get_new_min_and_chunk_index (widgets->min_speed, widgets->max_speed_dist, chunkss, G_N_ELEMENTS(chunkss), &widgets->draw_min_speed, &widgets->cisd);
7b624086
RN
2196
2197 // Assign locally
2198 mins = widgets->draw_min_speed;
7b624086 2199
206157df
RN
2200 // Reset before redrawing
2201 clear_images (pix, window, widgets);
7b624086
RN
2202
2203 /* draw grid */
2204 for (i=0; i<=LINES; i++) {
7b624086 2205 gchar s[32];
56cb1807 2206
7b624086
RN
2207 // NB: No need to convert here anymore as numbers are in the appropriate units
2208 switch (speed_units) {
2209 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
2210 sprintf(s, "%8dkm/h", (int)(mins + (LINES-i)*chunkss[widgets->cisd]));
2211 break;
2212 case VIK_UNITS_SPEED_MILES_PER_HOUR:
2213 sprintf(s, "%8dmph", (int)(mins + (LINES-i)*chunkss[widgets->cisd]));
2214 break;
2215 case VIK_UNITS_SPEED_METRES_PER_SECOND:
2216 sprintf(s, "%8dm/s", (int)(mins + (LINES-i)*chunkss[widgets->cisd]));
2217 break;
2218 case VIK_UNITS_SPEED_KNOTS:
2219 sprintf(s, "%8dknots", (int)(mins + (LINES-i)*chunkss[widgets->cisd]));
2220 break;
2221 default:
2222 sprintf(s, "--");
2223 g_critical("Houston, we've had a problem. speed=%d", speed_units);
2224 }
2225
206157df 2226 draw_grid_y ( window, image, widgets, pix, s, i );
7b624086 2227 }
7b624086 2228
87819e20
RN
2229 draw_distance_divisions ( window, image, pix, widgets, a_vik_get_units_distance() );
2230
7b624086 2231 /* draw speeds */
7f44287f 2232 guint height = widgets->profile_height + MARGIN_Y;
7b624086 2233 for ( i = 0; i < widgets->profile_width; i++ )
ff37db21 2234 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
7f44287f 2235 i + MARGIN_X, height, i + MARGIN_X, height - widgets->profile_height*(widgets->speeds_dist[i]-mins)/(chunkss[widgets->cisd]*LINES) );
7b624086
RN
2236
2237
2238 if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->w_show_sd_gps_speed)) ) {
2239
9b082b39 2240 GdkGC *gps_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
7b624086
RN
2241 GdkColor color;
2242 gdk_color_parse ( "red", &color );
2243 gdk_gc_set_rgb_fg_color ( gps_speed_gc, &color);
2244
2245 gdouble dist = vik_track_get_length_including_gaps(tr);
2246 gdouble dist_tp = 0.0;
2247
2248 GList *iter = tr->trackpoints;
2249 for (iter = iter->next; iter; iter = iter->next) {
2250 gdouble gps_speed = VIK_TRACKPOINT(iter->data)->speed;
2251 if (isnan(gps_speed))
2252 continue;
2253 switch (speed_units) {
2254 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
2255 gps_speed = VIK_MPS_TO_KPH(gps_speed);
2256 break;
2257 case VIK_UNITS_SPEED_MILES_PER_HOUR:
2258 gps_speed = VIK_MPS_TO_MPH(gps_speed);
2259 break;
2260 case VIK_UNITS_SPEED_KNOTS:
2261 gps_speed = VIK_MPS_TO_KNOTS(gps_speed);
2262 break;
2263 default:
2264 // VIK_UNITS_SPEED_METRES_PER_SECOND:
2265 // No need to convert as already in m/s
2266 break;
2267 }
2268 dist_tp += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) );
206157df 2269 int x = MARGIN_X + (widgets->profile_width * dist_tp / dist);
7f44287f 2270 int y = height - widgets->profile_height*(gps_speed - mins)/(chunkss[widgets->cisd]*LINES);
7b624086
RN
2271 gdk_draw_rectangle(GDK_DRAWABLE(pix), gps_speed_gc, TRUE, x-2, y-2, 4, 4);
2272 }
2273 g_object_unref ( G_OBJECT(gps_speed_gc) );
2274 }
2275
2276 /* draw border */
7f44287f 2277 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN_X, MARGIN_Y, widgets->profile_width-1, widgets->profile_height-1);
7b624086
RN
2278
2279 g_object_unref ( G_OBJECT(pix) );
2280}
950d8a07
RN
2281#undef LINES
2282
950d8a07 2283/**
2f078f56 2284 * Draw all graphs
950d8a07 2285 */
2cf168ac 2286static void draw_all_graphs ( GtkWidget *widget, PropWidgets *widgets, gboolean resized )
950d8a07 2287{
950d8a07
RN
2288 // Draw graphs even if they are not visible
2289
2290 GList *child = NULL;
2f078f56
RN
2291 GtkWidget *image = NULL;
2292 GtkWidget *window = gtk_widget_get_toplevel(widget);
2293 gdouble pc = NAN;
50391e88 2294 gdouble pc_blob = NAN;
2f078f56 2295
950d8a07
RN
2296 // Draw elevations
2297 if (widgets->elev_box != NULL) {
2f078f56
RN
2298
2299 // Saved image no longer any good as we've resized, so we remove it here
2300 if (resized && widgets->elev_graph_saved_img.img) {
2301 g_object_unref(widgets->elev_graph_saved_img.img);
2302 widgets->elev_graph_saved_img.img = NULL;
2303 widgets->elev_graph_saved_img.saved = FALSE;
2304 }
2305
950d8a07 2306 child = gtk_container_get_children(GTK_CONTAINER(widgets->elev_box));
2cf168ac 2307 draw_elevations (GTK_WIDGET(child->data), widgets->tr, widgets );
2f078f56
RN
2308
2309 image = GTK_WIDGET(child->data);
950d8a07 2310 g_list_free(child);
2f078f56 2311
50391e88
RN
2312 // Ensure marker or blob are redrawn if necessary
2313 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
2f078f56 2314
2cf168ac 2315 pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
206157df 2316 gdouble x_blob = -MARGIN_X - 1.0; // i.e. Don't draw unless we get a valid value
50391e88
RN
2317 gint y_blob = 0;
2318 if (widgets->is_blob_drawn) {
2cf168ac 2319 pc_blob = tp_percentage_by_distance ( widgets->tr, widgets->blob_tp, widgets->track_length_inc_gaps );
50391e88
RN
2320 if (!isnan(pc_blob)) {
2321 x_blob = (pc_blob * widgets->profile_width);
2322 }
2323 y_blob = blobby_altitude (x_blob, widgets);
2324 }
2325
2326 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
2f078f56 2327 if (!isnan(pc)) {
206157df 2328 marker_x = (pc * widgets->profile_width) + MARGIN_X;
50391e88
RN
2329 }
2330
2331 save_image_and_draw_graph_marks (image,
2f078f56 2332 marker_x,
ff37db21 2333 gtk_widget_get_style(window)->black_gc,
206157df 2334 x_blob+MARGIN_X,
7f44287f 2335 y_blob+MARGIN_Y,
2f078f56
RN
2336 &widgets->elev_graph_saved_img,
2337 widgets->profile_width,
2338 widgets->profile_height,
50391e88
RN
2339 &widgets->is_marker_drawn,
2340 &widgets->is_blob_drawn);
2f078f56 2341 }
950d8a07
RN
2342 }
2343
0ba33e1d
FA
2344 // Draw gradients
2345 if (widgets->gradient_box != NULL) {
2346
2347 // Saved image no longer any good as we've resized, so we remove it here
2348 if (resized && widgets->gradient_graph_saved_img.img) {
2349 g_object_unref(widgets->gradient_graph_saved_img.img);
2350 widgets->gradient_graph_saved_img.img = NULL;
2351 widgets->gradient_graph_saved_img.saved = FALSE;
2352 }
2353
2354 child = gtk_container_get_children(GTK_CONTAINER(widgets->gradient_box));
2cf168ac 2355 draw_gradients (GTK_WIDGET(child->data), widgets->tr, widgets );
0ba33e1d
FA
2356
2357 image = GTK_WIDGET(child->data);
2358 g_list_free(child);
2359
2360 // Ensure marker or blob are redrawn if necessary
2361 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
2362
2cf168ac 2363 pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
206157df 2364 gdouble x_blob = -MARGIN_X - 1.0; // i.e. Don't draw unless we get a valid value
0ba33e1d
FA
2365 gint y_blob = 0;
2366 if (widgets->is_blob_drawn) {
2cf168ac 2367 pc_blob = tp_percentage_by_distance ( widgets->tr, widgets->blob_tp, widgets->track_length_inc_gaps );
0ba33e1d
FA
2368 if (!isnan(pc_blob)) {
2369 x_blob = (pc_blob * widgets->profile_width);
2370 }
2371 y_blob = blobby_gradient (x_blob, widgets);
2372 }
2373
2374 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
2375 if (!isnan(pc)) {
206157df 2376 marker_x = (pc * widgets->profile_width) + MARGIN_X;
0ba33e1d
FA
2377 }
2378
2379 save_image_and_draw_graph_marks (image,
2380 marker_x,
ff37db21 2381 gtk_widget_get_style(window)->black_gc,
206157df 2382 x_blob+MARGIN_X,
7f44287f 2383 y_blob+MARGIN_Y,
0ba33e1d
FA
2384 &widgets->gradient_graph_saved_img,
2385 widgets->profile_width,
2386 widgets->profile_height,
2387 &widgets->is_marker_drawn,
2388 &widgets->is_blob_drawn);
2389 }
2390 }
2391
950d8a07
RN
2392 // Draw speeds
2393 if (widgets->speed_box != NULL) {
2f078f56
RN
2394
2395 // Saved image no longer any good as we've resized
2396 if (resized && widgets->speed_graph_saved_img.img) {
2397 g_object_unref(widgets->speed_graph_saved_img.img);
2398 widgets->speed_graph_saved_img.img = NULL;
2399 widgets->speed_graph_saved_img.saved = FALSE;
2400 }
2401
950d8a07 2402 child = gtk_container_get_children(GTK_CONTAINER(widgets->speed_box));
2cf168ac 2403 draw_vt (GTK_WIDGET(child->data), widgets->tr, widgets );
2f078f56
RN
2404
2405 image = GTK_WIDGET(child->data);
950d8a07 2406 g_list_free(child);
2f078f56 2407
50391e88
RN
2408 // Ensure marker or blob are redrawn if necessary
2409 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
2f078f56 2410
2cf168ac 2411 pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
2f078f56 2412
206157df 2413 gdouble x_blob = -MARGIN_X - 1.0; // i.e. Don't draw unless we get a valid value
50391e88
RN
2414 gint y_blob = 0;
2415 if (widgets->is_blob_drawn) {
2cf168ac 2416 pc_blob = tp_percentage_by_time ( widgets->tr, widgets->blob_tp );
50391e88
RN
2417 if (!isnan(pc_blob)) {
2418 x_blob = (pc_blob * widgets->profile_width);
2419 }
2420
2421 y_blob = blobby_speed (x_blob, widgets);
2422 }
2423
2424 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
2f078f56 2425 if (!isnan(pc)) {
206157df 2426 marker_x = (pc * widgets->profile_width) + MARGIN_X;
50391e88
RN
2427 }
2428
2429 save_image_and_draw_graph_marks (image,
2f078f56 2430 marker_x,
ff37db21 2431 gtk_widget_get_style(window)->black_gc,
206157df 2432 x_blob+MARGIN_X,
7f44287f 2433 y_blob+MARGIN_Y,
2f078f56
RN
2434 &widgets->speed_graph_saved_img,
2435 widgets->profile_width,
2436 widgets->profile_height,
50391e88
RN
2437 &widgets->is_marker_drawn,
2438 &widgets->is_blob_drawn);
2f078f56
RN
2439 }
2440 }
926c8140
RN
2441
2442 // Draw Distances
2443 if (widgets->dist_box != NULL) {
2444
2445 // Saved image no longer any good as we've resized
2446 if (resized && widgets->dist_graph_saved_img.img) {
2447 g_object_unref(widgets->dist_graph_saved_img.img);
2448 widgets->dist_graph_saved_img.img = NULL;
2449 widgets->dist_graph_saved_img.saved = FALSE;
2450 }
2451
2452 child = gtk_container_get_children(GTK_CONTAINER(widgets->dist_box));
2cf168ac 2453 draw_dt (GTK_WIDGET(child->data), widgets->tr, widgets );
926c8140
RN
2454
2455 image = GTK_WIDGET(child->data);
2456 g_list_free(child);
2457
2458 // Ensure marker or blob are redrawn if necessary
2459 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
2460
2cf168ac 2461 pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
926c8140 2462
206157df 2463 gdouble x_blob = -MARGIN_X - 1.0; // i.e. Don't draw unless we get a valid value
926c8140
RN
2464 gint y_blob = 0;
2465 if (widgets->is_blob_drawn) {
2cf168ac 2466 pc_blob = tp_percentage_by_time ( widgets->tr, widgets->blob_tp );
926c8140
RN
2467 if (!isnan(pc_blob)) {
2468 x_blob = (pc_blob * widgets->profile_width);
2469 }
2470
2471 y_blob = blobby_distance (x_blob, widgets);
2472 }
2473
2474 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
2475 if (!isnan(pc)) {
206157df 2476 marker_x = (pc * widgets->profile_width) + MARGIN_X;
926c8140
RN
2477 }
2478
2479 save_image_and_draw_graph_marks (image,
2480 marker_x,
ff37db21 2481 gtk_widget_get_style(window)->black_gc,
206157df 2482 x_blob+MARGIN_X,
7f44287f 2483 y_blob+MARGIN_Y,
926c8140
RN
2484 &widgets->dist_graph_saved_img,
2485 widgets->profile_width,
2486 widgets->profile_height,
2487 &widgets->is_marker_drawn,
2488 &widgets->is_blob_drawn);
2489 }
2490 }
2491
8de26632
RN
2492 // Draw Elevations in timely manner
2493 if (widgets->elev_time_box != NULL) {
2494
2495 // Saved image no longer any good as we've resized
2496 if (resized && widgets->elev_time_graph_saved_img.img) {
2497 g_object_unref(widgets->elev_time_graph_saved_img.img);
2498 widgets->elev_time_graph_saved_img.img = NULL;
2499 widgets->elev_time_graph_saved_img.saved = FALSE;
2500 }
2501
2502 child = gtk_container_get_children(GTK_CONTAINER(widgets->elev_time_box));
2cf168ac 2503 draw_et (GTK_WIDGET(child->data), widgets->tr, widgets );
8de26632
RN
2504
2505 image = GTK_WIDGET(child->data);
2506 g_list_free(child);
2507
2508 // Ensure marker or blob are redrawn if necessary
2509 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
2510
2cf168ac 2511 pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
8de26632 2512
206157df 2513 gdouble x_blob = -MARGIN_X - 1.0; // i.e. Don't draw unless we get a valid value
8de26632
RN
2514 gint y_blob = 0;
2515 if (widgets->is_blob_drawn) {
2cf168ac 2516 pc_blob = tp_percentage_by_time ( widgets->tr, widgets->blob_tp );
8de26632
RN
2517 if (!isnan(pc_blob)) {
2518 x_blob = (pc_blob * widgets->profile_width);
2519 }
2520 y_blob = blobby_altitude_time (x_blob, widgets);
2521 }
2522
2523 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
2524 if (!isnan(pc)) {
206157df 2525 marker_x = (pc * widgets->profile_width) + MARGIN_X;
8de26632
RN
2526 }
2527
2528 save_image_and_draw_graph_marks (image,
2529 marker_x,
ff37db21 2530 gtk_widget_get_style(window)->black_gc,
206157df 2531 x_blob+MARGIN_X,
7f44287f 2532 y_blob+MARGIN_Y,
8de26632
RN
2533 &widgets->elev_time_graph_saved_img,
2534 widgets->profile_width,
2535 widgets->profile_height,
2536 &widgets->is_marker_drawn,
2537 &widgets->is_blob_drawn);
2538 }
2539 }
2540
7b624086
RN
2541 // Draw speed distances
2542 if (widgets->speed_dist_box != NULL) {
2543
2544 // Saved image no longer any good as we've resized, so we remove it here
2545 if (resized && widgets->speed_dist_graph_saved_img.img) {
2546 g_object_unref(widgets->speed_dist_graph_saved_img.img);
2547 widgets->speed_dist_graph_saved_img.img = NULL;
2548 widgets->speed_dist_graph_saved_img.saved = FALSE;
2549 }
2550
2551 child = gtk_container_get_children(GTK_CONTAINER(widgets->speed_dist_box));
2cf168ac 2552 draw_sd (GTK_WIDGET(child->data), widgets->tr, widgets );
7b624086
RN
2553
2554 image = GTK_WIDGET(child->data);
2555 g_list_free(child);
2556
2557 // Ensure marker or blob are redrawn if necessary
2558 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
2559
2cf168ac 2560 pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
206157df 2561 gdouble x_blob = -MARGIN_X - 1.0; // i.e. Don't draw unless we get a valid value
7b624086
RN
2562 gint y_blob = 0;
2563 if (widgets->is_blob_drawn) {
2cf168ac 2564 pc_blob = tp_percentage_by_distance ( widgets->tr, widgets->blob_tp, widgets->track_length_inc_gaps );
7b624086
RN
2565 if (!isnan(pc_blob)) {
2566 x_blob = (pc_blob * widgets->profile_width);
2567 }
2568 y_blob = blobby_speed_dist (x_blob, widgets);
2569 }
2570
2571 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
2572 if (!isnan(pc)) {
206157df 2573 marker_x = (pc * widgets->profile_width) + MARGIN_X;
7b624086
RN
2574 }
2575
2576 save_image_and_draw_graph_marks (image,
2577 marker_x,
ff37db21 2578 gtk_widget_get_style(window)->black_gc,
206157df 2579 x_blob+MARGIN_X,
7f44287f 2580 y_blob+MARGIN_Y,
7b624086
RN
2581 &widgets->speed_dist_graph_saved_img,
2582 widgets->profile_width,
2583 widgets->profile_height,
2584 &widgets->is_marker_drawn,
2585 &widgets->is_blob_drawn);
2586 }
2587 }
2588
2f078f56
RN
2589}
2590
2591/**
2592 * Configure/Resize the profile & speed/time images
2593 */
2cf168ac 2594static gboolean configure_event ( GtkWidget *widget, GdkEventConfigure *event, PropWidgets *widgets )
2f078f56 2595{
2f078f56
RN
2596 if (widgets->configure_dialog) {
2597 // Determine size offsets between dialog size and size for images
2598 // Only on the initialisation of the dialog
2599 widgets->profile_width_offset = event->width - widgets->profile_width;
2600 widgets->profile_height_offset = event->height - widgets->profile_height;
2601 widgets->configure_dialog = FALSE;
2602
2603 // Without this the settting, the dialog will only grow in vertical size - one can not then make it smaller!
d45ac152 2604 gtk_widget_set_size_request ( widget, widgets->profile_width+widgets->profile_width_offset, widgets->profile_height+widgets->profile_height_offset );
972fc895
RN
2605
2606 // Allow resizing back down to a minimal size (especially useful if the initial size has been made bigger after restoring from the saved settings)
2607 GdkGeometry geom = { 600+widgets->profile_width_offset, 300+widgets->profile_height_offset, 0, 0, 0, 0, 0, 0, 0, 0, GDK_GRAVITY_STATIC };
2608 gdk_window_set_geometry_hints ( gtk_widget_get_window(widget), &geom, GDK_HINT_MIN_SIZE );
950d8a07 2609 }
2f078f56
RN
2610 else {
2611 widgets->profile_width_old = widgets->profile_width;
2612 widgets->profile_height_old = widgets->profile_height;
2613 }
2614
2615 // Now adjust From Dialog size to get image size
2616 widgets->profile_width = event->width - widgets->profile_width_offset;
2617 widgets->profile_height = event->height - widgets->profile_height_offset;
2618
2619 // ATM we receive configure_events when the dialog is moved and so no further action is necessary
2620 if ( !widgets->configure_dialog &&
2621 (widgets->profile_width_old == widgets->profile_width) && (widgets->profile_height_old == widgets->profile_height) )
2622 return FALSE;
2623
2624 // Draw stuff
2cf168ac 2625 draw_all_graphs ( widget, widgets, TRUE );
2f078f56 2626
950d8a07
RN
2627 return FALSE;
2628}
2629
2630/**
2631 * Create height profile widgets including the image and callbacks
2632 */
2cf168ac 2633GtkWidget *vik_trw_layer_create_profile ( GtkWidget *window, PropWidgets *widgets, gdouble *min_alt, gdouble *max_alt)
950d8a07
RN
2634{
2635 GdkPixmap *pix;
2636 GtkWidget *image;
950d8a07 2637 GtkWidget *eventbox;
950d8a07 2638
0b2bfa08 2639 // First allocation
2cf168ac 2640 widgets->altitudes = vik_track_make_elevation_map ( widgets->tr, widgets->profile_width );
0b2bfa08
RN
2641
2642 if ( widgets->altitudes == NULL ) {
950d8a07
RN
2643 *min_alt = *max_alt = VIK_DEFAULT_ALTITUDE;
2644 return NULL;
2645 }
2646
b66b0b38 2647 minmax_array(widgets->altitudes, min_alt, max_alt, TRUE, widgets->profile_width);
950d8a07 2648
7f44287f 2649 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
950d8a07
RN
2650 image = gtk_image_new_from_pixmap ( pix, NULL );
2651
950d8a07
RN
2652 g_object_unref ( G_OBJECT(pix) );
2653
950d8a07 2654 eventbox = gtk_event_box_new ();
2cf168ac
RN
2655 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_profile_click), widgets );
2656 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_profile_move), widgets );
950d8a07
RN
2657 gtk_container_add ( GTK_CONTAINER(eventbox), image );
2658 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_STRUCTURE_MASK);
2659
2660 return eventbox;
2661}
2662
0ba33e1d
FA
2663/**
2664 * Create height profile widgets including the image and callbacks
2665 */
2cf168ac 2666GtkWidget *vik_trw_layer_create_gradient ( GtkWidget *window, PropWidgets *widgets)
0ba33e1d
FA
2667{
2668 GdkPixmap *pix;
2669 GtkWidget *image;
2670 GtkWidget *eventbox;
0ba33e1d
FA
2671
2672 // First allocation
2cf168ac 2673 widgets->gradients = vik_track_make_gradient_map ( widgets->tr, widgets->profile_width );
0ba33e1d
FA
2674
2675 if ( widgets->gradients == NULL ) {
2676 return NULL;
2677 }
2678
7f44287f 2679 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
0ba33e1d
FA
2680 image = gtk_image_new_from_pixmap ( pix, NULL );
2681
2682 g_object_unref ( G_OBJECT(pix) );
2683
0ba33e1d 2684 eventbox = gtk_event_box_new ();
2cf168ac
RN
2685 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_gradient_click), widgets );
2686 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_gradient_move), widgets );
0ba33e1d
FA
2687 gtk_container_add ( GTK_CONTAINER(eventbox), image );
2688 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_STRUCTURE_MASK);
2689
2690 return eventbox;
2691}
2692
950d8a07
RN
2693/**
2694 * Create speed/time widgets including the image and callbacks
2695 */
2cf168ac 2696GtkWidget *vik_trw_layer_create_vtdiag ( GtkWidget *window, PropWidgets *widgets)
950d8a07
RN
2697{
2698 GdkPixmap *pix;
2699 GtkWidget *image;
2700 GtkWidget *eventbox;
950d8a07 2701
0b2bfa08 2702 // First allocation
2cf168ac 2703 widgets->speeds = vik_track_make_speed_map ( widgets->tr, widgets->profile_width );
0b2bfa08 2704 if ( widgets->speeds == NULL )
950d8a07
RN
2705 return NULL;
2706
7f44287f 2707 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
950d8a07
RN
2708 image = gtk_image_new_from_pixmap ( pix, NULL );
2709
2710#if 0
2711 /* XXX this can go out, it's just a helpful dev tool */
2712 {
2713 int j;
ff37db21
RN
2714 GdkGC **colors[8] = { gtk_widget_get_style(window)->bg_gc,
2715 gtk_widget_get_style(window)->fg_gc,
2716 gtk_widget_get_style(window)->light_gc,
2717 gtk_widget_get_style(window)->dark_gc,
2718 gtk_widget_get_style(window)->mid_gc,
2719 gtk_widget_get_style(window)->text_gc,
2720 gtk_widget_get_style(window)->base_gc,
2721 gtk_widget_get_style(window)->text_aa_gc };
950d8a07
RN
2722 for (i=0; i<5; i++) {
2723 for (j=0; j<8; j++) {
2724 gdk_draw_rectangle(GDK_DRAWABLE(pix), colors[j][i],
2725 TRUE, i*20, j*20, 20, 20);
ff37db21 2726 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc,
950d8a07
RN
2727 FALSE, i*20, j*20, 20, 20);
2728 }
2729 }
2730 }
2731#endif
2732
950d8a07
RN
2733 g_object_unref ( G_OBJECT(pix) );
2734
561e6ad0 2735 eventbox = gtk_event_box_new ();
2cf168ac
RN
2736 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_vt_click), widgets );
2737 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_vt_move), widgets );
561e6ad0 2738 gtk_container_add ( GTK_CONTAINER(eventbox), image );
950d8a07 2739 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
561e6ad0
EB
2740
2741 return eventbox;
25e44eac 2742}
926c8140
RN
2743
2744/**
2745 * Create distance / time widgets including the image and callbacks
2746 */
2cf168ac 2747GtkWidget *vik_trw_layer_create_dtdiag ( GtkWidget *window, PropWidgets *widgets)
926c8140
RN
2748{
2749 GdkPixmap *pix;
2750 GtkWidget *image;
2751 GtkWidget *eventbox;
926c8140
RN
2752
2753 // First allocation
2cf168ac 2754 widgets->distances = vik_track_make_distance_map ( widgets->tr, widgets->profile_width );
926c8140
RN
2755 if ( widgets->distances == NULL )
2756 return NULL;
2757
7f44287f 2758 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
926c8140
RN
2759 image = gtk_image_new_from_pixmap ( pix, NULL );
2760
2761 g_object_unref ( G_OBJECT(pix) );
2762
2763 eventbox = gtk_event_box_new ();
2cf168ac
RN
2764 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_dt_click), widgets );
2765 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_dt_move), widgets );
2766 //g_signal_connect_swapped ( G_OBJECT(eventbox), "destroy", G_CALLBACK(g_free), widgets );
926c8140
RN
2767 gtk_container_add ( GTK_CONTAINER(eventbox), image );
2768 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
2769
2770 return eventbox;
2771}
8de26632
RN
2772
2773/**
2774 * Create elevation / time widgets including the image and callbacks
2775 */
2cf168ac 2776GtkWidget *vik_trw_layer_create_etdiag ( GtkWidget *window, PropWidgets *widgets)
8de26632
RN
2777{
2778 GdkPixmap *pix;
2779 GtkWidget *image;
2780 GtkWidget *eventbox;
8de26632
RN
2781
2782 // First allocation
2cf168ac 2783 widgets->ats = vik_track_make_elevation_time_map ( widgets->tr, widgets->profile_width );
8de26632
RN
2784 if ( widgets->ats == NULL )
2785 return NULL;
2786
7f44287f 2787 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
8de26632
RN
2788 image = gtk_image_new_from_pixmap ( pix, NULL );
2789
2790 g_object_unref ( G_OBJECT(pix) );
2791
2792 eventbox = gtk_event_box_new ();
2cf168ac
RN
2793 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_et_click), widgets );
2794 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_et_move), widgets );
8de26632
RN
2795 gtk_container_add ( GTK_CONTAINER(eventbox), image );
2796 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
2797
2798 return eventbox;
2799}
7b624086
RN
2800
2801/**
2802 * Create speed/distance widgets including the image and callbacks
2803 */
2cf168ac 2804GtkWidget *vik_trw_layer_create_sddiag ( GtkWidget *window, PropWidgets *widgets)
7b624086
RN
2805{
2806 GdkPixmap *pix;
2807 GtkWidget *image;
2808 GtkWidget *eventbox;
7b624086
RN
2809
2810 // First allocation
2cf168ac 2811 widgets->speeds_dist = vik_track_make_speed_dist_map ( widgets->tr, widgets->profile_width );
7b624086
RN
2812 if ( widgets->speeds_dist == NULL )
2813 return NULL;
2814
7f44287f 2815 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
7b624086
RN
2816 image = gtk_image_new_from_pixmap ( pix, NULL );
2817
2818 g_object_unref ( G_OBJECT(pix) );
2819
2820 eventbox = gtk_event_box_new ();
2cf168ac
RN
2821 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_sd_click), widgets );
2822 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_sd_move), widgets );
7b624086
RN
2823 gtk_container_add ( GTK_CONTAINER(eventbox), image );
2824 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
2825
2826 return eventbox;
2827}
206157df 2828#undef MARGIN_X
50a14534 2829
972fc895
RN
2830#define VIK_SETTINGS_TRACK_PROFILE_WIDTH "track_profile_display_width"
2831#define VIK_SETTINGS_TRACK_PROFILE_HEIGHT "track_profile_display_height"
2832
2833static void save_values ( PropWidgets *widgets )
2834{
932b4a7a 2835 // Session settings
972fc895
RN
2836 a_settings_set_integer ( VIK_SETTINGS_TRACK_PROFILE_WIDTH, widgets->profile_width );
2837 a_settings_set_integer ( VIK_SETTINGS_TRACK_PROFILE_HEIGHT, widgets->profile_height );
932b4a7a
RN
2838
2839 // Just for this session ATM
2840 show_dem = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->w_show_dem) );
2841 show_alt_gps_speed = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->w_show_alt_gps_speed) );
2842 show_gps_speed = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->w_show_gps_speed) );
2843 show_gradient_gps_speed = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->w_show_gradient_gps_speed) );
2844 show_dist_speed = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->w_show_dist_speed) );
2845 show_elev_dem = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->w_show_elev_dem) );
2846 show_elev_speed = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->w_show_elev_speed) );
2847 show_sd_gps_speed = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->w_show_sd_gps_speed) );
972fc895
RN
2848}
2849
2cf168ac
RN
2850static void destroy_cb ( GtkDialog *dialog, PropWidgets *widgets )
2851{
972fc895 2852 save_values(widgets);
2cf168ac
RN
2853 prop_widgets_free(widgets);
2854}
2855
2856static void propwin_response_cb( GtkDialog *dialog, gint resp, PropWidgets *widgets )
50a14534 2857{
21700912
QT
2858 VikTrack *tr = widgets->tr;
2859 VikTrwLayer *vtl = widgets->vtl;
8dcb3ba4 2860 gboolean keep_dialog = FALSE;
21700912
QT
2861
2862 /* FIXME: check and make sure the track still exists before doing anything to it */
2863 /* Note: destroying diaglog (eg, parent window exit) won't give "response" */
2864 switch (resp) {
2865 case GTK_RESPONSE_DELETE_EVENT: /* received delete event (not from buttons) */
2866 case GTK_RESPONSE_REJECT:
2867 break;
2868 case GTK_RESPONSE_ACCEPT:
2869 vik_track_set_comment(tr, gtk_entry_get_text(GTK_ENTRY(widgets->w_comment)));
6b2f262e 2870 vik_track_set_description(tr, gtk_entry_get_text(GTK_ENTRY(widgets->w_description)));
b1453c16 2871 gtk_color_button_get_color ( GTK_COLOR_BUTTON(widgets->w_color), &(tr->color) );
387ff7ac
RN
2872 tr->draw_name_mode = gtk_combo_box_get_active ( GTK_COMBO_BOX(widgets->w_namelabel) );
2873 tr->max_number_dist_labels = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(widgets->w_number_distlabels) );
93ee73b3 2874 trw_layer_update_treeview ( widgets->vtl, widgets->tr );
da121f9b 2875 vik_layer_emit_update ( VIK_LAYER(vtl) );
21700912
QT
2876 break;
2877 case VIK_TRW_LAYER_PROPWIN_REVERSE:
2878 vik_track_reverse(tr);
da121f9b 2879 vik_layer_emit_update ( VIK_LAYER(vtl) );
21700912
QT
2880 break;
2881 case VIK_TRW_LAYER_PROPWIN_DEL_DUP:
4f48c541
RN
2882 vik_track_remove_dup_points(tr); // NB ignore the returned answer
2883 // As we could have seen the nuber of dulplicates that would be deleted in the properties statistics tab,
2884 // choose not to inform the user unnecessarily
2885
21700912 2886 /* above operation could have deleted current_tp or last_tp */
ce4bd1cf 2887 trw_layer_cancel_tps_of_track ( vtl, tr );
da121f9b 2888 vik_layer_emit_update ( VIK_LAYER(vtl) );
21700912
QT
2889 break;
2890 case VIK_TRW_LAYER_PROPWIN_SPLIT:
2891 {
9914238e 2892 /* get new tracks, add them and then the delete old one. old can still exist on clipboard. */
21700912 2893 guint ntracks;
ce4bd1cf 2894
21700912
QT
2895 VikTrack **tracks = vik_track_split_into_segments(tr, &ntracks);
2896 gchar *new_tr_name;
2897 guint i;
2898 for ( i = 0; i < ntracks; i++ )
2899 {
9914238e 2900 if ( tracks[i] ) {
0d2b891f
RN
2901 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl,
2902 widgets->tr->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
2903 widgets->tr->name);
2904 if ( widgets->tr->is_route )
2905 vik_trw_layer_add_route ( vtl, new_tr_name, tracks[i] );
2906 else
2907 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
ab5392de 2908 vik_track_calculate_bounds ( tracks[i] );
1613e468
RN
2909
2910 g_free ( new_tr_name );
9914238e 2911 }
21700912
QT
2912 }
2913 if ( tracks )
2914 {
2915 g_free ( tracks );
2916 /* Don't let track destroy this dialog */
2917 vik_track_clear_property_dialog(tr);
0d2b891f
RN
2918 if ( widgets->tr->is_route )
2919 vik_trw_layer_delete_route ( vtl, tr );
2920 else
2921 vik_trw_layer_delete_track ( vtl, tr );
da121f9b 2922 vik_layer_emit_update ( VIK_LAYER(vtl) ); /* chase thru the hoops */
21700912
QT
2923 }
2924 }
2925 break;
8dcb3ba4
QT
2926 case VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER:
2927 {
2928 GList *iter = tr->trackpoints;
2929 while ((iter = iter->next)) {
2930 if (widgets->marker_tp == VIK_TRACKPOINT(iter->data))
2931 break;
2932 }
2933 if (iter == NULL) {
2934 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR,
2935 _("Failed spliting track. Track unchanged"), NULL);
2936 keep_dialog = TRUE;
2937 break;
2938 }
2939
0d2b891f
RN
2940 gchar *r_name = trw_layer_new_unique_sublayer_name(vtl,
2941 widgets->tr->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
2942 widgets->tr->name);
8dcb3ba4
QT
2943 iter->prev->next = NULL;
2944 iter->prev = NULL;
2945 VikTrack *tr_right = vik_track_new();
2946 if ( tr->comment )
2947 vik_track_set_comment ( tr_right, tr->comment );
2948 tr_right->visible = tr->visible;
0d2b891f 2949 tr_right->is_route = tr->is_route;
8dcb3ba4
QT
2950 tr_right->trackpoints = iter;
2951
0d2b891f
RN
2952 if ( widgets->tr->is_route )
2953 vik_trw_layer_add_route(vtl, r_name, tr_right);
2954 else
2955 vik_trw_layer_add_track(vtl, r_name, tr_right);
86e57c5b 2956 vik_track_calculate_bounds ( tr );
ab5392de 2957 vik_track_calculate_bounds ( tr_right );
1613e468
RN
2958
2959 g_free ( r_name );
2960
da121f9b 2961 vik_layer_emit_update ( VIK_LAYER(vtl) );
8dcb3ba4
QT
2962 }
2963 break;
21700912
QT
2964 default:
2965 fprintf(stderr, "DEBUG: unknown response\n");
2966 return;
2967 }
2968
2969 /* Keep same behaviour for now: destroy dialog if click on any button */
8dcb3ba4 2970 if (!keep_dialog) {
8dcb3ba4
QT
2971 vik_track_clear_property_dialog(tr);
2972 gtk_widget_destroy ( GTK_WIDGET(dialog) );
2973 }
21700912
QT
2974}
2975
ec32b567
RN
2976/**
2977 * Force a redraw when checkbutton has been toggled to show/hide that information
2978 */
2cf168ac 2979static void checkbutton_toggle_cb ( GtkToggleButton *togglebutton, PropWidgets *widgets, gpointer dummy )
ec32b567 2980{
ec32b567
RN
2981 // Even though not resized, we'll pretend it is -
2982 // as this invalidates the saved images (since the image may have changed)
2cf168ac 2983 draw_all_graphs ( widgets->dialog, widgets, TRUE );
ec32b567
RN
2984}
2985
780d3771
RN
2986/**
2987 * Create the widgets for the given graph tab
2988 */
065f60f1
GB
2989static GtkWidget *create_graph_page ( GtkWidget *graph,
2990 const gchar *markup,
780d3771
RN
2991 GtkWidget *value,
2992 const gchar *markup2,
ec32b567 2993 GtkWidget *value2,
1b40910b
RN
2994 const gchar *markup3,
2995 GtkWidget *value3,
ec32b567
RN
2996 GtkWidget *checkbutton1,
2997 gboolean checkbutton1_default,
2998 GtkWidget *checkbutton2,
2999 gboolean checkbutton2_default )
065f60f1
GB
3000{
3001 GtkWidget *hbox = gtk_hbox_new ( FALSE, 10 );
3002 GtkWidget *vbox = gtk_vbox_new ( FALSE, 10 );
3003 GtkWidget *label = gtk_label_new (NULL);
780d3771 3004 GtkWidget *label2 = gtk_label_new (NULL);
1b40910b 3005 GtkWidget *label3 = gtk_label_new (NULL);
065f60f1
GB
3006 gtk_box_pack_start (GTK_BOX(vbox), graph, FALSE, FALSE, 0);
3007 gtk_label_set_markup ( GTK_LABEL(label), markup );
780d3771 3008 gtk_label_set_markup ( GTK_LABEL(label2), markup2 );
1b40910b 3009 gtk_label_set_markup ( GTK_LABEL(label3), markup3 );
065f60f1
GB
3010 gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0);
3011 gtk_box_pack_start (GTK_BOX(hbox), value, FALSE, FALSE, 0);
780d3771
RN
3012 gtk_box_pack_start (GTK_BOX(hbox), label2, FALSE, FALSE, 0);
3013 gtk_box_pack_start (GTK_BOX(hbox), value2, FALSE, FALSE, 0);
1b40910b
RN
3014 if ( value3 ) {
3015 gtk_box_pack_start (GTK_BOX(hbox), label3, FALSE, FALSE, 0);
3016 gtk_box_pack_start (GTK_BOX(hbox), value3, FALSE, FALSE, 0);
3017 }
ec32b567
RN
3018 if (checkbutton2) {
3019 gtk_box_pack_end (GTK_BOX(hbox), checkbutton2, FALSE, FALSE, 0);
3020 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbutton2), checkbutton2_default);
3021 }
3022 if (checkbutton1) {
3023 gtk_box_pack_end (GTK_BOX(hbox), checkbutton1, FALSE, FALSE, 0);
3024 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbutton1), checkbutton1_default);
3025 }
065f60f1 3026 gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
ec32b567 3027
065f60f1
GB
3028 return vbox;
3029}
3030
7cdf9f1e
RN
3031static GtkWidget *create_table (int cnt, char *labels[], GtkWidget *contents[])
3032{
3033 GtkTable *table;
3034 int i;
3035
3036 table = GTK_TABLE(gtk_table_new (cnt, 2, FALSE));
3037 gtk_table_set_col_spacing (table, 0, 10);
3038 for (i=0; i<cnt; i++) {
3039 GtkWidget *label;
3040
3041 // Settings so the text positioning only moves around vertically when the dialog is resized
3042 // This also gives more room to see the track comment
3043 label = gtk_label_new(NULL);
3044 gtk_misc_set_alignment ( GTK_MISC(label), 1, 0.5 ); // Position text centrally in vertical plane
3045 gtk_label_set_markup ( GTK_LABEL(label), _(labels[i]) );
3046 gtk_table_attach ( table, label, 0, 1, i, i+1, GTK_FILL, GTK_SHRINK, 0, 0 );
3047 if (GTK_IS_MISC(contents[i])) {
3048 gtk_misc_set_alignment ( GTK_MISC(contents[i]), 0, 0.5 );
3049 }
3050 if ( GTK_IS_COLOR_BUTTON(contents[i]) || GTK_IS_COMBO_BOX(contents[i]) )
3051 // Buttons compressed - otherwise look weird (to me) if vertically massive
3052 gtk_table_attach ( table, contents[i], 1, 2, i, i+1, GTK_FILL, GTK_SHRINK, 0, 5 );
3053 else
3054 // Expand for comments + descriptions / labels
3055 gtk_table_attach_defaults ( table, contents[i], 1, 2, i, i+1 );
3056 }
3057
3058 return GTK_WIDGET (table);
3059}
3060
b1453c16
RN
3061void vik_trw_layer_propwin_run ( GtkWindow *parent,
3062 VikTrwLayer *vtl,
3063 VikTrack *tr,
3064 gpointer vlp,
3065 VikViewport *vvp,
626de648 3066 gboolean start_on_stats )
21700912 3067{
ca7e67ef 3068 PropWidgets *widgets = prop_widgets_new();
21700912 3069 widgets->vtl = vtl;
2cf168ac
RN
3070 widgets->vvp = vvp;
3071 widgets->vlp = vlp;
21700912 3072 widgets->tr = tr;
972fc895
RN
3073
3074 gint profile_size_value;
3075 // Ensure minimum values
3076 widgets->profile_width = 600;
3077 if ( a_settings_get_integer ( VIK_SETTINGS_TRACK_PROFILE_WIDTH, &profile_size_value ) )
3078 if ( profile_size_value > widgets->profile_width )
3079 widgets->profile_width = profile_size_value;
3080
3081 widgets->profile_height = 300;
3082 if ( a_settings_get_integer ( VIK_SETTINGS_TRACK_PROFILE_HEIGHT, &profile_size_value ) )
3083 if ( profile_size_value > widgets->profile_height )
3084 widgets->profile_height = profile_size_value;
3085
ce4bd1cf 3086 gchar *title = g_strdup_printf(_("%s - Track Properties"), tr->name);
dc27aba1 3087 GtkWidget *dialog = gtk_dialog_new_with_buttons (title,
21700912
QT
3088 parent,
3089 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
3090 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
c51f24b1
RN
3091 _("Split at _Marker"), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER,
3092 _("Split _Segments"), VIK_TRW_LAYER_PROPWIN_SPLIT,
3093 _("_Reverse"), VIK_TRW_LAYER_PROPWIN_REVERSE,
3094 _("_Delete Dupl."), VIK_TRW_LAYER_PROPWIN_DEL_DUP,
21700912
QT
3095 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
3096 NULL);
8dcb3ba4 3097 widgets->dialog = dialog;
2cf168ac
RN
3098 g_signal_connect( G_OBJECT(dialog), "response", G_CALLBACK(propwin_response_cb), widgets);
3099
dc27aba1 3100 g_free(title);
7cdf9f1e 3101 GtkWidget *table;
50a14534 3102 gdouble tr_len;
901fd9e2
RN
3103 gulong tp_count;
3104 guint seg_count;
50a14534
EB
3105
3106 gdouble min_alt, max_alt;
2cf168ac
RN
3107 widgets->elev_box = vik_trw_layer_create_profile(GTK_WIDGET(parent), widgets, &min_alt, &max_alt);
3108 widgets->gradient_box = vik_trw_layer_create_gradient(GTK_WIDGET(parent), widgets);
3109 widgets->speed_box = vik_trw_layer_create_vtdiag(GTK_WIDGET(parent), widgets);
3110 widgets->dist_box = vik_trw_layer_create_dtdiag(GTK_WIDGET(parent), widgets);
3111 widgets->elev_time_box = vik_trw_layer_create_etdiag(GTK_WIDGET(parent), widgets);
3112 widgets->speed_dist_box = vik_trw_layer_create_sddiag(GTK_WIDGET(parent), widgets);
25e44eac 3113 GtkWidget *graphs = gtk_notebook_new();
50a14534 3114
7cdf9f1e
RN
3115 GtkWidget *content_prop[20];
3116 int cnt_prop = 0;
4b00e581 3117
6b2f262e
RN
3118 static gchar *label_texts[] = {
3119 N_("<b>Comment:</b>"),
3120 N_("<b>Description:</b>"),
7cdf9f1e
RN
3121 N_("<b>Color:</b>"),
3122 N_("<b>Draw Name:</b>"),
3123 N_("<b>Distance Labels:</b>"),
3124 };
3125 static gchar *stats_texts[] = {
6b2f262e
RN
3126 N_("<b>Track Length:</b>"),
3127 N_("<b>Trackpoints:</b>"),
3128 N_("<b>Segments:</b>"),
3129 N_("<b>Duplicate Points:</b>"),
3130 N_("<b>Max Speed:</b>"),
3131 N_("<b>Avg. Speed:</b>"),
3132 N_("<b>Moving Avg. Speed:</b>"),
3133 N_("<b>Avg. Dist. Between TPs:</b>"),
3134 N_("<b>Elevation Range:</b>"),
3135 N_("<b>Total Elevation Gain/Loss:</b>"),
3136 N_("<b>Start:</b>"),
3137 N_("<b>End:</b>"),
b1453c16 3138 N_("<b>Duration:</b>"),
387ff7ac 3139 };
32e48121 3140 static gchar tmp_buf[50];
8c4f1350 3141 gdouble tmp_speed;
50a14534 3142
7cdf9f1e 3143 // Properties
21700912 3144 widgets->w_comment = gtk_entry_new ();
50a14534 3145 if ( tr->comment )
21700912
QT
3146 gtk_entry_set_text ( GTK_ENTRY(widgets->w_comment), tr->comment );
3147 g_signal_connect_swapped ( widgets->w_comment, "activate", G_CALLBACK(a_dialog_response_accept), GTK_DIALOG(dialog) );
7cdf9f1e 3148 content_prop[cnt_prop++] = widgets->w_comment;
50a14534 3149
6b2f262e
RN
3150 widgets->w_description = gtk_entry_new ();
3151 if ( tr->description )
3152 gtk_entry_set_text ( GTK_ENTRY(widgets->w_description), tr->description );
3153 g_signal_connect_swapped ( widgets->w_description, "activate", G_CALLBACK(a_dialog_response_accept), GTK_DIALOG(dialog) );
7cdf9f1e
RN
3154 content_prop[cnt_prop++] = widgets->w_description;
3155
3156 widgets->w_color = content_prop[cnt_prop++] = gtk_color_button_new_with_color ( &(tr->color) );
3157
3158 static gchar *draw_name_labels[] = {
3159 N_("No"),
3160 N_("Centre"),
3161 N_("Start only"),
3162 N_("End only"),
3163 N_("Start and End"),
3164 N_("Centre, Start and End"),
3165 NULL
3166 };
3167
3168 widgets->w_namelabel = content_prop[cnt_prop++] = vik_combo_box_text_new ();
3169 gchar **pstr = draw_name_labels;
3170 while ( *pstr )
3171 vik_combo_box_text_append ( widgets->w_namelabel, *(pstr++) );
3172 gtk_combo_box_set_active ( GTK_COMBO_BOX(widgets->w_namelabel), tr->draw_name_mode );
3173
3174 widgets->w_number_distlabels = content_prop[cnt_prop++] =
3175 gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new(tr->max_number_dist_labels, 0, 100, 1, 1, 0)), 1, 0 );
3176 gtk_widget_set_tooltip_text ( GTK_WIDGET(widgets->w_number_distlabels), _("Maximum number of distance labels to be shown") );
3177
3178 table = create_table (cnt_prop, label_texts, content_prop);
3179
3180 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), GTK_WIDGET(table), gtk_label_new(_("Properties")));
3181
3182 // Statistics
3183 GtkWidget *content[20];
3184 int cnt = 0;
6b2f262e 3185
6f9336aa
RN
3186 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3187
f9744e8f
RN
3188 // NB This value not shown yet - but is used by internal calculations
3189 widgets->track_length_inc_gaps = vik_track_get_length_including_gaps(tr);
3190
e60fc2c9 3191 tr_len = widgets->track_length = vik_track_get_length(tr);
6f9336aa
RN
3192 switch (dist_units) {
3193 case VIK_UNITS_DISTANCE_KILOMETRES:
3194 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km", tr_len/1000.0 );
3195 break;
3196 case VIK_UNITS_DISTANCE_MILES:
433b3f7f 3197 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f miles", VIK_METERS_TO_MILES(tr_len) );
6f9336aa 3198 break;
b22233bd
RN
3199 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3200 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f NM", VIK_METERS_TO_NAUTICAL_MILES(tr_len) );
3201 break;
6f9336aa
RN
3202 default:
3203 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3204 }
21700912 3205 widgets->w_track_length = content[cnt++] = gtk_label_new ( tmp_buf );
50a14534
EB
3206
3207 tp_count = vik_track_get_tp_count(tr);
901fd9e2 3208 g_snprintf(tmp_buf, sizeof(tmp_buf), "%lu", tp_count );
21700912 3209 widgets->w_tp_count = content[cnt++] = gtk_label_new ( tmp_buf );
50a14534
EB
3210
3211 seg_count = vik_track_get_segment_count(tr) ;
3212 g_snprintf(tmp_buf, sizeof(tmp_buf), "%u", seg_count );
21700912 3213 widgets->w_segment_count = content[cnt++] = gtk_label_new ( tmp_buf );
50a14534
EB
3214
3215 g_snprintf(tmp_buf, sizeof(tmp_buf), "%lu", vik_track_get_dup_point_count(tr) );
21700912 3216 widgets->w_duptp_count = content[cnt++] = gtk_label_new ( tmp_buf );
50a14534 3217
13bdea80 3218 vik_units_speed_t speed_units = a_vik_get_units_speed ();
8c4f1350
EB
3219 tmp_speed = vik_track_get_max_speed(tr);
3220 if ( tmp_speed == 0 )
4c77d5e0 3221 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
13bdea80
RN
3222 else {
3223 switch (speed_units) {
3224 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
ab1e0693 3225 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km/h", VIK_MPS_TO_KPH(tmp_speed));
13bdea80
RN
3226 break;
3227 case VIK_UNITS_SPEED_MILES_PER_HOUR:
ab1e0693 3228 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f mph", VIK_MPS_TO_MPH(tmp_speed));
13bdea80
RN
3229 break;
3230 case VIK_UNITS_SPEED_METRES_PER_SECOND:
3231 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m/s", tmp_speed );
3232 break;
a4c92313 3233 case VIK_UNITS_SPEED_KNOTS:
ab1e0693 3234 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f knots", VIK_MPS_TO_KNOTS(tmp_speed));
a4c92313 3235 break;
13bdea80
RN
3236 default:
3237 g_snprintf (tmp_buf, sizeof(tmp_buf), "--" );
3238 g_critical("Houston, we've had a problem. speed=%d", speed_units);
3239 }
3240 }
21700912 3241 widgets->w_max_speed = content[cnt++] = gtk_label_new ( tmp_buf );
50a14534 3242
8c4f1350
EB
3243 tmp_speed = vik_track_get_average_speed(tr);
3244 if ( tmp_speed == 0 )
4c77d5e0 3245 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
13bdea80
RN
3246 else {
3247 switch (speed_units) {
3248 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
ab1e0693 3249 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km/h", VIK_MPS_TO_KPH(tmp_speed));
13bdea80
RN
3250 break;
3251 case VIK_UNITS_SPEED_MILES_PER_HOUR:
ab1e0693 3252 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f mph", VIK_MPS_TO_MPH(tmp_speed));
13bdea80
RN
3253 break;
3254 case VIK_UNITS_SPEED_METRES_PER_SECOND:
3255 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m/s", tmp_speed );
3256 break;
a4c92313 3257 case VIK_UNITS_SPEED_KNOTS:
ab1e0693 3258 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f knots", VIK_MPS_TO_KNOTS(tmp_speed));
a4c92313 3259 break;
13bdea80
RN
3260 default:
3261 g_snprintf (tmp_buf, sizeof(tmp_buf), "--" );
3262 g_critical("Houston, we've had a problem. speed=%d", speed_units);
3263 }
3264 }
21700912 3265 widgets->w_avg_speed = content[cnt++] = gtk_label_new ( tmp_buf );
50a14534 3266
a68ddd9c
RN
3267 // Use 60sec as the default period to be considered stopped
3268 // this is the TrackWaypoint draw stops default value 'vtl->stop_length'
3269 // however this variable is not directly accessible - and I don't expect it's often changed from the default
3270 // so ATM just put in the number
3271 tmp_speed = vik_track_get_average_speed_moving(tr, 60);
3272 if ( tmp_speed == 0 )
3273 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
3274 else {
3275 switch (speed_units) {
3276 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
3277 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km/h", VIK_MPS_TO_KPH(tmp_speed));
3278 break;
3279 case VIK_UNITS_SPEED_MILES_PER_HOUR:
3280 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f mph", VIK_MPS_TO_MPH(tmp_speed));
3281 break;
3282 case VIK_UNITS_SPEED_METRES_PER_SECOND:
3283 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m/s", tmp_speed );
3284 break;
3285 case VIK_UNITS_SPEED_KNOTS:
3286 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f knots", VIK_MPS_TO_KNOTS(tmp_speed));
3287 break;
3288 default:
3289 g_snprintf (tmp_buf, sizeof(tmp_buf), "--" );
3290 g_critical("Houston, we've had a problem. speed=%d", speed_units);
3291 }
3292 }
3293 widgets->w_mvg_speed = content[cnt++] = gtk_label_new ( tmp_buf );
3294
6f9336aa
RN
3295 switch (dist_units) {
3296 case VIK_UNITS_DISTANCE_KILOMETRES:
3297 // Even though kilometres, the average distance between points is going to be quite small so keep in metres
3298 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m", (tp_count - seg_count) == 0 ? 0 : tr_len / ( tp_count - seg_count ) );
3299 break;
3300 case VIK_UNITS_DISTANCE_MILES:
433b3f7f 3301 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.3f miles", (tp_count - seg_count) == 0 ? 0 : VIK_METERS_TO_MILES(tr_len / ( tp_count - seg_count )) );
6f9336aa 3302 break;
b22233bd
RN
3303 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3304 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.3f NM", (tp_count - seg_count) == 0 ? 0 : VIK_METERS_TO_NAUTICAL_MILES(tr_len / ( tp_count - seg_count )) );
3305 break;
6f9336aa
RN
3306 default:
3307 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3308 }
21700912 3309 widgets->w_avg_dist = content[cnt++] = gtk_label_new ( tmp_buf );
50a14534 3310
6027a9c5 3311 vik_units_height_t height_units = a_vik_get_units_height ();
8c4f1350 3312 if ( min_alt == VIK_DEFAULT_ALTITUDE )
4c77d5e0 3313 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
6027a9c5
RN
3314 else {
3315 switch (height_units) {
3316 case VIK_UNITS_HEIGHT_METRES:
3317 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f m - %.0f m", min_alt, max_alt );
3318 break;
3319 case VIK_UNITS_HEIGHT_FEET:
6c20e59a 3320 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f feet - %.0f feet", VIK_METERS_TO_FEET(min_alt), VIK_METERS_TO_FEET(max_alt) );
6027a9c5
RN
3321 break;
3322 default:
3323 g_snprintf(tmp_buf, sizeof(tmp_buf), "--" );
3324 g_critical("Houston, we've had a problem. height=%d", height_units);
3325 }
3326 }
21700912 3327 widgets->w_elev_range = content[cnt++] = gtk_label_new ( tmp_buf );
50a14534
EB
3328
3329 vik_track_get_total_elevation_gain(tr, &max_alt, &min_alt );
bf35388d 3330 if ( min_alt == VIK_DEFAULT_ALTITUDE )
4c77d5e0 3331 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
6027a9c5
RN
3332 else {
3333 switch (height_units) {
3334 case VIK_UNITS_HEIGHT_METRES:
3335 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f m / %.0f m", max_alt, min_alt );
3336 break;
3337 case VIK_UNITS_HEIGHT_FEET:
6c20e59a 3338 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f feet / %.0f feet", VIK_METERS_TO_FEET(max_alt), VIK_METERS_TO_FEET(min_alt) );
6027a9c5
RN
3339 break;
3340 default:
3341 g_snprintf(tmp_buf, sizeof(tmp_buf), "--" );
3342 g_critical("Houston, we've had a problem. height=%d", height_units);
3343 }
3344 }
21700912 3345 widgets->w_elev_gain = content[cnt++] = gtk_label_new ( tmp_buf );
24d5c7e2 3346
4b00e581
AF
3347#if 0
3348#define PACK(w) gtk_box_pack_start (GTK_BOX(right_vbox), w, FALSE, FALSE, 0);
3349 gtk_box_pack_start (GTK_BOX(right_vbox), e_cmt, FALSE, FALSE, 0);
3350 PACK(l_len);
3351 PACK(l_tps);
3352 PACK(l_segs);
3353 PACK(l_dups);
3354 PACK(l_maxs);
3355 PACK(l_avgs);
3356 PACK(l_avgd);
3357 PACK(l_elev);
3358 PACK(l_galo);
3359#undef PACK;
3360#endif
24d5c7e2 3361
8c4f1350 3362 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->timestamp )
f583082b
AF
3363 {
3364 time_t t1, t2;
3365 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
3366 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
3367
8ada46de
RN
3368 VikCoord vc;
3369 // Notional center of a track is simply an average of the bounding box extremities
3370 struct LatLon center = { (tr->bbox.north+tr->bbox.south)/2, (tr->bbox.east+tr->bbox.west)/2 };
3371 vik_coord_load_from_latlon ( &vc, vik_trw_layer_get_coord_mode(vtl), &center );
3372
74562734
RN
3373 widgets->tz = vu_get_tz_at_location ( &vc );
3374
8ada46de 3375 gchar *msg;
74562734 3376 msg = vu_get_time_string ( &t1, "%c", &vc, widgets->tz );
8ada46de
RN
3377 widgets->w_time_start = content[cnt++] = gtk_label_new(msg);
3378 g_free ( msg );
f583082b 3379
74562734 3380 msg = vu_get_time_string ( &t2, "%c", &vc, widgets->tz );
8ada46de
RN
3381 widgets->w_time_end = content[cnt++] = gtk_label_new(msg);
3382 g_free ( msg );
f583082b 3383
4c77d5e0 3384 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%d minutes"), (int)(t2-t1)/60);
21700912 3385 widgets->w_time_dur = content[cnt++] = gtk_label_new(tmp_buf);
8c4f1350 3386 } else {
4c77d5e0
GB
3387 widgets->w_time_start = content[cnt++] = gtk_label_new(_("No Data"));
3388 widgets->w_time_end = content[cnt++] = gtk_label_new(_("No Data"));
3389 widgets->w_time_dur = content[cnt++] = gtk_label_new(_("No Data"));
24d5c7e2 3390 }
4b00e581 3391
7cdf9f1e 3392 table = create_table (cnt, stats_texts, content);
4b00e581 3393
954e061e 3394 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), GTK_WIDGET(table), gtk_label_new(_("Statistics")));
50a14534 3395
950d8a07 3396 if ( widgets->elev_box ) {
065f60f1
GB
3397 GtkWidget *page = NULL;
3398 widgets->w_cur_dist = gtk_label_new(_("No Data"));
780d3771 3399 widgets->w_cur_elevation = gtk_label_new(_("No Data"));
ec32b567
RN
3400 widgets->w_show_dem = gtk_check_button_new_with_mnemonic(_("Show D_EM"));
3401 widgets->w_show_alt_gps_speed = gtk_check_button_new_with_mnemonic(_("Show _GPS Speed"));
780d3771 3402 page = create_graph_page (widgets->elev_box,
1b40910b
RN
3403 _("<b>Track Distance:</b>"), widgets->w_cur_dist,
3404 _("<b>Track Height:</b>"), widgets->w_cur_elevation,
3405 NULL, NULL,
932b4a7a
RN
3406 widgets->w_show_dem, show_dem,
3407 widgets->w_show_alt_gps_speed, show_alt_gps_speed);
2cf168ac
RN
3408 g_signal_connect (widgets->w_show_dem, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
3409 g_signal_connect (widgets->w_show_alt_gps_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
065f60f1
GB
3410 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Elevation-distance")));
3411 }
c79f0206 3412
0ba33e1d
FA
3413 if ( widgets->gradient_box ) {
3414 GtkWidget *page = NULL;
3415 widgets->w_cur_gradient_dist = gtk_label_new(_("No Data"));
3416 widgets->w_cur_gradient_gradient = gtk_label_new(_("No Data"));
3417 widgets->w_show_gradient_gps_speed = gtk_check_button_new_with_mnemonic(_("Show _GPS Speed"));
3418 page = create_graph_page (widgets->gradient_box,
1b40910b
RN
3419 _("<b>Track Distance:</b>"), widgets->w_cur_gradient_dist,
3420 _("<b>Track Gradient:</b>"), widgets->w_cur_gradient_gradient,
3421 NULL, NULL,
932b4a7a 3422 widgets->w_show_gradient_gps_speed, show_gradient_gps_speed,
1b40910b 3423 NULL, FALSE);
2cf168ac 3424 g_signal_connect (widgets->w_show_gradient_gps_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
0ba33e1d
FA
3425 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Gradient-distance")));
3426 }
3427
950d8a07 3428 if ( widgets->speed_box ) {
065f60f1
GB
3429 GtkWidget *page = NULL;
3430 widgets->w_cur_time = gtk_label_new(_("No Data"));
780d3771 3431 widgets->w_cur_speed = gtk_label_new(_("No Data"));
1b40910b 3432 widgets->w_cur_time_real = gtk_label_new(_("No Data"));
ec32b567 3433 widgets->w_show_gps_speed = gtk_check_button_new_with_mnemonic(_("Show _GPS Speed"));
780d3771 3434 page = create_graph_page (widgets->speed_box,
1b40910b
RN
3435 _("<b>Track Time:</b>"), widgets->w_cur_time,
3436 _("<b>Track Speed:</b>"), widgets->w_cur_speed,
9e405703 3437 _("<b>Time/Date:</b>"), widgets->w_cur_time_real,
932b4a7a 3438 widgets->w_show_gps_speed, show_gps_speed,
1b40910b 3439 NULL, FALSE);
2cf168ac 3440 g_signal_connect (widgets->w_show_gps_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
065f60f1
GB
3441 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Speed-time")));
3442 }
50a14534 3443
926c8140
RN
3444 if ( widgets->dist_box ) {
3445 GtkWidget *page = NULL;
3446 widgets->w_cur_dist_time = gtk_label_new(_("No Data"));
3447 widgets->w_cur_dist_dist = gtk_label_new(_("No Data"));
1b40910b 3448 widgets->w_cur_dist_time_real = gtk_label_new(_("No Data"));
926c8140
RN
3449 widgets->w_show_dist_speed = gtk_check_button_new_with_mnemonic(_("Show S_peed"));
3450 page = create_graph_page (widgets->dist_box,
1b40910b
RN
3451 _("<b>Track Distance:</b>"), widgets->w_cur_dist_dist,
3452 _("<b>Track Time:</b>"), widgets->w_cur_dist_time,
9e405703 3453 _("<b>Time/Date:</b>"), widgets->w_cur_dist_time_real,
932b4a7a 3454 widgets->w_show_dist_speed, show_dist_speed,
1b40910b 3455 NULL, FALSE);
2cf168ac 3456 g_signal_connect (widgets->w_show_dist_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
926c8140
RN
3457 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Distance-time")));
3458 }
3459
8de26632
RN
3460 if ( widgets->elev_time_box ) {
3461 GtkWidget *page = NULL;
3462 widgets->w_cur_elev_time = gtk_label_new(_("No Data"));
3463 widgets->w_cur_elev_elev = gtk_label_new(_("No Data"));
1b40910b 3464 widgets->w_cur_elev_time_real = gtk_label_new(_("No Data"));
8de26632 3465 widgets->w_show_elev_speed = gtk_check_button_new_with_mnemonic(_("Show S_peed"));
3772b8c2 3466 widgets->w_show_elev_dem = gtk_check_button_new_with_mnemonic(_("Show D_EM"));
8de26632 3467 page = create_graph_page (widgets->elev_time_box,
1b40910b
RN
3468 _("<b>Track Time:</b>"), widgets->w_cur_elev_time,
3469 _("<b>Track Height:</b>"), widgets->w_cur_elev_elev,
9e405703 3470 _("<b>Time/Date:</b>"), widgets->w_cur_elev_time_real,
932b4a7a
RN
3471 widgets->w_show_elev_dem, show_elev_dem,
3472 widgets->w_show_elev_speed, show_elev_speed);
3772b8c2 3473 g_signal_connect (widgets->w_show_elev_dem, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
2cf168ac 3474 g_signal_connect (widgets->w_show_elev_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
8de26632
RN
3475 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Elevation-time")));
3476 }
3477
7b624086
RN
3478 if ( widgets->speed_dist_box ) {
3479 GtkWidget *page = NULL;
3480 widgets->w_cur_speed_dist = gtk_label_new(_("No Data"));
3481 widgets->w_cur_speed_speed = gtk_label_new(_("No Data"));
3482 widgets->w_show_sd_gps_speed = gtk_check_button_new_with_mnemonic(_("Show _GPS Speed"));
3483 page = create_graph_page (widgets->speed_dist_box,
1b40910b
RN
3484 _("<b>Track Distance:</b>"), widgets->w_cur_speed_dist,
3485 _("<b>Track Speed:</b>"), widgets->w_cur_speed_speed,
3486 NULL, NULL,
932b4a7a 3487 widgets->w_show_sd_gps_speed, show_sd_gps_speed,
1b40910b 3488 NULL, FALSE);
2cf168ac 3489 g_signal_connect (widgets->w_show_sd_gps_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
7b624086
RN
3490 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Speed-distance")));
3491 }
3492
9b082b39 3493 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), graphs, FALSE, FALSE, 0);
1d0135d8 3494
8dcb3ba4 3495 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER, FALSE);
1d0135d8 3496 if (seg_count <= 1)
8dbfe7a3 3497 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), VIK_TRW_LAYER_PROPWIN_SPLIT, FALSE);
1d0135d8 3498 if (vik_track_get_dup_point_count(tr) <= 0)
8dbfe7a3 3499 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), VIK_TRW_LAYER_PROPWIN_DEL_DUP, FALSE);
1d0135d8 3500
d45ac152 3501 // On dialog realization configure_event causes the graphs to be initially drawn
950d8a07 3502 widgets->configure_dialog = TRUE;
2cf168ac
RN
3503 g_signal_connect ( G_OBJECT(dialog), "configure-event", G_CALLBACK (configure_event), widgets );
3504
3505 g_signal_connect ( G_OBJECT(dialog), "destroy", G_CALLBACK (destroy_cb), widgets );
950d8a07 3506
21700912 3507 vik_track_set_property_dialog(tr, dialog);
77b591fa 3508 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
50a14534 3509 gtk_widget_show_all ( dialog );
626de648
RN
3510
3511 // Gtk note: due to historical reasons, this must be done after widgets are shown
3512 if ( start_on_stats )
3513 gtk_notebook_set_current_page ( GTK_NOTEBOOK(graphs), 1 );
50a14534 3514}
ce4bd1cf
RN
3515
3516
3517/**
3518 * Update this property dialog
3519 * e.g. if the track has been renamed
3520 */
3521void vik_trw_layer_propwin_update ( VikTrack *trk )
3522{
3523 // If not displayed do nothing
3524 if ( !trk->property_dialog )
3525 return;
3526
3527 // Update title with current name
3528 if ( trk->name ) {
3529 gchar *title = g_strdup_printf ( _("%s - Track Properties"), trk->name );
3530 gtk_window_set_title ( GTK_WINDOW(trk->property_dialog), title );
3531 g_free(title);
3532 }
3533
3534}