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