crossfade hack and slash. removed overlap checks, overlap mode, default length,...
[ardour.git] / gtk2_ardour / shuttle_control.cc
1 /*
2     Copyright (C) 2011 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 #include <algorithm>
20
21 #include <cairo/cairo.h>
22
23 #include "ardour/ardour.h"
24 #include "ardour/audioengine.h"
25 #include "ardour/rc_configuration.h"
26 #include "ardour/session.h"
27
28 #include "gtkmm2ext/keyboard.h"
29 #include "gtkmm2ext/gui_thread.h"
30 #include "gtkmm2ext/cairocell.h"
31 #include "gtkmm2ext/utils.h"
32 #include "gtkmm2ext/rgb_macros.h"
33
34 #include "ardour_ui.h"
35 #include "rgb_macros.h"
36 #include "shuttle_control.h"
37
38 #include "i18n.h"
39
40 using namespace Gtk;
41 using namespace Gtkmm2ext;
42 using namespace ARDOUR;
43 using std::min;
44 using std::max;
45
46 gboolean qt (gboolean, gint, gint, gboolean, Gtk::Tooltip*, gpointer)
47 {
48         return FALSE;
49 }
50
51 ShuttleControl::ShuttleControl ()
52         : _controllable (new ShuttleControllable (*this))
53         , binding_proxy (_controllable)
54 {
55         ARDOUR_UI::instance()->set_tip (*this, _("Shuttle speed control (Context-click for options)"));
56
57         pattern = 0;
58         shine_pattern = 0;
59         last_shuttle_request = 0;
60         last_speed_displayed = -99999999;
61         shuttle_grabbed = false;
62         shuttle_speed_on_grab = 0;
63         shuttle_fract = 0.0;
64         shuttle_max_speed = 8.0f;
65         shuttle_style_menu = 0;
66         shuttle_unit_menu = 0;
67         shuttle_context_menu = 0;
68         _hovering = false;
69
70         set_flags (CAN_FOCUS);
71         add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK);
72         set_size_request (85, 20);
73         set_name (X_("ShuttleControl"));
74
75         Config->ParameterChanged.connect (parameter_connection, MISSING_INVALIDATOR, boost::bind (&ShuttleControl::parameter_changed, this, _1), gui_context());
76
77         /* gtkmm 2.4: the C++ wrapper doesn't work */
78         g_signal_connect ((GObject*) gobj(), "query-tooltip", G_CALLBACK (qt), NULL);
79         // signal_query_tooltip().connect (sigc::mem_fun (*this, &ShuttleControl::on_query_tooltip));
80 }
81
82 ShuttleControl::~ShuttleControl ()
83 {
84         cairo_pattern_destroy (pattern);
85         cairo_pattern_destroy (shine_pattern);
86 }
87
88 void
89 ShuttleControl::set_session (Session *s)
90 {
91         SessionHandlePtr::set_session (s);
92
93         if (_session) {
94                 set_sensitive (true);
95                 _session->add_controllable (_controllable);
96         } else {
97                 set_sensitive (false);
98         }
99 }
100
101 void
102 ShuttleControl::on_size_allocate (Gtk::Allocation& alloc)
103 {
104         if (pattern) {
105                 cairo_pattern_destroy (pattern);
106                 pattern = 0;
107                 cairo_pattern_destroy (shine_pattern);
108                 shine_pattern = 0;
109         }
110
111         CairoWidget::on_size_allocate ( alloc);
112
113         //background
114         pattern = cairo_pattern_create_linear (0, 0, 0, alloc.get_height());
115         uint32_t col = ARDOUR_UI::config()->canvasvar_Shuttle.get();
116         int r,b,g,a;
117         UINT_TO_RGBA(col, &r, &g, &b, &a);
118         cairo_pattern_add_color_stop_rgb (pattern, 0.0, r/400.0, g/400.0, b/400.0);
119         cairo_pattern_add_color_stop_rgb (pattern, 0.4, r/255.0, g/255.0, b/255.0);
120         cairo_pattern_add_color_stop_rgb (pattern, 1.0, r/512.0, g/512.0, b/512.0);
121
122         //reflection
123         shine_pattern = cairo_pattern_create_linear (0.0, 0.0, 0.0, 10);
124         cairo_pattern_add_color_stop_rgba (shine_pattern, 0, 1,1,1,0.0);
125         cairo_pattern_add_color_stop_rgba (shine_pattern, 0.2, 1,1,1,0.4);
126         cairo_pattern_add_color_stop_rgba (shine_pattern, 1, 1,1,1,0.1);
127 }
128
129 void
130 ShuttleControl::map_transport_state ()
131 {
132         float speed = _session->transport_speed ();
133
134         if (fabs(speed) <= (2*DBL_EPSILON)) {
135                 shuttle_fract = 0;
136         } else {
137                 if (Config->get_shuttle_units() == Semitones) {
138                         bool reverse;
139                         int semi = speed_as_semitones (speed, reverse);
140                         shuttle_fract = semitones_as_fract (semi, reverse);
141                 } else {
142                         shuttle_fract = speed/shuttle_max_speed;
143                 }
144         }
145
146         queue_draw ();
147 }
148
149 void
150 ShuttleControl::build_shuttle_context_menu ()
151 {
152         using namespace Menu_Helpers;
153
154         shuttle_context_menu = new Menu();
155         MenuList& items = shuttle_context_menu->items();
156
157         Menu* speed_menu = manage (new Menu());
158         MenuList& speed_items = speed_menu->items();
159
160         Menu* units_menu = manage (new Menu);
161         MenuList& units_items = units_menu->items();
162         RadioMenuItem::Group units_group;
163
164         units_items.push_back (RadioMenuElem (units_group, _("Percent"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_units), Percentage)));
165         if (Config->get_shuttle_units() == Percentage) {
166                 static_cast<RadioMenuItem*>(&units_items.back())->set_active();
167         }
168         units_items.push_back (RadioMenuElem (units_group, _("Semitones"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_units), Semitones)));
169         if (Config->get_shuttle_units() == Semitones) {
170                 static_cast<RadioMenuItem*>(&units_items.back())->set_active();
171         }
172         items.push_back (MenuElem (_("Units"), *units_menu));
173
174         Menu* style_menu = manage (new Menu);
175         MenuList& style_items = style_menu->items();
176         RadioMenuItem::Group style_group;
177
178         style_items.push_back (RadioMenuElem (style_group, _("Sprung"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_style), Sprung)));
179         if (Config->get_shuttle_behaviour() == Sprung) {
180                 static_cast<RadioMenuItem*>(&style_items.back())->set_active();
181         }
182         style_items.push_back (RadioMenuElem (style_group, _("Wheel"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_style), Wheel)));
183         if (Config->get_shuttle_behaviour() == Wheel) {
184                 static_cast<RadioMenuItem*>(&style_items.back())->set_active();
185         }
186
187         items.push_back (MenuElem (_("Mode"), *style_menu));
188
189         RadioMenuItem::Group speed_group;
190
191         speed_items.push_back (RadioMenuElem (speed_group, "8", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 8.0f)));
192         if (shuttle_max_speed == 8.0) {
193                 static_cast<RadioMenuItem*>(&speed_items.back())->set_active ();
194         }
195         speed_items.push_back (RadioMenuElem (speed_group, "6", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 6.0f)));
196         if (shuttle_max_speed == 6.0) {
197                 static_cast<RadioMenuItem*>(&speed_items.back())->set_active ();
198         }
199         speed_items.push_back (RadioMenuElem (speed_group, "4", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 4.0f)));
200         if (shuttle_max_speed == 4.0) {
201                 static_cast<RadioMenuItem*>(&speed_items.back())->set_active ();
202         }
203         speed_items.push_back (RadioMenuElem (speed_group, "3", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 3.0f)));
204         if (shuttle_max_speed == 3.0) {
205                 static_cast<RadioMenuItem*>(&speed_items.back())->set_active ();
206         }
207         speed_items.push_back (RadioMenuElem (speed_group, "2", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 2.0f)));
208         if (shuttle_max_speed == 2.0) {
209                 static_cast<RadioMenuItem*>(&speed_items.back())->set_active ();
210         }
211         speed_items.push_back (RadioMenuElem (speed_group, "1.5", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 1.5f)));
212         if (shuttle_max_speed == 1.5) {
213                 static_cast<RadioMenuItem*>(&speed_items.back())->set_active ();
214         }
215
216         items.push_back (MenuElem (_("Maximum speed"), *speed_menu));
217
218 }
219
220 void
221 ShuttleControl::show_shuttle_context_menu ()
222 {
223         if (shuttle_context_menu == 0) {
224                 build_shuttle_context_menu ();
225         }
226
227         shuttle_context_menu->popup (1, gtk_get_current_event_time());
228 }
229
230 void
231 ShuttleControl::set_shuttle_max_speed (float speed)
232 {
233         shuttle_max_speed = speed;
234 }
235
236 bool
237 ShuttleControl::on_button_press_event (GdkEventButton* ev)
238 {
239         if (!_session) {
240                 return true;
241         }
242
243         if (binding_proxy.button_press_handler (ev)) {
244                 return true;
245         }
246
247         if (Keyboard::is_context_menu_event (ev)) {
248                 show_shuttle_context_menu ();
249                 return true;
250         }
251
252         switch (ev->button) {
253         case 1:
254                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
255                         if (_session->transport_rolling()) {
256                                 _session->request_transport_speed (1.0);
257                         }
258                 } else {
259                         add_modal_grab ();
260                         shuttle_grabbed = true;
261                         shuttle_speed_on_grab = _session->transport_speed ();
262                         mouse_shuttle (ev->x, true);
263                 }
264                 break;
265
266         case 2:
267         case 3:
268                 return true;
269                 break;
270         }
271
272         return true;
273 }
274
275 bool
276 ShuttleControl::on_button_release_event (GdkEventButton* ev)
277 {
278         if (!_session) {
279                 return true;
280         }
281
282         switch (ev->button) {
283         case 1:
284                 if (shuttle_grabbed) {
285                         shuttle_grabbed = false;
286                         remove_modal_grab ();
287                         
288                         if (Config->get_shuttle_behaviour() == Sprung) {
289                                 _session->request_transport_speed (shuttle_speed_on_grab);
290                         } else {
291                                 mouse_shuttle (ev->x, true);
292                         }
293                 }
294                 return true;
295
296         case 2:
297                 if (_session->transport_rolling()) {
298                         _session->request_transport_speed (1.0, Config->get_shuttle_behaviour() == Wheel);
299                 }
300                 return true;
301
302         case 3:
303         default:
304                 return true;
305
306         }
307
308         return true;
309 }
310
311 bool
312 ShuttleControl::on_query_tooltip (int, int, bool, const Glib::RefPtr<Gtk::Tooltip>&)
313 {
314         return false;
315 }
316
317 bool
318 ShuttleControl::on_scroll_event (GdkEventScroll* ev)
319 {
320         if (!_session || Config->get_shuttle_behaviour() != Wheel) {
321                 return true;
322         }
323
324         bool semis = (Config->get_shuttle_units() == Semitones);
325
326         switch (ev->direction) {
327         case GDK_SCROLL_UP:
328         case GDK_SCROLL_RIGHT:
329                 if (semis) {
330                         if (shuttle_fract == 0) {
331                                 shuttle_fract = semitones_as_fract (1, false);
332                         } else {
333                                 bool rev;
334                                 int st = fract_as_semitones (shuttle_fract, rev);
335                                 shuttle_fract = semitones_as_fract (st + 1, rev);
336                         }
337                 } else {
338                         shuttle_fract += 0.00125;
339                 }
340                 break;
341         case GDK_SCROLL_DOWN:
342         case GDK_SCROLL_LEFT:
343                 if (semis) {
344                         if (shuttle_fract == 0) {
345                                 shuttle_fract = semitones_as_fract (1, true);
346                         } else {
347                                 bool rev;
348                                 int st = fract_as_semitones (shuttle_fract, rev);
349                                 shuttle_fract = semitones_as_fract (st - 1, rev);
350                         }
351                 } else {
352                         shuttle_fract -= 0.00125;
353                 }
354                 break;
355         default:
356                 return false;
357         }
358         
359         if (semis) {
360
361                 float lower_side_of_dead_zone = semitones_as_fract (-24, true);
362                 float upper_side_of_dead_zone = semitones_as_fract (-24, false);
363
364                 /* if we entered the "dead zone" (-24 semitones in forward or reverse), jump
365                    to the far side of it.
366                 */
367
368                 if (shuttle_fract > lower_side_of_dead_zone && shuttle_fract < upper_side_of_dead_zone) {
369                         switch (ev->direction) {
370                         case GDK_SCROLL_UP:
371                         case GDK_SCROLL_RIGHT:
372                                 shuttle_fract = upper_side_of_dead_zone;
373                                 break;
374                         case GDK_SCROLL_DOWN:
375                         case GDK_SCROLL_LEFT:
376                                 shuttle_fract = lower_side_of_dead_zone;
377                                 break;
378                         default:
379                                 /* impossible, checked above */
380                                 return false;
381                         }
382                 }
383         }
384
385         use_shuttle_fract (true);
386
387         return true;
388 }
389
390 bool
391 ShuttleControl::on_motion_notify_event (GdkEventMotion* ev)
392 {
393         if (!_session || !shuttle_grabbed) {
394                 return true;
395         }
396
397         return mouse_shuttle (ev->x, false);
398 }
399
400 gint
401 ShuttleControl::mouse_shuttle (double x, bool force)
402 {
403         double const center = get_width() / 2.0;
404         double distance_from_center = x - center;
405
406         if (distance_from_center > 0) {
407                 distance_from_center = min (distance_from_center, center);
408         } else {
409                 distance_from_center = max (distance_from_center, -center);
410         }
411
412         /* compute shuttle fract as expressing how far between the center
413            and the edge we are. positive values indicate we are right of
414            center, negative values indicate left of center
415         */
416
417         shuttle_fract = distance_from_center / center; // center == half the width
418         use_shuttle_fract (force);
419         return true;
420 }
421
422 void
423 ShuttleControl::set_shuttle_fract (double f)
424 {
425         shuttle_fract = f;
426         use_shuttle_fract (false);
427 }
428
429 int
430 ShuttleControl::speed_as_semitones (float speed, bool& reverse)
431 {
432         assert (speed != 0.0);
433
434         if (speed < 0.0) {
435                 reverse = true;
436                 return (int) round (12.0 * fast_log2 (-speed));
437         } else {
438                 reverse = false;
439                 return (int) round (12.0 * fast_log2 (speed));
440         }
441 }
442
443 float
444 ShuttleControl::semitones_as_speed (int semi, bool reverse)
445 {
446         if (reverse) {
447                 return -pow (2.0, (semi / 12.0));
448         } else {
449                 return pow (2.0, (semi / 12.0));
450         }
451 }
452
453 float
454 ShuttleControl::semitones_as_fract (int semi, bool reverse)
455 {
456         float speed = semitones_as_speed (semi, reverse);
457         return speed/4.0; /* 4.0 is the maximum speed for a 24 semitone shift */
458 }
459
460 int
461 ShuttleControl::fract_as_semitones (float fract, bool& reverse)
462 {
463         assert (fract != 0.0);
464         return speed_as_semitones (fract * 4.0, reverse);
465 }
466
467 void
468 ShuttleControl::use_shuttle_fract (bool force)
469 {
470         microseconds_t now = get_microseconds();
471
472         shuttle_fract = max (-1.0f, shuttle_fract);
473         shuttle_fract = min (1.0f, shuttle_fract);
474
475         /* do not attempt to submit a motion-driven transport speed request
476            more than once per process cycle.
477         */
478
479         if (!force && (last_shuttle_request - now) < (microseconds_t) AudioEngine::instance()->usecs_per_cycle()) {
480                 return;
481         }
482
483         last_shuttle_request = now;
484
485         double speed = 0;
486
487         if (Config->get_shuttle_units() == Semitones) {
488                 if (shuttle_fract != 0.0) {
489                         bool reverse;
490                         int semi = fract_as_semitones (shuttle_fract, reverse);
491                         speed = semitones_as_speed (semi, reverse);
492                 } else {
493                         speed = 0.0;
494                 }
495         } else {
496                 speed = shuttle_max_speed * shuttle_fract;
497         }
498
499         _session->request_transport_speed_nonzero (speed, true);
500 }
501
502 void
503 ShuttleControl::render (cairo_t* cr)
504 {
505         cairo_text_extents_t extents;
506
507         //black border
508         cairo_set_source_rgb (cr, 0, 0.0, 0.0);
509         rounded_rectangle (cr, 0, 0, get_width(), get_height(), 4);
510 //      cairo_fill_preserve (cr);
511 //      cairo_stroke (cr);
512         cairo_fill (cr);
513
514         float speed = 0.0;
515
516         if (_session) {
517                 speed = _session->transport_speed ();
518         }
519
520         /* Marker */
521         float visual_fraction = std::min (1.0f, speed/shuttle_max_speed);
522         float marker_size = get_height()-4;
523         float avail_width = get_width() - marker_size;
524         float x = get_width()*0.5 + visual_fraction * avail_width*0.5;
525         float offset = x - marker_size*0.5;
526 //      cairo_set_source_rgb (cr, 0, 1, 0.0);
527         cairo_set_source (cr, pattern);
528         if (speed == 1.0) {
529                 cairo_move_to( cr, offset-4, 2);
530                 cairo_line_to( cr, offset+4, 2+marker_size*0.5);
531                 cairo_line_to( cr, offset-4, 2+marker_size);
532                 cairo_line_to( cr, offset-4, 2);
533         } else if ( speed ==0.0 )
534                 rounded_rectangle (cr, offset, 4, marker_size-2, marker_size-2, 1);
535         else
536                 cairo_arc (cr, offset + marker_size*0.5, 2 + marker_size*0.5, marker_size*0.5, 0, 360);
537         cairo_set_line_width (cr, 2);
538         cairo_stroke (cr);
539
540         /* speed text */
541
542         char buf[32];
543
544         if (speed != 0) {
545
546                 if (Config->get_shuttle_units() == Percentage) {
547
548                         if (speed == 1.0) {
549                                 snprintf (buf, sizeof (buf), "%s", _("Playing"));
550                         } else {
551                                 if (speed < 0.0) {
552                                         snprintf (buf, sizeof (buf), "<<< %d%%", (int) round (-speed * 100));
553                                 } else {
554                                         snprintf (buf, sizeof (buf), ">>> %d%%", (int) round (speed * 100));
555                                 }
556                         }
557
558                 } else {
559
560                         bool reversed;
561                         int semi = speed_as_semitones (speed, reversed);
562
563                         if (reversed) {
564                                 snprintf (buf, sizeof (buf), _("<<< %+d semitones"), semi);
565                         } else {
566                                 snprintf (buf, sizeof (buf), _(">>> %+d semitones"), semi);
567                         }
568                 }
569
570         } else {
571                 snprintf (buf, sizeof (buf), "%s", _("Stopped"));
572         }
573
574         last_speed_displayed = speed;
575
576         cairo_set_source_rgb (cr, 0.6, 0.6, 0.6);
577         cairo_text_extents (cr, buf, &extents);
578         cairo_move_to (cr, 10, extents.height + 4);
579         cairo_set_font_size (cr, 13.0);
580         cairo_show_text (cr, buf);
581
582         /* style text */
583
584
585         switch (Config->get_shuttle_behaviour()) {
586         case Sprung:
587                 snprintf (buf, sizeof (buf), "%s", _("Sprung"));
588                 break;
589         case Wheel:
590                 snprintf (buf, sizeof (buf), "%s", _("Wheel"));
591                 break;
592         }
593
594         cairo_text_extents (cr, buf, &extents);
595         cairo_move_to (cr, get_width() - (fabs(extents.x_advance) + 5), extents.height + 4);
596         cairo_show_text (cr, buf);
597
598         float _corner_radius = 4.0;
599
600 /*      //reflection
601         float rheight = 10.0;
602         Gtkmm2ext::rounded_rectangle (cr, 2, 1, get_width()-4, rheight, _corner_radius);
603         cairo_set_source (cr, shine_pattern);
604         cairo_fill (cr);
605 */
606         if (ARDOUR::Config->get_widget_prelight()) {
607                 if (_hovering) {
608                         rounded_rectangle (cr, 1, 1, get_width()-2, get_height()-2, _corner_radius);
609                         cairo_set_source_rgba (cr, 1, 1, 1, 0.2);
610                         cairo_fill (cr);
611                 }
612         }
613 }
614
615 void
616 ShuttleControl::shuttle_unit_clicked ()
617 {
618         if (shuttle_unit_menu == 0) {
619                 shuttle_unit_menu = dynamic_cast<Menu*> (ActionManager::get_widget ("/ShuttleUnitPopup"));
620         }
621         shuttle_unit_menu->popup (1, gtk_get_current_event_time());
622 }
623
624 void
625 ShuttleControl::set_shuttle_style (ShuttleBehaviour s)
626 {
627         Config->set_shuttle_behaviour (s);
628 }
629
630 void
631 ShuttleControl::set_shuttle_units (ShuttleUnits s)
632 {
633         Config->set_shuttle_units (s);
634 }
635
636 void
637 ShuttleControl::update_speed_display ()
638 {
639         if (_session->transport_speed() != last_speed_displayed) {
640                 queue_draw ();
641         }
642 }
643
644 ShuttleControl::ShuttleControllable::ShuttleControllable (ShuttleControl& s)
645         : PBD::Controllable (X_("Shuttle"))
646         , sc (s)
647 {
648 }
649
650 void
651 ShuttleControl::ShuttleControllable::set_value (double val)
652 {
653         double fract;
654
655         if (val == 0.5) {
656                 fract = 0.0;
657         } else {
658                 if (val < 0.5) {
659                         fract = -((0.5 - val)/0.5);
660                 } else {
661                         fract = ((val - 0.5)/0.5);
662                 }
663         }
664
665         sc.set_shuttle_fract (fract);
666 }
667
668 double
669 ShuttleControl::ShuttleControllable::get_value () const
670 {
671         return sc.get_shuttle_fract ();
672 }
673
674 void
675 ShuttleControl::parameter_changed (std::string p)
676 {
677         if (p == "shuttle-behaviour") {
678                 switch (Config->get_shuttle_behaviour ()) {
679                 case Sprung:
680                         /* back to Sprung - reset to speed = 1.0 if playing
681                          */
682                         if (_session) {
683                                 if (_session->transport_rolling()) {
684                                         if (_session->transport_speed() == 1.0) {
685                                                 queue_draw ();
686                                         } else {
687                                                 /* reset current speed and
688                                                    revert to 1.0 as the default
689                                                 */
690                                                 _session->request_transport_speed (1.0);
691                                                 /* redraw when speed changes */
692                                         }
693                                 } else {
694                                         queue_draw ();
695                                 }
696                         }
697                         break;
698
699                 case Wheel:
700                         queue_draw ();
701                         break;
702                 }
703
704         } else if (p == "shuttle-units") {
705                 queue_draw ();
706         }
707 }
708
709
710 bool
711 ShuttleControl::on_enter_notify_event (GdkEventCrossing* ev)
712 {
713         _hovering = true;
714
715         if (ARDOUR::Config->get_widget_prelight()) {
716                 queue_draw ();
717         }
718
719         return CairoWidget::on_enter_notify_event (ev);
720 }
721
722 bool
723 ShuttleControl::on_leave_notify_event (GdkEventCrossing* ev)
724 {
725         _hovering = false;
726
727         if (ARDOUR::Config->get_widget_prelight()) {
728                 queue_draw ();
729         }
730
731         return CairoWidget::on_leave_notify_event (ev);
732 }