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"
26 #include "ardour/utils.h"
28 #include "gtkmm2ext/gui_thread.h"
29 #include "gtkmm2ext/rgb_macros.h"
31 #include "canvas/colors.h"
32 #include "canvas/text.h"
41 using namespace ARDOUR;
42 using namespace ArdourSurface;
43 using namespace ArdourCanvas;
45 Push2Knob::Element Push2Knob::default_elements = Push2Knob::Element (Push2Knob::Arc);
47 Push2Knob::Push2Knob (Push2& p, Item* parent, Element e, Flags flags)
56 Pango::FontDescription fd ("Sans 10");
58 text = new Text (this);
59 text->set_font_description (fd);
60 text->set_position (Duple (0, -20)); /* changed when radius changes */
62 /* typically over-ridden */
64 text_color = p2.get_color (Push2::ParameterName);
65 arc_start_color = p2.get_color (Push2::KnobArcStart);
66 arc_end_color = p2.get_color (Push2::KnobArcEnd);
69 Push2Knob::~Push2Knob ()
74 Push2Knob::set_text_color (Color c)
80 Push2Knob::set_radius (double r)
83 text->set_position (Duple (-_r, -_r - 20));
84 _bounding_box_dirty = true;
89 Push2Knob::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
92 /* no controllable, nothing to draw */
96 const float scale = 2.0 * _r;
97 const float pointer_thickness = 3.0 * (scale/80); //(if the knob is 80 pixels wide, we want a 3-pix line on it)
99 const float start_angle = ((180 - 65) * G_PI) / 180;
100 const float end_angle = ((360 + 65) * G_PI) / 180;
104 if (_flags & ArcToZero) {
108 const float value_angle = start_angle + (_val * (end_angle - start_angle));
109 const float zero_angle = start_angle + (zero * (end_angle - start_angle));
111 float value_x = cos (value_angle);
112 float value_y = sin (value_angle);
114 /* translate so that all coordinates are based on the center of the
115 * knob (which is also its position()
117 Duple origin = item_to_window (Duple (0, 0));
118 context->translate (origin.x, origin.y);
119 context->begin_new_path ();
121 float center_radius = 0.48*scale;
122 float border_width = 0.8;
124 const bool arc = (_elements & Arc)==Arc;
125 const bool flat = false;
128 center_radius = scale*0.33;
130 float inner_progress_radius = scale*0.38;
131 float outer_progress_radius = scale*0.48;
132 float progress_width = (outer_progress_radius-inner_progress_radius);
133 float progress_radius = inner_progress_radius + progress_width/2.0;
135 //dark arc background
136 set_source_rgb (context, p2.get_color (Push2::KnobArcBackground));
137 context->set_line_width (progress_width);
138 context->arc (0, 0, progress_radius, start_angle, end_angle);
141 double red_start, green_start, blue_start, astart;
142 double red_end, green_end, blue_end, aend;
144 ArdourCanvas::color_to_rgba (arc_start_color, red_start, green_start, blue_start, astart);
145 ArdourCanvas::color_to_rgba (arc_end_color, red_end, green_end, blue_end, aend);
147 //vary the arc color over the travel of the knob
148 float intensity = fabsf (_val - zero) / std::max(zero, (1.f - zero));
149 const float intensity_inv = 1.0 - intensity;
150 float r = intensity_inv * red_end + intensity * red_start;
151 float g = intensity_inv * green_end + intensity * green_start;
152 float b = intensity_inv * blue_end + intensity * blue_start;
155 context->set_source_rgb (r,g,b);
156 context->set_line_width (progress_width);
157 if (zero_angle > value_angle) {
158 context->arc (0, 0, progress_radius, value_angle, zero_angle);
160 context->arc (0, 0, progress_radius, zero_angle, value_angle);
166 //note we have to offset the pattern from our centerpoint
167 Cairo::RefPtr<Cairo::LinearGradient> pattern = Cairo::LinearGradient::create (0.0, -_position.y, 0.0, _position.y);
168 pattern->add_color_stop_rgba (0.0, 1,1,1, 0.15);
169 pattern->add_color_stop_rgba (0.5, 1,1,1, 0.0);
170 pattern->add_color_stop_rgba (1.0, 1,1,1, 0.0);
171 context->set_source (pattern);
172 context->arc (0, 0, outer_progress_radius-1, 0, 2.0*G_PI);
180 context->translate(pointer_thickness+1, pointer_thickness+1 );
181 set_source_rgba (context, p2.get_color (Push2::KnobShadow));
182 context->arc (0, 0, center_radius-1, 0, 2.0*G_PI);
187 set_source_rgb (context, p2.get_color (Push2::KnobForeground));
188 context->arc (0, 0, center_radius, 0, 2.0*G_PI);
191 //radial gradient as a lightness shade
192 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
193 pattern->add_color_stop_rgba (0.0, 0, 0, 0, 0.2);
194 pattern->add_color_stop_rgba (1.0, 1, 1, 1, 0.3);
195 context->set_source (pattern);
196 context->arc (0, 0, center_radius, 0, 2.0*G_PI);
202 context->set_line_width (border_width);
203 set_source_rgba (context, p2.get_color (Push2::KnobBorder));
204 context->set_source_rgba (0, 0, 0, 1 );
205 context->arc (0, 0, center_radius, 0, 2.0*G_PI);
211 context->translate(1, 1 );
212 set_source_rgba (context, p2.get_color (Push2::KnobLineShadow));
213 context->set_line_cap (Cairo::LINE_CAP_ROUND);
214 context->set_line_width (pointer_thickness);
215 context->move_to ((center_radius * value_x), (center_radius * value_y));
216 context->line_to (((center_radius*0.4) * value_x), ((center_radius*0.4) * value_y));
222 set_source_rgba (context, p2.get_color (Push2::KnobLine));
223 context->set_line_cap (Cairo::LINE_CAP_ROUND);
224 context->set_line_width (pointer_thickness);
225 context->move_to ((center_radius * value_x), (center_radius * value_y));
226 context->line_to (((center_radius*0.4) * value_x), ((center_radius*0.4) * value_y));
229 /* reset all translations, scaling etc. */
230 context->set_identity_matrix();
232 render_children (area, context);
236 Push2Knob::compute_bounding_box () const
238 if (!_canvas || _r == 0) {
239 _bounding_box = boost::optional<Rect> ();
240 _bounding_box_dirty = false;
244 if (_bounding_box_dirty) {
245 Rect r = Rect (_position.x - _r, _position.y - _r, _position.x + _r, _position.y + _r);
247 _bounding_box_dirty = false;
250 add_child_bounding_boxes ();
254 Push2Knob::set_controllable (boost::shared_ptr<AutomationControl> c)
256 watch_connection.disconnect (); //stop watching the old controllable
259 _controllable.reset ();
264 _controllable->Changed.connect (watch_connection, invalidator(*this), boost::bind (&Push2Knob::controllable_changed, this), &p2);
266 controllable_changed ();
270 Push2Knob::set_pan_azimuth_text (double pos)
272 /* We show the position of the center of the image relative to the left & right.
273 This is expressed as a pair of percentage values that ranges from (100,0)
274 (hard left) through (50,50) (hard center) to (0,100) (hard right).
276 This is pretty wierd, but its the way audio engineers expect it. Just remember that
277 the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
281 snprintf (buf, sizeof (buf), _("L:%3d R:%3d"), (int) rint (100.0 * (1.0 - pos)), (int) rint (100.0 * pos));
286 Push2Knob::set_pan_width_text (double val)
289 snprintf (buf, sizeof (buf), "%d%%", (int) floor (val*100));
294 Push2Knob::set_gain_text (double)
298 /* need to ignore argument, because it has already been converted into
299 the "interface" (0..1) range.
302 snprintf (buf, sizeof (buf), "%.1f dB", accurate_coefficient_to_dB (_controllable->get_value()));
307 Push2Knob::controllable_changed ()
310 _normal = _controllable->internal_to_interface (_controllable->normal());
311 _val = _controllable->internal_to_interface (_controllable->get_value());
313 switch (_controllable->parameter().type()) {
314 case ARDOUR::PanAzimuthAutomation:
315 set_pan_azimuth_text (_val);
318 case ARDOUR::PanWidthAutomation:
319 set_pan_width_text (_val);
322 case ARDOUR::GainAutomation:
323 case ARDOUR::BusSendLevel:
324 case ARDOUR::TrimAutomation:
325 set_gain_text (_val);
329 text->set (std::string());
337 Push2Knob::add_flag (Flags f)
339 _flags = Flags (_flags | f);
344 Push2Knob::remove_flag (Flags f)
346 _flags = Flags (_flags & ~f);
351 Push2Knob::set_arc_start_color (uint32_t c)
358 Push2Knob::set_arc_end_color (uint32_t c)