fix merge 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 "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()->get_canvasvar_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 (fabs(speed) <= (2*DBL_EPSILON)) {
136                 shuttle_fract = 0;
137         } else {
138                 if (Config->get_shuttle_units() == Semitones) {
139                         bool reverse;
140                         int semi = speed_as_semitones (speed, reverse);
141                         shuttle_fract = semitones_as_fract (semi, reverse);
142                 } else {
143                         shuttle_fract = speed/shuttle_max_speed;
144                 }
145         }
146
147         queue_draw ();
148 }
149
150 void
151 ShuttleControl::build_shuttle_context_menu ()
152 {
153         using namespace Menu_Helpers;
154
155         shuttle_context_menu = new Menu();
156         MenuList& items = shuttle_context_menu->items();
157
158         Menu* speed_menu = manage (new Menu());
159         MenuList& speed_items = speed_menu->items();
160
161         Menu* units_menu = manage (new Menu);
162         MenuList& units_items = units_menu->items();
163         RadioMenuItem::Group units_group;
164
165         units_items.push_back (RadioMenuElem (units_group, _("Percent"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_units), Percentage)));
166         if (Config->get_shuttle_units() == Percentage) {
167                 static_cast<RadioMenuItem*>(&units_items.back())->set_active();
168         }
169         units_items.push_back (RadioMenuElem (units_group, _("Semitones"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_units), Semitones)));
170         if (Config->get_shuttle_units() == Semitones) {
171                 static_cast<RadioMenuItem*>(&units_items.back())->set_active();
172         }
173         items.push_back (MenuElem (_("Units"), *units_menu));
174
175         Menu* style_menu = manage (new Menu);
176         MenuList& style_items = style_menu->items();
177         RadioMenuItem::Group style_group;
178
179         style_items.push_back (RadioMenuElem (style_group, _("Sprung"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_style), Sprung)));
180         if (Config->get_shuttle_behaviour() == Sprung) {
181                 static_cast<RadioMenuItem*>(&style_items.back())->set_active();
182         }
183         style_items.push_back (RadioMenuElem (style_group, _("Wheel"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_style), Wheel)));
184         if (Config->get_shuttle_behaviour() == Wheel) {
185                 static_cast<RadioMenuItem*>(&style_items.back())->set_active();
186         }
187
188         items.push_back (MenuElem (_("Mode"), *style_menu));
189
190         RadioMenuItem::Group speed_group;
191
192         speed_items.push_back (RadioMenuElem (speed_group, "8", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 8.0f)));
193         if (shuttle_max_speed == 8.0) {
194                 static_cast<RadioMenuItem*>(&speed_items.back())->set_active ();
195         }
196         speed_items.push_back (RadioMenuElem (speed_group, "6", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 6.0f)));
197         if (shuttle_max_speed == 6.0) {
198                 static_cast<RadioMenuItem*>(&speed_items.back())->set_active ();
199         }
200         speed_items.push_back (RadioMenuElem (speed_group, "4", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 4.0f)));
201         if (shuttle_max_speed == 4.0) {
202                 static_cast<RadioMenuItem*>(&speed_items.back())->set_active ();
203         }
204         speed_items.push_back (RadioMenuElem (speed_group, "3", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 3.0f)));
205         if (shuttle_max_speed == 3.0) {
206                 static_cast<RadioMenuItem*>(&speed_items.back())->set_active ();
207         }
208         speed_items.push_back (RadioMenuElem (speed_group, "2", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 2.0f)));
209         if (shuttle_max_speed == 2.0) {
210                 static_cast<RadioMenuItem*>(&speed_items.back())->set_active ();
211         }
212         speed_items.push_back (RadioMenuElem (speed_group, "1.5", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 1.5f)));
213         if (shuttle_max_speed == 1.5) {
214                 static_cast<RadioMenuItem*>(&speed_items.back())->set_active ();
215         }
216
217         items.push_back (MenuElem (_("Maximum speed"), *speed_menu));
218
219 }
220
221 void
222 ShuttleControl::show_shuttle_context_menu ()
223 {
224         if (shuttle_context_menu == 0) {
225                 build_shuttle_context_menu ();
226         }
227
228         shuttle_context_menu->popup (1, gtk_get_current_event_time());
229 }
230
231 void
232 ShuttleControl::set_shuttle_max_speed (float speed)
233 {
234         shuttle_max_speed = speed;
235 }
236
237 bool
238 ShuttleControl::on_button_press_event (GdkEventButton* ev)
239 {
240         if (!_session) {
241                 return true;
242         }
243
244         if (binding_proxy.button_press_handler (ev)) {
245                 return true;
246         }
247
248         if (Keyboard::is_context_menu_event (ev)) {
249                 show_shuttle_context_menu ();
250                 return true;
251         }
252
253         switch (ev->button) {
254         case 1:
255                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
256                         if (_session->transport_rolling()) {
257                                 _session->request_transport_speed (1.0);
258                         }
259                 } else {
260                         add_modal_grab ();
261                         shuttle_grabbed = true;
262                         shuttle_speed_on_grab = _session->transport_speed ();
263                         mouse_shuttle (ev->x, true);
264                         gdk_pointer_grab(ev->window,false,
265                                         GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK),
266                                         NULL,NULL,ev->time);
267                 }
268                 break;
269
270         case 2:
271         case 3:
272                 return true;
273                 break;
274         }
275
276         return true;
277 }
278
279 bool
280 ShuttleControl::on_button_release_event (GdkEventButton* ev)
281 {
282         if (!_session) {
283                 return true;
284         }
285
286         switch (ev->button) {
287         case 1:
288                 if (shuttle_grabbed) {
289                         shuttle_grabbed = false;
290                         remove_modal_grab ();
291                         gdk_pointer_ungrab (GDK_CURRENT_TIME);
292                         
293                         if (Config->get_shuttle_behaviour() == Sprung) {
294                                 if (shuttle_speed_on_grab == 0 ) {
295                                         _session->request_transport_speed (1.0);
296                                 }
297                                 _session->request_transport_speed (shuttle_speed_on_grab);
298                         } else {
299                                 mouse_shuttle (ev->x, true);
300                         }
301                 }
302                 return true;
303
304         case 2:
305                 if (_session->transport_rolling()) {
306                         _session->request_transport_speed (1.0, Config->get_shuttle_behaviour() == Wheel);
307                 }
308                 return true;
309
310         case 3:
311         default:
312                 return true;
313
314         }
315
316         return true;
317 }
318
319 bool
320 ShuttleControl::on_query_tooltip (int, int, bool, const Glib::RefPtr<Gtk::Tooltip>&)
321 {
322         return false;
323 }
324
325 bool
326 ShuttleControl::on_scroll_event (GdkEventScroll* ev)
327 {
328         if (!_session || Config->get_shuttle_behaviour() != Wheel) {
329                 return true;
330         }
331
332         bool semis = (Config->get_shuttle_units() == Semitones);
333
334         switch (ev->direction) {
335         case GDK_SCROLL_UP:
336         case GDK_SCROLL_RIGHT:
337                 if (semis) {
338                         if (shuttle_fract == 0) {
339                                 shuttle_fract = semitones_as_fract (1, false);
340                         } else {
341                                 bool rev;
342                                 int st = fract_as_semitones (shuttle_fract, rev);
343                                 shuttle_fract = semitones_as_fract (st + 1, rev);
344                         }
345                 } else {
346                         shuttle_fract += 0.00125;
347                 }
348                 break;
349         case GDK_SCROLL_DOWN:
350         case GDK_SCROLL_LEFT:
351                 if (semis) {
352                         if (shuttle_fract == 0) {
353                                 shuttle_fract = semitones_as_fract (1, true);
354                         } else {
355                                 bool rev;
356                                 int st = fract_as_semitones (shuttle_fract, rev);
357                                 shuttle_fract = semitones_as_fract (st - 1, rev);
358                         }
359                 } else {
360                         shuttle_fract -= 0.00125;
361                 }
362                 break;
363         default:
364                 return false;
365         }
366         
367         if (semis) {
368
369                 float lower_side_of_dead_zone = semitones_as_fract (-24, true);
370                 float upper_side_of_dead_zone = semitones_as_fract (-24, false);
371
372                 /* if we entered the "dead zone" (-24 semitones in forward or reverse), jump
373                    to the far side of it.
374                 */
375
376                 if (shuttle_fract > lower_side_of_dead_zone && shuttle_fract < upper_side_of_dead_zone) {
377                         switch (ev->direction) {
378                         case GDK_SCROLL_UP:
379                         case GDK_SCROLL_RIGHT:
380                                 shuttle_fract = upper_side_of_dead_zone;
381                                 break;
382                         case GDK_SCROLL_DOWN:
383                         case GDK_SCROLL_LEFT:
384                                 shuttle_fract = lower_side_of_dead_zone;
385                                 break;
386                         default:
387                                 /* impossible, checked above */
388                                 return false;
389                         }
390                 }
391         }
392
393         use_shuttle_fract (true);
394
395         return true;
396 }
397
398 bool
399 ShuttleControl::on_motion_notify_event (GdkEventMotion* ev)
400 {
401         if (!_session || !shuttle_grabbed) {
402                 return true;
403         }
404
405         return mouse_shuttle (ev->x, false);
406 }
407
408 gint
409 ShuttleControl::mouse_shuttle (double x, bool force)
410 {
411         double const center = get_width() / 2.0;
412         double distance_from_center = x - center;
413
414         if (distance_from_center > 0) {
415                 distance_from_center = min (distance_from_center, center);
416         } else {
417                 distance_from_center = max (distance_from_center, -center);
418         }
419
420         /* compute shuttle fract as expressing how far between the center
421            and the edge we are. positive values indicate we are right of
422            center, negative values indicate left of center
423         */
424
425         shuttle_fract = distance_from_center / center; // center == half the width
426         use_shuttle_fract (force);
427         return true;
428 }
429
430 void
431 ShuttleControl::set_shuttle_fract (double f, bool zero_ok)
432 {
433         shuttle_fract = f;
434         use_shuttle_fract (false, zero_ok);
435 }
436
437 int
438 ShuttleControl::speed_as_semitones (float speed, bool& reverse)
439 {
440         assert (speed != 0.0);
441
442         if (speed < 0.0) {
443                 reverse = true;
444                 return (int) round (12.0 * fast_log2 (-speed));
445         } else {
446                 reverse = false;
447                 return (int) round (12.0 * fast_log2 (speed));
448         }
449 }
450
451 float
452 ShuttleControl::semitones_as_speed (int semi, bool reverse)
453 {
454         if (reverse) {
455                 return -pow (2.0, (semi / 12.0));
456         } else {
457                 return pow (2.0, (semi / 12.0));
458         }
459 }
460
461 float
462 ShuttleControl::semitones_as_fract (int semi, bool reverse)
463 {
464         float speed = semitones_as_speed (semi, reverse);
465         return speed/4.0; /* 4.0 is the maximum speed for a 24 semitone shift */
466 }
467
468 int
469 ShuttleControl::fract_as_semitones (float fract, bool& reverse)
470 {
471         assert (fract != 0.0);
472         return speed_as_semitones (fract * 4.0, reverse);
473 }
474
475 void
476 ShuttleControl::use_shuttle_fract (bool force, bool zero_ok)
477 {
478         microseconds_t now = get_microseconds();
479
480         shuttle_fract = max (-1.0f, shuttle_fract);
481         shuttle_fract = min (1.0f, shuttle_fract);
482
483         /* do not attempt to submit a motion-driven transport speed request
484            more than once per process cycle.
485         */
486
487         if (!force && (last_shuttle_request - now) < (microseconds_t) AudioEngine::instance()->usecs_per_cycle()) {
488                 return;
489         }
490
491         last_shuttle_request = now;
492
493         double speed = 0;
494
495         if (Config->get_shuttle_units() == Semitones) {
496                 if (shuttle_fract != 0.0) {
497                         bool reverse;
498                         int semi = fract_as_semitones (shuttle_fract, reverse);
499                         speed = semitones_as_speed (semi, reverse);
500                 } else {
501                         speed = 0.0;
502                 }
503         } else {
504                 speed = shuttle_max_speed * shuttle_fract;
505         }
506
507         if (zero_ok) {
508                 _session->request_transport_speed (speed, Config->get_shuttle_behaviour() == Wheel);
509         } else {
510                 _session->request_transport_speed_nonzero (speed, Config->get_shuttle_behaviour() == Wheel);
511         }
512 }
513
514 void
515 ShuttleControl::render (cairo_t* cr)
516 {
517         cairo_text_extents_t extents;
518
519         //black border
520         cairo_set_source_rgb (cr, 0, 0.0, 0.0);
521         rounded_rectangle (cr, 0, 0, get_width(), get_height(), 4);
522 //      cairo_fill_preserve (cr);
523 //      cairo_stroke (cr);
524         cairo_fill (cr);
525
526         float speed = 0.0;
527
528         if (_session) {
529                 speed = _session->transport_speed ();
530         }
531
532         /* Marker */
533         float visual_fraction = std::min (1.0f, speed/shuttle_max_speed);
534         float marker_size = get_height()-4;
535         float avail_width = get_width() - marker_size;
536         float x = get_width()*0.5 + visual_fraction * avail_width*0.5;
537         float offset = x - marker_size*0.5;
538 //      cairo_set_source_rgb (cr, 0, 1, 0.0);
539         cairo_set_source (cr, pattern);
540         if (speed == 1.0) {
541                 cairo_move_to( cr, offset-4, 2);
542                 cairo_line_to( cr, offset+4, 2+marker_size*0.5);
543                 cairo_line_to( cr, offset-4, 2+marker_size);
544                 cairo_line_to( cr, offset-4, 2);
545         } else if ( speed ==0.0 )
546                 rounded_rectangle (cr, offset, 4, marker_size-2, marker_size-2, 1);
547         else
548                 cairo_arc (cr, offset + marker_size*0.5, 2 + marker_size*0.5, marker_size*0.5, 0, 360);
549         cairo_set_line_width (cr, 2);
550         cairo_stroke (cr);
551
552         /* speed text */
553
554         char buf[32];
555
556         if (speed != 0) {
557
558                 if (Config->get_shuttle_units() == Percentage) {
559
560                         if (speed == 1.0) {
561                                 snprintf (buf, sizeof (buf), "%s", _("Playing"));
562                         } else {
563                                 if (speed < 0.0) {
564                                         snprintf (buf, sizeof (buf), "<<< %d%%", (int) round (-speed * 100));
565                                 } else {
566                                         snprintf (buf, sizeof (buf), ">>> %d%%", (int) round (speed * 100));
567                                 }
568                         }
569
570                 } else {
571
572                         bool reversed;
573                         int semi = speed_as_semitones (speed, reversed);
574
575                         if (reversed) {
576                                 snprintf (buf, sizeof (buf), _("<<< %+d semitones"), semi);
577                         } else {
578                                 snprintf (buf, sizeof (buf), _(">>> %+d semitones"), semi);
579                         }
580                 }
581
582         } else {
583                 snprintf (buf, sizeof (buf), "%s", _("Stopped"));
584         }
585
586         last_speed_displayed = speed;
587
588         cairo_set_source_rgb (cr, 0.6, 0.6, 0.6);
589         cairo_text_extents (cr, buf, &extents);
590         cairo_move_to (cr, 10, extents.height + 4);
591         cairo_set_font_size (cr, 13.0);
592         cairo_show_text (cr, buf);
593
594         /* style text */
595
596
597         switch (Config->get_shuttle_behaviour()) {
598         case Sprung:
599                 snprintf (buf, sizeof (buf), "%s", _("Sprung"));
600                 break;
601         case Wheel:
602                 snprintf (buf, sizeof (buf), "%s", _("Wheel"));
603                 break;
604         }
605
606         cairo_text_extents (cr, buf, &extents);
607         cairo_move_to (cr, get_width() - (fabs(extents.x_advance) + 5), extents.height + 4);
608         cairo_show_text (cr, buf);
609
610         float _corner_radius = 4.0;
611
612 /*      //reflection
613         float rheight = 10.0;
614         Gtkmm2ext::rounded_rectangle (cr, 2, 1, get_width()-4, rheight, _corner_radius);
615         cairo_set_source (cr, shine_pattern);
616         cairo_fill (cr);
617 */
618         if (ARDOUR::Config->get_widget_prelight()) {
619                 if (_hovering) {
620                         rounded_rectangle (cr, 1, 1, get_width()-2, get_height()-2, _corner_radius);
621                         cairo_set_source_rgba (cr, 1, 1, 1, 0.2);
622                         cairo_fill (cr);
623                 }
624         }
625 }
626
627 void
628 ShuttleControl::shuttle_unit_clicked ()
629 {
630         if (shuttle_unit_menu == 0) {
631                 shuttle_unit_menu = dynamic_cast<Menu*> (ActionManager::get_widget ("/ShuttleUnitPopup"));
632         }
633         shuttle_unit_menu->popup (1, gtk_get_current_event_time());
634 }
635
636 void
637 ShuttleControl::set_shuttle_style (ShuttleBehaviour s)
638 {
639         Config->set_shuttle_behaviour (s);
640 }
641
642 void
643 ShuttleControl::set_shuttle_units (ShuttleUnits s)
644 {
645         Config->set_shuttle_units (s);
646 }
647
648 void
649 ShuttleControl::update_speed_display ()
650 {
651         if (_session->transport_speed() != last_speed_displayed) {
652                 queue_draw ();
653         }
654 }
655
656 ShuttleControl::ShuttleControllable::ShuttleControllable (ShuttleControl& s)
657         : PBD::Controllable (X_("Shuttle"))
658         , sc (s)
659 {
660 }
661
662 void
663 ShuttleControl::ShuttleControllable::set_value (double val)
664 {
665         sc.set_shuttle_fract ((val - lower()) / (upper() - lower()), true);
666 }
667
668 double
669 ShuttleControl::ShuttleControllable::get_value () const
670 {
671         return lower() + (sc.get_shuttle_fract () * (upper() - lower()));
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 }