Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  FormComponent.cxx

  Sprache: C
 

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <componenttools.hxx>
#include <FormComponent.hxx>
#include <strings.hrc>
#include <frm_resource.hxx>
#include <property.hxx>
#include <services.hxx>

#include <com/sun/star/awt/XTextComponent.hpp>
#include <com/sun/star/awt/XWindow.hpp>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/form/FormComponentType.hpp>
#include <com/sun/star/form/XForm.hpp>
#include <com/sun/star/form/XLoadable.hpp>
#include <com/sun/star/form/binding/IncompatibleTypesException.hpp>
#include <com/sun/star/io/IOException.hpp>
#include <com/sun/star/io/XMarkableStream.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
#include <com/sun/star/sdb/XRowSetChangeBroadcaster.hpp>
#include <com/sun/star/sdb/XRowSetSupplier.hpp>
#include <com/sun/star/sdbc/ColumnValue.hpp>
#include <com/sun/star/sdbc/DataType.hpp>
#include <com/sun/star/sdbc/SQLException.hpp>
#include <com/sun/star/util/VetoException.hpp>
#include <com/sun/star/util/XModifyBroadcaster.hpp>

#include <comphelper/basicio.hxx>
#include <comphelper/guarding.hxx>
#include <comphelper/property.hxx>
#include <connectivity/dbtools.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <tools/debug.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <sal/log.hxx>

#include <algorithm>

namespace frm
{
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::sdb;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::sdbcx;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::form;
using namespace ::com::sun::star::awt;
using namespace ::com::sun::star::io;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::form::binding;
using namespace ::com::sun::star::form::validation;
using namespace ::dbtools;
using namespace ::comphelper;

// FieldChangeNotifier
void ControlModelLock::impl_notifyAll_nothrow()
{
    m_rModel.firePropertyChanges( m_aHandles, m_aOldValues, m_aNewValues, OControlModel::LockAccess() );
}

void ControlModelLock::addPropertyNotification( const sal_Int32 _nHandle, const Any&&nbsp;_rOldValue, const Any& _rNewValue )
{
    assert( m_aHandles.size() == m_aOldValues.size() && m_aOldValues.size() == m_aNewValues.size() );

    m_aHandles.push_back( _nHandle );
    m_aOldValues.push_back( _rOldValue );
    m_aNewValues.push_back( _rNewValue );
}

namespace {

class FieldChangeNotifier
{
public:
    explicit FieldChangeNotifier(ControlModelLock& _rLock)
        : m_rLock( _rLock )
        , m_rModel( dynamic_cast< OBoundControlModel& >( _rLock.getModel() ) )
    {
        m_xOldField = m_rModel.getField();
    }

    ~FieldChangeNotifier()
    {
        Reference< XPropertySet > xNewField( m_rModel.getField() );
        if ( m_xOldField != xNewField )
            m_rLock.addPropertyNotification( PROPERTY_ID_BOUNDFIELD, Any( m_xOldField ), Any( xNewField ) );
    }

private:
    ControlModelLock&           m_rLock;
    OBoundControlModel&         m_rModel;
    Reference< XPropertySet >   m_xOldField;
};

}

// base class for form layer controls
OControl::OControl( const Reference< XComponentContext >& _rxContext, const OUString&&nbsp;_rAggregateService, const bool _bSetDelegator )
            :OComponentHelper(m_aMutex)
            ,m_xContext( _rxContext )
{
    // Aggregate VCL Control
    // Increment the RefCount for aggregates, because the aggregate by itself increments the RefCount in the setDelegator
    osl_atomic_increment( &m_refCount );
    {
        m_xAggregate.set(_rxContext->getServiceManager()->createInstanceWithContext(_rAggregateService, _rxContext), css::uno::UNO_QUERY);
        m_xControl.set(m_xAggregate, css::uno::UNO_QUERY);
    }
    osl_atomic_decrement( &m_refCount );

    if ( _bSetDelegator )
        doSetDelegator();
}

OControl::~OControl()
{
    doResetDelegator();
}

void OControl::doResetDelegator()
{
    if ( m_xAggregate.is() )
        m_xAggregate->setDelegator( nullptr );
}

void OControl::doSetDelegator()
{
    osl_atomic_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 ) );
    }
    osl_atomic_decrement( &m_refCount );
}

// UNO Binding
Any SAL_CALL OControl::queryAggregation( const Type& _rType )
{
    // ask the base class
    Any aReturn( OComponentHelper::queryAggregation(_rType) );
    // ask our own interfaces
    if (!aReturn.hasValue())
    {
        aReturn = OControl_BASE::queryInterface(_rType);
        // ask our aggregate
        if (!aReturn.hasValue() && m_xAggregate.is())
            aReturn = m_xAggregate->queryAggregation(_rType);
    }

    return aReturn;
}

Sequence<sal_Int8> SAL_CALL OControl::getImplementationId()
{
    return css::uno::Sequence<sal_Int8>();
}

Sequence<Type> SAL_CALL OControl::getTypes()
{
    TypeBag aTypes( _getTypes() );

    if (auto xProv = query_aggregation<XTypeProvider>(m_xAggregate))
        aTypes.addTypes( xProv->getTypes() );

    return aTypes.getTypes();
}

Sequence<Type> OControl::_getTypes()
{
    return TypeBag( OComponentHelper::getTypes(), OControl_BASE::getTypes() ).getTypes();
}

// OComponentHelper
void OControl::disposing()
{
    OComponentHelper::disposing();

    m_aWindowStateGuard.attach( nullptr, nullptr );

    if (auto xComp = query_aggregation<XComponent>(m_xAggregate))
        xComp->dispose();
}

// XServiceInfo
sal_Bool SAL_CALL OControl::supportsService(const OUString& _rsServiceName)
{
    return cppu::supportsService(this, _rsServiceName);
}

Sequence< OUString > OControl::getAggregateServiceNames() const
{
    Sequence< OUString > aAggServices;
    if (auto xInfo = query_aggregation<XServiceInfo>(m_xAggregate))
        aAggServices = xInfo->getSupportedServiceNames();

    return aAggServices;
}

Sequence<OUString> SAL_CALL OControl::getSupportedServiceNames()
{
    // no own supported service names
    return getAggregateServiceNames();
}

// XEventListener
void SAL_CALL OControl::disposing(const css::lang::EventObject& _rEvent)
{
    auto xAggAsIface = query_aggregation<XInterface>(m_xAggregate);

    // does the disposing come from the aggregate?
    if (xAggAsIface != Reference< XInterface >(_rEvent.Source, UNO_QUERY))
    {   // no -> forward it
        if (auto xListener = query_aggregation<css::lang::XEventListener>(m_xAggregate))
            xListener->disposing(_rEvent);
    }
}

// XControl
void SAL_CALL OControl::setContext(const Reference< XInterface >& Context)
{
    if (m_xControl.is())
        m_xControl->setContext(Context);
}

Reference< XInterface > SAL_CALL OControl::getContext()
{
    return m_xControl.is() ? m_xControl->getContext() : Reference< XInterface >();
}

void OControl::impl_resetStateGuard_nothrow()
{
    Reference< XWindow2 > xWindow;
    Reference< XControlModel > xModel;
    try
    {
        xWindow.set( getPeer(), UNO_QUERY );
        xModel = getModel();
    }
    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("forms.component");
    }
    m_aWindowStateGuard.attach( xWindow, xModel );
}

void SAL_CALL OControl::createPeer(const Reference<XToolkit>& _rxToolkit, const Reference<XWindowPeer>& _rxParent)
{
    if ( m_xControl.is() )
    {
        m_xControl->createPeer( _rxToolkit, _rxParent );
        impl_resetStateGuard_nothrow();
    }
}

Reference<XWindowPeer> SAL_CALL OControl::getPeer()
{
    return m_xControl.is() ? m_xControl->getPeer() : Reference<XWindowPeer>();
}

sal_Bool SAL_CALL OControl::setModel(const Reference<XControlModel>& Model)
{
    if ( !m_xControl.is() )
        return false;

    bool bSuccess = m_xControl->setModel( Model );
    impl_resetStateGuard_nothrow();
    return bSuccess;
}

Reference<XControlModel> SAL_CALL OControl::getModel()
{
    return m_xControl.is() ? m_xControl->getModel() : Reference<XControlModel>();
}

Reference<XView> SAL_CALL OControl::getView()
{
    return m_xControl.is() ? m_xControl->getView() : Reference<XView>();
}

void SAL_CALL OControl::setDesignMode(sal_Bool bOn)
{
    if (m_xControl.is())
        m_xControl->setDesignMode(bOn);
}

sal_Bool SAL_CALL OControl::isDesignMode()
{
    return !m_xControl.is() || m_xControl->isDesignMode();
}

sal_Bool SAL_CALL OControl::isTransparent()
{
    return !m_xControl.is() || m_xControl->isTransparent();
}

OBoundControl::OBoundControl( const Reference< XComponentContext >& _rxContext,
            const OUString& _rAggregateService, const bool _bSetDelegator )
    :OControl( _rxContext, _rAggregateService, _bSetDelegator )
    ,m_bLocked(false)
{
}

OBoundControl::~OBoundControl()
{
}

Sequence< Type> OBoundControl::_getTypes()
{
    return TypeBag( OControl::_getTypes(), OBoundControl_BASE::getTypes() ).getTypes();
}

Any SAL_CALL OBoundControl::queryAggregation(const Type& _rType)
{
    Any aReturn;

    // XTypeProvider first - don't ask the OBoundControl_BASE, it would deliver incomplete types
    if ( _rType.equals( cppu::UnoType<XTypeProvider>::get() ) )
        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()
{
    return m_bLocked;
}

void SAL_CALL OBoundControl::setLock(sal_Bool _bLock)
{
    if (m_bLocked == bool(_bLock))
        return;

    osl::MutexGuard aGuard(m_aMutex);
    _setLock(_bLock);
    m_bLocked = _bLock;
}

void OBoundControl::_setLock(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 )
{
    return OControl::setModel( _rxModel );
}

void SAL_CALL OBoundControl::disposing(const EventObject& Source)
{
    // just disambiguate
    OControl::disposing(Source);
}

void OBoundControl::disposing()
{
    OControl::disposing();
}

// OControlModel
Sequence<sal_Int8> SAL_CALL OControlModel::getImplementationId()
{
    return css::uno::Sequence<sal_Int8>();
}

Sequence<Type> SAL_CALL OControlModel::getTypes()
{
    TypeBag aTypes( _getTypes() );

    if (auto xProv = query_aggregation<XTypeProvider>(m_xAggregate))
        aTypes.addTypes( xProv->getTypes() );

    return aTypes.getTypes();
}

Sequence<Type> OControlModel::_getTypes()
{
    return TypeBag( OComponentHelper::getTypes(),
        OPropertySetAggregationHelper::getTypes(),
        OControlModel_BASE::getTypes()
    ).getTypes();
}

Any SAL_CALL OControlModel::queryAggregation(const Type& _rType)
{
    // 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(cppu::UnoType<XCloneable>::get()))
                aReturn = m_xAggregate->queryAggregation(_rType);
        }
    }
    return aReturn;
}

void OControlModel::readHelpTextCompatibly(const css::uno::Reference< css::io::XObjectInputStream >& _rxInStream)
{
    OUString sHelpText;
    ::comphelper::operator>>( _rxInStream, sHelpText);
    try
    {
        if (m_xAggregateSet.is())
            m_xAggregateSet->setPropertyValue(PROPERTY_HELPTEXT, Any(sHelpText));
    }
    catch(const Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("forms.component");
        SAL_WARN("forms.component""OControlModel::readHelpTextCompatibly: could not forward the property value to the aggregate!");
    }
}

void OControlModel::writeHelpTextCompatibly(const css::uno::Reference< css::io::XObjectOutputStream >& _rxOutStream)
{
    OUString sHelpText;
    try
    {
        if (m_xAggregateSet.is())
            m_xAggregateSet->getPropertyValue(PROPERTY_HELPTEXT) >>= sHelpText;
    }
    catch(const Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("forms.component");
        SAL_WARN("forms.component""OControlModel::writeHelpTextCompatibly: could not retrieve the property value from the aggregate!");
    }
    ::comphelper::operator<<( _rxOutStream, sHelpText);
}

OControlModel::OControlModel(
            const Reference<XComponentContext>& _rxContext,
            const OUString& _rUnoControlModelTypeName,
            const OUString& rDefault, const bool _bSetDelegator)
    :OComponentHelper(m_aMutex)
    ,OPropertySetAggregationHelper(OComponentHelper::rBHelper)
    ,m_xContext( _rxContext )
    ,m_lockCount( 0 )
    ,m_aPropertyBagHelper( *this )
    ,m_nTabIndex(FRM_DEFAULT_TABINDEX)
    ,m_nClassId(FormComponentType::CONTROL)
    ,m_bNativeLook( false )
    ,m_bStandardTheme( false )
    ,m_bGenerateVbEvents( false )
    ,m_nControlTypeinMSO(0) // 0 : default value is create from AOO
    ,m_nObjIDinMSO(INVALID_OBJ_ID_IN_MSO)
    // form controls are usually embedded into documents, not dialogs, and in documents
    // the native look is ugly...
    // #i37342#
{
    if (_rUnoControlModelTypeName.isEmpty())  // the is a model we have to aggregate
        return;

    osl_atomic_increment(&m_refCount);
    {
        m_xAggregate.set(m_xContext->getServiceManager()->createInstanceWithContext(_rUnoControlModelTypeName, m_xContext), UNO_QUERY);
        setAggregation(m_xAggregate);

        if ( m_xAggregateSet.is() )
        {
            try
            {
                if ( !rDefault.isEmpty() )
                    m_xAggregateSet->setPropertyValue( PROPERTY_DEFAULTCONTROL, Any( rDefault ) );
            }
            catchconst Exception& )
            {
                TOOLS_WARN_EXCEPTION("forms.component",  "OControlModel::OControlModel");
            }
        }
    }
    if (_bSetDelegator)
        doSetDelegator();

    // Refcount is at NULL again
    osl_atomic_decrement(&m_refCount);
}

OControlModel::OControlModel( const OControlModel* _pOriginal, const Reference< XComponentContext>& _rxFactory, const bool _bCloneAggregate, const bool _bSetDelegator )
    :OComponentHelper( m_aMutex )
    ,OPropertySetAggregationHelper( OComponentHelper::rBHelper )
    ,m_xContext( _rxFactory )
    ,m_lockCount( 0 )
    ,m_aPropertyBagHelper( *this )
    ,m_nTabIndex( FRM_DEFAULT_TABINDEX )
    ,m_nClassId( FormComponentType::CONTROL )
{
    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;
    m_bStandardTheme = _pOriginal->m_bStandardTheme;
    m_bGenerateVbEvents = _pOriginal->m_bGenerateVbEvents;
    m_nControlTypeinMSO = _pOriginal->m_nControlTypeinMSO;
    m_nObjIDinMSO = _pOriginal->m_nObjIDinMSO;

    if ( !_bCloneAggregate )
        return;

    // temporarily increment refcount because of temporary references to ourself in the following
    osl_atomic_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
    osl_atomic_decrement( &m_refCount );
}

OControlModel::~OControlModel()
{
    // release the aggregate
    doResetDelegator( );
}

void OControlModel::clonedFrom( const OControlModel* /*_pOriginal*/ )
{
    // nothing to do in this base class
}

void OControlModel::doResetDelegator()
{
    if (m_xAggregate.is())
        m_xAggregate->setDelegator(nullptr);
}

void OControlModel::doSetDelegator()
{
    osl_atomic_increment(&m_refCount);
    if (m_xAggregate.is())
    {
        m_xAggregate->setDelegator(static_cast<XWeak*>(this));
    }
    osl_atomic_decrement(&m_refCount);
}

// XChild
Reference< XInterface > SAL_CALL OControlModel::getParent()
{
    return m_xParent;
}

void SAL_CALL OControlModel::setParent(const Reference< XInterface >& _rxParent)
{
    osl::MutexGuard aGuard(m_aMutex);

    Reference<XComponent> xComp(m_xParent, UNO_QUERY);
    if (xComp.is())
        xComp->removeEventListener(static_cast<XPropertiesChangeListener*>(this));

    m_xParent = _rxParent;
    xComp.set(m_xParent, css::uno::UNO_QUERY);

    if ( xComp.is() )
        xComp->addEventListener(static_cast<XPropertiesChangeListener*>(this));
}

// XNamed
OUString SAL_CALL OControlModel::getName()
{
    OUString aReturn;
    try
    {
        OPropertySetHelper::getFastPropertyValue(PROPERTY_ID_NAME) >>= aReturn;
    }
    catch (const css::beans::UnknownPropertyException&)
    {
        css::uno::Any a(cppu::getCaughtException());
        throw WrappedTargetRuntimeException(
            u"OControlModel::getName"_ustr,
            *this,
            a
        );
    }
    return aReturn;
}

void SAL_CALL OControlModel::setName(const OUString& _rName)
{
    try
    {
        setFastPropertyValue(PROPERTY_ID_NAME, Any(_rName));
    }
    catch (const css::beans::UnknownPropertyException&)
    {
        css::uno::Any a(cppu::getCaughtException());
        throw WrappedTargetRuntimeException(
            u"OControlModel::setName"_ustr,
            *this,
            a
        );
    }
}

// XServiceInfo
sal_Bool SAL_CALL OControlModel::supportsService(const OUString& _rServiceName)
{
    return cppu::supportsService(this, _rServiceName);
}

Sequence< OUString > OControlModel::getAggregateServiceNames() const
{
    Sequence< OUString > aAggServices;
    if (auto xInfo = query_aggregation<XServiceInfo>(m_xAggregate))
        aAggServices = xInfo->getSupportedServiceNames();
    return aAggServices;
}

Sequence<OUString> SAL_CALL OControlModel::getSupportedServiceNames()
{
    return ::comphelper::concatSequences(
        getAggregateServiceNames(),
        getSupportedServiceNames_Static()
    );
}

Sequence< OUString > OControlModel::getSupportedServiceNames_Static()
{
    return { FRM_SUN_FORMCOMPONENT, u"com.sun.star.form.FormControlModel"_ustr };
}

// XEventListener
void SAL_CALL OControlModel::disposing(const css::lang::EventObject& _rSource)
{
    // release the parent
    if (_rSource.Source == m_xParent)
    {
        osl::MutexGuard aGuard(m_aMutex);
        m_xParent = nullptr;
    }
    else
    {
        if (auto xEvtLst = query_aggregation<css::lang::XEventListener>(m_xAggregate))
        {
            osl::MutexGuard aGuard(m_aMutex);
            xEvtLst->disposing(_rSource);
        }
    }
}

// OComponentHelper
void OControlModel::disposing()
{
    OPropertySetAggregationHelper::disposing();

    if (auto xComp = query_aggregation<css::lang::XComponent>(m_xAggregate))
        xComp->dispose();

    setParent(Reference<XFormComponent>());

    m_aPropertyBagHelper.dispose();
}

void OControlModel::writeAggregate( const Reference< XObjectOutputStream >& _rxOutStream ) const
{
    if (auto xPersist = query_aggregation<XPersistObject>(m_xAggregate))
        xPersist->write( _rxOutStream );
}

void OControlModel::readAggregate( const Reference< XObjectInputStream >& _rxInStream )
{
    if (auto xPersist = query_aggregation<XPersistObject>(m_xAggregate))
        xPersist->read( _rxInStream );
}

void SAL_CALL OControlModel::write(const Reference<css::io::XObjectOutputStream>& _rxOutStream)
{
    osl::MutexGuard aGuard(m_aMutex);

    // 1. writing the UnoControls
    Reference<css::io::XMarkableStream> xMark(_rxOutStream, UNO_QUERY);
    if ( !xMark.is() )
    {
        throw IOException(
            ResourceManager::loadString(RID_STR_INVALIDSTREAM),
            static_cast< ::cppu::OWeakObject* >( this )
        );
    }

    sal_Int32 nMark = xMark->createMark();
    sal_Int32 nLen = 0;

    _rxOutStream->writeLong(nLen);

    writeAggregate( _rxOutStream );

    // determining the length
    nLen = xMark->offsetToMark(nMark) - 4;
    xMark->jumpToMark(nMark);
    _rxOutStream->writeLong(nLen);
    xMark->jumpToFurthest();
    xMark->deleteMark(nMark);

    // 2. writing a version number
    _rxOutStream->writeShort(0x0003);

    // 3. writing the general properties
    ::comphelper::operator<<( _rxOutStream, m_aName);
    _rxOutStream->writeShort(m_nTabIndex);
    ::comphelper::operator<<( _rxOutStream, m_aTag); // 3rd 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<css::io::XObjectInputStream>& InStream)
{
    osl::MutexGuard aGuard(m_aMutex);

    Reference<css::io::XMarkableStream> xMark(InStream, UNO_QUERY);
    if ( !xMark.is() )
    {
        throw IOException(
            ResourceManager::loadString(RID_STR_INVALIDSTREAM),
            static_cast< ::cppu::OWeakObject* >( this )
        );
    }

    // 1. reading the UnoControls
    sal_Int32 nLen = InStream->readLong();
    if (nLen)
    {
        sal_Int32 nMark = xMark->createMark();

        try
        {
            readAggregate( InStream );
        }

        catchconst Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("forms.component");
        }

        xMark->jumpToMark(nMark);
        InStream->skipBytes(nLen);
        xMark->deleteMark(nMark);
    }

    // 2. reading the version number
    sal_uInt16 nVersion = InStream->readShort();

    // 3. reading the general properties
    ::comphelper::operator>>( InStream, m_aName);
    m_nTabIndex  = InStream->readShort();

    if (nVersion > 0x0002)
        ::comphelper::operator>>( InStream, m_aTag);

    // we had a version where we wrote the help text
    if (nVersion == 0x0004)
        readHelpTextCompatibly(InStream);

    DBG_ASSERT(nVersion < 5, "OControlModel::read : suspicious version number !");
    // 4 was the version where we wrote the help text
    // later versions shouldn't exist (see write for a detailed comment)
}

PropertyState OControlModel::getPropertyStateByHandle( sal_Int32 _nHandle )
{
    // simply compare the current and the default value
    Any aCurrentValue = getPropertyDefaultByHandle( _nHandle );
    Any aDefaultValue;  getFastPropertyValue( aDefaultValue, _nHandle );

    bool bEqual = aCurrentValue == aDefaultValue;
    return bEqual ? PropertyState_DEFAULT_VALUE : PropertyState_DIRECT_VALUE;
}

void OControlModel::setPropertyToDefaultByHandle( sal_Int32 _nHandle)
{
    Any aDefault = getPropertyDefaultByHandle( _nHandle );

    Any aConvertedValue, aOldValue;
    if ( convertFastPropertyValue( aConvertedValue, aOldValue, _nHandle, aDefault ) )
    {
        setFastPropertyValue_NoBroadcast( _nHandle, aConvertedValue );
        // TODO: fire the property change
    }
}

Any OControlModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
{
    Any aReturn;
    switch ( _nHandle )
    {
        case PROPERTY_ID_NAME:
        case PROPERTY_ID_TAG:
            aReturn <<= 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 <<= true;
            break;
        case PROPERTY_ID_STANDARD_THEME:
        case PROPERTY_ID_GENERATEVBAEVENTS:
            aReturn <<= false;
            break;
        // added for exporting OCX control
        case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
            aReturn <<= sal_Int16(0);
            break;
        case PROPERTY_ID_OBJ_ID_IN_MSO:
            aReturn <<= sal_uInt16(INVALID_OBJ_ID_IN_MSO);
            break;
        default:
            if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
                m_aPropertyBagHelper.getDynamicPropertyDefaultByHandle( _nHandle, aReturn );
            else
                SAL_WARN("forms.component",  "OControlModel::convertFastPropertyValue: unknown handle " << _nHandle);
    }
    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 <<= m_bNativeLook;
            break;
        case PROPERTY_ID_STANDARD_THEME:
            _rValue <<= m_bStandardTheme;
            break;
        case PROPERTY_ID_GENERATEVBAEVENTS:
            _rValue <<= m_bGenerateVbEvents;
            break;
        // added for exporting OCX control
        case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
            _rValue <<= m_nControlTypeinMSO;
            break;
        case PROPERTY_ID_OBJ_ID_IN_MSO:
            _rValue <<= m_nObjIDinMSO;
            break;
        default:
            if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
                m_aPropertyBagHelper.getDynamicFastPropertyValue( _nHandle, _rValue );
            else
                OPropertySetAggregationHelper::getFastPropertyValue( _rValue, _nHandle );
            break;
    }
}

sal_Bool OControlModel::convertFastPropertyValue(
                        Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue)
{
    bool bModified(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;
        case PROPERTY_ID_STANDARD_THEME:
            bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bStandardTheme);
            break;
        case PROPERTY_ID_GENERATEVBAEVENTS:
            bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bGenerateVbEvents);
            break;
        // added for exporting OCX control
        case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
            bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_nControlTypeinMSO);
            break;
        case PROPERTY_ID_OBJ_ID_IN_MSO:
            bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_nObjIDinMSO);
            break;
        default:
            if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
                bModified = m_aPropertyBagHelper.convertDynamicFastPropertyValue( _nHandle, _rValue, _rConvertedValue, _rOldValue );
            else
                SAL_WARN("forms.component",  "OControlModel::convertFastPropertyValue: unknown handle " << _nHandle);
            break;
    }
    return bModified;
}

void OControlModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any&&nbsp;_rValue)
{
    switch (_nHandle)
    {
        case PROPERTY_ID_NAME:
            DBG_ASSERT(_rValue.getValueType() == cppu::UnoType<OUString>::get(),
                "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" );
            _rValue >>= m_aName;
            break;
        case PROPERTY_ID_TAG:
            DBG_ASSERT(_rValue.getValueType() == cppu::UnoType<OUString>::get(),
                "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" );
            _rValue >>= m_aTag;
            break;
        case PROPERTY_ID_TABINDEX:
            DBG_ASSERT(_rValue.getValueType() == cppu::UnoType<sal_Int16>::get(),
                "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" );
            _rValue >>= m_nTabIndex;
            break;
        case PROPERTY_ID_NATIVE_LOOK:
            OSL_VERIFY( _rValue >>= m_bNativeLook );
            break;
        case PROPERTY_ID_STANDARD_THEME:
            OSL_VERIFY( _rValue >>= m_bStandardTheme );
            break;
        case PROPERTY_ID_GENERATEVBAEVENTS:
            OSL_VERIFY( _rValue >>= m_bGenerateVbEvents );
            break;
        // added for exporting OCX control
        case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
            OSL_VERIFY( _rValue >>= m_nControlTypeinMSO );
            break;
        case PROPERTY_ID_OBJ_ID_IN_MSO:
            OSL_VERIFY( _rValue >>= m_nObjIDinMSO );
            break;
        default:
            if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
                m_aPropertyBagHelper.setDynamicFastPropertyValue( _nHandle, _rValue );
            else
                SAL_WARN("forms.component",  "OControlModel::setFastPropertyValue_NoBroadcast: unknown handle " << _nHandle );
            break;
    }
}

void OControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const
{
    _rProps.realloc(8);
    css::beans::Property* pProperties = _rProps.getArray();
    *pProperties++ = css::beans::Property(PROPERTY_CLASSID, PROPERTY_ID_CLASSID, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
    *pProperties++ = css::beans::Property(PROPERTY_NAME, PROPERTY_ID_NAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
    *pProperties++ = css::beans::Property(PROPERTY_NATIVE_LOOK, PROPERTY_ID_NATIVE_LOOK, cppu::UnoType<bool>::get(),
                                          css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT);
    *pProperties++ = css::beans::Property(PROPERTY_STANDARD_THEME, PROPERTY_ID_STANDARD_THEME, cppu::UnoType<bool>::get(),
                                          css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT);
    *pProperties++ = css::beans::Property(PROPERTY_TAG, PROPERTY_ID_TAG, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
    *pProperties++ = css::beans::Property(PROPERTY_GENERATEVBAEVENTS, PROPERTY_ID_GENERATEVBAEVENTS, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::TRANSIENT);
    *pProperties++ = css::beans::Property(PROPERTY_CONTROL_TYPE_IN_MSO, PROPERTY_ID_CONTROL_TYPE_IN_MSO, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
    *pProperties++ = css::beans::Property(PROPERTY_OBJ_ID_IN_MSO, PROPERTY_ID_OBJ_ID_IN_MSO, cppu::UnoType<cppu::UnoUnsignedShortType>::get(), css::beans::PropertyAttribute::BOUND);
    DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
}

void OControlModel::describeAggregateProperties( Sequence< Property >& /* [out] */ _rAggregateProps ) const
{
    if ( m_xAggregateSet.is() )
    {
        Reference< XPropertySetInfo > xPSI( m_xAggregateSet->getPropertySetInfo() );
        if ( xPSI.is() )
            _rAggregateProps = xPSI->getProperties();
    }
}

::osl::Mutex& OControlModel::getMutex()
{
    return m_aMutex;
}

void OControlModel::describeFixedAndAggregateProperties( Sequence< Property >& _out_rFixedProperties, Sequence< Property >& _out_rAggregateProperties ) const
{
    describeFixedProperties( _out_rFixedProperties );
    describeAggregateProperties( _out_rAggregateProperties );
}

Reference< XMultiPropertySet > OControlModel::getPropertiesInterface()
{
    return this;
}

Reference< XPropertySetInfo> SAL_CALL OControlModel::getPropertySetInfo()
{
    return createPropertySetInfo( getInfoHelper() );
}

::cppu::IPropertyArrayHelper& OControlModel::getInfoHelper()
{
    return m_aPropertyBagHelper.getInfoHelper();
}

void SAL_CALL OControlModel::addProperty( const OUString& _rName, ::sal_Int16 _nAttributes, const Any& _rInitialValue )
{
    m_aPropertyBagHelper.addProperty( _rName, _nAttributes, _rInitialValue );
}

void SAL_CALL OControlModel::removeProperty( const OUString& _rName )
{
    m_aPropertyBagHelper.removeProperty( _rName );
}

Sequence< PropertyValue > SAL_CALL OControlModel::getPropertyValues()
{
    return m_aPropertyBagHelper.getPropertyValues();
}

void SAL_CALL OControlModel::setPropertyValues( const Sequence< PropertyValue >& _rProps )
{
    m_aPropertyBagHelper.setPropertyValues( _rProps );
}

void OControlModel::lockInstance( LockAccess )
{
    m_aMutex.acquire();
    osl_atomic_increment( &m_lockCount );
}

oslInterlockedCount OControlModel::unlockInstance( LockAccess )
{
    OSL_ENSURE( m_lockCount > 0, "OControlModel::unlockInstance: not locked!" );
    oslInterlockedCount lockCount = osl_atomic_decrement( &m_lockCount );
    m_aMutex.release();
    return lockCount;
}

void OControlModel::firePropertyChanges( const std::vector< sal_Int32 >& _rHandles, const std::vector< Any >& _rOldValues,
                                        const std::vector< Any >& _rNewValues, LockAccess )
{
    OPropertySetHelper::fire(
        const_cast< std::vector< sal_Int32 >& >( _rHandles ).data(),
        _rNewValues.data(),
        _rOldValues.data(),
        _rHandles.size(),
        false
    );
}

// OBoundControlModel
Any SAL_CALL OBoundControlModel::queryAggregation( const Type& _rType )
{
    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< XComponentContext>& _rxFactory,
        const OUString& _rUnoControlModelTypeName, const OUString& _rDefault,
        const bool _bCommitable, const bool _bSupportExternalBinding, const bool _bSupportsValidation )
    :OControlModel( _rxFactory, _rUnoControlModelTypeName, _rDefault, false )
    ,OPropertyChangeListener()
    ,m_nValuePropertyAggregateHandle( -1 )
    ,m_nFieldType( DataType::OTHER )
    ,m_bValuePropertyMayBeVoid( false )
    ,m_aResetHelper( *this, m_aMutex )
    ,m_aUpdateListeners(m_aMutex)
    ,m_aFormComponentListeners( m_aMutex )
    ,m_bInputRequired( false )
    ,m_bFormListening( false )
    ,m_bLoaded(false)
    ,m_bRequired(false)
    ,m_bCommitable(_bCommitable)
    ,m_bSupportsExternalBinding( _bSupportExternalBinding )
    ,m_bSupportsValidation( _bSupportsValidation )
    ,m_bForwardValueChanges(true)
    ,m_bTransferringValue( false )
    ,m_bIsCurrentValueValid( true )
    ,m_bBindingControlsRO( false )
    ,m_bBindingControlsEnable( false )
    ,m_eControlValueChangeInstigator( eOther )
    ,m_aLabelServiceName(FRM_SUN_COMPONENT_FIXEDTEXT)
{
    // start property listening at the aggregate
    implInitAggMultiplexer( );
}

OBoundControlModel::OBoundControlModel(
        const OBoundControlModel* _pOriginal, const Reference< XComponentContext>& _rxFactory )
    :OControlModel( _pOriginal, _rxFactory, truefalse )
    ,OPropertyChangeListener()
    ,m_nValuePropertyAggregateHandle( _pOriginal->m_nValuePropertyAggregateHandle )
    ,m_nFieldType( DataType::OTHER )
    ,m_bValuePropertyMayBeVoid( _pOriginal->m_bValuePropertyMayBeVoid )
    ,m_aResetHelper( *this, m_aMutex )
    ,m_aUpdateListeners( m_aMutex )
    ,m_aFormComponentListeners( m_aMutex )
    ,m_xValidator( _pOriginal->m_xValidator )
    ,m_bInputRequired( false )
    ,m_bFormListening( false )
    ,m_bLoaded( false )
    ,m_bRequired( false )
    ,m_bCommitable( _pOriginal->m_bCommitable )
    ,m_bSupportsExternalBinding( _pOriginal->m_bSupportsExternalBinding )
    ,m_bSupportsValidation( _pOriginal->m_bSupportsValidation )
    ,m_bForwardValueChanges( true )
    ,m_bTransferringValue( false )
    ,m_bIsCurrentValueValid( _pOriginal->m_bIsCurrentValueValid )
    ,m_bBindingControlsRO( false )
    ,m_bBindingControlsEnable( false )
    ,m_eControlValueChangeInstigator( eOther )
{
    // start property listening at the aggregate
    implInitAggMultiplexer( );
    m_aLabelServiceName = _pOriginal->m_aLabelServiceName;
    m_sValuePropertyName = _pOriginal->m_sValuePropertyName;
    m_nValuePropertyAggregateHandle = _pOriginal->m_nValuePropertyAggregateHandle;
    m_bValuePropertyMayBeVoid = _pOriginal->m_bValuePropertyMayBeVoid;
    m_aValuePropertyType = _pOriginal->m_aValuePropertyType;
    m_aControlSource = _pOriginal->m_aControlSource;
    m_bInputRequired = _pOriginal->m_bInputRequired;
    // m_xLabelControl, though being a property, is not to be cloned, not even the reference will be transferred.
    // (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 = nullptr;
    }
}

void OBoundControlModel::clonedFrom( const OControlModel* _pOriginal )
{
    const OBoundControlModel* pBoundOriginal = static_castconst 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()) )
        return;

    try
    {
        setValueBinding( pBoundOriginal->m_xExternalBinding );
    }

    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("forms.component");
    }
}

void OBoundControlModel::implInitAggMultiplexer( )
{
    osl_atomic_increment( &m_refCount );
    if ( m_xAggregateSet.is() )
    {
        m_pAggPropMultiplexer = new OPropertyChangeMultiplexer( this, m_xAggregateSet, false );
    }

    osl_atomic_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.isEmpty() )
            m_pAggPropMultiplexer->addProperty( m_sValuePropertyName );
    }
}

void OBoundControlModel::initOwnValueProperty( const OUString& i_rValuePropertyName )
{
    OSL_PRECOND( m_sValuePropertyName.isEmpty() && -1 == m_nValuePropertyAggregateHandle,
        "OBoundControlModel::initOwnValueProperty: value property is already initialized!" );
    OSL_ENSURE( !i_rValuePropertyName.isEmpty(), "OBoundControlModel::initOwnValueProperty: invalid property name!" );
    m_sValuePropertyName = i_rValuePropertyName;
}

void OBoundControlModel::initValueProperty( const OUString& _rValuePropertyName, sal_Int32 _nValuePropertyExternalHandle )
{
    OSL_PRECOND( m_sValuePropertyName.isEmpty() && -1 == m_nValuePropertyAggregateHandle,
        "OBoundControlModel::initValueProperty: value property is already initialized!" );
    OSL_ENSURE( !_rValuePropertyName.isEmpty(), "OBoundControlModel::initValueProperty: invalid property name!" );
    OSL_ENSURE( _nValuePropertyExternalHandle != -1, "OBoundControlModel::initValueProperty: invalid property handle!" );

    m_sValuePropertyName = _rValuePropertyName;
    m_nValuePropertyAggregateHandle = getOriginalHandle( _nValuePropertyExternalHandle );
    OSL_ENSURE( m_nValuePropertyAggregateHandle != -1, "OBoundControlModel::initValueProperty: unable to find the original handle!" );

    if ( m_nValuePropertyAggregateHandle != -1 )
    {
        Reference< XPropertySetInfo > xPropInfo( m_xAggregateSet->getPropertySetInfo(), UNO_SET_THROW );
        Property aValuePropDesc = xPropInfo->getPropertyByName( m_sValuePropertyName );
        m_aValuePropertyType = aValuePropDesc.Type;
        m_bValuePropertyMayBeVoid = ( aValuePropDesc.Attributes & PropertyAttribute::MAYBEVOID ) != 0;
    }

    // start listening for changes at the value property
    implInitValuePropertyListening( );
}

void OBoundControlModel::suspendValueListening( )
{
    OSL_PRECOND( !m_sValuePropertyName.isEmpty(), "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.isEmpty(), "OBoundControlModel::resumeValueListening: don't have a value property!" );
    OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::resumeValueListening: I *am* not listening at all!" );
    OSL_PRECOND( !m_pAggPropMultiplexer || m_pAggPropMultiplexer->locked(), "OBoundControlModel::resumeValueListening: listening not suspended currently!" );
    if ( m_pAggPropMultiplexer )
        m_pAggPropMultiplexer->unlock();
}

Sequence< Type > OBoundControlModel::_getTypes()
{
    TypeBag aTypes(
        OControlModel::_getTypes(),
        OBoundControlModel_BASE1::getTypes()
    );

    if ( m_bCommitable )
        aTypes.addTypes( OBoundControlModel_COMMITTING::getTypes() );

    if ( m_bSupportsExternalBinding )
        aTypes.addTypes( OBoundControlModel_BINDING::getTypes() );

    if ( m_bSupportsValidation )
        aTypes.addTypes( OBoundControlModel_VALIDATION::getTypes() );
    return aTypes.getTypes();
}

// OComponentHelper
void OBoundControlModel::disposing()
{
    OControlModel::disposing();

    osl::MutexGuard aGuard(m_aMutex);

    if ( m_pAggPropMultiplexer )
        m_pAggPropMultiplexer->dispose();

    // notify all our listeners
    css::lang::EventObject aEvt( static_cast< XWeak* >( this ) );
    m_aUpdateListeners.disposeAndClear( aEvt );
    m_aResetHelper.disposing();

    // disconnect from our database column
    // TODO: could we replace the following 5 lines with a call to impl_disconnectDatabaseColumn_noNotify?
    // The only more thing which it does is calling onDisconnectedDbColumn - could this
    // cause trouble? At least when we continue to call OControlModel::disposing before, it *may*.
    if ( hasField() )
    {
        getField()->removePropertyChangeListener( PROPERTY_VALUE, this );
        resetField();
    }

    m_xCursor = nullptr;
    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();
    // ditto for the validator
    if ( hasValidator() )
        disconnectValidator( );
}

void OBoundControlModel::onValuePropertyChange( ControlModelLock& i_rControLock )
{
    if ( hasExternalValueBinding() )
    {
        // the control value changed, while we have an external value binding
        // -> forward the value to it
        if ( m_eControlValueChangeInstigator != eExternalBinding )
            transferControlValueToExternal( i_rControLock );
    }

    else if ( !m_bCommitable && m_xColumnUpdate.is() )
    {
        // the control value changed, while we are  bound to a database column,
        // but not committable (which means changes in the control have to be reflected to
        // the underlying database column immediately)
        // -> forward the value to the database column
        if ( m_eControlValueChangeInstigator != eDbColumnBinding )
            commitControlValueToDbColumn( false );
    }

    // validate the new value
    if ( m_bSupportsValidation )
        recheckValidity( true );
}

void OBoundControlModel::_propertyChanged( const PropertyChangeEvent& _rEvt )
{
    ControlModelLock aLock( *this );
    OSL_ENSURE( _rEvt.PropertyName == m_sValuePropertyName,
        "OBoundControlModel::_propertyChanged: where did this come from (1)?" );
    OSL_ENSURE( m_pAggPropMultiplexer && !m_pAggPropMultiplexer->locked(),
        "OBoundControlModel::_propertyChanged: where did this come from (2)?" );
    if ( _rEvt.PropertyName == m_sValuePropertyName )
    {
        onValuePropertyChange( aLock );
    }
}

void OBoundControlModel::startAggregatePropertyListening( const OUString& _rPropertyName )
{
    OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::startAggregatePropertyListening: no multiplexer!" );
    OSL_ENSURE( !_rPropertyName.isEmpty(), "OBoundControlModel::startAggregatePropertyListening: invalid property name!" );
    if ( m_pAggPropMultiplexer && !_rPropertyName.isEmpty() )
    {
        m_pAggPropMultiplexer->addProperty( _rPropertyName );
    }
}

void OBoundControlModel::doFormListening( const bool _bStart )
{
    OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::doFormListening: external value binding should overrule the database binding!" );
    if ( isFormListening() == _bStart )
        return;
    if ( m_xAmbientForm.is() )
        _bStart ? m_xAmbientForm->addLoadListener( this ) : m_xAmbientForm->removeLoadListener( this );
    Reference< XLoadable > xParentLoadable( getParent(), UNO_QUERY );
    if ( getParent().is() && !xParentLoadable.is() )
    {
        // if our parent does not directly support the XLoadable interface, then it might support the
        // XRowSetSupplier/XRowSetChangeBroadcaster interfaces. In this case we have to listen for changes
        // broadcasted by the latter.
        Reference< XRowSetChangeBroadcaster > xRowSetBroadcaster( getParent(), UNO_QUERY );
        if ( xRowSetBroadcaster.is() )
            _bStart ? xRowSetBroadcaster->addRowSetChangeListener( this ) : xRowSetBroadcaster->removeRowSetChangeListener( this );
    }

    m_bFormListening = _bStart && m_xAmbientForm.is();
}

// XChild
void SAL_CALL OBoundControlModel::setParent(const Reference<XInterface>& _rxParent)
{
    ControlModelLock aLock( *this );
    FieldChangeNotifier aBoundFieldNotifier( aLock );
    if ( getParent() == _rxParent )
        return;
    // disconnect from database column (which is controlled by parent, directly or indirectly)
    if ( hasField() )
        impl_disconnectDatabaseColumn_noNotify();
    // log off old listeners
    if ( isFormListening() )
        doFormListening( false );
    // actually set the new parent
    OControlModel::setParent( _rxParent );
    // a new parent means a new ambient form
    impl_determineAmbientForm_nothrow();
    if ( !hasExternalValueBinding() )
    {
        // log on new listeners
        doFormListening( true );
        // re-connect to database column of the new parent
        if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() )
            impl_connectDatabaseColumn_noNotify( false );
    }
}

// XEventListener
void SAL_CALL OBoundControlModel::disposing(const css::lang::EventObject& _rEvent)
{
    ControlModelLock aLock( *this );
    if ( _rEvent.Source == getField() )
    {
        resetField();
    }

    else if ( _rEvent.Source == m_xLabelControl )
    {
        Reference<XPropertySet> xOldValue = m_xLabelControl;
        m_xLabelControl = nullptr;
        // fire a propertyChanged (when we leave aLock's scope)
        aLock.addPropertyNotification( PROPERTY_ID_CONTROLLABEL, Any( xOldValue ), Any( m_xLabelControl ) );
    }

    else if ( _rEvent.Source == m_xExternalBinding )
    {   // *first* check for the external binding
        disconnectExternalValueBinding( );
    }

    else if ( _rEvent.Source == m_xValidator )
    {   // *then* check for the validator. Reason is that bindings may also act as validator at the same
        // time, in this case, the validator is automatically revoked when the binding is revoked
        disconnectValidator( );
    }

    else
        OControlModel::disposing(_rEvent);
}

// XServiceInfo
css::uno::Sequence<OUString> SAL_CALL OBoundControlModel::getSupportedServiceNames()
{
    return ::comphelper::combineSequences(
        getAggregateServiceNames(),
        getSupportedServiceNames_Static()
    );
}

Sequence< OUString > OBoundControlModel::getSupportedServiceNames_Static()
{
    static constexpr OUString aOwnServiceNames[] { u"com.sun.star.form.DataAwareControlModel"_ustr };
    return ::comphelper::concatSequences(
        OControlModel::getSupportedServiceNames_Static(),
        aOwnServiceNames
    );
}

// XPersist
void SAL_CALL OBoundControlModel::write( const Reference<css::io::XObjectOutputStream>&&nbsp;_rxOutStream )
{
    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 !!!
}

void OBoundControlModel::defaultCommonProperties()
{
    Reference<css::lang::XComponent> xComp(m_xLabelControl, UNO_QUERY);
    if (xComp.is())
        xComp->removeEventListener(static_cast<css::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
    m_xLabelControl = nullptr;
}

void OBoundControlModel::readCommonProperties(const Reference<css::io::XObjectInputStream>& _rxInStream)
{
    sal_Int32 nLen = _rxInStream->readLong();
    Reference<css::io::XMarkableStream> xMark(_rxInStream, UNO_QUERY);
    DBG_ASSERT(xMark.is(), "OBoundControlModel::readCommonProperties : can only work with markable streams !");
    sal_Int32 nMark = xMark->createMark();
    // read the reference to the label control
    Reference<css::io::XPersistObject> xPersist;
    sal_Int32 nUsedFlag;
    nUsedFlag = _rxInStream->readLong();
    if (nUsedFlag)
        xPersist = _rxInStream->readObject();
    m_xLabelControl.set(xPersist, css::uno::UNO_QUERY);
    Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY );
    if (xComp.is())
        xComp->addEventListener(static_cast<css::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
    // read any other new common properties here
    // skip the remaining bytes
    xMark->jumpToMark(nMark);
    _rxInStream->skipBytes(nLen);
    xMark->deleteMark(nMark);
}

void OBoundControlModel::writeCommonProperties(const Reference<css::io::XObjectOutputStream>& _rxOutStream)
{
    Reference<css::io::XMarkableStream> xMark(_rxOutStream, UNO_QUERY);
    DBG_ASSERT(xMark.is(), "OBoundControlModel::writeCommonProperties : can only work with markable streams !");
    sal_Int32 nMark = xMark->createMark();
    // a placeholder where we will write the overall length (later in this method)
    sal_Int32 nLen = 0;
    _rxOutStream->writeLong(nLen);
    // write the reference to the label control
    Reference<css::io::XPersistObject> xPersist(m_xLabelControl, UNO_QUERY);
    sal_Int32 nUsedFlag = 0;
    if (xPersist.is())
        nUsedFlag = 1;
    _rxOutStream->writeLong(nUsedFlag);
    if (xPersist.is())
        _rxOutStream->writeObject(xPersist);
    // write any other new common properties here
    // write the correct length at the beginning of the block
    nLen = xMark->offsetToMark(nMark) - sizeof(nLen);
    xMark->jumpToMark(nMark);
    _rxOutStream->writeLong(nLen);
    xMark->jumpToFurthest();
    xMark->deleteMark(nMark);
}

void SAL_CALL OBoundControlModel::read( const Reference< css::io::XObjectInputStream >&&nbsp;_rxInStream )
{
    OControlModel::read(_rxInStream);
    osl::MutexGuard aGuard(m_aMutex);
    _rxInStream->readShort(); // version;
    ::comphelper::operator>>( _rxInStream, m_aControlSource);
}

void OBoundControlModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const
{
    switch (nHandle)
    {
        case PROPERTY_ID_INPUT_REQUIRED:
            rValue <<= m_bInputRequired;
            break;
        case PROPERTY_ID_CONTROLSOURCEPROPERTY:
            rValue <<= m_sValuePropertyName;
            break;
        case PROPERTY_ID_CONTROLSOURCE:
            rValue <<= m_aControlSource;
            break;
        case PROPERTY_ID_BOUNDFIELD:
            rValue <<= getField();
            break;
        case PROPERTY_ID_CONTROLLABEL:
            if (!m_xLabelControl.is())
                rValue.clear();
            else
                rValue <<= m_xLabelControl;
            break;
        default:
            OControlModel::getFastPropertyValue(rValue, nHandle);
    }
}

sal_Bool OBoundControlModel::convertFastPropertyValue(
                                Any& _rConvertedValue, Any& _rOldValue,
                sal_Int32 _nHandle,
                                const Any& _rValue)
{
    bool bModified(false);
    switch (_nHandle)
    {
        case PROPERTY_ID_INPUT_REQUIRED:
            bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_bInputRequired );
            break;
        case PROPERTY_ID_CONTROLSOURCE:
            bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aControlSource);
            break;
        case PROPERTY_ID_BOUNDFIELD:
            SAL_WARN("forms.component",  "OBoundControlModel::convertFastPropertyValue: BoundField should be a read-only property !" );
            throw css::lang::IllegalArgumentException();
        case PROPERTY_ID_CONTROLLABEL:
            if (!_rValue.hasValue())
            {   // property set to void
                _rConvertedValue = Any();
                getFastPropertyValue(_rOldValue, _nHandle);
                bModified = m_xLabelControl.is();
            }

            else
            {
                bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_xLabelControl);
                if (!m_xLabelControl.is())
                    // an empty interface is interpreted as VOID
                    _rOldValue.clear();
            }

            break;
        default:
            bModified = OControlModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue);
    }
    return bModified;
}

Any OBoundControlModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
{
    Any aDefault;
    switch ( _nHandle )
    {
        case PROPERTY_ID_INPUT_REQUIRED:
            aDefault <<= false;
            break;
        case PROPERTY_ID_CONTROLSOURCE:
            aDefault <<= OUString();
            break;
        case PROPERTY_ID_CONTROLLABEL:
            aDefault <<= Reference< XPropertySet >();
            break;
    }
    return aDefault;
}

void OBoundControlModel::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
{
    switch (nHandle)
    {
        case PROPERTY_ID_INPUT_REQUIRED:
            OSL_VERIFY( rValue >>= m_bInputRequired );
            break;
        case PROPERTY_ID_CONTROLSOURCE:
            OSL_VERIFY( rValue >>= m_aControlSource );
            break;
        case PROPERTY_ID_BOUNDFIELD:
            SAL_WARN("forms.component""OBoundControlModel::setFastPropertyValue_NoBroadcast : BoundField should be a read-only property !");
            throw css::lang::IllegalArgumentException();
        case PROPERTY_ID_CONTROLLABEL:
        {
            if ( rValue.hasValue() && ( rValue.getValueTypeClass() != TypeClass_INTERFACE ) )
                throw css::lang::IllegalArgumentException();
            Reference< XInterface > xNewValue( rValue, UNO_QUERY );
            if ( !xNewValue.is() )
            {   // set property to "void"
                Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY );
                if ( xComp.is() )
                    xComp->removeEventListener( static_cast< XPropertyChangeListener* >( this ) );
                m_xLabelControl = nullptr;
                break;
            }

            Reference< XControlModel >  xAsModel        ( xNewValue,        UNO_QUERY );
            Reference< XServiceInfo >   xAsServiceInfo  ( xAsModel,         UNO_QUERY );
            Reference< XPropertySet >   xAsPropSet      ( xAsServiceInfo,   UNO_QUERY );
            Reference< XChild >         xAsChild        ( xAsPropSet,       UNO_QUERY );
            if ( !xAsChild.is() || !xAsServiceInfo->supportsService( m_aLabelServiceName ) )
            {
                throw css::lang::IllegalArgumentException();
            }

            // Check if we and the given model have a common ancestor (up to the forms collection)
            Reference<XChild> xCont(this);
            Reference< XInterface > xMyTopLevel = xCont->getParent();
            while (xMyTopLevel.is())
            {
                Reference<XForm> xAsForm(xMyTopLevel, UNO_QUERY);
                if (!xAsForm.is())
                    // found my root
                    break;
                Reference<XChild> xLoopAsChild(xMyTopLevel, UNO_QUERY);
                xMyTopLevel = xLoopAsChild.is() ? xLoopAsChild->getParent() : Reference< XInterface >();
            }

            Reference< XInterface > xNewTopLevel = xAsChild->getParent();
            while (xNewTopLevel.is())
            {
                Reference<XForm> xAsForm(xNewTopLevel, UNO_QUERY);
                if (!xAsForm.is())
                    break;
                Reference<XChild> xLoopAsChild(xNewTopLevel, UNO_QUERY);
                xNewTopLevel = xLoopAsChild.is() ? xLoopAsChild->getParent() : Reference< XInterface >();
            }

            if (xNewTopLevel != xMyTopLevel)
            {
                // the both objects don't belong to the same forms collection -> not acceptable
                throw css::lang::IllegalArgumentException();
            }

            m_xLabelControl = std::move(xAsPropSet);
            Reference<css::lang::XComponent> xComp(m_xLabelControl, UNO_QUERY);
            if (xComp.is())
                xComp->addEventListener(static_cast<css::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
        }

        break;
        default:
            OControlModel::setFastPropertyValue_NoBroadcast(nHandle, rValue );
    }
}

// XPropertyChangeListener
void SAL_CALL OBoundControlModel::propertyChange( const PropertyChangeEvent& evt )
{
    // if the DBColumn value changed, transfer it to the control
    if ( evt.PropertyName == PROPERTY_VALUE )
    {
        OSL_ENSURE( evt.Source == getField(), "OBoundControlModel::propertyChange: value changes from components other than our database column?" );
        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
        OUString sBindingControlledProperty;
        bool bForwardToLabelControl = false;
        if ( evt.PropertyName == PROPERTY_READONLY )
        {
            sBindingControlledProperty = PROPERTY_READONLY;
        }

        else if ( evt.PropertyName == 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 );
        }

        catchconst Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("forms.component");
            SAL_WARN("forms.component",  "OBoundControlModel::propertyChange: could not adjust my binding-controlled property!");
        }

    }
}

void SAL_CALL OBoundControlModel::onRowSetChanged( const EventObject& /*i_Event*/ )
{
    ControlModelLock aLock( *this );
    FieldChangeNotifier aBoundFieldNotifier( aLock );
    // disconnect from database column (which is controlled by parent, directly or indirectly)
    if ( hasField() )
        impl_disconnectDatabaseColumn_noNotify();
    // log off old listeners
    if ( isFormListening() )
        doFormListening( false );
    // determine the new ambient form
    impl_determineAmbientForm_nothrow();
    // log on new listeners
    doFormListening( true );
    // re-connect to database column if needed and possible
    if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() )
        impl_connectDatabaseColumn_noNotify( false );
}

// XBoundComponent
void SAL_CALL OBoundControlModel::addUpdateListener(const Reference<XUpdateListener>&&nbsp;_rxListener)
{
    m_aUpdateListeners.addInterface(_rxListener);
}

void SAL_CALL OBoundControlModel::removeUpdateListener(const Reference< XUpdateListener>& _rxListener)
{
    m_aUpdateListeners.removeInterface(_rxListener);
}

sal_Bool SAL_CALL OBoundControlModel::commit()
{
    ControlModelLock aLock( *this );
    OSL_PRECOND( m_bCommitable, "OBoundControlModel::commit: invalid call (I'm not committable!) " );
    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.isEmpty() )
            // but for those derivees which did not use this feature, we need an
            // explicit transfer
            transferControlValueToExternal( aLock );
        return true;
    }

    OSL_ENSURE( !hasExternalValueBinding(), "OBoundControlModel::commit: control flow broken!" );
        // we reach this only if we're not working with an external binding
    if ( !hasField() )
        return true;
    ::comphelper::OInterfaceIteratorHelper3 aIter( m_aUpdateListeners );
    EventObject aEvent;
    aEvent.Source = static_cast< XWeak* >( this );
    bool bSuccess = true;
    aLock.release();
    // UNSAFE >
    while (aIter.hasMoreElements() && bSuccess)
        bSuccess = aIter.next()->approveUpdate( aEvent );
    // < UNSAFE
    aLock.acquire();
    if ( bSuccess )
    {
        try
        {
            if ( m_xColumnUpdate.is() )
                bSuccess = commitControlValueToDbColumn( false );
        }

        catch(const Exception&)
        {
            bSuccess = false;
        }

    }

    if ( bSuccess )
    {
        aLock.release();
        m_aUpdateListeners.notifyEach( &XUpdateListener::updated, aEvent );
    }
    return bSuccess;
}

void OBoundControlModel::resetField()
{
    m_xColumnUpdate.clear();
    m_xColumn.clear();
    m_xField.clear();
    m_nFieldType = DataType::OTHER;
}

void OBoundControlModel::connectToField(const Reference<XRowSet>& rForm)
{
    OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::connectToField: invalid call (have an external binding)!" );
    // if there's a connection to the database
    if (!(rForm.is() && getConnection(rForm).is()))
        return;

    // determine field and PropertyChangeListener
    m_xCursor = rForm;
    Reference<XPropertySet> xFieldCandidate;
    if (m_xCursor.is())
    {
        Reference<XColumnsSupplier> xColumnsSupplier(m_xCursor, UNO_QUERY);
        DBG_ASSERT(xColumnsSupplier.is(), "OBoundControlModel::connectToField : the row set should support the css::sdb::ResultSet service !");
        if (xColumnsSupplier.is())
        {
            Reference<XNameAccess> xColumns = xColumnsSupplier->getColumns();
            if (xColumns.is() && xColumns->hasByName(m_aControlSource))
            {
                OSL_VERIFY( xColumns->getByName(m_aControlSource) >>= xFieldCandidate );
            }

        }

    }

    try
    {
        sal_Int32 nFieldType = DataType::OTHER;
        if ( xFieldCandidate.is() )
        {
            xFieldCandidate->getPropertyValue( PROPERTY_FIELDTYPE ) >>= nFieldType;
            if ( approveDbColumnType( nFieldType ) )
                impl_setField_noNotify( xFieldCandidate );
        }

        else
            impl_setField_noNotify( nullptr );
        if ( m_xField.is() )
        {
            if ( m_xField->getPropertySetInfo()->hasPropertyByName( PROPERTY_VALUE ) )
            {
                m_nFieldType = nFieldType;
                // listen to changing values
                m_xField->addPropertyChangeListener( PROPERTY_VALUE, this );
                m_xColumnUpdate.set( m_xField, UNO_QUERY );
                m_xColumn.set( m_xField, UNO_QUERY );
                sal_Int32 nNullableFlag = ColumnValue::NO_NULLS;
                m_xField->getPropertyValue(PROPERTY_ISNULLABLE) >>= nNullableFlag;
                // tdf#122319 - don't allow nullable form components if input is required
                m_bRequired = (ColumnValue::NO_NULLS == nNullableFlag || m_bInputRequired);
                // we're optimistic: in case of ColumnValue_NULLABLE_UNKNOWN we assume nullability...
            }
            else
            {
                SAL_WARN("forms.component""OBoundControlModel::connectToField: property " << PROPERTY_VALUE << " not supported!");
                impl_setField_noNotify( nullptr );
            }

        }

    }

    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("forms.component");
        resetField();
    }
}

void OBoundControlModel::initFromField( const Reference< XRowSet >& _rxRowSet )
{
    // but only if the rowset is positioned on a valid record
    if ( !(hasField() && _rxRowSet.is()) )
        return;

    bool shouldTransfer(!_rxRowSet->isBeforeFirst() && !_rxRowSet->isAfterLast());
    if (!shouldTransfer)
    {
        const Reference< XPropertySet > xPS(_rxRowSet, UNO_QUERY);
        if (xPS.is())
        {
            assert(!shouldTransfer);
            xPS->getPropertyValue(u"IsNew"_ustr) >>= shouldTransfer;
        }
    }
    if ( shouldTransfer )
        transferDbValueToControl();
    else
        // reset the field if the row set is empty
        // #i30661#
        resetNoBroadcast();
}

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 false;
    return true;
}

void OBoundControlModel::impl_determineAmbientForm_nothrow()
{
    Reference< XInterface > xParent( getParent() );
    m_xAmbientForm.set( xParent, UNO_QUERY );
    if ( !m_xAmbientForm.is() )
    {
        Reference< XRowSetSupplier > xSupRowSet( xParent, UNO_QUERY );
        if ( xSupRowSet.is() )
            m_xAmbientForm.set( xSupRowSet->getRowSet(), UNO_QUERY );
    }
}

void OBoundControlModel::impl_connectDatabaseColumn_noNotify( bool _bFromReload )
{
    OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::impl_connectDatabaseColumn_noNotify: not to be called with an external value binding!" );
    // consistency checks
    DBG_ASSERT( !( hasField() && !_bFromReload ),
        "OBoundControlModel::impl_connectDatabaseColumn_noNotify: the form is just *loaded*, but we already have a field!" );

    Reference< XRowSet > xRowSet( m_xAmbientForm, UNO_QUERY );
    OSL_ENSURE( xRowSet.is(), "OBoundControlModel::impl_connectDatabaseColumn_noNotify: no row set!" );
    if ( !xRowSet.is() )
        return;
    if ( !hasField() || _bFromReload )
    {
        // connect to the column
        connectToField( xRowSet );
    }

    // now that we're connected (more or less, even if we did not find a column),
    // we definitely want to forward any potentially occurring value changes
    m_bForwardValueChanges = true;
    // let derived classes react on this new connection
    m_bLoaded = true;
    onConnectedDbColumn( xRowSet );
    // initially transfer the db column value to the control, if we successfully connected to a database column
    if ( hasField() )
        initFromField( xRowSet );
}

void OBoundControlModel::impl_disconnectDatabaseColumn_noNotify()
{
    OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::impl_disconnectDatabaseColumn_noNotify: not to be called with an external value binding!" );
    // let derived classes react on this
    onDisconnectedDbColumn();
    if ( hasField() )
    {
        getField()->removePropertyChangeListener( PROPERTY_VALUE, this );
        resetField();
    }

    m_xCursor = nullptr;
    m_bLoaded = false;
}

// XLoadListener
void SAL_CALL OBoundControlModel::loaded( const EventObject& _rEvent )
{
    ControlModelLock aLock( *this );
    FieldChangeNotifier aBoundFieldNotifier( aLock );
    OSL_ENSURE( _rEvent.Source == m_xAmbientForm, "OBoundControlModel::loaded: where does this come from?" );
    OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::loaded: we should never reach this with an external value binding!" );
    if ( hasExternalValueBinding() )
        return;
    impl_connectDatabaseColumn_noNotify( false );
}

void SAL_CALL OBoundControlModel::unloaded( const css::lang::EventObject& /*aEvent*/ )
{
    OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::unloaded: we should never reach this with an external value binding!" );
}

void SAL_CALL OBoundControlModel::reloading( const css::lang::EventObject& /*aEvent*/ )
{
    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 = false;
}

void SAL_CALL OBoundControlModel::unloading(const css::lang::EventObject& /*aEvent*/)
{
    ControlModelLock aLock( *this );
    FieldChangeNotifier aBoundFieldNotifier( aLock );
    OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::unloading: we should never reach this with an external value binding!" );
    if ( hasExternalValueBinding() )
        return;
    impl_disconnectDatabaseColumn_noNotify();
}

void SAL_CALL OBoundControlModel::reloaded( const EventObject& _rEvent )
{
    ControlModelLock aLock( *this );
    FieldChangeNotifier aBoundFieldNotifier( aLock );
    OSL_ENSURE( _rEvent.Source == m_xAmbientForm, "OBoundControlModel::reloaded: where does this come from?" );
    OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::reloaded: we should never reach this with an external value binding!" );
    if ( hasExternalValueBinding() )
        return;
    impl_connectDatabaseColumn_noNotify( true );
}

void OBoundControlModel::setControlValue( const Any& _rValue, ValueChangeInstigator _eInstigator )
{
    m_eControlValueChangeInstigator = _eInstigator;
    doSetControlValue( _rValue );
    m_eControlValueChangeInstigator = eOther;
}

void OBoundControlModel::doSetControlValue( const Any& _rValue )
{
    OSL_PRECOND( m_xAggregateFastSet.is() && m_xAggregateSet.is(),
        "OBoundControlModel::doSetControlValue: invalid aggregate !" );
    OSL_PRECOND( !m_sValuePropertyName.isEmpty() || ( 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 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
        MutexRelease aRelease( m_aMutex );
        if ( ( m_nValuePropertyAggregateHandle != -1 ) && m_xAggregateFastSet.is() )
        {
            m_xAggregateFastSet->setFastPropertyValue( m_nValuePropertyAggregateHandle, _rValue );
        }

        else if ( !m_sValuePropertyName.isEmpty() && m_xAggregateSet.is() )
        {
            m_xAggregateSet->setPropertyValue( m_sValuePropertyName, _rValue );
        }

    }

    catchconst Exception& )
    {
        TOOLS_WARN_EXCEPTION("forms.component",  "OBoundControlModel::doSetControlValue");
    }
}

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, Any( false ) );
    }

    catchconst Exception& )
    {
        TOOLS_WARN_EXCEPTION("forms.component",  "OBoundControlModel::onConnectedValidator");
    }

    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, Any( true ) );
    }

    catchconst Exception& )
    {
        TOOLS_WARN_EXCEPTION("forms.component",  "OBoundControlModel::onDisconnectedValidator");
    }

    recheckValidity( false );
}

void OBoundControlModel::onConnectedExternalValue( )
{
    calculateExternalValueType();
}

void OBoundControlModel::onConnectedDbColumn( const Reference< XInterface >& /*_rxForm*/ )
{
    OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::onConnectedDbColumn: how this? There's an external value binding!" );
}

void OBoundControlModel::onDisconnectedDbColumn()
{
    OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::onDisconnectedDbColumn: how this? There's an external value binding!" );
}

// XReset
Any OBoundControlModel::getDefaultForReset() const
{
    return Any();
}

void OBoundControlModel::resetNoBroadcast()
{
    setControlValue( getDefaultForReset(), eOther );
}

void OBoundControlModel::addResetListener(const Reference<XResetListener>& l)
{
    m_aResetHelper.addResetListener( l );
}

void OBoundControlModel::removeResetListener(const Reference<XResetListener>& l)
{
    m_aResetHelper.removeResetListener( l );
}

void OBoundControlModel::reset()
{
    if ( !m_aResetHelper.approveReset() )
       return;
    ControlModelLock aLock( *this );
    // on a new record?
    bool bIsNewRecord = false;
    Reference<XPropertySet> xSet( m_xCursor, UNO_QUERY );
    if ( xSet.is() )
    {
        try
        {
            xSet->getPropertyValue( PROPERTY_ISNEW ) >>= bIsNewRecord;
        }

        catchconst Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("forms.component");
        }

    }

    // cursor on an invalid row?
    bool bInvalidCursorPosition = true;
    try
    {
        bInvalidCursorPosition =    m_xCursor.is()
                                &&  (  m_xCursor->isAfterLast()
                                    || m_xCursor->isBeforeFirst()
                                    )
                                &&  !bIsNewRecord;
    }

    catchconst SQLException& )
    {
        TOOLS_WARN_EXCEPTION("forms.component",  "OBoundControlModel::reset: caught an SQL exception!" );
    }

    // #i24495# - don't count the insert row as "invalid"
    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?
        bool bIsNull = true;
        // we have to access the field content at least once to get a reliable result by XColumn::wasNull
        try
        {
            // normally, we'd do a getString here. However, this is extremely expensive in the case
            // of binary fields. Unfortunately, getString is the only method which is guaranteed
            // to *always* succeed, all other getXXX methods may fail if the column is asked for a
            // non-convertible type
            sal_Int32 nFieldType = DataType::OBJECT;
            getField()->getPropertyValue( PROPERTY_FIELDTYPE ) >>= nFieldType;
            if  (  ( nFieldType == DataType::BINARY        )
                || ( nFieldType == DataType::VARBINARY     )
                || ( nFieldType == DataType::LONGVARBINARY )
                || ( nFieldType == DataType::OBJECT        )
                /*|| ( nFieldType == DataType::CLOB          )*/
                )
                m_xColumn->getBinaryStream();
            else if ( nFieldType == DataType::BLOB          )
                m_xColumn->getBlob();
            else
                m_xColumn->getString();
            bIsNull = m_xColumn->wasNull();
        }

        catch(const Exception&)
        {
            DBG_UNHANDLED_EXCEPTION("forms.component");
            SAL_WARN("forms.component""OBoundControlModel::reset: this should have succeeded in all cases!");
        }

        bool bNeedValueTransfer = true;
        if ( bIsNull )
        {
            if ( bIsNewRecord )
            {
                // reset the control to its default
                resetNoBroadcast();
                // and immediately commit the changes to the DB column, to keep consistency
                commitControlValueToDbColumn( true );
                bNeedValueTransfer = false;
            }

        }

        if ( bNeedValueTransfer )
            transferDbValueToControl();
    }

    else
    {
        resetNoBroadcast();
        // transfer to the external binding, if necessary
        if ( hasExternalValueBinding() )
            transferControlValueToExternal( aLock );
    }

    // revalidate, if necessary
    if ( hasValidator() )
        recheckValidity( true );
    aLock.release();
    m_aResetHelper.notifyResetted();
}

void OBoundControlModel::impl_setField_noNotify( const Reference< XPropertySet>& _rxField )
{
    DBG_ASSERT( !hasExternalValueBinding(), "OBoundControlModel::impl_setField_noNotify: We have an external value binding!" );
    m_xField = _rxField;
}

bool OBoundControlModel::impl_approveValueBinding_nolock( const Reference< XValueBinding >& _rxBinding )
{
    if ( !_rxBinding.is() )
        return false;
    Sequence< Type > aTypeCandidates;
    {
        // SYNCHRONIZED >
        ::osl::MutexGuard aGuard( m_aMutex );
        aTypeCandidates = getSupportedBindingTypes();
        // < SYNCHRONIZED
    }

    for (auto const& type : aTypeCandidates)
    {
        if ( _rxBinding->supportsType( type ) )
            return true;
    }
    return false;
}

void OBoundControlModel::connectExternalValueBinding(
        const Reference< XValueBinding >& _rxBinding, ControlModelLock& _rInstanceLock )
{
    OSL_PRECOND( _rxBinding.is(), "OBoundControlModel::connectExternalValueBinding: invalid binding instance!" );
    OSL_PRECOND( !hasExternalValueBinding( ), "OBoundControlModel::connectExternalValueBinding: precond not met (currently have a binding)!" );
    // if we're connected to a database column, suspend this
    if ( hasField() )
        impl_disconnectDatabaseColumn_noNotify();
    // suspend listening for load-related events at out ambient form.
    // This is because an external value binding overrules a possible database binding.
    if ( isFormListening() )
        doFormListening( false );
    // remember this new binding
    m_xExternalBinding = _rxBinding;
    // tell the derivee
    onConnectedExternalValue();
    try
    {
        // add as value listener so we get notified when the value changes
        Reference< XModifyBroadcaster > xModifiable( m_xExternalBinding, UNO_QUERY );
        if ( xModifiable.is() )
            xModifiable->addModifyListener( this );
        // add as property change listener for some (possibly present) properties we're
        // interested in
        Reference< XPropertySet > xBindingProps( m_xExternalBinding, UNO_QUERY );
        Reference< XPropertySetInfo > xBindingPropsInfo( xBindingProps.is() ? xBindingProps->getPropertySetInfo() : Reference< XPropertySetInfo >() );
        if ( xBindingPropsInfo.is() )
        {
            if ( xBindingPropsInfo->hasPropertyByName( PROPERTY_READONLY ) )
            {
                xBindingProps->addPropertyChangeListener( PROPERTY_READONLY, this );
                m_bBindingControlsRO = true;
            }

            if ( xBindingPropsInfo->hasPropertyByName( PROPERTY_RELEVANT ) )
            {
                xBindingProps->addPropertyChangeListener( PROPERTY_RELEVANT, this );
                m_bBindingControlsEnable = true;
            }

        }

    }

    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("forms.component");
    }

    // propagate our new value
    transferExternalValueToControl( _rInstanceLock );
    // if the binding is also a validator, use it, too. This is a constraint of the
    // com.sun.star.form.binding.ValidatableBindableFormComponent service
    if ( !m_bSupportsValidation )
        return;

    try
    {
        Reference< XValidator > xAsValidator( _rxBinding, UNO_QUERY );
        if ( xAsValidator.is() )
            setValidator( xAsValidator );
    }

    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("forms.component");
    }
}

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 );
    }

    catchconst Exception& )
    {
        TOOLS_WARN_EXCEPTION("forms.component",  "OBoundControlModel::disconnectExternalValueBinding");
    }

    // if the binding also acts as our validator, disconnect the validator, too
    if ( ( m_xExternalBinding == m_xValidator ) && m_xValidator.is() )
        disconnectValidator( );
    // no binding anymore
    m_xExternalBinding.clear();
    // be a load listener at our form, again. This was suspended while we had
    // an external value binding in place.
    doFormListening( true );
    // re-connect to database column of the new parent
    if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() )
        impl_connectDatabaseColumn_noNotify( false );
}

void SAL_CALL OBoundControlModel::setValueBinding( const Reference< XValueBinding >& _rxBinding )
{
    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
    // allow reset
    if ( _rxBinding.is() && !impl_approveValueBinding_nolock( _rxBinding ) )
    {
        throw IncompatibleTypesException(
            ResourceManager::loadString(RID_STR_INCOMPATIBLE_TYPES),
            *this
        );
    }

    ControlModelLock aLock( *this );
    // since a ValueBinding overrules any potentially active database binding, the change in a ValueBinding
    // might trigger a change in our BoundField.
    FieldChangeNotifier aBoundFieldNotifier( aLock );
    // disconnect from the old binding
    if ( hasExternalValueBinding() )
        disconnectExternalValueBinding( );
    // connect to the new binding
    if ( _rxBinding.is() )
        connectExternalValueBinding( _rxBinding, aLock );
}

Reference< XValueBinding > SAL_CALL OBoundControlModel::getValueBinding(  )
{
    ::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 )
{
    ControlModelLock aLock( *this );
    OSL_PRECOND( hasExternalValueBinding(), "OBoundControlModel::modified: Where did this come from?" );
    if ( !m_bTransferringValue && ( m_xExternalBinding == _rEvent.Source ) && m_xExternalBinding.is() )
    {
        transferExternalValueToControl( aLock );
    }
}

void OBoundControlModel::transferDbValueToControl( )
{
    try
    {
        setControlValue( translateDbColumnToControlValue(), eDbColumnBinding );
    }

    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("forms.component");
    }
}

void OBoundControlModel::transferExternalValueToControl( ControlModelLock& _rInstanceLock )
{
        Reference< XValueBinding > xExternalBinding( m_xExternalBinding );
        Type aValueExchangeType( getExternalValueType() );
        _rInstanceLock.release();
        // UNSAFE >
        Any aExternalValue;
        try
        {
            aExternalValue = xExternalBinding->getValue( aValueExchangeType );
        }

        catchconst Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("forms.component");
        }
        // < UNSAFE
        _rInstanceLock.acquire();
        setControlValue( translateExternalValueToControlValue( aExternalValue ), eExternalBinding );
}

void OBoundControlModel::transferControlValueToExternal( ControlModelLock& _rInstanceLock )
{
    OSL_PRECOND( m_bSupportsExternalBinding && hasExternalValueBinding(),
        "OBoundControlModel::transferControlValueToExternal: precondition not met!" );
    if ( !m_xExternalBinding.is() )
        return;

    Any aExternalValue( translateControlValueToExternalValue() );
    m_bTransferringValue = true;
    _rInstanceLock.release();
     // UNSAFE >
    try
    {
        m_xExternalBinding->setValue( aExternalValue );
    }

    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("forms.component");
    }

    // < UNSAFE
    _rInstanceLock.acquire();
    m_bTransferringValue = false;
}

Sequence< Type > OBoundControlModel::getSupportedBindingTypes()
{
    return Sequence< Type >( &m_aValuePropertyType, 1 );
}

void OBoundControlModel::calculateExternalValueType()
{
    m_aExternalValueType = Type();
    if ( !m_xExternalBinding.is() )
        return;
    const Sequence< Type > aTypeCandidates( getSupportedBindingTypes() );
    for ( auto const & typeCandidate : aTypeCandidates )
    {
        if ( m_xExternalBinding->supportsType( typeCandidate ) )
        {
            m_aExternalValueType = typeCandidate;
            break;
        }
    }
}

Any OBoundControlModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
{
    OSL_PRECOND( m_bSupportsExternalBinding && hasExternalValueBinding(),
        "OBoundControlModel::translateExternalValueToControlValue: precondition not met!" );
    Any aControlValue( _rExternalValue );
    // if the external value is VOID, and our value property is not allowed to be VOID,
    // then default-construct a value
    if ( !aControlValue.hasValue() && !m_bValuePropertyMayBeVoid )
        aControlValue.setValue( nullptr, m_aValuePropertyType );
    // out of here
    return aControlValue;
}

Any OBoundControlModel::translateControlValueToExternalValue( ) const
{
    return getControlValue( );
}

Any OBoundControlModel::translateControlValueToValidatableValue( ) const
{
    OSL_PRECOND( m_xValidator.is(), "OBoundControlModel::translateControlValueToValidatableValue: no validator, so why should I?" );
    if ( ( m_xValidator == m_xExternalBinding ) && m_xValidator.is() )
        return translateControlValueToExternalValue();
    return getControlValue();
}

Any OBoundControlModel::getControlValue( ) const
{
    OSL_PRECOND( m_xAggregateFastSet.is() && m_xAggregateSet.is(),
        "OBoundControlModel::getControlValue: invalid aggregate !" );
    OSL_PRECOND( !m_sValuePropertyName.isEmpty() || ( 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.isEmpty() && 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 );
        }

        catchconst 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 );
        }

        catchconst RuntimeException& )
        {
        }
    }

    m_xValidator.clear();

    onDisconnectedValidator( );
}

void SAL_CALL OBoundControlModel::setValidator( const Reference< XValidator >& _rxValidator )
{
    osl::MutexGuard 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(
            ResourceManager::loadString(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(  )
{
    ::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*/ )
{
    osl::MutexGuard 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(  )
{
    return m_bIsCurrentValueValid;
}

css::uno::Any OBoundControlModel::getCurrentFormComponentValue() const
{
    if ( hasValidator() )
        return translateControlValueToValidatableValue();
    return getControlValue();
}

Any SAL_CALL OBoundControlModel::getCurrentValue(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    return getCurrentFormComponentValue();
}

void SAL_CALL OBoundControlModel::addFormComponentValidityListener( const Reference< validation::XFormComponentValidityListener >& Listener )
{
    if ( Listener.is() )
        m_aFormComponentListeners.addInterface( Listener );
}

void SAL_CALL OBoundControlModel::removeFormComponentValidityListener( const Reference< validation::XFormComponentValidityListener >& Listener )
{
    if ( Listener.is() )
        m_aFormComponentListeners.removeInterface( Listener );
}

void OBoundControlModel::recheckValidity( bool _bForceNotification )
{
    try
    {
        bool bIsCurrentlyValid = true;
        if ( hasValidator() )
            bIsCurrentlyValid = m_xValidator->isValid( translateControlValueToValidatableValue() );

        if ( ( bIsCurrentlyValid != m_bIsCurrentValueValid ) || _bForceNotification )
        {
            m_bIsCurrentValueValid = bIsCurrentlyValid;

            // release our mutex for the notifications
            MutexRelease aRelease( m_aMutex );
            m_aFormComponentListeners.notifyEach( &validation::XFormComponentValidityListener::componentValidityChanged, EventObject( *this ) );
        }

    }

    catchconst Exception& )
    {
        TOOLS_WARN_EXCEPTION("forms.component",  "OBoundControlModel::recheckValidity");
    }
}

void OBoundControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const
{
    OControlModel::describeFixedProperties( _rProps );
    sal_Int32 nOldCount = _rProps.getLength();
    _rProps.realloc( nOldCount + 5);
    css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
    *pProperties++ = css::beans::Property(PROPERTY_CONTROLSOURCE, PROPERTY_ID_CONTROLSOURCE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
    *pProperties++ = css::beans::Property(PROPERTY_BOUNDFIELD, PROPERTY_ID_BOUNDFIELD, cppu::UnoType<XPropertySet>::get(),
                                              css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
    *pProperties++ = css::beans::Property(PROPERTY_CONTROLLABEL, PROPERTY_ID_CONTROLLABEL, cppu::UnoType<XPropertySet>::get(),
                                              css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID);
    *pProperties++ = css::beans::Property(PROPERTY_CONTROLSOURCEPROPERTY, PROPERTY_ID_CONTROLSOURCEPROPERTY, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
    *pProperties++ = css::beans::Property(PROPERTY_INPUT_REQUIRED, PROPERTY_ID_INPUT_REQUIRED, cppu::UnoType<bool>::get(),
                                          css::beans::PropertyAttribute::BOUND);
    DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
}
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5 in Prozent
C=91 H=99 G=94

¤ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet am  2026-05-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge