2 * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
3 * Copyright (C) 2009-2015 David Robillard <d@drobilla.net>
4 * Copyright (C) 2009-2019 Paul Davis <paul@linuxaudiosystems.com>
5 * Copyright (C) 2012-2013 Colin Fletcher <colin.m.fletcher@googlemail.com>
6 * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org>
7 * Copyright (C) 2014-2016 Nick Mainsbridge <mainsbridge@gmail.com>
8 * Copyright (C) 2015-2016 Tim Mayberry <mojofunk@gmail.com>
9 * Copyright (C) 2017-2019 Ben Loftis <ben@harrisonconsoles.com>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include "ardour/session.h"
28 #include "canvas/debug.h"
30 #include <gtkmm/menu.h>
31 #include <gtkmm/menuitem.h>
33 #include "context_menu_helper.h"
34 #include "time_axis_view.h"
35 #include "streamview.h"
36 #include "editor_summary.h"
37 #include "gui_thread.h"
39 #include "region_view.h"
40 #include "rgb_macros.h"
42 #include "editor_routes.h"
43 #include "editor_cursors.h"
44 #include "mouse_cursors.h"
45 #include "route_time_axis.h"
46 #include "ui_config.h"
51 using namespace ARDOUR;
52 using Gtkmm2ext::Keyboard;
54 /** Construct an EditorSummary.
55 * @param e Editor to represent.
57 EditorSummary::EditorSummary (Editor* e)
58 : EditorComponent (e),
64 _move_dragging (false),
65 _view_rectangle_x (0, 0),
66 _view_rectangle_y (0, 0),
67 _zoom_trim_dragging (false),
68 _old_follow_playhead (false),
70 _background_dirty (true)
72 CairoWidget::use_nsglview ();
73 add_events (Gdk::POINTER_MOTION_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
74 set_flags (get_flags() | Gtk::CAN_FOCUS);
76 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &EditorSummary::parameter_changed));
79 EditorSummary::~EditorSummary ()
81 cairo_surface_destroy (_image);
85 EditorSummary::parameter_changed (string p)
88 if (p == "color-regions-using-track-color") {
89 set_background_dirty ();
93 /** Handle a size allocation.
94 * @param alloc GTK allocation.
97 EditorSummary::on_size_allocate (Gtk::Allocation& alloc)
99 CairoWidget::on_size_allocate (alloc);
100 set_background_dirty ();
104 /** Connect to a session.
108 EditorSummary::set_session (Session* s)
110 SessionHandlePtr::set_session (s);
114 /* Note: the EditorSummary already finds out about new regions from Editor::region_view_added
115 * (which attaches to StreamView::RegionViewAdded), and cut regions by the RegionPropertyChanged
116 * emitted when a cut region is added to the `cutlist' playlist.
120 Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
121 PresentationInfo::Change.connect (route_ctrl_id_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
122 _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), boost::bind (&EditorSummary::playhead_position_changed, this, _1), gui_context());
123 _session->StartTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
124 _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
125 _editor->selection->RegionsChanged.connect (sigc::mem_fun(*this, &EditorSummary::set_background_dirty));
128 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &EditorSummary::set_colors));
132 _leftmost = max_samplepos;
137 EditorSummary::render_background_image ()
139 cairo_surface_destroy (_image); // passing NULL is safe
140 _image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, get_width (), get_height ());
142 cairo_t* cr = cairo_create (_image);
144 /* background (really just the dividing lines between tracks */
146 cairo_set_source_rgb (cr, 0, 0, 0);
147 cairo_rectangle (cr, 0, 0, get_width(), get_height());
150 /* compute start and end points for the summary */
152 std::pair<samplepos_t, samplepos_t> ext = _editor->session_gui_extents();
153 double theoretical_start = ext.first;
154 double theoretical_end = ext.second;
156 /* the summary should encompass the full extent of everywhere we've visited since the session was opened */
157 if (_leftmost < theoretical_start)
158 theoretical_start = _leftmost;
159 if (_rightmost > theoretical_end)
160 theoretical_end = _rightmost;
163 _start = theoretical_start > 0 ? theoretical_start : 0;
164 _end = theoretical_end < max_samplepos ? theoretical_end : max_samplepos;
166 /* calculate x scale */
167 if (_end != _start) {
168 _x_scale = static_cast<double> (get_width()) / (_end - _start);
173 /* compute track height */
175 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
176 if (!(*i)->hidden()) {
184 _track_height = (double) get_height() / N;
187 /* render tracks and regions */
190 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
192 if ((*i)->hidden()) {
196 /* paint a non-bg colored strip to represent the track itself */
198 if (_track_height > 4) {
199 cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
200 cairo_set_line_width (cr, _track_height - 1);
201 cairo_move_to (cr, 0, y + _track_height / 2);
202 cairo_line_to (cr, get_width(), y + _track_height / 2);
206 StreamView* s = (*i)->view ();
209 cairo_set_line_width (cr, _track_height * 0.8);
211 s->foreach_regionview (sigc::bind (
212 sigc::mem_fun (*this, &EditorSummary::render_region),
214 y + _track_height / 2
221 /* start and end markers */
223 cairo_set_line_width (cr, 1);
224 cairo_set_source_rgb (cr, 1, 1, 0);
226 const double p = (_session->current_start_sample() - _start) * _x_scale;
227 cairo_move_to (cr, p, 0);
228 cairo_line_to (cr, p, get_height());
230 double const q = (_session->current_end_sample() - _start) * _x_scale;
231 cairo_move_to (cr, q, 0);
232 cairo_line_to (cr, q, get_height());
238 /** Render the required regions to a cairo context.
242 EditorSummary::render (Cairo::RefPtr<Cairo::Context> const& ctx, cairo_rectangle_t*)
244 cairo_t* cr = ctx->cobj();
250 /* maintain the leftmost and rightmost locations that we've ever reached */
251 samplecnt_t const leftmost = _editor->leftmost_sample ();
252 if (leftmost < _leftmost) {
253 _leftmost = leftmost;
254 _background_dirty = true;
256 samplecnt_t const rightmost = leftmost + _editor->current_page_samples();
257 if (rightmost > _rightmost) {
258 _rightmost = rightmost;
259 _background_dirty = true;
262 /* draw the background (regions, markers, etc) if they've changed */
263 if (!_image || _background_dirty) {
264 render_background_image ();
265 _background_dirty = false;
268 cairo_push_group (cr);
270 /* Fill with the background image */
272 cairo_rectangle (cr, 0, 0, get_width(), get_height());
273 cairo_set_source_surface (cr, _image, 0, 0);
276 /* Render the view rectangle. If there is an editor visual pending, don't update
277 * the view rectangle now --- wait until the expose event that we'll get after
278 * the visual change. This prevents a flicker.
281 if (_editor->pending_visual_change.idle_handler_id < 0) {
282 get_editor (&_view_rectangle_x, &_view_rectangle_y);
285 int32_t width = _view_rectangle_x.second - _view_rectangle_x.first;
287 cairo_rectangle (cr, _view_rectangle_x.first, 0, width, get_height ());
288 cairo_set_source_rgba (cr, 1, 1, 1, 0.15);
292 cairo_rectangle (cr, _view_rectangle_x.first, 0, width, get_height ());
293 cairo_set_line_width (cr, 1);
294 cairo_set_source_rgba (cr, 1, 1, 1, 0.9);
299 cairo_set_line_width (cr, 1);
301 double r,g,b,a; Gtkmm2ext::color_to_rgba(_phead_color, r,g,b,a);
302 cairo_set_source_rgb (cr, r,g,b); // playhead color
304 const double ph= playhead_sample_to_position (_editor->playhead_cursor->current_sample());
305 cairo_move_to (cr, ph, 0);
306 cairo_line_to (cr, ph, get_height());
308 cairo_pop_group_to_source (cr);
315 EditorSummary::set_colors ()
317 _phead_color = UIConfiguration::instance().color ("play head");
322 /** Render a region for the summary.
323 * @param r Region view.
324 * @param cr Cairo context.
325 * @param y y coordinate to render at.
328 EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const
330 /*NOTE: you can optimize this operation by coalescing adjacent regions into a single line stroke.
331 * In a session with a single track ~1,000 regions, this reduced render time from 14ms to 11 ms.
332 * However, you lose a lot of visual information. The current method preserves a sense of separation between regions.
333 * The current method shows the current selection (red regions), which needs to be preserved if this is optimized.
334 * I think it's not worth it for now, but we might choose to revisit this someday.
337 uint32_t const c = r->get_fill_color ();
338 cairo_set_source_rgb (cr, UINT_RGBA_R (c) / 255.0, UINT_RGBA_G (c) / 255.0, UINT_RGBA_B (c) / 255.0);
340 if (r->region()->position() > _start) {
341 cairo_move_to (cr, (r->region()->position() - _start) * _x_scale, y);
343 cairo_move_to (cr, 0, y);
346 if ((r->region()->position() + r->region()->length()) > _start) {
347 cairo_line_to (cr, ((r->region()->position() - _start + r->region()->length())) * _x_scale, y);
349 cairo_line_to (cr, 0, y);
356 EditorSummary::set_background_dirty ()
358 if (!_background_dirty) {
359 _background_dirty = true;
364 /** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
366 EditorSummary::set_overlays_dirty ()
368 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
372 /** Set the summary so that just the overlays (viewbox, playhead etc.) in a given area will be re-rendered */
374 EditorSummary::set_overlays_dirty_rect (int x, int y, int w, int h)
376 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty_rect);
377 queue_draw_area (x, y, w, h);
381 /** Handle a size request.
382 * @param req GTK requisition
385 EditorSummary::on_size_request (Gtk::Requisition *req)
387 /* The left/right buttons will determine our height */
394 EditorSummary::centre_on_click (GdkEventButton* ev)
396 pair<double, double> xr;
399 double const w = xr.second - xr.first;
400 double ex = ev->x - w / 2;
403 } else if ((ex + w) > get_width()) {
404 ex = get_width() - w;
411 EditorSummary::on_enter_notify_event (GdkEventCrossing*)
414 Keyboard::magic_widget_grab_focus ();
419 EditorSummary::on_leave_notify_event (GdkEventCrossing*)
421 /* there are no inferior/child windows, so any leave event means that
424 Keyboard::magic_widget_drop_focus ();
429 EditorSummary::on_key_press_event (GdkEventKey* key)
432 GtkAccelKey set_playhead_accel;
434 /* XXXX this is really ugly and should be using our own action maps and bindings */
436 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
437 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
440 _session->request_locate (_start + (samplepos_t) x / _x_scale, _session->transport_rolling());
450 EditorSummary::on_key_release_event (GdkEventKey* key)
453 GtkAccelKey set_playhead_accel;
455 /* XXXX this is really ugly and should be using our own action maps and bindings */
457 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
458 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
465 #include "gtkmm2ext/utils.h"
467 /** Handle a button press.
468 * @param ev GTK event.
471 EditorSummary::on_button_press_event (GdkEventButton* ev)
473 _old_follow_playhead = _editor->follow_playhead ();
475 if (ev->button == 3) { // right-click: show the reset menu action
476 using namespace Gtk::Menu_Helpers;
477 Gtk::Menu* m = ARDOUR_UI_UTILS::shared_popup_menu ();
478 MenuList& items = m->items ();
479 items.push_back(MenuElem(_("Reset Summary to Extents"),
480 sigc::mem_fun(*this, &EditorSummary::reset_to_extents)));
481 m->popup (ev->button, ev->time);
485 if (ev->button != 1) {
489 pair<double, double> xr;
492 _start_editor_x = xr;
493 _start_mouse_x = ev->x;
494 _start_mouse_y = ev->y;
495 _start_position = get_position (ev->x, ev->y);
497 if (_start_position != INSIDE && _start_position != TO_LEFT_OR_RIGHT) {
499 /* start a zoom_trim drag */
501 _zoom_trim_position = get_position (ev->x, ev->y);
502 _zoom_trim_dragging = true;
503 _editor->_dragging_playhead = true;
504 _editor->set_follow_playhead (false);
506 if (suspending_editor_updates ()) {
507 get_editor (&_pending_editor_x, &_pending_editor_y);
508 _pending_editor_changed = false;
511 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
513 /* secondary-modifier-click: locate playhead */
515 _session->request_locate (ev->x / _x_scale + _start);
518 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
520 centre_on_click (ev);
524 /* start a move+zoom drag */
525 get_editor (&_pending_editor_x, &_pending_editor_y);
526 _pending_editor_changed = false;
527 _editor->_dragging_playhead = true;
528 _editor->set_follow_playhead (false);
530 _move_dragging = true;
538 get_window()->set_cursor (*_editor->_cursors->expand_left_right);
545 /** @return true if we are currently suspending updates to the editor's viewport,
546 * which we do if configured to do so, and if in a drag of some kind.
549 EditorSummary::suspending_editor_updates () const
551 return (!UIConfiguration::instance().get_update_editor_during_summary_drag () && (_zoom_trim_dragging || _move_dragging));
554 /** Fill in x and y with the editor's current viewable area in summary coordinates */
556 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
559 if (suspending_editor_updates ()) {
561 /* We are dragging, and configured not to update the editor window during drags,
562 * so just return where the editor will be when the drag finishes.
565 *x = _pending_editor_x;
567 *y = _pending_editor_y;
572 /* Otherwise query the editor for its actual position */
574 x->first = (_editor->leftmost_sample () - _start) * _x_scale;
575 x->second = x->first + _editor->current_page_samples() * _x_scale;
578 y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
579 y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y);
583 /** Get an expression of the position of a point with respect to the view rectangle */
584 EditorSummary::Position
585 EditorSummary::get_position (double x, double y) const
587 /* how close the mouse has to be to the edge of the view rectangle to be considered `on it',
590 int x_edge_size = (_view_rectangle_x.second - _view_rectangle_x.first) / 4;
591 x_edge_size = min (x_edge_size, 8);
592 x_edge_size = max (x_edge_size, 1);
594 bool const near_left = (std::abs (x - _view_rectangle_x.first) < x_edge_size);
595 bool const near_right = (std::abs (x - _view_rectangle_x.second) < x_edge_size);
596 bool const within_x = _view_rectangle_x.first < x && x < _view_rectangle_x.second;
600 } else if (near_right) {
602 } else if (within_x) {
605 return TO_LEFT_OR_RIGHT;
610 EditorSummary::reset_to_extents()
612 /* reset as if the user never went anywhere outside the extents */
613 _leftmost = max_samplepos;
616 _editor->temporal_zoom_extents ();
617 set_background_dirty ();
622 EditorSummary::set_cursor (Position p)
626 get_window()->set_cursor (*_editor->_cursors->resize_left);
629 get_window()->set_cursor (*_editor->_cursors->resize_right);
632 get_window()->set_cursor (*_editor->_cursors->move);
634 case TO_LEFT_OR_RIGHT:
635 get_window()->set_cursor (*_editor->_cursors->move);
639 get_window()->set_cursor ();
645 EditorSummary::summary_zoom_step (int steps /* positive steps to zoom "out" , negative steps to zoom "in" */ )
647 pair<double, double> xn;
654 /* for now, disallow really close zooming-in from the scroomer. (Currently it
655 * causes the start-offset to 'walk' because of integer limitations.
656 * To fix this, probably need to maintain float throught the get/set_editor() path.)
659 if ((xn.second - xn.first) < 2)
663 set_overlays_dirty ();
669 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
671 if (_move_dragging) {
673 /* To avoid accidental zooming, the mouse must move exactly vertical, not diagonal, to trigger a zoom step
674 * we use screen coordinates for this, not canvas-based grab_x */
676 double dx = mx - _last_mx;
678 double dy = my - _last_my;
680 /* do zooming in windowed "steps" so it feels more reversible ? */
681 const int stepsize = 2;
682 int y_delta = _start_mouse_y - my;
683 y_delta = y_delta / stepsize;
686 const float zscale = 3;
687 if ((dx == 0) && (_last_dx == 0) && (y_delta != _last_y_delta)) {
689 summary_zoom_step (dy * zscale);
691 /* after the zoom we must re-calculate x-pos grabs */
692 pair<double, double> xr;
694 _start_editor_x = xr;
695 _start_mouse_x = ev->x;
697 _last_y_delta = y_delta;
700 /* always track horizontal movement, if any */
703 double x = _start_editor_x.first;
704 x += ev->x - _start_mouse_x;
710 /* zoom-behavior-tweaks: protect the right edge from expanding beyond the end */
711 pair<double, double> xr;
713 double w = xr.second - xr.first;
714 if (x + w < get_width()) {
724 } else if (_zoom_trim_dragging) {
726 pair<double, double> xr = _start_editor_x;
728 double const dx = ev->x - _start_mouse_x;
730 if (_zoom_trim_position == LEFT) {
732 } else if (_zoom_trim_position == RIGHT) {
734 /* zoom-behavior-tweaks: protect the right edge from expanding beyond the edge */
735 if ((xr.second + dx) < get_width()) {
741 xr.first = -1; /* do not change */
744 set_overlays_dirty ();
745 set_cursor (_zoom_trim_position);
749 set_cursor (get_position (ev->x, ev->y));
756 EditorSummary::on_button_release_event (GdkEventButton*)
758 bool const was_suspended = suspending_editor_updates ();
760 _move_dragging = false;
761 _zoom_trim_dragging = false;
762 _editor->_dragging_playhead = false;
763 _editor->set_follow_playhead (_old_follow_playhead, false);
765 if (was_suspended && _pending_editor_changed) {
766 set_editor (_pending_editor_x);
773 EditorSummary::on_scroll_event (GdkEventScroll* ev)
776 pair<double, double> xr;
780 switch (ev->direction) {
781 case GDK_SCROLL_UP: {
783 summary_zoom_step (-4);
788 case GDK_SCROLL_DOWN: {
790 summary_zoom_step (4);
795 case GDK_SCROLL_LEFT:
796 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
797 _editor->temporal_zoom_step (false);
798 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
800 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
803 _editor->scroll_left_half_page ();
807 case GDK_SCROLL_RIGHT:
808 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
809 _editor->temporal_zoom_step (true);
810 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
812 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
815 _editor->scroll_right_half_page ();
827 /** Set the editor to display a x range with the left at a given position
828 * and a y range with the top at a given position.
829 * x and y parameters are specified in summary coordinates.
830 * Zoom is not changed in either direction.
833 EditorSummary::set_editor (double const x)
835 if (_editor->pending_visual_change.idle_handler_id >= 0 && _editor->pending_visual_change.being_handled == true) {
837 /* As a side-effect, the Editor's visual change idle handler processes
838 pending GTK events. Hence this motion notify handler can be called
839 in the middle of a visual change idle handler, and if this happens,
840 the queue_visual_change calls below modify the variables that the
841 idle handler is working with. This causes problems. Hence this
842 check. It ensures that we won't modify the pending visual change
843 while a visual change idle handler is in progress. It's not perfect,
844 as it also means that we won't change these variables if an idle handler
845 is merely pending but not executing. But c'est la vie.
854 /** Set the editor to display a given x range and a y range with the top at a given position.
855 * The editor's x zoom is adjusted if necessary, but the y zoom is not changed.
856 * x and y parameters are specified in summary coordinates.
859 EditorSummary::set_editor (pair<double,double> const x)
861 if (_editor->pending_visual_change.idle_handler_id >= 0) {
862 /* see comment in other set_editor () */
871 /** Set the left of the x range visible in the editor.
872 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
873 * @param x new x left position in summary coordinates.
876 EditorSummary::set_editor_x (double x)
882 if (suspending_editor_updates ()) {
883 double const w = _pending_editor_x.second - _pending_editor_x.first;
884 _pending_editor_x.first = x;
885 _pending_editor_x.second = x + w;
886 _pending_editor_changed = true;
889 _editor->reset_x_origin (x / _x_scale + _start);
893 /** Set the x range visible in the editor.
894 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
895 * @param x new x range in summary coordinates.
898 EditorSummary::set_editor_x (pair<double, double> x)
905 x.second = x.first + 1;
908 if (suspending_editor_updates ()) {
909 _pending_editor_x = x;
910 _pending_editor_changed = true;
913 _editor->reset_x_origin (x.first / _x_scale + _start);
916 ((x.second - x.first) / _x_scale) /
917 _editor->sample_to_pixel (_editor->current_page_samples())
920 if (nx != _editor->get_current_zoom ()) {
921 _editor->reset_zoom (nx);
927 EditorSummary::playhead_position_changed (samplepos_t p)
929 int const o = int (_last_playhead);
930 int const n = int (playhead_sample_to_position (p));
931 if (_session && o != n) {
932 int a = max(2, min (o, n));
934 set_overlays_dirty_rect (a - 2, 0, b + 2, get_height ());
939 EditorSummary::editor_y_to_summary (double y) const
942 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
944 if ((*i)->hidden()) {
948 double const h = (*i)->effective_height ();
951 return sy + y * _track_height / h;
962 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
964 for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
965 /* Connect to the relevant signal for the route so that we know when its colour has changed */
966 (*i)->route()->presentation_info().PropertyChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
967 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> ((*i)->route ());
969 tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context ());
973 set_background_dirty ();
977 EditorSummary::route_gui_changed (PBD::PropertyChange const& what_changed)
979 if (what_changed.contains (Properties::color)) {
980 set_background_dirty ();
985 EditorSummary::playhead_sample_to_position (samplepos_t t) const
987 return (t - _start) * _x_scale;
991 EditorSummary::position_to_playhead_sample_to_position (double pos) const
993 return _start + (pos * _x_scale);