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