From 6e6a7c960e533b6f5d49b7183a974ebee2212e9d Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Fri, 3 Jul 2009 19:16:53 +0000 Subject: [PATCH] Added Dragger::s/getActivationModKeyMask(..) and Dragger::s/getActivationKeyEvent(...) methods to make it possible to have draggers that only respond when you press a specified modified key or standard key. Changed the optional dragger in osgvolume to require the shift key to be pressed for the dragger to become active. --- applications/osgviewer/osgviewer.cpp | 251 ++++++++++++--------- examples/osgmanipulator/osgmanipulator.cpp | 12 +- examples/osgvolume/osgvolume.cpp | 1 + include/osgManipulator/Dragger | 16 +- src/osgManipulator/Dragger.cpp | 129 +++++++---- src/osgPlugins/ffmpeg/CMakeLists.txt | 1 - src/osgVolume/RayTracedTechnique.cpp | 2 +- src/osgWrappers/osgManipulator/Dragger.cpp | 39 +++- 8 files changed, 287 insertions(+), 164 deletions(-) diff --git a/applications/osgviewer/osgviewer.cpp b/applications/osgviewer/osgviewer.cpp index e4066787a..7d7060ed6 100644 --- a/applications/osgviewer/osgviewer.cpp +++ b/applications/osgviewer/osgviewer.cpp @@ -1,12 +1,12 @@ /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield - * - * This application is open source and may be redistributed and/or modified - * freely and without restriction, both in commericial and non commericial applications, - * as long as this copyright notice is maintained. - * - * This application is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* +* This application is open source and may be redistributed and/or modified +* freely and without restriction, both in commericial and non commericial applications, +* as long as this copyright notice is maintained. +* +* This application is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -26,135 +27,167 @@ #include #include #include -#include #include +#ifdef _WINDOWS +#include +#include +#include +#include "vsynctoggle_custom.h" +#endif + int main(int argc, char** argv) { - // use an ArgumentParser object to manage the program arguments. - osg::ArgumentParser arguments(&argc,argv); + // use an ArgumentParser object to manage the program arguments. + osg::ArgumentParser arguments(&argc,argv); - arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName()); - arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the standard OpenSceneGraph example which loads and visualises 3d models."); - arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ..."); - arguments.getApplicationUsage()->addCommandLineOption("--image ","Load an image and render it on a quad"); - arguments.getApplicationUsage()->addCommandLineOption("--dem ","Load an image/DEM and render it on a HeightField"); - arguments.getApplicationUsage()->addCommandLineOption("--login ","Provide authentication information for http file access."); + arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName()); + arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the standard OpenSceneGraph example which loads and visualises 3d models."); + arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ..."); + arguments.getApplicationUsage()->addCommandLineOption("--image ","Load an image and render it on a quad"); + arguments.getApplicationUsage()->addCommandLineOption("--dem ","Load an image/DEM and render it on a HeightField"); + arguments.getApplicationUsage()->addCommandLineOption("--login ","Provide authentication information for http file access."); - osgViewer::Viewer viewer(arguments); + osgViewer::Viewer viewer(arguments); - unsigned int helpType = 0; - if ((helpType = arguments.readHelpType())) + unsigned int helpType = 0; + if ((helpType = arguments.readHelpType())) + { + arguments.getApplicationUsage()->write(std::cout, helpType); + return 1; + } + + // report any errors if they have occurred when parsing the program arguments. + if (arguments.errors()) + { + arguments.writeErrorMessages(std::cout); + return 1; + } + + if (arguments.argc()<=1) + { + arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION); + return 1; + } + + std::string url, username, password; + while(arguments.read("--login",url, username, password)) + { + if (!osgDB::Registry::instance()->getAuthenticationMap()) { - arguments.getApplicationUsage()->write(std::cout, helpType); - return 1; + osgDB::Registry::instance()->setAuthenticationMap(new osgDB::AuthenticationMap); + osgDB::Registry::instance()->getAuthenticationMap()->addAuthenticationDetails( + url, + new osgDB::AuthenticationDetails(username, password) + ); } - - // report any errors if they have occurred when parsing the program arguments. - if (arguments.errors()) + } + + // set up the camera manipulators. + { + osg::ref_ptr keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator; + + keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() ); + keyswitchManipulator->addMatrixManipulator( '2', "Flight", new osgGA::FlightManipulator() ); + keyswitchManipulator->addMatrixManipulator( '3', "Drive", new osgGA::DriveManipulator() ); + keyswitchManipulator->addMatrixManipulator( '4', "Terrain", new osgGA::TerrainManipulator() ); + + std::string pathfile; + char keyForAnimationPath = '5'; + while (arguments.read("-p",pathfile)) { - arguments.writeErrorMessages(std::cout); - return 1; - } - - if (arguments.argc()<=1) - { - arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION); - return 1; + osgGA::AnimationPathManipulator* apm = new osgGA::AnimationPathManipulator(pathfile); + if (apm || !apm->valid()) + { + unsigned int num = keyswitchManipulator->getNumMatrixManipulators(); + keyswitchManipulator->addMatrixManipulator( keyForAnimationPath, "Path", apm ); + keyswitchManipulator->selectMatrixManipulator(num); + ++keyForAnimationPath; + } } - std::string url, username, password; - while(arguments.read("--login",url, username, password)) - { - if (!osgDB::Registry::instance()->getAuthenticationMap()) - { - osgDB::Registry::instance()->setAuthenticationMap(new osgDB::AuthenticationMap); - osgDB::Registry::instance()->getAuthenticationMap()->addAuthenticationDetails( - url, - new osgDB::AuthenticationDetails(username, password) - ); - } - } + viewer.setCameraManipulator( keyswitchManipulator.get() ); + } - // set up the camera manipulators. - { - osg::ref_ptr keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator; + // add the state manipulator + viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) ); - keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() ); - keyswitchManipulator->addMatrixManipulator( '2', "Flight", new osgGA::FlightManipulator() ); - keyswitchManipulator->addMatrixManipulator( '3', "Drive", new osgGA::DriveManipulator() ); - keyswitchManipulator->addMatrixManipulator( '4', "Terrain", new osgGA::TerrainManipulator() ); - keyswitchManipulator->addMatrixManipulator( '5', "Spherical", new osgGA::SphericalManipulator() ); + // add the thread model handler + viewer.addEventHandler(new osgViewer::ThreadingHandler); - std::string pathfile; - char keyForAnimationPath = '6'; - while (arguments.read("-p",pathfile)) - { - osgGA::AnimationPathManipulator* apm = new osgGA::AnimationPathManipulator(pathfile); - if (apm || !apm->valid()) - { - unsigned int num = keyswitchManipulator->getNumMatrixManipulators(); - keyswitchManipulator->addMatrixManipulator( keyForAnimationPath, "Path", apm ); - keyswitchManipulator->selectMatrixManipulator(num); - ++keyForAnimationPath; - } - } + // add the window size toggle handler + viewer.addEventHandler(new osgViewer::WindowSizeHandler); - viewer.setCameraManipulator( keyswitchManipulator.get() ); - } + // add the stats handler + viewer.addEventHandler(new osgViewer::StatsHandler); - // add the state manipulator - viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) ); - - // add the thread model handler - viewer.addEventHandler(new osgViewer::ThreadingHandler); + // add the help handler + viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage())); - // add the window size toggle handler - viewer.addEventHandler(new osgViewer::WindowSizeHandler); - - // add the stats handler - viewer.addEventHandler(new osgViewer::StatsHandler); + // add the record camera path handler + viewer.addEventHandler(new osgViewer::RecordCameraPathHandler); - // add the help handler - viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage())); + // add the LOD Scale handler + viewer.addEventHandler(new osgViewer::LODScaleHandler); - // add the record camera path handler - viewer.addEventHandler(new osgViewer::RecordCameraPathHandler); + // add the screen capture handler + viewer.addEventHandler(new osgViewer::ScreenCaptureHandler); - // add the LOD Scale handler - viewer.addEventHandler(new osgViewer::LODScaleHandler); + // load the data + osg::ref_ptr loadedModel = osgDB::readNodeFiles(arguments); + if (!loadedModel) + { + std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl; + return 1; + } - // add the screen capture handler - viewer.addEventHandler(new osgViewer::ScreenCaptureHandler); + // any option left unread are converted into errors to write out later. + arguments.reportRemainingOptionsAsUnrecognized(); - // load the data - osg::ref_ptr loadedModel = osgDB::readNodeFiles(arguments); - if (!loadedModel) - { - std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl; - return 1; - } - - // any option left unread are converted into errors to write out later. - arguments.reportRemainingOptionsAsUnrecognized(); - - // report any errors if they have occurred when parsing the program arguments. - if (arguments.errors()) - { - arguments.writeErrorMessages(std::cout); - return 1; - } + // report any errors if they have occurred when parsing the program arguments. + if (arguments.errors()) + { + arguments.writeErrorMessages(std::cout); + return 1; + } - // optimize the scene graph, remove redundant nodes and state etc. - osgUtil::Optimizer optimizer; - optimizer.optimize(loadedModel.get()); + // optimize the scene graph, remove redundant nodes and state etc. + osgUtil::Optimizer optimizer; + optimizer.optimize(loadedModel.get()); - viewer.setSceneData( loadedModel.get() ); + osg::ref_ptr group = new osg::Group(); + group->addChild(loadedModel.get()); - viewer.realize(); + viewer.setSceneData( group.get() ); - return viewer.run(); + { + char tmp[256]; + tmp[sizeof(tmp) - 1] = 0; + getcwd(tmp, sizeof(tmp) - 1); + + std::string path(tmp); + path += "\\system\\cache\\planet"; + + tmp[sizeof(tmp) - 1] = 0; + snprintf(tmp, sizeof(tmp) - 1, "%s", path.c_str()); + //_putenv(tmp); + // osgDB::Registry::instance()->setFileCache(new osgDB::FileCache( tmp ) ); + viewer.getDatabasePager()->setTargetMaximumNumberOfPageLOD(500); + +#ifdef _WINDOWS + osg::ref_ptr vsync = new baronvsync::CVsyncDrawable(); + osg::ref_ptr vsyncGeode = new osg::Geode(); + vsyncGeode->addDrawable(vsync.get()); + vsync->SetVSync(false); + group->addChild(vsyncGeode.get()); +#endif + + } + + viewer.realize(); + + return viewer.run(); } diff --git a/examples/osgmanipulator/osgmanipulator.cpp b/examples/osgmanipulator/osgmanipulator.cpp index 9b6a4f2c9..fc22b3e97 100644 --- a/examples/osgmanipulator/osgmanipulator.cpp +++ b/examples/osgmanipulator/osgmanipulator.cpp @@ -98,7 +98,6 @@ osg::Node* addDraggerToScene(osg::Node* scene, const std::string& name) osgManipulator::Dragger* dragger = createDragger(name); - dragger->setHandleEvents(true); osg::Group* root = new osg::Group; root->addChild(dragger); @@ -110,6 +109,17 @@ osg::Node* addDraggerToScene(osg::Node* scene, const std::string& name) dragger->addTransformUpdating(selection); + // we want the dragger to handle it's own events automatically + dragger->setHandleEvents(true); + + // if we don't set an activation key or mod mask then any mouse click on + // the dragger will activate it, however if do define either of ActivationModKeyMask or + // and ActivationKeyEvent then you'll have to press either than mod key or the specified key to + // be able to activate the dragger when you mouse click on it. Please note the follow allows + // activation if either the ctrl key or the 'a' key is pressed and held down. + dragger->setActivationModKeyMask(osgGA::GUIEventAdapter::MODKEY_CTRL); + dragger->setActivationKeyEvent('a'); + return root; } diff --git a/examples/osgvolume/osgvolume.cpp b/examples/osgvolume/osgvolume.cpp index 12cbea094..a34fb91ac 100644 --- a/examples/osgvolume/osgvolume.cpp +++ b/examples/osgvolume/osgvolume.cpp @@ -1477,6 +1477,7 @@ int main( int argc, char **argv ) #endif dragger->setupDefaultGeometry(); dragger->setHandleEvents(true); + dragger->setActivationModKeyMask(osgGA::GUIEventAdapter::MODKEY_SHIFT); dragger->addDraggerCallback(new DraggerVolumeTileCallback(tile.get(), tile->getLocator())); dragger->setMatrix(osg::Matrix::translate(0.5,0.5,0.5)*tile->getLocator()->getTransform()); diff --git a/include/osgManipulator/Dragger b/include/osgManipulator/Dragger index f7b734059..2aedbbdf8 100644 --- a/include/osgManipulator/Dragger +++ b/include/osgManipulator/Dragger @@ -217,8 +217,11 @@ class OSGMANIPULATOR_EXPORT Dragger : public osg::MatrixTransform void setHandleEvents(bool flag); bool getHandleEvents() const { return _handleEvents; } - void setDraggerActive(bool active) { _draggerActive = active; } - bool getDraggerActive() const { return _draggerActive; } + void setActivationModKeyMask(unsigned int mask) { _activationModKeyMask = mask; } + unsigned int getActivationModKeyMask() const { return _activationModKeyMask; } + + void setActivationKeyEvent(int key) { _activationKeyEvent = key; } + int getActivationKeyEvent() const { return _activationKeyEvent; } virtual void traverse(osg::NodeVisitor& nv); @@ -260,8 +263,17 @@ class OSGMANIPULATOR_EXPORT Dragger : public osg::MatrixTransform virtual bool receive(const MotionCommand& command); void dispatch(MotionCommand& command); + void setDraggerActive(bool active) { _draggerActive = active; } + bool getDraggerActive() const { return _draggerActive; } + bool _handleEvents; bool _draggerActive; + + unsigned int _activationModKeyMask; + int _activationKeyEvent; + bool _activationPermittedByModKeyMask; + bool _activationPermittedByKeyEvent; + osgManipulator::PointerInfo _pointer; Dragger* _parentDragger; diff --git a/src/osgManipulator/Dragger.cpp b/src/osgManipulator/Dragger.cpp index 69c508e26..c18674361 100644 --- a/src/osgManipulator/Dragger.cpp +++ b/src/osgManipulator/Dragger.cpp @@ -125,7 +125,11 @@ bool PointerInfo::projectWindowXYIntoObject(const osg::Vec2d& windowCoord, osg:: // Dragger::Dragger() : _handleEvents(false), - _draggerActive(false) + _draggerActive(false), + _activationModKeyMask(0), + _activationKeyEvent(0), + _activationPermittedByModKeyMask(false), + _activationPermittedByKeyEvent(false) { _parentDragger = this; getOrCreateStateSet()->setDataVariance(osg::Object::DYNAMIC); @@ -135,7 +139,13 @@ Dragger::Dragger() : } Dragger::Dragger(const Dragger& rhs, const osg::CopyOp& copyop): - osg::MatrixTransform(rhs, copyop) + osg::MatrixTransform(rhs, copyop), + _handleEvents(rhs._handleEvents), + _draggerActive(false), + _activationModKeyMask(rhs._activationModKeyMask), + _activationKeyEvent(rhs._activationKeyEvent), + _activationPermittedByModKeyMask(false), + _activationPermittedByKeyEvent(false) { osg::notify(osg::NOTICE)<<"CompositeDragger::CompositeDragger(const CompositeDragger& rhs, const osg::CopyOp& copyop) not Implemented yet."<computeIntersections(ea.getX(),ea.getY(),intersections)) + switch (ea.getEventType()) { - _pointer.setCamera(view->getCamera()); - _pointer.setMousePosition(ea.getX(), ea.getY()); - - for(osgUtil::LineSegmentIntersector::Intersections::iterator hitr = intersections.begin(); - hitr != intersections.end(); - ++hitr) + case osgGA::GUIEventAdapter::KEYDOWN: { - _pointer.addIntersection(hitr->nodePath, hitr->getLocalIntersectPoint()); + if (ea.getKey()==_activationKeyEvent) _activationPermittedByKeyEvent = true; + break; } - for (osg::NodePath::iterator itr = _pointer._hitList.front().first.begin(); - itr != _pointer._hitList.front().first.end(); - ++itr) + case osgGA::GUIEventAdapter::KEYUP: { - osgManipulator::Dragger* dragger = dynamic_cast(*itr); - if (dragger) + if (ea.getKey()==_activationKeyEvent) _activationPermittedByKeyEvent = false; + break; + } + default: + break; + } + } + + activationPermitted = _activationPermittedByModKeyMask || _activationPermittedByKeyEvent; + + } + + if (activationPermitted || _draggerActive) + { + switch (ea.getEventType()) + { + case osgGA::GUIEventAdapter::PUSH: + { + osgUtil::LineSegmentIntersector::Intersections intersections; + + _pointer.reset(); + + if (view->computeIntersections(ea.getX(),ea.getY(),intersections)) + { + _pointer.setCamera(view->getCamera()); + _pointer.setMousePosition(ea.getX(), ea.getY()); + + for(osgUtil::LineSegmentIntersector::Intersections::iterator hitr = intersections.begin(); + hitr != intersections.end(); + ++hitr) { - if (dragger==this) + _pointer.addIntersection(hitr->nodePath, hitr->getLocalIntersectPoint()); + } + for (osg::NodePath::iterator itr = _pointer._hitList.front().first.begin(); + itr != _pointer._hitList.front().first.end(); + ++itr) + { + osgManipulator::Dragger* dragger = dynamic_cast(*itr); + if (dragger) { - dragger->handle(_pointer, ea, aa); - dragger->setDraggerActive(true); - handled = true; + if (dragger==this) + { + dragger->handle(_pointer, ea, aa); + dragger->setDraggerActive(true); + handled = true; + } } } } } - } - case osgGA::GUIEventAdapter::DRAG: - case osgGA::GUIEventAdapter::RELEASE: - { - if (_draggerActive) + case osgGA::GUIEventAdapter::DRAG: + case osgGA::GUIEventAdapter::RELEASE: { - _pointer._hitIter = _pointer._hitList.begin(); - _pointer.setCamera(view->getCamera()); - _pointer.setMousePosition(ea.getX(), ea.getY()); + if (_draggerActive) + { + _pointer._hitIter = _pointer._hitList.begin(); + _pointer.setCamera(view->getCamera()); + _pointer.setMousePosition(ea.getX(), ea.getY()); - handle(_pointer, ea, aa); + handle(_pointer, ea, aa); - handled = true; + handled = true; + } + break; } - break; + default: + break; } - default: - break; - } - if (_draggerActive && ea.getEventType() == osgGA::GUIEventAdapter::RELEASE) - { - setDraggerActive(false); - _pointer.reset(); + if (_draggerActive && ea.getEventType() == osgGA::GUIEventAdapter::RELEASE) + { + setDraggerActive(false); + _pointer.reset(); + } } return handled; diff --git a/src/osgPlugins/ffmpeg/CMakeLists.txt b/src/osgPlugins/ffmpeg/CMakeLists.txt index 1492bef76..a0dc7d7b1 100644 --- a/src/osgPlugins/ffmpeg/CMakeLists.txt +++ b/src/osgPlugins/ffmpeg/CMakeLists.txt @@ -45,7 +45,6 @@ SET(TARGET_H IF(CMAKE_COMPILER_IS_GNUCXX) # Remove -pedantic flag as it barfs on ffmoeg headers STRING(REGEX REPLACE "-pedantic" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - ENDIF() diff --git a/src/osgVolume/RayTracedTechnique.cpp b/src/osgVolume/RayTracedTechnique.cpp index 5d7446ddb..e3f89fd82 100644 --- a/src/osgVolume/RayTracedTechnique.cpp +++ b/src/osgVolume/RayTracedTechnique.cpp @@ -459,7 +459,7 @@ void RayTracedTechnique::init() osg::ref_ptr locatorCallback = new TexGenLocatorCallback(texgen, masterLocator, layerLocator); masterLocator->addCallback(locatorCallback.get()); - layerLocator->addCallback(locatorCallback.get()); + if (masterLocator != layerLocator) layerLocator->addCallback(locatorCallback.get()); stateset->setTextureAttributeAndModes(0, texgen, osg::StateAttribute::ON); diff --git a/src/osgWrappers/osgManipulator/Dragger.cpp b/src/osgWrappers/osgManipulator/Dragger.cpp index ad668b84b..0a05e06dd 100644 --- a/src/osgWrappers/osgManipulator/Dragger.cpp +++ b/src/osgWrappers/osgManipulator/Dragger.cpp @@ -215,14 +215,24 @@ BEGIN_OBJECT_REFLECTOR(osgManipulator::Dragger) __bool__getHandleEvents, "", ""); - I_Method1(void, setDraggerActive, IN, bool, active, + I_Method1(void, setActivationModKeyMask, IN, unsigned int, mask, Properties::NON_VIRTUAL, - __void__setDraggerActive__bool, + __void__setActivationModKeyMask__unsigned_int, "", ""); - I_Method0(bool, getDraggerActive, + I_Method0(unsigned int, getActivationModKeyMask, Properties::NON_VIRTUAL, - __bool__getDraggerActive, + __unsigned_int__getActivationModKeyMask, + "", + ""); + I_Method1(void, setActivationKeyEvent, IN, int, key, + Properties::NON_VIRTUAL, + __void__setActivationKeyEvent__int, + "", + ""); + I_Method0(int, getActivationKeyEvent, + Properties::NON_VIRTUAL, + __int__getActivationKeyEvent, "", ""); I_Method1(void, traverse, IN, osg::NodeVisitor &, x, @@ -314,15 +324,30 @@ BEGIN_OBJECT_REFLECTOR(osgManipulator::Dragger) __void__dispatch__MotionCommand_R1, "", ""); + I_ProtectedMethod1(void, setDraggerActive, IN, bool, active, + Properties::NON_VIRTUAL, + Properties::NON_CONST, + __void__setDraggerActive__bool, + "", + ""); + I_ProtectedMethod0(bool, getDraggerActive, + Properties::NON_VIRTUAL, + Properties::CONST, + __bool__getDraggerActive, + "", + ""); + I_SimpleProperty(int, ActivationKeyEvent, + __int__getActivationKeyEvent, + __void__setActivationKeyEvent__int); + I_SimpleProperty(unsigned int, ActivationModKeyMask, + __unsigned_int__getActivationModKeyMask, + __void__setActivationModKeyMask__unsigned_int); I_SimpleProperty(osgManipulator::CompositeDragger *, Composite, __CompositeDragger_P1__getComposite, 0); I_SimpleProperty(osgManipulator::Dragger::Constraints &, Constraints, __Constraints_R1__getConstraints, 0); - I_SimpleProperty(bool, DraggerActive, - __bool__getDraggerActive, - __void__setDraggerActive__bool); I_SimpleProperty(osgManipulator::Dragger::DraggerCallbacks &, DraggerCallbacks, __DraggerCallbacks_R1__getDraggerCallbacks, 0);