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