2c191385f45ab163e2e5106971e53351318a90bb
[ardour.git] / gtk2_ardour / ardour_button.cc
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
29 #include "gtkmm2ext/utils.h"
30 #include "gtkmm2ext/rgb_macros.h"
31 #include "gtkmm2ext/gui_thread.h"
32
33 #include "ardour/rc_configuration.h" // for widget prelight preference
34
35 #include "ardour_button.h"
36 #include "ardour_ui.h"
37 #include "global_signals.h"
38
39 #include "i18n.h"
40
41 #define REFLECTION_HEIGHT 2
42
43 using namespace Gdk;
44 using namespace Gtk;
45 using namespace Glib;
46 using namespace PBD;
47 using std::max;
48 using std::min;
49 using namespace std;
50
51 ArdourButton::Element ArdourButton::default_elements = ArdourButton::Element (ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text);
52 ArdourButton::Element ArdourButton::led_default_elements = ArdourButton::Element (ArdourButton::default_elements|ArdourButton::Indicator);
53 ArdourButton::Element ArdourButton::just_led_default_elements = ArdourButton::Element (ArdourButton::Edge|ArdourButton::Body|ArdourButton::Indicator);
54 bool ArdourButton::_flat_buttons = false;
55
56 ArdourButton::ArdourButton (Element e)
57         : _elements (e)
58         , _tweaks (Tweaks (0))
59         , _text_width (0)
60         , _text_height (0)
61         , _diameter (11.0)
62         , _corner_radius (4.0)
63         , _corner_mask (0xf)
64         , _angle(0)
65         , _xalign(.5)
66         , _yalign(.5)
67         , border_color (0)
68         , fill_color_active (0)
69         , fill_color_inactive (0)
70         , fill_pattern (0)
71         , fill_pattern_active (0)
72         , shine_pattern (0)
73         , led_inset_pattern (0)
74         , reflection_pattern (0)
75         , _led_rect (0)
76         , _act_on_release (true)
77         , _led_left (false)
78         , _fixed_diameter (true)
79         , _distinct_led_click (false)
80         , _hovering (false)
81 {
82         ColorsChanged.connect (sigc::mem_fun (*this, &ArdourButton::color_handler));
83 }
84
85 ArdourButton::ArdourButton (const std::string& str, Element e)
86         : _elements (e)
87         , _tweaks (Tweaks (0))
88         , _text_width (0)
89         , _text_height (0)
90         , _diameter (11.0)
91         , _corner_radius (4.0)
92         , _corner_mask (0xf)
93         , _angle(0)
94         , _xalign(.5)
95         , _yalign(.5)
96         , border_color (0)
97         , fill_color_active (0)
98         , fill_color_inactive (0)
99         , fill_pattern (0)
100         , fill_pattern_active (0)
101         , shine_pattern (0)
102         , led_inset_pattern (0)
103         , reflection_pattern (0)
104         , _led_rect (0)
105         , _act_on_release (true)
106         , _led_left (false)
107         , _fixed_diameter (true)
108         , _distinct_led_click (false)
109         , _hovering (false)
110 {
111         set_text (str);
112 }
113
114 ArdourButton::~ArdourButton()
115 {
116         delete _led_rect;
117
118         if (shine_pattern) {
119                 cairo_pattern_destroy (shine_pattern);
120         }
121
122         if (fill_pattern) {
123                 cairo_pattern_destroy (fill_pattern);
124         }
125         
126         if (fill_pattern_active) {
127                 cairo_pattern_destroy (fill_pattern_active);
128         }
129         
130         if (led_inset_pattern) {
131                 cairo_pattern_destroy (led_inset_pattern);
132         }
133         
134         if (reflection_pattern) {
135                 cairo_pattern_destroy (reflection_pattern);
136         }
137
138 }
139
140 void
141 ArdourButton::set_text (const std::string& str)
142 {
143         _text = str;
144
145         if (!_layout && !_text.empty()) {
146                 _layout = Pango::Layout::create (get_pango_context());
147         } 
148
149         if (_layout) {
150                 _layout->set_text (str);
151         }
152
153         queue_resize ();
154 }
155
156 void
157 ArdourButton::set_markup (const std::string& str)
158 {
159         _text = str;
160
161         if (!_layout) {
162                 _layout = Pango::Layout::create (get_pango_context());
163         } 
164
165         _layout->set_text (str);
166         queue_resize ();
167 }
168
169 void
170 ArdourButton::set_angle (const double angle)
171 {
172         _angle = angle;
173 }
174
175 void
176 ArdourButton::set_alignment (const float xa, const float ya)
177 {
178         _xalign = xa;
179         _yalign = ya;
180 }
181
182 void
183 ArdourButton::render (cairo_t* cr)
184 {
185         void (*rounded_function)(cairo_t*, double, double, double, double, double);
186
187         switch (_corner_mask) {
188         case 0x1: /* upper left only */
189                 rounded_function = Gtkmm2ext::rounded_top_left_rectangle;
190                 break;
191         case 0x2: /* upper right only */
192                 rounded_function = Gtkmm2ext::rounded_top_right_rectangle;
193                 break;
194         case 0x3: /* upper only */
195                 rounded_function = Gtkmm2ext::rounded_top_rectangle;
196                 break;
197                 /* should really have functions for lower right, lower left,
198                    lower only, but for now, we don't
199                 */
200         default:
201                 rounded_function = Gtkmm2ext::rounded_rectangle;
202         }
203
204         if (!_fixed_diameter) {
205                 _diameter = std::min (get_width(), get_height());
206         }
207
208         float r,g,b,a;
209
210         if ((_elements & Body)==Body) {
211                 if (_elements & Edge) {
212
213                         cairo_set_source_rgba (cr, 0, 0, 0, 1);
214                         rounded_function(cr, 0, 0, get_width(), get_height(), _corner_radius);
215                         cairo_fill (cr);
216
217                         rounded_function (cr, 1, 1, get_width()-2, get_height()-2, _corner_radius - 1.5);
218                 } else {
219                         rounded_function (cr, 0, 0, get_width(), get_height(), _corner_radius);
220                 }
221
222                 if (active_state() == Gtkmm2ext::ImplicitActive) {
223                         
224                         if (!(_tweaks & ImplicitUsesSolidColor)) {
225                                 cairo_set_source (cr, fill_pattern);
226                         } else {
227                                 cairo_set_source (cr, fill_pattern_active);
228                         }
229                         cairo_fill (cr);
230                         
231                         if (!(_tweaks & ImplicitUsesSolidColor)) {
232                                 //border
233                                 UINT_TO_RGBA (fill_color_active, &r, &g, &b, &a);
234                                 cairo_set_line_width (cr, 1.0);
235                                 rounded_function (cr, 2, 2, get_width()-4, get_height()-4, _corner_radius - 1.5);
236                                 cairo_set_source_rgba (cr, r/255.0, g/255.0, b/255.0, a/255.0);
237                                 cairo_stroke (cr);
238                         }
239                                 
240                 } else if (active_state() == Gtkmm2ext::ExplicitActive || ((_elements & Indicator)==Indicator) ) {
241
242                         //background color
243                         cairo_set_source (cr, fill_pattern_active);
244                         cairo_fill (cr);
245
246                 } else {
247
248                         //background color
249                         cairo_set_source (cr, fill_pattern);
250                         cairo_fill (cr);
251
252                 }
253         }
254
255         if ( ((_elements & FlatFace)==FlatFace) && (active_state() != Gtkmm2ext::ExplicitActive) ) {
256
257                 if ( !_flat_buttons ) {
258                         float rheight = get_height()*0.5-REFLECTION_HEIGHT;
259                         Gtkmm2ext::rounded_rectangle (cr, 2, 3, get_width()-4, rheight, _corner_radius-1);
260                         cairo_set_source (cr, shine_pattern);
261                         cairo_fill (cr);
262                 }
263
264                 if (active_state() == Gtkmm2ext::ExplicitActive) {
265
266                         UINT_TO_RGBA (fill_color_active, &r, &g, &b, &a);
267                         cairo_set_line_width (cr, 2.0);
268                         rounded_function (cr, 2, 2, get_width()-4, get_height()-4, _corner_radius - 2.0);
269                         cairo_set_source_rgba (cr, r/255.0, g/255.0, b/255.0, a/255.0);
270                         cairo_fill (cr);
271
272                 } else {
273
274                         UINT_TO_RGBA (fill_color_inactive, &r, &g, &b, &a);
275                         cairo_set_line_width (cr, 2.0);
276                         rounded_function (cr, 2, 2, get_width()-4, get_height()-4, _corner_radius - 2.0);
277                         cairo_set_source_rgba (cr, r/255.0, g/255.0, b/255.0, a/255.0);
278                         cairo_fill (cr);
279
280                 }
281         }
282
283         if (_pixbuf) {
284
285                 double x,y;
286                 x = (get_width() - _pixbuf->get_width())/2.0;
287                 y = (get_height() - _pixbuf->get_height())/2.0;
288
289                 cairo_rectangle (cr, x, y, _pixbuf->get_width(), _pixbuf->get_height());
290                 gdk_cairo_set_source_pixbuf (cr, _pixbuf->gobj(), x, y);
291                 cairo_fill (cr);
292         }
293
294         /* text, if any */
295
296         int text_margin;
297
298         if (get_width() < 75) {
299                 text_margin = 5;
300         } else {
301                 text_margin = 10;
302         }
303
304         if ( ((_elements & Text)==Text) && !_text.empty()) {
305
306                 cairo_new_path (cr);    
307                 cairo_set_source_rgba (cr, text_r, text_g, text_b, text_a);
308
309                 if (_elements & Indicator) {
310                         if (_led_left) {
311                                 cairo_move_to (cr, text_margin + _diameter + 4, get_height()/2.0 - _text_height/2.0);
312                         } else {
313                                 cairo_move_to (cr, text_margin, get_height()/2.0 - _text_height/2.0);
314                         }
315                         pango_cairo_show_layout (cr, _layout->gobj());
316                 } else {
317                         /* align text */
318
319                         double ww, wh;
320                         double xa, ya;
321                         ww = get_width();
322                         wh = get_height();
323                         cairo_save (cr); // TODO retain rotataion.. adj. LED,...
324                         cairo_rotate(cr, _angle * M_PI / 180.0);
325                         cairo_device_to_user(cr, &ww, &wh);
326                         xa = (ww - _text_width) * _xalign;
327                         ya = (wh - _text_height) * _yalign;
328
329                         /* quick hack for left/bottom alignment at -90deg
330                          * TODO this should be generalized incl rotation.
331                          * currently only 'user' of this API is meter_strip.cc
332                          */
333                         if (_xalign < 0) xa = (ww * fabs(_xalign) + text_margin);
334
335                         // TODO honor left/right text_margin with min/max()
336
337                         cairo_move_to (cr, xa, ya);
338                         pango_cairo_update_layout(cr, _layout->gobj());
339                         pango_cairo_show_layout (cr, _layout->gobj());
340                         cairo_restore (cr);
341
342                         /* use old center'ed layout for follow up items - until rotation/aligment code is completed */
343                         cairo_move_to (cr, (get_width() - _text_width)/2.0, get_height()/2.0 - _text_height/2.0);
344                 }
345
346         } 
347
348         if (((_elements & Indicator)==Indicator)) {
349
350                 /* move to the center of the indicator/led */
351
352                 cairo_save (cr);
353
354                 if (_elements & Text) {
355                         if (_led_left) {
356                                 cairo_translate (cr, text_margin + (_diameter/2.0), get_height()/2.0);
357                         } else {
358                                 cairo_translate (cr, get_width() - ((_diameter/2.0) + 4.0), get_height()/2.0);
359                         }
360                 } else {
361                         cairo_translate (cr, get_width()/2.0, get_height()/2.0);
362                 }
363                 
364                 //inset
365                 cairo_arc (cr, 0, 0, _diameter/2, 0, 2 * M_PI);
366                 cairo_set_source (cr, led_inset_pattern);
367                 cairo_fill (cr);
368                 
369                 //black ring
370                 cairo_set_source_rgb (cr, 0, 0, 0);
371                 cairo_arc (cr, 0, 0, _diameter/2-2, 0, 2 * M_PI);
372                 cairo_fill(cr);
373                 
374                 //led color
375                 cairo_set_source_rgba (cr, led_r, led_g, led_b, led_a);
376                 cairo_arc (cr, 0, 0, _diameter/2-3, 0, 2 * M_PI);
377                 cairo_fill(cr);
378                 
379                 cairo_restore (cr);
380         }
381
382
383         /* a partially transparent gray layer to indicate insensitivity */
384
385         if ((visual_state() & Gtkmm2ext::Insensitive)) {
386                 rounded_function (cr, 0, 0, get_width(), get_height(), _corner_radius);
387                 cairo_set_source_rgba (cr, 0.505, 0.517, 0.525, 0.5);
388                 cairo_fill (cr);
389         }
390
391         //reflection
392         bool show_reflection = (active_state() == Gtkmm2ext::ExplicitActive);
393         show_reflection &= !_flat_buttons;
394         show_reflection &= !((_elements & Indicator)==Indicator);
395         if ( show_reflection ) {
396                 float rheight = get_height()*0.5-REFLECTION_HEIGHT;
397                 Gtkmm2ext::rounded_rectangle (cr, 2, get_height()*0.5-1, get_width()-4, rheight, _corner_radius-1);
398                 cairo_set_source (cr, shine_pattern);
399                 cairo_fill (cr);
400         }
401
402         /* if requested, show hovering */
403         
404         if (ARDOUR::Config->get_widget_prelight()
405                         && !((visual_state() & Gtkmm2ext::Insensitive))) {
406                 if (_hovering) {
407                         rounded_function (cr, 0, 0, get_width(), get_height(), _corner_radius);
408                         cairo_set_source_rgba (cr, 0.905, 0.917, 0.925, 0.2);
409                         cairo_fill (cr);
410                 }
411         }
412 }
413
414 void
415 ArdourButton::set_diameter (float d)
416 {
417         _diameter = (d*2) + 5.0;
418
419         if (_diameter != 0.0) {
420                 _fixed_diameter = true;
421         }
422
423         set_colors ();
424 }
425
426 void
427 ArdourButton::set_corner_radius (float r)
428 {
429         _corner_radius = r;
430         set_dirty ();
431 }
432
433 void
434 ArdourButton::on_size_request (Gtk::Requisition* req)
435 {
436         int xpad = 0;
437         int ypad = 6;
438
439         CairoWidget::on_size_request (req);
440
441         if ((_elements & Text) && !_text.empty()) {
442                 _layout->get_pixel_size (_text_width, _text_height);
443                 if (_text_width + _diameter < 75) {
444                         xpad = 7;
445                 } else {
446                         xpad = 20;
447                 }
448         } else {
449                 _text_width = 0;
450                 _text_height = 0;
451         }
452
453         if (_pixbuf) {
454                 xpad = 6;
455         }
456
457         if ((_elements & Indicator) && _fixed_diameter) {
458                 if (_pixbuf) {
459                         req->width = _pixbuf->get_width() + lrint (_diameter) + xpad;
460                         req->height = max (_pixbuf->get_height(), (int) lrint (_diameter)) + ypad;
461                 } else {
462                         req->width = _text_width + lrint (_diameter) + xpad;
463                         req->height = max (_text_height, (int) lrint (_diameter)) + ypad;
464                 }
465         } else {
466                 if (_pixbuf) {
467                         req->width = _pixbuf->get_width() + xpad;
468                         req->height = _pixbuf->get_height() + ypad;
469                 }  else {
470                         req->width = _text_width + xpad;
471                         req->height = _text_height + ypad;
472                 }
473         }
474 }
475
476 void
477 ArdourButton::set_colors ()
478 {
479         uint32_t start_color;
480         uint32_t end_color;
481         uint32_t r, g, b, a;
482         uint32_t text_color;
483         uint32_t led_color;
484
485         if (active_state() == Gtkmm2ext::ImplicitActive && (_tweaks & ImplicitUsesSolidColor)) {
486                 fill_color_active = ARDOUR_UI::config()->color_by_name (string_compose ("%1: led active", get_name()));
487         } else {
488                 fill_color_active = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill end active", get_name()));
489         }
490         fill_color_inactive = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill end", get_name()));
491         border_color = ARDOUR_UI::config()->color_by_name ( "button border" );
492
493         if (shine_pattern) {
494                 cairo_pattern_destroy (shine_pattern);
495                 shine_pattern = 0;
496         }
497
498         if (fill_pattern) {
499                 cairo_pattern_destroy (fill_pattern);
500                 fill_pattern = 0;
501         }
502
503         if (fill_pattern_active) {
504                 cairo_pattern_destroy (fill_pattern_active);
505                 fill_pattern_active = 0;
506         }
507
508         if (_elements & Body) {
509
510                 start_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill start active", get_name()));
511                 
512                 if (_flat_buttons) {
513                         end_color = start_color;
514                 } else {
515                         end_color = fill_color_active;
516                 }
517                 UINT_TO_RGBA (start_color, &r, &g, &b, &a);
518
519                 active_r = r/255.0;
520                 active_g = g/255.0;
521                 active_b = b/255.0;
522                 active_a = a/255.0;
523
524                 shine_pattern = cairo_pattern_create_linear (0.0, 0.0, 0.0, get_height());
525                 cairo_pattern_add_color_stop_rgba (shine_pattern, 0, 1,1,1,0.0);
526                 cairo_pattern_add_color_stop_rgba (shine_pattern, 0.5, 1,1,1,0.1);
527                 cairo_pattern_add_color_stop_rgba (shine_pattern, 0.7, 1,1,1,0.2);
528                 cairo_pattern_add_color_stop_rgba (shine_pattern, 1, 1,1,1,0.1);
529
530                 fill_pattern = cairo_pattern_create_linear (0.0, 0.0, 0.0, get_height()-3);
531                 if (_flat_buttons) {
532                         end_color = start_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill end", get_name()));
533                 } else {
534                         start_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill start", get_name()));
535                         end_color = fill_color_inactive;
536                 }
537                 UINT_TO_RGBA (start_color, &r, &g, &b, &a);
538                 cairo_pattern_add_color_stop_rgba (fill_pattern, 0, r/255.0,g/255.0,b/255.0, a/255.0);
539                 UINT_TO_RGBA (end_color, &r, &g, &b, &a);
540                 cairo_pattern_add_color_stop_rgba (fill_pattern, 1, r/255.0,g/255.0,b/255.0, a/255.0);
541
542                 fill_pattern_active = cairo_pattern_create_linear (0.0, 0.0, 0.0, get_height()-3);
543                 if (_flat_buttons) {
544                         if (active_state() == Gtkmm2ext::ImplicitActive && (_tweaks & ImplicitUsesSolidColor)) {
545                                 end_color = start_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: led active", get_name()));
546                         } else {
547                                 end_color = start_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill end active", get_name()));
548                         }
549                 } else {
550                         if (active_state() == Gtkmm2ext::ImplicitActive && (_tweaks & ImplicitUsesSolidColor)) {
551                                 start_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: led", get_name()));
552                                 end_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: led active", get_name()));
553                         } else {
554                                 start_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill start active", get_name()));
555                                 end_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill end active", get_name()));
556                         }
557                 }
558                 UINT_TO_RGBA (start_color, &r, &g, &b, &a);
559                 cairo_pattern_add_color_stop_rgba (fill_pattern_active, 0, r/255.0,g/255.0,b/255.0, a/255.0);
560                 UINT_TO_RGBA (end_color, &r, &g, &b, &a);
561                 cairo_pattern_add_color_stop_rgba (fill_pattern_active, 1, r/255.0,g/255.0,b/255.0, a/255.0);
562         }
563
564         if (led_inset_pattern) {
565                 cairo_pattern_destroy (led_inset_pattern);
566         }
567         
568         if (reflection_pattern) {
569                 cairo_pattern_destroy (reflection_pattern);
570         }
571
572         if (_elements & Indicator) {
573                 led_inset_pattern = cairo_pattern_create_linear (0.0, 0.0, 0.0, _diameter);
574                 cairo_pattern_add_color_stop_rgba (led_inset_pattern, 0, 0,0,0, 0.4);
575                 cairo_pattern_add_color_stop_rgba (led_inset_pattern, 1, 1,1,1, 0.7);
576
577                 reflection_pattern = cairo_pattern_create_linear (0.0, 0.0, 0.0, _diameter/2-3);
578                 cairo_pattern_add_color_stop_rgba (reflection_pattern, 0, 1,1,1, active_state() ? 0.4 : 0.2);
579                 cairo_pattern_add_color_stop_rgba (reflection_pattern, 1, 1,1,1, 0.0);
580         }
581         
582         /* text and LED colors */
583
584         if (active_state() == Gtkmm2ext::ExplicitActive || ((_tweaks & ImplicitUsesSolidColor) && active_state() == Gtkmm2ext::ImplicitActive)) {
585                 text_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: text active", get_name()));
586                 led_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: led active", get_name()));
587         } else {
588                 text_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: text", get_name()));
589                 led_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: led", get_name()));
590         }
591
592         UINT_TO_RGBA (text_color, &r, &g, &b, &a);
593         text_r = r/255.0;
594         text_g = g/255.0;
595         text_b = b/255.0;
596         text_a = a/255.0;
597         UINT_TO_RGBA (led_color, &r, &g, &b, &a);
598         led_r = r/255.0;
599         led_g = g/255.0;
600         led_b = b/255.0;
601         led_a = a/255.0;
602
603         set_dirty ();
604 }
605
606 void
607 ArdourButton::set_led_left (bool yn)
608 {
609         _led_left = yn;
610 }
611
612 bool
613 ArdourButton::on_button_press_event (GdkEventButton *ev)
614 {
615         if ((_elements & Indicator) && _led_rect && _distinct_led_click) {
616                 if (ev->x >= _led_rect->x && ev->x < _led_rect->x + _led_rect->width && 
617                     ev->y >= _led_rect->y && ev->y < _led_rect->y + _led_rect->height) {
618                         return true;
619                 }
620         }
621
622         if (_tweaks & ShowClick) {
623                 set_active_state (Gtkmm2ext::ExplicitActive);
624         }
625
626         if (binding_proxy.button_press_handler (ev)) {
627                 return true;
628         }
629
630         if (!_act_on_release) {
631                 if (_action) {
632                         _action->activate ();
633                         return true;
634                 }
635         }
636
637         return false;
638 }
639
640 bool
641 ArdourButton::on_button_release_event (GdkEventButton *ev)
642 {
643         if ((_elements & Indicator) && _led_rect && _distinct_led_click) {
644                 if (ev->x >= _led_rect->x && ev->x < _led_rect->x + _led_rect->width && 
645                     ev->y >= _led_rect->y && ev->y < _led_rect->y + _led_rect->height) {
646                         signal_led_clicked(); /* EMIT SIGNAL */
647                         return true;
648                 }
649         }
650
651         if (_tweaks & ShowClick) {
652                 unset_active_state ();
653         }
654
655         signal_clicked ();
656
657         if (_act_on_release) {
658                 if (_action) {
659                         _action->activate ();
660                         return true;
661                 }
662         }
663
664
665         return false;
666 }
667
668 void
669 ArdourButton::set_distinct_led_click (bool yn)
670 {
671         _distinct_led_click = yn;
672         setup_led_rect ();
673 }
674
675 void
676 ArdourButton::color_handler ()
677 {
678         set_colors ();
679         set_dirty ();
680 }
681
682 void
683 ArdourButton::on_size_allocate (Allocation& alloc)
684 {
685         CairoWidget::on_size_allocate (alloc);
686         setup_led_rect ();
687         set_colors ();
688 }
689
690 void
691 ArdourButton::set_controllable (boost::shared_ptr<Controllable> c)
692 {
693         watch_connection.disconnect ();
694         binding_proxy.set_controllable (c);
695 }
696
697 void
698 ArdourButton::watch ()
699 {
700         boost::shared_ptr<Controllable> c (binding_proxy.get_controllable ());
701
702         if (!c) {
703                 warning << _("button cannot watch state of non-existing Controllable\n") << endmsg;
704                 return;
705         }
706
707         c->Changed.connect (watch_connection, invalidator(*this), boost::bind (&ArdourButton::controllable_changed, this), gui_context());
708 }
709
710 void
711 ArdourButton::controllable_changed ()
712 {
713         float val = binding_proxy.get_controllable()->get_value();
714
715         if (fabs (val) >= 0.5f) {
716                 set_active_state (Gtkmm2ext::ExplicitActive);
717         } else {
718                 unset_active_state ();
719         }
720 }
721
722 void
723 ArdourButton::set_related_action (RefPtr<Action> act)
724 {
725         Gtkmm2ext::Activatable::set_related_action (act);
726
727         if (_action) {
728
729                 action_tooltip_changed ();
730
731                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (_action);
732                 if (tact) {
733                         action_toggled ();
734                         tact->signal_toggled().connect (sigc::mem_fun (*this, &ArdourButton::action_toggled));
735                 } 
736
737                 _action->connect_property_changed ("sensitive", sigc::mem_fun (*this, &ArdourButton::action_sensitivity_changed));
738                 _action->connect_property_changed ("visible", sigc::mem_fun (*this, &ArdourButton::action_visibility_changed));
739                 _action->connect_property_changed ("tooltip", sigc::mem_fun (*this, &ArdourButton::action_tooltip_changed));
740         }
741 }
742
743 void
744 ArdourButton::action_toggled ()
745 {
746         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (_action);
747
748         if (tact) {
749                 if (tact->get_active()) {
750                         set_active_state (Gtkmm2ext::ExplicitActive);
751                 } else {
752                         unset_active_state ();
753                 }
754         }
755 }       
756
757 void
758 ArdourButton::on_style_changed (const RefPtr<Gtk::Style>&)
759 {
760         set_colors ();
761 }
762
763 void
764 ArdourButton::setup_led_rect ()
765 {
766         int text_margin;
767
768         if (get_width() < 75) {
769                 text_margin = 3;
770         } else {
771                 text_margin = 10;
772         }
773
774         if (_elements & Indicator) {
775                 _led_rect = new cairo_rectangle_t;
776                 
777                 if (_elements & Text) {
778                         if (_led_left) {
779                                 _led_rect->x = text_margin;
780                         } else {
781                                 _led_rect->x = get_width() - text_margin - _diameter/2.0;
782                         }
783                 } else {
784                         /* centered */
785                         _led_rect->x = get_width()/2.0 - _diameter/2.0;
786                 }
787
788                 _led_rect->y = get_height()/2.0 - _diameter/2.0;
789                 _led_rect->width = _diameter;
790                 _led_rect->height = _diameter;
791
792         } else {
793                 delete _led_rect;
794                 _led_rect = 0;
795         }
796 }
797
798 void
799 ArdourButton::set_image (const RefPtr<Gdk::Pixbuf>& img)
800 {
801         _pixbuf = img;
802         queue_draw ();
803 }
804
805 void
806 ArdourButton::set_active_state (Gtkmm2ext::ActiveState s)
807 {
808         bool changed = (_active_state != s);
809         CairoWidget::set_active_state (s);
810         if (changed) {
811                 set_colors ();
812         }
813 }
814         
815 void
816 ArdourButton::set_visual_state (Gtkmm2ext::VisualState s)
817 {
818         bool changed = (_visual_state != s);
819         CairoWidget::set_visual_state (s);
820         if (changed) {
821                 set_colors ();
822         }
823 }
824         
825 bool
826 ArdourButton::on_enter_notify_event (GdkEventCrossing* ev)
827 {
828         _hovering = true;
829
830         if (ARDOUR::Config->get_widget_prelight()) {
831                 queue_draw ();
832         }
833
834         return CairoWidget::on_enter_notify_event (ev);
835 }
836
837 bool
838 ArdourButton::on_leave_notify_event (GdkEventCrossing* ev)
839 {
840         _hovering = false;
841
842         if (ARDOUR::Config->get_widget_prelight()) {
843                 queue_draw ();
844         }
845
846         return CairoWidget::on_leave_notify_event (ev);
847 }
848
849 void
850 ArdourButton::set_tweaks (Tweaks t)
851 {
852         if (_tweaks != t) {
853                 _tweaks = t;
854                 queue_draw ();
855         }
856 }
857
858 void
859 ArdourButton::action_sensitivity_changed ()
860 {
861         if (_action->property_sensitive ()) {
862                 set_visual_state (Gtkmm2ext::VisualState (visual_state() & ~Gtkmm2ext::Insensitive));
863         } else {
864                 set_visual_state (Gtkmm2ext::VisualState (visual_state() | Gtkmm2ext::Insensitive));
865         }
866         
867 }
868
869
870 void
871 ArdourButton::action_visibility_changed ()
872 {
873         if (_action->property_visible ()) {
874                 show ();
875         } else {
876                 hide ();
877         }
878 }
879
880 void
881 ArdourButton::action_tooltip_changed ()
882 {
883         string str = _action->property_tooltip().get_value();
884         ARDOUR_UI::instance()->set_tip (*this, str);
885 }
886
887 void
888 ArdourButton::set_rounded_corner_mask (int mask)
889 {
890         _corner_mask = mask;
891         queue_draw ();
892 }
893
894 void
895 ArdourButton::set_elements (Element e)
896 {
897         _elements = e;
898         set_colors ();
899 }
900
901 void
902 ArdourButton::add_elements (Element e)
903 {
904         _elements = (ArdourButton::Element) (_elements | e);
905         set_colors ();
906 }
907
908 void
909 ArdourButton::set_flat_buttons (bool yn)
910 {
911         _flat_buttons = yn;
912 }