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