Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/svx/source/form/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 138 kB image not shown  

Quelle  formcontroller.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 <fmcontrolbordermanager.hxx>
#include <fmcontrollayout.hxx>
#include <formcontroller.hxx>
#include <formfeaturedispatcher.hxx>
#include <fmdocumentclassification.hxx>
#include <formcontrolling.hxx>
#include <fmprop.hxx>
#include <svx/dialmgr.hxx>
#include <svx/strings.hrc>
#include <fmservs.hxx>
#include <svx/fmtools.hxx>
#include <fmurl.hxx>

#include <com/sun/star/awt/FocusChangeReason.hpp>
#include <com/sun/star/awt/XCheckBox.hpp>
#include <com/sun/star/awt/XComboBox.hpp>
#include <com/sun/star/awt/XListBox.hpp>
#include <com/sun/star/awt/XVclWindowPeer.hpp>
#include <com/sun/star/awt/TabController.hpp>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/container/XIdentifierReplace.hpp>
#include <com/sun/star/form/TabulatorCycle.hpp>
#include <com/sun/star/form/validation/XValidatableFormComponent.hpp>
#include <com/sun/star/form/XBoundComponent.hpp>
#include <com/sun/star/form/XBoundControl.hpp>
#include <com/sun/star/form/XGridControl.hpp>
#include <com/sun/star/form/XLoadable.hpp>
#include <com/sun/star/form/XReset.hpp>
#include <com/sun/star/form/control/FilterControl.hpp>
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/lang/NoSupportException.hpp>
#include <com/sun/star/sdb/ParametersRequest.hpp>
#include <com/sun/star/sdb/RowChangeAction.hpp>
#include <com/sun/star/sdb/SQLFilterOperator.hpp>
#include <com/sun/star/sdb/XInteractionSupplyParameters.hpp>
#include <com/sun/star/sdbc/ColumnValue.hpp>
#include <com/sun/star/task/InteractionHandler.hpp>
#include <com/sun/star/form/runtime/FormOperations.hpp>
#include <com/sun/star/form/runtime/FormFeature.hpp>
#include <com/sun/star/container/XContainer.hpp>
#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
#include <com/sun/star/util/NumberFormatter.hpp>
#include <com/sun/star/sdb/SQLContext.hpp>
#include <com/sun/star/sdb/XColumn.hpp>

#include <comphelper/enumhelper.hxx>
#include <comphelper/interaction.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/property.hxx>
#include <comphelper/sequence.hxx>
#include <comphelper/flagguard.hxx>
#include <comphelper/types.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <connectivity/IParseContext.hxx>
#include <connectivity/dbtools.hxx>
#include <connectivity/sqlparse.hxx>
#include <toolkit/controls/unocontrol.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <tools/debug.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <unotools/localedatawrapper.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <o3tl/safeint.hxx>
#include <osl/mutex.hxx>
#include <sal/log.hxx>

#include <algorithm>
#include <iterator>

using namespace ::com::sun::star;
using namespace ::comphelper;
using namespace ::connectivity;
using namespace ::dbtools;


css::uno::Reference< css::uno::XInterface >
    FormController_NewInstance_Impl( const css::uno::Reference< css::lang::XMultiServiceFactory > & _rxORB )
{
    return *( new ::svxform::FormController( comphelper::getComponentContext(_rxORB) ) );
}

namespace svxform
{

    using ::com::sun::star::sdb::XColumn;
    using ::com::sun::star::awt::XControl;
    using ::com::sun::star::awt::TabController;
    using ::com::sun::star::awt::XToolkit;
    using ::com::sun::star::awt::XWindowPeer;
    using ::com::sun::star::form::XGrid;
    using ::com::sun::star::beans::XPropertySet;
    using ::com::sun::star::uno::UNO_SET_THROW;
    using ::com::sun::star::uno::UNO_QUERY_THROW;
    using ::com::sun::star::container::XIndexAccess;
    using ::com::sun::star::uno::Exception;
    using ::com::sun::star::uno::XInterface;
    using ::com::sun::star::uno::UNO_QUERY;
    using ::com::sun::star::uno::Sequence;
    using ::com::sun::star::uno::Reference;
    using ::com::sun::star::beans::XPropertySetInfo;
    using ::com::sun::star::beans::PropertyValue;
    using ::com::sun::star::lang::IndexOutOfBoundsException;
    using ::com::sun::star::sdb::XInteractionSupplyParameters;
    using ::com::sun::star::awt::XTextComponent;
    using ::com::sun::star::awt::XTextListener;
    using ::com::sun::star::uno::Any;
    using ::com::sun::star::frame::XDispatch;
    using ::com::sun::star::lang::XMultiServiceFactory;
    using ::com::sun::star::uno::Type;
    using ::com::sun::star::lang::IllegalArgumentException;
    using ::com::sun::star::sdbc::XConnection;
    using ::com::sun::star::sdbc::XRowSet;
    using ::com::sun::star::sdbc::XDatabaseMetaData;
    using ::com::sun::star::util::XNumberFormatsSupplier;
    using ::com::sun::star::util::NumberFormatter;
    using ::com::sun::star::util::XNumberFormatter;
    using ::com::sun::star::sdbcx::XColumnsSupplier;
    using ::com::sun::star::container::XNameAccess;
    using ::com::sun::star::lang::EventObject;
    using ::com::sun::star::beans::Property;
    using ::com::sun::star::container::XEnumeration;
    using ::com::sun::star::form::XFormComponent;
    using ::com::sun::star::form::runtime::XFormOperations;
    using ::com::sun::star::form::runtime::FilterEvent;
    using ::com::sun::star::form::runtime::XFilterControllerListener;
    using ::com::sun::star::awt::XControlContainer;
    using ::com::sun::star::container::XIdentifierReplace;
    using ::com::sun::star::form::XFormControllerListener;
    using ::com::sun::star::awt::XWindow;
    using ::com::sun::star::sdbc::XResultSet;
    using ::com::sun::star::awt::XControlModel;
    using ::com::sun::star::awt::XTabControllerModel;
    using ::com::sun::star::beans::PropertyChangeEvent;
    using ::com::sun::star::form::validation::XValidatableFormComponent;
    using ::com::sun::star::form::XLoadable;
    using ::com::sun::star::form::XBoundControl;
    using ::com::sun::star::beans::XPropertyChangeListener;
    using ::com::sun::star::awt::TextEvent;
    using ::com::sun::star::form::XBoundComponent;
    using ::com::sun::star::awt::XCheckBox;
    using ::com::sun::star::awt::XComboBox;
    using ::com::sun::star::awt::XListBox;
    using ::com::sun::star::awt::ItemEvent;
    using ::com::sun::star::util::XModifyListener;
    using ::com::sun::star::form::XReset;
    using ::com::sun::star::frame::XDispatchProviderInterception;
    using ::com::sun::star::form::XGridControl;
    using ::com::sun::star::awt::XVclWindowPeer;
    using ::com::sun::star::form::validation::XValidator;
    using ::com::sun::star::awt::FocusEvent;
    using ::com::sun::star::sdb::SQLContext;
    using ::com::sun::star::container::XChild;
    using ::com::sun::star::form::TabulatorCycle_RECORDS;
    using ::com::sun::star::container::ContainerEvent;
    using ::com::sun::star::lang::DisposedException;
    using ::com::sun::star::lang::Locale;
    using ::com::sun::star::lang::NoSupportException;
    using ::com::sun::star::sdb::RowChangeEvent;
    using ::com::sun::star::frame::XStatusListener;
    using ::com::sun::star::frame::XDispatchProviderInterceptor;
    using ::com::sun::star::sdb::SQLErrorEvent;
    using ::com::sun::star::form::DatabaseParameterEvent;
    using ::com::sun::star::sdb::ParametersRequest;
    using ::com::sun::star::task::XInteractionRequest;
    using ::com::sun::star::util::URL;
    using ::com::sun::star::frame::FeatureStateEvent;
    using ::com::sun::star::form::runtime::XFormControllerContext;
    using ::com::sun::star::task::InteractionHandler;
    using ::com::sun::star::task::XInteractionHandler;
    using ::com::sun::star::form::runtime::FormOperations;
    using ::com::sun::star::container::XContainer;
    using ::com::sun::star::sdbc::SQLWarning;

    namespace ColumnValue = ::com::sun::star::sdbc::ColumnValue;
    namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute;
    namespace FocusChangeReason = ::com::sun::star::awt::FocusChangeReason;
    namespace RowChangeAction = ::com::sun::star::sdb::RowChangeAction;
    namespace FormFeature = ::com::sun::star::form::runtime::FormFeature;

namespace {

struct ColumnInfo
{
    // information about the column itself
    Reference< XColumn >    xColumn;
    sal_Int32               nNullable;
    bool                bAutoIncrement;
    bool                bReadOnly;
    OUString         sName;

    // information about the control(s) bound to this column

    /// the first control which is bound to the given column, and which requires input
    Reference< XControl >   xFirstControlWithInputRequired;
    /** the first grid control which contains a column which is bound to the given database column, and requires
        input
    */

    Reference< XGrid >      xFirstGridWithInputRequiredColumn;
    /** if xFirstControlWithInputRequired is a grid control, then nRequiredGridColumn specifies the position
        of the grid column which is actually bound
    */

    sal_Int32               nRequiredGridColumn;

    ColumnInfo()
        :nNullable( ColumnValue::NULLABLE_UNKNOWN )
        ,bAutoIncrement( false )
        ,bReadOnly( false )
        ,nRequiredGridColumn( -1 )
    {
    }
};

}

class ColumnInfoCache
{
public:
    explicit ColumnInfoCache( const Reference< XColumnsSupplier >& _rxColSupplier );

    size_t        getColumnCount() const { return m_aColumns.size(); }
    const ColumnInfo&   getColumnInfo( size_t _pos );

    bool    controlsInitialized() const { return m_bControlsInitialized; }
    void    initializeControls( const Sequence< Reference< XControl > >& _rControls );
    void    deinitializeControls();

private:
    typedef ::std::vector< ColumnInfo > ColumnInfos;
    ColumnInfos                         m_aColumns;
    bool                                m_bControlsInitialized;
};


ColumnInfoCache::ColumnInfoCache( const Reference< XColumnsSupplier >& _rxColSupplier )
    :m_bControlsInitialized( false )
{
    try
    {
        m_aColumns.clear();

        Reference< XIndexAccess > xColumns( _rxColSupplier->getColumns(), UNO_QUERY_THROW );
        sal_Int32 nColumnCount = xColumns->getCount();
        m_aColumns.reserve( nColumnCount );

        Reference< XPropertySet >   xColumnProps;
        for ( sal_Int32 i = 0; i < nColumnCount; ++i )
        {
            ColumnInfo aColInfo;
            aColInfo.xColumn.set( xColumns->getByIndex(i), UNO_QUERY_THROW );

            xColumnProps.set( aColInfo.xColumn, UNO_QUERY_THROW );
            OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_ISNULLABLE ) >>= aColInfo.nNullable );
            OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_AUTOINCREMENT ) >>= aColInfo.bAutoIncrement );
            OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_NAME ) >>= aColInfo.sName );
            OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_ISREADONLY ) >>= aColInfo.bReadOnly );

            m_aColumns.push_back( aColInfo );
        }
    }
    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }
}


namespace
{
    bool lcl_isBoundTo( const Reference< XPropertySet >& _rxControlModel, const Reference< XInterface >& _rxNormDBField )
    {
        Reference< XInterface > xNormBoundField( _rxControlModel->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY );
        return ( xNormBoundField == _rxNormDBField );
    }

    bool lcl_isInputRequired( const Reference< XPropertySet >& _rxControlModel )
    {
        bool bInputRequired = false;
        OSL_VERIFY( _rxControlModel->getPropertyValue( FM_PROP_INPUT_REQUIRED ) >>= bInputRequired );
        return bInputRequired;
    }

    void lcl_resetColumnControlInfo( ColumnInfo& _rColInfo )
    {
        _rColInfo.xFirstControlWithInputRequired.clear();
        _rColInfo.xFirstGridWithInputRequiredColumn.clear();
        _rColInfo.nRequiredGridColumn = -1;
    }
}


void ColumnInfoCache::deinitializeControls()
{
    for (auto& rCol : m_aColumns)
    {
        lcl_resetColumnControlInfo( rCol );
    }
    m_bControlsInitialized = false;
}


void ColumnInfoCache::initializeControls( const Sequence< Reference< XControl > >& _rControls )
{
    try
    {
        // for every of our known columns, find the controls which are bound to this column
        for (auto& rCol : m_aColumns)
        {
            OSL_ENSURE( !rCol.xFirstControlWithInputRequired.is() && !rCol.xFirstGridWithInputRequiredColumn.is()
                && ( rCol.nRequiredGridColumn == -1 ), "ColumnInfoCache::initializeControls: called me twice?" );

            lcl_resetColumnControlInfo( rCol );

            Reference< XInterface > xNormColumn( rCol.xColumn, UNO_QUERY_THROW );

            const Reference< XControl >* pControl( _rControls.getConstArray() );
            const Reference< XControl >* pControlEnd( pControl + _rControls.getLength() );
            for ( ; pControl != pControlEnd; ++pControl )
            {
                if ( !pControl->is() )
                    continue;

                Reference< XPropertySet > xModel( (*pControl)->getModel(), UNO_QUERY_THROW );
                Reference< XPropertySetInfo > xModelPSI( xModel->getPropertySetInfo(), UNO_SET_THROW );

                // special handling for grid controls
                Reference< XGrid > xGrid( *pControl, UNO_QUERY );
                if ( xGrid.is() )
                {
                    Reference< XIndexAccess > xGridColAccess( xModel, UNO_QUERY_THROW );
                    sal_Int32 gridColCount = xGridColAccess->getCount();
                    sal_Int32 gridCol = 0;
                    for ( gridCol = 0; gridCol < gridColCount; ++gridCol )
                    {
                        Reference< XPropertySet > xGridColumnModel( xGridColAccess->getByIndex( gridCol ), UNO_QUERY_THROW );

                        if  (   !lcl_isBoundTo( xGridColumnModel, xNormColumn )
                            ||  !lcl_isInputRequired( xGridColumnModel )
                            )
                            continue;   // with next grid column

                        break;
                    }

                    if ( gridCol < gridColCount )
                    {
                        // found a grid column which is bound to the given
                        rCol.xFirstGridWithInputRequiredColumn = std::move(xGrid);
                        rCol.nRequiredGridColumn = gridCol;
                        break;
                    }

                    continue;   // with next control
                }

                if  (   !xModelPSI->hasPropertyByName( FM_PROP_BOUNDFIELD )
                    ||  !lcl_isBoundTo( xModel, xNormColumn )
                    ||  !lcl_isInputRequired( xModel )
                    )
                    continue;   // with next control

                break;
            }

            if ( pControl == pControlEnd )
                // did not find a control which is bound to this particular column, and for which the input is required
                continue;   // with next DB column

            rCol.xFirstControlWithInputRequired = *pControl;
        }
    }
    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }

    m_bControlsInitialized = true;
}


const ColumnInfo& ColumnInfoCache::getColumnInfo( size_t _pos )
{
    if ( _pos >= m_aColumns.size() )
        throw IndexOutOfBoundsException();

    return m_aColumns[ _pos ];
}

namespace {

class OParameterContinuation : public OInteraction< XInteractionSupplyParameters >
{
    Sequence< PropertyValue >       m_aValues;

public:
    OParameterContinuation() { }

    const Sequence< PropertyValue >&   getValues() const { return m_aValues; }

// XInteractionSupplyParameters
    virtual void SAL_CALL setParameters( const Sequence< PropertyValue >& _rValues ) override;
};

}

void SAL_CALL OParameterContinuation::setParameters( const Sequence< PropertyValue >& ;_rValues )
{
    m_aValues = _rValues;
}


// FmXAutoControl

struct FmFieldInfo
{
    OUString       aFieldName;
    Reference< XPropertySet >   xField;
    Reference< XTextComponent >  xText;

    FmFieldInfo(const Reference< XPropertySet >& _xField, const Reference< XTextComponent >&&nbsp;_xText)
        :xField(_xField)
        ,xText(_xText)
    {xField->getPropertyValue(FM_PROP_NAME) >>= aFieldName;}
};

namespace {

class FmXAutoControl: public UnoControl

{
public:
    FmXAutoControl()
    {
    }

    virtual OUString GetComponentServiceName() const override {return u"Edit"_ustr;}
    virtual void SAL_CALL createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer >  & rParentPeer ) override;

protected:
    virtual void ImplSetPeerProperty( const OUString& rPropName, const Any& rVal ) override;
};

}

void FmXAutoControl::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer >  & rParentPeer )
{
    UnoControl::createPeer( rxToolkit, rParentPeer );

    Reference< XTextComponent >  xText(getPeer() , UNO_QUERY);
    if (xText.is())
    {
        xText->setText(SvxResId(RID_STR_AUTOFIELD));
        xText->setEditable(false);
    }
}


void FmXAutoControl::ImplSetPeerProperty( const OUString& rPropName, const Any& ;rVal )
{
    // these properties are ignored
    if (rPropName == FM_PROP_TEXT)
        return;

    UnoControl::ImplSetPeerProperty( rPropName, rVal );
}


IMPL_LINK_NOARG( FormController, OnActivateTabOrder, Timer*, void )
{
    activateTabOrder();
}

namespace {

struct UpdateAllListeners
{
    bool operator()( const Reference< XDispatch >& _rxDispatcher ) const
    {
        static_cast< svx::OSingleFeatureDispatcher* >( _rxDispatcher.get() )->updateAllListeners();
        // the return is a dummy only so we can use this struct in a lambda expression
        return true;
    }
};

}

IMPL_LINK_NOARG( FormController, OnInvalidateFeatures, Timer*, void )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    for (const auto& rFeature : m_aInvalidFeatures)
    {
        DispatcherContainer::const_iterator aDispatcherPos = m_aFeatureDispatchers.find( rFeature );
        if ( aDispatcherPos != m_aFeatureDispatchers.end() )
        {
            // TODO: for the real and actual listener notifications, we should release
            // our mutex
            UpdateAllListeners( )( aDispatcherPos->second );
        }
    }
}

FormController::FormController(const Reference< css::uno::XComponentContext > & _rxORB )
                  :FormController_BASE( m_aMutex )
                  ,OPropertySetHelper( FormController_BASE::rBHelper )
                  ,OSQLParserClient( _rxORB )
                  ,m_xComponentContext( _rxORB )
                  ,m_aActivateListeners(m_aMutex)
                  ,m_aModifyListeners(m_aMutex)
                  ,m_aErrorListeners(m_aMutex)
                  ,m_aDeleteListeners(m_aMutex)
                  ,m_aRowSetApproveListeners(m_aMutex)
                  ,m_aParameterListeners(m_aMutex)
                  ,m_aFilterListeners(m_aMutex)
                  ,m_aTabActivationIdle("svx FormController m_aTabActivationIdle")
                  ,m_aFeatureInvalidationTimer("svx FormController m_aFeatureInvalidationTimer")
                  ,m_aMode( u"DataMode"_ustr )
                  ,m_aLoadEvent( LINK( this, FormController, OnLoad ) )
                  ,m_aToggleEvent( LINK( this, FormController, OnToggleAutoFields ) )
                  ,m_aActivationEvent( LINK( this, FormController, OnActivated ) )
                  ,m_aDeactivationEvent( LINK( this, FormController, OnDeactivated ) )
                  ,m_nCurrentFilterPosition(-1)
                  ,m_bCurrentRecordModified(false)
                  ,m_bCurrentRecordNew(false)
                  ,m_bLocked(false)
                  ,m_bDBConnection(false)
                  ,m_bCycle(false)
                  ,m_bCanInsert(false)
                  ,m_bCanUpdate(false)
                  ,m_bCommitLock(false)
                  ,m_bModified(false)
                  ,m_bControlsSorted(false)
                  ,m_bFiltering(false)
                  ,m_bAttachEvents(true)
                  ,m_bDetachEvents(true)
                  ,m_bAttemptedHandlerCreation( false )
                  ,m_bSuspendFilterTextListening( false )
{

    osl_atomic_increment(&m_refCount);
    {
        m_xTabController = TabController::create( m_xComponentContext );
        m_xAggregate.set( m_xTabController, UNO_QUERY_THROW );
        m_xAggregate->setDelegator( *this );
    }
    osl_atomic_decrement(&m_refCount);

    m_aTabActivationIdle.SetPriority( TaskPriority::LOWEST );
    m_aTabActivationIdle.SetInvokeHandler( LINK( this, FormController, OnActivateTabOrder ) );

    m_aFeatureInvalidationTimer.SetTimeout( 200 );
    m_aFeatureInvalidationTimer.SetInvokeHandler( LINK( this, FormController, OnInvalidateFeatures ) );
}


FormController::~FormController()
{
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        m_aLoadEvent.CancelPendingCall();
        m_aToggleEvent.CancelPendingCall();
        m_aActivationEvent.CancelPendingCall();
        m_aDeactivationEvent.CancelPendingCall();

        if ( m_aTabActivationIdle.IsActive() )
            m_aTabActivationIdle.Stop();
    }

    if ( m_aFeatureInvalidationTimer.IsActive() )
        m_aFeatureInvalidationTimer.Stop();

    disposeAllFeaturesAndDispatchers();

    if ( m_xFormOperations.is() )
        m_xFormOperations->dispose();
    m_xFormOperations.clear();

    // release of aggregation
    if ( m_xAggregate.is() )
    {
        m_xAggregate->setDelegator( nullptr );
        m_xAggregate.clear();
    }
}


void SAL_CALL FormController::acquire() noexcept
{
    FormController_BASE::acquire();
}


void SAL_CALL FormController::release() noexcept
{
    FormController_BASE::release();
}


Any SAL_CALL FormController::queryInterface( const Type& _rType )
{
    Any aRet = FormController_BASE::queryInterface( _rType );
    if ( !aRet.hasValue() )
        aRet = OPropertySetHelper::queryInterface( _rType );
    if ( !aRet.hasValue() )
        aRet = m_xAggregate->queryAggregation( _rType );
    return aRet;
}


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

Sequence< Type > SAL_CALL FormController::getTypes(  )
{
    return comphelper::concatSequences(
        FormController_BASE::getTypes(),
        ::cppu::OPropertySetHelper::getTypes()
    );
}

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

OUString SAL_CALL FormController::getImplementationName()
{
    return u"org.openoffice.comp.svx.FormController"_ustr;
}

Sequence< OUString> SAL_CALL FormController::getSupportedServiceNames()
{
    // service names which are supported only, but cannot be used to created an
    // instance at a service factory
    static constexpr OUString aNonCreatableServiceNames[] { u"com.sun.star.form.FormControllerDispatcher"_ustr };

    // services which can be used to created an instance at a service factory
    Sequence< OUString > aCreatableServiceNames( getSupportedServiceNames_Static() );
    return ::comphelper::concatSequences( aCreatableServiceNames, aNonCreatableServiceNames );
}


sal_Bool SAL_CALL FormController::approveReset(const EventObject& /*rEvent*/)
{
    return true;
}


void SAL_CALL FormController::resetted(const EventObject& rEvent)
{
    ::osl::MutexGuard aGuard(m_aMutex);
    if (getCurrentControl().is() &&  (getCurrentControl()->getModel() == rEvent.Source))
        m_bModified = false;
}


Sequence< OUString> const & FormController::getSupportedServiceNames_Static()
{
    static Sequence< OUString> const aServices
    {
        u"com.sun.star.form.runtime.FormController"_ustr,
        u"com.sun.star.awt.control.TabController"_ustr
    };
    return aServices;
}


namespace
{
    struct ResetComponentText
    {
        void operator()( const Reference< XTextComponent >& _rxText )
        {
            _rxText->setText( OUString() );
        }
    };

    struct RemoveComponentTextListener
    {
        explicit RemoveComponentTextListener( const Reference< XTextListener >& _rxListener )
            :m_xListener( _rxListener )
        {
        }

        void operator()( const Reference< XTextComponent >& _rxText )
        {
            _rxText->removeTextListener( m_xListener );
        }

    private:
        Reference< XTextListener >  m_xListener;
    };
}


void FormController::impl_setTextOnAllFilter_throw()
{
    m_bSuspendFilterTextListening = true;
    ::comphelper::FlagGuard aResetFlag( m_bSuspendFilterTextListening );

    // reset the text for all controls
    ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), ResetComponentText() );

    if ( m_aFilterRows.empty() )
        // nothing to do anymore
        return;

    if ( m_nCurrentFilterPosition < 0 )
        return;

    // set the text for all filters
    OSL_ENSURE( m_aFilterRows.size() > o3tl::make_unsigned(m_nCurrentFilterPosition),
        "FormController::impl_setTextOnAllFilter_throw: m_nCurrentFilterPosition too big" );

    if ( o3tl::make_unsigned(m_nCurrentFilterPosition) < m_aFilterRows.size() )
    {
        FmFilterRow& rRow = m_aFilterRows[ m_nCurrentFilterPosition ];
        for (const auto& rEntry : rRow)
        {
            rEntry.first->setText( rEntry.second );
        }
    }
}
// OPropertySetHelper

sal_Bool FormController::convertFastPropertyValue( Any & /*rConvertedValue*/, Any &&nbsp;/*rOldValue*/,
                                            sal_Int32 /*nHandle*/, const Any& /*rValue*/ )
{
    return false;
}


void FormController::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/, const Any& /*rValue*/ )
{
}


void FormController::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
{
    switch (nHandle)
    {
        case FM_ATTR_FILTER:
        {
            OUStringBuffer aFilter;
            Reference<XConnection> xConnection(getConnection(Reference< XRowSet>(m_xModelAsIndex, UNO_QUERY)));
            if (xConnection.is())
            {
                Reference< XNumberFormatsSupplier> xFormatSupplier( getNumberFormats( xConnection, true ) );
                Reference< XNumberFormatter> xFormatter = NumberFormatter::create(m_xComponentContext);
                xFormatter->attachNumberFormatsSupplier(xFormatSupplier);

                // now add the filter rows
                try
                {
                    for (const FmFilterRow& rRow : m_aFilterRows)
                    {
                        if ( rRow.empty() )
                            continue;

                        OUStringBuffer aRowFilter;
                        for ( FmFilterRow::const_iterator condition = rRow.begin(); condition != rRow.end(); ++condition )
                        {
                            // get the field of the controls map
                            Reference< XControl > xControl( condition->first, UNO_QUERY_THROW );
                            Reference< XPropertySet > xModelProps( xControl->getModel(), UNO_QUERY_THROW );
                            Reference< XPropertySet > xField( xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY_THROW );

                            OUString sFilterValue( condition->second );

                            OUString sErrorMsg;
                            const std::unique_ptr< OSQLParseNode > pParseNode =
                                predicateTree( sErrorMsg, sFilterValue, xFormatter, xField );
                            OSL_ENSURE( pParseNode != nullptr, "FormController::getFastPropertyValue: could not parse the field value predicate!" );
                            if ( pParseNode != nullptr )
                            {
                                OUString sCriteria;
                                // don't use a parse context here, we need it unlocalized
                                pParseNode->parseNodeToStr( sCriteria, xConnection );
                                if ( condition != rRow.begin() )
                                    aRowFilter.append( " AND " );
                                aRowFilter.append( sCriteria );
                            }
                        }
                        if ( !aRowFilter.isEmpty() )
                        {
                            if ( !aFilter.isEmpty() )
                                aFilter.append( " OR " );

                            aFilter.append( "( " + aRowFilter + " )" );
                        }
                    }
                }
                catchconst Exception& )
                {
                    DBG_UNHANDLED_EXCEPTION("svx");
                    aFilter.setLength(0);
                }
            }
            rValue <<= aFilter.makeStringAndClear();
        }
        break;

        case FM_ATTR_FORM_OPERATIONS:
            rValue <<= m_xFormOperations;
            break;
    }
}


Reference< XPropertySetInfo >  FormController::getPropertySetInfo()
{
    static Reference< XPropertySetInfo >  xInfo( createPropertySetInfo( getInfoHelper() ) );
    return xInfo;
}


void FormController::fillProperties(
        Sequence< Property >& /* [out] */ _rProps,
        Sequence< Property >& /* [out] */ /*_rAggregateProps*/
        ) const
{
    _rProps.realloc(2);
    sal_Int32 nPos = 0;
    Property* pDesc = _rProps.getArray();

    pDesc[nPos++] = Property(FM_PROP_FILTER, FM_ATTR_FILTER,
                             cppu::UnoType<OUString>::get(),
                             PropertyAttribute::READONLY);
    pDesc[nPos++] = Property(FM_PROP_FORM_OPERATIONS, FM_ATTR_FORM_OPERATIONS,
                             cppu::UnoType<XFormOperations>::get(),
                             PropertyAttribute::READONLY);
}


::cppu::IPropertyArrayHelper& FormController::getInfoHelper()
{
    return *getArrayHelper();
}

// XFilterController

void SAL_CALL FormController::addFilterControllerListener( const Reference< XFilterControllerListener >& Listener )
{
    m_aFilterListeners.addInterface( Listener );
}


void SAL_CALL FormController::removeFilterControllerListener( const Reference< XFilterControllerListener >& Listener )
{
    m_aFilterListeners.removeInterface( Listener );
}


::sal_Int32 SAL_CALL FormController::getFilterComponents()
{
    ::osl::MutexGuard aGuard( m_aMutex );
    impl_checkDisposed_throw();

    return m_aFilterComponents.size();
}


::sal_Int32 SAL_CALL FormController::getDisjunctiveTerms()
{
    ::osl::MutexGuard aGuard( m_aMutex );
    impl_checkDisposed_throw();

    return m_aFilterRows.size();
}


void SAL_CALL FormController::setPredicateExpression( ::sal_Int32 Component, ::sal_Int32 Term, const OUString& PredicateExpression )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    impl_checkDisposed_throw();

    if ( ( Component < 0 ) || ( Component >= getFilterComponents() ) || ( Term < 0 ) || ( Term >= getDisjunctiveTerms() ) )
        throw IndexOutOfBoundsException( OUString(), *this );

    Reference< XTextComponent > xText( m_aFilterComponents[ Component ] );
    xText->setText( PredicateExpression );

    FmFilterRow& rFilterRow = m_aFilterRows[ Term ];
    if ( !PredicateExpression.isEmpty() )
        rFilterRow[ xText ] = PredicateExpression;
    else
        rFilterRow.erase( xText );
}


Reference< XControl > FormController::getFilterComponent( ::sal_Int32 Component )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    impl_checkDisposed_throw();

    if ( ( Component < 0 ) || ( Component >= getFilterComponents() ) )
        throw IndexOutOfBoundsException( OUString(), *this );

    return Reference< XControl >( m_aFilterComponents[ Component ], UNO_QUERY );
}


