2 Copyright (C) 2016 Paul Davis
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.
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.
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.
21 #include <cairomm/context.h>
22 #include <cairomm/pattern.h>
24 #include "ardour/automation_control.h"
25 #include "ardour/dB.h"
27 #include "gtkmm2ext/gui_thread.h"
28 #include "gtkmm2ext/rgb_macros.h"
30 #include "canvas/colors.h"
39 using namespace ARDOUR;
40 using namespace ArdourSurface;
41 using namespace ArdourCanvas;
43 Push2Knob::Element Push2Knob::default_elements = Push2Knob::Element (Push2Knob::Arc);
45 Push2Knob::Push2Knob (Push2& p, Item* parent, Element e, Flags flags)
55 Pango::FontDescription fd ("Sans 10");
56 text.set_font_description (fd);
57 text.set_position (Duple (0, -20)); /* changed when radius changes */
59 /* typically over-ridden */
61 text_color = p2.get_color (Push2::ParameterName);
62 arc_start_color = p2.get_color (Push2::KnobArcStart);
63 arc_end_color = p2.get_color (Push2::KnobArcEnd);
66 Push2Knob::~Push2Knob ()
71 Push2Knob::set_text_color (Color c)
77 Push2Knob::set_radius (double r)
80 text.set_position (Duple (-_r, -_r - 20));
85 Push2Knob::compute_bounding_box () const
87 if (!_canvas || _r == 0) {
88 _bounding_box = boost::optional<Rect> ();
89 _bounding_box_dirty = false;
93 if (_bounding_box_dirty) {
94 Rect r = Rect (0, 0, _r * 2.0, _r * 2.0);
96 _bounding_box_dirty = false;
99 add_child_bounding_boxes ();
103 Push2Knob::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
105 if (!_controllable) {
106 /* no controllable, nothing to draw */
110 const float scale = 2.0 * _r;
111 const float pointer_thickness = 3.0 * (scale/80); //(if the knob is 80 pixels wide, we want a 3-pix line on it)
113 const float start_angle = ((180 - 65) * G_PI) / 180;
114 const float end_angle = ((360 + 65) * G_PI) / 180;
118 if (_flags & ArcToZero) {
122 const float value_angle = start_angle + (_val * (end_angle - start_angle));
123 const float zero_angle = start_angle + (zero * (end_angle - start_angle));
125 float value_x = cos (value_angle);
126 float value_y = sin (value_angle);
128 context->translate (_position.x, _position.y); //after this, everything is based on the center of the knob
129 context->begin_new_path ();
131 float center_radius = 0.48*scale;
132 float border_width = 0.8;
134 const bool arc = (_elements & Arc)==Arc;
135 const bool flat = false;
138 center_radius = scale*0.33;
140 float inner_progress_radius = scale*0.38;
141 float outer_progress_radius = scale*0.48;
142 float progress_width = (outer_progress_radius-inner_progress_radius);
143 float progress_radius = inner_progress_radius + progress_width/2.0;
145 //dark arc background
146 set_source_rgb (context, p2.get_color (Push2::KnobArcBackground));
147 context->set_line_width (progress_width);
148 context->arc (0, 0, progress_radius, start_angle, end_angle);
152 double red_start, green_start, blue_start, astart;
153 double red_end, green_end, blue_end, aend;
155 ArdourCanvas::color_to_rgba (arc_start_color, red_start, green_start, blue_start, astart);
156 ArdourCanvas::color_to_rgba (arc_end_color, red_end, green_end, blue_end, aend);
158 //vary the arc color over the travel of the knob
159 float intensity = fabsf (_val - zero) / std::max(zero, (1.f - zero));
160 const float intensity_inv = 1.0 - intensity;
161 float r = intensity_inv * red_end + intensity * red_start;
162 float g = intensity_inv * green_end + intensity * green_start;
163 float b = intensity_inv * blue_end + intensity * blue_start;
166 context->set_source_rgb (r,g,b);
167 context->set_line_width (progress_width);
168 if (zero_angle > value_angle) {
169 context->arc (0, 0, progress_radius, value_angle, zero_angle);
171 context->arc (0, 0, progress_radius, zero_angle, value_angle);
177 //note we have to offset the pattern from our centerpoint
178 Cairo::RefPtr<Cairo::LinearGradient> pattern = Cairo::LinearGradient::create (0.0, -_position.y, 0.0, _position.y);
179 pattern->add_color_stop_rgba (0.0, 1,1,1, 0.15);
180 pattern->add_color_stop_rgba (0.5, 1,1,1, 0.0);
181 pattern->add_color_stop_rgba (1.0, 1,1,1, 0.0);
182 context->set_source (pattern);
183 context->arc (0, 0, outer_progress_radius-1, 0, 2.0*G_PI);
191 context->translate(pointer_thickness+1, pointer_thickness+1 );
192 set_source_rgba (context, p2.get_color (Push2::KnobShadow));
193 context->arc (0, 0, center_radius-1, 0, 2.0*G_PI);
198 set_source_rgb (context, p2.get_color (Push2::KnobForeground));
199 context->arc (0, 0, center_radius, 0, 2.0*G_PI);
202 //radial gradient as a lightness shade
203 Cairo::RefPtr<Cairo::RadialGradient> pattern = Cairo::RadialGradient::create (-center_radius, -center_radius, 1, -center_radius, -center_radius, center_radius*2.5 ); //note we have to offset the gradient from our centerpoint
204 pattern->add_color_stop_rgba (0.0, 0, 0, 0, 0.2);
205 pattern->add_color_stop_rgba (1.0, 1, 1, 1, 0.3);
206 context->set_source (pattern);
207 context->arc (0, 0, center_radius, 0, 2.0*G_PI);
213 context->set_line_width (border_width);
214 set_source_rgba (context, p2.get_color (Push2::KnobBorder));
215 context->set_source_rgba (0, 0, 0, 1 );
216 context->arc (0, 0, center_radius, 0, 2.0*G_PI);
222 context->translate(1, 1 );
223 set_source_rgba (context, p2.get_color (Push2::KnobLineShadow));
224 context->set_line_cap (Cairo::LINE_CAP_ROUND);
225 context->set_line_width (pointer_thickness);
226 context->move_to ((center_radius * value_x), (center_radius * value_y));
227 context->line_to (((center_radius*0.4) * value_x), ((center_radius*0.4) * value_y));
233 set_source_rgba (context, p2.get_color (Push2::KnobLine));
234 context->set_line_cap (Cairo::LINE_CAP_ROUND);
235 context->set_line_width (pointer_thickness);
236 context->move_to ((center_radius * value_x), (center_radius * value_y));
237 context->line_to (((center_radius*0.4) * value_x), ((center_radius*0.4) * value_y));
240 /* reset all translations, scaling etc. */
241 context->set_identity_matrix();
243 render_children (area, context);
247 Push2Knob::set_controllable (boost::shared_ptr<AutomationControl> c)
249 watch_connection.disconnect (); //stop watching the old controllable
252 _controllable.reset ();
257 _controllable->Changed.connect (watch_connection, invalidator(*this), boost::bind (&Push2Knob::controllable_changed, this), &p2);
259 controllable_changed ();
263 Push2Knob::set_pan_azimuth_text (double pos)
265 /* We show the position of the center of the image relative to the left & right.
266 This is expressed as a pair of percentage values that ranges from (100,0)
267 (hard left) through (50,50) (hard center) to (0,100) (hard right).
269 This is pretty wierd, but its the way audio engineers expect it. Just remember that
270 the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
274 snprintf (buf, sizeof (buf), _("L:%3d R:%3d"), (int) rint (100.0 * (1.0 - pos)), (int) rint (100.0 * pos));
279 Push2Knob::set_pan_width_text (double val)
282 snprintf (buf, sizeof (buf), "%d%%", (int) floor (val*100));
287 Push2Knob::set_gain_text (double)
291 /* need to ignore argument, because it has already been converted into
292 the "interface" (0..1) range.
295 snprintf (buf, sizeof (buf), "%.1f dB", accurate_coefficient_to_dB (_controllable->get_value()));
300 Push2Knob::controllable_changed ()
303 _normal = _controllable->internal_to_interface (_controllable->normal());
304 _val = _controllable->internal_to_interface (_controllable->get_value());
306 switch (_controllable->parameter().type()) {
307 case ARDOUR::PanAzimuthAutomation:
308 set_pan_azimuth_text (_val);
311 case ARDOUR::PanWidthAutomation:
312 set_pan_width_text (_val);
315 case ARDOUR::GainAutomation:
316 case ARDOUR::BusSendLevel:
317 set_gain_text (_val);
321 text.set (std::string());
329 Push2Knob::add_flag (Flags f)
331 _flags = Flags (_flags | f);
336 Push2Knob::remove_flag (Flags f)
338 _flags = Flags (_flags & ~f);
343 Push2Knob::set_arc_start_color (uint32_t c)
350 Push2Knob::set_arc_end_color (uint32_t c)