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