ardour
stereo_panner.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2000-2007 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 <iostream>
20 #include <iomanip>
21 #include <cstring>
22 #include <cmath>
23 
24 #include <gtkmm/window.h>
25 #include <pangomm/layout.h>
26 
27 #include "pbd/controllable.h"
28 #include "pbd/compose.h"
29 
30 #include "gtkmm2ext/gui_thread.h"
31 #include "gtkmm2ext/gtk_ui.h"
32 #include "gtkmm2ext/keyboard.h"
33 #include "gtkmm2ext/utils.h"
35 
36 #include "ardour/pannable.h"
37 #include "ardour/panner.h"
38 #include "ardour/panner_shell.h"
39 
40 #include "canvas/colors.h"
41 
42 #include "ardour_ui.h"
43 #include "global_signals.h"
44 #include "stereo_panner.h"
45 #include "stereo_panner_editor.h"
46 #include "rgb_macros.h"
47 #include "utils.h"
48 
49 #include "i18n.h"
50 
51 using namespace std;
52 using namespace Gtk;
53 using namespace Gtkmm2ext;
54 using namespace ARDOUR_UI_UTILS;
55 
57 bool StereoPanner::have_colors = false;
58 
60 bool StereoPanner::have_font = false;
61 
62 using namespace ARDOUR;
63 
65  : PannerInterface (p->panner())
66  , _panner_shell (p)
67  , position_control (_panner->pannable()->pan_azimuth_control)
68  , width_control (_panner->pannable()->pan_width_control)
69  , dragging_position (false)
70  , dragging_left (false)
71  , dragging_right (false)
72  , drag_start_x (0)
73  , last_drag_x (0)
74  , accumulated_delta (0)
75  , detented (false)
76  , position_binder (position_control)
77  , width_binder (width_control)
78  , _dragging (false)
79 {
80  if (!have_colors) {
81  set_colors ();
82  have_colors = true;
83  }
84  if (!have_font) {
85  Pango::FontDescription font;
86  Pango::AttrFontDesc* font_attr;
87  font = Pango::FontDescription (ARDOUR_UI::config()->get_SmallBoldMonospaceFont());
88  font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font));
89  panner_font_attributes.change(*font_attr);
90  delete font_attr;
91  have_font = true;
92  }
93 
96 
99 
100  ColorsChanged.connect (sigc::mem_fun (*this, &StereoPanner::color_handler));
101 
102  set_tooltip ();
103 }
104 
106 {
107 
108 }
109 
110 void
112 {
113  if (_panner_shell->bypassed()) {
114  _tooltip.set_tip (_("bypassed"));
115  return;
116  }
117  double pos = position_control->get_value(); // 0..1
118 
119  /* We show the position of the center of the image relative to the left & right.
120  This is expressed as a pair of percentage values that ranges from (100,0)
121  (hard left) through (50,50) (hard center) to (0,100) (hard right).
122 
123  This is pretty wierd, but its the way audio engineers expect it. Just remember that
124  the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
125  */
126 
127  char buf[64];
128  snprintf (buf, sizeof (buf), _("L:%3d R:%3d Width:%d%%"), (int) rint (100.0 * (1.0 - pos)),
129  (int) rint (100.0 * pos),
130  (int) floor (100.0 * width_control->get_value()));
131  _tooltip.set_tip (buf);
132 }
133 
134 bool
136 {
137  Glib::RefPtr<Gdk::Window> win (get_window());
138  Glib::RefPtr<Gdk::GC> gc (get_style()->get_base_gc (get_state()));
139  Cairo::RefPtr<Cairo::Context> context = get_window()->create_cairo_context();
140  Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(get_pango_context());
141  layout->set_attributes (panner_font_attributes);
142 
143  int tw, th;
144  int width, height;
145  const double pos = position_control->get_value (); /* 0..1 */
146  const double swidth = width_control->get_value (); /* -1..+1 */
147  const double fswidth = fabs (swidth);
148  uint32_t o, f, t, b, r;
149  State state;
150 
151  width = get_width();
152  height = get_height ();
153 
154  const int step_down = rint(height / 3.5);
155  const double corner_radius = 5.0 * ARDOUR_UI::ui_scale;
156  const int lr_box_size = height - 2 * step_down;
157  const int pos_box_size = (int)(rint(step_down * .8)) | 1;
158  const int top_step = step_down - pos_box_size;
159 
160  if (swidth == 0.0) {
161  state = Mono;
162  } else if (swidth < 0.0) {
163  state = Inverted;
164  } else {
165  state = Normal;
166  }
167 
168  o = colors[state].outline;
169  f = colors[state].fill;
170  t = colors[state].text;
171  b = colors[state].background;
172  r = colors[state].rule;
173 
174  if (_panner_shell->bypassed()) {
175  b = 0x20202040;
176  f = 0x404040ff;
177  o = 0x606060ff;
178  t = 0x606060ff;
179  r = 0x606060ff;
180  }
181 
182  if (_send_mode) {
183  b = ARDOUR_UI::config()->color ("send bg");
184  // b = rgba_from_style("SendStripBase",
185  // UINT_RGBA_R(b), UINT_RGBA_G(b), UINT_RGBA_B(b), 255,
186  // "fg");
187  }
188  /* background */
189 
190  context->set_source_rgba (UINT_RGBA_R_FLT(b), UINT_RGBA_G_FLT(b), UINT_RGBA_B_FLT(b), UINT_RGBA_A_FLT(b));
191  cairo_rectangle (context->cobj(), 0, 0, width, height);
192  context->fill_preserve ();
193  context->clip();
194 
195  /* the usable width is reduced from the real width, because we need space for
196  the two halves of LR boxes that will extend past the actual left/right
197  positions (indicated by the vertical line segment above them).
198  */
199 
200  double usable_width = width - lr_box_size;
201 
202  /* compute the centers of the L/R boxes based on the current stereo width */
203 
204  if (fmod (usable_width,2.0) == 0) {
205  /* even width, but we need odd, so that there is an exact center.
206  So, offset cairo by 1, and reduce effective width by 1
207  */
208  usable_width -= 1.0;
209  context->translate (1.0, 0.0);
210  }
211 
212  const double half_lr_box = lr_box_size/2.0;
213  const double center = rint(half_lr_box + (usable_width * pos));
214  const double pan_spread = rint((fswidth * (usable_width-1.0))/2.0);
215  const double left = center - pan_spread;
216  const double right = center + pan_spread;
217 
218  /* center line */
219  context->set_line_width (1.0);
220  context->move_to ((usable_width + lr_box_size)/2.0, 0);
221  context->rel_line_to (0, height);
222  context->set_source_rgba (UINT_RGBA_R_FLT(r), UINT_RGBA_G_FLT(r), UINT_RGBA_B_FLT(r), UINT_RGBA_A_FLT(r));
223  context->stroke ();
224 
225  /* compute & draw the line through the box */
226  context->set_line_width (2);
227  context->set_source_rgba (UINT_RGBA_R_FLT(o), UINT_RGBA_G_FLT(o), UINT_RGBA_B_FLT(o), UINT_RGBA_A_FLT(o));
228  context->move_to (left, top_step + (pos_box_size/2.0) + step_down + 1.0);
229  context->line_to (left, top_step + (pos_box_size/2.0));
230  context->line_to (right, top_step + (pos_box_size/2.0));
231  context->line_to (right, top_step + (pos_box_size/2.0) + step_down + 1.0);
232  context->stroke ();
233 
234  context->set_line_width (1.0);
235 
236  /* left box */
237  if (state != Mono) {
238  rounded_rectangle (context, left - half_lr_box,
239  half_lr_box+step_down,
240  lr_box_size, lr_box_size, corner_radius);
241  context->set_source_rgba (UINT_RGBA_R_FLT(f), UINT_RGBA_G_FLT(f), UINT_RGBA_B_FLT(f), UINT_RGBA_A_FLT(f));
242  context->fill_preserve();
243  context->set_source_rgba (UINT_RGBA_R_FLT(o), UINT_RGBA_G_FLT(o), UINT_RGBA_B_FLT(o), UINT_RGBA_A_FLT(o));
244  context->stroke();
245 
246  /* add text */
247  context->set_source_rgba (UINT_RGBA_R_FLT(t), UINT_RGBA_G_FLT(t), UINT_RGBA_B_FLT(t), UINT_RGBA_A_FLT(t));
248  if (swidth < 0.0) {
249  layout->set_text (S_("Panner|R"));
250  } else {
251  layout->set_text (S_("Panner|L"));
252  }
253  layout->get_pixel_size(tw, th);
254  context->move_to (rint(left - tw/2), rint(lr_box_size + step_down - th/2));
255  pango_cairo_show_layout (context->cobj(), layout->gobj());
256  }
257 
258  /* right box */
259  rounded_rectangle (context, right - half_lr_box,
260  half_lr_box+step_down,
261  lr_box_size, lr_box_size, corner_radius);
262  context->set_source_rgba (UINT_RGBA_R_FLT(f), UINT_RGBA_G_FLT(f), UINT_RGBA_B_FLT(f), UINT_RGBA_A_FLT(f));
263  context->fill_preserve();
264  context->set_source_rgba (UINT_RGBA_R_FLT(o), UINT_RGBA_G_FLT(o), UINT_RGBA_B_FLT(o), UINT_RGBA_A_FLT(o));
265  context->stroke();
266 
267  /* add text */
268  context->set_source_rgba (UINT_RGBA_R_FLT(t), UINT_RGBA_G_FLT(t), UINT_RGBA_B_FLT(t), UINT_RGBA_A_FLT(t));
269 
270  if (state == Mono) {
271  layout->set_text (S_("Panner|M"));
272  } else {
273  if (swidth < 0.0) {
274  layout->set_text (S_("Panner|L"));
275  } else {
276  layout->set_text (S_("Panner|R"));
277  }
278  }
279  layout->get_pixel_size(tw, th);
280  context->move_to (rint(right - tw/2), rint(lr_box_size + step_down - th/2));
281  pango_cairo_show_layout (context->cobj(), layout->gobj());
282 
283  /* draw the central box */
284  context->set_line_width (2.0);
285  context->move_to (center + (pos_box_size/2.0), top_step); /* top right */
286  context->rel_line_to (0.0, pos_box_size); /* lower right */
287  context->rel_line_to (-pos_box_size/2.0, 4.0); /* bottom point */
288  context->rel_line_to (-pos_box_size/2.0, -4.0); /* lower left */
289  context->rel_line_to (0.0, -pos_box_size); /* upper left */
290  context->close_path ();
291 
292  context->set_source_rgba (UINT_RGBA_R_FLT(o), UINT_RGBA_G_FLT(o), UINT_RGBA_B_FLT(o), UINT_RGBA_A_FLT(o));
293  context->stroke_preserve ();
294  context->set_source_rgba (UINT_RGBA_R_FLT(f), UINT_RGBA_G_FLT(f), UINT_RGBA_B_FLT(f), UINT_RGBA_A_FLT(f));
295  context->fill ();
296 
297  return true;
298 }
299 
300 bool
302 {
304  return true;
305  }
306 
307  if (_panner_shell->bypassed()) {
308  return true;
309  }
310 
311  drag_start_x = ev->x;
312  last_drag_x = ev->x;
313 
314  dragging_position = false;
315  dragging_left = false;
316  dragging_right = false;
317  _dragging = false;
319  accumulated_delta = 0;
320  detented = false;
321 
322  /* Let the binding proxies get first crack at the press event
323  */
324 
325  if (ev->y < 20) {
327  return true;
328  }
329  } else {
331  return true;
332  }
333  }
334 
335  if (ev->button != 1) {
336  return false;
337  }
338 
339  if (ev->type == GDK_2BUTTON_PRESS) {
340  int width = get_width();
341 
342  if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
343  /* handled by button release */
344  return true;
345  }
346 
347  if (ev->y < 20) {
348 
349  /* upper section: adjusts position, constrained by width */
350 
351  const double w = fabs (width_control->get_value ());
352  const double max_pos = 1.0 - (w/2.0);
353  const double min_pos = w/2.0;
354 
355  if (ev->x <= width/3) {
356  /* left side dbl click */
357  if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
358  /* 2ndary-double click on left, collapse to hard left */
361  } else {
362  position_control->set_value (min_pos);
363  }
364  } else if (ev->x > 2*width/3) {
365  if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
366  /* 2ndary-double click on right, collapse to hard right */
369  } else {
370  position_control->set_value (max_pos);
371  }
372  } else {
374  }
375 
376  } else {
377 
378  /* lower section: adjusts width, constrained by position */
379 
380  const double p = position_control->get_value ();
381  const double max_width = 2.0 * min ((1.0 - p), p);
382 
383  if (ev->x <= width/3) {
384  /* left side dbl click */
385  width_control->set_value (max_width); // reset width to 100%
386  } else if (ev->x > 2*width/3) {
387  /* right side dbl click */
388  width_control->set_value (-max_width); // reset width to inverted 100%
389  } else {
390  /* center dbl click */
391  width_control->set_value (0); // collapse width to 0%
392  }
393  }
394 
395  _dragging = false;
397 
398  } else if (ev->type == GDK_BUTTON_PRESS) {
399 
400  if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
401  /* handled by button release */
402  return true;
403  }
404 
405  if (ev->y < 20) {
406  /* top section of widget is for position drags */
407  dragging_position = true;
409  } else {
410  /* lower section is for dragging width */
411 
412  double pos = position_control->get_value (); /* 0..1 */
413  double swidth = width_control->get_value (); /* -1..+1 */
414  double fswidth = fabs (swidth);
415  const int lr_box_size = get_height() - 2 * rint(get_height() / 3.5);
416  int usable_width = get_width() - lr_box_size;
417  double center = (lr_box_size/2.0) + (usable_width * pos);
418  int left = lrint (center - (fswidth * usable_width / 2.0)); // center of leftmost box
419  int right = lrint (center + (fswidth * usable_width / 2.0)); // center of rightmost box
420  const int half_box = lr_box_size/2;
421 
422  if (ev->x >= (left - half_box) && ev->x < (left + half_box)) {
423  if (swidth < 0.0) {
424  dragging_right = true;
425  } else {
426  dragging_left = true;
427  }
428  } else if (ev->x >= (right - half_box) && ev->x < (right + half_box)) {
429  if (swidth < 0.0) {
430  dragging_left = true;
431  } else {
432  dragging_right = true;
433  }
434  }
436  }
437 
438  _dragging = true;
440  }
441 
442  return true;
443 }
444 
445 bool
447 {
449  return true;
450  }
451 
452  if (ev->button != 1) {
453  return false;
454  }
455 
456  if (_panner_shell->bypassed()) {
457  return false;
458  }
459 
460  bool const dp = dragging_position;
461 
462  _dragging = false;
464  dragging_position = false;
465  dragging_left = false;
466  dragging_right = false;
467  accumulated_delta = 0;
468  detented = false;
469 
470  if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
471  _panner->reset ();
472  } else {
473  if (dp) {
475  } else {
476  StopWidthGesture ();
477  }
478  }
479 
480  return true;
481 }
482 
483 bool
484 StereoPanner::on_scroll_event (GdkEventScroll* ev)
485 {
486  double one_degree = 1.0/180.0; // one degree as a number from 0..1, since 180 degrees is the full L/R axis
487  double pv = position_control->get_value(); // 0..1.0 ; 0 = left
488  double wv = width_control->get_value(); // 0..1.0 ; 0 = left
489  double step;
490 
491  if (_panner_shell->bypassed()) {
492  return false;
493  }
494 
495  if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
496  step = one_degree;
497  } else {
498  step = one_degree * 5.0;
499  }
500 
501  switch (ev->direction) {
502  case GDK_SCROLL_LEFT:
503  wv += step;
504  width_control->set_value (wv);
505  break;
506  case GDK_SCROLL_UP:
507  pv -= step;
509  break;
510  case GDK_SCROLL_RIGHT:
511  wv -= step;
512  width_control->set_value (wv);
513  break;
514  case GDK_SCROLL_DOWN:
515  pv += step;
517  break;
518  }
519 
520  return true;
521 }
522 
523 bool
525 {
526  if (_panner_shell->bypassed()) {
527  _dragging = false;
528  }
529  if (!_dragging) {
530  return false;
531  }
532 
533  const int lr_box_size = get_height() - 2 * rint(get_height() / 3.5);
534  int usable_width = get_width() - lr_box_size;
535  double delta = (ev->x - last_drag_x) / (double) usable_width;
536  double current_width = width_control->get_value ();
537 
538  if (dragging_left) {
539  delta = -delta;
540  }
541 
542  if (dragging_left || dragging_right) {
543 
544  if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
545 
546  /* change width and position in a way that keeps the
547  * other side in the same place
548  */
549 
550  _panner->freeze ();
551 
552  double pv = position_control->get_value();
553 
554  if (dragging_left) {
555  position_control->set_value (pv - delta);
556  } else {
557  position_control->set_value (pv + delta);
558  }
559 
560  if (delta > 0.0) {
561  /* delta is positive, so we're about to
562  increase the width. But we need to increase it
563  by twice the required value so that the
564  other side remains in place when we set
565  the position as well.
566  */
567  width_control->set_value (current_width + (delta * 2.0));
568  } else {
569  width_control->set_value (current_width + delta);
570  }
571 
572  _panner->thaw ();
573 
574  } else {
575 
576  /* maintain position as invariant as we change the width */
577 
578  /* create a detent close to the center */
579 
580  if (!detented && fabs (current_width) < 0.02) {
581  detented = true;
582  /* snap to zero */
584  }
585 
586  if (detented) {
587 
588  accumulated_delta += delta;
589 
590  /* have we pulled far enough to escape ? */
591 
592  if (fabs (accumulated_delta) >= 0.025) {
593  width_control->set_value (current_width + accumulated_delta);
594  detented = false;
595  accumulated_delta = false;
596  }
597 
598  } else {
599  /* width needs to change by 2 * delta because both L & R move */
600  width_control->set_value (current_width + (delta * 2.0));
601  }
602  }
603 
604  } else if (dragging_position) {
605 
606  double pv = position_control->get_value(); // 0..1.0 ; 0 = left
607  position_control->set_value (pv + delta);
608  }
609 
610  last_drag_x = ev->x;
611  return true;
612 }
613 
614 bool
616 {
617  double one_degree = 1.0/180.0;
618  double pv = position_control->get_value(); // 0..1.0 ; 0 = left
619  double wv = width_control->get_value(); // 0..1.0 ; 0 = left
620  double step;
621 
622  if (_panner_shell->bypassed()) {
623  return false;
624  }
625 
626  if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
627  step = one_degree;
628  } else {
629  step = one_degree * 5.0;
630  }
631 
632  /* up/down control width because we consider pan position more "important"
633  (and thus having higher "sense" priority) than width.
634  */
635 
636  switch (ev->keyval) {
637  case GDK_Up:
638  if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
639  width_control->set_value (1.0);
640  } else {
641  width_control->set_value (wv + step);
642  }
643  break;
644  case GDK_Down:
645  if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
646  width_control->set_value (-1.0);
647  } else {
648  width_control->set_value (wv - step);
649  }
650  break;
651 
652  case GDK_Left:
653  pv -= step;
655  break;
656  case GDK_Right:
657  pv += step;
659  break;
660  case GDK_0:
661  case GDK_KP_0:
662  width_control->set_value (0.0);
663  break;
664 
665  default:
666  return false;
667  }
668 
669  return true;
670 }
671 
672 void
674 {
675  colors[Normal].fill = ARDOUR_UI::config()->color_mod ("stereo panner fill", "panner fill");
676  // colors[Normal].outline = ARDOUR_UI::config()->color ("stereo panner outline");
677  colors[Normal].outline = ArdourCanvas::HSV (colors[Normal].fill).outline().color ();
678  colors[Normal].text = ARDOUR_UI::config()->color ("stereo panner text");
679  colors[Normal].background = ARDOUR_UI::config()->color ("stereo panner bg");
680  colors[Normal].rule = ARDOUR_UI::config()->color ("stereo panner rule");
681 
682  colors[Mono].fill = ARDOUR_UI::config()->color ("stereo panner mono fill");
683  colors[Mono].outline = ARDOUR_UI::config()->color ("stereo panner mono outline");
684  colors[Mono].text = ARDOUR_UI::config()->color ("stereo panner mono text");
685  colors[Mono].background = ARDOUR_UI::config()->color ("stereo panner mono bg");
686  colors[Mono].rule = ARDOUR_UI::config()->color ("stereo panner rule");
687 
688  colors[Inverted].fill = ARDOUR_UI::config()->color_mod ("stereo panner inverted fill", "stereo panner inverted");
689  colors[Inverted].outline = ARDOUR_UI::config()->color ("stereo panner inverted outline");
690  colors[Inverted].text = ARDOUR_UI::config()->color ("stereo panner inverted text");
691  colors[Inverted].background = ARDOUR_UI::config()->color_mod ("stereo panner inverted bg", "stereo panner inverted bg");
692  colors[Inverted].rule = ARDOUR_UI::config()->color ("stereo panner rule");
693 }
694 
695 void
697 {
698  set_colors ();
699  queue_draw ();
700 }
701 
702 void
704 {
705  queue_draw ();
706 }
707 
708 void
710 {
712  position_control = _panner->pannable()->pan_azimuth_control;
713  width_control = _panner->pannable()->pan_width_control;
716 
719  queue_draw ();
720 }
721 
724 {
725  return new StereoPannerEditor (this);
726 }
boost::shared_ptr< Pannable > pannable() const
Definition: panner.h:163
ArdourCanvas::Color color(const std::string &, bool *failed=0) const
Definition: ui_config.cc:567
bool on_button_release_event(GdkEventButton *)
bool bypassed() const
void set_tooltip()
bool dragging_right
Definition: stereo_panner.h:71
PannerEditor * editor()
BindingProxy width_binder
Definition: stereo_panner.h:78
PBD::Signal0< void > PannableChanged
Definition: panner_shell.h:67
Definition: ardour_ui.h:130
#define UINT_RGBA_A_FLT(x)
Definition: rgb_macros.h:60
static ColorScheme colors[3]
#define UINT_RGBA_G_FLT(x)
Definition: rgb_macros.h:58
bool on_expose_event(GdkEventExpose *)
sigc::signal< void > StopPositionGesture
Definition: stereo_panner.h:49
bool on_motion_notify_event(GdkEventMotion *)
void set_controllable(boost::shared_ptr< PBD::Controllable >)
StereoPanner(boost::shared_ptr< ARDOUR::PannerShell >)
tuple f
Definition: signals.py:35
Definition: Beats.hpp:239
#define UINT_RGBA_B_FLT(x)
Definition: rgb_macros.h:59
#define invalidator(x)
Definition: gui_thread.h:40
virtual void thaw()
Definition: panner.cc:130
#define _(Text)
Definition: i18n.h:11
static void set_colors()
bool dragging_position
Definition: stereo_panner.h:69
PBD::ScopedConnectionList panshell_connections
Definition: stereo_panner.h:68
bool on_scroll_event(GdkEventScroll *)
boost::shared_ptr< ARDOUR::PannerShell > _panner_shell
Definition: stereo_panner.h:63
bool dragging_left
Definition: stereo_panner.h:70
void pannable_handler()
static bool have_font
Definition: stereo_panner.h:99
double accumulated_delta
Definition: stereo_panner.h:74
Definition: amp.h:29
#define gui_context()
Definition: gui_thread.h:36
bool on_key_press_event(GdkEventKey *)
virtual void reset()=0
bool on_button_press_event(GdkEventButton *)
virtual double get_value(void) const =0
bool button_press_handler(GdkEventButton *)
static Pango::AttrList panner_font_attributes
Definition: stereo_panner.h:98
static bool have_colors
boost::shared_ptr< PBD::Controllable > width_control
Definition: stereo_panner.h:66
bool on_button_press_event(GdkEventButton *)
virtual void set_value(double)=0
PBD::Signal0< void > Changed
Definition: panner_shell.h:68
virtual void freeze()
Definition: panner.cc:124
ArdourCanvas::Color color_mod(std::string const &color, std::string const &modifier) const
Definition: ui_config.cc:555
static UIConfiguration * config()
Definition: ardour_ui.h:188
PBD::Signal0< void > Changed
Definition: controllable.h:94
sigc::signal< void > StartWidthGesture
Definition: stereo_panner.h:50
sigc::signal< void > ColorsChanged
PBD::ScopedConnectionList panvalue_connections
Definition: stereo_panner.h:67
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
sigc::signal< void > StopWidthGesture
Definition: stereo_panner.h:51
#define S_(Text)
Definition: i18n.h:18
sigc::signal< void > StartPositionGesture
Definition: stereo_panner.h:48
BindingProxy position_binder
Definition: stereo_panner.h:77
void bypass_handler()
static float ui_scale
Definition: ardour_ui.h:189
boost::shared_ptr< ARDOUR::Panner > _panner
boost::shared_ptr< PBD::Controllable > position_control
Definition: stereo_panner.h:65
PannerPersistentTooltip _tooltip
bool on_button_release_event(GdkEventButton *)
void color_handler()
#define UINT_RGBA_R_FLT(x)
Definition: rgb_macros.h:57