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