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