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 PresentationInfo::Change.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 if (!_background_dirty) {
297 _background_dirty = true;
302 /** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
304 EditorSummary::set_overlays_dirty ()
306 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
310 /** Set the summary so that just the overlays (viewbox, playhead etc.) in a given area will be re-rendered */
312 EditorSummary::set_overlays_dirty (int x, int y, int w, int h)
314 ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
315 queue_draw_area (x, y, w, h);
319 /** Handle a size request.
320 * @param req GTK requisition
323 EditorSummary::on_size_request (Gtk::Requisition *req)
325 /* Use a dummy, small width and the actual height that we want */
332 EditorSummary::centre_on_click (GdkEventButton* ev)
334 pair<double, double> xr;
335 pair<double, double> yr;
336 get_editor (&xr, &yr);
338 double const w = xr.second - xr.first;
339 double ex = ev->x - w / 2;
342 } else if ((ex + w) > get_width()) {
343 ex = get_width() - w;
346 double const h = yr.second - yr.first;
347 double ey = ev->y - h / 2;
350 } else if ((ey + h) > get_height()) {
351 ey = get_height() - h;
358 EditorSummary::on_enter_notify_event (GdkEventCrossing*)
361 Keyboard::magic_widget_grab_focus ();
366 EditorSummary::on_leave_notify_event (GdkEventCrossing*)
368 /* there are no inferior/child windows, so any leave event means that
371 Keyboard::magic_widget_drop_focus ();
376 EditorSummary::on_key_press_event (GdkEventKey* key)
379 GtkAccelKey set_playhead_accel;
380 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
381 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
384 _session->request_locate (_start + (framepos_t) x / _x_scale, _session->transport_rolling());
394 EditorSummary::on_key_release_event (GdkEventKey* key)
397 GtkAccelKey set_playhead_accel;
398 if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
399 if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
406 /** Handle a button press.
407 * @param ev GTK event.
410 EditorSummary::on_button_press_event (GdkEventButton* ev)
412 _old_follow_playhead = _editor->follow_playhead ();
414 if (ev->button == 1) {
416 pair<double, double> xr;
417 pair<double, double> yr;
418 get_editor (&xr, &yr);
420 _start_editor_x = xr;
421 _start_editor_y = yr;
422 _start_mouse_x = ev->x;
423 _start_mouse_y = ev->y;
424 _start_position = get_position (ev->x, ev->y);
426 if (_start_position != INSIDE && _start_position != BELOW_OR_ABOVE &&
427 _start_position != TO_LEFT_OR_RIGHT && _start_position != OTHERWISE_OUTSIDE
430 /* start a zoom drag */
432 _zoom_position = get_position (ev->x, ev->y);
433 _zoom_dragging = true;
434 _editor->_dragging_playhead = true;
435 _editor->set_follow_playhead (false);
437 if (suspending_editor_updates ()) {
438 get_editor (&_pending_editor_x, &_pending_editor_y);
439 _pending_editor_changed = false;
442 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
444 /* secondary-modifier-click: locate playhead */
446 _session->request_locate (ev->x / _x_scale + _start);
449 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
451 centre_on_click (ev);
455 /* start a move drag */
457 /* get the editor's state in case we are suspending updates */
458 get_editor (&_pending_editor_x, &_pending_editor_y);
459 _pending_editor_changed = false;
461 _move_dragging = true;
463 _editor->_dragging_playhead = true;
464 _editor->set_follow_playhead (false);
466 ArdourCanvas::checkpoint ("sum", "------------------ summary move drag starts.\n");
473 /** @return true if we are currently suspending updates to the editor's viewport,
474 * which we do if configured to do so, and if in a drag of some kind.
477 EditorSummary::suspending_editor_updates () const
479 return (!UIConfiguration::instance().get_update_editor_during_summary_drag () && (_zoom_dragging || _move_dragging));
482 /** Fill in x and y with the editor's current viewable area in summary coordinates */
484 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
489 if (suspending_editor_updates ()) {
491 /* We are dragging, and configured not to update the editor window during drags,
492 so just return where the editor will be when the drag finishes.
495 *x = _pending_editor_x;
496 *y = _pending_editor_y;
500 /* Otherwise query the editor for its actual position */
502 x->first = (_editor->leftmost_sample () - _start) * _x_scale;
503 x->second = x->first + _editor->current_page_samples() * _x_scale;
505 y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
506 y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y);
510 /** Get an expression of the position of a point with respect to the view rectangle */
511 EditorSummary::Position
512 EditorSummary::get_position (double x, double y) const
514 /* how close the mouse has to be to the edge of the view rectangle to be considered `on it',
517 int x_edge_size = (_view_rectangle_x.second - _view_rectangle_x.first) / 4;
518 x_edge_size = min (x_edge_size, 8);
519 x_edge_size = max (x_edge_size, 1);
521 int y_edge_size = (_view_rectangle_y.second - _view_rectangle_y.first) / 4;
522 y_edge_size = min (y_edge_size, 8);
523 y_edge_size = max (y_edge_size, 1);
525 bool const near_left = (std::abs (x - _view_rectangle_x.first) < x_edge_size);
526 bool const near_right = (std::abs (x - _view_rectangle_x.second) < x_edge_size);
527 bool const near_top = (std::abs (y - _view_rectangle_y.first) < y_edge_size);
528 bool const near_bottom = (std::abs (y - _view_rectangle_y.second) < y_edge_size);
529 bool const within_x = _view_rectangle_x.first < x && x < _view_rectangle_x.second;
530 bool const within_y = _view_rectangle_y.first < y && y < _view_rectangle_y.second;
532 if (near_left && near_top) {
534 } else if (near_left && near_bottom) {
536 } else if (near_right && near_top) {
538 } else if (near_right && near_bottom) {
540 } else if (near_left && within_y) {
542 } else if (near_right && within_y) {
544 } else if (near_top && within_x) {
546 } else if (near_bottom && within_x) {
548 } else if (within_x && within_y) {
550 } else if (within_x) {
551 return BELOW_OR_ABOVE;
552 } else if (within_y) {
553 return TO_LEFT_OR_RIGHT;
555 return OTHERWISE_OUTSIDE;
560 EditorSummary::set_cursor (Position p)
564 get_window()->set_cursor (*_editor->_cursors->resize_left);
567 get_window()->set_cursor (*_editor->_cursors->resize_top_left);
570 get_window()->set_cursor (*_editor->_cursors->resize_top);
573 get_window()->set_cursor (*_editor->_cursors->resize_top_right);
576 get_window()->set_cursor (*_editor->_cursors->resize_right);
579 get_window()->set_cursor (*_editor->_cursors->resize_bottom_right);
582 get_window()->set_cursor (*_editor->_cursors->resize_bottom);
585 get_window()->set_cursor (*_editor->_cursors->resize_bottom_left);
588 get_window()->set_cursor (*_editor->_cursors->move);
590 case TO_LEFT_OR_RIGHT:
591 get_window()->set_cursor (*_editor->_cursors->expand_left_right);
594 get_window()->set_cursor (*_editor->_cursors->expand_up_down);
597 get_window()->set_cursor ();
603 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
605 pair<double, double> xr = _start_editor_x;
606 pair<double, double> yr = _start_editor_y;
607 double x = _start_editor_x.first;
608 double y = _start_editor_y.first;
610 if (_move_dragging) {
614 /* don't alter x if we clicked outside and above or below the viewbox */
615 if (_start_position == INSIDE || _start_position == TO_LEFT_OR_RIGHT || _start_position == OTHERWISE_OUTSIDE) {
616 x += ev->x - _start_mouse_x;
619 /* don't alter y if we clicked outside and to the left or right of the viewbox */
620 if (_start_position == INSIDE || _start_position == BELOW_OR_ABOVE) {
621 y += ev->y - _start_mouse_y;
633 // set_cursor (_start_position);
635 } else if (_zoom_dragging) {
637 double const dx = ev->x - _start_mouse_x;
638 double const dy = ev->y - _start_mouse_y;
640 if (_zoom_position == LEFT || _zoom_position == LEFT_TOP || _zoom_position == LEFT_BOTTOM) {
642 } else if (_zoom_position == RIGHT || _zoom_position == RIGHT_TOP || _zoom_position == RIGHT_BOTTOM) {
645 xr.first = -1; /* do not change */
648 if (_zoom_position == TOP || _zoom_position == LEFT_TOP || _zoom_position == RIGHT_TOP) {
650 } else if (_zoom_position == BOTTOM || _zoom_position == LEFT_BOTTOM || _zoom_position == RIGHT_BOTTOM) {
653 yr.first = -1; /* do not change y */
656 set_overlays_dirty ();
657 set_cursor (_zoom_position);
662 set_cursor (get_position (ev->x, ev->y));
670 EditorSummary::on_button_release_event (GdkEventButton*)
672 bool const was_suspended = suspending_editor_updates ();
674 _move_dragging = false;
675 _zoom_dragging = false;
676 _editor->_dragging_playhead = false;
677 _editor->set_follow_playhead (_old_follow_playhead, false);
679 if (was_suspended && _pending_editor_changed) {
680 set_editor (_pending_editor_x, _pending_editor_y);
687 EditorSummary::on_scroll_event (GdkEventScroll* ev)
691 pair<double, double> xr;
692 pair<double, double> yr;
693 get_editor (&xr, &yr);
697 switch (ev->direction) {
699 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
700 _editor->scroll_left_half_page ();
702 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
703 _editor->temporal_zoom_step (false);
704 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
713 case GDK_SCROLL_DOWN:
714 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
715 _editor->scroll_right_half_page ();
717 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
718 _editor->temporal_zoom_step (true);
719 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
728 case GDK_SCROLL_LEFT:
729 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
731 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
734 _editor->scroll_left_half_page ();
738 case GDK_SCROLL_RIGHT:
739 if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
741 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
744 _editor->scroll_right_half_page ();
756 /** Set the editor to display a x range with the left at a given position
757 * and a y range with the top at a given position.
758 * x and y parameters are specified in summary coordinates.
759 * Zoom is not changed in either direction.
762 EditorSummary::set_editor (double const x, double const y)
764 if (_editor->pending_visual_change.idle_handler_id >= 0 && _editor->pending_visual_change.being_handled == true) {
766 /* As a side-effect, the Editor's visual change idle handler processes
767 pending GTK events. Hence this motion notify handler can be called
768 in the middle of a visual change idle handler, and if this happens,
769 the queue_visual_change calls below modify the variables that the
770 idle handler is working with. This causes problems. Hence this
771 check. It ensures that we won't modify the pending visual change
772 while a visual change idle handler is in progress. It's not perfect,
773 as it also means that we won't change these variables if an idle handler
774 is merely pending but not executing. But c'est la vie.
784 /** Set the editor to display a given x range and a y range with the top at a given position.
785 * The editor's x zoom is adjusted if necessary, but the y zoom is not changed.
786 * x and y parameters are specified in summary coordinates.
789 EditorSummary::set_editor (pair<double,double> const x, double const y)
791 if (_editor->pending_visual_change.idle_handler_id >= 0) {
792 /* see comment in other set_editor () */
800 /** Set the editor to display given x and y ranges. x zoom and track heights are
801 * adjusted if necessary.
802 * x and y parameters are specified in summary coordinates.
805 EditorSummary::set_editor (pair<double,double> const x, pair<double, double> const y)
807 if (_editor->pending_visual_change.idle_handler_id >= 0) {
808 /* see comment in other set_editor () */
820 /** Set the left of the x range visible in the editor.
821 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
822 * @param x new x left position in summary coordinates.
825 EditorSummary::set_editor_x (double x)
831 if (suspending_editor_updates ()) {
832 double const w = _pending_editor_x.second - _pending_editor_x.first;
833 _pending_editor_x.first = x;
834 _pending_editor_x.second = x + w;
835 _pending_editor_changed = true;
838 _editor->reset_x_origin (x / _x_scale + _start);
842 /** Set the x range visible in the editor.
843 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
844 * @param x new x range in summary coordinates.
847 EditorSummary::set_editor_x (pair<double, double> x)
854 x.second = x.first + 1;
857 if (suspending_editor_updates ()) {
858 _pending_editor_x = x;
859 _pending_editor_changed = true;
862 _editor->reset_x_origin (x.first / _x_scale + _start);
865 ((x.second - x.first) / _x_scale) /
866 _editor->sample_to_pixel (_editor->current_page_samples())
869 if (nx != _editor->get_current_zoom ()) {
870 _editor->reset_zoom (nx);
875 /** Set the top of the y range visible in the editor.
876 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
877 * @param y new editor top in summary coodinates.
880 EditorSummary::set_editor_y (double const y)
882 double y1 = summary_y_to_editor (y);
883 double const eh = _editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y;
886 double const full_editor_height = _editor->_full_canvas_height;
888 if (y2 > full_editor_height) {
889 y1 -= y2 - full_editor_height;
896 if (suspending_editor_updates ()) {
897 double const h = _pending_editor_y.second - _pending_editor_y.first;
898 _pending_editor_y.first = y;
899 _pending_editor_y.second = y + h;
900 _pending_editor_changed = true;
903 _editor->reset_y_origin (y1);
907 /** Set the y range visible in the editor. This is achieved by scaling track heights,
909 * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
910 * @param y new editor range in summary coodinates.
913 EditorSummary::set_editor_y (pair<double, double> const y)
915 if (suspending_editor_updates ()) {
916 _pending_editor_y = y;
917 _pending_editor_changed = true;
922 /* Compute current height of tracks between y.first and y.second. We add up
923 the total height into `total_height' and the height of complete tracks into
927 /* Copy of target range for use below */
928 pair<double, double> yc = y;
929 /* Total height of all tracks */
930 double total_height = 0;
931 /* Height of any parts of tracks that aren't fully in the desired range */
932 double partial_height = 0;
933 /* Height of any tracks that are fully in the desired range */
934 double scale_height = 0;
936 _editor->_routes->suspend_redisplay ();
938 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
940 if ((*i)->hidden()) {
944 double const h = (*i)->effective_height ();
947 if (yc.first > 0 && yc.first < _track_height) {
948 partial_height += (_track_height - yc.first) * h / _track_height;
949 } else if (yc.first <= 0 && yc.second >= _track_height) {
951 } else if (yc.second > 0 && yc.second < _track_height) {
952 partial_height += yc.second * h / _track_height;
956 yc.first -= _track_height;
957 yc.second -= _track_height;
960 /* Height that we will use for scaling; use the whole editor height unless there are not
961 enough tracks to fill it.
963 double const ch = min (total_height, (_editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y));
965 /* hence required scale factor of the complete tracks to fit the required y range;
966 the amount of space they should take up divided by the amount they currently take up.
968 double const scale = (ch - partial_height) / scale_height;
972 /* Scale complete tracks within the range to make it fit */
974 for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
976 if ((*i)->hidden()) {
980 if (yc.first <= 0 && yc.second >= _track_height) {
981 (*i)->set_height (max (TimeAxisView::preset_height (HeightSmall), (uint32_t) ((*i)->effective_height() * scale)), TimeAxisView::TotalHeight);
984 yc.first -= _track_height;
985 yc.second -= _track_height;
988 _editor->_routes->resume_redisplay ();
990 set_editor_y (y.first);
994 EditorSummary::playhead_position_changed (framepos_t p)
996 int const o = int (_last_playhead);
997 int const n = int (playhead_frame_to_position (p));
998 if (_session && o != n) {
999 int a = max(2, min (o, n));
1001 set_overlays_dirty (a - 2, 0, b + 2, get_height ());
1006 EditorSummary::summary_y_to_editor (double y) const
1009 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
1011 if ((*i)->hidden()) {
1015 double const h = (*i)->effective_height ();
1016 if (y < _track_height) {
1018 return ey + y * h / _track_height;
1029 EditorSummary::editor_y_to_summary (double y) const
1032 for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
1034 if ((*i)->hidden()) {
1038 double const h = (*i)->effective_height ();
1041 return sy + y * _track_height / h;
1044 sy += _track_height;
1052 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
1054 for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
1055 /* Connect to the relevant signal for the route so that we know when its colour has changed */
1056 (*i)->route()->presentation_info().PropertyChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
1057 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> ((*i)->route ());
1059 tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context ());
1063 set_background_dirty ();
1067 EditorSummary::route_gui_changed (PBD::PropertyChange const& what_changed)
1069 if (what_changed.contains (Properties::color)) {
1070 set_background_dirty ();
1075 EditorSummary::playhead_frame_to_position (framepos_t t) const
1077 return (t - _start) * _x_scale;
1081 EditorSummary::position_to_playhead_frame_to_position (double pos) const
1083 return _start + (pos * _x_scale);