ardour
shuttle_control.cc
Go to the documentation of this file.
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"
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 
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;
61  last_speed_displayed = -99999999;
62  shuttle_grabbed = false;
64  shuttle_fract = 0.0;
65  shuttle_max_speed = 8.0f;
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 
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 
84 {
85  cairo_pattern_destroy (pattern);
86  cairo_pattern_destroy (shine_pattern);
87 }
88 
89 void
91 {
92  SessionHandlePtr::set_session (s);
93 
94  if (_session) {
95  set_sensitive (true);
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 
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
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 {
153  }
154  }
155 
156  queue_draw ();
157 }
158 
159 void
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
232 {
233  if (shuttle_context_menu == 0) {
235  }
236 
237  shuttle_context_menu->popup (1, gtk_get_current_event_time());
238 }
239 
240 void
242 {
243  shuttle_max_speed = speed;
244  last_speed_displayed = -99999999;
245 }
246 
247 bool
249 {
250  if (!_session) {
251  return true;
252  }
253 
255  return true;
256  }
257 
258  if (Keyboard::is_context_menu_event (ev)) {
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()) {
268  }
269  } else {
270  add_modal_grab ();
271  shuttle_grabbed = true;
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
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 ) {
306  }
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
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
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 {
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 {
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), "<<< %.1f%%", -speed * 100.f);
572  } else {
573  snprintf (buf, sizeof (buf), ">>> %.1f%%", speed * 100.f);
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
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
640 {
641  Config->set_shuttle_behaviour (s);
642 }
643 
644 void
646 {
647  Config->set_shuttle_units (s);
648 }
649 
651  : PBD::Controllable (X_("Shuttle"))
652  , sc (s)
653 {
654 }
655 
656 void
658 {
659  sc.set_shuttle_fract ((val - lower()) / (upper() - lower()), true);
660 }
661 
662 double
664 {
665  return lower() + (sc.get_shuttle_fract () * (upper() - lower()));
666 }
667 
668 void
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  */
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
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
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 }
bool transport_rolling() const
Definition: session.h:592
void use_shuttle_fract(bool force, bool zero_ok=false)
ArdourCanvas::Color color(const std::string &, bool *failed=0) const
Definition: ui_config.cc:567
LIBGTKMM2EXT_API Gtk::Widget * get_widget(const char *name)
Definition: actions.cc:366
void request_transport_speed_nonzero(double, bool as_default=false)
float last_speed_displayed
void on_size_allocate(Gtk::Allocation &)
int fract_as_semitones(float, bool &)
double transport_speed() const
Definition: session.h:590
BindingProxy binding_proxy
gint mouse_shuttle(double x, bool force)
void set_shuttle_units(ARDOUR::ShuttleUnits)
Definition: ardour_ui.h:130
static ARDOUR_UI * instance()
Definition: ardour_ui.h:187
Gtk::Menu * shuttle_unit_menu
tuple f
Definition: signals.py:35
bool on_button_release_event(GdkEventButton *)
bool on_scroll_event(GdkEventScroll *)
cairo_pattern_t * pattern
#define UINT_TO_RGBA(u, r, g, b, a)
Definition: fastmeter.cc:35
int speed_as_semitones(float, bool &)
bool on_query_tooltip(int, int, bool, const Glib::RefPtr< Gtk::Tooltip > &)
LIBARDOUR_API microseconds_t get_microseconds()
Definition: globals.cc:728
#define _(Text)
Definition: i18n.h:11
static float fast_log2(float val)
Definition: fastlog.h:15
void set_shuttle_max_speed(float)
PBD::ScopedConnection parameter_connection
LIBGTKMM2EXT_API void set_sensitive(std::vector< Glib::RefPtr< Gtk::Action > > &actions, bool)
void show_shuttle_context_menu()
#define X_(Text)
Definition: i18n.h:13
LIBARDOUR_API RCConfiguration * Config
Definition: globals.cc:119
Definition: amp.h:29
gboolean qt(gboolean, gint, gint, gboolean, Gtk::Tooltip *, gpointer)
void request_transport_speed(double speed, bool as_default=false)
Gtk::Menu * shuttle_style_menu
#define gui_context()
Definition: gui_thread.h:36
void add_controllable(boost::shared_ptr< PBD::Controllable >)
bool button_press_handler(GdkEventButton *)
void set_shuttle_fract(double, bool zero_ok=false)
PBD::Signal1< void, std::string > ParameterChanged
Definition: configuration.h:44
bool on_button_press_event(GdkEventButton *)
void shuttle_unit_clicked()
void set_tip(Gtk::Widget &w, const gchar *tip)
Gtk::Menu * shuttle_context_menu
void map_transport_state()
ShuttleUnits
Definition: types.h:515
void parameter_changed(std::string)
#define upper
Definition: auto_spin.cc:28
cairo_pattern_t * shine_pattern
void set_session(ARDOUR::Session *)
void render(cairo_t *, cairo_rectangle_t *)
void set_active(bool)
boost::shared_ptr< ShuttleControllable > _controllable
bool on_leave_notify_event(GdkEventCrossing *)
static UIConfiguration * config()
Definition: ardour_ui.h:188
Definition: debug.h:30
ARDOUR::microseconds_t last_shuttle_request
LIBGTKMM2EXT_API void rounded_rectangle(Cairo::RefPtr< Cairo::Context > context, double x, double y, double w, double h, double r=10)
Definition: utils.cc:520
#define lower
Definition: auto_spin.cc:29
float semitones_as_fract(int, bool)
bool on_motion_notify_event(GdkEventMotion *)
float semitones_as_speed(int, bool)
#define MISSING_INVALIDATOR
Definition: event_loop.h:86
bool on_enter_notify_event(GdkEventCrossing *)
uint64_t microseconds_t
Definition: types.h:60
ARDOUR::Session * _session
void set_shuttle_style(ARDOUR::ShuttleBehaviour)
void build_shuttle_context_menu()
ShuttleBehaviour
Definition: types.h:510
void on_size_allocate(Gtk::Allocation &)
double shuttle_speed_on_grab