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