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