tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / forms / source / component / FormComponent.cxx
blob74d46b8c0a83730d8e95593d2c09daeb74f3ecd7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
19 #include <componenttools.hxx>
20 #include <FormComponent.hxx>
21 #include <strings.hrc>
22 #include <frm_resource.hxx>
23 #include <property.hxx>
24 #include <services.hxx>
26 #include <com/sun/star/awt/XTextComponent.hpp>
27 #include <com/sun/star/awt/XWindow.hpp>
28 #include <com/sun/star/beans/PropertyAttribute.hpp>
29 #include <com/sun/star/form/FormComponentType.hpp>
30 #include <com/sun/star/form/XForm.hpp>
31 #include <com/sun/star/form/XLoadable.hpp>
32 #include <com/sun/star/form/binding/IncompatibleTypesException.hpp>
33 #include <com/sun/star/io/IOException.hpp>
34 #include <com/sun/star/io/XMarkableStream.hpp>
35 #include <com/sun/star/lang/DisposedException.hpp>
36 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
37 #include <com/sun/star/sdb/XRowSetChangeBroadcaster.hpp>
38 #include <com/sun/star/sdb/XRowSetSupplier.hpp>
39 #include <com/sun/star/sdbc/ColumnValue.hpp>
40 #include <com/sun/star/sdbc/DataType.hpp>
41 #include <com/sun/star/sdbc/SQLException.hpp>
42 #include <com/sun/star/util/VetoException.hpp>
43 #include <com/sun/star/util/XModifyBroadcaster.hpp>
45 #include <comphelper/basicio.hxx>
46 #include <comphelper/guarding.hxx>
47 #include <comphelper/property.hxx>
48 #include <connectivity/dbtools.hxx>
49 #include <cppuhelper/exc_hlp.hxx>
50 #include <cppuhelper/supportsservice.hxx>
51 #include <tools/debug.hxx>
52 #include <comphelper/diagnose_ex.hxx>
53 #include <sal/log.hxx>
55 #include <algorithm>
57 namespace frm
59 using namespace ::com::sun::star::uno;
60 using namespace ::com::sun::star::sdb;
61 using namespace ::com::sun::star::sdbc;
62 using namespace ::com::sun::star::sdbcx;
63 using namespace ::com::sun::star::beans;
64 using namespace ::com::sun::star::container;
65 using namespace ::com::sun::star::form;
66 using namespace ::com::sun::star::awt;
67 using namespace ::com::sun::star::io;
68 using namespace ::com::sun::star::lang;
69 using namespace ::com::sun::star::util;
70 using namespace ::com::sun::star::form::binding;
71 using namespace ::com::sun::star::form::validation;
72 using namespace ::dbtools;
73 using namespace ::comphelper;
75 // FieldChangeNotifier
76 void ControlModelLock::impl_notifyAll_nothrow()
78 m_rModel.firePropertyChanges( m_aHandles, m_aOldValues, m_aNewValues, OControlModel::LockAccess() );
81 void ControlModelLock::addPropertyNotification( const sal_Int32 _nHandle, const Any& _rOldValue, const Any& _rNewValue )
83 assert( m_aHandles.size() == m_aOldValues.size() && m_aOldValues.size() == m_aNewValues.size() );
85 m_aHandles.push_back( _nHandle );
86 m_aOldValues.push_back( _rOldValue );
87 m_aNewValues.push_back( _rNewValue );
90 namespace {
92 class FieldChangeNotifier
94 public:
95 explicit FieldChangeNotifier(ControlModelLock& _rLock)
96 : m_rLock( _rLock )
97 , m_rModel( dynamic_cast< OBoundControlModel& >( _rLock.getModel() ) )
99 m_xOldField = m_rModel.getField();
102 ~FieldChangeNotifier()
104 Reference< XPropertySet > xNewField( m_rModel.getField() );
105 if ( m_xOldField != xNewField )
106 m_rLock.addPropertyNotification( PROPERTY_ID_BOUNDFIELD, Any( m_xOldField ), Any( xNewField ) );
109 private:
110 ControlModelLock& m_rLock;
111 OBoundControlModel& m_rModel;
112 Reference< XPropertySet > m_xOldField;
117 // base class for form layer controls
118 OControl::OControl( const Reference< XComponentContext >& _rxContext, const OUString& _rAggregateService, const bool _bSetDelegator )
119 :OComponentHelper(m_aMutex)
120 ,m_xContext( _rxContext )
122 // Aggregate VCL Control
123 // Increment the RefCount for aggregates, because the aggregate by itself increments the RefCount in the setDelegator
124 osl_atomic_increment( &m_refCount );
126 m_xAggregate.set(_rxContext->getServiceManager()->createInstanceWithContext(_rAggregateService, _rxContext), css::uno::UNO_QUERY);
127 m_xControl.set(m_xAggregate, css::uno::UNO_QUERY);
129 osl_atomic_decrement( &m_refCount );
131 if ( _bSetDelegator )
132 doSetDelegator();
135 OControl::~OControl()
137 doResetDelegator();
140 void OControl::doResetDelegator()
142 if ( m_xAggregate.is() )
143 m_xAggregate->setDelegator( nullptr );
146 void OControl::doSetDelegator()
148 osl_atomic_increment( &m_refCount );
149 if ( m_xAggregate.is() )
150 { // those brackets are important for some compilers, don't remove!
151 // (they ensure that the temporary object created in the line below
152 // is destroyed *before* the refcount-decrement)
153 m_xAggregate->setDelegator( static_cast< XWeak* >( this ) );
155 osl_atomic_decrement( &m_refCount );
158 // UNO Binding
159 Any SAL_CALL OControl::queryAggregation( const Type& _rType )
161 // ask the base class
162 Any aReturn( OComponentHelper::queryAggregation(_rType) );
163 // ask our own interfaces
164 if (!aReturn.hasValue())
166 aReturn = OControl_BASE::queryInterface(_rType);
167 // ask our aggregate
168 if (!aReturn.hasValue() && m_xAggregate.is())
169 aReturn = m_xAggregate->queryAggregation(_rType);
172 return aReturn;
175 Sequence<sal_Int8> SAL_CALL OControl::getImplementationId()
177 return css::uno::Sequence<sal_Int8>();
180 Sequence<Type> SAL_CALL OControl::getTypes()
182 TypeBag aTypes( _getTypes() );
184 if (auto xProv = query_aggregation<XTypeProvider>(m_xAggregate))
185 aTypes.addTypes( xProv->getTypes() );
187 return aTypes.getTypes();
190 Sequence<Type> OControl::_getTypes()
192 return TypeBag( OComponentHelper::getTypes(), OControl_BASE::getTypes() ).getTypes();
195 // OComponentHelper
196 void OControl::disposing()
198 OComponentHelper::disposing();
200 m_aWindowStateGuard.attach( nullptr, nullptr );
202 if (auto xComp = query_aggregation<XComponent>(m_xAggregate))
203 xComp->dispose();
206 // XServiceInfo
207 sal_Bool SAL_CALL OControl::supportsService(const OUString& _rsServiceName)
209 return cppu::supportsService(this, _rsServiceName);
212 Sequence< OUString > OControl::getAggregateServiceNames() const
214 Sequence< OUString > aAggServices;
215 if (auto xInfo = query_aggregation<XServiceInfo>(m_xAggregate))
216 aAggServices = xInfo->getSupportedServiceNames();
218 return aAggServices;
221 Sequence<OUString> SAL_CALL OControl::getSupportedServiceNames()
223 // no own supported service names
224 return getAggregateServiceNames();
227 // XEventListener
228 void SAL_CALL OControl::disposing(const css::lang::EventObject& _rEvent)
230 auto xAggAsIface = query_aggregation<XInterface>(m_xAggregate);
232 // does the disposing come from the aggregate?
233 if (xAggAsIface != Reference< XInterface >(_rEvent.Source, UNO_QUERY))
234 { // no -> forward it
235 if (auto xListener = query_aggregation<css::lang::XEventListener>(m_xAggregate))
236 xListener->disposing(_rEvent);
240 // XControl
241 void SAL_CALL OControl::setContext(const Reference< XInterface >& Context)
243 if (m_xControl.is())
244 m_xControl->setContext(Context);
247 Reference< XInterface > SAL_CALL OControl::getContext()
249 return m_xControl.is() ? m_xControl->getContext() : Reference< XInterface >();
252 void OControl::impl_resetStateGuard_nothrow()
254 Reference< XWindow2 > xWindow;
255 Reference< XControlModel > xModel;
258 xWindow.set( getPeer(), UNO_QUERY );
259 xModel = getModel();
261 catch( const Exception& )
263 DBG_UNHANDLED_EXCEPTION("forms.component");
265 m_aWindowStateGuard.attach( xWindow, xModel );
268 void SAL_CALL OControl::createPeer(const Reference<XToolkit>& _rxToolkit, const Reference<XWindowPeer>& _rxParent)
270 if ( m_xControl.is() )
272 m_xControl->createPeer( _rxToolkit, _rxParent );
273 impl_resetStateGuard_nothrow();
277 Reference<XWindowPeer> SAL_CALL OControl::getPeer()
279 return m_xControl.is() ? m_xControl->getPeer() : Reference<XWindowPeer>();
282 sal_Bool SAL_CALL OControl::setModel(const Reference<XControlModel>& Model)
284 if ( !m_xControl.is() )
285 return false;
287 bool bSuccess = m_xControl->setModel( Model );
288 impl_resetStateGuard_nothrow();
289 return bSuccess;
292 Reference<XControlModel> SAL_CALL OControl::getModel()
294 return m_xControl.is() ? m_xControl->getModel() : Reference<XControlModel>();
297 Reference<XView> SAL_CALL OControl::getView()
299 return m_xControl.is() ? m_xControl->getView() : Reference<XView>();
302 void SAL_CALL OControl::setDesignMode(sal_Bool bOn)
304 if (m_xControl.is())
305 m_xControl->setDesignMode(bOn);
308 sal_Bool SAL_CALL OControl::isDesignMode()
310 return !m_xControl.is() || m_xControl->isDesignMode();
313 sal_Bool SAL_CALL OControl::isTransparent()
315 return !m_xControl.is() || m_xControl->isTransparent();
318 OBoundControl::OBoundControl( const Reference< XComponentContext >& _rxContext,
319 const OUString& _rAggregateService, const bool _bSetDelegator )
320 :OControl( _rxContext, _rAggregateService, _bSetDelegator )
321 ,m_bLocked(false)
325 OBoundControl::~OBoundControl()
329 Sequence< Type> OBoundControl::_getTypes()
331 return TypeBag( OControl::_getTypes(), OBoundControl_BASE::getTypes() ).getTypes();
334 Any SAL_CALL OBoundControl::queryAggregation(const Type& _rType)
336 Any aReturn;
338 // XTypeProvider first - don't ask the OBoundControl_BASE, it would deliver incomplete types
339 if ( _rType.equals( cppu::UnoType<XTypeProvider>::get() ) )
340 aReturn = OControl::queryAggregation( _rType );
342 // ask our own interfaces
343 // (do this first (except XTypeProvider ) - we want to "overwrite" XPropertiesChangeListener)
344 if ( !aReturn.hasValue() )
345 aReturn = OBoundControl_BASE::queryInterface( _rType );
347 // ask the base class
348 if ( !aReturn.hasValue() )
349 aReturn = OControl::queryAggregation( _rType );
351 return aReturn;
354 sal_Bool SAL_CALL OBoundControl::getLock()
356 return m_bLocked;
359 void SAL_CALL OBoundControl::setLock(sal_Bool _bLock)
361 if (m_bLocked == bool(_bLock))
362 return;
364 osl::MutexGuard aGuard(m_aMutex);
365 _setLock(_bLock);
366 m_bLocked = _bLock;
369 void OBoundControl::_setLock(bool _bLock)
371 // try to set the text component to readonly
372 Reference< XWindowPeer > xPeer = getPeer();
373 Reference< XTextComponent > xText( xPeer, UNO_QUERY );
375 if ( xText.is() )
376 xText->setEditable( !_bLock );
377 else
379 // disable the window
380 Reference< XWindow > xComp( xPeer, UNO_QUERY );
381 if ( xComp.is() )
382 xComp->setEnable( !_bLock );
386 sal_Bool SAL_CALL OBoundControl::setModel( const Reference< XControlModel >& _rxModel )
388 return OControl::setModel( _rxModel );
391 void SAL_CALL OBoundControl::disposing(const EventObject& Source)
393 // just disambiguate
394 OControl::disposing(Source);
397 void OBoundControl::disposing()
399 OControl::disposing();
402 // OControlModel
403 Sequence<sal_Int8> SAL_CALL OControlModel::getImplementationId()
405 return css::uno::Sequence<sal_Int8>();
408 Sequence<Type> SAL_CALL OControlModel::getTypes()
410 TypeBag aTypes( _getTypes() );
412 if (auto xProv = query_aggregation<XTypeProvider>(m_xAggregate))
413 aTypes.addTypes( xProv->getTypes() );
415 return aTypes.getTypes();
418 Sequence<Type> OControlModel::_getTypes()
420 return TypeBag( OComponentHelper::getTypes(),
421 OPropertySetAggregationHelper::getTypes(),
422 OControlModel_BASE::getTypes()
423 ).getTypes();
426 Any SAL_CALL OControlModel::queryAggregation(const Type& _rType)
428 // base class 1
429 Any aReturn(OComponentHelper::queryAggregation(_rType));
431 // base class 2
432 if (!aReturn.hasValue())
434 aReturn = OControlModel_BASE::queryInterface(_rType);
436 // our own interfaces
437 if (!aReturn.hasValue())
439 aReturn = OPropertySetAggregationHelper::queryInterface(_rType);
440 // our aggregate
441 if (!aReturn.hasValue() && m_xAggregate.is() && !_rType.equals(cppu::UnoType<XCloneable>::get()))
442 aReturn = m_xAggregate->queryAggregation(_rType);
445 return aReturn;
448 void OControlModel::readHelpTextCompatibly(const css::uno::Reference< css::io::XObjectInputStream >& _rxInStream)
450 OUString sHelpText;
451 ::comphelper::operator>>( _rxInStream, sHelpText);
454 if (m_xAggregateSet.is())
455 m_xAggregateSet->setPropertyValue(PROPERTY_HELPTEXT, Any(sHelpText));
457 catch(const Exception&)
459 DBG_UNHANDLED_EXCEPTION("forms.component");
460 SAL_WARN("forms.component", "OControlModel::readHelpTextCompatibly: could not forward the property value to the aggregate!");
464 void OControlModel::writeHelpTextCompatibly(const css::uno::Reference< css::io::XObjectOutputStream >& _rxOutStream)
466 OUString sHelpText;
469 if (m_xAggregateSet.is())
470 m_xAggregateSet->getPropertyValue(PROPERTY_HELPTEXT) >>= sHelpText;
472 catch(const Exception&)
474 DBG_UNHANDLED_EXCEPTION("forms.component");
475 SAL_WARN("forms.component", "OControlModel::writeHelpTextCompatibly: could not retrieve the property value from the aggregate!");
477 ::comphelper::operator<<( _rxOutStream, sHelpText);
480 OControlModel::OControlModel(
481 const Reference<XComponentContext>& _rxContext,
482 const OUString& _rUnoControlModelTypeName,
483 const OUString& rDefault, const bool _bSetDelegator)
484 :OComponentHelper(m_aMutex)
485 ,OPropertySetAggregationHelper(OComponentHelper::rBHelper)
486 ,m_xContext( _rxContext )
487 ,m_lockCount( 0 )
488 ,m_aPropertyBagHelper( *this )
489 ,m_nTabIndex(FRM_DEFAULT_TABINDEX)
490 ,m_nClassId(FormComponentType::CONTROL)
491 ,m_bNativeLook( false )
492 ,m_bStandardTheme( false )
493 ,m_bGenerateVbEvents( false )
494 ,m_nControlTypeinMSO(0) // 0 : default value is create from AOO
495 ,m_nObjIDinMSO(INVALID_OBJ_ID_IN_MSO)
496 // form controls are usually embedded into documents, not dialogs, and in documents
497 // the native look is ugly...
498 // #i37342#
500 if (_rUnoControlModelTypeName.isEmpty()) // the is a model we have to aggregate
501 return;
503 osl_atomic_increment(&m_refCount);
505 m_xAggregate.set(m_xContext->getServiceManager()->createInstanceWithContext(_rUnoControlModelTypeName, m_xContext), UNO_QUERY);
506 setAggregation(m_xAggregate);
508 if ( m_xAggregateSet.is() )
512 if ( !rDefault.isEmpty() )
513 m_xAggregateSet->setPropertyValue( PROPERTY_DEFAULTCONTROL, Any( rDefault ) );
515 catch( const Exception& )
517 TOOLS_WARN_EXCEPTION("forms.component", "OControlModel::OControlModel");
521 if (_bSetDelegator)
522 doSetDelegator();
524 // Refcount is at NULL again
525 osl_atomic_decrement(&m_refCount);
528 OControlModel::OControlModel( const OControlModel* _pOriginal, const Reference< XComponentContext>& _rxFactory, const bool _bCloneAggregate, const bool _bSetDelegator )
529 :OComponentHelper( m_aMutex )
530 ,OPropertySetAggregationHelper( OComponentHelper::rBHelper )
531 ,m_xContext( _rxFactory )
532 ,m_lockCount( 0 )
533 ,m_aPropertyBagHelper( *this )
534 ,m_nTabIndex( FRM_DEFAULT_TABINDEX )
535 ,m_nClassId( FormComponentType::CONTROL )
537 assert(_pOriginal && "OControlModel::OControlModel: invalid original!");
539 // copy members
540 m_aName = _pOriginal->m_aName;
541 m_aTag = _pOriginal->m_aTag;
542 m_nTabIndex = _pOriginal->m_nTabIndex;
543 m_nClassId = _pOriginal->m_nClassId;
544 m_bNativeLook = _pOriginal->m_bNativeLook;
545 m_bStandardTheme = _pOriginal->m_bStandardTheme;
546 m_bGenerateVbEvents = _pOriginal->m_bGenerateVbEvents;
547 m_nControlTypeinMSO = _pOriginal->m_nControlTypeinMSO;
548 m_nObjIDinMSO = _pOriginal->m_nObjIDinMSO;
550 if ( !_bCloneAggregate )
551 return;
553 // temporarily increment refcount because of temporary references to ourself in the following
554 osl_atomic_increment( &m_refCount );
556 // transfer the (only, at the very moment!) ref count
557 m_xAggregate = createAggregateClone( _pOriginal );
559 // set aggregation (retrieve other direct interfaces of the aggregate)
560 setAggregation( m_xAggregate );
563 // set the delegator, if allowed by our derived class
564 if ( _bSetDelegator )
565 doSetDelegator();
567 // decrement ref count
568 osl_atomic_decrement( &m_refCount );
571 OControlModel::~OControlModel()
573 // release the aggregate
574 doResetDelegator( );
577 void OControlModel::clonedFrom( const OControlModel* /*_pOriginal*/ )
579 // nothing to do in this base class
582 void OControlModel::doResetDelegator()
584 if (m_xAggregate.is())
585 m_xAggregate->setDelegator(nullptr);
588 void OControlModel::doSetDelegator()
590 osl_atomic_increment(&m_refCount);
591 if (m_xAggregate.is())
593 m_xAggregate->setDelegator(static_cast<XWeak*>(this));
595 osl_atomic_decrement(&m_refCount);
598 // XChild
599 Reference< XInterface > SAL_CALL OControlModel::getParent()
601 return m_xParent;
604 void SAL_CALL OControlModel::setParent(const Reference< XInterface >& _rxParent)
606 osl::MutexGuard aGuard(m_aMutex);
608 Reference<XComponent> xComp(m_xParent, UNO_QUERY);
609 if (xComp.is())
610 xComp->removeEventListener(static_cast<XPropertiesChangeListener*>(this));
612 m_xParent = _rxParent;
613 xComp.set(m_xParent, css::uno::UNO_QUERY);
615 if ( xComp.is() )
616 xComp->addEventListener(static_cast<XPropertiesChangeListener*>(this));
619 // XNamed
620 OUString SAL_CALL OControlModel::getName()
622 OUString aReturn;
625 OPropertySetHelper::getFastPropertyValue(PROPERTY_ID_NAME) >>= aReturn;
627 catch (const css::beans::UnknownPropertyException&)
629 css::uno::Any a(cppu::getCaughtException());
630 throw WrappedTargetRuntimeException(
631 u"OControlModel::getName"_ustr,
632 *this,
636 return aReturn;
639 void SAL_CALL OControlModel::setName(const OUString& _rName)
643 setFastPropertyValue(PROPERTY_ID_NAME, Any(_rName));
645 catch (const css::beans::UnknownPropertyException&)
647 css::uno::Any a(cppu::getCaughtException());
648 throw WrappedTargetRuntimeException(
649 u"OControlModel::setName"_ustr,
650 *this,
656 // XServiceInfo
657 sal_Bool SAL_CALL OControlModel::supportsService(const OUString& _rServiceName)
659 return cppu::supportsService(this, _rServiceName);
662 Sequence< OUString > OControlModel::getAggregateServiceNames() const
664 Sequence< OUString > aAggServices;
665 if (auto xInfo = query_aggregation<XServiceInfo>(m_xAggregate))
666 aAggServices = xInfo->getSupportedServiceNames();
667 return aAggServices;
670 Sequence<OUString> SAL_CALL OControlModel::getSupportedServiceNames()
672 return ::comphelper::concatSequences(
673 getAggregateServiceNames(),
674 getSupportedServiceNames_Static()
678 Sequence< OUString > OControlModel::getSupportedServiceNames_Static()
680 return { FRM_SUN_FORMCOMPONENT, u"com.sun.star.form.FormControlModel"_ustr };
683 // XEventListener
684 void SAL_CALL OControlModel::disposing(const css::lang::EventObject& _rSource)
686 // release the parent
687 if (_rSource.Source == m_xParent)
689 osl::MutexGuard aGuard(m_aMutex);
690 m_xParent = nullptr;
692 else
694 if (auto xEvtLst = query_aggregation<css::lang::XEventListener>(m_xAggregate))
696 osl::MutexGuard aGuard(m_aMutex);
697 xEvtLst->disposing(_rSource);
702 // OComponentHelper
703 void OControlModel::disposing()
705 OPropertySetAggregationHelper::disposing();
707 if (auto xComp = query_aggregation<css::lang::XComponent>(m_xAggregate))
708 xComp->dispose();
710 setParent(Reference<XFormComponent>());
712 m_aPropertyBagHelper.dispose();
715 void OControlModel::writeAggregate( const Reference< XObjectOutputStream >& _rxOutStream ) const
717 if (auto xPersist = query_aggregation<XPersistObject>(m_xAggregate))
718 xPersist->write( _rxOutStream );
721 void OControlModel::readAggregate( const Reference< XObjectInputStream >& _rxInStream )
723 if (auto xPersist = query_aggregation<XPersistObject>(m_xAggregate))
724 xPersist->read( _rxInStream );
727 void SAL_CALL OControlModel::write(const Reference<css::io::XObjectOutputStream>& _rxOutStream)
729 osl::MutexGuard aGuard(m_aMutex);
731 // 1. writing the UnoControls
732 Reference<css::io::XMarkableStream> xMark(_rxOutStream, UNO_QUERY);
733 if ( !xMark.is() )
735 throw IOException(
736 ResourceManager::loadString(RID_STR_INVALIDSTREAM),
737 static_cast< ::cppu::OWeakObject* >( this )
741 sal_Int32 nMark = xMark->createMark();
742 sal_Int32 nLen = 0;
744 _rxOutStream->writeLong(nLen);
746 writeAggregate( _rxOutStream );
748 // determining the length
749 nLen = xMark->offsetToMark(nMark) - 4;
750 xMark->jumpToMark(nMark);
751 _rxOutStream->writeLong(nLen);
752 xMark->jumpToFurthest();
753 xMark->deleteMark(nMark);
755 // 2. writing a version number
756 _rxOutStream->writeShort(0x0003);
758 // 3. writing the general properties
759 ::comphelper::operator<<( _rxOutStream, m_aName);
760 _rxOutStream->writeShort(m_nTabIndex);
761 ::comphelper::operator<<( _rxOutStream, m_aTag); // 3rd version
763 // IMPORTANT NOTE!
764 // don't write any new members here: this wouldn't be compatible with older versions, as OControlModel
765 // is a base class which is called in derived classes "read" method. So if you increment the version
766 // and write new stuff, older office versions will read this in the _derived_ classes, which may result
767 // in anything from data loss to crash.
768 // EOIN!
771 void OControlModel::read(const Reference<css::io::XObjectInputStream>& InStream)
773 osl::MutexGuard aGuard(m_aMutex);
775 Reference<css::io::XMarkableStream> xMark(InStream, UNO_QUERY);
776 if ( !xMark.is() )
778 throw IOException(
779 ResourceManager::loadString(RID_STR_INVALIDSTREAM),
780 static_cast< ::cppu::OWeakObject* >( this )
784 // 1. reading the UnoControls
785 sal_Int32 nLen = InStream->readLong();
786 if (nLen)
788 sal_Int32 nMark = xMark->createMark();
792 readAggregate( InStream );
795 catch( const Exception& )
797 DBG_UNHANDLED_EXCEPTION("forms.component");
800 xMark->jumpToMark(nMark);
801 InStream->skipBytes(nLen);
802 xMark->deleteMark(nMark);
805 // 2. reading the version number
806 sal_uInt16 nVersion = InStream->readShort();
808 // 3. reading the general properties
809 ::comphelper::operator>>( InStream, m_aName);
810 m_nTabIndex = InStream->readShort();
812 if (nVersion > 0x0002)
813 ::comphelper::operator>>( InStream, m_aTag);
815 // we had a version where we wrote the help text
816 if (nVersion == 0x0004)
817 readHelpTextCompatibly(InStream);
819 DBG_ASSERT(nVersion < 5, "OControlModel::read : suspicious version number !");
820 // 4 was the version where we wrote the help text
821 // later versions shouldn't exist (see write for a detailed comment)
824 PropertyState OControlModel::getPropertyStateByHandle( sal_Int32 _nHandle )
826 // simply compare the current and the default value
827 Any aCurrentValue = getPropertyDefaultByHandle( _nHandle );
828 Any aDefaultValue; getFastPropertyValue( aDefaultValue, _nHandle );
830 bool bEqual = aCurrentValue == aDefaultValue;
831 return bEqual ? PropertyState_DEFAULT_VALUE : PropertyState_DIRECT_VALUE;
834 void OControlModel::setPropertyToDefaultByHandle( sal_Int32 _nHandle)
836 Any aDefault = getPropertyDefaultByHandle( _nHandle );
838 Any aConvertedValue, aOldValue;
839 if ( convertFastPropertyValue( aConvertedValue, aOldValue, _nHandle, aDefault ) )
841 setFastPropertyValue_NoBroadcast( _nHandle, aConvertedValue );
842 // TODO: fire the property change
846 Any OControlModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
848 Any aReturn;
849 switch ( _nHandle )
851 case PROPERTY_ID_NAME:
852 case PROPERTY_ID_TAG:
853 aReturn <<= OUString();
854 break;
855 case PROPERTY_ID_CLASSID:
856 aReturn <<= sal_Int16(FormComponentType::CONTROL);
857 break;
858 case PROPERTY_ID_TABINDEX:
859 aReturn <<= sal_Int16(FRM_DEFAULT_TABINDEX);
860 break;
861 case PROPERTY_ID_NATIVE_LOOK:
862 aReturn <<= true;
863 break;
864 case PROPERTY_ID_STANDARD_THEME:
865 case PROPERTY_ID_GENERATEVBAEVENTS:
866 aReturn <<= false;
867 break;
868 // added for exporting OCX control
869 case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
870 aReturn <<= sal_Int16(0);
871 break;
872 case PROPERTY_ID_OBJ_ID_IN_MSO:
873 aReturn <<= sal_uInt16(INVALID_OBJ_ID_IN_MSO);
874 break;
875 default:
876 if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
877 m_aPropertyBagHelper.getDynamicPropertyDefaultByHandle( _nHandle, aReturn );
878 else
879 SAL_WARN("forms.component", "OControlModel::convertFastPropertyValue: unknown handle " << _nHandle);
881 return aReturn;
884 void OControlModel::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
886 switch ( _nHandle )
888 case PROPERTY_ID_NAME:
889 _rValue <<= m_aName;
890 break;
891 case PROPERTY_ID_TAG:
892 _rValue <<= m_aTag;
893 break;
894 case PROPERTY_ID_CLASSID:
895 _rValue <<= m_nClassId;
896 break;
897 case PROPERTY_ID_TABINDEX:
898 _rValue <<= m_nTabIndex;
899 break;
900 case PROPERTY_ID_NATIVE_LOOK:
901 _rValue <<= m_bNativeLook;
902 break;
903 case PROPERTY_ID_STANDARD_THEME:
904 _rValue <<= m_bStandardTheme;
905 break;
906 case PROPERTY_ID_GENERATEVBAEVENTS:
907 _rValue <<= m_bGenerateVbEvents;
908 break;
909 // added for exporting OCX control
910 case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
911 _rValue <<= m_nControlTypeinMSO;
912 break;
913 case PROPERTY_ID_OBJ_ID_IN_MSO:
914 _rValue <<= m_nObjIDinMSO;
915 break;
916 default:
917 if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
918 m_aPropertyBagHelper.getDynamicFastPropertyValue( _nHandle, _rValue );
919 else
920 OPropertySetAggregationHelper::getFastPropertyValue( _rValue, _nHandle );
921 break;
925 sal_Bool OControlModel::convertFastPropertyValue(
926 Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue)
928 bool bModified(false);
929 switch (_nHandle)
931 case PROPERTY_ID_NAME:
932 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aName);
933 break;
934 case PROPERTY_ID_TAG:
935 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aTag);
936 break;
937 case PROPERTY_ID_TABINDEX:
938 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_nTabIndex);
939 break;
940 case PROPERTY_ID_NATIVE_LOOK:
941 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bNativeLook);
942 break;
943 case PROPERTY_ID_STANDARD_THEME:
944 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bStandardTheme);
945 break;
946 case PROPERTY_ID_GENERATEVBAEVENTS:
947 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bGenerateVbEvents);
948 break;
949 // added for exporting OCX control
950 case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
951 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_nControlTypeinMSO);
952 break;
953 case PROPERTY_ID_OBJ_ID_IN_MSO:
954 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_nObjIDinMSO);
955 break;
956 default:
957 if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
958 bModified = m_aPropertyBagHelper.convertDynamicFastPropertyValue( _nHandle, _rValue, _rConvertedValue, _rOldValue );
959 else
960 SAL_WARN("forms.component", "OControlModel::convertFastPropertyValue: unknown handle " << _nHandle);
961 break;
963 return bModified;
966 void OControlModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
968 switch (_nHandle)
970 case PROPERTY_ID_NAME:
971 DBG_ASSERT(_rValue.getValueType() == cppu::UnoType<OUString>::get(),
972 "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" );
973 _rValue >>= m_aName;
974 break;
975 case PROPERTY_ID_TAG:
976 DBG_ASSERT(_rValue.getValueType() == cppu::UnoType<OUString>::get(),
977 "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" );
978 _rValue >>= m_aTag;
979 break;
980 case PROPERTY_ID_TABINDEX:
981 DBG_ASSERT(_rValue.getValueType() == cppu::UnoType<sal_Int16>::get(),
982 "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" );
983 _rValue >>= m_nTabIndex;
984 break;
985 case PROPERTY_ID_NATIVE_LOOK:
986 OSL_VERIFY( _rValue >>= m_bNativeLook );
987 break;
988 case PROPERTY_ID_STANDARD_THEME:
989 OSL_VERIFY( _rValue >>= m_bStandardTheme );
990 break;
991 case PROPERTY_ID_GENERATEVBAEVENTS:
992 OSL_VERIFY( _rValue >>= m_bGenerateVbEvents );
993 break;
994 // added for exporting OCX control
995 case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
996 OSL_VERIFY( _rValue >>= m_nControlTypeinMSO );
997 break;
998 case PROPERTY_ID_OBJ_ID_IN_MSO:
999 OSL_VERIFY( _rValue >>= m_nObjIDinMSO );
1000 break;
1001 default:
1002 if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
1003 m_aPropertyBagHelper.setDynamicFastPropertyValue( _nHandle, _rValue );
1004 else
1005 SAL_WARN("forms.component", "OControlModel::setFastPropertyValue_NoBroadcast: unknown handle " << _nHandle );
1006 break;
1010 void OControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const
1012 _rProps.realloc(8);
1013 css::beans::Property* pProperties = _rProps.getArray();
1014 *pProperties++ = css::beans::Property(PROPERTY_CLASSID, PROPERTY_ID_CLASSID, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
1015 *pProperties++ = css::beans::Property(PROPERTY_NAME, PROPERTY_ID_NAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
1016 *pProperties++ = css::beans::Property(PROPERTY_NATIVE_LOOK, PROPERTY_ID_NATIVE_LOOK, cppu::UnoType<bool>::get(),
1017 css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT);
1018 *pProperties++ = css::beans::Property(PROPERTY_STANDARD_THEME, PROPERTY_ID_STANDARD_THEME, cppu::UnoType<bool>::get(),
1019 css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT);
1020 *pProperties++ = css::beans::Property(PROPERTY_TAG, PROPERTY_ID_TAG, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
1021 *pProperties++ = css::beans::Property(PROPERTY_GENERATEVBAEVENTS, PROPERTY_ID_GENERATEVBAEVENTS, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::TRANSIENT);
1022 *pProperties++ = css::beans::Property(PROPERTY_CONTROL_TYPE_IN_MSO, PROPERTY_ID_CONTROL_TYPE_IN_MSO, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
1023 *pProperties++ = css::beans::Property(PROPERTY_OBJ_ID_IN_MSO, PROPERTY_ID_OBJ_ID_IN_MSO, cppu::UnoType<cppu::UnoUnsignedShortType>::get(), css::beans::PropertyAttribute::BOUND);
1024 DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
1027 void OControlModel::describeAggregateProperties( Sequence< Property >& /* [out] */ _rAggregateProps ) const
1029 if ( m_xAggregateSet.is() )
1031 Reference< XPropertySetInfo > xPSI( m_xAggregateSet->getPropertySetInfo() );
1032 if ( xPSI.is() )
1033 _rAggregateProps = xPSI->getProperties();
1037 ::osl::Mutex& OControlModel::getMutex()
1039 return m_aMutex;
1042 void OControlModel::describeFixedAndAggregateProperties( Sequence< Property >& _out_rFixedProperties, Sequence< Property >& _out_rAggregateProperties ) const
1044 describeFixedProperties( _out_rFixedProperties );
1045 describeAggregateProperties( _out_rAggregateProperties );
1048 Reference< XMultiPropertySet > OControlModel::getPropertiesInterface()
1050 return this;
1053 Reference< XPropertySetInfo> SAL_CALL OControlModel::getPropertySetInfo()
1055 return createPropertySetInfo( getInfoHelper() );
1058 ::cppu::IPropertyArrayHelper& OControlModel::getInfoHelper()
1060 return m_aPropertyBagHelper.getInfoHelper();
1063 void SAL_CALL OControlModel::addProperty( const OUString& _rName, ::sal_Int16 _nAttributes, const Any& _rInitialValue )
1065 m_aPropertyBagHelper.addProperty( _rName, _nAttributes, _rInitialValue );
1068 void SAL_CALL OControlModel::removeProperty( const OUString& _rName )
1070 m_aPropertyBagHelper.removeProperty( _rName );
1073 Sequence< PropertyValue > SAL_CALL OControlModel::getPropertyValues()
1075 return m_aPropertyBagHelper.getPropertyValues();
1078 void SAL_CALL OControlModel::setPropertyValues( const Sequence< PropertyValue >& _rProps )
1080 m_aPropertyBagHelper.setPropertyValues( _rProps );
1083 void OControlModel::lockInstance( LockAccess )
1085 m_aMutex.acquire();
1086 osl_atomic_increment( &m_lockCount );
1089 oslInterlockedCount OControlModel::unlockInstance( LockAccess )
1091 OSL_ENSURE( m_lockCount > 0, "OControlModel::unlockInstance: not locked!" );
1092 oslInterlockedCount lockCount = osl_atomic_decrement( &m_lockCount );
1093 m_aMutex.release();
1094 return lockCount;
1097 void OControlModel::firePropertyChanges( const std::vector< sal_Int32 >& _rHandles, const std::vector< Any >& _rOldValues,
1098 const std::vector< Any >& _rNewValues, LockAccess )
1100 OPropertySetHelper::fire(
1101 const_cast< std::vector< sal_Int32 >& >( _rHandles ).data(),
1102 _rNewValues.data(),
1103 _rOldValues.data(),
1104 _rHandles.size(),
1105 false
1109 // OBoundControlModel
1110 Any SAL_CALL OBoundControlModel::queryAggregation( const Type& _rType )
1112 Any aReturn( OControlModel::queryAggregation(_rType) );
1113 if (!aReturn.hasValue())
1115 aReturn = OBoundControlModel_BASE1::queryInterface(_rType);
1117 if ( !aReturn.hasValue() && m_bCommitable )
1118 aReturn = OBoundControlModel_COMMITTING::queryInterface( _rType );
1120 if ( !aReturn.hasValue() && m_bSupportsExternalBinding )
1121 aReturn = OBoundControlModel_BINDING::queryInterface( _rType );
1123 if ( !aReturn.hasValue() && m_bSupportsValidation )
1124 aReturn = OBoundControlModel_VALIDATION::queryInterface( _rType );
1126 return aReturn;
1129 OBoundControlModel::OBoundControlModel(
1130 const Reference< XComponentContext>& _rxFactory,
1131 const OUString& _rUnoControlModelTypeName, const OUString& _rDefault,
1132 const bool _bCommitable, const bool _bSupportExternalBinding, const bool _bSupportsValidation )
1133 :OControlModel( _rxFactory, _rUnoControlModelTypeName, _rDefault, false )
1134 ,OPropertyChangeListener()
1135 ,m_nValuePropertyAggregateHandle( -1 )
1136 ,m_nFieldType( DataType::OTHER )
1137 ,m_bValuePropertyMayBeVoid( false )
1138 ,m_aResetHelper( *this, m_aMutex )
1139 ,m_aUpdateListeners(m_aMutex)
1140 ,m_aFormComponentListeners( m_aMutex )
1141 ,m_bInputRequired( false )
1142 ,m_bFormListening( false )
1143 ,m_bLoaded(false)
1144 ,m_bRequired(false)
1145 ,m_bCommitable(_bCommitable)
1146 ,m_bSupportsExternalBinding( _bSupportExternalBinding )
1147 ,m_bSupportsValidation( _bSupportsValidation )
1148 ,m_bForwardValueChanges(true)
1149 ,m_bTransferringValue( false )
1150 ,m_bIsCurrentValueValid( true )
1151 ,m_bBindingControlsRO( false )
1152 ,m_bBindingControlsEnable( false )
1153 ,m_eControlValueChangeInstigator( eOther )
1154 ,m_aLabelServiceName(FRM_SUN_COMPONENT_FIXEDTEXT)
1156 // start property listening at the aggregate
1157 implInitAggMultiplexer( );
1160 OBoundControlModel::OBoundControlModel(
1161 const OBoundControlModel* _pOriginal, const Reference< XComponentContext>& _rxFactory )
1162 :OControlModel( _pOriginal, _rxFactory, true, false )
1163 ,OPropertyChangeListener()
1164 ,m_nValuePropertyAggregateHandle( _pOriginal->m_nValuePropertyAggregateHandle )
1165 ,m_nFieldType( DataType::OTHER )
1166 ,m_bValuePropertyMayBeVoid( _pOriginal->m_bValuePropertyMayBeVoid )
1167 ,m_aResetHelper( *this, m_aMutex )
1168 ,m_aUpdateListeners( m_aMutex )
1169 ,m_aFormComponentListeners( m_aMutex )
1170 ,m_xValidator( _pOriginal->m_xValidator )
1171 ,m_bInputRequired( false )
1172 ,m_bFormListening( false )
1173 ,m_bLoaded( false )
1174 ,m_bRequired( false )
1175 ,m_bCommitable( _pOriginal->m_bCommitable )
1176 ,m_bSupportsExternalBinding( _pOriginal->m_bSupportsExternalBinding )
1177 ,m_bSupportsValidation( _pOriginal->m_bSupportsValidation )
1178 ,m_bForwardValueChanges( true )
1179 ,m_bTransferringValue( false )
1180 ,m_bIsCurrentValueValid( _pOriginal->m_bIsCurrentValueValid )
1181 ,m_bBindingControlsRO( false )
1182 ,m_bBindingControlsEnable( false )
1183 ,m_eControlValueChangeInstigator( eOther )
1185 // start property listening at the aggregate
1186 implInitAggMultiplexer( );
1187 m_aLabelServiceName = _pOriginal->m_aLabelServiceName;
1188 m_sValuePropertyName = _pOriginal->m_sValuePropertyName;
1189 m_nValuePropertyAggregateHandle = _pOriginal->m_nValuePropertyAggregateHandle;
1190 m_bValuePropertyMayBeVoid = _pOriginal->m_bValuePropertyMayBeVoid;
1191 m_aValuePropertyType = _pOriginal->m_aValuePropertyType;
1192 m_aControlSource = _pOriginal->m_aControlSource;
1193 m_bInputRequired = _pOriginal->m_bInputRequired;
1194 // m_xLabelControl, though being a property, is not to be cloned, not even the reference will be transferred.
1195 // (the former should be clear - a clone of the object we're only referencing does not make sense)
1196 // (the second would violate the restriction for label controls that they're part of the
1197 // same form component hierarchy - we ourself are no part, yet, so we can't have a label control)
1198 // start listening for changes at the value property
1199 implInitValuePropertyListening( );
1202 OBoundControlModel::~OBoundControlModel()
1204 if ( !OComponentHelper::rBHelper.bDisposed )
1206 acquire();
1207 dispose();
1210 doResetDelegator( );
1211 OSL_ENSURE( m_pAggPropMultiplexer, "OBoundControlModel::~OBoundControlModel: what about my property multiplexer?" );
1212 if ( m_pAggPropMultiplexer )
1214 m_pAggPropMultiplexer->dispose();
1215 m_pAggPropMultiplexer = nullptr;
1219 void OBoundControlModel::clonedFrom( const OControlModel* _pOriginal )
1221 const OBoundControlModel* pBoundOriginal = static_cast< const OBoundControlModel* >( _pOriginal );
1222 // the value binding can be handled as if somebody called setValueBinding here
1223 // By definition, bindings can be share between bindables
1224 if ( !(pBoundOriginal && pBoundOriginal->m_xExternalBinding.is()) )
1225 return;
1229 setValueBinding( pBoundOriginal->m_xExternalBinding );
1232 catch( const Exception& )
1234 DBG_UNHANDLED_EXCEPTION("forms.component");
1238 void OBoundControlModel::implInitAggMultiplexer( )
1240 osl_atomic_increment( &m_refCount );
1241 if ( m_xAggregateSet.is() )
1243 m_pAggPropMultiplexer = new OPropertyChangeMultiplexer( this, m_xAggregateSet, false );
1246 osl_atomic_decrement( &m_refCount );
1247 doSetDelegator();
1250 void OBoundControlModel::implInitValuePropertyListening( ) const
1252 // start listening for changes at the value property
1253 // There are three pre-requisites for this to be done:
1254 // 1. We support external value bindings. In this case, the changes in the control value need to
1255 // be propagated to the external binding immediately when they happen
1256 // 2. We support external validation. In this case, we need to listen for changes in the value
1257 // property, since we need to revalidate then.
1258 // 3. We are not committable. In this case, changes in the control value need to be propagated
1259 // to the database column immediately when they happen.
1260 if ( m_bSupportsExternalBinding || m_bSupportsValidation || !m_bCommitable )
1262 OSL_ENSURE( m_pAggPropMultiplexer, "OBoundControlModel::implInitValuePropertyListening: no multiplexer!" );
1263 if ( m_pAggPropMultiplexer && !m_sValuePropertyName.isEmpty() )
1264 m_pAggPropMultiplexer->addProperty( m_sValuePropertyName );
1268 void OBoundControlModel::initOwnValueProperty( const OUString& i_rValuePropertyName )
1270 OSL_PRECOND( m_sValuePropertyName.isEmpty() && -1 == m_nValuePropertyAggregateHandle,
1271 "OBoundControlModel::initOwnValueProperty: value property is already initialized!" );
1272 OSL_ENSURE( !i_rValuePropertyName.isEmpty(), "OBoundControlModel::initOwnValueProperty: invalid property name!" );
1273 m_sValuePropertyName = i_rValuePropertyName;
1276 void OBoundControlModel::initValueProperty( const OUString& _rValuePropertyName, sal_Int32 _nValuePropertyExternalHandle )
1278 OSL_PRECOND( m_sValuePropertyName.isEmpty() && -1 == m_nValuePropertyAggregateHandle,
1279 "OBoundControlModel::initValueProperty: value property is already initialized!" );
1280 OSL_ENSURE( !_rValuePropertyName.isEmpty(), "OBoundControlModel::initValueProperty: invalid property name!" );
1281 OSL_ENSURE( _nValuePropertyExternalHandle != -1, "OBoundControlModel::initValueProperty: invalid property handle!" );
1283 m_sValuePropertyName = _rValuePropertyName;
1284 m_nValuePropertyAggregateHandle = getOriginalHandle( _nValuePropertyExternalHandle );
1285 OSL_ENSURE( m_nValuePropertyAggregateHandle != -1, "OBoundControlModel::initValueProperty: unable to find the original handle!" );
1287 if ( m_nValuePropertyAggregateHandle != -1 )
1289 Reference< XPropertySetInfo > xPropInfo( m_xAggregateSet->getPropertySetInfo(), UNO_SET_THROW );
1290 Property aValuePropDesc = xPropInfo->getPropertyByName( m_sValuePropertyName );
1291 m_aValuePropertyType = aValuePropDesc.Type;
1292 m_bValuePropertyMayBeVoid = ( aValuePropDesc.Attributes & PropertyAttribute::MAYBEVOID ) != 0;
1295 // start listening for changes at the value property
1296 implInitValuePropertyListening( );
1299 void OBoundControlModel::suspendValueListening( )
1301 OSL_PRECOND( !m_sValuePropertyName.isEmpty(), "OBoundControlModel::suspendValueListening: don't have a value property!" );
1302 OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::suspendValueListening: I *am* not listening!" );
1304 if ( m_pAggPropMultiplexer )
1305 m_pAggPropMultiplexer->lock();
1308 void OBoundControlModel::resumeValueListening( )
1310 OSL_PRECOND( !m_sValuePropertyName.isEmpty(), "OBoundControlModel::resumeValueListening: don't have a value property!" );
1311 OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::resumeValueListening: I *am* not listening at all!" );
1312 OSL_PRECOND( !m_pAggPropMultiplexer || m_pAggPropMultiplexer->locked(), "OBoundControlModel::resumeValueListening: listening not suspended currently!" );
1313 if ( m_pAggPropMultiplexer )
1314 m_pAggPropMultiplexer->unlock();
1317 Sequence< Type > OBoundControlModel::_getTypes()
1319 TypeBag aTypes(
1320 OControlModel::_getTypes(),
1321 OBoundControlModel_BASE1::getTypes()
1324 if ( m_bCommitable )
1325 aTypes.addTypes( OBoundControlModel_COMMITTING::getTypes() );
1327 if ( m_bSupportsExternalBinding )
1328 aTypes.addTypes( OBoundControlModel_BINDING::getTypes() );
1330 if ( m_bSupportsValidation )
1331 aTypes.addTypes( OBoundControlModel_VALIDATION::getTypes() );
1332 return aTypes.getTypes();
1335 // OComponentHelper
1336 void OBoundControlModel::disposing()
1338 OControlModel::disposing();
1340 osl::MutexGuard aGuard(m_aMutex);
1342 if ( m_pAggPropMultiplexer )
1343 m_pAggPropMultiplexer->dispose();
1345 // notify all our listeners
1346 css::lang::EventObject aEvt( static_cast< XWeak* >( this ) );
1347 m_aUpdateListeners.disposeAndClear( aEvt );
1348 m_aResetHelper.disposing();
1350 // disconnect from our database column
1351 // TODO: could we replace the following 5 lines with a call to impl_disconnectDatabaseColumn_noNotify?
1352 // The only more thing which it does is calling onDisconnectedDbColumn - could this
1353 // cause trouble? At least when we continue to call OControlModel::disposing before, it *may*.
1354 if ( hasField() )
1356 getField()->removePropertyChangeListener( PROPERTY_VALUE, this );
1357 resetField();
1360 m_xCursor = nullptr;
1361 Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY );
1362 if ( xComp.is() )
1363 xComp->removeEventListener(static_cast< XEventListener* >( static_cast< XPropertyChangeListener* >( this ) ) );
1364 // disconnect from our external value binding
1365 if ( hasExternalValueBinding() )
1366 disconnectExternalValueBinding();
1367 // ditto for the validator
1368 if ( hasValidator() )
1369 disconnectValidator( );
1372 void OBoundControlModel::onValuePropertyChange( ControlModelLock& i_rControLock )
1374 if ( hasExternalValueBinding() )
1376 // the control value changed, while we have an external value binding
1377 // -> forward the value to it
1378 if ( m_eControlValueChangeInstigator != eExternalBinding )
1379 transferControlValueToExternal( i_rControLock );
1382 else if ( !m_bCommitable && m_xColumnUpdate.is() )
1384 // the control value changed, while we are bound to a database column,
1385 // but not committable (which means changes in the control have to be reflected to
1386 // the underlying database column immediately)
1387 // -> forward the value to the database column
1388 if ( m_eControlValueChangeInstigator != eDbColumnBinding )
1389 commitControlValueToDbColumn( false );
1392 // validate the new value
1393 if ( m_bSupportsValidation )
1394 recheckValidity( true );
1397 void OBoundControlModel::_propertyChanged( const PropertyChangeEvent& _rEvt )
1399 ControlModelLock aLock( *this );
1400 OSL_ENSURE( _rEvt.PropertyName == m_sValuePropertyName,
1401 "OBoundControlModel::_propertyChanged: where did this come from (1)?" );
1402 OSL_ENSURE( m_pAggPropMultiplexer && !m_pAggPropMultiplexer->locked(),
1403 "OBoundControlModel::_propertyChanged: where did this come from (2)?" );
1404 if ( _rEvt.PropertyName == m_sValuePropertyName )
1406 onValuePropertyChange( aLock );
1410 void OBoundControlModel::startAggregatePropertyListening( const OUString& _rPropertyName )
1412 OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::startAggregatePropertyListening: no multiplexer!" );
1413 OSL_ENSURE( !_rPropertyName.isEmpty(), "OBoundControlModel::startAggregatePropertyListening: invalid property name!" );
1414 if ( m_pAggPropMultiplexer && !_rPropertyName.isEmpty() )
1416 m_pAggPropMultiplexer->addProperty( _rPropertyName );
1420 void OBoundControlModel::doFormListening( const bool _bStart )
1422 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::doFormListening: external value binding should overrule the database binding!" );
1423 if ( isFormListening() == _bStart )
1424 return;
1425 if ( m_xAmbientForm.is() )
1426 _bStart ? m_xAmbientForm->addLoadListener( this ) : m_xAmbientForm->removeLoadListener( this );
1427 Reference< XLoadable > xParentLoadable( getParent(), UNO_QUERY );
1428 if ( getParent().is() && !xParentLoadable.is() )
1430 // if our parent does not directly support the XLoadable interface, then it might support the
1431 // XRowSetSupplier/XRowSetChangeBroadcaster interfaces. In this case we have to listen for changes
1432 // broadcasted by the latter.
1433 Reference< XRowSetChangeBroadcaster > xRowSetBroadcaster( getParent(), UNO_QUERY );
1434 if ( xRowSetBroadcaster.is() )
1435 _bStart ? xRowSetBroadcaster->addRowSetChangeListener( this ) : xRowSetBroadcaster->removeRowSetChangeListener( this );
1438 m_bFormListening = _bStart && m_xAmbientForm.is();
1441 // XChild
1442 void SAL_CALL OBoundControlModel::setParent(const Reference<XInterface>& _rxParent)
1444 ControlModelLock aLock( *this );
1445 FieldChangeNotifier aBoundFieldNotifier( aLock );
1446 if ( getParent() == _rxParent )
1447 return;
1448 // disconnect from database column (which is controlled by parent, directly or indirectly)
1449 if ( hasField() )
1450 impl_disconnectDatabaseColumn_noNotify();
1451 // log off old listeners
1452 if ( isFormListening() )
1453 doFormListening( false );
1454 // actually set the new parent
1455 OControlModel::setParent( _rxParent );
1456 // a new parent means a new ambient form
1457 impl_determineAmbientForm_nothrow();
1458 if ( !hasExternalValueBinding() )
1460 // log on new listeners
1461 doFormListening( true );
1462 // re-connect to database column of the new parent
1463 if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() )
1464 impl_connectDatabaseColumn_noNotify( false );
1468 // XEventListener
1469 void SAL_CALL OBoundControlModel::disposing(const css::lang::EventObject& _rEvent)
1471 ControlModelLock aLock( *this );
1472 if ( _rEvent.Source == getField() )
1474 resetField();
1477 else if ( _rEvent.Source == m_xLabelControl )
1479 Reference<XPropertySet> xOldValue = m_xLabelControl;
1480 m_xLabelControl = nullptr;
1481 // fire a propertyChanged (when we leave aLock's scope)
1482 aLock.addPropertyNotification( PROPERTY_ID_CONTROLLABEL, Any( xOldValue ), Any( m_xLabelControl ) );
1485 else if ( _rEvent.Source == m_xExternalBinding )
1486 { // *first* check for the external binding
1487 disconnectExternalValueBinding( );
1490 else if ( _rEvent.Source == m_xValidator )
1491 { // *then* check for the validator. Reason is that bindings may also act as validator at the same
1492 // time, in this case, the validator is automatically revoked when the binding is revoked
1493 disconnectValidator( );
1496 else
1497 OControlModel::disposing(_rEvent);
1500 // XServiceInfo
1501 css::uno::Sequence<OUString> SAL_CALL OBoundControlModel::getSupportedServiceNames()
1503 return ::comphelper::combineSequences(
1504 getAggregateServiceNames(),
1505 getSupportedServiceNames_Static()
1509 Sequence< OUString > OBoundControlModel::getSupportedServiceNames_Static()
1511 static constexpr OUString aOwnServiceNames[] { u"com.sun.star.form.DataAwareControlModel"_ustr };
1512 return ::comphelper::concatSequences(
1513 OControlModel::getSupportedServiceNames_Static(),
1514 aOwnServiceNames
1518 // XPersist
1519 void SAL_CALL OBoundControlModel::write( const Reference<css::io::XObjectOutputStream>& _rxOutStream )
1521 OControlModel::write(_rxOutStream);
1522 osl::MutexGuard aGuard(m_aMutex);
1523 // Version
1524 _rxOutStream->writeShort(0x0002);
1525 // Controlsource
1526 ::comphelper::operator<<( _rxOutStream, m_aControlSource);
1527 // !!! IMPORTANT NOTE !!!
1528 // don't write any new members here: this wouldn't be compatible with older versions, as OBoundControlModel
1529 // is a base class which is called in derived classes "read" method. So if you increment the version
1530 // and write new stuff, older office versions will read this in the _derived_ classes, which may result
1531 // in anything from data loss to crash.
1532 // (use writeCommonProperties instead, this is called in derived classes write-method)
1533 // !!! EOIN !!!
1536 void OBoundControlModel::defaultCommonProperties()
1538 Reference<css::lang::XComponent> xComp(m_xLabelControl, UNO_QUERY);
1539 if (xComp.is())
1540 xComp->removeEventListener(static_cast<css::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
1541 m_xLabelControl = nullptr;
1544 void OBoundControlModel::readCommonProperties(const Reference<css::io::XObjectInputStream>& _rxInStream)
1546 sal_Int32 nLen = _rxInStream->readLong();
1547 Reference<css::io::XMarkableStream> xMark(_rxInStream, UNO_QUERY);
1548 DBG_ASSERT(xMark.is(), "OBoundControlModel::readCommonProperties : can only work with markable streams !");
1549 sal_Int32 nMark = xMark->createMark();
1550 // read the reference to the label control
1551 Reference<css::io::XPersistObject> xPersist;
1552 sal_Int32 nUsedFlag;
1553 nUsedFlag = _rxInStream->readLong();
1554 if (nUsedFlag)
1555 xPersist = _rxInStream->readObject();
1556 m_xLabelControl.set(xPersist, css::uno::UNO_QUERY);
1557 Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY );
1558 if (xComp.is())
1559 xComp->addEventListener(static_cast<css::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
1560 // read any other new common properties here
1561 // skip the remaining bytes
1562 xMark->jumpToMark(nMark);
1563 _rxInStream->skipBytes(nLen);
1564 xMark->deleteMark(nMark);
1567 void OBoundControlModel::writeCommonProperties(const Reference<css::io::XObjectOutputStream>& _rxOutStream)
1569 Reference<css::io::XMarkableStream> xMark(_rxOutStream, UNO_QUERY);
1570 DBG_ASSERT(xMark.is(), "OBoundControlModel::writeCommonProperties : can only work with markable streams !");
1571 sal_Int32 nMark = xMark->createMark();
1572 // a placeholder where we will write the overall length (later in this method)
1573 sal_Int32 nLen = 0;
1574 _rxOutStream->writeLong(nLen);
1575 // write the reference to the label control
1576 Reference<css::io::XPersistObject> xPersist(m_xLabelControl, UNO_QUERY);
1577 sal_Int32 nUsedFlag = 0;
1578 if (xPersist.is())
1579 nUsedFlag = 1;
1580 _rxOutStream->writeLong(nUsedFlag);
1581 if (xPersist.is())
1582 _rxOutStream->writeObject(xPersist);
1583 // write any other new common properties here
1584 // write the correct length at the beginning of the block
1585 nLen = xMark->offsetToMark(nMark) - sizeof(nLen);
1586 xMark->jumpToMark(nMark);
1587 _rxOutStream->writeLong(nLen);
1588 xMark->jumpToFurthest();
1589 xMark->deleteMark(nMark);
1592 void SAL_CALL OBoundControlModel::read( const Reference< css::io::XObjectInputStream >& _rxInStream )
1594 OControlModel::read(_rxInStream);
1595 osl::MutexGuard aGuard(m_aMutex);
1596 _rxInStream->readShort(); // version;
1597 ::comphelper::operator>>( _rxInStream, m_aControlSource);
1600 void OBoundControlModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const
1602 switch (nHandle)
1604 case PROPERTY_ID_INPUT_REQUIRED:
1605 rValue <<= m_bInputRequired;
1606 break;
1607 case PROPERTY_ID_CONTROLSOURCEPROPERTY:
1608 rValue <<= m_sValuePropertyName;
1609 break;
1610 case PROPERTY_ID_CONTROLSOURCE:
1611 rValue <<= m_aControlSource;
1612 break;
1613 case PROPERTY_ID_BOUNDFIELD:
1614 rValue <<= getField();
1615 break;
1616 case PROPERTY_ID_CONTROLLABEL:
1617 if (!m_xLabelControl.is())
1618 rValue.clear();
1619 else
1620 rValue <<= m_xLabelControl;
1621 break;
1622 default:
1623 OControlModel::getFastPropertyValue(rValue, nHandle);
1627 sal_Bool OBoundControlModel::convertFastPropertyValue(
1628 Any& _rConvertedValue, Any& _rOldValue,
1629 sal_Int32 _nHandle,
1630 const Any& _rValue)
1632 bool bModified(false);
1633 switch (_nHandle)
1635 case PROPERTY_ID_INPUT_REQUIRED:
1636 bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_bInputRequired );
1637 break;
1638 case PROPERTY_ID_CONTROLSOURCE:
1639 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aControlSource);
1640 break;
1641 case PROPERTY_ID_BOUNDFIELD:
1642 SAL_WARN("forms.component", "OBoundControlModel::convertFastPropertyValue: BoundField should be a read-only property !" );
1643 throw css::lang::IllegalArgumentException();
1644 case PROPERTY_ID_CONTROLLABEL:
1645 if (!_rValue.hasValue())
1646 { // property set to void
1647 _rConvertedValue = Any();
1648 getFastPropertyValue(_rOldValue, _nHandle);
1649 bModified = m_xLabelControl.is();
1652 else
1654 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_xLabelControl);
1655 if (!m_xLabelControl.is())
1656 // an empty interface is interpreted as VOID
1657 _rOldValue.clear();
1660 break;
1661 default:
1662 bModified = OControlModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue);
1664 return bModified;
1667 Any OBoundControlModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
1669 Any aDefault;
1670 switch ( _nHandle )
1672 case PROPERTY_ID_INPUT_REQUIRED:
1673 aDefault <<= false;
1674 break;
1675 case PROPERTY_ID_CONTROLSOURCE:
1676 aDefault <<= OUString();
1677 break;
1678 case PROPERTY_ID_CONTROLLABEL:
1679 aDefault <<= Reference< XPropertySet >();
1680 break;
1682 return aDefault;
1685 void OBoundControlModel::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
1687 switch (nHandle)
1689 case PROPERTY_ID_INPUT_REQUIRED:
1690 OSL_VERIFY( rValue >>= m_bInputRequired );
1691 break;
1692 case PROPERTY_ID_CONTROLSOURCE:
1693 OSL_VERIFY( rValue >>= m_aControlSource );
1694 break;
1695 case PROPERTY_ID_BOUNDFIELD:
1696 SAL_WARN("forms.component", "OBoundControlModel::setFastPropertyValue_NoBroadcast : BoundField should be a read-only property !");
1697 throw css::lang::IllegalArgumentException();
1698 case PROPERTY_ID_CONTROLLABEL:
1700 if ( rValue.hasValue() && ( rValue.getValueTypeClass() != TypeClass_INTERFACE ) )
1701 throw css::lang::IllegalArgumentException();
1702 Reference< XInterface > xNewValue( rValue, UNO_QUERY );
1703 if ( !xNewValue.is() )
1704 { // set property to "void"
1705 Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY );
1706 if ( xComp.is() )
1707 xComp->removeEventListener( static_cast< XPropertyChangeListener* >( this ) );
1708 m_xLabelControl = nullptr;
1709 break;
1712 Reference< XControlModel > xAsModel ( xNewValue, UNO_QUERY );
1713 Reference< XServiceInfo > xAsServiceInfo ( xAsModel, UNO_QUERY );
1714 Reference< XPropertySet > xAsPropSet ( xAsServiceInfo, UNO_QUERY );
1715 Reference< XChild > xAsChild ( xAsPropSet, UNO_QUERY );
1716 if ( !xAsChild.is() || !xAsServiceInfo->supportsService( m_aLabelServiceName ) )
1718 throw css::lang::IllegalArgumentException();
1721 // Check if we and the given model have a common ancestor (up to the forms collection)
1722 Reference<XChild> xCont(this);
1723 Reference< XInterface > xMyTopLevel = xCont->getParent();
1724 while (xMyTopLevel.is())
1726 Reference<XForm> xAsForm(xMyTopLevel, UNO_QUERY);
1727 if (!xAsForm.is())
1728 // found my root
1729 break;
1730 Reference<XChild> xLoopAsChild(xMyTopLevel, UNO_QUERY);
1731 xMyTopLevel = xLoopAsChild.is() ? xLoopAsChild->getParent() : Reference< XInterface >();
1734 Reference< XInterface > xNewTopLevel = xAsChild->getParent();
1735 while (xNewTopLevel.is())
1737 Reference<XForm> xAsForm(xNewTopLevel, UNO_QUERY);
1738 if (!xAsForm.is())
1739 break;
1740 Reference<XChild> xLoopAsChild(xNewTopLevel, UNO_QUERY);
1741 xNewTopLevel = xLoopAsChild.is() ? xLoopAsChild->getParent() : Reference< XInterface >();
1744 if (xNewTopLevel != xMyTopLevel)
1746 // the both objects don't belong to the same forms collection -> not acceptable
1747 throw css::lang::IllegalArgumentException();
1750 m_xLabelControl = std::move(xAsPropSet);
1751 Reference<css::lang::XComponent> xComp(m_xLabelControl, UNO_QUERY);
1752 if (xComp.is())
1753 xComp->addEventListener(static_cast<css::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
1756 break;
1757 default:
1758 OControlModel::setFastPropertyValue_NoBroadcast(nHandle, rValue );
1762 // XPropertyChangeListener
1763 void SAL_CALL OBoundControlModel::propertyChange( const PropertyChangeEvent& evt )
1765 // if the DBColumn value changed, transfer it to the control
1766 if ( evt.PropertyName == PROPERTY_VALUE )
1768 OSL_ENSURE( evt.Source == getField(), "OBoundControlModel::propertyChange: value changes from components other than our database column?" );
1769 osl::MutexGuard aGuard(m_aMutex);
1770 if ( m_bForwardValueChanges && m_xColumn.is() )
1771 transferDbValueToControl();
1774 else
1776 OSL_ENSURE( evt.Source == m_xExternalBinding, "OBoundControlModel::propertyChange: where did this come from?" );
1777 // our binding has properties which can control properties of ourself
1778 OUString sBindingControlledProperty;
1779 bool bForwardToLabelControl = false;
1780 if ( evt.PropertyName == PROPERTY_READONLY )
1782 sBindingControlledProperty = PROPERTY_READONLY;
1785 else if ( evt.PropertyName == PROPERTY_RELEVANT )
1787 sBindingControlledProperty = PROPERTY_ENABLED;
1788 bForwardToLabelControl = true;
1791 else
1792 return;
1795 setPropertyValue( sBindingControlledProperty, evt.NewValue );
1796 if ( bForwardToLabelControl && m_xLabelControl.is() )
1797 m_xLabelControl->setPropertyValue( sBindingControlledProperty, evt.NewValue );
1800 catch( const Exception& )
1802 DBG_UNHANDLED_EXCEPTION("forms.component");
1803 SAL_WARN("forms.component", "OBoundControlModel::propertyChange: could not adjust my binding-controlled property!");
1809 void SAL_CALL OBoundControlModel::onRowSetChanged( const EventObject& /*i_Event*/ )
1811 ControlModelLock aLock( *this );
1812 FieldChangeNotifier aBoundFieldNotifier( aLock );
1813 // disconnect from database column (which is controlled by parent, directly or indirectly)
1814 if ( hasField() )
1815 impl_disconnectDatabaseColumn_noNotify();
1816 // log off old listeners
1817 if ( isFormListening() )
1818 doFormListening( false );
1819 // determine the new ambient form
1820 impl_determineAmbientForm_nothrow();
1821 // log on new listeners
1822 doFormListening( true );
1823 // re-connect to database column if needed and possible
1824 if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() )
1825 impl_connectDatabaseColumn_noNotify( false );
1828 // XBoundComponent
1829 void SAL_CALL OBoundControlModel::addUpdateListener(const Reference<XUpdateListener>& _rxListener)
1831 m_aUpdateListeners.addInterface(_rxListener);
1834 void SAL_CALL OBoundControlModel::removeUpdateListener(const Reference< XUpdateListener>& _rxListener)
1836 m_aUpdateListeners.removeInterface(_rxListener);
1839 sal_Bool SAL_CALL OBoundControlModel::commit()
1841 ControlModelLock aLock( *this );
1842 OSL_PRECOND( m_bCommitable, "OBoundControlModel::commit: invalid call (I'm not committable!) " );
1843 if ( hasExternalValueBinding() )
1845 // in most cases, no action is required: For most derivees, we know the value property of
1846 // our control (see initValueProperty), and when an external binding is active, we
1847 // instantly forward all changes in this property to the external binding.
1848 if ( m_sValuePropertyName.isEmpty() )
1849 // but for those derivees which did not use this feature, we need an
1850 // explicit transfer
1851 transferControlValueToExternal( aLock );
1852 return true;
1855 OSL_ENSURE( !hasExternalValueBinding(), "OBoundControlModel::commit: control flow broken!" );
1856 // we reach this only if we're not working with an external binding
1857 if ( !hasField() )
1858 return true;
1859 ::comphelper::OInterfaceIteratorHelper3 aIter( m_aUpdateListeners );
1860 EventObject aEvent;
1861 aEvent.Source = static_cast< XWeak* >( this );
1862 bool bSuccess = true;
1863 aLock.release();
1864 // UNSAFE >
1865 while (aIter.hasMoreElements() && bSuccess)
1866 bSuccess = aIter.next()->approveUpdate( aEvent );
1867 // < UNSAFE
1868 aLock.acquire();
1869 if ( bSuccess )
1873 if ( m_xColumnUpdate.is() )
1874 bSuccess = commitControlValueToDbColumn( false );
1877 catch(const Exception&)
1879 bSuccess = false;
1884 if ( bSuccess )
1886 aLock.release();
1887 m_aUpdateListeners.notifyEach( &XUpdateListener::updated, aEvent );
1889 return bSuccess;
1892 void OBoundControlModel::resetField()
1894 m_xColumnUpdate.clear();
1895 m_xColumn.clear();
1896 m_xField.clear();
1897 m_nFieldType = DataType::OTHER;
1900 void OBoundControlModel::connectToField(const Reference<XRowSet>& rForm)
1902 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::connectToField: invalid call (have an external binding)!" );
1903 // if there's a connection to the database
1904 if (!(rForm.is() && getConnection(rForm).is()))
1905 return;
1907 // determine field and PropertyChangeListener
1908 m_xCursor = rForm;
1909 Reference<XPropertySet> xFieldCandidate;
1910 if (m_xCursor.is())
1912 Reference<XColumnsSupplier> xColumnsSupplier(m_xCursor, UNO_QUERY);
1913 DBG_ASSERT(xColumnsSupplier.is(), "OBoundControlModel::connectToField : the row set should support the css::sdb::ResultSet service !");
1914 if (xColumnsSupplier.is())
1916 Reference<XNameAccess> xColumns = xColumnsSupplier->getColumns();
1917 if (xColumns.is() && xColumns->hasByName(m_aControlSource))
1919 OSL_VERIFY( xColumns->getByName(m_aControlSource) >>= xFieldCandidate );
1928 sal_Int32 nFieldType = DataType::OTHER;
1929 if ( xFieldCandidate.is() )
1931 xFieldCandidate->getPropertyValue( PROPERTY_FIELDTYPE ) >>= nFieldType;
1932 if ( approveDbColumnType( nFieldType ) )
1933 impl_setField_noNotify( xFieldCandidate );
1936 else
1937 impl_setField_noNotify( nullptr );
1938 if ( m_xField.is() )
1940 if ( m_xField->getPropertySetInfo()->hasPropertyByName( PROPERTY_VALUE ) )
1942 m_nFieldType = nFieldType;
1943 // listen to changing values
1944 m_xField->addPropertyChangeListener( PROPERTY_VALUE, this );
1945 m_xColumnUpdate.set( m_xField, UNO_QUERY );
1946 m_xColumn.set( m_xField, UNO_QUERY );
1947 sal_Int32 nNullableFlag = ColumnValue::NO_NULLS;
1948 m_xField->getPropertyValue(PROPERTY_ISNULLABLE) >>= nNullableFlag;
1949 // tdf#122319 - don't allow nullable form components if input is required
1950 m_bRequired = (ColumnValue::NO_NULLS == nNullableFlag || m_bInputRequired);
1951 // we're optimistic: in case of ColumnValue_NULLABLE_UNKNOWN we assume nullability...
1953 else
1955 SAL_WARN("forms.component", "OBoundControlModel::connectToField: property " << PROPERTY_VALUE << " not supported!");
1956 impl_setField_noNotify( nullptr );
1963 catch( const Exception& )
1965 DBG_UNHANDLED_EXCEPTION("forms.component");
1966 resetField();
1970 void OBoundControlModel::initFromField( const Reference< XRowSet >& _rxRowSet )
1972 // but only if the rowset is positioned on a valid record
1973 if ( !(hasField() && _rxRowSet.is()) )
1974 return;
1976 bool shouldTransfer(!_rxRowSet->isBeforeFirst() && !_rxRowSet->isAfterLast());
1977 if (!shouldTransfer)
1979 const Reference< XPropertySet > xPS(_rxRowSet, UNO_QUERY);
1980 if (xPS.is())
1982 assert(!shouldTransfer);
1983 xPS->getPropertyValue(u"IsNew"_ustr) >>= shouldTransfer;
1986 if ( shouldTransfer )
1987 transferDbValueToControl();
1988 else
1989 // reset the field if the row set is empty
1990 // #i30661#
1991 resetNoBroadcast();
1994 bool OBoundControlModel::approveDbColumnType(sal_Int32 _nColumnType)
1996 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::approveDbColumnType: invalid call (have an external binding)!" );
1997 if ((_nColumnType == DataType::BINARY) || (_nColumnType == DataType::VARBINARY)
1998 || (_nColumnType == DataType::LONGVARBINARY) || (_nColumnType == DataType::OTHER)
1999 || (_nColumnType == DataType::OBJECT) || (_nColumnType == DataType::DISTINCT)
2000 || (_nColumnType == DataType::STRUCT) || (_nColumnType == DataType::ARRAY)
2001 || (_nColumnType == DataType::BLOB) /*|| (_nColumnType == DataType::CLOB)*/
2002 || (_nColumnType == DataType::REF) || (_nColumnType == DataType::SQLNULL))
2003 return false;
2004 return true;
2007 void OBoundControlModel::impl_determineAmbientForm_nothrow()
2009 Reference< XInterface > xParent( getParent() );
2010 m_xAmbientForm.set( xParent, UNO_QUERY );
2011 if ( !m_xAmbientForm.is() )
2013 Reference< XRowSetSupplier > xSupRowSet( xParent, UNO_QUERY );
2014 if ( xSupRowSet.is() )
2015 m_xAmbientForm.set( xSupRowSet->getRowSet(), UNO_QUERY );
2019 void OBoundControlModel::impl_connectDatabaseColumn_noNotify( bool _bFromReload )
2021 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::impl_connectDatabaseColumn_noNotify: not to be called with an external value binding!" );
2022 // consistency checks
2023 DBG_ASSERT( !( hasField() && !_bFromReload ),
2024 "OBoundControlModel::impl_connectDatabaseColumn_noNotify: the form is just *loaded*, but we already have a field!" );
2026 Reference< XRowSet > xRowSet( m_xAmbientForm, UNO_QUERY );
2027 OSL_ENSURE( xRowSet.is(), "OBoundControlModel::impl_connectDatabaseColumn_noNotify: no row set!" );
2028 if ( !xRowSet.is() )
2029 return;
2030 if ( !hasField() || _bFromReload )
2032 // connect to the column
2033 connectToField( xRowSet );
2036 // now that we're connected (more or less, even if we did not find a column),
2037 // we definitely want to forward any potentially occurring value changes
2038 m_bForwardValueChanges = true;
2039 // let derived classes react on this new connection
2040 m_bLoaded = true;
2041 onConnectedDbColumn( xRowSet );
2042 // initially transfer the db column value to the control, if we successfully connected to a database column
2043 if ( hasField() )
2044 initFromField( xRowSet );
2047 void OBoundControlModel::impl_disconnectDatabaseColumn_noNotify()
2049 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::impl_disconnectDatabaseColumn_noNotify: not to be called with an external value binding!" );
2050 // let derived classes react on this
2051 onDisconnectedDbColumn();
2052 if ( hasField() )
2054 getField()->removePropertyChangeListener( PROPERTY_VALUE, this );
2055 resetField();
2058 m_xCursor = nullptr;
2059 m_bLoaded = false;
2062 // XLoadListener
2063 void SAL_CALL OBoundControlModel::loaded( const EventObject& _rEvent )
2065 ControlModelLock aLock( *this );
2066 FieldChangeNotifier aBoundFieldNotifier( aLock );
2067 OSL_ENSURE( _rEvent.Source == m_xAmbientForm, "OBoundControlModel::loaded: where does this come from?" );
2068 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::loaded: we should never reach this with an external value binding!" );
2069 if ( hasExternalValueBinding() )
2070 return;
2071 impl_connectDatabaseColumn_noNotify( false );
2074 void SAL_CALL OBoundControlModel::unloaded( const css::lang::EventObject& /*aEvent*/ )
2076 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::unloaded: we should never reach this with an external value binding!" );
2079 void SAL_CALL OBoundControlModel::reloading( const css::lang::EventObject& /*aEvent*/ )
2081 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::reloading: we should never reach this with an external value binding!" );
2082 if ( hasExternalValueBinding() )
2083 return;
2084 osl::MutexGuard aGuard(m_aMutex);
2085 m_bForwardValueChanges = false;
2088 void SAL_CALL OBoundControlModel::unloading(const css::lang::EventObject& /*aEvent*/)
2090 ControlModelLock aLock( *this );
2091 FieldChangeNotifier aBoundFieldNotifier( aLock );
2092 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::unloading: we should never reach this with an external value binding!" );
2093 if ( hasExternalValueBinding() )
2094 return;
2095 impl_disconnectDatabaseColumn_noNotify();
2098 void SAL_CALL OBoundControlModel::reloaded( const EventObject& _rEvent )
2100 ControlModelLock aLock( *this );
2101 FieldChangeNotifier aBoundFieldNotifier( aLock );
2102 OSL_ENSURE( _rEvent.Source == m_xAmbientForm, "OBoundControlModel::reloaded: where does this come from?" );
2103 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::reloaded: we should never reach this with an external value binding!" );
2104 if ( hasExternalValueBinding() )
2105 return;
2106 impl_connectDatabaseColumn_noNotify( true );
2109 void OBoundControlModel::setControlValue( const Any& _rValue, ValueChangeInstigator _eInstigator )
2111 m_eControlValueChangeInstigator = _eInstigator;
2112 doSetControlValue( _rValue );
2113 m_eControlValueChangeInstigator = eOther;
2116 void OBoundControlModel::doSetControlValue( const Any& _rValue )
2118 OSL_PRECOND( m_xAggregateFastSet.is() && m_xAggregateSet.is(),
2119 "OBoundControlModel::doSetControlValue: invalid aggregate !" );
2120 OSL_PRECOND( !m_sValuePropertyName.isEmpty() || ( m_nValuePropertyAggregateHandle != -1 ),
2121 "OBoundControlModel::doSetControlValue: please override if you have own value property handling!" );
2124 // release our mutex once (it's acquired in one of the calling methods), as setting aggregate properties
2125 // may cause any uno controls belonging to us to lock the solar mutex, which is potentially dangerous with
2126 // our own mutex locked
2127 MutexRelease aRelease( m_aMutex );
2128 if ( ( m_nValuePropertyAggregateHandle != -1 ) && m_xAggregateFastSet.is() )
2130 m_xAggregateFastSet->setFastPropertyValue( m_nValuePropertyAggregateHandle, _rValue );
2133 else if ( !m_sValuePropertyName.isEmpty() && m_xAggregateSet.is() )
2135 m_xAggregateSet->setPropertyValue( m_sValuePropertyName, _rValue );
2140 catch( const Exception& )
2142 TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::doSetControlValue");
2146 void OBoundControlModel::onConnectedValidator( )
2150 // if we have an external validator, we do not want the control to force invalid
2151 // inputs to the default value. Instead, invalid inputs should be translated
2152 // to NaN (not a number)
2153 Reference< XPropertySetInfo > xAggregatePropertyInfo;
2154 if ( m_xAggregateSet.is() )
2155 xAggregatePropertyInfo = m_xAggregateSet->getPropertySetInfo();
2156 if ( xAggregatePropertyInfo.is() && xAggregatePropertyInfo->hasPropertyByName( PROPERTY_ENFORCE_FORMAT ) )
2157 m_xAggregateSet->setPropertyValue( PROPERTY_ENFORCE_FORMAT, Any( false ) );
2160 catch( const Exception& )
2162 TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::onConnectedValidator");
2165 recheckValidity( false );
2168 void OBoundControlModel::onDisconnectedValidator( )
2172 Reference< XPropertySetInfo > xAggregatePropertyInfo;
2173 if ( m_xAggregateSet.is() )
2174 xAggregatePropertyInfo = m_xAggregateSet->getPropertySetInfo();
2175 if ( xAggregatePropertyInfo.is() && xAggregatePropertyInfo->hasPropertyByName( PROPERTY_ENFORCE_FORMAT ) )
2176 m_xAggregateSet->setPropertyValue( PROPERTY_ENFORCE_FORMAT, Any( true ) );
2179 catch( const Exception& )
2181 TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::onDisconnectedValidator");
2184 recheckValidity( false );
2187 void OBoundControlModel::onConnectedExternalValue( )
2189 calculateExternalValueType();
2192 void OBoundControlModel::onConnectedDbColumn( const Reference< XInterface >& /*_rxForm*/ )
2194 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::onConnectedDbColumn: how this? There's an external value binding!" );
2197 void OBoundControlModel::onDisconnectedDbColumn()
2199 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::onDisconnectedDbColumn: how this? There's an external value binding!" );
2202 // XReset
2203 Any OBoundControlModel::getDefaultForReset() const
2205 return Any();
2208 void OBoundControlModel::resetNoBroadcast()
2210 setControlValue( getDefaultForReset(), eOther );
2213 void OBoundControlModel::addResetListener(const Reference<XResetListener>& l)
2215 m_aResetHelper.addResetListener( l );
2218 void OBoundControlModel::removeResetListener(const Reference<XResetListener>& l)
2220 m_aResetHelper.removeResetListener( l );
2223 void OBoundControlModel::reset()
2225 if ( !m_aResetHelper.approveReset() )
2226 return;
2227 ControlModelLock aLock( *this );
2228 // on a new record?
2229 bool bIsNewRecord = false;
2230 Reference<XPropertySet> xSet( m_xCursor, UNO_QUERY );
2231 if ( xSet.is() )
2235 xSet->getPropertyValue( PROPERTY_ISNEW ) >>= bIsNewRecord;
2238 catch( const Exception& )
2240 DBG_UNHANDLED_EXCEPTION("forms.component");
2245 // cursor on an invalid row?
2246 bool bInvalidCursorPosition = true;
2249 bInvalidCursorPosition = m_xCursor.is()
2250 && ( m_xCursor->isAfterLast()
2251 || m_xCursor->isBeforeFirst()
2253 && !bIsNewRecord;
2256 catch( const SQLException& )
2258 TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::reset: caught an SQL exception!" );
2261 // #i24495# - don't count the insert row as "invalid"
2262 bool bSimpleReset =
2263 ( !m_xColumn.is() // no connection to a database column
2264 || ( m_xCursor.is() // OR we have an improperly positioned cursor
2265 && bInvalidCursorPosition
2267 || hasExternalValueBinding() // OR we have an external value binding
2269 if ( !bSimpleReset )
2271 // The default values will be set if and only if the current value of the field which we're bound
2272 // to is NULL.
2273 // Else, the current field value should be refreshed
2274 // This behaviour is not completely ... "matured": What should happen if the field as well as the
2275 // control have a default value?
2276 bool bIsNull = true;
2277 // we have to access the field content at least once to get a reliable result by XColumn::wasNull
2280 // normally, we'd do a getString here. However, this is extremely expensive in the case
2281 // of binary fields. Unfortunately, getString is the only method which is guaranteed
2282 // to *always* succeed, all other getXXX methods may fail if the column is asked for a
2283 // non-convertible type
2284 sal_Int32 nFieldType = DataType::OBJECT;
2285 getField()->getPropertyValue( PROPERTY_FIELDTYPE ) >>= nFieldType;
2286 if ( ( nFieldType == DataType::BINARY )
2287 || ( nFieldType == DataType::VARBINARY )
2288 || ( nFieldType == DataType::LONGVARBINARY )
2289 || ( nFieldType == DataType::OBJECT )
2290 /*|| ( nFieldType == DataType::CLOB )*/
2292 m_xColumn->getBinaryStream();
2293 else if ( nFieldType == DataType::BLOB )
2294 m_xColumn->getBlob();
2295 else
2296 m_xColumn->getString();
2297 bIsNull = m_xColumn->wasNull();
2300 catch(const Exception&)
2302 DBG_UNHANDLED_EXCEPTION("forms.component");
2303 SAL_WARN("forms.component", "OBoundControlModel::reset: this should have succeeded in all cases!");
2306 bool bNeedValueTransfer = true;
2307 if ( bIsNull )
2309 if ( bIsNewRecord )
2311 // reset the control to its default
2312 resetNoBroadcast();
2313 // and immediately commit the changes to the DB column, to keep consistency
2314 commitControlValueToDbColumn( true );
2315 bNeedValueTransfer = false;
2320 if ( bNeedValueTransfer )
2321 transferDbValueToControl();
2324 else
2326 resetNoBroadcast();
2327 // transfer to the external binding, if necessary
2328 if ( hasExternalValueBinding() )
2329 transferControlValueToExternal( aLock );
2332 // revalidate, if necessary
2333 if ( hasValidator() )
2334 recheckValidity( true );
2335 aLock.release();
2336 m_aResetHelper.notifyResetted();
2339 void OBoundControlModel::impl_setField_noNotify( const Reference< XPropertySet>& _rxField )
2341 DBG_ASSERT( !hasExternalValueBinding(), "OBoundControlModel::impl_setField_noNotify: We have an external value binding!" );
2342 m_xField = _rxField;
2345 bool OBoundControlModel::impl_approveValueBinding_nolock( const Reference< XValueBinding >& _rxBinding )
2347 if ( !_rxBinding.is() )
2348 return false;
2349 Sequence< Type > aTypeCandidates;
2351 // SYNCHRONIZED >
2352 ::osl::MutexGuard aGuard( m_aMutex );
2353 aTypeCandidates = getSupportedBindingTypes();
2354 // < SYNCHRONIZED
2357 for (auto const& type : aTypeCandidates)
2359 if ( _rxBinding->supportsType( type ) )
2360 return true;
2362 return false;
2365 void OBoundControlModel::connectExternalValueBinding(
2366 const Reference< XValueBinding >& _rxBinding, ControlModelLock& _rInstanceLock )
2368 OSL_PRECOND( _rxBinding.is(), "OBoundControlModel::connectExternalValueBinding: invalid binding instance!" );
2369 OSL_PRECOND( !hasExternalValueBinding( ), "OBoundControlModel::connectExternalValueBinding: precond not met (currently have a binding)!" );
2370 // if we're connected to a database column, suspend this
2371 if ( hasField() )
2372 impl_disconnectDatabaseColumn_noNotify();
2373 // suspend listening for load-related events at out ambient form.
2374 // This is because an external value binding overrules a possible database binding.
2375 if ( isFormListening() )
2376 doFormListening( false );
2377 // remember this new binding
2378 m_xExternalBinding = _rxBinding;
2379 // tell the derivee
2380 onConnectedExternalValue();
2383 // add as value listener so we get notified when the value changes
2384 Reference< XModifyBroadcaster > xModifiable( m_xExternalBinding, UNO_QUERY );
2385 if ( xModifiable.is() )
2386 xModifiable->addModifyListener( this );
2387 // add as property change listener for some (possibly present) properties we're
2388 // interested in
2389 Reference< XPropertySet > xBindingProps( m_xExternalBinding, UNO_QUERY );
2390 Reference< XPropertySetInfo > xBindingPropsInfo( xBindingProps.is() ? xBindingProps->getPropertySetInfo() : Reference< XPropertySetInfo >() );
2391 if ( xBindingPropsInfo.is() )
2393 if ( xBindingPropsInfo->hasPropertyByName( PROPERTY_READONLY ) )
2395 xBindingProps->addPropertyChangeListener( PROPERTY_READONLY, this );
2396 m_bBindingControlsRO = true;
2399 if ( xBindingPropsInfo->hasPropertyByName( PROPERTY_RELEVANT ) )
2401 xBindingProps->addPropertyChangeListener( PROPERTY_RELEVANT, this );
2402 m_bBindingControlsEnable = true;
2409 catch( const Exception& )
2411 DBG_UNHANDLED_EXCEPTION("forms.component");
2414 // propagate our new value
2415 transferExternalValueToControl( _rInstanceLock );
2416 // if the binding is also a validator, use it, too. This is a constraint of the
2417 // com.sun.star.form.binding.ValidatableBindableFormComponent service
2418 if ( !m_bSupportsValidation )
2419 return;
2423 Reference< XValidator > xAsValidator( _rxBinding, UNO_QUERY );
2424 if ( xAsValidator.is() )
2425 setValidator( xAsValidator );
2428 catch( const Exception& )
2430 DBG_UNHANDLED_EXCEPTION("forms.component");
2434 void OBoundControlModel::disconnectExternalValueBinding( )
2438 // not listening at the binding anymore
2439 Reference< XModifyBroadcaster > xModifiable( m_xExternalBinding, UNO_QUERY );
2440 if ( xModifiable.is() )
2441 xModifiable->removeModifyListener( this );
2442 // remove as property change listener
2443 Reference< XPropertySet > xBindingProps( m_xExternalBinding, UNO_QUERY );
2444 if ( m_bBindingControlsRO )
2445 xBindingProps->removePropertyChangeListener( PROPERTY_READONLY, this );
2446 if ( m_bBindingControlsEnable )
2447 xBindingProps->removePropertyChangeListener( PROPERTY_RELEVANT, this );
2450 catch( const Exception& )
2452 TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::disconnectExternalValueBinding");
2455 // if the binding also acts as our validator, disconnect the validator, too
2456 if ( ( m_xExternalBinding == m_xValidator ) && m_xValidator.is() )
2457 disconnectValidator( );
2458 // no binding anymore
2459 m_xExternalBinding.clear();
2460 // be a load listener at our form, again. This was suspended while we had
2461 // an external value binding in place.
2462 doFormListening( true );
2463 // re-connect to database column of the new parent
2464 if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() )
2465 impl_connectDatabaseColumn_noNotify( false );
2468 void SAL_CALL OBoundControlModel::setValueBinding( const Reference< XValueBinding >& _rxBinding )
2470 OSL_PRECOND( m_bSupportsExternalBinding, "OBoundControlModel::setValueBinding: How did you reach this method?" );
2471 // the interface for this method should not have been exposed if we do not
2472 // support binding to external data
2473 // allow reset
2474 if ( _rxBinding.is() && !impl_approveValueBinding_nolock( _rxBinding ) )
2476 throw IncompatibleTypesException(
2477 ResourceManager::loadString(RID_STR_INCOMPATIBLE_TYPES),
2478 *this
2482 ControlModelLock aLock( *this );
2483 // since a ValueBinding overrules any potentially active database binding, the change in a ValueBinding
2484 // might trigger a change in our BoundField.
2485 FieldChangeNotifier aBoundFieldNotifier( aLock );
2486 // disconnect from the old binding
2487 if ( hasExternalValueBinding() )
2488 disconnectExternalValueBinding( );
2489 // connect to the new binding
2490 if ( _rxBinding.is() )
2491 connectExternalValueBinding( _rxBinding, aLock );
2494 Reference< XValueBinding > SAL_CALL OBoundControlModel::getValueBinding( )
2496 ::osl::MutexGuard aGuard( m_aMutex );
2497 OSL_PRECOND( m_bSupportsExternalBinding, "OBoundControlModel::getValueBinding: How did you reach this method?" );
2498 // the interface for this method should not have been exposed if we do not
2499 // support binding to external data
2500 return m_xExternalBinding;
2503 void SAL_CALL OBoundControlModel::modified( const EventObject& _rEvent )
2505 ControlModelLock aLock( *this );
2506 OSL_PRECOND( hasExternalValueBinding(), "OBoundControlModel::modified: Where did this come from?" );
2507 if ( !m_bTransferringValue && ( m_xExternalBinding == _rEvent.Source ) && m_xExternalBinding.is() )
2509 transferExternalValueToControl( aLock );
2513 void OBoundControlModel::transferDbValueToControl( )
2517 setControlValue( translateDbColumnToControlValue(), eDbColumnBinding );
2520 catch( const Exception& )
2522 DBG_UNHANDLED_EXCEPTION("forms.component");
2526 void OBoundControlModel::transferExternalValueToControl( ControlModelLock& _rInstanceLock )
2528 Reference< XValueBinding > xExternalBinding( m_xExternalBinding );
2529 Type aValueExchangeType( getExternalValueType() );
2530 _rInstanceLock.release();
2531 // UNSAFE >
2532 Any aExternalValue;
2535 aExternalValue = xExternalBinding->getValue( aValueExchangeType );
2538 catch( const Exception& )
2540 DBG_UNHANDLED_EXCEPTION("forms.component");
2542 // < UNSAFE
2543 _rInstanceLock.acquire();
2544 setControlValue( translateExternalValueToControlValue( aExternalValue ), eExternalBinding );
2547 void OBoundControlModel::transferControlValueToExternal( ControlModelLock& _rInstanceLock )
2549 OSL_PRECOND( m_bSupportsExternalBinding && hasExternalValueBinding(),
2550 "OBoundControlModel::transferControlValueToExternal: precondition not met!" );
2551 if ( !m_xExternalBinding.is() )
2552 return;
2554 Any aExternalValue( translateControlValueToExternalValue() );
2555 m_bTransferringValue = true;
2556 _rInstanceLock.release();
2557 // UNSAFE >
2560 m_xExternalBinding->setValue( aExternalValue );
2563 catch( const Exception& )
2565 DBG_UNHANDLED_EXCEPTION("forms.component");
2568 // < UNSAFE
2569 _rInstanceLock.acquire();
2570 m_bTransferringValue = false;
2573 Sequence< Type > OBoundControlModel::getSupportedBindingTypes()
2575 return Sequence< Type >( &m_aValuePropertyType, 1 );
2578 void OBoundControlModel::calculateExternalValueType()
2580 m_aExternalValueType = Type();
2581 if ( !m_xExternalBinding.is() )
2582 return;
2583 const Sequence< Type > aTypeCandidates( getSupportedBindingTypes() );
2584 for ( auto const & typeCandidate : aTypeCandidates )
2586 if ( m_xExternalBinding->supportsType( typeCandidate ) )
2588 m_aExternalValueType = typeCandidate;
2589 break;
2594 Any OBoundControlModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
2596 OSL_PRECOND( m_bSupportsExternalBinding && hasExternalValueBinding(),
2597 "OBoundControlModel::translateExternalValueToControlValue: precondition not met!" );
2598 Any aControlValue( _rExternalValue );
2599 // if the external value is VOID, and our value property is not allowed to be VOID,
2600 // then default-construct a value
2601 if ( !aControlValue.hasValue() && !m_bValuePropertyMayBeVoid )
2602 aControlValue.setValue( nullptr, m_aValuePropertyType );
2603 // out of here
2604 return aControlValue;
2607 Any OBoundControlModel::translateControlValueToExternalValue( ) const
2609 return getControlValue( );
2612 Any OBoundControlModel::translateControlValueToValidatableValue( ) const
2614 OSL_PRECOND( m_xValidator.is(), "OBoundControlModel::translateControlValueToValidatableValue: no validator, so why should I?" );
2615 if ( ( m_xValidator == m_xExternalBinding ) && m_xValidator.is() )
2616 return translateControlValueToExternalValue();
2617 return getControlValue();
2620 Any OBoundControlModel::getControlValue( ) const
2622 OSL_PRECOND( m_xAggregateFastSet.is() && m_xAggregateSet.is(),
2623 "OBoundControlModel::getControlValue: invalid aggregate !" );
2624 OSL_PRECOND( !m_sValuePropertyName.isEmpty() || ( m_nValuePropertyAggregateHandle != -1 ),
2625 "OBoundControlModel::getControlValue: please override if you have own value property handling!" );
2626 // determine the current control value
2627 Any aControlValue;
2628 if ( ( m_nValuePropertyAggregateHandle != -1 ) && m_xAggregateFastSet.is() )
2630 aControlValue = m_xAggregateFastSet->getFastPropertyValue( m_nValuePropertyAggregateHandle );
2633 else if ( !m_sValuePropertyName.isEmpty() && m_xAggregateSet.is() )
2635 aControlValue = m_xAggregateSet->getPropertyValue( m_sValuePropertyName );
2637 return aControlValue;
2640 void OBoundControlModel::connectValidator( const Reference< XValidator >& _rxValidator )
2642 OSL_PRECOND( _rxValidator.is(), "OBoundControlModel::connectValidator: invalid validator instance!" );
2643 OSL_PRECOND( !hasValidator( ), "OBoundControlModel::connectValidator: precond not met (have a validator currently)!" );
2644 m_xValidator = _rxValidator;
2646 // add as value listener so we get notified when the value changes
2647 if ( m_xValidator.is() )
2651 m_xValidator->addValidityConstraintListener( this );
2654 catch( const RuntimeException& )
2658 onConnectedValidator( );
2661 void OBoundControlModel::disconnectValidator( )
2663 OSL_PRECOND( hasValidator( ), "OBoundControlModel::connectValidator: precond not met (don't have a validator currently)!" );
2665 // add as value listener so we get notified when the value changes
2666 if ( m_xValidator.is() )
2670 m_xValidator->removeValidityConstraintListener( this );
2673 catch( const RuntimeException& )
2678 m_xValidator.clear();
2680 onDisconnectedValidator( );
2683 void SAL_CALL OBoundControlModel::setValidator( const Reference< XValidator >& _rxValidator )
2685 osl::MutexGuard aGuard( m_aMutex );
2686 OSL_PRECOND( m_bSupportsValidation, "OBoundControlModel::setValidator: How did you reach this method?" );
2687 // the interface for this method should not have been exposed if we do not
2688 // support validation
2690 // early out if the validator does not change
2691 if ( _rxValidator == m_xValidator )
2692 return;
2694 if ( m_xValidator.is() && ( m_xValidator == m_xExternalBinding ) )
2695 throw VetoException(
2696 ResourceManager::loadString(RID_STR_INVALID_VALIDATOR),
2697 *this
2700 // disconnect from the old validator
2701 if ( hasValidator() )
2702 disconnectValidator( );
2704 // connect to the new validator
2705 if ( _rxValidator.is() )
2706 connectValidator( _rxValidator );
2709 Reference< XValidator > SAL_CALL OBoundControlModel::getValidator( )
2711 ::osl::MutexGuard aGuard( m_aMutex );
2712 OSL_PRECOND( m_bSupportsValidation, "OBoundControlModel::getValidator: How did you reach this method?" );
2713 // the interface for this method should not have been exposed if we do not
2714 // support validation
2716 return m_xValidator;
2719 void SAL_CALL OBoundControlModel::validityConstraintChanged( const EventObject& /*Source*/ )
2721 osl::MutexGuard aGuard( m_aMutex );
2722 OSL_PRECOND( m_bSupportsValidation, "OBoundControlModel::validityConstraintChanged: How did you reach this method?" );
2723 // the interface for this method should not have been exposed if we do not
2724 // support validation
2726 recheckValidity( false );
2729 sal_Bool SAL_CALL OBoundControlModel::isValid( )
2731 return m_bIsCurrentValueValid;
2734 css::uno::Any OBoundControlModel::getCurrentFormComponentValue() const
2736 if ( hasValidator() )
2737 return translateControlValueToValidatableValue();
2738 return getControlValue();
2741 Any SAL_CALL OBoundControlModel::getCurrentValue( )
2743 ::osl::MutexGuard aGuard( m_aMutex );
2744 return getCurrentFormComponentValue();
2747 void SAL_CALL OBoundControlModel::addFormComponentValidityListener( const Reference< validation::XFormComponentValidityListener >& Listener )
2749 if ( Listener.is() )
2750 m_aFormComponentListeners.addInterface( Listener );
2753 void SAL_CALL OBoundControlModel::removeFormComponentValidityListener( const Reference< validation::XFormComponentValidityListener >& Listener )
2755 if ( Listener.is() )
2756 m_aFormComponentListeners.removeInterface( Listener );
2759 void OBoundControlModel::recheckValidity( bool _bForceNotification )
2763 bool bIsCurrentlyValid = true;
2764 if ( hasValidator() )
2765 bIsCurrentlyValid = m_xValidator->isValid( translateControlValueToValidatableValue() );
2767 if ( ( bIsCurrentlyValid != m_bIsCurrentValueValid ) || _bForceNotification )
2769 m_bIsCurrentValueValid = bIsCurrentlyValid;
2771 // release our mutex for the notifications
2772 MutexRelease aRelease( m_aMutex );
2773 m_aFormComponentListeners.notifyEach( &validation::XFormComponentValidityListener::componentValidityChanged, EventObject( *this ) );
2778 catch( const Exception& )
2780 TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::recheckValidity");
2784 void OBoundControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const
2786 OControlModel::describeFixedProperties( _rProps );
2787 sal_Int32 nOldCount = _rProps.getLength();
2788 _rProps.realloc( nOldCount + 5);
2789 css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
2790 *pProperties++ = css::beans::Property(PROPERTY_CONTROLSOURCE, PROPERTY_ID_CONTROLSOURCE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
2791 *pProperties++ = css::beans::Property(PROPERTY_BOUNDFIELD, PROPERTY_ID_BOUNDFIELD, cppu::UnoType<XPropertySet>::get(),
2792 css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
2793 *pProperties++ = css::beans::Property(PROPERTY_CONTROLLABEL, PROPERTY_ID_CONTROLLABEL, cppu::UnoType<XPropertySet>::get(),
2794 css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID);
2795 *pProperties++ = css::beans::Property(PROPERTY_CONTROLSOURCEPROPERTY, PROPERTY_ID_CONTROLSOURCEPROPERTY, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
2796 *pProperties++ = css::beans::Property(PROPERTY_INPUT_REQUIRED, PROPERTY_ID_INPUT_REQUIRED, cppu::UnoType<bool>::get(),
2797 css::beans::PropertyAttribute::BOUND);
2798 DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
2802 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */