diff --git a/include/osg/Callback b/include/osg/Callback index 61aad35c8..891fa8aa2 100644 --- a/include/osg/Callback +++ b/include/osg/Callback @@ -123,6 +123,14 @@ inline CallbackObject* getCallbackObject(osg::Object* object, const std::string& return udc ? dynamic_cast(udc->getUserObject(name)) : 0; } + +/** Convinience function for getting the CallbackObject associated with specificed name from an Object's UserDataContainer.*/ +inline const CallbackObject* getCallbackObject(const osg::Object* object, const std::string& name) +{ + const osg::UserDataContainer* udc = object->getUserDataContainer(); + return udc ? dynamic_cast(udc->getUserObject(name)) : 0; +} + /** Call run(..) on named CallbackObjects attached to specified Object. Return true if at least one CallbackObject has been successfully invoked.*/ inline bool runNamedCallbackObjects(osg::Object* object, const std::string& name, osg::Parameters& inputParameters, osg::Parameters& outputParameters) { diff --git a/include/osgUI/LineEdit b/include/osgUI/LineEdit index 3f98f334c..8fc59e810 100644 --- a/include/osgUI/LineEdit +++ b/include/osgUI/LineEdit @@ -15,6 +15,7 @@ #define OSGUI_LINEEDIT #include +#include #include namespace osgUI @@ -35,9 +36,9 @@ public: virtual void enterImplementation(); virtual void leaveImplementation(); - - virtual bool validate(const std::string& text); - virtual bool validateImplementation(const std::string& text); + void setValidator(Validator* validator) { _validator = validator; } + Validator* getValidator() { return _validator.get(); } + const Validator* getValidator() const { return _validator.get(); } virtual void textChanged(const std::string& text); virtual void textChangedImplementation(const std::string& text); @@ -48,6 +49,8 @@ public: protected: virtual ~LineEdit() {} + osg::ref_ptr _validator; + std::string _text; // implementation detail diff --git a/include/osgUI/Validator b/include/osgUI/Validator new file mode 100644 index 000000000..08706ffb4 --- /dev/null +++ b/include/osgUI/Validator @@ -0,0 +1,118 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2014 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library 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. See the + * OpenSceneGraph Public License for more details. +*/ + +#ifndef OSGUI_VALIDATOR +#define OSGUI_VALIDATOR + +#include +#include + +namespace osgUI +{ + +class OSGUI_EXPORT Validator : public osg::Object +{ +public: + Validator(); + Validator(const Validator& validator, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY); + META_Object(osgUI, Validator); + + enum State + { + INVALID, + INTERMEDIATE, + ACCEPTABLE + }; + + /** entry point to validate(..) method, checks for "validate" CallbackObject and calls it if present, otherwise calls validateImplementation(..) + str parameter is the string that needs to be validated + cursorpos is the position of the cursor within the str string. + return validatidy State. */ + virtual State validate(std::string& /*str*/, int& cursorpos) const; + + /// override in subclasses to proviude the validate implementation. + virtual State validateImplementation(std::string& /*str*/, int& /*cursorpos*/) const; + + /** entry point to fixup, checks for "validate" Callbac Object and calls it if present, otherwise calls validateImplementation(..) + fixup(..) is called when user pressers return/enter in a field being edited. + str parameter is string that needs to be corrected.*/ + virtual void fixup(std::string& /*str*/) const; + + /// override in subclass to provide the fixup implementation. + virtual void fixupImplementation(std::string& /*str*/) const; + +protected: + virtual ~Validator() {} +}; + +class OSGUI_EXPORT IntValidator : public Validator +{ +public: + IntValidator(); + IntValidator(const IntValidator& widget, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY); + META_Object(osgUI, IntValidator); + + /// set the bottom value that is accepted as valid, default -INT_MAX + void setBottom(int bottom) { _bottom = bottom; } + int getBottom() const { return _bottom; } + + /// set the top value that is accepted as valid, default INT_MAX + void setTop(int top) { _top = top; } + int getTop() const { return _top; } + + /// override validate implementation. + virtual State validateImplementation(std::string& str, int& cursorpos) const; + /// override validate implementation. + virtual void fixupImplementation(std::string& str) const; + +protected: + virtual ~IntValidator() {} + int _bottom; + int _top; +}; + +class OSGUI_EXPORT DoubleValidator : public Validator +{ +public: + DoubleValidator(); + DoubleValidator(const DoubleValidator& widget, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY); + META_Object(osgUI, DoubleValidator); + + /** set the number of decimal places to accept, default is -1, + all negative values disable validation against maximum number of places thus allows any number of decimals places. */ + void setDecimals(int numDecimals) { _decimals = numDecimals; } + int getDecimals() const { return _decimals; } + + /// set the bottom value that is accepted as valid, default -DBL_MAX + void setBottom(double bottom) { _bottom = bottom; } + double getBottom() const { return _bottom; } + + /// set the top value that is accepted as valid, default DBL_MAX + void setTop(double top) { _top = top; } + double getTop() const { return _top; } + + /// override validate implementation. + virtual State validateImplementation(std::string& str, int& cursorpos) const; + /// override validate implementation. + virtual void fixupImplementation(std::string& str) const; + +protected: + virtual ~DoubleValidator() {} + int _decimals; + double _bottom; + double _top; +}; + +} + +#endif diff --git a/src/osgUI/CMakeLists.txt b/src/osgUI/CMakeLists.txt index 648788f83..06b7313ea 100644 --- a/src/osgUI/CMakeLists.txt +++ b/src/osgUI/CMakeLists.txt @@ -21,6 +21,7 @@ SET(TARGET_H ${HEADER_PATH}/AlignmentSettings ${HEADER_PATH}/FrameSettings ${HEADER_PATH}/TextSettings + ${HEADER_PATH}/Validator ) SET(TARGET_SRC @@ -36,6 +37,7 @@ SET(TARGET_SRC AlignmentSettings.cpp FrameSettings.cpp TextSettings.cpp + Validator.cpp ${OPENSCENEGRAPH_VERSIONINFO_RC} ) diff --git a/src/osgUI/LineEdit.cpp b/src/osgUI/LineEdit.cpp index 5b857451b..e2f824f02 100644 --- a/src/osgUI/LineEdit.cpp +++ b/src/osgUI/LineEdit.cpp @@ -58,6 +58,17 @@ bool LineEdit::handleImplementation(osgGA::EventVisitor* ev, osgGA::Event* event } else if (ea->getKey()==osgGA::GUIEventAdapter::KEY_Return ) { + if (_validator.valid()) + { + std::string text_copy(_text); + int cursorpos; + if (_validator->validate(text_copy, cursorpos)==Validator::INTERMEDIATE) + { + _validator->fixup(text_copy); + } + if (text_copy!=_text) setText(text_copy); + } + returnPressed(); return true; } @@ -74,10 +85,17 @@ bool LineEdit::handleImplementation(osgGA::EventVisitor* ev, osgGA::Event* event void LineEdit::setText(const std::string& text) { - if (!validate(text)) return; if (_text==text) return; - _text = text; + std::string text_copy(text); + if (_validator.valid()) + { + int cursorpos = 0; + Validator::State state = _validator->validate(text_copy, cursorpos); + if (state==Validator::INVALID) return; + } + + _text = text_copy; textChanged(_text); @@ -97,36 +115,6 @@ void LineEdit::leaveImplementation() if (_backgroundSwitch.valid()) _backgroundSwitch->setSingleChildOn(0); } -bool LineEdit::validate(const std::string& text) -{ - osg::CallbackObject* co = getCallbackObject(this, "validate"); - if (co) - { - osg::Parameters inputParameters, outputParameters; - inputParameters.push_back(new osg::StringValueObject("text",text)); - if (co->run(this, inputParameters, outputParameters)) - { - if (outputParameters.size()>=1) - { - osg::Object* object = outputParameters[0].get(); - osg::BoolValueObject* bvo = dynamic_cast(object); - if (bvo) - { - OSG_NOTICE<<"Have bool return value from validate "<getValue()<getValue(); - } - OSG_NOTICE<<"Called validate CallbackObject but didn't get bool return value."<className()< +#include +#include +#include +#include + +using namespace osgUI; + +Validator::Validator() +{ +} + +Validator::Validator(const osgUI::Validator& validator, const osg::CopyOp& copyop): + osg::Object(validator, copyop) +{ +} + +Validator::State Validator::validate(std::string& text, int& cursorpos) const +{ + const osg::CallbackObject* co = getCallbackObject(this, "validate"); + if (co) + { + osg::ref_ptr textInput = new osg::StringValueObject("text",text); + osg::ref_ptr cursorposInput = new osg::IntValueObject("cursorpos",cursorpos); + + osg::Parameters inputParameters, outputParameters; + inputParameters.push_back(textInput.get()); + inputParameters.push_back(cursorposInput.get()); + if (co->run(const_cast(this), inputParameters, outputParameters)) + { + if (textInput->getValue()!=text) + { + OSG_NOTICE<<"Updating text in CallbackObject "<getValue()<getValue(); + } + if (cursorposInput->getValue()!=cursorpos) + { + OSG_NOTICE<<"Updating cursor pos in CallbackObject "<getValue()<getValue(); + } + + if (outputParameters.size()>=1) + { + osg::Object* object = outputParameters[0].get(); + osg::StringValueObject* svo = dynamic_cast(object); + if (svo) + { + OSG_NOTICE<<"Have string return value from validate "<getValue()<getValue(); + if (returnString=="INVALID") return INVALID; + else if (returnString=="INTERMEDITATE") return INTERMEDIATE; + else if (returnString=="ACCEPTABLE") return ACCEPTABLE; + } + OSG_NOTICE<<"Called validate CallbackObject but didn't get string return value."<className()< textInput = new osg::StringValueObject("text",text); + + osg::Parameters inputParameters, outputParameters; + inputParameters.push_back(textInput.get()); + if (co->run(const_cast(this), inputParameters, outputParameters)) + { + if (textInput->getValue()!=text) + { + OSG_NOTICE<<"Updating text in CallbackObject "<getValue()<getValue(); + } + } + } + return fixupImplementation(text); +} + +void Validator::fixupImplementation(std::string& text) const +{ + OSG_NOTICE<<"Validator::fixupImplemetation("<='0' && c<='9') + { + validChar = true; + } + else if (c=='-') + { + if (canBeNegative) + { + if (numNegative==0) validChar = true; + ++numNegative; + } + } + + if (validChar) newstring.push_back(c); + } + + str = newstring; + + if (str.empty()) return INTERMEDIATE; + + + int v = static_cast(osg::asciiToDouble(str.c_str())); + if (v<_bottom) + { + return INTERMEDIATE; + } + if (v>_top) + { + return INTERMEDIATE; + } + + return ACCEPTABLE; +} + +void IntValidator::fixupImplementation(std::string& str) const +{ + if (str.empty()) return; + + int v = static_cast(osg::asciiToDouble(str.c_str())); + if (v<_bottom) + { + v = _bottom; + } + if (v>_top) + { + v = _top; + } + + std::stringstream buffer; + buffer<=0 ? _decimals : str.size(); + + int numPlacesAfterDecimal = 0; + int numNegative = 0; + bool hasDecimal = false; + for(std::size_t pos = 0; pos='0' && c<='9') + { + if (hasDecimal) + { + ++numPlacesAfterDecimal; + if (numPlacesAfterDecimal<=maxNumDecimalPlaces) validChar = true; + } + else + { + validChar = true; + } + } + else if (c=='-') + { + if (canBeNegative) + { + if (numNegative==0) validChar = true; + ++numNegative; + } + } + else if (c=='.') + { + if (!hasDecimal) + { + validChar = true; + hasDecimal = true; + } + } + + if (validChar) newstring.push_back(c); + } + + str = newstring; + + if (str.empty()) return INTERMEDIATE; + + + double v = osg::asciiToDouble(str.c_str()); + if (v<_bottom) + { + return INTERMEDIATE; + } + if (v>_top) + { + return INTERMEDIATE; + } + + return ACCEPTABLE; +} + +void DoubleValidator::fixupImplementation(std::string& str) const +{ + if (str.empty()) return; + + double v = osg::asciiToDouble(str.c_str()); + if (v<_bottom) + { + v = _bottom; + } + if (v>_top) + { + v = _top; + } + + std::stringstream buffer; + buffer< +#include +#include +#include +#include + +#include + +REGISTER_OBJECT_WRAPPER( DoubleValidator, + new osgUI::DoubleValidator, + osgUI::DoubleValidator, + "osg::Object osgUI::Validator osgUI::DoubleValidator" ) +{ + ADD_INT_SERIALIZER(Decimals, -1); + ADD_DOUBLE_SERIALIZER(Bottom, -DBL_MAX); + ADD_DOUBLE_SERIALIZER(Top, DBL_MAX); +} diff --git a/src/osgWrappers/serializers/osgUI/IntValidator.cpp b/src/osgWrappers/serializers/osgUI/IntValidator.cpp new file mode 100644 index 000000000..6c55526f2 --- /dev/null +++ b/src/osgWrappers/serializers/osgUI/IntValidator.cpp @@ -0,0 +1,16 @@ +#include +#include +#include +#include +#include + +#include + +REGISTER_OBJECT_WRAPPER( IntValidator, + new osgUI::IntValidator, + osgUI::IntValidator, + "osg::Object osgUI::Validator osgUI::IntValidator" ) +{ + ADD_INT_SERIALIZER(Bottom, -INT_MAX); + ADD_INT_SERIALIZER(Top, INT_MAX); +} diff --git a/src/osgWrappers/serializers/osgUI/LineEdit.cpp b/src/osgWrappers/serializers/osgUI/LineEdit.cpp index 538720a89..e840b49ff 100644 --- a/src/osgWrappers/serializers/osgUI/LineEdit.cpp +++ b/src/osgWrappers/serializers/osgUI/LineEdit.cpp @@ -10,5 +10,6 @@ REGISTER_OBJECT_WRAPPER( LineEdit, osgUI::LineEdit, "osg::Object osg::Node osg::Group osgUI::Widget osgUI::LineEdit" ) { + ADD_OBJECT_SERIALIZER( Validator, osgUI::Validator, NULL); ADD_STRING_SERIALIZER( Text, std::string()); } diff --git a/src/osgWrappers/serializers/osgUI/Validator.cpp b/src/osgWrappers/serializers/osgUI/Validator.cpp new file mode 100644 index 000000000..b0759bf71 --- /dev/null +++ b/src/osgWrappers/serializers/osgUI/Validator.cpp @@ -0,0 +1,13 @@ +#include +#include +#include +#include +#include + + +REGISTER_OBJECT_WRAPPER( Validator, + new osgUI::Validator, + osgUI::Validator, + "osg::Object osgUI::Validator" ) +{ +}