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