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