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