Sequence< Sequence< OUString > > FormController::getPredicateExpressions()
{
    ::osl::MutexGuard aGuard( m_aMutex );
    impl_checkDisposed_throw();

    Sequence< Sequence< OUString > > aExpressions( m_aFilterRows.size() );
    auto aExpressionsRange = asNonConstRange(aExpressions);
    sal_Int32 termIndex = 0;
    for (const FmFilterRow& rRow : m_aFilterRows)
    {
        Sequence< OUString > aConjunction( m_aFilterComponents.size() );
        auto aConjunctionRange = asNonConstRange(aConjunction);
        sal_Int32 componentIndex = 0;
        for (const auto& rComp : m_aFilterComponents)
        {
            FmFilterRow::const_iterator predicate = rRow.find( rComp );
            if ( predicate != rRow.end() )
                aConjunctionRange[ componentIndex ] = predicate->second;
            ++componentIndex;
        }

        aExpressionsRange[ termIndex ] = std::move(aConjunction);
        ++termIndex;
    }

    return aExpressions;
}


void SAL_CALL FormController::removeDisjunctiveTerm( ::sal_Int32 Term )
{
    // SYNCHRONIZED -->
    ::osl::ClearableMutexGuard aGuard( m_aMutex );
    impl_checkDisposed_throw();

    if ( ( Term < 0 ) || ( Term >= getDisjunctiveTerms() ) )
        throw IndexOutOfBoundsException( OUString(), *this );

    // if the to-be-deleted row is our current row, we need to shift
    if ( Term == m_nCurrentFilterPosition )
    {
        if ( m_nCurrentFilterPosition < sal_Int32( m_aFilterRows.size() - 1 ) )
            ++m_nCurrentFilterPosition;
        else
            --m_nCurrentFilterPosition;
    }

    FmFilterRows::iterator pos = m_aFilterRows.begin() + Term;
    m_aFilterRows.erase( pos );

    // adjust m_nCurrentFilterPosition if the removed row preceded it
    if ( Term < m_nCurrentFilterPosition )
        --m_nCurrentFilterPosition;

    SAL_WARN_IF( !( ( m_nCurrentFilterPosition < 0 ) != ( m_aFilterRows.empty() ) ),
        "svx.form""FormController::removeDisjunctiveTerm: inconsistency!" );

    // update the texts in the filter controls
    impl_setTextOnAllFilter_throw();

    FilterEvent aEvent;
    aEvent.Source = *this;
    aEvent.DisjunctiveTerm = Term;
    aGuard.clear();
    // <-- SYNCHRONIZED

    m_aFilterListeners.notifyEach( &XFilterControllerListener::disjunctiveTermRemoved, aEvent );
}


void SAL_CALL FormController::appendEmptyDisjunctiveTerm()
{
    // SYNCHRONIZED -->
    ::osl::ClearableMutexGuard aGuard( m_aMutex );
    impl_checkDisposed_throw();

    impl_appendEmptyFilterRow( aGuard );
    // <-- SYNCHRONIZED
}


::sal_Int32 SAL_CALL FormController::getActiveTerm()
{
    ::osl::MutexGuard aGuard( m_aMutex );
    impl_checkDisposed_throw();

    return m_nCurrentFilterPosition;
}


void SAL_CALL FormController::setActiveTerm( ::sal_Int32 ActiveTerm )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    impl_checkDisposed_throw();

    if ( ( ActiveTerm < 0 ) || ( ActiveTerm >= getDisjunctiveTerms() ) )
        throw IndexOutOfBoundsException( OUString(), *this );

    if ( ActiveTerm == getActiveTerm() )
        return;

    m_nCurrentFilterPosition = ActiveTerm;
    impl_setTextOnAllFilter_throw();
}

// XElementAccess

sal_Bool SAL_CALL FormController::hasElements()
{
    ::osl::MutexGuard aGuard( m_aMutex );
    return !m_aChildren.empty();
}


Type SAL_CALL  FormController::getElementType()
{
    return cppu::UnoType<XFormController>::get();

}

// XEnumerationAccess

Reference< XEnumeration > SAL_CALL  FormController::createEnumeration()
{
    ::osl::MutexGuard aGuard( m_aMutex );
    return new ::comphelper::OEnumerationByIndex(this);
}

// XIndexAccess

sal_Int32 SAL_CALL FormController::getCount()
{
    ::osl::MutexGuard aGuard( m_aMutex );
    return m_aChildren.size();
}


Any SAL_CALL FormController::getByIndex(sal_Int32 Index)
{
    ::osl::MutexGuard aGuard( m_aMutex );
    if (Index < 0 ||
        o3tl::make_unsigned(Index) >= m_aChildren.size())
        throw IndexOutOfBoundsException();

    return Any( m_aChildren[ Index ] );
}

//  EventListener

void SAL_CALL FormController::disposing(const EventObject& e)
{
    // has the container been disposed
    ::osl::MutexGuard aGuard( m_aMutex );
    Reference< XControlContainer >  xContainer(e.Source, UNO_QUERY);
    if (xContainer.is())
    {
        setContainer(Reference< XControlContainer > ());
    }
    else
    {
        // has a control been disposed
        Reference< XControl >  xControl(e.Source, UNO_QUERY);
        if (xControl.is())
        {
            if (getContainer().is())
                removeControl(xControl);
        }
    }
}

// OComponentHelper

void FormController::disposeAllFeaturesAndDispatchers()
{
    for (auto& rDispatcher : m_aFeatureDispatchers)
    {
        try
        {
            ::comphelper::disposeComponent( rDispatcher.second );
        }
        catchconst Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("svx");
        }
    }
    m_aFeatureDispatchers.clear();
}


void FormController::disposing()
{
    EventObject aEvt( *this );

    // if we're still active, simulate a "deactivated" event
    if ( m_xActiveControl.is() )
        m_aActivateListeners.notifyEach( &XFormControllerListener::formDeactivated, aEvt );

    // notify all our listeners
    m_aActivateListeners.disposeAndClear(aEvt);
    m_aModifyListeners.disposeAndClear(aEvt);
    m_aErrorListeners.disposeAndClear(aEvt);
    m_aDeleteListeners.disposeAndClear(aEvt);
    m_aRowSetApproveListeners.disposeAndClear(aEvt);
    m_aParameterListeners.disposeAndClear(aEvt);
    m_aFilterListeners.disposeAndClear(aEvt);

    removeBoundFieldListener();
    stopFiltering();

    m_aControlBorderManager.restoreAll();

    m_aFilterRows.clear();

    ::osl::MutexGuard aGuard( m_aMutex );
    m_xActiveControl = nullptr;
    implSetCurrentControl( nullptr );

    // clean up our children
    for (const auto& rpChild : m_aChildren)
    {
        // search the position of the model within the form
        Reference< XFormComponent >  xForm(rpChild->getModel(), UNO_QUERY);
        sal_uInt32 nPos = m_xModelAsIndex->getCount();
        Reference< XFormComponent > xTemp;
        for( ; nPos; )
        {

            m_xModelAsIndex->getByIndex( --nPos ) >>= xTemp;
            if ( xForm.get() == xTemp.get() )
            {
                Reference< XInterface > xIfc( rpChild, UNO_QUERY );
                m_xModelAsManager->detach( nPos, xIfc );
                break;
            }
        }

        Reference< XComponent > (rpChild, UNO_QUERY_THROW)->dispose();
    }
    m_aChildren.clear();

    disposeAllFeaturesAndDispatchers();

    if ( m_xFormOperations.is() )
        m_xFormOperations->dispose();
    m_xFormOperations.clear();

    if (m_bDBConnection)
        unload();

    setContainer( nullptr );
    setModel( nullptr );
    setParent( nullptr );

    ::comphelper::disposeComponent( m_xComposer );

    m_bDBConnection = false;
}


namespace
{
    bool lcl_shouldUseDynamicControlBorder( const Reference< XInterface >& _rxForm, const Any& _rDynamicColorProp )
    {
        bool bDoUse = false;
        if ( !( _rDynamicColorProp >>= bDoUse ) )
        {
            DocumentType eDocType = DocumentClassification::classifyHostDocument( _rxForm );
            return ControlLayouter::useDynamicBorderColor( eDocType );
        }
        return bDoUse;
    }
}


