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