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