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