/* -*- 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 );
}
}
catch( const 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;
}
}
catch( const 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 >& _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
Sequence<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 & /*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 + " )" );
}
}
}
catch( const 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 );
}
catch( const 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 >& _rxExistentControl, const 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();
}
}
}
}
catch( const 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();
}
}
catch( const 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 ), false, false ) );
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 );
}
}
}
catch( const 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!" );
OSL_ENSURE( xControl.is(), "FormController::addToEventAttacher: invalid control - how did you reach this?" );
if ( !xControl.is() )
return; /* throw IllegalArgumentException(); */
// register at the event attacher
Reference< XFormComponent > xComp(xControl->getModel(), UNO_QUERY);
if (!(xComp.is() && m_xModelAsIndex.is()))
return;
// and look for the position of the ControlModel in it
sal_uInt32 nPos = m_xModelAsIndex->getCount();
Reference< XFormComponent > xTemp;
for( ; nPos; )
{
m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
if (xComp.get() == xTemp.get())
{
m_xModelAsManager->attach( nPos, Reference<XInterface>( xControl, UNO_QUERY ), Any(xControl) );
break;
}
}
}
void FormController::removeFromEventAttacher(const Reference< XControl > & xControl)
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
OSL_ENSURE( xControl.is(), "FormController::removeFromEventAttacher: invalid control - how did you reach this?" );
if ( !xControl.is() )
return; /* throw IllegalArgumentException(); */
// register at the event attacher
Reference< XFormComponent > xComp(xControl->getModel(), UNO_QUERY);
if ( !(xComp.is() && m_xModelAsIndex.is()) )
return;
// and look for the position of the ControlModel in it
sal_uInt32 nPos = m_xModelAsIndex->getCount();
Reference< XFormComponent > xTemp;
for( ; nPos; )
{
m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
if (xComp.get() == xTemp.get())
{
m_xModelAsManager->detach( nPos, Reference<XInterface>( xControl, UNO_QUERY ) );
break;
}
}
}
void FormController::setContainer(const Reference< XControlContainer > & xContainer)
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
Reference< XTabControllerModel > xTabModel(getModel());
DBG_ASSERT(xTabModel.is() || !xContainer.is(), "No Model defined");
// if we have a new container we need a model
DBG_ASSERT(m_xTabController.is(), "FormController::setContainer : invalid aggregate !");
::osl::MutexGuard aGuard( m_aMutex );
Reference< XContainer > xCurrentContainer;
if (m_xTabController.is())
xCurrentContainer.set(m_xTabController->getContainer(), UNO_QUERY);
if (xCurrentContainer.is())
{
xCurrentContainer->removeContainerListener(this);
if ( m_aTabActivationIdle.IsActive() )
m_aTabActivationIdle.Stop();
// clear the filter map
::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), RemoveComponentTextListener( this ) );
m_aFilterComponents.clear();
// collecting the controls
for (const Reference<XControl>& rControl : m_aControls)
implControlRemoved( rControl, true );
// make database-specific things
if (m_bDBConnection && isListeningForChanges())
stopListening();
m_aControls.realloc( 0 );
}
if (m_xTabController.is())
m_xTabController->setContainer(xContainer);
// What controls belong to the container?
if (xContainer.is() && xTabModel.is())
{
const Sequence< Reference< XControlModel > > aModels = xTabModel->getControlModels();
Sequence< Reference< XControl > > aAllControls = xContainer->getControls();
sal_Int32 nCount = aModels.getLength();
m_aControls = Sequence< Reference< XControl > >( nCount );
Reference< XControl > * pControls = m_aControls.getArray();
// collecting the controls
sal_Int32 j = 0;
for (const Reference< XControlModel >& rModel : aModels )
{
Reference< XControl > xControl = findControl( aAllControls, rModel, false, true );
if ( xControl.is() )
{
pControls[j++] = xControl;
implControlInserted( xControl, true );
}
}
// not every model had an associated control
if (j != nCount)
m_aControls.realloc(j);
// listen at the container
Reference< XContainer > xNewContainer(xContainer, UNO_QUERY);
if (xNewContainer.is())
xNewContainer->addContainerListener(this);
// make database-specific things
if (m_bDBConnection)
{
m_bLocked = determineLockState();
setLocks();
if (!isLocked())
startListening();
}
}
// the controls are in the right order
m_bControlsSorted = true;
}
Reference< XControlContainer > FormController::getContainer()
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
DBG_ASSERT(m_xTabController.is(), "FormController::getContainer : invalid aggregate !");
if (!m_xTabController.is())
return Reference< XControlContainer > ();
return m_xTabController->getContainer();
}
Sequence< Reference< XControl > > FormController::getControls()
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
if (!m_bControlsSorted)
{
Reference< XTabControllerModel > xModel = getModel();
if (!xModel.is())
return m_aControls;
const Sequence< Reference< XControlModel > > aControlModels = xModel->getControlModels();
sal_Int32 nModels = aControlModels.getLength();
Sequence< Reference< XControl > > aNewControls(nModels);
Reference< XControl > * pControls = aNewControls.getArray();
Reference< XControl > xControl;
// rearrange the controls according to the tab order sequence
sal_Int32 j = 0;
for ( const Reference< XControlModel >& rModel : aControlModels )
{
xControl = findControl( m_aControls, rModel, true, true );
if ( xControl.is() )
pControls[j++] = xControl;
}
// not every model had an associated control
if ( j != nModels )
aNewControls.realloc( j );
m_aControls = std::move(aNewControls);
m_bControlsSorted = true;
}
return m_aControls;
}
void FormController::autoTabOrder()
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
DBG_ASSERT(m_xTabController.is(), "FormController::autoTabOrder : invalid aggregate !");
if (m_xTabController.is())
m_xTabController->autoTabOrder();
}
void FormController::activateTabOrder()
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
DBG_ASSERT(m_xTabController.is(), "FormController::activateTabOrder : invalid aggregate !");
if (m_xTabController.is())
m_xTabController->activateTabOrder();
}
void FormController::setControlLock(const Reference< XControl > & xControl)
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
bool bLocked = isLocked();
// It is locked
// a. if the entire record is locked
// b. if the associated field is locked
Reference< XBoundControl > xBound(xControl, UNO_QUERY);
if (!(xBound.is() &&
( (bLocked && bLocked != bool(xBound->getLock())) ||
!bLocked))) // always uncheck individual fields when unlocking
return;
// there is a data source
Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
if (!(xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet)))
return;
// what about the ReadOnly and Enable properties
bool bTouch = true;
if (::comphelper::hasProperty(FM_PROP_ENABLED, xSet))
bTouch = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ENABLED));
if (bTouch && ::comphelper::hasProperty(FM_PROP_READONLY, xSet))
bTouch = !::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_READONLY));
if (!bTouch)
return;
Reference< XPropertySet > xField;
xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
if (!xField.is())
return;
if (bLocked)
xBound->setLock(bLocked);
else
{
try
{
Any aVal = xField->getPropertyValue(FM_PROP_ISREADONLY);
if (aVal.hasValue() && ::comphelper::getBOOL(aVal))
xBound->setLock(true);
else
xBound->setLock(bLocked);
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
}
}
}
void FormController::setLocks()
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
// lock/unlock all controls connected to a data source
for (const Reference<XControl>& rControl : m_aControls)
setControlLock( rControl );
}
namespace
{
bool lcl_shouldListenForModifications( const Reference< XControl >& _rxControl, const Reference< XPropertyChangeListener >& _rxBoundFieldListener )
{
bool bShould = false;
Reference< XBoundComponent > xBound( _rxControl, UNO_QUERY );
if ( xBound.is() )
{
bShould = true;
}
else if ( _rxControl.is() )
{
Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY );
if ( xModelProps.is() && ::comphelper::hasProperty( FM_PROP_BOUNDFIELD, xModelProps ) )
{
Reference< XPropertySet > xField;
xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ) >>= xField;
bShould = xField.is();
if ( !bShould && _rxBoundFieldListener.is() )
xModelProps->addPropertyChangeListener( FM_PROP_BOUNDFIELD, _rxBoundFieldListener );
}
}
return bShould;
}
}
void FormController::startControlModifyListening(const Reference< XControl > & xControl)
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
bool bModifyListening = lcl_shouldListenForModifications( xControl, this );
// artificial while
while ( bModifyListening )
{
Reference< XModifyBroadcaster > xMod(xControl, UNO_QUERY);
if (xMod.is())
{
xMod->addModifyListener(this);
break;
}
// all the text to prematurely recognize a modified
Reference< XTextComponent > xText(xControl, UNO_QUERY);
if (xText.is())
{
xText->addTextListener(this);
break;
}
Reference< XCheckBox > xBox(xControl, UNO_QUERY);
if (xBox.is())
{
xBox->addItemListener(this);
break;
}
Reference< XComboBox > xCbBox(xControl, UNO_QUERY);
if (xCbBox.is())
{
xCbBox->addItemListener(this);
break;
}
Reference< XListBox > xListBox(xControl, UNO_QUERY);
if (xListBox.is())
{
xListBox->addItemListener(this);
break;
}
break;
}
}
void FormController::stopControlModifyListening(const Reference< XControl > & xControl)
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
bool bModifyListening = lcl_shouldListenForModifications( xControl, nullptr );
// artificial while
while (bModifyListening)
{
Reference< XModifyBroadcaster > xMod(xControl, UNO_QUERY);
if (xMod.is())
{
xMod->removeModifyListener(this);
break;
}
// all the text to prematurely recognize a modified
Reference< XTextComponent > xText(xControl, UNO_QUERY);
if (xText.is())
{
xText->removeTextListener(this);
break;
}
Reference< XCheckBox > xBox(xControl, UNO_QUERY);
if (xBox.is())
{
xBox->removeItemListener(this);
break;
}
Reference< XComboBox > xCbBox(xControl, UNO_QUERY);
if (xCbBox.is())
{
xCbBox->removeItemListener(this);
break;
}
Reference< XListBox > xListBox(xControl, UNO_QUERY);
if (xListBox.is())
{
xListBox->removeItemListener(this);
break;
}
break;
}
}
void FormController::startListening()
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
m_bModified = false;
// now register at bound fields
for (const Reference<XControl>& rControl : m_aControls)
startControlModifyListening( rControl );
}
void FormController::stopListening()
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
m_bModified = false;
// now register at bound fields
for (const Reference<XControl>& rControl : m_aControls)
stopControlModifyListening( rControl );
}
Reference< XControl > FormController::findControl(Sequence< Reference< XControl > >& _rControls, const Reference< XControlModel > & xCtrlModel ,bool _bRemove,bool _bOverWrite) const
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
DBG_ASSERT( xCtrlModel.is(), "findControl - which ?!" );
const Reference< XControl >* pControls = std::find_if(std::cbegin(_rControls), std::cend(_rControls),
[&xCtrlModel](const Reference< XControl >& rControl) {
return rControl.is() && rControl->getModel().get() == xCtrlModel.get(); });
if (pControls != std::cend(_rControls))
{
Reference< XControl > xControl( *pControls );
auto i = static_cast<sal_Int32>(std::distance(std::cbegin(_rControls), pControls));
if ( _bRemove )
::comphelper::removeElementAt( _rControls, i );
else if ( _bOverWrite )
_rControls.getArray()[i].clear();
return xControl;
}
return Reference< XControl > ();
}
void FormController::implControlInserted( const Reference< XControl>& _rxControl, bool _bAddToEventAttacher )
{
Reference< XWindow > xWindow( _rxControl, UNO_QUERY );
if ( xWindow.is() )
{
xWindow->addFocusListener( this );
xWindow->addMouseListener( this );
if ( _bAddToEventAttacher )
addToEventAttacher( _rxControl );
}
// add a dispatch interceptor to the control (if supported)
Reference< XDispatchProviderInterception > xInterception( _rxControl, UNO_QUERY );
if ( xInterception.is() )
createInterceptor( xInterception );
if ( !_rxControl.is() )
return;
Reference< XControlModel > xModel( _rxControl->getModel() );
// we want to know about the reset of the model of our controls
// (for correctly resetting m_bModified)
Reference< XReset > xReset( xModel, UNO_QUERY );
if ( xReset.is() )
xReset->addResetListener( this );
// and we want to know about the validity, to visually indicate it
Reference< XValidatableFormComponent > xValidatable( xModel, UNO_QUERY );
if ( xValidatable.is() )
{
xValidatable->addFormComponentValidityListener( this );
m_aControlBorderManager.validityChanged( _rxControl, xValidatable );
}
}
void FormController::implControlRemoved( const Reference< XControl>& _rxControl, bool _bRemoveFromEventAttacher )
{
Reference< XWindow > xWindow( _rxControl, UNO_QUERY );
if ( xWindow.is() )
{
xWindow->removeFocusListener( this );
xWindow->removeMouseListener( this );
if ( _bRemoveFromEventAttacher )
removeFromEventAttacher( _rxControl );
}
Reference< XDispatchProviderInterception > xInterception( _rxControl, UNO_QUERY);
if ( xInterception.is() )
deleteInterceptor( xInterception );
if ( _rxControl.is() )
{
Reference< XControlModel > xModel( _rxControl->getModel() );
Reference< XReset > xReset( xModel, UNO_QUERY );
if ( xReset.is() )
xReset->removeResetListener( this );
Reference< XValidatableFormComponent > xValidatable( xModel, UNO_QUERY );
if ( xValidatable.is() )
xValidatable->removeFormComponentValidityListener( this );
}
}
void FormController::implSetCurrentControl( const Reference< XControl >& _rxControl )
{
if ( m_xCurrentControl.get() == _rxControl.get() )
return;
Reference< XGridControl > xGridControl( m_xCurrentControl, UNO_QUERY );
if ( xGridControl.is() )
xGridControl->removeGridControlListener( this );
m_xCurrentControl = _rxControl;
xGridControl.set( m_xCurrentControl, UNO_QUERY );
if ( xGridControl.is() )
xGridControl->addGridControlListener( this );
}
void FormController::insertControl(const Reference< XControl > & xControl)
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
m_bControlsSorted = false;
m_aControls.realloc(m_aControls.getLength() + 1);
m_aControls.getArray()[m_aControls.getLength() - 1] = xControl;
if (m_pColumnInfoCache)
m_pColumnInfoCache->deinitializeControls();
implControlInserted( xControl, m_bAttachEvents );
if (m_bDBConnection && !m_bFiltering)
setControlLock(xControl);
if (isListeningForChanges() && m_bAttachEvents)
startControlModifyListening( xControl );
}
void FormController::removeControl(const Reference< XControl > & xControl)
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
auto pControl = std::find_if(std::cbegin(m_aControls), std::cend(m_aControls),
[&xControl](const Reference< XControl >& rControl) { return xControl.get() == rControl.get(); });
if (pControl != std::cend(m_aControls))
{
auto nIndex = static_cast<sal_Int32>(std::distance(std::cbegin(m_aControls), pControl));
::comphelper::removeElementAt( m_aControls, nIndex );
}
FilterComponents::iterator componentPos = ::std::find( m_aFilterComponents.begin(), m_aFilterComponents.end(), xControl );
if ( componentPos != m_aFilterComponents.end() )
m_aFilterComponents.erase( componentPos );
implControlRemoved( xControl, m_bDetachEvents );
if ( isListeningForChanges() && m_bDetachEvents )
stopControlModifyListening( xControl );
}
// XLoadListener
void FormController::loaded(const EventObject& rEvent)
{
OSL_ENSURE( rEvent.Source == m_xModelAsIndex, "FormController::loaded: where did this come from?" );
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
::osl::MutexGuard aGuard( m_aMutex );
Reference< XRowSet > xForm(rEvent.Source, UNO_QUERY);
// do we have a connected data source
if (xForm.is() && getConnection(xForm).is())
{
Reference< XPropertySet > xSet(xForm, UNO_QUERY);
if (xSet.is())
{
Any aVal = xSet->getPropertyValue(FM_PROP_CYCLE);
sal_Int32 aVal2 = 0;
::cppu::enum2int(aVal2,aVal);
m_bCycle = !aVal.hasValue() || static_cast<form::TabulatorCycle>(aVal2) == TabulatorCycle_RECORDS;
m_bCanUpdate = canUpdate(xSet);
m_bCanInsert = canInsert(xSet);
m_bCurrentRecordModified = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED));
m_bCurrentRecordNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
startFormListening( xSet, false );
// set the locks for the current controls
if (getContainer().is())
{
m_aLoadEvent.Call();
}
}
else
{
m_bCanInsert = m_bCanUpdate = m_bCycle = false;
m_bCurrentRecordModified = false;
m_bCurrentRecordNew = false;
m_bLocked = false;
}
m_bDBConnection = true;
}
else
{
m_bDBConnection = false;
m_bCanInsert = m_bCanUpdate = m_bCycle = false;
m_bCurrentRecordModified = false;
m_bCurrentRecordNew = false;
m_bLocked = false;
}
Reference< XColumnsSupplier > xFormColumns( xForm, UNO_QUERY );
m_pColumnInfoCache.reset( xFormColumns.is() ? new ColumnInfoCache( xFormColumns ) : nullptr );
updateAllDispatchers();
}
void FormController::updateAllDispatchers() const
{
::std::for_each(
m_aFeatureDispatchers.begin(),
m_aFeatureDispatchers.end(),
[] (const DispatcherContainer::value_type& dispatcher) {
UpdateAllListeners()(dispatcher.second);
});
}
IMPL_LINK_NOARG(FormController, OnLoad, void*, void)
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
m_bLocked = determineLockState();
setLocks();
if (!m_bLocked)
startListening();
// just one exception toggle the auto values
if (m_bCurrentRecordNew)
toggleAutoFields(true);
}
void FormController::unloaded(const EventObject& /*rEvent*/)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
updateAllDispatchers();
}
void FormController::reloading(const EventObject& /*aEvent*/)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
// do the same like in unloading
// just one exception toggle the auto values
m_aToggleEvent.CancelPendingCall();
unload();
}
void FormController::reloaded(const EventObject& aEvent)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
loaded(aEvent);
}
void FormController::unloading(const EventObject& /*aEvent*/)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
unload();
}
void FormController::unload()
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
m_aLoadEvent.CancelPendingCall();
// be sure not to have autofields
if (m_bCurrentRecordNew)
toggleAutoFields(false);
// remove bound field listing again
removeBoundFieldListener();
if (m_bDBConnection && isListeningForChanges())
stopListening();
Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
if ( m_bDBConnection && xSet.is() )
stopFormListening( xSet, false );
m_bDBConnection = false;
m_bCanInsert = m_bCanUpdate = m_bCycle = false;
m_bCurrentRecordModified = m_bCurrentRecordNew = m_bLocked = false;
m_pColumnInfoCache.reset();
}
void FormController::removeBoundFieldListener()
{
for (const Reference<XControl>& rControl : m_aControls)
{
Reference< XPropertySet > xProp( rControl, UNO_QUERY );
if ( xProp.is() )
xProp->removePropertyChangeListener( FM_PROP_BOUNDFIELD, this );
}
}
void FormController::startFormListening( const Reference< XPropertySet >& _rxForm, bool _bPropertiesOnly )
{
try
{
if ( m_bCanInsert || m_bCanUpdate ) // form can be modified
{
_rxForm->addPropertyChangeListener( FM_PROP_ISNEW, this );
_rxForm->addPropertyChangeListener( FM_PROP_ISMODIFIED, this );
if ( !_bPropertiesOnly )
{
// set the Listener for UI interaction
Reference< XRowSetApproveBroadcaster > xApprove( _rxForm, UNO_QUERY );
if ( xApprove.is() )
xApprove->addRowSetApproveListener( this );
// listener for row set changes
Reference< XRowSet > xRowSet( _rxForm, UNO_QUERY );
if ( xRowSet.is() )
xRowSet->addRowSetListener( this );
}
}
Reference< XPropertySetInfo > xInfo = _rxForm->getPropertySetInfo();
if ( xInfo.is() && xInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER ) )
_rxForm->addPropertyChangeListener( FM_PROP_DYNAMIC_CONTROL_BORDER, this );
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
}
}
void FormController::stopFormListening( const Reference< XPropertySet >& _rxForm, bool _bPropertiesOnly )
{
try
{
if ( m_bCanInsert || m_bCanUpdate )
{
_rxForm->removePropertyChangeListener( FM_PROP_ISNEW, this );
_rxForm->removePropertyChangeListener( FM_PROP_ISMODIFIED, this );
if ( !_bPropertiesOnly )
{
Reference< XRowSetApproveBroadcaster > xApprove( _rxForm, UNO_QUERY );
if (xApprove.is())
xApprove->removeRowSetApproveListener(this);
Reference< XRowSet > xRowSet( _rxForm, UNO_QUERY );
if ( xRowSet.is() )
xRowSet->removeRowSetListener( this );
}
}
Reference< XPropertySetInfo > xInfo = _rxForm->getPropertySetInfo();
if ( xInfo.is() && xInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER ) )
_rxForm->removePropertyChangeListener( FM_PROP_DYNAMIC_CONTROL_BORDER, this );
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
}
}
// css::sdbc::XRowSetListener
void FormController::cursorMoved(const EventObject& /*event*/)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
// toggle the locking ?
if (m_bLocked != determineLockState())
{
m_bLocked = !m_bLocked;
setLocks();
if (isListeningForChanges())
startListening();
else
stopListening();
}
// neither the current control nor the current record are modified anymore
m_bCurrentRecordModified = m_bModified = false;
}
void FormController::rowChanged(const EventObject& /*event*/)
{
// not interested in ...
}
void FormController::rowSetChanged(const EventObject& /*event*/)
{
// not interested in ...
}
// XContainerListener
void SAL_CALL FormController::elementInserted(const ContainerEvent& evt)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
Reference< XControl > xControl( evt.Element, UNO_QUERY );
if ( !xControl.is() )
return;
Reference< XFormComponent > xModel(xControl->getModel(), UNO_QUERY);
if (xModel.is() && m_xModelAsIndex == xModel->getParent())
{
insertControl(xControl);
if ( m_aTabActivationIdle.IsActive() )
m_aTabActivationIdle.Stop();
m_aTabActivationIdle.Start();
}
// are we in filtermode and a XModeSelector has inserted an element
else if (m_bFiltering && Reference< XModeSelector > (evt.Source, UNO_QUERY).is())
{
xModel.set(evt.Source, UNO_QUERY);
if (xModel.is() && m_xModelAsIndex == xModel->getParent())
{
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;
Reference< XTextComponent > xText(xControl, UNO_QUERY);
// may we filter the field?
if (xText.is() && xField.is() && ::comphelper::hasProperty(FM_PROP_SEARCHABLE, xField) &&
::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_SEARCHABLE)))
{
m_aFilterComponents.push_back( xText );
xText->addTextListener( this );
}
}
}
}
}
void SAL_CALL FormController::elementReplaced(const ContainerEvent& evt)
{
// simulate an elementRemoved
ContainerEvent aRemoveEvent( evt );
aRemoveEvent.Element = evt.ReplacedElement;
aRemoveEvent.ReplacedElement = Any();
elementRemoved( aRemoveEvent );
// simulate an elementInserted
ContainerEvent aInsertEvent( evt );
aInsertEvent.ReplacedElement = Any();
elementInserted( aInsertEvent );
}
void SAL_CALL FormController::elementRemoved(const ContainerEvent& evt)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
Reference< XControl > xControl;
evt.Element >>= xControl;
if (!xControl.is())
return;
Reference< XFormComponent > xModel(xControl->getModel(), UNO_QUERY);
if (xModel.is() && m_xModelAsIndex == xModel->getParent())
{
removeControl(xControl);
// Do not recalculate TabOrder, because it must already work internally!
}
// are we in filtermode and a XModeSelector has inserted an element
else if (m_bFiltering && Reference< XModeSelector > (evt.Source, UNO_QUERY).is())
{
FilterComponents::iterator componentPos = ::std::find(
m_aFilterComponents.begin(), m_aFilterComponents.end(), xControl );
if ( componentPos != m_aFilterComponents.end() )
m_aFilterComponents.erase( componentPos );
}
}
Reference< XControl > FormController::isInList(const Reference< XWindowPeer > & xPeer) const
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
const Reference< XControl >* pControls = m_aControls.getConstArray();
sal_uInt32 nCtrls = m_aControls.getLength();
for ( sal_uInt32 n = 0; n < nCtrls && xPeer.is(); ++n, ++pControls )
{
if ( pControls->is() )
{
Reference< XVclWindowPeer > xCtrlPeer( (*pControls)->getPeer(), UNO_QUERY);
if ( ( xCtrlPeer.get() == xPeer.get() ) || xCtrlPeer->isChild( xPeer ) )
return *pControls;
}
}
return Reference< XControl > ();
}
void FormController::activateFirst()
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
DBG_ASSERT(m_xTabController.is(), "FormController::activateFirst : invalid aggregate !");
if (m_xTabController.is())
m_xTabController->activateFirst();
}
void FormController::activateLast()
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
DBG_ASSERT(m_xTabController.is(), "FormController::activateLast : invalid aggregate !");
if (m_xTabController.is())
m_xTabController->activateLast();
}
// XFormController
Reference< XFormOperations > SAL_CALL FormController::getFormOperations()
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
return m_xFormOperations;
}
Reference< XControl> SAL_CALL FormController::getCurrentControl()
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
return m_xCurrentControl;
}
void SAL_CALL FormController::addActivateListener(const Reference< XFormControllerListener > & l)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
m_aActivateListeners.addInterface(l);
}
void SAL_CALL FormController::removeActivateListener(const Reference< XFormControllerListener > & l)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
m_aActivateListeners.removeInterface(l);
}
void SAL_CALL FormController::addChildController( const Reference< XFormController >& ChildController )
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
if ( !ChildController.is() )
throw IllegalArgumentException( OUString(), *this, 1 );
// TODO: (localized) error message
// the parent of our (to-be-)child must be our own model
Reference< XFormComponent > xFormOfChild( ChildController->getModel(), UNO_QUERY );
if ( !xFormOfChild.is() )
throw IllegalArgumentException( OUString(), *this, 1 );
// TODO: (localized) error message
if ( xFormOfChild->getParent() != m_xModelAsIndex )
throw IllegalArgumentException( OUString(), *this, 1 );
// TODO: (localized) error message
m_aChildren.push_back( ChildController );
ChildController->setParent( *this );
// search the position of the model within the form
sal_uInt32 nPos = m_xModelAsIndex->getCount();
Reference< XFormComponent > xTemp;
for( ; nPos; )
{
m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
if ( xFormOfChild == xTemp )
{
m_xModelAsManager->attach( nPos, Reference<XInterface>( ChildController, UNO_QUERY ), Any( ChildController) );
break;
}
}
}
Reference< XFormControllerContext > SAL_CALL FormController::getContext()
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
return m_xFormControllerContext;
}
void SAL_CALL FormController::setContext( const Reference< XFormControllerContext >& _context )
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
m_xFormControllerContext = _context;
}
Reference< XInteractionHandler > SAL_CALL FormController::getInteractionHandler()
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
return m_xInteractionHandler;
}
void SAL_CALL FormController::setInteractionHandler( const Reference< XInteractionHandler >& _interactionHandler )
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
m_xInteractionHandler = _interactionHandler;
}
void FormController::setFilter(::std::vector<FmFieldInfo>& rFieldInfos)
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
// create the composer
Reference< XRowSet > xForm(m_xModelAsIndex, UNO_QUERY);
Reference< XConnection > xConnection(getConnection(xForm));
if (xForm.is())
{
try
{
Reference< XMultiServiceFactory > xFactory( xConnection, UNO_QUERY_THROW );
m_xComposer.set(
xFactory->createInstance(u"com.sun.star.sdb.SingleSelectQueryComposer"_ustr),
UNO_QUERY_THROW );
Reference< XPropertySet > xSet( xForm, UNO_QUERY );
OUString sStatement = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_ACTIVECOMMAND ) );
OUString sFilter = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_FILTER ) );
m_xComposer->setElementaryQuery( sStatement );
m_xComposer->setFilter( sFilter );
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
}
}
if (m_xComposer.is())
{
const Sequence< Sequence < PropertyValue > > aFilterRows = m_xComposer->getStructuredFilter();
// ok, we receive the list of filters as sequence of fieldnames, value
// now we have to transform the fieldname into UI names, that could be a label of the field or
// an aliasname or the fieldname itself
// first adjust the field names if necessary
Reference< XNameAccess > xQueryColumns =
Reference< XColumnsSupplier >( m_xComposer, UNO_QUERY_THROW )->getColumns();
for (auto& rFieldInfo : rFieldInfos)
{
if ( xQueryColumns->hasByName(rFieldInfo.aFieldName) )
{
if ( (xQueryColumns->getByName(rFieldInfo.aFieldName) >>= rFieldInfo.xField) && rFieldInfo.xField.is() )
rFieldInfo.xField->getPropertyValue(FM_PROP_REALNAME) >>= rFieldInfo.aFieldName;
}
}
Reference< XDatabaseMetaData> xMetaData(xConnection->getMetaData());
// now transfer the filters into Value/TextComponent pairs
::comphelper::UStringMixEqual aCompare(xMetaData->storesMixedCaseQuotedIdentifiers());
// need to parse criteria localized
Reference< XNumberFormatsSupplier> xFormatSupplier( getNumberFormats(xConnection, true));
Reference< XNumberFormatter> xFormatter = NumberFormatter::create(m_xComponentContext);
xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
Locale aAppLocale = Application::GetSettings().GetUILanguageTag().getLocale();
const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetUILocaleDataWrapper() );
OUString strDecimalSeparator = rLocaleWrapper.getNumDecimalSep();
// retrieving the filter
for (const Sequence < PropertyValue >& rRow : aFilterRows)
{
FmFilterRow aRow;
// search a field for the given name
for (const PropertyValue& rRefValue : rRow)
{
// look for the text component
Reference< XPropertySet > xField;
try
{
Reference< XPropertySet > xSet;
OUString aRealName;
// first look with the given name
if (xQueryColumns->hasByName(rRefValue.Name))
{
xQueryColumns->getByName(rRefValue.Name) >>= xSet;
// get the RealName
xSet->getPropertyValue(u"RealName"_ustr) >>= aRealName;
// compare the condition field name and the RealName
if (aCompare(aRealName, rRefValue.Name))
xField = xSet;
}
if (!xField.is())
{
// no we have to check every column to find the realname
Reference< XIndexAccess > xColumnsByIndex(xQueryColumns, UNO_QUERY);
for (sal_Int32 n = 0, nCount = xColumnsByIndex->getCount(); n < nCount; n++)
{
xColumnsByIndex->getByIndex(n) >>= xSet;
xSet->getPropertyValue(u"RealName"_ustr) >>= aRealName;
if (aCompare(aRealName, rRefValue.Name))
{
// get the column by its alias
xField = xSet;
break;
}
}
}
if (!xField.is())
continue;
}
catch (const Exception&)
{
continue;
}
// find the text component
for (const auto& rFieldInfo : rFieldInfos)
{
// we found the field so insert a new entry to the filter row
if (rFieldInfo.xField == xField)
{
// do we already have the control ?
if (aRow.find(rFieldInfo.xText) != aRow.end())
{
OString aVal = m_pParser->getContext().getIntlKeywordAscii(IParseContext::InternationalKeyCode::And);
aRow[rFieldInfo.xText] = aRow[rFieldInfo.xText] + " " +
OStringToOUString(aVal, RTL_TEXTENCODING_ASCII_US) + " " +
::comphelper::getString(rRefValue.Value);
}
else
{
OUString sPredicate,sErrorMsg;
rRefValue.Value >>= sPredicate;
std::unique_ptr< OSQLParseNode > pParseNode = predicateTree(sErrorMsg, sPredicate, xFormatter, xField);
if ( pParseNode != nullptr )
{
OUString sCriteria;
switch (rRefValue.Handle)
{
case css::sdb::SQLFilterOperator::EQUAL:
sCriteria += "=";
break;
case css::sdb::SQLFilterOperator::NOT_EQUAL:
sCriteria += "!=";
break;
case css::sdb::SQLFilterOperator::LESS:
sCriteria += "<";
break;
case css::sdb::SQLFilterOperator::GREATER:
sCriteria += ">";
break;
case css::sdb::SQLFilterOperator::LESS_EQUAL:
sCriteria += "<=";
break;
case css::sdb::SQLFilterOperator::GREATER_EQUAL:
sCriteria += ">=";
break;
case css::sdb::SQLFilterOperator::LIKE:
sCriteria += "LIKE ";
break;
case css::sdb::SQLFilterOperator::NOT_LIKE:
sCriteria += "NOT LIKE ";
break;
case css::sdb::SQLFilterOperator::SQLNULL:
sCriteria += "IS NULL";
break;
case css::sdb::SQLFilterOperator::NOT_SQLNULL:
sCriteria += "IS NOT NULL";
break;
}
pParseNode->parseNodeToPredicateStr( sCriteria
,xConnection
,xFormatter
,xField
,OUString()
,aAppLocale
,strDecimalSeparator
,getParseContext());
aRow[rFieldInfo.xText] = sCriteria;
}
}
}
}
}
if (aRow.empty())
continue;
impl_addFilterRow( aRow );
}
}
// now set the filter controls
for (const auto& rFieldInfo : rFieldInfos)
{
m_aFilterComponents.push_back( rFieldInfo.xText );
}
}
void FormController::startFiltering()
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
Reference< XConnection > xConnection( getConnection( Reference< XRowSet >( m_xModelAsIndex, UNO_QUERY ) ) );
if ( !xConnection.is() )
// nothing to do - can't filter a form which is not connected
return;
// stop listening for controls
if (isListeningForChanges())
stopListening();
m_bFiltering = true;
// as we don't want new controls to be attached to the scripting environment
// we change attach flags
m_bAttachEvents = false;
// exchanging the controls for the current form
Sequence< Reference< XControl > > aControlsCopy( m_aControls );
const Reference< XControl >* pControls = aControlsCopy.getConstArray();
sal_Int32 nControlCount = aControlsCopy.getLength();
// the control we have to activate after replacement
Reference< XNumberFormatsSupplier > xFormatSupplier = getNumberFormats(xConnection, true);
Reference< XNumberFormatter > xFormatter = NumberFormatter::create(m_xComponentContext);
xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
// structure for storing the field info
::std::vector<FmFieldInfo> aFieldInfos;
for (sal_Int32 i = nControlCount; i > 0;)
{
Reference< XControl > xControl = pControls[--i];
if (xControl.is())
{
// no events for the control anymore
removeFromEventAttacher(xControl);
// do we have a mode selector
Reference< XModeSelector > xSelector(xControl, UNO_QUERY);
if (xSelector.is())
{
xSelector->setMode( u"FilterMode"_ustr );
// listening for new controls of the selector
Reference< XContainer > xContainer(xSelector, UNO_QUERY);
if (xContainer.is())
xContainer->addContainerListener(this);
Reference< XEnumerationAccess > xElementAccess(xSelector, UNO_QUERY);
if (xElementAccess.is())
{
Reference< XEnumeration > xEnumeration(xElementAccess->createEnumeration());
Reference< XControl > xSubControl;
while (xEnumeration->hasMoreElements())
{
xEnumeration->nextElement() >>= xSubControl;
if (xSubControl.is())
{
Reference< XPropertySet > xSet(xSubControl->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;
Reference< XTextComponent > xText(xSubControl, UNO_QUERY);
// may we filter the field?
if (xText.is() && xField.is() && ::comphelper::hasProperty(FM_PROP_SEARCHABLE, xField) &&
::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_SEARCHABLE)))
{
aFieldInfos.emplace_back(xField, xText);
xText->addTextListener(this);
}
}
}
}
}
continue;
}
Reference< XPropertySet > xModel( xControl->getModel(), UNO_QUERY );
if (xModel.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xModel))
{
// does the model use a bound field ?
Any aVal = xModel->getPropertyValue(FM_PROP_BOUNDFIELD);
Reference< XPropertySet > xField;
aVal >>= xField;
// may we filter the field?
if ( xField.is()
&& ::comphelper::hasProperty( FM_PROP_SEARCHABLE, xField )
&& ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_SEARCHABLE ) )
)
{
// create a filter control
Reference< XControl > xFilterControl = form::control::FilterControl::createWithFormat(
m_xComponentContext,
getDialogParentWindow(this),
xFormatter,
xModel);
if ( replaceControl( xControl, xFilterControl ) )
{
Reference< XTextComponent > xFilterText( xFilterControl, UNO_QUERY );
aFieldInfos.emplace_back( xField, xFilterText );
xFilterText->addTextListener(this);
}
}
}
else
{
// unsubscribe from EventManager
}
}
}
// we have all filter controls now, so the next step is to read the filters from the form
// resolve all aliases and set the current filter to the according structure
setFilter(aFieldInfos);
Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
if ( xSet.is() )
stopFormListening( xSet, true );
impl_setTextOnAllFilter_throw();
// lock all controls which are not used for filtering
m_bLocked = determineLockState();
setLocks();
m_bAttachEvents = true;
}
void FormController::stopFiltering()
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
if ( !m_bFiltering ) // #104693# OJ
{ // nothing to do
return;
}
m_bFiltering = false;
m_bDetachEvents = false;
::comphelper::disposeComponent(m_xComposer);
// exchanging the controls for the current form
Sequence< Reference< XControl > > aControlsCopy( m_aControls );
const Reference< XControl > * pControls = aControlsCopy.getConstArray();
sal_Int32 nControlCount = aControlsCopy.getLength();
// clear the filter control map
::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), RemoveComponentTextListener( this ) );
m_aFilterComponents.clear();
for ( sal_Int32 i = nControlCount; i > 0; )
{
Reference< XControl > xControl = pControls[--i];
if (xControl.is())
{
// now enable event handling again
addToEventAttacher(xControl);
Reference< XModeSelector > xSelector(xControl, UNO_QUERY);
if (xSelector.is())
{
xSelector->setMode( u"DataMode"_ustr );
// listening for new controls of the selector
Reference< XContainer > xContainer(xSelector, UNO_QUERY);
if (xContainer.is())
xContainer->removeContainerListener(this);
continue;
}
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;
// may we filter the field?
if ( xField.is()
&& ::comphelper::hasProperty( FM_PROP_SEARCHABLE, xField )
&& ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_SEARCHABLE ) )
)
{
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 );
}
}
}
}
Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
if ( xSet.is() )
startFormListening( xSet, true );
m_bDetachEvents = true;
m_aFilterRows.clear();
m_nCurrentFilterPosition = -1;
// release the locks if possible
// lock all controls which are not used for filtering
m_bLocked = determineLockState();
setLocks();
// restart listening for control modifications
if (isListeningForChanges())
startListening();
}
// XModeSelector
void FormController::setMode(const OUString& Mode)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
if (!supportsMode(Mode))
throw NoSupportException();
if (Mode == m_aMode)
return;
m_aMode = Mode;
if ( Mode == "FilterMode" )
startFiltering();
else
stopFiltering();
for (const auto& rChild : m_aChildren)
{
Reference< XModeSelector > xMode(rChild, UNO_QUERY);
if ( xMode.is() )
xMode->setMode(Mode);
}
}
OUString SAL_CALL FormController::getMode()
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
return m_aMode;
}
Sequence< OUString > SAL_CALL FormController::getSupportedModes()
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
static Sequence< OUString > const aModes
{
u"DataMode"_ustr,
u"FilterMode"_ustr
};
return aModes;
}
sal_Bool SAL_CALL FormController::supportsMode(const OUString& Mode)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
Sequence< OUString > aModes(getSupportedModes());
return comphelper::findValue(aModes, Mode) != -1;
}
css::uno::Reference<css::awt::XWindow> FormController::getDialogParentWindow(css::uno::Reference<css::form::runtime::XFormController> xFormController)
{
try
{
Reference< XControl > xContainerControl( xFormController->getContainer(), UNO_QUERY_THROW );
Reference<XWindow> xContainerWindow(xContainerControl->getPeer(), UNO_QUERY_THROW);
return xContainerWindow;
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
}
return nullptr;
}
bool FormController::checkFormComponentValidity( OUString& /* [out] */ _rFirstInvalidityExplanation, Reference< XControlModel >& /* [out] */ _rxFirstInvalidModel )
{
try
{
Reference< XEnumerationAccess > xControlEnumAcc( getModel(), UNO_QUERY );
Reference< XEnumeration > xControlEnumeration;
if ( xControlEnumAcc.is() )
xControlEnumeration = xControlEnumAcc->createEnumeration();
OSL_ENSURE( xControlEnumeration.is(), "FormController::checkFormComponentValidity: cannot enumerate the controls!" );
if ( !xControlEnumeration.is() )
// assume all valid
return true;
Reference< XValidatableFormComponent > xValidatable;
while ( xControlEnumeration->hasMoreElements() )
{
if ( !( xControlEnumeration->nextElement() >>= xValidatable ) )
// control does not support validation
continue;
if ( xValidatable->isValid() )
continue;
Reference< XValidator > xValidator( xValidatable->getValidator() );
OSL_ENSURE( xValidator.is(), "FormController::checkFormComponentValidity: invalid, but no validator?" );
if ( !xValidator.is() )
// this violates the interface definition of css.form.validation.XValidatableFormComponent ...
continue;
_rFirstInvalidityExplanation = xValidator->explainInvalid( xValidatable->getCurrentValue() );
_rxFirstInvalidModel.set(xValidatable, css::uno::UNO_QUERY);
return false;
}
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
}
return true;
}
Reference< XControl > FormController::locateControl( const Reference< XControlModel >& _rxModel )
{
try
{
const Sequence< Reference< XControl > > aControls( getControls() );
for ( auto const & control : aControls )
{
OSL_ENSURE( control.is(), "FormController::locateControl: NULL-control?" );
if ( control.is() )
{
if ( control->getModel() == _rxModel )
return control;
}
}
OSL_FAIL( "FormController::locateControl: did not find a control for this model!" );
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
}
return nullptr;
}
namespace
{
void displayErrorSetFocus(const OUString& _rMessage, const Reference<XControl>& _rxFocusControl,
const css::uno::Reference<css::awt::XWindow>& rDialogParent)
{
SQLContext aError(SvxResId(RID_STR_WRITEERROR), {}, {}, 0, {}, _rMessage);
displayException(aError, rDialogParent);
if ( _rxFocusControl.is() )
{
Reference< XWindow > xControlWindow( _rxFocusControl, UNO_QUERY );
OSL_ENSURE( xControlWindow.is(), "displayErrorSetFocus: invalid control!" );
if ( xControlWindow.is() )
xControlWindow->setFocus();
}
}
bool lcl_shouldValidateRequiredFields_nothrow( const Reference< XInterface >& _rxForm )
{
try
{
static constexpr OUString s_sFormsCheckRequiredFields = u"FormsCheckRequiredFields"_ustr;
// first, check whether the form has a property telling us the answer
// this allows people to use the XPropertyContainer interface of a form to control
// the behaviour on a per-form basis.
Reference< XPropertySet > xFormProps( _rxForm, UNO_QUERY_THROW );
Reference< XPropertySetInfo > xPSI( xFormProps->getPropertySetInfo() );
if ( xPSI->hasPropertyByName( s_sFormsCheckRequiredFields ) )
{
bool bShouldValidate = true;
OSL_VERIFY( xFormProps->getPropertyValue( s_sFormsCheckRequiredFields ) >>= bShouldValidate );
return bShouldValidate;
}
// next, check the data source which created the connection
Reference< XChild > xConnectionAsChild( xFormProps->getPropertyValue( FM_PROP_ACTIVE_CONNECTION ), UNO_QUERY_THROW );
Reference< XPropertySet > xDataSource( xConnectionAsChild->getParent(), UNO_QUERY );
if ( !xDataSource.is() )
// seldom (but possible): this is not a connection created by a data source
return true;
Reference< XPropertySet > xDataSourceSettings(
xDataSource->getPropertyValue(u"Settings"_ustr),
UNO_QUERY_THROW );
bool bShouldValidate = true;
OSL_VERIFY( xDataSourceSettings->getPropertyValue( s_sFormsCheckRequiredFields ) >>= bShouldValidate );
return bShouldValidate;
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
}
return true;
}
}
// XRowSetApproveListener
sal_Bool SAL_CALL FormController::approveRowChange(const RowChangeEvent& _rEvent)
{
::osl::ClearableMutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
::comphelper::OInterfaceIteratorHelper3 aIter(m_aRowSetApproveListeners);
bool bValid = true;
if (aIter.hasMoreElements())
{
RowChangeEvent aEvt( _rEvent );
aEvt.Source = *this;
bValid = aIter.next()->approveRowChange(aEvt);
}
if ( !bValid )
return bValid;
if ( ( _rEvent.Action != RowChangeAction::INSERT )
&& ( _rEvent.Action != RowChangeAction::UPDATE )
)
return bValid;
// if some of the control models are bound to validators, check them
OUString sInvalidityExplanation;
Reference< XControlModel > xInvalidModel;
if ( !checkFormComponentValidity( sInvalidityExplanation, xInvalidModel ) )
{
Reference< XControl > xControl( locateControl( xInvalidModel ) );
aGuard.clear();
displayErrorSetFocus( sInvalidityExplanation, xControl, getDialogParentWindow(this) );
return false;
}
// check values on NULL and required flag
if ( !lcl_shouldValidateRequiredFields_nothrow( _rEvent.Source ) )
return true;
OSL_ENSURE(m_pColumnInfoCache, "FormController::approveRowChange: no column infos!");
if (!m_pColumnInfoCache)
return true;
try
{
if ( !m_pColumnInfoCache->controlsInitialized() )
m_pColumnInfoCache->initializeControls( getControls() );
size_t colCount = m_pColumnInfoCache->getColumnCount();
for ( size_t col = 0; col < colCount; ++col )
{
const ColumnInfo& rColInfo = m_pColumnInfoCache->getColumnInfo( col );
if ( rColInfo.bAutoIncrement )
continue;
if ( rColInfo.bReadOnly )
continue;
if ( !rColInfo.xFirstControlWithInputRequired.is() && !rColInfo.xFirstGridWithInputRequiredColumn.is() )
{
continue;
}
// TODO: in case of binary fields, this "getString" below is extremely expensive
if ( !rColInfo.xColumn->getString().isEmpty() || !rColInfo.xColumn->wasNull() )
continue;
OUString sMessage( SvxResId( RID_ERR_FIELDREQUIRED ) );
sMessage = sMessage.replaceFirst( "#", rColInfo.sName );
// the control to focus
Reference< XControl > xControl( rColInfo.xFirstControlWithInputRequired );
if ( !xControl.is() )
xControl.set( rColInfo.xFirstGridWithInputRequiredColumn, UNO_QUERY );
aGuard.clear();
displayErrorSetFocus( sMessage, rColInfo.xFirstControlWithInputRequired, getDialogParentWindow(this) );
return false;
}
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
}
return true;
}
sal_Bool SAL_CALL FormController::approveCursorMove(const EventObject& event)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
::comphelper::OInterfaceIteratorHelper3 aIter(m_aRowSetApproveListeners);
if (aIter.hasMoreElements())
{
EventObject aEvt(event);
aEvt.Source = *this;
return aIter.next()->approveCursorMove(aEvt);
}
return true;
}
sal_Bool SAL_CALL FormController::approveRowSetChange(const EventObject& event)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
::comphelper::OInterfaceIteratorHelper3 aIter(m_aRowSetApproveListeners);
if (aIter.hasMoreElements())
{
EventObject aEvt(event);
aEvt.Source = *this;
return aIter.next()->approveRowSetChange(aEvt);
}
return true;
}
// XRowSetApproveBroadcaster
void SAL_CALL FormController::addRowSetApproveListener(const Reference< XRowSetApproveListener > & _rxListener)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
m_aRowSetApproveListeners.addInterface(_rxListener);
}
void SAL_CALL FormController::removeRowSetApproveListener(const Reference< XRowSetApproveListener > & _rxListener)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
m_aRowSetApproveListeners.removeInterface(_rxListener);
}
// XErrorListener
void SAL_CALL FormController::errorOccured(const SQLErrorEvent& aEvent)
{
::osl::ClearableMutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
::comphelper::OInterfaceIteratorHelper3 aIter(m_aErrorListeners);
if (aIter.hasMoreElements())
{
SQLErrorEvent aEvt(aEvent);
aEvt.Source = *this;
aIter.next()->errorOccured(aEvt);
}
else
{
aGuard.clear();
displayException(aEvent, getDialogParentWindow(this));
}
}
// XErrorBroadcaster
void SAL_CALL FormController::addSQLErrorListener(const Reference< XSQLErrorListener > & aListener)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
m_aErrorListeners.addInterface(aListener);
}
void SAL_CALL FormController::removeSQLErrorListener(const Reference< XSQLErrorListener > & aListener)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
m_aErrorListeners.removeInterface(aListener);
}
// XDatabaseParameterBroadcaster2
void SAL_CALL FormController::addDatabaseParameterListener(const Reference< XDatabaseParameterListener > & aListener)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
m_aParameterListeners.addInterface(aListener);
}
void SAL_CALL FormController::removeDatabaseParameterListener(const Reference< XDatabaseParameterListener > & aListener)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
m_aParameterListeners.removeInterface(aListener);
}
// XDatabaseParameterBroadcaster
void SAL_CALL FormController::addParameterListener(const Reference< XDatabaseParameterListener > & aListener)
{
FormController::addDatabaseParameterListener( aListener );
}
void SAL_CALL FormController::removeParameterListener(const Reference< XDatabaseParameterListener > & aListener)
{
FormController::removeDatabaseParameterListener( aListener );
}
// XDatabaseParameterListener
sal_Bool SAL_CALL FormController::approveParameter(const DatabaseParameterEvent& aEvent)
{
SolarMutexGuard aSolarGuard;
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
::comphelper::OInterfaceIteratorHelper3 aIter(m_aParameterListeners);
if (aIter.hasMoreElements())
{
DatabaseParameterEvent aEvt(aEvent);
aEvt.Source = *this;
return aIter.next()->approveParameter(aEvt);
}
else
{
// default handling: instantiate an interaction handler and let it handle the parameter request
try
{
if ( !ensureInteractionHandler() )
return false;
// two continuations allowed: OK and Cancel
rtl::Reference<OParameterContinuation> pParamValues = new OParameterContinuation;
rtl::Reference<OInteractionAbort> pAbort = new OInteractionAbort;
// the request
ParametersRequest aRequest;
aRequest.Parameters = aEvent.Parameters;
aRequest.Connection = getConnection(Reference< XRowSet >(aEvent.Source, UNO_QUERY));
rtl::Reference<OInteractionRequest> pParamRequest = new OInteractionRequest(Any(aRequest));
// some knittings
pParamRequest->addContinuation(pParamValues);
pParamRequest->addContinuation(pAbort);
// handle the request
m_xInteractionHandler->handle(pParamRequest);
if (!pParamValues->wasSelected())
// canceled
return false;
// transfer the values into the parameter supplier
Sequence< PropertyValue > aFinalValues = pParamValues->getValues();
if (aFinalValues.getLength() != aRequest.Parameters->getCount())
{
OSL_FAIL("FormController::approveParameter: the InteractionHandler returned nonsense!");
return false;
}
const PropertyValue* pFinalValues = aFinalValues.getConstArray();
for (sal_Int32 i=0; i<aFinalValues.getLength(); ++i, ++pFinalValues)
{
Reference< XPropertySet > xParam(
aRequest.Parameters->getByIndex(i), css::uno::UNO_QUERY);
if (xParam.is())
{
#ifdef DBG_UTIL
OUString sName;
xParam->getPropertyValue(FM_PROP_NAME) >>= sName;
DBG_ASSERT(sName == pFinalValues->Name, "FormController::approveParameter: suspicious value names!");
#endif
try { xParam->setPropertyValue(FM_PROP_VALUE, pFinalValues->Value); }
catch(Exception&)
{
OSL_FAIL("FormController::approveParameter: setting one of the properties failed!");
}
}
}
}
catch(Exception&)
{
DBG_UNHANDLED_EXCEPTION("svx");
}
}
return true;
}
// XConfirmDeleteBroadcaster
void SAL_CALL FormController::addConfirmDeleteListener(const Reference< XConfirmDeleteListener > & aListener)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
m_aDeleteListeners.addInterface(aListener);
}
void SAL_CALL FormController::removeConfirmDeleteListener(const Reference< XConfirmDeleteListener > & aListener)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
m_aDeleteListeners.removeInterface(aListener);
}
// XConfirmDeleteListener
sal_Bool SAL_CALL FormController::confirmDelete(const RowChangeEvent& aEvent)
{
::osl::MutexGuard aGuard( m_aMutex );
impl_checkDisposed_throw();
::comphelper::OInterfaceIteratorHelper3 aIter(m_aDeleteListeners);
if (aIter.hasMoreElements())
{
RowChangeEvent aEvt(aEvent);
aEvt.Source = *this;
return aIter.next()->confirmDelete(aEvt);
}
// default handling: instantiate an interaction handler and let it handle the request
OUString sTitle;
sal_Int32 nLength = aEvent.Rows;
if ( nLength > 1 )
{
sTitle = SvxResId( RID_STR_DELETECONFIRM_RECORDS );
sTitle = sTitle.replaceFirst( "#", OUString::number(nLength) );
}
else
sTitle = SvxResId( RID_STR_DELETECONFIRM_RECORD );
try
{
if ( !ensureInteractionHandler() )
return false;
// two continuations allowed: Yes and No
rtl::Reference<OInteractionApprove> pApprove = new OInteractionApprove;
rtl::Reference<OInteractionDisapprove> pDisapprove = new OInteractionDisapprove;
// the request
SQLWarning aDetails(SvxResId(RID_STR_DELETECONFIRM), {}, {}, 0, {});
SQLWarning aWarning(sTitle, {}, {}, 0, css::uno::Any(aDetails));
rtl::Reference<OInteractionRequest> pRequest = new OInteractionRequest( Any( aWarning ) );
// some knittings
pRequest->addContinuation( pApprove );
pRequest->addContinuation( pDisapprove );
// handle the request
m_xInteractionHandler->handle( pRequest );
if ( pApprove->wasSelected() )
return true;
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
}
return false;
}
void SAL_CALL FormController::invalidateFeatures( const Sequence< ::sal_Int16 >& Features )
{
::osl::MutexGuard aGuard( m_aMutex );
// for now, just copy the ids of the features, because...
m_aInvalidFeatures.insert( Features.begin(), Features.end() );
// ... we will do the real invalidation asynchronously
if ( !m_aFeatureInvalidationTimer.IsActive() )
m_aFeatureInvalidationTimer.Start();
}
void SAL_CALL FormController::invalidateAllFeatures( )
{
::osl::ClearableMutexGuard aGuard( m_aMutex );
Sequence< sal_Int16 > aInterceptedFeatures( comphelper::mapKeysToSequence(m_aFeatureDispatchers) );
aGuard.clear();
if ( aInterceptedFeatures.hasElements() )
invalidateFeatures( aInterceptedFeatures );
}
Reference< XDispatch >
FormController::interceptedQueryDispatch( const URL& aURL,
const OUString& /*aTargetFrameName*/, sal_Int32 /*nSearchFlags*/)
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
Reference< XDispatch > xReturn;
// dispatches handled by ourself
if ( ( aURL.Complete == FMURL_CONFIRM_DELETION )
|| ( ( aURL.Complete == "private:/InteractionHandler" )
&& ensureInteractionHandler()
)
)
xReturn = static_cast< XDispatch* >( this );
// dispatches of FormSlot-URLs we have to translate
if ( !xReturn.is() && m_xFormOperations.is() )
{
// find the slot id which corresponds to the URL
sal_Int32 nFeatureSlotId = svx::FeatureSlotTranslation::getControllerFeatureSlotIdForURL( aURL.Main );
sal_Int16 nFormFeature = ( nFeatureSlotId != -1 ) ? svx::FeatureSlotTranslation::getFormFeatureForSlotId( nFeatureSlotId ) : -1;
if ( nFormFeature > 0 )
{
// get the dispatcher for this feature, create if necessary
DispatcherContainer::const_iterator aDispatcherPos = m_aFeatureDispatchers.find( nFormFeature );
if ( aDispatcherPos == m_aFeatureDispatchers.end() )
{
aDispatcherPos = m_aFeatureDispatchers.emplace(
nFormFeature, new svx::OSingleFeatureDispatcher( aURL, nFormFeature, m_xFormOperations, m_aMutex )
).first;
}
OSL_ENSURE( aDispatcherPos->second.is(), "FormController::interceptedQueryDispatch: should have a dispatcher by now!" );
return aDispatcherPos->second;
}
}
// no more to offer
return xReturn;
}
void SAL_CALL FormController::dispatch( const URL& _rURL, const Sequence< PropertyValue >& _rArgs )
{
if ( _rArgs.getLength() != 1 )
{
OSL_FAIL( "FormController::dispatch: no arguments -> no dispatch!" );
return;
}
if ( _rURL.Complete == "private:/InteractionHandler" )
{
Reference< XInteractionRequest > xRequest;
OSL_VERIFY( _rArgs[0].Value >>= xRequest );
if ( xRequest.is() )
handle( xRequest );
return;
}
if ( _rURL.Complete == FMURL_CONFIRM_DELETION )
{
OSL_FAIL( "FormController::dispatch: How do you expect me to return something via this call?" );
// confirmDelete has a return value - dispatch hasn't
return;
}
OSL_FAIL( "FormController::dispatch: unknown URL!" );
}
void SAL_CALL FormController::addStatusListener( const Reference< XStatusListener >& _rxListener, const URL& _rURL )
{
if (_rURL.Complete == FMURL_CONFIRM_DELETION)
{
if (_rxListener.is())
{ // send an initial statusChanged event
FeatureStateEvent aEvent;
aEvent.FeatureURL = _rURL;
aEvent.IsEnabled = true;
_rxListener->statusChanged(aEvent);
// and don't add the listener at all (the status will never change)
}
}
else
OSL_FAIL("FormController::addStatusListener: invalid (unsupported) URL!");
}
Reference< XInterface > SAL_CALL FormController::getParent()
{
return m_xParent;
}
void SAL_CALL FormController::setParent( const Reference< XInterface >& Parent)
{
m_xParent = Parent;
}
void SAL_CALL FormController::removeStatusListener( const Reference< XStatusListener >& /*_rxListener*/, const URL& _rURL )
{
OSL_ENSURE(_rURL.Complete == FMURL_CONFIRM_DELETION, "FormController::removeStatusListener: invalid (unsupported) URL!");
// we never really added the listener, so we don't need to remove it
}
Reference< XDispatchProviderInterceptor > FormController::createInterceptor(const Reference< XDispatchProviderInterception > & _xInterception)
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
#ifdef DBG_UTIL
// check if we already have an interceptor for the given object
for ( const auto & it : m_aControlDispatchInterceptors )
{
if (it->getIntercepted() == _xInterception)
OSL_FAIL("FormController::createInterceptor : we already do intercept this objects dispatches !");
}
#endif
rtl::Reference<DispatchInterceptionMultiplexer> pInterceptor(new DispatchInterceptionMultiplexer( _xInterception, this ));
m_aControlDispatchInterceptors.push_back( pInterceptor );
return pInterceptor;
}
bool FormController::ensureInteractionHandler()
{
if ( m_xInteractionHandler.is() )
return true;
if ( m_bAttemptedHandlerCreation )
return false;
m_bAttemptedHandlerCreation = true;
m_xInteractionHandler = InteractionHandler::createWithParent(m_xComponentContext,
getDialogParentWindow(this));
return m_xInteractionHandler.is();
}
void SAL_CALL FormController::handle( const Reference< XInteractionRequest >& _rRequest )
{
if ( !ensureInteractionHandler() )
return;
m_xInteractionHandler->handle( _rRequest );
}
void FormController::deleteInterceptor(const Reference< XDispatchProviderInterception > & _xInterception)
{
OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
// search the interceptor responsible for the given object
auto aIter = std::find_if(m_aControlDispatchInterceptors.begin(), m_aControlDispatchInterceptors.end(),
[&_xInterception](const rtl::Reference<DispatchInterceptionMultiplexer>& rpInterceptor) {
return rpInterceptor->getIntercepted() == _xInterception;
});
if (aIter != m_aControlDispatchInterceptors.end())
{
// log off the interception from its interception object
(*aIter)->dispose();
// remove the interceptor from our array
m_aControlDispatchInterceptors.erase(aIter);
}
}
void FormController::implInvalidateCurrentControlDependentFeatures()
{
Sequence< sal_Int16 > aCurrentControlDependentFeatures
{
FormFeature::SortAscending,
FormFeature::SortDescending,
FormFeature::AutoFilter,
FormFeature::RefreshCurrentControl
};
invalidateFeatures( aCurrentControlDependentFeatures );
}
void SAL_CALL FormController::columnChanged( const EventObject& /*_event*/ )
{
implInvalidateCurrentControlDependentFeatures();
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V728 An excessive check can be simplified. The '||' operator is surrounded by opposite expressions '!bLocked' and 'bLocked'.