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