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"
22 #include "canvas/debug.h"
24 #include <gtkmm/menu.h>
25 #include <gtkmm/menuitem.h>
27 #include "ardour_ui.h"
28 #include "time_axis_view.h"
29 #include "streamview.h"
30 #include "editor_summary.h"
31 #include "gui_thread.h"
33 #include "region_view.h"
34 #include "rgb_macros.h"
36 #include "editor_routes.h"
37 #include "editor_cursors.h"
38 #include "mouse_cursors.h"
39 #include "route_time_axis.h"
40 #include "ui_config.h"
45 using namespace ARDOUR;
46 using Gtkmm2ext::Keyboard;
48 /** Construct an EditorSummary.
49 * @param e Editor to represent.
51 EditorSummary::EditorSummary (Editor* e)
52 : EditorComponent (e),
58 _move_dragging (false),
59 _view_rectangle_x (0, 0),
60 _view_rectangle_y (0, 0),
61 _zoom_trim_dragging (false),
62 _old_follow_playhead (false),
64 _background_dirty (true)
66 CairoWidget::use_nsglview ();
67 add_events (Gdk::POINTER_MOTION_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
68 set_flags (get_flags() | Gtk::CAN_FOCUS);
70 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &EditorSummary::parameter_changed));
73 EditorSummary::~EditorSummary ()
75 cairo_surface_destroy (_image);
79 EditorSummary::parameter_changed (string p)
82 if (p == "color-regions-using-track-color") {
83 set_background_dirty ();
87 /** Handle a size allocation.
88 * @param alloc GTK allocation.
91 EditorSummary::on_size_allocate (Gtk::Allocation& alloc)
93 CairoWidget::on_size_allocate (alloc);
94 set_background_dirty ();
98 /** Connect to a session.
102 EditorSummary::set_session (Session* s)
104 SessionHandlePtr::set_session (s);
108 /* Note: the EditorSummary already finds out about new regions from Editor::region_view_added
109 * (which attaches to StreamView::RegionViewAdded), and cut regions by the RegionPropertyChanged
110 * emitted when a cut region is added to the `cutlist' playlist.
114 Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
115 PresentationInfo::Change.connect (route_ctrl_id_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
116 _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), boost::bind (&EditorSummary::playhead_position_changed, this, _1), gui_context());
117 _session->StartTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
118 _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
119 _editor->selection->RegionsChanged.connect (sigc::mem_fun(*this, &EditorSummary::set_background_dirty));
122 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &EditorSummary::set_colors));
126 _leftmost = max_samplepos;
131 EditorSummary::render_background_image ()
133 cairo_surface_destroy (_image); // passing NULL is safe
134 _image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, get_width (), get_height ());
136 cairo_t* cr = cairo_create (_image);
138 /* background (really just the dividing lines between tracks */
140 cairo_set_source_rgb (cr, 0, 0, 0);
141 cairo_rectangle (cr, 0, 0, get_width(), get_height());
144 /* compute start and end points for the summary */
146 std::pair<samplepos_t, samplepos_t> ext = _editor->session_gui_extents();
147 double theoretical_start = ext.first;
148 double theoretical_end = ext.second;
150 /* the summary should encompass the full extent of everywhere we've visited since the session was opened */
151 if (_leftmost < theoretical_start)
152 theoretical_start = _leftmost;
153 if (_rightmost > theoretical_end)
154 theoretical_end = _rightmost;
157 _start = theoretical_start > 0 ? theoretical_start : 0;
158 _end = theoretical_end < max_samplepos ? theoretical_end : max_samplepos;
160 /* calculate x scale */
161 if (_end != _start) {
162 _x_scale = static_cast<double> (get_width()) / (_end - _start);
167 /* compute track height */
169 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
170 if (!(*i)->hidden()) {
178 _track_height = (double) get_height() / N;
181 /* render tracks and regions */
184 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
186 if ((*i)->hidden()) {
190 /* paint a non-bg colored strip to represent the track itself */
192 if (_track_height > 4) {
193 cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
194 cairo_set_line_width (cr, _track_height - 1);
195 cairo_move_to (cr, 0, y + _track_height / 2);
196 cairo_line_to (cr, get_width(), y + _track_height / 2);
200 StreamView* s = (*i)->view ();
203 cairo_set_line_width (cr, _track_height * 0.8);
205 s->foreach_regionview (sigc::bind (
206 sigc::mem_fun (*this, &EditorSummary::render_region),
208 y + _track_height / 2
215 /* start and end markers */
217 cairo_set_line_width (cr, 1);
218 cairo_set_source_rgb (cr, 1, 1, 0);
220 const double p = (_session->current_start_sample() - _start) * _x_scale;
221 cairo_move_to (cr, p, 0);
222 cairo_line_to (cr, p, get_height());
224 double const q = (_session->current_end_sample() - _start) * _x_scale;
225 cairo_move_to (cr, q, 0);
226 cairo_line_to (cr, q, get_height());
232 /** Render the required regions to a cairo context.
236 EditorSummary::render (Cairo::RefPtr<Cairo::Context> const& ctx, cairo_rectangle_t*)
238 cairo_t* cr = ctx->cobj();
244 /* maintain the leftmost and rightmost locations that we've ever reached */
245 samplecnt_t const leftmost = _editor->leftmost_sample ();
246 if (leftmost < _leftmost) {
247 _leftmost = leftmost;
248 _background_dirty = true;
250 samplecnt_t const rightmost = leftmost + _editor->current_page_samples();
251 if (rightmost > _rightmost) {
252 _rightmost = rightmost;
253 _background_dirty = true;
256 /* draw the background (regions, markers, etc) if they've changed */
257 if (!_image || _background_dirty) {
258 render_background_image ();
259 _background_dirty = false;
262 cairo_push_group (cr);
264 /* Fill with the background image */
266 cairo_rectangle (cr, 0, 0, get_width(), get_height());
267 cairo_set_source_surface (cr, _image, 0, 0);
270 /* Render the view rectangle. If there is an editor visual pending, don't update
271 * the view rectangle now --- wait until the expose event that we'll get after
272 * the visual change. This prevents a flicker.
275 if (_editor->pending_visual_change.idle_handler_id < 0) {
276 get_editor (&_view_rectangle_x, &_view_rectangle_y);
279 int32_t width = _view_rectangle_x.second - _view_rectangle_x.first;
281 cairo_rectangle (cr, _view_rectangle_x.first, 0, width, get_height ());
282 cairo_set_source_rgba (cr, 1, 1, 1, 0.15);
286 cairo_rectangle (cr, _view_rectangle_x.first, 0, width, get_height ());
287 cairo_set_line_width (cr, 1);
288 cairo_set_source_rgba (cr, 1, 1, 1, 0.9);
293 cairo_set_line_width (cr, 1);
295 double r,g,b,a; Gtkmm2ext::color_to_rgba(_phead_color, r,g,b,a);
296 cairo_set_source_rgb (cr, r,g,b); // playhead color
298 const double ph= playhead_sample_to_position (_editor->playhead_cursor->current_sample());
299 cairo_move_to (cr, ph, 0);
300 cairo_line_to (cr, ph, get_height());
302 cairo_pop_group_to_source (cr);
309 EditorSummary::set_colors ()
311 _phead_color = UIConfiguration::instance().color ("play head");
316 /** Render a region for the summary.
317 * @param r Region view.
318 * @param cr Cairo context.
319 * @param y y coordinate to render at.
322 EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const
324 /*NOTE: you can optimize this operation by coalescing adjacent regions into a single line stroke.
325 * In a session with a single track ~1,000 regions, this reduced render time from 14ms to 11 ms.
326 * However, you lose a lot of visual information. The current method preserves a sense of separation between regions.
327 * The current method shows the current selection (red regions), which needs to be preserved if this is optimized.
328 * I think it's not worth it for now, but we might choose to revisit this someday.
331 uint32_t const c = r->get_fill_color ();
332 cairo_set_source_rgb (cr, UINT_RGBA_R (c) / 255.0, UINT_RGBA_G (c) / 255.0, UINT_RGBA_B (c) / 255.0);
334 if (r->region()->position() > _start) {
335 cairo_move_to (cr, (r->region()->position() - _start) * _x_scale, y);
337 cairo_move_to (cr, 0, y);
340 if ((r->region()->position() + r->region()->length()) > _start) {
341 cairo_line_to (cr, ((r->region()->position() - _start + r->region()->length())) * _x_scale, y);
343 cairo_line_to (cr, 0, y);
350 EditorSummary::set_background_dirty ()
352 if (!_background_dirty) {
353 _background_dirty = true;
358 /** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
360 EditorSummary::set_overlays_dirty ()
362 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
366 /** Set the summary so that just the overlays (viewbox, playhead etc.) in a given area will be re-rendered */
368 EditorSummary::set_overlays_dirty_rect (int x, int y, int w, int h)
370 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty_rect);
371 queue_draw_area (x, y, w, h);
375 /** Handle a size request.
376 * @param req GTK requisition
379 EditorSummary::on_size_request (Gtk::Requisition *req)
381 /* The left/right buttons will determine our height */
388 EditorSummary::centre_on_click (GdkEventButton* ev)
390 pair<double, double> xr;
393 double const w = xr.second - xr.first;
394 double ex = ev->x - w / 2;
397 } else if ((ex + w) > get_width()) {
398 ex = get_width() - w;
405 EditorSummary::on_enter_notify_event (GdkEventCrossing*)
408 Keyboard::magic_widget_grab_focus ();
413 EditorSummary::on_leave_notify_event (GdkEventCrossing*)
415 /* there are no inferior/child windows, so any leave event means that
418 Keyboard::magic_widget_drop_focus ();
423 EditorSummary::on_key_press_event (GdkEventKey* key)
426 GtkAccelKey set_playhead_accel;
427 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
428 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
431 _session->request_locate (_start + (samplepos_t) x / _x_scale, _session->transport_rolling());
441 EditorSummary::on_key_release_event (GdkEventKey* key)
444 GtkAccelKey set_playhead_accel;
445 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
446 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
453 #include "gtkmm2ext/utils.h"
455 /** Handle a button press.
456 * @param ev GTK event.
459 EditorSummary::on_button_press_event (GdkEventButton* ev)
461 _old_follow_playhead = _editor->follow_playhead ();
463 if (ev->button == 3) { // right-click: show the reset menu action
464 using namespace Gtk::Menu_Helpers;
465 Gtk::Menu* m = ARDOUR_UI::instance()->shared_popup_menu ();
466 MenuList& items = m->items ();
467 items.push_back(MenuElem(_("Reset Summary to Extents"),
468 sigc::mem_fun(*this, &EditorSummary::reset_to_extents)));
469 m->popup (ev->button, ev->time);
473 if (ev->button != 1) {
477 pair<double, double> xr;
480 _start_editor_x = xr;
481 _start_mouse_x = ev->x;
482 _start_mouse_y = ev->y;
483 _start_position = get_position (ev->x, ev->y);
485 if (_start_position != INSIDE && _start_position != TO_LEFT_OR_RIGHT) {
487 /* start a zoom_trim drag */
489 _zoom_trim_position = get_position (ev->x, ev->y);
490 _zoom_trim_dragging = true;
491 _editor->_dragging_playhead = true;
492 _editor->set_follow_playhead (false);
494 if (suspending_editor_updates ()) {
495 get_editor (&_pending_editor_x, &_pending_editor_y);
496 _pending_editor_changed = false;
499 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
501 /* secondary-modifier-click: locate playhead */
503 _session->request_locate (ev->x / _x_scale + _start);
506 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
508 centre_on_click (ev);
512 /* start a move+zoom drag */
513 get_editor (&_pending_editor_x, &_pending_editor_y);
514 _pending_editor_changed = false;
515 _editor->_dragging_playhead = true;
516 _editor->set_follow_playhead (false);
518 _move_dragging = true;
526 get_window()->set_cursor (*_editor->_cursors->expand_left_right);
533 /** @return true if we are currently suspending updates to the editor's viewport,
534 * which we do if configured to do so, and if in a drag of some kind.
537 EditorSummary::suspending_editor_updates () const
539 return (!UIConfiguration::instance().get_update_editor_during_summary_drag () && (_zoom_trim_dragging || _move_dragging));
542 /** Fill in x and y with the editor's current viewable area in summary coordinates */
544 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
547 if (suspending_editor_updates ()) {
549 /* We are dragging, and configured not to update the editor window during drags,
550 * so just return where the editor will be when the drag finishes.
553 *x = _pending_editor_x;
555 *y = _pending_editor_y;
560 /* Otherwise query the editor for its actual position */
562 x->first = (_editor->leftmost_sample () - _start) * _x_scale;
563 x->second = x->first + _editor->current_page_samples() * _x_scale;
566 y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
567 y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y);
571 /** Get an expression of the position of a point with respect to the view rectangle */
572 EditorSummary::Position
573 EditorSummary::get_position (double x, double y) const
575 /* how close the mouse has to be to the edge of the view rectangle to be considered `on it',
578 int x_edge_size = (_view_rectangle_x.second - _view_rectangle_x.first) / 4;
579 x_edge_size = min (x_edge_size, 8);
580 x_edge_size = max (x_edge_size, 1);
582 bool const near_left = (std::abs (x - _view_rectangle_x.first) < x_edge_size);
583 bool const near_right = (std::abs (x - _view_rectangle_x.second) < x_edge_size);
584 bool const within_x = _view_rectangle_x.first < x && x < _view_rectangle_x.second;
588 } else if (near_right) {
590 } else if (within_x) {
593 return TO_LEFT_OR_RIGHT;
598 EditorSummary::reset_to_extents()
600 /* reset as if the user never went anywhere outside the extents */
601 _leftmost = max_samplepos;
604 _editor->temporal_zoom_extents ();
605 set_background_dirty ();
610 EditorSummary::set_cursor (Position p)
614 get_window()->set_cursor (*_editor->_cursors->resize_left);
617 get_window()->set_cursor (*_editor->_cursors->resize_right);
620 get_window()->set_cursor (*_editor->_cursors->move);
622 case TO_LEFT_OR_RIGHT:
623 get_window()->set_cursor (*_editor->_cursors->move);
627 get_window()->set_cursor ();
633 EditorSummary::summary_zoom_step (int steps /* positive steps to zoom "out" , negative steps to zoom "in" */ )
635 pair<double, double> xn;
642 /* for now, disallow really close zooming-in from the scroomer. (Currently it
643 * causes the start-offset to 'walk' because of integer limitations.
644 * To fix this, probably need to maintain float throught the get/set_editor() path.)
647 if ((xn.second - xn.first) < 2)
651 set_overlays_dirty ();
657 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
659 if (_move_dragging) {
661 /* To avoid accidental zooming, the mouse must move exactly vertical, not diagonal, to trigger a zoom step
662 * we use screen coordinates for this, not canvas-based grab_x */
664 double dx = mx - _last_mx;
666 double dy = my - _last_my;
668 /* do zooming in windowed "steps" so it feels more reversible ? */
669 const int stepsize = 2;
670 int y_delta = _start_mouse_y - my;
671 y_delta = y_delta / stepsize;
674 const float zscale = 3;
675 if ((dx == 0) && (_last_dx == 0) && (y_delta != _last_y_delta)) {
677 summary_zoom_step (dy * zscale);
679 /* after the zoom we must re-calculate x-pos grabs */
680 pair<double, double> xr;
682 _start_editor_x = xr;
683 _start_mouse_x = ev->x;
685 _last_y_delta = y_delta;
688 /* always track horizontal movement, if any */
691 double x = _start_editor_x.first;
692 x += ev->x - _start_mouse_x;
698 /* zoom-behavior-tweaks: protect the right edge from expanding beyond the end */
699 pair<double, double> xr;
701 double w = xr.second - xr.first;
702 if (x + w < get_width()) {
712 } else if (_zoom_trim_dragging) {
714 pair<double, double> xr = _start_editor_x;
716 double const dx = ev->x - _start_mouse_x;
718 if (_zoom_trim_position == LEFT) {
720 } else if (_zoom_trim_position == RIGHT) {
722 /* zoom-behavior-tweaks: protect the right edge from expanding beyond the edge */
723 if ((xr.second + dx) < get_width()) {
729 xr.first = -1; /* do not change */
732 set_overlays_dirty ();
733 set_cursor (_zoom_trim_position);
737 set_cursor (get_position (ev->x, ev->y));
744 EditorSummary::on_button_release_event (GdkEventButton*)
746 bool const was_suspended = suspending_editor_updates ();
748 _move_dragging = false;
749 _zoom_trim_dragging = false;
750 _editor->_dragging_playhead = false;
751 _editor->set_follow_playhead (_old_follow_playhead, false);
753 if (was_suspended && _pending_editor_changed) {
754 set_editor (_pending_editor_x);
761 EditorSummary::on_scroll_event (GdkEventScroll* ev)
764 pair<double, double> xr;
768 switch (ev->direction) {
769 case GDK_SCROLL_UP: {
771 summary_zoom_step (-4);
776 case GDK_SCROLL_DOWN: {
778 summary_zoom_step (4);
783 case GDK_SCROLL_LEFT:
784 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
785 _editor->temporal_zoom_step (false);
786 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
788 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
791 _editor->scroll_left_half_page ();
795 case GDK_SCROLL_RIGHT:
796 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
797 _editor->temporal_zoom_step (true);
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_right_half_page ();
815 /** Set the editor to display a x range with the left at a given position
816 * and a y range with the top at a given position.
817 * x and y parameters are specified in summary coordinates.
818 * Zoom is not changed in either direction.
821 EditorSummary::set_editor (double const x)
823 if (_editor->pending_visual_change.idle_handler_id >= 0 && _editor->pending_visual_change.being_handled == true) {
825 /* As a side-effect, the Editor's visual change idle handler processes
826 pending GTK events. Hence this motion notify handler can be called
827 in the middle of a visual change idle handler, and if this happens,
828 the queue_visual_change calls below modify the variables that the
829 idle handler is working with. This causes problems. Hence this
830 check. It ensures that we won't modify the pending visual change
831 while a visual change idle handler is in progress. It's not perfect,
832 as it also means that we won't change these variables if an idle handler
833 is merely pending but not executing. But c'est la vie.
842 /** Set the editor to display a given x range and a y range with the top at a given position.
843 * The editor's x zoom is adjusted if necessary, but the y zoom is not changed.
844 * x and y parameters are specified in summary coordinates.
847 EditorSummary::set_editor (pair<double,double> const x)
849 if (_editor->pending_visual_change.idle_handler_id >= 0) {
850 /* see comment in other set_editor () */
859 /** Set the left of the x range visible in the editor.
860 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
861 * @param x new x left position in summary coordinates.
864 EditorSummary::set_editor_x (double x)
870 if (suspending_editor_updates ()) {
871 double const w = _pending_editor_x.second - _pending_editor_x.first;
872 _pending_editor_x.first = x;
873 _pending_editor_x.second = x + w;
874 _pending_editor_changed = true;
877 _editor->reset_x_origin (x / _x_scale + _start);
881 /** Set the x range visible in the editor.
882 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
883 * @param x new x range in summary coordinates.
886 EditorSummary::set_editor_x (pair<double, double> x)
893 x.second = x.first + 1;
896 if (suspending_editor_updates ()) {
897 _pending_editor_x = x;
898 _pending_editor_changed = true;
901 _editor->reset_x_origin (x.first / _x_scale + _start);
904 ((x.second - x.first) / _x_scale) /
905 _editor->sample_to_pixel (_editor->current_page_samples())
908 if (nx != _editor->get_current_zoom ()) {
909 _editor->reset_zoom (nx);
915 EditorSummary::playhead_position_changed (samplepos_t p)
917 int const o = int (_last_playhead);
918 int const n = int (playhead_sample_to_position (p));
919 if (_session && o != n) {
920 int a = max(2, min (o, n));
922 set_overlays_dirty_rect (a - 2, 0, b + 2, get_height ());
927 EditorSummary::editor_y_to_summary (double y) const
930 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
932 if ((*i)->hidden()) {
936 double const h = (*i)->effective_height ();
939 return sy + y * _track_height / h;
950 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
952 for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
953 /* Connect to the relevant signal for the route so that we know when its colour has changed */
954 (*i)->route()->presentation_info().PropertyChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
955 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> ((*i)->route ());
957 tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context ());
961 set_background_dirty ();
965 EditorSummary::route_gui_changed (PBD::PropertyChange const& what_changed)
967 if (what_changed.contains (Properties::color)) {
968 set_background_dirty ();
973 EditorSummary::playhead_sample_to_position (samplepos_t t) const
975 return (t - _start) * _x_scale;
979 EditorSummary::position_to_playhead_sample_to_position (double pos) const
981 return _start + (pos * _x_scale);