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 "gtkmm2ext/colors.h"
32 #include "canvas/text.h"
41 #define Rect ArdourCanvas::Rect
45 using namespace ARDOUR;
46 using namespace ArdourSurface;
47 using namespace ArdourCanvas;
49 Push2Knob::Element Push2Knob::default_elements = Push2Knob::Element (Push2Knob::Arc);
51 Push2Knob::Push2Knob (Push2& p, Item* parent, Element e, Flags flags)
60 Pango::FontDescription fd ("Sans 10");
62 text = new Text (this);
63 text->set_font_description (fd);
64 text->set_position (Duple (0, -20)); /* changed when radius changes */
66 /* typically over-ridden */
68 text_color = p2.get_color (Push2::ParameterName);
69 arc_start_color = p2.get_color (Push2::KnobArcStart);
70 arc_end_color = p2.get_color (Push2::KnobArcEnd);
73 Push2Knob::~Push2Knob ()
78 Push2Knob::set_text_color (Gtkmm2ext::Color c)
84 Push2Knob::set_radius (double r)
87 text->set_position (Duple (-_r, -_r - 20));
88 _bounding_box_dirty = true;
93 Push2Knob::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
96 /* no controllable, nothing to draw */
100 const float scale = 2.0 * _r;
101 const float pointer_thickness = 3.0 * (scale/80); //(if the knob is 80 pixels wide, we want a 3-pix line on it)
103 const float start_angle = ((180 - 65) * G_PI) / 180;
104 const float end_angle = ((360 + 65) * G_PI) / 180;
108 if (_flags & ArcToZero) {
112 const float value_angle = start_angle + (_val * (end_angle - start_angle));
113 const float zero_angle = start_angle + (zero * (end_angle - start_angle));
115 float value_x = cos (value_angle);
116 float value_y = sin (value_angle);
118 /* translate so that all coordinates are based on the center of the
119 * knob (which is also its position()
121 Duple origin = item_to_window (Duple (0, 0));
122 context->translate (origin.x, origin.y);
123 context->begin_new_path ();
125 float center_radius = 0.48*scale;
126 float border_width = 0.8;
128 const bool arc = (_elements & Arc)==Arc;
129 const bool flat = false;
132 center_radius = scale*0.33;
134 float inner_progress_radius = scale*0.38;
135 float outer_progress_radius = scale*0.48;
136 float progress_width = (outer_progress_radius-inner_progress_radius);
137 float progress_radius = inner_progress_radius + progress_width/2.0;
139 //dark arc background
140 set_source_rgb (context, p2.get_color (Push2::KnobArcBackground));
141 context->set_line_width (progress_width);
142 context->arc (0, 0, progress_radius, start_angle, end_angle);
145 double red_start, green_start, blue_start, astart;
146 double red_end, green_end, blue_end, aend;
148 Gtkmm2ext::color_to_rgba (arc_start_color, red_start, green_start, blue_start, astart);
149 Gtkmm2ext::color_to_rgba (arc_end_color, red_end, green_end, blue_end, aend);
151 //vary the arc color over the travel of the knob
152 float intensity = fabsf (_val - zero) / std::max(zero, (1.f - zero));
153 const float intensity_inv = 1.0 - intensity;
154 float r = intensity_inv * red_end + intensity * red_start;
155 float g = intensity_inv * green_end + intensity * green_start;
156 float b = intensity_inv * blue_end + intensity * blue_start;
159 context->set_source_rgb (r,g,b);
160 context->set_line_width (progress_width);
161 if (zero_angle > value_angle) {
162 context->arc (0, 0, progress_radius, value_angle, zero_angle);
164 context->arc (0, 0, progress_radius, zero_angle, value_angle);
170 //note we have to offset the pattern from our centerpoint
171 Cairo::RefPtr<Cairo::LinearGradient> pattern = Cairo::LinearGradient::create (0.0, -_position.y, 0.0, _position.y);
172 pattern->add_color_stop_rgba (0.0, 1,1,1, 0.15);
173 pattern->add_color_stop_rgba (0.5, 1,1,1, 0.0);
174 pattern->add_color_stop_rgba (1.0, 1,1,1, 0.0);
175 context->set_source (pattern);
176 context->arc (0, 0, outer_progress_radius-1, 0, 2.0*G_PI);
184 context->translate(pointer_thickness+1, pointer_thickness+1 );
185 set_source_rgba (context, p2.get_color (Push2::KnobShadow));
186 context->arc (0, 0, center_radius-1, 0, 2.0*G_PI);
191 set_source_rgb (context, p2.get_color (Push2::KnobForeground));
192 context->arc (0, 0, center_radius, 0, 2.0*G_PI);
195 //radial gradient as a lightness shade
196 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
197 pattern->add_color_stop_rgba (0.0, 0, 0, 0, 0.2);
198 pattern->add_color_stop_rgba (1.0, 1, 1, 1, 0.3);
199 context->set_source (pattern);
200 context->arc (0, 0, center_radius, 0, 2.0*G_PI);
206 context->set_line_width (border_width);
207 set_source_rgba (context, p2.get_color (Push2::KnobBorder));
208 context->set_source_rgba (0, 0, 0, 1 );
209 context->arc (0, 0, center_radius, 0, 2.0*G_PI);
215 context->translate(1, 1 );
216 set_source_rgba (context, p2.get_color (Push2::KnobLineShadow));
217 context->set_line_cap (Cairo::LINE_CAP_ROUND);
218 context->set_line_width (pointer_thickness);
219 context->move_to ((center_radius * value_x), (center_radius * value_y));
220 context->line_to (((center_radius*0.4) * value_x), ((center_radius*0.4) * value_y));
226 set_source_rgba (context, p2.get_color (Push2::KnobLine));
227 context->set_line_cap (Cairo::LINE_CAP_ROUND);
228 context->set_line_width (pointer_thickness);
229 context->move_to ((center_radius * value_x), (center_radius * value_y));
230 context->line_to (((center_radius*0.4) * value_x), ((center_radius*0.4) * value_y));
233 /* reset all translations, scaling etc. */
234 context->set_identity_matrix();
236 render_children (area, context);
240 Push2Knob::compute_bounding_box () const
242 if (!_canvas || _r == 0) {
243 _bounding_box = Rect ();
244 _bounding_box_dirty = false;
248 if (_bounding_box_dirty) {
249 Rect r = Rect (_position.x - _r, _position.y - _r, _position.x + _r, _position.y + _r);
251 _bounding_box_dirty = false;
254 add_child_bounding_boxes ();
258 Push2Knob::set_controllable (boost::shared_ptr<AutomationControl> c)
260 watch_connection.disconnect (); //stop watching the old controllable
263 _controllable.reset ();
268 _controllable->Changed.connect (watch_connection, invalidator(*this), boost::bind (&Push2Knob::controllable_changed, this), &p2);
270 controllable_changed ();
274 Push2Knob::set_pan_azimuth_text (double pos)
276 /* We show the position of the center of the image relative to the left & right.
277 This is expressed as a pair of percentage values that ranges from (100,0)
278 (hard left) through (50,50) (hard center) to (0,100) (hard right).
280 This is pretty wierd, but its the way audio engineers expect it. Just remember that
281 the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
285 snprintf (buf, sizeof (buf), _("L:%3d R:%3d"), (int) rint (100.0 * (1.0 - pos)), (int) rint (100.0 * pos));
290 Push2Knob::set_pan_width_text (double val)
293 snprintf (buf, sizeof (buf), "%d%%", (int) floor (val*100));
298 Push2Knob::set_gain_text (double)
302 /* need to ignore argument, because it has already been converted into
303 the "interface" (0..1) range.
306 snprintf (buf, sizeof (buf), "%.1f dB", accurate_coefficient_to_dB (_controllable->get_value()));
311 Push2Knob::controllable_changed ()
314 _normal = _controllable->internal_to_interface (_controllable->normal());
315 _val = _controllable->internal_to_interface (_controllable->get_value());
317 switch (_controllable->parameter().type()) {
318 case ARDOUR::PanAzimuthAutomation:
319 set_pan_azimuth_text (_val);
322 case ARDOUR::PanWidthAutomation:
323 set_pan_width_text (_val);
326 case ARDOUR::GainAutomation:
327 case ARDOUR::BusSendLevel:
328 case ARDOUR::TrimAutomation:
329 set_gain_text (_val);
333 text->set (std::string());
341 Push2Knob::add_flag (Flags f)
343 _flags = Flags (_flags | f);
348 Push2Knob::remove_flag (Flags f)
350 _flags = Flags (_flags & ~f);
355 Push2Knob::set_arc_start_color (uint32_t c)
362 Push2Knob::set_arc_end_color (uint32_t c)