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