2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include "ardour/session.h"
22 #include "canvas/debug.h"
24 #include "time_axis_view.h"
25 #include "streamview.h"
26 #include "editor_summary.h"
27 #include "gui_thread.h"
29 #include "region_view.h"
30 #include "rgb_macros.h"
32 #include "editor_routes.h"
33 #include "editor_cursors.h"
34 #include "mouse_cursors.h"
35 #include "route_time_axis.h"
36 #include "ui_config.h"
39 using namespace ARDOUR;
40 using Gtkmm2ext::Keyboard;
42 /** Construct an EditorSummary.
43 * @param e Editor to represent.
45 EditorSummary::EditorSummary (Editor* e)
46 : EditorComponent (e),
49 _overhang_fraction (0.1),
53 _move_dragging (false),
55 _view_rectangle_x (0, 0),
56 _view_rectangle_y (0, 0),
57 _zoom_dragging (false),
58 _old_follow_playhead (false),
60 _background_dirty (true)
62 add_events (Gdk::POINTER_MOTION_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
63 set_flags (get_flags() | Gtk::CAN_FOCUS);
65 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &EditorSummary::parameter_changed));
68 EditorSummary::~EditorSummary ()
70 cairo_surface_destroy (_image);
74 EditorSummary::parameter_changed (string p)
77 if (p == "color-regions-using-track-color") {
78 set_background_dirty ();
82 /** Handle a size allocation.
83 * @param alloc GTK allocation.
86 EditorSummary::on_size_allocate (Gtk::Allocation& alloc)
88 CairoWidget::on_size_allocate (alloc);
89 set_background_dirty ();
93 /** Connect to a session.
97 EditorSummary::set_session (Session* s)
99 SessionHandlePtr::set_session (s);
103 /* Note: the EditorSummary already finds out about new regions from Editor::region_view_added
104 * (which attaches to StreamView::RegionViewAdded), and cut regions by the RegionPropertyChanged
105 * emitted when a cut region is added to the `cutlist' playlist.
109 Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
110 Route::RemoteControlIDChange.connect (route_ctrl_id_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
111 _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), boost::bind (&EditorSummary::playhead_position_changed, this, _1), gui_context());
112 _session->StartTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
113 _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
114 _editor->selection->RegionsChanged.connect (sigc::mem_fun(*this, &EditorSummary::set_background_dirty));
119 EditorSummary::render_background_image ()
121 cairo_surface_destroy (_image); // passing NULL is safe
122 _image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, get_width (), get_height ());
124 cairo_t* cr = cairo_create (_image);
126 /* background (really just the dividing lines between tracks */
128 cairo_set_source_rgb (cr, 0, 0, 0);
129 cairo_rectangle (cr, 0, 0, get_width(), get_height());
132 /* compute start and end points for the summary */
134 framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
135 double const theoretical_start = _session->current_start_frame() - session_length * _overhang_fraction;
136 _start = theoretical_start > 0 ? theoretical_start : 0;
137 _end = _session->current_end_frame() + session_length * _overhang_fraction;
139 /* compute track height */
141 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
142 if (!(*i)->hidden()) {
150 _track_height = (double) get_height() / N;
153 /* calculate x scale */
154 if (_end != _start) {
155 _x_scale = static_cast<double> (get_width()) / (_end - _start);
160 /* render tracks and regions */
163 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
165 if ((*i)->hidden()) {
169 /* paint a non-bg colored strip to represent the track itself */
171 cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
172 cairo_set_line_width (cr, _track_height - 1);
173 cairo_move_to (cr, 0, y + _track_height / 2);
174 cairo_line_to (cr, get_width(), y + _track_height / 2);
177 StreamView* s = (*i)->view ();
180 cairo_set_line_width (cr, _track_height * 0.8);
182 s->foreach_regionview (sigc::bind (
183 sigc::mem_fun (*this, &EditorSummary::render_region),
185 y + _track_height / 2
192 /* start and end markers */
194 cairo_set_line_width (cr, 1);
195 cairo_set_source_rgb (cr, 1, 1, 0);
197 const double p = (_session->current_start_frame() - _start) * _x_scale;
198 cairo_move_to (cr, p, 0);
199 cairo_line_to (cr, p, get_height());
201 double const q = (_session->current_end_frame() - _start) * _x_scale;
202 cairo_move_to (cr, q, 0);
203 cairo_line_to (cr, q, get_height());
209 /** Render the required regions to a cairo context.
213 EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
220 if (!_image || _background_dirty) {
221 render_background_image ();
222 _background_dirty = false;
225 cairo_push_group (cr);
227 /* Fill with the background image */
229 cairo_rectangle (cr, 0, 0, get_width(), get_height());
230 cairo_set_source_surface (cr, _image, 0, 0);
233 /* Render the view rectangle. If there is an editor visual pending, don't update
234 the view rectangle now --- wait until the expose event that we'll get after
235 the visual change. This prevents a flicker.
238 if (_editor->pending_visual_change.idle_handler_id < 0) {
239 get_editor (&_view_rectangle_x, &_view_rectangle_y);
242 int32_t width = _view_rectangle_x.second - _view_rectangle_x.first;
243 int32_t height = _view_rectangle_y.second - _view_rectangle_y.first;
244 cairo_rectangle (cr, _view_rectangle_x.first, _view_rectangle_y.first, width, height);
245 cairo_set_source_rgba (cr, 1, 1, 1, 0.1);
246 cairo_fill_preserve (cr);
247 cairo_set_line_width (cr, 1);
248 cairo_set_source_rgba (cr, 1, 1, 1, 0.4);
253 cairo_set_line_width (cr, 1);
254 /* XXX: colour should be set from configuration file */
255 cairo_set_source_rgba (cr, 1, 0, 0, 1);
257 const double ph= playhead_frame_to_position (_editor->playhead_cursor->current_frame());
258 cairo_move_to (cr, ph, 0);
259 cairo_line_to (cr, ph, get_height());
261 cairo_pop_group_to_source (cr);
267 /** Render a region for the summary.
268 * @param r Region view.
269 * @param cr Cairo context.
270 * @param y y coordinate to render at.
273 EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const
275 uint32_t const c = r->get_fill_color ();
276 cairo_set_source_rgb (cr, UINT_RGBA_R (c) / 255.0, UINT_RGBA_G (c) / 255.0, UINT_RGBA_B (c) / 255.0);
278 if (r->region()->position() > _start) {
279 cairo_move_to (cr, (r->region()->position() - _start) * _x_scale, y);
281 cairo_move_to (cr, 0, y);
284 if ((r->region()->position() + r->region()->length()) > _start) {
285 cairo_line_to (cr, ((r->region()->position() - _start + r->region()->length())) * _x_scale, y);
287 cairo_line_to (cr, 0, y);
294 EditorSummary::set_background_dirty ()
296 _background_dirty = true;
300 /** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
302 EditorSummary::set_overlays_dirty ()
304 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
308 /** Set the summary so that just the overlays (viewbox, playhead etc.) in a given area will be re-rendered */
310 EditorSummary::set_overlays_dirty (int x, int y, int w, int h)
312 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
313 queue_draw_area (x, y, w, h);
317 /** Handle a size request.
318 * @param req GTK requisition
321 EditorSummary::on_size_request (Gtk::Requisition *req)
323 /* Use a dummy, small width and the actual height that we want */
330 EditorSummary::centre_on_click (GdkEventButton* ev)
332 pair<double, double> xr;
333 pair<double, double> yr;
334 get_editor (&xr, &yr);
336 double const w = xr.second - xr.first;
337 double ex = ev->x - w / 2;
340 } else if ((ex + w) > get_width()) {
341 ex = get_width() - w;
344 double const h = yr.second - yr.first;
345 double ey = ev->y - h / 2;
348 } else if ((ey + h) > get_height()) {
349 ey = get_height() - h;
356 EditorSummary::on_enter_notify_event (GdkEventCrossing*)
359 Keyboard::magic_widget_grab_focus ();
364 EditorSummary::on_leave_notify_event (GdkEventCrossing*)
366 /* there are no inferior/child windows, so any leave event means that
369 Keyboard::magic_widget_drop_focus ();
374 EditorSummary::on_key_press_event (GdkEventKey* key)
377 GtkAccelKey set_playhead_accel;
378 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
379 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
382 _session->request_locate (_start + (framepos_t) x / _x_scale, _session->transport_rolling());
392 EditorSummary::on_key_release_event (GdkEventKey* key)
395 GtkAccelKey set_playhead_accel;
396 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
397 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
404 /** Handle a button press.
405 * @param ev GTK event.
408 EditorSummary::on_button_press_event (GdkEventButton* ev)
410 _old_follow_playhead = _editor->follow_playhead ();
412 if (ev->button == 1) {
414 pair<double, double> xr;
415 pair<double, double> yr;
416 get_editor (&xr, &yr);
418 _start_editor_x = xr;
419 _start_editor_y = yr;
420 _start_mouse_x = ev->x;
421 _start_mouse_y = ev->y;
422 _start_position = get_position (ev->x, ev->y);
424 if (_start_position != INSIDE && _start_position != BELOW_OR_ABOVE &&
425 _start_position != TO_LEFT_OR_RIGHT && _start_position != OTHERWISE_OUTSIDE
428 /* start a zoom drag */
430 _zoom_position = get_position (ev->x, ev->y);
431 _zoom_dragging = true;
432 _editor->_dragging_playhead = true;
433 _editor->set_follow_playhead (false);
435 if (suspending_editor_updates ()) {
436 get_editor (&_pending_editor_x, &_pending_editor_y);
437 _pending_editor_changed = false;
440 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
442 /* secondary-modifier-click: locate playhead */
444 _session->request_locate (ev->x / _x_scale + _start);
447 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
449 centre_on_click (ev);
453 /* start a move drag */
455 /* get the editor's state in case we are suspending updates */
456 get_editor (&_pending_editor_x, &_pending_editor_y);
457 _pending_editor_changed = false;
459 _move_dragging = true;
461 _editor->_dragging_playhead = true;
462 _editor->set_follow_playhead (false);
464 ArdourCanvas::checkpoint ("sum", "------------------ summary move drag starts.\n");
471 /** @return true if we are currently suspending updates to the editor's viewport,
472 * which we do if configured to do so, and if in a drag of some kind.
475 EditorSummary::suspending_editor_updates () const
477 return (!UIConfiguration::instance().get_update_editor_during_summary_drag () && (_zoom_dragging || _move_dragging));
480 /** Fill in x and y with the editor's current viewable area in summary coordinates */
482 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
487 if (suspending_editor_updates ()) {
489 /* We are dragging, and configured not to update the editor window during drags,
490 so just return where the editor will be when the drag finishes.
493 *x = _pending_editor_x;
494 *y = _pending_editor_y;
498 /* Otherwise query the editor for its actual position */
500 x->first = (_editor->leftmost_sample () - _start) * _x_scale;
501 x->second = x->first + _editor->current_page_samples() * _x_scale;
503 y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
504 y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y);
508 /** Get an expression of the position of a point with respect to the view rectangle */
509 EditorSummary::Position
510 EditorSummary::get_position (double x, double y) const
512 /* how close the mouse has to be to the edge of the view rectangle to be considered `on it',
515 int x_edge_size = (_view_rectangle_x.second - _view_rectangle_x.first) / 4;
516 x_edge_size = min (x_edge_size, 8);
517 x_edge_size = max (x_edge_size, 1);
519 int y_edge_size = (_view_rectangle_y.second - _view_rectangle_y.first) / 4;
520 y_edge_size = min (y_edge_size, 8);
521 y_edge_size = max (y_edge_size, 1);
523 bool const near_left = (std::abs (x - _view_rectangle_x.first) < x_edge_size);
524 bool const near_right = (std::abs (x - _view_rectangle_x.second) < x_edge_size);
525 bool const near_top = (std::abs (y - _view_rectangle_y.first) < y_edge_size);
526 bool const near_bottom = (std::abs (y - _view_rectangle_y.second) < y_edge_size);
527 bool const within_x = _view_rectangle_x.first < x && x < _view_rectangle_x.second;
528 bool const within_y = _view_rectangle_y.first < y && y < _view_rectangle_y.second;
530 if (near_left && near_top) {
532 } else if (near_left && near_bottom) {
534 } else if (near_right && near_top) {
536 } else if (near_right && near_bottom) {
538 } else if (near_left && within_y) {
540 } else if (near_right && within_y) {
542 } else if (near_top && within_x) {
544 } else if (near_bottom && within_x) {
546 } else if (within_x && within_y) {
548 } else if (within_x) {
549 return BELOW_OR_ABOVE;
550 } else if (within_y) {
551 return TO_LEFT_OR_RIGHT;
553 return OTHERWISE_OUTSIDE;
558 EditorSummary::set_cursor (Position p)
562 get_window()->set_cursor (*_editor->_cursors->resize_left);
565 get_window()->set_cursor (*_editor->_cursors->resize_top_left);
568 get_window()->set_cursor (*_editor->_cursors->resize_top);
571 get_window()->set_cursor (*_editor->_cursors->resize_top_right);
574 get_window()->set_cursor (*_editor->_cursors->resize_right);
577 get_window()->set_cursor (*_editor->_cursors->resize_bottom_right);
580 get_window()->set_cursor (*_editor->_cursors->resize_bottom);
583 get_window()->set_cursor (*_editor->_cursors->resize_bottom_left);
586 get_window()->set_cursor (*_editor->_cursors->move);
588 case TO_LEFT_OR_RIGHT:
589 get_window()->set_cursor (*_editor->_cursors->expand_left_right);
592 get_window()->set_cursor (*_editor->_cursors->expand_up_down);
595 get_window()->set_cursor ();
601 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
603 pair<double, double> xr = _start_editor_x;
604 pair<double, double> yr = _start_editor_y;
605 double x = _start_editor_x.first;
606 double y = _start_editor_y.first;
608 if (_move_dragging) {
612 /* don't alter x if we clicked outside and above or below the viewbox */
613 if (_start_position == INSIDE || _start_position == TO_LEFT_OR_RIGHT || _start_position == OTHERWISE_OUTSIDE) {
614 x += ev->x - _start_mouse_x;
617 /* don't alter y if we clicked outside and to the left or right of the viewbox */
618 if (_start_position == INSIDE || _start_position == BELOW_OR_ABOVE) {
619 y += ev->y - _start_mouse_y;
631 // set_cursor (_start_position);
633 } else if (_zoom_dragging) {
635 double const dx = ev->x - _start_mouse_x;
636 double const dy = ev->y - _start_mouse_y;
638 if (_zoom_position == LEFT || _zoom_position == LEFT_TOP || _zoom_position == LEFT_BOTTOM) {
640 } else if (_zoom_position == RIGHT || _zoom_position == RIGHT_TOP || _zoom_position == RIGHT_BOTTOM) {
643 xr.first = -1; /* do not change */
646 if (_zoom_position == TOP || _zoom_position == LEFT_TOP || _zoom_position == RIGHT_TOP) {
648 } else if (_zoom_position == BOTTOM || _zoom_position == LEFT_BOTTOM || _zoom_position == RIGHT_BOTTOM) {
651 yr.first = -1; /* do not change y */
654 set_overlays_dirty ();
655 set_cursor (_zoom_position);
660 set_cursor (get_position (ev->x, ev->y));
668 EditorSummary::on_button_release_event (GdkEventButton*)
670 bool const was_suspended = suspending_editor_updates ();
672 _move_dragging = false;
673 _zoom_dragging = false;
674 _editor->_dragging_playhead = false;
675 _editor->set_follow_playhead (_old_follow_playhead, false);
677 if (was_suspended && _pending_editor_changed) {
678 set_editor (_pending_editor_x, _pending_editor_y);
685 EditorSummary::on_scroll_event (GdkEventScroll* ev)
689 pair<double, double> xr;
690 pair<double, double> yr;
691 get_editor (&xr, &yr);
695 switch (ev->direction) {
697 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
698 _editor->scroll_left_half_page ();
700 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
701 _editor->temporal_zoom_step (false);
702 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
711 case GDK_SCROLL_DOWN:
712 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
713 _editor->scroll_right_half_page ();
715 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
716 _editor->temporal_zoom_step (true);
717 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
726 case GDK_SCROLL_LEFT:
727 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
729 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
732 _editor->scroll_left_half_page ();
736 case GDK_SCROLL_RIGHT:
737 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
739 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
742 _editor->scroll_right_half_page ();
754 /** Set the editor to display a x range with the left at a given position
755 * and a y range with the top at a given position.
756 * x and y parameters are specified in summary coordinates.
757 * Zoom is not changed in either direction.
760 EditorSummary::set_editor (double const x, double const y)
762 if (_editor->pending_visual_change.idle_handler_id >= 0 && _editor->pending_visual_change.being_handled == true) {
764 /* As a side-effect, the Editor's visual change idle handler processes
765 pending GTK events. Hence this motion notify handler can be called
766 in the middle of a visual change idle handler, and if this happens,
767 the queue_visual_change calls below modify the variables that the
768 idle handler is working with. This causes problems. Hence this
769 check. It ensures that we won't modify the pending visual change
770 while a visual change idle handler is in progress. It's not perfect,
771 as it also means that we won't change these variables if an idle handler
772 is merely pending but not executing. But c'est la vie.
782 /** Set the editor to display a given x range and a y range with the top at a given position.
783 * The editor's x zoom is adjusted if necessary, but the y zoom is not changed.
784 * x and y parameters are specified in summary coordinates.
787 EditorSummary::set_editor (pair<double,double> const x, double const y)
789 if (_editor->pending_visual_change.idle_handler_id >= 0) {
790 /* see comment in other set_editor () */
798 /** Set the editor to display given x and y ranges. x zoom and track heights are
799 * adjusted if necessary.
800 * x and y parameters are specified in summary coordinates.
803 EditorSummary::set_editor (pair<double,double> const x, pair<double, double> const y)
805 if (_editor->pending_visual_change.idle_handler_id >= 0) {
806 /* see comment in other set_editor () */
818 /** Set the left of 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 left position in summary coordinates.
823 EditorSummary::set_editor_x (double x)
829 if (suspending_editor_updates ()) {
830 double const w = _pending_editor_x.second - _pending_editor_x.first;
831 _pending_editor_x.first = x;
832 _pending_editor_x.second = x + w;
833 _pending_editor_changed = true;
836 _editor->reset_x_origin (x / _x_scale + _start);
840 /** Set the x range visible in the editor.
841 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
842 * @param x new x range in summary coordinates.
845 EditorSummary::set_editor_x (pair<double, double> x)
852 x.second = x.first + 1;
855 if (suspending_editor_updates ()) {
856 _pending_editor_x = x;
857 _pending_editor_changed = true;
860 _editor->reset_x_origin (x.first / _x_scale + _start);
863 ((x.second - x.first) / _x_scale) /
864 _editor->sample_to_pixel (_editor->current_page_samples())
867 if (nx != _editor->get_current_zoom ()) {
868 _editor->reset_zoom (nx);
873 /** Set the top of the y range visible in the editor.
874 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
875 * @param y new editor top in summary coodinates.
878 EditorSummary::set_editor_y (double const y)
880 double y1 = summary_y_to_editor (y);
881 double const eh = _editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y;
884 double const full_editor_height = _editor->_full_canvas_height;
886 if (y2 > full_editor_height) {
887 y1 -= y2 - full_editor_height;
894 if (suspending_editor_updates ()) {
895 double const h = _pending_editor_y.second - _pending_editor_y.first;
896 _pending_editor_y.first = y;
897 _pending_editor_y.second = y + h;
898 _pending_editor_changed = true;
901 _editor->reset_y_origin (y1);
905 /** Set the y range visible in the editor. This is achieved by scaling track heights,
907 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
908 * @param y new editor range in summary coodinates.
911 EditorSummary::set_editor_y (pair<double, double> const y)
913 if (suspending_editor_updates ()) {
914 _pending_editor_y = y;
915 _pending_editor_changed = true;
920 /* Compute current height of tracks between y.first and y.second. We add up
921 the total height into `total_height' and the height of complete tracks into
925 /* Copy of target range for use below */
926 pair<double, double> yc = y;
927 /* Total height of all tracks */
928 double total_height = 0;
929 /* Height of any parts of tracks that aren't fully in the desired range */
930 double partial_height = 0;
931 /* Height of any tracks that are fully in the desired range */
932 double scale_height = 0;
934 _editor->_routes->suspend_redisplay ();
936 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
938 if ((*i)->hidden()) {
942 double const h = (*i)->effective_height ();
945 if (yc.first > 0 && yc.first < _track_height) {
946 partial_height += (_track_height - yc.first) * h / _track_height;
947 } else if (yc.first <= 0 && yc.second >= _track_height) {
949 } else if (yc.second > 0 && yc.second < _track_height) {
950 partial_height += yc.second * h / _track_height;
954 yc.first -= _track_height;
955 yc.second -= _track_height;
958 /* Height that we will use for scaling; use the whole editor height unless there are not
959 enough tracks to fill it.
961 double const ch = min (total_height, (_editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y));
963 /* hence required scale factor of the complete tracks to fit the required y range;
964 the amount of space they should take up divided by the amount they currently take up.
966 double const scale = (ch - partial_height) / scale_height;
970 /* Scale complete tracks within the range to make it fit */
972 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
974 if ((*i)->hidden()) {
978 if (yc.first <= 0 && yc.second >= _track_height) {
979 (*i)->set_height (max (TimeAxisView::preset_height (HeightSmall), (uint32_t) ((*i)->effective_height() * scale)), TimeAxisView::TotalHeight);
982 yc.first -= _track_height;
983 yc.second -= _track_height;
986 _editor->_routes->resume_redisplay ();
988 set_editor_y (y.first);
992 EditorSummary::playhead_position_changed (framepos_t p)
994 int const o = int (_last_playhead);
995 int const n = int (playhead_frame_to_position (p));
996 if (_session && o != n) {
997 int a = max(2, min (o, n));
999 set_overlays_dirty (a - 2, 0, b + 2, get_height ());
1004 EditorSummary::summary_y_to_editor (double y) const
1007 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
1009 if ((*i)->hidden()) {
1013 double const h = (*i)->effective_height ();
1014 if (y < _track_height) {
1016 return ey + y * h / _track_height;
1027 EditorSummary::editor_y_to_summary (double y) const
1030 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
1032 if ((*i)->hidden()) {
1036 double const h = (*i)->effective_height ();
1039 return sy + y * _track_height / h;
1042 sy += _track_height;
1050 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
1052 for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
1053 /* Connect to gui_changed() on the route so that we know when their colour has changed */
1054 (*i)->route()->gui_changed.connect (*this, invalidator (*this), boost::bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
1055 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> ((*i)->route ());
1057 tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context ());
1061 set_background_dirty ();
1065 EditorSummary::route_gui_changed (string c)
1068 set_background_dirty ();
1073 EditorSummary::playhead_frame_to_position (framepos_t t) const
1075 return (t - _start) * _x_scale;
1079 EditorSummary::position_to_playhead_frame_to_position (double pos) const
1081 return _start + (pos * _x_scale);