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 (&EditorSummary::set_background_dirty, this), gui_context());
96 _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
97 _editor->selection->RegionsChanged.connect (sigc::mem_fun(*this, &EditorSummary::set_background_dirty));
102 EditorSummary::render_background_image ()
106 stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, get_width ());
107 data = (unsigned char*) malloc (stride * get_height ());
108 _image = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, get_width (), get_height (), stride);
110 cairo_t* cr = cairo_create (_image);
112 /* background (really just the dividing lines between tracks */
114 cairo_set_source_rgb (cr, 0, 0, 0);
115 cairo_rectangle (cr, 0, 0, get_width(), get_height());
118 /* compute start and end points for the summary */
120 framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
121 double const theoretical_start = _session->current_start_frame() - session_length * _overhang_fraction;
122 _start = theoretical_start > 0 ? theoretical_start : 0;
123 _end = _session->current_end_frame() + session_length * _overhang_fraction;
125 /* compute track height */
127 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
128 if (!(*i)->hidden()) {
136 _track_height = (double) get_height() / N;
139 /* calculate x scale */
140 if (_end != _start) {
141 _x_scale = static_cast<double> (get_width()) / (_end - _start);
146 /* render tracks and regions */
149 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
151 if ((*i)->hidden()) {
155 /* paint a non-bg colored strip to represent the track itself */
157 cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
158 cairo_set_line_width (cr, _track_height - 1);
159 cairo_move_to (cr, 0, y + _track_height / 2);
160 cairo_line_to (cr, get_width(), y + _track_height / 2);
163 StreamView* s = (*i)->view ();
166 cairo_set_line_width (cr, _track_height * 0.6);
168 s->foreach_regionview (sigc::bind (
169 sigc::mem_fun (*this, &EditorSummary::render_region),
171 y + _track_height / 2
178 /* start and end markers */
180 cairo_set_line_width (cr, 1);
181 cairo_set_source_rgb (cr, 1, 1, 0);
183 const double p = (_session->current_start_frame() - _start) * _x_scale;
184 cairo_move_to (cr, p, 0);
185 cairo_line_to (cr, p, get_height());
187 double const q = (_session->current_end_frame() - _start) * _x_scale;
188 cairo_move_to (cr, q, 0);
189 cairo_line_to (cr, q, get_height());
195 /** Render the required regions to a cairo context.
199 EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
206 if (!_image || _background_dirty) {
207 render_background_image ();
208 _background_dirty = false;
211 cairo_push_group (cr);
213 /* Fill with the background image */
215 cairo_rectangle (cr, 0, 0, get_width(), get_height());
216 cairo_set_source_surface (cr, _image, 0, 0);
219 /* Render the view rectangle. If there is an editor visual pending, don't update
220 the view rectangle now --- wait until the expose event that we'll get after
221 the visual change. This prevents a flicker.
224 if (_editor->pending_visual_change.idle_handler_id < 0) {
225 get_editor (&_view_rectangle_x, &_view_rectangle_y);
228 int32_t width = _view_rectangle_x.second - _view_rectangle_x.first;
229 int32_t height = _view_rectangle_y.second - _view_rectangle_y.first;
230 cairo_rectangle (cr, _view_rectangle_x.first, _view_rectangle_y.first, width, height);
231 cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
232 cairo_fill_preserve (cr);
233 cairo_set_line_width (cr, 1);
234 cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
239 cairo_set_line_width (cr, 1);
240 /* XXX: colour should be set from configuration file */
241 cairo_set_source_rgba (cr, 1, 0, 0, 1);
243 const double ph= playhead_frame_to_position (_editor->playhead_cursor->current_frame());
244 cairo_move_to (cr, ph, 0);
245 cairo_line_to (cr, ph, get_height());
247 cairo_pop_group_to_source (cr);
253 /** Render a region for the summary.
254 * @param r Region view.
255 * @param cr Cairo context.
256 * @param y y coordinate to render at.
259 EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const
261 uint32_t const c = r->get_fill_color ();
262 cairo_set_source_rgb (cr, UINT_RGBA_R (c) / 255.0, UINT_RGBA_G (c) / 255.0, UINT_RGBA_B (c) / 255.0);
264 if (r->region()->position() > _start) {
265 cairo_move_to (cr, (r->region()->position() - _start) * _x_scale, y);
267 cairo_move_to (cr, 0, y);
270 if ((r->region()->position() + r->region()->length()) > _start) {
271 cairo_line_to (cr, ((r->region()->position() - _start + r->region()->length())) * _x_scale, y);
273 cairo_line_to (cr, 0, y);
280 EditorSummary::set_background_dirty ()
282 _background_dirty = true;
286 /** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
288 EditorSummary::set_overlays_dirty ()
290 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
294 /** Set the summary so that just the overlays (viewbox, playhead etc.) in a given area will be re-rendered */
296 EditorSummary::set_overlays_dirty (int x, int y, int w, int h)
298 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
299 queue_draw_area (x, y, w, h);
303 /** Handle a size request.
304 * @param req GTK requisition
307 EditorSummary::on_size_request (Gtk::Requisition *req)
309 /* Use a dummy, small width and the actual height that we want */
316 EditorSummary::centre_on_click (GdkEventButton* ev)
318 pair<double, double> xr;
319 pair<double, double> yr;
320 get_editor (&xr, &yr);
322 double const w = xr.second - xr.first;
323 double ex = ev->x - w / 2;
326 } else if ((ex + w) > get_width()) {
327 ex = get_width() - w;
330 double const h = yr.second - yr.first;
331 double ey = ev->y - h / 2;
334 } else if ((ey + h) > get_height()) {
335 ey = get_height() - h;
342 EditorSummary::on_enter_notify_event (GdkEventCrossing*)
345 Keyboard::magic_widget_grab_focus ();
350 EditorSummary::on_leave_notify_event (GdkEventCrossing*)
352 /* there are no inferior/child windows, so any leave event means that
355 Keyboard::magic_widget_drop_focus ();
360 EditorSummary::on_key_press_event (GdkEventKey* key)
363 GtkAccelKey set_playhead_accel;
364 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
365 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
368 _session->request_locate (_start + (framepos_t) x / _x_scale, _session->transport_rolling());
378 EditorSummary::on_key_release_event (GdkEventKey* key)
381 GtkAccelKey set_playhead_accel;
382 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
383 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
390 /** Handle a button press.
391 * @param ev GTK event.
394 EditorSummary::on_button_press_event (GdkEventButton* ev)
396 _old_follow_playhead = _editor->follow_playhead ();
398 if (ev->button == 1) {
400 pair<double, double> xr;
401 pair<double, double> yr;
402 get_editor (&xr, &yr);
404 _start_editor_x = xr;
405 _start_editor_y = yr;
406 _start_mouse_x = ev->x;
407 _start_mouse_y = ev->y;
408 _start_position = get_position (ev->x, ev->y);
410 if (_start_position != INSIDE && _start_position != BELOW_OR_ABOVE &&
411 _start_position != TO_LEFT_OR_RIGHT && _start_position != OTHERWISE_OUTSIDE
414 /* start a zoom drag */
416 _zoom_position = get_position (ev->x, ev->y);
417 _zoom_dragging = true;
418 _editor->_dragging_playhead = true;
419 _editor->set_follow_playhead (false);
421 if (suspending_editor_updates ()) {
422 get_editor (&_pending_editor_x, &_pending_editor_y);
423 _pending_editor_changed = false;
426 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
428 /* secondary-modifier-click: locate playhead */
430 _session->request_locate (ev->x / _x_scale + _start);
433 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
435 centre_on_click (ev);
439 /* start a move drag */
441 /* get the editor's state in case we are suspending updates */
442 get_editor (&_pending_editor_x, &_pending_editor_y);
443 _pending_editor_changed = false;
445 _move_dragging = true;
447 _editor->_dragging_playhead = true;
448 _editor->set_follow_playhead (false);
450 ArdourCanvas::checkpoint ("sum", "------------------ summary move drag starts.\n");
457 /** @return true if we are currently suspending updates to the editor's viewport,
458 * which we do if configured to do so, and if in a drag of some kind.
461 EditorSummary::suspending_editor_updates () const
463 return (!Config->get_update_editor_during_summary_drag () && (_zoom_dragging || _move_dragging));
466 /** Fill in x and y with the editor's current viewable area in summary coordinates */
468 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
473 if (suspending_editor_updates ()) {
475 /* We are dragging, and configured not to update the editor window during drags,
476 so just return where the editor will be when the drag finishes.
479 *x = _pending_editor_x;
480 *y = _pending_editor_y;
484 /* Otherwise query the editor for its actual position */
486 x->first = (_editor->leftmost_sample () - _start) * _x_scale;
487 x->second = x->first + _editor->current_page_samples() * _x_scale;
489 y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
490 y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->visible_canvas_height());
494 /** Get an expression of the position of a point with respect to the view rectangle */
495 EditorSummary::Position
496 EditorSummary::get_position (double x, double y) const
498 /* how close the mouse has to be to the edge of the view rectangle to be considered `on it',
501 int x_edge_size = (_view_rectangle_x.second - _view_rectangle_x.first) / 4;
502 x_edge_size = min (x_edge_size, 8);
503 x_edge_size = max (x_edge_size, 1);
505 int y_edge_size = (_view_rectangle_y.second - _view_rectangle_y.first) / 4;
506 y_edge_size = min (y_edge_size, 8);
507 y_edge_size = max (y_edge_size, 1);
509 bool const near_left = (std::abs (x - _view_rectangle_x.first) < x_edge_size);
510 bool const near_right = (std::abs (x - _view_rectangle_x.second) < x_edge_size);
511 bool const near_top = (std::abs (y - _view_rectangle_y.first) < y_edge_size);
512 bool const near_bottom = (std::abs (y - _view_rectangle_y.second) < y_edge_size);
513 bool const within_x = _view_rectangle_x.first < x && x < _view_rectangle_x.second;
514 bool const within_y = _view_rectangle_y.first < y && y < _view_rectangle_y.second;
516 if (near_left && near_top) {
518 } else if (near_left && near_bottom) {
520 } else if (near_right && near_top) {
522 } else if (near_right && near_bottom) {
524 } else if (near_left && within_y) {
526 } else if (near_right && within_y) {
528 } else if (near_top && within_x) {
530 } else if (near_bottom && within_x) {
532 } else if (within_x && within_y) {
534 } else if (within_x) {
535 return BELOW_OR_ABOVE;
536 } else if (within_y) {
537 return TO_LEFT_OR_RIGHT;
539 return OTHERWISE_OUTSIDE;
544 EditorSummary::set_cursor (Position p)
548 get_window()->set_cursor (*_editor->_cursors->resize_left);
551 get_window()->set_cursor (*_editor->_cursors->resize_top_left);
554 get_window()->set_cursor (*_editor->_cursors->resize_top);
557 get_window()->set_cursor (*_editor->_cursors->resize_top_right);
560 get_window()->set_cursor (*_editor->_cursors->resize_right);
563 get_window()->set_cursor (*_editor->_cursors->resize_bottom_right);
566 get_window()->set_cursor (*_editor->_cursors->resize_bottom);
569 get_window()->set_cursor (*_editor->_cursors->resize_bottom_left);
572 get_window()->set_cursor (*_editor->_cursors->move);
574 case TO_LEFT_OR_RIGHT:
575 get_window()->set_cursor (*_editor->_cursors->expand_left_right);
578 get_window()->set_cursor (*_editor->_cursors->expand_up_down);
581 get_window()->set_cursor ();
587 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
589 pair<double, double> xr = _start_editor_x;
590 pair<double, double> yr = _start_editor_y;
591 double x = _start_editor_x.first;
592 double y = _start_editor_y.first;
594 if (_move_dragging) {
598 /* don't alter x if we clicked outside and above or below the viewbox */
599 if (_start_position == INSIDE || _start_position == TO_LEFT_OR_RIGHT || _start_position == OTHERWISE_OUTSIDE) {
600 x += ev->x - _start_mouse_x;
603 /* don't alter y if we clicked outside and to the left or right of the viewbox */
604 if (_start_position == INSIDE || _start_position == BELOW_OR_ABOVE) {
605 y += ev->y - _start_mouse_y;
617 // set_cursor (_start_position);
619 } else if (_zoom_dragging) {
621 double const dx = ev->x - _start_mouse_x;
622 double const dy = ev->y - _start_mouse_y;
624 if (_zoom_position == LEFT || _zoom_position == LEFT_TOP || _zoom_position == LEFT_BOTTOM) {
626 } else if (_zoom_position == RIGHT || _zoom_position == RIGHT_TOP || _zoom_position == RIGHT_BOTTOM) {
630 if (_zoom_position == TOP || _zoom_position == LEFT_TOP || _zoom_position == RIGHT_TOP) {
632 } else if (_zoom_position == BOTTOM || _zoom_position == LEFT_BOTTOM || _zoom_position == RIGHT_BOTTOM) {
636 set_overlays_dirty ();
637 set_cursor (_zoom_position);
642 set_cursor (get_position (ev->x, ev->y));
650 EditorSummary::on_button_release_event (GdkEventButton*)
652 bool const was_suspended = suspending_editor_updates ();
654 _move_dragging = false;
655 _zoom_dragging = false;
656 _editor->_dragging_playhead = false;
657 _editor->set_follow_playhead (_old_follow_playhead, false);
659 if (was_suspended && _pending_editor_changed) {
660 set_editor (_pending_editor_x, _pending_editor_y);
667 EditorSummary::on_scroll_event (GdkEventScroll* ev)
671 pair<double, double> xr;
672 pair<double, double> yr;
673 get_editor (&xr, &yr);
677 switch (ev->direction) {
679 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
681 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
682 _editor->temporal_zoom_step (false);
683 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
692 case GDK_SCROLL_DOWN:
693 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
695 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
696 _editor->temporal_zoom_step (true);
697 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
706 case GDK_SCROLL_LEFT:
707 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
709 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
715 case GDK_SCROLL_RIGHT:
716 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
718 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
732 /** Set the editor to display a x range with the left at a given position
733 * and a y range with the top at a given position.
734 * x and y parameters are specified in summary coordinates.
735 * Zoom is not changed in either direction.
738 EditorSummary::set_editor (double const x, double const y)
740 if (_editor->pending_visual_change.idle_handler_id >= 0 && _editor->pending_visual_change.being_handled == true) {
742 /* As a side-effect, the Editor's visual change idle handler processes
743 pending GTK events. Hence this motion notify handler can be called
744 in the middle of a visual change idle handler, and if this happens,
745 the queue_visual_change calls below modify the variables that the
746 idle handler is working with. This causes problems. Hence this
747 check. It ensures that we won't modify the pending visual change
748 while a visual change idle handler is in progress. It's not perfect,
749 as it also means that we won't change these variables if an idle handler
750 is merely pending but not executing. But c'est la vie.
760 /** Set the editor to display a given x range and a y range with the top at a given position.
761 * The editor's x zoom is adjusted if necessary, but the y zoom is not changed.
762 * x and y parameters are specified in summary coordinates.
765 EditorSummary::set_editor (pair<double,double> const x, double const y)
767 if (_editor->pending_visual_change.idle_handler_id >= 0) {
768 /* see comment in other set_editor () */
776 /** Set the editor to display given x and y ranges. x zoom and track heights are
777 * adjusted if necessary.
778 * x and y parameters are specified in summary coordinates.
781 EditorSummary::set_editor (pair<double,double> const x, pair<double, double> const y)
783 if (_editor->pending_visual_change.idle_handler_id >= 0) {
784 /* see comment in other set_editor () */
792 /** Set the left of the x range visible in the editor.
793 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
794 * @param x new x left position in summary coordinates.
797 EditorSummary::set_editor_x (double x)
803 if (suspending_editor_updates ()) {
804 double const w = _pending_editor_x.second - _pending_editor_x.first;
805 _pending_editor_x.first = x;
806 _pending_editor_x.second = x + w;
807 _pending_editor_changed = true;
810 _editor->reset_x_origin (x / _x_scale + _start);
814 /** Set 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 range in summary coordinates.
819 EditorSummary::set_editor_x (pair<double, double> x)
826 x.second = x.first + 1;
829 if (suspending_editor_updates ()) {
830 _pending_editor_x = x;
831 _pending_editor_changed = true;
834 _editor->reset_x_origin (x.first / _x_scale + _start);
837 ((x.second - x.first) / _x_scale) /
838 _editor->sample_to_pixel (_editor->current_page_samples())
841 if (nx != _editor->get_current_zoom ()) {
842 _editor->reset_zoom (nx);
847 /** Set the top of the y range visible in the editor.
848 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
849 * @param y new editor top in summary coodinates.
852 EditorSummary::set_editor_y (double const y)
854 double y1 = summary_y_to_editor (y);
855 double const eh = _editor->visible_canvas_height();
858 double const full_editor_height = _editor->_full_canvas_height;
860 if (y2 > full_editor_height) {
861 y1 -= y2 - full_editor_height;
868 if (suspending_editor_updates ()) {
869 double const h = _pending_editor_y.second - _pending_editor_y.first;
870 _pending_editor_y.first = y;
871 _pending_editor_y.second = y + h;
872 _pending_editor_changed = true;
875 _editor->reset_y_origin (y1);
879 /** Set the y range visible in the editor. This is achieved by scaling track heights,
881 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
882 * @param y new editor range in summary coodinates.
885 EditorSummary::set_editor_y (pair<double, double> const y)
887 if (suspending_editor_updates ()) {
888 _pending_editor_y = y;
889 _pending_editor_changed = true;
894 /* Compute current height of tracks between y.first and y.second. We add up
895 the total height into `total_height' and the height of complete tracks into
899 /* Copy of target range for use below */
900 pair<double, double> yc = y;
901 /* Total height of all tracks */
902 double total_height = 0;
903 /* Height of any parts of tracks that aren't fully in the desired range */
904 double partial_height = 0;
905 /* Height of any tracks that are fully in the desired range */
906 double scale_height = 0;
908 _editor->_routes->suspend_redisplay ();
910 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
912 if ((*i)->hidden()) {
916 double const h = (*i)->effective_height ();
919 if (yc.first > 0 && yc.first < _track_height) {
920 partial_height += (_track_height - yc.first) * h / _track_height;
921 } else if (yc.first <= 0 && yc.second >= _track_height) {
923 } else if (yc.second > 0 && yc.second < _track_height) {
924 partial_height += yc.second * h / _track_height;
928 yc.first -= _track_height;
929 yc.second -= _track_height;
932 /* Height that we will use for scaling; use the whole editor height unless there are not
933 enough tracks to fill it.
935 double const ch = min (total_height, _editor->visible_canvas_height());
937 /* hence required scale factor of the complete tracks to fit the required y range;
938 the amount of space they should take up divided by the amount they currently take up.
940 double const scale = (ch - partial_height) / scale_height;
944 /* Scale complete tracks within the range to make it fit */
946 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
948 if ((*i)->hidden()) {
952 if (yc.first <= 0 && yc.second >= _track_height) {
953 (*i)->set_height (max (TimeAxisView::preset_height (HeightSmall), (uint32_t) ((*i)->effective_height() * scale)));
956 yc.first -= _track_height;
957 yc.second -= _track_height;
960 _editor->_routes->resume_redisplay ();
962 set_editor_y (y.first);
966 EditorSummary::playhead_position_changed (framepos_t p)
968 int const o = int (_last_playhead);
969 int const n = int (playhead_frame_to_position (p));
970 if (_session && o != n) {
971 int a = max(2, min (o, n));
973 set_overlays_dirty (a - 2, 0, b + 2, get_height ());
978 EditorSummary::summary_y_to_editor (double y) const
981 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
983 if ((*i)->hidden()) {
987 double const h = (*i)->effective_height ();
988 if (y < _track_height) {
990 return ey + y * h / _track_height;
1001 EditorSummary::editor_y_to_summary (double y) const
1004 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
1006 if ((*i)->hidden()) {
1010 double const h = (*i)->effective_height ();
1013 return sy + y * _track_height / h;
1016 sy += _track_height;
1024 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
1026 for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
1027 /* Connect to gui_changed() on the route so that we know when their colour has changed */
1028 (*i)->route()->gui_changed.connect (*this, invalidator (*this), boost::bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
1029 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> ((*i)->route ());
1031 tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context ());
1035 _background_dirty = true;
1040 EditorSummary::route_gui_changed (string c)
1043 _background_dirty = true;
1049 EditorSummary::playhead_frame_to_position (framepos_t t) const
1051 return (t - _start) * _x_scale;
1055 EditorSummary::position_to_playhead_frame_to_position (double pos) const
1057 return _start + (pos * _x_scale);