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