LibreOffice Module svx (master)  1
formcontroller.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 
22 #include <fmcontrollayout.hxx>
23 #include <formcontroller.hxx>
26 #include <formcontrolling.hxx>
27 #include <fmprop.hxx>
28 #include <svx/dialmgr.hxx>
29 #include <svx/strings.hrc>
30 #include <fmservs.hxx>
31 #include <svx/fmtools.hxx>
32 #include <fmurl.hxx>
33 
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>
66 
70 #include <comphelper/property.hxx>
71 #include <comphelper/sequence.hxx>
72 #include <comphelper/flagguard.hxx>
73 #include <comphelper/types.hxx>
76 #include <connectivity/dbtools.hxx>
80 #include <tools/debug.hxx>
81 #include <tools/diagnose_ex.h>
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>
88 
89 #include <algorithm>
90 #include <iterator>
91 
92 using namespace ::com::sun::star;
93 using namespace ::comphelper;
94 using namespace ::connectivity;
95 using namespace ::dbtools;
96 
97 
98 css::uno::Reference< css::uno::XInterface >
99  FormController_NewInstance_Impl( const css::uno::Reference< css::lang::XMultiServiceFactory > & _rxORB )
100 {
101  return *( new ::svxform::FormController( comphelper::getComponentContext(_rxORB) ) );
102 }
103 
104 namespace svxform
105 {
106 
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;
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;
195 
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;
201 
202 namespace {
203 
204 struct ColumnInfo
205 {
206  // information about the column itself
207  Reference< XColumn > xColumn;
208  sal_Int32 nNullable;
210  bool bReadOnly;
211  OUString sName;
212 
213  // information about the control(s) bound to this column
214 
216  Reference< XControl > xFirstControlWithInputRequired;
225 
226  ColumnInfo()
227  :nNullable( ColumnValue::NULLABLE_UNKNOWN )
228  ,bAutoIncrement( false )
229  ,bReadOnly( false )
230  ,nRequiredGridColumn( -1 )
231  {
232  }
233 };
234 
235 }
236 
238 {
239 public:
240  explicit ColumnInfoCache( const Reference< XColumnsSupplier >& _rxColSupplier );
241 
242  size_t getColumnCount() const { return m_aColumns.size(); }
243  const ColumnInfo& getColumnInfo( size_t _pos );
244 
246  void initializeControls( const Sequence< Reference< XControl > >& _rControls );
247  void deinitializeControls();
248 
249 private:
250  typedef ::std::vector< ColumnInfo > ColumnInfos;
251  ColumnInfos m_aColumns;
253 };
254 
255 
256 ColumnInfoCache::ColumnInfoCache( const Reference< XColumnsSupplier >& _rxColSupplier )
257  :m_bControlsInitialized( false )
258 {
259  try
260  {
261  m_aColumns.clear();
262 
263  Reference< XIndexAccess > xColumns( _rxColSupplier->getColumns(), UNO_QUERY_THROW );
264  sal_Int32 nColumnCount = xColumns->getCount();
265  m_aColumns.reserve( nColumnCount );
266 
267  Reference< XPropertySet > xColumnProps;
268  for ( sal_Int32 i = 0; i < nColumnCount; ++i )
269  {
270  ColumnInfo aColInfo;
271  aColInfo.xColumn.set( xColumns->getByIndex(i), UNO_QUERY_THROW );
272 
273  xColumnProps.set( aColInfo.xColumn, UNO_QUERY_THROW );
274  OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_ISNULLABLE ) >>= aColInfo.nNullable );
275  OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_AUTOINCREMENT ) >>= aColInfo.bAutoIncrement );
276  OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_NAME ) >>= aColInfo.sName );
277  OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_ISREADONLY ) >>= aColInfo.bReadOnly );
278 
279  m_aColumns.push_back( aColInfo );
280  }
281  }
282  catch( const Exception& )
283  {
285  }
286 }
287 
288 
289 namespace
290 {
291  bool lcl_isBoundTo( const Reference< XPropertySet >& _rxControlModel, const Reference< XInterface >& _rxNormDBField )
292  {
293  Reference< XInterface > xNormBoundField( _rxControlModel->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY );
294  return ( xNormBoundField == _rxNormDBField );
295  }
296 
297  bool lcl_isInputRequired( const Reference< XPropertySet >& _rxControlModel )
298  {
299  bool bInputRequired = false;
300  OSL_VERIFY( _rxControlModel->getPropertyValue( FM_PROP_INPUT_REQUIRED ) >>= bInputRequired );
301  return bInputRequired;
302  }
303 
304  void lcl_resetColumnControlInfo( ColumnInfo& _rColInfo )
305  {
306  _rColInfo.xFirstControlWithInputRequired.clear();
307  _rColInfo.xFirstGridWithInputRequiredColumn.clear();
308  _rColInfo.nRequiredGridColumn = -1;
309  }
310 }
311 
312 
314 {
315  for (auto& rCol : m_aColumns)
316  {
317  lcl_resetColumnControlInfo( rCol );
318  }
319  m_bControlsInitialized = false;
320 }
321 
322 
323 void ColumnInfoCache::initializeControls( const Sequence< Reference< XControl > >& _rControls )
324 {
325  try
326  {
327  // for every of our known columns, find the controls which are bound to this column
328  for (auto& rCol : m_aColumns)
329  {
330  OSL_ENSURE( !rCol.xFirstControlWithInputRequired.is() && !rCol.xFirstGridWithInputRequiredColumn.is()
331  && ( rCol.nRequiredGridColumn == -1 ), "ColumnInfoCache::initializeControls: called me twice?" );
332 
333  lcl_resetColumnControlInfo( rCol );
334 
335  Reference< XInterface > xNormColumn( rCol.xColumn, UNO_QUERY_THROW );
336 
337  const Reference< XControl >* pControl( _rControls.getConstArray() );
338  const Reference< XControl >* pControlEnd( pControl + _rControls.getLength() );
339  for ( ; pControl != pControlEnd; ++pControl )
340  {
341  if ( !pControl->is() )
342  continue;
343 
344  Reference< XPropertySet > xModel( (*pControl)->getModel(), UNO_QUERY_THROW );
345  Reference< XPropertySetInfo > xModelPSI( xModel->getPropertySetInfo(), UNO_SET_THROW );
346 
347  // special handling for grid controls
348  Reference< XGrid > xGrid( *pControl, UNO_QUERY );
349  if ( xGrid.is() )
350  {
351  Reference< XIndexAccess > xGridColAccess( xModel, UNO_QUERY_THROW );
352  sal_Int32 gridColCount = xGridColAccess->getCount();
353  sal_Int32 gridCol = 0;
354  for ( gridCol = 0; gridCol < gridColCount; ++gridCol )
355  {
356  Reference< XPropertySet > xGridColumnModel( xGridColAccess->getByIndex( gridCol ), UNO_QUERY_THROW );
357 
358  if ( !lcl_isBoundTo( xGridColumnModel, xNormColumn )
359  || !lcl_isInputRequired( xGridColumnModel )
360  )
361  continue; // with next grid column
362 
363  break;
364  }
365 
366  if ( gridCol < gridColCount )
367  {
368  // found a grid column which is bound to the given
369  rCol.xFirstGridWithInputRequiredColumn = xGrid;
370  rCol.nRequiredGridColumn = gridCol;
371  break;
372  }
373 
374  continue; // with next control
375  }
376 
377  if ( !xModelPSI->hasPropertyByName( FM_PROP_BOUNDFIELD )
378  || !lcl_isBoundTo( xModel, xNormColumn )
379  || !lcl_isInputRequired( xModel )
380  )
381  continue; // with next control
382 
383  break;
384  }
385 
386  if ( pControl == pControlEnd )
387  // did not find a control which is bound to this particular column, and for which the input is required
388  continue; // with next DB column
389 
390  rCol.xFirstControlWithInputRequired = *pControl;
391  }
392  }
393  catch( const Exception& )
394  {
396  }
397 
398  m_bControlsInitialized = true;
399 }
400 
401 
402 const ColumnInfo& ColumnInfoCache::getColumnInfo( size_t _pos )
403 {
404  if ( _pos >= m_aColumns.size() )
405  throw IndexOutOfBoundsException();
406 
407  return m_aColumns[ _pos ];
408 }
409 
410 namespace {
411 
412 class OParameterContinuation : public OInteraction< XInteractionSupplyParameters >
413 {
414  Sequence< PropertyValue > m_aValues;
415 
416 public:
417  OParameterContinuation() { }
418 
419  const Sequence< PropertyValue >& getValues() const { return m_aValues; }
420 
421 // XInteractionSupplyParameters
422  virtual void SAL_CALL setParameters( const Sequence< PropertyValue >& _rValues ) override;
423 };
424 
425 }
426 
427 void SAL_CALL OParameterContinuation::setParameters( const Sequence< PropertyValue >& _rValues )
428 {
429  m_aValues = _rValues;
430 }
431 
432 
433 // FmXAutoControl
434 
436 {
437  OUString aFieldName;
439  Reference< XTextComponent > xText;
440 
441  FmFieldInfo(const Reference< XPropertySet >& _xField, const Reference< XTextComponent >& _xText)
442  :xField(_xField)
443  ,xText(_xText)
444  {xField->getPropertyValue(FM_PROP_NAME) >>= aFieldName;}
445 };
446 
447 namespace {
448 
449 class FmXAutoControl: public UnoControl
450 
451 {
452 public:
453  FmXAutoControl()
454  {
455  }
456 
457  virtual OUString GetComponentServiceName() override {return "Edit";}
458  virtual void SAL_CALL createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) override;
459 
460 protected:
461  virtual void ImplSetPeerProperty( const OUString& rPropName, const Any& rVal ) override;
462 };
463 
464 }
465 
466 void FmXAutoControl::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer )
467 {
468  UnoControl::createPeer( rxToolkit, rParentPeer );
469 
470  Reference< XTextComponent > xText(getPeer() , UNO_QUERY);
471  if (xText.is())
472  {
473  xText->setText(SvxResId(RID_STR_AUTOFIELD));
474  xText->setEditable(false);
475  }
476 }
477 
478 
479 void FmXAutoControl::ImplSetPeerProperty( const OUString& rPropName, const Any& rVal )
480 {
481  // these properties are ignored
482  if (rPropName == FM_PROP_TEXT)
483  return;
484 
485  UnoControl::ImplSetPeerProperty( rPropName, rVal );
486 }
487 
488 
489 IMPL_LINK_NOARG( FormController, OnActivateTabOrder, Timer*, void )
490 {
491  activateTabOrder();
492 }
493 
494 namespace {
495 
496 struct UpdateAllListeners
497 {
498  bool operator()( const Reference< XDispatch >& _rxDispatcher ) const
499  {
500  static_cast< svx::OSingleFeatureDispatcher* >( _rxDispatcher.get() )->updateAllListeners();
501  // the return is a dummy only so we can use this struct in a lambda expression
502  return true;
503  }
504 };
505 
506 }
507 
508 IMPL_LINK_NOARG( FormController, OnInvalidateFeatures, Timer*, void )
509 {
510  ::osl::MutexGuard aGuard( m_aMutex );
511  for (const auto& rFeature : m_aInvalidFeatures)
512  {
513  DispatcherContainer::const_iterator aDispatcherPos = m_aFeatureDispatchers.find( rFeature );
514  if ( aDispatcherPos != m_aFeatureDispatchers.end() )
515  {
516  // TODO: for the real and actual listener notifications, we should release
517  // our mutex
518  UpdateAllListeners( )( aDispatcherPos->second );
519  }
520  }
521 }
522 
523 FormController::FormController(const Reference< css::uno::XComponentContext > & _rxORB )
525  ,OPropertySetHelper( FormController_BASE::rBHelper )
526  ,OSQLParserClient( _rxORB )
527  ,m_xComponentContext( _rxORB )
528  ,m_aActivateListeners(m_aMutex)
529  ,m_aModifyListeners(m_aMutex)
530  ,m_aErrorListeners(m_aMutex)
531  ,m_aDeleteListeners(m_aMutex)
532  ,m_aRowSetApproveListeners(m_aMutex)
533  ,m_aParameterListeners(m_aMutex)
534  ,m_aFilterListeners(m_aMutex)
535  ,m_aTabActivationIdle("svx FormController m_aTabActivationIdle")
536  ,m_aFeatureInvalidationTimer("svx FormController m_aFeatureInvalidationTimer")
537  ,m_aMode( OUString( "DataMode" ) )
538  ,m_aLoadEvent( LINK( this, FormController, OnLoad ) )
539  ,m_aToggleEvent( LINK( this, FormController, OnToggleAutoFields ) )
540  ,m_aActivationEvent( LINK( this, FormController, OnActivated ) )
541  ,m_aDeactivationEvent( LINK( this, FormController, OnDeactivated ) )
542  ,m_nCurrentFilterPosition(-1)
543  ,m_bCurrentRecordModified(false)
544  ,m_bCurrentRecordNew(false)
545  ,m_bLocked(false)
546  ,m_bDBConnection(false)
547  ,m_bCycle(false)
548  ,m_bCanInsert(false)
549  ,m_bCanUpdate(false)
550  ,m_bCommitLock(false)
551  ,m_bModified(false)
552  ,m_bControlsSorted(false)
553  ,m_bFiltering(false)
554  ,m_bAttachEvents(true)
555  ,m_bDetachEvents(true)
556  ,m_bAttemptedHandlerCreation( false )
557  ,m_bSuspendFilterTextListening( false )
558 {
559 
560  osl_atomic_increment(&m_refCount);
561  {
562  m_xTabController = TabController::create( m_xComponentContext );
563  m_xAggregate.set( m_xTabController, UNO_QUERY_THROW );
564  m_xAggregate->setDelegator( *this );
565  }
566  osl_atomic_decrement(&m_refCount);
567 
568  m_aTabActivationIdle.SetPriority( TaskPriority::LOWEST );
569  m_aTabActivationIdle.SetInvokeHandler( LINK( this, FormController, OnActivateTabOrder ) );
570 
571  m_aFeatureInvalidationTimer.SetTimeout( 200 );
572  m_aFeatureInvalidationTimer.SetInvokeHandler( LINK( this, FormController, OnInvalidateFeatures ) );
573 }
574 
575 
576 FormController::~FormController()
577 {
578  {
579  ::osl::MutexGuard aGuard( m_aMutex );
580 
581  m_aLoadEvent.CancelPendingCall();
582  m_aToggleEvent.CancelPendingCall();
583  m_aActivationEvent.CancelPendingCall();
584  m_aDeactivationEvent.CancelPendingCall();
585 
586  if ( m_aTabActivationIdle.IsActive() )
587  m_aTabActivationIdle.Stop();
588  }
589 
590  if ( m_aFeatureInvalidationTimer.IsActive() )
591  m_aFeatureInvalidationTimer.Stop();
592 
593  disposeAllFeaturesAndDispatchers();
594 
595  if ( m_xFormOperations.is() )
596  m_xFormOperations->dispose();
597  m_xFormOperations.clear();
598 
599  // release of aggregation
600  if ( m_xAggregate.is() )
601  {
602  m_xAggregate->setDelegator( nullptr );
603  m_xAggregate.clear();
604  }
605 }
606 
607 
608 void SAL_CALL FormController::acquire() noexcept
609 {
610  FormController_BASE::acquire();
611 }
612 
613 
614 void SAL_CALL FormController::release() noexcept
615 {
616  FormController_BASE::release();
617 }
618 
619 
620 Any SAL_CALL FormController::queryInterface( const Type& _rType )
621 {
622  Any aRet = FormController_BASE::queryInterface( _rType );
623  if ( !aRet.hasValue() )
624  aRet = OPropertySetHelper::queryInterface( _rType );
625  if ( !aRet.hasValue() )
626  aRet = m_xAggregate->queryAggregation( _rType );
627  return aRet;
628 }
629 
630 
631 Sequence< sal_Int8 > SAL_CALL FormController::getImplementationId()
632 {
633  return css::uno::Sequence<sal_Int8>();
634 }
635 
636 Sequence< Type > SAL_CALL FormController::getTypes( )
637 {
639  FormController_BASE::getTypes(),
641  );
642 }
643 
644 // XServiceInfo
645 sal_Bool SAL_CALL FormController::supportsService(const OUString& ServiceName)
646 {
647  return cppu::supportsService(this, ServiceName);
648 }
649 
650 OUString SAL_CALL FormController::getImplementationName()
651 {
652  return "org.openoffice.comp.svx.FormController";
653 }
654 
655 Sequence< OUString> SAL_CALL FormController::getSupportedServiceNames()
656 {
657  // service names which are supported only, but cannot be used to created an
658  // instance at a service factory
659  Sequence<OUString> aNonCreatableServiceNames { "com.sun.star.form.FormControllerDispatcher" };
660 
661  // services which can be used to created an instance at a service factory
662  Sequence< OUString > aCreatableServiceNames( getSupportedServiceNames_Static() );
663  return ::comphelper::concatSequences( aCreatableServiceNames, aNonCreatableServiceNames );
664 }
665 
666 
667 sal_Bool SAL_CALL FormController::approveReset(const EventObject& /*rEvent*/)
668 {
669  return true;
670 }
671 
672 
673 void SAL_CALL FormController::resetted(const EventObject& rEvent)
674 {
675  ::osl::MutexGuard aGuard(m_aMutex);
676  if (getCurrentControl().is() && (getCurrentControl()->getModel() == rEvent.Source))
677  m_bModified = false;
678 }
679 
680 
681 Sequence< OUString> const & FormController::getSupportedServiceNames_Static()
682 {
683  static Sequence< OUString> const aServices
684  {
685  "com.sun.star.form.runtime.FormController",
686  "com.sun.star.awt.control.TabController"
687  };
688  return aServices;
689 }
690 
691 
692 namespace
693 {
694  struct ResetComponentText
695  {
696  void operator()( const Reference< XTextComponent >& _rxText )
697  {
698  _rxText->setText( OUString() );
699  }
700  };
701 
702  struct RemoveComponentTextListener
703  {
704  explicit RemoveComponentTextListener( const Reference< XTextListener >& _rxListener )
705  :m_xListener( _rxListener )
706  {
707  }
708 
709  void operator()( const Reference< XTextComponent >& _rxText )
710  {
711  _rxText->removeTextListener( m_xListener );
712  }
713 
714  private:
715  Reference< XTextListener > m_xListener;
716  };
717 }
718 
719 
720 void FormController::impl_setTextOnAllFilter_throw()
721 {
722  m_bSuspendFilterTextListening = true;
723  ::comphelper::FlagGuard aResetFlag( m_bSuspendFilterTextListening );
724 
725  // reset the text for all controls
726  ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), ResetComponentText() );
727 
728  if ( m_aFilterRows.empty() )
729  // nothing to do anymore
730  return;
731 
732  if ( m_nCurrentFilterPosition < 0 )
733  return;
734 
735  // set the text for all filters
736  OSL_ENSURE( m_aFilterRows.size() > o3tl::make_unsigned(m_nCurrentFilterPosition),
737  "FormController::impl_setTextOnAllFilter_throw: m_nCurrentFilterPosition too big" );
738 
739  if ( o3tl::make_unsigned(m_nCurrentFilterPosition) < m_aFilterRows.size() )
740  {
741  FmFilterRow& rRow = m_aFilterRows[ m_nCurrentFilterPosition ];
742  for (const auto& rEntry : rRow)
743  {
744  rEntry.first->setText( rEntry.second );
745  }
746  }
747 }
748 // OPropertySetHelper
749 
750 sal_Bool FormController::convertFastPropertyValue( Any & /*rConvertedValue*/, Any & /*rOldValue*/,
751  sal_Int32 /*nHandle*/, const Any& /*rValue*/ )
752 {
753  return false;
754 }
755 
756 
757 void FormController::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/, const Any& /*rValue*/ )
758 {
759 }
760 
761 
762 void FormController::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
763 {
764  switch (nHandle)
765  {
766  case FM_ATTR_FILTER:
767  {
768  OUStringBuffer aFilter;
769  Reference<XConnection> xConnection(getConnection(Reference< XRowSet>(m_xModelAsIndex, UNO_QUERY)));
770  if (xConnection.is())
771  {
772  Reference< XNumberFormatsSupplier> xFormatSupplier( getNumberFormats( xConnection, true ) );
773  Reference< XNumberFormatter> xFormatter = NumberFormatter::create(m_xComponentContext);
774  xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
775 
776  // now add the filter rows
777  try
778  {
779  for (const FmFilterRow& rRow : m_aFilterRows)
780  {
781  if ( rRow.empty() )
782  continue;
783 
784  OUStringBuffer aRowFilter;
785  for ( FmFilterRow::const_iterator condition = rRow.begin(); condition != rRow.end(); ++condition )
786  {
787  // get the field of the controls map
788  Reference< XControl > xControl( condition->first, UNO_QUERY_THROW );
789  Reference< XPropertySet > xModelProps( xControl->getModel(), UNO_QUERY_THROW );
790  Reference< XPropertySet > xField( xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY_THROW );
791 
792  OUString sFilterValue( condition->second );
793 
794  OUString sErrorMsg;
795  const std::unique_ptr< OSQLParseNode > pParseNode =
796  predicateTree( sErrorMsg, sFilterValue, xFormatter, xField );
797  OSL_ENSURE( pParseNode != nullptr, "FormController::getFastPropertyValue: could not parse the field value predicate!" );
798  if ( pParseNode != nullptr )
799  {
800  OUString sCriteria;
801  // don't use a parse context here, we need it unlocalized
802  pParseNode->parseNodeToStr( sCriteria, xConnection );
803  if ( condition != rRow.begin() )
804  aRowFilter.append( " AND " );
805  aRowFilter.append( sCriteria );
806  }
807  }
808  if ( !aRowFilter.isEmpty() )
809  {
810  if ( !aFilter.isEmpty() )
811  aFilter.append( " OR " );
812 
813  aFilter.append( "( " );
814  aFilter.append( aRowFilter );
815  aFilter.append( " )" );
816  }
817  }
818  }
819  catch( const Exception& )
820  {
822  aFilter.setLength(0);
823  }
824  }
825  rValue <<= aFilter.makeStringAndClear();
826  }
827  break;
828 
830  rValue <<= m_xFormOperations;
831  break;
832  }
833 }
834 
835 
836 Reference< XPropertySetInfo > FormController::getPropertySetInfo()
837 {
838  static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
839  return xInfo;
840 }
841 
842 
843 void FormController::fillProperties(
844  Sequence< Property >& /* [out] */ _rProps,
845  Sequence< Property >& /* [out] */ /*_rAggregateProps*/
846  ) const
847 {
848  _rProps.realloc(2);
849  sal_Int32 nPos = 0;
850  Property* pDesc = _rProps.getArray();
851 
852  pDesc[nPos++] = Property(FM_PROP_FILTER, FM_ATTR_FILTER,
854  PropertyAttribute::READONLY);
857  PropertyAttribute::READONLY);
858 }
859 
860 
861 ::cppu::IPropertyArrayHelper& FormController::getInfoHelper()
862 {
863  return *getArrayHelper();
864 }
865 
866 // XFilterController
867 
868 void SAL_CALL FormController::addFilterControllerListener( const Reference< XFilterControllerListener >& Listener )
869 {
870  m_aFilterListeners.addInterface( Listener );
871 }
872 
873 
874 void SAL_CALL FormController::removeFilterControllerListener( const Reference< XFilterControllerListener >& Listener )
875 {
876  m_aFilterListeners.removeInterface( Listener );
877 }
878 
879 
880 ::sal_Int32 SAL_CALL FormController::getFilterComponents()
881 {
882  ::osl::MutexGuard aGuard( m_aMutex );
883  impl_checkDisposed_throw();
884 
885  return m_aFilterComponents.size();
886 }
887 
888 
889 ::sal_Int32 SAL_CALL FormController::getDisjunctiveTerms()
890 {
891  ::osl::MutexGuard aGuard( m_aMutex );
892  impl_checkDisposed_throw();
893 
894  return m_aFilterRows.size();
895 }
896 
897 
898 void SAL_CALL FormController::setPredicateExpression( ::sal_Int32 Component, ::sal_Int32 Term, const OUString& PredicateExpression )
899 {
900  ::osl::MutexGuard aGuard( m_aMutex );
901  impl_checkDisposed_throw();
902 
903  if ( ( Component < 0 ) || ( Component >= getFilterComponents() ) || ( Term < 0 ) || ( Term >= getDisjunctiveTerms() ) )
904  throw IndexOutOfBoundsException( OUString(), *this );
905 
906  Reference< XTextComponent > xText( m_aFilterComponents[ Component ] );
907  xText->setText( PredicateExpression );
908 
909  FmFilterRow& rFilterRow = m_aFilterRows[ Term ];
910  if ( !PredicateExpression.isEmpty() )
911  rFilterRow[ xText ] = PredicateExpression;
912  else
913  rFilterRow.erase( xText );
914 }
915 
916 
917 Reference< XControl > FormController::getFilterComponent( ::sal_Int32 Component )
918 {
919  ::osl::MutexGuard aGuard( m_aMutex );
920  impl_checkDisposed_throw();
921 
922  if ( ( Component < 0 ) || ( Component >= getFilterComponents() ) )
923  throw IndexOutOfBoundsException( OUString(), *this );
924 
925  return Reference< XControl >( m_aFilterComponents[ Component ], UNO_QUERY );
926 }
927 
928 
929 Sequence< Sequence< OUString > > FormController::getPredicateExpressions()
930 {
931  ::osl::MutexGuard aGuard( m_aMutex );
932  impl_checkDisposed_throw();
933 
934  Sequence< Sequence< OUString > > aExpressions( m_aFilterRows.size() );
935  sal_Int32 termIndex = 0;
936  for (const FmFilterRow& rRow : m_aFilterRows)
937  {
938  Sequence< OUString > aConjunction( m_aFilterComponents.size() );
939  sal_Int32 componentIndex = 0;
940  for (const auto& rComp : m_aFilterComponents)
941  {
942  FmFilterRow::const_iterator predicate = rRow.find( rComp );
943  if ( predicate != rRow.end() )
944  aConjunction[ componentIndex ] = predicate->second;
945  ++componentIndex;
946  }
947 
948  aExpressions[ termIndex ] = aConjunction;
949  ++termIndex;
950  }
951 
952  return aExpressions;
953 }
954 
955 
956 void SAL_CALL FormController::removeDisjunctiveTerm( ::sal_Int32 Term )
957 {
958  // SYNCHRONIZED -->
959  ::osl::ClearableMutexGuard aGuard( m_aMutex );
960  impl_checkDisposed_throw();
961 
962  if ( ( Term < 0 ) || ( Term >= getDisjunctiveTerms() ) )
963  throw IndexOutOfBoundsException( OUString(), *this );
964 
965  // if the to-be-deleted row is our current row, we need to shift
966  if ( Term == m_nCurrentFilterPosition )
967  {
968  if ( m_nCurrentFilterPosition < sal_Int32( m_aFilterRows.size() - 1 ) )
969  ++m_nCurrentFilterPosition;
970  else
971  --m_nCurrentFilterPosition;
972  }
973 
974  FmFilterRows::iterator pos = m_aFilterRows.begin() + Term;
975  m_aFilterRows.erase( pos );
976 
977  // adjust m_nCurrentFilterPosition if the removed row preceded it
978  if ( Term < m_nCurrentFilterPosition )
979  --m_nCurrentFilterPosition;
980 
981  SAL_WARN_IF( !( ( m_nCurrentFilterPosition < 0 ) != ( m_aFilterRows.empty() ) ),
982  "svx.form", "FormController::removeDisjunctiveTerm: inconsistency!" );
983 
984  // update the texts in the filter controls
985  impl_setTextOnAllFilter_throw();
986 
987  FilterEvent aEvent;
988  aEvent.Source = *this;
989  aEvent.DisjunctiveTerm = Term;
990  aGuard.clear();
991  // <-- SYNCHRONIZED
992 
993  m_aFilterListeners.notifyEach( &XFilterControllerListener::disjunctiveTermRemoved, aEvent );
994 }
995 
996 
997 void SAL_CALL FormController::appendEmptyDisjunctiveTerm()
998 {
999  // SYNCHRONIZED -->
1000  ::osl::ClearableMutexGuard aGuard( m_aMutex );
1001  impl_checkDisposed_throw();
1002 
1003  impl_appendEmptyFilterRow( aGuard );
1004  // <-- SYNCHRONIZED
1005 }
1006 
1007 
1008 ::sal_Int32 SAL_CALL FormController::getActiveTerm()
1009 {
1010  ::osl::MutexGuard aGuard( m_aMutex );
1011  impl_checkDisposed_throw();
1012 
1013  return m_nCurrentFilterPosition;
1014 }
1015 
1016 
1017 void SAL_CALL FormController::setActiveTerm( ::sal_Int32 ActiveTerm )
1018 {
1019  ::osl::MutexGuard aGuard( m_aMutex );
1020  impl_checkDisposed_throw();
1021 
1022  if ( ( ActiveTerm < 0 ) || ( ActiveTerm >= getDisjunctiveTerms() ) )
1023  throw IndexOutOfBoundsException( OUString(), *this );
1024 
1025  if ( ActiveTerm == getActiveTerm() )
1026  return;
1027 
1028  m_nCurrentFilterPosition = ActiveTerm;
1029  impl_setTextOnAllFilter_throw();
1030 }
1031 
1032 // XElementAccess
1033 
1034 sal_Bool SAL_CALL FormController::hasElements()
1035 {
1036  ::osl::MutexGuard aGuard( m_aMutex );
1037  return !m_aChildren.empty();
1038 }
1039 
1040 
1041 Type SAL_CALL FormController::getElementType()
1042 {
1044 
1045 }
1046 
1047 // XEnumerationAccess
1048 
1049 Reference< XEnumeration > SAL_CALL FormController::createEnumeration()
1050 {
1051  ::osl::MutexGuard aGuard( m_aMutex );
1052  return new ::comphelper::OEnumerationByIndex(this);
1053 }
1054 
1055 // XIndexAccess
1056 
1057 sal_Int32 SAL_CALL FormController::getCount()
1058 {
1059  ::osl::MutexGuard aGuard( m_aMutex );
1060  return m_aChildren.size();
1061 }
1062 
1063 
1064 Any SAL_CALL FormController::getByIndex(sal_Int32 Index)
1065 {
1066  ::osl::MutexGuard aGuard( m_aMutex );
1067  if (Index < 0 ||
1068  Index >= static_cast<sal_Int32>(m_aChildren.size()))
1069  throw IndexOutOfBoundsException();
1070 
1071  return makeAny( m_aChildren[ Index ] );
1072 }
1073 
1074 // EventListener
1075 
1076 void SAL_CALL FormController::disposing(const EventObject& e)
1077 {
1078  // has the container been disposed
1079  ::osl::MutexGuard aGuard( m_aMutex );
1080  Reference< XControlContainer > xContainer(e.Source, UNO_QUERY);
1081  if (xContainer.is())
1082  {
1083  setContainer(Reference< XControlContainer > ());
1084  }
1085  else
1086  {
1087  // has a control been disposed
1088  Reference< XControl > xControl(e.Source, UNO_QUERY);
1089  if (xControl.is())
1090  {
1091  if (getContainer().is())
1092  removeControl(xControl);
1093  }
1094  }
1095 }
1096 
1097 // OComponentHelper
1098 
1099 void FormController::disposeAllFeaturesAndDispatchers()
1100 {
1101  for (auto& rDispatcher : m_aFeatureDispatchers)
1102  {
1103  try
1104  {
1105  ::comphelper::disposeComponent( rDispatcher.second );
1106  }
1107  catch( const Exception& )
1108  {
1109  DBG_UNHANDLED_EXCEPTION("svx");
1110  }
1111  }
1112  m_aFeatureDispatchers.clear();
1113 }
1114 
1115 
1116 void FormController::disposing()
1117 {
1118  EventObject aEvt( *this );
1119 
1120  // if we're still active, simulate a "deactivated" event
1121  if ( m_xActiveControl.is() )
1122  m_aActivateListeners.notifyEach( &XFormControllerListener::formDeactivated, aEvt );
1123 
1124  // notify all our listeners
1125  m_aActivateListeners.disposeAndClear(aEvt);
1126  m_aModifyListeners.disposeAndClear(aEvt);
1127  m_aErrorListeners.disposeAndClear(aEvt);
1128  m_aDeleteListeners.disposeAndClear(aEvt);
1129  m_aRowSetApproveListeners.disposeAndClear(aEvt);
1130  m_aParameterListeners.disposeAndClear(aEvt);
1131  m_aFilterListeners.disposeAndClear(aEvt);
1132 
1133  removeBoundFieldListener();
1134  stopFiltering();
1135 
1136  m_aControlBorderManager.restoreAll();
1137 
1138  m_aFilterRows.clear();
1139 
1140  ::osl::MutexGuard aGuard( m_aMutex );
1141  m_xActiveControl = nullptr;
1142  implSetCurrentControl( nullptr );
1143 
1144  // clean up our children
1145  for (const auto& rpChild : m_aChildren)
1146  {
1147  // search the position of the model within the form
1148  Reference< XFormComponent > xForm(rpChild->getModel(), UNO_QUERY);
1149  sal_uInt32 nPos = m_xModelAsIndex->getCount();
1150  Reference< XFormComponent > xTemp;
1151  for( ; nPos; )
1152  {
1153 
1154  m_xModelAsIndex->getByIndex( --nPos ) >>= xTemp;
1155  if ( xForm.get() == xTemp.get() )
1156  {
1157  Reference< XInterface > xIfc( rpChild, UNO_QUERY );
1158  m_xModelAsManager->detach( nPos, xIfc );
1159  break;
1160  }
1161  }
1162 
1163  Reference< XComponent > (rpChild, UNO_QUERY_THROW)->dispose();
1164  }
1165  m_aChildren.clear();
1166 
1167  disposeAllFeaturesAndDispatchers();
1168 
1169  if ( m_xFormOperations.is() )
1170  m_xFormOperations->dispose();
1171  m_xFormOperations.clear();
1172 
1173  if (m_bDBConnection)
1174  unload();
1175 
1176  setContainer( nullptr );
1177  setModel( nullptr );
1178  setParent( nullptr );
1179 
1180  ::comphelper::disposeComponent( m_xComposer );
1181 
1182  m_bDBConnection = false;
1183 }
1184 
1185 
1186 namespace
1187 {
1188  bool lcl_shouldUseDynamicControlBorder( const Reference< XInterface >& _rxForm, const Any& _rDynamicColorProp )
1189  {
1190  bool bDoUse = false;
1191  if ( !( _rDynamicColorProp >>= bDoUse ) )
1192  {
1193  DocumentType eDocType = DocumentClassification::classifyHostDocument( _rxForm );
1194  return ControlLayouter::useDynamicBorderColor( eDocType );
1195  }
1196  return bDoUse;
1197  }
1198 }
1199 
1200 
1201 void SAL_CALL FormController::propertyChange(const PropertyChangeEvent& evt)
1202 {
1203  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1204  if ( evt.PropertyName == FM_PROP_BOUNDFIELD )
1205  {
1206  Reference<XPropertySet> xOldBound;
1207  evt.OldValue >>= xOldBound;
1208  if ( !xOldBound.is() && evt.NewValue.hasValue() )
1209  {
1210  Reference< XControlModel > xControlModel(evt.Source,UNO_QUERY);
1211  Reference< XControl > xControl = findControl(m_aControls,xControlModel,false,false);
1212  if ( xControl.is() )
1213  {
1214  startControlModifyListening( xControl );
1215  Reference<XPropertySet> xProp(xControlModel,UNO_QUERY);
1216  if ( xProp.is() )
1217  xProp->removePropertyChangeListener(FM_PROP_BOUNDFIELD, this);
1218  }
1219  }
1220  }
1221  else
1222  {
1223  bool bModifiedChanged = (evt.PropertyName == FM_PROP_ISMODIFIED);
1224  bool bNewChanged = (evt.PropertyName == FM_PROP_ISNEW);
1225  if (bModifiedChanged || bNewChanged)
1226  {
1227  ::osl::MutexGuard aGuard( m_aMutex );
1228  if (bModifiedChanged)
1229  m_bCurrentRecordModified = ::comphelper::getBOOL(evt.NewValue);
1230  else
1231  m_bCurrentRecordNew = ::comphelper::getBOOL(evt.NewValue);
1232 
1233  // toggle the locking
1234  if (m_bLocked != determineLockState())
1235  {
1236  m_bLocked = !m_bLocked;
1237  setLocks();
1238  if (isListeningForChanges())
1239  startListening();
1240  else
1241  stopListening();
1242  }
1243 
1244  if ( bNewChanged )
1245  m_aToggleEvent.Call();
1246 
1247  if (!m_bCurrentRecordModified)
1248  m_bModified = false;
1249  }
1250  else if ( evt.PropertyName == FM_PROP_DYNAMIC_CONTROL_BORDER )
1251  {
1252  bool bEnable = lcl_shouldUseDynamicControlBorder( evt.Source, evt.NewValue );
1253  if ( bEnable )
1254  {
1255  m_aControlBorderManager.enableDynamicBorderColor();
1256  if ( m_xActiveControl.is() )
1257  m_aControlBorderManager.focusGained( m_xActiveControl );
1258  }
1259  else
1260  {
1261  m_aControlBorderManager.disableDynamicBorderColor();
1262  }
1263  }
1264  }
1265 }
1266 
1267 
1268 bool FormController::replaceControl( const Reference< XControl >& _rxExistentControl, const Reference< XControl >& _rxNewControl )
1269 {
1270  bool bSuccess = false;
1271  try
1272  {
1273  Reference< XIdentifierReplace > xContainer( getContainer(), UNO_QUERY );
1274  DBG_ASSERT( xContainer.is(), "FormController::replaceControl: yes, it's not required by the service description, but XIdentifierReplace would be nice!" );
1275  if ( xContainer.is() )
1276  {
1277  // look up the ID of _rxExistentControl
1278  const Sequence< sal_Int32 > aIdentifiers( xContainer->getIdentifiers() );
1279  const sal_Int32* pIdentifiers = std::find_if(aIdentifiers.begin(), aIdentifiers.end(),
1280  [&xContainer, &_rxExistentControl](const sal_Int32 nId) {
1281  Reference< XControl > xCheck( xContainer->getByIdentifier( nId ), UNO_QUERY );
1282  return xCheck == _rxExistentControl;
1283  });
1284  DBG_ASSERT( pIdentifiers != aIdentifiers.end(), "FormController::replaceControl: did not find the control in the container!" );
1285  if ( pIdentifiers != aIdentifiers.end() )
1286  {
1287  bool bReplacedWasActive = ( m_xActiveControl.get() == _rxExistentControl.get() );
1288  bool bReplacedWasCurrent = ( m_xCurrentControl.get() == _rxExistentControl.get() );
1289 
1290  if ( bReplacedWasActive )
1291  {
1292  m_xActiveControl = nullptr;
1293  implSetCurrentControl( nullptr );
1294  }
1295  else if ( bReplacedWasCurrent )
1296  {
1297  implSetCurrentControl( _rxNewControl );
1298  }
1299 
1300  // carry over the model
1301  _rxNewControl->setModel( _rxExistentControl->getModel() );
1302 
1303  xContainer->replaceByIdentifer( *pIdentifiers, makeAny( _rxNewControl ) );
1304  bSuccess = true;
1305 
1306  if ( bReplacedWasActive )
1307  {
1308  Reference< XWindow > xControlWindow( _rxNewControl, UNO_QUERY );
1309  if ( xControlWindow.is() )
1310  xControlWindow->setFocus();
1311  }
1312  }
1313  }
1314  }
1315  catch( const Exception& )
1316  {
1317  DBG_UNHANDLED_EXCEPTION("svx");
1318  }
1319 
1320  Reference< XControl > xDisposeIt( bSuccess ? _rxExistentControl : _rxNewControl );
1321  ::comphelper::disposeComponent( xDisposeIt );
1322  return bSuccess;
1323 }
1324 
1325 
1326 void FormController::toggleAutoFields(bool bAutoFields)
1327 {
1328  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1329 
1330 
1331  Sequence< Reference< XControl > > aControlsCopy( m_aControls );
1332  const Reference< XControl >* pControls = aControlsCopy.getConstArray();
1333  sal_Int32 nControls = aControlsCopy.getLength();
1334 
1335  if (bAutoFields)
1336  {
1337  // as we don't want new controls to be attached to the scripting environment
1338  // we change attach flags
1339  m_bAttachEvents = false;
1340  for (sal_Int32 i = nControls; i > 0;)
1341  {
1342  Reference< XControl > xControl = pControls[--i];
1343  if (xControl.is())
1344  {
1345  Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
1346  if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
1347  {
1348  // does the model use a bound field ?
1350  xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
1351 
1352  // is it an autofield?
1353  if ( xField.is()
1354  && ::comphelper::hasProperty( FM_PROP_AUTOINCREMENT, xField )
1355  && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_AUTOINCREMENT ) )
1356  )
1357  {
1358  replaceControl( xControl, new FmXAutoControl() );
1359  }
1360  }
1361  }
1362  }
1363  m_bAttachEvents = true;
1364  }
1365  else
1366  {
1367  m_bDetachEvents = false;
1368  for (sal_Int32 i = nControls; i > 0;)
1369  {
1370  Reference< XControl > xControl = pControls[--i];
1371  if (xControl.is())
1372  {
1373  Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
1374  if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
1375  {
1376  // does the model use a bound field ?
1378  xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
1379 
1380  // is it an autofield?
1381  if ( xField.is()
1382  && ::comphelper::hasProperty( FM_PROP_AUTOINCREMENT, xField )
1383  && ::comphelper::getBOOL( xField->getPropertyValue(FM_PROP_AUTOINCREMENT ) )
1384  )
1385  {
1386  OUString sServiceName;
1387  OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DEFAULTCONTROL ) >>= sServiceName );
1388  Reference< XControl > xNewControl( m_xComponentContext->getServiceManager()->createInstanceWithContext( sServiceName, m_xComponentContext ), UNO_QUERY );
1389  replaceControl( xControl, xNewControl );
1390  }
1391  }
1392  }
1393  }
1394  m_bDetachEvents = true;
1395  }
1396 }
1397 
1398 
1399 IMPL_LINK_NOARG(FormController, OnToggleAutoFields, void*, void)
1400 {
1401  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1402 
1403  toggleAutoFields(m_bCurrentRecordNew);
1404 }
1405 
1406 // XTextListener
1407 void SAL_CALL FormController::textChanged(const TextEvent& e)
1408 {
1409  // SYNCHRONIZED -->
1410  ::osl::ClearableMutexGuard aGuard( m_aMutex );
1411  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1412  if ( !m_bFiltering )
1413  {
1414  impl_onModify();
1415  return;
1416  }
1417 
1418  if ( m_bSuspendFilterTextListening )
1419  return;
1420 
1421  Reference< XTextComponent > xText(e.Source,UNO_QUERY);
1422  OUString aText = xText->getText();
1423 
1424  if ( m_aFilterRows.empty() )
1425  appendEmptyDisjunctiveTerm();
1426 
1427  // find the current row
1428  if ( ( m_nCurrentFilterPosition < 0 ) || ( o3tl::make_unsigned(m_nCurrentFilterPosition) >= m_aFilterRows.size() ) )
1429  {
1430  OSL_ENSURE( false, "FormController::textChanged: m_nCurrentFilterPosition is wrong!" );
1431  return;
1432  }
1433 
1434  FmFilterRow& rRow = m_aFilterRows[ m_nCurrentFilterPosition ];
1435 
1436  // do we have a new filter
1437  if (!aText.isEmpty())
1438  rRow[xText] = aText;
1439  else
1440  {
1441  // do we have the control in the row
1442  FmFilterRow::iterator iter = rRow.find(xText);
1443  // erase the entry out of the row
1444  if (iter != rRow.end())
1445  rRow.erase(iter);
1446  }
1447 
1448  // multiplex the event to our FilterControllerListeners
1449  FilterEvent aEvent;
1450  aEvent.Source = *this;
1451  aEvent.FilterComponent = ::std::find( m_aFilterComponents.begin(), m_aFilterComponents.end(), xText ) - m_aFilterComponents.begin();
1452  aEvent.DisjunctiveTerm = getActiveTerm();
1453  aEvent.PredicateExpression = aText;
1454 
1455  aGuard.clear();
1456  // <-- SYNCHRONIZED
1457 
1458  // notify the changed filter expression
1459  m_aFilterListeners.notifyEach( &XFilterControllerListener::predicateExpressionChanged, aEvent );
1460 }
1461 
1462 // XItemListener
1463 void SAL_CALL FormController::itemStateChanged(const ItemEvent& /*rEvent*/)
1464 {
1465  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1466  impl_onModify();
1467 }
1468 
1469 // XModificationBroadcaster
1470 void SAL_CALL FormController::addModifyListener(const Reference< XModifyListener > & l)
1471 {
1472  ::osl::MutexGuard aGuard( m_aMutex );
1473  impl_checkDisposed_throw();
1474  m_aModifyListeners.addInterface( l );
1475 }
1476 
1477 void FormController::removeModifyListener(const Reference< XModifyListener > & l)
1478 {
1479  ::osl::MutexGuard aGuard( m_aMutex );
1480  impl_checkDisposed_throw();
1481  m_aModifyListeners.removeInterface( l );
1482 }
1483 
1484 // XModificationListener
1485 void FormController::modified( const EventObject& _rEvent )
1486 {
1487  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1488 
1489  try
1490  {
1491  if ( _rEvent.Source != m_xActiveControl )
1492  { // let this control grab the focus
1493  // (this case may happen if somebody moves the scroll wheel of the mouse over a control
1494  // which does not have the focus)
1495  // 85511 - 29.05.2001 - frank.schoenheit@germany.sun.com
1496 
1497  // also, it happens when an image control gets a new image by double-clicking it
1498  // #i88458# / 2009-01-12 / frank.schoenheit@sun.com
1499  Reference< XWindow > xControlWindow( _rEvent.Source, UNO_QUERY_THROW );
1500  xControlWindow->setFocus();
1501  }
1502  }
1503  catch( const Exception& )
1504  {
1505  DBG_UNHANDLED_EXCEPTION("svx");
1506  }
1507 
1508  impl_onModify();
1509 }
1510 
1511 void FormController::impl_checkDisposed_throw() const
1512 {
1513  if ( impl_isDisposed_nofail() )
1514  throw DisposedException( OUString(), *const_cast< FormController* >( this ) );
1515 }
1516 
1517 void FormController::impl_onModify()
1518 {
1519  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1520 
1521  {
1522  ::osl::MutexGuard aGuard( m_aMutex );
1523  if ( !m_bModified )
1524  m_bModified = true;
1525  }
1526 
1527  EventObject aEvt(static_cast<cppu::OWeakObject*>(this));
1528  m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvt );
1529 }
1530 
1531 void FormController::impl_addFilterRow( const FmFilterRow& _row )
1532 {
1533  m_aFilterRows.push_back( _row );
1534 
1535  if ( m_aFilterRows.size() == 1 )
1536  { // that's the first row ever
1537  OSL_ENSURE( m_nCurrentFilterPosition == -1, "FormController::impl_addFilterRow: inconsistency!" );
1538  m_nCurrentFilterPosition = 0;
1539  }
1540 }
1541 
1542 void FormController::impl_appendEmptyFilterRow( ::osl::ClearableMutexGuard& _rClearBeforeNotify )
1543 {
1544  // SYNCHRONIZED -->
1545  impl_addFilterRow( FmFilterRow() );
1546 
1547  // notify the listeners
1548  FilterEvent aEvent;
1549  aEvent.Source = *this;
1550  aEvent.DisjunctiveTerm = static_cast<sal_Int32>(m_aFilterRows.size()) - 1;
1551  _rClearBeforeNotify.clear();
1552  // <-- SYNCHRONIZED
1553  m_aFilterListeners.notifyEach( &XFilterControllerListener::disjunctiveTermAdded, aEvent );
1554 }
1555 
1556 bool FormController::determineLockState() const
1557 {
1558  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1559  // a.) in filter mode we are always locked
1560  // b.) if we have no valid model or our model (a result set) is not alive -> we're locked
1561  // c.) if we are inserting everything is OK and we are not locked
1562  // d.) if are not updatable or on invalid position
1563  Reference< XResultSet > xResultSet(m_xModelAsIndex, UNO_QUERY);
1564  if (m_bFiltering || !xResultSet.is() || !isRowSetAlive(xResultSet))
1565  return true;
1566  else
1567  return !(m_bCanInsert && m_bCurrentRecordNew)
1568  && (xResultSet->isBeforeFirst() || xResultSet->isAfterLast() || xResultSet->rowDeleted() || !m_bCanUpdate);
1569 }
1570 
1571 // FocusListener
1572 void FormController::focusGained(const FocusEvent& e)
1573 {
1574  // SYNCHRONIZED -->
1575  ::osl::ClearableMutexGuard aGuard( m_aMutex );
1576  impl_checkDisposed_throw();
1577 
1578  m_aControlBorderManager.focusGained( e.Source );
1579 
1580  Reference< XControl > xControl(e.Source, UNO_QUERY);
1581  if (m_bDBConnection)
1582  {
1583  // do we need to keep the locking of the commit
1584  // we hold the lock as long as the control differs from the current
1585  // otherwise we disabled the lock
1586  m_bCommitLock = m_bCommitLock && xControl.get() != m_xCurrentControl.get();
1587  if (m_bCommitLock)
1588  return;
1589 
1590  // when do we have to commit a value to form or a filter
1591  // a.) if the current value is modified
1592  // b.) there must be a current control
1593  // c.) and it must be different from the new focus owning control or
1594  // d.) the focus is moving around (so we have only one control)
1595 
1596  if ( ( m_bModified || m_bFiltering )
1597  && m_xCurrentControl.is()
1598  && ( ( xControl.get() != m_xCurrentControl.get() )
1599  || ( ( e.FocusFlags & FocusChangeReason::AROUND )
1600  && ( m_bCycle || m_bFiltering )
1601  )
1602  )
1603  )
1604  {
1605  // check the old control if the content is ok
1606 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
1607  Reference< XBoundControl > xLockingTest(m_xCurrentControl, UNO_QUERY);
1608  bool bControlIsLocked = xLockingTest.is() && xLockingTest->getLock();
1609  assert(!bControlIsLocked && "FormController::Gained: I'm modified and the current control is locked ? How this ?");
1610  // normally, a locked control should not be modified, so probably my bModified must
1611  // have been set from a different context, which I would not understand ...
1612 #endif
1613  DBG_ASSERT(m_xCurrentControl.is(), "no CurrentControl set");
1614  // first the control ask if it supports the IFace
1615  Reference< XBoundComponent > xBound(m_xCurrentControl, UNO_QUERY);
1616  if (!xBound.is() && m_xCurrentControl.is())
1617  xBound.set(m_xCurrentControl->getModel(), UNO_QUERY);
1618 
1619  // lock if we lose the focus during commit
1620  m_bCommitLock = true;
1621 
1622  // commit unsuccessful, reset focus
1623  if (xBound.is() && !xBound->commit())
1624  {
1625  // the commit failed and we don't commit again until the current control
1626  // which couldn't be commit gains the focus again
1627  Reference< XWindow > xWindow(m_xCurrentControl, UNO_QUERY);
1628  if (xWindow.is())
1629  xWindow->setFocus();
1630  return;
1631  }
1632  else
1633  {
1634  m_bModified = false;
1635  m_bCommitLock = false;
1636  }
1637  }
1638 
1639  if (!m_bFiltering && m_bCycle && (e.FocusFlags & FocusChangeReason::AROUND) && m_xCurrentControl.is())
1640  {
1641  OSL_ENSURE( m_xFormOperations.is(), "FormController::focusGained: hmm?" );
1642  // should have been created in setModel
1643  try
1644  {
1645  if ( e.FocusFlags & FocusChangeReason::FORWARD )
1646  {
1647  if ( m_xFormOperations.is() && m_xFormOperations->isEnabled( FormFeature::MoveToNext ) )
1648  m_xFormOperations->execute( FormFeature::MoveToNext );
1649  }
1650  else // backward
1651  {
1652  if ( m_xFormOperations.is() && m_xFormOperations->isEnabled( FormFeature::MoveToPrevious ) )
1653  m_xFormOperations->execute( FormFeature::MoveToPrevious );
1654  }
1655  }
1656  catch ( const Exception& )
1657  {
1658  // don't handle this any further. That's an ... admissible error.
1659  DBG_UNHANDLED_EXCEPTION("svx");
1660  }
1661  }
1662  }
1663 
1664  // still one and the same control
1665  if ( ( m_xActiveControl == xControl )
1666  && ( xControl == m_xCurrentControl )
1667  )
1668  {
1669  DBG_ASSERT(m_xCurrentControl.is(), "No CurrentControl selected");
1670  return;
1671  }
1672 
1673  bool bActivated = !m_xActiveControl.is() && xControl.is();
1674 
1675  m_xActiveControl = xControl;
1676 
1677  implSetCurrentControl( xControl );
1678  SAL_WARN_IF( !m_xCurrentControl.is(), "svx.form", "implSetCurrentControl did nonsense!" );
1679 
1680  if ( bActivated )
1681  {
1682  // (asynchronously) call activation handlers
1683  m_aActivationEvent.Call();
1684 
1685  // call modify listeners
1686  if ( m_bModified )
1687  m_aModifyListeners.notifyEach( &XModifyListener::modified, EventObject( *this ) );
1688  }
1689 
1690  // invalidate all features which depend on the currently focused control
1691  if ( m_bDBConnection && !m_bFiltering )
1692  implInvalidateCurrentControlDependentFeatures();
1693 
1694  if ( !m_xCurrentControl.is() )
1695  return;
1696 
1697  // control gets focus, then possibly in the visible range
1698  Reference< XFormControllerContext > xContext( m_xFormControllerContext );
1699  Reference< XControl > xCurrentControl( m_xCurrentControl );
1700  aGuard.clear();
1701  // <-- SYNCHRONIZED
1702 
1703  if ( xContext.is() )
1704  xContext->makeVisible( xCurrentControl );
1705 }
1706 
1707 IMPL_LINK_NOARG( FormController, OnActivated, void*, void )
1708 {
1709  EventObject aEvent;
1710  aEvent.Source = *this;
1711  m_aActivateListeners.notifyEach( &XFormControllerListener::formActivated, aEvent );
1712 }
1713 
1714 IMPL_LINK_NOARG( FormController, OnDeactivated, void*, void )
1715 {
1716  EventObject aEvent;
1717  aEvent.Source = *this;
1718  m_aActivateListeners.notifyEach( &XFormControllerListener::formDeactivated, aEvent );
1719 }
1720 
1721 void FormController::focusLost(const FocusEvent& e)
1722 {
1723  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1724 
1725  m_aControlBorderManager.focusLost( e.Source );
1726 
1727  Reference< XWindowPeer > xNext(e.NextFocus, UNO_QUERY);
1728  // if focus hasn't passed to some other window, e.g. focus in a welded item, don't deactivate
1729  if (!xNext)
1730  return;
1731  Reference< XControl > xNextControl = isInList(xNext);
1732  if (!xNextControl.is())
1733  {
1734  m_xActiveControl = nullptr;
1735  m_aDeactivationEvent.Call();
1736  }
1737 }
1738 
1739 void SAL_CALL FormController::mousePressed( const awt::MouseEvent& /*_rEvent*/ )
1740 {
1741  // not interested in
1742 }
1743 
1744 void SAL_CALL FormController::mouseReleased( const awt::MouseEvent& /*_rEvent*/ )
1745 {
1746  // not interested in
1747 }
1748 
1749 void SAL_CALL FormController::mouseEntered( const awt::MouseEvent& _rEvent )
1750 {
1751  m_aControlBorderManager.mouseEntered( _rEvent.Source );
1752 }
1753 
1754 void SAL_CALL FormController::mouseExited( const awt::MouseEvent& _rEvent )
1755 {
1756  m_aControlBorderManager.mouseExited( _rEvent.Source );
1757 }
1758 
1759 void SAL_CALL FormController::componentValidityChanged( const EventObject& _rSource )
1760 {
1761  Reference< XControl > xControl( findControl( m_aControls, Reference< XControlModel >( _rSource.Source, UNO_QUERY ), false, false ) );
1762  Reference< XValidatableFormComponent > xValidatable( _rSource.Source, UNO_QUERY );
1763 
1764  OSL_ENSURE( xControl.is() && xValidatable.is(), "FormController::componentValidityChanged: huh?" );
1765 
1766  if ( xControl.is() && xValidatable.is() )
1767  m_aControlBorderManager.validityChanged( xControl, xValidatable );
1768 }
1769 
1770 
1771 void FormController::setModel(const Reference< XTabControllerModel > & Model)
1772 {
1773  ::osl::MutexGuard aGuard( m_aMutex );
1774  impl_checkDisposed_throw();
1775 
1776  DBG_ASSERT(m_xTabController.is(), "FormController::setModel : invalid aggregate !");
1777 
1778  try
1779  {
1780  // disconnect from the old model
1781  if (m_xModelAsIndex.is())
1782  {
1783  if (m_bDBConnection)
1784  {
1785  // we are currently working on the model
1786  EventObject aEvt(m_xModelAsIndex);
1787  unloaded(aEvt);
1788  }
1789 
1790  Reference< XLoadable > xForm(m_xModelAsIndex, UNO_QUERY);
1791  if (xForm.is())
1792  xForm->removeLoadListener(this);
1793 
1794  Reference< XSQLErrorBroadcaster > xBroadcaster(m_xModelAsIndex, UNO_QUERY);
1795  if (xBroadcaster.is())
1796  xBroadcaster->removeSQLErrorListener(this);
1797 
1798  Reference< XDatabaseParameterBroadcaster > xParamBroadcaster(m_xModelAsIndex, UNO_QUERY);
1799  if (xParamBroadcaster.is())
1800  xParamBroadcaster->removeParameterListener(this);
1801 
1802  }
1803 
1804  disposeAllFeaturesAndDispatchers();
1805 
1806  if ( m_xFormOperations.is() )
1807  m_xFormOperations->dispose();
1808  m_xFormOperations.clear();
1809 
1810  // set the new model wait for the load event
1811  if (m_xTabController.is())
1812  m_xTabController->setModel(Model);
1813  m_xModelAsIndex.set(Model, UNO_QUERY);
1814  m_xModelAsManager.set(Model, UNO_QUERY);
1815 
1816  // only if both ifaces exit, the controller will work successful
1817  if (!m_xModelAsIndex.is() || !m_xModelAsManager.is())
1818  {
1819  m_xModelAsManager = nullptr;
1820  m_xModelAsIndex = nullptr;
1821  }
1822 
1823  if (m_xModelAsIndex.is())
1824  {
1825  // re-create m_xFormOperations
1826  m_xFormOperations = FormOperations::createWithFormController( m_xComponentContext, this );
1827  m_xFormOperations->setFeatureInvalidation( this );
1828 
1829  // adding load and ui interaction listeners
1830  Reference< XLoadable > xForm(Model, UNO_QUERY);
1831  if (xForm.is())
1832  xForm->addLoadListener(this);
1833 
1834  Reference< XSQLErrorBroadcaster > xBroadcaster(Model, UNO_QUERY);
1835  if (xBroadcaster.is())
1836  xBroadcaster->addSQLErrorListener(this);
1837 
1838  Reference< XDatabaseParameterBroadcaster > xParamBroadcaster(Model, UNO_QUERY);
1839  if (xParamBroadcaster.is())
1840  xParamBroadcaster->addParameterListener(this);
1841 
1842  // well, is the database already loaded?
1843  // then we have to simulate a load event
1844  Reference< XLoadable > xCursor(m_xModelAsIndex, UNO_QUERY);
1845  if (xCursor.is() && xCursor->isLoaded())
1846  {
1847  EventObject aEvt(xCursor);
1848  loaded(aEvt);
1849  }
1850 
1851  Reference< XPropertySet > xModelProps( m_xModelAsIndex, UNO_QUERY );
1852  Reference< XPropertySetInfo > xPropInfo( xModelProps->getPropertySetInfo() );
1853  if ( xPropInfo.is()
1854  && xPropInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER )
1855  && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_FOCUS )
1856  && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_MOUSE )
1857  && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_INVALID )
1858  )
1859  {
1860  bool bEnableDynamicControlBorder = lcl_shouldUseDynamicControlBorder(
1861  xModelProps, xModelProps->getPropertyValue( FM_PROP_DYNAMIC_CONTROL_BORDER ) );
1862  if ( bEnableDynamicControlBorder )
1863  m_aControlBorderManager.enableDynamicBorderColor();
1864  else
1865  m_aControlBorderManager.disableDynamicBorderColor();
1866 
1867  Color nColor;
1868  if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_FOCUS ) >>= nColor )
1869  m_aControlBorderManager.setStatusColor( ControlStatus::Focused, nColor );
1870  if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_MOUSE ) >>= nColor )
1871  m_aControlBorderManager.setStatusColor( ControlStatus::MouseHover, nColor );
1872  if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_INVALID ) >>= nColor )
1873  m_aControlBorderManager.setStatusColor( ControlStatus::Invalid, nColor );
1874  }
1875  }
1876  }
1877  catch( const Exception& )
1878  {
1879  DBG_UNHANDLED_EXCEPTION("svx");
1880  }
1881 }
1882 
1883 
1884 Reference< XTabControllerModel > FormController::getModel()
1885 {
1886  ::osl::MutexGuard aGuard( m_aMutex );
1887  impl_checkDisposed_throw();
1888 
1889  DBG_ASSERT(m_xTabController.is(), "FormController::getModel : invalid aggregate !");
1890  if (!m_xTabController.is())
1891  return Reference< XTabControllerModel > ();
1892  return m_xTabController->getModel();
1893 }
1894 
1895 
1896 void FormController::addToEventAttacher(const Reference< XControl > & xControl)
1897 {
1898  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1899  OSL_ENSURE( xControl.is(), "FormController::addToEventAttacher: invalid control - how did you reach this?" );
1900  if ( !xControl.is() )
1901  return; /* throw IllegalArgumentException(); */
1902 
1903  // register at the event attacher
1904  Reference< XFormComponent > xComp(xControl->getModel(), UNO_QUERY);
1905  if (!(xComp.is() && m_xModelAsIndex.is()))
1906  return;
1907 
1908  // and look for the position of the ControlModel in it
1909  sal_uInt32 nPos = m_xModelAsIndex->getCount();
1910  Reference< XFormComponent > xTemp;
1911  for( ; nPos; )
1912  {
1913  m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
1914  if (xComp.get() == xTemp.get())
1915  {
1916  m_xModelAsManager->attach( nPos, Reference<XInterface>( xControl, UNO_QUERY ), makeAny(xControl) );
1917  break;
1918  }
1919  }
1920 }
1921 
1922 
1923 void FormController::removeFromEventAttacher(const Reference< XControl > & xControl)
1924 {
1925  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1926  OSL_ENSURE( xControl.is(), "FormController::removeFromEventAttacher: invalid control - how did you reach this?" );
1927  if ( !xControl.is() )
1928  return; /* throw IllegalArgumentException(); */
1929 
1930  // register at the event attacher
1931  Reference< XFormComponent > xComp(xControl->getModel(), UNO_QUERY);
1932  if ( !(xComp.is() && m_xModelAsIndex.is()) )
1933  return;
1934 
1935  // and look for the position of the ControlModel in it
1936  sal_uInt32 nPos = m_xModelAsIndex->getCount();
1937  Reference< XFormComponent > xTemp;
1938  for( ; nPos; )
1939  {
1940  m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
1941  if (xComp.get() == xTemp.get())
1942  {
1943  m_xModelAsManager->detach( nPos, Reference<XInterface>( xControl, UNO_QUERY ) );
1944  break;
1945  }
1946  }
1947 }
1948 
1949 
1950 void FormController::setContainer(const Reference< XControlContainer > & xContainer)
1951 {
1952  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1953  Reference< XTabControllerModel > xTabModel(getModel());
1954  DBG_ASSERT(xTabModel.is() || !xContainer.is(), "No Model defined");
1955  // if we have a new container we need a model
1956  DBG_ASSERT(m_xTabController.is(), "FormController::setContainer : invalid aggregate !");
1957 
1958  ::osl::MutexGuard aGuard( m_aMutex );
1959  Reference< XContainer > xCurrentContainer;
1960  if (m_xTabController.is())
1961  xCurrentContainer.set(m_xTabController->getContainer(), UNO_QUERY);
1962  if (xCurrentContainer.is())
1963  {
1964  xCurrentContainer->removeContainerListener(this);
1965 
1966  if ( m_aTabActivationIdle.IsActive() )
1967  m_aTabActivationIdle.Stop();
1968 
1969  // clear the filter map
1970  ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), RemoveComponentTextListener( this ) );
1971  m_aFilterComponents.clear();
1972 
1973  // collecting the controls
1974  for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
1975  implControlRemoved( rControl, true );
1976 
1977  // make database-specific things
1978  if (m_bDBConnection && isListeningForChanges())
1979  stopListening();
1980 
1981  m_aControls.realloc( 0 );
1982  }
1983 
1984  if (m_xTabController.is())
1985  m_xTabController->setContainer(xContainer);
1986 
1987  // What controls belong to the container?
1988  if (xContainer.is() && xTabModel.is())
1989  {
1990  const Sequence< Reference< XControlModel > > aModels = xTabModel->getControlModels();
1991  Sequence< Reference< XControl > > aAllControls = xContainer->getControls();
1992 
1993  sal_Int32 nCount = aModels.getLength();
1994  m_aControls = Sequence< Reference< XControl > >( nCount );
1995  Reference< XControl > * pControls = m_aControls.getArray();
1996 
1997  // collecting the controls
1998  sal_Int32 j = 0;
1999  for (const Reference< XControlModel >& rModel : aModels )
2000  {
2001  Reference< XControl > xControl = findControl( aAllControls, rModel, false, true );
2002  if ( xControl.is() )
2003  {
2004  pControls[j++] = xControl;
2005  implControlInserted( xControl, true );
2006  }
2007  }
2008 
2009  // not every model had an associated control
2010  if (j != nCount)
2011  m_aControls.realloc(j);
2012 
2013  // listen at the container
2014  Reference< XContainer > xNewContainer(xContainer, UNO_QUERY);
2015  if (xNewContainer.is())
2016  xNewContainer->addContainerListener(this);
2017 
2018  // make database-specific things
2019  if (m_bDBConnection)
2020  {
2021  m_bLocked = determineLockState();
2022  setLocks();
2023  if (!isLocked())
2024  startListening();
2025  }
2026  }
2027  // the controls are in the right order
2028  m_bControlsSorted = true;
2029 }
2030 
2031 
2032 Reference< XControlContainer > FormController::getContainer()
2033 {
2034  ::osl::MutexGuard aGuard( m_aMutex );
2035  impl_checkDisposed_throw();
2036 
2037  DBG_ASSERT(m_xTabController.is(), "FormController::getContainer : invalid aggregate !");
2038  if (!m_xTabController.is())
2039  return Reference< XControlContainer > ();
2040  return m_xTabController->getContainer();
2041 }
2042 
2043 
2044 Sequence< Reference< XControl > > FormController::getControls()
2045 {
2046  ::osl::MutexGuard aGuard( m_aMutex );
2047  impl_checkDisposed_throw();
2048 
2049  if (!m_bControlsSorted)
2050  {
2051  Reference< XTabControllerModel > xModel = getModel();
2052  if (!xModel.is())
2053  return m_aControls;
2054 
2055  const Sequence< Reference< XControlModel > > aControlModels = xModel->getControlModels();
2056  sal_Int32 nModels = aControlModels.getLength();
2057 
2058  Sequence< Reference< XControl > > aNewControls(nModels);
2059 
2060  Reference< XControl > * pControls = aNewControls.getArray();
2061  Reference< XControl > xControl;
2062 
2063  // rearrange the controls according to the tab order sequence
2064  sal_Int32 j = 0;
2065  for ( const Reference< XControlModel >& rModel : aControlModels )
2066  {
2067  xControl = findControl( m_aControls, rModel, true, true );
2068  if ( xControl.is() )
2069  pControls[j++] = xControl;
2070  }
2071 
2072  // not every model had an associated control
2073  if ( j != nModels )
2074  aNewControls.realloc( j );
2075 
2076  m_aControls = aNewControls;
2077  m_bControlsSorted = true;
2078  }
2079  return m_aControls;
2080 }
2081 
2082 
2083 void FormController::autoTabOrder()
2084 {
2085  ::osl::MutexGuard aGuard( m_aMutex );
2086  impl_checkDisposed_throw();
2087 
2088  DBG_ASSERT(m_xTabController.is(), "FormController::autoTabOrder : invalid aggregate !");
2089  if (m_xTabController.is())
2090  m_xTabController->autoTabOrder();
2091 }
2092 
2093 
2094 void FormController::activateTabOrder()
2095 {
2096  ::osl::MutexGuard aGuard( m_aMutex );
2097  impl_checkDisposed_throw();
2098 
2099  DBG_ASSERT(m_xTabController.is(), "FormController::activateTabOrder : invalid aggregate !");
2100  if (m_xTabController.is())
2101  m_xTabController->activateTabOrder();
2102 }
2103 
2104 
2105 void FormController::setControlLock(const Reference< XControl > & xControl)
2106 {
2107  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2108  bool bLocked = isLocked();
2109 
2110  // It is locked
2111  // a. if the entire record is locked
2112  // b. if the associated field is locked
2113  Reference< XBoundControl > xBound(xControl, UNO_QUERY);
2114  if (!(xBound.is() &&
2115  ( (bLocked && bLocked != bool(xBound->getLock())) ||
2116  !bLocked))) // always uncheck individual fields when unlocking
2117  return;
2118 
2119  // there is a data source
2120  Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
2121  if (!(xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet)))
2122  return;
2123 
2124  // what about the ReadOnly and Enable properties
2125  bool bTouch = true;
2127  bTouch = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ENABLED));
2129  bTouch = !::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_READONLY));
2130 
2131  if (!bTouch)
2132  return;
2133 
2135  xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
2136  if (!xField.is())
2137  return;
2138 
2139  if (bLocked)
2140  xBound->setLock(bLocked);
2141  else
2142  {
2143  try
2144  {
2145  Any aVal = xField->getPropertyValue(FM_PROP_ISREADONLY);
2146  if (aVal.hasValue() && ::comphelper::getBOOL(aVal))
2147  xBound->setLock(true);
2148  else
2149  xBound->setLock(bLocked);
2150  }
2151  catch( const Exception& )
2152  {
2153  DBG_UNHANDLED_EXCEPTION("svx");
2154  }
2155 
2156  }
2157 }
2158 
2159 
2160 void FormController::setLocks()
2161 {
2162  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2163  // lock/unlock all controls connected to a data source
2164  for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
2165  setControlLock( rControl );
2166 }
2167 
2168 
2169 namespace
2170 {
2171  bool lcl_shouldListenForModifications( const Reference< XControl >& _rxControl, const Reference< XPropertyChangeListener >& _rxBoundFieldListener )
2172  {
2173  bool bShould = false;
2174 
2175  Reference< XBoundComponent > xBound( _rxControl, UNO_QUERY );
2176  if ( xBound.is() )
2177  {
2178  bShould = true;
2179  }
2180  else if ( _rxControl.is() )
2181  {
2182  Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY );
2183  if ( xModelProps.is() && ::comphelper::hasProperty( FM_PROP_BOUNDFIELD, xModelProps ) )
2184  {
2186  xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ) >>= xField;
2187  bShould = xField.is();
2188 
2189  if ( !bShould && _rxBoundFieldListener.is() )
2190  xModelProps->addPropertyChangeListener( FM_PROP_BOUNDFIELD, _rxBoundFieldListener );
2191  }
2192  }
2193 
2194  return bShould;
2195  }
2196 }
2197 
2198 
2199 void FormController::startControlModifyListening(const Reference< XControl > & xControl)
2200 {
2201  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2202 
2203  bool bModifyListening = lcl_shouldListenForModifications( xControl, this );
2204 
2205  // artificial while
2206  while ( bModifyListening )
2207  {
2208  Reference< XModifyBroadcaster > xMod(xControl, UNO_QUERY);
2209  if (xMod.is())
2210  {
2211  xMod->addModifyListener(this);
2212  break;
2213  }
2214 
2215  // all the text to prematurely recognize a modified
2216  Reference< XTextComponent > xText(xControl, UNO_QUERY);
2217  if (xText.is())
2218  {
2219  xText->addTextListener(this);
2220  break;
2221  }
2222 
2223  Reference< XCheckBox > xBox(xControl, UNO_QUERY);
2224  if (xBox.is())
2225  {
2226  xBox->addItemListener(this);
2227  break;
2228  }
2229 
2230  Reference< XComboBox > xCbBox(xControl, UNO_QUERY);
2231  if (xCbBox.is())
2232  {
2233  xCbBox->addItemListener(this);
2234  break;
2235  }
2236 
2237  Reference< XListBox > xListBox(xControl, UNO_QUERY);
2238  if (xListBox.is())
2239  {
2240  xListBox->addItemListener(this);
2241  break;
2242  }
2243  break;
2244  }
2245 }
2246 
2247 
2248 void FormController::stopControlModifyListening(const Reference< XControl > & xControl)
2249 {
2250  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2251 
2252  bool bModifyListening = lcl_shouldListenForModifications( xControl, nullptr );
2253 
2254  // artificial while
2255  while (bModifyListening)
2256  {
2257  Reference< XModifyBroadcaster > xMod(xControl, UNO_QUERY);
2258  if (xMod.is())
2259  {
2260  xMod->removeModifyListener(this);
2261  break;
2262  }
2263  // all the text to prematurely recognize a modified
2264  Reference< XTextComponent > xText(xControl, UNO_QUERY);
2265  if (xText.is())
2266  {
2267  xText->removeTextListener(this);
2268  break;
2269  }
2270 
2271  Reference< XCheckBox > xBox(xControl, UNO_QUERY);
2272  if (xBox.is())
2273  {
2274  xBox->removeItemListener(this);
2275  break;
2276  }
2277 
2278  Reference< XComboBox > xCbBox(xControl, UNO_QUERY);
2279  if (xCbBox.is())
2280  {
2281  xCbBox->removeItemListener(this);
2282  break;
2283  }
2284 
2285  Reference< XListBox > xListBox(xControl, UNO_QUERY);
2286  if (xListBox.is())
2287  {
2288  xListBox->removeItemListener(this);
2289  break;
2290  }
2291  break;
2292  }
2293 }
2294 
2295 
2296 void FormController::startListening()
2297 {
2298  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2299  m_bModified = false;
2300 
2301  // now register at bound fields
2302  for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
2303  startControlModifyListening( rControl );
2304 }
2305 
2306 
2307 void FormController::stopListening()
2308 {
2309  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2310  m_bModified = false;
2311 
2312  // now register at bound fields
2313  for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
2314  stopControlModifyListening( rControl );
2315 }
2316 
2317 
2318 Reference< XControl > FormController::findControl(Sequence< Reference< XControl > >& _rControls, const Reference< XControlModel > & xCtrlModel ,bool _bRemove,bool _bOverWrite) const
2319 {
2320  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2321  DBG_ASSERT( xCtrlModel.is(), "findControl - which ?!" );
2322 
2323  const Reference< XControl >* pControls = std::find_if(std::cbegin(_rControls), std::cend(_rControls),
2324  [&xCtrlModel](const Reference< XControl >& rControl) {
2325  return rControl.is() && rControl->getModel().get() == xCtrlModel.get(); });
2326  if (pControls != std::cend(_rControls))
2327  {
2328  Reference< XControl > xControl( *pControls );
2329  auto i = static_cast<sal_Int32>(std::distance(std::cbegin(_rControls), pControls));
2330  if ( _bRemove )
2331  ::comphelper::removeElementAt( _rControls, i );
2332  else if ( _bOverWrite )
2333  _rControls[i].clear();
2334  return xControl;
2335  }
2336  return Reference< XControl > ();
2337 }
2338 
2339 
2340 void FormController::implControlInserted( const Reference< XControl>& _rxControl, bool _bAddToEventAttacher )
2341 {
2342  Reference< XWindow > xWindow( _rxControl, UNO_QUERY );
2343  if ( xWindow.is() )
2344  {
2345  xWindow->addFocusListener( this );
2346  xWindow->addMouseListener( this );
2347 
2348  if ( _bAddToEventAttacher )
2349  addToEventAttacher( _rxControl );
2350  }
2351 
2352  // add a dispatch interceptor to the control (if supported)
2353  Reference< XDispatchProviderInterception > xInterception( _rxControl, UNO_QUERY );
2354  if ( xInterception.is() )
2355  createInterceptor( xInterception );
2356 
2357  if ( !_rxControl.is() )
2358  return;
2359 
2360  Reference< XControlModel > xModel( _rxControl->getModel() );
2361 
2362  // we want to know about the reset of the model of our controls
2363  // (for correctly resetting m_bModified)
2364  Reference< XReset > xReset( xModel, UNO_QUERY );
2365  if ( xReset.is() )
2366  xReset->addResetListener( this );
2367 
2368  // and we want to know about the validity, to visually indicate it
2369  Reference< XValidatableFormComponent > xValidatable( xModel, UNO_QUERY );
2370  if ( xValidatable.is() )
2371  {
2372  xValidatable->addFormComponentValidityListener( this );
2373  m_aControlBorderManager.validityChanged( _rxControl, xValidatable );
2374  }
2375 
2376 }
2377 
2378 
2379 void FormController::implControlRemoved( const Reference< XControl>& _rxControl, bool _bRemoveFromEventAttacher )
2380 {
2381  Reference< XWindow > xWindow( _rxControl, UNO_QUERY );
2382  if ( xWindow.is() )
2383  {
2384  xWindow->removeFocusListener( this );
2385  xWindow->removeMouseListener( this );
2386 
2387  if ( _bRemoveFromEventAttacher )
2388  removeFromEventAttacher( _rxControl );
2389  }
2390 
2391  Reference< XDispatchProviderInterception > xInterception( _rxControl, UNO_QUERY);
2392  if ( xInterception.is() )
2393  deleteInterceptor( xInterception );
2394 
2395  if ( _rxControl.is() )
2396  {
2397  Reference< XControlModel > xModel( _rxControl->getModel() );
2398 
2399  Reference< XReset > xReset( xModel, UNO_QUERY );
2400  if ( xReset.is() )
2401  xReset->removeResetListener( this );
2402 
2403  Reference< XValidatableFormComponent > xValidatable( xModel, UNO_QUERY );
2404  if ( xValidatable.is() )
2405  xValidatable->removeFormComponentValidityListener( this );
2406  }
2407 }
2408 
2409 
2410 void FormController::implSetCurrentControl( const Reference< XControl >& _rxControl )
2411 {
2412  if ( m_xCurrentControl.get() == _rxControl.get() )
2413  return;
2414 
2415  Reference< XGridControl > xGridControl( m_xCurrentControl, UNO_QUERY );
2416  if ( xGridControl.is() )
2417  xGridControl->removeGridControlListener( this );
2418 
2419  m_xCurrentControl = _rxControl;
2420 
2421  xGridControl.set( m_xCurrentControl, UNO_QUERY );
2422  if ( xGridControl.is() )
2423  xGridControl->addGridControlListener( this );
2424 }
2425 
2426 
2427 void FormController::insertControl(const Reference< XControl > & xControl)
2428 {
2429  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2430  m_bControlsSorted = false;
2431  m_aControls.realloc(m_aControls.getLength() + 1);
2432  m_aControls.getArray()[m_aControls.getLength() - 1] = xControl;
2433 
2434  if (m_pColumnInfoCache)
2435  m_pColumnInfoCache->deinitializeControls();
2436 
2437  implControlInserted( xControl, m_bAttachEvents );
2438 
2439  if (m_bDBConnection && !m_bFiltering)
2440  setControlLock(xControl);
2441 
2442  if (isListeningForChanges() && m_bAttachEvents)
2443  startControlModifyListening( xControl );
2444 }
2445 
2446 
2447 void FormController::removeControl(const Reference< XControl > & xControl)
2448 {
2449  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2450  auto pControl = std::find_if(std::cbegin(m_aControls), std::cend(m_aControls),
2451  [&xControl](const Reference< XControl >& rControl) { return xControl.get() == rControl.get(); });
2452  if (pControl != std::cend(m_aControls))
2453  {
2454  auto nIndex = static_cast<sal_Int32>(std::distance(std::cbegin(m_aControls), pControl));
2455  ::comphelper::removeElementAt( m_aControls, nIndex );
2456  }
2457 
2458  FilterComponents::iterator componentPos = ::std::find( m_aFilterComponents.begin(), m_aFilterComponents.end(), xControl );
2459  if ( componentPos != m_aFilterComponents.end() )
2460  m_aFilterComponents.erase( componentPos );
2461 
2462  implControlRemoved( xControl, m_bDetachEvents );
2463 
2464  if ( isListeningForChanges() && m_bDetachEvents )
2465  stopControlModifyListening( xControl );
2466 }
2467 
2468 // XLoadListener
2469 
2470 void FormController::loaded(const EventObject& rEvent)
2471 {
2472  OSL_ENSURE( rEvent.Source == m_xModelAsIndex, "FormController::loaded: where did this come from?" );
2473 
2474  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2475  ::osl::MutexGuard aGuard( m_aMutex );
2476  Reference< XRowSet > xForm(rEvent.Source, UNO_QUERY);
2477  // do we have a connected data source
2478  if (xForm.is() && getConnection(xForm).is())
2479  {
2480  Reference< XPropertySet > xSet(xForm, UNO_QUERY);
2481  if (xSet.is())
2482  {
2483  Any aVal = xSet->getPropertyValue(FM_PROP_CYCLE);
2484  sal_Int32 aVal2 = 0;
2485  ::cppu::enum2int(aVal2,aVal);
2486  m_bCycle = !aVal.hasValue() || static_cast<form::TabulatorCycle>(aVal2) == TabulatorCycle_RECORDS;
2487  m_bCanUpdate = canUpdate(xSet);
2488  m_bCanInsert = canInsert(xSet);
2489  m_bCurrentRecordModified = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED));
2490  m_bCurrentRecordNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
2491 
2492  startFormListening( xSet, false );
2493 
2494  // set the locks for the current controls
2495  if (getContainer().is())
2496  {
2497  m_aLoadEvent.Call();
2498  }
2499  }
2500  else
2501  {
2502  m_bCanInsert = m_bCanUpdate = m_bCycle = false;
2503  m_bCurrentRecordModified = false;
2504  m_bCurrentRecordNew = false;
2505  m_bLocked = false;
2506  }
2507  m_bDBConnection = true;
2508  }
2509  else
2510  {
2511  m_bDBConnection = false;
2512  m_bCanInsert = m_bCanUpdate = m_bCycle = false;
2513  m_bCurrentRecordModified = false;
2514  m_bCurrentRecordNew = false;
2515  m_bLocked = false;
2516  }
2517 
2518  Reference< XColumnsSupplier > xFormColumns( xForm, UNO_QUERY );
2519  m_pColumnInfoCache.reset( xFormColumns.is() ? new ColumnInfoCache( xFormColumns ) : nullptr );
2520 
2521  updateAllDispatchers();
2522 }
2523 
2524 
2525 void FormController::updateAllDispatchers() const
2526 {
2527  ::std::for_each(
2528  m_aFeatureDispatchers.begin(),
2529  m_aFeatureDispatchers.end(),
2530  [] (const DispatcherContainer::value_type& dispatcher) {
2531  UpdateAllListeners()(dispatcher.second);
2532  });
2533 }
2534 
2535 
2536 IMPL_LINK_NOARG(FormController, OnLoad, void*, void)
2537 {
2538  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2539  m_bLocked = determineLockState();
2540 
2541  setLocks();
2542 
2543  if (!m_bLocked)
2544  startListening();
2545 
2546  // just one exception toggle the auto values
2547  if (m_bCurrentRecordNew)
2548  toggleAutoFields(true);
2549 }
2550 
2551 
2552 void FormController::unloaded(const EventObject& /*rEvent*/)
2553 {
2554  ::osl::MutexGuard aGuard( m_aMutex );
2555  impl_checkDisposed_throw();
2556 
2557  updateAllDispatchers();
2558 }
2559 
2560 
2561 void FormController::reloading(const EventObject& /*aEvent*/)
2562 {
2563  ::osl::MutexGuard aGuard( m_aMutex );
2564  impl_checkDisposed_throw();
2565 
2566  // do the same like in unloading
2567  // just one exception toggle the auto values
2568  m_aToggleEvent.CancelPendingCall();
2569  unload();
2570 }
2571 
2572 
2573 void FormController::reloaded(const EventObject& aEvent)
2574 {
2575  ::osl::MutexGuard aGuard( m_aMutex );
2576  impl_checkDisposed_throw();
2577 
2578  loaded(aEvent);
2579 }
2580 
2581 
2582 void FormController::unloading(const EventObject& /*aEvent*/)
2583 {
2584  ::osl::MutexGuard aGuard( m_aMutex );
2585  impl_checkDisposed_throw();
2586 
2587  unload();
2588 }
2589 
2590 
2591 void FormController::unload()
2592 {
2593  ::osl::MutexGuard aGuard( m_aMutex );
2594  impl_checkDisposed_throw();
2595 
2596  m_aLoadEvent.CancelPendingCall();
2597 
2598  // be sure not to have autofields
2599  if (m_bCurrentRecordNew)
2600  toggleAutoFields(false);
2601 
2602  // remove bound field listing again
2603  removeBoundFieldListener();
2604 
2605  if (m_bDBConnection && isListeningForChanges())
2606  stopListening();
2607 
2608  Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
2609  if ( m_bDBConnection && xSet.is() )
2610  stopFormListening( xSet, false );
2611 
2612  m_bDBConnection = false;
2613  m_bCanInsert = m_bCanUpdate = m_bCycle = false;
2614  m_bCurrentRecordModified = m_bCurrentRecordNew = m_bLocked = false;
2615 
2616  m_pColumnInfoCache.reset();
2617 }
2618 
2619 
2620 void FormController::removeBoundFieldListener()
2621 {
2622  for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
2623  {
2624  Reference< XPropertySet > xProp( rControl, UNO_QUERY );
2625  if ( xProp.is() )
2626  xProp->removePropertyChangeListener( FM_PROP_BOUNDFIELD, this );
2627  }
2628 }
2629 
2630 
2631 void FormController::startFormListening( const Reference< XPropertySet >& _rxForm, bool _bPropertiesOnly )
2632 {
2633  try
2634  {
2635  if ( m_bCanInsert || m_bCanUpdate ) // form can be modified
2636  {
2637  _rxForm->addPropertyChangeListener( FM_PROP_ISNEW, this );
2638  _rxForm->addPropertyChangeListener( FM_PROP_ISMODIFIED, this );
2639 
2640  if ( !_bPropertiesOnly )
2641  {
2642  // set the Listener for UI interaction
2643  Reference< XRowSetApproveBroadcaster > xApprove( _rxForm, UNO_QUERY );
2644  if ( xApprove.is() )
2645  xApprove->addRowSetApproveListener( this );
2646 
2647  // listener for row set changes
2648  Reference< XRowSet > xRowSet( _rxForm, UNO_QUERY );
2649  if ( xRowSet.is() )
2650  xRowSet->addRowSetListener( this );
2651  }
2652  }
2653 
2654  Reference< XPropertySetInfo > xInfo = _rxForm->getPropertySetInfo();
2655  if ( xInfo.is() && xInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER ) )
2656  _rxForm->addPropertyChangeListener( FM_PROP_DYNAMIC_CONTROL_BORDER, this );
2657  }
2658  catch( const Exception& )
2659  {
2660  DBG_UNHANDLED_EXCEPTION("svx");
2661  }
2662 }
2663 
2664 
2665 void FormController::stopFormListening( const Reference< XPropertySet >& _rxForm, bool _bPropertiesOnly )
2666 {
2667  try
2668  {
2669  if ( m_bCanInsert || m_bCanUpdate )
2670  {
2671  _rxForm->removePropertyChangeListener( FM_PROP_ISNEW, this );
2672  _rxForm->removePropertyChangeListener( FM_PROP_ISMODIFIED, this );
2673 
2674  if ( !_bPropertiesOnly )
2675  {
2676  Reference< XRowSetApproveBroadcaster > xApprove( _rxForm, UNO_QUERY );
2677  if (xApprove.is())
2678  xApprove->removeRowSetApproveListener(this);
2679 
2680  Reference< XRowSet > xRowSet( _rxForm, UNO_QUERY );
2681  if ( xRowSet.is() )
2682  xRowSet->removeRowSetListener( this );
2683  }
2684  }
2685 
2686  Reference< XPropertySetInfo > xInfo = _rxForm->getPropertySetInfo();
2687  if ( xInfo.is() && xInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER ) )
2688  _rxForm->removePropertyChangeListener( FM_PROP_DYNAMIC_CONTROL_BORDER, this );
2689  }
2690  catch( const Exception& )
2691  {
2692  DBG_UNHANDLED_EXCEPTION("svx");
2693  }
2694 }
2695 
2696 // css::sdbc::XRowSetListener
2697 
2698 void FormController::cursorMoved(const EventObject& /*event*/)
2699 {
2700  ::osl::MutexGuard aGuard( m_aMutex );
2701  impl_checkDisposed_throw();
2702 
2703  // toggle the locking ?
2704  if (m_bLocked != determineLockState())
2705  {
2706  m_bLocked = !m_bLocked;
2707  setLocks();
2708  if (isListeningForChanges())
2709  startListening();
2710  else
2711  stopListening();
2712  }
2713 
2714  // neither the current control nor the current record are modified anymore
2715  m_bCurrentRecordModified = m_bModified = false;
2716 }
2717 
2718 
2719 void FormController::rowChanged(const EventObject& /*event*/)
2720 {
2721  // not interested in ...
2722 }
2723 
2724 void FormController::rowSetChanged(const EventObject& /*event*/)
2725 {
2726  // not interested in ...
2727 }
2728 
2729 
2730 // XContainerListener
2731 
2732 void SAL_CALL FormController::elementInserted(const ContainerEvent& evt)
2733 {
2734  ::osl::MutexGuard aGuard( m_aMutex );
2735  impl_checkDisposed_throw();
2736 
2737  Reference< XControl > xControl( evt.Element, UNO_QUERY );
2738  if ( !xControl.is() )
2739  return;
2740 
2741  Reference< XFormComponent > xModel(xControl->getModel(), UNO_QUERY);
2742  if (xModel.is() && m_xModelAsIndex == xModel->getParent())
2743  {
2744  insertControl(xControl);
2745 
2746  if ( m_aTabActivationIdle.IsActive() )
2747  m_aTabActivationIdle.Stop();
2748 
2749  m_aTabActivationIdle.Start();
2750  }
2751  // are we in filtermode and a XModeSelector has inserted an element
2752  else if (m_bFiltering && Reference< XModeSelector > (evt.Source, UNO_QUERY).is())
2753  {
2754  xModel.set(evt.Source, UNO_QUERY);
2755  if (xModel.is() && m_xModelAsIndex == xModel->getParent())
2756  {
2757  Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
2758  if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
2759  {
2760  // does the model use a bound field ?
2762  xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
2763 
2764  Reference< XTextComponent > xText(xControl, UNO_QUERY);
2765  // may we filter the field?
2766  if (xText.is() && xField.is() && ::comphelper::hasProperty(FM_PROP_SEARCHABLE, xField) &&
2767  ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_SEARCHABLE)))
2768  {
2769  m_aFilterComponents.push_back( xText );
2770  xText->addTextListener( this );
2771  }
2772  }
2773  }
2774  }
2775 }
2776 
2777 
2778 void SAL_CALL FormController::elementReplaced(const ContainerEvent& evt)
2779 {
2780  // simulate an elementRemoved
2781  ContainerEvent aRemoveEvent( evt );
2782  aRemoveEvent.Element = evt.ReplacedElement;
2783  aRemoveEvent.ReplacedElement = Any();
2784  elementRemoved( aRemoveEvent );
2785 
2786  // simulate an elementInserted
2787  ContainerEvent aInsertEvent( evt );
2788  aInsertEvent.ReplacedElement = Any();
2789  elementInserted( aInsertEvent );
2790 }
2791 
2792 
2793 void SAL_CALL FormController::elementRemoved(const ContainerEvent& evt)
2794 {
2795  ::osl::MutexGuard aGuard( m_aMutex );
2796  impl_checkDisposed_throw();
2797 
2798  Reference< XControl > xControl;
2799  evt.Element >>= xControl;
2800  if (!xControl.is())
2801  return;
2802 
2803  Reference< XFormComponent > xModel(xControl->getModel(), UNO_QUERY);
2804  if (xModel.is() && m_xModelAsIndex == xModel->getParent())
2805  {
2806  removeControl(xControl);
2807  // Do not recalculate TabOrder, because it must already work internally!
2808  }
2809  // are we in filtermode and a XModeSelector has inserted an element
2810  else if (m_bFiltering && Reference< XModeSelector > (evt.Source, UNO_QUERY).is())
2811  {
2812  FilterComponents::iterator componentPos = ::std::find(
2813  m_aFilterComponents.begin(), m_aFilterComponents.end(), xControl );
2814  if ( componentPos != m_aFilterComponents.end() )
2815  m_aFilterComponents.erase( componentPos );
2816  }
2817 }
2818 
2819 
2820 Reference< XControl > FormController::isInList(const Reference< XWindowPeer > & xPeer) const
2821 {
2822  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2823  const Reference< XControl >* pControls = m_aControls.getConstArray();
2824 
2825  sal_uInt32 nCtrls = m_aControls.getLength();
2826  for ( sal_uInt32 n = 0; n < nCtrls && xPeer.is(); ++n, ++pControls )
2827  {
2828  if ( pControls->is() )
2829  {
2830  Reference< XVclWindowPeer > xCtrlPeer( (*pControls)->getPeer(), UNO_QUERY);
2831  if ( ( xCtrlPeer.get() == xPeer.get() ) || xCtrlPeer->isChild( xPeer ) )
2832  return *pControls;
2833  }
2834  }
2835  return Reference< XControl > ();
2836 }
2837 
2838 
2839 void FormController::activateFirst()
2840 {
2841  ::osl::MutexGuard aGuard( m_aMutex );
2842  impl_checkDisposed_throw();
2843 
2844  DBG_ASSERT(m_xTabController.is(), "FormController::activateFirst : invalid aggregate !");
2845  if (m_xTabController.is())
2846  m_xTabController->activateFirst();
2847 }
2848 
2849 
2850 void FormController::activateLast()
2851 {
2852  ::osl::MutexGuard aGuard( m_aMutex );
2853  impl_checkDisposed_throw();
2854 
2855  DBG_ASSERT(m_xTabController.is(), "FormController::activateLast : invalid aggregate !");
2856  if (m_xTabController.is())
2857  m_xTabController->activateLast();
2858 }
2859 
2860 // XFormController
2861 
2862 Reference< XFormOperations > SAL_CALL FormController::getFormOperations()
2863 {
2864  ::osl::MutexGuard aGuard( m_aMutex );
2865  impl_checkDisposed_throw();
2866 
2867  return m_xFormOperations;
2868 }
2869 
2870 
2871 Reference< XControl> SAL_CALL FormController::getCurrentControl()
2872 {
2873  ::osl::MutexGuard aGuard( m_aMutex );
2874  impl_checkDisposed_throw();
2875  return m_xCurrentControl;
2876 }
2877 
2878 
2879 void SAL_CALL FormController::addActivateListener(const Reference< XFormControllerListener > & l)
2880 {
2881  ::osl::MutexGuard aGuard( m_aMutex );
2882  impl_checkDisposed_throw();
2883  m_aActivateListeners.addInterface(l);
2884 }
2885 
2886 void SAL_CALL FormController::removeActivateListener(const Reference< XFormControllerListener > & l)
2887 {
2888  ::osl::MutexGuard aGuard( m_aMutex );
2889  impl_checkDisposed_throw();
2890  m_aActivateListeners.removeInterface(l);
2891 }
2892 
2893 
2894 void SAL_CALL FormController::addChildController( const Reference< XFormController >& ChildController )
2895 {
2896  ::osl::MutexGuard aGuard( m_aMutex );
2897  impl_checkDisposed_throw();
2898 
2899  if ( !ChildController.is() )
2900  throw IllegalArgumentException( OUString(), *this, 1 );
2901  // TODO: (localized) error message
2902 
2903  // the parent of our (to-be-)child must be our own model
2904  Reference< XFormComponent > xFormOfChild( ChildController->getModel(), UNO_QUERY );
2905  if ( !xFormOfChild.is() )
2906  throw IllegalArgumentException( OUString(), *this, 1 );
2907  // TODO: (localized) error message
2908 
2909  if ( xFormOfChild->getParent() != m_xModelAsIndex )
2910  throw IllegalArgumentException( OUString(), *this, 1 );
2911  // TODO: (localized) error message
2912 
2913  m_aChildren.push_back( ChildController );
2914  ChildController->setParent( *this );
2915 
2916  // search the position of the model within the form
2917  sal_uInt32 nPos = m_xModelAsIndex->getCount();
2918  Reference< XFormComponent > xTemp;
2919  for( ; nPos; )
2920  {
2921  m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
2922  if ( xFormOfChild == xTemp )
2923  {
2924  m_xModelAsManager->attach( nPos, Reference<XInterface>( ChildController, UNO_QUERY ), makeAny( ChildController) );
2925  break;
2926  }
2927  }
2928 }
2929 
2930 
2931 Reference< XFormControllerContext > SAL_CALL FormController::getContext()
2932 {
2933  ::osl::MutexGuard aGuard( m_aMutex );
2934  impl_checkDisposed_throw();
2935  return m_xFormControllerContext;
2936 }
2937 
2938 
2939 void SAL_CALL FormController::setContext( const Reference< XFormControllerContext >& _context )
2940 {
2941  ::osl::MutexGuard aGuard( m_aMutex );
2942  impl_checkDisposed_throw();
2943  m_xFormControllerContext = _context;
2944 }
2945 
2946 
2947 Reference< XInteractionHandler > SAL_CALL FormController::getInteractionHandler()
2948 {
2949  ::osl::MutexGuard aGuard( m_aMutex );
2950  impl_checkDisposed_throw();
2951  return m_xInteractionHandler;
2952 }
2953 
2954 
2955 void SAL_CALL FormController::setInteractionHandler( const Reference< XInteractionHandler >& _interactionHandler )
2956 {
2957  ::osl::MutexGuard aGuard( m_aMutex );
2958  impl_checkDisposed_throw();
2959  m_xInteractionHandler = _interactionHandler;
2960 }
2961 
2962 
2963 void FormController::setFilter(::std::vector<FmFieldInfo>& rFieldInfos)
2964 {
2965  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2966  // create the composer
2967  Reference< XRowSet > xForm(m_xModelAsIndex, UNO_QUERY);
2968  Reference< XConnection > xConnection(getConnection(xForm));
2969  if (xForm.is())
2970  {
2971  try
2972  {
2973  Reference< XMultiServiceFactory > xFactory( xConnection, UNO_QUERY_THROW );
2974  m_xComposer.set(
2975  xFactory->createInstance("com.sun.star.sdb.SingleSelectQueryComposer"),
2976  UNO_QUERY_THROW );
2977 
2978  Reference< XPropertySet > xSet( xForm, UNO_QUERY );
2979  OUString sStatement = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_ACTIVECOMMAND ) );
2980  OUString sFilter = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_FILTER ) );
2981  m_xComposer->setElementaryQuery( sStatement );
2982  m_xComposer->setFilter( sFilter );
2983  }
2984  catch( const Exception& )
2985  {
2986  DBG_UNHANDLED_EXCEPTION("svx");
2987  }
2988  }
2989 
2990  if (m_xComposer.is())
2991  {
2992  const Sequence< Sequence < PropertyValue > > aFilterRows = m_xComposer->getStructuredFilter();
2993 
2994  // ok, we receive the list of filters as sequence of fieldnames, value
2995  // now we have to transform the fieldname into UI names, that could be a label of the field or
2996  // an aliasname or the fieldname itself
2997 
2998  // first adjust the field names if necessary
2999  Reference< XNameAccess > xQueryColumns =
3000  Reference< XColumnsSupplier >( m_xComposer, UNO_QUERY_THROW )->getColumns();
3001 
3002  for (auto& rFieldInfo : rFieldInfos)
3003  {
3004  if ( xQueryColumns->hasByName(rFieldInfo.aFieldName) )
3005  {
3006  if ( (xQueryColumns->getByName(rFieldInfo.aFieldName) >>= rFieldInfo.xField) && rFieldInfo.xField.is() )
3007  rFieldInfo.xField->getPropertyValue(FM_PROP_REALNAME) >>= rFieldInfo.aFieldName;
3008  }
3009  }
3010 
3011  Reference< XDatabaseMetaData> xMetaData(xConnection->getMetaData());
3012  // now transfer the filters into Value/TextComponent pairs
3013  ::comphelper::UStringMixEqual aCompare(xMetaData->storesMixedCaseQuotedIdentifiers());
3014 
3015  // need to parse criteria localized
3016  Reference< XNumberFormatsSupplier> xFormatSupplier( getNumberFormats(xConnection, true));
3017  Reference< XNumberFormatter> xFormatter = NumberFormatter::create(m_xComponentContext);
3018  xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
3020  const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetUILocaleDataWrapper() );
3021  OUString strDecimalSeparator = rLocaleWrapper.getNumDecimalSep();
3022 
3023  // retrieving the filter
3024  for (const Sequence < PropertyValue >& rRow : aFilterRows)
3025  {
3026  FmFilterRow aRow;
3027 
3028  // search a field for the given name
3029  for (const PropertyValue& rRefValue : rRow)
3030  {
3031  // look for the text component
3033  try
3034  {
3036  OUString aRealName;
3037 
3038  // first look with the given name
3039  if (xQueryColumns->hasByName(rRefValue.Name))
3040  {
3041  xQueryColumns->getByName(rRefValue.Name) >>= xSet;
3042 
3043  // get the RealName
3044  xSet->getPropertyValue("RealName") >>= aRealName;
3045 
3046  // compare the condition field name and the RealName
3047  if (aCompare(aRealName, rRefValue.Name))
3048  xField = xSet;
3049  }
3050  if (!xField.is())
3051  {
3052  // no we have to check every column to find the realname
3053  Reference< XIndexAccess > xColumnsByIndex(xQueryColumns, UNO_QUERY);
3054  for (sal_Int32 n = 0, nCount = xColumnsByIndex->getCount(); n < nCount; n++)
3055  {
3056  xColumnsByIndex->getByIndex(n) >>= xSet;
3057  xSet->getPropertyValue("RealName") >>= aRealName;
3058  if (aCompare(aRealName, rRefValue.Name))
3059  {
3060  // get the column by its alias
3061  xField = xSet;
3062  break;
3063  }
3064  }
3065  }
3066  if (!xField.is())
3067  continue;
3068  }
3069  catch (const Exception&)
3070  {
3071  continue;
3072  }
3073 
3074  // find the text component
3075  for (const auto& rFieldInfo : rFieldInfos)
3076  {
3077  // we found the field so insert a new entry to the filter row
3078  if (rFieldInfo.xField == xField)
3079  {
3080  // do we already have the control ?
3081  if (aRow.find(rFieldInfo.xText) != aRow.end())
3082  {
3083  OString aVal = m_pParser->getContext().getIntlKeywordAscii(IParseContext::InternationalKeyCode::And);
3084  OUString aCompText = aRow[rFieldInfo.xText] + " " +
3085  OUString(aVal.getStr(),aVal.getLength(),RTL_TEXTENCODING_ASCII_US) + " " +
3086  ::comphelper::getString(rRefValue.Value);
3087  aRow[rFieldInfo.xText] = aCompText;
3088  }
3089  else
3090  {
3091  OUString sPredicate,sErrorMsg;
3092  rRefValue.Value >>= sPredicate;
3093  std::unique_ptr< OSQLParseNode > pParseNode = predicateTree(sErrorMsg, sPredicate, xFormatter, xField);
3094  if ( pParseNode != nullptr )
3095  {
3096  OUString sCriteria;
3097  switch (rRefValue.Handle)
3098  {
3099  case css::sdb::SQLFilterOperator::EQUAL:
3100  sCriteria += "=";
3101  break;
3102  case css::sdb::SQLFilterOperator::NOT_EQUAL:
3103  sCriteria += "!=";
3104  break;
3105  case css::sdb::SQLFilterOperator::LESS:
3106  sCriteria += "<";
3107  break;
3108  case css::sdb::SQLFilterOperator::GREATER:
3109  sCriteria += ">";
3110  break;
3111  case css::sdb::SQLFilterOperator::LESS_EQUAL:
3112  sCriteria += "<=";
3113  break;
3114  case css::sdb::SQLFilterOperator::GREATER_EQUAL:
3115  sCriteria += ">=";
3116  break;
3117  case css::sdb::SQLFilterOperator::LIKE:
3118  sCriteria += "LIKE ";
3119  break;
3120  case css::sdb::SQLFilterOperator::NOT_LIKE:
3121  sCriteria += "NOT LIKE ";
3122  break;
3123  case css::sdb::SQLFilterOperator::SQLNULL:
3124  sCriteria += "IS NULL";
3125  break;
3126  case css::sdb::SQLFilterOperator::NOT_SQLNULL:
3127  sCriteria += "IS NOT NULL";
3128  break;
3129  }
3130  pParseNode->parseNodeToPredicateStr( sCriteria
3131  ,xConnection
3132  ,xFormatter
3133  ,xField
3134  ,OUString()
3135  ,aAppLocale
3136  ,strDecimalSeparator
3137  ,getParseContext());
3138  aRow[rFieldInfo.xText] = sCriteria;
3139  }
3140  }
3141  }
3142  }
3143  }
3144 
3145  if (aRow.empty())
3146  continue;
3147 
3148  impl_addFilterRow( aRow );
3149  }
3150  }
3151 
3152  // now set the filter controls
3153  for (const auto& rFieldInfo : rFieldInfos)
3154  {
3155  m_aFilterComponents.push_back( rFieldInfo.xText );
3156  }
3157 }
3158 
3159 
3160 void FormController::startFiltering()
3161 {
3162  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
3163 
3164  Reference< XConnection > xConnection( getConnection( Reference< XRowSet >( m_xModelAsIndex, UNO_QUERY ) ) );
3165  if ( !xConnection.is() )
3166  // nothing to do - can't filter a form which is not connected
3167  return;
3168 
3169  // stop listening for controls
3170  if (isListeningForChanges())
3171  stopListening();
3172 
3173  m_bFiltering = true;
3174 
3175  // as we don't want new controls to be attached to the scripting environment
3176  // we change attach flags
3177  m_bAttachEvents = false;
3178 
3179  // exchanging the controls for the current form
3180  Sequence< Reference< XControl > > aControlsCopy( m_aControls );
3181  const Reference< XControl >* pControls = aControlsCopy.getConstArray();
3182  sal_Int32 nControlCount = aControlsCopy.getLength();
3183 
3184  // the control we have to activate after replacement
3185  Reference< XNumberFormatsSupplier > xFormatSupplier = getNumberFormats(xConnection, true);
3186  Reference< XNumberFormatter > xFormatter = NumberFormatter::create(m_xComponentContext);
3187  xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
3188 
3189  // structure for storing the field info
3190  ::std::vector<FmFieldInfo> aFieldInfos;
3191 
3192  for (sal_Int32 i = nControlCount; i > 0;)
3193  {
3194  Reference< XControl > xControl = pControls[--i];
3195  if (xControl.is())
3196  {
3197  // no events for the control anymore
3198  removeFromEventAttacher(xControl);
3199 
3200  // do we have a mode selector
3201  Reference< XModeSelector > xSelector(xControl, UNO_QUERY);
3202  if (xSelector.is())
3203  {
3204  xSelector->setMode( "FilterMode" );
3205 
3206  // listening for new controls of the selector
3207  Reference< XContainer > xContainer(xSelector, UNO_QUERY);
3208  if (xContainer.is())
3209  xContainer->addContainerListener(this);
3210 
3211  Reference< XEnumerationAccess > xElementAccess(xSelector, UNO_QUERY);
3212  if (xElementAccess.is())
3213  {
3214  Reference< XEnumeration > xEnumeration(xElementAccess->createEnumeration());
3215  Reference< XControl > xSubControl;
3216  while (xEnumeration->hasMoreElements())
3217  {
3218  xEnumeration->nextElement() >>= xSubControl;
3219  if (xSubControl.is())
3220  {
3221  Reference< XPropertySet > xSet(xSubControl->getModel(), UNO_QUERY);
3222  if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
3223  {
3224  // does the model use a bound field ?
3226  xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
3227 
3228  Reference< XTextComponent > xText(xSubControl, UNO_QUERY);
3229  // may we filter the field?
3230  if (xText.is() && xField.is() && ::comphelper::hasProperty(FM_PROP_SEARCHABLE, xField) &&
3231  ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_SEARCHABLE)))
3232  {
3233  aFieldInfos.emplace_back(xField, xText);
3234  xText->addTextListener(this);
3235  }
3236  }
3237  }
3238  }
3239  }
3240  continue;
3241  }
3242 
3243  Reference< XPropertySet > xModel( xControl->getModel(), UNO_QUERY );
3244  if (xModel.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xModel))
3245  {
3246  // does the model use a bound field ?
3247  Any aVal = xModel->getPropertyValue(FM_PROP_BOUNDFIELD);
3249  aVal >>= xField;
3250 
3251  // may we filter the field?
3252 
3253  if ( xField.is()
3254  && ::comphelper::hasProperty( FM_PROP_SEARCHABLE, xField )
3255  && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_SEARCHABLE ) )
3256  )
3257  {
3258  // create a filter control
3259  Reference< XControl > xFilterControl = form::control::FilterControl::createWithFormat(
3261  getDialogParentWindow(this),
3262  xFormatter,
3263  xModel);
3264 
3265  if ( replaceControl( xControl, xFilterControl ) )
3266  {
3267  Reference< XTextComponent > xFilterText( xFilterControl, UNO_QUERY );
3268  aFieldInfos.emplace_back( xField, xFilterText );
3269  xFilterText->addTextListener(this);
3270  }
3271  }
3272  }
3273  else
3274  {
3275  // unsubscribe from EventManager
3276  }
3277  }
3278  }
3279 
3280  // we have all filter controls now, so the next step is to read the filters from the form
3281  // resolve all aliases and set the current filter to the according structure
3282  setFilter(aFieldInfos);
3283 
3284  Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
3285  if ( xSet.is() )
3286  stopFormListening( xSet, true );
3287 
3288  impl_setTextOnAllFilter_throw();
3289 
3290  // lock all controls which are not used for filtering
3291  m_bLocked = determineLockState();
3292  setLocks();
3293  m_bAttachEvents = true;
3294 }
3295 
3296 
3297 void FormController::stopFiltering()
3298 {
3299  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
3300  if ( !m_bFiltering ) // #104693# OJ
3301  { // nothing to do
3302  return;
3303  }
3304 
3305  m_bFiltering = false;
3306  m_bDetachEvents = false;
3307 
3308  ::comphelper::disposeComponent(m_xComposer);
3309 
3310  // exchanging the controls for the current form
3311  Sequence< Reference< XControl > > aControlsCopy( m_aControls );
3312  const Reference< XControl > * pControls = aControlsCopy.getConstArray();
3313  sal_Int32 nControlCount = aControlsCopy.getLength();
3314 
3315  // clear the filter control map
3316  ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), RemoveComponentTextListener( this ) );
3317  m_aFilterComponents.clear();
3318 
3319  for ( sal_Int32 i = nControlCount; i > 0; )
3320  {
3321  Reference< XControl > xControl = pControls[--i];
3322  if (xControl.is())
3323  {
3324  // now enable event handling again
3325  addToEventAttacher(xControl);
3326 
3327  Reference< XModeSelector > xSelector(xControl, UNO_QUERY);
3328  if (xSelector.is())
3329  {
3330  xSelector->setMode( "DataMode" );
3331 
3332  // listening for new controls of the selector
3333  Reference< XContainer > xContainer(xSelector, UNO_QUERY);
3334  if (xContainer.is())
3335  xContainer->removeContainerListener(this);
3336  continue;
3337  }
3338 
3339  Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
3340  if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
3341  {
3342  // does the model use a bound field ?
3344  xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
3345 
3346  // may we filter the field?
3347  if ( xField.is()
3348  && ::comphelper::hasProperty( FM_PROP_SEARCHABLE, xField )
3349  && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_SEARCHABLE ) )
3350  )
3351  {
3352  OUString sServiceName;
3353  OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DEFAULTCONTROL ) >>= sServiceName );
3354  Reference< XControl > xNewControl( m_xComponentContext->getServiceManager()->createInstanceWithContext( sServiceName, m_xComponentContext ), UNO_QUERY );
3355  replaceControl( xControl, xNewControl );
3356  }
3357  }
3358  }
3359  }
3360 
3361  Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
3362  if ( xSet.is() )
3363  startFormListening( xSet, true );
3364 
3365  m_bDetachEvents = true;
3366 
3367  m_aFilterRows.clear();
3368  m_nCurrentFilterPosition = -1;
3369 
3370  // release the locks if possible
3371  // lock all controls which are not used for filtering
3372  m_bLocked = determineLockState();
3373  setLocks();
3374 
3375  // restart listening for control modifications
3376  if (isListeningForChanges())
3377  startListening();
3378 }
3379 
3380 // XModeSelector
3381 
3382 void FormController::setMode(const OUString& Mode)
3383 {
3384  ::osl::MutexGuard aGuard( m_aMutex );
3385  impl_checkDisposed_throw();
3386 
3387  if (!supportsMode(Mode))
3388  throw NoSupportException();
3389 
3390  if (Mode == m_aMode)
3391  return;
3392 
3393  m_aMode = Mode;
3394 
3395  if ( Mode == "FilterMode" )
3396  startFiltering();
3397  else
3398  stopFiltering();
3399 
3400  for (const auto& rChild : m_aChildren)
3401  {
3402  Reference< XModeSelector > xMode(rChild, UNO_QUERY);
3403  if ( xMode.is() )
3404  xMode->setMode(Mode);
3405  }
3406 }
3407 
3408 
3409 OUString SAL_CALL FormController::getMode()
3410 {
3411  ::osl::MutexGuard aGuard( m_aMutex );
3412  impl_checkDisposed_throw();
3413 
3414  return m_aMode;
3415 }
3416 
3417 
3418 Sequence< OUString > SAL_CALL FormController::getSupportedModes()
3419 {
3420  ::osl::MutexGuard aGuard( m_aMutex );
3421  impl_checkDisposed_throw();
3422 
3423  static Sequence< OUString > const aModes
3424  {
3425  "DataMode",
3426  "FilterMode"
3427  };
3428  return aModes;
3429 }
3430 
3431 sal_Bool SAL_CALL FormController::supportsMode(const OUString& Mode)
3432 {
3433  ::osl::MutexGuard aGuard( m_aMutex );
3434  impl_checkDisposed_throw();
3435 
3436  Sequence< OUString > aModes(getSupportedModes());
3437  return comphelper::findValue(aModes, Mode) != -1;
3438 }
3439 
3440 css::uno::Reference<css::awt::XWindow> FormController::getDialogParentWindow(css::uno::Reference<css::form::runtime::XFormController> xFormController)
3441 {
3442  try
3443  {
3444  Reference< XControl > xContainerControl( xFormController->getContainer(), UNO_QUERY_THROW );
3445  Reference<XWindow> xContainerWindow(xContainerControl->getPeer(), UNO_QUERY_THROW);
3446  return xContainerWindow;
3447  }
3448  catch( const Exception& )
3449  {
3450  DBG_UNHANDLED_EXCEPTION("svx");
3451  }
3452  return nullptr;
3453 }
3454 
3455 bool FormController::checkFormComponentValidity( OUString& /* [out] */ _rFirstInvalidityExplanation, Reference< XControlModel >& /* [out] */ _rxFirstInvalidModel )
3456 {
3457  try
3458  {
3459  Reference< XEnumerationAccess > xControlEnumAcc( getModel(), UNO_QUERY );
3460  Reference< XEnumeration > xControlEnumeration;
3461  if ( xControlEnumAcc.is() )
3462  xControlEnumeration = xControlEnumAcc->createEnumeration();
3463  OSL_ENSURE( xControlEnumeration.is(), "FormController::checkFormComponentValidity: cannot enumerate the controls!" );
3464  if ( !xControlEnumeration.is() )
3465  // assume all valid
3466  return true;
3467 
3468  Reference< XValidatableFormComponent > xValidatable;
3469  while ( xControlEnumeration->hasMoreElements() )
3470  {
3471  if ( !( xControlEnumeration->nextElement() >>= xValidatable ) )
3472  // control does not support validation
3473  continue;
3474 
3475  if ( xValidatable->isValid() )
3476  continue;
3477 
3478  Reference< XValidator > xValidator( xValidatable->getValidator() );
3479  OSL_ENSURE( xValidator.is(), "FormController::checkFormComponentValidity: invalid, but no validator?" );
3480  if ( !xValidator.is() )
3481  // this violates the interface definition of css.form.validation.XValidatableFormComponent ...
3482  continue;
3483 
3484  _rFirstInvalidityExplanation = xValidator->explainInvalid( xValidatable->getCurrentValue() );
3485  _rxFirstInvalidModel.set(xValidatable, css::uno::UNO_QUERY);
3486  return false;
3487  }
3488  }
3489  catch( const Exception& )
3490  {
3491  DBG_UNHANDLED_EXCEPTION("svx");
3492  }
3493  return true;
3494 }
3495 
3496 
3497 Reference< XControl > FormController::locateControl( const Reference< XControlModel >& _rxModel )
3498 {
3499  try
3500  {
3501  const Sequence< Reference< XControl > > aControls( getControls() );
3502 
3503  for ( auto const & control : aControls )
3504  {
3505  OSL_ENSURE( control.is(), "FormController::locateControl: NULL-control?" );
3506  if ( control.is() )
3507  {
3508  if ( control->getModel() == _rxModel )
3509  return control;
3510  }
3511  }
3512  OSL_FAIL( "FormController::locateControl: did not find a control for this model!" );
3513  }
3514  catch( const Exception& )
3515  {
3516  DBG_UNHANDLED_EXCEPTION("svx");
3517  }
3518  return nullptr;
3519 }
3520 
3521 
3522 namespace
3523 {
3524  void displayErrorSetFocus(const OUString& _rMessage, const Reference<XControl>& _rxFocusControl,
3525  const css::uno::Reference<css::awt::XWindow>& rDialogParent)
3526  {
3527  SQLContext aError;
3528  aError.Message = SvxResId(RID_STR_WRITEERROR);
3529  aError.Details = _rMessage;
3530  displayException(aError, rDialogParent);
3531 
3532  if ( _rxFocusControl.is() )
3533  {
3534  Reference< XWindow > xControlWindow( _rxFocusControl, UNO_QUERY );
3535  OSL_ENSURE( xControlWindow.is(), "displayErrorSetFocus: invalid control!" );
3536  if ( xControlWindow.is() )
3537  xControlWindow->setFocus();
3538  }
3539  }
3540 
3541  bool lcl_shouldValidateRequiredFields_nothrow( const Reference< XInterface >& _rxForm )
3542  {
3543  try
3544  {
3545  static constexpr OUStringLiteral s_sFormsCheckRequiredFields = u"FormsCheckRequiredFields";
3546 
3547  // first, check whether the form has a property telling us the answer
3548  // this allows people to use the XPropertyContainer interface of a form to control
3549  // the behaviour on a per-form basis.
3550  Reference< XPropertySet > xFormProps( _rxForm, UNO_QUERY_THROW );
3551  Reference< XPropertySetInfo > xPSI( xFormProps->getPropertySetInfo() );
3552  if ( xPSI->hasPropertyByName( s_sFormsCheckRequiredFields ) )
3553  {
3554  bool bShouldValidate = true;
3555  OSL_VERIFY( xFormProps->getPropertyValue( s_sFormsCheckRequiredFields ) >>= bShouldValidate );
3556  return bShouldValidate;
3557  }
3558 
3559  // next, check the data source which created the connection
3560  Reference< XChild > xConnectionAsChild( xFormProps->getPropertyValue( FM_PROP_ACTIVE_CONNECTION ), UNO_QUERY_THROW );
3561  Reference< XPropertySet > xDataSource( xConnectionAsChild->getParent(), UNO_QUERY );
3562  if ( !xDataSource.is() )
3563  // seldom (but possible): this is not a connection created by a data source
3564  return true;
3565 
3566  Reference< XPropertySet > xDataSourceSettings(
3567  xDataSource->getPropertyValue("Settings"),
3568  UNO_QUERY_THROW );
3569 
3570  bool bShouldValidate = true;
3571  OSL_VERIFY( xDataSourceSettings->getPropertyValue( s_sFormsCheckRequiredFields ) >>= bShouldValidate );
3572  return bShouldValidate;
3573  }
3574  catch( const Exception& )
3575  {
3576  DBG_UNHANDLED_EXCEPTION("svx");
3577  }
3578 
3579  return true;
3580  }
3581 }
3582 
3583 // XRowSetApproveListener
3584 
3585 sal_Bool SAL_CALL FormController::approveRowChange(const RowChangeEvent& _rEvent)
3586 {
3587  ::osl::ClearableMutexGuard aGuard( m_aMutex );
3588  impl_checkDisposed_throw();
3589 
3590  ::comphelper::OInterfaceIteratorHelper2 aIter(m_aRowSetApproveListeners);
3591  bool bValid = true;
3592  if (aIter.hasMoreElements())
3593  {
3594  RowChangeEvent aEvt( _rEvent );
3595  aEvt.Source = *this;
3596  bValid = static_cast<XRowSetApproveListener*>(aIter.next())->approveRowChange(aEvt);
3597  }
3598 
3599  if ( !bValid )
3600  return bValid;
3601 
3602  if ( ( _rEvent.Action != RowChangeAction::INSERT )
3603  && ( _rEvent.Action != RowChangeAction::UPDATE )
3604  )
3605  return bValid;
3606 
3607  // if some of the control models are bound to validators, check them
3608  OUString sInvalidityExplanation;
3609  Reference< XControlModel > xInvalidModel;
3610  if ( !checkFormComponentValidity( sInvalidityExplanation, xInvalidModel ) )
3611  {
3612  Reference< XControl > xControl( locateControl( xInvalidModel ) );
3613  aGuard.clear();
3614  displayErrorSetFocus( sInvalidityExplanation, xControl, getDialogParentWindow(this) );
3615  return false;
3616  }
3617 
3618  // check values on NULL and required flag
3619  if ( !lcl_shouldValidateRequiredFields_nothrow( _rEvent.Source ) )
3620  return true;
3621 
3622  OSL_ENSURE(m_pColumnInfoCache, "FormController::approveRowChange: no column infos!");
3623  if (!m_pColumnInfoCache)
3624  return true;
3625 
3626  try
3627  {
3628  if ( !m_pColumnInfoCache->controlsInitialized() )
3629  m_pColumnInfoCache->initializeControls( getControls() );
3630 
3631  size_t colCount = m_pColumnInfoCache->getColumnCount();
3632  for ( size_t col = 0; col < colCount; ++col )
3633  {
3634  const ColumnInfo& rColInfo = m_pColumnInfoCache->getColumnInfo( col );
3635 
3636  if ( rColInfo.bAutoIncrement )
3637  continue;
3638 
3639  if ( rColInfo.bReadOnly )
3640  continue;
3641 
3642  if ( !rColInfo.xFirstControlWithInputRequired.is() && !rColInfo.xFirstGridWithInputRequiredColumn.is() )
3643  {
3644  continue;
3645  }
3646 
3647  // TODO: in case of binary fields, this "getString" below is extremely expensive
3648  if ( !rColInfo.xColumn->wasNull() || !rColInfo.xColumn->getString().isEmpty() )
3649  continue;
3650 
3651  OUString sMessage( SvxResId( RID_ERR_FIELDREQUIRED ) );
3652  sMessage = sMessage.replaceFirst( "#", rColInfo.sName );
3653 
3654  // the control to focus
3655  Reference< XControl > xControl( rColInfo.xFirstControlWithInputRequired );
3656  if ( !xControl.is() )
3657  xControl.set( rColInfo.xFirstGridWithInputRequiredColumn, UNO_QUERY );
3658 
3659  aGuard.clear();
3660  displayErrorSetFocus( sMessage, rColInfo.xFirstControlWithInputRequired, getDialogParentWindow(this) );
3661  return false;
3662  }
3663  }
3664  catch( const Exception& )
3665  {
3666  DBG_UNHANDLED_EXCEPTION("svx");
3667  }
3668 
3669  return true;
3670 }
3671 
3672 
3673 sal_Bool SAL_CALL FormController::approveCursorMove(const EventObject& event)
3674 {
3675  ::osl::MutexGuard aGuard( m_aMutex );
3676  impl_checkDisposed_throw();
3677 
3678  ::comphelper::OInterfaceIteratorHelper2 aIter(m_aRowSetApproveListeners);
3679  if (aIter.hasMoreElements())
3680  {
3681  EventObject aEvt(event);
3682  aEvt.Source = *this;
3683  return static_cast<XRowSetApproveListener*>(aIter.next())->approveCursorMove(aEvt);
3684  }
3685 
3686  return true;
3687 }
3688 
3689 
3690 sal_Bool SAL_CALL FormController::approveRowSetChange(const EventObject& event)
3691 {
3692  ::osl::MutexGuard aGuard( m_aMutex );
3693  impl_checkDisposed_throw();
3694 
3695  ::comphelper::OInterfaceIteratorHelper2 aIter(m_aRowSetApproveListeners);
3696  if (aIter.hasMoreElements())
3697  {
3698  EventObject aEvt(event);
3699  aEvt.Source = *this;
3700  return static_cast<XRowSetApproveListener*>(aIter.next())->approveRowSetChange(aEvt);
3701  }
3702 
3703  return true;
3704 }
3705 
3706 // XRowSetApproveBroadcaster
3707 
3708 void SAL_CALL FormController::addRowSetApproveListener(const Reference< XRowSetApproveListener > & _rxListener)
3709 {
3710  ::osl::MutexGuard aGuard( m_aMutex );
3711  impl_checkDisposed_throw();
3712 
3713  m_aRowSetApproveListeners.addInterface(_rxListener);
3714 }
3715 
3716 
3717 void SAL_CALL FormController::removeRowSetApproveListener(const Reference< XRowSetApproveListener > & _rxListener)
3718 {
3719  ::osl::MutexGuard aGuard( m_aMutex );
3720  impl_checkDisposed_throw();
3721 
3722  m_aRowSetApproveListeners.removeInterface(_rxListener);
3723 }
3724 
3725 // XErrorListener
3726 
3727 void SAL_CALL FormController::errorOccured(const SQLErrorEvent& aEvent)
3728 {
3729  ::osl::ClearableMutexGuard aGuard( m_aMutex );
3730  impl_checkDisposed_throw();
3731 
3732  ::comphelper::OInterfaceIteratorHelper2 aIter(m_aErrorListeners);
3733  if (aIter.hasMoreElements())
3734  {
3735  SQLErrorEvent aEvt(aEvent);
3736  aEvt.Source = *this;
3737  static_cast<XSQLErrorListener*>(aIter.next())->errorOccured(aEvt);
3738  }
3739  else
3740  {
3741  aGuard.clear();
3742  displayException(aEvent, getDialogParentWindow(this));
3743  }
3744 }
3745 
3746 // XErrorBroadcaster
3747 
3748 void SAL_CALL FormController::addSQLErrorListener(const Reference< XSQLErrorListener > & aListener)
3749 {
3750  ::osl::MutexGuard aGuard( m_aMutex );
3751  impl_checkDisposed_throw();
3752 
3753  m_aErrorListeners.addInterface(aListener);
3754 }
3755 
3756 
3757 void SAL_CALL FormController::removeSQLErrorListener(const Reference< XSQLErrorListener > & aListener)
3758 {
3759  ::osl::MutexGuard aGuard( m_aMutex );
3760  impl_checkDisposed_throw();
3761 
3762  m_aErrorListeners.removeInterface(aListener);
3763 }
3764 
3765 // XDatabaseParameterBroadcaster2
3766 
3767 void SAL_CALL FormController::addDatabaseParameterListener(const Reference< XDatabaseParameterListener > & aListener)
3768 {
3769  ::osl::MutexGuard aGuard( m_aMutex );
3770  impl_checkDisposed_throw();
3771 
3772  m_aParameterListeners.addInterface(aListener);
3773 }
3774 
3775 
3776 void SAL_CALL FormController::removeDatabaseParameterListener(const Reference< XDatabaseParameterListener > & aListener)
3777 {
3778  ::osl::MutexGuard aGuard( m_aMutex );
3779  impl_checkDisposed_throw();
3780 
3781  m_aParameterListeners.removeInterface(aListener);
3782 }
3783 
3784 // XDatabaseParameterBroadcaster
3785 
3786 void SAL_CALL FormController::addParameterListener(const Reference< XDatabaseParameterListener > & aListener)
3787 {
3788  FormController::addDatabaseParameterListener( aListener );
3789 }
3790 
3791 
3792 void SAL_CALL FormController::removeParameterListener(const Reference< XDatabaseParameterListener > & aListener)
3793 {
3794  FormController::removeDatabaseParameterListener( aListener );
3795 }
3796 
3797 // XDatabaseParameterListener
3798 
3799 sal_Bool SAL_CALL FormController::approveParameter(const DatabaseParameterEvent& aEvent)
3800 {
3801  SolarMutexGuard aSolarGuard;
3802  ::osl::MutexGuard aGuard( m_aMutex );
3803  impl_checkDisposed_throw();
3804 
3805  ::comphelper::OInterfaceIteratorHelper2 aIter(m_aParameterListeners);
3806  if (aIter.hasMoreElements())
3807  {
3808  DatabaseParameterEvent aEvt(aEvent);
3809  aEvt.Source = *this;
3810  return static_cast<XDatabaseParameterListener*>(aIter.next())->approveParameter(aEvt);
3811  }
3812  else
3813  {
3814  // default handling: instantiate an interaction handler and let it handle the parameter request
3815  try
3816  {
3817  if ( !ensureInteractionHandler() )
3818  return false;
3819 
3820  // two continuations allowed: OK and Cancel
3821  rtl::Reference<OParameterContinuation> pParamValues = new OParameterContinuation;
3823  // the request
3824  ParametersRequest aRequest;
3825  aRequest.Parameters = aEvent.Parameters;
3826  aRequest.Connection = getConnection(Reference< XRowSet >(aEvent.Source, UNO_QUERY));
3827  rtl::Reference<OInteractionRequest> pParamRequest = new OInteractionRequest(makeAny(aRequest));
3828  // some knittings
3829  pParamRequest->addContinuation(pParamValues);
3830  pParamRequest->addContinuation(pAbort);
3831 
3832  // handle the request
3833  m_xInteractionHandler->handle(pParamRequest);
3834 
3835  if (!pParamValues->wasSelected())
3836  // canceled
3837  return false;
3838 
3839  // transfer the values into the parameter supplier
3840  Sequence< PropertyValue > aFinalValues = pParamValues->getValues();
3841  if (aFinalValues.getLength() != aRequest.Parameters->getCount())
3842  {
3843  OSL_FAIL("FormController::approveParameter: the InteractionHandler returned nonsense!");
3844  return false;
3845  }
3846  const PropertyValue* pFinalValues = aFinalValues.getConstArray();
3847  for (sal_Int32 i=0; i<aFinalValues.getLength(); ++i, ++pFinalValues)
3848  {
3850  aRequest.Parameters->getByIndex(i), css::uno::UNO_QUERY);
3851  if (xParam.is())
3852  {
3853 #ifdef DBG_UTIL
3854  OUString sName;
3855  xParam->getPropertyValue(FM_PROP_NAME) >>= sName;
3856  DBG_ASSERT(sName == pFinalValues->Name, "FormController::approveParameter: suspicious value names!");
3857 #endif
3858  try { xParam->setPropertyValue(FM_PROP_VALUE, pFinalValues->Value); }
3859  catch(Exception&)
3860  {
3861  OSL_FAIL("FormController::approveParameter: setting one of the properties failed!");
3862  }
3863  }
3864  }
3865  }
3866  catch(Exception&)
3867  {
3868  DBG_UNHANDLED_EXCEPTION("svx");
3869  }
3870  }
3871  return true;
3872 }
3873 
3874 // XConfirmDeleteBroadcaster
3875 
3876 void SAL_CALL FormController::addConfirmDeleteListener(const Reference< XConfirmDeleteListener > & aListener)
3877 {
3878  ::osl::MutexGuard aGuard( m_aMutex );
3879  impl_checkDisposed_throw();
3880 
3881  m_aDeleteListeners.addInterface(aListener);
3882 }
3883 
3884 
3885 void SAL_CALL FormController::removeConfirmDeleteListener(const Reference< XConfirmDeleteListener > & aListener)
3886 {
3887  ::osl::MutexGuard aGuard( m_aMutex );
3888  impl_checkDisposed_throw();
3889 
3890  m_aDeleteListeners.removeInterface(aListener);
3891 }
3892 
3893 // XConfirmDeleteListener
3894 
3895 sal_Bool SAL_CALL FormController::confirmDelete(const RowChangeEvent& aEvent)
3896 {
3897  ::osl::MutexGuard aGuard( m_aMutex );
3898  impl_checkDisposed_throw();
3899 
3900  ::comphelper::OInterfaceIteratorHelper2 aIter(m_aDeleteListeners);
3901  if (aIter.hasMoreElements())
3902  {
3903  RowChangeEvent aEvt(aEvent);
3904  aEvt.Source = *this;
3905  return static_cast<XConfirmDeleteListener*>(aIter.next())->confirmDelete(aEvt);
3906  }
3907  // default handling: instantiate an interaction handler and let it handle the request
3908 
3909  OUString sTitle;
3910  sal_Int32 nLength = aEvent.Rows;
3911  if ( nLength > 1 )
3912  {
3913  sTitle = SvxResId( RID_STR_DELETECONFIRM_RECORDS );
3914  sTitle = sTitle.replaceFirst( "#", OUString::number(nLength) );
3915  }
3916  else
3917  sTitle = SvxResId( RID_STR_DELETECONFIRM_RECORD );
3918 
3919  try
3920  {
3921  if ( !ensureInteractionHandler() )
3922  return false;
3923 
3924  // two continuations allowed: Yes and No
3927 
3928  // the request
3929  SQLWarning aWarning;
3930  aWarning.Message = sTitle;
3931  SQLWarning aDetails;
3932  aDetails.Message = SvxResId(RID_STR_DELETECONFIRM);
3933  aWarning.NextException <<= aDetails;
3934 
3935  rtl::Reference<OInteractionRequest> pRequest = new OInteractionRequest( makeAny( aWarning ) );
3936 
3937  // some knittings
3938  pRequest->addContinuation( pApprove );
3939  pRequest->addContinuation( pDisapprove );
3940 
3941  // handle the request
3942  m_xInteractionHandler->handle( pRequest );
3943 
3944  if ( pApprove->wasSelected() )
3945  return true;
3946  }
3947  catch( const Exception& )
3948  {
3949  DBG_UNHANDLED_EXCEPTION("svx");
3950  }
3951 
3952  return false;
3953 }
3954 
3955 
3956 void SAL_CALL FormController::invalidateFeatures( const Sequence< ::sal_Int16 >& Features )
3957 {
3958  ::osl::MutexGuard aGuard( m_aMutex );
3959  // for now, just copy the ids of the features, because...
3960  m_aInvalidFeatures.insert( Features.begin(), Features.end() );
3961 
3962  // ... we will do the real invalidation asynchronously
3963  if ( !m_aFeatureInvalidationTimer.IsActive() )
3964  m_aFeatureInvalidationTimer.Start();
3965 }
3966 
3967 
3968 void SAL_CALL FormController::invalidateAllFeatures( )
3969 {
3970  ::osl::ClearableMutexGuard aGuard( m_aMutex );
3971 
3972  Sequence< sal_Int16 > aInterceptedFeatures( comphelper::mapKeysToSequence(m_aFeatureDispatchers) );
3973 
3974  aGuard.clear();
3975  if ( aInterceptedFeatures.hasElements() )
3976  invalidateFeatures( aInterceptedFeatures );
3977 }
3978 
3979 
3980 Reference< XDispatch >
3981 FormController::interceptedQueryDispatch( const URL& aURL,
3982  const OUString& /*aTargetFrameName*/, sal_Int32 /*nSearchFlags*/)
3983 {
3984  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
3985  Reference< XDispatch > xReturn;
3986  // dispatches handled by ourself
3987  if ( ( aURL.Complete == FMURL_CONFIRM_DELETION )
3988  || ( ( aURL.Complete == "private:/InteractionHandler" )
3989  && ensureInteractionHandler()
3990  )
3991  )
3992  xReturn = static_cast< XDispatch* >( this );
3993 
3994  // dispatches of FormSlot-URLs we have to translate
3995  if ( !xReturn.is() && m_xFormOperations.is() )
3996  {
3997  // find the slot id which corresponds to the URL
3998  sal_Int32 nFeatureSlotId = svx::FeatureSlotTranslation::getControllerFeatureSlotIdForURL( aURL.Main );
3999  sal_Int16 nFormFeature = ( nFeatureSlotId != -1 ) ? svx::FeatureSlotTranslation::getFormFeatureForSlotId( nFeatureSlotId ) : -1;
4000  if ( nFormFeature > 0 )
4001  {
4002  // get the dispatcher for this feature, create if necessary
4003  DispatcherContainer::const_iterator aDispatcherPos = m_aFeatureDispatchers.find( nFormFeature );
4004  if ( aDispatcherPos == m_aFeatureDispatchers.end() )
4005  {
4006  aDispatcherPos = m_aFeatureDispatchers.emplace(
4007  nFormFeature, new svx::OSingleFeatureDispatcher( aURL, nFormFeature, m_xFormOperations, m_aMutex )
4008  ).first;
4009  }
4010 
4011  OSL_ENSURE( aDispatcherPos->second.is(), "FormController::interceptedQueryDispatch: should have a dispatcher by now!" );
4012  return aDispatcherPos->second;
4013  }
4014  }
4015 
4016  // no more to offer
4017  return xReturn;
4018 }
4019 
4020 
4021 void SAL_CALL FormController::dispatch( const URL& _rURL, const Sequence< PropertyValue >& _rArgs )
4022 {
4023  if ( _rArgs.getLength() != 1 )
4024  {
4025  OSL_FAIL( "FormController::dispatch: no arguments -> no dispatch!" );
4026  return;
4027  }
4028 
4029  if ( _rURL.Complete == "private:/InteractionHandler" )
4030  {
4031  Reference< XInteractionRequest > xRequest;
4032  OSL_VERIFY( _rArgs[0].Value >>= xRequest );
4033  if ( xRequest.is() )
4034  handle( xRequest );
4035  return;
4036  }
4037 
4038  if ( _rURL.Complete == FMURL_CONFIRM_DELETION )
4039  {
4040  OSL_FAIL( "FormController::dispatch: How do you expect me to return something via this call?" );
4041  // confirmDelete has a return value - dispatch hasn't
4042  return;
4043  }
4044 
4045  OSL_FAIL( "FormController::dispatch: unknown URL!" );
4046 }
4047 
4048 
4049 void SAL_CALL FormController::addStatusListener( const Reference< XStatusListener >& _rxListener, const URL& _rURL )
4050 {
4051  if (_rURL.Complete == FMURL_CONFIRM_DELETION)
4052  {
4053  if (_rxListener.is())
4054  { // send an initial statusChanged event
4055  FeatureStateEvent aEvent;
4056  aEvent.FeatureURL = _rURL;
4057  aEvent.IsEnabled = true;
4058  _rxListener->statusChanged(aEvent);
4059  // and don't add the listener at all (the status will never change)
4060  }
4061  }
4062  else
4063  OSL_FAIL("FormController::addStatusListener: invalid (unsupported) URL!");
4064 }
4065 
4066 
4067 Reference< XInterface > SAL_CALL FormController::getParent()
4068 {
4069  return m_xParent;
4070 }
4071 
4072 
4073 void SAL_CALL FormController::setParent( const Reference< XInterface >& Parent)
4074 {
4075  m_xParent = Parent;
4076 }
4077 
4078 
4079 void SAL_CALL FormController::removeStatusListener( const Reference< XStatusListener >& /*_rxListener*/, const URL& _rURL )
4080 {
4081  OSL_ENSURE(_rURL.Complete == FMURL_CONFIRM_DELETION, "FormController::removeStatusListener: invalid (unsupported) URL!");
4082  // we never really added the listener, so we don't need to remove it
4083 }
4084 
4085 
4086 Reference< XDispatchProviderInterceptor > FormController::createInterceptor(const Reference< XDispatchProviderInterception > & _xInterception)
4087 {
4088  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
4089 #ifdef DBG_UTIL
4090  // check if we already have an interceptor for the given object
4091  for ( const auto & it : m_aControlDispatchInterceptors )
4092  {
4093  if (it->getIntercepted() == _xInterception)
4094  OSL_FAIL("FormController::createInterceptor : we already do intercept this objects dispatches !");
4095  }
4096 #endif
4097 
4098  rtl::Reference<DispatchInterceptionMultiplexer> pInterceptor(new DispatchInterceptionMultiplexer( _xInterception, this ));
4099  m_aControlDispatchInterceptors.push_back( pInterceptor );
4100 
4101  return pInterceptor;
4102 }
4103 
4104 
4105 bool FormController::ensureInteractionHandler()
4106 {
4107  if ( m_xInteractionHandler.is() )
4108  return true;
4109  if ( m_bAttemptedHandlerCreation )
4110  return false;
4111  m_bAttemptedHandlerCreation = true;
4112 
4113  m_xInteractionHandler = InteractionHandler::createWithParent(m_xComponentContext,
4114  getDialogParentWindow(this));
4115  return m_xInteractionHandler.is();
4116 }
4117 
4118 
4119 void SAL_CALL FormController::handle( const Reference< XInteractionRequest >& _rRequest )
4120 {
4121  if ( !ensureInteractionHandler() )
4122  return;
4123  m_xInteractionHandler->handle( _rRequest );
4124 }
4125 
4126 
4127 void FormController::deleteInterceptor(const Reference< XDispatchProviderInterception > & _xInterception)
4128 {
4129  OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
4130  // search the interceptor responsible for the given object
4131  auto aIter = std::find_if(m_aControlDispatchInterceptors.begin(), m_aControlDispatchInterceptors.end(),
4132  [&_xInterception](const rtl::Reference<DispatchInterceptionMultiplexer>& rpInterceptor) {
4133  return rpInterceptor->getIntercepted() == _xInterception;
4134  });
4135  if (aIter != m_aControlDispatchInterceptors.end())
4136  {
4137  // log off the interception from its interception object
4138  (*aIter)->dispose();
4139  // remove the interceptor from our array
4140  m_aControlDispatchInterceptors.erase(aIter);
4141  }
4142 }
4143 
4144 
4145 void FormController::implInvalidateCurrentControlDependentFeatures()
4146 {
4147  Sequence< sal_Int16 > aCurrentControlDependentFeatures
4148  {
4149  FormFeature::SortAscending,
4150  FormFeature::SortDescending,
4151  FormFeature::AutoFilter,
4152  FormFeature::RefreshCurrentControl
4153  };
4154 
4155  invalidateFeatures( aCurrentControlDependentFeatures );
4156 }
4157 
4158 
4159 void SAL_CALL FormController::columnChanged( const EventObject& /*_event*/ )
4160 {
4161  implInvalidateCurrentControlDependentFeatures();
4162 }
4163 
4164 }
4165 
4166 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
virtual void ImplSetPeerProperty(const OUString &rPropName, const css::uno::Any &rVal)
Type
#define FM_PROP_READONLY
Definition: fmprop.hxx:46
FmFieldInfo(const Reference< XPropertySet > &_xField, const Reference< XTextComponent > &_xText)
OUString sMessage
sal_Int32 nIndex
sal_Int32 nNullable
const ColumnInfo & getColumnInfo(size_t _pos)
FormController(const css::uno::Reference< css::uno::XComponentContext > &_rxORB)
IMPL_LINK_NOARG(FormController, OnLoad, void *, void)
exports com.sun.star.form. control
const LanguageTag & GetUILanguageTag() const
sal_Int32 findValue(const css::uno::Sequence< T1 > &_rList, const T2 &_rValue)
#define FM_PROP_VALUE
Definition: fmprop.hxx:35
::std::vector< ColumnInfo > ColumnInfos
sal_uInt16 char char * pDesc
#define FM_PROP_ISMODIFIED
Definition: fmprop.hxx:114
OInteraction< css::task::XInteractionAbort > OInteractionAbort
static const AllSettings & GetSettings()
#define FM_PROP_DYNAMIC_CONTROL_BORDER
Definition: fmprop.hxx:138
bool controlsInitialized() const
bool getBOOL(const Any &_rAny)
sal_Int64 n
Reference< XTextComponent > xText
cppu::WeakComponentImplHelper< css::form::runtime::XFormController, css::form::runtime::XFilterController, css::awt::XFocusListener, css::form::XLoadListener, css::beans::XPropertyChangeListener, css::awt::XTextListener, css::awt::XItemListener, css::container::XContainerListener, css::util::XModifyListener, css::form::XConfirmDeleteListener, css::sdb::XSQLErrorListener, css::sdbc::XRowSetListener, css::sdb::XRowSetApproveListener, css::form::XDatabaseParameterListener, css::lang::XServiceInfo, css::form::XResetListener, css::frame::XDispatch, css::awt::XMouseListener, css::form::validation::XFormComponentValidityListener, css::task::XInteractionHandler, css::form::XGridControlListener, css::form::runtime::XFeatureInvalidation > FormController_BASE
#define FM_PROP_ISNEW
Definition: fmprop.hxx:115
sal_Int16 nId
IMPL_LINK_NOARG(XFormsPage, ItemSelectHdl, weld::TreeView &, void)
Definition: datanavi.cxx:243
Reference< XInteractionHandler > m_xInteractionHandler
Mode
Reference< XNumberFormatsSupplier > getNumberFormats(const Reference< XConnection > &_rxConn, bool _bAlloweDefault, const Reference< XComponentContext > &_rxContext)
bool bReadOnly
#define FM_PROP_AUTOINCREMENT
Definition: fmprop.hxx:48
bool isRowSetAlive(const Reference< XInterface > &_rxRowSet)
Definition: fmtools.cxx:361
const css::lang::Locale & getLocale(bool bResolveSystem=true) const
bool canUpdate(const Reference< XPropertySet > &_rxCursorSet)
#define FM_PROP_NAME
Definition: fmprop.hxx:29
Value
#define FM_PROP_CONTROL_BORDER_COLOR_FOCUS
Definition: fmprop.hxx:139
size_t pos
constexpr OUStringLiteral sServiceName
int nCount
OUString SvxResId(TranslateId aId)
Definition: dialmgr.cxx:24
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
OInteraction< css::task::XInteractionDisapprove > OInteractionDisapprove
std::mutex m_aMutex
#define FM_PROP_BOUNDFIELD
Definition: fmprop.hxx:101
sal_Int32 nRequiredGridColumn
if xFirstControlWithInputRequired is a grid control, then nRequiredGridColumn specifies the position ...
#define FMURL_CONFIRM_DELETION
Definition: fmurl.hxx:42
#define FM_PROP_CONTROL_BORDER_COLOR_MOUSE
Definition: fmprop.hxx:140
#define FM_PROP_CYCLE
Definition: fmprop.hxx:42
::std::pair< MetaAction *, int > Component
bool bAutoIncrement
Reference< XComponentContext > const m_xComponentContext
Reference< XPropertySet > xField
const OUString & getNumDecimalSep() const
#define DBG_UNHANDLED_EXCEPTION(...)
#define FM_PROP_SEARCHABLE
Definition: fmprop.hxx:50
#define DBG_ASSERT(sCon, aError)
int i
Reference< XTextListener > m_xListener
#define FM_PROP_FORM_OPERATIONS
Definition: fmprop.hxx:143
class SAL_NO_VTABLE XPropertySet
Definition: xmlexchg.hxx:28
OUString sName
css::uno::Sequence< css::uno::Type > getTypes()
#define FM_PROP_ISNULLABLE
Definition: fmprop.hxx:124
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
static sal_Int32 getControllerFeatureSlotIdForURL(const OUString &_rMainURL)
retrieves the feature id for a given feature URL
float u
unsigned char sal_Bool
void SAL_CALL elementRemoved(const css::container::ContainerEvent &Event) override
DECL_LISTENERMULTIPLEXER_END void SAL_CALL elementInserted(const css::container::ContainerEvent &Event) override
#define FM_PROP_FILTER
Definition: fmprop.hxx:47
css::uno::Type const & get()
const sal_Int16 nFormFeature
Reference< XComponentContext > getComponentContext(Reference< XMultiServiceFactory > const &factory)
css::uno::Sequence< T > concatSequences(const css::uno::Sequence< T > &rS1, const Ss &...rSn)
ColumnInfoCache(const Reference< XColumnsSupplier > &_rxColSupplier)
::std::map< css::uno::Reference< css::awt::XTextComponent >, OUString, FmXTextComponentLess > FmFilterRow
Sequence< PropertyValue > m_aValues
size_t getColumnCount() const
#define SAL_WARN_IF(condition, area, stream)
#define FM_ATTR_FILTER
Definition: fmprop.hxx:26
#define FM_PROP_ACTIVE_CONNECTION
Definition: fmprop.hxx:128
bool canInsert(const Reference< XPropertySet > &_rxCursorSet)
Reference< XGrid > xFirstGridWithInputRequiredColumn
the first grid control which contains a column which is bound to the given database column...
#define FM_PROP_CONTROL_BORDER_COLOR_INVALID
Definition: fmprop.hxx:141
#define FM_PROP_ACTIVECOMMAND
Definition: fmprop.hxx:125
#define FM_PROP_INPUT_REQUIRED
Definition: fmprop.hxx:144
void initializeControls(const Sequence< Reference< XControl > > &_rControls)
#define FM_PROP_ISREADONLY
Definition: fmprop.hxx:74
Reference< XColumn > xColumn
static sal_Int16 getFormFeatureForSlotId(sal_Int32 _nSlotId)
retrieves the css.form.runtime.FormFeature ID for a given slot ID
Reference< XConnection > getConnection(const Reference< XRowSet > &_rxRowSet)
bool hasProperty(const OUString &_rName, const Reference< XPropertySet > &_rxSet)
Reference< XSingleServiceFactory > xFactory
Reference< XModel > xModel
class FmSearchEngine - Impl class for FmSearchDialog
css::uno::XInterface * next()
#define FM_PROP_DEFAULTCONTROL
Definition: fmprop.hxx:53
sal_Int32 nLength
void dispose()
#define FM_PROP_TEXT
Definition: fmprop.hxx:39
OUString getString(const Any &_rAny)
css::uno::Reference< css::uno::XInterface > FormController_NewInstance_Impl(const css::uno::Reference< css::lang::XMultiServiceFactory > &_rxORB)
#define FM_PROP_REALNAME
Definition: fmprop.hxx:130
Reference< XControl > xFirstControlWithInputRequired
the first control which is bound to the given column, and which requires input
#define FM_ATTR_FORM_OPERATIONS
Definition: fmprop.hxx:27
OInteraction< css::task::XInteractionApprove > OInteractionApprove
struct _ADOIndex Index
AnyEventRef aEvent
void SAL_CALL createPeer(const css::uno::Reference< css::awt::XToolkit > &Toolkit, const css::uno::Reference< css::awt::XWindowPeer > &Parent) override
sal_uInt16 nPos
css::uno::Sequence< typename M::key_type > mapKeysToSequence(M const &map)
void displayException(const Any &_rExcept, const css::uno::Reference< css::awt::XWindow > &rParent)
Definition: fmtools.cxx:86
#define FM_PROP_ENABLED
Definition: fmprop.hxx:44
bool useDynamicBorderColor(DocumentType _eDocType)
determines whether for the given document type, dynamic control border coloring is enabled ...
css::uno::Any SAL_CALL makeAny(const SharedUNOComponent< INTERFACE, COMPONENT > &value)