diff options
Diffstat (limited to 'forms/source/component/FormComponent.cxx')
-rw-r--r-- | forms/source/component/FormComponent.cxx | 3076 |
1 files changed, 3076 insertions, 0 deletions
diff --git a/forms/source/component/FormComponent.cxx b/forms/source/component/FormComponent.cxx new file mode 100644 index 000000000000..de2dda9ca388 --- /dev/null +++ b/forms/source/component/FormComponent.cxx @@ -0,0 +1,3076 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_forms.hxx" + +#include "componenttools.hxx" +#include "FormComponent.hxx" +#include "frm_resource.hrc" +#include "frm_resource.hxx" +#include "property.hrc" +#include "services.hxx" + +/** === begin UNO includes === **/ +#include <com/sun/star/awt/XTextComponent.hpp> +#include <com/sun/star/awt/XVclWindowPeer.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/form/XForm.hpp> +#include <com/sun/star/form/XLoadable.hpp> +#include <com/sun/star/io/XMarkableStream.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/sdb/XRowSetChangeBroadcaster.hpp> +#include <com/sun/star/sdb/XRowSetSupplier.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/util/XModifyBroadcaster.hpp> +/** === end UNO includes === **/ + +#include <comphelper/basicio.hxx> +#include <comphelper/guarding.hxx> +#include <comphelper/listenernotification.hxx> +#include <comphelper/property.hxx> +#include <connectivity/dbtools.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <rtl/logfile.hxx> +#include <toolkit/helper/emptyfontdescriptor.hxx> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> + +#include <functional> +#include <algorithm> + +#include <functional> +#include <algorithm> + + +//... namespace frm ....................................................... +namespace frm +{ +//......................................................................... + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::sdb; + using namespace ::com::sun::star::sdbc; + using namespace ::com::sun::star::sdbcx; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::form; + 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 ::com::sun::star::form::binding; + using namespace ::com::sun::star::form::validation; + using namespace ::dbtools; + using namespace ::comphelper; + + //========================================================================= + //= FieldChangeNotifier + //========================================================================= + //------------------------------------------------------------------------- + void ControlModelLock::impl_notifyAll_nothrow() + { + m_rModel.firePropertyChanges( m_aHandles, m_aOldValues, m_aNewValues, OControlModel::LockAccess() ); + } + + //------------------------------------------------------------------------- + void ControlModelLock::addPropertyNotification( const sal_Int32 _nHandle, const Any& _rOldValue, const Any& _rNewValue ) + { + sal_Int32 nOldLength = m_aHandles.getLength(); + if ( ( nOldLength != m_aOldValues.getLength() ) + || ( nOldLength != m_aNewValues.getLength() ) + ) + throw RuntimeException( ::rtl::OUString(), m_rModel ); + + m_aHandles.realloc( nOldLength + 1 ); + m_aHandles[ nOldLength ] = _nHandle; + m_aOldValues.realloc( nOldLength + 1 ); + m_aOldValues[ nOldLength ] = _rOldValue; + m_aNewValues.realloc( nOldLength + 1 ); + m_aNewValues[ nOldLength ] = _rNewValue; + } + + //========================================================================= + //= FieldChangeNotifier + //========================================================================= + //------------------------------------------------------------------------- + class FieldChangeNotifier + { + public: + FieldChangeNotifier( ControlModelLock& _rLock ) + :m_rLock( _rLock ) + ,m_rModel( dynamic_cast< OBoundControlModel& >( _rLock.getModel() ) ) + { + m_xOldField = m_rModel.getField(); + } + + ~FieldChangeNotifier() + { + Reference< XPropertySet > xNewField( m_rModel.getField() ); + if ( m_xOldField != xNewField ) + m_rLock.addPropertyNotification( PROPERTY_ID_BOUNDFIELD, makeAny( m_xOldField ), makeAny( xNewField ) ); + } + + private: + ControlModelLock& m_rLock; + OBoundControlModel& m_rModel; + Reference< XPropertySet > m_xOldField; + }; + +//============================================================================= +//= base class for form layer controls +//============================================================================= +DBG_NAME(frm_OControl) +//------------------------------------------------------------------------------ +OControl::OControl( const Reference< XMultiServiceFactory >& _rxFactory, const rtl::OUString& _rAggregateService, const sal_Bool _bSetDelegator ) + :OComponentHelper(m_aMutex) + ,m_aContext( _rxFactory ) +{ + DBG_CTOR(frm_OControl, NULL); + // VCL-Control aggregieren + // bei Aggregation den Refcount um eins erhoehen da im setDelegator + // das Aggregat selbst den Refcount erhoeht + increment( m_refCount ); + { + m_xAggregate = m_xAggregate.query( _rxFactory->createInstance( _rAggregateService ) ); + m_xControl = m_xControl.query( m_xAggregate ); + } + decrement( m_refCount ); + + if ( _bSetDelegator ) + doSetDelegator(); +} + +//------------------------------------------------------------------------------ +OControl::~OControl() +{ + DBG_DTOR(frm_OControl, NULL); + doResetDelegator(); +} + +//------------------------------------------------------------------------------ +void OControl::doResetDelegator() +{ + if ( m_xAggregate.is() ) + m_xAggregate->setDelegator( NULL ); +} + +//------------------------------------------------------------------------------ +void OControl::doSetDelegator() +{ + increment( m_refCount ); + if ( m_xAggregate.is() ) + { // those brackets are important for some compilers, don't remove! + // (they ensure that the temporary object created in the line below + // is destroyed *before* the refcount-decrement) + m_xAggregate->setDelegator( static_cast< XWeak* >( this ) ); + } + decrement( m_refCount ); +} + +// UNO Anbindung +//------------------------------------------------------------------------------ +Any SAL_CALL OControl::queryAggregation( const Type& _rType ) throw(RuntimeException) +{ + // ask the base class + Any aReturn( OComponentHelper::queryAggregation(_rType) ); + // ask our own interfaces + if (!aReturn.hasValue()) + { + aReturn = OControl_BASE::queryInterface(_rType); + // ask our aggregate + if (!aReturn.hasValue() && m_xAggregate.is()) + aReturn = m_xAggregate->queryAggregation(_rType); + } + + return aReturn; +} + +//------------------------------------------------------------------------------ +Sequence<sal_Int8> SAL_CALL OControl::getImplementationId() throw(RuntimeException) +{ + return OImplementationIds::getImplementationId(getTypes()); +} + +//------------------------------------------------------------------------------ +Sequence<Type> SAL_CALL OControl::getTypes() throw(RuntimeException) +{ + TypeBag aTypes( _getTypes() ); + + Reference< XTypeProvider > xProv; + if ( query_aggregation( m_xAggregate, xProv ) ) + aTypes.addTypes( xProv->getTypes() ); + + return aTypes.getTypes(); +} + +//------------------------------------------------------------------------------ +Sequence<Type> OControl::_getTypes() +{ + return TypeBag( OComponentHelper::getTypes(), OControl_BASE::getTypes() ).getTypes(); +} + +//------------------------------------------------------------------------------ +void OControl::initFormControlPeer( const Reference< XWindowPeer >& /*_rxPeer*/ ) +{ + // nothing to do here +} + +// OComponentHelper +//------------------------------------------------------------------------------ +void OControl::disposing() +{ + OComponentHelper::disposing(); + + m_aWindowStateGuard.attach( NULL, NULL ); + + Reference< XComponent > xComp; + if (query_aggregation(m_xAggregate, xComp)) + xComp->dispose(); +} + +// XServiceInfo +//------------------------------------------------------------------------------ +sal_Bool SAL_CALL OControl::supportsService(const rtl::OUString& _rsServiceName) throw ( RuntimeException) +{ + Sequence<rtl::OUString> aSupported = getSupportedServiceNames(); + const rtl::OUString* pSupported = aSupported.getConstArray(); + for (sal_Int32 i=0; i<aSupported.getLength(); ++i, ++pSupported) + if (pSupported->equals(_rsServiceName)) + return sal_True; + return sal_False; +} + +//------------------------------------------------------------------------------ +Sequence< ::rtl::OUString > OControl::getAggregateServiceNames() +{ + Sequence< ::rtl::OUString > aAggServices; + Reference< XServiceInfo > xInfo; + if ( query_aggregation( m_xAggregate, xInfo ) ) + aAggServices = xInfo->getSupportedServiceNames(); + return aAggServices; +} + +//------------------------------------------------------------------------------ +Sequence<rtl::OUString> SAL_CALL OControl::getSupportedServiceNames() throw(RuntimeException) +{ + return ::comphelper::concatSequences( + getAggregateServiceNames(), + getSupportedServiceNames_Static() + ); +} + +//------------------------------------------------------------------------------ +Sequence< ::rtl::OUString > SAL_CALL OControl::getSupportedServiceNames_Static() throw( RuntimeException ) +{ + // no own supported service names + return Sequence< ::rtl::OUString >(); +} + +// XEventListener +//------------------------------------------------------------------------------ +void SAL_CALL OControl::disposing(const com::sun::star::lang::EventObject& _rEvent) throw (RuntimeException) +{ + Reference< XInterface > xAggAsIface; + query_aggregation(m_xAggregate, xAggAsIface); + + // does the disposing come from the aggregate ? + if (xAggAsIface != Reference< XInterface >(_rEvent.Source, UNO_QUERY)) + { // no -> forward it + Reference<com::sun::star::lang::XEventListener> xListener; + if (query_aggregation(m_xAggregate, xListener)) + xListener->disposing(_rEvent); + } +} + +// XControl +//------------------------------------------------------------------------------ +void SAL_CALL OControl::setContext(const Reference< XInterface >& Context) throw (RuntimeException) +{ + if (m_xControl.is()) + m_xControl->setContext(Context); +} + +//------------------------------------------------------------------------------ +Reference< XInterface > SAL_CALL OControl::getContext() throw (RuntimeException) +{ + return m_xControl.is() ? m_xControl->getContext() : Reference< XInterface >(); +} + +//------------------------------------------------------------------------------ +void OControl::impl_resetStateGuard_nothrow() +{ + Reference< XWindow2 > xWindow; + Reference< XControlModel > xModel; + try + { + xWindow.set( getPeer(), UNO_QUERY ); + xModel.set( getModel(), UNO_QUERY ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + } + m_aWindowStateGuard.attach( xWindow, xModel ); +} + +//------------------------------------------------------------------------------ +void SAL_CALL OControl::createPeer(const Reference<XToolkit>& _rxToolkit, const Reference<XWindowPeer>& _rxParent) throw (RuntimeException) +{ + if ( m_xControl.is() ) + { + m_xControl->createPeer( _rxToolkit, _rxParent ); + + initFormControlPeer( getPeer() ); + + impl_resetStateGuard_nothrow(); + } +} + +//------------------------------------------------------------------------------ +Reference<XWindowPeer> SAL_CALL OControl::getPeer() throw ( RuntimeException) +{ + return m_xControl.is() ? m_xControl->getPeer() : Reference<XWindowPeer>(); +} + +//------------------------------------------------------------------------------ +sal_Bool SAL_CALL OControl::setModel(const Reference<XControlModel>& Model) throw ( RuntimeException) +{ + if ( !m_xControl.is() ) + return sal_False; + + sal_Bool bSuccess = m_xControl->setModel( Model ); + impl_resetStateGuard_nothrow(); + return bSuccess; +} + +//------------------------------------------------------------------------------ +Reference<XControlModel> SAL_CALL OControl::getModel() throw ( RuntimeException) +{ + return m_xControl.is() ? m_xControl->getModel() : Reference<XControlModel>(); +} + +//------------------------------------------------------------------------------ +Reference<XView> SAL_CALL OControl::getView() throw ( RuntimeException) +{ + return m_xControl.is() ? m_xControl->getView() : Reference<XView>(); +} + +//------------------------------------------------------------------------------ +void SAL_CALL OControl::setDesignMode(sal_Bool bOn) throw ( RuntimeException) +{ + if (m_xControl.is()) + m_xControl->setDesignMode(bOn); +} + +//------------------------------------------------------------------------------ +sal_Bool SAL_CALL OControl::isDesignMode() throw ( RuntimeException) +{ + return m_xControl.is() ? m_xControl->isDesignMode() : sal_True; +} + +//------------------------------------------------------------------------------ +sal_Bool SAL_CALL OControl::isTransparent() throw ( RuntimeException) +{ + return m_xControl.is() ? m_xControl->isTransparent() : sal_True; +} + +//================================================================== +//= OBoundControl +//================================================================== +DBG_NAME(frm_OBoundControl); +//------------------------------------------------------------------ +OBoundControl::OBoundControl( const Reference< XMultiServiceFactory >& _rxFactory, + const ::rtl::OUString& _rAggregateService, const sal_Bool _bSetDelegator ) + :OControl( _rxFactory, _rAggregateService, _bSetDelegator ) + ,m_bLocked(sal_False) + ,m_aOriginalFont( EmptyFontDescriptor() ) + ,m_nOriginalTextLineColor( 0 ) +{ + DBG_CTOR(frm_OBoundControl, NULL); +} + +//------------------------------------------------------------------ +OBoundControl::~OBoundControl() +{ + DBG_DTOR(frm_OBoundControl, NULL); +} +// ----------------------------------------------------------------------------- +Sequence< Type> OBoundControl::_getTypes() +{ + return TypeBag( OControl::_getTypes(), OBoundControl_BASE::getTypes() ).getTypes(); +} +//------------------------------------------------------------------ +Any SAL_CALL OBoundControl::queryAggregation(const Type& _rType) throw(RuntimeException) +{ + Any aReturn; + + // XTypeProvider first - don't ask the OBoundControl_BASE, it would deliver incomplete types + if ( _rType.equals( ::getCppuType( static_cast< Reference< XTypeProvider >* >( NULL ) ) ) ) + aReturn = OControl::queryAggregation( _rType ); + + // ask our own interfaces + // (do this first (except XTypeProvider ) - we want to "overwrite" XPropertiesChangeListener) + if ( !aReturn.hasValue() ) + aReturn = OBoundControl_BASE::queryInterface( _rType ); + + // ask the base class + if ( !aReturn.hasValue() ) + aReturn = OControl::queryAggregation( _rType ); + + return aReturn; +} + +//------------------------------------------------------------------ +sal_Bool SAL_CALL OBoundControl::getLock() throw(RuntimeException) +{ + return m_bLocked; +} + +//------------------------------------------------------------------ +void SAL_CALL OBoundControl::setLock(sal_Bool _bLock) throw(RuntimeException) +{ + if (m_bLocked == _bLock) + return; + + osl::MutexGuard aGuard(m_aMutex); + _setLock(_bLock); + m_bLocked = _bLock; +} + +//------------------------------------------------------------------ +void OBoundControl::_setLock(sal_Bool _bLock) +{ + // try to set the text component to readonly + Reference< XWindowPeer > xPeer = getPeer(); + Reference< XTextComponent > xText( xPeer, UNO_QUERY ); + + if ( xText.is() ) + xText->setEditable( !_bLock ); + else + { + // disable the window + Reference< XWindow > xComp( xPeer, UNO_QUERY ); + if ( xComp.is() ) + xComp->setEnable( !_bLock ); + } +} + +//-------------------------------------------------------------------- +sal_Bool SAL_CALL OBoundControl::setModel( const Reference< XControlModel >& _rxModel ) throw (RuntimeException) +{ + return OControl::setModel( _rxModel ); +} + +//-------------------------------------------------------------------- +void SAL_CALL OBoundControl::disposing(const EventObject& Source) throw (RuntimeException) +{ + // just disambiguate + OControl::disposing(Source); +} + +//-------------------------------------------------------------------- +void OBoundControl::disposing() +{ + OControl::disposing(); +} + +//================================================================== +//= OControlModel +//================================================================== +DBG_NAME(OControlModel) +//------------------------------------------------------------------ +Sequence<sal_Int8> SAL_CALL OControlModel::getImplementationId() throw(RuntimeException) +{ + return OImplementationIds::getImplementationId(getTypes()); +} + +//------------------------------------------------------------------ +Sequence<Type> SAL_CALL OControlModel::getTypes() throw(RuntimeException) +{ + TypeBag aTypes( _getTypes() ); + + Reference< XTypeProvider > xProv; + if ( query_aggregation( m_xAggregate, xProv ) ) + aTypes.addTypes( xProv->getTypes() ); + + return aTypes.getTypes(); +} + +//------------------------------------------------------------------------------ +Sequence<Type> OControlModel::_getTypes() +{ + return TypeBag( OComponentHelper::getTypes(), + OPropertySetAggregationHelper::getTypes(), + OControlModel_BASE::getTypes() + ).getTypes(); +} + +//------------------------------------------------------------------ +Any SAL_CALL OControlModel::queryAggregation(const Type& _rType) throw (RuntimeException) +{ + // base class 1 + Any aReturn(OComponentHelper::queryAggregation(_rType)); + + // base class 2 + if (!aReturn.hasValue()) + { + aReturn = OControlModel_BASE::queryInterface(_rType); + + // our own interfaces + if (!aReturn.hasValue()) + { + aReturn = OPropertySetAggregationHelper::queryInterface(_rType); + // our aggregate + if (!aReturn.hasValue() && m_xAggregate.is() && !_rType.equals(::getCppuType(static_cast< Reference< XCloneable>* >(NULL)))) + aReturn = m_xAggregate->queryAggregation(_rType); + } + } + return aReturn; +} + +//------------------------------------------------------------------------------ +void OControlModel::readHelpTextCompatibly(const staruno::Reference< stario::XObjectInputStream >& _rxInStream) +{ + ::rtl::OUString sHelpText; + ::comphelper::operator>>( _rxInStream, sHelpText); + try + { + if (m_xAggregateSet.is()) + m_xAggregateSet->setPropertyValue(PROPERTY_HELPTEXT, makeAny(sHelpText)); + } + catch(const Exception&) + { + OSL_ENSURE(sal_False, "OControlModel::readHelpTextCompatibly: could not forward the property value to the aggregate!"); + } +} + +//------------------------------------------------------------------------------ +void OControlModel::writeHelpTextCompatibly(const staruno::Reference< stario::XObjectOutputStream >& _rxOutStream) +{ + ::rtl::OUString sHelpText; + try + { + if (m_xAggregateSet.is()) + m_xAggregateSet->getPropertyValue(PROPERTY_HELPTEXT) >>= sHelpText; + } + catch(const Exception&) + { + OSL_ENSURE(sal_False, "OControlModel::writeHelpTextCompatibly: could not retrieve the property value from the aggregate!"); + } + ::comphelper::operator<<( _rxOutStream, sHelpText); +} + +//------------------------------------------------------------------ +OControlModel::OControlModel( + const Reference<com::sun::star::lang::XMultiServiceFactory>& _rxFactory, + const ::rtl::OUString& _rUnoControlModelTypeName, + const ::rtl::OUString& rDefault, const sal_Bool _bSetDelegator) + :OComponentHelper(m_aMutex) + ,OPropertySetAggregationHelper(OComponentHelper::rBHelper) + ,m_aContext( _rxFactory ) + ,m_lockCount( 0 ) + ,m_aPropertyBagHelper( *this ) + ,m_nTabIndex(FRM_DEFAULT_TABINDEX) + ,m_nClassId(FormComponentType::CONTROL) + ,m_bNativeLook( sal_False ) + // form controls are usually embedded into documents, not dialogs, and in documents + // the native look is ugly .... + // #i37342# / 2004-11-19 / frank.schoenheit@sun.com +{ + DBG_CTOR(OControlModel, NULL); + if (_rUnoControlModelTypeName.getLength()) // the is a model we have to aggregate + { + increment(m_refCount); + + { + m_xAggregate = Reference<XAggregation>(_rxFactory->createInstance(_rUnoControlModelTypeName), UNO_QUERY); + setAggregation(m_xAggregate); + + if ( m_xAggregateSet.is() ) + { + try + { + if ( rDefault.getLength() ) + m_xAggregateSet->setPropertyValue( PROPERTY_DEFAULTCONTROL, makeAny( rDefault ) ); + } + catch( const Exception& ) + { + OSL_ENSURE( sal_False, "OControlModel::OControlModel: caught an exception!" ); + } + } + } + + if (_bSetDelegator) + doSetDelegator(); + + // Refcount wieder bei NULL + decrement(m_refCount); + } +} + +//------------------------------------------------------------------ +OControlModel::OControlModel( const OControlModel* _pOriginal, const Reference< XMultiServiceFactory>& _rxFactory, const sal_Bool _bCloneAggregate, const sal_Bool _bSetDelegator ) + :OComponentHelper( m_aMutex ) + ,OPropertySetAggregationHelper( OComponentHelper::rBHelper ) + ,m_aContext( _rxFactory ) + ,m_lockCount( 0 ) + ,m_aPropertyBagHelper( *this ) + ,m_nTabIndex( FRM_DEFAULT_TABINDEX ) + ,m_nClassId( FormComponentType::CONTROL ) +{ + DBG_CTOR( OControlModel, NULL ); + DBG_ASSERT( _pOriginal, "OControlModel::OControlModel: invalid original!" ); + + // copy members + m_aName = _pOriginal->m_aName; + m_aTag = _pOriginal->m_aTag; + m_nTabIndex = _pOriginal->m_nTabIndex; + m_nClassId = _pOriginal->m_nClassId; + m_bNativeLook = _pOriginal->m_bNativeLook; + + if ( _bCloneAggregate ) + { + // temporarily increment refcount because of temporary references to ourself in the following + increment( m_refCount ); + + { + // transfer the (only, at the very moment!) ref count + m_xAggregate = createAggregateClone( _pOriginal ); + + // set aggregation (retrieve other direct interfaces of the aggregate) + setAggregation( m_xAggregate ); + } + + // set the delegator, if allowed by our derived class + if ( _bSetDelegator ) + doSetDelegator(); + + // decrement ref count + decrement( m_refCount ); + } +} + +//------------------------------------------------------------------ +OControlModel::~OControlModel() +{ + // release the aggregate + doResetDelegator( ); + + DBG_DTOR(OControlModel, NULL); +} + +//------------------------------------------------------------------ +void OControlModel::clonedFrom( const OControlModel* /*_pOriginal*/ ) +{ + // nothing to do in this base class +} + +//------------------------------------------------------------------------------ +void OControlModel::doResetDelegator() +{ + if (m_xAggregate.is()) + m_xAggregate->setDelegator(NULL); +} + +//------------------------------------------------------------------------------ +void OControlModel::doSetDelegator() +{ + increment(m_refCount); + if (m_xAggregate.is()) + { + m_xAggregate->setDelegator(static_cast<XWeak*>(this)); + } + decrement(m_refCount); +} + +// XChild +//------------------------------------------------------------------------------ +Reference< XInterface > SAL_CALL OControlModel::getParent() throw(RuntimeException) +{ + return m_xParent; +} + +//------------------------------------------------------------------------------ +void SAL_CALL OControlModel::setParent(const Reference< XInterface >& _rxParent) throw(com::sun::star::lang::NoSupportException, RuntimeException) +{ + osl::MutexGuard aGuard(m_aMutex); + + Reference<XComponent> xComp(m_xParent, UNO_QUERY); + if (xComp.is()) + xComp->removeEventListener(static_cast<XPropertiesChangeListener*>(this)); + + m_xParent = _rxParent; + xComp = xComp.query( m_xParent ); + + if ( xComp.is() ) + xComp->addEventListener(static_cast<XPropertiesChangeListener*>(this)); +} + +// XNamed +//------------------------------------------------------------------------------ +::rtl::OUString SAL_CALL OControlModel::getName() throw(RuntimeException) +{ + ::rtl::OUString aReturn; + OPropertySetHelper::getFastPropertyValue(PROPERTY_ID_NAME) >>= aReturn; + return aReturn; +} + +//------------------------------------------------------------------------------ +void SAL_CALL OControlModel::setName(const ::rtl::OUString& _rName) throw(RuntimeException) +{ + setFastPropertyValue(PROPERTY_ID_NAME, makeAny(_rName)); +} + +// XServiceInfo +//------------------------------------------------------------------------------ +sal_Bool SAL_CALL OControlModel::supportsService(const rtl::OUString& _rServiceName) throw ( RuntimeException) +{ + Sequence<rtl::OUString> aSupported = getSupportedServiceNames(); + const rtl::OUString* pSupported = aSupported.getConstArray(); + for (sal_Int32 i=0; i<aSupported.getLength(); ++i, ++pSupported) + if (pSupported->equals(_rServiceName)) + return sal_True; + return sal_False; +} + +//------------------------------------------------------------------------------ +Sequence< ::rtl::OUString > OControlModel::getAggregateServiceNames() +{ + Sequence< ::rtl::OUString > aAggServices; + Reference< XServiceInfo > xInfo; + if ( query_aggregation( m_xAggregate, xInfo ) ) + aAggServices = xInfo->getSupportedServiceNames(); + return aAggServices; +} + +//------------------------------------------------------------------------------ +Sequence<rtl::OUString> SAL_CALL OControlModel::getSupportedServiceNames() throw(RuntimeException) +{ + return ::comphelper::concatSequences( + getAggregateServiceNames(), + getSupportedServiceNames_Static() + ); +} + +//------------------------------------------------------------------------------ +Sequence< ::rtl::OUString > SAL_CALL OControlModel::getSupportedServiceNames_Static() throw( RuntimeException ) +{ + Sequence< ::rtl::OUString > aServiceNames( 2 ); + aServiceNames[ 0 ] = FRM_SUN_FORMCOMPONENT; + aServiceNames[ 1 ] = ::rtl::OUString::createFromAscii( "com.sun.star.form.FormControlModel" ); + return aServiceNames; +} + +// XEventListener +//------------------------------------------------------------------------------ +void SAL_CALL OControlModel::disposing(const com::sun::star::lang::EventObject& _rSource) throw (RuntimeException) +{ + // release the parent + if (_rSource.Source == m_xParent) + { + osl::MutexGuard aGuard(m_aMutex); + m_xParent = NULL; + } + else + { + Reference<com::sun::star::lang::XEventListener> xEvtLst; + if (query_aggregation(m_xAggregate, xEvtLst)) + { + osl::MutexGuard aGuard(m_aMutex); + xEvtLst->disposing(_rSource); + } + } +} + +// OComponentHelper +//----------------------------------------------------------------------------- +void OControlModel::disposing() +{ + OPropertySetAggregationHelper::disposing(); + + Reference<com::sun::star::lang::XComponent> xComp; + if (query_aggregation(m_xAggregate, xComp)) + xComp->dispose(); + + setParent(Reference<XFormComponent>()); + + m_aPropertyBagHelper.dispose(); +} + +//------------------------------------------------------------------------------ +void OControlModel::writeAggregate( const Reference< XObjectOutputStream >& _rxOutStream ) const +{ + Reference< XPersistObject > xPersist; + if ( query_aggregation( m_xAggregate, xPersist ) ) + xPersist->write( _rxOutStream ); +} + +//------------------------------------------------------------------------------ +void OControlModel::readAggregate( const Reference< XObjectInputStream >& _rxInStream ) +{ + Reference< XPersistObject > xPersist; + if ( query_aggregation( m_xAggregate, xPersist ) ) + xPersist->read( _rxInStream ); +} + +//------------------------------------------------------------------------------ +void SAL_CALL OControlModel::write(const Reference<stario::XObjectOutputStream>& _rxOutStream) + throw(stario::IOException, RuntimeException) +{ + osl::MutexGuard aGuard(m_aMutex); + + // 1. Schreiben des UnoControls + Reference<stario::XMarkableStream> xMark(_rxOutStream, UNO_QUERY); + if ( !xMark.is() ) + { + throw IOException( + FRM_RES_STRING( RID_STR_INVALIDSTREAM ), + static_cast< ::cppu::OWeakObject* >( this ) + ); + } + + sal_Int32 nMark = xMark->createMark(); + sal_Int32 nLen = 0; + + _rxOutStream->writeLong(nLen); + + writeAggregate( _rxOutStream ); + + // feststellen der Laenge + nLen = xMark->offsetToMark(nMark) - 4; + xMark->jumpToMark(nMark); + _rxOutStream->writeLong(nLen); + xMark->jumpToFurthest(); + xMark->deleteMark(nMark); + + // 2. Schreiben einer VersionsNummer + _rxOutStream->writeShort(0x0003); + + // 3. Schreiben der allgemeinen Properties + ::comphelper::operator<<( _rxOutStream, m_aName); + _rxOutStream->writeShort(m_nTabIndex); + ::comphelper::operator<<( _rxOutStream, m_aTag); // 3. version + + // !!! IMPORTANT NOTE !!! + // don't write any new members here : this wouldn't be compatible with older versions, as OControlModel + // is a base class which is called in derived classes "read" method. So if you increment the version + // and write new stuff, older office versions will read this in the _derived_ classes, which may result + // in anything from data loss to crash. + // !!! EOIN !!! +} + +//------------------------------------------------------------------------------ +void OControlModel::read(const Reference<stario::XObjectInputStream>& InStream) throw (::com::sun::star::io::IOException, RuntimeException) +{ + osl::MutexGuard aGuard(m_aMutex); + + Reference<stario::XMarkableStream> xMark(InStream, UNO_QUERY); + if ( !xMark.is() ) + { + throw IOException( + FRM_RES_STRING( RID_STR_INVALIDSTREAM ), + static_cast< ::cppu::OWeakObject* >( this ) + ); + } + + // 1. Lesen des UnoControls + sal_Int32 nLen = InStream->readLong(); + if (nLen) + { + sal_Int32 nMark = xMark->createMark(); + + try + { + readAggregate( InStream ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + } + + xMark->jumpToMark(nMark); + InStream->skipBytes(nLen); + xMark->deleteMark(nMark); + } + + // 2. Lesen des Versionsnummer + UINT16 nVersion = InStream->readShort(); + + // 3. Lesen der allgemeinen Properties + ::comphelper::operator>>( InStream, m_aName); + m_nTabIndex = InStream->readShort(); + + if (nVersion > 0x0002) + ::comphelper::operator>>( InStream, m_aTag); + + // we had a version where we wrote the help text + if (nVersion == 0x0004) + readHelpTextCompatibly(InStream); + + DBG_ASSERT(nVersion < 5, "OControlModel::read : suspicious version number !"); + // 4 was the version where we wrote the help text + // later versions shouldn't exist (see write for a detailed comment) +} + +//------------------------------------------------------------------------------ +PropertyState OControlModel::getPropertyStateByHandle( sal_Int32 _nHandle ) +{ + // simply compare the current and the default value + Any aCurrentValue = getPropertyDefaultByHandle( _nHandle ); + Any aDefaultValue; getFastPropertyValue( aDefaultValue, _nHandle ); + + sal_Bool bEqual = uno_type_equalData( + const_cast< void* >( aCurrentValue.getValue() ), aCurrentValue.getValueType().getTypeLibType(), + const_cast< void* >( aDefaultValue.getValue() ), aDefaultValue.getValueType().getTypeLibType(), + reinterpret_cast< uno_QueryInterfaceFunc >(cpp_queryInterface), + reinterpret_cast< uno_ReleaseFunc >(cpp_release) + ); + return bEqual ? PropertyState_DEFAULT_VALUE : PropertyState_DIRECT_VALUE; +} + +//------------------------------------------------------------------------------ +void OControlModel::setPropertyToDefaultByHandle( sal_Int32 _nHandle) +{ + Any aDefault = getPropertyDefaultByHandle( _nHandle ); + + Any aConvertedValue, aOldValue; + if ( convertFastPropertyValue( aConvertedValue, aOldValue, _nHandle, aDefault ) ) + { + setFastPropertyValue_NoBroadcast( _nHandle, aConvertedValue ); + // TODO: fire the property change + } +} + +//------------------------------------------------------------------------------ +Any OControlModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const +{ + Any aReturn; + switch ( _nHandle ) + { + case PROPERTY_ID_NAME: + case PROPERTY_ID_TAG: + aReturn <<= ::rtl::OUString(); + break; + + case PROPERTY_ID_CLASSID: + aReturn <<= (sal_Int16)FormComponentType::CONTROL; + break; + + case PROPERTY_ID_TABINDEX: + aReturn <<= (sal_Int16)FRM_DEFAULT_TABINDEX; + break; + + case PROPERTY_ID_NATIVE_LOOK: + aReturn <<= (sal_Bool)sal_True; + break; + + default: + if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) ) + m_aPropertyBagHelper.getDynamicPropertyDefaultByHandle( _nHandle, aReturn ); + else + OSL_ENSURE( false, "OControlModel::convertFastPropertyValue: unknown handle!" ); + } + return aReturn; +} + +//------------------------------------------------------------------------------ +void OControlModel::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const +{ + switch ( _nHandle ) + { + case PROPERTY_ID_NAME: + _rValue <<= m_aName; + break; + case PROPERTY_ID_TAG: + _rValue <<= m_aTag; + break; + case PROPERTY_ID_CLASSID: + _rValue <<= m_nClassId; + break; + case PROPERTY_ID_TABINDEX: + _rValue <<= m_nTabIndex; + break; + case PROPERTY_ID_NATIVE_LOOK: + _rValue <<= (sal_Bool)m_bNativeLook; + break; + default: + if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) ) + m_aPropertyBagHelper.getDynamicFastPropertyValue( _nHandle, _rValue ); + else + OPropertySetAggregationHelper::getFastPropertyValue( _rValue, _nHandle ); + break; + } +} + +//------------------------------------------------------------------------------ +sal_Bool OControlModel::convertFastPropertyValue( + Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue) + throw (com::sun::star::lang::IllegalArgumentException) +{ + sal_Bool bModified(sal_False); + switch (_nHandle) + { + case PROPERTY_ID_NAME: + bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aName); + break; + case PROPERTY_ID_TAG: + bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aTag); + break; + case PROPERTY_ID_TABINDEX: + bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_nTabIndex); + break; + case PROPERTY_ID_NATIVE_LOOK: + bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bNativeLook); + break; + default: + if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) ) + bModified = m_aPropertyBagHelper.convertDynamicFastPropertyValue( _nHandle, _rValue, _rConvertedValue, _rOldValue ); + else + OSL_ENSURE( false, "OControlModel::convertFastPropertyValue: unknown handle!" ); + break; + } + return bModified; +} + +//------------------------------------------------------------------------------ +void OControlModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue) + throw (Exception) +{ + switch (_nHandle) + { + case PROPERTY_ID_NAME: + DBG_ASSERT(_rValue.getValueType() == getCppuType((const ::rtl::OUString*)NULL), + "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" ); + _rValue >>= m_aName; + break; + case PROPERTY_ID_TAG: + DBG_ASSERT(_rValue.getValueType() == getCppuType((const ::rtl::OUString*)NULL), + "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" ); + _rValue >>= m_aTag; + break; + case PROPERTY_ID_TABINDEX: + DBG_ASSERT(_rValue.getValueType() == getCppuType((const sal_Int16*)NULL), + "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" ); + _rValue >>= m_nTabIndex; + break; + case PROPERTY_ID_NATIVE_LOOK: + OSL_VERIFY( _rValue >>= m_bNativeLook ); + break; + default: + if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) ) + m_aPropertyBagHelper.setDynamicFastPropertyValue( _nHandle, _rValue ); + else + OSL_ENSURE( false, "OControlModel::setFastPropertyValue_NoBroadcast: unknown handle!" ); + break; + } +} + +//------------------------------------------------------------------------------ +void OControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const +{ + BEGIN_DESCRIBE_BASE_PROPERTIES( 4 ) + DECL_PROP2 (CLASSID, sal_Int16, READONLY, TRANSIENT); + DECL_PROP1 (NAME, ::rtl::OUString, BOUND); + DECL_BOOL_PROP2 (NATIVE_LOOK, BOUND, TRANSIENT); + DECL_PROP1 (TAG, ::rtl::OUString, BOUND); + END_DESCRIBE_PROPERTIES() +} + +//------------------------------------------------------------------------------ +void OControlModel::describeAggregateProperties( Sequence< Property >& /* [out] */ _rAggregateProps ) const +{ + if ( m_xAggregateSet.is() ) + { + Reference< XPropertySetInfo > xPSI( m_xAggregateSet->getPropertySetInfo() ); + if ( xPSI.is() ) + _rAggregateProps = xPSI->getProperties(); + } +} + +//------------------------------------------------------------------------------ +::osl::Mutex& OControlModel::getMutex() +{ + return m_aMutex; +} + +//------------------------------------------------------------------------------ +void OControlModel::describeFixedAndAggregateProperties( Sequence< Property >& _out_rFixedProperties, Sequence< Property >& _out_rAggregateProperties ) const +{ + describeFixedProperties( _out_rFixedProperties ); + describeAggregateProperties( _out_rAggregateProperties ); +} + +//------------------------------------------------------------------------------ +Reference< XMultiPropertySet > OControlModel::getPropertiesInterface() +{ + return Reference< XMultiPropertySet >( *this, UNO_QUERY ); +} + +//------------------------------------------------------------------------------ +Reference< XPropertySetInfo> SAL_CALL OControlModel::getPropertySetInfo() throw( RuntimeException) +{ + return createPropertySetInfo( getInfoHelper() ); +} + +//------------------------------------------------------------------------------ +::cppu::IPropertyArrayHelper& OControlModel::getInfoHelper() +{ + return m_aPropertyBagHelper.getInfoHelper(); +} + +//-------------------------------------------------------------------- +void SAL_CALL OControlModel::addProperty( const ::rtl::OUString& _rName, ::sal_Int16 _nAttributes, const Any& _rInitialValue ) throw (PropertyExistException, IllegalTypeException, IllegalArgumentException, RuntimeException) +{ + m_aPropertyBagHelper.addProperty( _rName, _nAttributes, _rInitialValue ); +} + +//-------------------------------------------------------------------- +void SAL_CALL OControlModel::removeProperty( const ::rtl::OUString& _rName ) throw (UnknownPropertyException, NotRemoveableException, RuntimeException) +{ + m_aPropertyBagHelper.removeProperty( _rName ); +} + +//-------------------------------------------------------------------- +Sequence< PropertyValue > SAL_CALL OControlModel::getPropertyValues() throw (RuntimeException) +{ + return m_aPropertyBagHelper.getPropertyValues(); +} + +//-------------------------------------------------------------------- +void SAL_CALL OControlModel::setPropertyValues( const Sequence< PropertyValue >& _rProps ) throw (UnknownPropertyException, PropertyVetoException, IllegalArgumentException, WrappedTargetException, RuntimeException) +{ + m_aPropertyBagHelper.setPropertyValues( _rProps ); +} + +//-------------------------------------------------------------------- +void OControlModel::lockInstance( LockAccess ) +{ + m_aMutex.acquire(); + osl_incrementInterlockedCount( &m_lockCount ); +} + +//-------------------------------------------------------------------- +oslInterlockedCount OControlModel::unlockInstance( LockAccess ) +{ + OSL_ENSURE( m_lockCount > 0, "OControlModel::unlockInstance: not locked!" ); + oslInterlockedCount lockCount = osl_decrementInterlockedCount( &m_lockCount ); + m_aMutex.release(); + return lockCount; +} + +//-------------------------------------------------------------------- +void OControlModel::firePropertyChanges( const Sequence< sal_Int32 >& _rHandles, const Sequence< Any >& _rOldValues, + const Sequence< Any >& _rNewValues, LockAccess ) +{ + OPropertySetHelper::fire( + const_cast< Sequence< sal_Int32 >& >( _rHandles ).getArray(), + _rNewValues.getConstArray(), + _rOldValues.getConstArray(), + _rHandles.getLength(), + sal_False + ); +} + +//================================================================== +//= OBoundControlModel +//================================================================== +DBG_NAME(frm_OBoundControlModel); +//------------------------------------------------------------------ +Any SAL_CALL OBoundControlModel::queryAggregation( const Type& _rType ) throw (RuntimeException) +{ + Any aReturn( OControlModel::queryAggregation(_rType) ); + if (!aReturn.hasValue()) + { + aReturn = OBoundControlModel_BASE1::queryInterface(_rType); + + if ( !aReturn.hasValue() && m_bCommitable ) + aReturn = OBoundControlModel_COMMITTING::queryInterface( _rType ); + + if ( !aReturn.hasValue() && m_bSupportsExternalBinding ) + aReturn = OBoundControlModel_BINDING::queryInterface( _rType ); + + if ( !aReturn.hasValue() && m_bSupportsValidation ) + aReturn = OBoundControlModel_VALIDATION::queryInterface( _rType ); + } + + return aReturn; +} + +//------------------------------------------------------------------ +OBoundControlModel::OBoundControlModel( + const Reference< XMultiServiceFactory>& _rxFactory, + const ::rtl::OUString& _rUnoControlModelTypeName, const ::rtl::OUString& _rDefault, + const sal_Bool _bCommitable, const sal_Bool _bSupportExternalBinding, const sal_Bool _bSupportsValidation ) + :OControlModel( _rxFactory, _rUnoControlModelTypeName, _rDefault, sal_False ) + ,OPropertyChangeListener( m_aMutex ) + ,m_xField() + ,m_xAmbientForm() + ,m_nValuePropertyAggregateHandle( -1 ) + ,m_nFieldType( DataType::OTHER ) + ,m_bValuePropertyMayBeVoid( false ) + ,m_aResetHelper( *this, m_aMutex ) + ,m_aUpdateListeners(m_aMutex) + ,m_aFormComponentListeners( m_aMutex ) + ,m_bInputRequired( sal_True ) + ,m_pAggPropMultiplexer( NULL ) + ,m_bFormListening( false ) + ,m_bLoaded(sal_False) + ,m_bRequired(sal_False) + ,m_bCommitable(_bCommitable) + ,m_bSupportsExternalBinding( _bSupportExternalBinding ) + ,m_bSupportsValidation( _bSupportsValidation ) + ,m_bForwardValueChanges(sal_True) + ,m_bTransferingValue( sal_False ) + ,m_bIsCurrentValueValid( sal_True ) + ,m_bBindingControlsRO( sal_False ) + ,m_bBindingControlsEnable( sal_False ) + ,m_eControlValueChangeInstigator( eOther ) + ,m_aLabelServiceName(FRM_SUN_COMPONENT_FIXEDTEXT) +{ + DBG_CTOR(frm_OBoundControlModel, NULL); + + // start property listening at the aggregate + implInitAggMultiplexer( ); +} + +//------------------------------------------------------------------ +OBoundControlModel::OBoundControlModel( + const OBoundControlModel* _pOriginal, const Reference< XMultiServiceFactory>& _rxFactory ) + :OControlModel( _pOriginal, _rxFactory, sal_True, sal_False ) + ,OPropertyChangeListener( m_aMutex ) + ,m_xField() + ,m_xAmbientForm() + ,m_nValuePropertyAggregateHandle( _pOriginal->m_nValuePropertyAggregateHandle ) + ,m_nFieldType( DataType::OTHER ) + ,m_bValuePropertyMayBeVoid( _pOriginal->m_bValuePropertyMayBeVoid ) + ,m_aResetHelper( *this, m_aMutex ) + ,m_aUpdateListeners( m_aMutex ) + ,m_aFormComponentListeners( m_aMutex ) + ,m_xValidator( _pOriginal->m_xValidator ) + ,m_bInputRequired( sal_True ) + ,m_pAggPropMultiplexer( NULL ) + ,m_bFormListening( false ) + ,m_bLoaded( sal_False ) + ,m_bRequired( sal_False ) + ,m_bCommitable( _pOriginal->m_bCommitable ) + ,m_bSupportsExternalBinding( _pOriginal->m_bSupportsExternalBinding ) + ,m_bSupportsValidation( _pOriginal->m_bSupportsValidation ) + ,m_bForwardValueChanges( sal_True ) + ,m_bTransferingValue( sal_False ) + ,m_bIsCurrentValueValid( _pOriginal->m_bIsCurrentValueValid ) + ,m_bBindingControlsRO( sal_False ) + ,m_bBindingControlsEnable( sal_False ) + ,m_eControlValueChangeInstigator( eOther ) +{ + DBG_CTOR(frm_OBoundControlModel, NULL); + + // start property listening at the aggregate + implInitAggMultiplexer( ); + + m_aLabelServiceName = _pOriginal->m_aLabelServiceName; + m_sValuePropertyName = _pOriginal->m_sValuePropertyName; + m_nValuePropertyAggregateHandle = _pOriginal->m_nValuePropertyAggregateHandle; + m_bValuePropertyMayBeVoid = _pOriginal->m_bValuePropertyMayBeVoid; + m_aValuePropertyType = _pOriginal->m_aValuePropertyType; + m_aControlSource = _pOriginal->m_aControlSource; + m_bInputRequired = _pOriginal->m_bInputRequired; + // 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) + + // 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() ) + { + try + { + setValueBinding( pBoundOriginal->m_xExternalBinding ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + } + } +} + +//----------------------------------------------------------------------------- +void OBoundControlModel::implInitAggMultiplexer( ) +{ + increment( m_refCount ); + if ( m_xAggregateSet.is() ) + { + m_pAggPropMultiplexer = new OPropertyChangeMultiplexer( this, m_xAggregateSet, sal_False ); + m_pAggPropMultiplexer->acquire(); + } + decrement( m_refCount ); + + doSetDelegator(); +} + +//----------------------------------------------------------------------------- +void OBoundControlModel::implInitValuePropertyListening( ) const +{ + // start listening for changes at the value property + // There are three 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 support external validation. In this case, we need to listen for changes in the value + // property, since we need to revalidate then. + // 3. 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_bSupportsValidation || !m_bCommitable ) + { + OSL_ENSURE( m_pAggPropMultiplexer, "OBoundControlModel::implInitValuePropertyListening: no multiplexer!" ); + if ( m_pAggPropMultiplexer && m_sValuePropertyName.getLength() ) + m_pAggPropMultiplexer->addProperty( m_sValuePropertyName ); + } +} + +//----------------------------------------------------------------------------- +void OBoundControlModel::initOwnValueProperty( const ::rtl::OUString& i_rValuePropertyName ) +{ + OSL_PRECOND( !m_sValuePropertyName.getLength() && -1 == m_nValuePropertyAggregateHandle, + "OBoundControlModel::initOwnValueProperty: value property is already initialized!" ); + OSL_ENSURE( i_rValuePropertyName.getLength(), "OBoundControlModel::initOwnValueProperty: invalid property name!" ); + m_sValuePropertyName = i_rValuePropertyName; +} + +//----------------------------------------------------------------------------- +void OBoundControlModel::initValueProperty( const ::rtl::OUString& _rValuePropertyName, sal_Int32 _nValuePropertyExternalHandle ) +{ + OSL_PRECOND( !m_sValuePropertyName.getLength() && -1 == m_nValuePropertyAggregateHandle, + "OBoundControlModel::initValueProperty: value property is already initialized!" ); + 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!" ); + + if ( m_nValuePropertyAggregateHandle != -1 ) + { + Reference< XPropertySetInfo > xPropInfo( m_xAggregateSet->getPropertySetInfo(), UNO_SET_THROW ); + Property aValuePropDesc = xPropInfo->getPropertyByName( m_sValuePropertyName ); + m_aValuePropertyType = aValuePropDesc.Type; + m_bValuePropertyMayBeVoid = ( aValuePropDesc.Attributes & PropertyAttribute::MAYBEVOID ) != 0; + } + + // 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(); +} + +//----------------------------------------------------------------------------- +Sequence< Type > OBoundControlModel::_getTypes() +{ + TypeBag aTypes( + OControlModel::_getTypes(), + OBoundControlModel_BASE1::getTypes() + ); + + if ( m_bCommitable ) + aTypes.addTypes( OBoundControlModel_COMMITTING::getTypes() ); + + if ( m_bSupportsExternalBinding ) + aTypes.addTypes( OBoundControlModel_BINDING::getTypes() ); + + if ( m_bSupportsValidation ) + aTypes.addTypes( OBoundControlModel_VALIDATION::getTypes() ); + + return aTypes.getTypes(); +} + +// OComponentHelper +//----------------------------------------------------------------------------- +void OBoundControlModel::disposing() +{ + OControlModel::disposing(); + + ::osl::ClearableMutexGuard aGuard(m_aMutex); + + if ( m_pAggPropMultiplexer ) + m_pAggPropMultiplexer->dispose(); + + // notify all our listeners + com::sun::star::lang::EventObject aEvt( static_cast< XWeak* >( this ) ); + m_aUpdateListeners.disposeAndClear( aEvt ); + m_aResetHelper.disposing(); + + // disconnect from our database column + // TODO: could we replace the following 5 lines with a call to impl_disconnectDatabaseColumn_noNotify? + // 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 ( hasField() ) + { + getField()->removePropertyChangeListener( PROPERTY_VALUE, this ); + resetField(); + } + m_xCursor = NULL; + + Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY ); + if ( xComp.is() ) + xComp->removeEventListener(static_cast< XEventListener* >( static_cast< XPropertyChangeListener* >( this ) ) ); + + // disconnect from our external value binding + if ( hasExternalValueBinding() ) + disconnectExternalValueBinding(); + + // dito for the validator + if ( hasValidator() ) + disconnectValidator( ); +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::onValuePropertyChange( ControlModelLock& i_rControLock ) +{ + if ( hasExternalValueBinding() ) + { // the control value changed, while we have an external value binding + // -> forward the value to it + if ( m_eControlValueChangeInstigator != eExternalBinding ) + transferControlValueToExternal( i_rControLock ); + } + else if ( !m_bCommitable && m_xColumnUpdate.is() ) + { // the control value changed, while we 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 + if ( m_eControlValueChangeInstigator != eDbColumnBinding ) + commitControlValueToDbColumn( false ); + } + + // validate the new value + if ( m_bSupportsValidation ) + recheckValidity( true ); +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::_propertyChanged( const PropertyChangeEvent& _rEvt ) throw ( RuntimeException ) +{ + ControlModelLock aLock( *this ); + + 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 ) + { + onValuePropertyChange( aLock ); + } +} + +//------------------------------------------------------------------------------ +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::doFormListening( const bool _bStart ) +{ + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::doFormListening: external value binding should overrule the database binding!" ); + + if ( isFormListening() == _bStart ) + return; + + if ( m_xAmbientForm.is() ) + _bStart ? m_xAmbientForm->addLoadListener( this ) : m_xAmbientForm->removeLoadListener( this ); + + Reference< XLoadable > xParentLoadable( getParent(), UNO_QUERY ); + if ( getParent().is() && !xParentLoadable.is() ) + { + // if our parent does not directly support the XLoadable interface, then it might support the + // XRowSetSupplier/XRowSetChangeBroadcaster interfaces. In this case we have to listen for changes + // broadcasted by the latter. + Reference< XRowSetChangeBroadcaster > xRowSetBroadcaster( getParent(), UNO_QUERY ); + if ( xRowSetBroadcaster.is() ) + _bStart ? xRowSetBroadcaster->addRowSetChangeListener( this ) : xRowSetBroadcaster->removeRowSetChangeListener( this ); + } + + m_bFormListening = _bStart && m_xAmbientForm.is(); +} + +// XChild +//------------------------------------------------------------------------------ +void SAL_CALL OBoundControlModel::setParent(const Reference<XInterface>& _rxParent) throw(com::sun::star::lang::NoSupportException, RuntimeException) +{ + ControlModelLock aLock( *this ); + FieldChangeNotifier aBoundFieldNotifier( aLock ); + + if ( getParent() == _rxParent ) + return; + + // disconnect from database column (which is controlled by parent, directly or indirectly) + if ( hasField() ) + impl_disconnectDatabaseColumn_noNotify(); + + // log off old listeners + if ( isFormListening() ) + doFormListening( false ); + + // actually set the new parent + OControlModel::setParent( _rxParent ); + + // a new parent means a new ambient form + impl_determineAmbientForm_nothrow(); + + if ( !hasExternalValueBinding() ) + { + // log on new listeners + doFormListening( true ); + + // re-connect to database column of the new parent + if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() ) + impl_connectDatabaseColumn_noNotify( false ); + } +} + +// XEventListener +//------------------------------------------------------------------------------ +void SAL_CALL OBoundControlModel::disposing(const com::sun::star::lang::EventObject& _rEvent) throw (RuntimeException) +{ + ControlModelLock aLock( *this ); + + if ( _rEvent.Source == getField() ) + { + resetField(); + } + else if ( _rEvent.Source == m_xLabelControl ) + { + Reference<XPropertySet> xOldValue = m_xLabelControl; + m_xLabelControl = NULL; + + // fire a propertyChanged (when we leave aLock's scope) + aLock.addPropertyNotification( PROPERTY_ID_CONTROLLABEL, makeAny( xOldValue ), makeAny( m_xLabelControl ) ); + } + else if ( _rEvent.Source == m_xExternalBinding ) + { // *first* check for the external binding + disconnectExternalValueBinding( ); + } + else if ( _rEvent.Source == m_xValidator ) + { // *then* check for the validator. Reason is that bindings may also act as validator at the same + // time, in this case, the validator is automatically revoked when the binding is revoked + disconnectValidator( ); + } + else + OControlModel::disposing(_rEvent); +} + +// XServiceInfo +//------------------------------------------------------------------------------ +StringSequence SAL_CALL OBoundControlModel::getSupportedServiceNames() throw(RuntimeException) +{ + return ::comphelper::concatSequences( + getAggregateServiceNames(), + getSupportedServiceNames_Static() + ); +} + +//------------------------------------------------------------------------------ +Sequence< ::rtl::OUString > SAL_CALL OBoundControlModel::getSupportedServiceNames_Static() throw( RuntimeException ) +{ + Sequence< ::rtl::OUString > aOwnServiceNames( 1 ); + aOwnServiceNames[ 0 ] = ::rtl::OUString::createFromAscii( "com.sun.star.form.DataAwareControlModel" ); + + return ::comphelper::concatSequences( + OControlModel::getSupportedServiceNames_Static(), + aOwnServiceNames + ); +} + +// XPersist +//------------------------------------------------------------------------------ +void SAL_CALL OBoundControlModel::write( const Reference<stario::XObjectOutputStream>& _rxOutStream ) throw(stario::IOException, RuntimeException) +{ + OControlModel::write(_rxOutStream); + + osl::MutexGuard aGuard(m_aMutex); + + // Version + _rxOutStream->writeShort(0x0002); + + // Controlsource + ::comphelper::operator<<( _rxOutStream, m_aControlSource); + + // !!! IMPORTANT NOTE !!! + // don't write any new members here : this wouldn't be compatible with older versions, as OBoundControlModel + // is a base class which is called in derived classes "read" method. So if you increment the version + // and write new stuff, older office versions will read this in the _derived_ classes, which may result + // in anything from data loss to crash. + // (use writeCommonProperties instead, this is called in derived classes write-method) + // !!! EOIN !!! + // FS - 68876 - 28.09.1999 +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::defaultCommonProperties() +{ + 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))); + m_xLabelControl = NULL; +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::readCommonProperties(const Reference<stario::XObjectInputStream>& _rxInStream) +{ + sal_Int32 nLen = _rxInStream->readLong(); + + Reference<stario::XMarkableStream> xMark(_rxInStream, UNO_QUERY); + DBG_ASSERT(xMark.is(), "OBoundControlModel::readCommonProperties : can only work with markable streams !"); + sal_Int32 nMark = xMark->createMark(); + + // read the reference to the label control + Reference<stario::XPersistObject> xPersist; + sal_Int32 nUsedFlag; + nUsedFlag = _rxInStream->readLong(); + if (nUsedFlag) + xPersist = _rxInStream->readObject(); + m_xLabelControl = m_xLabelControl.query( xPersist ); + Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY ); + if (xComp.is()) + xComp->addEventListener(static_cast<com::sun::star::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this))); + + // read any other new common properties here + + // skip the remaining bytes + xMark->jumpToMark(nMark); + _rxInStream->skipBytes(nLen); + xMark->deleteMark(nMark); +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::writeCommonProperties(const Reference<stario::XObjectOutputStream>& _rxOutStream) +{ + Reference<stario::XMarkableStream> xMark(_rxOutStream, UNO_QUERY); + DBG_ASSERT(xMark.is(), "OBoundControlModel::writeCommonProperties : can only work with markable streams !"); + sal_Int32 nMark = xMark->createMark(); + + // a placeholder where we will write the overall length (later in this method) + sal_Int32 nLen = 0; + _rxOutStream->writeLong(nLen); + + // write the reference to the label control + Reference<stario::XPersistObject> xPersist(m_xLabelControl, UNO_QUERY); + sal_Int32 nUsedFlag = 0; + if (xPersist.is()) + nUsedFlag = 1; + _rxOutStream->writeLong(nUsedFlag); + if (xPersist.is()) + _rxOutStream->writeObject(xPersist); + + // write any other new common properties here + + // write the correct length at the beginning of the block + nLen = xMark->offsetToMark(nMark) - sizeof(nLen); + xMark->jumpToMark(nMark); + _rxOutStream->writeLong(nLen); + xMark->jumpToFurthest(); + xMark->deleteMark(nMark); +} + +//------------------------------------------------------------------------------ +void SAL_CALL OBoundControlModel::read( const Reference< stario::XObjectInputStream >& _rxInStream ) throw(stario::IOException, RuntimeException) +{ + OControlModel::read(_rxInStream); + + osl::MutexGuard aGuard(m_aMutex); + UINT16 nVersion = _rxInStream->readShort(); (void)nVersion; + ::comphelper::operator>>( _rxInStream, m_aControlSource); +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const +{ + switch (nHandle) + { + case PROPERTY_ID_INPUT_REQUIRED: + rValue <<= m_bInputRequired; + break; + case PROPERTY_ID_CONTROLSOURCEPROPERTY: + rValue <<= m_sValuePropertyName; + break; + case PROPERTY_ID_CONTROLSOURCE: + rValue <<= m_aControlSource; + break; + case PROPERTY_ID_BOUNDFIELD: + rValue <<= getField(); + break; + case PROPERTY_ID_CONTROLLABEL: + if (!m_xLabelControl.is()) + rValue.clear(); + else + rValue <<= m_xLabelControl; + break; + default: + OControlModel::getFastPropertyValue(rValue, nHandle); + } +} + +//------------------------------------------------------------------------------ +sal_Bool OBoundControlModel::convertFastPropertyValue( + Any& _rConvertedValue, Any& _rOldValue, + sal_Int32 _nHandle, + const Any& _rValue) + throw (com::sun::star::lang::IllegalArgumentException) +{ + sal_Bool bModified(sal_False); + switch (_nHandle) + { + case PROPERTY_ID_INPUT_REQUIRED: + bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_bInputRequired ); + break; + case PROPERTY_ID_CONTROLSOURCE: + bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aControlSource); + break; + case PROPERTY_ID_BOUNDFIELD: + DBG_ERROR( "OBoundControlModel::convertFastPropertyValue: BoundField should be a read-only property !" ); + throw com::sun::star::lang::IllegalArgumentException(); + case PROPERTY_ID_CONTROLLABEL: + if (!_rValue.hasValue()) + { // property set to void + _rConvertedValue = Any(); + getFastPropertyValue(_rOldValue, _nHandle); + bModified = m_xLabelControl.is(); + } + else + { + bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_xLabelControl); + if (!m_xLabelControl.is()) + // an empty interface is interpreted as VOID + _rOldValue.clear(); + } + break; + default: + bModified = OControlModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue); + } + return bModified; +} + +//------------------------------------------------------------------------------ +Any OBoundControlModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const +{ + Any aDefault; + switch ( _nHandle ) + { + case PROPERTY_ID_INPUT_REQUIRED: + aDefault <<= sal_Bool( sal_True ); + break; + + case PROPERTY_ID_CONTROLSOURCE: + aDefault <<= ::rtl::OUString(); + break; + + case PROPERTY_ID_CONTROLLABEL: + aDefault <<= Reference< XPropertySet >(); + break; + } + return aDefault; +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue ) throw (Exception) +{ + switch (nHandle) + { + case PROPERTY_ID_INPUT_REQUIRED: + OSL_VERIFY( rValue >>= m_bInputRequired ); + break; + case PROPERTY_ID_CONTROLSOURCE: + OSL_VERIFY( rValue >>= m_aControlSource ); + break; + case PROPERTY_ID_BOUNDFIELD: + DBG_ERROR("OBoundControlModel::setFastPropertyValue_NoBroadcast : BoundField should be a read-only property !"); + throw com::sun::star::lang::IllegalArgumentException(); + case PROPERTY_ID_CONTROLLABEL: + { + if ( rValue.hasValue() && ( rValue.getValueTypeClass() != TypeClass_INTERFACE ) ) + throw com::sun::star::lang::IllegalArgumentException(); + + Reference< XInterface > xNewValue( rValue, UNO_QUERY ); + if ( !xNewValue.is() ) + { // set property to "void" + Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY ); + if ( xComp.is() ) + xComp->removeEventListener( static_cast< XPropertyChangeListener* >( this ) ); + m_xLabelControl = NULL; + break; + } + + Reference< XControlModel > xAsModel ( xNewValue, UNO_QUERY ); + Reference< XServiceInfo > xAsServiceInfo ( xAsModel, UNO_QUERY ); + Reference< XPropertySet > xAsPropSet ( xAsServiceInfo, UNO_QUERY ); + Reference< XChild > xAsChild ( xAsPropSet, UNO_QUERY ); + if ( !xAsChild.is() || !xAsServiceInfo->supportsService( m_aLabelServiceName ) ) + { + throw com::sun::star::lang::IllegalArgumentException(); + } + + // check if weself and the given model have a common anchestor (up to the forms collection) + Reference<XChild> xCont; + query_interface(static_cast<XWeak*>(this), xCont); + Reference< XInterface > xMyTopLevel = xCont->getParent(); + while (xMyTopLevel.is()) + { + Reference<XForm> xAsForm(xMyTopLevel, UNO_QUERY); + if (!xAsForm.is()) + // found my root + break; + + Reference<XChild> xLoopAsChild(xMyTopLevel, UNO_QUERY); + xMyTopLevel = xLoopAsChild.is() ? xLoopAsChild->getParent() : Reference< XInterface >(); + } + Reference< XInterface > xNewTopLevel = xAsChild->getParent(); + while (xNewTopLevel.is()) + { + Reference<XForm> xAsForm(xNewTopLevel, UNO_QUERY); + if (!xAsForm.is()) + break; + + Reference<XChild> xLoopAsChild(xNewTopLevel, UNO_QUERY); + xNewTopLevel = xLoopAsChild.is() ? xLoopAsChild->getParent() : Reference< XInterface >(); + } + if (xNewTopLevel != xMyTopLevel) + { + // the both objects don't belong to the same forms collection -> not acceptable + throw com::sun::star::lang::IllegalArgumentException(); + } + + m_xLabelControl = xAsPropSet; + Reference<com::sun::star::lang::XComponent> xComp(m_xLabelControl, UNO_QUERY); + if (xComp.is()) + xComp->addEventListener(static_cast<com::sun::star::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this))); + } + break; + default: + OControlModel::setFastPropertyValue_NoBroadcast(nHandle, rValue ); + } +} + +// XPropertyChangeListener +//------------------------------------------------------------------------------ +void SAL_CALL OBoundControlModel::propertyChange( const PropertyChangeEvent& evt ) throw(RuntimeException) +{ + // if the DBColumn value changed, transfer it to the control + if ( evt.PropertyName.equals( PROPERTY_VALUE ) ) + { + OSL_ENSURE( evt.Source == getField(), "OBoundControlModel::propertyChange: value changes from components other than our database colum?" ); + osl::MutexGuard aGuard(m_aMutex); + if ( m_bForwardValueChanges && m_xColumn.is() ) + transferDbValueToControl(); + } + else + { + OSL_ENSURE( evt.Source == m_xExternalBinding, "OBoundControlModel::propertyChange: where did this come from?" ); + + // our binding has properties which can control properties of ourself + ::rtl::OUString sBindingControlledProperty; + bool bForwardToLabelControl = false; + if ( evt.PropertyName.equals( PROPERTY_READONLY ) ) + { + sBindingControlledProperty = PROPERTY_READONLY; + } + else if ( evt.PropertyName.equals( PROPERTY_RELEVANT ) ) + { + sBindingControlledProperty = PROPERTY_ENABLED; + bForwardToLabelControl = true; + } + else + return; + + try + { + setPropertyValue( sBindingControlledProperty, evt.NewValue ); + if ( bForwardToLabelControl && m_xLabelControl.is() ) + m_xLabelControl->setPropertyValue( sBindingControlledProperty, evt.NewValue ); + } + catch( const Exception& ) + { + OSL_ENSURE( sal_False, "OBoundControlModel::propertyChange: could not adjust my binding-controlled property!" ); + } + } +} + +//------------------------------------------------------------------------------ +void SAL_CALL OBoundControlModel::onRowSetChanged( const EventObject& /*i_Event*/ ) throw (RuntimeException) +{ + ControlModelLock aLock( *this ); + FieldChangeNotifier aBoundFieldNotifier( aLock ); + + // disconnect from database column (which is controlled by parent, directly or indirectly) + if ( hasField() ) + impl_disconnectDatabaseColumn_noNotify(); + + // log off old listeners + if ( isFormListening() ) + doFormListening( false ); + + // determine the new ambient form + impl_determineAmbientForm_nothrow(); + + // log on new listeners + doFormListening( true ); + + // re-connect to database column if needed and possible + if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() ) + impl_connectDatabaseColumn_noNotify( false ); +} + +// XBoundComponent +//------------------------------------------------------------------------------ +void SAL_CALL OBoundControlModel::addUpdateListener(const Reference<XUpdateListener>& _rxListener) throw(RuntimeException) +{ + m_aUpdateListeners.addInterface(_rxListener); +} + +//------------------------------------------------------------------------------ +void SAL_CALL OBoundControlModel::removeUpdateListener(const Reference< XUpdateListener>& _rxListener) throw(RuntimeException) +{ + m_aUpdateListeners.removeInterface(_rxListener); +} + +//------------------------------------------------------------------------------ +sal_Bool SAL_CALL OBoundControlModel::commit() throw(RuntimeException) +{ + ControlModelLock aLock( *this ); + + 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( aLock ); + return sal_True; + } + + OSL_ENSURE( !hasExternalValueBinding(), "OBoundControlModel::commit: control flow broken!" ); + // we reach this only if we're not working with an external binding + + if ( !hasField() ) + return sal_True; + + ::cppu::OInterfaceIteratorHelper aIter( m_aUpdateListeners ); + EventObject aEvent; + aEvent.Source = static_cast< XWeak* >( this ); + sal_Bool bSuccess = sal_True; + + aLock.release(); + // >>>>>>>> ----- UNSAFE ----- >>>>>>>> + while (aIter.hasMoreElements() && bSuccess) + bSuccess = static_cast< XUpdateListener* >( aIter.next() )->approveUpdate( aEvent ); + // <<<<<<<< ----- UNSAFE ----- <<<<<<<< + aLock.acquire(); + + if ( bSuccess ) + { + try + { + if ( m_xColumnUpdate.is() ) + bSuccess = commitControlValueToDbColumn( sal_False ); + } + catch(Exception&) + { + bSuccess = sal_False; + } + } + + if ( bSuccess ) + { + aLock.release(); + m_aUpdateListeners.notifyEach( &XUpdateListener::updated, aEvent ); + } + + return bSuccess; +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::resetField() +{ + m_xColumnUpdate.clear(); + m_xColumn.clear(); + m_xField.clear(); + m_nFieldType = DataType::OTHER; +} + +//------------------------------------------------------------------------------ +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()) + { + // Feld bestimmen und PropertyChangeListener + m_xCursor = rForm; + Reference<XPropertySet> xFieldCandidate; + + if (m_xCursor.is()) + { + Reference<XColumnsSupplier> xColumnsSupplier(m_xCursor, UNO_QUERY); + DBG_ASSERT(xColumnsSupplier.is(), "OBoundControlModel::connectToField : the row set should support the com::sun::star::sdb::ResultSet service !"); + if (xColumnsSupplier.is()) + { + Reference<XNameAccess> xColumns(xColumnsSupplier->getColumns(), UNO_QUERY); + if (xColumns.is() && xColumns->hasByName(m_aControlSource)) + { + OSL_VERIFY( xColumns->getByName(m_aControlSource) >>= xFieldCandidate ); + } + } + } + + try + { + sal_Int32 nFieldType = DataType::OTHER; + if ( xFieldCandidate.is() ) + { + xFieldCandidate->getPropertyValue( PROPERTY_FIELDTYPE ) >>= nFieldType; + if ( approveDbColumnType( nFieldType ) ) + impl_setField_noNotify( xFieldCandidate ); + } + else + impl_setField_noNotify( NULL ); + + if ( m_xField.is() ) + { + if( m_xField->getPropertySetInfo()->hasPropertyByName( PROPERTY_VALUE ) ) + { + m_nFieldType = nFieldType; + + // 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 = 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 .... + } + else + { + OSL_ENSURE(sal_False, "OBoundControlModel::connectToField: property NAME not supported!"); + impl_setField_noNotify( NULL ); + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + resetField(); + } + } + return hasField(); +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::initFromField( const Reference< XRowSet >& _rxRowSet ) +{ + // but only if the rowset if posisitioned on a valid record + if ( hasField() && _rxRowSet.is() ) + { + if ( !_rxRowSet->isBeforeFirst() && !_rxRowSet->isAfterLast() ) + transferDbValueToControl(); + else + // reset the field if the row set is empty + // #i30661# / 2004-12-16 / frank.schoenheit@sun.com + resetNoBroadcast(); + } +} + +//------------------------------------------------------------------------------ +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) + || (_nColumnType == DataType::STRUCT) || (_nColumnType == DataType::ARRAY) + || (_nColumnType == DataType::BLOB) /*|| (_nColumnType == DataType::CLOB)*/ + || (_nColumnType == DataType::REF) || (_nColumnType == DataType::SQLNULL)) + return sal_False; + + return sal_True; +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::impl_determineAmbientForm_nothrow() +{ + Reference< XInterface > xParent( const_cast< OBoundControlModel* >( this )->getParent() ); + + m_xAmbientForm.set( xParent, UNO_QUERY ); + if ( !m_xAmbientForm.is() ) + { + Reference< XRowSetSupplier > xSupRowSet( xParent, UNO_QUERY ); + if ( xSupRowSet.is() ) + m_xAmbientForm.set( xSupRowSet->getRowSet(), UNO_QUERY ); + } +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::impl_connectDatabaseColumn_noNotify( bool _bFromReload ) +{ + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::impl_connectDatabaseColumn_noNotify: not to be called with an external value binding!" ); + + // consistency checks + DBG_ASSERT( !( hasField() && !_bFromReload ), + "OBoundControlModel::impl_connectDatabaseColumn_noNotify: the form is just *loaded*, but we already have a field!" ); + (void)_bFromReload; + + Reference< XRowSet > xRowSet( m_xAmbientForm, UNO_QUERY ); + OSL_ENSURE( xRowSet.is(), "OBoundControlModel::impl_connectDatabaseColumn_noNotify: no row set!" ); + if ( !xRowSet.is() ) + return; + + if ( !hasField() ) + { + // connect to the column + connectToField( xRowSet ); + } + + // 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( xRowSet ); + + // initially transfer the db column value to the control, if we successfully connected to a database column + if ( hasField() ) + initFromField( xRowSet ); +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::impl_disconnectDatabaseColumn_noNotify() +{ + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::impl_disconnectDatabaseColumn_noNotify: not to be called with an external value binding!" ); + + // let derived classes react on this + onDisconnectedDbColumn(); + + if ( hasField() ) + { + getField()->removePropertyChangeListener( PROPERTY_VALUE, this ); + resetField(); + } + + m_xCursor = NULL; + m_bLoaded = sal_False; +} + +//============================================================================== +// XLoadListener +//------------------------------------------------------------------------------ +void SAL_CALL OBoundControlModel::loaded( const EventObject& _rEvent ) throw(RuntimeException) +{ + ControlModelLock aLock( *this ); + FieldChangeNotifier aBoundFieldNotifier( aLock ); + + OSL_ENSURE( _rEvent.Source == m_xAmbientForm, "OBoundControlModel::loaded: where does this come from?" ); + (void)_rEvent; + + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::loaded: we should never reach this with an external value binding!" ); + if ( hasExternalValueBinding() ) + return; + + impl_connectDatabaseColumn_noNotify( 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; +} + +//------------------------------------------------------------------------------ +void SAL_CALL OBoundControlModel::unloading(const com::sun::star::lang::EventObject& /*aEvent*/) throw(RuntimeException) +{ + ControlModelLock aLock( *this ); + FieldChangeNotifier aBoundFieldNotifier( aLock ); + + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::unloading: we should never reach this with an external value binding!" ); + if ( hasExternalValueBinding() ) + return; + + impl_disconnectDatabaseColumn_noNotify(); +} + +//------------------------------------------------------------------------------ +void SAL_CALL OBoundControlModel::reloaded( const EventObject& _rEvent ) throw(RuntimeException) +{ + ControlModelLock aLock( *this ); + FieldChangeNotifier aBoundFieldNotifier( aLock ); + + OSL_ENSURE( _rEvent.Source == m_xAmbientForm, "OBoundControlModel::reloaded: where does this come from?" ); + (void)_rEvent; + + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::reloaded: we should never reach this with an external value binding!" ); + if ( hasExternalValueBinding() ) + return; + + impl_connectDatabaseColumn_noNotify( true ); +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::setControlValue( const Any& _rValue, ValueChangeInstigator _eInstigator ) +{ + m_eControlValueChangeInstigator = _eInstigator; + doSetControlValue( _rValue ); + m_eControlValueChangeInstigator = eOther; +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::doSetControlValue( const Any& _rValue ) +{ + OSL_PRECOND( m_xAggregateFastSet.is() && m_xAggregateSet.is(), + "OBoundControlModel::doSetControlValue: invalid aggregate !" ); + OSL_PRECOND( m_sValuePropertyName.getLength() || ( m_nValuePropertyAggregateHandle != -1 ), + "OBoundControlModel::doSetControlValue: please override if you have own value property handling!" ); + + try + { + // release our mutex once (it's acquired in one of the the calling methods), 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 + // #72451# / 2000-01-31 / frank.schoenheit@sun.com + 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 ); + } + } + catch( const Exception& ) + { + OSL_ENSURE( sal_False, "OBoundControlModel::doSetControlValue: caught an exception!" ); + } +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::onConnectedValidator( ) +{ + try + { + // if we have an external validator, we do not want the control to force invalid + // inputs to the default value. Instead, invalid inputs should be translated + // to NaN (not a number) + Reference< XPropertySetInfo > xAggregatePropertyInfo; + if ( m_xAggregateSet.is() ) + xAggregatePropertyInfo = m_xAggregateSet->getPropertySetInfo(); + if ( xAggregatePropertyInfo.is() && xAggregatePropertyInfo->hasPropertyByName( PROPERTY_ENFORCE_FORMAT ) ) + m_xAggregateSet->setPropertyValue( PROPERTY_ENFORCE_FORMAT, makeAny( (sal_Bool)sal_False ) ); + } + catch( const Exception& ) + { + OSL_ENSURE( sal_False, "OBoundControlModel::onConnectedValidator: caught an exception!" ); + } + recheckValidity( false ); +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::onDisconnectedValidator( ) +{ + try + { + Reference< XPropertySetInfo > xAggregatePropertyInfo; + if ( m_xAggregateSet.is() ) + xAggregatePropertyInfo = m_xAggregateSet->getPropertySetInfo(); + if ( xAggregatePropertyInfo.is() && xAggregatePropertyInfo->hasPropertyByName( PROPERTY_ENFORCE_FORMAT ) ) + m_xAggregateSet->setPropertyValue( PROPERTY_ENFORCE_FORMAT, makeAny( (sal_Bool)sal_True ) ); + } + catch( const Exception& ) + { + OSL_ENSURE( sal_False, "OBoundControlModel::onDisconnectedValidator: caught an exception!" ); + } + recheckValidity( false ); +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::onConnectedExternalValue( ) +{ + calculateExternalValueType(); +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::onDisconnectedExternalValue( ) +{ +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::onConnectedDbColumn( const Reference< XInterface >& /*_rxForm*/ ) +{ + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::onConnectedDbColumn: how this? There's an external value binding!" ); +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::onDisconnectedDbColumn() +{ + OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::onDisconnectedDbColumn: how this? There's an external value binding!" ); +} + +// XReset +//----------------------------------------------------------------------------- +Any OBoundControlModel::getDefaultForReset() const +{ + return Any(); +} + +//----------------------------------------------------------------------------- +void OBoundControlModel::resetNoBroadcast() +{ + setControlValue( getDefaultForReset(), eOther ); +} + +//----------------------------------------------------------------------------- +void OBoundControlModel::addResetListener(const Reference<XResetListener>& l) throw (RuntimeException) +{ + m_aResetHelper.addResetListener( l ); +} + +//----------------------------------------------------------------------------- +void OBoundControlModel::removeResetListener(const Reference<XResetListener>& l) throw (RuntimeException) +{ + m_aResetHelper.removeResetListener( l ); +} + +//----------------------------------------------------------------------------- +void OBoundControlModel::reset() throw (RuntimeException) +{ + if ( !m_aResetHelper.approveReset() ) + return; + + ControlModelLock aLock( *this ); + + // on a new record? + sal_Bool bIsNewRecord = sal_False; + Reference<XPropertySet> xSet( m_xCursor, UNO_QUERY ); + if ( xSet.is() ) + { + try + { + xSet->getPropertyValue( PROPERTY_ISNEW ) >>= bIsNewRecord; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + } + } + + // cursor on an invalid row? + sal_Bool bInvalidCursorPosition = sal_True; + try + { + bInvalidCursorPosition = m_xCursor.is() + && ( m_xCursor->isAfterLast() + || m_xCursor->isBeforeFirst() + ) + && !bIsNewRecord; + } + catch( const SQLException& ) + { + OSL_ENSURE( sal_False, "OBoundControlModel::reset: caught an SQL exception!" ); + } + // don't count the insert row as "invalid" + // @since #i24495# + // @date 2004-05-14 + // @author fs@openoffice.org + + sal_Bool bSimpleReset = + ( !m_xColumn.is() // no connection to a database column + || ( m_xCursor.is() // OR we have an improperly positioned cursor + && bInvalidCursorPosition + ) + || hasExternalValueBinding() // OR we have an external value binding + ); + + if ( !bSimpleReset ) + { + // The default values will be set if and only if the current value of the field which we're bound + // to is NULL. + // Else, the current field value should be refreshed + // This behaviour is not completely ... "matured": What should happen if the field as well as the + // control have a default value? + + sal_Bool bIsNull = sal_True; + // we have to access the field content at least once to get a reliable result by XColumn::wasNull + try + { + // normally, we'd do a getString here. However, this is extremely expensive in the case + // of binary fields. Unfortunately, getString is the only method which is guaranteed + // to *always* succeed, all other getXXX methods may fail if the column is asked for a + // non-convertible type + sal_Int32 nFieldType = DataType::OBJECT; + getField()->getPropertyValue( PROPERTY_FIELDTYPE ) >>= nFieldType; + if ( ( nFieldType == DataType::BINARY ) + || ( nFieldType == DataType::VARBINARY ) + || ( nFieldType == DataType::LONGVARBINARY ) + || ( nFieldType == DataType::OBJECT ) + /*|| ( nFieldType == DataType::CLOB )*/ + ) + m_xColumn->getBinaryStream(); + else if ( nFieldType == DataType::BLOB ) + m_xColumn->getBlob(); + else + m_xColumn->getString(); + + bIsNull = m_xColumn->wasNull(); + } + catch(Exception&) + { + DBG_ERROR("OBoundControlModel::reset: this should have succeeded in all cases!"); + } + + sal_Bool bNeedValueTransfer = sal_True; + + if ( bIsNull ) + { + if ( bIsNewRecord ) + { + // 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; + } + } + + if ( bNeedValueTransfer ) + transferDbValueToControl(); + } + else + { + resetNoBroadcast(); + + // transfer to the external binding, if necessary + if ( hasExternalValueBinding() ) + transferControlValueToExternal( aLock ); + } + + // revalidate, if necessary + if ( hasValidator() ) + recheckValidity( true ); + + aLock.release(); + + m_aResetHelper.notifyResetted(); +} + +// ----------------------------------------------------------------------------- +void OBoundControlModel::impl_setField_noNotify( const Reference< XPropertySet>& _rxField ) +{ + DBG_ASSERT( !hasExternalValueBinding(), "OBoundControlModel::impl_setField_noNotify: We have an external value binding!" ); + m_xField = _rxField; +} + +//-------------------------------------------------------------------- +sal_Bool OBoundControlModel::impl_approveValueBinding_nolock( const Reference< XValueBinding >& _rxBinding ) +{ + if ( !_rxBinding.is() ) + return sal_False; + + Sequence< Type > aTypeCandidates; + { + // SYNCHRONIZED --> + ::osl::MutexGuard aGuard( m_aMutex ); + aTypeCandidates = getSupportedBindingTypes(); + // <-- SYNCHRONIZED + } + + for ( const Type* pType = aTypeCandidates.getConstArray(); + pType != aTypeCandidates.getConstArray() + aTypeCandidates.getLength(); + ++pType + ) + { + if ( _rxBinding->supportsType( *pType ) ) + return sal_True; + } + + return sal_False; +} + +//-------------------------------------------------------------------- +void OBoundControlModel::connectExternalValueBinding( + const Reference< XValueBinding >& _rxBinding, ControlModelLock& _rInstanceLock ) +{ + OSL_PRECOND( _rxBinding.is(), "OBoundControlModel::connectExternalValueBinding: invalid binding instance!" ); + OSL_PRECOND( !hasExternalValueBinding( ), "OBoundControlModel::connectExternalValueBinding: precond not met (currently have a binding)!" ); + + // if we're connected to a database column, suspend this + if ( hasField() ) + impl_disconnectDatabaseColumn_noNotify(); + + // suspend listening for load-related events at out ambient form. + // This is because an external value binding overrules a possible database binding. + if ( isFormListening() ) + doFormListening( false ); + + // remember this new binding + m_xExternalBinding = _rxBinding; + + // tell the derivee + onConnectedExternalValue(); + + try + { + // 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 ); + + // add as property change listener for some (possibly present) properties we're + // interested in + Reference< XPropertySet > xBindingProps( m_xExternalBinding, UNO_QUERY ); + Reference< XPropertySetInfo > xBindingPropsInfo( xBindingProps.is() ? xBindingProps->getPropertySetInfo() : Reference< XPropertySetInfo >() ); + if ( xBindingPropsInfo.is() ) + { + if ( xBindingPropsInfo->hasPropertyByName( PROPERTY_READONLY ) ) + { + xBindingProps->addPropertyChangeListener( PROPERTY_READONLY, this ); + m_bBindingControlsRO = sal_True; + } + if ( xBindingPropsInfo->hasPropertyByName( PROPERTY_RELEVANT ) ) + { + xBindingProps->addPropertyChangeListener( PROPERTY_RELEVANT, this ); + m_bBindingControlsEnable = sal_True; + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + } + + // propagate our new value + transferExternalValueToControl( _rInstanceLock ); + + // if the binding is also a validator, use it, too. This is a constraint of the + // com.sun.star.form.binding.ValidatableBindableFormComponent service + if ( m_bSupportsValidation ) + { + try + { + Reference< XValidator > xAsValidator( _rxBinding, UNO_QUERY ); + if ( xAsValidator.is() ) + setValidator( xAsValidator ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + } + } +} + +//-------------------------------------------------------------------- +void OBoundControlModel::disconnectExternalValueBinding( ) +{ + try + { + // not listening at the binding anymore + Reference< XModifyBroadcaster > xModifiable( m_xExternalBinding, UNO_QUERY ); + if ( xModifiable.is() ) + xModifiable->removeModifyListener( this ); + + // remove as property change listener + Reference< XPropertySet > xBindingProps( m_xExternalBinding, UNO_QUERY ); + if ( m_bBindingControlsRO ) + xBindingProps->removePropertyChangeListener( PROPERTY_READONLY, this ); + if ( m_bBindingControlsEnable ) + xBindingProps->removePropertyChangeListener( PROPERTY_RELEVANT, this ); + } + catch( const Exception& ) + { + OSL_ENSURE( sal_False, "OBoundControlModel::disconnectExternalValueBinding: caught an exception!" ); + } + + // if the binding also acts as our validator, disconnect the validator, too + if ( ( m_xExternalBinding == m_xValidator ) && m_xValidator.is() ) + disconnectValidator( ); + + // no binding anymore + m_xExternalBinding.clear(); + + // be a load listener at our form, again. This was suspended while we had + // an external value binding in place. + doFormListening( true ); + + // re-connect to database column of the new parent + if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() ) + impl_connectDatabaseColumn_noNotify( false ); + + // tell the derivee + onDisconnectedExternalValue(); +} + +//-------------------------------------------------------------------- +void SAL_CALL OBoundControlModel::setValueBinding( const Reference< XValueBinding >& _rxBinding ) throw (IncompatibleTypesException, RuntimeException) +{ + 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 ( !impl_approveValueBinding_nolock( _rxBinding ) ) + { + throw IncompatibleTypesException( + FRM_RES_STRING( RID_STR_INCOMPATIBLE_TYPES ), + *this + ); + } + + ControlModelLock aLock( *this ); + + // since a ValueBinding overrules any potentially active database binding, the change in a ValueBinding + // might trigger a change in our BoundField. + FieldChangeNotifier aBoundFieldNotifier( aLock ); + + // disconnect from the old binding + if ( hasExternalValueBinding() ) + disconnectExternalValueBinding( ); + + // connect to the new binding + if ( _rxBinding.is() ) + connectExternalValueBinding( _rxBinding, aLock ); +} + +//-------------------------------------------------------------------- +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 ) +{ + ControlModelLock aLock( *this ); + + OSL_PRECOND( hasExternalValueBinding(), "OBoundControlModel::modified: Where did this come from?" ); + if ( !m_bTransferingValue && ( m_xExternalBinding == _rEvent.Source ) && m_xExternalBinding.is() ) + { + transferExternalValueToControl( aLock ); + } +} + +//-------------------------------------------------------------------- +void OBoundControlModel::transferDbValueToControl( ) +{ + try + { + setControlValue( translateDbColumnToControlValue(), eDbColumnBinding ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + } +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::transferExternalValueToControl( ControlModelLock& _rInstanceLock ) +{ + Reference< XValueBinding > xExternalBinding( m_xExternalBinding ); + Type aValueExchangeType( getExternalValueType() ); + + _rInstanceLock.release(); + // >>>>>>>> ----- UNSAFE ----- >>>>>>>> + Any aExternalValue; + try + { + aExternalValue = xExternalBinding->getValue( aValueExchangeType ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + } + // <<<<<<<< ----- UNSAFE ----- <<<<<<<< + _rInstanceLock.acquire(); + + setControlValue( translateExternalValueToControlValue( aExternalValue ), eExternalBinding ); +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::transferControlValueToExternal( ControlModelLock& _rInstanceLock ) +{ + OSL_PRECOND( m_bSupportsExternalBinding && hasExternalValueBinding(), + "OBoundControlModel::transferControlValueToExternal: precondition not met!" ); + + if ( m_xExternalBinding.is() ) + { + Any aExternalValue( translateControlValueToExternalValue() ); + m_bTransferingValue = sal_True; + + _rInstanceLock.release(); + // >>>>>>>> ----- UNSAFE ----- >>>>>>>> + try + { + m_xExternalBinding->setValue( aExternalValue ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + } + // <<<<<<<< ----- UNSAFE ----- <<<<<<<< + _rInstanceLock.acquire(); + + m_bTransferingValue = sal_False; + } +} + +// ----------------------------------------------------------------------------- +Sequence< Type > OBoundControlModel::getSupportedBindingTypes() +{ + return Sequence< Type >( &m_aValuePropertyType, 1 ); +} + +//----------------------------------------------------------------------------- +void OBoundControlModel::calculateExternalValueType() +{ + m_aExternalValueType = Type(); + if ( !m_xExternalBinding.is() ) + return; + + Sequence< Type > aTypeCandidates( getSupportedBindingTypes() ); + for ( const Type* pTypeCandidate = aTypeCandidates.getConstArray(); + pTypeCandidate != aTypeCandidates.getConstArray() + aTypeCandidates.getLength(); + ++pTypeCandidate + ) + { + if ( m_xExternalBinding->supportsType( *pTypeCandidate ) ) + { + m_aExternalValueType = *pTypeCandidate; + break; + } + } +} + +//----------------------------------------------------------------------------- +Any OBoundControlModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const +{ + OSL_PRECOND( m_bSupportsExternalBinding && hasExternalValueBinding(), + "OBoundControlModel::translateExternalValueToControlValue: precondition not met!" ); + + Any aControlValue( _rExternalValue ); + + // if the external value is VOID, and our value property is not allowed to be VOID, + // then default-construct a value + if ( !aControlValue.hasValue() && !m_bValuePropertyMayBeVoid ) + aControlValue.setValue( NULL, m_aValuePropertyType ); + + // outta here + return aControlValue; +} + +//------------------------------------------------------------------------------ +Any OBoundControlModel::translateControlValueToExternalValue( ) const +{ + return getControlValue( ); +} + +//------------------------------------------------------------------------------ +Any OBoundControlModel::translateControlValueToValidatableValue( ) const +{ + OSL_PRECOND( m_xValidator.is(), "OBoundControlModel::translateControlValueToValidatableValue: no validator, so why should I?" ); + if ( ( m_xValidator == m_xExternalBinding ) && m_xValidator.is() ) + return translateControlValueToExternalValue(); + return getControlValue(); +} + +//------------------------------------------------------------------------------ +Any OBoundControlModel::getControlValue( ) const +{ + OSL_PRECOND( m_xAggregateFastSet.is() && m_xAggregateSet.is(), + "OBoundControlModel::getControlValue: invalid aggregate !" ); + OSL_PRECOND( m_sValuePropertyName.getLength() || ( m_nValuePropertyAggregateHandle != -1 ), + "OBoundControlModel::getControlValue: 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; +} + +//-------------------------------------------------------------------- +void OBoundControlModel::connectValidator( const Reference< XValidator >& _rxValidator ) +{ + OSL_PRECOND( _rxValidator.is(), "OBoundControlModel::connectValidator: invalid validator instance!" ); + OSL_PRECOND( !hasValidator( ), "OBoundControlModel::connectValidator: precond not met (have a validator currently)!" ); + + m_xValidator = _rxValidator; + + // add as value listener so we get notified when the value changes + if ( m_xValidator.is() ) + { + try + { + m_xValidator->addValidityConstraintListener( this ); + } + catch( const RuntimeException& ) + { + } + } + + onConnectedValidator( ); +} + +//-------------------------------------------------------------------- +void OBoundControlModel::disconnectValidator( ) +{ + OSL_PRECOND( hasValidator( ), "OBoundControlModel::connectValidator: precond not met (don't have a validator currently)!" ); + + // add as value listener so we get notified when the value changes + if ( m_xValidator.is() ) + { + try + { + m_xValidator->removeValidityConstraintListener( this ); + } + catch( const RuntimeException& ) + { + } + } + + m_xValidator.clear(); + + onDisconnectedValidator( ); +} + +//-------------------------------------------------------------------- +void SAL_CALL OBoundControlModel::setValidator( const Reference< XValidator >& _rxValidator ) throw (VetoException,RuntimeException) +{ + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + OSL_PRECOND( m_bSupportsValidation, "OBoundControlModel::setValidator: How did you reach this method?" ); + // the interface for this method should not have been exposed if we do not + // support validation + + // early out if the validator does not change + if( _rxValidator == m_xValidator ) + return; + + if ( m_xValidator.is() && ( m_xValidator == m_xExternalBinding ) ) + throw VetoException( + FRM_RES_STRING( RID_STR_INVALID_VALIDATOR ), + *this + ); + + // disconnect from the old validator + if ( hasValidator() ) + disconnectValidator( ); + + // connect to the new validator + if ( _rxValidator.is() ) + connectValidator( _rxValidator ); +} + +//-------------------------------------------------------------------- +Reference< XValidator > SAL_CALL OBoundControlModel::getValidator( ) throw (RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + OSL_PRECOND( m_bSupportsValidation, "OBoundControlModel::getValidator: How did you reach this method?" ); + // the interface for this method should not have been exposed if we do not + // support validation + + return m_xValidator; +} + +//-------------------------------------------------------------------- +void SAL_CALL OBoundControlModel::validityConstraintChanged( const EventObject& /*Source*/ ) throw (RuntimeException) +{ + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + OSL_PRECOND( m_bSupportsValidation, "OBoundControlModel::validityConstraintChanged: How did you reach this method?" ); + // the interface for this method should not have been exposed if we do not + // support validation + + recheckValidity( false ); +} + +//-------------------------------------------------------------------- +sal_Bool SAL_CALL OBoundControlModel::isValid( ) throw (RuntimeException) +{ + return m_bIsCurrentValueValid; +} + +//-------------------------------------------------------------------- +::com::sun::star::uno::Any OBoundControlModel::getCurrentFormComponentValue() const +{ + if ( hasValidator() ) + return translateControlValueToValidatableValue(); + return getControlValue(); +} + +//-------------------------------------------------------------------- +Any SAL_CALL OBoundControlModel::getCurrentValue( ) throw (RuntimeException) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + return getCurrentFormComponentValue(); +} + +//-------------------------------------------------------------------- +void SAL_CALL OBoundControlModel::addFormComponentValidityListener( const Reference< validation::XFormComponentValidityListener >& Listener ) throw (NullPointerException, RuntimeException) +{ + if ( Listener.is() ) + m_aFormComponentListeners.addInterface( Listener ); +} + +//-------------------------------------------------------------------- +void SAL_CALL OBoundControlModel::removeFormComponentValidityListener( const Reference< validation::XFormComponentValidityListener >& Listener ) throw (NullPointerException, RuntimeException) +{ + if ( Listener.is() ) + m_aFormComponentListeners.removeInterface( Listener ); +} + +//-------------------------------------------------------------------- +void OBoundControlModel::recheckValidity( bool _bForceNotification ) +{ + try + { + sal_Bool bIsCurrentlyValid = sal_True; + if ( hasValidator() ) + bIsCurrentlyValid = m_xValidator->isValid( translateControlValueToValidatableValue() ); + + if ( ( bIsCurrentlyValid != m_bIsCurrentValueValid ) || _bForceNotification ) + { + m_bIsCurrentValueValid = bIsCurrentlyValid; + + // release our mutex for the notifications + MutexRelease aRelease( m_aMutex ); + m_aFormComponentListeners.notifyEach( &validation::XFormComponentValidityListener::componentValidityChanged, EventObject( *this ) ); + } + } + catch( const Exception& ) + { + OSL_ENSURE( sal_False, "OBoundControlModel::recheckValidity: caught an exception!" ); + } +} + +//------------------------------------------------------------------------------ +void OBoundControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const +{ + BEGIN_DESCRIBE_PROPERTIES( 5, OControlModel ) + DECL_PROP1 ( CONTROLSOURCE, ::rtl::OUString, BOUND ); + DECL_IFACE_PROP3( BOUNDFIELD, XPropertySet, BOUND, READONLY, TRANSIENT ); + DECL_IFACE_PROP2( CONTROLLABEL, XPropertySet, BOUND, MAYBEVOID ); + DECL_PROP2 ( CONTROLSOURCEPROPERTY, ::rtl::OUString, READONLY, TRANSIENT ); + DECL_BOOL_PROP1 ( INPUT_REQUIRED, BOUND ); + END_DESCRIBE_PROPERTIES() +} + +// ----------------------------------------------------------------------------- + +//......................................................................... +} +//... namespace frm ....................................................... + |