void SAL_CALL FormController::propertyChange(const PropertyChangeEvent& evt)
{
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
    if ( evt.PropertyName == FM_PROP_BOUNDFIELD )
    {
        Reference<XPropertySet> xOldBound;
        evt.OldValue >>= xOldBound;
        if ( !xOldBound.is() && evt.NewValue.hasValue() )
        {
            Reference< XControlModel > xControlModel(evt.Source,UNO_QUERY);
            Reference< XControl > xControl = findControl(m_aControls,xControlModel,false,false);
            if ( xControl.is() )
            {
                startControlModifyListening( xControl );
                Reference<XPropertySet> xProp(xControlModel,UNO_QUERY);
                if ( xProp.is() )
                    xProp->removePropertyChangeListener(FM_PROP_BOUNDFIELD, this);
            }
        }
    }
    else
    {
        bool bModifiedChanged = (evt.PropertyName == FM_PROP_ISMODIFIED);
        bool bNewChanged = (evt.PropertyName == FM_PROP_ISNEW);
        if (bModifiedChanged || bNewChanged)
        {
            ::osl::MutexGuard aGuard( m_aMutex );
            if (bModifiedChanged)
                m_bCurrentRecordModified = ::comphelper::getBOOL(evt.NewValue);
            else
                m_bCurrentRecordNew = ::comphelper::getBOOL(evt.NewValue);

            // toggle the locking
            if (m_bLocked != determineLockState())
            {
                m_bLocked = !m_bLocked;
                setLocks();
                if (isListeningForChanges())
                    startListening();
                else
                    stopListening();
            }

            if ( bNewChanged )
                m_aToggleEvent.Call();

            if (!m_bCurrentRecordModified)
                m_bModified = false;
        }
        else if ( evt.PropertyName == FM_PROP_DYNAMIC_CONTROL_BORDER )
        {
            bool bEnable = lcl_shouldUseDynamicControlBorder( evt.Source, evt.NewValue );
            if ( bEnable )
            {
                m_aControlBorderManager.enableDynamicBorderColor();
                if ( m_xActiveControl.is() )
                    m_aControlBorderManager.focusGained( m_xActiveControl );
            }
            else
            {
                m_aControlBorderManager.disableDynamicBorderColor();
            }
        }
    }
}


bool FormController::replaceControl( const Reference< XControl >& _rxExistentControlconst Reference< XControl >& _rxNewControl )
{
    bool bSuccess = false;
    try
    {
        Reference< XIdentifierReplace > xContainer( getContainer(), UNO_QUERY );
        DBG_ASSERT( xContainer.is(), "FormController::replaceControl: yes, it's not required by the service description, but XIdentifierReplace would be nice!" );
        if ( xContainer.is() )
        {
            // look up the ID of _rxExistentControl
            const Sequence< sal_Int32 > aIdentifiers( xContainer->getIdentifiers() );
            const sal_Int32* pIdentifiers = std::find_if(aIdentifiers.begin(), aIdentifiers.end(),
                [&xContainer, &_rxExistentControl](const sal_Int32 nId) {
                    Reference< XControl > xCheck( xContainer->getByIdentifier( nId ), UNO_QUERY );
                    return xCheck == _rxExistentControl;
                });
            DBG_ASSERT( pIdentifiers != aIdentifiers.end(), "FormController::replaceControl: did not find the control in the container!" );
            if ( pIdentifiers != aIdentifiers.end() )
            {
                bool bReplacedWasActive = ( m_xActiveControl.get() == _rxExistentControl.get() );
                bool bReplacedWasCurrent = ( m_xCurrentControl.get() == _rxExistentControl.get() );

                if ( bReplacedWasActive )
                {
                    m_xActiveControl = nullptr;
                    implSetCurrentControl( nullptr );
                }
                else if ( bReplacedWasCurrent )
                {
                    implSetCurrentControl( _rxNewControl );
                }

                // carry over the model
                _rxNewControl->setModel( _rxExistentControl->getModel() );

                xContainer->replaceByIdentifer( *pIdentifiers, Any( _rxNewControl ) );
                bSuccess = true;

                if ( bReplacedWasActive )
                {
                    Reference< XWindow > xControlWindow( _rxNewControl, UNO_QUERY );
                    if ( xControlWindow.is() )
                        xControlWindow->setFocus();
                }
            }
        }
    }
    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }

    Reference< XControl > xDisposeIt( bSuccess ? _rxExistentControl : _rxNewControl );
    ::comphelper::disposeComponent( xDisposeIt );
    return bSuccess;
}


void FormController::toggleAutoFields(bool bAutoFields)
{
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );


    Sequence< Reference< XControl > > aControlsCopy( m_aControls );
    const Reference< XControl >* pControls = aControlsCopy.getConstArray();
    sal_Int32 nControls = aControlsCopy.getLength();

    if (bAutoFields)
    {
        // as we don't want new controls to be attached to the scripting environment
        // we change attach flags
        m_bAttachEvents = false;
        for (sal_Int32 i = nControls; i > 0;)
        {
            Reference< XControl > xControl = pControls[--i];
            if (xControl.is())
            {
                Reference< XPropertySet >  xSet(xControl->getModel(), UNO_QUERY);
                if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
                {
                    // does the model use a bound field ?
                    Reference< XPropertySet >  xField;
                    xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;

                    // is it an autofield?
                    if  (   xField.is()
                        &&  ::comphelper::hasProperty( FM_PROP_AUTOINCREMENT, xField )
                        &&  ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_AUTOINCREMENT ) )
                        )
                    {
                        replaceControl( xControl, new FmXAutoControl() );
                    }
                }
            }
        }
        m_bAttachEvents = true;
    }
    else
    {
        m_bDetachEvents = false;
        for (sal_Int32 i = nControls; i > 0;)
        {
            Reference< XControl > xControl = pControls[--i];
            if (xControl.is())
            {
                Reference< XPropertySet >  xSet(xControl->getModel(), UNO_QUERY);
                if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
                {
                    // does the model use a bound field ?
                    Reference< XPropertySet >  xField;
                    xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;

                    // is it an autofield?
                    if  (   xField.is()
                        &&  ::comphelper::hasProperty( FM_PROP_AUTOINCREMENT, xField )
                        &&  ::comphelper::getBOOL( xField->getPropertyValue(FM_PROP_AUTOINCREMENT ) )
                        )
                    {
                        OUString sServiceName;
                        OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DEFAULTCONTROL ) >>= sServiceName );
                        Reference< XControl > xNewControl( m_xComponentContext->getServiceManager()->createInstanceWithContext( sServiceName, m_xComponentContext ), UNO_QUERY );
                        replaceControl( xControl, xNewControl );
                    }
                }
            }
        }
        m_bDetachEvents = true;
    }
}


IMPL_LINK_NOARG(FormController, OnToggleAutoFields, void*, void)
{
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );

    toggleAutoFields(m_bCurrentRecordNew);
}

// XTextListener
void SAL_CALL FormController::textChanged(const TextEvent& e)
{
    // SYNCHRONIZED -->
    ::osl::ClearableMutexGuard aGuard( m_aMutex );
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
    if ( !m_bFiltering )
    {
        impl_onModify();
        return;
    }

    if ( m_bSuspendFilterTextListening )
        return;

    Reference< XTextComponent >  xText(e.Source,UNO_QUERY);
    OUString aText = xText->getText();

    if ( m_aFilterRows.empty() )
        appendEmptyDisjunctiveTerm();

    // find the current row
    if ( ( m_nCurrentFilterPosition < 0 ) || ( o3tl::make_unsigned(m_nCurrentFilterPosition) >= m_aFilterRows.size() )  )
    {
        OSL_ENSURE( false"FormController::textChanged: m_nCurrentFilterPosition is wrong!" );
        return;
    }

    FmFilterRow& rRow = m_aFilterRows[ m_nCurrentFilterPosition ];

    // do we have a new filter
    if (!aText.isEmpty())
        rRow[xText] = aText;
    else
    {
        // do we have the control in the row
        FmFilterRow::iterator iter = rRow.find(xText);
        // erase the entry out of the row
        if (iter != rRow.end())
            rRow.erase(iter);
    }

    // multiplex the event to our FilterControllerListeners
    FilterEvent aEvent;
    aEvent.Source = *this;
    aEvent.FilterComponent = ::std::find( m_aFilterComponents.begin(), m_aFilterComponents.end(), xText ) - m_aFilterComponents.begin();
    aEvent.DisjunctiveTerm = getActiveTerm();
    aEvent.PredicateExpression = aText;

    aGuard.clear();
    // <-- SYNCHRONIZED

    // notify the changed filter expression
    m_aFilterListeners.notifyEach( &XFilterControllerListener::predicateExpressionChanged, aEvent );
}

