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