2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2007, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2007-2008, Quy Tonthat <qtonthat@gmail.com>
7 * Copyright (C) 2012, Rob Norris <rw_norris@hotmail.com>
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.
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.
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
34 #include <glib/gi18n.h>
39 #include "viktrwlayer.h"
40 #include "viktrwlayer_propwin.h"
43 #include "vikviewport.h" /* ugh */
44 #include <gdk-pixbuf/gdk-pixdata.h>
47 PROPWIN_GRAPH_TYPE_ELEVATION_DISTANCE,
48 PROPWIN_GRAPH_TYPE_GRADIENT_DISTANCE,
49 PROPWIN_GRAPH_TYPE_SPEED_TIME,
50 PROPWIN_GRAPH_TYPE_DISTANCE_TIME,
51 PROPWIN_GRAPH_TYPE_ELEVATION_TIME,
52 PROPWIN_GRAPH_TYPE_SPEED_DISTANCE,
53 PROPWIN_GRAPH_TYPE_END,
54 } VikPropWinGraphType_t;
56 /* (Hopefully!) Human friendly altitude grid sizes - note no fixed 'ratio' just numbers that look nice...*/
57 static const gdouble chunksa[] = {2.0, 5.0, 10.0, 15.0, 20.0,
58 25.0, 40.0, 50.0, 75.0, 100.0,
59 150.0, 200.0, 250.0, 375.0, 500.0,
60 750.0, 1000.0, 2000.0, 5000.0, 10000.0, 100000.0};
62 /* (Hopefully!) Human friendly gradient grid sizes - note no fixed 'ratio' just numbers that look nice...*/
63 static const gdouble chunksg[] = {1.0, 2.0, 3.0, 4.0, 5.0, 8.0, 10.0,
64 12.0, 15.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 75.0,
65 100.0, 150.0, 200.0, 250.0, 375.0, 500.0,
66 750.0, 1000.0, 10000.0, 100000.0};
67 // Normally gradients should range up to couple hundred precent at most,
68 // however there are possibilities of having points with no altitude after a point with a big altitude
69 // (such as places with invalid DEM values in otherwise mountainous regions) - thus giving huge negative gradients.
71 /* (Hopefully!) Human friendly grid sizes - note no fixed 'ratio' just numbers that look nice...*/
72 /* As need to cover walking speeds - have many low numbers (but also may go up to airplane speeds!) */
73 static const gdouble chunkss[] = {1.0, 2.0, 3.0, 4.0, 5.0, 8.0, 10.0,
74 15.0, 20.0, 25.0, 40.0, 50.0, 75.0,
75 100.0, 150.0, 200.0, 250.0, 375.0, 500.0,
76 750.0, 1000.0, 10000.0};
78 /* (Hopefully!) Human friendly distance grid sizes - note no fixed 'ratio' just numbers that look nice...*/
79 static const gdouble chunksd[] = {0.1, 0.5, 1.0, 2.0, 3.0, 4.0, 5.0, 8.0, 10.0,
80 15.0, 20.0, 25.0, 40.0, 50.0, 75.0,
81 100.0, 150.0, 200.0, 250.0, 375.0, 500.0,
82 750.0, 1000.0, 10000.0};
84 typedef struct _propsaved {
89 typedef struct _propwidgets {
90 gboolean configure_dialog;
97 gint profile_width_old;
98 gint profile_height_old;
99 gint profile_width_offset;
100 gint profile_height_offset;
102 GtkWidget *w_comment;
103 GtkWidget *w_description;
104 GtkWidget *w_track_length;
105 GtkWidget *w_tp_count;
106 GtkWidget *w_segment_count;
107 GtkWidget *w_duptp_count;
108 GtkWidget *w_max_speed;
109 GtkWidget *w_avg_speed;
110 GtkWidget *w_mvg_speed;
111 GtkWidget *w_avg_dist;
112 GtkWidget *w_elev_range;
113 GtkWidget *w_elev_gain;
114 GtkWidget *w_time_start;
115 GtkWidget *w_time_end;
116 GtkWidget *w_time_dur;
118 GtkWidget *w_namelabel;
119 GtkWidget *w_number_distlabels;
120 GtkWidget *w_cur_dist; /*< Current distance */
121 GtkWidget *w_cur_elevation;
122 GtkWidget *w_cur_gradient_dist; /*< Current distance on gradient graph */
123 GtkWidget *w_cur_gradient_gradient; /*< Current gradient on gradient graph */
124 GtkWidget *w_cur_time; /*< Current time */
125 GtkWidget *w_cur_speed;
126 GtkWidget *w_cur_dist_dist; /*< Current distance on distance graph */
127 GtkWidget *w_cur_dist_time; /*< Current time on distance graph */
128 GtkWidget *w_cur_elev_elev;
129 GtkWidget *w_cur_elev_time;
130 GtkWidget *w_cur_speed_dist;
131 GtkWidget *w_cur_speed_speed;
132 GtkWidget *w_show_dem;
133 GtkWidget *w_show_alt_gps_speed;
134 GtkWidget *w_show_gps_speed;
135 GtkWidget *w_show_gradient_gps_speed;
136 GtkWidget *w_show_dist_speed;
137 GtkWidget *w_show_elev_speed;
138 GtkWidget *w_show_sd_gps_speed;
139 gdouble track_length;
140 gdouble track_length_inc_gaps;
141 PropSaved elev_graph_saved_img;
142 PropSaved gradient_graph_saved_img;
143 PropSaved speed_graph_saved_img;
144 PropSaved dist_graph_saved_img;
145 PropSaved elev_time_graph_saved_img;
146 PropSaved speed_dist_graph_saved_img;
148 GtkWidget *gradient_box;
149 GtkWidget *speed_box;
151 GtkWidget *elev_time_box;
152 GtkWidget *speed_dist_box;
154 gdouble *ats; // altitudes in time
155 gdouble min_altitude;
156 gdouble max_altitude;
157 gdouble draw_min_altitude;
158 gdouble draw_min_altitude_time;
159 gint cia; // Chunk size Index into Altitudes
160 gint ciat; // Chunk size Index into Altitudes / Time
161 // NB cia & ciat are normally same value but sometimes not due to differing methods of altitude array creation
162 // thus also have draw_min_altitude for each altitude graph type
164 gdouble min_gradient;
165 gdouble max_gradient;
166 gdouble draw_min_gradient;
167 gint cig; // Chunk size Index into Gradients
169 gdouble *speeds_dist;
172 gdouble draw_min_speed;
173 gdouble max_speed_dist;
174 gint cis; // Chunk size Index into Speeds
175 gint cisd; // Chunk size Index into Speed/Distance
177 gint cid; // Chunk size Index into Distance
178 VikTrackpoint *marker_tp;
179 gboolean is_marker_drawn;
180 VikTrackpoint *blob_tp;
181 gboolean is_blob_drawn;
184 static PropWidgets *prop_widgets_new()
186 PropWidgets *widgets = g_malloc0(sizeof(PropWidgets));
191 static void prop_widgets_free(PropWidgets *widgets)
193 if (widgets->elev_graph_saved_img.img)
194 g_object_unref(widgets->elev_graph_saved_img.img);
195 if (widgets->gradient_graph_saved_img.img)
196 g_object_unref(widgets->gradient_graph_saved_img.img);
197 if (widgets->speed_graph_saved_img.img)
198 g_object_unref(widgets->speed_graph_saved_img.img);
199 if (widgets->dist_graph_saved_img.img)
200 g_object_unref(widgets->dist_graph_saved_img.img);
201 if (widgets->elev_time_graph_saved_img.img)
202 g_object_unref(widgets->elev_time_graph_saved_img.img);
203 if (widgets->speed_dist_graph_saved_img.img)
204 g_object_unref(widgets->speed_dist_graph_saved_img.img);
205 if (widgets->altitudes)
206 g_free(widgets->altitudes);
207 if (widgets->gradients)
208 g_free(widgets->gradients);
210 g_free(widgets->speeds);
211 if (widgets->distances)
212 g_free(widgets->distances);
214 g_free(widgets->ats);
215 if (widgets->speeds_dist)
216 g_free(widgets->speeds_dist);
220 static void minmax_array(const gdouble *array, gdouble *min, gdouble *max, gboolean NO_ALT_TEST, gint PROFILE_WIDTH)
225 for ( i=0; i < PROFILE_WIDTH; i++ ) {
226 if ( NO_ALT_TEST || (array[i] != VIK_DEFAULT_ALTITUDE) ) {
227 if ( array[i] > *max )
229 if ( array[i] < *min )
238 * get_new_min_and_chunk_index:
239 * Returns via pointers:
240 * the new minimum value to be used for the graph
241 * the index in to the chunk sizes array (ci = Chunk Index)
243 static void get_new_min_and_chunk_index (gdouble mina, gdouble maxa, const gdouble *chunks, size_t chunky, gdouble *new_min, gint *ci)
245 /* Get unitized chunk */
246 /* Find suitable chunk index */
248 gdouble diff_chunk = (maxa - mina)/LINES;
250 /* Loop through to find best match */
251 while (diff_chunk > chunks[*ci]) {
253 /* Last Resort Check */
254 if ( *ci == chunky ) {
255 // Use previous value and exit loop
261 /* Ensure adjusted minimum .. maximum covers mina->maxa */
263 // Now work out adjusted minimum point to the nearest lowest chunk divisor value
264 // When negative ensure logic uses lowest value
266 *new_min = (gdouble) ( (gint)((mina - chunks[*ci]) / chunks[*ci]) * chunks[*ci] );
268 *new_min = (gdouble) ( (gint)(mina / chunks[*ci]) * chunks[*ci] );
270 // Range not big enough - as new minimum has lowered
271 if ((*new_min + (chunks[*ci] * LINES) < maxa)) {
272 // Next chunk should cover it
273 if ( *ci < chunky-1 ) {
275 // Remember to adjust the minimum too...
277 *new_min = (gdouble) ( (gint)((mina - chunks[*ci]) / chunks[*ci]) * chunks[*ci] );
279 *new_min = (gdouble) ( (gint)(mina / chunks[*ci]) * chunks[*ci] );
284 static VikTrackpoint *set_center_at_graph_position(gdouble event_x,
293 VikTrackpoint *trackpoint;
294 gdouble x = event_x - img_width / 2 + PROFILE_WIDTH / 2 - MARGIN / 2;
297 if (x > PROFILE_WIDTH)
301 trackpoint = vik_track_get_closest_tp_by_percentage_time ( tr, (gdouble) x / PROFILE_WIDTH, NULL );
303 trackpoint = vik_track_get_closest_tp_by_percentage_dist ( tr, (gdouble) x / PROFILE_WIDTH, NULL );
306 VikCoord coord = trackpoint->coord;
308 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &coord );
309 vik_layers_panel_emit_update ( vlp );
312 /* since vlp not set, vvp should be valid instead! */
314 vik_viewport_set_center_coord ( vvp, &coord );
315 vik_layer_emit_update ( VIK_LAYER(vtl) );
322 * Returns whether the marker was drawn or not and whether the blob was drawn or not
324 static void save_image_and_draw_graph_marks (GtkWidget *image,
329 PropSaved *saved_img,
332 gboolean *marker_drawn,
333 gboolean *blob_drawn)
335 GdkPixmap *pix = NULL;
336 /* the pixmap = margin + graph area */
337 gtk_image_get_pixmap(GTK_IMAGE(image), &pix, NULL);
339 /* Restore previously saved image */
340 if (saved_img->saved) {
341 gdk_draw_image(GDK_DRAWABLE(pix), gc, saved_img->img, 0, 0, 0, 0, MARGIN+PROFILE_WIDTH, PROFILE_HEIGHT);
342 saved_img->saved = FALSE;
345 // ATM always save whole image - as anywhere could have changed
347 gdk_drawable_copy_to_image(GDK_DRAWABLE(pix), saved_img->img, 0, 0, 0, 0, MARGIN+PROFILE_WIDTH, PROFILE_HEIGHT);
349 saved_img->img = gdk_drawable_copy_to_image(GDK_DRAWABLE(pix), saved_img->img, 0, 0, 0, 0, MARGIN+PROFILE_WIDTH, PROFILE_HEIGHT);
350 saved_img->saved = TRUE;
352 if ((marker_x >= MARGIN) && (marker_x < (PROFILE_WIDTH + MARGIN))) {
353 gdk_draw_line (GDK_DRAWABLE(pix), gc, marker_x, 0, marker_x, image->allocation.height);
354 *marker_drawn = TRUE;
357 *marker_drawn = FALSE;
359 // Draw a square blob to indicate where we are on track for this graph
360 if ( (blob_x >= MARGIN) && (blob_x < (PROFILE_WIDTH + MARGIN)) && (blob_y < PROFILE_HEIGHT) ) {
361 gdk_draw_rectangle (GDK_DRAWABLE(pix), gc, TRUE, blob_x-3, blob_y-3, 6, 6);
367 // Anywhere on image could have changed
368 if (*marker_drawn || *blob_drawn)
369 gtk_widget_queue_draw(image);
373 * Return the percentage of how far a trackpoint is a long a track via the time method
375 static gdouble tp_percentage_by_time ( VikTrack *tr, VikTrackpoint *trackpoint )
378 if (trackpoint == NULL)
380 time_t t_start, t_end, t_total;
381 t_start = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
382 t_end = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
383 t_total = t_end - t_start;
384 pc = (gdouble)(trackpoint->timestamp - t_start)/t_total;
389 * Return the percentage of how far a trackpoint is a long a track via the distance method
391 static gdouble tp_percentage_by_distance ( VikTrack *tr, VikTrackpoint *trackpoint, gdouble track_length )
394 if (trackpoint == NULL)
398 for (iter = tr->trackpoints->next; iter != NULL; iter = iter->next) {
399 dist += vik_coord_diff(&(VIK_TRACKPOINT(iter->data)->coord),
400 &(VIK_TRACKPOINT(iter->prev->data)->coord));
401 /* Assuming trackpoint is not a copy */
402 if (trackpoint == VIK_TRACKPOINT(iter->data))
406 pc = dist/track_length;
410 static void track_graph_click( GtkWidget *event_box, GdkEventButton *event, PropWidgets *widgets, VikPropWinGraphType_t graph_type )
412 gboolean is_time_graph =
413 ( graph_type == PROPWIN_GRAPH_TYPE_SPEED_TIME ||
414 graph_type == PROPWIN_GRAPH_TYPE_DISTANCE_TIME ||
415 graph_type == PROPWIN_GRAPH_TYPE_ELEVATION_TIME );
417 VikTrackpoint *trackpoint = set_center_at_graph_position(event->x, event_box->allocation.width, widgets->vtl, widgets->vlp, widgets->vvp, widgets->tr, is_time_graph, widgets->profile_width);
418 // Unable to get the point so give up
419 if ( trackpoint == NULL ) {
420 gtk_dialog_set_response_sensitive(GTK_DIALOG(widgets->dialog), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER, FALSE);
424 widgets->marker_tp = trackpoint;
428 GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(event_box));
429 GtkWidget *graph_box;
430 PropSaved *graph_saved_img;
433 // Attempt to redraw marker on all graph types
435 for ( graphite = PROPWIN_GRAPH_TYPE_ELEVATION_DISTANCE;
436 graphite < PROPWIN_GRAPH_TYPE_END;
439 // Switch commonal variables to particular graph type
442 case PROPWIN_GRAPH_TYPE_ELEVATION_DISTANCE:
443 graph_box = widgets->elev_box;
444 graph_saved_img = &widgets->elev_graph_saved_img;
445 is_time_graph = FALSE;
447 case PROPWIN_GRAPH_TYPE_GRADIENT_DISTANCE:
448 graph_box = widgets->gradient_box;
449 graph_saved_img = &widgets->gradient_graph_saved_img;
450 is_time_graph = FALSE;
452 case PROPWIN_GRAPH_TYPE_SPEED_TIME:
453 graph_box = widgets->speed_box;
454 graph_saved_img = &widgets->speed_graph_saved_img;
455 is_time_graph = TRUE;
457 case PROPWIN_GRAPH_TYPE_DISTANCE_TIME:
458 graph_box = widgets->dist_box;
459 graph_saved_img = &widgets->dist_graph_saved_img;
460 is_time_graph = TRUE;
462 case PROPWIN_GRAPH_TYPE_ELEVATION_TIME:
463 graph_box = widgets->elev_time_box;
464 graph_saved_img = &widgets->elev_time_graph_saved_img;
465 is_time_graph = TRUE;
467 case PROPWIN_GRAPH_TYPE_SPEED_DISTANCE:
468 graph_box = widgets->speed_dist_box;
469 graph_saved_img = &widgets->speed_dist_graph_saved_img;
470 is_time_graph = FALSE;
474 // Commonal method of redrawing marker
477 child = gtk_container_get_children(GTK_CONTAINER(graph_box));
478 image = GTK_WIDGET(child->data);
481 pc = tp_percentage_by_time ( widgets->tr, trackpoint );
483 pc = tp_percentage_by_distance ( widgets->tr, trackpoint, widgets->track_length_inc_gaps );
486 gdouble marker_x = (pc * widgets->profile_width) + MARGIN;
487 save_image_and_draw_graph_marks(image,
489 gtk_widget_get_style(window)->black_gc,
490 -1, // Don't draw blob on clicks
493 widgets->profile_width,
494 widgets->profile_height,
495 &widgets->is_marker_drawn,
496 &widgets->is_blob_drawn);
502 gtk_dialog_set_response_sensitive(GTK_DIALOG(widgets->dialog), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER, widgets->is_marker_drawn);
505 static gboolean track_profile_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
507 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_ELEVATION_DISTANCE);
508 return TRUE; /* don't call other (further) callbacks */
511 static gboolean track_gradient_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
513 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_GRADIENT_DISTANCE);
514 return TRUE; /* don't call other (further) callbacks */
517 static gboolean track_vt_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
519 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_SPEED_TIME);
520 return TRUE; /* don't call other (further) callbacks */
523 static gboolean track_dt_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
525 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_DISTANCE_TIME);
526 return TRUE; /* don't call other (further) callbacks */
529 static gboolean track_et_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
531 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_ELEVATION_TIME);
532 return TRUE; /* don't call other (further) callbacks */
535 static gboolean track_sd_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
537 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_SPEED_DISTANCE);
538 return TRUE; /* don't call other (further) callbacks */
542 * Calculate y position for blob on elevation graph
544 static gint blobby_altitude ( gdouble x_blob, PropWidgets *widgets )
546 gint ix = (gint)x_blob;
547 // Ensure ix is inbounds
548 if (ix == widgets->profile_width)
551 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->altitudes[ix]-widgets->draw_min_altitude)/(chunksa[widgets->cia]*LINES);
557 * Calculate y position for blob on gradient graph
559 static gint blobby_gradient ( gdouble x_blob, PropWidgets *widgets )
561 gint ix = (gint)x_blob;
562 // Ensure ix is inbounds
563 if (ix == widgets->profile_width)
566 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->gradients[ix]-widgets->draw_min_gradient)/(chunksg[widgets->cig]*LINES);
572 * Calculate y position for blob on speed graph
574 static gint blobby_speed ( gdouble x_blob, PropWidgets *widgets )
576 gint ix = (gint)x_blob;
577 // Ensure ix is inbounds
578 if (ix == widgets->profile_width)
581 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->speeds[ix]-widgets->draw_min_speed)/(chunkss[widgets->cis]*LINES);
587 * Calculate y position for blob on distance graph
589 static gint blobby_distance ( gdouble x_blob, PropWidgets *widgets )
591 gint ix = (gint)x_blob;
592 // Ensure ix is inbounds
593 if (ix == widgets->profile_width)
596 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->distances[ix])/(chunksd[widgets->cid]*LINES);
597 //NB min distance is always 0, so no need to subtract that from this ______/
603 * Calculate y position for blob on elevation/time graph
605 static gint blobby_altitude_time ( gdouble x_blob, PropWidgets *widgets )
607 gint ix = (gint)x_blob;
608 // Ensure ix is inbounds
609 if (ix == widgets->profile_width)
612 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->ats[ix]-widgets->draw_min_altitude_time)/(chunksa[widgets->ciat]*LINES);
617 * Calculate y position for blob on speed/dist graph
619 static gint blobby_speed_dist ( gdouble x_blob, PropWidgets *widgets )
621 gint ix = (gint)x_blob;
622 // Ensure ix is inbounds
623 if (ix == widgets->profile_width)
626 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->speeds_dist[ix]-widgets->draw_min_speed)/(chunkss[widgets->cisd]*LINES);
632 void track_profile_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
634 int mouse_x, mouse_y;
635 GdkModifierType state;
638 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
642 gdouble x = mouse_x - event_box->allocation.width / 2 + widgets->profile_width / 2 - MARGIN / 2;
645 if (x > widgets->profile_width)
646 x = widgets->profile_width;
648 gdouble meters_from_start;
649 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_dist ( widgets->tr, (gdouble) x / widgets->profile_width, &meters_from_start );
650 if (trackpoint && widgets->w_cur_dist) {
651 static gchar tmp_buf[20];
652 vik_units_distance_t dist_units = a_vik_get_units_distance ();
653 switch (dist_units) {
654 case VIK_UNITS_DISTANCE_KILOMETRES:
655 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km", meters_from_start/1000.0);
657 case VIK_UNITS_DISTANCE_MILES:
658 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f miles", VIK_METERS_TO_MILES(meters_from_start) );
661 g_critical("Houston, we've had a problem. distance=%d", dist_units);
663 gtk_label_set_text(GTK_LABEL(widgets->w_cur_dist), tmp_buf);
666 // Show track elevation for this position - to the nearest whole number
667 if (trackpoint && widgets->w_cur_elevation) {
668 static gchar tmp_buf[20];
669 if (a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET)
670 g_snprintf(tmp_buf, sizeof(tmp_buf), "%d ft", (int)VIK_METERS_TO_FEET(trackpoint->altitude));
672 g_snprintf(tmp_buf, sizeof(tmp_buf), "%d m", (int)trackpoint->altitude);
673 gtk_label_set_text(GTK_LABEL(widgets->w_cur_elevation), tmp_buf);
676 widgets->blob_tp = trackpoint;
678 if ( widgets->altitudes == NULL )
681 GtkWidget *window = gtk_widget_get_toplevel (event_box);
682 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
683 GtkWidget *image = GTK_WIDGET(child->data);
685 gint y_blob = blobby_altitude (x, widgets);
687 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
688 if (widgets->is_marker_drawn) {
689 gdouble pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
691 marker_x = (pc * widgets->profile_width) + MARGIN;
695 save_image_and_draw_graph_marks (image,
697 gtk_widget_get_style(window)->black_gc,
700 &widgets->elev_graph_saved_img,
701 widgets->profile_width,
702 widgets->profile_height,
703 &widgets->is_marker_drawn,
704 &widgets->is_blob_drawn);
709 void track_gradient_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
711 int mouse_x, mouse_y;
712 GdkModifierType state;
715 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
719 gdouble x = mouse_x - event_box->allocation.width / 2 + widgets->profile_width / 2 - MARGIN / 2;
722 if (x > widgets->profile_width)
723 x = widgets->profile_width;
725 gdouble meters_from_start;
726 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_dist ( widgets->tr, (gdouble) x / widgets->profile_width, &meters_from_start );
727 if (trackpoint && widgets->w_cur_gradient_dist) {
728 static gchar tmp_buf[20];
729 vik_units_distance_t dist_units = a_vik_get_units_distance ();
730 switch (dist_units) {
731 case VIK_UNITS_DISTANCE_KILOMETRES:
732 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km", meters_from_start/1000.0);
734 case VIK_UNITS_DISTANCE_MILES:
735 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f miles", VIK_METERS_TO_MILES(meters_from_start) );
738 g_critical("Houston, we've had a problem. distance=%d", dist_units);
740 gtk_label_set_text(GTK_LABEL(widgets->w_cur_gradient_dist), tmp_buf);
743 // Show track gradient for this position - to the nearest whole number
744 if (trackpoint && widgets->w_cur_gradient_gradient) {
745 static gchar tmp_buf[20];
747 double gradient = widgets->gradients[(int) x];
749 g_snprintf(tmp_buf, sizeof(tmp_buf), "%d%%", (int)gradient);
750 gtk_label_set_text(GTK_LABEL(widgets->w_cur_gradient_gradient), tmp_buf);
753 widgets->blob_tp = trackpoint;
755 if ( widgets->gradients == NULL )
758 GtkWidget *window = gtk_widget_get_toplevel (event_box);
759 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
760 GtkWidget *image = GTK_WIDGET(child->data);
762 gint y_blob = blobby_gradient (x, widgets);
764 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
765 if (widgets->is_marker_drawn) {
766 gdouble pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
768 marker_x = (pc * widgets->profile_width) + MARGIN;
772 save_image_and_draw_graph_marks (image,
774 gtk_widget_get_style(window)->black_gc,
777 &widgets->gradient_graph_saved_img,
778 widgets->profile_width,
779 widgets->profile_height,
780 &widgets->is_marker_drawn,
781 &widgets->is_blob_drawn);
786 void track_vt_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
788 int mouse_x, mouse_y;
789 GdkModifierType state;
792 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
796 gdouble x = mouse_x - event_box->allocation.width / 2 + widgets->profile_width / 2 - MARGIN / 2;
799 if (x > widgets->profile_width)
800 x = widgets->profile_width;
802 time_t seconds_from_start;
803 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_time ( widgets->tr, (gdouble) x / widgets->profile_width, &seconds_from_start );
804 if (trackpoint && widgets->w_cur_time) {
805 static gchar tmp_buf[20];
807 h = seconds_from_start/3600;
808 m = (seconds_from_start - h*3600)/60;
809 s = seconds_from_start - (3600*h) - (60*m);
810 g_snprintf(tmp_buf, sizeof(tmp_buf), "%02d:%02d:%02d", h, m, s);
812 gtk_label_set_text(GTK_LABEL(widgets->w_cur_time), tmp_buf);
816 // Ensure ix is inbounds
817 if (ix == widgets->profile_width)
820 // Show track speed for this position
821 if (trackpoint && widgets->w_cur_speed) {
822 static gchar tmp_buf[20];
823 // Even if GPS speed available (trackpoint->speed), the text will correspond to the speed map shown
824 // No conversions needed as already in appropriate units
825 vik_units_speed_t speed_units = a_vik_get_units_speed ();
826 switch (speed_units) {
827 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
828 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f kph"), widgets->speeds[ix]);
830 case VIK_UNITS_SPEED_MILES_PER_HOUR:
831 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f mph"), widgets->speeds[ix]);
833 case VIK_UNITS_SPEED_KNOTS:
834 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f knots"), widgets->speeds[ix]);
837 // VIK_UNITS_SPEED_METRES_PER_SECOND:
838 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f m/s"), widgets->speeds[ix]);
841 gtk_label_set_text(GTK_LABEL(widgets->w_cur_speed), tmp_buf);
844 widgets->blob_tp = trackpoint;
846 if ( widgets->speeds == NULL )
849 GtkWidget *window = gtk_widget_get_toplevel (event_box);
850 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
851 GtkWidget *image = GTK_WIDGET(child->data);
853 gint y_blob = blobby_speed (x, widgets);
855 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
856 if (widgets->is_marker_drawn) {
857 gdouble pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
859 marker_x = (pc * widgets->profile_width) + MARGIN;
863 save_image_and_draw_graph_marks (image,
865 gtk_widget_get_style(window)->black_gc,
868 &widgets->speed_graph_saved_img,
869 widgets->profile_width,
870 widgets->profile_height,
871 &widgets->is_marker_drawn,
872 &widgets->is_blob_drawn);
878 * Update labels and blob marker on mouse moves in the distance/time graph
880 void track_dt_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
882 int mouse_x, mouse_y;
883 GdkModifierType state;
886 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
890 gdouble x = mouse_x - event_box->allocation.width / 2 + widgets->profile_width / 2 - MARGIN / 2;
893 if (x > widgets->profile_width)
894 x = widgets->profile_width;
896 time_t seconds_from_start;
897 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_time ( widgets->tr, (gdouble) x / widgets->profile_width, &seconds_from_start );
898 if (trackpoint && widgets->w_cur_dist_time) {
899 static gchar tmp_buf[20];
901 h = seconds_from_start/3600;
902 m = (seconds_from_start - h*3600)/60;
903 s = seconds_from_start - (3600*h) - (60*m);
904 g_snprintf(tmp_buf, sizeof(tmp_buf), "%02d:%02d:%02d", h, m, s);
906 gtk_label_set_text(GTK_LABEL(widgets->w_cur_dist_time), tmp_buf);
910 // Ensure ix is inbounds
911 if (ix == widgets->profile_width)
914 if (trackpoint && widgets->w_cur_dist_dist) {
915 static gchar tmp_buf[20];
916 if ( a_vik_get_units_distance () == VIK_UNITS_DISTANCE_MILES )
917 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f miles", widgets->distances[ix]);
919 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km", widgets->distances[ix]);
920 gtk_label_set_text(GTK_LABEL(widgets->w_cur_dist_dist), tmp_buf);
923 widgets->blob_tp = trackpoint;
925 if ( widgets->distances == NULL )
928 GtkWidget *window = gtk_widget_get_toplevel (event_box);
929 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
930 GtkWidget *image = GTK_WIDGET(child->data);
932 gint y_blob = blobby_distance (x, widgets);
934 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
935 if (widgets->is_marker_drawn) {
936 gdouble pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
938 marker_x = (pc * widgets->profile_width) + MARGIN;
942 save_image_and_draw_graph_marks (image,
944 gtk_widget_get_style(window)->black_gc,
947 &widgets->dist_graph_saved_img,
948 widgets->profile_width,
949 widgets->profile_height,
950 &widgets->is_marker_drawn,
951 &widgets->is_blob_drawn);
957 * Update labels and blob marker on mouse moves in the elevation/time graph
959 void track_et_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
961 int mouse_x, mouse_y;
962 GdkModifierType state;
965 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
969 gdouble x = mouse_x - event_box->allocation.width / 2 + widgets->profile_width / 2 - MARGIN / 2;
972 if (x > widgets->profile_width)
973 x = widgets->profile_width;
975 time_t seconds_from_start;
976 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_time ( widgets->tr, (gdouble) x / widgets->profile_width, &seconds_from_start );
977 if (trackpoint && widgets->w_cur_elev_time) {
978 static gchar tmp_buf[20];
980 h = seconds_from_start/3600;
981 m = (seconds_from_start - h*3600)/60;
982 s = seconds_from_start - (3600*h) - (60*m);
983 g_snprintf(tmp_buf, sizeof(tmp_buf), "%02d:%02d:%02d", h, m, s);
985 gtk_label_set_text(GTK_LABEL(widgets->w_cur_elev_time), tmp_buf);
989 // Ensure ix is inbounds
990 if (ix == widgets->profile_width)
993 if (trackpoint && widgets->w_cur_elev_elev) {
994 static gchar tmp_buf[20];
995 if (a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET)
996 g_snprintf(tmp_buf, sizeof(tmp_buf), "%d ft", (int)VIK_METERS_TO_FEET(trackpoint->altitude));
998 g_snprintf(tmp_buf, sizeof(tmp_buf), "%d m", (int)trackpoint->altitude);
999 gtk_label_set_text(GTK_LABEL(widgets->w_cur_elev_elev), tmp_buf);
1002 widgets->blob_tp = trackpoint;
1004 if ( widgets->ats == NULL )
1007 GtkWidget *window = gtk_widget_get_toplevel (event_box);
1008 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
1009 GtkWidget *image = GTK_WIDGET(child->data);
1011 gint y_blob = blobby_altitude_time (x, widgets);
1013 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
1014 if (widgets->is_marker_drawn) {
1015 gdouble pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
1017 marker_x = (pc * widgets->profile_width) + MARGIN;
1021 save_image_and_draw_graph_marks (image,
1023 gtk_widget_get_style(window)->black_gc,
1026 &widgets->elev_time_graph_saved_img,
1027 widgets->profile_width,
1028 widgets->profile_height,
1029 &widgets->is_marker_drawn,
1030 &widgets->is_blob_drawn);
1035 void track_sd_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
1037 int mouse_x, mouse_y;
1038 GdkModifierType state;
1041 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
1045 gdouble x = mouse_x - event_box->allocation.width / 2 + widgets->profile_width / 2 - MARGIN / 2;
1048 if (x > widgets->profile_width)
1049 x = widgets->profile_width;
1051 gdouble meters_from_start;
1052 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_dist ( widgets->tr, (gdouble) x / widgets->profile_width, &meters_from_start );
1053 if (trackpoint && widgets->w_cur_speed_dist) {
1054 static gchar tmp_buf[20];
1055 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1056 switch (dist_units) {
1057 case VIK_UNITS_DISTANCE_KILOMETRES:
1058 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km", meters_from_start/1000.0);
1060 case VIK_UNITS_DISTANCE_MILES:
1061 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f miles", VIK_METERS_TO_MILES(meters_from_start) );
1064 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1066 gtk_label_set_text(GTK_LABEL(widgets->w_cur_speed_dist), tmp_buf);
1070 // Ensure ix is inbounds
1071 if (ix == widgets->profile_width)
1074 if ( widgets->speeds_dist == NULL )
1077 // Show track speed for this position
1078 if (widgets->w_cur_speed_speed) {
1079 static gchar tmp_buf[20];
1080 // Even if GPS speed available (trackpoint->speed), the text will correspond to the speed map shown
1081 // No conversions needed as already in appropriate units
1082 vik_units_speed_t speed_units = a_vik_get_units_speed ();
1083 switch (speed_units) {
1084 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
1085 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f kph"), widgets->speeds_dist[ix]);
1087 case VIK_UNITS_SPEED_MILES_PER_HOUR:
1088 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f mph"), widgets->speeds_dist[ix]);
1090 case VIK_UNITS_SPEED_KNOTS:
1091 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f knots"), widgets->speeds_dist[ix]);
1094 // VIK_UNITS_SPEED_METRES_PER_SECOND:
1095 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f m/s"), widgets->speeds_dist[ix]);
1098 gtk_label_set_text(GTK_LABEL(widgets->w_cur_speed_speed), tmp_buf);
1101 widgets->blob_tp = trackpoint;
1103 GtkWidget *window = gtk_widget_get_toplevel (event_box);
1104 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
1105 GtkWidget *image = GTK_WIDGET(child->data);
1107 gint y_blob = blobby_speed_dist (x, widgets);
1109 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
1110 if (widgets->is_marker_drawn) {
1111 gdouble pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
1113 marker_x = (pc * widgets->profile_width) + MARGIN;
1117 save_image_and_draw_graph_marks (image,
1119 gtk_widget_get_style(window)->black_gc,
1122 &widgets->speed_dist_graph_saved_img,
1123 widgets->profile_width,
1124 widgets->profile_height,
1125 &widgets->is_marker_drawn,
1126 &widgets->is_blob_drawn);
1132 * Draws DEM points and a respresentative speed on the supplied pixmap
1133 * (which is the elevations graph)
1135 static void draw_dem_alt_speed_dist(VikTrack *tr,
1141 gdouble max_speed_in,
1150 gdouble max_speed = 0;
1151 gdouble total_length = vik_track_get_length_including_gaps(tr);
1153 // Calculate the max speed factor
1155 max_speed = max_speed_in * 110 / 100;
1158 for (iter = tr->trackpoints->next; iter; iter = iter->next) {
1160 dist += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
1161 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
1162 x = (width * dist)/total_length + margin;
1164 gint16 elev = a_dems_get_elev_by_coord(&(VIK_TRACKPOINT(iter->data)->coord), VIK_DEM_INTERPOL_BEST);
1166 if ( elev != VIK_DEM_INVALID_ELEVATION ) {
1167 // Convert into height units
1168 if (a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET)
1169 elev = VIK_METERS_TO_FEET(elev);
1170 // No conversion needed if already in metres
1172 // consider chunk size
1173 int y_alt = height - ((height * elev)/(chunksa[cia]*LINES) );
1174 gdk_draw_rectangle(GDK_DRAWABLE(pix), alt_gc, TRUE, x-2, y_alt-2, 4, 4);
1178 // This is just a speed indicator - no actual values can be inferred by user
1179 if (!isnan(VIK_TRACKPOINT(iter->data)->speed)) {
1180 int y_speed = height - (height * VIK_TRACKPOINT(iter->data)->speed)/max_speed;
1181 gdk_draw_rectangle(GDK_DRAWABLE(pix), speed_gc, TRUE, x-2, y_speed-2, 4, 4);
1190 * A common way to draw the grid with y axis labels
1193 static void draw_grid ( GtkWidget *window, GtkWidget *image, PropWidgets *widgets, GdkPixmap *pix, gchar *ss, gint i )
1195 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(image), NULL);
1197 pango_layout_set_alignment (pl, PANGO_ALIGN_RIGHT);
1198 pango_layout_set_font_description (pl, gtk_widget_get_style(window)->font_desc);
1200 gchar *label_markup = g_strdup_printf ( "<span size=\"small\">%s</span>", ss );
1201 pango_layout_set_markup ( pl, label_markup, -1 );
1202 g_free ( label_markup );
1205 pango_layout_get_pixel_size ( pl, &w, &h );
1207 gdk_draw_layout ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->fg_gc[0], MARGIN-w-3,
1208 CLAMP((int)i*widgets->profile_height/LINES - h/2, 0, widgets->profile_height-h), pl );
1210 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[0],
1211 MARGIN, widgets->profile_height/LINES * i, MARGIN + widgets->profile_width, widgets->profile_height/LINES * i );
1212 g_object_unref ( G_OBJECT ( pl ) );
1217 * Draw just the height profile image
1219 static void draw_elevations (GtkWidget *image, VikTrack *tr, PropWidgets *widgets )
1229 // Free previous allocation
1230 if ( widgets->altitudes )
1231 g_free ( widgets->altitudes );
1233 widgets->altitudes = vik_track_make_elevation_map ( tr, widgets->profile_width );
1235 if ( widgets->altitudes == NULL )
1238 // Convert into appropriate units
1239 vik_units_height_t height_units = a_vik_get_units_height ();
1240 if ( height_units == VIK_UNITS_HEIGHT_FEET ) {
1241 // Convert altitudes into feet units
1242 for ( i = 0; i < widgets->profile_width; i++ ) {
1243 widgets->altitudes[i] = VIK_METERS_TO_FEET(widgets->altitudes[i]);
1246 // Otherwise leave in metres
1248 minmax_array(widgets->altitudes, &widgets->min_altitude, &widgets->max_altitude, TRUE, widgets->profile_width);
1250 get_new_min_and_chunk_index (widgets->min_altitude, widgets->max_altitude, chunksa, G_N_ELEMENTS(chunksa), &widgets->draw_min_altitude, &widgets->cia);
1253 mina = widgets->draw_min_altitude;
1254 maxa = widgets->max_altitude;
1256 window = gtk_widget_get_toplevel (widgets->elev_box);
1258 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width + MARGIN, widgets->profile_height, -1 );
1260 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
1262 no_alt_info = gdk_gc_new ( gtk_widget_get_window(window) );
1263 gdk_color_parse ( "yellow", &color );
1264 gdk_gc_set_rgb_fg_color ( no_alt_info, &color);
1266 /* clear the image */
1267 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->bg_gc[0],
1268 TRUE, 0, 0, MARGIN, widgets->profile_height);
1269 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->mid_gc[0],
1270 TRUE, MARGIN, 0, widgets->profile_width, widgets->profile_height);
1273 for (i=0; i<=LINES; i++) {
1276 switch (height_units) {
1277 case VIK_UNITS_HEIGHT_METRES:
1278 sprintf(s, "%8dm", (int)(mina + (LINES-i)*chunksa[widgets->cia]));
1280 case VIK_UNITS_HEIGHT_FEET:
1281 // NB values already converted into feet
1282 sprintf(s, "%8dft", (int)(mina + (LINES-i)*chunksa[widgets->cia]));
1286 g_critical("Houston, we've had a problem. height=%d", height_units);
1289 draw_grid ( window, image, widgets, pix, s, i );
1292 /* draw elevations */
1293 for ( i = 0; i < widgets->profile_width; i++ )
1294 if ( widgets->altitudes[i] == VIK_DEFAULT_ALTITUDE )
1295 gdk_draw_line ( GDK_DRAWABLE(pix), no_alt_info,
1296 i + MARGIN, 0, i + MARGIN, widgets->profile_height );
1298 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
1299 i + MARGIN, widgets->profile_height, i + MARGIN, widgets->profile_height-widgets->profile_height*(widgets->altitudes[i]-mina)/(chunksa[widgets->cia]*LINES) );
1301 if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_dem)) ||
1302 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_alt_gps_speed)) ) {
1304 GdkGC *dem_alt_gc = gdk_gc_new ( gtk_widget_get_window(window) );
1305 GdkGC *gps_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
1307 gdk_color_parse ( "green", &color );
1308 gdk_gc_set_rgb_fg_color ( dem_alt_gc, &color);
1310 gdk_color_parse ( "red", &color );
1311 gdk_gc_set_rgb_fg_color ( gps_speed_gc, &color);
1313 // Ensure somekind of max speed when not set
1314 if ( widgets->max_speed < 0.01 )
1315 widgets->max_speed = vik_track_get_max_speed(tr);
1317 draw_dem_alt_speed_dist(tr,
1325 widgets->profile_width,
1326 widgets->profile_height,
1328 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_dem)),
1329 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_alt_gps_speed)));
1331 g_object_unref ( G_OBJECT(dem_alt_gc) );
1332 g_object_unref ( G_OBJECT(gps_speed_gc) );
1336 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN, 0, widgets->profile_width-1, widgets->profile_height-1);
1338 g_object_unref ( G_OBJECT(pix) );
1339 g_object_unref ( G_OBJECT(no_alt_info) );
1343 * Draws representative speed on the supplied pixmap
1344 * (which is the gradients graph)
1346 static void draw_speed_dist(VikTrack *tr,
1349 gdouble max_speed_in,
1356 gdouble max_speed = 0;
1357 gdouble total_length = vik_track_get_length_including_gaps(tr);
1359 // Calculate the max speed factor
1361 max_speed = max_speed_in * 110 / 100;
1364 for (iter = tr->trackpoints->next; iter; iter = iter->next) {
1366 dist += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
1367 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
1368 x = (width * dist)/total_length + margin;
1370 // This is just a speed indicator - no actual values can be inferred by user
1371 if (!isnan(VIK_TRACKPOINT(iter->data)->speed)) {
1372 int y_speed = height - (height * VIK_TRACKPOINT(iter->data)->speed)/max_speed;
1373 gdk_draw_rectangle(GDK_DRAWABLE(pix), speed_gc, TRUE, x-2, y_speed-2, 4, 4);
1380 * Draw just the gradient image
1382 static void draw_gradients (GtkWidget *image, VikTrack *tr, PropWidgets *widgets )
1391 // Free previous allocation
1392 if ( widgets->gradients )
1393 g_free ( widgets->gradients );
1395 widgets->gradients = vik_track_make_gradient_map ( tr, widgets->profile_width );
1397 if ( widgets->gradients == NULL )
1400 minmax_array(widgets->gradients, &widgets->min_gradient, &widgets->max_gradient, TRUE, widgets->profile_width);
1402 get_new_min_and_chunk_index (widgets->min_gradient, widgets->max_gradient, chunksg, G_N_ELEMENTS(chunksg), &widgets->draw_min_gradient, &widgets->cig);
1405 mina = widgets->draw_min_gradient;
1407 window = gtk_widget_get_toplevel (widgets->gradient_box);
1409 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width + MARGIN, widgets->profile_height, -1 );
1411 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
1413 /* clear the image */
1414 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->bg_gc[0],
1415 TRUE, 0, 0, MARGIN, widgets->profile_height);
1416 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->mid_gc[0],
1417 TRUE, MARGIN, 0, widgets->profile_width, widgets->profile_height);
1420 for (i=0; i<=LINES; i++) {
1423 sprintf(s, "%8d%%", (int)(mina + (LINES-i)*chunksg[widgets->cig]));
1425 draw_grid ( window, image, widgets, pix, s, i );
1428 /* draw gradients */
1429 for ( i = 0; i < widgets->profile_width; i++ )
1430 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
1431 i + MARGIN, widgets->profile_height, i + MARGIN, widgets->profile_height-widgets->profile_height*(widgets->gradients[i]-mina)/(chunksg[widgets->cig]*LINES) );
1433 if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_gradient_gps_speed)) ) {
1434 GdkGC *gps_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
1436 gdk_color_parse ( "red", &color );
1437 gdk_gc_set_rgb_fg_color ( gps_speed_gc, &color);
1439 // Ensure somekind of max speed when not set
1440 if ( widgets->max_speed < 0.01 )
1441 widgets->max_speed = vik_track_get_max_speed(tr);
1447 widgets->profile_width,
1448 widgets->profile_height,
1450 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_alt_gps_speed)));
1452 g_object_unref ( G_OBJECT(gps_speed_gc) );
1456 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN, 0, widgets->profile_width-1, widgets->profile_height-1);
1458 g_object_unref ( G_OBJECT(pix) );
1462 * Draw just the speed (velocity)/time image
1464 static void draw_vt ( GtkWidget *image, VikTrack *tr, PropWidgets *widgets)
1471 // Free previous allocation
1472 if ( widgets->speeds )
1473 g_free ( widgets->speeds );
1475 widgets->speeds = vik_track_make_speed_map ( tr, widgets->profile_width );
1476 if ( widgets->speeds == NULL )
1479 // Convert into appropriate units
1480 vik_units_speed_t speed_units = a_vik_get_units_speed ();
1481 switch (speed_units) {
1482 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
1483 for ( i = 0; i < widgets->profile_width; i++ ) {
1484 widgets->speeds[i] = VIK_MPS_TO_KPH(widgets->speeds[i]);
1487 case VIK_UNITS_SPEED_MILES_PER_HOUR:
1488 for ( i = 0; i < widgets->profile_width; i++ ) {
1489 widgets->speeds[i] = VIK_MPS_TO_MPH(widgets->speeds[i]);
1492 case VIK_UNITS_SPEED_KNOTS:
1493 for ( i = 0; i < widgets->profile_width; i++ ) {
1494 widgets->speeds[i] = VIK_MPS_TO_KNOTS(widgets->speeds[i]);
1498 // VIK_UNITS_SPEED_METRES_PER_SECOND:
1499 // No need to convert as already in m/s
1503 window = gtk_widget_get_toplevel (widgets->speed_box);
1505 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width + MARGIN, widgets->profile_height, -1 );
1507 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
1509 minmax_array(widgets->speeds, &widgets->min_speed, &widgets->max_speed, FALSE, widgets->profile_width);
1510 if (widgets->min_speed < 0.0)
1511 widgets->min_speed = 0; /* splines sometimes give negative speeds */
1513 /* Find suitable chunk index */
1514 get_new_min_and_chunk_index (widgets->min_speed, widgets->max_speed, chunkss, G_N_ELEMENTS(chunkss), &widgets->draw_min_speed, &widgets->cis);
1517 mins = widgets->draw_min_speed;
1519 /* clear the image */
1520 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->bg_gc[0],
1521 TRUE, 0, 0, MARGIN, widgets->profile_height);
1522 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->mid_gc[0],
1523 TRUE, MARGIN, 0, widgets->profile_width, widgets->profile_height);
1526 for (i=0; i<=LINES; i++) {
1529 // NB: No need to convert here anymore as numbers are in the appropriate units
1530 switch (speed_units) {
1531 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
1532 sprintf(s, "%8dkm/h", (int)(mins + (LINES-i)*chunkss[widgets->cis]));
1534 case VIK_UNITS_SPEED_MILES_PER_HOUR:
1535 sprintf(s, "%8dmph", (int)(mins + (LINES-i)*chunkss[widgets->cis]));
1537 case VIK_UNITS_SPEED_METRES_PER_SECOND:
1538 sprintf(s, "%8dm/s", (int)(mins + (LINES-i)*chunkss[widgets->cis]));
1540 case VIK_UNITS_SPEED_KNOTS:
1541 sprintf(s, "%8dknots", (int)(mins + (LINES-i)*chunkss[widgets->cis]));
1545 g_critical("Houston, we've had a problem. speed=%d", speed_units);
1548 draw_grid ( window, image, widgets, pix, s, i );
1553 for ( i = 0; i < widgets->profile_width; i++ )
1554 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
1555 i + MARGIN, widgets->profile_height, i + MARGIN, widgets->profile_height-widgets->profile_height*(widgets->speeds[i]-mins)/(chunkss[widgets->cis]*LINES) );
1558 if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->w_show_gps_speed)) ) {
1560 GdkGC *gps_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
1562 gdk_color_parse ( "red", &color );
1563 gdk_gc_set_rgb_fg_color ( gps_speed_gc, &color);
1565 time_t beg_time = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1566 time_t dur = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp - beg_time;
1569 for (iter = tr->trackpoints; iter; iter = iter->next) {
1570 gdouble gps_speed = VIK_TRACKPOINT(iter->data)->speed;
1571 if (isnan(gps_speed))
1573 switch (speed_units) {
1574 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
1575 gps_speed = VIK_MPS_TO_KPH(gps_speed);
1577 case VIK_UNITS_SPEED_MILES_PER_HOUR:
1578 gps_speed = VIK_MPS_TO_MPH(gps_speed);
1580 case VIK_UNITS_SPEED_KNOTS:
1581 gps_speed = VIK_MPS_TO_KNOTS(gps_speed);
1584 // VIK_UNITS_SPEED_METRES_PER_SECOND:
1585 // No need to convert as already in m/s
1588 int x = MARGIN + widgets->profile_width * (VIK_TRACKPOINT(iter->data)->timestamp - beg_time) / dur;
1589 int y = widgets->profile_height - widgets->profile_height*(gps_speed - mins)/(chunkss[widgets->cis]*LINES);
1590 gdk_draw_rectangle(GDK_DRAWABLE(pix), gps_speed_gc, TRUE, x-2, y-2, 4, 4);
1592 g_object_unref ( G_OBJECT(gps_speed_gc) );
1596 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN, 0, widgets->profile_width-1, widgets->profile_height-1);
1598 g_object_unref ( G_OBJECT(pix) );
1602 * Draw just the distance/time image
1604 static void draw_dt ( GtkWidget *image, VikTrack *tr, PropWidgets *widgets )
1611 // Free previous allocation
1612 if ( widgets->distances )
1613 g_free ( widgets->distances );
1615 widgets->distances = vik_track_make_distance_map ( tr, widgets->profile_width );
1616 if ( widgets->distances == NULL )
1619 // Convert into appropriate units
1620 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1621 if ( dist_units == VIK_UNITS_DISTANCE_MILES ) {
1622 for ( i = 0; i < widgets->profile_width; i++ ) {
1623 widgets->distances[i] = VIK_METERS_TO_MILES(widgets->distances[i]);
1627 // Metres - but want in kms
1628 for ( i = 0; i < widgets->profile_width; i++ ) {
1629 widgets->distances[i] = widgets->distances[i]/1000.0;
1633 window = gtk_widget_get_toplevel (widgets->dist_box);
1635 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width + MARGIN, widgets->profile_height, -1 );
1637 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
1639 // easy to work out min / max of distance!
1641 // mind = 0.0; - Thus not used
1642 if ( dist_units == VIK_UNITS_DISTANCE_MILES )
1643 maxd = VIK_METERS_TO_MILES(vik_track_get_length_including_gaps (tr));
1645 maxd = vik_track_get_length_including_gaps (tr) / 1000.0;
1647 /* Find suitable chunk index */
1648 gdouble dummy = 0.0; // expect this to remain the same! (not that it's used)
1649 get_new_min_and_chunk_index (0, maxd, chunksd, G_N_ELEMENTS(chunksd), &dummy, &widgets->cid);
1651 /* clear the image */
1652 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->bg_gc[0],
1653 TRUE, 0, 0, MARGIN, widgets->profile_height);
1654 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->mid_gc[0],
1655 TRUE, MARGIN, 0, widgets->profile_width, widgets->profile_height);
1658 for (i=0; i<=LINES; i++) {
1661 if ( dist_units == VIK_UNITS_DISTANCE_MILES )
1662 sprintf(s, _("%.1f miles"), ((LINES-i)*chunksd[widgets->cid]));
1664 sprintf(s, _("%.1f km"), ((LINES-i)*chunksd[widgets->cid]));
1666 draw_grid ( window, image, widgets, pix, s, i );
1670 for ( i = 0; i < widgets->profile_width; i++ )
1671 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
1672 i + MARGIN, widgets->profile_height, i + MARGIN, widgets->profile_height-widgets->profile_height*(widgets->distances[i])/(chunksd[widgets->cid]*LINES) );
1674 // Show speed indicator
1675 if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->w_show_dist_speed)) ) {
1676 GdkGC *dist_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
1678 gdk_color_parse ( "red", &color );
1679 gdk_gc_set_rgb_fg_color ( dist_speed_gc, &color);
1681 gdouble max_speed = 0;
1682 max_speed = widgets->max_speed * 110 / 100;
1684 // This is just an indicator - no actual values can be inferred by user
1686 for ( i = 0; i < widgets->profile_width; i++ ) {
1687 int y_speed = widgets->profile_height - (widgets->profile_height * widgets->speeds[i])/max_speed;
1688 gdk_draw_rectangle(GDK_DRAWABLE(pix), dist_speed_gc, TRUE, i+MARGIN-2, y_speed-2, 4, 4);
1690 g_object_unref ( G_OBJECT(dist_speed_gc) );
1694 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN, 0, widgets->profile_width-1, widgets->profile_height-1);
1696 g_object_unref ( G_OBJECT(pix) );
1701 * Draw just the elevation/time image
1703 static void draw_et ( GtkWidget *image, VikTrack *tr, PropWidgets *widgets )
1710 // Free previous allocation
1712 g_free ( widgets->ats );
1714 widgets->ats = vik_track_make_elevation_time_map ( tr, widgets->profile_width );
1716 if ( widgets->ats == NULL )
1719 // Convert into appropriate units
1720 vik_units_height_t height_units = a_vik_get_units_height ();
1721 if ( height_units == VIK_UNITS_HEIGHT_FEET ) {
1722 // Convert altitudes into feet units
1723 for ( i = 0; i < widgets->profile_width; i++ ) {
1724 widgets->ats[i] = VIK_METERS_TO_FEET(widgets->ats[i]);
1727 // Otherwise leave in metres
1729 minmax_array(widgets->ats, &widgets->min_altitude, &widgets->max_altitude, TRUE, widgets->profile_width);
1731 get_new_min_and_chunk_index (widgets->min_altitude, widgets->max_altitude, chunksa, G_N_ELEMENTS(chunksa), &widgets->draw_min_altitude_time, &widgets->ciat);
1734 mina = widgets->draw_min_altitude_time;
1736 window = gtk_widget_get_toplevel (widgets->elev_time_box);
1738 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width + MARGIN, widgets->profile_height, -1 );
1740 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
1742 /* clear the image */
1743 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->bg_gc[0],
1744 TRUE, 0, 0, MARGIN, widgets->profile_height);
1745 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->mid_gc[0],
1746 TRUE, MARGIN, 0, widgets->profile_width, widgets->profile_height);
1749 for (i=0; i<=LINES; i++) {
1752 switch (height_units) {
1753 case VIK_UNITS_HEIGHT_METRES:
1754 sprintf(s, "%8dm", (int)(mina + (LINES-i)*chunksa[widgets->ciat]));
1756 case VIK_UNITS_HEIGHT_FEET:
1757 // NB values already converted into feet
1758 sprintf(s, "%8dft", (int)(mina + (LINES-i)*chunksa[widgets->ciat]));
1762 g_critical("Houston, we've had a problem. height=%d", height_units);
1765 draw_grid ( window, image, widgets, pix, s, i );
1768 /* draw elevations */
1769 for ( i = 0; i < widgets->profile_width; i++ )
1770 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
1771 i + MARGIN, widgets->profile_height, i + MARGIN, widgets->profile_height-widgets->profile_height*(widgets->ats[i]-mina)/(chunksa[widgets->ciat]*LINES) );
1773 // Show speed indicator
1774 if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->w_show_elev_speed)) ) {
1775 GdkGC *elev_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
1777 gdk_color_parse ( "red", &color );
1778 gdk_gc_set_rgb_fg_color ( elev_speed_gc, &color);
1780 gdouble max_speed = 0;
1781 max_speed = widgets->max_speed * 110 / 100;
1783 // This is just an indicator - no actual values can be inferred by user
1785 for ( i = 0; i < widgets->profile_width; i++ ) {
1786 int y_speed = widgets->profile_height - (widgets->profile_height * widgets->speeds[i])/max_speed;
1787 gdk_draw_rectangle(GDK_DRAWABLE(pix), elev_speed_gc, TRUE, i+MARGIN-2, y_speed-2, 4, 4);
1789 g_object_unref ( G_OBJECT(elev_speed_gc) );
1793 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN, 0, widgets->profile_width-1, widgets->profile_height-1);
1795 g_object_unref ( G_OBJECT(pix) );
1800 * Draw just the speed/distance image
1802 static void draw_sd ( GtkWidget *image, VikTrack *tr, PropWidgets *widgets)
1809 // Free previous allocation
1810 if ( widgets->speeds_dist )
1811 g_free ( widgets->speeds_dist );
1813 widgets->speeds_dist = vik_track_make_speed_dist_map ( tr, widgets->profile_width );
1814 if ( widgets->speeds_dist == NULL )
1817 // Convert into appropriate units
1818 vik_units_speed_t speed_units = a_vik_get_units_speed ();
1819 switch (speed_units) {
1820 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
1821 for ( i = 0; i < widgets->profile_width; i++ ) {
1822 widgets->speeds_dist[i] = VIK_MPS_TO_KPH(widgets->speeds_dist[i]);
1825 case VIK_UNITS_SPEED_MILES_PER_HOUR:
1826 for ( i = 0; i < widgets->profile_width; i++ ) {
1827 widgets->speeds_dist[i] = VIK_MPS_TO_MPH(widgets->speeds_dist[i]);
1830 case VIK_UNITS_SPEED_KNOTS:
1831 for ( i = 0; i < widgets->profile_width; i++ ) {
1832 widgets->speeds_dist[i] = VIK_MPS_TO_KNOTS(widgets->speeds_dist[i]);
1836 // VIK_UNITS_SPEED_METRES_PER_SECOND:
1837 // No need to convert as already in m/s
1841 window = gtk_widget_get_toplevel (widgets->speed_dist_box);
1843 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width + MARGIN, widgets->profile_height, -1 );
1845 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
1847 // OK to resuse min_speed here
1848 minmax_array(widgets->speeds_dist, &widgets->min_speed, &widgets->max_speed_dist, FALSE, widgets->profile_width);
1849 if (widgets->min_speed < 0.0)
1850 widgets->min_speed = 0; /* splines sometimes give negative speeds */
1852 /* Find suitable chunk index */
1853 get_new_min_and_chunk_index (widgets->min_speed, widgets->max_speed_dist, chunkss, G_N_ELEMENTS(chunkss), &widgets->draw_min_speed, &widgets->cisd);
1856 mins = widgets->draw_min_speed;
1858 /* clear the image */
1859 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->bg_gc[0],
1860 TRUE, 0, 0, MARGIN, widgets->profile_height);
1861 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->mid_gc[0],
1862 TRUE, MARGIN, 0, widgets->profile_width, widgets->profile_height);
1865 for (i=0; i<=LINES; i++) {
1868 // NB: No need to convert here anymore as numbers are in the appropriate units
1869 switch (speed_units) {
1870 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
1871 sprintf(s, "%8dkm/h", (int)(mins + (LINES-i)*chunkss[widgets->cisd]));
1873 case VIK_UNITS_SPEED_MILES_PER_HOUR:
1874 sprintf(s, "%8dmph", (int)(mins + (LINES-i)*chunkss[widgets->cisd]));
1876 case VIK_UNITS_SPEED_METRES_PER_SECOND:
1877 sprintf(s, "%8dm/s", (int)(mins + (LINES-i)*chunkss[widgets->cisd]));
1879 case VIK_UNITS_SPEED_KNOTS:
1880 sprintf(s, "%8dknots", (int)(mins + (LINES-i)*chunkss[widgets->cisd]));
1884 g_critical("Houston, we've had a problem. speed=%d", speed_units);
1887 draw_grid ( window, image, widgets, pix, s, i );
1891 for ( i = 0; i < widgets->profile_width; i++ )
1892 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
1893 i + MARGIN, widgets->profile_height, i + MARGIN, widgets->profile_height-widgets->profile_height*(widgets->speeds_dist[i]-mins)/(chunkss[widgets->cisd]*LINES) );
1896 if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->w_show_sd_gps_speed)) ) {
1898 GdkGC *gps_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
1900 gdk_color_parse ( "red", &color );
1901 gdk_gc_set_rgb_fg_color ( gps_speed_gc, &color);
1903 gdouble dist = vik_track_get_length_including_gaps(tr);
1904 gdouble dist_tp = 0.0;
1906 GList *iter = tr->trackpoints;
1907 for (iter = iter->next; iter; iter = iter->next) {
1908 gdouble gps_speed = VIK_TRACKPOINT(iter->data)->speed;
1909 if (isnan(gps_speed))
1911 switch (speed_units) {
1912 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
1913 gps_speed = VIK_MPS_TO_KPH(gps_speed);
1915 case VIK_UNITS_SPEED_MILES_PER_HOUR:
1916 gps_speed = VIK_MPS_TO_MPH(gps_speed);
1918 case VIK_UNITS_SPEED_KNOTS:
1919 gps_speed = VIK_MPS_TO_KNOTS(gps_speed);
1922 // VIK_UNITS_SPEED_METRES_PER_SECOND:
1923 // No need to convert as already in m/s
1926 dist_tp += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) );
1927 int x = MARGIN + (widgets->profile_width * dist_tp / dist);
1928 int y = widgets->profile_height - widgets->profile_height*(gps_speed - mins)/(chunkss[widgets->cisd]*LINES);
1929 gdk_draw_rectangle(GDK_DRAWABLE(pix), gps_speed_gc, TRUE, x-2, y-2, 4, 4);
1931 g_object_unref ( G_OBJECT(gps_speed_gc) );
1935 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN, 0, widgets->profile_width-1, widgets->profile_height-1);
1937 g_object_unref ( G_OBJECT(pix) );
1944 static void draw_all_graphs ( GtkWidget *widget, PropWidgets *widgets, gboolean resized )
1946 // Draw graphs even if they are not visible
1948 GList *child = NULL;
1949 GtkWidget *image = NULL;
1950 GtkWidget *window = gtk_widget_get_toplevel(widget);
1952 gdouble pc_blob = NAN;
1955 if (widgets->elev_box != NULL) {
1957 // Saved image no longer any good as we've resized, so we remove it here
1958 if (resized && widgets->elev_graph_saved_img.img) {
1959 g_object_unref(widgets->elev_graph_saved_img.img);
1960 widgets->elev_graph_saved_img.img = NULL;
1961 widgets->elev_graph_saved_img.saved = FALSE;
1964 child = gtk_container_get_children(GTK_CONTAINER(widgets->elev_box));
1965 draw_elevations (GTK_WIDGET(child->data), widgets->tr, widgets );
1967 image = GTK_WIDGET(child->data);
1970 // Ensure marker or blob are redrawn if necessary
1971 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
1973 pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
1974 gdouble x_blob = -MARGIN - 1.0; // i.e. Don't draw unless we get a valid value
1976 if (widgets->is_blob_drawn) {
1977 pc_blob = tp_percentage_by_distance ( widgets->tr, widgets->blob_tp, widgets->track_length_inc_gaps );
1978 if (!isnan(pc_blob)) {
1979 x_blob = (pc_blob * widgets->profile_width);
1981 y_blob = blobby_altitude (x_blob, widgets);
1984 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
1986 marker_x = (pc * widgets->profile_width) + MARGIN;
1989 save_image_and_draw_graph_marks (image,
1991 gtk_widget_get_style(window)->black_gc,
1994 &widgets->elev_graph_saved_img,
1995 widgets->profile_width,
1996 widgets->profile_height,
1997 &widgets->is_marker_drawn,
1998 &widgets->is_blob_drawn);
2003 if (widgets->gradient_box != NULL) {
2005 // Saved image no longer any good as we've resized, so we remove it here
2006 if (resized && widgets->gradient_graph_saved_img.img) {
2007 g_object_unref(widgets->gradient_graph_saved_img.img);
2008 widgets->gradient_graph_saved_img.img = NULL;
2009 widgets->gradient_graph_saved_img.saved = FALSE;
2012 child = gtk_container_get_children(GTK_CONTAINER(widgets->gradient_box));
2013 draw_gradients (GTK_WIDGET(child->data), widgets->tr, widgets );
2015 image = GTK_WIDGET(child->data);
2018 // Ensure marker or blob are redrawn if necessary
2019 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
2021 pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
2022 gdouble x_blob = -MARGIN - 1.0; // i.e. Don't draw unless we get a valid value
2024 if (widgets->is_blob_drawn) {
2025 pc_blob = tp_percentage_by_distance ( widgets->tr, widgets->blob_tp, widgets->track_length_inc_gaps );
2026 if (!isnan(pc_blob)) {
2027 x_blob = (pc_blob * widgets->profile_width);
2029 y_blob = blobby_gradient (x_blob, widgets);
2032 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
2034 marker_x = (pc * widgets->profile_width) + MARGIN;
2037 save_image_and_draw_graph_marks (image,
2039 gtk_widget_get_style(window)->black_gc,
2042 &widgets->gradient_graph_saved_img,
2043 widgets->profile_width,
2044 widgets->profile_height,
2045 &widgets->is_marker_drawn,
2046 &widgets->is_blob_drawn);
2051 if (widgets->speed_box != NULL) {
2053 // Saved image no longer any good as we've resized
2054 if (resized && widgets->speed_graph_saved_img.img) {
2055 g_object_unref(widgets->speed_graph_saved_img.img);
2056 widgets->speed_graph_saved_img.img = NULL;
2057 widgets->speed_graph_saved_img.saved = FALSE;
2060 child = gtk_container_get_children(GTK_CONTAINER(widgets->speed_box));
2061 draw_vt (GTK_WIDGET(child->data), widgets->tr, widgets );
2063 image = GTK_WIDGET(child->data);
2066 // Ensure marker or blob are redrawn if necessary
2067 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
2069 pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
2071 gdouble x_blob = -MARGIN - 1.0; // i.e. Don't draw unless we get a valid value
2073 if (widgets->is_blob_drawn) {
2074 pc_blob = tp_percentage_by_time ( widgets->tr, widgets->blob_tp );
2075 if (!isnan(pc_blob)) {
2076 x_blob = (pc_blob * widgets->profile_width);
2079 y_blob = blobby_speed (x_blob, widgets);
2082 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
2084 marker_x = (pc * widgets->profile_width) + MARGIN;
2087 save_image_and_draw_graph_marks (image,
2089 gtk_widget_get_style(window)->black_gc,
2092 &widgets->speed_graph_saved_img,
2093 widgets->profile_width,
2094 widgets->profile_height,
2095 &widgets->is_marker_drawn,
2096 &widgets->is_blob_drawn);
2101 if (widgets->dist_box != NULL) {
2103 // Saved image no longer any good as we've resized
2104 if (resized && widgets->dist_graph_saved_img.img) {
2105 g_object_unref(widgets->dist_graph_saved_img.img);
2106 widgets->dist_graph_saved_img.img = NULL;
2107 widgets->dist_graph_saved_img.saved = FALSE;
2110 child = gtk_container_get_children(GTK_CONTAINER(widgets->dist_box));
2111 draw_dt (GTK_WIDGET(child->data), widgets->tr, widgets );
2113 image = GTK_WIDGET(child->data);
2116 // Ensure marker or blob are redrawn if necessary
2117 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
2119 pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
2121 gdouble x_blob = -MARGIN - 1.0; // i.e. Don't draw unless we get a valid value
2123 if (widgets->is_blob_drawn) {
2124 pc_blob = tp_percentage_by_time ( widgets->tr, widgets->blob_tp );
2125 if (!isnan(pc_blob)) {
2126 x_blob = (pc_blob * widgets->profile_width);
2129 y_blob = blobby_distance (x_blob, widgets);
2132 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
2134 marker_x = (pc * widgets->profile_width) + MARGIN;
2137 save_image_and_draw_graph_marks (image,
2139 gtk_widget_get_style(window)->black_gc,
2142 &widgets->dist_graph_saved_img,
2143 widgets->profile_width,
2144 widgets->profile_height,
2145 &widgets->is_marker_drawn,
2146 &widgets->is_blob_drawn);
2150 // Draw Elevations in timely manner
2151 if (widgets->elev_time_box != NULL) {
2153 // Saved image no longer any good as we've resized
2154 if (resized && widgets->elev_time_graph_saved_img.img) {
2155 g_object_unref(widgets->elev_time_graph_saved_img.img);
2156 widgets->elev_time_graph_saved_img.img = NULL;
2157 widgets->elev_time_graph_saved_img.saved = FALSE;
2160 child = gtk_container_get_children(GTK_CONTAINER(widgets->elev_time_box));
2161 draw_et (GTK_WIDGET(child->data), widgets->tr, widgets );
2163 image = GTK_WIDGET(child->data);
2166 // Ensure marker or blob are redrawn if necessary
2167 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
2169 pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
2171 gdouble x_blob = -MARGIN - 1.0; // i.e. Don't draw unless we get a valid value
2173 if (widgets->is_blob_drawn) {
2174 pc_blob = tp_percentage_by_time ( widgets->tr, widgets->blob_tp );
2175 if (!isnan(pc_blob)) {
2176 x_blob = (pc_blob * widgets->profile_width);
2178 y_blob = blobby_altitude_time (x_blob, widgets);
2181 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
2183 marker_x = (pc * widgets->profile_width) + MARGIN;
2186 save_image_and_draw_graph_marks (image,
2188 gtk_widget_get_style(window)->black_gc,
2191 &widgets->elev_time_graph_saved_img,
2192 widgets->profile_width,
2193 widgets->profile_height,
2194 &widgets->is_marker_drawn,
2195 &widgets->is_blob_drawn);
2199 // Draw speed distances
2200 if (widgets->speed_dist_box != NULL) {
2202 // Saved image no longer any good as we've resized, so we remove it here
2203 if (resized && widgets->speed_dist_graph_saved_img.img) {
2204 g_object_unref(widgets->speed_dist_graph_saved_img.img);
2205 widgets->speed_dist_graph_saved_img.img = NULL;
2206 widgets->speed_dist_graph_saved_img.saved = FALSE;
2209 child = gtk_container_get_children(GTK_CONTAINER(widgets->speed_dist_box));
2210 draw_sd (GTK_WIDGET(child->data), widgets->tr, widgets );
2212 image = GTK_WIDGET(child->data);
2215 // Ensure marker or blob are redrawn if necessary
2216 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
2218 pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
2219 gdouble x_blob = -MARGIN - 1.0; // i.e. Don't draw unless we get a valid value
2221 if (widgets->is_blob_drawn) {
2222 pc_blob = tp_percentage_by_distance ( widgets->tr, widgets->blob_tp, widgets->track_length_inc_gaps );
2223 if (!isnan(pc_blob)) {
2224 x_blob = (pc_blob * widgets->profile_width);
2226 y_blob = blobby_speed_dist (x_blob, widgets);
2229 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
2231 marker_x = (pc * widgets->profile_width) + MARGIN;
2234 save_image_and_draw_graph_marks (image,
2236 gtk_widget_get_style(window)->black_gc,
2239 &widgets->speed_dist_graph_saved_img,
2240 widgets->profile_width,
2241 widgets->profile_height,
2242 &widgets->is_marker_drawn,
2243 &widgets->is_blob_drawn);
2250 * Configure/Resize the profile & speed/time images
2252 static gboolean configure_event ( GtkWidget *widget, GdkEventConfigure *event, PropWidgets *widgets )
2254 if (widgets->configure_dialog) {
2255 // Determine size offsets between dialog size and size for images
2256 // Only on the initialisation of the dialog
2257 widgets->profile_width_offset = event->width - widgets->profile_width;
2258 widgets->profile_height_offset = event->height - widgets->profile_height;
2259 widgets->configure_dialog = FALSE;
2261 // Without this the settting, the dialog will only grow in vertical size - one can not then make it smaller!
2262 gtk_widget_set_size_request ( widget, widgets->profile_width+widgets->profile_width_offset, widgets->profile_height+widgets->profile_height_offset );
2264 // Allow resizing back down to a minimal size (especially useful if the initial size has been made bigger after restoring from the saved settings)
2265 GdkGeometry geom = { 600+widgets->profile_width_offset, 300+widgets->profile_height_offset, 0, 0, 0, 0, 0, 0, 0, 0, GDK_GRAVITY_STATIC };
2266 gdk_window_set_geometry_hints ( gtk_widget_get_window(widget), &geom, GDK_HINT_MIN_SIZE );
2269 widgets->profile_width_old = widgets->profile_width;
2270 widgets->profile_height_old = widgets->profile_height;
2273 // Now adjust From Dialog size to get image size
2274 widgets->profile_width = event->width - widgets->profile_width_offset;
2275 widgets->profile_height = event->height - widgets->profile_height_offset;
2277 // ATM we receive configure_events when the dialog is moved and so no further action is necessary
2278 if ( !widgets->configure_dialog &&
2279 (widgets->profile_width_old == widgets->profile_width) && (widgets->profile_height_old == widgets->profile_height) )
2283 draw_all_graphs ( widget, widgets, TRUE );
2289 * Create height profile widgets including the image and callbacks
2291 GtkWidget *vik_trw_layer_create_profile ( GtkWidget *window, PropWidgets *widgets, gdouble *min_alt, gdouble *max_alt)
2295 GtkWidget *eventbox;
2298 widgets->altitudes = vik_track_make_elevation_map ( widgets->tr, widgets->profile_width );
2300 if ( widgets->altitudes == NULL ) {
2301 *min_alt = *max_alt = VIK_DEFAULT_ALTITUDE;
2305 minmax_array(widgets->altitudes, min_alt, max_alt, TRUE, widgets->profile_width);
2307 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width + MARGIN, widgets->profile_height, -1 );
2308 image = gtk_image_new_from_pixmap ( pix, NULL );
2310 g_object_unref ( G_OBJECT(pix) );
2312 eventbox = gtk_event_box_new ();
2313 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_profile_click), widgets );
2314 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_profile_move), widgets );
2315 gtk_container_add ( GTK_CONTAINER(eventbox), image );
2316 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_STRUCTURE_MASK);
2322 * Create height profile widgets including the image and callbacks
2324 GtkWidget *vik_trw_layer_create_gradient ( GtkWidget *window, PropWidgets *widgets)
2328 GtkWidget *eventbox;
2331 widgets->gradients = vik_track_make_gradient_map ( widgets->tr, widgets->profile_width );
2333 if ( widgets->gradients == NULL ) {
2337 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width + MARGIN, widgets->profile_height, -1 );
2338 image = gtk_image_new_from_pixmap ( pix, NULL );
2340 g_object_unref ( G_OBJECT(pix) );
2342 eventbox = gtk_event_box_new ();
2343 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_gradient_click), widgets );
2344 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_gradient_move), widgets );
2345 gtk_container_add ( GTK_CONTAINER(eventbox), image );
2346 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_STRUCTURE_MASK);
2352 * Create speed/time widgets including the image and callbacks
2354 GtkWidget *vik_trw_layer_create_vtdiag ( GtkWidget *window, PropWidgets *widgets)
2358 GtkWidget *eventbox;
2361 widgets->speeds = vik_track_make_speed_map ( widgets->tr, widgets->profile_width );
2362 if ( widgets->speeds == NULL )
2365 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width + MARGIN, widgets->profile_height, -1 );
2366 image = gtk_image_new_from_pixmap ( pix, NULL );
2369 /* XXX this can go out, it's just a helpful dev tool */
2372 GdkGC **colors[8] = { gtk_widget_get_style(window)->bg_gc,
2373 gtk_widget_get_style(window)->fg_gc,
2374 gtk_widget_get_style(window)->light_gc,
2375 gtk_widget_get_style(window)->dark_gc,
2376 gtk_widget_get_style(window)->mid_gc,
2377 gtk_widget_get_style(window)->text_gc,
2378 gtk_widget_get_style(window)->base_gc,
2379 gtk_widget_get_style(window)->text_aa_gc };
2380 for (i=0; i<5; i++) {
2381 for (j=0; j<8; j++) {
2382 gdk_draw_rectangle(GDK_DRAWABLE(pix), colors[j][i],
2383 TRUE, i*20, j*20, 20, 20);
2384 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc,
2385 FALSE, i*20, j*20, 20, 20);
2391 g_object_unref ( G_OBJECT(pix) );
2393 eventbox = gtk_event_box_new ();
2394 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_vt_click), widgets );
2395 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_vt_move), widgets );
2396 gtk_container_add ( GTK_CONTAINER(eventbox), image );
2397 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
2403 * Create distance / time widgets including the image and callbacks
2405 GtkWidget *vik_trw_layer_create_dtdiag ( GtkWidget *window, PropWidgets *widgets)
2409 GtkWidget *eventbox;
2412 widgets->distances = vik_track_make_distance_map ( widgets->tr, widgets->profile_width );
2413 if ( widgets->distances == NULL )
2416 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width + MARGIN, widgets->profile_height, -1 );
2417 image = gtk_image_new_from_pixmap ( pix, NULL );
2419 g_object_unref ( G_OBJECT(pix) );
2421 eventbox = gtk_event_box_new ();
2422 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_dt_click), widgets );
2423 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_dt_move), widgets );
2424 //g_signal_connect_swapped ( G_OBJECT(eventbox), "destroy", G_CALLBACK(g_free), widgets );
2425 gtk_container_add ( GTK_CONTAINER(eventbox), image );
2426 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
2432 * Create elevation / time widgets including the image and callbacks
2434 GtkWidget *vik_trw_layer_create_etdiag ( GtkWidget *window, PropWidgets *widgets)
2438 GtkWidget *eventbox;
2441 widgets->ats = vik_track_make_elevation_time_map ( widgets->tr, widgets->profile_width );
2442 if ( widgets->ats == NULL )
2445 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width + MARGIN, widgets->profile_height, -1 );
2446 image = gtk_image_new_from_pixmap ( pix, NULL );
2448 g_object_unref ( G_OBJECT(pix) );
2450 eventbox = gtk_event_box_new ();
2451 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_et_click), widgets );
2452 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_et_move), widgets );
2453 gtk_container_add ( GTK_CONTAINER(eventbox), image );
2454 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
2460 * Create speed/distance widgets including the image and callbacks
2462 GtkWidget *vik_trw_layer_create_sddiag ( GtkWidget *window, PropWidgets *widgets)
2466 GtkWidget *eventbox;
2469 widgets->speeds_dist = vik_track_make_speed_dist_map ( widgets->tr, widgets->profile_width );
2470 if ( widgets->speeds_dist == NULL )
2473 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width + MARGIN, widgets->profile_height, -1 );
2474 image = gtk_image_new_from_pixmap ( pix, NULL );
2476 g_object_unref ( G_OBJECT(pix) );
2478 eventbox = gtk_event_box_new ();
2479 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_sd_click), widgets );
2480 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_sd_move), widgets );
2481 gtk_container_add ( GTK_CONTAINER(eventbox), image );
2482 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
2488 #define VIK_SETTINGS_TRACK_PROFILE_WIDTH "track_profile_display_width"
2489 #define VIK_SETTINGS_TRACK_PROFILE_HEIGHT "track_profile_display_height"
2491 static void save_values ( PropWidgets *widgets )
2493 a_settings_set_integer ( VIK_SETTINGS_TRACK_PROFILE_WIDTH, widgets->profile_width );
2494 a_settings_set_integer ( VIK_SETTINGS_TRACK_PROFILE_HEIGHT, widgets->profile_height );
2497 static void destroy_cb ( GtkDialog *dialog, PropWidgets *widgets )
2499 save_values(widgets);
2500 prop_widgets_free(widgets);
2503 static void propwin_response_cb( GtkDialog *dialog, gint resp, PropWidgets *widgets )
2505 VikTrack *tr = widgets->tr;
2506 VikTrwLayer *vtl = widgets->vtl;
2507 gboolean keep_dialog = FALSE;
2509 /* FIXME: check and make sure the track still exists before doing anything to it */
2510 /* Note: destroying diaglog (eg, parent window exit) won't give "response" */
2512 case GTK_RESPONSE_DELETE_EVENT: /* received delete event (not from buttons) */
2513 case GTK_RESPONSE_REJECT:
2515 case GTK_RESPONSE_ACCEPT:
2516 vik_track_set_comment(tr, gtk_entry_get_text(GTK_ENTRY(widgets->w_comment)));
2517 vik_track_set_description(tr, gtk_entry_get_text(GTK_ENTRY(widgets->w_description)));
2518 gtk_color_button_get_color ( GTK_COLOR_BUTTON(widgets->w_color), &(tr->color) );
2519 tr->draw_name_mode = gtk_combo_box_get_active ( GTK_COMBO_BOX(widgets->w_namelabel) );
2520 tr->max_number_dist_labels = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(widgets->w_number_distlabels) );
2521 trw_layer_update_treeview ( widgets->vtl, widgets->tr );
2522 vik_layer_emit_update ( VIK_LAYER(vtl) );
2524 case VIK_TRW_LAYER_PROPWIN_REVERSE:
2525 vik_track_reverse(tr);
2526 vik_layer_emit_update ( VIK_LAYER(vtl) );
2528 case VIK_TRW_LAYER_PROPWIN_DEL_DUP:
2529 vik_track_remove_dup_points(tr); // NB ignore the returned answer
2530 // As we could have seen the nuber of dulplicates that would be deleted in the properties statistics tab,
2531 // choose not to inform the user unnecessarily
2533 /* above operation could have deleted current_tp or last_tp */
2534 trw_layer_cancel_tps_of_track ( vtl, tr );
2535 vik_layer_emit_update ( VIK_LAYER(vtl) );
2537 case VIK_TRW_LAYER_PROPWIN_SPLIT:
2539 /* get new tracks, add them and then the delete old one. old can still exist on clipboard. */
2542 VikTrack **tracks = vik_track_split_into_segments(tr, &ntracks);
2545 for ( i = 0; i < ntracks; i++ )
2548 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl,
2549 widgets->tr->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
2551 if ( widgets->tr->is_route )
2552 vik_trw_layer_add_route ( vtl, new_tr_name, tracks[i] );
2554 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
2555 vik_track_calculate_bounds ( tracks[i] );
2557 g_free ( new_tr_name );
2563 /* Don't let track destroy this dialog */
2564 vik_track_clear_property_dialog(tr);
2565 if ( widgets->tr->is_route )
2566 vik_trw_layer_delete_route ( vtl, tr );
2568 vik_trw_layer_delete_track ( vtl, tr );
2569 vik_layer_emit_update ( VIK_LAYER(vtl) ); /* chase thru the hoops */
2573 case VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER:
2575 GList *iter = tr->trackpoints;
2576 while ((iter = iter->next)) {
2577 if (widgets->marker_tp == VIK_TRACKPOINT(iter->data))
2581 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR,
2582 _("Failed spliting track. Track unchanged"), NULL);
2587 gchar *r_name = trw_layer_new_unique_sublayer_name(vtl,
2588 widgets->tr->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
2590 iter->prev->next = NULL;
2592 VikTrack *tr_right = vik_track_new();
2594 vik_track_set_comment ( tr_right, tr->comment );
2595 tr_right->visible = tr->visible;
2596 tr_right->is_route = tr->is_route;
2597 tr_right->trackpoints = iter;
2599 if ( widgets->tr->is_route )
2600 vik_trw_layer_add_route(vtl, r_name, tr_right);
2602 vik_trw_layer_add_track(vtl, r_name, tr_right);
2603 vik_track_calculate_bounds ( tr_right );
2607 vik_layer_emit_update ( VIK_LAYER(vtl) );
2611 fprintf(stderr, "DEBUG: unknown response\n");
2615 /* Keep same behaviour for now: destroy dialog if click on any button */
2617 vik_track_clear_property_dialog(tr);
2618 gtk_widget_destroy ( GTK_WIDGET(dialog) );
2623 * Force a redraw when checkbutton has been toggled to show/hide that information
2625 static void checkbutton_toggle_cb ( GtkToggleButton *togglebutton, PropWidgets *widgets, gpointer dummy )
2627 // Even though not resized, we'll pretend it is -
2628 // as this invalidates the saved images (since the image may have changed)
2629 draw_all_graphs ( widgets->dialog, widgets, TRUE );
2633 * Create the widgets for the given graph tab
2635 static GtkWidget *create_graph_page ( GtkWidget *graph,
2636 const gchar *markup,
2638 const gchar *markup2,
2640 GtkWidget *checkbutton1,
2641 gboolean checkbutton1_default,
2642 GtkWidget *checkbutton2,
2643 gboolean checkbutton2_default )
2645 GtkWidget *hbox = gtk_hbox_new ( FALSE, 10 );
2646 GtkWidget *vbox = gtk_vbox_new ( FALSE, 10 );
2647 GtkWidget *label = gtk_label_new (NULL);
2648 GtkWidget *label2 = gtk_label_new (NULL);
2649 gtk_box_pack_start (GTK_BOX(vbox), graph, FALSE, FALSE, 0);
2650 gtk_label_set_markup ( GTK_LABEL(label), markup );
2651 gtk_label_set_markup ( GTK_LABEL(label2), markup2 );
2652 gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0);
2653 gtk_box_pack_start (GTK_BOX(hbox), value, FALSE, FALSE, 0);
2654 gtk_box_pack_start (GTK_BOX(hbox), label2, FALSE, FALSE, 0);
2655 gtk_box_pack_start (GTK_BOX(hbox), value2, FALSE, FALSE, 0);
2657 gtk_box_pack_end (GTK_BOX(hbox), checkbutton2, FALSE, FALSE, 0);
2658 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbutton2), checkbutton2_default);
2661 gtk_box_pack_end (GTK_BOX(hbox), checkbutton1, FALSE, FALSE, 0);
2662 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbutton1), checkbutton1_default);
2664 gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2669 static GtkWidget *create_table (int cnt, char *labels[], GtkWidget *contents[])
2674 table = GTK_TABLE(gtk_table_new (cnt, 2, FALSE));
2675 gtk_table_set_col_spacing (table, 0, 10);
2676 for (i=0; i<cnt; i++) {
2679 // Settings so the text positioning only moves around vertically when the dialog is resized
2680 // This also gives more room to see the track comment
2681 label = gtk_label_new(NULL);
2682 gtk_misc_set_alignment ( GTK_MISC(label), 1, 0.5 ); // Position text centrally in vertical plane
2683 gtk_label_set_markup ( GTK_LABEL(label), _(labels[i]) );
2684 gtk_table_attach ( table, label, 0, 1, i, i+1, GTK_FILL, GTK_SHRINK, 0, 0 );
2685 if (GTK_IS_MISC(contents[i])) {
2686 gtk_misc_set_alignment ( GTK_MISC(contents[i]), 0, 0.5 );
2688 if ( GTK_IS_COLOR_BUTTON(contents[i]) || GTK_IS_COMBO_BOX(contents[i]) )
2689 // Buttons compressed - otherwise look weird (to me) if vertically massive
2690 gtk_table_attach ( table, contents[i], 1, 2, i, i+1, GTK_FILL, GTK_SHRINK, 0, 5 );
2692 // Expand for comments + descriptions / labels
2693 gtk_table_attach_defaults ( table, contents[i], 1, 2, i, i+1 );
2696 return GTK_WIDGET (table);
2699 void vik_trw_layer_propwin_run ( GtkWindow *parent,
2704 gboolean start_on_stats )
2706 PropWidgets *widgets = prop_widgets_new();
2712 gint profile_size_value;
2713 // Ensure minimum values
2714 widgets->profile_width = 600;
2715 if ( a_settings_get_integer ( VIK_SETTINGS_TRACK_PROFILE_WIDTH, &profile_size_value ) )
2716 if ( profile_size_value > widgets->profile_width )
2717 widgets->profile_width = profile_size_value;
2719 widgets->profile_height = 300;
2720 if ( a_settings_get_integer ( VIK_SETTINGS_TRACK_PROFILE_HEIGHT, &profile_size_value ) )
2721 if ( profile_size_value > widgets->profile_height )
2722 widgets->profile_height = profile_size_value;
2724 gchar *title = g_strdup_printf(_("%s - Track Properties"), tr->name);
2725 GtkWidget *dialog = gtk_dialog_new_with_buttons (title,
2727 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
2728 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2729 _("Split at _Marker"), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER,
2730 _("Split _Segments"), VIK_TRW_LAYER_PROPWIN_SPLIT,
2731 _("_Reverse"), VIK_TRW_LAYER_PROPWIN_REVERSE,
2732 _("_Delete Dupl."), VIK_TRW_LAYER_PROPWIN_DEL_DUP,
2733 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2735 widgets->dialog = dialog;
2736 g_signal_connect( G_OBJECT(dialog), "response", G_CALLBACK(propwin_response_cb), widgets);
2744 gdouble min_alt, max_alt;
2745 widgets->elev_box = vik_trw_layer_create_profile(GTK_WIDGET(parent), widgets, &min_alt, &max_alt);
2746 widgets->gradient_box = vik_trw_layer_create_gradient(GTK_WIDGET(parent), widgets);
2747 widgets->speed_box = vik_trw_layer_create_vtdiag(GTK_WIDGET(parent), widgets);
2748 widgets->dist_box = vik_trw_layer_create_dtdiag(GTK_WIDGET(parent), widgets);
2749 widgets->elev_time_box = vik_trw_layer_create_etdiag(GTK_WIDGET(parent), widgets);
2750 widgets->speed_dist_box = vik_trw_layer_create_sddiag(GTK_WIDGET(parent), widgets);
2751 GtkWidget *graphs = gtk_notebook_new();
2753 GtkWidget *content_prop[20];
2756 static gchar *label_texts[] = {
2757 N_("<b>Comment:</b>"),
2758 N_("<b>Description:</b>"),
2759 N_("<b>Color:</b>"),
2760 N_("<b>Draw Name:</b>"),
2761 N_("<b>Distance Labels:</b>"),
2763 static gchar *stats_texts[] = {
2764 N_("<b>Track Length:</b>"),
2765 N_("<b>Trackpoints:</b>"),
2766 N_("<b>Segments:</b>"),
2767 N_("<b>Duplicate Points:</b>"),
2768 N_("<b>Max Speed:</b>"),
2769 N_("<b>Avg. Speed:</b>"),
2770 N_("<b>Moving Avg. Speed:</b>"),
2771 N_("<b>Avg. Dist. Between TPs:</b>"),
2772 N_("<b>Elevation Range:</b>"),
2773 N_("<b>Total Elevation Gain/Loss:</b>"),
2774 N_("<b>Start:</b>"),
2776 N_("<b>Duration:</b>"),
2778 static gchar tmp_buf[50];
2782 widgets->w_comment = gtk_entry_new ();
2784 gtk_entry_set_text ( GTK_ENTRY(widgets->w_comment), tr->comment );
2785 g_signal_connect_swapped ( widgets->w_comment, "activate", G_CALLBACK(a_dialog_response_accept), GTK_DIALOG(dialog) );
2786 content_prop[cnt_prop++] = widgets->w_comment;
2788 widgets->w_description = gtk_entry_new ();
2789 if ( tr->description )
2790 gtk_entry_set_text ( GTK_ENTRY(widgets->w_description), tr->description );
2791 g_signal_connect_swapped ( widgets->w_description, "activate", G_CALLBACK(a_dialog_response_accept), GTK_DIALOG(dialog) );
2792 content_prop[cnt_prop++] = widgets->w_description;
2794 widgets->w_color = content_prop[cnt_prop++] = gtk_color_button_new_with_color ( &(tr->color) );
2796 static gchar *draw_name_labels[] = {
2801 N_("Start and End"),
2802 N_("Centre, Start and End"),
2806 widgets->w_namelabel = content_prop[cnt_prop++] = vik_combo_box_text_new ();
2807 gchar **pstr = draw_name_labels;
2809 vik_combo_box_text_append ( widgets->w_namelabel, *(pstr++) );
2810 gtk_combo_box_set_active ( GTK_COMBO_BOX(widgets->w_namelabel), tr->draw_name_mode );
2812 widgets->w_number_distlabels = content_prop[cnt_prop++] =
2813 gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new(tr->max_number_dist_labels, 0, 100, 1, 1, 0)), 1, 0 );
2814 gtk_widget_set_tooltip_text ( GTK_WIDGET(widgets->w_number_distlabels), _("Maximum number of distance labels to be shown") );
2816 table = create_table (cnt_prop, label_texts, content_prop);
2818 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), GTK_WIDGET(table), gtk_label_new(_("Properties")));
2821 GtkWidget *content[20];
2824 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2826 // NB This value not shown yet - but is used by internal calculations
2827 widgets->track_length_inc_gaps = vik_track_get_length_including_gaps(tr);
2829 tr_len = widgets->track_length = vik_track_get_length(tr);
2830 switch (dist_units) {
2831 case VIK_UNITS_DISTANCE_KILOMETRES:
2832 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km", tr_len/1000.0 );
2834 case VIK_UNITS_DISTANCE_MILES:
2835 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f miles", VIK_METERS_TO_MILES(tr_len) );
2838 g_critical("Houston, we've had a problem. distance=%d", dist_units);
2840 widgets->w_track_length = content[cnt++] = gtk_label_new ( tmp_buf );
2842 tp_count = vik_track_get_tp_count(tr);
2843 g_snprintf(tmp_buf, sizeof(tmp_buf), "%lu", tp_count );
2844 widgets->w_tp_count = content[cnt++] = gtk_label_new ( tmp_buf );
2846 seg_count = vik_track_get_segment_count(tr) ;
2847 g_snprintf(tmp_buf, sizeof(tmp_buf), "%u", seg_count );
2848 widgets->w_segment_count = content[cnt++] = gtk_label_new ( tmp_buf );
2850 g_snprintf(tmp_buf, sizeof(tmp_buf), "%lu", vik_track_get_dup_point_count(tr) );
2851 widgets->w_duptp_count = content[cnt++] = gtk_label_new ( tmp_buf );
2853 vik_units_speed_t speed_units = a_vik_get_units_speed ();
2854 tmp_speed = vik_track_get_max_speed(tr);
2855 if ( tmp_speed == 0 )
2856 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
2858 switch (speed_units) {
2859 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
2860 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km/h", VIK_MPS_TO_KPH(tmp_speed));
2862 case VIK_UNITS_SPEED_MILES_PER_HOUR:
2863 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f mph", VIK_MPS_TO_MPH(tmp_speed));
2865 case VIK_UNITS_SPEED_METRES_PER_SECOND:
2866 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m/s", tmp_speed );
2868 case VIK_UNITS_SPEED_KNOTS:
2869 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f knots", VIK_MPS_TO_KNOTS(tmp_speed));
2872 g_snprintf (tmp_buf, sizeof(tmp_buf), "--" );
2873 g_critical("Houston, we've had a problem. speed=%d", speed_units);
2876 widgets->w_max_speed = content[cnt++] = gtk_label_new ( tmp_buf );
2878 tmp_speed = vik_track_get_average_speed(tr);
2879 if ( tmp_speed == 0 )
2880 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
2882 switch (speed_units) {
2883 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
2884 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km/h", VIK_MPS_TO_KPH(tmp_speed));
2886 case VIK_UNITS_SPEED_MILES_PER_HOUR:
2887 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f mph", VIK_MPS_TO_MPH(tmp_speed));
2889 case VIK_UNITS_SPEED_METRES_PER_SECOND:
2890 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m/s", tmp_speed );
2892 case VIK_UNITS_SPEED_KNOTS:
2893 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f knots", VIK_MPS_TO_KNOTS(tmp_speed));
2896 g_snprintf (tmp_buf, sizeof(tmp_buf), "--" );
2897 g_critical("Houston, we've had a problem. speed=%d", speed_units);
2900 widgets->w_avg_speed = content[cnt++] = gtk_label_new ( tmp_buf );
2902 // Use 60sec as the default period to be considered stopped
2903 // this is the TrackWaypoint draw stops default value 'vtl->stop_length'
2904 // however this variable is not directly accessible - and I don't expect it's often changed from the default
2905 // so ATM just put in the number
2906 tmp_speed = vik_track_get_average_speed_moving(tr, 60);
2907 if ( tmp_speed == 0 )
2908 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
2910 switch (speed_units) {
2911 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
2912 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km/h", VIK_MPS_TO_KPH(tmp_speed));
2914 case VIK_UNITS_SPEED_MILES_PER_HOUR:
2915 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f mph", VIK_MPS_TO_MPH(tmp_speed));
2917 case VIK_UNITS_SPEED_METRES_PER_SECOND:
2918 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m/s", tmp_speed );
2920 case VIK_UNITS_SPEED_KNOTS:
2921 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f knots", VIK_MPS_TO_KNOTS(tmp_speed));
2924 g_snprintf (tmp_buf, sizeof(tmp_buf), "--" );
2925 g_critical("Houston, we've had a problem. speed=%d", speed_units);
2928 widgets->w_mvg_speed = content[cnt++] = gtk_label_new ( tmp_buf );
2930 switch (dist_units) {
2931 case VIK_UNITS_DISTANCE_KILOMETRES:
2932 // Even though kilometres, the average distance between points is going to be quite small so keep in metres
2933 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m", (tp_count - seg_count) == 0 ? 0 : tr_len / ( tp_count - seg_count ) );
2935 case VIK_UNITS_DISTANCE_MILES:
2936 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 )) );
2939 g_critical("Houston, we've had a problem. distance=%d", dist_units);
2941 widgets->w_avg_dist = content[cnt++] = gtk_label_new ( tmp_buf );
2943 vik_units_height_t height_units = a_vik_get_units_height ();
2944 if ( min_alt == VIK_DEFAULT_ALTITUDE )
2945 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
2947 switch (height_units) {
2948 case VIK_UNITS_HEIGHT_METRES:
2949 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f m - %.0f m", min_alt, max_alt );
2951 case VIK_UNITS_HEIGHT_FEET:
2952 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f feet - %.0f feet", VIK_METERS_TO_FEET(min_alt), VIK_METERS_TO_FEET(max_alt) );
2955 g_snprintf(tmp_buf, sizeof(tmp_buf), "--" );
2956 g_critical("Houston, we've had a problem. height=%d", height_units);
2959 widgets->w_elev_range = content[cnt++] = gtk_label_new ( tmp_buf );
2961 vik_track_get_total_elevation_gain(tr, &max_alt, &min_alt );
2962 if ( min_alt == VIK_DEFAULT_ALTITUDE )
2963 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
2965 switch (height_units) {
2966 case VIK_UNITS_HEIGHT_METRES:
2967 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f m / %.0f m", max_alt, min_alt );
2969 case VIK_UNITS_HEIGHT_FEET:
2970 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f feet / %.0f feet", VIK_METERS_TO_FEET(max_alt), VIK_METERS_TO_FEET(min_alt) );
2973 g_snprintf(tmp_buf, sizeof(tmp_buf), "--" );
2974 g_critical("Houston, we've had a problem. height=%d", height_units);
2977 widgets->w_elev_gain = content[cnt++] = gtk_label_new ( tmp_buf );
2980 #define PACK(w) gtk_box_pack_start (GTK_BOX(right_vbox), w, FALSE, FALSE, 0);
2981 gtk_box_pack_start (GTK_BOX(right_vbox), e_cmt, FALSE, FALSE, 0);
2994 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->timestamp )
2997 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2998 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
3000 strftime (tmp_buf, sizeof(tmp_buf), "%c", localtime(&(t1)));
3001 widgets->w_time_start = content[cnt++] = gtk_label_new(tmp_buf);
3003 strftime (tmp_buf, sizeof(tmp_buf), "%c", localtime(&(t2)));
3004 widgets->w_time_end = content[cnt++] = gtk_label_new(tmp_buf);
3006 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%d minutes"), (int)(t2-t1)/60);
3007 widgets->w_time_dur = content[cnt++] = gtk_label_new(tmp_buf);
3009 widgets->w_time_start = content[cnt++] = gtk_label_new(_("No Data"));
3010 widgets->w_time_end = content[cnt++] = gtk_label_new(_("No Data"));
3011 widgets->w_time_dur = content[cnt++] = gtk_label_new(_("No Data"));
3014 table = create_table (cnt, stats_texts, content);
3016 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), GTK_WIDGET(table), gtk_label_new(_("Statistics")));
3018 if ( widgets->elev_box ) {
3019 GtkWidget *page = NULL;
3020 widgets->w_cur_dist = gtk_label_new(_("No Data"));
3021 widgets->w_cur_elevation = gtk_label_new(_("No Data"));
3022 widgets->w_show_dem = gtk_check_button_new_with_mnemonic(_("Show D_EM"));
3023 widgets->w_show_alt_gps_speed = gtk_check_button_new_with_mnemonic(_("Show _GPS Speed"));
3024 page = create_graph_page (widgets->elev_box,
3025 _("<b>Track Distance:</b>"), widgets->w_cur_dist,
3026 _("<b>Track Height:</b>"), widgets->w_cur_elevation,
3027 widgets->w_show_dem, TRUE,
3028 widgets->w_show_alt_gps_speed, TRUE);
3029 g_signal_connect (widgets->w_show_dem, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
3030 g_signal_connect (widgets->w_show_alt_gps_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
3031 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Elevation-distance")));
3034 if ( widgets->gradient_box ) {
3035 GtkWidget *page = NULL;
3036 widgets->w_cur_gradient_dist = gtk_label_new(_("No Data"));
3037 widgets->w_cur_gradient_gradient = gtk_label_new(_("No Data"));
3038 widgets->w_show_gradient_gps_speed = gtk_check_button_new_with_mnemonic(_("Show _GPS Speed"));
3039 page = create_graph_page (widgets->gradient_box,
3040 _("<b>Track Distance:</b>"), widgets->w_cur_gradient_dist,
3041 _("<b>Track Gradient:</b>"), widgets->w_cur_gradient_gradient,
3042 widgets->w_show_gradient_gps_speed, TRUE,
3044 g_signal_connect (widgets->w_show_gradient_gps_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
3045 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Gradient-distance")));
3048 if ( widgets->speed_box ) {
3049 GtkWidget *page = NULL;
3050 widgets->w_cur_time = gtk_label_new(_("No Data"));
3051 widgets->w_cur_speed = gtk_label_new(_("No Data"));
3052 widgets->w_show_gps_speed = gtk_check_button_new_with_mnemonic(_("Show _GPS Speed"));
3053 page = create_graph_page (widgets->speed_box,
3054 _("<b>Track Time:</b>"), widgets->w_cur_time,
3055 _("<b>Track Speed:</b>"), widgets->w_cur_speed,
3056 widgets->w_show_gps_speed, TRUE,
3058 g_signal_connect (widgets->w_show_gps_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
3059 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Speed-time")));
3062 if ( widgets->dist_box ) {
3063 GtkWidget *page = NULL;
3064 widgets->w_cur_dist_time = gtk_label_new(_("No Data"));
3065 widgets->w_cur_dist_dist = gtk_label_new(_("No Data"));
3066 widgets->w_show_dist_speed = gtk_check_button_new_with_mnemonic(_("Show S_peed"));
3067 page = create_graph_page (widgets->dist_box,
3068 _("<b>Track Distance:</b>"), widgets->w_cur_dist_dist,
3069 _("<b>Track Time:</b>"), widgets->w_cur_dist_time,
3070 widgets->w_show_dist_speed, FALSE,
3072 g_signal_connect (widgets->w_show_dist_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
3073 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Distance-time")));
3076 if ( widgets->elev_time_box ) {
3077 GtkWidget *page = NULL;
3078 widgets->w_cur_elev_time = gtk_label_new(_("No Data"));
3079 widgets->w_cur_elev_elev = gtk_label_new(_("No Data"));
3080 widgets->w_show_elev_speed = gtk_check_button_new_with_mnemonic(_("Show S_peed"));
3081 page = create_graph_page (widgets->elev_time_box,
3082 _("<b>Track Time:</b>"), widgets->w_cur_elev_time,
3083 _("<b>Track Height:</b>"), widgets->w_cur_elev_elev,
3084 widgets->w_show_elev_speed, FALSE,
3086 g_signal_connect (widgets->w_show_elev_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
3087 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Elevation-time")));
3090 if ( widgets->speed_dist_box ) {
3091 GtkWidget *page = NULL;
3092 widgets->w_cur_speed_dist = gtk_label_new(_("No Data"));
3093 widgets->w_cur_speed_speed = gtk_label_new(_("No Data"));
3094 widgets->w_show_sd_gps_speed = gtk_check_button_new_with_mnemonic(_("Show _GPS Speed"));
3095 page = create_graph_page (widgets->speed_dist_box,
3096 _("<b>Track Distance:</b>"), widgets->w_cur_speed_dist,
3097 _("<b>Track Speed:</b>"), widgets->w_cur_speed_speed,
3098 widgets->w_show_sd_gps_speed, TRUE,
3100 g_signal_connect (widgets->w_show_sd_gps_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
3101 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Speed-distance")));
3104 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), graphs, FALSE, FALSE, 0);
3106 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER, FALSE);
3108 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), VIK_TRW_LAYER_PROPWIN_SPLIT, FALSE);
3109 if (vik_track_get_dup_point_count(tr) <= 0)
3110 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), VIK_TRW_LAYER_PROPWIN_DEL_DUP, FALSE);
3112 // On dialog realization configure_event causes the graphs to be initially drawn
3113 widgets->configure_dialog = TRUE;
3114 g_signal_connect ( G_OBJECT(dialog), "configure-event", G_CALLBACK (configure_event), widgets );
3116 g_signal_connect ( G_OBJECT(dialog), "destroy", G_CALLBACK (destroy_cb), widgets );
3118 vik_track_set_property_dialog(tr, dialog);
3119 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3120 gtk_widget_show_all ( dialog );
3122 // Gtk note: due to historical reasons, this must be done after widgets are shown
3123 if ( start_on_stats )
3124 gtk_notebook_set_current_page ( GTK_NOTEBOOK(graphs), 1 );
3129 * Update this property dialog
3130 * e.g. if the track has been renamed
3132 void vik_trw_layer_propwin_update ( VikTrack *trk )
3134 // If not displayed do nothing
3135 if ( !trk->property_dialog )
3138 // Update title with current name
3140 gchar *title = g_strdup_printf ( _("%s - Track Properties"), trk->name );
3141 gtk_window_set_title ( GTK_WINDOW(trk->property_dialog), title );