diff options
author | Oliver Bolte <obo@openoffice.org> | 2003-10-21 07:57:42 +0000 |
---|---|---|
committer | Oliver Bolte <obo@openoffice.org> | 2003-10-21 07:57:42 +0000 |
commit | 95ec3a0eb9f73b653170700296e100b6b1f7af4d (patch) | |
tree | db52ad681fe525ed421984e71875090e46667751 /forms/source/component | |
parent | e003d475ceec25756bb1ab3f4df6e6275205f66e (diff) |
INTEGRATION: CWS formcelllinkage (1.24.38); FILE MERGED
2003/10/01 09:18:23 fs 1.24.38.1: #i18994# merging the changes from the CWS fs002
Diffstat (limited to 'forms/source/component')
-rw-r--r-- | forms/source/component/FormComponent.cxx | 830 |
1 files changed, 693 insertions, 137 deletions
diff --git a/forms/source/component/FormComponent.cxx b/forms/source/component/FormComponent.cxx index 87438070eb32..4c801d3a0061 100644 --- a/forms/source/component/FormComponent.cxx +++ b/forms/source/component/FormComponent.cxx @@ -2,9 +2,9 @@ * * $RCSfile: FormComponent.cxx,v $ * - * $Revision: 1.24 $ + * $Revision: 1.25 $ * - * last change: $Author: vg $ $Date: 2003-05-19 13:08:37 $ + * last change: $Author: obo $ $Date: 2003-10-21 08:57:42 $ * * The Contents of this file are made available subject to the terms of * either of the following licenses @@ -92,8 +92,8 @@ #ifndef _COM_SUN_STAR_SDBC_COLUMNVALUE_HPP_ #include <com/sun/star/sdbc/ColumnValue.hpp> #endif -#ifndef _COM_SUN_STAR_UTIL_XCLONEABLE_HPP_ -#include <com/sun/star/util/XCloneable.hpp> +#ifndef _COM_SUN_STAR_UTIL_XMODIFYBROADCASTER_HPP_ +#include <com/sun/star/util/XModifyBroadcaster.hpp> #endif #ifndef _COMPHELPER_PROPERTY_HXX_ @@ -139,6 +139,7 @@ using namespace ::com::sun::star::awt; using namespace ::com::sun::star::io; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::util; +using namespace ::drafts::com::sun::star::form; using namespace ::dbtools; using namespace ::comphelper; @@ -576,10 +577,16 @@ OControlModel::OControlModel( const OControlModel* _pOriginal, const Reference< //------------------------------------------------------------------ OControlModel::~OControlModel() { - DBG_DTOR(OControlModel, NULL); // release the aggregate - if (m_xAggregate.is()) - m_xAggregate->setDelegator(InterfaceRef()); + doResetDelegator( ); + + DBG_DTOR(OControlModel, NULL); +} + +//------------------------------------------------------------------ +void OControlModel::clonedFrom( const OControlModel* _pOriginal ) +{ + // nothing to do in this base class } //------------------------------------------------------------------------------ @@ -927,8 +934,12 @@ Any SAL_CALL OBoundControlModel::queryAggregation( const Type& _rType ) throw (R if (!aReturn.hasValue()) { aReturn = OBoundControlModel_BASE1::queryInterface(_rType); - if (!aReturn.hasValue() && m_bCommitable) - aReturn = OBoundControlModel_BASE2::queryInterface(_rType); + + if ( !aReturn.hasValue() && m_bCommitable ) + aReturn = OBoundControlModel_BASE2::queryInterface( _rType ); + + if ( !aReturn.hasValue() && m_bSupportsExternalBinding ) + aReturn = OBoundControlModel_BASE3::queryInterface( _rType ); } return aReturn; @@ -940,58 +951,195 @@ OBoundControlModel::OBoundControlModel( const ::rtl::OUString& _rUnoControlModelTypeName, const ::rtl::OUString& _rDefault, const sal_Bool _bCommitable, - const sal_Bool _bSetDelegator) - :OControlModel(_rxFactory, _rUnoControlModelTypeName, _rDefault, _bSetDelegator) + const sal_Bool _bSupportExternalBinding) + :OControlModel( _rxFactory, _rUnoControlModelTypeName, _rDefault, sal_False ) + ,OPropertyChangeListener( m_aMutex ) ,m_aUpdateListeners(m_aMutex) ,m_aResetListeners(m_aMutex) ,m_bLoaded(sal_False) ,m_bRequired(sal_False) ,m_bCommitable(_bCommitable) + ,m_bSupportsExternalBinding( _bSupportExternalBinding ) ,m_aLabelServiceName(FRM_SUN_COMPONENT_FIXEDTEXT) - ,m_bResetting(sal_False) ,m_bForwardValueChanges(sal_True) + ,m_bLoadListening( sal_False ) + ,m_nValuePropertyAggregateHandle( -1 ) + ,m_pAggPropMultiplexer( NULL ) + ,m_bSettingAggregateState( sal_False ) { DBG_CTOR(frm_OBoundControlModel, NULL); + + // start property listening at the aggregate + implInitAggMultiplexer( ); } //------------------------------------------------------------------ OBoundControlModel::OBoundControlModel( - const OBoundControlModel* _pOriginal, const Reference< XMultiServiceFactory>& _rxFactory, - const sal_Bool _bCommitable, const sal_Bool _bSetDelegator ) - :OControlModel( _pOriginal, _rxFactory, _bSetDelegator ) + const OBoundControlModel* _pOriginal, const Reference< XMultiServiceFactory>& _rxFactory ) + :OControlModel( _pOriginal, _rxFactory, sal_False ) + ,OPropertyChangeListener( m_aMutex ) ,m_aUpdateListeners( m_aMutex ) ,m_aResetListeners( m_aMutex ) ,m_bLoaded( sal_False ) ,m_bRequired( sal_False ) - ,m_bCommitable( _bCommitable ) - ,m_bResetting( sal_False ) + ,m_bCommitable( _pOriginal->m_bCommitable ) + ,m_bSupportsExternalBinding( _pOriginal->m_bSupportsExternalBinding ) ,m_bForwardValueChanges( sal_True ) + ,m_bLoadListening( sal_False ) + ,m_nValuePropertyAggregateHandle( _pOriginal->m_nValuePropertyAggregateHandle ) + ,m_pAggPropMultiplexer( NULL ) + ,m_bSettingAggregateState( sal_False ) { DBG_CTOR(frm_OBoundControlModel, NULL); + // start property listening at the aggregate + implInitAggMultiplexer( ); + m_aLabelServiceName = _pOriginal->m_aLabelServiceName; - m_sDataFieldConnectivityProperty = _pOriginal->m_sDataFieldConnectivityProperty; + m_sValuePropertyName = _pOriginal->m_sValuePropertyName; + m_nValuePropertyAggregateHandle = _pOriginal->m_nValuePropertyAggregateHandle; m_aControlSource = _pOriginal->m_aControlSource; - // m_xLabelControl, though bneing a property, is not to be cloned, not even the reference will be transfered. + // m_xLabelControl, though being a property, is not to be cloned, not even the reference will be transfered. // (the former should be clear - a clone of the object we're only referencing does not make sense) // (the second would violate the restriction for label controls that they're part of the // same form component hierarchy - we ourself are no part, yet, so we can't have a label control) - m_bCommitable = _pOriginal->m_bCommitable; + + // start listening for changes at the value property + implInitValuePropertyListening( ); } //------------------------------------------------------------------ OBoundControlModel::~OBoundControlModel() { + if ( !OComponentHelper::rBHelper.bDisposed ) + { + acquire(); + dispose(); + } + + doResetDelegator( ); + + OSL_ENSURE( m_pAggPropMultiplexer, "OBoundControlModel::~OBoundControlModel: what about my property multiplexer?" ); + if ( m_pAggPropMultiplexer ) + { + m_pAggPropMultiplexer->dispose(); + m_pAggPropMultiplexer->release(); + m_pAggPropMultiplexer = NULL; + } + DBG_DTOR(frm_OBoundControlModel, NULL); } +//------------------------------------------------------------------ +void OBoundControlModel::clonedFrom( const OControlModel* _pOriginal ) +{ + const OBoundControlModel* pBoundOriginal = static_cast< const OBoundControlModel* >( _pOriginal ); + // the value binding can be handled as if somebody called setValueBinding here + // By definition, bindings can be share between bindables + if ( pBoundOriginal && pBoundOriginal->m_xExternalBinding.is() ) + setValueBinding( pBoundOriginal->m_xExternalBinding ); +} + //----------------------------------------------------------------------------- -Sequence<Type> OBoundControlModel::_getTypes() +void OBoundControlModel::implInitAggMultiplexer( ) { - if (m_bCommitable) - return concatSequences(OControlModel::_getTypes(),OBoundControlModel_BASE1::getTypes(),OBoundControlModel_BASE2::getTypes()); + increment( m_refCount ); + if ( m_xAggregateSet.is() ) + { + m_pAggPropMultiplexer = new OPropertyChangeMultiplexer( this, m_xAggregateSet, sal_False ); + m_pAggPropMultiplexer->acquire(); + } + decrement( m_refCount ); - return concatSequences(OControlModel::_getTypes(),OBoundControlModel_BASE1::getTypes()); + doSetDelegator(); +} + +//----------------------------------------------------------------------------- +void OBoundControlModel::implInitValuePropertyListening( ) const +{ + // start listening for changes at the value property + // There are two pre-requisites for this to be done: + // 1. We support external value bindings. In this case, the changes in the control value need to + // be propagated to the external binding immediately when they happen + // 2. We are not committable. In this case, changes in the control value need to be propagated + // to the database column immediately when they happen. + if ( m_bSupportsExternalBinding || !m_bCommitable ) + { + OSL_ENSURE( m_pAggPropMultiplexer, "OBoundControlModel::implInitValuePropertyListening: no multiplexer!" ); + if ( m_pAggPropMultiplexer && m_sValuePropertyName.getLength() ) + m_pAggPropMultiplexer->addProperty( m_sValuePropertyName ); + } +} + +//----------------------------------------------------------------------------- +void OBoundControlModel::initValueProperty( const ::rtl::OUString& _rValuePropertyName, sal_Int32 _nValuePropertyExternalHandle ) +{ + OSL_PRECOND( !m_sValuePropertyName.getLength() && -1 == m_nValuePropertyAggregateHandle, + "OBoundControlModel::initValueProperty: already called before!" ); + OSL_ENSURE( _rValuePropertyName.getLength(), "OBoundControlModel::initValueProperty: invalid property name!" ); + OSL_ENSURE( _nValuePropertyExternalHandle != -1, "OBoundControlModel::initValueProperty: invalid property handle!" ); + + m_sValuePropertyName = _rValuePropertyName; + m_nValuePropertyAggregateHandle = getOriginalHandle( _nValuePropertyExternalHandle ); + OSL_ENSURE( m_nValuePropertyAggregateHandle != -1, "OBoundControlModel::initValueProperty: unable to find the original handle!" ); + + // start listening for changes at the value property + implInitValuePropertyListening( ); +} + +//----------------------------------------------------------------------------- +void OBoundControlModel::suspendValueListening( ) +{ + OSL_PRECOND( m_sValuePropertyName.getLength(), "OBoundControlModel::suspendValueListening: don't have a value property!" ); + OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::suspendValueListening: I *am* not listening!" ); + + if ( m_pAggPropMultiplexer ) + m_pAggPropMultiplexer->lock(); +} + +//----------------------------------------------------------------------------- +void OBoundControlModel::resumeValueListening( ) +{ + OSL_PRECOND( m_sValuePropertyName.getLength(), "OBoundControlModel::resumeValueListening: don't have a value property!" ); + OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::resumeValueListening: I *am* not listening at all!" ); + OSL_PRECOND( !m_pAggPropMultiplexer || m_pAggPropMultiplexer->locked(), "OBoundControlModel::resumeValueListening: listening not suspended currently!" ); + + if ( m_pAggPropMultiplexer ) + m_pAggPropMultiplexer->unlock(); +} + +//----------------------------------------------------------------------------- +namespace +{ + template< class T > + void appendSequence( Sequence< T >& _rInOut, const Sequence< T >& _rAppend ) + { + sal_Int32 nLengthBefore = _rInOut.getLength(); + sal_Int32 nLengthAppend = _rAppend.getLength(); + _rInOut.realloc( nLengthBefore + nLengthAppend ); + ::std::copy( + _rAppend.getConstArray(), + _rAppend.getConstArray() + nLengthAppend, + _rInOut.getArray() + nLengthBefore + ); + } +} + +//----------------------------------------------------------------------------- +Sequence< Type > OBoundControlModel::_getTypes() +{ + Sequence< Type > aTypes( concatSequences( + OControlModel::_getTypes(), + OBoundControlModel_BASE1::getTypes() + ) ); + + if ( m_bCommitable ) + appendSequence( aTypes, OBoundControlModel_BASE2::getTypes() ); + + if ( m_bSupportsExternalBinding ) + appendSequence( aTypes, OBoundControlModel_BASE3::getTypes() ); + + return aTypes; } // OComponentHelper @@ -1001,63 +1149,147 @@ void OBoundControlModel::disposing() OControlModel::disposing(); osl::MutexGuard aGuard(m_aMutex); - com::sun::star::lang::EventObject aEvt(static_cast<XWeak*>(this)); - m_aResetListeners.disposeAndClear(aEvt); - m_aUpdateListeners.disposeAndClear(aEvt); - if (m_xField.is()) + if ( m_pAggPropMultiplexer ) + m_pAggPropMultiplexer->dispose(); + + // notify all our listeners + com::sun::star::lang::EventObject aEvt( static_cast< XWeak* >( this ) ); + m_aResetListeners.disposeAndClear( aEvt ); + m_aUpdateListeners.disposeAndClear( aEvt ); + + // disconnect from our database column + // TODO: could we replace the following 5 lines with a call to disconnectDatabaseColumn? + // The only more thing which it does is calling onDisconnectedDbColumn - could this + // cause trouble? At least when we continue to call OControlModel::disposing before, it *may*. + if ( m_xField.is() ) { - m_xField->removePropertyChangeListener(PROPERTY_VALUE, this); + m_xField->removePropertyChangeListener( PROPERTY_VALUE, this ); resetField(); } m_xCursor = NULL; - Reference<com::sun::star::lang::XComponent> xComp(m_xLabelControl, UNO_QUERY); - if (xComp.is()) - xComp->removeEventListener(static_cast<com::sun::star::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this))); + // disconnect from our external value binding + if ( hasExternalValueBinding() ) + disconnectExternalValueBinding(); + + Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY ); + if ( xComp.is() ) + xComp->removeEventListener(static_cast< XEventListener* >( static_cast< XPropertyChangeListener* >( this ) ) ); } -// XChild //------------------------------------------------------------------------------ -void SAL_CALL OBoundControlModel::setParent(const Reference<XInterface>& _rxParent) throw(com::sun::star::lang::NoSupportException, RuntimeException) +void OBoundControlModel::_propertyChanged( const PropertyChangeEvent& _rEvt ) throw ( RuntimeException ) { - osl::MutexGuard aGuard(m_aMutex); + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( m_bSettingAggregateState ) + // we caused this ourself + return; + + OSL_ENSURE( _rEvt.PropertyName == m_sValuePropertyName, + "OBoundControlModel::_propertyChanged: where did this come from (1)?" ); + OSL_ENSURE( m_pAggPropMultiplexer && !m_pAggPropMultiplexer->locked(), + "OBoundControlModel::_propertyChanged: where did this come from (2)?" ); + + if ( _rEvt.PropertyName == m_sValuePropertyName ) + { + if ( hasExternalValueBinding() ) + { // the control value changed, while we have an external value binding + // -> forward the value to it + transferControlValueToExternal( ); + } + else if ( !m_bCommitable && m_xColumnUpdate.is() ) + { // the control value changed, while we have are bound to a database column, + // but not committable (which means changes in the control have to be reflected to + // the underlying database column immediately) + // -> forward the value to the database column + commitControlValueToDbColumn( false ); + } + } +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::startAggregatePropertyListening( const ::rtl::OUString& _rPropertyName ) +{ + OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::startAggregatePropertyListening: no multiplexer!" ); + OSL_ENSURE( _rPropertyName.getLength(), "OBoundControlModel::startAggregatePropertyListening: invalid property name!" ); + + if ( m_pAggPropMultiplexer && _rPropertyName.getLength() ) + { + m_pAggPropMultiplexer->addProperty( _rPropertyName ); + } +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::startLoadListening( ) +{ + OSL_PRECOND( !isLoadListening(), "OBoundControlModel::startLoadListening: already listening!" ); + OSL_PRECOND( m_xParent.is(), "OBoundControlModel::startLoadListening: no parent to listen at!" ); + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::startLoadListening: external value binding should overrule the database binding!" ); - // log off old listeners Reference< XLoadable > xLoadable( m_xParent, UNO_QUERY ); if ( xLoadable.is() ) - xLoadable->removeLoadListener( this ); + { + RTL_LOGFILE_CONTEXT( aLogger, "forms::OBoundControlModel::startLoadListening" ); + xLoadable->addLoadListener( this ); + m_bLoadListening = sal_True; + } +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::stopLoadListening( ) +{ + OSL_PRECOND( isLoadListening(), "OBoundControlModel::stopLoadListening: not listening!" ); - // log on new listeners + Reference< XLoadable > xLoadable( m_xParent, UNO_QUERY ); + if ( xLoadable.is() && isLoadListening() ) { - xLoadable = xLoadable.query( _rxParent ); - RTL_LOGFILE_CONTEXT( aLogger, "forms::OBoundControlModel::setParent::logOnLoadListener" ); - if ( xLoadable.is() ) - xLoadable->addLoadListener( this ); + xLoadable->removeLoadListener( this ); + m_bLoadListening = sal_False; } +} + +// XChild +//------------------------------------------------------------------------------ +void SAL_CALL OBoundControlModel::setParent(const Reference<XInterface>& _rxParent) throw(com::sun::star::lang::NoSupportException, RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + // log off old listeners + if ( isLoadListening() ) + stopLoadListening( ); OControlModel::setParent(_rxParent); + + // log on new listeners - only in case we do not have an external value binding + if ( m_xParent.is() && !hasExternalValueBinding() ) + startLoadListening( ); } // XEventListener //------------------------------------------------------------------------------ void SAL_CALL OBoundControlModel::disposing(const com::sun::star::lang::EventObject& _rEvent) throw (RuntimeException) { - osl::MutexGuard aGuard(m_aMutex); - if (m_xField == _rEvent.Source) + ::osl::MutexGuard aGuard( m_aMutex ); + if ( _rEvent.Source == m_xField ) { resetField(); } - else if (m_xLabelControl == _rEvent.Source) + else if ( _rEvent.Source == m_xLabelControl ) { Reference<XPropertySet> xOldValue = m_xLabelControl; m_xLabelControl = NULL; // fire a property change event - Any aOldValue; aOldValue <<= xOldValue; - Any aNewValue; aNewValue <<= m_xLabelControl; + Any aOldValue; aOldValue <<= xOldValue; + Any aNewValue; aNewValue <<= m_xLabelControl; sal_Int32 nHandle = PROPERTY_ID_CONTROLLABEL; - OPropertySetHelper::fire(&nHandle, &aNewValue, &aOldValue, 1, sal_False); + OPropertySetHelper::fire( &nHandle, &aNewValue, &aOldValue, 1, sal_False ); + } + else if ( _rEvent.Source == m_xExternalBinding ) + { + disconnectExternalValueBinding( ); } else OControlModel::disposing(_rEvent); @@ -1183,7 +1415,7 @@ void OBoundControlModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle) co switch (nHandle) { case PROPERTY_ID_CONTROLSOURCEPROPERTY: - rValue <<= m_sDataFieldConnectivityProperty; + rValue <<= m_sValuePropertyName; break; case PROPERTY_ID_CONTROLSOURCE: rValue <<= m_aControlSource; @@ -1327,12 +1559,17 @@ void OBoundControlModel::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, co //------------------------------------------------------------------------------ void SAL_CALL OBoundControlModel::propertyChange( const PropertyChangeEvent& evt ) throw(RuntimeException) { + // TODO: check how this works with external bindings + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::propertyChange: Hey, I have an external binding, so how this?" ); + // if we have an external value binding, then we should *not* be a property change + // listener at an sdb::Column + // Bei Wertaenderung neu initialisieren - if (evt.PropertyName.equals(PROPERTY_VALUE)) + if ( evt.PropertyName.equals( PROPERTY_VALUE ) ) { osl::MutexGuard aGuard(m_aMutex); - if (m_bForwardValueChanges && m_xColumn.is()) - _onValueChanged(); + if ( m_bForwardValueChanges && m_xColumn.is() ) + transferDbValueToControl(); } } @@ -1352,26 +1589,42 @@ void SAL_CALL OBoundControlModel::removeUpdateListener(const Reference< XUpdateL //------------------------------------------------------------------------------ sal_Bool SAL_CALL OBoundControlModel::commit() throw(RuntimeException) { - DBG_ASSERT(m_bCommitable, "OBoundControlModel::commit : invalid call (I'm not commitable !) "); + OSL_PRECOND( m_bCommitable, "OBoundControlModel::commit: invalid call (I'm not commitable !) " ); + if ( hasExternalValueBinding() ) + { + // in most cases, no action is required: For most derivees, we know the value property of + // our control (see initValueProperty), and when an external binding is active, we + // instantly forward all changes in this property to the external binding. + if ( !m_sValuePropertyName.getLength() ) + // but for those derivees which did not use this feature, we need an + // explicit transfer + transferControlValueToExternal( ); + return sal_True; + } + + OSL_ENSURE( !hasExternalValueBinding(), "OBoundControlModel::commit: control flow broken!" ); + // we reach this only if we're not working with an external binding + { osl::MutexGuard aGuard(m_aMutex); - if (!m_xField.is()) + if ( !m_xField.is() ) return sal_True; } - cppu::OInterfaceIteratorHelper aIter(m_aUpdateListeners); - com::sun::star::lang::EventObject aEvt; - aEvt.Source = static_cast<XWeak*>(this); + ::cppu::OInterfaceIteratorHelper aIter( m_aUpdateListeners ); + EventObject aEvt; + aEvt.Source = static_cast< XWeak* >( this ); sal_Bool bSucceed = sal_True; while (aIter.hasMoreElements() && bSucceed) - bSucceed = ((XUpdateListener*)aIter.next())->approveUpdate(aEvt); + bSucceed = static_cast< XUpdateListener* >( aIter.next() )->approveUpdate( aEvt ); - if (bSucceed) + if ( bSucceed ) { osl::MutexGuard aGuard(m_aMutex); try { - bSucceed = _commit(); + if ( m_xColumnUpdate.is() ) + bSucceed = commitControlValueToDbColumn( sal_False ); } catch(Exception&) { @@ -1379,19 +1632,29 @@ sal_Bool SAL_CALL OBoundControlModel::commit() throw(RuntimeException) } } - if (bSucceed) + if ( bSucceed ) { - cppu::OInterfaceIteratorHelper aIter(m_aUpdateListeners); - while (aIter.hasMoreElements()) - ((XUpdateListener*)aIter.next())->updated(aEvt); + cppu::OInterfaceIteratorHelper aIter( m_aUpdateListeners ); + while ( aIter.hasMoreElements() ) + static_cast< XUpdateListener* >( aIter.next() )->updated( aEvt ); } return bSucceed; } //------------------------------------------------------------------------------ +void OBoundControlModel::resetField() +{ + m_xColumnUpdate.clear(); + m_xColumn.clear(); + m_xField.clear(); +} + +//------------------------------------------------------------------------------ sal_Bool OBoundControlModel::connectToField(const Reference<XRowSet>& rForm) { + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::connectToField: invalid call (have an external binding)!" ); + // wenn eine Verbindung zur Datenbank existiert if (rForm.is() && getConnection(rForm).is()) { @@ -1422,7 +1685,7 @@ sal_Bool OBoundControlModel::connectToField(const Reference<XRowSet>& rForm) { sal_Int32 nFieldType; xFieldCandidate->getPropertyValue(PROPERTY_FIELDTYPE) >>= nFieldType; - if (_approve(nFieldType)) + if (approveDbColumnType(nFieldType)) setField(xFieldCandidate,sal_False); } else @@ -1433,10 +1696,12 @@ sal_Bool OBoundControlModel::connectToField(const Reference<XRowSet>& rForm) if(m_xField->getPropertySetInfo()->hasPropertyByName(PROPERTY_VALUE)) { // an wertaenderungen horchen - m_xField->addPropertyChangeListener(PROPERTY_VALUE, this); - m_xColumnUpdate = Reference<XColumnUpdate>(m_xField, UNO_QUERY); - m_xColumn = Reference<XColumn>(m_xField, UNO_QUERY); - INT32 nNullableFlag; m_xField->getPropertyValue(PROPERTY_ISNULLABLE) >>= nNullableFlag; + m_xField->addPropertyChangeListener( PROPERTY_VALUE, this ); + m_xColumnUpdate = Reference< XColumnUpdate >( m_xField, UNO_QUERY ); + m_xColumn = Reference< XColumn >( m_xField, UNO_QUERY ); + + INT32 nNullableFlag = ColumnValue::NO_NULLS; + m_xField->getPropertyValue(PROPERTY_ISNULLABLE) >>= nNullableFlag; m_bRequired = (ColumnValue::NO_NULLS == nNullableFlag); // we're optimistic : in case of ColumnValue_NULLABLE_UNKNOWN we assume nullability .... } @@ -1451,8 +1716,10 @@ sal_Bool OBoundControlModel::connectToField(const Reference<XRowSet>& rForm) } //------------------------------------------------------------------------------ -sal_Bool OBoundControlModel::_approve(sal_Int32 _nColumnType) +sal_Bool OBoundControlModel::approveDbColumnType(sal_Int32 _nColumnType) { + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::approveDbColumnType: invalid call (have an external binding)!" ); + if ((_nColumnType == DataType::BINARY) || (_nColumnType == DataType::VARBINARY) || (_nColumnType == DataType::LONGVARBINARY) || (_nColumnType == DataType::OTHER) || (_nColumnType == DataType::OBJECT) || (_nColumnType == DataType::DISTINCT) @@ -1464,28 +1731,42 @@ sal_Bool OBoundControlModel::_approve(sal_Int32 _nColumnType) return sal_True; } -// XLoadListener +//============================================================================== +// value binding handling + //------------------------------------------------------------------------------ -void SAL_CALL OBoundControlModel::loaded(const com::sun::star::lang::EventObject& _rEvent) throw(RuntimeException) +void OBoundControlModel::connectDatabaseColumn( const Reference< XRowSet >& _rxRowSet, bool _bFromReload ) { - osl::MutexGuard aGuard(m_aMutex); - Reference<XRowSet> xForm(_rEvent.Source, UNO_QUERY); - Reference<XPropertySet> xOldField = m_xField; - connectToField(xForm); + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::connectDatabaseColumn: not to be called with an external value binding!" ); + ::osl::MutexGuard aGuard( m_aMutex ); - m_bLoaded = sal_True; - _loaded(_rEvent); + // consistency checks + DBG_ASSERT( !( m_xField.is() && !_bFromReload ), + "OBoundControlModel::connectDatabaseColumn: the form is just *loaded*, but we already have a field!" ); - if (m_xField.is()) + Reference< XPropertySet > xOldField = m_xField; + if ( !m_xField.is() ) { - // initially call _onValueChanged + // connect to the column + connectToField( _rxRowSet ); + } + + // now that we're connected (more or less, even if we did not find a column), + // we definately want to forward any potentially occuring value changes + m_bForwardValueChanges = sal_True; + + // let derived classes react on this new connection + m_bLoaded = sal_True; + onConnectedDbColumn( _rxRowSet ); + + // did we successfully connect to a database column? + if ( m_xField.is() ) + { // initially transfer the db column value to the control // but only if the rowset if posisitioned on a valid record - Reference< XRowSet > xRowset( _rEvent.Source, UNO_QUERY ); - OSL_ENSURE( xRowset.is(), "OBoundControlModel::loaded: invalid event source (no rowset)!" ); - if ( xRowset.is() ) + if ( _rxRowSet.is() ) { - if ( !xRowset->isBeforeFirst() && !xRowset->isAfterLast() ) - _onValueChanged(); + if ( !_rxRowSet->isBeforeFirst() && !_rxRowSet->isAfterLast() ) + transferDbValueToControl(); } } @@ -1498,15 +1779,54 @@ void SAL_CALL OBoundControlModel::loaded(const com::sun::star::lang::EventObject } } +//------------------------------------------------------------------------------ +void OBoundControlModel::disconnectDatabaseColumn( ) +{ + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::disconnectDatabaseColumn: not to be called with an external value binding!" ); + ::osl::MutexGuard aGuard( m_aMutex ); + + // let derived classes react on this + onDisconnectedDbColumn(); + + if ( m_xField.is() ) + { + m_xField->removePropertyChangeListener( PROPERTY_VALUE, this ); + resetField(); + } + + m_xCursor = NULL; + m_bLoaded = sal_False; +} + +//============================================================================== +// XLoadListener +//------------------------------------------------------------------------------ +void SAL_CALL OBoundControlModel::loaded( const EventObject& _rEvent ) throw(RuntimeException) +{ + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::loaded: we should never reach this with an external value binding!" ); + if ( hasExternalValueBinding() ) + return; + + // connect to the database column described by our SQL-binding-related properties + Reference< XRowSet > xRowSet( _rEvent.Source, UNO_QUERY ); + DBG_ASSERT( xRowSet.is(), "OBoundControlModel::loaded: event source is no RowSet?!" ); + connectDatabaseColumn( xRowSet, false ); +} + //------------------------------------------------------------------------------ void SAL_CALL OBoundControlModel::unloaded( const com::sun::star::lang::EventObject& aEvent ) throw(RuntimeException) { + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::unloaded: we should never reach this with an external value binding!" ); } //------------------------------------------------------------------------------ void SAL_CALL OBoundControlModel::reloading( const com::sun::star::lang::EventObject& aEvent ) throw(RuntimeException) { + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::reloading: we should never reach this with an external value binding!" ); + if ( hasExternalValueBinding() ) + return; + osl::MutexGuard aGuard(m_aMutex); m_bForwardValueChanges = sal_False; } @@ -1514,60 +1834,86 @@ void SAL_CALL OBoundControlModel::reloading( const com::sun::star::lang::EventOb //------------------------------------------------------------------------------ void SAL_CALL OBoundControlModel::unloading(const com::sun::star::lang::EventObject& aEvent) throw(RuntimeException) { - osl::MutexGuard aGuard(m_aMutex); - _unloaded(); + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::unloading: we should never reach this with an external value binding!" ); + if ( hasExternalValueBinding() ) + return; - if (m_xField.is()) - { - m_xField->removePropertyChangeListener(PROPERTY_VALUE, this); - resetField(); - } - m_xCursor = NULL; - m_bLoaded = sal_False; + // disconnect from the database column described by our SQL-binding-related properties + disconnectDatabaseColumn(); } //------------------------------------------------------------------------------ -void SAL_CALL OBoundControlModel::reloaded(const com::sun::star::lang::EventObject& aEvent) throw(RuntimeException) +void SAL_CALL OBoundControlModel::reloaded( const EventObject& _rEvent ) throw(RuntimeException) { - osl::MutexGuard aGuard(m_aMutex); - Reference<XPropertySet> xOldField = m_xField; - // did we lost the connection to the field because there was a new created? - if (!m_xField.is()) - { - Reference<XRowSet> xForm(aEvent.Source, UNO_QUERY); - connectToField(xForm); - } + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::reloaded: we should never reach this with an external value binding!" ); + if ( hasExternalValueBinding() ) + return; - m_bForwardValueChanges = sal_True; - _loaded(aEvent); + Reference< XRowSet > xRowSet( _rEvent.Source, UNO_QUERY ); + DBG_ASSERT( xRowSet.is(), "OBoundControlModel::reloaded: event source is no RowSet?!" ); + connectDatabaseColumn( xRowSet, true ); +} - // do we have a field, than get the new value - if (m_xField.is()) - _onValueChanged(); +//------------------------------------------------------------------------------ +void OBoundControlModel::setControlValue( const Any& _rValue ) +{ + OSL_PRECOND( m_xAggregateFastSet.is() && m_xAggregateSet.is(), + "OBoundControlModel::setControlValue: invalid aggregate !" ); + OSL_PRECOND( m_sValuePropertyName.getLength() || ( m_nValuePropertyAggregateHandle != -1 ), + "OBoundControlModel::setControlValue: please override if you have own value property handling!" ); - if ( xOldField != m_xField ) + m_bSettingAggregateState = sal_True; { - Any aNewValue; aNewValue <<= m_xField; - Any aOldValue; aOldValue <<= xOldField; - sal_Int32 nHandle = PROPERTY_ID_BOUNDFIELD; - OPropertySetHelper::fire(&nHandle, &aNewValue, &aOldValue, 1, sal_False); + // release our mutex once (it's acquired in the calling method !), as setting aggregate properties + // may cause any uno controls belonging to us to lock the solar mutex, which is potentially dangerous with + // our own mutex locked + // FS - 72451 - 31.01.00 + MutexRelease aRelease( m_aMutex ); + if ( ( m_nValuePropertyAggregateHandle != -1 ) && m_xAggregateFastSet.is() ) + { + m_xAggregateFastSet->setFastPropertyValue( m_nValuePropertyAggregateHandle, _rValue ); + } + else if ( m_sValuePropertyName.getLength() && m_xAggregateSet.is() ) + { + m_xAggregateSet->setPropertyValue( m_sValuePropertyName, _rValue ); + } } + m_bSettingAggregateState = sal_False; +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::onConnectedExternalValue( ) +{ +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::onDisconnectedExternalValue( ) +{ } //------------------------------------------------------------------------------ -void OBoundControlModel::_loaded(const com::sun::star::lang::EventObject& rEvent) +void OBoundControlModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm ) { + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::onConnectedDbColumn: how this? There's an external value binding!" ); } //------------------------------------------------------------------------------ -void OBoundControlModel::_unloaded() +void OBoundControlModel::onDisconnectedDbColumn() { + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::onDisconnectedDbColumn: how this? There's an external value binding!" ); } // XReset //----------------------------------------------------------------------------- -void OBoundControlModel::_reset() +Any OBoundControlModel::getDefaultForReset() const { + return Any(); +} + +//----------------------------------------------------------------------------- +void OBoundControlModel::resetNoBroadcast() +{ + setControlValue( getDefaultForReset() ); } //----------------------------------------------------------------------------- @@ -1586,23 +1932,25 @@ void OBoundControlModel::removeResetListener(const Reference<XResetListener>& l) void OBoundControlModel::reset() throw (RuntimeException) { cppu::OInterfaceIteratorHelper aIter(m_aResetListeners); - com::sun::star::lang::EventObject aResetEvent(static_cast<XWeak*>(this)); + EventObject aResetEvent(static_cast<XWeak*>(this)); sal_Bool bContinue = sal_True; - while (aIter.hasMoreElements() && bContinue) - bContinue = reinterpret_cast<XResetListener*>(aIter.next())->approveReset(aResetEvent); + while ( aIter.hasMoreElements() && bContinue ) + bContinue = static_cast< XResetListener* >( aIter.next() )->approveReset( aResetEvent ); if (!bContinue) return; osl::ClearableMutexGuard aGuard(m_aMutex); - m_bResetting = sal_True; - sal_Bool bSimpleReset = !m_xField.is() // no connection to a database field + sal_Bool bSimpleReset = + ( !m_xColumn.is() // no connection to a database column || ( m_xCursor.is() // OR we have an improperly positioned cursor && ( m_xCursor->isAfterLast() || m_xCursor->isBeforeFirst() ) - ); + ) + || hasExternalValueBinding() // OR we have an external value binding + ); if ( !bSimpleReset ) { @@ -1616,6 +1964,7 @@ void OBoundControlModel::reset() throw (RuntimeException) // we have to access the field content at least once to get a reliable result by XColumn::wasNull try { + // TODO: this is expensive in the case of binary fields m_xColumn->getString(); bIsNull = m_xColumn->wasNull(); } @@ -1624,28 +1973,36 @@ void OBoundControlModel::reset() throw (RuntimeException) DBG_ERROR("OBoundControlModel::reset : XColumn::getString and wasNull are expected to always succeed !"); } - if (bIsNull) + sal_Bool bNeedValueTransfer = sal_True; + + if ( bIsNull ) { sal_Bool bIsNewRecord = sal_False; - Reference<XPropertySet> xSet(m_xCursor, UNO_QUERY); - if (xSet.is()) - xSet->getPropertyValue(PROPERTY_ISNEW) >>= bIsNewRecord; - if (bIsNewRecord) + Reference<XPropertySet> xSet( m_xCursor, UNO_QUERY ); + if ( xSet.is() ) + xSet->getPropertyValue( PROPERTY_ISNEW ) >>= bIsNewRecord; + + if ( bIsNewRecord ) { - _reset(); // setzen der Werte, - _commit(); // uebertragen der Werte ins Feld - // fuer das zuruecksetzen des modifyflags ist das Formular zuständig + // reset the control to it's default + resetNoBroadcast(); + // and immediately commit the changes to the DB column, to keep consistency + commitControlValueToDbColumn( sal_True ); + + bNeedValueTransfer = sal_False; } - else - _onValueChanged(); } - else - _onValueChanged(); + + if ( bNeedValueTransfer ) + transferDbValueToControl(); } else - _reset(); + { + resetNoBroadcast(); + if ( hasExternalValueBinding() ) + transferControlValueToExternal(); + } - m_bResetting = sal_False; aGuard.clear(); cppu::OInterfaceIteratorHelper aIterDone(m_aResetListeners); @@ -1653,8 +2010,10 @@ void OBoundControlModel::reset() throw (RuntimeException) reinterpret_cast<XResetListener*>(aIterDone.next())->resetted(aResetEvent); } // ----------------------------------------------------------------------------- -void OBoundControlModel::setField( const Reference< XPropertySet>& _rxField,sal_Bool _bFire) +void OBoundControlModel::setField( const Reference< XPropertySet>& _rxField,sal_Bool _bFire ) { + DBG_ASSERT( !hasExternalValueBinding(), "OBoundControlModel::setField: We have an external value binding!" ); + // fire a property change event if ( m_xField != _rxField ) { @@ -1668,6 +2027,203 @@ void OBoundControlModel::setField( const Reference< XPropertySet>& _rxField,sal_ } } } +//-------------------------------------------------------------------- +sal_Bool OBoundControlModel::approveValueBinding( const Reference< XValueBinding >& _rxBinding ) +{ + // reject everything. Derived classes need to override this if they want to + // benefit from this data binding functionality + return sal_False; +} + +//-------------------------------------------------------------------- +void OBoundControlModel::connectExternalValueBinding( const Reference< XValueBinding >& _rxBinding ) +{ + OSL_PRECOND( _rxBinding.is(), "OBoundControlModel::connectExternalValueBinding: invalid binding instance!" ); + OSL_PRECOND( approveValueBinding( _rxBinding ), "OBoundControlModel::connectExternalValueBinding: binding is not approved!" ); + OSL_PRECOND( !hasExternalValueBinding( ), "OBoundControlModel::connectExternalValueBinding: precond not met (currently have a binding)!" ); + + // Suspend being a load listener at our parent form. This is because + // an external value binding overrules a possible database binding + if ( isLoadListening() ) + stopLoadListening( ); + + // TODO: if we're already connected to a db column, we should disconnect from it here, + // shouldn't we? + + // remember this new binding + m_xExternalBinding = _rxBinding; + + // add as value listener so we get notified when the value changes + Reference< XModifyBroadcaster > xModifiable( m_xExternalBinding, UNO_QUERY ); + if ( xModifiable.is() ) + xModifiable->addModifyListener( this ); + + // tell the derivee + onConnectedExternalValue(); + + // propagate our new value + transferExternalValueToControl( ); +} + +//-------------------------------------------------------------------- +void OBoundControlModel::disconnectExternalValueBinding( ) +{ + // not listening at the binding anymore + Reference< XModifyBroadcaster > xModifiable( m_xExternalBinding, UNO_QUERY ); + if ( xModifiable.is() ) + xModifiable->removeModifyListener( this ); + + // no binding anymore + m_xExternalBinding.clear(); + + // be a load listener at our parent, again. This was suspended while we had + // an external value binding in place. + if ( m_xParent.is() ) + startLoadListening( ); + + // TODO: anything to care for here? Changing values? Falling back to a + // database binding if appropriate? + + // tell the derivee + onDisconnectedExternalValue(); +} + +//-------------------------------------------------------------------- +void SAL_CALL OBoundControlModel::setValueBinding( const Reference< XValueBinding >& _rxBinding ) throw (IncompatibleTypesException, RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + OSL_PRECOND( m_bSupportsExternalBinding, "OBoundControlModel::setValueBinding: How did you reach this method?" ); + // the interface for this method should not have been exposed if we do not + // support binding to external data + + if ( _rxBinding.is() && !approveValueBinding( _rxBinding ) ) + { + throw IncompatibleTypesException( + ::rtl::OUString::createFromAscii( "The value types supported by the binding cannot be used for exchanging data with this control." ), + *this + ); + // TODO: localize this error message + } + + // disconnect from the old binding + if ( hasExternalValueBinding() ) + disconnectExternalValueBinding( ); + + // connect to the new binding + if ( _rxBinding.is() ) + connectExternalValueBinding( _rxBinding ); +} + +//-------------------------------------------------------------------- +Reference< XValueBinding > SAL_CALL OBoundControlModel::getValueBinding( ) throw (RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + OSL_PRECOND( m_bSupportsExternalBinding, "OBoundControlModel::getValueBinding: How did you reach this method?" ); + // the interface for this method should not have been exposed if we do not + // support binding to external data + + return m_xExternalBinding; +} + +//-------------------------------------------------------------------- +void SAL_CALL OBoundControlModel::modified( const EventObject& _rEvent ) throw ( RuntimeException ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + OSL_PRECOND( hasExternalValueBinding(), "OBoundControlModel::modified: Where did this come from?" ); + if ( hasExternalValueBinding() && ( m_xExternalBinding == _rEvent.Source ) ) + { + transferExternalValueToControl( ); + } +} + +//-------------------------------------------------------------------- +void OBoundControlModel::transferDbValueToControl( ) +{ + setControlValue( translateDbColumnToControlValue() ); +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::transferExternalValueToControl( ) +{ + try + { + setControlValue( translateExternalValueToControlValue( ) ); + } + catch( const Exception& ) + { + OSL_ENSURE( sal_False, "OBoundControlModel::transferExternalValueToControl: caught an exception!" ); + } +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::transferControlValueToExternal( ) +{ + OSL_PRECOND( m_bSupportsExternalBinding && hasExternalValueBinding(), + "OBoundControlModel::transferControlValueToExternal: precondition not met!" ); + + if ( m_xExternalBinding.is() ) + { + try + { + m_xExternalBinding->setValue( translateControlValueToExternalValue( ) ); + } + catch( const IncompatibleTypesException& ) + { + OSL_ENSURE( sal_False, "OBoundControlModel::transferControlValueToExternal: could not commit the value (incomptible types)!" ); + } + catch( const NoSupportException& ) + { + OSL_ENSURE( sal_False, "OBoundControlModel::transferControlValueToExternal: could not commit the value (no support)!" ); + } + } +} + +// ----------------------------------------------------------------------------- +Any OBoundControlModel::translateExternalValueToControlValue( ) +{ + OSL_PRECOND( m_bSupportsExternalBinding && hasExternalValueBinding(), + "OBoundControlModel::translateExternalValueToControlValue: precondition not met!" ); + + // determine the type of our value property + if ( m_sValuePropertyName.getLength() && m_xAggregateSet.is() ) + { + Reference< XPropertySetInfo > xPropInfo( m_xAggregateSet->getPropertySetInfo( ) ); + if ( xPropInfo.is() ) + { + Type aValuePropType = xPropInfo->getPropertyByName( m_sValuePropertyName ).Type; + if ( m_xExternalBinding.is() && m_xExternalBinding->supportsType( aValuePropType ) ) + return m_xExternalBinding->getValue( aValuePropType ); + } + } + + OSL_ENSURE( sal_False, "OBoundControlModel::translateExternalValueToControlValue: no default implementation available!" ); + return Any(); +} + +//------------------------------------------------------------------------------ +Any OBoundControlModel::translateControlValueToExternalValue( ) +{ + OSL_PRECOND( m_xAggregateFastSet.is() && m_xAggregateSet.is(), + "OBoundControlModel::translateControlValueToExternalValue: invalid aggregate !" ); + OSL_PRECOND( m_sValuePropertyName.getLength() || ( m_nValuePropertyAggregateHandle != -1 ), + "OBoundControlModel::translateControlValueToExternalValue: please override if you have own value property handling!" ); + + // determine the current control value + Any aControlValue; + if ( ( m_nValuePropertyAggregateHandle != -1 ) && m_xAggregateFastSet.is() ) + { + aControlValue = m_xAggregateFastSet->getFastPropertyValue( m_nValuePropertyAggregateHandle ); + } + else if ( m_sValuePropertyName.getLength() && m_xAggregateSet.is() ) + { + aControlValue = m_xAggregateSet->getPropertyValue( m_sValuePropertyName ); + } + + return aControlValue; +} + // ----------------------------------------------------------------------------- //......................................................................... |