// XItemListener
void SAL_CALL FormController::itemStateChanged(const ItemEvent& /*rEvent*/)
{
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
    impl_onModify();
}

// XModificationBroadcaster
void SAL_CALL FormController::addModifyListener(const Reference< XModifyListener > & l)
{
    ::osl::MutexGuard aGuard( m_aMutex );
    impl_checkDisposed_throw();
    m_aModifyListeners.addInterface( l );
}

void FormController::removeModifyListener(const Reference< XModifyListener > & l)
{
    ::osl::MutexGuard aGuard( m_aMutex );
    impl_checkDisposed_throw();
    m_aModifyListeners.removeInterface( l );
}

// XModificationListener
void FormController::modified( const EventObject& _rEvent )
{
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );

    try
    {
        if ( _rEvent.Source != m_xActiveControl )
        {   // let this control grab the focus
            // (this case may happen if somebody moves the scroll wheel of the mouse over a control
            // which does not have the focus)
            // 85511 - 29.05.2001 - frank.schoenheit@germany.sun.com

            // also, it happens when an image control gets a new image by double-clicking it
            // #i88458# / 2009-01-12 / frank.schoenheit@sun.com
            Reference< XWindow > xControlWindow( _rEvent.Source, UNO_QUERY_THROW );
            xControlWindow->setFocus();
        }
    }
    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }

    impl_onModify();
}

void FormController::impl_checkDisposed_throw() const
{
    if ( impl_isDisposed_nofail() )
        throw DisposedException( OUString(), *const_cast< FormController* >( this ) );
}

void FormController::impl_onModify()
{
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );

    {
        ::osl::MutexGuard aGuard( m_aMutex );
        if ( !m_bModified )
            m_bModified = true;
    }

    EventObject aEvt(getXWeak());
    m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvt );
}

void FormController::impl_addFilterRow( const FmFilterRow& _row )
{
    m_aFilterRows.push_back( _row );

    if ( m_aFilterRows.size() == 1 )
    {   // that's the first row ever
        OSL_ENSURE( m_nCurrentFilterPosition == -1, "FormController::impl_addFilterRow: inconsistency!" );
        m_nCurrentFilterPosition = 0;
    }
}

void FormController::impl_appendEmptyFilterRow( ::osl::ClearableMutexGuard& _rClearBeforeNotify )
{
    // SYNCHRONIZED -->
    impl_addFilterRow( FmFilterRow() );

    // notify the listeners
    FilterEvent aEvent;
    aEvent.Source = *this;
    aEvent.DisjunctiveTerm = static_cast<sal_Int32>(m_aFilterRows.size()) - 1;
    _rClearBeforeNotify.clear();
    // <-- SYNCHRONIZED
    m_aFilterListeners.notifyEach( &XFilterControllerListener::disjunctiveTermAdded, aEvent );
}

bool FormController::determineLockState() const
{
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
    // a.) in filter mode we are always locked
    // b.) if we have no valid model or our model (a result set) is not alive -> we're locked
    // c.) if we are inserting everything is OK and we are not locked
    // d.) if are not updatable or on invalid position
    Reference< XResultSet >  xResultSet(m_xModelAsIndex, UNO_QUERY);
    if (m_bFiltering || !xResultSet.is() || !isRowSetAlive(xResultSet))
        return true;
    else
        return !(m_bCanInsert && m_bCurrentRecordNew)
            && (xResultSet->isBeforeFirst() || xResultSet->isAfterLast() || xResultSet->rowDeleted() || !m_bCanUpdate);
}

//  FocusListener
void FormController::focusGained(const FocusEvent& e)
{
    // SYNCHRONIZED -->
    ::osl::ClearableMutexGuard aGuard( m_aMutex );
    impl_checkDisposed_throw();

    m_aControlBorderManager.focusGained( e.Source );

    Reference< XControl >  xControl(e.Source, UNO_QUERY);
    if (m_bDBConnection)
    {
        // do we need to keep the locking of the commit
        // we hold the lock as long as the control differs from the current
        // otherwise we disabled the lock
        m_bCommitLock = m_bCommitLock && xControl.get() != m_xCurrentControl.get();
        if (m_bCommitLock)
            return;

        // when do we have to commit a value to form or a filter
        // a.) if the current value is modified
        // b.) there must be a current control
        // c.) and it must be different from the new focus owning control or
        // d.) the focus is moving around (so we have only one control)

        if  (   ( m_bModified || m_bFiltering )
            &&  m_xCurrentControl.is()
            &&  (   ( xControl.get() != m_xCurrentControl.get() )
                ||  (   ( e.FocusFlags & FocusChangeReason::AROUND )
                    &&  ( m_bCycle || m_bFiltering )
                    )
                )
            )
        {
            // check the old control if the content is ok
#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
            Reference< XBoundControl >  xLockingTest(m_xCurrentControl, UNO_QUERY);
            bool bControlIsLocked = xLockingTest.is() && xLockingTest->getLock();
            assert(!bControlIsLocked && "FormController::Gained: I'm modified and the current control is locked ? How this ?");
            // normally, a locked control should not be modified, so probably my bModified must
            // have been set from a different context, which I would not understand ...
#endif
            DBG_ASSERT(m_xCurrentControl.is(), "no CurrentControl set");
            // first the control ask if it supports the IFace
            Reference< XBoundComponent >  xBound(m_xCurrentControl, UNO_QUERY);
            if (!xBound.is() && m_xCurrentControl.is())
                xBound.set(m_xCurrentControl->getModel(), UNO_QUERY);

            // lock if we lose the focus during commit
            m_bCommitLock = true;

            // commit unsuccessful, reset focus
            if (xBound.is() && !xBound->commit())
            {
                // the commit failed and we don't commit again until the current control
                // which couldn't be commit gains the focus again
                Reference< XWindow >  xWindow(m_xCurrentControl, UNO_QUERY);
                if (xWindow.is())
                    xWindow->setFocus();
                return;
            }
            else
            {
                m_bModified = false;
                m_bCommitLock = false;
            }
        }

        if (!m_bFiltering && m_bCycle && (e.FocusFlags & FocusChangeReason::AROUND) && m_xCurrentControl.is())
        {
            OSL_ENSURE( m_xFormOperations.is(), "FormController::focusGained: hmm?" );
                // should have been created in setModel
            try
            {
                if ( e.FocusFlags & FocusChangeReason::FORWARD )
                {
                    if ( m_xFormOperations.is() && m_xFormOperations->isEnabled( FormFeature::MoveToNext ) )
                        m_xFormOperations->execute( FormFeature::MoveToNext );
                }
                else // backward
                {
                    if ( m_xFormOperations.is() && m_xFormOperations->isEnabled( FormFeature::MoveToPrevious ) )
                        m_xFormOperations->execute( FormFeature::MoveToPrevious );
                }
            }
            catch ( const Exception& )
            {
                // don't handle this any further. That's an ... admissible error.
                DBG_UNHANDLED_EXCEPTION("svx");
            }
        }
    }

    // still one and the same control
    if  (   ( m_xActiveControl == xControl )
        &&  ( xControl == m_xCurrentControl )
        )
    {
        DBG_ASSERT(m_xCurrentControl.is(), "No CurrentControl selected");
        return;
    }

    bool bActivated = !m_xActiveControl.is() && xControl.is();

    m_xActiveControl  = xControl;

    implSetCurrentControl( xControl );
    SAL_WARN_IF( !m_xCurrentControl.is(), "svx.form""implSetCurrentControl did nonsense!" );

    if ( bActivated )
    {
        // (asynchronously) call activation handlers
        m_aActivationEvent.Call();

        // call modify listeners
        if ( m_bModified )
            m_aModifyListeners.notifyEach( &XModifyListener::modified, EventObject( *this ) );
    }

    // invalidate all features which depend on the currently focused control
    if ( m_bDBConnection && !m_bFiltering )
        implInvalidateCurrentControlDependentFeatures();

    if ( !m_xCurrentControl.is() )
        return;

    // control gets focus, then possibly in the visible range
    Reference< XFormControllerContext > xContext( m_xFormControllerContext );
    Reference< XControl > xCurrentControl( m_xCurrentControl );
    aGuard.clear();
    // <-- SYNCHRONIZED

    if ( xContext.is() )
        xContext->makeVisible( xCurrentControl );
}

IMPL_LINK_NOARG( FormController, OnActivated, void*, void )
{
    EventObject aEvent;
    aEvent.Source = *this;
    m_aActivateListeners.notifyEach( &XFormControllerListener::formActivated, aEvent );
}

IMPL_LINK_NOARG( FormController, OnDeactivated, void*, void )
{
    EventObject aEvent;
    aEvent.Source = *this;
    m_aActivateListeners.notifyEach( &XFormControllerListener::formDeactivated, aEvent );
}

void FormController::focusLost(const FocusEvent& e)
{
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );

    m_aControlBorderManager.focusLost( e.Source );

    Reference< XWindowPeer >  xNext(e.NextFocus, UNO_QUERY);
    // if focus hasn't passed to some other window, e.g. focus in a welded item, don't deactivate
    if (!xNext)
        return;
    Reference< XControl >  xNextControl = isInList(xNext);
    if (!xNextControl.is())
    {
        m_xActiveControl = nullptr;
        m_aDeactivationEvent.Call();
    }
}

void SAL_CALL FormController::mousePressed( const awt::MouseEvent& /*_rEvent*/ )
{
    // not interested in
}

void SAL_CALL FormController::mouseReleased( const awt::MouseEvent& /*_rEvent*/ )
{
    // not interested in
}

void SAL_CALL FormController::mouseEntered( const awt::MouseEvent& _rEvent )
{
    m_aControlBorderManager.mouseEntered( _rEvent.Source );
}

void SAL_CALL FormController::mouseExited( const awt::MouseEvent& _rEvent )
{
    m_aControlBorderManager.mouseExited( _rEvent.Source );
}

void SAL_CALL FormController::componentValidityChanged( const EventObject& _rSource )
{
    Reference< XControl > xControl( findControl( m_aControls, Reference< XControlModel >( _rSource.Source, UNO_QUERY ), falsefalse ) );
    Reference< XValidatableFormComponent > xValidatable( _rSource.Source, UNO_QUERY );

    OSL_ENSURE( xControl.is() && xValidatable.is(), "FormController::componentValidityChanged: huh?" );

    if ( xControl.is() && xValidatable.is() )
        m_aControlBorderManager.validityChanged( xControl, xValidatable );
}


void FormController::setModel(const Reference< XTabControllerModel > & Model)
{
    ::osl::MutexGuard aGuard( m_aMutex );
    impl_checkDisposed_throw();

    DBG_ASSERT(m_xTabController.is(), "FormController::setModel : invalid aggregate !");

    try
    {
        // disconnect from the old model
        if (m_xModelAsIndex.is())
        {
            if (m_bDBConnection)
            {
                // we are currently working on the model
                EventObject aEvt(m_xModelAsIndex);
                unloaded(aEvt);
            }

            Reference< XLoadable >  xForm(m_xModelAsIndex, UNO_QUERY);
            if (xForm.is())
                xForm->removeLoadListener(this);

            Reference< XSQLErrorBroadcaster >  xBroadcaster(m_xModelAsIndex, UNO_QUERY);
            if (xBroadcaster.is())
                xBroadcaster->removeSQLErrorListener(this);

            Reference< XDatabaseParameterBroadcaster >  xParamBroadcaster(m_xModelAsIndex, UNO_QUERY);
            if (xParamBroadcaster.is())
                xParamBroadcaster->removeParameterListener(this);

        }

        disposeAllFeaturesAndDispatchers();

        if ( m_xFormOperations.is() )
            m_xFormOperations->dispose();
        m_xFormOperations.clear();

        // set the new model wait for the load event
        if (m_xTabController.is())
            m_xTabController->setModel(Model);
        m_xModelAsIndex.set(Model, UNO_QUERY);
        m_xModelAsManager.set(Model, UNO_QUERY);

        // only if both ifaces exit, the controller will work successful
        if (!m_xModelAsIndex.is() || !m_xModelAsManager.is())
        {
            m_xModelAsManager = nullptr;
            m_xModelAsIndex = nullptr;
        }

        if (m_xModelAsIndex.is())
        {
            // re-create m_xFormOperations
            m_xFormOperations = FormOperations::createWithFormController( m_xComponentContext, this );
            m_xFormOperations->setFeatureInvalidation( this );

            // adding load and ui interaction listeners
            Reference< XLoadable >  xForm(Model, UNO_QUERY);
            if (xForm.is())
                xForm->addLoadListener(this);

            Reference< XSQLErrorBroadcaster >  xBroadcaster(Model, UNO_QUERY);
            if (xBroadcaster.is())
                xBroadcaster->addSQLErrorListener(this);

            Reference< XDatabaseParameterBroadcaster >  xParamBroadcaster(Model, UNO_QUERY);
            if (xParamBroadcaster.is())
                xParamBroadcaster->addParameterListener(this);

            // well, is the database already loaded?
            // then we have to simulate a load event
            Reference< XLoadable >  xCursor(m_xModelAsIndex, UNO_QUERY);
            if (xCursor.is() && xCursor->isLoaded())
            {
                EventObject aEvt(xCursor);
                loaded(aEvt);
            }

            Reference< XPropertySet > xModelProps( m_xModelAsIndex, UNO_QUERY );
            Reference< XPropertySetInfo > xPropInfo( xModelProps->getPropertySetInfo() );
            if (  xPropInfo.is()
               && xPropInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER )
               && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_FOCUS )
               && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_MOUSE )
               && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_INVALID )
               )
            {
                bool bEnableDynamicControlBorder = lcl_shouldUseDynamicControlBorder(
                    xModelProps, xModelProps->getPropertyValue( FM_PROP_DYNAMIC_CONTROL_BORDER ) );
                if ( bEnableDynamicControlBorder )
                    m_aControlBorderManager.enableDynamicBorderColor();
                else
                    m_aControlBorderManager.disableDynamicBorderColor();

                Color nColor;
                if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_FOCUS ) >>= nColor )
                    m_aControlBorderManager.setStatusColor( ControlStatus::Focused, nColor );
                if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_MOUSE ) >>= nColor )
                    m_aControlBorderManager.setStatusColor( ControlStatus::MouseHover, nColor );
                if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_INVALID ) >>= nColor )
                    m_aControlBorderManager.setStatusColor( ControlStatus::Invalid, nColor );
            }
        }
    }
    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }
}


Reference< XTabControllerModel >  FormController::getModel()
{
    ::osl::MutexGuard aGuard( m_aMutex );
    impl_checkDisposed_throw();

    DBG_ASSERT(m_xTabController.is(), "FormController::getModel : invalid aggregate !");
    if (!m_xTabController.is())
        return Reference< XTabControllerModel > ();
    return m_xTabController->getModel();
}


void FormController::addToEventAttacher(const Reference< XControl > & xControl)
{
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=97 G=95

¤ Dauer der Verarbeitung: 0.31 Sekunden  (vorverarbeitet)  ¤

*© 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.