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