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