1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
28 #include <svx/dialmgr.hxx>
29 #include <svx/strings.hrc>
30 #include <fmservs.hxx>
31 #include <svx/fmtools.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>
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
) ) );
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
;
211 // information about the column itself
212 Reference
< XColumn
> xColumn
;
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
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
;
233 ,nNullable( ColumnValue::NULLABLE_UNKNOWN
)
234 ,bAutoIncrement( false )
237 ,xFirstControlWithInputRequired()
238 ,xFirstGridWithInputRequiredColumn()
239 ,nRequiredGridColumn( -1 )
244 class ColumnInfoCache
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();
257 typedef ::std::vector
< ColumnInfo
> ColumnInfos
;
258 ColumnInfos m_aColumns
;
259 bool m_bControlsInitialized
;
263 ColumnInfoCache::ColumnInfoCache( const Reference
< XColumnsSupplier
>& _rxColSupplier
)
265 ,m_bControlsInitialized( false )
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
)
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");
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() )
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
);
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
374 if ( gridCol
< gridColCount
)
376 // found a grid column which is bound to the given
377 rCol
.xFirstGridWithInputRequiredColumn
= xGrid
;
378 rCol
.nRequiredGridColumn
= gridCol
;
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
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
;
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
;
443 Reference
< XPropertySet
> xField
;
444 Reference
< XTextComponent
> xText
;
446 FmFieldInfo(const Reference
< XPropertySet
>& _xField
, const Reference
< XTextComponent
>& _xText
)
449 {xField
->getPropertyValue(FM_PROP_NAME
) >>= aFieldName
;}
452 class FmXAutoControl
: public UnoControl
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
;
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
);
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
)
487 UnoControl::ImplSetPeerProperty( rPropName
, rVal
);
491 IMPL_LINK_NOARG( FormController
, OnActivateTabOrder
, Timer
*, void )
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
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
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
)
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)
544 ,m_bDBConnection(false)
548 ,m_bCommitLock(false)
550 ,m_bControlsSorted(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
);
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()
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*/)
671 void SAL_CALL
FormController::resetted(const EventObject
& rEvent
)
673 ::osl::MutexGuard
aGuard(m_aMutex
);
674 if (getCurrentControl().is() && (getCurrentControl()->getModel() == rEvent
.Source
))
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"
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
);
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
730 if ( m_nCurrentFilterPosition
< 0 )
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*/ )
755 void FormController::setFastPropertyValue_NoBroadcast( sal_Int32
/*nHandle*/, const Any
& /*rValue*/ )
760 void FormController::getFastPropertyValue( Any
& rValue
, sal_Int32 nHandle
) const
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
)
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();
826 case FM_ATTR_FORM_OPERATIONS
:
827 rValue
<<= m_xFormOperations
;
833 Reference
< XPropertySetInfo
> FormController::getPropertySetInfo()
835 static Reference
< XPropertySetInfo
> xInfo( createPropertySetInfo( getInfoHelper() ) );
840 void FormController::fillProperties(
841 Sequence
< Property
>& /* [out] */ _rProps
,
842 Sequence
< Property
>& /* [out] */ /*_rAggregateProps*/
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();
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
;
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
;
945 aExpressions
[ termIndex
] = aConjunction
;
953 void SAL_CALL
FormController::removeDisjunctiveTerm( ::sal_Int32 Term
)
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
;
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();
985 aEvent
.Source
= *this;
986 aEvent
.DisjunctiveTerm
= Term
;
990 m_aFilterListeners
.notifyEach( &XFilterControllerListener::disjunctiveTermRemoved
, aEvent
);
994 void SAL_CALL
FormController::appendEmptyDisjunctiveTerm()
997 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
998 impl_checkDisposed_throw();
1000 impl_appendEmptyFilterRow( aGuard
);
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() )
1025 m_nCurrentFilterPosition
= ActiveTerm
;
1026 impl_setTextOnAllFilter_throw();
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);
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
);
1065 Index
>= static_cast<sal_Int32
>(m_aChildren
.size()))
1066 throw IndexOutOfBoundsException();
1068 return makeAny( m_aChildren
[ Index
] );
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
> ());
1084 // has a control been disposed
1085 Reference
< XControl
> xControl(e
.Source
, UNO_QUERY
);
1088 if (getContainer().is())
1089 removeControl(xControl
);
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();
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
;
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
);
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
)
1173 setContainer( nullptr );
1174 setModel( nullptr );
1175 setParent( nullptr );
1177 ::comphelper::disposeComponent( m_xComposer
);
1179 m_bDBConnection
= false;
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
);
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
);
1214 xProp
->removePropertyChangeListener(FM_PROP_BOUNDFIELD
, this);
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
);
1228 m_bCurrentRecordNew
= ::comphelper::getBOOL(evt
.NewValue
);
1230 // toggle the locking
1231 if (m_bLocked
!= determineLockState())
1233 m_bLocked
= !m_bLocked
;
1235 if (isListeningForChanges())
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
);
1252 m_aControlBorderManager
.enableDynamicBorderColor();
1253 if ( m_xActiveControl
.is() )
1254 m_aControlBorderManager
.focusGained( m_xActiveControl
.get() );
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
) );
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
);
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();
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
];
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?
1351 && ::comphelper::hasProperty( FM_PROP_AUTOINCREMENT
, xField
)
1352 && ::comphelper::getBOOL( xField
->getPropertyValue( FM_PROP_AUTOINCREMENT
) )
1355 replaceControl( xControl
, new FmXAutoControl() );
1360 m_bAttachEvents
= true;
1364 m_bDetachEvents
= false;
1365 for (sal_Int32 i
= nControls
; i
> 0;)
1367 Reference
< XControl
> xControl
= pControls
[--i
];
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?
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
);
1405 void SAL_CALL
FormController::textChanged(const TextEvent
& e
)
1408 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
1409 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1410 if ( !m_bFiltering
)
1416 if ( m_bSuspendFilterTextListening
)
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!" );
1432 FmFilterRow
& rRow
= m_aFilterRows
[ m_nCurrentFilterPosition
];
1434 // do we have a new filter
1435 if (!aText
.isEmpty())
1436 rRow
[xText
] = aText
;
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())
1446 // multiplex the event to our FilterControllerListeners
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
;
1456 // notify the changed filter expression
1457 m_aFilterListeners
.notifyEach( &XFilterControllerListener::predicateExpressionChanged
, aEvent
);
1462 void SAL_CALL
FormController::itemStateChanged(const ItemEvent
& /*rEvent*/)
1464 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
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");
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
);
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
)
1551 impl_addFilterRow( FmFilterRow() );
1553 // notify the listeners
1555 aEvent
.Source
= *this;
1556 aEvent
.DisjunctiveTerm
= static_cast<sal_Int32
>(m_aFilterRows
.size()) - 1;
1557 _rClearBeforeNotify
.clear();
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
))
1574 return !(m_bCanInsert
&& m_bCurrentRecordNew
)
1575 && (xResultSet
->isBeforeFirst() || xResultSet
->isAfterLast() || xResultSet
->rowDeleted() || !m_bCanUpdate
);
1580 void FormController::focusGained(const FocusEvent
& e
)
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();
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 ...
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
);
1637 xWindow
->setFocus();
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
);
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");
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!" );
1691 // (asynchronously) call activation handlers
1692 m_aActivationEvent
.Call();
1694 // call modify listeners
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() )
1706 // control gets focus, then possibly in the visible range
1707 Reference
< XFormControllerContext
> xContext( m_xFormControllerContext
);
1708 Reference
< XControl
> xCurrentControl( m_xCurrentControl
);
1712 if ( xContext
.is() )
1713 xContext
->makeVisible( xCurrentControl
);
1717 IMPL_LINK_NOARG( FormController
, OnActivated
, void*, void )
1720 aEvent
.Source
= *this;
1721 m_aActivateListeners
.notifyEach( &XFormControllerListener::formActivated
, aEvent
);
1725 IMPL_LINK_NOARG( FormController
, OnDeactivated
, void*, void )
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
);
1804 Reference
< XLoadable
> xForm(m_xModelAsIndex
, UNO_QUERY
);
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
);
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
);
1865 Reference
< XPropertySet
> xModelProps( m_xModelAsIndex
, UNO_QUERY
);
1866 Reference
< XPropertySetInfo
> xPropInfo( xModelProps
->getPropertySetInfo() );
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();
1879 m_aControlBorderManager
.disableDynamicBorderColor();
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
;
1926 m_xModelAsIndex
->getByIndex(--nPos
) >>= xTemp
;
1927 if (xComp
.get() == xTemp
.get())
1929 m_xModelAsManager
->attach( nPos
, Reference
<XInterface
>( xControl
, UNO_QUERY
), makeAny(xControl
) );
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
;
1953 m_xModelAsIndex
->getByIndex(--nPos
) >>= xTemp
;
1954 if (xComp
.get() == xTemp
.get())
1956 m_xModelAsManager
->detach( nPos
, Reference
<XInterface
>( xControl
, UNO_QUERY
) );
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())
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
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
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();
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();
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
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
2088 aNewControls
.realloc( j
);
2090 m_aControls
= aNewControls
;
2091 m_bControlsSorted
= true;
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();
2125 // a. if the entire record is locked
2126 // b. if the associated field is locked
2127 Reference
< XBoundControl
> xBound(xControl
, UNO_QUERY
);
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
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
));
2145 Reference
< XPropertySet
> xField
;
2146 xSet
->getPropertyValue(FM_PROP_BOUNDFIELD
) >>= xField
;
2150 xBound
->setLock(bLocked
);
2155 Any aVal
= xField
->getPropertyValue(FM_PROP_ISREADONLY
);
2156 if (aVal
.hasValue() && ::comphelper::getBOOL(aVal
))
2157 xBound
->setLock(true);
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
);
2185 bool lcl_shouldListenForModifications( const Reference
< XControl
>& _rxControl
, const Reference
< XPropertyChangeListener
>& _rxBoundFieldListener
)
2187 bool bShould
= false;
2189 Reference
< XBoundComponent
> xBound( _rxControl
, UNO_QUERY
);
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
);
2213 void FormController::startControlModifyListening(const Reference
< XControl
> & xControl
)
2215 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2217 bool bModifyListening
= lcl_shouldListenForModifications( xControl
, this );
2220 while ( bModifyListening
)
2222 Reference
< XModifyBroadcaster
> xMod(xControl
, UNO_QUERY
);
2225 xMod
->addModifyListener(this);
2229 // all the text to prematurely recognize a modified
2230 Reference
< XTextComponent
> xText(xControl
, UNO_QUERY
);
2233 xText
->addTextListener(this);
2237 Reference
< XCheckBox
> xBox(xControl
, UNO_QUERY
);
2240 xBox
->addItemListener(this);
2244 Reference
< XComboBox
> xCbBox(xControl
, UNO_QUERY
);
2247 xCbBox
->addItemListener(this);
2251 Reference
< XListBox
> xListBox(xControl
, UNO_QUERY
);
2254 xListBox
->addItemListener(this);
2262 void FormController::stopControlModifyListening(const Reference
< XControl
> & xControl
)
2264 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2266 bool bModifyListening
= lcl_shouldListenForModifications( xControl
, nullptr );
2269 while (bModifyListening
)
2271 Reference
< XModifyBroadcaster
> xMod(xControl
, UNO_QUERY
);
2274 xMod
->removeModifyListener(this);
2277 // all the text to prematurely recognize a modified
2278 Reference
< XTextComponent
> xText(xControl
, UNO_QUERY
);
2281 xText
->removeTextListener(this);
2285 Reference
< XCheckBox
> xBox(xControl
, UNO_QUERY
);
2288 xBox
->removeItemListener(this);
2292 Reference
< XComboBox
> xCbBox(xControl
, UNO_QUERY
);
2295 xCbBox
->removeItemListener(this);
2299 Reference
< XListBox
> xListBox(xControl
, UNO_QUERY
);
2302 xListBox
->removeItemListener(this);
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
);
2345 auto i
= static_cast<sal_Int32
>(std::distance(_rControls
.begin(), pControls
));
2346 ::comphelper::removeElementAt( _rControls
, i
);
2348 else if ( _bOverWrite
)
2352 return Reference
< XControl
> ();
2356 void FormController::implControlInserted( const Reference
< XControl
>& _rxControl
, bool _bAddToEventAttacher
)
2358 Reference
< XWindow
> xWindow( _rxControl
, UNO_QUERY
);
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
);
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
);
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
);
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() )
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
);
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
);
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();
2518 m_bCanInsert
= m_bCanUpdate
= m_bCycle
= false;
2519 m_bCurrentRecordModified
= false;
2520 m_bCurrentRecordNew
= false;
2523 m_bDBConnection
= true;
2527 m_bDBConnection
= false;
2528 m_bCanInsert
= m_bCanUpdate
= m_bCycle
= false;
2529 m_bCurrentRecordModified
= false;
2530 m_bCurrentRecordNew
= 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
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();
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();
2589 void FormController::reloaded(const EventObject
& aEvent
)
2591 ::osl::MutexGuard
aGuard( m_aMutex
);
2592 impl_checkDisposed_throw();
2598 void FormController::unloading(const EventObject
& /*aEvent*/)
2600 ::osl::MutexGuard
aGuard( m_aMutex
);
2601 impl_checkDisposed_throw();
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())
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
);
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
);
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
);
2694 xApprove
->removeRowSetApproveListener(this);
2696 Reference
< XRowSet
> xRowSet( _rxForm
, UNO_QUERY
);
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
;
2724 if (isListeningForChanges())
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() )
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
;
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
) )
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();
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
;
2937 m_xModelAsIndex
->getByIndex(--nPos
) >>= xTemp
;
2938 if ( xFormOfChild
== xTemp
)
2940 m_xModelAsManager
->attach( nPos
, Reference
<XInterface
>( ChildController
, UNO_QUERY
), makeAny( ChildController
) );
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
));
2989 Reference
< XMultiServiceFactory
> xFactory( xConnection
, UNO_QUERY_THROW
);
2991 xFactory
->createInstance("com.sun.star.sdb.SingleSelectQueryComposer"),
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
)
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
;
3054 // first look with the given name
3055 if (xQueryColumns
->hasByName(rRefValue
.Name
))
3057 xQueryColumns
->getByName(rRefValue
.Name
) >>= xSet
;
3060 xSet
->getPropertyValue("RealName") >>= aRealName
;
3062 // compare the condition field name and the RealName
3063 if (aCompare(aRealName
, rRefValue
.Name
))
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
3085 catch (const Exception
&)
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
;
3107 OUString sPredicate
,sErrorMsg
;
3108 rRefValue
.Value
>>= sPredicate
;
3109 std::shared_ptr
< OSQLParseNode
> pParseNode
= predicateTree(sErrorMsg
, sPredicate
, xFormatter
, xField
);
3110 if ( pParseNode
!= nullptr )
3113 switch (rRefValue
.Handle
)
3115 case css::sdb::SQLFilterOperator::EQUAL
:
3118 case css::sdb::SQLFilterOperator::NOT_EQUAL
:
3121 case css::sdb::SQLFilterOperator::LESS
:
3124 case css::sdb::SQLFilterOperator::GREATER
:
3127 case css::sdb::SQLFilterOperator::LESS_EQUAL
:
3130 case css::sdb::SQLFilterOperator::GREATER_EQUAL
:
3133 case css::sdb::SQLFilterOperator::LIKE
:
3134 sCriteria
+= "LIKE ";
3136 case css::sdb::SQLFilterOperator::NOT_LIKE
:
3137 sCriteria
+= "NOT LIKE ";
3139 case css::sdb::SQLFilterOperator::SQLNULL
:
3140 sCriteria
+= "IS NULL";
3142 case css::sdb::SQLFilterOperator::NOT_SQLNULL
:
3143 sCriteria
+= "IS NOT NULL";
3146 pParseNode
->parseNodeToPredicateStr( sCriteria
3152 ,strDecimalSeparator
3153 ,getParseContext());
3154 aRow
[rFieldInfo
.xText
] = sCriteria
;
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
3185 // stop listening for controls
3186 if (isListeningForChanges())
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
];
3213 // no events for the control anymore
3214 removeFromEventAttacher(xControl
);
3216 // do we have a mode selector
3217 Reference
< XModeSelector
> xSelector(xControl
, UNO_QUERY
);
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);
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
;
3267 // may we filter the field?
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() ),
3281 if ( replaceControl( xControl
, xFilterControl
) )
3283 Reference
< XTextComponent
> xFilterText( xFilterControl
, UNO_QUERY
);
3284 aFieldInfos
.emplace_back( xField
, xFilterText
);
3285 xFilterText
->addTextListener(this);
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
);
3302 stopFormListening( xSet
, true );
3304 impl_setTextOnAllFilter_throw();
3306 // lock all controls which are not used for filtering
3307 m_bLocked
= determineLockState();
3309 m_bAttachEvents
= true;
3313 void FormController::stopFiltering()
3315 OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
3316 if ( !m_bFiltering
) // #104693# OJ
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
];
3340 // now enable event handling again
3341 addToEventAttacher(xControl
);
3343 Reference
< XModeSelector
> xSelector(xControl
, UNO_QUERY
);
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);
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?
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
);
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();
3391 // restart listening for control modifications
3392 if (isListeningForChanges())
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
)
3411 if ( Mode
== "FilterMode" )
3416 for (const auto& rChild
: m_aChildren
)
3418 Reference
< XModeSelector
> xMode(rChild
, UNO_QUERY
);
3420 xMode
->setMode(Mode
);
3425 OUString SAL_CALL
FormController::getMode()
3427 ::osl::MutexGuard
aGuard( m_aMutex
);
3428 impl_checkDisposed_throw();
3434 Sequence
< OUString
> SAL_CALL
FormController::getSupportedModes()
3436 ::osl::MutexGuard
aGuard( m_aMutex
);
3437 impl_checkDisposed_throw();
3439 static Sequence
< OUString
> const 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() )
3488 Reference
< XValidatableFormComponent
> xValidatable
;
3489 while ( xControlEnumeration
->hasMoreElements() )
3491 if ( !( xControlEnumeration
->nextElement() >>= xValidatable
) )
3492 // control does not support validation
3495 if ( xValidatable
->isValid() )
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 ...
3504 _rFirstInvalidityExplanation
= xValidator
->explainInvalid( xValidatable
->getCurrentValue() );
3505 _rxFirstInvalidModel
.set(xValidatable
, css::uno::UNO_QUERY
);
3509 catch( const Exception
& )
3511 DBG_UNHANDLED_EXCEPTION("svx");
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?" );
3528 if ( control
->getModel() == _rxModel
)
3532 OSL_FAIL( "FormController::locateControl: did not find a control for this model!" );
3534 catch( const Exception
& )
3536 DBG_UNHANDLED_EXCEPTION("svx");
3544 void displayErrorSetFocus( const OUString
& _rMessage
, const Reference
< XControl
>& _rxFocusControl
, vcl::Window
* _pDialogParent
)
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
3585 Reference
< XPropertySet
> xDataSourceSettings(
3586 xDataSource
->getPropertyValue("Settings"),
3589 bool bShouldValidate
= true;
3590 OSL_VERIFY( xDataSourceSettings
->getPropertyValue( s_sFormsCheckRequiredFields
) >>= bShouldValidate
);
3591 return bShouldValidate
;
3593 catch( const Exception
& )
3595 DBG_UNHANDLED_EXCEPTION("svx");
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
);
3611 if (aIter
.hasMoreElements())
3613 RowChangeEvent
aEvt( _rEvent
);
3614 aEvt
.Source
= *this;
3615 bValid
= static_cast<XRowSetApproveListener
*>(aIter
.next())->approveRowChange(aEvt
);
3621 if ( ( _rEvent
.Action
!= RowChangeAction::INSERT
)
3622 && ( _rEvent
.Action
!= RowChangeAction::UPDATE
)
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
) );
3633 displayErrorSetFocus( sInvalidityExplanation
, xControl
, getDialogParentWindow() );
3637 // check values on NULL and required flag
3638 if ( !lcl_shouldValidateRequiredFields_nothrow( _rEvent
.Source
) )
3641 OSL_ENSURE(m_pColumnInfoCache
, "FormController::approveRowChange: no column infos!");
3642 if (!m_pColumnInfoCache
)
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
)
3658 if ( rColInfo
.bReadOnly
)
3661 if ( !rColInfo
.xFirstControlWithInputRequired
.is() && !rColInfo
.xFirstGridWithInputRequiredColumn
.is() )
3666 // TODO: in case of binary fields, this "getString" below is extremely expensive
3667 if ( !rColInfo
.xColumn
->wasNull() || !rColInfo
.xColumn
->getString().isEmpty() )
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
);
3679 displayErrorSetFocus( sMessage
, rColInfo
.xFirstControlWithInputRequired
, getDialogParentWindow() );
3683 catch( const Exception
& )
3685 DBG_UNHANDLED_EXCEPTION("svx");
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
);
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
);
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
);
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
);
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
);
3833 // default handling: instantiate an interaction handler and let it handle the parameter request
3836 if ( !ensureInteractionHandler() )
3839 // two continuations allowed: OK and Cancel
3840 OParameterContinuation
* pParamValues
= new OParameterContinuation
;
3841 OInteractionAbort
* pAbort
= new OInteractionAbort
;
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
);
3849 pParamRequest
->addContinuation(pParamValues
);
3850 pParamRequest
->addContinuation(pAbort
);
3852 // handle the request
3853 m_xInteractionHandler
->handle(xParamRequest
);
3855 if (!pParamValues
->wasSelected())
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!");
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
);
3875 xParam
->getPropertyValue(FM_PROP_NAME
) >>= sName
;
3876 DBG_ASSERT(sName
== pFinalValues
->Name
, "FormController::approveParameter: suspicious value names!");
3878 try { xParam
->setPropertyValue(FM_PROP_VALUE
, pFinalValues
->Value
); }
3881 OSL_FAIL("FormController::approveParameter: setting one of the properties failed!");
3888 DBG_UNHANDLED_EXCEPTION("svx");
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
3930 sal_Int32 nLength
= aEvent
.Rows
;
3933 sTitle
= SvxResId( RID_STR_DELETECONFIRM_RECORDS
);
3934 sTitle
= sTitle
.replaceFirst( "#", OUString::number(nLength
) );
3937 sTitle
= SvxResId( RID_STR_DELETECONFIRM_RECORD
);
3941 if ( !ensureInteractionHandler() )
3944 // two continuations allowed: Yes and No
3945 OInteractionApprove
* pApprove
= new OInteractionApprove
;
3946 OInteractionDisapprove
* pDisapprove
= new OInteractionDisapprove
;
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
);
3959 pRequest
->addContinuation( pApprove
);
3960 pRequest
->addContinuation( pDisapprove
);
3962 // handle the request
3963 m_xInteractionHandler
->handle( xRequest
);
3965 if ( pApprove
->wasSelected() )
3968 catch( const Exception
& )
3970 DBG_UNHANDLED_EXCEPTION("svx");
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
) );
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
)
4034 OSL_ENSURE( aDispatcherPos
->second
.is(), "FormController::interceptedQueryDispatch: should have a dispatcher by now!" );
4035 return aDispatcherPos
->second
;
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!" );
4052 if ( _rURL
.Complete
== "private:/InteractionHandler" )
4054 Reference
< XInteractionRequest
> xRequest
;
4055 OSL_VERIFY( _rArgs
[0].Value
>>= xRequest
);
4056 if ( xRequest
.is() )
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
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)
4086 OSL_FAIL("FormController::addStatusListener: invalid (unsupported) URL!");
4090 Reference
< XInterface
> SAL_CALL
FormController::getParent()
4096 void SAL_CALL
FormController::setParent( const Reference
< XInterface
>& 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!" );
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 !");
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() )
4132 if ( m_bAttemptedHandlerCreation
)
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() )
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: */