Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / forms / source / component / FormComponent.cxx
blobe18ca14628c13a89bbf15c2c4bf818b60b472077
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 Reference< XTypeProvider > xProv;
185 if ( query_aggregation( m_xAggregate, xProv ) )
186 aTypes.addTypes( xProv->getTypes() );
188 return aTypes.getTypes();
191 Sequence<Type> OControl::_getTypes()
193 return TypeBag( OComponentHelper::getTypes(), OControl_BASE::getTypes() ).getTypes();
196 // OComponentHelper
197 void OControl::disposing()
199 OComponentHelper::disposing();
201 m_aWindowStateGuard.attach( nullptr, nullptr );
203 Reference< XComponent > xComp;
204 if (query_aggregation(m_xAggregate, xComp))
205 xComp->dispose();
208 // XServiceInfo
209 sal_Bool SAL_CALL OControl::supportsService(const OUString& _rsServiceName)
211 return cppu::supportsService(this, _rsServiceName);
214 Sequence< OUString > OControl::getAggregateServiceNames() const
216 Sequence< OUString > aAggServices;
217 Reference< XServiceInfo > xInfo;
218 if ( query_aggregation( m_xAggregate, xInfo ) )
219 aAggServices = xInfo->getSupportedServiceNames();
221 return aAggServices;
224 Sequence<OUString> SAL_CALL OControl::getSupportedServiceNames()
226 // no own supported service names
227 return getAggregateServiceNames();
230 // XEventListener
231 void SAL_CALL OControl::disposing(const css::lang::EventObject& _rEvent)
233 Reference< XInterface > xAggAsIface;
234 query_aggregation(m_xAggregate, xAggAsIface);
236 // does the disposing come from the aggregate?
237 if (xAggAsIface != Reference< XInterface >(_rEvent.Source, UNO_QUERY))
238 { // no -> forward it
239 Reference<css::lang::XEventListener> xListener;
240 if (query_aggregation(m_xAggregate, xListener))
241 xListener->disposing(_rEvent);
245 // XControl
246 void SAL_CALL OControl::setContext(const Reference< XInterface >& Context)
248 if (m_xControl.is())
249 m_xControl->setContext(Context);
252 Reference< XInterface > SAL_CALL OControl::getContext()
254 return m_xControl.is() ? m_xControl->getContext() : Reference< XInterface >();
257 void OControl::impl_resetStateGuard_nothrow()
259 Reference< XWindow2 > xWindow;
260 Reference< XControlModel > xModel;
263 xWindow.set( getPeer(), UNO_QUERY );
264 xModel = getModel();
266 catch( const Exception& )
268 DBG_UNHANDLED_EXCEPTION("forms.component");
270 m_aWindowStateGuard.attach( xWindow, xModel );
273 void SAL_CALL OControl::createPeer(const Reference<XToolkit>& _rxToolkit, const Reference<XWindowPeer>& _rxParent)
275 if ( m_xControl.is() )
277 m_xControl->createPeer( _rxToolkit, _rxParent );
278 impl_resetStateGuard_nothrow();
282 Reference<XWindowPeer> SAL_CALL OControl::getPeer()
284 return m_xControl.is() ? m_xControl->getPeer() : Reference<XWindowPeer>();
287 sal_Bool SAL_CALL OControl::setModel(const Reference<XControlModel>& Model)
289 if ( !m_xControl.is() )
290 return false;
292 bool bSuccess = m_xControl->setModel( Model );
293 impl_resetStateGuard_nothrow();
294 return bSuccess;
297 Reference<XControlModel> SAL_CALL OControl::getModel()
299 return m_xControl.is() ? m_xControl->getModel() : Reference<XControlModel>();
302 Reference<XView> SAL_CALL OControl::getView()
304 return m_xControl.is() ? m_xControl->getView() : Reference<XView>();
307 void SAL_CALL OControl::setDesignMode(sal_Bool bOn)
309 if (m_xControl.is())
310 m_xControl->setDesignMode(bOn);
313 sal_Bool SAL_CALL OControl::isDesignMode()
315 return !m_xControl.is() || m_xControl->isDesignMode();
318 sal_Bool SAL_CALL OControl::isTransparent()
320 return !m_xControl.is() || m_xControl->isTransparent();
323 OBoundControl::OBoundControl( const Reference< XComponentContext >& _rxContext,
324 const OUString& _rAggregateService, const bool _bSetDelegator )
325 :OControl( _rxContext, _rAggregateService, _bSetDelegator )
326 ,m_bLocked(false)
330 OBoundControl::~OBoundControl()
334 Sequence< Type> OBoundControl::_getTypes()
336 return TypeBag( OControl::_getTypes(), OBoundControl_BASE::getTypes() ).getTypes();
339 Any SAL_CALL OBoundControl::queryAggregation(const Type& _rType)
341 Any aReturn;
343 // XTypeProvider first - don't ask the OBoundControl_BASE, it would deliver incomplete types
344 if ( _rType.equals( cppu::UnoType<XTypeProvider>::get() ) )
345 aReturn = OControl::queryAggregation( _rType );
347 // ask our own interfaces
348 // (do this first (except XTypeProvider ) - we want to "overwrite" XPropertiesChangeListener)
349 if ( !aReturn.hasValue() )
350 aReturn = OBoundControl_BASE::queryInterface( _rType );
352 // ask the base class
353 if ( !aReturn.hasValue() )
354 aReturn = OControl::queryAggregation( _rType );
356 return aReturn;
359 sal_Bool SAL_CALL OBoundControl::getLock()
361 return m_bLocked;
364 void SAL_CALL OBoundControl::setLock(sal_Bool _bLock)
366 if (m_bLocked == bool(_bLock))
367 return;
369 osl::MutexGuard aGuard(m_aMutex);
370 _setLock(_bLock);
371 m_bLocked = _bLock;
374 void OBoundControl::_setLock(bool _bLock)
376 // try to set the text component to readonly
377 Reference< XWindowPeer > xPeer = getPeer();
378 Reference< XTextComponent > xText( xPeer, UNO_QUERY );
380 if ( xText.is() )
381 xText->setEditable( !_bLock );
382 else
384 // disable the window
385 Reference< XWindow > xComp( xPeer, UNO_QUERY );
386 if ( xComp.is() )
387 xComp->setEnable( !_bLock );
391 sal_Bool SAL_CALL OBoundControl::setModel( const Reference< XControlModel >& _rxModel )
393 return OControl::setModel( _rxModel );
396 void SAL_CALL OBoundControl::disposing(const EventObject& Source)
398 // just disambiguate
399 OControl::disposing(Source);
402 void OBoundControl::disposing()
404 OControl::disposing();
407 // OControlModel
408 Sequence<sal_Int8> SAL_CALL OControlModel::getImplementationId()
410 return css::uno::Sequence<sal_Int8>();
413 Sequence<Type> SAL_CALL OControlModel::getTypes()
415 TypeBag aTypes( _getTypes() );
417 Reference< XTypeProvider > xProv;
419 if ( query_aggregation( m_xAggregate, xProv ) )
420 aTypes.addTypes( xProv->getTypes() );
422 return aTypes.getTypes();
425 Sequence<Type> OControlModel::_getTypes()
427 return TypeBag( OComponentHelper::getTypes(),
428 OPropertySetAggregationHelper::getTypes(),
429 OControlModel_BASE::getTypes()
430 ).getTypes();
433 Any SAL_CALL OControlModel::queryAggregation(const Type& _rType)
435 // base class 1
436 Any aReturn(OComponentHelper::queryAggregation(_rType));
438 // base class 2
439 if (!aReturn.hasValue())
441 aReturn = OControlModel_BASE::queryInterface(_rType);
443 // our own interfaces
444 if (!aReturn.hasValue())
446 aReturn = OPropertySetAggregationHelper::queryInterface(_rType);
447 // our aggregate
448 if (!aReturn.hasValue() && m_xAggregate.is() && !_rType.equals(cppu::UnoType<XCloneable>::get()))
449 aReturn = m_xAggregate->queryAggregation(_rType);
452 return aReturn;
455 void OControlModel::readHelpTextCompatibly(const css::uno::Reference< css::io::XObjectInputStream >& _rxInStream)
457 OUString sHelpText;
458 ::comphelper::operator>>( _rxInStream, sHelpText);
461 if (m_xAggregateSet.is())
462 m_xAggregateSet->setPropertyValue(PROPERTY_HELPTEXT, Any(sHelpText));
464 catch(const Exception&)
466 DBG_UNHANDLED_EXCEPTION("forms.component");
467 SAL_WARN("forms.component", "OControlModel::readHelpTextCompatibly: could not forward the property value to the aggregate!");
471 void OControlModel::writeHelpTextCompatibly(const css::uno::Reference< css::io::XObjectOutputStream >& _rxOutStream)
473 OUString sHelpText;
476 if (m_xAggregateSet.is())
477 m_xAggregateSet->getPropertyValue(PROPERTY_HELPTEXT) >>= sHelpText;
479 catch(const Exception&)
481 DBG_UNHANDLED_EXCEPTION("forms.component");
482 SAL_WARN("forms.component", "OControlModel::writeHelpTextCompatibly: could not retrieve the property value from the aggregate!");
484 ::comphelper::operator<<( _rxOutStream, sHelpText);
487 OControlModel::OControlModel(
488 const Reference<XComponentContext>& _rxContext,
489 const OUString& _rUnoControlModelTypeName,
490 const OUString& rDefault, const bool _bSetDelegator)
491 :OComponentHelper(m_aMutex)
492 ,OPropertySetAggregationHelper(OComponentHelper::rBHelper)
493 ,m_xContext( _rxContext )
494 ,m_lockCount( 0 )
495 ,m_aPropertyBagHelper( *this )
496 ,m_nTabIndex(FRM_DEFAULT_TABINDEX)
497 ,m_nClassId(FormComponentType::CONTROL)
498 ,m_bNativeLook( false )
499 ,m_bStandardTheme( false )
500 ,m_bGenerateVbEvents( false )
501 ,m_nControlTypeinMSO(0) // 0 : default value is create from AOO
502 ,m_nObjIDinMSO(INVALID_OBJ_ID_IN_MSO)
503 // form controls are usually embedded into documents, not dialogs, and in documents
504 // the native look is ugly...
505 // #i37342#
507 if (_rUnoControlModelTypeName.isEmpty()) // the is a model we have to aggregate
508 return;
510 osl_atomic_increment(&m_refCount);
512 m_xAggregate.set(m_xContext->getServiceManager()->createInstanceWithContext(_rUnoControlModelTypeName, m_xContext), UNO_QUERY);
513 setAggregation(m_xAggregate);
515 if ( m_xAggregateSet.is() )
519 if ( !rDefault.isEmpty() )
520 m_xAggregateSet->setPropertyValue( PROPERTY_DEFAULTCONTROL, Any( rDefault ) );
522 catch( const Exception& )
524 TOOLS_WARN_EXCEPTION("forms.component", "OControlModel::OControlModel");
528 if (_bSetDelegator)
529 doSetDelegator();
531 // Refcount is at NULL again
532 osl_atomic_decrement(&m_refCount);
535 OControlModel::OControlModel( const OControlModel* _pOriginal, const Reference< XComponentContext>& _rxFactory, const bool _bCloneAggregate, const bool _bSetDelegator )
536 :OComponentHelper( m_aMutex )
537 ,OPropertySetAggregationHelper( OComponentHelper::rBHelper )
538 ,m_xContext( _rxFactory )
539 ,m_lockCount( 0 )
540 ,m_aPropertyBagHelper( *this )
541 ,m_nTabIndex( FRM_DEFAULT_TABINDEX )
542 ,m_nClassId( FormComponentType::CONTROL )
544 DBG_ASSERT( _pOriginal, "OControlModel::OControlModel: invalid original!" );
546 // copy members
547 m_aName = _pOriginal->m_aName;
548 m_aTag = _pOriginal->m_aTag;
549 m_nTabIndex = _pOriginal->m_nTabIndex;
550 m_nClassId = _pOriginal->m_nClassId;
551 m_bNativeLook = _pOriginal->m_bNativeLook;
552 m_bStandardTheme = _pOriginal->m_bStandardTheme;
553 m_bGenerateVbEvents = _pOriginal->m_bGenerateVbEvents;
554 m_nControlTypeinMSO = _pOriginal->m_nControlTypeinMSO;
555 m_nObjIDinMSO = _pOriginal->m_nObjIDinMSO;
557 if ( !_bCloneAggregate )
558 return;
560 // temporarily increment refcount because of temporary references to ourself in the following
561 osl_atomic_increment( &m_refCount );
563 // transfer the (only, at the very moment!) ref count
564 m_xAggregate = createAggregateClone( _pOriginal );
566 // set aggregation (retrieve other direct interfaces of the aggregate)
567 setAggregation( m_xAggregate );
570 // set the delegator, if allowed by our derived class
571 if ( _bSetDelegator )
572 doSetDelegator();
574 // decrement ref count
575 osl_atomic_decrement( &m_refCount );
578 OControlModel::~OControlModel()
580 // release the aggregate
581 doResetDelegator( );
584 void OControlModel::clonedFrom( const OControlModel* /*_pOriginal*/ )
586 // nothing to do in this base class
589 void OControlModel::doResetDelegator()
591 if (m_xAggregate.is())
592 m_xAggregate->setDelegator(nullptr);
595 void OControlModel::doSetDelegator()
597 osl_atomic_increment(&m_refCount);
598 if (m_xAggregate.is())
600 m_xAggregate->setDelegator(static_cast<XWeak*>(this));
602 osl_atomic_decrement(&m_refCount);
605 // XChild
606 Reference< XInterface > SAL_CALL OControlModel::getParent()
608 return m_xParent;
611 void SAL_CALL OControlModel::setParent(const Reference< XInterface >& _rxParent)
613 osl::MutexGuard aGuard(m_aMutex);
615 Reference<XComponent> xComp(m_xParent, UNO_QUERY);
616 if (xComp.is())
617 xComp->removeEventListener(static_cast<XPropertiesChangeListener*>(this));
619 m_xParent = _rxParent;
620 xComp.set(m_xParent, css::uno::UNO_QUERY);
622 if ( xComp.is() )
623 xComp->addEventListener(static_cast<XPropertiesChangeListener*>(this));
626 // XNamed
627 OUString SAL_CALL OControlModel::getName()
629 OUString aReturn;
632 OPropertySetHelper::getFastPropertyValue(PROPERTY_ID_NAME) >>= aReturn;
634 catch (const css::beans::UnknownPropertyException&)
636 css::uno::Any a(cppu::getCaughtException());
637 throw WrappedTargetRuntimeException(
638 "OControlModel::getName",
639 *this,
643 return aReturn;
646 void SAL_CALL OControlModel::setName(const OUString& _rName)
650 setFastPropertyValue(PROPERTY_ID_NAME, Any(_rName));
652 catch (const css::beans::UnknownPropertyException&)
654 css::uno::Any a(cppu::getCaughtException());
655 throw WrappedTargetRuntimeException(
656 "OControlModel::setName",
657 *this,
663 // XServiceInfo
664 sal_Bool SAL_CALL OControlModel::supportsService(const OUString& _rServiceName)
666 return cppu::supportsService(this, _rServiceName);
669 Sequence< OUString > OControlModel::getAggregateServiceNames() const
671 Sequence< OUString > aAggServices;
672 Reference< XServiceInfo > xInfo;
673 if ( query_aggregation( m_xAggregate, xInfo ) )
674 aAggServices = xInfo->getSupportedServiceNames();
675 return aAggServices;
678 Sequence<OUString> SAL_CALL OControlModel::getSupportedServiceNames()
680 return ::comphelper::concatSequences(
681 getAggregateServiceNames(),
682 getSupportedServiceNames_Static()
686 Sequence< OUString > OControlModel::getSupportedServiceNames_Static()
688 return { FRM_SUN_FORMCOMPONENT, "com.sun.star.form.FormControlModel" };
691 // XEventListener
692 void SAL_CALL OControlModel::disposing(const css::lang::EventObject& _rSource)
694 // release the parent
695 if (_rSource.Source == m_xParent)
697 osl::MutexGuard aGuard(m_aMutex);
698 m_xParent = nullptr;
700 else
702 Reference<css::lang::XEventListener> xEvtLst;
703 if (query_aggregation(m_xAggregate, xEvtLst))
705 osl::MutexGuard aGuard(m_aMutex);
706 xEvtLst->disposing(_rSource);
711 // OComponentHelper
712 void OControlModel::disposing()
714 OPropertySetAggregationHelper::disposing();
716 Reference<css::lang::XComponent> xComp;
717 if (query_aggregation(m_xAggregate, xComp))
718 xComp->dispose();
720 setParent(Reference<XFormComponent>());
722 m_aPropertyBagHelper.dispose();
725 void OControlModel::writeAggregate( const Reference< XObjectOutputStream >& _rxOutStream ) const
727 Reference< XPersistObject > xPersist;
728 if ( query_aggregation( m_xAggregate, xPersist ) )
729 xPersist->write( _rxOutStream );
732 void OControlModel::readAggregate( const Reference< XObjectInputStream >& _rxInStream )
734 Reference< XPersistObject > xPersist;
735 if ( query_aggregation( m_xAggregate, xPersist ) )
736 xPersist->read( _rxInStream );
739 void SAL_CALL OControlModel::write(const Reference<css::io::XObjectOutputStream>& _rxOutStream)
741 osl::MutexGuard aGuard(m_aMutex);
743 // 1. writing the UnoControls
744 Reference<css::io::XMarkableStream> xMark(_rxOutStream, UNO_QUERY);
745 if ( !xMark.is() )
747 throw IOException(
748 ResourceManager::loadString(RID_STR_INVALIDSTREAM),
749 static_cast< ::cppu::OWeakObject* >( this )
753 sal_Int32 nMark = xMark->createMark();
754 sal_Int32 nLen = 0;
756 _rxOutStream->writeLong(nLen);
758 writeAggregate( _rxOutStream );
760 // determining the length
761 nLen = xMark->offsetToMark(nMark) - 4;
762 xMark->jumpToMark(nMark);
763 _rxOutStream->writeLong(nLen);
764 xMark->jumpToFurthest();
765 xMark->deleteMark(nMark);
767 // 2. writing a version number
768 _rxOutStream->writeShort(0x0003);
770 // 3. writing the general properties
771 ::comphelper::operator<<( _rxOutStream, m_aName);
772 _rxOutStream->writeShort(m_nTabIndex);
773 ::comphelper::operator<<( _rxOutStream, m_aTag); // 3rd version
775 // IMPORTANT NOTE!
776 // don't write any new members here: this wouldn't be compatible with older versions, as OControlModel
777 // is a base class which is called in derived classes "read" method. So if you increment the version
778 // and write new stuff, older office versions will read this in the _derived_ classes, which may result
779 // in anything from data loss to crash.
780 // EOIN!
783 void OControlModel::read(const Reference<css::io::XObjectInputStream>& InStream)
785 osl::MutexGuard aGuard(m_aMutex);
787 Reference<css::io::XMarkableStream> xMark(InStream, UNO_QUERY);
788 if ( !xMark.is() )
790 throw IOException(
791 ResourceManager::loadString(RID_STR_INVALIDSTREAM),
792 static_cast< ::cppu::OWeakObject* >( this )
796 // 1. reading the UnoControls
797 sal_Int32 nLen = InStream->readLong();
798 if (nLen)
800 sal_Int32 nMark = xMark->createMark();
804 readAggregate( InStream );
807 catch( const Exception& )
809 DBG_UNHANDLED_EXCEPTION("forms.component");
812 xMark->jumpToMark(nMark);
813 InStream->skipBytes(nLen);
814 xMark->deleteMark(nMark);
817 // 2. reading the version number
818 sal_uInt16 nVersion = InStream->readShort();
820 // 3. reading the general properties
821 ::comphelper::operator>>( InStream, m_aName);
822 m_nTabIndex = InStream->readShort();
824 if (nVersion > 0x0002)
825 ::comphelper::operator>>( InStream, m_aTag);
827 // we had a version where we wrote the help text
828 if (nVersion == 0x0004)
829 readHelpTextCompatibly(InStream);
831 DBG_ASSERT(nVersion < 5, "OControlModel::read : suspicious version number !");
832 // 4 was the version where we wrote the help text
833 // later versions shouldn't exist (see write for a detailed comment)
836 PropertyState OControlModel::getPropertyStateByHandle( sal_Int32 _nHandle )
838 // simply compare the current and the default value
839 Any aCurrentValue = getPropertyDefaultByHandle( _nHandle );
840 Any aDefaultValue; getFastPropertyValue( aDefaultValue, _nHandle );
842 bool bEqual = aCurrentValue == aDefaultValue;
843 return bEqual ? PropertyState_DEFAULT_VALUE : PropertyState_DIRECT_VALUE;
846 void OControlModel::setPropertyToDefaultByHandle( sal_Int32 _nHandle)
848 Any aDefault = getPropertyDefaultByHandle( _nHandle );
850 Any aConvertedValue, aOldValue;
851 if ( convertFastPropertyValue( aConvertedValue, aOldValue, _nHandle, aDefault ) )
853 setFastPropertyValue_NoBroadcast( _nHandle, aConvertedValue );
854 // TODO: fire the property change
858 Any OControlModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
860 Any aReturn;
861 switch ( _nHandle )
863 case PROPERTY_ID_NAME:
864 case PROPERTY_ID_TAG:
865 aReturn <<= OUString();
866 break;
867 case PROPERTY_ID_CLASSID:
868 aReturn <<= sal_Int16(FormComponentType::CONTROL);
869 break;
870 case PROPERTY_ID_TABINDEX:
871 aReturn <<= sal_Int16(FRM_DEFAULT_TABINDEX);
872 break;
873 case PROPERTY_ID_NATIVE_LOOK:
874 aReturn <<= true;
875 break;
876 case PROPERTY_ID_STANDARD_THEME:
877 aReturn <<= false;
878 break;
879 case PROPERTY_ID_GENERATEVBAEVENTS:
880 aReturn <<= false;
881 break;
882 // added for exporting OCX control
883 case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
884 aReturn <<= sal_Int16(0);
885 break;
886 case PROPERTY_ID_OBJ_ID_IN_MSO:
887 aReturn <<= sal_uInt16(INVALID_OBJ_ID_IN_MSO);
888 break;
889 default:
890 if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
891 m_aPropertyBagHelper.getDynamicPropertyDefaultByHandle( _nHandle, aReturn );
892 else
893 SAL_WARN("forms.component", "OControlModel::convertFastPropertyValue: unknown handle " << _nHandle);
895 return aReturn;
898 void OControlModel::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
900 switch ( _nHandle )
902 case PROPERTY_ID_NAME:
903 _rValue <<= m_aName;
904 break;
905 case PROPERTY_ID_TAG:
906 _rValue <<= m_aTag;
907 break;
908 case PROPERTY_ID_CLASSID:
909 _rValue <<= m_nClassId;
910 break;
911 case PROPERTY_ID_TABINDEX:
912 _rValue <<= m_nTabIndex;
913 break;
914 case PROPERTY_ID_NATIVE_LOOK:
915 _rValue <<= m_bNativeLook;
916 break;
917 case PROPERTY_ID_STANDARD_THEME:
918 _rValue <<= m_bStandardTheme;
919 break;
920 case PROPERTY_ID_GENERATEVBAEVENTS:
921 _rValue <<= m_bGenerateVbEvents;
922 break;
923 // added for exporting OCX control
924 case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
925 _rValue <<= m_nControlTypeinMSO;
926 break;
927 case PROPERTY_ID_OBJ_ID_IN_MSO:
928 _rValue <<= m_nObjIDinMSO;
929 break;
930 default:
931 if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
932 m_aPropertyBagHelper.getDynamicFastPropertyValue( _nHandle, _rValue );
933 else
934 OPropertySetAggregationHelper::getFastPropertyValue( _rValue, _nHandle );
935 break;
939 sal_Bool OControlModel::convertFastPropertyValue(
940 Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue)
942 bool bModified(false);
943 switch (_nHandle)
945 case PROPERTY_ID_NAME:
946 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aName);
947 break;
948 case PROPERTY_ID_TAG:
949 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aTag);
950 break;
951 case PROPERTY_ID_TABINDEX:
952 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_nTabIndex);
953 break;
954 case PROPERTY_ID_NATIVE_LOOK:
955 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bNativeLook);
956 break;
957 case PROPERTY_ID_STANDARD_THEME:
958 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bStandardTheme);
959 break;
960 case PROPERTY_ID_GENERATEVBAEVENTS:
961 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bGenerateVbEvents);
962 break;
963 // added for exporting OCX control
964 case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
965 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_nControlTypeinMSO);
966 break;
967 case PROPERTY_ID_OBJ_ID_IN_MSO:
968 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_nObjIDinMSO);
969 break;
970 default:
971 if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
972 bModified = m_aPropertyBagHelper.convertDynamicFastPropertyValue( _nHandle, _rValue, _rConvertedValue, _rOldValue );
973 else
974 SAL_WARN("forms.component", "OControlModel::convertFastPropertyValue: unknown handle " << _nHandle);
975 break;
977 return bModified;
980 void OControlModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
982 switch (_nHandle)
984 case PROPERTY_ID_NAME:
985 DBG_ASSERT(_rValue.getValueType() == cppu::UnoType<OUString>::get(),
986 "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" );
987 _rValue >>= m_aName;
988 break;
989 case PROPERTY_ID_TAG:
990 DBG_ASSERT(_rValue.getValueType() == cppu::UnoType<OUString>::get(),
991 "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" );
992 _rValue >>= m_aTag;
993 break;
994 case PROPERTY_ID_TABINDEX:
995 DBG_ASSERT(_rValue.getValueType() == cppu::UnoType<sal_Int16>::get(),
996 "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" );
997 _rValue >>= m_nTabIndex;
998 break;
999 case PROPERTY_ID_NATIVE_LOOK:
1000 OSL_VERIFY( _rValue >>= m_bNativeLook );
1001 break;
1002 case PROPERTY_ID_STANDARD_THEME:
1003 OSL_VERIFY( _rValue >>= m_bStandardTheme );
1004 break;
1005 case PROPERTY_ID_GENERATEVBAEVENTS:
1006 OSL_VERIFY( _rValue >>= m_bGenerateVbEvents );
1007 break;
1008 // added for exporting OCX control
1009 case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
1010 OSL_VERIFY( _rValue >>= m_nControlTypeinMSO );
1011 break;
1012 case PROPERTY_ID_OBJ_ID_IN_MSO:
1013 OSL_VERIFY( _rValue >>= m_nObjIDinMSO );
1014 break;
1015 default:
1016 if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
1017 m_aPropertyBagHelper.setDynamicFastPropertyValue( _nHandle, _rValue );
1018 else
1019 SAL_WARN("forms.component", "OControlModel::setFastPropertyValue_NoBroadcast: unknown handle " << _nHandle );
1020 break;
1024 void OControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const
1026 _rProps.realloc(8);
1027 css::beans::Property* pProperties = _rProps.getArray();
1028 *pProperties++ = css::beans::Property(PROPERTY_CLASSID, PROPERTY_ID_CLASSID, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
1029 *pProperties++ = css::beans::Property(PROPERTY_NAME, PROPERTY_ID_NAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
1030 *pProperties++ = css::beans::Property(PROPERTY_NATIVE_LOOK, PROPERTY_ID_NATIVE_LOOK, cppu::UnoType<bool>::get(),
1031 css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT);
1032 *pProperties++ = css::beans::Property(PROPERTY_STANDARD_THEME, PROPERTY_ID_STANDARD_THEME, cppu::UnoType<bool>::get(),
1033 css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT);
1034 *pProperties++ = css::beans::Property(PROPERTY_TAG, PROPERTY_ID_TAG, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
1035 *pProperties++ = css::beans::Property(PROPERTY_GENERATEVBAEVENTS, PROPERTY_ID_GENERATEVBAEVENTS, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::TRANSIENT);
1036 *pProperties++ = css::beans::Property(PROPERTY_CONTROL_TYPE_IN_MSO, PROPERTY_ID_CONTROL_TYPE_IN_MSO, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
1037 *pProperties++ = css::beans::Property(PROPERTY_OBJ_ID_IN_MSO, PROPERTY_ID_OBJ_ID_IN_MSO, cppu::UnoType<cppu::UnoUnsignedShortType>::get(), css::beans::PropertyAttribute::BOUND);
1038 DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
1041 void OControlModel::describeAggregateProperties( Sequence< Property >& /* [out] */ _rAggregateProps ) const
1043 if ( m_xAggregateSet.is() )
1045 Reference< XPropertySetInfo > xPSI( m_xAggregateSet->getPropertySetInfo() );
1046 if ( xPSI.is() )
1047 _rAggregateProps = xPSI->getProperties();
1051 ::osl::Mutex& OControlModel::getMutex()
1053 return m_aMutex;
1056 void OControlModel::describeFixedAndAggregateProperties( Sequence< Property >& _out_rFixedProperties, Sequence< Property >& _out_rAggregateProperties ) const
1058 describeFixedProperties( _out_rFixedProperties );
1059 describeAggregateProperties( _out_rAggregateProperties );
1062 Reference< XMultiPropertySet > OControlModel::getPropertiesInterface()
1064 return this;
1067 Reference< XPropertySetInfo> SAL_CALL OControlModel::getPropertySetInfo()
1069 return createPropertySetInfo( getInfoHelper() );
1072 ::cppu::IPropertyArrayHelper& OControlModel::getInfoHelper()
1074 return m_aPropertyBagHelper.getInfoHelper();
1077 void SAL_CALL OControlModel::addProperty( const OUString& _rName, ::sal_Int16 _nAttributes, const Any& _rInitialValue )
1079 m_aPropertyBagHelper.addProperty( _rName, _nAttributes, _rInitialValue );
1082 void SAL_CALL OControlModel::removeProperty( const OUString& _rName )
1084 m_aPropertyBagHelper.removeProperty( _rName );
1087 Sequence< PropertyValue > SAL_CALL OControlModel::getPropertyValues()
1089 return m_aPropertyBagHelper.getPropertyValues();
1092 void SAL_CALL OControlModel::setPropertyValues( const Sequence< PropertyValue >& _rProps )
1094 m_aPropertyBagHelper.setPropertyValues( _rProps );
1097 void OControlModel::lockInstance( LockAccess )
1099 m_aMutex.acquire();
1100 osl_atomic_increment( &m_lockCount );
1103 oslInterlockedCount OControlModel::unlockInstance( LockAccess )
1105 OSL_ENSURE( m_lockCount > 0, "OControlModel::unlockInstance: not locked!" );
1106 oslInterlockedCount lockCount = osl_atomic_decrement( &m_lockCount );
1107 m_aMutex.release();
1108 return lockCount;
1111 void OControlModel::firePropertyChanges( const std::vector< sal_Int32 >& _rHandles, const std::vector< Any >& _rOldValues,
1112 const std::vector< Any >& _rNewValues, LockAccess )
1114 OPropertySetHelper::fire(
1115 const_cast< std::vector< sal_Int32 >& >( _rHandles ).data(),
1116 _rNewValues.data(),
1117 _rOldValues.data(),
1118 _rHandles.size(),
1119 false
1123 // OBoundControlModel
1124 Any SAL_CALL OBoundControlModel::queryAggregation( const Type& _rType )
1126 Any aReturn( OControlModel::queryAggregation(_rType) );
1127 if (!aReturn.hasValue())
1129 aReturn = OBoundControlModel_BASE1::queryInterface(_rType);
1131 if ( !aReturn.hasValue() && m_bCommitable )
1132 aReturn = OBoundControlModel_COMMITTING::queryInterface( _rType );
1134 if ( !aReturn.hasValue() && m_bSupportsExternalBinding )
1135 aReturn = OBoundControlModel_BINDING::queryInterface( _rType );
1137 if ( !aReturn.hasValue() && m_bSupportsValidation )
1138 aReturn = OBoundControlModel_VALIDATION::queryInterface( _rType );
1140 return aReturn;
1143 OBoundControlModel::OBoundControlModel(
1144 const Reference< XComponentContext>& _rxFactory,
1145 const OUString& _rUnoControlModelTypeName, const OUString& _rDefault,
1146 const bool _bCommitable, const bool _bSupportExternalBinding, const bool _bSupportsValidation )
1147 :OControlModel( _rxFactory, _rUnoControlModelTypeName, _rDefault, false )
1148 ,OPropertyChangeListener( m_aMutex )
1149 ,m_nValuePropertyAggregateHandle( -1 )
1150 ,m_nFieldType( DataType::OTHER )
1151 ,m_bValuePropertyMayBeVoid( false )
1152 ,m_aResetHelper( *this, m_aMutex )
1153 ,m_aUpdateListeners(m_aMutex)
1154 ,m_aFormComponentListeners( m_aMutex )
1155 ,m_bInputRequired( false )
1156 ,m_bFormListening( false )
1157 ,m_bLoaded(false)
1158 ,m_bRequired(false)
1159 ,m_bCommitable(_bCommitable)
1160 ,m_bSupportsExternalBinding( _bSupportExternalBinding )
1161 ,m_bSupportsValidation( _bSupportsValidation )
1162 ,m_bForwardValueChanges(true)
1163 ,m_bTransferringValue( false )
1164 ,m_bIsCurrentValueValid( true )
1165 ,m_bBindingControlsRO( false )
1166 ,m_bBindingControlsEnable( false )
1167 ,m_eControlValueChangeInstigator( eOther )
1168 ,m_aLabelServiceName(FRM_SUN_COMPONENT_FIXEDTEXT)
1170 // start property listening at the aggregate
1171 implInitAggMultiplexer( );
1174 OBoundControlModel::OBoundControlModel(
1175 const OBoundControlModel* _pOriginal, const Reference< XComponentContext>& _rxFactory )
1176 :OControlModel( _pOriginal, _rxFactory, true, false )
1177 ,OPropertyChangeListener( m_aMutex )
1178 ,m_nValuePropertyAggregateHandle( _pOriginal->m_nValuePropertyAggregateHandle )
1179 ,m_nFieldType( DataType::OTHER )
1180 ,m_bValuePropertyMayBeVoid( _pOriginal->m_bValuePropertyMayBeVoid )
1181 ,m_aResetHelper( *this, m_aMutex )
1182 ,m_aUpdateListeners( m_aMutex )
1183 ,m_aFormComponentListeners( m_aMutex )
1184 ,m_xValidator( _pOriginal->m_xValidator )
1185 ,m_bInputRequired( false )
1186 ,m_bFormListening( false )
1187 ,m_bLoaded( false )
1188 ,m_bRequired( false )
1189 ,m_bCommitable( _pOriginal->m_bCommitable )
1190 ,m_bSupportsExternalBinding( _pOriginal->m_bSupportsExternalBinding )
1191 ,m_bSupportsValidation( _pOriginal->m_bSupportsValidation )
1192 ,m_bForwardValueChanges( true )
1193 ,m_bTransferringValue( false )
1194 ,m_bIsCurrentValueValid( _pOriginal->m_bIsCurrentValueValid )
1195 ,m_bBindingControlsRO( false )
1196 ,m_bBindingControlsEnable( false )
1197 ,m_eControlValueChangeInstigator( eOther )
1199 // start property listening at the aggregate
1200 implInitAggMultiplexer( );
1201 m_aLabelServiceName = _pOriginal->m_aLabelServiceName;
1202 m_sValuePropertyName = _pOriginal->m_sValuePropertyName;
1203 m_nValuePropertyAggregateHandle = _pOriginal->m_nValuePropertyAggregateHandle;
1204 m_bValuePropertyMayBeVoid = _pOriginal->m_bValuePropertyMayBeVoid;
1205 m_aValuePropertyType = _pOriginal->m_aValuePropertyType;
1206 m_aControlSource = _pOriginal->m_aControlSource;
1207 m_bInputRequired = _pOriginal->m_bInputRequired;
1208 // m_xLabelControl, though being a property, is not to be cloned, not even the reference will be transferred.
1209 // (the former should be clear - a clone of the object we're only referencing does not make sense)
1210 // (the second would violate the restriction for label controls that they're part of the
1211 // same form component hierarchy - we ourself are no part, yet, so we can't have a label control)
1212 // start listening for changes at the value property
1213 implInitValuePropertyListening( );
1216 OBoundControlModel::~OBoundControlModel()
1218 if ( !OComponentHelper::rBHelper.bDisposed )
1220 acquire();
1221 dispose();
1224 doResetDelegator( );
1225 OSL_ENSURE( m_pAggPropMultiplexer, "OBoundControlModel::~OBoundControlModel: what about my property multiplexer?" );
1226 if ( m_pAggPropMultiplexer )
1228 m_pAggPropMultiplexer->dispose();
1229 m_pAggPropMultiplexer = nullptr;
1233 void OBoundControlModel::clonedFrom( const OControlModel* _pOriginal )
1235 const OBoundControlModel* pBoundOriginal = static_cast< const OBoundControlModel* >( _pOriginal );
1236 // the value binding can be handled as if somebody called setValueBinding here
1237 // By definition, bindings can be share between bindables
1238 if ( !(pBoundOriginal && pBoundOriginal->m_xExternalBinding.is()) )
1239 return;
1243 setValueBinding( pBoundOriginal->m_xExternalBinding );
1246 catch( const Exception& )
1248 DBG_UNHANDLED_EXCEPTION("forms.component");
1252 void OBoundControlModel::implInitAggMultiplexer( )
1254 osl_atomic_increment( &m_refCount );
1255 if ( m_xAggregateSet.is() )
1257 m_pAggPropMultiplexer = new OPropertyChangeMultiplexer( this, m_xAggregateSet, false );
1260 osl_atomic_decrement( &m_refCount );
1261 doSetDelegator();
1264 void OBoundControlModel::implInitValuePropertyListening( ) const
1266 // start listening for changes at the value property
1267 // There are three pre-requisites for this to be done:
1268 // 1. We support external value bindings. In this case, the changes in the control value need to
1269 // be propagated to the external binding immediately when they happen
1270 // 2. We support external validation. In this case, we need to listen for changes in the value
1271 // property, since we need to revalidate then.
1272 // 3. We are not committable. In this case, changes in the control value need to be propagated
1273 // to the database column immediately when they happen.
1274 if ( m_bSupportsExternalBinding || m_bSupportsValidation || !m_bCommitable )
1276 OSL_ENSURE( m_pAggPropMultiplexer, "OBoundControlModel::implInitValuePropertyListening: no multiplexer!" );
1277 if ( m_pAggPropMultiplexer && !m_sValuePropertyName.isEmpty() )
1278 m_pAggPropMultiplexer->addProperty( m_sValuePropertyName );
1282 void OBoundControlModel::initOwnValueProperty( const OUString& i_rValuePropertyName )
1284 OSL_PRECOND( m_sValuePropertyName.isEmpty() && -1 == m_nValuePropertyAggregateHandle,
1285 "OBoundControlModel::initOwnValueProperty: value property is already initialized!" );
1286 OSL_ENSURE( !i_rValuePropertyName.isEmpty(), "OBoundControlModel::initOwnValueProperty: invalid property name!" );
1287 m_sValuePropertyName = i_rValuePropertyName;
1290 void OBoundControlModel::initValueProperty( const OUString& _rValuePropertyName, sal_Int32 _nValuePropertyExternalHandle )
1292 OSL_PRECOND( m_sValuePropertyName.isEmpty() && -1 == m_nValuePropertyAggregateHandle,
1293 "OBoundControlModel::initValueProperty: value property is already initialized!" );
1294 OSL_ENSURE( !_rValuePropertyName.isEmpty(), "OBoundControlModel::initValueProperty: invalid property name!" );
1295 OSL_ENSURE( _nValuePropertyExternalHandle != -1, "OBoundControlModel::initValueProperty: invalid property handle!" );
1297 m_sValuePropertyName = _rValuePropertyName;
1298 m_nValuePropertyAggregateHandle = getOriginalHandle( _nValuePropertyExternalHandle );
1299 OSL_ENSURE( m_nValuePropertyAggregateHandle != -1, "OBoundControlModel::initValueProperty: unable to find the original handle!" );
1301 if ( m_nValuePropertyAggregateHandle != -1 )
1303 Reference< XPropertySetInfo > xPropInfo( m_xAggregateSet->getPropertySetInfo(), UNO_SET_THROW );
1304 Property aValuePropDesc = xPropInfo->getPropertyByName( m_sValuePropertyName );
1305 m_aValuePropertyType = aValuePropDesc.Type;
1306 m_bValuePropertyMayBeVoid = ( aValuePropDesc.Attributes & PropertyAttribute::MAYBEVOID ) != 0;
1309 // start listening for changes at the value property
1310 implInitValuePropertyListening( );
1313 void OBoundControlModel::suspendValueListening( )
1315 OSL_PRECOND( !m_sValuePropertyName.isEmpty(), "OBoundControlModel::suspendValueListening: don't have a value property!" );
1316 OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::suspendValueListening: I *am* not listening!" );
1318 if ( m_pAggPropMultiplexer )
1319 m_pAggPropMultiplexer->lock();
1322 void OBoundControlModel::resumeValueListening( )
1324 OSL_PRECOND( !m_sValuePropertyName.isEmpty(), "OBoundControlModel::resumeValueListening: don't have a value property!" );
1325 OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::resumeValueListening: I *am* not listening at all!" );
1326 OSL_PRECOND( !m_pAggPropMultiplexer || m_pAggPropMultiplexer->locked(), "OBoundControlModel::resumeValueListening: listening not suspended currently!" );
1327 if ( m_pAggPropMultiplexer )
1328 m_pAggPropMultiplexer->unlock();
1331 Sequence< Type > OBoundControlModel::_getTypes()
1333 TypeBag aTypes(
1334 OControlModel::_getTypes(),
1335 OBoundControlModel_BASE1::getTypes()
1338 if ( m_bCommitable )
1339 aTypes.addTypes( OBoundControlModel_COMMITTING::getTypes() );
1341 if ( m_bSupportsExternalBinding )
1342 aTypes.addTypes( OBoundControlModel_BINDING::getTypes() );
1344 if ( m_bSupportsValidation )
1345 aTypes.addTypes( OBoundControlModel_VALIDATION::getTypes() );
1346 return aTypes.getTypes();
1349 // OComponentHelper
1350 void OBoundControlModel::disposing()
1352 OControlModel::disposing();
1354 osl::MutexGuard aGuard(m_aMutex);
1356 if ( m_pAggPropMultiplexer )
1357 m_pAggPropMultiplexer->dispose();
1359 // notify all our listeners
1360 css::lang::EventObject aEvt( static_cast< XWeak* >( this ) );
1361 m_aUpdateListeners.disposeAndClear( aEvt );
1362 m_aResetHelper.disposing();
1364 // disconnect from our database column
1365 // TODO: could we replace the following 5 lines with a call to impl_disconnectDatabaseColumn_noNotify?
1366 // The only more thing which it does is calling onDisconnectedDbColumn - could this
1367 // cause trouble? At least when we continue to call OControlModel::disposing before, it *may*.
1368 if ( hasField() )
1370 getField()->removePropertyChangeListener( PROPERTY_VALUE, this );
1371 resetField();
1374 m_xCursor = nullptr;
1375 Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY );
1376 if ( xComp.is() )
1377 xComp->removeEventListener(static_cast< XEventListener* >( static_cast< XPropertyChangeListener* >( this ) ) );
1378 // disconnect from our external value binding
1379 if ( hasExternalValueBinding() )
1380 disconnectExternalValueBinding();
1381 // ditto for the validator
1382 if ( hasValidator() )
1383 disconnectValidator( );
1386 void OBoundControlModel::onValuePropertyChange( ControlModelLock& i_rControLock )
1388 if ( hasExternalValueBinding() )
1390 // the control value changed, while we have an external value binding
1391 // -> forward the value to it
1392 if ( m_eControlValueChangeInstigator != eExternalBinding )
1393 transferControlValueToExternal( i_rControLock );
1396 else if ( !m_bCommitable && m_xColumnUpdate.is() )
1398 // the control value changed, while we are bound to a database column,
1399 // but not committable (which means changes in the control have to be reflected to
1400 // the underlying database column immediately)
1401 // -> forward the value to the database column
1402 if ( m_eControlValueChangeInstigator != eDbColumnBinding )
1403 commitControlValueToDbColumn( false );
1406 // validate the new value
1407 if ( m_bSupportsValidation )
1408 recheckValidity( true );
1411 void OBoundControlModel::_propertyChanged( const PropertyChangeEvent& _rEvt )
1413 ControlModelLock aLock( *this );
1414 OSL_ENSURE( _rEvt.PropertyName == m_sValuePropertyName,
1415 "OBoundControlModel::_propertyChanged: where did this come from (1)?" );
1416 OSL_ENSURE( m_pAggPropMultiplexer && !m_pAggPropMultiplexer->locked(),
1417 "OBoundControlModel::_propertyChanged: where did this come from (2)?" );
1418 if ( _rEvt.PropertyName == m_sValuePropertyName )
1420 onValuePropertyChange( aLock );
1424 void OBoundControlModel::startAggregatePropertyListening( const OUString& _rPropertyName )
1426 OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::startAggregatePropertyListening: no multiplexer!" );
1427 OSL_ENSURE( !_rPropertyName.isEmpty(), "OBoundControlModel::startAggregatePropertyListening: invalid property name!" );
1428 if ( m_pAggPropMultiplexer && !_rPropertyName.isEmpty() )
1430 m_pAggPropMultiplexer->addProperty( _rPropertyName );
1434 void OBoundControlModel::doFormListening( const bool _bStart )
1436 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::doFormListening: external value binding should overrule the database binding!" );
1437 if ( isFormListening() == _bStart )
1438 return;
1439 if ( m_xAmbientForm.is() )
1440 _bStart ? m_xAmbientForm->addLoadListener( this ) : m_xAmbientForm->removeLoadListener( this );
1441 Reference< XLoadable > xParentLoadable( getParent(), UNO_QUERY );
1442 if ( getParent().is() && !xParentLoadable.is() )
1444 // if our parent does not directly support the XLoadable interface, then it might support the
1445 // XRowSetSupplier/XRowSetChangeBroadcaster interfaces. In this case we have to listen for changes
1446 // broadcasted by the latter.
1447 Reference< XRowSetChangeBroadcaster > xRowSetBroadcaster( getParent(), UNO_QUERY );
1448 if ( xRowSetBroadcaster.is() )
1449 _bStart ? xRowSetBroadcaster->addRowSetChangeListener( this ) : xRowSetBroadcaster->removeRowSetChangeListener( this );
1452 m_bFormListening = _bStart && m_xAmbientForm.is();
1455 // XChild
1456 void SAL_CALL OBoundControlModel::setParent(const Reference<XInterface>& _rxParent)
1458 ControlModelLock aLock( *this );
1459 FieldChangeNotifier aBoundFieldNotifier( aLock );
1460 if ( getParent() == _rxParent )
1461 return;
1462 // disconnect from database column (which is controlled by parent, directly or indirectly)
1463 if ( hasField() )
1464 impl_disconnectDatabaseColumn_noNotify();
1465 // log off old listeners
1466 if ( isFormListening() )
1467 doFormListening( false );
1468 // actually set the new parent
1469 OControlModel::setParent( _rxParent );
1470 // a new parent means a new ambient form
1471 impl_determineAmbientForm_nothrow();
1472 if ( !hasExternalValueBinding() )
1474 // log on new listeners
1475 doFormListening( true );
1476 // re-connect to database column of the new parent
1477 if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() )
1478 impl_connectDatabaseColumn_noNotify( false );
1482 // XEventListener
1483 void SAL_CALL OBoundControlModel::disposing(const css::lang::EventObject& _rEvent)
1485 ControlModelLock aLock( *this );
1486 if ( _rEvent.Source == getField() )
1488 resetField();
1491 else if ( _rEvent.Source == m_xLabelControl )
1493 Reference<XPropertySet> xOldValue = m_xLabelControl;
1494 m_xLabelControl = nullptr;
1495 // fire a propertyChanged (when we leave aLock's scope)
1496 aLock.addPropertyNotification( PROPERTY_ID_CONTROLLABEL, Any( xOldValue ), Any( m_xLabelControl ) );
1499 else if ( _rEvent.Source == m_xExternalBinding )
1500 { // *first* check for the external binding
1501 disconnectExternalValueBinding( );
1504 else if ( _rEvent.Source == m_xValidator )
1505 { // *then* check for the validator. Reason is that bindings may also act as validator at the same
1506 // time, in this case, the validator is automatically revoked when the binding is revoked
1507 disconnectValidator( );
1510 else
1511 OControlModel::disposing(_rEvent);
1514 // XServiceInfo
1515 css::uno::Sequence<OUString> SAL_CALL OBoundControlModel::getSupportedServiceNames()
1517 return ::comphelper::combineSequences(
1518 getAggregateServiceNames(),
1519 getSupportedServiceNames_Static()
1523 Sequence< OUString > OBoundControlModel::getSupportedServiceNames_Static()
1525 Sequence<OUString> aOwnServiceNames { "com.sun.star.form.DataAwareControlModel" };
1526 return ::comphelper::concatSequences(
1527 OControlModel::getSupportedServiceNames_Static(),
1528 aOwnServiceNames
1532 // XPersist
1533 void SAL_CALL OBoundControlModel::write( const Reference<css::io::XObjectOutputStream>& _rxOutStream )
1535 OControlModel::write(_rxOutStream);
1536 osl::MutexGuard aGuard(m_aMutex);
1537 // Version
1538 _rxOutStream->writeShort(0x0002);
1539 // Controlsource
1540 ::comphelper::operator<<( _rxOutStream, m_aControlSource);
1541 // !!! IMPORTANT NOTE !!!
1542 // don't write any new members here: this wouldn't be compatible with older versions, as OBoundControlModel
1543 // is a base class which is called in derived classes "read" method. So if you increment the version
1544 // and write new stuff, older office versions will read this in the _derived_ classes, which may result
1545 // in anything from data loss to crash.
1546 // (use writeCommonProperties instead, this is called in derived classes write-method)
1547 // !!! EOIN !!!
1550 void OBoundControlModel::defaultCommonProperties()
1552 Reference<css::lang::XComponent> xComp(m_xLabelControl, UNO_QUERY);
1553 if (xComp.is())
1554 xComp->removeEventListener(static_cast<css::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
1555 m_xLabelControl = nullptr;
1558 void OBoundControlModel::readCommonProperties(const Reference<css::io::XObjectInputStream>& _rxInStream)
1560 sal_Int32 nLen = _rxInStream->readLong();
1561 Reference<css::io::XMarkableStream> xMark(_rxInStream, UNO_QUERY);
1562 DBG_ASSERT(xMark.is(), "OBoundControlModel::readCommonProperties : can only work with markable streams !");
1563 sal_Int32 nMark = xMark->createMark();
1564 // read the reference to the label control
1565 Reference<css::io::XPersistObject> xPersist;
1566 sal_Int32 nUsedFlag;
1567 nUsedFlag = _rxInStream->readLong();
1568 if (nUsedFlag)
1569 xPersist = _rxInStream->readObject();
1570 m_xLabelControl.set(xPersist, css::uno::UNO_QUERY);
1571 Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY );
1572 if (xComp.is())
1573 xComp->addEventListener(static_cast<css::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
1574 // read any other new common properties here
1575 // skip the remaining bytes
1576 xMark->jumpToMark(nMark);
1577 _rxInStream->skipBytes(nLen);
1578 xMark->deleteMark(nMark);
1581 void OBoundControlModel::writeCommonProperties(const Reference<css::io::XObjectOutputStream>& _rxOutStream)
1583 Reference<css::io::XMarkableStream> xMark(_rxOutStream, UNO_QUERY);
1584 DBG_ASSERT(xMark.is(), "OBoundControlModel::writeCommonProperties : can only work with markable streams !");
1585 sal_Int32 nMark = xMark->createMark();
1586 // a placeholder where we will write the overall length (later in this method)
1587 sal_Int32 nLen = 0;
1588 _rxOutStream->writeLong(nLen);
1589 // write the reference to the label control
1590 Reference<css::io::XPersistObject> xPersist(m_xLabelControl, UNO_QUERY);
1591 sal_Int32 nUsedFlag = 0;
1592 if (xPersist.is())
1593 nUsedFlag = 1;
1594 _rxOutStream->writeLong(nUsedFlag);
1595 if (xPersist.is())
1596 _rxOutStream->writeObject(xPersist);
1597 // write any other new common properties here
1598 // write the correct length at the beginning of the block
1599 nLen = xMark->offsetToMark(nMark) - sizeof(nLen);
1600 xMark->jumpToMark(nMark);
1601 _rxOutStream->writeLong(nLen);
1602 xMark->jumpToFurthest();
1603 xMark->deleteMark(nMark);
1606 void SAL_CALL OBoundControlModel::read( const Reference< css::io::XObjectInputStream >& _rxInStream )
1608 OControlModel::read(_rxInStream);
1609 osl::MutexGuard aGuard(m_aMutex);
1610 _rxInStream->readShort(); // version;
1611 ::comphelper::operator>>( _rxInStream, m_aControlSource);
1614 void OBoundControlModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const
1616 switch (nHandle)
1618 case PROPERTY_ID_INPUT_REQUIRED:
1619 rValue <<= m_bInputRequired;
1620 break;
1621 case PROPERTY_ID_CONTROLSOURCEPROPERTY:
1622 rValue <<= m_sValuePropertyName;
1623 break;
1624 case PROPERTY_ID_CONTROLSOURCE:
1625 rValue <<= m_aControlSource;
1626 break;
1627 case PROPERTY_ID_BOUNDFIELD:
1628 rValue <<= getField();
1629 break;
1630 case PROPERTY_ID_CONTROLLABEL:
1631 if (!m_xLabelControl.is())
1632 rValue.clear();
1633 else
1634 rValue <<= m_xLabelControl;
1635 break;
1636 default:
1637 OControlModel::getFastPropertyValue(rValue, nHandle);
1641 sal_Bool OBoundControlModel::convertFastPropertyValue(
1642 Any& _rConvertedValue, Any& _rOldValue,
1643 sal_Int32 _nHandle,
1644 const Any& _rValue)
1646 bool bModified(false);
1647 switch (_nHandle)
1649 case PROPERTY_ID_INPUT_REQUIRED:
1650 bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_bInputRequired );
1651 break;
1652 case PROPERTY_ID_CONTROLSOURCE:
1653 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aControlSource);
1654 break;
1655 case PROPERTY_ID_BOUNDFIELD:
1656 SAL_WARN("forms.component", "OBoundControlModel::convertFastPropertyValue: BoundField should be a read-only property !" );
1657 throw css::lang::IllegalArgumentException();
1658 case PROPERTY_ID_CONTROLLABEL:
1659 if (!_rValue.hasValue())
1660 { // property set to void
1661 _rConvertedValue = Any();
1662 getFastPropertyValue(_rOldValue, _nHandle);
1663 bModified = m_xLabelControl.is();
1666 else
1668 bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_xLabelControl);
1669 if (!m_xLabelControl.is())
1670 // an empty interface is interpreted as VOID
1671 _rOldValue.clear();
1674 break;
1675 default:
1676 bModified = OControlModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue);
1678 return bModified;
1681 Any OBoundControlModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
1683 Any aDefault;
1684 switch ( _nHandle )
1686 case PROPERTY_ID_INPUT_REQUIRED:
1687 aDefault <<= false;
1688 break;
1689 case PROPERTY_ID_CONTROLSOURCE:
1690 aDefault <<= OUString();
1691 break;
1692 case PROPERTY_ID_CONTROLLABEL:
1693 aDefault <<= Reference< XPropertySet >();
1694 break;
1696 return aDefault;
1699 void OBoundControlModel::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
1701 switch (nHandle)
1703 case PROPERTY_ID_INPUT_REQUIRED:
1704 OSL_VERIFY( rValue >>= m_bInputRequired );
1705 break;
1706 case PROPERTY_ID_CONTROLSOURCE:
1707 OSL_VERIFY( rValue >>= m_aControlSource );
1708 break;
1709 case PROPERTY_ID_BOUNDFIELD:
1710 SAL_WARN("forms.component", "OBoundControlModel::setFastPropertyValue_NoBroadcast : BoundField should be a read-only property !");
1711 throw css::lang::IllegalArgumentException();
1712 case PROPERTY_ID_CONTROLLABEL:
1714 if ( rValue.hasValue() && ( rValue.getValueTypeClass() != TypeClass_INTERFACE ) )
1715 throw css::lang::IllegalArgumentException();
1716 Reference< XInterface > xNewValue( rValue, UNO_QUERY );
1717 if ( !xNewValue.is() )
1718 { // set property to "void"
1719 Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY );
1720 if ( xComp.is() )
1721 xComp->removeEventListener( static_cast< XPropertyChangeListener* >( this ) );
1722 m_xLabelControl = nullptr;
1723 break;
1726 Reference< XControlModel > xAsModel ( xNewValue, UNO_QUERY );
1727 Reference< XServiceInfo > xAsServiceInfo ( xAsModel, UNO_QUERY );
1728 Reference< XPropertySet > xAsPropSet ( xAsServiceInfo, UNO_QUERY );
1729 Reference< XChild > xAsChild ( xAsPropSet, UNO_QUERY );
1730 if ( !xAsChild.is() || !xAsServiceInfo->supportsService( m_aLabelServiceName ) )
1732 throw css::lang::IllegalArgumentException();
1735 // Check if we and the given model have a common ancestor (up to the forms collection)
1736 Reference<XChild> xCont(this);
1737 Reference< XInterface > xMyTopLevel = xCont->getParent();
1738 while (xMyTopLevel.is())
1740 Reference<XForm> xAsForm(xMyTopLevel, UNO_QUERY);
1741 if (!xAsForm.is())
1742 // found my root
1743 break;
1744 Reference<XChild> xLoopAsChild(xMyTopLevel, UNO_QUERY);
1745 xMyTopLevel = xLoopAsChild.is() ? xLoopAsChild->getParent() : Reference< XInterface >();
1748 Reference< XInterface > xNewTopLevel = xAsChild->getParent();
1749 while (xNewTopLevel.is())
1751 Reference<XForm> xAsForm(xNewTopLevel, UNO_QUERY);
1752 if (!xAsForm.is())
1753 break;
1754 Reference<XChild> xLoopAsChild(xNewTopLevel, UNO_QUERY);
1755 xNewTopLevel = xLoopAsChild.is() ? xLoopAsChild->getParent() : Reference< XInterface >();
1758 if (xNewTopLevel != xMyTopLevel)
1760 // the both objects don't belong to the same forms collection -> not acceptable
1761 throw css::lang::IllegalArgumentException();
1764 m_xLabelControl = xAsPropSet;
1765 Reference<css::lang::XComponent> xComp(m_xLabelControl, UNO_QUERY);
1766 if (xComp.is())
1767 xComp->addEventListener(static_cast<css::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
1770 break;
1771 default:
1772 OControlModel::setFastPropertyValue_NoBroadcast(nHandle, rValue );
1776 // XPropertyChangeListener
1777 void SAL_CALL OBoundControlModel::propertyChange( const PropertyChangeEvent& evt )
1779 // if the DBColumn value changed, transfer it to the control
1780 if ( evt.PropertyName == PROPERTY_VALUE )
1782 OSL_ENSURE( evt.Source == getField(), "OBoundControlModel::propertyChange: value changes from components other than our database column?" );
1783 osl::MutexGuard aGuard(m_aMutex);
1784 if ( m_bForwardValueChanges && m_xColumn.is() )
1785 transferDbValueToControl();
1788 else
1790 OSL_ENSURE( evt.Source == m_xExternalBinding, "OBoundControlModel::propertyChange: where did this come from?" );
1791 // our binding has properties which can control properties of ourself
1792 OUString sBindingControlledProperty;
1793 bool bForwardToLabelControl = false;
1794 if ( evt.PropertyName == PROPERTY_READONLY )
1796 sBindingControlledProperty = PROPERTY_READONLY;
1799 else if ( evt.PropertyName == PROPERTY_RELEVANT )
1801 sBindingControlledProperty = PROPERTY_ENABLED;
1802 bForwardToLabelControl = true;
1805 else
1806 return;
1809 setPropertyValue( sBindingControlledProperty, evt.NewValue );
1810 if ( bForwardToLabelControl && m_xLabelControl.is() )
1811 m_xLabelControl->setPropertyValue( sBindingControlledProperty, evt.NewValue );
1814 catch( const Exception& )
1816 DBG_UNHANDLED_EXCEPTION("forms.component");
1817 SAL_WARN("forms.component", "OBoundControlModel::propertyChange: could not adjust my binding-controlled property!");
1823 void SAL_CALL OBoundControlModel::onRowSetChanged( const EventObject& /*i_Event*/ )
1825 ControlModelLock aLock( *this );
1826 FieldChangeNotifier aBoundFieldNotifier( aLock );
1827 // disconnect from database column (which is controlled by parent, directly or indirectly)
1828 if ( hasField() )
1829 impl_disconnectDatabaseColumn_noNotify();
1830 // log off old listeners
1831 if ( isFormListening() )
1832 doFormListening( false );
1833 // determine the new ambient form
1834 impl_determineAmbientForm_nothrow();
1835 // log on new listeners
1836 doFormListening( true );
1837 // re-connect to database column if needed and possible
1838 if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() )
1839 impl_connectDatabaseColumn_noNotify( false );
1842 // XBoundComponent
1843 void SAL_CALL OBoundControlModel::addUpdateListener(const Reference<XUpdateListener>& _rxListener)
1845 m_aUpdateListeners.addInterface(_rxListener);
1848 void SAL_CALL OBoundControlModel::removeUpdateListener(const Reference< XUpdateListener>& _rxListener)
1850 m_aUpdateListeners.removeInterface(_rxListener);
1853 sal_Bool SAL_CALL OBoundControlModel::commit()
1855 ControlModelLock aLock( *this );
1856 OSL_PRECOND( m_bCommitable, "OBoundControlModel::commit: invalid call (I'm not committable!) " );
1857 if ( hasExternalValueBinding() )
1859 // in most cases, no action is required: For most derivees, we know the value property of
1860 // our control (see initValueProperty), and when an external binding is active, we
1861 // instantly forward all changes in this property to the external binding.
1862 if ( m_sValuePropertyName.isEmpty() )
1863 // but for those derivees which did not use this feature, we need an
1864 // explicit transfer
1865 transferControlValueToExternal( aLock );
1866 return true;
1869 OSL_ENSURE( !hasExternalValueBinding(), "OBoundControlModel::commit: control flow broken!" );
1870 // we reach this only if we're not working with an external binding
1871 if ( !hasField() )
1872 return true;
1873 ::comphelper::OInterfaceIteratorHelper3 aIter( m_aUpdateListeners );
1874 EventObject aEvent;
1875 aEvent.Source = static_cast< XWeak* >( this );
1876 bool bSuccess = true;
1877 aLock.release();
1878 // UNSAFE >
1879 while (aIter.hasMoreElements() && bSuccess)
1880 bSuccess = aIter.next()->approveUpdate( aEvent );
1881 // < UNSAFE
1882 aLock.acquire();
1883 if ( bSuccess )
1887 if ( m_xColumnUpdate.is() )
1888 bSuccess = commitControlValueToDbColumn( false );
1891 catch(const Exception&)
1893 bSuccess = false;
1898 if ( bSuccess )
1900 aLock.release();
1901 m_aUpdateListeners.notifyEach( &XUpdateListener::updated, aEvent );
1903 return bSuccess;
1906 void OBoundControlModel::resetField()
1908 m_xColumnUpdate.clear();
1909 m_xColumn.clear();
1910 m_xField.clear();
1911 m_nFieldType = DataType::OTHER;
1914 void OBoundControlModel::connectToField(const Reference<XRowSet>& rForm)
1916 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::connectToField: invalid call (have an external binding)!" );
1917 // if there's a connection to the database
1918 if (!(rForm.is() && getConnection(rForm).is()))
1919 return;
1921 // determine field and PropertyChangeListener
1922 m_xCursor = rForm;
1923 Reference<XPropertySet> xFieldCandidate;
1924 if (m_xCursor.is())
1926 Reference<XColumnsSupplier> xColumnsSupplier(m_xCursor, UNO_QUERY);
1927 DBG_ASSERT(xColumnsSupplier.is(), "OBoundControlModel::connectToField : the row set should support the css::sdb::ResultSet service !");
1928 if (xColumnsSupplier.is())
1930 Reference<XNameAccess> xColumns = xColumnsSupplier->getColumns();
1931 if (xColumns.is() && xColumns->hasByName(m_aControlSource))
1933 OSL_VERIFY( xColumns->getByName(m_aControlSource) >>= xFieldCandidate );
1942 sal_Int32 nFieldType = DataType::OTHER;
1943 if ( xFieldCandidate.is() )
1945 xFieldCandidate->getPropertyValue( PROPERTY_FIELDTYPE ) >>= nFieldType;
1946 if ( approveDbColumnType( nFieldType ) )
1947 impl_setField_noNotify( xFieldCandidate );
1950 else
1951 impl_setField_noNotify( nullptr );
1952 if ( m_xField.is() )
1954 if ( m_xField->getPropertySetInfo()->hasPropertyByName( PROPERTY_VALUE ) )
1956 m_nFieldType = nFieldType;
1957 // listen to changing values
1958 m_xField->addPropertyChangeListener( PROPERTY_VALUE, this );
1959 m_xColumnUpdate.set( m_xField, UNO_QUERY );
1960 m_xColumn.set( m_xField, UNO_QUERY );
1961 sal_Int32 nNullableFlag = ColumnValue::NO_NULLS;
1962 m_xField->getPropertyValue(PROPERTY_ISNULLABLE) >>= nNullableFlag;
1963 // tdf#122319 - don't allow nullable form components if input is required
1964 m_bRequired = (ColumnValue::NO_NULLS == nNullableFlag || m_bInputRequired);
1965 // we're optimistic: in case of ColumnValue_NULLABLE_UNKNOWN we assume nullability...
1967 else
1969 SAL_WARN("forms.component", "OBoundControlModel::connectToField: property " << PROPERTY_VALUE << " not supported!");
1970 impl_setField_noNotify( nullptr );
1977 catch( const Exception& )
1979 DBG_UNHANDLED_EXCEPTION("forms.component");
1980 resetField();
1984 void OBoundControlModel::initFromField( const Reference< XRowSet >& _rxRowSet )
1986 // but only if the rowset is positioned on a valid record
1987 if ( !(hasField() && _rxRowSet.is()) )
1988 return;
1990 bool shouldTransfer(!_rxRowSet->isBeforeFirst() && !_rxRowSet->isAfterLast());
1991 if (!shouldTransfer)
1993 const Reference< XPropertySet > xPS(_rxRowSet, UNO_QUERY);
1994 if (xPS.is())
1996 assert(!shouldTransfer);
1997 xPS->getPropertyValue("IsNew") >>= shouldTransfer;
2000 if ( shouldTransfer )
2001 transferDbValueToControl();
2002 else
2003 // reset the field if the row set is empty
2004 // #i30661#
2005 resetNoBroadcast();
2008 bool OBoundControlModel::approveDbColumnType(sal_Int32 _nColumnType)
2010 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::approveDbColumnType: invalid call (have an external binding)!" );
2011 if ((_nColumnType == DataType::BINARY) || (_nColumnType == DataType::VARBINARY)
2012 || (_nColumnType == DataType::LONGVARBINARY) || (_nColumnType == DataType::OTHER)
2013 || (_nColumnType == DataType::OBJECT) || (_nColumnType == DataType::DISTINCT)
2014 || (_nColumnType == DataType::STRUCT) || (_nColumnType == DataType::ARRAY)
2015 || (_nColumnType == DataType::BLOB) /*|| (_nColumnType == DataType::CLOB)*/
2016 || (_nColumnType == DataType::REF) || (_nColumnType == DataType::SQLNULL))
2017 return false;
2018 return true;
2021 void OBoundControlModel::impl_determineAmbientForm_nothrow()
2023 Reference< XInterface > xParent( getParent() );
2024 m_xAmbientForm.set( xParent, UNO_QUERY );
2025 if ( !m_xAmbientForm.is() )
2027 Reference< XRowSetSupplier > xSupRowSet( xParent, UNO_QUERY );
2028 if ( xSupRowSet.is() )
2029 m_xAmbientForm.set( xSupRowSet->getRowSet(), UNO_QUERY );
2033 void OBoundControlModel::impl_connectDatabaseColumn_noNotify( bool _bFromReload )
2035 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::impl_connectDatabaseColumn_noNotify: not to be called with an external value binding!" );
2036 // consistency checks
2037 DBG_ASSERT( !( hasField() && !_bFromReload ),
2038 "OBoundControlModel::impl_connectDatabaseColumn_noNotify: the form is just *loaded*, but we already have a field!" );
2040 Reference< XRowSet > xRowSet( m_xAmbientForm, UNO_QUERY );
2041 OSL_ENSURE( xRowSet.is(), "OBoundControlModel::impl_connectDatabaseColumn_noNotify: no row set!" );
2042 if ( !xRowSet.is() )
2043 return;
2044 if ( !hasField() || _bFromReload )
2046 // connect to the column
2047 connectToField( xRowSet );
2050 // now that we're connected (more or less, even if we did not find a column),
2051 // we definitely want to forward any potentially occurring value changes
2052 m_bForwardValueChanges = true;
2053 // let derived classes react on this new connection
2054 m_bLoaded = true;
2055 onConnectedDbColumn( xRowSet );
2056 // initially transfer the db column value to the control, if we successfully connected to a database column
2057 if ( hasField() )
2058 initFromField( xRowSet );
2061 void OBoundControlModel::impl_disconnectDatabaseColumn_noNotify()
2063 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::impl_disconnectDatabaseColumn_noNotify: not to be called with an external value binding!" );
2064 // let derived classes react on this
2065 onDisconnectedDbColumn();
2066 if ( hasField() )
2068 getField()->removePropertyChangeListener( PROPERTY_VALUE, this );
2069 resetField();
2072 m_xCursor = nullptr;
2073 m_bLoaded = false;
2076 // XLoadListener
2077 void SAL_CALL OBoundControlModel::loaded( const EventObject& _rEvent )
2079 ControlModelLock aLock( *this );
2080 FieldChangeNotifier aBoundFieldNotifier( aLock );
2081 OSL_ENSURE( _rEvent.Source == m_xAmbientForm, "OBoundControlModel::loaded: where does this come from?" );
2082 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::loaded: we should never reach this with an external value binding!" );
2083 if ( hasExternalValueBinding() )
2084 return;
2085 impl_connectDatabaseColumn_noNotify( false );
2088 void SAL_CALL OBoundControlModel::unloaded( const css::lang::EventObject& /*aEvent*/ )
2090 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::unloaded: we should never reach this with an external value binding!" );
2093 void SAL_CALL OBoundControlModel::reloading( const css::lang::EventObject& /*aEvent*/ )
2095 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::reloading: we should never reach this with an external value binding!" );
2096 if ( hasExternalValueBinding() )
2097 return;
2098 osl::MutexGuard aGuard(m_aMutex);
2099 m_bForwardValueChanges = false;
2102 void SAL_CALL OBoundControlModel::unloading(const css::lang::EventObject& /*aEvent*/)
2104 ControlModelLock aLock( *this );
2105 FieldChangeNotifier aBoundFieldNotifier( aLock );
2106 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::unloading: we should never reach this with an external value binding!" );
2107 if ( hasExternalValueBinding() )
2108 return;
2109 impl_disconnectDatabaseColumn_noNotify();
2112 void SAL_CALL OBoundControlModel::reloaded( const EventObject& _rEvent )
2114 ControlModelLock aLock( *this );
2115 FieldChangeNotifier aBoundFieldNotifier( aLock );
2116 OSL_ENSURE( _rEvent.Source == m_xAmbientForm, "OBoundControlModel::reloaded: where does this come from?" );
2117 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::reloaded: we should never reach this with an external value binding!" );
2118 if ( hasExternalValueBinding() )
2119 return;
2120 impl_connectDatabaseColumn_noNotify( true );
2123 void OBoundControlModel::setControlValue( const Any& _rValue, ValueChangeInstigator _eInstigator )
2125 m_eControlValueChangeInstigator = _eInstigator;
2126 doSetControlValue( _rValue );
2127 m_eControlValueChangeInstigator = eOther;
2130 void OBoundControlModel::doSetControlValue( const Any& _rValue )
2132 OSL_PRECOND( m_xAggregateFastSet.is() && m_xAggregateSet.is(),
2133 "OBoundControlModel::doSetControlValue: invalid aggregate !" );
2134 OSL_PRECOND( !m_sValuePropertyName.isEmpty() || ( m_nValuePropertyAggregateHandle != -1 ),
2135 "OBoundControlModel::doSetControlValue: please override if you have own value property handling!" );
2138 // release our mutex once (it's acquired in one of the calling methods), as setting aggregate properties
2139 // may cause any uno controls belonging to us to lock the solar mutex, which is potentially dangerous with
2140 // our own mutex locked
2141 MutexRelease aRelease( m_aMutex );
2142 if ( ( m_nValuePropertyAggregateHandle != -1 ) && m_xAggregateFastSet.is() )
2144 m_xAggregateFastSet->setFastPropertyValue( m_nValuePropertyAggregateHandle, _rValue );
2147 else if ( !m_sValuePropertyName.isEmpty() && m_xAggregateSet.is() )
2149 m_xAggregateSet->setPropertyValue( m_sValuePropertyName, _rValue );
2154 catch( const Exception& )
2156 TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::doSetControlValue");
2160 void OBoundControlModel::onConnectedValidator( )
2164 // if we have an external validator, we do not want the control to force invalid
2165 // inputs to the default value. Instead, invalid inputs should be translated
2166 // to NaN (not a number)
2167 Reference< XPropertySetInfo > xAggregatePropertyInfo;
2168 if ( m_xAggregateSet.is() )
2169 xAggregatePropertyInfo = m_xAggregateSet->getPropertySetInfo();
2170 if ( xAggregatePropertyInfo.is() && xAggregatePropertyInfo->hasPropertyByName( PROPERTY_ENFORCE_FORMAT ) )
2171 m_xAggregateSet->setPropertyValue( PROPERTY_ENFORCE_FORMAT, Any( false ) );
2174 catch( const Exception& )
2176 TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::onConnectedValidator");
2179 recheckValidity( false );
2182 void OBoundControlModel::onDisconnectedValidator( )
2186 Reference< XPropertySetInfo > xAggregatePropertyInfo;
2187 if ( m_xAggregateSet.is() )
2188 xAggregatePropertyInfo = m_xAggregateSet->getPropertySetInfo();
2189 if ( xAggregatePropertyInfo.is() && xAggregatePropertyInfo->hasPropertyByName( PROPERTY_ENFORCE_FORMAT ) )
2190 m_xAggregateSet->setPropertyValue( PROPERTY_ENFORCE_FORMAT, Any( true ) );
2193 catch( const Exception& )
2195 TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::onDisconnectedValidator");
2198 recheckValidity( false );
2201 void OBoundControlModel::onConnectedExternalValue( )
2203 calculateExternalValueType();
2206 void OBoundControlModel::onConnectedDbColumn( const Reference< XInterface >& /*_rxForm*/ )
2208 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::onConnectedDbColumn: how this? There's an external value binding!" );
2211 void OBoundControlModel::onDisconnectedDbColumn()
2213 OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::onDisconnectedDbColumn: how this? There's an external value binding!" );
2216 // XReset
2217 Any OBoundControlModel::getDefaultForReset() const
2219 return Any();
2222 void OBoundControlModel::resetNoBroadcast()
2224 setControlValue( getDefaultForReset(), eOther );
2227 void OBoundControlModel::addResetListener(const Reference<XResetListener>& l)
2229 m_aResetHelper.addResetListener( l );
2232 void OBoundControlModel::removeResetListener(const Reference<XResetListener>& l)
2234 m_aResetHelper.removeResetListener( l );
2237 void OBoundControlModel::reset()
2239 if ( !m_aResetHelper.approveReset() )
2240 return;
2241 ControlModelLock aLock( *this );
2242 // on a new record?
2243 bool bIsNewRecord = false;
2244 Reference<XPropertySet> xSet( m_xCursor, UNO_QUERY );
2245 if ( xSet.is() )
2249 xSet->getPropertyValue( PROPERTY_ISNEW ) >>= bIsNewRecord;
2252 catch( const Exception& )
2254 DBG_UNHANDLED_EXCEPTION("forms.component");
2259 // cursor on an invalid row?
2260 bool bInvalidCursorPosition = true;
2263 bInvalidCursorPosition = m_xCursor.is()
2264 && ( m_xCursor->isAfterLast()
2265 || m_xCursor->isBeforeFirst()
2267 && !bIsNewRecord;
2270 catch( const SQLException& )
2272 TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::reset: caught an SQL exception!" );
2275 // #i24495# - don't count the insert row as "invalid"
2276 bool bSimpleReset =
2277 ( !m_xColumn.is() // no connection to a database column
2278 || ( m_xCursor.is() // OR we have an improperly positioned cursor
2279 && bInvalidCursorPosition
2281 || hasExternalValueBinding() // OR we have an external value binding
2283 if ( !bSimpleReset )
2285 // The default values will be set if and only if the current value of the field which we're bound
2286 // to is NULL.
2287 // Else, the current field value should be refreshed
2288 // This behaviour is not completely ... "matured": What should happen if the field as well as the
2289 // control have a default value?
2290 bool bIsNull = true;
2291 // we have to access the field content at least once to get a reliable result by XColumn::wasNull
2294 // normally, we'd do a getString here. However, this is extremely expensive in the case
2295 // of binary fields. Unfortunately, getString is the only method which is guaranteed
2296 // to *always* succeed, all other getXXX methods may fail if the column is asked for a
2297 // non-convertible type
2298 sal_Int32 nFieldType = DataType::OBJECT;
2299 getField()->getPropertyValue( PROPERTY_FIELDTYPE ) >>= nFieldType;
2300 if ( ( nFieldType == DataType::BINARY )
2301 || ( nFieldType == DataType::VARBINARY )
2302 || ( nFieldType == DataType::LONGVARBINARY )
2303 || ( nFieldType == DataType::OBJECT )
2304 /*|| ( nFieldType == DataType::CLOB )*/
2306 m_xColumn->getBinaryStream();
2307 else if ( nFieldType == DataType::BLOB )
2308 m_xColumn->getBlob();
2309 else
2310 m_xColumn->getString();
2311 bIsNull = m_xColumn->wasNull();
2314 catch(const Exception&)
2316 DBG_UNHANDLED_EXCEPTION("forms.component");
2317 SAL_WARN("forms.component", "OBoundControlModel::reset: this should have succeeded in all cases!");
2320 bool bNeedValueTransfer = true;
2321 if ( bIsNull )
2323 if ( bIsNewRecord )
2325 // reset the control to its default
2326 resetNoBroadcast();
2327 // and immediately commit the changes to the DB column, to keep consistency
2328 commitControlValueToDbColumn( true );
2329 bNeedValueTransfer = false;
2334 if ( bNeedValueTransfer )
2335 transferDbValueToControl();
2338 else
2340 resetNoBroadcast();
2341 // transfer to the external binding, if necessary
2342 if ( hasExternalValueBinding() )
2343 transferControlValueToExternal( aLock );
2346 // revalidate, if necessary
2347 if ( hasValidator() )
2348 recheckValidity( true );
2349 aLock.release();
2350 m_aResetHelper.notifyResetted();
2353 void OBoundControlModel::impl_setField_noNotify( const Reference< XPropertySet>& _rxField )
2355 DBG_ASSERT( !hasExternalValueBinding(), "OBoundControlModel::impl_setField_noNotify: We have an external value binding!" );
2356 m_xField = _rxField;
2359 bool OBoundControlModel::impl_approveValueBinding_nolock( const Reference< XValueBinding >& _rxBinding )
2361 if ( !_rxBinding.is() )
2362 return false;
2363 Sequence< Type > aTypeCandidates;
2365 // SYNCHRONIZED >
2366 ::osl::MutexGuard aGuard( m_aMutex );
2367 aTypeCandidates = getSupportedBindingTypes();
2368 // < SYNCHRONIZED
2371 for ( auto const & type : std::as_const(aTypeCandidates) )
2373 if ( _rxBinding->supportsType( type ) )
2374 return true;
2376 return false;
2379 void OBoundControlModel::connectExternalValueBinding(
2380 const Reference< XValueBinding >& _rxBinding, ControlModelLock& _rInstanceLock )
2382 OSL_PRECOND( _rxBinding.is(), "OBoundControlModel::connectExternalValueBinding: invalid binding instance!" );
2383 OSL_PRECOND( !hasExternalValueBinding( ), "OBoundControlModel::connectExternalValueBinding: precond not met (currently have a binding)!" );
2384 // if we're connected to a database column, suspend this
2385 if ( hasField() )
2386 impl_disconnectDatabaseColumn_noNotify();
2387 // suspend listening for load-related events at out ambient form.
2388 // This is because an external value binding overrules a possible database binding.
2389 if ( isFormListening() )
2390 doFormListening( false );
2391 // remember this new binding
2392 m_xExternalBinding = _rxBinding;
2393 // tell the derivee
2394 onConnectedExternalValue();
2397 // add as value listener so we get notified when the value changes
2398 Reference< XModifyBroadcaster > xModifiable( m_xExternalBinding, UNO_QUERY );
2399 if ( xModifiable.is() )
2400 xModifiable->addModifyListener( this );
2401 // add as property change listener for some (possibly present) properties we're
2402 // interested in
2403 Reference< XPropertySet > xBindingProps( m_xExternalBinding, UNO_QUERY );
2404 Reference< XPropertySetInfo > xBindingPropsInfo( xBindingProps.is() ? xBindingProps->getPropertySetInfo() : Reference< XPropertySetInfo >() );
2405 if ( xBindingPropsInfo.is() )
2407 if ( xBindingPropsInfo->hasPropertyByName( PROPERTY_READONLY ) )
2409 xBindingProps->addPropertyChangeListener( PROPERTY_READONLY, this );
2410 m_bBindingControlsRO = true;
2413 if ( xBindingPropsInfo->hasPropertyByName( PROPERTY_RELEVANT ) )
2415 xBindingProps->addPropertyChangeListener( PROPERTY_RELEVANT, this );
2416 m_bBindingControlsEnable = true;
2423 catch( const Exception& )
2425 DBG_UNHANDLED_EXCEPTION("forms.component");
2428 // propagate our new value
2429 transferExternalValueToControl( _rInstanceLock );
2430 // if the binding is also a validator, use it, too. This is a constraint of the
2431 // com.sun.star.form.binding.ValidatableBindableFormComponent service
2432 if ( !m_bSupportsValidation )
2433 return;
2437 Reference< XValidator > xAsValidator( _rxBinding, UNO_QUERY );
2438 if ( xAsValidator.is() )
2439 setValidator( xAsValidator );
2442 catch( const Exception& )
2444 DBG_UNHANDLED_EXCEPTION("forms.component");
2448 void OBoundControlModel::disconnectExternalValueBinding( )
2452 // not listening at the binding anymore
2453 Reference< XModifyBroadcaster > xModifiable( m_xExternalBinding, UNO_QUERY );
2454 if ( xModifiable.is() )
2455 xModifiable->removeModifyListener( this );
2456 // remove as property change listener
2457 Reference< XPropertySet > xBindingProps( m_xExternalBinding, UNO_QUERY );
2458 if ( m_bBindingControlsRO )
2459 xBindingProps->removePropertyChangeListener( PROPERTY_READONLY, this );
2460 if ( m_bBindingControlsEnable )
2461 xBindingProps->removePropertyChangeListener( PROPERTY_RELEVANT, this );
2464 catch( const Exception& )
2466 TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::disconnectExternalValueBinding");
2469 // if the binding also acts as our validator, disconnect the validator, too
2470 if ( ( m_xExternalBinding == m_xValidator ) && m_xValidator.is() )
2471 disconnectValidator( );
2472 // no binding anymore
2473 m_xExternalBinding.clear();
2474 // be a load listener at our form, again. This was suspended while we had
2475 // an external value binding in place.
2476 doFormListening( true );
2477 // re-connect to database column of the new parent
2478 if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() )
2479 impl_connectDatabaseColumn_noNotify( false );
2482 void SAL_CALL OBoundControlModel::setValueBinding( const Reference< XValueBinding >& _rxBinding )
2484 OSL_PRECOND( m_bSupportsExternalBinding, "OBoundControlModel::setValueBinding: How did you reach this method?" );
2485 // the interface for this method should not have been exposed if we do not
2486 // support binding to external data
2487 // allow reset
2488 if ( _rxBinding.is() && !impl_approveValueBinding_nolock( _rxBinding ) )
2490 throw IncompatibleTypesException(
2491 ResourceManager::loadString(RID_STR_INCOMPATIBLE_TYPES),
2492 *this
2496 ControlModelLock aLock( *this );
2497 // since a ValueBinding overrules any potentially active database binding, the change in a ValueBinding
2498 // might trigger a change in our BoundField.
2499 FieldChangeNotifier aBoundFieldNotifier( aLock );
2500 // disconnect from the old binding
2501 if ( hasExternalValueBinding() )
2502 disconnectExternalValueBinding( );
2503 // connect to the new binding
2504 if ( _rxBinding.is() )
2505 connectExternalValueBinding( _rxBinding, aLock );
2508 Reference< XValueBinding > SAL_CALL OBoundControlModel::getValueBinding( )
2510 ::osl::MutexGuard aGuard( m_aMutex );
2511 OSL_PRECOND( m_bSupportsExternalBinding, "OBoundControlModel::getValueBinding: How did you reach this method?" );
2512 // the interface for this method should not have been exposed if we do not
2513 // support binding to external data
2514 return m_xExternalBinding;
2517 void SAL_CALL OBoundControlModel::modified( const EventObject& _rEvent )
2519 ControlModelLock aLock( *this );
2520 OSL_PRECOND( hasExternalValueBinding(), "OBoundControlModel::modified: Where did this come from?" );
2521 if ( !m_bTransferringValue && ( m_xExternalBinding == _rEvent.Source ) && m_xExternalBinding.is() )
2523 transferExternalValueToControl( aLock );
2527 void OBoundControlModel::transferDbValueToControl( )
2531 setControlValue( translateDbColumnToControlValue(), eDbColumnBinding );
2534 catch( const Exception& )
2536 DBG_UNHANDLED_EXCEPTION("forms.component");
2540 void OBoundControlModel::transferExternalValueToControl( ControlModelLock& _rInstanceLock )
2542 Reference< XValueBinding > xExternalBinding( m_xExternalBinding );
2543 Type aValueExchangeType( getExternalValueType() );
2544 _rInstanceLock.release();
2545 // UNSAFE >
2546 Any aExternalValue;
2549 aExternalValue = xExternalBinding->getValue( aValueExchangeType );
2552 catch( const Exception& )
2554 DBG_UNHANDLED_EXCEPTION("forms.component");
2556 // < UNSAFE
2557 _rInstanceLock.acquire();
2558 setControlValue( translateExternalValueToControlValue( aExternalValue ), eExternalBinding );
2561 void OBoundControlModel::transferControlValueToExternal( ControlModelLock& _rInstanceLock )
2563 OSL_PRECOND( m_bSupportsExternalBinding && hasExternalValueBinding(),
2564 "OBoundControlModel::transferControlValueToExternal: precondition not met!" );
2565 if ( !m_xExternalBinding.is() )
2566 return;
2568 Any aExternalValue( translateControlValueToExternalValue() );
2569 m_bTransferringValue = true;
2570 _rInstanceLock.release();
2571 // UNSAFE >
2574 m_xExternalBinding->setValue( aExternalValue );
2577 catch( const Exception& )
2579 DBG_UNHANDLED_EXCEPTION("forms.component");
2582 // < UNSAFE
2583 _rInstanceLock.acquire();
2584 m_bTransferringValue = false;
2587 Sequence< Type > OBoundControlModel::getSupportedBindingTypes()
2589 return Sequence< Type >( &m_aValuePropertyType, 1 );
2592 void OBoundControlModel::calculateExternalValueType()
2594 m_aExternalValueType = Type();
2595 if ( !m_xExternalBinding.is() )
2596 return;
2597 const Sequence< Type > aTypeCandidates( getSupportedBindingTypes() );
2598 for ( auto const & typeCandidate : aTypeCandidates )
2600 if ( m_xExternalBinding->supportsType( typeCandidate ) )
2602 m_aExternalValueType = typeCandidate;
2603 break;
2608 Any OBoundControlModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
2610 OSL_PRECOND( m_bSupportsExternalBinding && hasExternalValueBinding(),
2611 "OBoundControlModel::translateExternalValueToControlValue: precondition not met!" );
2612 Any aControlValue( _rExternalValue );
2613 // if the external value is VOID, and our value property is not allowed to be VOID,
2614 // then default-construct a value
2615 if ( !aControlValue.hasValue() && !m_bValuePropertyMayBeVoid )
2616 aControlValue.setValue( nullptr, m_aValuePropertyType );
2617 // out of here
2618 return aControlValue;
2621 Any OBoundControlModel::translateControlValueToExternalValue( ) const
2623 return getControlValue( );
2626 Any OBoundControlModel::translateControlValueToValidatableValue( ) const
2628 OSL_PRECOND( m_xValidator.is(), "OBoundControlModel::translateControlValueToValidatableValue: no validator, so why should I?" );
2629 if ( ( m_xValidator == m_xExternalBinding ) && m_xValidator.is() )
2630 return translateControlValueToExternalValue();
2631 return getControlValue();
2634 Any OBoundControlModel::getControlValue( ) const
2636 OSL_PRECOND( m_xAggregateFastSet.is() && m_xAggregateSet.is(),
2637 "OBoundControlModel::getControlValue: invalid aggregate !" );
2638 OSL_PRECOND( !m_sValuePropertyName.isEmpty() || ( m_nValuePropertyAggregateHandle != -1 ),
2639 "OBoundControlModel::getControlValue: please override if you have own value property handling!" );
2640 // determine the current control value
2641 Any aControlValue;
2642 if ( ( m_nValuePropertyAggregateHandle != -1 ) && m_xAggregateFastSet.is() )
2644 aControlValue = m_xAggregateFastSet->getFastPropertyValue( m_nValuePropertyAggregateHandle );
2647 else if ( !m_sValuePropertyName.isEmpty() && m_xAggregateSet.is() )
2649 aControlValue = m_xAggregateSet->getPropertyValue( m_sValuePropertyName );
2651 return aControlValue;
2654 void OBoundControlModel::connectValidator( const Reference< XValidator >& _rxValidator )
2656 OSL_PRECOND( _rxValidator.is(), "OBoundControlModel::connectValidator: invalid validator instance!" );
2657 OSL_PRECOND( !hasValidator( ), "OBoundControlModel::connectValidator: precond not met (have a validator currently)!" );
2658 m_xValidator = _rxValidator;
2660 // add as value listener so we get notified when the value changes
2661 if ( m_xValidator.is() )
2665 m_xValidator->addValidityConstraintListener( this );
2668 catch( const RuntimeException& )
2672 onConnectedValidator( );
2675 void OBoundControlModel::disconnectValidator( )
2677 OSL_PRECOND( hasValidator( ), "OBoundControlModel::connectValidator: precond not met (don't have a validator currently)!" );
2679 // add as value listener so we get notified when the value changes
2680 if ( m_xValidator.is() )
2684 m_xValidator->removeValidityConstraintListener( this );
2687 catch( const RuntimeException& )
2692 m_xValidator.clear();
2694 onDisconnectedValidator( );
2697 void SAL_CALL OBoundControlModel::setValidator( const Reference< XValidator >& _rxValidator )
2699 osl::MutexGuard aGuard( m_aMutex );
2700 OSL_PRECOND( m_bSupportsValidation, "OBoundControlModel::setValidator: How did you reach this method?" );
2701 // the interface for this method should not have been exposed if we do not
2702 // support validation
2704 // early out if the validator does not change
2705 if ( _rxValidator == m_xValidator )
2706 return;
2708 if ( m_xValidator.is() && ( m_xValidator == m_xExternalBinding ) )
2709 throw VetoException(
2710 ResourceManager::loadString(RID_STR_INVALID_VALIDATOR),
2711 *this
2714 // disconnect from the old validator
2715 if ( hasValidator() )
2716 disconnectValidator( );
2718 // connect to the new validator
2719 if ( _rxValidator.is() )
2720 connectValidator( _rxValidator );
2723 Reference< XValidator > SAL_CALL OBoundControlModel::getValidator( )
2725 ::osl::MutexGuard aGuard( m_aMutex );
2726 OSL_PRECOND( m_bSupportsValidation, "OBoundControlModel::getValidator: How did you reach this method?" );
2727 // the interface for this method should not have been exposed if we do not
2728 // support validation
2730 return m_xValidator;
2733 void SAL_CALL OBoundControlModel::validityConstraintChanged( const EventObject& /*Source*/ )
2735 osl::MutexGuard aGuard( m_aMutex );
2736 OSL_PRECOND( m_bSupportsValidation, "OBoundControlModel::validityConstraintChanged: How did you reach this method?" );
2737 // the interface for this method should not have been exposed if we do not
2738 // support validation
2740 recheckValidity( false );
2743 sal_Bool SAL_CALL OBoundControlModel::isValid( )
2745 return m_bIsCurrentValueValid;
2748 css::uno::Any OBoundControlModel::getCurrentFormComponentValue() const
2750 if ( hasValidator() )
2751 return translateControlValueToValidatableValue();
2752 return getControlValue();
2755 Any SAL_CALL OBoundControlModel::getCurrentValue( )
2757 ::osl::MutexGuard aGuard( m_aMutex );
2758 return getCurrentFormComponentValue();
2761 void SAL_CALL OBoundControlModel::addFormComponentValidityListener( const Reference< validation::XFormComponentValidityListener >& Listener )
2763 if ( Listener.is() )
2764 m_aFormComponentListeners.addInterface( Listener );
2767 void SAL_CALL OBoundControlModel::removeFormComponentValidityListener( const Reference< validation::XFormComponentValidityListener >& Listener )
2769 if ( Listener.is() )
2770 m_aFormComponentListeners.removeInterface( Listener );
2773 void OBoundControlModel::recheckValidity( bool _bForceNotification )
2777 bool bIsCurrentlyValid = true;
2778 if ( hasValidator() )
2779 bIsCurrentlyValid = m_xValidator->isValid( translateControlValueToValidatableValue() );
2781 if ( ( bIsCurrentlyValid != m_bIsCurrentValueValid ) || _bForceNotification )
2783 m_bIsCurrentValueValid = bIsCurrentlyValid;
2785 // release our mutex for the notifications
2786 MutexRelease aRelease( m_aMutex );
2787 m_aFormComponentListeners.notifyEach( &validation::XFormComponentValidityListener::componentValidityChanged, EventObject( *this ) );
2792 catch( const Exception& )
2794 TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::recheckValidity");
2798 void OBoundControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const
2800 OControlModel::describeFixedProperties( _rProps );
2801 sal_Int32 nOldCount = _rProps.getLength();
2802 _rProps.realloc( nOldCount + 5);
2803 css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
2804 *pProperties++ = css::beans::Property(PROPERTY_CONTROLSOURCE, PROPERTY_ID_CONTROLSOURCE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
2805 *pProperties++ = css::beans::Property(PROPERTY_BOUNDFIELD, PROPERTY_ID_BOUNDFIELD, cppu::UnoType<XPropertySet>::get(),
2806 css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
2807 *pProperties++ = css::beans::Property(PROPERTY_CONTROLLABEL, PROPERTY_ID_CONTROLLABEL, cppu::UnoType<XPropertySet>::get(),
2808 css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID);
2809 *pProperties++ = css::beans::Property(PROPERTY_CONTROLSOURCEPROPERTY, PROPERTY_ID_CONTROLSOURCEPROPERTY, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
2810 *pProperties++ = css::beans::Property(PROPERTY_INPUT_REQUIRED, PROPERTY_ID_INPUT_REQUIRED, cppu::UnoType<bool>::get(),
2811 css::beans::PropertyAttribute::BOUND);
2812 DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
2816 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */