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