more pre-commit testing
[ardour.git] / gtk2_ardour / ardour_knob.cc
index 31a81a7af3ae658fef3cb319b7a98c734f1063a2..0397b85c7b0870e0f35e58f553d4bdf4f9f3b412 100644 (file)
@@ -37,6 +37,7 @@
 #include "ardour_knob.h"
 #include "ardour_ui.h"
 #include "global_signals.h"
+#include "timers.h"
 
 #include "canvas/colors.h"
 #include "canvas/utils.h"
@@ -54,17 +55,21 @@ using namespace std;
 
 ArdourKnob::Element ArdourKnob::default_elements = ArdourKnob::Element (ArdourKnob::Arc);
 
-ArdourKnob::ArdourKnob (Element e, bool arc_to_zero)
+ArdourKnob::ArdourKnob (Element e, Flags flags)
        : _elements (e)
        , _hovering (false)
        , _grabbed_x (0)
        , _grabbed_y (0)
        , _val (0)
-       , _zero (0)
-       , _arc_to_zero (arc_to_zero)
+       , _normal (0)
+       , _dead_zone_delta (0)
+       , _flags (flags)
        , _tooltip (this)
 {
        ARDOUR_UI_UTILS::ColorsChanged.connect (sigc::mem_fun (*this, &ArdourKnob::color_handler));
+
+       // watch automation :(
+       Timers::rapid_connect (sigc::mem_fun (*this, &ArdourKnob::controllable_changed));
 }
 
 ArdourKnob::~ArdourKnob()
@@ -85,8 +90,13 @@ ArdourKnob::render (cairo_t* cr, cairo_rectangle_t *)
        const float start_angle = ((180 - 65) * G_PI) / 180;
        const float end_angle = ((360 + 65) * G_PI) / 180;
 
+       float zero = 0;
+       if (_flags & ArcToZero) {
+               zero = _normal;
+       }
+
        const float value_angle = start_angle + (_val * (end_angle - start_angle));
-       const float zero_angle = start_angle + (_zero * (end_angle - start_angle));
+       const float zero_angle = start_angle + (zero * (end_angle - start_angle));
        
        float value_x = cos (value_angle);
        float value_y = sin (value_angle);
@@ -129,7 +139,7 @@ ArdourKnob::render (cairo_t* cr, cairo_rectangle_t *)
                ArdourCanvas::color_to_rgba( arc_end_color, red_end, green_end, blue_end, unused );
 
                //vary the arc color over the travel of the knob
-               float intensity = fabs (_val - _zero) / std::max(_zero, (1.f - _zero));
+               float intensity = fabsf (_val - zero) / std::max(zero, (1.f - zero));
                const float intensity_inv = 1.0 - intensity;
                float r = intensity_inv * red_end   + intensity * red_start;
                float g = intensity_inv * green_end + intensity * green_start;
@@ -297,7 +307,7 @@ ArdourKnob::on_scroll_event (GdkEventScroll* ev)
                float val = c->get_interface();
        
                if ( ev->direction == GDK_SCROLL_UP )
-                       val += scale;  
+                       val += scale;
                else
                        val -= scale;                   
 
@@ -308,10 +318,22 @@ ArdourKnob::on_scroll_event (GdkEventScroll* ev)
 }
 
 bool
