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 "time_axis_view.h"
25 #include "streamview.h"
26 #include "editor_summary.h"
27 #include "gui_thread.h"
29 #include "region_view.h"
30 #include "rgb_macros.h"
32 #include "editor_routes.h"
33 #include "editor_cursors.h"
34 #include "mouse_cursors.h"
35 #include "route_time_axis.h"
38 using namespace ARDOUR;
39 using Gtkmm2ext::Keyboard;
41 /** Construct an EditorSummary.
42 * @param e Editor to represent.
44 EditorSummary::EditorSummary (Editor* e)
45 : EditorComponent (e),
48 _overhang_fraction (0.1),
52 _move_dragging (false),
54 _view_rectangle_x (0, 0),
55 _view_rectangle_y (0, 0),
56 _zoom_dragging (false),
57 _old_follow_playhead (false),
58 _background_dirty (true)
60 add_events (Gdk::POINTER_MOTION_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
61 set_flags (get_flags() | Gtk::CAN_FOCUS);
64 /** Handle a size allocation.
65 * @param alloc GTK allocation.
68 EditorSummary::on_size_allocate (Gtk::Allocation& alloc)
70 Gtk::EventBox::on_size_allocate (alloc);
71 _background_dirty = true;
76 /** Connect to a session.
80 EditorSummary::set_session (Session* s)
82 SessionHandlePtr::set_session (s);
86 /* Note: the EditorSummary already finds out about new regions from Editor::region_view_added
87 * (which attaches to StreamView::RegionViewAdded), and cut regions by the RegionPropertyChanged
88 * emitted when a cut region is added to the `cutlist' playlist.
92 Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
93 Route::RemoteControlIDChange.connect (route_ctrl_id_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
94 _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), boost::bind (&EditorSummary::playhead_position_changed, this, _1), gui_context());
95 _session->StartTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context());
96 _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context());
97 _editor->selection->RegionsChanged.connect (sigc::mem_fun(*this, &EditorSummary::set_background_dirty));
102 EditorSummary::render_background_image ()
104 _image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, get_width (), get_height ());
105 cairo_t* cr = cairo_create (_image);
107 /* background (really just the dividing lines between tracks */
109 cairo_set_source_rgb (cr, 0, 0, 0);
110 cairo_rectangle (cr, 0, 0, get_width(), get_height());
113 /* compute start and end points for the summary */
115 framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
116 double const theoretical_start = _session->current_start_frame() - session_length * _overhang_fraction;
117 _start = theoretical_start > 0 ? theoretical_start : 0;
118 _end = _session->current_end_frame() + session_length * _overhang_fraction;
120 /* compute track height */
122 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
123 if (!(*i)->hidden()) {
131 _track_height = (double) get_height() / N;
134 /* calculate x scale */
135 if (_end != _start) {
136 _x_scale = static_cast<double> (get_width()) / (_end - _start);
141 /* render tracks and regions */
144 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
146 if ((*i)->hidden()) {
150 /* paint a non-bg colored strip to represent the track itself */
152 cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
153 cairo_set_line_width (cr, _track_height - 1);
154 cairo_move_to (cr, 0, y + _track_height / 2);
155 cairo_line_to (cr, get_width(), y + _track_height / 2);
158 StreamView* s = (*i)->view ();
161 cairo_set_line_width (cr, _track_height * 0.6);
163 s->foreach_regionview (sigc::bind (
164 sigc::mem_fun (*this, &EditorSummary::render_region),
166 y + _track_height / 2
175 /** Render the required regions to a cairo context.
179 EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
186 if (!_image || _background_dirty) {
187 render_background_image ();
188 _background_dirty = false;
191 cairo_push_group (cr);
193 cairo_rectangle (cr, 0, 0, get_width(), get_height());
194 cairo_set_source_surface (cr, _image, 0, 0);
197 /* start and end markers */
199 cairo_set_line_width (cr, 1);
200 cairo_set_source_rgb (cr, 1, 1, 0);
202 const double p = (_session->current_start_frame() - _start) * _x_scale;
203 cairo_move_to (cr, p, 0);
204 cairo_line_to (cr, p, get_height());
206 double const q = (_session->current_end_frame() - _start) * _x_scale;
207 cairo_move_to (cr, q, 0);
208 cairo_line_to (cr, q, get_height());
211 /* Render the view rectangle. If there is an editor visual pending, don't update
212 the view rectangle now --- wait until the expose event that we'll get after
213 the visual change. This prevents a flicker.
216 if (_editor->pending_visual_change.idle_handler_id < 0) {
217 get_editor (&_view_rectangle_x, &_view_rectangle_y);
220 int32_t width = _view_rectangle_x.second - _view_rectangle_x.first;
221 int32_t height = _view_rectangle_y.second - _view_rectangle_y.first;
222 cairo_rectangle (cr, _view_rectangle_x.first, _view_rectangle_y.first, width, height);
223 cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
224 cairo_fill_preserve (cr);
225 cairo_set_line_width (cr, 1);
226 cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
231 cairo_set_line_width (cr, 1);
232 /* XXX: colour should be set from configuration file */
233 cairo_set_source_rgba (cr, 1, 0, 0, 1);
235 const double ph= playhead_frame_to_position (_editor->playhead_cursor->current_frame());
236 cairo_move_to (cr, ph, 0);
237 cairo_line_to (cr, ph, get_height());
239 cairo_pop_group_to_source (cr);
245 /** Render a region for the summary.
246 * @param r Region view.
247 * @param cr Cairo context.
248 * @param y y coordinate to render at.
251 EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const
253 uint32_t const c = r->get_fill_color ();
254 cairo_set_source_rgb (cr, UINT_RGBA_R (c) / 255.0, UINT_RGBA_G (c) / 255.0, UINT_RGBA_B (c) / 255.0);
256 if (r->region()->position() > _start) {
257 cairo_move_to (cr, (r->region()->position() - _start) * _x_scale, y);
259 cairo_move_to (cr, 0, y);
262 if ((r->region()->position() + r->region()->length()) > _start) {
263 cairo_line_to (cr, ((r->region()->position() - _start + r->region()->length())) * _x_scale, y);
265 cairo_line_to (cr, 0, y);
272 EditorSummary::set_background_dirty ()
274 _background_dirty = true;
278 /** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
280 EditorSummary::set_overlays_dirty ()
282 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
286 /** Set the summary so that just the overlays (viewbox, playhead etc.) in a given area will be re-rendered */
288 EditorSummary::set_overlays_dirty (int x, int y, int w, int h)
290 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
291 queue_draw_area (x, y, w, h);
295 /** Handle a size request.
296 * @param req GTK requisition
299 EditorSummary::on_size_request (Gtk::Requisition *req)
301 /* Use a dummy, small width and the actual height that we want */
308 EditorSummary::centre_on_click (GdkEventButton* ev)
310 pair<double, double> xr;
311 pair<double, double> yr;
312 get_editor (&xr, &yr);
314 double const w = xr.second - xr.first;
315 double ex = ev->x - w / 2;
318 } else if ((ex + w) > get_width()) {
319 ex = get_width() - w;
322 double const h = yr.second - yr.first;
323 double ey = ev->y - h / 2;
326 } else if ((ey + h) > get_height()) {
327 ey = get_height() - h;
334 EditorSummary::on_enter_notify_event (GdkEventCrossing*)
337 Keyboard::magic_widget_grab_focus ();
342 EditorSummary::on_leave_notify_event (GdkEventCrossing*)
344 /* there are no inferior/child windows, so any leave event means that
347 Keyboard::magic_widget_drop_focus ();
352 EditorSummary::on_key_press_event (GdkEventKey* key)
355 GtkAccelKey set_playhead_accel;
356 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
357 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
360 _session->request_locate (_start + (framepos_t) x / _x_scale, _session->transport_rolling());
370 EditorSummary::on_key_release_event (GdkEventKey* key)
373 GtkAccelKey set_playhead_accel;
374 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
375 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
382 /** Handle a button press.
383 * @param ev GTK event.
386 EditorSummary::on_button_press_event (GdkEventButton* ev)
388 _old_follow_playhead = _editor->follow_playhead ();
390 if (ev->button == 1) {
392 pair<double, double> xr;
393 pair<double, double> yr;
394 get_editor (&xr, &yr);
396 _start_editor_x = xr;
397 _start_editor_y = yr;
398 _start_mouse_x = ev->x;
399 _start_mouse_y = ev->y;
400 _start_position = get_position (ev->x, ev->y);
402 if (_start_position != INSIDE && _start_position != BELOW_OR_ABOVE &&
403 _start_position != TO_LEFT_OR_RIGHT && _start_position != OTHERWISE_OUTSIDE
406 /* start a zoom drag */
408 _zoom_position = get_position (ev->x, ev->y);
409 _zoom_dragging = true;
410 _editor->_dragging_playhead = true;
411 _editor->set_follow_playhead (false);
413 if (suspending_editor_updates ()) {
414 get_editor (&_pending_editor_x, &_pending_editor_y);
415 _pending_editor_changed = false;
418 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
420 /* secondary-modifier-click: locate playhead */
422 _session->request_locate (ev->x / _x_scale + _start);
425 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
427 centre_on_click (ev);
431 /* start a move drag */
433 /* get the editor's state in case we are suspending updates */
434 get_editor (&_pending_editor_x, &_pending_editor_y);
435 _pending_editor_changed = false;
437 _move_dragging = true;
439 _editor->_dragging_playhead = true;
440 _editor->set_follow_playhead (false);
442 ArdourCanvas::checkpoint ("sum", "------------------ summary move drag starts.\n");
449 /** @return true if we are currently suspending updates to the editor's viewport,
450 * which we do if configured to do so, and if in a drag of some kind.
453 EditorSummary::suspending_editor_updates () const
455 return (!Config->get_update_editor_during_summary_drag () && (_zoom_dragging || _move_dragging));
458 /** Fill in x and y with the editor's current viewable area in summary coordinates */
460 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
465 if (suspending_editor_updates ()) {
467 /* We are dragging, and configured not to update the editor window during drags,
468 so just return where the editor will be when the drag finishes.
471 *x = _pending_editor_x;
472 *y = _pending_editor_y;
476 /* Otherwise query the editor for its actual position */
478 x->first = (_editor->leftmost_sample () - _start) * _x_scale;
479 x->second = x->first + _editor->current_page_samples() * _x_scale;
481 y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
482 y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->visible_canvas_height());
486 /** Get an expression of the position of a point with respect to the view rectangle */
487 EditorSummary::Position
488 EditorSummary::get_position (double x, double y) const
490 /* how close the mouse has to be to the edge of the view rectangle to be considered `on it',
493 int x_edge_size = (_view_rectangle_x.second - _view_rectangle_x.first) / 4;
494 x_edge_size = min (x_edge_size, 8);
495 x_edge_size = max (x_edge_size, 1);
497 int y_edge_size = (_view_rectangle_y.second - _view_rectangle_y.first) / 4;
498 y_edge_size = min (y_edge_size, 8);
499 y_edge_size = max (y_edge_size, 1);
501 bool const near_left = (std::abs (x - _view_rectangle_x.first) < x_edge_size);
502 bool const near_right = (std::abs (x - _view_rectangle_x.second) < x_edge_size);
503 bool const near_top = (std::abs (y - _view_rectangle_y.first) < y_edge_size);
504 bool const near_bottom = (std::abs (y - _view_rectangle_y.second) < y_edge_size);
505 bool const within_x = _view_rectangle_x.first < x && x < _view_rectangle_x.second;
506 bool const within_y = _view_rectangle_y.first < y && y < _view_rectangle_y.second;
508 if (near_left && near_top) {
510 } else if (near_left && near_bottom) {
512 } else if (near_right && near_top) {
514 } else if (near_right && near_bottom) {
516 } else if (near_left && within_y) {
518 } else if (near_right && within_y) {
520 } else if (near_top && within_x) {
522 } else if (near_bottom && within_x) {
524 } else if (within_x && within_y) {
526 } else if (within_x) {
527 return BELOW_OR_ABOVE;
528 } else if (within_y) {
529 return TO_LEFT_OR_RIGHT;
531 return OTHERWISE_OUTSIDE;
536 EditorSummary::set_cursor (Position p)
540 get_window()->set_cursor (*_editor->_cursors->resize_left);
543 get_window()->set_cursor (*_editor->_cursors->resize_top_left);
546 get_window()->set_cursor (*_editor->_cursors->resize_top);
549 get_window()->set_cursor (*_editor->_cursors->resize_top_right);
552 get_window()->set_cursor (*_editor->_cursors->resize_right);
555 get_window()->set_cursor (*_editor->_cursors->resize_bottom_right);
558 get_window()->set_cursor (*_editor->_cursors->resize_bottom);
561 get_window()->set_cursor (*_editor->_cursors->resize_bottom_left);
564 get_window()->set_cursor (*_editor->_cursors->move);
566 case TO_LEFT_OR_RIGHT:
567 get_window()->set_cursor (*_editor->_cursors->expand_left_right);
570 get_window()->set_cursor (*_editor->_cursors->expand_up_down);
573 get_window()->set_cursor ();
579 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
581 pair<double, double> xr = _start_editor_x;
582 pair<double, double> yr = _start_editor_y;
583 double x = _start_editor_x.first;
584 double y = _start_editor_y.first;
586 if (_move_dragging) {
590 /* don't alter x if we clicked outside and above or below the viewbox */
591 if (_start_position == INSIDE || _start_position == TO_LEFT_OR_RIGHT || _start_position == OTHERWISE_OUTSIDE) {
592 x += ev->x - _start_mouse_x;
595 /* don't alter y if we clicked outside and to the left or right of the viewbox */
596 if (_start_position == INSIDE || _start_position == BELOW_OR_ABOVE) {
597 y += ev->y - _start_mouse_y;
609 // set_cursor (_start_position);
611 } else if (_zoom_dragging) {
613 double const dx = ev->x - _start_mouse_x;
614 double const dy = ev->y - _start_mouse_y;
616 if (_zoom_position == LEFT || _zoom_position == LEFT_TOP || _zoom_position == LEFT_BOTTOM) {
618 } else if (_zoom_position == RIGHT || _zoom_position == RIGHT_TOP || _zoom_position == RIGHT_BOTTOM) {
622 if (_zoom_position == TOP || _zoom_position == LEFT_TOP || _zoom_position == RIGHT_TOP) {
624 } else if (_zoom_position == BOTTOM || _zoom_position == LEFT_BOTTOM || _zoom_position == RIGHT_BOTTOM) {
628 set_overlays_dirty ();
629 set_cursor (_zoom_position);
634 set_cursor (get_position (ev->x, ev->y));
642 EditorSummary::on_button_release_event (GdkEventButton*)
644 bool const was_suspended = suspending_editor_updates ();
646 _move_dragging = false;
647 _zoom_dragging = false;
648 _editor->_dragging_playhead = false;
649 _editor->set_follow_playhead (_old_follow_playhead, false);
651 if (was_suspended && _pending_editor_changed) {
652 set_editor (_pending_editor_x, _pending_editor_y);
659 EditorSummary::on_scroll_event (GdkEventScroll* ev)
663 pair<double, double> xr;
664 pair<double, double> yr;
665 get_editor (&xr, &yr);
669 switch (ev->direction) {
671 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
673 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
674 _editor->temporal_zoom_step (false);
675 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
684 case GDK_SCROLL_DOWN:
685 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
687 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
688 _editor->temporal_zoom_step (true);
689 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
698 case GDK_SCROLL_LEFT:
699 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
701 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
707 case GDK_SCROLL_RIGHT:
708 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
710 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
724 /** Set the editor to display a x range with the left at a given position
725 * and a y range with the top at a given position.
726 * x and y parameters are specified in summary coordinates.
727 * Zoom is not changed in either direction.
730 EditorSummary::set_editor (double const x, double const y)
732 if (_editor->pending_visual_change.idle_handler_id >= 0 && _editor->pending_visual_change.being_handled == true) {
734 /* As a side-effect, the Editor's visual change idle handler processes
735 pending GTK events. Hence this motion notify handler can be called
736 in the middle of a visual change idle handler, and if this happens,
737 the queue_visual_change calls below modify the variables that the
738 idle handler is working with. This causes problems. Hence this
739 check. It ensures that we won't modify the pending visual change
740 while a visual change idle handler is in progress. It's not perfect,
741 as it also means that we won't change these variables if an idle handler
742 is merely pending but not executing. But c'est la vie.
752 /** Set the editor to display a given x range and a y range with the top at a given position.
753 * The editor's x zoom is adjusted if necessary, but the y zoom is not changed.
754 * x and y parameters are specified in summary coordinates.
757 EditorSummary::set_editor (pair<double,double> const x, double const y)
759 if (_editor->pending_visual_change.idle_handler_id >= 0) {
760 /* see comment in other set_editor () */
768 /** Set the editor to display given x and y ranges. x zoom and track heights are
769 * adjusted if necessary.
770 * x and y parameters are specified in summary coordinates.
773 EditorSummary::set_editor (pair<double,double> const x, pair<double, double> const y)
775 if (_editor->pending_visual_change.idle_handler_id >= 0) {
776 /* see comment in other set_editor () */
784 /** Set the left of the x range visible in the editor.
785 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
786 * @param x new x left position in summary coordinates.
789 EditorSummary::set_editor_x (double x)
795 if (suspending_editor_updates ()) {
796 double const w = _pending_editor_x.second - _pending_editor_x.first;
797 _pending_editor_x.first = x;
798 _pending_editor_x.second = x + w;
799 _pending_editor_changed = true;
802 _editor->reset_x_origin (x / _x_scale + _start);
806 /** Set the x range visible in the editor.
807 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
808 * @param x new x range in summary coordinates.
811 EditorSummary::set_editor_x (pair<double, double> x)
818 x.second = x.first + 1;
821 if (suspending_editor_updates ()) {
822 _pending_editor_x = x;
823 _pending_editor_changed = true;
826 _editor->reset_x_origin (x.first / _x_scale + _start);
829 ((x.second - x.first) / _x_scale) /
830 _editor->sample_to_pixel (_editor->current_page_samples())
833 if (nx != _editor->get_current_zoom ()) {
834 _editor->reset_zoom (nx);
839 /** Set the top of the y range visible in the editor.
840 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
841 * @param y new editor top in summary coodinates.
844 EditorSummary::set_editor_y (double const y)
846 double y1 = summary_y_to_editor (y);
847 double const eh = _editor->visible_canvas_height();
850 double const full_editor_height = _editor->_full_canvas_height;
852 if (y2 > full_editor_height) {
853 y1 -= y2 - full_editor_height;
860 if (suspending_editor_updates ()) {
861 double const h = _pending_editor_y.second - _pending_editor_y.first;
862 _pending_editor_y.first = y;
863 _pending_editor_y.second = y + h;
864 _pending_editor_changed = true;
867 _editor->reset_y_origin (y1);
871 /** Set the y range visible in the editor. This is achieved by scaling track heights,
873 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
874 * @param y new editor range in summary coodinates.
877 EditorSummary::set_editor_y (pair<double, double> const y)
879 if (suspending_editor_updates ()) {
880 _pending_editor_y = y;
881 _pending_editor_changed = true;
886 /* Compute current height of tracks between y.first and y.second. We add up
887 the total height into `total_height' and the height of complete tracks into
891 /* Copy of target range for use below */
892 pair<double, double> yc = y;
893 /* Total height of all tracks */
894 double total_height = 0;
895 /* Height of any parts of tracks that aren't fully in the desired range */
896 double partial_height = 0;
897 /* Height of any tracks that are fully in the desired range */
898 double scale_height = 0;
900 _editor->_routes->suspend_redisplay ();
902 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
904 if ((*i)->hidden()) {
908 double const h = (*i)->effective_height ();
911 if (yc.first > 0 && yc.first < _track_height) {
912 partial_height += (_track_height - yc.first) * h / _track_height;
913 } else if (yc.first <= 0 && yc.second >= _track_height) {
915 } else if (yc.second > 0 && yc.second < _track_height) {
916 partial_height += yc.second * h / _track_height;
920 yc.first -= _track_height;
921 yc.second -= _track_height;
924 /* Height that we will use for scaling; use the whole editor height unless there are not
925 enough tracks to fill it.
927 double const ch = min (total_height, _editor->visible_canvas_height());
929 /* hence required scale factor of the complete tracks to fit the required y range;
930 the amount of space they should take up divided by the amount they currently take up.
932 double const scale = (ch - partial_height) / scale_height;
936 /* Scale complete tracks within the range to make it fit */
938 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
940 if ((*i)->hidden()) {
944 if (yc.first <= 0 && yc.second >= _track_height) {
945 (*i)->set_height (max (TimeAxisView::preset_height (HeightSmall), (uint32_t) ((*i)->effective_height() * scale)));
948 yc.first -= _track_height;
949 yc.second -= _track_height;
952 _editor->_routes->resume_redisplay ();
954 set_editor_y (y.first);
958 EditorSummary::playhead_position_changed (framepos_t p)
960 int const o = int (_last_playhead);
961 int const n = int (playhead_frame_to_position (p));
962 if (_session && o != n) {
963 int a = max(2, min (o, n));
965 set_overlays_dirty (a - 2, 0, b + 2, get_height ());
970 EditorSummary::summary_y_to_editor (double y) const
973 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
975 if ((*i)->hidden()) {
979 double const h = (*i)->effective_height ();
980 if (y < _track_height) {
982 return ey + y * h / _track_height;
993 EditorSummary::editor_y_to_summary (double y) const
996 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
998 if ((*i)->hidden()) {
1002 double const h = (*i)->effective_height ();
1005 return sy + y * _track_height / h;
1008 sy += _track_height;
1016 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
1018 for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
1019 /* Connect to gui_changed() on the route so that we know when their colour has changed */
1020 (*i)->route()->gui_changed.connect (*this, invalidator (*this), boost::bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
1021 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> ((*i)->route ());
1023 tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context ());
1027 _background_dirty = true;
1032 EditorSummary::route_gui_changed (string c)
1035 _background_dirty = true;
1041 EditorSummary::playhead_frame_to_position (framepos_t t) const
1043 return (t - _start) * _x_scale;
1047 EditorSummary::position_to_playhead_frame_to_position (double pos) const
1049 return _start + (pos * _x_scale);