change packing options within mixer window to fix #4271
[ardour.git] / gtk2_ardour / editor_summary.cc
1 /*
2     Copyright (C) 2009 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #include "ardour/session.h"
21 #include "time_axis_view.h"
22 #include "streamview.h"
23 #include "editor_summary.h"
24 #include "gui_thread.h"
25 #include "editor.h"
26 #include "region_view.h"
27 #include "rgb_macros.h"
28 #include "keyboard.h"
29 #include "editor_routes.h"
30 #include "editor_cursors.h"
31 #include "mouse_cursors.h"
32 #include "route_time_axis.h"
33
34 using namespace std;
35 using namespace ARDOUR;
36 using Gtkmm2ext::Keyboard;
37
38 /** Construct an EditorSummary.
39  *  @param e Editor to represent.
40  */
41 EditorSummary::EditorSummary (Editor* e)
42         : EditorComponent (e),
43           _start (0),
44           _end (1),
45           _overhang_fraction (0.1),
46           _x_scale (1),
47           _track_height (16),
48           _last_playhead (-1),
49           _move_dragging (false),
50           _moved (false),
51           _view_rectangle_x (0, 0),
52           _view_rectangle_y (0, 0),
53           _zoom_dragging (false),
54           _old_follow_playhead (false)
55 {
56         Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context());
57         _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), ui_bind (&EditorSummary::playhead_position_changed, this, _1), gui_context());
58
59         add_events (Gdk::POINTER_MOTION_MASK);
60 }
61
62 /** Connect to a session.
63  *  @param s Session.
64  */
65 void
66 EditorSummary::set_session (Session* s)
67 {
68         SessionHandlePtr::set_session (s);
69
70         set_dirty ();
71
72         /* Note: the EditorSummary already finds out about new regions from Editor::region_view_added
73          * (which attaches to StreamView::RegionViewAdded), and cut regions by the RegionPropertyChanged
74          * emitted when a cut region is added to the `cutlist' playlist.
75          */
76
77         if (_session) {
78                 _session->StartTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_dirty, this), gui_context());
79                 _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_dirty, this), gui_context());
80         }
81 }
82
83 /** Handle an expose event.
84  *  @param event Event from GTK.
85  */
86 bool
87 EditorSummary::on_expose_event (GdkEventExpose* event)
88 {
89         CairoWidget::on_expose_event (event);
90
91         if (_session == 0) {
92                 return false;
93         }
94
95         cairo_t* cr = gdk_cairo_create (get_window()->gobj());
96
97         /* Render the view rectangle.  If there is an editor visual pending, don't update
98            the view rectangle now --- wait until the expose event that we'll get after
99            the visual change.  This prevents a flicker.
100         */
101
102         if (_editor->pending_visual_change.idle_handler_id < 0) {
103                 get_editor (&_view_rectangle_x, &_view_rectangle_y);
104         }
105
106         cairo_move_to (cr, _view_rectangle_x.first, _view_rectangle_y.first);
107         cairo_line_to (cr, _view_rectangle_x.second, _view_rectangle_y.first);
108         cairo_line_to (cr, _view_rectangle_x.second, _view_rectangle_y.second);
109         cairo_line_to (cr, _view_rectangle_x.first, _view_rectangle_y.second);
110         cairo_line_to (cr, _view_rectangle_x.first, _view_rectangle_y.first);
111         cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
112         cairo_fill_preserve (cr);
113         cairo_set_line_width (cr, 1);
114         cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
115         cairo_stroke (cr);
116
117         /* Playhead */
118
119         cairo_set_line_width (cr, 1);
120         /* XXX: colour should be set from configuration file */
121         cairo_set_source_rgba (cr, 1, 0, 0, 1);
122
123         double const p = (_editor->playhead_cursor->current_frame - _start) * _x_scale;
124         cairo_move_to (cr, p, 0);
125         cairo_line_to (cr, p, _height);
126         cairo_stroke (cr);
127         _last_playhead = p;
128
129         cairo_destroy (cr);
130
131         return true;
132 }
133
134 /** Render the required regions to a cairo context.
135  *  @param cr Context.
136  */
137 void
138 EditorSummary::render (cairo_t* cr)
139 {
140         /* background */
141
142         cairo_set_source_rgb (cr, 0, 0, 0);
143         cairo_rectangle (cr, 0, 0, _width, _height);
144         cairo_fill (cr);
145
146         if (_session == 0) {
147                 return;
148         }
149
150         /* compute start and end points for the summary */
151
152         framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
153         double const theoretical_start = _session->current_start_frame() - session_length * _overhang_fraction;
154         _start = theoretical_start > 0 ? theoretical_start : 0;
155         _end = _session->current_end_frame() + session_length * _overhang_fraction;
156
157         /* compute track height */
158         int N = 0;
159         for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
160                 if (!(*i)->hidden()) {
161                         ++N;
162                 }
163         }
164
165         if (N == 0) {
166                 _track_height = 16;
167         } else {
168                 _track_height = (double) _height / N;
169         }
170
171         /* calculate x scale */
172         if (_end != _start) {
173                 _x_scale = static_cast<double> (_width) / (_end - _start);
174         } else {
175                 _x_scale = 1;
176         }
177
178         /* render tracks and regions */
179
180         double y = 0;
181         for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
182
183                 if ((*i)->hidden()) {
184                         continue;
185                 }
186
187                 cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
188                 cairo_set_line_width (cr, _track_height - 2);
189                 cairo_move_to (cr, 0, y + _track_height / 2);
190                 cairo_line_to (cr, _width, y + _track_height / 2);
191                 cairo_stroke (cr);
192
193                 StreamView* s = (*i)->view ();
194
195                 if (s) {
196                         cairo_set_line_width (cr, _track_height * 0.6);
197
198                         s->foreach_regionview (sigc::bind (
199                                                        sigc::mem_fun (*this, &EditorSummary::render_region),
200                                                        cr,
201                                                        y + _track_height / 2
202                                                        ));
203                 }
204
205                 y += _track_height;
206         }
207
208         /* start and end markers */
209
210         cairo_set_line_width (cr, 1);
211         cairo_set_source_rgb (cr, 1, 1, 0);
212
213         double const p = (_session->current_start_frame() - _start) * _x_scale;
214         cairo_move_to (cr, p, 0);
215         cairo_line_to (cr, p, _height);
216         cairo_stroke (cr);
217
218         double const q = (_session->current_end_frame() - _start) * _x_scale;
219         cairo_move_to (cr, q, 0);
220         cairo_line_to (cr, q, _height);
221         cairo_stroke (cr);
222 }
223
224 /** Render a region for the summary.
225  *  @param r Region view.
226  *  @param cr Cairo context.
227  *  @param y y coordinate to render at.
228  */
229 void
230 EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const
231 {
232         uint32_t const c = r->get_fill_color ();
233         cairo_set_source_rgb (cr, UINT_RGBA_R (c) / 255.0, UINT_RGBA_G (c) / 255.0, UINT_RGBA_B (c) / 255.0);
234
235         if (r->region()->position() > _start) {
236                 cairo_move_to (cr, (r->region()->position() - _start) * _x_scale, y);
237         } else {
238                 cairo_move_to (cr, 0, y);
239         }
240
241         if ((r->region()->position() + r->region()->length()) > _start) {
242                 cairo_line_to (cr, ((r->region()->position() - _start + r->region()->length())) * _x_scale, y);
243         } else {
244                 cairo_line_to (cr, 0, y);
245         }
246
247         cairo_stroke (cr);
248 }
249
250 /** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
251 void
252 EditorSummary::set_overlays_dirty ()
253 {
254         ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty)
255         queue_draw ();
256 }
257
258 /** Handle a size request.
259  *  @param req GTK requisition
260  */
261 void
262 EditorSummary::on_size_request (Gtk::Requisition *req)
263 {
264         /* Use a dummy, small width and the actual height that we want */
265         req->width = 64;
266         req->height = 32;
267 }
268
269
270 void
271 EditorSummary::centre_on_click (GdkEventButton* ev)
272 {
273         pair<double, double> xr;
274         pair<double, double> yr;
275         get_editor (&xr, &yr);
276
277         double const w = xr.second - xr.first;
278         double ex = ev->x - w / 2;
279         if (ex < 0) {
280                 ex = 0;
281         } else if ((ex + w) > _width) {
282                 ex = _width - w;
283         }
284
285         double const h = yr.second - yr.first;
286         double ey = ev->y - h / 2;
287         if (ey < 0) {
288                 ey = 0;
289         } else if ((ey + h) > _height) {
290                 ey = _height - h;
291         }
292
293         set_editor (ex, ey);
294 }
295
296 /** Handle a button press.
297  *  @param ev GTK event.
298  */
299 bool
300 EditorSummary::on_button_press_event (GdkEventButton* ev)
301 {
302         if (ev->button == 1) {
303
304                 pair<double, double> xr;
305                 pair<double, double> yr;
306                 get_editor (&xr, &yr);
307
308                 _start_editor_x = xr;
309                 _start_editor_y = yr;
310                 _start_mouse_x = ev->x;
311                 _start_mouse_y = ev->y;
312                 _start_position = get_position (ev->x, ev->y);
313
314                 if (_start_position != INSIDE && _start_position != BELOW_OR_ABOVE &&
315                     _start_position != TO_LEFT_OR_RIGHT && _start_position != OTHERWISE_OUTSIDE
316                         ) {
317
318                         /* start a zoom drag */
319
320                         _zoom_position = get_position (ev->x, ev->y);
321                         _zoom_dragging = true;
322                         _editor->_dragging_playhead = true;
323                         _old_follow_playhead = _editor->follow_playhead ();
324                         _editor->set_follow_playhead (false);
325
326                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
327
328                         /* secondary-modifier-click: locate playhead */
329                         if (_session) {
330                                 _session->request_locate (ev->x / _x_scale + _start);
331                         }
332
333                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
334
335                         centre_on_click (ev);
336
337                 } else {
338
339                         /* start a move drag */
340
341                         _move_dragging = true;
342                         _moved = false;
343                         _editor->_dragging_playhead = true;
344                         _old_follow_playhead = _editor->follow_playhead ();
345                         _editor->set_follow_playhead (false);
346                 }
347         }
348
349         return true;
350 }
351
352 /** Fill in x and y with the editor's current viewable area in summary coordinates */
353 void
354 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
355 {
356         assert (x);
357         assert (y);
358
359         x->first = (_editor->leftmost_position () - _start) * _x_scale;
360         x->second = x->first + _editor->current_page_frames() * _x_scale;
361
362         y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
363         y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->canvas_height() - _editor->get_canvas_timebars_vsize());
364 }
365
366 /** Get an expression of the position of a point with respect to the view rectangle */
367 EditorSummary::Position
368 EditorSummary::get_position (double x, double y) const
369 {
370         /* how close the mouse has to be to the edge of the view rectangle to be considered `on it',
371            in pixels */
372
373         int x_edge_size = (_view_rectangle_x.second - _view_rectangle_x.first) / 4;
374         x_edge_size = min (x_edge_size, 8);
375         x_edge_size = max (x_edge_size, 1);
376
377         int y_edge_size = (_view_rectangle_y.second - _view_rectangle_y.first) / 4;
378         y_edge_size = min (y_edge_size, 8);
379         y_edge_size = max (y_edge_size, 1);
380
381         bool const near_left = (std::abs (x - _view_rectangle_x.first) < x_edge_size);
382         bool const near_right = (std::abs (x - _view_rectangle_x.second) < x_edge_size);
383         bool const near_top = (std::abs (y - _view_rectangle_y.first) < y_edge_size);
384         bool const near_bottom = (std::abs (y - _view_rectangle_y.second) < y_edge_size);
385         bool const within_x = _view_rectangle_x.first < x && x < _view_rectangle_x.second;
386         bool const within_y = _view_rectangle_y.first < y && y < _view_rectangle_y.second;
387
388         if (near_left && near_top) {
389                 return LEFT_TOP;
390         } else if (near_left && near_bottom) {
391                 return LEFT_BOTTOM;
392         } else if (near_right && near_top) {
393                 return RIGHT_TOP;
394         } else if (near_right && near_bottom) {
395                 return RIGHT_BOTTOM;
396         } else if (near_left && within_y) {
397                 return LEFT;
398         } else if (near_right && within_y) {
399                 return RIGHT;
400         } else if (near_top && within_x) {
401                 return TOP;
402         } else if (near_bottom && within_x) {
403                 return BOTTOM;
404         } else if (within_x && within_y) {
405                 return INSIDE;
406         } else if (within_x) {
407                 return BELOW_OR_ABOVE;
408         } else if (within_y) {
409                 return TO_LEFT_OR_RIGHT;
410         } else {
411                 return OTHERWISE_OUTSIDE;
412         }
413 }
414
415 void
416 EditorSummary::set_cursor (Position p)
417 {
418         switch (p) {
419         case LEFT:
420                 get_window()->set_cursor (*_editor->_cursors->resize_left);
421                 break;
422         case LEFT_TOP:
423                 get_window()->set_cursor (*_editor->_cursors->resize_top_left);
424                 break;
425         case TOP:
426                 get_window()->set_cursor (*_editor->_cursors->resize_top);
427                 break;
428         case RIGHT_TOP:
429                 get_window()->set_cursor (*_editor->_cursors->resize_top_right);
430                 break;
431         case RIGHT:
432                 get_window()->set_cursor (*_editor->_cursors->resize_right);
433                 break;
434         case RIGHT_BOTTOM:
435                 get_window()->set_cursor (*_editor->_cursors->resize_bottom_right);
436                 break;
437         case BOTTOM:
438                 get_window()->set_cursor (*_editor->_cursors->resize_bottom);
439                 break;
440         case LEFT_BOTTOM:
441                 get_window()->set_cursor (*_editor->_cursors->resize_bottom_left);
442                 break;
443         case INSIDE:
444                 get_window()->set_cursor (*_editor->_cursors->move);
445                 break;
446         case TO_LEFT_OR_RIGHT:
447                 get_window()->set_cursor (*_editor->_cursors->expand_left_right);
448                 break;
449         case BELOW_OR_ABOVE:
450                 get_window()->set_cursor (*_editor->_cursors->expand_up_down);
451                 break;
452         default:
453                 get_window()->set_cursor ();
454                 break;
455         }
456 }
457
458 bool
459 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
460 {
461         pair<double, double> xr = _start_editor_x;
462         pair<double, double> yr = _start_editor_y;
463         double x = _start_editor_x.first;
464         double y = _start_editor_y.first;
465
466         if (_move_dragging) {
467
468                 _moved = true;
469
470                 /* don't alter x if we clicked outside and above or below the viewbox */
471                 if (_start_position == INSIDE || _start_position == TO_LEFT_OR_RIGHT || _start_position == OTHERWISE_OUTSIDE) {
472                         x += ev->x - _start_mouse_x;
473                 }
474
475                 /* don't alter y if we clicked outside and to the left or right of the viewbox */
476                 if (_start_position == INSIDE || _start_position == BELOW_OR_ABOVE) {
477                         y += ev->y - _start_mouse_y;
478                 }
479
480                 if (x < 0) {
481                         x = 0;
482                 }
483
484                 if (y < 0) {
485                         y = 0;
486                 }
487
488                 set_editor (x, y);
489                 set_cursor (_start_position);
490
491         } else if (_zoom_dragging) {
492
493                 double const dx = ev->x - _start_mouse_x;
494                 double const dy = ev->y - _start_mouse_y;
495
496                 if (_zoom_position == LEFT || _zoom_position == LEFT_TOP || _zoom_position == LEFT_BOTTOM) {
497                         xr.first += dx;
498                 } else if (_zoom_position == RIGHT || _zoom_position == RIGHT_TOP || _zoom_position == RIGHT_BOTTOM) {
499                         xr.second += dx;
500                 }
501
502                 if (_zoom_position == TOP || _zoom_position == LEFT_TOP || _zoom_position == RIGHT_TOP) {
503                         yr.first += dy;
504                 } else if (_zoom_position == BOTTOM || _zoom_position == LEFT_BOTTOM || _zoom_position == RIGHT_BOTTOM) {
505                         yr.second += dy;
506                 }
507
508                 set_overlays_dirty ();
509                 set_cursor (_zoom_position);
510                 set_editor (xr, yr);
511
512         } else {
513
514                 set_cursor (get_position (ev->x, ev->y));
515
516         }
517
518         return true;
519 }
520
521 bool
522 EditorSummary::on_button_release_event (GdkEventButton*)
523 {
524         _move_dragging = false;
525         _zoom_dragging = false;
526         _editor->_dragging_playhead = false;
527         _editor->set_follow_playhead (_old_follow_playhead, false);
528
529         return true;
530 }
531
532 bool
533 EditorSummary::on_scroll_event (GdkEventScroll* ev)
534 {
535         /* mouse wheel */
536
537         pair<double, double> xr;
538         pair<double, double> yr;
539         get_editor (&xr, &yr);
540         double x = xr.first;
541         double y = yr.first;
542
543         double amount = 8;
544
545         if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
546                 amount = 64;
547         } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
548                 amount = 1;
549         }
550
551         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
552
553                 /* primary-wheel == left-right scrolling */
554
555                 if (ev->direction == GDK_SCROLL_UP) {
556                         x += amount;
557                 } else if (ev->direction == GDK_SCROLL_DOWN) {
558                         x -= amount;
559                 }
560
561         } else {
562
563                 if (ev->direction == GDK_SCROLL_DOWN) {
564                         y += amount;
565                 } else if (ev->direction == GDK_SCROLL_UP) {
566                         y -= amount;
567                 } else if (ev->direction == GDK_SCROLL_LEFT) {
568                         x -= amount;
569                 } else if (ev->direction == GDK_SCROLL_RIGHT) {
570                         x += amount;
571                 }
572         }
573
574         set_editor (x, y);
575         return true;
576 }
577
578 /** Set the editor to display a x range with the left at a given position
579  *  and a y range with the top at a given position.
580  *  x and y parameters are specified in summary coordinates.
581  *  Zoom is not changed in either direction.
582  */
583 void
584 EditorSummary::set_editor (double const x, double const y)
585 {
586         if (_editor->pending_visual_change.idle_handler_id >= 0) {
587
588                 /* As a side-effect, the Editor's visual change idle handler processes
589                    pending GTK events.  Hence this motion notify handler can be called
590                    in the middle of a visual change idle handler, and if this happens,
591                    the queue_visual_change calls below modify the variables that the
592                    idle handler is working with.  This causes problems.  Hence this
593                    check.  It ensures that we won't modify the pending visual change
594                    while a visual change idle handler is in progress.  It's not perfect,
595                    as it also means that we won't change these variables if an idle handler
596                    is merely pending but not executing.  But c'est la vie.
597                 */
598
599                 return;
600         }
601
602         set_editor_x (x);
603         set_editor_y (y);
604 }
605
606 /** Set the editor to display a given x range and a y range with the top at a given position.
607  *  The editor's x zoom is adjusted if necessary, but the y zoom is not changed.
608  *  x and y parameters are specified in summary coordinates.
609  */
610 void
611 EditorSummary::set_editor (pair<double,double> const & x, double const y)
612 {
613         if (_editor->pending_visual_change.idle_handler_id >= 0) {
614                 /* see comment in other set_editor () */
615                 return;
616         }
617
618         set_editor_x (x);
619         set_editor_y (y);
620 }
621
622 /** Set the editor to display given x and y ranges.  x zoom and track heights are
623  *  adjusted if necessary.
624  *  x and y parameters are specified in summary coordinates.
625  */
626 void
627 EditorSummary::set_editor (pair<double,double> const & x, pair<double, double> const & y)
628 {
629         if (_editor->pending_visual_change.idle_handler_id >= 0) {
630                 /* see comment in other set_editor () */
631                 return;
632         }
633
634         set_editor_x (x);
635         set_editor_y (y);
636 }
637
638 /** Set the left of the x range visible in the editor.
639  *  Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
640  *  @param x new x left position in summary coordinates.
641  */
642 void
643 EditorSummary::set_editor_x (double const x)
644 {
645         _editor->reset_x_origin (x / _x_scale + _start);
646 }
647
648 /** Set the x range visible in the editor.
649  *  Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
650  *  @param x new x range in summary coordinates.
651  */
652 void
653 EditorSummary::set_editor_x (pair<double, double> const & x)
654 {
655         _editor->reset_x_origin (x.first / _x_scale + _start);
656
657         double const nx = (
658                 ((x.second - x.first) / _x_scale) /
659                 _editor->frame_to_unit (_editor->current_page_frames())
660                 );
661
662         if (nx != _editor->get_current_zoom ()) {
663                 _editor->reset_zoom (nx);
664         }
665 }
666
667 /** Set the top of the y range visible in the editor.
668  *  Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
669  *  @param y new editor top in summary coodinates.
670  */
671 void
672 EditorSummary::set_editor_y (double const y)
673 {
674         double y1 = summary_y_to_editor (y);
675         double const eh = _editor->canvas_height() - _editor->get_canvas_timebars_vsize ();
676         double y2 = y1 + eh;
677
678         double const full_editor_height = _editor->full_canvas_height - _editor->get_canvas_timebars_vsize();
679
680         if (y2 > full_editor_height) {
681                 y1 -= y2 - full_editor_height;
682         }
683
684         if (y1 < 0) {
685                 y1 = 0;
686         }
687
688         _editor->reset_y_origin (y1);
689 }
690
691 /** Set the y range visible in the editor.  This is achieved by scaling track heights,
692  *  if necessary.
693  *  Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
694  *  @param y new editor range in summary coodinates.
695  */
696 void
697 EditorSummary::set_editor_y (pair<double, double> const & y)
698 {
699         /* Compute current height of tracks between y.first and y.second.  We add up
700            the total height into `total_height' and the height of complete tracks into
701            `scale height'.
702         */
703
704         /* Copy of target range for use below */
705         pair<double, double> yc = y;
706         /* Total height of all tracks */
707         double total_height = 0;
708         /* Height of any parts of tracks that aren't fully in the desired range */
709         double partial_height = 0;
710         /* Height of any tracks that are fully in the desired range */
711         double scale_height = 0;
712
713         _editor->_routes->suspend_redisplay ();
714
715         for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
716
717                 if ((*i)->hidden()) {
718                         continue;
719                 }
720
721                 double const h = (*i)->effective_height ();
722                 total_height += h;
723
724                 if (yc.first > 0 && yc.first < _track_height) {
725                         partial_height += (_track_height - yc.first) * h / _track_height;
726                 } else if (yc.first <= 0 && yc.second >= _track_height) {
727                         scale_height += h;
728                 } else if (yc.second > 0 && yc.second < _track_height) {
729                         partial_height += yc.second * h / _track_height;
730                         break;
731                 }
732
733                 yc.first -= _track_height;
734                 yc.second -= _track_height;
735         }
736
737         /* Height that we will use for scaling; use the whole editor height unless there are not
738            enough tracks to fill it.
739         */
740         double const ch = min (total_height, _editor->canvas_height() - _editor->get_canvas_timebars_vsize());
741
742         /* hence required scale factor of the complete tracks to fit the required y range;
743            the amount of space they should take up divided by the amount they currently take up.
744         */
745         double const scale = (ch - partial_height) / scale_height;
746
747         yc = y;
748
749         /* Scale complete tracks within the range to make it fit */
750
751         for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
752
753                 if ((*i)->hidden()) {
754                         continue;
755                 }
756
757                 if (yc.first <= 0 && yc.second >= _track_height) {
758                         (*i)->set_height (max (TimeAxisView::preset_height (HeightSmall), (uint32_t) ((*i)->effective_height() * scale)));
759                 }
760
761                 yc.first -= _track_height;
762                 yc.second -= _track_height;
763         }
764
765         _editor->_routes->resume_redisplay ();
766
767         set_editor_y (y.first);
768 }
769
770 void
771 EditorSummary::playhead_position_changed (framepos_t p)
772 {
773         if (_session && int (p * _x_scale) != int (_last_playhead)) {
774                 set_overlays_dirty ();
775         }
776 }
777
778 double
779 EditorSummary::summary_y_to_editor (double y) const
780 {
781         double ey = 0;
782         for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
783
784                 if ((*i)->hidden()) {
785                         continue;
786                 }
787
788                 double const h = (*i)->effective_height ();
789                 if (y < _track_height) {
790                         /* in this track */
791                         return ey + y * h / _track_height;
792                 }
793
794                 ey += h;
795                 y -= _track_height;
796         }
797
798         return ey;
799 }
800
801 double
802 EditorSummary::editor_y_to_summary (double y) const
803 {
804         double sy = 0;
805         for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
806
807                 if ((*i)->hidden()) {
808                         continue;
809                 }
810
811                 double const h = (*i)->effective_height ();
812                 if (y < h) {
813                         /* in this track */
814                         return sy + y * _track_height / h;
815                 }
816
817                 sy += _track_height;
818                 y -= h;
819         }
820
821         return sy;
822 }
823
824 void
825 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
826 {
827         /* Connect to gui_changed() on the routes so that we know when their colour has changed */
828         for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
829                 (*i)->route()->gui_changed.connect (*this, invalidator (*this), ui_bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
830         }
831
832         set_dirty ();
833 }
834
835 void
836 EditorSummary::route_gui_changed (string c)
837 {
838         if (c == "color") {
839                 set_dirty ();
840         }
841 }