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"
36 #include "ui_config.h"
39 using namespace ARDOUR;
40 using Gtkmm2ext::Keyboard;
42 /** Construct an EditorSummary.
43 * @param e Editor to represent.
45 EditorSummary::EditorSummary (Editor* e)
46 : EditorComponent (e),
49 _overhang_fraction (0.1),
53 _move_dragging (false),
55 _view_rectangle_x (0, 0),
56 _view_rectangle_y (0, 0),
57 _zoom_dragging (false),
58 _old_follow_playhead (false),
60 _background_dirty (true)
62 add_events (Gdk::POINTER_MOTION_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
63 set_flags (get_flags() | Gtk::CAN_FOCUS);
65 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &EditorSummary::parameter_changed));
68 EditorSummary::~EditorSummary ()
70 cairo_surface_destroy (_image);
74 EditorSummary::parameter_changed (string p)
77 if (p == "color-regions-using-track-color") {
78 set_background_dirty ();
82 /** Handle a size allocation.
83 * @param alloc GTK allocation.
86 EditorSummary::on_size_allocate (Gtk::Allocation& alloc)
88 CairoWidget::on_size_allocate (alloc);
89 set_background_dirty ();
93 /** Connect to a session.
97 EditorSummary::set_session (Session* s)
99 SessionHandlePtr::set_session (s);
103 /* Note: the EditorSummary already finds out about new regions from Editor::region_view_added
104 * (which attaches to StreamView::RegionViewAdded), and cut regions by the RegionPropertyChanged
105 * emitted when a cut region is added to the `cutlist' playlist.
109 Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
110 Route::RemoteControlIDChange.connect (route_ctrl_id_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
111 _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), boost::bind (&EditorSummary::playhead_position_changed, this, _1), gui_context());
112 _session->StartTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
113 _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
114 _editor->selection->RegionsChanged.connect (sigc::mem_fun(*this, &EditorSummary::set_background_dirty));
119 EditorSummary::render_background_image ()
121 cairo_surface_destroy (_image); // passing NULL is safe
122 _image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, get_width (), get_height ());
124 cairo_t* cr = cairo_create (_image);
126 /* background (really just the dividing lines between tracks */
128 cairo_set_source_rgb (cr, 0, 0, 0);
129 cairo_rectangle (cr, 0, 0, get_width(), get_height());
132 /* compute start and end points for the summary */
134 framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
135 double const theoretical_start = _session->current_start_frame() - session_length * _overhang_fraction;
136 _start = theoretical_start > 0 ? theoretical_start : 0;
137 _end = _session->current_end_frame() + session_length * _overhang_fraction;
139 /* compute track height */
141 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
142 if (!(*i)->hidden()) {
150 _track_height = (double) get_height() / N;
153 /* calculate x scale */
154 if (_end != _start) {
155 _x_scale = static_cast<double> (get_width()) / (_end - _start);
160 /* render tracks and regions */
163 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
165 if ((*i)->hidden()) {
169 /* paint a non-bg colored strip to represent the track itself */
171 cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
172 cairo_set_line_width (cr, _track_height - 1);
173 cairo_move_to (cr, 0, y + _track_height / 2);
174 cairo_line_to (cr, get_width(), y + _track_height / 2);
177 StreamView* s = (*i)->view ();
180 cairo_set_line_width (cr, _track_height * 0.8);
182 s->foreach_regionview (sigc::bind (
183 sigc::mem_fun (*this, &EditorSummary::render_region),
185 y + _track_height / 2
192 /* start and end markers */
194 cairo_set_line_width (cr, 1);
195 cairo_set_source_rgb (cr, 1, 1, 0);
197 const double p = (_session->current_start_frame() - _start) * _x_scale;
198 cairo_move_to (cr, p, 0);
199 cairo_line_to (cr, p, get_height());
201 double const q = (_session->current_end_frame() - _start) * _x_scale;
202 cairo_move_to (cr, q, 0);
203 cairo_line_to (cr, q, get_height());
209 /** Render the required regions to a cairo context.
213 EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
220 if (!_image || _background_dirty) {
221 render_background_image ();
222 _background_dirty = false;
225 cairo_push_group (cr);
227 /* Fill with the background image */
229 cairo_rectangle (cr, 0, 0, get_width(), get_height());
230 cairo_set_source_surface (cr, _image, 0, 0);
233 /* Render the view rectangle. If there is an editor visual pending, don't update
234 the view rectangle now --- wait until the expose event that we'll get after
235 the visual change. This prevents a flicker.
238 if (_editor->pending_visual_change.idle_handler_id < 0) {
239 get_editor (&_view_rectangle_x, &_view_rectangle_y);
242 int32_t width = _view_rectangle_x.second - _view_rectangle_x.first;
243 int32_t height = _view_rectangle_y.second - _view_rectangle_y.first;
244 cairo_rectangle (cr, _view_rectangle_x.first, _view_rectangle_y.first, width, height);
245 cairo_set_source_rgba (cr, 1, 1, 1, 0.1);
246 cairo_fill_preserve (cr);
247 cairo_set_line_width (cr, 1);
248 cairo_set_source_rgba (cr, 1, 1, 1, 0.4);
253 cairo_set_line_width (cr, 1);
254 /* XXX: colour should be set from configuration file */
255 cairo_set_source_rgba (cr, 1, 0, 0, 1);
257 const double ph= playhead_frame_to_position (_editor->playhead_cursor->current_frame());
258 cairo_move_to (cr, ph, 0);
259 cairo_line_to (cr, ph, get_height());
261 cairo_pop_group_to_source (cr);
267 /** Render a region for the summary.
268 * @param r Region view.
269 * @param cr Cairo context.
270 * @param y y coordinate to render at.
273 EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const
275 uint32_t const c = r->get_fill_color ();
276 cairo_set_source_rgb (cr, UINT_RGBA_R (c) / 255.0, UINT_RGBA_G (c) / 255.0, UINT_RGBA_B (c) / 255.0);
278 if (r->region()->position() > _start) {
279 cairo_move_to (cr, (r->region()->position() - _start) * _x_scale, y);
281 cairo_move_to (cr, 0, y);
284 if ((r->region()->position() + r->region()->length()) > _start) {
285 cairo_line_to (cr, ((r->region()->position() - _start + r->region()->length())) * _x_scale, y);
287 cairo_line_to (cr, 0, y);
294 EditorSummary::set_background_dirty ()
296 _background_dirty = true;
300 /** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
302 EditorSummary::set_overlays_dirty ()
304 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
308 /** Set the summary so that just the overlays (viewbox, playhead etc.) in a given area will be re-rendered */
310 EditorSummary::set_overlays_dirty (int x, int y, int w, int h)
312 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
313 queue_draw_area (x, y, w, h);
317 /** Handle a size request.
318 * @param req GTK requisition
321 EditorSummary::on_size_request (Gtk::Requisition *req)
323 /* Use a dummy, small width and the actual height that we want */
330 EditorSummary::centre_on_click (GdkEventButton* ev)
332 pair<double, double> xr;
333 pair<double, double> yr;
334 get_editor (&xr, &yr);
336 double const w = xr.second - xr.first;
337 double ex = ev->x - w / 2;
340 } else if ((ex + w) > get_width()) {
341 ex = get_width() - w;
344 double const h = yr.second - yr.first;
345 double ey = ev->y - h / 2;
348 } else if ((ey + h) > get_height()) {
349 ey = get_height() - h;
356 EditorSummary::on_enter_notify_event (GdkEventCrossing*)
359 Keyboard::magic_widget_grab_focus ();
364 EditorSummary::on_leave_notify_event (GdkEventCrossing*)
366 /* there are no inferior/child windows, so any leave event means that
369 Keyboard::magic_widget_drop_focus ();
374 EditorSummary::on_key_press_event (GdkEventKey* key)
377 GtkAccelKey set_playhead_accel;
378 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
379 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
382 _session->request_locate (_start + (framepos_t) x / _x_scale, _session->transport_rolling());
392 EditorSummary::on_key_release_event (GdkEventKey* key)
395 GtkAccelKey set_playhead_accel;
396 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
397 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
404 /** Handle a button press.
405 * @param ev GTK event.
408 EditorSummary::on_button_press_event (GdkEventButton* ev)
410 _old_follow_playhead = _editor->follow_playhead ();
412 if (ev->button == 1) {
414 pair<double, double> xr;
415 pair<double, double> yr;
416 get_editor (&xr, &yr);
418 _start_editor_x = xr;
419 _start_editor_y = yr;
420 _start_mouse_x = ev->x;
421 _start_mouse_y = ev->y;
422 _start_position = get_position (ev->x, ev->y);
424 if (_start_position != INSIDE && _start_position != BELOW_OR_ABOVE &&
425 _start_position != TO_LEFT_OR_RIGHT && _start_position != OTHERWISE_OUTSIDE
428 /* start a zoom drag */
430 _zoom_position = get_position (ev->x, ev->y);
431 _zoom_dragging = true;
432 _editor->_dragging_playhead = true;
433 _editor->set_follow_playhead (false);
435 if (suspending_editor_updates ()) {
436 get_editor (&_pending_editor_x, &_pending_editor_y);
437 _pending_editor_changed = false;
440 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
442 /* secondary-modifier-click: locate playhead */
444 _session->request_locate (ev->x / _x_scale + _start);
447 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
449 centre_on_click (ev);
453 /* start a move drag */
455 /* get the editor's state in case we are suspending updates */
456 get_editor (&_pending_editor_x, &_pending_editor_y);
457 _pending_editor_changed = false;
459 _move_dragging = true;
461 _editor->_dragging_playhead = true;
462 _editor->set_follow_playhead (false);
464 ArdourCanvas::checkpoint ("sum", "------------------ summary move drag starts.\n");
471 /** @return true if we are currently suspending updates to the editor's viewport,
472 * which we do if configured to do so, and if in a drag of some kind.
475 EditorSummary::suspending_editor_updates () const
477 return (!UIConfiguration::instance().get_update_editor_during_summary_drag () && (_zoom_dragging || _move_dragging));
480 /** Fill in x and y with the editor's current viewable area in summary coordinates */
482 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
487 if (suspending_editor_updates ()) {
489 /* We are dragging, and configured not to update the editor window during drags,
490 so just return where the editor will be when the drag finishes.
493 *x = _pending_editor_x;
494 *y = _pending_editor_y;
498 /* Otherwise query the editor for its actual position */
500 x->first = (_editor->leftmost_sample () - _start) * _x_scale;
501 x->second = x->first + _editor->current_page_samples() * _x_scale;
503 y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
504 y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y);
508 /** Get an expression of the position of a point with respect to the view rectangle */
509 EditorSummary::Position
510 EditorSummary::get_position (double x, double y) const
512 /* how close the mouse has to be to the edge of the view rectangle to be considered `on it',
515 int x_edge_size = (_view_rectangle_x.second - _view_rectangle_x.first) / 4;
516 x_edge_size = min (x_edge_size, 8);
517 x_edge_size = max (x_edge_size, 1);
519 int y_edge_size = (_view_rectangle_y.second - _view_rectangle_y.first) / 4;
520 y_edge_size = min (y_edge_size, 8);
521 y_edge_size = max (y_edge_size, 1);
523 bool const near_left = (std::abs (x - _view_rectangle_x.first) < x_edge_size);
524 bool const near_right = (std::abs (x - _view_rectangle_x.second) < x_edge_size);
525 bool const near_top = (std::abs (y - _view_rectangle_y.first) < y_edge_size);
526 bool const near_bottom = (std::abs (y - _view_rectangle_y.second) < y_edge_size);
527 bool const within_x = _view_rectangle_x.first < x && x < _view_rectangle_x.second;
528 bool const within_y = _view_rectangle_y.first < y && y < _view_rectangle_y.second;
530 if (near_left && near_top) {
532 } else if (near_left && near_bottom) {
534 } else if (near_right && near_top) {
536 } else if (near_right && near_bottom) {
538 } else if (near_left && within_y) {
540 } else if (near_right && within_y) {
542 } else if (near_top && within_x) {
544 } else if (near_bottom && within_x) {
546 } else if (within_x && within_y) {
548 } else if (within_x) {
549 return BELOW_OR_ABOVE;
550 } else if (within_y) {
551 return TO_LEFT_OR_RIGHT;
553 return OTHERWISE_OUTSIDE;
558 EditorSummary::set_cursor (Position p)
562 get_window()->set_cursor (*_editor->_cursors->resize_left);
565 get_window()->set_cursor (*_editor->_cursors->resize_top_left);
568 get_window()->set_cursor (*_editor->_cursors->resize_top);
571 get_window()->set_cursor (*_editor->_cursors->resize_top_right);
574 get_window()->set_cursor (*_editor->_cursors->resize_right);
577 get_window()->set_cursor (*_editor->_cursors->resize_bottom_right);
580 get_window()->set_cursor (*_editor->_cursors->resize_bottom);
583 get_window()->set_cursor (*_editor->_cursors->resize_bottom_left);
586 get_window()->set_cursor (*_editor->_cursors->move);
588 case TO_LEFT_OR_RIGHT:
589 get_window()->set_cursor (*_editor->_cursors->expand_left_right);
592 get_window()->set_cursor (*_editor->_cursors->expand_up_down);
595 get_window()->set_cursor ();
601 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
603 pair<double, double> xr = _start_editor_x;
604 pair<double, double> yr = _start_editor_y;
605 double x = _start_editor_x.first;
606 double y = _start_editor_y.first;
608 if (_move_dragging) {
612 /* don't alter x if we clicked outside and above or below the viewbox */
613 if (_start_position == INSIDE || _start_position == TO_LEFT_OR_RIGHT || _start_position == OTHERWISE_OUTSIDE) {
614 x += ev->x - _start_mouse_x;
617 /* don't alter y if we clicked outside and to the left or right of the viewbox */
618 if (_start_position == INSIDE || _start_position == BELOW_OR_ABOVE) {
619 y += ev->y - _start_mouse_y;
631 // set_cursor (_start_position);
633 } else if (_zoom_dragging) {
635 double const dx = ev->x - _start_mouse_x;
636 double const dy = ev->y - _start_mouse_y;
638 if (_zoom_position == LEFT || _zoom_position == LEFT_TOP || _zoom_position == LEFT_BOTTOM) {
640 } else if (_zoom_position == RIGHT || _zoom_position == RIGHT_TOP || _zoom_position == RIGHT_BOTTOM) {
643 xr.first = -1; /* do not change */
646 if (_zoom_position == TOP || _zoom_position == LEFT_TOP || _zoom_position == RIGHT_TOP) {
648 } else if (_zoom_position == BOTTOM || _zoom_position == LEFT_BOTTOM || _zoom_position == RIGHT_BOTTOM) {
651 yr.first = -1; /* do not change y */
654 set_overlays_dirty ();
655 set_cursor (_zoom_position);
660 set_cursor (get_position (ev->x, ev->y));
668 EditorSummary::on_button_release_event (GdkEventButton*)
670 bool const was_suspended = suspending_editor_updates ();
672 _move_dragging = false;
673 _zoom_dragging = false;
674 _editor->_dragging_playhead = false;
675 _editor->set_follow_playhead (_old_follow_playhead, false);
677 if (was_suspended && _pending_editor_changed) {
678 set_editor (_pending_editor_x, _pending_editor_y);
685 EditorSummary::on_scroll_event (GdkEventScroll* ev)
689 pair<double, double> xr;
690 pair<double, double> yr;
691 get_editor (&xr, &yr);
695 switch (ev->direction) {
697 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
699 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
700 _editor->temporal_zoom_step (false);
701 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
710 case GDK_SCROLL_DOWN:
711 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
713 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
714 _editor->temporal_zoom_step (true);
715 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
724 case GDK_SCROLL_LEFT:
725 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
727 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
733 case GDK_SCROLL_RIGHT:
734 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
736 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
750 /** Set the editor to display a x range with the left at a given position
751 * and a y range with the top at a given position.
752 * x and y parameters are specified in summary coordinates.
753 * Zoom is not changed in either direction.
756 EditorSummary::set_editor (double const x, double const y)
758 if (_editor->pending_visual_change.idle_handler_id >= 0 && _editor->pending_visual_change.being_handled == true) {
760 /* As a side-effect, the Editor's visual change idle handler processes
761 pending GTK events. Hence this motion notify handler can be called
762 in the middle of a visual change idle handler, and if this happens,
763 the queue_visual_change calls below modify the variables that the
764 idle handler is working with. This causes problems. Hence this
765 check. It ensures that we won't modify the pending visual change
766 while a visual change idle handler is in progress. It's not perfect,
767 as it also means that we won't change these variables if an idle handler
768 is merely pending but not executing. But c'est la vie.
778 /** Set the editor to display a given x range and a y range with the top at a given position.
779 * The editor's x zoom is adjusted if necessary, but the y zoom is not changed.
780 * x and y parameters are specified in summary coordinates.
783 EditorSummary::set_editor (pair<double,double> const x, double const y)
785 if (_editor->pending_visual_change.idle_handler_id >= 0) {
786 /* see comment in other set_editor () */
794 /** Set the editor to display given x and y ranges. x zoom and track heights are
795 * adjusted if necessary.
796 * x and y parameters are specified in summary coordinates.
799 EditorSummary::set_editor (pair<double,double> const x, pair<double, double> const y)
801 if (_editor->pending_visual_change.idle_handler_id >= 0) {
802 /* see comment in other set_editor () */
814 /** Set the left of the x range visible in the editor.
815 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
816 * @param x new x left position in summary coordinates.
819 EditorSummary::set_editor_x (double x)
825 if (suspending_editor_updates ()) {
826 double const w = _pending_editor_x.second - _pending_editor_x.first;
827 _pending_editor_x.first = x;
828 _pending_editor_x.second = x + w;
829 _pending_editor_changed = true;
832 _editor->reset_x_origin (x / _x_scale + _start);
836 /** Set the x range visible in the editor.
837 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
838 * @param x new x range in summary coordinates.
841 EditorSummary::set_editor_x (pair<double, double> x)
848 x.second = x.first + 1;
851 if (suspending_editor_updates ()) {
852 _pending_editor_x = x;
853 _pending_editor_changed = true;
856 _editor->reset_x_origin (x.first / _x_scale + _start);
859 ((x.second - x.first) / _x_scale) /
860 _editor->sample_to_pixel (_editor->current_page_samples())
863 if (nx != _editor->get_current_zoom ()) {
864 _editor->reset_zoom (nx);
869 /** Set the top of the y range visible in the editor.
870 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
871 * @param y new editor top in summary coodinates.
874 EditorSummary::set_editor_y (double const y)
876 double y1 = summary_y_to_editor (y);
877 double const eh = _editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y;
880 double const full_editor_height = _editor->_full_canvas_height;
882 if (y2 > full_editor_height) {
883 y1 -= y2 - full_editor_height;
890 if (suspending_editor_updates ()) {
891 double const h = _pending_editor_y.second - _pending_editor_y.first;
892 _pending_editor_y.first = y;
893 _pending_editor_y.second = y + h;
894 _pending_editor_changed = true;
897 _editor->reset_y_origin (y1);
901 /** Set the y range visible in the editor. This is achieved by scaling track heights,
903 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
904 * @param y new editor range in summary coodinates.
907 EditorSummary::set_editor_y (pair<double, double> const y)
909 if (suspending_editor_updates ()) {
910 _pending_editor_y = y;
911 _pending_editor_changed = true;
916 /* Compute current height of tracks between y.first and y.second. We add up
917 the total height into `total_height' and the height of complete tracks into
921 /* Copy of target range for use below */
922 pair<double, double> yc = y;
923 /* Total height of all tracks */
924 double total_height = 0;
925 /* Height of any parts of tracks that aren't fully in the desired range */
926 double partial_height = 0;
927 /* Height of any tracks that are fully in the desired range */
928 double scale_height = 0;
930 _editor->_routes->suspend_redisplay ();
932 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
934 if ((*i)->hidden()) {
938 double const h = (*i)->effective_height ();
941 if (yc.first > 0 && yc.first < _track_height) {
942 partial_height += (_track_height - yc.first) * h / _track_height;
943 } else if (yc.first <= 0 && yc.second >= _track_height) {
945 } else if (yc.second > 0 && yc.second < _track_height) {
946 partial_height += yc.second * h / _track_height;
950 yc.first -= _track_height;
951 yc.second -= _track_height;
954 /* Height that we will use for scaling; use the whole editor height unless there are not
955 enough tracks to fill it.
957 double const ch = min (total_height, (_editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y));
959 /* hence required scale factor of the complete tracks to fit the required y range;
960 the amount of space they should take up divided by the amount they currently take up.
962 double const scale = (ch - partial_height) / scale_height;
966 /* Scale complete tracks within the range to make it fit */
968 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
970 if ((*i)->hidden()) {
974 if (yc.first <= 0 && yc.second >= _track_height) {
975 (*i)->set_height (max (TimeAxisView::preset_height (HeightSmall), (uint32_t) ((*i)->effective_height() * scale)), TimeAxisView::TotalHeight);
978 yc.first -= _track_height;
979 yc.second -= _track_height;
982 _editor->_routes->resume_redisplay ();
984 set_editor_y (y.first);
988 EditorSummary::playhead_position_changed (framepos_t p)
990 int const o = int (_last_playhead);
991 int const n = int (playhead_frame_to_position (p));
992 if (_session && o != n) {
993 int a = max(2, min (o, n));
995 set_overlays_dirty (a - 2, 0, b + 2, get_height ());
1000 EditorSummary::summary_y_to_editor (double y) const
1003 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
1005 if ((*i)->hidden()) {
1009 double const h = (*i)->effective_height ();
1010 if (y < _track_height) {
1012 return ey + y * h / _track_height;
1023 EditorSummary::editor_y_to_summary (double y) const
1026 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
1028 if ((*i)->hidden()) {
1032 double const h = (*i)->effective_height ();
1035 return sy + y * _track_height / h;
1038 sy += _track_height;
1046 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
1048 for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
1049 /* Connect to gui_changed() on the route so that we know when their colour has changed */
1050 (*i)->route()->gui_changed.connect (*this, invalidator (*this), boost::bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
1051 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> ((*i)->route ());
1053 tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context ());
1057 set_background_dirty ();
1061 EditorSummary::route_gui_changed (string c)
1064 set_background_dirty ();
1069 EditorSummary::playhead_frame_to_position (framepos_t t) const
1071 return (t - _start) * _x_scale;
1075 EditorSummary::position_to_playhead_frame_to_position (double pos) const
1077 return _start + (pos * _x_scale);