]> git.mxchange.org Git - simgear.git/blobdiff - simgear/scene/model/SGPickAnimation.cxx
Restructure to avoid ordering issues.
[simgear.git] / simgear / scene / model / SGPickAnimation.cxx
index 0a8a4d883a184396cd085db4cd1d66cf806664d6..ddc41030d55ddaf53d3591e65adeb33b057e4d23 100644 (file)
@@ -22,6 +22,8 @@
      
 #include <simgear/scene/model/SGPickAnimation.hxx>
 
+#include <algorithm>
+
 #include <osg/Geode>
 #include <osg/PolygonOffset>
 #include <osg/PolygonMode>
@@ -85,9 +87,19 @@ osg::Vec2d eventToWindowCoords(const osgGA::GUIEventAdapter* ea)
 
      _bindingsDown = readBindingList(configNode->getChildren("binding"), modelRoot);
      readOptionalBindingList(configNode, modelRoot, "mod-up", _bindingsUp);
-     readOptionalBindingList(configNode, modelRoot, "hovered", _hover);
+     
+     
+     if (configNode->hasChild("cursor")) {
+       _cursorName = configNode->getStringValue("cursor");
+     }
    }
-   
+     
+     void addHoverBindings(const SGPropertyNode* hoverNode,
+                             SGPropertyNode* modelRoot)
+     {
+         _hover = readBindingList(hoverNode->getChildren("binding"), modelRoot);
+     }
+     
    virtual bool buttonPressed(int button, const osgGA::GUIEventAdapter* ea, const Info&)
    {
        if (_buttons.find(button) == _buttons.end()) {
@@ -129,6 +141,9 @@ osg::Vec2d eventToWindowCoords(const osgGA::GUIEventAdapter* ea)
        fireBindingList(_hover, params.ptr());
        return true;
    }
+   
+   std::string getCursor() const
+   { return _cursorName; }
  private:
    SGBindingList _bindingsDown;
    SGBindingList _bindingsUp;
@@ -137,6 +152,7 @@ osg::Vec2d eventToWindowCoords(const osgGA::GUIEventAdapter* ea)
    bool _repeatable;
    double _repeatInterval;
    double _repeatTime;
+   std::string _cursorName;
  };
 
  class VncVisitor : public osg::NodeVisitor {
@@ -217,108 +233,106 @@ osg::Vec2d eventToWindowCoords(const osgGA::GUIEventAdapter* ea)
 
 ///////////////////////////////////////////////////////////////////////////////
 
- class SGPickAnimation::VncCallback : public SGPickCallback {
- public:
-   VncCallback(const SGPropertyNode* configNode,
-                SGPropertyNode* modelRoot,
-                osg::Group *node)
-       : _node(node)
-   {
-     SG_LOG(SG_INPUT, SG_DEBUG, "Configuring VNC callback");
-     const char *cornernames[3] = {"top-left", "top-right", "bottom-left"};
-     SGVec3d *cornercoords[3] = {&_topLeft, &_toRight, &_toDown};
-     for (int c =0; c < 3; c++) {
-       const SGPropertyNode* cornerNode = configNode->getChild(cornernames[c]);
-       *cornercoords[c] = SGVec3d(
-         cornerNode->getDoubleValue("x"),
-         cornerNode->getDoubleValue("y"),
-         cornerNode->getDoubleValue("z"));
-     }
-     _toRight -= _topLeft;
-     _toDown -= _topLeft;
-     _squaredRight = dot(_toRight, _toRight);
-     _squaredDown = dot(_toDown, _toDown);
+class SGPickAnimation::VncCallback : public SGPickCallback {
+public:
+ VncCallback(const SGPropertyNode* configNode,
+              SGPropertyNode* modelRoot,
+              osg::Group *node)
+     : _node(node)
+ {
+   SG_LOG(SG_INPUT, SG_DEBUG, "Configuring VNC callback");
+   const char *cornernames[3] = {"top-left", "top-right", "bottom-left"};
+   SGVec3d *cornercoords[3] = {&_topLeft, &_toRight, &_toDown};
+   for (int c =0; c < 3; c++) {
+     const SGPropertyNode* cornerNode = configNode->getChild(cornernames[c]);
+     *cornercoords[c] = SGVec3d(
+       cornerNode->getDoubleValue("x"),
+       cornerNode->getDoubleValue("y"),
+       cornerNode->getDoubleValue("z"));
    }
+   _toRight -= _topLeft;
+   _toDown -= _topLeft;
+   _squaredRight = dot(_toRight, _toRight);
+   _squaredDown = dot(_toDown, _toDown);
+ }
 
  virtual bool buttonPressed(int button, const osgGA::GUIEventAdapter* ea, const Info& info)
  {
-     SGVec3d loc(info.local);
-     SG_LOG(SG_INPUT, SG_DEBUG, "VNC pressed " << button << ": " << loc);
-     loc -= _topLeft;
-     _x = dot(loc, _toRight) / _squaredRight;
-     _y = dot(loc, _toDown) / _squaredDown;
-     if (_x<0) _x = 0; else if (_x > 1) _x = 1;
-     if (_y<0) _y = 0; else if (_y > 1) _y = 1;
-     VncVisitor vv(_x, _y, 1 << button);
-     _node->accept(vv);
-     return vv.wasSuccessful();
+ virtual bool buttonPressed(int button, const osgGA::GUIEventAdapter* ea, const Info& info)
+ {
+   SGVec3d loc(info.local);
+   SG_LOG(SG_INPUT, SG_DEBUG, "VNC pressed " << button << ": " << loc);
+   loc -= _topLeft;
+   _x = dot(loc, _toRight) / _squaredRight;
+   _y = dot(loc, _toDown) / _squaredDown;
+   if (_x<0) _x = 0; else if (_x > 1) _x = 1;
+   if (_y<0) _y = 0; else if (_y > 1) _y = 1;
+   VncVisitor vv(_x, _y, 1 << button);
+   _node->accept(vv);
+   return vv.wasSuccessful();
 
  }
  virtual void buttonReleased(int keyModState)
  {
-     SG_UNUSED(keyModState);
-     SG_LOG(SG_INPUT, SG_DEBUG, "VNC release");
-     VncVisitor vv(_x, _y, 0);
-     _node->accept(vv);
  }
+ }
+ virtual void buttonReleased(int keyModState)
+ {
+   SG_UNUSED(keyModState);
+   SG_LOG(SG_INPUT, SG_DEBUG, "VNC release");
+   VncVisitor vv(_x, _y, 0);
+   _node->accept(vv);
+ }
 
- private:
  double _x, _y;
  osg::ref_ptr<osg::Group> _node;
  SGVec3d _topLeft, _toRight, _toDown;
  double _squaredRight, _squaredDown;
- };
+private:
+ double _x, _y;
+ osg::ref_ptr<osg::Group> _node;
+ SGVec3d _topLeft, _toRight, _toDown;
+ double _squaredRight, _squaredDown;
+};
 
 ///////////////////////////////////////////////////////////////////////////////
 
- SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
-                                  SGPropertyNode* modelRoot) :
-   SGAnimation(configNode, modelRoot)
- {
- }
+SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
+                                SGPropertyNode* modelRoot) :
+  SGAnimation(configNode, modelRoot)
+{
+  std::vector<SGPropertyNode_ptr> names =
+    configNode->getChildren("proxy-name");
+  for (unsigned i = 0; i < names.size(); ++i) {
+    _proxyNames.push_back(names[i]->getStringValue());
+  }
+}
+
+void SGPickAnimation::apply(osg::Node* node)
+{
+    SGAnimation::apply(node);
+}
+
+namespace
+{
+OpenThreads::Mutex highlightStateSetMutex;
+osg::ref_ptr<osg::StateSet> static_highlightStateSet;
+}
 
