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 /** Handle a size allocation.
66 * @param alloc GTK allocation.
69 EditorSummary::on_size_allocate (Gtk::Allocation& alloc)
71 Gtk::EventBox::on_size_allocate (alloc);
72 _background_dirty = true;
77 /** Connect to a session.
81 EditorSummary::set_session (Session* s)
83 SessionHandlePtr::set_session (s);
87 /* Note: the EditorSummary already finds out about new regions from Editor::region_view_added
88 * (which attaches to StreamView::RegionViewAdded), and cut regions by the RegionPropertyChanged
89 * emitted when a cut region is added to the `cutlist' playlist.
93 Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
94 Route::RemoteControlIDChange.connect (route_ctrl_id_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
95 _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), boost::bind (&EditorSummary::playhead_position_changed, this, _1), gui_context());
96 _session->StartTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
97 _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
98 _editor->selection->RegionsChanged.connect (sigc::mem_fun(*this, &EditorSummary::set_background_dirty));
103 EditorSummary::render_background_image ()
107 stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, get_width ());
108 data = (unsigned char*) malloc (stride * get_height ());
109 _image = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, get_width (), get_height (), stride);
111 cairo_t* cr = cairo_create (_image);
113 /* background (really just the dividing lines between tracks */
115 cairo_set_source_rgb (cr, 0, 0, 0);
116 cairo_rectangle (cr, 0, 0, get_width(), get_height());
119 /* compute start and end points for the summary */
121 framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
122 double const theoretical_start = _session->current_start_frame() - session_length * _overhang_fraction;
123 _start = theoretical_start > 0 ? theoretical_start : 0;
124 _end = _session->current_end_frame() + session_length * _overhang_fraction;
126 /* compute track height */
128 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
129 if (!(*i)->hidden()) {
137 _track_height = (double) get_height() / N;
140 /* calculate x scale */
141 if (_end != _start) {
142 _x_scale = static_cast<double> (get_width()) / (_end - _start);
147 /* render tracks and regions */
150 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
152 if ((*i)->hidden()) {
156 /* paint a non-bg colored strip to represent the track itself */
158 cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
159 cairo_set_line_width (cr, _track_height - 1);
160 cairo_move_to (cr, 0, y + _track_height / 2);
161 cairo_line_to (cr, get_width(), y + _track_height / 2);
164 StreamView* s = (*i)->view ();
167 cairo_set_line_width (cr, _track_height * 0.6);
169 s->foreach_regionview (sigc::bind (
170 sigc::mem_fun (*this, &EditorSummary::render_region),
172 y + _track_height / 2
179 /* start and end markers */
181 cairo_set_line_width (cr, 1);
182 cairo_set_source_rgb (cr, 1, 1, 0);
184 const double p = (_session->current_start_frame() - _start) * _x_scale;
185 cairo_move_to (cr, p, 0);
186 cairo_line_to (cr, p, get_height());
188 double const q = (_session->current_end_frame() - _start) * _x_scale;
189 cairo_move_to (cr, q, 0);
190 cairo_line_to (cr, q, get_height());
196 /** Render the required regions to a cairo context.
200 EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
207 if (!_image || _background_dirty) {
208 render_background_image ();
209 _background_dirty = false;
212 cairo_push_group (cr);
214 /* Fill with the background image */
216 cairo_rectangle (cr, 0, 0, get_width(), get_height());
217 cairo_set_source_surface (cr, _image, 0, 0);
220 /* Render the view rectangle. If there is an editor visual pending, don't update
221 the view rectangle now --- wait until the expose event that we'll get after
222 the visual change. This prevents a flicker.
225 if (_editor->pending_visual_change.idle_handler_id < 0) {
226 get_editor (&_view_rectangle_x, &_view_rectangle_y);
229 int32_t width = _view_rectangle_x.second - _view_rectangle_x.first;
230 int32_t height = _view_rectangle_y.second - _view_rectangle_y.first;
231 cairo_rectangle (cr, _view_rectangle_x.first, _view_rectangle_y.first, width, height);
232 cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
233 cairo_fill_preserve (cr);
234 cairo_set_line_width (cr, 1);
235 cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
240 cairo_set_line_width (cr, 1);
241 /* XXX: colour should be set from configuration file */
242 cairo_set_source_rgba (cr, 1, 0, 0, 1);
244 const double ph= playhead_frame_to_position (_editor->playhead_cursor->current_frame());
245 cairo_move_to (cr, ph, 0);
246 cairo_line_to (cr, ph, get_height());
248 cairo_pop_group_to_source (cr);
254 /** Render a region for the summary.
255 * @param r Region view.
256 * @param cr Cairo context.
257 * @param y y coordinate to render at.
260 EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const
262 uint32_t const c = r->get_fill_color ();
263 cairo_set_source_rgb (cr, UINT_RGBA_R (c) / 255.0, UINT_RGBA_G (c) / 255.0, UINT_RGBA_B (c) / 255.0);
265 if (r->region()->position() > _start) {
266 cairo_move_to (cr, (r->region()->position() - _start) * _x_scale, y);
268 cairo_move_to (cr, 0, y);
271 if ((r->region()->position() + r->region()->length()) > _start) {
272 cairo_line_to (cr, ((r->region()->position() - _start + r->region()->length())) * _x_scale, y);
274 cairo_line_to (cr, 0, y);
281 EditorSummary::set_background_dirty ()
283 _background_dirty = true;
287 /** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
289 EditorSummary::set_overlays_dirty ()
291 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
295 /** Set the summary so that just the overlays (viewbox, playhead etc.) in a given area will be re-rendered */
297 EditorSummary::set_overlays_dirty (int x, int y, int w, int h)
299 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
300 queue_draw_area (x, y, w, h);
304 /** Handle a size request.
305 * @param req GTK requisition
308 EditorSummary::on_size_request (Gtk::Requisition *req)
310 /* Use a dummy, small width and the actual height that we want */
317 EditorSummary::centre_on_click (GdkEventButton* ev)
319 pair<double, double> xr;
320 pair<double, double> yr;
321 get_editor (&xr, &yr);
323 double const w = xr.second - xr.first;
324 double ex = ev->x - w / 2;
327 } else if ((ex + w) > get_width()) {
328 ex = get_width() - w;
331 double const h = yr.second - yr.first;
332 double ey = ev->y - h / 2;
335 } else if ((ey + h) > get_height()) {
336 ey = get_height() - h;
343 EditorSummary::on_enter_notify_event (GdkEventCrossing*)
346 Keyboard::magic_widget_grab_focus ();
351 EditorSummary::on_leave_notify_event (GdkEventCrossing*)
353 /* there are no inferior/child windows, so any leave event means that
356 Keyboard::magic_widget_drop_focus ();
361 EditorSummary::on_key_press_event (GdkEventKey* key)
364 GtkAccelKey set_playhead_accel;
365 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
366 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
369 _session->request_locate (_start + (framepos_t) x / _x_scale, _session->transport_rolling());
379 EditorSummary::on_key_release_event (GdkEventKey* key)
382 GtkAccelKey set_playhead_accel;
383 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
384 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
391 /** Handle a button press.
392 * @param ev GTK event.
395 EditorSummary::on_button_press_event (GdkEventButton* ev)
397 _old_follow_playhead = _editor->follow_playhead ();
399 if (ev->button == 1) {
401 pair<double, double> xr;
402 pair<double, double> yr;
403 get_editor (&xr, &yr);
405 _start_editor_x = xr;
406 _start_editor_y = yr;
407 _start_mouse_x = ev->x;
408 _start_mouse_y = ev->y;
409 _start_position = get_position (ev->x, ev->y);
411 if (_start_position != INSIDE && _start_position != BELOW_OR_ABOVE &&
412 _start_position != TO_LEFT_OR_RIGHT && _start_position != OTHERWISE_OUTSIDE
415 /* start a zoom drag */
417 _zoom_position = get_position (ev->x, ev->y);
418 _zoom_dragging = true;
419 _editor->_dragging_playhead = true;
420 _editor->set_follow_playhead (false);
422 if (suspending_editor_updates ()) {
423 get_editor (&_pending_editor_x, &_pending_editor_y);
424 _pending_editor_changed = false;
427 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
429 /* secondary-modifier-click: locate playhead */
431 _session->request_locate (ev->x / _x_scale + _start);
434 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
436 centre_on_click (ev);
440 /* start a move drag */
442 /* get the editor's state in case we are suspending updates */
443 get_editor (&_pending_editor_x, &_pending_editor_y);
444 _pending_editor_changed = false;
446 _move_dragging = true;
448 _editor->_dragging_playhead = true;
449 _editor->set_follow_playhead (false);
451 ArdourCanvas::checkpoint ("sum", "------------------ summary move drag starts.\n");
458 /** @return true if we are currently suspending updates to the editor's viewport,
459 * which we do if configured to do so, and if in a drag of some kind.
462 EditorSummary::suspending_editor_updates () const
464 return (!Config->get_update_editor_during_summary_drag () && (_zoom_dragging || _move_dragging));
467 /** Fill in x and y with the editor's current viewable area in summary coordinates */
469 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
474 if (suspending_editor_updates ()) {
476 /* We are dragging, and configured not to update the editor window during drags,
477 so just return where the editor will be when the drag finishes.
480 *x = _pending_editor_x;
481 *y = _pending_editor_y;
485 /* Otherwise query the editor for its actual position */
487 x->first = (_editor->leftmost_sample () - _start) * _x_scale;
488 x->second = x->first + _editor->current_page_samples() * _x_scale;
490 y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
491 y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->visible_canvas_height());
495 /** Get an expression of the position of a point with respect to the view rectangle */
496 EditorSummary::Position
497 EditorSummary::get_position (double x, double y) const
499 /* how close the mouse has to be to the edge of the view rectangle to be considered `on it',
502 int x_edge_size = (_view_rectangle_x.second - _view_rectangle_x.first) / 4;
503 x_edge_size = min (x_edge_size, 8);
504 x_edge_size = max (x_edge_size, 1);
506 int y_edge_size = (_view_rectangle_y.second - _view_rectangle_y.first) / 4;
507 y_edge_size = min (y_edge_size, 8);
508 y_edge_size = max (y_edge_size, 1);
510 bool const near_left = (std::abs (x - _view_rectangle_x.first) < x_edge_size);
511 bool const near_right = (std::abs (x - _view_rectangle_x.second) < x_edge_size);
512 bool const near_top = (std::abs (y - _view_rectangle_y.first) < y_edge_size);
513 bool const near_bottom = (std::abs (y - _view_rectangle_y.second) < y_edge_size);
514 bool const within_x = _view_rectangle_x.first < x && x < _view_rectangle_x.second;
515 bool const within_y = _view_rectangle_y.first < y && y < _view_rectangle_y.second;
517 if (near_left && near_top) {
519 } else if (near_left && near_bottom) {
521 } else if (near_right && near_top) {
523 } else if (near_right && near_bottom) {
525 } else if (near_left && within_y) {
527 } else if (near_right && within_y) {
529 } else if (near_top && within_x) {
531 } else if (near_bottom && within_x) {
533 } else if (within_x && within_y) {
535 } else if (within_x) {
536 return BELOW_OR_ABOVE;
537 } else if (within_y) {
538 return TO_LEFT_OR_RIGHT;
540 return OTHERWISE_OUTSIDE;
545 EditorSummary::set_cursor (Position p)
549 get_window()->set_cursor (*_editor->_cursors->resize_left);
552 get_window()->set_cursor (*_editor->_cursors->resize_top_left);
555 get_window()->set_cursor (*_editor->_cursors->resize_top);
558 get_window()->set_cursor (*_editor->_cursors->resize_top_right);
561 get_window()->set_cursor (*_editor->_cursors->resize_right);
564 get_window()->set_cursor (*_editor->_cursors->resize_bottom_right);
567 get_window()->set_cursor (*_editor->_cursors->resize_bottom);
570 get_window()->set_cursor (*_editor->_cursors->resize_bottom_left);
573 get_window()->set_cursor (*_editor->_cursors->move);
575 case TO_LEFT_OR_RIGHT:
576 get_window()->set_cursor (*_editor->_cursors->expand_left_right);
579 get_window()->set_cursor (*_editor->_cursors->expand_up_down);
582 get_window()->set_cursor ();
588 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
590 pair<double, double> xr = _start_editor_x;
591 pair<double, double> yr = _start_editor_y;
592 double x = _start_editor_x.first;
593 double y = _start_editor_y.first;
595 if (_move_dragging) {
599 /* don't alter x if we clicked outside and above or below the viewbox */
600 if (_start_position == INSIDE || _start_position == TO_LEFT_OR_RIGHT || _start_position == OTHERWISE_OUTSIDE) {
601 x += ev->x - _start_mouse_x;
604 /* don't alter y if we clicked outside and to the left or right of the viewbox */
605 if (_start_position == INSIDE || _start_position == BELOW_OR_ABOVE) {
606 y += ev->y - _start_mouse_y;
618 // set_cursor (_start_position);
620 } else if (_zoom_dragging) {
622 double const dx = ev->x - _start_mouse_x;
623 double const dy = ev->y - _start_mouse_y;
625 if (_zoom_position == LEFT || _zoom_position == LEFT_TOP || _zoom_position == LEFT_BOTTOM) {
627 } else if (_zoom_position == RIGHT || _zoom_position == RIGHT_TOP || _zoom_position == RIGHT_BOTTOM) {
631 if (_zoom_position == TOP || _zoom_position == LEFT_TOP || _zoom_position == RIGHT_TOP) {
633 } else if (_zoom_position == BOTTOM || _zoom_position == LEFT_BOTTOM || _zoom_position == RIGHT_BOTTOM) {
637 set_overlays_dirty ();
638 set_cursor (_zoom_position);
643 set_cursor (get_position (ev->x, ev->y));
651 EditorSummary::on_button_release_event (GdkEventButton*)
653 bool const was_suspended = suspending_editor_updates ();
655 _move_dragging = false;
656 _zoom_dragging = false;
657 _editor->_dragging_playhead = false;
658 _editor->set_follow_playhead (_old_follow_playhead, false);
660 if (was_suspended && _pending_editor_changed) {
661 set_editor (_pending_editor_x, _pending_editor_y);
668 EditorSummary::on_scroll_event (GdkEventScroll* ev)
672 pair<double, double> xr;
673 pair<double, double> yr;
674 get_editor (&xr, &yr);
678 switch (ev->direction) {
680 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
682 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
683 _editor->temporal_zoom_step (false);
684 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
693 case GDK_SCROLL_DOWN:
694 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
696 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
697 _editor->temporal_zoom_step (true);
698 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
707 case GDK_SCROLL_LEFT:
708 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
710 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
716 case GDK_SCROLL_RIGHT:
717 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
719 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
733 /** Set the editor to display a x range with the left at a given position
734 * and a y range with the top at a given position.
735 * x and y parameters are specified in summary coordinates.
736 * Zoom is not changed in either direction.
739 EditorSummary::set_editor (double const x, double const y)
741 if (_editor->pending_visual_change.idle_handler_id >= 0 && _editor->pending_visual_change.being_handled == true) {
743 /* As a side-effect, the Editor's visual change idle handler processes
744 pending GTK events. Hence this motion notify handler can be called
745 in the middle of a visual change idle handler, and if this happens,
746 the queue_visual_change calls below modify the variables that the
747 idle handler is working with. This causes problems. Hence this
748 check. It ensures that we won't modify the pending visual change
749 while a visual change idle handler is in progress. It's not perfect,
750 as it also means that we won't change these variables if an idle handler
751 is merely pending but not executing. But c'est la vie.
761 /** Set the editor to display a given x range and a y range with the top at a given position.
762 * The editor's x zoom is adjusted if necessary, but the y zoom is not changed.
763 * x and y parameters are specified in summary coordinates.
766 EditorSummary::set_editor (pair<double,double> const x, double const y)
768 if (_editor->pending_visual_change.idle_handler_id >= 0) {
769 /* see comment in other set_editor () */
777 /** Set the editor to display given x and y ranges. x zoom and track heights are
778 * adjusted if necessary.
779 * x and y parameters are specified in summary coordinates.
782 EditorSummary::set_editor (pair<double,double> const x, pair<double, double> const y)
784 if (_editor->pending_visual_change.idle_handler_id >= 0) {
785 /* see comment in other set_editor () */
793 /** Set the left of the x range visible in the editor.
794 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
795 * @param x new x left position in summary coordinates.
798 EditorSummary::set_editor_x (double x)
804 if (suspending_editor_updates ()) {
805 double const w = _pending_editor_x.second - _pending_editor_x.first;
806 _pending_editor_x.first = x;
807 _pending_editor_x.second = x + w;
808 _pending_editor_changed = true;
811 _editor->reset_x_origin (x / _x_scale + _start);
815 /** Set the x range visible in the editor.
816 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
817 * @param x new x range in summary coordinates.
820 EditorSummary::set_editor_x (pair<double, double> x)
827 x.second = x.first + 1;
830 if (suspending_editor_updates ()) {
831 _pending_editor_x = x;
832 _pending_editor_changed = true;
835 _editor->reset_x_origin (x.first / _x_scale + _start);
838 ((x.second - x.first) / _x_scale) /
839 _editor->sample_to_pixel (_editor->current_page_samples())
842 if (nx != _editor->get_current_zoom ()) {
843 _editor->reset_zoom (nx);
848 /** Set the top of the y range visible in the editor.
849 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
850 * @param y new editor top in summary coodinates.
853 EditorSummary::set_editor_y (double const y)
855 double y1 = summary_y_to_editor (y);
856 double const eh = _editor->visible_canvas_height();
859 double const full_editor_height = _editor->_full_canvas_height;
861 if (y2 > full_editor_height) {
862 y1 -= y2 - full_editor_height;
869 if (suspending_editor_updates ()) {
870 double const h = _pending_editor_y.second - _pending_editor_y.first;
871 _pending_editor_y.first = y;
872 _pending_editor_y.second = y + h;
873 _pending_editor_changed = true;
876 _editor->reset_y_origin (y1);
880 /** Set the y range visible in the editor. This is achieved by scaling track heights,
882 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
883 * @param y new editor range in summary coodinates.
886 EditorSummary::set_editor_y (pair<double, double> const y)
888 if (suspending_editor_updates ()) {
889 _pending_editor_y = y;
890 _pending_editor_changed = true;
895 /* Compute current height of tracks between y.first and y.second. We add up
896 the total height into `total_height' and the height of complete tracks into
900 /* Copy of target range for use below */
901 pair<double, double> yc = y;
902 /* Total height of all tracks */
903 double total_height = 0;
904 /* Height of any parts of tracks that aren't fully in the desired range */
905 double partial_height = 0;
906 /* Height of any tracks that are fully in the desired range */
907 double scale_height = 0;
909 _editor->_routes->suspend_redisplay ();
911 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
913 if ((*i)->hidden()) {
917 double const h = (*i)->effective_height ();
920 if (yc.first > 0 && yc.first < _track_height) {
921 partial_height += (_track_height - yc.first) * h / _track_height;
922 } else if (yc.first <= 0 && yc.second >= _track_height) {
924 } else if (yc.second > 0 && yc.second < _track_height) {
925 partial_height += yc.second * h / _track_height;
929 yc.first -= _track_height;
930 yc.second -= _track_height;
933 /* Height that we will use for scaling; use the whole editor height unless there are not
934 enough tracks to fill it.
936 double const ch = min (total_height, _editor->visible_canvas_height());
938 /* hence required scale factor of the complete tracks to fit the required y range;
939 the amount of space they should take up divided by the amount they currently take up.
941 double const scale = (ch - partial_height) / scale_height;
945 /* Scale complete tracks within the range to make it fit */
947 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
949 if ((*i)->hidden()) {
953 if (yc.first <= 0 && yc.second >= _track_height) {
954 (*i)->set_height (max (TimeAxisView::preset_height (HeightSmall), (uint32_t) ((*i)->effective_height() * scale)));
957 yc.first -= _track_height;
958 yc.second -= _track_height;
961 _editor->_routes->resume_redisplay ();
963 set_editor_y (y.first);
967 EditorSummary::playhead_position_changed (framepos_t p)
969 int const o = int (_last_playhead);
970 int const n = int (playhead_frame_to_position (p));
971 if (_session && o != n) {
972 int a = max(2, min (o, n));
974 set_overlays_dirty (a - 2, 0, b + 2, get_height ());
979 EditorSummary::summary_y_to_editor (double y) const
982 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
984 if ((*i)->hidden()) {
988 double const h = (*i)->effective_height ();
989 if (y < _track_height) {
991 return ey + y * h / _track_height;
1002 EditorSummary::editor_y_to_summary (double y) const
1005 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
1007 if ((*i)->hidden()) {
1011 double const h = (*i)->effective_height ();
1014 return sy + y * _track_height / h;
1017 sy += _track_height;
1025 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
1027 for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
1028 /* Connect to gui_changed() on the route so that we know when their colour has changed */
1029 (*i)->route()->gui_changed.connect (*this, invalidator (*this), boost::bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
1030 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> ((*i)->route ());
1032 tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context ());
1036 _background_dirty = true;
1041 EditorSummary::route_gui_changed (string c)
1044 _background_dirty = true;
1050 EditorSummary::playhead_frame_to_position (framepos_t t) const
1052 return (t - _start) * _x_scale;
1056 EditorSummary::position_to_playhead_frame_to_position (double pos) const
1058 return _start + (pos * _x_scale);