Fix insertion of processors at the point at which the processor menu was opened;...
[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 x)
644 {
645         if (x < 0) {
646                 x = 0;
647         }
648         
649         _editor->reset_x_origin (x / _x_scale + _start);
650 }
651
652 /** Set the x range visible in the editor.
653  *  Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
654  *  @param x new x range in summary coordinates.
655  */
656 void
657 EditorSummary::set_editor_x (pair<double, double> x)
658 {
659         if (x.first < 0) {
660                 x.first = 0;
661         }
662
663         if (x.second < 0) {
664                 x.second = x.first + 1;
665         }
666         
667         _editor->reset_x_origin (x.first / _x_scale + _start);
668
669         double const nx = (
670                 ((x.second - x.first) / _x_scale) /
671                 _editor->frame_to_unit (_editor->current_page_frames())
672                 );
673
674         if (nx != _editor->get_current_zoom ()) {
675                 _editor->reset_zoom (nx);
676         }
677 }
678
679 /** Set the top of the y range visible in the editor.
680  *  Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
681  *  @param y new editor top in summary coodinates.
682  */
683 void
684 EditorSummary::set_editor_y (double const y)
685 {
686         double y1 = summary_y_to_editor (y);
687         double const eh = _editor->canvas_height() - _editor->get_canvas_timebars_vsize ();
688         double y2 = y1 + eh;
689
690         double const full_editor_height = _editor->full_canvas_height - _editor->get_canvas_timebars_vsize();
691
692         if (y2 > full_editor_height) {
693                 y1 -= y2 - full_editor_height;
694         }
695
696         if (y1 < 0) {
697                 y1 = 0;
698         }
699
700         _editor->reset_y_origin (y1);
701 }
702
703 /** Set the y range visible in the editor.  This is achieved by scaling track heights,
704  *  if necessary.
705  *  Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
706  *  @param y new editor range in summary coodinates.
707  */
708 void
709 EditorSummary::set_editor_y (pair<double, double> const y)
710 {
711         /* Compute current height of tracks between y.first and y.second.  We add up
712            the total height into `total_height' and the height of complete tracks into
713            `scale height'.
714         */
715
716         /* Copy of target range for use below */
717         pair<double, double> yc = y;
718         /* Total height of all tracks */
719         double total_height = 0;
720         /* Height of any parts of tracks that aren't fully in the desired range */
721         double partial_height = 0;
722         /* Height of any tracks that are fully in the desired range */
723         double scale_height = 0;
724
725         _editor->_routes->suspend_redisplay ();
726
727         for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
728
729                 if ((*i)->hidden()) {
730                         continue;
731                 }
732
733                 double const h = (*i)->effective_height ();
734                 total_height += h;
735
736                 if (yc.first > 0 && yc.first < _track_height) {
737                         partial_height += (_track_height - yc.first) * h / _track_height;
738                 } else if (yc.first <= 0 && yc.second >= _track_height) {
739                         scale_height += h;
740                 } else if (yc.second > 0 && yc.second < _track_height) {
741                         partial_height += yc.second * h / _track_height;
742                         break;
743                 }
744
745                 yc.first -= _track_height;
746                 yc.second -= _track_height;
747         }
748
749         /* Height that we will use for scaling; use the whole editor height unless there are not
750            enough tracks to fill it.
751         */
752         double const ch = min (total_height, _editor->canvas_height() - _editor->get_canvas_timebars_vsize());
753
754         /* hence required scale factor of the complete tracks to fit the required y range;
755            the amount of space they should take up divided by the amount they currently take up.
756         */
757         double const scale = (ch - partial_height) / scale_height;
758
759         yc = y;
760
761         /* Scale complete tracks within the range to make it fit */
762
763         for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
764
765                 if ((*i)->hidden()) {
766                         continue;
767                 }
768
769                 if (yc.first <= 0 && yc.second >= _track_height) {
770                         (*i)->set_height (max (TimeAxisView::preset_height (HeightSmall), (uint32_t) ((*i)->effective_height() * scale)));
771                 }
772
773                 yc.first -= _track_height;
774                 yc.second -= _track_height;
775         }
776
777         _editor->_routes->resume_redisplay ();
778
779         set_editor_y (y.first);
780 }
781
782 void
783 EditorSummary::playhead_position_changed (framepos_t p)
784 {
785         if (_session && int (p * _x_scale) != int (_last_playhead)) {
786                 set_overlays_dirty ();
787         }
788 }
789
790 double
791 EditorSummary::summary_y_to_editor (double y) const
792 {
793         double ey = 0;
794         for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
795
796                 if ((*i)->hidden()) {
797                         continue;
798                 }
799
800                 double const h = (*i)->effective_height ();
801                 if (y < _track_height) {
802                         /* in this track */
803                         return ey + y * h / _track_height;
804                 }
805
806                 ey += h;
807                 y -= _track_height;
808         }
809
810         return ey;
811 }
812
813 double
814 EditorSummary::editor_y_to_summary (double y) const
815 {
816         double sy = 0;
817         for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
818
819                 if ((*i)->hidden()) {
820                         continue;
821                 }
822
823                 double const h = (*i)->effective_height ();
824                 if (y < h) {
825                         /* in this track */
826                         return sy + y * _track_height / h;
827                 }
828
829                 sy += _track_height;
830                 y -= h;
831         }
832
833         return sy;
834 }
835
836 void
837 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
838 {
839         /* Connect to gui_changed() on the routes so that we know when their colour has changed */
840         for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
841                 (*i)->route()->gui_changed.connect (*this, invalidator (*this), ui_bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
842         }
843
844         set_dirty ();
845 }
846
847 void
848 EditorSummary::route_gui_changed (string c)
849 {
850         if (c == "color") {
851                 set_dirty ();
852         }
853 }