Fix up summary mouse cursors.
[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         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 || _start_position == OTHERWISE_OUTSIDE) {
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                         xr.first += dx;
488                 } else if (_zoom_position == RIGHT || _zoom_position == RIGHT_TOP || _zoom_position == RIGHT_BOTTOM) {
489                         xr.second += dx;
490                 }
491
492                 if (_zoom_position == TOP || _zoom_position == LEFT_TOP || _zoom_position == RIGHT_TOP) {
493                         yr.first += dy;
494                 } else if (_zoom_position == BOTTOM || _zoom_position == LEFT_BOTTOM || _zoom_position == RIGHT_BOTTOM) {
495                         yr.second += dy;
496                 }
497
498                 set_overlays_dirty ();
499                 set_cursor (_zoom_position);
500                 set_editor (xr, yr);
501
502         } else {
503
504                 set_cursor (get_position (ev->x, ev->y));
505
506         }
507
508         return true;
509 }
510
511 bool
512 EditorSummary::on_button_release_event (GdkEventButton*)
513 {
514         _move_dragging = false;
515         _zoom_dragging = false;
516         _editor->_dragging_playhead = false;
517         return true;
518 }
519
520 bool
521 EditorSummary::on_scroll_event (GdkEventScroll* ev)
522 {
523         /* mouse wheel */
524
525         pair<double, double> xr;
526         pair<double, double> yr;
527         get_editor (&xr, &yr);
528         double y = yr.first;
529
530         double amount = 8;
531
532         if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
533                 amount = 64;
534         } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
535                 amount = 1;
536         }
537
538         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
539
540                 /* primary-wheel == left-right scrolling */
541
542                 if (ev->direction == GDK_SCROLL_UP) {
543                         xr.first += amount;
544                         xr.second += amount;
545                 } else if (ev->direction == GDK_SCROLL_DOWN) {
546                         xr.first -= amount;
547                         xr.second -= amount;
548                 }
549
550         } else {
551
552                 if (ev->direction == GDK_SCROLL_DOWN) {
553                         y += amount;
554                 } else if (ev->direction == GDK_SCROLL_UP) {
555                         y -= amount;
556                 } else if (ev->direction == GDK_SCROLL_LEFT) {
557                         xr.first -= amount;
558                         xr.second -= amount;
559                 } else if (ev->direction == GDK_SCROLL_RIGHT) {
560                         xr.first += amount;
561                         xr.second += amount;
562                 }
563         }
564
565         set_editor (xr, y);
566         return true;
567 }
568
569 /** Set the editor to display a given x range and a y range with the top at a given position.
570  *  The editor's x zoom is adjusted if necessary, but the y zoom is not changed.
571  *  x and y parameters are specified in summary coordinates.
572  */
573 void
574 EditorSummary::set_editor (pair<double,double> const & x, double const y)
575 {
576         if (_editor->pending_visual_change.idle_handler_id >= 0) {
577
578                 /* As a side-effect, the Editor's visual change idle handler processes
579                    pending GTK events.  Hence this motion notify handler can be called
580                    in the middle of a visual change idle handler, and if this happens,
581                    the queue_visual_change calls below modify the variables that the
582                    idle handler is working with.  This causes problems.  Hence this
583                    check.  It ensures that we won't modify the pending visual change
584                    while a visual change idle handler is in progress.  It's not perfect,
585                    as it also means that we won't change these variables if an idle handler
586                    is merely pending but not executing.  But c'est la vie.
587                 */
588
589                 return;
590         }
591         
592         set_editor_x (x);
593         set_editor_y (y);
594 }
595
596 /** Set the editor to display given x and y ranges.  x zoom and track heights are
597  *  adjusted if necessary.
598  *  x and y parameters are specified in summary coordinates.
599  */
600 void
601 EditorSummary::set_editor (pair<double,double> const & x, pair<double, double> const & y)
602 {
603         if (_editor->pending_visual_change.idle_handler_id >= 0) {
604                 /* see comment in other set_editor () */
605                 return;
606         }
607         
608         set_editor_x (x);
609         set_editor_y (y);
610 }
611
612 /** Set the x range visible in the editor.
613  *  Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
614  *  @param x new x range in summary coordinates.
615  */
616 void
617 EditorSummary::set_editor_x (pair<double, double> const & x)
618 {
619         _editor->reset_x_origin (x.first / _x_scale + _start);
620
621         double const nx = (
622                 ((x.second - x.first) / _x_scale) /
623                 _editor->frame_to_unit (_editor->current_page_frames())
624                 );
625         
626         if (nx != _editor->get_current_zoom ()) {
627                 _editor->reset_zoom (nx);
628         }       
629 }
630
631 /** Set the top of the y range visible in the editor.
632  *  Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
633  *  @param y new editor top in summary coodinates.
634  */
635 void
636 EditorSummary::set_editor_y (double const y)
637 {
638         double y1 = summary_y_to_editor (y);
639         double const eh = _editor->canvas_height() - _editor->get_canvas_timebars_vsize ();
640         double y2 = y1 + eh;
641         
642         double const full_editor_height = _editor->full_canvas_height - _editor->get_canvas_timebars_vsize();
643
644         if (y2 > full_editor_height) {
645                 y1 -= y2 - full_editor_height;
646         }
647         
648         if (y1 < 0) {
649                 y1 = 0;
650         }
651
652         _editor->reset_y_origin (y1);
653 }
654
655 /** Set the y range visible in the editor.  This is achieved by scaling track heights,
656  *  if necessary.
657  *  Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
658  *  @param y new editor range in summary coodinates.
659  */
660 void
661 EditorSummary::set_editor_y (pair<double, double> const & y)
662 {
663         /* Compute current height of tracks between y.first and y.second.  We add up
664            the total height into `total_height' and the height of complete tracks into
665            `scale height'.
666         */
667         pair<double, double> yc = y;
668         double total_height = 0;
669         double scale_height = 0;
670         
671         _editor->_routes->suspend_redisplay ();
672
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 (max (TimeAxisView::preset_height (HeightSmall), (uint32_t) ((*i)->effective_height() * scale)));
710                 }
711
712                 yc.first -= _track_height;
713                 yc.second -= _track_height;
714         }
715
716         _editor->_routes->resume_redisplay ();
717         
718         set_editor_y (y.first);
719 }
720
721 void
722 EditorSummary::playhead_position_changed (framepos_t p)
723 {
724         if (_session && int (p * _x_scale) != int (_last_playhead)) {
725                 set_overlays_dirty ();
726         }
727 }
728
729 double
730 EditorSummary::summary_y_to_editor (double y) const
731 {
732         double ey = 0;
733         for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
734                 
735                 if ((*i)->hidden()) {
736                         continue;
737                 }
738                 
739                 double const h = (*i)->effective_height ();
740                 if (y < _track_height) {
741                         /* in this track */
742                         return ey + y * h / _track_height;
743                 }
744
745                 ey += h;
746                 y -= _track_height;
747         }
748
749         return ey;
750 }
751
752 double
753 EditorSummary::editor_y_to_summary (double y) const
754 {
755         double sy = 0;
756         for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
757                 
758                 if ((*i)->hidden()) {
759                         continue;
760                 }
761
762                 double const h = (*i)->effective_height ();
763                 if (y < h) {
764                         /* in this track */
765                         return sy + y * _track_height / h;
766                 }
767
768                 sy += _track_height;
769                 y -= h;
770         }
771
772         return sy;
773 }