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