ardour
ardour_button.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 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 
20 #include <iostream>
21 #include <cmath>
22 #include <algorithm>
23 
24 #include <pangomm/layout.h>
25 
26 #include "pbd/compose.h"
27 #include "pbd/error.h"
28 #include "pbd/stacktrace.h"
29 
30 #include "gtkmm2ext/utils.h"
31 #include "gtkmm2ext/rgb_macros.h"
32 #include "gtkmm2ext/gui_thread.h"
33 
34 #include "ardour/rc_configuration.h" // for widget prelight preference
35 
36 #include "canvas/utils.h"
37 #include "canvas/colors.h"
38 
39 #include "ardour_button.h"
40 #include "ardour_ui.h"
41 #include "global_signals.h"
42 
43 #include "i18n.h"
44 
45 #define BASELINESTRETCH (1.25)
46 #define TRACKHEADERBTNW (3.10)
47 
48 using namespace Gdk;
49 using namespace Gtk;
50 using namespace Glib;
51 using namespace PBD;
52 using std::max;
53 using std::min;
54 using namespace std;
55 
59 
61  : _elements (e)
62  , _icon (ArdourButton::NoIcon)
63  , _tweaks (Tweaks (0))
64  , _char_pixel_width (0)
65  , _char_pixel_height (0)
66  , _char_avg_pixel_width (0)
67  , _text_width (0)
68  , _text_height (0)
69  , _diameter (0)
70  , _corner_radius (2.5)
71  , _corner_mask (0xf)
72  , _angle(0)
73  , _xalign(.5)
74  , _yalign(.5)
75  , fill_inactive_color (0)
76  , fill_active_color (0)
77  , text_active_color(0)
78  , text_inactive_color(0)
79  , led_active_color(0)
80  , led_inactive_color(0)
81  , led_custom_color (0)
82  , use_custom_led_color (false)
83  , convex_pattern (0)
84  , concave_pattern (0)
85  , led_inset_pattern (0)
86  , _led_rect (0)
87  , _act_on_release (true)
88  , _led_left (false)
89  , _distinct_led_click (false)
90  , _hovering (false)
91  , _focused (false)
92  , _fixed_colors_set (false)
93  , _fallthrough_to_parent (false)
94  , _layout_ellipsize_width (-1)
95  , _ellipsis (Pango::ELLIPSIZE_NONE)
96  , _update_colors (true)
97  , _pattern_height (0)
98 {
99  ARDOUR_UI_UTILS::ColorsChanged.connect (sigc::mem_fun (*this, &ArdourButton::color_handler));
100 }
101 
102 ArdourButton::ArdourButton (const std::string& str, Element e)
103  : _elements (e)
104  , _tweaks (Tweaks (0))
105  , _text_width (0)
106  , _text_height (0)
107  , _diameter (0)
108  , _corner_radius (2.5)
109  , _corner_mask (0xf)
110  , _angle(0)
111  , _xalign(.5)
112  , _yalign(.5)
113  , fill_inactive_color (0)
114  , fill_active_color (0)
115  , text_active_color(0)
116  , text_inactive_color(0)
117  , led_active_color(0)
118  , led_inactive_color(0)
119  , led_custom_color (0)
120  , use_custom_led_color (false)
121  , convex_pattern (0)
122  , concave_pattern (0)
123  , led_inset_pattern (0)
124  , _led_rect (0)
125  , _act_on_release (true)
126  , _led_left (false)
127  , _distinct_led_click (false)
128  , _hovering (false)
129  , _focused (false)
130  , _fixed_colors_set (false)
131  , _fallthrough_to_parent (false)
132  , _layout_ellipsize_width (-1)
133  , _ellipsis (Pango::ELLIPSIZE_NONE)
134  , _update_colors (true)
135  , _pattern_height (0)
136 {
137  set_text (str);
138  ARDOUR_UI_UTILS::ColorsChanged.connect (sigc::mem_fun (*this, &ArdourButton::color_handler));
139  ARDOUR_UI_UTILS::DPIReset.connect (sigc::mem_fun (*this, &ArdourButton::on_name_changed));
140 }
141 
143 {
144  delete _led_rect;
145 
146  if (convex_pattern) {
147  cairo_pattern_destroy (convex_pattern);
148  }
149 
150  if (concave_pattern) {
151  cairo_pattern_destroy (concave_pattern);
152  }
153 
154  if (led_inset_pattern) {
155  cairo_pattern_destroy (led_inset_pattern);
156  }
157 }
158 
159 void
160 ArdourButton::set_layout_font (const Pango::FontDescription& fd)
161 {
162  ensure_layout ();
163  if (_layout) {
164  _layout->set_font_description (fd);
165  queue_resize ();
166  }
167 }
168 
169 void
170 ArdourButton::set_text (const std::string& str)
171 {
172  _text = str;
173  if (!is_realized()) {
174  return;
175  }
176  ensure_layout ();
177  if (_layout && _layout->get_text() != _text) {
178  _layout->set_text (_text);
179  queue_resize ();
180  }
181 }
182 
183 void
184 ArdourButton::set_angle (const double angle)
185 {
186  _angle = angle;
187 }
188 
189 void
190 ArdourButton::set_alignment (const float xa, const float ya)
191 {
192  _xalign = xa;
193  _yalign = ya;
194 }
195 
196 void
197 ArdourButton::render (cairo_t* cr, cairo_rectangle_t *)
198 {
199  uint32_t text_color;
200  uint32_t led_color;
201 
202  const float corner_radius = std::max(2.f, _corner_radius * ARDOUR_UI::ui_scale);
203 
204  if (_update_colors) {
205  set_colors ();
206  }
207  if (get_height() != _pattern_height) {
208  build_patterns ();
209  }
210 
212  text_color = text_active_color;
213  led_color = led_active_color;
214  } else {
215  text_color = text_inactive_color;
216  led_color = led_inactive_color;
217  }
218 
219  if (use_custom_led_color) {
220  led_color = led_custom_color;
221  }
222 
223  void (*rounded_function)(cairo_t*, double, double, double, double, double);
224 
225  switch (_corner_mask) {
226  case 0x1: /* upper left only */
227  rounded_function = Gtkmm2ext::rounded_top_left_rectangle;
228  break;
229  case 0x2: /* upper right only */
230  rounded_function = Gtkmm2ext::rounded_top_right_rectangle;
231  break;
232  case 0x3: /* upper only */
233  rounded_function = Gtkmm2ext::rounded_top_rectangle;
234  break;
235  /* should really have functions for lower right, lower left,
236  lower only, but for now, we don't
237  */
238  default:
239  rounded_function = Gtkmm2ext::rounded_rectangle;
240  }
241 
242  // draw edge (filling a rect underneath, rather than stroking a border on top, allows the corners to be lighter-weight.
243  if ((_elements & (Body|Edge)) == (Body|Edge)) {
244  rounded_function (cr, 0, 0, get_width(), get_height(), corner_radius + 1.5);
245  cairo_set_source_rgba (cr, 0, 0, 0, 1);
246  cairo_fill(cr);
247  }
248 
249  // background fill
250  if ((_elements & Body)==Body) {
251  rounded_function (cr, 1, 1, get_width() - 2, get_height() - 2, corner_radius);
252  if (active_state() == Gtkmm2ext::ImplicitActive && !((_elements & Indicator)==Indicator)) {
253  ArdourCanvas::set_source_rgba (cr, fill_inactive_color);
254  cairo_fill (cr);
255  } else if ( (active_state() == Gtkmm2ext::ExplicitActive) && !((_elements & Indicator)==Indicator) ) {
256  //background color
257  ArdourCanvas::set_source_rgba (cr, fill_active_color);
258  cairo_fill (cr);
259  } else { //inactive, or it has an indicator
260  //background color
261  ArdourCanvas::set_source_rgba (cr, fill_inactive_color);
262  }
263  cairo_fill (cr);
264  }
265 
266  // IMPLICIT ACTIVE: draw a border of the active color
267  if ((_elements & Body)==Body) {
268  if (active_state() == Gtkmm2ext::ImplicitActive && !((_elements & Indicator)==Indicator)) {
269  cairo_set_line_width (cr, 2.0);
270  rounded_function (cr, 2, 2, get_width() - 4, get_height() - 4, corner_radius-0.5);
271  ArdourCanvas::set_source_rgba (cr, fill_active_color);
272  cairo_stroke (cr);
273  }
274  }
275 
276  //show the "convex" or "concave" gradient
277  if (!_flat_buttons) {
278  if ( active_state() == Gtkmm2ext::ExplicitActive && ( !((_elements & Indicator)==Indicator) || use_custom_led_color) ) {
279  //concave
280  cairo_set_source (cr, concave_pattern);
281  Gtkmm2ext::rounded_rectangle (cr, 1, 1, get_width() - 2, get_height() - 2, corner_radius);
282  cairo_fill (cr);
283  } else {
284  cairo_set_source (cr, convex_pattern);
285  Gtkmm2ext::rounded_rectangle (cr, 1, 1, get_width() - 2, get_height() - 2, corner_radius);
286  cairo_fill (cr);
287  }
288  }
289 
290 #define VECTORICONSTROKEFILL(fillalpha) \
291  cairo_set_line_width(cr, 1.5); \
292  cairo_set_source_rgba (cr, 0, 0, 0, 1.0); \
293  cairo_stroke_preserve(cr); \
294  cairo_set_source_rgba (cr, 1, 1, 1, (fillalpha)); \
295  cairo_fill(cr);
296 
297  //Pixbuf, if any
298  if (_pixbuf) {
299  double x = rint((get_width() - _pixbuf->get_width()) * .5);
300  const double y = rint((get_height() - _pixbuf->get_height()) * .5);
301 #if 0 // DEBUG style (print on hover)
302  if (_hovering || (_elements & Inactive)) {
303  printf("%s: p:%dx%d (%dx%d)\n",
304  get_name().c_str(),
305  _pixbuf->get_width(), _pixbuf->get_height(),
306  get_width(), get_height());
307  }
308 #endif
309  if (_elements & Menu) {
310  //if this is a DropDown with an icon, then we need to
311  //move the icon left slightly to accomomodate the arrow
312  x -= _diameter - 2;
313  }
314  cairo_rectangle (cr, x, y, _pixbuf->get_width(), _pixbuf->get_height());
315  gdk_cairo_set_source_pixbuf (cr, _pixbuf->gobj(), x, y);
316  cairo_fill (cr);
317  }
318  else /* VectorIcons are exclusive to Pixbuf Icons */
319  /* TODO separate these into dedicated class
320  * it may also be efficient to render them only once for every size (image-surface) */
321  if ((_elements & VectorIcon) && _icon == RecTapeMode) {
322  const double x = get_width() * .5;
323  const double y = get_height() * .5;
324  const double r = std::min(x, y) * .6;
325  const double slit = .11 * M_PI;
326  cairo_save(cr);
327  cairo_translate(cr, x, y);
328 
329  cairo_arc (cr, 0, 0, r, 0, 2 * M_PI);
331  cairo_set_source_rgba (cr, .95, .1, .1, 1.);
332  } else {
333  cairo_set_source_rgba (cr, .95, .44, .44, 1.); // #f46f6f
334  }
335  cairo_fill_preserve(cr);
336  cairo_set_source_rgba (cr, .0, .0, .0, .5);
337  cairo_set_line_width(cr, 1);
338  cairo_stroke(cr);
339 
340  cairo_save(cr);
341  cairo_set_source_rgba (cr, .15, .07, .07, 1.0);
342 
343  cairo_rotate (cr, -.5 * M_PI);
344  cairo_move_to(cr, 0, 0);
345  cairo_arc (cr, 0, 0, r *.85, -slit, slit);
346  cairo_line_to(cr, 0, 0);
347  cairo_close_path(cr);
348 
349  cairo_fill(cr);
350  cairo_rotate (cr, 2. * M_PI / 3.);
351 
352  cairo_move_to(cr, 0, 0);
353  cairo_arc (cr, 0, 0, r *.85, -slit, slit);
354  cairo_line_to(cr, 0, 0);
355  cairo_close_path(cr);
356  cairo_fill(cr);
357 
358  cairo_rotate (cr, 2. * M_PI / 3.);
359  cairo_move_to(cr, 0, 0);
360  cairo_arc (cr, 0, 0, r *.85, -slit, slit);
361  cairo_line_to(cr, 0, 0);
362  cairo_close_path(cr);
363  cairo_fill(cr);
364 
365  cairo_restore(cr);
366 
367  cairo_arc (cr, 0, 0, r * .3, 0, 2 * M_PI);
369  cairo_set_source_rgba (cr, .95, .1, .1, 1.);
370  else
371  cairo_set_source_rgba (cr, .95, .44, .44, 1.); // #f46f6f
372  cairo_fill(cr);
373  cairo_set_source_rgba (cr, .0, .0, .0, 1.0);
374  cairo_arc (cr, 0, 0, r *.15, 0, 2 * M_PI); // hole in the middle
375  cairo_fill(cr);
376 
377  cairo_restore(cr);
378  }
379  else if ((_elements & VectorIcon) && _icon == RecButton) {
380  const double x = get_width() * .5;
381  const double y = get_height() * .5;
382  const double r = std::min(x, y) * .55;
383  cairo_arc (cr, x, y, r, 0, 2 * M_PI);
385  cairo_set_source_rgba (cr, .95, .1, .1, 1.);
386  else
387  cairo_set_source_rgba (cr, .95, .44, .44, 1.); // #f46f6f
388  cairo_fill_preserve(cr);
389  cairo_set_source_rgba (cr, .0, .0, .0, .8);
390  cairo_set_line_width(cr, 1);
391  cairo_stroke(cr);
392  }
393  else if ((_elements & VectorIcon) && _icon == CloseCross) {
394  const double x = get_width() * .5;
395  const double y = get_height() * .5;
396  const double o = .5 + std::min(x, y) * .4;
397  ArdourCanvas::set_source_rgba (cr, text_color);
398  cairo_set_line_width(cr, 1);
399  cairo_move_to(cr, x-o, y-o);
400  cairo_line_to(cr, x+o, y+o);
401  cairo_move_to(cr, x+o, y-o);
402  cairo_line_to(cr, x-o, y+o);
403  cairo_stroke(cr);
404  }
405  else if ((_elements & VectorIcon) && _icon == StripWidth) {
406  const double x0 = get_width() * .2;
407  const double x1 = get_width() * .8;
408 
409  const double y0 = get_height() * .25;
410  const double y1= get_height() * .75;
411 
412  const double ym= get_height() * .5;
413 
414  // arrow
415  const double xa0= get_height() * .39;
416  const double xa1= get_height() * .61;
417  const double ya0= get_height() * .35;
418  const double ya1= get_height() * .65;
419 
420  ArdourCanvas::set_source_rgba (cr, text_color);
421  cairo_set_line_width(cr, 1);
422 
423  // left + right
424  cairo_move_to(cr, x0, y0);
425  cairo_line_to(cr, x0, y1);
426  cairo_move_to(cr, x1, y0);
427  cairo_line_to(cr, x1, y1);
428 
429  // horiz center line
430  cairo_move_to(cr, x0, ym);
431  cairo_line_to(cr, x1, ym);
432 
433  // arrow left
434  cairo_move_to(cr, x0, ym);
435  cairo_line_to(cr, xa0, ya0);
436  cairo_move_to(cr, x0, ym);
437  cairo_line_to(cr, xa0, ya1);
438 
439  // arrow right
440  cairo_move_to(cr, x1, ym);
441  cairo_line_to(cr, xa1, ya0);
442  cairo_move_to(cr, x1, ym);
443  cairo_line_to(cr, xa1, ya1);
444  cairo_stroke(cr);
445  }
446  else if ((_elements & VectorIcon) && _icon == DinMidi) {
447  const double x = get_width() * .5;
448  const double y = get_height() * .5;
449  const double r = std::min(x, y) * .75;
450  ArdourCanvas::set_source_rgba (cr, text_color);
451  cairo_set_line_width(cr, 1);
452  cairo_arc (cr, x, y, r, .57 * M_PI, 2.43 * M_PI);
453  cairo_stroke(cr);
454 
455  // pins equally spaced 45deg
456  cairo_arc (cr, x, y * 0.5, r * .15, 0, 2 * M_PI);
457  cairo_fill(cr);
458  cairo_arc (cr, x * 0.5, y, r * .15, 0, 2 * M_PI);
459  cairo_fill(cr);
460  cairo_arc (cr, x * 1.5, y, r * .15, 0, 2 * M_PI);
461  cairo_fill(cr);
462  // .5 + .5 * .5 * sin(45deg), 1.5 - .5 * .5 * cos(45deg)
463  cairo_arc (cr, x * 0.677, y * .677, r * .15, 0, 2 * M_PI);
464  cairo_fill(cr);
465  cairo_arc (cr, x * 1.323, y * .677, r * .15, 0, 2 * M_PI);
466  cairo_fill(cr);
467 
468  // bottom notch
469  cairo_arc (cr, x, y+r, r * .26, 1.05 * M_PI, 1.95 * M_PI);
470  cairo_stroke(cr);
471  }
472  else if ((_elements & VectorIcon) && _icon == TransportStop) {
473  const int wh = std::min (get_width(), get_height());
474  cairo_rectangle (cr,
475  (get_width() - wh) * .5 + wh * .25,
476  (get_height() - wh) * .5 + wh * .25,
477  wh * .5, wh * .5);
478 
480  }
481  else if ((_elements & VectorIcon) && _icon == TransportPlay) {
482  const int wh = std::min (get_width(), get_height()) * .5;
483  const double y = get_height() * .5;
484  const double x = get_width() - wh;
485 
486  const float tri = ceil(.577 * wh); // 1/sqrt(3)
487 
488  cairo_move_to (cr, x + wh * .5, y);
489  cairo_line_to (cr, x - wh * .5, y - tri);
490  cairo_line_to (cr, x - wh * .5, y + tri);
491  cairo_close_path (cr);
492 
494  }
495  else if ((_elements & VectorIcon) && _icon == TransportPanic) {
496  const int wh = std::min (get_width(), get_height()) * .1;
497  const double xc = get_width() * .5;
498  const double yh = get_height();
499  cairo_rectangle (cr,
500  xc - wh, yh *.19,
501  wh * 2, yh *.41);
503 
504  cairo_arc (cr, xc, yh *.75, wh, 0, 2 * M_PI);
506  }
507  else if ((_elements & VectorIcon) && (_icon == TransportStart || _icon == TransportEnd || _icon == TransportRange)) {
508  // small play triangle
509  int wh = std::min (get_width(), get_height());
510  const double y = get_height() * .5;
511  const double x = get_width() - wh * .5;
512  wh *= .18;
513  const float tri = ceil(.577 * wh * 2); // 1/sqrt(3)
514 
515  const float ln = std::min (get_width(), get_height()) * .07;
516 
517  if (_icon == TransportStart || _icon == TransportRange) {
518  cairo_rectangle (cr,
519  x - wh - ln, y - tri * 1.7,
520  ln * 2, tri * 3.4);
521 
523  }
524 
525  if (_icon == TransportEnd || _icon == TransportRange) {
526  cairo_rectangle (cr,
527  x + wh - ln, y - tri * 1.7,
528  ln * 2, tri * 3.4);
529 
531  }
532 
533  if (_icon == TransportStart) {
534  cairo_move_to (cr, x - wh, y);
535  cairo_line_to (cr, x + wh, y - tri);
536  cairo_line_to (cr, x + wh, y + tri);
537  } else {
538  cairo_move_to (cr, x + wh, y);
539  cairo_line_to (cr, x - wh, y - tri);
540  cairo_line_to (cr, x - wh, y + tri);
541  }
542 
543  cairo_close_path (cr);
545  }
546  else if ((_elements & VectorIcon) && _icon == TransportLoop) {
547  const double x = get_width() * .5;
548  const double y = get_height() * .5;
549  const double r = std::min(x, y);
550 
551  cairo_arc (cr, x, y, r * .62, 0, 2 * M_PI);
552  cairo_arc_negative (cr, x, y, r * .35, 2 * M_PI, 0);
553 
555 #define ARCARROW(rad, ang) \
556  x + (rad) * sin((ang) * 2.0 * M_PI), y + (rad) * cos((ang) * 2.0 * M_PI)
557 
558  cairo_move_to (cr, ARCARROW(r * .35, .72));
559  cairo_line_to (cr, ARCARROW(r * .15, .72));
560  cairo_line_to (cr, ARCARROW(r * .56, .60));
561  cairo_line_to (cr, ARCARROW(r * .75, .72));
562  cairo_line_to (cr, ARCARROW(r * .62, .72));
563 
564  cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
565  cairo_stroke_preserve(cr);
566  cairo_close_path (cr);
567  cairo_set_source_rgba (cr, 1, 1, 1, 1.0);
568  cairo_fill(cr);
569 #undef ARCARROW
570  }
571  else if ((_elements & VectorIcon) && _icon == TransportMetronom) {
572  const double x = get_width() * .5;
573  const double y = get_height() * .5;
574  const double wh = std::min(x, y);
575  const double h = wh * .85;
576  const double w = wh * .55;
577  const double lw = w * .34;
578 
579  cairo_rectangle (cr,
580  x - w * .7, y + h * .25,
581  w * 1.4, lw);
582 
584 
585  cairo_move_to (cr, x - w, y + h);
586  cairo_line_to (cr, x + w, y + h);
587  cairo_line_to (cr, x + w * .35, y - h);
588  cairo_line_to (cr, x - w * .35, y - h);
589  cairo_line_to (cr, x - w, y + h);
590 
591  cairo_move_to (cr, x - w + lw, y + h -lw);
592  cairo_line_to (cr, x - w * .35 + lw, y - h + lw);
593  cairo_line_to (cr, x + w * .35 - lw, y - h + lw);
594  cairo_line_to (cr, x + w - lw, y + h -lw);
595  cairo_line_to (cr, x - w + lw, y + h -lw);
596 
598 
599  // ddx = .70 w = .75 * .5 wh = .375 wh
600  // ddy = .75 h - lw = .75 * .8 wh - wh .5 * .2 = .5 wh
601  // ang = (ddx/ddy):
602  // -> angle = atan (ang) = atan (375 / .5) ~= 36deg
603  const double dx = lw * .2; // 1 - cos(tan^-1(ang))
604  const double dy = lw * .4; // 1 - sin(tan^-1(ang))
605  cairo_move_to (cr, x - w * .3 , y + h * .25 + lw * .5);
606  cairo_line_to (cr, x - w + dx , y - h + lw + dy);
607  cairo_line_to (cr, x - w + lw , y - h + lw);
608  cairo_line_to (cr, x - w * .3 + lw, y + h * .25 + lw * .5);
609  cairo_close_path (cr);
610 
612 
613  cairo_rectangle (cr,
614  x - w * .7, y + h * .25,
615  w * 1.4, lw);
616  cairo_fill(cr);
617  }
618  else if (_elements & VectorIcon) {
619  // missing icon
620  assert(0);
621  }
622 #undef VECTORICONSTROKEFILL
623 
624  const int text_margin = char_pixel_width();
625  // Text, if any
626  if (!_pixbuf && ((_elements & Text)==Text) && !_text.empty()) {
627  assert(_layout);
628 #if 0 // DEBUG style (print on hover)
629  if (_hovering || (_elements & Inactive)) {
630  bool layout_font = true;
631  Pango::FontDescription fd = _layout->get_font_description();
632  if (fd.gobj() == NULL) {
633  layout_font = false;
634  fd = get_pango_context()->get_font_description();
635  }
636  printf("%s: f:%dx%d aw:%.3f bh:%.0f t:%dx%d (%dx%d) %s\"%s\"\n",
637  get_name().c_str(),
641  get_width(), get_height(),
642  layout_font ? "L:" : "W:",
643  fd.to_string().c_str());
644  }
645 #endif
646 
647  cairo_save (cr);
648  cairo_rectangle (cr, 2, 1, get_width() - 4, get_height() - 2);
649  cairo_clip(cr);
650 
651  cairo_new_path (cr);
652  ArdourCanvas::set_source_rgba (cr, text_color);
653  const double text_ypos = (get_height() - _text_height) * .5;
654 
655  if (_elements & Menu) {
656  // always left align (dropdown)
657  cairo_move_to (cr, text_margin, text_ypos);
658  pango_cairo_show_layout (cr, _layout->gobj());
659  } else if ( (_elements & Indicator) == Indicator) {
660  // left/right align depending on LED position
661  if (_led_left) {
662  cairo_move_to (cr, text_margin + _diameter + .5 * char_pixel_width(), text_ypos);
663  } else {
664  cairo_move_to (cr, text_margin, text_ypos);
665  }
666  pango_cairo_show_layout (cr, _layout->gobj());
667  } else {
668  /* centered text otherwise */
669  double ww, wh;
670  double xa, ya;
671  ww = get_width();
672  wh = get_height();
673 
674  cairo_save (cr);
675  cairo_rotate(cr, _angle * M_PI / 180.0);
676  cairo_device_to_user(cr, &ww, &wh);
677  xa = (ww - _text_width) * _xalign;
678  ya = (wh - _text_height) * _yalign;
679 
680  /* quick hack for left/bottom alignment at -90deg
681  * TODO this should be generalized incl rotation.
682  * currently only 'user' of this API is meter_strip.cc
683  */
684  if (_xalign < 0) xa = ceil(.5 + (ww * fabs(_xalign) + text_margin));
685 
686  cairo_move_to (cr, xa, ya);
687  pango_cairo_update_layout(cr, _layout->gobj());
688  pango_cairo_show_layout (cr, _layout->gobj());
689  cairo_restore (cr);
690  }
691  cairo_restore (cr);
692  }
693 
694  //Menu "triangle"
695  if (_elements & Menu) {
696  const float trih = ceil(_diameter * .5);
697  const float triw2 = ceil(.577 * _diameter * .5); // 1/sqrt(3) Equilateral triangle
698  //menu arrow
699  cairo_set_source_rgba (cr, 1, 1, 1, 0.4);
700  cairo_move_to(cr, get_width() - triw2 - 3. , rint((get_height() + trih) * .5));
701  cairo_rel_line_to(cr, -triw2, -trih);
702  cairo_rel_line_to(cr, 2. * triw2, 0);
703  cairo_close_path(cr);
704 
705  cairo_set_source_rgba (cr, 1, 1, 1, 0.4);
706  cairo_fill(cr);
707 
708  cairo_move_to(cr, get_width() - triw2 - 3 , rint((get_height() + trih) * .5));
709  cairo_rel_line_to(cr, .5 - triw2, .5 - trih);
710  cairo_rel_line_to(cr, 2. * triw2 - 1, 0);
711  cairo_close_path(cr);
712  cairo_set_source_rgba (cr, 0, 0, 0, 0.8);
713  cairo_set_line_width(cr, 1);
714  cairo_stroke(cr);
715  }
716 
717  //Indicator LED
718  if (_elements & Indicator) {
719  cairo_save (cr);
720 
721  /* move to the center of the indicator/led */
722  if (_elements & Text) {
723  int led_xoff = ceil(char_pixel_width() + _diameter * .5);
724  if (_led_left) {
725  cairo_translate (cr, led_xoff, get_height() * .5);
726  } else {
727  cairo_translate (cr, get_width() - led_xoff, get_height() * .5);
728  }
729  } else {
730  cairo_translate (cr, get_width() * .5, get_height() * .5);
731  }
732 
733  //inset
734  if (!_flat_buttons) {
735  cairo_arc (cr, 0, 0, _diameter * .5, 0, 2 * M_PI);
736  cairo_set_source (cr, led_inset_pattern);
737  cairo_fill (cr);
738  }
739 
740  //black ring
741  cairo_set_source_rgb (cr, 0, 0, 0);
742  cairo_arc (cr, 0, 0, _diameter * .5 - 1 * ARDOUR_UI::ui_scale, 0, 2 * M_PI);
743  cairo_fill(cr);
744 
745  //led color
746  ArdourCanvas::set_source_rgba (cr, led_color);
747  cairo_arc (cr, 0, 0, _diameter * .5 - 3 * ARDOUR_UI::ui_scale, 0, 2 * M_PI);
748  cairo_fill(cr);
749 
750  cairo_restore (cr);
751  }
752 
753  // a transparent overlay to indicate insensitivity
755  rounded_function (cr, 0, 0, get_width(), get_height(), corner_radius);
756  uint32_t ins_color = ARDOUR_UI::config()->color ("gtk_background");
757  ArdourCanvas::set_source_rgb_a (cr, ins_color, 0.6);
758  cairo_fill (cr);
759  }
760 
761  // if requested, show hovering
762  if (ARDOUR_UI::config()->get_widget_prelight()
763  && !((visual_state() & Gtkmm2ext::Insensitive))) {
764  if (_hovering) {
765  rounded_function (cr, 1, 1, get_width() - 2, get_height() - 2, corner_radius);
766  cairo_set_source_rgba (cr, 0.905, 0.917, 0.925, 0.2);
767  cairo_fill (cr);
768  }
769  }
770 
771  //user is currently pressing the button. dark outline helps to indicate this
772  if (_grabbed && !(_elements & (Inactive|Menu))) {
773  rounded_function (cr, 1, 1, get_width() - 2, get_height() - 2, corner_radius);
774  cairo_set_line_width(cr, 2);
775  cairo_set_source_rgba (cr, 0.1, 0.1, 0.1, .5);
776  cairo_stroke (cr);
777  }
778 
779  //some buttons (like processor boxes) can be selected (so they can be deleted). Draw a selection indicator
781  cairo_set_line_width(cr, 1);
782  cairo_set_source_rgba (cr, 1, 0, 0, 0.8);
783  rounded_function (cr, 0.5, 0.5, get_width() - 1, get_height() - 1, corner_radius);
784  cairo_stroke (cr);
785  }
786 
787  //I guess this means we have keyboard focus. I don't think this works currently
788  //
789  //A: yes, it's keyboard focus and it does work when there's no editor window
790  // (the editor is always the first receiver for KeyDown).
791  // It's needed for eg. the engine-dialog at startup or after closing a sesion.
792  if (_focused) {
793  rounded_function (cr, 1.5, 1.5, get_width() - 3, get_height() - 3, corner_radius);
794  cairo_set_source_rgba (cr, 0.905, 0.917, 0.925, 0.8);
795  double dashes = 1;
796  cairo_set_dash (cr, &dashes, 1, 0);
797  cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
798  cairo_set_line_width (cr, 1.0);
799  cairo_stroke (cr);
800  cairo_set_dash (cr, 0, 0, 0);
801  }
802 }
803 
804 void
806 {
807  _corner_radius = r;
809 }
810 
811 void
813 {
814  CairoWidget::on_realize ();
815  ensure_layout ();
816  if (_layout && _layout->get_text() != _text) {
817  _layout->set_text (_text);
818  queue_resize ();
819  }
820 }
821 
822 void
823 ArdourButton::on_size_request (Gtk::Requisition* req)
824 {
825  req->width = req->height = 0;
826  CairoWidget::on_size_request (req);
827 
828  if (_diameter == 0) {
829  const float newdia = rintf (11.f * ARDOUR_UI::ui_scale);
830  if (_diameter != newdia) {
831  _pattern_height = 0;
832  _diameter = newdia;
833  }
834  }
835 
836  if ((_elements & Text) && !_text.empty()) {
837  // if _layout does not exist, char_pixel_height() creates it,
838  req->height = std::max(req->height, (int) ceil(char_pixel_height() * BASELINESTRETCH + 1.0));
839  _layout->get_pixel_size (_text_width, _text_height);
840  req->width += rint(1.75 * char_pixel_width()); // padding
841  req->width += _text_width;
842  } else {
843  _text_width = 0;
844  _text_height = 0;
845  }
846 
847  if (_pixbuf) {
848  req->width += _pixbuf->get_width() + char_pixel_width();
849  req->height = std::max(req->height, _pixbuf->get_height() + 4);
850  }
851 
852  if (_elements & Indicator) {
853  req->width += lrint (_diameter) + char_pixel_width();
854  req->height = std::max (req->height, (int) lrint (_diameter) + 4);
855  }
856 
857  if ((_elements & Menu)) {
858  req->width += _diameter + 4;
859  }
860 
861  if (_elements & VectorIcon) {
862  assert(!(_elements & Text));
863  const int wh = std::max (rint (TRACKHEADERBTNW * char_avg_pixel_width()), ceil (char_pixel_height() * BASELINESTRETCH + 1.));
864  req->width += wh;
865  req->height = std::max(req->height, wh);
866  }
867 
868  /* Tweaks to mess the nice stuff above up again. */
869  if (_tweaks & TrackHeader) {
870  // forget everything above and just use a fixed square [em] size
871  // "TrackHeader Buttons" are single letter (usually uppercase)
872  // a SizeGroup is much less efficient (lots of gtk work under the hood for each track)
873  const int wh = std::max (rint (TRACKHEADERBTNW * char_avg_pixel_width()), ceil (char_pixel_height() * BASELINESTRETCH + 1.));
874  req->width = wh;
875  req->height = wh;
876  }
877  else if (_tweaks & Square) {
878  // currerntly unused (again)
879  if (req->width < req->height)
880  req->width = req->height;
881  if (req->height < req->width)
882  req->height = req->width;
883  } else if (_text_width > 0 && !(_elements & (Menu | Indicator))) {
884  // properly centered text for those elements that are centered
885  // (no sub-pixel offset)
886  if ((req->width - _text_width) & 1) { ++req->width; }
887  if ((req->height - _text_height) & 1) { ++req->height; }
888  }
889 #if 0
890  printf("REQ: %s: %dx%d\n", get_name().c_str(), req->width, req->height);
891 #endif
892 }
893 
898 void
900 {
901  _update_colors = false;
902  if (_fixed_colors_set) {
903  return;
904  }
905  std::string name = get_name();
906  bool failed = false;
907 
908  fill_active_color = ARDOUR_UI::config()->color (string_compose ("%1: fill active", name), &failed);
909  if (failed) {
910  fill_active_color = ARDOUR_UI::config()->color ("generic button: fill active");
911  }
912  fill_inactive_color = ARDOUR_UI::config()->color (string_compose ("%1: fill", name), &failed);
913  if (failed) {
914  fill_inactive_color = ARDOUR_UI::config()->color ("generic button: fill");
915  }
916 
919 
920  led_active_color = ARDOUR_UI::config()->color (string_compose ("%1: led active", name), &failed);
921  if (failed) {
922  led_active_color = ARDOUR_UI::config()->color ("generic button: led active");
923  }
924 
925  /* The inactive color for the LED is just a fairly dark version of the
926  * active color.
927  */
928 
929  ArdourCanvas::HSV inactive (led_active_color);
930  inactive.v = 0.35;
931 
932  led_inactive_color = inactive.color ();
933 }
934 
940 void ArdourButton::set_fixed_colors (const uint32_t color_active, const uint32_t color_inactive)
941 {
942  _fixed_colors_set = true;
943 
944  fill_active_color = color_active;
945  fill_inactive_color = color_inactive;
946 
947  unsigned char r, g, b, a;
948  UINT_TO_RGBA(color_active, &r, &g, &b, &a);
949 
950  double white_contrast = (max (double(r), 255.) - min (double(r), 255.)) +
951  (max (double(g), 255.) - min (double(g), 255.)) +
952  (max (double(b), 255.) - min (double(b), 255.));
953 
954  double black_contrast = (max (double(r), 0.) - min (double(r), 0.)) +
955  (max (double(g), 0.) - min (double(g), 0.)) +
956  (max (double(b), 0.) - min (double(b), 0.));
957 
958  text_active_color = (white_contrast > black_contrast) ?
959  RGBA_TO_UINT(255, 255, 255, 255) : /* use white */
960  RGBA_TO_UINT( 0, 0, 0, 255); /* use black */
961 
962 
963  UINT_TO_RGBA(color_inactive, &r, &g, &b, &a);
964 
965  white_contrast = (max (double(r), 255.) - min (double(r), 255.)) +
966  (max (double(g), 255.) - min (double(g), 255.)) +
967  (max (double(b), 255.) - min (double(b), 255.));
968 
969  black_contrast = (max (double(r), 0.) - min (double(r), 0.)) +
970  (max (double(g), 0.) - min (double(g), 0.)) +
971  (max (double(b), 0.) - min (double(b), 0.));
972 
973  text_inactive_color = (white_contrast > black_contrast) ?
974  RGBA_TO_UINT(255, 255, 255, 255) : /* use white */
975  RGBA_TO_UINT( 0, 0, 0, 255); /* use black */
976 
977  /* XXX what about led colors ? */
979 }
980 
981 void
983 {
984  if (convex_pattern) {
985  cairo_pattern_destroy (convex_pattern);
986  convex_pattern = 0;
987  }
988 
989  if (concave_pattern) {
990  cairo_pattern_destroy (concave_pattern);
991  concave_pattern = 0;
992  }
993 
994  if (led_inset_pattern) {
995  cairo_pattern_destroy (led_inset_pattern);
996  led_inset_pattern = 0;
997  }
998 
999  //convex gradient
1000  convex_pattern = cairo_pattern_create_linear (0.0, 0, 0.0, get_height());
1001  cairo_pattern_add_color_stop_rgba (convex_pattern, 0.0, 0,0,0, 0.0);
1002  cairo_pattern_add_color_stop_rgba (convex_pattern, 1.0, 0,0,0, 0.35);
1003 
1004  //concave gradient
1005  concave_pattern = cairo_pattern_create_linear (0.0, 0, 0.0, get_height());
1006  cairo_pattern_add_color_stop_rgba (concave_pattern, 0.0, 0,0,0, 0.5);
1007  cairo_pattern_add_color_stop_rgba (concave_pattern, 0.7, 0,0,0, 0.0);
1008 
1009  led_inset_pattern = cairo_pattern_create_linear (0.0, 0.0, 0.0, _diameter);
1010  cairo_pattern_add_color_stop_rgba (led_inset_pattern, 0, 0,0,0, 0.4);
1011  cairo_pattern_add_color_stop_rgba (led_inset_pattern, 1, 1,1,1, 0.7);
1012 
1013  _pattern_height = get_height() ;
1014 }
1015 
1016 void
1018 {
1019  _led_left = yn;
1020 }
1021 
1022 bool
1024 {
1025  focus_handler ();
1026 
1027  if (ev->button == 1 && (_elements & Indicator) && _led_rect && _distinct_led_click) {
1028  if (ev->x >= _led_rect->x && ev->x < _led_rect->x + _led_rect->width &&
1029  ev->y >= _led_rect->y && ev->y < _led_rect->y + _led_rect->height) {
1030  return true;
1031  }
1032  }
1033 
1035  return true;
1036  }
1037 
1038  _grabbed = true;
1040 
1041  if (ev->button == 1 && !_act_on_release) {
1042  if (_action) {
1043  _action->activate ();
1044  return true;
1045  }
1046  }
1047 
1049  return false;
1050 
1051  return true;
1052 }
1053 
1054 bool
1056 {
1057  if (ev->button == 1 && _hovering && (_elements & Indicator) && _led_rect && _distinct_led_click) {
1058  if (ev->x >= _led_rect->x && ev->x < _led_rect->x + _led_rect->width &&
1059  ev->y >= _led_rect->y && ev->y < _led_rect->y + _led_rect->height) {
1060  signal_led_clicked(); /* EMIT SIGNAL */
1061  return true;
1062  }
1063  }
1064 
1065  _grabbed = false;
1067 
1068  if (ev->button == 1 && _hovering) {
1069  signal_clicked ();
1070  if (_act_on_release) {
1071  if (_action) {
1072  _action->activate ();
1073  return true;
1074  }
1075  }
1076  }
1077 
1079  return false;
1080 
1081  return true;
1082 }
1083 
1084 void
1086 {
1087  _distinct_led_click = yn;
1088  setup_led_rect ();
1089 }
1090 
1091 void
1093 {
1094  _update_colors = true;
1096 }
1097 
1098 void
1100 {
1102  setup_led_rect ();
1103 }
1104 
1105 void
1107 {
1110 }
1111 
1112 void
1114 {
1116 
1117  if (!c) {
1118  warning << _("button cannot watch state of non-existing Controllable\n") << endmsg;
1119  return;
1120  }
1121  c->Changed.connect (watch_connection, invalidator(*this), boost::bind (&ArdourButton::controllable_changed, this), gui_context());
1122 }
1123 
1124 void
1126 {
1127  float val = binding_proxy.get_controllable()->get_value();
1128 
1129  if (fabs (val) >= 0.5f) {
1131  } else {
1132  unset_active_state ();
1133  }
1135 }
1136 
1137 void
1139 {
1141 
1142  if (_action) {
1143 
1145 
1146  Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (_action);
1147  if (tact) {
1148  action_toggled ();
1149  tact->signal_toggled().connect (sigc::mem_fun (*this, &ArdourButton::action_toggled));
1150  }
1151 
1152  _action->connect_property_changed ("sensitive", sigc::mem_fun (*this, &ArdourButton::action_sensitivity_changed));
1153  _action->connect_property_changed ("visible", sigc::mem_fun (*this, &ArdourButton::action_visibility_changed));
1154  _action->connect_property_changed ("tooltip", sigc::mem_fun (*this, &ArdourButton::action_tooltip_changed));
1155  }
1156 }
1157 
1158 void
1160 {
1161  Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (_action);
1162 
1163  if (tact) {
1164  if (tact->get_active()) {
1166  } else {
1167  unset_active_state ();
1168  }
1169  }
1170 }
1171 
1172 void
1173 ArdourButton::on_style_changed (const RefPtr<Gtk::Style>&)
1174 {
1175  _update_colors = true;
1177 }
1178 
1179 void
1181 {
1182  _char_pixel_width = 0;
1183  _char_pixel_height = 0;
1184  _diameter = 0;
1185  _update_colors = true;
1186  if (is_realized()) {
1187  queue_resize ();
1188  }
1189 }
1190 
1191 void
1193 {
1194  if (!(_elements & Indicator)) {
1195  delete _led_rect;
1196  _led_rect = 0;
1197  return;
1198  }
1199 
1200  if (!_led_rect) {
1201  _led_rect = new cairo_rectangle_t;
1202  }
1203 
1204  if (_elements & Text) {
1205  if (_led_left) {
1206  _led_rect->x = char_pixel_width();
1207  } else {
1208  _led_rect->x = get_width() - char_pixel_width() + _diameter;
1209  }
1210  } else {
1211  /* centered */
1212  _led_rect->x = .5 * get_width() - _diameter;
1213  }
1214 
1215  _led_rect->y = .5 * (get_height() - _diameter);
1216  _led_rect->width = _diameter;
1217  _led_rect->height = _diameter;
1218 }
1219 
1220 void
1221 ArdourButton::set_image (const RefPtr<Gdk::Pixbuf>& img)
1222 {
1223  _pixbuf = img;
1224  if (is_realized()) {
1225  queue_resize ();
1226  }
1227 }
1228 
1229 void
1230 ArdourButton::set_active_state (Gtkmm2ext::ActiveState s)
1231 {
1232  bool changed = (_active_state != s);
1234  if (changed) {
1235  _update_colors = true;
1237  }
1238 }
1239 
1240 void
1241 ArdourButton::set_visual_state (Gtkmm2ext::VisualState s)
1242 {
1243  bool changed = (_visual_state != s);
1245  if (changed) {
1246  _update_colors = true;
1248  }
1249 }
1250 
1251 bool
1253 {
1254  _focused = true;
1256  return CairoWidget::on_focus_in_event (ev);
1257 }
1258 
1259 bool
1261 {
1262  _focused = false;
1264  return CairoWidget::on_focus_out_event (ev);
1265 }
1266 
1267 bool
1269  if (_focused &&
1270  (ev->keyval == GDK_space || ev->keyval == GDK_Return))
1271  {
1272  signal_clicked();
1273  if (_action) {
1274  _action->activate ();
1275  }
1276  return true;
1277  }
1278  return CairoWidget::on_key_release_event (ev);
1279 }
1280 
1281 bool
1283 {
1284  _hovering = (_elements & Inactive) ? false : true;
1285 
1286  if (ARDOUR_UI::config()->get_widget_prelight()) {
1288  }
1289 
1290  return CairoWidget::on_enter_notify_event (ev);
1291 }
1292 
1293 bool
1295 {
1296  _hovering = false;
1297 
1298  if (ARDOUR_UI::config()->get_widget_prelight()) {
1300  }
1301 
1302  return CairoWidget::on_leave_notify_event (ev);
1303 }
1304 
1305 void
1307 {
1308  if (_tweaks != t) {
1309  _tweaks = t;
1310  if (is_realized()) {
1311  queue_resize ();
1312  }
1313  }
1314 }
1315 
1316 void
1318 {
1319  if (_action->property_sensitive ()) {
1320  set_visual_state (Gtkmm2ext::VisualState (visual_state() & ~Gtkmm2ext::Insensitive));
1321  } else {
1322  set_visual_state (Gtkmm2ext::VisualState (visual_state() | Gtkmm2ext::Insensitive));
1323  }
1324 }
1325 
1326 void
1328 {
1329  if (_layout_ellipsize_width == w) {
1330  return;
1331  }
1333  if (!_layout) {
1334  return;
1335  }
1336  if (_layout_ellipsize_width > 3 * PANGO_SCALE) {
1337  _layout->set_width (_layout_ellipsize_width - 3 * PANGO_SCALE);
1338  }
1339  if (is_realized ()) {
1340  queue_resize ();
1341  }
1342 }
1343 
1344 void
1345 ArdourButton::set_text_ellipsize (Pango::EllipsizeMode e)
1346 {
1347  if (_ellipsis == e) {
1348  return;
1349  }
1350  _ellipsis = e;
1351  if (!_layout) {
1352  return;
1353  }
1354  _layout->set_ellipsize(_ellipsis);
1355  if (_layout_ellipsize_width > 3 * PANGO_SCALE) {
1356  _layout->set_width (_layout_ellipsize_width - 3 * PANGO_SCALE);
1357  }
1358  if (is_realized ()) {
1359  queue_resize ();
1360  }
1361 }
1362 
1363 void
1365 {
1366  if (!_layout) {
1367  ensure_style ();
1368  _layout = Pango::Layout::create (get_pango_context());
1369  _layout->set_ellipsize(_ellipsis);
1370  if (_layout_ellipsize_width > 3 * PANGO_SCALE) {
1371  _layout->set_width (_layout_ellipsize_width - 3* PANGO_SCALE);
1372  }
1373  }
1374 }
1375 
1376 void
1378 {
1379  if (_char_pixel_height > 0 && _char_pixel_width > 0) {
1380  return;
1381  }
1382  ensure_layout();
1383  // NB. this is not static, since the geometry is different
1384  // depending on the font used.
1385  int w, h;
1386  std::string x = _("ABCDEFGHIJLKMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
1387  _layout->set_text (x);
1388  _layout->get_pixel_size (w, h);
1389  _char_pixel_height = std::max(4, h);
1390  // number of actual chars in the string (not bytes)
1391  // Glib to the rescue.
1392  Glib::ustring gx(x);
1393  _char_avg_pixel_width = w / (float)gx.size();
1394  _char_pixel_width = std::max(4, (int) ceil (_char_avg_pixel_width));
1395  _layout->set_text (_text);
1396 }
1397 
1398 void
1400 {
1401  if (_action->property_visible ()) {
1402  show ();
1403  } else {
1404  hide ();
1405  }
1406 }
1407 
1408 void
1410 {
1411  string str = _action->property_tooltip().get_value();
1412  ARDOUR_UI::instance()->set_tip (*this, str);
1413 }
1414 
1415 void
1417 {
1418  _elements = e;
1420 }
1421 
1422 void
1424 {
1427 }
1428 
1429 void
1431 {
1432  _icon = i;
1434 }
1435 
1436 void
1437 ArdourButton::set_custom_led_color (uint32_t c, bool useit)
1438 {
1439  if (led_custom_color == c && use_custom_led_color == useit) {
1440  return;
1441  }
1442 
1443  led_custom_color = c;
1444  use_custom_led_color = useit;
1446 }
bool _act_on_release
void action_sensitivity_changed()
void recalc_char_pixel_geometry()
BindingProxy binding_proxy
ArdourCanvas::Color color(const std::string &, bool *failed=0) const
Definition: ui_config.cc:567
uint32_t led_custom_color
sigc::signal< void > signal_led_clicked
bool use_custom_led_color
boost::shared_ptr< PBD::Controllable > get_controllable() const
Definition: binding_proxy.h:48
int _layout_ellipsize_width
sigc::signal< void > signal_clicked
PBD::ScopedConnection watch_connection
void action_toggled()
#define BASELINESTRETCH
sigc::signal< void > DPIReset
Definition: utils.cc:68
bool on_key_release_event(GdkEventKey *)
float _char_avg_pixel_width
void set_distinct_led_click(bool yn)
Definition: ardour_ui.h:130
static ARDOUR_UI * instance()
Definition: ardour_ui.h:187
Gtkmm2ext::ActiveState active_state() const
Definition: cairo_widget.h:40
void add_elements(Element)
#define VECTORICONSTROKEFILL(fillalpha)
void set_related_action(Glib::RefPtr< Gtk::Action >)
void set_controllable(boost::shared_ptr< PBD::Controllable >)
void on_size_allocate(Gtk::Allocation &)
bool on_focus_in_event(GdkEventFocus *)
tuple f
Definition: signals.py:35
Definition: Beats.hpp:239
virtual void set_active_state(Gtkmm2ext::ActiveState)
LIBPBD_API Transmitter warning
uint32_t text_active_color
cairo_pattern_t * concave_pattern
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
#define UINT_TO_RGBA(u, r, g, b, a)
Definition: fastmeter.cc:35
void set_image(const Glib::RefPtr< Gdk::Pixbuf > &)
void unset_active_state()
Definition: cairo_widget.h:50
void set_tweaks(Tweaks)
#define invalidator(x)
Definition: gui_thread.h:40
void action_tooltip_changed()
void set_elements(Element)
unsigned int _char_pixel_width
float char_avg_pixel_width()
void on_style_changed(const Glib::RefPtr< Gtk::Style > &)
static sigc::slot< void > focus_handler
Definition: cairo_widget.h:121
void set_custom_led_color(const uint32_t c, const bool useit=true)
Glib::RefPtr< Pango::Layout > _layout
#define _(Text)
Definition: i18n.h:11
cairo_pattern_t * convex_pattern
ArdourButton(Element e=default_elements)
virtual void set_related_action(Glib::RefPtr< Gtk::Action > a)
Definition: activatable.h:39
uint32_t contrasting_text_color(uint32_t c)
unsigned int _char_pixel_height
ExplicitActive
Definition: widget_state.h:13
bool on_leave_notify_event(GdkEventCrossing *)
std::string _text
void set_icon(Icon)
Pango::EllipsizeMode _ellipsis
void on_size_request(Gtk::Requisition *req)
Element _elements
Gtkmm2ext::VisualState visual_state() const
Definition: cairo_widget.h:41
LIBGTKMM2EXT_API void rounded_top_rectangle(Cairo::RefPtr< Cairo::Context > context, double x, double y, double w, double h, double r=10)
Definition: utils.cc:525
void build_patterns()
uint32_t led_inactive_color
ImplicitActive
Definition: widget_state.h:13
uint32_t text_inactive_color
void set_fixed_colors(const uint32_t active_color, const uint32_t inactive_color)
void set_controllable(boost::shared_ptr< PBD::Controllable > c)
void controllable_changed()
void set_visual_state(Gtkmm2ext::VisualState)
#define gui_context()
Definition: gui_thread.h:36
Selected
Definition: widget_state.h:21
void set_dirty()
bool _distinct_led_click
bool on_enter_notify_event(GdkEventCrossing *)
virtual double get_value(void) const =0
bool button_press_handler(GdkEventButton *)
static Element just_led_default_elements
Definition: ardour_button.h:65
Gtkmm2ext::VisualState _visual_state
Definition: cairo_widget.h:114
void set_active_state(Gtkmm2ext::ActiveState)
void set_layout_ellipsize_width(int w)
uint32_t fill_inactive_color
void set_layout_font(const Pango::FontDescription &)
uint32_t led_active_color
void setup_led_rect()
void set_tip(Gtk::Widget &w, const gchar *tip)
unsigned int char_pixel_width()
uint32_t fill_active_color
void set_angle(const double)
bool _fixed_colors_set
Insensitive
Definition: widget_state.h:23
Definition: panner2d.h:48
const char * name
bool _fallthrough_to_parent
void on_name_changed()
void set_text_ellipsize(Pango::EllipsizeMode)
void action_visibility_changed()
void color_handler()
static UIConfiguration * config()
Definition: ardour_ui.h:188
Glib::RefPtr< Gdk::Pixbuf > _pixbuf
void set_text(const std::string &)
bool on_focus_out_event(GdkEventFocus *)
sigc::signal< void > ColorsChanged
Definition: debug.h:30
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
bool on_button_press_event(GdkEventButton *)
bool on_button_release_event(GdkEventButton *)
virtual ~ArdourButton()
static bool _flat_buttons
Definition: cairo_widget.h:117
virtual void set_visual_state(Gtkmm2ext::VisualState)
float _corner_radius
Glib::RefPtr< Gtk::Action > _action
Definition: activatable.h:48
void set_alignment(const float, const float)
static Element default_elements
Definition: ardour_button.h:63
void render(cairo_t *, cairo_rectangle_t *)
#define RGBA_TO_UINT(r, g, b, a)
Definition: rgb_macros.h:34
static float ui_scale
Definition: ardour_ui.h:189
static Element led_default_elements
Definition: ardour_button.h:64
void ensure_layout()
#define ARCARROW(rad, ang)
cairo_rectangle_t * _led_rect
unsigned int char_pixel_height()
LIBGTKMM2EXT_API void rounded_top_left_rectangle(Cairo::RefPtr< Cairo::Context > context, double x, double y, double w, double h, double r=10)
Definition: utils.cc:530
LIBGTKMM2EXT_API void rounded_top_right_rectangle(Cairo::RefPtr< Cairo::Context > context, double x, double y, double w, double h, double r=10)
Definition: utils.cc:535
void set_led_left(bool yn)
void set_corner_radius(float)
#define TRACKHEADERBTNW
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
Gtkmm2ext::ActiveState _active_state
Definition: cairo_widget.h:111
void on_size_allocate(Gtk::Allocation &)
cairo_pattern_t * led_inset_pattern