its about that time
[ardour2.git] / gtk2_ardour / editor_summary.cc
blob442fca93b64dbd1af13fb37d5ff906143b10bd34
1 /*
2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include "ardour/session.h"
21 #include "time_axis_view.h"
22 #include "streamview.h"
23 #include "editor_summary.h"
24 #include "gui_thread.h"
25 #include "editor.h"
26 #include "region_view.h"
27 #include "rgb_macros.h"
28 #include "keyboard.h"
29 #include "editor_routes.h"
30 #include "editor_cursors.h"
31 #include "mouse_cursors.h"
32 #include "route_time_axis.h"
34 using namespace std;
35 using namespace ARDOUR;
36 using Gtkmm2ext::Keyboard;
38 /** Construct an EditorSummary.
39 * @param e Editor to represent.
41 EditorSummary::EditorSummary (Editor* e)
42 : EditorComponent (e),
43 _start (0),
44 _end (1),
45 _overhang_fraction (0.1),
46 _x_scale (1),
47 _track_height (16),
48 _last_playhead (-1),
49 _move_dragging (false),
50 _moved (false),
51 _view_rectangle_x (0, 0),
52 _view_rectangle_y (0, 0),
53 _zoom_dragging (false),
54 _old_follow_playhead (false)
56 Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context());
57 _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), ui_bind (&EditorSummary::playhead_position_changed, this, _1), gui_context());
59 add_events (Gdk::POINTER_MOTION_MASK);
62 /** Connect to a session.
63 * @param s Session.
65 void
66 EditorSummary::set_session (Session* s)
68 SessionHandlePtr::set_session (s);
70 set_dirty ();
72 /* Note: the EditorSummary already finds out about new regions from Editor::region_view_added
73 * (which attaches to StreamView::RegionViewAdded), and cut regions by the RegionPropertyChanged
74 * emitted when a cut region is added to the `cutlist' playlist.
77 if (_session) {
78 _session->StartTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_dirty, this), gui_context());
79 _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_dirty, this), gui_context());
83 /** Handle an expose event.
84 * @param event Event from GTK.
86 bool
87 EditorSummary::on_expose_event (GdkEventExpose* event)
89 CairoWidget::on_expose_event (event);
91 if (_session == 0) {
92 return false;
95 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
97 /* Render the view rectangle. If there is an editor visual pending, don't update
98 the view rectangle now --- wait until the expose event that we'll get after
99 the visual change. This prevents a flicker.
102 if (_editor->pending_visual_change.idle_handler_id < 0) {
103 get_editor (&_view_rectangle_x, &_view_rectangle_y);
106 cairo_move_to (cr, _view_rectangle_x.first, _view_rectangle_y.first);
107 cairo_line_to (cr, _view_rectangle_x.second, _view_rectangle_y.first);
108 cairo_line_to (cr, _view_rectangle_x.second, _view_rectangle_y.second);
109 cairo_line_to (cr, _view_rectangle_x.first, _view_rectangle_y.second);
110 cairo_line_to (cr, _view_rectangle_x.first, _view_rectangle_y.first);
111 cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
112 cairo_fill_preserve (cr);
113 cairo_set_line_width (cr, 1);
114 cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
115 cairo_stroke (cr);
117 /* Playhead */
119 cairo_set_line_width (cr, 1);
120 /* XXX: colour should be set from configuration file */
121 cairo_set_source_rgba (cr, 1, 0, 0, 1);
123 double const p = (_editor->playhead_cursor->current_frame - _start) * _x_scale;
124 cairo_move_to (cr, p, 0);
125 cairo_line_to (cr, p, _height);
126 cairo_stroke (cr);
127 _last_playhead = p;
129 cairo_destroy (cr);
131 return true;
134 /** Render the required regions to a cairo context.
135 * @param cr Context.
137 void
138 EditorSummary::render (cairo_t* cr)
140 /* background */
142 cairo_set_source_rgb (cr, 0, 0, 0);
143 cairo_rectangle (cr, 0, 0, _width, _height);
144 cairo_fill (cr);
146 if (_session == 0) {
147 return;
150 /* compute start and end points for the summary */
152 framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
153 double const theoretical_start = _session->current_start_frame() - session_length * _overhang_fraction;
154 _start = theoretical_start > 0 ? theoretical_start : 0;
155 _end = _session->current_end_frame() + session_length * _overhang_fraction;
157 /* compute track height */
158 int N = 0;
159 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
160 if (!(*i)->hidden()) {
161 ++N;
165 if (N == 0) {
166 _track_height = 16;
167 } else {
168 _track_height = (double) _height / N;
171 /* calculate x scale */
172 if (_end != _start) {
173 _x_scale = static_cast<double> (_width) / (_end - _start);
174 } else {
175 _x_scale = 1;
178 /* render tracks and regions */
180 double y = 0;
181 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
183 if ((*i)->hidden()) {
184 continue;
187 cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
188 cairo_set_line_width (cr, _track_height - 2);
189 cairo_move_to (cr, 0, y + _track_height / 2);
190 cairo_line_to (cr, _width, y + _track_height / 2);
191 cairo_stroke (cr);
193 StreamView* s = (*i)->view ();
195 if (s) {
196 cairo_set_line_width (cr, _track_height * 0.6);
198 s->foreach_regionview (sigc::bind (
199 sigc::mem_fun (*this, &EditorSummary::render_region),
201 y + _track_height / 2
205 y += _track_height;
208 /* start and end markers */
210 cairo_set_line_width (cr, 1);
211 cairo_set_source_rgb (cr, 1, 1, 0);
213 double const p = (_session->current_start_frame() - _start) * _x_scale;
214 cairo_move_to (cr, p, 0);
215 cairo_line_to (cr, p, _height);
216 cairo_stroke (cr);
218 double const q = (_session->current_end_frame() - _start) * _x_scale;
219 cairo_move_to (cr, q, 0);
220 cairo_line_to (cr, q, _height);
221 cairo_stroke (cr);
224 /** Render a region for the summary.
225 * @param r Region view.
226 * @param cr Cairo context.
227 * @param y y coordinate to render at.
229 void
230 EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const
232 uint32_t const c = r->get_fill_color ();
233 cairo_set_source_rgb (cr, UINT_RGBA_R (c) / 255.0, UINT_RGBA_G (c) / 255.0, UINT_RGBA_B (c) / 255.0);
235 if (r->region()->position() > _start) {
236 cairo_move_to (cr, (r->region()->position() - _start) * _x_scale, y);
237 } else {
238 cairo_move_to (cr, 0, y);
241 if ((r->region()->position() + r->region()->length()) > _start) {
242 cairo_line_to (cr, ((r->region()->position() - _start + r->region()->length())) * _x_scale, y);
243 } else {
244 cairo_line_to (cr, 0, y);
247 cairo_stroke (cr);
250 /** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
251 void
252 EditorSummary::set_overlays_dirty ()
254 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty)
255 queue_draw ();
258 /** Handle a size request.
259 * @param req GTK requisition
261 void
262 EditorSummary::on_size_request (Gtk::Requisition *req)
264 /* Use a dummy, small width and the actual height that we want */
265 req->width = 64;
266 req->height = 32;
270 void
271 EditorSummary::centre_on_click (GdkEventButton* ev)
273 pair<double, double> xr;
274 pair<double, double> yr;
275 get_editor (&xr, &yr);
277 double const w = xr.second - xr.first;
279 xr.first = ev->x - w / 2;
280 xr.second = ev->x + w / 2;
282 if (xr.first < 0) {
283 xr.first = 0;
284 xr.second = w;
285 } else if (xr.second > _width) {
286 xr.second = _width;
287 xr.first = _width - w;
290 double ey = summary_y_to_editor (ev->y);
291 ey -= (_editor->canvas_height() - _editor->get_canvas_timebars_vsize ()) / 2;
292 if (ey < 0) {
293 ey = 0;
296 set_editor (xr, editor_y_to_summary (ey));
299 /** Handle a button press.
300 * @param ev GTK event.
302 bool
303 EditorSummary::on_button_press_event (GdkEventButton* ev)
305 if (ev->button == 1) {
307 pair<double, double> xr;
308 pair<double, double> yr;
309 get_editor (&xr, &yr);
311 _start_editor_x = xr;
312 _start_editor_y = yr;
313 _start_mouse_x = ev->x;
314 _start_mouse_y = ev->y;
315 _start_position = get_position (ev->x, ev->y);
317 if (_start_position != INSIDE && _start_position != BELOW_OR_ABOVE &&
318 _start_position != TO_LEFT_OR_RIGHT && _start_position != OTHERWISE_OUTSIDE
321 /* start a zoom drag */
323 _zoom_position = get_position (ev->x, ev->y);
324 _zoom_dragging = true;
325 _editor->_dragging_playhead = true;
326 _old_follow_playhead = _editor->follow_playhead ();
327 _editor->set_follow_playhead (false);
329 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
331 /* secondary-modifier-click: locate playhead */
332 if (_session) {
333 _session->request_locate (ev->x / _x_scale + _start);
336 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
338 centre_on_click (ev);
340 } else {
342 /* start a move drag */
344 _move_dragging = true;
345 _moved = false;
346 _editor->_dragging_playhead = true;
347 _old_follow_playhead = _editor->follow_playhead ();
348 _editor->set_follow_playhead (false);
352 return true;
355 /** Fill in x and y with the editor's current viewable area in summary coordinates */
356 void
357 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
359 assert (x);
360 assert (y);
362 x->first = (_editor->leftmost_position () - _start) * _x_scale;
363 x->second = x->first + _editor->current_page_frames() * _x_scale;
365 y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
366 y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->canvas_height() - _editor->get_canvas_timebars_vsize());
369 /** Get an expression of the position of a point with respect to the view rectangle */
370 EditorSummary::Position
371 EditorSummary::get_position (double x, double y) const
373 /* how close the mouse has to be to the edge of the view rectangle to be considered `on it',
374 in pixels */
376 int x_edge_size = (_view_rectangle_x.second - _view_rectangle_x.first) / 4;
377 x_edge_size = min (x_edge_size, 8);
378 x_edge_size = max (x_edge_size, 1);
380 int y_edge_size = (_view_rectangle_y.second - _view_rectangle_y.first) / 4;
381 y_edge_size = min (y_edge_size, 8);
382 y_edge_size = max (y_edge_size, 1);
384 bool const near_left = (std::abs (x - _view_rectangle_x.first) < x_edge_size);
385 bool const near_right = (std::abs (x - _view_rectangle_x.second) < x_edge_size);
386 bool const near_top = (std::abs (y - _view_rectangle_y.first) < y_edge_size);
387 bool const near_bottom = (std::abs (y - _view_rectangle_y.second) < y_edge_size);
388 bool const within_x = _view_rectangle_x.first < x && x < _view_rectangle_x.second;
389 bool const within_y = _view_rectangle_y.first < y && y < _view_rectangle_y.second;
391 if (near_left && near_top) {
392 return LEFT_TOP;
393 } else if (near_left && near_bottom) {
394 return LEFT_BOTTOM;
395 } else if (near_right && near_top) {
396 return RIGHT_TOP;
397 } else if (near_right && near_bottom) {
398 return RIGHT_BOTTOM;
399 } else if (near_left && within_y) {
400 return LEFT;
401 } else if (near_right && within_y) {
402 return RIGHT;
403 } else if (near_top && within_x) {
404 return TOP;
405 } else if (near_bottom && within_x) {
406 return BOTTOM;
407 } else if (within_x && within_y) {
408 return INSIDE;
409 } else if (within_x) {
410 return BELOW_OR_ABOVE;
411 } else if (within_y) {
412 return TO_LEFT_OR_RIGHT;
413 } else {
414 return OTHERWISE_OUTSIDE;
418 void
419 EditorSummary::set_cursor (Position p)
421 switch (p) {
422 case LEFT:
423 get_window()->set_cursor (*_editor->_cursors->resize_left);
424 break;
425 case LEFT_TOP:
426 get_window()->set_cursor (*_editor->_cursors->resize_top_left);
427 break;
428 case TOP:
429 get_window()->set_cursor (*_editor->_cursors->resize_top);
430 break;
431 case RIGHT_TOP:
432 get_window()->set_cursor (*_editor->_cursors->resize_top_right);
433 break;
434 case RIGHT:
435 get_window()->set_cursor (*_editor->_cursors->resize_right);
436 break;
437 case RIGHT_BOTTOM:
438 get_window()->set_cursor (*_editor->_cursors->resize_bottom_right);
439 break;
440 case BOTTOM:
441 get_window()->set_cursor (*_editor->_cursors->resize_bottom);
442 break;
443 case LEFT_BOTTOM:
444 get_window()->set_cursor (*_editor->_cursors->resize_bottom_left);
445 break;
446 case INSIDE:
447 get_window()->set_cursor (*_editor->_cursors->move);
448 break;
449 case TO_LEFT_OR_RIGHT:
450 get_window()->set_cursor (*_editor->_cursors->expand_left_right);
451 break;
452 case BELOW_OR_ABOVE:
453 get_window()->set_cursor (*_editor->_cursors->expand_up_down);
454 break;
455 default:
456 get_window()->set_cursor ();
457 break;
461 bool
462 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
464 pair<double, double> xr = _start_editor_x;
465 pair<double, double> yr = _start_editor_y;
466 double y = _start_editor_y.first;
468 if (_move_dragging) {
470 _moved = true;
472 /* don't alter x if we clicked outside and above or below the viewbox */
473 if (_start_position == INSIDE || _start_position == TO_LEFT_OR_RIGHT || _start_position == OTHERWISE_OUTSIDE) {
474 xr.first += ev->x - _start_mouse_x;
475 xr.second += ev->x - _start_mouse_x;
478 /* don't alter y if we clicked outside and to the left or right of the viewbox */
479 if (_start_position == INSIDE || _start_position == BELOW_OR_ABOVE) {
480 y += ev->y - _start_mouse_y;
483 if (xr.first < 0) {
484 xr.second -= xr.first;
485 xr.first = 0;
488 if (y < 0) {
489 y = 0;
492 set_editor (xr, y);
493 set_cursor (_start_position);
495 } else if (_zoom_dragging) {
497 double const dx = ev->x - _start_mouse_x;
498 double const dy = ev->y - _start_mouse_y;
500 if (_zoom_position == LEFT || _zoom_position == LEFT_TOP || _zoom_position == LEFT_BOTTOM) {
501 xr.first += dx;
502 } else if (_zoom_position == RIGHT || _zoom_position == RIGHT_TOP || _zoom_position == RIGHT_BOTTOM) {
503 xr.second += dx;
506 if (_zoom_position == TOP || _zoom_position == LEFT_TOP || _zoom_position == RIGHT_TOP) {
507 yr.first += dy;
508 } else if (_zoom_position == BOTTOM || _zoom_position == LEFT_BOTTOM || _zoom_position == RIGHT_BOTTOM) {
509 yr.second += dy;
512 set_overlays_dirty ();
513 set_cursor (_zoom_position);
514 set_editor (xr, yr);
516 } else {
518 set_cursor (get_position (ev->x, ev->y));
522 return true;
525 bool
526 EditorSummary::on_button_release_event (GdkEventButton*)
528 _move_dragging = false;
529 _zoom_dragging = false;
530 _editor->_dragging_playhead = false;
531 _editor->set_follow_playhead (_old_follow_playhead, false);
533 return true;
536 bool
537 EditorSummary::on_scroll_event (GdkEventScroll* ev)
539 /* mouse wheel */
541 pair<double, double> xr;
542 pair<double, double> yr;
543 get_editor (&xr, &yr);
544 double y = yr.first;
546 double amount = 8;
548 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
549 amount = 64;
550 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
551 amount = 1;
554 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
556 /* primary-wheel == left-right scrolling */
558 if (ev->direction == GDK_SCROLL_UP) {
559 xr.first += amount;
560 xr.second += amount;
561 } else if (ev->direction == GDK_SCROLL_DOWN) {
562 xr.first -= amount;
563 xr.second -= amount;
566 } else {
568 if (ev->direction == GDK_SCROLL_DOWN) {
569 y += amount;
570 } else if (ev->direction == GDK_SCROLL_UP) {
571 y -= amount;
572 } else if (ev->direction == GDK_SCROLL_LEFT) {
573 xr.first -= amount;
574 xr.second -= amount;
575 } else if (ev->direction == GDK_SCROLL_RIGHT) {
576 xr.first += amount;
577 xr.second += amount;
581 set_editor (xr, y);
582 return true;
585 /** Set the editor to display a given x range and a y range with the top at a given position.
586 * The editor's x zoom is adjusted if necessary, but the y zoom is not changed.
587 * x and y parameters are specified in summary coordinates.
589 void
590 EditorSummary::set_editor (pair<double,double> const & x, double const y)
592 if (_editor->pending_visual_change.idle_handler_id >= 0) {
594 /* As a side-effect, the Editor's visual change idle handler processes
595 pending GTK events. Hence this motion notify handler can be called
596 in the middle of a visual change idle handler, and if this happens,
597 the queue_visual_change calls below modify the variables that the
598 idle handler is working with. This causes problems. Hence this
599 check. It ensures that we won't modify the pending visual change
600 while a visual change idle handler is in progress. It's not perfect,
601 as it also means that we won't change these variables if an idle handler
602 is merely pending but not executing. But c'est la vie.
605 return;
608 set_editor_x (x);
609 set_editor_y (y);
612 /** Set the editor to display given x and y ranges. x zoom and track heights are
613 * adjusted if necessary.
614 * x and y parameters are specified in summary coordinates.
616 void
617 EditorSummary::set_editor (pair<double,double> const & x, pair<double, double> const & y)
619 if (_editor->pending_visual_change.idle_handler_id >= 0) {
620 /* see comment in other set_editor () */
621 return;
624 set_editor_x (x);
625 set_editor_y (y);
628 /** Set the x range visible in the editor.
629 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
630 * @param x new x range in summary coordinates.
632 void
633 EditorSummary::set_editor_x (pair<double, double> const & x)
635 _editor->reset_x_origin (x.first / _x_scale + _start);
637 double const nx = (
638 ((x.second - x.first) / _x_scale) /
639 _editor->frame_to_unit (_editor->current_page_frames())
642 if (nx != _editor->get_current_zoom ()) {
643 _editor->reset_zoom (nx);
647 /** Set the top of the y range visible in the editor.
648 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
649 * @param y new editor top in summary coodinates.
651 void
652 EditorSummary::set_editor_y (double const y)
654 double y1 = summary_y_to_editor (y);
655 double const eh = _editor->canvas_height() - _editor->get_canvas_timebars_vsize ();
656 double y2 = y1 + eh;
658 double const full_editor_height = _editor->full_canvas_height - _editor->get_canvas_timebars_vsize();
660 if (y2 > full_editor_height) {
661 y1 -= y2 - full_editor_height;
664 if (y1 < 0) {
665 y1 = 0;
668 _editor->reset_y_origin (y1);
671 /** Set the y range visible in the editor. This is achieved by scaling track heights,
672 * if necessary.
673 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
674 * @param y new editor range in summary coodinates.
676 void
677 EditorSummary::set_editor_y (pair<double, double> const & y)
679 /* Compute current height of tracks between y.first and y.second. We add up
680 the total height into `total_height' and the height of complete tracks into
681 `scale height'.
684 /* Copy of target range for use below */
685 pair<double, double> yc = y;
686 /* Total height of all tracks */
687 double total_height = 0;
688 /* Height of any parts of tracks that aren't fully in the desired range */
689 double partial_height = 0;
690 /* Height of any tracks that are fully in the desired range */
691 double scale_height = 0;
693 _editor->_routes->suspend_redisplay ();
695 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
697 if ((*i)->hidden()) {
698 continue;
701 double const h = (*i)->effective_height ();
702 total_height += h;
704 if (yc.first > 0 && yc.first < _track_height) {
705 partial_height += (_track_height - yc.first) * h / _track_height;
706 } else if (yc.first <= 0 && yc.second >= _track_height) {
707 scale_height += h;
708 } else if (yc.second > 0 && yc.second < _track_height) {
709 partial_height += yc.second * h / _track_height;
710 break;
713 yc.first -= _track_height;
714 yc.second -= _track_height;
717 /* Height that we will use for scaling; use the whole editor height unless there are not
718 enough tracks to fill it.
720 double const ch = min (total_height, _editor->canvas_height() - _editor->get_canvas_timebars_vsize());
722 /* hence required scale factor of the complete tracks to fit the required y range;
723 the amount of space they should take up divided by the amount they currently take up.
725 double const scale = (ch - partial_height) / scale_height;
727 yc = y;
729 /* Scale complete tracks within the range to make it fit */
731 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
733 if ((*i)->hidden()) {
734 continue;
737 if (yc.first <= 0 && yc.second >= _track_height) {
738 (*i)->set_height (max (TimeAxisView::preset_height (HeightSmall), (uint32_t) ((*i)->effective_height() * scale)));
741 yc.first -= _track_height;
742 yc.second -= _track_height;
745 _editor->_routes->resume_redisplay ();
747 set_editor_y (y.first);
750 void
751 EditorSummary::playhead_position_changed (framepos_t p)
753 if (_session && int (p * _x_scale) != int (_last_playhead)) {
754 set_overlays_dirty ();
758 double
759 EditorSummary::summary_y_to_editor (double y) const
761 double ey = 0;
762 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
764 if ((*i)->hidden()) {
765 continue;
768 double const h = (*i)->effective_height ();
769 if (y < _track_height) {
770 /* in this track */
771 return ey + y * h / _track_height;
774 ey += h;
775 y -= _track_height;
778 return ey;
781 double
782 EditorSummary::editor_y_to_summary (double y) const
784 double sy = 0;
785 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
787 if ((*i)->hidden()) {
788 continue;
791 double const h = (*i)->effective_height ();
792 if (y < h) {
793 /* in this track */
794 return sy + y * _track_height / h;
797 sy += _track_height;
798 y -= h;
801 return sy;
804 void
805 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
807 /* Connect to gui_changed() on the routes so that we know when their colour has changed */
808 for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
809 (*i)->route()->gui_changed.connect (*this, invalidator (*this), ui_bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
812 set_dirty ();
815 void
816 EditorSummary::route_gui_changed (string c)
818 if (c == "color") {
819 set_dirty ();