/************************************************************************* * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: FormComponent.cxx,v $ * * $Revision: 1.45 $ * * last change: $Author: obo $ $Date: 2006-01-19 15:37:21 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. * * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2005 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * ************************************************************************/ #ifndef _FORMS_FORMCOMPONENT_HXX_ #include "FormComponent.hxx" #endif #ifndef _TOOLS_DEBUG_HXX #include #endif #ifndef TOOLS_DIAGNOSE_EX_H #include #endif #ifndef _CPPUHELPER_QUERYINTERFACE_HXX_ #include #endif #ifndef _COM_SUN_STAR_AWT_XTEXTCOMPONENT_HPP_ #include #endif #ifndef _COM_SUN_STAR_AWT_XWINDOW_HPP_ #include #endif #ifndef _COM_SUN_STAR_IO_XMARKABLESTREAM_HPP_ #include #endif #ifndef _COM_SUN_STAR_FORM_XLOADABLE_HPP_ #include #endif #ifndef _COM_SUN_STAR_FORM_XFORM_HPP_ #include #endif #ifndef _COM_SUN_STAR_SDBC_DATATYPE_HPP_ #include #endif #ifndef _COM_SUN_STAR_SDBC_COLUMNVALUE_HPP_ #include #endif #ifndef _COM_SUN_STAR_UTIL_XMODIFYBROADCASTER_HPP_ #include #endif #ifndef _COM_SUN_STAR_AWT_XVCLWINDOWPEER_HPP_ #include #endif #ifndef _COM_SUN_STAR_LANG_DISPOSEDEXCEPTION_HPP_ #include #endif #ifndef _COMPHELPER_PROPERTY_HXX_ #include #endif #ifndef _COMPHELPER_GUARDING_HXX_ #include #endif #ifndef _CONNECTIVITY_DBTOOLS_HXX_ #include #endif #ifndef _FRM_PROPERTY_HRC_ #include "property.hrc" #endif #ifndef _FRM_SERVICES_HXX_ #include "services.hxx" #endif #ifndef FORMS_SOURCE_INC_COMPONENTTOOLS_HXX #include "componenttools.hxx" #endif #ifndef _RTL_LOGFILE_HXX_ #include #endif #ifndef _COMPHELPER_BASIC_IO_HXX_ #include #endif #ifndef COMPHELPER_INC_COMPHELPER_LISTENERNOTIFICATION_HXX #include #endif #ifndef _TOOLKIT_HELPER_EMPTYFONTDESCRIPTOR_HXX_ #include #endif #ifndef _FRM_RESOURCE_HXX_ #include "frm_resource.hxx" #endif #ifndef _FRM_RESOURCE_HRC_ #include "frm_resource.hrc" #endif //... 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; //==================================================================== //= OFormComponentListeners //==================================================================== typedef ::comphelper::OListenerContainerBase < XFormComponentValidityListener , EventObject > OFormComponentListeners_Base; class OFormComponentListeners : public OFormComponentListeners_Base { public: OFormComponentListeners( ::osl::Mutex& _rMutex ) :OFormComponentListeners_Base( _rMutex ) { } protected: virtual bool implNotify( const Reference< XFormComponentValidityListener >& _rxListener, const EventObject& _rEvent ) SAL_THROW( ( Exception ) ); }; //------------------------------------------------------------------------------ bool OFormComponentListeners::implNotify( const Reference< XFormComponentValidityListener >& _rxListener, const EventObject& _rEvent ) SAL_THROW( ( Exception ) ) { _rxListener->componentValidityChanged( _rEvent ); return true; // continue calling listeners } //========================================================================= //= 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_xServiceFactory(_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_CALL OControl::getImplementationId() throw(RuntimeException) { return OImplementationIds::getImplementationId(getTypes()); } //------------------------------------------------------------------------------ Sequence 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 OControl::_getTypes() { return TypeBag( OComponentHelper::getTypes(), OControl_BASE::getTypes() ).getTypes(); } // OComponentHelper //------------------------------------------------------------------------------ void OControl::disposing() { OComponentHelper::disposing(); Reference xComp; if (query_aggregation(m_xAggregate, xComp)) xComp->dispose(); } // XServiceInfo //------------------------------------------------------------------------------ sal_Bool SAL_CALL OControl::supportsService(const rtl::OUString& _rsServiceName) throw ( RuntimeException) { Sequence aSupported = getSupportedServiceNames(); const rtl::OUString* pSupported = aSupported.getConstArray(); for (sal_Int32 i=0; iequals(_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 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) { InterfaceRef xAggAsIface; query_aggregation(m_xAggregate, xAggAsIface); // does the disposing come from the aggregate ? if (xAggAsIface != InterfaceRef(_rEvent.Source, UNO_QUERY)) { // no -> forward it Reference xListener; if (query_aggregation(m_xAggregate, xListener)) xListener->disposing(_rEvent); } } // XControl //------------------------------------------------------------------------------ void SAL_CALL OControl::setContext(const InterfaceRef& Context) throw (RuntimeException) { if (m_xControl.is()) m_xControl->setContext(Context); } //------------------------------------------------------------------------------ InterfaceRef SAL_CALL OControl::getContext() throw (RuntimeException) { return m_xControl.is() ? m_xControl->getContext() : InterfaceRef(); } //------------------------------------------------------------------------------ void SAL_CALL OControl::createPeer(const Reference& _rxToolkit, const Reference& _rxParent) throw (RuntimeException) { if ( m_xControl.is() ) m_xControl->createPeer( _rxToolkit, _rxParent ); } //------------------------------------------------------------------------------ Reference SAL_CALL OControl::getPeer() throw ( RuntimeException) { return m_xControl.is() ? m_xControl->getPeer() : Reference(); } //------------------------------------------------------------------------------ sal_Bool SAL_CALL OControl::setModel(const Reference& Model) throw ( RuntimeException) { return m_xControl.is() ? m_xControl->setModel( Model ) : sal_False; } //------------------------------------------------------------------------------ Reference SAL_CALL OControl::getModel() throw ( RuntimeException) { return m_xControl.is() ? m_xControl->getModel() : Reference(); } //------------------------------------------------------------------------------ Reference SAL_CALL OControl::getView() throw ( RuntimeException) { return m_xControl.is() ? m_xControl->getView() : Reference(); } //------------------------------------------------------------------------------ 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 //================================================================== ConcretInfoService OControlModel::s_aPropInfos; DBG_NAME(OControlModel) //------------------------------------------------------------------ Sequence SAL_CALL OControlModel::getImplementationId() throw(RuntimeException) { return OImplementationIds::getImplementationId(getTypes()); } //------------------------------------------------------------------ Sequence 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 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& _rxFactory, const ::rtl::OUString& _rUnoControlModelTypeName, const ::rtl::OUString& rDefault, const sal_Bool _bSetDelegator) :OComponentHelper(m_aMutex) ,OPropertySetAggregationHelper(OComponentHelper::rBHelper) ,m_nTabIndex(FRM_DEFAULT_TABINDEX) ,m_nClassId(FormComponentType::CONTROL) ,m_xServiceFactory(_rxFactory) ,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(_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_nTabIndex( FRM_DEFAULT_TABINDEX ) ,m_nClassId( FormComponentType::CONTROL ) ,m_xServiceFactory( _rxFactory ) { 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(this)); } decrement(m_refCount); } //------------------------------------------------------------------------------ void OControlModel::ensureAlive() SAL_THROW( ( DisposedException ) ) { if ( OComponentHelper::rBHelper.bDisposed || OComponentHelper::rBHelper.bInDispose ) throw DisposedException(); } // XChild //------------------------------------------------------------------------------ InterfaceRef SAL_CALL OControlModel::getParent() throw(RuntimeException) { return m_xParent; } //------------------------------------------------------------------------------ void SAL_CALL OControlModel::setParent(const InterfaceRef& _rxParent) throw(com::sun::star::lang::NoSupportException, RuntimeException) { osl::MutexGuard aGuard(m_aMutex); Reference xComp(m_xParent, UNO_QUERY); if (xComp.is()) xComp->removeEventListener(static_cast(this)); { xComp = xComp.query( _rxParent ); RTL_LOGFILE_CONTEXT( aLogger, "OControlModel::setParent::logOnEventListener" ); if ( xComp.is() ) xComp->addEventListener(static_cast(this)); } m_xParent = _rxParent; } // 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 aSupported = getSupportedServiceNames(); const rtl::OUString* pSupported = aSupported.getConstArray(); for (sal_Int32 i=0; iequals(_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 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 xEvtLst; if (query_aggregation(m_xAggregate, xEvtLst)) { osl::MutexGuard aGuard(m_aMutex); xEvtLst->disposing(_rSource); } } } // OComponentHelper //----------------------------------------------------------------------------- void OControlModel::disposing() { OPropertySetAggregationHelper::disposing(); Reference xComp; if (query_aggregation(m_xAggregate, xComp)) xComp->dispose(); setParent(Reference()); } //------------------------------------------------------------------------------ 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 ); } //------------------------------------------------------------------------------ namespace { static ::rtl::OUString getCompatibleControlServiceName( const ::rtl::OUString& _rServiceName, bool _bRevertedLookup ) { ::rtl::OUString sReturn( _rServiceName ); ::rtl::OUString aServiceNamePairs[] = { FRM_SUN_CONTROL_COMMANDBUTTON, STARDIV_ONE_FORM_CONTROL_COMMANDBUTTON, FRM_SUN_CONTROL_RADIOBUTTON, STARDIV_ONE_FORM_CONTROL_RADIOBUTTON, FRM_SUN_CONTROL_CHECKBOX, STARDIV_ONE_FORM_CONTROL_CHECKBOX, FRM_SUN_CONTROL_TEXTFIELD, STARDIV_ONE_FORM_CONTROL_EDIT, FRM_SUN_CONTROL_LISTBOX, STARDIV_ONE_FORM_CONTROL_LISTBOX, FRM_SUN_CONTROL_COMBOBOX, STARDIV_ONE_FORM_CONTROL_COMBOBOX, FRM_SUN_CONTROL_GROUPBOX, STARDIV_ONE_FORM_CONTROL_GROUPBOX, FRM_SUN_CONTROL_IMAGEBUTTON, STARDIV_ONE_FORM_CONTROL_IMAGEBUTTON, FRM_SUN_CONTROL_TIMEFIELD, STARDIV_ONE_FORM_CONTROL_TIMEFIELD, FRM_SUN_CONTROL_DATEFIELD, STARDIV_ONE_FORM_CONTROL_DATEFIELD, FRM_SUN_CONTROL_NUMERICFIELD, STARDIV_ONE_FORM_CONTROL_NUMERICFIELD, FRM_SUN_CONTROL_CURRENCYFIELD, STARDIV_ONE_FORM_CONTROL_CURRENCYFIELD, FRM_SUN_CONTROL_PATTERNFIELD, STARDIV_ONE_FORM_CONTROL_PATTERNFIELD, FRM_SUN_CONTROL_IMAGECONTROL, STARDIV_ONE_FORM_CONTROL_IMAGECONTROL, FRM_SUN_CONTROL_FORMATTEDFIELD, STARDIV_ONE_FORM_CONTROL_FORMATTEDFIELD }; sal_Int32 i = 0; for ( ; i < sizeof( aServiceNamePairs ) / sizeof( aServiceNamePairs[ 0 ] ) / 2; ++i ) { if ( _bRevertedLookup ) { if ( _rServiceName == aServiceNamePairs[ 2 * i + 1 ] ) { sReturn = aServiceNamePairs[ 2 * i ]; break; } } else { if ( _rServiceName == aServiceNamePairs[ 2 * i ] ) { sReturn = aServiceNamePairs[ 2 * i + 1 ]; break; } } } #if OSL_DEBUG_LEVEL > 0 if ( i >= sizeof( aServiceNamePairs ) / sizeof( aServiceNamePairs[ 0 ] ) / 2 ) { DBG_WARNING( ( ::rtl::OString( "getCompatibleControlServiceName: unknown service name (" ) += ::rtl::OString( _rServiceName.getStr(), _rServiceName.getLength(), RTL_TEXTENCODING_ASCII_US ) += ::rtl::OString( ")!" ) ).getStr() ); } #endif return sReturn; } } //------------------------------------------------------------------------------ void SAL_CALL OControlModel::write(const Reference& _rxOutStream) throw(stario::IOException, RuntimeException) { osl::MutexGuard aGuard(m_aMutex); // 1. Schreiben des UnoControls Reference 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); // during writing the aggregate, temporarily reset the DefaultControl property // In the binary format, we need to have the old service names containing // a "stardiv...." string, while today at runtime, we have "new" service names // #92831# - 2004-03-17 - fs@openoffice.org ::rtl::OUString sOriginalDefaultControl; try { if ( m_xAggregateSet.is() ) { m_xAggregateSet->getPropertyValue( PROPERTY_DEFAULTCONTROL ) >>= sOriginalDefaultControl; ::rtl::OUString sNewDefaultControl( getCompatibleControlServiceName( sOriginalDefaultControl, false ) ); m_xAggregateSet->setPropertyValue( PROPERTY_DEFAULTCONTROL, makeAny( sNewDefaultControl ) ); } writeAggregate( _rxOutStream ); if ( m_xAggregateSet.is() ) m_xAggregateSet->setPropertyValue( PROPERTY_DEFAULTCONTROL, makeAny( sOriginalDefaultControl ) ); } catch( const Exception& ) { if ( m_xAggregateSet.is() ) m_xAggregateSet->setPropertyValue( PROPERTY_DEFAULTCONTROL, makeAny( sOriginalDefaultControl ) ); throw; } // 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& InStream) throw (::com::sun::star::io::IOException, RuntimeException) { osl::MutexGuard aGuard(m_aMutex); Reference 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 ); // translate the service name to a more modern one. // #i29828# - 2004-06-18 - fs@openoffice.org if ( m_xAggregateSet.is() ) { ::rtl::OUString sOriginalDefaultControl; m_xAggregateSet->getPropertyValue( PROPERTY_DEFAULTCONTROL ) >>= sOriginalDefaultControl; ::rtl::OUString sNewDefaultControl( getCompatibleControlServiceName( sOriginalDefaultControl, true ) ); m_xAggregateSet->setPropertyValue( PROPERTY_DEFAULTCONTROL, makeAny( sNewDefaultControl ) ); } } 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 ) { Any aDefault = getPropertyDefaultByHandle( _nHandle ); Any aCurrent; getFastPropertyValue( aCurrent, _nHandle ); if ( ::comphelper::compare( aDefault, aCurrent ) ) return PropertyState_DEFAULT_VALUE; return 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; } 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: OPropertySetAggregationHelper::getFastPropertyValue(rValue, nHandle); } } //------------------------------------------------------------------------------ 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; } 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; } } //------------------------------------------------------------------------------ void OControlModel::fillProperties( Sequence< Property >& /* [out] */ _rProps, Sequence< Property >& /* [out] */ _rAggregateProps ) const { BEGIN_DESCRIBE_AGGREGATION_PROPERTIES( 4, m_xAggregateSet ) 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() } //================================================================== //= 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_aUpdateListeners(m_aMutex) ,m_aResetListeners(m_aMutex) ,m_pFormComponentListeners( new OFormComponentListeners( m_aMutex ) ) ,m_bLoaded(sal_False) ,m_bRequired(sal_False) ,m_bCommitable(_bCommitable) ,m_bSupportsExternalBinding( _bSupportExternalBinding ) ,m_bSupportsValidation( _bSupportsValidation ) ,m_aLabelServiceName(FRM_SUN_COMPONENT_FIXEDTEXT) ,m_bForwardValueChanges(sal_True) ,m_bLoadListening( sal_False ) ,m_nValuePropertyAggregateHandle( -1 ) ,m_pAggPropMultiplexer( NULL ) ,m_eControlValueChangeInstigator( eOther ) ,m_bIsCurrentValueValid( sal_True ) ,m_bTransferingValue( sal_False ) ,m_bBindingControlsRO( sal_False ) ,m_bBindingControlsEnable( sal_False ) { 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_aUpdateListeners( m_aMutex ) ,m_aResetListeners( m_aMutex ) ,m_pFormComponentListeners( new OFormComponentListeners( m_aMutex ) ) ,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_bLoadListening( sal_False ) ,m_nValuePropertyAggregateHandle( _pOriginal->m_nValuePropertyAggregateHandle ) ,m_pAggPropMultiplexer( NULL ) ,m_eControlValueChangeInstigator( eOther ) ,m_xValidator( _pOriginal->m_xValidator ) ,m_bIsCurrentValueValid( _pOriginal->m_bIsCurrentValueValid ) ,m_bTransferingValue( sal_False ) { 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_aControlSource = _pOriginal->m_aControlSource; // 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() ) setValueBinding( pBoundOriginal->m_xExternalBinding ); } //----------------------------------------------------------------------------- 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::initValueProperty( const ::rtl::OUString& _rValuePropertyName, sal_Int32 _nValuePropertyExternalHandle ) { OSL_PRECOND( !m_sValuePropertyName.getLength() && -1 == m_nValuePropertyAggregateHandle, "OBoundControlModel::initValueProperty: already called before!" ); OSL_ENSURE( _rValuePropertyName.getLength(), "OBoundControlModel::initValueProperty: invalid property name!" ); OSL_ENSURE( _nValuePropertyExternalHandle != -1, "OBoundControlModel::initValueProperty: invalid property handle!" ); m_sValuePropertyName = _rValuePropertyName; m_nValuePropertyAggregateHandle = getOriginalHandle( _nValuePropertyExternalHandle ); OSL_ENSURE( m_nValuePropertyAggregateHandle != -1, "OBoundControlModel::initValueProperty: unable to find the original handle!" ); // start listening for changes at the value property implInitValuePropertyListening( ); } //----------------------------------------------------------------------------- void OBoundControlModel::suspendValueListening( ) { OSL_PRECOND( m_sValuePropertyName.getLength(), "OBoundControlModel::suspendValueListening: don't have a value property!" ); OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::suspendValueListening: I *am* not listening!" ); if ( m_pAggPropMultiplexer ) m_pAggPropMultiplexer->lock(); } //----------------------------------------------------------------------------- void OBoundControlModel::resumeValueListening( ) { OSL_PRECOND( m_sValuePropertyName.getLength(), "OBoundControlModel::resumeValueListening: don't have a value property!" ); OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::resumeValueListening: I *am* not listening at all!" ); OSL_PRECOND( !m_pAggPropMultiplexer || m_pAggPropMultiplexer->locked(), "OBoundControlModel::resumeValueListening: listening not suspended currently!" ); if ( m_pAggPropMultiplexer ) m_pAggPropMultiplexer->unlock(); } //----------------------------------------------------------------------------- namespace { template< class T > void appendSequence( Sequence< T >& _rInOut, const Sequence< T >& _rAppend ) { sal_Int32 nLengthBefore = _rInOut.getLength(); sal_Int32 nLengthAppend = _rAppend.getLength(); _rInOut.realloc( nLengthBefore + nLengthAppend ); ::std::copy( _rAppend.getConstArray(), _rAppend.getConstArray() + nLengthAppend, _rInOut.getArray() + nLengthBefore ); } } //----------------------------------------------------------------------------- Sequence< Type > OBoundControlModel::_getTypes() { 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_aResetListeners.disposeAndClear( aEvt ); m_aUpdateListeners.disposeAndClear( aEvt ); // disconnect from our database column // TODO: could we replace the following 5 lines with a call to disconnectDatabaseColumn? // The only more thing which it does is calling onDisconnectedDbColumn - could this // cause trouble? At least when we continue to call OControlModel::disposing before, it *may*. if ( m_xField.is() ) { m_xField->removePropertyChangeListener( PROPERTY_VALUE, this ); 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::_propertyChanged( const PropertyChangeEvent& _rEvt ) throw ( RuntimeException ) { ::osl::ClearableMutexGuard aGuard( m_aMutex ); 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 ) { // our control value changed if ( hasExternalValueBinding() ) { // the control value changed, while we have an external value binding // -> forward the value to it if ( m_eControlValueChangeInstigator != eExternalBinding ) transferControlValueToExternal( ); } 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::startAggregatePropertyListening( const ::rtl::OUString& _rPropertyName ) { OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::startAggregatePropertyListening: no multiplexer!" ); OSL_ENSURE( _rPropertyName.getLength(), "OBoundControlModel::startAggregatePropertyListening: invalid property name!" ); if ( m_pAggPropMultiplexer && _rPropertyName.getLength() ) { m_pAggPropMultiplexer->addProperty( _rPropertyName ); } } //------------------------------------------------------------------------------ void OBoundControlModel::startLoadListening( ) { OSL_PRECOND( !isLoadListening(), "OBoundControlModel::startLoadListening: already listening!" ); OSL_PRECOND( m_xParent.is(), "OBoundControlModel::startLoadListening: no parent to listen at!" ); OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::startLoadListening: external value binding should overrule the database binding!" ); Reference< XLoadable > xLoadable( m_xParent, UNO_QUERY ); if ( xLoadable.is() ) { RTL_LOGFILE_CONTEXT( aLogger, "forms::OBoundControlModel::startLoadListening" ); xLoadable->addLoadListener( this ); m_bLoadListening = sal_True; } } //------------------------------------------------------------------------------ void OBoundControlModel::stopLoadListening( ) { OSL_PRECOND( isLoadListening(), "OBoundControlModel::stopLoadListening: not listening!" ); Reference< XLoadable > xLoadable( m_xParent, UNO_QUERY ); if ( xLoadable.is() && isLoadListening() ) { xLoadable->removeLoadListener( this ); m_bLoadListening = sal_False; } } // XChild //------------------------------------------------------------------------------ void SAL_CALL OBoundControlModel::setParent(const Reference& _rxParent) throw(com::sun::star::lang::NoSupportException, RuntimeException) { ::osl::MutexGuard aGuard( m_aMutex ); // log off old listeners if ( isLoadListening() ) stopLoadListening( ); OControlModel::setParent(_rxParent); // log on new listeners - only in case we do not have an external value binding if ( m_xParent.is() && !hasExternalValueBinding() ) startLoadListening( ); } // XEventListener //------------------------------------------------------------------------------ void SAL_CALL OBoundControlModel::disposing(const com::sun::star::lang::EventObject& _rEvent) throw (RuntimeException) { ::osl::ClearableMutexGuard aGuard( m_aMutex ); if ( _rEvent.Source == m_xField ) { resetField(); } else if ( _rEvent.Source == m_xLabelControl ) { Reference xOldValue = m_xLabelControl; m_xLabelControl = NULL; // fire a property change event Any aOldValue; aOldValue <<= xOldValue; Any aNewValue; aNewValue <<= m_xLabelControl; sal_Int32 nHandle = PROPERTY_ID_CONTROLLABEL; OPropertySetHelper::fire( &nHandle, &aNewValue, &aOldValue, 1, sal_False ); } 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& _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 xComp(m_xLabelControl, UNO_QUERY); if (xComp.is()) xComp->removeEventListener(static_cast(static_cast(this))); m_xLabelControl = NULL; } //------------------------------------------------------------------------------ void OBoundControlModel::readCommonProperties(const Reference& _rxInStream) { sal_Int32 nLen = _rxInStream->readLong(); Reference 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 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(static_cast(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& _rxOutStream) { Reference 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 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(); ::comphelper::operator>>( _rxInStream, m_aControlSource); } //------------------------------------------------------------------------------ void OBoundControlModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const { switch (nHandle) { case PROPERTY_ID_CONTROLSOURCEPROPERTY: rValue <<= m_sValuePropertyName; break; case PROPERTY_ID_CONTROLSOURCE: rValue <<= m_aControlSource; break; case PROPERTY_ID_BOUNDFIELD: rValue <<= m_xField; 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_CONTROLSOURCE: bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aControlSource); break; case PROPERTY_ID_BOUNDFIELD: bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_xField); break; 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_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_CONTROLSOURCE: DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_STRING, "invalid type" ); 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(); break; case PROPERTY_ID_CONTROLLABEL: { DBG_ASSERT(!rValue.hasValue() || (rValue.getValueType().getTypeClass() == TypeClass_INTERFACE), "OBoundControlModel::setFastPropertyValue_NoBroadcast : invalid argument !"); if (!rValue.hasValue()) { // set property to "void" Reference xComp(m_xLabelControl, UNO_QUERY); if (xComp.is()) xComp->removeEventListener(static_cast(static_cast(this))); m_xLabelControl = NULL; break; } InterfaceRef xNewValue; rValue >>= xNewValue; Reference xAsModel(xNewValue, UNO_QUERY); Reference xAsServiceInfo(xNewValue, UNO_QUERY); Reference xAsPropSet(xNewValue, UNO_QUERY); Reference xAsChild(xNewValue, UNO_QUERY); if (!xAsModel.is() || !xAsServiceInfo.is() || !xAsPropSet.is() || !xAsChild.is()) { throw com::sun::star::lang::IllegalArgumentException(); } if (!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 xCont; query_interface(static_cast(this), xCont); InterfaceRef xMyTopLevel = xCont->getParent(); while (xMyTopLevel.is()) { Reference xAsForm(xMyTopLevel, UNO_QUERY); if (!xAsForm.is()) // found my root break; Reference xAsChild(xMyTopLevel, UNO_QUERY); xMyTopLevel = xAsChild.is() ? xAsChild->getParent() : InterfaceRef(); } InterfaceRef xNewTopLevel = xAsChild->getParent(); while (xNewTopLevel.is()) { Reference xAsForm(xNewTopLevel, UNO_QUERY); if (!xAsForm.is()) break; Reference xAsChild(xNewTopLevel, UNO_QUERY); xNewTopLevel = xAsChild.is() ? xAsChild->getParent() : InterfaceRef(); } 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 xComp(m_xLabelControl, UNO_QUERY); if (xComp.is()) xComp->addEventListener(static_cast(static_cast(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 == m_xField, "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; #if FS_PRIV_DEBUG sal_Bool bValue = sal_False; OSL_VERIFY( evt.NewValue >>= bValue ); setPropertyValue( PROPERTY_BACKGROUNDCOLOR, makeAny( sal_Int32( bValue ? 0xFF0000 : 0xFFFFFF ) ) ); setPropertyValue( PROPERTY_TEXTCOLOR, makeAny( sal_Int32( bValue ? 0x000000 : 0x00000 ) ) ); #endif } 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!" ); } } } // XBoundComponent //------------------------------------------------------------------------------ void SAL_CALL OBoundControlModel::addUpdateListener(const Reference& _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) { OSL_PRECOND( m_bCommitable, "OBoundControlModel::commit: invalid call (I'm not commitable !) " ); if ( hasExternalValueBinding() ) { // in most cases, no action is required: For most derivees, we know the value property of // our control (see initValueProperty), and when an external binding is active, we // instantly forward all changes in this property to the external binding. if ( !m_sValuePropertyName.getLength() ) // but for those derivees which did not use this feature, we need an // explicit transfer transferControlValueToExternal( ); return sal_True; } OSL_ENSURE( !hasExternalValueBinding(), "OBoundControlModel::commit: control flow broken!" ); // we reach this only if we're not working with an external binding { osl::MutexGuard aGuard(m_aMutex); if ( !m_xField.is() ) return sal_True; } ::cppu::OInterfaceIteratorHelper aIter( m_aUpdateListeners ); EventObject aEvt; aEvt.Source = static_cast< XWeak* >( this ); sal_Bool bSucceed = sal_True; while (aIter.hasMoreElements() && bSucceed) bSucceed = static_cast< XUpdateListener* >( aIter.next() )->approveUpdate( aEvt ); if ( bSucceed ) { osl::MutexGuard aGuard(m_aMutex); try { if ( m_xColumnUpdate.is() ) bSucceed = commitControlValueToDbColumn( sal_False ); } catch(Exception&) { bSucceed = sal_False; } } if ( bSucceed ) { cppu::OInterfaceIteratorHelper aIter( m_aUpdateListeners ); while ( aIter.hasMoreElements() ) static_cast< XUpdateListener* >( aIter.next() )->updated( aEvt ); } return bSucceed; } //------------------------------------------------------------------------------ void OBoundControlModel::resetField() { m_xColumnUpdate.clear(); m_xColumn.clear(); m_xField.clear(); } //------------------------------------------------------------------------------ sal_Bool OBoundControlModel::connectToField(const Reference& 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 xFieldCandidate; if (m_xCursor.is()) { Reference 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 xColumns(xColumnsSupplier->getColumns(), UNO_QUERY); if (xColumns.is() && xColumns->hasByName(m_aControlSource)) { Any aElement(xColumns->getByName(m_aControlSource)); DBG_ASSERT(xColumns->getElementType().equals(::getCppuType(reinterpret_cast*>(NULL))), "OBoundControlModel::connectToField : the columns container should contain XPropertySets !"); // if this assertion fails we probably should do a queryInterface .... aElement >>= xFieldCandidate; } } } // darf ich mich ueberhaupt an dieses Feld binden (Typ-Check) if (xFieldCandidate.is()) { sal_Int32 nFieldType; xFieldCandidate->getPropertyValue(PROPERTY_FIELDTYPE) >>= nFieldType; if (approveDbColumnType(nFieldType)) setField(xFieldCandidate,sal_False); } else setField(NULL,sal_False); if (m_xField.is()) { if(m_xField->getPropertySetInfo()->hasPropertyByName(PROPERTY_VALUE)) { // an wertaenderungen horchen m_xField->addPropertyChangeListener( PROPERTY_VALUE, this ); m_xColumnUpdate = Reference< XColumnUpdate >( m_xField, UNO_QUERY ); m_xColumn = Reference< XColumn >( m_xField, UNO_QUERY ); INT32 nNullableFlag = 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!"); setField(NULL,sal_False); } } } return m_xField.is(); } //------------------------------------------------------------------------------ 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; } //============================================================================== // value binding handling //------------------------------------------------------------------------------ void OBoundControlModel::connectDatabaseColumn( const Reference< XRowSet >& _rxRowSet, bool _bFromReload ) { OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::connectDatabaseColumn: not to be called with an external value binding!" ); ::osl::MutexGuard aGuard( m_aMutex ); // consistency checks DBG_ASSERT( !( m_xField.is() && !_bFromReload ), "OBoundControlModel::connectDatabaseColumn: the form is just *loaded*, but we already have a field!" ); Reference< XPropertySet > xOldField = m_xField; if ( !m_xField.is() ) { // connect to the column connectToField( _rxRowSet ); } // now that we're connected (more or less, even if we did not find a column), // we definately want to forward any potentially occuring value changes m_bForwardValueChanges = sal_True; // let derived classes react on this new connection m_bLoaded = sal_True; onConnectedDbColumn( _rxRowSet ); // did we successfully connect to a database column? if ( m_xField.is() ) { // initially transfer the db column value to the control // but only if the rowset if posisitioned on a valid record if ( _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(); } } if ( xOldField != m_xField ) { Any aNewValue; aNewValue <<= m_xField; Any aOldValue; aOldValue <<= xOldField; sal_Int32 nHandle = PROPERTY_ID_BOUNDFIELD; OPropertySetHelper::fire(&nHandle, &aNewValue, &aOldValue, 1, sal_False); } } //------------------------------------------------------------------------------ void OBoundControlModel::disconnectDatabaseColumn( ) { OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::disconnectDatabaseColumn: not to be called with an external value binding!" ); ::osl::MutexGuard aGuard( m_aMutex ); // let derived classes react on this onDisconnectedDbColumn(); if ( m_xField.is() ) { m_xField->removePropertyChangeListener( PROPERTY_VALUE, this ); resetField(); } m_xCursor = NULL; m_bLoaded = sal_False; } //============================================================================== // XLoadListener //------------------------------------------------------------------------------ void SAL_CALL OBoundControlModel::loaded( const EventObject& _rEvent ) throw(RuntimeException) { OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::loaded: we should never reach this with an external value binding!" ); if ( hasExternalValueBinding() ) return; // connect to the database column described by our SQL-binding-related properties Reference< XRowSet > xRowSet( _rEvent.Source, UNO_QUERY ); DBG_ASSERT( xRowSet.is(), "OBoundControlModel::loaded: event source is no RowSet?!" ); connectDatabaseColumn( xRowSet, false ); } //------------------------------------------------------------------------------ void SAL_CALL OBoundControlModel::unloaded( const com::sun::star::lang::EventObject& aEvent ) throw(RuntimeException) { OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::unloaded: we should never reach this with an external value binding!" ); } //------------------------------------------------------------------------------ void SAL_CALL OBoundControlModel::reloading( const com::sun::star::lang::EventObject& aEvent ) throw(RuntimeException) { OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::reloading: we should never reach this with an external value binding!" ); if ( hasExternalValueBinding() ) return; osl::MutexGuard aGuard(m_aMutex); m_bForwardValueChanges = sal_False; } //------------------------------------------------------------------------------ void SAL_CALL OBoundControlModel::unloading(const com::sun::star::lang::EventObject& aEvent) throw(RuntimeException) { OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::unloading: we should never reach this with an external value binding!" ); if ( hasExternalValueBinding() ) return; // disconnect from the database column described by our SQL-binding-related properties disconnectDatabaseColumn(); } //------------------------------------------------------------------------------ void SAL_CALL OBoundControlModel::reloaded( const EventObject& _rEvent ) throw(RuntimeException) { OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::reloaded: we should never reach this with an external value binding!" ); if ( hasExternalValueBinding() ) return; Reference< XRowSet > xRowSet( _rEvent.Source, UNO_QUERY ); DBG_ASSERT( xRowSet.is(), "OBoundControlModel::reloaded: event source is no RowSet?!" ); connectDatabaseColumn( xRowSet, 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( ) { } //------------------------------------------------------------------------------ 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& l) throw (RuntimeException) { m_aResetListeners.addInterface(l); } //----------------------------------------------------------------------------- void OBoundControlModel::removeResetListener(const Reference& l) throw (RuntimeException) { m_aResetListeners.removeInterface(l); } //----------------------------------------------------------------------------- void OBoundControlModel::reset() throw (RuntimeException) { cppu::OInterfaceIteratorHelper aIter(m_aResetListeners); EventObject aResetEvent(static_cast(this)); sal_Bool bContinue = sal_True; while ( aIter.hasMoreElements() && bContinue ) bContinue = static_cast< XResetListener* >( aIter.next() )->approveReset( aResetEvent ); if (!bContinue) return; ::osl::ClearableMutexGuard aGuard( m_aMutex ); // on a new record? sal_Bool bIsNewRecord = sal_False; Reference xSet( m_xCursor, UNO_QUERY ); if ( xSet.is() ) { try { xSet->getPropertyValue( PROPERTY_ISNEW ) >>= bIsNewRecord; } catch( const Exception& ) { OSL_ENSURE( sal_False, "OBoundControlModel::reset: caught an 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::BLOB ) || ( nFieldType == DataType::CLOB ) ) m_xColumn->getBinaryStream(); 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(); } // revalidate, if necessary if ( hasValidator() ) recheckValidity( true ); aGuard.clear(); cppu::OInterfaceIteratorHelper aIterDone(m_aResetListeners); while (aIterDone.hasMoreElements()) reinterpret_cast(aIterDone.next())->resetted(aResetEvent); } // ----------------------------------------------------------------------------- void OBoundControlModel::setField( const Reference< XPropertySet>& _rxField,sal_Bool _bFire ) { DBG_ASSERT( !hasExternalValueBinding(), "OBoundControlModel::setField: We have an external value binding!" ); // fire a property change event if ( m_xField != _rxField ) { Any aOldValue; aOldValue <<= m_xField; m_xField = _rxField; if ( _bFire ) { Any aNewValue; aNewValue <<= _rxField; sal_Int32 nHandle = PROPERTY_ID_BOUNDFIELD; OPropertySetHelper::fire(&nHandle, &aNewValue, &aOldValue, 1, sal_False); } } } //-------------------------------------------------------------------- sal_Bool OBoundControlModel::approveValueBinding( const Reference< XValueBinding >& _rxBinding ) { // reject everything. Derived classes need to override this if they want to // benefit from this data binding functionality return sal_False; } //-------------------------------------------------------------------- void OBoundControlModel::connectExternalValueBinding( const Reference< XValueBinding >& _rxBinding ) { OSL_PRECOND( _rxBinding.is(), "OBoundControlModel::connectExternalValueBinding: invalid binding instance!" ); OSL_PRECOND( approveValueBinding( _rxBinding ), "OBoundControlModel::connectExternalValueBinding: binding is not approved!" ); OSL_PRECOND( !hasExternalValueBinding( ), "OBoundControlModel::connectExternalValueBinding: precond not met (currently have a binding)!" ); // Suspend being a load listener at our parent form. This is because // an external value binding overrules a possible database binding if ( isLoadListening() ) stopLoadListening( ); // TODO: if we're already connected to a db column, we should disconnect from it here, // shouldn't we? // remember this new binding m_xExternalBinding = _rxBinding; 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& ) { OSL_ENSURE( sal_False, "OBoundControlModel::connectExternalValueBinding: caught an exception!" ); } // tell the derivee onConnectedExternalValue(); // propagate our new value transferExternalValueToControl( ); // 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& ) { OSL_ENSURE( sal_False, "OBoundControlModel::connectExternalValueBinding: caught an exception while establishing the binding as validator!" ); } } } //-------------------------------------------------------------------- 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 parent, again. This was suspended while we had // an external value binding in place. if ( m_xParent.is() ) startLoadListening( ); // TODO: anything to care for here? Changing values? Falling back to a // database binding if appropriate? // tell the derivee onDisconnectedExternalValue(); } //-------------------------------------------------------------------- void SAL_CALL OBoundControlModel::setValueBinding( const Reference< XValueBinding >& _rxBinding ) throw (IncompatibleTypesException, RuntimeException) { ::osl::MutexGuard aGuard( m_aMutex ); OSL_PRECOND( m_bSupportsExternalBinding, "OBoundControlModel::setValueBinding: How did you reach this method?" ); // the interface for this method should not have been exposed if we do not // support binding to external data if ( _rxBinding.is() && !approveValueBinding( _rxBinding ) ) { throw IncompatibleTypesException( FRM_RES_STRING( RID_STR_INCOMPATIBLE_TYPES ), *this ); } // disconnect from the old binding if ( hasExternalValueBinding() ) disconnectExternalValueBinding( ); // connect to the new binding if ( _rxBinding.is() ) connectExternalValueBinding( _rxBinding ); } //-------------------------------------------------------------------- Reference< XValueBinding > SAL_CALL OBoundControlModel::getValueBinding( ) throw (RuntimeException) { ::osl::MutexGuard aGuard( m_aMutex ); OSL_PRECOND( m_bSupportsExternalBinding, "OBoundControlModel::getValueBinding: How did you reach this method?" ); // the interface for this method should not have been exposed if we do not // support binding to external data return m_xExternalBinding; } //-------------------------------------------------------------------- void SAL_CALL OBoundControlModel::modified( const EventObject& _rEvent ) throw ( RuntimeException ) { ::osl::MutexGuard aGuard( m_aMutex ); OSL_PRECOND( hasExternalValueBinding(), "OBoundControlModel::modified: Where did this come from?" ); if ( !m_bTransferingValue && hasExternalValueBinding() && ( m_xExternalBinding == _rEvent.Source ) ) { transferExternalValueToControl( ); } } //-------------------------------------------------------------------- void OBoundControlModel::transferDbValueToControl( ) { setControlValue( translateDbColumnToControlValue(), eDbColumnBinding ); } //------------------------------------------------------------------------------ void OBoundControlModel::transferExternalValueToControl( ) { try { setControlValue( translateExternalValueToControlValue( ), eExternalBinding ); } catch( const Exception& ) { OSL_ENSURE( sal_False, "OBoundControlModel::transferExternalValueToControl: caught an exception!" ); } } //------------------------------------------------------------------------------ void OBoundControlModel::transferControlValueToExternal( ) { OSL_PRECOND( m_bSupportsExternalBinding && hasExternalValueBinding(), "OBoundControlModel::transferControlValueToExternal: precondition not met!" ); if ( m_xExternalBinding.is() ) { m_bTransferingValue = sal_True; try { m_xExternalBinding->setValue( translateControlValueToExternalValue( ) ); } catch( const IncompatibleTypesException& ) { OSL_ENSURE( sal_False, "OBoundControlModel::transferControlValueToExternal: could not commit the value (incomptible types)!" ); } catch( const NoSupportException& ) { OSL_ENSURE( sal_False, "OBoundControlModel::transferControlValueToExternal: could not commit the value (no support)!" ); } catch( const DisposedException& ) { OSL_ENSURE( sal_False, "OBoundControlModel::transferControlValueToExternal: could not commit the value: the binding is already disposed!!" ); } catch( const Exception& e ) { #if OSL_DEBUG_LEVEL > 0 ::rtl::OString sMessage( "OBoundControlModel::transferControlValueToExternal: could not commit the value: unexpected exception type!\n" ); sMessage += "message:\n"; sMessage += ::rtl::OString( e.Message.getStr(), e.Message.getLength(), RTL_TEXTENCODING_ASCII_US ); OSL_ENSURE( sal_False, sMessage.getStr() ); #endif } m_bTransferingValue = sal_False; } } // ----------------------------------------------------------------------------- Any OBoundControlModel::translateExternalValueToControlValue( ) const { OSL_PRECOND( m_bSupportsExternalBinding && hasExternalValueBinding(), "OBoundControlModel::translateExternalValueToControlValue: precondition not met!" ); // determine the type of our value property if ( m_sValuePropertyName.getLength() && m_xAggregateSet.is() ) { Reference< XPropertySetInfo > xPropInfo( m_xAggregateSet->getPropertySetInfo( ) ); if ( xPropInfo.is() ) { Property aValuePropertyDescriptor = xPropInfo->getPropertyByName( m_sValuePropertyName ); // if the external binding does support this type, ask for it if ( m_xExternalBinding.is() && m_xExternalBinding->supportsType( aValuePropertyDescriptor.Type ) ) { Any aExternalValue = m_xExternalBinding->getValue( aValuePropertyDescriptor.Type ); // if the external value is VOID, and our value property is not allowed to be VOID, // then default-construct a value if ( !aExternalValue.hasValue() && ( ( aValuePropertyDescriptor.Attributes & PropertyAttribute::MAYBEVOID ) == 0 ) ) aExternalValue.setValue( NULL, aValuePropertyDescriptor.Type ); // outta here return aExternalValue; } } } OSL_ENSURE( sal_False, "OBoundControlModel::translateExternalValueToControlValue: no default implementation available!" ); return Any(); } //------------------------------------------------------------------------------ Any OBoundControlModel::translateControlValueToExternalValue( ) const { return getControlValue( ); } //------------------------------------------------------------------------------ Any OBoundControlModel::translateControlValueToValidatableValue( ) const { return translateControlValueToExternalValue(); } //------------------------------------------------------------------------------ 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; } //-------------------------------------------------------------------- Any SAL_CALL OBoundControlModel::getCurrentValue( ) throw (RuntimeException) { return translateControlValueToValidatableValue(); } //-------------------------------------------------------------------- void SAL_CALL OBoundControlModel::addFormComponentValidityListener( const Reference< validation::XFormComponentValidityListener >& Listener ) throw (NullPointerException, RuntimeException) { if ( !Listener.is() ) throw NullPointerException(); m_pFormComponentListeners->addListener( Listener ); } //-------------------------------------------------------------------- void SAL_CALL OBoundControlModel::removeFormComponentValidityListener( const Reference< validation::XFormComponentValidityListener >& Listener ) throw (NullPointerException, RuntimeException) { if ( !Listener.is() ) throw NullPointerException(); m_pFormComponentListeners->removeListener( 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_pFormComponentListeners->notify( EventObject( *this ) ); } } catch( const Exception& ) { OSL_ENSURE( sal_False, "OBoundControlModel::recheckValidity: caught an exception!" ); } } //------------------------------------------------------------------------------ void OBoundControlModel::fillProperties( Sequence< Property >& /* [out] */ _rProps, Sequence< Property >& /* [out] */ _rAggregateProps ) const { BEGIN_DESCRIBE_PROPERTIES( 4, 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 ); END_DESCRIBE_PROPERTIES() } // ----------------------------------------------------------------------------- //......................................................................... } //... namespace frm .......................................................