change the meters into CairoWidget, add expose_area to CairoWidget::render()
[ardour.git] / gtk2_ardour / button_joiner.cc
1 /*
2     Copyright (C) 2012 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 <algorithm>
22
23
24 #include <gtkmm/toggleaction.h>
25
26 #include "pbd/compose.h"
27 #include "gtkmm2ext/utils.h"
28 #include "gtkmm2ext/rgb_macros.h"
29
30 #include "ardour_ui.h"
31 #include "button_joiner.h"
32
33 using namespace Gtk;
34
35 ButtonJoiner::ButtonJoiner (const std::string& str, Gtk::Widget& lw, Gtk::Widget& rw, bool central_joiner)
36         : left (lw)
37         , right (rw)
38         , name (str)
39         , active_fill_pattern (0)
40         , inactive_fill_pattern (0)
41         , central_link (central_joiner)
42 {
43         packer.set_homogeneous (true);
44
45         if (central_link) {
46                 packer.set_spacing (20);
47         }
48
49         packer.pack_start (left);
50         packer.pack_start (right);
51         packer.show ();
52
53         /* this alignment is how we position the box that holds the two widgets
54            within our allocation, and how we request more space around them.
55         */
56
57         align.add (packer);
58
59         if (!central_link) {
60                 align.set (0.5, 1.0);
61                 align.set_padding (9, 0, 9, 9);
62         } else {
63                 align.set (0.5, 0.5);
64                 align.set_padding (1, 1, 1, 1);
65         }
66
67         align.show ();
68
69         add (align);
70
71         add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|
72                     Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
73
74         uint32_t border_color;
75         uint32_t r, g, b, a;
76
77         border_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: border end", name));
78         UINT_TO_RGBA (border_color, &r, &g, &b, &a);
79         
80         border_r = r/255.0;
81         border_g = g/255.0;
82         border_b = b/255.0;
83
84         /* child cairo widgets need the color of the inner edge as their
85          * "background"
86          */
87
88         Gdk::Color col;
89         col.set_rgb_p (border_r, border_g, border_b);
90         provide_background_for_cairo_widget (*this, col);
91 }
92
93 ButtonJoiner::~ButtonJoiner ()
94 {
95         if (active_fill_pattern) {
96                 cairo_pattern_destroy (active_fill_pattern);
97                 cairo_pattern_destroy (inactive_fill_pattern);
98         }
99 }
100
101 void
102 ButtonJoiner::render (cairo_t* cr, cairo_rectangle_t*)
103 {
104         double h = get_height();
105         
106         if (!get_active()) {
107                 cairo_set_source (cr, inactive_fill_pattern);
108         } else {
109                 cairo_set_source (cr, active_fill_pattern);
110         }
111
112         if (!central_link) {
113                 /* outer rect */
114                 
115                 Gtkmm2ext::rounded_top_rectangle (cr, 0, 0, get_width(), h, 8);
116                 cairo_fill_preserve (cr);
117                 
118                 /* outer edge */
119                 
120                 cairo_set_line_width (cr, 1.5);
121                 cairo_set_source_rgb (cr, border_r, border_g, border_b);
122                 cairo_stroke (cr);
123                 
124                 /* inner "edge" */
125                 
126                 Gtkmm2ext::rounded_top_rectangle (cr, 8, 8, get_width() - 16, h - 8, 6);
127                 cairo_stroke (cr);
128         } else {
129                 if (get_active()) {
130                         Gtkmm2ext::rounded_top_rectangle (cr, 0, 0, (get_width() - 20.0)/2.0 , h, 8);
131                         cairo_fill_preserve (cr);
132                         
133                         Gtkmm2ext::rounded_top_rectangle (cr, (get_width() - 20.)/2.0 + 20.0, 0.0, 
134                                                           (get_width() - 20.0)/2.0 , h, 8);
135                         cairo_fill_preserve (cr);
136
137                         cairo_move_to (cr, get_width()/2.0 - 10.0, h/2.0);
138                         cairo_set_line_width (cr, 1.5);
139                         cairo_rel_line_to (cr, 20.0, 0.0);
140                         cairo_set_source (cr, active_fill_pattern);
141                         cairo_stroke (cr);
142                 } else {
143                         cairo_arc (cr, get_width()/2.0, h/2.0, 6.0, 0, M_PI*2.0);
144                         cairo_set_line_width (cr, 1.5);
145                         cairo_fill_preserve (cr);
146                         cairo_set_source_rgb (cr, border_r, border_g, border_b);
147                         cairo_stroke (cr);              
148                 }
149         }
150 }
151
152 void
153 ButtonJoiner::on_size_allocate (Allocation& alloc)
154 {
155         CairoWidget::on_size_allocate (alloc);
156         set_colors ();
157 }
158
159 bool
160 ButtonJoiner::on_button_release_event (GdkEventButton*)
161 {
162         if (_action) {
163                 _action->activate ();
164         }
165
166         return true;
167 }
168
169 void
170 ButtonJoiner::on_size_request (Gtk::Requisition* r)
171 {
172         CairoWidget::on_size_request (r);
173 }
174
175 void
176 ButtonJoiner::set_related_action (Glib::RefPtr<Action> act)
177 {
178         Gtkmm2ext::Activatable::set_related_action (act);
179
180         if (_action) {
181
182                 action_tooltip_changed ();
183
184                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (_action);
185                 if (tact) {
186                         action_toggled ();
187                         tact->signal_toggled().connect (sigc::mem_fun (*this, &ButtonJoiner::action_toggled));
188                 } 
189
190                 _action->connect_property_changed ("sensitive", sigc::mem_fun (*this, &ButtonJoiner::action_sensitivity_changed));
191                 _action->connect_property_changed ("visible", sigc::mem_fun (*this, &ButtonJoiner::action_visibility_changed));
192                 _action->connect_property_changed ("tooltip", sigc::mem_fun (*this, &ButtonJoiner::action_tooltip_changed));
193         }
194 }
195
196 void
197 ButtonJoiner::action_sensitivity_changed ()
198 {
199         if (_action->property_sensitive ()) {
200                 set_visual_state (Gtkmm2ext::VisualState (visual_state() & ~Gtkmm2ext::Insensitive));
201         } else {
202                 set_visual_state (Gtkmm2ext::VisualState (visual_state() | Gtkmm2ext::Insensitive));
203         }
204         
205 }
206
207 void
208 ButtonJoiner::action_visibility_changed ()
209 {
210         if (_action->property_visible ()) {
211                 show ();
212         } else {
213                 hide ();
214         }
215 }
216
217 void
218 ButtonJoiner::action_tooltip_changed ()
219 {
220         std::string str = _action->property_tooltip().get_value();
221         ARDOUR_UI::instance()->set_tip (*this, str);
222 }
223
224 void
225 ButtonJoiner::action_toggled ()
226 {
227         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (_action);
228
229         if (tact) {
230                 set_active (tact->get_active());
231         }
232 }       
233
234 void
235 ButtonJoiner::set_active_state (Gtkmm2ext::ActiveState s)
236 {
237         bool changed = (_active_state != s);
238         CairoWidget::set_active_state (s);
239         if (changed) {
240                 set_colors ();
241         }
242 }
243
244 void
245 ButtonJoiner::set_colors ()
246 {
247         uint32_t start_color;
248         uint32_t end_color;
249         uint32_t r, g, b, a;
250
251         if (active_fill_pattern) {
252                 cairo_pattern_destroy (active_fill_pattern);
253                 cairo_pattern_destroy (inactive_fill_pattern);
254         }
255
256         active_fill_pattern = cairo_pattern_create_linear (0.0, 0.0, 0.0, get_height());
257         inactive_fill_pattern = cairo_pattern_create_linear (0.0, 0.0, 0.0, get_height());
258
259         start_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill start", name));
260         end_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill end", name));
261         UINT_TO_RGBA (start_color, &r, &g, &b, &a);
262         cairo_pattern_add_color_stop_rgba (inactive_fill_pattern, 0, r/255.0,g/255.0,b/255.0, a/255.0);
263         UINT_TO_RGBA (end_color, &r, &g, &b, &a);
264         cairo_pattern_add_color_stop_rgba (inactive_fill_pattern, 1, r/255.0,g/255.0,b/255.0, a/255.0);
265
266         start_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill start active", name));
267         end_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill end active", name));
268         UINT_TO_RGBA (start_color, &r, &g, &b, &a);
269         cairo_pattern_add_color_stop_rgba (active_fill_pattern, 0, r/255.0,g/255.0,b/255.0, a/255.0);
270         UINT_TO_RGBA (end_color, &r, &g, &b, &a);
271         cairo_pattern_add_color_stop_rgba (active_fill_pattern, 1, r/255.0,g/255.0,b/255.0, a/255.0);
272
273         queue_draw ();
274 }
275