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 "context_menu_helper.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;
428 /* XXXX this is really ugly and should be using our own action maps and bindings */
430 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
431 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
434 _session->request_locate (_start + (samplepos_t) x / _x_scale, _session->transport_rolling());
444 EditorSummary::on_key_release_event (GdkEventKey* key)
447 GtkAccelKey set_playhead_accel;
449 /* XXXX this is really ugly and should be using our own action maps and bindings */
451 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
452 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
459 #include "gtkmm2ext/utils.h"
461 /** Handle a button press.
462 * @param ev GTK event.
465 EditorSummary::on_button_press_event (GdkEventButton* ev)
467 _old_follow_playhead = _editor->follow_playhead ();
469 if (ev->button == 3) { // right-click: show the reset menu action
470 using namespace Gtk::Menu_Helpers;
471 Gtk::Menu* m = ARDOUR_UI_UTILS::shared_popup_menu ();
472 MenuList& items = m->items ();
473 items.push_back(MenuElem(_("Reset Summary to Extents"),
474 sigc::mem_fun(*this, &EditorSummary::reset_to_extents)));
475 m->popup (ev->button, ev->time);
479 if (ev->button != 1) {
483 pair<double, double> xr;
486 _start_editor_x = xr;
487 _start_mouse_x = ev->x;
488 _start_mouse_y = ev->y;
489 _start_position = get_position (ev->x, ev->y);
491 if (_start_position != INSIDE && _start_position != TO_LEFT_OR_RIGHT) {
493 /* start a zoom_trim drag */
495 _zoom_trim_position = get_position (ev->x, ev->y);
496 _zoom_trim_dragging = true;
497 _editor->_dragging_playhead = true;
498 _editor->set_follow_playhead (false);
500 if (suspending_editor_updates ()) {
501 get_editor (&_pending_editor_x, &_pending_editor_y);
502 _pending_editor_changed = false;
505 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
507 /* secondary-modifier-click: locate playhead */
509 _session->request_locate (ev->x / _x_scale + _start);
512 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
514 centre_on_click (ev);
518 /* start a move+zoom drag */
519 get_editor (&_pending_editor_x, &_pending_editor_y);
520 _pending_editor_changed = false;
521 _editor->_dragging_playhead = true;
522 _editor->set_follow_playhead (false);
524 _move_dragging = true;
532 get_window()->set_cursor (*_editor->_cursors->expand_left_right);
539 /** @return true if we are currently suspending updates to the editor's viewport,
540 * which we do if configured to do so, and if in a drag of some kind.
543 EditorSummary::suspending_editor_updates () const
545 return (!UIConfiguration::instance().get_update_editor_during_summary_drag () && (_zoom_trim_dragging || _move_dragging));
548 /** Fill in x and y with the editor's current viewable area in summary coordinates */
550 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
553 if (suspending_editor_updates ()) {
555 /* We are dragging, and configured not to update the editor window during drags,
556 * so just return where the editor will be when the drag finishes.
559 *x = _pending_editor_x;
561 *y = _pending_editor_y;
566 /* Otherwise query the editor for its actual position */
568 x->first = (_editor->leftmost_sample () - _start) * _x_scale;
569 x->second = x->first + _editor->current_page_samples() * _x_scale;
572 y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
573 y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y);
577 /** Get an expression of the position of a point with respect to the view rectangle */
578 EditorSummary::Position
579 EditorSummary::get_position (double x, double y) const
581 /* how close the mouse has to be to the edge of the view rectangle to be considered `on it',
584 int x_edge_size = (_view_rectangle_x.second - _view_rectangle_x.first) / 4;
585 x_edge_size = min (x_edge_size, 8);
586 x_edge_size = max (x_edge_size, 1);
588 bool const near_left = (std::abs (x - _view_rectangle_x.first) < x_edge_size);
589 bool const near_right = (std::abs (x - _view_rectangle_x.second) < x_edge_size);
590 bool const within_x = _view_rectangle_x.first < x && x < _view_rectangle_x.second;
594 } else if (near_right) {
596 } else if (within_x) {
599 return TO_LEFT_OR_RIGHT;
604 EditorSummary::reset_to_extents()
606 /* reset as if the user never went anywhere outside the extents */
607 _leftmost = max_samplepos;
610 _editor->temporal_zoom_extents ();
611 set_background_dirty ();
616 EditorSummary::set_cursor (Position p)
620 get_window()->set_cursor (*_editor->_cursors->resize_left);
623 get_window()->set_cursor (*_editor->_cursors->resize_right);
626 get_window()->set_cursor (*_editor->_cursors->move);
628 case TO_LEFT_OR_RIGHT:
629 get_window()->set_cursor (*_editor->_cursors->move);
633 get_window()->set_cursor ();
639 EditorSummary::summary_zoom_step (int steps /* positive steps to zoom "out" , negative steps to zoom "in" */ )
641 pair<double, double> xn;
648 /* for now, disallow really close zooming-in from the scroomer. (Currently it
649 * causes the start-offset to 'walk' because of integer limitations.
650 * To fix this, probably need to maintain float throught the get/set_editor() path.)
653 if ((xn.second - xn.first) < 2)
657 set_overlays_dirty ();
663 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
665 if (_move_dragging) {
667 /* To avoid accidental zooming, the mouse must move exactly vertical, not diagonal, to trigger a zoom step
668 * we use screen coordinates for this, not canvas-based grab_x */
670 double dx = mx - _last_mx;
672 double dy = my - _last_my;
674 /* do zooming in windowed "steps" so it feels more reversible ? */
675 const int stepsize = 2;
676 int y_delta = _start_mouse_y - my;
677 y_delta = y_delta / stepsize;
680 const float zscale = 3;
681 if ((dx == 0) && (_last_dx == 0) && (y_delta != _last_y_delta)) {
683 summary_zoom_step (dy * zscale);
685 /* after the zoom we must re-calculate x-pos grabs */
686 pair<double, double> xr;
688 _start_editor_x = xr;
689 _start_mouse_x = ev->x;
691 _last_y_delta = y_delta;
694 /* always track horizontal movement, if any */
697 double x = _start_editor_x.first;
698 x += ev->x - _start_mouse_x;
704 /* zoom-behavior-tweaks: protect the right edge from expanding beyond the end */
705 pair<double, double> xr;
707 double w = xr.second - xr.first;
708 if (x + w < get_width()) {
718 } else if (_zoom_trim_dragging) {
720 pair<double, double> xr = _start_editor_x;
722 double const dx = ev->x - _start_mouse_x;
724 if (_zoom_trim_position == LEFT) {
726 } else if (_zoom_trim_position == RIGHT) {
728 /* zoom-behavior-tweaks: protect the right edge from expanding beyond the edge */
729 if ((xr.second + dx) < get_width()) {
735 xr.first = -1; /* do not change */
738 set_overlays_dirty ();
739 set_cursor (_zoom_trim_position);
743 set_cursor (get_position (ev->x, ev->y));
750 EditorSummary::on_button_release_event (GdkEventButton*)
752 bool const was_suspended = suspending_editor_updates ();
754 _move_dragging = false;
755 _zoom_trim_dragging = false;
756 _editor->_dragging_playhead = false;
757 _editor->set_follow_playhead (_old_follow_playhead, false);
759 if (was_suspended && _pending_editor_changed) {
760 set_editor (_pending_editor_x);
767 EditorSummary::on_scroll_event (GdkEventScroll* ev)
770 pair<double, double> xr;
774 switch (ev->direction) {
775 case GDK_SCROLL_UP: {
777 summary_zoom_step (-4);
782 case GDK_SCROLL_DOWN: {
784 summary_zoom_step (4);
789 case GDK_SCROLL_LEFT:
790 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
791 _editor->temporal_zoom_step (false);
792 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
794 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
797 _editor->scroll_left_half_page ();
801 case GDK_SCROLL_RIGHT:
802 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
803 _editor->temporal_zoom_step (true);
804 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
806 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
809 _editor->scroll_right_half_page ();
821 /** Set the editor to display a x range with the left at a given position
822 * and a y range with the top at a given position.
823 * x and y parameters are specified in summary coordinates.
824 * Zoom is not changed in either direction.
827 EditorSummary::set_editor (double const x)
829 if (_editor->pending_visual_change.idle_handler_id >= 0 && _editor->pending_visual_change.being_handled == true) {
831 /* As a side-effect, the Editor's visual change idle handler processes
832 pending GTK events. Hence this motion notify handler can be called
833 in the middle of a visual change idle handler, and if this happens,
834 the queue_visual_change calls below modify the variables that the
835 idle handler is working with. This causes problems. Hence this
836 check. It ensures that we won't modify the pending visual change
837 while a visual change idle handler is in progress. It's not perfect,
838 as it also means that we won't change these variables if an idle handler
839 is merely pending but not executing. But c'est la vie.
848 /** Set the editor to display a given x range and a y range with the top at a given position.
849 * The editor's x zoom is adjusted if necessary, but the y zoom is not changed.
850 * x and y parameters are specified in summary coordinates.
853 EditorSummary::set_editor (pair<double,double> const x)
855 if (_editor->pending_visual_change.idle_handler_id >= 0) {
856 /* see comment in other set_editor () */
865 /** Set the left of the x range visible in the editor.
866 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
867 * @param x new x left position in summary coordinates.
870 EditorSummary::set_editor_x (double x)
876 if (suspending_editor_updates ()) {
877 double const w = _pending_editor_x.second - _pending_editor_x.first;
878 _pending_editor_x.first = x;
879 _pending_editor_x.second = x + w;
880 _pending_editor_changed = true;
883 _editor->reset_x_origin (x / _x_scale + _start);
887 /** Set the x range visible in the editor.
888 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
889 * @param x new x range in summary coordinates.
892 EditorSummary::set_editor_x (pair<double, double> x)
899 x.second = x.first + 1;
902 if (suspending_editor_updates ()) {
903 _pending_editor_x = x;
904 _pending_editor_changed = true;
907 _editor->reset_x_origin (x.first / _x_scale + _start);
910 ((x.second - x.first) / _x_scale) /
911 _editor->sample_to_pixel (_editor->current_page_samples())
914 if (nx != _editor->get_current_zoom ()) {
915 _editor->reset_zoom (nx);
921 EditorSummary::playhead_position_changed (samplepos_t p)
923 int const o = int (_last_playhead);
924 int const n = int (playhead_sample_to_position (p));
925 if (_session && o != n) {
926 int a = max(2, min (o, n));
928 set_overlays_dirty_rect (a - 2, 0, b + 2, get_height ());
933 EditorSummary::editor_y_to_summary (double y) const
936 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
938 if ((*i)->hidden()) {
942 double const h = (*i)->effective_height ();
945 return sy + y * _track_height / h;
956 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
958 for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
959 /* Connect to the relevant signal for the route so that we know when its colour has changed */
960 (*i)->route()->presentation_info().PropertyChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
961 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> ((*i)->route ());
963 tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context ());
967 set_background_dirty ();
971 EditorSummary::route_gui_changed (PBD::PropertyChange const& what_changed)
973 if (what_changed.contains (Properties::color)) {
974 set_background_dirty ();
979 EditorSummary::playhead_sample_to_position (samplepos_t t) const
981 return (t - _start) * _x_scale;
985 EditorSummary::position_to_playhead_sample_to_position (double pos) const
987 return _start + (pos * _x_scale);