/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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::uno::RuntimeException; 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::lang::WrappedTargetException; 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; 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() :xColumn() ,nNullable( ColumnValue::NULLABLE_UNKNOWN ) ,bAutoIncrement( false ) ,bReadOnly( false ) ,sName() ,xFirstControlWithInputRequired() ,xFirstGridWithInputRequiredColumn() ,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_aColumns() ,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 = true; 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() { ColumnInfos::const_iterator aEnd = m_aColumns.end(); for ( ColumnInfos::iterator col = m_aColumns.begin(); col != aEnd; ++col ) { lcl_resetColumnControlInfo( *col ); } } void ColumnInfoCache::initializeControls( const Sequence< Reference< XControl > >& _rControls ) { try { ColumnInfos::const_iterator aEnd = m_aColumns.end(); // for every of our known columns, find the controls which are bound to this column for ( ColumnInfos::iterator col = m_aColumns.begin(); col != aEnd; ++col ) { OSL_ENSURE( !col->xFirstControlWithInputRequired.is() && !col->xFirstGridWithInputRequiredColumn.is() && ( col->nRequiredGridColumn == -1 ), "ColumnInfoCache::initializeControls: called me twice?" ); lcl_resetColumnControlInfo( *col ); Reference< XInterface > xNormColumn( col->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 col->xFirstGridWithInputRequiredColumn = xGrid; col->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 col->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 ]; } 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;} }; class FmXAutoControl: public UnoControl { public: FmXAutoControl() :UnoControl() { } virtual OUString GetComponentServiceName() override {return OUString("Edit");} 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(); } 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 ( ::std::set< sal_Int16 >::const_iterator aLoop = m_aInvalidFeatures.begin(); aLoop != m_aInvalidFeatures.end(); ++aLoop ) { DispatcherContainer::const_iterator aDispatcherPos = m_aFeatureDispatchers.find( *aLoop ); 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_xFormOperations() ,m_aMode( OUString( "DataMode" ) ) ,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() throw () { FormController_BASE::acquire(); } void SAL_CALL FormController::release() throw () { 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(); } 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 OUString("org.openoffice.comp.svx.FormController"); } 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 aNonCreatableServiceNames { "com.sun.star.form.FormControllerDispatcher" }; // 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> aServices; if (!aServices.getLength()) { aServices.realloc(2); aServices.getArray()[0] = "com.sun.star.form.runtime.FormController"; aServices.getArray()[1] = "com.sun.star.awt.control.TabController"; } 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() > static_cast(m_nCurrentFilterPosition), "FormController::impl_setTextOnAllFilter_throw: m_nCurrentFilterPosition too big" ); if ( static_cast(m_nCurrentFilterPosition) < m_aFilterRows.size() ) { FmFilterRow& rRow = m_aFilterRows[ m_nCurrentFilterPosition ]; for ( FmFilterRow::const_iterator iter2 = rRow.begin(); iter2 != rRow.end(); ++iter2 ) { iter2->first->setText( iter2->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(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 ( FmFilterRows::const_iterator row = m_aFilterRows.begin(); row != m_aFilterRows.end(); ++row ) { const FmFilterRow& rRow = *row; 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, sCriteria; const std::shared_ptr< OSQLParseNode > pParseNode = predicateTree( sErrorMsg, sFilterValue, xFormatter, xField ); OSL_ENSURE( pParseNode != nullptr, "FormController::getFastPropertyValue: could not parse the field value predicate!" ); if ( pParseNode != nullptr ) { // 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( "( " ); aFilter.append( aRowFilter.makeStringAndClear() ); aFilter.append( " )" ); } } } 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::get(), PropertyAttribute::READONLY); pDesc[nPos++] = Property(FM_PROP_FORM_OPERATIONS, FM_ATTR_FORM_OPERATIONS, cppu::UnoType::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() ); sal_Int32 termIndex = 0; for ( FmFilterRows::const_iterator row = m_aFilterRows.begin(); row != m_aFilterRows.end(); ++row, ++termIndex ) { const FmFilterRow& rRow( *row ); Sequence< OUString > aConjunction( m_aFilterComponents.size() ); sal_Int32 componentIndex = 0; for ( FilterComponents::const_iterator comp = m_aFilterComponents.begin(); comp != m_aFilterComponents.end(); ++comp, ++componentIndex ) { FmFilterRow::const_iterator predicate = rRow.find( *comp ); if ( predicate != rRow.end() ) aConjunction[ componentIndex ] = predicate->second; } aExpressions[ termIndex ] = aConjunction; } 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::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 || Index >= static_cast(m_aChildren.size())) throw IndexOutOfBoundsException(); return makeAny( 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() { DispatcherContainer::const_iterator aEnd = m_aFeatureDispatchers.end(); for ( DispatcherContainer::iterator aDispatcher = m_aFeatureDispatchers.begin(); aDispatcher != aEnd; ++aDispatcher ) { try { ::comphelper::disposeComponent( aDispatcher->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 (FmFormControllers::const_iterator i = m_aChildren.begin(); i != m_aChildren.end(); ++i) { // search the position of the model within the form Reference< XFormComponent > xForm((*i)->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( *i, UNO_QUERY ); m_xModelAsManager->detach( nPos, xIfc ); break; } } Reference< XComponent > (*i, UNO_QUERY)->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 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 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.get() ); } 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 Sequence< sal_Int32 > aIdentifiers( xContainer->getIdentifiers() ); const sal_Int32* pIdentifiers = aIdentifiers.getConstArray(); const sal_Int32* pIdentifiersEnd = aIdentifiers.getConstArray() + aIdentifiers.getLength(); for ( ; pIdentifiers != pIdentifiersEnd; ++pIdentifiers ) { Reference< XControl > xCheck( xContainer->getByIdentifier( *pIdentifiers ), UNO_QUERY ); if ( xCheck == _rxExistentControl ) break; } DBG_ASSERT( pIdentifiers != pIdentifiersEnd, "FormController::replaceControl: did not find the control in the container!" ); if ( pIdentifiers != pIdentifiersEnd ) { 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, makeAny( _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 a 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 a 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 ( ( static_cast(m_nCurrentFilterPosition) >= m_aFilterRows.size() ) || ( m_nCurrentFilterPosition < 0 ) ) { 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(static_cast(this)); 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(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 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()) { SQLErrorEvent aErrorEvent; 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); 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.get(), 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()) { // 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( xControl, UNO_QUERY ), makeAny(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() ) { // 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( 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 const Reference< XControl >* pControls = m_aControls.getConstArray(); const Reference< XControl >* pControlsEnd = pControls + m_aControls.getLength(); while ( pControls != pControlsEnd ) implControlRemoved( *pControls++, 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()) { Sequence< Reference< XControlModel > > aModels = xTabModel->getControlModels(); const Reference< XControlModel > * pModels = aModels.getConstArray(); 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 i, j; for (i = 0, j = 0; i < nCount; ++i, ++pModels ) { Reference< XControl > xControl = findControl( aAllControls, *pModels, false, true ); if ( xControl.is() ) { pControls[j++] = xControl; implControlInserted( xControl, true ); } } // not every model had an associated control if (j != i) 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; Sequence< Reference< XControlModel > > aControlModels = xModel->getControlModels(); const Reference< XControlModel > * pModels = aControlModels.getConstArray(); 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 (sal_Int32 i = 0; i < nModels; ++i, ++pModels ) { xControl = findControl( m_aControls, *pModels, true, true ); if ( xControl.is() ) pControls[j++] = xControl; } // not every model had an associated control if ( j != nModels ) aNewControls.realloc( j ); m_aControls = 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 { // there is a data source Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY); if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet)) { // 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 (::comphelper::hasProperty(FM_PROP_READONLY, xSet)) bTouch = !::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_READONLY)); if (bTouch) { Reference< XPropertySet > xField; xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField; if (xField.is()) { 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 const Reference< XControl >* pControls = m_aControls.getConstArray(); const Reference< XControl >* pControlsEnd = pControls + m_aControls.getLength(); while ( pControls != pControlsEnd ) setControlLock( *pControls++ ); } 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 const Reference< XControl >* pControls = m_aControls.getConstArray(); const Reference< XControl >* pControlsEnd = pControls + m_aControls.getLength(); while ( pControls != pControlsEnd ) startControlModifyListening( *pControls++ ); } void FormController::stopListening() { OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); m_bModified = false; // now register at bound fields const Reference< XControl >* pControls = m_aControls.getConstArray(); const Reference< XControl >* pControlsEnd = pControls + m_aControls.getLength(); while ( pControls != pControlsEnd ) stopControlModifyListening( *pControls++ ); } 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 ?!" ); Reference< XControl >* pControls = _rControls.getArray(); Reference< XControlModel > xModel; for ( sal_Int32 i = 0, nCount = _rControls.getLength(); i < nCount; ++i, ++pControls ) { if ( pControls->is() ) { xModel = (*pControls)->getModel(); if ( xModel.get() == xCtrlModel.get() ) { Reference< XControl > xControl( *pControls ); if ( _bRemove ) ::comphelper::removeElementAt( _rControls, i ); else if ( _bOverWrite ) pControls->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() ) { 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.get() ) 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!" ); const Reference< XControl >* pControls = m_aControls.getConstArray(); const Reference< XControl >* pControlsEnd = pControls + m_aControls.getLength(); while ( pControls != pControlsEnd ) { if ( xControl.get() == (*pControls++).get() ) { ::comphelper::removeElementAt( m_aControls, pControls - m_aControls.getConstArray() - 1 ); break; } } 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(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() { const Reference< XControl >* pControls = m_aControls.getConstArray(); const Reference< XControl >* pControlsEnd = pControls + m_aControls.getLength(); while ( pControls != pControlsEnd ) { Reference< XPropertySet > xProp( *pControls++, 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( ChildController, UNO_QUERY ), makeAny( 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& 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("com.sun.star.sdb.SingleSelectQueryComposer"), 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()) { 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 // a aliasname or the fieldname itself // first adjust the field names if necessary Reference< XNameAccess > xQueryColumns = Reference< XColumnsSupplier >( m_xComposer, UNO_QUERY_THROW )->getColumns(); ::std::vector::const_iterator aEnd = rFieldInfos.end(); for (::std::vector::iterator iter = rFieldInfos.begin(); iter != aEnd; ++iter) { if ( xQueryColumns->hasByName((*iter).aFieldName) ) { if ( (xQueryColumns->getByName((*iter).aFieldName) >>= (*iter).xField) && (*iter).xField.is() ) (*iter).xField->getPropertyValue(FM_PROP_REALNAME) >>= (*iter).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() ); /* FIXME: casting this to sal_Char is plain wrong and of course only * works for ASCII separators, but * pParseNode->parseNodeToPredicateStr() expects a sal_Char. Fix it * there. */ sal_Char cDecimalSeparator = static_cast(rLocaleWrapper.getNumDecimalSep()[0]); SAL_WARN_IF( static_cast(cDecimalSeparator) != rLocaleWrapper.getNumDecimalSep()[0], "svx.form", "FormController::setFilter: wrong cast of decimal separator to sal_Char!"); // retrieving the filter const Sequence < PropertyValue >* pRow = aFilterRows.getConstArray(); for (sal_Int32 i = 0, nLen = aFilterRows.getLength(); i < nLen; ++i) { FmFilterRow aRow; // search a field for the given name const PropertyValue* pRefValues = pRow[i].getConstArray(); for (sal_Int32 j = 0, nLen1 = pRow[i].getLength(); j < nLen1; j++) { // look for the text component Reference< XPropertySet > xField; try { Reference< XPropertySet > xSet; OUString aRealName; // first look with the given name if (xQueryColumns->hasByName(pRefValues[j].Name)) { xQueryColumns->getByName(pRefValues[j].Name) >>= xSet; // get the RealName xSet->getPropertyValue("RealName") >>= aRealName; // compare the condition field name and the RealName if (aCompare(aRealName, pRefValues[j].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("RealName") >>= aRealName; if (aCompare(aRealName, pRefValues[j].Name)) { // get the column by its alias xField = xSet; break; } } } if (!xField.is()) continue; } catch (const Exception&) { continue; } // find the text component for (::std::vector::const_iterator iter = rFieldInfos.begin(); iter != aEnd; ++iter) { // we found the field so insert a new entry to the filter row if ((*iter).xField == xField) { // do we already have the control ? if (aRow.find((*iter).xText) != aRow.end()) { OUString aCompText = aRow[(*iter).xText]; aCompText += " "; OString aVal = m_pParser->getContext().getIntlKeywordAscii(IParseContext::InternationalKeyCode::And); aCompText += OUString(aVal.getStr(),aVal.getLength(),RTL_TEXTENCODING_ASCII_US); aCompText += " "; aCompText += ::comphelper::getString(pRefValues[j].Value); aRow[(*iter).xText] = aCompText; } else { OUString sPredicate,sErrorMsg; pRefValues[j].Value >>= sPredicate; std::shared_ptr< OSQLParseNode > pParseNode = predicateTree(sErrorMsg, sPredicate, xFormatter, xField); if ( pParseNode != nullptr ) { OUString sCriteria; switch (pRefValues[j].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 ,cDecimalSeparator ,getParseContext()); aRow[(*iter).xText] = sCriteria; } } } } } if (aRow.empty()) continue; impl_addFilterRow( aRow ); } } // now set the filter controls for ( ::std::vector::const_iterator field = rFieldInfos.begin(), aEnd = rFieldInfos.end(); field != aEnd; ++field ) { m_aFilterComponents.push_back( field->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 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( "FilterMode" ); // 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, VCLUnoHelper::GetInterface( getDialogParentWindow() ), 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( "DataMode" ); // 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 (FmFormControllers::const_iterator i = m_aChildren.begin(); i != m_aChildren.end(); ++i) { Reference< XModeSelector > xMode(*i, 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 > aModes; if (!aModes.getLength()) { aModes.realloc(2); aModes[0] = "DataMode"; aModes[1] = "FilterMode"; } return aModes; } sal_Bool SAL_CALL FormController::supportsMode(const OUString& Mode) { ::osl::MutexGuard aGuard( m_aMutex ); impl_checkDisposed_throw(); Sequence< OUString > aModes(getSupportedModes()); const OUString* pModes = aModes.getConstArray(); for (sal_Int32 i = aModes.getLength(); i > 0; ) { if (pModes[--i] == Mode) return true; } return false; } vcl::Window* FormController::getDialogParentWindow() { OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" ); vcl::Window* pParentWindow = nullptr; try { Reference< XControl > xContainerControl( getContainer(), UNO_QUERY_THROW ); Reference< XWindowPeer > xContainerPeer( xContainerControl->getPeer(), UNO_QUERY_THROW ); pParentWindow = VCLUnoHelper::GetWindow( xContainerPeer ).get(); } catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); } return pParentWindow; } 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 { 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, vcl::Window* _pDialogParent ) { SQLContext aError; aError.Message = SvxResId(RID_STR_WRITEERROR); aError.Details = _rMessage; displayException( aError, _pDialogParent ); 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 const char s_sFormsCheckRequiredFields[] = "FormsCheckRequiredFields"; // 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("Settings"), 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::OInterfaceIteratorHelper2 aIter(m_aRowSetApproveListeners); bool bValid = true; if (aIter.hasMoreElements()) { RowChangeEvent aEvt( _rEvent ); aEvt.Source = *this; bValid = static_cast(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() ); return false; } // check values on NULL and required flag if ( !lcl_shouldValidateRequiredFields_nothrow( _rEvent.Source ) ) return true; OSL_ENSURE( m_pColumnInfoCache.get(), "FormController::approveRowChange: no column infos!" ); if ( !m_pColumnInfoCache.get() ) 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.nNullable != ColumnValue::NO_NULLS ) continue; 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() ); 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::OInterfaceIteratorHelper2 aIter(m_aRowSetApproveListeners); if (aIter.hasMoreElements()) { EventObject aEvt(event); aEvt.Source = *this; return static_cast(aIter.next())->approveCursorMove(aEvt); } return true; } sal_Bool SAL_CALL FormController::approveRowSetChange(const EventObject& event) { ::osl::MutexGuard aGuard( m_aMutex ); impl_checkDisposed_throw(); ::comphelper::OInterfaceIteratorHelper2 aIter(m_aRowSetApproveListeners); if (aIter.hasMoreElements()) { EventObject aEvt(event); aEvt.Source = *this; return static_cast(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::OInterfaceIteratorHelper2 aIter(m_aErrorListeners); if (aIter.hasMoreElements()) { SQLErrorEvent aEvt(aEvent); aEvt.Source = *this; static_cast(aIter.next())->errorOccured(aEvt); } else { aGuard.clear(); displayException(aEvent, getDialogParentWindow()); } } // 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::OInterfaceIteratorHelper2 aIter(m_aParameterListeners); if (aIter.hasMoreElements()) { DatabaseParameterEvent aEvt(aEvent); aEvt.Source = *this; return static_cast(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 OParameterContinuation* pParamValues = new OParameterContinuation; OInteractionAbort* pAbort = new OInteractionAbort; // the request ParametersRequest aRequest; aRequest.Parameters = aEvent.Parameters; aRequest.Connection = getConnection(Reference< XRowSet >(aEvent.Source, UNO_QUERY)); OInteractionRequest* pParamRequest = new OInteractionRequest(makeAny(aRequest)); Reference< XInteractionRequest > xParamRequest(pParamRequest); // some knittings pParamRequest->addContinuation(pParamValues); pParamRequest->addContinuation(pAbort); // handle the request m_xInteractionHandler->handle(xParamRequest); 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 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::OInterfaceIteratorHelper2 aIter(m_aDeleteListeners); if (aIter.hasMoreElements()) { RowChangeEvent aEvt(aEvent); aEvt.Source = *this; return static_cast(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 OInteractionApprove* pApprove = new OInteractionApprove; OInteractionDisapprove* pDisapprove = new OInteractionDisapprove; // the request SQLWarning aWarning; aWarning.Message = sTitle; SQLWarning aDetails; aDetails.Message = SvxResId(RID_STR_DELETECONFIRM); aWarning.NextException <<= aDetails; OInteractionRequest* pRequest = new OInteractionRequest( makeAny( aWarning ) ); Reference< XInteractionRequest > xRequest( pRequest ); // some knittings pRequest->addContinuation( pApprove ); pRequest->addContinuation( pDisapprove ); // handle the request m_xInteractionHandler->handle( xRequest ); 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 .... ::std::copy( Features.begin(), Features.end(), ::std::insert_iterator< ::std::set< sal_Int16 > >( m_aInvalidFeatures, m_aInvalidFeatures.begin() ) ); // ... 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.getLength() ) 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 a 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 pInterceptor(new DispatchInterceptionMultiplexer( _xInterception, this )); m_aControlDispatchInterceptors.push_back( pInterceptor ); return pInterceptor.get(); } bool FormController::ensureInteractionHandler() { if ( m_xInteractionHandler.is() ) return true; if ( m_bAttemptedHandlerCreation ) return false; m_bAttemptedHandlerCreation = true; m_xInteractionHandler = InteractionHandler::createWithParent(m_xComponentContext, nullptr); 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 const auto aEnd = m_aControlDispatchInterceptors.end(); for ( auto aIter = m_aControlDispatchInterceptors.begin(); aIter != aEnd; ++aIter ) { if ((*aIter)->getIntercepted() == _xInterception) { // log off the interception from its interception object (*aIter)->dispose(); // remove the interceptor from our array m_aControlDispatchInterceptors.erase(aIter); return; } } } void FormController::implInvalidateCurrentControlDependentFeatures() { Sequence< sal_Int16 > aCurrentControlDependentFeatures(4); aCurrentControlDependentFeatures[0] = FormFeature::SortAscending; aCurrentControlDependentFeatures[1] = FormFeature::SortDescending; aCurrentControlDependentFeatures[2] = FormFeature::AutoFilter; aCurrentControlDependentFeatures[3] = FormFeature::RefreshCurrentControl; invalidateFeatures( aCurrentControlDependentFeatures ); } void SAL_CALL FormController::columnChanged( const EventObject& /*_event*/ ) { implInvalidateCurrentControlDependentFeatures(); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */