bump product version to 7.2.5.1
[LibreOffice.git] / svx / source / form / formcontroller.cxx
blobf71034f173484d81a594651050cc51ec007e8283
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <fmcontrolbordermanager.hxx>
22 #include <fmcontrollayout.hxx>
23 #include <formcontroller.hxx>
24 #include <formfeaturedispatcher.hxx>
25 #include <fmdocumentclassification.hxx>
26 #include <formcontrolling.hxx>
27 #include <fmprop.hxx>
28 #include <svx/dialmgr.hxx>
29 #include <svx/strings.hrc>
30 #include <fmservs.hxx>
31 #include <svx/fmtools.hxx>
32 #include <fmurl.hxx>
34 #include <com/sun/star/awt/FocusChangeReason.hpp>
35 #include <com/sun/star/awt/XCheckBox.hpp>
36 #include <com/sun/star/awt/XComboBox.hpp>
37 #include <com/sun/star/awt/XListBox.hpp>
38 #include <com/sun/star/awt/XVclWindowPeer.hpp>
39 #include <com/sun/star/awt/TabController.hpp>
40 #include <com/sun/star/beans/PropertyAttribute.hpp>
41 #include <com/sun/star/container/XIdentifierReplace.hpp>
42 #include <com/sun/star/form/TabulatorCycle.hpp>
43 #include <com/sun/star/form/validation/XValidatableFormComponent.hpp>
44 #include <com/sun/star/form/XBoundComponent.hpp>
45 #include <com/sun/star/form/XBoundControl.hpp>
46 #include <com/sun/star/form/XGridControl.hpp>
47 #include <com/sun/star/form/XLoadable.hpp>
48 #include <com/sun/star/form/XReset.hpp>
49 #include <com/sun/star/form/control/FilterControl.hpp>
50 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
51 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
52 #include <com/sun/star/lang/NoSupportException.hpp>
53 #include <com/sun/star/sdb/ParametersRequest.hpp>
54 #include <com/sun/star/sdb/RowChangeAction.hpp>
55 #include <com/sun/star/sdb/SQLFilterOperator.hpp>
56 #include <com/sun/star/sdb/XInteractionSupplyParameters.hpp>
57 #include <com/sun/star/sdbc/ColumnValue.hpp>
58 #include <com/sun/star/task/InteractionHandler.hpp>
59 #include <com/sun/star/form/runtime/FormOperations.hpp>
60 #include <com/sun/star/form/runtime/FormFeature.hpp>
61 #include <com/sun/star/container/XContainer.hpp>
62 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
63 #include <com/sun/star/util/NumberFormatter.hpp>
64 #include <com/sun/star/sdb/SQLContext.hpp>
65 #include <com/sun/star/sdb/XColumn.hpp>
67 #include <comphelper/enumhelper.hxx>
68 #include <comphelper/interaction.hxx>
69 #include <comphelper/processfactory.hxx>
70 #include <comphelper/property.hxx>
71 #include <comphelper/sequence.hxx>
72 #include <comphelper/flagguard.hxx>
73 #include <comphelper/types.hxx>
74 #include <cppuhelper/supportsservice.hxx>
75 #include <connectivity/IParseContext.hxx>
76 #include <connectivity/dbtools.hxx>
77 #include <connectivity/sqlparse.hxx>
78 #include <toolkit/controls/unocontrol.hxx>
79 #include <toolkit/helper/vclunohelper.hxx>
80 #include <tools/debug.hxx>
81 #include <tools/diagnose_ex.h>
82 #include <unotools/localedatawrapper.hxx>
83 #include <vcl/svapp.hxx>
84 #include <vcl/settings.hxx>
85 #include <o3tl/safeint.hxx>
86 #include <osl/mutex.hxx>
87 #include <sal/log.hxx>
89 #include <algorithm>
90 #include <iterator>
92 using namespace ::com::sun::star;
93 using namespace ::comphelper;
94 using namespace ::connectivity;
95 using namespace ::dbtools;
98 css::uno::Reference< css::uno::XInterface >
99 FormController_NewInstance_Impl( const css::uno::Reference< css::lang::XMultiServiceFactory > & _rxORB )
101 return *( new ::svxform::FormController( comphelper::getComponentContext(_rxORB) ) );
104 namespace svxform
107 using ::com::sun::star::sdb::XColumn;
108 using ::com::sun::star::awt::XControl;
109 using ::com::sun::star::awt::TabController;
110 using ::com::sun::star::awt::XToolkit;
111 using ::com::sun::star::awt::XWindowPeer;
112 using ::com::sun::star::form::XGrid;
113 using ::com::sun::star::beans::XPropertySet;
114 using ::com::sun::star::uno::UNO_SET_THROW;
115 using ::com::sun::star::uno::UNO_QUERY_THROW;
116 using ::com::sun::star::container::XIndexAccess;
117 using ::com::sun::star::uno::Exception;
118 using ::com::sun::star::uno::XInterface;
119 using ::com::sun::star::uno::UNO_QUERY;
120 using ::com::sun::star::uno::Sequence;
121 using ::com::sun::star::uno::Reference;
122 using ::com::sun::star::beans::XPropertySetInfo;
123 using ::com::sun::star::beans::PropertyValue;
124 using ::com::sun::star::lang::IndexOutOfBoundsException;
125 using ::com::sun::star::sdb::XInteractionSupplyParameters;
126 using ::com::sun::star::awt::XTextComponent;
127 using ::com::sun::star::awt::XTextListener;
128 using ::com::sun::star::uno::Any;
129 using ::com::sun::star::frame::XDispatch;
130 using ::com::sun::star::lang::XMultiServiceFactory;
131 using ::com::sun::star::uno::Type;
132 using ::com::sun::star::lang::IllegalArgumentException;
133 using ::com::sun::star::sdbc::XConnection;
134 using ::com::sun::star::sdbc::XRowSet;
135 using ::com::sun::star::sdbc::XDatabaseMetaData;
136 using ::com::sun::star::util::XNumberFormatsSupplier;
137 using ::com::sun::star::util::NumberFormatter;
138 using ::com::sun::star::util::XNumberFormatter;
139 using ::com::sun::star::sdbcx::XColumnsSupplier;
140 using ::com::sun::star::container::XNameAccess;
141 using ::com::sun::star::lang::EventObject;
142 using ::com::sun::star::beans::Property;
143 using ::com::sun::star::container::XEnumeration;
144 using ::com::sun::star::form::XFormComponent;
145 using ::com::sun::star::form::runtime::XFormOperations;
146 using ::com::sun::star::form::runtime::FilterEvent;
147 using ::com::sun::star::form::runtime::XFilterControllerListener;
148 using ::com::sun::star::awt::XControlContainer;
149 using ::com::sun::star::container::XIdentifierReplace;
150 using ::com::sun::star::form::XFormControllerListener;
151 using ::com::sun::star::awt::XWindow;
152 using ::com::sun::star::sdbc::XResultSet;
153 using ::com::sun::star::awt::XControlModel;
154 using ::com::sun::star::awt::XTabControllerModel;
155 using ::com::sun::star::beans::PropertyChangeEvent;
156 using ::com::sun::star::form::validation::XValidatableFormComponent;
157 using ::com::sun::star::form::XLoadable;
158 using ::com::sun::star::form::XBoundControl;
159 using ::com::sun::star::beans::XPropertyChangeListener;
160 using ::com::sun::star::awt::TextEvent;
161 using ::com::sun::star::form::XBoundComponent;
162 using ::com::sun::star::awt::XCheckBox;
163 using ::com::sun::star::awt::XComboBox;
164 using ::com::sun::star::awt::XListBox;
165 using ::com::sun::star::awt::ItemEvent;
166 using ::com::sun::star::util::XModifyListener;
167 using ::com::sun::star::form::XReset;
168 using ::com::sun::star::frame::XDispatchProviderInterception;
169 using ::com::sun::star::form::XGridControl;
170 using ::com::sun::star::awt::XVclWindowPeer;
171 using ::com::sun::star::form::validation::XValidator;
172 using ::com::sun::star::awt::FocusEvent;
173 using ::com::sun::star::sdb::SQLContext;
174 using ::com::sun::star::container::XChild;
175 using ::com::sun::star::form::TabulatorCycle_RECORDS;
176 using ::com::sun::star::container::ContainerEvent;
177 using ::com::sun::star::lang::DisposedException;
178 using ::com::sun::star::lang::Locale;
179 using ::com::sun::star::lang::NoSupportException;
180 using ::com::sun::star::sdb::RowChangeEvent;
181 using ::com::sun::star::frame::XStatusListener;
182 using ::com::sun::star::frame::XDispatchProviderInterceptor;
183 using ::com::sun::star::sdb::SQLErrorEvent;
184 using ::com::sun::star::form::DatabaseParameterEvent;
185 using ::com::sun::star::sdb::ParametersRequest;
186 using ::com::sun::star::task::XInteractionRequest;
187 using ::com::sun::star::util::URL;
188 using ::com::sun::star::frame::FeatureStateEvent;
189 using ::com::sun::star::form::runtime::XFormControllerContext;
190 using ::com::sun::star::task::InteractionHandler;
191 using ::com::sun::star::task::XInteractionHandler;
192 using ::com::sun::star::form::runtime::FormOperations;
193 using ::com::sun::star::container::XContainer;
194 using ::com::sun::star::sdbc::SQLWarning;
196 namespace ColumnValue = ::com::sun::star::sdbc::ColumnValue;
197 namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute;
198 namespace FocusChangeReason = ::com::sun::star::awt::FocusChangeReason;
199 namespace RowChangeAction = ::com::sun::star::sdb::RowChangeAction;
200 namespace FormFeature = ::com::sun::star::form::runtime::FormFeature;
202 namespace {
204 struct ColumnInfo
206 // information about the column itself
207 Reference< XColumn > xColumn;
208 sal_Int32 nNullable;
209 bool bAutoIncrement;
210 bool bReadOnly;
211 OUString sName;
213 // information about the control(s) bound to this column
215 /// the first control which is bound to the given column, and which requires input
216 Reference< XControl > xFirstControlWithInputRequired;
217 /** the first grid control which contains a column which is bound to the given database column, and requires
218 input
220 Reference< XGrid > xFirstGridWithInputRequiredColumn;
221 /** if xFirstControlWithInputRequired is a grid control, then nRequiredGridColumn specifies the position
222 of the grid column which is actually bound
224 sal_Int32 nRequiredGridColumn;
226 ColumnInfo()
227 :xColumn()
228 ,nNullable( ColumnValue::NULLABLE_UNKNOWN )
229 ,bAutoIncrement( false )
230 ,bReadOnly( false )
231 ,sName()
232 ,xFirstControlWithInputRequired()
233 ,xFirstGridWithInputRequiredColumn()
234 ,nRequiredGridColumn( -1 )
241 class ColumnInfoCache
243 public:
244 explicit ColumnInfoCache( const Reference< XColumnsSupplier >& _rxColSupplier );
246 size_t getColumnCount() const { return m_aColumns.size(); }
247 const ColumnInfo& getColumnInfo( size_t _pos );
249 bool controlsInitialized() const { return m_bControlsInitialized; }
250 void initializeControls( const Sequence< Reference< XControl > >& _rControls );
251 void deinitializeControls();
253 private:
254 typedef ::std::vector< ColumnInfo > ColumnInfos;
255 ColumnInfos m_aColumns;
256 bool m_bControlsInitialized;
260 ColumnInfoCache::ColumnInfoCache( const Reference< XColumnsSupplier >& _rxColSupplier )
261 :m_aColumns()
262 ,m_bControlsInitialized( false )
266 m_aColumns.clear();
268 Reference< XIndexAccess > xColumns( _rxColSupplier->getColumns(), UNO_QUERY_THROW );
269 sal_Int32 nColumnCount = xColumns->getCount();
270 m_aColumns.reserve( nColumnCount );
272 Reference< XPropertySet > xColumnProps;
273 for ( sal_Int32 i = 0; i < nColumnCount; ++i )
275 ColumnInfo aColInfo;
276 aColInfo.xColumn.set( xColumns->getByIndex(i), UNO_QUERY_THROW );
278 xColumnProps.set( aColInfo.xColumn, UNO_QUERY_THROW );
279 OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_ISNULLABLE ) >>= aColInfo.nNullable );
280 OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_AUTOINCREMENT ) >>= aColInfo.bAutoIncrement );
281 OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_NAME ) >>= aColInfo.sName );
282 OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_ISREADONLY ) >>= aColInfo.bReadOnly );
284 m_aColumns.push_back( aColInfo );
287 catch( const Exception& )
289 DBG_UNHANDLED_EXCEPTION("svx");
294 namespace
296 bool lcl_isBoundTo( const Reference< XPropertySet >& _rxControlModel, const Reference< XInterface >& _rxNormDBField )
298 Reference< XInterface > xNormBoundField( _rxControlModel->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY );
299 return ( xNormBoundField == _rxNormDBField );
302 bool lcl_isInputRequired( const Reference< XPropertySet >& _rxControlModel )
304 bool bInputRequired = false;
305 OSL_VERIFY( _rxControlModel->getPropertyValue( FM_PROP_INPUT_REQUIRED ) >>= bInputRequired );
306 return bInputRequired;
309 void lcl_resetColumnControlInfo( ColumnInfo& _rColInfo )
311 _rColInfo.xFirstControlWithInputRequired.clear();
312 _rColInfo.xFirstGridWithInputRequiredColumn.clear();
313 _rColInfo.nRequiredGridColumn = -1;
318 void ColumnInfoCache::deinitializeControls()
320 for (auto& rCol : m_aColumns)
322 lcl_resetColumnControlInfo( rCol );
324 m_bControlsInitialized = false;
328 void ColumnInfoCache::initializeControls( const Sequence< Reference< XControl > >& _rControls )
332 // for every of our known columns, find the controls which are bound to this column
333 for (auto& rCol : m_aColumns)
335 OSL_ENSURE( !rCol.xFirstControlWithInputRequired.is() && !rCol.xFirstGridWithInputRequiredColumn.is()
336 && ( rCol.nRequiredGridColumn == -1 ), "ColumnInfoCache::initializeControls: called me twice?" );
338 lcl_resetColumnControlInfo( rCol );
340 Reference< XInterface > xNormColumn( rCol.xColumn, UNO_QUERY_THROW );
342 const Reference< XControl >* pControl( _rControls.getConstArray() );
343 const Reference< XControl >* pControlEnd( pControl + _rControls.getLength() );
344 for ( ; pControl != pControlEnd; ++pControl )
346 if ( !pControl->is() )
347 continue;
349 Reference< XPropertySet > xModel( (*pControl)->getModel(), UNO_QUERY_THROW );
350 Reference< XPropertySetInfo > xModelPSI( xModel->getPropertySetInfo(), UNO_SET_THROW );
352 // special handling for grid controls
353 Reference< XGrid > xGrid( *pControl, UNO_QUERY );
354 if ( xGrid.is() )
356 Reference< XIndexAccess > xGridColAccess( xModel, UNO_QUERY_THROW );
357 sal_Int32 gridColCount = xGridColAccess->getCount();
358 sal_Int32 gridCol = 0;
359 for ( gridCol = 0; gridCol < gridColCount; ++gridCol )
361 Reference< XPropertySet > xGridColumnModel( xGridColAccess->getByIndex( gridCol ), UNO_QUERY_THROW );
363 if ( !lcl_isBoundTo( xGridColumnModel, xNormColumn )
364 || !lcl_isInputRequired( xGridColumnModel )
366 continue; // with next grid column
368 break;
371 if ( gridCol < gridColCount )
373 // found a grid column which is bound to the given
374 rCol.xFirstGridWithInputRequiredColumn = xGrid;
375 rCol.nRequiredGridColumn = gridCol;
376 break;
379 continue; // with next control
382 if ( !xModelPSI->hasPropertyByName( FM_PROP_BOUNDFIELD )
383 || !lcl_isBoundTo( xModel, xNormColumn )
384 || !lcl_isInputRequired( xModel )
386 continue; // with next control
388 break;
391 if ( pControl == pControlEnd )
392 // did not find a control which is bound to this particular column, and for which the input is required
393 continue; // with next DB column
395 rCol.xFirstControlWithInputRequired = *pControl;
398 catch( const Exception& )
400 DBG_UNHANDLED_EXCEPTION("svx");
403 m_bControlsInitialized = true;
407 const ColumnInfo& ColumnInfoCache::getColumnInfo( size_t _pos )
409 if ( _pos >= m_aColumns.size() )
410 throw IndexOutOfBoundsException();
412 return m_aColumns[ _pos ];
415 namespace {
417 class OParameterContinuation : public OInteraction< XInteractionSupplyParameters >
419 Sequence< PropertyValue > m_aValues;
421 public:
422 OParameterContinuation() { }
424 const Sequence< PropertyValue >& getValues() const { return m_aValues; }
426 // XInteractionSupplyParameters
427 virtual void SAL_CALL setParameters( const Sequence< PropertyValue >& _rValues ) override;
432 void SAL_CALL OParameterContinuation::setParameters( const Sequence< PropertyValue >& _rValues )
434 m_aValues = _rValues;
438 // FmXAutoControl
440 struct FmFieldInfo
442 OUString aFieldName;
443 Reference< XPropertySet > xField;
444 Reference< XTextComponent > xText;
446 FmFieldInfo(const Reference< XPropertySet >& _xField, const Reference< XTextComponent >& _xText)
447 :xField(_xField)
448 ,xText(_xText)
449 {xField->getPropertyValue(FM_PROP_NAME) >>= aFieldName;}
452 namespace {
454 class FmXAutoControl: public UnoControl
457 public:
458 FmXAutoControl() :UnoControl()
462 virtual OUString GetComponentServiceName() override {return "Edit";}
463 virtual void SAL_CALL createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) override;
465 protected:
466 virtual void ImplSetPeerProperty( const OUString& rPropName, const Any& rVal ) override;
471 void FmXAutoControl::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer )
473 UnoControl::createPeer( rxToolkit, rParentPeer );
475 Reference< XTextComponent > xText(getPeer() , UNO_QUERY);
476 if (xText.is())
478 xText->setText(SvxResId(RID_STR_AUTOFIELD));
479 xText->setEditable(false);
484 void FmXAutoControl::ImplSetPeerProperty( const OUString& rPropName, const Any& rVal )
486 // these properties are ignored
487 if (rPropName == FM_PROP_TEXT)
488 return;
490 UnoControl::ImplSetPeerProperty( rPropName, rVal );
494 IMPL_LINK_NOARG( FormController, OnActivateTabOrder, Timer*, void )
496 activateTabOrder();
499 namespace {
501 struct UpdateAllListeners
503 bool operator()( const Reference< XDispatch >& _rxDispatcher ) const
505 static_cast< svx::OSingleFeatureDispatcher* >( _rxDispatcher.get() )->updateAllListeners();
506 // the return is a dummy only so we can use this struct in a lambda expression
507 return true;
513 IMPL_LINK_NOARG( FormController, OnInvalidateFeatures, Timer*, void )
515 ::osl::MutexGuard aGuard( m_aMutex );
516 for (const auto& rFeature : m_aInvalidFeatures)
518 DispatcherContainer::const_iterator aDispatcherPos = m_aFeatureDispatchers.find( rFeature );
519 if ( aDispatcherPos != m_aFeatureDispatchers.end() )
521 // TODO: for the real and actual listener notifications, we should release
522 // our mutex
523 UpdateAllListeners( )( aDispatcherPos->second );
528 FormController::FormController(const Reference< css::uno::XComponentContext > & _rxORB )
529 :FormController_BASE( m_aMutex )
530 ,OPropertySetHelper( FormController_BASE::rBHelper )
531 ,OSQLParserClient( _rxORB )
532 ,m_xComponentContext( _rxORB )
533 ,m_aActivateListeners(m_aMutex)
534 ,m_aModifyListeners(m_aMutex)
535 ,m_aErrorListeners(m_aMutex)
536 ,m_aDeleteListeners(m_aMutex)
537 ,m_aRowSetApproveListeners(m_aMutex)
538 ,m_aParameterListeners(m_aMutex)
539 ,m_aFilterListeners(m_aMutex)
540 ,m_xFormOperations()
541 ,m_aMode( OUString( "DataMode" ) )
542 ,m_aLoadEvent( LINK( this, FormController, OnLoad ) )
543 ,m_aToggleEvent( LINK( this, FormController, OnToggleAutoFields ) )
544 ,m_aActivationEvent( LINK( this, FormController, OnActivated ) )
545 ,m_aDeactivationEvent( LINK( this, FormController, OnDeactivated ) )
546 ,m_nCurrentFilterPosition(-1)
547 ,m_bCurrentRecordModified(false)
548 ,m_bCurrentRecordNew(false)
549 ,m_bLocked(false)
550 ,m_bDBConnection(false)
551 ,m_bCycle(false)
552 ,m_bCanInsert(false)
553 ,m_bCanUpdate(false)
554 ,m_bCommitLock(false)
555 ,m_bModified(false)
556 ,m_bControlsSorted(false)
557 ,m_bFiltering(false)
558 ,m_bAttachEvents(true)
559 ,m_bDetachEvents(true)
560 ,m_bAttemptedHandlerCreation( false )
561 ,m_bSuspendFilterTextListening( false )
564 osl_atomic_increment(&m_refCount);
566 m_xTabController = TabController::create( m_xComponentContext );
567 m_xAggregate.set( m_xTabController, UNO_QUERY_THROW );
568 m_xAggregate->setDelegator( *this );
570 osl_atomic_decrement(&m_refCount);
572 m_aTabActivationIdle.SetPriority( TaskPriority::LOWEST );
573 m_aTabActivationIdle.SetInvokeHandler( LINK( this, FormController, OnActivateTabOrder ) );
575 m_aFeatureInvalidationTimer.SetTimeout( 200 );
576 m_aFeatureInvalidationTimer.SetInvokeHandler( LINK( this, FormController, OnInvalidateFeatures ) );
580 FormController::~FormController()
583 ::osl::MutexGuard aGuard( m_aMutex );
585 m_aLoadEvent.CancelPendingCall();
586 m_aToggleEvent.CancelPendingCall();
587 m_aActivationEvent.CancelPendingCall();
588 m_aDeactivationEvent.CancelPendingCall();
590 if ( m_aTabActivationIdle.IsActive() )
591 m_aTabActivationIdle.Stop();
594 if ( m_aFeatureInvalidationTimer.IsActive() )
595 m_aFeatureInvalidationTimer.Stop();
597 disposeAllFeaturesAndDispatchers();
599 if ( m_xFormOperations.is() )
600 m_xFormOperations->dispose();
601 m_xFormOperations.clear();
603 // release of aggregation
604 if ( m_xAggregate.is() )
606 m_xAggregate->setDelegator( nullptr );
607 m_xAggregate.clear();
612 void SAL_CALL FormController::acquire() noexcept
614 FormController_BASE::acquire();
618 void SAL_CALL FormController::release() noexcept
620 FormController_BASE::release();
624 Any SAL_CALL FormController::queryInterface( const Type& _rType )
626 Any aRet = FormController_BASE::queryInterface( _rType );
627 if ( !aRet.hasValue() )
628 aRet = OPropertySetHelper::queryInterface( _rType );
629 if ( !aRet.hasValue() )
630 aRet = m_xAggregate->queryAggregation( _rType );
631 return aRet;
635 Sequence< sal_Int8 > SAL_CALL FormController::getImplementationId()
637 return css::uno::Sequence<sal_Int8>();
640 Sequence< Type > SAL_CALL FormController::getTypes( )
642 return comphelper::concatSequences(
643 FormController_BASE::getTypes(),
644 ::cppu::OPropertySetHelper::getTypes()
648 // XServiceInfo
649 sal_Bool SAL_CALL FormController::supportsService(const OUString& ServiceName)
651 return cppu::supportsService(this, ServiceName);
654 OUString SAL_CALL FormController::getImplementationName()
656 return "org.openoffice.comp.svx.FormController";
659 Sequence< OUString> SAL_CALL FormController::getSupportedServiceNames()
661 // service names which are supported only, but cannot be used to created an
662 // instance at a service factory
663 Sequence<OUString> aNonCreatableServiceNames { "com.sun.star.form.FormControllerDispatcher" };
665 // services which can be used to created an instance at a service factory
666 Sequence< OUString > aCreatableServiceNames( getSupportedServiceNames_Static() );
667 return ::comphelper::concatSequences( aCreatableServiceNames, aNonCreatableServiceNames );
671 sal_Bool SAL_CALL FormController::approveReset(const EventObject& /*rEvent*/)
673 return true;
677 void SAL_CALL FormController::resetted(const EventObject& rEvent)
679 ::osl::MutexGuard aGuard(m_aMutex);
680 if (getCurrentControl().is() && (getCurrentControl()->getModel() == rEvent.Source))
681 m_bModified = false;
685 Sequence< OUString> const & FormController::getSupportedServiceNames_Static()
687 static Sequence< OUString> const aServices
689 "com.sun.star.form.runtime.FormController",
690 "com.sun.star.awt.control.TabController"
692 return aServices;
696 namespace
698 struct ResetComponentText
700 void operator()( const Reference< XTextComponent >& _rxText )
702 _rxText->setText( OUString() );
706 struct RemoveComponentTextListener
708 explicit RemoveComponentTextListener( const Reference< XTextListener >& _rxListener )
709 :m_xListener( _rxListener )
713 void operator()( const Reference< XTextComponent >& _rxText )
715 _rxText->removeTextListener( m_xListener );
718 private:
719 Reference< XTextListener > m_xListener;
724 void FormController::impl_setTextOnAllFilter_throw()
726 m_bSuspendFilterTextListening = true;
727 ::comphelper::FlagGuard aResetFlag( m_bSuspendFilterTextListening );
729 // reset the text for all controls
730 ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), ResetComponentText() );
732 if ( m_aFilterRows.empty() )
733 // nothing to do anymore
734 return;
736 if ( m_nCurrentFilterPosition < 0 )
737 return;
739 // set the text for all filters
740 OSL_ENSURE( m_aFilterRows.size() > o3tl::make_unsigned(m_nCurrentFilterPosition),
741 "FormController::impl_setTextOnAllFilter_throw: m_nCurrentFilterPosition too big" );
743 if ( o3tl::make_unsigned(m_nCurrentFilterPosition) < m_aFilterRows.size() )
745 FmFilterRow& rRow = m_aFilterRows[ m_nCurrentFilterPosition ];
746 for (const auto& rEntry : rRow)
748 rEntry.first->setText( rEntry.second );
752 // OPropertySetHelper
754 sal_Bool FormController::convertFastPropertyValue( Any & /*rConvertedValue*/, Any & /*rOldValue*/,
755 sal_Int32 /*nHandle*/, const Any& /*rValue*/ )
757 return false;
761 void FormController::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/, const Any& /*rValue*/ )
766 void FormController::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
768 switch (nHandle)
770 case FM_ATTR_FILTER:
772 OUStringBuffer aFilter;
773 Reference<XConnection> xConnection(getConnection(Reference< XRowSet>(m_xModelAsIndex, UNO_QUERY)));
774 if (xConnection.is())
776 Reference< XNumberFormatsSupplier> xFormatSupplier( getNumberFormats( xConnection, true ) );
777 Reference< XNumberFormatter> xFormatter = NumberFormatter::create(m_xComponentContext);
778 xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
780 // now add the filter rows
783 for (const FmFilterRow& rRow : m_aFilterRows)
785 if ( rRow.empty() )
786 continue;
788 OUStringBuffer aRowFilter;
789 for ( FmFilterRow::const_iterator condition = rRow.begin(); condition != rRow.end(); ++condition )
791 // get the field of the controls map
792 Reference< XControl > xControl( condition->first, UNO_QUERY_THROW );
793 Reference< XPropertySet > xModelProps( xControl->getModel(), UNO_QUERY_THROW );
794 Reference< XPropertySet > xField( xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY_THROW );
796 OUString sFilterValue( condition->second );
798 OUString sErrorMsg;
799 const std::unique_ptr< OSQLParseNode > pParseNode =
800 predicateTree( sErrorMsg, sFilterValue, xFormatter, xField );
801 OSL_ENSURE( pParseNode != nullptr, "FormController::getFastPropertyValue: could not parse the field value predicate!" );
802 if ( pParseNode != nullptr )
804 OUString sCriteria;
805 // don't use a parse context here, we need it unlocalized
806 pParseNode->parseNodeToStr( sCriteria, xConnection );
807 if ( condition != rRow.begin() )
808 aRowFilter.append( " AND " );
809 aRowFilter.append( sCriteria );
812 if ( !aRowFilter.isEmpty() )
814 if ( !aFilter.isEmpty() )
815 aFilter.append( " OR " );
817 aFilter.append( "( " );
818 aFilter.append( aRowFilter );
819 aFilter.append( " )" );
823 catch( const Exception& )
825 DBG_UNHANDLED_EXCEPTION("svx");
826 aFilter.setLength(0);
829 rValue <<= aFilter.makeStringAndClear();
831 break;
833 case FM_ATTR_FORM_OPERATIONS:
834 rValue <<= m_xFormOperations;
835 break;
840 Reference< XPropertySetInfo > FormController::getPropertySetInfo()
842 static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
843 return xInfo;
847 void FormController::fillProperties(
848 Sequence< Property >& /* [out] */ _rProps,
849 Sequence< Property >& /* [out] */ /*_rAggregateProps*/
850 ) const
852 _rProps.realloc(2);
853 sal_Int32 nPos = 0;
854 Property* pDesc = _rProps.getArray();
856 pDesc[nPos++] = Property(FM_PROP_FILTER, FM_ATTR_FILTER,
857 cppu::UnoType<OUString>::get(),
858 PropertyAttribute::READONLY);
859 pDesc[nPos++] = Property(FM_PROP_FORM_OPERATIONS, FM_ATTR_FORM_OPERATIONS,
860 cppu::UnoType<XFormOperations>::get(),
861 PropertyAttribute::READONLY);
865 ::cppu::IPropertyArrayHelper& FormController::getInfoHelper()
867 return *getArrayHelper();
870 // XFilterController
872 void SAL_CALL FormController::addFilterControllerListener( const Reference< XFilterControllerListener >& Listener )
874 m_aFilterListeners.addInterface( Listener );
878 void SAL_CALL FormController::removeFilterControllerListener( const Reference< XFilterControllerListener >& Listener )
880 m_aFilterListeners.removeInterface( Listener );
884 ::sal_Int32 SAL_CALL FormController::getFilterComponents()
886 ::osl::MutexGuard aGuard( m_aMutex );
887 impl_checkDisposed_throw();
889 return m_aFilterComponents.size();
893 ::sal_Int32 SAL_CALL FormController::getDisjunctiveTerms()
895 ::osl::MutexGuard aGuard( m_aMutex );
896 impl_checkDisposed_throw();
898 return m_aFilterRows.size();
902 void SAL_CALL FormController::setPredicateExpression( ::sal_Int32 Component, ::sal_Int32 Term, const OUString& PredicateExpression )
904 ::osl::MutexGuard aGuard( m_aMutex );
905 impl_checkDisposed_throw();
907 if ( ( Component < 0 ) || ( Component >= getFilterComponents() ) || ( Term < 0 ) || ( Term >= getDisjunctiveTerms() ) )
908 throw IndexOutOfBoundsException( OUString(), *this );
910 Reference< XTextComponent > xText( m_aFilterComponents[ Component ] );
911 xText->setText( PredicateExpression );
913 FmFilterRow& rFilterRow = m_aFilterRows[ Term ];
914 if ( !PredicateExpression.isEmpty() )
915 rFilterRow[ xText ] = PredicateExpression;
916 else
917 rFilterRow.erase( xText );
921 Reference< XControl > FormController::getFilterComponent( ::sal_Int32 Component )
923 ::osl::MutexGuard aGuard( m_aMutex );
924 impl_checkDisposed_throw();
926 if ( ( Component < 0 ) || ( Component >= getFilterComponents() ) )
927 throw IndexOutOfBoundsException( OUString(), *this );
929 return Reference< XControl >( m_aFilterComponents[ Component ], UNO_QUERY );
933 Sequence< Sequence< OUString > > FormController::getPredicateExpressions()
935 ::osl::MutexGuard aGuard( m_aMutex );
936 impl_checkDisposed_throw();
938 Sequence< Sequence< OUString > > aExpressions( m_aFilterRows.size() );
939 sal_Int32 termIndex = 0;
940 for (const FmFilterRow& rRow : m_aFilterRows)
942 Sequence< OUString > aConjunction( m_aFilterComponents.size() );
943 sal_Int32 componentIndex = 0;
944 for (const auto& rComp : m_aFilterComponents)
946 FmFilterRow::const_iterator predicate = rRow.find( rComp );
947 if ( predicate != rRow.end() )
948 aConjunction[ componentIndex ] = predicate->second;
949 ++componentIndex;
952 aExpressions[ termIndex ] = aConjunction;
953 ++termIndex;
956 return aExpressions;
960 void SAL_CALL FormController::removeDisjunctiveTerm( ::sal_Int32 Term )
962 // SYNCHRONIZED -->
963 ::osl::ClearableMutexGuard aGuard( m_aMutex );
964 impl_checkDisposed_throw();
966 if ( ( Term < 0 ) || ( Term >= getDisjunctiveTerms() ) )
967 throw IndexOutOfBoundsException( OUString(), *this );
969 // if the to-be-deleted row is our current row, we need to shift
970 if ( Term == m_nCurrentFilterPosition )
972 if ( m_nCurrentFilterPosition < sal_Int32( m_aFilterRows.size() - 1 ) )
973 ++m_nCurrentFilterPosition;
974 else
975 --m_nCurrentFilterPosition;
978 FmFilterRows::iterator pos = m_aFilterRows.begin() + Term;
979 m_aFilterRows.erase( pos );
981 // adjust m_nCurrentFilterPosition if the removed row preceded it
982 if ( Term < m_nCurrentFilterPosition )
983 --m_nCurrentFilterPosition;
985 SAL_WARN_IF( !( ( m_nCurrentFilterPosition < 0 ) != ( m_aFilterRows.empty() ) ),
986 "svx.form", "FormController::removeDisjunctiveTerm: inconsistency!" );
988 // update the texts in the filter controls
989 impl_setTextOnAllFilter_throw();
991 FilterEvent aEvent;
992 aEvent.Source = *this;
993 aEvent.DisjunctiveTerm = Term;
994 aGuard.clear();
995 // <-- SYNCHRONIZED
997 m_aFilterListeners.notifyEach( &XFilterControllerListener::disjunctiveTermRemoved, aEvent );
1001 void SAL_CALL FormController::appendEmptyDisjunctiveTerm()
1003 // SYNCHRONIZED -->
1004 ::osl::ClearableMutexGuard aGuard( m_aMutex );
1005 impl_checkDisposed_throw();
1007 impl_appendEmptyFilterRow( aGuard );
1008 // <-- SYNCHRONIZED
1012 ::sal_Int32 SAL_CALL FormController::getActiveTerm()
1014 ::osl::MutexGuard aGuard( m_aMutex );
1015 impl_checkDisposed_throw();
1017 return m_nCurrentFilterPosition;
1021 void SAL_CALL FormController::setActiveTerm( ::sal_Int32 ActiveTerm )
1023 ::osl::MutexGuard aGuard( m_aMutex );
1024 impl_checkDisposed_throw();
1026 if ( ( ActiveTerm < 0 ) || ( ActiveTerm >= getDisjunctiveTerms() ) )
1027 throw IndexOutOfBoundsException( OUString(), *this );
1029 if ( ActiveTerm == getActiveTerm() )
1030 return;
1032 m_nCurrentFilterPosition = ActiveTerm;
1033 impl_setTextOnAllFilter_throw();
1036 // XElementAccess
1038 sal_Bool SAL_CALL FormController::hasElements()
1040 ::osl::MutexGuard aGuard( m_aMutex );
1041 return !m_aChildren.empty();
1045 Type SAL_CALL FormController::getElementType()
1047 return cppu::UnoType<XFormController>::get();
1051 // XEnumerationAccess
1053 Reference< XEnumeration > SAL_CALL FormController::createEnumeration()
1055 ::osl::MutexGuard aGuard( m_aMutex );
1056 return new ::comphelper::OEnumerationByIndex(this);
1059 // XIndexAccess
1061 sal_Int32 SAL_CALL FormController::getCount()
1063 ::osl::MutexGuard aGuard( m_aMutex );
1064 return m_aChildren.size();
1068 Any SAL_CALL FormController::getByIndex(sal_Int32 Index)
1070 ::osl::MutexGuard aGuard( m_aMutex );
1071 if (Index < 0 ||
1072 Index >= static_cast<sal_Int32>(m_aChildren.size()))
1073 throw IndexOutOfBoundsException();
1075 return makeAny( m_aChildren[ Index ] );
1078 // EventListener
1080 void SAL_CALL FormController::disposing(const EventObject& e)
1082 // has the container been disposed
1083 ::osl::MutexGuard aGuard( m_aMutex );
1084 Reference< XControlContainer > xContainer(e.Source, UNO_QUERY);
1085 if (xContainer.is())
1087 setContainer(Reference< XControlContainer > ());
1089 else
1091 // has a control been disposed
1092 Reference< XControl > xControl(e.Source, UNO_QUERY);
1093 if (xControl.is())
1095 if (getContainer().is())
1096 removeControl(xControl);
1101 // OComponentHelper
1103 void FormController::disposeAllFeaturesAndDispatchers()
1105 for (auto& rDispatcher : m_aFeatureDispatchers)
1109 ::comphelper::disposeComponent( rDispatcher.second );
1111 catch( const Exception& )
1113 DBG_UNHANDLED_EXCEPTION("svx");
1116 m_aFeatureDispatchers.clear();
1120 void FormController::disposing()
1122 EventObject aEvt( *this );
1124 // if we're still active, simulate a "deactivated" event
1125 if ( m_xActiveControl.is() )
1126 m_aActivateListeners.notifyEach( &XFormControllerListener::formDeactivated, aEvt );
1128 // notify all our listeners
1129 m_aActivateListeners.disposeAndClear(aEvt);
1130 m_aModifyListeners.disposeAndClear(aEvt);
1131 m_aErrorListeners.disposeAndClear(aEvt);
1132 m_aDeleteListeners.disposeAndClear(aEvt);
1133 m_aRowSetApproveListeners.disposeAndClear(aEvt);
1134 m_aParameterListeners.disposeAndClear(aEvt);
1135 m_aFilterListeners.disposeAndClear(aEvt);
1137 removeBoundFieldListener();
1138 stopFiltering();
1140 m_aControlBorderManager.restoreAll();
1142 m_aFilterRows.clear();
1144 ::osl::MutexGuard aGuard( m_aMutex );
1145 m_xActiveControl = nullptr;
1146 implSetCurrentControl( nullptr );
1148 // clean up our children
1149 for (const auto& rpChild : m_aChildren)
1151 // search the position of the model within the form
1152 Reference< XFormComponent > xForm(rpChild->getModel(), UNO_QUERY);
1153 sal_uInt32 nPos = m_xModelAsIndex->getCount();
1154 Reference< XFormComponent > xTemp;
1155 for( ; nPos; )
1158 m_xModelAsIndex->getByIndex( --nPos ) >>= xTemp;
1159 if ( xForm.get() == xTemp.get() )
1161 Reference< XInterface > xIfc( rpChild, UNO_QUERY );
1162 m_xModelAsManager->detach( nPos, xIfc );
1163 break;
1167 Reference< XComponent > (rpChild, UNO_QUERY_THROW)->dispose();
1169 m_aChildren.clear();
1171 disposeAllFeaturesAndDispatchers();
1173 if ( m_xFormOperations.is() )
1174 m_xFormOperations->dispose();
1175 m_xFormOperations.clear();
1177 if (m_bDBConnection)
1178 unload();
1180 setContainer( nullptr );
1181 setModel( nullptr );
1182 setParent( nullptr );
1184 ::comphelper::disposeComponent( m_xComposer );
1186 m_bDBConnection = false;
1190 namespace
1192 bool lcl_shouldUseDynamicControlBorder( const Reference< XInterface >& _rxForm, const Any& _rDynamicColorProp )
1194 bool bDoUse = false;
1195 if ( !( _rDynamicColorProp >>= bDoUse ) )
1197 DocumentType eDocType = DocumentClassification::classifyHostDocument( _rxForm );
1198 return ControlLayouter::useDynamicBorderColor( eDocType );
1200 return bDoUse;
1205 void SAL_CALL FormController::propertyChange(const PropertyChangeEvent& evt)
1207 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1208 if ( evt.PropertyName == FM_PROP_BOUNDFIELD )
1210 Reference<XPropertySet> xOldBound;
1211 evt.OldValue >>= xOldBound;
1212 if ( !xOldBound.is() && evt.NewValue.hasValue() )
1214 Reference< XControlModel > xControlModel(evt.Source,UNO_QUERY);
1215 Reference< XControl > xControl = findControl(m_aControls,xControlModel,false,false);
1216 if ( xControl.is() )
1218 startControlModifyListening( xControl );
1219 Reference<XPropertySet> xProp(xControlModel,UNO_QUERY);
1220 if ( xProp.is() )
1221 xProp->removePropertyChangeListener(FM_PROP_BOUNDFIELD, this);
1225 else
1227 bool bModifiedChanged = (evt.PropertyName == FM_PROP_ISMODIFIED);
1228 bool bNewChanged = (evt.PropertyName == FM_PROP_ISNEW);
1229 if (bModifiedChanged || bNewChanged)
1231 ::osl::MutexGuard aGuard( m_aMutex );
1232 if (bModifiedChanged)
1233 m_bCurrentRecordModified = ::comphelper::getBOOL(evt.NewValue);
1234 else
1235 m_bCurrentRecordNew = ::comphelper::getBOOL(evt.NewValue);
1237 // toggle the locking
1238 if (m_bLocked != determineLockState())
1240 m_bLocked = !m_bLocked;
1241 setLocks();
1242 if (isListeningForChanges())
1243 startListening();
1244 else
1245 stopListening();
1248 if ( bNewChanged )
1249 m_aToggleEvent.Call();
1251 if (!m_bCurrentRecordModified)
1252 m_bModified = false;
1254 else if ( evt.PropertyName == FM_PROP_DYNAMIC_CONTROL_BORDER )
1256 bool bEnable = lcl_shouldUseDynamicControlBorder( evt.Source, evt.NewValue );
1257 if ( bEnable )
1259 m_aControlBorderManager.enableDynamicBorderColor();
1260 if ( m_xActiveControl.is() )
1261 m_aControlBorderManager.focusGained( m_xActiveControl );
1263 else
1265 m_aControlBorderManager.disableDynamicBorderColor();
1272 bool FormController::replaceControl( const Reference< XControl >& _rxExistentControl, const Reference< XControl >& _rxNewControl )
1274 bool bSuccess = false;
1277 Reference< XIdentifierReplace > xContainer( getContainer(), UNO_QUERY );
1278 DBG_ASSERT( xContainer.is(), "FormController::replaceControl: yes, it's not required by the service description, but XIdentifierReplace would be nice!" );
1279 if ( xContainer.is() )
1281 // look up the ID of _rxExistentControl
1282 Sequence< sal_Int32 > aIdentifiers( xContainer->getIdentifiers() );
1283 const sal_Int32* pIdentifiers = std::find_if(aIdentifiers.begin(), aIdentifiers.end(),
1284 [&xContainer, &_rxExistentControl](const sal_Int32 nId) {
1285 Reference< XControl > xCheck( xContainer->getByIdentifier( nId ), UNO_QUERY );
1286 return xCheck == _rxExistentControl;
1288 DBG_ASSERT( pIdentifiers != aIdentifiers.end(), "FormController::replaceControl: did not find the control in the container!" );
1289 if ( pIdentifiers != aIdentifiers.end() )
1291 bool bReplacedWasActive = ( m_xActiveControl.get() == _rxExistentControl.get() );
1292 bool bReplacedWasCurrent = ( m_xCurrentControl.get() == _rxExistentControl.get() );
1294 if ( bReplacedWasActive )
1296 m_xActiveControl = nullptr;
1297 implSetCurrentControl( nullptr );
1299 else if ( bReplacedWasCurrent )
1301 implSetCurrentControl( _rxNewControl );
1304 // carry over the model
1305 _rxNewControl->setModel( _rxExistentControl->getModel() );
1307 xContainer->replaceByIdentifer( *pIdentifiers, makeAny( _rxNewControl ) );
1308 bSuccess = true;
1310 if ( bReplacedWasActive )
1312 Reference< XWindow > xControlWindow( _rxNewControl, UNO_QUERY );
1313 if ( xControlWindow.is() )
1314 xControlWindow->setFocus();
1319 catch( const Exception& )
1321 DBG_UNHANDLED_EXCEPTION("svx");
1324 Reference< XControl > xDisposeIt( bSuccess ? _rxExistentControl : _rxNewControl );
1325 ::comphelper::disposeComponent( xDisposeIt );
1326 return bSuccess;
1330 void FormController::toggleAutoFields(bool bAutoFields)
1332 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1335 Sequence< Reference< XControl > > aControlsCopy( m_aControls );
1336 const Reference< XControl >* pControls = aControlsCopy.getConstArray();
1337 sal_Int32 nControls = aControlsCopy.getLength();
1339 if (bAutoFields)
1341 // as we don't want new controls to be attached to the scripting environment
1342 // we change attach flags
1343 m_bAttachEvents = false;
1344 for (sal_Int32 i = nControls; i > 0;)
1346 Reference< XControl > xControl = pControls[--i];
1347 if (xControl.is())
1349 Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
1350 if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
1352 // does the model use a bound field ?
1353 Reference< XPropertySet > xField;
1354 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
1356 // is it an autofield?
1357 if ( xField.is()
1358 && ::comphelper::hasProperty( FM_PROP_AUTOINCREMENT, xField )
1359 && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_AUTOINCREMENT ) )
1362 replaceControl( xControl, new FmXAutoControl() );
1367 m_bAttachEvents = true;
1369 else
1371 m_bDetachEvents = false;
1372 for (sal_Int32 i = nControls; i > 0;)
1374 Reference< XControl > xControl = pControls[--i];
1375 if (xControl.is())
1377 Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
1378 if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
1380 // does the model use a bound field ?
1381 Reference< XPropertySet > xField;
1382 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
1384 // is it an autofield?
1385 if ( xField.is()
1386 && ::comphelper::hasProperty( FM_PROP_AUTOINCREMENT, xField )
1387 && ::comphelper::getBOOL( xField->getPropertyValue(FM_PROP_AUTOINCREMENT ) )
1390 OUString sServiceName;
1391 OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DEFAULTCONTROL ) >>= sServiceName );
1392 Reference< XControl > xNewControl( m_xComponentContext->getServiceManager()->createInstanceWithContext( sServiceName, m_xComponentContext ), UNO_QUERY );
1393 replaceControl( xControl, xNewControl );
1398 m_bDetachEvents = true;
1403 IMPL_LINK_NOARG(FormController, OnToggleAutoFields, void*, void)
1405 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1407 toggleAutoFields(m_bCurrentRecordNew);
1410 // XTextListener
1411 void SAL_CALL FormController::textChanged(const TextEvent& e)
1413 // SYNCHRONIZED -->
1414 ::osl::ClearableMutexGuard aGuard( m_aMutex );
1415 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1416 if ( !m_bFiltering )
1418 impl_onModify();
1419 return;
1422 if ( m_bSuspendFilterTextListening )
1423 return;
1425 Reference< XTextComponent > xText(e.Source,UNO_QUERY);
1426 OUString aText = xText->getText();
1428 if ( m_aFilterRows.empty() )
1429 appendEmptyDisjunctiveTerm();
1431 // find the current row
1432 if ( ( m_nCurrentFilterPosition < 0 ) || ( o3tl::make_unsigned(m_nCurrentFilterPosition) >= m_aFilterRows.size() ) )
1434 OSL_ENSURE( false, "FormController::textChanged: m_nCurrentFilterPosition is wrong!" );
1435 return;
1438 FmFilterRow& rRow = m_aFilterRows[ m_nCurrentFilterPosition ];
1440 // do we have a new filter
1441 if (!aText.isEmpty())
1442 rRow[xText] = aText;
1443 else
1445 // do we have the control in the row
1446 FmFilterRow::iterator iter = rRow.find(xText);
1447 // erase the entry out of the row
1448 if (iter != rRow.end())
1449 rRow.erase(iter);
1452 // multiplex the event to our FilterControllerListeners
1453 FilterEvent aEvent;
1454 aEvent.Source = *this;
1455 aEvent.FilterComponent = ::std::find( m_aFilterComponents.begin(), m_aFilterComponents.end(), xText ) - m_aFilterComponents.begin();
1456 aEvent.DisjunctiveTerm = getActiveTerm();
1457 aEvent.PredicateExpression = aText;
1459 aGuard.clear();
1460 // <-- SYNCHRONIZED
1462 // notify the changed filter expression
1463 m_aFilterListeners.notifyEach( &XFilterControllerListener::predicateExpressionChanged, aEvent );
1466 // XItemListener
1467 void SAL_CALL FormController::itemStateChanged(const ItemEvent& /*rEvent*/)
1469 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1470 impl_onModify();
1473 // XModificationBroadcaster
1474 void SAL_CALL FormController::addModifyListener(const Reference< XModifyListener > & l)
1476 ::osl::MutexGuard aGuard( m_aMutex );
1477 impl_checkDisposed_throw();
1478 m_aModifyListeners.addInterface( l );
1481 void FormController::removeModifyListener(const Reference< XModifyListener > & l)
1483 ::osl::MutexGuard aGuard( m_aMutex );
1484 impl_checkDisposed_throw();
1485 m_aModifyListeners.removeInterface( l );
1488 // XModificationListener
1489 void FormController::modified( const EventObject& _rEvent )
1491 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1495 if ( _rEvent.Source != m_xActiveControl )
1496 { // let this control grab the focus
1497 // (this case may happen if somebody moves the scroll wheel of the mouse over a control
1498 // which does not have the focus)
1499 // 85511 - 29.05.2001 - frank.schoenheit@germany.sun.com
1501 // also, it happens when an image control gets a new image by double-clicking it
1502 // #i88458# / 2009-01-12 / frank.schoenheit@sun.com
1503 Reference< XWindow > xControlWindow( _rEvent.Source, UNO_QUERY_THROW );
1504 xControlWindow->setFocus();
1507 catch( const Exception& )
1509 DBG_UNHANDLED_EXCEPTION("svx");
1512 impl_onModify();
1515 void FormController::impl_checkDisposed_throw() const
1517 if ( impl_isDisposed_nofail() )
1518 throw DisposedException( OUString(), *const_cast< FormController* >( this ) );
1521 void FormController::impl_onModify()
1523 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1526 ::osl::MutexGuard aGuard( m_aMutex );
1527 if ( !m_bModified )
1528 m_bModified = true;
1531 EventObject aEvt(static_cast<cppu::OWeakObject*>(this));
1532 m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvt );
1535 void FormController::impl_addFilterRow( const FmFilterRow& _row )
1537 m_aFilterRows.push_back( _row );
1539 if ( m_aFilterRows.size() == 1 )
1540 { // that's the first row ever
1541 OSL_ENSURE( m_nCurrentFilterPosition == -1, "FormController::impl_addFilterRow: inconsistency!" );
1542 m_nCurrentFilterPosition = 0;
1546 void FormController::impl_appendEmptyFilterRow( ::osl::ClearableMutexGuard& _rClearBeforeNotify )
1548 // SYNCHRONIZED -->
1549 impl_addFilterRow( FmFilterRow() );
1551 // notify the listeners
1552 FilterEvent aEvent;
1553 aEvent.Source = *this;
1554 aEvent.DisjunctiveTerm = static_cast<sal_Int32>(m_aFilterRows.size()) - 1;
1555 _rClearBeforeNotify.clear();
1556 // <-- SYNCHRONIZED
1557 m_aFilterListeners.notifyEach( &XFilterControllerListener::disjunctiveTermAdded, aEvent );
1560 bool FormController::determineLockState() const
1562 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1563 // a.) in filter mode we are always locked
1564 // b.) if we have no valid model or our model (a result set) is not alive -> we're locked
1565 // c.) if we are inserting everything is OK and we are not locked
1566 // d.) if are not updatable or on invalid position
1567 Reference< XResultSet > xResultSet(m_xModelAsIndex, UNO_QUERY);
1568 if (m_bFiltering || !xResultSet.is() || !isRowSetAlive(xResultSet))
1569 return true;
1570 else
1571 return !(m_bCanInsert && m_bCurrentRecordNew)
1572 && (xResultSet->isBeforeFirst() || xResultSet->isAfterLast() || xResultSet->rowDeleted() || !m_bCanUpdate);
1575 // FocusListener
1576 void FormController::focusGained(const FocusEvent& e)
1578 // SYNCHRONIZED -->
1579 ::osl::ClearableMutexGuard aGuard( m_aMutex );
1580 impl_checkDisposed_throw();
1582 m_aControlBorderManager.focusGained( e.Source );
1584 Reference< XControl > xControl(e.Source, UNO_QUERY);
1585 if (m_bDBConnection)
1587 // do we need to keep the locking of the commit
1588 // we hold the lock as long as the control differs from the current
1589 // otherwise we disabled the lock
1590 m_bCommitLock = m_bCommitLock && xControl.get() != m_xCurrentControl.get();
1591 if (m_bCommitLock)
1592 return;
1594 // when do we have to commit a value to form or a filter
1595 // a.) if the current value is modified
1596 // b.) there must be a current control
1597 // c.) and it must be different from the new focus owning control or
1598 // d.) the focus is moving around (so we have only one control)
1600 if ( ( m_bModified || m_bFiltering )
1601 && m_xCurrentControl.is()
1602 && ( ( xControl.get() != m_xCurrentControl.get() )
1603 || ( ( e.FocusFlags & FocusChangeReason::AROUND )
1604 && ( m_bCycle || m_bFiltering )
1609 // check the old control if the content is ok
1610 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
1611 Reference< XBoundControl > xLockingTest(m_xCurrentControl, UNO_QUERY);
1612 bool bControlIsLocked = xLockingTest.is() && xLockingTest->getLock();
1613 assert(!bControlIsLocked && "FormController::Gained: I'm modified and the current control is locked ? How this ?");
1614 // normally, a locked control should not be modified, so probably my bModified must
1615 // have been set from a different context, which I would not understand ...
1616 #endif
1617 DBG_ASSERT(m_xCurrentControl.is(), "no CurrentControl set");
1618 // first the control ask if it supports the IFace
1619 Reference< XBoundComponent > xBound(m_xCurrentControl, UNO_QUERY);
1620 if (!xBound.is() && m_xCurrentControl.is())
1621 xBound.set(m_xCurrentControl->getModel(), UNO_QUERY);
1623 // lock if we lose the focus during commit
1624 m_bCommitLock = true;
1626 // commit unsuccessful, reset focus
1627 if (xBound.is() && !xBound->commit())
1629 // the commit failed and we don't commit again until the current control
1630 // which couldn't be commit gains the focus again
1631 Reference< XWindow > xWindow(m_xCurrentControl, UNO_QUERY);
1632 if (xWindow.is())
1633 xWindow->setFocus();
1634 return;
1636 else
1638 m_bModified = false;
1639 m_bCommitLock = false;
1643 if (!m_bFiltering && m_bCycle && (e.FocusFlags & FocusChangeReason::AROUND) && m_xCurrentControl.is())
1645 OSL_ENSURE( m_xFormOperations.is(), "FormController::focusGained: hmm?" );
1646 // should have been created in setModel
1649 if ( e.FocusFlags & FocusChangeReason::FORWARD )
1651 if ( m_xFormOperations.is() && m_xFormOperations->isEnabled( FormFeature::MoveToNext ) )
1652 m_xFormOperations->execute( FormFeature::MoveToNext );
1654 else // backward
1656 if ( m_xFormOperations.is() && m_xFormOperations->isEnabled( FormFeature::MoveToPrevious ) )
1657 m_xFormOperations->execute( FormFeature::MoveToPrevious );
1660 catch ( const Exception& )
1662 // don't handle this any further. That's an ... admissible error.
1663 DBG_UNHANDLED_EXCEPTION("svx");
1668 // still one and the same control
1669 if ( ( m_xActiveControl == xControl )
1670 && ( xControl == m_xCurrentControl )
1673 DBG_ASSERT(m_xCurrentControl.is(), "No CurrentControl selected");
1674 return;
1677 bool bActivated = !m_xActiveControl.is() && xControl.is();
1679 m_xActiveControl = xControl;
1681 implSetCurrentControl( xControl );
1682 SAL_WARN_IF( !m_xCurrentControl.is(), "svx.form", "implSetCurrentControl did nonsense!" );
1684 if ( bActivated )
1686 // (asynchronously) call activation handlers
1687 m_aActivationEvent.Call();
1689 // call modify listeners
1690 if ( m_bModified )
1691 m_aModifyListeners.notifyEach( &XModifyListener::modified, EventObject( *this ) );
1694 // invalidate all features which depend on the currently focused control
1695 if ( m_bDBConnection && !m_bFiltering )
1696 implInvalidateCurrentControlDependentFeatures();
1698 if ( !m_xCurrentControl.is() )
1699 return;
1701 // control gets focus, then possibly in the visible range
1702 Reference< XFormControllerContext > xContext( m_xFormControllerContext );
1703 Reference< XControl > xCurrentControl( m_xCurrentControl );
1704 aGuard.clear();
1705 // <-- SYNCHRONIZED
1707 if ( xContext.is() )
1708 xContext->makeVisible( xCurrentControl );
1711 IMPL_LINK_NOARG( FormController, OnActivated, void*, void )
1713 EventObject aEvent;
1714 aEvent.Source = *this;
1715 m_aActivateListeners.notifyEach( &XFormControllerListener::formActivated, aEvent );
1718 IMPL_LINK_NOARG( FormController, OnDeactivated, void*, void )
1720 EventObject aEvent;
1721 aEvent.Source = *this;
1722 m_aActivateListeners.notifyEach( &XFormControllerListener::formDeactivated, aEvent );
1725 void FormController::focusLost(const FocusEvent& e)
1727 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1729 m_aControlBorderManager.focusLost( e.Source );
1731 Reference< XWindowPeer > xNext(e.NextFocus, UNO_QUERY);
1732 // if focus hasn't passed to some other window, e.g. focus in a welded item, don't deactivate
1733 if (!xNext)
1734 return;
1735 Reference< XControl > xNextControl = isInList(xNext);
1736 if (!xNextControl.is())
1738 m_xActiveControl = nullptr;
1739 m_aDeactivationEvent.Call();
1743 void SAL_CALL FormController::mousePressed( const awt::MouseEvent& /*_rEvent*/ )
1745 // not interested in
1748 void SAL_CALL FormController::mouseReleased( const awt::MouseEvent& /*_rEvent*/ )
1750 // not interested in
1753 void SAL_CALL FormController::mouseEntered( const awt::MouseEvent& _rEvent )
1755 m_aControlBorderManager.mouseEntered( _rEvent.Source );
1758 void SAL_CALL FormController::mouseExited( const awt::MouseEvent& _rEvent )
1760 m_aControlBorderManager.mouseExited( _rEvent.Source );
1763 void SAL_CALL FormController::componentValidityChanged( const EventObject& _rSource )
1765 Reference< XControl > xControl( findControl( m_aControls, Reference< XControlModel >( _rSource.Source, UNO_QUERY ), false, false ) );
1766 Reference< XValidatableFormComponent > xValidatable( _rSource.Source, UNO_QUERY );
1768 OSL_ENSURE( xControl.is() && xValidatable.is(), "FormController::componentValidityChanged: huh?" );
1770 if ( xControl.is() && xValidatable.is() )
1771 m_aControlBorderManager.validityChanged( xControl, xValidatable );
1775 void FormController::setModel(const Reference< XTabControllerModel > & Model)
1777 ::osl::MutexGuard aGuard( m_aMutex );
1778 impl_checkDisposed_throw();
1780 DBG_ASSERT(m_xTabController.is(), "FormController::setModel : invalid aggregate !");
1784 // disconnect from the old model
1785 if (m_xModelAsIndex.is())
1787 if (m_bDBConnection)
1789 // we are currently working on the model
1790 EventObject aEvt(m_xModelAsIndex);
1791 unloaded(aEvt);
1794 Reference< XLoadable > xForm(m_xModelAsIndex, UNO_QUERY);
1795 if (xForm.is())
1796 xForm->removeLoadListener(this);
1798 Reference< XSQLErrorBroadcaster > xBroadcaster(m_xModelAsIndex, UNO_QUERY);
1799 if (xBroadcaster.is())
1800 xBroadcaster->removeSQLErrorListener(this);
1802 Reference< XDatabaseParameterBroadcaster > xParamBroadcaster(m_xModelAsIndex, UNO_QUERY);
1803 if (xParamBroadcaster.is())
1804 xParamBroadcaster->removeParameterListener(this);
1808 disposeAllFeaturesAndDispatchers();
1810 if ( m_xFormOperations.is() )
1811 m_xFormOperations->dispose();
1812 m_xFormOperations.clear();
1814 // set the new model wait for the load event
1815 if (m_xTabController.is())
1816 m_xTabController->setModel(Model);
1817 m_xModelAsIndex.set(Model, UNO_QUERY);
1818 m_xModelAsManager.set(Model, UNO_QUERY);
1820 // only if both ifaces exit, the controller will work successful
1821 if (!m_xModelAsIndex.is() || !m_xModelAsManager.is())
1823 m_xModelAsManager = nullptr;
1824 m_xModelAsIndex = nullptr;
1827 if (m_xModelAsIndex.is())
1829 // re-create m_xFormOperations
1830 m_xFormOperations = FormOperations::createWithFormController( m_xComponentContext, this );
1831 m_xFormOperations->setFeatureInvalidation( this );
1833 // adding load and ui interaction listeners
1834 Reference< XLoadable > xForm(Model, UNO_QUERY);
1835 if (xForm.is())
1836 xForm->addLoadListener(this);
1838 Reference< XSQLErrorBroadcaster > xBroadcaster(Model, UNO_QUERY);
1839 if (xBroadcaster.is())
1840 xBroadcaster->addSQLErrorListener(this);
1842 Reference< XDatabaseParameterBroadcaster > xParamBroadcaster(Model, UNO_QUERY);
1843 if (xParamBroadcaster.is())
1844 xParamBroadcaster->addParameterListener(this);
1846 // well, is the database already loaded?
1847 // then we have to simulate a load event
1848 Reference< XLoadable > xCursor(m_xModelAsIndex, UNO_QUERY);
1849 if (xCursor.is() && xCursor->isLoaded())
1851 EventObject aEvt(xCursor);
1852 loaded(aEvt);
1855 Reference< XPropertySet > xModelProps( m_xModelAsIndex, UNO_QUERY );
1856 Reference< XPropertySetInfo > xPropInfo( xModelProps->getPropertySetInfo() );
1857 if ( xPropInfo.is()
1858 && xPropInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER )
1859 && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_FOCUS )
1860 && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_MOUSE )
1861 && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_INVALID )
1864 bool bEnableDynamicControlBorder = lcl_shouldUseDynamicControlBorder(
1865 xModelProps, xModelProps->getPropertyValue( FM_PROP_DYNAMIC_CONTROL_BORDER ) );
1866 if ( bEnableDynamicControlBorder )
1867 m_aControlBorderManager.enableDynamicBorderColor();
1868 else
1869 m_aControlBorderManager.disableDynamicBorderColor();
1871 Color nColor;
1872 if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_FOCUS ) >>= nColor )
1873 m_aControlBorderManager.setStatusColor( ControlStatus::Focused, nColor );
1874 if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_MOUSE ) >>= nColor )
1875 m_aControlBorderManager.setStatusColor( ControlStatus::MouseHover, nColor );
1876 if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_INVALID ) >>= nColor )
1877 m_aControlBorderManager.setStatusColor( ControlStatus::Invalid, nColor );
1881 catch( const Exception& )
1883 DBG_UNHANDLED_EXCEPTION("svx");
1888 Reference< XTabControllerModel > FormController::getModel()
1890 ::osl::MutexGuard aGuard( m_aMutex );
1891 impl_checkDisposed_throw();
1893 DBG_ASSERT(m_xTabController.is(), "FormController::getModel : invalid aggregate !");
1894 if (!m_xTabController.is())
1895 return Reference< XTabControllerModel > ();
1896 return m_xTabController->getModel();
1900 void FormController::addToEventAttacher(const Reference< XControl > & xControl)
1902 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1903 OSL_ENSURE( xControl.is(), "FormController::addToEventAttacher: invalid control - how did you reach this?" );
1904 if ( !xControl.is() )
1905 return; /* throw IllegalArgumentException(); */
1907 // register at the event attacher
1908 Reference< XFormComponent > xComp(xControl->getModel(), UNO_QUERY);
1909 if (!(xComp.is() && m_xModelAsIndex.is()))
1910 return;
1912 // and look for the position of the ControlModel in it
1913 sal_uInt32 nPos = m_xModelAsIndex->getCount();
1914 Reference< XFormComponent > xTemp;
1915 for( ; nPos; )
1917 m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
1918 if (xComp.get() == xTemp.get())
1920 m_xModelAsManager->attach( nPos, Reference<XInterface>( xControl, UNO_QUERY ), makeAny(xControl) );
1921 break;
1927 void FormController::removeFromEventAttacher(const Reference< XControl > & xControl)
1929 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1930 OSL_ENSURE( xControl.is(), "FormController::removeFromEventAttacher: invalid control - how did you reach this?" );
1931 if ( !xControl.is() )
1932 return; /* throw IllegalArgumentException(); */
1934 // register at the event attacher
1935 Reference< XFormComponent > xComp(xControl->getModel(), UNO_QUERY);
1936 if ( !(xComp.is() && m_xModelAsIndex.is()) )
1937 return;
1939 // and look for the position of the ControlModel in it
1940 sal_uInt32 nPos = m_xModelAsIndex->getCount();
1941 Reference< XFormComponent > xTemp;
1942 for( ; nPos; )
1944 m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
1945 if (xComp.get() == xTemp.get())
1947 m_xModelAsManager->detach( nPos, Reference<XInterface>( xControl, UNO_QUERY ) );
1948 break;
1954 void FormController::setContainer(const Reference< XControlContainer > & xContainer)
1956 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1957 Reference< XTabControllerModel > xTabModel(getModel());
1958 DBG_ASSERT(xTabModel.is() || !xContainer.is(), "No Model defined");
1959 // if we have a new container we need a model
1960 DBG_ASSERT(m_xTabController.is(), "FormController::setContainer : invalid aggregate !");
1962 ::osl::MutexGuard aGuard( m_aMutex );
1963 Reference< XContainer > xCurrentContainer;
1964 if (m_xTabController.is())
1965 xCurrentContainer.set(m_xTabController->getContainer(), UNO_QUERY);
1966 if (xCurrentContainer.is())
1968 xCurrentContainer->removeContainerListener(this);
1970 if ( m_aTabActivationIdle.IsActive() )
1971 m_aTabActivationIdle.Stop();
1973 // clear the filter map
1974 ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), RemoveComponentTextListener( this ) );
1975 m_aFilterComponents.clear();
1977 // collecting the controls
1978 for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
1979 implControlRemoved( rControl, true );
1981 // make database-specific things
1982 if (m_bDBConnection && isListeningForChanges())
1983 stopListening();
1985 m_aControls.realloc( 0 );
1988 if (m_xTabController.is())
1989 m_xTabController->setContainer(xContainer);
1991 // What controls belong to the container?
1992 if (xContainer.is() && xTabModel.is())
1994 const Sequence< Reference< XControlModel > > aModels = xTabModel->getControlModels();
1995 Sequence< Reference< XControl > > aAllControls = xContainer->getControls();
1997 sal_Int32 nCount = aModels.getLength();
1998 m_aControls = Sequence< Reference< XControl > >( nCount );
1999 Reference< XControl > * pControls = m_aControls.getArray();
2001 // collecting the controls
2002 sal_Int32 j = 0;
2003 for (const Reference< XControlModel >& rModel : aModels )
2005 Reference< XControl > xControl = findControl( aAllControls, rModel, false, true );
2006 if ( xControl.is() )
2008 pControls[j++] = xControl;
2009 implControlInserted( xControl, true );
2013 // not every model had an associated control
2014 if (j != nCount)
2015 m_aControls.realloc(j);
2017 // listen at the container
2018 Reference< XContainer > xNewContainer(xContainer, UNO_QUERY);
2019 if (xNewContainer.is())
2020 xNewContainer->addContainerListener(this);
2022 // make database-specific things
2023 if (m_bDBConnection)
2025 m_bLocked = determineLockState();
2026 setLocks();
2027 if (!isLocked())
2028 startListening();
2031 // the controls are in the right order
2032 m_bControlsSorted = true;
2036 Reference< XControlContainer > FormController::getContainer()
2038 ::osl::MutexGuard aGuard( m_aMutex );
2039 impl_checkDisposed_throw();
2041 DBG_ASSERT(m_xTabController.is(), "FormController::getContainer : invalid aggregate !");
2042 if (!m_xTabController.is())
2043 return Reference< XControlContainer > ();
2044 return m_xTabController->getContainer();
2048 Sequence< Reference< XControl > > FormController::getControls()
2050 ::osl::MutexGuard aGuard( m_aMutex );
2051 impl_checkDisposed_throw();
2053 if (!m_bControlsSorted)
2055 Reference< XTabControllerModel > xModel = getModel();
2056 if (!xModel.is())
2057 return m_aControls;
2059 const Sequence< Reference< XControlModel > > aControlModels = xModel->getControlModels();
2060 sal_Int32 nModels = aControlModels.getLength();
2062 Sequence< Reference< XControl > > aNewControls(nModels);
2064 Reference< XControl > * pControls = aNewControls.getArray();
2065 Reference< XControl > xControl;
2067 // rearrange the controls according to the tab order sequence
2068 sal_Int32 j = 0;
2069 for ( const Reference< XControlModel >& rModel : aControlModels )
2071 xControl = findControl( m_aControls, rModel, true, true );
2072 if ( xControl.is() )
2073 pControls[j++] = xControl;
2076 // not every model had an associated control
2077 if ( j != nModels )
2078 aNewControls.realloc( j );
2080 m_aControls = aNewControls;
2081 m_bControlsSorted = true;
2083 return m_aControls;
2087 void FormController::autoTabOrder()
2089 ::osl::MutexGuard aGuard( m_aMutex );
2090 impl_checkDisposed_throw();
2092 DBG_ASSERT(m_xTabController.is(), "FormController::autoTabOrder : invalid aggregate !");
2093 if (m_xTabController.is())
2094 m_xTabController->autoTabOrder();
2098 void FormController::activateTabOrder()
2100 ::osl::MutexGuard aGuard( m_aMutex );
2101 impl_checkDisposed_throw();
2103 DBG_ASSERT(m_xTabController.is(), "FormController::activateTabOrder : invalid aggregate !");
2104 if (m_xTabController.is())
2105 m_xTabController->activateTabOrder();
2109 void FormController::setControlLock(const Reference< XControl > & xControl)
2111 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2112 bool bLocked = isLocked();
2114 // It is locked
2115 // a. if the entire record is locked
2116 // b. if the associated field is locked
2117 Reference< XBoundControl > xBound(xControl, UNO_QUERY);
2118 if (!(xBound.is() &&
2119 ( (bLocked && bLocked != bool(xBound->getLock())) ||
2120 !bLocked))) // always uncheck individual fields when unlocking
2121 return;
2123 // there is a data source
2124 Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
2125 if (!(xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet)))
2126 return;
2128 // what about the ReadOnly and Enable properties
2129 bool bTouch = true;
2130 if (::comphelper::hasProperty(FM_PROP_ENABLED, xSet))
2131 bTouch = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ENABLED));
2132 if (::comphelper::hasProperty(FM_PROP_READONLY, xSet))
2133 bTouch = !::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_READONLY));
2135 if (!bTouch)
2136 return;
2138 Reference< XPropertySet > xField;
2139 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
2140 if (!xField.is())
2141 return;
2143 if (bLocked)
2144 xBound->setLock(bLocked);
2145 else
2149 Any aVal = xField->getPropertyValue(FM_PROP_ISREADONLY);
2150 if (aVal.hasValue() && ::comphelper::getBOOL(aVal))
2151 xBound->setLock(true);
2152 else
2153 xBound->setLock(bLocked);
2155 catch( const Exception& )
2157 DBG_UNHANDLED_EXCEPTION("svx");
2164 void FormController::setLocks()
2166 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2167 // lock/unlock all controls connected to a data source
2168 for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
2169 setControlLock( rControl );
2173 namespace
2175 bool lcl_shouldListenForModifications( const Reference< XControl >& _rxControl, const Reference< XPropertyChangeListener >& _rxBoundFieldListener )
2177 bool bShould = false;
2179 Reference< XBoundComponent > xBound( _rxControl, UNO_QUERY );
2180 if ( xBound.is() )
2182 bShould = true;
2184 else if ( _rxControl.is() )
2186 Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY );
2187 if ( xModelProps.is() && ::comphelper::hasProperty( FM_PROP_BOUNDFIELD, xModelProps ) )
2189 Reference< XPropertySet > xField;
2190 xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ) >>= xField;
2191 bShould = xField.is();
2193 if ( !bShould && _rxBoundFieldListener.is() )
2194 xModelProps->addPropertyChangeListener( FM_PROP_BOUNDFIELD, _rxBoundFieldListener );
2198 return bShould;
2203 void FormController::startControlModifyListening(const Reference< XControl > & xControl)
2205 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2207 bool bModifyListening = lcl_shouldListenForModifications( xControl, this );
2209 // artificial while
2210 while ( bModifyListening )
2212 Reference< XModifyBroadcaster > xMod(xControl, UNO_QUERY);
2213 if (xMod.is())
2215 xMod->addModifyListener(this);
2216 break;
2219 // all the text to prematurely recognize a modified
2220 Reference< XTextComponent > xText(xControl, UNO_QUERY);
2221 if (xText.is())
2223 xText->addTextListener(this);
2224 break;
2227 Reference< XCheckBox > xBox(xControl, UNO_QUERY);
2228 if (xBox.is())
2230 xBox->addItemListener(this);
2231 break;
2234 Reference< XComboBox > xCbBox(xControl, UNO_QUERY);
2235 if (xCbBox.is())
2237 xCbBox->addItemListener(this);
2238 break;
2241 Reference< XListBox > xListBox(xControl, UNO_QUERY);
2242 if (xListBox.is())
2244 xListBox->addItemListener(this);
2245 break;
2247 break;
2252 void FormController::stopControlModifyListening(const Reference< XControl > & xControl)
2254 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2256 bool bModifyListening = lcl_shouldListenForModifications( xControl, nullptr );
2258 // artificial while
2259 while (bModifyListening)
2261 Reference< XModifyBroadcaster > xMod(xControl, UNO_QUERY);
2262 if (xMod.is())
2264 xMod->removeModifyListener(this);
2265 break;
2267 // all the text to prematurely recognize a modified
2268 Reference< XTextComponent > xText(xControl, UNO_QUERY);
2269 if (xText.is())
2271 xText->removeTextListener(this);
2272 break;
2275 Reference< XCheckBox > xBox(xControl, UNO_QUERY);
2276 if (xBox.is())
2278 xBox->removeItemListener(this);
2279 break;
2282 Reference< XComboBox > xCbBox(xControl, UNO_QUERY);
2283 if (xCbBox.is())
2285 xCbBox->removeItemListener(this);
2286 break;
2289 Reference< XListBox > xListBox(xControl, UNO_QUERY);
2290 if (xListBox.is())
2292 xListBox->removeItemListener(this);
2293 break;
2295 break;
2300 void FormController::startListening()
2302 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2303 m_bModified = false;
2305 // now register at bound fields
2306 for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
2307 startControlModifyListening( rControl );
2311 void FormController::stopListening()
2313 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2314 m_bModified = false;
2316 // now register at bound fields
2317 for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
2318 stopControlModifyListening( rControl );
2322 Reference< XControl > FormController::findControl(Sequence< Reference< XControl > >& _rControls, const Reference< XControlModel > & xCtrlModel ,bool _bRemove,bool _bOverWrite) const
2324 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2325 DBG_ASSERT( xCtrlModel.is(), "findControl - which ?!" );
2327 Reference< XControl >* pControls = std::find_if(_rControls.begin(), _rControls.end(),
2328 [&xCtrlModel](const Reference< XControl >& rControl) {
2329 return rControl.is() && rControl->getModel().get() == xCtrlModel.get(); });
2330 if (pControls != _rControls.end())
2332 Reference< XControl > xControl( *pControls );
2333 if ( _bRemove )
2335 auto i = static_cast<sal_Int32>(std::distance(_rControls.begin(), pControls));
2336 ::comphelper::removeElementAt( _rControls, i );
2338 else if ( _bOverWrite )
2339 pControls->clear();
2340 return xControl;
2342 return Reference< XControl > ();
2346 void FormController::implControlInserted( const Reference< XControl>& _rxControl, bool _bAddToEventAttacher )
2348 Reference< XWindow > xWindow( _rxControl, UNO_QUERY );
2349 if ( xWindow.is() )
2351 xWindow->addFocusListener( this );
2352 xWindow->addMouseListener( this );
2354 if ( _bAddToEventAttacher )
2355 addToEventAttacher( _rxControl );
2358 // add a dispatch interceptor to the control (if supported)
2359 Reference< XDispatchProviderInterception > xInterception( _rxControl, UNO_QUERY );
2360 if ( xInterception.is() )
2361 createInterceptor( xInterception );
2363 if ( !_rxControl.is() )
2364 return;
2366 Reference< XControlModel > xModel( _rxControl->getModel() );
2368 // we want to know about the reset of the model of our controls
2369 // (for correctly resetting m_bModified)
2370 Reference< XReset > xReset( xModel, UNO_QUERY );
2371 if ( xReset.is() )
2372 xReset->addResetListener( this );
2374 // and we want to know about the validity, to visually indicate it
2375 Reference< XValidatableFormComponent > xValidatable( xModel, UNO_QUERY );
2376 if ( xValidatable.is() )
2378 xValidatable->addFormComponentValidityListener( this );
2379 m_aControlBorderManager.validityChanged( _rxControl, xValidatable );
2385 void FormController::implControlRemoved( const Reference< XControl>& _rxControl, bool _bRemoveFromEventAttacher )
2387 Reference< XWindow > xWindow( _rxControl, UNO_QUERY );
2388 if ( xWindow.is() )
2390 xWindow->removeFocusListener( this );
2391 xWindow->removeMouseListener( this );
2393 if ( _bRemoveFromEventAttacher )
2394 removeFromEventAttacher( _rxControl );
2397 Reference< XDispatchProviderInterception > xInterception( _rxControl, UNO_QUERY);
2398 if ( xInterception.is() )
2399 deleteInterceptor( xInterception );
2401 if ( _rxControl.is() )
2403 Reference< XControlModel > xModel( _rxControl->getModel() );
2405 Reference< XReset > xReset( xModel, UNO_QUERY );
2406 if ( xReset.is() )
2407 xReset->removeResetListener( this );
2409 Reference< XValidatableFormComponent > xValidatable( xModel, UNO_QUERY );
2410 if ( xValidatable.is() )
2411 xValidatable->removeFormComponentValidityListener( this );
2416 void FormController::implSetCurrentControl( const Reference< XControl >& _rxControl )
2418 if ( m_xCurrentControl.get() == _rxControl.get() )
2419 return;
2421 Reference< XGridControl > xGridControl( m_xCurrentControl, UNO_QUERY );
2422 if ( xGridControl.is() )
2423 xGridControl->removeGridControlListener( this );
2425 m_xCurrentControl = _rxControl;
2427 xGridControl.set( m_xCurrentControl, UNO_QUERY );
2428 if ( xGridControl.is() )
2429 xGridControl->addGridControlListener( this );
2433 void FormController::insertControl(const Reference< XControl > & xControl)
2435 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2436 m_bControlsSorted = false;
2437 m_aControls.realloc(m_aControls.getLength() + 1);
2438 m_aControls.getArray()[m_aControls.getLength() - 1] = xControl;
2440 if (m_pColumnInfoCache)
2441 m_pColumnInfoCache->deinitializeControls();
2443 implControlInserted( xControl, m_bAttachEvents );
2445 if (m_bDBConnection && !m_bFiltering)
2446 setControlLock(xControl);
2448 if (isListeningForChanges() && m_bAttachEvents)
2449 startControlModifyListening( xControl );
2453 void FormController::removeControl(const Reference< XControl > & xControl)
2455 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2456 auto pControl = std::find_if(m_aControls.begin(), m_aControls.end(),
2457 [&xControl](const Reference< XControl >& rControl) { return xControl.get() == rControl.get(); });
2458 if (pControl != m_aControls.end())
2460 auto nIndex = static_cast<sal_Int32>(std::distance(m_aControls.begin(), pControl));
2461 ::comphelper::removeElementAt( m_aControls, nIndex );
2464 FilterComponents::iterator componentPos = ::std::find( m_aFilterComponents.begin(), m_aFilterComponents.end(), xControl );
2465 if ( componentPos != m_aFilterComponents.end() )
2466 m_aFilterComponents.erase( componentPos );
2468 implControlRemoved( xControl, m_bDetachEvents );
2470 if ( isListeningForChanges() && m_bDetachEvents )
2471 stopControlModifyListening( xControl );
2474 // XLoadListener
2476 void FormController::loaded(const EventObject& rEvent)
2478 OSL_ENSURE( rEvent.Source == m_xModelAsIndex, "FormController::loaded: where did this come from?" );
2480 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2481 ::osl::MutexGuard aGuard( m_aMutex );
2482 Reference< XRowSet > xForm(rEvent.Source, UNO_QUERY);
2483 // do we have a connected data source
2484 if (xForm.is() && getConnection(xForm).is())
2486 Reference< XPropertySet > xSet(xForm, UNO_QUERY);
2487 if (xSet.is())
2489 Any aVal = xSet->getPropertyValue(FM_PROP_CYCLE);
2490 sal_Int32 aVal2 = 0;
2491 ::cppu::enum2int(aVal2,aVal);
2492 m_bCycle = !aVal.hasValue() || static_cast<form::TabulatorCycle>(aVal2) == TabulatorCycle_RECORDS;
2493 m_bCanUpdate = canUpdate(xSet);
2494 m_bCanInsert = canInsert(xSet);
2495 m_bCurrentRecordModified = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED));
2496 m_bCurrentRecordNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
2498 startFormListening( xSet, false );
2500 // set the locks for the current controls
2501 if (getContainer().is())
2503 m_aLoadEvent.Call();
2506 else
2508 m_bCanInsert = m_bCanUpdate = m_bCycle = false;
2509 m_bCurrentRecordModified = false;
2510 m_bCurrentRecordNew = false;
2511 m_bLocked = false;
2513 m_bDBConnection = true;
2515 else
2517 m_bDBConnection = false;
2518 m_bCanInsert = m_bCanUpdate = m_bCycle = false;
2519 m_bCurrentRecordModified = false;
2520 m_bCurrentRecordNew = false;
2521 m_bLocked = false;
2524 Reference< XColumnsSupplier > xFormColumns( xForm, UNO_QUERY );
2525 m_pColumnInfoCache.reset( xFormColumns.is() ? new ColumnInfoCache( xFormColumns ) : nullptr );
2527 updateAllDispatchers();
2531 void FormController::updateAllDispatchers() const
2533 ::std::for_each(
2534 m_aFeatureDispatchers.begin(),
2535 m_aFeatureDispatchers.end(),
2536 [] (const DispatcherContainer::value_type& dispatcher) {
2537 UpdateAllListeners()(dispatcher.second);
2542 IMPL_LINK_NOARG(FormController, OnLoad, void*, void)
2544 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2545 m_bLocked = determineLockState();
2547 setLocks();
2549 if (!m_bLocked)
2550 startListening();
2552 // just one exception toggle the auto values
2553 if (m_bCurrentRecordNew)
2554 toggleAutoFields(true);
2558 void FormController::unloaded(const EventObject& /*rEvent*/)
2560 ::osl::MutexGuard aGuard( m_aMutex );
2561 impl_checkDisposed_throw();
2563 updateAllDispatchers();
2567 void FormController::reloading(const EventObject& /*aEvent*/)
2569 ::osl::MutexGuard aGuard( m_aMutex );
2570 impl_checkDisposed_throw();
2572 // do the same like in unloading
2573 // just one exception toggle the auto values
2574 m_aToggleEvent.CancelPendingCall();
2575 unload();
2579 void FormController::reloaded(const EventObject& aEvent)
2581 ::osl::MutexGuard aGuard( m_aMutex );
2582 impl_checkDisposed_throw();
2584 loaded(aEvent);
2588 void FormController::unloading(const EventObject& /*aEvent*/)
2590 ::osl::MutexGuard aGuard( m_aMutex );
2591 impl_checkDisposed_throw();
2593 unload();
2597 void FormController::unload()
2599 ::osl::MutexGuard aGuard( m_aMutex );
2600 impl_checkDisposed_throw();
2602 m_aLoadEvent.CancelPendingCall();
2604 // be sure not to have autofields
2605 if (m_bCurrentRecordNew)
2606 toggleAutoFields(false);
2608 // remove bound field listing again
2609 removeBoundFieldListener();
2611 if (m_bDBConnection && isListeningForChanges())
2612 stopListening();
2614 Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
2615 if ( m_bDBConnection && xSet.is() )
2616 stopFormListening( xSet, false );
2618 m_bDBConnection = false;
2619 m_bCanInsert = m_bCanUpdate = m_bCycle = false;
2620 m_bCurrentRecordModified = m_bCurrentRecordNew = m_bLocked = false;
2622 m_pColumnInfoCache.reset();
2626 void FormController::removeBoundFieldListener()
2628 for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
2630 Reference< XPropertySet > xProp( rControl, UNO_QUERY );
2631 if ( xProp.is() )
2632 xProp->removePropertyChangeListener( FM_PROP_BOUNDFIELD, this );
2637 void FormController::startFormListening( const Reference< XPropertySet >& _rxForm, bool _bPropertiesOnly )
2641 if ( m_bCanInsert || m_bCanUpdate ) // form can be modified
2643 _rxForm->addPropertyChangeListener( FM_PROP_ISNEW, this );
2644 _rxForm->addPropertyChangeListener( FM_PROP_ISMODIFIED, this );
2646 if ( !_bPropertiesOnly )
2648 // set the Listener for UI interaction
2649 Reference< XRowSetApproveBroadcaster > xApprove( _rxForm, UNO_QUERY );
2650 if ( xApprove.is() )
2651 xApprove->addRowSetApproveListener( this );
2653 // listener for row set changes
2654 Reference< XRowSet > xRowSet( _rxForm, UNO_QUERY );
2655 if ( xRowSet.is() )
2656 xRowSet->addRowSetListener( this );
2660 Reference< XPropertySetInfo > xInfo = _rxForm->getPropertySetInfo();
2661 if ( xInfo.is() && xInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER ) )
2662 _rxForm->addPropertyChangeListener( FM_PROP_DYNAMIC_CONTROL_BORDER, this );
2664 catch( const Exception& )
2666 DBG_UNHANDLED_EXCEPTION("svx");
2671 void FormController::stopFormListening( const Reference< XPropertySet >& _rxForm, bool _bPropertiesOnly )
2675 if ( m_bCanInsert || m_bCanUpdate )
2677 _rxForm->removePropertyChangeListener( FM_PROP_ISNEW, this );
2678 _rxForm->removePropertyChangeListener( FM_PROP_ISMODIFIED, this );
2680 if ( !_bPropertiesOnly )
2682 Reference< XRowSetApproveBroadcaster > xApprove( _rxForm, UNO_QUERY );
2683 if (xApprove.is())
2684 xApprove->removeRowSetApproveListener(this);
2686 Reference< XRowSet > xRowSet( _rxForm, UNO_QUERY );
2687 if ( xRowSet.is() )
2688 xRowSet->removeRowSetListener( this );
2692 Reference< XPropertySetInfo > xInfo = _rxForm->getPropertySetInfo();
2693 if ( xInfo.is() && xInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER ) )
2694 _rxForm->removePropertyChangeListener( FM_PROP_DYNAMIC_CONTROL_BORDER, this );
2696 catch( const Exception& )
2698 DBG_UNHANDLED_EXCEPTION("svx");
2702 // css::sdbc::XRowSetListener
2704 void FormController::cursorMoved(const EventObject& /*event*/)
2706 ::osl::MutexGuard aGuard( m_aMutex );
2707 impl_checkDisposed_throw();
2709 // toggle the locking ?
2710 if (m_bLocked != determineLockState())
2712 m_bLocked = !m_bLocked;
2713 setLocks();
2714 if (isListeningForChanges())
2715 startListening();
2716 else
2717 stopListening();
2720 // neither the current control nor the current record are modified anymore
2721 m_bCurrentRecordModified = m_bModified = false;
2725 void FormController::rowChanged(const EventObject& /*event*/)
2727 // not interested in ...
2730 void FormController::rowSetChanged(const EventObject& /*event*/)
2732 // not interested in ...
2736 // XContainerListener
2738 void SAL_CALL FormController::elementInserted(const ContainerEvent& evt)
2740 ::osl::MutexGuard aGuard( m_aMutex );
2741 impl_checkDisposed_throw();
2743 Reference< XControl > xControl( evt.Element, UNO_QUERY );
2744 if ( !xControl.is() )
2745 return;
2747 Reference< XFormComponent > xModel(xControl->getModel(), UNO_QUERY);
2748 if (xModel.is() && m_xModelAsIndex == xModel->getParent())
2750 insertControl(xControl);
2752 if ( m_aTabActivationIdle.IsActive() )
2753 m_aTabActivationIdle.Stop();
2755 m_aTabActivationIdle.Start();
2757 // are we in filtermode and a XModeSelector has inserted an element
2758 else if (m_bFiltering && Reference< XModeSelector > (evt.Source, UNO_QUERY).is())
2760 xModel.set(evt.Source, UNO_QUERY);
2761 if (xModel.is() && m_xModelAsIndex == xModel->getParent())
2763 Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
2764 if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
2766 // does the model use a bound field ?
2767 Reference< XPropertySet > xField;
2768 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
2770 Reference< XTextComponent > xText(xControl, UNO_QUERY);
2771 // may we filter the field?
2772 if (xText.is() && xField.is() && ::comphelper::hasProperty(FM_PROP_SEARCHABLE, xField) &&
2773 ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_SEARCHABLE)))
2775 m_aFilterComponents.push_back( xText );
2776 xText->addTextListener( this );
2784 void SAL_CALL FormController::elementReplaced(const ContainerEvent& evt)
2786 // simulate an elementRemoved
2787 ContainerEvent aRemoveEvent( evt );
2788 aRemoveEvent.Element = evt.ReplacedElement;
2789 aRemoveEvent.ReplacedElement = Any();
2790 elementRemoved( aRemoveEvent );
2792 // simulate an elementInserted
2793 ContainerEvent aInsertEvent( evt );
2794 aInsertEvent.ReplacedElement = Any();
2795 elementInserted( aInsertEvent );
2799 void SAL_CALL FormController::elementRemoved(const ContainerEvent& evt)
2801 ::osl::MutexGuard aGuard( m_aMutex );
2802 impl_checkDisposed_throw();
2804 Reference< XControl > xControl;
2805 evt.Element >>= xControl;
2806 if (!xControl.is())
2807 return;
2809 Reference< XFormComponent > xModel(xControl->getModel(), UNO_QUERY);
2810 if (xModel.is() && m_xModelAsIndex == xModel->getParent())
2812 removeControl(xControl);
2813 // Do not recalculate TabOrder, because it must already work internally!
2815 // are we in filtermode and a XModeSelector has inserted an element
2816 else if (m_bFiltering && Reference< XModeSelector > (evt.Source, UNO_QUERY).is())
2818 FilterComponents::iterator componentPos = ::std::find(
2819 m_aFilterComponents.begin(), m_aFilterComponents.end(), xControl );
2820 if ( componentPos != m_aFilterComponents.end() )
2821 m_aFilterComponents.erase( componentPos );
2826 Reference< XControl > FormController::isInList(const Reference< XWindowPeer > & xPeer) const
2828 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2829 const Reference< XControl >* pControls = m_aControls.getConstArray();
2831 sal_uInt32 nCtrls = m_aControls.getLength();
2832 for ( sal_uInt32 n = 0; n < nCtrls && xPeer.is(); ++n, ++pControls )
2834 if ( pControls->is() )
2836 Reference< XVclWindowPeer > xCtrlPeer( (*pControls)->getPeer(), UNO_QUERY);
2837 if ( ( xCtrlPeer.get() == xPeer.get() ) || xCtrlPeer->isChild( xPeer ) )
2838 return *pControls;
2841 return Reference< XControl > ();
2845 void FormController::activateFirst()
2847 ::osl::MutexGuard aGuard( m_aMutex );
2848 impl_checkDisposed_throw();
2850 DBG_ASSERT(m_xTabController.is(), "FormController::activateFirst : invalid aggregate !");
2851 if (m_xTabController.is())
2852 m_xTabController->activateFirst();
2856 void FormController::activateLast()
2858 ::osl::MutexGuard aGuard( m_aMutex );
2859 impl_checkDisposed_throw();
2861 DBG_ASSERT(m_xTabController.is(), "FormController::activateLast : invalid aggregate !");
2862 if (m_xTabController.is())
2863 m_xTabController->activateLast();
2866 // XFormController
2868 Reference< XFormOperations > SAL_CALL FormController::getFormOperations()
2870 ::osl::MutexGuard aGuard( m_aMutex );
2871 impl_checkDisposed_throw();
2873 return m_xFormOperations;
2877 Reference< XControl> SAL_CALL FormController::getCurrentControl()
2879 ::osl::MutexGuard aGuard( m_aMutex );
2880 impl_checkDisposed_throw();
2881 return m_xCurrentControl;
2885 void SAL_CALL FormController::addActivateListener(const Reference< XFormControllerListener > & l)
2887 ::osl::MutexGuard aGuard( m_aMutex );
2888 impl_checkDisposed_throw();
2889 m_aActivateListeners.addInterface(l);
2892 void SAL_CALL FormController::removeActivateListener(const Reference< XFormControllerListener > & l)
2894 ::osl::MutexGuard aGuard( m_aMutex );
2895 impl_checkDisposed_throw();
2896 m_aActivateListeners.removeInterface(l);
2900 void SAL_CALL FormController::addChildController( const Reference< XFormController >& ChildController )
2902 ::osl::MutexGuard aGuard( m_aMutex );
2903 impl_checkDisposed_throw();
2905 if ( !ChildController.is() )
2906 throw IllegalArgumentException( OUString(), *this, 1 );
2907 // TODO: (localized) error message
2909 // the parent of our (to-be-)child must be our own model
2910 Reference< XFormComponent > xFormOfChild( ChildController->getModel(), UNO_QUERY );
2911 if ( !xFormOfChild.is() )
2912 throw IllegalArgumentException( OUString(), *this, 1 );
2913 // TODO: (localized) error message
2915 if ( xFormOfChild->getParent() != m_xModelAsIndex )
2916 throw IllegalArgumentException( OUString(), *this, 1 );
2917 // TODO: (localized) error message
2919 m_aChildren.push_back( ChildController );
2920 ChildController->setParent( *this );
2922 // search the position of the model within the form
2923 sal_uInt32 nPos = m_xModelAsIndex->getCount();
2924 Reference< XFormComponent > xTemp;
2925 for( ; nPos; )
2927 m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
2928 if ( xFormOfChild == xTemp )
2930 m_xModelAsManager->attach( nPos, Reference<XInterface>( ChildController, UNO_QUERY ), makeAny( ChildController) );
2931 break;
2937 Reference< XFormControllerContext > SAL_CALL FormController::getContext()
2939 ::osl::MutexGuard aGuard( m_aMutex );
2940 impl_checkDisposed_throw();
2941 return m_xFormControllerContext;
2945 void SAL_CALL FormController::setContext( const Reference< XFormControllerContext >& _context )
2947 ::osl::MutexGuard aGuard( m_aMutex );
2948 impl_checkDisposed_throw();
2949 m_xFormControllerContext = _context;
2953 Reference< XInteractionHandler > SAL_CALL FormController::getInteractionHandler()
2955 ::osl::MutexGuard aGuard( m_aMutex );
2956 impl_checkDisposed_throw();
2957 return m_xInteractionHandler;
2961 void SAL_CALL FormController::setInteractionHandler( const Reference< XInteractionHandler >& _interactionHandler )
2963 ::osl::MutexGuard aGuard( m_aMutex );
2964 impl_checkDisposed_throw();
2965 m_xInteractionHandler = _interactionHandler;
2969 void FormController::setFilter(::std::vector<FmFieldInfo>& rFieldInfos)
2971 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2972 // create the composer
2973 Reference< XRowSet > xForm(m_xModelAsIndex, UNO_QUERY);
2974 Reference< XConnection > xConnection(getConnection(xForm));
2975 if (xForm.is())
2979 Reference< XMultiServiceFactory > xFactory( xConnection, UNO_QUERY_THROW );
2980 m_xComposer.set(
2981 xFactory->createInstance("com.sun.star.sdb.SingleSelectQueryComposer"),
2982 UNO_QUERY_THROW );
2984 Reference< XPropertySet > xSet( xForm, UNO_QUERY );
2985 OUString sStatement = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_ACTIVECOMMAND ) );
2986 OUString sFilter = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_FILTER ) );
2987 m_xComposer->setElementaryQuery( sStatement );
2988 m_xComposer->setFilter( sFilter );
2990 catch( const Exception& )
2992 DBG_UNHANDLED_EXCEPTION("svx");
2996 if (m_xComposer.is())
2998 const Sequence< Sequence < PropertyValue > > aFilterRows = m_xComposer->getStructuredFilter();
3000 // ok, we receive the list of filters as sequence of fieldnames, value
3001 // now we have to transform the fieldname into UI names, that could be a label of the field or
3002 // an aliasname or the fieldname itself
3004 // first adjust the field names if necessary
3005 Reference< XNameAccess > xQueryColumns =
3006 Reference< XColumnsSupplier >( m_xComposer, UNO_QUERY_THROW )->getColumns();
3008 for (auto& rFieldInfo : rFieldInfos)
3010 if ( xQueryColumns->hasByName(rFieldInfo.aFieldName) )
3012 if ( (xQueryColumns->getByName(rFieldInfo.aFieldName) >>= rFieldInfo.xField) && rFieldInfo.xField.is() )
3013 rFieldInfo.xField->getPropertyValue(FM_PROP_REALNAME) >>= rFieldInfo.aFieldName;
3017 Reference< XDatabaseMetaData> xMetaData(xConnection->getMetaData());
3018 // now transfer the filters into Value/TextComponent pairs
3019 ::comphelper::UStringMixEqual aCompare(xMetaData->storesMixedCaseQuotedIdentifiers());
3021 // need to parse criteria localized
3022 Reference< XNumberFormatsSupplier> xFormatSupplier( getNumberFormats(xConnection, true));
3023 Reference< XNumberFormatter> xFormatter = NumberFormatter::create(m_xComponentContext);
3024 xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
3025 Locale aAppLocale = Application::GetSettings().GetUILanguageTag().getLocale();
3026 const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetUILocaleDataWrapper() );
3027 OUString strDecimalSeparator = rLocaleWrapper.getNumDecimalSep();
3029 // retrieving the filter
3030 for (const Sequence < PropertyValue >& rRow : aFilterRows)
3032 FmFilterRow aRow;
3034 // search a field for the given name
3035 for (const PropertyValue& rRefValue : rRow)
3037 // look for the text component
3038 Reference< XPropertySet > xField;
3041 Reference< XPropertySet > xSet;
3042 OUString aRealName;
3044 // first look with the given name
3045 if (xQueryColumns->hasByName(rRefValue.Name))
3047 xQueryColumns->getByName(rRefValue.Name) >>= xSet;
3049 // get the RealName
3050 xSet->getPropertyValue("RealName") >>= aRealName;
3052 // compare the condition field name and the RealName
3053 if (aCompare(aRealName, rRefValue.Name))
3054 xField = xSet;
3056 if (!xField.is())
3058 // no we have to check every column to find the realname
3059 Reference< XIndexAccess > xColumnsByIndex(xQueryColumns, UNO_QUERY);
3060 for (sal_Int32 n = 0, nCount = xColumnsByIndex->getCount(); n < nCount; n++)
3062 xColumnsByIndex->getByIndex(n) >>= xSet;
3063 xSet->getPropertyValue("RealName") >>= aRealName;
3064 if (aCompare(aRealName, rRefValue.Name))
3066 // get the column by its alias
3067 xField = xSet;
3068 break;
3072 if (!xField.is())
3073 continue;
3075 catch (const Exception&)
3077 continue;
3080 // find the text component
3081 for (const auto& rFieldInfo : rFieldInfos)
3083 // we found the field so insert a new entry to the filter row
3084 if (rFieldInfo.xField == xField)
3086 // do we already have the control ?
3087 if (aRow.find(rFieldInfo.xText) != aRow.end())
3089 OString aVal = m_pParser->getContext().getIntlKeywordAscii(IParseContext::InternationalKeyCode::And);
3090 OUString aCompText = aRow[rFieldInfo.xText] + " " +
3091 OUString(aVal.getStr(),aVal.getLength(),RTL_TEXTENCODING_ASCII_US) + " " +
3092 ::comphelper::getString(rRefValue.Value);
3093 aRow[rFieldInfo.xText] = aCompText;
3095 else
3097 OUString sPredicate,sErrorMsg;
3098 rRefValue.Value >>= sPredicate;
3099 std::unique_ptr< OSQLParseNode > pParseNode = predicateTree(sErrorMsg, sPredicate, xFormatter, xField);
3100 if ( pParseNode != nullptr )
3102 OUString sCriteria;
3103 switch (rRefValue.Handle)
3105 case css::sdb::SQLFilterOperator::EQUAL:
3106 sCriteria += "=";
3107 break;
3108 case css::sdb::SQLFilterOperator::NOT_EQUAL:
3109 sCriteria += "!=";
3110 break;
3111 case css::sdb::SQLFilterOperator::LESS:
3112 sCriteria += "<";
3113 break;
3114 case css::sdb::SQLFilterOperator::GREATER:
3115 sCriteria += ">";
3116 break;
3117 case css::sdb::SQLFilterOperator::LESS_EQUAL:
3118 sCriteria += "<=";
3119 break;
3120 case css::sdb::SQLFilterOperator::GREATER_EQUAL:
3121 sCriteria += ">=";
3122 break;
3123 case css::sdb::SQLFilterOperator::LIKE:
3124 sCriteria += "LIKE ";
3125 break;
3126 case css::sdb::SQLFilterOperator::NOT_LIKE:
3127 sCriteria += "NOT LIKE ";
3128 break;
3129 case css::sdb::SQLFilterOperator::SQLNULL:
3130 sCriteria += "IS NULL";
3131 break;
3132 case css::sdb::SQLFilterOperator::NOT_SQLNULL:
3133 sCriteria += "IS NOT NULL";
3134 break;
3136 pParseNode->parseNodeToPredicateStr( sCriteria
3137 ,xConnection
3138 ,xFormatter
3139 ,xField
3140 ,OUString()
3141 ,aAppLocale
3142 ,strDecimalSeparator
3143 ,getParseContext());
3144 aRow[rFieldInfo.xText] = sCriteria;
3151 if (aRow.empty())
3152 continue;
3154 impl_addFilterRow( aRow );
3158 // now set the filter controls
3159 for (const auto& rFieldInfo : rFieldInfos)
3161 m_aFilterComponents.push_back( rFieldInfo.xText );
3166 void FormController::startFiltering()
3168 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
3170 Reference< XConnection > xConnection( getConnection( Reference< XRowSet >( m_xModelAsIndex, UNO_QUERY ) ) );
3171 if ( !xConnection.is() )
3172 // nothing to do - can't filter a form which is not connected
3173 return;
3175 // stop listening for controls
3176 if (isListeningForChanges())
3177 stopListening();
3179 m_bFiltering = true;
3181 // as we don't want new controls to be attached to the scripting environment
3182 // we change attach flags
3183 m_bAttachEvents = false;
3185 // exchanging the controls for the current form
3186 Sequence< Reference< XControl > > aControlsCopy( m_aControls );
3187 const Reference< XControl >* pControls = aControlsCopy.getConstArray();
3188 sal_Int32 nControlCount = aControlsCopy.getLength();
3190 // the control we have to activate after replacement
3191 Reference< XNumberFormatsSupplier > xFormatSupplier = getNumberFormats(xConnection, true);
3192 Reference< XNumberFormatter > xFormatter = NumberFormatter::create(m_xComponentContext);
3193 xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
3195 // structure for storing the field info
3196 ::std::vector<FmFieldInfo> aFieldInfos;
3198 for (sal_Int32 i = nControlCount; i > 0;)
3200 Reference< XControl > xControl = pControls[--i];
3201 if (xControl.is())
3203 // no events for the control anymore
3204 removeFromEventAttacher(xControl);
3206 // do we have a mode selector
3207 Reference< XModeSelector > xSelector(xControl, UNO_QUERY);
3208 if (xSelector.is())
3210 xSelector->setMode( "FilterMode" );
3212 // listening for new controls of the selector
3213 Reference< XContainer > xContainer(xSelector, UNO_QUERY);
3214 if (xContainer.is())
3215 xContainer->addContainerListener(this);
3217 Reference< XEnumerationAccess > xElementAccess(xSelector, UNO_QUERY);
3218 if (xElementAccess.is())
3220 Reference< XEnumeration > xEnumeration(xElementAccess->createEnumeration());
3221 Reference< XControl > xSubControl;
3222 while (xEnumeration->hasMoreElements())
3224 xEnumeration->nextElement() >>= xSubControl;
3225 if (xSubControl.is())
3227 Reference< XPropertySet > xSet(xSubControl->getModel(), UNO_QUERY);
3228 if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
3230 // does the model use a bound field ?
3231 Reference< XPropertySet > xField;
3232 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
3234 Reference< XTextComponent > xText(xSubControl, UNO_QUERY);
3235 // may we filter the field?
3236 if (xText.is() && xField.is() && ::comphelper::hasProperty(FM_PROP_SEARCHABLE, xField) &&
3237 ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_SEARCHABLE)))
3239 aFieldInfos.emplace_back(xField, xText);
3240 xText->addTextListener(this);
3246 continue;
3249 Reference< XPropertySet > xModel( xControl->getModel(), UNO_QUERY );
3250 if (xModel.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xModel))
3252 // does the model use a bound field ?
3253 Any aVal = xModel->getPropertyValue(FM_PROP_BOUNDFIELD);
3254 Reference< XPropertySet > xField;
3255 aVal >>= xField;
3257 // may we filter the field?
3259 if ( xField.is()
3260 && ::comphelper::hasProperty( FM_PROP_SEARCHABLE, xField )
3261 && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_SEARCHABLE ) )
3264 // create a filter control
3265 Reference< XControl > xFilterControl = form::control::FilterControl::createWithFormat(
3266 m_xComponentContext,
3267 getDialogParentWindow(this),
3268 xFormatter,
3269 xModel);
3271 if ( replaceControl( xControl, xFilterControl ) )
3273 Reference< XTextComponent > xFilterText( xFilterControl, UNO_QUERY );
3274 aFieldInfos.emplace_back( xField, xFilterText );
3275 xFilterText->addTextListener(this);
3279 else
3281 // unsubscribe from EventManager
3286 // we have all filter controls now, so the next step is to read the filters from the form
3287 // resolve all aliases and set the current filter to the according structure
3288 setFilter(aFieldInfos);
3290 Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
3291 if ( xSet.is() )
3292 stopFormListening( xSet, true );
3294 impl_setTextOnAllFilter_throw();
3296 // lock all controls which are not used for filtering
3297 m_bLocked = determineLockState();
3298 setLocks();
3299 m_bAttachEvents = true;
3303 void FormController::stopFiltering()
3305 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
3306 if ( !m_bFiltering ) // #104693# OJ
3307 { // nothing to do
3308 return;
3311 m_bFiltering = false;
3312 m_bDetachEvents = false;
3314 ::comphelper::disposeComponent(m_xComposer);
3316 // exchanging the controls for the current form
3317 Sequence< Reference< XControl > > aControlsCopy( m_aControls );
3318 const Reference< XControl > * pControls = aControlsCopy.getConstArray();
3319 sal_Int32 nControlCount = aControlsCopy.getLength();
3321 // clear the filter control map
3322 ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), RemoveComponentTextListener( this ) );
3323 m_aFilterComponents.clear();
3325 for ( sal_Int32 i = nControlCount; i > 0; )
3327 Reference< XControl > xControl = pControls[--i];
3328 if (xControl.is())
3330 // now enable event handling again
3331 addToEventAttacher(xControl);
3333 Reference< XModeSelector > xSelector(xControl, UNO_QUERY);
3334 if (xSelector.is())
3336 xSelector->setMode( "DataMode" );
3338 // listening for new controls of the selector
3339 Reference< XContainer > xContainer(xSelector, UNO_QUERY);
3340 if (xContainer.is())
3341 xContainer->removeContainerListener(this);
3342 continue;
3345 Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
3346 if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
3348 // does the model use a bound field ?
3349 Reference< XPropertySet > xField;
3350 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
3352 // may we filter the field?
3353 if ( xField.is()
3354 && ::comphelper::hasProperty( FM_PROP_SEARCHABLE, xField )
3355 && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_SEARCHABLE ) )
3358 OUString sServiceName;
3359 OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DEFAULTCONTROL ) >>= sServiceName );
3360 Reference< XControl > xNewControl( m_xComponentContext->getServiceManager()->createInstanceWithContext( sServiceName, m_xComponentContext ), UNO_QUERY );
3361 replaceControl( xControl, xNewControl );
3367 Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
3368 if ( xSet.is() )
3369 startFormListening( xSet, true );
3371 m_bDetachEvents = true;
3373 m_aFilterRows.clear();
3374 m_nCurrentFilterPosition = -1;
3376 // release the locks if possible
3377 // lock all controls which are not used for filtering
3378 m_bLocked = determineLockState();
3379 setLocks();
3381 // restart listening for control modifications
3382 if (isListeningForChanges())
3383 startListening();
3386 // XModeSelector
3388 void FormController::setMode(const OUString& Mode)
3390 ::osl::MutexGuard aGuard( m_aMutex );
3391 impl_checkDisposed_throw();
3393 if (!supportsMode(Mode))
3394 throw NoSupportException();
3396 if (Mode == m_aMode)
3397 return;
3399 m_aMode = Mode;
3401 if ( Mode == "FilterMode" )
3402 startFiltering();
3403 else
3404 stopFiltering();
3406 for (const auto& rChild : m_aChildren)
3408 Reference< XModeSelector > xMode(rChild, UNO_QUERY);
3409 if ( xMode.is() )
3410 xMode->setMode(Mode);
3415 OUString SAL_CALL FormController::getMode()
3417 ::osl::MutexGuard aGuard( m_aMutex );
3418 impl_checkDisposed_throw();
3420 return m_aMode;
3424 Sequence< OUString > SAL_CALL FormController::getSupportedModes()
3426 ::osl::MutexGuard aGuard( m_aMutex );
3427 impl_checkDisposed_throw();
3429 static Sequence< OUString > const aModes
3431 "DataMode",
3432 "FilterMode"
3434 return aModes;
3437 sal_Bool SAL_CALL FormController::supportsMode(const OUString& Mode)
3439 ::osl::MutexGuard aGuard( m_aMutex );
3440 impl_checkDisposed_throw();
3442 Sequence< OUString > aModes(getSupportedModes());
3443 return comphelper::findValue(aModes, Mode) != -1;
3446 css::uno::Reference<css::awt::XWindow> FormController::getDialogParentWindow(css::uno::Reference<css::form::runtime::XFormController> xFormController)
3450 Reference< XControl > xContainerControl( xFormController->getContainer(), UNO_QUERY_THROW );
3451 Reference<XWindow> xContainerWindow(xContainerControl->getPeer(), UNO_QUERY_THROW);
3452 return xContainerWindow;
3454 catch( const Exception& )
3456 DBG_UNHANDLED_EXCEPTION("svx");
3458 return nullptr;
3461 bool FormController::checkFormComponentValidity( OUString& /* [out] */ _rFirstInvalidityExplanation, Reference< XControlModel >& /* [out] */ _rxFirstInvalidModel )
3465 Reference< XEnumerationAccess > xControlEnumAcc( getModel(), UNO_QUERY );
3466 Reference< XEnumeration > xControlEnumeration;
3467 if ( xControlEnumAcc.is() )
3468 xControlEnumeration = xControlEnumAcc->createEnumeration();
3469 OSL_ENSURE( xControlEnumeration.is(), "FormController::checkFormComponentValidity: cannot enumerate the controls!" );
3470 if ( !xControlEnumeration.is() )
3471 // assume all valid
3472 return true;
3474 Reference< XValidatableFormComponent > xValidatable;
3475 while ( xControlEnumeration->hasMoreElements() )
3477 if ( !( xControlEnumeration->nextElement() >>= xValidatable ) )
3478 // control does not support validation
3479 continue;
3481 if ( xValidatable->isValid() )
3482 continue;
3484 Reference< XValidator > xValidator( xValidatable->getValidator() );
3485 OSL_ENSURE( xValidator.is(), "FormController::checkFormComponentValidity: invalid, but no validator?" );
3486 if ( !xValidator.is() )
3487 // this violates the interface definition of css.form.validation.XValidatableFormComponent ...
3488 continue;
3490 _rFirstInvalidityExplanation = xValidator->explainInvalid( xValidatable->getCurrentValue() );
3491 _rxFirstInvalidModel.set(xValidatable, css::uno::UNO_QUERY);
3492 return false;
3495 catch( const Exception& )
3497 DBG_UNHANDLED_EXCEPTION("svx");
3499 return true;
3503 Reference< XControl > FormController::locateControl( const Reference< XControlModel >& _rxModel )
3507 const Sequence< Reference< XControl > > aControls( getControls() );
3509 for ( auto const & control : aControls )
3511 OSL_ENSURE( control.is(), "FormController::locateControl: NULL-control?" );
3512 if ( control.is() )
3514 if ( control->getModel() == _rxModel )
3515 return control;
3518 OSL_FAIL( "FormController::locateControl: did not find a control for this model!" );
3520 catch( const Exception& )
3522 DBG_UNHANDLED_EXCEPTION("svx");
3524 return nullptr;
3528 namespace
3530 void displayErrorSetFocus(const OUString& _rMessage, const Reference<XControl>& _rxFocusControl,
3531 const css::uno::Reference<css::awt::XWindow>& rDialogParent)
3533 SQLContext aError;
3534 aError.Message = SvxResId(RID_STR_WRITEERROR);
3535 aError.Details = _rMessage;
3536 displayException(aError, rDialogParent);
3538 if ( _rxFocusControl.is() )
3540 Reference< XWindow > xControlWindow( _rxFocusControl, UNO_QUERY );
3541 OSL_ENSURE( xControlWindow.is(), "displayErrorSetFocus: invalid control!" );
3542 if ( xControlWindow.is() )
3543 xControlWindow->setFocus();
3547 bool lcl_shouldValidateRequiredFields_nothrow( const Reference< XInterface >& _rxForm )
3551 static constexpr OUStringLiteral s_sFormsCheckRequiredFields = u"FormsCheckRequiredFields";
3553 // first, check whether the form has a property telling us the answer
3554 // this allows people to use the XPropertyContainer interface of a form to control
3555 // the behaviour on a per-form basis.
3556 Reference< XPropertySet > xFormProps( _rxForm, UNO_QUERY_THROW );
3557 Reference< XPropertySetInfo > xPSI( xFormProps->getPropertySetInfo() );
3558 if ( xPSI->hasPropertyByName( s_sFormsCheckRequiredFields ) )
3560 bool bShouldValidate = true;
3561 OSL_VERIFY( xFormProps->getPropertyValue( s_sFormsCheckRequiredFields ) >>= bShouldValidate );
3562 return bShouldValidate;
3565 // next, check the data source which created the connection
3566 Reference< XChild > xConnectionAsChild( xFormProps->getPropertyValue( FM_PROP_ACTIVE_CONNECTION ), UNO_QUERY_THROW );
3567 Reference< XPropertySet > xDataSource( xConnectionAsChild->getParent(), UNO_QUERY );
3568 if ( !xDataSource.is() )
3569 // seldom (but possible): this is not a connection created by a data source
3570 return true;
3572 Reference< XPropertySet > xDataSourceSettings(
3573 xDataSource->getPropertyValue("Settings"),
3574 UNO_QUERY_THROW );
3576 bool bShouldValidate = true;
3577 OSL_VERIFY( xDataSourceSettings->getPropertyValue( s_sFormsCheckRequiredFields ) >>= bShouldValidate );
3578 return bShouldValidate;
3580 catch( const Exception& )
3582 DBG_UNHANDLED_EXCEPTION("svx");
3585 return true;
3589 // XRowSetApproveListener
3591 sal_Bool SAL_CALL FormController::approveRowChange(const RowChangeEvent& _rEvent)
3593 ::osl::ClearableMutexGuard aGuard( m_aMutex );
3594 impl_checkDisposed_throw();
3596 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aRowSetApproveListeners);
3597 bool bValid = true;
3598 if (aIter.hasMoreElements())
3600 RowChangeEvent aEvt( _rEvent );
3601 aEvt.Source = *this;
3602 bValid = static_cast<XRowSetApproveListener*>(aIter.next())->approveRowChange(aEvt);
3605 if ( !bValid )
3606 return bValid;
3608 if ( ( _rEvent.Action != RowChangeAction::INSERT )
3609 && ( _rEvent.Action != RowChangeAction::UPDATE )
3611 return bValid;
3613 // if some of the control models are bound to validators, check them
3614 OUString sInvalidityExplanation;
3615 Reference< XControlModel > xInvalidModel;
3616 if ( !checkFormComponentValidity( sInvalidityExplanation, xInvalidModel ) )
3618 Reference< XControl > xControl( locateControl( xInvalidModel ) );
3619 aGuard.clear();
3620 displayErrorSetFocus( sInvalidityExplanation, xControl, getDialogParentWindow(this) );
3621 return false;
3624 // check values on NULL and required flag
3625 if ( !lcl_shouldValidateRequiredFields_nothrow( _rEvent.Source ) )
3626 return true;
3628 OSL_ENSURE(m_pColumnInfoCache, "FormController::approveRowChange: no column infos!");
3629 if (!m_pColumnInfoCache)
3630 return true;
3634 if ( !m_pColumnInfoCache->controlsInitialized() )
3635 m_pColumnInfoCache->initializeControls( getControls() );
3637 size_t colCount = m_pColumnInfoCache->getColumnCount();
3638 for ( size_t col = 0; col < colCount; ++col )
3640 const ColumnInfo& rColInfo = m_pColumnInfoCache->getColumnInfo( col );
3642 if ( rColInfo.bAutoIncrement )
3643 continue;
3645 if ( rColInfo.bReadOnly )
3646 continue;
3648 if ( !rColInfo.xFirstControlWithInputRequired.is() && !rColInfo.xFirstGridWithInputRequiredColumn.is() )
3650 continue;
3653 // TODO: in case of binary fields, this "getString" below is extremely expensive
3654 if ( !rColInfo.xColumn->wasNull() || !rColInfo.xColumn->getString().isEmpty() )
3655 continue;
3657 OUString sMessage( SvxResId( RID_ERR_FIELDREQUIRED ) );
3658 sMessage = sMessage.replaceFirst( "#", rColInfo.sName );
3660 // the control to focus
3661 Reference< XControl > xControl( rColInfo.xFirstControlWithInputRequired );
3662 if ( !xControl.is() )
3663 xControl.set( rColInfo.xFirstGridWithInputRequiredColumn, UNO_QUERY );
3665 aGuard.clear();
3666 displayErrorSetFocus( sMessage, rColInfo.xFirstControlWithInputRequired, getDialogParentWindow(this) );
3667 return false;
3670 catch( const Exception& )
3672 DBG_UNHANDLED_EXCEPTION("svx");
3675 return true;
3679 sal_Bool SAL_CALL FormController::approveCursorMove(const EventObject& event)
3681 ::osl::MutexGuard aGuard( m_aMutex );
3682 impl_checkDisposed_throw();
3684 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aRowSetApproveListeners);
3685 if (aIter.hasMoreElements())
3687 EventObject aEvt(event);
3688 aEvt.Source = *this;
3689 return static_cast<XRowSetApproveListener*>(aIter.next())->approveCursorMove(aEvt);
3692 return true;
3696 sal_Bool SAL_CALL FormController::approveRowSetChange(const EventObject& event)
3698 ::osl::MutexGuard aGuard( m_aMutex );
3699 impl_checkDisposed_throw();
3701 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aRowSetApproveListeners);
3702 if (aIter.hasMoreElements())
3704 EventObject aEvt(event);
3705 aEvt.Source = *this;
3706 return static_cast<XRowSetApproveListener*>(aIter.next())->approveRowSetChange(aEvt);
3709 return true;
3712 // XRowSetApproveBroadcaster
3714 void SAL_CALL FormController::addRowSetApproveListener(const Reference< XRowSetApproveListener > & _rxListener)
3716 ::osl::MutexGuard aGuard( m_aMutex );
3717 impl_checkDisposed_throw();
3719 m_aRowSetApproveListeners.addInterface(_rxListener);
3723 void SAL_CALL FormController::removeRowSetApproveListener(const Reference< XRowSetApproveListener > & _rxListener)
3725 ::osl::MutexGuard aGuard( m_aMutex );
3726 impl_checkDisposed_throw();
3728 m_aRowSetApproveListeners.removeInterface(_rxListener);
3731 // XErrorListener
3733 void SAL_CALL FormController::errorOccured(const SQLErrorEvent& aEvent)
3735 ::osl::ClearableMutexGuard aGuard( m_aMutex );
3736 impl_checkDisposed_throw();
3738 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aErrorListeners);
3739 if (aIter.hasMoreElements())
3741 SQLErrorEvent aEvt(aEvent);
3742 aEvt.Source = *this;
3743 static_cast<XSQLErrorListener*>(aIter.next())->errorOccured(aEvt);
3745 else
3747 aGuard.clear();
3748 displayException(aEvent, getDialogParentWindow(this));
3752 // XErrorBroadcaster
3754 void SAL_CALL FormController::addSQLErrorListener(const Reference< XSQLErrorListener > & aListener)
3756 ::osl::MutexGuard aGuard( m_aMutex );
3757 impl_checkDisposed_throw();
3759 m_aErrorListeners.addInterface(aListener);
3763 void SAL_CALL FormController::removeSQLErrorListener(const Reference< XSQLErrorListener > & aListener)
3765 ::osl::MutexGuard aGuard( m_aMutex );
3766 impl_checkDisposed_throw();
3768 m_aErrorListeners.removeInterface(aListener);
3771 // XDatabaseParameterBroadcaster2
3773 void SAL_CALL FormController::addDatabaseParameterListener(const Reference< XDatabaseParameterListener > & aListener)
3775 ::osl::MutexGuard aGuard( m_aMutex );
3776 impl_checkDisposed_throw();
3778 m_aParameterListeners.addInterface(aListener);
3782 void SAL_CALL FormController::removeDatabaseParameterListener(const Reference< XDatabaseParameterListener > & aListener)
3784 ::osl::MutexGuard aGuard( m_aMutex );
3785 impl_checkDisposed_throw();
3787 m_aParameterListeners.removeInterface(aListener);
3790 // XDatabaseParameterBroadcaster
3792 void SAL_CALL FormController::addParameterListener(const Reference< XDatabaseParameterListener > & aListener)
3794 FormController::addDatabaseParameterListener( aListener );
3798 void SAL_CALL FormController::removeParameterListener(const Reference< XDatabaseParameterListener > & aListener)
3800 FormController::removeDatabaseParameterListener( aListener );
3803 // XDatabaseParameterListener
3805 sal_Bool SAL_CALL FormController::approveParameter(const DatabaseParameterEvent& aEvent)
3807 SolarMutexGuard aSolarGuard;
3808 ::osl::MutexGuard aGuard( m_aMutex );
3809 impl_checkDisposed_throw();
3811 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aParameterListeners);
3812 if (aIter.hasMoreElements())
3814 DatabaseParameterEvent aEvt(aEvent);
3815 aEvt.Source = *this;
3816 return static_cast<XDatabaseParameterListener*>(aIter.next())->approveParameter(aEvt);
3818 else
3820 // default handling: instantiate an interaction handler and let it handle the parameter request
3823 if ( !ensureInteractionHandler() )
3824 return false;
3826 // two continuations allowed: OK and Cancel
3827 rtl::Reference<OParameterContinuation> pParamValues = new OParameterContinuation;
3828 rtl::Reference<OInteractionAbort> pAbort = new OInteractionAbort;
3829 // the request
3830 ParametersRequest aRequest;
3831 aRequest.Parameters = aEvent.Parameters;
3832 aRequest.Connection = getConnection(Reference< XRowSet >(aEvent.Source, UNO_QUERY));
3833 rtl::Reference<OInteractionRequest> pParamRequest = new OInteractionRequest(makeAny(aRequest));
3834 // some knittings
3835 pParamRequest->addContinuation(pParamValues);
3836 pParamRequest->addContinuation(pAbort);
3838 // handle the request
3839 m_xInteractionHandler->handle(pParamRequest);
3841 if (!pParamValues->wasSelected())
3842 // canceled
3843 return false;
3845 // transfer the values into the parameter supplier
3846 Sequence< PropertyValue > aFinalValues = pParamValues->getValues();
3847 if (aFinalValues.getLength() != aRequest.Parameters->getCount())
3849 OSL_FAIL("FormController::approveParameter: the InteractionHandler returned nonsense!");
3850 return false;
3852 const PropertyValue* pFinalValues = aFinalValues.getConstArray();
3853 for (sal_Int32 i=0; i<aFinalValues.getLength(); ++i, ++pFinalValues)
3855 Reference< XPropertySet > xParam(
3856 aRequest.Parameters->getByIndex(i), css::uno::UNO_QUERY);
3857 if (xParam.is())
3859 #ifdef DBG_UTIL
3860 OUString sName;
3861 xParam->getPropertyValue(FM_PROP_NAME) >>= sName;
3862 DBG_ASSERT(sName == pFinalValues->Name, "FormController::approveParameter: suspicious value names!");
3863 #endif
3864 try { xParam->setPropertyValue(FM_PROP_VALUE, pFinalValues->Value); }
3865 catch(Exception&)
3867 OSL_FAIL("FormController::approveParameter: setting one of the properties failed!");
3872 catch(Exception&)
3874 DBG_UNHANDLED_EXCEPTION("svx");
3877 return true;
3880 // XConfirmDeleteBroadcaster
3882 void SAL_CALL FormController::addConfirmDeleteListener(const Reference< XConfirmDeleteListener > & aListener)
3884 ::osl::MutexGuard aGuard( m_aMutex );
3885 impl_checkDisposed_throw();
3887 m_aDeleteListeners.addInterface(aListener);
3891 void SAL_CALL FormController::removeConfirmDeleteListener(const Reference< XConfirmDeleteListener > & aListener)
3893 ::osl::MutexGuard aGuard( m_aMutex );
3894 impl_checkDisposed_throw();
3896 m_aDeleteListeners.removeInterface(aListener);
3899 // XConfirmDeleteListener
3901 sal_Bool SAL_CALL FormController::confirmDelete(const RowChangeEvent& aEvent)
3903 ::osl::MutexGuard aGuard( m_aMutex );
3904 impl_checkDisposed_throw();
3906 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aDeleteListeners);
3907 if (aIter.hasMoreElements())
3909 RowChangeEvent aEvt(aEvent);
3910 aEvt.Source = *this;
3911 return static_cast<XConfirmDeleteListener*>(aIter.next())->confirmDelete(aEvt);
3913 // default handling: instantiate an interaction handler and let it handle the request
3915 OUString sTitle;
3916 sal_Int32 nLength = aEvent.Rows;
3917 if ( nLength > 1 )
3919 sTitle = SvxResId( RID_STR_DELETECONFIRM_RECORDS );
3920 sTitle = sTitle.replaceFirst( "#", OUString::number(nLength) );
3922 else
3923 sTitle = SvxResId( RID_STR_DELETECONFIRM_RECORD );
3927 if ( !ensureInteractionHandler() )
3928 return false;
3930 // two continuations allowed: Yes and No
3931 rtl::Reference<OInteractionApprove> pApprove = new OInteractionApprove;
3932 rtl::Reference<OInteractionDisapprove> pDisapprove = new OInteractionDisapprove;
3934 // the request
3935 SQLWarning aWarning;
3936 aWarning.Message = sTitle;
3937 SQLWarning aDetails;
3938 aDetails.Message = SvxResId(RID_STR_DELETECONFIRM);
3939 aWarning.NextException <<= aDetails;
3941 rtl::Reference<OInteractionRequest> pRequest = new OInteractionRequest( makeAny( aWarning ) );
3943 // some knittings
3944 pRequest->addContinuation( pApprove );
3945 pRequest->addContinuation( pDisapprove );
3947 // handle the request
3948 m_xInteractionHandler->handle( pRequest );
3950 if ( pApprove->wasSelected() )
3951 return true;
3953 catch( const Exception& )
3955 DBG_UNHANDLED_EXCEPTION("svx");
3958 return false;
3962 void SAL_CALL FormController::invalidateFeatures( const Sequence< ::sal_Int16 >& Features )
3964 ::osl::MutexGuard aGuard( m_aMutex );
3965 // for now, just copy the ids of the features, because...
3966 m_aInvalidFeatures.insert( Features.begin(), Features.end() );
3968 // ... we will do the real invalidation asynchronously
3969 if ( !m_aFeatureInvalidationTimer.IsActive() )
3970 m_aFeatureInvalidationTimer.Start();
3974 void SAL_CALL FormController::invalidateAllFeatures( )
3976 ::osl::ClearableMutexGuard aGuard( m_aMutex );
3978 Sequence< sal_Int16 > aInterceptedFeatures( comphelper::mapKeysToSequence(m_aFeatureDispatchers) );
3980 aGuard.clear();
3981 if ( aInterceptedFeatures.hasElements() )
3982 invalidateFeatures( aInterceptedFeatures );
3986 Reference< XDispatch >
3987 FormController::interceptedQueryDispatch( const URL& aURL,
3988 const OUString& /*aTargetFrameName*/, sal_Int32 /*nSearchFlags*/)
3990 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
3991 Reference< XDispatch > xReturn;
3992 // dispatches handled by ourself
3993 if ( ( aURL.Complete == FMURL_CONFIRM_DELETION )
3994 || ( ( aURL.Complete == "private:/InteractionHandler" )
3995 && ensureInteractionHandler()
3998 xReturn = static_cast< XDispatch* >( this );
4000 // dispatches of FormSlot-URLs we have to translate
4001 if ( !xReturn.is() && m_xFormOperations.is() )
4003 // find the slot id which corresponds to the URL
4004 sal_Int32 nFeatureSlotId = svx::FeatureSlotTranslation::getControllerFeatureSlotIdForURL( aURL.Main );
4005 sal_Int16 nFormFeature = ( nFeatureSlotId != -1 ) ? svx::FeatureSlotTranslation::getFormFeatureForSlotId( nFeatureSlotId ) : -1;
4006 if ( nFormFeature > 0 )
4008 // get the dispatcher for this feature, create if necessary
4009 DispatcherContainer::const_iterator aDispatcherPos = m_aFeatureDispatchers.find( nFormFeature );
4010 if ( aDispatcherPos == m_aFeatureDispatchers.end() )
4012 aDispatcherPos = m_aFeatureDispatchers.emplace(
4013 nFormFeature, new svx::OSingleFeatureDispatcher( aURL, nFormFeature, m_xFormOperations, m_aMutex )
4014 ).first;
4017 OSL_ENSURE( aDispatcherPos->second.is(), "FormController::interceptedQueryDispatch: should have a dispatcher by now!" );
4018 return aDispatcherPos->second;
4022 // no more to offer
4023 return xReturn;
4027 void SAL_CALL FormController::dispatch( const URL& _rURL, const Sequence< PropertyValue >& _rArgs )
4029 if ( _rArgs.getLength() != 1 )
4031 OSL_FAIL( "FormController::dispatch: no arguments -> no dispatch!" );
4032 return;
4035 if ( _rURL.Complete == "private:/InteractionHandler" )
4037 Reference< XInteractionRequest > xRequest;
4038 OSL_VERIFY( _rArgs[0].Value >>= xRequest );
4039 if ( xRequest.is() )
4040 handle( xRequest );
4041 return;
4044 if ( _rURL.Complete == FMURL_CONFIRM_DELETION )
4046 OSL_FAIL( "FormController::dispatch: How do you expect me to return something via this call?" );
4047 // confirmDelete has a return value - dispatch hasn't
4048 return;
4051 OSL_FAIL( "FormController::dispatch: unknown URL!" );
4055 void SAL_CALL FormController::addStatusListener( const Reference< XStatusListener >& _rxListener, const URL& _rURL )
4057 if (_rURL.Complete == FMURL_CONFIRM_DELETION)
4059 if (_rxListener.is())
4060 { // send an initial statusChanged event
4061 FeatureStateEvent aEvent;
4062 aEvent.FeatureURL = _rURL;
4063 aEvent.IsEnabled = true;
4064 _rxListener->statusChanged(aEvent);
4065 // and don't add the listener at all (the status will never change)
4068 else
4069 OSL_FAIL("FormController::addStatusListener: invalid (unsupported) URL!");
4073 Reference< XInterface > SAL_CALL FormController::getParent()
4075 return m_xParent;
4079 void SAL_CALL FormController::setParent( const Reference< XInterface >& Parent)
4081 m_xParent = Parent;
4085 void SAL_CALL FormController::removeStatusListener( const Reference< XStatusListener >& /*_rxListener*/, const URL& _rURL )
4087 OSL_ENSURE(_rURL.Complete == FMURL_CONFIRM_DELETION, "FormController::removeStatusListener: invalid (unsupported) URL!");
4088 // we never really added the listener, so we don't need to remove it
4092 Reference< XDispatchProviderInterceptor > FormController::createInterceptor(const Reference< XDispatchProviderInterception > & _xInterception)
4094 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
4095 #ifdef DBG_UTIL
4096 // check if we already have an interceptor for the given object
4097 for ( const auto & it : m_aControlDispatchInterceptors )
4099 if (it->getIntercepted() == _xInterception)
4100 OSL_FAIL("FormController::createInterceptor : we already do intercept this objects dispatches !");
4102 #endif
4104 rtl::Reference<DispatchInterceptionMultiplexer> pInterceptor(new DispatchInterceptionMultiplexer( _xInterception, this ));
4105 m_aControlDispatchInterceptors.push_back( pInterceptor );
4107 return pInterceptor;
4111 bool FormController::ensureInteractionHandler()
4113 if ( m_xInteractionHandler.is() )
4114 return true;
4115 if ( m_bAttemptedHandlerCreation )
4116 return false;
4117 m_bAttemptedHandlerCreation = true;
4119 m_xInteractionHandler = InteractionHandler::createWithParent(m_xComponentContext,
4120 getDialogParentWindow(this));
4121 return m_xInteractionHandler.is();
4125 void SAL_CALL FormController::handle( const Reference< XInteractionRequest >& _rRequest )
4127 if ( !ensureInteractionHandler() )
4128 return;
4129 m_xInteractionHandler->handle( _rRequest );
4133 void FormController::deleteInterceptor(const Reference< XDispatchProviderInterception > & _xInterception)
4135 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
4136 // search the interceptor responsible for the given object
4137 auto aIter = std::find_if(m_aControlDispatchInterceptors.begin(), m_aControlDispatchInterceptors.end(),
4138 [&_xInterception](const rtl::Reference<DispatchInterceptionMultiplexer>& rpInterceptor) {
4139 return rpInterceptor->getIntercepted() == _xInterception;
4141 if (aIter != m_aControlDispatchInterceptors.end())
4143 // log off the interception from its interception object
4144 (*aIter)->dispose();
4145 // remove the interceptor from our array
4146 m_aControlDispatchInterceptors.erase(aIter);
4151 void FormController::implInvalidateCurrentControlDependentFeatures()
4153 Sequence< sal_Int16 > aCurrentControlDependentFeatures
4155 FormFeature::SortAscending,
4156 FormFeature::SortDescending,
4157 FormFeature::AutoFilter,
4158 FormFeature::RefreshCurrentControl
4161 invalidateFeatures( aCurrentControlDependentFeatures );
4165 void SAL_CALL FormController::columnChanged( const EventObject& /*_event*/ )
4167 implInvalidateCurrentControlDependentFeatures();
4172 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */