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 ()
108 free (cairo_image_surface_get_data (_image));
110 stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, get_width ());
111 data = (unsigned char*) malloc (stride * get_height ());
112 _image = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, get_width (), get_height (), stride);
114 cairo_t* cr = cairo_create (_image);
116 /* background (really just the dividing lines between tracks */
118 cairo_set_source_rgb (cr, 0, 0, 0);
119 cairo_rectangle (cr, 0, 0, get_width(), get_height());
122 /* compute start and end points for the summary */
124 framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
125 double const theoretical_start = _session->current_start_frame() - session_length * _overhang_fraction;
126 _start = theoretical_start > 0 ? theoretical_start : 0;
127 _end = _session->current_end_frame() + session_length * _overhang_fraction;
129 /* compute track height */
131 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
132 if (!(*i)->hidden()) {
140 _track_height = (double) get_height() / N;
143 /* calculate x scale */
144 if (_end != _start) {
145 _x_scale = static_cast<double> (get_width()) / (_end - _start);
150 /* render tracks and regions */
153 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
155 if ((*i)->hidden()) {
159 /* paint a non-bg colored strip to represent the track itself */
161 cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
162 cairo_set_line_width (cr, _track_height - 1);
163 cairo_move_to (cr, 0, y + _track_height / 2);
164 cairo_line_to (cr, get_width(), y + _track_height / 2);
167 StreamView* s = (*i)->view ();
170 cairo_set_line_width (cr, _track_height * 0.6);
172 s->foreach_regionview (sigc::bind (
173 sigc::mem_fun (*this, &EditorSummary::render_region),
175 y + _track_height / 2
182 /* start and end markers */
184 cairo_set_line_width (cr, 1);
185 cairo_set_source_rgb (cr, 1, 1, 0);
187 const double p = (_session->current_start_frame() - _start) * _x_scale;
188 cairo_move_to (cr, p, 0);
189 cairo_line_to (cr, p, get_height());
191 double const q = (_session->current_end_frame() - _start) * _x_scale;
192 cairo_move_to (cr, q, 0);
193 cairo_line_to (cr, q, get_height());
199 /** Render the required regions to a cairo context.
203 EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
210 if (!_image || _background_dirty) {
211 render_background_image ();
212 _background_dirty = false;
215 cairo_push_group (cr);
217 /* Fill with the background image */
219 cairo_rectangle (cr, 0, 0, get_width(), get_height());
220 cairo_set_source_surface (cr, _image, 0, 0);
223 /* Render the view rectangle. If there is an editor visual pending, don't update
224 the view rectangle now --- wait until the expose event that we'll get after
225 the visual change. This prevents a flicker.
228 if (_editor->pending_visual_change.idle_handler_id < 0) {
229 get_editor (&_view_rectangle_x, &_view_rectangle_y);
232 int32_t width = _view_rectangle_x.second - _view_rectangle_x.first;
233 int32_t height = _view_rectangle_y.second - _view_rectangle_y.first;
234 cairo_rectangle (cr, _view_rectangle_x.first, _view_rectangle_y.first, width, height);
235 cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
236 cairo_fill_preserve (cr);
237 cairo_set_line_width (cr, 1);
238 cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
243 cairo_set_line_width (cr, 1);
244 /* XXX: colour should be set from configuration file */
245 cairo_set_source_rgba (cr, 1, 0, 0, 1);
247 const double ph= playhead_frame_to_position (_editor->playhead_cursor->current_frame());
248 cairo_move_to (cr, ph, 0);
249 cairo_line_to (cr, ph, get_height());
251 cairo_pop_group_to_source (cr);
257 /** Render a region for the summary.
258 * @param r Region view.
259 * @param cr Cairo context.
260 * @param y y coordinate to render at.
263 EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const
265 uint32_t const c = r->get_fill_color ();
266 cairo_set_source_rgb (cr, UINT_RGBA_R (c) / 255.0, UINT_RGBA_G (c) / 255.0, UINT_RGBA_B (c) / 255.0);
268 if (r->region()->position() > _start) {
269 cairo_move_to (cr, (r->region()->position() - _start) * _x_scale, y);
271 cairo_move_to (cr, 0, y);
274 if ((r->region()->position() + r->region()->length()) > _start) {
275 cairo_line_to (cr, ((r->region()->position() - _start + r->region()->length())) * _x_scale, y);
277 cairo_line_to (cr, 0, y);
284 EditorSummary::set_background_dirty ()
286 _background_dirty = true;
290 /** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
292 EditorSummary::set_overlays_dirty ()
294 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
298 /** Set the summary so that just the overlays (viewbox, playhead etc.) in a given area will be re-rendered */
300 EditorSummary::set_overlays_dirty (int x, int y, int w, int h)
302 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
303 queue_draw_area (x, y, w, h);
307 /** Handle a size request.
308 * @param req GTK requisition
311 EditorSummary::on_size_request (Gtk::Requisition *req)
313 /* Use a dummy, small width and the actual height that we want */
320 EditorSummary::centre_on_click (GdkEventButton* ev)
322 pair<double, double> xr;
323 pair<double, double> yr;
324 get_editor (&xr, &yr);
326 double const w = xr.second - xr.first;
327 double ex = ev->x - w / 2;
330 } else if ((ex + w) > get_width()) {
331 ex = get_width() - w;
334 double const h = yr.second - yr.first;
335 double ey = ev->y - h / 2;
338 } else if ((ey + h) > get_height()) {
339 ey = get_height() - h;
346 EditorSummary::on_enter_notify_event (GdkEventCrossing*)
349 Keyboard::magic_widget_grab_focus ();
354 EditorSummary::on_leave_notify_event (GdkEventCrossing*)
356 /* there are no inferior/child windows, so any leave event means that
359 Keyboard::magic_widget_drop_focus ();
364 EditorSummary::on_key_press_event (GdkEventKey* key)
367 GtkAccelKey set_playhead_accel;
368 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
369 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
372 _session->request_locate (_start + (framepos_t) x / _x_scale, _session->transport_rolling());
382 EditorSummary::on_key_release_event (GdkEventKey* key)
385 GtkAccelKey set_playhead_accel;
386 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
387 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
394 /** Handle a button press.
395 * @param ev GTK event.
398 EditorSummary::on_button_press_event (GdkEventButton* ev)
400 _old_follow_playhead = _editor->follow_playhead ();
402 if (ev->button == 1) {
404 pair<double, double> xr;
405 pair<double, double> yr;
406 get_editor (&xr, &yr);
408 _start_editor_x = xr;
409 _start_editor_y = yr;
410 _start_mouse_x = ev->x;
411 _start_mouse_y = ev->y;
412 _start_position = get_position (ev->x, ev->y);
414 if (_start_position != INSIDE && _start_position != BELOW_OR_ABOVE &&
415 _start_position != TO_LEFT_OR_RIGHT && _start_position != OTHERWISE_OUTSIDE
418 /* start a zoom drag */
420 _zoom_position = get_position (ev->x, ev->y);
421 _zoom_dragging = true;
422 _editor->_dragging_playhead = true;
423 _editor->set_follow_playhead (false);
425 if (suspending_editor_updates ()) {
426 get_editor (&_pending_editor_x, &_pending_editor_y);
427 _pending_editor_changed = false;
430 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
432 /* secondary-modifier-click: locate playhead */
434 _session->request_locate (ev->x / _x_scale + _start);
437 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
439 centre_on_click (ev);
443 /* start a move drag */
445 /* get the editor's state in case we are suspending updates */
446 get_editor (&_pending_editor_x, &_pending_editor_y);
447 _pending_editor_changed = false;
449 _move_dragging = true;
451 _editor->_dragging_playhead = true;
452 _editor->set_follow_playhead (false);
454 ArdourCanvas::checkpoint ("sum", "------------------ summary move drag starts.\n");
461 /** @return true if we are currently suspending updates to the editor's viewport,
462 * which we do if configured to do so, and if in a drag of some kind.
465 EditorSummary::suspending_editor_updates () const
467 return (!Config->get_update_editor_during_summary_drag () && (_zoom_dragging || _move_dragging));
470 /** Fill in x and y with the editor's current viewable area in summary coordinates */
472 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
477 if (suspending_editor_updates ()) {
479 /* We are dragging, and configured not to update the editor window during drags,
480 so just return where the editor will be when the drag finishes.
483 *x = _pending_editor_x;
484 *y = _pending_editor_y;
488 /* Otherwise query the editor for its actual position */
490 x->first = (_editor->leftmost_sample () - _start) * _x_scale;
491 x->second = x->first + _editor->current_page_samples() * _x_scale;
493 y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
494 y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->visible_canvas_height());
498 /** Get an expression of the position of a point with respect to the view rectangle */
499 EditorSummary::Position
500 EditorSummary::get_position (double x, double y) const
502 /* how close the mouse has to be to the edge of the view rectangle to be considered `on it',
505 int x_edge_size = (_view_rectangle_x.second - _view_rectangle_x.first) / 4;
506 x_edge_size = min (x_edge_size, 8);
507 x_edge_size = max (x_edge_size, 1);
509 int y_edge_size = (_view_rectangle_y.second - _view_rectangle_y.first) / 4;
510 y_edge_size = min (y_edge_size, 8);
511 y_edge_size = max (y_edge_size, 1);
513 bool const near_left = (std::abs (x - _view_rectangle_x.first) < x_edge_size);
514 bool const near_right = (std::abs (x - _view_rectangle_x.second) < x_edge_size);
515 bool const near_top = (std::abs (y - _view_rectangle_y.first) < y_edge_size);
516 bool const near_bottom = (std::abs (y - _view_rectangle_y.second) < y_edge_size);
517 bool const within_x = _view_rectangle_x.first < x && x < _view_rectangle_x.second;
518 bool const within_y = _view_rectangle_y.first < y && y < _view_rectangle_y.second;
520 if (near_left && near_top) {
522 } else if (near_left && near_bottom) {
524 } else if (near_right && near_top) {
526 } else if (near_right && near_bottom) {
528 } else if (near_left && within_y) {
530 } else if (near_right && within_y) {
532 } else if (near_top && within_x) {
534 } else if (near_bottom && within_x) {
536 } else if (within_x && within_y) {
538 } else if (within_x) {
539 return BELOW_OR_ABOVE;
540 } else if (within_y) {
541 return TO_LEFT_OR_RIGHT;
543 return OTHERWISE_OUTSIDE;
548 EditorSummary::set_cursor (Position p)
552 get_window()->set_cursor (*_editor->_cursors->resize_left);
555 get_window()->set_cursor (*_editor->_cursors->resize_top_left);
558 get_window()->set_cursor (*_editor->_cursors->resize_top);
561 get_window()->set_cursor (*_editor->_cursors->resize_top_right);
564 get_window()->set_cursor (*_editor->_cursors->resize_right);
567 get_window()->set_cursor (*_editor->_cursors->resize_bottom_right);
570 get_window()->set_cursor (*_editor->_cursors->resize_bottom);
573 get_window()->set_cursor (*_editor->_cursors->resize_bottom_left);
576 get_window()->set_cursor (*_editor->_cursors->move);
578 case TO_LEFT_OR_RIGHT:
579 get_window()->set_cursor (*_editor->_cursors->expand_left_right);
582 get_window()->set_cursor (*_editor->_cursors->expand_up_down);
585 get_window()->set_cursor ();
591 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
593 pair<double, double> xr = _start_editor_x;
594 pair<double, double> yr = _start_editor_y;
595 double x = _start_editor_x.first;
596 double y = _start_editor_y.first;
598 if (_move_dragging) {
602 /* don't alter x if we clicked outside and above or below the viewbox */
603 if (_start_position == INSIDE || _start_position == TO_LEFT_OR_RIGHT || _start_position == OTHERWISE_OUTSIDE) {
604 x += ev->x - _start_mouse_x;
607 /* don't alter y if we clicked outside and to the left or right of the viewbox */
608 if (_start_position == INSIDE || _start_position == BELOW_OR_ABOVE) {
609 y += ev->y - _start_mouse_y;
621 // set_cursor (_start_position);
623 } else if (_zoom_dragging) {
625 double const dx = ev->x - _start_mouse_x;
626 double const dy = ev->y - _start_mouse_y;
628 if (_zoom_position == LEFT || _zoom_position == LEFT_TOP || _zoom_position == LEFT_BOTTOM) {
630 } else if (_zoom_position == RIGHT || _zoom_position == RIGHT_TOP || _zoom_position == RIGHT_BOTTOM) {
634 if (_zoom_position == TOP || _zoom_position == LEFT_TOP || _zoom_position == RIGHT_TOP) {
636 } else if (_zoom_position == BOTTOM || _zoom_position == LEFT_BOTTOM || _zoom_position == RIGHT_BOTTOM) {
640 set_overlays_dirty ();
641 set_cursor (_zoom_position);
646 set_cursor (get_position (ev->x, ev->y));
654 EditorSummary::on_button_release_event (GdkEventButton*)
656 bool const was_suspended = suspending_editor_updates ();
658 _move_dragging = false;
659 _zoom_dragging = false;
660 _editor->_dragging_playhead = false;
661 _editor->set_follow_playhead (_old_follow_playhead, false);
663 if (was_suspended && _pending_editor_changed) {
664 set_editor (_pending_editor_x, _pending_editor_y);
671 EditorSummary::on_scroll_event (GdkEventScroll* ev)
675 pair<double, double> xr;
676 pair<double, double> yr;
677 get_editor (&xr, &yr);
681 switch (ev->direction) {
683 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
685 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
686 _editor->temporal_zoom_step (false);
687 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
696 case GDK_SCROLL_DOWN:
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 (true);
701 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
710 case GDK_SCROLL_LEFT:
711 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
713 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
719 case GDK_SCROLL_RIGHT:
720 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
722 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
736 /** Set the editor to display a x range with the left at a given position
737 * and a y range with the top at a given position.
738 * x and y parameters are specified in summary coordinates.
739 * Zoom is not changed in either direction.
742 EditorSummary::set_editor (double const x, double const y)
744 if (_editor->pending_visual_change.idle_handler_id >= 0 && _editor->pending_visual_change.being_handled == true) {
746 /* As a side-effect, the Editor's visual change idle handler processes
747 pending GTK events. Hence this motion notify handler can be called
748 in the middle of a visual change idle handler, and if this happens,
749 the queue_visual_change calls below modify the variables that the
750 idle handler is working with. This causes problems. Hence this
751 check. It ensures that we won't modify the pending visual change
752 while a visual change idle handler is in progress. It's not perfect,
753 as it also means that we won't change these variables if an idle handler
754 is merely pending but not executing. But c'est la vie.
764 /** Set the editor to display a given x range and a y range with the top at a given position.
765 * The editor's x zoom is adjusted if necessary, but the y zoom is not changed.
766 * x and y parameters are specified in summary coordinates.
769 EditorSummary::set_editor (pair<double,double> const x, double const y)
771 if (_editor->pending_visual_change.idle_handler_id >= 0) {
772 /* see comment in other set_editor () */
780 /** Set the editor to display given x and y ranges. x zoom and track heights are
781 * adjusted if necessary.
782 * x and y parameters are specified in summary coordinates.
785 EditorSummary::set_editor (pair<double,double> const x, pair<double, double> const y)
787 if (_editor->pending_visual_change.idle_handler_id >= 0) {
788 /* see comment in other set_editor () */
796 /** Set the left of the x range visible in the editor.
797 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
798 * @param x new x left position in summary coordinates.
801 EditorSummary::set_editor_x (double x)
807 if (suspending_editor_updates ()) {
808 double const w = _pending_editor_x.second - _pending_editor_x.first;
809 _pending_editor_x.first = x;
810 _pending_editor_x.second = x + w;
811 _pending_editor_changed = true;
814 _editor->reset_x_origin (x / _x_scale + _start);
818 /** Set the x range visible in the editor.
819 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
820 * @param x new x range in summary coordinates.
823 EditorSummary::set_editor_x (pair<double, double> x)
830 x.second = x.first + 1;
833 if (suspending_editor_updates ()) {
834 _pending_editor_x = x;
835 _pending_editor_changed = true;
838 _editor->reset_x_origin (x.first / _x_scale + _start);
841 ((x.second - x.first) / _x_scale) /
842 _editor->sample_to_pixel (_editor->current_page_samples())
845 if (nx != _editor->get_current_zoom ()) {
846 _editor->reset_zoom (nx);
851 /** Set the top of the y range visible in the editor.
852 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
853 * @param y new editor top in summary coodinates.
856 EditorSummary::set_editor_y (double const y)
858 double y1 = summary_y_to_editor (y);
859 double const eh = _editor->visible_canvas_height();
862 double const full_editor_height = _editor->_full_canvas_height;
864 if (y2 > full_editor_height) {
865 y1 -= y2 - full_editor_height;
872 if (suspending_editor_updates ()) {
873 double const h = _pending_editor_y.second - _pending_editor_y.first;
874 _pending_editor_y.first = y;
875 _pending_editor_y.second = y + h;
876 _pending_editor_changed = true;
879 _editor->reset_y_origin (y1);
883 /** Set the y range visible in the editor. This is achieved by scaling track heights,
885 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
886 * @param y new editor range in summary coodinates.
889 EditorSummary::set_editor_y (pair<double, double> const y)
891 if (suspending_editor_updates ()) {
892 _pending_editor_y = y;
893 _pending_editor_changed = true;
898 /* Compute current height of tracks between y.first and y.second. We add up
899 the total height into `total_height' and the height of complete tracks into
903 /* Copy of target range for use below */
904 pair<double, double> yc = y;
905 /* Total height of all tracks */
906 double total_height = 0;
907 /* Height of any parts of tracks that aren't fully in the desired range */
908 double partial_height = 0;
909 /* Height of any tracks that are fully in the desired range */
910 double scale_height = 0;
912 _editor->_routes->suspend_redisplay ();
914 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
916 if ((*i)->hidden()) {
920 double const h = (*i)->effective_height ();
923 if (yc.first > 0 && yc.first < _track_height) {
924 partial_height += (_track_height - yc.first) * h / _track_height;
925 } else if (yc.first <= 0 && yc.second >= _track_height) {
927 } else if (yc.second > 0 && yc.second < _track_height) {
928 partial_height += yc.second * h / _track_height;
932 yc.first -= _track_height;
933 yc.second -= _track_height;
936 /* Height that we will use for scaling; use the whole editor height unless there are not
937 enough tracks to fill it.
939 double const ch = min (total_height, _editor->visible_canvas_height());
941 /* hence required scale factor of the complete tracks to fit the required y range;
942 the amount of space they should take up divided by the amount they currently take up.
944 double const scale = (ch - partial_height) / scale_height;
948 /* Scale complete tracks within the range to make it fit */
950 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
952 if ((*i)->hidden()) {
956 if (yc.first <= 0 && yc.second >= _track_height) {
957 (*i)->set_height (max (TimeAxisView::preset_height (HeightSmall), (uint32_t) ((*i)->effective_height() * scale)));
960 yc.first -= _track_height;
961 yc.second -= _track_height;
964 _editor->_routes->resume_redisplay ();
966 set_editor_y (y.first);
970 EditorSummary::playhead_position_changed (framepos_t p)
972 int const o = int (_last_playhead);
973 int const n = int (playhead_frame_to_position (p));
974 if (_session && o != n) {
975 int a = max(2, min (o, n));
977 set_overlays_dirty (a - 2, 0, b + 2, get_height ());
982 EditorSummary::summary_y_to_editor (double y) const
985 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
987 if ((*i)->hidden()) {
991 double const h = (*i)->effective_height ();
992 if (y < _track_height) {
994 return ey + y * h / _track_height;
1005 EditorSummary::editor_y_to_summary (double y) const
1008 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
1010 if ((*i)->hidden()) {
1014 double const h = (*i)->effective_height ();
1017 return sy + y * _track_height / h;
1020 sy += _track_height;
1028 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
1030 for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
1031 /* Connect to gui_changed() on the route so that we know when their colour has changed */
1032 (*i)->route()->gui_changed.connect (*this, invalidator (*this), boost::bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
1033 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> ((*i)->route ());
1035 tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context ());
1039 _background_dirty = true;
1044 EditorSummary::route_gui_changed (string c)
1047 _background_dirty = true;
1053 EditorSummary::playhead_frame_to_position (framepos_t t) const
1055 return (t - _start) * _x_scale;
1059 EditorSummary::position_to_playhead_frame_to_position (double pos) const
1061 return _start + (pos * _x_scale);