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),
59 _background_dirty (true)
61 add_events (Gdk::POINTER_MOTION_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
62 set_flags (get_flags() | Gtk::CAN_FOCUS);
65 EditorSummary::~EditorSummary ()
67 cairo_surface_destroy (_image);
70 /** Handle a size allocation.
71 * @param alloc GTK allocation.
74 EditorSummary::on_size_allocate (Gtk::Allocation& alloc)
76 Gtk::EventBox::on_size_allocate (alloc);
77 _background_dirty = true;
82 /** Connect to a session.
86 EditorSummary::set_session (Session* s)
88 SessionHandlePtr::set_session (s);
92 /* Note: the EditorSummary already finds out about new regions from Editor::region_view_added
93 * (which attaches to StreamView::RegionViewAdded), and cut regions by the RegionPropertyChanged
94 * emitted when a cut region is added to the `cutlist' playlist.
98 Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
99 Route::RemoteControlIDChange.connect (route_ctrl_id_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
100 _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), boost::bind (&EditorSummary::playhead_position_changed, this, _1), gui_context());
101 _session->StartTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
102 _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
103 _editor->selection->RegionsChanged.connect (sigc::mem_fun(*this, &EditorSummary::set_background_dirty));
108 EditorSummary::render_background_image ()
110 cairo_surface_destroy (_image); // passing NULL is safe
111 _image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, get_width (), get_height ());
113 cairo_t* cr = cairo_create (_image);
115 /* background (really just the dividing lines between tracks */
117 cairo_set_source_rgb (cr, 0, 0, 0);
118 cairo_rectangle (cr, 0, 0, get_width(), get_height());
121 /* compute start and end points for the summary */
123 framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
124 double const theoretical_start = _session->current_start_frame() - session_length * _overhang_fraction;
125 _start = theoretical_start > 0 ? theoretical_start : 0;
126 _end = _session->current_end_frame() + session_length * _overhang_fraction;
128 /* compute track height */
130 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
131 if (!(*i)->hidden()) {
139 _track_height = (double) get_height() / N;
142 /* calculate x scale */
143 if (_end != _start) {
144 _x_scale = static_cast<double> (get_width()) / (_end - _start);
149 /* render tracks and regions */
152 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
154 if ((*i)->hidden()) {
158 /* paint a non-bg colored strip to represent the track itself */
160 cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
161 cairo_set_line_width (cr, _track_height - 1);
162 cairo_move_to (cr, 0, y + _track_height / 2);
163 cairo_line_to (cr, get_width(), y + _track_height / 2);
166 StreamView* s = (*i)->view ();
169 cairo_set_line_width (cr, _track_height * 0.6);
171 s->foreach_regionview (sigc::bind (
172 sigc::mem_fun (*this, &EditorSummary::render_region),
174 y + _track_height / 2
181 /* start and end markers */
183 cairo_set_line_width (cr, 1);
184 cairo_set_source_rgb (cr, 1, 1, 0);
186 const double p = (_session->current_start_frame() - _start) * _x_scale;
187 cairo_move_to (cr, p, 0);
188 cairo_line_to (cr, p, get_height());
190 double const q = (_session->current_end_frame() - _start) * _x_scale;
191 cairo_move_to (cr, q, 0);
192 cairo_line_to (cr, q, get_height());
198 /** Render the required regions to a cairo context.
202 EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
209 if (!_image || _background_dirty) {
210 render_background_image ();
211 _background_dirty = false;
214 cairo_push_group (cr);
216 /* Fill with the background image */
218 cairo_rectangle (cr, 0, 0, get_width(), get_height());
219 cairo_set_source_surface (cr, _image, 0, 0);
222 /* Render the view rectangle. If there is an editor visual pending, don't update
223 the view rectangle now --- wait until the expose event that we'll get after
224 the visual change. This prevents a flicker.
227 if (_editor->pending_visual_change.idle_handler_id < 0) {
228 get_editor (&_view_rectangle_x, &_view_rectangle_y);
231 int32_t width = _view_rectangle_x.second - _view_rectangle_x.first;
232 int32_t height = _view_rectangle_y.second - _view_rectangle_y.first;
233 cairo_rectangle (cr, _view_rectangle_x.first, _view_rectangle_y.first, width, height);
234 cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
235 cairo_fill_preserve (cr);
236 cairo_set_line_width (cr, 1);
237 cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
242 cairo_set_line_width (cr, 1);
243 /* XXX: colour should be set from configuration file */
244 cairo_set_source_rgba (cr, 1, 0, 0, 1);
246 const double ph= playhead_frame_to_position (_editor->playhead_cursor->current_frame());
247 cairo_move_to (cr, ph, 0);
248 cairo_line_to (cr, ph, get_height());
250 cairo_pop_group_to_source (cr);
256 /** Render a region for the summary.
257 * @param r Region view.
258 * @param cr Cairo context.
259 * @param y y coordinate to render at.
262 EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const
264 uint32_t const c = r->get_fill_color ();
265 cairo_set_source_rgb (cr, UINT_RGBA_R (c) / 255.0, UINT_RGBA_G (c) / 255.0, UINT_RGBA_B (c) / 255.0);
267 if (r->region()->position() > _start) {
268 cairo_move_to (cr, (r->region()->position() - _start) * _x_scale, y);
270 cairo_move_to (cr, 0, y);
273 if ((r->region()->position() + r->region()->length()) > _start) {
274 cairo_line_to (cr, ((r->region()->position() - _start + r->region()->length())) * _x_scale, y);
276 cairo_line_to (cr, 0, y);
283 EditorSummary::set_background_dirty ()
285 _background_dirty = true;
289 /** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
291 EditorSummary::set_overlays_dirty ()
293 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
297 /** Set the summary so that just the overlays (viewbox, playhead etc.) in a given area will be re-rendered */
299 EditorSummary::set_overlays_dirty (int x, int y, int w, int h)
301 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
302 queue_draw_area (x, y, w, h);
306 /** Handle a size request.
307 * @param req GTK requisition
310 EditorSummary::on_size_request (Gtk::Requisition *req)
312 /* Use a dummy, small width and the actual height that we want */
319 EditorSummary::centre_on_click (GdkEventButton* ev)
321 pair<double, double> xr;
322 pair<double, double> yr;
323 get_editor (&xr, &yr);
325 double const w = xr.second - xr.first;
326 double ex = ev->x - w / 2;
329 } else if ((ex + w) > get_width()) {
330 ex = get_width() - w;
333 double const h = yr.second - yr.first;
334 double ey = ev->y - h / 2;
337 } else if ((ey + h) > get_height()) {
338 ey = get_height() - h;
345 EditorSummary::on_enter_notify_event (GdkEventCrossing*)
348 Keyboard::magic_widget_grab_focus ();
353 EditorSummary::on_leave_notify_event (GdkEventCrossing*)
355 /* there are no inferior/child windows, so any leave event means that
358 Keyboard::magic_widget_drop_focus ();
363 EditorSummary::on_key_press_event (GdkEventKey* key)
366 GtkAccelKey set_playhead_accel;
367 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
368 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
371 _session->request_locate (_start + (framepos_t) x / _x_scale, _session->transport_rolling());
381 EditorSummary::on_key_release_event (GdkEventKey* key)
384 GtkAccelKey set_playhead_accel;
385 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
386 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
393 /** Handle a button press.
394 * @param ev GTK event.
397 EditorSummary::on_button_press_event (GdkEventButton* ev)
399 _old_follow_playhead = _editor->follow_playhead ();
401 if (ev->button == 1) {
403 pair<double, double> xr;
404 pair<double, double> yr;
405 get_editor (&xr, &yr);
407 _start_editor_x = xr;
408 _start_editor_y = yr;
409 _start_mouse_x = ev->x;
410 _start_mouse_y = ev->y;
411 _start_position = get_position (ev->x, ev->y);
413 if (_start_position != INSIDE && _start_position != BELOW_OR_ABOVE &&
414 _start_position != TO_LEFT_OR_RIGHT && _start_position != OTHERWISE_OUTSIDE
417 /* start a zoom drag */
419 _zoom_position = get_position (ev->x, ev->y);
420 _zoom_dragging = true;
421 _editor->_dragging_playhead = true;
422 _editor->set_follow_playhead (false);
424 if (suspending_editor_updates ()) {
425 get_editor (&_pending_editor_x, &_pending_editor_y);
426 _pending_editor_changed = false;
429 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
431 /* secondary-modifier-click: locate playhead */
433 _session->request_locate (ev->x / _x_scale + _start);
436 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
438 centre_on_click (ev);
442 /* start a move drag */
444 /* get the editor's state in case we are suspending updates */
445 get_editor (&_pending_editor_x, &_pending_editor_y);
446 _pending_editor_changed = false;
448 _move_dragging = true;
450 _editor->_dragging_playhead = true;
451 _editor->set_follow_playhead (false);
453 ArdourCanvas::checkpoint ("sum", "------------------ summary move drag starts.\n");
460 /** @return true if we are currently suspending updates to the editor's viewport,
461 * which we do if configured to do so, and if in a drag of some kind.
464 EditorSummary::suspending_editor_updates () const
466 return (!Config->get_update_editor_during_summary_drag () && (_zoom_dragging || _move_dragging));
469 /** Fill in x and y with the editor's current viewable area in summary coordinates */
471 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
476 if (suspending_editor_updates ()) {
478 /* We are dragging, and configured not to update the editor window during drags,
479 so just return where the editor will be when the drag finishes.
482 *x = _pending_editor_x;
483 *y = _pending_editor_y;
487 /* Otherwise query the editor for its actual position */
489 x->first = (_editor->leftmost_sample () - _start) * _x_scale;
490 x->second = x->first + _editor->current_page_samples() * _x_scale;
492 y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
493 y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->visible_canvas_height());
497 /** Get an expression of the position of a point with respect to the view rectangle */
498 EditorSummary::Position
499 EditorSummary::get_position (double x, double y) const
501 /* how close the mouse has to be to the edge of the view rectangle to be considered `on it',
504 int x_edge_size = (_view_rectangle_x.second - _view_rectangle_x.first) / 4;
505 x_edge_size = min (x_edge_size, 8);
506 x_edge_size = max (x_edge_size, 1);
508 int y_edge_size = (_view_rectangle_y.second - _view_rectangle_y.first) / 4;
509 y_edge_size = min (y_edge_size, 8);
510 y_edge_size = max (y_edge_size, 1);
512 bool const near_left = (std::abs (x - _view_rectangle_x.first) < x_edge_size);
513 bool const near_right = (std::abs (x - _view_rectangle_x.second) < x_edge_size);
514 bool const near_top = (std::abs (y - _view_rectangle_y.first) < y_edge_size);
515 bool const near_bottom = (std::abs (y - _view_rectangle_y.second) < y_edge_size);
516 bool const within_x = _view_rectangle_x.first < x && x < _view_rectangle_x.second;
517 bool const within_y = _view_rectangle_y.first < y && y < _view_rectangle_y.second;
519 if (near_left && near_top) {
521 } else if (near_left && near_bottom) {
523 } else if (near_right && near_top) {
525 } else if (near_right && near_bottom) {
527 } else if (near_left && within_y) {
529 } else if (near_right && within_y) {
531 } else if (near_top && within_x) {
533 } else if (near_bottom && within_x) {
535 } else if (within_x && within_y) {
537 } else if (within_x) {
538 return BELOW_OR_ABOVE;
539 } else if (within_y) {
540 return TO_LEFT_OR_RIGHT;
542 return OTHERWISE_OUTSIDE;
547 EditorSummary::set_cursor (Position p)
551 get_window()->set_cursor (*_editor->_cursors->resize_left);
554 get_window()->set_cursor (*_editor->_cursors->resize_top_left);
557 get_window()->set_cursor (*_editor->_cursors->resize_top);
560 get_window()->set_cursor (*_editor->_cursors->resize_top_right);
563 get_window()->set_cursor (*_editor->_cursors->resize_right);
566 get_window()->set_cursor (*_editor->_cursors->resize_bottom_right);
569 get_window()->set_cursor (*_editor->_cursors->resize_bottom);
572 get_window()->set_cursor (*_editor->_cursors->resize_bottom_left);
575 get_window()->set_cursor (*_editor->_cursors->move);
577 case TO_LEFT_OR_RIGHT:
578 get_window()->set_cursor (*_editor->_cursors->expand_left_right);
581 get_window()->set_cursor (*_editor->_cursors->expand_up_down);
584 get_window()->set_cursor ();
590 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
592 pair<double, double> xr = _start_editor_x;
593 pair<double, double> yr = _start_editor_y;
594 double x = _start_editor_x.first;
595 double y = _start_editor_y.first;
597 if (_move_dragging) {
601 /* don't alter x if we clicked outside and above or below the viewbox */
602 if (_start_position == INSIDE || _start_position == TO_LEFT_OR_RIGHT || _start_position == OTHERWISE_OUTSIDE) {
603 x += ev->x - _start_mouse_x;
606 /* don't alter y if we clicked outside and to the left or right of the viewbox */
607 if (_start_position == INSIDE || _start_position == BELOW_OR_ABOVE) {
608 y += ev->y - _start_mouse_y;
620 // set_cursor (_start_position);
622 } else if (_zoom_dragging) {
624 double const dx = ev->x - _start_mouse_x;
625 double const dy = ev->y - _start_mouse_y;
627 if (_zoom_position == LEFT || _zoom_position == LEFT_TOP || _zoom_position == LEFT_BOTTOM) {
629 } else if (_zoom_position == RIGHT || _zoom_position == RIGHT_TOP || _zoom_position == RIGHT_BOTTOM) {
633 if (_zoom_position == TOP || _zoom_position == LEFT_TOP || _zoom_position == RIGHT_TOP) {
635 } else if (_zoom_position == BOTTOM || _zoom_position == LEFT_BOTTOM || _zoom_position == RIGHT_BOTTOM) {
639 set_overlays_dirty ();
640 set_cursor (_zoom_position);
645 set_cursor (get_position (ev->x, ev->y));
653 EditorSummary::on_button_release_event (GdkEventButton*)
655 bool const was_suspended = suspending_editor_updates ();
657 _move_dragging = false;
658 _zoom_dragging = false;
659 _editor->_dragging_playhead = false;
660 _editor->set_follow_playhead (_old_follow_playhead, false);
662 if (was_suspended && _pending_editor_changed) {
663 set_editor (_pending_editor_x, _pending_editor_y);
670 EditorSummary::on_scroll_event (GdkEventScroll* ev)
674 pair<double, double> xr;
675 pair<double, double> yr;
676 get_editor (&xr, &yr);
680 switch (ev->direction) {
682 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
684 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
685 _editor->temporal_zoom_step (false);
686 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
695 case GDK_SCROLL_DOWN:
696 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
698 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
699 _editor->temporal_zoom_step (true);
700 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
709 case GDK_SCROLL_LEFT:
710 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
712 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
718 case GDK_SCROLL_RIGHT:
719 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
721 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
735 /** Set the editor to display a x range with the left at a given position
736 * and a y range with the top at a given position.
737 * x and y parameters are specified in summary coordinates.
738 * Zoom is not changed in either direction.
741 EditorSummary::set_editor (double const x, double const y)
743 if (_editor->pending_visual_change.idle_handler_id >= 0 && _editor->pending_visual_change.being_handled == true) {
745 /* As a side-effect, the Editor's visual change idle handler processes
746 pending GTK events. Hence this motion notify handler can be called
747 in the middle of a visual change idle handler, and if this happens,
748 the queue_visual_change calls below modify the variables that the
749 idle handler is working with. This causes problems. Hence this
750 check. It ensures that we won't modify the pending visual change
751 while a visual change idle handler is in progress. It's not perfect,
752 as it also means that we won't change these variables if an idle handler
753 is merely pending but not executing. But c'est la vie.
763 /** Set the editor to display a given x range and a y range with the top at a given position.
764 * The editor's x zoom is adjusted if necessary, but the y zoom is not changed.
765 * x and y parameters are specified in summary coordinates.
768 EditorSummary::set_editor (pair<double,double> const x, double const y)
770 if (_editor->pending_visual_change.idle_handler_id >= 0) {
771 /* see comment in other set_editor () */
779 /** Set the editor to display given x and y ranges. x zoom and track heights are
780 * adjusted if necessary.
781 * x and y parameters are specified in summary coordinates.
784 EditorSummary::set_editor (pair<double,double> const x, pair<double, double> const y)
786 if (_editor->pending_visual_change.idle_handler_id >= 0) {
787 /* see comment in other set_editor () */
795 /** Set the left of the x range visible in the editor.
796 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
797 * @param x new x left position in summary coordinates.
800 EditorSummary::set_editor_x (double x)
806 if (suspending_editor_updates ()) {
807 double const w = _pending_editor_x.second - _pending_editor_x.first;
808 _pending_editor_x.first = x;
809 _pending_editor_x.second = x + w;
810 _pending_editor_changed = true;
813 _editor->reset_x_origin (x / _x_scale + _start);
817 /** Set the x range visible in the editor.
818 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
819 * @param x new x range in summary coordinates.
822 EditorSummary::set_editor_x (pair<double, double> x)
829 x.second = x.first + 1;
832 if (suspending_editor_updates ()) {
833 _pending_editor_x = x;
834 _pending_editor_changed = true;
837 _editor->reset_x_origin (x.first / _x_scale + _start);
840 ((x.second - x.first) / _x_scale) /
841 _editor->sample_to_pixel (_editor->current_page_samples())
844 if (nx != _editor->get_current_zoom ()) {
845 _editor->reset_zoom (nx);
850 /** Set the top of the y range visible in the editor.
851 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
852 * @param y new editor top in summary coodinates.
855 EditorSummary::set_editor_y (double const y)
857 double y1 = summary_y_to_editor (y);
858 double const eh = _editor->visible_canvas_height();
861 double const full_editor_height = _editor->_full_canvas_height;
863 if (y2 > full_editor_height) {
864 y1 -= y2 - full_editor_height;
871 if (suspending_editor_updates ()) {
872 double const h = _pending_editor_y.second - _pending_editor_y.first;
873 _pending_editor_y.first = y;
874 _pending_editor_y.second = y + h;
875 _pending_editor_changed = true;
878 _editor->reset_y_origin (y1);
882 /** Set the y range visible in the editor. This is achieved by scaling track heights,
884 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
885 * @param y new editor range in summary coodinates.
888 EditorSummary::set_editor_y (pair<double, double> const y)
890 if (suspending_editor_updates ()) {
891 _pending_editor_y = y;
892 _pending_editor_changed = true;
897 /* Compute current height of tracks between y.first and y.second. We add up
898 the total height into `total_height' and the height of complete tracks into
902 /* Copy of target range for use below */
903 pair<double, double> yc = y;
904 /* Total height of all tracks */
905 double total_height = 0;
906 /* Height of any parts of tracks that aren't fully in the desired range */
907 double partial_height = 0;
908 /* Height of any tracks that are fully in the desired range */
909 double scale_height = 0;
911 _editor->_routes->suspend_redisplay ();
913 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
915 if ((*i)->hidden()) {
919 double const h = (*i)->effective_height ();
922 if (yc.first > 0 && yc.first < _track_height) {
923 partial_height += (_track_height - yc.first) * h / _track_height;
924 } else if (yc.first <= 0 && yc.second >= _track_height) {
926 } else if (yc.second > 0 && yc.second < _track_height) {
927 partial_height += yc.second * h / _track_height;
931 yc.first -= _track_height;
932 yc.second -= _track_height;
935 /* Height that we will use for scaling; use the whole editor height unless there are not
936 enough tracks to fill it.
938 double const ch = min (total_height, _editor->visible_canvas_height());
940 /* hence required scale factor of the complete tracks to fit the required y range;
941 the amount of space they should take up divided by the amount they currently take up.
943 double const scale = (ch - partial_height) / scale_height;
947 /* Scale complete tracks within the range to make it fit */
949 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
951 if ((*i)->hidden()) {
955 if (yc.first <= 0 && yc.second >= _track_height) {
956 (*i)->set_height (max (TimeAxisView::preset_height (HeightSmall), (uint32_t) ((*i)->effective_height() * scale)));
959 yc.first -= _track_height;
960 yc.second -= _track_height;
963 _editor->_routes->resume_redisplay ();
965 set_editor_y (y.first);
969 EditorSummary::playhead_position_changed (framepos_t p)
971 int const o = int (_last_playhead);
972 int const n = int (playhead_frame_to_position (p));
973 if (_session && o != n) {
974 int a = max(2, min (o, n));
976 set_overlays_dirty (a - 2, 0, b + 2, get_height ());
981 EditorSummary::summary_y_to_editor (double y) const
984 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
986 if ((*i)->hidden()) {
990 double const h = (*i)->effective_height ();
991 if (y < _track_height) {
993 return ey + y * h / _track_height;
1004 EditorSummary::editor_y_to_summary (double y) const
1007 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
1009 if ((*i)->hidden()) {
1013 double const h = (*i)->effective_height ();
1016 return sy + y * _track_height / h;
1019 sy += _track_height;
1027 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
1029 for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
1030 /* Connect to gui_changed() on the route so that we know when their colour has changed */
1031 (*i)->route()->gui_changed.connect (*this, invalidator (*this), boost::bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
1032 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> ((*i)->route ());
1034 tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context ());
1038 _background_dirty = true;
1043 EditorSummary::route_gui_changed (string c)
1046 _background_dirty = true;
1052 EditorSummary::playhead_frame_to_position (framepos_t t) const
1054 return (t - _start) * _x_scale;
1058 EditorSummary::position_to_playhead_frame_to_position (double pos) const
1060 return _start + (pos * _x_scale);