]> git.street.me.uk Git - andy/viking.git/blame - src/viktrwlayer_propwin.c
Track Properties: Keep markers of the graphs in sync with each other.
[andy/viking.git] / src / viktrwlayer_propwin.c
CommitLineData
50a14534
EB
1/*
2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3 *
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
4c77d5e0
GB
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
25
07596bf4 26#include <math.h>
4c77d5e0 27
50a14534 28#include <gtk/gtk.h>
4c77d5e0 29#include <glib/gi18n.h>
50a14534 30#include <time.h>
f583082b 31#include <string.h>
50a14534
EB
32#include "coords.h"
33#include "vikcoord.h"
34#include "viktrack.h"
21700912 35#include "viktrwlayer.h"
50a14534
EB
36#include "viktrwlayer_propwin.h"
37#include "vikwaypoint.h"
38#include "dialog.h"
39#include "globals.h"
07596bf4 40#include "dems.h"
50a14534 41
24d5c7e2
EB
42#include "vikviewport.h" /* ugh */
43#include "viktreeview.h" /* ugh */
44#include <gdk-pixbuf/gdk-pixdata.h>
45#include "viklayer.h" /* ugh */
46#include "vikaggregatelayer.h"
47#include "viklayerspanel.h" /* ugh */
48
50a14534
EB
49#define PROFILE_WIDTH 600
50#define PROFILE_HEIGHT 300
25e44eac
AF
51#define MIN_ALT_DIFF 100.0
52#define MIN_SPEED_DIFF 20.0
50a14534 53
ca7e67ef
QT
54typedef struct _propsaved {
55 gboolean saved;
56 gint pos;
57 GdkImage *img;
58} PropSaved;
59
21700912
QT
60typedef struct _propwidgets {
61 VikTrwLayer *vtl;
62 VikTrack *tr;
63 VikLayersPanel *vlp;
64 gchar *track_name;
65 GtkWidget *w_comment;
66 GtkWidget *w_track_length;
67 GtkWidget *w_tp_count;
68 GtkWidget *w_segment_count;
69 GtkWidget *w_duptp_count;
70 GtkWidget *w_max_speed;
71 GtkWidget *w_avg_speed;
72 GtkWidget *w_avg_dist;
73 GtkWidget *w_elev_range;
74 GtkWidget *w_elev_gain;
75 GtkWidget *w_time_start;
76 GtkWidget *w_time_end;
77 GtkWidget *w_time_dur;
78 GtkWidget *w_dist_time;
e60fc2c9 79 gdouble track_length;
ca7e67ef
QT
80 PropSaved elev_graph_saved_img;
81 PropSaved speed_graph_saved_img;
e60fc2c9
QT
82 GtkWidget *elev_box;
83 GtkWidget *speed_box;
21700912
QT
84} PropWidgets;
85
ca7e67ef
QT
86static PropWidgets *prop_widgets_new()
87{
88 PropWidgets *widgets = g_malloc0(sizeof(PropWidgets));
89
90 return widgets;
91}
92
93static void prop_widgets_free(PropWidgets *widgets)
94{
95
96 if (widgets->elev_graph_saved_img.img)
97 g_object_unref(widgets->elev_graph_saved_img.img);
98 if (widgets->speed_graph_saved_img.img)
99 g_object_unref(widgets->speed_graph_saved_img.img);
100 g_free(widgets);
101}
102
50a14534
EB
103static void minmax_alt(const gdouble *altitudes, gdouble *min, gdouble *max)
104{
105 *max = -1000;
106 *min = 20000;
107 guint i;
108 for ( i=0; i < PROFILE_WIDTH; i++ ) {
109 if ( altitudes[i] != VIK_DEFAULT_ALTITUDE ) {
110 if ( altitudes[i] > *max )
111 *max = altitudes[i];
112 if ( altitudes[i] < *min )
113 *min = altitudes[i];
114 }
115 }
50a14534
EB
116}
117
d03d80e6 118#define MARGIN 70
25e44eac 119#define LINES 5
e60fc2c9 120static VikTrackpoint *set_center_at_graph_position(gdouble event_x, gint img_width, VikLayersPanel *vlp, VikTrack *tr, gboolean time_base)
24d5c7e2 121{
32e48121
QT
122 VikTrackpoint *trackpoint;
123 gdouble x = event_x - img_width / 2 + PROFILE_WIDTH / 2 - MARGIN / 2;
e1e2f2c6
JJ
124 if (x < 0)
125 x = 0;
126 if (x > PROFILE_WIDTH)
127 x = PROFILE_WIDTH;
32e48121
QT
128
129 if (time_base)
ddc2372e 130 trackpoint = vik_track_get_closest_tp_by_percentage_time ( tr, (gdouble) x / PROFILE_WIDTH, NULL );
32e48121 131 else
ddc2372e 132 trackpoint = vik_track_get_closest_tp_by_percentage_dist ( tr, (gdouble) x / PROFILE_WIDTH, NULL );
32e48121 133
e1e2f2c6
JJ
134 if ( trackpoint ) {
135 VikCoord coord = trackpoint->coord;
e1e2f2c6 136 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &coord );
24d5c7e2 137 vik_layers_panel_emit_update ( vlp );
e1e2f2c6 138 }
e60fc2c9 139 return trackpoint;
e1e2f2c6 140}
ca7e67ef 141
9dc30292 142static void draw_graph_mark(GtkWidget *image, gdouble event_x, gint img_width, GdkGC *gc, PropSaved *saved_img)
32e48121 143{
ca7e67ef
QT
144 GdkPixmap *pix;
145 const int saved_width = 5;
9dc30292
QT
146 /* the pixmap = margin + graph area */
147 gdouble x = event_x - img_width/2 + PROFILE_WIDTH/2 + MARGIN/2;
148
e60fc2c9 149 // fprintf(stderr, "event_x=%f img_width=%d x=%f\n", event_x, img_width, x);
ca7e67ef
QT
150
151 gtk_image_get_pixmap(GTK_IMAGE(image), &pix, NULL);
152 if (saved_img->saved) {
153 gdk_draw_image(GDK_DRAWABLE(pix), gc, saved_img->img, 0, 0,
154 saved_img->pos, 0, -1, -1);
155 saved_img->saved = FALSE;
9dc30292
QT
156 gtk_widget_queue_draw_area(image,
157 saved_img->pos + img_width/2 - PROFILE_WIDTH/2 - MARGIN/2, 0,
ca7e67ef
QT
158 saved_img->img->width, saved_img->img->height);
159 }
160 if ((x >= MARGIN) && (x < (PROFILE_WIDTH + MARGIN))) {
161 if (saved_img->img)
162 gdk_drawable_copy_to_image(GDK_DRAWABLE(pix), saved_img->img,
163 x - (saved_width/2), 0, 0, 0, saved_img->img->width, saved_img->img->height);
164 else
165 saved_img->img = gdk_drawable_copy_to_image(GDK_DRAWABLE(pix),
166 saved_img->img, x - (saved_width/2), 0, 0, 0, saved_width, PROFILE_HEIGHT);
167 saved_img->pos = x - (saved_width/2);
168 saved_img->saved = TRUE;
169 gdk_draw_line (GDK_DRAWABLE(pix), gc, x, 0, x, image->allocation.height);
170 /* redraw the area which contains the line, saved_width is just convenient */
9dc30292 171 gtk_widget_queue_draw_area(image, event_x - saved_width/2, 0, saved_width, PROFILE_HEIGHT);
ca7e67ef 172 }
32e48121
QT
173}
174
ca7e67ef 175static void track_graph_click( GtkWidget *event_box, GdkEventButton *event, gpointer *pass_along, gboolean is_vt_graph )
32e48121
QT
176{
177 VikTrack *tr = pass_along[0];
178 VikLayersPanel *vlp = pass_along[1];
ca7e67ef
QT
179 PropWidgets *widgets = pass_along[2];
180 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
181 GtkWidget *image = GTK_WIDGET(child->data);
182 GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(event_box));
183
184
e60fc2c9 185 VikTrackpoint *trackpoint = set_center_at_graph_position(event->x, event_box->allocation.width, vlp, tr, is_vt_graph);
9dc30292 186 draw_graph_mark(image, event->x, event_box->allocation.width, window->style->black_gc,
ca7e67ef
QT
187 is_vt_graph ? &widgets->speed_graph_saved_img : &widgets->elev_graph_saved_img);
188 g_list_free(child);
189
e60fc2c9
QT
190 /* draw on the other graph */
191 if (trackpoint == NULL || widgets->elev_box == NULL || widgets->speed_box == NULL)
192 /* This test assumes we have only 2 graphs */
193 return;
194
195 gdouble pc = NAN;
196 gdouble x2;
197 GList *other_child = gtk_container_get_children(GTK_CONTAINER(
198 is_vt_graph ? widgets->elev_box : widgets->speed_box));
199 GtkWidget *other_image = GTK_WIDGET(other_child->data);
200 if (is_vt_graph) {
201 gdouble dist = 0.0;
202 GList *iter;
203 for (iter = tr->trackpoints->next; iter != NULL; iter = iter->next) {
204 dist += vik_coord_diff(&(VIK_TRACKPOINT(iter->data)->coord),
205 &(VIK_TRACKPOINT(iter->prev->data)->coord));
206 /* Assuming trackpoint is not a copy */
207 if (trackpoint == VIK_TRACKPOINT(iter->data))
208 break;
209 }
210 if (iter != NULL)
211 pc = dist/widgets->track_length;
212 } else {
213 time_t t_start, t_end, t_total;
214 t_start = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
215 t_end = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
216 t_total = t_end - t_start;
217 pc = (gdouble)(trackpoint->timestamp - t_start)/t_total;
218 }
219 if (!isnan(pc)) {
220 x2 = pc * PROFILE_WIDTH + MARGIN + (event_box->allocation.width/2 - PROFILE_WIDTH/2 - MARGIN/2);
221 draw_graph_mark(other_image, x2, event_box->allocation.width, window->style->black_gc,
222 is_vt_graph ? &widgets->elev_graph_saved_img : &widgets->speed_graph_saved_img);
223 }
224
225 g_list_free(other_child);
226
ca7e67ef
QT
227}
228
229static gboolean track_profile_click( GtkWidget *event_box, GdkEventButton *event, gpointer *pass_along )
230{
231 track_graph_click(event_box, event, pass_along, FALSE);
232 return TRUE; /* don't call other (further) callbacks */
233}
234
235static gboolean track_vt_click( GtkWidget *event_box, GdkEventButton *event, gpointer *pass_along )
236{
237 track_graph_click(event_box, event, pass_along, TRUE);
238 return TRUE; /* don't call other (further) callbacks */
32e48121
QT
239}
240
e1e2f2c6
JJ
241void track_profile_move( GtkWidget *image, GdkEventMotion *event, gpointer *pass_along )
242{
243 VikTrack *tr = pass_along[0];
21700912 244 PropWidgets *widgets = pass_along[2];
e1e2f2c6
JJ
245 int mouse_x, mouse_y;
246 GdkModifierType state;
247
248 if (event->is_hint)
249 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
250 else
251 mouse_x = event->x;
252
253 gdouble x = mouse_x - image->allocation.width / 2 + PROFILE_WIDTH / 2 - MARGIN / 2;
254 if (x < 0)
255 x = 0;
256 if (x > PROFILE_WIDTH)
257 x = PROFILE_WIDTH;
258
ddc2372e
QT
259 gdouble meters_from_start;
260 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_dist ( tr, (gdouble) x / PROFILE_WIDTH, &meters_from_start );
21700912 261 if (trackpoint && widgets->w_dist_time) {
ddc2372e
QT
262 static gchar tmp_buf[20];
263 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f m", meters_from_start);
21700912 264 gtk_label_set_text(GTK_LABEL(widgets->w_dist_time), tmp_buf);
ddc2372e
QT
265 }
266}
267
268void track_vt_move( GtkWidget *image, GdkEventMotion *event, gpointer *pass_along )
269{
270 VikTrack *tr = pass_along[0];
21700912 271 PropWidgets *widgets = pass_along[2];
ddc2372e
QT
272 int mouse_x, mouse_y;
273 GdkModifierType state;
274
275 if (event->is_hint)
276 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
277 else
278 mouse_x = event->x;
279
280 gdouble x = mouse_x - image->allocation.width / 2 + PROFILE_WIDTH / 2 - MARGIN / 2;
281 if (x < 0)
282 x = 0;
283 if (x > PROFILE_WIDTH)
284 x = PROFILE_WIDTH;
285
286 time_t seconds_from_start;
287 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_time ( tr, (gdouble) x / PROFILE_WIDTH, &seconds_from_start );
21700912 288 if (trackpoint && widgets->w_dist_time) {
ddc2372e
QT
289 static gchar tmp_buf[20];
290 guint h, m, s;
291 h = seconds_from_start/3600;
292 m = (seconds_from_start - h*3600)/60;
293 s = seconds_from_start - (3600*h) - (60*m);
294 g_snprintf(tmp_buf, sizeof(tmp_buf), "%02d:%02d:%02d", h, m, s);
e1e2f2c6 295
21700912 296 gtk_label_set_text(GTK_LABEL(widgets->w_dist_time), tmp_buf);
24d5c7e2
EB
297 }
298}
299
b42009f6 300static void draw_dem_alt_speed_dist(VikTrack *tr, GdkDrawable *pix, GdkGC *alt_gc, GdkGC *speed_gc, gdouble alt_offset, gdouble alt_diff, gint width, gint height, gint margin)
07596bf4
QT
301{
302 GList *iter;
303 gdouble dist = 0;
304 gdouble max_speed = 0;
305 gdouble total_length = vik_track_get_length_including_gaps(tr);
306
307 for (iter = tr->trackpoints->next; iter; iter = iter->next) {
308 if (!isnan(VIK_TRACKPOINT(iter->data)->speed))
309 max_speed = MAX(max_speed, VIK_TRACKPOINT(iter->data)->speed);
310 }
3220e336 311 max_speed = max_speed * 110 / 100;
07596bf4
QT
312
313 for (iter = tr->trackpoints->next; iter; iter = iter->next) {
314 int x, y_alt, y_speed;
228213c5 315 gint16 elev = a_dems_get_elev_by_coord(&(VIK_TRACKPOINT(iter->data)->coord), VIK_DEM_INTERPOL_BEST);
b42009f6 316 elev -= alt_offset;
07596bf4
QT
317 dist += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
318 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
319 x = (width * dist)/total_length + margin;
320 if ( elev != VIK_DEM_INVALID_ELEVATION ) {
2c50b28a 321 y_alt = height - ((height * elev)/alt_diff);
07596bf4
QT
322 gdk_draw_rectangle(GDK_DRAWABLE(pix), alt_gc, TRUE, x-2, y_alt-2, 4, 4);
323 }
324 if (!isnan(VIK_TRACKPOINT(iter->data)->speed)) {
325 y_speed = height - (height * VIK_TRACKPOINT(iter->data)->speed)/max_speed;
326 gdk_draw_rectangle(GDK_DRAWABLE(pix), speed_gc, TRUE, x-2, y_speed-2, 4, 4);
327 }
328 }
329}
330
21700912 331GtkWidget *vik_trw_layer_create_profile ( GtkWidget *window, VikTrack *tr, gpointer vlp, PropWidgets *widgets, gdouble *min_alt, gdouble *max_alt)
50a14534 332{
c79f0206
EB
333 GdkPixmap *pix;
334 GtkWidget *image;
50a14534 335 gdouble *altitudes = vik_track_make_elevation_map ( tr, PROFILE_WIDTH );
25e44eac 336 gdouble mina, maxa;
561e6ad0 337 GtkWidget *eventbox;
24d5c7e2 338 gpointer *pass_along;
50a14534
EB
339 guint i;
340
8c4f1350
EB
341 if ( altitudes == NULL ) {
342 *min_alt = *max_alt = VIK_DEFAULT_ALTITUDE;
c79f0206 343 return NULL;
8c4f1350 344 }
c79f0206
EB
345
346 pix = gdk_pixmap_new( window->window, PROFILE_WIDTH + MARGIN, PROFILE_HEIGHT, -1 );
347 image = gtk_image_new_from_pixmap ( pix, NULL );
348
50a14534 349 GdkGC *no_alt_info = gdk_gc_new ( window->window );
07596bf4
QT
350 GdkGC *dem_alt_gc = gdk_gc_new ( window->window );
351 GdkGC *gps_speed_gc = gdk_gc_new ( window->window );
50a14534
EB
352 GdkColor color;
353
07596bf4 354 gdk_color_parse ( "yellow", &color );
50a14534 355 gdk_gc_set_rgb_fg_color ( no_alt_info, &color);
07596bf4
QT
356 gdk_color_parse ( "green", &color );
357 gdk_gc_set_rgb_fg_color ( dem_alt_gc, &color);
358 gdk_color_parse ( "red", &color );
359 gdk_gc_set_rgb_fg_color ( gps_speed_gc, &color);
50a14534 360
25e44eac 361
50a14534 362 minmax_alt(altitudes, min_alt, max_alt);
3220e336 363 mina = *min_alt;
2c50b28a 364 maxa = *max_alt + ((*max_alt - *min_alt) * 0.25);
25e44eac
AF
365
366 /* clear the image */
367 gdk_draw_rectangle(GDK_DRAWABLE(pix), window->style->bg_gc[0],
368 TRUE, 0, 0, MARGIN, PROFILE_HEIGHT);
369 gdk_draw_rectangle(GDK_DRAWABLE(pix), window->style->mid_gc[0],
370 TRUE, MARGIN, 0, PROFILE_WIDTH, PROFILE_HEIGHT);
371
25e44eac 372 /* draw grid */
d03d80e6 373#define LABEL_FONT "Sans 7"
25e44eac
AF
374 for (i=0; i<=LINES; i++) {
375 PangoFontDescription *pfd;
376 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(image), NULL);
377 gchar s[32];
d03d80e6 378 int w, h;
25e44eac 379
d03d80e6 380 pango_layout_set_alignment (pl, PANGO_ALIGN_RIGHT);
25e44eac
AF
381 pfd = pango_font_description_from_string (LABEL_FONT);
382 pango_layout_set_font_description (pl, pfd);
383 pango_font_description_free (pfd);
384 sprintf(s, "%8dm", (int)(mina + (LINES-i)*(maxa-mina)/LINES));
385 pango_layout_set_text(pl, s, -1);
d03d80e6
AF
386 pango_layout_get_pixel_size (pl, &w, &h);
387 gdk_draw_layout(GDK_DRAWABLE(pix), window->style->fg_gc[0], MARGIN-w-3,
388 CLAMP((int)i*PROFILE_HEIGHT/LINES - h/2, 0, PROFILE_HEIGHT-h), pl);
25e44eac
AF
389
390 gdk_draw_line (GDK_DRAWABLE(pix), window->style->dark_gc[0],
391 MARGIN, PROFILE_HEIGHT/LINES * i, MARGIN + PROFILE_WIDTH, PROFILE_HEIGHT/LINES * i);
392 }
393
394 /* draw elevations */
50a14534 395 for ( i = 0; i < PROFILE_WIDTH; i++ )
bb71de8b 396 if ( altitudes[i] == VIK_DEFAULT_ALTITUDE )
25e44eac
AF
397 gdk_draw_line ( GDK_DRAWABLE(pix), no_alt_info,
398 i + MARGIN, 0, i + MARGIN, PROFILE_HEIGHT );
bb71de8b 399 else
25e44eac
AF
400 gdk_draw_line ( GDK_DRAWABLE(pix), window->style->dark_gc[3],
401 i + MARGIN, PROFILE_HEIGHT, i + MARGIN, PROFILE_HEIGHT-PROFILE_HEIGHT*(altitudes[i]-mina)/(maxa-mina) );
734652bf 402
b42009f6 403 draw_dem_alt_speed_dist(tr, GDK_DRAWABLE(pix), dem_alt_gc, gps_speed_gc, mina, maxa - mina, PROFILE_WIDTH, PROFILE_HEIGHT, MARGIN);
07596bf4 404
25e44eac
AF
405 /* draw border */
406 gdk_draw_rectangle(GDK_DRAWABLE(pix), window->style->black_gc, FALSE, MARGIN, 0, PROFILE_WIDTH-1, PROFILE_HEIGHT-1);
407
24d5c7e2
EB
408
409
50a14534
EB
410 g_object_unref ( G_OBJECT(pix) );
411 g_free ( altitudes );
412 g_object_unref ( G_OBJECT(no_alt_info) );
07596bf4
QT
413 g_object_unref ( G_OBJECT(dem_alt_gc) );
414 g_object_unref ( G_OBJECT(gps_speed_gc) );
24d5c7e2 415
21700912 416 pass_along = g_malloc ( sizeof(gpointer) * 3 ); /* FIXME: mem leak -- never be freed */
24d5c7e2
EB
417 pass_along[0] = tr;
418 pass_along[1] = vlp;
21700912 419 pass_along[2] = widgets;
24d5c7e2
EB
420
421 eventbox = gtk_event_box_new ();
422 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_profile_click), pass_along );
e1e2f2c6 423 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_profile_move), pass_along );
24d5c7e2
EB
424 g_signal_connect_swapped ( G_OBJECT(eventbox), "destroy", G_CALLBACK(g_free), pass_along );
425 gtk_container_add ( GTK_CONTAINER(eventbox), image );
e1e2f2c6
JJ
426 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK
427 | GDK_POINTER_MOTION_MASK
428 | GDK_POINTER_MOTION_HINT_MASK);
24d5c7e2
EB
429
430 return eventbox;
50a14534 431}
24d5c7e2 432
25e44eac
AF
433#define METRIC 1
434#ifdef METRIC
32e48121 435#define MTOK(v) ( (v)*3.6) /* m/s to km/h */
25e44eac
AF
436#else
437#define MTOK(v) ( (v)*3600.0/1000.0 * 0.6214) /* m/s to mph - we'll handle this globally eventually but for now ...*/
438#endif
439
21700912 440GtkWidget *vik_trw_layer_create_vtdiag ( GtkWidget *window, VikTrack *tr, gpointer vlp, PropWidgets *widgets)
25e44eac 441{
c79f0206
EB
442 GdkPixmap *pix;
443 GtkWidget *image;
25e44eac 444 gdouble mins, maxs;
25e44eac 445 guint i;
561e6ad0
EB
446 GtkWidget *eventbox;
447 gpointer *pass_along;
25e44eac 448
21700912 449 pass_along = g_malloc ( sizeof(gpointer) * 3 ); /* FIXME: mem leak -- never be freed */
561e6ad0
EB
450 pass_along[0] = tr;
451 pass_along[1] = vlp;
21700912 452 pass_along[2] = widgets;
c79f0206
EB
453
454 gdouble *speeds = vik_track_make_speed_map ( tr, PROFILE_WIDTH );
455 if ( speeds == NULL )
456 return NULL;
457
458 pix = gdk_pixmap_new( window->window, PROFILE_WIDTH + MARGIN, PROFILE_HEIGHT, -1 );
459 image = gtk_image_new_from_pixmap ( pix, NULL );
460
25e44eac
AF
461 for (i=0; i<PROFILE_WIDTH; i++) {
462 speeds[i] = MTOK(speeds[i]);
463 }
464
465 minmax_alt(speeds, &mins, &maxs);
2c50b28a
QT
466 if (mins < 0.0)
467 mins = 0; /* splines sometimes give negative speeds */
468 maxs = maxs + ((maxs - mins) * 0.1);
25e44eac
AF
469 if (maxs-mins < MIN_SPEED_DIFF) {
470 maxs = mins + MIN_SPEED_DIFF;
471 }
472
473 /* clear the image */
474 gdk_draw_rectangle(GDK_DRAWABLE(pix), window->style->bg_gc[0],
475 TRUE, 0, 0, MARGIN, PROFILE_HEIGHT);
476 gdk_draw_rectangle(GDK_DRAWABLE(pix), window->style->mid_gc[0],
477 TRUE, MARGIN, 0, PROFILE_WIDTH, PROFILE_HEIGHT);
478
479#if 0
480 /* XXX this can go out, it's just a helpful dev tool */
481 {
482 int j;
483 GdkGC **colors[8] = { window->style->bg_gc, window->style->fg_gc,
484 window->style->light_gc,
485 window->style->dark_gc, window->style->mid_gc,
486 window->style->text_gc, window->style->base_gc,
487 window->style->text_aa_gc };
488 for (i=0; i<5; i++) {
489 for (j=0; j<8; j++) {
490 gdk_draw_rectangle(GDK_DRAWABLE(pix), colors[j][i],
491 TRUE, i*20, j*20, 20, 20);
492 gdk_draw_rectangle(GDK_DRAWABLE(pix), window->style->black_gc,
493 FALSE, i*20, j*20, 20, 20);
494 }
495 }
496 }
497#else
498
499 /* draw grid */
d03d80e6 500#define LABEL_FONT "Sans 7"
25e44eac
AF
501 for (i=0; i<=LINES; i++) {
502 PangoFontDescription *pfd;
503 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(image), NULL);
504 gchar s[32];
d03d80e6 505 int w, h;
25e44eac 506
d03d80e6 507 pango_layout_set_alignment (pl, PANGO_ALIGN_RIGHT);
25e44eac
AF
508 pfd = pango_font_description_from_string (LABEL_FONT);
509 pango_layout_set_font_description (pl, pfd);
510 pango_font_description_free (pfd);
511#ifdef METRIC
512 sprintf(s, "%5dkm/h", (int)(mins + (LINES-i)*(maxs-mins)/LINES));
513#else
514 sprintf(s, "%8dmph", (int)(mins + (LINES-i)*(maxs-mins)/LINES));
515#endif
516 pango_layout_set_text(pl, s, -1);
d03d80e6
AF
517 pango_layout_get_pixel_size (pl, &w, &h);
518 gdk_draw_layout(GDK_DRAWABLE(pix), window->style->fg_gc[0], MARGIN-w-3,
519 CLAMP((int)i*PROFILE_HEIGHT/LINES - h/2, 0, PROFILE_HEIGHT-h), pl);
25e44eac
AF
520
521 gdk_draw_line (GDK_DRAWABLE(pix), window->style->dark_gc[0],
522 MARGIN, PROFILE_HEIGHT/LINES * i, MARGIN + PROFILE_WIDTH, PROFILE_HEIGHT/LINES * i);
523 }
d03d80e6 524
25e44eac
AF
525
526 /* draw speeds */
527 for ( i = 0; i < PROFILE_WIDTH; i++ )
528 gdk_draw_line ( GDK_DRAWABLE(pix), window->style->dark_gc[3],
529 i + MARGIN, PROFILE_HEIGHT, i + MARGIN, PROFILE_HEIGHT-PROFILE_HEIGHT*(speeds[i]-mins)/(maxs-mins) );
530#endif
07596bf4
QT
531
532
533 GdkGC *gps_speed_gc = gdk_gc_new ( window->window );
534 GdkColor color;
535
536 gdk_color_parse ( "red", &color );
537 gdk_gc_set_rgb_fg_color ( gps_speed_gc, &color);
538
539 time_t beg_time = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
540 time_t dur = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp - beg_time;
541 GList *iter;
542 for (iter = tr->trackpoints; iter; iter = iter->next) {
543 gdouble gps_speed = VIK_TRACKPOINT(iter->data)->speed;
544 if (isnan(gps_speed))
545 continue;
546 int x = MARGIN + PROFILE_WIDTH * (VIK_TRACKPOINT(iter->data)->timestamp - beg_time) / dur;
547 int y = PROFILE_HEIGHT - PROFILE_HEIGHT*(MTOK(gps_speed) - mins)/(maxs - mins);
548 gdk_draw_rectangle(GDK_DRAWABLE(pix), gps_speed_gc, TRUE, x-2, y-2, 4, 4);
549 }
550
25e44eac
AF
551 /* draw border */
552 gdk_draw_rectangle(GDK_DRAWABLE(pix), window->style->black_gc, FALSE, MARGIN, 0, PROFILE_WIDTH-1, PROFILE_HEIGHT-1);
553
554 g_object_unref ( G_OBJECT(pix) );
07596bf4 555 g_object_unref ( G_OBJECT(gps_speed_gc) );
25e44eac 556 g_free ( speeds );
561e6ad0
EB
557
558 eventbox = gtk_event_box_new ();
32e48121 559 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_vt_click), pass_along );
ddc2372e 560 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_vt_move), pass_along );
561e6ad0
EB
561 g_signal_connect_swapped ( G_OBJECT(eventbox), "destroy", G_CALLBACK(g_free), pass_along );
562 gtk_container_add ( GTK_CONTAINER(eventbox), image );
e1e2f2c6
JJ
563 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK
564 | GDK_POINTER_MOTION_MASK
565 | GDK_POINTER_MOTION_HINT_MASK);
561e6ad0
EB
566
567 return eventbox;
25e44eac
AF
568}
569#undef MARGIN
570#undef LINES
50a14534 571
42f34743 572static void propwin_response_cb( GtkDialog *dialog, gint resp, PropWidgets *widgets)
50a14534 573{
21700912
QT
574 VikTrack *tr = widgets->tr;
575 VikTrwLayer *vtl = widgets->vtl;
576
577 /* FIXME: check and make sure the track still exists before doing anything to it */
578 /* Note: destroying diaglog (eg, parent window exit) won't give "response" */
579 switch (resp) {
580 case GTK_RESPONSE_DELETE_EVENT: /* received delete event (not from buttons) */
581 case GTK_RESPONSE_REJECT:
582 break;
583 case GTK_RESPONSE_ACCEPT:
584 vik_track_set_comment(tr, gtk_entry_get_text(GTK_ENTRY(widgets->w_comment)));
585 break;
586 case VIK_TRW_LAYER_PROPWIN_REVERSE:
587 vik_track_reverse(tr);
588 vik_layer_emit_update ( VIK_LAYER(vtl) );
589 break;
590 case VIK_TRW_LAYER_PROPWIN_DEL_DUP:
591 vik_track_remove_dup_points(tr);
592 /* above operation could have deleted current_tp or last_tp */
593 trw_layer_cancel_tps_of_track ( vtl, widgets->track_name );
594 vik_layer_emit_update ( VIK_LAYER(vtl) );
595 break;
596 case VIK_TRW_LAYER_PROPWIN_SPLIT:
597 {
598 /* get new tracks, add them, resolve naming conflicts (free if cancel), and delete old. old can still exist on clipboard. */
599 guint ntracks;
600 VikTrack **tracks = vik_track_split_into_segments(tr, &ntracks);
601 gchar *new_tr_name;
602 guint i;
603 for ( i = 0; i < ntracks; i++ )
604 {
605 g_assert ( tracks[i] );
606 new_tr_name = g_strdup_printf("%s #%d", widgets->track_name, i+1);
607 /* if ( (wp_exists) && (! overwrite) ) */
608 /* don't need to upper case new_tr_name because old tr name was uppercase */
609 if ( vik_trw_layer_get_track(vtl, new_tr_name ) &&
610 ( ! a_dialog_overwrite ( VIK_GTK_WINDOW_FROM_LAYER(vtl), "The track \"%s\" exists, do you wish to overwrite it?", new_tr_name ) ) )
611 {
612 gchar *new_new_tr_name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vik_trw_layer_get_tracks(vtl) );
613 g_free ( new_tr_name );
614 if (new_new_tr_name)
615 new_tr_name = new_new_tr_name;
616 else
617 {
618 new_tr_name = NULL;
619 vik_track_free ( tracks[i] );
620 }
621 }
622 if ( new_tr_name )
623 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
624 }
625 if ( tracks )
626 {
627 g_free ( tracks );
628 /* Don't let track destroy this dialog */
629 vik_track_clear_property_dialog(tr);
630 vik_trw_layer_delete_track ( vtl, widgets->track_name );
631 vik_layer_emit_update ( VIK_LAYER(vtl) ); /* chase thru the hoops */
632 }
633 }
634 break;
635 default:
636 fprintf(stderr, "DEBUG: unknown response\n");
637 return;
638 }
639
640 /* Keep same behaviour for now: destroy dialog if click on any button */
ca7e67ef 641 prop_widgets_free(widgets);
21700912
QT
642 vik_track_clear_property_dialog(tr);
643 gtk_widget_destroy ( GTK_WIDGET(dialog) );
644}
645
646void vik_trw_layer_propwin_run ( GtkWindow *parent, VikTrwLayer *vtl, VikTrack *tr, gpointer vlp, gchar *track_name )
647{
648 /* FIXME: free widgets when destroy signal received */
ca7e67ef 649 PropWidgets *widgets = prop_widgets_new();
21700912
QT
650 widgets->vtl = vtl;
651 widgets->tr = tr;
652 widgets->vlp = vlp;
653 widgets->track_name = track_name;
4c77d5e0 654 gchar *title = g_strdup_printf(_("%s - Track Properties"), track_name);
dc27aba1 655 GtkWidget *dialog = gtk_dialog_new_with_buttons (title,
21700912
QT
656 parent,
657 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
658 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
7760b0cf
JJ
659 _("Split Segments"), VIK_TRW_LAYER_PROPWIN_SPLIT,
660 _("Reverse"), VIK_TRW_LAYER_PROPWIN_REVERSE,
661 _("Delete Dupl."), VIK_TRW_LAYER_PROPWIN_DEL_DUP,
21700912
QT
662 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
663 NULL);
dc27aba1 664 g_free(title);
42f34743 665 g_signal_connect(dialog, "response", G_CALLBACK(propwin_response_cb), widgets);
21700912 666 //fprintf(stderr, "DEBUG: dialog=0x%p\n", dialog);
4b00e581 667 GtkTable *table;
50a14534
EB
668 gdouble tr_len;
669 guint32 tp_count, seg_count;
50a14534
EB
670
671 gdouble min_alt, max_alt;
21700912
QT
672 GtkWidget *profile = vik_trw_layer_create_profile(GTK_WIDGET(parent),tr, vlp, widgets, &min_alt,&max_alt);
673 GtkWidget *vtdiag = vik_trw_layer_create_vtdiag(GTK_WIDGET(parent), tr, vlp, widgets);
25e44eac 674 GtkWidget *graphs = gtk_notebook_new();
50a14534 675
e60fc2c9
QT
676 widgets->elev_box = profile;
677 widgets->speed_box = vtdiag;
678
4b00e581
AF
679 GtkWidget *content[20];
680 int cnt;
681 int i;
682
4c77d5e0 683 static gchar *label_texts[] = { N_("<b>Comment:</b>"), N_("<b>Track Length:</b>"), N_("<b>Trackpoints:</b>"), N_("<b>Segments:</b>"), N_("<b>Duplicate Points:</b>"), N_("<b>Max Speed:</b>"), N_("<b>Avg. Speed:</b>"), N_("<b>Avg. Dist. Between TPs:</b>"), N_("<b>Elevation Range:</b>"), N_("<b>Total Elevation Gain/Loss:</b>"), N_("<b>Start:</b>"), N_("<b>End:</b>"), N_("<b>Duration:</b>"), N_("<b>Track Distance/Time:</b>") };
32e48121 684 static gchar tmp_buf[50];
8c4f1350 685 gdouble tmp_speed;
50a14534 686
4b00e581 687 cnt = 0;
21700912 688 widgets->w_comment = gtk_entry_new ();
50a14534 689 if ( tr->comment )
21700912
QT
690 gtk_entry_set_text ( GTK_ENTRY(widgets->w_comment), tr->comment );
691 g_signal_connect_swapped ( widgets->w_comment, "activate", G_CALLBACK(a_dialog_response_accept), GTK_DIALOG(dialog) );
692 content[cnt++] = widgets->w_comment;
50a14534 693
e60fc2c9 694 tr_len = widgets->track_length = vik_track_get_length(tr);
50a14534 695 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m", tr_len );
21700912 696 widgets->w_track_length = content[cnt++] = gtk_label_new ( tmp_buf );
50a14534
EB
697
698 tp_count = vik_track_get_tp_count(tr);
699 g_snprintf(tmp_buf, sizeof(tmp_buf), "%u", tp_count );
21700912 700 widgets->w_tp_count = content[cnt++] = gtk_label_new ( tmp_buf );
50a14534
EB
701
702 seg_count = vik_track_get_segment_count(tr) ;
703 g_snprintf(tmp_buf, sizeof(tmp_buf), "%u", seg_count );
21700912 704 widgets->w_segment_count = content[cnt++] = gtk_label_new ( tmp_buf );
50a14534
EB
705
706 g_snprintf(tmp_buf, sizeof(tmp_buf), "%lu", vik_track_get_dup_point_count(tr) );
21700912 707 widgets->w_duptp_count = content[cnt++] = gtk_label_new ( tmp_buf );
50a14534 708
8c4f1350
EB
709 tmp_speed = vik_track_get_max_speed(tr);
710 if ( tmp_speed == 0 )
4c77d5e0 711 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
8c4f1350 712 else
32e48121 713 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m/s (%.0f km/h)", tmp_speed, MTOK(tmp_speed) );
21700912 714 widgets->w_max_speed = content[cnt++] = gtk_label_new ( tmp_buf );
50a14534 715
8c4f1350
EB
716 tmp_speed = vik_track_get_average_speed(tr);
717 if ( tmp_speed == 0 )
4c77d5e0 718 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
8c4f1350 719 else
32e48121 720 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m/s (%.0f km/h)", tmp_speed, MTOK(tmp_speed) );
21700912 721 widgets->w_avg_speed = content[cnt++] = gtk_label_new ( tmp_buf );
50a14534
EB
722
723 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m", (tp_count - seg_count) == 0 ? 0 : tr_len / ( tp_count - seg_count ) );
21700912 724 widgets->w_avg_dist = content[cnt++] = gtk_label_new ( tmp_buf );
50a14534 725
8c4f1350 726 if ( min_alt == VIK_DEFAULT_ALTITUDE )
4c77d5e0 727 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
8c4f1350
EB
728 else
729 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f m - %.0f m", min_alt, max_alt );
21700912 730 widgets->w_elev_range = content[cnt++] = gtk_label_new ( tmp_buf );
50a14534
EB
731
732 vik_track_get_total_elevation_gain(tr, &max_alt, &min_alt );
bf35388d 733 if ( min_alt == VIK_DEFAULT_ALTITUDE )
4c77d5e0 734 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
8c4f1350
EB
735 else
736 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f m / %.0f m", max_alt, min_alt );
21700912 737 widgets->w_elev_gain = content[cnt++] = gtk_label_new ( tmp_buf );
24d5c7e2 738
4b00e581
AF
739#if 0
740#define PACK(w) gtk_box_pack_start (GTK_BOX(right_vbox), w, FALSE, FALSE, 0);
741 gtk_box_pack_start (GTK_BOX(right_vbox), e_cmt, FALSE, FALSE, 0);
742 PACK(l_len);
743 PACK(l_tps);
744 PACK(l_segs);
745 PACK(l_dups);
746 PACK(l_maxs);
747 PACK(l_avgs);
748 PACK(l_avgd);
749 PACK(l_elev);
750 PACK(l_galo);
751#undef PACK;
752#endif
24d5c7e2 753
8c4f1350 754 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->timestamp )
f583082b
AF
755 {
756 time_t t1, t2;
757 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
758 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
759
760 strncpy(tmp_buf, ctime(&t1), sizeof(tmp_buf));
a45242c2 761 tmp_buf[sizeof(tmp_buf)-1] = 0;
32e48121 762 g_strchomp(tmp_buf);
21700912 763 widgets->w_time_start = content[cnt++] = gtk_label_new(tmp_buf);
f583082b
AF
764
765 strncpy(tmp_buf, ctime(&t2), sizeof(tmp_buf));
a45242c2 766 tmp_buf[sizeof(tmp_buf)-1] = 0;
32e48121 767 g_strchomp(tmp_buf);
21700912 768 widgets->w_time_end = content[cnt++] = gtk_label_new(tmp_buf);
f583082b 769
4c77d5e0 770 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%d minutes"), (int)(t2-t1)/60);
21700912 771 widgets->w_time_dur = content[cnt++] = gtk_label_new(tmp_buf);
8c4f1350 772 } else {
4c77d5e0
GB
773 widgets->w_time_start = content[cnt++] = gtk_label_new(_("No Data"));
774 widgets->w_time_end = content[cnt++] = gtk_label_new(_("No Data"));
775 widgets->w_time_dur = content[cnt++] = gtk_label_new(_("No Data"));
24d5c7e2 776 }
4c77d5e0 777 widgets->w_dist_time = content[cnt++] = gtk_label_new(_("No Data"));
4b00e581
AF
778
779 table = GTK_TABLE(gtk_table_new (cnt, 2, FALSE));
780 gtk_table_set_col_spacing (table, 0, 10);
781 for (i=0; i<cnt; i++) {
782 GtkWidget *label;
783
784 label = gtk_label_new(NULL);
785 gtk_misc_set_alignment ( GTK_MISC(label), 1, 0 );
4c77d5e0 786 gtk_label_set_markup ( GTK_LABEL(label), _(label_texts[i]) );
4b00e581
AF
787 gtk_table_attach_defaults ( table, label, 0, 1, i, i+1 );
788 if (GTK_IS_MISC(content[i])) {
789 gtk_misc_set_alignment ( GTK_MISC(content[i]), 0, 0 );
790 }
791 gtk_table_attach_defaults ( table, content[i], 1, 2, i, i+1 );
792 }
793
794 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), GTK_WIDGET(table), FALSE, FALSE, 0);
50a14534 795
c79f0206 796 if ( profile )
4c77d5e0 797 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), profile, gtk_label_new(_("Elevation-distance")));
c79f0206
EB
798
799 if ( vtdiag )
4c77d5e0 800 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), vtdiag, gtk_label_new(_("Speed-time")));
50a14534 801
25e44eac 802 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), graphs, FALSE, FALSE, 0);
1d0135d8
QT
803
804 if (seg_count <= 1)
8dbfe7a3 805 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), VIK_TRW_LAYER_PROPWIN_SPLIT, FALSE);
1d0135d8 806 if (vik_track_get_dup_point_count(tr) <= 0)
8dbfe7a3 807 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), VIK_TRW_LAYER_PROPWIN_DEL_DUP, FALSE);
1d0135d8 808
21700912 809 vik_track_set_property_dialog(tr, dialog);
50a14534 810 gtk_widget_show_all ( dialog );
50a14534 811}