2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
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.
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.
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
31 #include <glib/gi18n.h>
39 #include "viktrwlayer.h"
40 #include "viktrwlayer_propwin.h"
41 #include "vikwaypoint.h"
46 #include "vikviewport.h" /* ugh */
47 #include "viktreeview.h" /* ugh */
48 #include <gdk-pixbuf/gdk-pixdata.h>
49 #include "viklayer.h" /* ugh */
50 #include "vikaggregatelayer.h"
51 #include "viklayerspanel.h" /* ugh */
53 #define PROFILE_WIDTH 600
54 #define PROFILE_HEIGHT 300
55 #define MIN_ALT_DIFF 100.0
56 #define MIN_SPEED_DIFF 20.0
58 typedef struct _propsaved
{
64 typedef struct _propwidgets
{
71 GtkWidget
*w_track_length
;
72 GtkWidget
*w_tp_count
;
73 GtkWidget
*w_segment_count
;
74 GtkWidget
*w_duptp_count
;
75 GtkWidget
*w_max_speed
;
76 GtkWidget
*w_avg_speed
;
77 GtkWidget
*w_avg_dist
;
78 GtkWidget
*w_elev_range
;
79 GtkWidget
*w_elev_gain
;
80 GtkWidget
*w_time_start
;
81 GtkWidget
*w_time_end
;
82 GtkWidget
*w_time_dur
;
83 GtkWidget
*w_cur_dist
; /*< Current distance */
84 GtkWidget
*w_cur_time
; /*< Current time */
86 PropSaved elev_graph_saved_img
;
87 PropSaved speed_graph_saved_img
;
90 VikTrackpoint
*marker_tp
;
93 static PropWidgets
*prop_widgets_new()
95 PropWidgets
*widgets
= g_malloc0(sizeof(PropWidgets
));
100 static void prop_widgets_free(PropWidgets
*widgets
)
103 if (widgets
->elev_graph_saved_img
.img
)
104 g_object_unref(widgets
->elev_graph_saved_img
.img
);
105 if (widgets
->speed_graph_saved_img
.img
)
106 g_object_unref(widgets
->speed_graph_saved_img
.img
);
110 static void minmax_alt(const gdouble
*altitudes
, gdouble
*min
, gdouble
*max
)
115 for ( i
=0; i
< PROFILE_WIDTH
; i
++ ) {
116 if ( altitudes
[i
] != VIK_DEFAULT_ALTITUDE
) {
117 if ( altitudes
[i
] > *max
)
119 if ( altitudes
[i
] < *min
)
127 static VikTrackpoint
*set_center_at_graph_position(gdouble event_x
, gint img_width
, VikLayersPanel
*vlp
, VikTrack
*tr
, gboolean time_base
)
129 VikTrackpoint
*trackpoint
;
130 gdouble x
= event_x
- img_width
/ 2 + PROFILE_WIDTH
/ 2 - MARGIN
/ 2;
133 if (x
> PROFILE_WIDTH
)
137 trackpoint
= vik_track_get_closest_tp_by_percentage_time ( tr
, (gdouble
) x
/ PROFILE_WIDTH
, NULL
);
139 trackpoint
= vik_track_get_closest_tp_by_percentage_dist ( tr
, (gdouble
) x
/ PROFILE_WIDTH
, NULL
);
142 VikCoord coord
= trackpoint
->coord
;
143 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp
), &coord
);
144 vik_layers_panel_emit_update ( vlp
);
149 static void draw_graph_mark(GtkWidget
*image
, gdouble event_x
, gint img_width
, GdkGC
*gc
, PropSaved
*saved_img
)
152 const int saved_width
= 5;
153 /* the pixmap = margin + graph area */
154 gdouble x
= event_x
- img_width
/2 + PROFILE_WIDTH
/2 + MARGIN
/2;
156 // fprintf(stderr, "event_x=%f img_width=%d x=%f\n", event_x, img_width, x);
158 gtk_image_get_pixmap(GTK_IMAGE(image
), &pix
, NULL
);
159 if (saved_img
->saved
) {
160 gdk_draw_image(GDK_DRAWABLE(pix
), gc
, saved_img
->img
, 0, 0,
161 saved_img
->pos
, 0, -1, -1);
162 saved_img
->saved
= FALSE
;
163 gtk_widget_queue_draw_area(image
,
164 saved_img
->pos
+ img_width
/2 - PROFILE_WIDTH
/2 - MARGIN
/2, 0,
165 saved_img
->img
->width
, saved_img
->img
->height
);
167 if ((x
>= MARGIN
) && (x
< (PROFILE_WIDTH
+ MARGIN
))) {
169 gdk_drawable_copy_to_image(GDK_DRAWABLE(pix
), saved_img
->img
,
170 x
- (saved_width
/2), 0, 0, 0, saved_img
->img
->width
, saved_img
->img
->height
);
172 saved_img
->img
= gdk_drawable_copy_to_image(GDK_DRAWABLE(pix
),
173 saved_img
->img
, x
- (saved_width
/2), 0, 0, 0, saved_width
, PROFILE_HEIGHT
);
174 saved_img
->pos
= x
- (saved_width
/2);
175 saved_img
->saved
= TRUE
;
176 gdk_draw_line (GDK_DRAWABLE(pix
), gc
, x
, 0, x
, image
->allocation
.height
);
177 /* redraw the area which contains the line, saved_width is just convenient */
178 gtk_widget_queue_draw_area(image
, event_x
- saved_width
/2, 0, saved_width
, PROFILE_HEIGHT
);
182 static void track_graph_click( GtkWidget
*event_box
, GdkEventButton
*event
, gpointer
*pass_along
, gboolean is_vt_graph
)
184 VikTrack
*tr
= pass_along
[0];
185 VikLayersPanel
*vlp
= pass_along
[1];
186 PropWidgets
*widgets
= pass_along
[2];
187 GList
*child
= gtk_container_get_children(GTK_CONTAINER(event_box
));
188 GtkWidget
*image
= GTK_WIDGET(child
->data
);
189 GtkWidget
*window
= gtk_widget_get_toplevel(GTK_WIDGET(event_box
));
192 VikTrackpoint
*trackpoint
= set_center_at_graph_position(event
->x
, event_box
->allocation
.width
, vlp
, tr
, is_vt_graph
);
193 draw_graph_mark(image
, event
->x
, event_box
->allocation
.width
, window
->style
->black_gc
,
194 is_vt_graph
? &widgets
->speed_graph_saved_img
: &widgets
->elev_graph_saved_img
);
196 widgets
->marker_tp
= trackpoint
;
197 gtk_dialog_set_response_sensitive(GTK_DIALOG(widgets
->dialog
), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER
, TRUE
);
199 /* draw on the other graph */
200 if (trackpoint
== NULL
|| widgets
->elev_box
== NULL
|| widgets
->speed_box
== NULL
)
201 /* This test assumes we have only 2 graphs */
206 GList
*other_child
= gtk_container_get_children(GTK_CONTAINER(
207 is_vt_graph
? widgets
->elev_box
: widgets
->speed_box
));
208 GtkWidget
*other_image
= GTK_WIDGET(other_child
->data
);
212 for (iter
= tr
->trackpoints
->next
; iter
!= NULL
; iter
= iter
->next
) {
213 dist
+= vik_coord_diff(&(VIK_TRACKPOINT(iter
->data
)->coord
),
214 &(VIK_TRACKPOINT(iter
->prev
->data
)->coord
));
215 /* Assuming trackpoint is not a copy */
216 if (trackpoint
== VIK_TRACKPOINT(iter
->data
))
220 pc
= dist
/widgets
->track_length
;
222 time_t t_start
, t_end
, t_total
;
223 t_start
= VIK_TRACKPOINT(tr
->trackpoints
->data
)->timestamp
;
224 t_end
= VIK_TRACKPOINT(g_list_last(tr
->trackpoints
)->data
)->timestamp
;
225 t_total
= t_end
- t_start
;
226 pc
= (gdouble
)(trackpoint
->timestamp
- t_start
)/t_total
;
229 x2
= pc
* PROFILE_WIDTH
+ MARGIN
+ (event_box
->allocation
.width
/2 - PROFILE_WIDTH
/2 - MARGIN
/2);
230 draw_graph_mark(other_image
, x2
, event_box
->allocation
.width
, window
->style
->black_gc
,
231 is_vt_graph
? &widgets
->elev_graph_saved_img
: &widgets
->speed_graph_saved_img
);
234 g_list_free(other_child
);
238 static gboolean
track_profile_click( GtkWidget
*event_box
, GdkEventButton
*event
, gpointer
*pass_along
)
240 track_graph_click(event_box
, event
, pass_along
, FALSE
);
241 return TRUE
; /* don't call other (further) callbacks */
244 static gboolean
track_vt_click( GtkWidget
*event_box
, GdkEventButton
*event
, gpointer
*pass_along
)
246 track_graph_click(event_box
, event
, pass_along
, TRUE
);
247 return TRUE
; /* don't call other (further) callbacks */
250 void track_profile_move( GtkWidget
*image
, GdkEventMotion
*event
, gpointer
*pass_along
)
252 VikTrack
*tr
= pass_along
[0];
253 PropWidgets
*widgets
= pass_along
[2];
254 int mouse_x
, mouse_y
;
255 GdkModifierType state
;
258 gdk_window_get_pointer (event
->window
, &mouse_x
, &mouse_y
, &state
);
262 gdouble x
= mouse_x
- image
->allocation
.width
/ 2 + PROFILE_WIDTH
/ 2 - MARGIN
/ 2;
265 if (x
> PROFILE_WIDTH
)
268 gdouble meters_from_start
;
269 VikTrackpoint
*trackpoint
= vik_track_get_closest_tp_by_percentage_dist ( tr
, (gdouble
) x
/ PROFILE_WIDTH
, &meters_from_start
);
270 if (trackpoint
&& widgets
->w_cur_dist
) {
271 static gchar tmp_buf
[20];
272 g_snprintf(tmp_buf
, sizeof(tmp_buf
), "%.0f m", meters_from_start
);
273 gtk_label_set_text(GTK_LABEL(widgets
->w_cur_dist
), tmp_buf
);
277 void track_vt_move( GtkWidget
*image
, GdkEventMotion
*event
, gpointer
*pass_along
)
279 VikTrack
*tr
= pass_along
[0];
280 PropWidgets
*widgets
= pass_along
[2];
281 int mouse_x
, mouse_y
;
282 GdkModifierType state
;
285 gdk_window_get_pointer (event
->window
, &mouse_x
, &mouse_y
, &state
);
289 gdouble x
= mouse_x
- image
->allocation
.width
/ 2 + PROFILE_WIDTH
/ 2 - MARGIN
/ 2;
292 if (x
> PROFILE_WIDTH
)
295 time_t seconds_from_start
;
296 VikTrackpoint
*trackpoint
= vik_track_get_closest_tp_by_percentage_time ( tr
, (gdouble
) x
/ PROFILE_WIDTH
, &seconds_from_start
);
297 if (trackpoint
&& widgets
->w_cur_time
) {
298 static gchar tmp_buf
[20];
300 h
= seconds_from_start
/3600;
301 m
= (seconds_from_start
- h
*3600)/60;
302 s
= seconds_from_start
- (3600*h
) - (60*m
);
303 g_snprintf(tmp_buf
, sizeof(tmp_buf
), "%02d:%02d:%02d", h
, m
, s
);
305 gtk_label_set_text(GTK_LABEL(widgets
->w_cur_time
), tmp_buf
);
309 static 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
)
313 gdouble max_speed
= 0;
314 gdouble total_length
= vik_track_get_length_including_gaps(tr
);
316 for (iter
= tr
->trackpoints
->next
; iter
; iter
= iter
->next
) {
317 if (!isnan(VIK_TRACKPOINT(iter
->data
)->speed
))
318 max_speed
= MAX(max_speed
, VIK_TRACKPOINT(iter
->data
)->speed
);
320 max_speed
= max_speed
* 110 / 100;
322 for (iter
= tr
->trackpoints
->next
; iter
; iter
= iter
->next
) {
323 int x
, y_alt
, y_speed
;
324 gint16 elev
= a_dems_get_elev_by_coord(&(VIK_TRACKPOINT(iter
->data
)->coord
), VIK_DEM_INTERPOL_BEST
);
326 dist
+= vik_coord_diff ( &(VIK_TRACKPOINT(iter
->data
)->coord
),
327 &(VIK_TRACKPOINT(iter
->prev
->data
)->coord
) );
328 x
= (width
* dist
)/total_length
+ margin
;
329 if ( elev
!= VIK_DEM_INVALID_ELEVATION
) {
330 y_alt
= height
- ((height
* elev
)/alt_diff
);
331 gdk_draw_rectangle(GDK_DRAWABLE(pix
), alt_gc
, TRUE
, x
-2, y_alt
-2, 4, 4);
333 if (!isnan(VIK_TRACKPOINT(iter
->data
)->speed
)) {
334 y_speed
= height
- (height
* VIK_TRACKPOINT(iter
->data
)->speed
)/max_speed
;
335 gdk_draw_rectangle(GDK_DRAWABLE(pix
), speed_gc
, TRUE
, x
-2, y_speed
-2, 4, 4);
340 GtkWidget
*vik_trw_layer_create_profile ( GtkWidget
*window
, VikTrack
*tr
, gpointer vlp
, PropWidgets
*widgets
, gdouble
*min_alt
, gdouble
*max_alt
)
344 gdouble
*altitudes
= vik_track_make_elevation_map ( tr
, PROFILE_WIDTH
);
347 gpointer
*pass_along
;
350 if ( altitudes
== NULL
) {
351 *min_alt
= *max_alt
= VIK_DEFAULT_ALTITUDE
;
355 pix
= gdk_pixmap_new( window
->window
, PROFILE_WIDTH
+ MARGIN
, PROFILE_HEIGHT
, -1 );
356 image
= gtk_image_new_from_pixmap ( pix
, NULL
);
358 GdkGC
*no_alt_info
= gdk_gc_new ( window
->window
);
359 GdkGC
*dem_alt_gc
= gdk_gc_new ( window
->window
);
360 GdkGC
*gps_speed_gc
= gdk_gc_new ( window
->window
);
363 gdk_color_parse ( "yellow", &color
);
364 gdk_gc_set_rgb_fg_color ( no_alt_info
, &color
);
365 gdk_color_parse ( "green", &color
);
366 gdk_gc_set_rgb_fg_color ( dem_alt_gc
, &color
);
367 gdk_color_parse ( "red", &color
);
368 gdk_gc_set_rgb_fg_color ( gps_speed_gc
, &color
);
371 minmax_alt(altitudes
, min_alt
, max_alt
);
373 maxa
= *max_alt
+ ((*max_alt
- *min_alt
) * 0.25);
375 /* clear the image */
376 gdk_draw_rectangle(GDK_DRAWABLE(pix
), window
->style
->bg_gc
[0],
377 TRUE
, 0, 0, MARGIN
, PROFILE_HEIGHT
);
378 gdk_draw_rectangle(GDK_DRAWABLE(pix
), window
->style
->mid_gc
[0],
379 TRUE
, MARGIN
, 0, PROFILE_WIDTH
, PROFILE_HEIGHT
);
382 #define LABEL_FONT "Sans 7"
383 for (i
=0; i
<=LINES
; i
++) {
384 PangoFontDescription
*pfd
;
385 PangoLayout
*pl
= gtk_widget_create_pango_layout (GTK_WIDGET(image
), NULL
);
389 pango_layout_set_alignment (pl
, PANGO_ALIGN_RIGHT
);
390 pfd
= pango_font_description_from_string (LABEL_FONT
);
391 pango_layout_set_font_description (pl
, pfd
);
392 pango_font_description_free (pfd
);
393 sprintf(s
, "%8dm", (int)(mina
+ (LINES
-i
)*(maxa
-mina
)/LINES
));
394 pango_layout_set_text(pl
, s
, -1);
395 pango_layout_get_pixel_size (pl
, &w
, &h
);
396 gdk_draw_layout(GDK_DRAWABLE(pix
), window
->style
->fg_gc
[0], MARGIN
-w
-3,
397 CLAMP((int)i
*PROFILE_HEIGHT
/LINES
- h
/2, 0, PROFILE_HEIGHT
-h
), pl
);
399 gdk_draw_line (GDK_DRAWABLE(pix
), window
->style
->dark_gc
[0],
400 MARGIN
, PROFILE_HEIGHT
/LINES
* i
, MARGIN
+ PROFILE_WIDTH
, PROFILE_HEIGHT
/LINES
* i
);
401 g_object_unref ( G_OBJECT ( pl
) );
405 /* draw elevations */
406 for ( i
= 0; i
< PROFILE_WIDTH
; i
++ )
407 if ( altitudes
[i
] == VIK_DEFAULT_ALTITUDE
)
408 gdk_draw_line ( GDK_DRAWABLE(pix
), no_alt_info
,
409 i
+ MARGIN
, 0, i
+ MARGIN
, PROFILE_HEIGHT
);
411 gdk_draw_line ( GDK_DRAWABLE(pix
), window
->style
->dark_gc
[3],
412 i
+ MARGIN
, PROFILE_HEIGHT
, i
+ MARGIN
, PROFILE_HEIGHT
-PROFILE_HEIGHT
*(altitudes
[i
]-mina
)/(maxa
-mina
) );
414 draw_dem_alt_speed_dist(tr
, GDK_DRAWABLE(pix
), dem_alt_gc
, gps_speed_gc
, mina
, maxa
- mina
, PROFILE_WIDTH
, PROFILE_HEIGHT
, MARGIN
);
417 gdk_draw_rectangle(GDK_DRAWABLE(pix
), window
->style
->black_gc
, FALSE
, MARGIN
, 0, PROFILE_WIDTH
-1, PROFILE_HEIGHT
-1);
421 g_object_unref ( G_OBJECT(pix
) );
422 g_free ( altitudes
);
423 g_object_unref ( G_OBJECT(no_alt_info
) );
424 g_object_unref ( G_OBJECT(dem_alt_gc
) );
425 g_object_unref ( G_OBJECT(gps_speed_gc
) );
427 pass_along
= g_malloc ( sizeof(gpointer
) * 3 ); /* FIXME: mem leak -- never be freed */
430 pass_along
[2] = widgets
;
432 eventbox
= gtk_event_box_new ();
433 g_signal_connect ( G_OBJECT(eventbox
), "button_press_event", G_CALLBACK(track_profile_click
), pass_along
);
434 g_signal_connect ( G_OBJECT(eventbox
), "motion_notify_event", G_CALLBACK(track_profile_move
), pass_along
);
435 g_signal_connect_swapped ( G_OBJECT(eventbox
), "destroy", G_CALLBACK(g_free
), pass_along
);
436 gtk_container_add ( GTK_CONTAINER(eventbox
), image
);
437 gtk_widget_set_events (eventbox
, GDK_BUTTON_PRESS_MASK
438 | GDK_POINTER_MOTION_MASK
439 | GDK_POINTER_MOTION_HINT_MASK
);
446 #define MTOK(v) ( (v)*3.6) /* m/s to km/h */
448 #define MTOK(v) ( (v)*3600.0/1000.0 * 0.6214) /* m/s to mph - we'll handle this globally eventually but for now ...*/
451 GtkWidget
*vik_trw_layer_create_vtdiag ( GtkWidget
*window
, VikTrack
*tr
, gpointer vlp
, PropWidgets
*widgets
)
458 gpointer
*pass_along
;
460 pass_along
= g_malloc ( sizeof(gpointer
) * 3 ); /* FIXME: mem leak -- never be freed */
463 pass_along
[2] = widgets
;
465 gdouble
*speeds
= vik_track_make_speed_map ( tr
, PROFILE_WIDTH
);
466 if ( speeds
== NULL
)
469 pix
= gdk_pixmap_new( window
->window
, PROFILE_WIDTH
+ MARGIN
, PROFILE_HEIGHT
, -1 );
470 image
= gtk_image_new_from_pixmap ( pix
, NULL
);
472 for (i
=0; i
<PROFILE_WIDTH
; i
++) {
473 speeds
[i
] = MTOK(speeds
[i
]);
476 minmax_alt(speeds
, &mins
, &maxs
);
478 mins
= 0; /* splines sometimes give negative speeds */
479 maxs
= maxs
+ ((maxs
- mins
) * 0.1);
480 if (maxs
-mins
< MIN_SPEED_DIFF
) {
481 maxs
= mins
+ MIN_SPEED_DIFF
;
484 /* clear the image */
485 gdk_draw_rectangle(GDK_DRAWABLE(pix
), window
->style
->bg_gc
[0],
486 TRUE
, 0, 0, MARGIN
, PROFILE_HEIGHT
);
487 gdk_draw_rectangle(GDK_DRAWABLE(pix
), window
->style
->mid_gc
[0],
488 TRUE
, MARGIN
, 0, PROFILE_WIDTH
, PROFILE_HEIGHT
);
491 /* XXX this can go out, it's just a helpful dev tool */
494 GdkGC
**colors
[8] = { window
->style
->bg_gc
, window
->style
->fg_gc
,
495 window
->style
->light_gc
,
496 window
->style
->dark_gc
, window
->style
->mid_gc
,
497 window
->style
->text_gc
, window
->style
->base_gc
,
498 window
->style
->text_aa_gc
};
499 for (i
=0; i
<5; i
++) {
500 for (j
=0; j
<8; j
++) {
501 gdk_draw_rectangle(GDK_DRAWABLE(pix
), colors
[j
][i
],
502 TRUE
, i
*20, j
*20, 20, 20);
503 gdk_draw_rectangle(GDK_DRAWABLE(pix
), window
->style
->black_gc
,
504 FALSE
, i
*20, j
*20, 20, 20);
511 #define LABEL_FONT "Sans 7"
512 for (i
=0; i
<=LINES
; i
++) {
513 PangoFontDescription
*pfd
;
514 PangoLayout
*pl
= gtk_widget_create_pango_layout (GTK_WIDGET(image
), NULL
);
518 pango_layout_set_alignment (pl
, PANGO_ALIGN_RIGHT
);
519 pfd
= pango_font_description_from_string (LABEL_FONT
);
520 pango_layout_set_font_description (pl
, pfd
);
521 pango_font_description_free (pfd
);
523 sprintf(s
, "%5dkm/h", (int)(mins
+ (LINES
-i
)*(maxs
-mins
)/LINES
));
525 sprintf(s
, "%8dmph", (int)(mins
+ (LINES
-i
)*(maxs
-mins
)/LINES
));
527 pango_layout_set_text(pl
, s
, -1);
528 pango_layout_get_pixel_size (pl
, &w
, &h
);
529 gdk_draw_layout(GDK_DRAWABLE(pix
), window
->style
->fg_gc
[0], MARGIN
-w
-3,
530 CLAMP((int)i
*PROFILE_HEIGHT
/LINES
- h
/2, 0, PROFILE_HEIGHT
-h
), pl
);
532 gdk_draw_line (GDK_DRAWABLE(pix
), window
->style
->dark_gc
[0],
533 MARGIN
, PROFILE_HEIGHT
/LINES
* i
, MARGIN
+ PROFILE_WIDTH
, PROFILE_HEIGHT
/LINES
* i
);
534 g_object_unref ( G_OBJECT ( pl
) );
540 for ( i
= 0; i
< PROFILE_WIDTH
; i
++ )
541 gdk_draw_line ( GDK_DRAWABLE(pix
), window
->style
->dark_gc
[3],
542 i
+ MARGIN
, PROFILE_HEIGHT
, i
+ MARGIN
, PROFILE_HEIGHT
-PROFILE_HEIGHT
*(speeds
[i
]-mins
)/(maxs
-mins
) );
546 GdkGC
*gps_speed_gc
= gdk_gc_new ( window
->window
);
549 gdk_color_parse ( "red", &color
);
550 gdk_gc_set_rgb_fg_color ( gps_speed_gc
, &color
);
552 time_t beg_time
= VIK_TRACKPOINT(tr
->trackpoints
->data
)->timestamp
;
553 time_t dur
= VIK_TRACKPOINT(g_list_last(tr
->trackpoints
)->data
)->timestamp
- beg_time
;
555 for (iter
= tr
->trackpoints
; iter
; iter
= iter
->next
) {
556 gdouble gps_speed
= VIK_TRACKPOINT(iter
->data
)->speed
;
557 if (isnan(gps_speed
))
559 int x
= MARGIN
+ PROFILE_WIDTH
* (VIK_TRACKPOINT(iter
->data
)->timestamp
- beg_time
) / dur
;
560 int y
= PROFILE_HEIGHT
- PROFILE_HEIGHT
*(MTOK(gps_speed
) - mins
)/(maxs
- mins
);
561 gdk_draw_rectangle(GDK_DRAWABLE(pix
), gps_speed_gc
, TRUE
, x
-2, y
-2, 4, 4);
565 gdk_draw_rectangle(GDK_DRAWABLE(pix
), window
->style
->black_gc
, FALSE
, MARGIN
, 0, PROFILE_WIDTH
-1, PROFILE_HEIGHT
-1);
567 g_object_unref ( G_OBJECT(pix
) );
568 g_object_unref ( G_OBJECT(gps_speed_gc
) );
571 eventbox
= gtk_event_box_new ();
572 g_signal_connect ( G_OBJECT(eventbox
), "button_press_event", G_CALLBACK(track_vt_click
), pass_along
);
573 g_signal_connect ( G_OBJECT(eventbox
), "motion_notify_event", G_CALLBACK(track_vt_move
), pass_along
);
574 g_signal_connect_swapped ( G_OBJECT(eventbox
), "destroy", G_CALLBACK(g_free
), pass_along
);
575 gtk_container_add ( GTK_CONTAINER(eventbox
), image
);
576 gtk_widget_set_events (eventbox
, GDK_BUTTON_PRESS_MASK
577 | GDK_POINTER_MOTION_MASK
578 | GDK_POINTER_MOTION_HINT_MASK
);
585 static void propwin_response_cb( GtkDialog
*dialog
, gint resp
, PropWidgets
*widgets
)
587 VikTrack
*tr
= widgets
->tr
;
588 VikTrwLayer
*vtl
= widgets
->vtl
;
589 gboolean keep_dialog
= FALSE
;
591 /* FIXME: check and make sure the track still exists before doing anything to it */
592 /* Note: destroying diaglog (eg, parent window exit) won't give "response" */
594 case GTK_RESPONSE_DELETE_EVENT
: /* received delete event (not from buttons) */
595 case GTK_RESPONSE_REJECT
:
597 case GTK_RESPONSE_ACCEPT
:
598 vik_track_set_comment(tr
, gtk_entry_get_text(GTK_ENTRY(widgets
->w_comment
)));
600 case VIK_TRW_LAYER_PROPWIN_REVERSE
:
601 vik_track_reverse(tr
);
602 vik_layer_emit_update ( VIK_LAYER(vtl
) );
604 case VIK_TRW_LAYER_PROPWIN_DEL_DUP
:
605 vik_track_remove_dup_points(tr
);
606 /* above operation could have deleted current_tp or last_tp */
607 trw_layer_cancel_tps_of_track ( vtl
, widgets
->track_name
);
608 vik_layer_emit_update ( VIK_LAYER(vtl
) );
610 case VIK_TRW_LAYER_PROPWIN_SPLIT
:
612 /* get new tracks, add them, resolve naming conflicts (free if cancel), and delete old. old can still exist on clipboard. */
614 VikTrack
**tracks
= vik_track_split_into_segments(tr
, &ntracks
);
617 for ( i
= 0; i
< ntracks
; i
++ )
619 g_assert ( tracks
[i
] );
620 new_tr_name
= g_strdup_printf("%s #%d", widgets
->track_name
, i
+1);
621 /* if ( (wp_exists) && (! overwrite) ) */
622 /* don't need to upper case new_tr_name because old tr name was uppercase */
623 if ( vik_trw_layer_get_track(vtl
, new_tr_name
) &&
624 ( ! a_dialog_overwrite ( VIK_GTK_WINDOW_FROM_LAYER(vtl
), "The track \"%s\" exists, do you wish to overwrite it?", new_tr_name
) ) )
626 gchar
*new_new_tr_name
= a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl
), vik_trw_layer_get_tracks(vtl
) );
627 g_free ( new_tr_name
);
629 new_tr_name
= new_new_tr_name
;
633 vik_track_free ( tracks
[i
] );
637 vik_trw_layer_add_track ( vtl
, new_tr_name
, tracks
[i
] );
642 /* Don't let track destroy this dialog */
643 vik_track_clear_property_dialog(tr
);
644 vik_trw_layer_delete_track ( vtl
, widgets
->track_name
);
645 vik_layer_emit_update ( VIK_LAYER(vtl
) ); /* chase thru the hoops */
649 case VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER
:
651 GList
*iter
= tr
->trackpoints
;
652 while ((iter
= iter
->next
)) {
653 if (widgets
->marker_tp
== VIK_TRACKPOINT(iter
->data
))
657 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl
), GTK_MESSAGE_ERROR
,
658 _("Failed spliting track. Track unchanged"), NULL
);
663 gchar
*r_name
= g_strdup_printf("%s #R", widgets
->track_name
);
664 if (vik_trw_layer_get_track(vtl
, r_name
) &&
665 ( ! a_dialog_overwrite( VIK_GTK_WINDOW_FROM_LAYER(vtl
),
666 "The track \"%s\" exists, do you wish to overwrite it?", r_name
)))
668 gchar
*new_r_name
= a_dialog_new_track( VIK_GTK_WINDOW_FROM_LAYER(vtl
), vik_trw_layer_get_tracks(vtl
) );
674 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl
), GTK_MESSAGE_WARNING
,
675 _("Operation Aborted. Track unchanged"), NULL
);
680 iter
->prev
->next
= NULL
;
682 VikTrack
*tr_right
= vik_track_new();
684 vik_track_set_comment ( tr_right
, tr
->comment
);
685 tr_right
->visible
= tr
->visible
;
686 tr_right
->trackpoints
= iter
;
688 vik_trw_layer_add_track(vtl
, r_name
, tr_right
);
689 vik_layer_emit_update ( VIK_LAYER(vtl
) );
693 fprintf(stderr
, "DEBUG: unknown response\n");
697 /* Keep same behaviour for now: destroy dialog if click on any button */
699 prop_widgets_free(widgets
);
700 vik_track_clear_property_dialog(tr
);
701 gtk_widget_destroy ( GTK_WIDGET(dialog
) );
705 static GtkWidget
*create_graph_page ( GtkWidget
*graph
,
709 GtkWidget
*hbox
= gtk_hbox_new ( FALSE
, 10 );
710 GtkWidget
*vbox
= gtk_vbox_new ( FALSE
, 10 );
711 GtkWidget
*label
= gtk_label_new (NULL
);
712 gtk_box_pack_start (GTK_BOX(vbox
), graph
, FALSE
, FALSE
, 0);
713 gtk_label_set_markup ( GTK_LABEL(label
), markup
);
714 gtk_box_pack_start (GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
715 gtk_box_pack_start (GTK_BOX(hbox
), value
, FALSE
, FALSE
, 0);
716 gtk_box_pack_start (GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
721 void vik_trw_layer_propwin_run ( GtkWindow
*parent
, VikTrwLayer
*vtl
, VikTrack
*tr
, gpointer vlp
, gchar
*track_name
)
723 /* FIXME: free widgets when destroy signal received */
724 PropWidgets
*widgets
= prop_widgets_new();
728 widgets
->track_name
= track_name
;
729 gchar
*title
= g_strdup_printf(_("%s - Track Properties"), track_name
);
730 GtkWidget
*dialog
= gtk_dialog_new_with_buttons (title
,
732 GTK_DIALOG_DESTROY_WITH_PARENT
| GTK_DIALOG_NO_SEPARATOR
,
733 GTK_STOCK_CANCEL
, GTK_RESPONSE_REJECT
,
734 _("Split at Marker"), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER
,
735 _("Split Segments"), VIK_TRW_LAYER_PROPWIN_SPLIT
,
736 _("Reverse"), VIK_TRW_LAYER_PROPWIN_REVERSE
,
737 _("Delete Dupl."), VIK_TRW_LAYER_PROPWIN_DEL_DUP
,
738 GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
,
740 widgets
->dialog
= dialog
;
742 g_signal_connect(dialog
, "response", G_CALLBACK(propwin_response_cb
), widgets
);
743 //fprintf(stderr, "DEBUG: dialog=0x%p\n", dialog);
746 guint32 tp_count
, seg_count
;
748 gdouble min_alt
, max_alt
;
749 GtkWidget
*profile
= vik_trw_layer_create_profile(GTK_WIDGET(parent
),tr
, vlp
, widgets
, &min_alt
,&max_alt
);
750 GtkWidget
*vtdiag
= vik_trw_layer_create_vtdiag(GTK_WIDGET(parent
), tr
, vlp
, widgets
);
751 GtkWidget
*graphs
= gtk_notebook_new();
753 widgets
->elev_box
= profile
;
754 widgets
->speed_box
= vtdiag
;
756 GtkWidget
*content
[20];
760 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>") };
761 static gchar tmp_buf
[50];
765 widgets
->w_comment
= gtk_entry_new ();
767 gtk_entry_set_text ( GTK_ENTRY(widgets
->w_comment
), tr
->comment
);
768 g_signal_connect_swapped ( widgets
->w_comment
, "activate", G_CALLBACK(a_dialog_response_accept
), GTK_DIALOG(dialog
) );
769 content
[cnt
++] = widgets
->w_comment
;
771 tr_len
= widgets
->track_length
= vik_track_get_length(tr
);
772 g_snprintf(tmp_buf
, sizeof(tmp_buf
), "%.2f m", tr_len
);
773 widgets
->w_track_length
= content
[cnt
++] = gtk_label_new ( tmp_buf
);
775 tp_count
= vik_track_get_tp_count(tr
);
776 g_snprintf(tmp_buf
, sizeof(tmp_buf
), "%u", tp_count
);
777 widgets
->w_tp_count
= content
[cnt
++] = gtk_label_new ( tmp_buf
);
779 seg_count
= vik_track_get_segment_count(tr
) ;
780 g_snprintf(tmp_buf
, sizeof(tmp_buf
), "%u", seg_count
);
781 widgets
->w_segment_count
= content
[cnt
++] = gtk_label_new ( tmp_buf
);
783 g_snprintf(tmp_buf
, sizeof(tmp_buf
), "%lu", vik_track_get_dup_point_count(tr
) );
784 widgets
->w_duptp_count
= content
[cnt
++] = gtk_label_new ( tmp_buf
);
786 tmp_speed
= vik_track_get_max_speed(tr
);
787 if ( tmp_speed
== 0 )
788 g_snprintf(tmp_buf
, sizeof(tmp_buf
), _("No Data"));
790 g_snprintf(tmp_buf
, sizeof(tmp_buf
), "%.2f m/s (%.0f km/h)", tmp_speed
, MTOK(tmp_speed
) );
791 widgets
->w_max_speed
= content
[cnt
++] = gtk_label_new ( tmp_buf
);
793 tmp_speed
= vik_track_get_average_speed(tr
);
794 if ( tmp_speed
== 0 )
795 g_snprintf(tmp_buf
, sizeof(tmp_buf
), _("No Data"));
797 g_snprintf(tmp_buf
, sizeof(tmp_buf
), "%.2f m/s (%.0f km/h)", tmp_speed
, MTOK(tmp_speed
) );
798 widgets
->w_avg_speed
= content
[cnt
++] = gtk_label_new ( tmp_buf
);
800 g_snprintf(tmp_buf
, sizeof(tmp_buf
), "%.2f m", (tp_count
- seg_count
) == 0 ? 0 : tr_len
/ ( tp_count
- seg_count
) );
801 widgets
->w_avg_dist
= content
[cnt
++] = gtk_label_new ( tmp_buf
);
803 if ( min_alt
== VIK_DEFAULT_ALTITUDE
)
804 g_snprintf(tmp_buf
, sizeof(tmp_buf
), _("No Data"));
806 g_snprintf(tmp_buf
, sizeof(tmp_buf
), "%.0f m - %.0f m", min_alt
, max_alt
);
807 widgets
->w_elev_range
= content
[cnt
++] = gtk_label_new ( tmp_buf
);
809 vik_track_get_total_elevation_gain(tr
, &max_alt
, &min_alt
);
810 if ( min_alt
== VIK_DEFAULT_ALTITUDE
)
811 g_snprintf(tmp_buf
, sizeof(tmp_buf
), _("No Data"));
813 g_snprintf(tmp_buf
, sizeof(tmp_buf
), "%.0f m / %.0f m", max_alt
, min_alt
);
814 widgets
->w_elev_gain
= content
[cnt
++] = gtk_label_new ( tmp_buf
);
817 #define PACK(w) gtk_box_pack_start (GTK_BOX(right_vbox), w, FALSE, FALSE, 0);
818 gtk_box_pack_start (GTK_BOX(right_vbox
), e_cmt
, FALSE
, FALSE
, 0);
831 if ( tr
->trackpoints
&& VIK_TRACKPOINT(tr
->trackpoints
->data
)->timestamp
)
834 t1
= VIK_TRACKPOINT(tr
->trackpoints
->data
)->timestamp
;
835 t2
= VIK_TRACKPOINT(g_list_last(tr
->trackpoints
)->data
)->timestamp
;
837 strncpy(tmp_buf
, ctime(&t1
), sizeof(tmp_buf
));
838 tmp_buf
[sizeof(tmp_buf
)-1] = 0;
840 widgets
->w_time_start
= content
[cnt
++] = gtk_label_new(tmp_buf
);
842 strncpy(tmp_buf
, ctime(&t2
), sizeof(tmp_buf
));
843 tmp_buf
[sizeof(tmp_buf
)-1] = 0;
845 widgets
->w_time_end
= content
[cnt
++] = gtk_label_new(tmp_buf
);
847 g_snprintf(tmp_buf
, sizeof(tmp_buf
), _("%d minutes"), (int)(t2
-t1
)/60);
848 widgets
->w_time_dur
= content
[cnt
++] = gtk_label_new(tmp_buf
);
850 widgets
->w_time_start
= content
[cnt
++] = gtk_label_new(_("No Data"));
851 widgets
->w_time_end
= content
[cnt
++] = gtk_label_new(_("No Data"));
852 widgets
->w_time_dur
= content
[cnt
++] = gtk_label_new(_("No Data"));
855 table
= GTK_TABLE(gtk_table_new (cnt
, 2, FALSE
));
856 gtk_table_set_col_spacing (table
, 0, 10);
857 for (i
=0; i
<cnt
; i
++) {
860 label
= gtk_label_new(NULL
);
861 gtk_misc_set_alignment ( GTK_MISC(label
), 1, 0 );
862 gtk_label_set_markup ( GTK_LABEL(label
), _(label_texts
[i
]) );
863 gtk_table_attach_defaults ( table
, label
, 0, 1, i
, i
+1 );
864 if (GTK_IS_MISC(content
[i
])) {
865 gtk_misc_set_alignment ( GTK_MISC(content
[i
]), 0, 0 );
867 gtk_table_attach_defaults ( table
, content
[i
], 1, 2, i
, i
+1 );
870 gtk_notebook_append_page(GTK_NOTEBOOK(graphs
), GTK_WIDGET(table
), gtk_label_new(_("Statistics")));
873 GtkWidget
*page
= NULL
;
874 widgets
->w_cur_dist
= gtk_label_new(_("No Data"));
875 page
= create_graph_page (profile
, _("<b>Track Distance:</b>"), widgets
->w_cur_dist
);
876 gtk_notebook_append_page(GTK_NOTEBOOK(graphs
), page
, gtk_label_new(_("Elevation-distance")));
880 GtkWidget
*page
= NULL
;
881 widgets
->w_cur_time
= gtk_label_new(_("No Data"));
882 page
= create_graph_page (vtdiag
, _("<b>Track Time:</b>"), widgets
->w_cur_time
);
883 gtk_notebook_append_page(GTK_NOTEBOOK(graphs
), page
, gtk_label_new(_("Speed-time")));
886 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog
)->vbox
), graphs
, FALSE
, FALSE
, 0);
888 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog
), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER
, FALSE
);
890 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog
), VIK_TRW_LAYER_PROPWIN_SPLIT
, FALSE
);
891 if (vik_track_get_dup_point_count(tr
) <= 0)
892 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog
), VIK_TRW_LAYER_PROPWIN_DEL_DUP
, FALSE
);
894 vik_track_set_property_dialog(tr
, dialog
);
895 gtk_widget_show_all ( dialog
);