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