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