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