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