| /************************************************************** | |
| * | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| * | |
| *************************************************************/ | |
| // MARKER(update_precomp.py): autogen include statement, do not remove | |
| #include "precompiled_forms.hxx" | |
| #include "binding.hxx" | |
| #include "model.hxx" | |
| #include "unohelper.hxx" | |
| #include "NameContainer.hxx" | |
| #include "evaluationcontext.hxx" | |
| #include "convert.hxx" | |
| #include "resourcehelper.hxx" | |
| #include "xmlhelper.hxx" | |
| #include "xformsevent.hxx" | |
| #include <rtl/ustrbuf.hxx> | |
| #include <osl/diagnose.h> | |
| #include <tools/diagnose_ex.h> | |
| #include <algorithm> | |
| #include <functional> | |
| #include <com/sun/star/uno/Any.hxx> | |
| #include <com/sun/star/xml/dom/XNodeList.hpp> | |
| #include <com/sun/star/xml/dom/XNode.hpp> | |
| #include <com/sun/star/xml/dom/XDocument.hpp> | |
| #include <com/sun/star/xml/dom/XElement.hpp> | |
| #include <com/sun/star/xml/dom/NodeType.hpp> | |
| #include <com/sun/star/xml/dom/events/XEventTarget.hpp> | |
| #include <com/sun/star/xml/dom/events/XEventListener.hpp> | |
| #include <com/sun/star/xml/dom/events/XDocumentEvent.hpp> | |
| #include <com/sun/star/lang/XUnoTunnel.hpp> | |
| #include <com/sun/star/lang/IndexOutOfBoundsException.hpp> | |
| #include <com/sun/star/container/XSet.hpp> | |
| #include <com/sun/star/container/XNameContainer.hpp> | |
| #include <comphelper/propertysetinfo.hxx> | |
| #include <unotools/textsearch.hxx> | |
| #include <cppuhelper/typeprovider.hxx> | |
| using namespace com::sun::star::xml::xpath; | |
| using namespace com::sun::star::xml::dom::events; | |
| using rtl::OUString; | |
| using rtl::OUStringBuffer; | |
| using std::vector; | |
| using xforms::Binding; | |
| using xforms::MIP; | |
| using xforms::Model; | |
| using xforms::getResource; | |
| using xforms::EvaluationContext; | |
| using com::sun::star::beans::PropertyVetoException; | |
| using com::sun::star::beans::UnknownPropertyException; | |
| using com::sun::star::beans::XPropertySet; | |
| using com::sun::star::container::XSet; | |
| using com::sun::star::container::XNameAccess; | |
| using com::sun::star::form::binding::IncompatibleTypesException; | |
| using com::sun::star::form::binding::InvalidBindingStateException; | |
| using com::sun::star::form::binding::XValueBinding; | |
| using com::sun::star::lang::EventObject; | |
| using com::sun::star::lang::IllegalArgumentException; | |
| using com::sun::star::lang::IndexOutOfBoundsException; | |
| using com::sun::star::lang::NoSupportException; | |
| using com::sun::star::lang::NullPointerException; | |
| using com::sun::star::lang::WrappedTargetException; | |
| using com::sun::star::lang::XUnoTunnel; | |
| using com::sun::star::uno::Any; | |
| using com::sun::star::uno::Reference; | |
| using com::sun::star::uno::RuntimeException; | |
| using com::sun::star::uno::Sequence; | |
| using com::sun::star::uno::UNO_QUERY; | |
| using com::sun::star::uno::UNO_QUERY_THROW; | |
| using com::sun::star::uno::XInterface; | |
| using com::sun::star::uno::Exception; | |
| using com::sun::star::uno::makeAny; | |
| using com::sun::star::util::XModifyListener; | |
| using com::sun::star::xforms::XDataTypeRepository; | |
| using com::sun::star::xml::dom::NodeType_ATTRIBUTE_NODE; | |
| using com::sun::star::xml::dom::NodeType_TEXT_NODE; | |
| using com::sun::star::xml::dom::XNode; | |
| using com::sun::star::xml::dom::XNodeList; | |
| using com::sun::star::xml::dom::events::XEventListener; | |
| using com::sun::star::xml::dom::events::XEventTarget; | |
| using com::sun::star::xsd::XDataType; | |
| #define EXCEPT(msg) OUSTRING(msg),static_cast<XValueBinding*>(this) | |
| #define HANDLE_BindingID 0 | |
| #define HANDLE_BindingExpression 1 | |
| #define HANDLE_Model 2 | |
| #define HANDLE_ModelID 3 | |
| #define HANDLE_BindingNamespaces 4 | |
| #define HANDLE_ReadonlyExpression 5 | |
| #define HANDLE_RelevantExpression 6 | |
| #define HANDLE_RequiredExpression 7 | |
| #define HANDLE_ConstraintExpression 8 | |
| #define HANDLE_CalculateExpression 9 | |
| #define HANDLE_Type 10 | |
| #define HANDLE_ReadOnly 11 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control | |
| #define HANDLE_Relevant 12 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control | |
| #define HANDLE_ModelNamespaces 13 | |
| #define HANDLE_ExternalData 14 | |
| Binding::Binding() : | |
| mxModel(), | |
| msBindingID(), | |
| maBindingExpression(), | |
| maReadonly(), | |
| mxNamespaces( new NameContainer<OUString>() ), | |
| mbInCalculate( false ), | |
| mnDeferModifyNotifications( 0 ), | |
| mbValueModified( false ), | |
| mbBindingModified( false ) | |
| { | |
| initializePropertySet(); | |
| } | |
| Binding::~Binding() throw() | |
| { | |
| _setModel(NULL); | |
| } | |
| Binding::Model_t Binding::getModel() const | |
| { | |
| return mxModel; | |
| } | |
| void Binding::_setModel( const Model_t& xModel ) | |
| { | |
| PropertyChangeNotifier aNotifyModelChange( *this, HANDLE_Model ); | |
| PropertyChangeNotifier aNotifyModelIDChange( *this, HANDLE_ModelID ); | |
| // prepare binding for removal of old model | |
| clear(); // remove all cached data (e.g. XPath evaluation results) | |
| XNameContainer_t xNamespaces = getModelNamespaces(); // save namespaces | |
| mxModel = xModel; | |
| // set namespaces (and move to model, if appropriate) | |
| setBindingNamespaces( xNamespaces ); | |
| _checkBindingID(); | |
| notifyAndCachePropertyValue( HANDLE_ExternalData ); | |
| } | |
| OUString Binding::getModelID() const | |
| { | |
| Model* pModel = getModelImpl(); | |
| return ( pModel == NULL ) ? OUString() : pModel->getID(); | |
| } | |
| Binding::XNodeList_t Binding::getXNodeList() | |
| { | |
| // first make sure we are bound | |
| if( ! maBindingExpression.hasValue() ) | |
| bind( sal_False ); | |
| return maBindingExpression.getXNodeList(); | |
| } | |
| bool Binding::isSimpleBinding() const | |
| { | |
| return maBindingExpression.isSimpleExpression() | |
| && maReadonly.isSimpleExpression() | |
| && maRelevant.isSimpleExpression() | |
| && maRequired.isSimpleExpression() | |
| && maConstraint.isSimpleExpression() | |
| && maCalculate.isSimpleExpression(); | |
| } | |
| bool Binding::isSimpleBindingExpression() const | |
| { | |
| return maBindingExpression.isSimpleExpression(); | |
| } | |
| void Binding::update() | |
| { | |
| // clear all expressions (to remove cached node references) | |
| maBindingExpression.clear(); | |
| maReadonly.clear(); | |
| maRelevant.clear(); | |
| maRequired.clear(); | |
| maConstraint.clear(); | |
| maCalculate.clear(); | |
| // let's just pretend the binding has been modified -> full rebind() | |
| bindingModified(); | |
| } | |
| void Binding::deferNotifications( bool bDefer ) | |
| { | |
| mnDeferModifyNotifications += ( bDefer ? 1 : -1 ); | |
| OSL_ENSURE( mnDeferModifyNotifications >= 0, "you're deferring too much" ); | |
| if( mnDeferModifyNotifications == 0 ) | |
| { | |
| if( mbBindingModified ) | |
| bindingModified(); | |
| if( mbValueModified ) | |
| valueModified(); | |
| } | |
| OSL_ENSURE( ( mnDeferModifyNotifications > 0 ) | |
| || ( ! mbBindingModified && ! mbValueModified ), | |
| "deferred modifications not delivered?" ); | |
| } | |
| bool Binding::isValid() | |
| { | |
| // TODO: determine whether node is suitable, not just whether it exists | |
| return maBindingExpression.getNode().is() && | |
| isValid_DataType() && | |
| maMIP.isConstraint() && | |
| ( ! maMIP.isRequired() || | |
| ( maBindingExpression.hasValue() && | |
| maBindingExpression.getString().getLength() > 0 ) ); | |
| } | |
| bool Binding::isUseful() | |
| { | |
| // we are useful, if | |
| // 0) we don't have a model | |
| // (at least, in this case we shouldn't be removed from the model) | |
| // 1) we have a proper name | |
| // 2) we have some MIPs, | |
| // 3) we are bound to some control | |
| // (this can be assumed if some listeners are set) | |
| bool bUseful = | |
| getModelImpl() == NULL | |
| // || msBindingID.getLength() > 0 | |
| || msTypeName.getLength() > 0 | |
| || ! maReadonly.isEmptyExpression() | |
| || ! maRelevant.isEmptyExpression() | |
| || ! maRequired.isEmptyExpression() | |
| || ! maConstraint.isEmptyExpression() | |
| || ! maCalculate.isEmptyExpression() | |
| || ! maModifyListeners.empty() | |
| || ! maListEntryListeners.empty() | |
| || ! maValidityListeners.empty(); | |
| return bUseful; | |
| } | |
| OUString Binding::explainInvalid() | |
| { | |
| OUString sReason; | |
| if( ! maBindingExpression.getNode().is() ) | |
| { | |
| sReason = ( maBindingExpression.getExpression().getLength() == 0 ) | |
| ? getResource( RID_STR_XFORMS_NO_BINDING_EXPRESSION ) | |
| : getResource( RID_STR_XFORMS_INVALID_BINDING_EXPRESSION ); | |
| } | |
| else if( ! isValid_DataType() ) | |
| { | |
| sReason = explainInvalid_DataType(); | |
| if( sReason.getLength() == 0 ) | |
| { | |
| // no explanation given by data type? Then give generic message | |
| sReason = getResource( RID_STR_XFORMS_INVALID_VALUE, | |
| maMIP.getTypeName() ); | |
| } | |
| } | |
| else if( ! maMIP.isConstraint() ) | |
| { | |
| sReason = maMIP.getConstraintExplanation(); | |
| } | |
| else if( maMIP.isRequired() && maBindingExpression.hasValue() && | |
| ( maBindingExpression.getString().getLength() == 0 ) ) | |
| { | |
| sReason = getResource( RID_STR_XFORMS_REQUIRED ); | |
| } | |
| // else: no explanation given; should only happen if data is valid | |
| OSL_ENSURE( ( sReason.getLength() == 0 ) == isValid(), | |
| "invalid data should have an explanation!" ); | |
| return sReason; | |
| } | |
| EvaluationContext Binding::getEvaluationContext() const | |
| { | |
| OSL_ENSURE( getModelImpl() != NULL, "need model impl" ); | |
| EvaluationContext aContext = getModelImpl()->getEvaluationContext(); | |
| aContext.mxNamespaces = getBindingNamespaces(); | |
| return aContext; | |
| } | |
| ::std::vector<EvaluationContext> Binding::getMIPEvaluationContexts() | |
| { | |
| OSL_ENSURE( getModelImpl() != NULL, "need model impl" ); | |
| // bind (in case we were not bound before) | |
| bind( sal_False ); | |
| return _getMIPEvaluationContexts(); | |
| } | |
| Binding::IntSequence_t Binding::getUnoTunnelID() | |
| { | |
| static cppu::OImplementationId aImplementationId; | |
| return aImplementationId.getImplementationId(); | |
| } | |
| Binding* SAL_CALL Binding::getBinding( const Reference<XPropertySet>& xPropertySet ) | |
| { | |
| Reference<XUnoTunnel> xTunnel( xPropertySet, UNO_QUERY ); | |
| return xTunnel.is() | |
| ? reinterpret_cast<Binding*>( xTunnel->getSomething(getUnoTunnelID())) | |
| : NULL; | |
| } | |
| OUString Binding::getBindingID() const | |
| { | |
| return msBindingID; | |
| } | |
| void Binding::setBindingID( const OUString& sBindingID ) | |
| { | |
| msBindingID = sBindingID; | |
| } | |
| OUString Binding::getBindingExpression() const | |
| { | |
| return maBindingExpression.getExpression(); | |
| } | |
| void Binding::setBindingExpression( const OUString& sBindingExpression) | |
| { | |
| maBindingExpression.setExpression( sBindingExpression ); | |
| bindingModified(); | |
| } | |
| OUString Binding::getReadonlyExpression() const | |
| { | |
| return maReadonly.getExpression(); | |
| } | |
| void Binding::setReadonlyExpression( const OUString& sReadonly) | |
| { | |
| maReadonly.setExpression( sReadonly ); | |
| bindingModified(); | |
| } | |
| OUString Binding::getRelevantExpression() const | |
| { | |
| return maRelevant.getExpression(); | |
| } | |
| void Binding::setRelevantExpression( const OUString& sRelevant ) | |
| { | |
| maRelevant.setExpression( sRelevant ); | |
| bindingModified(); | |
| } | |
| OUString Binding::getRequiredExpression() const | |
| { | |
| return maRequired.getExpression(); | |
| } | |
| void Binding::setRequiredExpression( const OUString& sRequired ) | |
| { | |
| maRequired.setExpression( sRequired ); | |
| bindingModified(); | |
| } | |
| OUString Binding::getConstraintExpression() const | |
| { | |
| return maConstraint.getExpression(); | |
| } | |
| void Binding::setConstraintExpression( const OUString& sConstraint ) | |
| { | |
| maConstraint.setExpression( sConstraint ); | |
| msExplainConstraint = getResource( RID_STR_XFORMS_INVALID_CONSTRAINT, | |
| sConstraint ); | |
| // TODO: This should only re-evaluate the constraint, and notify | |
| // the validity constraint listeners; instead we currently pretend | |
| // the entire binding was notified, which does a little too much. | |
| bindingModified(); | |
| } | |
| OUString Binding::getCalculateExpression() const | |
| { | |
| return maCalculate.getExpression(); | |
| } | |
| void Binding::setCalculateExpression( const OUString& sCalculate ) | |
| { | |
| maCalculate.setExpression( sCalculate ); | |
| bindingModified(); | |
| } | |
| OUString Binding::getType() const | |
| { | |
| return msTypeName; | |
| } | |
| void Binding::setType( const OUString& sTypeName ) | |
| { | |
| msTypeName = sTypeName; | |
| bindingModified(); | |
| } | |
| Binding::XNameContainer_t Binding::getBindingNamespaces() const | |
| { | |
| // return _getNamespaces(); | |
| return mxNamespaces; | |
| } | |
| void Binding::setBindingNamespaces( const XNameContainer_t& rNamespaces ) | |
| { | |
| _setNamespaces( rNamespaces, true ); | |
| } | |
| Binding::XNameContainer_t Binding::getModelNamespaces() const | |
| { | |
| return _getNamespaces(); | |
| } | |
| void Binding::setModelNamespaces( const XNameContainer_t& rNamespaces ) | |
| { | |
| _setNamespaces( rNamespaces, false ); | |
| } | |
| bool Binding::getReadOnly() const | |
| { | |
| return maMIP.isReadonly(); | |
| } | |
| bool Binding::getRelevant() const | |
| { | |
| return maMIP.isRelevant(); | |
| } | |
| bool Binding::getExternalData() const | |
| { | |
| bool bExternalData = true; | |
| if ( !mxModel.is() ) | |
| return bExternalData; | |
| try | |
| { | |
| Reference< XPropertySet > xModelProps( mxModel, UNO_QUERY_THROW ); | |
| OSL_VERIFY( | |
| xModelProps->getPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ExternalData" ) ) ) >>= bExternalData ); | |
| } | |
| catch( const Exception& ) | |
| { | |
| DBG_UNHANDLED_EXCEPTION(); | |
| } | |
| return bExternalData; | |
| } | |
| void Binding::checkLive() | |
| throw( RuntimeException ) | |
| { | |
| if( ! isLive() ) | |
| throw RuntimeException( EXCEPT("Binding not initialized") ); | |
| } | |
| void Binding::checkModel() | |
| throw( RuntimeException ) | |
| { | |
| if( ! mxModel.is() ) | |
| throw RuntimeException( EXCEPT("Binding has no Model") ); | |
| } | |
| bool Binding::isLive() const | |
| { | |
| const Model* pModel = getModelImpl(); | |
| return ( pModel != NULL ) ? pModel->isInitialized() : false; | |
| } | |
| Model* Binding::getModelImpl() const | |
| { | |
| return getModelImpl( mxModel ); | |
| } | |
| Model* Binding::getModelImpl( const Model_t& xModel ) const | |
| { | |
| Reference<XUnoTunnel> xTunnel( xModel, UNO_QUERY ); | |
| Model* pModel = xTunnel.is() | |
| ? reinterpret_cast<Model*>( | |
| xTunnel->getSomething( Model::getUnoTunnelID() ) ) | |
| : NULL; | |
| return pModel; | |
| } | |
| void lcl_addListenerToNode( Reference<XNode> xNode, | |
| Reference<XEventListener> xListener ) | |
| { | |
| Reference<XEventTarget> xTarget( xNode, UNO_QUERY ); | |
| if( xTarget.is() ) | |
| { | |
| xTarget->addEventListener( OUSTRING("DOMCharacterDataModified"), | |
| xListener, false ); | |
| xTarget->addEventListener( OUSTRING("DOMCharacterDataModified"), | |
| xListener, true ); | |
| xTarget->addEventListener( OUSTRING("DOMAttrModified"), | |
| xListener, false ); | |
| xTarget->addEventListener( OUSTRING("DOMAttrModified"), | |
| xListener, true ); | |
| xTarget->addEventListener( OUSTRING("DOMAttrModified"), | |
| xListener, true ); | |
| xTarget->addEventListener( OUSTRING("xforms-generic"), | |
| xListener, true ); | |
| } | |
| } | |
| void lcl_removeListenerFromNode( Reference<XNode> xNode, | |
| Reference<XEventListener> xListener ) | |
| { | |
| Reference<XEventTarget> xTarget( xNode, UNO_QUERY ); | |
| if( xTarget.is() ) | |
| { | |
| xTarget->removeEventListener( OUSTRING("DOMCharacterDataModified"), | |
| xListener, false ); | |
| xTarget->removeEventListener( OUSTRING("DOMCharacterDataModified"), | |
| xListener, true ); | |
| xTarget->removeEventListener( OUSTRING("DOMAttrModified"), | |
| xListener, false ); | |
| xTarget->removeEventListener( OUSTRING("DOMAttrModified"), | |
| xListener, true ); | |
| xTarget->removeEventListener( OUSTRING("xforms-generic"), | |
| xListener, true ); | |
| } | |
| } | |
| ::std::vector<EvaluationContext> Binding::_getMIPEvaluationContexts() const | |
| { | |
| OSL_ENSURE( getModelImpl() != NULL, "need model impl" ); | |
| // iterate over nodes of bind expression and create | |
| // EvaluationContext for each | |
| PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList(); | |
| ::std::vector<EvaluationContext> aVector; | |
| sal_Int32 nCount = 0; // count nodes for context position | |
| for( PathExpression::NodeVector_t::iterator aIter = aNodes.begin(); | |
| aIter != aNodes.end(); | |
| aIter++, nCount++ ) | |
| { | |
| OSL_ENSURE( aIter->is(), "no node?" ); | |
| // create proper evaluation context for this MIP | |
| aVector.push_back( EvaluationContext( *aIter, getModel(), | |
| getBindingNamespaces(), | |
| nCount, aNodes.size() ) ); | |
| } | |
| return aVector; | |
| } | |
| void Binding::bind( bool bForceRebind ) | |
| { | |
| checkModel(); | |
| // bind() will evaluate this binding as follows: | |
| // 1) evaluate the binding expression | |
| // 1b) if necessary, create node according to 'lazy author' rules | |
| // 2) register suitable listeners on the instance (and remove old ones) | |
| // 3) remove old MIPs defined by this binding | |
| // 4) for every node in the binding nodeset do: | |
| // 1) create proper evaluation context for this MIP | |
| // 2) evaluate calculate expression (and push value into instance) | |
| // 3) evaluate remaining MIPs | |
| // 4) evaluate the locally defined MIPs, and push them to the model | |
| // 1) evaluate the binding expression | |
| EvaluationContext aContext = getEvaluationContext(); | |
| maBindingExpression.evaluate( aContext ); | |
| if( ! maBindingExpression.getNode().is() ) | |
| { | |
| // 1b) create node (if valid element name) | |
| if( isValidQName( maBindingExpression.getExpression(), | |
| aContext.mxNamespaces ) ) | |
| { | |
| aContext.mxContextNode->appendChild( | |
| Reference<XNode>( | |
| aContext.mxContextNode->getOwnerDocument()->createElement( | |
| maBindingExpression.getExpression() ), | |
| UNO_QUERY ) ); | |
| maBindingExpression.evaluate( aContext ); | |
| OSL_ENSURE( maBindingExpression.getNode().is(), | |
| "we should bind to the newly inserted node!" ); | |
| } | |
| } | |
| PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList(); | |
| // 2) register suitable listeners on the instance (and remove old ones) | |
| if( maEventNodes.empty() || bForceRebind ) | |
| { | |
| for( XNodes_t::iterator aIter = maEventNodes.begin(); | |
| aIter != maEventNodes.end(); | |
| aIter ++ ) | |
| lcl_removeListenerFromNode( *aIter, this ); | |
| maEventNodes.clear(); | |
| if( isSimpleBinding() ) | |
| for( PathExpression::NodeVector_t::iterator aIter = aNodes.begin(); | |
| aIter != aNodes.end(); | |
| aIter++ ) | |
| maEventNodes.push_back( *aIter ); | |
| else | |
| maEventNodes.push_back( | |
| Reference<XNode>( aContext.mxContextNode->getOwnerDocument(), | |
| UNO_QUERY_THROW ) ); | |
| for( PathExpression::NodeVector_t::iterator aIter2 = maEventNodes.begin(); | |
| aIter2 != maEventNodes.end(); | |
| aIter2 ++ ) | |
| lcl_addListenerToNode( *aIter2, this ); | |
| } | |
| // 3) remove old MIPs defined by this binding | |
| Model* pModel = getModelImpl(); | |
| OSL_ENSURE( pModel != NULL, "need model" ); | |
| pModel->removeMIPs( this ); | |
| // 4) calculate all MIPs | |
| ::std::vector<EvaluationContext> aMIPContexts = _getMIPEvaluationContexts(); | |
| for( ::std::vector<EvaluationContext>::iterator aIter = aMIPContexts.begin(); | |
| aIter != aMIPContexts.end(); | |
| aIter++ ) | |
| { | |
| EvaluationContext& rContext = *aIter; | |
| // evaluate calculate expression (and push value into instance) | |
| // (prevent recursion using mbInCalculate | |
| if( ! maCalculate.isEmptyExpression() ) | |
| { | |
| if( ! mbInCalculate ) | |
| { | |
| mbInCalculate = true; | |
| maCalculate.evaluate( rContext ); | |
| pModel->setSimpleContent( rContext.mxContextNode, | |
| maCalculate.getString() ); | |
| mbInCalculate = false; | |
| } | |
| } | |
| // now evaluate remaining MIPs in the apropriate context | |
| maReadonly.evaluate( rContext ); | |
| maRelevant.evaluate( rContext ); | |
| maRequired.evaluate( rContext ); | |
| maConstraint.evaluate( rContext ); | |
| // type is static; does not need updating | |
| // evaluate the locally defined MIPs, and push them to the model | |
| pModel->addMIP( this, rContext.mxContextNode, getLocalMIP() ); | |
| } | |
| } | |
| // helper for Binding::valueModified | |
| void lcl_modified( const Binding::XModifyListener_t xListener, | |
| const Reference<XInterface> xSource ) | |
| { | |
| OSL_ENSURE( xListener.is(), "no listener?" ); | |
| xListener->modified( EventObject( xSource ) ); | |
| } | |
| // helper for Binding::valueModified | |
| void lcl_listentry( const Binding::XListEntryListener_t xListener, | |
| const Reference<XInterface> xSource ) | |
| { | |
| OSL_ENSURE( xListener.is(), "no listener?" ); | |
| // TODO: send fine granular events | |
| xListener->allEntriesChanged( EventObject( xSource ) ); | |
| } | |
| // helper for Binding::valueModified | |
| void lcl_validate( const Binding::XValidityConstraintListener_t xListener, | |
| const Reference<XInterface> xSource ) | |
| { | |
| OSL_ENSURE( xListener.is(), "no listener?" ); | |
| xListener->validityConstraintChanged( EventObject( xSource ) ); | |
| } | |
| void Binding::valueModified() | |
| { | |
| // defer notifications, if so desired | |
| if( mnDeferModifyNotifications > 0 ) | |
| { | |
| mbValueModified = true; | |
| return; | |
| } | |
| mbValueModified = false; | |
| // query MIP used by our first node (also note validity) | |
| Reference<XNode> xNode = maBindingExpression.getNode(); | |
| maMIP = getModelImpl()->queryMIP( xNode ); | |
| // distribute MIPs _used_ by this binding | |
| if( xNode.is() ) | |
| { | |
| notifyAndCachePropertyValue( HANDLE_ReadOnly ); | |
| notifyAndCachePropertyValue( HANDLE_Relevant ); | |
| } | |
| // iterate over _value_ listeners and send each a modified signal, | |
| // using this object as source (will also update validity, because | |
| // control will query once the value has changed) | |
| Reference<XInterface> xSource = static_cast<XPropertySet*>( this ); | |
| ::std::for_each( maModifyListeners.begin(), | |
| maModifyListeners.end(), | |
| ::std::bind2nd( ::std::ptr_fun( lcl_modified ), xSource ) ); | |
| ::std::for_each( maListEntryListeners.begin(), | |
| maListEntryListeners.end(), | |
| ::std::bind2nd( ::std::ptr_fun( lcl_listentry ), xSource ) ); | |
| ::std::for_each( maValidityListeners.begin(), | |
| maValidityListeners.end(), | |
| ::std::bind2nd( ::std::ptr_fun( lcl_validate ), xSource ) ); | |
| // now distribute MIPs to childs | |
| if( xNode.is() ) | |
| distributeMIP( xNode->getFirstChild() ); | |
| } | |
| void Binding::distributeMIP( const XNode_t & rxNode ) { | |
| typedef com::sun::star::xforms::XFormsEventConcrete XFormsEvent_t; | |
| OUString sEventName( RTL_CONSTASCII_USTRINGPARAM("xforms-generic") ); | |
| XFormsEvent_t *pEvent = new XFormsEvent_t; | |
| pEvent->initXFormsEvent(sEventName, sal_True, sal_False); | |
| Reference<XEvent> xEvent(pEvent); | |
| // naive depth-first traversal | |
| XNode_t xNode( rxNode ); | |
| while(xNode.is()) { | |
| // notifications should be triggered at the | |
| // leaf nodes first, bubbling upwards the hierarchy. | |
| XNode_t child(xNode->getFirstChild()); | |
| if(child.is()) | |
| distributeMIP(child); | |
| // we're standing at a particular node somewhere | |
| // below the one which changed a property (MIP). | |
| // bindings which are listening at this node will receive | |
| // a notification message about what exactly happened. | |
| Reference< XEventTarget > target(xNode,UNO_QUERY); | |
| target->dispatchEvent(xEvent); | |
| xNode = xNode->getNextSibling(); | |
| }; | |
| } | |
| void Binding::bindingModified() | |
| { | |
| // defer notifications, if so desired | |
| if( mnDeferModifyNotifications > 0 ) | |
| { | |
| mbBindingModified = true; | |
| return; | |
| } | |
| mbBindingModified = false; | |
| // rebind (if live); then call valueModified | |
| // A binding should be inert until its model is fully constructed. | |
| if( isLive() ) | |
| { | |
| bind( true ); | |
| valueModified(); | |
| } | |
| } | |
| MIP Binding::getLocalMIP() const | |
| { | |
| MIP aMIP; | |
| if( maReadonly.hasValue() ) | |
| aMIP.setReadonly( maReadonly.getBool( false ) ); | |
| if( maRelevant.hasValue() ) | |
| aMIP.setRelevant( maRelevant.getBool( true ) ); | |
| if( maRequired.hasValue() ) | |
| aMIP.setRequired( maRequired.getBool( false ) ); | |
| if( maConstraint.hasValue() ) | |
| { | |
| aMIP.setConstraint( maConstraint.getBool( true ) ); | |
| if( ! aMIP.isConstraint() ) | |
| aMIP.setConstraintExplanation( msExplainConstraint ); | |
| } | |
| if( msTypeName.getLength() > 0 ) | |
| aMIP.setTypeName( msTypeName ); | |
| // calculate: only handle presence of calculate; value set elsewhere | |
| aMIP.setHasCalculate( !maCalculate.isEmptyExpression() ); | |
| return aMIP; | |
| } | |
| Binding::XDataType_t Binding::getDataType() | |
| { | |
| OSL_ENSURE( getModel().is(), "need model" ); | |
| OSL_ENSURE( getModel()->getDataTypeRepository().is(), "need types" ); | |
| Reference<XDataTypeRepository> xRepository( | |
| getModel()->getDataTypeRepository(), UNO_QUERY ); | |
| OUString sTypeName = maMIP.getTypeName(); | |
| return ( xRepository.is() && xRepository->hasByName( sTypeName ) ) | |
| ? Reference<XDataType>( xRepository->getByName( sTypeName ), UNO_QUERY) | |
| : Reference<XDataType>( NULL ); | |
| } | |
| bool Binding::isValid_DataType() | |
| { | |
| Reference<XDataType> xDataType = getDataType(); | |
| return xDataType.is() | |
| ? xDataType->validate( maBindingExpression.getString() ) | |
| : true; | |
| } | |
| rtl::OUString Binding::explainInvalid_DataType() | |
| { | |
| Reference<XDataType> xDataType = getDataType(); | |
| return xDataType.is() | |
| ? xDataType->explainInvalid( maBindingExpression.getString() ) | |
| : OUString(); | |
| } | |
| void Binding::clear() | |
| { | |
| // remove MIPs contributed by this binding | |
| Model* pModel = getModelImpl(); | |
| if( pModel != NULL ) | |
| pModel->removeMIPs( this ); | |
| // remove all references | |
| for( XNodes_t::iterator aIter = maEventNodes.begin(); | |
| aIter != maEventNodes.end(); | |
| aIter ++ ) | |
| lcl_removeListenerFromNode( *aIter, this ); | |
| maEventNodes.clear(); | |
| // clear expressions | |
| maBindingExpression.clear(); | |
| maReadonly.clear(); | |
| maRelevant.clear(); | |
| maRequired.clear(); | |
| maConstraint.clear(); | |
| maCalculate.clear(); | |
| // TODO: what about our listeners? | |
| } | |
| void lcl_removeOtherNamespaces( const Binding::XNameContainer_t& xFrom, | |
| Binding::XNameContainer_t& xTo ) | |
| { | |
| OSL_ENSURE( xFrom.is(), "no source" ); | |
| OSL_ENSURE( xTo.is(), "no target" ); | |
| // iterate over name in source | |
| Sequence<OUString> aNames = xTo->getElementNames(); | |
| sal_Int32 nNames = aNames.getLength(); | |
| const OUString* pNames = aNames.getConstArray(); | |
| for( sal_Int32 i = 0; i < nNames; i++ ) | |
| { | |
| const OUString& rName = pNames[i]; | |
| if( ! xFrom->hasByName( rName ) ) | |
| xTo->removeByName( rName ); | |
| } | |
| } | |
| /** copy namespaces from one namespace container into another | |
| * @param bOverwrite true: overwrite namespaces in target | |
| * false: do not overwrite namespaces in target | |
| * @param bMove true: move namespaces (i.e., delete in source) | |
| * false: copy namespaces (do not modify source) | |
| * @param bFromSource true: use elements from source | |
| * false: use only elements from target | |
| */ | |
| void lcl_copyNamespaces( const Binding::XNameContainer_t& xFrom, | |
| Binding::XNameContainer_t& xTo, | |
| bool bOverwrite ) | |
| { | |
| OSL_ENSURE( xFrom.is(), "no source" ); | |
| OSL_ENSURE( xTo.is(), "no target" ); | |
| // iterate over name in source | |
| Sequence<OUString> aNames = xFrom->getElementNames(); | |
| sal_Int32 nNames = aNames.getLength(); | |
| const OUString* pNames = aNames.getConstArray(); | |
| for( sal_Int32 i = 0; i < nNames; i++ ) | |
| { | |
| const OUString& rName = pNames[i]; | |
| // determine whether to copy the value, and whether to delete | |
| // it in the source: | |
| bool bInTarget = xTo->hasByName( rName ); | |
| // we copy: if property is in target, and | |
| // if bOverwrite is set, or when the namespace prefix is free | |
| bool bCopy = bOverwrite || ! bInTarget; | |
| // and now... ACTION! | |
| if( bCopy ) | |
| { | |
| if( bInTarget ) | |
| xTo->replaceByName( rName, xFrom->getByName( rName ) ); | |
| else | |
| xTo->insertByName( rName, xFrom->getByName( rName ) ); | |
| } | |
| } | |
| } | |
| // implement get*Namespaces() | |
| // (identical for both variants) | |
| Binding::XNameContainer_t Binding::_getNamespaces() const | |
| { | |
| XNameContainer_t xNamespaces = new NameContainer<OUString>(); | |
| lcl_copyNamespaces( mxNamespaces, xNamespaces, true ); | |
| // merge model's with binding's own namespaces | |
| Model* pModel = getModelImpl(); | |
| if( pModel != NULL ) | |
| lcl_copyNamespaces( pModel->getNamespaces(), xNamespaces, false ); | |
| return xNamespaces; | |
| } | |
| // implement set*Namespaces() | |
| // bBinding = true: setBindingNamespaces, otherwise: setModelNamespaces | |
| void Binding::_setNamespaces( const XNameContainer_t& rNamespaces, | |
| bool bBinding ) | |
| { | |
| Model* pModel = getModelImpl(); | |
| XNameContainer_t xModelNamespaces = ( pModel != NULL ) | |
| ? pModel->getNamespaces() | |
| : NULL; | |
| OSL_ENSURE( ( pModel != NULL ) == xModelNamespaces.is(), "no model nmsp?"); | |
| // remove deleted namespaces | |
| lcl_removeOtherNamespaces( rNamespaces, mxNamespaces ); | |
| if( !bBinding && xModelNamespaces.is() ) | |
| lcl_removeOtherNamespaces( rNamespaces, xModelNamespaces ); | |
| // copy namespaces as appropriate | |
| Sequence<OUString> aNames = rNamespaces->getElementNames(); | |
| sal_Int32 nNames = aNames.getLength(); | |
| const OUString* pNames = aNames.getConstArray(); | |
| for( sal_Int32 i = 0; i < nNames; i++ ) | |
| { | |
| const OUString& rName = pNames[i]; | |
| Any aValue = rNamespaces->getByName( rName ); | |
| // determine whether the namespace should go into model's or | |
| // into binding's namespaces | |
| bool bLocal = | |
| ! xModelNamespaces.is() | |
| || mxNamespaces->hasByName( rName ) | |
| || ( bBinding | |
| && xModelNamespaces.is() | |
| && xModelNamespaces->hasByName( rName ) ); | |
| // write namespace into the appropriate namespace container | |
| XNameContainer_t& rWhich = bLocal ? mxNamespaces : xModelNamespaces; | |
| OSL_ENSURE( rWhich.is(), "whoops" ); | |
| if( rWhich->hasByName( rName ) ) | |
| rWhich->replaceByName( rName, aValue ); | |
| else | |
| rWhich->insertByName( rName, aValue ); | |
| // always 'promote' namespaces from binding to model, if equal | |
| if( xModelNamespaces.is() | |
| && xModelNamespaces->hasByName( rName ) | |
| && mxNamespaces->hasByName( rName ) | |
| && xModelNamespaces->getByName( rName ) == mxNamespaces->getByName( rName ) ) | |
| { | |
| mxNamespaces->removeByName( rName ); | |
| } | |
| } | |
| // ... done. But we modified the binding! | |
| bindingModified(); | |
| } | |
| void Binding::_checkBindingID() | |
| { | |
| if( getModel().is() ) | |
| { | |
| Reference<XNameAccess> xBindings( getModel()->getBindings(), UNO_QUERY_THROW ); | |
| if( msBindingID.getLength() == 0 ) | |
| { | |
| // no binding ID? then make one up! | |
| OUString sIDPrefix = getResource( RID_STR_XFORMS_BINDING_UI_NAME ); | |
| sIDPrefix += String::CreateFromAscii( " " ); | |
| sal_Int32 nNumber = 0; | |
| OUString sName; | |
| do | |
| { | |
| nNumber++; | |
| sName = sIDPrefix + OUString::valueOf( nNumber ); | |
| } | |
| while( xBindings->hasByName( sName ) ); | |
| setBindingID( sName ); | |
| } | |
| } | |
| } | |
| // | |
| // XValueBinding | |
| // | |
| Binding::Sequence_Type_t Binding::getSupportedValueTypes() | |
| throw( RuntimeException ) | |
| { | |
| return Convert::get().getTypes(); | |
| } | |
| sal_Bool Binding::supportsType( const Type_t& rType ) | |
| throw( RuntimeException ) | |
| { | |
| return Convert::get().hasType( rType ); | |
| } | |
| Binding::Any_t Binding::getValue( const Type_t& rType ) | |
| throw( IncompatibleTypesException, | |
| RuntimeException ) | |
| { | |
| // first, check for model | |
| checkLive(); | |
| // second, check for type | |
| if( ! supportsType( rType ) ) | |
| throw IncompatibleTypesException( EXCEPT( "type unsupported" ) ); | |
| // return string value (if present; else return empty Any) | |
| Binding::Any_t result = Any(); | |
| if(maBindingExpression.hasValue()) { | |
| rtl::OUString pathExpr(maBindingExpression.getString()); | |
| Convert &rConvert = Convert::get(); | |
| result = rConvert.toAny(pathExpr,rType); | |
| } | |
| // return maBindingExpression.hasValue() | |
| // ? Convert::get().toAny( maBindingExpression.getString(), rType ) | |
| // : Any(); | |
| return result; | |
| } | |
| void Binding::setValue( const Any_t& aValue ) | |
| throw( IncompatibleTypesException, | |
| InvalidBindingStateException, | |
| NoSupportException, | |
| RuntimeException ) | |
| { | |
| // first, check for model | |
| checkLive(); | |
| // check for supported type | |
| if( ! supportsType( aValue.getValueType() ) ) | |
| throw IncompatibleTypesException( EXCEPT( "type unsupported" ) ); | |
| if( maBindingExpression.hasValue() ) | |
| { | |
| Binding::XNode_t xNode = maBindingExpression.getNode(); | |
| if( xNode.is() ) | |
| { | |
| OUString sValue = Convert::get().toXSD( aValue ); | |
| bool bSuccess = getModelImpl()->setSimpleContent( xNode, sValue ); | |
| if( ! bSuccess ) | |
| throw InvalidBindingStateException( EXCEPT( "can't set value" ) ); | |
| } | |
| else | |
| throw InvalidBindingStateException( EXCEPT( "no suitable node found" ) ); | |
| } | |
| else | |
| throw InvalidBindingStateException( EXCEPT( "no suitable node found" ) ); | |
| } | |
| // | |
| // XListEntry Source | |
| // | |
| sal_Int32 Binding::getListEntryCount() | |
| throw( RuntimeException ) | |
| { | |
| // first, check for model | |
| checkLive(); | |
| // return size of node list | |
| return maBindingExpression.getNodeList().size(); | |
| } | |
| void lcl_getString( const Reference<XNode>& xNode, OUStringBuffer& rBuffer ) | |
| { | |
| if( xNode->getNodeType() == NodeType_TEXT_NODE | |
| || xNode->getNodeType() == NodeType_ATTRIBUTE_NODE ) | |
| { | |
| rBuffer.append( xNode->getNodeValue() ); | |
| } | |
| else | |
| { | |
| for( Reference<XNode> xChild = xNode->getFirstChild(); | |
| xChild.is(); | |
| xChild = xChild->getNextSibling() ) | |
| { | |
| lcl_getString( xChild, rBuffer ); | |
| } | |
| } | |
| } | |
| OUString lcl_getString( const Reference<XNode>& xNode ) | |
| { | |
| OUStringBuffer aBuffer; | |
| lcl_getString( xNode, aBuffer ); | |
| return aBuffer.makeStringAndClear(); | |
| } | |
| OUString Binding::getListEntry( sal_Int32 nPosition ) | |
| throw( IndexOutOfBoundsException, | |
| RuntimeException ) | |
| { | |
| // first, check for model | |
| checkLive(); | |
| // check bounds and return proper item | |
| PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList(); | |
| if( nPosition < 0 || nPosition >= static_cast<sal_Int32>( aNodes.size() ) ) | |
| throw IndexOutOfBoundsException( EXCEPT("") ); | |
| return lcl_getString( aNodes[ nPosition ] ); | |
| } | |
| Sequence<OUString> Binding::getAllListEntries() | |
| throw( RuntimeException ) | |
| { | |
| // first, check for model | |
| checkLive(); | |
| // create sequence of string values | |
| PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList(); | |
| Sequence<OUString> aSequence( aNodes.size() ); | |
| OUString* pSequence = aSequence.getArray(); | |
| for( sal_Int32 n = 0; n < aSequence.getLength(); n++ ) | |
| { | |
| pSequence[n] = lcl_getString( aNodes[n] ); | |
| } | |
| return aSequence; | |
| } | |
| void Binding::addListEntryListener( const XListEntryListener_t& xListener ) | |
| throw( NullPointerException, | |
| RuntimeException ) | |
| { | |
| OSL_ENSURE( xListener.is(), "need listener!" ); | |
| if( ::std::find( maListEntryListeners.begin(), | |
| maListEntryListeners.end(), | |
| xListener) | |
| == maListEntryListeners.end() ) | |
| maListEntryListeners.push_back( xListener ); | |
| } | |
| void Binding::removeListEntryListener( const XListEntryListener_t& xListener ) | |
| throw( NullPointerException, | |
| RuntimeException ) | |
| { | |
| XListEntryListeners_t::iterator aIter = | |
| ::std::find( maListEntryListeners.begin(), maListEntryListeners.end(), | |
| xListener ); | |
| if( aIter != maListEntryListeners.end() ) | |
| maListEntryListeners.erase( aIter ); | |
| } | |
| // | |
| // XValidator | |
| // | |
| sal_Bool Binding::isValid( const Any_t& ) | |
| throw( RuntimeException ) | |
| { | |
| // first, check for model | |
| checkLive(); | |
| // ignore value; determine validate only on current data | |
| return isValid(); | |
| } | |
| rtl::OUString Binding::explainInvalid( | |
| const Any_t& /*Value*/ ) | |
| throw( RuntimeException ) | |
| { | |
| // first, check for model | |
| checkLive(); | |
| // ignore value; determine explanation only on current data | |
| return explainInvalid(); | |
| } | |
| void Binding::addValidityConstraintListener( | |
| const XValidityConstraintListener_t& xListener ) | |
| throw( NullPointerException, | |
| RuntimeException ) | |
| { | |
| OSL_ENSURE( xListener.is(), "need listener!" ); | |
| if( ::std::find(maValidityListeners.begin(), maValidityListeners.end(), xListener) | |
| == maValidityListeners.end() ) | |
| maValidityListeners.push_back( xListener ); | |
| } | |
| void Binding::removeValidityConstraintListener( | |
| const XValidityConstraintListener_t& xListener ) | |
| throw( NullPointerException, | |
| RuntimeException ) | |
| { | |
| XValidityConstraintListeners_t::iterator aIter = | |
| ::std::find( maValidityListeners.begin(), maValidityListeners.end(), | |
| xListener ); | |
| if( aIter != maValidityListeners.end() ) | |
| maValidityListeners.erase( aIter ); | |
| } | |
| // | |
| // xml::dom::event::XEventListener | |
| // | |
| void Binding::handleEvent( const XEvent_t& xEvent ) | |
| throw( RuntimeException ) | |
| { | |
| OUString sType(xEvent->getType()); | |
| //OUString sEventMIPChanged(RTL_CONSTASCII_USTRINGPARAM("xforms-generic")); | |
| //if(sType.equals(sEventMIPChanged)) { | |
| if(!sType.compareToAscii("xforms-generic")) { | |
| // the modification of the 'mnDeferModifyNotifications'-member | |
| // is necessary to prevent infinite notication looping. | |
| // This can happend in case the binding which caused | |
| // the notification chain is listening to those events | |
| // as well... | |
| bool bPreserveValueModified = mbValueModified; | |
| mnDeferModifyNotifications++; | |
| valueModified(); | |
| --mnDeferModifyNotifications; | |
| mbValueModified = bPreserveValueModified; | |
| return; | |
| } | |
| // if we're a dynamic binding, we better re-bind, too! | |
| bind( false ); | |
| // our value was maybe modified | |
| valueModified(); | |
| } | |
| // | |
| // lang::XUnoTunnel | |
| // | |
| sal_Int64 Binding::getSomething( const IntSequence_t& xId ) | |
| throw( RuntimeException ) | |
| { | |
| return reinterpret_cast<sal_Int64>( ( xId == getUnoTunnelID() ) ? this : NULL ); | |
| } | |
| // | |
| // XCloneable | |
| // | |
| Binding::XCloneable_t SAL_CALL Binding::createClone() | |
| throw( RuntimeException ) | |
| { | |
| Reference< XPropertySet > xClone; | |
| Model* pModel = getModelImpl(); | |
| if ( pModel ) | |
| xClone = pModel->cloneBinding( this ); | |
| else | |
| { | |
| xClone = new Binding; | |
| copy( this, xClone ); | |
| } | |
| return XCloneable_t( xClone, UNO_QUERY ); | |
| } | |
| // | |
| // property set implementations | |
| // | |
| #define REGISTER_PROPERTY( property, type ) \ | |
| registerProperty( PROPERTY( property, type ), \ | |
| new DirectPropertyAccessor< Binding, type >( this, &Binding::set##property, &Binding::get##property ) ); | |
| #define REGISTER_PROPERTY_RO( property, type ) \ | |
| registerProperty( PROPERTY_RO( property, type ), \ | |
| new DirectPropertyAccessor< Binding, type >( this, NULL, &Binding::get##property ) ); | |
| #define REGISTER_BOOL_PROPERTY_RO( property ) \ | |
| registerProperty( PROPERTY_RO( property, sal_Bool ), \ | |
| new BooleanPropertyAccessor< Binding, bool >( this, NULL, &Binding::get##property ) ); | |
| void Binding::initializePropertySet() | |
| { | |
| REGISTER_PROPERTY ( BindingID, OUString ); | |
| REGISTER_PROPERTY ( BindingExpression, OUString ); | |
| REGISTER_PROPERTY_RO ( Model, Model_t ); | |
| REGISTER_PROPERTY ( BindingNamespaces, XNameContainer_t ); | |
| REGISTER_PROPERTY ( ModelNamespaces, XNameContainer_t ); | |
| REGISTER_PROPERTY_RO ( ModelID, OUString ); | |
| REGISTER_PROPERTY ( ReadonlyExpression, OUString ); | |
| REGISTER_PROPERTY ( RelevantExpression, OUString ); | |
| REGISTER_PROPERTY ( RequiredExpression, OUString ); | |
| REGISTER_PROPERTY ( ConstraintExpression, OUString ); | |
| REGISTER_PROPERTY ( CalculateExpression, OUString ); | |
| REGISTER_PROPERTY ( Type, OUString ); | |
| REGISTER_PROPERTY_RO ( ReadOnly, bool ); | |
| REGISTER_PROPERTY_RO ( Relevant, bool ); | |
| REGISTER_BOOL_PROPERTY_RO( ExternalData ); | |
| initializePropertyValueCache( HANDLE_ReadOnly ); | |
| initializePropertyValueCache( HANDLE_Relevant ); | |
| initializePropertyValueCache( HANDLE_ExternalData ); | |
| } | |
| void Binding::addModifyListener( | |
| const XModifyListener_t& xListener ) | |
| throw( RuntimeException ) | |
| { | |
| OSL_ENSURE( xListener.is(), "need listener!" ); | |
| if( ::std::find( maModifyListeners.begin(), maModifyListeners.end(), xListener ) | |
| == maModifyListeners.end() ) | |
| maModifyListeners.push_back( xListener ); | |
| // HACK: currently, we have to 'push' some MIPs to the control | |
| // (read-only, relevant, etc.) To enable this, we need to update | |
| // the control at least once when it registers here. | |
| valueModified(); | |
| } | |
| void Binding::removeModifyListener( | |
| const XModifyListener_t& xListener ) | |
| throw( RuntimeException ) | |
| { | |
| ModifyListeners_t::iterator aIter = | |
| ::std::find( maModifyListeners.begin(), maModifyListeners.end(), xListener ); | |
| if( aIter != maModifyListeners.end() ) | |
| maModifyListeners.erase( aIter ); | |
| } | |
| rtl::OUString Binding::getName() | |
| throw( RuntimeException ) | |
| { | |
| return getBindingID(); | |
| } | |
| void SAL_CALL Binding::setName( const rtl::OUString& rName ) | |
| throw( RuntimeException ) | |
| { | |
| // use the XPropertySet methods, so the change in the name is notified to the | |
| // property listeners | |
| setFastPropertyValue( HANDLE_BindingID, makeAny( rName ) ); | |
| } |