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