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 CairoWidget::use_nsglview ();
63 add_events (Gdk::POINTER_MOTION_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
64 set_flags (get_flags() | Gtk::CAN_FOCUS);
66 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &EditorSummary::parameter_changed));
69 EditorSummary::~EditorSummary ()
71 cairo_surface_destroy (_image);
75 EditorSummary::parameter_changed (string p)
78 if (p == "color-regions-using-track-color") {
79 set_background_dirty ();
83 /** Handle a size allocation.
84 * @param alloc GTK allocation.
87 EditorSummary::on_size_allocate (Gtk::Allocation& alloc)
89 CairoWidget::on_size_allocate (alloc);
90 set_background_dirty ();
94 /** Connect to a session.
98 EditorSummary::set_session (Session* s)
100 SessionHandlePtr::set_session (s);
104 /* Note: the EditorSummary already finds out about new regions from Editor::region_view_added
105 * (which attaches to StreamView::RegionViewAdded), and cut regions by the RegionPropertyChanged
106 * emitted when a cut region is added to the `cutlist' playlist.
110 Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
111 PresentationInfo::Change.connect (route_ctrl_id_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
112 _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), boost::bind (&EditorSummary::playhead_position_changed, this, _1), gui_context());
113 _session->StartTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
114 _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
115 _editor->selection->RegionsChanged.connect (sigc::mem_fun(*this, &EditorSummary::set_background_dirty));
120 EditorSummary::render_background_image ()
122 cairo_surface_destroy (_image); // passing NULL is safe
123 _image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, get_width (), get_height ());
125 cairo_t* cr = cairo_create (_image);
127 /* background (really just the dividing lines between tracks */
129 cairo_set_source_rgb (cr, 0, 0, 0);
130 cairo_rectangle (cr, 0, 0, get_width(), get_height());
133 /* compute start and end points for the summary */
135 framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
136 double const theoretical_start = _session->current_start_frame() - session_length * _overhang_fraction;
137 _start = theoretical_start > 0 ? theoretical_start : 0;
138 _end = _session->current_end_frame() + session_length * _overhang_fraction;
140 /* compute track height */
142 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
143 if (!(*i)->hidden()) {
151 _track_height = (double) get_height() / N;
154 /* calculate x scale */
155 if (_end != _start) {
156 _x_scale = static_cast<double> (get_width()) / (_end - _start);
161 /* render tracks and regions */
164 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
166 if ((*i)->hidden()) {
170 /* paint a non-bg colored strip to represent the track itself */
172 cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
173 cairo_set_line_width (cr, _track_height - 1);
174 cairo_move_to (cr, 0, y + _track_height / 2);
175 cairo_line_to (cr, get_width(), y + _track_height / 2);
178 StreamView* s = (*i)->view ();
181 cairo_set_line_width (cr, _track_height * 0.8);
183 s->foreach_regionview (sigc::bind (
184 sigc::mem_fun (*this, &EditorSummary::render_region),
186 y + _track_height / 2
193 /* start and end markers */
195 cairo_set_line_width (cr, 1);
196 cairo_set_source_rgb (cr, 1, 1, 0);
198 const double p = (_session->current_start_frame() - _start) * _x_scale;
199 cairo_move_to (cr, p, 0);
200 cairo_line_to (cr, p, get_height());
202 double const q = (_session->current_end_frame() - _start) * _x_scale;
203 cairo_move_to (cr, q, 0);
204 cairo_line_to (cr, q, get_height());
210 /** Render the required regions to a cairo context.
214 EditorSummary::render (Cairo::RefPtr<Cairo::Context> const& ctx, cairo_rectangle_t*)
216 cairo_t* cr = ctx->cobj();
222 if (!_image || _background_dirty) {
223 render_background_image ();
224 _background_dirty = false;
227 cairo_push_group (cr);
229 /* Fill with the background image */
231 cairo_rectangle (cr, 0, 0, get_width(), get_height());
232 cairo_set_source_surface (cr, _image, 0, 0);
235 /* Render the view rectangle. If there is an editor visual pending, don't update
236 the view rectangle now --- wait until the expose event that we'll get after
237 the visual change. This prevents a flicker.
240 if (_editor->pending_visual_change.idle_handler_id < 0) {
241 get_editor (&_view_rectangle_x, &_view_rectangle_y);
244 int32_t width = _view_rectangle_x.second - _view_rectangle_x.first;
245 int32_t height = _view_rectangle_y.second - _view_rectangle_y.first;
246 cairo_rectangle (cr, _view_rectangle_x.first, _view_rectangle_y.first, width, height);
247 cairo_set_source_rgba (cr, 1, 1, 1, 0.1);
248 cairo_fill_preserve (cr);
249 cairo_set_line_width (cr, 1);
250 cairo_set_source_rgba (cr, 1, 1, 1, 0.4);
255 cairo_set_line_width (cr, 1);
256 /* XXX: colour should be set from configuration file */
257 cairo_set_source_rgba (cr, 1, 0, 0, 1);
259 const double ph= playhead_frame_to_position (_editor->playhead_cursor->current_frame());
260 cairo_move_to (cr, ph, 0);
261 cairo_line_to (cr, ph, get_height());
263 cairo_pop_group_to_source (cr);
269 /** Render a region for the summary.
270 * @param r Region view.
271 * @param cr Cairo context.
272 * @param y y coordinate to render at.
275 EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const
277 uint32_t const c = r->get_fill_color ();
278 cairo_set_source_rgb (cr, UINT_RGBA_R (c) / 255.0, UINT_RGBA_G (c) / 255.0, UINT_RGBA_B (c) / 255.0);
280 if (r->region()->position() > _start) {
281 cairo_move_to (cr, (r->region()->position() - _start) * _x_scale, y);
283 cairo_move_to (cr, 0, y);
286 if ((r->region()->position() + r->region()->length()) > _start) {
287 cairo_line_to (cr, ((r->region()->position() - _start + r->region()->length())) * _x_scale, y);
289 cairo_line_to (cr, 0, y);
296 EditorSummary::set_background_dirty ()
298 if (!_background_dirty) {
299 _background_dirty = true;
304 /** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
306 EditorSummary::set_overlays_dirty ()
308 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
312 /** Set the summary so that just the overlays (viewbox, playhead etc.) in a given area will be re-rendered */
314 EditorSummary::set_overlays_dirty (int x, int y, int w, int h)
316 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
317 queue_draw_area (x, y, w, h);
321 /** Handle a size request.
322 * @param req GTK requisition
325 EditorSummary::on_size_request (Gtk::Requisition *req)
327 /* Use a dummy, small width and the actual height that we want */
334 EditorSummary::centre_on_click (GdkEventButton* ev)
336 pair<double, double> xr;
337 pair<double, double> yr;
338 get_editor (&xr, &yr);
340 double const w = xr.second - xr.first;
341 double ex = ev->x - w / 2;
344 } else if ((ex + w) > get_width()) {
345 ex = get_width() - w;
348 double const h = yr.second - yr.first;
349 double ey = ev->y - h / 2;
352 } else if ((ey + h) > get_height()) {
353 ey = get_height() - h;
360 EditorSummary::on_enter_notify_event (GdkEventCrossing*)
363 Keyboard::magic_widget_grab_focus ();
368 EditorSummary::on_leave_notify_event (GdkEventCrossing*)
370 /* there are no inferior/child windows, so any leave event means that
373 Keyboard::magic_widget_drop_focus ();
378 EditorSummary::on_key_press_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) {
386 _session->request_locate (_start + (framepos_t) x / _x_scale, _session->transport_rolling());
396 EditorSummary::on_key_release_event (GdkEventKey* key)
399 GtkAccelKey set_playhead_accel;
400 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
401 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
408 /** Handle a button press.
409 * @param ev GTK event.
412 EditorSummary::on_button_press_event (GdkEventButton* ev)
414 _old_follow_playhead = _editor->follow_playhead ();
416 if (ev->button == 1) {
418 pair<double, double> xr;
419 pair<double, double> yr;
420 get_editor (&xr, &yr);
422 _start_editor_x = xr;
423 _start_editor_y = yr;
424 _start_mouse_x = ev->x;
425 _start_mouse_y = ev->y;
426 _start_position = get_position (ev->x, ev->y);
428 if (_start_position != INSIDE && _start_position != BELOW_OR_ABOVE &&
429 _start_position != TO_LEFT_OR_RIGHT && _start_position != OTHERWISE_OUTSIDE
432 /* start a zoom drag */
434 _zoom_position = get_position (ev->x, ev->y);
435 _zoom_dragging = true;
436 _editor->_dragging_playhead = true;
437 _editor->set_follow_playhead (false);
439 if (suspending_editor_updates ()) {
440 get_editor (&_pending_editor_x, &_pending_editor_y);
441 _pending_editor_changed = false;
444 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
446 /* secondary-modifier-click: locate playhead */
448 _session->request_locate (ev->x / _x_scale + _start);
451 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
453 centre_on_click (ev);
457 /* start a move drag */
459 /* get the editor's state in case we are suspending updates */
460 get_editor (&_pending_editor_x, &_pending_editor_y);
461 _pending_editor_changed = false;
463 _move_dragging = true;
465 _editor->_dragging_playhead = true;
466 _editor->set_follow_playhead (false);
468 ArdourCanvas::checkpoint ("sum", "------------------ summary move drag starts.\n");
475 /** @return true if we are currently suspending updates to the editor's viewport,
476 * which we do if configured to do so, and if in a drag of some kind.
479 EditorSummary::suspending_editor_updates () const
481 return (!UIConfiguration::instance().get_update_editor_during_summary_drag () && (_zoom_dragging || _move_dragging));
484 /** Fill in x and y with the editor's current viewable area in summary coordinates */
486 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
491 if (suspending_editor_updates ()) {
493 /* We are dragging, and configured not to update the editor window during drags,
494 so just return where the editor will be when the drag finishes.
497 *x = _pending_editor_x;
498 *y = _pending_editor_y;
502 /* Otherwise query the editor for its actual position */
504 x->first = (_editor->leftmost_sample () - _start) * _x_scale;
505 x->second = x->first + _editor->current_page_samples() * _x_scale;
507 y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
508 y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y);
512 /** Get an expression of the position of a point with respect to the view rectangle */
513 EditorSummary::Position
514 EditorSummary::get_position (double x, double y) const
516 /* how close the mouse has to be to the edge of the view rectangle to be considered `on it',
519 int x_edge_size = (_view_rectangle_x.second - _view_rectangle_x.first) / 4;
520 x_edge_size = min (x_edge_size, 8);
521 x_edge_size = max (x_edge_size, 1);
523 int y_edge_size = (_view_rectangle_y.second - _view_rectangle_y.first) / 4;
524 y_edge_size = min (y_edge_size, 8);
525 y_edge_size = max (y_edge_size, 1);
527 bool const near_left = (std::abs (x - _view_rectangle_x.first) < x_edge_size);
528 bool const near_right = (std::abs (x - _view_rectangle_x.second) < x_edge_size);
529 bool const near_top = (std::abs (y - _view_rectangle_y.first) < y_edge_size);
530 bool const near_bottom = (std::abs (y - _view_rectangle_y.second) < y_edge_size);
531 bool const within_x = _view_rectangle_x.first < x && x < _view_rectangle_x.second;
532 bool const within_y = _view_rectangle_y.first < y && y < _view_rectangle_y.second;
534 if (near_left && near_top) {
536 } else if (near_left && near_bottom) {
538 } else if (near_right && near_top) {
540 } else if (near_right && near_bottom) {
542 } else if (near_left && within_y) {
544 } else if (near_right && within_y) {
546 } else if (near_top && within_x) {
548 } else if (near_bottom && within_x) {
550 } else if (within_x && within_y) {
552 } else if (within_x) {
553 return BELOW_OR_ABOVE;
554 } else if (within_y) {
555 return TO_LEFT_OR_RIGHT;
557 return OTHERWISE_OUTSIDE;
562 EditorSummary::set_cursor (Position p)
566 get_window()->set_cursor (*_editor->_cursors->resize_left);
569 get_window()->set_cursor (*_editor->_cursors->resize_top_left);
572 get_window()->set_cursor (*_editor->_cursors->resize_top);
575 get_window()->set_cursor (*_editor->_cursors->resize_top_right);
578 get_window()->set_cursor (*_editor->_cursors->resize_right);
581 get_window()->set_cursor (*_editor->_cursors->resize_bottom_right);
584 get_window()->set_cursor (*_editor->_cursors->resize_bottom);
587 get_window()->set_cursor (*_editor->_cursors->resize_bottom_left);
590 get_window()->set_cursor (*_editor->_cursors->move);
592 case TO_LEFT_OR_RIGHT:
593 get_window()->set_cursor (*_editor->_cursors->expand_left_right);
596 get_window()->set_cursor (*_editor->_cursors->expand_up_down);
599 get_window()->set_cursor ();
605 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
607 pair<double, double> xr = _start_editor_x;
608 pair<double, double> yr = _start_editor_y;
609 double x = _start_editor_x.first;
610 double y = _start_editor_y.first;
612 if (_move_dragging) {
616 /* don't alter x if we clicked outside and above or below the viewbox */
617 if (_start_position == INSIDE || _start_position == TO_LEFT_OR_RIGHT || _start_position == OTHERWISE_OUTSIDE) {
618 x += ev->x - _start_mouse_x;
621 /* don't alter y if we clicked outside and to the left or right of the viewbox */
622 if (_start_position == INSIDE || _start_position == BELOW_OR_ABOVE) {
623 y += ev->y - _start_mouse_y;
635 // set_cursor (_start_position);
637 } else if (_zoom_dragging) {
639 double const dx = ev->x - _start_mouse_x;
640 double const dy = ev->y - _start_mouse_y;
642 if (_zoom_position == LEFT || _zoom_position == LEFT_TOP || _zoom_position == LEFT_BOTTOM) {
644 } else if (_zoom_position == RIGHT || _zoom_position == RIGHT_TOP || _zoom_position == RIGHT_BOTTOM) {
647 xr.first = -1; /* do not change */
650 if (_zoom_position == TOP || _zoom_position == LEFT_TOP || _zoom_position == RIGHT_TOP) {
652 } else if (_zoom_position == BOTTOM || _zoom_position == LEFT_BOTTOM || _zoom_position == RIGHT_BOTTOM) {
655 yr.first = -1; /* do not change y */
658 set_overlays_dirty ();
659 set_cursor (_zoom_position);
664 set_cursor (get_position (ev->x, ev->y));
672 EditorSummary::on_button_release_event (GdkEventButton*)
674 bool const was_suspended = suspending_editor_updates ();
676 _move_dragging = false;
677 _zoom_dragging = false;
678 _editor->_dragging_playhead = false;
679 _editor->set_follow_playhead (_old_follow_playhead, false);
681 if (was_suspended && _pending_editor_changed) {
682 set_editor (_pending_editor_x, _pending_editor_y);
689 EditorSummary::on_scroll_event (GdkEventScroll* ev)
693 pair<double, double> xr;
694 pair<double, double> yr;
695 get_editor (&xr, &yr);
699 switch (ev->direction) {
701 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
702 _editor->scroll_left_half_page ();
704 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
705 _editor->temporal_zoom_step (false);
706 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
715 case GDK_SCROLL_DOWN:
716 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
717 _editor->scroll_right_half_page ();
719 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
720 _editor->temporal_zoom_step (true);
721 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
730 case GDK_SCROLL_LEFT:
731 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
733 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
736 _editor->scroll_left_half_page ();
740 case GDK_SCROLL_RIGHT:
741 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
743 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
746 _editor->scroll_right_half_page ();
758 /** Set the editor to display a x range with the left at a given position
759 * and a y range with the top at a given position.
760 * x and y parameters are specified in summary coordinates.
761 * Zoom is not changed in either direction.
764 EditorSummary::set_editor (double const x, double const y)
766 if (_editor->pending_visual_change.idle_handler_id >= 0 && _editor->pending_visual_change.being_handled == true) {
768 /* As a side-effect, the Editor's visual change idle handler processes
769 pending GTK events. Hence this motion notify handler can be called
770 in the middle of a visual change idle handler, and if this happens,
771 the queue_visual_change calls below modify the variables that the
772 idle handler is working with. This causes problems. Hence this
773 check. It ensures that we won't modify the pending visual change
774 while a visual change idle handler is in progress. It's not perfect,
775 as it also means that we won't change these variables if an idle handler
776 is merely pending but not executing. But c'est la vie.
786 /** Set the editor to display a given x range and a y range with the top at a given position.
787 * The editor's x zoom is adjusted if necessary, but the y zoom is not changed.
788 * x and y parameters are specified in summary coordinates.
791 EditorSummary::set_editor (pair<double,double> const x, double const y)
793 if (_editor->pending_visual_change.idle_handler_id >= 0) {
794 /* see comment in other set_editor () */
802 /** Set the editor to display given x and y ranges. x zoom and track heights are
803 * adjusted if necessary.
804 * x and y parameters are specified in summary coordinates.
807 EditorSummary::set_editor (pair<double,double> const x, pair<double, double> const y)
809 if (_editor->pending_visual_change.idle_handler_id >= 0) {
810 /* see comment in other set_editor () */
822 /** Set the left of the x range visible in the editor.
823 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
824 * @param x new x left position in summary coordinates.
827 EditorSummary::set_editor_x (double x)
833 if (suspending_editor_updates ()) {
834 double const w = _pending_editor_x.second - _pending_editor_x.first;
835 _pending_editor_x.first = x;
836 _pending_editor_x.second = x + w;
837 _pending_editor_changed = true;
840 _editor->reset_x_origin (x / _x_scale + _start);
844 /** Set the x range visible in the editor.
845 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
846 * @param x new x range in summary coordinates.
849 EditorSummary::set_editor_x (pair<double, double> x)
856 x.second = x.first + 1;
859 if (suspending_editor_updates ()) {
860 _pending_editor_x = x;
861 _pending_editor_changed = true;
864 _editor->reset_x_origin (x.first / _x_scale + _start);
867 ((x.second - x.first) / _x_scale) /
868 _editor->sample_to_pixel (_editor->current_page_samples())
871 if (nx != _editor->get_current_zoom ()) {
872 _editor->reset_zoom (nx);
877 /** Set the top of the y range visible in the editor.
878 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
879 * @param y new editor top in summary coodinates.
882 EditorSummary::set_editor_y (double const y)
884 double y1 = summary_y_to_editor (y);
885 double const eh = _editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y;
888 double const full_editor_height = _editor->_full_canvas_height;
890 if (y2 > full_editor_height) {
891 y1 -= y2 - full_editor_height;
898 if (suspending_editor_updates ()) {
899 double const h = _pending_editor_y.second - _pending_editor_y.first;
900 _pending_editor_y.first = y;
901 _pending_editor_y.second = y + h;
902 _pending_editor_changed = true;
905 _editor->reset_y_origin (y1);
909 /** Set the y range visible in the editor. This is achieved by scaling track heights,
911 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
912 * @param y new editor range in summary coodinates.
915 EditorSummary::set_editor_y (pair<double, double> const y)
917 if (suspending_editor_updates ()) {
918 _pending_editor_y = y;
919 _pending_editor_changed = true;
924 /* Compute current height of tracks between y.first and y.second. We add up
925 the total height into `total_height' and the height of complete tracks into
929 /* Copy of target range for use below */
930 pair<double, double> yc = y;
931 /* Total height of all tracks */
932 double total_height = 0;
933 /* Height of any parts of tracks that aren't fully in the desired range */
934 double partial_height = 0;
935 /* Height of any tracks that are fully in the desired range */
936 double scale_height = 0;
938 _editor->_routes->suspend_redisplay ();
940 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
942 if ((*i)->hidden()) {
946 double const h = (*i)->effective_height ();
949 if (yc.first > 0 && yc.first < _track_height) {
950 partial_height += (_track_height - yc.first) * h / _track_height;
951 } else if (yc.first <= 0 && yc.second >= _track_height) {
953 } else if (yc.second > 0 && yc.second < _track_height) {
954 partial_height += yc.second * h / _track_height;
958 yc.first -= _track_height;
959 yc.second -= _track_height;
962 /* Height that we will use for scaling; use the whole editor height unless there are not
963 enough tracks to fill it.
965 double const ch = min (total_height, (_editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y));
967 /* hence required scale factor of the complete tracks to fit the required y range;
968 the amount of space they should take up divided by the amount they currently take up.
970 double const scale = (ch - partial_height) / scale_height;
974 /* Scale complete tracks within the range to make it fit */
976 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
978 if ((*i)->hidden()) {
982 if (yc.first <= 0 && yc.second >= _track_height) {
983 (*i)->set_height (max (TimeAxisView::preset_height (HeightSmall), (uint32_t) ((*i)->effective_height() * scale)), TimeAxisView::TotalHeight);
986 yc.first -= _track_height;
987 yc.second -= _track_height;
990 _editor->_routes->resume_redisplay ();
992 set_editor_y (y.first);
996 EditorSummary::playhead_position_changed (framepos_t p)
998 int const o = int (_last_playhead);
999 int const n = int (playhead_frame_to_position (p));
1000 if (_session && o != n) {
1001 int a = max(2, min (o, n));
1003 set_overlays_dirty (a - 2, 0, b + 2, get_height ());
1008 EditorSummary::summary_y_to_editor (double y) const
1011 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
1013 if ((*i)->hidden()) {
1017 double const h = (*i)->effective_height ();
1018 if (y < _track_height) {
1020 return ey + y * h / _track_height;
1031 EditorSummary::editor_y_to_summary (double y) const
1034 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
1036 if ((*i)->hidden()) {
1040 double const h = (*i)->effective_height ();
1043 return sy + y * _track_height / h;
1046 sy += _track_height;
1054 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
1056 for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
1057 /* Connect to the relevant signal for the route so that we know when its colour has changed */
1058 (*i)->route()->presentation_info().PropertyChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
1059 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> ((*i)->route ());
1061 tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context ());
1065 set_background_dirty ();
1069 EditorSummary::route_gui_changed (PBD::PropertyChange const& what_changed)
1071 if (what_changed.contains (Properties::color)) {
1072 set_background_dirty ();
1077 EditorSummary::playhead_frame_to_position (framepos_t t) const
1079 return (t - _start) * _x_scale;
1083 EditorSummary::position_to_playhead_frame_to_position (double pos) const
1085 return _start + (pos * _x_scale);