- namespace
- {
- OpenThreads::Mutex colorModeUniformMutex;
- osg::ref_ptr<osg::Uniform> colorModeUniform;
- }
 
 
-void 
-SGPickAnimation::innerSetupPickGroup(osg::Group* commonGroup, osg::Group& parent)
+
+osg::StateSet* sharedHighlightStateSet()
 {
-    // Contains the normal geometry that is interactive
-    osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
-    normalGroup->setName("pick normal group");
-    normalGroup->addChild(commonGroup);
-    
-    // Used to render the geometry with just yellow edges
-    osg::Group* highlightGroup = new osg::Group;
-    highlightGroup->setName("pick highlight group");
-    highlightGroup->setNodeMask(simgear::PICK_BIT);
-    highlightGroup->addChild(commonGroup);
+  ScopedLock<Mutex> lock(highlightStateSetMutex);
+  if (!static_highlightStateSet.valid()) {
+    static_highlightStateSet = new osg::StateSet;
     
-    // prepare a state set that paints the edges of this object yellow
-    // The material and texture attributes are set with
-    // OVERRIDE|PROTECTED in case there is a material animation on a
-    // higher node in the scene graph, which would have its material
-    // attribute set with OVERRIDE.
-    osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
     osg::Texture2D* white = StateAttributeFactory::instance()->getWhiteTexture();
-    stateSet->setTextureAttributeAndModes(0, white,
+    static_highlightStateSet->setTextureAttributeAndModes(0, white,
                                           (osg::StateAttribute::ON
                                            | osg::StateAttribute::OVERRIDE
                                            | osg::StateAttribute::PROTECTED));
     osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
     polygonOffset->setFactor(-1);
     polygonOffset->setUnits(-1);
-    stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
-    stateSet->setMode(GL_POLYGON_OFFSET_LINE,
+    static_highlightStateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
+    static_highlightStateSet->setMode(GL_POLYGON_OFFSET_LINE,
                       osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
     osg::PolygonMode* polygonMode = new osg::PolygonMode;
     polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
                          osg::PolygonMode::LINE);
-    stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
+    static_highlightStateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
     osg::Material* material = new osg::Material;
     material->setColorMode(osg::Material::OFF);
     material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 1));
@@ -329,48 +343,122 @@ SGPickAnimation::innerSetupPickGroup(osg::Group* commonGroup, osg::Group& parent
     material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, .95));
     material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
     material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
-    stateSet->setAttribute(
+    static_highlightStateSet->setAttribute(
                            material, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED);
     // The default shader has a colorMode uniform that mimics the
     // behavior of Material color mode.
-    osg::Uniform* cmUniform = 0;
-    {
-        ScopedLock<Mutex> lock(colorModeUniformMutex);
-        if (!colorModeUniform.valid()) {
-            colorModeUniform = new osg::Uniform(osg::Uniform::INT, "colorMode");
-            colorModeUniform->set(0); // MODE_OFF
-            colorModeUniform->setDataVariance(osg::Object::STATIC);
-        }
-        cmUniform = colorModeUniform.get();
-    }
-    stateSet->addUniform(cmUniform,
+    osg::Uniform* colorModeUniform = new osg::Uniform(osg::Uniform::INT, "colorMode");
+    colorModeUniform->set(0); // MODE_OFF
+    colorModeUniform->setDataVariance(osg::Object::STATIC);
+    static_highlightStateSet->addUniform(colorModeUniform,
                          osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
+  }
+  
+  return static_highlightStateSet.get();
+}
+
+void
+SGPickAnimation::apply(osg::Group& group)
+{
+  if (_objectNames.empty() && _proxyNames.empty()) {
+    return;
+  }
+  
+  group.traverse(*this);
+  
+  // iterate over all group children
+  int i = group.getNumChildren() - 1;
+  for (; 0 <= i; --i) {
+    osg::Node* child = group.getChild(i);
+    if (child->getName().empty()) {
+        continue;
+    }
+      
+    std::list<std::string>::iterator it = std::find(_objectNames.begin(), _objectNames.end(), child->getName());
+    if (it != _objectNames.end()) {
+      _objectNames.erase(it);
+      install(*child);
+      
+      osg::ref_ptr<osg::Group> renderGroup, pickGroup;      
+      osg::Group* mainGroup = createMainGroup(&group);
+      mainGroup->setName(child->getName());
+      child->setName(""); // don't apply other animations twice
+      
+      if (getConfig()->getBoolValue("visible", true)) {
+          renderGroup = new osg::Group;
+          renderGroup->setName("pick render group");
+          renderGroup->addChild(child);
+          mainGroup->addChild(renderGroup);
+      }
+      
+      pickGroup = new osg::Group;
+      pickGroup->setName("pick highlight group");
+      pickGroup->setNodeMask(simgear::PICK_BIT);
+      pickGroup->setStateSet(sharedHighlightStateSet());
+      mainGroup->addChild(pickGroup);
+      
+      setupCallbacks(SGSceneUserData::getOrCreateSceneUserData(pickGroup), mainGroup);
+
+      pickGroup->addChild(child);
+      group.removeChild(child);
+      continue;
+    }
+    
+    string_list::iterator j = std::find(_proxyNames.begin(), _proxyNames.end(), child->getName());
+    if (j == _proxyNames.end()) {
+      continue;
+    }
     
-    // Only add normal geometry if configured
-    if (getConfig()->getBoolValue("visible", true))
-        parent.addChild(normalGroup.get());
-    parent.addChild(highlightGroup);
+    _proxyNames.erase(j);
+    osg::ref_ptr<osg::Group> proxyGroup = new osg::Group;
+    group.addChild(proxyGroup);
+    proxyGroup->setStateSet(sharedHighlightStateSet());
+    proxyGroup->setNodeMask(simgear::PICK_BIT);
+      
+    setupCallbacks(SGSceneUserData::getOrCreateSceneUserData(proxyGroup), proxyGroup);
+    proxyGroup->addChild(child);
+    group.removeChild(child);
+  } // of group children iteration
 }
 
- osg::Group*
- SGPickAnimation::createAnimationGroup(osg::Group& parent)
- {
-     osg::Group* commonGroup = new osg::Group;
-     innerSetupPickGroup(commonGroup, parent);
-     SGSceneUserData* ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
-
-   // add actions that become macro and command invocations
-   std::vector<SGPropertyNode_ptr> actions;
-   actions = getConfig()->getChildren("action");
-   for (unsigned int i = 0; i < actions.size(); ++i)
-     ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
-   // Look for the VNC sessions that want raw mouse input
-   actions = getConfig()->getChildren("vncaction");
-   for (unsigned int i = 0; i < actions.size(); ++i)
-     ud->addPickCallback(new VncCallback(actions[i], getModelRoot(),
-       &parent));
-
-   return commonGroup;
+osg::Group*
+SGPickAnimation::createMainGroup(osg::Group* pr)
+{
+  osg::Group* g = new osg::Group;
+  pr->addChild(g);
+  return g;
+}
+
+void
+SGPickAnimation::setupCallbacks(SGSceneUserData* ud, osg::Group* parent)
+{
+  PickCallback* pickCb = NULL;
+  
+  // add actions that become macro and command invocations
+  std::vector<SGPropertyNode_ptr> actions;
+  actions = getConfig()->getChildren("action");
+  for (unsigned int i = 0; i < actions.size(); ++i) {
+    pickCb = new PickCallback(actions[i], getModelRoot());
+    ud->addPickCallback(pickCb);
+  }
+  
+  if (getConfig()->hasChild("hovered")) {
+    if (!pickCb) {
+      // make a trivial PickCallback to hang the hovered off of
+      SGPropertyNode_ptr dummyNode(new SGPropertyNode);
+      pickCb = new PickCallback(dummyNode.ptr(), getModelRoot());
+      ud->addPickCallback(pickCb);
+    }
+    
+    pickCb->addHoverBindings(getConfig()->getNode("hovered"), getModelRoot());
+  }
+  
+  // Look for the VNC sessions that want raw mouse input
+  actions = getConfig()->getChildren("vncaction");
+  for (unsigned int i = 0; i < actions.size(); ++i) {
+    ud->addPickCallback(new VncCallback(actions[i], getModelRoot(), parent));
+  }
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -388,18 +476,35 @@ static void repeatBindings(const SGBindingList& a, SGBindingList& out, int count
 
 static bool static_knobMouseWheelAlternateDirection = false;
 static bool static_knobDragAlternateAxis = false;
+static double static_dragSensitivity = 1.0;
 
 class KnobSliderPickCallback : public SGPickCallback {
 public:
+    enum Direction
+    {
+        DIRECTION_NONE,
+        DIRECTION_INCREASE,
+        DIRECTION_DECREASE
+    };
+    
+    enum DragDirection
+    {
+        DRAG_DEFAULT = 0,
+        DRAG_VERTICAL,
+        DRAG_HORIZONTAL
+    };
+
+    
     KnobSliderPickCallback(const SGPropertyNode* configNode,
                  SGPropertyNode* modelRoot) :
         SGPickCallback(PriorityPanel),
         _direction(DIRECTION_NONE),
-        _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
+        _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1)),
+        _dragDirection(DRAG_DEFAULT)
     {
         readOptionalBindingList(configNode, modelRoot, "action", _action);
-        readOptionalBindingList(configNode, modelRoot, "increase", _bindingsCW);
-        readOptionalBindingList(configNode, modelRoot, "decrease", _bindingsCCW);
+        readOptionalBindingList(configNode, modelRoot, "increase", _bindingsIncrease);
+        readOptionalBindingList(configNode, modelRoot, "decrease", _bindingsDecrease);
         
         readOptionalBindingList(configNode, modelRoot, "release", _releaseAction);
         readOptionalBindingList(configNode, modelRoot, "hovered", _hover);
@@ -409,17 +514,34 @@ public:
         {
         // explicit shifted behaviour - just do exactly what was provided
             readOptionalBindingList(configNode, modelRoot, "shift-action", _shiftedAction);
-            readOptionalBindingList(configNode, modelRoot, "shift-increase", _shiftedCW);
-            readOptionalBindingList(configNode, modelRoot, "shift-decrease", _shiftedCCW);
+            readOptionalBindingList(configNode, modelRoot, "shift-increase", _shiftedIncrease);
+            readOptionalBindingList(configNode, modelRoot, "shift-decrease", _shiftedDecrease);
         } else {
             // default shifted behaviour - repeat normal bindings N times.
             int shiftRepeat = configNode->getIntValue("shift-repeat", 10);
             repeatBindings(_action, _shiftedAction, shiftRepeat);
-            repeatBindings(_bindingsCW, _shiftedCW, shiftRepeat);
-            repeatBindings(_bindingsCCW, _shiftedCCW, shiftRepeat);
+            repeatBindings(_bindingsIncrease, _shiftedIncrease, shiftRepeat);
+            repeatBindings(_bindingsDecrease, _shiftedDecrease, shiftRepeat);
         } // of default shifted behaviour
         
         _dragScale = configNode->getDoubleValue("drag-scale-px", 10.0);
+        std::string dragDir = configNode->getStringValue("drag-direction");
+        if (dragDir == "vertical") {
+            _dragDirection = DRAG_VERTICAL;
+        } else if (dragDir == "horizontal") {
+            _dragDirection = DRAG_HORIZONTAL;
+        }
+      
+        if (configNode->hasChild("cursor")) {
+            _cursorName = configNode->getStringValue("cursor");
+        } else {
+          DragDirection dir = effectiveDragDirection();
+          if (dir == DRAG_VERTICAL) {
+            _cursorName = "drag-vertical";
+          } else if (dir == DRAG_HORIZONTAL) {
+            _cursorName = "drag-horizontal";
+          }
+        }
     }
     
     virtual bool buttonPressed(int button, const osgGA::GUIEventAdapter* ea, const Info&)
@@ -435,9 +557,9 @@ public:
             
         _direction = DIRECTION_NONE;
         if ((button == 0) || (button == increaseMouseWheel)) {
-            _direction = DIRECTION_CLOCKWISE;
+            _direction = DIRECTION_INCREASE;
         } else if ((button == 1) || (button == decreaseMouseWheel)) {
-            _direction = DIRECTION_ANTICLOCKWISE;
+            _direction = DIRECTION_DECREASE;
         } else {
             return false;
         }
@@ -453,16 +575,26 @@ public:
     {
         // for *clicks*, we only fire on button release
         if (!_hasDragged) {
-           // std::cout << "no drag, firing" << std::endl;
-            fire(keyModState & osgGA::GUIEventAdapter::MODKEY_SHIFT);
+            fire(keyModState & osgGA::GUIEventAdapter::MODKEY_SHIFT, _direction);
         }
         
         fireBindingList(_releaseAction);
     }
-    
+  
+    DragDirection effectiveDragDirection() const
+    {
+      if (_dragDirection == DRAG_DEFAULT) {
+        // respect the current default settings - this allows runtime
+        // setting of the default drag direction.
+        return static_knobDragAlternateAxis ? DRAG_VERTICAL : DRAG_HORIZONTAL;
+      }
+      
+      return _dragDirection;
+  }
+  
     virtual void mouseMoved(const osgGA::GUIEventAdapter* ea)
     {
-        _mousePos = eventToWindowCoords(ea);;
+        _mousePos = eventToWindowCoords(ea);
         osg::Vec2d deltaMouse = _mousePos - _lastFirePos;
         
         if (!_hasDragged) {
@@ -476,13 +608,17 @@ public:
         // user is dragging, disable repeat behaviour
             _hasDragged = true;
         }
-        double delta = static_knobDragAlternateAxis ? deltaMouse.y() : deltaMouse.x();
-        delta /= _dragScale;
+      
+        double delta = (effectiveDragDirection() == DRAG_VERTICAL) ? deltaMouse.y() : deltaMouse.x();
+    // per-animation scale factor lets the aircraft author tune for expectations,
+    // eg heading setting vs 5-state switch.
+    // then we scale by a global sensitivity, which the user can set.
+        delta *= static_dragSensitivity / _dragScale;
+        
         if (fabs(delta) >= 1.0) {
             // determine direction from sign of delta
-            _direction = (delta > 0.0) ? DIRECTION_CLOCKWISE : DIRECTION_ANTICLOCKWISE;
-            fire(ea->getModKeyMask());
+            Direction dir = (delta > 0.0) ? DIRECTION_INCREASE : DIRECTION_DECREASE;
+            fire(ea->getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_SHIFT, dir);
             _lastFirePos = _mousePos;
         }
     }
@@ -496,7 +632,7 @@ public:
         _repeatTime += dt;
         while (_repeatInterval < _repeatTime) {
             _repeatTime -= _repeatInterval;
-            fire(keyModState & osgGA::GUIEventAdapter::MODKEY_SHIFT);
+            fire(keyModState & osgGA::GUIEventAdapter::MODKEY_SHIFT, _direction);
         } // of repeat iteration
     }
 
@@ -512,21 +648,30 @@ public:
         fireBindingList(_hover, params.ptr());
         return true;
     }
+  
+    void setCursor(const std::string& aName)
+    {
+      _cursorName = aName;
+    }
+  
+    virtual std::string getCursor() const
+    { return _cursorName; }
+  
 private:
-    void fire(bool isShifted)
+    void fire(bool isShifted, Direction dir)
     {
         const SGBindingList& act(isShifted ? _shiftedAction : _action);
-        const SGBindingList& cw(isShifted ? _shiftedCW : _bindingsCW);
-        const SGBindingList& ccw(isShifted ? _shiftedCCW : _bindingsCCW);
+        const SGBindingList& incr(isShifted ? _shiftedIncrease : _bindingsIncrease);
+        const SGBindingList& decr(isShifted ? _shiftedDecrease : _bindingsDecrease);
         
-        switch (_direction) {
-            case DIRECTION_CLOCKWISE:
+        switch (dir) {
+            case DIRECTION_INCREASE:
                 fireBindingListWithOffset(act,  1, 1);
-                fireBindingList(cw);
+                fireBindingList(incr);
                 break;
-            case DIRECTION_ANTICLOCKWISE:
+            case DIRECTION_DECREASE:
                 fireBindingListWithOffset(act, -1, 1);
-                fireBindingList(ccw);
+                fireBindingList(decr);
                 break;
             default: break;
         }
@@ -534,25 +679,22 @@ private:
     
     SGBindingList _action, _shiftedAction;
     SGBindingList _releaseAction;
-    SGBindingList _bindingsCW, _shiftedCW,
-        _bindingsCCW, _shiftedCCW;
+    SGBindingList _bindingsIncrease, _shiftedIncrease,
+        _bindingsDecrease, _shiftedDecrease;
     SGBindingList _hover;
     
-    enum Direction
-    {
-        DIRECTION_NONE,
-        DIRECTION_CLOCKWISE,
-        DIRECTION_ANTICLOCKWISE
-    };
-    
+        
     Direction _direction;
     double _repeatInterval;
     double _repeatTime;
     
+    DragDirection _dragDirection;
     bool _hasDragged; ///< has the mouse been dragged since the press?
     osg::Vec2d _mousePos, ///< current window coords location of the mouse
         _lastFirePos; ///< mouse location where we last fired the bindings
     double _dragScale;
+  
+    std::string _cursorName;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -588,22 +730,24 @@ SGKnobAnimation::SGKnobAnimation(const SGPropertyNode* configNode,
     readRotationCenterAndAxis(configNode, _center, _axis);
 }
 
-
 osg::Group*
-SGKnobAnimation::createAnimationGroup(osg::Group& parent)
+SGKnobAnimation::createMainGroup(osg::Group* pr)
+{  
+  SGRotateTransform* transform = new SGRotateTransform();
+  
+  UpdateCallback* uc = new UpdateCallback(_animationValue);
+  transform->setUpdateCallback(uc);
+  transform->setCenter(_center);
+  transform->setAxis(_axis);
+  
+  pr->addChild(transform);
+  return transform;
+}
+
+void
+SGKnobAnimation::setupCallbacks(SGSceneUserData* ud, osg::Group*)
 {
-    SGRotateTransform* transform = new SGRotateTransform();
-    innerSetupPickGroup(transform, parent);
-    
-    UpdateCallback* uc = new UpdateCallback(_animationValue);
-    transform->setUpdateCallback(uc);
-    transform->setCenter(_center);
-    transform->setAxis(_axis);
-        
-    SGSceneUserData* ud = SGSceneUserData::getOrCreateSceneUserData(transform);
-    ud->setPickCallback(new KnobSliderPickCallback(getConfig(), getModelRoot()));
-    
-    return transform;
+  ud->setPickCallback(new KnobSliderPickCallback(getConfig(), getModelRoot()));
 }
 
 void SGKnobAnimation::setAlternateMouseWheelDirection(bool aToggle)
@@ -616,6 +760,11 @@ void SGKnobAnimation::setAlternateDragAxis(bool aToggle)
     static_knobDragAlternateAxis = aToggle;
 }
 
+void SGKnobAnimation::setDragSensitivity(double aFactor)
+{
+    static_dragSensitivity = aFactor;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 class SGSliderAnimation::UpdateCallback : public osg::NodeCallback {
@@ -650,17 +799,20 @@ SGSliderAnimation::SGSliderAnimation(const SGPropertyNode* configNode,
 }
 
 osg::Group*
-SGSliderAnimation::createAnimationGroup(osg::Group& parent)
+SGSliderAnimation::createMainGroup(osg::Group* pr)
+{  
+  SGTranslateTransform* transform = new SGTranslateTransform();
+  
+  UpdateCallback* uc = new UpdateCallback(_animationValue);
+  transform->setUpdateCallback(uc);
+  transform->setAxis(_axis);
+  
+  pr->addChild(transform);
+  return transform;
+}
+
+void
+SGSliderAnimation::setupCallbacks(SGSceneUserData* ud, osg::Group*)
 {
-    SGTranslateTransform* transform = new SGTranslateTransform();
-    innerSetupPickGroup(transform, parent);
-    
-    UpdateCallback* uc = new UpdateCallback(_animationValue);
-    transform->setUpdateCallback(uc);
-    transform->setAxis(_axis);
-    
-    SGSceneUserData* ud = SGSceneUserData::getOrCreateSceneUserData(transform);
-    ud->setPickCallback(new KnobSliderPickCallback(getConfig(), getModelRoot()));
-    
-    return transform;
+  ud->setPickCallback(new KnobSliderPickCallback(getConfig(), getModelRoot()));
 }