Bump version to 6.4-15
[LibreOffice.git] / svx / source / form / formcontroller.cxx
blob0a1aa102d718dffa7e1926a2da46bcba86f07ec5
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/NoSupportException.hpp>
52 #include <com/sun/star/sdb/ParametersRequest.hpp>
53 #include <com/sun/star/sdb/RowChangeAction.hpp>
54 #include <com/sun/star/sdb/SQLFilterOperator.hpp>
55 #include <com/sun/star/sdb/XInteractionSupplyParameters.hpp>
56 #include <com/sun/star/sdbc/ColumnValue.hpp>
57 #include <com/sun/star/sdbc/DataType.hpp>
58 #include <com/sun/star/task/InteractionHandler.hpp>
59 #include <com/sun/star/util/XURLTransformer.hpp>
60 #include <com/sun/star/form/runtime/FormOperations.hpp>
61 #include <com/sun/star/form/runtime/FormFeature.hpp>
62 #include <com/sun/star/container/XContainer.hpp>
63 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
64 #include <com/sun/star/util/NumberFormatter.hpp>
65 #include <com/sun/star/sdb/SQLContext.hpp>
66 #include <com/sun/star/sdb/XColumn.hpp>
68 #include <comphelper/enumhelper.hxx>
69 #include <comphelper/interaction.hxx>
70 #include <comphelper/processfactory.hxx>
71 #include <comphelper/property.hxx>
72 #include <comphelper/sequence.hxx>
73 #include <comphelper/flagguard.hxx>
74 #include <comphelper/types.hxx>
75 #include <cppuhelper/queryinterface.hxx>
76 #include <cppuhelper/supportsservice.hxx>
77 #include <cppuhelper/typeprovider.hxx>
78 #include <connectivity/IParseContext.hxx>
79 #include <connectivity/dbtools.hxx>
80 #include <connectivity/sqlparse.hxx>
81 #include <toolkit/controls/unocontrol.hxx>
82 #include <toolkit/helper/vclunohelper.hxx>
83 #include <tools/debug.hxx>
84 #include <tools/diagnose_ex.h>
85 #include <unotools/localedatawrapper.hxx>
86 #include <vcl/svapp.hxx>
87 #include <vcl/settings.hxx>
88 #include <vcl/window.hxx>
89 #include <osl/mutex.hxx>
90 #include <sal/log.hxx>
92 #include <algorithm>
93 #include <iterator>
95 #include <o3tl/functional.hxx>
97 using namespace ::com::sun::star;
98 using namespace ::comphelper;
99 using namespace ::connectivity;
100 using namespace ::dbtools;
103 css::uno::Reference< css::uno::XInterface >
104 FormController_NewInstance_Impl( const css::uno::Reference< css::lang::XMultiServiceFactory > & _rxORB )
106 return *( new ::svxform::FormController( comphelper::getComponentContext(_rxORB) ) );
109 namespace svxform
112 using ::com::sun::star::sdb::XColumn;
113 using ::com::sun::star::awt::XControl;
114 using ::com::sun::star::awt::TabController;
115 using ::com::sun::star::awt::XToolkit;
116 using ::com::sun::star::awt::XWindowPeer;
117 using ::com::sun::star::form::XGrid;
118 using ::com::sun::star::beans::XPropertySet;
119 using ::com::sun::star::uno::UNO_SET_THROW;
120 using ::com::sun::star::uno::UNO_QUERY_THROW;
121 using ::com::sun::star::container::XIndexAccess;
122 using ::com::sun::star::uno::Exception;
123 using ::com::sun::star::uno::XInterface;
124 using ::com::sun::star::uno::UNO_QUERY;
125 using ::com::sun::star::uno::Sequence;
126 using ::com::sun::star::uno::Reference;
127 using ::com::sun::star::beans::XPropertySetInfo;
128 using ::com::sun::star::beans::PropertyValue;
129 using ::com::sun::star::uno::RuntimeException;
130 using ::com::sun::star::lang::IndexOutOfBoundsException;
131 using ::com::sun::star::sdb::XInteractionSupplyParameters;
132 using ::com::sun::star::awt::XTextComponent;
133 using ::com::sun::star::awt::XTextListener;
134 using ::com::sun::star::uno::Any;
135 using ::com::sun::star::frame::XDispatch;
136 using ::com::sun::star::lang::XMultiServiceFactory;
137 using ::com::sun::star::uno::Type;
138 using ::com::sun::star::lang::IllegalArgumentException;
139 using ::com::sun::star::sdbc::XConnection;
140 using ::com::sun::star::sdbc::XRowSet;
141 using ::com::sun::star::sdbc::XDatabaseMetaData;
142 using ::com::sun::star::util::XNumberFormatsSupplier;
143 using ::com::sun::star::util::NumberFormatter;
144 using ::com::sun::star::util::XNumberFormatter;
145 using ::com::sun::star::sdbcx::XColumnsSupplier;
146 using ::com::sun::star::container::XNameAccess;
147 using ::com::sun::star::lang::EventObject;
148 using ::com::sun::star::beans::Property;
149 using ::com::sun::star::container::XEnumeration;
150 using ::com::sun::star::form::XFormComponent;
151 using ::com::sun::star::form::runtime::XFormOperations;
152 using ::com::sun::star::form::runtime::FilterEvent;
153 using ::com::sun::star::form::runtime::XFilterControllerListener;
154 using ::com::sun::star::awt::XControlContainer;
155 using ::com::sun::star::container::XIdentifierReplace;
156 using ::com::sun::star::lang::WrappedTargetException;
157 using ::com::sun::star::form::XFormControllerListener;
158 using ::com::sun::star::awt::XWindow;
159 using ::com::sun::star::sdbc::XResultSet;
160 using ::com::sun::star::awt::XControlModel;
161 using ::com::sun::star::awt::XTabControllerModel;
162 using ::com::sun::star::beans::PropertyChangeEvent;
163 using ::com::sun::star::form::validation::XValidatableFormComponent;
164 using ::com::sun::star::form::XLoadable;
165 using ::com::sun::star::form::XBoundControl;
166 using ::com::sun::star::beans::XPropertyChangeListener;
167 using ::com::sun::star::awt::TextEvent;
168 using ::com::sun::star::form::XBoundComponent;
169 using ::com::sun::star::awt::XCheckBox;
170 using ::com::sun::star::awt::XComboBox;
171 using ::com::sun::star::awt::XListBox;
172 using ::com::sun::star::awt::ItemEvent;
173 using ::com::sun::star::util::XModifyListener;
174 using ::com::sun::star::form::XReset;
175 using ::com::sun::star::frame::XDispatchProviderInterception;
176 using ::com::sun::star::form::XGridControl;
177 using ::com::sun::star::awt::XVclWindowPeer;
178 using ::com::sun::star::form::validation::XValidator;
179 using ::com::sun::star::awt::FocusEvent;
180 using ::com::sun::star::sdb::SQLContext;
181 using ::com::sun::star::container::XChild;
182 using ::com::sun::star::form::TabulatorCycle_RECORDS;
183 using ::com::sun::star::container::ContainerEvent;
184 using ::com::sun::star::lang::DisposedException;
185 using ::com::sun::star::lang::Locale;
186 using ::com::sun::star::lang::NoSupportException;
187 using ::com::sun::star::sdb::RowChangeEvent;
188 using ::com::sun::star::frame::XStatusListener;
189 using ::com::sun::star::frame::XDispatchProviderInterceptor;
190 using ::com::sun::star::sdb::SQLErrorEvent;
191 using ::com::sun::star::form::DatabaseParameterEvent;
192 using ::com::sun::star::sdb::ParametersRequest;
193 using ::com::sun::star::task::XInteractionRequest;
194 using ::com::sun::star::util::URL;
195 using ::com::sun::star::frame::FeatureStateEvent;
196 using ::com::sun::star::form::runtime::XFormControllerContext;
197 using ::com::sun::star::task::InteractionHandler;
198 using ::com::sun::star::task::XInteractionHandler;
199 using ::com::sun::star::form::runtime::FormOperations;
200 using ::com::sun::star::container::XContainer;
201 using ::com::sun::star::sdbc::SQLWarning;
203 namespace ColumnValue = ::com::sun::star::sdbc::ColumnValue;
204 namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute;
205 namespace FocusChangeReason = ::com::sun::star::awt::FocusChangeReason;
206 namespace RowChangeAction = ::com::sun::star::sdb::RowChangeAction;
207 namespace FormFeature = ::com::sun::star::form::runtime::FormFeature;
209 struct ColumnInfo
211 // information about the column itself
212 Reference< XColumn > xColumn;
213 sal_Int32 nNullable;
214 bool bAutoIncrement;
215 bool bReadOnly;
216 OUString sName;
218 // information about the control(s) bound to this column
220 /// the first control which is bound to the given column, and which requires input
221 Reference< XControl > xFirstControlWithInputRequired;
222 /** the first grid control which contains a column which is bound to the given database column, and requires
223 input
225 Reference< XGrid > xFirstGridWithInputRequiredColumn;
226 /** if xFirstControlWithInputRequired is a grid control, then nRequiredGridColumn specifies the position
227 of the grid column which is actually bound
229 sal_Int32 nRequiredGridColumn;
231 ColumnInfo()
232 :xColumn()
233 ,nNullable( ColumnValue::NULLABLE_UNKNOWN )
234 ,bAutoIncrement( false )
235 ,bReadOnly( false )
236 ,sName()
237 ,xFirstControlWithInputRequired()
238 ,xFirstGridWithInputRequiredColumn()
239 ,nRequiredGridColumn( -1 )
244 class ColumnInfoCache
246 public:
247 explicit ColumnInfoCache( const Reference< XColumnsSupplier >& _rxColSupplier );
249 size_t getColumnCount() const { return m_aColumns.size(); }
250 const ColumnInfo& getColumnInfo( size_t _pos );
252 bool controlsInitialized() const { return m_bControlsInitialized; }
253 void initializeControls( const Sequence< Reference< XControl > >& _rControls );
254 void deinitializeControls();
256 private:
257 typedef ::std::vector< ColumnInfo > ColumnInfos;
258 ColumnInfos m_aColumns;
259 bool m_bControlsInitialized;
263 ColumnInfoCache::ColumnInfoCache( const Reference< XColumnsSupplier >& _rxColSupplier )
264 :m_aColumns()
265 ,m_bControlsInitialized( false )
269 m_aColumns.clear();
271 Reference< XIndexAccess > xColumns( _rxColSupplier->getColumns(), UNO_QUERY_THROW );
272 sal_Int32 nColumnCount = xColumns->getCount();
273 m_aColumns.reserve( nColumnCount );
275 Reference< XPropertySet > xColumnProps;
276 for ( sal_Int32 i = 0; i < nColumnCount; ++i )
278 ColumnInfo aColInfo;
279 aColInfo.xColumn.set( xColumns->getByIndex(i), UNO_QUERY_THROW );
281 xColumnProps.set( aColInfo.xColumn, UNO_QUERY_THROW );
282 OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_ISNULLABLE ) >>= aColInfo.nNullable );
283 OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_AUTOINCREMENT ) >>= aColInfo.bAutoIncrement );
284 OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_NAME ) >>= aColInfo.sName );
285 OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_ISREADONLY ) >>= aColInfo.bReadOnly );
287 m_aColumns.push_back( aColInfo );
290 catch( const Exception& )
292 DBG_UNHANDLED_EXCEPTION("svx");
297 namespace
299 bool lcl_isBoundTo( const Reference< XPropertySet >& _rxControlModel, const Reference< XInterface >& _rxNormDBField )
301 Reference< XInterface > xNormBoundField( _rxControlModel->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY );
302 return ( xNormBoundField == _rxNormDBField );
305 bool lcl_isInputRequired( const Reference< XPropertySet >& _rxControlModel )
307 bool bInputRequired = false;
308 OSL_VERIFY( _rxControlModel->getPropertyValue( FM_PROP_INPUT_REQUIRED ) >>= bInputRequired );
309 return bInputRequired;
312 void lcl_resetColumnControlInfo( ColumnInfo& _rColInfo )
314 _rColInfo.xFirstControlWithInputRequired.clear();
315 _rColInfo.xFirstGridWithInputRequiredColumn.clear();
316 _rColInfo.nRequiredGridColumn = -1;
321 void ColumnInfoCache::deinitializeControls()
323 for (auto& rCol : m_aColumns)
325 lcl_resetColumnControlInfo( rCol );
327 m_bControlsInitialized = false;
331 void ColumnInfoCache::initializeControls( const Sequence< Reference< XControl > >& _rControls )
335 // for every of our known columns, find the controls which are bound to this column
336 for (auto& rCol : m_aColumns)
338 OSL_ENSURE( !rCol.xFirstControlWithInputRequired.is() && !rCol.xFirstGridWithInputRequiredColumn.is()
339 && ( rCol.nRequiredGridColumn == -1 ), "ColumnInfoCache::initializeControls: called me twice?" );
341 lcl_resetColumnControlInfo( rCol );
343 Reference< XInterface > xNormColumn( rCol.xColumn, UNO_QUERY_THROW );
345 const Reference< XControl >* pControl( _rControls.getConstArray() );
346 const Reference< XControl >* pControlEnd( pControl + _rControls.getLength() );
347 for ( ; pControl != pControlEnd; ++pControl )
349 if ( !pControl->is() )
350 continue;
352 Reference< XPropertySet > xModel( (*pControl)->getModel(), UNO_QUERY_THROW );
353 Reference< XPropertySetInfo > xModelPSI( xModel->getPropertySetInfo(), UNO_SET_THROW );
355 // special handling for grid controls
356 Reference< XGrid > xGrid( *pControl, UNO_QUERY );
357 if ( xGrid.is() )
359 Reference< XIndexAccess > xGridColAccess( xModel, UNO_QUERY_THROW );
360 sal_Int32 gridColCount = xGridColAccess->getCount();
361 sal_Int32 gridCol = 0;
362 for ( gridCol = 0; gridCol < gridColCount; ++gridCol )
364 Reference< XPropertySet > xGridColumnModel( xGridColAccess->getByIndex( gridCol ), UNO_QUERY_THROW );
366 if ( !lcl_isBoundTo( xGridColumnModel, xNormColumn )
367 || !lcl_isInputRequired( xGridColumnModel )
369 continue; // with next grid column
371 break;
374 if ( gridCol < gridColCount )
376 // found a grid column which is bound to the given
377 rCol.xFirstGridWithInputRequiredColumn = xGrid;
378 rCol.nRequiredGridColumn = gridCol;
379 break;
382 continue; // with next control
385 if ( !xModelPSI->hasPropertyByName( FM_PROP_BOUNDFIELD )
386 || !lcl_isBoundTo( xModel, xNormColumn )
387 || !lcl_isInputRequired( xModel )
389 continue; // with next control
391 break;
394 if ( pControl == pControlEnd )
395 // did not find a control which is bound to this particular column, and for which the input is required
396 continue; // with next DB column
398 rCol.xFirstControlWithInputRequired = *pControl;
401 catch( const Exception& )
403 DBG_UNHANDLED_EXCEPTION("svx");
406 m_bControlsInitialized = true;
410 const ColumnInfo& ColumnInfoCache::getColumnInfo( size_t _pos )
412 if ( _pos >= m_aColumns.size() )
413 throw IndexOutOfBoundsException();
415 return m_aColumns[ _pos ];
418 class OParameterContinuation : public OInteraction< XInteractionSupplyParameters >
420 Sequence< PropertyValue > m_aValues;
422 public:
423 OParameterContinuation() { }
425 const Sequence< PropertyValue >& getValues() const { return m_aValues; }
427 // XInteractionSupplyParameters
428 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 class FmXAutoControl: public UnoControl
455 public:
456 FmXAutoControl() :UnoControl()
460 virtual OUString GetComponentServiceName() override {return "Edit";}
461 virtual void SAL_CALL createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) override;
463 protected:
464 virtual void ImplSetPeerProperty( const OUString& rPropName, const Any& rVal ) override;
468 void FmXAutoControl::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer )
470 UnoControl::createPeer( rxToolkit, rParentPeer );
472 Reference< XTextComponent > xText(getPeer() , UNO_QUERY);
473 if (xText.is())
475 xText->setText(SvxResId(RID_STR_AUTOFIELD));
476 xText->setEditable(false);
481 void FmXAutoControl::ImplSetPeerProperty( const OUString& rPropName, const Any& rVal )
483 // these properties are ignored
484 if (rPropName == FM_PROP_TEXT)
485 return;
487 UnoControl::ImplSetPeerProperty( rPropName, rVal );
491 IMPL_LINK_NOARG( FormController, OnActivateTabOrder, Timer*, void )
493 activateTabOrder();
497 struct UpdateAllListeners
499 bool operator()( const Reference< XDispatch >& _rxDispatcher ) const
501 static_cast< svx::OSingleFeatureDispatcher* >( _rxDispatcher.get() )->updateAllListeners();
502 // the return is a dummy only so we can use this struct in a lambda expression
503 return true;
507 IMPL_LINK_NOARG( FormController, OnInvalidateFeatures, Timer*, void )
509 ::osl::MutexGuard aGuard( m_aMutex );
510 for (const auto& rFeature : m_aInvalidFeatures)
512 DispatcherContainer::const_iterator aDispatcherPos = m_aFeatureDispatchers.find( rFeature );
513 if ( aDispatcherPos != m_aFeatureDispatchers.end() )
515 // TODO: for the real and actual listener notifications, we should release
516 // our mutex
517 UpdateAllListeners( )( aDispatcherPos->second );
522 FormController::FormController(const Reference< css::uno::XComponentContext > & _rxORB )
523 :FormController_BASE( m_aMutex )
524 ,OPropertySetHelper( FormController_BASE::rBHelper )
525 ,OSQLParserClient( _rxORB )
526 ,m_xComponentContext( _rxORB )
527 ,m_aActivateListeners(m_aMutex)
528 ,m_aModifyListeners(m_aMutex)
529 ,m_aErrorListeners(m_aMutex)
530 ,m_aDeleteListeners(m_aMutex)
531 ,m_aRowSetApproveListeners(m_aMutex)
532 ,m_aParameterListeners(m_aMutex)
533 ,m_aFilterListeners(m_aMutex)
534 ,m_xFormOperations()
535 ,m_aMode( OUString( "DataMode" ) )
536 ,m_aLoadEvent( LINK( this, FormController, OnLoad ) )
537 ,m_aToggleEvent( LINK( this, FormController, OnToggleAutoFields ) )
538 ,m_aActivationEvent( LINK( this, FormController, OnActivated ) )
539 ,m_aDeactivationEvent( LINK( this, FormController, OnDeactivated ) )
540 ,m_nCurrentFilterPosition(-1)
541 ,m_bCurrentRecordModified(false)
542 ,m_bCurrentRecordNew(false)
543 ,m_bLocked(false)
544 ,m_bDBConnection(false)
545 ,m_bCycle(false)
546 ,m_bCanInsert(false)
547 ,m_bCanUpdate(false)
548 ,m_bCommitLock(false)
549 ,m_bModified(false)
550 ,m_bControlsSorted(false)
551 ,m_bFiltering(false)
552 ,m_bAttachEvents(true)
553 ,m_bDetachEvents(true)
554 ,m_bAttemptedHandlerCreation( false )
555 ,m_bSuspendFilterTextListening( false )
558 osl_atomic_increment(&m_refCount);
560 m_xTabController = TabController::create( m_xComponentContext );
561 m_xAggregate.set( m_xTabController, UNO_QUERY_THROW );
562 m_xAggregate->setDelegator( *this );
564 osl_atomic_decrement(&m_refCount);
566 m_aTabActivationIdle.SetPriority( TaskPriority::LOWEST );
567 m_aTabActivationIdle.SetInvokeHandler( LINK( this, FormController, OnActivateTabOrder ) );
569 m_aFeatureInvalidationTimer.SetTimeout( 200 );
570 m_aFeatureInvalidationTimer.SetInvokeHandler( LINK( this, FormController, OnInvalidateFeatures ) );
574 FormController::~FormController()
577 ::osl::MutexGuard aGuard( m_aMutex );
579 m_aLoadEvent.CancelPendingCall();
580 m_aToggleEvent.CancelPendingCall();
581 m_aActivationEvent.CancelPendingCall();
582 m_aDeactivationEvent.CancelPendingCall();
584 if ( m_aTabActivationIdle.IsActive() )
585 m_aTabActivationIdle.Stop();
588 if ( m_aFeatureInvalidationTimer.IsActive() )
589 m_aFeatureInvalidationTimer.Stop();
591 disposeAllFeaturesAndDispatchers();
593 if ( m_xFormOperations.is() )
594 m_xFormOperations->dispose();
595 m_xFormOperations.clear();
597 // release of aggregation
598 if ( m_xAggregate.is() )
600 m_xAggregate->setDelegator( nullptr );
601 m_xAggregate.clear();
606 void SAL_CALL FormController::acquire() throw ()
608 FormController_BASE::acquire();
612 void SAL_CALL FormController::release() throw ()
614 FormController_BASE::release();
618 Any SAL_CALL FormController::queryInterface( const Type& _rType )
620 Any aRet = FormController_BASE::queryInterface( _rType );
621 if ( !aRet.hasValue() )
622 aRet = OPropertySetHelper::queryInterface( _rType );
623 if ( !aRet.hasValue() )
624 aRet = m_xAggregate->queryAggregation( _rType );
625 return aRet;
629 Sequence< sal_Int8 > SAL_CALL FormController::getImplementationId()
631 return css::uno::Sequence<sal_Int8>();
634 Sequence< Type > SAL_CALL FormController::getTypes( )
636 return comphelper::concatSequences(
637 FormController_BASE::getTypes(),
638 ::cppu::OPropertySetHelper::getTypes()
642 // XServiceInfo
643 sal_Bool SAL_CALL FormController::supportsService(const OUString& ServiceName)
645 return cppu::supportsService(this, ServiceName);
648 OUString SAL_CALL FormController::getImplementationName()
650 return "org.openoffice.comp.svx.FormController";
653 Sequence< OUString> SAL_CALL FormController::getSupportedServiceNames()
655 // service names which are supported only, but cannot be used to created an
656 // instance at a service factory
657 Sequence<OUString> aNonCreatableServiceNames { "com.sun.star.form.FormControllerDispatcher" };
659 // services which can be used to created an instance at a service factory
660 Sequence< OUString > aCreatableServiceNames( getSupportedServiceNames_Static() );
661 return ::comphelper::concatSequences( aCreatableServiceNames, aNonCreatableServiceNames );
665 sal_Bool SAL_CALL FormController::approveReset(const EventObject& /*rEvent*/)
667 return true;
671 void SAL_CALL FormController::resetted(const EventObject& rEvent)
673 ::osl::MutexGuard aGuard(m_aMutex);
674 if (getCurrentControl().is() && (getCurrentControl()->getModel() == rEvent.Source))
675 m_bModified = false;
679 Sequence< OUString> const & FormController::getSupportedServiceNames_Static()
681 static Sequence< OUString> const aServices
683 "com.sun.star.form.runtime.FormController",
684 "com.sun.star.awt.control.TabController"
686 return aServices;
690 namespace
692 struct ResetComponentText
694 void operator()( const Reference< XTextComponent >& _rxText )
696 _rxText->setText( OUString() );
700 struct RemoveComponentTextListener
702 explicit RemoveComponentTextListener( const Reference< XTextListener >& _rxListener )
703 :m_xListener( _rxListener )
707 void operator()( const Reference< XTextComponent >& _rxText )
709 _rxText->removeTextListener( m_xListener );
712 private:
713 Reference< XTextListener > m_xListener;
718 void FormController::impl_setTextOnAllFilter_throw()
720 m_bSuspendFilterTextListening = true;
721 ::comphelper::FlagGuard aResetFlag( m_bSuspendFilterTextListening );
723 // reset the text for all controls
724 ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), ResetComponentText() );
726 if ( m_aFilterRows.empty() )
727 // nothing to do anymore
728 return;
730 if ( m_nCurrentFilterPosition < 0 )
731 return;
733 // set the text for all filters
734 OSL_ENSURE( m_aFilterRows.size() > static_cast<size_t>(m_nCurrentFilterPosition),
735 "FormController::impl_setTextOnAllFilter_throw: m_nCurrentFilterPosition too big" );
737 if ( static_cast<size_t>(m_nCurrentFilterPosition) < m_aFilterRows.size() )
739 FmFilterRow& rRow = m_aFilterRows[ m_nCurrentFilterPosition ];
740 for (const auto& rEntry : rRow)
742 rEntry.first->setText( rEntry.second );
746 // OPropertySetHelper
748 sal_Bool FormController::convertFastPropertyValue( Any & /*rConvertedValue*/, Any & /*rOldValue*/,
749 sal_Int32 /*nHandle*/, const Any& /*rValue*/ )
751 return false;
755 void FormController::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/, const Any& /*rValue*/ )
760 void FormController::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
762 switch (nHandle)
764 case FM_ATTR_FILTER:
766 OUStringBuffer aFilter;
767 Reference<XConnection> xConnection(getConnection(Reference< XRowSet>(m_xModelAsIndex, UNO_QUERY)));
768 if (xConnection.is())
770 Reference< XNumberFormatsSupplier> xFormatSupplier( getNumberFormats( xConnection, true ) );
771 Reference< XNumberFormatter> xFormatter = NumberFormatter::create(m_xComponentContext);
772 xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
774 // now add the filter rows
777 for (const FmFilterRow& rRow : m_aFilterRows)
779 if ( rRow.empty() )
780 continue;
782 OUStringBuffer aRowFilter;
783 for ( FmFilterRow::const_iterator condition = rRow.begin(); condition != rRow.end(); ++condition )
785 // get the field of the controls map
786 Reference< XControl > xControl( condition->first, UNO_QUERY_THROW );
787 Reference< XPropertySet > xModelProps( xControl->getModel(), UNO_QUERY_THROW );
788 Reference< XPropertySet > xField( xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY_THROW );
790 OUString sFilterValue( condition->second );
792 OUString sErrorMsg, sCriteria;
793 const std::shared_ptr< OSQLParseNode > pParseNode =
794 predicateTree( sErrorMsg, sFilterValue, xFormatter, xField );
795 OSL_ENSURE( pParseNode != nullptr, "FormController::getFastPropertyValue: could not parse the field value predicate!" );
796 if ( pParseNode != nullptr )
798 // don't use a parse context here, we need it unlocalized
799 pParseNode->parseNodeToStr( sCriteria, xConnection );
800 if ( condition != rRow.begin() )
801 aRowFilter.append( " AND " );
802 aRowFilter.append( sCriteria );
805 if ( !aRowFilter.isEmpty() )
807 if ( !aFilter.isEmpty() )
808 aFilter.append( " OR " );
810 aFilter.append( "( " );
811 aFilter.append( aRowFilter.makeStringAndClear() );
812 aFilter.append( " )" );
816 catch( const Exception& )
818 DBG_UNHANDLED_EXCEPTION("svx");
819 aFilter.setLength(0);
822 rValue <<= aFilter.makeStringAndClear();
824 break;
826 case FM_ATTR_FORM_OPERATIONS:
827 rValue <<= m_xFormOperations;
828 break;
833 Reference< XPropertySetInfo > FormController::getPropertySetInfo()
835 static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
836 return xInfo;
840 void FormController::fillProperties(
841 Sequence< Property >& /* [out] */ _rProps,
842 Sequence< Property >& /* [out] */ /*_rAggregateProps*/
843 ) const
845 _rProps.realloc(2);
846 sal_Int32 nPos = 0;
847 Property* pDesc = _rProps.getArray();
849 pDesc[nPos++] = Property(FM_PROP_FILTER, FM_ATTR_FILTER,
850 cppu::UnoType<OUString>::get(),
851 PropertyAttribute::READONLY);
852 pDesc[nPos++] = Property(FM_PROP_FORM_OPERATIONS, FM_ATTR_FORM_OPERATIONS,
853 cppu::UnoType<XFormOperations>::get(),
854 PropertyAttribute::READONLY);
858 ::cppu::IPropertyArrayHelper& FormController::getInfoHelper()
860 return *getArrayHelper();
863 // XFilterController
865 void SAL_CALL FormController::addFilterControllerListener( const Reference< XFilterControllerListener >& Listener )
867 m_aFilterListeners.addInterface( Listener );
871 void SAL_CALL FormController::removeFilterControllerListener( const Reference< XFilterControllerListener >& Listener )
873 m_aFilterListeners.removeInterface( Listener );
877 ::sal_Int32 SAL_CALL FormController::getFilterComponents()
879 ::osl::MutexGuard aGuard( m_aMutex );
880 impl_checkDisposed_throw();
882 return m_aFilterComponents.size();
886 ::sal_Int32 SAL_CALL FormController::getDisjunctiveTerms()
888 ::osl::MutexGuard aGuard( m_aMutex );
889 impl_checkDisposed_throw();
891 return m_aFilterRows.size();
895 void SAL_CALL FormController::setPredicateExpression( ::sal_Int32 Component, ::sal_Int32 Term, const OUString& PredicateExpression )
897 ::osl::MutexGuard aGuard( m_aMutex );
898 impl_checkDisposed_throw();
900 if ( ( Component < 0 ) || ( Component >= getFilterComponents() ) || ( Term < 0 ) || ( Term >= getDisjunctiveTerms() ) )
901 throw IndexOutOfBoundsException( OUString(), *this );
903 Reference< XTextComponent > xText( m_aFilterComponents[ Component ] );
904 xText->setText( PredicateExpression );
906 FmFilterRow& rFilterRow = m_aFilterRows[ Term ];
907 if ( !PredicateExpression.isEmpty() )
908 rFilterRow[ xText ] = PredicateExpression;
909 else
910 rFilterRow.erase( xText );
914 Reference< XControl > FormController::getFilterComponent( ::sal_Int32 Component )
916 ::osl::MutexGuard aGuard( m_aMutex );
917 impl_checkDisposed_throw();
919 if ( ( Component < 0 ) || ( Component >= getFilterComponents() ) )
920 throw IndexOutOfBoundsException( OUString(), *this );
922 return Reference< XControl >( m_aFilterComponents[ Component ], UNO_QUERY );
926 Sequence< Sequence< OUString > > FormController::getPredicateExpressions()
928 ::osl::MutexGuard aGuard( m_aMutex );
929 impl_checkDisposed_throw();
931 Sequence< Sequence< OUString > > aExpressions( m_aFilterRows.size() );
932 sal_Int32 termIndex = 0;
933 for (const FmFilterRow& rRow : m_aFilterRows)
935 Sequence< OUString > aConjunction( m_aFilterComponents.size() );
936 sal_Int32 componentIndex = 0;
937 for (const auto& rComp : m_aFilterComponents)
939 FmFilterRow::const_iterator predicate = rRow.find( rComp );
940 if ( predicate != rRow.end() )
941 aConjunction[ componentIndex ] = predicate->second;
942 ++componentIndex;
945 aExpressions[ termIndex ] = aConjunction;
946 ++termIndex;
949 return aExpressions;
953 void SAL_CALL FormController::removeDisjunctiveTerm( ::sal_Int32 Term )
955 // SYNCHRONIZED -->
956 ::osl::ClearableMutexGuard aGuard( m_aMutex );
957 impl_checkDisposed_throw();
959 if ( ( Term < 0 ) || ( Term >= getDisjunctiveTerms() ) )
960 throw IndexOutOfBoundsException( OUString(), *this );
962 // if the to-be-deleted row is our current row, we need to shift
963 if ( Term == m_nCurrentFilterPosition )
965 if ( m_nCurrentFilterPosition < sal_Int32( m_aFilterRows.size() - 1 ) )
966 ++m_nCurrentFilterPosition;
967 else
968 --m_nCurrentFilterPosition;
971 FmFilterRows::iterator pos = m_aFilterRows.begin() + Term;
972 m_aFilterRows.erase( pos );
974 // adjust m_nCurrentFilterPosition if the removed row preceded it
975 if ( Term < m_nCurrentFilterPosition )
976 --m_nCurrentFilterPosition;
978 SAL_WARN_IF( !( ( m_nCurrentFilterPosition < 0 ) != ( m_aFilterRows.empty() ) ),
979 "svx.form", "FormController::removeDisjunctiveTerm: inconsistency!" );
981 // update the texts in the filter controls
982 impl_setTextOnAllFilter_throw();
984 FilterEvent aEvent;
985 aEvent.Source = *this;
986 aEvent.DisjunctiveTerm = Term;
987 aGuard.clear();
988 // <-- SYNCHRONIZED
990 m_aFilterListeners.notifyEach( &XFilterControllerListener::disjunctiveTermRemoved, aEvent );
994 void SAL_CALL FormController::appendEmptyDisjunctiveTerm()
996 // SYNCHRONIZED -->
997 ::osl::ClearableMutexGuard aGuard( m_aMutex );
998 impl_checkDisposed_throw();
1000 impl_appendEmptyFilterRow( aGuard );
1001 // <-- SYNCHRONIZED
1005 ::sal_Int32 SAL_CALL FormController::getActiveTerm()
1007 ::osl::MutexGuard aGuard( m_aMutex );
1008 impl_checkDisposed_throw();
1010 return m_nCurrentFilterPosition;
1014 void SAL_CALL FormController::setActiveTerm( ::sal_Int32 ActiveTerm )
1016 ::osl::MutexGuard aGuard( m_aMutex );
1017 impl_checkDisposed_throw();
1019 if ( ( ActiveTerm < 0 ) || ( ActiveTerm >= getDisjunctiveTerms() ) )
1020 throw IndexOutOfBoundsException( OUString(), *this );
1022 if ( ActiveTerm == getActiveTerm() )
1023 return;
1025 m_nCurrentFilterPosition = ActiveTerm;
1026 impl_setTextOnAllFilter_throw();
1029 // XElementAccess
1031 sal_Bool SAL_CALL FormController::hasElements()
1033 ::osl::MutexGuard aGuard( m_aMutex );
1034 return !m_aChildren.empty();
1038 Type SAL_CALL FormController::getElementType()
1040 return cppu::UnoType<XFormController>::get();
1044 // XEnumerationAccess
1046 Reference< XEnumeration > SAL_CALL FormController::createEnumeration()
1048 ::osl::MutexGuard aGuard( m_aMutex );
1049 return new ::comphelper::OEnumerationByIndex(this);
1052 // XIndexAccess
1054 sal_Int32 SAL_CALL FormController::getCount()
1056 ::osl::MutexGuard aGuard( m_aMutex );
1057 return m_aChildren.size();
1061 Any SAL_CALL FormController::getByIndex(sal_Int32 Index)
1063 ::osl::MutexGuard aGuard( m_aMutex );
1064 if (Index < 0 ||
1065 Index >= static_cast<sal_Int32>(m_aChildren.size()))
1066 throw IndexOutOfBoundsException();
1068 return makeAny( m_aChildren[ Index ] );
1071 // EventListener
1073 void SAL_CALL FormController::disposing(const EventObject& e)
1075 // has the container been disposed
1076 ::osl::MutexGuard aGuard( m_aMutex );
1077 Reference< XControlContainer > xContainer(e.Source, UNO_QUERY);
1078 if (xContainer.is())
1080 setContainer(Reference< XControlContainer > ());
1082 else
1084 // has a control been disposed
1085 Reference< XControl > xControl(e.Source, UNO_QUERY);
1086 if (xControl.is())
1088 if (getContainer().is())
1089 removeControl(xControl);
1094 // OComponentHelper
1096 void FormController::disposeAllFeaturesAndDispatchers()
1098 for (auto& rDispatcher : m_aFeatureDispatchers)
1102 ::comphelper::disposeComponent( rDispatcher.second );
1104 catch( const Exception& )
1106 DBG_UNHANDLED_EXCEPTION("svx");
1109 m_aFeatureDispatchers.clear();
1113 void FormController::disposing()
1115 EventObject aEvt( *this );
1117 // if we're still active, simulate a "deactivated" event
1118 if ( m_xActiveControl.is() )
1119 m_aActivateListeners.notifyEach( &XFormControllerListener::formDeactivated, aEvt );
1121 // notify all our listeners
1122 m_aActivateListeners.disposeAndClear(aEvt);
1123 m_aModifyListeners.disposeAndClear(aEvt);
1124 m_aErrorListeners.disposeAndClear(aEvt);
1125 m_aDeleteListeners.disposeAndClear(aEvt);
1126 m_aRowSetApproveListeners.disposeAndClear(aEvt);
1127 m_aParameterListeners.disposeAndClear(aEvt);
1128 m_aFilterListeners.disposeAndClear(aEvt);
1130 removeBoundFieldListener();
1131 stopFiltering();
1133 m_aControlBorderManager.restoreAll();
1135 m_aFilterRows.clear();
1137 ::osl::MutexGuard aGuard( m_aMutex );
1138 m_xActiveControl = nullptr;
1139 implSetCurrentControl( nullptr );
1141 // clean up our children
1142 for (const auto& rpChild : m_aChildren)
1144 // search the position of the model within the form
1145 Reference< XFormComponent > xForm(rpChild->getModel(), UNO_QUERY);
1146 sal_uInt32 nPos = m_xModelAsIndex->getCount();
1147 Reference< XFormComponent > xTemp;
1148 for( ; nPos; )
1151 m_xModelAsIndex->getByIndex( --nPos ) >>= xTemp;
1152 if ( xForm.get() == xTemp.get() )
1154 Reference< XInterface > xIfc( rpChild, UNO_QUERY );
1155 m_xModelAsManager->detach( nPos, xIfc );
1156 break;
1160 Reference< XComponent > (rpChild, UNO_QUERY_THROW)->dispose();
1162 m_aChildren.clear();
1164 disposeAllFeaturesAndDispatchers();
1166 if ( m_xFormOperations.is() )
1167 m_xFormOperations->dispose();
1168 m_xFormOperations.clear();
1170 if (m_bDBConnection)
1171 unload();
1173 setContainer( nullptr );
1174 setModel( nullptr );
1175 setParent( nullptr );
1177 ::comphelper::disposeComponent( m_xComposer );
1179 m_bDBConnection = false;
1183 namespace
1185 bool lcl_shouldUseDynamicControlBorder( const Reference< XInterface >& _rxForm, const Any& _rDynamicColorProp )
1187 bool bDoUse = false;
1188 if ( !( _rDynamicColorProp >>= bDoUse ) )
1190 DocumentType eDocType = DocumentClassification::classifyHostDocument( _rxForm );
1191 return ControlLayouter::useDynamicBorderColor( eDocType );
1193 return bDoUse;
1198 void SAL_CALL FormController::propertyChange(const PropertyChangeEvent& evt)
1200 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1201 if ( evt.PropertyName == FM_PROP_BOUNDFIELD )
1203 Reference<XPropertySet> xOldBound;
1204 evt.OldValue >>= xOldBound;
1205 if ( !xOldBound.is() && evt.NewValue.hasValue() )
1207 Reference< XControlModel > xControlModel(evt.Source,UNO_QUERY);
1208 Reference< XControl > xControl = findControl(m_aControls,xControlModel,false,false);
1209 if ( xControl.is() )
1211 startControlModifyListening( xControl );
1212 Reference<XPropertySet> xProp(xControlModel,UNO_QUERY);
1213 if ( xProp.is() )
1214 xProp->removePropertyChangeListener(FM_PROP_BOUNDFIELD, this);
1218 else
1220 bool bModifiedChanged = (evt.PropertyName == FM_PROP_ISMODIFIED);
1221 bool bNewChanged = (evt.PropertyName == FM_PROP_ISNEW);
1222 if (bModifiedChanged || bNewChanged)
1224 ::osl::MutexGuard aGuard( m_aMutex );
1225 if (bModifiedChanged)
1226 m_bCurrentRecordModified = ::comphelper::getBOOL(evt.NewValue);
1227 else
1228 m_bCurrentRecordNew = ::comphelper::getBOOL(evt.NewValue);
1230 // toggle the locking
1231 if (m_bLocked != determineLockState())
1233 m_bLocked = !m_bLocked;
1234 setLocks();
1235 if (isListeningForChanges())
1236 startListening();
1237 else
1238 stopListening();
1241 if ( bNewChanged )
1242 m_aToggleEvent.Call();
1244 if (!m_bCurrentRecordModified)
1245 m_bModified = false;
1247 else if ( evt.PropertyName == FM_PROP_DYNAMIC_CONTROL_BORDER )
1249 bool bEnable = lcl_shouldUseDynamicControlBorder( evt.Source, evt.NewValue );
1250 if ( bEnable )
1252 m_aControlBorderManager.enableDynamicBorderColor();
1253 if ( m_xActiveControl.is() )
1254 m_aControlBorderManager.focusGained( m_xActiveControl.get() );
1256 else
1258 m_aControlBorderManager.disableDynamicBorderColor();
1265 bool FormController::replaceControl( const Reference< XControl >& _rxExistentControl, const Reference< XControl >& _rxNewControl )
1267 bool bSuccess = false;
1270 Reference< XIdentifierReplace > xContainer( getContainer(), UNO_QUERY );
1271 DBG_ASSERT( xContainer.is(), "FormController::replaceControl: yes, it's not required by the service description, but XIdentifierReplace would be nice!" );
1272 if ( xContainer.is() )
1274 // look up the ID of _rxExistentControl
1275 Sequence< sal_Int32 > aIdentifiers( xContainer->getIdentifiers() );
1276 const sal_Int32* pIdentifiers = std::find_if(aIdentifiers.begin(), aIdentifiers.end(),
1277 [&xContainer, &_rxExistentControl](const sal_Int32 nId) {
1278 Reference< XControl > xCheck( xContainer->getByIdentifier( nId ), UNO_QUERY );
1279 return xCheck == _rxExistentControl;
1281 DBG_ASSERT( pIdentifiers != aIdentifiers.end(), "FormController::replaceControl: did not find the control in the container!" );
1282 if ( pIdentifiers != aIdentifiers.end() )
1284 bool bReplacedWasActive = ( m_xActiveControl.get() == _rxExistentControl.get() );
1285 bool bReplacedWasCurrent = ( m_xCurrentControl.get() == _rxExistentControl.get() );
1287 if ( bReplacedWasActive )
1289 m_xActiveControl = nullptr;
1290 implSetCurrentControl( nullptr );
1292 else if ( bReplacedWasCurrent )
1294 implSetCurrentControl( _rxNewControl );
1297 // carry over the model
1298 _rxNewControl->setModel( _rxExistentControl->getModel() );
1300 xContainer->replaceByIdentifer( *pIdentifiers, makeAny( _rxNewControl ) );
1301 bSuccess = true;
1303 if ( bReplacedWasActive )
1305 Reference< XWindow > xControlWindow( _rxNewControl, UNO_QUERY );
1306 if ( xControlWindow.is() )
1307 xControlWindow->setFocus();
1312 catch( const Exception& )
1314 DBG_UNHANDLED_EXCEPTION("svx");
1317 Reference< XControl > xDisposeIt( bSuccess ? _rxExistentControl : _rxNewControl );
1318 ::comphelper::disposeComponent( xDisposeIt );
1319 return bSuccess;
1323 void FormController::toggleAutoFields(bool bAutoFields)
1325 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1328 Sequence< Reference< XControl > > aControlsCopy( m_aControls );
1329 const Reference< XControl >* pControls = aControlsCopy.getConstArray();
1330 sal_Int32 nControls = aControlsCopy.getLength();
1332 if (bAutoFields)
1334 // as we don't want new controls to be attached to the scripting environment
1335 // we change attach flags
1336 m_bAttachEvents = false;
1337 for (sal_Int32 i = nControls; i > 0;)
1339 Reference< XControl > xControl = pControls[--i];
1340 if (xControl.is())
1342 Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
1343 if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
1345 // does the model use a bound field ?
1346 Reference< XPropertySet > xField;
1347 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
1349 // is it an autofield?
1350 if ( xField.is()
1351 && ::comphelper::hasProperty( FM_PROP_AUTOINCREMENT, xField )
1352 && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_AUTOINCREMENT ) )
1355 replaceControl( xControl, new FmXAutoControl() );
1360 m_bAttachEvents = true;
1362 else
1364 m_bDetachEvents = false;
1365 for (sal_Int32 i = nControls; i > 0;)
1367 Reference< XControl > xControl = pControls[--i];
1368 if (xControl.is())
1370 Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
1371 if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
1373 // does the model use a bound field ?
1374 Reference< XPropertySet > xField;
1375 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
1377 // is it an autofield?
1378 if ( xField.is()
1379 && ::comphelper::hasProperty( FM_PROP_AUTOINCREMENT, xField )
1380 && ::comphelper::getBOOL( xField->getPropertyValue(FM_PROP_AUTOINCREMENT ) )
1383 OUString sServiceName;
1384 OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DEFAULTCONTROL ) >>= sServiceName );
1385 Reference< XControl > xNewControl( m_xComponentContext->getServiceManager()->createInstanceWithContext( sServiceName, m_xComponentContext ), UNO_QUERY );
1386 replaceControl( xControl, xNewControl );
1391 m_bDetachEvents = true;
1396 IMPL_LINK_NOARG(FormController, OnToggleAutoFields, void*, void)
1398 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1400 toggleAutoFields(m_bCurrentRecordNew);
1403 // XTextListener
1405 void SAL_CALL FormController::textChanged(const TextEvent& e)
1407 // SYNCHRONIZED -->
1408 ::osl::ClearableMutexGuard aGuard( m_aMutex );
1409 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1410 if ( !m_bFiltering )
1412 impl_onModify();
1413 return;
1416 if ( m_bSuspendFilterTextListening )
1417 return;
1419 Reference< XTextComponent > xText(e.Source,UNO_QUERY);
1420 OUString aText = xText->getText();
1422 if ( m_aFilterRows.empty() )
1423 appendEmptyDisjunctiveTerm();
1425 // find the current row
1426 if ( ( static_cast<size_t>(m_nCurrentFilterPosition) >= m_aFilterRows.size() ) || ( m_nCurrentFilterPosition < 0 ) )
1428 OSL_ENSURE( false, "FormController::textChanged: m_nCurrentFilterPosition is wrong!" );
1429 return;
1432 FmFilterRow& rRow = m_aFilterRows[ m_nCurrentFilterPosition ];
1434 // do we have a new filter
1435 if (!aText.isEmpty())
1436 rRow[xText] = aText;
1437 else
1439 // do we have the control in the row
1440 FmFilterRow::iterator iter = rRow.find(xText);
1441 // erase the entry out of the row
1442 if (iter != rRow.end())
1443 rRow.erase(iter);
1446 // multiplex the event to our FilterControllerListeners
1447 FilterEvent aEvent;
1448 aEvent.Source = *this;
1449 aEvent.FilterComponent = ::std::find( m_aFilterComponents.begin(), m_aFilterComponents.end(), xText ) - m_aFilterComponents.begin();
1450 aEvent.DisjunctiveTerm = getActiveTerm();
1451 aEvent.PredicateExpression = aText;
1453 aGuard.clear();
1454 // <-- SYNCHRONIZED
1456 // notify the changed filter expression
1457 m_aFilterListeners.notifyEach( &XFilterControllerListener::predicateExpressionChanged, aEvent );
1460 // XItemListener
1462 void SAL_CALL FormController::itemStateChanged(const ItemEvent& /*rEvent*/)
1464 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1465 impl_onModify();
1468 // XModificationBroadcaster
1470 void SAL_CALL FormController::addModifyListener(const Reference< XModifyListener > & l)
1472 ::osl::MutexGuard aGuard( m_aMutex );
1473 impl_checkDisposed_throw();
1474 m_aModifyListeners.addInterface( l );
1478 void FormController::removeModifyListener(const Reference< XModifyListener > & l)
1480 ::osl::MutexGuard aGuard( m_aMutex );
1481 impl_checkDisposed_throw();
1482 m_aModifyListeners.removeInterface( l );
1485 // XModificationListener
1487 void FormController::modified( const EventObject& _rEvent )
1489 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1493 if ( _rEvent.Source != m_xActiveControl )
1494 { // let this control grab the focus
1495 // (this case may happen if somebody moves the scroll wheel of the mouse over a control
1496 // which does not have the focus)
1497 // 85511 - 29.05.2001 - frank.schoenheit@germany.sun.com
1499 // also, it happens when an image control gets a new image by double-clicking it
1500 // #i88458# / 2009-01-12 / frank.schoenheit@sun.com
1501 Reference< XWindow > xControlWindow( _rEvent.Source, UNO_QUERY_THROW );
1502 xControlWindow->setFocus();
1505 catch( const Exception& )
1507 DBG_UNHANDLED_EXCEPTION("svx");
1510 impl_onModify();
1514 void FormController::impl_checkDisposed_throw() const
1516 if ( impl_isDisposed_nofail() )
1517 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 );
1536 void FormController::impl_addFilterRow( const FmFilterRow& _row )
1538 m_aFilterRows.push_back( _row );
1540 if ( m_aFilterRows.size() == 1 )
1541 { // that's the first row ever
1542 OSL_ENSURE( m_nCurrentFilterPosition == -1, "FormController::impl_addFilterRow: inconsistency!" );
1543 m_nCurrentFilterPosition = 0;
1548 void FormController::impl_appendEmptyFilterRow( ::osl::ClearableMutexGuard& _rClearBeforeNotify )
1550 // SYNCHRONIZED -->
1551 impl_addFilterRow( FmFilterRow() );
1553 // notify the listeners
1554 FilterEvent aEvent;
1555 aEvent.Source = *this;
1556 aEvent.DisjunctiveTerm = static_cast<sal_Int32>(m_aFilterRows.size()) - 1;
1557 _rClearBeforeNotify.clear();
1558 // <-- SYNCHRONIZED
1559 m_aFilterListeners.notifyEach( &XFilterControllerListener::disjunctiveTermAdded, aEvent );
1563 bool FormController::determineLockState() const
1565 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1566 // a.) in filter mode we are always locked
1567 // b.) if we have no valid model or our model (a result set) is not alive -> we're locked
1568 // c.) if we are inserting everything is OK and we are not locked
1569 // d.) if are not updatable or on invalid position
1570 Reference< XResultSet > xResultSet(m_xModelAsIndex, UNO_QUERY);
1571 if (m_bFiltering || !xResultSet.is() || !isRowSetAlive(xResultSet))
1572 return true;
1573 else
1574 return !(m_bCanInsert && m_bCurrentRecordNew)
1575 && (xResultSet->isBeforeFirst() || xResultSet->isAfterLast() || xResultSet->rowDeleted() || !m_bCanUpdate);
1578 // FocusListener
1580 void FormController::focusGained(const FocusEvent& e)
1582 // SYNCHRONIZED -->
1583 ::osl::ClearableMutexGuard aGuard( m_aMutex );
1584 impl_checkDisposed_throw();
1586 m_aControlBorderManager.focusGained( e.Source );
1588 Reference< XControl > xControl(e.Source, UNO_QUERY);
1589 if (m_bDBConnection)
1591 // do we need to keep the locking of the commit
1592 // we hold the lock as long as the control differs from the current
1593 // otherwise we disabled the lock
1594 m_bCommitLock = m_bCommitLock && xControl.get() != m_xCurrentControl.get();
1595 if (m_bCommitLock)
1596 return;
1598 // when do we have to commit a value to form or a filter
1599 // a.) if the current value is modified
1600 // b.) there must be a current control
1601 // c.) and it must be different from the new focus owning control or
1602 // d.) the focus is moving around (so we have only one control)
1604 if ( ( m_bModified || m_bFiltering )
1605 && m_xCurrentControl.is()
1606 && ( ( xControl.get() != m_xCurrentControl.get() )
1607 || ( ( e.FocusFlags & FocusChangeReason::AROUND )
1608 && ( m_bCycle || m_bFiltering )
1613 // check the old control if the content is ok
1614 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
1615 Reference< XBoundControl > xLockingTest(m_xCurrentControl, UNO_QUERY);
1616 bool bControlIsLocked = xLockingTest.is() && xLockingTest->getLock();
1617 assert(!bControlIsLocked && "FormController::Gained: I'm modified and the current control is locked ? How this ?");
1618 // normally, a locked control should not be modified, so probably my bModified must
1619 // have been set from a different context, which I would not understand ...
1620 #endif
1621 DBG_ASSERT(m_xCurrentControl.is(), "no CurrentControl set");
1622 // first the control ask if it supports the IFace
1623 Reference< XBoundComponent > xBound(m_xCurrentControl, UNO_QUERY);
1624 if (!xBound.is() && m_xCurrentControl.is())
1625 xBound.set(m_xCurrentControl->getModel(), UNO_QUERY);
1627 // lock if we lose the focus during commit
1628 m_bCommitLock = true;
1630 // commit unsuccessful, reset focus
1631 if (xBound.is() && !xBound->commit())
1633 // the commit failed and we don't commit again until the current control
1634 // which couldn't be commit gains the focus again
1635 Reference< XWindow > xWindow(m_xCurrentControl, UNO_QUERY);
1636 if (xWindow.is())
1637 xWindow->setFocus();
1638 return;
1640 else
1642 m_bModified = false;
1643 m_bCommitLock = false;
1647 if (!m_bFiltering && m_bCycle && (e.FocusFlags & FocusChangeReason::AROUND) && m_xCurrentControl.is())
1649 SQLErrorEvent aErrorEvent;
1650 OSL_ENSURE( m_xFormOperations.is(), "FormController::focusGained: hmm?" );
1651 // should have been created in setModel
1654 if ( e.FocusFlags & FocusChangeReason::FORWARD )
1656 if ( m_xFormOperations.is() && m_xFormOperations->isEnabled( FormFeature::MoveToNext ) )
1657 m_xFormOperations->execute( FormFeature::MoveToNext );
1659 else // backward
1661 if ( m_xFormOperations.is() && m_xFormOperations->isEnabled( FormFeature::MoveToPrevious ) )
1662 m_xFormOperations->execute( FormFeature::MoveToPrevious );
1665 catch ( const Exception& )
1667 // don't handle this any further. That's an ... admissible error.
1668 DBG_UNHANDLED_EXCEPTION("svx");
1673 // still one and the same control
1674 if ( ( m_xActiveControl == xControl )
1675 && ( xControl == m_xCurrentControl )
1678 DBG_ASSERT(m_xCurrentControl.is(), "No CurrentControl selected");
1679 return;
1682 bool bActivated = !m_xActiveControl.is() && xControl.is();
1684 m_xActiveControl = xControl;
1686 implSetCurrentControl( xControl );
1687 SAL_WARN_IF( !m_xCurrentControl.is(), "svx.form", "implSetCurrentControl did nonsense!" );
1689 if ( bActivated )
1691 // (asynchronously) call activation handlers
1692 m_aActivationEvent.Call();
1694 // call modify listeners
1695 if ( m_bModified )
1696 m_aModifyListeners.notifyEach( &XModifyListener::modified, EventObject( *this ) );
1699 // invalidate all features which depend on the currently focused control
1700 if ( m_bDBConnection && !m_bFiltering )
1701 implInvalidateCurrentControlDependentFeatures();
1703 if ( !m_xCurrentControl.is() )
1704 return;
1706 // control gets focus, then possibly in the visible range
1707 Reference< XFormControllerContext > xContext( m_xFormControllerContext );
1708 Reference< XControl > xCurrentControl( m_xCurrentControl );
1709 aGuard.clear();
1710 // <-- SYNCHRONIZED
1712 if ( xContext.is() )
1713 xContext->makeVisible( xCurrentControl );
1717 IMPL_LINK_NOARG( FormController, OnActivated, void*, void )
1719 EventObject aEvent;
1720 aEvent.Source = *this;
1721 m_aActivateListeners.notifyEach( &XFormControllerListener::formActivated, aEvent );
1725 IMPL_LINK_NOARG( FormController, OnDeactivated, void*, void )
1727 EventObject aEvent;
1728 aEvent.Source = *this;
1729 m_aActivateListeners.notifyEach( &XFormControllerListener::formDeactivated, aEvent );
1733 void FormController::focusLost(const FocusEvent& e)
1735 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1737 m_aControlBorderManager.focusLost( e.Source );
1739 Reference< XWindowPeer > xNext(e.NextFocus, UNO_QUERY);
1740 Reference< XControl > xNextControl = isInList(xNext);
1741 if (!xNextControl.is())
1743 m_xActiveControl = nullptr;
1744 m_aDeactivationEvent.Call();
1749 void SAL_CALL FormController::mousePressed( const awt::MouseEvent& /*_rEvent*/ )
1751 // not interested in
1755 void SAL_CALL FormController::mouseReleased( const awt::MouseEvent& /*_rEvent*/ )
1757 // not interested in
1761 void SAL_CALL FormController::mouseEntered( const awt::MouseEvent& _rEvent )
1763 m_aControlBorderManager.mouseEntered( _rEvent.Source );
1767 void SAL_CALL FormController::mouseExited( const awt::MouseEvent& _rEvent )
1769 m_aControlBorderManager.mouseExited( _rEvent.Source );
1773 void SAL_CALL FormController::componentValidityChanged( const EventObject& _rSource )
1775 Reference< XControl > xControl( findControl( m_aControls, Reference< XControlModel >( _rSource.Source, UNO_QUERY ), false, false ) );
1776 Reference< XValidatableFormComponent > xValidatable( _rSource.Source, UNO_QUERY );
1778 OSL_ENSURE( xControl.is() && xValidatable.is(), "FormController::componentValidityChanged: huh?" );
1780 if ( xControl.is() && xValidatable.is() )
1781 m_aControlBorderManager.validityChanged( xControl, xValidatable );
1785 void FormController::setModel(const Reference< XTabControllerModel > & Model)
1787 ::osl::MutexGuard aGuard( m_aMutex );
1788 impl_checkDisposed_throw();
1790 DBG_ASSERT(m_xTabController.is(), "FormController::setModel : invalid aggregate !");
1794 // disconnect from the old model
1795 if (m_xModelAsIndex.is())
1797 if (m_bDBConnection)
1799 // we are currently working on the model
1800 EventObject aEvt(m_xModelAsIndex);
1801 unloaded(aEvt);
1804 Reference< XLoadable > xForm(m_xModelAsIndex, UNO_QUERY);
1805 if (xForm.is())
1806 xForm->removeLoadListener(this);
1808 Reference< XSQLErrorBroadcaster > xBroadcaster(m_xModelAsIndex, UNO_QUERY);
1809 if (xBroadcaster.is())
1810 xBroadcaster->removeSQLErrorListener(this);
1812 Reference< XDatabaseParameterBroadcaster > xParamBroadcaster(m_xModelAsIndex, UNO_QUERY);
1813 if (xParamBroadcaster.is())
1814 xParamBroadcaster->removeParameterListener(this);
1818 disposeAllFeaturesAndDispatchers();
1820 if ( m_xFormOperations.is() )
1821 m_xFormOperations->dispose();
1822 m_xFormOperations.clear();
1824 // set the new model wait for the load event
1825 if (m_xTabController.is())
1826 m_xTabController->setModel(Model);
1827 m_xModelAsIndex.set(Model, UNO_QUERY);
1828 m_xModelAsManager.set(Model, UNO_QUERY);
1830 // only if both ifaces exit, the controller will work successful
1831 if (!m_xModelAsIndex.is() || !m_xModelAsManager.is())
1833 m_xModelAsManager = nullptr;
1834 m_xModelAsIndex = nullptr;
1837 if (m_xModelAsIndex.is())
1839 // re-create m_xFormOperations
1840 m_xFormOperations = FormOperations::createWithFormController( m_xComponentContext, this );
1841 m_xFormOperations->setFeatureInvalidation( this );
1843 // adding load and ui interaction listeners
1844 Reference< XLoadable > xForm(Model, UNO_QUERY);
1845 if (xForm.is())
1846 xForm->addLoadListener(this);
1848 Reference< XSQLErrorBroadcaster > xBroadcaster(Model, UNO_QUERY);
1849 if (xBroadcaster.is())
1850 xBroadcaster->addSQLErrorListener(this);
1852 Reference< XDatabaseParameterBroadcaster > xParamBroadcaster(Model, UNO_QUERY);
1853 if (xParamBroadcaster.is())
1854 xParamBroadcaster->addParameterListener(this);
1856 // well, is the database already loaded?
1857 // then we have to simulate a load event
1858 Reference< XLoadable > xCursor(m_xModelAsIndex, UNO_QUERY);
1859 if (xCursor.is() && xCursor->isLoaded())
1861 EventObject aEvt(xCursor);
1862 loaded(aEvt);
1865 Reference< XPropertySet > xModelProps( m_xModelAsIndex, UNO_QUERY );
1866 Reference< XPropertySetInfo > xPropInfo( xModelProps->getPropertySetInfo() );
1867 if ( xPropInfo.is()
1868 && xPropInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER )
1869 && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_FOCUS )
1870 && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_MOUSE )
1871 && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_INVALID )
1874 bool bEnableDynamicControlBorder = lcl_shouldUseDynamicControlBorder(
1875 xModelProps.get(), xModelProps->getPropertyValue( FM_PROP_DYNAMIC_CONTROL_BORDER ) );
1876 if ( bEnableDynamicControlBorder )
1877 m_aControlBorderManager.enableDynamicBorderColor();
1878 else
1879 m_aControlBorderManager.disableDynamicBorderColor();
1881 Color nColor;
1882 if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_FOCUS ) >>= nColor )
1883 m_aControlBorderManager.setStatusColor( ControlStatus::Focused, nColor );
1884 if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_MOUSE ) >>= nColor )
1885 m_aControlBorderManager.setStatusColor( ControlStatus::MouseHover, nColor );
1886 if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_INVALID ) >>= nColor )
1887 m_aControlBorderManager.setStatusColor( ControlStatus::Invalid, nColor );
1891 catch( const Exception& )
1893 DBG_UNHANDLED_EXCEPTION("svx");
1898 Reference< XTabControllerModel > FormController::getModel()
1900 ::osl::MutexGuard aGuard( m_aMutex );
1901 impl_checkDisposed_throw();
1903 DBG_ASSERT(m_xTabController.is(), "FormController::getModel : invalid aggregate !");
1904 if (!m_xTabController.is())
1905 return Reference< XTabControllerModel > ();
1906 return m_xTabController->getModel();
1910 void FormController::addToEventAttacher(const Reference< XControl > & xControl)
1912 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1913 OSL_ENSURE( xControl.is(), "FormController::addToEventAttacher: invalid control - how did you reach this?" );
1914 if ( !xControl.is() )
1915 return; /* throw IllegalArgumentException(); */
1917 // register at the event attacher
1918 Reference< XFormComponent > xComp(xControl->getModel(), UNO_QUERY);
1919 if (xComp.is() && m_xModelAsIndex.is())
1921 // and look for the position of the ControlModel in it
1922 sal_uInt32 nPos = m_xModelAsIndex->getCount();
1923 Reference< XFormComponent > xTemp;
1924 for( ; nPos; )
1926 m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
1927 if (xComp.get() == xTemp.get())
1929 m_xModelAsManager->attach( nPos, Reference<XInterface>( xControl, UNO_QUERY ), makeAny(xControl) );
1930 break;
1937 void FormController::removeFromEventAttacher(const Reference< XControl > & xControl)
1939 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1940 OSL_ENSURE( xControl.is(), "FormController::removeFromEventAttacher: invalid control - how did you reach this?" );
1941 if ( !xControl.is() )
1942 return; /* throw IllegalArgumentException(); */
1944 // register at the event attacher
1945 Reference< XFormComponent > xComp(xControl->getModel(), UNO_QUERY);
1946 if ( xComp.is() && m_xModelAsIndex.is() )
1948 // and look for the position of the ControlModel in it
1949 sal_uInt32 nPos = m_xModelAsIndex->getCount();
1950 Reference< XFormComponent > xTemp;
1951 for( ; nPos; )
1953 m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
1954 if (xComp.get() == xTemp.get())
1956 m_xModelAsManager->detach( nPos, Reference<XInterface>( xControl, UNO_QUERY ) );
1957 break;
1964 void FormController::setContainer(const Reference< XControlContainer > & xContainer)
1966 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1967 Reference< XTabControllerModel > xTabModel(getModel());
1968 DBG_ASSERT(xTabModel.is() || !xContainer.is(), "No Model defined");
1969 // if we have a new container we need a model
1970 DBG_ASSERT(m_xTabController.is(), "FormController::setContainer : invalid aggregate !");
1972 ::osl::MutexGuard aGuard( m_aMutex );
1973 Reference< XContainer > xCurrentContainer;
1974 if (m_xTabController.is())
1975 xCurrentContainer.set(m_xTabController->getContainer(), UNO_QUERY);
1976 if (xCurrentContainer.is())
1978 xCurrentContainer->removeContainerListener(this);
1980 if ( m_aTabActivationIdle.IsActive() )
1981 m_aTabActivationIdle.Stop();
1983 // clear the filter map
1984 ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), RemoveComponentTextListener( this ) );
1985 m_aFilterComponents.clear();
1987 // collecting the controls
1988 for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
1989 implControlRemoved( rControl, true );
1991 // make database-specific things
1992 if (m_bDBConnection && isListeningForChanges())
1993 stopListening();
1995 m_aControls.realloc( 0 );
1998 if (m_xTabController.is())
1999 m_xTabController->setContainer(xContainer);
2001 // What controls belong to the container?
2002 if (xContainer.is() && xTabModel.is())
2004 const Sequence< Reference< XControlModel > > aModels = xTabModel->getControlModels();
2005 Sequence< Reference< XControl > > aAllControls = xContainer->getControls();
2007 sal_Int32 nCount = aModels.getLength();
2008 m_aControls = Sequence< Reference< XControl > >( nCount );
2009 Reference< XControl > * pControls = m_aControls.getArray();
2011 // collecting the controls
2012 sal_Int32 j = 0;
2013 for (const Reference< XControlModel >& rModel : aModels )
2015 Reference< XControl > xControl = findControl( aAllControls, rModel, false, true );
2016 if ( xControl.is() )
2018 pControls[j++] = xControl;
2019 implControlInserted( xControl, true );
2023 // not every model had an associated control
2024 if (j != nCount)
2025 m_aControls.realloc(j);
2027 // listen at the container
2028 Reference< XContainer > xNewContainer(xContainer, UNO_QUERY);
2029 if (xNewContainer.is())
2030 xNewContainer->addContainerListener(this);
2032 // make database-specific things
2033 if (m_bDBConnection)
2035 m_bLocked = determineLockState();
2036 setLocks();
2037 if (!isLocked())
2038 startListening();
2041 // the controls are in the right order
2042 m_bControlsSorted = true;
2046 Reference< XControlContainer > FormController::getContainer()
2048 ::osl::MutexGuard aGuard( m_aMutex );
2049 impl_checkDisposed_throw();
2051 DBG_ASSERT(m_xTabController.is(), "FormController::getContainer : invalid aggregate !");
2052 if (!m_xTabController.is())
2053 return Reference< XControlContainer > ();
2054 return m_xTabController->getContainer();
2058 Sequence< Reference< XControl > > FormController::getControls()
2060 ::osl::MutexGuard aGuard( m_aMutex );
2061 impl_checkDisposed_throw();
2063 if (!m_bControlsSorted)
2065 Reference< XTabControllerModel > xModel = getModel();
2066 if (!xModel.is())
2067 return m_aControls;
2069 const Sequence< Reference< XControlModel > > aControlModels = xModel->getControlModels();
2070 sal_Int32 nModels = aControlModels.getLength();
2072 Sequence< Reference< XControl > > aNewControls(nModels);
2074 Reference< XControl > * pControls = aNewControls.getArray();
2075 Reference< XControl > xControl;
2077 // rearrange the controls according to the tab order sequence
2078 sal_Int32 j = 0;
2079 for ( const Reference< XControlModel >& rModel : aControlModels )
2081 xControl = findControl( m_aControls, rModel, true, true );
2082 if ( xControl.is() )
2083 pControls[j++] = xControl;
2086 // not every model had an associated control
2087 if ( j != nModels )
2088 aNewControls.realloc( j );
2090 m_aControls = aNewControls;
2091 m_bControlsSorted = true;
2093 return m_aControls;
2097 void FormController::autoTabOrder()
2099 ::osl::MutexGuard aGuard( m_aMutex );
2100 impl_checkDisposed_throw();
2102 DBG_ASSERT(m_xTabController.is(), "FormController::autoTabOrder : invalid aggregate !");
2103 if (m_xTabController.is())
2104 m_xTabController->autoTabOrder();
2108 void FormController::activateTabOrder()
2110 ::osl::MutexGuard aGuard( m_aMutex );
2111 impl_checkDisposed_throw();
2113 DBG_ASSERT(m_xTabController.is(), "FormController::activateTabOrder : invalid aggregate !");
2114 if (m_xTabController.is())
2115 m_xTabController->activateTabOrder();
2119 void FormController::setControlLock(const Reference< XControl > & xControl)
2121 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2122 bool bLocked = isLocked();
2124 // It is locked
2125 // a. if the entire record is locked
2126 // b. if the associated field is locked
2127 Reference< XBoundControl > xBound(xControl, UNO_QUERY);
2128 if (xBound.is() &&
2129 ( (bLocked && bLocked != bool(xBound->getLock())) ||
2130 !bLocked)) // always uncheck individual fields when unlocking
2132 // there is a data source
2133 Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
2134 if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
2136 // what about the ReadOnly and Enable properties
2137 bool bTouch = true;
2138 if (::comphelper::hasProperty(FM_PROP_ENABLED, xSet))
2139 bTouch = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ENABLED));
2140 if (::comphelper::hasProperty(FM_PROP_READONLY, xSet))
2141 bTouch = !::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_READONLY));
2143 if (bTouch)
2145 Reference< XPropertySet > xField;
2146 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
2147 if (xField.is())
2149 if (bLocked)
2150 xBound->setLock(bLocked);
2151 else
2155 Any aVal = xField->getPropertyValue(FM_PROP_ISREADONLY);
2156 if (aVal.hasValue() && ::comphelper::getBOOL(aVal))
2157 xBound->setLock(true);
2158 else
2159 xBound->setLock(bLocked);
2161 catch( const Exception& )
2163 DBG_UNHANDLED_EXCEPTION("svx");
2174 void FormController::setLocks()
2176 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2177 // lock/unlock all controls connected to a data source
2178 for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
2179 setControlLock( rControl );
2183 namespace
2185 bool lcl_shouldListenForModifications( const Reference< XControl >& _rxControl, const Reference< XPropertyChangeListener >& _rxBoundFieldListener )
2187 bool bShould = false;
2189 Reference< XBoundComponent > xBound( _rxControl, UNO_QUERY );
2190 if ( xBound.is() )
2192 bShould = true;
2194 else if ( _rxControl.is() )
2196 Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY );
2197 if ( xModelProps.is() && ::comphelper::hasProperty( FM_PROP_BOUNDFIELD, xModelProps ) )
2199 Reference< XPropertySet > xField;
2200 xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ) >>= xField;
2201 bShould = xField.is();
2203 if ( !bShould && _rxBoundFieldListener.is() )
2204 xModelProps->addPropertyChangeListener( FM_PROP_BOUNDFIELD, _rxBoundFieldListener );
2208 return bShould;
2213 void FormController::startControlModifyListening(const Reference< XControl > & xControl)
2215 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2217 bool bModifyListening = lcl_shouldListenForModifications( xControl, this );
2219 // artificial while
2220 while ( bModifyListening )
2222 Reference< XModifyBroadcaster > xMod(xControl, UNO_QUERY);
2223 if (xMod.is())
2225 xMod->addModifyListener(this);
2226 break;
2229 // all the text to prematurely recognize a modified
2230 Reference< XTextComponent > xText(xControl, UNO_QUERY);
2231 if (xText.is())
2233 xText->addTextListener(this);
2234 break;
2237 Reference< XCheckBox > xBox(xControl, UNO_QUERY);
2238 if (xBox.is())
2240 xBox->addItemListener(this);
2241 break;
2244 Reference< XComboBox > xCbBox(xControl, UNO_QUERY);
2245 if (xCbBox.is())
2247 xCbBox->addItemListener(this);
2248 break;
2251 Reference< XListBox > xListBox(xControl, UNO_QUERY);
2252 if (xListBox.is())
2254 xListBox->addItemListener(this);
2255 break;
2257 break;
2262 void FormController::stopControlModifyListening(const Reference< XControl > & xControl)
2264 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2266 bool bModifyListening = lcl_shouldListenForModifications( xControl, nullptr );
2268 // artificial while
2269 while (bModifyListening)
2271 Reference< XModifyBroadcaster > xMod(xControl, UNO_QUERY);
2272 if (xMod.is())
2274 xMod->removeModifyListener(this);
2275 break;
2277 // all the text to prematurely recognize a modified
2278 Reference< XTextComponent > xText(xControl, UNO_QUERY);
2279 if (xText.is())
2281 xText->removeTextListener(this);
2282 break;
2285 Reference< XCheckBox > xBox(xControl, UNO_QUERY);
2286 if (xBox.is())
2288 xBox->removeItemListener(this);
2289 break;
2292 Reference< XComboBox > xCbBox(xControl, UNO_QUERY);
2293 if (xCbBox.is())
2295 xCbBox->removeItemListener(this);
2296 break;
2299 Reference< XListBox > xListBox(xControl, UNO_QUERY);
2300 if (xListBox.is())
2302 xListBox->removeItemListener(this);
2303 break;
2305 break;
2310 void FormController::startListening()
2312 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2313 m_bModified = false;
2315 // now register at bound fields
2316 for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
2317 startControlModifyListening( rControl );
2321 void FormController::stopListening()
2323 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2324 m_bModified = false;
2326 // now register at bound fields
2327 for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
2328 stopControlModifyListening( rControl );
2332 Reference< XControl > FormController::findControl(Sequence< Reference< XControl > >& _rControls, const Reference< XControlModel > & xCtrlModel ,bool _bRemove,bool _bOverWrite) const
2334 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2335 DBG_ASSERT( xCtrlModel.is(), "findControl - which ?!" );
2337 Reference< XControl >* pControls = std::find_if(_rControls.begin(), _rControls.end(),
2338 [&xCtrlModel](const Reference< XControl >& rControl) {
2339 return rControl.is() && rControl->getModel().get() == xCtrlModel.get(); });
2340 if (pControls != _rControls.end())
2342 Reference< XControl > xControl( *pControls );
2343 if ( _bRemove )
2345 auto i = static_cast<sal_Int32>(std::distance(_rControls.begin(), pControls));
2346 ::comphelper::removeElementAt( _rControls, i );
2348 else if ( _bOverWrite )
2349 pControls->clear();
2350 return xControl;
2352 return Reference< XControl > ();
2356 void FormController::implControlInserted( const Reference< XControl>& _rxControl, bool _bAddToEventAttacher )
2358 Reference< XWindow > xWindow( _rxControl, UNO_QUERY );
2359 if ( xWindow.is() )
2361 xWindow->addFocusListener( this );
2362 xWindow->addMouseListener( this );
2364 if ( _bAddToEventAttacher )
2365 addToEventAttacher( _rxControl );
2368 // add a dispatch interceptor to the control (if supported)
2369 Reference< XDispatchProviderInterception > xInterception( _rxControl, UNO_QUERY );
2370 if ( xInterception.is() )
2371 createInterceptor( xInterception );
2373 if ( _rxControl.is() )
2375 Reference< XControlModel > xModel( _rxControl->getModel() );
2377 // we want to know about the reset of the model of our controls
2378 // (for correctly resetting m_bModified)
2379 Reference< XReset > xReset( xModel, UNO_QUERY );
2380 if ( xReset.is() )
2381 xReset->addResetListener( this );
2383 // and we want to know about the validity, to visually indicate it
2384 Reference< XValidatableFormComponent > xValidatable( xModel, UNO_QUERY );
2385 if ( xValidatable.is() )
2387 xValidatable->addFormComponentValidityListener( this );
2388 m_aControlBorderManager.validityChanged( _rxControl, xValidatable );
2395 void FormController::implControlRemoved( const Reference< XControl>& _rxControl, bool _bRemoveFromEventAttacher )
2397 Reference< XWindow > xWindow( _rxControl, UNO_QUERY );
2398 if ( xWindow.is() )
2400 xWindow->removeFocusListener( this );
2401 xWindow->removeMouseListener( this );
2403 if ( _bRemoveFromEventAttacher )
2404 removeFromEventAttacher( _rxControl );
2407 Reference< XDispatchProviderInterception > xInterception( _rxControl, UNO_QUERY);
2408 if ( xInterception.is() )
2409 deleteInterceptor( xInterception );
2411 if ( _rxControl.is() )
2413 Reference< XControlModel > xModel( _rxControl->getModel() );
2415 Reference< XReset > xReset( xModel, UNO_QUERY );
2416 if ( xReset.is() )
2417 xReset->removeResetListener( this );
2419 Reference< XValidatableFormComponent > xValidatable( xModel, UNO_QUERY );
2420 if ( xValidatable.is() )
2421 xValidatable->removeFormComponentValidityListener( this );
2426 void FormController::implSetCurrentControl( const Reference< XControl >& _rxControl )
2428 if ( m_xCurrentControl.get() == _rxControl.get() )
2429 return;
2431 Reference< XGridControl > xGridControl( m_xCurrentControl, UNO_QUERY );
2432 if ( xGridControl.is() )
2433 xGridControl->removeGridControlListener( this );
2435 m_xCurrentControl = _rxControl;
2437 xGridControl.set( m_xCurrentControl, UNO_QUERY );
2438 if ( xGridControl.is() )
2439 xGridControl->addGridControlListener( this );
2443 void FormController::insertControl(const Reference< XControl > & xControl)
2445 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2446 m_bControlsSorted = false;
2447 m_aControls.realloc(m_aControls.getLength() + 1);
2448 m_aControls.getArray()[m_aControls.getLength() - 1] = xControl;
2450 if (m_pColumnInfoCache)
2451 m_pColumnInfoCache->deinitializeControls();
2453 implControlInserted( xControl, m_bAttachEvents );
2455 if (m_bDBConnection && !m_bFiltering)
2456 setControlLock(xControl);
2458 if (isListeningForChanges() && m_bAttachEvents)
2459 startControlModifyListening( xControl );
2463 void FormController::removeControl(const Reference< XControl > & xControl)
2465 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2466 auto pControl = std::find_if(m_aControls.begin(), m_aControls.end(),
2467 [&xControl](const Reference< XControl >& rControl) { return xControl.get() == rControl.get(); });
2468 if (pControl != m_aControls.end())
2470 auto nIndex = static_cast<sal_Int32>(std::distance(m_aControls.begin(), pControl));
2471 ::comphelper::removeElementAt( m_aControls, nIndex );
2474 FilterComponents::iterator componentPos = ::std::find( m_aFilterComponents.begin(), m_aFilterComponents.end(), xControl );
2475 if ( componentPos != m_aFilterComponents.end() )
2476 m_aFilterComponents.erase( componentPos );
2478 implControlRemoved( xControl, m_bDetachEvents );
2480 if ( isListeningForChanges() && m_bDetachEvents )
2481 stopControlModifyListening( xControl );
2484 // XLoadListener
2486 void FormController::loaded(const EventObject& rEvent)
2488 OSL_ENSURE( rEvent.Source == m_xModelAsIndex, "FormController::loaded: where did this come from?" );
2490 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2491 ::osl::MutexGuard aGuard( m_aMutex );
2492 Reference< XRowSet > xForm(rEvent.Source, UNO_QUERY);
2493 // do we have a connected data source
2494 if (xForm.is() && getConnection(xForm).is())
2496 Reference< XPropertySet > xSet(xForm, UNO_QUERY);
2497 if (xSet.is())
2499 Any aVal = xSet->getPropertyValue(FM_PROP_CYCLE);
2500 sal_Int32 aVal2 = 0;
2501 ::cppu::enum2int(aVal2,aVal);
2502 m_bCycle = !aVal.hasValue() || static_cast<form::TabulatorCycle>(aVal2) == TabulatorCycle_RECORDS;
2503 m_bCanUpdate = canUpdate(xSet);
2504 m_bCanInsert = canInsert(xSet);
2505 m_bCurrentRecordModified = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED));
2506 m_bCurrentRecordNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
2508 startFormListening( xSet, false );
2510 // set the locks for the current controls
2511 if (getContainer().is())
2513 m_aLoadEvent.Call();
2516 else
2518 m_bCanInsert = m_bCanUpdate = m_bCycle = false;
2519 m_bCurrentRecordModified = false;
2520 m_bCurrentRecordNew = false;
2521 m_bLocked = false;
2523 m_bDBConnection = true;
2525 else
2527 m_bDBConnection = false;
2528 m_bCanInsert = m_bCanUpdate = m_bCycle = false;
2529 m_bCurrentRecordModified = false;
2530 m_bCurrentRecordNew = false;
2531 m_bLocked = false;
2534 Reference< XColumnsSupplier > xFormColumns( xForm, UNO_QUERY );
2535 m_pColumnInfoCache.reset( xFormColumns.is() ? new ColumnInfoCache( xFormColumns ) : nullptr );
2537 updateAllDispatchers();
2541 void FormController::updateAllDispatchers() const
2543 ::std::for_each(
2544 m_aFeatureDispatchers.begin(),
2545 m_aFeatureDispatchers.end(),
2546 [] (const DispatcherContainer::value_type& dispatcher) {
2547 UpdateAllListeners()(dispatcher.second);
2552 IMPL_LINK_NOARG(FormController, OnLoad, void*, void)
2554 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2555 m_bLocked = determineLockState();
2557 setLocks();
2559 if (!m_bLocked)
2560 startListening();
2562 // just one exception toggle the auto values
2563 if (m_bCurrentRecordNew)
2564 toggleAutoFields(true);
2568 void FormController::unloaded(const EventObject& /*rEvent*/)
2570 ::osl::MutexGuard aGuard( m_aMutex );
2571 impl_checkDisposed_throw();
2573 updateAllDispatchers();
2577 void FormController::reloading(const EventObject& /*aEvent*/)
2579 ::osl::MutexGuard aGuard( m_aMutex );
2580 impl_checkDisposed_throw();
2582 // do the same like in unloading
2583 // just one exception toggle the auto values
2584 m_aToggleEvent.CancelPendingCall();
2585 unload();
2589 void FormController::reloaded(const EventObject& aEvent)
2591 ::osl::MutexGuard aGuard( m_aMutex );
2592 impl_checkDisposed_throw();
2594 loaded(aEvent);
2598 void FormController::unloading(const EventObject& /*aEvent*/)
2600 ::osl::MutexGuard aGuard( m_aMutex );
2601 impl_checkDisposed_throw();
2603 unload();
2607 void FormController::unload()
2609 ::osl::MutexGuard aGuard( m_aMutex );
2610 impl_checkDisposed_throw();
2612 m_aLoadEvent.CancelPendingCall();
2614 // be sure not to have autofields
2615 if (m_bCurrentRecordNew)
2616 toggleAutoFields(false);
2618 // remove bound field listing again
2619 removeBoundFieldListener();
2621 if (m_bDBConnection && isListeningForChanges())
2622 stopListening();
2624 Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
2625 if ( m_bDBConnection && xSet.is() )
2626 stopFormListening( xSet, false );
2628 m_bDBConnection = false;
2629 m_bCanInsert = m_bCanUpdate = m_bCycle = false;
2630 m_bCurrentRecordModified = m_bCurrentRecordNew = m_bLocked = false;
2632 m_pColumnInfoCache.reset();
2636 void FormController::removeBoundFieldListener()
2638 for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
2640 Reference< XPropertySet > xProp( rControl, UNO_QUERY );
2641 if ( xProp.is() )
2642 xProp->removePropertyChangeListener( FM_PROP_BOUNDFIELD, this );
2647 void FormController::startFormListening( const Reference< XPropertySet >& _rxForm, bool _bPropertiesOnly )
2651 if ( m_bCanInsert || m_bCanUpdate ) // form can be modified
2653 _rxForm->addPropertyChangeListener( FM_PROP_ISNEW, this );
2654 _rxForm->addPropertyChangeListener( FM_PROP_ISMODIFIED, this );
2656 if ( !_bPropertiesOnly )
2658 // set the Listener for UI interaction
2659 Reference< XRowSetApproveBroadcaster > xApprove( _rxForm, UNO_QUERY );
2660 if ( xApprove.is() )
2661 xApprove->addRowSetApproveListener( this );
2663 // listener for row set changes
2664 Reference< XRowSet > xRowSet( _rxForm, UNO_QUERY );
2665 if ( xRowSet.is() )
2666 xRowSet->addRowSetListener( this );
2670 Reference< XPropertySetInfo > xInfo = _rxForm->getPropertySetInfo();
2671 if ( xInfo.is() && xInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER ) )
2672 _rxForm->addPropertyChangeListener( FM_PROP_DYNAMIC_CONTROL_BORDER, this );
2674 catch( const Exception& )
2676 DBG_UNHANDLED_EXCEPTION("svx");
2681 void FormController::stopFormListening( const Reference< XPropertySet >& _rxForm, bool _bPropertiesOnly )
2685 if ( m_bCanInsert || m_bCanUpdate )
2687 _rxForm->removePropertyChangeListener( FM_PROP_ISNEW, this );
2688 _rxForm->removePropertyChangeListener( FM_PROP_ISMODIFIED, this );
2690 if ( !_bPropertiesOnly )
2692 Reference< XRowSetApproveBroadcaster > xApprove( _rxForm, UNO_QUERY );
2693 if (xApprove.is())
2694 xApprove->removeRowSetApproveListener(this);
2696 Reference< XRowSet > xRowSet( _rxForm, UNO_QUERY );
2697 if ( xRowSet.is() )
2698 xRowSet->removeRowSetListener( this );
2702 Reference< XPropertySetInfo > xInfo = _rxForm->getPropertySetInfo();
2703 if ( xInfo.is() && xInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER ) )
2704 _rxForm->removePropertyChangeListener( FM_PROP_DYNAMIC_CONTROL_BORDER, this );
2706 catch( const Exception& )
2708 DBG_UNHANDLED_EXCEPTION("svx");
2712 // css::sdbc::XRowSetListener
2714 void FormController::cursorMoved(const EventObject& /*event*/)
2716 ::osl::MutexGuard aGuard( m_aMutex );
2717 impl_checkDisposed_throw();
2719 // toggle the locking ?
2720 if (m_bLocked != determineLockState())
2722 m_bLocked = !m_bLocked;
2723 setLocks();
2724 if (isListeningForChanges())
2725 startListening();
2726 else
2727 stopListening();
2730 // neither the current control nor the current record are modified anymore
2731 m_bCurrentRecordModified = m_bModified = false;
2735 void FormController::rowChanged(const EventObject& /*event*/)
2737 // not interested in ...
2740 void FormController::rowSetChanged(const EventObject& /*event*/)
2742 // not interested in ...
2746 // XContainerListener
2748 void SAL_CALL FormController::elementInserted(const ContainerEvent& evt)
2750 ::osl::MutexGuard aGuard( m_aMutex );
2751 impl_checkDisposed_throw();
2753 Reference< XControl > xControl( evt.Element, UNO_QUERY );
2754 if ( !xControl.is() )
2755 return;
2757 Reference< XFormComponent > xModel(xControl->getModel(), UNO_QUERY);
2758 if (xModel.is() && m_xModelAsIndex == xModel->getParent())
2760 insertControl(xControl);
2762 if ( m_aTabActivationIdle.IsActive() )
2763 m_aTabActivationIdle.Stop();
2765 m_aTabActivationIdle.Start();
2767 // are we in filtermode and a XModeSelector has inserted an element
2768 else if (m_bFiltering && Reference< XModeSelector > (evt.Source, UNO_QUERY).is())
2770 xModel.set(evt.Source, UNO_QUERY);
2771 if (xModel.is() && m_xModelAsIndex == xModel->getParent())
2773 Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
2774 if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
2776 // does the model use a bound field ?
2777 Reference< XPropertySet > xField;
2778 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
2780 Reference< XTextComponent > xText(xControl, UNO_QUERY);
2781 // may we filter the field?
2782 if (xText.is() && xField.is() && ::comphelper::hasProperty(FM_PROP_SEARCHABLE, xField) &&
2783 ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_SEARCHABLE)))
2785 m_aFilterComponents.push_back( xText );
2786 xText->addTextListener( this );
2794 void SAL_CALL FormController::elementReplaced(const ContainerEvent& evt)
2796 // simulate an elementRemoved
2797 ContainerEvent aRemoveEvent( evt );
2798 aRemoveEvent.Element = evt.ReplacedElement;
2799 aRemoveEvent.ReplacedElement = Any();
2800 elementRemoved( aRemoveEvent );
2802 // simulate an elementInserted
2803 ContainerEvent aInsertEvent( evt );
2804 aInsertEvent.ReplacedElement = Any();
2805 elementInserted( aInsertEvent );
2809 void SAL_CALL FormController::elementRemoved(const ContainerEvent& evt)
2811 ::osl::MutexGuard aGuard( m_aMutex );
2812 impl_checkDisposed_throw();
2814 Reference< XControl > xControl;
2815 evt.Element >>= xControl;
2816 if (!xControl.is())
2817 return;
2819 Reference< XFormComponent > xModel(xControl->getModel(), UNO_QUERY);
2820 if (xModel.is() && m_xModelAsIndex == xModel->getParent())
2822 removeControl(xControl);
2823 // Do not recalculate TabOrder, because it must already work internally!
2825 // are we in filtermode and a XModeSelector has inserted an element
2826 else if (m_bFiltering && Reference< XModeSelector > (evt.Source, UNO_QUERY).is())
2828 FilterComponents::iterator componentPos = ::std::find(
2829 m_aFilterComponents.begin(), m_aFilterComponents.end(), xControl );
2830 if ( componentPos != m_aFilterComponents.end() )
2831 m_aFilterComponents.erase( componentPos );
2836 Reference< XControl > FormController::isInList(const Reference< XWindowPeer > & xPeer) const
2838 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2839 const Reference< XControl >* pControls = m_aControls.getConstArray();
2841 sal_uInt32 nCtrls = m_aControls.getLength();
2842 for ( sal_uInt32 n = 0; n < nCtrls && xPeer.is(); ++n, ++pControls )
2844 if ( pControls->is() )
2846 Reference< XVclWindowPeer > xCtrlPeer( (*pControls)->getPeer(), UNO_QUERY);
2847 if ( ( xCtrlPeer.get() == xPeer.get() ) || xCtrlPeer->isChild( xPeer ) )
2848 return *pControls;
2851 return Reference< XControl > ();
2855 void FormController::activateFirst()
2857 ::osl::MutexGuard aGuard( m_aMutex );
2858 impl_checkDisposed_throw();
2860 DBG_ASSERT(m_xTabController.is(), "FormController::activateFirst : invalid aggregate !");
2861 if (m_xTabController.is())
2862 m_xTabController->activateFirst();
2866 void FormController::activateLast()
2868 ::osl::MutexGuard aGuard( m_aMutex );
2869 impl_checkDisposed_throw();
2871 DBG_ASSERT(m_xTabController.is(), "FormController::activateLast : invalid aggregate !");
2872 if (m_xTabController.is())
2873 m_xTabController->activateLast();
2876 // XFormController
2878 Reference< XFormOperations > SAL_CALL FormController::getFormOperations()
2880 ::osl::MutexGuard aGuard( m_aMutex );
2881 impl_checkDisposed_throw();
2883 return m_xFormOperations;
2887 Reference< XControl> SAL_CALL FormController::getCurrentControl()
2889 ::osl::MutexGuard aGuard( m_aMutex );
2890 impl_checkDisposed_throw();
2891 return m_xCurrentControl;
2895 void SAL_CALL FormController::addActivateListener(const Reference< XFormControllerListener > & l)
2897 ::osl::MutexGuard aGuard( m_aMutex );
2898 impl_checkDisposed_throw();
2899 m_aActivateListeners.addInterface(l);
2902 void SAL_CALL FormController::removeActivateListener(const Reference< XFormControllerListener > & l)
2904 ::osl::MutexGuard aGuard( m_aMutex );
2905 impl_checkDisposed_throw();
2906 m_aActivateListeners.removeInterface(l);
2910 void SAL_CALL FormController::addChildController( const Reference< XFormController >& ChildController )
2912 ::osl::MutexGuard aGuard( m_aMutex );
2913 impl_checkDisposed_throw();
2915 if ( !ChildController.is() )
2916 throw IllegalArgumentException( OUString(), *this, 1 );
2917 // TODO: (localized) error message
2919 // the parent of our (to-be-)child must be our own model
2920 Reference< XFormComponent > xFormOfChild( ChildController->getModel(), UNO_QUERY );
2921 if ( !xFormOfChild.is() )
2922 throw IllegalArgumentException( OUString(), *this, 1 );
2923 // TODO: (localized) error message
2925 if ( xFormOfChild->getParent() != m_xModelAsIndex )
2926 throw IllegalArgumentException( OUString(), *this, 1 );
2927 // TODO: (localized) error message
2929 m_aChildren.push_back( ChildController );
2930 ChildController->setParent( *this );
2932 // search the position of the model within the form
2933 sal_uInt32 nPos = m_xModelAsIndex->getCount();
2934 Reference< XFormComponent > xTemp;
2935 for( ; nPos; )
2937 m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
2938 if ( xFormOfChild == xTemp )
2940 m_xModelAsManager->attach( nPos, Reference<XInterface>( ChildController, UNO_QUERY ), makeAny( ChildController) );
2941 break;
2947 Reference< XFormControllerContext > SAL_CALL FormController::getContext()
2949 ::osl::MutexGuard aGuard( m_aMutex );
2950 impl_checkDisposed_throw();
2951 return m_xFormControllerContext;
2955 void SAL_CALL FormController::setContext( const Reference< XFormControllerContext >& _context )
2957 ::osl::MutexGuard aGuard( m_aMutex );
2958 impl_checkDisposed_throw();
2959 m_xFormControllerContext = _context;
2963 Reference< XInteractionHandler > SAL_CALL FormController::getInteractionHandler()
2965 ::osl::MutexGuard aGuard( m_aMutex );
2966 impl_checkDisposed_throw();
2967 return m_xInteractionHandler;
2971 void SAL_CALL FormController::setInteractionHandler( const Reference< XInteractionHandler >& _interactionHandler )
2973 ::osl::MutexGuard aGuard( m_aMutex );
2974 impl_checkDisposed_throw();
2975 m_xInteractionHandler = _interactionHandler;
2979 void FormController::setFilter(::std::vector<FmFieldInfo>& rFieldInfos)
2981 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2982 // create the composer
2983 Reference< XRowSet > xForm(m_xModelAsIndex, UNO_QUERY);
2984 Reference< XConnection > xConnection(getConnection(xForm));
2985 if (xForm.is())
2989 Reference< XMultiServiceFactory > xFactory( xConnection, UNO_QUERY_THROW );
2990 m_xComposer.set(
2991 xFactory->createInstance("com.sun.star.sdb.SingleSelectQueryComposer"),
2992 UNO_QUERY_THROW );
2994 Reference< XPropertySet > xSet( xForm, UNO_QUERY );
2995 OUString sStatement = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_ACTIVECOMMAND ) );
2996 OUString sFilter = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_FILTER ) );
2997 m_xComposer->setElementaryQuery( sStatement );
2998 m_xComposer->setFilter( sFilter );
3000 catch( const Exception& )
3002 DBG_UNHANDLED_EXCEPTION("svx");
3006 if (m_xComposer.is())
3008 const Sequence< Sequence < PropertyValue > > aFilterRows = m_xComposer->getStructuredFilter();
3010 // ok, we receive the list of filters as sequence of fieldnames, value
3011 // now we have to transform the fieldname into UI names, that could be a label of the field or
3012 // an aliasname or the fieldname itself
3014 // first adjust the field names if necessary
3015 Reference< XNameAccess > xQueryColumns =
3016 Reference< XColumnsSupplier >( m_xComposer, UNO_QUERY_THROW )->getColumns();
3018 for (auto& rFieldInfo : rFieldInfos)
3020 if ( xQueryColumns->hasByName(rFieldInfo.aFieldName) )
3022 if ( (xQueryColumns->getByName(rFieldInfo.aFieldName) >>= rFieldInfo.xField) && rFieldInfo.xField.is() )
3023 rFieldInfo.xField->getPropertyValue(FM_PROP_REALNAME) >>= rFieldInfo.aFieldName;
3027 Reference< XDatabaseMetaData> xMetaData(xConnection->getMetaData());
3028 // now transfer the filters into Value/TextComponent pairs
3029 ::comphelper::UStringMixEqual aCompare(xMetaData->storesMixedCaseQuotedIdentifiers());
3031 // need to parse criteria localized
3032 Reference< XNumberFormatsSupplier> xFormatSupplier( getNumberFormats(xConnection, true));
3033 Reference< XNumberFormatter> xFormatter = NumberFormatter::create(m_xComponentContext);
3034 xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
3035 Locale aAppLocale = Application::GetSettings().GetUILanguageTag().getLocale();
3036 const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetUILocaleDataWrapper() );
3037 OUString strDecimalSeparator = rLocaleWrapper.getNumDecimalSep();
3039 // retrieving the filter
3040 for (const Sequence < PropertyValue >& rRow : aFilterRows)
3042 FmFilterRow aRow;
3044 // search a field for the given name
3045 for (const PropertyValue& rRefValue : rRow)
3047 // look for the text component
3048 Reference< XPropertySet > xField;
3051 Reference< XPropertySet > xSet;
3052 OUString aRealName;
3054 // first look with the given name
3055 if (xQueryColumns->hasByName(rRefValue.Name))
3057 xQueryColumns->getByName(rRefValue.Name) >>= xSet;
3059 // get the RealName
3060 xSet->getPropertyValue("RealName") >>= aRealName;
3062 // compare the condition field name and the RealName
3063 if (aCompare(aRealName, rRefValue.Name))
3064 xField = xSet;
3066 if (!xField.is())
3068 // no we have to check every column to find the realname
3069 Reference< XIndexAccess > xColumnsByIndex(xQueryColumns, UNO_QUERY);
3070 for (sal_Int32 n = 0, nCount = xColumnsByIndex->getCount(); n < nCount; n++)
3072 xColumnsByIndex->getByIndex(n) >>= xSet;
3073 xSet->getPropertyValue("RealName") >>= aRealName;
3074 if (aCompare(aRealName, rRefValue.Name))
3076 // get the column by its alias
3077 xField = xSet;
3078 break;
3082 if (!xField.is())
3083 continue;
3085 catch (const Exception&)
3087 continue;
3090 // find the text component
3091 for (const auto& rFieldInfo : rFieldInfos)
3093 // we found the field so insert a new entry to the filter row
3094 if (rFieldInfo.xField == xField)
3096 // do we already have the control ?
3097 if (aRow.find(rFieldInfo.xText) != aRow.end())
3099 OString aVal = m_pParser->getContext().getIntlKeywordAscii(IParseContext::InternationalKeyCode::And);
3100 OUString aCompText = aRow[rFieldInfo.xText] + " " +
3101 OUString(aVal.getStr(),aVal.getLength(),RTL_TEXTENCODING_ASCII_US) + " " +
3102 ::comphelper::getString(rRefValue.Value);
3103 aRow[rFieldInfo.xText] = aCompText;
3105 else
3107 OUString sPredicate,sErrorMsg;
3108 rRefValue.Value >>= sPredicate;
3109 std::shared_ptr< OSQLParseNode > pParseNode = predicateTree(sErrorMsg, sPredicate, xFormatter, xField);
3110 if ( pParseNode != nullptr )
3112 OUString sCriteria;
3113 switch (rRefValue.Handle)
3115 case css::sdb::SQLFilterOperator::EQUAL:
3116 sCriteria += "=";
3117 break;
3118 case css::sdb::SQLFilterOperator::NOT_EQUAL:
3119 sCriteria += "!=";
3120 break;
3121 case css::sdb::SQLFilterOperator::LESS:
3122 sCriteria += "<";
3123 break;
3124 case css::sdb::SQLFilterOperator::GREATER:
3125 sCriteria += ">";
3126 break;
3127 case css::sdb::SQLFilterOperator::LESS_EQUAL:
3128 sCriteria += "<=";
3129 break;
3130 case css::sdb::SQLFilterOperator::GREATER_EQUAL:
3131 sCriteria += ">=";
3132 break;
3133 case css::sdb::SQLFilterOperator::LIKE:
3134 sCriteria += "LIKE ";
3135 break;
3136 case css::sdb::SQLFilterOperator::NOT_LIKE:
3137 sCriteria += "NOT LIKE ";
3138 break;
3139 case css::sdb::SQLFilterOperator::SQLNULL:
3140 sCriteria += "IS NULL";
3141 break;
3142 case css::sdb::SQLFilterOperator::NOT_SQLNULL:
3143 sCriteria += "IS NOT NULL";
3144 break;
3146 pParseNode->parseNodeToPredicateStr( sCriteria
3147 ,xConnection
3148 ,xFormatter
3149 ,xField
3150 ,OUString()
3151 ,aAppLocale
3152 ,strDecimalSeparator
3153 ,getParseContext());
3154 aRow[rFieldInfo.xText] = sCriteria;
3161 if (aRow.empty())
3162 continue;
3164 impl_addFilterRow( aRow );
3168 // now set the filter controls
3169 for (const auto& rFieldInfo : rFieldInfos)
3171 m_aFilterComponents.push_back( rFieldInfo.xText );
3176 void FormController::startFiltering()
3178 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
3180 Reference< XConnection > xConnection( getConnection( Reference< XRowSet >( m_xModelAsIndex, UNO_QUERY ) ) );
3181 if ( !xConnection.is() )
3182 // nothing to do - can't filter a form which is not connected
3183 return;
3185 // stop listening for controls
3186 if (isListeningForChanges())
3187 stopListening();
3189 m_bFiltering = true;
3191 // as we don't want new controls to be attached to the scripting environment
3192 // we change attach flags
3193 m_bAttachEvents = false;
3195 // exchanging the controls for the current form
3196 Sequence< Reference< XControl > > aControlsCopy( m_aControls );
3197 const Reference< XControl >* pControls = aControlsCopy.getConstArray();
3198 sal_Int32 nControlCount = aControlsCopy.getLength();
3200 // the control we have to activate after replacement
3201 Reference< XNumberFormatsSupplier > xFormatSupplier = getNumberFormats(xConnection, true);
3202 Reference< XNumberFormatter > xFormatter = NumberFormatter::create(m_xComponentContext);
3203 xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
3205 // structure for storing the field info
3206 ::std::vector<FmFieldInfo> aFieldInfos;
3208 for (sal_Int32 i = nControlCount; i > 0;)
3210 Reference< XControl > xControl = pControls[--i];
3211 if (xControl.is())
3213 // no events for the control anymore
3214 removeFromEventAttacher(xControl);
3216 // do we have a mode selector
3217 Reference< XModeSelector > xSelector(xControl, UNO_QUERY);
3218 if (xSelector.is())
3220 xSelector->setMode( "FilterMode" );
3222 // listening for new controls of the selector
3223 Reference< XContainer > xContainer(xSelector, UNO_QUERY);
3224 if (xContainer.is())
3225 xContainer->addContainerListener(this);
3227 Reference< XEnumerationAccess > xElementAccess(xSelector, UNO_QUERY);
3228 if (xElementAccess.is())
3230 Reference< XEnumeration > xEnumeration(xElementAccess->createEnumeration());
3231 Reference< XControl > xSubControl;
3232 while (xEnumeration->hasMoreElements())
3234 xEnumeration->nextElement() >>= xSubControl;
3235 if (xSubControl.is())
3237 Reference< XPropertySet > xSet(xSubControl->getModel(), UNO_QUERY);
3238 if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
3240 // does the model use a bound field ?
3241 Reference< XPropertySet > xField;
3242 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
3244 Reference< XTextComponent > xText(xSubControl, UNO_QUERY);
3245 // may we filter the field?
3246 if (xText.is() && xField.is() && ::comphelper::hasProperty(FM_PROP_SEARCHABLE, xField) &&
3247 ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_SEARCHABLE)))
3249 aFieldInfos.emplace_back(xField, xText);
3250 xText->addTextListener(this);
3256 continue;
3259 Reference< XPropertySet > xModel( xControl->getModel(), UNO_QUERY );
3260 if (xModel.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xModel))
3262 // does the model use a bound field ?
3263 Any aVal = xModel->getPropertyValue(FM_PROP_BOUNDFIELD);
3264 Reference< XPropertySet > xField;
3265 aVal >>= xField;
3267 // may we filter the field?
3269 if ( xField.is()
3270 && ::comphelper::hasProperty( FM_PROP_SEARCHABLE, xField )
3271 && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_SEARCHABLE ) )
3274 // create a filter control
3275 Reference< XControl > xFilterControl = form::control::FilterControl::createWithFormat(
3276 m_xComponentContext,
3277 VCLUnoHelper::GetInterface( getDialogParentWindow() ),
3278 xFormatter,
3279 xModel);
3281 if ( replaceControl( xControl, xFilterControl ) )
3283 Reference< XTextComponent > xFilterText( xFilterControl, UNO_QUERY );
3284 aFieldInfos.emplace_back( xField, xFilterText );
3285 xFilterText->addTextListener(this);
3289 else
3291 // unsubscribe from EventManager
3296 // we have all filter controls now, so the next step is to read the filters from the form
3297 // resolve all aliases and set the current filter to the according structure
3298 setFilter(aFieldInfos);
3300 Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
3301 if ( xSet.is() )
3302 stopFormListening( xSet, true );
3304 impl_setTextOnAllFilter_throw();
3306 // lock all controls which are not used for filtering
3307 m_bLocked = determineLockState();
3308 setLocks();
3309 m_bAttachEvents = true;
3313 void FormController::stopFiltering()
3315 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
3316 if ( !m_bFiltering ) // #104693# OJ
3317 { // nothing to do
3318 return;
3321 m_bFiltering = false;
3322 m_bDetachEvents = false;
3324 ::comphelper::disposeComponent(m_xComposer);
3326 // exchanging the controls for the current form
3327 Sequence< Reference< XControl > > aControlsCopy( m_aControls );
3328 const Reference< XControl > * pControls = aControlsCopy.getConstArray();
3329 sal_Int32 nControlCount = aControlsCopy.getLength();
3331 // clear the filter control map
3332 ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), RemoveComponentTextListener( this ) );
3333 m_aFilterComponents.clear();
3335 for ( sal_Int32 i = nControlCount; i > 0; )
3337 Reference< XControl > xControl = pControls[--i];
3338 if (xControl.is())
3340 // now enable event handling again
3341 addToEventAttacher(xControl);
3343 Reference< XModeSelector > xSelector(xControl, UNO_QUERY);
3344 if (xSelector.is())
3346 xSelector->setMode( "DataMode" );
3348 // listening for new controls of the selector
3349 Reference< XContainer > xContainer(xSelector, UNO_QUERY);
3350 if (xContainer.is())
3351 xContainer->removeContainerListener(this);
3352 continue;
3355 Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
3356 if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
3358 // does the model use a bound field ?
3359 Reference< XPropertySet > xField;
3360 xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
3362 // may we filter the field?
3363 if ( xField.is()
3364 && ::comphelper::hasProperty( FM_PROP_SEARCHABLE, xField )
3365 && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_SEARCHABLE ) )
3368 OUString sServiceName;
3369 OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DEFAULTCONTROL ) >>= sServiceName );
3370 Reference< XControl > xNewControl( m_xComponentContext->getServiceManager()->createInstanceWithContext( sServiceName, m_xComponentContext ), UNO_QUERY );
3371 replaceControl( xControl, xNewControl );
3377 Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
3378 if ( xSet.is() )
3379 startFormListening( xSet, true );
3381 m_bDetachEvents = true;
3383 m_aFilterRows.clear();
3384 m_nCurrentFilterPosition = -1;
3386 // release the locks if possible
3387 // lock all controls which are not used for filtering
3388 m_bLocked = determineLockState();
3389 setLocks();
3391 // restart listening for control modifications
3392 if (isListeningForChanges())
3393 startListening();
3396 // XModeSelector
3398 void FormController::setMode(const OUString& Mode)
3400 ::osl::MutexGuard aGuard( m_aMutex );
3401 impl_checkDisposed_throw();
3403 if (!supportsMode(Mode))
3404 throw NoSupportException();
3406 if (Mode == m_aMode)
3407 return;
3409 m_aMode = Mode;
3411 if ( Mode == "FilterMode" )
3412 startFiltering();
3413 else
3414 stopFiltering();
3416 for (const auto& rChild : m_aChildren)
3418 Reference< XModeSelector > xMode(rChild, UNO_QUERY);
3419 if ( xMode.is() )
3420 xMode->setMode(Mode);
3425 OUString SAL_CALL FormController::getMode()
3427 ::osl::MutexGuard aGuard( m_aMutex );
3428 impl_checkDisposed_throw();
3430 return m_aMode;
3434 Sequence< OUString > SAL_CALL FormController::getSupportedModes()
3436 ::osl::MutexGuard aGuard( m_aMutex );
3437 impl_checkDisposed_throw();
3439 static Sequence< OUString > const aModes
3441 "DataMode",
3442 "FilterMode"
3444 return aModes;
3448 sal_Bool SAL_CALL FormController::supportsMode(const OUString& Mode)
3450 ::osl::MutexGuard aGuard( m_aMutex );
3451 impl_checkDisposed_throw();
3453 Sequence< OUString > aModes(getSupportedModes());
3454 return comphelper::findValue(aModes, Mode) != -1;
3458 vcl::Window* FormController::getDialogParentWindow()
3460 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
3461 vcl::Window* pParentWindow = nullptr;
3464 Reference< XControl > xContainerControl( getContainer(), UNO_QUERY_THROW );
3465 Reference< XWindowPeer > xContainerPeer( xContainerControl->getPeer(), UNO_SET_THROW );
3466 pParentWindow = VCLUnoHelper::GetWindow( xContainerPeer ).get();
3468 catch( const Exception& )
3470 DBG_UNHANDLED_EXCEPTION("svx");
3472 return pParentWindow;
3475 bool FormController::checkFormComponentValidity( OUString& /* [out] */ _rFirstInvalidityExplanation, Reference< XControlModel >& /* [out] */ _rxFirstInvalidModel )
3479 Reference< XEnumerationAccess > xControlEnumAcc( getModel(), UNO_QUERY );
3480 Reference< XEnumeration > xControlEnumeration;
3481 if ( xControlEnumAcc.is() )
3482 xControlEnumeration = xControlEnumAcc->createEnumeration();
3483 OSL_ENSURE( xControlEnumeration.is(), "FormController::checkFormComponentValidity: cannot enumerate the controls!" );
3484 if ( !xControlEnumeration.is() )
3485 // assume all valid
3486 return true;
3488 Reference< XValidatableFormComponent > xValidatable;
3489 while ( xControlEnumeration->hasMoreElements() )
3491 if ( !( xControlEnumeration->nextElement() >>= xValidatable ) )
3492 // control does not support validation
3493 continue;
3495 if ( xValidatable->isValid() )
3496 continue;
3498 Reference< XValidator > xValidator( xValidatable->getValidator() );
3499 OSL_ENSURE( xValidator.is(), "FormController::checkFormComponentValidity: invalid, but no validator?" );
3500 if ( !xValidator.is() )
3501 // this violates the interface definition of css.form.validation.XValidatableFormComponent ...
3502 continue;
3504 _rFirstInvalidityExplanation = xValidator->explainInvalid( xValidatable->getCurrentValue() );
3505 _rxFirstInvalidModel.set(xValidatable, css::uno::UNO_QUERY);
3506 return false;
3509 catch( const Exception& )
3511 DBG_UNHANDLED_EXCEPTION("svx");
3513 return true;
3517 Reference< XControl > FormController::locateControl( const Reference< XControlModel >& _rxModel )
3521 const Sequence< Reference< XControl > > aControls( getControls() );
3523 for ( auto const & control : aControls )
3525 OSL_ENSURE( control.is(), "FormController::locateControl: NULL-control?" );
3526 if ( control.is() )
3528 if ( control->getModel() == _rxModel )
3529 return control;
3532 OSL_FAIL( "FormController::locateControl: did not find a control for this model!" );
3534 catch( const Exception& )
3536 DBG_UNHANDLED_EXCEPTION("svx");
3538 return nullptr;
3542 namespace
3544 void displayErrorSetFocus( const OUString& _rMessage, const Reference< XControl >& _rxFocusControl, vcl::Window* _pDialogParent )
3546 SQLContext aError;
3547 aError.Message = SvxResId(RID_STR_WRITEERROR);
3548 aError.Details = _rMessage;
3549 displayException( aError, _pDialogParent );
3551 if ( _rxFocusControl.is() )
3553 Reference< XWindow > xControlWindow( _rxFocusControl, UNO_QUERY );
3554 OSL_ENSURE( xControlWindow.is(), "displayErrorSetFocus: invalid control!" );
3555 if ( xControlWindow.is() )
3556 xControlWindow->setFocus();
3560 bool lcl_shouldValidateRequiredFields_nothrow( const Reference< XInterface >& _rxForm )
3564 static const char s_sFormsCheckRequiredFields[] = "FormsCheckRequiredFields";
3566 // first, check whether the form has a property telling us the answer
3567 // this allows people to use the XPropertyContainer interface of a form to control
3568 // the behaviour on a per-form basis.
3569 Reference< XPropertySet > xFormProps( _rxForm, UNO_QUERY_THROW );
3570 Reference< XPropertySetInfo > xPSI( xFormProps->getPropertySetInfo() );
3571 if ( xPSI->hasPropertyByName( s_sFormsCheckRequiredFields ) )
3573 bool bShouldValidate = true;
3574 OSL_VERIFY( xFormProps->getPropertyValue( s_sFormsCheckRequiredFields ) >>= bShouldValidate );
3575 return bShouldValidate;
3578 // next, check the data source which created the connection
3579 Reference< XChild > xConnectionAsChild( xFormProps->getPropertyValue( FM_PROP_ACTIVE_CONNECTION ), UNO_QUERY_THROW );
3580 Reference< XPropertySet > xDataSource( xConnectionAsChild->getParent(), UNO_QUERY );
3581 if ( !xDataSource.is() )
3582 // seldom (but possible): this is not a connection created by a data source
3583 return true;
3585 Reference< XPropertySet > xDataSourceSettings(
3586 xDataSource->getPropertyValue("Settings"),
3587 UNO_QUERY_THROW );
3589 bool bShouldValidate = true;
3590 OSL_VERIFY( xDataSourceSettings->getPropertyValue( s_sFormsCheckRequiredFields ) >>= bShouldValidate );
3591 return bShouldValidate;
3593 catch( const Exception& )
3595 DBG_UNHANDLED_EXCEPTION("svx");
3598 return true;
3602 // XRowSetApproveListener
3604 sal_Bool SAL_CALL FormController::approveRowChange(const RowChangeEvent& _rEvent)
3606 ::osl::ClearableMutexGuard aGuard( m_aMutex );
3607 impl_checkDisposed_throw();
3609 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aRowSetApproveListeners);
3610 bool bValid = true;
3611 if (aIter.hasMoreElements())
3613 RowChangeEvent aEvt( _rEvent );
3614 aEvt.Source = *this;
3615 bValid = static_cast<XRowSetApproveListener*>(aIter.next())->approveRowChange(aEvt);
3618 if ( !bValid )
3619 return bValid;
3621 if ( ( _rEvent.Action != RowChangeAction::INSERT )
3622 && ( _rEvent.Action != RowChangeAction::UPDATE )
3624 return bValid;
3626 // if some of the control models are bound to validators, check them
3627 OUString sInvalidityExplanation;
3628 Reference< XControlModel > xInvalidModel;
3629 if ( !checkFormComponentValidity( sInvalidityExplanation, xInvalidModel ) )
3631 Reference< XControl > xControl( locateControl( xInvalidModel ) );
3632 aGuard.clear();
3633 displayErrorSetFocus( sInvalidityExplanation, xControl, getDialogParentWindow() );
3634 return false;
3637 // check values on NULL and required flag
3638 if ( !lcl_shouldValidateRequiredFields_nothrow( _rEvent.Source ) )
3639 return true;
3641 OSL_ENSURE(m_pColumnInfoCache, "FormController::approveRowChange: no column infos!");
3642 if (!m_pColumnInfoCache)
3643 return true;
3647 if ( !m_pColumnInfoCache->controlsInitialized() )
3648 m_pColumnInfoCache->initializeControls( getControls() );
3650 size_t colCount = m_pColumnInfoCache->getColumnCount();
3651 for ( size_t col = 0; col < colCount; ++col )
3653 const ColumnInfo& rColInfo = m_pColumnInfoCache->getColumnInfo( col );
3655 if ( rColInfo.bAutoIncrement )
3656 continue;
3658 if ( rColInfo.bReadOnly )
3659 continue;
3661 if ( !rColInfo.xFirstControlWithInputRequired.is() && !rColInfo.xFirstGridWithInputRequiredColumn.is() )
3663 continue;
3666 // TODO: in case of binary fields, this "getString" below is extremely expensive
3667 if ( !rColInfo.xColumn->wasNull() || !rColInfo.xColumn->getString().isEmpty() )
3668 continue;
3670 OUString sMessage( SvxResId( RID_ERR_FIELDREQUIRED ) );
3671 sMessage = sMessage.replaceFirst( "#", rColInfo.sName );
3673 // the control to focus
3674 Reference< XControl > xControl( rColInfo.xFirstControlWithInputRequired );
3675 if ( !xControl.is() )
3676 xControl.set( rColInfo.xFirstGridWithInputRequiredColumn, UNO_QUERY );
3678 aGuard.clear();
3679 displayErrorSetFocus( sMessage, rColInfo.xFirstControlWithInputRequired, getDialogParentWindow() );
3680 return false;
3683 catch( const Exception& )
3685 DBG_UNHANDLED_EXCEPTION("svx");
3688 return true;
3692 sal_Bool SAL_CALL FormController::approveCursorMove(const EventObject& event)
3694 ::osl::MutexGuard aGuard( m_aMutex );
3695 impl_checkDisposed_throw();
3697 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aRowSetApproveListeners);
3698 if (aIter.hasMoreElements())
3700 EventObject aEvt(event);
3701 aEvt.Source = *this;
3702 return static_cast<XRowSetApproveListener*>(aIter.next())->approveCursorMove(aEvt);
3705 return true;
3709 sal_Bool SAL_CALL FormController::approveRowSetChange(const EventObject& event)
3711 ::osl::MutexGuard aGuard( m_aMutex );
3712 impl_checkDisposed_throw();
3714 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aRowSetApproveListeners);
3715 if (aIter.hasMoreElements())
3717 EventObject aEvt(event);
3718 aEvt.Source = *this;
3719 return static_cast<XRowSetApproveListener*>(aIter.next())->approveRowSetChange(aEvt);
3722 return true;
3725 // XRowSetApproveBroadcaster
3727 void SAL_CALL FormController::addRowSetApproveListener(const Reference< XRowSetApproveListener > & _rxListener)
3729 ::osl::MutexGuard aGuard( m_aMutex );
3730 impl_checkDisposed_throw();
3732 m_aRowSetApproveListeners.addInterface(_rxListener);
3736 void SAL_CALL FormController::removeRowSetApproveListener(const Reference< XRowSetApproveListener > & _rxListener)
3738 ::osl::MutexGuard aGuard( m_aMutex );
3739 impl_checkDisposed_throw();
3741 m_aRowSetApproveListeners.removeInterface(_rxListener);
3744 // XErrorListener
3746 void SAL_CALL FormController::errorOccured(const SQLErrorEvent& aEvent)
3748 ::osl::ClearableMutexGuard aGuard( m_aMutex );
3749 impl_checkDisposed_throw();
3751 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aErrorListeners);
3752 if (aIter.hasMoreElements())
3754 SQLErrorEvent aEvt(aEvent);
3755 aEvt.Source = *this;
3756 static_cast<XSQLErrorListener*>(aIter.next())->errorOccured(aEvt);
3758 else
3760 aGuard.clear();
3761 displayException(aEvent, getDialogParentWindow());
3765 // XErrorBroadcaster
3767 void SAL_CALL FormController::addSQLErrorListener(const Reference< XSQLErrorListener > & aListener)
3769 ::osl::MutexGuard aGuard( m_aMutex );
3770 impl_checkDisposed_throw();
3772 m_aErrorListeners.addInterface(aListener);
3776 void SAL_CALL FormController::removeSQLErrorListener(const Reference< XSQLErrorListener > & aListener)
3778 ::osl::MutexGuard aGuard( m_aMutex );
3779 impl_checkDisposed_throw();
3781 m_aErrorListeners.removeInterface(aListener);
3784 // XDatabaseParameterBroadcaster2
3786 void SAL_CALL FormController::addDatabaseParameterListener(const Reference< XDatabaseParameterListener > & aListener)
3788 ::osl::MutexGuard aGuard( m_aMutex );
3789 impl_checkDisposed_throw();
3791 m_aParameterListeners.addInterface(aListener);
3795 void SAL_CALL FormController::removeDatabaseParameterListener(const Reference< XDatabaseParameterListener > & aListener)
3797 ::osl::MutexGuard aGuard( m_aMutex );
3798 impl_checkDisposed_throw();
3800 m_aParameterListeners.removeInterface(aListener);
3803 // XDatabaseParameterBroadcaster
3805 void SAL_CALL FormController::addParameterListener(const Reference< XDatabaseParameterListener > & aListener)
3807 FormController::addDatabaseParameterListener( aListener );
3811 void SAL_CALL FormController::removeParameterListener(const Reference< XDatabaseParameterListener > & aListener)
3813 FormController::removeDatabaseParameterListener( aListener );
3816 // XDatabaseParameterListener
3818 sal_Bool SAL_CALL FormController::approveParameter(const DatabaseParameterEvent& aEvent)
3820 SolarMutexGuard aSolarGuard;
3821 ::osl::MutexGuard aGuard( m_aMutex );
3822 impl_checkDisposed_throw();
3824 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aParameterListeners);
3825 if (aIter.hasMoreElements())
3827 DatabaseParameterEvent aEvt(aEvent);
3828 aEvt.Source = *this;
3829 return static_cast<XDatabaseParameterListener*>(aIter.next())->approveParameter(aEvt);
3831 else
3833 // default handling: instantiate an interaction handler and let it handle the parameter request
3836 if ( !ensureInteractionHandler() )
3837 return false;
3839 // two continuations allowed: OK and Cancel
3840 OParameterContinuation* pParamValues = new OParameterContinuation;
3841 OInteractionAbort* pAbort = new OInteractionAbort;
3842 // the request
3843 ParametersRequest aRequest;
3844 aRequest.Parameters = aEvent.Parameters;
3845 aRequest.Connection = getConnection(Reference< XRowSet >(aEvent.Source, UNO_QUERY));
3846 OInteractionRequest* pParamRequest = new OInteractionRequest(makeAny(aRequest));
3847 Reference< XInteractionRequest > xParamRequest(pParamRequest);
3848 // some knittings
3849 pParamRequest->addContinuation(pParamValues);
3850 pParamRequest->addContinuation(pAbort);
3852 // handle the request
3853 m_xInteractionHandler->handle(xParamRequest);
3855 if (!pParamValues->wasSelected())
3856 // canceled
3857 return false;
3859 // transfer the values into the parameter supplier
3860 Sequence< PropertyValue > aFinalValues = pParamValues->getValues();
3861 if (aFinalValues.getLength() != aRequest.Parameters->getCount())
3863 OSL_FAIL("FormController::approveParameter: the InteractionHandler returned nonsense!");
3864 return false;
3866 const PropertyValue* pFinalValues = aFinalValues.getConstArray();
3867 for (sal_Int32 i=0; i<aFinalValues.getLength(); ++i, ++pFinalValues)
3869 Reference< XPropertySet > xParam(
3870 aRequest.Parameters->getByIndex(i), css::uno::UNO_QUERY);
3871 if (xParam.is())
3873 #ifdef DBG_UTIL
3874 OUString sName;
3875 xParam->getPropertyValue(FM_PROP_NAME) >>= sName;
3876 DBG_ASSERT(sName == pFinalValues->Name, "FormController::approveParameter: suspicious value names!");
3877 #endif
3878 try { xParam->setPropertyValue(FM_PROP_VALUE, pFinalValues->Value); }
3879 catch(Exception&)
3881 OSL_FAIL("FormController::approveParameter: setting one of the properties failed!");
3886 catch(Exception&)
3888 DBG_UNHANDLED_EXCEPTION("svx");
3891 return true;
3894 // XConfirmDeleteBroadcaster
3896 void SAL_CALL FormController::addConfirmDeleteListener(const Reference< XConfirmDeleteListener > & aListener)
3898 ::osl::MutexGuard aGuard( m_aMutex );
3899 impl_checkDisposed_throw();
3901 m_aDeleteListeners.addInterface(aListener);
3905 void SAL_CALL FormController::removeConfirmDeleteListener(const Reference< XConfirmDeleteListener > & aListener)
3907 ::osl::MutexGuard aGuard( m_aMutex );
3908 impl_checkDisposed_throw();
3910 m_aDeleteListeners.removeInterface(aListener);
3913 // XConfirmDeleteListener
3915 sal_Bool SAL_CALL FormController::confirmDelete(const RowChangeEvent& aEvent)
3917 ::osl::MutexGuard aGuard( m_aMutex );
3918 impl_checkDisposed_throw();
3920 ::comphelper::OInterfaceIteratorHelper2 aIter(m_aDeleteListeners);
3921 if (aIter.hasMoreElements())
3923 RowChangeEvent aEvt(aEvent);
3924 aEvt.Source = *this;
3925 return static_cast<XConfirmDeleteListener*>(aIter.next())->confirmDelete(aEvt);
3927 // default handling: instantiate an interaction handler and let it handle the request
3929 OUString sTitle;
3930 sal_Int32 nLength = aEvent.Rows;
3931 if ( nLength > 1 )
3933 sTitle = SvxResId( RID_STR_DELETECONFIRM_RECORDS );
3934 sTitle = sTitle.replaceFirst( "#", OUString::number(nLength) );
3936 else
3937 sTitle = SvxResId( RID_STR_DELETECONFIRM_RECORD );
3941 if ( !ensureInteractionHandler() )
3942 return false;
3944 // two continuations allowed: Yes and No
3945 OInteractionApprove* pApprove = new OInteractionApprove;
3946 OInteractionDisapprove* pDisapprove = new OInteractionDisapprove;
3948 // the request
3949 SQLWarning aWarning;
3950 aWarning.Message = sTitle;
3951 SQLWarning aDetails;
3952 aDetails.Message = SvxResId(RID_STR_DELETECONFIRM);
3953 aWarning.NextException <<= aDetails;
3955 OInteractionRequest* pRequest = new OInteractionRequest( makeAny( aWarning ) );
3956 Reference< XInteractionRequest > xRequest( pRequest );
3958 // some knittings
3959 pRequest->addContinuation( pApprove );
3960 pRequest->addContinuation( pDisapprove );
3962 // handle the request
3963 m_xInteractionHandler->handle( xRequest );
3965 if ( pApprove->wasSelected() )
3966 return true;
3968 catch( const Exception& )
3970 DBG_UNHANDLED_EXCEPTION("svx");
3973 return false;
3977 void SAL_CALL FormController::invalidateFeatures( const Sequence< ::sal_Int16 >& Features )
3979 ::osl::MutexGuard aGuard( m_aMutex );
3980 // for now, just copy the ids of the features, because...
3981 ::std::copy( Features.begin(), Features.end(),
3982 ::std::insert_iterator< ::std::set< sal_Int16 > >( m_aInvalidFeatures, m_aInvalidFeatures.begin() )
3985 // ... we will do the real invalidation asynchronously
3986 if ( !m_aFeatureInvalidationTimer.IsActive() )
3987 m_aFeatureInvalidationTimer.Start();
3991 void SAL_CALL FormController::invalidateAllFeatures( )
3993 ::osl::ClearableMutexGuard aGuard( m_aMutex );
3995 Sequence< sal_Int16 > aInterceptedFeatures( comphelper::mapKeysToSequence(m_aFeatureDispatchers) );
3997 aGuard.clear();
3998 if ( aInterceptedFeatures.hasElements() )
3999 invalidateFeatures( aInterceptedFeatures );
4003 Reference< XDispatch >
4004 FormController::interceptedQueryDispatch( const URL& aURL,
4005 const OUString& /*aTargetFrameName*/, sal_Int32 /*nSearchFlags*/)
4007 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
4008 Reference< XDispatch > xReturn;
4009 // dispatches handled by ourself
4010 if ( ( aURL.Complete == FMURL_CONFIRM_DELETION )
4011 || ( ( aURL.Complete == "private:/InteractionHandler" )
4012 && ensureInteractionHandler()
4015 xReturn = static_cast< XDispatch* >( this );
4017 // dispatches of FormSlot-URLs we have to translate
4018 if ( !xReturn.is() && m_xFormOperations.is() )
4020 // find the slot id which corresponds to the URL
4021 sal_Int32 nFeatureSlotId = svx::FeatureSlotTranslation::getControllerFeatureSlotIdForURL( aURL.Main );
4022 sal_Int16 nFormFeature = ( nFeatureSlotId != -1 ) ? svx::FeatureSlotTranslation::getFormFeatureForSlotId( nFeatureSlotId ) : -1;
4023 if ( nFormFeature > 0 )
4025 // get the dispatcher for this feature, create if necessary
4026 DispatcherContainer::const_iterator aDispatcherPos = m_aFeatureDispatchers.find( nFormFeature );
4027 if ( aDispatcherPos == m_aFeatureDispatchers.end() )
4029 aDispatcherPos = m_aFeatureDispatchers.emplace(
4030 nFormFeature, new svx::OSingleFeatureDispatcher( aURL, nFormFeature, m_xFormOperations, m_aMutex )
4031 ).first;
4034 OSL_ENSURE( aDispatcherPos->second.is(), "FormController::interceptedQueryDispatch: should have a dispatcher by now!" );
4035 return aDispatcherPos->second;
4039 // no more to offer
4040 return xReturn;
4044 void SAL_CALL FormController::dispatch( const URL& _rURL, const Sequence< PropertyValue >& _rArgs )
4046 if ( _rArgs.getLength() != 1 )
4048 OSL_FAIL( "FormController::dispatch: no arguments -> no dispatch!" );
4049 return;
4052 if ( _rURL.Complete == "private:/InteractionHandler" )
4054 Reference< XInteractionRequest > xRequest;
4055 OSL_VERIFY( _rArgs[0].Value >>= xRequest );
4056 if ( xRequest.is() )
4057 handle( xRequest );
4058 return;
4061 if ( _rURL.Complete == FMURL_CONFIRM_DELETION )
4063 OSL_FAIL( "FormController::dispatch: How do you expect me to return something via this call?" );
4064 // confirmDelete has a return value - dispatch hasn't
4065 return;
4068 OSL_FAIL( "FormController::dispatch: unknown URL!" );
4072 void SAL_CALL FormController::addStatusListener( const Reference< XStatusListener >& _rxListener, const URL& _rURL )
4074 if (_rURL.Complete == FMURL_CONFIRM_DELETION)
4076 if (_rxListener.is())
4077 { // send an initial statusChanged event
4078 FeatureStateEvent aEvent;
4079 aEvent.FeatureURL = _rURL;
4080 aEvent.IsEnabled = true;
4081 _rxListener->statusChanged(aEvent);
4082 // and don't add the listener at all (the status will never change)
4085 else
4086 OSL_FAIL("FormController::addStatusListener: invalid (unsupported) URL!");
4090 Reference< XInterface > SAL_CALL FormController::getParent()
4092 return m_xParent;
4096 void SAL_CALL FormController::setParent( const Reference< XInterface >& Parent)
4098 m_xParent = Parent;
4102 void SAL_CALL FormController::removeStatusListener( const Reference< XStatusListener >& /*_rxListener*/, const URL& _rURL )
4104 OSL_ENSURE(_rURL.Complete == FMURL_CONFIRM_DELETION, "FormController::removeStatusListener: invalid (unsupported) URL!");
4105 // we never really added the listener, so we don't need to remove it
4109 Reference< XDispatchProviderInterceptor > FormController::createInterceptor(const Reference< XDispatchProviderInterception > & _xInterception)
4111 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
4112 #ifdef DBG_UTIL
4113 // check if we already have an interceptor for the given object
4114 for ( const auto & it : m_aControlDispatchInterceptors )
4116 if (it->getIntercepted() == _xInterception)
4117 OSL_FAIL("FormController::createInterceptor : we already do intercept this objects dispatches !");
4119 #endif
4121 rtl::Reference<DispatchInterceptionMultiplexer> pInterceptor(new DispatchInterceptionMultiplexer( _xInterception, this ));
4122 m_aControlDispatchInterceptors.push_back( pInterceptor );
4124 return pInterceptor.get();
4128 bool FormController::ensureInteractionHandler()
4130 if ( m_xInteractionHandler.is() )
4131 return true;
4132 if ( m_bAttemptedHandlerCreation )
4133 return false;
4134 m_bAttemptedHandlerCreation = true;
4136 m_xInteractionHandler = InteractionHandler::createWithParent(m_xComponentContext,
4137 VCLUnoHelper::GetInterface(getDialogParentWindow()));
4138 return m_xInteractionHandler.is();
4142 void SAL_CALL FormController::handle( const Reference< XInteractionRequest >& _rRequest )
4144 if ( !ensureInteractionHandler() )
4145 return;
4146 m_xInteractionHandler->handle( _rRequest );
4150 void FormController::deleteInterceptor(const Reference< XDispatchProviderInterception > & _xInterception)
4152 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
4153 // search the interceptor responsible for the given object
4154 auto aIter = std::find_if(m_aControlDispatchInterceptors.begin(), m_aControlDispatchInterceptors.end(),
4155 [&_xInterception](const rtl::Reference<DispatchInterceptionMultiplexer>& rpInterceptor) {
4156 return rpInterceptor->getIntercepted() == _xInterception;
4158 if (aIter != m_aControlDispatchInterceptors.end())
4160 // log off the interception from its interception object
4161 (*aIter)->dispose();
4162 // remove the interceptor from our array
4163 m_aControlDispatchInterceptors.erase(aIter);
4168 void FormController::implInvalidateCurrentControlDependentFeatures()
4170 Sequence< sal_Int16 > aCurrentControlDependentFeatures(4);
4172 aCurrentControlDependentFeatures[0] = FormFeature::SortAscending;
4173 aCurrentControlDependentFeatures[1] = FormFeature::SortDescending;
4174 aCurrentControlDependentFeatures[2] = FormFeature::AutoFilter;
4175 aCurrentControlDependentFeatures[3] = FormFeature::RefreshCurrentControl;
4177 invalidateFeatures( aCurrentControlDependentFeatures );
4181 void SAL_CALL FormController::columnChanged( const EventObject& /*_event*/ )
4183 implInvalidateCurrentControlDependentFeatures();
4188 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */