merge resolution with master
[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 "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()->get_canvasvar_Shuttle();
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                         gdk_pointer_grab(ev->window,false,
264                                         GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK),
265                                         NULL,NULL,ev->time);
266                 }
267                 break;
268
269         case 2:
270         case 3:
271                 return true;
272                 break;
273         }
274
275         return true;
276 }
277
278 bool
279 ShuttleControl::on_button_release_event (GdkEventButton* ev)
280 {
281         if (!_session) {
282                 return true;
283         }
284
285         switch (ev->button) {
286         case 1:
287                 if (shuttle_grabbed) {
288                         shuttle_grabbed = false;
289                         remove_modal_grab ();
290                         gdk_pointer_ungrab (GDK_CURRENT_TIME);
291                         
292                         if (Config->get_shuttle_behaviour() == Sprung) {
293                                 if (shuttle_speed_on_grab == 0 ) {
294                                         _session->request_transport_speed (1.0);
295                                 }
296                                 _session->request_transport_speed (shuttle_speed_on_grab);
297                         } else {
298                                 mouse_shuttle (ev->x, true);
299                         }
300                 }
301                 return true;
302
303         case 2:
304                 if (_session->transport_rolling()) {
305                         _session->request_transport_speed (1.0, Config->get_shuttle_behaviour() == Wheel);
306                 }
307                 return true;
308
309         case 3:
310         default:
311                 return true;
312
313         }
314
315         return true;
316 }
317
318 bool
319 ShuttleControl::on_query_tooltip (int, int, bool, const Glib::RefPtr<Gtk::Tooltip>&)
320 {
321         return false;
322 }
323
324 bool
325 ShuttleControl::on_scroll_event (GdkEventScroll* ev)
326 {
327         if (!_session || Config->get_shuttle_behaviour() != Wheel) {
328                 return true;
329         }
330
331         bool semis = (Config->get_shuttle_units() == Semitones);
332
333         switch (ev->direction) {
334         case GDK_SCROLL_UP:
335         case GDK_SCROLL_RIGHT:
336                 if (semis) {
337                         if (shuttle_fract == 0) {
338                                 shuttle_fract = semitones_as_fract (1, false);
339                         } else {
340                                 bool rev;
341                                 int st = fract_as_semitones (shuttle_fract, rev);
342                                 shuttle_fract = semitones_as_fract (st + 1, rev);
343                         }
344                 } else {
345                         shuttle_fract += 0.00125;
346                 }
347                 break;
348         case GDK_SCROLL_DOWN:
349         case GDK_SCROLL_LEFT:
350                 if (semis) {
351                         if (shuttle_fract == 0) {
352                                 shuttle_fract = semitones_as_fract (1, true);
353                         } else {
354                                 bool rev;
355                                 int st = fract_as_semitones (shuttle_fract, rev);
356                                 shuttle_fract = semitones_as_fract (st - 1, rev);
357                         }
358                 } else {
359                         shuttle_fract -= 0.00125;
360                 }
361                 break;
362         default:
363                 return false;
364         }
365         
366         if (semis) {
367
368                 float lower_side_of_dead_zone = semitones_as_fract (-24, true);
369                 float upper_side_of_dead_zone = semitones_as_fract (-24, false);
370
371                 /* if we entered the "dead zone" (-24 semitones in forward or reverse), jump
372                    to the far side of it.
373                 */
374
375                 if (shuttle_fract > lower_side_of_dead_zone && shuttle_fract < upper_side_of_dead_zone) {
376                         switch (ev->direction) {
377                         case GDK_SCROLL_UP:
378                         case GDK_SCROLL_RIGHT:
379                                 shuttle_fract = upper_side_of_dead_zone;
380                                 break;
381                         case GDK_SCROLL_DOWN:
382                         case GDK_SCROLL_LEFT:
383                                 shuttle_fract = lower_side_of_dead_zone;
384                                 break;
385                         default:
386                                 /* impossible, checked above */
387                                 return false;
388                         }
389                 }
390         }
391
392         use_shuttle_fract (true);
393
394         return true;
395 }
396
397 bool
398 ShuttleControl::on_motion_notify_event (GdkEventMotion* ev)
399 {
400         if (!_session || !shuttle_grabbed) {
401                 return true;
402         }
403
404         return mouse_shuttle (ev->x, false);
405 }
406
407 gint
408 ShuttleControl::mouse_shuttle (double x, bool force)
409 {
410         double const center = get_width() / 2.0;
411         double distance_from_center = x - center;
412
413         if (distance_from_center > 0) {
414                 distance_from_center = min (distance_from_center, center);
415         } else {
416                 distance_from_center = max (distance_from_center, -center);
417         }
418
419         /* compute shuttle fract as expressing how far between the center
420            and the edge we are. positive values indicate we are right of
421            center, negative values indicate left of center
422         */
423
424         shuttle_fract = distance_from_center / center; // center == half the width
425         use_shuttle_fract (force);
426         return true;
427 }
428
429 void
430 ShuttleControl::set_shuttle_fract (double f, bool zero_ok)
431 {
432         shuttle_fract = f;
433         use_shuttle_fract (false, zero_ok);
434 }
435
436 int
437 ShuttleControl::speed_as_semitones (float speed, bool& reverse)
438 {
439         assert (speed != 0.0);
440
441         if (speed < 0.0) {
442                 reverse = true;
443                 return (int) round (12.0 * fast_log2 (-speed));
444         } else {
445                 reverse = false;
446                 return (int) round (12.0 * fast_log2 (speed));
447         }
448 }
449
450 float
451 ShuttleControl::semitones_as_speed (int semi, bool reverse)
452 {
453         if (reverse) {
454                 return -pow (2.0, (semi / 12.0));
455         } else {
456                 return pow (2.0, (semi / 12.0));
457         }
458 }
459
460 float
461 ShuttleControl::semitones_as_fract (int semi, bool reverse)
462 {
463         float speed = semitones_as_speed (semi, reverse);
464         return speed/4.0; /* 4.0 is the maximum speed for a 24 semitone shift */
465 }
466
467 int
468 ShuttleControl::fract_as_semitones (float fract, bool& reverse)
469 {
470         assert (fract != 0.0);
471         return speed_as_semitones (fract * 4.0, reverse);
472 }
473
474 void
475 ShuttleControl::use_shuttle_fract (bool force, bool zero_ok)
476 {
477         microseconds_t now = get_microseconds();
478
479         shuttle_fract = max (-1.0f, shuttle_fract);
480         shuttle_fract = min (1.0f, shuttle_fract);
481
482         /* do not attempt to submit a motion-driven transport speed request
483            more than once per process cycle.
484         */
485
486         if (!force && (last_shuttle_request - now) < (microseconds_t) AudioEngine::instance()->usecs_per_cycle()) {
487                 return;
488         }
489
490         last_shuttle_request = now;
491
492         double speed = 0;
493
494         if (Config->get_shuttle_units() == Semitones) {
495                 if (shuttle_fract != 0.0) {
496                         bool reverse;
497                         int semi = fract_as_semitones (shuttle_fract, reverse);
498                         speed = semitones_as_speed (semi, reverse);
499                 } else {
500                         speed = 0.0;
501                 }
502         } else {
503                 speed = shuttle_max_speed * shuttle_fract;
504         }
505
506         if (zero_ok) {
507                 _session->request_transport_speed (speed, Config->get_shuttle_behaviour() == Wheel);
508         } else {
509                 _session->request_transport_speed_nonzero (speed, Config->get_shuttle_behaviour() == Wheel);
510         }
511 }
512
513 void
514 ShuttleControl::render (cairo_t* cr)
515 {
516         cairo_text_extents_t extents;
517
518         //black border
519         cairo_set_source_rgb (cr, 0, 0.0, 0.0);
520         rounded_rectangle (cr, 0, 0, get_width(), get_height(), 4);
521 //      cairo_fill_preserve (cr);
522 //      cairo_stroke (cr);
523         cairo_fill (cr);
524
525         float speed = 0.0;
526
527         if (_session) {
528                 speed = _session->transport_speed ();
529         }
530
531         /* Marker */
532         float visual_fraction = std::min (1.0f, speed/shuttle_max_speed);
533         float marker_size = get_height()-4;
534         float avail_width = get_width() - marker_size;
535         float x = get_width()*0.5 + visual_fraction * avail_width*0.5;
536         float offset = x - marker_size*0.5;
537 //      cairo_set_source_rgb (cr, 0, 1, 0.0);
538         cairo_set_source (cr, pattern);
539         if (speed == 1.0) {
540                 cairo_move_to( cr, offset-4, 2);
541                 cairo_line_to( cr, offset+4, 2+marker_size*0.5);
542                 cairo_line_to( cr, offset-4, 2+marker_size);
543                 cairo_line_to( cr, offset-4, 2);
544         } else if ( speed ==0.0 )
545                 rounded_rectangle (cr, offset, 4, marker_size-2, marker_size-2, 1);
546         else
547                 cairo_arc (cr, offset + marker_size*0.5, 2 + marker_size*0.5, marker_size*0.5, 0, 360);
548         cairo_set_line_width (cr, 2);
549         cairo_stroke (cr);
550
551         /* speed text */
552
553         char buf[32];
554
555         if (speed != 0) {
556
557                 if (Config->get_shuttle_units() == Percentage) {
558
559                         if (speed == 1.0) {
560                                 snprintf (buf, sizeof (buf), "%s", _("Playing"));
561                         } else {
562                                 if (speed < 0.0) {
563                                         snprintf (buf, sizeof (buf), "<<< %d%%", (int) round (-speed * 100));
564                                 } else {
565                                         snprintf (buf, sizeof (buf), ">>> %d%%", (int) round (speed * 100));
566                                 }
567                         }
568
569                 } else {
570
571                         bool reversed;
572                         int semi = speed_as_semitones (speed, reversed);
573
574                         if (reversed) {
575                                 snprintf (buf, sizeof (buf), _("<<< %+d semitones"), semi);
576                         } else {
577                                 snprintf (buf, sizeof (buf), _(">>> %+d semitones"), semi);
578                         }
579                 }
580
581         } else {
582                 snprintf (buf, sizeof (buf), "%s", _("Stopped"));
583         }
584
585         last_speed_displayed = speed;
586
587         cairo_set_source_rgb (cr, 0.6, 0.6, 0.6);
588         cairo_text_extents (cr, buf, &extents);
589         cairo_move_to (cr, 10, extents.height + 4);
590         cairo_set_font_size (cr, 13.0);
591         cairo_show_text (cr, buf);
592
593         /* style text */
594
595
596         switch (Config->get_shuttle_behaviour()) {
597         case Sprung:
598                 snprintf (buf, sizeof (buf), "%s", _("Sprung"));
599                 break;
600         case Wheel:
601                 snprintf (buf, sizeof (buf), "%s", _("Wheel"));
602                 break;
603         }
604
605         cairo_text_extents (cr, buf, &extents);
606         cairo_move_to (cr, get_width() - (fabs(extents.x_advance) + 5), extents.height + 4);
607         cairo_show_text (cr, buf);
608
609         float _corner_radius = 4.0;
610
611 /*      //reflection
612         float rheight = 10.0;
613         Gtkmm2ext::rounded_rectangle (cr, 2, 1, get_width()-4, rheight, _corner_radius);
614         cairo_set_source (cr, shine_pattern);
615         cairo_fill (cr);
616 */
617         if (ARDOUR::Config->get_widget_prelight()) {
618                 if (_hovering) {
619                         rounded_rectangle (cr, 1, 1, get_width()-2, get_height()-2, _corner_radius);
620                         cairo_set_source_rgba (cr, 1, 1, 1, 0.2);
621                         cairo_fill (cr);
622                 }
623         }
624 }
625
626 void
627 ShuttleControl::shuttle_unit_clicked ()
628 {
629         if (shuttle_unit_menu == 0) {
630                 shuttle_unit_menu = dynamic_cast<Menu*> (ActionManager::get_widget ("/ShuttleUnitPopup"));
631         }
632         shuttle_unit_menu->popup (1, gtk_get_current_event_time());
633 }
634
635 void
636 ShuttleControl::set_shuttle_style (ShuttleBehaviour s)
637 {
638         Config->set_shuttle_behaviour (s);
639 }
640
641 void
642 ShuttleControl::set_shuttle_units (ShuttleUnits s)
643 {
644         Config->set_shuttle_units (s);
645 }
646
647 void
648 ShuttleControl::update_speed_display ()
649 {
650         if (_session->transport_speed() != last_speed_displayed) {
651                 queue_draw ();
652         }
653 }
654
655 ShuttleControl::ShuttleControllable::ShuttleControllable (ShuttleControl& s)
656         : PBD::Controllable (X_("Shuttle"))
657         , sc (s)
658 {
659 }
660
661 void
662 ShuttleControl::ShuttleControllable::set_value (double val)
663 {
664         sc.set_shuttle_fract ((val - lower()) / (upper() - lower()), true);
665 }
666
667 double
668 ShuttleControl::ShuttleControllable::get_value () const
669 {
670         return lower() + (sc.get_shuttle_fract () * (upper() - lower()));
671 }
672
673 void
674 ShuttleControl::parameter_changed (std::string p)
675 {
676         if (p == "shuttle-behaviour") {
677                 switch (Config->get_shuttle_behaviour ()) {
678                 case Sprung:
679                         /* back to Sprung - reset to speed = 1.0 if playing
680                          */
681                         if (_session) {
682                                 if (_session->transport_rolling()) {
683                                         if (_session->transport_speed() == 1.0) {
684                                                 queue_draw ();
685                                         } else {
686                                                 /* reset current speed and
687                                                    revert to 1.0 as the default
688                                                 */
689                                                 _session->request_transport_speed (1.0);
690                                                 /* redraw when speed changes */
691                                         }
692                                 } else {
693                                         queue_draw ();
694                                 }
695                         }
696                         break;
697
698                 case Wheel:
699                         queue_draw ();
700                         break;
701                 }
702
703         } else if (p == "shuttle-units") {
704                 queue_draw ();
705         }
706 }
707
708
709 bool
710 ShuttleControl::on_enter_notify_event (GdkEventCrossing* ev)
711 {
712         _hovering = true;
713
714         if (ARDOUR::Config->get_widget_prelight()) {
715                 queue_draw ();
716         }
717
718         return CairoWidget::on_enter_notify_event (ev);
719 }
720
721 bool
722 ShuttleControl::on_leave_notify_event (GdkEventCrossing* ev)
723 {
724         _hovering = false;
725
726         if (ARDOUR::Config->get_widget_prelight()) {
727                 queue_draw ();
728         }
729
730         return CairoWidget::on_leave_notify_event (ev);
731 }