-ArdourKnob::on_motion_notify_event (GdkEventMotion *ev) 
+ArdourKnob::on_motion_notify_event (GdkEventMotion *ev)
 {
-       //scale the adjustment based on keyboard modifiers
-       float scale = 0.0025;
+       if (!(ev->state & Gdk::BUTTON1_MASK)) {
+               return true;
+       }
+
+       boost::shared_ptr<PBD::Controllable> c = binding_proxy.get_controllable();
+       if (!c) {
+               return true;
+       }
+
+
+       //scale the adjustment based on keyboard modifiers & GUI size
+       const float ui_scale = max (1.f, ARDOUR_UI::ui_scale);
+       float scale = 0.0025 / ui_scale;
+
        if (ev->state & Keyboard::GainFineScaleModifier) {
                if (ev->state & Keyboard::GainExtraFineScaleModifier) {
                        scale *= 0.01;
@@ -321,22 +343,46 @@ ArdourKnob::on_motion_notify_event (GdkEventMotion *ev)
        }
 
        //calculate the travel of the mouse
-       int delta = 0;
-       if (ev->state & Gdk::BUTTON1_MASK) {
-               delta = (_grabbed_y - ev->y) - (_grabbed_x - ev->x);
-               _grabbed_x = ev->x;
-               _grabbed_y = ev->y;
-               if (delta == 0) return TRUE;
+       int delta = (_grabbed_y - ev->y) - (_grabbed_x - ev->x);
+       if (delta == 0) {
+               return true;
        }
 
-       //step the value of the controllable
-       boost::shared_ptr<PBD::Controllable> c = binding_proxy.get_controllable();
-       if (c) {
-               float val = c->get_interface();
-               val += delta * scale;
-               c->set_interface(val);
+       _grabbed_x = ev->x;
+       _grabbed_y = ev->y;
+       float val = c->get_interface();
+
+       if (_flags & Detent) {
+               const float px_deadzone = 42.f * ui_scale;
+
+               if ((val - _normal) * (val - _normal + delta * scale) < 0) {
+                       /* detent */
+                       const int tozero = (_normal - val) * scale;
+                       int remain = delta - tozero;
+                       if (abs (remain) > px_deadzone) {
+                               /* slow down passing the default value */
+                               remain += (remain > 0) ? px_deadzone * -.5 : px_deadzone * .5;
+                               delta = tozero + remain;
+                               _dead_zone_delta = 0;
+                       } else {
+                               c->set_value (c->normal());
+                               _dead_zone_delta = remain / px_deadzone;
+                               return true;
+                       }
+               }
+
+               if (fabsf (rintf((val - _normal) / scale) + _dead_zone_delta) < 1) {
+                       c->set_value (c->normal());
+                       _dead_zone_delta += delta / px_deadzone;
+                       return true;
+               }
+
+               _dead_zone_delta = 0;
        }
 
+       val += delta * scale;
+       c->set_interface(val);
+
        return true;
 }
 
@@ -345,32 +391,52 @@ ArdourKnob::on_button_press_event (GdkEventButton *ev)
 {
        _grabbed_x = ev->x;
        _grabbed_y = ev->y;
-       _tooltip.start_drag();
-       
-       set_active_state (Gtkmm2ext::ExplicitActive);
+       _dead_zone_delta = 0;
+
+       if (ev->type != GDK_BUTTON_PRESS) {
+               if (_grabbed) {
+                       remove_modal_grab();
+                       gdk_pointer_ungrab (GDK_CURRENT_TIME);
+               }
+               return true;
+       }
 
        if (binding_proxy.button_press_handler (ev)) {
                return true;
        }
 
-       return false;
+       if (ev->button != 1 && ev->button != 2) {
+               return false;
+       }
+       
+       set_active_state (Gtkmm2ext::ExplicitActive);
+       _tooltip.start_drag();
+       add_modal_grab();
+       _grabbed = true;
+       gdk_pointer_grab(ev->window,false,
+                       GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK),
+                       NULL,NULL,ev->time);
+       return true;
 }
 
 bool
 ArdourKnob::on_button_release_event (GdkEventButton *ev)
 {
+       _tooltip.stop_drag();
+       _grabbed = false;
+       remove_modal_grab();
+       gdk_pointer_ungrab (GDK_CURRENT_TIME);
+
        if ( (_grabbed_y == ev->y && _grabbed_x == ev->x) && Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {  //no move, shift-click sets to default
                boost::shared_ptr<PBD::Controllable> c = binding_proxy.get_controllable();
                if (!c) return false;
                c->set_value (c->normal());
-        return true;
+               return true;
        }
 
-       //_tooltip.target_stop_drag ();
-       _tooltip.stop_drag();
        unset_active_state ();
 
-       return false;
+       return true;
 }
 
 void
@@ -388,7 +454,7 @@ ArdourKnob::on_size_allocate (Allocation& alloc)
 void
 ArdourKnob::set_controllable (boost::shared_ptr<Controllable> c)
 {
-    watch_connection.disconnect ();  //stop watching the old controllable
+       watch_connection.disconnect ();  //stop watching the old controllable
 
        if (!c) return;
 
@@ -396,11 +462,7 @@ ArdourKnob::set_controllable (boost::shared_ptr<Controllable> c)
 
        c->Changed.connect (watch_connection, invalidator(*this), boost::bind (&ArdourKnob::controllable_changed, this), gui_context());
 
-       if (_arc_to_zero) {
-               _zero = c->internal_to_interface(c->normal());
-       } else {
-               _zero = 0;
-       }
+       _normal = c->internal_to_interface(c->normal());
 
        controllable_changed();
 }
@@ -409,10 +471,16 @@ void
 ArdourKnob::controllable_changed ()
 {
        boost::shared_ptr<PBD::Controllable> c = binding_proxy.get_controllable();
+       if (!c) return;
 
-       _val = c->get_interface();  //% of knob travel
-       _val = min( max(0.0f, _val), 1.0f);  //range check
+       float val = c->get_interface();
+       val = min( max(0.0f, val), 1.0f); // clamp
+
+       if (val == _val) {
+               return;
+       }
 
+       _val = val;
        if (!_tooltip_prefix.empty()) {
                _tooltip.set_tip (_tooltip_prefix + c->get_user_string());
        }