]> git.street.me.uk Git - andy/viking.git/blob - src/viktrwlayer_propwin.c
Update source code copyright on viktrwlayer_propwin.c
[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 /* (Hopefully!) Human friendly altitude grid sizes - note no fixed 'ratio' just numbers that look nice...*/
62 static const gdouble chunksa[] = {2.0, 5.0, 10.0, 15.0, 20.0,
63                                   25.0, 50.0, 75.0, 100.0,
64                                   150.0, 200.0, 250.0, 375.0, 500.0,
65                                   750.0, 1000.0, 2000.0, 5000.0, 10000.0, 100000.0};
66
67 /* (Hopefully!) Human friendly grid sizes - note no fixed 'ratio' just numbers that look nice...*/
68 /* As need to cover walking speeds - have many low numbers (but also may go up to airplane speeds!) */
69 static const gdouble chunkss[] = {1.0, 2.0, 3.0, 4.0, 5.0, 8.0, 10.0,
70                                   15.0, 20.0, 25.0, 40.0, 50.0, 75.0,
71                                   100.0, 150.0, 200.0, 250.0, 375.0, 500.0,
72                                   750.0, 1000.0, 10000.0};
73
74 typedef struct _propsaved {
75   gboolean saved;
76   GdkImage *img;
77 } PropSaved;
78
79 typedef struct _propwidgets {
80   gboolean  configure_dialog;
81   VikTrwLayer *vtl;
82   VikTrack *tr;
83   gchar *track_name;
84   gint      profile_width;
85   gint      profile_height;
86   gint      profile_width_old;
87   gint      profile_height_old;
88   gint      profile_width_offset;
89   gint      profile_height_offset;
90   GtkWidget *dialog;
91   GtkWidget *w_comment;
92   GtkWidget *w_track_length;
93   GtkWidget *w_tp_count;
94   GtkWidget *w_segment_count;
95   GtkWidget *w_duptp_count;
96   GtkWidget *w_max_speed;
97   GtkWidget *w_avg_speed;
98   GtkWidget *w_avg_dist;
99   GtkWidget *w_elev_range;
100   GtkWidget *w_elev_gain;
101   GtkWidget *w_time_start;
102   GtkWidget *w_time_end;
103   GtkWidget *w_time_dur;
104   GtkWidget *w_cur_dist; /*< Current distance */
105   GtkWidget *w_cur_elevation;
106   GtkWidget *w_cur_time; /*< Current time */
107   GtkWidget *w_cur_speed;
108   GtkWidget *w_show_dem;
109   GtkWidget *w_show_alt_gps_speed;
110   GtkWidget *w_show_gps_speed;
111   gdouble   track_length;
112   PropSaved elev_graph_saved_img;
113   PropSaved speed_graph_saved_img;
114   GtkWidget *elev_box;
115   GtkWidget *speed_box;
116   gdouble   *altitudes;
117   gdouble   min_altitude;
118   gdouble   max_altitude;
119   gdouble   draw_min_altitude;
120   gint      cia; // Chunk size Index into Altitudes
121   gdouble   *speeds;
122   gdouble   min_speed;
123   gdouble   max_speed;
124   gdouble   draw_min_speed;
125   gint      cis; // Chunk size Index into Speeds
126   VikTrackpoint *marker_tp;
127   gboolean  is_marker_drawn;
128   VikTrackpoint *blob_tp;
129   gboolean  is_blob_drawn;
130 } PropWidgets;
131
132 static PropWidgets *prop_widgets_new()
133 {
134   PropWidgets *widgets = g_malloc0(sizeof(PropWidgets));
135
136   return widgets;
137 }
138
139 static void prop_widgets_free(PropWidgets *widgets)
140 {
141   if (widgets->elev_graph_saved_img.img)
142     g_object_unref(widgets->elev_graph_saved_img.img);
143   if (widgets->speed_graph_saved_img.img)
144     g_object_unref(widgets->speed_graph_saved_img.img);
145   if (widgets->altitudes)
146     g_free(widgets->altitudes);
147   if (widgets->speeds)
148     g_free(widgets->speeds);
149   g_free(widgets);
150 }
151
152 static void minmax_array(const gdouble *array, gdouble *min, gdouble *max, gboolean NO_ALT_TEST, gint PROFILE_WIDTH)
153 {
154   *max = -1000;
155   *min = 20000;
156   guint i;
157   for ( i=0; i < PROFILE_WIDTH; i++ ) {
158     if ( NO_ALT_TEST || (array[i] != VIK_DEFAULT_ALTITUDE) ) {
159       if ( array[i] > *max )
160         *max = array[i];
161       if ( array[i] < *min )
162         *min = array[i];
163     }
164   }
165 }
166
167 #define MARGIN 70
168 #define LINES 5
169 /**
170  * Returns via pointers:
171  *   the new minimum value to be used for the altitude graph
172  *   the index in to the altitudes chunk sizes array (ci = Chunk Index)
173  */
174 static void get_new_min_and_chunk_index_altitude (gdouble mina, gdouble maxa, gdouble *new_min, gint *ci)
175 {
176   /* Get unitized chunk */
177   /* Find suitable chunk index */
178   *ci = 0;
179   gdouble diffa_chunk = (maxa - mina)/LINES;
180
181   /* Loop through to find best match */
182   while (diffa_chunk > chunksa[*ci]) {
183     (*ci)++;
184     /* Last Resort Check */
185     if ( *ci == sizeof(chunksa)/sizeof(chunksa[0]) )
186       break;
187   }
188
189   /* Ensure adjusted minimum .. maximum covers mina->maxa */
190
191   // Now work out adjusted minimum point to the nearest lowest chunk divisor value
192   // When negative ensure logic uses lowest value
193   if ( mina < 0 )
194     *new_min = (gdouble) ( ( (gint)(mina - chunksa[*ci]) / (gint)chunksa[*ci] ) * (gint)chunksa[*ci] );
195   else
196     *new_min = (gdouble) ( ( (gint)mina / (gint)chunksa[*ci] ) * (gint)chunksa[*ci] );
197
198   // Range not big enough - as new minimum has lowered
199   if ((*new_min + (chunksa[*ci] * LINES) < maxa)) {
200     // Next chunk should cover it
201     if ( *ci < sizeof(chunksa)/sizeof(chunksa[0]) ) {
202       (*ci)++;
203     }
204   }
205 }
206
207 /**
208  * Returns via pointers:
209  *   the new minimum value to be used for the appropriate graph
210  *   the index in to that array (ci = Chunk Index)
211  */
212 static void get_new_min_and_chunk_index (gdouble mins, gdouble maxs, const gdouble *chunks, size_t chunky, gdouble *new_min, gint *ci)
213 {
214   *ci = 0;
215   gdouble diff_chunk = (maxs - mins)/LINES;
216
217   /* Loop through to find best match */
218   while (diff_chunk > chunks[*ci]) {
219     (*ci)++;
220     /* Last Resort Check */
221     if ( *ci == chunky )
222       break;
223   }
224   *new_min = (gdouble) ( (gint)((mins / chunks[*ci] ) * chunks[*ci]) );
225
226   // Speeds are never negative so don't need to worry about a negative new minimum
227 }
228
229 static VikTrackpoint *set_center_at_graph_position(gdouble event_x,
230                                                    gint img_width,
231                                                    VikTrwLayer *vtl,
232                                                    VikLayersPanel *vlp,
233                                                    VikViewport *vvp,
234                                                    VikTrack *tr,
235                                                    gboolean time_base,
236                                                    gint PROFILE_WIDTH)
237 {
238   VikTrackpoint *trackpoint;
239   gdouble x = event_x - img_width / 2 + PROFILE_WIDTH / 2 - MARGIN / 2;
240   if (x < 0)
241     x = 0;
242   if (x > PROFILE_WIDTH)
243     x = PROFILE_WIDTH;
244
245   if (time_base)
246     trackpoint = vik_track_get_closest_tp_by_percentage_time ( tr, (gdouble) x / PROFILE_WIDTH, NULL );
247   else
248     trackpoint = vik_track_get_closest_tp_by_percentage_dist ( tr, (gdouble) x / PROFILE_WIDTH, NULL );
249
250   if ( trackpoint ) {
251     VikCoord coord = trackpoint->coord;
252     if ( vlp ) {
253       vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &coord );
254       vik_layers_panel_emit_update ( vlp );
255     }
256     else {
257       /* since vlp not set, vvp should be valid instead! */
258       if ( vvp )
259         vik_viewport_set_center_coord ( vvp, &coord );
260       vik_layer_emit_update ( VIK_LAYER(vtl) );
261     }
262   }
263   return trackpoint;
264 }
265
266 /**
267  * Returns whether the marker was drawn or not and whether the blob was drawn or not
268  */
269 static void save_image_and_draw_graph_marks (GtkWidget *image,
270                                              gdouble marker_x,
271                                              GdkGC *gc,
272                                              gint blob_x,
273                                              gint blob_y,
274                                              PropSaved *saved_img,
275                                              gint PROFILE_WIDTH,
276                                              gint PROFILE_HEIGHT,
277                                              gboolean *marker_drawn,
278                                              gboolean *blob_drawn)
279 {
280   GdkPixmap *pix = NULL;
281   /* the pixmap = margin + graph area */
282   gtk_image_get_pixmap(GTK_IMAGE(image), &pix, NULL);
283
284   /* Restore previously saved image */
285   if (saved_img->saved) {
286     gdk_draw_image(GDK_DRAWABLE(pix), gc, saved_img->img, 0, 0, 0, 0, MARGIN+PROFILE_WIDTH, PROFILE_HEIGHT);
287     saved_img->saved = FALSE;
288   }
289
290   // ATM always save whole image - as anywhere could have changed
291   if (saved_img->img)
292     gdk_drawable_copy_to_image(GDK_DRAWABLE(pix), saved_img->img, 0, 0, 0, 0, MARGIN+PROFILE_WIDTH, PROFILE_HEIGHT);
293   else
294     saved_img->img = gdk_drawable_copy_to_image(GDK_DRAWABLE(pix), saved_img->img, 0, 0, 0, 0, MARGIN+PROFILE_WIDTH, PROFILE_HEIGHT);
295   saved_img->saved = TRUE;
296
297   if ((marker_x >= MARGIN) && (marker_x < (PROFILE_WIDTH + MARGIN))) {
298     gdk_draw_line (GDK_DRAWABLE(pix), gc, marker_x, 0, marker_x, image->allocation.height);
299     *marker_drawn = TRUE;
300   }
301   else
302     *marker_drawn = FALSE;
303
304   // Draw a square blob to indicate where we are on track for this graph
305   if ( (blob_x >= MARGIN) && (blob_x < (PROFILE_WIDTH + MARGIN)) && (blob_y < PROFILE_HEIGHT) ) {
306     gdk_draw_rectangle (GDK_DRAWABLE(pix), gc, TRUE, blob_x-3, blob_y-3, 6, 6);
307     *blob_drawn = TRUE;
308   }
309   else
310     *blob_drawn = FALSE;
311   
312   // Anywhere on image could have changed
313   if (*marker_drawn || *blob_drawn)
314     gtk_widget_queue_draw(image);
315 }
316
317 /**
318  * Return the percentage of how far a trackpoint is a long a track via the time method
319  */
320 static gdouble tp_percentage_by_time ( VikTrack *tr, VikTrackpoint *trackpoint )
321 {
322   gdouble pc = NAN;
323   if (trackpoint == NULL)
324     return pc;
325   time_t t_start, t_end, t_total;
326   t_start = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
327   t_end = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
328   t_total = t_end - t_start;
329   pc = (gdouble)(trackpoint->timestamp - t_start)/t_total;
330   return pc;
331 }
332
333 /**
334  * Return the percentage of how far a trackpoint is a long a track via the distance method
335  */
336 static gdouble tp_percentage_by_distance ( VikTrack *tr, VikTrackpoint *trackpoint, gdouble track_length )
337 {
338   gdouble pc = NAN;
339   if (trackpoint == NULL)
340     return pc;
341   gdouble dist = 0.0;
342   GList *iter;
343   for (iter = tr->trackpoints->next; iter != NULL; iter = iter->next) {
344     dist += vik_coord_diff(&(VIK_TRACKPOINT(iter->data)->coord),
345                            &(VIK_TRACKPOINT(iter->prev->data)->coord));
346     /* Assuming trackpoint is not a copy */
347     if (trackpoint == VIK_TRACKPOINT(iter->data))
348       break;
349   }
350   if (iter != NULL)
351     pc = dist/track_length;
352   return pc;
353 }
354
355 static void track_graph_click( GtkWidget *event_box, GdkEventButton *event, gpointer *pass_along, gboolean is_vt_graph )
356 {
357   VikTrack *tr = pass_along[0];
358   VikLayersPanel *vlp = pass_along[1];
359   VikViewport *vvp = pass_along[2];
360   PropWidgets *widgets = pass_along[3];
361   GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
362   GtkWidget *image = GTK_WIDGET(child->data);
363   GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(event_box));
364
365   VikTrackpoint *trackpoint = set_center_at_graph_position(event->x, event_box->allocation.width, widgets->vtl, vlp, vvp, tr, is_vt_graph, widgets->profile_width);
366   gdouble xm = event->x - event_box->allocation.width/2 + widgets->profile_width/2 + MARGIN/2;
367   save_image_and_draw_graph_marks(image,
368                                   xm,
369                                   window->style->black_gc,
370                                   -1, // Don't draw blob on clicks
371                                   0,
372                                   is_vt_graph ? &widgets->speed_graph_saved_img : &widgets->elev_graph_saved_img,
373                                   widgets->profile_width,
374                                   widgets->profile_height,
375                                   &widgets->is_marker_drawn,
376                                   &widgets->is_blob_drawn);
377   g_list_free(child);
378   widgets->marker_tp = trackpoint;
379   gtk_dialog_set_response_sensitive(GTK_DIALOG(widgets->dialog), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER, widgets->is_marker_drawn);
380
381   /* draw on the other graph */
382   if (trackpoint == NULL || widgets->elev_box == NULL || widgets->speed_box == NULL)
383     /* This test assumes we have only 2 graphs */
384     return;
385
386   gdouble pc = NAN;
387   GList *other_child = gtk_container_get_children(GTK_CONTAINER(
388                          is_vt_graph ? widgets->elev_box : widgets->speed_box));
389   GtkWidget *other_image = GTK_WIDGET(other_child->data);
390   if (is_vt_graph) {
391     pc = tp_percentage_by_distance ( tr, trackpoint, widgets->track_length );
392   } else {
393     pc = tp_percentage_by_time ( tr, trackpoint );
394   }
395   if (!isnan(pc)) {
396     gdouble x2 = pc * widgets->profile_width + MARGIN;
397     save_image_and_draw_graph_marks(other_image,
398                                     x2,
399                                     window->style->black_gc,
400                                     -1, // Don't draw blob on clicks
401                                     0,
402                                     is_vt_graph ? &widgets->elev_graph_saved_img : &widgets->speed_graph_saved_img,
403                                     widgets->profile_width,
404                                     widgets->profile_height,
405                                     &widgets->is_marker_drawn,
406                                     &widgets->is_blob_drawn);
407   }
408
409   g_list_free(other_child);
410
411 }
412
413 static gboolean track_profile_click( GtkWidget *event_box, GdkEventButton *event, gpointer *pass_along )
414 {
415   track_graph_click(event_box, event, pass_along, FALSE);
416   return TRUE;  /* don't call other (further) callbacks */
417 }
418
419 static gboolean track_vt_click( GtkWidget *event_box, GdkEventButton *event, gpointer *pass_along )
420 {
421   track_graph_click(event_box, event, pass_along, TRUE);
422   return TRUE;  /* don't call other (further) callbacks */
423 }
424
425 /**
426  * Calculate y position for blob on elevation graph
427  */
428 static gint blobby_altitude ( gdouble x_blob, PropWidgets *widgets )
429 {
430   gint ix = (gint)x_blob;
431   // Ensure ix is inbounds
432   if (ix == widgets->profile_width)
433     ix--;
434
435   gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->altitudes[ix]-widgets->draw_min_altitude)/(chunksa[widgets->cia]*LINES);
436
437   return y_blob;
438 }
439
440 /**
441  * Calculate y position for blob on speed graph
442  */
443 static gint blobby_speed ( gdouble x_blob, PropWidgets *widgets )
444 {
445   gint ix = (gint)x_blob;
446   // Ensure ix is inbounds
447   if (ix == widgets->profile_width)
448     ix--;
449
450   gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->speeds[ix]-widgets->draw_min_speed)/(chunkss[widgets->cis]*LINES);
451
452   return y_blob;
453 }
454
455 void track_profile_move( GtkWidget *event_box, GdkEventMotion *event, gpointer *pass_along )
456 {
457   VikTrack *tr = pass_along[0];
458   PropWidgets *widgets = pass_along[3];
459   int mouse_x, mouse_y;
460   GdkModifierType state;
461
462   if (event->is_hint)
463     gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
464   else
465     mouse_x = event->x;
466
467   gdouble x = mouse_x - event_box->allocation.width / 2 + widgets->profile_width / 2 - MARGIN / 2;
468   if (x < 0)
469     x = 0;
470   if (x > widgets->profile_width)
471     x = widgets->profile_width;
472
473   gdouble meters_from_start;
474   VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_dist ( tr, (gdouble) x / widgets->profile_width, &meters_from_start );
475   if (trackpoint && widgets->w_cur_dist) {
476     static gchar tmp_buf[20];
477     vik_units_distance_t dist_units = a_vik_get_units_distance ();
478     switch (dist_units) {
479     case VIK_UNITS_DISTANCE_KILOMETRES:
480       g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km", meters_from_start/1000.0);
481       break;
482     case VIK_UNITS_DISTANCE_MILES:
483       g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f miles", VIK_METERS_TO_MILES(meters_from_start) );
484       break;
485     default:
486       g_critical("Houston, we've had a problem. distance=%d", dist_units);
487     }
488     gtk_label_set_text(GTK_LABEL(widgets->w_cur_dist), tmp_buf);
489   }
490
491   // Show track elevation for this position - to the nearest whole number
492   if (trackpoint && widgets->w_cur_elevation) {
493     static gchar tmp_buf[20];
494     if (a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET)
495       g_snprintf(tmp_buf, sizeof(tmp_buf), "%d ft", (int)VIK_METERS_TO_FEET(trackpoint->altitude));
496     else
497       g_snprintf(tmp_buf, sizeof(tmp_buf), "%d m", (int)trackpoint->altitude);
498     gtk_label_set_text(GTK_LABEL(widgets->w_cur_elevation), tmp_buf);
499   }
500
501   widgets->blob_tp = trackpoint;
502
503   if ( widgets->altitudes == NULL )
504     return;
505
506   GtkWidget *window = gtk_widget_get_toplevel (event_box);
507   GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
508   GtkWidget *image = GTK_WIDGET(child->data);
509
510   gint y_blob = blobby_altitude (x, widgets);
511
512   gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
513   if (widgets->is_marker_drawn) {
514     gdouble pc = tp_percentage_by_distance ( tr, widgets->marker_tp, widgets->track_length );
515     if (!isnan(pc)) {
516       marker_x = (pc * widgets->profile_width) + MARGIN;
517     }
518   }
519
520   save_image_and_draw_graph_marks (image,
521                                    marker_x,
522                                    window->style->black_gc,
523                                    MARGIN+x,
524                                    y_blob,
525                                    &widgets->elev_graph_saved_img,
526                                    widgets->profile_width,
527                                    widgets->profile_height,
528                                    &widgets->is_marker_drawn,
529                                    &widgets->is_blob_drawn);
530
531   g_list_free(child);
532 }
533
534 void track_vt_move( GtkWidget *event_box, GdkEventMotion *event, gpointer *pass_along )
535 {
536   VikTrack *tr = pass_along[0];
537   PropWidgets *widgets = pass_along[3];
538   int mouse_x, mouse_y;
539   GdkModifierType state;
540
541   if (event->is_hint)
542     gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
543   else
544     mouse_x = event->x;
545
546   gdouble x = mouse_x - event_box->allocation.width / 2 + widgets->profile_width / 2 - MARGIN / 2;
547   if (x < 0)
548     x = 0;
549   if (x > widgets->profile_width)
550     x = widgets->profile_width;
551
552   time_t seconds_from_start;
553   VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_time ( tr, (gdouble) x / widgets->profile_width, &seconds_from_start );
554   if (trackpoint && widgets->w_cur_time) {
555     static gchar tmp_buf[20];
556     guint h, m, s;
557     h = seconds_from_start/3600;
558     m = (seconds_from_start - h*3600)/60;
559     s = seconds_from_start - (3600*h) - (60*m);
560     g_snprintf(tmp_buf, sizeof(tmp_buf), "%02d:%02d:%02d", h, m, s);
561
562     gtk_label_set_text(GTK_LABEL(widgets->w_cur_time), tmp_buf);
563   }
564
565   gint ix = (gint)x;
566   // Ensure ix is inbounds
567   if (ix == widgets->profile_width)
568     ix--;
569
570   // Show track speed for this position
571   if (trackpoint && widgets->w_cur_speed) {
572     static gchar tmp_buf[20];
573     // Even if GPS speed available (trackpoint->speed), the text will correspond to the speed map shown
574     // No conversions needed as already in appropriate units
575     vik_units_speed_t speed_units = a_vik_get_units_speed ();
576     switch (speed_units) {
577     case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
578       g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f kph"), widgets->speeds[ix]);
579       break;
580     case VIK_UNITS_SPEED_MILES_PER_HOUR:
581       g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f mph"), widgets->speeds[ix]);
582       break;
583     case VIK_UNITS_SPEED_KNOTS:
584       g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f knots"), widgets->speeds[ix]);
585       break;
586     default:
587       // VIK_UNITS_SPEED_METRES_PER_SECOND:
588       g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f m/s"), widgets->speeds[ix]);
589       break;
590     }
591     gtk_label_set_text(GTK_LABEL(widgets->w_cur_speed), tmp_buf);
592   }
593
594   widgets->blob_tp = trackpoint;
595
596   if ( widgets->speeds == NULL )
597     return;
598
599   GtkWidget *window = gtk_widget_get_toplevel (event_box);
600   GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
601   GtkWidget *image = GTK_WIDGET(child->data);
602
603   gint y_blob = blobby_speed (x, widgets);
604
605   gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
606   if (widgets->is_marker_drawn) {
607     gdouble pc = tp_percentage_by_time ( tr, widgets->marker_tp );
608     if (!isnan(pc)) {
609       marker_x = (pc * widgets->profile_width) + MARGIN;
610     }
611   }
612
613   save_image_and_draw_graph_marks (image,
614                                    marker_x,
615                                    window->style->black_gc,
616                                    MARGIN+x,
617                                    y_blob,
618                                    &widgets->speed_graph_saved_img,
619                                    widgets->profile_width,
620                                    widgets->profile_height,
621                                    &widgets->is_marker_drawn,
622                                    &widgets->is_blob_drawn);
623
624   g_list_free(child);
625 }
626
627 /**
628  * Draws DEM points and a respresentative speed on the supplied pixmap
629  *   (which is the elevations graph)
630  */
631 static void draw_dem_alt_speed_dist(VikTrack *tr,
632                                     GdkDrawable *pix,
633                                     GdkGC *alt_gc,
634                                     GdkGC *speed_gc,
635                                     gdouble alt_offset,
636                                     gdouble alt_diff,
637                                     gdouble max_speed_in,
638                                     gint cia,
639                                     gint width,
640                                     gint height,
641                                     gint margin,
642                                     gboolean do_dem,
643                                     gboolean do_speed)
644 {
645   GList *iter;
646   gdouble max_speed = 0;
647   gdouble total_length = vik_track_get_length_including_gaps(tr);
648
649   // Calculate the max speed factor
650   if (do_speed)
651     max_speed = max_speed_in * 110 / 100;
652
653   gdouble dist = 0;
654   for (iter = tr->trackpoints->next; iter; iter = iter->next) {
655     int x;
656     dist += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
657                              &(VIK_TRACKPOINT(iter->prev->data)->coord) );
658     x = (width * dist)/total_length + margin;
659     if (do_dem) {
660       gint16 elev = a_dems_get_elev_by_coord(&(VIK_TRACKPOINT(iter->data)->coord), VIK_DEM_INTERPOL_BEST);
661       elev -= alt_offset;
662       if ( elev != VIK_DEM_INVALID_ELEVATION ) {
663         // Convert into height units
664         if (a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET)
665           elev =  VIK_METERS_TO_FEET(elev);
666         // No conversion needed if already in metres
667
668         // consider chunk size
669         int y_alt = height - ((height * (elev-alt_offset))/(chunksa[cia]*LINES) );
670         gdk_draw_rectangle(GDK_DRAWABLE(pix), alt_gc, TRUE, x-2, y_alt-2, 4, 4);
671       }
672     }
673     if (do_speed) {
674       // This is just a speed indicator - no actual values can be inferred by user
675       if (!isnan(VIK_TRACKPOINT(iter->data)->speed)) {
676         int y_speed = height - (height * VIK_TRACKPOINT(iter->data)->speed)/max_speed;
677         gdk_draw_rectangle(GDK_DRAWABLE(pix), speed_gc, TRUE, x-2, y_speed-2, 4, 4);
678       }
679     }
680   }
681 }
682
683 /**
684  * Draw just the height profile image
685  */
686 static void draw_elevations (GtkWidget *image, VikTrack *tr, PropWidgets *widgets )
687 {
688   GtkWidget *window;
689   GdkPixmap *pix;
690   gdouble mina, maxa;
691   guint i;
692
693   GdkGC *no_alt_info;
694   GdkGC *dem_alt_gc;
695   GdkGC *gps_speed_gc;
696
697   GdkColor color;
698
699   // Free previous allocation
700   if ( widgets->altitudes )
701     g_free ( widgets->altitudes );
702
703   widgets->altitudes = vik_track_make_elevation_map ( tr, widgets->profile_width );
704
705   if ( widgets->altitudes == NULL )
706     return;
707
708   // Convert into appropriate units
709   vik_units_height_t height_units = a_vik_get_units_height ();
710   if ( height_units == VIK_UNITS_HEIGHT_FEET ) {
711     // Convert altitudes into feet units
712     for ( i = 0; i < widgets->profile_width; i++ ) {
713       widgets->altitudes[i] = VIK_METERS_TO_FEET(widgets->altitudes[i]);
714     }
715   }
716   // Otherwise leave in metres
717
718   minmax_array(widgets->altitudes, &widgets->min_altitude, &widgets->max_altitude, TRUE, widgets->profile_width);
719
720   get_new_min_and_chunk_index_altitude (widgets->min_altitude, widgets->max_altitude, &widgets->draw_min_altitude, &widgets->cia);
721
722   // Assign locally
723   mina = widgets->draw_min_altitude;
724   maxa = widgets->max_altitude;
725
726   window = gtk_widget_get_toplevel (widgets->elev_box);
727
728   pix = gdk_pixmap_new( window->window, widgets->profile_width + MARGIN, widgets->profile_height, -1 );
729
730   gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
731
732   no_alt_info = gdk_gc_new ( window->window );
733   gdk_color_parse ( "yellow", &color );
734   gdk_gc_set_rgb_fg_color ( no_alt_info, &color);
735
736   dem_alt_gc = gdk_gc_new ( window->window );
737   gdk_color_parse ( "green", &color );
738   gdk_gc_set_rgb_fg_color ( dem_alt_gc, &color);
739
740   gps_speed_gc = gdk_gc_new ( window->window );
741   gdk_color_parse ( "red", &color );
742   gdk_gc_set_rgb_fg_color ( gps_speed_gc, &color);
743
744   /* clear the image */
745   gdk_draw_rectangle(GDK_DRAWABLE(pix), window->style->bg_gc[0], 
746                      TRUE, 0, 0, MARGIN, widgets->profile_height);
747   gdk_draw_rectangle(GDK_DRAWABLE(pix), window->style->mid_gc[0], 
748                      TRUE, MARGIN, 0, widgets->profile_width, widgets->profile_height);
749   
750   /* draw grid */
751   for (i=0; i<=LINES; i++) {
752     PangoFontDescription *pfd;
753     PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(image), NULL);
754     gchar s[32];
755     int w, h;
756
757     pango_layout_set_alignment (pl, PANGO_ALIGN_RIGHT);
758     pfd = pango_font_description_from_string (PROPWIN_LABEL_FONT);
759     pango_layout_set_font_description (pl, pfd);
760     pango_font_description_free (pfd);
761     switch (height_units) {
762     case VIK_UNITS_HEIGHT_METRES:
763       sprintf(s, "%8dm", (int)(mina + (LINES-i)*chunksa[widgets->cia]));
764       break;
765     case VIK_UNITS_HEIGHT_FEET:
766       // NB values already converted into feet
767       sprintf(s, "%8dft", (int)(mina + (LINES-i)*chunksa[widgets->cia]));
768       break;
769     default:
770       sprintf(s, "--");
771       g_critical("Houston, we've had a problem. height=%d", height_units);
772     }
773     pango_layout_set_text(pl, s, -1);
774     pango_layout_get_pixel_size (pl, &w, &h);
775     gdk_draw_layout(GDK_DRAWABLE(pix), window->style->fg_gc[0], MARGIN-w-3, 
776                     CLAMP((int)i*widgets->profile_height/LINES - h/2, 0, widgets->profile_height-h), pl);
777
778     gdk_draw_line (GDK_DRAWABLE(pix), window->style->dark_gc[0], 
779                    MARGIN, widgets->profile_height/LINES * i, MARGIN + widgets->profile_width, widgets->profile_height/LINES * i);
780     g_object_unref ( G_OBJECT ( pl ) );
781     pl = NULL;
782   }
783
784   /* draw elevations */
785   for ( i = 0; i < widgets->profile_width; i++ )
786     if ( widgets->altitudes[i] == VIK_DEFAULT_ALTITUDE )
787       gdk_draw_line ( GDK_DRAWABLE(pix), no_alt_info, 
788                       i + MARGIN, 0, i + MARGIN, widgets->profile_height );
789     else 
790       gdk_draw_line ( GDK_DRAWABLE(pix), window->style->dark_gc[3], 
791                       i + MARGIN, widgets->profile_height, i + MARGIN, widgets->profile_height-widgets->profile_height*(widgets->altitudes[i]-mina)/(chunksa[widgets->cia]*LINES) );
792
793   // Ensure somekind of max speed when not set
794   if ( widgets->max_speed < 0.01 )
795     widgets->max_speed = vik_track_get_max_speed(tr);
796
797   draw_dem_alt_speed_dist(tr,
798                           GDK_DRAWABLE(pix),
799                           dem_alt_gc,
800                           gps_speed_gc,
801                           mina,
802                           maxa - mina,
803                           widgets->max_speed,
804                           widgets->cia,
805                           widgets->profile_width,
806                           widgets->profile_height,
807                           MARGIN,
808                           gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_dem)),
809                           gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_alt_gps_speed)));
810
811   /* draw border */
812   gdk_draw_rectangle(GDK_DRAWABLE(pix), window->style->black_gc, FALSE, MARGIN, 0, widgets->profile_width-1, widgets->profile_height-1);
813
814   g_object_unref ( G_OBJECT(pix) );
815   g_object_unref ( G_OBJECT(no_alt_info) );
816   g_object_unref ( G_OBJECT(dem_alt_gc) );
817   g_object_unref ( G_OBJECT(gps_speed_gc) );
818
819 }
820
821 /**
822  * Draw just the speed (velocity)/time image
823  */
824 static void draw_vt ( GtkWidget *image, VikTrack *tr, PropWidgets *widgets)
825 {
826   GtkWidget *window;
827   GdkPixmap *pix;
828   gdouble mins, maxs;
829   guint i;
830
831   // Free previous allocation
832   if ( widgets->speeds )
833     g_free ( widgets->speeds );
834
835   widgets->speeds = vik_track_make_speed_map ( tr, widgets->profile_width );
836   if ( widgets->speeds == NULL )
837     return;
838
839   // Convert into appropriate units
840   vik_units_speed_t speed_units = a_vik_get_units_speed ();
841   switch (speed_units) {
842   case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
843     for ( i = 0; i < widgets->profile_width; i++ ) {
844       widgets->speeds[i] = VIK_MPS_TO_KPH(widgets->speeds[i]);
845     }
846     break;
847   case VIK_UNITS_SPEED_MILES_PER_HOUR:
848     for ( i = 0; i < widgets->profile_width; i++ ) {
849       widgets->speeds[i] = VIK_MPS_TO_MPH(widgets->speeds[i]);
850     }
851     break;
852   case VIK_UNITS_SPEED_KNOTS:
853     for ( i = 0; i < widgets->profile_width; i++ ) {
854       widgets->speeds[i] = VIK_MPS_TO_KNOTS(widgets->speeds[i]);
855     }
856     break;
857   default:
858     // VIK_UNITS_SPEED_METRES_PER_SECOND:
859     // No need to convert as already in m/s
860     break;
861   }
862
863   GdkGC *gps_speed_gc;
864   GdkColor color;
865
866   window = gtk_widget_get_toplevel (widgets->speed_box);
867
868   pix = gdk_pixmap_new( window->window, widgets->profile_width + MARGIN, widgets->profile_height, -1 );
869
870   gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
871
872   gps_speed_gc = gdk_gc_new ( window->window );
873   gdk_color_parse ( "red", &color );
874   gdk_gc_set_rgb_fg_color ( gps_speed_gc, &color);
875
876   minmax_array(widgets->speeds, &widgets->min_speed, &widgets->max_speed, FALSE, widgets->profile_width);
877   if (widgets->min_speed < 0.0)
878     widgets->min_speed = 0; /* splines sometimes give negative speeds */
879
880   /* Find suitable chunk index */
881   get_new_min_and_chunk_index (widgets->min_speed, widgets->max_speed, chunkss, sizeof(chunkss)/sizeof(chunkss[0]), &widgets->draw_min_speed, &widgets->cis);
882
883   // Assign locally
884   mins = widgets->draw_min_speed;
885   maxs = widgets->max_speed;
886   
887   /* clear the image */
888   gdk_draw_rectangle(GDK_DRAWABLE(pix), window->style->bg_gc[0], 
889                      TRUE, 0, 0, MARGIN, widgets->profile_height);
890   gdk_draw_rectangle(GDK_DRAWABLE(pix), window->style->mid_gc[0], 
891                      TRUE, MARGIN, 0, widgets->profile_width, widgets->profile_height);
892
893   /* draw grid */
894   for (i=0; i<=LINES; i++) {
895     PangoFontDescription *pfd;
896     PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(image), NULL);
897     gchar s[32];
898     int w, h;
899
900     pango_layout_set_alignment (pl, PANGO_ALIGN_RIGHT);
901     pfd = pango_font_description_from_string (PROPWIN_LABEL_FONT);
902     pango_layout_set_font_description (pl, pfd);
903     pango_font_description_free (pfd);
904     // NB: No need to convert here anymore as numbers are in the appropriate units
905     switch (speed_units) {
906     case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
907       sprintf(s, "%8dkm/h", (int)(mins + (LINES-i)*chunkss[widgets->cis]));
908       break;
909     case VIK_UNITS_SPEED_MILES_PER_HOUR:
910       sprintf(s, "%8dmph", (int)(mins + (LINES-i)*chunkss[widgets->cis]));
911       break;
912     case VIK_UNITS_SPEED_METRES_PER_SECOND:
913       sprintf(s, "%8dm/s", (int)(mins + (LINES-i)*chunkss[widgets->cis]));
914       break;
915     case VIK_UNITS_SPEED_KNOTS:
916       sprintf(s, "%8dknots", (int)(mins + (LINES-i)*chunkss[widgets->cis]));
917       break;
918     default:
919       sprintf(s, "--");
920       g_critical("Houston, we've had a problem. speed=%d", speed_units);
921     }
922
923     pango_layout_set_text(pl, s, -1);
924     pango_layout_get_pixel_size (pl, &w, &h);
925     gdk_draw_layout(GDK_DRAWABLE(pix), window->style->fg_gc[0], MARGIN-w-3, 
926                     CLAMP((int)i*widgets->profile_height/LINES - h/2, 0, widgets->profile_height-h), pl);
927
928     gdk_draw_line (GDK_DRAWABLE(pix), window->style->dark_gc[0], 
929                    MARGIN, widgets->profile_height/LINES * i, MARGIN + widgets->profile_width, widgets->profile_height/LINES * i);
930     g_object_unref ( G_OBJECT ( pl ) );
931     pl = NULL;
932   }
933   
934
935   /* draw speeds */
936   for ( i = 0; i < widgets->profile_width; i++ )
937       gdk_draw_line ( GDK_DRAWABLE(pix), window->style->dark_gc[3], 
938                       i + MARGIN, widgets->profile_height, i + MARGIN, widgets->profile_height-widgets->profile_height*(widgets->speeds[i]-mins)/(chunkss[widgets->cis]*LINES) );
939
940
941   time_t beg_time = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
942   time_t dur =  VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp - beg_time;
943
944   if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->w_show_gps_speed)) ) {
945     GList *iter;
946     for (iter = tr->trackpoints; iter; iter = iter->next) {
947       gdouble gps_speed = VIK_TRACKPOINT(iter->data)->speed;
948       if (isnan(gps_speed))
949         continue;
950       switch (speed_units) {
951       case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
952         gps_speed = VIK_MPS_TO_KPH(gps_speed);
953         break;
954       case VIK_UNITS_SPEED_MILES_PER_HOUR:
955         gps_speed = VIK_MPS_TO_MPH(gps_speed);
956         break;
957       case VIK_UNITS_SPEED_KNOTS:
958         gps_speed = VIK_MPS_TO_KNOTS(gps_speed);
959         break;
960       default:
961         // VIK_UNITS_SPEED_METRES_PER_SECOND:
962         // No need to convert as already in m/s
963         break;
964       }
965       int x = MARGIN + widgets->profile_width * (VIK_TRACKPOINT(iter->data)->timestamp - beg_time) / dur;
966       int y = widgets->profile_height - widgets->profile_height*(gps_speed - mins)/(chunkss[widgets->cis]*LINES);
967       gdk_draw_rectangle(GDK_DRAWABLE(pix), gps_speed_gc, TRUE, x-2, y-2, 4, 4);
968     }
969   }
970
971   /* draw border */
972   gdk_draw_rectangle(GDK_DRAWABLE(pix), window->style->black_gc, FALSE, MARGIN, 0, widgets->profile_width-1, widgets->profile_height-1);
973
974   g_object_unref ( G_OBJECT(pix) );
975   g_object_unref ( G_OBJECT(gps_speed_gc) );
976
977 }
978 #undef LINES
979
980 /**
981  * Draw all graphs
982  */
983 static void draw_all_graphs ( GtkWidget *widget, gpointer *pass_along, gboolean resized )
984 {
985   VikTrack *tr = pass_along[0];
986   PropWidgets *widgets = pass_along[3];
987
988   // Draw graphs even if they are not visible
989
990   GList *child = NULL;
991   GtkWidget *image = NULL;
992   GtkWidget *window = gtk_widget_get_toplevel(widget);
993   gdouble pc = NAN;
994   gdouble pc_blob = NAN;
995
996   // Draw elevations
997   if (widgets->elev_box != NULL) {
998
999     // Saved image no longer any good as we've resized, so we remove it here
1000     if (resized && widgets->elev_graph_saved_img.img) {
1001       g_object_unref(widgets->elev_graph_saved_img.img);
1002       widgets->elev_graph_saved_img.img = NULL;
1003       widgets->elev_graph_saved_img.saved = FALSE;
1004     }
1005
1006     child = gtk_container_get_children(GTK_CONTAINER(widgets->elev_box));
1007     draw_elevations (GTK_WIDGET(child->data), tr, widgets );
1008
1009     image = GTK_WIDGET(child->data);
1010     g_list_free(child);
1011
1012     // Ensure marker or blob are redrawn if necessary
1013     if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
1014
1015       pc = tp_percentage_by_distance ( tr, widgets->marker_tp, widgets->track_length );
1016       gdouble x_blob = -MARGIN - 1.0; // i.e. Don't draw unless we get a valid value
1017       gint y_blob = 0;
1018       if (widgets->is_blob_drawn) {
1019         pc_blob = tp_percentage_by_distance ( tr, widgets->blob_tp, widgets->track_length );
1020         if (!isnan(pc_blob)) {
1021           x_blob = (pc_blob * widgets->profile_width);
1022         }
1023         y_blob = blobby_altitude (x_blob, widgets);
1024       }
1025
1026       gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
1027       if (!isnan(pc)) {
1028         marker_x = (pc * widgets->profile_width) + MARGIN;
1029       }
1030
1031       save_image_and_draw_graph_marks (image,
1032                                        marker_x,
1033                                        window->style->black_gc,
1034                                        x_blob+MARGIN,
1035                                        y_blob,
1036                                        &widgets->elev_graph_saved_img,
1037                                        widgets->profile_width,
1038                                        widgets->profile_height,
1039                                        &widgets->is_marker_drawn,
1040                                        &widgets->is_blob_drawn);
1041     }
1042   }
1043
1044   // Draw speeds
1045   if (widgets->speed_box != NULL) {
1046
1047     // Saved image no longer any good as we've resized
1048     if (resized && widgets->speed_graph_saved_img.img) {
1049       g_object_unref(widgets->speed_graph_saved_img.img);
1050       widgets->speed_graph_saved_img.img = NULL;
1051       widgets->speed_graph_saved_img.saved = FALSE;
1052     }
1053
1054     child = gtk_container_get_children(GTK_CONTAINER(widgets->speed_box));
1055     draw_vt (GTK_WIDGET(child->data), tr, widgets );
1056
1057     image = GTK_WIDGET(child->data);
1058     g_list_free(child);
1059
1060     // Ensure marker or blob are redrawn if necessary
1061     if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
1062
1063       pc = tp_percentage_by_time ( tr, widgets->marker_tp );
1064
1065       gdouble x_blob = -MARGIN - 1.0; // i.e. Don't draw unless we get a valid value
1066       gint    y_blob = 0;
1067       if (widgets->is_blob_drawn) {
1068         pc_blob = tp_percentage_by_time ( tr, widgets->blob_tp );
1069         if (!isnan(pc_blob)) {
1070           x_blob = (pc_blob * widgets->profile_width);
1071         }
1072          
1073         y_blob = blobby_speed (x_blob, widgets);
1074       }
1075
1076       gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
1077       if (!isnan(pc)) {
1078         marker_x = (pc * widgets->profile_width) + MARGIN;
1079       }
1080
1081       save_image_and_draw_graph_marks (image,
1082                                        marker_x,
1083                                        window->style->black_gc,
1084                                        x_blob+MARGIN,
1085                                        y_blob,
1086                                        &widgets->speed_graph_saved_img,
1087                                        widgets->profile_width,
1088                                        widgets->profile_height,
1089                                        &widgets->is_marker_drawn,
1090                                        &widgets->is_blob_drawn);
1091     }
1092   }
1093 }
1094
1095 /**
1096  * Configure/Resize the profile & speed/time images
1097  */
1098 static gboolean configure_event ( GtkWidget *widget, GdkEventConfigure *event, gpointer *pass_along )
1099 {
1100   PropWidgets *widgets = pass_along[3];
1101
1102   if (widgets->configure_dialog) {
1103     // Determine size offsets between dialog size and size for images
1104     // Only on the initialisation of the dialog
1105     widgets->profile_width_offset = event->width - widgets->profile_width;
1106     widgets->profile_height_offset = event->height - widgets->profile_height;
1107     widgets->configure_dialog = FALSE;
1108
1109     // Without this the settting, the dialog will only grow in vertical size - one can not then make it smaller!
1110     gtk_widget_set_size_request ( widget, widgets->profile_width+widgets->profile_width_offset, widgets->profile_height );
1111     // In fact this allows one to compress it a bit more vertically as I don't add on the height offset
1112   }
1113   else {
1114     widgets->profile_width_old = widgets->profile_width;
1115     widgets->profile_height_old = widgets->profile_height;
1116   }
1117
1118   // Now adjust From Dialog size to get image size
1119   widgets->profile_width = event->width - widgets->profile_width_offset;
1120   widgets->profile_height = event->height - widgets->profile_height_offset;
1121
1122   // ATM we receive configure_events when the dialog is moved and so no further action is necessary
1123   if ( !widgets->configure_dialog &&
1124        (widgets->profile_width_old == widgets->profile_width) && (widgets->profile_height_old == widgets->profile_height) )
1125     return FALSE;
1126
1127   // Draw stuff
1128   draw_all_graphs ( widget, pass_along, TRUE );
1129
1130   return FALSE;
1131 }
1132
1133 /**
1134  * Create height profile widgets including the image and callbacks
1135  */
1136 GtkWidget *vik_trw_layer_create_profile ( GtkWidget *window, VikTrack *tr, gpointer vlp, VikViewport *vvp, PropWidgets *widgets, gdouble *min_alt, gdouble *max_alt)
1137 {
1138   GdkPixmap *pix;
1139   GtkWidget *image;
1140   GtkWidget *eventbox;
1141   gpointer *pass_along;
1142
1143   // First allocation
1144   widgets->altitudes = vik_track_make_elevation_map ( tr, widgets->profile_width );
1145
1146   if ( widgets->altitudes == NULL ) {
1147     *min_alt = *max_alt = VIK_DEFAULT_ALTITUDE;
1148     return NULL;
1149   }
1150
1151   minmax_array(widgets->altitudes, min_alt, max_alt, TRUE, widgets->profile_width);
1152   
1153   pix = gdk_pixmap_new( window->window, widgets->profile_width + MARGIN, widgets->profile_height, -1 );
1154   image = gtk_image_new_from_pixmap ( pix, NULL );
1155
1156   g_object_unref ( G_OBJECT(pix) );
1157
1158   pass_along = g_malloc ( sizeof(gpointer) * 4 ); /* FIXME: mem leak -- never be freed */
1159   pass_along[0] = tr;
1160   pass_along[1] = vlp;
1161   pass_along[2] = vvp;
1162   pass_along[3] = widgets;
1163
1164   eventbox = gtk_event_box_new ();
1165   g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_profile_click), pass_along );
1166   g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_profile_move), pass_along );
1167   g_signal_connect_swapped ( G_OBJECT(eventbox), "destroy", G_CALLBACK(g_free), pass_along );
1168   gtk_container_add ( GTK_CONTAINER(eventbox), image );
1169   gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_STRUCTURE_MASK);
1170
1171   return eventbox;
1172 }
1173
1174 /**
1175  * Create speed/time widgets including the image and callbacks
1176  */
1177 GtkWidget *vik_trw_layer_create_vtdiag ( GtkWidget *window, VikTrack *tr, gpointer vlp, VikViewport *vvp, PropWidgets *widgets)
1178 {
1179   GdkPixmap *pix;
1180   GtkWidget *image;
1181   GtkWidget *eventbox;
1182   gpointer *pass_along;
1183
1184   // First allocation
1185   widgets->speeds = vik_track_make_speed_map ( tr, widgets->profile_width );
1186   if ( widgets->speeds == NULL )
1187     return NULL;
1188
1189   pass_along = g_malloc ( sizeof(gpointer) * 4 ); /* FIXME: mem leak -- never be freed */
1190   pass_along[0] = tr;
1191   pass_along[1] = vlp;
1192   pass_along[2] = vvp;
1193   pass_along[3] = widgets;
1194
1195   pix = gdk_pixmap_new( window->window, widgets->profile_width + MARGIN, widgets->profile_height, -1 );
1196   image = gtk_image_new_from_pixmap ( pix, NULL );
1197
1198 #if 0
1199   /* XXX this can go out, it's just a helpful dev tool */
1200   {
1201     int j;
1202     GdkGC **colors[8] = { window->style->bg_gc,
1203                           window->style->fg_gc,
1204                           window->style->light_gc,
1205                           window->style->dark_gc,
1206                           window->style->mid_gc,
1207                           window->style->text_gc,
1208                           window->style->base_gc,
1209                           window->style->text_aa_gc };
1210     for (i=0; i<5; i++) {
1211       for (j=0; j<8; j++) {
1212         gdk_draw_rectangle(GDK_DRAWABLE(pix), colors[j][i],
1213                            TRUE, i*20, j*20, 20, 20);
1214         gdk_draw_rectangle(GDK_DRAWABLE(pix), window->style->black_gc,
1215                            FALSE, i*20, j*20, 20, 20);
1216       }
1217     }
1218   }
1219 #endif
1220
1221   g_object_unref ( G_OBJECT(pix) );
1222
1223   eventbox = gtk_event_box_new ();
1224   g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_vt_click), pass_along );
1225   g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_vt_move), pass_along );
1226   g_signal_connect_swapped ( G_OBJECT(eventbox), "destroy", G_CALLBACK(g_free), pass_along );
1227   gtk_container_add ( GTK_CONTAINER(eventbox), image );
1228   gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
1229
1230   return eventbox;
1231 }
1232 #undef MARGIN
1233
1234 static void propwin_response_cb( GtkDialog *dialog, gint resp, PropWidgets *widgets)
1235 {
1236   VikTrack *tr = widgets->tr;
1237   VikTrwLayer *vtl = widgets->vtl;
1238   gboolean keep_dialog = FALSE;
1239
1240   /* FIXME: check and make sure the track still exists before doing anything to it */
1241   /* Note: destroying diaglog (eg, parent window exit) won't give "response" */
1242   switch (resp) {
1243     case GTK_RESPONSE_DELETE_EVENT: /* received delete event (not from buttons) */
1244     case GTK_RESPONSE_REJECT:
1245       break;
1246     case GTK_RESPONSE_ACCEPT:
1247       vik_track_set_comment(tr, gtk_entry_get_text(GTK_ENTRY(widgets->w_comment)));
1248       break;
1249     case VIK_TRW_LAYER_PROPWIN_REVERSE:
1250       vik_track_reverse(tr);
1251       vik_layer_emit_update ( VIK_LAYER(vtl) );
1252       break;
1253     case VIK_TRW_LAYER_PROPWIN_DEL_DUP:
1254       vik_track_remove_dup_points(tr);
1255       /* above operation could have deleted current_tp or last_tp */
1256       trw_layer_cancel_tps_of_track ( vtl, widgets->track_name );
1257       vik_layer_emit_update ( VIK_LAYER(vtl) );
1258       break;
1259     case VIK_TRW_LAYER_PROPWIN_SPLIT:
1260       {
1261         /* get new tracks, add them, resolve naming conflicts (free if cancel), and delete old. old can still exist on clipboard. */
1262         guint ntracks;
1263         VikTrack **tracks = vik_track_split_into_segments(tr, &ntracks);
1264         gchar *new_tr_name;
1265         guint i;
1266         for ( i = 0; i < ntracks; i++ )
1267         {
1268           g_assert ( tracks[i] );
1269           new_tr_name = g_strdup_printf("%s #%d", widgets->track_name, i+1);
1270           /* if ( (wp_exists) && (! overwrite) ) */
1271           /* don't need to upper case new_tr_name because old tr name was uppercase */
1272           if ( vik_trw_layer_get_track(vtl, new_tr_name ) && 
1273              ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl), "The track \"%s\" exists, do you wish to overwrite it?", new_tr_name ) ) )
1274           {
1275             gchar *new_new_tr_name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vik_trw_layer_get_tracks(vtl), NULL );
1276             g_free ( new_tr_name );
1277             if (new_new_tr_name)
1278               new_tr_name = new_new_tr_name;
1279             else
1280             {
1281               new_tr_name = NULL;
1282               vik_track_free ( tracks[i] );
1283             }
1284           }
1285           if ( new_tr_name )
1286             vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
1287         }
1288         if ( tracks )
1289         {
1290           g_free ( tracks );
1291           /* Don't let track destroy this dialog */
1292           vik_track_clear_property_dialog(tr);
1293           vik_trw_layer_delete_track ( vtl, widgets->track_name );
1294           vik_layer_emit_update ( VIK_LAYER(vtl) ); /* chase thru the hoops */
1295         }
1296       }
1297       break;
1298     case VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER:
1299       {
1300         GList *iter = tr->trackpoints;
1301         while ((iter = iter->next)) {
1302           if (widgets->marker_tp == VIK_TRACKPOINT(iter->data))
1303             break;
1304         }
1305         if (iter == NULL) {
1306           a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR,
1307                   _("Failed spliting track. Track unchanged"), NULL);
1308           keep_dialog = TRUE;
1309           break;
1310         }
1311
1312         gchar *r_name = g_strdup_printf("%s #R", widgets->track_name);
1313         if (vik_trw_layer_get_track(vtl, r_name ) && 
1314              ( ! a_dialog_yes_or_no( VIK_GTK_WINDOW_FROM_LAYER(vtl),
1315               "The track \"%s\" exists, do you wish to overwrite it?", r_name)))
1316         {
1317           gchar *new_r_name = a_dialog_new_track( VIK_GTK_WINDOW_FROM_LAYER(vtl), vik_trw_layer_get_tracks(vtl), NULL );
1318             if (new_r_name) {
1319               g_free( r_name );
1320               r_name = new_r_name;
1321             }
1322             else {
1323               a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_WARNING,
1324                   _("Operation Aborted. Track unchanged"), NULL);
1325               keep_dialog = TRUE;
1326               break;
1327             }
1328         }
1329         iter->prev->next = NULL;
1330         iter->prev = NULL;
1331         VikTrack *tr_right = vik_track_new();
1332         if ( tr->comment )
1333           vik_track_set_comment ( tr_right, tr->comment );
1334         tr_right->visible = tr->visible;
1335         tr_right->trackpoints = iter;
1336
1337         vik_trw_layer_add_track(vtl, r_name, tr_right);
1338         vik_layer_emit_update ( VIK_LAYER(vtl) );
1339       }
1340       break;
1341     default:
1342       fprintf(stderr, "DEBUG: unknown response\n");
1343       return;
1344   }
1345
1346   /* Keep same behaviour for now: destroy dialog if click on any button */
1347   if (!keep_dialog) {
1348     prop_widgets_free(widgets);
1349     vik_track_clear_property_dialog(tr);
1350     gtk_widget_destroy ( GTK_WIDGET(dialog) );
1351   }
1352 }
1353
1354 /**
1355  * Force a redraw when checkbutton has been toggled to show/hide that information
1356  */
1357 static void checkbutton_toggle_cb ( GtkToggleButton *togglebutton, gpointer *pass_along, gpointer dummy)
1358 {
1359   PropWidgets *widgets = pass_along[3];
1360   // Even though not resized, we'll pretend it is -
1361   //  as this invalidates the saved images (since the image may have changed)
1362   draw_all_graphs ( widgets->dialog, pass_along, TRUE);
1363 }
1364
1365 /**
1366  *  Create the widgets for the given graph tab
1367  */
1368 static GtkWidget *create_graph_page ( GtkWidget *graph,
1369                                       const gchar *markup,
1370                                       GtkWidget *value,
1371                                       const gchar *markup2,
1372                                       GtkWidget *value2,
1373                                       GtkWidget *checkbutton1,
1374                                       gboolean checkbutton1_default,
1375                                       GtkWidget *checkbutton2,
1376                                       gboolean checkbutton2_default )
1377 {
1378   GtkWidget *hbox = gtk_hbox_new ( FALSE, 10 );
1379   GtkWidget *vbox = gtk_vbox_new ( FALSE, 10 );
1380   GtkWidget *label = gtk_label_new (NULL);
1381   GtkWidget *label2 = gtk_label_new (NULL);
1382   gtk_box_pack_start (GTK_BOX(vbox), graph, FALSE, FALSE, 0);
1383   gtk_label_set_markup ( GTK_LABEL(label), markup );
1384   gtk_label_set_markup ( GTK_LABEL(label2), markup2 );
1385   gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0);
1386   gtk_box_pack_start (GTK_BOX(hbox), value, FALSE, FALSE, 0);
1387   gtk_box_pack_start (GTK_BOX(hbox), label2, FALSE, FALSE, 0);
1388   gtk_box_pack_start (GTK_BOX(hbox), value2, FALSE, FALSE, 0);
1389   if (checkbutton2) {
1390     gtk_box_pack_end (GTK_BOX(hbox), checkbutton2, FALSE, FALSE, 0);
1391     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbutton2), checkbutton2_default);
1392   }
1393   if (checkbutton1) {
1394     gtk_box_pack_end (GTK_BOX(hbox), checkbutton1, FALSE, FALSE, 0);
1395     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbutton1), checkbutton1_default);
1396   }
1397   gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1398
1399   return vbox;
1400 }
1401
1402 void vik_trw_layer_propwin_run ( GtkWindow *parent, VikTrwLayer *vtl, VikTrack *tr, gpointer vlp, gchar *track_name, VikViewport *vvp )
1403 {
1404   /* FIXME: free widgets when destroy signal received */
1405   PropWidgets *widgets = prop_widgets_new();
1406   widgets->vtl = vtl;
1407   widgets->tr = tr;
1408   widgets->profile_width  = PROPWIN_PROFILE_WIDTH;
1409   widgets->profile_height = PROPWIN_PROFILE_HEIGHT;
1410   widgets->track_name = track_name;
1411   gchar *title = g_strdup_printf(_("%s - Track Properties"), track_name);
1412   GtkWidget *dialog = gtk_dialog_new_with_buttons (title,
1413                          parent,
1414                          GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
1415                          GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1416                          _("Split at _Marker"), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER,
1417                          _("Split _Segments"), VIK_TRW_LAYER_PROPWIN_SPLIT,
1418                          _("_Reverse"),        VIK_TRW_LAYER_PROPWIN_REVERSE,
1419                          _("_Delete Dupl."),   VIK_TRW_LAYER_PROPWIN_DEL_DUP,
1420                          GTK_STOCK_OK,     GTK_RESPONSE_ACCEPT,
1421                          NULL);
1422   widgets->dialog = dialog;
1423   g_free(title);
1424   g_signal_connect(dialog, "response", G_CALLBACK(propwin_response_cb), widgets);
1425   GtkTable *table;
1426   gdouble tr_len;
1427   guint32 tp_count, seg_count;
1428
1429   gdouble min_alt, max_alt;
1430   widgets->elev_box = vik_trw_layer_create_profile(GTK_WIDGET(parent), tr, vlp, vvp, widgets, &min_alt, &max_alt);
1431   widgets->speed_box = vik_trw_layer_create_vtdiag(GTK_WIDGET(parent), tr, vlp, vvp, widgets);
1432   GtkWidget *graphs = gtk_notebook_new();
1433
1434   GtkWidget *content[20];
1435   int cnt;
1436   int i;
1437
1438   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>") };
1439   static gchar tmp_buf[50];
1440   gdouble tmp_speed;
1441
1442   cnt = 0;
1443   widgets->w_comment = gtk_entry_new ();
1444   if ( tr->comment )
1445     gtk_entry_set_text ( GTK_ENTRY(widgets->w_comment), tr->comment );
1446   g_signal_connect_swapped ( widgets->w_comment, "activate", G_CALLBACK(a_dialog_response_accept), GTK_DIALOG(dialog) );
1447   content[cnt++] = widgets->w_comment;
1448
1449   vik_units_distance_t dist_units = a_vik_get_units_distance ();
1450
1451   tr_len = widgets->track_length = vik_track_get_length(tr);
1452   switch (dist_units) {
1453   case VIK_UNITS_DISTANCE_KILOMETRES:
1454     g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km", tr_len/1000.0 );
1455     break;
1456   case VIK_UNITS_DISTANCE_MILES:
1457     g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f miles", VIK_METERS_TO_MILES(tr_len) );
1458     break;
1459   default:
1460     g_critical("Houston, we've had a problem. distance=%d", dist_units);
1461   }
1462   widgets->w_track_length = content[cnt++] = gtk_label_new ( tmp_buf );
1463
1464   tp_count = vik_track_get_tp_count(tr);
1465   g_snprintf(tmp_buf, sizeof(tmp_buf), "%u", tp_count );
1466   widgets->w_tp_count = content[cnt++] = gtk_label_new ( tmp_buf );
1467
1468   seg_count = vik_track_get_segment_count(tr) ;
1469   g_snprintf(tmp_buf, sizeof(tmp_buf), "%u", seg_count );
1470   widgets->w_segment_count = content[cnt++] = gtk_label_new ( tmp_buf );
1471
1472   g_snprintf(tmp_buf, sizeof(tmp_buf), "%lu", vik_track_get_dup_point_count(tr) );
1473   widgets->w_duptp_count = content[cnt++] = gtk_label_new ( tmp_buf );
1474
1475   vik_units_speed_t speed_units = a_vik_get_units_speed ();
1476   tmp_speed = vik_track_get_max_speed(tr);
1477   if ( tmp_speed == 0 )
1478     g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
1479   else {
1480     switch (speed_units) {
1481     case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
1482       g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km/h", VIK_MPS_TO_KPH(tmp_speed));
1483       break;
1484     case VIK_UNITS_SPEED_MILES_PER_HOUR:
1485       g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f mph", VIK_MPS_TO_MPH(tmp_speed));
1486       break;
1487     case VIK_UNITS_SPEED_METRES_PER_SECOND:
1488       g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m/s", tmp_speed );
1489       break;
1490     case VIK_UNITS_SPEED_KNOTS:
1491       g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f knots", VIK_MPS_TO_KNOTS(tmp_speed));
1492       break;
1493     default:
1494       g_snprintf (tmp_buf, sizeof(tmp_buf), "--" );
1495       g_critical("Houston, we've had a problem. speed=%d", speed_units);
1496     }
1497   }
1498   widgets->w_max_speed = content[cnt++] = gtk_label_new ( tmp_buf );
1499
1500   tmp_speed = vik_track_get_average_speed(tr);
1501   if ( tmp_speed == 0 )
1502     g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
1503   else {
1504     switch (speed_units) {
1505     case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
1506       g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km/h", VIK_MPS_TO_KPH(tmp_speed));
1507       break;
1508     case VIK_UNITS_SPEED_MILES_PER_HOUR:
1509       g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f mph", VIK_MPS_TO_MPH(tmp_speed));
1510       break;
1511     case VIK_UNITS_SPEED_METRES_PER_SECOND:
1512       g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m/s", tmp_speed );
1513       break;
1514     case VIK_UNITS_SPEED_KNOTS:
1515       g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f knots", VIK_MPS_TO_KNOTS(tmp_speed));
1516       break;
1517     default:
1518       g_snprintf (tmp_buf, sizeof(tmp_buf), "--" );
1519       g_critical("Houston, we've had a problem. speed=%d", speed_units);
1520     }
1521   }
1522   widgets->w_avg_speed = content[cnt++] = gtk_label_new ( tmp_buf );
1523
1524   switch (dist_units) {
1525   case VIK_UNITS_DISTANCE_KILOMETRES:
1526     // Even though kilometres, the average distance between points is going to be quite small so keep in metres
1527     g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m", (tp_count - seg_count) == 0 ? 0 : tr_len / ( tp_count - seg_count ) );
1528     break;
1529   case VIK_UNITS_DISTANCE_MILES:
1530     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 )) );
1531     break;
1532   default:
1533     g_critical("Houston, we've had a problem. distance=%d", dist_units);
1534   }
1535   widgets->w_avg_dist = content[cnt++] = gtk_label_new ( tmp_buf );
1536
1537   vik_units_height_t height_units = a_vik_get_units_height ();
1538   if ( min_alt == VIK_DEFAULT_ALTITUDE )
1539     g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
1540   else {
1541     switch (height_units) {
1542     case VIK_UNITS_HEIGHT_METRES:
1543       g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f m - %.0f m", min_alt, max_alt );
1544       break;
1545     case VIK_UNITS_HEIGHT_FEET:
1546       g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f feet - %.0f feet", VIK_METERS_TO_FEET(min_alt), VIK_METERS_TO_FEET(max_alt) );
1547       break;
1548     default:
1549       g_snprintf(tmp_buf, sizeof(tmp_buf), "--" );
1550       g_critical("Houston, we've had a problem. height=%d", height_units);
1551     }
1552   }
1553   widgets->w_elev_range = content[cnt++] = gtk_label_new ( tmp_buf );
1554
1555   vik_track_get_total_elevation_gain(tr, &max_alt, &min_alt );
1556   if ( min_alt == VIK_DEFAULT_ALTITUDE )
1557     g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
1558   else {
1559     switch (height_units) {
1560     case VIK_UNITS_HEIGHT_METRES:
1561       g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f m / %.0f m", max_alt, min_alt );
1562       break;
1563     case VIK_UNITS_HEIGHT_FEET:
1564       g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f feet / %.0f feet", VIK_METERS_TO_FEET(max_alt), VIK_METERS_TO_FEET(min_alt) );
1565       break;
1566     default:
1567       g_snprintf(tmp_buf, sizeof(tmp_buf), "--" );
1568       g_critical("Houston, we've had a problem. height=%d", height_units);
1569     }
1570   }
1571   widgets->w_elev_gain = content[cnt++] = gtk_label_new ( tmp_buf );
1572
1573 #if 0
1574 #define PACK(w) gtk_box_pack_start (GTK_BOX(right_vbox), w, FALSE, FALSE, 0);
1575   gtk_box_pack_start (GTK_BOX(right_vbox), e_cmt, FALSE, FALSE, 0); 
1576   PACK(l_len);
1577   PACK(l_tps);
1578   PACK(l_segs);
1579   PACK(l_dups);
1580   PACK(l_maxs);
1581   PACK(l_avgs);
1582   PACK(l_avgd);
1583   PACK(l_elev);
1584   PACK(l_galo);
1585 #undef PACK;
1586 #endif
1587
1588   if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->timestamp )
1589   {
1590     time_t t1, t2;
1591     t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1592     t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1593
1594     strncpy(tmp_buf, ctime(&t1), sizeof(tmp_buf));
1595     tmp_buf[sizeof(tmp_buf)-1] = 0;
1596     g_strchomp(tmp_buf);
1597     widgets->w_time_start = content[cnt++] = gtk_label_new(tmp_buf);
1598
1599     strncpy(tmp_buf, ctime(&t2), sizeof(tmp_buf));
1600     tmp_buf[sizeof(tmp_buf)-1] = 0;
1601     g_strchomp(tmp_buf);
1602     widgets->w_time_end = content[cnt++] = gtk_label_new(tmp_buf);
1603
1604     g_snprintf(tmp_buf, sizeof(tmp_buf), _("%d minutes"), (int)(t2-t1)/60);
1605     widgets->w_time_dur = content[cnt++] = gtk_label_new(tmp_buf);
1606   } else {
1607     widgets->w_time_start = content[cnt++] = gtk_label_new(_("No Data"));
1608     widgets->w_time_end = content[cnt++] = gtk_label_new(_("No Data"));
1609     widgets->w_time_dur = content[cnt++] = gtk_label_new(_("No Data"));
1610   }
1611
1612   table = GTK_TABLE(gtk_table_new (cnt, 2, FALSE));
1613   gtk_table_set_col_spacing (table, 0, 10);
1614   for (i=0; i<cnt; i++) {
1615     GtkWidget *label;
1616
1617     // Settings so the text positioning only moves around vertically when the dialog is resized
1618     // This also gives more room to see the track comment
1619     label = gtk_label_new(NULL);
1620     gtk_misc_set_alignment ( GTK_MISC(label), 1, 0.5 ); // Position text centrally in vertical plane
1621     gtk_label_set_markup ( GTK_LABEL(label), _(label_texts[i]) );
1622     gtk_table_attach ( table, label, 0, 1, i, i+1, GTK_FILL, GTK_SHRINK, 0, 0 );
1623     if (GTK_IS_MISC(content[i])) {
1624       gtk_misc_set_alignment ( GTK_MISC(content[i]), 0, 0.5 );
1625     }
1626     gtk_table_attach_defaults ( table, content[i], 1, 2, i, i+1 );
1627   }
1628
1629   gtk_notebook_append_page(GTK_NOTEBOOK(graphs), GTK_WIDGET(table), gtk_label_new(_("Statistics")));
1630
1631   gpointer *pass_along;
1632   pass_along = g_malloc ( sizeof(gpointer) * 4 ); /* FIXME: mem leak -- never be freed */
1633   pass_along[0] = tr;
1634   pass_along[1] = vlp;
1635   pass_along[2] = vvp;
1636   pass_along[3] = widgets;
1637
1638   if ( widgets->elev_box ) {
1639     GtkWidget *page = NULL;
1640     widgets->w_cur_dist = gtk_label_new(_("No Data"));
1641     widgets->w_cur_elevation = gtk_label_new(_("No Data"));
1642     widgets->w_show_dem = gtk_check_button_new_with_mnemonic(_("Show D_EM"));
1643     widgets->w_show_alt_gps_speed = gtk_check_button_new_with_mnemonic(_("Show _GPS Speed"));
1644     page = create_graph_page (widgets->elev_box,
1645                               _("<b>Track Distance:</b>"), widgets->w_cur_dist,
1646                               _("<b>Track Height:</b>"), widgets->w_cur_elevation,
1647                               widgets->w_show_dem, TRUE,
1648                               widgets->w_show_alt_gps_speed, TRUE);
1649     g_signal_connect (widgets->w_show_dem, "toggled", G_CALLBACK (checkbutton_toggle_cb), pass_along);
1650     g_signal_connect (widgets->w_show_alt_gps_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), pass_along);
1651     gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Elevation-distance")));
1652   }
1653
1654   if ( widgets->speed_box ) {
1655     GtkWidget *page = NULL;
1656     widgets->w_cur_time = gtk_label_new(_("No Data"));
1657     widgets->w_cur_speed = gtk_label_new(_("No Data"));
1658     widgets->w_show_gps_speed = gtk_check_button_new_with_mnemonic(_("Show _GPS Speed"));
1659     page = create_graph_page (widgets->speed_box,
1660                               _("<b>Track Time:</b>"), widgets->w_cur_time,
1661                               _("<b>Track Speed:</b>"), widgets->w_cur_speed,
1662                               widgets->w_show_gps_speed, TRUE,
1663                               NULL, FALSE);
1664     g_signal_connect (widgets->w_show_gps_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), pass_along);
1665     gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Speed-time")));
1666   }
1667
1668   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), graphs, FALSE, FALSE, 0);
1669
1670   gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER, FALSE);
1671   if (seg_count <= 1)
1672     gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), VIK_TRW_LAYER_PROPWIN_SPLIT, FALSE);
1673   if (vik_track_get_dup_point_count(tr) <= 0)
1674     gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), VIK_TRW_LAYER_PROPWIN_DEL_DUP, FALSE);
1675
1676   // On dialog realization configure_event casues the graphs to be initially drawn
1677   widgets->configure_dialog = TRUE;
1678   g_signal_connect ( G_OBJECT(dialog), "configure-event", G_CALLBACK (configure_event), pass_along);
1679
1680   vik_track_set_property_dialog(tr, dialog);
1681   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
1682   gtk_widget_show_all ( dialog );
1683 }