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