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