2 Copyright (C) 2014 Waves Audio Ltd.
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.
18 $Id: fastmeter.h 570 2006-06-07 21:21:21Z sampo $
24 #include "pbd/stacktrace.h"
26 #include "gtkmm2ext/fader.h"
27 #include "gtkmm2ext/keyboard.h"
28 #include "gtkmm2ext/rgb_macros.h"
29 #include "gtkmm2ext/utils.h"
30 #include "pbd/failed_constructor.h"
31 #include "pbd/file_utils.h"
32 #include "ardour/filesystem_paths.h"
34 using namespace Gtkmm2ext;
38 #define CORNER_RADIUS 4
40 #define CORNER_OFFSET 1
41 #define FADER_RESERVE 5
44 static void get_closest_point_on_line(double xa, double ya, double xb, double yb, double xp, double yp, double& xl, double& yl )
46 // Storing vector A->B
47 double a_to_b_x = xb - xa;
48 double a_to_b_y = yb - ya;
50 // Storing vector A->P
51 double a_to_p_x = xp - xa;
52 double a_to_p_y = yp - ya;
55 // Basically finding the squared magnitude
57 double atb2 = a_to_b_x * a_to_b_x + a_to_b_y * a_to_b_y;
59 // The dot product of a_to_p and a_to_b
60 double atp_dot_atb = a_to_p_x * a_to_b_x + a_to_p_y * a_to_b_y;
62 // The normalized "distance" from a to
64 double t = atp_dot_atb / atb2;
66 // The vector perpendicular to a_to_b;
67 // This step can also be combined with the next
68 double perpendicular_x = -a_to_b_y;
69 double perpendicular_y = a_to_b_x;
71 // Finding Q, the point "in the right direction"
72 // If you want a mess, you can also combine this
73 // with the next step.
74 double xq = xp + perpendicular_x;
75 double yq = yp + perpendicular_y;
77 // Add the distance to A, moving
79 double x = xa + a_to_b_x * t;
80 double y = ya + a_to_b_y * t;
83 if ((x < xa) && (x < xb)) {
91 } else if ((x > xa) && (x > xb)) {
101 if ((y < ya) && (y < yb)) {
109 } else if ((y > ya) && (y > yb)) {
124 Fader::Fader (Gtk::Adjustment& adj,
125 const Glib::RefPtr<Gdk::Pixbuf>& face_pixbuf,
126 const Glib::RefPtr<Gdk::Pixbuf>& active_face_pixbuf,
127 const Glib::RefPtr<Gdk::Pixbuf>& underlay_pixbuf,
128 const Glib::RefPtr<Gdk::Pixbuf>& handle_pixbuf,
129 const Glib::RefPtr<Gdk::Pixbuf>& active_handle_pixbuf,
136 , _face_pixbuf (face_pixbuf)
137 , _active_face_pixbuf (active_face_pixbuf)
138 , _underlay_pixbuf (underlay_pixbuf)
139 , _handle_pixbuf (handle_pixbuf)
140 , _active_handle_pixbuf (active_handle_pixbuf)
141 , _min_pos_x (min_pos_x)
142 , _min_pos_y (min_pos_y)
143 , _max_pos_x (max_pos_x)
144 , _max_pos_y (max_pos_y)
145 , _default_value (adjustment.get_value())
147 , _read_only (read_only)
151 update_unity_position ();
154 add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
157 adjustment.signal_value_changed().connect (mem_fun (*this, &Fader::adjustment_changed));
158 adjustment.signal_changed().connect (mem_fun (*this, &Fader::adjustment_changed));
159 CairoWidget::set_size_request(_face_pixbuf->get_width(), _face_pixbuf->get_height());
165 delete _touch_cursor;
170 Fader::set_touch_cursor (const Glib::RefPtr<Gdk::Pixbuf>& touch_cursor)
172 _touch_cursor = new Gdk::Cursor (Gdk::Display::get_default(), touch_cursor, 12, 12);
176 Fader::render (cairo_t* cr, cairo_rectangle_t*)
178 get_handle_position (_last_drawn_x, _last_drawn_y);
180 if (_underlay_pixbuf != 0) {
181 cairo_rectangle (cr, 0, 0, get_width(), get_height());
182 gdk_cairo_set_source_pixbuf (cr,
183 _underlay_pixbuf->gobj(),
184 _last_drawn_x - (int)(_underlay_pixbuf->get_width()/2.0 + 0.5),
185 _last_drawn_y - (int)(_underlay_pixbuf->get_height()/2.0 + 0.5));
189 cairo_rectangle (cr, 0, 0, get_width(), get_height());
190 gdk_cairo_set_source_pixbuf (cr,
191 ((get_state () == Gtk::STATE_ACTIVE) && (_active_face_pixbuf != 0)) ?
192 _active_face_pixbuf->gobj() :
193 _face_pixbuf->gobj(),
198 cairo_rectangle (cr, 0, 0, get_width(), get_height());
200 gdk_cairo_set_source_pixbuf (cr,
201 _active_handle_pixbuf->gobj(),
202 _last_drawn_x - (int)(_active_handle_pixbuf->get_width()/2.0 + 0.5),
203 _last_drawn_y - (int)(_active_handle_pixbuf->get_height()/2.0 + 0.5));
205 gdk_cairo_set_source_pixbuf (cr,
206 _handle_pixbuf->gobj(),
207 _last_drawn_x - (int)(_handle_pixbuf->get_width()/2.0 + 0.5),
208 _last_drawn_y - (int)(_handle_pixbuf->get_height()/2.0 + 0.5));
214 Fader::on_size_request (GtkRequisition* req)
216 req->width = _face_pixbuf->get_width();
217 req->height = _face_pixbuf->get_height();
221 Fader::on_size_allocate (Gtk::Allocation& alloc)
223 CairoWidget::on_size_allocate(alloc);
224 update_unity_position ();
228 Fader::on_button_press_event (GdkEventButton* ev)
236 if (ev->type != GDK_BUTTON_PRESS) {
240 if (ev->button != 1 && ev->button != 2) {
245 get_window()->set_cursor (*_touch_cursor);
248 _grab_start_mouse_x = ev->x;
249 _grab_start_mouse_y = ev->y;
250 get_handle_position (_grab_start_handle_x, _grab_start_handle_y);
252 double hw = _handle_pixbuf->get_width();
253 double hh = _handle_pixbuf->get_height();
255 if ((ev->x < (_grab_start_handle_x - hw/2)) || (ev->x > (_grab_start_handle_x + hw/2)) || (ev->y < (_grab_start_handle_y - hh/2)) || (ev->y > (_grab_start_handle_y + hh/2))) {
262 get_closest_point_on_line(_min_pos_x, _min_pos_y,
263 _max_pos_x, _max_pos_y,
265 ev_pos_x, ev_pos_y );
268 _grab_window = ev->window;
271 gdk_pointer_grab(ev->window,false,
272 GdkEventMask (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK),
283 Fader::on_button_release_event (GdkEventButton* ev)
290 get_window()->set_cursor ();
293 if (_dragging) { //temp
296 gdk_pointer_ungrab (GDK_CURRENT_TIME);
303 Fader::on_scroll_event (GdkEventScroll* ev)
311 switch (ev->direction) {
312 case GDK_SCROLL_RIGHT:
315 if ( ev->state & GDK_SHIFT_MASK ) {
324 case GDK_SCROLL_LEFT:
325 case GDK_SCROLL_DOWN:
327 if ( ev->state & GDK_SHIFT_MASK ) {
339 adjustment.set_value (adjustment.get_value() + step_factor * (adjustment.get_step_increment() ));
344 Fader::on_motion_notify_event (GdkEventMotion* ev)
354 if (ev->window != _grab_window) {
355 _grab_window = ev->window;
359 get_closest_point_on_line(_min_pos_x, _min_pos_y,
360 _max_pos_x, _max_pos_y,
361 _grab_start_handle_x + (ev->x - _grab_start_mouse_x), _grab_start_handle_y + (ev->y - _grab_start_mouse_y),
362 ev_pos_x, ev_pos_y );
364 double const fract = sqrt((ev_pos_x - _min_pos_x) * (ev_pos_x - _min_pos_x) +
365 (ev_pos_y - _min_pos_y) * (ev_pos_y - _min_pos_y)) /
366 sqrt((_max_pos_x - _min_pos_x) * (_max_pos_x - _min_pos_x) +
367 (_max_pos_y - _min_pos_y) * (_max_pos_y - _min_pos_y));
369 adjustment.set_value (adjustment.get_lower() + (adjustment.get_upper() - adjustment.get_lower()) * fract);
375 Fader::adjustment_changed ()
379 get_handle_position (handle_x, handle_y);
381 if ((handle_x != _last_drawn_x) || (handle_y != _last_drawn_y)) {
386 /** @return pixel offset of the current value from the right or bottom of the fader */
388 Fader::get_handle_position (double& x, double& y)
390 double fract = (adjustment.get_value () - adjustment.get_lower()) / ((adjustment.get_upper() - adjustment.get_lower()));
392 x = (int)(_min_pos_x + (_max_pos_x - _min_pos_x) * fract);
393 y = (int)(_min_pos_y + (_max_pos_y - _min_pos_y) * fract);
397 Fader::on_enter_notify_event (GdkEventCrossing*)
400 Keyboard::magic_widget_grab_focus ();
406 Fader::on_leave_notify_event (GdkEventCrossing*)
414 Keyboard::magic_widget_drop_focus();
421 Fader::set_default_value (float d)
424 update_unity_position ();
428 Fader::update_unity_position ()