summaryrefslogtreecommitdiffstats
path: root/forms/source/component
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /forms/source/component
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'forms/source/component')
-rw-r--r--forms/source/component/BaseListBox.hxx30
-rw-r--r--forms/source/component/Button.cxx780
-rw-r--r--forms/source/component/Button.hxx201
-rw-r--r--forms/source/component/CheckBox.cxx293
-rw-r--r--forms/source/component/CheckBox.hxx83
-rw-r--r--forms/source/component/Columns.cxx900
-rw-r--r--forms/source/component/Columns.hxx322
-rw-r--r--forms/source/component/ComboBox.cxx888
-rw-r--r--forms/source/component/ComboBox.hxx145
-rw-r--r--forms/source/component/Currency.cxx253
-rw-r--r--forms/source/component/Currency.hxx85
-rw-r--r--forms/source/component/DatabaseForm.cxx4044
-rw-r--r--forms/source/component/DatabaseForm.hxx527
-rw-r--r--forms/source/component/Date.cxx326
-rw-r--r--forms/source/component/Date.hxx116
-rw-r--r--forms/source/component/Edit.cxx717
-rw-r--r--forms/source/component/Edit.hxx178
-rw-r--r--forms/source/component/EditBase.cxx377
-rw-r--r--forms/source/component/EditBase.hxx96
-rw-r--r--forms/source/component/EventThread.cxx196
-rw-r--r--forms/source/component/EventThread.hxx105
-rw-r--r--forms/source/component/File.cxx275
-rw-r--r--forms/source/component/File.hxx96
-rw-r--r--forms/source/component/Filter.cxx890
-rw-r--r--forms/source/component/Filter.hxx138
-rw-r--r--forms/source/component/FixedText.cxx125
-rw-r--r--forms/source/component/FixedText.hxx67
-rw-r--r--forms/source/component/FormComponent.cxx2799
-rw-r--r--forms/source/component/FormattedField.cxx1022
-rw-r--r--forms/source/component/FormattedField.hxx182
-rw-r--r--forms/source/component/FormattedFieldWrapper.cxx360
-rw-r--r--forms/source/component/FormattedFieldWrapper.hxx87
-rw-r--r--forms/source/component/FormsCollection.cxx141
-rw-r--r--forms/source/component/FormsCollection.hxx149
-rw-r--r--forms/source/component/Grid.cxx992
-rw-r--r--forms/source/component/Grid.hxx200
-rw-r--r--forms/source/component/GroupBox.cxx162
-rw-r--r--forms/source/component/GroupBox.hxx82
-rw-r--r--forms/source/component/GroupManager.cxx421
-rw-r--r--forms/source/component/GroupManager.hxx194
-rw-r--r--forms/source/component/Hidden.cxx177
-rw-r--r--forms/source/component/Hidden.hxx76
-rw-r--r--forms/source/component/ImageButton.cxx249
-rw-r--r--forms/source/component/ImageButton.hxx101
-rw-r--r--forms/source/component/ImageControl.cxx992
-rw-r--r--forms/source/component/ImageControl.hxx196
-rw-r--r--forms/source/component/ListBox.cxx2198
-rw-r--r--forms/source/component/ListBox.hxx332
-rw-r--r--forms/source/component/Numeric.cxx202
-rw-r--r--forms/source/component/Numeric.hxx88
-rw-r--r--forms/source/component/Pattern.cxx237
-rw-r--r--forms/source/component/Pattern.hxx94
-rw-r--r--forms/source/component/RadioButton.cxx402
-rw-r--r--forms/source/component/RadioButton.hxx93
-rw-r--r--forms/source/component/Time.cxx334
-rw-r--r--forms/source/component/Time.hxx115
-rw-r--r--forms/source/component/cachedrowset.cxx175
-rw-r--r--forms/source/component/cachedrowset.hxx78
-rw-r--r--forms/source/component/clickableimage.cxx838
-rw-r--r--forms/source/component/clickableimage.hxx283
-rw-r--r--forms/source/component/cloneable.cxx59
-rw-r--r--forms/source/component/entrylisthelper.cxx325
-rw-r--r--forms/source/component/entrylisthelper.hxx196
-rw-r--r--forms/source/component/errorbroadcaster.cxx94
-rw-r--r--forms/source/component/errorbroadcaster.hxx60
-rw-r--r--forms/source/component/findpos.cxx47
-rw-r--r--forms/source/component/findpos.hxx31
-rw-r--r--forms/source/component/formcontrolfont.cxx578
-rw-r--r--forms/source/component/imgprod.cxx491
-rw-r--r--forms/source/component/imgprod.hxx89
-rw-r--r--forms/source/component/navigationbar.cxx487
-rw-r--r--forms/source/component/navigationbar.hxx116
-rw-r--r--forms/source/component/propertybaghelper.cxx339
-rw-r--r--forms/source/component/refvaluecomponent.cxx294
-rw-r--r--forms/source/component/refvaluecomponent.hxx87
-rw-r--r--forms/source/component/scrollbar.cxx317
-rw-r--r--forms/source/component/scrollbar.hxx93
-rw-r--r--forms/source/component/spinbutton.cxx271
-rw-r--r--forms/source/component/spinbutton.hxx89
79 files changed, 30367 insertions, 0 deletions
diff --git a/forms/source/component/BaseListBox.hxx b/forms/source/component/BaseListBox.hxx
new file mode 100644
index 000000000..fe0930ea7
--- /dev/null
+++ b/forms/source/component/BaseListBox.hxx
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+namespace frm
+{
+const sal_uInt16 ENTRY_NOT_FOUND = 0xFFFF;
+const sal_uInt16 BOUNDCOLUMN = 0x0001;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Button.cxx b/forms/source/component/Button.cxx
new file mode 100644
index 000000000..3c08b6524
--- /dev/null
+++ b/forms/source/component/Button.cxx
@@ -0,0 +1,780 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "Button.hxx"
+#include <property.hxx>
+#include <services.hxx>
+
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+#include <comphelper/streamsection.hxx>
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+#include <o3tl/any.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/mutex.hxx>
+
+namespace frm
+{
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+using ::com::sun::star::frame::XDispatchProviderInterceptor;
+
+
+//= OButtonModel
+
+
+OButtonModel::OButtonModel(const Reference<XComponentContext>& _rxFactory)
+ :OClickableImageBaseModel( _rxFactory, VCL_CONTROLMODEL_COMMANDBUTTON, FRM_SUN_CONTROL_COMMANDBUTTON )
+ // use the old control name for compatibility reasons
+ ,m_aResetHelper( *this, m_aMutex )
+ ,m_eDefaultState( TRISTATE_FALSE )
+{
+ m_nClassId = FormComponentType::COMMANDBUTTON;
+}
+
+
+Any SAL_CALL OButtonModel::queryAggregation( const Type& _type )
+{
+ Any aReturn = OClickableImageBaseModel::queryAggregation( _type );
+ if ( !aReturn.hasValue() )
+ aReturn = OButtonModel_Base::queryInterface( _type );
+ return aReturn;
+}
+
+
+Sequence< Type > OButtonModel::_getTypes()
+{
+ return ::comphelper::concatSequences(
+ OClickableImageBaseModel::_getTypes(),
+ OButtonModel_Base::getTypes()
+ );
+}
+
+
+OButtonModel::OButtonModel( const OButtonModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OClickableImageBaseModel( _pOriginal, _rxFactory )
+ ,m_aResetHelper( *this, m_aMutex )
+ ,m_eDefaultState( _pOriginal->m_eDefaultState )
+{
+ m_nClassId = FormComponentType::COMMANDBUTTON;
+
+ implInitializeImageURL();
+}
+
+
+OButtonModel::~OButtonModel()
+{
+}
+
+
+void OButtonModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OClickableImageBaseModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 6);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_BUTTONTYPE, PROPERTY_ID_BUTTONTYPE, cppu::UnoType<FormButtonType>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_STATE, PROPERTY_ID_DEFAULT_STATE, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_DISPATCHURLINTERNAL, PROPERTY_ID_DISPATCHURLINTERNAL, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TARGET_URL, PROPERTY_ID_TARGET_URL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TARGET_FRAME, PROPERTY_ID_TARGET_FRAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OButtonModel::createClone()
+{
+ rtl::Reference<OButtonModel> pClone = new OButtonModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> OButtonModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OClickableImageBaseModel::getSupportedServiceNames();
+ aSupported.realloc( aSupported.getLength() + 2 );
+
+ OUString* pArray = aSupported.getArray();
+ pArray[ aSupported.getLength() - 2 ] = FRM_SUN_COMPONENT_COMMANDBUTTON;
+ pArray[ aSupported.getLength() - 1 ] = FRM_COMPONENT_COMMANDBUTTON;
+
+ return aSupported;
+}
+
+
+OUString OButtonModel::getServiceName()
+{
+ return FRM_COMPONENT_COMMANDBUTTON; // old (non-sun) name for compatibility !
+}
+
+
+void OButtonModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ OClickableImageBaseModel::write(_rxOutStream);
+
+ _rxOutStream->writeShort(0x0003); // Version
+
+ {
+ OStreamSection aSection( _rxOutStream );
+ // this will allow readers to skip unknown bytes in their dtor
+
+ _rxOutStream->writeShort( static_cast<sal_uInt16>(m_eButtonType) );
+
+ OUString sTmp = INetURLObject::decode( m_sTargetURL, INetURLObject::DecodeMechanism::Unambiguous);
+ _rxOutStream << sTmp;
+ _rxOutStream << m_sTargetFrame;
+ writeHelpTextCompatibly(_rxOutStream);
+ _rxOutStream << isDispatchUrlInternal();
+ }
+}
+
+
+void OButtonModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ OClickableImageBaseModel::read(_rxInStream);
+
+ sal_uInt16 nVersion = _rxInStream->readShort(); // Version
+ switch (nVersion)
+ {
+ case 0x0001:
+ {
+ m_eButtonType = static_cast<FormButtonType>(_rxInStream->readShort());
+
+ _rxInStream >> m_sTargetURL;
+ _rxInStream >> m_sTargetFrame;
+ }
+ break;
+
+ case 0x0002:
+ {
+ m_eButtonType = static_cast<FormButtonType>(_rxInStream->readShort());
+
+ _rxInStream >> m_sTargetURL;
+ _rxInStream >> m_sTargetFrame;
+ readHelpTextCompatibly(_rxInStream);
+ }
+ break;
+
+ case 0x0003:
+ {
+ OStreamSection aSection( _rxInStream );
+ // this will skip any unknown bytes in its dtor
+
+ // button type
+ m_eButtonType = static_cast<FormButtonType>(_rxInStream->readShort());
+
+ // URL
+ _rxInStream >> m_sTargetURL;
+
+ // target frame
+ _rxInStream >> m_sTargetFrame;
+
+ // help text
+ readHelpTextCompatibly(_rxInStream);
+
+ // DispatchInternal
+ bool bDispatch;
+ _rxInStream >> bDispatch;
+ setDispatchUrlInternal(bDispatch);
+ }
+ break;
+
+ default:
+ OSL_FAIL("OButtonModel::read : unknown version !");
+ m_eButtonType = FormButtonType_PUSH;
+ m_sTargetURL.clear();
+ m_sTargetFrame.clear();
+ break;
+ }
+}
+
+
+void SAL_CALL OButtonModel::disposing()
+{
+ m_aResetHelper.disposing();
+ OClickableImageBaseModel::disposing();
+}
+
+
+void SAL_CALL OButtonModel::reset()
+{
+ if ( !m_aResetHelper.approveReset() )
+ return;
+
+ impl_resetNoBroadcast_nothrow();
+
+ m_aResetHelper.notifyResetted();
+}
+
+
+void SAL_CALL OButtonModel::addResetListener( const Reference< XResetListener >& _listener )
+{
+ m_aResetHelper.addResetListener( _listener );
+}
+
+
+void SAL_CALL OButtonModel::removeResetListener( const Reference< XResetListener >& _listener )
+{
+ m_aResetHelper.removeResetListener( _listener );
+}
+
+
+void SAL_CALL OButtonModel::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+{
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_STATE:
+ _rValue <<= static_cast<sal_Int16>(m_eDefaultState);
+ break;
+
+ default:
+ OClickableImageBaseModel::getFastPropertyValue( _rValue, _nHandle );
+ break;
+ }
+}
+
+
+void SAL_CALL OButtonModel::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue )
+{
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_STATE:
+ {
+ sal_Int16 nDefaultState = sal_Int16(TRISTATE_FALSE);
+ OSL_VERIFY( _rValue >>= nDefaultState );
+ m_eDefaultState = static_cast<ToggleState>(nDefaultState);
+ impl_resetNoBroadcast_nothrow();
+ }
+ break;
+
+ default:
+ OClickableImageBaseModel::setFastPropertyValue_NoBroadcast( _nHandle, _rValue );
+ break;
+ }
+}
+
+
+sal_Bool SAL_CALL OButtonModel::convertFastPropertyValue( Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+{
+ bool bModified = false;
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_STATE:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, static_cast<sal_Int16>(m_eDefaultState) );
+ break;
+
+ default:
+ bModified = OClickableImageBaseModel::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+ break;
+ }
+ return bModified;
+}
+
+
+Any OButtonModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+{
+ Any aDefault;
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_STATE:
+ aDefault <<= sal_Int16(TRISTATE_FALSE);
+ break;
+
+ default:
+ aDefault = OClickableImageBaseModel::getPropertyDefaultByHandle( _nHandle );
+ break;
+ }
+ return aDefault;
+}
+
+
+void OButtonModel::impl_resetNoBroadcast_nothrow()
+{
+ try
+ {
+ setPropertyValue( PROPERTY_STATE, getPropertyValue( PROPERTY_DEFAULT_STATE ) );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+}
+
+
+// OButtonControl
+
+
+Sequence<Type> OButtonControl::_getTypes()
+{
+ return ::comphelper::concatSequences(
+ OButtonControl_BASE::getTypes(),
+ OClickableImageBaseControl::_getTypes(),
+ OFormNavigationHelper::getTypes()
+ );
+}
+
+
+css::uno::Sequence<OUString> OButtonControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OClickableImageBaseControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_COMMANDBUTTON;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_COMMANDBUTTON;
+ return aSupported;
+}
+
+
+OButtonControl::OButtonControl(const Reference<XComponentContext>& _rxFactory)
+ :OClickableImageBaseControl(_rxFactory, VCL_CONTROL_COMMANDBUTTON)
+ ,OFormNavigationHelper( _rxFactory )
+ ,m_nClickEvent( nullptr )
+ ,m_nTargetUrlFeatureId( -1 )
+ ,m_bEnabledByPropertyValue( false )
+{
+ osl_atomic_increment(&m_refCount);
+ {
+ // Register as ActionListener
+ Reference<XButton> xButton;
+ query_aggregation( m_xAggregate, xButton);
+ if (xButton.is())
+ xButton->addActionListener(this);
+ }
+ // For Listener: refcount at one
+ osl_atomic_decrement(&m_refCount);
+}
+
+
+OButtonControl::~OButtonControl()
+{
+ if (m_nClickEvent)
+ Application::RemoveUserEvent(m_nClickEvent);
+}
+
+// UNO binding
+
+Any SAL_CALL OButtonControl::queryAggregation(const Type& _rType)
+{
+ // if asked for the XTypeProvider, don't let OButtonControl_BASE do this
+ Any aReturn;
+ if ( !_rType.equals( cppu::UnoType<XTypeProvider>::get() ) )
+ aReturn = OButtonControl_BASE::queryInterface( _rType );
+
+ if ( !aReturn.hasValue() )
+ aReturn = OClickableImageBaseControl::queryAggregation( _rType );
+
+ if ( !aReturn.hasValue() )
+ aReturn = OFormNavigationHelper::queryInterface( _rType );
+
+ return aReturn;
+}
+
+
+void SAL_CALL OButtonControl::disposing()
+{
+ startOrStopModelPropertyListening( false );
+
+ OClickableImageBaseControl::disposing();
+ OFormNavigationHelper::dispose();
+}
+
+
+void SAL_CALL OButtonControl::disposing( const EventObject& _rSource )
+{
+ OControl::disposing( _rSource );
+ OFormNavigationHelper::disposing( _rSource );
+}
+
+// ActionListener
+
+void OButtonControl::actionPerformed(const ActionEvent& /*rEvent*/)
+{
+ // Asynchronous for css::util::URL-Button
+ ImplSVEvent * n = Application::PostUserEvent( LINK(this, OButtonControl, OnClick) );
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_nClickEvent = n;
+ }
+}
+
+
+IMPL_LINK_NOARG(OButtonControl, OnClick, void*, void)
+{
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ m_nClickEvent = nullptr;
+
+ if (m_aApproveActionListeners.getLength())
+ {
+ // if there are listeners, start the action in an own thread, to not allow
+ // them to block us here (we're in the application's main thread)
+ getImageProducerThread()->addEvent();
+ }
+ else
+ {
+ // Else, don't. We then must not notify the Listeners in any case,
+ // not even if added later on.
+ aGuard.clear();
+
+ // recognize the button type
+ Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
+ if (!xSet.is())
+ return;
+
+ if (FormButtonType_PUSH == *o3tl::doAccess<FormButtonType>(xSet->getPropertyValue(PROPERTY_BUTTONTYPE)))
+ {
+ // notify the action listeners for a push button
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aActionListeners);
+ ActionEvent aEvt(static_cast<XWeak*>(this), m_aActionCommand);
+ while(aIter.hasMoreElements() )
+ {
+ // catch exceptions
+ // and catch them on a per-listener basis - if one listener fails, the others still need
+ // to get notified
+ try
+ {
+ aIter.next()->actionPerformed(aEvt);
+ }
+#ifdef DBG_UTIL
+ catch( const RuntimeException& )
+ {
+ // silence this
+ }
+#endif
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.component", "OButtonControl::OnClick: caught an exception other than RuntimeException!" );
+ }
+ }
+ }
+ else
+ actionPerformed_Impl( false, css::awt::MouseEvent() );
+ }
+}
+
+
+void OButtonControl::actionPerformed_Impl( bool _bNotifyListener, const css::awt::MouseEvent& _rEvt )
+{
+ {
+ sal_Int16 nFeatureId = -1;
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ nFeatureId = m_nTargetUrlFeatureId;
+ }
+
+ if ( nFeatureId != -1 )
+ {
+ if ( !approveAction() )
+ return;
+
+ SolarMutexGuard aGuard;
+ dispatch( nFeatureId );
+ return;
+ }
+ }
+
+ OClickableImageBaseControl::actionPerformed_Impl( _bNotifyListener, _rEvt );
+}
+
+// XButton
+
+void OButtonControl::setLabel(const OUString& Label)
+{
+ Reference<XButton> xButton;
+ query_aggregation( m_xAggregate, xButton );
+ if (xButton.is())
+ xButton->setLabel(Label);
+}
+
+
+void SAL_CALL OButtonControl::setActionCommand(const OUString& _rCommand)
+{
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_aActionCommand = _rCommand;
+ }
+
+ Reference<XButton> xButton;
+ query_aggregation( m_xAggregate, xButton);
+ if (xButton.is())
+ xButton->setActionCommand(_rCommand);
+}
+
+
+void SAL_CALL OButtonControl::addActionListener(const Reference<XActionListener>& _rxListener)
+{
+ m_aActionListeners.addInterface(_rxListener);
+}
+
+
+void SAL_CALL OButtonControl::removeActionListener(const Reference<XActionListener>& _rxListener)
+{
+ m_aActionListeners.removeInterface(_rxListener);
+}
+
+namespace {
+
+class DoPropertyListening
+{
+private:
+ Reference< XPropertySet > m_xProps;
+ Reference< XPropertyChangeListener > m_xListener;
+ bool m_bStartListening;
+
+public:
+ DoPropertyListening(
+ const Reference< XInterface >& _rxComponent,
+ const Reference< XPropertyChangeListener >& _rxListener,
+ bool _bStart
+ );
+
+ void handleListening( const OUString& _rPropertyName );
+};
+
+}
+
+DoPropertyListening::DoPropertyListening(
+ const Reference< XInterface >& _rxComponent, const Reference< XPropertyChangeListener >& _rxListener,
+ bool _bStart )
+ :m_xProps( _rxComponent, UNO_QUERY )
+ ,m_xListener( _rxListener )
+ ,m_bStartListening( _bStart )
+{
+ DBG_ASSERT( m_xProps.is() || !_rxComponent.is(), "DoPropertyListening::DoPropertyListening: valid component, but no property set!" );
+ DBG_ASSERT( m_xListener.is(), "DoPropertyListening::DoPropertyListening: invalid listener!" );
+}
+
+
+void DoPropertyListening::handleListening( const OUString& _rPropertyName )
+{
+ if ( m_xProps.is() )
+ {
+ if ( m_bStartListening )
+ m_xProps->addPropertyChangeListener( _rPropertyName, m_xListener );
+ else
+ m_xProps->removePropertyChangeListener( _rPropertyName, m_xListener );
+ }
+}
+
+
+void OButtonControl::startOrStopModelPropertyListening( bool _bStart )
+{
+ DoPropertyListening aListeningHandler( getModel(), this, _bStart );
+ aListeningHandler.handleListening( PROPERTY_TARGET_URL );
+ aListeningHandler.handleListening( PROPERTY_BUTTONTYPE );
+ aListeningHandler.handleListening( PROPERTY_ENABLED );
+}
+
+
+sal_Bool SAL_CALL OButtonControl::setModel( const Reference< XControlModel >& _rxModel )
+{
+ startOrStopModelPropertyListening( false );
+ bool bResult = OClickableImageBaseControl::setModel( _rxModel );
+ startOrStopModelPropertyListening( true );
+
+ m_bEnabledByPropertyValue = true;
+ Reference< XPropertySet > xModelProps( _rxModel, UNO_QUERY );
+ if ( xModelProps.is() )
+ xModelProps->getPropertyValue( PROPERTY_ENABLED ) >>= m_bEnabledByPropertyValue;
+
+ modelFeatureUrlPotentiallyChanged( );
+
+ return bResult;
+}
+
+
+void OButtonControl::modelFeatureUrlPotentiallyChanged( )
+{
+ sal_Int16 nOldUrlFeatureId = m_nTargetUrlFeatureId;
+
+ // Do we have another TargetURL now? If so, we need to update our dispatches
+ m_nTargetUrlFeatureId = getModelUrlFeatureId( );
+ if ( nOldUrlFeatureId != m_nTargetUrlFeatureId )
+ {
+ invalidateSupportedFeaturesSet();
+ if ( !isDesignMode() )
+ updateDispatches( );
+ }
+}
+
+
+void SAL_CALL OButtonControl::propertyChange( const PropertyChangeEvent& _rEvent )
+{
+ if ( _rEvent.PropertyName == PROPERTY_TARGET_URL
+ || _rEvent.PropertyName == PROPERTY_BUTTONTYPE
+ )
+ {
+ modelFeatureUrlPotentiallyChanged( );
+ }
+ else if ( _rEvent.PropertyName == PROPERTY_ENABLED )
+ {
+ _rEvent.NewValue >>= m_bEnabledByPropertyValue;
+ }
+}
+
+
+namespace
+{
+ bool isFormControllerURL( const OUString& _rURL )
+ {
+ return ( _rURL.getLength() > RTL_CONSTASCII_LENGTH( ".uno:FormController/" ) )
+ && ( _rURL.startsWith( ".uno:FormController/" ) );
+ }
+}
+
+
+sal_Int16 OButtonControl::getModelUrlFeatureId( ) const
+{
+ sal_Int16 nFeatureId = -1;
+
+ // some URL related properties of the model
+ OUString sUrl;
+ FormButtonType eButtonType = FormButtonType_PUSH;
+
+ Reference< XPropertySet > xModelProps( const_cast< OButtonControl* >( this )->getModel(), UNO_QUERY );
+ if ( xModelProps.is() )
+ {
+ xModelProps->getPropertyValue( PROPERTY_TARGET_URL ) >>= sUrl;
+ xModelProps->getPropertyValue( PROPERTY_BUTTONTYPE ) >>= eButtonType;
+ }
+
+ // are we a URL button?
+ if ( eButtonType == FormButtonType_URL )
+ {
+ // is it a feature URL?
+ if ( isFormControllerURL( sUrl ) )
+ {
+ nFeatureId = OFormNavigationMapper::getFeatureId( sUrl );
+ }
+ }
+
+ return nFeatureId;
+}
+
+
+void SAL_CALL OButtonControl::setDesignMode( sal_Bool _bOn )
+{
+ OClickableImageBaseControl::setDesignMode( _bOn );
+
+ if ( _bOn )
+ disconnectDispatchers();
+ else
+ connectDispatchers();
+ // this will connect if not already connected and just update else
+}
+
+
+void OButtonControl::getSupportedFeatures( ::std::vector< sal_Int16 >& /* [out] */ _rFeatureIds )
+{
+ if ( -1 != m_nTargetUrlFeatureId )
+ _rFeatureIds.push_back( m_nTargetUrlFeatureId );
+}
+
+
+void OButtonControl::featureStateChanged( sal_Int16 _nFeatureId, bool _bEnabled )
+{
+ if ( _nFeatureId == m_nTargetUrlFeatureId )
+ {
+ // enable or disable our peer, according to the new state
+ Reference< XVclWindowPeer > xPeer( getPeer(), UNO_QUERY );
+ if ( xPeer.is() )
+ xPeer->setProperty( PROPERTY_ENABLED, Any( m_bEnabledByPropertyValue && _bEnabled ) );
+ // if we're disabled according to our model's property, then
+ // we don't care for the feature state, but *are* disabled.
+ // If the model's property states that we're enabled, then we *do*
+ // care for the feature state
+ }
+
+ // base class
+ OFormNavigationHelper::featureStateChanged( _nFeatureId, _bEnabled );
+}
+
+
+void OButtonControl::allFeatureStatesChanged( )
+{
+ if ( -1 != m_nTargetUrlFeatureId )
+ // we have only one supported feature, so simulate it has changed ...
+ featureStateChanged( m_nTargetUrlFeatureId, isEnabled( m_nTargetUrlFeatureId ) );
+
+ // base class
+ OFormNavigationHelper::allFeatureStatesChanged( );
+}
+
+
+bool OButtonControl::isEnabled( sal_Int16 _nFeatureId ) const
+{
+ if ( const_cast< OButtonControl* >( this )->isDesignMode() )
+ // TODO: the model property?
+ return true;
+
+ return OFormNavigationHelper::isEnabled( _nFeatureId );
+}
+
+
+void SAL_CALL OButtonControl::registerDispatchProviderInterceptor( const Reference< XDispatchProviderInterceptor >& _rxInterceptor )
+{
+ OClickableImageBaseControl::registerDispatchProviderInterceptor( _rxInterceptor );
+ OFormNavigationHelper::registerDispatchProviderInterceptor( _rxInterceptor );
+}
+
+
+void SAL_CALL OButtonControl::releaseDispatchProviderInterceptor( const Reference< XDispatchProviderInterceptor >& _rxInterceptor )
+{
+ OClickableImageBaseControl::releaseDispatchProviderInterceptor( _rxInterceptor );
+ OFormNavigationHelper::releaseDispatchProviderInterceptor( _rxInterceptor );
+}
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OButtonModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OButtonModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OButtonControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OButtonControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Button.hxx b/forms/source/component/Button.hxx
new file mode 100644
index 000000000..7ccaf7d8a
--- /dev/null
+++ b/forms/source/component/Button.hxx
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "clickableimage.hxx"
+#include <togglestate.hxx>
+#include <formnavigation.hxx>
+#include <resettable.hxx>
+
+#include <com/sun/star/awt/MouseEvent.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/awt/ActionEvent.hpp>
+#include <com/sun/star/awt/XActionListener.hpp>
+#include <com/sun/star/awt/XButton.hpp>
+#include <com/sun/star/form/XReset.hpp>
+#include <com/sun/star/beans/PropertyChangeEvent.hpp>
+
+#include <cppuhelper/implbase1.hxx>
+
+struct ImplSVEvent;
+
+namespace frm
+{
+
+typedef ::cppu::ImplHelper1 < css::form::XReset
+ > OButtonModel_Base;
+class OButtonModel :public OClickableImageBaseModel
+ ,public OButtonModel_Base
+{
+public:
+ OButtonModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OButtonModel(
+ const OButtonModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OButtonModel() override;
+
+ // UNO
+ DECLARE_UNO3_AGG_DEFAULTS( OButtonModel, OClickableImageBaseModel )
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+ css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+// css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OButtonModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+// css::io::XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // XReset
+ virtual void SAL_CALL reset( ) override;
+ virtual void SAL_CALL addResetListener( const css::uno::Reference< css::form::XResetListener >& aListener ) override;
+ virtual void SAL_CALL removeResetListener( const css::uno::Reference< css::form::XResetListener >& aListener ) override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+ // XPropertySet and friends
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle) const override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(
+ css::uno::Any& _rConvertedValue, css::uno::Any& _rOldValue, sal_Int32 _nHandle, const css::uno::Any& _rValue ) override;
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 nHandle ) const override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+private:
+ void impl_resetNoBroadcast_nothrow();
+
+ using ::cppu::OPropertySetHelper::getFastPropertyValue;
+
+private:
+ ResetHelper m_aResetHelper;
+
+ // <properties>
+ ToggleState m_eDefaultState; // the default check state
+ // </properties>
+protected:
+ using OClickableImageBaseModel::disposing;
+};
+
+
+// OButtonControl
+
+typedef ::cppu::ImplHelper3 < css::awt::XButton
+ , css::awt::XActionListener
+ , css::beans::XPropertyChangeListener
+ > OButtonControl_BASE;
+
+class OButtonControl :public OButtonControl_BASE
+ ,public OClickableImageBaseControl
+ ,public OFormNavigationHelper
+{
+private:
+ ImplSVEvent * m_nClickEvent;
+ sal_Int16 m_nTargetUrlFeatureId;
+ /// caches the value of the "Enabled" property of our model
+ bool m_bEnabledByPropertyValue;
+
+protected:
+
+ // UNO binding
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ explicit OButtonControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+ virtual ~OButtonControl() override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OButtonControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // UNO binding
+ DECLARE_UNO3_AGG_DEFAULTS(OButtonControl, OClickableImageBaseControl)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ // XActionListener
+ virtual void SAL_CALL actionPerformed(const css::awt::ActionEvent& rEvent) override;
+
+ // XButton
+ virtual void SAL_CALL addActionListener(const css::uno::Reference< css::awt::XActionListener>& _rxListener) override;
+ virtual void SAL_CALL removeActionListener(const css::uno::Reference< css::awt::XActionListener>& _rxListener) override;
+ virtual void SAL_CALL setLabel(const OUString& Label) override;
+ virtual void SAL_CALL setActionCommand(const OUString& _rCommand) override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // XPropertyChangeListener
+ virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource) override;
+
+ // XControl
+ virtual sal_Bool SAL_CALL setModel( const css::uno::Reference< css::awt::XControlModel >& _rxModel ) override;
+ void SAL_CALL setDesignMode(sal_Bool bOn) override;
+
+protected:
+ // OFormNavigationHelper overriables
+ virtual void getSupportedFeatures( ::std::vector< sal_Int16 >& /* [out] */ _rFeatureIds ) override;
+ virtual void featureStateChanged( sal_Int16 _nFeatureId, bool _bEnabled ) override;
+ virtual void allFeatureStatesChanged( ) override;
+ virtual bool isEnabled( sal_Int16 _nFeatureId ) const override;
+
+ // XDispatchProviderInterception disambiguation
+ virtual void SAL_CALL registerDispatchProviderInterceptor( const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& Interceptor ) override;
+ virtual void SAL_CALL releaseDispatchProviderInterceptor( const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& Interceptor ) override;
+
+ // OImageControl overridables
+ virtual void actionPerformed_Impl( bool bNotifyListener, const css::awt::MouseEvent& _rEvt ) override;
+
+private:
+ DECL_LINK( OnClick, void*, void );
+
+ /// to be called whenever the feature URL represented by our model has potentially changed
+ void modelFeatureUrlPotentiallyChanged( );
+
+ /// retrieves the feature id (see OFormNavigationHelper) of the TargetURL of the model.
+ sal_Int16 getModelUrlFeatureId( ) const;
+
+ /// starts or stops listening for changes in model properties we're interested in
+ void startOrStopModelPropertyListening( bool _bStart );
+};
+
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/CheckBox.cxx b/forms/source/component/CheckBox.cxx
new file mode 100644
index 000000000..fcfdabc53
--- /dev/null
+++ b/forms/source/component/CheckBox.cxx
@@ -0,0 +1,293 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "CheckBox.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <comphelper/basicio.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+namespace frm
+{
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::form::binding;
+
+OCheckBoxControl::OCheckBoxControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_CHECKBOX)
+{
+}
+
+
+css::uno::Sequence<OUString> SAL_CALL OCheckBoxControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString* pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_CHECKBOX;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_CHECKBOX;
+ return aSupported;
+}
+
+
+//= OCheckBoxModel
+
+OCheckBoxModel::OCheckBoxModel(const Reference<XComponentContext>& _rxFactory)
+ :OReferenceValueComponent( _rxFactory, VCL_CONTROLMODEL_CHECKBOX, FRM_SUN_CONTROL_CHECKBOX )
+ // use the old control name for compytibility reasons
+{
+
+ m_nClassId = FormComponentType::CHECKBOX;
+ initValueProperty( PROPERTY_STATE, PROPERTY_ID_STATE );
+}
+
+
+OCheckBoxModel::OCheckBoxModel( const OCheckBoxModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OReferenceValueComponent( _pOriginal, _rxFactory )
+{
+}
+
+
+OCheckBoxModel::~OCheckBoxModel()
+{
+}
+
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OCheckBoxModel::createClone()
+{
+ rtl::Reference<OCheckBoxModel> pClone = new OCheckBoxModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL OCheckBoxModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OReferenceValueComponent::getSupportedServiceNames();
+
+ sal_Int32 nOldLen = aSupported.getLength();
+ aSupported.realloc( nOldLen + 9 );
+ OUString* pStoreTo = aSupported.getArray() + nOldLen;
+
+ *pStoreTo++ = BINDABLE_CONTROL_MODEL;
+ *pStoreTo++ = DATA_AWARE_CONTROL_MODEL;
+ *pStoreTo++ = VALIDATABLE_CONTROL_MODEL;
+
+ *pStoreTo++ = BINDABLE_DATA_AWARE_CONTROL_MODEL;
+ *pStoreTo++ = VALIDATABLE_BINDABLE_CONTROL_MODEL;
+
+ *pStoreTo++ = FRM_SUN_COMPONENT_CHECKBOX;
+ *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_CHECKBOX;
+ *pStoreTo++ = BINDABLE_DATABASE_CHECK_BOX;
+
+ *pStoreTo++ = FRM_COMPONENT_CHECKBOX;
+
+ return aSupported;
+}
+
+
+void OCheckBoxModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OReferenceValueComponent::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 1);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+OUString SAL_CALL OCheckBoxModel::getServiceName()
+{
+ return FRM_COMPONENT_CHECKBOX; // old (non-sun) name for compatibility !
+}
+
+
+void SAL_CALL OCheckBoxModel::write(const Reference<css::io::XObjectOutputStream>& _rxOutStream)
+{
+ OReferenceValueComponent::write(_rxOutStream);
+
+ // Version
+ _rxOutStream->writeShort(0x0003);
+ // Properties
+ _rxOutStream << getReferenceValue();
+ _rxOutStream << static_cast<sal_Int16>(getDefaultChecked());
+ writeHelpTextCompatibly(_rxOutStream);
+ // from version 0x0003 : common properties
+ writeCommonProperties(_rxOutStream);
+}
+
+
+void SAL_CALL OCheckBoxModel::read(const Reference<css::io::XObjectInputStream>& _rxInStream)
+{
+ OReferenceValueComponent::read(_rxInStream);
+ osl::MutexGuard aGuard(m_aMutex);
+
+ // Version
+ sal_uInt16 nVersion = _rxInStream->readShort();
+
+ OUString sReferenceValue;
+ sal_Int16 nDefaultChecked( 0 );
+ switch ( nVersion )
+ {
+ case 0x0001:
+ _rxInStream >> sReferenceValue;
+ nDefaultChecked = _rxInStream->readShort();
+ break;
+ case 0x0002:
+ _rxInStream >> sReferenceValue;
+ _rxInStream >> nDefaultChecked;
+ readHelpTextCompatibly( _rxInStream );
+ break;
+ case 0x0003:
+ _rxInStream >> sReferenceValue;
+ _rxInStream >> nDefaultChecked;
+ readHelpTextCompatibly(_rxInStream);
+ readCommonProperties(_rxInStream);
+ break;
+ default:
+ OSL_FAIL("OCheckBoxModel::read : unknown version !");
+ defaultCommonProperties();
+ break;
+ }
+ setReferenceValue( sReferenceValue );
+ setDefaultChecked( static_cast< ToggleState >( nDefaultChecked ) );
+
+ // After reading in, display the default values
+ if ( !getControlSource().isEmpty() )
+ // (not if we don't have a control source - the "State" property acts like it is persistent, then
+ resetNoBroadcast();
+}
+
+bool OCheckBoxModel::DbUseBool()
+{
+ return getReferenceValue().isEmpty() && getNoCheckReferenceValue().isEmpty();
+}
+
+
+Any OCheckBoxModel::translateDbColumnToControlValue()
+{
+ Any aValue;
+
+
+ // Set value in ControlModel
+ bool bValue = bool(); // avoid warning
+ if(DbUseBool())
+ {
+ bValue = m_xColumn->getBoolean();
+ }
+ else
+ {
+ const OUString sVal(m_xColumn->getString());
+ if (sVal == getReferenceValue())
+ bValue = true;
+ else if (sVal == getNoCheckReferenceValue())
+ bValue = false;
+ else
+ aValue <<= static_cast<sal_Int16>(getDefaultChecked());
+ }
+ if ( m_xColumn->wasNull() )
+ {
+ bool bTriState = true;
+ if ( m_xAggregateSet.is() )
+ m_xAggregateSet->getPropertyValue( PROPERTY_TRISTATE ) >>= bTriState;
+ aValue <<= static_cast<sal_Int16>( bTriState ? TRISTATE_INDET : getDefaultChecked() );
+ }
+ else if ( !aValue.hasValue() )
+ {
+ // Since above either bValue is initialised, either aValue.hasValue(),
+ // bValue cannot be used uninitialised here.
+ // But GCC does not see/understand that, which breaks -Werror builds,
+ // so we explicitly default-initialise it.
+ aValue <<= static_cast<sal_Int16>( bValue ? TRISTATE_TRUE : TRISTATE_FALSE );
+ }
+
+ return aValue;
+}
+
+
+bool OCheckBoxModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+{
+ OSL_PRECOND( m_xColumnUpdate.is(), "OCheckBoxModel::commitControlValueToDbColumn: not bound!" );
+ if ( !m_xColumnUpdate )
+ return true;
+
+ Any aControlValue( m_xAggregateSet->getPropertyValue( PROPERTY_STATE ) );
+ try
+ {
+ sal_Int16 nValue = TRISTATE_INDET;
+ aControlValue >>= nValue;
+ switch (nValue)
+ {
+ case TRISTATE_INDET:
+ m_xColumnUpdate->updateNull();
+ break;
+ case TRISTATE_TRUE:
+ if (DbUseBool())
+ m_xColumnUpdate->updateBoolean( true );
+ else
+ m_xColumnUpdate->updateString( getReferenceValue() );
+ break;
+ case TRISTATE_FALSE:
+ if (DbUseBool())
+ m_xColumnUpdate->updateBoolean( false );
+ else
+ m_xColumnUpdate->updateString( getNoCheckReferenceValue() );
+ break;
+ default:
+ OSL_FAIL("OCheckBoxModel::commitControlValueToDbColumn: invalid value !");
+ }
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("OCheckBoxModel::commitControlValueToDbColumn: could not commit !");
+ }
+ return true;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OCheckBoxModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OCheckBoxModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OCheckBoxControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OCheckBoxControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/CheckBox.hxx b/forms/source/component/CheckBox.hxx
new file mode 100644
index 000000000..8023e7446
--- /dev/null
+++ b/forms/source/component/CheckBox.hxx
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "refvaluecomponent.hxx"
+
+
+namespace frm
+{
+
+class OCheckBoxModel final : public OReferenceValueComponent
+{
+ bool DbUseBool();
+
+public:
+ OCheckBoxModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OCheckBoxModel(
+ const OCheckBoxModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OCheckBoxModel() override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OCheckBoxModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL
+ write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL
+ read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+private:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ // OBoundControlModel overridables
+ virtual css::uno::Any translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+};
+
+class OCheckBoxControl : public OBoundControl
+{
+public:
+ explicit OCheckBoxControl(const css::uno::Reference< css::uno::XComponentContext>& _rxContext);
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OCheckBoxControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Columns.cxx b/forms/source/component/Columns.cxx
new file mode 100644
index 000000000..7ab2e0801
--- /dev/null
+++ b/forms/source/component/Columns.cxx
@@ -0,0 +1,900 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "Columns.hxx"
+#include <property.hxx>
+#include <componenttools.hxx>
+#include "findpos.hxx"
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/io/XPersistObject.hpp>
+#include <com/sun/star/io/XMarkableStream.hpp>
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/form/binding/XBindableValue.hpp>
+#include <com/sun/star/beans/XPropertyContainer.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <comphelper/property.hxx>
+#include <comphelper/basicio.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/types.hxx>
+#include <services.hxx>
+#include <tools/debug.hxx>
+#include <o3tl/sorted_vector.hxx>
+
+
+namespace frm
+{
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::form::binding;
+
+const sal_uInt16 WIDTH = 0x0001;
+const sal_uInt16 ALIGN = 0x0002;
+const sal_uInt16 OLD_HIDDEN = 0x0004;
+const sal_uInt16 COMPATIBLE_HIDDEN = 0x0008;
+
+
+const css::uno::Sequence<OUString>& getColumnTypes()
+{
+ static css::uno::Sequence<OUString> aColumnTypes = []()
+ {
+ css::uno::Sequence<OUString> tmp(10);
+ OUString* pNames = tmp.getArray();
+ pNames[TYPE_CHECKBOX] = "CheckBox";
+ pNames[TYPE_COMBOBOX] = "ComboBox";
+ pNames[TYPE_CURRENCYFIELD] = "CurrencyField";
+ pNames[TYPE_DATEFIELD] = "DateField";
+ pNames[TYPE_FORMATTEDFIELD] = "FormattedField";
+ pNames[TYPE_LISTBOX] = "ListBox";
+ pNames[TYPE_NUMERICFIELD] = "NumericField";
+ pNames[TYPE_PATTERNFIELD] = "PatternField";
+ pNames[TYPE_TEXTFIELD] = "TextField";
+ pNames[TYPE_TIMEFIELD] = "TimeField";
+ return tmp;
+ }();
+ return aColumnTypes;
+}
+
+
+sal_Int32 getColumnTypeByModelName(const OUString& aModelName)
+{
+ static const OUStringLiteral aModelPrefix (u"com.sun.star.form.component.");
+ static const OUStringLiteral aCompatibleModelPrefix (u"stardiv.one.form.component.");
+
+ sal_Int32 nTypeId = -1;
+ if (aModelName == FRM_COMPONENT_EDIT)
+ nTypeId = TYPE_TEXTFIELD;
+ else
+ {
+ sal_Int32 nPrefixPos = aModelName.indexOf(aModelPrefix);
+#ifdef DBG_UTIL
+ sal_Int32 nCompatiblePrefixPos = aModelName.indexOf(aCompatibleModelPrefix);
+ DBG_ASSERT( (nPrefixPos != -1) || (nCompatiblePrefixPos != -1),
+ "::getColumnTypeByModelName() : wrong service!");
+#endif
+
+ OUString aColumnType = (nPrefixPos != -1)
+ ? aModelName.copy(aModelPrefix.getLength())
+ : aModelName.copy(aCompatibleModelPrefix.getLength());
+
+ const css::uno::Sequence<OUString>& rColumnTypes = getColumnTypes();
+ nTypeId = ::detail::findPos(aColumnType, rColumnTypes);
+ }
+ return nTypeId;
+}
+
+const Sequence<sal_Int8>& OGridColumn::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theOGridColumnImplementationId;
+ return theOGridColumnImplementationId.getSeq();
+}
+
+
+sal_Int64 SAL_CALL OGridColumn::getSomething( const Sequence<sal_Int8>& _rIdentifier)
+{
+ sal_Int64 nReturn(0);
+
+ if ( comphelper::isUnoTunnelId<OGridColumn>(_rIdentifier) )
+ {
+ nReturn = comphelper::getSomething_cast(this);
+ }
+ else
+ {
+ Reference< XUnoTunnel > xAggTunnel;
+ if ( query_aggregation( m_xAggregate, xAggTunnel ) )
+ return xAggTunnel->getSomething( _rIdentifier );
+ }
+ return nReturn;
+}
+
+
+Sequence<sal_Int8> SAL_CALL OGridColumn::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+
+Sequence<Type> SAL_CALL OGridColumn::getTypes()
+{
+ TypeBag aTypes( OGridColumn_BASE::getTypes() );
+ // erase the types which we do not support
+ aTypes.removeType( cppu::UnoType<XFormComponent>::get() );
+ aTypes.removeType( cppu::UnoType<XServiceInfo>::get() );
+ aTypes.removeType( cppu::UnoType<XBindableValue>::get() );
+ aTypes.removeType( cppu::UnoType<XPropertyContainer>::get() );
+
+ // but re-add their base class(es)
+ aTypes.addType( cppu::UnoType<XChild>::get() );
+
+ Reference< XTypeProvider > xProv;
+ if ( query_aggregation( m_xAggregate, xProv ))
+ aTypes.addTypes( xProv->getTypes() );
+
+ aTypes.removeType( cppu::UnoType<XTextRange>::get() );
+ aTypes.removeType( cppu::UnoType<XSimpleText>::get() );
+ aTypes.removeType( cppu::UnoType<XText>::get() );
+
+ return aTypes.getTypes();
+}
+
+
+Any SAL_CALL OGridColumn::queryAggregation( const Type& _rType )
+{
+ Any aReturn;
+ // some functionality at our aggregate cannot be reasonably fulfilled here.
+ if ( _rType.equals(cppu::UnoType<XFormComponent>::get())
+ || _rType.equals(cppu::UnoType<XServiceInfo>::get())
+ || _rType.equals(cppu::UnoType<XBindableValue>::get())
+ || _rType.equals(cppu::UnoType<XPropertyContainer>::get())
+ || comphelper::isAssignableFrom(cppu::UnoType<XTextRange>::get(),_rType)
+ )
+ return aReturn;
+
+ aReturn = OGridColumn_BASE::queryAggregation(_rType);
+ if (!aReturn.hasValue())
+ {
+ aReturn = OPropertySetAggregationHelper::queryInterface(_rType);
+ if (!aReturn.hasValue() && m_xAggregate.is())
+ aReturn = m_xAggregate->queryAggregation(_rType);
+ }
+
+ return aReturn;
+}
+
+
+OGridColumn::OGridColumn( const Reference<XComponentContext>& _rContext, const OUString& _sModelName )
+ :OGridColumn_BASE(m_aMutex)
+ ,OPropertySetAggregationHelper(OGridColumn_BASE::rBHelper)
+ ,m_aHidden( Any( false ) )
+ ,m_aModelName(_sModelName)
+{
+
+ // Create the UnoControlModel
+ if ( m_aModelName.isEmpty() ) // is there a to-be-aggregated model?
+ return;
+
+ osl_atomic_increment( &m_refCount );
+
+ {
+ m_xAggregate.set( _rContext->getServiceManager()->createInstanceWithContext( m_aModelName, _rContext ), UNO_QUERY );
+ setAggregation( m_xAggregate );
+ }
+
+ if ( m_xAggregate.is() )
+ { // don't omit those brackets - they ensure that the following temporary is properly deleted
+ m_xAggregate->setDelegator( static_cast< ::cppu::OWeakObject* >( this ) );
+ }
+
+ // Set refcount back to zero
+ osl_atomic_decrement( &m_refCount );
+}
+
+
+OGridColumn::OGridColumn( const OGridColumn* _pOriginal )
+ :OGridColumn_BASE( m_aMutex )
+ ,OPropertySetAggregationHelper( OGridColumn_BASE::rBHelper )
+{
+
+ m_aWidth = _pOriginal->m_aWidth;
+ m_aAlign = _pOriginal->m_aAlign;
+ m_aHidden = _pOriginal->m_aHidden;
+ m_aModelName = _pOriginal->m_aModelName;
+ m_aLabel = _pOriginal->m_aLabel;
+
+ osl_atomic_increment( &m_refCount );
+ {
+ {
+ m_xAggregate = createAggregateClone( _pOriginal );
+ setAggregation( m_xAggregate );
+ }
+
+ if ( m_xAggregate.is() )
+ { // don't omit this brackets - they ensure that the following temporary is properly deleted
+ m_xAggregate->setDelegator( static_cast< ::cppu::OWeakObject* >( this ) );
+ }
+ }
+ osl_atomic_decrement( &m_refCount );
+}
+
+
+OGridColumn::~OGridColumn()
+{
+ if (!OGridColumn_BASE::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+ // Free the aggregate
+ if (m_xAggregate.is())
+ {
+ css::uno::Reference<css::uno::XInterface> xIface;
+ m_xAggregate->setDelegator(xIface);
+ }
+
+}
+
+// XEventListener
+
+void SAL_CALL OGridColumn::disposing(const EventObject& _rSource)
+{
+ OPropertySetAggregationHelper::disposing(_rSource);
+
+ Reference<XEventListener> xEvtLstner;
+ if (query_aggregation(m_xAggregate, xEvtLstner))
+ xEvtLstner->disposing(_rSource);
+}
+
+// OGridColumn_BASE
+
+void OGridColumn::disposing()
+{
+ OGridColumn_BASE::disposing();
+ OPropertySetAggregationHelper::disposing();
+
+ Reference<XComponent> xComp;
+ if (query_aggregation(m_xAggregate, xComp))
+ xComp->dispose();
+}
+
+
+void OGridColumn::clearAggregateProperties( Sequence< Property >& _rProps, bool bAllowDropDown )
+{
+ // some properties are not to be exposed to the outer world
+ static const o3tl::sorted_vector< OUString > aForbiddenProperties {
+ PROPERTY_ALIGN,
+ PROPERTY_AUTOCOMPLETE,
+ PROPERTY_BACKGROUNDCOLOR,
+ PROPERTY_BORDER,
+ PROPERTY_BORDERCOLOR,
+ PROPERTY_ECHO_CHAR,
+ PROPERTY_FILLCOLOR,
+ PROPERTY_FONT,
+ PROPERTY_FONT_NAME,
+ PROPERTY_FONT_STYLENAME,
+ PROPERTY_FONT_FAMILY,
+ PROPERTY_FONT_CHARSET,
+ PROPERTY_FONT_HEIGHT,
+ PROPERTY_FONT_WEIGHT,
+ PROPERTY_FONT_SLANT,
+ PROPERTY_FONT_UNDERLINE,
+ PROPERTY_FONT_STRIKEOUT,
+ PROPERTY_FONT_WORDLINEMODE,
+ PROPERTY_TEXTLINECOLOR,
+ PROPERTY_FONTEMPHASISMARK,
+ PROPERTY_FONTRELIEF,
+ PROPERTY_HARDLINEBREAKS,
+ PROPERTY_HSCROLL,
+ PROPERTY_LABEL,
+ PROPERTY_LINECOLOR,
+ PROPERTY_MULTISELECTION,
+ PROPERTY_PRINTABLE,
+ PROPERTY_TABINDEX,
+ PROPERTY_TABSTOP,
+ PROPERTY_TEXTCOLOR,
+ PROPERTY_VSCROLL,
+ PROPERTY_CONTROLLABEL,
+ PROPERTY_RICH_TEXT,
+ PROPERTY_VERTICAL_ALIGN,
+ PROPERTY_IMAGE_URL,
+ PROPERTY_IMAGE_POSITION,
+ PROPERTY_ENABLEVISIBLE
+ };
+
+ Sequence< Property > aNewProps( _rProps.getLength() );
+ Property* pNewProps = aNewProps.getArray();
+
+ const Property* pProps = _rProps.getConstArray();
+ const Property* pPropsEnd = pProps + _rProps.getLength();
+ for ( ; pProps != pPropsEnd; ++pProps )
+ {
+ if ( aForbiddenProperties.find( pProps->Name ) == aForbiddenProperties.end()
+ && (bAllowDropDown || pProps->Name != PROPERTY_DROPDOWN))
+ *pNewProps++ = *pProps;
+ }
+
+ aNewProps.realloc( pNewProps - aNewProps.getArray() );
+ _rProps = aNewProps;
+}
+
+
+void OGridColumn::setOwnProperties(Sequence<Property>& aDescriptor)
+{
+ aDescriptor.realloc(5);
+ Property* pProperties = aDescriptor.getArray();
+ *pProperties++ = css::beans::Property(PROPERTY_LABEL, PROPERTY_ID_LABEL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_WIDTH, PROPERTY_ID_WIDTH, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_ALIGN, PROPERTY_ID_ALIGN, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_HIDDEN, PROPERTY_ID_HIDDEN, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_COLUMNSERVICENAME, PROPERTY_ID_COLUMNSERVICENAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY);
+}
+
+// Reference<XPropertySet>
+
+void OGridColumn::getFastPropertyValue(Any& rValue, sal_Int32 nHandle ) const
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_COLUMNSERVICENAME:
+ rValue <<= m_aModelName;
+ break;
+ case PROPERTY_ID_LABEL:
+ rValue <<= m_aLabel;
+ break;
+ case PROPERTY_ID_WIDTH:
+ rValue = m_aWidth;
+ break;
+ case PROPERTY_ID_ALIGN:
+ rValue = m_aAlign;
+ break;
+ case PROPERTY_ID_HIDDEN:
+ rValue = m_aHidden;
+ break;
+ default:
+ OPropertySetAggregationHelper::getFastPropertyValue(rValue, nHandle);
+ }
+}
+
+
+sal_Bool OGridColumn::convertFastPropertyValue( Any& rConvertedValue, Any& rOldValue,
+ sal_Int32 nHandle, const Any& rValue )
+{
+ bool bModified(false);
+ switch (nHandle)
+ {
+ case PROPERTY_ID_LABEL:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aLabel);
+ break;
+ case PROPERTY_ID_WIDTH:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aWidth, cppu::UnoType<sal_Int32>::get());
+ break;
+ case PROPERTY_ID_ALIGN:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aAlign, cppu::UnoType<sal_Int32>::get());
+ // strange enough, css.awt.TextAlign is a 32-bit integer, while the Align property (both here for grid controls
+ // and for ordinary toolkit controls) is a 16-bit integer. So, allow for 32 bit, but normalize it to 16 bit
+ if ( bModified )
+ {
+ sal_Int32 nAlign( 0 );
+ if ( rConvertedValue >>= nAlign )
+ rConvertedValue <<= static_cast<sal_Int16>(nAlign);
+ }
+ break;
+ case PROPERTY_ID_HIDDEN:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, getBOOL(m_aHidden));
+ break;
+ }
+ return bModified;
+}
+
+
+void OGridColumn::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_LABEL:
+ DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_STRING, "invalid type" );
+ rValue >>= m_aLabel;
+ break;
+ case PROPERTY_ID_WIDTH:
+ m_aWidth = rValue;
+ break;
+ case PROPERTY_ID_ALIGN:
+ m_aAlign = rValue;
+ break;
+ case PROPERTY_ID_HIDDEN:
+ m_aHidden = rValue;
+ break;
+ }
+}
+
+
+// XPropertyState
+
+Any OGridColumn::getPropertyDefaultByHandle( sal_Int32 nHandle ) const
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_WIDTH:
+ case PROPERTY_ID_ALIGN:
+ return Any();
+ case PROPERTY_ID_HIDDEN:
+ return Any(false);
+ default:
+ return OPropertySetAggregationHelper::getPropertyDefaultByHandle(nHandle);
+ }
+}
+
+// XCloneable
+
+Reference< XCloneable > SAL_CALL OGridColumn::createClone( )
+{
+ return createCloneColumn();
+}
+
+// XPersistObject
+
+void OGridColumn::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ // 1. Write the UnoControl
+ Reference<XMarkableStream> xMark(_rxOutStream, UNO_QUERY);
+ sal_Int32 nMark = xMark->createMark();
+
+ sal_Int32 nLen = 0;
+ _rxOutStream->writeLong(nLen);
+
+ Reference<XPersistObject> xPersist;
+ if (query_aggregation(m_xAggregate, xPersist))
+ xPersist->write(_rxOutStream);
+
+ // Calculate the length
+ nLen = xMark->offsetToMark(nMark) - 4;
+ xMark->jumpToMark(nMark);
+ _rxOutStream->writeLong(nLen);
+ xMark->jumpToFurthest();
+ xMark->deleteMark(nMark);
+
+ // 2. Write a version number
+ _rxOutStream->writeShort(0x0002);
+
+ sal_uInt16 nAnyMask = 0;
+ if (m_aWidth.getValueType().getTypeClass() == TypeClass_LONG)
+ nAnyMask |= WIDTH;
+
+ if (m_aAlign.getValueTypeClass() == TypeClass_SHORT)
+ nAnyMask |= ALIGN;
+
+ nAnyMask |= COMPATIBLE_HIDDEN;
+
+ _rxOutStream->writeShort(nAnyMask);
+
+ if (nAnyMask & WIDTH)
+ _rxOutStream->writeLong(getINT32(m_aWidth));
+ if (nAnyMask & ALIGN)
+ _rxOutStream->writeShort(getINT16(m_aAlign));
+
+ // Name
+ _rxOutStream << m_aLabel;
+
+ // the new place for the hidden flag : after m_aLabel, so older office version read the correct label, too
+ if (nAnyMask & COMPATIBLE_HIDDEN)
+ _rxOutStream->writeBoolean(getBOOL(m_aHidden));
+}
+
+
+void OGridColumn::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ // 1. Read the UnoControl
+ sal_Int32 nLen = _rxInStream->readLong();
+ if (nLen)
+ {
+ Reference<XMarkableStream> xMark(_rxInStream, UNO_QUERY);
+ sal_Int32 nMark = xMark->createMark();
+ Reference<XPersistObject> xPersist;
+ if (query_aggregation(m_xAggregate, xPersist))
+ xPersist->read(_rxInStream);
+
+ xMark->jumpToMark(nMark);
+ _rxInStream->skipBytes(nLen);
+ xMark->deleteMark(nMark);
+ }
+
+ // 2. Write a version number
+ _rxInStream->readShort(); // version;
+ sal_uInt16 nAnyMask = _rxInStream->readShort();
+
+ if (nAnyMask & WIDTH)
+ {
+ sal_Int32 nValue = _rxInStream->readLong();
+ m_aWidth <<= nValue;
+ }
+
+ if (nAnyMask & ALIGN)
+ {
+ sal_Int16 nValue = _rxInStream->readShort();
+ m_aAlign <<= nValue;
+ }
+ if (nAnyMask & OLD_HIDDEN)
+ {
+ bool bValue = _rxInStream->readBoolean();
+ m_aHidden <<= bValue;
+ }
+
+ // Name
+ _rxInStream >> m_aLabel;
+
+ if (nAnyMask & COMPATIBLE_HIDDEN)
+ {
+ bool bValue = _rxInStream->readBoolean();
+ m_aHidden <<= bValue;
+ }
+}
+
+TextFieldColumn::TextFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext)
+ :OGridColumn(_rContext, FRM_SUN_COMPONENT_TEXTFIELD)
+{
+}
+TextFieldColumn::TextFieldColumn(const TextFieldColumn* _pCloneFrom)
+ :OGridColumn( _pCloneFrom )
+{
+}
+css::uno::Reference< css::beans::XPropertySetInfo> TextFieldColumn::getPropertySetInfo()
+{
+ css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+}
+::cppu::IPropertyArrayHelper& TextFieldColumn::getInfoHelper()
+{
+ return *getArrayHelper();
+}
+void TextFieldColumn::fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const
+{
+ if (m_xAggregateSet.is())
+ {
+ _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties();
+ clearAggregateProperties(_rAggregateProps, false);
+ setOwnProperties(_rProps);
+ }
+}
+rtl::Reference<OGridColumn> TextFieldColumn::createCloneColumn() const
+{
+ return new TextFieldColumn(this);
+}
+
+PatternFieldColumn::PatternFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext)
+ :OGridColumn(_rContext, FRM_SUN_COMPONENT_PATTERNFIELD)
+{
+}
+PatternFieldColumn::PatternFieldColumn(const PatternFieldColumn* _pCloneFrom)
+ :OGridColumn( _pCloneFrom )
+{
+}
+css::uno::Reference< css::beans::XPropertySetInfo> PatternFieldColumn::getPropertySetInfo()
+{
+ css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+}
+::cppu::IPropertyArrayHelper& PatternFieldColumn::getInfoHelper()
+{
+ return *getArrayHelper();
+}
+void PatternFieldColumn::fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const
+{
+ if (m_xAggregateSet.is())
+ {
+ _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties();
+ clearAggregateProperties(_rAggregateProps, false);
+ setOwnProperties(_rProps);
+ }
+}
+rtl::Reference<OGridColumn> PatternFieldColumn::createCloneColumn() const
+{
+ return new PatternFieldColumn(this);
+}
+
+DateFieldColumn::DateFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext)
+ :OGridColumn(_rContext, FRM_SUN_COMPONENT_DATEFIELD)
+{
+}
+DateFieldColumn::DateFieldColumn(const DateFieldColumn* _pCloneFrom)
+ :OGridColumn( _pCloneFrom )
+{
+}
+css::uno::Reference< css::beans::XPropertySetInfo> DateFieldColumn::getPropertySetInfo()
+{
+ css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+}
+::cppu::IPropertyArrayHelper& DateFieldColumn::getInfoHelper()
+{
+ return *getArrayHelper();
+}
+void DateFieldColumn::fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const
+{
+ if (m_xAggregateSet.is())
+ {
+ _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties();
+ clearAggregateProperties(_rAggregateProps, true);
+ setOwnProperties(_rProps);
+ }
+}
+rtl::Reference<OGridColumn> DateFieldColumn::createCloneColumn() const
+{
+ return new DateFieldColumn(this);
+}
+
+TimeFieldColumn::TimeFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext)
+ :OGridColumn(_rContext, FRM_SUN_COMPONENT_TIMEFIELD)
+{
+}
+TimeFieldColumn::TimeFieldColumn(const TimeFieldColumn* _pCloneFrom)
+ :OGridColumn( _pCloneFrom )
+{
+}
+css::uno::Reference< css::beans::XPropertySetInfo> TimeFieldColumn::getPropertySetInfo()
+{
+ css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+}
+::cppu::IPropertyArrayHelper& TimeFieldColumn::getInfoHelper()
+{
+ return *getArrayHelper();
+}
+void TimeFieldColumn::fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const
+{
+ if (m_xAggregateSet.is())
+ {
+ _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties();
+ clearAggregateProperties(_rAggregateProps, false);
+ setOwnProperties(_rProps);
+ }
+}
+rtl::Reference<OGridColumn> TimeFieldColumn::createCloneColumn() const
+{
+ return new TimeFieldColumn(this);
+}
+
+NumericFieldColumn::NumericFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext)
+ :OGridColumn(_rContext, FRM_SUN_COMPONENT_NUMERICFIELD)
+{
+}
+NumericFieldColumn::NumericFieldColumn(const NumericFieldColumn* _pCloneFrom)
+ :OGridColumn( _pCloneFrom )
+{
+}
+css::uno::Reference< css::beans::XPropertySetInfo> NumericFieldColumn::getPropertySetInfo()
+{
+ css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+}
+::cppu::IPropertyArrayHelper& NumericFieldColumn::getInfoHelper()
+{
+ return *getArrayHelper();
+}
+void NumericFieldColumn::fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const
+{
+ if (m_xAggregateSet.is())
+ {
+ _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties();
+ clearAggregateProperties(_rAggregateProps, false);
+ setOwnProperties(_rProps);
+ }
+}
+rtl::Reference<OGridColumn> NumericFieldColumn::createCloneColumn() const
+{
+ return new NumericFieldColumn(this);
+}
+
+CurrencyFieldColumn::CurrencyFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext)
+ :OGridColumn(_rContext, FRM_SUN_COMPONENT_CURRENCYFIELD)
+{
+}
+CurrencyFieldColumn::CurrencyFieldColumn(const CurrencyFieldColumn* _pCloneFrom)
+ :OGridColumn( _pCloneFrom )
+{
+}
+css::uno::Reference< css::beans::XPropertySetInfo> CurrencyFieldColumn::getPropertySetInfo()
+{
+ css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+}
+::cppu::IPropertyArrayHelper& CurrencyFieldColumn::getInfoHelper()
+{
+ return *getArrayHelper();
+}
+void CurrencyFieldColumn::fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const
+{
+ if (m_xAggregateSet.is())
+ {
+ _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties();
+ clearAggregateProperties(_rAggregateProps, false);
+ setOwnProperties(_rProps);
+ }
+}
+rtl::Reference<OGridColumn> CurrencyFieldColumn::createCloneColumn() const
+{
+ return new CurrencyFieldColumn(this);
+}
+
+CheckBoxColumn::CheckBoxColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext)
+ :OGridColumn(_rContext, FRM_SUN_COMPONENT_CHECKBOX)
+{
+}
+CheckBoxColumn::CheckBoxColumn(const CheckBoxColumn* _pCloneFrom)
+ :OGridColumn( _pCloneFrom )
+{
+}
+css::uno::Reference< css::beans::XPropertySetInfo> CheckBoxColumn::getPropertySetInfo()
+{
+ css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+}
+::cppu::IPropertyArrayHelper& CheckBoxColumn::getInfoHelper()
+{
+ return *getArrayHelper();
+}
+void CheckBoxColumn::fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const
+{
+ if (m_xAggregateSet.is())
+ {
+ _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties();
+ clearAggregateProperties(_rAggregateProps, false);
+ setOwnProperties(_rProps);
+ }
+}
+rtl::Reference<OGridColumn> CheckBoxColumn::createCloneColumn() const
+{
+ return new CheckBoxColumn(this);
+}
+
+ComboBoxColumn::ComboBoxColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext)
+ :OGridColumn(_rContext, FRM_SUN_COMPONENT_COMBOBOX)
+{
+}
+ComboBoxColumn::ComboBoxColumn(const ComboBoxColumn* _pCloneFrom)
+ :OGridColumn( _pCloneFrom )
+{
+}
+css::uno::Reference< css::beans::XPropertySetInfo> ComboBoxColumn::getPropertySetInfo()
+{
+ css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+}
+::cppu::IPropertyArrayHelper& ComboBoxColumn::getInfoHelper()
+{
+ return *getArrayHelper();
+}
+void ComboBoxColumn::fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const
+{
+ if (m_xAggregateSet.is())
+ {
+ _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties();
+ clearAggregateProperties(_rAggregateProps, false);
+ setOwnProperties(_rProps);
+ }
+}
+rtl::Reference<OGridColumn> ComboBoxColumn::createCloneColumn() const
+{
+ return new ComboBoxColumn(this);
+}
+
+ListBoxColumn::ListBoxColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext)
+ :OGridColumn(_rContext, FRM_SUN_COMPONENT_LISTBOX)
+{
+}
+ListBoxColumn::ListBoxColumn(const ListBoxColumn* _pCloneFrom)
+ :OGridColumn( _pCloneFrom )
+{
+}
+css::uno::Reference< css::beans::XPropertySetInfo> ListBoxColumn::getPropertySetInfo()
+{
+ css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+}
+::cppu::IPropertyArrayHelper& ListBoxColumn::getInfoHelper()
+{
+ return *getArrayHelper();
+}
+void ListBoxColumn::fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const
+{
+ if (m_xAggregateSet.is())
+ {
+ _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties();
+ clearAggregateProperties(_rAggregateProps, false);
+ setOwnProperties(_rProps);
+ }
+}
+rtl::Reference<OGridColumn> ListBoxColumn::createCloneColumn() const
+{
+ return new ListBoxColumn(this);
+}
+
+FormattedFieldColumn::FormattedFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext)
+ :OGridColumn(_rContext, FRM_SUN_COMPONENT_FORMATTEDFIELD)
+{
+}
+FormattedFieldColumn::FormattedFieldColumn(const FormattedFieldColumn* _pCloneFrom)
+ :OGridColumn( _pCloneFrom )
+{
+}
+css::uno::Reference< css::beans::XPropertySetInfo> FormattedFieldColumn::getPropertySetInfo()
+{
+ css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+}
+::cppu::IPropertyArrayHelper& FormattedFieldColumn::getInfoHelper()
+{
+ return *getArrayHelper();
+}
+void FormattedFieldColumn::fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const
+{
+ if (m_xAggregateSet.is())
+ {
+ _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties();
+ clearAggregateProperties(_rAggregateProps, false);
+ setOwnProperties(_rProps);
+ }
+}
+rtl::Reference<OGridColumn> FormattedFieldColumn::createCloneColumn() const
+{
+ return new FormattedFieldColumn(this);
+}
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Columns.hxx b/forms/source/component/Columns.hxx
new file mode 100644
index 000000000..fd2caa3b9
--- /dev/null
+++ b/forms/source/component/Columns.hxx
@@ -0,0 +1,322 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <cloneable.hxx>
+
+#include <com/sun/star/io/XObjectInputStream.hpp>
+#include <com/sun/star/io/XObjectOutputStream.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+
+#include <comphelper/propagg.hxx>
+#include <comphelper/proparrhlp.hxx>
+#include <comphelper/uno3.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase2.hxx>
+#include <rtl/ref.hxx>
+
+using namespace comphelper;
+
+
+namespace frm
+{
+
+typedef ::cppu::WeakAggComponentImplHelper2 < css::lang::XUnoTunnel
+ , css::util::XCloneable > OGridColumn_BASE;
+class OGridColumn :public ::cppu::BaseMutex
+ ,public OGridColumn_BASE
+ ,public OPropertySetAggregationHelper
+ ,public OCloneableAggregation
+{
+// [properties]
+ css::uno::Any m_aWidth; // column width
+ css::uno::Any m_aAlign; // column alignment
+ css::uno::Any m_aHidden; // column hidden?
+// [properties]
+
+ OUString m_aModelName;
+
+// [properties]
+ OUString m_aLabel; // Column name
+// [properties]
+
+public:
+ OGridColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext, const OUString& _sModelName);
+ explicit OGridColumn(const OGridColumn* _pOriginal );
+ virtual ~OGridColumn() override;
+
+ // UNO binding
+ DECLARE_UNO3_AGG_DEFAULTS(OGridControlModel, OGridColumn_BASE)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+ static const css::uno::Sequence<sal_Int8>& getUnoTunnelId();
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence<sal_Int8>& _rIdentifier) override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override;
+ virtual css::uno::Sequence< css::uno::Type> SAL_CALL getTypes() override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource) override;
+
+ // XPersistObject
+ void write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream);
+ void read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream);
+
+ // XPropertySet
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override = 0;
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle ) const override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(css::uno::Any& rConvertedValue, css::uno::Any& rOldValue,
+ sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const css::uno::Any& rValue) override;
+
+ using OPropertySetAggregationHelper::getFastPropertyValue;
+
+ // css::beans::XPropertyState
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 nHandle ) const override;
+
+ // XCloneable
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ const OUString& getModelName() const { return m_aModelName; }
+
+protected:
+ static void clearAggregateProperties(css::uno::Sequence< css::beans::Property>& seqProps, bool bAllowDropDown);
+ static void setOwnProperties(css::uno::Sequence< css::beans::Property>& seqProps);
+
+ virtual rtl::Reference<OGridColumn> createCloneColumn() const = 0;
+};
+
+// column type ids
+#define TYPE_CHECKBOX 0
+#define TYPE_COMBOBOX 1
+#define TYPE_CURRENCYFIELD 2
+#define TYPE_DATEFIELD 3
+#define TYPE_FORMATTEDFIELD 4
+#define TYPE_LISTBOX 5
+#define TYPE_NUMERICFIELD 6
+#define TYPE_PATTERNFIELD 7
+#define TYPE_TEXTFIELD 8
+#define TYPE_TIMEFIELD 9
+
+// List of all known columns
+const css::uno::Sequence<OUString>& getColumnTypes();
+sal_Int32 getColumnTypeByModelName(const OUString& aModelName);
+
+// Columns
+class TextFieldColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< TextFieldColumn >
+{
+public:
+ explicit TextFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext );
+ explicit TextFieldColumn(const TextFieldColumn* _pCloneFrom);
+
+ virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ virtual void fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+
+ virtual rtl::Reference<OGridColumn> createCloneColumn() const override;
+};
+
+class PatternFieldColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< PatternFieldColumn >
+{
+public:
+ explicit PatternFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext );
+ explicit PatternFieldColumn(const PatternFieldColumn* _pCloneFrom);
+
+ virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ virtual void fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+
+ virtual rtl::Reference<OGridColumn> createCloneColumn() const override;
+};
+
+class DateFieldColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< DateFieldColumn >
+{
+public:
+ explicit DateFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext );
+ explicit DateFieldColumn(const DateFieldColumn* _pCloneFrom);
+
+ virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ virtual void fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+
+ virtual rtl::Reference<OGridColumn> createCloneColumn() const override;
+};
+
+class TimeFieldColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< TimeFieldColumn >
+{
+public:
+ explicit TimeFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext );
+ explicit TimeFieldColumn(const TimeFieldColumn* _pCloneFrom);
+
+ virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ virtual void fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+
+ virtual rtl::Reference<OGridColumn> createCloneColumn() const override;
+};
+
+class NumericFieldColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< NumericFieldColumn >
+{
+public:
+ explicit NumericFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext );
+ explicit NumericFieldColumn(const NumericFieldColumn* _pCloneFrom);
+
+ virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ virtual void fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+
+ virtual rtl::Reference<OGridColumn> createCloneColumn() const override;
+};
+
+class CurrencyFieldColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< CurrencyFieldColumn >
+{
+public:
+ explicit CurrencyFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext );
+ explicit CurrencyFieldColumn(const CurrencyFieldColumn* _pCloneFrom);
+
+ virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ virtual void fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+
+ virtual rtl::Reference<OGridColumn> createCloneColumn() const override;
+};
+
+class CheckBoxColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< CheckBoxColumn >
+{
+public:
+ explicit CheckBoxColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext );
+ explicit CheckBoxColumn(const CheckBoxColumn* _pCloneFrom);
+
+ virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ virtual void fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+
+ virtual rtl::Reference<OGridColumn> createCloneColumn() const override;
+};
+
+class ComboBoxColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< ComboBoxColumn >
+{
+public:
+ explicit ComboBoxColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext );
+ explicit ComboBoxColumn(const ComboBoxColumn* _pCloneFrom);
+
+ virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ virtual void fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+
+ virtual rtl::Reference<OGridColumn> createCloneColumn() const override;
+};
+
+class ListBoxColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< ListBoxColumn >
+{
+public:
+ explicit ListBoxColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext );
+ explicit ListBoxColumn(const ListBoxColumn* _pCloneFrom);
+
+ virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ virtual void fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+
+ virtual rtl::Reference<OGridColumn> createCloneColumn() const override;
+};
+
+class FormattedFieldColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< FormattedFieldColumn >
+{
+public:
+ explicit FormattedFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _rContext );
+ explicit FormattedFieldColumn(const FormattedFieldColumn* _pCloneFrom);
+
+ virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ virtual void fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+
+ virtual rtl::Reference<OGridColumn> createCloneColumn() const override;
+};
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/ComboBox.cxx b/forms/source/component/ComboBox.cxx
new file mode 100644
index 000000000..d38c16abc
--- /dev/null
+++ b/forms/source/component/ComboBox.cxx
@@ -0,0 +1,888 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "ComboBox.hxx"
+#include <property.hxx>
+#include <services.hxx>
+
+#include <frm_resource.hxx>
+#include <strings.hrc>
+#include "BaseListBox.hxx"
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+#include <connectivity/dbtools.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/sharedunocomponent.hxx>
+
+#include <limits.h>
+
+using namespace dbtools;
+
+
+namespace frm
+{
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::form::binding;
+
+
+Sequence<Type> OComboBoxModel::_getTypes()
+{
+ return ::comphelper::concatSequences(
+ OBoundControlModel::_getTypes(),
+ OEntryListHelper::getTypes(),
+ OErrorBroadcaster::getTypes()
+ );
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL OComboBoxModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControlModel::getSupportedServiceNames();
+
+ sal_Int32 nOldLen = aSupported.getLength();
+ aSupported.realloc( nOldLen + 9 );
+ OUString* pStoreTo = aSupported.getArray() + nOldLen;
+
+ *pStoreTo++ = BINDABLE_CONTROL_MODEL;
+ *pStoreTo++ = DATA_AWARE_CONTROL_MODEL;
+ *pStoreTo++ = VALIDATABLE_CONTROL_MODEL;
+
+ *pStoreTo++ = BINDABLE_DATA_AWARE_CONTROL_MODEL;
+ *pStoreTo++ = VALIDATABLE_BINDABLE_CONTROL_MODEL;
+
+ *pStoreTo++ = FRM_SUN_COMPONENT_COMBOBOX;
+ *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_COMBOBOX;
+ *pStoreTo++ = BINDABLE_DATABASE_COMBO_BOX;
+
+ *pStoreTo++ = FRM_COMPONENT_COMBOBOX;
+
+ return aSupported;
+}
+
+
+Any SAL_CALL OComboBoxModel::queryAggregation(const Type& _rType)
+{
+ Any aReturn = OBoundControlModel::queryAggregation( _rType );
+ if ( !aReturn.hasValue() )
+ aReturn = OEntryListHelper::queryInterface( _rType );
+ if ( !aReturn.hasValue() )
+ aReturn = OErrorBroadcaster::queryInterface( _rType );
+ return aReturn;
+}
+
+
+OComboBoxModel::OComboBoxModel(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControlModel( _rxFactory, VCL_CONTROLMODEL_COMBOBOX, FRM_SUN_CONTROL_COMBOBOX, true, true, true )
+ // use the old control name for compatibility reasons
+ ,OEntryListHelper( static_cast<OControlModel&>(*this) )
+ ,OErrorBroadcaster( OComponentHelper::rBHelper )
+ ,m_eListSourceType(ListSourceType_TABLE)
+ ,m_bEmptyIsNull(true)
+{
+ m_nClassId = FormComponentType::COMBOBOX;
+ initValueProperty( PROPERTY_TEXT, PROPERTY_ID_TEXT );
+}
+
+
+OComboBoxModel::OComboBoxModel( const OComboBoxModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OBoundControlModel( _pOriginal, _rxFactory )
+ ,OEntryListHelper( *_pOriginal, static_cast<OControlModel&>(*this) )
+ ,OErrorBroadcaster( OComponentHelper::rBHelper )
+ ,m_aListSource( _pOriginal->m_aListSource )
+ ,m_aDefaultText( _pOriginal->m_aDefaultText )
+ ,m_eListSourceType( _pOriginal->m_eListSourceType )
+ ,m_bEmptyIsNull( _pOriginal->m_bEmptyIsNull )
+{
+}
+
+
+OComboBoxModel::~OComboBoxModel()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+}
+
+// XCloneable
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OComboBoxModel::createClone()
+{
+ rtl::Reference<OComboBoxModel> pClone = new OComboBoxModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+void OComboBoxModel::disposing()
+{
+ OBoundControlModel::disposing();
+ OEntryListHelper::disposing();
+ OErrorBroadcaster::disposing();
+}
+
+
+void OComboBoxModel::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const
+{
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_LISTSOURCETYPE:
+ _rValue <<= m_eListSourceType;
+ break;
+
+ case PROPERTY_ID_LISTSOURCE:
+ _rValue <<= m_aListSource;
+ break;
+
+ case PROPERTY_ID_EMPTY_IS_NULL:
+ _rValue <<= m_bEmptyIsNull;
+ break;
+
+ case PROPERTY_ID_DEFAULT_TEXT:
+ _rValue <<= m_aDefaultText;
+ break;
+
+ case PROPERTY_ID_STRINGITEMLIST:
+ _rValue <<= comphelper::containerToSequence(getStringItemList());
+ break;
+
+ case PROPERTY_ID_TYPEDITEMLIST:
+ _rValue <<= getTypedItemList();
+ break;
+
+ default:
+ OBoundControlModel::getFastPropertyValue(_rValue, _nHandle);
+ }
+}
+
+
+void OComboBoxModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
+{
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_LISTSOURCETYPE :
+ DBG_ASSERT(_rValue.getValueType().equals(::cppu::UnoType<ListSourceType>::get()),
+ "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ _rValue >>= m_eListSourceType;
+ break;
+
+ case PROPERTY_ID_LISTSOURCE :
+ DBG_ASSERT(_rValue.getValueType().getTypeClass() == TypeClass_STRING,
+ "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ _rValue >>= m_aListSource;
+ // The ListSource has changed -> reload
+ if (ListSourceType_VALUELIST != m_eListSourceType)
+ {
+ if ( m_xCursor.is() && !hasField() && !hasExternalListSource() )
+ // combo box is already connected to a database, and no external list source
+ // data source changed -> refresh
+ loadData( false );
+ }
+ break;
+
+ case PROPERTY_ID_EMPTY_IS_NULL :
+ DBG_ASSERT(_rValue.getValueType().getTypeClass() == TypeClass_BOOLEAN,
+ "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ _rValue >>= m_bEmptyIsNull;
+ break;
+
+ case PROPERTY_ID_DEFAULT_TEXT :
+ DBG_ASSERT(_rValue.getValueType().getTypeClass() == TypeClass_STRING,
+ "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ _rValue >>= m_aDefaultText;
+ resetNoBroadcast();
+ break;
+
+ case PROPERTY_ID_STRINGITEMLIST:
+ {
+ ControlModelLock aLock( *this );
+ setNewStringItemList( _rValue, aLock );
+ // FIXME: this is bogus. setNewStringItemList expects a guard which has the *only*
+ // lock to the mutex, but setFastPropertyValue_NoBroadcast is already called with
+ // a lock - so we effectively has two locks here, of which setNewStringItemList can
+ // only control one.
+ }
+ break;
+
+ case PROPERTY_ID_TYPEDITEMLIST:
+ {
+ ControlModelLock aLock( *this );
+ setNewTypedItemList( _rValue, aLock );
+ // Same FIXME as above.
+ }
+ break;
+
+ default:
+ OBoundControlModel::setFastPropertyValue_NoBroadcast(_nHandle, _rValue);
+ }
+}
+
+sal_Bool OComboBoxModel::convertFastPropertyValue(
+ Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue)
+{
+ bool bModified(false);
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_LISTSOURCETYPE :
+ bModified = tryPropertyValueEnum(_rConvertedValue, _rOldValue, _rValue, m_eListSourceType);
+ break;
+
+ case PROPERTY_ID_LISTSOURCE :
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aListSource);
+ break;
+
+ case PROPERTY_ID_EMPTY_IS_NULL :
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bEmptyIsNull);
+ break;
+
+ case PROPERTY_ID_DEFAULT_TEXT :
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aDefaultText);
+ break;
+
+ case PROPERTY_ID_STRINGITEMLIST:
+ bModified = convertNewListSourceProperty( _rConvertedValue, _rOldValue, _rValue );
+ break;
+
+ case PROPERTY_ID_TYPEDITEMLIST :
+ if (hasExternalListSource())
+ throw IllegalArgumentException();
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, getTypedItemList());
+ break;
+
+ default:
+ bModified = OBoundControlModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue);
+ break;
+ }
+ return bModified;
+}
+
+void OComboBoxModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OBoundControlModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 7);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_LISTSOURCETYPE, PROPERTY_ID_LISTSOURCETYPE, cppu::UnoType<ListSourceType>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_LISTSOURCE, PROPERTY_ID_LISTSOURCE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_EMPTY_IS_NULL, PROPERTY_ID_EMPTY_IS_NULL, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_TEXT, PROPERTY_ID_DEFAULT_TEXT, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_STRINGITEMLIST, PROPERTY_ID_STRINGITEMLIST, cppu::UnoType<Sequence< OUString >>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TYPEDITEMLIST, PROPERTY_ID_TYPEDITEMLIST, cppu::UnoType<Sequence< Any >>::get(), css::beans::PropertyAttribute::OPTIONAL);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+void OComboBoxModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const
+{
+ OBoundControlModel::describeAggregateProperties( _rAggregateProps );
+
+ // superseded properties:
+ RemoveProperty( _rAggregateProps, PROPERTY_STRINGITEMLIST );
+ RemoveProperty( _rAggregateProps, PROPERTY_TYPEDITEMLIST );
+}
+
+
+OUString SAL_CALL OComboBoxModel::getServiceName()
+{
+ return FRM_COMPONENT_COMBOBOX; // old (non-sun) name for compatibility !
+}
+
+
+void SAL_CALL OComboBoxModel::write(const Reference<css::io::XObjectOutputStream>& _rxOutStream)
+{
+ OBoundControlModel::write(_rxOutStream);
+
+ // Version
+ // Version 0x0002: EmptyIsNull
+ // Version 0x0003: ListSource->Seq
+ // Version 0x0004: DefaultText
+ // Version 0x0005: HelpText
+ _rxOutStream->writeShort(0x0006);
+
+ // Mask for Any
+ sal_uInt16 nAnyMask = 0;
+ if (m_aBoundColumn.getValueType().getTypeClass() == TypeClass_SHORT)
+ nAnyMask |= BOUNDCOLUMN;
+ _rxOutStream << nAnyMask;
+
+ css::uno::Sequence<OUString> aListSourceSeq(&m_aListSource, 1);
+ _rxOutStream << aListSourceSeq;
+ _rxOutStream << static_cast<sal_Int16>(m_eListSourceType);
+
+ if ((nAnyMask & BOUNDCOLUMN) == BOUNDCOLUMN)
+ {
+ sal_Int16 nBoundColumn = 0;
+ m_aBoundColumn >>= nBoundColumn;
+ _rxOutStream << nBoundColumn;
+ }
+
+ _rxOutStream << m_bEmptyIsNull;
+ _rxOutStream << m_aDefaultText;
+ writeHelpTextCompatibly(_rxOutStream);
+
+ // from version 0x0006 : common properties
+ writeCommonProperties(_rxOutStream);
+}
+
+
+void SAL_CALL OComboBoxModel::read(const Reference<css::io::XObjectInputStream>& _rxInStream)
+{
+ OBoundControlModel::read(_rxInStream);
+ ControlModelLock aLock( *this );
+
+ // since we are "overwriting" the StringItemList of our aggregate (means we have
+ // an own place to store the value, instead of relying on our aggregate storing it),
+ // we need to respect what the aggregate just read for the StringItemList property.
+ try
+ {
+ if ( m_xAggregateSet.is() )
+ setNewStringItemList( m_xAggregateSet->getPropertyValue( PROPERTY_STRINGITEMLIST ), aLock );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.component", "OComboBoxModel::read: caught an exception while examining the aggregate's string item list!" );
+ }
+
+ // Version
+ sal_uInt16 nVersion = _rxInStream->readShort();
+ DBG_ASSERT(nVersion > 0, "OComboBoxModel::read : version 0 ? this should never have been written !");
+
+ if (nVersion > 0x0006)
+ {
+ OSL_FAIL("OComboBoxModel::read : invalid (means unknown) version !");
+ m_aListSource.clear();
+ m_aBoundColumn <<= sal_Int16(0);
+ m_aDefaultText.clear();
+ m_eListSourceType = ListSourceType_TABLE;
+ m_bEmptyIsNull = true;
+ defaultCommonProperties();
+ return;
+ }
+
+ // Mask for Any
+ sal_uInt16 nAnyMask;
+ _rxInStream >> nAnyMask;
+
+ // ListSource
+ if (nVersion < 0x0003)
+ {
+ _rxInStream >> m_aListSource;
+ }
+ else // nVersion == 4
+ {
+ m_aListSource.clear();
+ css::uno::Sequence<OUString> aListSource;
+ _rxInStream >> aListSource;
+ for (const OUString& rToken : std::as_const(aListSource))
+ m_aListSource += rToken;
+ }
+
+ sal_Int16 nListSourceType;
+ _rxInStream >> nListSourceType;
+ m_eListSourceType = static_cast<ListSourceType>(nListSourceType);
+
+ if ((nAnyMask & BOUNDCOLUMN) == BOUNDCOLUMN)
+ {
+ sal_Int16 nValue;
+ _rxInStream >> nValue;
+ m_aBoundColumn <<= nValue;
+ }
+
+ if (nVersion > 0x0001)
+ {
+ bool bNull;
+ _rxInStream >> bNull;
+ m_bEmptyIsNull = bNull;
+ }
+
+ if (nVersion > 0x0003) // nVersion == 4
+ _rxInStream >> m_aDefaultText;
+
+ // StringList must be emptied if a ListSource is set.
+ // This can be the case if we save in alive mode.
+ if ( !m_aListSource.isEmpty()
+ && !hasExternalListSource()
+ )
+ {
+ setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( css::uno::Sequence<OUString>() ) );
+ setFastPropertyValue( PROPERTY_ID_TYPEDITEMLIST, Any( css::uno::Sequence<css::uno::Any>() ) );
+ }
+
+ if (nVersion > 0x0004)
+ readHelpTextCompatibly(_rxInStream);
+
+ if (nVersion > 0x0005)
+ readCommonProperties(_rxInStream);
+
+ // After reading in, display the default values
+ if ( !getControlSource().isEmpty() )
+ {
+ // (not if we don't have a control source - the "State" property acts like it is persistent, then
+ resetNoBroadcast();
+ }
+}
+
+
+void OComboBoxModel::loadData( bool _bForce )
+{
+ DBG_ASSERT(m_eListSourceType != ListSourceType_VALUELIST, "OComboBoxModel::loadData : do not call for a value list !");
+ DBG_ASSERT( !hasExternalListSource(), "OComboBoxModel::loadData: cannot load from DB when I have an external list source!" );
+
+ if ( hasExternalListSource() )
+ return;
+
+ // Get Connection
+ if (!m_xCursor.is())
+ return;
+ Reference<XConnection> xConnection = getConnection(m_xCursor);
+ if (!xConnection.is())
+ return;
+
+ Reference<XServiceInfo> xServiceInfo(xConnection, UNO_QUERY);
+ if (!xServiceInfo.is() || !xServiceInfo->supportsService(SRV_SDB_CONNECTION))
+ {
+ OSL_FAIL("OComboBoxModel::loadData : invalid connection !");
+ return;
+ }
+
+ if (m_aListSource.isEmpty() || m_eListSourceType == ListSourceType_VALUELIST)
+ return;
+
+ ::utl::SharedUNOComponent< XResultSet > xListCursor;
+ try
+ {
+ m_aListRowSet.setConnection( xConnection );
+
+ bool bExecuteRowSet( false );
+ switch (m_eListSourceType)
+ {
+ case ListSourceType_TABLEFIELDS:
+ // don't work with a statement here, the fields will be collected below
+ break;
+ case ListSourceType_TABLE:
+ {
+ // does the bound field belong to the table ?
+ // if we use an alias for the bound field, we won't find it
+ // in that case we use the first field of the table
+
+ Reference<XNameAccess> xFieldsByName = getTableFields(xConnection, m_aListSource);
+
+ OUString aFieldName;
+ if ( xFieldsByName.is() && xFieldsByName->hasByName( getControlSource() ) )
+ {
+ aFieldName = getControlSource();
+ }
+ else
+ {
+ // otherwise look for the alias
+ Reference<XPropertySet> xFormProp(m_xCursor,UNO_QUERY);
+ Reference< XColumnsSupplier > xSupplyFields;
+ xFormProp->getPropertyValue("SingleSelectQueryComposer") >>= xSupplyFields;
+
+ // search the field
+ DBG_ASSERT(xSupplyFields.is(), "OComboBoxModel::loadData : invalid query composer !");
+
+ Reference< XNameAccess > xFieldNames = xSupplyFields->getColumns();
+ if ( xFieldNames->hasByName( getControlSource() ) )
+ {
+ Reference< XPropertySet > xComposerFieldAsSet;
+ xFieldNames->getByName( getControlSource() ) >>= xComposerFieldAsSet;
+ if (hasProperty(PROPERTY_FIELDSOURCE, xComposerFieldAsSet))
+ xComposerFieldAsSet->getPropertyValue(PROPERTY_FIELDSOURCE) >>= aFieldName;
+ }
+ }
+
+ if (aFieldName.isEmpty())
+ break;
+
+ Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
+ OSL_ENSURE(xMeta.is(),"No database meta data!");
+ if ( xMeta.is() )
+ {
+ OUString aQuote = xMeta->getIdentifierQuoteString();
+
+ OUString sCatalog, sSchema, sTable;
+ qualifiedNameComponents( xMeta, m_aListSource, sCatalog, sSchema, sTable, EComposeRule::InDataManipulation );
+
+ OUString aStatement =
+ "SELECT DISTINCT " +
+ quoteName( aQuote, aFieldName ) +
+ " FROM " +
+ composeTableNameForSelect( xConnection, sCatalog, sSchema, sTable );
+
+ m_aListRowSet.setEscapeProcessing( false );
+ m_aListRowSet.setCommand( aStatement );
+ bExecuteRowSet = true;
+ }
+ } break;
+ case ListSourceType_QUERY:
+ {
+ m_aListRowSet.setCommandFromQuery( m_aListSource );
+ bExecuteRowSet = true;
+ }
+ break;
+
+ default:
+ {
+ m_aListRowSet.setEscapeProcessing( ListSourceType_SQLPASSTHROUGH != m_eListSourceType );
+ m_aListRowSet.setCommand( m_aListSource );
+ bExecuteRowSet = true;
+ }
+ }
+
+ if ( bExecuteRowSet )
+ {
+ if ( !_bForce && !m_aListRowSet.isDirty() )
+ {
+ // if none of the settings of the row set changed, compared to the last
+ // invocation of loadData, then don't re-fill the list. Instead, assume
+ // the list entries are the same.
+ return;
+ }
+ xListCursor.reset( m_aListRowSet.execute() );
+ }
+ }
+ catch(const SQLException& eSQL)
+ {
+ onError(eSQL, ResourceManager::loadString(RID_BASELISTBOX_ERROR_FILLLIST));
+ return;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ return;
+ }
+
+ ::std::vector< OUString > aStringList;
+ aStringList.reserve(16);
+ try
+ {
+ OSL_ENSURE( xListCursor.is() || ( ListSourceType_TABLEFIELDS == m_eListSourceType ),
+ "OComboBoxModel::loadData: logic error!" );
+ if ( !xListCursor.is() && ( ListSourceType_TABLEFIELDS != m_eListSourceType ) )
+ return;
+
+ switch (m_eListSourceType)
+ {
+ case ListSourceType_SQL:
+ case ListSourceType_SQLPASSTHROUGH:
+ case ListSourceType_TABLE:
+ case ListSourceType_QUERY:
+ {
+ // The XDatabaseVariant of the first column
+ Reference<XColumnsSupplier> xSupplyCols(xListCursor, UNO_QUERY);
+ DBG_ASSERT(xSupplyCols.is(), "OComboBoxModel::loadData : cursor supports the row set service but is no column supplier?!");
+ Reference<XIndexAccess> xColumns;
+ if (xSupplyCols.is())
+ {
+ xColumns.set(xSupplyCols->getColumns(), UNO_QUERY);
+ DBG_ASSERT(xColumns.is(), "OComboBoxModel::loadData : no columns supplied by the row set !");
+ }
+ Reference< XPropertySet > xDataField;
+ if ( xColumns.is() )
+ xColumns->getByIndex(0) >>= xDataField;
+ if ( !xDataField.is() )
+ return;
+
+ ::dbtools::FormattedColumnValue aValueFormatter( getContext(), m_xCursor, xDataField );
+
+ // Fill Lists
+ sal_Int16 i = 0;
+ // At the moment by definition the list cursor is positioned _before_ the first row
+ while (xListCursor->next() && (i++<SHRT_MAX)) // Set max. count
+ {
+ aStringList.push_back( aValueFormatter.getFormattedValue() );
+ }
+ }
+ break;
+ case ListSourceType_TABLEFIELDS:
+ {
+ Reference<XNameAccess> xFieldNames = getTableFields(xConnection, m_aListSource);
+ if (xFieldNames.is())
+ {
+ const Sequence<OUString> aFieldNames = xFieldNames->getElementNames();
+ aStringList.insert(aStringList.end(), aFieldNames.begin(), aFieldNames.end());
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "OComboBoxModel::loadData: unreachable!" );
+ break;
+ }
+ }
+ catch(const SQLException& eSQL)
+ {
+ onError(eSQL, ResourceManager::loadString(RID_BASELISTBOX_ERROR_FILLLIST));
+ return;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ return;
+ }
+
+ // Set String-Sequence at ListBox
+ setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( comphelper::containerToSequence(aStringList) ) );
+ // Reset TypedItemList, no matching data.
+ setFastPropertyValue( PROPERTY_ID_TYPEDITEMLIST, Any( css::uno::Sequence<css::uno::Any>() ) );
+}
+
+
+void OComboBoxModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
+{
+ Reference<XPropertySet> xField = getField();
+ if ( xField.is() )
+ m_pValueFormatter.reset( new ::dbtools::FormattedColumnValue( getContext(), Reference< XRowSet >( _rxForm, UNO_QUERY ), xField ) );
+ getPropertyValue( PROPERTY_STRINGITEMLIST ) >>= m_aDesignModeStringItems;
+
+ // Only load data if a ListSource was supplied
+ if ( !m_aListSource.isEmpty() && m_xCursor.is() && !hasExternalListSource() )
+ loadData( false );
+}
+
+
+void OComboBoxModel::onDisconnectedDbColumn()
+{
+ m_pValueFormatter.reset();
+
+ // reset the string item list
+ if ( !hasExternalListSource() )
+ setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( m_aDesignModeStringItems ) );
+
+ m_aListRowSet.dispose();
+}
+
+
+void SAL_CALL OComboBoxModel::reloaded( const EventObject& aEvent )
+{
+ OBoundControlModel::reloaded(aEvent);
+
+ // reload data if we have a list source
+ if ( !m_aListSource.isEmpty() && m_xCursor.is() && !hasExternalListSource() )
+ loadData( false );
+}
+
+
+void OComboBoxModel::resetNoBroadcast()
+{
+ OBoundControlModel::resetNoBroadcast();
+ m_aLastKnownValue.clear();
+}
+
+
+bool OComboBoxModel::commitControlValueToDbColumn( bool _bPostReset )
+{
+ Any aNewValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
+
+ OUString sNewValue;
+ aNewValue >>= sNewValue;
+
+ bool bModified = ( aNewValue != m_aLastKnownValue );
+ if ( bModified )
+ {
+ if ( !aNewValue.hasValue()
+ || ( sNewValue.isEmpty() // an empty string
+ && m_bEmptyIsNull // which should be interpreted as NULL
+ )
+ )
+ {
+ m_xColumnUpdate->updateNull();
+ }
+ else
+ {
+ try
+ {
+ OSL_PRECOND(m_pValueFormatter,
+ "OComboBoxModel::commitControlValueToDbColumn: no value formatter!");
+ if (m_pValueFormatter)
+ {
+ if ( !m_pValueFormatter->setFormattedValue( sNewValue ) )
+ return false;
+ }
+ else
+ m_xColumnUpdate->updateString( sNewValue );
+ }
+ catch ( const Exception& )
+ {
+ return false;
+ }
+ }
+
+ m_aLastKnownValue = aNewValue;
+ }
+
+ // add the new value to the list
+ bool bAddToList = bModified && !_bPostReset;
+ // (only if this is not the "commit" triggered by a "reset")
+
+ if ( !bAddToList )
+ return true;
+
+ css::uno::Sequence<OUString> aStringItemList;
+ if ( !(getPropertyValue( PROPERTY_STRINGITEMLIST ) >>= aStringItemList) )
+ return true;
+
+ bool bFound = false;
+ for (const OUString& rStringItem : std::as_const(aStringItemList))
+ {
+ if ( (bFound = rStringItem == sNewValue) )
+ break;
+ }
+
+ // not found -> add
+ if (!bFound)
+ {
+ sal_Int32 nOldLen = aStringItemList.getLength();
+ aStringItemList.realloc( nOldLen + 1 );
+ aStringItemList.getArray()[ nOldLen ] = sNewValue;
+
+ setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( aStringItemList ) );
+ setFastPropertyValue( PROPERTY_ID_TYPEDITEMLIST, Any( css::uno::Sequence<css::uno::Any>() ) );
+ }
+
+ return true;
+}
+
+// XPropertiesChangeListener
+
+Any OComboBoxModel::translateDbColumnToControlValue()
+{
+ OSL_PRECOND(m_pValueFormatter,
+ "OComboBoxModel::translateDbColumnToControlValue: no value formatter!");
+ if (m_pValueFormatter)
+ {
+ OUString sValue( m_pValueFormatter->getFormattedValue() );
+ if ( sValue.isEmpty()
+ && m_pValueFormatter->getColumn().is()
+ && m_pValueFormatter->getColumn()->wasNull()
+ )
+ {
+ m_aLastKnownValue.clear();
+ }
+ else
+ {
+
+ m_aLastKnownValue <<= sValue;
+ }
+ }
+ else
+ m_aLastKnownValue.clear();
+
+ return m_aLastKnownValue.hasValue() ? m_aLastKnownValue : Any( OUString() );
+ // (m_aLastKnownValue is allowed to be VOID, the control value isn't)
+}
+
+
+Any OComboBoxModel::getDefaultForReset() const
+{
+ return Any( m_aDefaultText );
+}
+
+
+void OComboBoxModel::stringItemListChanged( ControlModelLock& /*_rInstanceLock*/ )
+{
+ if ( m_xAggregateSet.is() )
+ {
+ m_xAggregateSet->setPropertyValue( PROPERTY_STRINGITEMLIST, Any( comphelper::containerToSequence(getStringItemList()) ) );
+ m_xAggregateSet->setPropertyValue( PROPERTY_TYPEDITEMLIST, Any( getTypedItemList()) ) ;
+ }
+}
+
+
+void OComboBoxModel::refreshInternalEntryList()
+{
+ DBG_ASSERT( !hasExternalListSource(), "OComboBoxModel::refreshInternalEntryList: invalid call!" );
+
+ if ( !hasExternalListSource( )
+ && ( m_eListSourceType != ListSourceType_VALUELIST )
+ && ( m_xCursor.is() )
+ )
+ {
+ loadData( true );
+ }
+}
+
+
+void SAL_CALL OComboBoxModel::disposing( const EventObject& _rSource )
+{
+ if ( !OEntryListHelper::handleDisposing( _rSource ) )
+ OBoundControlModel::disposing( _rSource );
+}
+
+
+//= OComboBoxControl
+
+OComboBoxControl::OComboBoxControl(const Reference<XComponentContext>& _rxContext)
+ :OBoundControl(_rxContext, VCL_CONTROL_COMBOBOX)
+{
+}
+
+
+css::uno::Sequence<OUString> SAL_CALL OComboBoxControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString* pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_COMBOBOX;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_COMBOBOX;
+ return aSupported;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OComboBoxModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OComboBoxModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OComboBoxControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OComboBoxControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/ComboBox.hxx b/forms/source/component/ComboBox.hxx
new file mode 100644
index 000000000..ed2360144
--- /dev/null
+++ b/forms/source/component/ComboBox.hxx
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <FormComponent.hxx>
+#include "errorbroadcaster.hxx"
+#include "entrylisthelper.hxx"
+#include "cachedrowset.hxx"
+
+#include <com/sun/star/form/ListSourceType.hpp>
+
+#include <connectivity/formattedcolumnvalue.hxx>
+
+
+namespace frm
+{
+
+class OComboBoxModel final
+ :public OBoundControlModel
+ ,public OEntryListHelper
+ ,public OErrorBroadcaster
+{
+ CachedRowSet m_aListRowSet; // the row set to fill the list
+ css::uno::Any m_aBoundColumn; // obsolete
+ OUString m_aListSource;
+ OUString m_aDefaultText; // DefaultText
+ css::uno::Any m_aLastKnownValue;
+
+ css::uno::Sequence<OUString> m_aDesignModeStringItems;
+
+ css::form::ListSourceType m_eListSourceType; // ListSource's type
+ bool m_bEmptyIsNull; // Empty string is interpreted as NULL
+
+ ::std::unique_ptr< ::dbtools::FormattedColumnValue > m_pValueFormatter;
+
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ OComboBoxModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OComboBoxModel(
+ const OComboBoxModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OComboBoxModel() override;
+
+ virtual void SAL_CALL disposing() override;
+
+ // OPropertySetHelper
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle) const override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(
+ css::uno::Any& _rConvertedValue, css::uno::Any& _rOldValue, sal_Int32 _nHandle, const css::uno::Any& _rValue ) override;
+
+ // XLoadListener
+ virtual void SAL_CALL reloaded( const css::lang::EventObject& aEvent ) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OComboBoxModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // UNO
+ DECLARE_UNO3_AGG_DEFAULTS(OComboBoxModel, OBoundControlModel)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL
+ write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL
+ read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+ virtual void describeAggregateProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+
+ // prevent method hiding
+ using OBoundControlModel::getFastPropertyValue;
+
+private:
+ // OBoundControlModel overridables
+ virtual css::uno::Any translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+
+ virtual void onConnectedDbColumn( const css::uno::Reference< css::uno::XInterface >& _rxForm ) override;
+ virtual void onDisconnectedDbColumn() override;
+
+ virtual css::uno::Any getDefaultForReset() const override;
+
+ virtual void resetNoBroadcast() override;
+
+ // OEntryListHelper overridables
+ virtual void stringItemListChanged( ControlModelLock& _rInstanceLock ) override;
+ virtual void refreshInternalEntryList() override;
+
+ void loadData( bool _bForce );
+
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+class OComboBoxControl : public OBoundControl
+{
+public:
+ explicit OComboBoxControl(const css::uno::Reference< css::uno::XComponentContext>& _rxContext);
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OComboBoxControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Currency.cxx b/forms/source/component/Currency.cxx
new file mode 100644
index 000000000..2e4839dd8
--- /dev/null
+++ b/forms/source/component/Currency.cxx
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "Currency.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <comphelper/types.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+
+namespace frm
+{
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+
+OCurrencyControl::OCurrencyControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_CURRENCYFIELD)
+{
+}
+
+css::uno::Sequence<OUString> SAL_CALL OCurrencyControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_CURRENCYFIELD;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_CURRENCYFIELD;
+ return aSupported;
+}
+
+
+// OCurrencyModel
+
+void OCurrencyModel::implConstruct()
+{
+ if (!m_xAggregateSet.is())
+ return;
+
+ try
+ {
+ // get the system international information
+ const SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& aLocaleInfo = aSysLocale.GetLocaleData();
+
+ OUString sCurrencySymbol;
+ bool bPrependCurrencySymbol = false;
+ switch ( aLocaleInfo.getCurrPositiveFormat() )
+ {
+ case 0: // $1
+ sCurrencySymbol = aLocaleInfo.getCurrSymbol();
+ bPrependCurrencySymbol = true;
+ break;
+ case 1: // 1$
+ sCurrencySymbol = aLocaleInfo.getCurrSymbol();
+ bPrependCurrencySymbol = false;
+ break;
+ case 2: // $ 1
+ sCurrencySymbol = aLocaleInfo.getCurrSymbol() + " ";
+ bPrependCurrencySymbol = true;
+ break;
+ case 3: // 1 $
+ sCurrencySymbol = " " + aLocaleInfo.getCurrSymbol();
+ bPrependCurrencySymbol = false;
+ break;
+ }
+ if (!sCurrencySymbol.isEmpty())
+ {
+ m_xAggregateSet->setPropertyValue(PROPERTY_CURRENCYSYMBOL, Any(sCurrencySymbol));
+ m_xAggregateSet->setPropertyValue(PROPERTY_CURRSYM_POSITION, Any(bPrependCurrencySymbol));
+ }
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "forms.component", "OCurrencyModel::implConstruct: caught an exception while initializing the aggregate!" );
+ }
+}
+
+
+OCurrencyModel::OCurrencyModel(const Reference<XComponentContext>& _rxFactory)
+ :OEditBaseModel( _rxFactory, VCL_CONTROLMODEL_CURRENCYFIELD, FRM_SUN_CONTROL_CURRENCYFIELD, false, true )
+ // use the old control name for compatibility reasons
+{
+
+ m_nClassId = FormComponentType::CURRENCYFIELD;
+ initValueProperty( PROPERTY_VALUE, PROPERTY_ID_VALUE );
+
+ implConstruct();
+}
+
+
+OCurrencyModel::OCurrencyModel( const OCurrencyModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OEditBaseModel( _pOriginal, _rxFactory )
+{
+ implConstruct();
+}
+
+
+OCurrencyModel::~OCurrencyModel()
+{
+}
+
+// XCloneable
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OCurrencyModel::createClone()
+{
+ rtl::Reference<OCurrencyModel> pClone = new OCurrencyModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL OCurrencyModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControlModel::getSupportedServiceNames();
+
+ sal_Int32 nOldLen = aSupported.getLength();
+ aSupported.realloc( nOldLen + 5 );
+ OUString* pStoreTo = aSupported.getArray() + nOldLen;
+
+ *pStoreTo++ = DATA_AWARE_CONTROL_MODEL;
+ *pStoreTo++ = VALIDATABLE_CONTROL_MODEL;
+
+ *pStoreTo++ = FRM_SUN_COMPONENT_CURRENCYFIELD;
+ *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_CURRENCYFIELD;
+
+ *pStoreTo++ = FRM_COMPONENT_CURRENCYFIELD;
+
+ return aSupported;
+}
+
+
+void OCurrencyModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OEditBaseModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 2);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ // Set Value to transient
+ // ModifyPropertyAttributes(_rAggregateProps, PROPERTY_VALUE, PropertyAttribute::TRANSIENT, 0);
+
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_VALUE, PROPERTY_ID_DEFAULT_VALUE, cppu::UnoType<double>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+OUString SAL_CALL OCurrencyModel::getServiceName()
+{
+ return FRM_COMPONENT_CURRENCYFIELD; // old (non-sun) name for compatibility !
+}
+
+
+bool OCurrencyModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+{
+ Any aControlValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
+ if ( aControlValue != m_aSaveValue )
+ {
+ if ( aControlValue.getValueType().getTypeClass() == TypeClass_VOID )
+ m_xColumnUpdate->updateNull();
+ else
+ {
+ try
+ {
+ m_xColumnUpdate->updateDouble( getDouble( aControlValue ) );
+ }
+ catch(const Exception&)
+ {
+ return false;
+ }
+ }
+ m_aSaveValue = aControlValue;
+ }
+ return true;
+}
+
+
+Any OCurrencyModel::translateDbColumnToControlValue()
+{
+ m_aSaveValue <<= m_xColumn->getDouble();
+ if ( m_xColumn->wasNull() )
+ m_aSaveValue.clear();
+ return m_aSaveValue;
+}
+
+// XReset
+
+Any OCurrencyModel::getDefaultForReset() const
+{
+ Any aValue;
+ if ( m_aDefault.getValueType().getTypeClass() == TypeClass_DOUBLE )
+ aValue = m_aDefault;
+
+ return aValue;
+}
+
+
+void OCurrencyModel::resetNoBroadcast()
+{
+ OEditBaseModel::resetNoBroadcast();
+ m_aSaveValue.clear();
+}
+
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OCurrencyModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OCurrencyModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OCurrencyControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OCurrencyControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Currency.hxx b/forms/source/component/Currency.hxx
new file mode 100644
index 000000000..f77f711f0
--- /dev/null
+++ b/forms/source/component/Currency.hxx
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "EditBase.hxx"
+
+
+namespace frm
+{
+
+class OCurrencyModel final
+ :public OEditBaseModel
+{
+ css::uno::Any m_aSaveValue;
+
+public:
+ OCurrencyModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OCurrencyModel(
+ const OCurrencyModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OCurrencyModel() override;
+
+ // css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OCurrencyModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // css::io::XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+private:
+ // OBoundControlModel overridables
+ virtual css::uno::Any translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+
+ virtual css::uno::Any getDefaultForReset() const override;
+
+ virtual void resetNoBroadcast() override;
+
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ void implConstruct();
+};
+
+class OCurrencyControl: public OBoundControl
+{
+public:
+ explicit OCurrencyControl(const css::uno::Reference< css::uno::XComponentContext>& _rxContext);
+ // css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OCurrencyControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/DatabaseForm.cxx b/forms/source/component/DatabaseForm.cxx
new file mode 100644
index 000000000..cfb22d927
--- /dev/null
+++ b/forms/source/component/DatabaseForm.cxx
@@ -0,0 +1,4044 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <componenttools.hxx>
+#include "DatabaseForm.hxx"
+#include "EventThread.hxx"
+#include <strings.hrc>
+#include <frm_resource.hxx>
+#include "GroupManager.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+#include <com/sun/star/awt/XControlContainer.hpp>
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/DataSelectionType.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/form/TabulatorCycle.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/io/XObjectInputStream.hpp>
+#include <com/sun/star/io/XObjectOutputStream.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdb/RowSetVetoException.hpp>
+#include <com/sun/star/sdb/XColumnUpdate.hpp>
+#include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
+#include <com/sun/star/sdbc/ResultSetType.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sdbcx/Privilege.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/util/XModifiable2.hpp>
+
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/seqstream.hxx>
+#include <comphelper/sequence.hxx>
+#include <connectivity/dbtools.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/types.hxx>
+#include <rtl/math.hxx>
+#include <rtl/tencinfo.h>
+#include <svl/inettype.hxx>
+#include <tools/datetime.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/inetmsg.hxx>
+#include <tools/inetstrm.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/timer.hxx>
+#include <osl/mutex.hxx>
+
+using namespace ::dbtools;
+using namespace ::comphelper;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+
+
+namespace frm
+{
+
+namespace {
+
+class DocumentModifyGuard
+{
+public:
+ explicit DocumentModifyGuard( const Reference< XInterface >& _rxFormComponent )
+ :m_xDocumentModify( getXModel( _rxFormComponent ), UNO_QUERY )
+ {
+ impl_changeModifiableFlag_nothrow( false );
+ }
+ ~DocumentModifyGuard()
+ {
+ impl_changeModifiableFlag_nothrow( true );
+ }
+
+private:
+ void impl_changeModifiableFlag_nothrow( const bool _enable )
+ {
+ try
+ {
+ if ( m_xDocumentModify.is() )
+ _enable ? m_xDocumentModify->enableSetModified() : m_xDocumentModify->disableSetModified();
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+
+private:
+ Reference< XModifiable2 > m_xDocumentModify;
+};
+
+}
+
+// submitting and resetting html-forms asynchronously
+class OFormSubmitResetThread: public OComponentEventThread
+{
+protected:
+
+ // process an event. while processing the mutex isn't locked, and pCompImpl
+ // is made sure to remain valid
+ virtual void processEvent( ::cppu::OComponentHelper* _pCompImpl,
+ const EventObject* _pEvt,
+ const Reference<XControl>& _rControl,
+ bool _bSubmit) override;
+
+public:
+
+ explicit OFormSubmitResetThread(ODatabaseForm* pControl) : OComponentEventThread(pControl) { }
+};
+
+
+void OFormSubmitResetThread::processEvent(
+ ::cppu::OComponentHelper* pCompImpl,
+ const EventObject *_pEvt,
+ const Reference<XControl>& _rControl,
+ bool _bSubmit)
+{
+ if (_bSubmit)
+ static_cast<ODatabaseForm *>(pCompImpl)->submit_impl(_rControl, *static_cast<const css::awt::MouseEvent*>(_pEvt));
+ else
+ static_cast<ODatabaseForm *>(pCompImpl)->reset_impl(true);
+}
+
+
+//= ODatabaseForm
+
+Sequence<sal_Int8> SAL_CALL ODatabaseForm::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+
+Sequence<Type> SAL_CALL ODatabaseForm::getTypes()
+{
+ // ask the aggregate
+ Sequence<Type> aAggregateTypes;
+ Reference<XTypeProvider> xAggregateTypes;
+ if (query_aggregation(m_xAggregate, xAggregateTypes))
+ aAggregateTypes = xAggregateTypes->getTypes();
+
+ Sequence< Type > aRet = concatSequences(
+ aAggregateTypes, ODatabaseForm_BASE1::getTypes(), OFormComponents::getTypes()
+ );
+ aRet = concatSequences( aRet, ODatabaseForm_BASE2::getTypes(), ODatabaseForm_BASE3::getTypes() );
+ return concatSequences( aRet, OPropertySetAggregationHelper::getTypes() );
+}
+
+
+Any SAL_CALL ODatabaseForm::queryAggregation(const Type& _rType)
+{
+ Any aReturn = ODatabaseForm_BASE1::queryInterface(_rType);
+ // our own interfaces
+ if (!aReturn.hasValue())
+ {
+ aReturn = ODatabaseForm_BASE2::queryInterface(_rType);
+ // property set related interfaces
+ if (!aReturn.hasValue())
+ {
+ aReturn = OPropertySetAggregationHelper::queryInterface(_rType);
+
+ // form component collection related interfaces
+ if (!aReturn.hasValue())
+ {
+ aReturn = OFormComponents::queryAggregation(_rType);
+
+ // interfaces already present in the aggregate which we want to reroute
+ // only available if we could create the aggregate
+ if (!aReturn.hasValue() && m_xAggregateAsRowSet.is())
+ aReturn = ODatabaseForm_BASE3::queryInterface(_rType);
+
+ // aggregate interfaces
+ // (ask the aggregated object _after_ the OComponentHelper (base of OFormComponents),
+ // so calls to the XComponent interface reach us and not the aggregation)
+ if (!aReturn.hasValue() && m_xAggregate.is())
+ aReturn = m_xAggregate->queryAggregation(_rType);
+ }
+ }
+ }
+
+ return aReturn;
+}
+
+
+ODatabaseForm::ODatabaseForm(const Reference<XComponentContext>& _rxContext)
+ :OFormComponents(_rxContext)
+ ,OPropertySetAggregationHelper(OComponentHelper::rBHelper)
+ ,OPropertyChangeListener(m_aMutex)
+ ,m_aLoadListeners(m_aMutex)
+ ,m_aRowSetApproveListeners(m_aMutex)
+ ,m_aSubmitListeners(m_aMutex)
+ ,m_aErrorListeners(m_aMutex)
+ ,m_aResetListeners(m_aMutex)
+ ,m_aPropertyBagHelper( *this )
+ ,m_aParameterManager( m_aMutex, _rxContext )
+ ,m_aFilterManager()
+ ,m_nResetsPending(0)
+ ,m_nPrivileges(0)
+ ,m_bInsertOnly( false )
+ ,m_eSubmitMethod(FormSubmitMethod_GET)
+ ,m_eSubmitEncoding(FormSubmitEncoding_URL)
+ ,m_eNavigation(NavigationBarMode_CURRENT)
+ ,m_bAllowInsert(true)
+ ,m_bAllowUpdate(true)
+ ,m_bAllowDelete(true)
+ ,m_bLoaded(false)
+ ,m_bSubForm(false)
+ ,m_bForwardingConnection(false)
+ ,m_bSharingConnection( false )
+{
+ impl_construct();
+}
+
+
+ODatabaseForm::ODatabaseForm( const ODatabaseForm& _cloneSource )
+ :OFormComponents( _cloneSource )
+ ,OPropertySetAggregationHelper( OComponentHelper::rBHelper )
+ ,OPropertyChangeListener( m_aMutex )
+ ,ODatabaseForm_BASE1()
+ ,ODatabaseForm_BASE2()
+ ,ODatabaseForm_BASE3()
+ ,IPropertyBagHelperContext()
+ ,m_aLoadListeners( m_aMutex )
+ ,m_aRowSetApproveListeners( m_aMutex )
+ ,m_aSubmitListeners( m_aMutex )
+ ,m_aErrorListeners( m_aMutex )
+ ,m_aResetListeners( m_aMutex )
+ ,m_aPropertyBagHelper( *this )
+ ,m_aParameterManager( m_aMutex, _cloneSource.m_xContext )
+ ,m_aFilterManager()
+ ,m_nResetsPending( 0 )
+ ,m_nPrivileges( 0 )
+ ,m_bInsertOnly( _cloneSource.m_bInsertOnly )
+ ,m_aControlBorderColorFocus( _cloneSource.m_aControlBorderColorFocus )
+ ,m_aControlBorderColorMouse( _cloneSource.m_aControlBorderColorMouse )
+ ,m_aControlBorderColorInvalid( _cloneSource.m_aControlBorderColorInvalid )
+ ,m_aDynamicControlBorder( _cloneSource.m_aDynamicControlBorder )
+ ,m_sName( _cloneSource.m_sName )
+ ,m_aTargetURL( _cloneSource.m_aTargetURL )
+ ,m_aTargetFrame( _cloneSource.m_aTargetFrame )
+ ,m_eSubmitMethod( _cloneSource.m_eSubmitMethod )
+ ,m_eSubmitEncoding( _cloneSource.m_eSubmitEncoding )
+ ,m_eNavigation( _cloneSource.m_eNavigation )
+ ,m_bAllowInsert( _cloneSource.m_bAllowInsert )
+ ,m_bAllowUpdate( _cloneSource.m_bAllowUpdate )
+ ,m_bAllowDelete( _cloneSource.m_bAllowDelete )
+ ,m_bLoaded( false )
+ ,m_bSubForm( false )
+ ,m_bForwardingConnection( false )
+ ,m_bSharingConnection( false )
+{
+
+ impl_construct();
+
+ osl_atomic_increment( &m_refCount );
+ {
+ // our aggregated rowset itself is not cloneable, so simply copy the properties
+ ::comphelper::copyProperties( _cloneSource.m_xAggregateSet, m_xAggregateSet );
+
+ // also care for the dynamic properties: If the clone source has properties which we do not have,
+ // then add them
+ try
+ {
+ Reference< XPropertySet > xSourceProps( const_cast< ODatabaseForm& >( _cloneSource ).queryAggregation(
+ cppu::UnoType<XPropertySet>::get() ), UNO_QUERY_THROW );
+ Reference< XPropertySetInfo > xSourcePSI( xSourceProps->getPropertySetInfo(), UNO_SET_THROW );
+ Reference< XPropertyState > xSourcePropState( xSourceProps, UNO_QUERY );
+
+ Reference< XPropertySetInfo > xDestPSI( getPropertySetInfo(), UNO_SET_THROW );
+
+ const Sequence< Property > aSourceProperties( xSourcePSI->getProperties() );
+ for ( auto const & sourceProperty : aSourceProperties )
+ {
+ if ( xDestPSI->hasPropertyByName( sourceProperty.Name ) )
+ continue;
+
+ // the initial value passed to XPropertyContainer is also used as default, usually. So, try
+ // to retrieve the default of the source property
+ Any aInitialValue;
+ if ( xSourcePropState.is() )
+ {
+ aInitialValue = xSourcePropState->getPropertyDefault( sourceProperty.Name );
+ }
+ else
+ {
+ aInitialValue = xSourceProps->getPropertyValue( sourceProperty.Name );
+ }
+ addProperty( sourceProperty.Name, sourceProperty.Attributes, aInitialValue );
+ setPropertyValue( sourceProperty.Name, xSourceProps->getPropertyValue( sourceProperty.Name ) );
+ }
+ }
+ catch(const RuntimeException&)
+ {
+ throw;
+ }
+ catch(const Exception&)
+ {
+ css::uno::Any a(cppu::getCaughtException());
+ throw WrappedTargetRuntimeException(
+ "Could not clone the given database form.",
+ *const_cast< ODatabaseForm* >( &_cloneSource ),
+ a
+ );
+ }
+ }
+ osl_atomic_decrement( &m_refCount );
+}
+
+void ODatabaseForm::impl_construct()
+{
+ // aggregate a row set
+ osl_atomic_increment(&m_refCount);
+ {
+ m_xAggregate.set( m_xContext->getServiceManager()->createInstanceWithContext(SRV_SDB_ROWSET, m_xContext), UNO_QUERY_THROW );
+ m_xAggregateAsRowSet.set( m_xAggregate, UNO_QUERY_THROW );
+ setAggregation( m_xAggregate );
+ }
+
+ // listen for the properties, important for Parameters
+ if ( m_xAggregateSet.is() )
+ {
+ m_xAggregatePropertyMultiplexer = new OPropertyChangeMultiplexer(this, m_xAggregateSet, false);
+ m_xAggregatePropertyMultiplexer->addProperty(PROPERTY_COMMAND);
+ m_xAggregatePropertyMultiplexer->addProperty(PROPERTY_ACTIVE_CONNECTION);
+ }
+
+ {
+ Reference< XWarningsSupplier > xRowSetWarnings( m_xAggregate, UNO_QUERY );
+ m_aWarnings.setExternalWarnings( xRowSetWarnings );
+ }
+
+ if ( m_xAggregate.is() )
+ {
+ m_xAggregate->setDelegator( static_cast< XWeak* >( this ) );
+ }
+
+ {
+ m_aFilterManager.initialize( m_xAggregateSet );
+ m_aParameterManager.initialize( this, m_xAggregate );
+
+ declareForwardedProperty( PROPERTY_ID_ACTIVE_CONNECTION );
+ }
+ osl_atomic_decrement( &m_refCount );
+
+ m_pGroupManager = new OGroupManager( this );
+}
+
+
+ODatabaseForm::~ODatabaseForm()
+{
+ m_pGroupManager.clear();
+
+ if (m_xAggregate.is())
+ m_xAggregate->setDelegator( nullptr );
+
+ m_aWarnings.setExternalWarnings( nullptr );
+
+ if (m_xAggregatePropertyMultiplexer.is())
+ {
+ m_xAggregatePropertyMultiplexer->dispose();
+ m_xAggregatePropertyMultiplexer.clear();
+ }
+}
+
+
+// html tools
+
+OUString ODatabaseForm::GetDataEncoded(bool _bURLEncoded,const Reference<XControl>& SubmitButton, const css::awt::MouseEvent& MouseEvt)
+{
+ // Fill List of successful Controls
+ HtmlSuccessfulObjList aSuccObjList;
+ FillSuccessfulList( aSuccObjList, SubmitButton, MouseEvt );
+
+
+ // Aggregate the list to OUString
+ OUStringBuffer aResult;
+ OUString aName;
+ OUString aValue;
+
+ for ( HtmlSuccessfulObjList::iterator pSuccObj = aSuccObjList.begin();
+ pSuccObj < aSuccObjList.end();
+ ++pSuccObj
+ )
+ {
+ aName = pSuccObj->aName;
+ aValue = pSuccObj->aValue;
+ if( pSuccObj->nRepresentation == SUCCESSFUL_REPRESENT_FILE && !aValue.isEmpty() )
+ {
+ // For File URLs we transfer the file name and not a URL, because Netscape does it like that
+ INetURLObject aURL;
+ aURL.SetSmartProtocol(INetProtocol::File);
+ aURL.SetSmartURL(aValue);
+ if( INetProtocol::File == aURL.GetProtocol() )
+ aValue = INetURLObject::decode(aURL.PathToFileName(), INetURLObject::DecodeMechanism::Unambiguous);
+ }
+ Encode( aName );
+ Encode( aValue );
+
+ aResult.append(aName);
+ aResult.append('=');
+ aResult.append(aValue);
+
+ if (pSuccObj < aSuccObjList.end() - 1)
+ {
+ if ( _bURLEncoded )
+ aResult.append('&');
+ else
+ aResult.append("\r\n");
+ }
+ }
+
+
+ aSuccObjList.clear();
+
+ return aResult.makeStringAndClear();
+}
+
+
+// html tools
+
+Sequence<sal_Int8> ODatabaseForm::GetDataMultiPartEncoded(const Reference<XControl>& SubmitButton, const css::awt::MouseEvent& MouseEvt, OUString& rContentType)
+{
+
+ // Create Parent
+ INetMIMEMessage aParent;
+ aParent.EnableAttachMultipartFormDataChild();
+
+
+ // Fill List of successful Controls
+ HtmlSuccessfulObjList aSuccObjList;
+ FillSuccessfulList( aSuccObjList, SubmitButton, MouseEvt );
+
+
+ // Aggregate List to OUString
+ for (auto const& succObj : aSuccObjList)
+ {
+ if( succObj.nRepresentation == SUCCESSFUL_REPRESENT_TEXT )
+ InsertTextPart( aParent, succObj.aName, succObj.aValue );
+ else if( succObj.nRepresentation == SUCCESSFUL_REPRESENT_FILE )
+ InsertFilePart( aParent, succObj.aName, succObj.aValue );
+ }
+
+
+ // Delete List
+ aSuccObjList.clear();
+
+ // Create MessageStream for parent
+ INetMIMEMessageStream aMessStream(&aParent, true);
+
+ // Copy MessageStream to SvStream
+ SvMemoryStream aMemStream;
+ std::unique_ptr<char[]> pBuf(new char[1025]);
+ int nRead;
+ while( (nRead = aMessStream.Read(pBuf.get(), 1024)) > 0 )
+ {
+ aMemStream.WriteBytes(pBuf.get(), nRead);
+ }
+ pBuf.reset();
+
+ aMemStream.FlushBuffer();
+ aMemStream.Seek( 0 );
+ void const * pData = aMemStream.GetData();
+ sal_Int32 nLen = aMemStream.TellEnd();
+
+ rContentType = aParent.GetContentType();
+ return Sequence<sal_Int8>(static_cast<sal_Int8 const *>(pData), nLen);
+}
+
+
+namespace
+{
+ void appendDigits( sal_Int32 _nNumber, sal_Int8 nDigits, OUStringBuffer& _rOut )
+ {
+ sal_Int32 nCurLen = _rOut.getLength();
+ _rOut.append( _nNumber );
+ while ( _rOut.getLength() - nCurLen < nDigits )
+ _rOut.insert( nCurLen, '0' );
+ }
+}
+
+
+void ODatabaseForm::AppendComponent(HtmlSuccessfulObjList& rList, const Reference<XPropertySet>& xComponentSet, std::u16string_view rNamePrefix,
+ const Reference<XControl>& rxSubmitButton, const css::awt::MouseEvent& MouseEvt)
+{
+ if (!xComponentSet.is())
+ return;
+
+ // TODO: Catch nested Forms; or would we need to submit them?
+ if (!hasProperty(PROPERTY_CLASSID, xComponentSet))
+ return;
+
+ // Get names
+ if (!hasProperty(PROPERTY_NAME, xComponentSet))
+ return;
+
+ sal_Int16 nClassId = 0;
+ xComponentSet->getPropertyValue(PROPERTY_CLASSID) >>= nClassId;
+ OUString aName;
+ xComponentSet->getPropertyValue( PROPERTY_NAME ) >>= aName;
+ if( aName.isEmpty() && nClassId != FormComponentType::IMAGEBUTTON)
+ return;
+ else // Extend name with the prefix
+ aName = rNamePrefix + aName;
+
+ switch( nClassId )
+ {
+ // Buttons
+ case FormComponentType::COMMANDBUTTON:
+ {
+ // We only evaluate the pressed Submit button
+ // If one is passed at all
+ if( rxSubmitButton.is() )
+ {
+ Reference<XPropertySet> xSubmitButtonComponent(rxSubmitButton->getModel(), UNO_QUERY);
+ if (xSubmitButtonComponent == xComponentSet && hasProperty(PROPERTY_LABEL, xComponentSet))
+ {
+ // <name>=<label>
+ OUString aLabel;
+ xComponentSet->getPropertyValue( PROPERTY_LABEL ) >>= aLabel;
+ rList.emplace_back(aName, aLabel );
+ }
+ }
+ } break;
+
+ // ImageButtons
+ case FormComponentType::IMAGEBUTTON:
+ {
+ // We only evaluate the pressed Submit button
+ // If one is passed at all
+ if( rxSubmitButton.is() )
+ {
+ Reference<XPropertySet> xSubmitButtonComponent(rxSubmitButton->getModel(), UNO_QUERY);
+ if (xSubmitButtonComponent == xComponentSet)
+ {
+ // <name>.x=<pos.X>&<name>.y=<pos.Y>
+ OUString aRhs = OUString::number( MouseEvt.X );
+
+ // Only if a name is available we have a name.x
+ OUStringBuffer aLhs(aName);
+ if (!aName.isEmpty())
+ aLhs.append(".x");
+ else
+ aLhs.append("x");
+ rList.emplace_back(aLhs.makeStringAndClear(), aRhs );
+
+ aLhs.append(aName);
+ aRhs = OUString::number( MouseEvt.Y );
+ if (!aName.isEmpty())
+ aLhs.append(".y");
+ else
+ aLhs.append("y");
+ rList.emplace_back(aLhs.makeStringAndClear(), aRhs );
+ }
+ }
+ } break;
+
+ // CheckBoxes/RadioButtons
+ case FormComponentType::CHECKBOX:
+ case FormComponentType::RADIOBUTTON:
+ {
+ // <name>=<refValue>
+ if( !hasProperty(PROPERTY_STATE, xComponentSet) )
+ break;
+ sal_Int16 nChecked = 0;
+ xComponentSet->getPropertyValue( PROPERTY_STATE ) >>= nChecked;
+ if( nChecked != 1 )
+ break;
+
+ OUString aStrValue;
+ if( hasProperty(PROPERTY_REFVALUE, xComponentSet) )
+ xComponentSet->getPropertyValue( PROPERTY_REFVALUE ) >>= aStrValue;
+
+ rList.emplace_back(aName, aStrValue );
+ } break;
+
+ // Edit
+ case FormComponentType::TEXTFIELD:
+ {
+ // <name>=<text>
+ if( !hasProperty(PROPERTY_TEXT, xComponentSet) )
+ break;
+
+ // Special treatment for multiline edit only if we have a control for it
+ Any aTmp = xComponentSet->getPropertyValue( PROPERTY_MULTILINE );
+ bool bMulti = rxSubmitButton.is()
+ && (aTmp.getValueType().getTypeClass() == TypeClass_BOOLEAN)
+ && getBOOL(aTmp);
+ OUString sText;
+ if ( bMulti ) // For multiline edit, get the text at the control
+ {
+
+ Reference<XControlContainer> xControlContainer(rxSubmitButton->getContext(), UNO_QUERY);
+ if( !xControlContainer.is() ) break;
+
+ // Find the right control
+ bool bFound = false;
+ const Sequence<Reference<XControl>> aControls = xControlContainer->getControls();
+ for( auto const& xControl : aControls )
+ {
+ Reference<XPropertySet> xModel(xControl->getModel(), UNO_QUERY);
+ if ((bFound = xModel == xComponentSet))
+ {
+ Reference<XTextComponent> xTextComponent(xControl, UNO_QUERY);
+ if( xTextComponent.is() )
+ sText = xTextComponent->getText();
+ break;
+ }
+ }
+ // Couldn't find control or it does not exist (edit in the grid)
+ if (!bFound)
+ xComponentSet->getPropertyValue( PROPERTY_TEXT ) >>= sText;
+ }
+ else
+ xComponentSet->getPropertyValue( PROPERTY_TEXT ) >>= sText;
+
+ rList.emplace_back(aName, sText );
+ } break;
+
+ // ComboBox, Patternfield
+ case FormComponentType::COMBOBOX:
+ case FormComponentType::PATTERNFIELD:
+ {
+ // <name>=<text>
+ if( hasProperty(PROPERTY_TEXT, xComponentSet) )
+ {
+ OUString aText;
+ xComponentSet->getPropertyValue( PROPERTY_TEXT ) >>= aText;
+ rList.emplace_back(aName, aText );
+ }
+ } break;
+ case FormComponentType::CURRENCYFIELD:
+ case FormComponentType::NUMERICFIELD:
+ {
+ // <name>=<value> // Value is a double with dot as decimal delimiter
+ // no value (NULL) means empty value
+ if( hasProperty(PROPERTY_VALUE, xComponentSet) )
+ {
+ OUString aText;
+ Any aVal = xComponentSet->getPropertyValue( PROPERTY_VALUE );
+
+ double aDoubleVal = 0;
+ if (aVal >>= aDoubleVal)
+ {
+ sal_Int16 nScale = 0;
+ xComponentSet->getPropertyValue( PROPERTY_DECIMAL_ACCURACY ) >>= nScale;
+ aText = ::rtl::math::doubleToUString(aDoubleVal, rtl_math_StringFormat_F, nScale, '.', true);
+ }
+ rList.emplace_back(aName, aText );
+ }
+ } break;
+ case FormComponentType::DATEFIELD:
+ {
+ // <name>=<value> // Value is a Date with the format MM-DD-YYYY
+ // no value (NULL) means empty value
+ if( hasProperty(PROPERTY_DATE, xComponentSet) )
+ {
+ OUString aText;
+ Any aVal = xComponentSet->getPropertyValue( PROPERTY_DATE );
+ sal_Int32 nInt32Val = 0;
+ if (aVal >>= nInt32Val)
+ {
+ ::Date aDate( nInt32Val );
+ OUStringBuffer aBuffer;
+ appendDigits( aDate.GetMonth(), 2, aBuffer );
+ aBuffer.append( '-' );
+ appendDigits( aDate.GetDay(), 2, aBuffer );
+ aBuffer.append( '-' );
+ appendDigits( aDate.GetYear(), 4, aBuffer );
+ aText = aBuffer.makeStringAndClear();
+ }
+ rList.emplace_back(aName, aText );
+ }
+ } break;
+ case FormComponentType::TIMEFIELD:
+ {
+ // <name>=<value> // Value is a Time with the format HH:MM:SS
+ // no value (NULL) means empty value
+ if( hasProperty(PROPERTY_TIME, xComponentSet) )
+ {
+ OUString aText;
+ Any aVal = xComponentSet->getPropertyValue( PROPERTY_TIME );
+ sal_Int32 nInt32Val = 0;
+ if (aVal >>= nInt32Val)
+ {
+ ::tools::Time aTime(nInt32Val);
+ OUStringBuffer aBuffer;
+ appendDigits( aTime.GetHour(), 2, aBuffer );
+ aBuffer.append( '-' );
+ appendDigits( aTime.GetMin(), 2, aBuffer );
+ aBuffer.append( '-' );
+ appendDigits( aTime.GetSec(), 2, aBuffer );
+ aText = aBuffer.makeStringAndClear();
+ }
+ rList.emplace_back(aName, aText );
+ }
+ } break;
+
+ // starform
+ case FormComponentType::HIDDENCONTROL:
+ {
+
+ // <name>=<value>
+ if( hasProperty(PROPERTY_HIDDEN_VALUE, xComponentSet) )
+ {
+ OUString aText;
+ xComponentSet->getPropertyValue( PROPERTY_HIDDEN_VALUE ) >>= aText;
+ rList.emplace_back(aName, aText );
+ }
+ } break;
+
+ // starform
+ case FormComponentType::FILECONTROL:
+ {
+ // <name>=<text>
+ if( hasProperty(PROPERTY_TEXT, xComponentSet) )
+ {
+
+ OUString aText;
+ xComponentSet->getPropertyValue( PROPERTY_TEXT ) >>= aText;
+ rList.emplace_back(aName, aText, SUCCESSFUL_REPRESENT_FILE );
+ }
+ } break;
+
+ // starform
+ case FormComponentType::LISTBOX:
+ {
+
+ // <name>=<Token0>&<name>=<Token1>&...&<name>=<TokenN> (multiple selection)
+ if (!hasProperty(PROPERTY_SELECT_SEQ, xComponentSet) ||
+ !hasProperty(PROPERTY_STRINGITEMLIST, xComponentSet))
+ break;
+
+ // Displayed values
+ Sequence< OUString > aVisibleList;
+ xComponentSet->getPropertyValue( PROPERTY_STRINGITEMLIST ) >>= aVisibleList;
+ sal_Int32 nStringCnt = aVisibleList.getLength();
+ const OUString* pStrings = aVisibleList.getConstArray();
+
+ // Value list
+ Sequence< OUString > aValueList;
+ xComponentSet->getPropertyValue( PROPERTY_VALUE_SEQ ) >>= aValueList;
+ sal_Int32 nValCnt = aValueList.getLength();
+ const OUString* pVals = aValueList.getConstArray();
+
+ // Selection
+ Sequence<sal_Int16> aSelectList;
+ xComponentSet->getPropertyValue( PROPERTY_SELECT_SEQ ) >>= aSelectList;
+ sal_Int32 nSelCount = aSelectList.getLength();
+ const sal_Int16* pSels = aSelectList.getConstArray();
+
+ // Simple or multiple selection
+ // For simple selections MT only accounts for the list's first entry.
+ if (nSelCount > 1 && !getBOOL(xComponentSet->getPropertyValue(PROPERTY_MULTISELECTION)))
+ nSelCount = 1;
+
+ // The indices in the selection list can also be invalid, so we first have to
+ // find the valid ones to determine the length of the new list.
+ sal_Int32 nCurCnt = 0;
+ sal_Int32 i;
+ for( i=0; i<nSelCount; ++i )
+ {
+ if( pSels[i] < nStringCnt )
+ ++nCurCnt;
+ }
+
+ OUString aSubValue;
+ for(i=0; i<nCurCnt; ++i )
+ {
+ sal_Int16 nSelPos = pSels[i];
+ if (nSelPos < nValCnt && !pVals[nSelPos].isEmpty())
+ {
+ aSubValue = pVals[nSelPos];
+ }
+ else
+ {
+ aSubValue = pStrings[nSelPos];
+ }
+ rList.emplace_back(aName, aSubValue );
+ }
+ } break;
+ case FormComponentType::GRIDCONTROL:
+ {
+ // Each of the column values is sent;
+ // the name is extended by the grid's prefix.
+ Reference<XIndexAccess> xContainer(xComponentSet, UNO_QUERY);
+ if (!xContainer.is())
+ break;
+
+ aName += ".";
+
+ Reference<XPropertySet> xSet;
+ sal_Int32 nCount = xContainer->getCount();
+ // we know already how many objects should be appended,
+ // so why not allocate the space for them
+ rList.reserve( nCount + rList.capacity() ); // not size()
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ {
+ xContainer->getByIndex(i) >>= xSet;
+ if (xSet.is())
+ AppendComponent(rList, xSet, aName, rxSubmitButton, MouseEvt);
+ }
+ }
+ }
+}
+
+
+void ODatabaseForm::FillSuccessfulList( HtmlSuccessfulObjList& rList,
+ const Reference<XControl>& rxSubmitButton, const css::awt::MouseEvent& MouseEvt )
+{
+ // Delete list
+ rList.clear();
+ // Iterate over Components
+ Reference<XPropertySet> xComponentSet;
+
+ // we know already how many objects should be appended,
+ // so why not allocate the space for them
+ rList.reserve( getCount() );
+ for( sal_Int32 nIndex=0; nIndex < getCount(); nIndex++ )
+ {
+ getByIndex( nIndex ) >>= xComponentSet;
+ AppendComponent(rList, xComponentSet, std::u16string_view(), rxSubmitButton, MouseEvt);
+ }
+}
+
+
+void ODatabaseForm::Encode( OUString& rString )
+{
+ OUStringBuffer aResult;
+
+ // Line endings are represented as CR
+ rString = convertLineEnd(rString, LINEEND_CR);
+
+ // Check each character
+ sal_Int32 nStrLen = rString.getLength();
+ sal_Unicode nCharCode;
+ for( sal_Int32 nCurPos=0; nCurPos < nStrLen; ++nCurPos )
+ {
+ nCharCode = rString[nCurPos];
+
+ // Handle chars, which are not an alphanumeric character and character codes > 127
+ if( (!rtl::isAsciiAlphanumeric(nCharCode) && nCharCode != ' ')
+ || nCharCode > 127 )
+ {
+ switch( nCharCode )
+ {
+ case 13: // CR
+ aResult.append("%0D%0A"); // CR LF in hex
+ break;
+
+
+ // Special treatment for Netscape
+ case 42: // '*'
+ case 45: // '-'
+ case 46: // '.'
+ case 64: // '@'
+ case 95: // '_'
+ aResult.append(nCharCode);
+ break;
+
+ default:
+ {
+ // Convert to hex
+ short nHi = static_cast<sal_Int16>(nCharCode) / 16;
+ short nLo = static_cast<sal_Int16>(nCharCode) - (nHi*16);
+ if( nHi > 9 ) nHi += int('A')-10; else nHi += int('0');
+ if( nLo > 9 ) nLo += int('A')-10; else nLo += int('0');
+ aResult.append('%');
+ aResult.append(static_cast<sal_Unicode>(nHi));
+ aResult.append(static_cast<sal_Unicode>(nLo));
+ }
+ }
+ }
+ else
+ aResult.append(nCharCode);
+ }
+
+ // Replace spaces with '+'
+ rString = aResult.makeStringAndClear().replace(' ', '+');
+}
+
+
+void ODatabaseForm::InsertTextPart( INetMIMEMessage& rParent, std::u16string_view rName,
+ std::u16string_view rData )
+{
+ // Create part as MessageChild
+ std::unique_ptr<INetMIMEMessage> pChild(new INetMIMEMessage);
+
+ // Header
+ //TODO: Encode rName into a properly formatted Content-Disposition header
+ // field as per RFC 2231:
+ OUString aContentDisp = OUString::Concat("form-data; name=\"") + rName + "\"";
+ pChild->SetContentDisposition(aContentDisp);
+
+ rtl_TextEncoding eSystemEncoding = osl_getThreadTextEncoding();
+ const char* pBestMatchingEncoding = rtl_getBestMimeCharsetFromTextEncoding( eSystemEncoding );
+ OUString aBestMatchingEncoding = OUString::createFromAscii(pBestMatchingEncoding);
+ pChild->SetContentType(
+ "text/plain; charset=\"" + aBestMatchingEncoding + "\"");
+ pChild->SetContentTransferEncoding("8bit");
+
+ // Body
+ SvMemoryStream* pStream = new SvMemoryStream;
+ pStream->WriteLine( OUStringToOString(rData, rtl_getTextEncodingFromMimeCharset(pBestMatchingEncoding)) );
+ pStream->FlushBuffer();
+ pStream->Seek( 0 );
+ pChild->SetDocumentLB( new SvLockBytes(pStream, true) );
+ rParent.AttachChild( std::move(pChild) );
+}
+
+
+void ODatabaseForm::InsertFilePart( INetMIMEMessage& rParent, std::u16string_view rName,
+ const OUString& rFileName )
+{
+ OUString aFileName(rFileName);
+ OUString aContentType(CONTENT_TYPE_STR_TEXT_PLAIN);
+ std::unique_ptr<SvStream> pStream;
+
+ if (!aFileName.isEmpty())
+ {
+ // We can only process File URLs yet
+ INetURLObject aURL;
+ aURL.SetSmartProtocol(INetProtocol::File);
+ aURL.SetSmartURL(rFileName);
+ if( INetProtocol::File == aURL.GetProtocol() )
+ {
+ aFileName = INetURLObject::decode(aURL.PathToFileName(), INetURLObject::DecodeMechanism::Unambiguous);
+ pStream = ::utl::UcbStreamHelper::CreateStream(aFileName, StreamMode::READ);
+ if (!pStream || (pStream->GetError() != ERRCODE_NONE))
+ {
+ pStream.reset();
+ }
+ sal_Int32 nSepInd = aFileName.lastIndexOf('.');
+ OUString aExtension = aFileName.copy( nSepInd + 1 );
+ INetContentType eContentType = INetContentTypes::GetContentType4Extension( aExtension );
+ if (eContentType != CONTENT_TYPE_UNKNOWN)
+ aContentType = INetContentTypes::GetContentType(eContentType);
+ }
+ }
+
+ // If something didn't work, we create an empty MemoryStream
+ if( !pStream )
+ pStream.reset( new SvMemoryStream );
+
+
+ // Create part as MessageChild
+ std::unique_ptr<INetMIMEMessage> pChild(new INetMIMEMessage);
+
+
+ // Header
+ //TODO: Encode rName and aFileName into a properly formatted
+ // Content-Disposition header field as per RFC 2231:
+ OUString aContentDisp =
+ OUString::Concat("form-data; name=\"") +
+ rName +
+ "\""
+ "; filename=\"" +
+ aFileName +
+ "\"";
+ pChild->SetContentDisposition(aContentDisp);
+ pChild->SetContentType( aContentType );
+ pChild->SetContentTransferEncoding("8bit");
+
+
+ // Body
+ pChild->SetDocumentLB( new SvLockBytes(pStream.release(), true) );
+ rParent.AttachChild( std::move(pChild) );
+}
+
+
+// internals
+
+void ODatabaseForm::onError( const SQLErrorEvent& _rEvent )
+{
+ m_aErrorListeners.notifyEach( &XSQLErrorListener::errorOccured, _rEvent );
+}
+
+
+void ODatabaseForm::onError( const SQLException& _rException, const OUString& _rContextDescription )
+{
+ if ( !m_aErrorListeners.getLength() )
+ return;
+
+ SQLErrorEvent aEvent( *this, Any( prependErrorInfo( _rException, *this, _rContextDescription ) ) );
+ onError( aEvent );
+}
+
+
+void ODatabaseForm::updateParameterInfo()
+{
+ m_aParameterManager.updateParameterInfo( m_aFilterManager );
+}
+
+
+bool ODatabaseForm::hasValidParent() const
+{
+ // do we have to fill the parameters again?
+ if (!m_bSubForm)
+ return true;
+ Reference<XResultSet> xResultSet(m_xParent, UNO_QUERY);
+ if (!xResultSet.is())
+ {
+ OSL_FAIL("ODatabaseForm::hasValidParent() : no parent resultset !");
+ return false;
+ }
+ try
+ {
+ Reference< XPropertySet > xSet( m_xParent, UNO_QUERY );
+ Reference< XLoadable > xLoad( m_xParent, UNO_QUERY );
+ if ( xLoad->isLoaded()
+ && ( xResultSet->isBeforeFirst()
+ || xResultSet->isAfterLast()
+ || getBOOL( xSet->getPropertyValue( PROPERTY_ISNEW ) )
+ )
+ )
+ // the parent form is loaded and on a "virtual" row -> not valid
+ return false;
+ }
+ catch(const Exception&)
+ {
+ // parent could be forwardonly?
+ return false;
+ }
+ return true;
+}
+
+
+bool ODatabaseForm::fillParameters( ::osl::ResettableMutexGuard& _rClearForNotifies, const Reference< XInteractionHandler >& _rxCompletionHandler )
+{
+ // do we have to fill the parameters again?
+ if ( !m_aParameterManager.isUpToDate() )
+ updateParameterInfo();
+
+ // is there a valid parent?
+ if ( m_bSubForm && !hasValidParent() )
+ return true;
+
+ // ensure we're connected
+ if ( !implEnsureConnection() )
+ return false;
+
+ if ( m_aParameterManager.isUpToDate() )
+ return m_aParameterManager.fillParameterValues( _rxCompletionHandler, _rClearForNotifies );
+
+ return true;
+}
+
+
+void ODatabaseForm::saveInsertOnlyState( )
+{
+ OSL_ENSURE( !m_aIgnoreResult.hasValue(), "ODatabaseForm::saveInsertOnlyState: overriding old value!" );
+ m_aIgnoreResult = m_xAggregateSet->getPropertyValue( PROPERTY_INSERTONLY );
+}
+
+
+void ODatabaseForm::restoreInsertOnlyState( )
+{
+ if ( m_aIgnoreResult.hasValue() )
+ {
+ m_xAggregateSet->setPropertyValue( PROPERTY_INSERTONLY, m_aIgnoreResult );
+ m_aIgnoreResult = Any();
+ }
+}
+
+
+bool ODatabaseForm::executeRowSet(::osl::ResettableMutexGuard& _rClearForNotifies, bool bMoveToFirst, const Reference< XInteractionHandler >& _rxCompletionHandler)
+{
+ if (!m_xAggregateAsRowSet.is())
+ return false;
+
+ if (!fillParameters(_rClearForNotifies, _rxCompletionHandler))
+ return false;
+
+ restoreInsertOnlyState( );
+
+ // ensure the aggregated row set has the correct properties
+ sal_Int32 nConcurrency = ResultSetConcurrency::READ_ONLY;
+
+ // if we have a parent, who is not positioned on a valid row
+ // we can't be updatable!
+ if (m_bSubForm && !hasValidParent())
+ {
+ nConcurrency = ResultSetConcurrency::READ_ONLY;
+
+ // don't use any parameters if we don't have a valid parent
+ m_aParameterManager.setAllParametersNull();
+
+ // switch to "insert only" mode
+ saveInsertOnlyState( );
+ m_xAggregateSet->setPropertyValue( PROPERTY_INSERTONLY, Any( true ) );
+ }
+ else if (m_bAllowInsert || m_bAllowUpdate || m_bAllowDelete)
+ nConcurrency = ResultSetConcurrency::UPDATABLE;
+ else
+ nConcurrency = ResultSetConcurrency::READ_ONLY;
+
+ m_xAggregateSet->setPropertyValue( PROPERTY_RESULTSET_CONCURRENCY, Any( nConcurrency ) );
+ m_xAggregateSet->setPropertyValue( PROPERTY_RESULTSET_TYPE, Any( sal_Int32(ResultSetType::SCROLL_SENSITIVE) ) );
+
+ bool bSuccess = false;
+ try
+ {
+ m_xAggregateAsRowSet->execute();
+ bSuccess = true;
+ }
+ catch(const RowSetVetoException&)
+ {
+ }
+ catch(const SQLException& eDb)
+ {
+ _rClearForNotifies.clear();
+ if (!m_sCurrentErrorContext.isEmpty())
+ onError(eDb, m_sCurrentErrorContext);
+ else
+ onError(eDb, ResourceManager::loadString(RID_STR_READERROR));
+ _rClearForNotifies.reset();
+
+ restoreInsertOnlyState( );
+ }
+
+ if (bSuccess)
+ {
+ // adjust the privilege property
+ // m_nPrivileges;
+ m_xAggregateSet->getPropertyValue(PROPERTY_PRIVILEGES) >>= m_nPrivileges;
+ if (!m_bAllowInsert)
+ m_nPrivileges &= ~Privilege::INSERT;
+ if (!m_bAllowUpdate)
+ m_nPrivileges &= ~Privilege::UPDATE;
+ if (!m_bAllowDelete)
+ m_nPrivileges &= ~Privilege::DELETE;
+
+ if (bMoveToFirst)
+ {
+ // the row set is positioned _before_ the first row (per definitionem), so move the set ...
+ try
+ {
+ // if we have an insert only rowset we move to the insert row
+ next();
+ if (((m_nPrivileges & Privilege::INSERT) == Privilege::INSERT)
+ && isAfterLast())
+ {
+ // move on the insert row of set
+ // resetting must be done later, after the load events have been posted
+ // see: moveToInsertRow and load , reload
+ Reference<XResultSetUpdate> xUpdate;
+ if (query_aggregation( m_xAggregate, xUpdate))
+ xUpdate->moveToInsertRow();
+ }
+ }
+ catch(const SQLException& eDB)
+ {
+ _rClearForNotifies.clear();
+ if (!m_sCurrentErrorContext.isEmpty())
+ onError(eDB, m_sCurrentErrorContext);
+ else
+ onError(eDB, ResourceManager::loadString(RID_STR_READERROR));
+ _rClearForNotifies.reset();
+ bSuccess = false;
+ }
+ }
+ }
+ return bSuccess;
+}
+
+
+void ODatabaseForm::disposing()
+{
+ if (m_xAggregatePropertyMultiplexer.is())
+ m_xAggregatePropertyMultiplexer->dispose();
+
+ if (m_bLoaded)
+ unload();
+
+ // cancel the submit/reset-thread
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_pThread.clear();
+ }
+
+ EventObject aEvt(static_cast<XWeak*>(this));
+ m_aLoadListeners.disposeAndClear(aEvt);
+ m_aRowSetApproveListeners.disposeAndClear(aEvt);
+ m_aResetListeners.disposeAndClear(aEvt);
+ m_aSubmitListeners.disposeAndClear(aEvt);
+ m_aErrorListeners.disposeAndClear(aEvt);
+
+ m_aParameterManager.dispose(); // To free any references it may have to be me
+ m_aFilterManager.dispose(); // (ditto)
+
+ OFormComponents::disposing();
+ OPropertySetAggregationHelper::disposing();
+
+ // stop listening on the aggregate
+ if (m_xAggregateAsRowSet.is())
+ m_xAggregateAsRowSet->removeRowSetListener(this);
+
+ // dispose the active connection
+ Reference<XComponent> xAggregationComponent;
+ if (query_aggregation(m_xAggregate, xAggregationComponent))
+ xAggregationComponent->dispose();
+
+ m_aPropertyBagHelper.dispose();
+}
+
+
+Reference< XConnection > ODatabaseForm::getConnection()
+{
+ Reference< XConnection > xConn;
+ m_xAggregateSet->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xConn;
+ return xConn;
+}
+
+
+::osl::Mutex& ODatabaseForm::getMutex()
+{
+ return m_aMutex;
+}
+
+
+// property handling
+
+void ODatabaseForm::describeFixedAndAggregateProperties(
+ Sequence< Property >& _rProps,
+ Sequence< Property >& _rAggregateProps ) const
+{
+ _rProps.realloc( 23 );
+ css::beans::Property* pProperties = _rProps.getArray();
+
+ if (m_xAggregateSet.is())
+ _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties();
+
+
+ // we want to "override" the privileges, since we have additional "AllowInsert" etc. properties
+ RemoveProperty( _rAggregateProps, PROPERTY_PRIVILEGES );
+
+ // InsertOnly is also to be overridden, since we sometimes change it ourself
+ RemoveProperty( _rAggregateProps, PROPERTY_INSERTONLY );
+
+ // we remove and re-declare the DataSourceName property, 'cause we want it to be constrained, and the
+ // original property of our aggregate isn't
+ RemoveProperty( _rAggregateProps, PROPERTY_DATASOURCE );
+
+ // for connection sharing, we need to override the ActiveConnection property, too
+ RemoveProperty( _rAggregateProps, PROPERTY_ACTIVE_CONNECTION );
+
+ // the Filter property is also overwritten, since we have some implicit filters
+ // (e.g. the ones which result from linking master fields to detail fields
+ // via column names instead of parameters)
+ RemoveProperty( _rAggregateProps, PROPERTY_FILTER );
+ RemoveProperty( _rAggregateProps, PROPERTY_HAVINGCLAUSE );
+ RemoveProperty( _rAggregateProps, PROPERTY_APPLYFILTER );
+
+ *pProperties++ = css::beans::Property(PROPERTY_ACTIVE_CONNECTION, PROPERTY_ID_ACTIVE_CONNECTION, cppu::UnoType<XConnection>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT |
+ css::beans::PropertyAttribute::MAYBEVOID | PropertyAttribute::CONSTRAINED);
+ *pProperties++ = css::beans::Property(PROPERTY_APPLYFILTER, PROPERTY_ID_APPLYFILTER, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_NAME, PROPERTY_ID_NAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_MASTERFIELDS, PROPERTY_ID_MASTERFIELDS, cppu::UnoType<Sequence< OUString >>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_DETAILFIELDS, PROPERTY_ID_DETAILFIELDS, cppu::UnoType<Sequence< OUString >>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_DATASOURCE, PROPERTY_ID_DATASOURCE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::CONSTRAINED);
+ *pProperties++ = css::beans::Property(PROPERTY_CYCLE, PROPERTY_ID_CYCLE, cppu::UnoType<TabulatorCycle>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FILTER, PROPERTY_ID_FILTER, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_HAVINGCLAUSE, PROPERTY_ID_HAVINGCLAUSE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_INSERTONLY, PROPERTY_ID_INSERTONLY, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_NAVIGATION, PROPERTY_ID_NAVIGATION, cppu::UnoType<NavigationBarMode>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_ALLOWADDITIONS, PROPERTY_ID_ALLOWADDITIONS, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_ALLOWEDITS, PROPERTY_ID_ALLOWEDITS, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_ALLOWDELETIONS, PROPERTY_ID_ALLOWDELETIONS, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_PRIVILEGES, PROPERTY_ID_PRIVILEGES, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY);
+ *pProperties++ = css::beans::Property(PROPERTY_TARGET_URL, PROPERTY_ID_TARGET_URL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TARGET_FRAME, PROPERTY_ID_TARGET_FRAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_SUBMIT_METHOD, PROPERTY_ID_SUBMIT_METHOD, cppu::UnoType<FormSubmitMethod>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_SUBMIT_ENCODING, PROPERTY_ID_SUBMIT_ENCODING, cppu::UnoType<FormSubmitEncoding>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_DYNAMIC_CONTROL_BORDER, PROPERTY_ID_DYNAMIC_CONTROL_BORDER, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::MAYBEDEFAULT );
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROL_BORDER_COLOR_FOCUS, PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROL_BORDER_COLOR_MOUSE, PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROL_BORDER_COLOR_INVALID, PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+Reference< XMultiPropertySet > ODatabaseForm::getPropertiesInterface()
+{
+ return Reference< XMultiPropertySet >( *this, UNO_QUERY );
+}
+
+
+::cppu::IPropertyArrayHelper& ODatabaseForm::getInfoHelper()
+{
+ return m_aPropertyBagHelper.getInfoHelper();
+}
+
+
+Reference< XPropertySetInfo > ODatabaseForm::getPropertySetInfo()
+{
+ return createPropertySetInfo( getInfoHelper() );
+}
+
+
+void SAL_CALL ODatabaseForm::addProperty( const OUString& _rName, ::sal_Int16 _nAttributes, const Any& _rInitialValue )
+{
+ m_aPropertyBagHelper.addProperty( _rName, _nAttributes, _rInitialValue );
+}
+
+
+void SAL_CALL ODatabaseForm::removeProperty( const OUString& _rName )
+{
+ m_aPropertyBagHelper.removeProperty( _rName );
+}
+
+
+Sequence< PropertyValue > SAL_CALL ODatabaseForm::getPropertyValues()
+{
+ return m_aPropertyBagHelper.getPropertyValues();
+}
+
+
+void SAL_CALL ODatabaseForm::setPropertyValues( const Sequence< PropertyValue >& _rProps )
+{
+ m_aPropertyBagHelper.setPropertyValues( _rProps );
+}
+
+
+Any SAL_CALL ODatabaseForm::getWarnings( )
+{
+ return m_aWarnings.getWarnings();
+}
+
+
+void SAL_CALL ODatabaseForm::clearWarnings( )
+{
+ m_aWarnings.clearWarnings();
+}
+
+
+Reference< XCloneable > SAL_CALL ODatabaseForm::createClone( )
+{
+ rtl::Reference<ODatabaseForm> pClone = new ODatabaseForm( *this );
+ pClone->clonedFrom( *this );
+ return pClone;
+}
+
+
+void ODatabaseForm::fire( sal_Int32* pnHandles, const Any* pNewValues, const Any* pOldValues, sal_Int32 nCount )
+{
+ // same as in getFastPropertyValue(sal_Int32) : if we're resetting currently don't fire any changes of the
+ // IsModified property from sal_False to sal_True, as this is only temporary 'til the reset is done
+ if (m_nResetsPending > 0)
+ {
+ // look for the PROPERTY_ID_ISMODIFIED
+ sal_Int32 nPos = 0;
+ for (nPos=0; nPos<nCount; ++nPos)
+ if (pnHandles[nPos] == PROPERTY_ID_ISMODIFIED)
+ break;
+
+ if ((nPos < nCount) && (pNewValues[nPos].getValueType().getTypeClass() == TypeClass_BOOLEAN) && getBOOL(pNewValues[nPos]))
+ { // yeah, we found it, and it changed to TRUE
+ if (nPos == 0)
+ { // just cut the first element
+ ++pnHandles;
+ ++pNewValues;
+ ++pOldValues;
+ --nCount;
+ }
+ else if (nPos == nCount - 1)
+ // just cut the last element
+ --nCount;
+ else
+ { // split into two base class calls
+ OPropertySetAggregationHelper::fire(pnHandles, pNewValues, pOldValues, nPos, false/*bVetoable*/);
+ ++nPos;
+ OPropertySetAggregationHelper::fire(pnHandles + nPos, pNewValues + nPos, pOldValues + nPos, nCount - nPos, false/*bVetoable*/);
+ return;
+ }
+ }
+ }
+
+ OPropertySetAggregationHelper::fire(pnHandles, pNewValues, pOldValues, nCount, false/*bVetoable*/);
+}
+
+
+Any SAL_CALL ODatabaseForm::getFastPropertyValue( sal_Int32 nHandle )
+{
+ if ((nHandle == PROPERTY_ID_ISMODIFIED) && (m_nResetsPending > 0))
+ return css::uno::Any(false);
+ // don't allow the aggregate which is currently being reset to return a (temporary) "yes"
+ else
+ return OPropertySetAggregationHelper::getFastPropertyValue(nHandle);
+}
+
+
+void ODatabaseForm::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_INSERTONLY:
+ rValue <<= m_bInsertOnly;
+ break;
+
+ case PROPERTY_ID_FILTER:
+ rValue <<= m_aFilterManager.getFilterComponent( FilterManager::FilterComponent::PublicFilter );
+ break;
+
+ case PROPERTY_ID_HAVINGCLAUSE:
+ rValue <<= m_aFilterManager.getFilterComponent( FilterManager::FilterComponent::PublicHaving );
+ break;
+
+ case PROPERTY_ID_APPLYFILTER:
+ rValue <<= m_aFilterManager.isApplyPublicFilter();
+ break;
+
+ case PROPERTY_ID_DATASOURCE:
+ rValue = m_xAggregateSet->getPropertyValue( PROPERTY_DATASOURCE );
+ break;
+
+ case PROPERTY_ID_TARGET_URL:
+ rValue <<= m_aTargetURL;
+ break;
+ case PROPERTY_ID_TARGET_FRAME:
+ rValue <<= m_aTargetFrame;
+ break;
+ case PROPERTY_ID_SUBMIT_METHOD:
+ rValue <<= m_eSubmitMethod;
+ break;
+ case PROPERTY_ID_SUBMIT_ENCODING:
+ rValue <<= m_eSubmitEncoding;
+ break;
+ case PROPERTY_ID_NAME:
+ rValue <<= m_sName;
+ break;
+ case PROPERTY_ID_MASTERFIELDS:
+ rValue <<= m_aMasterFields;
+ break;
+ case PROPERTY_ID_DETAILFIELDS:
+ rValue <<= m_aDetailFields;
+ break;
+ case PROPERTY_ID_CYCLE:
+ rValue = m_aCycle;
+ break;
+ case PROPERTY_ID_NAVIGATION:
+ rValue <<= m_eNavigation;
+ break;
+ case PROPERTY_ID_ALLOWADDITIONS:
+ rValue <<= m_bAllowInsert;
+ break;
+ case PROPERTY_ID_ALLOWEDITS:
+ rValue <<= m_bAllowUpdate;
+ break;
+ case PROPERTY_ID_ALLOWDELETIONS:
+ rValue <<= m_bAllowDelete;
+ break;
+ case PROPERTY_ID_PRIVILEGES:
+ rValue <<= m_nPrivileges;
+ break;
+ case PROPERTY_ID_DYNAMIC_CONTROL_BORDER:
+ rValue = m_aDynamicControlBorder;
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS:
+ rValue = m_aControlBorderColorFocus;
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE:
+ rValue = m_aControlBorderColorMouse;
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID:
+ rValue = m_aControlBorderColorInvalid;
+ break;
+ default:
+ if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( nHandle ) )
+ m_aPropertyBagHelper.getDynamicFastPropertyValue( nHandle, rValue );
+ else
+ OPropertySetAggregationHelper::getFastPropertyValue( rValue, nHandle );
+ break;
+ }
+}
+
+sal_Bool ODatabaseForm::convertFastPropertyValue( Any& rConvertedValue, Any& rOldValue,
+ sal_Int32 nHandle, const Any& rValue )
+{
+ bool bModified(false);
+ switch (nHandle)
+ {
+ case PROPERTY_ID_INSERTONLY:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_bInsertOnly );
+ break;
+
+ case PROPERTY_ID_FILTER:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aFilterManager.getFilterComponent( FilterManager::FilterComponent::PublicFilter ) );
+ break;
+
+ case PROPERTY_ID_HAVINGCLAUSE:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aFilterManager.getFilterComponent( FilterManager::FilterComponent::PublicHaving ) );
+ break;
+
+ case PROPERTY_ID_APPLYFILTER:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aFilterManager.isApplyPublicFilter() );
+ break;
+
+ case PROPERTY_ID_DATASOURCE:
+ {
+ Any aAggregateProperty;
+ getFastPropertyValue(aAggregateProperty, PROPERTY_ID_DATASOURCE);
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, aAggregateProperty, cppu::UnoType<OUString>::get());
+ }
+ break;
+ case PROPERTY_ID_TARGET_URL:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aTargetURL);
+ break;
+ case PROPERTY_ID_TARGET_FRAME:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aTargetFrame);
+ break;
+ case PROPERTY_ID_SUBMIT_METHOD:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_eSubmitMethod);
+ break;
+ case PROPERTY_ID_SUBMIT_ENCODING:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_eSubmitEncoding);
+ break;
+ case PROPERTY_ID_NAME:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_sName);
+ break;
+ case PROPERTY_ID_MASTERFIELDS:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aMasterFields);
+ break;
+ case PROPERTY_ID_DETAILFIELDS:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aDetailFields);
+ break;
+ case PROPERTY_ID_CYCLE:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aCycle, cppu::UnoType<TabulatorCycle>::get());
+ break;
+ case PROPERTY_ID_NAVIGATION:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_eNavigation);
+ break;
+ case PROPERTY_ID_ALLOWADDITIONS:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bAllowInsert);
+ break;
+ case PROPERTY_ID_ALLOWEDITS:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bAllowUpdate);
+ break;
+ case PROPERTY_ID_ALLOWDELETIONS:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bAllowDelete);
+ break;
+ case PROPERTY_ID_DYNAMIC_CONTROL_BORDER:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aDynamicControlBorder, cppu::UnoType<bool>::get() );
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aControlBorderColorFocus, cppu::UnoType<sal_Int32>::get() );
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aControlBorderColorMouse, cppu::UnoType<sal_Int32>::get() );
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aControlBorderColorInvalid, cppu::UnoType<sal_Int32>::get() );
+ break;
+ default:
+ if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle ( nHandle ) )
+ bModified = m_aPropertyBagHelper.convertDynamicFastPropertyValue( nHandle, rValue, rConvertedValue, rOldValue );
+ else
+ bModified = OPropertySetAggregationHelper::convertFastPropertyValue( rConvertedValue, rOldValue, nHandle, rValue );
+ break;
+ }
+ return bModified;
+}
+
+void ODatabaseForm::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_INSERTONLY:
+ rValue >>= m_bInsertOnly;
+ if ( m_aIgnoreResult.hasValue() )
+ m_aIgnoreResult <<= m_bInsertOnly;
+ else
+ m_xAggregateSet->setPropertyValue( PROPERTY_INSERTONLY, Any( m_bInsertOnly ) );
+ break;
+
+ case PROPERTY_ID_FILTER:
+ {
+ OUString sNewFilter;
+ rValue >>= sNewFilter;
+ m_aFilterManager.setFilterComponent( FilterManager::FilterComponent::PublicFilter, sNewFilter );
+ }
+ break;
+
+ case PROPERTY_ID_HAVINGCLAUSE:
+ {
+ OUString sNewFilter;
+ rValue >>= sNewFilter;
+ m_aFilterManager.setFilterComponent( FilterManager::FilterComponent::PublicHaving, sNewFilter );
+ }
+ break;
+
+ case PROPERTY_ID_APPLYFILTER:
+ {
+ bool bApply = true;
+ rValue >>= bApply;
+ m_aFilterManager.setApplyPublicFilter( bApply );
+ }
+ break;
+
+ case PROPERTY_ID_DATASOURCE:
+ {
+ Reference< XConnection > xSomeConnection;
+ if ( ::dbtools::isEmbeddedInDatabase( getParent(), xSomeConnection ) )
+ throw PropertyVetoException();
+
+ try
+ {
+ m_xAggregateSet->setPropertyValue(PROPERTY_DATASOURCE, rValue);
+ }
+ catch(const Exception&)
+ {
+ }
+ }
+ break;
+ case PROPERTY_ID_TARGET_URL:
+ rValue >>= m_aTargetURL;
+ break;
+ case PROPERTY_ID_TARGET_FRAME:
+ rValue >>= m_aTargetFrame;
+ break;
+ case PROPERTY_ID_SUBMIT_METHOD:
+ rValue >>= m_eSubmitMethod;
+ break;
+ case PROPERTY_ID_SUBMIT_ENCODING:
+ rValue >>= m_eSubmitEncoding;
+ break;
+ case PROPERTY_ID_NAME:
+ rValue >>= m_sName;
+ break;
+ case PROPERTY_ID_MASTERFIELDS:
+ rValue >>= m_aMasterFields;
+ invalidateParameters();
+ break;
+ case PROPERTY_ID_DETAILFIELDS:
+ rValue >>= m_aDetailFields;
+ invalidateParameters();
+ break;
+ case PROPERTY_ID_CYCLE:
+ m_aCycle = rValue;
+ break;
+ case PROPERTY_ID_NAVIGATION:
+ rValue >>= m_eNavigation;
+ break;
+ case PROPERTY_ID_ALLOWADDITIONS:
+ m_bAllowInsert = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_ALLOWEDITS:
+ m_bAllowUpdate = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_ALLOWDELETIONS:
+ m_bAllowDelete = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_DYNAMIC_CONTROL_BORDER:
+ m_aDynamicControlBorder = rValue;
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS:
+ m_aControlBorderColorFocus = rValue;
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE:
+ m_aControlBorderColorMouse = rValue;
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID:
+ m_aControlBorderColorInvalid = rValue;
+ break;
+
+ case PROPERTY_ID_ACTIVE_CONNECTION:
+ {
+ Reference< XConnection > xOuterConnection;
+ if ( ::dbtools::isEmbeddedInDatabase( getParent(), xOuterConnection ) )
+ {
+ if ( xOuterConnection != Reference< XConnection >( rValue, UNO_QUERY ) )
+ // somebody's trying to set a connection which is not equal the connection
+ // implied by the database we're embedded in
+ throw PropertyVetoException();
+ }
+ OPropertySetAggregationHelper::setFastPropertyValue_NoBroadcast( nHandle, rValue );
+ break;
+ }
+
+ default:
+ if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( nHandle ) )
+ m_aPropertyBagHelper.setDynamicFastPropertyValue( nHandle, rValue );
+ else
+ OPropertySetAggregationHelper::setFastPropertyValue_NoBroadcast( nHandle, rValue );
+ break;
+ }
+}
+
+
+void ODatabaseForm::forwardingPropertyValue( sal_Int32 _nHandle )
+{
+ OSL_ENSURE( _nHandle == PROPERTY_ID_ACTIVE_CONNECTION, "ODatabaseForm::forwardingPropertyValue: unexpected property!" );
+ if ( _nHandle == PROPERTY_ID_ACTIVE_CONNECTION )
+ {
+ if ( m_bSharingConnection )
+ stopSharingConnection( );
+ m_bForwardingConnection = true;
+ }
+}
+
+
+void ODatabaseForm::forwardedPropertyValue( sal_Int32 _nHandle )
+{
+ OSL_ENSURE( _nHandle == PROPERTY_ID_ACTIVE_CONNECTION, "ODatabaseForm::forwardedPropertyValue: unexpected property!" );
+ if ( _nHandle == PROPERTY_ID_ACTIVE_CONNECTION )
+ {
+ m_bForwardingConnection = false;
+ }
+}
+
+
+// css::beans::XPropertyState
+
+PropertyState ODatabaseForm::getPropertyStateByHandle(sal_Int32 nHandle)
+{
+ PropertyState eState;
+ switch (nHandle)
+ {
+ case PROPERTY_ID_NAVIGATION:
+ return (NavigationBarMode_CURRENT == m_eNavigation) ? PropertyState_DEFAULT_VALUE : PropertyState_DIRECT_VALUE;
+
+ case PROPERTY_ID_CYCLE:
+ eState = m_aCycle.hasValue() ? PropertyState_DIRECT_VALUE : PropertyState_DEFAULT_VALUE;
+ break;
+
+ case PROPERTY_ID_INSERTONLY:
+ eState = m_bInsertOnly ? PropertyState_DIRECT_VALUE : PropertyState_DEFAULT_VALUE;
+ break;
+
+ case PROPERTY_ID_FILTER:
+ if ( m_aFilterManager.getFilterComponent( FilterManager::FilterComponent::PublicFilter ).isEmpty() )
+ eState = PropertyState_DEFAULT_VALUE;
+ else
+ eState = PropertyState_DIRECT_VALUE;
+ break;
+
+ case PROPERTY_ID_HAVINGCLAUSE:
+ if ( m_aFilterManager.getFilterComponent( FilterManager::FilterComponent::PublicHaving ).isEmpty() )
+ eState = PropertyState_DEFAULT_VALUE;
+ else
+ eState = PropertyState_DIRECT_VALUE;
+ break;
+
+ case PROPERTY_ID_APPLYFILTER:
+ eState = m_aFilterManager.isApplyPublicFilter() ? PropertyState_DEFAULT_VALUE : PropertyState_DIRECT_VALUE;
+ break;
+
+ case PROPERTY_ID_DYNAMIC_CONTROL_BORDER:
+ eState = m_aDynamicControlBorder.hasValue() ? PropertyState_DIRECT_VALUE : PropertyState_DEFAULT_VALUE;
+ break;
+
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS:
+ eState = m_aControlBorderColorFocus.hasValue() ? PropertyState_DIRECT_VALUE : PropertyState_DEFAULT_VALUE;
+ break;
+
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE:
+ eState = m_aControlBorderColorMouse.hasValue() ? PropertyState_DIRECT_VALUE : PropertyState_DEFAULT_VALUE;
+ break;
+
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID:
+ eState = m_aControlBorderColorInvalid.hasValue() ? PropertyState_DIRECT_VALUE : PropertyState_DEFAULT_VALUE;
+ break;
+
+ default:
+ eState = OPropertySetAggregationHelper::getPropertyStateByHandle(nHandle);
+ }
+ return eState;
+}
+
+
+void ODatabaseForm::setPropertyToDefaultByHandle(sal_Int32 nHandle)
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_INSERTONLY:
+ case PROPERTY_ID_FILTER:
+ case PROPERTY_ID_HAVINGCLAUSE:
+ case PROPERTY_ID_APPLYFILTER:
+ case PROPERTY_ID_NAVIGATION:
+ case PROPERTY_ID_CYCLE:
+ case PROPERTY_ID_DYNAMIC_CONTROL_BORDER:
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS:
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE:
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID:
+ setFastPropertyValue( nHandle, getPropertyDefaultByHandle( nHandle ) );
+ break;
+
+ default:
+ OPropertySetAggregationHelper::setPropertyToDefaultByHandle(nHandle);
+ }
+}
+
+
+Any ODatabaseForm::getPropertyDefaultByHandle( sal_Int32 nHandle ) const
+{
+ Any aReturn;
+ switch (nHandle)
+ {
+ case PROPERTY_ID_INSERTONLY:
+ case PROPERTY_ID_DYNAMIC_CONTROL_BORDER:
+ aReturn <<= false;
+ break;
+
+ case PROPERTY_ID_FILTER:
+ aReturn <<= OUString();
+ break;
+
+ case PROPERTY_ID_HAVINGCLAUSE:
+ aReturn <<= OUString();
+ break;
+
+ case PROPERTY_ID_APPLYFILTER:
+ aReturn <<= true;
+ break;
+
+ case PROPERTY_ID_NAVIGATION:
+ aReturn <<= NavigationBarMode_CURRENT;
+ break;
+
+ case PROPERTY_ID_CYCLE:
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS:
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE:
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID:
+ break;
+
+ default:
+ if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( nHandle ) )
+ m_aPropertyBagHelper.getDynamicPropertyDefaultByHandle( nHandle, aReturn );
+ else
+ aReturn = OPropertySetAggregationHelper::getPropertyDefaultByHandle( nHandle );
+ break;
+ }
+ return aReturn;
+}
+
+
+// css::form::XReset
+
+void SAL_CALL ODatabaseForm::reset()
+{
+ osl::ClearableMutexGuard aGuard(m_aMutex);
+
+ if (isLoaded())
+ {
+ ::osl::MutexGuard aResetGuard(m_aResetSafety);
+ ++m_nResetsPending;
+ reset_impl(true);
+ return;
+ }
+
+ if ( m_aResetListeners.getLength() )
+ {
+ ::osl::MutexGuard aResetGuard(m_aResetSafety);
+ ++m_nResetsPending;
+ // create an own thread if we have (approve-)reset-listeners (so the listeners can't do that much damage
+ // to this thread which is probably the main one)
+ if (!m_pThread.is())
+ {
+ m_pThread = new OFormSubmitResetThread(this);
+ m_pThread->create();
+ }
+ m_pThread->addEvent(std::make_unique<EventObject>());
+ }
+ else
+ {
+ // direct call without any approving by the listeners
+ aGuard.clear();
+
+ ::osl::MutexGuard aResetGuard(m_aResetSafety);
+ ++m_nResetsPending;
+ reset_impl(false);
+ }
+}
+
+
+void ODatabaseForm::reset_impl(bool _bApproveByListeners)
+{
+ if ( _bApproveByListeners )
+ {
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aResetListeners);
+ EventObject aEvent(*this);
+ while (aIter.hasMoreElements())
+ if (!aIter.next()->approveReset(aEvent))
+ return;
+ }
+
+ ::osl::ResettableMutexGuard aResetGuard(m_aResetSafety);
+ // do we have a database connected form and stay on the insert row
+ bool bInsertRow = false;
+ if (m_xAggregateSet.is())
+ bInsertRow = getBOOL(m_xAggregateSet->getPropertyValue(PROPERTY_ISNEW));
+ if (bInsertRow)
+ {
+ try
+ {
+ // Iterate through all columns and set the default value
+ Reference< XColumnsSupplier > xColsSuppl( m_xAggregateSet, UNO_QUERY );
+ Reference< XIndexAccess > xIndexCols( xColsSuppl->getColumns(), UNO_QUERY );
+ for (sal_Int32 i = 0; i < xIndexCols->getCount(); ++i)
+ {
+ Reference< XPropertySet > xColProps;
+ xIndexCols->getByIndex(i) >>= xColProps;
+
+ Reference< XColumnUpdate > xColUpdate( xColProps, UNO_QUERY );
+ if ( !xColUpdate.is() )
+ continue;
+
+ Reference< XPropertySetInfo > xPSI;
+ if ( xColProps.is() )
+ xPSI = xColProps->getPropertySetInfo( );
+
+ static constexpr OUStringLiteral PROPERTY_CONTROLDEFAULT = u"ControlDefault";
+ if ( xPSI.is() && xPSI->hasPropertyByName( PROPERTY_CONTROLDEFAULT ) )
+ {
+ Any aDefault = xColProps->getPropertyValue( PROPERTY_CONTROLDEFAULT );
+
+ bool bReadOnly = false;
+ if ( xPSI->hasPropertyByName( PROPERTY_ISREADONLY ) )
+ xColProps->getPropertyValue( PROPERTY_ISREADONLY ) >>= bReadOnly;
+
+ if ( !bReadOnly )
+ {
+ try
+ {
+ if ( aDefault.hasValue() )
+ xColUpdate->updateObject( aDefault );
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+ }
+ }
+ }
+ catch(const Exception&)
+ {
+ }
+
+ if (m_bSubForm)
+ {
+ Reference< XColumnsSupplier > xParentColSupp( m_xParent, UNO_QUERY );
+ Reference< XNameAccess > xParentCols;
+ if ( xParentColSupp.is() )
+ xParentCols = xParentColSupp->getColumns();
+
+ if ( xParentCols.is() && xParentCols->hasElements() && m_aMasterFields.hasElements() )
+ {
+ try
+ {
+ // analyze our parameters
+ if ( !m_aParameterManager.isUpToDate() )
+ updateParameterInfo();
+
+ m_aParameterManager.resetParameterValues( );
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("ODatabaseForm::reset_impl: could not initialize the master-detail-driven parameters!");
+ }
+ }
+ }
+ }
+
+ aResetGuard.clear();
+ // iterate through all components. don't use an XIndexAccess as this will cause massive
+ // problems with the count.
+ Reference<XEnumeration> xIter = createEnumeration();
+ while (xIter->hasMoreElements())
+ {
+ Reference<XReset> xReset;
+ xIter->nextElement() >>= xReset;
+ if (xReset.is())
+ {
+ // TODO: all reset-methods have to be thread-safe
+ xReset->reset();
+ }
+ }
+
+ aResetGuard.reset();
+ // ensure that the row isn't modified
+ // (do this _before_ the listeners are notified ! their reaction (maybe asynchronous) may depend
+ // on the modified state of the row)
+ if (bInsertRow)
+ m_xAggregateSet->setPropertyValue(PROPERTY_ISMODIFIED, css::uno::Any(false));
+
+ aResetGuard.clear();
+ {
+ css::lang::EventObject aEvent( *this );
+ m_aResetListeners.notifyEach(&css::form::XResetListener::resetted, aEvent);
+ }
+
+ aResetGuard.reset();
+ // and again : ensure the row isn't modified
+ // we already did this after we (and maybe our dependents) reset the values, but the listeners may have changed the row, too
+ if (bInsertRow)
+ m_xAggregateSet->setPropertyValue(PROPERTY_ISMODIFIED, css::uno::Any(false));
+
+ --m_nResetsPending;
+}
+
+
+void SAL_CALL ODatabaseForm::addResetListener(const Reference<XResetListener>& _rListener)
+{
+ m_aResetListeners.addInterface( _rListener );
+}
+
+
+void SAL_CALL ODatabaseForm::removeResetListener(const Reference<XResetListener>& _rListener)
+{
+ m_aResetListeners.removeInterface( _rListener );
+}
+
+
+// css::form::XSubmit
+
+void SAL_CALL ODatabaseForm::submit( const Reference<XControl>& Control,
+ const css::awt::MouseEvent& MouseEvt )
+{
+ {
+ ::osl::MutexGuard aGuard(m_aMutex);
+ // Do we have controls and a Submit URL?
+ if( !getCount() || m_aTargetURL.isEmpty() )
+ return;
+ }
+
+ ::osl::ClearableMutexGuard aGuard(m_aMutex);
+ if (m_aSubmitListeners.getLength())
+ {
+ // create an own thread if we have (approve-)submit-listeners (so the listeners can't do that much damage
+ // to this thread which is probably the main one)
+ if (!m_pThread.is())
+ {
+ m_pThread = new OFormSubmitResetThread(this);
+ m_pThread->create();
+ }
+ m_pThread->addEvent(std::make_unique<css::awt::MouseEvent>(MouseEvt), Control, true);
+ }
+ else
+ {
+ // direct call without any approving by the listeners
+ aGuard.clear();
+ submit_impl( Control, MouseEvt );
+ }
+}
+
+static void lcl_dispatch(const Reference< XFrame >& xFrame,const Reference<XURLTransformer>& xTransformer,const OUString& aURLStr,const OUString& aReferer,const OUString& aTargetName
+ ,std::u16string_view aData,rtl_TextEncoding _eEncoding)
+{
+ URL aURL;
+ aURL.Complete = aURLStr;
+ xTransformer->parseStrict(aURL);
+
+ Reference< XDispatch > xDisp = Reference< XDispatchProvider > (xFrame,UNO_QUERY_THROW)->queryDispatch(aURL, aTargetName,
+ FrameSearchFlag::SELF | FrameSearchFlag::PARENT | FrameSearchFlag::CHILDREN |
+ FrameSearchFlag::SIBLINGS | FrameSearchFlag::CREATE | FrameSearchFlag::TASKS);
+
+ if (!xDisp.is())
+ return;
+
+
+ // build a sequence from the to-be-submitted string
+ OString a8BitData(OUStringToOString(aData, _eEncoding));
+ // always ANSI #58641
+ Sequence< sal_Int8 > aPostData(reinterpret_cast<const sal_Int8*>(a8BitData.getStr()), a8BitData.getLength());
+ Reference< XInputStream > xPostData = new SequenceInputStream(aPostData);
+
+ Sequence<PropertyValue> aArgs
+ {
+ comphelper::makePropertyValue("Referer", aReferer),
+ comphelper::makePropertyValue("PostData", xPostData)
+ };
+
+ xDisp->dispatch(aURL, aArgs);
+}
+
+void ODatabaseForm::submit_impl(const Reference<XControl>& Control, const css::awt::MouseEvent& MouseEvt)
+{
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aSubmitListeners);
+ EventObject aEvt(static_cast<XWeak*>(this));
+ bool bCanceled = false;
+ while (aIter.hasMoreElements() && !bCanceled)
+ {
+ if (!aIter.next()->approveSubmit(aEvt))
+ bCanceled = true;
+ }
+
+ if (bCanceled)
+ return;
+
+ FormSubmitEncoding eSubmitEncoding;
+ FormSubmitMethod eSubmitMethod;
+ OUString aURLStr;
+ OUString aReferer;
+ OUString aTargetName;
+ Reference< XModel > xModel;
+ {
+ SolarMutexGuard aGuard;
+ // starform->Forms
+
+ Reference<XChild> xParent(m_xParent, UNO_QUERY);
+
+ if (xParent.is())
+ xModel = getXModel(xParent->getParent());
+
+ if (xModel.is())
+ aReferer = xModel->getURL();
+
+ // TargetItem
+ aTargetName = m_aTargetFrame;
+
+ eSubmitEncoding = m_eSubmitEncoding;
+ eSubmitMethod = m_eSubmitMethod;
+ aURLStr = m_aTargetURL;
+ }
+
+ if (!xModel.is())
+ return;
+ Reference< XFrame > xFrame = xModel->getCurrentController()->getFrame();
+ if (!xFrame.is())
+ return;
+
+ Reference<XURLTransformer> xTransformer(URLTransformer::create(m_xContext));
+
+ // URL encoding
+ if( eSubmitEncoding == FormSubmitEncoding_URL )
+ {
+ OUString aData;
+ {
+ SolarMutexGuard aGuard;
+ aData = GetDataEncoded(true, Control, MouseEvt);
+ }
+
+ URL aURL;
+ // FormMethod GET
+ if( eSubmitMethod == FormSubmitMethod_GET )
+ {
+ INetURLObject aUrlObj( aURLStr, INetURLObject::EncodeMechanism::WasEncoded );
+ aUrlObj.SetParam( aData, INetURLObject::EncodeMechanism::All );
+ aURL.Complete = aUrlObj.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
+ if (xTransformer.is())
+ xTransformer->parseStrict(aURL);
+
+ Reference< XDispatch > xDisp = Reference< XDispatchProvider > (xFrame,UNO_QUERY_THROW)->queryDispatch(aURL, aTargetName,
+ FrameSearchFlag::SELF | FrameSearchFlag::PARENT | FrameSearchFlag::CHILDREN |
+ FrameSearchFlag::SIBLINGS | FrameSearchFlag::CREATE | FrameSearchFlag::TASKS);
+
+ if (xDisp.is())
+ {
+ Sequence<PropertyValue> aArgs { comphelper::makePropertyValue("Referer", aReferer) };
+ xDisp->dispatch(aURL, aArgs);
+ }
+ }
+ // FormMethod POST
+ else if( eSubmitMethod == FormSubmitMethod_POST )
+ {
+ lcl_dispatch(xFrame,xTransformer,aURLStr,aReferer,aTargetName,aData,RTL_TEXTENCODING_MS_1252);
+ }
+ }
+ else if( eSubmitEncoding == FormSubmitEncoding_MULTIPART )
+ {
+ URL aURL;
+ aURL.Complete = aURLStr;
+ xTransformer->parseStrict(aURL);
+
+ Reference< XDispatch > xDisp = Reference< XDispatchProvider > (xFrame,UNO_QUERY_THROW)->queryDispatch(aURL, aTargetName,
+ FrameSearchFlag::SELF | FrameSearchFlag::PARENT | FrameSearchFlag::CHILDREN |
+ FrameSearchFlag::SIBLINGS | FrameSearchFlag::CREATE | FrameSearchFlag::TASKS);
+
+ if (xDisp.is())
+ {
+ OUString aContentType;
+ Sequence<sal_Int8> aData;
+ {
+ SolarMutexGuard aGuard;
+ aData = GetDataMultiPartEncoded(Control, MouseEvt, aContentType);
+ }
+ if (!aData.hasElements())
+ return;
+
+ // build a sequence from the to-be-submitted string
+ Reference< XInputStream > xPostData = new SequenceInputStream(aData);
+
+ Sequence<PropertyValue> aArgs
+ {
+ comphelper::makePropertyValue("Referer", aReferer),
+ comphelper::makePropertyValue("ContentType", aContentType),
+ comphelper::makePropertyValue("PostData", xPostData)
+ };
+
+ xDisp->dispatch(aURL, aArgs);
+ }
+ }
+ else if( eSubmitEncoding == FormSubmitEncoding_TEXT )
+ {
+ OUString aData;
+ {
+ SolarMutexGuard aGuard;
+ aData = GetDataEncoded(false, Reference<XControl> (), MouseEvt);
+ }
+
+ lcl_dispatch(xFrame,xTransformer,aURLStr,aReferer,aTargetName,aData,osl_getThreadTextEncoding());
+ }
+ else {
+ OSL_FAIL("ODatabaseForm::submit_Impl : wrong encoding !");
+ }
+
+}
+
+// XSubmit
+
+void SAL_CALL ODatabaseForm::addSubmitListener(const Reference<XSubmitListener>& _rListener)
+{
+ m_aSubmitListeners.addInterface(_rListener);
+}
+
+
+void SAL_CALL ODatabaseForm::removeSubmitListener(const Reference<XSubmitListener>& _rListener)
+{
+ m_aSubmitListeners.removeInterface(_rListener);
+}
+
+
+// css::sdbc::XSQLErrorBroadcaster
+
+void SAL_CALL ODatabaseForm::addSQLErrorListener(const Reference<XSQLErrorListener>& _rListener)
+{
+ m_aErrorListeners.addInterface(_rListener);
+}
+
+
+void SAL_CALL ODatabaseForm::removeSQLErrorListener(const Reference<XSQLErrorListener>& _rListener)
+{
+ m_aErrorListeners.removeInterface(_rListener);
+}
+
+
+void ODatabaseForm::invalidateParameters()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ m_aParameterManager.clearAllParameterInformation();
+}
+
+
+// OChangeListener
+
+void ODatabaseForm::_propertyChanged(const PropertyChangeEvent& evt)
+{
+ if (evt.PropertyName == PROPERTY_ACTIVE_CONNECTION && !m_bForwardingConnection)
+ {
+ // the rowset changed its active connection itself (without interaction from our side), so
+ // we need to fire this event, too
+ sal_Int32 nHandle = PROPERTY_ID_ACTIVE_CONNECTION;
+ fire(&nHandle, &evt.NewValue, &evt.OldValue, 1);
+ }
+ else // it was one of the statement relevant props
+ {
+ // if the statement has changed we have to delete the parameter info
+ invalidateParameters();
+ }
+}
+
+
+// smartXChild
+
+void SAL_CALL ODatabaseForm::setParent(const css::uno::Reference<css::uno::XInterface>& Parent)
+{
+ // SYNCHRONIZED ----->
+ osl::ClearableMutexGuard aGuard(m_aMutex);
+
+ Reference<XForm> xParentForm(getParent(), UNO_QUERY);
+ if (xParentForm.is())
+ {
+ try
+ {
+ Reference< XRowSetApproveBroadcaster > xParentApprBroadcast( xParentForm, UNO_QUERY_THROW );
+ xParentApprBroadcast->removeRowSetApproveListener( this );
+
+ Reference< XLoadable > xParentLoadable( xParentForm, UNO_QUERY_THROW );
+ xParentLoadable->removeLoadListener( this );
+
+ Reference< XPropertySet > xParentProperties( xParentForm, UNO_QUERY_THROW );
+ xParentProperties->removePropertyChangeListener( PROPERTY_ISNEW, this );
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+
+ OFormComponents::setParent(Parent);
+
+ xParentForm.set(getParent(), UNO_QUERY);
+ if ( xParentForm.is() )
+ {
+ try
+ {
+ Reference< XRowSetApproveBroadcaster > xParentApprBroadcast( xParentForm, UNO_QUERY_THROW );
+ xParentApprBroadcast->addRowSetApproveListener( this );
+
+ Reference< XLoadable > xParentLoadable( xParentForm, UNO_QUERY_THROW );
+ xParentLoadable->addLoadListener( this );
+
+ Reference< XPropertySet > xParentProperties( xParentForm, UNO_QUERY_THROW );
+ xParentProperties->addPropertyChangeListener( PROPERTY_ISNEW, this );
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+
+ Reference< XPropertySet > xAggregateProperties( m_xAggregateSet );
+ aGuard.clear();
+ // <----- SYNCHRONIZED
+
+ Reference< XConnection > xOuterConnection;
+ bool bIsEmbedded = ::dbtools::isEmbeddedInDatabase( Parent, xOuterConnection );
+
+ if ( bIsEmbedded )
+ xAggregateProperties->setPropertyValue( PROPERTY_DATASOURCE, Any( OUString() ) );
+}
+
+
+// smartXTabControllerModel
+
+sal_Bool SAL_CALL ODatabaseForm::getGroupControl()
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ // Should controls be combined into a TabOrder group?
+ if (m_aCycle.hasValue())
+ {
+ sal_Int32 nCycle = 0;
+ ::cppu::enum2int(nCycle, m_aCycle);
+ return static_cast<TabulatorCycle>(nCycle) != TabulatorCycle_PAGE;
+ }
+
+ if (isLoaded() && getConnection().is())
+ return true;
+
+ return false;
+}
+
+
+void SAL_CALL ODatabaseForm::setControlModels(const Sequence<Reference<XControlModel> >& rControls)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ // Set TabIndex in the order of the sequence
+ sal_Int32 nCount = getCount();
+
+ // HiddenControls and forms are not listed
+ if (rControls.getLength() > nCount)
+ return;
+
+ sal_Int16 nTabIndex = 1;
+ for (auto const& rControl : rControls)
+ {
+ Reference<XFormComponent> xComp(rControl, UNO_QUERY);
+ if (xComp.is())
+ {
+ // Find component in the list
+ for (sal_Int32 j = 0; j < nCount; ++j)
+ {
+ Reference<XFormComponent> xElement(
+ getByIndex(j), css::uno::UNO_QUERY);
+ if (xComp == xElement)
+ {
+ Reference<XPropertySet> xSet(xComp, UNO_QUERY);
+ if (xSet.is() && hasProperty(PROPERTY_TABINDEX, xSet))
+ xSet->setPropertyValue( PROPERTY_TABINDEX, Any(nTabIndex++) );
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+Sequence<Reference<XControlModel> > SAL_CALL ODatabaseForm::getControlModels()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ return m_pGroupManager->getControlModels();
+}
+
+
+void SAL_CALL ODatabaseForm::setGroup( const Sequence<Reference<XControlModel> >& _rGroup, const OUString& Name )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ // The controls are grouped by adjusting their names to the name of the
+ // first control of the sequence
+ Reference< XPropertySet > xSet;
+ OUString sGroupName( Name );
+
+ for( auto const& rControl : _rGroup )
+ {
+ xSet.set(rControl, css::uno::UNO_QUERY);
+ if ( !xSet.is() )
+ {
+ // can't throw an exception other than a RuntimeException (which would not be appropriate),
+ // so we ignore (and only assert) this
+ OSL_FAIL( "ODatabaseForm::setGroup: invalid arguments!" );
+ continue;
+ }
+
+ if (sGroupName.isEmpty())
+ xSet->getPropertyValue(PROPERTY_NAME) >>= sGroupName;
+ else
+ xSet->setPropertyValue(PROPERTY_NAME, Any(sGroupName));
+ }
+}
+
+
+sal_Int32 SAL_CALL ODatabaseForm::getGroupCount()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ return m_pGroupManager->getGroupCount();
+}
+
+
+void SAL_CALL ODatabaseForm::getGroup( sal_Int32 nGroup, Sequence<Reference<XControlModel> >& _rGroup, OUString& _rName )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ _rGroup.realloc(0);
+ _rName.clear();
+
+ if ((nGroup < 0) || (nGroup >= m_pGroupManager->getGroupCount()))
+ return;
+ m_pGroupManager->getGroup( nGroup, _rGroup, _rName );
+}
+
+
+void SAL_CALL ODatabaseForm::getGroupByName(const OUString& Name, Sequence< Reference<XControlModel> >& _rGroup)
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ _rGroup.realloc(0);
+ m_pGroupManager->getGroupByName( Name, _rGroup );
+}
+
+
+// css::lang::XEventListener
+
+void SAL_CALL ODatabaseForm::disposing(const EventObject& Source)
+{
+ // does the call come from the connection which we are sharing with our parent?
+ if ( isSharingConnection() )
+ {
+ Reference< XConnection > xConnSource( Source.Source, UNO_QUERY );
+ if ( xConnSource.is() )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ Reference< XConnection > xActiveConn;
+ m_xAggregateSet->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xActiveConn;
+ OSL_ENSURE( xActiveConn.get() == xConnSource.get(), "ODatabaseForm::disposing: where did this come from?" );
+ // there should be exactly one XConnection object we're listening at - our aggregate connection
+#endif
+ disposingSharedConnection( xConnSource );
+ }
+ }
+
+ OInterfaceContainer::disposing(Source);
+
+ // does the disposing come from the aggregate ?
+ if (m_xAggregate.is())
+ { // no -> forward it
+ css::uno::Reference<css::lang::XEventListener> xListener;
+ if (query_aggregation(m_xAggregate, xListener))
+ xListener->disposing(Source);
+ }
+}
+
+
+void ODatabaseForm::impl_createLoadTimer()
+{
+ OSL_PRECOND( m_pLoadTimer == nullptr, "ODatabaseForm::impl_createLoadTimer: timer already exists!" );
+ m_pLoadTimer.reset(new Timer("DatabaseFormLoadTimer"));
+ m_pLoadTimer->SetTimeout(100);
+ m_pLoadTimer->SetInvokeHandler(LINK(this,ODatabaseForm,OnTimeout));
+}
+
+
+// css::form::XLoadListener
+
+void SAL_CALL ODatabaseForm::loaded(const EventObject& /*aEvent*/)
+{
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ Reference< XRowSet > xParentRowSet( m_xParent, UNO_QUERY_THROW );
+ xParentRowSet->addRowSetListener( this );
+
+ impl_createLoadTimer();
+ }
+
+ load_impl( true );
+}
+
+
+void SAL_CALL ODatabaseForm::unloading(const EventObject& /*aEvent*/)
+{
+ {
+ // now stop the rowset listening if we are a subform
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_pLoadTimer && m_pLoadTimer->IsActive() )
+ m_pLoadTimer->Stop();
+ m_pLoadTimer.reset();
+
+ Reference< XRowSet > xParentRowSet( m_xParent, UNO_QUERY_THROW );
+ xParentRowSet->removeRowSetListener( this );
+ }
+
+ unload();
+}
+
+
+void SAL_CALL ODatabaseForm::unloaded(const EventObject& /*aEvent*/)
+{
+ // nothing to do
+}
+
+
+void SAL_CALL ODatabaseForm::reloading(const EventObject& /*aEvent*/)
+{
+ // now stop the rowset listening if we are a subform
+ ::osl::MutexGuard aGuard(m_aMutex);
+ Reference<XRowSet> xParentRowSet(m_xParent, UNO_QUERY);
+ if (xParentRowSet.is())
+ xParentRowSet->removeRowSetListener(this);
+
+ if (m_pLoadTimer && m_pLoadTimer->IsActive())
+ m_pLoadTimer->Stop();
+}
+
+
+void SAL_CALL ODatabaseForm::reloaded(const EventObject& /*aEvent*/)
+{
+ reload_impl(true);
+ {
+ ::osl::MutexGuard aGuard(m_aMutex);
+ Reference<XRowSet> xParentRowSet(m_xParent, UNO_QUERY);
+ if (xParentRowSet.is())
+ xParentRowSet->addRowSetListener(this);
+ }
+}
+
+
+IMPL_LINK_NOARG(ODatabaseForm, OnTimeout, Timer *, void)
+{
+ reload_impl(true);
+}
+
+
+// css::form::XLoadable
+
+void SAL_CALL ODatabaseForm::load()
+{
+ load_impl(false);
+}
+
+
+bool ODatabaseForm::canShareConnection( const Reference< XPropertySet >& _rxParentProps )
+{
+ // our own data source
+ OUString sOwnDatasource;
+ m_xAggregateSet->getPropertyValue( PROPERTY_DATASOURCE ) >>= sOwnDatasource;
+
+ // our parents data source
+ OUString sParentDataSource;
+ OSL_ENSURE( _rxParentProps.is() && _rxParentProps->getPropertySetInfo().is() && _rxParentProps->getPropertySetInfo()->hasPropertyByName( PROPERTY_DATASOURCE ),
+ "ODatabaseForm::doShareConnection: invalid parent form!" );
+ if ( _rxParentProps.is() )
+ _rxParentProps->getPropertyValue( PROPERTY_DATASOURCE ) >>= sParentDataSource;
+
+ bool bCanShareConnection = false;
+
+ // both rowsets share are connected to the same data source
+ if ( sParentDataSource == sOwnDatasource )
+ {
+ if ( !sParentDataSource.isEmpty() )
+ // and it's really a data source name (not empty)
+ bCanShareConnection = true;
+ else
+ { // the data source name is empty
+ // -> ok for the URL
+ OUString sParentURL;
+ OUString sMyURL;
+ _rxParentProps->getPropertyValue( PROPERTY_URL ) >>= sParentURL;
+ m_xAggregateSet->getPropertyValue( PROPERTY_URL ) >>= sMyURL;
+
+ bCanShareConnection = (sParentURL == sMyURL);
+ }
+ }
+
+ if ( bCanShareConnection )
+ {
+ // check for the user/password
+
+ // take the user property on the rowset (if any) into account
+ OUString sParentUser, sParentPwd;
+ _rxParentProps->getPropertyValue( PROPERTY_USER ) >>= sParentUser;
+ _rxParentProps->getPropertyValue( PROPERTY_PASSWORD ) >>= sParentPwd;
+
+ OUString sMyUser, sMyPwd;
+ m_xAggregateSet->getPropertyValue( PROPERTY_USER ) >>= sMyUser;
+ m_xAggregateSet->getPropertyValue( PROPERTY_PASSWORD ) >>= sMyPwd;
+
+ bCanShareConnection =
+ ( sParentUser == sMyUser )
+ && ( sParentPwd == sMyPwd );
+ }
+
+ return bCanShareConnection;
+}
+
+
+void ODatabaseForm::doShareConnection( const Reference< XPropertySet >& _rxParentProps )
+{
+ // get the connection of the parent
+ Reference< XConnection > xParentConn;
+ _rxParentProps->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xParentConn;
+ OSL_ENSURE( xParentConn.is(), "ODatabaseForm::doShareConnection: we're a valid sub-form, but the parent has no connection?!" );
+
+ if ( xParentConn.is() )
+ {
+ // add as dispose listener to the connection
+ Reference< XComponent > xParentConnComp( xParentConn, UNO_QUERY );
+ OSL_ENSURE( xParentConnComp.is(), "ODatabaseForm::doShareConnection: invalid connection!" );
+ xParentConnComp->addEventListener( static_cast< XLoadListener* >( this ) );
+
+ // forward the connection to our own aggregate
+ m_bForwardingConnection = true;
+ m_xAggregateSet->setPropertyValue( PROPERTY_ACTIVE_CONNECTION, Any( xParentConn ) );
+ m_bForwardingConnection = false;
+
+ m_bSharingConnection = true;
+ }
+ else
+ m_bSharingConnection = false;
+}
+
+
+void ODatabaseForm::disposingSharedConnection( const Reference< XConnection >& /*_rxConn*/ )
+{
+ stopSharingConnection();
+
+ // TODO: we could think about whether or not to re-connect.
+ unload( );
+}
+
+
+void ODatabaseForm::stopSharingConnection( )
+{
+ OSL_ENSURE( m_bSharingConnection, "ODatabaseForm::stopSharingConnection: invalid call!" );
+
+ if ( !m_bSharingConnection )
+ return;
+
+ // get the connection
+ Reference< XConnection > xSharedConn;
+ m_xAggregateSet->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xSharedConn;
+ OSL_ENSURE( xSharedConn.is(), "ODatabaseForm::stopSharingConnection: there's no conn!" );
+
+ // remove ourself as event listener
+ Reference< XComponent > xSharedConnComp( xSharedConn, UNO_QUERY );
+ if ( xSharedConnComp.is() )
+ xSharedConnComp->removeEventListener( static_cast< XLoadListener* >( this ) );
+
+ // no need to dispose the conn: we're not the owner, this is our parent
+ // (in addition, this method may be called if the connection is being disposed while we use it)
+
+ // reset the property
+ xSharedConn.clear();
+ m_bForwardingConnection = true;
+ m_xAggregateSet->setPropertyValue( PROPERTY_ACTIVE_CONNECTION, Any( xSharedConn ) );
+ m_bForwardingConnection = false;
+
+ // reset the flag
+ m_bSharingConnection = false;
+}
+
+namespace
+{
+ Reference<css::awt::XWindow> GetDialogParentWindow(const Reference<XModel>& rModel)
+ {
+ if (!rModel.is())
+ return nullptr;
+ Reference<XController> xController(rModel->getCurrentController());
+ if (!xController.is())
+ return nullptr;
+ Reference<XFrame> xFrame(xController->getFrame());
+ if (!xFrame.is())
+ return nullptr;
+ return xFrame->getContainerWindow();
+ }
+}
+
+bool ODatabaseForm::implEnsureConnection()
+{
+ try
+ {
+ if ( getConnection( ).is() )
+ // if our aggregate already has a connection, nothing needs to be done about it
+ return true;
+
+ // see whether we're an embedded form
+ Reference< XConnection > xOuterConnection;
+ if ( ::dbtools::isEmbeddedInDatabase( getParent(), xOuterConnection ) )
+ {
+ m_xAggregateSet->setPropertyValue( PROPERTY_ACTIVE_CONNECTION, Any( xOuterConnection ) );
+ return xOuterConnection.is();
+ }
+
+ m_bSharingConnection = false;
+
+ // if we're a sub form, we try to re-use the connection of our parent
+ if (m_bSubForm)
+ {
+ OSL_ENSURE( Reference< XForm >( getParent(), UNO_QUERY ).is(),
+ "ODatabaseForm::implEnsureConnection: m_bSubForm is TRUE, but the parent is no form?" );
+
+ Reference< XPropertySet > xParentProps( getParent(), UNO_QUERY );
+
+ // can we re-use (aka share) the connection of the parent?
+ if ( canShareConnection( xParentProps ) )
+ {
+ // yep -> do it
+ doShareConnection( xParentProps );
+ // success?
+ if ( m_bSharingConnection )
+ // yes -> outta here
+ return true;
+ }
+ }
+
+ if (m_xAggregateSet.is())
+ {
+ //Dig out a suitable parent for any warning dialogs
+ Reference<css::awt::XWindow> m_xDialogParent;
+ Reference<XChild> xParent(m_xParent, UNO_QUERY);
+ if (xParent.is())
+ m_xDialogParent = GetDialogParentWindow(getXModel(xParent->getParent()));
+
+ Reference< XConnection > xConnection = connectRowset(
+ Reference<XRowSet> (m_xAggregate, UNO_QUERY),
+ m_xContext, m_xDialogParent
+ );
+ return xConnection.is();
+ }
+ }
+ catch(const SQLException& eDB)
+ {
+ onError(eDB, ResourceManager::loadString(RID_STR_CONNECTERROR));
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ return false;
+}
+
+
+void ODatabaseForm::load_impl(bool bCausedByParentForm, bool bMoveToFirst, const Reference< XInteractionHandler >& _rxCompletionHandler )
+{
+ ::osl::ResettableMutexGuard aGuard(m_aMutex);
+
+ // are we already loaded?
+ if (isLoaded())
+ return;
+
+ m_bSubForm = bCausedByParentForm;
+
+ // if we don't have a connection, we are not intended to be a database form or the aggregate was not able
+ // to establish a connection
+ bool bConnected = implEnsureConnection();
+
+ // we don't have to execute if we do not have a command to execute
+ bool bExecute = bConnected && m_xAggregateSet.is() && !getString(m_xAggregateSet->getPropertyValue(PROPERTY_COMMAND)).isEmpty();
+
+ // a database form always uses caching
+ // we use starting fetchsize with at least 10 rows
+ if (bConnected)
+ m_xAggregateSet->setPropertyValue(PROPERTY_FETCHSIZE, Any(sal_Int32(40)));
+
+ // if we're loaded as sub form we got a "rowSetChanged" from the parent rowset _before_ we got the "loaded"
+ // so we don't need to execute the statement again, this was already done
+ // (and there were no relevant changes between these two listener calls, the "load" of a form is quite an
+ // atomic operation.)
+
+ bool bSuccess = false;
+ if (bExecute)
+ {
+ m_sCurrentErrorContext = ResourceManager::loadString(RID_ERR_LOADING_FORM);
+ bSuccess = executeRowSet(aGuard, bMoveToFirst, _rxCompletionHandler);
+ }
+
+ if (bSuccess)
+ {
+ m_bLoaded = true;
+ aGuard.clear();
+ EventObject aEvt(static_cast<XWeak*>(this));
+ m_aLoadListeners.notifyEach( &XLoadListener::loaded, aEvt );
+
+ // if we are on the insert row, we have to reset all controls
+ // to set the default values
+ if (bExecute && getBOOL(m_xAggregateSet->getPropertyValue(PROPERTY_ISNEW)))
+ reset();
+ }
+}
+
+
+void SAL_CALL ODatabaseForm::unload()
+{
+ ::osl::ResettableMutexGuard aGuard(m_aMutex);
+ if (!isLoaded())
+ return;
+
+ m_pLoadTimer.reset();
+
+ aGuard.clear();
+ EventObject aEvt(static_cast<XWeak*>(this));
+ m_aLoadListeners.notifyEach( &XLoadListener::unloading, aEvt );
+
+ if (m_xAggregateAsRowSet.is())
+ {
+ // we may have reset the InsertOnly property on the aggregate - restore it
+ restoreInsertOnlyState( );
+
+ // clear the parameters if there are any
+ invalidateParameters();
+
+ try
+ {
+ // close the aggregate
+ Reference<XCloseable> xCloseable;
+ query_aggregation( m_xAggregate, xCloseable);
+ if (xCloseable.is())
+ xCloseable->close();
+ }
+ catch(const SQLException&)
+ {
+ }
+ }
+
+ aGuard.reset();
+ m_bLoaded = false;
+
+ // if the connection we used while we were loaded is only shared with our parent, we
+ // reset it
+ if ( isSharingConnection() )
+ stopSharingConnection();
+
+ aGuard.clear();
+ m_aLoadListeners.notifyEach( &XLoadListener::unloaded, aEvt );
+}
+
+
+void SAL_CALL ODatabaseForm::reload()
+{
+ reload_impl(true);
+}
+
+
+void ODatabaseForm::reload_impl(bool bMoveToFirst, const Reference< XInteractionHandler >& _rxCompletionHandler )
+{
+ ::osl::ResettableMutexGuard aGuard(m_aMutex);
+ if (!isLoaded())
+ return;
+
+ DocumentModifyGuard aModifyGuard( *this );
+ // ensures the document is not marked as "modified" just because we change some control's content during
+ // reloading...
+
+ EventObject aEvent(static_cast<XWeak*>(this));
+ {
+ // only if there is no approve listener we can post the event at this time
+ // otherwise see approveRowsetChange
+ // the approval is done by the aggregate
+ if (!m_aRowSetApproveListeners.getLength())
+ {
+ aGuard.clear();
+ m_aLoadListeners.notifyEach( &XLoadListener::reloading, aEvent);
+ aGuard.reset();
+ }
+ }
+
+ bool bSuccess = true;
+ try
+ {
+ m_sCurrentErrorContext = ResourceManager::loadString(RID_ERR_REFRESHING_FORM);
+ bSuccess = executeRowSet(aGuard, bMoveToFirst, _rxCompletionHandler);
+ }
+ catch(const SQLException&)
+ {
+ TOOLS_WARN_EXCEPTION( "forms.component", "ODatabaseForm::reload_impl : shouldn't executeRowSet catch this exception?");
+ }
+
+ if (bSuccess)
+ {
+ aGuard.clear();
+ m_aLoadListeners.notifyEach( &XLoadListener::reloaded, aEvent);
+
+ // if we are on the insert row, we have to reset all controls
+ // to set the default values
+ if (getBOOL(m_xAggregateSet->getPropertyValue(PROPERTY_ISNEW)))
+ reset();
+ }
+ else
+ m_bLoaded = false;
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::isLoaded()
+{
+ return m_bLoaded;
+}
+
+
+void SAL_CALL ODatabaseForm::addLoadListener(const Reference<XLoadListener>& aListener)
+{
+ m_aLoadListeners.addInterface(aListener);
+}
+
+
+void SAL_CALL ODatabaseForm::removeLoadListener(const Reference<XLoadListener>& aListener)
+{
+ m_aLoadListeners.removeInterface(aListener);
+}
+
+
+// css::sdbc::XCloseable
+void SAL_CALL ODatabaseForm::close()
+{
+ // unload will close the aggregate
+ unload();
+}
+
+
+// css::sdbc::XRowSetListener
+
+void SAL_CALL ODatabaseForm::cursorMoved(const EventObject& /*event*/)
+{
+ // reload the subform with the new parameters of the parent
+ // do this handling delayed to provide of execute too many SQL Statements
+ osl::MutexGuard aGuard(m_aMutex);
+
+ DBG_ASSERT( m_pLoadTimer, "ODatabaseForm::cursorMoved: how can this happen?!" );
+ if ( !m_pLoadTimer )
+ impl_createLoadTimer();
+
+ if ( m_pLoadTimer->IsActive() )
+ m_pLoadTimer->Stop();
+
+ // and start the timer again
+ m_pLoadTimer->Start();
+}
+
+
+void SAL_CALL ODatabaseForm::rowChanged(const EventObject& /*event*/)
+{
+ // ignore it
+}
+
+
+void SAL_CALL ODatabaseForm::rowSetChanged(const EventObject& /*event*/)
+{
+ // not interested in :
+ // if our parent is an ODatabaseForm, too, then after this rowSetChanged we'll get a "reloaded"
+ // or a "loaded" event.
+ // If somebody gave us another parent which is an XRowSet but doesn't handle an execute as
+ // "load" respectively "reload"... can't do anything...
+}
+
+
+bool ODatabaseForm::impl_approveRowChange_throw( const EventObject& _rEvent, const bool _bAllowSQLException,
+ ::osl::ClearableMutexGuard& _rGuard )
+{
+ ::comphelper::OInterfaceIteratorHelper3 aIter( m_aRowSetApproveListeners );
+ _rGuard.clear();
+ while ( aIter.hasMoreElements() )
+ {
+ Reference< XRowSetApproveListener > xListener( aIter.next() );
+ try
+ {
+ if ( !xListener->approveRowSetChange( _rEvent ) )
+ return false;
+ }
+ catch (const DisposedException& e)
+ {
+ if ( e.Context == xListener )
+ aIter.remove();
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const SQLException&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ if ( _bAllowSQLException )
+ throw;
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+ return true;
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::approveCursorMove(const EventObject& event)
+{
+ // is our aggregate calling?
+ if (event.Source == css::uno::Reference<css::uno::XInterface>(static_cast<XWeak*>(this)))
+ {
+ // Our aggregate doesn't have any ApproveRowSetListeners (expect ourself), as we re-routed the queryInterface
+ // for XRowSetApproveBroadcaster-interface.
+ // So we have to multiplex this approve request.
+ ::comphelper::OInterfaceIteratorHelper3 aIter( m_aRowSetApproveListeners );
+ while ( aIter.hasMoreElements() )
+ {
+ Reference< XRowSetApproveListener > xListener( aIter.next() );
+ try
+ {
+ if ( !xListener->approveCursorMove( event ) )
+ return false;
+ }
+ catch (const DisposedException& e)
+ {
+ if ( e.Context == xListener )
+ aIter.remove();
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+ return true;
+ }
+ else
+ {
+ // this is a call from our parent ...
+ // a parent's cursor move will result in a re-execute of our own row-set, so we have to
+ // ask our own RowSetChangesListeners, too
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ if ( !impl_approveRowChange_throw( event, false, aGuard ) )
+ return false;
+ }
+ return true;
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::approveRowChange(const RowChangeEvent& event)
+{
+ // is our aggregate calling?
+ if (event.Source != css::uno::Reference<css::uno::XInterface>(static_cast<XWeak*>(this)))
+ return true;
+
+ // Our aggregate doesn't have any ApproveRowSetListeners (expect ourself), as we re-routed the queryInterface
+ // for XRowSetApproveBroadcaster-interface.
+ // So we have to multiplex this approve request.
+ ::comphelper::OInterfaceIteratorHelper3 aIter( m_aRowSetApproveListeners );
+ while ( aIter.hasMoreElements() )
+ {
+ Reference< XRowSetApproveListener > xListener( aIter.next() );
+ try
+ {
+ if ( !xListener->approveRowChange( event ) )
+ return false;
+ }
+ catch (const DisposedException& e)
+ {
+ if ( e.Context == xListener )
+ aIter.remove();
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+ return true;
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::approveRowSetChange(const EventObject& event)
+{
+ if (event.Source == css::uno::Reference<css::uno::XInterface>(static_cast<XWeak*>(this))) // ignore our aggregate as we handle this approve ourself
+ {
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ bool bWasLoaded = isLoaded();
+ if ( !impl_approveRowChange_throw( event, false, aGuard ) )
+ return false;
+
+ if ( bWasLoaded )
+ {
+ m_aLoadListeners.notifyEach( &XLoadListener::reloading, event );
+ }
+ }
+ else
+ {
+ // this is a call from our parent ...
+ // a parent's cursor move will result in a re-execute of our own row-set, so we have to
+ // ask our own RowSetChangesListeners, too
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ if ( !impl_approveRowChange_throw( event, false, aGuard ) )
+ return false;
+ }
+ return true;
+}
+
+
+// css::sdb::XRowSetApproveBroadcaster
+
+void SAL_CALL ODatabaseForm::addRowSetApproveListener(const Reference<XRowSetApproveListener>& _rListener)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+ m_aRowSetApproveListeners.addInterface(_rListener);
+
+ // do we have to multiplex ?
+ if (m_aRowSetApproveListeners.getLength() == 1)
+ {
+ Reference<XRowSetApproveBroadcaster> xBroadcaster;
+ if (query_aggregation( m_xAggregate, xBroadcaster))
+ {
+ Reference<XRowSetApproveListener> xListener(static_cast<XRowSetApproveListener*>(this));
+ xBroadcaster->addRowSetApproveListener(xListener);
+ }
+ }
+}
+
+
+void SAL_CALL ODatabaseForm::removeRowSetApproveListener(const Reference<XRowSetApproveListener>& _rListener)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+ // do we have to remove the multiplex ?
+ m_aRowSetApproveListeners.removeInterface(_rListener);
+ if ( m_aRowSetApproveListeners.getLength() == 0 )
+ {
+ Reference<XRowSetApproveBroadcaster> xBroadcaster;
+ if (query_aggregation( m_xAggregate, xBroadcaster))
+ {
+ Reference<XRowSetApproveListener> xListener(static_cast<XRowSetApproveListener*>(this));
+ xBroadcaster->removeRowSetApproveListener(xListener);
+ }
+ }
+}
+
+
+// com::sun:star::form::XDatabaseParameterBroadcaster
+
+void SAL_CALL ODatabaseForm::addDatabaseParameterListener(const Reference<XDatabaseParameterListener>& _rListener)
+{
+ m_aParameterManager.addParameterListener( _rListener );
+}
+
+void SAL_CALL ODatabaseForm::removeDatabaseParameterListener(const Reference<XDatabaseParameterListener>& _rListener)
+{
+ m_aParameterManager.removeParameterListener( _rListener );
+}
+
+
+void SAL_CALL ODatabaseForm::addParameterListener(const Reference<XDatabaseParameterListener>& _rListener)
+{
+ ODatabaseForm::addDatabaseParameterListener( _rListener );
+}
+
+
+void SAL_CALL ODatabaseForm::removeParameterListener(const Reference<XDatabaseParameterListener>& _rListener)
+{
+ ODatabaseForm::removeDatabaseParameterListener( _rListener );
+}
+
+
+// css::sdb::XCompletedExecution
+
+void SAL_CALL ODatabaseForm::executeWithCompletion( const Reference< XInteractionHandler >& _rxHandler )
+{
+ ::osl::ClearableMutexGuard aGuard(m_aMutex);
+ // the difference between execute and load is, that we position on the first row in case of load
+ // after execute we remain before the first row
+ if (!isLoaded())
+ {
+ aGuard.clear();
+ load_impl(false, false, _rxHandler);
+ }
+ else
+ {
+ EventObject event(static_cast< XWeak* >(this));
+ if ( !impl_approveRowChange_throw( event, true, aGuard ) )
+ return;
+
+ // we're loaded and somebody wants to execute ourself -> this means a reload
+ reload_impl(false, _rxHandler);
+ }
+}
+
+
+// css::sdbc::XRowSet
+
+void SAL_CALL ODatabaseForm::execute()
+{
+ osl::ClearableMutexGuard aGuard(m_aMutex);
+ // if somebody calls an execute and we're not loaded we reroute this call to our load method.
+
+ // the difference between execute and load is, that we position on the first row in case of load
+ // after execute we remain before the first row
+ if (!isLoaded())
+ {
+ aGuard.clear();
+ load_impl(false, false);
+ }
+ else
+ {
+ EventObject event(static_cast< XWeak* >(this));
+ if ( !impl_approveRowChange_throw( event, true, aGuard ) )
+ return;
+
+ // we're loaded and somebody wants to execute ourself -> this means a reload
+ reload_impl(false);
+ }
+}
+
+
+void SAL_CALL ODatabaseForm::addRowSetListener(const Reference<XRowSetListener>& _rListener)
+{
+ if (m_xAggregateAsRowSet.is())
+ m_xAggregateAsRowSet->addRowSetListener(_rListener);
+}
+
+
+void SAL_CALL ODatabaseForm::removeRowSetListener(const Reference<XRowSetListener>& _rListener)
+{
+ if (m_xAggregateAsRowSet.is())
+ m_xAggregateAsRowSet->removeRowSetListener(_rListener);
+}
+
+
+// css::sdbc::XResultSet
+
+sal_Bool SAL_CALL ODatabaseForm::next()
+{
+ return m_xAggregateAsRowSet->next();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::isBeforeFirst()
+{
+ return m_xAggregateAsRowSet->isBeforeFirst();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::isAfterLast()
+{
+ return m_xAggregateAsRowSet->isAfterLast();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::isFirst()
+{
+ return m_xAggregateAsRowSet->isFirst();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::isLast()
+{
+ return m_xAggregateAsRowSet->isLast();
+}
+
+
+void SAL_CALL ODatabaseForm::beforeFirst()
+{
+ m_xAggregateAsRowSet->beforeFirst();
+}
+
+
+void SAL_CALL ODatabaseForm::afterLast()
+{
+ m_xAggregateAsRowSet->afterLast();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::first()
+{
+ return m_xAggregateAsRowSet->first();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::last()
+{
+ return m_xAggregateAsRowSet->last();
+}
+
+
+sal_Int32 SAL_CALL ODatabaseForm::getRow()
+{
+ return m_xAggregateAsRowSet->getRow();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::absolute(sal_Int32 row)
+{
+ return m_xAggregateAsRowSet->absolute(row);
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::relative(sal_Int32 rows)
+{
+ return m_xAggregateAsRowSet->relative(rows);
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::previous()
+{
+ return m_xAggregateAsRowSet->previous();
+}
+
+
+void SAL_CALL ODatabaseForm::refreshRow()
+{
+ m_xAggregateAsRowSet->refreshRow();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::rowUpdated()
+{
+ return m_xAggregateAsRowSet->rowUpdated();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::rowInserted()
+{
+ return m_xAggregateAsRowSet->rowInserted();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::rowDeleted()
+{
+ return m_xAggregateAsRowSet->rowDeleted();
+}
+
+
+css::uno::Reference<css::uno::XInterface> SAL_CALL ODatabaseForm::getStatement()
+{
+ return m_xAggregateAsRowSet->getStatement();
+}
+
+// css::sdbc::XResultSetUpdate
+// exceptions during insert update and delete will be forwarded to the errorlistener
+
+void SAL_CALL ODatabaseForm::insertRow()
+{
+ try
+ {
+ Reference<XResultSetUpdate> xUpdate;
+ if (query_aggregation( m_xAggregate, xUpdate))
+ xUpdate->insertRow();
+ }
+ catch(const RowSetVetoException&)
+ {
+ throw;
+ }
+ catch(const SQLException& eDb)
+ {
+ onError(eDb, ResourceManager::loadString(RID_STR_ERR_INSERTRECORD));
+ throw;
+ }
+}
+
+
+void SAL_CALL ODatabaseForm::updateRow()
+{
+ try
+ {
+ Reference<XResultSetUpdate> xUpdate;
+ if (query_aggregation( m_xAggregate, xUpdate))
+ xUpdate->updateRow();
+ }
+ catch(const RowSetVetoException&)
+ {
+ throw;
+ }
+ catch(const SQLException& eDb)
+ {
+ onError(eDb, ResourceManager::loadString(RID_STR_ERR_UPDATERECORD));
+ throw;
+ }
+}
+
+
+void SAL_CALL ODatabaseForm::deleteRow()
+{
+ try
+ {
+ Reference<XResultSetUpdate> xUpdate;
+ if (query_aggregation( m_xAggregate, xUpdate))
+ xUpdate->deleteRow();
+ }
+ catch(const RowSetVetoException&)
+ {
+ throw;
+ }
+ catch(const SQLException& eDb)
+ {
+ onError(eDb, ResourceManager::loadString(RID_STR_ERR_DELETERECORD));
+ throw;
+ }
+}
+
+
+void SAL_CALL ODatabaseForm::cancelRowUpdates()
+{
+ try
+ {
+ Reference<XResultSetUpdate> xUpdate;
+ if (query_aggregation( m_xAggregate, xUpdate))
+ xUpdate->cancelRowUpdates();
+ }
+ catch(const RowSetVetoException&)
+ {
+ throw;
+ }
+ catch(const SQLException& eDb)
+ {
+ onError(eDb, ResourceManager::loadString(RID_STR_ERR_INSERTRECORD));
+ throw;
+ }
+}
+
+
+void SAL_CALL ODatabaseForm::moveToInsertRow()
+{
+ Reference<XResultSetUpdate> xUpdate;
+ if (!query_aggregation( m_xAggregate, xUpdate))
+ return;
+
+ // _always_ move to the insert row
+ //
+ // Formerly, the following line was conditioned with a "not is new", means we did not move the aggregate
+ // to the insert row if it was already positioned there.
+ //
+ // This prevented the RowSet implementation from resetting its column values. We, ourself, formerly
+ // did this reset of columns in reset_impl, where we set every column to the ControlDefault, or, if this
+ // was not present, to NULL. However, the problem with setting to NULL was #88888#, the problem with
+ // _not_ setting to NULL (which was the original fix for #88888#) was #97955#.
+ //
+ // So now we
+ // * move our aggregate to the insert row
+ // * in reset_impl
+ // - set the control defaults into the columns if not void
+ // - do _not_ set the columns to NULL if no control default is set
+ //
+ // Still, there is #72756#. During fixing this bug, DG introduced not calling the aggregate here. So
+ // in theory, we re-introduced #72756#. But the bug described therein does not happen anymore, as the
+ // preliminaries for it changed (no display of guessed values for new records with autoinc fields)
+ //
+ // BTW: the public Issuezilla bug is #i2815#
+ //
+ xUpdate->moveToInsertRow();
+
+ // then set the default values and the parameters given from the parent
+ reset();
+}
+
+
+void SAL_CALL ODatabaseForm::moveToCurrentRow()
+{
+ Reference<XResultSetUpdate> xUpdate;
+ if (query_aggregation( m_xAggregate, xUpdate))
+ xUpdate->moveToCurrentRow();
+}
+
+// css::sdbcx::XDeleteRows
+
+Sequence<sal_Int32> SAL_CALL ODatabaseForm::deleteRows(const Sequence<Any>& rows)
+{
+ try
+ {
+ Reference<XDeleteRows> xDelete;
+ if (query_aggregation( m_xAggregate, xDelete))
+ return xDelete->deleteRows(rows);
+ }
+ catch(const RowSetVetoException&)
+ {
+ throw;
+ }
+ catch(const SQLException& eDb)
+ {
+ onError(eDb, ResourceManager::loadString(RID_STR_ERR_DELETERECORDS));
+ throw;
+ }
+
+ return Sequence< sal_Int32 >();
+}
+
+// css::sdbc::XParameters
+
+void SAL_CALL ODatabaseForm::setNull(sal_Int32 parameterIndex, sal_Int32 sqlType)
+{
+ m_aParameterManager.setNull(parameterIndex, sqlType);
+}
+
+
+void SAL_CALL ODatabaseForm::setObjectNull(sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& typeName)
+{
+ m_aParameterManager.setObjectNull(parameterIndex, sqlType, typeName);
+}
+
+
+void SAL_CALL ODatabaseForm::setBoolean(sal_Int32 parameterIndex, sal_Bool x)
+{
+ m_aParameterManager.setBoolean(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setByte(sal_Int32 parameterIndex, sal_Int8 x)
+{
+ m_aParameterManager.setByte(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setShort(sal_Int32 parameterIndex, sal_Int16 x)
+{
+ m_aParameterManager.setShort(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setInt(sal_Int32 parameterIndex, sal_Int32 x)
+{
+ m_aParameterManager.setInt(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setLong(sal_Int32 parameterIndex, sal_Int64 x)
+{
+ m_aParameterManager.setLong(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setFloat(sal_Int32 parameterIndex, float x)
+{
+ m_aParameterManager.setFloat(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setDouble(sal_Int32 parameterIndex, double x)
+{
+ m_aParameterManager.setDouble(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setString(sal_Int32 parameterIndex, const OUString& x)
+{
+ m_aParameterManager.setString(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setBytes(sal_Int32 parameterIndex, const Sequence< sal_Int8 >& x)
+{
+ m_aParameterManager.setBytes(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setDate(sal_Int32 parameterIndex, const css::util::Date& x)
+{
+ m_aParameterManager.setDate(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setTime(sal_Int32 parameterIndex, const css::util::Time& x)
+{
+ m_aParameterManager.setTime(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setTimestamp(sal_Int32 parameterIndex, const css::util::DateTime& x)
+{
+ m_aParameterManager.setTimestamp(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setBinaryStream(sal_Int32 parameterIndex, const Reference<XInputStream>& x, sal_Int32 length)
+{
+ m_aParameterManager.setBinaryStream(parameterIndex, x, length);
+}
+
+
+void SAL_CALL ODatabaseForm::setCharacterStream(sal_Int32 parameterIndex, const Reference<XInputStream>& x, sal_Int32 length)
+{
+ m_aParameterManager.setCharacterStream(parameterIndex, x, length);
+}
+
+
+void SAL_CALL ODatabaseForm::setObjectWithInfo(sal_Int32 parameterIndex, const Any& x, sal_Int32 targetSqlType, sal_Int32 scale)
+{
+ m_aParameterManager.setObjectWithInfo(parameterIndex, x, targetSqlType, scale);
+}
+
+
+void SAL_CALL ODatabaseForm::setObject(sal_Int32 parameterIndex, const Any& x)
+{
+ m_aParameterManager.setObject(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setRef(sal_Int32 parameterIndex, const Reference<XRef>& x)
+{
+ m_aParameterManager.setRef(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setBlob(sal_Int32 parameterIndex, const Reference<XBlob>& x)
+{
+ m_aParameterManager.setBlob(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setClob(sal_Int32 parameterIndex, const Reference<XClob>& x)
+{
+ m_aParameterManager.setClob(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setArray(sal_Int32 parameterIndex, const Reference<XArray>& x)
+{
+ m_aParameterManager.setArray(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::clearParameters()
+{
+ m_aParameterManager.clearParameters();
+}
+
+
+void SAL_CALL ODatabaseForm::propertyChange( const PropertyChangeEvent& evt )
+{
+ if ( evt.Source == m_xParent )
+ {
+ if ( evt.PropertyName == PROPERTY_ISNEW )
+ {
+ bool bCurrentIsNew( false );
+ OSL_VERIFY( evt.NewValue >>= bCurrentIsNew );
+ if ( !bCurrentIsNew )
+ reload_impl( true );
+ }
+ return;
+ }
+ OFormComponents::propertyChange( evt );
+}
+
+// css::lang::XServiceInfo
+
+OUString SAL_CALL ODatabaseForm::getImplementationName()
+{
+ return "com.sun.star.comp.forms.ODatabaseForm";
+}
+
+
+Sequence< OUString > SAL_CALL ODatabaseForm::getSupportedServiceNames()
+{
+ // the services of our aggregate
+ Sequence< OUString > aServices;
+ Reference< XServiceInfo > xInfo;
+ if (query_aggregation(m_xAggregate, xInfo))
+ aServices = xInfo->getSupportedServiceNames();
+
+ // concat without own services
+ return ::comphelper::concatSequences(
+ css::uno::Sequence<OUString> {
+ FRM_SUN_FORMCOMPONENT, "com.sun.star.form.FormComponents",
+ FRM_SUN_COMPONENT_FORM, FRM_SUN_COMPONENT_HTMLFORM,
+ FRM_SUN_COMPONENT_DATAFORM, FRM_COMPONENT_FORM },
+ aServices
+ );
+}
+
+sal_Bool SAL_CALL ODatabaseForm::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+// css::io::XPersistObject
+const sal_uInt16 CYCLE = 0x0001;
+const sal_uInt16 DONTAPPLYFILTER = 0x0002;
+
+OUString ODatabaseForm::getServiceName()
+{
+ return FRM_COMPONENT_FORM; // old (non-sun) name for compatibility !
+}
+
+void SAL_CALL ODatabaseForm::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ DBG_ASSERT(m_xAggregateSet.is(), "ODatabaseForm::write : only to be called if the aggregate exists !");
+
+ // all children
+ OFormComponents::write(_rxOutStream);
+
+ // version
+ _rxOutStream->writeShort(0x0005);
+
+ // Name
+ _rxOutStream << m_sName;
+
+ OUString sDataSource;
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->getPropertyValue(PROPERTY_DATASOURCE) >>= sDataSource;
+ _rxOutStream << sDataSource;
+
+ // former CursorSource
+ OUString sCommand;
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->getPropertyValue(PROPERTY_COMMAND) >>= sCommand;
+ _rxOutStream << sCommand;
+
+ // former MasterFields
+ _rxOutStream << m_aMasterFields;
+ // former DetailFields
+ _rxOutStream << m_aDetailFields;
+
+ // former DataSelectionType
+ DataSelectionType eTranslated = DataSelectionType_TABLE;
+ if (m_xAggregateSet.is())
+ {
+ sal_Int32 nCommandType = 0;
+ m_xAggregateSet->getPropertyValue(PROPERTY_COMMANDTYPE) >>= nCommandType;
+ switch (nCommandType)
+ {
+ case CommandType::TABLE : eTranslated = DataSelectionType_TABLE; break;
+ case CommandType::QUERY : eTranslated = DataSelectionType_QUERY; break;
+ case CommandType::COMMAND:
+ {
+ bool bEscapeProcessing = getBOOL(m_xAggregateSet->getPropertyValue(PROPERTY_ESCAPE_PROCESSING));
+ eTranslated = bEscapeProcessing ? DataSelectionType_SQL : DataSelectionType_SQLPASSTHROUGH;
+ }
+ break;
+ default : OSL_FAIL("ODatabaseForm::write : wrong CommandType !");
+ }
+ }
+ _rxOutStream->writeShort(static_cast<sal_Int16>(eTranslated)); // former DataSelectionType
+
+ // very old versions expect a CursorType here
+ _rxOutStream->writeShort(2); // DatabaseCursorType_KEYSET
+
+ _rxOutStream->writeBoolean(m_eNavigation != NavigationBarMode_NONE);
+
+ // former DataEntry
+ if (m_xAggregateSet.is())
+ _rxOutStream->writeBoolean(getBOOL(m_xAggregateSet->getPropertyValue(PROPERTY_INSERTONLY)));
+ else
+ _rxOutStream->writeBoolean(false);
+
+ _rxOutStream->writeBoolean(m_bAllowInsert);
+ _rxOutStream->writeBoolean(m_bAllowUpdate);
+ _rxOutStream->writeBoolean(m_bAllowDelete);
+
+ // html form stuff
+ OUString sTmp = INetURLObject::decode( m_aTargetURL, INetURLObject::DecodeMechanism::Unambiguous);
+ _rxOutStream << sTmp;
+ _rxOutStream->writeShort( static_cast<sal_Int16>(m_eSubmitMethod) );
+ _rxOutStream->writeShort( static_cast<sal_Int16>(m_eSubmitEncoding) );
+ _rxOutStream << m_aTargetFrame;
+
+ // version 2 didn't know some options and the "default" state
+ sal_Int32 nCycle = sal_Int32(TabulatorCycle_RECORDS);
+ if (m_aCycle.hasValue())
+ {
+ ::cppu::enum2int(nCycle, m_aCycle);
+ if (m_aCycle == TabulatorCycle_PAGE)
+ // unknown in earlier versions
+ nCycle = sal_Int32(TabulatorCycle_RECORDS);
+ }
+ _rxOutStream->writeShort(static_cast<sal_Int16>(nCycle));
+
+ _rxOutStream->writeShort(static_cast<sal_Int16>(m_eNavigation));
+
+ OUString sFilter;
+ OUString sSort;
+ if (m_xAggregateSet.is())
+ {
+ m_xAggregateSet->getPropertyValue(PROPERTY_FILTER) >>= sFilter;
+ // version 4
+ m_xAggregateSet->getPropertyValue(PROPERTY_SORT) >>= sSort;
+ }
+ _rxOutStream << sFilter;
+ _rxOutStream << sSort;
+
+ // version 3
+ sal_uInt16 nAnyMask = 0;
+ if (m_aCycle.hasValue())
+ nAnyMask |= CYCLE;
+
+ if (m_xAggregateSet.is() && !getBOOL(m_xAggregateSet->getPropertyValue(PROPERTY_APPLYFILTER)))
+ nAnyMask |= DONTAPPLYFILTER;
+
+ _rxOutStream->writeShort(nAnyMask);
+
+ if (nAnyMask & CYCLE)
+ {
+ sal_Int32 nRealCycle = 0;
+ ::cppu::enum2int(nRealCycle, m_aCycle);
+ _rxOutStream->writeShort(static_cast<sal_Int16>(nRealCycle));
+ }
+
+ // version 5
+ OUString sHaving;
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->getPropertyValue(PROPERTY_HAVINGCLAUSE) >>= sHaving;
+ _rxOutStream << sHaving;
+}
+
+
+void SAL_CALL ODatabaseForm::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ DBG_ASSERT(m_xAggregateSet.is(), "ODatabaseForm::read : only to be called if the aggregate exists !");
+
+ OFormComponents::read(_rxInStream);
+
+ // version
+ sal_uInt16 nVersion = _rxInStream->readShort();
+
+ _rxInStream >> m_sName;
+
+ OUString sAggregateProp;
+ _rxInStream >> sAggregateProp;
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_DATASOURCE, Any(sAggregateProp));
+ _rxInStream >> sAggregateProp;
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_COMMAND, Any(sAggregateProp));
+
+ _rxInStream >> m_aMasterFields;
+ _rxInStream >> m_aDetailFields;
+
+ sal_Int16 nCursorSourceType = _rxInStream->readShort();
+ sal_Int32 nCommandType = 0;
+ switch (static_cast<DataSelectionType>(nCursorSourceType))
+ {
+ case DataSelectionType_TABLE : nCommandType = CommandType::TABLE; break;
+ case DataSelectionType_QUERY : nCommandType = CommandType::QUERY; break;
+ case DataSelectionType_SQL:
+ case DataSelectionType_SQLPASSTHROUGH:
+ {
+ nCommandType = CommandType::COMMAND;
+ bool bEscapeProcessing = static_cast<DataSelectionType>(nCursorSourceType) != DataSelectionType_SQLPASSTHROUGH;
+ m_xAggregateSet->setPropertyValue(PROPERTY_ESCAPE_PROCESSING, Any(bEscapeProcessing));
+ }
+ break;
+ default : OSL_FAIL("ODatabaseForm::read : wrong CommandType !");
+ }
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_COMMANDTYPE, Any(nCommandType));
+
+ // obsolete
+ _rxInStream->readShort();
+
+ // navigation mode was a boolean in version 1
+ // was a sal_Bool in version 1
+ bool bNavigation = _rxInStream->readBoolean();
+ if (nVersion == 1)
+ m_eNavigation = bNavigation ? NavigationBarMode_CURRENT : NavigationBarMode_NONE;
+
+ bool bInsertOnly = _rxInStream->readBoolean();
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_INSERTONLY, Any(bInsertOnly));
+
+ m_bAllowInsert = _rxInStream->readBoolean();
+ m_bAllowUpdate = _rxInStream->readBoolean();
+ m_bAllowDelete = _rxInStream->readBoolean();
+
+ // html stuff
+ OUString sTmp;
+ _rxInStream >> sTmp;
+ m_aTargetURL = INetURLObject::decode( sTmp, INetURLObject::DecodeMechanism::Unambiguous);
+ m_eSubmitMethod = static_cast<FormSubmitMethod>(_rxInStream->readShort());
+ m_eSubmitEncoding = static_cast<FormSubmitEncoding>(_rxInStream->readShort());
+ _rxInStream >> m_aTargetFrame;
+
+ if (nVersion > 1)
+ {
+ sal_Int32 nCycle = _rxInStream->readShort();
+ m_aCycle <<= TabulatorCycle(nCycle);
+ m_eNavigation = static_cast<NavigationBarMode>(_rxInStream->readShort());
+
+ _rxInStream >> sAggregateProp;
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_FILTER, Any(sAggregateProp));
+ if(nVersion > 3)
+ {
+ _rxInStream >> sAggregateProp;
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_SORT, Any(sAggregateProp));
+ }
+ }
+
+ sal_uInt16 nAnyMask = 0;
+ if (nVersion > 2)
+ {
+ nAnyMask = _rxInStream->readShort();
+ if (nAnyMask & CYCLE)
+ {
+ sal_Int32 nCycle = _rxInStream->readShort();
+ m_aCycle <<= TabulatorCycle(nCycle);
+ }
+ else
+ m_aCycle.clear();
+ }
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_APPLYFILTER, Any((nAnyMask & DONTAPPLYFILTER) == 0));
+
+ if(nVersion > 4)
+ {
+ _rxInStream >> sAggregateProp;
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_HAVINGCLAUSE, Any(sAggregateProp));
+ }
+}
+
+
+void ODatabaseForm::implInserted( const ElementDescription* _pElement )
+{
+ OFormComponents::implInserted( _pElement );
+
+ Reference< XSQLErrorBroadcaster > xBroadcaster( _pElement->xInterface, UNO_QUERY );
+ Reference< XForm > xForm ( _pElement->xInterface, UNO_QUERY );
+
+ if ( xBroadcaster.is() && !xForm.is() )
+ { // the object is an error broadcaster, but no form itself -> add ourself as listener
+ xBroadcaster->addSQLErrorListener( this );
+ }
+}
+
+
+void ODatabaseForm::implRemoved(const css::uno::Reference<css::uno::XInterface>& _rxObject)
+{
+ OFormComponents::implRemoved( _rxObject );
+
+ Reference<XSQLErrorBroadcaster> xBroadcaster(_rxObject, UNO_QUERY);
+ Reference<XForm> xForm(_rxObject, UNO_QUERY);
+ if (xBroadcaster.is() && !xForm.is())
+ { // the object is an error broadcaster, but no form itself -> remove ourself as listener
+ xBroadcaster->removeSQLErrorListener(this);
+ }
+}
+
+void SAL_CALL ODatabaseForm::errorOccured(const SQLErrorEvent& _rEvent)
+{
+ // give it to my own error listener
+ onError(_rEvent);
+ // TODO: think about extending the chain with an SQLContext object saying
+ // "this was an error of one of my children"
+}
+
+// css::container::XNamed
+OUString SAL_CALL ODatabaseForm::getName()
+{
+ OUString sReturn;
+ try
+ {
+ OPropertySetHelper::getFastPropertyValue(PROPERTY_ID_NAME) >>= sReturn;
+ }
+ catch (const css::beans::UnknownPropertyException&)
+ {
+ throw WrappedTargetRuntimeException(
+ "ODatabaseForm::getName",
+ *this,
+ ::cppu::getCaughtException()
+ );
+ }
+ return sReturn;
+}
+
+void SAL_CALL ODatabaseForm::setName(const OUString& aName)
+{
+ setFastPropertyValue(PROPERTY_ID_NAME, Any(aName));
+}
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_forms_ODatabaseForm_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ODatabaseForm(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/DatabaseForm.hxx b/forms/source/component/DatabaseForm.hxx
new file mode 100644
index 000000000..7d6b86e03
--- /dev/null
+++ b/forms/source/component/DatabaseForm.hxx
@@ -0,0 +1,527 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+#include <vector>
+
+#include <propertybaghelper.hxx>
+#include <com/sun/star/sdb/XSQLErrorListener.hpp>
+#include <com/sun/star/sdb/XSQLErrorBroadcaster.hpp>
+#include <com/sun/star/form/FormSubmitMethod.hpp>
+#include <com/sun/star/form/FormSubmitEncoding.hpp>
+#include <com/sun/star/form/XSubmit.hpp>
+#include <com/sun/star/form/XReset.hpp>
+#include <com/sun/star/form/XDatabaseParameterBroadcaster2.hpp>
+#include <com/sun/star/sdb/XCompletedExecution.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sdbcx/XDeleteRows.hpp>
+#include <com/sun/star/sdbc/XResultSetUpdate.hpp>
+#include <com/sun/star/sdb/XRowSetApproveListener.hpp>
+#include <com/sun/star/sdb/XRowSetApproveBroadcaster.hpp>
+#include <com/sun/star/form/NavigationBarMode.hpp>
+#include <com/sun/star/form/XLoadable.hpp>
+#include <com/sun/star/form/XLoadListener.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/awt/XTabControllerModel.hpp>
+#include <com/sun/star/sdbc/XRowSetListener.hpp>
+#include <com/sun/star/sdbc/XCloseable.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/sdbc/XParameters.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/beans/XPropertyContainer.hpp>
+#include <com/sun/star/beans/XPropertyAccess.hpp>
+#include <com/sun/star/sdbc/XWarningsSupplier.hpp>
+
+
+#include <tools/link.hxx>
+#include <InterfaceContainer.hxx>
+
+#include <connectivity/parameters.hxx>
+#include <connectivity/filtermanager.hxx>
+#include <connectivity/warningscontainer.hxx>
+
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/propmultiplex.hxx>
+#include <comphelper/uno3.hxx>
+#include <cppuhelper/implbase12.hxx>
+#include <cppuhelper/implbase4.hxx>
+#include <cppuhelper/implbase7.hxx>
+#include <rtl/ref.hxx>
+
+namespace com::sun::star::sdbc { class SQLException; }
+
+class Timer;
+class INetMIMEMessage;
+
+
+namespace frm
+{
+
+
+//= html tools
+
+
+const sal_uInt16 SUCCESSFUL_REPRESENT_TEXT = 0x0001;
+const sal_uInt16 SUCCESSFUL_REPRESENT_FILE = 0x0002;
+
+
+class HtmlSuccessfulObj
+{
+public:
+ OUString aName;
+ OUString aValue;
+ sal_uInt16 nRepresentation;
+
+ HtmlSuccessfulObj( const OUString& _rName, const OUString& _rValue,
+ sal_uInt16 _nRepresent = SUCCESSFUL_REPRESENT_TEXT )
+ :aName( _rName )
+ ,aValue( _rValue )
+ ,nRepresentation( _nRepresent )
+ {
+ }
+};
+
+typedef std::vector<HtmlSuccessfulObj> HtmlSuccessfulObjList;
+
+
+class OGroupManager;
+class OFormSubmitResetThread;
+typedef ::cppu::ImplHelper12 < css::form::XForm
+ , css::awt::XTabControllerModel
+ , css::form::XLoadListener
+ , css::sdbc::XRowSetListener
+ , css::sdb::XRowSetApproveListener
+ , css::form::XDatabaseParameterBroadcaster2
+ , css::sdb::XSQLErrorListener
+ , css::sdb::XSQLErrorBroadcaster
+ , css::form::XReset
+ , css::form::XSubmit
+ , css::form::XLoadable
+ , css::container::XNamed
+ > ODatabaseForm_BASE1;
+
+
+typedef ::cppu::ImplHelper4 < css::lang::XServiceInfo
+ , css::beans::XPropertyContainer
+ , css::beans::XPropertyAccess
+ , css::sdbc::XWarningsSupplier
+ > ODatabaseForm_BASE2;
+
+typedef ::cppu::ImplHelper7< css::sdbc::XCloseable,
+ css::sdbc::XRowSet,
+ css::sdb::XCompletedExecution,
+ css::sdb::XRowSetApproveBroadcaster,
+ css::sdbc::XResultSetUpdate,
+ css::sdbcx::XDeleteRows,
+ css::sdbc::XParameters > ODatabaseForm_BASE3;
+
+
+class ODatabaseForm :public OFormComponents
+ ,public OPropertySetAggregationHelper
+ ,public OPropertyChangeListener
+ ,public ODatabaseForm_BASE1
+ ,public ODatabaseForm_BASE2
+ ,public ODatabaseForm_BASE3
+ ,public IPropertyBagHelperContext
+{
+ friend class OFormSubmitResetThread;
+
+ // listener administration
+ ::comphelper::OInterfaceContainerHelper3<css::form::XLoadListener> m_aLoadListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::sdb::XRowSetApproveListener> m_aRowSetApproveListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::form::XSubmitListener> m_aSubmitListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::sdb::XSQLErrorListener> m_aErrorListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::form::XResetListener> m_aResetListeners;
+ ::osl::Mutex m_aResetSafety;
+ css::uno::Any m_aCycle;
+ css::uno::Any m_aIgnoreResult; // set when we are a subform and our master form positioned on a new row
+ css::uno::Sequence< OUString > m_aMasterFields;
+ css::uno::Sequence< OUString > m_aDetailFields;
+
+ // the object doin' most of the work - an SDB-rowset
+ css::uno::Reference< css::uno::XAggregation> m_xAggregate;
+ // same object, interface as member because of performance reasons
+ css::uno::Reference< css::sdbc::XRowSet> m_xAggregateAsRowSet;
+
+ PropertyBagHelper m_aPropertyBagHelper;
+ ::dbtools::WarningsContainer m_aWarnings;
+ rtl::Reference<OPropertyChangeMultiplexer> m_xAggregatePropertyMultiplexer;
+ // Management of the Control Groups
+ rtl::Reference<OGroupManager> m_pGroupManager;
+ ::dbtools::ParameterManager m_aParameterManager;
+ ::dbtools::FilterManager m_aFilterManager;
+ std::unique_ptr<Timer> m_pLoadTimer;
+
+ rtl::Reference<OFormSubmitResetThread> m_pThread;
+ OUString m_sCurrentErrorContext;
+ // will be used as additional context information
+ // when an exception is caught and forwarded to the listeners
+
+ sal_Int32 m_nResetsPending;
+// <overwritten_properties>
+ sal_Int32 m_nPrivileges;
+ bool m_bInsertOnly;
+// </overwritten_properties>
+
+// <properties>
+ css::uno::Any m_aControlBorderColorFocus;
+ css::uno::Any m_aControlBorderColorMouse;
+ css::uno::Any m_aControlBorderColorInvalid;
+ css::uno::Any m_aDynamicControlBorder;
+ OUString m_sName;
+ OUString m_aTargetURL;
+ OUString m_aTargetFrame;
+ css::form::FormSubmitMethod m_eSubmitMethod;
+ css::form::FormSubmitEncoding m_eSubmitEncoding;
+ css::form::NavigationBarMode m_eNavigation;
+ bool m_bAllowInsert : 1;
+ bool m_bAllowUpdate : 1;
+ bool m_bAllowDelete : 1;
+// </properties>
+ bool m_bLoaded : 1;
+ bool m_bSubForm : 1;
+ bool m_bForwardingConnection : 1; // true if we're setting the ActiveConnection on the aggregate
+ bool m_bSharingConnection : 1; // true if the connection we're using is shared with our parent
+
+public:
+ explicit ODatabaseForm(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+ ODatabaseForm( const ODatabaseForm& _cloneSource );
+ virtual ~ODatabaseForm() override;
+
+ // UNO binding
+ DECLARE_UNO3_AGG_DEFAULTS(ODatabaseForm, OFormComponents)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type> SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId( ) override;
+
+ // css::lang::XComponent
+ virtual void SAL_CALL disposing() override;
+
+ // property handling
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle ) const override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(css::uno::Any& rConvertedValue, css::uno::Any& rOldValue, sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const css::uno::Any& rValue) override;
+
+ css::uno::Any SAL_CALL getFastPropertyValue( sal_Int32 nHandle ) override;
+ void fire( sal_Int32 * pnHandles, const css::uno::Any * pNewValues, const css::uno::Any * pOldValues, sal_Int32 nCount );
+
+ // IPropertyBagHelperContext
+ virtual ::osl::Mutex& getMutex() override;
+ virtual void describeFixedAndAggregateProperties(
+ css::uno::Sequence< css::beans::Property >& _out_rFixedProperties,
+ css::uno::Sequence< css::beans::Property >& _out_rAggregateProperties
+ ) const override;
+ virtual css::uno::Reference< css::beans::XMultiPropertySet >
+ getPropertiesInterface() override;
+
+ // css::beans::XPropertyState
+ virtual css::beans::PropertyState getPropertyStateByHandle(sal_Int32 nHandle) override;
+ virtual void setPropertyToDefaultByHandle(sal_Int32 nHandle) override;
+ virtual css::uno::Any getPropertyDefaultByHandle(sal_Int32 nHandle) const override;
+
+ // css::sdbc::XSQLErrorBroadcaster
+ virtual void SAL_CALL addSQLErrorListener(const css::uno::Reference< css::sdb::XSQLErrorListener>& _rxListener) override;
+ virtual void SAL_CALL removeSQLErrorListener(const css::uno::Reference< css::sdb::XSQLErrorListener>& _rxListener) override;
+
+ // css::form::XForm
+ // nothing to implement
+
+ // css::form::XReset
+ virtual void SAL_CALL reset() override;
+ virtual void SAL_CALL addResetListener(const css::uno::Reference< css::form::XResetListener>& _rxListener) override;
+ virtual void SAL_CALL removeResetListener(const css::uno::Reference< css::form::XResetListener>& _rxListener) override;
+
+ // css::form::XSubmit
+ virtual void SAL_CALL submit(const css::uno::Reference< css::awt::XControl>& aControl, const css::awt::MouseEvent& aMouseEvt) override;
+ virtual void SAL_CALL addSubmitListener(const css::uno::Reference< css::form::XSubmitListener>& _rxListener) override;
+ virtual void SAL_CALL removeSubmitListener(const css::uno::Reference< css::form::XSubmitListener>& _rxListener) override;
+
+ // css::container::XChild
+ virtual css::uno::Reference<css::uno::XInterface> SAL_CALL getParent() override { return OFormComponents::getParent(); }
+ virtual void SAL_CALL setParent(const css::uno::Reference<css::uno::XInterface>& Parent) override;
+
+ // css::container::XNamed
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName(const OUString& aName) override;
+
+ // css::awt::XTabControllerModel
+ virtual sal_Bool SAL_CALL getGroupControl() override;
+ virtual void SAL_CALL setGroupControl(sal_Bool /*_bGroupControl*/) override { }
+ virtual void SAL_CALL setControlModels(const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& _rControls) override;
+ virtual css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > SAL_CALL getControlModels() override;
+ virtual void SAL_CALL setGroup(const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& _rGroup, const OUString& _rGroupName) override;
+ virtual sal_Int32 SAL_CALL getGroupCount() override;
+ virtual void SAL_CALL getGroup(sal_Int32 _nGroup, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& _rxGroup, OUString& _rName) override;
+ virtual void SAL_CALL getGroupByName(const OUString& _rName, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& _rxGroup) override;
+
+ // css::lang::XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource) override;
+
+ // css::form::XLoadListener
+ virtual void SAL_CALL loaded(const css::lang::EventObject& aEvent) override;
+ virtual void SAL_CALL unloading(const css::lang::EventObject& aEvent) override;
+ virtual void SAL_CALL unloaded(const css::lang::EventObject& aEvent) override;
+ virtual void SAL_CALL reloading(const css::lang::EventObject& aEvent) override;
+ virtual void SAL_CALL reloaded(const css::lang::EventObject& aEvent) override;
+
+ // css::form::XLoadable
+ virtual void SAL_CALL load() override;
+ virtual void SAL_CALL unload() override;
+ virtual void SAL_CALL reload() override;
+ virtual sal_Bool SAL_CALL isLoaded() override;
+ virtual void SAL_CALL addLoadListener(const css::uno::Reference< css::form::XLoadListener>& _rxListener) override;
+ virtual void SAL_CALL removeLoadListener(const css::uno::Reference< css::form::XLoadListener>& _rxListener) override;
+
+ // css::sdbc::XCloseable
+ virtual void SAL_CALL close() override;
+
+ // css::sdbc::XRowSetListener
+ virtual void SAL_CALL cursorMoved(const css::lang::EventObject& event) override;
+ virtual void SAL_CALL rowChanged(const css::lang::EventObject& event) override;
+ virtual void SAL_CALL rowSetChanged(const css::lang::EventObject& event) override;
+
+ // css::sdb::XRowSetApproveListener
+ virtual sal_Bool SAL_CALL approveCursorMove(const css::lang::EventObject& event) override;
+ virtual sal_Bool SAL_CALL approveRowChange(const css::sdb::RowChangeEvent& event) override;
+ virtual sal_Bool SAL_CALL approveRowSetChange(const css::lang::EventObject& event) override;
+
+ // css::sdb::XRowSetApproveBroadcaster
+ virtual void SAL_CALL addRowSetApproveListener(const css::uno::Reference< css::sdb::XRowSetApproveListener>& _rxListener) override;
+ virtual void SAL_CALL removeRowSetApproveListener(const css::uno::Reference< css::sdb::XRowSetApproveListener>& _rxListener) override;
+
+ // com::sun:star::form::XDatabaseParameterBroadcaster2
+ virtual void SAL_CALL addDatabaseParameterListener(const css::uno::Reference< css::form::XDatabaseParameterListener>& _rxListener) override;
+ virtual void SAL_CALL removeDatabaseParameterListener(const css::uno::Reference< css::form::XDatabaseParameterListener>& _rxListener) override;
+
+ // com::sun:star::form::XDatabaseParameterBroadcaster
+ virtual void SAL_CALL addParameterListener(const css::uno::Reference< css::form::XDatabaseParameterListener>& _rxListener) override;
+ virtual void SAL_CALL removeParameterListener(const css::uno::Reference< css::form::XDatabaseParameterListener>& _rxListener) override;
+
+ // css::sdbc::XRowSet
+ virtual void SAL_CALL execute() override;
+ virtual void SAL_CALL addRowSetListener(const css::uno::Reference< css::sdbc::XRowSetListener>& _rxListener) override;
+ virtual void SAL_CALL removeRowSetListener(const css::uno::Reference< css::sdbc::XRowSetListener>& _rxListener) override;
+
+ // css::sdb::XCompletedExecution
+ virtual void SAL_CALL executeWithCompletion( const css::uno::Reference< css::task::XInteractionHandler >& handler ) override;
+
+ // css::sdbc::XResultSet
+ virtual sal_Bool SAL_CALL next() override;
+ virtual sal_Bool SAL_CALL isBeforeFirst() override;
+ virtual sal_Bool SAL_CALL isAfterLast() override;
+ virtual sal_Bool SAL_CALL isFirst() override;
+ virtual sal_Bool SAL_CALL isLast() override;
+ virtual void SAL_CALL beforeFirst() override;
+ virtual void SAL_CALL afterLast() override;
+ virtual sal_Bool SAL_CALL first() override;
+ virtual sal_Bool SAL_CALL last() override;
+ virtual sal_Int32 SAL_CALL getRow() override;
+ virtual sal_Bool SAL_CALL absolute(sal_Int32 row) override;
+ virtual sal_Bool SAL_CALL relative(sal_Int32 rows) override;
+ virtual sal_Bool SAL_CALL previous() override;
+ virtual void SAL_CALL refreshRow() override;
+ virtual sal_Bool SAL_CALL rowUpdated() override;
+ virtual sal_Bool SAL_CALL rowInserted() override;
+ virtual sal_Bool SAL_CALL rowDeleted() override;
+ virtual css::uno::Reference<css::uno::XInterface> SAL_CALL getStatement() override;
+
+ // css::sdbc::XResultSetUpdate
+ virtual void SAL_CALL insertRow() override;
+ virtual void SAL_CALL updateRow() override;
+ virtual void SAL_CALL deleteRow() override;
+ virtual void SAL_CALL cancelRowUpdates() override;
+ virtual void SAL_CALL moveToInsertRow() override;
+ virtual void SAL_CALL moveToCurrentRow() override;
+
+ // css::sdbcx::XDeleteRows
+ virtual css::uno::Sequence< sal_Int32 > SAL_CALL deleteRows(const css::uno::Sequence< css::uno::Any>& rows) override;
+
+ // css::lang::XServiceInfo
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // css::io::XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // css::sdbc::XSQLErrorListener
+ virtual void SAL_CALL errorOccured(const css::sdb::SQLErrorEvent& aEvent) override;
+
+ // css::sdbc::XParameters
+ virtual void SAL_CALL setNull(sal_Int32 parameterIndex, sal_Int32 sqlType) override;
+ virtual void SAL_CALL setObjectNull(sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& typeName) override;
+ virtual void SAL_CALL setBoolean(sal_Int32 parameterIndex, sal_Bool x) override;
+ virtual void SAL_CALL setByte(sal_Int32 parameterIndex, sal_Int8 x) override;
+ virtual void SAL_CALL setShort(sal_Int32 parameterIndex, sal_Int16 x) override;
+ virtual void SAL_CALL setInt(sal_Int32 parameterIndex, sal_Int32 x) override;
+ virtual void SAL_CALL setLong(sal_Int32 parameterIndex, sal_Int64 x) override;
+ virtual void SAL_CALL setFloat(sal_Int32 parameterIndex, float x) override;
+ virtual void SAL_CALL setDouble(sal_Int32 parameterIndex, double x) override;
+ virtual void SAL_CALL setString(sal_Int32 parameterIndex, const OUString& x) override;
+ virtual void SAL_CALL setBytes(sal_Int32 parameterIndex, const css::uno::Sequence< sal_Int8 >& x) override;
+ virtual void SAL_CALL setDate(sal_Int32 parameterIndex, const css::util::Date& x) override;
+ virtual void SAL_CALL setTime(sal_Int32 parameterIndex, const css::util::Time& x) override;
+ virtual void SAL_CALL setTimestamp(sal_Int32 parameterIndex, const css::util::DateTime& x) override;
+ virtual void SAL_CALL setBinaryStream(sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream>& x, sal_Int32 length) override;
+ virtual void SAL_CALL setCharacterStream(sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream>& x, sal_Int32 length) override;
+ virtual void SAL_CALL setObject(sal_Int32 parameterIndex, const css::uno::Any& x) override;
+ virtual void SAL_CALL setObjectWithInfo(sal_Int32 parameterIndex, const css::uno::Any& x, sal_Int32 targetSqlType, sal_Int32 scale) override;
+ virtual void SAL_CALL setRef(sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XRef>& x) override;
+ virtual void SAL_CALL setBlob(sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XBlob>& x) override;
+ virtual void SAL_CALL setClob(sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XClob>& x) override;
+ virtual void SAL_CALL setArray(sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XArray>& x) override;
+ virtual void SAL_CALL clearParameters() override;
+
+ // XPropertyChangeListener
+ virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override;
+
+ // XPropertyContainer
+ virtual void SAL_CALL addProperty( const OUString& Name, ::sal_Int16 Attributes, const css::uno::Any& DefaultValue ) override;
+ virtual void SAL_CALL removeProperty( const OUString& Name ) override;
+
+ // XPropertyAccess
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getPropertyValues( ) override;
+ virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< css::beans::PropertyValue >& aProps ) override;
+ using OPropertySetAggregationHelper::setPropertyValues;
+
+ // XWarningsSupplier
+ virtual css::uno::Any SAL_CALL getWarnings( ) override;
+ virtual void SAL_CALL clearWarnings( ) override;
+
+ // XCloneable
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+protected:
+ // OPropertySetAggregationHelper overridables
+ virtual void forwardingPropertyValue( sal_Int32 _nHandle ) override;
+ virtual void forwardedPropertyValue( sal_Int32 _nHandle ) override;
+
+ // OInterfaceContainer overridables
+ virtual void implInserted( const ElementDescription* _pElement ) override;
+ virtual void implRemoved(const css::uno::Reference<css::uno::XInterface>& _rxObject) override;
+
+ // OPropertyChangeListener
+ virtual void _propertyChanged( const css::beans::PropertyChangeEvent& ) override;
+
+private:
+ bool executeRowSet(::osl::ResettableMutexGuard& _rClearForNotifies, bool bMoveToFirst,
+ const css::uno::Reference< css::task::XInteractionHandler >& _rxCompletionHandler);
+ bool fillParameters(::osl::ResettableMutexGuard& _rClearForNotifies,
+ const css::uno::Reference< css::task::XInteractionHandler >& _rxCompletionHandler);
+ void updateParameterInfo();
+ bool hasValidParent() const;
+
+ // impl methods
+ /// @throws css::uno::RuntimeException
+ void load_impl(bool bCausedByParentForm, bool bMoveToFirst = true,
+ const css::uno::Reference< css::task::XInteractionHandler >& _rxCompletionHandler = css::uno::Reference< css::task::XInteractionHandler >());
+ /// @throws css::uno::RuntimeException
+ void reload_impl(bool bMoveToFirst,
+ const css::uno::Reference< css::task::XInteractionHandler >& _rxCompletionHandler = css::uno::Reference< css::task::XInteractionHandler >());
+ void submit_impl(const css::uno::Reference< css::awt::XControl>& Control, const css::awt::MouseEvent& MouseEvt);
+ void reset_impl(bool _bApproveByListeners);
+
+ bool implEnsureConnection();
+
+ // connection sharing
+
+ /// checks if we can re-use (aka share) the connection of the given parent
+ bool canShareConnection( const css::uno::Reference< css::beans::XPropertySet >& _rxParentProps );
+
+ /// starts sharing the connection with the parent
+ void doShareConnection( const css::uno::Reference< css::beans::XPropertySet >& _rxParentProps );
+
+ /// stops sharing the connection with the parent
+ void stopSharingConnection( );
+
+ /// called when the connection which we share with our parent is being disposed
+ void disposingSharedConnection( const css::uno::Reference< css::sdbc::XConnection >& _rxConn );
+
+ /// checks if we currently share our connection with our parent
+ bool isSharingConnection( ) const { return m_bSharingConnection; }
+
+ /** calls our row set approval listeners
+
+ @param _rEvent
+ the event to notify
+ @param _bAllowSQLException
+ <TRUE/> if SQLExceptions are allowed to leave the method
+ @param _rGuard
+ the guard to be cleared before actually calling into the listeners, but after making
+ a copy of the listeners array to operate on.
+ @return
+ <TRUE/> if and only if the execution has been approved
+ */
+ bool impl_approveRowChange_throw(
+ const css::lang::EventObject& _rEvent,
+ const bool _bAllowSQLException,
+ ::osl::ClearableMutexGuard& _rGuard
+ );
+
+ /// invalidate all our parameter-related stuff
+ void invalidateParameters();
+
+ void saveInsertOnlyState( );
+ void restoreInsertOnlyState( );
+
+ // error handling
+ void onError(const css::sdb::SQLErrorEvent& _rEvent);
+ void onError(const css::sdbc::SQLException&, const OUString& _rContextDescription);
+
+ // html tools
+ OUString GetDataEncoded(bool _bURLEncoded,const css::uno::Reference< css::awt::XControl>& SubmitButton, const css::awt::MouseEvent& MouseEvt);
+ css::uno::Sequence<sal_Int8> GetDataMultiPartEncoded(const css::uno::Reference< css::awt::XControl>& SubmitButton, const css::awt::MouseEvent& MouseEvt,
+ OUString& rContentType);
+
+ void AppendComponent(HtmlSuccessfulObjList& rList, const css::uno::Reference< css::beans::XPropertySet>& xComponentSet, std::u16string_view rNamePrefix,
+ const css::uno::Reference< css::awt::XControl>& rxSubmitButton, const css::awt::MouseEvent& MouseEvt);
+
+ void FillSuccessfulList(HtmlSuccessfulObjList& rList, const css::uno::Reference< css::awt::XControl>& rxSubmitButton, const css::awt::MouseEvent& MouseEvt);
+
+ static void InsertTextPart(INetMIMEMessage& rParent, std::u16string_view rName, std::u16string_view rData);
+ static void InsertFilePart(INetMIMEMessage& rParent, std::u16string_view rName, const OUString& rFileName);
+ static void Encode(OUString& rString);
+
+ css::uno::Reference< css::sdbc::XConnection > getConnection();
+
+ void impl_createLoadTimer();
+
+ void impl_construct();
+
+ DECL_LINK( OnTimeout, Timer*, void );
+protected:
+ using OPropertySetHelper::getPropertyValues;
+};
+
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Date.cxx b/forms/source/component/Date.cxx
new file mode 100644
index 000000000..9b1b3c496
--- /dev/null
+++ b/forms/source/component/Date.cxx
@@ -0,0 +1,326 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "Date.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <connectivity/dbconversion.hxx>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+using namespace dbtools;
+
+namespace frm
+{
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+
+
+ODateControl::ODateControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_DATEFIELD)
+{
+}
+
+
+Sequence<Type> ODateControl::_getTypes()
+{
+ return OBoundControl::_getTypes();
+}
+
+css::uno::Sequence<OUString> SAL_CALL ODateControl::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { FRM_SUN_CONTROL_DATEFIELD, STARDIV_ONE_FORM_CONTROL_DATEFIELD };
+ return comphelper::concatSequences(OBoundControl::getSupportedServiceNames(), vals);
+}
+
+
+Sequence<Type> ODateModel::_getTypes()
+{
+ return OEditBaseModel::_getTypes();
+}
+
+
+ODateModel::ODateModel(const Reference<XComponentContext>& _rxFactory)
+ : OEditBaseModel(_rxFactory, VCL_CONTROLMODEL_DATEFIELD,
+ FRM_SUN_CONTROL_DATEFIELD, true, true)
+ // use the old control name for compytibility reasons
+ , OLimitedFormats(_rxFactory, FormComponentType::DATEFIELD)
+ , m_bDateTimeField(false)
+{
+ m_nClassId = FormComponentType::DATEFIELD;
+ initValueProperty( PROPERTY_DATE, PROPERTY_ID_DATE );
+
+ setAggregateSet(m_xAggregateFastSet, getOriginalHandle(PROPERTY_ID_DATEFORMAT));
+
+ osl_atomic_increment( &m_refCount );
+ try
+ {
+ if ( m_xAggregateSet.is() )
+ m_xAggregateSet->setPropertyValue( PROPERTY_DATEMIN, Any(util::Date(1, 1, 1800)) );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.component", "ODateModel::ODateModel" );
+ }
+ osl_atomic_decrement( &m_refCount );
+}
+
+
+ODateModel::ODateModel( const ODateModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ : OEditBaseModel(_pOriginal, _rxFactory)
+ , OLimitedFormats(_rxFactory, FormComponentType::DATEFIELD)
+ , m_bDateTimeField(false)
+{
+ setAggregateSet( m_xAggregateFastSet, getOriginalHandle( PROPERTY_ID_DATEFORMAT ) );
+}
+
+
+ODateModel::~ODateModel( )
+{
+ setAggregateSet(Reference< XFastPropertySet >(), -1);
+}
+
+// XCloneable
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL ODateModel::createClone()
+{
+ rtl::Reference<ODateModel> pClone = new ODateModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL ODateModel::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals {
+ BINDABLE_CONTROL_MODEL,
+ DATA_AWARE_CONTROL_MODEL,
+ VALIDATABLE_CONTROL_MODEL,
+ BINDABLE_DATA_AWARE_CONTROL_MODEL,
+ VALIDATABLE_BINDABLE_CONTROL_MODEL,
+ FRM_SUN_COMPONENT_DATEFIELD,
+ FRM_SUN_COMPONENT_DATABASE_DATEFIELD,
+ BINDABLE_DATABASE_DATE_FIELD,
+ FRM_COMPONENT_DATEFIELD
+ };
+
+ return comphelper::concatSequences(OBoundControlModel::getSupportedServiceNames(), vals);
+}
+
+
+OUString SAL_CALL ODateModel::getServiceName()
+{
+ return FRM_COMPONENT_DATEFIELD; // old (non-sun) name for compatibility !
+}
+
+// XPropertySet
+
+void ODateModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OEditBaseModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 4);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_DATE, PROPERTY_ID_DEFAULT_DATE, cppu::UnoType<util::Date>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_FORMATKEY, PROPERTY_ID_FORMATKEY, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_FORMATSSUPPLIER, PROPERTY_ID_FORMATSSUPPLIER, cppu::UnoType<XNumberFormatsSupplier>::get(),
+ css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+void SAL_CALL ODateModel::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle ) const
+{
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_FORMATKEY:
+ getFormatKeyPropertyValue(_rValue);
+ break;
+ case PROPERTY_ID_FORMATSSUPPLIER:
+ _rValue <<= getFormatsSupplier();
+ break;
+ default:
+ OEditBaseModel::getFastPropertyValue(_rValue, _nHandle);
+ break;
+ }
+}
+
+
+sal_Bool SAL_CALL ODateModel::convertFastPropertyValue(Any& _rConvertedValue, Any& _rOldValue,
+ sal_Int32 _nHandle, const Any& _rValue )
+{
+ if (PROPERTY_ID_FORMATKEY == _nHandle)
+ return convertFormatKeyPropertyValue(_rConvertedValue, _rOldValue, _rValue);
+ else
+ return OEditBaseModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue );
+}
+
+
+void SAL_CALL ODateModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
+{
+ if (PROPERTY_ID_FORMATKEY == _nHandle)
+ setFormatKeyPropertyValue(_rValue);
+ else
+ OEditBaseModel::setFastPropertyValue_NoBroadcast(_nHandle, _rValue);
+}
+
+// XLoadListener
+
+void ODateModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
+{
+ OBoundControlModel::onConnectedDbColumn( _rxForm );
+ Reference<XPropertySet> xField = getField();
+ if (!xField.is())
+ return;
+
+ m_bDateTimeField = false;
+ try
+ {
+ sal_Int32 nFieldType = 0;
+ xField->getPropertyValue(PROPERTY_FIELDTYPE) >>= nFieldType;
+ m_bDateTimeField = (nFieldType == DataType::TIMESTAMP);
+ }
+ catch(const Exception&)
+ {
+ }
+}
+
+
+bool ODateModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+{
+ Any aControlValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
+ if ( aControlValue == m_aSaveValue )
+ return true;
+
+ if ( !aControlValue.hasValue() )
+ m_xColumnUpdate->updateNull();
+ else
+ {
+ try
+ {
+ util::Date aDate;
+ if ( !( aControlValue >>= aDate ) )
+ {
+ sal_Int32 nAsInt(0);
+ aControlValue >>= nAsInt;
+ aDate = DBTypeConversion::toDate(nAsInt);
+ }
+
+ if ( !m_bDateTimeField )
+ m_xColumnUpdate->updateDate( aDate );
+ else
+ {
+ util::DateTime aDateTime = m_xColumn->getTimestamp();
+ aDateTime.Day = aDate.Day;
+ aDateTime.Month = aDate.Month;
+ aDateTime.Year = aDate.Year;
+ m_xColumnUpdate->updateTimestamp( aDateTime );
+ }
+ }
+ catch(const Exception&)
+ {
+ return false;
+ }
+ }
+ m_aSaveValue = aControlValue;
+ return true;
+}
+
+
+Any ODateModel::translateControlValueToExternalValue( ) const
+{
+ return getControlValue();
+}
+
+
+Any ODateModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+{
+ return _rExternalValue;
+}
+
+
+Any ODateModel::translateControlValueToValidatableValue( ) const
+{
+ return getControlValue();
+}
+
+
+Any ODateModel::translateDbColumnToControlValue()
+{
+ util::Date aDate = m_xColumn->getDate();
+ if (m_xColumn->wasNull())
+ m_aSaveValue.clear();
+ else
+ m_aSaveValue <<= aDate;
+
+ return m_aSaveValue;
+}
+
+
+Any ODateModel::getDefaultForReset() const
+{
+ return m_aDefault;
+}
+
+
+void ODateModel::resetNoBroadcast()
+{
+ OEditBaseModel::resetNoBroadcast();
+ m_aSaveValue.clear();
+}
+
+
+Sequence< Type > ODateModel::getSupportedBindingTypes()
+{
+ return Sequence< Type >( & cppu::UnoType<util::Date>::get(), 1 );
+}
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_ODateModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ODateModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_ODateControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ODateControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Date.hxx b/forms/source/component/Date.hxx
new file mode 100644
index 000000000..77d4d8a3e
--- /dev/null
+++ b/forms/source/component/Date.hxx
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "EditBase.hxx"
+#include <limitedformats.hxx>
+
+
+namespace frm
+{
+
+class ODateModel
+ :public OEditBaseModel
+ ,public OLimitedFormats
+{
+ css::uno::Any m_aSaveValue;
+ bool m_bDateTimeField;
+
+protected:
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ ODateModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ ODateModel(
+ const ODateModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~ODateModel() override;
+
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+
+ // css::beans::XPropertySet
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle ) const override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(css::uno::Any& rConvertedValue, css::uno::Any& rOldValue,
+ sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const css::uno::Any& rValue) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.ODateModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XPropertySet
+ using OEditBaseModel::getFastPropertyValue;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+protected:
+ // OBoundControlModel overridables
+ virtual css::uno::Any
+ translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+
+ virtual css::uno::Any
+ translateControlValueToExternalValue( ) const override;
+ virtual css::uno::Sequence< css::uno::Type >
+ getSupportedBindingTypes() override;
+ virtual css::uno::Any translateExternalValueToControlValue( const css::uno::Any& _rExternalValue ) const override;
+
+ virtual css::uno::Any translateControlValueToValidatableValue( ) const override;
+
+ virtual css::uno::Any getDefaultForReset() const override;
+
+ virtual void resetNoBroadcast() override;
+
+ virtual void onConnectedDbColumn( const css::uno::Reference< css::uno::XInterface >& _rxForm ) override;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+class ODateControl: public OBoundControl
+{
+protected:
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ explicit ODateControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+ DECLARE_UNO3_AGG_DEFAULTS(ODateControl, OBoundControl)
+
+// css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.ODateControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Edit.cxx b/forms/source/component/Edit.cxx
new file mode 100644
index 000000000..6c7d83f20
--- /dev/null
+++ b/forms/source/component/Edit.cxx
@@ -0,0 +1,717 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "Edit.hxx"
+#include <property.hxx>
+#include <services.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/form/XSubmit.hpp>
+#include <com/sun/star/util/NumberFormat.hpp>
+
+#include <vcl/svapp.hxx>
+#include <vcl/keycodes.hxx>
+
+#include <connectivity/formattedcolumnvalue.hxx>
+
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+
+using namespace dbtools;
+
+namespace frm
+{
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::form::binding;
+
+
+Sequence<Type> OEditControl::_getTypes()
+{
+ // my two base classes
+ static Sequence<Type> const aTypes = concatSequences(OBoundControl::_getTypes(), OEditControl_BASE::getTypes());
+ return aTypes;
+}
+
+
+Any SAL_CALL OEditControl::queryAggregation(const Type& _rType)
+{
+ Any aReturn = OBoundControl::queryAggregation(_rType);
+ if (!aReturn.hasValue())
+ aReturn = OEditControl_BASE::queryInterface(_rType);
+
+ return aReturn;
+}
+
+
+OEditControl::OEditControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl( _rxFactory, FRM_SUN_CONTROL_RICHTEXTCONTROL )
+ ,m_aChangeListeners(m_aMutex)
+ ,m_nKeyEvent( nullptr )
+{
+
+ osl_atomic_increment(&m_refCount);
+ {
+ Reference<XWindow> xComp;
+ if (query_aggregation(m_xAggregate, xComp))
+ {
+ xComp->addFocusListener(this);
+ xComp->addKeyListener(this);
+ }
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+
+OEditControl::~OEditControl()
+{
+ if( m_nKeyEvent )
+ Application::RemoveUserEvent( m_nKeyEvent );
+
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+}
+
+// XChangeBroadcaster
+
+void OEditControl::addChangeListener(const Reference<XChangeListener>& l)
+{
+ m_aChangeListeners.addInterface( l );
+}
+
+
+void OEditControl::removeChangeListener(const Reference<XChangeListener>& l)
+{
+ m_aChangeListeners.removeInterface( l );
+}
+
+// OComponentHelper
+
+void OEditControl::disposing()
+{
+ OBoundControl::disposing();
+
+ EventObject aEvt(static_cast<XWeak*>(this));
+ m_aChangeListeners.disposeAndClear(aEvt);
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> OEditControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 3);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-3] = FRM_SUN_CONTROL_TEXTFIELD;
+ pArray[aSupported.getLength()-2] = STARDIV_ONE_FORM_CONTROL_EDIT;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_TEXTFIELD;
+ return aSupported;
+}
+
+// XEventListener
+
+void OEditControl::disposing(const EventObject& Source)
+{
+ OBoundControl::disposing(Source);
+}
+
+// XFocusListener
+
+void OEditControl::focusGained( const FocusEvent& /*e*/ )
+{
+ Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
+ if (xSet.is())
+ xSet->getPropertyValue( PROPERTY_TEXT ) >>= m_aHtmlChangeValue;
+}
+
+
+void OEditControl::focusLost( const FocusEvent& /*e*/ )
+{
+ Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
+ if (xSet.is())
+ {
+ OUString sNewHtmlChangeValue;
+ xSet->getPropertyValue( PROPERTY_TEXT ) >>= sNewHtmlChangeValue;
+ if( sNewHtmlChangeValue != m_aHtmlChangeValue )
+ {
+ EventObject aEvt( *this );
+ m_aChangeListeners.notifyEach( &XChangeListener::changed, aEvt );
+ }
+ }
+}
+
+// XKeyListener
+
+void OEditControl::keyPressed(const css::awt::KeyEvent& e)
+{
+ if( e.KeyCode != KEY_RETURN || e.Modifiers != 0 )
+ return;
+
+ // Is the Control in a form with a submit URL?
+ Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
+ if( !xSet.is() )
+ return;
+
+ // Not for multiline edits
+ Any aTmp( xSet->getPropertyValue(PROPERTY_MULTILINE));
+ if ((aTmp.getValueType().equals(cppu::UnoType<bool>::get())) && getBOOL(aTmp))
+ return;
+
+ Reference<XFormComponent> xFComp(xSet, UNO_QUERY);
+ css::uno::Reference<css::uno::XInterface> xParent = xFComp->getParent();
+ if( !xParent.is() )
+ return;
+
+ Reference<XPropertySet> xFormSet(xParent, UNO_QUERY);
+ if( !xFormSet.is() )
+ return;
+
+ aTmp = xFormSet->getPropertyValue( PROPERTY_TARGET_URL );
+ if (!aTmp.getValueType().equals(cppu::UnoType<OUString>::get()) ||
+ getString(aTmp).isEmpty() )
+ return;
+
+ Reference<XIndexAccess> xElements(xParent, UNO_QUERY);
+ sal_Int32 nCount = xElements->getCount();
+ if( nCount > 1 )
+ {
+ Reference<XPropertySet> xFCSet;
+ for( sal_Int32 nIndex=0; nIndex < nCount; nIndex++ )
+ {
+ // Any aElement(xElements->getByIndex(nIndex));
+ xElements->getByIndex(nIndex) >>= xFCSet;
+ OSL_ENSURE(xFCSet.is(),"OEditControl::keyPressed: No XPropertySet!");
+
+ if (hasProperty(PROPERTY_CLASSID, xFCSet) &&
+ getINT16(xFCSet->getPropertyValue(PROPERTY_CLASSID)) == FormComponentType::TEXTFIELD)
+ {
+ // Found another Edit -> then do not submit!
+ if (xFCSet != xSet)
+ return;
+ }
+ }
+ }
+
+ // Because we're still in the header, trigger submit asynchronously
+ if( m_nKeyEvent )
+ Application::RemoveUserEvent( m_nKeyEvent );
+ m_nKeyEvent = Application::PostUserEvent( LINK(this, OEditControl, OnKeyPressed) );
+}
+
+
+void OEditControl::keyReleased(const css::awt::KeyEvent& /*e*/)
+{
+}
+
+
+IMPL_LINK_NOARG(OEditControl, OnKeyPressed, void*, void)
+{
+ m_nKeyEvent = nullptr;
+
+ Reference<XFormComponent> xFComp(getModel(), UNO_QUERY);
+ css::uno::Reference<css::uno::XInterface> xParent = xFComp->getParent();
+ Reference<XSubmit> xSubmit(xParent, UNO_QUERY);
+ if (xSubmit.is())
+ xSubmit->submit( Reference<XControl>(), css::awt::MouseEvent() );
+}
+
+
+
+OEditModel::OEditModel(const Reference<XComponentContext>& _rxFactory)
+ :OEditBaseModel( _rxFactory, FRM_SUN_COMPONENT_RICHTEXTCONTROL, FRM_SUN_CONTROL_TEXTFIELD, true, true )
+ ,m_bMaxTextLenModified(false)
+ ,m_bWritingFormattedFake(false)
+{
+
+ m_nClassId = FormComponentType::TEXTFIELD;
+ initValueProperty( PROPERTY_TEXT, PROPERTY_ID_TEXT );
+}
+
+
+OEditModel::OEditModel( const OEditModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OEditBaseModel( _pOriginal, _rxFactory )
+ ,m_bMaxTextLenModified(false)
+ ,m_bWritingFormattedFake(false)
+{
+
+ // Note that most of the properties are not clone from the original object:
+ // Things as the format key, it's type, and such, depend on the field being part of a loaded form
+ // (they're initialized in onConnectedDbColumn). Even if the original object _is_ part of such a form, we ourself
+ // certainly aren't, so these members are defaulted. If we're inserted into a form which is already loaded,
+ // they will be set to new values, anyway...
+}
+
+
+OEditModel::~OEditModel()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+}
+
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OEditModel::createClone()
+{
+ rtl::Reference<OEditModel> pClone = new OEditModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+void OEditModel::disposing()
+{
+ OEditBaseModel::disposing();
+ m_pValueFormatter.reset();
+}
+
+// XPersistObject
+
+OUString SAL_CALL OEditModel::getServiceName()
+{
+ return FRM_COMPONENT_EDIT; // old (non-sun) name for compatibility !
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL OEditModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControlModel::getSupportedServiceNames();
+
+ sal_Int32 nOldLen = aSupported.getLength();
+ aSupported.realloc( nOldLen + 9 );
+ OUString* pStoreTo = aSupported.getArray() + nOldLen;
+
+ *pStoreTo++ = BINDABLE_CONTROL_MODEL;
+ *pStoreTo++ = DATA_AWARE_CONTROL_MODEL;
+ *pStoreTo++ = VALIDATABLE_CONTROL_MODEL;
+
+ *pStoreTo++ = BINDABLE_DATA_AWARE_CONTROL_MODEL;
+ *pStoreTo++ = VALIDATABLE_BINDABLE_CONTROL_MODEL;
+
+ *pStoreTo++ = FRM_SUN_COMPONENT_TEXTFIELD;
+ *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_TEXTFIELD;
+ *pStoreTo++ = BINDABLE_DATABASE_TEXT_FIELD;
+
+ *pStoreTo++ = FRM_COMPONENT_TEXTFIELD;
+
+ return aSupported;
+}
+
+// XPropertySet
+void SAL_CALL OEditModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle ) const
+{
+ if ( PROPERTY_ID_PERSISTENCE_MAXTEXTLENGTH == nHandle )
+ {
+ if ( m_bMaxTextLenModified )
+ rValue <<= sal_Int16(0);
+ else if ( m_xAggregateSet.is() )
+ rValue = m_xAggregateSet->getPropertyValue(PROPERTY_MAXTEXTLEN);
+ }
+ else
+ {
+ OEditBaseModel::getFastPropertyValue(rValue, nHandle );
+ }
+}
+
+
+void OEditModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OEditBaseModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 5);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_PERSISTENCE_MAXTEXTLENGTH, PROPERTY_ID_PERSISTENCE_MAXTEXTLENGTH, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_TEXT, PROPERTY_ID_DEFAULT_TEXT, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_EMPTY_IS_NULL, PROPERTY_ID_EMPTY_IS_NULL, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_FILTERPROPOSAL, PROPERTY_ID_FILTERPROPOSAL, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+void OEditModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const
+{
+ OEditBaseModel::describeAggregateProperties( _rAggregateProps );
+
+ // our aggregate is a rich text model, which also derives from OControlModel, as
+ // do we, so we need to remove some duplicate properties
+ RemoveProperty( _rAggregateProps, PROPERTY_TABINDEX );
+ RemoveProperty( _rAggregateProps, PROPERTY_CLASSID );
+ RemoveProperty( _rAggregateProps, PROPERTY_NAME );
+ RemoveProperty( _rAggregateProps, PROPERTY_TAG );
+ RemoveProperty( _rAggregateProps, PROPERTY_NATIVE_LOOK );
+
+}
+
+
+bool OEditModel::implActsAsRichText( ) const
+{
+ bool bActAsRichText = false;
+ if ( m_xAggregateSet.is() )
+ {
+ OSL_VERIFY( m_xAggregateSet->getPropertyValue( PROPERTY_RICH_TEXT ) >>= bActAsRichText );
+ }
+ return bActAsRichText;
+}
+
+
+void SAL_CALL OEditModel::reset( )
+{
+ // no reset if we currently act as rich text control
+ if ( implActsAsRichText() )
+ return;
+
+ OEditBaseModel::reset();
+}
+
+
+namespace
+{
+ void lcl_transferProperties( const Reference< XPropertySet >& _rxSource, const Reference< XPropertySet >& _rxDest )
+ {
+ try
+ {
+ Reference< XPropertySetInfo > xSourceInfo;
+ if ( _rxSource.is() )
+ xSourceInfo = _rxSource->getPropertySetInfo();
+
+ Reference< XPropertySetInfo > xDestInfo;
+ if ( _rxDest.is() )
+ xDestInfo = _rxDest->getPropertySetInfo();
+
+ if ( !xSourceInfo.is() || !xDestInfo.is() )
+ {
+ OSL_FAIL( "lcl_transferProperties: invalid property set(s)!" );
+ return;
+ }
+
+ const Sequence< Property > aSourceProps( xSourceInfo->getProperties() );
+ for ( auto const & sourceprop : aSourceProps )
+ {
+ if ( !xDestInfo->hasPropertyByName( sourceprop.Name ) )
+ {
+ continue;
+ }
+
+ Property aDestProp( xDestInfo->getPropertyByName( sourceprop.Name ) );
+ if ( 0 != ( aDestProp.Attributes & PropertyAttribute::READONLY ) )
+ {
+ continue;
+ }
+
+ try
+ {
+ _rxDest->setPropertyValue( sourceprop.Name, _rxSource->getPropertyValue( sourceprop.Name ) );
+ }
+ catch(const IllegalArgumentException&)
+ {
+ TOOLS_WARN_EXCEPTION( "forms.component", "could not transfer the property named '"
+ << sourceprop.Name
+ << "'" );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+}
+
+
+void OEditModel::writeAggregate( const Reference< XObjectOutputStream >& _rxOutStream ) const
+{
+ // we need to fake the writing of our aggregate. Since #i24387#, we have another aggregate,
+ // but for compatibility, we need to use an "old" aggregate for writing and reading
+
+ Reference< XPropertySet > xFakedAggregate(
+ getContext()->getServiceManager()->createInstanceWithContext( VCL_CONTROLMODEL_EDIT, getContext() ),
+ UNO_QUERY
+ );
+ OSL_ENSURE( xFakedAggregate.is(), "OEditModel::writeAggregate: could not create an old EditControlModel!" );
+ if ( !xFakedAggregate.is() )
+ return;
+
+ lcl_transferProperties( m_xAggregateSet, xFakedAggregate );
+
+ Reference< XPersistObject > xFakedPersist( xFakedAggregate, UNO_QUERY );
+ OSL_ENSURE( xFakedPersist.is(), "OEditModel::writeAggregate: no XPersistObject!" );
+ if ( xFakedPersist.is() )
+ xFakedPersist->write( _rxOutStream );
+}
+
+
+void OEditModel::readAggregate( const Reference< XObjectInputStream >& _rxInStream )
+{
+ // we need to fake the reading of our aggregate. Since #i24387#, we have another aggregate,
+ // but for compatibility, we need to use an "old" aggregate for writing and reading
+
+ Reference< XPropertySet > xFakedAggregate(
+ getContext()->getServiceManager()->createInstanceWithContext( VCL_CONTROLMODEL_EDIT, getContext() ),
+ UNO_QUERY
+ );
+ Reference< XPersistObject > xFakedPersist( xFakedAggregate, UNO_QUERY );
+ OSL_ENSURE( xFakedPersist.is(), "OEditModel::readAggregate: no XPersistObject, or no faked aggregate at all!" );
+ if ( xFakedPersist.is() )
+ {
+ xFakedPersist->read( _rxInStream );
+ lcl_transferProperties( xFakedAggregate, m_xAggregateSet );
+ }
+}
+
+
+void OEditModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ Any aCurrentText;
+ sal_Int16 nOldTextLen = 0;
+ // Am I loaded at the moment and did I switch MaxTextLen temporarily?
+ if ( m_bMaxTextLenModified )
+ { // -> for the duration of saving, make my aggregated model believe the old TextLen
+
+ // before doing this we have to save the current text value of the aggregate, as this may be affected by resetting the text len
+ aCurrentText = m_xAggregateSet->getPropertyValue(PROPERTY_TEXT);
+
+ m_xAggregateSet->getPropertyValue(PROPERTY_MAXTEXTLEN) >>= nOldTextLen;
+ m_xAggregateSet->setPropertyValue(PROPERTY_MAXTEXTLEN, Any(sal_Int16(0)));
+ }
+
+ OEditBaseModel::write(_rxOutStream);
+
+ if ( m_bMaxTextLenModified )
+ { // Reset again
+ m_xAggregateSet->setPropertyValue(PROPERTY_MAXTEXTLEN, Any(nOldTextLen));
+ // and reset the text
+ // First we set it to an empty string : Without this the second setPropertyValue would not do anything as it thinks
+ // we aren't changing the prop (it didn't notify the - implicit - change of the text prop while setting the max text len)
+ // This seems to be a bug with in toolkit's EditControl-implementation.
+ m_xAggregateSet->setPropertyValue(PROPERTY_TEXT, Any(OUString()));
+ m_xAggregateSet->setPropertyValue(PROPERTY_TEXT, aCurrentText);
+ }
+}
+
+
+void OEditModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ OEditBaseModel::read(_rxInStream);
+
+ // Some versions (5.1 'til about 552) wrote a wrong DefaultControl-property value which is unknown
+ // to older versions (5.0).
+ // correct this ...
+ if (m_xAggregateSet.is())
+ {
+ Any aDefaultControl = m_xAggregateSet->getPropertyValue(PROPERTY_DEFAULTCONTROL);
+ if ( (aDefaultControl.getValueType().getTypeClass() == TypeClass_STRING)
+ && (getString(aDefaultControl) == STARDIV_ONE_FORM_CONTROL_TEXTFIELD )
+ )
+ {
+ m_xAggregateSet->setPropertyValue( PROPERTY_DEFAULTCONTROL, Any( OUString(STARDIV_ONE_FORM_CONTROL_EDIT) ) );
+ // Older as well as current versions should understand this : the former knew only the STARDIV_ONE_FORM_CONTROL_EDIT,
+ // the latter are registered for both STARDIV_ONE_FORM_CONTROL_EDIT and STARDIV_ONE_FORM_CONTROL_TEXTFIELD.
+ }
+ }
+}
+
+
+sal_uInt16 OEditModel::getPersistenceFlags() const
+{
+ sal_uInt16 nFlags = OEditBaseModel::getPersistenceFlags();
+
+ if (m_bWritingFormattedFake)
+ nFlags |= PF_FAKE_FORMATTED_FIELD;
+
+ return nFlags;
+}
+
+
+void OEditModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
+{
+ Reference< XPropertySet > xField = getField();
+ if ( !xField.is() )
+ return;
+
+ m_pValueFormatter.reset( new ::dbtools::FormattedColumnValue( getContext(), Reference< XRowSet >( _rxForm, UNO_QUERY ), xField ) );
+
+ if ( m_pValueFormatter->getKeyType() == NumberFormat::SCIENTIFIC )
+ return;
+
+ m_bMaxTextLenModified = getINT16(m_xAggregateSet->getPropertyValue(PROPERTY_MAXTEXTLEN)) != 0;
+ if ( !m_bMaxTextLenModified )
+ {
+ sal_Int32 nFieldLen = 0;
+ xField->getPropertyValue("Precision") >>= nFieldLen;
+
+ if (nFieldLen > 0 && nFieldLen <= SAL_MAX_INT16)
+ {
+ Any aVal;
+ aVal <<= static_cast<sal_Int16>(nFieldLen);
+ m_xAggregateSet->setPropertyValue(PROPERTY_MAXTEXTLEN, aVal);
+
+ m_bMaxTextLenModified = true;
+ }
+ }
+ else
+ m_bMaxTextLenModified = false; // to get sure that the text len won't be set in unloaded
+}
+
+
+void OEditModel::onDisconnectedDbColumn()
+{
+ OEditBaseModel::onDisconnectedDbColumn();
+
+ m_pValueFormatter.reset();
+
+ if ( hasField() && m_bMaxTextLenModified )
+ {
+ Any aVal;
+ aVal <<= sal_Int16(0); // Only if it was 0, I switched it in onConnectedDbColumn
+ m_xAggregateSet->setPropertyValue(PROPERTY_MAXTEXTLEN, aVal);
+ m_bMaxTextLenModified = false;
+ }
+}
+
+
+bool OEditModel::approveDbColumnType( sal_Int32 _nColumnType )
+{
+ // if we act as rich text currently, we do not allow binding to a database column
+ if ( implActsAsRichText() )
+ return false;
+
+ return OEditBaseModel::approveDbColumnType( _nColumnType );
+}
+
+
+bool OEditModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+{
+ Any aNewValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
+
+ OUString sNewValue;
+ aNewValue >>= sNewValue;
+
+ if ( !aNewValue.hasValue()
+ || ( sNewValue.isEmpty() // an empty string
+ && m_bEmptyIsNull // which should be interpreted as NULL
+ )
+ )
+ {
+ m_xColumnUpdate->updateNull();
+ }
+ else
+ {
+ OSL_PRECOND(m_pValueFormatter,
+ "OEditModel::commitControlValueToDbColumn: no value formatter!");
+ try
+ {
+ if (m_pValueFormatter)
+ {
+ if ( !m_pValueFormatter->setFormattedValue( sNewValue ) )
+ return false;
+ }
+ else
+ m_xColumnUpdate->updateString( sNewValue );
+ }
+ catch ( const Exception& )
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+Any OEditModel::translateDbColumnToControlValue()
+{
+ OSL_PRECOND(m_pValueFormatter,
+ "OEditModel::translateDbColumnToControlValue: no value formatter!");
+ Any aRet;
+ if (m_pValueFormatter)
+ {
+ OUString sValue( m_pValueFormatter->getFormattedValue() );
+ if ( sValue.isEmpty()
+ && m_pValueFormatter->getColumn().is()
+ && m_pValueFormatter->getColumn()->wasNull()
+ )
+ {
+ }
+ else
+ {
+ // #i2817# OJ
+ sal_uInt16 nMaxTextLen = getINT16( m_xAggregateSet->getPropertyValue( PROPERTY_MAXTEXTLEN ) );
+ if ( nMaxTextLen && sValue.getLength() > nMaxTextLen )
+ {
+ sal_Int32 nDiff = sValue.getLength() - nMaxTextLen;
+ sValue = sValue.replaceAt( nMaxTextLen, nDiff, u"" );
+ }
+
+ aRet <<= sValue;
+ }
+ }
+
+ return aRet.hasValue() ? aRet : Any( OUString() );
+}
+
+
+Any OEditModel::getDefaultForReset() const
+{
+ return Any( m_aDefaultText );
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OEditModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OEditModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OEditControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OEditControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Edit.hxx b/forms/source/component/Edit.hxx
new file mode 100644
index 000000000..219b41d21
--- /dev/null
+++ b/forms/source/component/Edit.hxx
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include "EditBase.hxx"
+
+#include <tools/link.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <cppuhelper/implbase3.hxx>
+
+#include <com/sun/star/form/XChangeBroadcaster.hpp>
+
+namespace dbtools { class FormattedColumnValue; }
+struct ImplSVEvent;
+
+namespace frm
+{
+
+class OEditModel final : public OEditBaseModel
+{
+ ::std::unique_ptr< ::dbtools::FormattedColumnValue >
+ m_pValueFormatter;
+ bool m_bMaxTextLenModified : 1; // set to <TRUE/> when we change the MaxTextLen of the aggregate
+
+ bool m_bWritingFormattedFake : 1; // are we writing something which should be interpreted as formatted upon reading?
+
+public:
+ OEditModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OEditModel(
+ const OEditModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OEditModel() override;
+
+private:
+ void enableFormattedWriteFake() { m_bWritingFormattedFake = true; }
+ void disableFormattedWriteFake() { m_bWritingFormattedFake = false; }
+ bool lastReadWasFormattedFake() const { return (getLastReadVersion() & PF_FAKE_FORMATTED_FIELD) != 0; }
+
+ friend class OFormattedFieldWrapper;
+ friend class OFormattedModel; // temporary
+
+public:
+ virtual void SAL_CALL disposing() override;
+
+ // XPropertySet
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle ) const override;
+
+ // XPersistObject
+ virtual void SAL_CALL write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+ virtual OUString SAL_CALL getServiceName() override;
+
+ // XPropertySet
+ using OBoundControlModel::getFastPropertyValue;
+
+ // XReset
+ virtual void SAL_CALL reset( ) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OEditModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+ virtual void describeAggregateProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+
+ // XEventListener
+ using OBoundControlModel::disposing;
+
+private:
+ // OControlModel overridables
+ virtual void writeAggregate( const css::uno::Reference< css::io::XObjectOutputStream >& _rxOutStream ) const override;
+ virtual void readAggregate( const css::uno::Reference< css::io::XObjectInputStream >& _rxInStream ) override;
+
+ // OBoundControlModel overridables
+ virtual css::uno::Any translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+
+ virtual css::uno::Any getDefaultForReset() const override;
+
+ virtual void onConnectedDbColumn( const css::uno::Reference< css::uno::XInterface >& _rxForm ) override;
+ virtual void onDisconnectedDbColumn() override;
+
+ virtual bool approveDbColumnType( sal_Int32 _nColumnType ) override;
+
+ virtual sal_uInt16 getPersistenceFlags() const override;
+
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ bool implActsAsRichText( ) const;
+};
+
+
+//= OEditControl
+
+typedef ::cppu::ImplHelper3< css::awt::XFocusListener,
+ css::awt::XKeyListener,
+ css::form::XChangeBroadcaster > OEditControl_BASE;
+
+class OEditControl : public OBoundControl
+ ,public OEditControl_BASE
+{
+ ::comphelper::OInterfaceContainerHelper3<css::form::XChangeListener>
+ m_aChangeListeners;
+
+ OUString m_aHtmlChangeValue;
+ ImplSVEvent * m_nKeyEvent;
+
+public:
+ explicit OEditControl(const css::uno::Reference< css::uno::XComponentContext>& _rxContext);
+ virtual ~OEditControl() override;
+
+ DECLARE_UNO3_AGG_DEFAULTS(OEditControl, OBoundControl)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+// OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+// css::lang::XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource) override;
+
+// css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OEditControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+// css::form::XChangeBroadcaster
+ virtual void SAL_CALL addChangeListener(const css::uno::Reference< css::form::XChangeListener>& _rxListener) override;
+ virtual void SAL_CALL removeChangeListener(const css::uno::Reference< css::form::XChangeListener>& _rxListener) override;
+
+// css::awt::XFocusListener
+ virtual void SAL_CALL focusGained( const css::awt::FocusEvent& e ) override;
+ virtual void SAL_CALL focusLost( const css::awt::FocusEvent& e ) override;
+
+// css::awt::XKeyListener
+ virtual void SAL_CALL keyPressed(const css::awt::KeyEvent& e) override;
+ virtual void SAL_CALL keyReleased(const css::awt::KeyEvent& e) override;
+
+ // XControl
+ using OBoundControl::createPeer;
+
+private:
+ DECL_LINK( OnKeyPressed, void*, void );
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/EditBase.cxx b/forms/source/component/EditBase.cxx
new file mode 100644
index 000000000..b584eaa82
--- /dev/null
+++ b/forms/source/component/EditBase.cxx
@@ -0,0 +1,377 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "EditBase.hxx"
+#include <property.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <tools/time.hxx>
+#include <tools/date.hxx>
+#include <com/sun/star/io/XMarkableStream.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <com/sun/star/util/Date.hpp>
+
+
+namespace frm
+{
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+
+namespace
+{
+ const sal_uInt16 DEFAULT_LONG = 0x0001;
+ const sal_uInt16 DEFAULT_DOUBLE = 0x0002;
+ const sal_uInt16 FILTERPROPOSAL = 0x0004;
+ const sal_uInt16 DEFAULT_TIME = 0x0008;
+ const sal_uInt16 DEFAULT_DATE = 0x0010;
+}
+
+
+OEditBaseModel::OEditBaseModel( const Reference< XComponentContext >& _rxFactory, const OUString& rUnoControlModelName,
+ const OUString& rDefault, const bool _bSupportExternalBinding, const bool _bSupportsValidation )
+ :OBoundControlModel( _rxFactory, rUnoControlModelName, rDefault, true, _bSupportExternalBinding, _bSupportsValidation )
+ ,m_nLastReadVersion(0)
+ ,m_bEmptyIsNull(true)
+ ,m_bFilterProposal(false)
+{
+}
+
+
+OEditBaseModel::OEditBaseModel( const OEditBaseModel* _pOriginal, const Reference< XComponentContext >& _rxFactory )
+ :OBoundControlModel( _pOriginal, _rxFactory )
+ ,m_nLastReadVersion(0)
+{
+
+ m_bFilterProposal = _pOriginal->m_bFilterProposal;
+ m_bEmptyIsNull = _pOriginal->m_bEmptyIsNull;
+ m_aDefault = _pOriginal->m_aDefault;
+ m_aDefaultText = _pOriginal->m_aDefaultText;
+}
+
+
+OEditBaseModel::~OEditBaseModel( )
+{
+}
+
+// XPersist
+
+void OEditBaseModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ OBoundControlModel::write(_rxOutStream);
+
+ // Version
+ sal_uInt16 nVersionId = 0x0006;
+ DBG_ASSERT((getPersistenceFlags() & ~PF_SPECIAL_FLAGS) == 0,
+ "OEditBaseModel::write : invalid special version flags !");
+ // please don't use other flags, older versions can't interpret them !
+
+ nVersionId |= getPersistenceFlags();
+ _rxOutStream->writeShort(nVersionId);
+
+ // Name
+ _rxOutStream->writeShort(0); // obsolete
+ _rxOutStream << m_aDefaultText;
+
+ // Masking for any
+ sal_uInt16 nAnyMask = 0;
+ if (m_aDefault.getValueType().getTypeClass() == TypeClass_LONG)
+ nAnyMask |= DEFAULT_LONG;
+ else if (m_aDefault.getValueType().getTypeClass() == TypeClass_DOUBLE)
+ nAnyMask |= DEFAULT_DOUBLE;
+ else if (m_aDefault.getValueType() == cppu::UnoType<util::Time>::get())
+ nAnyMask |= DEFAULT_TIME;
+ else if (m_aDefault.getValueType() == cppu::UnoType<util::Date>::get())
+ nAnyMask |= DEFAULT_DATE;
+
+ if (m_bFilterProposal) // Don't save a value, because it's boolean
+ nAnyMask |= FILTERPROPOSAL;
+
+ _rxOutStream->writeBoolean(m_bEmptyIsNull);
+ _rxOutStream->writeShort(nAnyMask);
+
+ if ((nAnyMask & DEFAULT_LONG) == DEFAULT_LONG)
+ _rxOutStream->writeLong(getINT32(m_aDefault));
+ else if ((nAnyMask & DEFAULT_DOUBLE) == DEFAULT_DOUBLE)
+ _rxOutStream->writeDouble(getDouble(m_aDefault));
+ else if ((nAnyMask & DEFAULT_TIME) == DEFAULT_TIME)
+ {
+ util::Time aTime;
+ OSL_VERIFY(m_aDefault >>= aTime);
+ _rxOutStream->writeHyper(::tools::Time(aTime).GetTime());
+ }
+ else if ((nAnyMask & DEFAULT_DATE) == DEFAULT_DATE)
+ {
+ util::Date aDate;
+ OSL_VERIFY(m_aDefault >>= aDate);
+ _rxOutStream->writeLong(::Date(aDate).GetDate());
+ }
+
+ // since version 5 we write the help text
+ writeHelpTextCompatibly(_rxOutStream);
+ // (that's potentially bad : at the time I added the above line we had two derived classes : OEditModel and
+ // OFormattedModel. The first one does not have an own version handling, so it can't write the help text itself,
+ // the second one does its own writing (reading) after calling our method, so normally we shouldn't write any
+ // additional members as this is not compatible to older office versions.
+ // We decided to place the writing of the help text here as it seems the less worse alternative. There is no delivered
+ // office version including formatted controls (and thus the OFormattedModel), and the OFormattedModel::read seems
+ // robust against this change (as it will read a wrong and unknown file version and thus set its members to defaults).
+
+ if ((nVersionId & PF_HANDLE_COMMON_PROPS) != 0)
+ writeCommonEditProperties(_rxOutStream);
+
+ // !!! properties common to all OEditBaseModel derived classes should be written in writeCommonEditProperties !!!
+}
+
+
+sal_uInt16 OEditBaseModel::getPersistenceFlags() const
+{
+ return PF_HANDLE_COMMON_PROPS;
+}
+
+
+void OEditBaseModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ OBoundControlModel::read(_rxInStream);
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ // Version's own version number
+ sal_uInt16 nVersion = _rxInStream->readShort();
+ m_nLastReadVersion = nVersion;
+
+ bool bHandleCommonProps = (nVersion & PF_HANDLE_COMMON_PROPS) != 0;
+ nVersion = nVersion & ~PF_SPECIAL_FLAGS;
+
+ // obsolete
+ _rxInStream->readShort();
+
+ _rxInStream >> m_aDefaultText;
+
+ if (nVersion >= 0x0003)
+ {
+ m_bEmptyIsNull = _rxInStream->readBoolean();
+
+ sal_uInt16 nAnyMask = _rxInStream->readShort();
+ if ((nAnyMask & DEFAULT_LONG) == DEFAULT_LONG)
+ {
+ sal_Int32 nValue = _rxInStream->readLong();
+ m_aDefault <<= nValue;
+ }
+ else if ((nAnyMask & DEFAULT_DOUBLE) == DEFAULT_DOUBLE)
+ {
+ double fValue = _rxInStream->readDouble();
+ m_aDefault <<= fValue;
+ }
+ else if ((nAnyMask & DEFAULT_TIME) == DEFAULT_TIME)
+ {
+ m_aDefault <<= ::tools::Time(_rxInStream->readHyper()).GetUNOTime();
+ }
+ else if ((nAnyMask & DEFAULT_DATE) == DEFAULT_DATE)
+ {
+ m_aDefault <<= ::Date(_rxInStream->readLong()).GetUNODate();
+ }
+
+ if ((nAnyMask & FILTERPROPOSAL) == FILTERPROPOSAL)
+ m_bFilterProposal = true;
+ }
+
+ if (nVersion > 4)
+ readHelpTextCompatibly(_rxInStream);
+
+ if (bHandleCommonProps)
+ readCommonEditProperties(_rxInStream);
+
+ // After reading, display default values
+ if ( !getControlSource().isEmpty() )
+ // (not if we don't have a control source - the "State" property acts like it is persistent, then)
+ resetNoBroadcast();
+};
+
+
+void OEditBaseModel::defaultCommonEditProperties()
+{
+ OBoundControlModel::defaultCommonProperties();
+ // no own common properties at the moment
+}
+
+
+void OEditBaseModel::readCommonEditProperties(const Reference<XObjectInputStream>& _rxInStream)
+{
+ sal_Int32 nLen = _rxInStream->readLong();
+
+ Reference<XMarkableStream> xMark(_rxInStream, UNO_QUERY);
+ DBG_ASSERT(xMark.is(), "OBoundControlModel::readCommonProperties : can only work with markable streams !");
+ sal_Int32 nMark = xMark->createMark();
+
+ // read properties common to all OBoundControlModels
+ OBoundControlModel::readCommonProperties(_rxInStream);
+
+ // read properties common to all OEditBaseModels
+
+ // skip the remaining bytes
+ xMark->jumpToMark(nMark);
+ _rxInStream->skipBytes(nLen);
+ xMark->deleteMark(nMark);
+}
+
+
+void OEditBaseModel::writeCommonEditProperties(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ Reference<XMarkableStream> xMark(_rxOutStream, UNO_QUERY);
+ DBG_ASSERT(xMark.is(), "OEditBaseModel::writeCommonProperties : can only work with markable streams !");
+ sal_Int32 nMark = xMark->createMark();
+
+ // a placeholder where we will write the overall length (later in this method)
+ sal_Int32 nLen = 0;
+ _rxOutStream->writeLong(nLen);
+
+ // write properties common to all OBoundControlModels
+ OBoundControlModel::writeCommonProperties(_rxOutStream);
+
+ // write properties common to all OEditBaseModels
+
+ // close the block - write the correct length at the beginning
+ nLen = xMark->offsetToMark(nMark) - sizeof(nLen);
+ xMark->jumpToMark(nMark);
+ _rxOutStream->writeLong(nLen);
+ xMark->jumpToFurthest();
+ xMark->deleteMark(nMark);
+}
+
+
+void OEditBaseModel::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_EMPTY_IS_NULL:
+ rValue <<= m_bEmptyIsNull;
+ break;
+ case PROPERTY_ID_FILTERPROPOSAL:
+ rValue <<= m_bFilterProposal;
+ break;
+ case PROPERTY_ID_DEFAULT_TEXT:
+ rValue <<= m_aDefaultText;
+ break;
+ case PROPERTY_ID_DEFAULT_VALUE:
+ case PROPERTY_ID_DEFAULT_DATE:
+ case PROPERTY_ID_DEFAULT_TIME:
+ rValue = m_aDefault;
+ break;
+ default:
+ OBoundControlModel::getFastPropertyValue(rValue, nHandle);
+ }
+}
+
+sal_Bool OEditBaseModel::convertFastPropertyValue( Any& rConvertedValue, Any& rOldValue,
+ sal_Int32 nHandle, const Any& rValue )
+{
+ bool bModified(false);
+ switch (nHandle)
+ {
+ case PROPERTY_ID_EMPTY_IS_NULL:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bEmptyIsNull);
+ break;
+ case PROPERTY_ID_FILTERPROPOSAL:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bFilterProposal);
+ break;
+ case PROPERTY_ID_DEFAULT_TEXT:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aDefaultText);
+ break;
+ case PROPERTY_ID_DEFAULT_VALUE:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aDefault, cppu::UnoType<double>::get());
+ break;
+ case PROPERTY_ID_DEFAULT_DATE:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aDefault, cppu::UnoType<util::Date>::get());
+ break;
+ case PROPERTY_ID_DEFAULT_TIME:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aDefault, cppu::UnoType<util::Time>::get());
+ break;
+ default:
+ bModified = OBoundControlModel::convertFastPropertyValue(
+ rConvertedValue,
+ rOldValue,
+ nHandle,
+ rValue);
+ }
+ return bModified;
+}
+
+void OEditBaseModel::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_EMPTY_IS_NULL:
+ DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_BOOLEAN, "invalid type" );
+ m_bEmptyIsNull = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_FILTERPROPOSAL:
+ DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_BOOLEAN, "invalid type" );
+ m_bFilterProposal = getBOOL(rValue);
+ break;
+ // Changing the default values causes a reset
+ case PROPERTY_ID_DEFAULT_TEXT:
+ DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_STRING, "invalid type" );
+ rValue >>= m_aDefaultText;
+ resetNoBroadcast();
+ break;
+ case PROPERTY_ID_DEFAULT_VALUE:
+ case PROPERTY_ID_DEFAULT_DATE:
+ case PROPERTY_ID_DEFAULT_TIME:
+ m_aDefault = rValue;
+ resetNoBroadcast();
+ break;
+ default:
+ OBoundControlModel::setFastPropertyValue_NoBroadcast(nHandle, rValue );
+ }
+}
+
+// XPropertyState
+
+Any OEditBaseModel::getPropertyDefaultByHandle( sal_Int32 nHandle ) const
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_DEFAULT_TEXT:
+ return Any(OUString());
+ case PROPERTY_ID_FILTERPROPOSAL:
+ return Any(false);
+ case PROPERTY_ID_DEFAULT_VALUE:
+ case PROPERTY_ID_DEFAULT_DATE:
+ case PROPERTY_ID_DEFAULT_TIME:
+ return Any();
+ default:
+ return OBoundControlModel::getPropertyDefaultByHandle(nHandle);
+ }
+}
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/EditBase.hxx b/forms/source/component/EditBase.hxx
new file mode 100644
index 000000000..c74476bc0
--- /dev/null
+++ b/forms/source/component/EditBase.hxx
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+
+// persistence flags for use with the version id
+#define PF_HANDLE_COMMON_PROPS 0x8000
+ // Derived classes which use their own persistence methods (read/write) and have an own
+ // version handling therein may want to clear this flag in getPersistenceFlags.
+ // If done so, this class will write a version without a call to writeCommonEditProperties.
+#define PF_FAKE_FORMATTED_FIELD 0x4000
+ // ... hmmm... a fake, as the name suggests. see OFormattedFieldWrapper
+
+#define PF_SPECIAL_FLAGS 0xFF00
+
+
+namespace frm
+{
+
+class OEditBaseModel : public OBoundControlModel
+{
+ sal_Int16 m_nLastReadVersion;
+
+protected:
+// [properties] for all EditingFields
+ css::uno::Any m_aDefault;
+ OUString m_aDefaultText; // default value
+ bool m_bEmptyIsNull : 1; // empty string will be interpreted as NULL when committing
+ bool m_bFilterProposal : 1; // use a list of possible value in filtermode
+// [properties]
+
+ sal_Int16 getLastReadVersion() const { return m_nLastReadVersion; }
+
+public:
+ OEditBaseModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory,
+ const OUString& _rUnoControlModelTypeName,
+ const OUString& _rDefault,
+ const bool _bSupportExternalBinding,
+ const bool _bSupportsValidation
+ );
+
+ OEditBaseModel(
+ const OEditBaseModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+
+ virtual ~OEditBaseModel() override;
+
+ // XPersistObject
+ virtual void SAL_CALL write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // XPropertySet
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle ) const override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(css::uno::Any& rConvertedValue, css::uno::Any& rOldValue,
+ sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const css::uno::Any& rValue) override;
+ using ::cppu::OPropertySetHelper::getFastPropertyValue;
+
+ // XPropertyState
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 nHandle ) const override;
+
+protected:
+ // new properties common to all edit models should be handled with the following two methods
+ void readCommonEditProperties(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream);
+ void writeCommonEditProperties(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream);
+ void defaultCommonEditProperties();
+
+ virtual sal_uInt16 getPersistenceFlags() const;
+ // derived classes may use this if they want this base class to write additional version flags
+ // (one of the PF_... constants). After ::read they may ask for that flags with getLastReadVersion
+};
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/EventThread.cxx b/forms/source/component/EventThread.cxx
new file mode 100644
index 000000000..0b38e33f7
--- /dev/null
+++ b/forms/source/component/EventThread.cxx
@@ -0,0 +1,196 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "EventThread.hxx"
+#include <comphelper/guarding.hxx>
+#include <tools/debug.hxx>
+#include <cppuhelper/queryinterface.hxx>
+
+#include <memory>
+
+namespace frm
+{
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::lang;
+
+OComponentEventThread::OComponentEventThread( ::cppu::OComponentHelper* pCompImpl ) :
+ m_xComp( pCompImpl )
+{
+
+ osl_atomic_increment(&m_refCount);
+
+ // and add us at the Control
+ {
+ Reference<XEventListener> xEvtLstnr = static_cast<XEventListener*>(this);
+ m_xComp->addEventListener( xEvtLstnr );
+ }
+
+ osl_atomic_decrement(&m_refCount);
+}
+
+OComponentEventThread::~OComponentEventThread()
+{
+
+ DBG_ASSERT( m_aEvents.empty(),
+ "OComponentEventThread::~OComponentEventThread: Didn't call dispose?" );
+
+ impl_clearEventQueue();
+}
+
+Any SAL_CALL OComponentEventThread::queryInterface(const Type& _rType)
+{
+ Any aReturn = OWeakObject::queryInterface(_rType);
+
+ if (!aReturn.hasValue())
+ aReturn = ::cppu::queryInterface(_rType,
+ static_cast<XEventListener*>(this)
+ );
+
+ return aReturn;
+}
+
+void OComponentEventThread::impl_clearEventQueue()
+{
+ m_aEvents.clear();
+ m_aControls.clear();
+ m_aFlags.clear();
+}
+
+void OComponentEventThread::disposing( const EventObject& evt )
+{
+ if( evt.Source != static_cast<XWeak*>(m_xComp.get()) )
+ return;
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // Remove EventListener
+ Reference<XEventListener> xEvtLstnr = static_cast<XEventListener*>(this);
+ m_xComp->removeEventListener( xEvtLstnr );
+
+ // Clear EventQueue
+ impl_clearEventQueue();
+
+ // Free the Control and set pCompImpl to 0,
+ // so that the thread knows, that it should terminate.
+ m_xComp.clear();
+
+ // Wake up the thread and terminate
+ m_aCond.set();
+ terminate();
+}
+
+void OComponentEventThread::addEvent( std::unique_ptr<EventObject> _pEvt )
+{
+ Reference<XControl> xTmp;
+ addEvent( std::move(_pEvt), xTmp );
+}
+
+void OComponentEventThread::addEvent( std::unique_ptr<EventObject> _pEvt,
+ const Reference<XControl>& rControl,
+ bool bFlag )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // Put data into the queue
+ m_aEvents.push_back( std::move( _pEvt ) );
+
+ Reference<XWeak> xWeakControl(rControl, UNO_QUERY);
+ Reference<XAdapter> xControlAdapter = xWeakControl.is() ? xWeakControl->queryAdapter() : Reference<XAdapter>();
+ m_aControls.push_back( xControlAdapter );
+
+ m_aFlags.push_back( bFlag );
+
+ // Wake up thread
+ m_aCond.set();
+}
+
+void SAL_CALL OComponentEventThread::onTerminated()
+{
+ OComponentEventThread_TBASE::onTerminated();
+
+ release( );
+}
+
+void OComponentEventThread::run()
+{
+ osl_setThreadName("frm::OComponentEventThread");
+
+ acquire( );
+
+ // Hold on to ourselves, so that we're not deleted if a dispose is called at some point in time
+ css::uno::Reference<css::uno::XInterface> xThis(static_cast<XWeak*>(this));
+
+ do
+ {
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ while( !m_aEvents.empty() )
+ {
+ // Get the Control and hold on to it so that it cannot be deleted during actionPerformed
+ rtl::Reference<::cppu::OComponentHelper> xComp = m_xComp;
+
+ ThreadEvents::iterator firstEvent( m_aEvents.begin() );
+ std::unique_ptr<EventObject> pEvt = std::move(*firstEvent);
+ m_aEvents.erase( firstEvent );
+
+ ThreadObjects::iterator firstControl( m_aControls.begin() );
+ Reference<XAdapter> xControlAdapter = *firstControl;
+ m_aControls.erase( firstControl );
+
+ auto firstFlag( m_aFlags.begin() );
+ bool bFlag = *firstFlag;
+ m_aFlags.erase( firstFlag );
+
+ {
+ MutexRelease aReleaseOnce(m_aMutex);
+ // Because a queryHardRef can throw an Exception, it should not be called when
+ // the mutex is locked.
+ Reference<XControl> xControl;
+ if ( xControlAdapter.is() )
+ xControl.set(
+ xControlAdapter->queryAdapted(), css::uno::UNO_QUERY);
+
+ if( xComp.is() )
+ processEvent( xComp.get(), pEvt.get(), xControl, bFlag );
+ }
+ }
+
+ // After a Dispose, we do not know the Control anymore.
+ // Thus, we must not wait either.
+ if( !m_xComp.is() )
+ return;
+
+ // Reset waiting condition
+ m_aCond.reset();
+ {
+ MutexRelease aReleaseOnce(m_aMutex);
+ // And wait ... if, in the meantime, an Event came in after all
+ m_aCond.wait();
+ }
+ }
+ while( true );
+}
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/EventThread.hxx b/forms/source/component/EventThread.hxx
new file mode 100644
index 000000000..20aff7311
--- /dev/null
+++ b/forms/source/component/EventThread.hxx
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <memory>
+#include <vector>
+
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/awt/XControl.hpp>
+#include <osl/thread.hxx>
+#include <osl/conditn.hxx>
+#include <cppuhelper/component.hxx>
+#include <comphelper/uno3.hxx>
+#include <rtl/ref.hxx>
+
+
+using namespace comphelper;
+
+
+namespace frm
+{
+
+
+typedef ::osl::Thread OComponentEventThread_TBASE;
+class OComponentEventThread
+ :public OComponentEventThread_TBASE
+ ,public css::lang::XEventListener
+ ,public ::cppu::OWeakObject
+{
+ typedef std::vector<std::unique_ptr<css::lang::EventObject>> ThreadEvents;
+ typedef std::vector< css::uno::Reference< css::uno::XAdapter> > ThreadObjects;
+
+ ::osl::Mutex m_aMutex;
+ ::osl::Condition m_aCond; // Queue filled?
+ ThreadEvents m_aEvents; // EventQueue
+ ThreadObjects m_aControls; // Control for Submit
+ std::vector<bool> m_aFlags; // Flags for Submit/Reset
+
+ rtl::Reference<::cppu::OComponentHelper> m_xComp; // Implementation of the Control
+
+protected:
+
+ // XThread
+ virtual void SAL_CALL run() override;
+
+ virtual void SAL_CALL onTerminated() override;
+
+ // Edit an Event:
+ // The mutex is not locked, but pCompImpl stays valid in any case.
+ // pEvt can be a derived type, namely the one that cloneEvent returns.
+ // rControl is only set, if a Control has been passed in addEvent.
+ // Because the Control is only held as a WeakRef, it can disappear in the meantime.
+ virtual void processEvent( ::cppu::OComponentHelper* _pCompImpl,
+ const css::lang::EventObject* _pEvt,
+ const css::uno::Reference< css::awt::XControl>& _rControl,
+ bool _bFlag) = 0;
+
+public:
+
+ // UNO binding
+ DECLARE_UNO3_DEFAULTS(OComponentEventThread, OWeakObject)
+ virtual css::uno::Any SAL_CALL queryInterface(const css::uno::Type& _rType) override;
+
+ explicit OComponentEventThread(::cppu::OComponentHelper* pCompImpl);
+ virtual ~OComponentEventThread() override;
+
+ void addEvent( std::unique_ptr<css::lang::EventObject> _pEvt );
+ void addEvent( std::unique_ptr<css::lang::EventObject> _pEvt, const css::uno::Reference< css::awt::XControl>& rControl,
+ bool bFlag = false );
+
+ // css::lang::XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource ) override;
+
+ // Resolve ambiguity: both OWeakObject and osl::Thread have these memory operators
+ using osl::Thread::operator new;
+ using osl::Thread::operator delete;
+
+private:
+ void impl_clearEventQueue();
+};
+
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/File.cxx b/forms/source/component/File.cxx
new file mode 100644
index 000000000..f363d39d7
--- /dev/null
+++ b/forms/source/component/File.cxx
@@ -0,0 +1,275 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "File.hxx"
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+#include <property.hxx>
+#include <services.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+
+using namespace comphelper;
+
+
+namespace frm
+{
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+
+
+Sequence<Type> OFileControlModel::_getTypes()
+{
+ static Sequence<Type> const aTypes =
+ concatSequences(OControlModel::_getTypes(), Sequence<Type>{ cppu::UnoType<XReset>::get() });
+ return aTypes;
+}
+
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> OFileControlModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OControlModel::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_COMPONENT_FILECONTROL;
+ pArray[aSupported.getLength()-1] = FRM_COMPONENT_FILECONTROL;
+ return aSupported;
+}
+
+
+OFileControlModel::OFileControlModel(const Reference<XComponentContext>& _rxFactory)
+ :OControlModel(_rxFactory, VCL_CONTROLMODEL_FILECONTROL)
+ ,m_aResetListeners(m_aMutex)
+{
+ m_nClassId = FormComponentType::FILECONTROL;
+}
+
+
+OFileControlModel::OFileControlModel( const OFileControlModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OControlModel( _pOriginal, _rxFactory )
+ ,m_aResetListeners( m_aMutex )
+{
+
+ m_sDefaultValue = _pOriginal->m_sDefaultValue;
+}
+
+
+OFileControlModel::~OFileControlModel()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+}
+
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OFileControlModel::createClone()
+{
+ rtl::Reference<OFileControlModel> pClone = new OFileControlModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+Any SAL_CALL OFileControlModel::queryAggregation(const Type& _rType)
+{
+ Any aReturn = OControlModel::queryAggregation(_rType);
+ if (!aReturn.hasValue())
+ aReturn = ::cppu::queryInterface(_rType
+ ,static_cast<XReset*>(this)
+ );
+
+ return aReturn;
+}
+
+// OComponentHelper
+
+void OFileControlModel::disposing()
+{
+ OControlModel::disposing();
+
+ EventObject aEvt(static_cast<XWeak*>(this));
+ m_aResetListeners.disposeAndClear(aEvt);
+}
+
+
+Any OFileControlModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+{
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_TEXT:
+ return Any( OUString() );
+ }
+ return OControlModel::getPropertyDefaultByHandle( _nHandle );
+}
+
+
+void OFileControlModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_DEFAULT_TEXT : rValue <<= m_sDefaultValue; break;
+ default:
+ OControlModel::getFastPropertyValue(rValue, nHandle);
+ }
+}
+
+
+void OFileControlModel::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue)
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_DEFAULT_TEXT :
+ DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_STRING, "OFileControlModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ rValue >>= m_sDefaultValue;
+ break;
+ default:
+ OControlModel::setFastPropertyValue_NoBroadcast(nHandle, rValue);
+ }
+}
+
+
+sal_Bool OFileControlModel::convertFastPropertyValue(Any& rConvertedValue, Any& rOldValue, sal_Int32 nHandle, const Any& rValue)
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_DEFAULT_TEXT :
+ return tryPropertyValue(rConvertedValue, rOldValue, rValue, m_sDefaultValue);
+ default:
+ return OControlModel::convertFastPropertyValue(rConvertedValue, rOldValue, nHandle, rValue);
+ }
+}
+
+
+void OFileControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OControlModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 2);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_TEXT, PROPERTY_ID_DEFAULT_TEXT, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+OUString SAL_CALL OFileControlModel::getServiceName()
+{
+ return FRM_COMPONENT_FILECONTROL; // old (non-sun) name for compatibility !
+}
+
+
+void OFileControlModel::write(const Reference<css::io::XObjectOutputStream>& _rxOutStream)
+{
+ OControlModel::write(_rxOutStream);
+
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ // Version
+ _rxOutStream->writeShort(0x0002);
+ // Default value
+ _rxOutStream << m_sDefaultValue;
+ writeHelpTextCompatibly(_rxOutStream);
+}
+
+
+void OFileControlModel::read(const Reference<css::io::XObjectInputStream>& _rxInStream)
+{
+ OControlModel::read(_rxInStream);
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ // Version
+ sal_uInt16 nVersion = _rxInStream->readShort();
+ // Default value
+ switch (nVersion)
+ {
+ case 1:
+ _rxInStream >> m_sDefaultValue; break;
+ case 2:
+ _rxInStream >> m_sDefaultValue;
+ readHelpTextCompatibly(_rxInStream);
+ break;
+ default:
+ OSL_FAIL("OFileControlModel::read : unknown version !");
+ m_sDefaultValue.clear();
+ }
+
+ // Display default values after read
+// _reset();
+}
+
+
+void SAL_CALL OFileControlModel::reset()
+{
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aResetListeners);
+ EventObject aEvt(static_cast<XWeak*>(this));
+ bool bContinue = true;
+ while (aIter.hasMoreElements() && bContinue)
+ bContinue = aIter.next()->approveReset(aEvt);
+
+ if (bContinue)
+ {
+ // don't lock our mutex as setting aggregate properties
+ // may cause any uno controls belonging to us to lock the solar mutex, which is potentially dangerous with
+ // our own mutex locked
+ m_xAggregateSet->setPropertyValue(PROPERTY_TEXT, Any(m_sDefaultValue));
+ m_aResetListeners.notifyEach( &XResetListener::resetted, aEvt );
+ }
+}
+
+
+void OFileControlModel::addResetListener(const Reference<XResetListener>& _rxListener)
+{
+ m_aResetListeners.addInterface(_rxListener);
+}
+
+
+void OFileControlModel::removeResetListener(const Reference<XResetListener>& _rxListener)
+{
+ m_aResetListeners.removeInterface(_rxListener);
+}
+
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OFileControlModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OFileControlModel(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/File.hxx b/forms/source/component/File.hxx
new file mode 100644
index 000000000..6ef7532aa
--- /dev/null
+++ b/forms/source/component/File.hxx
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+
+
+namespace frm
+{
+
+class OFileControlModel
+ :public OControlModel
+ ,public css::form::XReset
+{
+ ::comphelper::OInterfaceContainerHelper3<css::form::XResetListener> m_aResetListeners;
+ OUString m_sDefaultValue;
+
+protected:
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ OFileControlModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OFileControlModel(
+ const OFileControlModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OFileControlModel() override;
+
+ DECLARE_UNO3_AGG_DEFAULTS(OFileControlModel, OControlModel)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OFileControlModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // XPropertySet and friends
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle) const override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const css::uno::Any& rValue) override;
+
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(css::uno::Any& rConvertedValue, css::uno::Any& rOldValue, sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 _nHandle ) const override;
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // XReset
+ virtual void SAL_CALL reset() override;
+ virtual void SAL_CALL addResetListener(const css::uno::Reference< css::form::XResetListener>& _rxListener) override;
+ virtual void SAL_CALL removeResetListener(const css::uno::Reference< css::form::XResetListener>& _rxListener) override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+ // prevent method hiding
+ using OControlModel::disposing;
+ using OControlModel::getFastPropertyValue;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Filter.cxx b/forms/source/component/Filter.cxx
new file mode 100644
index 000000000..a2a2ec5ca
--- /dev/null
+++ b/forms/source/component/Filter.cxx
@@ -0,0 +1,890 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <config_features.h>
+#include <config_fuzzers.h>
+
+#include "Filter.hxx"
+#include <strings.hrc>
+#include <frm_resource.hxx>
+#include <frm_strings.hxx>
+
+#include <com/sun/star/awt/VclWindowPeerAttribute.hpp>
+#include <com/sun/star/awt/XCheckBox.hpp>
+#include <com/sun/star/awt/XComboBox.hpp>
+#include <com/sun/star/awt/XListBox.hpp>
+#include <com/sun/star/awt/XRadioButton.hpp>
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/sdb/ErrorMessageDialog.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/util/NumberFormatter.hpp>
+#include <com/sun/star/awt/XItemList.hpp>
+
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <connectivity/dbtools.hxx>
+#include <connectivity/formattedcolumnvalue.hxx>
+#include <connectivity/predicateinput.hxx>
+#include <o3tl/safeint.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/gen.hxx>
+
+
+namespace frm
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::sdb;
+ using namespace ::com::sun::star::sdbc;
+ using namespace ::com::sun::star::sdbcx;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::ui::dialogs;
+
+ using namespace ::connectivity;
+
+ OFilterControl::OFilterControl( const Reference< XComponentContext >& _rxORB )
+ :m_aTextListeners( *this )
+ ,m_xContext( _rxORB )
+ ,m_nControlClass( FormComponentType::TEXTFIELD )
+ ,m_bFilterList( false )
+ ,m_bMultiLine( false )
+ ,m_bFilterListFilled( false )
+ {
+ }
+
+
+ bool OFilterControl::ensureInitialized( )
+ {
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ if ( !m_xField.is() )
+ {
+ OSL_FAIL( "OFilterControl::ensureInitialized: improperly initialized: no field!" );
+ return false;
+ }
+
+ if ( !m_xConnection.is() )
+ {
+ OSL_FAIL( "OFilterControl::ensureInitialized: improperly initialized: no connection!" );
+ return false;
+ }
+
+ if ( !m_xFormatter.is() )
+ {
+ // we can create one from the connection, if it's an SDB connection
+
+ Reference< XNumberFormatsSupplier > xFormatSupplier = ::dbtools::getNumberFormats( m_xConnection, true, m_xContext );
+
+ if ( xFormatSupplier.is() )
+ {
+ m_xFormatter.set(NumberFormatter::create(m_xContext), UNO_QUERY_THROW );
+ m_xFormatter->attachNumberFormatsSupplier( xFormatSupplier );
+ }
+ }
+ if ( !m_xFormatter.is() )
+ {
+ OSL_FAIL( "OFilterControl::ensureInitialized: no number formatter!" );
+ // no fallback anymore
+ return false;
+ }
+#endif
+ return true;
+ }
+
+
+ Any SAL_CALL OFilterControl::queryAggregation( const Type & rType )
+ {
+ Any aRet = UnoControl::queryAggregation( rType);
+ if(!aRet.hasValue())
+ aRet = OFilterControl_BASE::queryInterface(rType);
+
+ return aRet;
+ }
+
+
+ OUString OFilterControl::GetComponentServiceName() const
+ {
+ OUString aServiceName;
+ switch (m_nControlClass)
+ {
+ case FormComponentType::RADIOBUTTON:
+ aServiceName = "radiobutton";
+ break;
+ case FormComponentType::CHECKBOX:
+ aServiceName = "checkbox";
+ break;
+ case FormComponentType::COMBOBOX:
+ aServiceName = "combobox";
+ break;
+ case FormComponentType::LISTBOX:
+ aServiceName = "listbox";
+ break;
+ default:
+ if (m_bMultiLine)
+ aServiceName = "MultiLineEdit";
+ else
+ aServiceName = "Edit";
+ }
+ return aServiceName;
+ }
+
+ // XComponent
+
+ void OFilterControl::dispose()
+ {
+ EventObject aEvt(*this);
+ m_aTextListeners.disposeAndClear( aEvt );
+ UnoControl::dispose();
+ }
+
+
+ void OFilterControl::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer )
+ {
+ UnoControl::createPeer( rxToolkit, rParentPeer );
+
+ try
+ {
+ Reference< XVclWindowPeer > xVclWindow( getPeer(), UNO_QUERY_THROW );
+ switch ( m_nControlClass )
+ {
+ case FormComponentType::CHECKBOX:
+ {
+ // checkboxes always have a tristate-mode
+ xVclWindow->setProperty( PROPERTY_TRISTATE, Any( true ) );
+ xVclWindow->setProperty( PROPERTY_STATE, Any( sal_Int32( TRISTATE_INDET ) ) );
+
+ Reference< XCheckBox > xBox( getPeer(), UNO_QUERY_THROW );
+ xBox->addItemListener( this );
+
+ }
+ break;
+
+ case FormComponentType::RADIOBUTTON:
+ {
+ xVclWindow->setProperty( PROPERTY_STATE, Any( sal_Int32( TRISTATE_FALSE ) ) );
+
+ Reference< XRadioButton > xRadio( getPeer(), UNO_QUERY_THROW );
+ xRadio->addItemListener( this );
+ }
+ break;
+
+ case FormComponentType::LISTBOX:
+ {
+ Reference< XListBox > xListBox( getPeer(), UNO_QUERY_THROW );
+ xListBox->addItemListener( this );
+ [[fallthrough]];
+ }
+
+ case FormComponentType::COMBOBOX:
+ {
+ xVclWindow->setProperty(PROPERTY_AUTOCOMPLETE, Any( true ) );
+ [[fallthrough]];
+ }
+
+ default:
+ {
+ Reference< XWindow > xWindow( getPeer(), UNO_QUERY );
+ xWindow->addFocusListener( this );
+
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ xText->setMaxTextLen(0);
+ }
+ break;
+ }
+
+ // filter controls are _never_ readonly
+ Reference< XPropertySet > xModel( getModel(), UNO_QUERY_THROW );
+ Reference< XPropertySetInfo > xModelPSI( xModel->getPropertySetInfo(), UNO_SET_THROW );
+ if ( xModelPSI->hasPropertyByName( PROPERTY_READONLY ) )
+ xVclWindow->setProperty( PROPERTY_READONLY, Any( false ) );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ if (m_bFilterList)
+ m_bFilterListFilled = false;
+ }
+
+
+ void OFilterControl::PrepareWindowDescriptor( WindowDescriptor& rDescr )
+ {
+ if (m_bFilterList)
+ rDescr.WindowAttributes |= VclWindowPeerAttribute::DROPDOWN;
+ }
+
+
+ void OFilterControl::ImplSetPeerProperty( const OUString& rPropName, const Any& rVal )
+ {
+ // these properties are ignored
+ if (rPropName == PROPERTY_TEXT ||
+ rPropName == PROPERTY_STATE)
+ return;
+
+ UnoControl::ImplSetPeerProperty( rPropName, rVal );
+ }
+
+ // XEventListener
+
+ void SAL_CALL OFilterControl::disposing(const EventObject& Source)
+ {
+ UnoControl::disposing(Source);
+ }
+
+ // XItemListener
+
+ void SAL_CALL OFilterControl::itemStateChanged( const ItemEvent& rEvent )
+ {
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) rEvent;
+#else
+ OUStringBuffer aText;
+ switch (m_nControlClass)
+ {
+ case FormComponentType::CHECKBOX:
+ {
+ if ( ( rEvent.Selected == TRISTATE_TRUE ) || ( rEvent.Selected == TRISTATE_FALSE ) )
+ {
+ sal_Int32 nBooleanComparisonMode = ::dbtools::DatabaseMetaData( m_xConnection ).getBooleanComparisonMode();
+
+ bool bSelected = ( rEvent.Selected == TRISTATE_TRUE );
+
+ OUString sExpressionMarker( "$expression$" );
+ ::dbtools::getBooleanComparisonPredicate(
+ sExpressionMarker,
+ bSelected,
+ nBooleanComparisonMode,
+ aText
+ );
+
+ OUString sText( aText.makeStringAndClear() );
+ sal_Int32 nMarkerPos( sText.indexOf( sExpressionMarker ) );
+ OSL_ENSURE( nMarkerPos == 0, "OFilterControl::itemStateChanged: unsupported boolean comparison mode!" );
+ // If this assertion fails, then getBooleanComparisonPredicate created a predicate which
+ // does not start with the expression we gave it. The only known case is when
+ // the comparison mode is ACCESS_COMPAT, and the value is TRUE. In this case,
+ // the expression is rather complex.
+ // Well, so this is a known issue - the filter controls (and thus the form based filter)
+ // do not work with boolean MS Access fields.
+ // To fix this, we would probably have to revert here to always return "1" or "0" as normalized
+ // filter, and change our client code to properly translate this (which could be some effort).
+ if ( nMarkerPos == 0 )
+ aText.append( sText.subView(sExpressionMarker.getLength()) );
+ else
+ {
+ // fallback
+ aText.appendAscii( bSelected ? "1" : "0" );
+ }
+ }
+ }
+ break;
+
+ case FormComponentType::LISTBOX:
+ {
+ try
+ {
+ const Reference< XItemList > xItemList( getModel(), UNO_QUERY_THROW );
+ OUString sItemText( xItemList->getItemText( rEvent.Selected ) );
+
+ const MapString2String::const_iterator itemPos = m_aDisplayItemToValueItem.find( sItemText );
+ if ( itemPos != m_aDisplayItemToValueItem.end() )
+ {
+ sItemText = itemPos->second;
+ if ( !sItemText.isEmpty() )
+ {
+ ::dbtools::OPredicateInputController aPredicateInput( m_xContext, m_xConnection, getParseContext() );
+ OUString sErrorMessage;
+ OSL_VERIFY( aPredicateInput.normalizePredicateString( sItemText, m_xField, &sErrorMessage ) );
+ }
+ }
+ aText.append( sItemText );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+ break;
+
+ case FormComponentType::RADIOBUTTON:
+ {
+ if ( rEvent.Selected == TRISTATE_TRUE )
+ aText.append( ::comphelper::getString( Reference< XPropertySet >( getModel(), UNO_QUERY_THROW )->getPropertyValue( PROPERTY_REFVALUE ) ) );
+ }
+ break;
+ }
+
+ OUString sText( aText.makeStringAndClear() );
+ if ( m_aText != sText )
+ {
+ m_aText = sText;
+ TextEvent aEvt;
+ aEvt.Source = *this;
+ ::comphelper::OInterfaceIteratorHelper3 aIt(m_aTextListeners);
+ while( aIt.hasMoreElements() )
+ aIt.next()->textChanged(aEvt);
+ }
+#endif
+ }
+
+
+ void OFilterControl::implInitFilterList()
+ {
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ if ( !ensureInitialized( ) )
+ // already asserted in ensureInitialized
+ return;
+
+ // ensure the cursor and the statement are disposed as soon as we leave
+ ::utl::SharedUNOComponent< XResultSet > xListCursor;
+ ::utl::SharedUNOComponent< XStatement > xStatement;
+
+ try
+ {
+ m_bFilterListFilled = true;
+
+ if ( !m_xField.is() )
+ return;
+
+ OUString sFieldName;
+ m_xField->getPropertyValue( PROPERTY_NAME ) >>= sFieldName;
+
+ // here we need a table to which the field belongs to
+ const Reference< XChild > xModelAsChild( getModel(), UNO_QUERY_THROW );
+ const Reference< XRowSet > xForm( xModelAsChild->getParent(), UNO_QUERY_THROW );
+ const Reference< XPropertySet > xFormProps( xForm, UNO_QUERY_THROW );
+
+ // create a query composer
+ Reference< XColumnsSupplier > xSuppColumns;
+ xFormProps->getPropertyValue("SingleSelectQueryComposer") >>= xSuppColumns;
+
+ const Reference< XConnection > xConnection( ::dbtools::getConnection( xForm ), UNO_SET_THROW );
+ const Reference< XNameAccess > xFieldNames( xSuppColumns->getColumns(), UNO_SET_THROW );
+ if ( !xFieldNames->hasByName( sFieldName ) )
+ return;
+ OUString sRealFieldName, sTableName;
+ const Reference< XPropertySet > xComposerFieldProps( xFieldNames->getByName( sFieldName ), UNO_QUERY_THROW );
+ xComposerFieldProps->getPropertyValue( PROPERTY_REALNAME ) >>= sRealFieldName;
+ xComposerFieldProps->getPropertyValue( PROPERTY_TABLENAME ) >>= sTableName;
+
+ // obtain the table of the field
+ const Reference< XTablesSupplier > xSuppTables( xSuppColumns, UNO_QUERY_THROW );
+ const Reference< XNameAccess > xTablesNames( xSuppTables->getTables(), UNO_SET_THROW );
+ const Reference< XNamed > xNamedTable( xTablesNames->getByName( sTableName ), UNO_QUERY_THROW );
+ sTableName = xNamedTable->getName();
+
+ // create a statement selecting all values for the given field
+ OUStringBuffer aStatement;
+
+ const Reference< XDatabaseMetaData > xMeta( xConnection->getMetaData(), UNO_SET_THROW );
+ const OUString sQuoteChar = xMeta->getIdentifierQuoteString();
+
+ aStatement.append( "SELECT DISTINCT " );
+ aStatement.append( sQuoteChar );
+ aStatement.append( sRealFieldName );
+ aStatement.append( sQuoteChar );
+
+ // if the field had an alias in our form's statement, give it this alias in the new statement, too
+ if ( !sFieldName.isEmpty() && ( sFieldName != sRealFieldName ) )
+ {
+ aStatement.append(" AS ");
+ aStatement.append( sQuoteChar );
+ aStatement.append( sFieldName );
+ aStatement.append( sQuoteChar );
+ }
+
+ aStatement.append( " FROM " );
+
+ OUString sCatalog, sSchema, sTable;
+ ::dbtools::qualifiedNameComponents( xMeta, sTableName, sCatalog, sSchema, sTable, ::dbtools::EComposeRule::InDataManipulation );
+ aStatement.append( ::dbtools::composeTableNameForSelect( xConnection, sCatalog, sSchema, sTable ) );
+
+ // execute the statement
+ xStatement.reset( xConnection->createStatement() );
+ const OUString sSelectStatement( aStatement.makeStringAndClear( ) );
+ xListCursor.reset( xStatement->executeQuery( sSelectStatement ) );
+
+ // retrieve the one column which we take the values from
+ const Reference< XColumnsSupplier > xSupplyCols( xListCursor, UNO_QUERY_THROW );
+ const Reference< XIndexAccess > xFields( xSupplyCols->getColumns(), UNO_QUERY_THROW );
+ const Reference< XPropertySet > xDataField( xFields->getByIndex(0), UNO_QUERY_THROW );
+
+ // ensure the values will be formatted according to the field format
+ const ::dbtools::FormattedColumnValue aFormatter( m_xFormatter, xDataField );
+
+ ::std::vector< OUString > aProposals;
+ aProposals.reserve(16);
+
+ while ( xListCursor->next() && ( aProposals.size() < o3tl::make_unsigned( SHRT_MAX ) ) )
+ {
+ const OUString sCurrentValue = aFormatter.getFormattedValue();
+ aProposals.push_back( sCurrentValue );
+ }
+
+ // fill the list items into our peer
+ Sequence< OUString> aStringSeq( comphelper::containerToSequence(aProposals) );
+
+ const Reference< XComboBox > xComboBox( getPeer(), UNO_QUERY_THROW );
+ xComboBox->addItems( aStringSeq, 0 );
+
+ // set the drop down line count to something reasonable
+ const sal_Int16 nLineCount = ::std::min( sal_Int16( 16 ), sal_Int16( aStringSeq.getLength() ) );
+ xComboBox->setDropDownLineCount( nLineCount );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+#endif
+ }
+
+ // XFocusListener
+
+ void SAL_CALL OFilterControl::focusGained(const FocusEvent& /*e*/)
+ {
+ // should we fill the combobox?
+ if (m_bFilterList && !m_bFilterListFilled)
+ implInitFilterList();
+ }
+
+
+ void SAL_CALL OFilterControl::focusLost(const FocusEvent& /*e*/)
+ {
+ }
+
+
+ sal_Bool SAL_CALL OFilterControl::commit()
+ {
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ if ( !ensureInitialized( ) )
+ // already asserted in ensureInitialized
+ return true;
+
+ OUString aText;
+ switch (m_nControlClass)
+ {
+ case FormComponentType::TEXTFIELD:
+ case FormComponentType::COMBOBOX:
+ {
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ aText = xText->getText();
+ } break;
+ default:
+ return true;
+ }
+ if ( m_aText == aText )
+ return true;
+ // check the text with the SQL-Parser
+ OUString aNewText = aText.trim();
+ if ( !aNewText.isEmpty() )
+ {
+ ::dbtools::OPredicateInputController aPredicateInput( m_xContext, m_xConnection, getParseContext() );
+ OUString sErrorMessage;
+ if ( !aPredicateInput.normalizePredicateString( aNewText, m_xField, &sErrorMessage ) )
+ {
+ // display the error and outta here
+ SQLContext aError;
+ aError.Message = ResourceManager::loadString(RID_STR_SYNTAXERROR);
+ aError.Details = sErrorMessage;
+ displayException( aError );
+ return false;
+ }
+ }
+
+ setText(aNewText);
+ TextEvent aEvt;
+ aEvt.Source = *this;
+ ::comphelper::OInterfaceIteratorHelper3 aIt(m_aTextListeners);
+ while( aIt.hasMoreElements() )
+ aIt.next()->textChanged(aEvt);
+#endif
+ return true;
+ }
+
+ // XTextComponent
+
+ void SAL_CALL OFilterControl::addTextListener(const Reference< XTextListener > & l)
+ {
+ m_aTextListeners.addInterface( l );
+ }
+
+
+ void SAL_CALL OFilterControl::removeTextListener(const Reference< XTextListener > & l)
+ {
+ m_aTextListeners.removeInterface( l );
+ }
+
+
+ void SAL_CALL OFilterControl::setText( const OUString& aText )
+ {
+ if ( !ensureInitialized( ) )
+ // already asserted in ensureInitialized
+ return;
+
+ switch (m_nControlClass)
+ {
+ case FormComponentType::CHECKBOX:
+ {
+ Reference< XVclWindowPeer > xVclWindow( getPeer(), UNO_QUERY );
+ if (xVclWindow.is())
+ {
+ Any aValue;
+ if ( aText == "1"
+ || aText.equalsIgnoreAsciiCase("TRUE")
+ || aText.equalsIgnoreAsciiCase("IS TRUE")
+ )
+ {
+ aValue <<= sal_Int32(TRISTATE_TRUE);
+ }
+ else if ( aText == "0" || aText.equalsIgnoreAsciiCase("FALSE") )
+ {
+ aValue <<= sal_Int32(TRISTATE_FALSE);
+ }
+ else
+ aValue <<= sal_Int32(TRISTATE_INDET);
+
+ m_aText = aText;
+ xVclWindow->setProperty( PROPERTY_STATE, aValue );
+ }
+ } break;
+ case FormComponentType::RADIOBUTTON:
+ {
+ Reference< XVclWindowPeer > xVclWindow( getPeer(), UNO_QUERY );
+ if (xVclWindow.is())
+ {
+ OUString aRefText = ::comphelper::getString(css::uno::Reference< XPropertySet > (getModel(), UNO_QUERY_THROW)->getPropertyValue(PROPERTY_REFVALUE));
+ Any aValue;
+ if (aText == aRefText)
+ aValue <<= sal_Int32(TRISTATE_TRUE);
+ else
+ aValue <<= sal_Int32(TRISTATE_FALSE);
+ m_aText = aText;
+ xVclWindow->setProperty(PROPERTY_STATE, aValue);
+ }
+ } break;
+ case FormComponentType::LISTBOX:
+ {
+ Reference< XListBox > xListBox( getPeer(), UNO_QUERY );
+ if (xListBox.is())
+ {
+ m_aText = aText;
+ MapString2String::const_iterator itemPos = m_aDisplayItemToValueItem.find( m_aText );
+ if ( itemPos == m_aDisplayItemToValueItem.end() )
+ {
+ const bool isQuoted = ( m_aText.getLength() > 1 )
+ && ( m_aText[0] == '\'' )
+ && ( m_aText[ m_aText.getLength() - 1 ] == '\'' );
+ if ( isQuoted )
+ {
+ m_aText = m_aText.copy( 1, m_aText.getLength() - 2 );
+ itemPos = m_aDisplayItemToValueItem.find( m_aText );
+ }
+ }
+
+ OSL_ENSURE( ( itemPos != m_aDisplayItemToValueItem.end() ) || m_aText.isEmpty(),
+ "OFilterControl::setText: this text is not in my display list!" );
+ if ( itemPos == m_aDisplayItemToValueItem.end() )
+ m_aText.clear();
+
+ if ( m_aText.isEmpty() )
+ {
+ while ( xListBox->getSelectedItemPos() >= 0 )
+ {
+ xListBox->selectItemPos( xListBox->getSelectedItemPos(), false );
+ }
+ }
+ else
+ {
+ xListBox->selectItem( m_aText, true );
+ }
+ }
+ }
+ break;
+
+ default:
+ {
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ {
+ m_aText = aText;
+ xText->setText(aText);
+ }
+ }
+ }
+ }
+
+
+ void SAL_CALL OFilterControl::insertText( const css::awt::Selection& rSel, const OUString& aText )
+ {
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ {
+ xText->insertText(rSel, aText);
+ m_aText = xText->getText();
+ }
+ }
+
+
+ OUString SAL_CALL OFilterControl::getText()
+ {
+ return m_aText;
+ }
+
+
+ OUString SAL_CALL OFilterControl::getSelectedText()
+ {
+ OUString aSelected;
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ aSelected = xText->getSelectedText();
+
+ return aSelected;
+ }
+
+
+ void SAL_CALL OFilterControl::setSelection( const css::awt::Selection& aSelection )
+ {
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ xText->setSelection( aSelection );
+ }
+
+
+ css::awt::Selection SAL_CALL OFilterControl::getSelection()
+ {
+ css::awt::Selection aSel;
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ aSel = xText->getSelection();
+ return aSel;
+ }
+
+
+ sal_Bool SAL_CALL OFilterControl::isEditable()
+ {
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ return xText.is() && xText->isEditable();
+ }
+
+
+ void SAL_CALL OFilterControl::setEditable( sal_Bool bEditable )
+ {
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ xText->setEditable(bEditable);
+ }
+
+
+ sal_Int16 SAL_CALL OFilterControl::getMaxTextLen()
+ {
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ return xText.is() ? xText->getMaxTextLen() : 0;
+ }
+
+
+ void SAL_CALL OFilterControl::setMaxTextLen( sal_Int16 nLength )
+ {
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ xText->setMaxTextLen(nLength);
+ }
+
+
+ void OFilterControl::displayException( const css::sdb::SQLContext& _rExcept )
+ {
+ try
+ {
+ Reference< XExecutableDialog > xErrorDialog = ErrorMessageDialog::create( m_xContext, "", m_xMessageParent, Any(_rExcept));
+ xErrorDialog->execute();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+
+
+ void SAL_CALL OFilterControl::initialize( const Sequence< Any >& aArguments )
+ {
+ const Any* pArguments = aArguments.getConstArray();
+ const Any* pArgumentsEnd = pArguments + aArguments.getLength();
+
+ PropertyValue aProp;
+ NamedValue aValue;
+ const OUString* pName = nullptr;
+ const Any* pValue = nullptr;
+ Reference< XPropertySet > xControlModel;
+
+ if (aArguments.getLength() == 3
+ && (aArguments[0] >>= m_xMessageParent)
+ && (aArguments[1] >>= m_xFormatter)
+ && (aArguments[2] >>= xControlModel))
+ {
+ initControlModel(xControlModel);
+ }
+ else for ( ; pArguments != pArgumentsEnd; ++pArguments )
+ {
+ // we recognize PropertyValues and NamedValues
+ if ( *pArguments >>= aProp )
+ {
+ pName = &aProp.Name;
+ pValue = &aProp.Value;
+ }
+ else if ( *pArguments >>= aValue )
+ {
+ pName = &aValue.Name;
+ pValue = &aValue.Value;
+ }
+ else
+ {
+ OSL_FAIL( "OFilterControl::initialize: unrecognized argument!" );
+ continue;
+ }
+
+ if ( *pName == "MessageParent" )
+ {
+ // the message parent
+ *pValue >>= m_xMessageParent;
+ OSL_ENSURE( m_xMessageParent.is(), "OFilterControl::initialize: invalid MessageParent!" );
+ }
+ else if ( *pName == "NumberFormatter" )
+ {
+ // the number format. This argument is optional.
+ *pValue >>= m_xFormatter;
+ OSL_ENSURE( m_xFormatter.is(), "OFilterControl::initialize: invalid NumberFormatter!" );
+ }
+ else if ( *pName == "ControlModel" )
+ {
+ // the control model for which we act as filter control
+ if ( !(*pValue >>= xControlModel ) )
+ {
+ OSL_FAIL( "OFilterControl::initialize: invalid control model argument!" );
+ continue;
+ }
+ initControlModel(xControlModel);
+ }
+ }
+ }
+
+ void OFilterControl::initControlModel(Reference< XPropertySet > const & xControlModel)
+ {
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) xControlModel;
+#else
+ if ( !xControlModel.is() )
+ {
+ OSL_FAIL( "OFilterControl::initialize: invalid control model argument!" );
+ return;
+ }
+ // some properties which are "derived" from the control model we're working for
+
+ // the field
+ m_xField.clear();
+ OSL_ENSURE( ::comphelper::hasProperty( PROPERTY_BOUNDFIELD, xControlModel ), "OFilterControl::initialize: control model needs a bound field property!" );
+ xControlModel->getPropertyValue( PROPERTY_BOUNDFIELD ) >>= m_xField;
+
+
+ // filter list and control class
+ m_bFilterList = ::comphelper::hasProperty( PROPERTY_FILTERPROPOSAL, xControlModel ) && ::comphelper::getBOOL( xControlModel->getPropertyValue( PROPERTY_FILTERPROPOSAL ) );
+ if ( m_bFilterList )
+ m_nControlClass = FormComponentType::COMBOBOX;
+ else
+ {
+ sal_Int16 nClassId = ::comphelper::getINT16( xControlModel->getPropertyValue( PROPERTY_CLASSID ) );
+ switch (nClassId)
+ {
+ case FormComponentType::CHECKBOX:
+ case FormComponentType::RADIOBUTTON:
+ case FormComponentType::LISTBOX:
+ case FormComponentType::COMBOBOX:
+ m_nControlClass = nClassId;
+ if ( FormComponentType::LISTBOX == nClassId )
+ {
+ Sequence< OUString > aDisplayItems;
+ OSL_VERIFY( xControlModel->getPropertyValue( PROPERTY_STRINGITEMLIST ) >>= aDisplayItems );
+ Sequence< OUString > aValueItems;
+ OSL_VERIFY( xControlModel->getPropertyValue( PROPERTY_VALUE_SEQ ) >>= aValueItems );
+ OSL_ENSURE( aDisplayItems.getLength() == aValueItems.getLength(), "OFilterControl::initialize: inconsistent item lists!" );
+ for ( sal_Int32 i=0; i < ::std::min( aDisplayItems.getLength(), aValueItems.getLength() ); ++i )
+ m_aDisplayItemToValueItem[ aDisplayItems[i] ] = aValueItems[i];
+ }
+ break;
+ default:
+ m_bMultiLine = ::comphelper::hasProperty( PROPERTY_MULTILINE, xControlModel ) && ::comphelper::getBOOL( xControlModel->getPropertyValue( PROPERTY_MULTILINE ) );
+ m_nControlClass = FormComponentType::TEXTFIELD;
+ break;
+ }
+ }
+
+
+ // the connection meta data for the form which we're working for
+ Reference< XChild > xModel( xControlModel, UNO_QUERY );
+ Reference< XRowSet > xForm;
+ if ( xModel.is() )
+ xForm.set(xModel->getParent(), css::uno::UNO_QUERY);
+ m_xConnection = ::dbtools::getConnection( xForm );
+ OSL_ENSURE( m_xConnection.is(), "OFilterControl::initialize: unable to determine the form's connection!" );
+#endif
+ }
+
+ OUString SAL_CALL OFilterControl::getImplementationName( )
+ {
+ return "com.sun.star.comp.forms.OFilterControl";
+ }
+
+ sal_Bool SAL_CALL OFilterControl::supportsService( const OUString& ServiceName )
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ Sequence< OUString > SAL_CALL OFilterControl::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.form.control.FilterControl",
+ "com.sun.star.awt.UnoControl" };
+ }
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_forms_OFilterControl_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OFilterControl(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Filter.hxx b/forms/source/component/Filter.hxx
new file mode 100644
index 000000000..8815f5f11
--- /dev/null
+++ b/forms/source/component/Filter.hxx
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/form/XBoundComponent.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/XNumberFormatter.hpp>
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/sdb/SQLContext.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <toolkit/controls/unocontrol.hxx>
+
+#include <toolkit/helper/listenermultiplexer.hxx>
+#include <cppuhelper/implbase5.hxx>
+#include <comphelper/uno3.hxx>
+#include <svx/ParseContext.hxx>
+
+#include <unordered_map>
+
+
+namespace frm
+{
+
+
+ // OFilterControl
+
+ typedef ::cppu::ImplHelper5 < css::awt::XTextComponent
+ , css::awt::XFocusListener
+ , css::awt::XItemListener
+ , css::form::XBoundComponent
+ , css::lang::XInitialization
+ > OFilterControl_BASE;
+
+ class OFilterControl final :public UnoControl
+ ,public OFilterControl_BASE
+ ,public ::svxform::OParseContextClient
+ {
+ TextListenerMultiplexer m_aTextListeners;
+
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::beans::XPropertySet > m_xField;
+ css::uno::Reference< css::util::XNumberFormatter > m_xFormatter;
+ css::uno::Reference< css::sdbc::XConnection > m_xConnection;
+ css::uno::Reference< css::awt::XWindow > m_xMessageParent;
+
+ typedef std::unordered_map< OUString, OUString > MapString2String;
+ MapString2String m_aDisplayItemToValueItem;
+
+ OUString m_aText;
+ sal_Int16 m_nControlClass; // which kind of control do we use?
+ bool m_bFilterList : 1;
+ bool m_bMultiLine : 1;
+ bool m_bFilterListFilled : 1;
+
+ void implInitFilterList();
+ void initControlModel(css::uno::Reference< css::beans::XPropertySet > const & xControlModel);
+
+ public:
+ explicit OFilterControl( const css::uno::Reference< css::uno::XComponentContext >& _rxORB );
+
+ DECLARE_UNO3_AGG_DEFAULTS(OFilterControl,OWeakAggObject)
+ css::uno::Any SAL_CALL queryAggregation( const css::uno::Type & rType ) override;
+
+ virtual OUString GetComponentServiceName() const override;
+ virtual void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit > & rxToolkit, const css::uno::Reference< css::awt::XWindowPeer > & rParentPeer ) override;
+
+ // css::lang::XComponent
+ virtual void SAL_CALL dispose() override;
+
+ // css::awt::XTextComponent
+ virtual void SAL_CALL addTextListener( const css::uno::Reference< css::awt::XTextListener > & l ) override;
+ virtual void SAL_CALL removeTextListener( const css::uno::Reference< css::awt::XTextListener > & l ) override;
+ virtual void SAL_CALL setText( const OUString& aText ) override;
+ virtual void SAL_CALL insertText( const css::awt::Selection& rSel, const OUString& aText ) override;
+ virtual OUString SAL_CALL getText() override;
+ virtual OUString SAL_CALL getSelectedText() override;
+ virtual void SAL_CALL setSelection( const css::awt::Selection& aSelection ) override;
+ virtual css::awt::Selection SAL_CALL getSelection() override;
+ virtual sal_Bool SAL_CALL isEditable() override;
+ virtual void SAL_CALL setEditable( sal_Bool bEditable ) override;
+ virtual void SAL_CALL setMaxTextLen( sal_Int16 nLength ) override;
+ virtual sal_Int16 SAL_CALL getMaxTextLen() override;
+
+ // css::form::XBoundComponent
+ virtual void SAL_CALL addUpdateListener(const css::uno::Reference< css::form::XUpdateListener > & /*l*/) override {}
+ virtual void SAL_CALL removeUpdateListener(const css::uno::Reference< css::form::XUpdateListener > & /*l*/) override {}
+ virtual sal_Bool SAL_CALL commit() override;
+
+ // css::lang::XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+
+ // css::awt::XFocusListener
+ virtual void SAL_CALL focusGained(const css::awt::FocusEvent& e) override;
+ virtual void SAL_CALL focusLost(const css::awt::FocusEvent& e) override;
+
+ // css::awt::XItemListener
+ virtual void SAL_CALL itemStateChanged(const css::awt::ItemEvent& rEvent) override;
+
+ // css::util::XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ private:
+ virtual void PrepareWindowDescriptor( css::awt::WindowDescriptor& rDesc ) override;
+ virtual void ImplSetPeerProperty( const OUString& rPropName, const css::uno::Any& rVal ) override;
+
+ bool ensureInitialized( );
+
+ void displayException( const css::sdb::SQLContext& _rExcept );
+ };
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FixedText.cxx b/forms/source/component/FixedText.cxx
new file mode 100644
index 000000000..e68b09fed
--- /dev/null
+++ b/forms/source/component/FixedText.cxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "FixedText.hxx"
+#include <frm_strings.hxx>
+#include <services.hxx>
+
+#include <com/sun/star/form/FormComponentType.hpp>
+
+#include <comphelper/property.hxx>
+
+
+namespace frm
+{
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+using namespace comphelper;
+
+OFixedTextModel::OFixedTextModel( const Reference<XComponentContext>& _rxFactory )
+ :OControlModel(_rxFactory, VCL_CONTROLMODEL_FIXEDTEXT)
+
+{
+ m_nClassId = FormComponentType::FIXEDTEXT;
+}
+
+
+OFixedTextModel::OFixedTextModel( const OFixedTextModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OControlModel( _pOriginal, _rxFactory )
+
+{
+}
+
+
+OFixedTextModel::~OFixedTextModel( )
+{
+}
+
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OFixedTextModel::createClone()
+{
+ rtl::Reference<OFixedTextModel> pClone = new OFixedTextModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+css::uno::Sequence<OUString> SAL_CALL OFixedTextModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OControlModel::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString* pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_COMPONENT_FIXEDTEXT;
+ pArray[aSupported.getLength()-1] = FRM_COMPONENT_FIXEDTEXT;
+ return aSupported;
+}
+
+
+void OFixedTextModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const
+{
+ OControlModel::describeAggregateProperties( _rAggregateProps );
+ RemoveProperty( _rAggregateProps, PROPERTY_TABSTOP );
+}
+
+
+OUString SAL_CALL OFixedTextModel::getServiceName()
+{
+ return FRM_COMPONENT_FIXEDTEXT; // old (non-sun) name for compatibility !
+}
+
+
+void SAL_CALL OFixedTextModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ OControlModel::write(_rxOutStream);
+
+ // Version
+ _rxOutStream->writeShort(0x0002);
+ writeHelpTextCompatibly(_rxOutStream);
+}
+
+
+void SAL_CALL OFixedTextModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ OControlModel::read(_rxInStream);
+
+ // Version
+ sal_Int16 nVersion = _rxInStream->readShort();
+ if (nVersion > 1)
+ readHelpTextCompatibly(_rxInStream);
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OFixedTextModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OFixedTextModel(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FixedText.hxx b/forms/source/component/FixedText.hxx
new file mode 100644
index 000000000..a4bafab60
--- /dev/null
+++ b/forms/source/component/FixedText.hxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+
+
+namespace frm
+{
+
+class OFixedTextModel
+ :public OControlModel
+{
+public:
+ OFixedTextModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OFixedTextModel(
+ const OFixedTextModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OFixedTextModel() override;
+
+// XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OFixedTextModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+// XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL
+ write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL
+ read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // OControlModel's property handling
+ virtual void describeAggregateProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FormComponent.cxx b/forms/source/component/FormComponent.cxx
new file mode 100644
index 000000000..78213bd3a
--- /dev/null
+++ b/forms/source/component/FormComponent.cxx
@@ -0,0 +1,2799 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <componenttools.hxx>
+#include <FormComponent.hxx>
+#include <strings.hrc>
+#include <frm_resource.hxx>
+#include <property.hxx>
+#include <services.hxx>
+
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/form/XLoadable.hpp>
+#include <com/sun/star/form/binding/IncompatibleTypesException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XMarkableStream.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/sdb/XRowSetChangeBroadcaster.hpp>
+#include <com/sun/star/sdb/XRowSetSupplier.hpp>
+#include <com/sun/star/sdbc/ColumnValue.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/util/VetoException.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+
+#include <comphelper/basicio.hxx>
+#include <comphelper/guarding.hxx>
+#include <comphelper/property.hxx>
+#include <connectivity/dbtools.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+
+#include <algorithm>
+
+namespace frm
+{
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::form::binding;
+using namespace ::com::sun::star::form::validation;
+using namespace ::dbtools;
+using namespace ::comphelper;
+
+// FieldChangeNotifier
+void ControlModelLock::impl_notifyAll_nothrow()
+{
+ m_rModel.firePropertyChanges( m_aHandles, m_aOldValues, m_aNewValues, OControlModel::LockAccess() );
+}
+
+void ControlModelLock::addPropertyNotification( const sal_Int32 _nHandle, const Any& _rOldValue, const Any& _rNewValue )
+{
+ assert( m_aHandles.size() == m_aOldValues.size() && m_aOldValues.size() == m_aNewValues.size() );
+
+ m_aHandles.push_back( _nHandle );
+ m_aOldValues.push_back( _rOldValue );
+ m_aNewValues.push_back( _rNewValue );
+}
+
+namespace {
+
+class FieldChangeNotifier
+{
+public:
+ explicit FieldChangeNotifier(ControlModelLock& _rLock)
+ : m_rLock( _rLock )
+ , m_rModel( dynamic_cast< OBoundControlModel& >( _rLock.getModel() ) )
+ {
+ m_xOldField = m_rModel.getField();
+ }
+
+ ~FieldChangeNotifier()
+ {
+ Reference< XPropertySet > xNewField( m_rModel.getField() );
+ if ( m_xOldField != xNewField )
+ m_rLock.addPropertyNotification( PROPERTY_ID_BOUNDFIELD, Any( m_xOldField ), Any( xNewField ) );
+ }
+
+private:
+ ControlModelLock& m_rLock;
+ OBoundControlModel& m_rModel;
+ Reference< XPropertySet > m_xOldField;
+};
+
+}
+
+// base class for form layer controls
+OControl::OControl( const Reference< XComponentContext >& _rxContext, const OUString& _rAggregateService, const bool _bSetDelegator )
+ :OComponentHelper(m_aMutex)
+ ,m_xContext( _rxContext )
+{
+ // Aggregate VCL Control
+ // Increment the RefCount for aggregates, because the aggregate by itself increments the RefCount in the setDelegator
+ osl_atomic_increment( &m_refCount );
+ {
+ m_xAggregate.set(_rxContext->getServiceManager()->createInstanceWithContext(_rAggregateService, _rxContext), css::uno::UNO_QUERY);
+ m_xControl.set(m_xAggregate, css::uno::UNO_QUERY);
+ }
+ osl_atomic_decrement( &m_refCount );
+
+ if ( _bSetDelegator )
+ doSetDelegator();
+}
+
+OControl::~OControl()
+{
+ doResetDelegator();
+}
+
+void OControl::doResetDelegator()
+{
+ if ( m_xAggregate.is() )
+ m_xAggregate->setDelegator( nullptr );
+}
+
+void OControl::doSetDelegator()
+{
+ osl_atomic_increment( &m_refCount );
+ if ( m_xAggregate.is() )
+ { // those brackets are important for some compilers, don't remove!
+ // (they ensure that the temporary object created in the line below
+ // is destroyed *before* the refcount-decrement)
+ m_xAggregate->setDelegator( static_cast< XWeak* >( this ) );
+ }
+ osl_atomic_decrement( &m_refCount );
+}
+
+// UNO Binding
+Any SAL_CALL OControl::queryAggregation( const Type& _rType )
+{
+ // ask the base class
+ Any aReturn( OComponentHelper::queryAggregation(_rType) );
+ // ask our own interfaces
+ if (!aReturn.hasValue())
+ {
+ aReturn = OControl_BASE::queryInterface(_rType);
+ // ask our aggregate
+ if (!aReturn.hasValue() && m_xAggregate.is())
+ aReturn = m_xAggregate->queryAggregation(_rType);
+ }
+
+ return aReturn;
+}
+
+Sequence<sal_Int8> SAL_CALL OControl::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+Sequence<Type> SAL_CALL OControl::getTypes()
+{
+ TypeBag aTypes( _getTypes() );
+
+ Reference< XTypeProvider > xProv;
+ if ( query_aggregation( m_xAggregate, xProv ) )
+ aTypes.addTypes( xProv->getTypes() );
+
+ return aTypes.getTypes();
+}
+
+Sequence<Type> OControl::_getTypes()
+{
+ return TypeBag( OComponentHelper::getTypes(), OControl_BASE::getTypes() ).getTypes();
+}
+
+// OComponentHelper
+void OControl::disposing()
+{
+ OComponentHelper::disposing();
+
+ m_aWindowStateGuard.attach( nullptr, nullptr );
+
+ Reference< XComponent > xComp;
+ if (query_aggregation(m_xAggregate, xComp))
+ xComp->dispose();
+}
+
+// XServiceInfo
+sal_Bool SAL_CALL OControl::supportsService(const OUString& _rsServiceName)
+{
+ return cppu::supportsService(this, _rsServiceName);
+}
+
+Sequence< OUString > OControl::getAggregateServiceNames() const
+{
+ Sequence< OUString > aAggServices;
+ Reference< XServiceInfo > xInfo;
+ if ( query_aggregation( m_xAggregate, xInfo ) )
+ aAggServices = xInfo->getSupportedServiceNames();
+
+ return aAggServices;
+}
+
+Sequence<OUString> SAL_CALL OControl::getSupportedServiceNames()
+{
+ // no own supported service names
+ return getAggregateServiceNames();
+}
+
+// XEventListener
+void SAL_CALL OControl::disposing(const css::lang::EventObject& _rEvent)
+{
+ Reference< XInterface > xAggAsIface;
+ query_aggregation(m_xAggregate, xAggAsIface);
+
+ // does the disposing come from the aggregate?
+ if (xAggAsIface != Reference< XInterface >(_rEvent.Source, UNO_QUERY))
+ { // no -> forward it
+ Reference<css::lang::XEventListener> xListener;
+ if (query_aggregation(m_xAggregate, xListener))
+ xListener->disposing(_rEvent);
+ }
+}
+
+// XControl
+void SAL_CALL OControl::setContext(const Reference< XInterface >& Context)
+{
+ if (m_xControl.is())
+ m_xControl->setContext(Context);
+}
+
+Reference< XInterface > SAL_CALL OControl::getContext()
+{
+ return m_xControl.is() ? m_xControl->getContext() : Reference< XInterface >();
+}
+
+void OControl::impl_resetStateGuard_nothrow()
+{
+ Reference< XWindow2 > xWindow;
+ Reference< XControlModel > xModel;
+ try
+ {
+ xWindow.set( getPeer(), UNO_QUERY );
+ xModel = getModel();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ m_aWindowStateGuard.attach( xWindow, xModel );
+}
+
+void SAL_CALL OControl::createPeer(const Reference<XToolkit>& _rxToolkit, const Reference<XWindowPeer>& _rxParent)
+{
+ if ( m_xControl.is() )
+ {
+ m_xControl->createPeer( _rxToolkit, _rxParent );
+ impl_resetStateGuard_nothrow();
+ }
+}
+
+Reference<XWindowPeer> SAL_CALL OControl::getPeer()
+{
+ return m_xControl.is() ? m_xControl->getPeer() : Reference<XWindowPeer>();
+}
+
+sal_Bool SAL_CALL OControl::setModel(const Reference<XControlModel>& Model)
+{
+ if ( !m_xControl.is() )
+ return false;
+
+ bool bSuccess = m_xControl->setModel( Model );
+ impl_resetStateGuard_nothrow();
+ return bSuccess;
+}
+
+Reference<XControlModel> SAL_CALL OControl::getModel()
+{
+ return m_xControl.is() ? m_xControl->getModel() : Reference<XControlModel>();
+}
+
+Reference<XView> SAL_CALL OControl::getView()
+{
+ return m_xControl.is() ? m_xControl->getView() : Reference<XView>();
+}
+
+void SAL_CALL OControl::setDesignMode(sal_Bool bOn)
+{
+ if (m_xControl.is())
+ m_xControl->setDesignMode(bOn);
+}
+
+sal_Bool SAL_CALL OControl::isDesignMode()
+{
+ return !m_xControl.is() || m_xControl->isDesignMode();
+}
+
+sal_Bool SAL_CALL OControl::isTransparent()
+{
+ return !m_xControl.is() || m_xControl->isTransparent();
+}
+
+OBoundControl::OBoundControl( const Reference< XComponentContext >& _rxContext,
+ const OUString& _rAggregateService, const bool _bSetDelegator )
+ :OControl( _rxContext, _rAggregateService, _bSetDelegator )
+ ,m_bLocked(false)
+{
+}
+
+OBoundControl::~OBoundControl()
+{
+}
+
+Sequence< Type> OBoundControl::_getTypes()
+{
+ return TypeBag( OControl::_getTypes(), OBoundControl_BASE::getTypes() ).getTypes();
+}
+
+Any SAL_CALL OBoundControl::queryAggregation(const Type& _rType)
+{
+ Any aReturn;
+
+ // XTypeProvider first - don't ask the OBoundControl_BASE, it would deliver incomplete types
+ if ( _rType.equals( cppu::UnoType<XTypeProvider>::get() ) )
+ aReturn = OControl::queryAggregation( _rType );
+
+ // ask our own interfaces
+ // (do this first (except XTypeProvider ) - we want to "overwrite" XPropertiesChangeListener)
+ if ( !aReturn.hasValue() )
+ aReturn = OBoundControl_BASE::queryInterface( _rType );
+
+ // ask the base class
+ if ( !aReturn.hasValue() )
+ aReturn = OControl::queryAggregation( _rType );
+
+ return aReturn;
+}
+
+sal_Bool SAL_CALL OBoundControl::getLock()
+{
+ return m_bLocked;
+}
+
+void SAL_CALL OBoundControl::setLock(sal_Bool _bLock)
+{
+ if (m_bLocked == bool(_bLock))
+ return;
+
+ osl::MutexGuard aGuard(m_aMutex);
+ _setLock(_bLock);
+ m_bLocked = _bLock;
+}
+
+void OBoundControl::_setLock(bool _bLock)
+{
+ // try to set the text component to readonly
+ Reference< XWindowPeer > xPeer = getPeer();
+ Reference< XTextComponent > xText( xPeer, UNO_QUERY );
+
+ if ( xText.is() )
+ xText->setEditable( !_bLock );
+ else
+ {
+ // disable the window
+ Reference< XWindow > xComp( xPeer, UNO_QUERY );
+ if ( xComp.is() )
+ xComp->setEnable( !_bLock );
+ }
+}
+
+sal_Bool SAL_CALL OBoundControl::setModel( const Reference< XControlModel >& _rxModel )
+{
+ return OControl::setModel( _rxModel );
+}
+
+void SAL_CALL OBoundControl::disposing(const EventObject& Source)
+{
+ // just disambiguate
+ OControl::disposing(Source);
+}
+
+void OBoundControl::disposing()
+{
+ OControl::disposing();
+}
+
+// OControlModel
+Sequence<sal_Int8> SAL_CALL OControlModel::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+Sequence<Type> SAL_CALL OControlModel::getTypes()
+{
+ TypeBag aTypes( _getTypes() );
+
+ Reference< XTypeProvider > xProv;
+
+ if ( query_aggregation( m_xAggregate, xProv ) )
+ aTypes.addTypes( xProv->getTypes() );
+
+ return aTypes.getTypes();
+}
+
+Sequence<Type> OControlModel::_getTypes()
+{
+ return TypeBag( OComponentHelper::getTypes(),
+ OPropertySetAggregationHelper::getTypes(),
+ OControlModel_BASE::getTypes()
+ ).getTypes();
+}
+
+Any SAL_CALL OControlModel::queryAggregation(const Type& _rType)
+{
+ // base class 1
+ Any aReturn(OComponentHelper::queryAggregation(_rType));
+
+ // base class 2
+ if (!aReturn.hasValue())
+ {
+ aReturn = OControlModel_BASE::queryInterface(_rType);
+
+ // our own interfaces
+ if (!aReturn.hasValue())
+ {
+ aReturn = OPropertySetAggregationHelper::queryInterface(_rType);
+ // our aggregate
+ if (!aReturn.hasValue() && m_xAggregate.is() && !_rType.equals(cppu::UnoType<XCloneable>::get()))
+ aReturn = m_xAggregate->queryAggregation(_rType);
+ }
+ }
+ return aReturn;
+}
+
+void OControlModel::readHelpTextCompatibly(const css::uno::Reference< css::io::XObjectInputStream >& _rxInStream)
+{
+ OUString sHelpText;
+ ::comphelper::operator>>( _rxInStream, sHelpText);
+ try
+ {
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_HELPTEXT, Any(sHelpText));
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ SAL_WARN("forms.component", "OControlModel::readHelpTextCompatibly: could not forward the property value to the aggregate!");
+ }
+}
+
+void OControlModel::writeHelpTextCompatibly(const css::uno::Reference< css::io::XObjectOutputStream >& _rxOutStream)
+{
+ OUString sHelpText;
+ try
+ {
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->getPropertyValue(PROPERTY_HELPTEXT) >>= sHelpText;
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ SAL_WARN("forms.component", "OControlModel::writeHelpTextCompatibly: could not retrieve the property value from the aggregate!");
+ }
+ ::comphelper::operator<<( _rxOutStream, sHelpText);
+}
+
+OControlModel::OControlModel(
+ const Reference<XComponentContext>& _rxContext,
+ const OUString& _rUnoControlModelTypeName,
+ const OUString& rDefault, const bool _bSetDelegator)
+ :OComponentHelper(m_aMutex)
+ ,OPropertySetAggregationHelper(OComponentHelper::rBHelper)
+ ,m_xContext( _rxContext )
+ ,m_lockCount( 0 )
+ ,m_aPropertyBagHelper( *this )
+ ,m_nTabIndex(FRM_DEFAULT_TABINDEX)
+ ,m_nClassId(FormComponentType::CONTROL)
+ ,m_bNativeLook( false )
+ ,m_bGenerateVbEvents( false )
+ ,m_nControlTypeinMSO(0) // 0 : default value is create from AOO
+ ,m_nObjIDinMSO(INVALID_OBJ_ID_IN_MSO)
+ // form controls are usually embedded into documents, not dialogs, and in documents
+ // the native look is ugly...
+ // #i37342#
+{
+ if (_rUnoControlModelTypeName.isEmpty()) // the is a model we have to aggregate
+ return;
+
+ osl_atomic_increment(&m_refCount);
+ {
+ m_xAggregate.set(m_xContext->getServiceManager()->createInstanceWithContext(_rUnoControlModelTypeName, m_xContext), UNO_QUERY);
+ setAggregation(m_xAggregate);
+
+ if ( m_xAggregateSet.is() )
+ {
+ try
+ {
+ if ( !rDefault.isEmpty() )
+ m_xAggregateSet->setPropertyValue( PROPERTY_DEFAULTCONTROL, Any( rDefault ) );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("forms.component", "OControlModel::OControlModel");
+ }
+ }
+ }
+ if (_bSetDelegator)
+ doSetDelegator();
+
+ // Refcount is at NULL again
+ osl_atomic_decrement(&m_refCount);
+}
+
+OControlModel::OControlModel( const OControlModel* _pOriginal, const Reference< XComponentContext>& _rxFactory, const bool _bCloneAggregate, const bool _bSetDelegator )
+ :OComponentHelper( m_aMutex )
+ ,OPropertySetAggregationHelper( OComponentHelper::rBHelper )
+ ,m_xContext( _rxFactory )
+ ,m_lockCount( 0 )
+ ,m_aPropertyBagHelper( *this )
+ ,m_nTabIndex( FRM_DEFAULT_TABINDEX )
+ ,m_nClassId( FormComponentType::CONTROL )
+{
+ DBG_ASSERT( _pOriginal, "OControlModel::OControlModel: invalid original!" );
+
+ // copy members
+ m_aName = _pOriginal->m_aName;
+ m_aTag = _pOriginal->m_aTag;
+ m_nTabIndex = _pOriginal->m_nTabIndex;
+ m_nClassId = _pOriginal->m_nClassId;
+ m_bNativeLook = _pOriginal->m_bNativeLook;
+ m_bGenerateVbEvents = _pOriginal->m_bGenerateVbEvents;
+ m_nControlTypeinMSO = _pOriginal->m_nControlTypeinMSO;
+ m_nObjIDinMSO = _pOriginal->m_nObjIDinMSO;
+
+ if ( !_bCloneAggregate )
+ return;
+
+ // temporarily increment refcount because of temporary references to ourself in the following
+ osl_atomic_increment( &m_refCount );
+ {
+ // transfer the (only, at the very moment!) ref count
+ m_xAggregate = createAggregateClone( _pOriginal );
+
+ // set aggregation (retrieve other direct interfaces of the aggregate)
+ setAggregation( m_xAggregate );
+ }
+
+ // set the delegator, if allowed by our derived class
+ if ( _bSetDelegator )
+ doSetDelegator();
+
+ // decrement ref count
+ osl_atomic_decrement( &m_refCount );
+}
+
+OControlModel::~OControlModel()
+{
+ // release the aggregate
+ doResetDelegator( );
+}
+
+void OControlModel::clonedFrom( const OControlModel* /*_pOriginal*/ )
+{
+ // nothing to do in this base class
+}
+
+void OControlModel::doResetDelegator()
+{
+ if (m_xAggregate.is())
+ m_xAggregate->setDelegator(nullptr);
+}
+
+void OControlModel::doSetDelegator()
+{
+ osl_atomic_increment(&m_refCount);
+ if (m_xAggregate.is())
+ {
+ m_xAggregate->setDelegator(static_cast<XWeak*>(this));
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+// XChild
+Reference< XInterface > SAL_CALL OControlModel::getParent()
+{
+ return m_xParent;
+}
+
+void SAL_CALL OControlModel::setParent(const Reference< XInterface >& _rxParent)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ Reference<XComponent> xComp(m_xParent, UNO_QUERY);
+ if (xComp.is())
+ xComp->removeEventListener(static_cast<XPropertiesChangeListener*>(this));
+
+ m_xParent = _rxParent;
+ xComp.set(m_xParent, css::uno::UNO_QUERY);
+
+ if ( xComp.is() )
+ xComp->addEventListener(static_cast<XPropertiesChangeListener*>(this));
+}
+
+// XNamed
+OUString SAL_CALL OControlModel::getName()
+{
+ OUString aReturn;
+ try
+ {
+ OPropertySetHelper::getFastPropertyValue(PROPERTY_ID_NAME) >>= aReturn;
+ }
+ catch (const css::beans::UnknownPropertyException&)
+ {
+ css::uno::Any a(cppu::getCaughtException());
+ throw WrappedTargetRuntimeException(
+ "OControlModel::getName",
+ *this,
+ a
+ );
+ }
+ return aReturn;
+}
+
+void SAL_CALL OControlModel::setName(const OUString& _rName)
+{
+ try
+ {
+ setFastPropertyValue(PROPERTY_ID_NAME, Any(_rName));
+ }
+ catch (const css::beans::UnknownPropertyException&)
+ {
+ css::uno::Any a(cppu::getCaughtException());
+ throw WrappedTargetRuntimeException(
+ "OControlModel::setName",
+ *this,
+ a
+ );
+ }
+}
+
+// XServiceInfo
+sal_Bool SAL_CALL OControlModel::supportsService(const OUString& _rServiceName)
+{
+ return cppu::supportsService(this, _rServiceName);
+}
+
+Sequence< OUString > OControlModel::getAggregateServiceNames() const
+{
+ Sequence< OUString > aAggServices;
+ Reference< XServiceInfo > xInfo;
+ if ( query_aggregation( m_xAggregate, xInfo ) )
+ aAggServices = xInfo->getSupportedServiceNames();
+ return aAggServices;
+}
+
+Sequence<OUString> SAL_CALL OControlModel::getSupportedServiceNames()
+{
+ return ::comphelper::concatSequences(
+ getAggregateServiceNames(),
+ getSupportedServiceNames_Static()
+ );
+}
+
+Sequence< OUString > OControlModel::getSupportedServiceNames_Static()
+{
+ return { FRM_SUN_FORMCOMPONENT, "com.sun.star.form.FormControlModel" };
+}
+
+// XEventListener
+void SAL_CALL OControlModel::disposing(const css::lang::EventObject& _rSource)
+{
+ // release the parent
+ if (_rSource.Source == m_xParent)
+ {
+ osl::MutexGuard aGuard(m_aMutex);
+ m_xParent = nullptr;
+ }
+ else
+ {
+ Reference<css::lang::XEventListener> xEvtLst;
+ if (query_aggregation(m_xAggregate, xEvtLst))
+ {
+ osl::MutexGuard aGuard(m_aMutex);
+ xEvtLst->disposing(_rSource);
+ }
+ }
+}
+
+// OComponentHelper
+void OControlModel::disposing()
+{
+ OPropertySetAggregationHelper::disposing();
+
+ Reference<css::lang::XComponent> xComp;
+ if (query_aggregation(m_xAggregate, xComp))
+ xComp->dispose();
+
+ setParent(Reference<XFormComponent>());
+
+ m_aPropertyBagHelper.dispose();
+}
+
+void OControlModel::writeAggregate( const Reference< XObjectOutputStream >& _rxOutStream ) const
+{
+ Reference< XPersistObject > xPersist;
+ if ( query_aggregation( m_xAggregate, xPersist ) )
+ xPersist->write( _rxOutStream );
+}
+
+void OControlModel::readAggregate( const Reference< XObjectInputStream >& _rxInStream )
+{
+ Reference< XPersistObject > xPersist;
+ if ( query_aggregation( m_xAggregate, xPersist ) )
+ xPersist->read( _rxInStream );
+}
+
+void SAL_CALL OControlModel::write(const Reference<css::io::XObjectOutputStream>& _rxOutStream)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ // 1. writing the UnoControls
+ Reference<css::io::XMarkableStream> xMark(_rxOutStream, UNO_QUERY);
+ if ( !xMark.is() )
+ {
+ throw IOException(
+ ResourceManager::loadString(RID_STR_INVALIDSTREAM),
+ static_cast< ::cppu::OWeakObject* >( this )
+ );
+ }
+
+ sal_Int32 nMark = xMark->createMark();
+ sal_Int32 nLen = 0;
+
+ _rxOutStream->writeLong(nLen);
+
+ writeAggregate( _rxOutStream );
+
+ // determining the length
+ nLen = xMark->offsetToMark(nMark) - 4;
+ xMark->jumpToMark(nMark);
+ _rxOutStream->writeLong(nLen);
+ xMark->jumpToFurthest();
+ xMark->deleteMark(nMark);
+
+ // 2. writing a version number
+ _rxOutStream->writeShort(0x0003);
+
+ // 3. writing the general properties
+ ::comphelper::operator<<( _rxOutStream, m_aName);
+ _rxOutStream->writeShort(m_nTabIndex);
+ ::comphelper::operator<<( _rxOutStream, m_aTag); // 3rd version
+
+ // IMPORTANT NOTE!
+ // don't write any new members here: this wouldn't be compatible with older versions, as OControlModel
+ // is a base class which is called in derived classes "read" method. So if you increment the version
+ // and write new stuff, older office versions will read this in the _derived_ classes, which may result
+ // in anything from data loss to crash.
+ // EOIN!
+}
+
+void OControlModel::read(const Reference<css::io::XObjectInputStream>& InStream)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ Reference<css::io::XMarkableStream> xMark(InStream, UNO_QUERY);
+ if ( !xMark.is() )
+ {
+ throw IOException(
+ ResourceManager::loadString(RID_STR_INVALIDSTREAM),
+ static_cast< ::cppu::OWeakObject* >( this )
+ );
+ }
+
+ // 1. reading the UnoControls
+ sal_Int32 nLen = InStream->readLong();
+ if (nLen)
+ {
+ sal_Int32 nMark = xMark->createMark();
+
+ try
+ {
+ readAggregate( InStream );
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ xMark->jumpToMark(nMark);
+ InStream->skipBytes(nLen);
+ xMark->deleteMark(nMark);
+ }
+
+ // 2. reading the version number
+ sal_uInt16 nVersion = InStream->readShort();
+
+ // 3. reading the general properties
+ ::comphelper::operator>>( InStream, m_aName);
+ m_nTabIndex = InStream->readShort();
+
+ if (nVersion > 0x0002)
+ ::comphelper::operator>>( InStream, m_aTag);
+
+ // we had a version where we wrote the help text
+ if (nVersion == 0x0004)
+ readHelpTextCompatibly(InStream);
+
+ DBG_ASSERT(nVersion < 5, "OControlModel::read : suspicious version number !");
+ // 4 was the version where we wrote the help text
+ // later versions shouldn't exist (see write for a detailed comment)
+}
+
+PropertyState OControlModel::getPropertyStateByHandle( sal_Int32 _nHandle )
+{
+ // simply compare the current and the default value
+ Any aCurrentValue = getPropertyDefaultByHandle( _nHandle );
+ Any aDefaultValue; getFastPropertyValue( aDefaultValue, _nHandle );
+
+ bool bEqual = aCurrentValue == aDefaultValue;
+ return bEqual ? PropertyState_DEFAULT_VALUE : PropertyState_DIRECT_VALUE;
+}
+
+void OControlModel::setPropertyToDefaultByHandle( sal_Int32 _nHandle)
+{
+ Any aDefault = getPropertyDefaultByHandle( _nHandle );
+
+ Any aConvertedValue, aOldValue;
+ if ( convertFastPropertyValue( aConvertedValue, aOldValue, _nHandle, aDefault ) )
+ {
+ setFastPropertyValue_NoBroadcast( _nHandle, aConvertedValue );
+ // TODO: fire the property change
+ }
+}
+
+Any OControlModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+{
+ Any aReturn;
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_NAME:
+ case PROPERTY_ID_TAG:
+ aReturn <<= OUString();
+ break;
+ case PROPERTY_ID_CLASSID:
+ aReturn <<= sal_Int16(FormComponentType::CONTROL);
+ break;
+ case PROPERTY_ID_TABINDEX:
+ aReturn <<= sal_Int16(FRM_DEFAULT_TABINDEX);
+ break;
+ case PROPERTY_ID_NATIVE_LOOK:
+ aReturn <<= true;
+ break;
+ case PROPERTY_ID_GENERATEVBAEVENTS:
+ aReturn <<= false;
+ break;
+ // added for exporting OCX control
+ case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
+ aReturn <<= sal_Int16(0);
+ break;
+ case PROPERTY_ID_OBJ_ID_IN_MSO:
+ aReturn <<= sal_uInt16(INVALID_OBJ_ID_IN_MSO);
+ break;
+ default:
+ if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
+ m_aPropertyBagHelper.getDynamicPropertyDefaultByHandle( _nHandle, aReturn );
+ else
+ SAL_WARN("forms.component", "OControlModel::convertFastPropertyValue: unknown handle " << _nHandle);
+ }
+ return aReturn;
+}
+
+void OControlModel::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+{
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_NAME:
+ _rValue <<= m_aName;
+ break;
+ case PROPERTY_ID_TAG:
+ _rValue <<= m_aTag;
+ break;
+ case PROPERTY_ID_CLASSID:
+ _rValue <<= m_nClassId;
+ break;
+ case PROPERTY_ID_TABINDEX:
+ _rValue <<= m_nTabIndex;
+ break;
+ case PROPERTY_ID_NATIVE_LOOK:
+ _rValue <<= m_bNativeLook;
+ break;
+ case PROPERTY_ID_GENERATEVBAEVENTS:
+ _rValue <<= m_bGenerateVbEvents;
+ break;
+ // added for exporting OCX control
+ case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
+ _rValue <<= m_nControlTypeinMSO;
+ break;
+ case PROPERTY_ID_OBJ_ID_IN_MSO:
+ _rValue <<= m_nObjIDinMSO;
+ break;
+ default:
+ if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
+ m_aPropertyBagHelper.getDynamicFastPropertyValue( _nHandle, _rValue );
+ else
+ OPropertySetAggregationHelper::getFastPropertyValue( _rValue, _nHandle );
+ break;
+ }
+}
+
+sal_Bool OControlModel::convertFastPropertyValue(
+ Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue)
+{
+ bool bModified(false);
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_NAME:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aName);
+ break;
+ case PROPERTY_ID_TAG:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aTag);
+ break;
+ case PROPERTY_ID_TABINDEX:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_nTabIndex);
+ break;
+ case PROPERTY_ID_NATIVE_LOOK:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bNativeLook);
+ break;
+ case PROPERTY_ID_GENERATEVBAEVENTS:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bGenerateVbEvents);
+ break;
+ // added for exporting OCX control
+ case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_nControlTypeinMSO);
+ break;
+ case PROPERTY_ID_OBJ_ID_IN_MSO:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_nObjIDinMSO);
+ break;
+ default:
+ if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
+ bModified = m_aPropertyBagHelper.convertDynamicFastPropertyValue( _nHandle, _rValue, _rConvertedValue, _rOldValue );
+ else
+ SAL_WARN("forms.component", "OControlModel::convertFastPropertyValue: unknown handle " << _nHandle);
+ break;
+ }
+ return bModified;
+}
+
+void OControlModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
+{
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_NAME:
+ DBG_ASSERT(_rValue.getValueType() == cppu::UnoType<OUString>::get(),
+ "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" );
+ _rValue >>= m_aName;
+ break;
+ case PROPERTY_ID_TAG:
+ DBG_ASSERT(_rValue.getValueType() == cppu::UnoType<OUString>::get(),
+ "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" );
+ _rValue >>= m_aTag;
+ break;
+ case PROPERTY_ID_TABINDEX:
+ DBG_ASSERT(_rValue.getValueType() == cppu::UnoType<sal_Int16>::get(),
+ "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" );
+ _rValue >>= m_nTabIndex;
+ break;
+ case PROPERTY_ID_NATIVE_LOOK:
+ OSL_VERIFY( _rValue >>= m_bNativeLook );
+ break;
+ case PROPERTY_ID_GENERATEVBAEVENTS:
+ OSL_VERIFY( _rValue >>= m_bGenerateVbEvents );
+ break;
+ // added for exporting OCX control
+ case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
+ OSL_VERIFY( _rValue >>= m_nControlTypeinMSO );
+ break;
+ case PROPERTY_ID_OBJ_ID_IN_MSO:
+ OSL_VERIFY( _rValue >>= m_nObjIDinMSO );
+ break;
+ default:
+ if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
+ m_aPropertyBagHelper.setDynamicFastPropertyValue( _nHandle, _rValue );
+ else
+ SAL_WARN("forms.component", "OControlModel::setFastPropertyValue_NoBroadcast: unknown handle " << _nHandle );
+ break;
+ }
+}
+
+void OControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ _rProps.realloc(7);
+ css::beans::Property* pProperties = _rProps.getArray();
+ *pProperties++ = css::beans::Property(PROPERTY_CLASSID, PROPERTY_ID_CLASSID, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_NAME, PROPERTY_ID_NAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_NATIVE_LOOK, PROPERTY_ID_NATIVE_LOOK, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_TAG, PROPERTY_ID_TAG, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_GENERATEVBAEVENTS, PROPERTY_ID_GENERATEVBAEVENTS, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROL_TYPE_IN_MSO, PROPERTY_ID_CONTROL_TYPE_IN_MSO, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_OBJ_ID_IN_MSO, PROPERTY_ID_OBJ_ID_IN_MSO, cppu::UnoType<cppu::UnoUnsignedShortType>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+void OControlModel::describeAggregateProperties( Sequence< Property >& /* [out] */ _rAggregateProps ) const
+{
+ if ( m_xAggregateSet.is() )
+ {
+ Reference< XPropertySetInfo > xPSI( m_xAggregateSet->getPropertySetInfo() );
+ if ( xPSI.is() )
+ _rAggregateProps = xPSI->getProperties();
+ }
+}
+
+::osl::Mutex& OControlModel::getMutex()
+{
+ return m_aMutex;
+}
+
+void OControlModel::describeFixedAndAggregateProperties( Sequence< Property >& _out_rFixedProperties, Sequence< Property >& _out_rAggregateProperties ) const
+{
+ describeFixedProperties( _out_rFixedProperties );
+ describeAggregateProperties( _out_rAggregateProperties );
+}
+
+Reference< XMultiPropertySet > OControlModel::getPropertiesInterface()
+{
+ return Reference< XMultiPropertySet >( *this, UNO_QUERY );
+}
+
+Reference< XPropertySetInfo> SAL_CALL OControlModel::getPropertySetInfo()
+{
+ return createPropertySetInfo( getInfoHelper() );
+}
+
+::cppu::IPropertyArrayHelper& OControlModel::getInfoHelper()
+{
+ return m_aPropertyBagHelper.getInfoHelper();
+}
+
+void SAL_CALL OControlModel::addProperty( const OUString& _rName, ::sal_Int16 _nAttributes, const Any& _rInitialValue )
+{
+ m_aPropertyBagHelper.addProperty( _rName, _nAttributes, _rInitialValue );
+}
+
+void SAL_CALL OControlModel::removeProperty( const OUString& _rName )
+{
+ m_aPropertyBagHelper.removeProperty( _rName );
+}
+
+Sequence< PropertyValue > SAL_CALL OControlModel::getPropertyValues()
+{
+ return m_aPropertyBagHelper.getPropertyValues();
+}
+
+void SAL_CALL OControlModel::setPropertyValues( const Sequence< PropertyValue >& _rProps )
+{
+ m_aPropertyBagHelper.setPropertyValues( _rProps );
+}
+
+void OControlModel::lockInstance( LockAccess )
+{
+ m_aMutex.acquire();
+ osl_atomic_increment( &m_lockCount );
+}
+
+oslInterlockedCount OControlModel::unlockInstance( LockAccess )
+{
+ OSL_ENSURE( m_lockCount > 0, "OControlModel::unlockInstance: not locked!" );
+ oslInterlockedCount lockCount = osl_atomic_decrement( &m_lockCount );
+ m_aMutex.release();
+ return lockCount;
+}
+
+void OControlModel::firePropertyChanges( const std::vector< sal_Int32 >& _rHandles, const std::vector< Any >& _rOldValues,
+ const std::vector< Any >& _rNewValues, LockAccess )
+{
+ OPropertySetHelper::fire(
+ const_cast< std::vector< sal_Int32 >& >( _rHandles ).data(),
+ _rNewValues.data(),
+ _rOldValues.data(),
+ _rHandles.size(),
+ false
+ );
+}
+
+// OBoundControlModel
+Any SAL_CALL OBoundControlModel::queryAggregation( const Type& _rType )
+{
+ Any aReturn( OControlModel::queryAggregation(_rType) );
+ if (!aReturn.hasValue())
+ {
+ aReturn = OBoundControlModel_BASE1::queryInterface(_rType);
+
+ if ( !aReturn.hasValue() && m_bCommitable )
+ aReturn = OBoundControlModel_COMMITTING::queryInterface( _rType );
+
+ if ( !aReturn.hasValue() && m_bSupportsExternalBinding )
+ aReturn = OBoundControlModel_BINDING::queryInterface( _rType );
+
+ if ( !aReturn.hasValue() && m_bSupportsValidation )
+ aReturn = OBoundControlModel_VALIDATION::queryInterface( _rType );
+ }
+ return aReturn;
+}
+
+OBoundControlModel::OBoundControlModel(
+ const Reference< XComponentContext>& _rxFactory,
+ const OUString& _rUnoControlModelTypeName, const OUString& _rDefault,
+ const bool _bCommitable, const bool _bSupportExternalBinding, const bool _bSupportsValidation )
+ :OControlModel( _rxFactory, _rUnoControlModelTypeName, _rDefault, false )
+ ,OPropertyChangeListener( m_aMutex )
+ ,m_nValuePropertyAggregateHandle( -1 )
+ ,m_nFieldType( DataType::OTHER )
+ ,m_bValuePropertyMayBeVoid( false )
+ ,m_aResetHelper( *this, m_aMutex )
+ ,m_aUpdateListeners(m_aMutex)
+ ,m_aFormComponentListeners( m_aMutex )
+ ,m_bInputRequired( false )
+ ,m_bFormListening( false )
+ ,m_bLoaded(false)
+ ,m_bRequired(false)
+ ,m_bCommitable(_bCommitable)
+ ,m_bSupportsExternalBinding( _bSupportExternalBinding )
+ ,m_bSupportsValidation( _bSupportsValidation )
+ ,m_bForwardValueChanges(true)
+ ,m_bTransferringValue( false )
+ ,m_bIsCurrentValueValid( true )
+ ,m_bBindingControlsRO( false )
+ ,m_bBindingControlsEnable( false )
+ ,m_eControlValueChangeInstigator( eOther )
+ ,m_aLabelServiceName(FRM_SUN_COMPONENT_FIXEDTEXT)
+{
+ // start property listening at the aggregate
+ implInitAggMultiplexer( );
+}
+
+OBoundControlModel::OBoundControlModel(
+ const OBoundControlModel* _pOriginal, const Reference< XComponentContext>& _rxFactory )
+ :OControlModel( _pOriginal, _rxFactory, true, false )
+ ,OPropertyChangeListener( m_aMutex )
+ ,m_nValuePropertyAggregateHandle( _pOriginal->m_nValuePropertyAggregateHandle )
+ ,m_nFieldType( DataType::OTHER )
+ ,m_bValuePropertyMayBeVoid( _pOriginal->m_bValuePropertyMayBeVoid )
+ ,m_aResetHelper( *this, m_aMutex )
+ ,m_aUpdateListeners( m_aMutex )
+ ,m_aFormComponentListeners( m_aMutex )
+ ,m_xValidator( _pOriginal->m_xValidator )
+ ,m_bInputRequired( false )
+ ,m_bFormListening( false )
+ ,m_bLoaded( false )
+ ,m_bRequired( false )
+ ,m_bCommitable( _pOriginal->m_bCommitable )
+ ,m_bSupportsExternalBinding( _pOriginal->m_bSupportsExternalBinding )
+ ,m_bSupportsValidation( _pOriginal->m_bSupportsValidation )
+ ,m_bForwardValueChanges( true )
+ ,m_bTransferringValue( false )
+ ,m_bIsCurrentValueValid( _pOriginal->m_bIsCurrentValueValid )
+ ,m_bBindingControlsRO( false )
+ ,m_bBindingControlsEnable( false )
+ ,m_eControlValueChangeInstigator( eOther )
+{
+ // start property listening at the aggregate
+ implInitAggMultiplexer( );
+ m_aLabelServiceName = _pOriginal->m_aLabelServiceName;
+ m_sValuePropertyName = _pOriginal->m_sValuePropertyName;
+ m_nValuePropertyAggregateHandle = _pOriginal->m_nValuePropertyAggregateHandle;
+ m_bValuePropertyMayBeVoid = _pOriginal->m_bValuePropertyMayBeVoid;
+ m_aValuePropertyType = _pOriginal->m_aValuePropertyType;
+ m_aControlSource = _pOriginal->m_aControlSource;
+ m_bInputRequired = _pOriginal->m_bInputRequired;
+ // m_xLabelControl, though being a property, is not to be cloned, not even the reference will be transferred.
+ // (the former should be clear - a clone of the object we're only referencing does not make sense)
+ // (the second would violate the restriction for label controls that they're part of the
+ // same form component hierarchy - we ourself are no part, yet, so we can't have a label control)
+ // start listening for changes at the value property
+ implInitValuePropertyListening( );
+}
+
+OBoundControlModel::~OBoundControlModel()
+{
+ if ( !OComponentHelper::rBHelper.bDisposed )
+ {
+ acquire();
+ dispose();
+ }
+
+ doResetDelegator( );
+ OSL_ENSURE( m_pAggPropMultiplexer, "OBoundControlModel::~OBoundControlModel: what about my property multiplexer?" );
+ if ( m_pAggPropMultiplexer )
+ {
+ m_pAggPropMultiplexer->dispose();
+ m_pAggPropMultiplexer = nullptr;
+ }
+}
+
+void OBoundControlModel::clonedFrom( const OControlModel* _pOriginal )
+{
+ const OBoundControlModel* pBoundOriginal = static_cast< const OBoundControlModel* >( _pOriginal );
+ // the value binding can be handled as if somebody called setValueBinding here
+ // By definition, bindings can be share between bindables
+ if ( !(pBoundOriginal && pBoundOriginal->m_xExternalBinding.is()) )
+ return;
+
+ try
+ {
+ setValueBinding( pBoundOriginal->m_xExternalBinding );
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+}
+
+void OBoundControlModel::implInitAggMultiplexer( )
+{
+ osl_atomic_increment( &m_refCount );
+ if ( m_xAggregateSet.is() )
+ {
+ m_pAggPropMultiplexer = new OPropertyChangeMultiplexer( this, m_xAggregateSet, false );
+ }
+
+ osl_atomic_decrement( &m_refCount );
+ doSetDelegator();
+}
+
+void OBoundControlModel::implInitValuePropertyListening( ) const
+{
+ // start listening for changes at the value property
+ // There are three pre-requisites for this to be done:
+ // 1. We support external value bindings. In this case, the changes in the control value need to
+ // be propagated to the external binding immediately when they happen
+ // 2. We support external validation. In this case, we need to listen for changes in the value
+ // property, since we need to revalidate then.
+ // 3. We are not committable. In this case, changes in the control value need to be propagated
+ // to the database column immediately when they happen.
+ if ( m_bSupportsExternalBinding || m_bSupportsValidation || !m_bCommitable )
+ {
+ OSL_ENSURE( m_pAggPropMultiplexer, "OBoundControlModel::implInitValuePropertyListening: no multiplexer!" );
+ if ( m_pAggPropMultiplexer && !m_sValuePropertyName.isEmpty() )
+ m_pAggPropMultiplexer->addProperty( m_sValuePropertyName );
+ }
+}
+
+void OBoundControlModel::initOwnValueProperty( const OUString& i_rValuePropertyName )
+{
+ OSL_PRECOND( m_sValuePropertyName.isEmpty() && -1 == m_nValuePropertyAggregateHandle,
+ "OBoundControlModel::initOwnValueProperty: value property is already initialized!" );
+ OSL_ENSURE( !i_rValuePropertyName.isEmpty(), "OBoundControlModel::initOwnValueProperty: invalid property name!" );
+ m_sValuePropertyName = i_rValuePropertyName;
+}
+
+void OBoundControlModel::initValueProperty( const OUString& _rValuePropertyName, sal_Int32 _nValuePropertyExternalHandle )
+{
+ OSL_PRECOND( m_sValuePropertyName.isEmpty() && -1 == m_nValuePropertyAggregateHandle,
+ "OBoundControlModel::initValueProperty: value property is already initialized!" );
+ OSL_ENSURE( !_rValuePropertyName.isEmpty(), "OBoundControlModel::initValueProperty: invalid property name!" );
+ OSL_ENSURE( _nValuePropertyExternalHandle != -1, "OBoundControlModel::initValueProperty: invalid property handle!" );
+
+ m_sValuePropertyName = _rValuePropertyName;
+ m_nValuePropertyAggregateHandle = getOriginalHandle( _nValuePropertyExternalHandle );
+ OSL_ENSURE( m_nValuePropertyAggregateHandle != -1, "OBoundControlModel::initValueProperty: unable to find the original handle!" );
+
+ if ( m_nValuePropertyAggregateHandle != -1 )
+ {
+ Reference< XPropertySetInfo > xPropInfo( m_xAggregateSet->getPropertySetInfo(), UNO_SET_THROW );
+ Property aValuePropDesc = xPropInfo->getPropertyByName( m_sValuePropertyName );
+ m_aValuePropertyType = aValuePropDesc.Type;
+ m_bValuePropertyMayBeVoid = ( aValuePropDesc.Attributes & PropertyAttribute::MAYBEVOID ) != 0;
+ }
+
+ // start listening for changes at the value property
+ implInitValuePropertyListening( );
+}
+
+void OBoundControlModel::suspendValueListening( )
+{
+ OSL_PRECOND( !m_sValuePropertyName.isEmpty(), "OBoundControlModel::suspendValueListening: don't have a value property!" );
+ OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::suspendValueListening: I *am* not listening!" );
+
+ if ( m_pAggPropMultiplexer )
+ m_pAggPropMultiplexer->lock();
+}
+
+void OBoundControlModel::resumeValueListening( )
+{
+ OSL_PRECOND( !m_sValuePropertyName.isEmpty(), "OBoundControlModel::resumeValueListening: don't have a value property!" );
+ OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::resumeValueListening: I *am* not listening at all!" );
+ OSL_PRECOND( !m_pAggPropMultiplexer || m_pAggPropMultiplexer->locked(), "OBoundControlModel::resumeValueListening: listening not suspended currently!" );
+ if ( m_pAggPropMultiplexer )
+ m_pAggPropMultiplexer->unlock();
+}
+
+Sequence< Type > OBoundControlModel::_getTypes()
+{
+ TypeBag aTypes(
+ OControlModel::_getTypes(),
+ OBoundControlModel_BASE1::getTypes()
+ );
+
+ if ( m_bCommitable )
+ aTypes.addTypes( OBoundControlModel_COMMITTING::getTypes() );
+
+ if ( m_bSupportsExternalBinding )
+ aTypes.addTypes( OBoundControlModel_BINDING::getTypes() );
+
+ if ( m_bSupportsValidation )
+ aTypes.addTypes( OBoundControlModel_VALIDATION::getTypes() );
+ return aTypes.getTypes();
+}
+
+// OComponentHelper
+void OBoundControlModel::disposing()
+{
+ OControlModel::disposing();
+
+ osl::MutexGuard aGuard(m_aMutex);
+
+ if ( m_pAggPropMultiplexer )
+ m_pAggPropMultiplexer->dispose();
+
+ // notify all our listeners
+ css::lang::EventObject aEvt( static_cast< XWeak* >( this ) );
+ m_aUpdateListeners.disposeAndClear( aEvt );
+ m_aResetHelper.disposing();
+
+ // disconnect from our database column
+ // TODO: could we replace the following 5 lines with a call to impl_disconnectDatabaseColumn_noNotify?
+ // The only more thing which it does is calling onDisconnectedDbColumn - could this
+ // cause trouble? At least when we continue to call OControlModel::disposing before, it *may*.
+ if ( hasField() )
+ {
+ getField()->removePropertyChangeListener( PROPERTY_VALUE, this );
+ resetField();
+ }
+
+ m_xCursor = nullptr;
+ Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY );
+ if ( xComp.is() )
+ xComp->removeEventListener(static_cast< XEventListener* >( static_cast< XPropertyChangeListener* >( this ) ) );
+ // disconnect from our external value binding
+ if ( hasExternalValueBinding() )
+ disconnectExternalValueBinding();
+ // ditto for the validator
+ if ( hasValidator() )
+ disconnectValidator( );
+}
+
+void OBoundControlModel::onValuePropertyChange( ControlModelLock& i_rControLock )
+{
+ if ( hasExternalValueBinding() )
+ {
+ // the control value changed, while we have an external value binding
+ // -> forward the value to it
+ if ( m_eControlValueChangeInstigator != eExternalBinding )
+ transferControlValueToExternal( i_rControLock );
+ }
+
+ else if ( !m_bCommitable && m_xColumnUpdate.is() )
+ {
+ // the control value changed, while we are bound to a database column,
+ // but not committable (which means changes in the control have to be reflected to
+ // the underlying database column immediately)
+ // -> forward the value to the database column
+ if ( m_eControlValueChangeInstigator != eDbColumnBinding )
+ commitControlValueToDbColumn( false );
+ }
+
+ // validate the new value
+ if ( m_bSupportsValidation )
+ recheckValidity( true );
+}
+
+void OBoundControlModel::_propertyChanged( const PropertyChangeEvent& _rEvt )
+{
+ ControlModelLock aLock( *this );
+ OSL_ENSURE( _rEvt.PropertyName == m_sValuePropertyName,
+ "OBoundControlModel::_propertyChanged: where did this come from (1)?" );
+ OSL_ENSURE( m_pAggPropMultiplexer && !m_pAggPropMultiplexer->locked(),
+ "OBoundControlModel::_propertyChanged: where did this come from (2)?" );
+ if ( _rEvt.PropertyName == m_sValuePropertyName )
+ {
+ onValuePropertyChange( aLock );
+ }
+}
+
+void OBoundControlModel::startAggregatePropertyListening( const OUString& _rPropertyName )
+{
+ OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::startAggregatePropertyListening: no multiplexer!" );
+ OSL_ENSURE( !_rPropertyName.isEmpty(), "OBoundControlModel::startAggregatePropertyListening: invalid property name!" );
+ if ( m_pAggPropMultiplexer && !_rPropertyName.isEmpty() )
+ {
+ m_pAggPropMultiplexer->addProperty( _rPropertyName );
+ }
+}
+
+void OBoundControlModel::doFormListening( const bool _bStart )
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::doFormListening: external value binding should overrule the database binding!" );
+ if ( isFormListening() == _bStart )
+ return;
+ if ( m_xAmbientForm.is() )
+ _bStart ? m_xAmbientForm->addLoadListener( this ) : m_xAmbientForm->removeLoadListener( this );
+ Reference< XLoadable > xParentLoadable( getParent(), UNO_QUERY );
+ if ( getParent().is() && !xParentLoadable.is() )
+ {
+ // if our parent does not directly support the XLoadable interface, then it might support the
+ // XRowSetSupplier/XRowSetChangeBroadcaster interfaces. In this case we have to listen for changes
+ // broadcasted by the latter.
+ Reference< XRowSetChangeBroadcaster > xRowSetBroadcaster( getParent(), UNO_QUERY );
+ if ( xRowSetBroadcaster.is() )
+ _bStart ? xRowSetBroadcaster->addRowSetChangeListener( this ) : xRowSetBroadcaster->removeRowSetChangeListener( this );
+ }
+
+ m_bFormListening = _bStart && m_xAmbientForm.is();
+}
+
+// XChild
+void SAL_CALL OBoundControlModel::setParent(const Reference<XInterface>& _rxParent)
+{
+ ControlModelLock aLock( *this );
+ FieldChangeNotifier aBoundFieldNotifier( aLock );
+ if ( getParent() == _rxParent )
+ return;
+ // disconnect from database column (which is controlled by parent, directly or indirectly)
+ if ( hasField() )
+ impl_disconnectDatabaseColumn_noNotify();
+ // log off old listeners
+ if ( isFormListening() )
+ doFormListening( false );
+ // actually set the new parent
+ OControlModel::setParent( _rxParent );
+ // a new parent means a new ambient form
+ impl_determineAmbientForm_nothrow();
+ if ( !hasExternalValueBinding() )
+ {
+ // log on new listeners
+ doFormListening( true );
+ // re-connect to database column of the new parent
+ if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() )
+ impl_connectDatabaseColumn_noNotify( false );
+ }
+}
+
+// XEventListener
+void SAL_CALL OBoundControlModel::disposing(const css::lang::EventObject& _rEvent)
+{
+ ControlModelLock aLock( *this );
+ if ( _rEvent.Source == getField() )
+ {
+ resetField();
+ }
+
+ else if ( _rEvent.Source == m_xLabelControl )
+ {
+ Reference<XPropertySet> xOldValue = m_xLabelControl;
+ m_xLabelControl = nullptr;
+ // fire a propertyChanged (when we leave aLock's scope)
+ aLock.addPropertyNotification( PROPERTY_ID_CONTROLLABEL, Any( xOldValue ), Any( m_xLabelControl ) );
+ }
+
+ else if ( _rEvent.Source == m_xExternalBinding )
+ { // *first* check for the external binding
+ disconnectExternalValueBinding( );
+ }
+
+ else if ( _rEvent.Source == m_xValidator )
+ { // *then* check for the validator. Reason is that bindings may also act as validator at the same
+ // time, in this case, the validator is automatically revoked when the binding is revoked
+ disconnectValidator( );
+ }
+
+ else
+ OControlModel::disposing(_rEvent);
+}
+
+// XServiceInfo
+css::uno::Sequence<OUString> SAL_CALL OBoundControlModel::getSupportedServiceNames()
+{
+ return ::comphelper::combineSequences(
+ getAggregateServiceNames(),
+ getSupportedServiceNames_Static()
+ );
+}
+
+Sequence< OUString > OBoundControlModel::getSupportedServiceNames_Static()
+{
+ Sequence<OUString> aOwnServiceNames { "com.sun.star.form.DataAwareControlModel" };
+ return ::comphelper::concatSequences(
+ OControlModel::getSupportedServiceNames_Static(),
+ aOwnServiceNames
+ );
+}
+
+// XPersist
+void SAL_CALL OBoundControlModel::write( const Reference<css::io::XObjectOutputStream>& _rxOutStream )
+{
+ OControlModel::write(_rxOutStream);
+ osl::MutexGuard aGuard(m_aMutex);
+ // Version
+ _rxOutStream->writeShort(0x0002);
+ // Controlsource
+ ::comphelper::operator<<( _rxOutStream, m_aControlSource);
+ // !!! IMPORTANT NOTE !!!
+ // don't write any new members here: this wouldn't be compatible with older versions, as OBoundControlModel
+ // is a base class which is called in derived classes "read" method. So if you increment the version
+ // and write new stuff, older office versions will read this in the _derived_ classes, which may result
+ // in anything from data loss to crash.
+ // (use writeCommonProperties instead, this is called in derived classes write-method)
+ // !!! EOIN !!!
+}
+
+void OBoundControlModel::defaultCommonProperties()
+{
+ Reference<css::lang::XComponent> xComp(m_xLabelControl, UNO_QUERY);
+ if (xComp.is())
+ xComp->removeEventListener(static_cast<css::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
+ m_xLabelControl = nullptr;
+}
+
+void OBoundControlModel::readCommonProperties(const Reference<css::io::XObjectInputStream>& _rxInStream)
+{
+ sal_Int32 nLen = _rxInStream->readLong();
+ Reference<css::io::XMarkableStream> xMark(_rxInStream, UNO_QUERY);
+ DBG_ASSERT(xMark.is(), "OBoundControlModel::readCommonProperties : can only work with markable streams !");
+ sal_Int32 nMark = xMark->createMark();
+ // read the reference to the label control
+ Reference<css::io::XPersistObject> xPersist;
+ sal_Int32 nUsedFlag;
+ nUsedFlag = _rxInStream->readLong();
+ if (nUsedFlag)
+ xPersist = _rxInStream->readObject();
+ m_xLabelControl.set(xPersist, css::uno::UNO_QUERY);
+ Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY );
+ if (xComp.is())
+ xComp->addEventListener(static_cast<css::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
+ // read any other new common properties here
+ // skip the remaining bytes
+ xMark->jumpToMark(nMark);
+ _rxInStream->skipBytes(nLen);
+ xMark->deleteMark(nMark);
+}
+
+void OBoundControlModel::writeCommonProperties(const Reference<css::io::XObjectOutputStream>& _rxOutStream)
+{
+ Reference<css::io::XMarkableStream> xMark(_rxOutStream, UNO_QUERY);
+ DBG_ASSERT(xMark.is(), "OBoundControlModel::writeCommonProperties : can only work with markable streams !");
+ sal_Int32 nMark = xMark->createMark();
+ // a placeholder where we will write the overall length (later in this method)
+ sal_Int32 nLen = 0;
+ _rxOutStream->writeLong(nLen);
+ // write the reference to the label control
+ Reference<css::io::XPersistObject> xPersist(m_xLabelControl, UNO_QUERY);
+ sal_Int32 nUsedFlag = 0;
+ if (xPersist.is())
+ nUsedFlag = 1;
+ _rxOutStream->writeLong(nUsedFlag);
+ if (xPersist.is())
+ _rxOutStream->writeObject(xPersist);
+ // write any other new common properties here
+ // write the correct length at the beginning of the block
+ nLen = xMark->offsetToMark(nMark) - sizeof(nLen);
+ xMark->jumpToMark(nMark);
+ _rxOutStream->writeLong(nLen);
+ xMark->jumpToFurthest();
+ xMark->deleteMark(nMark);
+}
+
+void SAL_CALL OBoundControlModel::read( const Reference< css::io::XObjectInputStream >& _rxInStream )
+{
+ OControlModel::read(_rxInStream);
+ osl::MutexGuard aGuard(m_aMutex);
+ _rxInStream->readShort(); // version;
+ ::comphelper::operator>>( _rxInStream, m_aControlSource);
+}
+
+void OBoundControlModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_INPUT_REQUIRED:
+ rValue <<= m_bInputRequired;
+ break;
+ case PROPERTY_ID_CONTROLSOURCEPROPERTY:
+ rValue <<= m_sValuePropertyName;
+ break;
+ case PROPERTY_ID_CONTROLSOURCE:
+ rValue <<= m_aControlSource;
+ break;
+ case PROPERTY_ID_BOUNDFIELD:
+ rValue <<= getField();
+ break;
+ case PROPERTY_ID_CONTROLLABEL:
+ if (!m_xLabelControl.is())
+ rValue.clear();
+ else
+ rValue <<= m_xLabelControl;
+ break;
+ default:
+ OControlModel::getFastPropertyValue(rValue, nHandle);
+ }
+}
+
+sal_Bool OBoundControlModel::convertFastPropertyValue(
+ Any& _rConvertedValue, Any& _rOldValue,
+ sal_Int32 _nHandle,
+ const Any& _rValue)
+{
+ bool bModified(false);
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_INPUT_REQUIRED:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_bInputRequired );
+ break;
+ case PROPERTY_ID_CONTROLSOURCE:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aControlSource);
+ break;
+ case PROPERTY_ID_BOUNDFIELD:
+ SAL_WARN("forms.component", "OBoundControlModel::convertFastPropertyValue: BoundField should be a read-only property !" );
+ throw css::lang::IllegalArgumentException();
+ case PROPERTY_ID_CONTROLLABEL:
+ if (!_rValue.hasValue())
+ { // property set to void
+ _rConvertedValue = Any();
+ getFastPropertyValue(_rOldValue, _nHandle);
+ bModified = m_xLabelControl.is();
+ }
+
+ else
+ {
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_xLabelControl);
+ if (!m_xLabelControl.is())
+ // an empty interface is interpreted as VOID
+ _rOldValue.clear();
+ }
+
+ break;
+ default:
+ bModified = OControlModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue);
+ }
+ return bModified;
+}
+
+Any OBoundControlModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+{
+ Any aDefault;
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_INPUT_REQUIRED:
+ aDefault <<= false;
+ break;
+ case PROPERTY_ID_CONTROLSOURCE:
+ aDefault <<= OUString();
+ break;
+ case PROPERTY_ID_CONTROLLABEL:
+ aDefault <<= Reference< XPropertySet >();
+ break;
+ }
+ return aDefault;
+}
+
+void OBoundControlModel::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_INPUT_REQUIRED:
+ OSL_VERIFY( rValue >>= m_bInputRequired );
+ break;
+ case PROPERTY_ID_CONTROLSOURCE:
+ OSL_VERIFY( rValue >>= m_aControlSource );
+ break;
+ case PROPERTY_ID_BOUNDFIELD:
+ SAL_WARN("forms.component", "OBoundControlModel::setFastPropertyValue_NoBroadcast : BoundField should be a read-only property !");
+ throw css::lang::IllegalArgumentException();
+ case PROPERTY_ID_CONTROLLABEL:
+ {
+ if ( rValue.hasValue() && ( rValue.getValueTypeClass() != TypeClass_INTERFACE ) )
+ throw css::lang::IllegalArgumentException();
+ Reference< XInterface > xNewValue( rValue, UNO_QUERY );
+ if ( !xNewValue.is() )
+ { // set property to "void"
+ Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY );
+ if ( xComp.is() )
+ xComp->removeEventListener( static_cast< XPropertyChangeListener* >( this ) );
+ m_xLabelControl = nullptr;
+ break;
+ }
+
+ Reference< XControlModel > xAsModel ( xNewValue, UNO_QUERY );
+ Reference< XServiceInfo > xAsServiceInfo ( xAsModel, UNO_QUERY );
+ Reference< XPropertySet > xAsPropSet ( xAsServiceInfo, UNO_QUERY );
+ Reference< XChild > xAsChild ( xAsPropSet, UNO_QUERY );
+ if ( !xAsChild.is() || !xAsServiceInfo->supportsService( m_aLabelServiceName ) )
+ {
+ throw css::lang::IllegalArgumentException();
+ }
+
+ // Check if we and the given model have a common ancestor (up to the forms collection)
+ Reference<XChild> xCont(this);
+ Reference< XInterface > xMyTopLevel = xCont->getParent();
+ while (xMyTopLevel.is())
+ {
+ Reference<XForm> xAsForm(xMyTopLevel, UNO_QUERY);
+ if (!xAsForm.is())
+ // found my root
+ break;
+ Reference<XChild> xLoopAsChild(xMyTopLevel, UNO_QUERY);
+ xMyTopLevel = xLoopAsChild.is() ? xLoopAsChild->getParent() : Reference< XInterface >();
+ }
+
+ Reference< XInterface > xNewTopLevel = xAsChild->getParent();
+ while (xNewTopLevel.is())
+ {
+ Reference<XForm> xAsForm(xNewTopLevel, UNO_QUERY);
+ if (!xAsForm.is())
+ break;
+ Reference<XChild> xLoopAsChild(xNewTopLevel, UNO_QUERY);
+ xNewTopLevel = xLoopAsChild.is() ? xLoopAsChild->getParent() : Reference< XInterface >();
+ }
+
+ if (xNewTopLevel != xMyTopLevel)
+ {
+ // the both objects don't belong to the same forms collection -> not acceptable
+ throw css::lang::IllegalArgumentException();
+ }
+
+ m_xLabelControl = xAsPropSet;
+ Reference<css::lang::XComponent> xComp(m_xLabelControl, UNO_QUERY);
+ if (xComp.is())
+ xComp->addEventListener(static_cast<css::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
+ }
+
+ break;
+ default:
+ OControlModel::setFastPropertyValue_NoBroadcast(nHandle, rValue );
+ }
+}
+
+// XPropertyChangeListener
+void SAL_CALL OBoundControlModel::propertyChange( const PropertyChangeEvent& evt )
+{
+ // if the DBColumn value changed, transfer it to the control
+ if ( evt.PropertyName == PROPERTY_VALUE )
+ {
+ OSL_ENSURE( evt.Source == getField(), "OBoundControlModel::propertyChange: value changes from components other than our database column?" );
+ osl::MutexGuard aGuard(m_aMutex);
+ if ( m_bForwardValueChanges && m_xColumn.is() )
+ transferDbValueToControl();
+ }
+
+ else
+ {
+ OSL_ENSURE( evt.Source == m_xExternalBinding, "OBoundControlModel::propertyChange: where did this come from?" );
+ // our binding has properties which can control properties of ourself
+ OUString sBindingControlledProperty;
+ bool bForwardToLabelControl = false;
+ if ( evt.PropertyName == PROPERTY_READONLY )
+ {
+ sBindingControlledProperty = PROPERTY_READONLY;
+ }
+
+ else if ( evt.PropertyName == PROPERTY_RELEVANT )
+ {
+ sBindingControlledProperty = PROPERTY_ENABLED;
+ bForwardToLabelControl = true;
+ }
+
+ else
+ return;
+ try
+ {
+ setPropertyValue( sBindingControlledProperty, evt.NewValue );
+ if ( bForwardToLabelControl && m_xLabelControl.is() )
+ m_xLabelControl->setPropertyValue( sBindingControlledProperty, evt.NewValue );
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ SAL_WARN("forms.component", "OBoundControlModel::propertyChange: could not adjust my binding-controlled property!");
+ }
+
+ }
+}
+
+void SAL_CALL OBoundControlModel::onRowSetChanged( const EventObject& /*i_Event*/ )
+{
+ ControlModelLock aLock( *this );
+ FieldChangeNotifier aBoundFieldNotifier( aLock );
+ // disconnect from database column (which is controlled by parent, directly or indirectly)
+ if ( hasField() )
+ impl_disconnectDatabaseColumn_noNotify();
+ // log off old listeners
+ if ( isFormListening() )
+ doFormListening( false );
+ // determine the new ambient form
+ impl_determineAmbientForm_nothrow();
+ // log on new listeners
+ doFormListening( true );
+ // re-connect to database column if needed and possible
+ if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() )
+ impl_connectDatabaseColumn_noNotify( false );
+}
+
+// XBoundComponent
+void SAL_CALL OBoundControlModel::addUpdateListener(const Reference<XUpdateListener>& _rxListener)
+{
+ m_aUpdateListeners.addInterface(_rxListener);
+}
+
+void SAL_CALL OBoundControlModel::removeUpdateListener(const Reference< XUpdateListener>& _rxListener)
+{
+ m_aUpdateListeners.removeInterface(_rxListener);
+}
+
+sal_Bool SAL_CALL OBoundControlModel::commit()
+{
+ ControlModelLock aLock( *this );
+ OSL_PRECOND( m_bCommitable, "OBoundControlModel::commit: invalid call (I'm not committable!) " );
+ if ( hasExternalValueBinding() )
+ {
+ // in most cases, no action is required: For most derivees, we know the value property of
+ // our control (see initValueProperty), and when an external binding is active, we
+ // instantly forward all changes in this property to the external binding.
+ if ( m_sValuePropertyName.isEmpty() )
+ // but for those derivees which did not use this feature, we need an
+ // explicit transfer
+ transferControlValueToExternal( aLock );
+ return true;
+ }
+
+ OSL_ENSURE( !hasExternalValueBinding(), "OBoundControlModel::commit: control flow broken!" );
+ // we reach this only if we're not working with an external binding
+ if ( !hasField() )
+ return true;
+ ::comphelper::OInterfaceIteratorHelper3 aIter( m_aUpdateListeners );
+ EventObject aEvent;
+ aEvent.Source = static_cast< XWeak* >( this );
+ bool bSuccess = true;
+ aLock.release();
+ // UNSAFE >
+ while (aIter.hasMoreElements() && bSuccess)
+ bSuccess = aIter.next()->approveUpdate( aEvent );
+ // < UNSAFE
+ aLock.acquire();
+ if ( bSuccess )
+ {
+ try
+ {
+ if ( m_xColumnUpdate.is() )
+ bSuccess = commitControlValueToDbColumn( false );
+ }
+
+ catch(const Exception&)
+ {
+ bSuccess = false;
+ }
+
+ }
+
+ if ( bSuccess )
+ {
+ aLock.release();
+ m_aUpdateListeners.notifyEach( &XUpdateListener::updated, aEvent );
+ }
+ return bSuccess;
+}
+
+void OBoundControlModel::resetField()
+{
+ m_xColumnUpdate.clear();
+ m_xColumn.clear();
+ m_xField.clear();
+ m_nFieldType = DataType::OTHER;
+}
+
+void OBoundControlModel::connectToField(const Reference<XRowSet>& rForm)
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::connectToField: invalid call (have an external binding)!" );
+ // if there's a connection to the database
+ if (!(rForm.is() && getConnection(rForm).is()))
+ return;
+
+ // determine field and PropertyChangeListener
+ m_xCursor = rForm;
+ Reference<XPropertySet> xFieldCandidate;
+ if (m_xCursor.is())
+ {
+ Reference<XColumnsSupplier> xColumnsSupplier(m_xCursor, UNO_QUERY);
+ DBG_ASSERT(xColumnsSupplier.is(), "OBoundControlModel::connectToField : the row set should support the css::sdb::ResultSet service !");
+ if (xColumnsSupplier.is())
+ {
+ Reference<XNameAccess> xColumns = xColumnsSupplier->getColumns();
+ if (xColumns.is() && xColumns->hasByName(m_aControlSource))
+ {
+ OSL_VERIFY( xColumns->getByName(m_aControlSource) >>= xFieldCandidate );
+ }
+
+ }
+
+ }
+
+ try
+ {
+ sal_Int32 nFieldType = DataType::OTHER;
+ if ( xFieldCandidate.is() )
+ {
+ xFieldCandidate->getPropertyValue( PROPERTY_FIELDTYPE ) >>= nFieldType;
+ if ( approveDbColumnType( nFieldType ) )
+ impl_setField_noNotify( xFieldCandidate );
+ }
+
+ else
+ impl_setField_noNotify( nullptr );
+ if ( m_xField.is() )
+ {
+ if ( m_xField->getPropertySetInfo()->hasPropertyByName( PROPERTY_VALUE ) )
+ {
+ m_nFieldType = nFieldType;
+ // listen to changing values
+ m_xField->addPropertyChangeListener( PROPERTY_VALUE, this );
+ m_xColumnUpdate.set( m_xField, UNO_QUERY );
+ m_xColumn.set( m_xField, UNO_QUERY );
+ sal_Int32 nNullableFlag = ColumnValue::NO_NULLS;
+ m_xField->getPropertyValue(PROPERTY_ISNULLABLE) >>= nNullableFlag;
+ m_bRequired = (ColumnValue::NO_NULLS == nNullableFlag);
+ // we're optimistic: in case of ColumnValue_NULLABLE_UNKNOWN we assume nullability...
+ }
+ else
+ {
+ SAL_WARN("forms.component", "OBoundControlModel::connectToField: property " << PROPERTY_VALUE << " not supported!");
+ impl_setField_noNotify( nullptr );
+ }
+
+ }
+
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ resetField();
+ }
+}
+
+void OBoundControlModel::initFromField( const Reference< XRowSet >& _rxRowSet )
+{
+ // but only if the rowset is positioned on a valid record
+ if ( !(hasField() && _rxRowSet.is()) )
+ return;
+
+ bool shouldTransfer(!_rxRowSet->isBeforeFirst() && !_rxRowSet->isAfterLast());
+ if (!shouldTransfer)
+ {
+ const Reference< XPropertySet > xPS(_rxRowSet, UNO_QUERY);
+ if (xPS.is())
+ {
+ assert(!shouldTransfer);
+ xPS->getPropertyValue("IsNew") >>= shouldTransfer;
+ }
+ }
+ if ( shouldTransfer )
+ transferDbValueToControl();
+ else
+ // reset the field if the row set is empty
+ // #i30661#
+ resetNoBroadcast();
+}
+
+bool OBoundControlModel::approveDbColumnType(sal_Int32 _nColumnType)
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::approveDbColumnType: invalid call (have an external binding)!" );
+ if ((_nColumnType == DataType::BINARY) || (_nColumnType == DataType::VARBINARY)
+ || (_nColumnType == DataType::LONGVARBINARY) || (_nColumnType == DataType::OTHER)
+ || (_nColumnType == DataType::OBJECT) || (_nColumnType == DataType::DISTINCT)
+ || (_nColumnType == DataType::STRUCT) || (_nColumnType == DataType::ARRAY)
+ || (_nColumnType == DataType::BLOB) /*|| (_nColumnType == DataType::CLOB)*/
+ || (_nColumnType == DataType::REF) || (_nColumnType == DataType::SQLNULL))
+ return false;
+ return true;
+}
+
+void OBoundControlModel::impl_determineAmbientForm_nothrow()
+{
+ Reference< XInterface > xParent( getParent() );
+ m_xAmbientForm.set( xParent, UNO_QUERY );
+ if ( !m_xAmbientForm.is() )
+ {
+ Reference< XRowSetSupplier > xSupRowSet( xParent, UNO_QUERY );
+ if ( xSupRowSet.is() )
+ m_xAmbientForm.set( xSupRowSet->getRowSet(), UNO_QUERY );
+ }
+}
+
+void OBoundControlModel::impl_connectDatabaseColumn_noNotify( bool _bFromReload )
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::impl_connectDatabaseColumn_noNotify: not to be called with an external value binding!" );
+ // consistency checks
+ DBG_ASSERT( !( hasField() && !_bFromReload ),
+ "OBoundControlModel::impl_connectDatabaseColumn_noNotify: the form is just *loaded*, but we already have a field!" );
+
+ Reference< XRowSet > xRowSet( m_xAmbientForm, UNO_QUERY );
+ OSL_ENSURE( xRowSet.is(), "OBoundControlModel::impl_connectDatabaseColumn_noNotify: no row set!" );
+ if ( !xRowSet.is() )
+ return;
+ if ( !hasField() || _bFromReload )
+ {
+ // connect to the column
+ connectToField( xRowSet );
+ }
+
+ // now that we're connected (more or less, even if we did not find a column),
+ // we definitely want to forward any potentially occurring value changes
+ m_bForwardValueChanges = true;
+ // let derived classes react on this new connection
+ m_bLoaded = true;
+ onConnectedDbColumn( xRowSet );
+ // initially transfer the db column value to the control, if we successfully connected to a database column
+ if ( hasField() )
+ initFromField( xRowSet );
+}
+
+void OBoundControlModel::impl_disconnectDatabaseColumn_noNotify()
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::impl_disconnectDatabaseColumn_noNotify: not to be called with an external value binding!" );
+ // let derived classes react on this
+ onDisconnectedDbColumn();
+ if ( hasField() )
+ {
+ getField()->removePropertyChangeListener( PROPERTY_VALUE, this );
+ resetField();
+ }
+
+ m_xCursor = nullptr;
+ m_bLoaded = false;
+}
+
+// XLoadListener
+void SAL_CALL OBoundControlModel::loaded( const EventObject& _rEvent )
+{
+ ControlModelLock aLock( *this );
+ FieldChangeNotifier aBoundFieldNotifier( aLock );
+ OSL_ENSURE( _rEvent.Source == m_xAmbientForm, "OBoundControlModel::loaded: where does this come from?" );
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::loaded: we should never reach this with an external value binding!" );
+ if ( hasExternalValueBinding() )
+ return;
+ impl_connectDatabaseColumn_noNotify( false );
+}
+
+void SAL_CALL OBoundControlModel::unloaded( const css::lang::EventObject& /*aEvent*/ )
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::unloaded: we should never reach this with an external value binding!" );
+}
+
+void SAL_CALL OBoundControlModel::reloading( const css::lang::EventObject& /*aEvent*/ )
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::reloading: we should never reach this with an external value binding!" );
+ if ( hasExternalValueBinding() )
+ return;
+ osl::MutexGuard aGuard(m_aMutex);
+ m_bForwardValueChanges = false;
+}
+
+void SAL_CALL OBoundControlModel::unloading(const css::lang::EventObject& /*aEvent*/)
+{
+ ControlModelLock aLock( *this );
+ FieldChangeNotifier aBoundFieldNotifier( aLock );
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::unloading: we should never reach this with an external value binding!" );
+ if ( hasExternalValueBinding() )
+ return;
+ impl_disconnectDatabaseColumn_noNotify();
+}
+
+void SAL_CALL OBoundControlModel::reloaded( const EventObject& _rEvent )
+{
+ ControlModelLock aLock( *this );
+ FieldChangeNotifier aBoundFieldNotifier( aLock );
+ OSL_ENSURE( _rEvent.Source == m_xAmbientForm, "OBoundControlModel::reloaded: where does this come from?" );
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::reloaded: we should never reach this with an external value binding!" );
+ if ( hasExternalValueBinding() )
+ return;
+ impl_connectDatabaseColumn_noNotify( true );
+}
+
+void OBoundControlModel::setControlValue( const Any& _rValue, ValueChangeInstigator _eInstigator )
+{
+ m_eControlValueChangeInstigator = _eInstigator;
+ doSetControlValue( _rValue );
+ m_eControlValueChangeInstigator = eOther;
+}
+
+void OBoundControlModel::doSetControlValue( const Any& _rValue )
+{
+ OSL_PRECOND( m_xAggregateFastSet.is() && m_xAggregateSet.is(),
+ "OBoundControlModel::doSetControlValue: invalid aggregate !" );
+ OSL_PRECOND( !m_sValuePropertyName.isEmpty() || ( m_nValuePropertyAggregateHandle != -1 ),
+ "OBoundControlModel::doSetControlValue: please override if you have own value property handling!" );
+ try
+ {
+ // release our mutex once (it's acquired in one of the calling methods), as setting aggregate properties
+ // may cause any uno controls belonging to us to lock the solar mutex, which is potentially dangerous with
+ // our own mutex locked
+ MutexRelease aRelease( m_aMutex );
+ if ( ( m_nValuePropertyAggregateHandle != -1 ) && m_xAggregateFastSet.is() )
+ {
+ m_xAggregateFastSet->setFastPropertyValue( m_nValuePropertyAggregateHandle, _rValue );
+ }
+
+ else if ( !m_sValuePropertyName.isEmpty() && m_xAggregateSet.is() )
+ {
+ m_xAggregateSet->setPropertyValue( m_sValuePropertyName, _rValue );
+ }
+
+ }
+
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::doSetControlValue");
+ }
+}
+
+void OBoundControlModel::onConnectedValidator( )
+{
+ try
+ {
+ // if we have an external validator, we do not want the control to force invalid
+ // inputs to the default value. Instead, invalid inputs should be translated
+ // to NaN (not a number)
+ Reference< XPropertySetInfo > xAggregatePropertyInfo;
+ if ( m_xAggregateSet.is() )
+ xAggregatePropertyInfo = m_xAggregateSet->getPropertySetInfo();
+ if ( xAggregatePropertyInfo.is() && xAggregatePropertyInfo->hasPropertyByName( PROPERTY_ENFORCE_FORMAT ) )
+ m_xAggregateSet->setPropertyValue( PROPERTY_ENFORCE_FORMAT, Any( false ) );
+ }
+
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::onConnectedValidator");
+ }
+
+ recheckValidity( false );
+}
+
+void OBoundControlModel::onDisconnectedValidator( )
+{
+ try
+ {
+ Reference< XPropertySetInfo > xAggregatePropertyInfo;
+ if ( m_xAggregateSet.is() )
+ xAggregatePropertyInfo = m_xAggregateSet->getPropertySetInfo();
+ if ( xAggregatePropertyInfo.is() && xAggregatePropertyInfo->hasPropertyByName( PROPERTY_ENFORCE_FORMAT ) )
+ m_xAggregateSet->setPropertyValue( PROPERTY_ENFORCE_FORMAT, Any( true ) );
+ }
+
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::onDisconnectedValidator");
+ }
+
+ recheckValidity( false );
+}
+
+void OBoundControlModel::onConnectedExternalValue( )
+{
+ calculateExternalValueType();
+}
+
+void OBoundControlModel::onConnectedDbColumn( const Reference< XInterface >& /*_rxForm*/ )
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::onConnectedDbColumn: how this? There's an external value binding!" );
+}
+
+void OBoundControlModel::onDisconnectedDbColumn()
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::onDisconnectedDbColumn: how this? There's an external value binding!" );
+}
+
+// XReset
+Any OBoundControlModel::getDefaultForReset() const
+{
+ return Any();
+}
+
+void OBoundControlModel::resetNoBroadcast()
+{
+ setControlValue( getDefaultForReset(), eOther );
+}
+
+void OBoundControlModel::addResetListener(const Reference<XResetListener>& l)
+{
+ m_aResetHelper.addResetListener( l );
+}
+
+void OBoundControlModel::removeResetListener(const Reference<XResetListener>& l)
+{
+ m_aResetHelper.removeResetListener( l );
+}
+
+void OBoundControlModel::reset()
+{
+ if ( !m_aResetHelper.approveReset() )
+ return;
+ ControlModelLock aLock( *this );
+ // on a new record?
+ bool bIsNewRecord = false;
+ Reference<XPropertySet> xSet( m_xCursor, UNO_QUERY );
+ if ( xSet.is() )
+ {
+ try
+ {
+ xSet->getPropertyValue( PROPERTY_ISNEW ) >>= bIsNewRecord;
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ }
+
+ // cursor on an invalid row?
+ bool bInvalidCursorPosition = true;
+ try
+ {
+ bInvalidCursorPosition = m_xCursor.is()
+ && ( m_xCursor->isAfterLast()
+ || m_xCursor->isBeforeFirst()
+ )
+ && !bIsNewRecord;
+ }
+
+ catch( const SQLException& )
+ {
+ TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::reset: caught an SQL exception!" );
+ }
+
+ // #i24495# - don't count the insert row as "invalid"
+ bool bSimpleReset =
+ ( !m_xColumn.is() // no connection to a database column
+ || ( m_xCursor.is() // OR we have an improperly positioned cursor
+ && bInvalidCursorPosition
+ )
+ || hasExternalValueBinding() // OR we have an external value binding
+ );
+ if ( !bSimpleReset )
+ {
+ // The default values will be set if and only if the current value of the field which we're bound
+ // to is NULL.
+ // Else, the current field value should be refreshed
+ // This behaviour is not completely ... "matured": What should happen if the field as well as the
+ // control have a default value?
+ bool bIsNull = true;
+ // we have to access the field content at least once to get a reliable result by XColumn::wasNull
+ try
+ {
+ // normally, we'd do a getString here. However, this is extremely expensive in the case
+ // of binary fields. Unfortunately, getString is the only method which is guaranteed
+ // to *always* succeed, all other getXXX methods may fail if the column is asked for a
+ // non-convertible type
+ sal_Int32 nFieldType = DataType::OBJECT;
+ getField()->getPropertyValue( PROPERTY_FIELDTYPE ) >>= nFieldType;
+ if ( ( nFieldType == DataType::BINARY )
+ || ( nFieldType == DataType::VARBINARY )
+ || ( nFieldType == DataType::LONGVARBINARY )
+ || ( nFieldType == DataType::OBJECT )
+ /*|| ( nFieldType == DataType::CLOB )*/
+ )
+ m_xColumn->getBinaryStream();
+ else if ( nFieldType == DataType::BLOB )
+ m_xColumn->getBlob();
+ else
+ m_xColumn->getString();
+ bIsNull = m_xColumn->wasNull();
+ }
+
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ SAL_WARN("forms.component", "OBoundControlModel::reset: this should have succeeded in all cases!");
+ }
+
+ bool bNeedValueTransfer = true;
+ if ( bIsNull )
+ {
+ if ( bIsNewRecord )
+ {
+ // reset the control to its default
+ resetNoBroadcast();
+ // and immediately commit the changes to the DB column, to keep consistency
+ commitControlValueToDbColumn( true );
+ bNeedValueTransfer = false;
+ }
+
+ }
+
+ if ( bNeedValueTransfer )
+ transferDbValueToControl();
+ }
+
+ else
+ {
+ resetNoBroadcast();
+ // transfer to the external binding, if necessary
+ if ( hasExternalValueBinding() )
+ transferControlValueToExternal( aLock );
+ }
+
+ // revalidate, if necessary
+ if ( hasValidator() )
+ recheckValidity( true );
+ aLock.release();
+ m_aResetHelper.notifyResetted();
+}
+
+void OBoundControlModel::impl_setField_noNotify( const Reference< XPropertySet>& _rxField )
+{
+ DBG_ASSERT( !hasExternalValueBinding(), "OBoundControlModel::impl_setField_noNotify: We have an external value binding!" );
+ m_xField = _rxField;
+}
+
+bool OBoundControlModel::impl_approveValueBinding_nolock( const Reference< XValueBinding >& _rxBinding )
+{
+ if ( !_rxBinding.is() )
+ return false;
+ Sequence< Type > aTypeCandidates;
+ {
+ // SYNCHRONIZED >
+ ::osl::MutexGuard aGuard( m_aMutex );
+ aTypeCandidates = getSupportedBindingTypes();
+ // < SYNCHRONIZED
+ }
+
+ for ( auto const & type : std::as_const(aTypeCandidates) )
+ {
+ if ( _rxBinding->supportsType( type ) )
+ return true;
+ }
+ return false;
+}
+
+void OBoundControlModel::connectExternalValueBinding(
+ const Reference< XValueBinding >& _rxBinding, ControlModelLock& _rInstanceLock )
+{
+ OSL_PRECOND( _rxBinding.is(), "OBoundControlModel::connectExternalValueBinding: invalid binding instance!" );
+ OSL_PRECOND( !hasExternalValueBinding( ), "OBoundControlModel::connectExternalValueBinding: precond not met (currently have a binding)!" );
+ // if we're connected to a database column, suspend this
+ if ( hasField() )
+ impl_disconnectDatabaseColumn_noNotify();
+ // suspend listening for load-related events at out ambient form.
+ // This is because an external value binding overrules a possible database binding.
+ if ( isFormListening() )
+ doFormListening( false );
+ // remember this new binding
+ m_xExternalBinding = _rxBinding;
+ // tell the derivee
+ onConnectedExternalValue();
+ try
+ {
+ // add as value listener so we get notified when the value changes
+ Reference< XModifyBroadcaster > xModifiable( m_xExternalBinding, UNO_QUERY );
+ if ( xModifiable.is() )
+ xModifiable->addModifyListener( this );
+ // add as property change listener for some (possibly present) properties we're
+ // interested in
+ Reference< XPropertySet > xBindingProps( m_xExternalBinding, UNO_QUERY );
+ Reference< XPropertySetInfo > xBindingPropsInfo( xBindingProps.is() ? xBindingProps->getPropertySetInfo() : Reference< XPropertySetInfo >() );
+ if ( xBindingPropsInfo.is() )
+ {
+ if ( xBindingPropsInfo->hasPropertyByName( PROPERTY_READONLY ) )
+ {
+ xBindingProps->addPropertyChangeListener( PROPERTY_READONLY, this );
+ m_bBindingControlsRO = true;
+ }
+
+ if ( xBindingPropsInfo->hasPropertyByName( PROPERTY_RELEVANT ) )
+ {
+ xBindingProps->addPropertyChangeListener( PROPERTY_RELEVANT, this );
+ m_bBindingControlsEnable = true;
+ }
+
+ }
+
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ // propagate our new value
+ transferExternalValueToControl( _rInstanceLock );
+ // if the binding is also a validator, use it, too. This is a constraint of the
+ // com.sun.star.form.binding.ValidatableBindableFormComponent service
+ if ( !m_bSupportsValidation )
+ return;
+
+ try
+ {
+ Reference< XValidator > xAsValidator( _rxBinding, UNO_QUERY );
+ if ( xAsValidator.is() )
+ setValidator( xAsValidator );
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+}
+
+void OBoundControlModel::disconnectExternalValueBinding( )
+{
+ try
+ {
+ // not listening at the binding anymore
+ Reference< XModifyBroadcaster > xModifiable( m_xExternalBinding, UNO_QUERY );
+ if ( xModifiable.is() )
+ xModifiable->removeModifyListener( this );
+ // remove as property change listener
+ Reference< XPropertySet > xBindingProps( m_xExternalBinding, UNO_QUERY );
+ if ( m_bBindingControlsRO )
+ xBindingProps->removePropertyChangeListener( PROPERTY_READONLY, this );
+ if ( m_bBindingControlsEnable )
+ xBindingProps->removePropertyChangeListener( PROPERTY_RELEVANT, this );
+ }
+
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::disconnectExternalValueBinding");
+ }
+
+ // if the binding also acts as our validator, disconnect the validator, too
+ if ( ( m_xExternalBinding == m_xValidator ) && m_xValidator.is() )
+ disconnectValidator( );
+ // no binding anymore
+ m_xExternalBinding.clear();
+ // be a load listener at our form, again. This was suspended while we had
+ // an external value binding in place.
+ doFormListening( true );
+ // re-connect to database column of the new parent
+ if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() )
+ impl_connectDatabaseColumn_noNotify( false );
+}
+
+void SAL_CALL OBoundControlModel::setValueBinding( const Reference< XValueBinding >& _rxBinding )
+{
+ OSL_PRECOND( m_bSupportsExternalBinding, "OBoundControlModel::setValueBinding: How did you reach this method?" );
+ // the interface for this method should not have been exposed if we do not
+ // support binding to external data
+ // allow reset
+ if ( _rxBinding.is() && !impl_approveValueBinding_nolock( _rxBinding ) )
+ {
+ throw IncompatibleTypesException(
+ ResourceManager::loadString(RID_STR_INCOMPATIBLE_TYPES),
+ *this
+ );
+ }
+
+ ControlModelLock aLock( *this );
+ // since a ValueBinding overrules any potentially active database binding, the change in a ValueBinding
+ // might trigger a change in our BoundField.
+ FieldChangeNotifier aBoundFieldNotifier( aLock );
+ // disconnect from the old binding
+ if ( hasExternalValueBinding() )
+ disconnectExternalValueBinding( );
+ // connect to the new binding
+ if ( _rxBinding.is() )
+ connectExternalValueBinding( _rxBinding, aLock );
+}
+
+Reference< XValueBinding > SAL_CALL OBoundControlModel::getValueBinding( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ OSL_PRECOND( m_bSupportsExternalBinding, "OBoundControlModel::getValueBinding: How did you reach this method?" );
+ // the interface for this method should not have been exposed if we do not
+ // support binding to external data
+ return m_xExternalBinding;
+}
+
+void SAL_CALL OBoundControlModel::modified( const EventObject& _rEvent )
+{
+ ControlModelLock aLock( *this );
+ OSL_PRECOND( hasExternalValueBinding(), "OBoundControlModel::modified: Where did this come from?" );
+ if ( !m_bTransferringValue && ( m_xExternalBinding == _rEvent.Source ) && m_xExternalBinding.is() )
+ {
+ transferExternalValueToControl( aLock );
+ }
+}
+
+void OBoundControlModel::transferDbValueToControl( )
+{
+ try
+ {
+ setControlValue( translateDbColumnToControlValue(), eDbColumnBinding );
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+}
+
+void OBoundControlModel::transferExternalValueToControl( ControlModelLock& _rInstanceLock )
+{
+ Reference< XValueBinding > xExternalBinding( m_xExternalBinding );
+ Type aValueExchangeType( getExternalValueType() );
+ _rInstanceLock.release();
+ // UNSAFE >
+ Any aExternalValue;
+ try
+ {
+ aExternalValue = xExternalBinding->getValue( aValueExchangeType );
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ // < UNSAFE
+ _rInstanceLock.acquire();
+ setControlValue( translateExternalValueToControlValue( aExternalValue ), eExternalBinding );
+}
+
+void OBoundControlModel::transferControlValueToExternal( ControlModelLock& _rInstanceLock )
+{
+ OSL_PRECOND( m_bSupportsExternalBinding && hasExternalValueBinding(),
+ "OBoundControlModel::transferControlValueToExternal: precondition not met!" );
+ if ( !m_xExternalBinding.is() )
+ return;
+
+ Any aExternalValue( translateControlValueToExternalValue() );
+ m_bTransferringValue = true;
+ _rInstanceLock.release();
+ // UNSAFE >
+ try
+ {
+ m_xExternalBinding->setValue( aExternalValue );
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ // < UNSAFE
+ _rInstanceLock.acquire();
+ m_bTransferringValue = false;
+}
+
+Sequence< Type > OBoundControlModel::getSupportedBindingTypes()
+{
+ return Sequence< Type >( &m_aValuePropertyType, 1 );
+}
+
+void OBoundControlModel::calculateExternalValueType()
+{
+ m_aExternalValueType = Type();
+ if ( !m_xExternalBinding.is() )
+ return;
+ const Sequence< Type > aTypeCandidates( getSupportedBindingTypes() );
+ for ( auto const & typeCandidate : aTypeCandidates )
+ {
+ if ( m_xExternalBinding->supportsType( typeCandidate ) )
+ {
+ m_aExternalValueType = typeCandidate;
+ break;
+ }
+ }
+}
+
+Any OBoundControlModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+{
+ OSL_PRECOND( m_bSupportsExternalBinding && hasExternalValueBinding(),
+ "OBoundControlModel::translateExternalValueToControlValue: precondition not met!" );
+ Any aControlValue( _rExternalValue );
+ // if the external value is VOID, and our value property is not allowed to be VOID,
+ // then default-construct a value
+ if ( !aControlValue.hasValue() && !m_bValuePropertyMayBeVoid )
+ aControlValue.setValue( nullptr, m_aValuePropertyType );
+ // out of here
+ return aControlValue;
+}
+
+Any OBoundControlModel::translateControlValueToExternalValue( ) const
+{
+ return getControlValue( );
+}
+
+Any OBoundControlModel::translateControlValueToValidatableValue( ) const
+{
+ OSL_PRECOND( m_xValidator.is(), "OBoundControlModel::translateControlValueToValidatableValue: no validator, so why should I?" );
+ if ( ( m_xValidator == m_xExternalBinding ) && m_xValidator.is() )
+ return translateControlValueToExternalValue();
+ return getControlValue();
+}
+
+Any OBoundControlModel::getControlValue( ) const
+{
+ OSL_PRECOND( m_xAggregateFastSet.is() && m_xAggregateSet.is(),
+ "OBoundControlModel::getControlValue: invalid aggregate !" );
+ OSL_PRECOND( !m_sValuePropertyName.isEmpty() || ( m_nValuePropertyAggregateHandle != -1 ),
+ "OBoundControlModel::getControlValue: please override if you have own value property handling!" );
+ // determine the current control value
+ Any aControlValue;
+ if ( ( m_nValuePropertyAggregateHandle != -1 ) && m_xAggregateFastSet.is() )
+ {
+ aControlValue = m_xAggregateFastSet->getFastPropertyValue( m_nValuePropertyAggregateHandle );
+ }
+
+ else if ( !m_sValuePropertyName.isEmpty() && m_xAggregateSet.is() )
+ {
+ aControlValue = m_xAggregateSet->getPropertyValue( m_sValuePropertyName );
+ }
+ return aControlValue;
+}
+
+void OBoundControlModel::connectValidator( const Reference< XValidator >& _rxValidator )
+{
+ OSL_PRECOND( _rxValidator.is(), "OBoundControlModel::connectValidator: invalid validator instance!" );
+ OSL_PRECOND( !hasValidator( ), "OBoundControlModel::connectValidator: precond not met (have a validator currently)!" );
+ m_xValidator = _rxValidator;
+
+ // add as value listener so we get notified when the value changes
+ if ( m_xValidator.is() )
+ {
+ try
+ {
+ m_xValidator->addValidityConstraintListener( this );
+ }
+
+ catch( const RuntimeException& )
+ {
+ }
+ }
+ onConnectedValidator( );
+}
+
+void OBoundControlModel::disconnectValidator( )
+{
+ OSL_PRECOND( hasValidator( ), "OBoundControlModel::connectValidator: precond not met (don't have a validator currently)!" );
+
+ // add as value listener so we get notified when the value changes
+ if ( m_xValidator.is() )
+ {
+ try
+ {
+ m_xValidator->removeValidityConstraintListener( this );
+ }
+
+ catch( const RuntimeException& )
+ {
+ }
+ }
+
+ m_xValidator.clear();
+
+ onDisconnectedValidator( );
+}
+
+void SAL_CALL OBoundControlModel::setValidator( const Reference< XValidator >& _rxValidator )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+ OSL_PRECOND( m_bSupportsValidation, "OBoundControlModel::setValidator: How did you reach this method?" );
+ // the interface for this method should not have been exposed if we do not
+ // support validation
+
+ // early out if the validator does not change
+ if ( _rxValidator == m_xValidator )
+ return;
+
+ if ( m_xValidator.is() && ( m_xValidator == m_xExternalBinding ) )
+ throw VetoException(
+ ResourceManager::loadString(RID_STR_INVALID_VALIDATOR),
+ *this
+ );
+
+ // disconnect from the old validator
+ if ( hasValidator() )
+ disconnectValidator( );
+
+ // connect to the new validator
+ if ( _rxValidator.is() )
+ connectValidator( _rxValidator );
+}
+
+Reference< XValidator > SAL_CALL OBoundControlModel::getValidator( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ OSL_PRECOND( m_bSupportsValidation, "OBoundControlModel::getValidator: How did you reach this method?" );
+ // the interface for this method should not have been exposed if we do not
+ // support validation
+
+ return m_xValidator;
+}
+
+void SAL_CALL OBoundControlModel::validityConstraintChanged( const EventObject& /*Source*/ )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+ OSL_PRECOND( m_bSupportsValidation, "OBoundControlModel::validityConstraintChanged: How did you reach this method?" );
+ // the interface for this method should not have been exposed if we do not
+ // support validation
+
+ recheckValidity( false );
+}
+
+sal_Bool SAL_CALL OBoundControlModel::isValid( )
+{
+ return m_bIsCurrentValueValid;
+}
+
+css::uno::Any OBoundControlModel::getCurrentFormComponentValue() const
+{
+ if ( hasValidator() )
+ return translateControlValueToValidatableValue();
+ return getControlValue();
+}
+
+Any SAL_CALL OBoundControlModel::getCurrentValue( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return getCurrentFormComponentValue();
+}
+
+void SAL_CALL OBoundControlModel::addFormComponentValidityListener( const Reference< validation::XFormComponentValidityListener >& Listener )
+{
+ if ( Listener.is() )
+ m_aFormComponentListeners.addInterface( Listener );
+}
+
+void SAL_CALL OBoundControlModel::removeFormComponentValidityListener( const Reference< validation::XFormComponentValidityListener >& Listener )
+{
+ if ( Listener.is() )
+ m_aFormComponentListeners.removeInterface( Listener );
+}
+
+void OBoundControlModel::recheckValidity( bool _bForceNotification )
+{
+ try
+ {
+ bool bIsCurrentlyValid = true;
+ if ( hasValidator() )
+ bIsCurrentlyValid = m_xValidator->isValid( translateControlValueToValidatableValue() );
+
+ if ( ( bIsCurrentlyValid != m_bIsCurrentValueValid ) || _bForceNotification )
+ {
+ m_bIsCurrentValueValid = bIsCurrentlyValid;
+
+ // release our mutex for the notifications
+ MutexRelease aRelease( m_aMutex );
+ m_aFormComponentListeners.notifyEach( &validation::XFormComponentValidityListener::componentValidityChanged, EventObject( *this ) );
+ }
+
+ }
+
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::recheckValidity");
+ }
+}
+
+void OBoundControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OControlModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 5);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROLSOURCE, PROPERTY_ID_CONTROLSOURCE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_BOUNDFIELD, PROPERTY_ID_BOUNDFIELD, cppu::UnoType<XPropertySet>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROLLABEL, PROPERTY_ID_CONTROLLABEL, cppu::UnoType<XPropertySet>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROLSOURCEPROPERTY, PROPERTY_ID_CONTROLSOURCEPROPERTY, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_INPUT_REQUIRED, PROPERTY_ID_INPUT_REQUIRED, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FormattedField.cxx b/forms/source/component/FormattedField.cxx
new file mode 100644
index 000000000..dbabcaf9b
--- /dev/null
+++ b/forms/source/component/FormattedField.cxx
@@ -0,0 +1,1022 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "FormattedField.hxx"
+#include <services.hxx>
+#include <property.hxx>
+#include <propertybaghelper.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/numbers.hxx>
+#include <comphelper/types.hxx>
+#include <connectivity/dbtools.hxx>
+#include <connectivity/dbconversion.hxx>
+#include <o3tl/any.hxx>
+#include <svl/numformat.hxx>
+#include <svl/numuno.hxx>
+#include <vcl/keycodes.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <tools/debug.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/util/NumberFormat.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <com/sun/star/awt/MouseEvent.hpp>
+#include <com/sun/star/form/XSubmit.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/util/XNumberFormatTypes.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <osl/mutex.hxx>
+// needed as long as we use the SolarMutex
+#include <comphelper/streamsection.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <unotools/desktopterminationobserver.hxx>
+#include <vector>
+#include <algorithm>
+
+
+using namespace dbtools;
+using namespace css::uno;
+using namespace css::sdb;
+using namespace css::sdbc;
+using namespace css::sdbcx;
+using namespace css::beans;
+using namespace css::container;
+using namespace css::form;
+using namespace css::awt;
+using namespace css::io;
+using namespace css::lang;
+using namespace css::util;
+using namespace css::form::binding;
+
+namespace frm
+{
+namespace {
+
+class StandardFormatsSupplier : public SvNumberFormatsSupplierObj, public ::utl::ITerminationListener
+{
+protected:
+ std::unique_ptr<SvNumberFormatter> m_pMyPrivateFormatter;
+ static WeakReference< XNumberFormatsSupplier > s_xDefaultFormatsSupplier;
+public:
+ static Reference< XNumberFormatsSupplier > get( const Reference< XComponentContext >& _rxORB );
+protected:
+ StandardFormatsSupplier(const Reference< XComponentContext >& _rxFactory,LanguageType _eSysLanguage);
+ virtual ~StandardFormatsSupplier() override;
+protected:
+ virtual bool queryTermination() const override;
+ virtual void notifyTermination() override;
+};
+
+}
+
+WeakReference< XNumberFormatsSupplier > StandardFormatsSupplier::s_xDefaultFormatsSupplier;
+StandardFormatsSupplier::StandardFormatsSupplier(const Reference< XComponentContext > & _rxContext,LanguageType _eSysLanguage)
+ :m_pMyPrivateFormatter(new SvNumberFormatter(_rxContext, _eSysLanguage))
+{
+ SetNumberFormatter(m_pMyPrivateFormatter.get());
+ // #i29147#
+ ::utl::DesktopTerminationObserver::registerTerminationListener( this );
+}
+StandardFormatsSupplier::~StandardFormatsSupplier()
+{
+ ::utl::DesktopTerminationObserver::revokeTerminationListener( this );
+}
+Reference< XNumberFormatsSupplier > StandardFormatsSupplier::get( const Reference< XComponentContext >& _rxORB )
+{
+ LanguageType eSysLanguage = LANGUAGE_SYSTEM;
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ Reference< XNumberFormatsSupplier > xSupplier = s_xDefaultFormatsSupplier;
+ if ( xSupplier.is() )
+ return xSupplier;
+ // get the Office's locale
+ eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
+ }
+ rtl::Reference<StandardFormatsSupplier> pSupplier = new StandardFormatsSupplier( _rxORB, eSysLanguage );
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ Reference< XNumberFormatsSupplier > xSupplier = s_xDefaultFormatsSupplier;
+ if ( xSupplier.is() )
+ // somebody used the small time frame where the mutex was not locked to create and set
+ // the supplier
+ return xSupplier;
+ s_xDefaultFormatsSupplier = css::uno::Reference<css::uno::XWeak>(pSupplier);
+ }
+ return pSupplier;
+}
+bool StandardFormatsSupplier::queryTermination() const
+{
+ return true;
+}
+void StandardFormatsSupplier::notifyTermination()
+{
+ Reference< XNumberFormatsSupplier > xKeepAlive = this;
+ // when the application is terminating, release our static reference so that we are cleared/destructed
+ // earlier than upon unloading the library
+ // #i29147#
+ s_xDefaultFormatsSupplier = WeakReference< XNumberFormatsSupplier >( );
+ SetNumberFormatter( nullptr );
+ m_pMyPrivateFormatter.reset();
+}
+Sequence<Type> OFormattedControl::_getTypes()
+{
+ return ::comphelper::concatSequences(
+ OFormattedControl_BASE::getTypes(),
+ OBoundControl::_getTypes()
+ );
+}
+Any SAL_CALL OFormattedControl::queryAggregation(const Type& _rType)
+{
+ Any aReturn = OBoundControl::queryAggregation(_rType);
+ if (!aReturn.hasValue())
+ aReturn = OFormattedControl_BASE::queryInterface(_rType);
+ return aReturn;
+}
+OFormattedControl::OFormattedControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_FORMATTEDFIELD)
+ ,m_nKeyEvent(nullptr)
+{
+ osl_atomic_increment(&m_refCount);
+ {
+ Reference<XWindow> xComp;
+ if (query_aggregation(m_xAggregate, xComp))
+ {
+ xComp->addKeyListener(this);
+ }
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+OFormattedControl::~OFormattedControl()
+{
+ if( m_nKeyEvent )
+ Application::RemoveUserEvent( m_nKeyEvent );
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+}
+
+// XKeyListener
+void OFormattedControl::disposing(const EventObject& _rSource)
+{
+ OBoundControl::disposing(_rSource);
+}
+void OFormattedControl::keyPressed(const css::awt::KeyEvent& e)
+{
+ if( e.KeyCode != KEY_RETURN || e.Modifiers != 0 )
+ return;
+ // Is the control located in a form with a Submit URL?
+ Reference<css::beans::XPropertySet> xSet(getModel(), UNO_QUERY);
+ if( !xSet.is() )
+ return;
+ Reference<XFormComponent> xFComp(xSet, UNO_QUERY);
+ css::uno::Reference<css::uno::XInterface> xParent = xFComp->getParent();
+ if( !xParent.is() )
+ return;
+ Reference<css::beans::XPropertySet> xFormSet(xParent, UNO_QUERY);
+ if( !xFormSet.is() )
+ return;
+ Any aTmp(xFormSet->getPropertyValue( PROPERTY_TARGET_URL ));
+ if (!aTmp.has<OUString>() ||
+ getString(aTmp).isEmpty() )
+ return;
+ Reference<XIndexAccess> xElements(xParent, UNO_QUERY);
+ sal_Int32 nCount = xElements->getCount();
+ if( nCount > 1 )
+ {
+ Reference<css::beans::XPropertySet> xFCSet;
+ for( sal_Int32 nIndex=0; nIndex < nCount; nIndex++ )
+ {
+ // Any aElement(xElements->getByIndex(nIndex));
+ xElements->getByIndex(nIndex) >>= xFCSet;
+ if (hasProperty(PROPERTY_CLASSID, xFCSet) &&
+ getINT16(xFCSet->getPropertyValue(PROPERTY_CLASSID)) == FormComponentType::TEXTFIELD)
+ {
+ // Found another Edit -> Do not submit then
+ if (xFCSet != xSet)
+ return;
+ }
+ }
+ }
+ // Because we're still in the Handler, execute submit asynchronously
+ if( m_nKeyEvent )
+ Application::RemoveUserEvent( m_nKeyEvent );
+ m_nKeyEvent = Application::PostUserEvent( LINK(this, OFormattedControl,
+ OnKeyPressed) );
+}
+
+void OFormattedControl::keyReleased(const css::awt::KeyEvent& /*e*/)
+{
+}
+
+IMPL_LINK_NOARG(OFormattedControl, OnKeyPressed, void*, void)
+{
+ m_nKeyEvent = nullptr;
+ Reference<XFormComponent> xFComp(getModel(), UNO_QUERY);
+ css::uno::Reference<css::uno::XInterface> xParent = xFComp->getParent();
+ Reference<XSubmit> xSubmit(xParent, UNO_QUERY);
+ if (xSubmit.is())
+ xSubmit->submit( Reference<XControl> (), css::awt::MouseEvent() );
+}
+
+css::uno::Sequence<OUString> OFormattedControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_FORMATTEDFIELD;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_FORMATTEDFIELD;
+ return aSupported;
+}
+
+void OFormattedModel::implConstruct()
+{
+ // members
+ m_bOriginalNumeric = false;
+ m_bNumeric = false;
+ m_xOriginalFormatter = nullptr;
+ m_nKeyType = NumberFormat::UNDEFINED;
+ m_aNullDate = DBTypeConversion::getStandardDate();
+ // default our formats supplier
+ osl_atomic_increment(&m_refCount);
+ setPropertyToDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER);
+ osl_atomic_decrement(&m_refCount);
+ startAggregatePropertyListening( PROPERTY_FORMATKEY );
+ startAggregatePropertyListening( PROPERTY_FORMATSSUPPLIER );
+}
+OFormattedModel::OFormattedModel(const Reference<XComponentContext>& _rxFactory)
+ :OEditBaseModel(_rxFactory, VCL_CONTROLMODEL_FORMATTEDFIELD, FRM_SUN_CONTROL_FORMATTEDFIELD, true, true )
+ // use the old control name for compytibility reasons
+ ,OErrorBroadcaster( OComponentHelper::rBHelper )
+{
+ implConstruct();
+ m_nClassId = FormComponentType::TEXTFIELD;
+ initValueProperty( PROPERTY_EFFECTIVE_VALUE, PROPERTY_ID_EFFECTIVE_VALUE );
+}
+OFormattedModel::OFormattedModel( const OFormattedModel* _pOriginal, const Reference< XComponentContext >& _rxFactory )
+ :OEditBaseModel( _pOriginal, _rxFactory )
+ ,OErrorBroadcaster( OComponentHelper::rBHelper )
+{
+ implConstruct();
+}
+
+OFormattedModel::~OFormattedModel()
+{
+}
+
+// XCloneable
+css::uno::Reference< css::util::XCloneable > SAL_CALL OFormattedModel::createClone()
+{
+ rtl::Reference<OFormattedModel> pClone = new OFormattedModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+void SAL_CALL OFormattedModel::disposing()
+{
+ OErrorBroadcaster::disposing();
+ OEditBaseModel::disposing();
+}
+
+// XServiceInfo
+css::uno::Sequence<OUString> OFormattedModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OEditBaseModel::getSupportedServiceNames();
+ sal_Int32 nOldLen = aSupported.getLength();
+ aSupported.realloc( nOldLen + 9 );
+ OUString* pStoreTo = aSupported.getArray() + nOldLen;
+ *pStoreTo++ = BINDABLE_CONTROL_MODEL;
+ *pStoreTo++ = DATA_AWARE_CONTROL_MODEL;
+ *pStoreTo++ = VALIDATABLE_CONTROL_MODEL;
+ *pStoreTo++ = BINDABLE_DATA_AWARE_CONTROL_MODEL;
+ *pStoreTo++ = VALIDATABLE_BINDABLE_CONTROL_MODEL;
+ *pStoreTo++ = FRM_SUN_COMPONENT_FORMATTEDFIELD;
+ *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_FORMATTEDFIELD;
+ *pStoreTo++ = BINDABLE_DATABASE_FORMATTED_FIELD;
+ *pStoreTo++ = FRM_COMPONENT_FORMATTEDFIELD;
+ return aSupported;
+}
+
+// XAggregation
+Any SAL_CALL OFormattedModel::queryAggregation(const Type& _rType)
+{
+ Any aReturn = OEditBaseModel::queryAggregation( _rType );
+ return aReturn.hasValue() ? aReturn : OErrorBroadcaster::queryInterface( _rType );
+}
+
+// XTypeProvider
+Sequence< Type > OFormattedModel::_getTypes()
+{
+ return ::comphelper::concatSequences(
+ OEditBaseModel::_getTypes(),
+ OErrorBroadcaster::getTypes()
+ );
+}
+
+// XPersistObject
+OUString SAL_CALL OFormattedModel::getServiceName()
+{
+ return FRM_COMPONENT_EDIT;
+}
+
+// XPropertySet
+void OFormattedModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OEditBaseModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 3);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_EMPTY_IS_NULL, PROPERTY_ID_EMPTY_IS_NULL, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_FILTERPROPOSAL, PROPERTY_ID_FILTERPROPOSAL, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+void OFormattedModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const
+{
+ OEditBaseModel::describeAggregateProperties( _rAggregateProps );
+ // TreatAsNumeric is not transient: we want to attach it to the UI
+ // This is necessary to make EffectiveDefault (which may be text or a number) meaningful
+ ModifyPropertyAttributes(_rAggregateProps, PROPERTY_TREATASNUMERIC, 0, PropertyAttribute::TRANSIENT);
+ // Same for FormatKey
+ // (though the paragraph above for the TreatAsNumeric does not hold anymore - we do not have an UI for this.
+ // But we have for the format key ...)
+ ModifyPropertyAttributes(_rAggregateProps, PROPERTY_FORMATKEY, 0, PropertyAttribute::TRANSIENT);
+ RemoveProperty(_rAggregateProps, PROPERTY_STRICTFORMAT);
+ // no strict format property for formatted fields: it does not make sense, 'cause
+ // there is no general way to decide which characters/sub strings are allowed during the input of an
+ // arbitrary formatted control
+}
+
+void OFormattedModel::setPropertyToDefaultByHandle(sal_Int32 nHandle)
+{
+ if (nHandle == PROPERTY_ID_FORMATSSUPPLIER)
+ {
+ Reference<XNumberFormatsSupplier> xSupplier = calcDefaultFormatsSupplier();
+ DBG_ASSERT(m_xAggregateSet.is(), "OFormattedModel::setPropertyToDefaultByHandle(FORMATSSUPPLIER) : have no aggregate !");
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_FORMATSSUPPLIER, Any(xSupplier));
+ }
+ else
+ OEditBaseModel::setPropertyToDefaultByHandle(nHandle);
+}
+
+void OFormattedModel::setPropertyToDefault(const OUString& aPropertyName)
+{
+ OPropertyArrayAggregationHelper& rPH = m_aPropertyBagHelper.getInfoHelper();
+ sal_Int32 nHandle = rPH.getHandleByName( aPropertyName );
+ if (nHandle == PROPERTY_ID_FORMATSSUPPLIER)
+ setPropertyToDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER);
+ else
+ OEditBaseModel::setPropertyToDefault(aPropertyName);
+}
+
+Any OFormattedModel::getPropertyDefaultByHandle( sal_Int32 nHandle ) const
+{
+ if (nHandle == PROPERTY_ID_FORMATSSUPPLIER)
+ {
+ Reference<XNumberFormatsSupplier> xSupplier = calcDefaultFormatsSupplier();
+ return Any(xSupplier);
+ }
+ else
+ return OEditBaseModel::getPropertyDefaultByHandle(nHandle);
+}
+
+Any SAL_CALL OFormattedModel::getPropertyDefault( const OUString& aPropertyName )
+{
+ OPropertyArrayAggregationHelper& rPH = m_aPropertyBagHelper.getInfoHelper();
+ sal_Int32 nHandle = rPH.getHandleByName( aPropertyName );
+ if (nHandle == PROPERTY_ID_FORMATSSUPPLIER)
+ return getPropertyDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER);
+ else
+ return OEditBaseModel::getPropertyDefault(aPropertyName);
+}
+
+void OFormattedModel::_propertyChanged( const css::beans::PropertyChangeEvent& evt )
+{
+ // TODO: check how this works with external bindings
+ OSL_ENSURE( evt.Source == m_xAggregateSet, "OFormattedModel::_propertyChanged: where did this come from?" );
+ if ( evt.Source != m_xAggregateSet )
+ return;
+
+ if ( evt.PropertyName == PROPERTY_FORMATKEY )
+ {
+ if ( evt.NewValue.getValueType().getTypeClass() == TypeClass_LONG )
+ {
+ try
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ Reference<XNumberFormatsSupplier> xSupplier( calcFormatsSupplier() );
+ m_nKeyType = getNumberFormatType(xSupplier->getNumberFormats(), getINT32( evt.NewValue ) );
+ // as m_aSaveValue (which is used by commitControlValueToDbColumn) is format dependent we have
+ // to recalc it, which is done by translateDbColumnToControlValue
+ if ( m_xColumn.is() && m_xAggregateFastSet.is() && !m_xCursor->isBeforeFirst() && !m_xCursor->isAfterLast())
+ {
+ setControlValue( translateDbColumnToControlValue(), eOther );
+ }
+ // if we're connected to an external value binding, then re-calculate the type
+ // used to exchange the value - it depends on the format, too
+ if ( hasExternalValueBinding() )
+ {
+ calculateExternalValueType();
+ }
+ }
+ catch(const Exception&)
+ {
+ }
+ }
+ return;
+ }
+ if ( evt.PropertyName == PROPERTY_FORMATSSUPPLIER )
+ {
+ updateFormatterNullDate();
+ return;
+ }
+ OBoundControlModel::_propertyChanged( evt );
+}
+
+void OFormattedModel::updateFormatterNullDate()
+{
+ // calc the current NULL date
+ Reference< XNumberFormatsSupplier > xSupplier( calcFormatsSupplier() );
+ if ( xSupplier.is() )
+ xSupplier->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate;
+}
+
+Reference< XNumberFormatsSupplier > OFormattedModel::calcFormatsSupplier() const
+{
+ Reference<XNumberFormatsSupplier> xSupplier;
+ DBG_ASSERT(m_xAggregateSet.is(), "OFormattedModel::calcFormatsSupplier : have no aggregate !");
+ // Does my aggregate model have a FormatSupplier?
+ if( m_xAggregateSet.is() )
+ m_xAggregateSet->getPropertyValue(PROPERTY_FORMATSSUPPLIER) >>= xSupplier;
+ if (!xSupplier.is())
+ // check if my parent form has a supplier
+ xSupplier = calcFormFormatsSupplier();
+ if (!xSupplier.is())
+ xSupplier = calcDefaultFormatsSupplier();
+ DBG_ASSERT(xSupplier.is(), "OFormattedModel::calcFormatsSupplier : no supplier !");
+ // We should have one by now
+ return xSupplier;
+}
+
+Reference<XNumberFormatsSupplier> OFormattedModel::calcFormFormatsSupplier() const
+{
+ Reference<XChild> xMe(const_cast<OFormattedModel*>(this));
+ // By this we make sure that we get the right object even when aggregating
+ DBG_ASSERT(xMe.is(), "OFormattedModel::calcFormFormatsSupplier : I should have a content interface !");
+ // Iterate through until we reach a StartForm (starting with an own Parent)
+ Reference<XChild> xParent(xMe->getParent(), UNO_QUERY);
+ Reference<XForm> xNextParentForm(xParent, UNO_QUERY);
+ while (!xNextParentForm.is() && xParent.is())
+ {
+ xParent.set(xParent->getParent(), css::uno::UNO_QUERY);
+ xNextParentForm.set(xParent, css::uno::UNO_QUERY);
+ }
+ if (!xNextParentForm.is())
+ {
+ OSL_FAIL("OFormattedModel::calcFormFormatsSupplier : have no ancestor which is a form !");
+ return nullptr;
+ }
+ // The FormatSupplier of my ancestor (if it has one)
+ Reference< XRowSet > xRowSet( xNextParentForm, UNO_QUERY );
+ Reference< XNumberFormatsSupplier > xSupplier;
+ if (xRowSet.is())
+ xSupplier = getNumberFormats( getConnection(xRowSet), true, getContext() );
+ return xSupplier;
+}
+
+Reference< XNumberFormatsSupplier > OFormattedModel::calcDefaultFormatsSupplier() const
+{
+ return StandardFormatsSupplier::get( getContext() );
+}
+
+// XBoundComponent
+void OFormattedModel::loaded(const EventObject& rEvent)
+{
+ // HACK: our onConnectedDbColumn accesses our NumberFormatter which locks the solar mutex (as it doesn't have
+ // an own one). To prevent deadlocks with other threads which may request a property from us in an
+ // UI-triggered action (e.g. a tooltip) we lock the solar mutex _here_ before our base class locks
+ // its own mutex (which is used for property requests)
+ // alternative a): we use two mutexes, one which is passed to the OPropertysetHelper and used for
+ // property requests and one for our own code. This would need a lot of code rewriting
+ // alternative b): The NumberFormatter has to be really threadsafe (with an own mutex), which is
+ // the only "clean" solution for me.
+ SolarMutexGuard aGuard;
+ OEditBaseModel::loaded(rEvent);
+}
+
+void OFormattedModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
+{
+ m_xOriginalFormatter = nullptr;
+ // get some properties of the field
+ Reference<XPropertySet> xField = getField();
+ sal_Int32 nFormatKey = 0;
+ DBG_ASSERT(m_xAggregateSet.is(), "OFormattedModel::onConnectedDbColumn : have no aggregate !");
+ if (m_xAggregateSet.is())
+ { // all the following doesn't make any sense if we have no aggregate ...
+ Any aSupplier = m_xAggregateSet->getPropertyValue(PROPERTY_FORMATSSUPPLIER);
+ DBG_ASSERT( aSupplier.hasValue(), "OFormattedModel::onConnectedDbColumn : invalid property value !" );
+ // This should've been set to the correct value in the ctor or in the read
+ Any aFmtKey = m_xAggregateSet->getPropertyValue(PROPERTY_FORMATKEY);
+ if ( !(aFmtKey >>= nFormatKey ) )
+ { // nobody gave us a format to use. So we examine the field we're bound to for a
+ // format key, and use it ourself, too
+ sal_Int32 nType = DataType::VARCHAR;
+ if (xField.is())
+ {
+ aFmtKey = xField->getPropertyValue(PROPERTY_FORMATKEY);
+ xField->getPropertyValue(PROPERTY_FIELDTYPE) >>= nType ;
+ }
+ Reference<XNumberFormatsSupplier> xSupplier = calcFormFormatsSupplier();
+ DBG_ASSERT(xSupplier.is(), "OFormattedModel::onConnectedDbColumn : bound to a field but no parent with a formatter ? how this ?");
+ if (xSupplier.is())
+ {
+ m_bOriginalNumeric = getBOOL(getPropertyValue(PROPERTY_TREATASNUMERIC));
+ if (!aFmtKey.hasValue())
+ { // we aren't bound to a field (or this field's format is invalid)
+ // -> determine the standard text (or numeric) format of the supplier
+ Reference<XNumberFormatTypes> xTypes(xSupplier->getNumberFormats(), UNO_QUERY);
+ if (xTypes.is())
+ {
+ Locale aApplicationLocale = Application::GetSettings().GetUILanguageTag().getLocale();
+ if (m_bOriginalNumeric)
+ aFmtKey <<= xTypes->getStandardFormat(NumberFormat::NUMBER, aApplicationLocale);
+ else
+ aFmtKey <<= xTypes->getStandardFormat(NumberFormat::TEXT, aApplicationLocale);
+ }
+ }
+ aSupplier >>= m_xOriginalFormatter;
+ m_xAggregateSet->setPropertyValue(PROPERTY_FORMATSSUPPLIER, Any(xSupplier));
+ m_xAggregateSet->setPropertyValue(PROPERTY_FORMATKEY, aFmtKey);
+ // Adapt the NumericFalg to my bound field
+ if (xField.is())
+ {
+ m_bNumeric = false;
+ switch (nType)
+ {
+ case DataType::BIT:
+ case DataType::BOOLEAN:
+ case DataType::TINYINT:
+ case DataType::SMALLINT:
+ case DataType::INTEGER:
+ case DataType::BIGINT:
+ case DataType::FLOAT:
+ case DataType::REAL:
+ case DataType::DOUBLE:
+ case DataType::NUMERIC:
+ case DataType::DECIMAL:
+ case DataType::DATE:
+ case DataType::TIME:
+ case DataType::TIMESTAMP:
+ m_bNumeric = true;
+ break;
+ }
+ }
+ else
+ m_bNumeric = m_bOriginalNumeric;
+ setPropertyValue(PROPERTY_TREATASNUMERIC, Any(m_bNumeric));
+ OSL_VERIFY( aFmtKey >>= nFormatKey );
+ }
+ }
+ }
+ Reference<XNumberFormatsSupplier> xSupplier = calcFormatsSupplier();
+ m_bNumeric = getBOOL( getPropertyValue( PROPERTY_TREATASNUMERIC ) );
+ m_nKeyType = getNumberFormatType( xSupplier->getNumberFormats(), nFormatKey );
+ xSupplier->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate;
+ OEditBaseModel::onConnectedDbColumn( _rxForm );
+}
+
+void OFormattedModel::onDisconnectedDbColumn()
+{
+ OEditBaseModel::onDisconnectedDbColumn();
+ if (m_xOriginalFormatter.is())
+ { // Our aggregated model does not hold any Format information
+ m_xAggregateSet->setPropertyValue(PROPERTY_FORMATSSUPPLIER, Any(m_xOriginalFormatter));
+ m_xAggregateSet->setPropertyValue(PROPERTY_FORMATKEY, Any());
+ setPropertyValue(PROPERTY_TREATASNUMERIC, Any(m_bOriginalNumeric));
+ m_xOriginalFormatter = nullptr;
+ }
+ m_nKeyType = NumberFormat::UNDEFINED;
+ m_aNullDate = DBTypeConversion::getStandardDate();
+}
+
+void OFormattedModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ OEditBaseModel::write(_rxOutStream);
+ _rxOutStream->writeShort(0x0003);
+ DBG_ASSERT(m_xAggregateSet.is(), "OFormattedModel::write : have no aggregate !");
+ // Bring my Format (may be void) to a persistent Format.
+ // The Supplier together with the Key is already persistent, but that doesn't mean
+ // we have to save the Supplier (which would be quite some overhead)
+ Reference<XNumberFormatsSupplier> xSupplier;
+ Any aFmtKey;
+ bool bVoidKey = true;
+ if (m_xAggregateSet.is())
+ {
+ Any aSupplier = m_xAggregateSet->getPropertyValue(PROPERTY_FORMATSSUPPLIER);
+ if (aSupplier.getValueType().getTypeClass() != TypeClass_VOID)
+ {
+ OSL_VERIFY( aSupplier >>= xSupplier );
+ }
+ aFmtKey = m_xAggregateSet->getPropertyValue(PROPERTY_FORMATKEY);
+ bVoidKey = (!xSupplier.is() || !aFmtKey.hasValue()) || (isLoaded() && m_xOriginalFormatter.is());
+ // (no Format and/or Key) OR (loaded and faked Formatter)
+ }
+ _rxOutStream->writeBoolean(!bVoidKey);
+ if (!bVoidKey)
+ {
+ // Create persistent values from the FormatKey and the Formatter
+ Any aKey = m_xAggregateSet->getPropertyValue(PROPERTY_FORMATKEY);
+ sal_Int32 nKey = aKey.hasValue() ? getINT32(aKey) : 0;
+ Reference<XNumberFormats> xFormats = xSupplier->getNumberFormats();
+ OUString sFormatDescription;
+ LanguageType eFormatLanguage = LANGUAGE_DONTKNOW;
+ static constexpr OUStringLiteral s_aLocaleProp = u"Locale";
+ Reference<css::beans::XPropertySet> xFormat = xFormats->getByKey(nKey);
+ if (hasProperty(s_aLocaleProp, xFormat))
+ {
+ Any aLocale = xFormat->getPropertyValue(s_aLocaleProp);
+ DBG_ASSERT(aLocale.has<Locale>(), "OFormattedModel::write : invalid language property !");
+ if (auto pLocale = o3tl::tryAccess<Locale>(aLocale))
+ {
+ eFormatLanguage = LanguageTag::convertToLanguageType( *pLocale, false);
+ }
+ }
+ static constexpr OUStringLiteral s_aFormatStringProp = u"FormatString";
+ if (hasProperty(s_aFormatStringProp, xFormat))
+ xFormat->getPropertyValue(s_aFormatStringProp) >>= sFormatDescription;
+ _rxOutStream->writeUTF(sFormatDescription);
+ _rxOutStream->writeLong(static_cast<sal_uInt16>(eFormatLanguage));
+ }
+ // version 2 : write the properties common to all OEditBaseModels
+ writeCommonEditProperties(_rxOutStream);
+ // version 3 : write the effective value property of the aggregate
+ // Due to a bug within the UnoControlFormattedFieldModel implementation (our default aggregate)
+ // this props value isn't correctly read and this can't be corrected without being incompatible.
+ // so we have our own handling.
+ // and to be a little bit more compatible we make the following section skippable
+ {
+ OStreamSection aDownCompat(_rxOutStream);
+ // a sub version within the skippable block
+ _rxOutStream->writeShort(0x0000);
+ // version 0: the effective value of the aggregate
+ Any aEffectiveValue;
+ if (m_xAggregateSet.is())
+ {
+ try { aEffectiveValue = m_xAggregateSet->getPropertyValue(PROPERTY_EFFECTIVE_VALUE); } catch(const Exception&) { }
+ }
+ {
+ OStreamSection aDownCompat2(_rxOutStream);
+ switch (aEffectiveValue.getValueType().getTypeClass())
+ {
+ case TypeClass_STRING:
+ _rxOutStream->writeShort(0x0000);
+ _rxOutStream->writeUTF(::comphelper::getString(aEffectiveValue));
+ break;
+ case TypeClass_DOUBLE:
+ _rxOutStream->writeShort(0x0001);
+ _rxOutStream->writeDouble(::comphelper::getDouble(aEffectiveValue));
+ break;
+ default: // void and all unknown states
+ DBG_ASSERT(!aEffectiveValue.hasValue(), "FmXFormattedModel::write : unknown property value type !");
+ _rxOutStream->writeShort(0x0002);
+ break;
+ }
+ }
+ }
+}
+
+void OFormattedModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ OEditBaseModel::read(_rxInStream);
+ sal_uInt16 nVersion = _rxInStream->readShort();
+ Reference<XNumberFormatsSupplier> xSupplier;
+ sal_Int32 nKey = -1;
+ switch (nVersion)
+ {
+ case 0x0001 :
+ case 0x0002 :
+ case 0x0003 :
+ {
+ bool bNonVoidKey = _rxInStream->readBoolean();
+ if (bNonVoidKey)
+ {
+ // read string and language...
+ OUString sFormatDescription = _rxInStream->readUTF();
+ LanguageType eDescriptionLanguage(_rxInStream->readLong());
+ // and let a formatter roll dice based on that to create a key...
+ xSupplier = calcFormatsSupplier();
+ // calcFormatsSupplier first takes the one from the model, then one from the starform, then a new one...
+ Reference<XNumberFormats> xFormats = xSupplier->getNumberFormats();
+ if (xFormats.is())
+ {
+ Locale aDescriptionLanguage( LanguageTag::convertToLocale(eDescriptionLanguage));
+ nKey = xFormats->queryKey(sFormatDescription, aDescriptionLanguage, false);
+ if (nKey == sal_Int32(-1))
+ { // does not yet exist in my formatter...
+ nKey = xFormats->addNew(sFormatDescription, aDescriptionLanguage);
+ }
+ }
+ }
+ if ((nVersion == 0x0002) || (nVersion == 0x0003))
+ readCommonEditProperties(_rxInStream);
+ if (nVersion == 0x0003)
+ { // since version 3 there is a "skippable" block at this position
+ OStreamSection aDownCompat(_rxInStream);
+ _rxInStream->readShort(); // sub-version
+ // version 0 and higher: the "effective value" property
+ Any aEffectiveValue;
+ {
+ OStreamSection aDownCompat2(_rxInStream);
+ switch (_rxInStream->readShort())
+ {
+ case 0: // String
+ aEffectiveValue <<= _rxInStream->readUTF();
+ break;
+ case 1: // double
+ aEffectiveValue <<= _rxInStream->readDouble();
+ break;
+ case 2:
+ break;
+ case 3:
+ OSL_FAIL("FmXFormattedModel::read : unknown effective value type!");
+ }
+ }
+ // this property is only to be set if we have no control source: in all other cases the base class made a
+ // reset after it's read and this set the effective value to a default value
+ if ( m_xAggregateSet.is() && getControlSource().isEmpty() )
+ {
+ try
+ {
+ m_xAggregateSet->setPropertyValue(PROPERTY_EFFECTIVE_VALUE, aEffectiveValue);
+ }
+ catch(const Exception&)
+ {
+ }
+ }
+ }
+ }
+ break;
+ default :
+ OSL_FAIL("OFormattedModel::read : unknown version !");
+ // then the format of the aggregated set stay like it was during creation: void
+ defaultCommonEditProperties();
+ break;
+ }
+ if ((nKey != -1) && m_xAggregateSet.is())
+ {
+ m_xAggregateSet->setPropertyValue(PROPERTY_FORMATSSUPPLIER, Any(xSupplier));
+ m_xAggregateSet->setPropertyValue(PROPERTY_FORMATKEY, Any(nKey));
+ }
+ else
+ {
+ setPropertyToDefault(PROPERTY_FORMATSSUPPLIER);
+ setPropertyToDefault(PROPERTY_FORMATKEY);
+ }
+}
+
+sal_uInt16 OFormattedModel::getPersistenceFlags() const
+{
+ return (OEditBaseModel::getPersistenceFlags() & ~PF_HANDLE_COMMON_PROPS);
+ // a) we do our own call to writeCommonEditProperties
+}
+
+bool OFormattedModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+{
+ Any aControlValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
+ if ( aControlValue == m_aSaveValue )
+ return true;
+
+ // empty string + EmptyIsNull = void
+ if ( !aControlValue.hasValue()
+ || ( ( aControlValue.getValueType().getTypeClass() == TypeClass_STRING )
+ && getString( aControlValue ).isEmpty()
+ && m_bEmptyIsNull
+ )
+ )
+ m_xColumnUpdate->updateNull();
+ else
+ {
+ try
+ {
+ double f = 0.0;
+ if ( aControlValue.getValueType().getTypeClass() == TypeClass_DOUBLE || (aControlValue >>= f)) // #i110323
+ {
+ DBTypeConversion::setValue( m_xColumnUpdate, m_aNullDate, getDouble( aControlValue ), m_nKeyType );
+ }
+ else
+ {
+ DBG_ASSERT( aControlValue.getValueType().getTypeClass() == TypeClass_STRING, "OFormattedModel::commitControlValueToDbColumn: invalid value type!" );
+ m_xColumnUpdate->updateString( getString( aControlValue ) );
+ }
+ }
+ catch(const Exception&)
+ {
+ return false;
+ }
+ }
+ m_aSaveValue = aControlValue;
+ return true;
+}
+
+void OFormattedModel::onConnectedExternalValue( )
+{
+ OEditBaseModel::onConnectedExternalValue();
+ updateFormatterNullDate();
+}
+
+Any OFormattedModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+{
+ Any aControlValue;
+ switch( _rExternalValue.getValueTypeClass() )
+ {
+ case TypeClass_VOID:
+ break;
+ case TypeClass_STRING:
+ aControlValue = _rExternalValue;
+ break;
+ case TypeClass_BOOLEAN:
+ {
+ bool bExternalValue = false;
+ _rExternalValue >>= bExternalValue;
+ aControlValue <<= static_cast<double>( bExternalValue ? 1 : 0 );
+ }
+ break;
+ default:
+ {
+ if ( _rExternalValue.getValueType().equals( cppu::UnoType< css::util::Date >::get() ) )
+ {
+ css::util::Date aDate;
+ _rExternalValue >>= aDate;
+ aControlValue <<= DBTypeConversion::toDouble( aDate, m_aNullDate );
+ }
+ else if ( _rExternalValue.getValueType().equals( cppu::UnoType< css::util::Time >::get() ) )
+ {
+ css::util::Time aTime;
+ _rExternalValue >>= aTime;
+ aControlValue <<= DBTypeConversion::toDouble( aTime );
+ }
+ else if ( _rExternalValue.getValueType().equals( cppu::UnoType< css::util::DateTime >::get() ) )
+ {
+ css::util::DateTime aDateTime;
+ _rExternalValue >>= aDateTime;
+ aControlValue <<= DBTypeConversion::toDouble( aDateTime, m_aNullDate );
+ }
+ else
+ {
+ OSL_ENSURE( _rExternalValue.getValueTypeClass() == TypeClass_DOUBLE,
+ "OFormattedModel::translateExternalValueToControlValue: don't know how to translate this type!" );
+ double fValue = 0;
+ OSL_VERIFY( _rExternalValue >>= fValue );
+ aControlValue <<= fValue;
+ }
+ }
+ }
+ return aControlValue;
+}
+
+Any OFormattedModel::translateControlValueToExternalValue( ) const
+{
+ OSL_PRECOND( hasExternalValueBinding(),
+ "OFormattedModel::translateControlValueToExternalValue: precondition not met!" );
+ Any aControlValue( getControlValue() );
+ if ( !aControlValue.hasValue() )
+ return aControlValue;
+ Any aExternalValue;
+ // translate into the external value type
+ Type aExternalValueType( getExternalValueType() );
+ switch ( aExternalValueType.getTypeClass() )
+ {
+ case TypeClass_STRING:
+ {
+ OUString sString;
+ if ( aControlValue >>= sString )
+ {
+ aExternalValue <<= sString;
+ break;
+ }
+ [[fallthrough]];
+ }
+ case TypeClass_BOOLEAN:
+ {
+ double fValue = 0;
+ OSL_VERIFY( aControlValue >>= fValue );
+ // if this asserts ... well, the somebody set the TreatAsNumeric property to false,
+ // and the control value is a string. This implies some weird misconfiguration
+ // of the FormattedModel, so we won't care for it for the moment.
+ aExternalValue <<= fValue != 0.0;
+ }
+ break;
+ default:
+ {
+ double fValue = 0;
+ OSL_VERIFY( aControlValue >>= fValue );
+ // if this asserts ... well, the somebody set the TreatAsNumeric property to false,
+ // and the control value is a string. This implies some weird misconfiguration
+ // of the FormattedModel, so we won't care for it for the moment.
+ if ( aExternalValueType.equals( cppu::UnoType< css::util::Date >::get() ) )
+ {
+ aExternalValue <<= DBTypeConversion::toDate( fValue, m_aNullDate );
+ }
+ else if ( aExternalValueType.equals( cppu::UnoType< css::util::Time >::get() ) )
+ {
+ aExternalValue <<= DBTypeConversion::toTime( fValue );
+ }
+ else if ( aExternalValueType.equals( cppu::UnoType< css::util::DateTime >::get() ) )
+ {
+ aExternalValue <<= DBTypeConversion::toDateTime( fValue, m_aNullDate );
+ }
+ else
+ {
+ OSL_ENSURE( aExternalValueType.equals( cppu::UnoType< double >::get() ),
+ "OFormattedModel::translateControlValueToExternalValue: don't know how to translate this type!" );
+ aExternalValue <<= fValue;
+ }
+ }
+ break;
+ }
+ return aExternalValue;
+}
+
+Any OFormattedModel::translateDbColumnToControlValue()
+{
+ if ( m_bNumeric )
+ m_aSaveValue <<= DBTypeConversion::getValue( m_xColumn, m_aNullDate ); // #100056# OJ
+ else
+ m_aSaveValue <<= m_xColumn->getString();
+ if ( m_xColumn->wasNull() )
+ m_aSaveValue.clear();
+ return m_aSaveValue;
+}
+
+Sequence< Type > OFormattedModel::getSupportedBindingTypes()
+{
+ ::std::vector< Type > aTypes;
+ switch ( m_nKeyType & ~NumberFormat::DEFINED )
+ {
+ case NumberFormat::DATE:
+ aTypes.push_back(cppu::UnoType< css::util::Date >::get() );
+ break;
+ case NumberFormat::TIME:
+ aTypes.push_back(cppu::UnoType< css::util::Time >::get() );
+ break;
+ case NumberFormat::DATETIME:
+ aTypes.push_back(cppu::UnoType< css::util::DateTime >::get() );
+ break;
+ case NumberFormat::TEXT:
+ aTypes.push_back(cppu::UnoType< OUString >::get() );
+ break;
+ case NumberFormat::LOGICAL:
+ aTypes.push_back(cppu::UnoType< sal_Bool >::get() );
+ break;
+ }
+ aTypes.push_back( cppu::UnoType< double >::get() );
+ return comphelper::containerToSequence(aTypes);
+}
+
+Any OFormattedModel::getDefaultForReset() const
+{
+ return m_xAggregateSet->getPropertyValue( PROPERTY_EFFECTIVE_DEFAULT );
+}
+
+void OFormattedModel::resetNoBroadcast()
+{
+ OEditBaseModel::resetNoBroadcast();
+ m_aSaveValue.clear();
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OFormattedControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OFormattedControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FormattedField.hxx b/forms/source/component/FormattedField.hxx
new file mode 100644
index 000000000..c9d0f1c17
--- /dev/null
+++ b/forms/source/component/FormattedField.hxx
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "EditBase.hxx"
+#include <tools/link.hxx>
+#include <cppuhelper/implbase1.hxx>
+#include "errorbroadcaster.hxx"
+
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+
+struct ImplSVEvent;
+
+namespace frm
+{
+class OFormattedModel final
+ :public OEditBaseModel
+ ,public OErrorBroadcaster
+ {
+ // the original, in case I faked the format properties of my aggregated model,
+ // i.e. I just passed on the attributes of the field to which I am bound
+ // (only valid if loaded)
+
+ css::uno::Reference< css::util::XNumberFormatsSupplier> m_xOriginalFormatter;
+ css::util::Date m_aNullDate;
+ css::uno::Any m_aSaveValue;
+
+ sal_Int16 m_nKeyType;
+ bool m_bOriginalNumeric : 1,
+ m_bNumeric : 1; // analogous for the TreatAsNumeric-property
+
+ css::uno::Reference< css::util::XNumberFormatsSupplier> calcDefaultFormatsSupplier() const;
+ css::uno::Reference< css::util::XNumberFormatsSupplier> calcFormFormatsSupplier() const;
+ css::uno::Reference< css::util::XNumberFormatsSupplier> calcFormatsSupplier() const;
+
+ OFormattedModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OFormattedModel(
+ const OFormattedModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OFormattedModel() override;
+
+ friend class OFormattedFieldWrapper;
+
+ public:
+ // XInterface
+ DECLARE_UNO3_AGG_DEFAULTS( OFormattedModel, OEditBaseModel )
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+ // XAggregation
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OFormattedModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XPersistObject
+ virtual void SAL_CALL write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+ virtual OUString SAL_CALL getServiceName() override;
+
+ // XLoadListener
+ virtual void SAL_CALL loaded(const css::lang::EventObject& rEvent) override;
+
+ // XPropertyState
+ void setPropertyToDefaultByHandle(sal_Int32 nHandle) override;
+ css::uno::Any getPropertyDefaultByHandle(sal_Int32 nHandle) const override;
+
+ void SAL_CALL setPropertyToDefault(const OUString& aPropertyName) override;
+ css::uno::Any SAL_CALL getPropertyDefault( const OUString& aPropertyName ) override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+ virtual void describeAggregateProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+
+ // XPropertyChangeListener
+ virtual void _propertyChanged(const css::beans::PropertyChangeEvent& evt) override;
+
+ // prevent method hiding
+ using OEditBaseModel::disposing;
+ using OEditBaseModel::getFastPropertyValue;
+
+ virtual sal_uInt16 getPersistenceFlags() const override;
+ // as we have an own version handling for persistence
+
+ // OBoundControlModel overridables
+ virtual css::uno::Any
+ translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+
+ virtual css::uno::Sequence< css::uno::Type >
+ getSupportedBindingTypes() override;
+ virtual css::uno::Any
+ translateExternalValueToControlValue( const css::uno::Any& _rExternalValue ) const override;
+ virtual css::uno::Any
+ translateControlValueToExternalValue( ) const override;
+ virtual void onConnectedExternalValue( ) override;
+
+ virtual css::uno::Any
+ getDefaultForReset() const override;
+ virtual void resetNoBroadcast() override;
+
+ virtual void onConnectedDbColumn( const css::uno::Reference< css::uno::XInterface >& _rxForm ) override;
+ virtual void onDisconnectedDbColumn() override;
+
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ void implConstruct();
+
+ void updateFormatterNullDate();
+ };
+
+ typedef ::cppu::ImplHelper1< css::awt::XKeyListener> OFormattedControl_BASE;
+ class OFormattedControl : public OBoundControl
+ ,public OFormattedControl_BASE
+ {
+ ImplSVEvent * m_nKeyEvent;
+
+ public:
+ explicit OFormattedControl(const css::uno::Reference< css::uno::XComponentContext>& _rxContext);
+ virtual ~OFormattedControl() override;
+
+ DECLARE_UNO3_AGG_DEFAULTS(OFormattedControl, OBoundControl)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+ // css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OFormattedControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // css::lang::XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource) override;
+
+ // css::awt::XKeyListener
+ virtual void SAL_CALL keyPressed(const css::awt::KeyEvent& e) override;
+ virtual void SAL_CALL keyReleased(const css::awt::KeyEvent& e) override;
+
+ // css::awt::XControl
+ using OBoundControl::setDesignMode;
+
+ // disambiguation
+ using OBoundControl::disposing;
+
+ private:
+ DECL_LINK( OnKeyPressed, void*, void );
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FormattedFieldWrapper.cxx b/forms/source/component/FormattedFieldWrapper.cxx
new file mode 100644
index 000000000..8a8f27830
--- /dev/null
+++ b/forms/source/component/FormattedFieldWrapper.cxx
@@ -0,0 +1,360 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "FormattedFieldWrapper.hxx"
+#include "Edit.hxx"
+#include "FormattedField.hxx"
+#include <services.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <connectivity/dbtools.hxx>
+#include <tools/debug.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <com/sun/star/io/XMarkableStream.hpp>
+
+using namespace comphelper;
+using namespace frm;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+
+OFormattedFieldWrapper::OFormattedFieldWrapper(const Reference<XComponentContext>& _rxFactory)
+ :m_xContext(_rxFactory)
+{
+}
+
+css::uno::Reference<css::uno::XInterface> OFormattedFieldWrapper::createFormattedFieldWrapper(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory, bool bActAsFormatted)
+{
+ rtl::Reference<OFormattedFieldWrapper> pRef = new OFormattedFieldWrapper(_rxFactory);
+
+ if (bActAsFormatted)
+ {
+ // instantiate a FormattedModel
+ // (instantiate it directly ..., as the OFormattedModel isn't
+ // registered for any service names anymore)
+ rtl::Reference<OFormattedModel> pModel = new OFormattedModel(pRef->m_xContext);
+
+ pRef->m_xAggregate = pModel;
+ OSL_ENSURE(pRef->m_xAggregate.is(), "the OFormattedModel didn't have an XAggregation interface !");
+
+ // _before_ setting the delegator, give it to the member references
+ pRef->m_xFormattedPart = pModel;
+ pRef->m_pEditPart.set(new OEditModel(pRef->m_xContext));
+ }
+
+ if (pRef->m_xAggregate.is())
+ { // has to be in its own block because of the temporary variable created by *this
+ pRef->m_xAggregate->setDelegator(static_cast<XWeak*>(pRef.get()));
+ }
+
+ css::uno::Reference<css::uno::XInterface> xRef(*pRef);
+
+ return xRef;
+}
+
+Reference< XCloneable > SAL_CALL OFormattedFieldWrapper::createClone()
+{
+ ensureAggregate();
+
+ rtl::Reference< OFormattedFieldWrapper > xRef(new OFormattedFieldWrapper(m_xContext));
+
+ Reference< XCloneable > xCloneAccess;
+ query_aggregation( m_xAggregate, xCloneAccess );
+
+ // clone the aggregate
+ if ( xCloneAccess.is() )
+ {
+ Reference< XCloneable > xClone = xCloneAccess->createClone();
+ xRef->m_xAggregate.set(xClone, UNO_QUERY);
+ OSL_ENSURE(xRef->m_xAggregate.is(), "invalid aggregate cloned !");
+
+ xRef->m_xFormattedPart.set(
+ Reference< XInterface >(xClone), css::uno::UNO_QUERY);
+
+ if ( m_pEditPart.is() )
+ {
+ xRef->m_pEditPart.set( new OEditModel(m_pEditPart.get(), m_xContext) );
+ }
+ }
+ else
+ { // the clone source does not yet have an aggregate -> we don't yet need one, too
+ }
+
+ if ( xRef->m_xAggregate.is() )
+ { // has to be in its own block because of the temporary variable created by *this
+ xRef->m_xAggregate->setDelegator(static_cast< XWeak* >(xRef.get()));
+ }
+
+ return xRef;
+}
+
+OFormattedFieldWrapper::~OFormattedFieldWrapper()
+{
+ // release the aggregated object (if any)
+ if (m_xAggregate.is())
+ m_xAggregate->setDelegator(css::uno::Reference<css::uno::XInterface> ());
+
+}
+
+Any SAL_CALL OFormattedFieldWrapper::queryAggregation(const Type& _rType)
+{
+ Any aReturn;
+
+ if (_rType.equals( cppu::UnoType<XTypeProvider>::get() ) )
+ { // a XTypeProvider interface needs a working aggregate - we don't want to give the type provider
+ // of our base class (OFormattedFieldWrapper_Base) to the caller as it supplies nearly nothing
+ ensureAggregate();
+ if (m_xAggregate.is())
+ aReturn = m_xAggregate->queryAggregation(_rType);
+ }
+
+ if (!aReturn.hasValue())
+ {
+ aReturn = OFormattedFieldWrapper_Base::queryAggregation(_rType);
+
+ if ((_rType.equals( cppu::UnoType<XServiceInfo>::get() ) ) && aReturn.hasValue())
+ { // somebody requested an XServiceInfo interface and our base class provided it
+ // check our aggregate if it has one, too
+ ensureAggregate();
+ }
+
+ if (!aReturn.hasValue())
+ {
+ aReturn = ::cppu::queryInterface( _rType,
+ static_cast< XPersistObject* >( this ),
+ static_cast< XCloneable* >( this )
+ );
+
+ if (!aReturn.hasValue())
+ {
+ // somebody requests an interface other than the basics (XInterface) and other than
+ // the two we can supply without an aggregate. So ensure
+ // the aggregate exists.
+ ensureAggregate();
+ if (m_xAggregate.is())
+ aReturn = m_xAggregate->queryAggregation(_rType);
+ }
+ }
+ }
+
+ return aReturn;
+}
+
+OUString SAL_CALL OFormattedFieldWrapper::getServiceName()
+{
+ // return the old compatibility name for an EditModel
+ return FRM_COMPONENT_EDIT;
+}
+
+OUString SAL_CALL OFormattedFieldWrapper::getImplementationName( )
+{
+ return "com.sun.star.comp.forms.OFormattedFieldWrapper_ForcedFormatted";
+}
+
+sal_Bool SAL_CALL OFormattedFieldWrapper::supportsService( const OUString& _rServiceName )
+{
+ return cppu::supportsService(this, _rServiceName);
+}
+
+Sequence< OUString > SAL_CALL OFormattedFieldWrapper::getSupportedServiceNames( )
+{
+ DBG_ASSERT(m_xAggregate.is(), "OFormattedFieldWrapper::getSupportedServiceNames: should never have made it 'til here without an aggregate!");
+ Reference< XServiceInfo > xSI;
+ m_xAggregate->queryAggregation(cppu::UnoType<XServiceInfo>::get()) >>= xSI;
+ return xSI->getSupportedServiceNames();
+}
+
+void SAL_CALL OFormattedFieldWrapper::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ // can't write myself
+ ensureAggregate();
+
+ // if we act as real edit field, we can simple forward this write request
+ if (!m_xFormattedPart.is())
+ {
+ Reference<XPersistObject> xAggregatePersistence;
+ query_aggregation(m_xAggregate, xAggregatePersistence);
+ DBG_ASSERT(xAggregatePersistence.is(), "OFormattedFieldWrapper::write : don't know how to handle this : can't write !");
+ // oops ... We gave an XPersistObject interface to the caller but now we aren't an XPersistObject ...
+ if (xAggregatePersistence.is())
+ xAggregatePersistence->write(_rxOutStream);
+ return;
+ }
+
+ // else we have to write an edit part first
+ OSL_ENSURE(m_pEditPart.is(), "OFormattedFieldWrapper::write : formatted part without edit part ?");
+ if ( !m_pEditPart.is() )
+ throw RuntimeException( OUString(), *this );
+
+ // for this we transfer the current props of the formatted part to the edit part
+ Reference<XPropertySet> xFormatProps(m_xFormattedPart, UNO_QUERY);
+ Reference<XPropertySet> xEditProps = m_pEditPart;
+
+ Locale aAppLanguage = Application::GetSettings().GetUILanguageTag().getLocale();
+ dbtools::TransferFormComponentProperties(xFormatProps, xEditProps, aAppLanguage);
+
+ // then write the edit part, after switching to "fake mode"
+ m_pEditPart->enableFormattedWriteFake();
+ m_pEditPart->write(_rxOutStream);
+ m_pEditPart->disableFormattedWriteFake();
+
+ // and finally write the formatted part we're really interested in
+ m_xFormattedPart->write(_rxOutStream);
+}
+
+void SAL_CALL OFormattedFieldWrapper::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ SolarMutexGuard g;
+ if (m_xAggregate.is())
+ { // we already made a decision if we're an EditModel or a FormattedModel
+
+ // if we act as formatted, we have to read the edit part first
+ if (m_xFormattedPart.is())
+ {
+ // two possible cases:
+ // a) the stuff was written by a version which didn't work with an Edit header (all intermediate
+ // versions >5.1 && <=568)
+ // b) it was written by a version using edit headers
+ // as we can distinguish a) from b) only after we have read the edit part, we need to remember the
+ // position
+ Reference<XMarkableStream> xInMarkable(_rxInStream, UNO_QUERY);
+ DBG_ASSERT(xInMarkable.is(), "OFormattedFieldWrapper::read : can only work with markable streams !");
+ sal_Int32 nBeforeEditPart = xInMarkable->createMark();
+
+ m_pEditPart->read(_rxInStream);
+ // this only works because an edit model can read the stuff written by a formatted model
+ // (maybe with some assertions) , but not vice versa
+ if (!m_pEditPart->lastReadWasFormattedFake())
+ { // case a), written with a version without the edit part fake, so seek to the start position, again
+ xInMarkable->jumpToMark(nBeforeEditPart);
+ }
+ xInMarkable->deleteMark(nBeforeEditPart);
+ }
+
+ Reference<XPersistObject> xAggregatePersistence;
+ query_aggregation(m_xAggregate, xAggregatePersistence);
+ DBG_ASSERT(xAggregatePersistence.is(), "OFormattedFieldWrapper::read : don't know how to handle this : can't read !");
+ // oops ... We gave an XPersistObject interface to the caller but now we aren't an XPersistObject ...
+
+ if (xAggregatePersistence.is())
+ xAggregatePersistence->read(_rxInStream);
+ return;
+ }
+
+ // we have to decide from the data within the stream whether we should
+ // be an EditModel or a FormattedModel
+
+ {
+ // let an OEditModel do the reading
+ rtl::Reference< OEditModel > pBasicReader(new OEditModel(m_xContext));
+ pBasicReader->read(_rxInStream);
+
+ // was it really an edit model ?
+ if (!pBasicReader->lastReadWasFormattedFake())
+ {
+ // yes -> all fine
+ m_xAggregate = pBasicReader;
+ }
+ else
+ { // no -> substitute it with a formatted model
+ // let the formatted model do the reading
+ m_xFormattedPart.set(new OFormattedModel(m_xContext));
+ m_xFormattedPart->read(_rxInStream);
+ m_pEditPart = pBasicReader;
+ m_xAggregate.set( m_xFormattedPart, UNO_QUERY );
+ }
+ }
+
+ // do the aggregation
+ osl_atomic_increment(&m_refCount);
+ if (m_xAggregate.is())
+ { // has to be in its own block because of the temporary variable created by *this
+ m_xAggregate->setDelegator(static_cast<XWeak*>(this));
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+void OFormattedFieldWrapper::ensureAggregate()
+{
+ if (m_xAggregate.is())
+ return;
+
+ {
+ // instantiate an EditModel (the only place where we are allowed to decide that we're a FormattedModel
+ // is in ::read)
+ css::uno::Reference<css::uno::XInterface> xEditModel = m_xContext->getServiceManager()->createInstanceWithContext(FRM_SUN_COMPONENT_TEXTFIELD, m_xContext);
+ if (!xEditModel.is())
+ {
+ // arghhh... instantiate it directly... it's dirty, but we really need this aggregate
+ rtl::Reference<OEditModel> pModel = new OEditModel(m_xContext);
+ xEditModel.set(static_cast<XWeak*>(pModel.get()), css::uno::UNO_QUERY);
+ }
+
+ m_xAggregate.set(xEditModel, UNO_QUERY);
+ DBG_ASSERT(m_xAggregate.is(), "OFormattedFieldWrapper::ensureAggregate : the OEditModel didn't have an XAggregation interface !");
+
+ {
+ Reference< XServiceInfo > xSI(m_xAggregate, UNO_QUERY);
+ if (!xSI.is())
+ {
+ OSL_FAIL("OFormattedFieldWrapper::ensureAggregate: the aggregate has no XServiceInfo!");
+ m_xAggregate.clear();
+ }
+ }
+ }
+
+ osl_atomic_increment(&m_refCount);
+ if (m_xAggregate.is())
+ { // has to be in its own block because of the temporary variable created by *this
+ m_xAggregate->setDelegator(static_cast<XWeak*>(this));
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OFormattedFieldWrapper_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ css::uno::Reference<css::uno::XInterface> inst(
+ OFormattedFieldWrapper::createFormattedFieldWrapper(component, false));
+ inst->acquire();
+ return inst.get();
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_forms_OFormattedFieldWrapper_ForcedFormatted_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ css::uno::Reference<css::uno::XInterface> inst(
+ OFormattedFieldWrapper::createFormattedFieldWrapper(component, true));
+ inst->acquire();
+ return inst.get();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FormattedFieldWrapper.hxx b/forms/source/component/FormattedFieldWrapper.hxx
new file mode 100644
index 000000000..b34db4148
--- /dev/null
+++ b/forms/source/component/FormattedFieldWrapper.hxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <comphelper/uno3.hxx>
+#include <cppuhelper/implbase3.hxx>
+#include <com/sun/star/io/XPersistObject.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <rtl/ref.hxx>
+
+namespace frm
+{
+
+class OEditModel;
+
+//= OFormattedFieldWrapper
+
+typedef ::cppu::WeakAggImplHelper3 < css::io::XPersistObject
+ , css::lang::XServiceInfo
+ , css::util::XCloneable
+ > OFormattedFieldWrapper_Base;
+
+class OFormattedFieldWrapper final : public OFormattedFieldWrapper_Base
+{
+ css::uno::Reference< css::uno::XComponentContext> m_xContext;
+
+ css::uno::Reference< css::uno::XAggregation> m_xAggregate;
+
+ rtl::Reference< OEditModel > m_pEditPart;
+ // if we act as formatted this is used to write the EditModel part
+ css::uno::Reference< css::io::XPersistObject> m_xFormattedPart;
+
+ OFormattedFieldWrapper(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+
+ virtual ~OFormattedFieldWrapper() override;
+
+public:
+ // if we act as formatted, this is the PersistObject interface of our aggregate, used
+ // to read and write the FormattedModel part
+ // if bActAsFormatted is false, the state is undetermined until somebody calls
+ // ::read or does anything which requires a living aggregate
+ static css::uno::Reference<css::uno::XInterface> createFormattedFieldWrapper(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory, bool bActAsFormatted);
+
+ // UNO
+ DECLARE_UNO3_AGG_DEFAULTS(OFormattedFieldWrapper, OWeakAggObject)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // XCloneable
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+private:
+ /// ensure we're in a defined state, which means a FormattedModel _OR_ an EditModel
+ void ensureAggregate();
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FormsCollection.cxx b/forms/source/component/FormsCollection.cxx
new file mode 100644
index 000000000..0f1b6d95c
--- /dev/null
+++ b/forms/source/component/FormsCollection.cxx
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "FormsCollection.hxx"
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/form/XForm.hpp>
+#include <rtl/ref.hxx>
+
+using namespace frm;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::util;
+
+OUString SAL_CALL OFormsCollection::getServiceName()
+{
+ return "com.sun.star.form.Forms";
+}
+
+Sequence< sal_Int8 > SAL_CALL OFormsCollection::getImplementationId( )
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+Sequence<Type> SAL_CALL OFormsCollection::getTypes()
+{
+ return concatSequences(OInterfaceContainer::getTypes(), ::cppu::OComponentHelper::getTypes(), OFormsCollection_BASE::getTypes());
+}
+
+OFormsCollection::OFormsCollection(const Reference<XComponentContext>& _rxFactory)
+ : ::cppu::OComponentHelper( m_aMutex )
+ ,OInterfaceContainer( _rxFactory, m_aMutex, cppu::UnoType<XForm>::get() )
+ ,OFormsCollection_BASE()
+{
+}
+
+OFormsCollection::OFormsCollection( const OFormsCollection& _cloneSource )
+ : ::cppu::OComponentHelper( m_aMutex )
+ ,OInterfaceContainer( m_aMutex, _cloneSource )
+ ,OFormsCollection_BASE()
+{
+}
+
+OFormsCollection::~OFormsCollection()
+{
+ if (!::cppu::OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+}
+
+Any SAL_CALL OFormsCollection::queryAggregation(const Type& _rType)
+{
+ Any aReturn = OFormsCollection_BASE::queryInterface(_rType);
+ if (!aReturn.hasValue())
+ {
+ aReturn = OInterfaceContainer::queryInterface(_rType);
+
+ if (!aReturn.hasValue())
+ aReturn = ::cppu::OComponentHelper::queryAggregation(_rType);
+ }
+
+ return aReturn;
+}
+
+OUString SAL_CALL OFormsCollection::getImplementationName()
+{
+ return "com.sun.star.form.OFormsCollection";
+}
+
+sal_Bool SAL_CALL OFormsCollection::supportsService( const OUString& _rServiceName )
+{
+ return cppu::supportsService(this, _rServiceName);
+}
+
+css::uno::Sequence<OUString> SAL_CALL OFormsCollection::getSupportedServiceNames()
+{
+ return { "com.sun.star.form.Forms", "com.sun.star.form.FormComponents" };
+}
+
+// XCloneable
+Reference< XCloneable > SAL_CALL OFormsCollection::createClone( )
+{
+ rtl::Reference<OFormsCollection> pClone = new OFormsCollection( *this );
+ pClone->clonedFrom( *this );
+ return static_cast<OInterfaceContainer*>(pClone.get());
+}
+
+// OComponentHelper
+
+void OFormsCollection::disposing()
+{
+ {
+ SAL_INFO( "forms.component", "forms::OFormsCollection::disposing" );
+ OInterfaceContainer::disposing();
+ }
+ ::cppu::OComponentHelper::disposing();
+ m_xParent = nullptr;
+}
+
+//XChild
+
+void OFormsCollection::setParent(const css::uno::Reference<css::uno::XInterface>& Parent)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_xParent = Parent;
+}
+
+css::uno::Reference<css::uno::XInterface> OFormsCollection::getParent()
+{
+ return m_xParent;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OFormsCollection_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OFormsCollection(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FormsCollection.hxx b/forms/source/component/FormsCollection.hxx
new file mode 100644
index 000000000..6a97148af
--- /dev/null
+++ b/forms/source/component/FormsCollection.hxx
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <InterfaceContainer.hxx>
+#include <cppuhelper/component.hxx>
+#include <cppuhelper/implbase2.hxx>
+#include <comphelper/uno3.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/form/XForms.hpp>
+
+
+namespace frm
+{
+
+// OFormsCollection
+// Implements the UNO Container for Forms and contains all assigned Forms.
+// It can either represent the Context for Forms or be passed a Context.
+
+typedef ::cppu::ImplHelper2< css::form::XForms
+ ,css::lang::XServiceInfo > OFormsCollection_BASE;
+class OFormsCollection
+ :public ::cppu::OComponentHelper
+ ,public OInterfaceContainer
+ ,public OFormsCollection_BASE
+{
+ ::osl::Mutex m_aMutex;
+ css::uno::Reference<css::uno::XInterface> m_xParent; // Parent
+
+public:
+ explicit OFormsCollection(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+ OFormsCollection( const OFormsCollection& _cloneSource );
+ virtual ~OFormsCollection() override;
+
+public:
+ DECLARE_UNO3_AGG_DEFAULTS(OFormsCollection, ::cppu::OComponentHelper)
+
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames( ) override;
+
+ // XCloneable
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // css::container::XChild
+ virtual css::uno::Reference<css::uno::XInterface> SAL_CALL getParent() override;
+ virtual void SAL_CALL setParent(const css::uno::Reference<css::uno::XInterface>& Parent) override;
+
+ // prevent method hiding
+ using OInterfaceContainer::disposing;
+
+ // inheritance ambiguity
+ virtual css::uno::Type SAL_CALL getElementType() override
+ { return OInterfaceContainer::getElementType(); }
+ virtual sal_Bool SAL_CALL hasElements() override
+ { return OInterfaceContainer::hasElements(); }
+ virtual css::uno::Any SAL_CALL getByName(const OUString& p1) override
+ { return OInterfaceContainer::getByName(p1); }
+ virtual css::uno::Sequence<OUString> SAL_CALL getElementNames() override
+ { return OInterfaceContainer::getElementNames(); }
+ virtual sal_Bool SAL_CALL hasByName(const OUString& p1) override
+ { return OInterfaceContainer::hasByName(p1); }
+ virtual void SAL_CALL replaceByName(const OUString& p1, const css::uno::Any& p2) override
+ { OInterfaceContainer::replaceByName(p1, p2); }
+ virtual void SAL_CALL insertByName(const OUString& p1, const css::uno::Any& p2) override
+ { OInterfaceContainer::insertByName(p1, p2); }
+ virtual void SAL_CALL removeByName(const OUString& p1) override
+ { OInterfaceContainer::removeByName(p1); }
+ virtual sal_Int32 SAL_CALL getCount() override
+ { return OInterfaceContainer::getCount(); }
+ virtual css::uno::Any SAL_CALL getByIndex(sal_Int32 p1) override
+ { return OInterfaceContainer::getByIndex(p1); }
+ virtual void SAL_CALL replaceByIndex(sal_Int32 p1, const css::uno::Any& p2) override
+ { return OInterfaceContainer::replaceByIndex(p1, p2); }
+ virtual void SAL_CALL insertByIndex(sal_Int32 p1, const css::uno::Any& p2) override
+ { return OInterfaceContainer::insertByIndex(p1, p2); }
+ virtual void SAL_CALL removeByIndex(sal_Int32 p1) override
+ { return OInterfaceContainer::removeByIndex(p1); }
+ virtual css::uno::Reference<css::container::XEnumeration> SAL_CALL createEnumeration() override
+ { return OInterfaceContainer::createEnumeration(); }
+ virtual void SAL_CALL registerScriptEvent(sal_Int32 p1, const css::script::ScriptEventDescriptor& p2) override
+ { OInterfaceContainer::registerScriptEvent(p1, p2); }
+ virtual void SAL_CALL registerScriptEvents(sal_Int32 p1, const css::uno::Sequence<css::script::ScriptEventDescriptor>& p2) override
+ { OInterfaceContainer::registerScriptEvents(p1, p2); }
+ virtual void SAL_CALL revokeScriptEvent(sal_Int32 p1, const OUString& p2, const OUString& p3, const OUString& p4) override
+ { OInterfaceContainer::revokeScriptEvent(p1, p2, p3, p4); }
+ virtual void SAL_CALL revokeScriptEvents(sal_Int32 p1) override
+ { OInterfaceContainer::revokeScriptEvents(p1); }
+ virtual void SAL_CALL insertEntry(sal_Int32 p1) override
+ { OInterfaceContainer::insertEntry(p1); }
+ virtual void SAL_CALL removeEntry(sal_Int32 p1) override
+ { OInterfaceContainer::removeEntry(p1); }
+ virtual css::uno::Sequence<css::script::ScriptEventDescriptor> SAL_CALL getScriptEvents(sal_Int32 p1) override
+ { return OInterfaceContainer::getScriptEvents(p1); }
+ virtual void SAL_CALL attach(sal_Int32 p1, const css::uno::Reference<css::uno::XInterface>& p2, const css::uno::Any& p3) override
+ { OInterfaceContainer::attach(p1, p2, p3); }
+ virtual void SAL_CALL detach(sal_Int32 p1, const css::uno::Reference<css::uno::XInterface>& p2) override
+ { OInterfaceContainer::detach(p1, p2); }
+ virtual void SAL_CALL addScriptListener(const css::uno::Reference<css::script::XScriptListener>& p1) override
+ { OInterfaceContainer::addScriptListener(p1); }
+ virtual void SAL_CALL removeScriptListener(const css::uno::Reference<css::script::XScriptListener>& p1) override
+ { OInterfaceContainer::removeScriptListener(p1); }
+ virtual void SAL_CALL dispose() override
+ { ::cppu::OComponentHelper::dispose(); }
+ virtual void SAL_CALL addEventListener(const css::uno::Reference<css::lang::XEventListener>& p1) override
+ { ::cppu::OComponentHelper::addEventListener(p1); }
+ virtual void SAL_CALL removeEventListener(const css::uno::Reference<css::lang::XEventListener>& p1) override
+ { ::cppu::OComponentHelper::removeEventListener(p1); }
+ virtual void SAL_CALL addContainerListener(const css::uno::Reference<css::container::XContainerListener>& p1) override
+ { OInterfaceContainer::addContainerListener(p1); }
+ virtual void SAL_CALL removeContainerListener(const css::uno::Reference<css::container::XContainerListener>& p1) override
+ { OInterfaceContainer::removeContainerListener(p1); }
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Grid.cxx b/forms/source/component/Grid.cxx
new file mode 100644
index 000000000..308ed8228
--- /dev/null
+++ b/forms/source/component/Grid.cxx
@@ -0,0 +1,992 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "Columns.hxx"
+#include "findpos.hxx"
+#include "Grid.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/io/XMarkableStream.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/types.hxx>
+#include <vcl/unohelp.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star::uno;
+
+namespace frm
+{
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::view;
+namespace WritingMode2 = ::com::sun::star::text::WritingMode2;
+const sal_uInt16 ROWHEIGHT = 0x0001;
+const sal_uInt16 FONTTYPE = 0x0002;
+const sal_uInt16 FONTSIZE = 0x0004;
+const sal_uInt16 FONTATTRIBS = 0x0008;
+const sal_uInt16 TABSTOP = 0x0010;
+const sal_uInt16 TEXTCOLOR = 0x0020;
+const sal_uInt16 FONTDESCRIPTOR = 0x0040;
+const sal_uInt16 RECORDMARKER = 0x0080;
+const sal_uInt16 BACKGROUNDCOLOR = 0x0100;
+
+OGridControlModel::OGridControlModel(const Reference<XComponentContext>& _rxFactory)
+ :OControlModel(_rxFactory, OUString())
+ ,OInterfaceContainer(_rxFactory, m_aMutex, cppu::UnoType<XPropertySet>::get())
+ ,OErrorBroadcaster( OComponentHelper::rBHelper )
+ ,FontControlModel( false )
+ ,m_aSelectListeners(m_aMutex)
+ ,m_aResetListeners(m_aMutex)
+ ,m_aRowSetChangeListeners(m_aMutex)
+ ,m_aDefaultControl( FRM_SUN_CONTROL_GRIDCONTROL )
+ ,m_nBorder(1)
+ ,m_nWritingMode( WritingMode2::CONTEXT )
+ ,m_nContextWritingMode( WritingMode2::CONTEXT )
+ ,m_bEnableVisible(true)
+ ,m_bEnable(true)
+ ,m_bNavigation(true)
+ ,m_bRecordMarker(true)
+ ,m_bPrintable(true)
+ ,m_bAlwaysShowCursor(false)
+ ,m_bDisplaySynchron(true)
+{
+ m_nClassId = FormComponentType::GRIDCONTROL;
+}
+
+OGridControlModel::OGridControlModel( const OGridControlModel* _pOriginal, const Reference< XComponentContext >& _rxFactory )
+ :OControlModel( _pOriginal, _rxFactory )
+ ,OInterfaceContainer( _rxFactory, m_aMutex, cppu::UnoType<XPropertySet>::get() )
+ ,OErrorBroadcaster( OComponentHelper::rBHelper )
+ ,FontControlModel( _pOriginal )
+ ,m_aSelectListeners( m_aMutex )
+ ,m_aResetListeners( m_aMutex )
+ ,m_aRowSetChangeListeners( m_aMutex )
+{
+ m_aDefaultControl = _pOriginal->m_aDefaultControl;
+ m_bEnable = _pOriginal->m_bEnable;
+ m_bEnableVisible = _pOriginal->m_bEnableVisible;
+ m_bNavigation = _pOriginal->m_bNavigation;
+ m_nBorder = _pOriginal->m_nBorder;
+ m_nWritingMode = _pOriginal->m_nWritingMode;
+ m_nContextWritingMode = _pOriginal->m_nContextWritingMode;
+ m_bRecordMarker = _pOriginal->m_bRecordMarker;
+ m_bPrintable = _pOriginal->m_bPrintable;
+ m_bAlwaysShowCursor = _pOriginal->m_bAlwaysShowCursor;
+ m_bDisplaySynchron = _pOriginal->m_bDisplaySynchron;
+ // clone the columns
+ cloneColumns( _pOriginal );
+ // TODO: clone the events?
+}
+
+OGridControlModel::~OGridControlModel()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+}
+
+// XCloneable
+Reference< XCloneable > SAL_CALL OGridControlModel::createClone( )
+{
+ rtl::Reference<OGridControlModel> pClone = new OGridControlModel( this, getContext() );
+ pClone->OControlModel::clonedFrom( this );
+ // do not call OInterfaceContainer::clonedFrom, it would clone the elements aka columns, which is
+ // already done in the ctor
+ //pClone->OInterfaceContainer::clonedFrom( *this );
+ return static_cast< XCloneable* >( static_cast< OControlModel* >( pClone.get() ) );
+}
+
+void OGridControlModel::cloneColumns( const OGridControlModel* _pOriginalContainer )
+{
+ try
+ {
+ Reference< XCloneable > xColCloneable;
+ sal_Int32 nIndex = 0;
+ for (auto const& column : _pOriginalContainer->m_aItems)
+ {
+ // ask the col for a factory for the clone
+ xColCloneable.set(column, css::uno::UNO_QUERY);
+ DBG_ASSERT( xColCloneable.is(), "OGridControlModel::cloneColumns: column is not cloneable!" );
+ if ( xColCloneable.is() )
+ {
+ // create a clone of the column
+ Reference< XCloneable > xColClone( xColCloneable->createClone() );
+ DBG_ASSERT( xColClone.is(), "OGridControlModel::cloneColumns: invalid column clone!" );
+ if ( xColClone.is() )
+ {
+ // insert this clone into our own container
+ insertByIndex( nIndex, xColClone->queryInterface( m_aElementType ) );
+ }
+ }
+ ++nIndex;
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.component", "OGridControlModel::cloneColumns: caught an exception while cloning the columns!" );
+ }
+}
+
+// XServiceInfo
+css::uno::Sequence<OUString> OGridControlModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OControlModel::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 4);
+ auto pSupported = aSupported.getArray();
+ pSupported[aSupported.getLength()-4] = "com.sun.star.awt.UnoControlModel";
+ pSupported[aSupported.getLength()-3] = FRM_SUN_COMPONENT_GRIDCONTROL;
+ pSupported[aSupported.getLength()-2] = FRM_COMPONENT_GRID;
+ pSupported[aSupported.getLength()-1] = FRM_COMPONENT_GRIDCONTROL;
+ return aSupported;
+}
+Any SAL_CALL OGridControlModel::queryAggregation( const Type& _rType )
+{
+ Any aReturn = OGridControlModel_BASE::queryInterface(_rType);
+ if ( !aReturn.hasValue() )
+ {
+ aReturn = OControlModel::queryAggregation( _rType );
+ if ( !aReturn.hasValue() )
+ {
+ aReturn = OInterfaceContainer::queryInterface( _rType );
+ if ( !aReturn.hasValue() )
+ aReturn = OErrorBroadcaster::queryInterface( _rType );
+ }
+ }
+ return aReturn;
+}
+
+// XSQLErrorListener
+void SAL_CALL OGridControlModel::errorOccured( const SQLErrorEvent& _rEvent )
+{
+ // forward the errors which happened to my columns to my own listeners
+ onError( _rEvent );
+}
+
+// XRowSetSupplier
+Reference< XRowSet > SAL_CALL OGridControlModel::getRowSet( )
+{
+ return Reference< XRowSet >( getParent(), UNO_QUERY );
+}
+
+void SAL_CALL OGridControlModel::setRowSet( const Reference< XRowSet >& /*_rxDataSource*/ )
+{
+ OSL_FAIL( "OGridControlModel::setRowSet: not supported!" );
+}
+
+void SAL_CALL OGridControlModel::addRowSetChangeListener( const Reference< XRowSetChangeListener >& i_Listener )
+{
+ if ( i_Listener.is() )
+ m_aRowSetChangeListeners.addInterface( i_Listener );
+}
+
+void SAL_CALL OGridControlModel::removeRowSetChangeListener( const Reference< XRowSetChangeListener >& i_Listener )
+{
+ m_aRowSetChangeListeners.removeInterface( i_Listener );
+}
+
+// XChild
+void SAL_CALL OGridControlModel::setParent( const css::uno::Reference<css::uno::XInterface>& i_Parent )
+{
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ if ( i_Parent == getParent() )
+ return;
+ OControlModel::setParent( i_Parent );
+ EventObject aEvent( *this );
+ aGuard.clear();
+ m_aRowSetChangeListeners.notifyEach( &XRowSetChangeListener::onRowSetChanged, aEvent );
+}
+Sequence< Type > SAL_CALL OGridControlModel::getTypes( )
+{
+ return concatSequences(
+ concatSequences(
+ OControlModel::getTypes(),
+ OInterfaceContainer::getTypes(),
+ OErrorBroadcaster::getTypes()
+ ),
+ OGridControlModel_BASE::getTypes()
+ );
+}
+
+// OComponentHelper
+void OGridControlModel::disposing()
+{
+ OControlModel::disposing();
+ OErrorBroadcaster::disposing();
+ OInterfaceContainer::disposing();
+ setParent(nullptr);
+ EventObject aEvt(static_cast<XWeak*>(this));
+ m_aSelectListeners.disposeAndClear(aEvt);
+ m_aResetListeners.disposeAndClear(aEvt);
+ m_aRowSetChangeListeners.disposeAndClear(aEvt);
+}
+
+// XEventListener
+void OGridControlModel::disposing(const EventObject& _rEvent)
+{
+ OControlModel::disposing( _rEvent );
+ OInterfaceContainer::disposing( _rEvent );
+}
+
+// XSelectionSupplier
+sal_Bool SAL_CALL OGridControlModel::select(const Any& rElement)
+{
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ Reference<XPropertySet> xSel;
+ if (rElement.hasValue())
+ {
+ xSel.set(rElement, css::uno::UNO_QUERY);
+ if (!xSel.is())
+ {
+ throw IllegalArgumentException();
+ }
+ }
+ css::uno::Reference<css::uno::XInterface> xMe = static_cast<XWeak*>(this);
+ if (xSel.is())
+ {
+ Reference<XChild> xAsChild(xSel, UNO_QUERY);
+ if (!xAsChild.is() || (xAsChild->getParent() != xMe))
+ {
+ throw IllegalArgumentException();
+ }
+ }
+ if ( xSel != m_xSelection )
+ {
+ m_xSelection = xSel;
+ aGuard.clear();
+ m_aSelectListeners.notifyEach( &XSelectionChangeListener::selectionChanged, EventObject( *this ) );
+ return true;
+ }
+ return false;
+}
+Any SAL_CALL OGridControlModel::getSelection()
+{
+ return Any(m_xSelection);
+}
+
+void OGridControlModel::addSelectionChangeListener(const Reference< XSelectionChangeListener >& _rxListener)
+{
+ m_aSelectListeners.addInterface(_rxListener);
+}
+
+void OGridControlModel::removeSelectionChangeListener(const Reference< XSelectionChangeListener >& _rxListener)
+{
+ m_aSelectListeners.removeInterface(_rxListener);
+}
+
+// XGridColumnFactory
+Reference<XPropertySet> SAL_CALL OGridControlModel::createColumn(const OUString& ColumnType)
+{
+ SolarMutexGuard g;
+ const Sequence< OUString >& rColumnTypes = frm::getColumnTypes();
+ return createColumnById( ::detail::findPos( ColumnType, rColumnTypes ) );
+}
+Reference<XPropertySet> OGridControlModel::createColumnById(sal_Int32 nTypeId) const
+{
+ Reference<XPropertySet> xReturn;
+ switch (nTypeId)
+ {
+ case TYPE_CHECKBOX: xReturn = new CheckBoxColumn( getContext() ); break;
+ case TYPE_COMBOBOX: xReturn = new ComboBoxColumn( getContext() ); break;
+ case TYPE_CURRENCYFIELD: xReturn = new CurrencyFieldColumn( getContext() ); break;
+ case TYPE_DATEFIELD: xReturn = new DateFieldColumn( getContext() ); break;
+ case TYPE_LISTBOX: xReturn = new ListBoxColumn( getContext() ); break;
+ case TYPE_NUMERICFIELD: xReturn = new NumericFieldColumn( getContext() ); break;
+ case TYPE_PATTERNFIELD: xReturn = new PatternFieldColumn( getContext() ); break;
+ case TYPE_TEXTFIELD: xReturn = new TextFieldColumn( getContext() ); break;
+ case TYPE_TIMEFIELD: xReturn = new TimeFieldColumn( getContext() ); break;
+ case TYPE_FORMATTEDFIELD: xReturn = new FormattedFieldColumn( getContext() ); break;
+ default:
+ OSL_FAIL("OGridControlModel::createColumn: Unknown Column");
+ break;
+ }
+ return xReturn;
+}
+css::uno::Sequence<OUString> SAL_CALL OGridControlModel::getColumnTypes()
+{
+ return frm::getColumnTypes();
+}
+
+// XReset
+void SAL_CALL OGridControlModel::reset()
+{
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aResetListeners);
+ EventObject aEvt(static_cast<XWeak*>(this));
+ bool bContinue = true;
+ while (aIter.hasMoreElements() && bContinue)
+ bContinue = aIter.next()->approveReset(aEvt);
+ if (bContinue)
+ {
+ _reset();
+ m_aResetListeners.notifyEach( &XResetListener::resetted, aEvt );
+ }
+}
+void SAL_CALL OGridControlModel::addResetListener(const Reference<XResetListener>& _rxListener)
+{
+ m_aResetListeners.addInterface(_rxListener);
+}
+void SAL_CALL OGridControlModel::removeResetListener(const Reference<XResetListener>& _rxListener)
+{
+ m_aResetListeners.removeInterface(_rxListener);
+}
+void OGridControlModel::_reset()
+{
+ Reference<XReset> xReset;
+ sal_Int32 nCount = getCount();
+ for (sal_Int32 nIndex=0; nIndex < nCount; nIndex++)
+ {
+ getByIndex( nIndex ) >>= xReset;
+ if (xReset.is())
+ xReset->reset();
+ }
+}
+
+// XPropertySet
+void OGridControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ _rProps.realloc(37);
+ css::beans::Property* pProperties = _rProps.getArray();
+ *pProperties++ = css::beans::Property(PROPERTY_NAME, PROPERTY_ID_NAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_CLASSID, PROPERTY_ID_CLASSID, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_TAG, PROPERTY_ID_TAG, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABSTOP, PROPERTY_ID_TABSTOP, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_HASNAVIGATION, PROPERTY_ID_HASNAVIGATION, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_ENABLED, PROPERTY_ID_ENABLED, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_ENABLEVISIBLE, PROPERTY_ID_ENABLEVISIBLE, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_BORDER, PROPERTY_ID_BORDER, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_BORDERCOLOR, PROPERTY_ID_BORDERCOLOR, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULTCONTROL, PROPERTY_ID_DEFAULTCONTROL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TEXTCOLOR, PROPERTY_ID_TEXTCOLOR, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_BACKGROUNDCOLOR, PROPERTY_ID_BACKGROUNDCOLOR, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT, PROPERTY_ID_FONT, cppu::UnoType<FontDescriptor>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_ROWHEIGHT, PROPERTY_ID_ROWHEIGHT, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_HELPTEXT, PROPERTY_ID_HELPTEXT, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_NAME, PROPERTY_ID_FONT_NAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_STYLENAME, PROPERTY_ID_FONT_STYLENAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_FAMILY, PROPERTY_ID_FONT_FAMILY, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_CHARSET, PROPERTY_ID_FONT_CHARSET, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_HEIGHT, PROPERTY_ID_FONT_HEIGHT, cppu::UnoType<float>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_WEIGHT, PROPERTY_ID_FONT_WEIGHT, cppu::UnoType<float>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_SLANT, PROPERTY_ID_FONT_SLANT, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_UNDERLINE, PROPERTY_ID_FONT_UNDERLINE, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_WORDLINEMODE, PROPERTY_ID_FONT_WORDLINEMODE, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_TEXTLINECOLOR, PROPERTY_ID_TEXTLINECOLOR, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_FONTEMPHASISMARK, PROPERTY_ID_FONTEMPHASISMARK, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONTRELIEF, PROPERTY_ID_FONTRELIEF, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_STRIKEOUT, PROPERTY_ID_FONT_STRIKEOUT, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_RECORDMARKER, PROPERTY_ID_RECORDMARKER, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_PRINTABLE, PROPERTY_ID_PRINTABLE, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_CURSORCOLOR, PROPERTY_ID_CURSORCOLOR, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT |
+ css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_ALWAYSSHOWCURSOR, PROPERTY_ID_ALWAYSSHOWCURSOR, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_DISPLAYSYNCHRON, PROPERTY_ID_DISPLAYSYNCHRON, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_HELPURL, PROPERTY_ID_HELPURL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_WRITING_MODE, PROPERTY_ID_WRITING_MODE, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_CONTEXT_WRITING_MODE, PROPERTY_ID_CONTEXT_WRITING_MODE, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::TRANSIENT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+void OGridControlModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle ) const
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_CONTEXT_WRITING_MODE:
+ rValue <<= m_nContextWritingMode;
+ break;
+ case PROPERTY_ID_WRITING_MODE:
+ rValue <<= m_nWritingMode;
+ break;
+ case PROPERTY_ID_HELPTEXT:
+ rValue <<= m_sHelpText;
+ break;
+ case PROPERTY_ID_HELPURL:
+ rValue <<= m_sHelpURL;
+ break;
+ case PROPERTY_ID_DISPLAYSYNCHRON:
+ rValue <<= m_bDisplaySynchron;
+ break;
+ case PROPERTY_ID_ALWAYSSHOWCURSOR:
+ rValue <<= m_bAlwaysShowCursor;
+ break;
+ case PROPERTY_ID_CURSORCOLOR:
+ rValue = m_aCursorColor;
+ break;
+ case PROPERTY_ID_PRINTABLE:
+ rValue <<= m_bPrintable;
+ break;
+ case PROPERTY_ID_TABSTOP:
+ rValue = m_aTabStop;
+ break;
+ case PROPERTY_ID_HASNAVIGATION:
+ rValue <<= m_bNavigation;
+ break;
+ case PROPERTY_ID_RECORDMARKER:
+ rValue <<= m_bRecordMarker;
+ break;
+ case PROPERTY_ID_ENABLED:
+ rValue <<= m_bEnable;
+ break;
+ case PROPERTY_ID_ENABLEVISIBLE:
+ rValue <<= m_bEnableVisible;
+ break;
+ case PROPERTY_ID_BORDER:
+ rValue <<= m_nBorder;
+ break;
+ case PROPERTY_ID_BORDERCOLOR:
+ rValue = m_aBorderColor;
+ break;
+ case PROPERTY_ID_DEFAULTCONTROL:
+ rValue <<= m_aDefaultControl;
+ break;
+ case PROPERTY_ID_BACKGROUNDCOLOR:
+ rValue = m_aBackgroundColor;
+ break;
+ case PROPERTY_ID_ROWHEIGHT:
+ rValue = m_aRowHeight;
+ break;
+ default:
+ if ( isFontRelatedProperty( nHandle ) )
+ FontControlModel::getFastPropertyValue( rValue, nHandle );
+ else
+ OControlModel::getFastPropertyValue( rValue, nHandle );
+ }
+}
+
+sal_Bool OGridControlModel::convertFastPropertyValue( Any& rConvertedValue, Any& rOldValue,
+ sal_Int32 nHandle, const Any& rValue )
+{
+ bool bModified(false);
+ switch (nHandle)
+ {
+ case PROPERTY_ID_CONTEXT_WRITING_MODE:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_nContextWritingMode );
+ break;
+ case PROPERTY_ID_WRITING_MODE:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_nWritingMode );
+ break;
+ case PROPERTY_ID_HELPTEXT:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_sHelpText);
+ break;
+ case PROPERTY_ID_HELPURL:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_sHelpURL);
+ break;
+ case PROPERTY_ID_DISPLAYSYNCHRON:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bDisplaySynchron);
+ break;
+ case PROPERTY_ID_ALWAYSSHOWCURSOR:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bAlwaysShowCursor);
+ break;
+ case PROPERTY_ID_CURSORCOLOR:
+ if (!rValue.hasValue() || !m_aCursorColor.hasValue())
+ {
+ if (rValue.hasValue() && (TypeClass_LONG != rValue.getValueType().getTypeClass()))
+ {
+ throw IllegalArgumentException();
+ }
+ rOldValue = m_aCursorColor;
+ rConvertedValue = rValue;
+ bModified = rOldValue != rConvertedValue;
+ }
+ else
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, getINT32(m_aCursorColor));
+ break;
+ case PROPERTY_ID_PRINTABLE:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bPrintable);
+ break;
+ case PROPERTY_ID_TABSTOP:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aTabStop, cppu::UnoType<bool>::get());
+ break;
+ case PROPERTY_ID_HASNAVIGATION:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bNavigation);
+ break;
+ case PROPERTY_ID_RECORDMARKER:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bRecordMarker);
+ break;
+ case PROPERTY_ID_ENABLED:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bEnable);
+ break;
+ case PROPERTY_ID_ENABLEVISIBLE:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bEnableVisible);
+ break;
+ case PROPERTY_ID_BORDER:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_nBorder);
+ break;
+ case PROPERTY_ID_BORDERCOLOR:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aBorderColor, cppu::UnoType<sal_Int32>::get());
+ break;
+ case PROPERTY_ID_DEFAULTCONTROL:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aDefaultControl);
+ break;
+ case PROPERTY_ID_BACKGROUNDCOLOR:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aBackgroundColor, cppu::UnoType<sal_Int32>::get());
+ break;
+ case PROPERTY_ID_ROWHEIGHT:
+ {
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aRowHeight, cppu::UnoType<sal_Int32>::get());
+ sal_Int32 nNewVal( 0 );
+ if ( ( rConvertedValue >>= nNewVal ) && ( nNewVal <= 0 ) )
+ {
+ rConvertedValue.clear();
+ bModified = m_aRowHeight.hasValue();
+ }
+ }
+ break;
+ default:
+ if ( isFontRelatedProperty( nHandle ) )
+ bModified = FontControlModel::convertFastPropertyValue( rConvertedValue, rOldValue, nHandle, rValue );
+ else
+ bModified = OControlModel::convertFastPropertyValue( rConvertedValue, rOldValue, nHandle, rValue);
+ }
+ return bModified;
+}
+void OGridControlModel::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_CONTEXT_WRITING_MODE:
+ rValue >>= m_nContextWritingMode;
+ break;
+ case PROPERTY_ID_WRITING_MODE:
+ rValue >>= m_nWritingMode;
+ break;
+ case PROPERTY_ID_HELPTEXT:
+ rValue >>= m_sHelpText;
+ break;
+ case PROPERTY_ID_HELPURL:
+ rValue >>= m_sHelpURL;
+ break;
+ case PROPERTY_ID_DISPLAYSYNCHRON:
+ m_bDisplaySynchron = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_ALWAYSSHOWCURSOR:
+ m_bAlwaysShowCursor = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_CURSORCOLOR:
+ m_aCursorColor = rValue;
+ break;
+ case PROPERTY_ID_PRINTABLE:
+ m_bPrintable = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_TABSTOP:
+ m_aTabStop = rValue;
+ break;
+ case PROPERTY_ID_HASNAVIGATION:
+ m_bNavigation = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_ENABLED:
+ m_bEnable = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_ENABLEVISIBLE:
+ m_bEnableVisible = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_RECORDMARKER:
+ m_bRecordMarker = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_BORDER:
+ rValue >>= m_nBorder;
+ break;
+ case PROPERTY_ID_BORDERCOLOR:
+ m_aBorderColor = rValue;
+ break;
+ case PROPERTY_ID_DEFAULTCONTROL:
+ rValue >>= m_aDefaultControl;
+ break;
+ case PROPERTY_ID_BACKGROUNDCOLOR:
+ m_aBackgroundColor = rValue;
+ break;
+ case PROPERTY_ID_ROWHEIGHT:
+ m_aRowHeight = rValue;
+ break;
+ default:
+ if ( isFontRelatedProperty( nHandle ) )
+ {
+ FontControlModel::setFastPropertyValue_NoBroadcast_impl(
+ *this, &OGridControlModel::setDependentFastPropertyValue,
+ nHandle, rValue);
+ }
+ else
+ OControlModel::setFastPropertyValue_NoBroadcast( nHandle, rValue );
+ }
+}
+
+//XPropertyState
+Any OGridControlModel::getPropertyDefaultByHandle( sal_Int32 nHandle ) const
+{
+ Any aReturn;
+ switch (nHandle)
+ {
+ case PROPERTY_ID_CONTEXT_WRITING_MODE:
+ case PROPERTY_ID_WRITING_MODE:
+ aReturn <<= WritingMode2::CONTEXT;
+ break;
+ case PROPERTY_ID_DEFAULTCONTROL:
+ aReturn <<= OUString( STARDIV_ONE_FORM_CONTROL_GRID );
+ break;
+ case PROPERTY_ID_PRINTABLE:
+ case PROPERTY_ID_HASNAVIGATION:
+ case PROPERTY_ID_RECORDMARKER:
+ case PROPERTY_ID_DISPLAYSYNCHRON:
+ case PROPERTY_ID_ENABLED:
+ case PROPERTY_ID_ENABLEVISIBLE:
+ aReturn <<= true;
+ break;
+ case PROPERTY_ID_ALWAYSSHOWCURSOR:
+ aReturn <<= false;
+ break;
+ case PROPERTY_ID_HELPURL:
+ case PROPERTY_ID_HELPTEXT:
+ aReturn <<= OUString();
+ break;
+ case PROPERTY_ID_BORDER:
+ aReturn <<= sal_Int16(1);
+ break;
+ case PROPERTY_ID_BORDERCOLOR:
+ case PROPERTY_ID_TABSTOP:
+ case PROPERTY_ID_BACKGROUNDCOLOR:
+ case PROPERTY_ID_ROWHEIGHT:
+ case PROPERTY_ID_CURSORCOLOR:
+ // void
+ break;
+ default:
+ if ( isFontRelatedProperty( nHandle ) )
+ aReturn = FontControlModel::getPropertyDefaultByHandle( nHandle );
+ else
+ aReturn = OControlModel::getPropertyDefaultByHandle(nHandle);
+ }
+ return aReturn;
+}
+
+void OGridControlModel::gotColumn( const Reference< XInterface >& _rxColumn )
+{
+ Reference< XSQLErrorBroadcaster > xBroadcaster( _rxColumn, UNO_QUERY );
+ if ( xBroadcaster.is() )
+ xBroadcaster->addSQLErrorListener( this );
+}
+
+void OGridControlModel::lostColumn(const Reference< XInterface >& _rxColumn)
+{
+ if ( m_xSelection == _rxColumn )
+ { // the currently selected element was replaced
+ m_xSelection.clear();
+ EventObject aEvt( static_cast< XWeak* >( this ) );
+ m_aSelectListeners.notifyEach( &XSelectionChangeListener::selectionChanged, aEvt );
+ }
+ Reference< XSQLErrorBroadcaster > xBroadcaster( _rxColumn, UNO_QUERY );
+ if ( xBroadcaster.is() )
+ xBroadcaster->removeSQLErrorListener( this );
+}
+
+void OGridControlModel::implRemoved(const css::uno::Reference<css::uno::XInterface>& _rxObject)
+{
+ OInterfaceContainer::implRemoved(_rxObject);
+ lostColumn(_rxObject);
+}
+
+void OGridControlModel::implInserted( const ElementDescription* _pElement )
+{
+ OInterfaceContainer::implInserted( _pElement );
+ gotColumn( _pElement->xInterface );
+}
+
+void OGridControlModel::impl_replacedElement( const ContainerEvent& _rEvent, ::osl::ClearableMutexGuard& _rInstanceLock )
+{
+ Reference< XInterface > xOldColumn( _rEvent.ReplacedElement, UNO_QUERY );
+ Reference< XInterface > xNewColumn( _rEvent.Element, UNO_QUERY );
+ bool bNewSelection = ( xOldColumn == m_xSelection );
+ lostColumn( xOldColumn );
+ gotColumn( xNewColumn );
+ if ( bNewSelection )
+ m_xSelection.set( xNewColumn, UNO_QUERY );
+ OInterfaceContainer::impl_replacedElement( _rEvent, _rInstanceLock );
+ // < SYNCHRONIZED
+ if ( bNewSelection )
+ {
+ m_aSelectListeners.notifyEach( &XSelectionChangeListener::selectionChanged, EventObject( *this ) );
+ }
+}
+
+ElementDescription* OGridControlModel::createElementMetaData( )
+{
+ return new ElementDescription;
+}
+
+void OGridControlModel::approveNewElement( const Reference< XPropertySet >& _rxObject, ElementDescription* _pElement )
+{
+ OGridColumn* pCol = comphelper::getFromUnoTunnel<OGridColumn>( _rxObject );
+ if ( !pCol )
+ throw IllegalArgumentException();
+ OInterfaceContainer::approveNewElement( _rxObject, _pElement );
+}
+
+// XPersistObject
+OUString SAL_CALL OGridControlModel::getServiceName()
+{
+ return FRM_COMPONENT_GRID; // old (non-sun) name for compatibility!
+}
+
+void OGridControlModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ OControlModel::write(_rxOutStream);
+ Reference<XMarkableStream> xMark(_rxOutStream, UNO_QUERY);
+ // 1. Version
+ _rxOutStream->writeShort(0x0008);
+ // 2. Columns
+ sal_Int32 nLen = getCount();
+ _rxOutStream->writeLong(nLen);
+ for (sal_Int32 i = 0; i < nLen; i++)
+ {
+ // first the service name for the underlying model
+ OGridColumn* pCol = comphelper::getFromUnoTunnel<OGridColumn>(m_aItems[i]);
+ DBG_ASSERT(pCol != nullptr, "OGridControlModel::write : such items should never reach it into my container !");
+ _rxOutStream << pCol->getModelName();
+ // then the object itself
+ sal_Int32 nMark = xMark->createMark();
+ sal_Int32 nObjLen = 0;
+ _rxOutStream->writeLong(nObjLen);
+ // writing the column
+ pCol->write(_rxOutStream);
+ // determining the length
+ nObjLen = xMark->offsetToMark(nMark) - 4;
+ xMark->jumpToMark(nMark);
+ _rxOutStream->writeLong(nObjLen);
+ xMark->jumpToFurthest();
+ xMark->deleteMark(nMark);
+ }
+ // 3. Events
+ writeEvents(_rxOutStream);
+ // 4. Attributes
+ // Masking for all 'any' types
+ sal_uInt16 nAnyMask = 0;
+ if (m_aRowHeight.getValueType().getTypeClass() == TypeClass_LONG)
+ nAnyMask |= ROWHEIGHT;
+ if ( getFont() != getDefaultFont() )
+ nAnyMask |= FONTATTRIBS | FONTSIZE | FONTTYPE | FONTDESCRIPTOR;
+ if (m_aTabStop.getValueType().getTypeClass() == TypeClass_BOOLEAN)
+ nAnyMask |= TABSTOP;
+ if ( hasTextColor() )
+ nAnyMask |= TEXTCOLOR;
+ if (m_aBackgroundColor.getValueType().getTypeClass() == TypeClass_LONG)
+ nAnyMask |= BACKGROUNDCOLOR;
+ if (!m_bRecordMarker)
+ nAnyMask |= RECORDMARKER;
+ _rxOutStream->writeShort(nAnyMask);
+ if (nAnyMask & ROWHEIGHT)
+ _rxOutStream->writeLong(getINT32(m_aRowHeight));
+ // old structures
+ const FontDescriptor& aFont = getFont();
+ if ( nAnyMask & FONTDESCRIPTOR )
+ {
+ // Attrib
+ _rxOutStream->writeShort( sal::static_int_cast< sal_Int16 >( vcl::unohelper::ConvertFontWeight( aFont.Weight ) ) );
+ _rxOutStream->writeShort( sal::static_int_cast< sal_Int16 >( aFont.Slant ) );
+ _rxOutStream->writeShort( aFont.Underline );
+ _rxOutStream->writeShort( aFont.Strikeout );
+ _rxOutStream->writeShort( sal_Int16(aFont.Orientation * 10) );
+ _rxOutStream->writeBoolean( aFont.Kerning );
+ _rxOutStream->writeBoolean( aFont.WordLineMode );
+ // Size
+ _rxOutStream->writeLong( aFont.Width );
+ _rxOutStream->writeLong( aFont.Height );
+ _rxOutStream->writeShort( sal::static_int_cast< sal_Int16 >( vcl::unohelper::ConvertFontWidth( aFont.CharacterWidth ) ) );
+ // Type
+ _rxOutStream->writeUTF( aFont.Name );
+ _rxOutStream->writeUTF( aFont.StyleName );
+ _rxOutStream->writeShort( aFont.Family );
+ _rxOutStream->writeShort( aFont.CharSet );
+ _rxOutStream->writeShort( aFont.Pitch );
+ }
+ _rxOutStream << m_aDefaultControl;
+ _rxOutStream->writeShort(m_nBorder);
+ _rxOutStream->writeBoolean(m_bEnable);
+ if (nAnyMask & TABSTOP)
+ _rxOutStream->writeBoolean(getBOOL(m_aTabStop));
+ _rxOutStream->writeBoolean(m_bNavigation);
+ if (nAnyMask & TEXTCOLOR)
+ _rxOutStream->writeLong( sal_Int32(getTextColor()) );
+ // new since version 6
+ _rxOutStream << m_sHelpText;
+ if (nAnyMask & FONTDESCRIPTOR)
+ _rxOutStream << getFont();
+ if (nAnyMask & RECORDMARKER)
+ _rxOutStream->writeBoolean(m_bRecordMarker);
+ // new since version 7
+ _rxOutStream->writeBoolean(m_bPrintable);
+ // new since version 8
+ if (nAnyMask & BACKGROUNDCOLOR)
+ _rxOutStream->writeLong(getINT32(m_aBackgroundColor));
+}
+
+void OGridControlModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ SolarMutexGuard g;
+ OControlModel::read(_rxInStream);
+ Reference<XMarkableStream> xMark(_rxInStream, UNO_QUERY);
+ // 1. version
+ sal_Int16 nVersion = _rxInStream->readShort();
+ // 2. reading the columns
+ sal_Int32 nLen = _rxInStream->readLong();
+ if (nLen)
+ {
+ for (sal_Int32 i = 0; i < nLen; i++)
+ {
+ // reading the model names
+ OUString sModelName;
+ _rxInStream >> sModelName;
+ Reference<XPropertySet> xCol(createColumnById(getColumnTypeByModelName(sModelName)));
+ DBG_ASSERT(xCol.is(), "OGridControlModel::read : unknown column type !");
+ sal_Int32 nObjLen = _rxInStream->readLong();
+ if (nObjLen)
+ {
+ sal_Int32 nMark = xMark->createMark();
+ if (xCol.is())
+ {
+ OGridColumn* pCol = comphelper::getFromUnoTunnel<OGridColumn>(xCol);
+ pCol->read(_rxInStream);
+ }
+ xMark->jumpToMark(nMark);
+ _rxInStream->skipBytes(nObjLen);
+ xMark->deleteMark(nMark);
+ }
+ if ( xCol.is() )
+ implInsert( i, xCol, false, nullptr, false );
+ }
+ }
+ // In the base implementation events are only read, elements in the container exist
+ // but since before TF_ONE for the GridControl events were always written, so they
+ // need to be read, too
+ sal_Int32 nObjLen = _rxInStream->readLong();
+ if (nObjLen)
+ {
+ sal_Int32 nMark = xMark->createMark();
+ Reference<XPersistObject> xObj(m_xEventAttacher, UNO_QUERY);
+ if (xObj.is())
+ xObj->read(_rxInStream);
+ xMark->jumpToMark(nMark);
+ _rxInStream->skipBytes(nObjLen);
+ xMark->deleteMark(nMark);
+ }
+ // reading the attachment
+ for (sal_Int32 i = 0; i < nLen; i++)
+ {
+ css::uno::Reference<css::uno::XInterface> xIfc(m_aItems[i], UNO_QUERY);
+ Reference<XPropertySet> xSet(xIfc, UNO_QUERY);
+ Any aHelper;
+ aHelper <<= xSet;
+ m_xEventAttacher->attach( i, xIfc, aHelper );
+ }
+ // 4. reading the attributes
+ if (nVersion == 1)
+ return;
+ // Masking for any
+ sal_uInt16 nAnyMask = _rxInStream->readShort();
+ if (nAnyMask & ROWHEIGHT)
+ {
+ sal_Int32 nValue = _rxInStream->readLong();
+ m_aRowHeight <<= nValue;
+ }
+ FontDescriptor aFont( getFont() );
+ if ( nAnyMask & FONTATTRIBS )
+ {
+ aFont.Weight = static_cast<float>(vcl::unohelper::ConvertFontWeight( _rxInStream->readShort() ));
+ aFont.Slant = static_cast<FontSlant>(_rxInStream->readShort());
+ aFont.Underline = _rxInStream->readShort();
+ aFont.Strikeout = _rxInStream->readShort();
+ aFont.Orientation = static_cast<float>(_rxInStream->readShort()) / 10;
+ aFont.Kerning = _rxInStream->readBoolean() != 0;
+ aFont.WordLineMode = _rxInStream->readBoolean() != 0;
+ }
+ if ( nAnyMask & FONTSIZE )
+ {
+ aFont.Width = static_cast<sal_Int16>(_rxInStream->readLong());
+ aFont.Height = static_cast<sal_Int16>(_rxInStream->readLong());
+ aFont.CharacterWidth = static_cast<float>(vcl::unohelper::ConvertFontWidth( _rxInStream->readShort() ));
+ }
+ if ( nAnyMask & FONTTYPE )
+ {
+ aFont.Name = _rxInStream->readUTF();
+ aFont.StyleName = _rxInStream->readUTF();
+ aFont.Family = _rxInStream->readShort();
+ aFont.CharSet = _rxInStream->readShort();
+ aFont.Pitch = _rxInStream->readShort();
+ }
+ if ( nAnyMask & ( FONTATTRIBS | FONTSIZE | FONTTYPE ) )
+ setFont( aFont );
+ // Name
+ _rxInStream >> m_aDefaultControl;
+ m_nBorder = _rxInStream->readShort();
+ m_bEnable = _rxInStream->readBoolean();
+ if (nAnyMask & TABSTOP)
+ {
+ m_aTabStop <<= (_rxInStream->readBoolean() != 0);
+ }
+ if (nVersion > 3)
+ m_bNavigation = _rxInStream->readBoolean();
+ if (nAnyMask & TEXTCOLOR)
+ {
+ sal_Int32 nValue = _rxInStream->readLong();
+ setTextColor( ::Color(ColorTransparency, nValue) );
+ }
+ // new since version 6
+ if (nVersion > 5)
+ _rxInStream >> m_sHelpText;
+ if (nAnyMask & FONTDESCRIPTOR)
+ {
+ FontDescriptor aUNOFont;
+ _rxInStream >> aUNOFont;
+ setFont( aFont );
+ }
+ if (nAnyMask & RECORDMARKER)
+ m_bRecordMarker = _rxInStream->readBoolean();
+ // new since version 7
+ if (nVersion > 6)
+ m_bPrintable = _rxInStream->readBoolean();
+ if (nAnyMask & BACKGROUNDCOLOR)
+ {
+ sal_Int32 nValue = _rxInStream->readLong();
+ m_aBackgroundColor <<= nValue;
+ }
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT XInterface*
+com_sun_star_form_OGridControlModel_get_implementation(XComponentContext* component,
+ Sequence<Any> const &)
+{
+ return cppu::acquire(new frm::OGridControlModel(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Grid.hxx b/forms/source/component/Grid.hxx
new file mode 100644
index 000000000..8339bb49a
--- /dev/null
+++ b/forms/source/component/Grid.hxx
@@ -0,0 +1,200 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "errorbroadcaster.hxx"
+#include <FormComponent.hxx>
+#include <formcontrolfont.hxx>
+#include <InterfaceContainer.hxx>
+
+#include <com/sun/star/form/XGridColumnFactory.hpp>
+#include <com/sun/star/sdb/XRowSetSupplier.hpp>
+#include <com/sun/star/sdb/XRowSetChangeBroadcaster.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+#include <comphelper/interfacecontainer3.hxx>
+#include <cppuhelper/implbase7.hxx>
+
+
+namespace frm
+{
+
+class OGridColumn;
+
+// OGridControlModel
+
+typedef ::cppu::ImplHelper7 < css::awt::XControlModel
+ , css::form::XGridColumnFactory
+ , css::form::XReset
+ , css::view::XSelectionSupplier
+ , css::sdb::XSQLErrorListener
+ , css::sdb::XRowSetSupplier
+ , css::sdb::XRowSetChangeBroadcaster
+ > OGridControlModel_BASE;
+
+class OGridControlModel final :public OControlModel
+ ,public OInterfaceContainer
+ ,public OErrorBroadcaster
+ ,public FontControlModel
+ ,public OGridControlModel_BASE
+{
+ ::comphelper::OInterfaceContainerHelper3<css::view::XSelectionChangeListener> m_aSelectListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::form::XResetListener> m_aResetListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::sdb::XRowSetChangeListener> m_aRowSetChangeListeners;
+
+// [properties]
+ css::uno::Any m_aRowHeight; // Row height
+ css::uno::Any m_aTabStop;
+ css::uno::Any m_aBackgroundColor;
+ css::uno::Any m_aCursorColor; // transient
+ css::uno::Any m_aBorderColor;
+ OUString m_aDefaultControl;
+ OUString m_sHelpText;
+// [properties]
+
+ css::uno::Reference< css::beans::XPropertySet > m_xSelection;
+
+// [properties]
+ OUString m_sHelpURL; // URL
+ sal_Int16 m_nBorder;
+ sal_Int16 m_nWritingMode;
+ sal_Int16 m_nContextWritingMode;
+ bool m_bEnableVisible : 1;
+ bool m_bEnable : 1;
+ bool m_bNavigation : 1;
+ bool m_bRecordMarker : 1;
+ bool m_bPrintable : 1;
+ bool m_bAlwaysShowCursor : 1; // transient
+ bool m_bDisplaySynchron : 1; // transient
+// [properties]
+
+ void _reset();
+
+public:
+ OGridControlModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OGridControlModel(
+ const OGridControlModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OGridControlModel() override;
+
+ // UNO Binding
+ DECLARE_UNO3_AGG_DEFAULTS(OGridControlModel, OControlModel)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+ // XChild
+ virtual void SAL_CALL setParent(const css::uno::Reference<css::uno::XInterface>& Parent) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OGridControlModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource) override;
+
+ // XReset
+ virtual void SAL_CALL reset() override;
+ virtual void SAL_CALL addResetListener(const css::uno::Reference< css::form::XResetListener>& _rxListener) override;
+ virtual void SAL_CALL removeResetListener(const css::uno::Reference< css::form::XResetListener>& _rxListener) override;
+
+ // XSelectionSupplier
+ virtual sal_Bool SAL_CALL select(const css::uno::Any& aElement) override;
+ virtual css::uno::Any SAL_CALL getSelection() override;
+ virtual void SAL_CALL addSelectionChangeListener(const css::uno::Reference< css::view::XSelectionChangeListener >& xListener) override;
+ virtual void SAL_CALL removeSelectionChangeListener(const css::uno::Reference< css::view::XSelectionChangeListener >& xListener) override;
+
+ // XGridColumnFactory
+ virtual css::uno::Reference< css::beans::XPropertySet> SAL_CALL createColumn(const OUString& ColumnType) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getColumnTypes() override;
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // XPropertySet
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle ) const override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(css::uno::Any& rConvertedValue, css::uno::Any& rOldValue,
+ sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const css::uno::Any& rValue) override;
+
+ // XPropertyState
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 nHandle ) const override;
+
+ // XSQLErrorListener
+ virtual void SAL_CALL errorOccured( const css::sdb::SQLErrorEvent& _rEvent ) override;
+
+ // XRowSetSupplier
+ virtual css::uno::Reference< css::sdbc::XRowSet > SAL_CALL getRowSet( ) override;
+ virtual void SAL_CALL setRowSet( const css::uno::Reference< css::sdbc::XRowSet >& xDataSource ) override;
+
+ // XRowSetChangeBroadcaster
+ virtual void SAL_CALL addRowSetChangeListener( const css::uno::Reference< css::sdb::XRowSetChangeListener >& i_Listener ) override;
+ virtual void SAL_CALL removeRowSetChangeListener( const css::uno::Reference< css::sdb::XRowSetChangeListener >& i_Listener ) override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+ // prevent method hiding
+ using OControlModel::disposing;
+ using OControlModel::getFastPropertyValue;
+
+private:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ virtual void approveNewElement(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxObject,
+ ElementDescription* _pElement
+ ) override;
+
+ css::uno::Reference< css::beans::XPropertySet> createColumnById(sal_Int32 nTypeId) const;
+
+ virtual ElementDescription* createElementMetaData( ) override;
+
+ virtual void implRemoved(const css::uno::Reference<css::uno::XInterface>& _rxObject) override;
+ virtual void implInserted( const ElementDescription* _pElement ) override;
+ virtual void impl_replacedElement(
+ const css::container::ContainerEvent& _rEvent,
+ ::osl::ClearableMutexGuard& _rInstanceLock
+ ) override;
+
+ void gotColumn(const css::uno::Reference< css::uno::XInterface >& _rxColumn);
+ void lostColumn(const css::uno::Reference< css::uno::XInterface >& _rxColumn);
+
+ void cloneColumns( const OGridControlModel* _pOriginalContainer );
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/GroupBox.cxx b/forms/source/component/GroupBox.cxx
new file mode 100644
index 000000000..fab26e2e7
--- /dev/null
+++ b/forms/source/component/GroupBox.cxx
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "GroupBox.hxx"
+#include <frm_strings.hxx>
+#include <services.hxx>
+#include <comphelper/property.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+namespace frm
+{
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+using namespace comphelper;
+
+// OGroupBoxModel
+
+
+OGroupBoxModel::OGroupBoxModel(const Reference<XComponentContext>& _rxFactory)
+ :OControlModel(_rxFactory, VCL_CONTROLMODEL_GROUPBOX, VCL_CONTROL_GROUPBOX)
+{
+ m_nClassId = FormComponentType::GROUPBOX;
+}
+
+
+OGroupBoxModel::OGroupBoxModel( const OGroupBoxModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OControlModel( _pOriginal, _rxFactory )
+{
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL OGroupBoxModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OControlModel::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString* pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_COMPONENT_GROUPBOX;
+ pArray[aSupported.getLength()-1] = FRM_COMPONENT_GROUPBOX;
+ return aSupported;
+}
+
+
+OGroupBoxModel::~OGroupBoxModel()
+{
+}
+
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OGroupBoxModel::createClone()
+{
+ rtl::Reference<OGroupBoxModel> pClone = new OGroupBoxModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+void OGroupBoxModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const
+{
+ OControlModel::describeAggregateProperties( _rAggregateProps );
+ // don't want to have the TabStop property
+ RemoveProperty(_rAggregateProps, PROPERTY_TABSTOP);
+}
+
+
+OUString SAL_CALL OGroupBoxModel::getServiceName()
+{
+ return FRM_COMPONENT_GROUPBOX; // old (non-sun) name for compatibility !
+}
+
+
+void SAL_CALL OGroupBoxModel::write(const Reference< XObjectOutputStream>& _rxOutStream)
+{
+ OControlModel::write(_rxOutStream);
+
+ // Version
+ _rxOutStream->writeShort(0x0002);
+ writeHelpTextCompatibly(_rxOutStream);
+}
+
+
+void SAL_CALL OGroupBoxModel::read(const Reference< XObjectInputStream>& _rxInStream)
+{
+ OControlModel::read( _rxInStream );
+
+ // Version
+ sal_uInt16 nVersion = _rxInStream->readShort();
+ DBG_ASSERT(nVersion > 0, "OGroupBoxModel::read : version 0 ? this should never have been written !");
+
+ if (nVersion == 2)
+ readHelpTextCompatibly(_rxInStream);
+
+ if (nVersion > 0x0002)
+ {
+ OSL_FAIL("OGroupBoxModel::read : unknown version !");
+ }
+};
+
+
+// OGroupBoxControl
+
+OGroupBoxControl::OGroupBoxControl(const Reference<XComponentContext>& _rxFactory)
+ :OControl(_rxFactory, VCL_CONTROL_GROUPBOX)
+{
+}
+
+
+css::uno::Sequence<OUString> SAL_CALL OGroupBoxControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString* pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_GROUPBOX;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_GROUPBOX;
+ return aSupported;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OGroupBoxModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OGroupBoxModel(component));
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OGroupBoxControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OGroupBoxControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/GroupBox.hxx b/forms/source/component/GroupBox.hxx
new file mode 100644
index 000000000..c1c53df0b
--- /dev/null
+++ b/forms/source/component/GroupBox.hxx
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+
+
+namespace frm
+{
+
+class OGroupBoxModel
+ :public OControlModel
+{
+public:
+ OGroupBoxModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OGroupBoxModel(
+ const OGroupBoxModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OGroupBoxModel() override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OGroupBoxModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL
+ write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL
+ read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // OControlModel's property handling
+ virtual void describeAggregateProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+
+// OGroupBoxControl (only for compatibility for 5.0)
+
+class OGroupBoxControl : public OControl
+{
+public:
+ explicit OGroupBoxControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OGroupBoxControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/GroupManager.cxx b/forms/source/component/GroupManager.cxx
new file mode 100644
index 000000000..deec1bcd5
--- /dev/null
+++ b/forms/source/component/GroupManager.cxx
@@ -0,0 +1,421 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "GroupManager.hxx"
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#include <frm_strings.hxx>
+
+#include <algorithm>
+
+namespace frm
+{
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::lang;
+using namespace ::comphelper;
+
+namespace
+{
+ bool isRadioButton( const Reference< XPropertySet >& _rxComponent )
+ {
+ bool bIs = false;
+ if ( hasProperty( PROPERTY_CLASSID, _rxComponent ) )
+ {
+ sal_Int16 nClassId = FormComponentType::CONTROL;
+ _rxComponent->getPropertyValue( PROPERTY_CLASSID ) >>= nClassId;
+ if ( nClassId == FormComponentType::RADIOBUTTON )
+ bIs = true;
+ }
+ return bIs;
+ }
+}
+
+OGroupCompAcc::OGroupCompAcc(const Reference<XPropertySet>& rxElement, const OGroupComp& _rGroupComp )
+ :m_xComponent( rxElement )
+ ,m_aGroupComp( _rGroupComp )
+{
+}
+
+bool OGroupCompAcc::operator==( const OGroupCompAcc& rCompAcc ) const
+{
+ return m_xComponent == rCompAcc.m_xComponent;
+}
+
+class OGroupCompAccLess
+{
+public:
+ bool operator() (const OGroupCompAcc& lhs, const OGroupCompAcc& rhs) const
+ {
+ return
+ reinterpret_cast<sal_Int64>(lhs.m_xComponent.get())
+ < reinterpret_cast<sal_Int64>(rhs.m_xComponent.get());
+ }
+};
+
+OGroupComp::OGroupComp()
+ :m_nPos( -1 )
+ ,m_nTabIndex( 0 )
+{
+}
+
+OGroupComp::OGroupComp(const Reference<XPropertySet>& rxSet, sal_Int32 nInsertPos )
+ : m_xComponent( rxSet )
+ , m_xControlModel(rxSet,UNO_QUERY)
+ , m_nPos( nInsertPos )
+ , m_nTabIndex(0)
+{
+ if (m_xComponent.is())
+ {
+ if (hasProperty( PROPERTY_TABINDEX, m_xComponent ) )
+ // Indices smaller than 0 are treated like 0
+ m_nTabIndex = std::max(getINT16(m_xComponent->getPropertyValue( PROPERTY_TABINDEX )) , sal_Int16(0));
+ }
+}
+
+bool OGroupComp::operator==( const OGroupComp& rComp ) const
+{
+ return m_nTabIndex == rComp.GetTabIndex() && m_nPos == rComp.GetPos();
+}
+
+class OGroupCompLess
+{
+public:
+ bool operator() (const OGroupComp& lhs, const OGroupComp& rhs) const
+ {
+ bool bResult;
+ // TabIndex of 0 will be added at the end
+ if (lhs.m_nTabIndex == rhs.GetTabIndex())
+ bResult = lhs.m_nPos < rhs.GetPos();
+ else if (lhs.m_nTabIndex && rhs.GetTabIndex())
+ bResult = lhs.m_nTabIndex < rhs.GetTabIndex();
+ else
+ bResult = lhs.m_nTabIndex != 0;
+ return bResult;
+ }
+};
+
+OGroup::OGroup( const OUString& rGroupName )
+ :m_aGroupName( rGroupName )
+ ,m_nInsertPos(0)
+{
+}
+
+void OGroup::InsertComponent( const Reference<XPropertySet>& xSet )
+{
+ OGroupComp aNewGroupComp( xSet, m_nInsertPos );
+ sal_Int32 nPosInserted = insert_sorted(m_aCompArray, aNewGroupComp, OGroupCompLess());
+
+ OGroupCompAcc aNewGroupCompAcc( xSet, m_aCompArray[nPosInserted] );
+ insert_sorted(m_aCompAccArray, aNewGroupCompAcc, OGroupCompAccLess());
+ m_nInsertPos++;
+}
+
+void OGroup::RemoveComponent( const Reference<XPropertySet>& rxElement )
+{
+ sal_Int32 nGroupCompAccPos;
+ OGroupCompAcc aSearchCompAcc( rxElement, OGroupComp() );
+ if ( seek_entry(m_aCompAccArray, aSearchCompAcc, nGroupCompAccPos, OGroupCompAccLess()) )
+ {
+ OGroupCompAcc& aGroupCompAcc = m_aCompAccArray[nGroupCompAccPos];
+ const OGroupComp& aGroupComp = aGroupCompAcc.GetGroupComponent();
+
+ sal_Int32 nGroupCompPos;
+ if ( seek_entry(m_aCompArray, aGroupComp, nGroupCompPos, OGroupCompLess()) )
+ {
+ m_aCompAccArray.erase( m_aCompAccArray.begin() + nGroupCompAccPos );
+ m_aCompArray.erase( m_aCompArray.begin() + nGroupCompPos );
+
+ /*
+ * By removing the GroupComp the insertion position has become invalid.
+ * We do not to change it here, however, because it's passed on continuously
+ * and ascending distinctively.
+ */
+ }
+ else
+ {
+ OSL_FAIL( "OGroup::RemoveComponent: Component not in Group" );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "OGroup::RemoveComponent: Component not in Group" );
+ }
+}
+
+Sequence< Reference<XControlModel> > OGroup::GetControlModels() const
+{
+ Sequence<Reference<XControlModel> > aControlModelSeq( m_aCompArray.size() );
+ Reference<XControlModel>* pModels = aControlModelSeq.getArray();
+
+ for (auto const& rGroupComp : m_aCompArray)
+ {
+ *pModels++ = rGroupComp.GetControlModel();
+ }
+ return aControlModelSeq;
+}
+
+OGroupManager::OGroupManager(const Reference< XContainer >& _rxContainer)
+ :m_pCompGroup( new OGroup( "AllComponentGroup" ) )
+ ,m_xContainer(_rxContainer)
+{
+ osl_atomic_increment(&m_refCount);
+ {
+ _rxContainer->addContainerListener(this);
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+OGroupManager::~OGroupManager()
+{
+}
+
+// XPropertyChangeListener
+void OGroupManager::disposing(const EventObject& evt)
+{
+ Reference<XContainer> xContainer(evt.Source, UNO_QUERY);
+ if (xContainer.get() == m_xContainer.get())
+ {
+ m_pCompGroup.reset();
+
+ // delete group
+ m_aGroupArr.clear();
+ m_xContainer.clear();
+ }
+}
+
+void OGroupManager::removeFromGroupMap(const OUString& _sGroupName,const Reference<XPropertySet>& _xSet)
+{
+ // remove Component from CompGroup
+ m_pCompGroup->RemoveComponent( _xSet );
+
+ OGroupArr::iterator aFind = m_aGroupArr.find(_sGroupName);
+
+ if ( aFind != m_aGroupArr.end() )
+ {
+ // group exists
+ aFind->second.RemoveComponent( _xSet );
+
+ // If the count of Group elements == 1 -> deactivate Group
+ sal_Int32 nCount = aFind->second.Count();
+ if ( nCount == 1 || nCount == 0 )
+ {
+ OActiveGroups::iterator aActiveFind = ::std::find(
+ m_aActiveGroupMap.begin(),
+ m_aActiveGroupMap.end(),
+ aFind
+ );
+ if ( aActiveFind != m_aActiveGroupMap.end() )
+ {
+ // the group is active. Deactivate it if the remaining component
+ // is *no* radio button
+ if ( nCount == 0 || !isRadioButton( aFind->second.GetObject( 0 ) ) )
+ m_aActiveGroupMap.erase( aActiveFind );
+ }
+ }
+ }
+
+
+ // Deregister as PropertyChangeListener at Component
+ _xSet->removePropertyChangeListener( PROPERTY_NAME, this );
+ if (hasProperty(PROPERTY_GROUP_NAME, _xSet))
+ _xSet->removePropertyChangeListener( PROPERTY_GROUP_NAME, this );
+ if (hasProperty(PROPERTY_TABINDEX, _xSet))
+ _xSet->removePropertyChangeListener( PROPERTY_TABINDEX, this );
+}
+
+void SAL_CALL OGroupManager::propertyChange(const PropertyChangeEvent& evt)
+{
+ Reference<XPropertySet> xSet(evt.Source, UNO_QUERY);
+
+ // remove Component from group
+ OUString sGroupName;
+ if (hasProperty( PROPERTY_GROUP_NAME, xSet ))
+ xSet->getPropertyValue( PROPERTY_GROUP_NAME ) >>= sGroupName;
+ if (evt.PropertyName == PROPERTY_NAME) {
+ if (!sGroupName.isEmpty())
+ return; // group hasn't changed; ignore this name change.
+ // no GroupName; use Name as GroupName
+ evt.OldValue >>= sGroupName;
+ }
+ else if (evt.PropertyName == PROPERTY_GROUP_NAME) {
+ evt.OldValue >>= sGroupName;
+ if (sGroupName.isEmpty()) {
+ // No prior GroupName; fallback to Name
+ xSet->getPropertyValue( PROPERTY_NAME ) >>= sGroupName;
+ }
+ }
+ else
+ sGroupName = GetGroupName( xSet );
+
+ removeFromGroupMap(sGroupName,xSet);
+
+ // Re-insert Component
+ InsertElement( xSet );
+}
+
+// XContainerListener
+void SAL_CALL OGroupManager::elementInserted(const ContainerEvent& Event)
+{
+ Reference< XPropertySet > xProps;
+ Event.Element >>= xProps;
+ if ( xProps.is() )
+ InsertElement( xProps );
+}
+
+void SAL_CALL OGroupManager::elementRemoved(const ContainerEvent& Event)
+{
+ Reference<XPropertySet> xProps;
+ Event.Element >>= xProps;
+ if ( xProps.is() )
+ RemoveElement( xProps );
+}
+
+void SAL_CALL OGroupManager::elementReplaced(const ContainerEvent& Event)
+{
+ Reference<XPropertySet> xProps;
+ Event.ReplacedElement >>= xProps;
+ if ( xProps.is() )
+ RemoveElement( xProps );
+
+ xProps.clear();
+ Event.Element >>= xProps;
+ if ( xProps.is() )
+ InsertElement( xProps );
+}
+
+// Other functions
+Sequence<Reference<XControlModel> > OGroupManager::getControlModels() const
+{
+ return m_pCompGroup->GetControlModels();
+}
+
+sal_Int32 OGroupManager::getGroupCount() const
+{
+ return m_aActiveGroupMap.size();
+}
+
+void OGroupManager::getGroup(sal_Int32 nGroup, Sequence< Reference<XControlModel> >& _rGroup, OUString& _rName)
+{
+ OSL_ENSURE(nGroup >= 0 && o3tl::make_unsigned(nGroup) < m_aActiveGroupMap.size(),"OGroupManager::getGroup: Invalid group index!");
+ OGroupArr::iterator aGroupPos = m_aActiveGroupMap[nGroup];
+ _rName = aGroupPos->second.GetGroupName();
+ _rGroup = aGroupPos->second.GetControlModels();
+}
+
+void OGroupManager::getGroupByName(const OUString& _rName, Sequence< Reference<XControlModel> >& _rGroup)
+{
+ OGroupArr::iterator aFind = m_aGroupArr.find(_rName);
+ if ( aFind != m_aGroupArr.end() )
+ _rGroup = aFind->second.GetControlModels();
+}
+
+void OGroupManager::InsertElement( const Reference<XPropertySet>& xSet )
+{
+ // Only ControlModels
+ Reference<XControlModel> xControl(xSet, UNO_QUERY);
+ if (!xControl.is() )
+ return;
+
+ // Add Component to CompGroup
+ m_pCompGroup->InsertComponent( xSet );
+
+ // Add Component to Group
+ OUString sGroupName( GetGroupName( xSet ) );
+
+ OGroupArr::iterator aFind = m_aGroupArr.find(sGroupName);
+
+ if ( aFind == m_aGroupArr.end() )
+ {
+ aFind = m_aGroupArr.emplace(sGroupName,OGroup(sGroupName)).first;
+ }
+
+ aFind->second.InsertComponent( xSet );
+
+ // if we have at least 2 elements in the group, then this is an "active group"
+ bool bActivateGroup = aFind->second.Count() == 2;
+
+ // Additionally, if the component is a radio button, then it's group becomes active,
+ // too. With this, we ensure that in a container with n radio buttons which all are
+ // in different groups the selection still works reliably (means that all radios can be
+ // clicked independently)
+ if ( aFind->second.Count() == 1 )
+ {
+ if ( isRadioButton( xSet ) )
+ bActivateGroup = true;
+ }
+
+ if ( bActivateGroup )
+ {
+ OActiveGroups::iterator aAlreadyExistent = ::std::find(
+ m_aActiveGroupMap.begin(),
+ m_aActiveGroupMap.end(),
+ aFind
+ );
+ if ( aAlreadyExistent == m_aActiveGroupMap.end() )
+ m_aActiveGroupMap.push_back( aFind );
+ }
+
+ // Register as PropertyChangeListener at Component
+ xSet->addPropertyChangeListener( PROPERTY_NAME, this );
+ if (hasProperty(PROPERTY_GROUP_NAME, xSet))
+ xSet->addPropertyChangeListener( PROPERTY_GROUP_NAME, this );
+
+ // Not everyone needs to support Tabindex
+ if (hasProperty(PROPERTY_TABINDEX, xSet))
+ xSet->addPropertyChangeListener( PROPERTY_TABINDEX, this );
+}
+
+void OGroupManager::RemoveElement( const Reference<XPropertySet>& xSet )
+{
+ // Only ControlModels
+ Reference<XControlModel> xControl(xSet, UNO_QUERY);
+ if (!xControl.is() )
+ return;
+
+ // Remove Component from Group
+ OUString sGroupName( GetGroupName( xSet ) );
+
+ removeFromGroupMap(sGroupName,xSet);
+}
+
+OUString OGroupManager::GetGroupName( const css::uno::Reference< css::beans::XPropertySet>& xComponent )
+{
+ if (!xComponent.is())
+ return OUString();
+ OUString sGroupName;
+ if (hasProperty( PROPERTY_GROUP_NAME, xComponent )) {
+ xComponent->getPropertyValue( PROPERTY_GROUP_NAME ) >>= sGroupName;
+ if (sGroupName.isEmpty())
+ xComponent->getPropertyValue( PROPERTY_NAME ) >>= sGroupName;
+ }
+ else
+ xComponent->getPropertyValue( PROPERTY_NAME ) >>= sGroupName;
+ return sGroupName;
+}
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/GroupManager.hxx b/forms/source/component/GroupManager.hxx
new file mode 100644
index 000000000..9365d1b44
--- /dev/null
+++ b/forms/source/component/GroupManager.hxx
@@ -0,0 +1,194 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyChangeListener.hpp>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+/*
+ * The GroupManager listens at the StarForm for FormComponent insertion and removal as well as
+ * its properties "Name" and "TabIndex" and updates its Group using this information.
+ *
+ * The GroupManager manages a Group in which all Controls are sorted by TabIndex.
+ * It also manages an array of Groups, in which each FormComponent is assigned a
+ * Group according to its name.
+ * Each Group is activated using a Map, if they contain more than one element.
+ *
+ * The Groups manage the FormComponents internally using two arrays.
+ * In the GroupCompArray the Components are sorted by TabIndex and insertion position.
+ * Because this array is accessed via the FormComponent, we also have the GroupCompAccessArray
+ * in which the FormComponents are sorted by their storage address.
+ * Every element of the GroupCompArray has a pointer to the GroupCompArray.
+ */
+namespace frm
+{
+
+
+ template <class ELEMENT, class LESS_COMPARE>
+ sal_Int32 insert_sorted(::std::vector<ELEMENT>& _rArray, const ELEMENT& _rNewElement, const LESS_COMPARE& _rCompareOp)
+ {
+ typename ::std::vector<ELEMENT>::iterator aInsertPos = ::std::lower_bound(
+ _rArray.begin(),
+ _rArray.end(),
+ _rNewElement,
+ _rCompareOp
+ );
+ aInsertPos = _rArray.insert(aInsertPos, _rNewElement);
+ return aInsertPos - _rArray.begin();
+ }
+
+ template <class ELEMENT, class LESS_COMPARE>
+ bool seek_entry(const ::std::vector<ELEMENT>& _rArray, const ELEMENT& _rNewElement, sal_Int32& nPos, const LESS_COMPARE& _rCompareOp)
+ {
+ typename ::std::vector<ELEMENT>::const_iterator aExistentPos = ::std::lower_bound(
+ _rArray.begin(),
+ _rArray.end(),
+ _rNewElement,
+ _rCompareOp
+ );
+ if ((aExistentPos != _rArray.end()) && (*aExistentPos == _rNewElement))
+ { // we have a valid "lower or equal" element and it's really "equal"
+ nPos = aExistentPos - _rArray.begin();
+ return true;
+ }
+ nPos = -1;
+ return false;
+ }
+
+
+class OGroupComp
+{
+ css::uno::Reference< css::beans::XPropertySet> m_xComponent;
+ css::uno::Reference< css::awt::XControlModel> m_xControlModel;
+ sal_Int32 m_nPos;
+ sal_Int16 m_nTabIndex;
+
+ friend class OGroupCompLess;
+
+public:
+ OGroupComp(const css::uno::Reference< css::beans::XPropertySet>& rxElement, sal_Int32 nInsertPos );
+ OGroupComp();
+
+ bool operator==( const OGroupComp& rComp ) const;
+
+ const css::uno::Reference< css::beans::XPropertySet>& GetComponent() const { return m_xComponent; }
+ const css::uno::Reference< css::awt::XControlModel>& GetControlModel() const { return m_xControlModel; }
+
+ sal_Int32 GetPos() const { return m_nPos; }
+ sal_Int16 GetTabIndex() const { return m_nTabIndex; }
+};
+
+
+class OGroupComp;
+class OGroupCompAcc
+{
+ css::uno::Reference< css::beans::XPropertySet> m_xComponent;
+
+ OGroupComp m_aGroupComp;
+
+ friend class OGroupCompAccLess;
+
+public:
+ OGroupCompAcc(const css::uno::Reference< css::beans::XPropertySet>& rxElement, const OGroupComp& _rGroupComp );
+
+ bool operator==( const OGroupCompAcc& rCompAcc ) const;
+
+ const OGroupComp& GetGroupComponent() const { return m_aGroupComp; }
+};
+
+class OGroup final
+{
+ std::vector<OGroupComp> m_aCompArray;
+ std::vector<OGroupCompAcc> m_aCompAccArray;
+
+ OUString m_aGroupName;
+ sal_uInt16 m_nInsertPos; // The insertion position of the GroupComps is determined by the Group
+
+ friend class OGroupLess;
+
+public:
+ explicit OGroup(const OUString& rGroupName);
+
+ const OUString& GetGroupName() const { return m_aGroupName; }
+ css::uno::Sequence< css::uno::Reference< css::awt::XControlModel> > GetControlModels() const;
+
+ void InsertComponent( const css::uno::Reference< css::beans::XPropertySet>& rxElement );
+ void RemoveComponent( const css::uno::Reference< css::beans::XPropertySet>& rxElement );
+ sal_uInt16 Count() const { return sal::static_int_cast< sal_uInt16 >(m_aCompArray.size()); }
+ const css::uno::Reference< css::beans::XPropertySet>& GetObject( sal_uInt16 nP ) const
+ { return m_aCompArray[nP].GetComponent(); }
+};
+
+typedef std::map<OUString, OGroup> OGroupArr;
+typedef std::vector<OGroupArr::iterator> OActiveGroups;
+
+
+class OGroupManager : public ::cppu::WeakImplHelper< css::beans::XPropertyChangeListener, css::container::XContainerListener>
+{
+ std::unique_ptr<OGroup>
+ m_pCompGroup; // Sort all Components by TabIndices
+ OGroupArr m_aGroupArr; // Sort all Components by group
+ OActiveGroups m_aActiveGroupMap; // This map contains all indices of all groups with more than 1 element
+
+ css::uno::Reference< css::container::XContainer >
+ m_xContainer;
+
+ // Helper functions
+ void InsertElement( const css::uno::Reference< css::beans::XPropertySet>& rxElement );
+ void RemoveElement( const css::uno::Reference< css::beans::XPropertySet>& rxElement );
+ void removeFromGroupMap(const OUString& _sGroupName,const css::uno::Reference< css::beans::XPropertySet>& _xSet);
+
+public:
+ explicit OGroupManager(const css::uno::Reference< css::container::XContainer >& _rxContainer);
+ virtual ~OGroupManager() override;
+
+// css::lang::XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource) override;
+
+// css::beans::XPropertyChangeListener
+ virtual void SAL_CALL propertyChange(const css::beans::PropertyChangeEvent& evt) override;
+
+// css::container::XContainerListener
+ virtual void SAL_CALL elementInserted(const css::container::ContainerEvent& _rEvent) override;
+ virtual void SAL_CALL elementRemoved(const css::container::ContainerEvent& _rEvent) override;
+ virtual void SAL_CALL elementReplaced(const css::container::ContainerEvent& _rEvent) override;
+
+// Other functions
+ sal_Int32 getGroupCount() const;
+ void getGroup(sal_Int32 nGroup, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel> >& _rGroup, OUString& Name);
+ void getGroupByName(const OUString& Name, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel> >& _rGroup);
+ css::uno::Sequence< css::uno::Reference< css::awt::XControlModel> > getControlModels() const;
+
+ static OUString GetGroupName( const css::uno::Reference< css::beans::XPropertySet>& xComponent );
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Hidden.cxx b/forms/source/component/Hidden.cxx
new file mode 100644
index 000000000..2bef53ca4
--- /dev/null
+++ b/forms/source/component/Hidden.cxx
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "Hidden.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+
+namespace frm
+{
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+
+
+OHiddenModel::OHiddenModel(const Reference<XComponentContext>& _rxFactory)
+ :OControlModel(_rxFactory, OUString())
+{
+ m_nClassId = FormComponentType::HIDDENCONTROL;
+}
+
+
+OHiddenModel::OHiddenModel( const OHiddenModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OControlModel( _pOriginal, _rxFactory )
+{
+ m_sHiddenValue = _pOriginal->m_sHiddenValue;
+}
+
+
+OHiddenModel::~OHiddenModel( )
+{
+}
+
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OHiddenModel::createClone()
+{
+ rtl::Reference<OHiddenModel> pClone = new OHiddenModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+void OHiddenModel::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const
+{
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_HIDDEN_VALUE : _rValue <<= m_sHiddenValue; break;
+ default:
+ OControlModel::getFastPropertyValue(_rValue, _nHandle);
+ }
+}
+
+
+void OHiddenModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
+{
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_HIDDEN_VALUE :
+ DBG_ASSERT(_rValue.getValueType().getTypeClass() == TypeClass_STRING, "OHiddenModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ _rValue >>= m_sHiddenValue;
+ break;
+ default:
+ OControlModel::setFastPropertyValue_NoBroadcast(_nHandle, _rValue);
+ }
+}
+
+
+sal_Bool OHiddenModel::convertFastPropertyValue(
+ Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue)
+{
+ bool bModified(false);
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_HIDDEN_VALUE :
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_sHiddenValue);
+ break;
+ default:
+ bModified = OControlModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue);
+ break;
+ }
+ return bModified;
+}
+
+
+void OHiddenModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ _rProps.realloc(4);
+ css::beans::Property* pProperties = _rProps.getArray();
+ *pProperties++ = css::beans::Property(PROPERTY_CLASSID, PROPERTY_ID_CLASSID, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_HIDDEN_VALUE, PROPERTY_ID_HIDDEN_VALUE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_NAME, PROPERTY_ID_NAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TAG, PROPERTY_ID_TAG, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL OHiddenModel::getSupportedServiceNames()
+{
+ return css::uno::Sequence<OUString>{
+ FRM_SUN_COMPONENT_HIDDENCONTROL, FRM_SUN_FORMCOMPONENT,
+ FRM_COMPONENT_HIDDEN, FRM_COMPONENT_HIDDENCONTROL };
+}
+
+
+OUString SAL_CALL OHiddenModel::getServiceName()
+{
+ return FRM_COMPONENT_HIDDEN; // old (non-sun) name for compatibility !
+}
+
+
+void SAL_CALL OHiddenModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ // Version
+ _rxOutStream->writeShort(0x0002);
+
+ // Value
+ _rxOutStream << m_sHiddenValue;
+
+ OControlModel::write(_rxOutStream);
+}
+
+
+void SAL_CALL OHiddenModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ // Version
+ sal_uInt16 nVersion = _rxInStream->readShort();
+
+ // Name
+ DBG_ASSERT(nVersion != 1, "OHiddenModel::read : this version is obsolete !");
+ switch (nVersion)
+ {
+ case 1 : { OUString sDummy; _rxInStream >> sDummy; _rxInStream >> m_sHiddenValue; } break;
+ case 2 : _rxInStream >> m_sHiddenValue; break;
+ default : OSL_FAIL("OHiddenModel::read : unknown version !"); m_sHiddenValue.clear();
+ }
+ OControlModel::read(_rxInStream);
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OHiddenModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OHiddenModel(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Hidden.hxx b/forms/source/component/Hidden.hxx
new file mode 100644
index 000000000..7bd01ccf9
--- /dev/null
+++ b/forms/source/component/Hidden.hxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+
+
+namespace frm
+{
+
+class OHiddenModel
+ :public OControlModel
+{
+ OUString m_sHiddenValue;
+
+public:
+ OHiddenModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OHiddenModel(
+ const OHiddenModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OHiddenModel() override;
+
+ // OPropertySetHelper
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle) const override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(
+ css::uno::Any& _rConvertedValue, css::uno::Any& _rOldValue, sal_Int32 _nHandle, const css::uno::Any& _rValue ) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OHiddenModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL
+ write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL
+ read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+ // prevent method hiding
+ using OControlModel::getFastPropertyValue;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/ImageButton.cxx b/forms/source/component/ImageButton.cxx
new file mode 100644
index 000000000..57bbe3969
--- /dev/null
+++ b/forms/source/component/ImageButton.cxx
@@ -0,0 +1,249 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ImageButton.hxx"
+#include <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/mutex.hxx>
+#include <comphelper/basicio.hxx>
+#include <com/sun/star/awt/MouseButton.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <property.hxx>
+#include <services.hxx>
+
+namespace frm
+{
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+
+// OImageButtonModel
+OImageButtonModel::OImageButtonModel(const Reference<XComponentContext>& _rxFactory)
+ :OClickableImageBaseModel( _rxFactory, VCL_CONTROLMODEL_IMAGEBUTTON, FRM_SUN_CONTROL_IMAGEBUTTON )
+ // use the old control name for compytibility reasons
+{
+ m_nClassId = FormComponentType::IMAGEBUTTON;
+}
+
+OImageButtonModel::OImageButtonModel( const OImageButtonModel* _pOriginal, const Reference<XComponentContext>& _rxFactory)
+ :OClickableImageBaseModel( _pOriginal, _rxFactory )
+{
+ implInitializeImageURL();
+}
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OImageButtonModel::createClone()
+{
+ rtl::Reference<OImageButtonModel> pClone = new OImageButtonModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+OImageButtonModel::~OImageButtonModel()
+{
+}
+
+// XServiceInfo
+css::uno::Sequence<OUString> OImageButtonModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OClickableImageBaseModel::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_COMPONENT_IMAGEBUTTON;
+ pArray[aSupported.getLength()-1] = FRM_COMPONENT_IMAGEBUTTON;
+ return aSupported;
+}
+
+void OImageButtonModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OClickableImageBaseModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 5);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_BUTTONTYPE, PROPERTY_ID_BUTTONTYPE, cppu::UnoType<FormButtonType>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_DISPATCHURLINTERNAL, PROPERTY_ID_DISPATCHURLINTERNAL, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TARGET_URL, PROPERTY_ID_TARGET_URL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TARGET_FRAME, PROPERTY_ID_TARGET_FRAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+OUString OImageButtonModel::getServiceName()
+{
+ return FRM_COMPONENT_IMAGEBUTTON; // old (non-sun) name for compatibility !
+}
+
+void OImageButtonModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ OControlModel::write(_rxOutStream);
+
+ // Version
+ _rxOutStream->writeShort(0x0003);
+ _rxOutStream->writeShort(static_cast<sal_uInt16>(m_eButtonType));
+
+ OUString sTmp(INetURLObject::decode( m_sTargetURL, INetURLObject::DecodeMechanism::Unambiguous));
+ _rxOutStream << sTmp;
+ _rxOutStream << m_sTargetFrame;
+ writeHelpTextCompatibly(_rxOutStream);
+}
+
+void OImageButtonModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ OControlModel::read(_rxInStream);
+
+ // Version
+ sal_uInt16 nVersion = _rxInStream->readShort();
+
+ switch (nVersion)
+ {
+ case 0x0001:
+ {
+ m_eButtonType = static_cast<FormButtonType>(_rxInStream->readShort());
+ }
+ break;
+ case 0x0002:
+ {
+ m_eButtonType = static_cast<FormButtonType>(_rxInStream->readShort());
+ _rxInStream >> m_sTargetURL;
+ _rxInStream >> m_sTargetFrame;
+ }
+ break;
+ case 0x0003:
+ {
+ m_eButtonType = static_cast<FormButtonType>(_rxInStream->readShort());
+ _rxInStream >> m_sTargetURL;
+ _rxInStream >> m_sTargetFrame;
+ readHelpTextCompatibly(_rxInStream);
+ }
+ break;
+
+ default :
+ OSL_FAIL("OImageButtonModel::read : unknown version !");
+ m_eButtonType = FormButtonType_PUSH;
+ m_sTargetURL.clear();
+ m_sTargetFrame.clear();
+ break;
+ }
+}
+
+// OImageButtonControl
+Sequence<Type> OImageButtonControl::_getTypes()
+{
+ static Sequence<Type> const aTypes =
+ concatSequences(OClickableImageBaseControl::_getTypes(), OImageButtonControl_BASE::getTypes());
+ return aTypes;
+}
+
+css::uno::Sequence<OUString> OImageButtonControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OClickableImageBaseControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_IMAGEBUTTON;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_IMAGEBUTTON;
+ return aSupported;
+}
+
+OImageButtonControl::OImageButtonControl(const Reference<XComponentContext>& _rxFactory)
+ :OClickableImageBaseControl(_rxFactory, VCL_CONTROL_IMAGEBUTTON)
+{
+ osl_atomic_increment(&m_refCount);
+ {
+ // Register as MouseListener
+ Reference< awt::XWindow > xComp;
+ query_aggregation( m_xAggregate, xComp);
+ if (xComp.is())
+ xComp->addMouseListener( static_cast< awt::XMouseListener* >( this ) );
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+// UNO Binding
+Any SAL_CALL OImageButtonControl::queryAggregation(const Type& _rType)
+{
+ Any aReturn = OClickableImageBaseControl::queryAggregation(_rType);
+ if (!aReturn.hasValue())
+ aReturn = OImageButtonControl_BASE::queryInterface(_rType);
+
+ return aReturn;
+}
+
+void OImageButtonControl::mousePressed(const awt::MouseEvent& e)
+{
+ SolarMutexGuard aSolarGuard;
+
+ if (e.Buttons != awt::MouseButton::LEFT)
+ return;
+
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ if( m_aApproveActionListeners.getLength() )
+ {
+ // if there are listeners, start the action in an own thread, to not allow
+ // them to block us here (we're in the application's main thread)
+ getImageProducerThread()->OComponentEventThread::addEvent( std::make_unique<awt::MouseEvent>(e) );
+ }
+ else
+ {
+ // Or else don't; we must not notify the listeners in that case.
+ // Even not if it's added later on.
+ aGuard.clear();
+ actionPerformed_Impl( false, e );
+ }
+}
+
+void SAL_CALL OImageButtonControl::mouseReleased(const awt::MouseEvent& /*e*/)
+{
+}
+
+void SAL_CALL OImageButtonControl::mouseEntered(const awt::MouseEvent& /*e*/)
+{
+}
+
+void SAL_CALL OImageButtonControl::mouseExited(const awt::MouseEvent& /*e*/)
+{
+}
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OImageButtonModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OImageButtonModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OImageButtonControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OImageButtonControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/ImageButton.hxx b/forms/source/component/ImageButton.hxx
new file mode 100644
index 000000000..0e5c402ab
--- /dev/null
+++ b/forms/source/component/ImageButton.hxx
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "clickableimage.hxx"
+#include <com/sun/star/awt/XMouseListener.hpp>
+
+
+namespace frm
+{
+
+class OImageButtonModel
+ :public OClickableImageBaseModel
+{
+public:
+ OImageButtonModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OImageButtonModel(
+ const OImageButtonModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OImageButtonModel() override;
+
+// css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OImageButtonModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+// css::io::XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+typedef ::cppu::ImplHelper1< css::awt::XMouseListener> OImageButtonControl_BASE;
+class OImageButtonControl : public OClickableImageBaseControl,
+ public OImageButtonControl_BASE
+{
+protected:
+ // UNO Binding
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ explicit OImageButtonControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OImageButtonControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // UNO Binding
+ DECLARE_UNO3_AGG_DEFAULTS(OImageButtonControl, OClickableImageBaseControl)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource) override
+ { OControl::disposing(_rSource); }
+
+ // XMouseListener
+ virtual void SAL_CALL mousePressed(const css::awt::MouseEvent& e) override;
+ virtual void SAL_CALL mouseReleased(const css::awt::MouseEvent& e) override;
+ virtual void SAL_CALL mouseEntered(const css::awt::MouseEvent& e) override;
+ virtual void SAL_CALL mouseExited(const css::awt::MouseEvent& e) override;
+
+ // prevent method hiding
+ using OClickableImageBaseControl::disposing;
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/ImageControl.cxx b/forms/source/component/ImageControl.cxx
new file mode 100644
index 000000000..3e8dee00e
--- /dev/null
+++ b/forms/source/component/ImageControl.cxx
@@ -0,0 +1,992 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ImageControl.hxx"
+
+#include <strings.hrc>
+#include <frm_resource.hxx>
+#include <property.hxx>
+#include <services.hxx>
+#include <componenttools.hxx>
+
+#include <svtools/imageresourceaccess.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <com/sun/star/awt/PopupMenu.hpp>
+#include <com/sun/star/awt/XPopupMenu.hpp>
+#include <com/sun/star/awt/PopupMenuDirection.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/awt/MouseButton.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/graphic/GraphicObject.hpp>
+#include <tools/urlobj.hxx>
+#include <tools/stream.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/streamhelper.hxx>
+#include <comphelper/guarding.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <svl/urihelper.hxx>
+
+#include <memory>
+
+#define ID_OPEN_GRAPHICS 1
+#define ID_CLEAR_GRAPHICS 2
+
+namespace frm
+{
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::graphic;
+using namespace ::com::sun::star::frame;
+
+
+//= OImageControlModel
+
+namespace
+{
+ enum ImageStoreType
+ {
+ ImageStoreBinary,
+ ImageStoreLink,
+
+ ImageStoreInvalid
+ };
+
+ ImageStoreType lcl_getImageStoreType( const sal_Int32 _nFieldType )
+ {
+ // binary/longvarchar types could be used to store images in binary representation
+ if ( ( _nFieldType == DataType::BINARY )
+ || ( _nFieldType == DataType::VARBINARY )
+ || ( _nFieldType == DataType::LONGVARBINARY )
+ || ( _nFieldType == DataType::OTHER )
+ || ( _nFieldType == DataType::OBJECT )
+ || ( _nFieldType == DataType::BLOB )
+ || ( _nFieldType == DataType::LONGVARCHAR )
+ || ( _nFieldType == DataType::CLOB )
+ )
+ return ImageStoreBinary;
+
+ // char types could be used to store links to images
+ if ( ( _nFieldType == DataType::CHAR )
+ || ( _nFieldType == DataType::VARCHAR )
+ )
+ return ImageStoreLink;
+
+ return ImageStoreInvalid;
+ }
+}
+
+
+// OImageControlModel
+
+Sequence<Type> OImageControlModel::_getTypes()
+{
+ return concatSequences(
+ OBoundControlModel::_getTypes(),
+ OImageControlModel_Base::getTypes()
+ );
+}
+
+
+OImageControlModel::OImageControlModel(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControlModel( _rxFactory, VCL_CONTROLMODEL_IMAGECONTROL, FRM_SUN_CONTROL_IMAGECONTROL, false, false, false )
+ // use the old control name for compytibility reasons
+ ,m_bExternalGraphic( true )
+ ,m_bReadOnly( false )
+{
+ m_nClassId = FormComponentType::IMAGECONTROL;
+ initOwnValueProperty( PROPERTY_IMAGE_URL );
+
+ implConstruct();
+}
+
+
+OImageControlModel::OImageControlModel( const OImageControlModel* _pOriginal, const Reference< XComponentContext >& _rxFactory )
+ :OBoundControlModel( _pOriginal, _rxFactory )
+ // use the old control name for compytibility reasons
+ ,m_bExternalGraphic( true )
+ ,m_bReadOnly( _pOriginal->m_bReadOnly )
+ ,m_sImageURL( _pOriginal->m_sImageURL )
+ ,m_xGraphicObject( _pOriginal->m_xGraphicObject )
+{
+ implConstruct();
+
+ osl_atomic_increment( &m_refCount );
+ {
+ // simulate a propertyChanged event for the ImageURL
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_handleNewImageURL_lck( eOther );
+ }
+ osl_atomic_decrement( &m_refCount );
+}
+
+
+void OImageControlModel::implConstruct()
+{
+ m_xImageProducer = new ImageProducer;
+ m_xImageProducer->SetDoneHdl( LINK( this, OImageControlModel, OnImageImportDone ) );
+}
+
+
+OImageControlModel::~OImageControlModel()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+}
+
+// XCloneable
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OImageControlModel::createClone()
+{
+ rtl::Reference<OImageControlModel> pClone = new OImageControlModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> OImageControlModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControlModel::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_COMPONENT_IMAGECONTROL;
+ pArray[aSupported.getLength()-1] = FRM_COMPONENT_IMAGECONTROL;
+ return aSupported;
+}
+
+
+Any SAL_CALL OImageControlModel::queryAggregation(const Type& _rType)
+{
+ // Order matters: we want to "override" the XImageProducer interface of the aggregate without
+ // own XImageProducer interface, thus we need to query OImageControlModel_Base first
+ Any aReturn = OImageControlModel_Base::queryInterface( _rType );
+
+ // BUT: _don't_ let it feel responsible for the XTypeProvider interface
+ // (as this is implemented by our base class in the proper way)
+ if ( _rType.equals( cppu::UnoType<XTypeProvider>::get() )
+ || !aReturn.hasValue()
+ )
+ aReturn = OBoundControlModel::queryAggregation( _rType );
+
+ return aReturn;
+}
+
+
+bool OImageControlModel::approveDbColumnType( sal_Int32 _nColumnType )
+{
+ return ImageStoreInvalid != lcl_getImageStoreType( _nColumnType );
+}
+
+
+void OImageControlModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_READONLY:
+ rValue <<= m_bReadOnly;
+ break;
+ case PROPERTY_ID_IMAGE_URL:
+ rValue <<= m_sImageURL;
+ break;
+ case PROPERTY_ID_GRAPHIC:
+ rValue <<= m_xGraphicObject.is() ? m_xGraphicObject->getGraphic() : Reference< XGraphic >();
+ break;
+ default:
+ OBoundControlModel::getFastPropertyValue(rValue, nHandle);
+ }
+}
+
+
+void OImageControlModel::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue)
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_READONLY :
+ DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_BOOLEAN, "OImageControlModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ m_bReadOnly = getBOOL(rValue);
+ break;
+
+ case PROPERTY_ID_IMAGE_URL:
+ OSL_VERIFY( rValue >>= m_sImageURL );
+ impl_handleNewImageURL_lck( eOther );
+ {
+ ControlModelLock aLock( *this );
+ // that's a fake ... onValuePropertyChange expects to receive the only lock to our instance,
+ // but we're already called with our mutex locked ...
+ onValuePropertyChange( aLock );
+ }
+ break;
+
+ case PROPERTY_ID_GRAPHIC:
+ {
+ Reference< XGraphic > xGraphic;
+ OSL_VERIFY( rValue >>= xGraphic );
+ if ( !xGraphic.is() )
+ m_xGraphicObject.clear();
+ else
+ {
+ m_xGraphicObject = graphic::GraphicObject::create( m_xContext );
+ m_xGraphicObject->setGraphic( xGraphic );
+ }
+
+ if ( m_bExternalGraphic )
+ {
+ m_sImageURL = OUString();
+ // TODO: speaking strictly, this would need to be notified, since ImageURL is a bound property. However,
+ // this method here is called with a locked mutex, so we cannot simply call listeners ...
+ // I think the missing notification (and thus clients which potentially cannot observe the change)
+ // is less severe than the potential deadlock ...
+ }
+ }
+ break;
+
+ default:
+ OBoundControlModel::setFastPropertyValue_NoBroadcast(nHandle, rValue);
+ break;
+ }
+}
+
+
+sal_Bool OImageControlModel::convertFastPropertyValue(Any& rConvertedValue, Any& rOldValue, sal_Int32 nHandle, const Any& rValue)
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_READONLY :
+ return tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bReadOnly);
+
+ case PROPERTY_ID_IMAGE_URL:
+ return tryPropertyValue( rConvertedValue, rOldValue, rValue, m_sImageURL );
+
+ case PROPERTY_ID_GRAPHIC:
+ {
+ const Reference< XGraphic > xGraphic( getFastPropertyValue( PROPERTY_ID_GRAPHIC ), UNO_QUERY );
+ return tryPropertyValue( rConvertedValue, rOldValue, rValue, xGraphic );
+ }
+
+ default:
+ return OBoundControlModel::convertFastPropertyValue(rConvertedValue, rOldValue, nHandle, rValue);
+ }
+}
+
+
+void OImageControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OBoundControlModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 4);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_GRAPHIC, PROPERTY_ID_GRAPHIC, cppu::UnoType<XGraphic>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_IMAGE_URL, PROPERTY_ID_IMAGE_URL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_READONLY, PROPERTY_ID_READONLY, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+void OImageControlModel::describeAggregateProperties( Sequence< Property >& /* [out] */ o_rAggregateProperties ) const
+{
+ OBoundControlModel::describeAggregateProperties( o_rAggregateProperties );
+ // remove ImageURL and Graphic properties, we "override" them.
+ // This is because our aggregate synchronizes those
+ // two, but we have an own synchronization mechanism.
+ RemoveProperty( o_rAggregateProperties, PROPERTY_IMAGE_URL );
+ RemoveProperty( o_rAggregateProperties, PROPERTY_GRAPHIC );
+}
+
+
+OUString OImageControlModel::getServiceName()
+{
+ return FRM_COMPONENT_IMAGECONTROL; // old (non-sun) name for compatibility !
+}
+
+
+void OImageControlModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ // Base class
+ OBoundControlModel::write(_rxOutStream);
+ // Version
+ _rxOutStream->writeShort(0x0003);
+ // Name
+ _rxOutStream->writeBoolean(m_bReadOnly);
+ writeHelpTextCompatibly(_rxOutStream);
+ // from version 0x0003 : common properties
+ writeCommonProperties(_rxOutStream);
+}
+
+
+void OImageControlModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ OBoundControlModel::read(_rxInStream);
+
+ // Version
+ sal_uInt16 nVersion = _rxInStream->readShort();
+ switch (nVersion)
+ {
+ case 0x0001:
+ m_bReadOnly = _rxInStream->readBoolean();
+ break;
+ case 0x0002:
+ m_bReadOnly = _rxInStream->readBoolean();
+ readHelpTextCompatibly(_rxInStream);
+ break;
+ case 0x0003:
+ m_bReadOnly = _rxInStream->readBoolean();
+ readHelpTextCompatibly(_rxInStream);
+ readCommonProperties(_rxInStream);
+ break;
+ default :
+ OSL_FAIL("OImageControlModel::read : unknown version !");
+ m_bReadOnly = false;
+ defaultCommonProperties();
+ break;
+ }
+ // Display default values after read
+ if ( !getControlSource().isEmpty() )
+ { // (not if we don't have a control source - the "State" property acts like it is persistent, then
+ ::osl::MutexGuard aGuard(m_aMutex); // resetNoBroadcast expects this mutex guarding
+ resetNoBroadcast();
+ }
+}
+
+
+bool OImageControlModel::impl_updateStreamForURL_lck( const OUString& _rURL, ValueChangeInstigator _eInstigator )
+{
+ // create a stream for the image specified by the URL
+ std::unique_ptr< SvStream > pImageStream;
+ Reference< XInputStream > xImageStream;
+
+ if ( ::svt::GraphicAccess::isSupportedURL( _rURL ) )
+ {
+ xImageStream = ::svt::GraphicAccess::getImageXStream( getContext(), _rURL );
+ }
+ else
+ {
+ pImageStream = ::utl::UcbStreamHelper::CreateStream( _rURL, StreamMode::READ );
+ bool bSetNull = (pImageStream == nullptr) || (ERRCODE_NONE != pImageStream->GetErrorCode());
+
+ if ( !bSetNull )
+ {
+ // get the size of the stream
+ sal_uInt64 const nSize = pImageStream->remainingSize();
+ if (pImageStream->GetBufferSize() < 8192)
+ pImageStream->SetBufferSize(8192);
+ pImageStream->Seek(STREAM_SEEK_TO_BEGIN);
+
+ xImageStream = new ::utl::OInputStreamHelper( new SvLockBytes( pImageStream.get(), false ), nSize );
+ }
+ }
+
+ if ( xImageStream.is() )
+ {
+ if ( m_xColumnUpdate.is() )
+ m_xColumnUpdate->updateBinaryStream( xImageStream, xImageStream->available() );
+ else
+ setControlValue( Any( xImageStream ), _eInstigator );
+ xImageStream->closeInput();
+ return true;
+ }
+
+ return false;
+}
+
+
+void OImageControlModel::impl_handleNewImageURL_lck( ValueChangeInstigator _eInstigator )
+{
+ switch ( lcl_getImageStoreType( getFieldType() ) )
+ {
+ case ImageStoreBinary:
+ if ( impl_updateStreamForURL_lck( m_sImageURL, _eInstigator ) )
+ return;
+ break;
+
+ case ImageStoreLink:
+ {
+ OUString sCommitURL( m_sImageURL );
+ if ( !m_sDocumentURL.isEmpty() )
+ sCommitURL = URIHelper::simpleNormalizedMakeRelative( m_sDocumentURL, sCommitURL );
+ OSL_ENSURE( m_xColumnUpdate.is(), "OImageControlModel::impl_handleNewImageURL_lck: no bound field, but ImageStoreLink?!" );
+ if ( m_xColumnUpdate.is() )
+ {
+ m_xColumnUpdate->updateString( sCommitURL );
+ return;
+ }
+ }
+ break;
+
+ case ImageStoreInvalid:
+ OSL_FAIL( "OImageControlModel::impl_handleNewImageURL_lck: image storage type type!" );
+ break;
+ }
+
+ // if we're here, then the above code was unable to update our field/control from the given URL
+ // => fall back to NULL/VOID
+ if ( m_xColumnUpdate.is() )
+ m_xColumnUpdate->updateNull();
+ else
+ setControlValue( Any(), _eInstigator );
+}
+
+
+bool OImageControlModel::commitControlValueToDbColumn( bool _bPostReset )
+{
+ if ( _bPostReset )
+ {
+ // since this is a "commit after reset", we can simply update the column
+ // with null - this is our "default" which we were just reset to
+ if ( m_xColumnUpdate.is() )
+ m_xColumnUpdate->updateNull();
+ }
+ else
+ {
+ ::osl::MutexGuard aGuard(m_aMutex);
+ impl_handleNewImageURL_lck( eDbColumnBinding );
+ }
+
+ return true;
+}
+
+
+namespace
+{
+ bool lcl_isValidDocumentURL( std::u16string_view _rDocURL )
+ {
+ return ( !_rDocURL.empty() && _rDocURL != u"private:object" );
+ }
+}
+
+
+void OImageControlModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
+{
+ OBoundControlModel::onConnectedDbColumn( _rxForm );
+
+ try
+ {
+ Reference< XModel > xDocument( getXModel( *this ) );
+ if ( xDocument.is() )
+ {
+ m_sDocumentURL = xDocument->getURL();
+ if ( !lcl_isValidDocumentURL( m_sDocumentURL ) )
+ {
+ Reference< XChild > xAsChild( xDocument, UNO_QUERY );
+ while ( xAsChild.is() && !lcl_isValidDocumentURL( m_sDocumentURL ) )
+ {
+ xDocument.set( xAsChild->getParent(), UNO_QUERY );
+ if ( xDocument.is() )
+ m_sDocumentURL = xDocument->getURL();
+ xAsChild.set( xDocument, UNO_QUERY );
+ }
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+}
+
+
+void OImageControlModel::onDisconnectedDbColumn()
+{
+ OBoundControlModel::onDisconnectedDbColumn();
+
+ m_sDocumentURL.clear();
+}
+
+
+Any OImageControlModel::translateDbColumnToControlValue()
+{
+ switch ( lcl_getImageStoreType( getFieldType() ) )
+ {
+ case ImageStoreBinary:
+ {
+ Reference< XInputStream > xImageStream( m_xColumn->getBinaryStream() );
+ if ( m_xColumn->wasNull() )
+ xImageStream.clear();
+ return Any( xImageStream );
+ }
+ case ImageStoreLink:
+ {
+ OUString sImageLink( m_xColumn->getString() );
+ if ( !m_sDocumentURL.isEmpty() )
+ sImageLink = INetURLObject::GetAbsURL( m_sDocumentURL, sImageLink );
+ return Any( sImageLink );
+ }
+ case ImageStoreInvalid:
+ OSL_FAIL( "OImageControlModel::translateDbColumnToControlValue: invalid field type!" );
+ break;
+ }
+ return Any();
+}
+
+
+Any OImageControlModel::getControlValue( ) const
+{
+ return Any( m_sImageURL );
+}
+
+
+void OImageControlModel::doSetControlValue( const Any& _rValue )
+{
+ DBG_ASSERT( GetImageProducer() && m_xImageProducer.is(), "OImageControlModel::doSetControlValue: no image producer!" );
+ if ( !GetImageProducer() || !m_xImageProducer.is() )
+ return;
+
+ bool bStartProduction = false;
+ switch ( lcl_getImageStoreType( getFieldType() ) )
+ {
+ case ImageStoreBinary:
+ {
+ // give the image producer the stream
+ Reference< XInputStream > xInStream;
+ _rValue >>= xInStream;
+ GetImageProducer()->setImage( xInStream );
+ bStartProduction = true;
+ }
+ break;
+
+ case ImageStoreLink:
+ {
+ OUString sImageURL;
+ _rValue >>= sImageURL;
+ GetImageProducer()->SetImage( sImageURL );
+ bStartProduction = true;
+ }
+ break;
+
+ case ImageStoreInvalid:
+ OSL_FAIL( "OImageControlModel::doSetControlValue: invalid field type!" );
+ break;
+
+ } // switch ( lcl_getImageStoreType( getFieldType() ) )
+
+ if ( bStartProduction )
+ {
+ // start production
+ rtl::Reference< ImageProducer > xProducer = m_xImageProducer;
+ {
+ // release our mutex once (it's acquired in the calling method!), as starting the image production may
+ // result in the locking of the solar mutex (unfortunately the default implementation of our aggregate,
+ // VCLXImageControl, does this locking)
+ MutexRelease aRelease(m_aMutex);
+ xProducer->startProduction();
+ }
+ }
+}
+
+void OImageControlModel::resetNoBroadcast()
+{
+ if ( hasField() ) // only reset when we are connected to a column
+ OBoundControlModel::resetNoBroadcast( );
+}
+
+
+Reference< XImageProducer > SAL_CALL OImageControlModel::getImageProducer()
+{
+ return this;
+}
+
+
+void SAL_CALL OImageControlModel::addConsumer( const Reference< XImageConsumer >& _rxConsumer )
+{
+ GetImageProducer()->addConsumer( _rxConsumer );
+}
+
+
+void SAL_CALL OImageControlModel::removeConsumer( const Reference< XImageConsumer >& _rxConsumer )
+{
+ GetImageProducer()->removeConsumer( _rxConsumer );
+}
+
+
+void SAL_CALL OImageControlModel::startProduction( )
+{
+ GetImageProducer()->startProduction();
+}
+
+
+IMPL_LINK( OImageControlModel, OnImageImportDone, ::Graphic*, i_pGraphic, void )
+{
+ const Reference< XGraphic > xGraphic(i_pGraphic != nullptr ? i_pGraphic->GetXGraphic() : nullptr);
+ m_bExternalGraphic = false;
+ try
+ {
+ setPropertyValue( PROPERTY_GRAPHIC, Any( xGraphic ) );
+ }
+ catch ( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ m_bExternalGraphic = true;
+}
+
+
+// OImageControlControl
+
+Sequence<Type> OImageControlControl::_getTypes()
+{
+ return concatSequences(
+ OBoundControl::_getTypes(),
+ OImageControlControl_Base::getTypes()
+ );
+}
+
+
+OImageControlControl::OImageControlControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_IMAGECONTROL)
+ ,m_aModifyListeners( m_aMutex )
+{
+ osl_atomic_increment(&m_refCount);
+ {
+ // Add as Focus- and MouseListener
+ Reference< XWindow > xComp;
+ query_aggregation( m_xAggregate, xComp );
+ if ( xComp.is() )
+ xComp->addMouseListener( this );
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+
+Any SAL_CALL OImageControlControl::queryAggregation(const Type& _rType)
+{
+ Any aReturn = OBoundControl::queryAggregation( _rType );
+ if ( !aReturn.hasValue() )
+ aReturn = ::cppu::queryInterface(
+ _rType,
+ static_cast< XMouseListener* >( this ),
+ static_cast< XModifyBroadcaster* >( this )
+ );
+
+ return aReturn;
+}
+
+
+css::uno::Sequence<OUString> OImageControlControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_IMAGECONTROL;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_IMAGECONTROL;
+ return aSupported;
+}
+
+
+void SAL_CALL OImageControlControl::addModifyListener( const Reference< XModifyListener >& Listener )
+{
+ m_aModifyListeners.addInterface( Listener );
+}
+
+
+void SAL_CALL OImageControlControl::removeModifyListener( const Reference< XModifyListener >& Listener )
+{
+ m_aModifyListeners.removeInterface( Listener );
+}
+
+
+void SAL_CALL OImageControlControl::disposing()
+{
+ EventObject aEvent( *this );
+ m_aModifyListeners.disposeAndClear( aEvent );
+
+ OBoundControl::disposing();
+}
+
+
+void SAL_CALL OImageControlControl::disposing( const EventObject& Event )
+{
+ OBoundControl::disposing( Event );
+}
+
+
+void OImageControlControl::implClearGraphics( bool _bForce )
+{
+ Reference< XPropertySet > xSet( getModel(), UNO_QUERY );
+ if ( !xSet.is() )
+ return;
+
+ if ( _bForce )
+ {
+ OUString sOldImageURL;
+ xSet->getPropertyValue( PROPERTY_IMAGE_URL ) >>= sOldImageURL;
+
+ if ( sOldImageURL.isEmpty() )
+ // the ImageURL is already empty, so simply setting a new empty one would not suffice
+ // (since it would be ignored)
+ xSet->setPropertyValue( PROPERTY_IMAGE_URL, Any( OUString( "private:emptyImage" ) ) );
+ // (the concrete URL we're passing here doesn't matter. It's important that
+ // the model cannot resolve it to a valid resource describing an image stream
+ }
+
+ xSet->setPropertyValue( PROPERTY_IMAGE_URL, Any( OUString() ) );
+}
+
+
+bool OImageControlControl::implInsertGraphics()
+{
+ Reference< XPropertySet > xSet( getModel(), UNO_QUERY );
+ if ( !xSet.is() )
+ return false;
+
+ OUString sTitle = ResourceManager::loadString(RID_STR_IMPORT_GRAPHIC);
+ // build some arguments for the upcoming dialog
+ try
+ {
+ Reference< XWindow > xWindow( static_cast< ::cppu::OWeakObject* >( this ), UNO_QUERY );
+ ::sfx2::FileDialogHelper aDialog(TemplateDescription::FILEOPEN_LINK_PREVIEW, FileDialogFlags::Graphic,
+ Application::GetFrameWeld(xWindow));
+ aDialog.SetContext(sfx2::FileDialogHelper::FormsInsertImage);
+ aDialog.SetTitle( sTitle );
+
+ Reference< XFilePickerControlAccess > xController( aDialog.GetFilePicker(), UNO_QUERY_THROW );
+ xController->setValue(ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, 0, css::uno::Any(true));
+
+ Reference<XPropertySet> xBoundField;
+ if ( hasProperty( PROPERTY_BOUNDFIELD, xSet ) )
+ xSet->getPropertyValue( PROPERTY_BOUNDFIELD ) >>= xBoundField;
+ bool bHasField = xBoundField.is();
+
+ // if the control is bound to a DB field, then it's not possible to decide whether or not to link
+ xController->enableControl(ExtendedFilePickerElementIds::CHECKBOX_LINK, !bHasField );
+
+ // if the control is bound to a DB field, then linking of the image depends on the type of the field
+ bool bImageIsLinked = true;
+ if ( bHasField )
+ {
+ sal_Int32 nFieldType = DataType::OTHER;
+ OSL_VERIFY( xBoundField->getPropertyValue( PROPERTY_FIELDTYPE ) >>= nFieldType );
+ bImageIsLinked = ( lcl_getImageStoreType( nFieldType ) == ImageStoreLink );
+ }
+ xController->setValue(ExtendedFilePickerElementIds::CHECKBOX_LINK, 0, Any( bImageIsLinked ) );
+
+ if ( ERRCODE_NONE == aDialog.Execute() )
+ {
+ // reset the url property in case it already has the value we're about to set - in this case
+ // our propertyChanged would not get called without this.
+ implClearGraphics( false );
+ bool bIsLink = false;
+ xController->getValue(ExtendedFilePickerElementIds::CHECKBOX_LINK, 0) >>= bIsLink;
+ // Force bIsLink to be sal_True if we're bound to a field. Though we initialized the file picker with IsLink=TRUE
+ // in this case, and disabled the respective control, there might be picker implementations which do not
+ // respect this, and return IsLink=FALSE here. In this case, "normalize" the flag.
+ // #i112659#
+ bIsLink |= bHasField;
+ if ( !bIsLink )
+ {
+ Graphic aGraphic;
+ aDialog.GetGraphic( aGraphic );
+ xSet->setPropertyValue( PROPERTY_GRAPHIC, Any( aGraphic.GetXGraphic() ) );
+ }
+ else
+ xSet->setPropertyValue( PROPERTY_IMAGE_URL, Any( aDialog.GetPath() ) );
+
+ return true;
+ }
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "forms.component", "OImageControlControl::implInsertGraphics: caught an exception while attempting to execute the FilePicker!");
+ }
+ return false;
+}
+
+
+bool OImageControlControl::impl_isEmptyGraphics_nothrow() const
+{
+ bool bIsEmpty = true;
+
+ try
+ {
+ Reference< XPropertySet > xModelProps( const_cast< OImageControlControl* >( this )->getModel(), UNO_QUERY_THROW );
+ Reference< XGraphic > xGraphic;
+ OSL_VERIFY( xModelProps->getPropertyValue("Graphic") >>= xGraphic );
+ bIsEmpty = !xGraphic.is();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ return bIsEmpty;
+}
+
+// MouseListener
+
+void OImageControlControl::mousePressed(const css::awt::MouseEvent& e)
+{
+ SolarMutexGuard aGuard;
+
+ if (e.Buttons != MouseButton::LEFT)
+ return;
+
+ bool bModified = false;
+ // is this a request for a context menu?
+ if ( e.PopupTrigger )
+ {
+ Reference< XPopupMenu > xMenu( awt::PopupMenu::create( m_xContext ) );
+ DBG_ASSERT( xMenu.is(), "OImageControlControl::mousePressed: could not create a popup menu!" );
+
+ Reference< XWindowPeer > xWindowPeer = getPeer();
+ DBG_ASSERT( xWindowPeer.is(), "OImageControlControl::mousePressed: no window!" );
+
+ if ( xMenu.is() && xWindowPeer.is() )
+ {
+ xMenu->insertItem( ID_OPEN_GRAPHICS, ResourceManager::loadString(RID_STR_OPEN_GRAPHICS), 0, 0 );
+ xMenu->insertItem( ID_CLEAR_GRAPHICS, ResourceManager::loadString(RID_STR_CLEAR_GRAPHICS), 0, 1 );
+
+ // check if the ImageURL is empty
+ if ( impl_isEmptyGraphics_nothrow() )
+ xMenu->enableItem( ID_CLEAR_GRAPHICS, false );
+
+ awt::Rectangle aRect( e.X, e.Y, 0, 0 );
+ if ( ( e.X < 0 ) || ( e.Y < 0 ) )
+ { // context menu triggered by keyboard
+ // position it in the center of the control
+ Reference< XWindow > xWindow( static_cast< ::cppu::OWeakObject* >( this ), UNO_QUERY );
+ OSL_ENSURE( xWindow.is(), "OImageControlControl::mousePressed: me not a window? How this?" );
+ if ( xWindow.is() )
+ {
+ awt::Rectangle aPosSize = xWindow->getPosSize();
+ aRect.X = aPosSize.Width / 2;
+ aRect.Y = aPosSize.Height / 2;
+ }
+ }
+
+ const sal_Int16 nResult = xMenu->execute( xWindowPeer, aRect, PopupMenuDirection::EXECUTE_DEFAULT );
+
+ switch ( nResult )
+ {
+ case ID_OPEN_GRAPHICS:
+ implInsertGraphics();
+ bModified = true;
+ break;
+
+ case ID_CLEAR_GRAPHICS:
+ implClearGraphics( true );
+ bModified = true;
+ break;
+ }
+ }
+ }
+ else
+ {
+
+ // Double click
+ if (e.ClickCount == 2)
+ {
+
+ Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
+ if (!xSet.is())
+ return;
+
+ // If the Control is not bound, do not display a dialog (because the to-be-sent URL would be invalid anyway)
+ Reference<XPropertySet> xBoundField;
+ if (hasProperty(PROPERTY_BOUNDFIELD, xSet))
+ xBoundField.set(
+ xSet->getPropertyValue(PROPERTY_BOUNDFIELD),
+ css::uno::UNO_QUERY);
+ if (!xBoundField.is())
+ {
+ // but only if our IMAGE_URL property is handled as if it is transient, which is equivalent to
+ // an empty control source
+ if ( !hasProperty(PROPERTY_CONTROLSOURCE, xSet) || !::comphelper::getString(xSet->getPropertyValue(PROPERTY_CONTROLSOURCE)).isEmpty() )
+ return;
+ }
+
+ bool bReadOnly = false;
+ xSet->getPropertyValue(PROPERTY_READONLY) >>= bReadOnly;
+ if (bReadOnly)
+ return;
+
+ if ( implInsertGraphics() )
+ bModified = true;
+ }
+ }
+
+ if ( bModified )
+ {
+ EventObject aEvent( *this );
+ m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvent );
+ }
+}
+
+
+void SAL_CALL OImageControlControl::mouseReleased(const awt::MouseEvent& /*e*/)
+{
+}
+
+
+void SAL_CALL OImageControlControl::mouseEntered(const awt::MouseEvent& /*e*/)
+{
+}
+
+
+void SAL_CALL OImageControlControl::mouseExited(const awt::MouseEvent& /*e*/)
+{
+}
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OImageControlModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OImageControlModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OImageControlControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OImageControlControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/ImageControl.hxx b/forms/source/component/ImageControl.hxx
new file mode 100644
index 000000000..245d13163
--- /dev/null
+++ b/forms/source/component/ImageControl.hxx
@@ -0,0 +1,196 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+#include "imgprod.hxx"
+#include <com/sun/star/form/XImageProducerSupplier.hpp>
+#include <com/sun/star/awt/XMouseListener.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <com/sun/star/graphic/XGraphicObject.hpp>
+#include <comphelper/interfacecontainer3.hxx>
+#include <cppuhelper/implbase2.hxx>
+#include <rtl/ref.hxx>
+
+
+namespace frm
+{
+
+
+// OImageControlModel
+
+typedef ::cppu::ImplHelper2 < css::form::XImageProducerSupplier
+ , css::awt::XImageProducer
+ > OImageControlModel_Base;
+
+class OImageControlModel final
+ :public OImageControlModel_Base
+ ,public OBoundControlModel
+{
+ rtl::Reference<ImageProducer> m_xImageProducer;
+ bool m_bExternalGraphic;
+ bool m_bReadOnly;
+ OUString m_sImageURL;
+ css::uno::Reference< css::graphic::XGraphicObject >
+ m_xGraphicObject;
+ OUString m_sDocumentURL;
+
+ // UNO binding
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+ ImageProducer* GetImageProducer() { return m_xImageProducer.get(); }
+
+public:
+ OImageControlModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OImageControlModel(
+ const OImageControlModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OImageControlModel() override;
+
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle ) const override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const css::uno::Any& rValue) override;
+
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(css::uno::Any& rConvertedValue, css::uno::Any& rOldValue, sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+
+ // UNO binding
+ DECLARE_UNO3_AGG_DEFAULTS(OImageControlModel, OBoundControlModel)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OImageControlModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // XImageProducerSupplier
+ virtual css::uno::Reference< css::awt::XImageProducer> SAL_CALL getImageProducer() override;
+
+ // XImageProducer
+ virtual void SAL_CALL addConsumer( const css::uno::Reference< css::awt::XImageConsumer >& xConsumer ) override;
+ virtual void SAL_CALL removeConsumer( const css::uno::Reference< css::awt::XImageConsumer >& xConsumer ) override;
+ virtual void SAL_CALL startProduction( ) override;
+
+ // OControlModel's property handling
+ virtual void describeAggregateProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+ // prevent method hiding
+ using OBoundControlModel::disposing;
+ using OBoundControlModel::getFastPropertyValue;
+
+private:
+ // OBoundControlModel overridables
+ virtual void onConnectedDbColumn( const css::uno::Reference< css::uno::XInterface >& _rxForm ) override;
+ virtual void onDisconnectedDbColumn() override;
+ virtual css::uno::Any translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+
+ virtual css::uno::Any getControlValue( ) const override;
+ virtual void doSetControlValue( const css::uno::Any& _rValue ) override;
+
+ virtual bool approveDbColumnType(sal_Int32 _nColumnType) override;
+
+ virtual void resetNoBroadcast() override;
+
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ void implConstruct();
+
+ /** displays the image described by the given URL
+ @precond
+ our own mutex is locked
+ */
+ void impl_handleNewImageURL_lck( ValueChangeInstigator _eInstigator );
+
+ /** updates the binary stream, created from loading the file which the given URL points to, into our
+ bound field, or the control itself if there is no bound field
+ */
+ bool impl_updateStreamForURL_lck( const OUString& _rURL, ValueChangeInstigator _eInstigator );
+
+ DECL_LINK( OnImageImportDone, ::Graphic*, void );
+};
+
+typedef ::cppu::ImplHelper2 < css::awt::XMouseListener
+ , css::util::XModifyBroadcaster
+ > OImageControlControl_Base;
+class OImageControlControl : public OBoundControl
+ , public OImageControlControl_Base
+{
+private:
+ ::comphelper::OInterfaceContainerHelper3<css::util::XModifyListener> m_aModifyListeners;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ explicit OImageControlControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+
+ // UNO
+ DECLARE_UNO3_AGG_DEFAULTS( OImageControlControl, OBoundControl )
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OImageControlControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XMouseListener
+ virtual void SAL_CALL mousePressed(const css::awt::MouseEvent& e) override;
+ virtual void SAL_CALL mouseReleased(const css::awt::MouseEvent& e) override;
+ virtual void SAL_CALL mouseEntered(const css::awt::MouseEvent& e) override;
+ virtual void SAL_CALL mouseExited(const css::awt::MouseEvent& e) override;
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+ virtual void SAL_CALL removeModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+private:
+ void implClearGraphics( bool _bForce );
+ bool implInsertGraphics();
+
+ /** determines whether the control does currently have an empty graphic set
+ */
+ bool impl_isEmptyGraphics_nothrow() const;
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/ListBox.cxx b/forms/source/component/ListBox.cxx
new file mode 100644
index 000000000..a6d4bd808
--- /dev/null
+++ b/forms/source/component/ListBox.cxx
@@ -0,0 +1,2198 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+#include <config_fuzzers.h>
+
+#include "ListBox.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <frm_resource.hxx>
+#include <strings.hrc>
+#include "BaseListBox.hxx"
+#include <componenttools.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/types.hxx>
+#include <connectivity/dbtools.hxx>
+#include <connectivity/formattedcolumnvalue.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/safeint.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+#include <unotools/sharedunocomponent.hxx>
+
+#include <optional>
+
+#include <algorithm>
+#include <iterator>
+#include <climits>
+
+namespace frm
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::sdb;
+ using namespace ::com::sun::star::sdbc;
+ using namespace ::com::sun::star::sdbcx;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::io;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::form::binding;
+ using namespace ::dbtools;
+
+ using ::connectivity::ORowSetValue;
+
+ const ::connectivity::ORowSetValue OListBoxModel::s_aEmptyValue;
+ const ::connectivity::ORowSetValue OListBoxModel::s_aEmptyStringValue = OUString();
+
+ //= helper
+
+ namespace
+ {
+
+ struct RowSetValueToString
+ {
+ OUString operator()( const ORowSetValue& _value ) const
+ {
+ return _value.getString();
+ }
+ };
+
+
+ struct AppendRowSetValueString
+ {
+ explicit AppendRowSetValueString( OUString& _string )
+ :m_string( _string )
+ {
+ }
+
+ void operator()( const ORowSetValue& _append )
+ {
+ m_string += _append.getString();
+ }
+
+ private:
+ OUString& m_string;
+ };
+
+
+ Sequence< OUString > lcl_convertToStringSequence( const ValueList& _values )
+ {
+ Sequence< OUString > aStrings( _values.size() );
+ ::std::transform(
+ _values.begin(),
+ _values.end(),
+ aStrings.getArray(),
+ RowSetValueToString()
+ );
+ return aStrings;
+ }
+ }
+
+
+ //= ItemEventDescription
+
+ typedef ::comphelper::EventHolder< ItemEvent > ItemEventDescription;
+
+
+ //= OListBoxModel
+
+ Sequence< Type> OListBoxModel::_getTypes()
+ {
+ return TypeBag(
+ OBoundControlModel::_getTypes(),
+ OEntryListHelper::getTypes(),
+ OErrorBroadcaster::getTypes()
+ ).getTypes();
+ }
+
+ // stuff common to all constructors
+ void OListBoxModel::init()
+ {
+ startAggregatePropertyListening( PROPERTY_STRINGITEMLIST );
+ startAggregatePropertyListening( PROPERTY_TYPEDITEMLIST );
+ }
+
+
+ OListBoxModel::OListBoxModel(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControlModel( _rxFactory, VCL_CONTROLMODEL_LISTBOX, FRM_SUN_CONTROL_LISTBOX, true, true, true )
+ // use the old control name for compatibility reasons
+ ,OEntryListHelper( static_cast<OControlModel&>(*this) )
+ ,OErrorBroadcaster( OComponentHelper::rBHelper )
+ ,m_nConvertedBoundValuesType(0)
+ ,m_nNULLPos(-1)
+ ,m_nBoundColumnType( DataType::SQLNULL )
+ {
+
+ m_nClassId = FormComponentType::LISTBOX;
+ m_eListSourceType = ListSourceType_VALUELIST;
+ m_aBoundColumn <<= sal_Int16(1);
+ initValueProperty( PROPERTY_SELECT_SEQ, PROPERTY_ID_SELECT_SEQ);
+
+ init();
+ }
+
+
+ OListBoxModel::OListBoxModel( const OListBoxModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OBoundControlModel( _pOriginal, _rxFactory )
+ ,OEntryListHelper( *_pOriginal, static_cast<OControlModel&>(*this) )
+ ,OErrorBroadcaster( OComponentHelper::rBHelper )
+ ,m_eListSourceType( _pOriginal->m_eListSourceType )
+ ,m_aBoundColumn( _pOriginal->m_aBoundColumn )
+ ,m_aListSourceValues( _pOriginal->m_aListSourceValues )
+ ,m_aBoundValues( _pOriginal->m_aBoundValues )
+ ,m_nConvertedBoundValuesType(0)
+ ,m_aDefaultSelectSeq( _pOriginal->m_aDefaultSelectSeq )
+ ,m_nNULLPos(-1)
+ ,m_nBoundColumnType( DataType::SQLNULL )
+ {
+
+ init();
+ }
+
+
+ OListBoxModel::~OListBoxModel()
+ {
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+ }
+
+ // XCloneable
+
+ css::uno::Reference< css::util::XCloneable > SAL_CALL OListBoxModel::createClone()
+{
+ rtl::Reference<OListBoxModel> pClone = new OListBoxModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+ // XServiceInfo
+
+ css::uno::Sequence<OUString> SAL_CALL OListBoxModel::getSupportedServiceNames()
+ {
+ css::uno::Sequence<OUString> aSupported = OBoundControlModel::getSupportedServiceNames();
+
+ sal_Int32 nOldLen = aSupported.getLength();
+ aSupported.realloc( nOldLen + 9 );
+ OUString* pStoreTo = aSupported.getArray() + nOldLen;
+
+ *pStoreTo++ = BINDABLE_CONTROL_MODEL;
+ *pStoreTo++ = DATA_AWARE_CONTROL_MODEL;
+ *pStoreTo++ = VALIDATABLE_CONTROL_MODEL;
+
+ *pStoreTo++ = BINDABLE_DATA_AWARE_CONTROL_MODEL;
+ *pStoreTo++ = VALIDATABLE_BINDABLE_CONTROL_MODEL;
+
+ *pStoreTo++ = FRM_SUN_COMPONENT_LISTBOX;
+ *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_LISTBOX;
+ *pStoreTo++ = BINDABLE_DATABASE_LIST_BOX;
+
+ *pStoreTo++ = FRM_COMPONENT_LISTBOX;
+
+ return aSupported;
+ }
+
+
+ Any SAL_CALL OListBoxModel::queryAggregation(const Type& _rType)
+ {
+ Any aReturn = OBoundControlModel::queryAggregation( _rType );
+ if ( !aReturn.hasValue() )
+ aReturn = OEntryListHelper::queryInterface( _rType );
+ if ( !aReturn.hasValue() )
+ aReturn = OErrorBroadcaster::queryInterface( _rType );
+ return aReturn;
+ }
+
+ // OComponentHelper
+
+ void OListBoxModel::disposing()
+ {
+ OBoundControlModel::disposing();
+ OEntryListHelper::disposing();
+ OErrorBroadcaster::disposing();
+ }
+
+
+ void OListBoxModel::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const
+ {
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_BOUNDCOLUMN:
+ _rValue = m_aBoundColumn;
+ break;
+
+ case PROPERTY_ID_LISTSOURCETYPE:
+ _rValue <<= m_eListSourceType;
+ break;
+
+ case PROPERTY_ID_LISTSOURCE:
+ _rValue <<= lcl_convertToStringSequence( m_aListSourceValues );
+ break;
+
+ case PROPERTY_ID_VALUE_SEQ:
+ _rValue <<= lcl_convertToStringSequence( m_aBoundValues );
+ break;
+
+ case PROPERTY_ID_SELECT_VALUE_SEQ:
+ _rValue <<= getCurrentMultiValue();
+ break;
+
+ case PROPERTY_ID_SELECT_VALUE:
+ _rValue = getCurrentSingleValue();
+ break;
+
+ case PROPERTY_ID_DEFAULT_SELECT_SEQ:
+ _rValue <<= m_aDefaultSelectSeq;
+ break;
+
+ case PROPERTY_ID_STRINGITEMLIST:
+ _rValue <<= comphelper::containerToSequence(getStringItemList());
+ break;
+
+ case PROPERTY_ID_TYPEDITEMLIST:
+ _rValue <<= getTypedItemList();
+ break;
+
+ default:
+ OBoundControlModel::getFastPropertyValue(_rValue, _nHandle);
+ }
+ }
+
+
+ void OListBoxModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
+ {
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_BOUNDCOLUMN :
+ DBG_ASSERT((_rValue.getValueType().getTypeClass() == TypeClass_SHORT) || (_rValue.getValueType().getTypeClass() == TypeClass_VOID),
+ "OListBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ m_aBoundColumn = _rValue;
+ break;
+
+ case PROPERTY_ID_LISTSOURCETYPE :
+ DBG_ASSERT(_rValue.getValueType().equals(::cppu::UnoType<ListSourceType>::get()),
+ "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ _rValue >>= m_eListSourceType;
+ break;
+
+ case PROPERTY_ID_LISTSOURCE:
+ {
+ // extract
+ Sequence< OUString > aListSource;
+ OSL_VERIFY( _rValue >>= aListSource );
+
+ // copy to member
+ ValueList().swap(m_aListSourceValues);
+ ::std::copy(
+ std::cbegin(aListSource),
+ std::cend(aListSource),
+ ::std::insert_iterator< ValueList >( m_aListSourceValues, m_aListSourceValues.end() )
+ );
+
+ // propagate
+ if ( m_eListSourceType == ListSourceType_VALUELIST )
+ {
+ setBoundValues(std::vector(m_aListSourceValues));
+ }
+ else
+ {
+ if ( m_xCursor.is() && !hasField() && !hasExternalListSource() )
+ // listbox is already connected to a database, and no external list source
+ // data source changed -> refresh
+ loadData( false );
+ }
+ }
+ break;
+
+ case PROPERTY_ID_VALUE_SEQ :
+ SAL_WARN( "forms.component", "ValueItemList is read-only!" );
+ throw PropertyVetoException();
+
+ case PROPERTY_ID_SELECT_VALUE_SEQ :
+ {
+ Sequence< const Any > v;
+ _rValue >>= v;
+ Any newSelectSeq(translateBindingValuesToControlValue(v));
+ setControlValue( newSelectSeq, eOther );
+ }
+ break;
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ case PROPERTY_ID_SELECT_VALUE :
+ {
+ ORowSetValue v;
+ v.fill(_rValue);
+ Any newSelectSeq(translateDbValueToControlValue(v));
+ setControlValue( newSelectSeq, eOther );
+ }
+ break;
+#endif
+ case PROPERTY_ID_DEFAULT_SELECT_SEQ :
+ DBG_ASSERT(_rValue.getValueType().equals(cppu::UnoType<Sequence<sal_Int16>>::get()),
+ "OListBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ _rValue >>= m_aDefaultSelectSeq;
+
+ DBG_ASSERT(m_xAggregateFastSet.is(), "OListBoxModel::setFastPropertyValue_NoBroadcast(DEFAULT_SELECT_SEQ) : invalid aggregate !");
+ if ( m_xAggregateFastSet.is() )
+ setControlValue( _rValue, eOther );
+ break;
+
+ case PROPERTY_ID_STRINGITEMLIST:
+ {
+ ControlModelLock aLock( *this );
+ setNewStringItemList( _rValue, aLock );
+ // TODO: this is bogus. setNewStringItemList expects a guard which has the *only*
+ // lock to the mutex, but setFastPropertyValue_NoBroadcast is already called with
+ // a lock - so we effectively has two locks here, of which setNewStringItemList can
+ // only control one.
+ }
+ resetNoBroadcast();
+ break;
+
+ case PROPERTY_ID_TYPEDITEMLIST:
+ {
+ ControlModelLock aLock( *this );
+ setNewTypedItemList( _rValue, aLock );
+ // Same TODO as above.
+ }
+ resetNoBroadcast();
+ break;
+
+ default:
+ OBoundControlModel::setFastPropertyValue_NoBroadcast(_nHandle, _rValue);
+ }
+ }
+
+
+ sal_Bool OListBoxModel::convertFastPropertyValue(
+ Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue)
+ {
+ bool bModified(false);
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_BOUNDCOLUMN :
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aBoundColumn, ::cppu::UnoType<sal_Int16>::get());
+ break;
+
+ case PROPERTY_ID_LISTSOURCETYPE:
+ bModified = tryPropertyValueEnum(_rConvertedValue, _rOldValue, _rValue, m_eListSourceType);
+ break;
+
+ case PROPERTY_ID_LISTSOURCE:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, lcl_convertToStringSequence( m_aListSourceValues ) );
+ break;
+
+ case PROPERTY_ID_VALUE_SEQ :
+ SAL_WARN( "forms.component", "ValueItemList is read-only!" );
+ throw IllegalArgumentException();
+
+ case PROPERTY_ID_SELECT_VALUE_SEQ :
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, getCurrentMultiValue());
+ break;
+
+ case PROPERTY_ID_SELECT_VALUE :
+ {
+ // Any from connectivity::ORowSetValue
+ Any _rCurrentValue = getCurrentSingleValue();
+ if (_rCurrentValue != _rValue)
+ {
+ _rOldValue = _rCurrentValue;
+ _rConvertedValue = _rValue;
+ bModified = true;
+ }
+ break;
+ }
+ case PROPERTY_ID_DEFAULT_SELECT_SEQ :
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aDefaultSelectSeq);
+ break;
+
+ case PROPERTY_ID_STRINGITEMLIST:
+ bModified = convertNewListSourceProperty( _rConvertedValue, _rOldValue, _rValue );
+ break;
+
+ case PROPERTY_ID_TYPEDITEMLIST :
+ if (hasExternalListSource())
+ throw IllegalArgumentException();
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, getTypedItemList());
+ break;
+
+ default:
+ return OBoundControlModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue);
+ }
+ return bModified;
+ }
+
+
+ void SAL_CALL OListBoxModel::setPropertyValues( const Sequence< OUString >& _rPropertyNames, const Sequence< Any >& _rValues )
+ {
+ // if both SelectedItems and StringItemList are set, care for this
+ // #i27024#
+ const Any* pSelectSequenceValue = nullptr;
+
+ const OUString* pSelectedItemsPos = std::find(
+ _rPropertyNames.begin(), _rPropertyNames.end(), PROPERTY_SELECT_SEQ
+ );
+ auto aStringItemListExists = std::any_of(
+ _rPropertyNames.begin(), _rPropertyNames.end(),
+ [](OUString const & s) { return s == PROPERTY_STRINGITEMLIST; }
+ );
+ if ( ( pSelectedItemsPos != _rPropertyNames.end() ) && aStringItemListExists )
+ {
+ // both properties are present
+ // -> remember the value for the select sequence
+ pSelectSequenceValue = _rValues.getConstArray() + ( pSelectedItemsPos - _rPropertyNames.begin() );
+ }
+
+ OBoundControlModel::setPropertyValues( _rPropertyNames, _rValues );
+
+ if ( pSelectSequenceValue )
+ {
+ setPropertyValue( PROPERTY_SELECT_SEQ, *pSelectSequenceValue );
+ // Note that this is the only reliable way, since one of the properties is implemented
+ // by ourself, and one is implemented by the aggregate, we cannot rely on any particular
+ // results when setting them both - too many undocumented behavior in all the involved
+
+ }
+ }
+
+
+ void OListBoxModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+ {
+ OBoundControlModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 10);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_BOUNDCOLUMN, PROPERTY_ID_BOUNDCOLUMN, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_LISTSOURCETYPE, PROPERTY_ID_LISTSOURCETYPE, cppu::UnoType<ListSourceType>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_LISTSOURCE, PROPERTY_ID_LISTSOURCE, cppu::UnoType<css::uno::Sequence<OUString>>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_VALUE_SEQ, PROPERTY_ID_VALUE_SEQ, cppu::UnoType<css::uno::Sequence<OUString>>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_SELECT_VALUE_SEQ, PROPERTY_ID_SELECT_VALUE_SEQ, cppu::UnoType<Sequence< Any >>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_SELECT_VALUE, PROPERTY_ID_SELECT_VALUE, cppu::UnoType<Any>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_SELECT_SEQ, PROPERTY_ID_DEFAULT_SELECT_SEQ, cppu::UnoType<Sequence<sal_Int16>>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_STRINGITEMLIST, PROPERTY_ID_STRINGITEMLIST, cppu::UnoType<Sequence< OUString >>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TYPEDITEMLIST, PROPERTY_ID_TYPEDITEMLIST, cppu::UnoType<Sequence< Any >>::get(), css::beans::PropertyAttribute::OPTIONAL);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+ }
+
+
+ void OListBoxModel::_propertyChanged( const PropertyChangeEvent& i_rEvent )
+ {
+ if ( i_rEvent.PropertyName == PROPERTY_STRINGITEMLIST )
+ {
+ ControlModelLock aLock( *this );
+ // SYNCHRONIZED ----->
+ // our aggregate internally changed its StringItemList property - reflect this in our "overridden"
+ // version of the property
+ setNewStringItemList( i_rEvent.NewValue, aLock );
+ // <----- SYNCHRONIZED
+ return;
+ }
+ else if ( i_rEvent.PropertyName == PROPERTY_TYPEDITEMLIST )
+ {
+ ControlModelLock aLock( *this );
+ // SYNCHRONIZED ----->
+ // our aggregate internally changed its TypedItemList property - reflect this in our "overridden"
+ // version of the property
+ setNewTypedItemList( i_rEvent.NewValue, aLock );
+ // <----- SYNCHRONIZED
+ return;
+ }
+ OBoundControlModel::_propertyChanged( i_rEvent );
+ }
+
+
+ void OListBoxModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const
+ {
+ OBoundControlModel::describeAggregateProperties( _rAggregateProps );
+
+ // superseded properties:
+ RemoveProperty( _rAggregateProps, PROPERTY_STRINGITEMLIST );
+ RemoveProperty( _rAggregateProps, PROPERTY_TYPEDITEMLIST );
+ }
+
+
+ OUString SAL_CALL OListBoxModel::getServiceName()
+ {
+ return FRM_COMPONENT_LISTBOX; // old (non-sun) name for compatibility !
+ }
+
+
+ void SAL_CALL OListBoxModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+ {
+ OBoundControlModel::write(_rxOutStream);
+
+ // Dummy sequence, to stay compatible if SelectSeq is not saved anymore
+ Sequence<sal_Int16> aDummySeq;
+
+ // Version
+ // Version 0x0002: ListSource becomes StringSeq
+ _rxOutStream->writeShort(0x0004);
+
+ // Masking for any
+ sal_uInt16 nAnyMask = 0;
+ if (m_aBoundColumn.getValueType().getTypeClass() != TypeClass_VOID)
+ nAnyMask |= BOUNDCOLUMN;
+
+ _rxOutStream << nAnyMask;
+
+ _rxOutStream << lcl_convertToStringSequence( m_aListSourceValues );
+ _rxOutStream << static_cast<sal_Int16>(m_eListSourceType);
+ _rxOutStream << aDummySeq;
+ _rxOutStream << m_aDefaultSelectSeq;
+
+ if ((nAnyMask & BOUNDCOLUMN) == BOUNDCOLUMN)
+ {
+ sal_Int16 nBoundColumn = 0;
+ m_aBoundColumn >>= nBoundColumn;
+ _rxOutStream << nBoundColumn;
+ }
+ writeHelpTextCompatibly(_rxOutStream);
+
+ // from version 0x0004 : common properties
+ writeCommonProperties(_rxOutStream);
+ }
+
+
+ void SAL_CALL OListBoxModel::read(const Reference<XObjectInputStream>& _rxInStream)
+ {
+ // We need to respect dependencies for certain variables.
+ // Therefore, we need to set them explicitly via setPropertyValue().
+
+ OBoundControlModel::read(_rxInStream);
+ ControlModelLock aLock( *this );
+
+ // since we are "overwriting" the StringItemList of our aggregate (means we have
+ // an own place to store the value, instead of relying on our aggregate storing it),
+ // we need to respect what the aggregate just read for the StringItemList property.
+ try
+ {
+ if ( m_xAggregateSet.is() )
+ setNewStringItemList( m_xAggregateSet->getPropertyValue( PROPERTY_STRINGITEMLIST ), aLock );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.component", "OComboBoxModel::read: caught an exception while examining the aggregate's string item list" );
+ }
+
+ // Version
+ sal_uInt16 nVersion = _rxInStream->readShort();
+ DBG_ASSERT(nVersion > 0, "OListBoxModel::read : version 0 ? this should never have been written !");
+
+ if (nVersion > 0x0004)
+ {
+ SAL_WARN( "forms.component", "OListBoxModel::read : invalid (means unknown) version !");
+ ValueList().swap(m_aListSourceValues);
+ m_aBoundColumn <<= sal_Int16(0);
+ clearBoundValues();
+ m_eListSourceType = ListSourceType_VALUELIST;
+ m_aDefaultSelectSeq.realloc(0);
+ defaultCommonProperties();
+ return;
+ }
+
+ // Masking for any
+ sal_uInt16 nAnyMask;
+ _rxInStream >> nAnyMask;
+
+ // ListSourceSeq
+ css::uno::Sequence<OUString> aListSourceSeq;
+ if (nVersion == 0x0001)
+ {
+ // Create ListSourceSeq from String
+ OUString sListSource;
+ _rxInStream >> sListSource;
+
+ const sal_Int32 nTokens{ comphelper::string::getTokenCount(sListSource, ';') };
+ aListSourceSeq.realloc( nTokens );
+ sal_Int32 nIdx{ 0 };
+ for (sal_Int32 i=0; i<nTokens; ++i)
+ {
+ aListSourceSeq.getArray()[i] = sListSource.getToken(0, ';', nIdx);
+ }
+ }
+ else
+ _rxInStream >> aListSourceSeq;
+
+ sal_Int16 nListSourceType;
+ _rxInStream >> nListSourceType;
+ m_eListSourceType = static_cast<ListSourceType>(nListSourceType);
+ Any aListSourceSeqAny;
+ aListSourceSeqAny <<= aListSourceSeq;
+
+ setFastPropertyValue(PROPERTY_ID_LISTSOURCE, aListSourceSeqAny );
+
+ // Dummy sequence, to stay compatible if SelectSeq is not saved anymore
+ Sequence<sal_Int16> aDummySeq;
+ _rxInStream >> aDummySeq;
+
+ // DefaultSelectSeq
+ Sequence<sal_Int16> aDefaultSelectSeq;
+ _rxInStream >> aDefaultSelectSeq;
+ Any aDefaultSelectSeqAny;
+ aDefaultSelectSeqAny <<= aDefaultSelectSeq;
+ setFastPropertyValue(PROPERTY_ID_DEFAULT_SELECT_SEQ, aDefaultSelectSeqAny);
+
+ // BoundColumn
+ if ((nAnyMask & BOUNDCOLUMN) == BOUNDCOLUMN)
+ {
+ sal_Int16 nValue;
+ _rxInStream >> nValue;
+ m_aBoundColumn <<= nValue;
+ }
+ else // the constructor initialises to 1, so if it is empty,
+ // we must explicitly set to empty
+ {
+ m_aBoundColumn = Any();
+ }
+
+ if (nVersion > 2)
+ readHelpTextCompatibly(_rxInStream);
+
+ // if our string list is not filled from the value list, we must empty it
+ // this can be the case when somebody saves in alive mode
+ if ( ( m_eListSourceType != ListSourceType_VALUELIST )
+ && !hasExternalListSource()
+ )
+ {
+ setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( css::uno::Sequence<OUString>() ) );
+ setFastPropertyValue( PROPERTY_ID_TYPEDITEMLIST, Any( css::uno::Sequence<css::uno::Any>() ) );
+ }
+
+ if (nVersion > 3)
+ readCommonProperties(_rxInStream);
+
+ // Display the default values after reading
+ if ( !getControlSource().isEmpty() )
+ // (not if we don't have a control source - the "State" property acts like it is persistent, then
+ resetNoBroadcast();
+ }
+
+
+ void OListBoxModel::loadData( bool _bForce )
+ {
+ SAL_INFO( "forms.component", "OListBoxModel::loadData" );
+ DBG_ASSERT( m_eListSourceType != ListSourceType_VALUELIST, "OListBoxModel::loadData: cannot load value list from DB!" );
+ DBG_ASSERT( !hasExternalListSource(), "OListBoxModel::loadData: cannot load from DB when I have an external list source!" );
+
+ const sal_Int16 nNULLPosBackup( m_nNULLPos );
+ const sal_Int32 nBoundColumnTypeBackup( m_nBoundColumnType );
+ m_nNULLPos = -1;
+ m_nBoundColumnType = DataType::SQLNULL;
+
+ // pre-requisites:
+ // PRE1: connection
+ Reference< XConnection > xConnection;
+ // is the active connection of our form
+ Reference< XPropertySet > xFormProps( m_xCursor, UNO_QUERY );
+ if ( xFormProps.is() )
+ xFormProps->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xConnection;
+
+ // PRE2: list source
+ OUString sListSource;
+ // if our list source type is no value list, we need to concatenate
+ // the single list source elements
+ ::std::for_each(
+ m_aListSourceValues.begin(),
+ m_aListSourceValues.end(),
+ AppendRowSetValueString( sListSource )
+ );
+
+ // outta here if we don't have all pre-requisites
+ if ( !xConnection.is() || sListSource.isEmpty() )
+ {
+ clearBoundValues();
+ return;
+ }
+
+ ::std::optional< sal_Int16 > aBoundColumn(std::nullopt);
+ if ( m_aBoundColumn.getValueType().getTypeClass() == TypeClass_SHORT )
+ {
+ sal_Int16 nBoundColumn( 0 );
+ m_aBoundColumn >>= nBoundColumn;
+ aBoundColumn = nBoundColumn;
+ }
+
+ ::utl::SharedUNOComponent< XResultSet > xListCursor;
+ try
+ {
+ m_aListRowSet.setConnection( xConnection );
+
+ bool bExecute = false;
+ switch (m_eListSourceType)
+ {
+ case ListSourceType_TABLEFIELDS:
+ // don't work with a statement here, the fields will be collected below
+ break;
+
+ case ListSourceType_TABLE:
+ {
+ Reference<XNameAccess> xFieldsByName = getTableFields(xConnection, sListSource);
+ Reference<XIndexAccess> xFieldsByIndex(xFieldsByName, UNO_QUERY);
+
+ // do we have a bound column if yes we have to select it
+ // and the displayed column is the first column otherwise we act as a combobox
+ OUString aFieldName;
+ OUString aBoundFieldName;
+
+ if ( !!aBoundColumn && ( *aBoundColumn >= 0 ) && xFieldsByIndex.is() )
+ {
+ if ( *aBoundColumn >= xFieldsByIndex->getCount() )
+ break;
+
+ Reference<XPropertySet> xFieldAsSet(xFieldsByIndex->getByIndex( *aBoundColumn ),UNO_QUERY);
+ assert(xFieldAsSet.is());
+ xFieldAsSet->getPropertyValue(PROPERTY_NAME) >>= aBoundFieldName;
+ aBoundColumn = 1;
+
+ xFieldAsSet.set(xFieldsByIndex->getByIndex(0),UNO_QUERY);
+ xFieldAsSet->getPropertyValue(PROPERTY_NAME) >>= aFieldName;
+ }
+ else if (xFieldsByName.is())
+ {
+ if ( xFieldsByName->hasByName( getControlSource() ) )
+ aFieldName = getControlSource();
+ else
+ {
+ // otherwise look for the alias
+ Reference< XColumnsSupplier > xSupplyFields;
+ xFormProps->getPropertyValue("SingleSelectQueryComposer") >>= xSupplyFields;
+
+ // search the field
+ DBG_ASSERT(xSupplyFields.is(), "OListBoxModel::loadData : invalid query composer !");
+
+ Reference<XNameAccess> xFieldNames = xSupplyFields->getColumns();
+ if ( xFieldNames->hasByName( getControlSource() ) )
+ {
+ Reference<XPropertySet> xComposerFieldAsSet;
+ xFieldNames->getByName( getControlSource() ) >>= xComposerFieldAsSet;
+ if (hasProperty(PROPERTY_FIELDSOURCE, xComposerFieldAsSet))
+ xComposerFieldAsSet->getPropertyValue(PROPERTY_FIELDSOURCE) >>= aFieldName;
+ }
+ }
+ }
+ if (aFieldName.isEmpty())
+ break;
+
+ Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
+ OUString aQuote = xMeta->getIdentifierQuoteString();
+ OUString aStatement("SELECT ");
+ if (aBoundFieldName.isEmpty()) // act like a combobox
+ aStatement += "DISTINCT ";
+
+ aStatement += quoteName(aQuote,aFieldName);
+ if (!aBoundFieldName.isEmpty())
+ {
+ aStatement += ", " + quoteName(aQuote, aBoundFieldName);
+ }
+ aStatement += " FROM ";
+
+ OUString sCatalog, sSchema, sTable;
+ qualifiedNameComponents( xMeta, sListSource, sCatalog, sSchema, sTable, EComposeRule::InDataManipulation );
+ aStatement += composeTableNameForSelect( xConnection, sCatalog, sSchema, sTable );
+
+ m_aListRowSet.setEscapeProcessing( false );
+ m_aListRowSet.setCommand( aStatement );
+ bExecute = true;
+ }
+ break;
+
+ case ListSourceType_QUERY:
+ m_aListRowSet.setCommandFromQuery( sListSource );
+ bExecute = true;
+ break;
+
+ default:
+ m_aListRowSet.setEscapeProcessing( ListSourceType_SQLPASSTHROUGH != m_eListSourceType );
+ m_aListRowSet.setCommand( sListSource );
+ bExecute = true;
+ break;
+ }
+
+ if (bExecute)
+ {
+ if ( !_bForce && !m_aListRowSet.isDirty() )
+ {
+ // if none of the settings of the row set changed, compared to the last
+ // invocation of loadData, then don't re-fill the list. Instead, assume
+ // the list entries are the same.
+ m_nNULLPos = nNULLPosBackup;
+ m_nBoundColumnType = nBoundColumnTypeBackup;
+ return;
+ }
+ xListCursor.reset( m_aListRowSet.execute() );
+ }
+ }
+ catch(const SQLException& eSQL)
+ {
+ onError(eSQL, ResourceManager::loadString(RID_BASELISTBOX_ERROR_FILLLIST));
+ return;
+ }
+ catch(const Exception&)
+ {
+ return;
+ }
+
+ // Fill display and value lists
+ ValueList aDisplayList, aValueList;
+ bool bUseNULL = hasField() && !isRequired();
+
+ // empty BoundColumn is treated as BoundColumn==0,
+ if(!aBoundColumn)
+ aBoundColumn = 0;
+
+ try
+ {
+ OSL_ENSURE( xListCursor.is() || ( ListSourceType_TABLEFIELDS == m_eListSourceType ),
+ "OListBoxModel::loadData: logic error!" );
+ if ( !xListCursor.is() && ( ListSourceType_TABLEFIELDS != m_eListSourceType ) )
+ return;
+
+ switch (m_eListSourceType)
+ {
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ case ListSourceType_SQL:
+ case ListSourceType_SQLPASSTHROUGH:
+ case ListSourceType_TABLE:
+ case ListSourceType_QUERY:
+ {
+ // Get field of the ResultSet's 1st column
+ Reference<XColumnsSupplier> xSupplyCols(xListCursor, UNO_QUERY);
+ DBG_ASSERT(xSupplyCols.is(), "OListBoxModel::loadData : cursor supports the row set service but is no column supplier?!");
+ Reference<XIndexAccess> xColumns;
+ if (xSupplyCols.is())
+ {
+ xColumns.set(xSupplyCols->getColumns(), UNO_QUERY);
+ DBG_ASSERT(xColumns.is(), "OListBoxModel::loadData : no columns supplied by the row set !");
+ }
+
+ Reference< XPropertySet > xDataField;
+ if ( xColumns.is() )
+ xColumns->getByIndex(0) >>= xDataField;
+ if ( !xDataField.is() )
+ return;
+
+ ::dbtools::FormattedColumnValue aValueFormatter( getContext(), m_xCursor, xDataField );
+
+ // Get the field of BoundColumn of the ResultSet
+ m_nBoundColumnType = DataType::SQLNULL;
+ if ( *aBoundColumn >= 0 )
+ {
+ try
+ {
+ Reference< XPropertySet > xBoundField( xColumns->getByIndex( *aBoundColumn ), UNO_QUERY_THROW );
+ OSL_VERIFY( xBoundField->getPropertyValue("Type") >>= m_nBoundColumnType );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+ else if ( *aBoundColumn == -1)
+ m_nBoundColumnType = DataType::SMALLINT;
+
+ // If the LB is bound to a field and empty entries are valid, we remember the position
+ // for an empty entry
+ SAL_INFO( "forms.component", "OListBoxModel::loadData: string collection" );
+ OUString aStr;
+ sal_Int16 entryPos = 0;
+ ORowSetValue aBoundValue;
+ Reference< XRow > xCursorRow( xListCursor, UNO_QUERY_THROW );
+ while ( xListCursor->next() && ( entryPos++ < SHRT_MAX ) ) // SHRT_MAX is the maximum number of entries
+ {
+ aStr = aValueFormatter.getFormattedValue();
+ aDisplayList.emplace_back(aStr );
+
+ if(*aBoundColumn >= 0)
+ aBoundValue.fill( *aBoundColumn + 1, m_nBoundColumnType, xCursorRow );
+ else
+ // -1 because getRow() is 1-indexed, but ListBox positions are 0-indexed
+ aBoundValue = static_cast<sal_Int16>(xListCursor->getRow()-1);
+ aValueList.push_back( aBoundValue );
+
+ if ( m_nNULLPos == -1 && aBoundValue.isNull() )
+ m_nNULLPos = sal_Int16( aDisplayList.size() - 1 );
+ if ( bUseNULL && ( m_nNULLPos == -1 ) && aStr.isEmpty() )
+ // There is already a non-NULL entry with empty display string;
+ // adding another one for NULL would make things confusing,
+ // so back off.
+ bUseNULL = false;
+ }
+ }
+ break;
+#endif
+ case ListSourceType_TABLEFIELDS:
+ {
+ Reference<XNameAccess> xFieldNames = getTableFields(xConnection, sListSource);
+ if (xFieldNames.is())
+ {
+ const css::uno::Sequence<OUString> seqNames = xFieldNames->getElementNames();
+ ::std::copy(
+ seqNames.begin(),
+ seqNames.end(),
+ ::std::insert_iterator< ValueList >( aDisplayList, aDisplayList.end() )
+ );
+ if(*aBoundColumn == -1)
+ {
+ // the type of i matters! It will be the type of the ORowSetValue pushed to aValueList!
+ for(size_t i=0; i < aDisplayList.size(); ++i)
+ {
+ aValueList.emplace_back(sal_Int16(i));
+ }
+ }
+ else
+ {
+ aValueList = aDisplayList;
+ }
+ }
+ }
+ break;
+ default:
+ SAL_WARN( "forms.component", "OListBoxModel::loadData: unreachable!" );
+ break;
+ }
+ }
+ catch(const SQLException& eSQL)
+ {
+ onError(eSQL, ResourceManager::loadString(RID_BASELISTBOX_ERROR_FILLLIST));
+ return;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ return;
+ }
+
+
+ // Create Values sequence
+ // Add NULL entry
+ if (bUseNULL && m_nNULLPos == -1)
+ {
+ aValueList.insert( aValueList.begin(), ORowSetValue() );
+
+ aDisplayList.insert( aDisplayList.begin(), ORowSetValue( OUString() ) );
+ m_nNULLPos = 0;
+ }
+
+ setBoundValues(std::move(aValueList));
+
+ setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( lcl_convertToStringSequence( aDisplayList ) ) );
+ setFastPropertyValue( PROPERTY_ID_TYPEDITEMLIST, Any( css::uno::Sequence<css::uno::Any>() ) );
+ }
+
+
+ void OListBoxModel::onConnectedDbColumn( const Reference< XInterface >& /*_rxForm*/ )
+ {
+ // list boxes which are bound to a db column don't have multi selection
+ // - this would be unable to reflect in the db column
+ if ( hasField() )
+ {
+ setFastPropertyValue( PROPERTY_ID_MULTISELECTION, css::uno::Any(false) );
+ }
+
+ if ( !hasExternalListSource() )
+ impl_refreshDbEntryList( false );
+ }
+
+
+ void OListBoxModel::onDisconnectedDbColumn()
+ {
+ clearBoundValues();
+ m_nNULLPos = -1;
+ m_nBoundColumnType = DataType::SQLNULL;
+
+ if ( m_eListSourceType != ListSourceType_VALUELIST )
+ {
+ if ( !hasExternalListSource() )
+ setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( css::uno::Sequence<OUString>() ) );
+
+ m_aListRowSet.dispose();
+ }
+ }
+
+
+ void OListBoxModel::setBoundValues(ValueList && l)
+ {
+ m_aConvertedBoundValues.clear();
+ m_aBoundValues = std::move(l);
+ }
+
+
+ void OListBoxModel::clearBoundValues()
+ {
+ ValueList().swap(m_aConvertedBoundValues);
+ ValueList().swap(m_aBoundValues);
+ }
+
+
+ void OListBoxModel::convertBoundValues(const sal_Int32 nFieldType) const
+ {
+ assert(s_aEmptyValue.isNull());
+ m_nNULLPos = -1;
+ m_aConvertedBoundValues.resize(m_aBoundValues.size());
+ ValueList::iterator dst = m_aConvertedBoundValues.begin();
+ sal_Int16 nPos = 0;
+ for (auto const& src : m_aBoundValues)
+ {
+ if(m_nNULLPos == -1 &&
+ !isRequired() &&
+ (src == s_aEmptyStringValue || src == s_aEmptyValue || src.isNull()) )
+ {
+ m_nNULLPos = nPos;
+ dst->setNull();
+ }
+ else
+ {
+ *dst = src;
+ }
+ dst->setTypeKind(nFieldType);
+ ++dst;
+ ++nPos;
+ }
+ m_nConvertedBoundValuesType = nFieldType;
+ OSL_ENSURE(dst == m_aConvertedBoundValues.end(), "OListBoxModel::convertBoundValues expected to have overwritten all of m_aConvertedBoundValues, but did not.");
+ assert(dst == m_aConvertedBoundValues.end());
+ }
+
+ sal_Int32 OListBoxModel::getValueType() const
+ {
+ return (m_nBoundColumnType != css::sdbc::DataType::SQLNULL) ?
+ m_nBoundColumnType :
+ ( hasField() ? getFieldType() : DataType::VARCHAR);
+ }
+
+ ValueList OListBoxModel::impl_getValues() const
+ {
+ const sal_Int32 nFieldType = getValueType();
+
+ if ( !m_aConvertedBoundValues.empty() && m_nConvertedBoundValuesType == nFieldType )
+ return m_aConvertedBoundValues;
+
+ if ( !m_aBoundValues.empty() )
+ {
+ convertBoundValues(nFieldType);
+ return m_aConvertedBoundValues;
+ }
+
+ const std::vector< OUString >& aStringItems( getStringItemList() );
+ ValueList aValues( aStringItems.size() );
+ ValueList::iterator dst = aValues.begin();
+ for (auto const& src : aStringItems)
+ {
+ *dst = src;
+ dst->setTypeKind(nFieldType);
+ ++dst;
+ }
+ m_nConvertedBoundValuesType = nFieldType;
+ OSL_ENSURE(dst == aValues.end(), "OListBoxModel::impl_getValues expected to have set all of aValues, but did not.");
+ assert(dst == aValues.end());
+ return aValues;
+ }
+
+ ORowSetValue OListBoxModel::getFirstSelectedValue() const
+ {
+ DBG_ASSERT( m_xAggregateFastSet.is(), "OListBoxModel::getFirstSelectedValue: invalid aggregate!" );
+ if ( !m_xAggregateFastSet.is() )
+ return s_aEmptyValue;
+
+ Sequence< sal_Int16 > aSelectedIndices;
+ OSL_VERIFY( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) >>= aSelectedIndices );
+ if ( !aSelectedIndices.hasElements() )
+ // nothing selected at all
+ return s_aEmptyValue;
+
+ if ( ( m_nNULLPos != -1 ) && ( aSelectedIndices[0] == m_nNULLPos ) )
+ // the dedicated "NULL" entry is selected
+ return s_aEmptyValue;
+
+ ValueList aValues( impl_getValues() );
+
+ size_t selectedValue = aSelectedIndices[0];
+ if ( selectedValue >= aValues.size() )
+ {
+ SAL_WARN( "forms.component", "OListBoxModel::getFirstSelectedValue: inconsistent selection/valuelist!" );
+ return s_aEmptyValue;
+ }
+
+ return aValues[ selectedValue ];
+ }
+
+
+ bool OListBoxModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+ {
+ // current selection list
+ const ORowSetValue aCurrentValue( getFirstSelectedValue() );
+ if ( aCurrentValue != m_aSaveValue )
+ {
+ if ( aCurrentValue.isNull() )
+ m_xColumnUpdate->updateNull();
+ else
+ {
+ try
+ {
+ m_xColumnUpdate->updateObject( aCurrentValue.makeAny() );
+ }
+ catch ( const Exception& )
+ {
+ return false;
+ }
+ }
+ m_aSaveValue = aCurrentValue;
+ }
+ return true;
+ }
+
+
+ Sequence< sal_Int16 > OListBoxModel::translateDbValueToControlValue(const ORowSetValue &i_aValue) const
+ {
+ Sequence< sal_Int16 > aSelectionIndicies;
+
+ // reset selection for NULL values
+ if ( i_aValue.isNull() )
+ {
+ if ( m_nNULLPos != -1 )
+ {
+ aSelectionIndicies = { m_nNULLPos };
+ }
+ }
+ else
+ {
+ ValueList aValues( impl_getValues() );
+ assert( m_nConvertedBoundValuesType == getValueType());
+ ORowSetValue v(i_aValue);
+ v.setTypeKind( m_nConvertedBoundValuesType );
+ ValueList::const_iterator curValuePos = ::std::find( aValues.begin(), aValues.end(), v );
+ if ( curValuePos != aValues.end() )
+ {
+ aSelectionIndicies = { o3tl::narrowing<sal_Int16>(curValuePos - aValues.begin()) };
+ }
+ }
+
+ return aSelectionIndicies;
+ }
+
+ Sequence< sal_Int16 > OListBoxModel::translateBindingValuesToControlValue(const Sequence< const Any > &i_aValues) const
+ {
+ const ValueList aValues( impl_getValues() );
+ assert( m_nConvertedBoundValuesType == getValueType());
+ Sequence< sal_Int16 > aSelectionIndicies(i_aValues.getLength());
+
+ sal_Int32 nCount(0);
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ sal_Int16 *pIndex = aSelectionIndicies.getArray();
+ for ( auto const & value : i_aValues)
+ {
+ if ( value.hasValue() )
+ {
+ ORowSetValue v;
+ v.fill(value);
+ v.setTypeKind( m_nConvertedBoundValuesType );
+ ValueList::const_iterator curValuePos = ::std::find( aValues.begin(), aValues.end(), v );
+ if ( curValuePos != aValues.end() )
+ {
+ *pIndex = curValuePos - aValues.begin();
+ ++pIndex;
+ ++nCount;
+ }
+ }
+ else
+ {
+ if ( m_nNULLPos != -1 )
+ {
+ *pIndex = m_nNULLPos;
+ ++pIndex;
+ ++nCount;
+ }
+ }
+ }
+ assert(aSelectionIndicies.getArray() + nCount == pIndex);
+#endif
+ aSelectionIndicies.realloc(nCount);
+ return aSelectionIndicies;
+ }
+
+ Any OListBoxModel::translateDbColumnToControlValue()
+ {
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ Reference< XPropertySet > xBoundField( getField() );
+ if ( !xBoundField.is() )
+ {
+ SAL_WARN( "forms.component", "OListBoxModel::translateDbColumnToControlValue: no field? How could that happen?!" );
+ return Any();
+ }
+
+ ORowSetValue aCurrentValue;
+ aCurrentValue.fill( getValueType(), m_xColumn );
+
+ m_aSaveValue = aCurrentValue;
+
+ return Any( translateDbValueToControlValue(aCurrentValue) );
+#else
+ return Any();
+#endif
+ }
+
+ // XReset
+
+ Any OListBoxModel::getDefaultForReset() const
+ {
+ Any aValue;
+ if (m_aDefaultSelectSeq.hasElements())
+ aValue <<= m_aDefaultSelectSeq;
+ else if (m_nNULLPos != -1) // bound Listbox
+ {
+ Sequence<sal_Int16> aSeq { m_nNULLPos };
+ aValue <<= aSeq;
+ }
+ else
+ {
+ Sequence<sal_Int16> aSeq;
+ aValue <<= aSeq;
+ }
+
+ return aValue;
+ }
+
+
+ void OListBoxModel::resetNoBroadcast()
+ {
+ OBoundControlModel::resetNoBroadcast();
+ m_aSaveValue.setNull();
+ }
+
+
+ void SAL_CALL OListBoxModel::disposing( const EventObject& _rSource )
+ {
+ if ( !OEntryListHelper::handleDisposing( _rSource ) )
+ OBoundControlModel::disposing( _rSource );
+ }
+
+
+ namespace
+ {
+ // The type of how we should transfer our selection to external value bindings
+ enum ExchangeType
+ {
+ eIndexList, /// as list of indexes of selected entries
+ eIndex, /// as index of the selected entry
+ eEntryList, /// as list of string representations of selected *display* entries
+ eEntry, /// as string representation of the selected *display* entry
+ eValueList, /// as list of string representations of selected values
+ eValue /// as string representation of the selected value
+ };
+
+
+ ExchangeType lcl_getCurrentExchangeType( const Type& _rExchangeType )
+ {
+ switch ( _rExchangeType.getTypeClass() )
+ {
+ case TypeClass_ANY:
+ return eValue;
+ case TypeClass_STRING:
+ return eEntry;
+ case TypeClass_LONG:
+ return eIndex;
+ case TypeClass_SEQUENCE:
+ {
+ Type aElementType = ::comphelper::getSequenceElementType( _rExchangeType );
+ switch ( aElementType.getTypeClass() )
+ {
+ case TypeClass_ANY:
+ return eValueList;
+ case TypeClass_STRING:
+ return eEntryList;
+ case TypeClass_LONG:
+ return eIndexList;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ SAL_WARN( "forms.component", "lcl_getCurrentExchangeType: unsupported (unexpected) exchange type!" );
+ return eEntry;
+ }
+ }
+
+
+ Any OListBoxModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+ {
+ Sequence< sal_Int16 > aSelectIndexes;
+
+ switch ( lcl_getCurrentExchangeType( getExternalValueType() ) )
+ {
+ case eValueList:
+ {
+ Sequence< const Any > aExternalValues;
+ OSL_VERIFY( _rExternalValue >>= aExternalValues );
+ aSelectIndexes = translateBindingValuesToControlValue( aExternalValues );
+ }
+ break;
+
+ case eValue:
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ {
+ ORowSetValue v;
+ v.fill(_rExternalValue);
+ aSelectIndexes = translateDbValueToControlValue(v);
+ }
+#endif
+ break;
+
+ case eIndexList:
+ {
+ // unfortunately, our select sequence is a sequence<short>, while the external binding
+ // supplies sequence<int> only -> transform this
+ Sequence< sal_Int32 > aSelectIndexesPure;
+ OSL_VERIFY( _rExternalValue >>= aSelectIndexesPure );
+ aSelectIndexes.realloc( aSelectIndexesPure.getLength() );
+ ::std::copy(
+ std::cbegin(aSelectIndexesPure),
+ std::cend(aSelectIndexesPure),
+ aSelectIndexes.getArray()
+ );
+ }
+ break;
+
+ case eIndex:
+ {
+ sal_Int32 nSelectIndex = -1;
+ OSL_VERIFY( _rExternalValue >>= nSelectIndex );
+ if ( ( nSelectIndex >= 0 ) && ( o3tl::make_unsigned(nSelectIndex) < getStringItemList().size() ) )
+ {
+ aSelectIndexes = { o3tl::narrowing<sal_Int16>(nSelectIndex) };
+ }
+ }
+ break;
+
+ case eEntryList:
+ {
+ // we can retrieve a string list from the binding for multiple selection
+ Sequence< OUString > aSelectEntries;
+ OSL_VERIFY( _rExternalValue >>= aSelectEntries );
+
+ ::std::set< sal_Int16 > aSelectionSet;
+
+ // find the selection entries in our item list
+ for ( OUString const & selectEntry : std::as_const(aSelectEntries) )
+ {
+ int idx = 0;
+ for(const OUString& s : getStringItemList())
+ {
+ if (s==selectEntry)
+ aSelectionSet.insert(idx);
+ ++idx;
+ }
+ }
+
+ // copy the indexes to the sequence
+ aSelectIndexes = comphelper::containerToSequence( aSelectionSet );
+ }
+ break;
+
+ case eEntry:
+ {
+ OUString sStringToSelect;
+ OSL_VERIFY( _rExternalValue >>= sStringToSelect );
+ ::std::set< sal_Int16 > aSelectionSet;
+ int idx = 0;
+ for(const OUString& s : getStringItemList())
+ {
+ if (s==sStringToSelect)
+ aSelectionSet.insert(idx);
+ ++idx;
+ }
+
+ aSelectIndexes = comphelper::containerToSequence( aSelectionSet );
+ }
+ break;
+ }
+
+ return Any( aSelectIndexes );
+ }
+
+
+ namespace
+ {
+
+ struct ExtractStringFromSequence_Safe
+ {
+ protected:
+ const std::vector< OUString >& m_rList;
+
+ public:
+ explicit ExtractStringFromSequence_Safe( const std::vector< OUString >& _rList ) : m_rList( _rList ) { }
+
+ OUString operator ()( sal_Int16 _nIndex )
+ {
+ OSL_ENSURE( _nIndex < static_cast<sal_Int32>(m_rList.size()), "ExtractStringFromSequence_Safe: inconsistence!" );
+ if ( _nIndex < static_cast<sal_Int32>(m_rList.size()) )
+ return m_rList[ _nIndex ];
+ return OUString();
+ }
+ };
+
+
+ Any lcl_getSingleSelectedEntryTyped( const Sequence< sal_Int16 >& _rSelectSequence, const Sequence<Any>& _rTypedList )
+ {
+ Any aReturn;
+
+ // by definition, multiple selected entries are transferred as NULL if the
+ // binding does not support lists
+ if ( _rSelectSequence.getLength() <= 1 )
+ {
+ if ( _rSelectSequence.getLength() == 1 )
+ {
+ sal_Int32 nIndex = _rSelectSequence[0];
+ if (0 <= nIndex && nIndex < _rTypedList.getLength())
+ aReturn = _rTypedList[nIndex];
+ }
+ }
+
+ return aReturn;
+ }
+
+
+ Any lcl_getSingleSelectedEntry( const Sequence< sal_Int16 >& _rSelectSequence, const std::vector< OUString >& _rStringList )
+ {
+ Any aReturn;
+
+ // by definition, multiple selected entries are transferred as NULL if the
+ // binding does not support string lists
+ if ( _rSelectSequence.getLength() <= 1 )
+ {
+ OUString sSelectedEntry;
+
+ if ( _rSelectSequence.getLength() == 1 )
+ sSelectedEntry = ExtractStringFromSequence_Safe( _rStringList )( _rSelectSequence[0] );
+
+ aReturn <<= sSelectedEntry;
+ }
+
+ return aReturn;
+ }
+
+
+ Any lcl_getMultiSelectedEntries( const Sequence< sal_Int16 >& _rSelectSequence, const std::vector< OUString >& _rStringList )
+ {
+ Sequence< OUString > aSelectedEntriesTexts( _rSelectSequence.getLength() );
+ ::std::transform(
+ _rSelectSequence.begin(),
+ _rSelectSequence.end(),
+ aSelectedEntriesTexts.getArray(),
+ ExtractStringFromSequence_Safe( _rStringList )
+ );
+ return Any( aSelectedEntriesTexts );
+ }
+
+
+ struct ExtractAnyFromValueList_Safe
+ {
+ protected:
+ const ValueList& m_rList;
+
+ public:
+ explicit ExtractAnyFromValueList_Safe( const ValueList& _rList ) : m_rList( _rList ) { }
+
+ Any operator ()( sal_Int16 _nIndex )
+ {
+ OSL_ENSURE( o3tl::make_unsigned(_nIndex) < m_rList.size(), "ExtractAnyFromValueList: inconsistence!" );
+ if ( o3tl::make_unsigned(_nIndex) < m_rList.size() )
+ return m_rList[ _nIndex ].makeAny();
+ return Any();
+ }
+ };
+
+
+ Any lcl_getSingleSelectedEntryAny( const Sequence< sal_Int16 >& _rSelectSequence, const ValueList& _rStringList )
+ {
+ Any aReturn;
+
+ // by definition, multiple selected entries are transferred as NULL if the
+ // binding does not support string lists
+ if ( _rSelectSequence.getLength() <= 1 )
+ {
+ if ( _rSelectSequence.getLength() == 1 )
+ aReturn = ExtractAnyFromValueList_Safe( _rStringList )( _rSelectSequence[0] );
+ }
+
+ return aReturn;
+ }
+
+
+ Sequence< Any > lcl_getMultiSelectedEntriesAny( const Sequence< sal_Int16 >& _rSelectSequence, const ValueList& _rStringList )
+ {
+ Sequence< Any > aSelectedEntriesValues( _rSelectSequence.getLength() );
+ ::std::transform(
+ _rSelectSequence.begin(),
+ _rSelectSequence.end(),
+ aSelectedEntriesValues.getArray(),
+ ExtractAnyFromValueList_Safe( _rStringList )
+ );
+ return aSelectedEntriesValues;
+ }
+ }
+
+
+ Any OListBoxModel::translateControlValueToExternalValue( ) const
+ {
+ OSL_PRECOND( hasExternalValueBinding(), "OListBoxModel::translateControlValueToExternalValue: no binding!" );
+
+ Sequence< sal_Int16 > aSelectSequence;
+ OSL_VERIFY( getControlValue() >>= aSelectSequence );
+
+ Any aReturn;
+ switch ( lcl_getCurrentExchangeType( getExternalValueType() ) )
+ {
+ case eValueList:
+ aReturn <<= getCurrentMultiValue();
+ break;
+
+ case eValue:
+ aReturn = getCurrentSingleValue();
+ break;
+
+ case eIndexList:
+ {
+ // unfortunately, the select sequence is a sequence<short>, but our binding
+ // expects int's
+ Sequence< sal_Int32 > aTransformed( aSelectSequence.getLength() );
+ ::std::copy(
+ std::cbegin(aSelectSequence),
+ std::cend(aSelectSequence),
+ aTransformed.getArray()
+ );
+ aReturn <<= aTransformed;
+ }
+ break;
+
+ case eIndex:
+ if ( aSelectSequence.getLength() <= 1 )
+ {
+ sal_Int32 nIndex = -1;
+
+ if ( aSelectSequence.getLength() == 1 )
+ nIndex = aSelectSequence[0];
+
+ aReturn <<= nIndex;
+ }
+ break;
+
+ case eEntryList:
+ aReturn = lcl_getMultiSelectedEntries( aSelectSequence, getStringItemList() );
+ break;
+
+ case eEntry:
+ {
+ const std::vector<OUString>& rStrings = getStringItemList();
+ const Sequence<Any>& rValues = getTypedItemList();
+ if (rStrings.size() == static_cast<size_t>(rValues.getLength()))
+ aReturn = lcl_getSingleSelectedEntryTyped( aSelectSequence, rValues );
+ else
+ aReturn = lcl_getSingleSelectedEntry( aSelectSequence, rStrings );
+ }
+ break;
+ }
+
+ return aReturn;
+ }
+
+
+ Any OListBoxModel::translateControlValueToValidatableValue( ) const
+ {
+ OSL_PRECOND( hasValidator(), "OListBoxModel::translateControlValueToValidatableValue: no validator, so why should I?" );
+ return getCurrentFormComponentValue();
+ }
+
+
+ Any OListBoxModel::getCurrentSingleValue() const
+ {
+ Any aCurrentValue;
+
+ try
+ {
+ Sequence< sal_Int16 > aSelectSequence;
+ OSL_VERIFY( getControlValue() >>= aSelectSequence );
+ aCurrentValue = lcl_getSingleSelectedEntryAny( aSelectSequence, impl_getValues() );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ return aCurrentValue;
+ }
+
+ Sequence< Any > OListBoxModel::getCurrentMultiValue() const
+ {
+ Sequence< Any > aCurrentValue;
+
+ try
+ {
+ Sequence< sal_Int16 > aSelectSequence;
+ OSL_VERIFY( getControlValue() >>= aSelectSequence );
+ aCurrentValue = lcl_getMultiSelectedEntriesAny( aSelectSequence, impl_getValues() );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ return aCurrentValue;
+ }
+
+ Any OListBoxModel::getCurrentFormComponentValue() const
+ {
+ {
+ Reference< css::form::validation::XValidator > vtor (const_cast<OListBoxModel*>(this)->getValidator());
+ Reference< XValueBinding > extBinding (const_cast<OListBoxModel*>(this)->getValueBinding());
+ if ( vtor.is() && vtor == extBinding )
+ return translateControlValueToExternalValue();
+ }
+
+ Any aCurrentValue;
+
+ try
+ {
+ bool bMultiSelection( false );
+ OSL_VERIFY( const_cast< OListBoxModel* >( this )->getPropertyValue( PROPERTY_MULTISELECTION ) >>= bMultiSelection );
+
+ if ( bMultiSelection )
+ aCurrentValue <<= getCurrentMultiValue();
+ else
+ aCurrentValue = getCurrentSingleValue();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ return aCurrentValue;
+ }
+
+
+ Sequence< Type > OListBoxModel::getSupportedBindingTypes()
+ {
+ return
+ {
+ cppu::UnoType<Sequence< Any >>::get(),
+ cppu::UnoType<Any>::get(),
+ cppu::UnoType<Sequence< sal_Int32 >>::get(),
+ cppu::UnoType<sal_Int32>::get(),
+ cppu::UnoType<Sequence< OUString >>::get(),
+ cppu::UnoType<OUString>::get()
+ };
+ }
+
+
+ void OListBoxModel::stringItemListChanged( ControlModelLock& _rInstanceLock )
+ {
+ if ( !m_xAggregateSet.is() )
+ return;
+
+ suspendValueListening();
+ try
+ {
+ m_xAggregateSet->setPropertyValue( PROPERTY_STRINGITEMLIST, Any( comphelper::containerToSequence(getStringItemList()) ) );
+ m_xAggregateSet->setPropertyValue( PROPERTY_TYPEDITEMLIST, Any( getTypedItemList() ) );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ resumeValueListening();
+
+ // update the selection here
+ if ( hasExternalValueBinding( ) )
+ transferExternalValueToControl( _rInstanceLock );
+ else
+ {
+ if ( hasField() )
+ {
+ // TODO: update the selection in case we're bound to a database column
+ }
+ else
+ {
+ if ( m_aDefaultSelectSeq.hasElements() )
+ setControlValue( Any( m_aDefaultSelectSeq ), eOther );
+ }
+ }
+ }
+
+
+ void OListBoxModel::impl_refreshDbEntryList( bool _bForce )
+ {
+ DBG_ASSERT( !hasExternalListSource(), "OListBoxModel::impl_refreshDbEntryList: invalid call!" );
+
+ if ( !hasExternalListSource( )
+ && ( m_eListSourceType != ListSourceType_VALUELIST )
+ && ( m_xCursor.is() )
+ )
+ {
+ loadData( _bForce );
+ }
+ }
+
+
+ void OListBoxModel::refreshInternalEntryList()
+ {
+ impl_refreshDbEntryList( true );
+ if ( hasField() && m_xCursor.is() )
+ initFromField( m_xCursor );
+ }
+
+
+ // OListBoxControl
+
+ Sequence< Type> OListBoxControl::_getTypes()
+ {
+ return TypeBag(
+ OBoundControl::_getTypes(),
+ OListBoxControl_BASE::getTypes()
+ ).getTypes();
+ }
+
+
+ Any SAL_CALL OListBoxControl::queryAggregation(const Type& _rType)
+ {
+ Any aReturn = OListBoxControl_BASE::queryInterface( _rType );
+
+ if ( !aReturn.hasValue()
+ || _rType.equals( cppu::UnoType<XTypeProvider>::get() )
+ )
+ aReturn = OBoundControl::queryAggregation( _rType );
+
+ return aReturn;
+ }
+
+
+ OListBoxControl::OListBoxControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl( _rxFactory, VCL_CONTROL_LISTBOX, false )
+ ,m_aChangeListeners( m_aMutex )
+ ,m_aItemListeners( m_aMutex )
+ ,m_aChangeIdle("forms OListBoxControl m_aChangedIdle")
+ {
+
+ osl_atomic_increment(&m_refCount);
+ {
+ // Register as FocusListener
+ Reference<XWindow> xComp;
+ if (query_aggregation(m_xAggregate, xComp))
+ xComp->addFocusListener(this);
+
+ // Register as ItemListener
+ if ( query_aggregation( m_xAggregate, m_xAggregateListBox ) )
+ m_xAggregateListBox->addItemListener(this);
+ }
+ // Refcount at 2 for registered Listener
+ osl_atomic_decrement(&m_refCount);
+
+ doSetDelegator();
+
+ m_aChangeIdle.SetPriority(TaskPriority::LOWEST);
+ m_aChangeIdle.SetInvokeHandler(LINK(this,OListBoxControl,OnTimeout));
+ }
+
+
+ OListBoxControl::~OListBoxControl()
+ {
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+ doResetDelegator();
+ m_xAggregateListBox.clear();
+
+ }
+
+
+ css::uno::Sequence<OUString> SAL_CALL OListBoxControl::getSupportedServiceNames()
+ {
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString* pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_LISTBOX;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_LISTBOX;
+ return aSupported;
+ }
+
+
+ // XFocusListener
+
+ void SAL_CALL OListBoxControl::focusGained(const FocusEvent& /*_rEvent*/)
+ {
+ ::osl::MutexGuard aGuard(m_aMutex);
+ if ( m_aChangeListeners.getLength() ) // only if there are listeners
+ {
+ Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
+ if (xSet.is())
+ {
+ // memorize the current selection for posting the change event
+ m_aCurrentSelection = xSet->getPropertyValue(PROPERTY_SELECT_SEQ);
+ }
+ }
+ }
+
+
+ void SAL_CALL OListBoxControl::focusLost(const FocusEvent& /*_rEvent*/)
+ {
+ m_aCurrentSelection.clear();
+ }
+
+ // XItemListener
+
+ void SAL_CALL OListBoxControl::itemStateChanged(const ItemEvent& _rEvent)
+ {
+ // forward this to our listeners
+ Reference< XChild > xChild( getModel(), UNO_QUERY );
+ if ( xChild.is() && xChild->getParent().is() )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( m_aItemListeners.getLength() )
+ {
+ if ( !m_pItemBroadcaster.is() )
+ {
+ m_pItemBroadcaster.set(
+ new ::comphelper::AsyncEventNotifier("ListBox"));
+ m_pItemBroadcaster->launch();
+ }
+ m_pItemBroadcaster->addEvent( new ItemEventDescription( _rEvent ), this );
+ }
+ }
+ else
+ m_aItemListeners.notifyEach( &XItemListener::itemStateChanged, _rEvent );
+
+ // and do the handling for the ChangeListeners
+ osl::MutexGuard aGuard(m_aMutex);
+ if ( m_aChangeIdle.IsActive() )
+ {
+ Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
+ m_aCurrentSelection = xSet->getPropertyValue(PROPERTY_SELECT_SEQ);
+
+ m_aChangeIdle.Stop();
+ m_aChangeIdle.Start();
+ }
+ else
+ {
+ if ( m_aChangeListeners.getLength() && m_aCurrentSelection.hasValue() )
+ {
+ Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
+ if (xSet.is())
+ {
+ // Has the selection been changed?
+ bool bModified(false);
+ Any aValue = xSet->getPropertyValue(PROPERTY_SELECT_SEQ);
+
+ Sequence<sal_Int16> const & rSelection = *o3tl::doAccess<Sequence<sal_Int16>>(aValue);
+ Sequence<sal_Int16> const & rOldSelection = *o3tl::doAccess<Sequence<sal_Int16>>(m_aCurrentSelection);
+ sal_Int32 nLen = rSelection.getLength();
+ if (nLen != rOldSelection.getLength())
+ bModified = true;
+ else
+ {
+ const sal_Int16* pVal = rSelection.getConstArray();
+ const sal_Int16* pCompVal = rOldSelection.getConstArray();
+
+ while (nLen-- && !bModified)
+ bModified = pVal[nLen] != pCompVal[nLen];
+ }
+
+ if (bModified)
+ {
+ m_aCurrentSelection = aValue;
+ m_aChangeIdle.Start();
+ }
+ }
+ }
+ else if (m_aCurrentSelection.hasValue())
+ m_aCurrentSelection.clear();
+ }
+ }
+
+ // XEventListener
+
+ void SAL_CALL OListBoxControl::disposing(const EventObject& _rSource)
+ {
+ OBoundControl::disposing(_rSource);
+ }
+
+ // XChangeBroadcaster
+
+ void SAL_CALL OListBoxControl::addChangeListener(const Reference<XChangeListener>& _rxListener)
+ {
+ m_aChangeListeners.addInterface( _rxListener );
+ }
+
+
+ void SAL_CALL OListBoxControl::removeChangeListener(const Reference<XChangeListener>& _rxListener)
+ {
+ m_aChangeListeners.removeInterface( _rxListener );
+ }
+
+ // OComponentHelper
+
+ void OListBoxControl::disposing()
+ {
+ if (m_aChangeIdle.IsActive())
+ m_aChangeIdle.Stop();
+
+ EventObject aEvent( *this );
+ m_aChangeListeners.disposeAndClear( aEvent );
+ m_aItemListeners.disposeAndClear( aEvent );
+
+ rtl::Reference< comphelper::AsyncEventNotifier > t;
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( m_pItemBroadcaster.is() )
+ {
+ t = m_pItemBroadcaster;
+ m_pItemBroadcaster->removeEventsForProcessor( this );
+ m_pItemBroadcaster->terminate();
+ m_pItemBroadcaster = nullptr;
+ }
+ }
+ if (t.is()) {
+ t->join();
+ }
+
+ OBoundControl::disposing();
+ }
+
+
+ void OListBoxControl::processEvent( const AnyEvent& _rEvent )
+ {
+ Reference< XListBox > xKeepAlive( this );
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( OComponentHelper::rBHelper.bDisposed )
+ return;
+ }
+ const ItemEventDescription& rItemEvent = static_cast< const ItemEventDescription& >( _rEvent );
+ m_aItemListeners.notifyEach( &XItemListener::itemStateChanged, rItemEvent.getEventObject() );
+ }
+
+
+ IMPL_LINK_NOARG(OListBoxControl, OnTimeout, Timer*, void)
+ {
+ m_aChangeListeners.notifyEach( &XChangeListener::changed, EventObject( *this ) );
+ }
+
+
+ void SAL_CALL OListBoxControl::addItemListener( const Reference< XItemListener >& l )
+ {
+ m_aItemListeners.addInterface( l );
+ }
+
+
+ void SAL_CALL OListBoxControl::removeItemListener( const Reference< XItemListener >& l )
+ {
+ m_aItemListeners.removeInterface( l );
+ }
+
+
+ void SAL_CALL OListBoxControl::addActionListener( const Reference< XActionListener >& l )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->addActionListener( l );
+ }
+
+
+ void SAL_CALL OListBoxControl::removeActionListener( const Reference< XActionListener >& l )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->removeActionListener( l );
+ }
+
+
+ void SAL_CALL OListBoxControl::addItem( const OUString& aItem, ::sal_Int16 nPos )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->addItem( aItem, nPos );
+ }
+
+
+ void SAL_CALL OListBoxControl::addItems( const Sequence< OUString >& aItems, ::sal_Int16 nPos )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->addItems( aItems, nPos );
+ }
+
+
+ void SAL_CALL OListBoxControl::removeItems( ::sal_Int16 nPos, ::sal_Int16 nCount )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->removeItems( nPos, nCount );
+ }
+
+
+ ::sal_Int16 SAL_CALL OListBoxControl::getItemCount( )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->getItemCount();
+ return 0;
+ }
+
+
+ OUString SAL_CALL OListBoxControl::getItem( ::sal_Int16 nPos )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->getItem( nPos );
+ return OUString( );
+ }
+
+
+ Sequence< OUString > SAL_CALL OListBoxControl::getItems( )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->getItems();
+ return Sequence< OUString >( );
+ }
+
+
+ ::sal_Int16 SAL_CALL OListBoxControl::getSelectedItemPos( )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->getSelectedItemPos();
+ return 0;
+ }
+
+
+ Sequence< ::sal_Int16 > SAL_CALL OListBoxControl::getSelectedItemsPos( )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->getSelectedItemsPos();
+ return Sequence< ::sal_Int16 >( );
+ }
+
+
+ OUString SAL_CALL OListBoxControl::getSelectedItem( )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->getSelectedItem();
+ return OUString( );
+ }
+
+
+ Sequence< OUString > SAL_CALL OListBoxControl::getSelectedItems( )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->getSelectedItems();
+ return Sequence< OUString >( );
+ }
+
+
+ void SAL_CALL OListBoxControl::selectItemPos( ::sal_Int16 nPos, sal_Bool bSelect )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->selectItemPos( nPos, bSelect );
+ }
+
+
+ void SAL_CALL OListBoxControl::selectItemsPos( const Sequence< ::sal_Int16 >& aPositions, sal_Bool bSelect )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->selectItemsPos( aPositions, bSelect );
+ }
+
+
+ void SAL_CALL OListBoxControl::selectItem( const OUString& aItem, sal_Bool bSelect )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->selectItem( aItem, bSelect );
+ }
+
+
+ sal_Bool SAL_CALL OListBoxControl::isMutipleMode( )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->isMutipleMode();
+ return false;
+ }
+
+
+ void SAL_CALL OListBoxControl::setMultipleMode( sal_Bool bMulti )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->setMultipleMode( bMulti );
+ }
+
+
+ ::sal_Int16 SAL_CALL OListBoxControl::getDropDownLineCount( )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->getDropDownLineCount();
+ return 0;
+ }
+
+
+ void SAL_CALL OListBoxControl::setDropDownLineCount( ::sal_Int16 nLines )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->setDropDownLineCount( nLines );
+ }
+
+
+ void SAL_CALL OListBoxControl::makeVisible( ::sal_Int16 nEntry )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->makeVisible( nEntry );
+ }
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OListBoxModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OListBoxModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OListBoxControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OListBoxControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/ListBox.hxx b/forms/source/component/ListBox.hxx
new file mode 100644
index 000000000..cad8cc708
--- /dev/null
+++ b/forms/source/component/ListBox.hxx
@@ -0,0 +1,332 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+#include "cachedrowset.hxx"
+#include "errorbroadcaster.hxx"
+#include "entrylisthelper.hxx"
+
+#include <com/sun/star/form/ListSourceType.hpp>
+#include <com/sun/star/awt/XItemListener.hpp>
+#include <com/sun/star/awt/XFocusListener.hpp>
+#include <com/sun/star/awt/XListBox.hpp>
+#include <com/sun/star/form/XChangeBroadcaster.hpp>
+
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/asyncnotification.hxx>
+#include <connectivity/FValue.hxx>
+#include <cppuhelper/implbase4.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/idle.hxx>
+
+#include <vector>
+
+using namespace comphelper;
+
+/** ListBox is a bit confusing / different from other form components,
+ so here are a few notes:
+
+ The general design philosophy is that a ListBox is a mechanism
+ to translate back and forth between:
+ 1) *display* values (strings that the user sees and chooses)
+ 2) *binding* values, which is what the program (for a dialog),
+ the database, ... cares about.
+
+ A non-data aware ListBox exposes this mechanism through
+ com.sun.star.awt.XItemList (get|set)ItemData.
+
+ In a data-aware ListBox, this is naturally embodied by the
+ StringItemList on the one hand, and the ValueList on the other
+ hand (where, depending on ListSourceType, the ValueList is
+ possibly automatically filled from the BoundColumn of the
+ ListSource).
+
+ This source file implements data-aware ListBox, and the rest
+ of this comment applies to data-aware ListBox (only).
+
+ In all public APIs of the *model* (OListBoxModel),
+ the value of the control is the *binding* value.
+ That is what the bound database field gets,
+ that is what a validator validates,
+ that is what an external value binding
+ (com.sun.star.form.binding.XValueBinding)
+ exchanges with the control.
+
+ As an *implementation* choice, we keep the current value of the
+ ListBox as a sequence of *indices* in the value list, and do the
+ lookup on demand:
+
+ - ListBox's content property (or value property, sorry the
+ terminology is not always consistent) is SelectedItems which is
+ a sequence of *indices* in the value list.
+
+ - That is used to synchronise with our peer (UnoControlListBoxModel).
+
+ In particular, note that getCurrentValue() is a public API (and
+ deals with bound values), but getControlValue and
+ (do)setControlValue are *internal* implementation helpers that
+ deal with *indices*.
+
+ Note that the *view* (OListBoxControl) presents a different story
+ than the model. E.g. the "SelectedItems" property is *display* *values*.
+*/
+
+
+namespace frm
+{
+
+typedef ::std::vector< ::connectivity::ORowSetValue > ValueList;
+
+class OListBoxModel final :public OBoundControlModel
+ ,public OEntryListHelper
+ ,public OErrorBroadcaster
+{
+
+ CachedRowSet m_aListRowSet; // the row set to fill the list
+ ::connectivity::ORowSetValue m_aSaveValue;
+
+ // <properties>
+ css::form::ListSourceType m_eListSourceType; // type of list source
+ css::uno::Any m_aBoundColumn;
+ ValueList m_aListSourceValues;
+ ValueList m_aBoundValues; // do not write directly; use setBoundValues()
+ mutable ValueList m_aConvertedBoundValues;
+ mutable sal_Int32 m_nConvertedBoundValuesType;
+ css::uno::Sequence<sal_Int16> m_aDefaultSelectSeq; // DefaultSelected
+ // </properties>
+
+ mutable sal_Int16 m_nNULLPos; // position of the NULL value in our list
+ sal_Int32 m_nBoundColumnType;
+
+private:
+ ::connectivity::ORowSetValue getFirstSelectedValue() const;
+
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ OListBoxModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OListBoxModel(
+ const OListBoxModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OListBoxModel() override;
+
+// XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OListBoxModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+// UNO binding
+ DECLARE_UNO3_AGG_DEFAULTS(OListBoxModel, OBoundControlModel)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+// OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+// OPropertySetHelper
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle) const override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(
+ css::uno::Any& _rConvertedValue, css::uno::Any& _rOldValue, sal_Int32 _nHandle, const css::uno::Any& _rValue ) override;
+
+private:
+ static const ::connectivity::ORowSetValue s_aEmptyValue;
+ static const ::connectivity::ORowSetValue s_aEmptyStringValue;
+
+ // XMultiPropertySet
+ virtual void SAL_CALL setPropertyValues(const css::uno::Sequence< OUString >& PropertyNames, const css::uno::Sequence< css::uno::Any >& Values) override;
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL
+ write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL
+ read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+ virtual void describeAggregateProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+
+ // OPropertyChangeListener
+ virtual void _propertyChanged( const css::beans::PropertyChangeEvent& _rEvt ) override;
+
+ // prevent method hiding
+ using OBoundControlModel::getFastPropertyValue;
+ using OBoundControlModel::setPropertyValues;
+
+ // OBoundControlModel overridables
+ virtual css::uno::Any translateDbColumnToControlValue( ) override;
+ virtual css::uno::Sequence< css::uno::Type >
+ getSupportedBindingTypes() override;
+ virtual css::uno::Any translateExternalValueToControlValue( const css::uno::Any& _rExternalValue ) const override;
+ virtual css::uno::Any translateControlValueToExternalValue( ) const override;
+ virtual css::uno::Any translateControlValueToValidatableValue( ) const override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+
+ virtual void onConnectedDbColumn( const css::uno::Reference< css::uno::XInterface >& _rxForm ) override;
+ virtual void onDisconnectedDbColumn() override;
+
+ virtual css::uno::Any getDefaultForReset() const override;
+ virtual void resetNoBroadcast() override;
+
+ virtual css::uno::Any getCurrentFormComponentValue() const override;
+
+ // OEntryListHelper overridables
+ virtual void stringItemListChanged( ControlModelLock& _rInstanceLock ) override;
+ virtual void refreshInternalEntryList() override;
+
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ void init();
+ css::uno::Any getCurrentSingleValue() const;
+ css::uno::Sequence<css::uno::Any> getCurrentMultiValue() const;
+ css::uno::Sequence< sal_Int16 > translateBindingValuesToControlValue(
+ const css::uno::Sequence< const css::uno::Any > &i_aValues)
+ const;
+ css::uno::Sequence< sal_Int16 > translateDbValueToControlValue(
+ const ::connectivity::ORowSetValue &aValue)
+ const;
+
+ void loadData( bool _bForce );
+
+ /** refreshes the list boxes list data
+ @precond we don't actually have an external list source
+ */
+ void impl_refreshDbEntryList( bool _bForce );
+
+ void setBoundValues(ValueList &&);
+ void clearBoundValues();
+
+ ValueList impl_getValues() const;
+
+ sal_Int32 getValueType() const;
+
+ void convertBoundValues(sal_Int32 nType) const;
+};
+
+
+//= OListBoxControl
+
+typedef ::cppu::ImplHelper4 < css::awt::XFocusListener
+ , css::awt::XItemListener
+ , css::awt::XListBox
+ , css::form::XChangeBroadcaster
+ > OListBoxControl_BASE;
+
+class OListBoxControl :public OBoundControl
+ ,public OListBoxControl_BASE
+ ,public IEventProcessor
+{
+private:
+ ::comphelper::OInterfaceContainerHelper3<css::form::XChangeListener> m_aChangeListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::awt::XItemListener> m_aItemListeners;
+
+ css::uno::Any m_aCurrentSelection;
+ Idle m_aChangeIdle;
+
+ css::uno::Reference< css::awt::XListBox >
+ m_xAggregateListBox;
+
+ ::rtl::Reference< ::comphelper::AsyncEventNotifier >
+ m_pItemBroadcaster;
+
+protected:
+ // UNO binding
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ explicit OListBoxControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+ virtual ~OListBoxControl() override;
+
+ // UNO binding
+ DECLARE_UNO3_AGG_DEFAULTS(OListBoxControl, OBoundControl)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+// XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OListBoxControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+// XChangeBroadcaster
+ virtual void SAL_CALL addChangeListener(const css::uno::Reference< css::form::XChangeListener>& _rxListener) override;
+ virtual void SAL_CALL removeChangeListener(const css::uno::Reference< css::form::XChangeListener>& _rxListener) override;
+
+// XFocusListener
+ virtual void SAL_CALL focusGained(const css::awt::FocusEvent& _rEvent) override;
+ virtual void SAL_CALL focusLost(const css::awt::FocusEvent& _rEvent) override;
+
+// XItemListener
+ virtual void SAL_CALL itemStateChanged(const css::awt::ItemEvent& _rEvent) override;
+
+// XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+
+// OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+// XListBox
+ virtual void SAL_CALL addItemListener( const css::uno::Reference< css::awt::XItemListener >& l ) override;
+ virtual void SAL_CALL removeItemListener( const css::uno::Reference< css::awt::XItemListener >& l ) override;
+ virtual void SAL_CALL addActionListener( const css::uno::Reference< css::awt::XActionListener >& l ) override;
+ virtual void SAL_CALL removeActionListener( const css::uno::Reference< css::awt::XActionListener >& l ) override;
+ virtual void SAL_CALL addItem( const OUString& aItem, ::sal_Int16 nPos ) override;
+ virtual void SAL_CALL addItems( const css::uno::Sequence< OUString >& aItems, ::sal_Int16 nPos ) override;
+ virtual void SAL_CALL removeItems( ::sal_Int16 nPos, ::sal_Int16 nCount ) override;
+ virtual ::sal_Int16 SAL_CALL getItemCount( ) override;
+ virtual OUString SAL_CALL getItem( ::sal_Int16 nPos ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getItems( ) override;
+ virtual ::sal_Int16 SAL_CALL getSelectedItemPos( ) override;
+ virtual css::uno::Sequence< ::sal_Int16 > SAL_CALL getSelectedItemsPos( ) override;
+ virtual OUString SAL_CALL getSelectedItem( ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSelectedItems( ) override;
+ virtual void SAL_CALL selectItemPos( ::sal_Int16 nPos, sal_Bool bSelect ) override;
+ virtual void SAL_CALL selectItemsPos( const css::uno::Sequence< ::sal_Int16 >& aPositions, sal_Bool bSelect ) override;
+ virtual void SAL_CALL selectItem( const OUString& aItem, sal_Bool bSelect ) override;
+ virtual sal_Bool SAL_CALL isMutipleMode( ) override;
+ virtual void SAL_CALL setMultipleMode( sal_Bool bMulti ) override;
+ virtual ::sal_Int16 SAL_CALL getDropDownLineCount( ) override;
+ virtual void SAL_CALL setDropDownLineCount( ::sal_Int16 nLines ) override;
+ virtual void SAL_CALL makeVisible( ::sal_Int16 nEntry ) override;
+
+protected:
+ // IEventProcessor
+ virtual void processEvent( const ::comphelper::AnyEvent& _rEvent ) override;
+
+private:
+ DECL_LINK( OnTimeout, Timer*, void );
+};
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Numeric.cxx b/forms/source/component/Numeric.cxx
new file mode 100644
index 000000000..7ef408647
--- /dev/null
+++ b/forms/source/component/Numeric.cxx
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "Numeric.hxx"
+#include <services.hxx>
+#include <property.hxx>
+#include <comphelper/types.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+namespace frm
+{
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::form::binding;
+
+ONumericControl::ONumericControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_NUMERICFIELD)
+{
+}
+
+
+css::uno::Sequence<OUString> ONumericControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_NUMERICFIELD;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_NUMERICFIELD;
+ return aSupported;
+}
+
+// ONumericModel
+
+ONumericModel::ONumericModel(const Reference<XComponentContext>& _rxFactory)
+ :OEditBaseModel( _rxFactory, VCL_CONTROLMODEL_NUMERICFIELD, FRM_SUN_CONTROL_NUMERICFIELD, true, true )
+ // use the old control name for compytibility reasons
+{
+
+ m_nClassId = FormComponentType::NUMERICFIELD;
+ initValueProperty( PROPERTY_VALUE, PROPERTY_ID_VALUE );
+}
+
+
+ONumericModel::ONumericModel( const ONumericModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OEditBaseModel( _pOriginal, _rxFactory )
+{
+}
+
+
+ONumericModel::~ONumericModel()
+{
+}
+
+// XCloneable
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL ONumericModel::createClone()
+{
+ rtl::Reference<ONumericModel> pClone = new ONumericModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> ONumericModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControlModel::getSupportedServiceNames();
+
+ sal_Int32 nOldLen = aSupported.getLength();
+ aSupported.realloc( nOldLen + 9 );
+ OUString* pStoreTo = aSupported.getArray() + nOldLen;
+
+ *pStoreTo++ = BINDABLE_CONTROL_MODEL;
+ *pStoreTo++ = DATA_AWARE_CONTROL_MODEL;
+ *pStoreTo++ = VALIDATABLE_CONTROL_MODEL;
+
+ *pStoreTo++ = BINDABLE_DATA_AWARE_CONTROL_MODEL;
+ *pStoreTo++ = VALIDATABLE_BINDABLE_CONTROL_MODEL;
+
+ *pStoreTo++ = FRM_SUN_COMPONENT_NUMERICFIELD;
+ *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_NUMERICFIELD;
+ *pStoreTo++ = BINDABLE_DATABASE_NUMERIC_FIELD;
+
+ *pStoreTo++ = FRM_COMPONENT_NUMERICFIELD;
+
+ return aSupported;
+}
+
+
+void ONumericModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OEditBaseModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 2);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_VALUE, PROPERTY_ID_DEFAULT_VALUE, cppu::UnoType<double>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+OUString SAL_CALL ONumericModel::getServiceName()
+{
+ return FRM_COMPONENT_NUMERICFIELD; // old (non-sun) name for compatibility !
+}
+
+
+bool ONumericModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+{
+ Any aControlValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
+ if ( aControlValue != m_aSaveValue )
+ {
+ if ( !aControlValue.hasValue() )
+ m_xColumnUpdate->updateNull();
+ else
+ {
+ try
+ {
+ m_xColumnUpdate->updateDouble( getDouble( aControlValue ) );
+ }
+ catch(const Exception&)
+ {
+ return false;
+ }
+ }
+ m_aSaveValue = aControlValue;
+ }
+ return true;
+}
+
+
+Any ONumericModel::translateDbColumnToControlValue()
+{
+ m_aSaveValue <<= m_xColumn->getDouble();
+ if ( m_xColumn->wasNull() )
+ m_aSaveValue.clear();
+
+ return m_aSaveValue;
+}
+
+
+Any ONumericModel::getDefaultForReset() const
+{
+ Any aValue;
+ if (m_aDefault.getValueType().getTypeClass() == TypeClass_DOUBLE)
+ aValue = m_aDefault;
+
+ return aValue;
+}
+
+
+void ONumericModel::resetNoBroadcast()
+{
+ OEditBaseModel::resetNoBroadcast();
+ m_aSaveValue.clear();
+}
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_ONumericModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ONumericModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_ONumericControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ONumericControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Numeric.hxx b/forms/source/component/Numeric.hxx
new file mode 100644
index 000000000..0b5cb703e
--- /dev/null
+++ b/forms/source/component/Numeric.hxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "EditBase.hxx"
+
+
+namespace frm
+{
+
+class ONumericModel
+ :public OEditBaseModel
+{
+private:
+ css::uno::Any m_aSaveValue;
+
+public:
+ ONumericModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ ONumericModel(
+ const ONumericModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~ONumericModel() override;
+
+ // css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.ONumericModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // css::io::XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+protected:
+ // OBoundControlModel overridables
+ virtual css::uno::Any
+ translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+
+ virtual css::uno::Any
+ getDefaultForReset() const override;
+ virtual void resetNoBroadcast() override;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+class ONumericControl: public OBoundControl
+{
+public:
+ explicit ONumericControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+
+ // css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.ONumericControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Pattern.cxx b/forms/source/component/Pattern.cxx
new file mode 100644
index 000000000..351db1b52
--- /dev/null
+++ b/forms/source/component/Pattern.cxx
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "Pattern.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::XComponentContext;
+using ::com::sun::star::beans::Property;
+using ::com::sun::star::uno::XInterface;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::sdbc::XRowSet;
+using ::com::sun::star::uno::UNO_QUERY;
+
+namespace FormComponentType = ::com::sun::star::form::FormComponentType;
+
+namespace frm
+{
+
+OPatternControl::OPatternControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_PATTERNFIELD)
+{
+}
+
+css::uno::Sequence<OUString> OPatternControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_PATTERNFIELD;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_PATTERNFIELD;
+ return aSupported;
+}
+
+
+// OPatternModel
+
+
+OPatternModel::OPatternModel(const Reference<XComponentContext>& _rxFactory)
+ :OEditBaseModel( _rxFactory, VCL_CONTROLMODEL_PATTERNFIELD, FRM_SUN_CONTROL_PATTERNFIELD, false, false )
+ // use the old control name for compytibility reasons
+{
+
+ m_nClassId = FormComponentType::PATTERNFIELD;
+ initValueProperty( PROPERTY_TEXT, PROPERTY_ID_TEXT );
+}
+
+
+OPatternModel::OPatternModel( const OPatternModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OEditBaseModel( _pOriginal, _rxFactory )
+{
+}
+
+
+OPatternModel::~OPatternModel()
+{
+}
+
+// XCloneable
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OPatternModel::createClone()
+{
+ rtl::Reference<OPatternModel> pClone = new OPatternModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL OPatternModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControlModel::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 3);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-3] = FRM_SUN_COMPONENT_DATABASE_PATTERNFIELD;
+ pArray[aSupported.getLength()-2] = FRM_SUN_COMPONENT_PATTERNFIELD;
+ pArray[aSupported.getLength()-1] = FRM_COMPONENT_PATTERNFIELD;
+ return aSupported;
+}
+
+
+void OPatternModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OEditBaseModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 4);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_TEXT, PROPERTY_ID_DEFAULT_TEXT, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_EMPTY_IS_NULL, PROPERTY_ID_EMPTY_IS_NULL, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_FILTERPROPOSAL, PROPERTY_ID_FILTERPROPOSAL, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+OUString SAL_CALL OPatternModel::getServiceName()
+{
+ return FRM_COMPONENT_PATTERNFIELD; // old (non-sun) name for compatibility !
+}
+
+
+bool OPatternModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+{
+ Any aNewValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
+
+ if ( aNewValue == m_aLastKnownValue )
+ return true;
+
+ OUString sNewValue;
+ aNewValue >>= sNewValue;
+
+ if ( !aNewValue.hasValue()
+ || ( sNewValue.isEmpty() // an empty string
+ && m_bEmptyIsNull // which should be interpreted as NULL
+ )
+ )
+ {
+ m_xColumnUpdate->updateNull();
+ }
+ else
+ {
+ OSL_ENSURE(m_pFormattedValue,
+ "OPatternModel::commitControlValueToDbColumn: no value helper!");
+ if (!m_pFormattedValue)
+ return false;
+
+ if ( !m_pFormattedValue->setFormattedValue( sNewValue ) )
+ return false;
+ }
+
+ m_aLastKnownValue = aNewValue;
+
+ return true;
+}
+
+
+void OPatternModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
+{
+ OEditBaseModel::onConnectedDbColumn( _rxForm );
+
+ Reference< XPropertySet > xField( getField() );
+ if ( !xField.is() )
+ return;
+
+ m_pFormattedValue.reset( new ::dbtools::FormattedColumnValue( getContext(), Reference< XRowSet >( _rxForm, UNO_QUERY ), xField ) );
+}
+
+
+void OPatternModel::onDisconnectedDbColumn()
+{
+ OEditBaseModel::onDisconnectedDbColumn();
+ m_pFormattedValue.reset();
+}
+
+// XPropertyChangeListener
+
+Any OPatternModel::translateDbColumnToControlValue()
+{
+ OSL_PRECOND(m_pFormattedValue,
+ "OPatternModel::translateDbColumnToControlValue: no value helper!");
+
+ if (m_pFormattedValue)
+ {
+ OUString sValue( m_pFormattedValue->getFormattedValue() );
+ if ( sValue.isEmpty()
+ && m_pFormattedValue->getColumn().is()
+ && m_pFormattedValue->getColumn()->wasNull()
+ )
+ {
+ m_aLastKnownValue.clear();
+ }
+ else
+ {
+ m_aLastKnownValue <<= sValue;
+ }
+ }
+ else
+ m_aLastKnownValue.clear();
+
+ return m_aLastKnownValue.hasValue() ? m_aLastKnownValue : Any( OUString() );
+ // (m_aLastKnownValue is allowed to be VOID, the control value isn't)
+}
+
+// XReset
+
+Any OPatternModel::getDefaultForReset() const
+{
+ return Any( m_aDefaultText );
+}
+
+void OPatternModel::resetNoBroadcast()
+{
+ OEditBaseModel::resetNoBroadcast();
+ m_aLastKnownValue.clear();
+}
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OPatternModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OPatternModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OPatternControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OPatternControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Pattern.hxx b/forms/source/component/Pattern.hxx
new file mode 100644
index 000000000..d0923c13c
--- /dev/null
+++ b/forms/source/component/Pattern.hxx
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "EditBase.hxx"
+
+#include <connectivity/formattedcolumnvalue.hxx>
+
+#include <memory>
+
+
+namespace frm
+{
+
+class OPatternModel
+ :public OEditBaseModel
+{
+private:
+ css::uno::Any m_aLastKnownValue;
+ ::std::unique_ptr< ::dbtools::FormattedColumnValue >
+ m_pFormattedValue;
+
+public:
+ OPatternModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OPatternModel(
+ const OPatternModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OPatternModel() override;
+
+ // css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OPatternModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // css::io::XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+protected:
+ // OBoundControlModel overridables
+ virtual css::uno::Any translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+ virtual void onConnectedDbColumn( const css::uno::Reference< css::uno::XInterface >& _rxForm ) override;
+ virtual void onDisconnectedDbColumn() override;
+
+ virtual css::uno::Any getDefaultForReset() const override;
+ virtual void resetNoBroadcast() override;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+class OPatternControl: public OBoundControl
+{
+public:
+ explicit OPatternControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+
+ // css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OPatternControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/RadioButton.cxx b/forms/source/component/RadioButton.cxx
new file mode 100644
index 000000000..a700f645b
--- /dev/null
+++ b/forms/source/component/RadioButton.cxx
@@ -0,0 +1,402 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "RadioButton.hxx"
+#include "GroupManager.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+namespace frm
+{
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::form::binding;
+
+
+css::uno::Sequence<OUString> SAL_CALL ORadioButtonControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString* pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_RADIOBUTTON;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_RADIOBUTTON;
+ return aSupported;
+}
+
+
+ORadioButtonControl::ORadioButtonControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_RADIOBUTTON)
+{
+}
+
+
+ORadioButtonModel::ORadioButtonModel(const Reference<XComponentContext>& _rxFactory)
+ :OReferenceValueComponent( _rxFactory, VCL_CONTROLMODEL_RADIOBUTTON, FRM_SUN_CONTROL_RADIOBUTTON )
+ // use the old control name for compytibility reasons
+{
+
+ m_nClassId = FormComponentType::RADIOBUTTON;
+ m_aLabelServiceName = FRM_SUN_COMPONENT_GROUPBOX;
+ initValueProperty( PROPERTY_STATE, PROPERTY_ID_STATE );
+ startAggregatePropertyListening( PROPERTY_GROUP_NAME );
+}
+
+
+ORadioButtonModel::ORadioButtonModel( const ORadioButtonModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OReferenceValueComponent( _pOriginal, _rxFactory )
+{
+}
+
+
+ORadioButtonModel::~ORadioButtonModel()
+{
+}
+
+// XCloneable
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL ORadioButtonModel::createClone()
+{
+ rtl::Reference<ORadioButtonModel> pClone = new ORadioButtonModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL ORadioButtonModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OReferenceValueComponent::getSupportedServiceNames();
+
+ sal_Int32 nOldLen = aSupported.getLength();
+ aSupported.realloc( nOldLen + 9 );
+ OUString* pStoreTo = aSupported.getArray() + nOldLen;
+
+ *pStoreTo++ = BINDABLE_CONTROL_MODEL;
+ *pStoreTo++ = DATA_AWARE_CONTROL_MODEL;
+ *pStoreTo++ = VALIDATABLE_CONTROL_MODEL;
+
+ *pStoreTo++ = BINDABLE_DATA_AWARE_CONTROL_MODEL;
+ *pStoreTo++ = VALIDATABLE_BINDABLE_CONTROL_MODEL;
+
+ *pStoreTo++ = FRM_SUN_COMPONENT_RADIOBUTTON;
+ *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_RADIOBUTTON;
+ *pStoreTo++ = BINDABLE_DATABASE_RADIO_BUTTON;
+
+ *pStoreTo++ = FRM_COMPONENT_RADIOBUTTON;
+
+ return aSupported;
+}
+
+
+void ORadioButtonModel::SetSiblingPropsTo(const OUString& rPropName, const Any& rValue)
+{
+ // my name
+ OUString sMyGroup;
+ if (hasProperty(PROPERTY_GROUP_NAME, this))
+ getPropertyValue(PROPERTY_GROUP_NAME) >>= sMyGroup;
+ if (sMyGroup.isEmpty())
+ sMyGroup = m_aName;
+
+ // Iterate over my siblings
+ Reference<XIndexAccess> xIndexAccess(getParent(), UNO_QUERY);
+ if (!xIndexAccess.is())
+ return;
+
+ Reference<XPropertySet> xMyProps = this;
+ OUString sCurrentGroup;
+ sal_Int32 nNumSiblings = xIndexAccess->getCount();
+ for (sal_Int32 i=0; i<nNumSiblings; ++i)
+ {
+ Reference<XPropertySet> xSiblingProperties(xIndexAccess->getByIndex(i), UNO_QUERY);
+ if (!xSiblingProperties.is())
+ continue;
+ if (xMyProps == xSiblingProperties)
+ continue; // do not set myself
+
+ // Only if it's a RadioButton
+ if (!hasProperty(PROPERTY_CLASSID, xSiblingProperties))
+ continue;
+ sal_Int16 nType = 0;
+ xSiblingProperties->getPropertyValue(PROPERTY_CLASSID) >>= nType;
+ if (nType != FormComponentType::RADIOBUTTON)
+ continue;
+
+ // The group association is attached to the name
+ sCurrentGroup = OGroupManager::GetGroupName( xSiblingProperties );
+ if (sCurrentGroup == sMyGroup)
+ xSiblingProperties->setPropertyValue(rPropName, rValue);
+ }
+}
+
+
+void ORadioButtonModel::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue)
+{
+ OReferenceValueComponent::setFastPropertyValue_NoBroadcast( nHandle, rValue );
+
+ // if the label control changed ...
+ if (nHandle == PROPERTY_ID_CONTROLLABEL)
+ { // ... forward this to our siblings
+ SetSiblingPropsTo(PROPERTY_CONTROLLABEL, rValue);
+ }
+
+ // If the ControlSource property has changed ...
+ if (nHandle == PROPERTY_ID_CONTROLSOURCE)
+ { // ... I have to pass the new ControlSource to my siblings, which are in the same RadioButton group as I am
+ SetSiblingPropsTo(PROPERTY_CONTROLSOURCE, rValue);
+ }
+
+ // The other way: if my name changes ...
+ if (nHandle == PROPERTY_ID_NAME)
+ {
+ setControlSource();
+ }
+
+ if (nHandle != PROPERTY_ID_DEFAULT_STATE)
+ return;
+
+ sal_Int16 nValue;
+ rValue >>= nValue;
+ if (1 == nValue)
+ { // Reset the 'default checked' for all Radios of the same group.
+ // Because (as the Highlander already knew): "There can be only one"
+ Any aZero;
+ nValue = 0;
+ aZero <<= nValue;
+ SetSiblingPropsTo(PROPERTY_DEFAULT_STATE, aZero);
+ }
+}
+
+void ORadioButtonModel::setControlSource()
+{
+ Reference<XIndexAccess> xIndexAccess(getParent(), UNO_QUERY);
+ if (!xIndexAccess.is())
+ return;
+
+ OUString sName, sGroupName;
+
+ if (hasProperty(PROPERTY_GROUP_NAME, this))
+ getPropertyValue(PROPERTY_GROUP_NAME) >>= sGroupName;
+ getPropertyValue(PROPERTY_NAME) >>= sName;
+
+ Reference<XPropertySet> xMyProps = this;
+ for (sal_Int32 i=0; i<xIndexAccess->getCount(); ++i)
+ {
+ Reference<XPropertySet> xSiblingProperties(xIndexAccess->getByIndex(i), UNO_QUERY);
+ if (!xSiblingProperties.is())
+ continue;
+
+ if (xMyProps == xSiblingProperties)
+ // Only if I didn't find myself
+ continue;
+
+ sal_Int16 nType = 0;
+ xSiblingProperties->getPropertyValue(PROPERTY_CLASSID) >>= nType;
+ if (nType != FormComponentType::RADIOBUTTON)
+ // Only RadioButtons
+ continue;
+
+ OUString sSiblingName, sSiblingGroupName;
+ if (hasProperty(PROPERTY_GROUP_NAME, xSiblingProperties))
+ xSiblingProperties->getPropertyValue(PROPERTY_GROUP_NAME) >>= sSiblingGroupName;
+ xSiblingProperties->getPropertyValue(PROPERTY_NAME) >>= sSiblingName;
+
+ if ((sGroupName.isEmpty() && sSiblingGroupName.isEmpty() && // (no group name
+ sName == sSiblingName) || // names match) or
+ (!sGroupName.isEmpty() && !sSiblingGroupName.isEmpty() && // (have group name
+ sGroupName == sSiblingGroupName)) // they match)
+ {
+ setPropertyValue(PROPERTY_CONTROLSOURCE, xSiblingProperties->getPropertyValue(PROPERTY_CONTROLSOURCE));
+ break;
+ }
+ }
+}
+
+
+void ORadioButtonModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OReferenceValueComponent::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 1);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+OUString SAL_CALL ORadioButtonModel::getServiceName()
+{
+ return FRM_COMPONENT_RADIOBUTTON; // old (non-sun) name for compatibility !
+}
+
+
+void SAL_CALL ORadioButtonModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ OReferenceValueComponent::write(_rxOutStream);
+
+ // Version
+ _rxOutStream->writeShort(0x0003);
+
+ // Properties
+ _rxOutStream << getReferenceValue();
+ _rxOutStream << static_cast<sal_Int16>(getDefaultChecked());
+ writeHelpTextCompatibly(_rxOutStream);
+
+ // from version 0x0003 : common properties
+ writeCommonProperties(_rxOutStream);
+}
+
+
+void SAL_CALL ORadioButtonModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ OReferenceValueComponent::read(_rxInStream);
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ // Version
+ sal_uInt16 nVersion = _rxInStream->readShort();
+
+ OUString sReferenceValue;
+ sal_Int16 nDefaultChecked( 0 );
+ switch (nVersion)
+ {
+ case 0x0001 :
+ _rxInStream >> sReferenceValue;
+ _rxInStream >> nDefaultChecked;
+ break;
+ case 0x0002 :
+ _rxInStream >> sReferenceValue;
+ _rxInStream >> nDefaultChecked;
+ readHelpTextCompatibly(_rxInStream);
+ break;
+ case 0x0003 :
+ _rxInStream >> sReferenceValue;
+ _rxInStream >> nDefaultChecked;
+ readHelpTextCompatibly(_rxInStream);
+ readCommonProperties(_rxInStream);
+ break;
+ default :
+ OSL_FAIL("ORadioButtonModel::read : unknown version !");
+ defaultCommonProperties();
+ break;
+ }
+
+ setReferenceValue( sReferenceValue );
+ setDefaultChecked( static_cast<ToggleState>(nDefaultChecked) );
+
+ // Display default values after read
+ if ( !getControlSource().isEmpty() )
+ // (not if we don't have a control source - the "State" property acts like it is persistent, then
+ resetNoBroadcast();
+}
+
+
+void ORadioButtonModel::_propertyChanged(const PropertyChangeEvent& _rEvent)
+{
+ if ( _rEvent.PropertyName == PROPERTY_STATE )
+ {
+ if ( _rEvent.NewValue == sal_Int16(1) )
+ {
+ // If my status has changed to 'checked', I have to reset all my siblings, which are in the same group as I am
+ Any aZero;
+ aZero <<= sal_Int16(0);
+ SetSiblingPropsTo( PROPERTY_STATE, aZero );
+ }
+ }
+ else if ( _rEvent.PropertyName == PROPERTY_GROUP_NAME )
+ {
+ setControlSource();
+ // Can't call OReferenceValueComponent::_propertyChanged(), as it
+ // doesn't know what to do with the GroupName property.
+ return;
+ }
+
+ OReferenceValueComponent::_propertyChanged( _rEvent );
+}
+
+
+Any ORadioButtonModel::translateDbColumnToControlValue()
+{
+ return Any( static_cast<sal_Int16>( ( m_xColumn->getString() == getReferenceValue() ) ? TRISTATE_TRUE : TRISTATE_FALSE )
+ );
+}
+
+
+Any ORadioButtonModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+{
+ Any aControlValue = OReferenceValueComponent::translateExternalValueToControlValue( _rExternalValue );
+ sal_Int16 nState = TRISTATE_FALSE;
+ if ( ( aControlValue >>= nState ) && ( nState == TRISTATE_INDET ) )
+ // radio buttons do not have the DONTKNOW state
+ aControlValue <<= sal_Int16(TRISTATE_FALSE);
+ return aControlValue;
+}
+
+
+bool ORadioButtonModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+{
+ Reference< XPropertySet > xField( getField() );
+ OSL_PRECOND( xField.is(), "ORadioButtonModel::commitControlValueToDbColumn: not bound!" );
+ if ( xField.is() )
+ {
+ try
+ {
+ sal_Int16 nValue = 0;
+ m_xAggregateSet->getPropertyValue( PROPERTY_STATE ) >>= nValue;
+ if ( nValue == 1 )
+ xField->setPropertyValue( PROPERTY_VALUE, Any( getReferenceValue() ) );
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("ORadioButtonModel::commitControlValueToDbColumn: could not commit !");
+ }
+ }
+ return true;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_ORadioButtonModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ORadioButtonModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_ORadioButtonControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ORadioButtonControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/RadioButton.hxx b/forms/source/component/RadioButton.hxx
new file mode 100644
index 000000000..0e50acfc4
--- /dev/null
+++ b/forms/source/component/RadioButton.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "refvaluecomponent.hxx"
+
+
+namespace frm
+{
+
+class ORadioButtonModel final : public OReferenceValueComponent
+{
+public:
+ ORadioButtonModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ ORadioButtonModel(
+ const ORadioButtonModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~ORadioButtonModel() override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.ORadioButtonModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // OPropertySetHelper
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL
+ write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL
+ read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // OPropertyChangeListener
+ virtual void _propertyChanged(const css::beans::PropertyChangeEvent& evt) override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+private:
+ // OBoundControlModel overridables
+ virtual css::uno::Any translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+ virtual css::uno::Any translateExternalValueToControlValue( const css::uno::Any& _rExternalValue ) const override;
+
+ void SetSiblingPropsTo(const OUString& rPropName, const css::uno::Any& rValue);
+
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ void setControlSource();
+};
+
+class ORadioButtonControl: public OBoundControl
+{
+public:
+ explicit ORadioButtonControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.ORadioButtonControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Time.cxx b/forms/source/component/Time.cxx
new file mode 100644
index 000000000..fffccb83a
--- /dev/null
+++ b/forms/source/component/Time.cxx
@@ -0,0 +1,334 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "Time.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <connectivity/dbconversion.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+using namespace dbtools;
+
+namespace frm
+{
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+
+
+//=
+
+OTimeControl::OTimeControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_TIMEFIELD)
+{
+}
+
+
+Sequence<Type> OTimeControl::_getTypes()
+{
+ return OBoundControl::_getTypes();
+}
+
+
+css::uno::Sequence<OUString> SAL_CALL OTimeControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_TIMEFIELD;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_TIMEFIELD;
+ return aSupported;
+}
+
+
+//= OTimeModel
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL OTimeModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControlModel::getSupportedServiceNames();
+
+ sal_Int32 nOldLen = aSupported.getLength();
+ aSupported.realloc( nOldLen + 9 );
+ OUString* pStoreTo = aSupported.getArray() + nOldLen;
+
+ *pStoreTo++ = BINDABLE_CONTROL_MODEL;
+ *pStoreTo++ = DATA_AWARE_CONTROL_MODEL;
+ *pStoreTo++ = VALIDATABLE_CONTROL_MODEL;
+
+ *pStoreTo++ = BINDABLE_DATA_AWARE_CONTROL_MODEL;
+ *pStoreTo++ = VALIDATABLE_BINDABLE_CONTROL_MODEL;
+
+ *pStoreTo++ = FRM_SUN_COMPONENT_TIMEFIELD;
+ *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_TIMEFIELD;
+ *pStoreTo++ = BINDABLE_DATABASE_TIME_FIELD;
+
+ *pStoreTo++ = FRM_COMPONENT_TIMEFIELD;
+
+ return aSupported;
+}
+
+
+Sequence<Type> OTimeModel::_getTypes()
+{
+ return OBoundControlModel::_getTypes();
+}
+
+
+OTimeModel::OTimeModel(const Reference<XComponentContext>& _rxFactory)
+ : OEditBaseModel(_rxFactory, VCL_CONTROLMODEL_TIMEFIELD,
+ FRM_SUN_CONTROL_TIMEFIELD, true, true)
+ // use the old control name for compatibility reasons
+ , OLimitedFormats(_rxFactory, FormComponentType::TIMEFIELD)
+ , m_bDateTimeField(false)
+{
+ m_nClassId = FormComponentType::TIMEFIELD;
+ initValueProperty( PROPERTY_TIME, PROPERTY_ID_TIME );
+
+ setAggregateSet(m_xAggregateFastSet, getOriginalHandle(PROPERTY_ID_TIMEFORMAT));
+}
+
+
+OTimeModel::OTimeModel(const OTimeModel* _pOriginal, const Reference<XComponentContext>& _rxFactory)
+ : OEditBaseModel(_pOriginal, _rxFactory)
+ , OLimitedFormats(_rxFactory, FormComponentType::TIMEFIELD)
+ , m_bDateTimeField(false)
+{
+ setAggregateSet( m_xAggregateFastSet, getOriginalHandle( PROPERTY_ID_TIMEFORMAT ) );
+}
+
+
+OTimeModel::~OTimeModel( )
+{
+ setAggregateSet(Reference< XFastPropertySet >(), -1);
+}
+
+// XCloneable
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OTimeModel::createClone()
+{
+ rtl::Reference<OTimeModel> pClone = new OTimeModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+OUString SAL_CALL OTimeModel::getServiceName()
+{
+ return FRM_COMPONENT_TIMEFIELD; // old (non-sun) name for compatibility !
+}
+
+// XPropertySet
+
+void OTimeModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OEditBaseModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 4);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_TIME, PROPERTY_ID_DEFAULT_TIME, cppu::UnoType<util::Time>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_FORMATKEY, PROPERTY_ID_FORMATKEY, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_FORMATSSUPPLIER, PROPERTY_ID_FORMATSSUPPLIER, cppu::UnoType<XNumberFormatsSupplier>::get(),
+ css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+void SAL_CALL OTimeModel::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle ) const
+{
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_FORMATKEY:
+ getFormatKeyPropertyValue(_rValue);
+ break;
+ case PROPERTY_ID_FORMATSSUPPLIER:
+ _rValue <<= getFormatsSupplier();
+ break;
+ default:
+ OEditBaseModel::getFastPropertyValue(_rValue, _nHandle);
+ break;
+ }
+}
+
+
+sal_Bool SAL_CALL OTimeModel::convertFastPropertyValue(Any& _rConvertedValue, Any& _rOldValue,
+ sal_Int32 _nHandle, const Any& _rValue )
+{
+ if (PROPERTY_ID_FORMATKEY == _nHandle)
+ return convertFormatKeyPropertyValue(_rConvertedValue, _rOldValue, _rValue);
+ else
+ return OEditBaseModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue );
+}
+
+
+void SAL_CALL OTimeModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
+{
+ if (PROPERTY_ID_FORMATKEY == _nHandle)
+ setFormatKeyPropertyValue(_rValue);
+ else
+ OEditBaseModel::setFastPropertyValue_NoBroadcast(_nHandle, _rValue);
+}
+
+// XLoadListener
+
+void OTimeModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
+{
+ OBoundControlModel::onConnectedDbColumn( _rxForm );
+ Reference<XPropertySet> xField = getField();
+ if (!xField.is())
+ return;
+
+ m_bDateTimeField = false;
+ try
+ {
+ sal_Int32 nFieldType = 0;
+ xField->getPropertyValue(PROPERTY_FIELDTYPE) >>= nFieldType;
+ m_bDateTimeField = (nFieldType == DataType::TIMESTAMP);
+ }
+ catch(const Exception&)
+ {
+ }
+}
+
+
+bool OTimeModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+{
+ Any aControlValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
+ if ( aControlValue == m_aSaveValue )
+ return true;
+
+ if ( !aControlValue.hasValue() )
+ m_xColumnUpdate->updateNull();
+ else
+ {
+ try
+ {
+ util::Time aTime;
+ if ( !( aControlValue >>= aTime ) )
+ {
+ sal_Int64 nAsInt(0);
+ aControlValue >>= nAsInt;
+ aTime = DBTypeConversion::toTime(nAsInt);
+ }
+
+ if (!m_bDateTimeField)
+ m_xColumnUpdate->updateTime(aTime);
+ else
+ {
+ util::DateTime aDateTime = m_xColumn->getTimestamp();
+ if (aDateTime.Year == 0 && aDateTime.Month == 0 && aDateTime.Day == 0)
+ aDateTime = ::com::sun::star::util::DateTime(0,0,0,0,30,12,1899, false);
+ aDateTime.NanoSeconds = aTime.NanoSeconds;
+ aDateTime.Seconds = aTime.Seconds;
+ aDateTime.Minutes = aTime.Minutes;
+ aDateTime.Hours = aTime.Hours;
+ m_xColumnUpdate->updateTimestamp(aDateTime);
+ }
+ }
+ catch(const Exception&)
+ {
+ return false;
+ }
+ }
+ m_aSaveValue = aControlValue;
+ return true;
+}
+
+
+Any OTimeModel::translateControlValueToExternalValue( ) const
+{
+ return getControlValue();
+}
+
+
+Any OTimeModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+{
+ return _rExternalValue;
+}
+
+
+Any OTimeModel::translateControlValueToValidatableValue( ) const
+{
+ return getControlValue();
+}
+
+
+Any OTimeModel::translateDbColumnToControlValue()
+{
+ util::Time aTime = m_xColumn->getTime();
+ if ( m_xColumn->wasNull() )
+ m_aSaveValue.clear();
+ else
+ m_aSaveValue <<= aTime;
+
+ return m_aSaveValue;
+}
+
+
+Any OTimeModel::getDefaultForReset() const
+{
+ return m_aDefault;
+}
+
+
+void OTimeModel::resetNoBroadcast()
+{
+ OEditBaseModel::resetNoBroadcast();
+ m_aSaveValue.clear();
+}
+
+
+Sequence< Type > OTimeModel::getSupportedBindingTypes()
+{
+ return Sequence< Type >( & cppu::UnoType<util::Time>::get(), 1 );
+}
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OTimeModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OTimeModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OTimeControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OTimeControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Time.hxx b/forms/source/component/Time.hxx
new file mode 100644
index 000000000..3a6793fa5
--- /dev/null
+++ b/forms/source/component/Time.hxx
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "EditBase.hxx"
+#include <limitedformats.hxx>
+
+
+namespace frm
+{
+
+class OTimeModel
+ :public OEditBaseModel
+ ,public OLimitedFormats
+{
+private:
+ css::uno::Any m_aSaveValue;
+ bool m_bDateTimeField;
+
+protected:
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ OTimeModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OTimeModel(
+ const OTimeModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OTimeModel() override;
+
+ // css::io::XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+
+ // css::beans::XPropertySet
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle ) const override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(css::uno::Any& rConvertedValue, css::uno::Any& rOldValue,
+ sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const css::uno::Any& rValue) override;
+
+ // css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OTimeModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+ // prevent method hiding
+ using OBoundControlModel::getFastPropertyValue;
+
+protected:
+ // OBoundControlModel overridables
+ virtual css::uno::Any
+ translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+
+ virtual css::uno::Sequence< css::uno::Type >
+ getSupportedBindingTypes() override;
+ virtual css::uno::Any translateControlValueToExternalValue( ) const override;
+ virtual css::uno::Any translateExternalValueToControlValue( const css::uno::Any& _rExternalValue ) const override;
+
+ virtual css::uno::Any translateControlValueToValidatableValue( ) const override;
+
+ virtual css::uno::Any getDefaultForReset() const override;
+ virtual void resetNoBroadcast() override;
+
+ virtual void onConnectedDbColumn( const css::uno::Reference< css::uno::XInterface >& _rxForm ) override;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+class OTimeControl: public OBoundControl
+{
+protected:
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ explicit OTimeControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+ DECLARE_UNO3_AGG_DEFAULTS(OTimeControl, OBoundControl)
+
+ // css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OTimeControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/cachedrowset.cxx b/forms/source/component/cachedrowset.cxx
new file mode 100644
index 000000000..33ca9c640
--- /dev/null
+++ b/forms/source/component/cachedrowset.cxx
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "cachedrowset.hxx"
+#include <frm_strings.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sdb/XQueriesSupplier.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbc/ResultSetType.hpp>
+
+#include <tools/diagnose_ex.h>
+
+
+namespace frm
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::sdbc::XConnection;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::sdbc::SQLException;
+ using ::com::sun::star::sdb::XQueriesSupplier;
+ using ::com::sun::star::container::XNameAccess;
+ using ::com::sun::star::sdbc::XResultSet;
+ using ::com::sun::star::sdbc::XStatement;
+
+ namespace ResultSetType = ::com::sun::star::sdbc::ResultSetType;
+
+ struct CachedRowSet_Data
+ {
+ OUString sCommand;
+ bool bEscapeProcessing;
+ Reference< XConnection > xConnection;
+
+ bool bStatementDirty;
+
+ CachedRowSet_Data()
+ :bEscapeProcessing( false )
+ ,bStatementDirty( true )
+ {
+ }
+ };
+
+ CachedRowSet::CachedRowSet()
+ :m_pData( new CachedRowSet_Data )
+ {
+ }
+
+
+ CachedRowSet::~CachedRowSet()
+ {
+ dispose();
+ }
+
+
+ void CachedRowSet::setCommand( const OUString& _rCommand )
+ {
+ if ( m_pData->sCommand == _rCommand )
+ return;
+
+ m_pData->sCommand = _rCommand;
+ m_pData->bStatementDirty = true;
+ }
+
+
+ void CachedRowSet::setCommandFromQuery( const OUString& _rQueryName )
+ {
+ Reference< XQueriesSupplier > xSupplyQueries( m_pData->xConnection, UNO_QUERY_THROW );
+ Reference< XNameAccess > xQueries ( xSupplyQueries->getQueries(), UNO_SET_THROW );
+ Reference< XPropertySet > xQuery ( xQueries->getByName( _rQueryName ), UNO_QUERY_THROW );
+
+ bool bEscapeProcessing( false );
+ OSL_VERIFY( xQuery->getPropertyValue( PROPERTY_ESCAPE_PROCESSING ) >>= bEscapeProcessing );
+ setEscapeProcessing( bEscapeProcessing );
+
+ OUString sCommand;
+ OSL_VERIFY( xQuery->getPropertyValue( PROPERTY_COMMAND ) >>= sCommand );
+ setCommand( sCommand );
+ }
+
+
+ void CachedRowSet::setEscapeProcessing ( const bool _bEscapeProcessing )
+ {
+ if ( m_pData->bEscapeProcessing == _bEscapeProcessing )
+ return;
+
+ m_pData->bEscapeProcessing = _bEscapeProcessing;
+ m_pData->bStatementDirty = true;
+ }
+
+
+ void CachedRowSet::setConnection( const Reference< XConnection >& _rxConnection )
+ {
+ if ( m_pData->xConnection == _rxConnection )
+ return;
+
+ m_pData->xConnection = _rxConnection;
+ m_pData->bStatementDirty = true;
+ }
+
+
+ Reference< XResultSet > CachedRowSet::execute()
+ {
+ Reference< XResultSet > xResult;
+ try
+ {
+ OSL_PRECOND( m_pData->xConnection.is(), "CachedRowSet::execute: how am I expected to do this without a connection?" );
+ if ( !m_pData->xConnection.is() )
+ return xResult;
+
+ Reference< XStatement > xStatement( m_pData->xConnection->createStatement(), UNO_SET_THROW );
+ Reference< XPropertySet > xStatementProps( xStatement, UNO_QUERY_THROW );
+ xStatementProps->setPropertyValue( PROPERTY_ESCAPE_PROCESSING, Any( m_pData->bEscapeProcessing ) );
+ xStatementProps->setPropertyValue( PROPERTY_RESULTSET_TYPE, Any( ResultSetType::FORWARD_ONLY ) );
+
+ xResult.set( xStatement->executeQuery( m_pData->sCommand ), UNO_SET_THROW );
+ m_pData->bStatementDirty = false;
+ }
+ catch( const SQLException& )
+ {
+ throw;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ return xResult;
+ }
+
+
+ bool CachedRowSet::isDirty() const
+ {
+ return m_pData->bStatementDirty;
+ }
+
+
+ void CachedRowSet::dispose()
+ {
+ try
+ {
+ m_pData.reset( new CachedRowSet_Data );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/cachedrowset.hxx b/forms/source/component/cachedrowset.hxx
new file mode 100644
index 000000000..f87ee2c07
--- /dev/null
+++ b/forms/source/component/cachedrowset.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+
+#include <memory>
+
+
+namespace frm
+{
+
+ struct CachedRowSet_Data;
+
+ /// caches a result set obtained from a SQL statement
+ class CachedRowSet
+ {
+ public:
+ CachedRowSet();
+ ~CachedRowSet();
+
+ public:
+ /** executes the statement
+
+ @return
+ the result set produced by the statement. The caller takes ownership of the
+ given object.
+
+ @throws css::sdbc::SQLException
+ if such an exception is thrown when executing the statement
+ */
+ css::uno::Reference< css::sdbc::XResultSet >
+ execute();
+
+ /// determines whether the row set properties are dirty, i.e. have changed since the last call to execute
+ bool isDirty() const;
+
+ /// disposes the instance and frees all associated resources
+ void dispose();
+
+ /** sets the command of a query as command to be executed
+
+ A connection must have been set before.
+
+ @throws Exception
+ */
+ void setCommandFromQuery ( const OUString& _rQueryName );
+
+ void setCommand ( const OUString& _rCommand );
+ void setEscapeProcessing ( const bool _bEscapeProcessing );
+ void setConnection ( const css::uno::Reference< css::sdbc::XConnection >& _rxConnection );
+
+ private:
+ ::std::unique_ptr< CachedRowSet_Data > m_pData;
+ };
+
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/clickableimage.cxx b/forms/source/component/clickableimage.cxx
new file mode 100644
index 000000000..7079344db
--- /dev/null
+++ b/forms/source/component/clickableimage.cxx
@@ -0,0 +1,838 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "clickableimage.hxx"
+#include <controlfeatureinterception.hxx>
+#include <urltransformer.hxx>
+#include <componenttools.hxx>
+#include <com/sun/star/form/XSubmit.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/awt/ActionEvent.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/graphic/GraphicObject.hpp>
+#include <com/sun/star/util/VetoException.hpp>
+#include <tools/urlobj.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/objsh.hxx>
+#include <osl/mutex.hxx>
+#include <property.hxx>
+#include <services.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <svtools/imageresourceaccess.hxx>
+#define LOCAL_URL_PREFIX '#'
+
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::sdb;
+ using namespace ::com::sun::star::sdbc;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::io;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::form::submission;
+ using namespace ::com::sun::star::graphic;
+ using ::com::sun::star::awt::MouseEvent;
+ using ::com::sun::star::task::XInteractionHandler;
+
+
+ // OClickableImageBaseControl
+
+
+ Sequence<Type> OClickableImageBaseControl::_getTypes()
+ {
+ static Sequence<Type> const aTypes =
+ concatSequences(OControl::_getTypes(), OClickableImageBaseControl_BASE::getTypes());
+ return aTypes;
+ }
+
+
+ OClickableImageBaseControl::OClickableImageBaseControl(const Reference<XComponentContext>& _rxFactory, const OUString& _aService)
+ :OControl(_rxFactory, _aService)
+ ,m_aSubmissionVetoListeners( m_aMutex )
+ ,m_aApproveActionListeners( m_aMutex )
+ ,m_aActionListeners( m_aMutex )
+ {
+ m_pFeatureInterception.reset( new ControlFeatureInterception( _rxFactory ) );
+ }
+
+
+ OClickableImageBaseControl::~OClickableImageBaseControl()
+ {
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+ }
+
+ // UNO Binding
+
+ Any SAL_CALL OClickableImageBaseControl::queryAggregation(const Type& _rType)
+ {
+ Any aReturn = OControl::queryAggregation(_rType);
+ if (!aReturn.hasValue())
+ aReturn = OClickableImageBaseControl_BASE::queryInterface(_rType);
+ return aReturn;
+ }
+
+ // XApproveActionBroadcaster
+
+ void OClickableImageBaseControl::addApproveActionListener(
+ const Reference<XApproveActionListener>& l)
+ {
+ m_aApproveActionListeners.addInterface(l);
+ }
+
+
+ void OClickableImageBaseControl::removeApproveActionListener(
+ const Reference<XApproveActionListener>& l)
+ {
+ m_aApproveActionListeners.removeInterface(l);
+ }
+
+
+ void SAL_CALL OClickableImageBaseControl::registerDispatchProviderInterceptor( const Reference< XDispatchProviderInterceptor >& _rxInterceptor )
+ {
+ m_pFeatureInterception->registerDispatchProviderInterceptor( _rxInterceptor );
+ }
+
+
+ void SAL_CALL OClickableImageBaseControl::releaseDispatchProviderInterceptor( const Reference< XDispatchProviderInterceptor >& _rxInterceptor )
+ {
+ m_pFeatureInterception->releaseDispatchProviderInterceptor( _rxInterceptor );
+ }
+
+ // OComponentHelper
+
+ void OClickableImageBaseControl::disposing()
+ {
+ EventObject aEvent( static_cast< XWeak* >( this ) );
+ m_aApproveActionListeners.disposeAndClear( aEvent );
+ m_aActionListeners.disposeAndClear( aEvent );
+ m_aSubmissionVetoListeners.disposeAndClear( aEvent );
+ m_pFeatureInterception->dispose();
+
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_pThread.clear();
+ }
+
+ OControl::disposing();
+ }
+
+
+ OImageProducerThread_Impl* OClickableImageBaseControl::getImageProducerThread()
+ {
+ if ( !m_pThread.is() )
+ {
+ m_pThread = new OImageProducerThread_Impl( this );
+ m_pThread->create();
+ }
+ return m_pThread.get();
+ }
+
+
+ bool OClickableImageBaseControl::approveAction( )
+ {
+ bool bCancelled = false;
+ EventObject aEvent( static_cast< XWeak* >( this ) );
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter( m_aApproveActionListeners );
+ while( !bCancelled && aIter.hasMoreElements() )
+ {
+ // Every approveAction method must be thread-safe!
+ if( !aIter.next()->approveAction( aEvent ) )
+ bCancelled = true;
+ }
+
+ return !bCancelled;
+ }
+
+
+ // This method is also called from a thread and thus must be thread-safe.
+ void OClickableImageBaseControl::actionPerformed_Impl(bool bNotifyListener, const MouseEvent& rEvt)
+ {
+ if( bNotifyListener )
+ {
+ if ( !approveAction() )
+ return;
+ }
+
+ // Whether the rest of the code is thread-safe, one can't tell. Therefore
+ // we do most of the work on a locked solar mutex.
+ Reference<XPropertySet> xSet;
+ Reference< XInterface > xModelsParent;
+ FormButtonType eButtonType = FormButtonType_PUSH;
+ {
+ SolarMutexGuard aGuard;
+
+ // Get parent
+ Reference<XFormComponent> xComp(getModel(), UNO_QUERY);
+ if (!xComp.is())
+ return;
+
+ xModelsParent = xComp->getParent();
+ if (!xModelsParent.is())
+ return;
+
+ // Which button type?
+ xSet.set(xComp, css::uno::UNO_QUERY);
+ if ( !xSet.is() )
+ return;
+ xSet->getPropertyValue(PROPERTY_BUTTONTYPE) >>= eButtonType;
+ }
+
+ switch (eButtonType)
+ {
+ case FormButtonType_RESET:
+ {
+ // Reset methods must be thread-safe!
+ Reference<XReset> xReset(xModelsParent, UNO_QUERY);
+ if (!xReset.is())
+ return;
+
+ xReset->reset();
+ }
+ break;
+
+ case FormButtonType_SUBMIT:
+ {
+ // if some outer component can provide an interaction handler, use it
+ Reference< XInteractionHandler > xHandler( m_pFeatureInterception->queryDispatch( "private:/InteractionHandler" ), UNO_QUERY );
+ try
+ {
+ implSubmit( rEvt, xHandler );
+ }
+ catch( const Exception& )
+ {
+ // ignore
+ }
+ }
+ break;
+
+ case FormButtonType_URL:
+ {
+ SolarMutexGuard aGuard;
+
+ Reference< XModel > xModel = getXModel(xModelsParent);
+ if (!xModel.is())
+ return;
+
+
+ // Execute URL now
+ Reference< XController > xController = xModel->getCurrentController();
+ if (!xController.is())
+ return;
+
+ Reference< XFrame > xFrame = xController->getFrame();
+ if( !xFrame.is() )
+ return;
+
+ URL aURL;
+ aURL.Complete =
+ getString(xSet->getPropertyValue(PROPERTY_TARGET_URL));
+
+ if (!aURL.Complete.isEmpty() && (LOCAL_URL_PREFIX == aURL.Complete[0]))
+ { // FIXME: The URL contains a local URL only. Since the URLTransformer does not handle this case correctly
+ // (it can't: it does not know the document URL), we have to take care for this ourself.
+ // The real solution would be to not allow such relative URLs (there is a rule that at runtime, all
+ // URLs have to be absolute), but for compatibility reasons this is no option.
+ // The more as the user does not want to see a local URL as "file://<path>/<document>#mark" if it
+ // could be "#mark" as well.
+ // If we someday say that this hack (yes, it's kind of a hack) is not sustainable anymore, the complete
+ // solution would be:
+ // * recognize URLs consisting of a mark only while _reading_ the document
+ // * for this, allow the INetURLObject (which at the moment is invoked when reading URLs) to
+ // transform such mark-only URLs into correct absolute URLs
+ // * at the UI, show only the mark
+ // * !!! recognize every SAVEAS on the document, so the absolute URL can be adjusted. This seems
+ // rather impossible !!!
+ aURL.Mark = aURL.Complete;
+ aURL.Complete = xModel->getURL();
+ aURL.Complete += aURL.Mark;
+ }
+
+ bool bDispatchUrlInternal = false;
+ xSet->getPropertyValue(PROPERTY_DISPATCHURLINTERNAL) >>= bDispatchUrlInternal;
+ if ( bDispatchUrlInternal )
+ {
+ m_pFeatureInterception->getTransformer().parseSmartWithProtocol( aURL, INET_FILE_SCHEME );
+
+ OUString aTargetFrame;
+ xSet->getPropertyValue(PROPERTY_TARGET_FRAME) >>= aTargetFrame;
+
+ Reference< XDispatch > xDisp = Reference< XDispatchProvider > (xFrame,UNO_QUERY_THROW)->queryDispatch( aURL, aTargetFrame,
+ FrameSearchFlag::SELF | FrameSearchFlag::PARENT |
+ FrameSearchFlag::SIBLINGS | FrameSearchFlag::CREATE );
+
+ Sequence<PropertyValue> aArgs { comphelper::makePropertyValue("Referer", xModel->getURL()) };
+
+ if (xDisp.is())
+ xDisp->dispatch( aURL, aArgs );
+ }
+ else
+ {
+ URL aHyperLink = m_pFeatureInterception->getTransformer().getStrictURL( ".uno:OpenHyperlink" );
+
+ Reference< XDispatch > xDisp = Reference< XDispatchProvider > (xFrame,UNO_QUERY_THROW)->queryDispatch(aHyperLink, OUString() , 0);
+
+ if ( xDisp.is() )
+ {
+ Sequence<PropertyValue> aProps{
+ comphelper::makePropertyValue("URL", aURL.Complete),
+ comphelper::makePropertyValue(
+ "FrameName", xSet->getPropertyValue(PROPERTY_TARGET_FRAME)),
+ comphelper::makePropertyValue("Referer", xModel->getURL())
+ };
+
+ xDisp->dispatch( aHyperLink, aProps );
+ }
+ }
+ } break;
+ default:
+ {
+ // notify the action listeners for a push button
+ ActionEvent aEvt(static_cast<XWeak*>(this), m_aActionCommand);
+ m_aActionListeners.notifyEach( &XActionListener::actionPerformed, aEvt );
+ }
+ }
+ }
+
+
+ void SAL_CALL OClickableImageBaseControl::addSubmissionVetoListener( const Reference< submission::XSubmissionVetoListener >& listener )
+ {
+ m_aSubmissionVetoListeners.addInterface( listener );
+ }
+
+
+ void SAL_CALL OClickableImageBaseControl::removeSubmissionVetoListener( const Reference< submission::XSubmissionVetoListener >& listener )
+ {
+ m_aSubmissionVetoListeners.removeInterface( listener );
+ }
+
+
+ void SAL_CALL OClickableImageBaseControl::submitWithInteraction( const Reference< XInteractionHandler >& _rxHandler )
+ {
+ implSubmit( MouseEvent(), _rxHandler );
+ }
+
+
+ void SAL_CALL OClickableImageBaseControl::submit( )
+ {
+ implSubmit( MouseEvent(), nullptr );
+ }
+
+
+ Sequence< OUString > SAL_CALL OClickableImageBaseControl::getSupportedServiceNames( )
+ {
+ Sequence< OUString > aSupported = OControl::getSupportedServiceNames();
+ aSupported.realloc( aSupported.getLength() + 1 );
+
+ OUString* pArray = aSupported.getArray();
+ pArray[ aSupported.getLength() - 1 ] = FRM_SUN_CONTROL_SUBMITBUTTON;
+
+ return aSupported;
+ }
+
+
+ void OClickableImageBaseControl::implSubmit( const MouseEvent& _rEvent, const Reference< XInteractionHandler >& _rxHandler )
+ {
+ try
+ {
+ // allow the veto listeners to join the game
+ m_aSubmissionVetoListeners.notifyEach( &XSubmissionVetoListener::submitting, EventObject( *this ) );
+
+ // see whether there's an "submit interceptor" set at our model
+ Reference< submission::XSubmissionSupplier > xSubmissionSupp( getModel(), UNO_QUERY );
+ Reference< XSubmission > xSubmission;
+ if ( xSubmissionSupp.is() )
+ xSubmission = xSubmissionSupp->getSubmission();
+
+ if ( xSubmission.is() )
+ {
+ if ( !_rxHandler.is() )
+ xSubmission->submit();
+ else
+ xSubmission->submitWithInteraction( _rxHandler );
+ }
+ else
+ {
+ // no "interceptor" -> ordinary (old-way) submission
+ Reference< XChild > xChild( getModel(), UNO_QUERY );
+ Reference< XSubmit > xParentSubmission;
+ if ( xChild.is() )
+ xParentSubmission.set(xChild->getParent(), css::uno::UNO_QUERY);
+ if ( xParentSubmission.is() )
+ xParentSubmission->submit( this, _rEvent );
+ }
+ }
+ catch( const VetoException& )
+ {
+ // allowed to leave
+ throw;
+ }
+ catch( const RuntimeException& )
+ {
+ // allowed to leave
+ throw;
+ }
+ catch( const WrappedTargetException& )
+ {
+ // allowed to leave
+ throw;
+ }
+ catch( const Exception& )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ TOOLS_WARN_EXCEPTION( "forms.component", "OClickableImageBaseControl::implSubmit: caught an unknown exception!" );
+ throw WrappedTargetException( OUString(), *this, anyEx );
+ }
+ }
+
+
+ // OClickableImageBaseModel
+
+
+ Sequence<Type> OClickableImageBaseModel::_getTypes()
+ {
+ return concatSequences(
+ OControlModel::_getTypes(),
+ OClickableImageBaseModel_Base::getTypes()
+ );
+ }
+
+
+ OClickableImageBaseModel::OClickableImageBaseModel( const Reference< XComponentContext >& _rxFactory, const OUString& _rUnoControlModelTypeName,
+ const OUString& rDefault )
+ :OControlModel( _rxFactory, _rUnoControlModelTypeName, rDefault )
+ ,OPropertyChangeListener(m_aMutex)
+ ,m_bDispatchUrlInternal(false)
+ ,m_bProdStarted(false)
+ {
+ implConstruct();
+ m_eButtonType = FormButtonType_PUSH;
+ }
+
+
+ OClickableImageBaseModel::OClickableImageBaseModel( const OClickableImageBaseModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OControlModel( _pOriginal, _rxFactory )
+ ,OPropertyChangeListener( m_aMutex )
+ ,m_xGraphicObject( _pOriginal->m_xGraphicObject )
+ ,m_bDispatchUrlInternal(false)
+ ,m_bProdStarted( false )
+ {
+ implConstruct();
+
+ // copy properties
+ m_eButtonType = _pOriginal->m_eButtonType;
+ m_sTargetURL = _pOriginal->m_sTargetURL;
+ m_sTargetFrame = _pOriginal->m_sTargetFrame;
+ m_bDispatchUrlInternal = _pOriginal->m_bDispatchUrlInternal;
+ }
+
+
+ void OClickableImageBaseModel::implInitializeImageURL( )
+ {
+ osl_atomic_increment( &m_refCount );
+ {
+ // simulate a propertyChanged event for the ImageURL
+ Any aImageURL;
+ getFastPropertyValue( aImageURL, PROPERTY_ID_IMAGE_URL );
+ _propertyChanged( PropertyChangeEvent( *this, PROPERTY_IMAGE_URL, false, PROPERTY_ID_IMAGE_URL, Any( ), aImageURL ) );
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+
+ void OClickableImageBaseModel::implConstruct()
+ {
+ m_xProducer = new ImageProducer;
+ m_xProducer->SetDoneHdl( LINK( this, OClickableImageBaseModel, OnImageImportDone ) );
+ osl_atomic_increment( &m_refCount );
+ {
+ if ( m_xAggregateSet.is() )
+ {
+ rtl::Reference<OPropertyChangeMultiplexer> pMultiplexer = new OPropertyChangeMultiplexer( this, m_xAggregateSet );
+ pMultiplexer->addProperty( PROPERTY_IMAGE_URL );
+ }
+ }
+ osl_atomic_decrement(&m_refCount);
+ }
+
+
+ OClickableImageBaseModel::~OClickableImageBaseModel()
+ {
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+ DBG_ASSERT(m_pMedium == nullptr, "OClickableImageBaseModel::~OClickableImageBaseModel : leaving a memory leak ...");
+ // This should be cleaned up at least in the dispose
+
+ }
+
+ // XImageProducer
+
+ void SAL_CALL OClickableImageBaseModel::addConsumer( const Reference< XImageConsumer >& _rxConsumer )
+ {
+ ImageModelMethodGuard aGuard( *this );
+ GetImageProducer()->addConsumer( _rxConsumer );
+ }
+
+
+ void SAL_CALL OClickableImageBaseModel::removeConsumer( const Reference< XImageConsumer >& _rxConsumer )
+ {
+ ImageModelMethodGuard aGuard( *this );
+ GetImageProducer()->removeConsumer( _rxConsumer );
+ }
+
+
+ void SAL_CALL OClickableImageBaseModel::startProduction( )
+ {
+ ImageModelMethodGuard aGuard( *this );
+ GetImageProducer()->startProduction();
+ }
+
+
+ Reference< submission::XSubmission > SAL_CALL OClickableImageBaseModel::getSubmission()
+ {
+ return m_xSubmissionDelegate;
+ }
+
+
+ void SAL_CALL OClickableImageBaseModel::setSubmission( const Reference< submission::XSubmission >& _submission )
+ {
+ m_xSubmissionDelegate = _submission;
+ }
+
+
+ Sequence< OUString > SAL_CALL OClickableImageBaseModel::getSupportedServiceNames( )
+ {
+ Sequence< OUString > aSupported = OControlModel::getSupportedServiceNames();
+ aSupported.realloc( aSupported.getLength() + 1 );
+
+ OUString* pArray = aSupported.getArray();
+ pArray[ aSupported.getLength() - 1 ] = FRM_SUN_COMPONENT_SUBMITBUTTON;
+
+ return aSupported;
+ }
+
+ // OComponentHelper
+
+ void OClickableImageBaseModel::disposing()
+ {
+ OControlModel::disposing();
+ m_pMedium.reset();
+ m_xProducer.clear();
+ }
+
+
+ Any SAL_CALL OClickableImageBaseModel::queryAggregation(const Type& _rType)
+ {
+ // order matters:
+ // we definitely want to "override" the XImageProducer interface of our aggregate,
+ // thus check OClickableImageBaseModel_Base (which provides this) first
+ Any aReturn = OClickableImageBaseModel_Base::queryInterface( _rType );
+
+ // BUT: _don't_ let it feel responsible for the XTypeProvider interface
+ // (as this is implemented by our base class in the proper way)
+ if ( _rType.equals( cppu::UnoType<XTypeProvider>::get() )
+ || !aReturn.hasValue()
+ )
+ aReturn = OControlModel::queryAggregation( _rType );
+
+ return aReturn;
+ }
+
+
+ void OClickableImageBaseModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const
+ {
+ switch (nHandle)
+ {
+ case PROPERTY_ID_BUTTONTYPE : rValue <<= m_eButtonType; break;
+ case PROPERTY_ID_TARGET_URL : rValue <<= m_sTargetURL; break;
+ case PROPERTY_ID_TARGET_FRAME : rValue <<= m_sTargetFrame; break;
+ case PROPERTY_ID_DISPATCHURLINTERNAL : rValue <<= m_bDispatchUrlInternal; break;
+ default:
+ OControlModel::getFastPropertyValue(rValue, nHandle);
+ }
+ }
+
+
+ void OClickableImageBaseModel::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue)
+ {
+ switch (nHandle)
+ {
+ case PROPERTY_ID_BUTTONTYPE :
+ DBG_ASSERT(rValue.has<FormButtonType>(), "OClickableImageBaseModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ rValue >>= m_eButtonType;
+ break;
+
+ case PROPERTY_ID_TARGET_URL :
+ DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_STRING, "OClickableImageBaseModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ rValue >>= m_sTargetURL;
+ break;
+
+ case PROPERTY_ID_TARGET_FRAME :
+ DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_STRING, "OClickableImageBaseModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ rValue >>= m_sTargetFrame;
+ break;
+
+ case PROPERTY_ID_DISPATCHURLINTERNAL:
+ DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_BOOLEAN, "OClickableImageBaseModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ rValue >>= m_bDispatchUrlInternal;
+ break;
+
+ default:
+ OControlModel::setFastPropertyValue_NoBroadcast(nHandle, rValue);
+ }
+ }
+
+
+ sal_Bool OClickableImageBaseModel::convertFastPropertyValue(Any& rConvertedValue, Any& rOldValue, sal_Int32 nHandle, const Any& rValue)
+ {
+ switch (nHandle)
+ {
+ case PROPERTY_ID_BUTTONTYPE :
+ return tryPropertyValueEnum( rConvertedValue, rOldValue, rValue, m_eButtonType );
+
+ case PROPERTY_ID_TARGET_URL :
+ return tryPropertyValue(rConvertedValue, rOldValue, rValue, m_sTargetURL);
+
+ case PROPERTY_ID_TARGET_FRAME :
+ return tryPropertyValue(rConvertedValue, rOldValue, rValue, m_sTargetFrame);
+
+ case PROPERTY_ID_DISPATCHURLINTERNAL :
+ return tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bDispatchUrlInternal);
+
+ default:
+ return OControlModel::convertFastPropertyValue(rConvertedValue, rOldValue, nHandle, rValue);
+ }
+ }
+
+
+ void OClickableImageBaseModel::StartProduction()
+ {
+ ImageProducer *pImgProd = GetImageProducer();
+ // grab the ImageURL
+ OUString sURL;
+ getPropertyValue("ImageURL") >>= sURL;
+ if (!m_pMedium)
+ {
+ if ( ::svt::GraphicAccess::isSupportedURL( sURL ) )
+ pImgProd->SetImage( sURL );
+ else
+ // caution: the medium may be NULL if somebody gave us an invalid URL to work with
+ pImgProd->SetImage(OUString());
+ return;
+ }
+ if (m_pMedium->GetErrorCode()==ERRCODE_NONE)
+ {
+ SvStream* pStream = m_pMedium->GetInStream();
+
+ pImgProd->SetImage(*pStream);
+ pImgProd->startProduction();
+ m_bProdStarted = true;
+ }
+ else
+ {
+ pImgProd->SetImage(OUString());
+ m_pMedium.reset();
+ }
+ }
+
+ SfxObjectShell* OClickableImageBaseModel::GetObjectShell()
+ {
+ // Find the XModel to get to the Object shell or at least the
+ // Referer.
+ // There's only a Model if we load HTML documents and the URL is
+ // changed in a document that is already loaded. There's no way
+ // we can get to the Model during loading.
+ Reference< XModel > xModel;
+ css::uno::Reference<css::uno::XInterface> xIfc( *this );
+ while( !xModel.is() && xIfc.is() )
+ {
+ Reference<XChild> xChild( xIfc, UNO_QUERY );
+ xIfc = xChild->getParent();
+ xModel.set(xIfc, css::uno::UNO_QUERY);
+ }
+
+ // Search for the Object shell by iterating over all Object shells
+ // and comparing their XModel to ours.
+ // As an optimization, we try the current Object shell first.
+ SfxObjectShell *pObjSh = nullptr;
+
+ if( xModel.is() )
+ {
+ SfxObjectShell *pTestObjSh = SfxObjectShell::Current();
+ if( pTestObjSh )
+ {
+ Reference< XModel > xTestModel = pTestObjSh->GetModel();
+ if( xTestModel == xModel )
+ pObjSh = pTestObjSh;
+ }
+ if( !pObjSh )
+ {
+ pTestObjSh = SfxObjectShell::GetFirst();
+ while( !pObjSh && pTestObjSh )
+ {
+ Reference< XModel > xTestModel = pTestObjSh->GetModel();
+ if( xTestModel == xModel )
+ pObjSh = pTestObjSh;
+ else
+ pTestObjSh = SfxObjectShell::GetNext( *pTestObjSh );
+ }
+ }
+ }
+
+ return pObjSh;
+ }
+
+ void OClickableImageBaseModel::SetURL( const OUString& rURL )
+ {
+ if (m_pMedium || rURL.isEmpty())
+ {
+ // Free the stream at the Producer, before the medium is deleted
+ GetImageProducer()->SetImage(OUString());
+ m_pMedium.reset();
+ }
+
+ // the SfxMedium is not allowed to be created with an invalid URL, so we have to check this first
+ INetURLObject aUrl(rURL);
+ if (INetProtocol::NotValid == aUrl.GetProtocol())
+ // we treat an invalid URL like we would treat no URL
+ return;
+
+ if (!rURL.isEmpty() && !::svt::GraphicAccess::isSupportedURL( rURL ) )
+ {
+ m_pMedium.reset(new SfxMedium(rURL, StreamMode::STD_READ));
+
+ SfxObjectShell *pObjSh = GetObjectShell();
+
+ if( pObjSh )
+ {
+ // Transfer target frame, so that javascript: URLs
+ // can also be "loaded"
+ const SfxMedium *pShMedium = pObjSh->GetMedium();
+ if( pShMedium )
+ m_pMedium->SetLoadTargetFrame(pShMedium->GetLoadTargetFrame());
+ }
+
+ m_bProdStarted = false;
+
+ // Kick off download (caution: can be synchronous).
+ m_pMedium->Download(LINK(this, OClickableImageBaseModel, DownloadDoneLink));
+ }
+ else
+ {
+ if ( ::svt::GraphicAccess::isSupportedURL( rURL ) )
+ GetImageProducer()->SetImage( rURL );
+ GetImageProducer()->startProduction();
+ }
+ }
+
+
+ void OClickableImageBaseModel::DataAvailable()
+ {
+ if (!m_bProdStarted)
+ StartProduction();
+
+ GetImageProducer()->NewDataAvailable();
+ }
+
+
+ IMPL_LINK_NOARG( OClickableImageBaseModel, DownloadDoneLink, void*, void )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ DataAvailable();
+ }
+
+
+ void OClickableImageBaseModel::_propertyChanged( const PropertyChangeEvent& rEvt )
+ {
+ // If a URL was set, it needs to be passed onto the ImageProducer.
+ ::osl::MutexGuard aGuard(m_aMutex);
+ SetURL( getString(rEvt.NewValue) );
+ }
+
+
+ Any OClickableImageBaseModel::getPropertyDefaultByHandle( sal_Int32 nHandle ) const
+ {
+ switch (nHandle)
+ {
+ case PROPERTY_ID_BUTTONTYPE : return Any( FormButtonType_PUSH );
+ case PROPERTY_ID_TARGET_URL :
+ case PROPERTY_ID_TARGET_FRAME : return Any( OUString() );
+ case PROPERTY_ID_DISPATCHURLINTERNAL : return Any( false );
+ default:
+ return OControlModel::getPropertyDefaultByHandle(nHandle);
+ }
+ }
+
+ IMPL_LINK( OClickableImageBaseModel, OnImageImportDone, Graphic*, i_pGraphic, void )
+ {
+ const Reference< XGraphic > xGraphic( i_pGraphic != nullptr ? Graphic(i_pGraphic->GetBitmapEx()).GetXGraphic() : nullptr );
+ if ( !xGraphic.is() )
+ {
+ m_xGraphicObject.clear();
+ }
+ else
+ {
+ m_xGraphicObject = css::graphic::GraphicObject::create( m_xContext );
+ m_xGraphicObject->setGraphic( xGraphic );
+ }
+ }
+
+
+ // OImageProducerThread_Impl
+
+ void OImageProducerThread_Impl::processEvent( ::cppu::OComponentHelper *pCompImpl,
+ const EventObject* pEvt,
+ const Reference<XControl>&,
+ bool )
+ {
+ static_cast<OClickableImageBaseControl *>(pCompImpl)->actionPerformed_Impl( true, *static_cast<const MouseEvent *>(pEvt) );
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/clickableimage.hxx b/forms/source/component/clickableimage.hxx
new file mode 100644
index 000000000..48b511c86
--- /dev/null
+++ b/forms/source/component/clickableimage.hxx
@@ -0,0 +1,283 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <FormComponent.hxx>
+#include "EventThread.hxx"
+#include "imgprod.hxx"
+#include <tools/link.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/propmultiplex.hxx>
+#include <com/sun/star/awt/XActionListener.hpp>
+#include <com/sun/star/form/XImageProducerSupplier.hpp>
+#include <com/sun/star/form/FormButtonType.hpp>
+#include <com/sun/star/form/XApproveActionListener.hpp>
+#include <com/sun/star/form/XApproveActionBroadcaster.hpp>
+#include <com/sun/star/form/submission/XSubmissionSupplier.hpp>
+#include <com/sun/star/form/submission/XSubmission.hpp>
+#include <com/sun/star/frame/XDispatchProviderInterception.hpp>
+#include <com/sun/star/graphic/XGraphicObject.hpp>
+#include <cppuhelper/implbase3.hxx>
+
+class SfxMedium;
+class SfxObjectShell;
+
+namespace frm
+{
+
+
+ class OImageProducerThread_Impl;
+ class ControlFeatureInterception;
+
+ // OClickableImageBaseModel
+
+ typedef ::cppu::ImplHelper3 < css::form::XImageProducerSupplier
+ , css::awt::XImageProducer
+ , css::form::submission::XSubmissionSupplier
+ > OClickableImageBaseModel_Base;
+
+ class OClickableImageBaseModel :public OClickableImageBaseModel_Base
+ ,public OControlModel
+ ,public OPropertyChangeListener
+ {
+ protected:
+ css::form::FormButtonType m_eButtonType; // Type of the button (push, submit, reset)
+ OUString m_sTargetURL; // URL for the URL button
+ OUString m_sTargetFrame; // TargetFrame to open
+
+ // ImageProducer stuff
+ // Store the image in a graphic object to make it accessible via graphic cache using graphic ID.
+ css::uno::Reference< css::graphic::XGraphicObject > m_xGraphicObject;
+ std::unique_ptr<SfxMedium> m_pMedium; // Download medium
+ rtl::Reference<ImageProducer> m_xProducer;
+ bool m_bDispatchUrlInternal; // property: is not allowed to set : 1
+ bool m_bProdStarted : 1;
+
+ // XSubmission stuff
+ css::uno::Reference< css::form::submission::XSubmission >
+ m_xSubmissionDelegate;
+
+ DECL_LINK( DownloadDoneLink, void*, void );
+
+ ImageProducer* GetImageProducer() { return m_xProducer.get(); }
+
+ void StartProduction();
+ void SetURL(const OUString& rURL);
+ void DataAvailable();
+
+ css::uno::Sequence< css::uno::Type> _getTypes() override;
+ bool isDispatchUrlInternal() const { return m_bDispatchUrlInternal; }
+ void setDispatchUrlInternal(bool _bDispatch) { m_bDispatchUrlInternal = _bDispatch; }
+
+ public:
+ OClickableImageBaseModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory,
+ const OUString& _rUnoControlModelTypeName,
+ const OUString& _rDefault
+ );
+
+ OClickableImageBaseModel (
+ const OClickableImageBaseModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+
+ virtual ~OClickableImageBaseModel() override;
+
+ // UNO Binding
+ DECLARE_UNO3_AGG_DEFAULTS(OClickableImageBaseModel, OControlModel)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ protected:
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // css::form::XImageProducerSupplier
+ virtual css::uno::Reference< css::awt::XImageProducer> SAL_CALL getImageProducer() override { return m_xProducer; }
+
+ // OPropertySetHelper
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle ) const override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const css::uno::Any& rValue) override;
+
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(css::uno::Any& rConvertedValue, css::uno::Any& rOldValue, sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+
+ using ::cppu::OPropertySetHelper::getFastPropertyValue;
+
+ // OPropertyChangeListener
+ virtual void _propertyChanged(const css::beans::PropertyChangeEvent&) override;
+
+ // XPropertyState
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 nHandle ) const override;
+
+ // XImageProducer
+ virtual void SAL_CALL addConsumer( const css::uno::Reference< css::awt::XImageConsumer >& xConsumer ) override;
+ virtual void SAL_CALL removeConsumer( const css::uno::Reference< css::awt::XImageConsumer >& xConsumer ) override;
+ virtual void SAL_CALL startProduction( ) override;
+
+ // XSubmissionSupplier
+ virtual css::uno::Reference< css::form::submission::XSubmission > SAL_CALL getSubmission() override;
+ virtual void SAL_CALL setSubmission( const css::uno::Reference< css::form::submission::XSubmission >& _submission ) override;
+
+ // XServiceInfo
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XEventListener
+ using OControlModel::disposing;
+
+ public:
+ struct GuardAccess { friend class ImageModelMethodGuard; private: GuardAccess() { } };
+ ::osl::Mutex& getMutex( GuardAccess ) { return m_aMutex; }
+ ImageProducer* getImageProducer( GuardAccess ) { return m_xProducer.get(); }
+
+ protected:
+ using OControlModel::getMutex;
+
+ void implConstruct();
+
+ // to be called from within the cloning-ctor of your derived class
+ void implInitializeImageURL( );
+
+ SfxObjectShell* GetObjectShell();
+
+ DECL_LINK( OnImageImportDone, ::Graphic*, void );
+ };
+
+ class ImageModelMethodGuard : public ::osl::MutexGuard
+ {
+ public:
+ explicit ImageModelMethodGuard( OClickableImageBaseModel& _rModel )
+ : ::osl::MutexGuard( _rModel.getMutex( OClickableImageBaseModel::GuardAccess() ) )
+ {
+ if ( nullptr == _rModel.getImageProducer( OClickableImageBaseModel::GuardAccess() ) )
+ throw css::lang::DisposedException(
+ OUString(),
+ static_cast< css::form::XImageProducerSupplier* >( &_rModel )
+ );
+ }
+ };
+
+
+ // OClickableImageBaseControl
+
+ typedef ::cppu::ImplHelper3 < css::form::XApproveActionBroadcaster
+ , css::form::submission::XSubmission
+ , css::frame::XDispatchProviderInterception
+ > OClickableImageBaseControl_BASE;
+
+ class OClickableImageBaseControl :public OClickableImageBaseControl_BASE
+ ,public OControl
+ {
+ friend class OImageProducerThread_Impl;
+
+ private:
+ rtl::Reference<OImageProducerThread_Impl> m_pThread;
+ ::comphelper::OInterfaceContainerHelper3<css::form::submission::XSubmissionVetoListener>
+ m_aSubmissionVetoListeners;
+ ::std::unique_ptr< ControlFeatureInterception >
+ m_pFeatureInterception;
+
+ protected:
+ ::comphelper::OInterfaceContainerHelper3<css::form::XApproveActionListener> m_aApproveActionListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::awt::XActionListener> m_aActionListeners;
+ OUString m_aActionCommand;
+
+ // XSubmission
+ virtual void SAL_CALL submit( ) override;
+ virtual void SAL_CALL submitWithInteraction( const css::uno::Reference< css::task::XInteractionHandler >& aHandler ) override;
+ virtual void SAL_CALL addSubmissionVetoListener( const css::uno::Reference< css::form::submission::XSubmissionVetoListener >& listener ) override;
+ virtual void SAL_CALL removeSubmissionVetoListener( const css::uno::Reference< css::form::submission::XSubmissionVetoListener >& listener ) override;
+
+ // XServiceInfo
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XEventListener
+ using OControl::disposing;
+
+ public:
+ OClickableImageBaseControl(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory,
+ const OUString& _aService);
+ virtual ~OClickableImageBaseControl() override;
+
+ protected:
+ // UNO Binding
+ DECLARE_UNO3_AGG_DEFAULTS(OClickableImageBaseControl, OControl)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // css::form::XApproveActionBroadcaster
+ virtual void SAL_CALL addApproveActionListener(const css::uno::Reference< css::form::XApproveActionListener>& _rxListener) override;
+ virtual void SAL_CALL removeApproveActionListener(const css::uno::Reference< css::form::XApproveActionListener>& _rxListener) override;
+
+ // XDispatchProviderInterception
+ virtual void SAL_CALL registerDispatchProviderInterceptor( const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& Interceptor ) override;
+ virtual void SAL_CALL releaseDispatchProviderInterceptor( const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& Interceptor ) override;
+
+ protected:
+ virtual void actionPerformed_Impl( bool bNotifyListener, const css::awt::MouseEvent& rEvt );
+
+ css::uno::Sequence< css::uno::Type > _getTypes() override;
+
+ /** approves the action by calling the approve listeners
+ @return <TRUE/> if and only if the action has <em>not</em> been cancelled by a listener
+ */
+ bool approveAction( );
+
+ /** retrieves (and if necessary creates) the image producer thread.
+
+ Must be called with our mutex locked
+ */
+ OImageProducerThread_Impl* getImageProducerThread();
+
+ private:
+ void implSubmit(
+ const css::awt::MouseEvent& _rEvent,
+ const css::uno::Reference< css::task::XInteractionHandler >& aHandler
+ );
+ };
+
+ class OImageProducerThread_Impl: public OComponentEventThread
+ {
+ protected:
+
+ // Process an Event.
+ // The mutex is not locked, pCompImpl stays valid in any case
+ virtual void processEvent( ::cppu::OComponentHelper *pCompImpl,
+ const css::lang::EventObject*,
+ const css::uno::Reference< css::awt::XControl>&,
+ bool ) override;
+
+ public:
+ explicit OImageProducerThread_Impl( OClickableImageBaseControl *pControl ) :
+ OComponentEventThread( pControl )
+ {}
+
+ void addEvent() { OComponentEventThread::addEvent( std::make_unique<css::lang::EventObject>() ); }
+
+ protected:
+ using OComponentEventThread::addEvent;
+ };
+
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/cloneable.cxx b/forms/source/component/cloneable.cxx
new file mode 100644
index 000000000..9770019c2
--- /dev/null
+++ b/forms/source/component/cloneable.cxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cloneable.hxx>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <comphelper/uno3.hxx>
+#include <tools/debug.hxx>
+
+
+namespace frm
+{
+
+
+ using namespace ::comphelper;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::util;
+
+
+ //= OCloneableAggregation
+
+
+ Reference< XAggregation > OCloneableAggregation::createAggregateClone( const OCloneableAggregation* _pOriginal )
+ {
+ Reference< XCloneable > xAggregateCloneable; // will be the aggregate's XCloneable
+ Reference< XAggregation > xAggregateClone; // will be the aggregate's clone
+
+ if ( query_aggregation( _pOriginal->m_xAggregate, xAggregateCloneable ) )
+ {
+ xAggregateClone.set(xAggregateCloneable->createClone(), css::uno::UNO_QUERY);
+ DBG_ASSERT( xAggregateClone.is(), "OCloneableAggregation::createAggregateClone: invalid clone returned by the aggregate!" );
+ }
+ else {
+ DBG_ASSERT( !_pOriginal->m_xAggregate.is(), "OCloneableAggregation::createAggregateClone: aggregate is not cloneable!" );
+ }
+
+ return xAggregateClone;
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/entrylisthelper.cxx b/forms/source/component/entrylisthelper.cxx
new file mode 100644
index 000000000..0f7810763
--- /dev/null
+++ b/forms/source/component/entrylisthelper.cxx
@@ -0,0 +1,325 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "entrylisthelper.hxx"
+#include <FormComponent.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/sequence.hxx>
+#include <comphelper/property.hxx>
+#include <com/sun/star/form/binding/XListEntryTypedSource.hpp>
+#include <algorithm>
+
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::form::binding;
+
+ OEntryListHelper::OEntryListHelper( OControlModel& _rControlModel )
+ :m_rControlModel( _rControlModel )
+ ,m_aRefreshListeners( _rControlModel.getInstanceMutex() )
+ {
+ }
+
+
+ OEntryListHelper::OEntryListHelper( const OEntryListHelper& _rSource, OControlModel& _rControlModel )
+ :m_rControlModel( _rControlModel )
+ ,m_xListSource ( _rSource.m_xListSource )
+ ,m_aStringItems( _rSource.m_aStringItems )
+ ,m_aRefreshListeners( _rControlModel.getInstanceMutex() )
+ {
+ }
+
+
+ OEntryListHelper::~OEntryListHelper( )
+ {
+ }
+
+
+ void SAL_CALL OEntryListHelper::setListEntrySource( const Reference< XListEntrySource >& _rxSource )
+ {
+ ControlModelLock aLock( m_rControlModel );
+
+ // disconnect from the current external list source
+ disconnectExternalListSource();
+
+ // and connect to the new one
+ if ( _rxSource.is() )
+ connectExternalListSource( _rxSource, aLock );
+ }
+
+
+ Reference< XListEntrySource > SAL_CALL OEntryListHelper::getListEntrySource( )
+ {
+ return m_xListSource;
+ }
+
+
+ void SAL_CALL OEntryListHelper::entryChanged( const ListEntryEvent& _rEvent )
+ {
+ ControlModelLock aLock( m_rControlModel );
+
+ OSL_ENSURE( _rEvent.Source == m_xListSource,
+ "OEntryListHelper::entryChanged: where did this come from?" );
+ OSL_ENSURE( ( _rEvent.Position >= 0 ) && ( o3tl::make_unsigned(_rEvent.Position) < m_aStringItems.size() ),
+ "OEntryListHelper::entryChanged: invalid index!" );
+ OSL_ENSURE( _rEvent.Entries.getLength() == 1,
+ "OEntryListHelper::entryChanged: invalid string list!" );
+
+ if ( ( _rEvent.Position >= 0 )
+ && ( o3tl::make_unsigned(_rEvent.Position) < m_aStringItems.size() )
+ && _rEvent.Entries.hasElements()
+ )
+ {
+ m_aStringItems[ _rEvent.Position ] = _rEvent.Entries[ 0 ];
+ if (m_aTypedItems.hasElements())
+ m_aTypedItems = Sequence<Any>(); // doesn't match anymore
+ stringItemListChanged( aLock );
+ }
+ }
+
+
+ void SAL_CALL OEntryListHelper::entryRangeInserted( const ListEntryEvent& _rEvent )
+ {
+ ControlModelLock aLock( m_rControlModel );
+
+ OSL_ENSURE( _rEvent.Source == m_xListSource,
+ "OEntryListHelper::entryRangeInserted: where did this come from?" );
+ OSL_ENSURE( ( _rEvent.Position > 0 ) && ( o3tl::make_unsigned(_rEvent.Position) < m_aStringItems.size() ) && _rEvent.Entries.hasElements(),
+ "OEntryListHelper::entryRangeRemoved: invalid count and/or position!" );
+
+ if ( ( _rEvent.Position > 0 )
+ && ( o3tl::make_unsigned(_rEvent.Position) < m_aStringItems.size() )
+ && _rEvent.Entries.hasElements()
+ )
+ {
+ m_aStringItems.insert(m_aStringItems.begin() + _rEvent.Position, _rEvent.Entries.begin(), _rEvent.Entries.end());
+ if (m_aTypedItems.hasElements())
+ m_aTypedItems = Sequence<Any>(); // doesn't match anymore
+ stringItemListChanged( aLock );
+ }
+ }
+
+
+ void SAL_CALL OEntryListHelper::entryRangeRemoved( const ListEntryEvent& _rEvent )
+ {
+ ControlModelLock aLock( m_rControlModel );
+
+ OSL_ENSURE( _rEvent.Source == m_xListSource,
+ "OEntryListHelper::entryRangeRemoved: where did this come from?" );
+ OSL_ENSURE( ( _rEvent.Position > 0 ) && ( _rEvent.Count > 0 ) && ( _rEvent.Position + _rEvent.Count <= static_cast<sal_Int32>(m_aStringItems.size()) ),
+ "OEntryListHelper::entryRangeRemoved: invalid count and/or position!" );
+
+ if ( !(( _rEvent.Position > 0 )
+ && ( _rEvent.Count > 0 )
+ && ( _rEvent.Position + _rEvent.Count <= static_cast<sal_Int32>(m_aStringItems.size()) ))
+ )
+ return;
+
+ m_aStringItems.erase(m_aStringItems.begin() + _rEvent.Position,
+ m_aStringItems.begin() + _rEvent.Position + _rEvent.Count );
+ if (_rEvent.Position + _rEvent.Count <= m_aTypedItems.getLength())
+ {
+ Sequence<Any> aTmp( m_aTypedItems.getLength() - _rEvent.Count );
+ auto aTmpRange = asNonConstRange(aTmp);
+ sal_Int32 nStop = _rEvent.Position;
+ sal_Int32 i = 0;
+ for ( ; i < nStop; ++i)
+ {
+ aTmpRange[i] = m_aTypedItems[i];
+ }
+ nStop = aTmp.getLength();
+ for (sal_Int32 j = _rEvent.Position + _rEvent.Count; i < nStop; ++i, ++j)
+ {
+ aTmpRange[i] = m_aTypedItems[j];
+ }
+ m_aTypedItems = aTmp;
+ }
+ else if (m_aTypedItems.hasElements())
+ {
+ m_aTypedItems = Sequence<Any>(); // doesn't match anymore
+ }
+ stringItemListChanged( aLock );
+ }
+
+
+ void SAL_CALL OEntryListHelper::allEntriesChanged( const EventObject& _rEvent )
+ {
+ ControlModelLock aLock( m_rControlModel );
+
+ OSL_ENSURE( _rEvent.Source == m_xListSource,
+ "OEntryListHelper::allEntriesChanged: where did this come from?" );
+
+ if ( _rEvent.Source == m_xListSource )
+ {
+ impl_lock_refreshList( aLock );
+ }
+ }
+
+ // XRefreshable
+
+ void SAL_CALL OEntryListHelper::addRefreshListener(const Reference<XRefreshListener>& _rxListener)
+ {
+ if ( _rxListener.is() )
+ m_aRefreshListeners.addInterface( _rxListener );
+ }
+
+
+ void SAL_CALL OEntryListHelper::removeRefreshListener(const Reference<XRefreshListener>& _rxListener)
+ {
+ if ( _rxListener.is() )
+ m_aRefreshListeners.removeInterface( _rxListener );
+ }
+
+
+ void SAL_CALL OEntryListHelper::refresh()
+ {
+ {
+ ControlModelLock aLock( m_rControlModel );
+ impl_lock_refreshList( aLock );
+ }
+
+ EventObject aEvt( static_cast< XRefreshable* >( this ) );
+ m_aRefreshListeners.notifyEach( &XRefreshListener::refreshed, aEvt );
+ }
+
+
+ void OEntryListHelper::impl_lock_refreshList( ControlModelLock& _rInstanceLock )
+ {
+ if ( hasExternalListSource() )
+ obtainListSourceEntries( _rInstanceLock );
+ else
+ refreshInternalEntryList();
+ }
+
+
+ bool OEntryListHelper::handleDisposing( const EventObject& _rEvent )
+ {
+ if ( m_xListSource .is() && ( _rEvent.Source == m_xListSource ) )
+ {
+ disconnectExternalListSource( );
+ return true;
+ }
+ return false;
+ }
+
+
+ void OEntryListHelper::disposing( )
+ {
+ EventObject aEvt( static_cast< XRefreshable* >( this ) );
+ m_aRefreshListeners.disposeAndClear(aEvt);
+
+ if ( hasExternalListSource( ) )
+ disconnectExternalListSource( );
+ }
+
+
+ void OEntryListHelper::disconnectExternalListSource( )
+ {
+ if ( m_xListSource.is() )
+ m_xListSource->removeListEntryListener( this );
+
+ m_xListSource.clear();
+ }
+
+
+ void OEntryListHelper::connectExternalListSource( const Reference< XListEntrySource >& _rxSource, ControlModelLock& _rInstanceLock )
+ {
+ OSL_ENSURE( !hasExternalListSource(), "OEntryListHelper::connectExternalListSource: only to be called if no external source is active!" );
+ OSL_ENSURE( _rxSource.is(), "OEntryListHelper::connectExternalListSource: invalid list source!" );
+
+ // remember it
+ m_xListSource = _rxSource;
+
+ // initially fill our item list
+ if ( m_xListSource.is() )
+ {
+ // be notified when the list changes ...
+ m_xListSource->addListEntryListener( this );
+
+ obtainListSourceEntries( _rInstanceLock );
+ }
+ }
+
+
+ void OEntryListHelper::obtainListSourceEntries( ControlModelLock& _rInstanceLock )
+ {
+ Reference< XListEntryTypedSource > xTyped;
+ xTyped.set( m_xListSource, UNO_QUERY);
+ if (xTyped.is())
+ {
+ comphelper::sequenceToContainer( m_aStringItems, xTyped->getAllListEntriesTyped( m_aTypedItems));
+ }
+ else
+ {
+ comphelper::sequenceToContainer( m_aStringItems, m_xListSource->getAllListEntries());
+ if (m_aTypedItems.hasElements())
+ m_aTypedItems = Sequence<Any>();
+ }
+ stringItemListChanged( _rInstanceLock );
+ }
+
+
+ bool OEntryListHelper::convertNewListSourceProperty( Any& _rConvertedValue,
+ Any& _rOldValue, const Any& _rValue )
+ {
+ if ( hasExternalListSource() )
+ throw IllegalArgumentException( );
+ // TODO: error message
+
+ return ::comphelper::tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, comphelper::containerToSequence(m_aStringItems) );
+ }
+
+
+ void OEntryListHelper::setNewStringItemList( const css::uno::Any& _rValue, ControlModelLock& _rInstanceLock )
+ {
+ OSL_PRECOND( !hasExternalListSource(), "OEntryListHelper::setNewStringItemList: this should never have survived convertNewListSourceProperty!" );
+ css::uno::Sequence<OUString> aTmp;
+ OSL_VERIFY( _rValue >>= aTmp );
+ comphelper::sequenceToContainer(m_aStringItems, aTmp);
+ if (m_aTypedItems.hasElements())
+ m_aTypedItems = Sequence<Any>(); // doesn't match anymore
+ stringItemListChanged( _rInstanceLock );
+ }
+
+
+ void OEntryListHelper::setNewTypedItemList( const css::uno::Any& _rValue, ControlModelLock& _rInstanceLock )
+ {
+ OSL_PRECOND( !hasExternalListSource(), "OEntryListHelper::setNewTypedItemList: this should never have survived convertNewListSourceProperty!" );
+ if (!(_rValue >>= m_aTypedItems ))
+ {
+ OSL_VERIFY(false);
+ if (m_aTypedItems.hasElements())
+ m_aTypedItems = Sequence<Any>(); // doesn't match anymore
+ }
+ // Sets both properties, assuming that TypedItemList belongs to StringItemList.
+ stringItemListChanged( _rInstanceLock );
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/entrylisthelper.hxx b/forms/source/component/entrylisthelper.hxx
new file mode 100644
index 000000000..cb0e0789a
--- /dev/null
+++ b/forms/source/component/entrylisthelper.hxx
@@ -0,0 +1,196 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/form/binding/XListEntrySink.hpp>
+#include <com/sun/star/util/XRefreshable.hpp>
+#include <com/sun/star/form/binding/XListEntryListener.hpp>
+
+#include <cppuhelper/implbase3.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+
+
+namespace frm
+{
+
+
+ class OControlModel;
+ class ControlModelLock;
+
+
+ //= OEntryListHelper
+
+ typedef ::cppu::ImplHelper3 < css::form::binding::XListEntrySink
+ , css::form::binding::XListEntryListener
+ , css::util::XRefreshable
+ > OEntryListHelper_BASE;
+
+ class OEntryListHelper : public OEntryListHelper_BASE
+ {
+ private:
+ OControlModel& m_rControlModel;
+
+ css::uno::Reference< css::form::binding::XListEntrySource >
+ m_xListSource; /// our external list source
+ std::vector< OUString >
+ m_aStringItems; /// "overridden" StringItemList property value
+ css::uno::Sequence< css::uno::Any >
+ m_aTypedItems; /// "overridden" TypedItemList property value
+ ::comphelper::OInterfaceContainerHelper3<css::util::XRefreshListener>
+ m_aRefreshListeners;
+
+
+ protected:
+ explicit OEntryListHelper( OControlModel& _rControlModel );
+ OEntryListHelper( const OEntryListHelper& _rSource, OControlModel& _rControlModel );
+ virtual ~OEntryListHelper( );
+
+ /// returns the current string item list
+ const std::vector< OUString >&
+ getStringItemList() const { return m_aStringItems; }
+
+ /// returns the current typed item list
+ const css::uno::Sequence< css::uno::Any >&
+ getTypedItemList() const { return m_aTypedItems; }
+
+ /// determines whether we actually have an external list source
+ bool hasExternalListSource( ) const { return m_xListSource.is(); }
+
+ /** handling the XEventListener::disposing call for the case where
+ our list source is being disposed
+ @return
+ <TRUE/> if and only if the disposed object was our list source, and so the
+ event was handled
+ */
+ bool handleDisposing( const css::lang::EventObject& _rEvent );
+
+ /** to be called by derived classes' instances when they're being disposed
+ */
+ void disposing( );
+
+ // prevent method hiding
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override = 0;
+
+ /** helper for implementing convertFastPropertyValue( StringItemList )
+
+ <p>The signature of this method and the return type have the same semantics
+ as convertFastPropertyValue.</p>
+ */
+ bool convertNewListSourceProperty(
+ css::uno::Any& _rConvertedValue,
+ css::uno::Any& _rOldValue,
+ const css::uno::Any& _rValue
+ );
+
+ /** helper for implementing setFastPropertyValueNoBroadcast
+
+ <p>Will internally call stringItemListChanged after the new item list
+ has been set.</p>
+
+ @precond
+ not to be called when we have an external list source
+ @see hasExternalListSource
+ */
+ void setNewStringItemList( const css::uno::Any& _rValue, ControlModelLock& _rInstanceLock );
+
+ /** helper for implementing setFastPropertyValueNoBroadcast
+
+ <p>Will internally call stringItemListChanged after the new item list
+ has been set.</p>
+
+ @precond
+ not to be called when we have an external list source
+ @see hasExternalListSource
+ */
+ void setNewTypedItemList( const css::uno::Any& _rValue, ControlModelLock& _rInstanceLock );
+
+ /** announces that the list of entries has changed.
+
+ <p>Derived classes have to override this. Most probably, they'll set the new
+ as model property.</p>
+
+ @pure
+ @see getStringItemList
+ */
+ virtual void stringItemListChanged( ControlModelLock& _rInstanceLock ) = 0;
+
+ /** called when XRefreshable::refresh has been called, and we do *not* have an external
+ list source
+ */
+ virtual void refreshInternalEntryList() = 0;
+
+ private:
+ // XListEntrySink
+ virtual void SAL_CALL setListEntrySource( const css::uno::Reference< css::form::binding::XListEntrySource >& _rxSource ) override;
+ virtual css::uno::Reference< css::form::binding::XListEntrySource > SAL_CALL getListEntrySource( ) override;
+
+ // XListEntryListener
+ virtual void SAL_CALL entryChanged( const css::form::binding::ListEntryEvent& _rSource ) override;
+ virtual void SAL_CALL entryRangeInserted( const css::form::binding::ListEntryEvent& _rSource ) override;
+ virtual void SAL_CALL entryRangeRemoved( const css::form::binding::ListEntryEvent& _rSource ) override;
+ virtual void SAL_CALL allEntriesChanged( const css::lang::EventObject& _rSource ) override;
+
+ // XRefreshable
+ virtual void SAL_CALL refresh() override;
+ virtual void SAL_CALL addRefreshListener(const css::uno::Reference< css::util::XRefreshListener>& _rxListener) override;
+ virtual void SAL_CALL removeRefreshListener(const css::uno::Reference< css::util::XRefreshListener>& _rxListener) override;
+
+ private:
+ /** disconnects from the active external list source, if present
+ @see connectExternalListSource
+ */
+ void disconnectExternalListSource( );
+
+ /** connects to a new external list source
+ @param _rxSource
+ the new list source. Must not be <NULL/>
+ @see disconnectExternalListSource
+ */
+ void connectExternalListSource(
+ const css::uno::Reference< css::form::binding::XListEntrySource >& _rxSource,
+ ControlModelLock& _rInstanceLock
+ );
+
+ /** obtains list entries and possibly data values from list source
+
+ @precond
+ m_xListSource has to hold an external list source
+ */
+ void obtainListSourceEntries( ControlModelLock& _rInstanceLock );
+
+ /** refreshes our list entries
+
+ In case we have an external list source, it's used to obtain the new entries, and then
+ stringItemListChanged is called to give the derived class the possibility to
+ react on this.
+
+ In case we do not have an external list source, refreshInternalEntryList is called.
+ */
+ void impl_lock_refreshList( ControlModelLock& _rInstanceLock );
+
+ private:
+ OEntryListHelper( const OEntryListHelper& ) = delete;
+ OEntryListHelper& operator=( const OEntryListHelper& ) = delete;
+ };
+
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/errorbroadcaster.cxx b/forms/source/component/errorbroadcaster.cxx
new file mode 100644
index 000000000..7f37e4e0e
--- /dev/null
+++ b/forms/source/component/errorbroadcaster.cxx
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "errorbroadcaster.hxx"
+#include <connectivity/dbtools.hxx>
+#include <sal/log.hxx>
+
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::sdbc;
+ using namespace ::com::sun::star::sdb;
+ using namespace ::dbtools;
+
+ OErrorBroadcaster::OErrorBroadcaster( ::cppu::OBroadcastHelper& _rBHelper )
+ :m_rBHelper( _rBHelper )
+ ,m_aErrorListeners( _rBHelper.rMutex )
+ {
+ }
+
+
+ OErrorBroadcaster::~OErrorBroadcaster( )
+ {
+ SAL_WARN_IF( !m_rBHelper.bDisposed && !m_rBHelper.bInDispose, "forms.component",
+ "OErrorBroadcaster::~OErrorBroadcaster: not disposed!" );
+ // herein, we don't have a chance to do the dispose ourself...
+
+ SAL_WARN_IF( m_aErrorListeners.getLength(), "forms.component",
+ "OErrorBroadcaster::~OErrorBroadcaster: still have listeners!" );
+ // either we're not disposed, or the derived class did not call our dispose from within their dispose
+ }
+
+
+ void OErrorBroadcaster::disposing()
+ {
+ EventObject aDisposeEvent( static_cast< XSQLErrorBroadcaster* >( this ) );
+ m_aErrorListeners.disposeAndClear( aDisposeEvent );
+ }
+
+
+ void OErrorBroadcaster::onError( const SQLException& _rException, const OUString& _rContextDescription )
+ {
+ Any aError;
+ if ( !_rContextDescription.isEmpty() )
+ aError <<= prependErrorInfo( _rException, static_cast< XSQLErrorBroadcaster* >( this ), _rContextDescription );
+ else
+ aError <<= _rException;
+
+ onError( SQLErrorEvent( static_cast< XSQLErrorBroadcaster* >( this ), aError ) );
+ }
+
+
+ void OErrorBroadcaster::onError( const css::sdb::SQLErrorEvent& _rError )
+ {
+ m_aErrorListeners.notifyEach( &XSQLErrorListener::errorOccured, _rError );
+ }
+
+
+ void SAL_CALL OErrorBroadcaster::addSQLErrorListener( const Reference< XSQLErrorListener >& _rxListener )
+ {
+ m_aErrorListeners.addInterface( _rxListener );
+ }
+
+
+ void SAL_CALL OErrorBroadcaster::removeSQLErrorListener( const Reference< XSQLErrorListener >& _rxListener )
+ {
+ m_aErrorListeners.removeInterface( _rxListener );
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/errorbroadcaster.hxx b/forms/source/component/errorbroadcaster.hxx
new file mode 100644
index 000000000..17fcb4d0f
--- /dev/null
+++ b/forms/source/component/errorbroadcaster.hxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase1.hxx>
+#include <com/sun/star/sdb/XSQLErrorBroadcaster.hpp>
+#include <cppuhelper/interfacecontainer.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdb/SQLErrorEvent.hpp>
+
+
+namespace frm
+{
+
+ typedef ::cppu::ImplHelper1 < css::sdb::XSQLErrorBroadcaster
+ > OErrorBroadcaster_BASE;
+
+ class OErrorBroadcaster : public OErrorBroadcaster_BASE
+ {
+ private:
+ ::cppu::OBroadcastHelper& m_rBHelper;
+ ::comphelper::OInterfaceContainerHelper3<css::sdb::XSQLErrorListener> m_aErrorListeners;
+
+ protected:
+ explicit OErrorBroadcaster( ::cppu::OBroadcastHelper& _rBHelper );
+ virtual ~OErrorBroadcaster( );
+
+ void disposing();
+
+ void onError( const css::sdbc::SQLException& _rException, const OUString& _rContextDescription );
+ void onError( const css::sdb::SQLErrorEvent& _rException );
+
+ protected:
+ // XSQLErrorBroadcaster
+ virtual void SAL_CALL addSQLErrorListener( const css::uno::Reference< css::sdb::XSQLErrorListener >& _rListener ) override;
+ virtual void SAL_CALL removeSQLErrorListener( const css::uno::Reference< css::sdb::XSQLErrorListener >& _rListener ) override;
+ };
+
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/findpos.cxx b/forms/source/component/findpos.cxx
new file mode 100644
index 000000000..6b2503ac0
--- /dev/null
+++ b/forms/source/component/findpos.cxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sal/config.h>
+
+#include "findpos.hxx"
+
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+
+#include <algorithm>
+
+namespace detail {
+
+sal_Int32 findPos(
+ const OUString& aStr,
+ const css::uno::Sequence< OUString >& rList)
+{
+ const OUString* pStrList = rList.getConstArray();
+ const OUString* pResult = ::std::lower_bound(
+ pStrList, pStrList + rList.getLength(), aStr );
+ if ( ( pResult != pStrList + rList.getLength() ) && ( *pResult == aStr ) )
+ return ( pResult - pStrList );
+
+ return -1;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/findpos.hxx b/forms/source/component/findpos.hxx
new file mode 100644
index 000000000..2a40bc3ca
--- /dev/null
+++ b/forms/source/component/findpos.hxx
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <sal/types.h>
+
+namespace detail
+{
+sal_Int32 findPos(const OUString& aStr, const css::uno::Sequence<OUString>& rList);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/formcontrolfont.cxx b/forms/source/component/formcontrolfont.cxx
new file mode 100644
index 000000000..bbb15303b
--- /dev/null
+++ b/forms/source/component/formcontrolfont.cxx
@@ -0,0 +1,578 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <formcontrolfont.hxx>
+#include <frm_strings.hxx>
+#include <property.hxx>
+#include <cppuhelper/propshlp.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <tools/color.hxx>
+#include <sal/log.hxx>
+#include <toolkit/helper/emptyfontdescriptor.hxx>
+#include <com/sun/star/awt/FontRelief.hpp>
+#include <com/sun/star/awt/FontEmphasisMark.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+
+namespace frm
+{
+
+
+ using namespace ::comphelper;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+
+
+ namespace
+ {
+ Any lcl_extractFontDescriptorAggregate( sal_Int32 _nHandle, const FontDescriptor& _rFont )
+ {
+ Any aValue;
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_FONT_NAME:
+ aValue <<= _rFont.Name;
+ break;
+
+ case PROPERTY_ID_FONT_STYLENAME:
+ aValue <<= _rFont.StyleName;
+ break;
+
+ case PROPERTY_ID_FONT_FAMILY:
+ aValue <<= _rFont.Family;
+ break;
+
+ case PROPERTY_ID_FONT_CHARSET:
+ aValue <<= _rFont.CharSet;
+ break;
+
+ case PROPERTY_ID_FONT_CHARWIDTH:
+ aValue <<= _rFont.CharacterWidth;
+ break;
+
+ case PROPERTY_ID_FONT_KERNING:
+ aValue <<= _rFont.Kerning;
+ break;
+
+ case PROPERTY_ID_FONT_ORIENTATION:
+ aValue <<= _rFont.Orientation;
+ break;
+
+ case PROPERTY_ID_FONT_PITCH:
+ aValue <<= _rFont.Pitch;
+ break;
+
+ case PROPERTY_ID_FONT_TYPE:
+ aValue <<= _rFont.Type;
+ break;
+
+ case PROPERTY_ID_FONT_WIDTH:
+ aValue <<= _rFont.Width;
+ break;
+
+ case PROPERTY_ID_FONT_HEIGHT:
+ aValue <<= static_cast<float>( _rFont.Height );
+ break;
+
+ case PROPERTY_ID_FONT_WEIGHT:
+ aValue <<= _rFont.Weight;
+ break;
+
+ case PROPERTY_ID_FONT_SLANT:
+ aValue <<= _rFont.Slant;
+ break;
+
+ case PROPERTY_ID_FONT_UNDERLINE:
+ aValue <<= _rFont.Underline;
+ break;
+
+ case PROPERTY_ID_FONT_STRIKEOUT:
+ aValue <<= _rFont.Strikeout;
+ break;
+
+ case PROPERTY_ID_FONT_WORDLINEMODE:
+ aValue <<= _rFont.WordLineMode;
+ break;
+
+ default:
+ OSL_FAIL( "lcl_extractFontDescriptorAggregate: invalid handle!" );
+ break;
+ }
+ return aValue;
+ }
+ }
+
+ FontControlModel::FontControlModel( bool _bToolkitCompatibleDefaults )
+ :m_nFontRelief( css::awt::FontRelief::NONE )
+ ,m_nFontEmphasis( css::awt::FontEmphasisMark::NONE )
+ ,m_bToolkitCompatibleDefaults( _bToolkitCompatibleDefaults )
+ {
+ }
+
+
+ FontControlModel::FontControlModel( const FontControlModel* _pOriginal )
+ {
+ m_aFont = _pOriginal->m_aFont;
+ m_nFontRelief = _pOriginal->m_nFontRelief;
+ m_nFontEmphasis = _pOriginal->m_nFontEmphasis;
+ m_aTextLineColor = _pOriginal->m_aTextLineColor;
+ m_aTextColor = _pOriginal->m_aTextColor;
+ m_bToolkitCompatibleDefaults = _pOriginal->m_bToolkitCompatibleDefaults;
+ }
+
+
+ bool FontControlModel::isFontRelatedProperty( sal_Int32 _nPropertyHandle )
+ {
+ return isFontAggregateProperty( _nPropertyHandle )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT )
+ || ( _nPropertyHandle == PROPERTY_ID_FONTEMPHASISMARK )
+ || ( _nPropertyHandle == PROPERTY_ID_FONTRELIEF )
+ || ( _nPropertyHandle == PROPERTY_ID_TEXTLINECOLOR )
+ || ( _nPropertyHandle == PROPERTY_ID_TEXTCOLOR );
+ }
+
+
+ bool FontControlModel::isFontAggregateProperty( sal_Int32 _nPropertyHandle )
+ {
+ return ( _nPropertyHandle == PROPERTY_ID_FONT_CHARWIDTH )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_ORIENTATION )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_WIDTH )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_NAME )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_STYLENAME )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_FAMILY )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_CHARSET )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_HEIGHT )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_WEIGHT )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_SLANT )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_UNDERLINE )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_STRIKEOUT )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_WORDLINEMODE )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_PITCH )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_KERNING )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_TYPE );
+ }
+
+
+ Color FontControlModel::getTextColor( ) const
+ {
+ Color nColor = COL_TRANSPARENT;
+ m_aTextColor >>= nColor;
+ return nColor;
+ }
+
+
+ Color FontControlModel::getTextLineColor( ) const
+ {
+ Color nColor = COL_TRANSPARENT;
+ m_aTextLineColor >>= nColor;
+ return nColor;
+ }
+
+
+ void FontControlModel::describeFontRelatedProperties( Sequence< Property >& /* [out] */ _rProps)
+ {
+ sal_Int32 nPos = _rProps.getLength();
+ _rProps.realloc( nPos + 21 );
+ Property* pProperties = _rProps.getArray();
+
+ *pProperties++ = css::beans::Property(PROPERTY_FONT, PROPERTY_ID_FONT, cppu::UnoType<FontDescriptor>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONTEMPHASISMARK, PROPERTY_ID_FONTEMPHASISMARK, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONTRELIEF, PROPERTY_ID_FONTRELIEF, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_TEXTCOLOR, PROPERTY_ID_TEXTCOLOR, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_TEXTLINECOLOR, PROPERTY_ID_TEXTLINECOLOR, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_CHARWIDTH, PROPERTY_ID_FONT_CHARWIDTH, cppu::UnoType<float>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_KERNING, PROPERTY_ID_FONT_KERNING, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_ORIENTATION, PROPERTY_ID_FONT_ORIENTATION, cppu::UnoType<float>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_PITCH, PROPERTY_ID_FONT_PITCH, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_TYPE, PROPERTY_ID_FONT_TYPE, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_WIDTH, PROPERTY_ID_FONT_WIDTH, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_NAME, PROPERTY_ID_FONT_NAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_STYLENAME, PROPERTY_ID_FONT_STYLENAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_FAMILY, PROPERTY_ID_FONT_FAMILY, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_CHARSET, PROPERTY_ID_FONT_CHARSET, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_HEIGHT, PROPERTY_ID_FONT_HEIGHT, cppu::UnoType<float>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_WEIGHT, PROPERTY_ID_FONT_WEIGHT, cppu::UnoType<float>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_SLANT, PROPERTY_ID_FONT_SLANT, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_UNDERLINE, PROPERTY_ID_FONT_UNDERLINE, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_STRIKEOUT, PROPERTY_ID_FONT_STRIKEOUT, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_WORDLINEMODE, PROPERTY_ID_FONT_WORDLINEMODE, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::MAYBEDEFAULT);
+ }
+
+
+ void FontControlModel::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+ {
+ switch( _nHandle )
+ {
+ case PROPERTY_ID_TEXTCOLOR:
+ _rValue = m_aTextColor;
+ break;
+
+ case PROPERTY_ID_FONTEMPHASISMARK:
+ _rValue <<= m_nFontEmphasis;
+ break;
+
+ case PROPERTY_ID_FONTRELIEF:
+ _rValue <<= m_nFontRelief;
+ break;
+
+ case PROPERTY_ID_TEXTLINECOLOR:
+ _rValue = m_aTextLineColor;
+ break;
+
+ case PROPERTY_ID_FONT:
+ _rValue <<= m_aFont;
+ break;
+
+ default:
+ _rValue = lcl_extractFontDescriptorAggregate( _nHandle, m_aFont );
+ break;
+ }
+ }
+
+
+ bool FontControlModel::convertFastPropertyValue( Any& _rConvertedValue, Any& _rOldValue,
+ sal_Int32 _nHandle, const Any& _rValue )
+ {
+ bool bModified = false;
+ switch( _nHandle )
+ {
+ case PROPERTY_ID_TEXTCOLOR:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aTextColor, cppu::UnoType<sal_Int32>::get() );
+ break;
+
+ case PROPERTY_ID_TEXTLINECOLOR:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aTextLineColor, cppu::UnoType<sal_Int32>::get() );
+ break;
+
+ case PROPERTY_ID_FONTEMPHASISMARK:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_nFontEmphasis );
+ break;
+
+ case PROPERTY_ID_FONTRELIEF:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_nFontRelief );
+ break;
+
+ case PROPERTY_ID_FONT:
+ {
+ Any aWorkAroundGccLimitation( m_aFont );
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, aWorkAroundGccLimitation, cppu::UnoType<decltype(m_aFont)>::get() );
+ }
+ break;
+
+ case PROPERTY_ID_FONT_NAME:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Name );
+ break;
+
+ case PROPERTY_ID_FONT_STYLENAME:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.StyleName );
+ break;
+
+ case PROPERTY_ID_FONT_FAMILY:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Family );
+ break;
+
+ case PROPERTY_ID_FONT_CHARSET:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.CharSet );
+ break;
+
+ case PROPERTY_ID_FONT_CHARWIDTH:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.CharacterWidth );
+ break;
+
+ case PROPERTY_ID_FONT_KERNING:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Kerning );
+ break;
+
+ case PROPERTY_ID_FONT_ORIENTATION:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Orientation );
+ break;
+
+ case PROPERTY_ID_FONT_PITCH:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Pitch );
+ break;
+
+ case PROPERTY_ID_FONT_TYPE:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Type );
+ break;
+
+ case PROPERTY_ID_FONT_WIDTH:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Width );
+ break;
+
+ case PROPERTY_ID_FONT_HEIGHT:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, float( m_aFont.Height ) );
+ break;
+
+ case PROPERTY_ID_FONT_WEIGHT:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Weight );
+ break;
+
+ case PROPERTY_ID_FONT_SLANT:
+ bModified = tryPropertyValueEnum( _rConvertedValue, _rOldValue, _rValue, m_aFont.Slant );
+ break;
+
+ case PROPERTY_ID_FONT_UNDERLINE:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Underline );
+ break;
+
+ case PROPERTY_ID_FONT_STRIKEOUT:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Strikeout );
+ break;
+
+ case PROPERTY_ID_FONT_WORDLINEMODE:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.WordLineMode );
+ break;
+
+ default:
+ OSL_FAIL( "FontControlModel::convertFastPropertyValue: no font aggregate!" );
+ }
+ return bModified;
+ }
+
+ /// @throws Exception
+ static void setFastPropertyValue_NoBroadcast_implimpl(
+ FontDescriptor & rFont,
+ sal_Int32 nHandle, const Any& rValue)
+ {
+ switch (nHandle)
+ {
+ case PROPERTY_ID_FONT_NAME:
+ rValue >>= rFont.Name;
+ break;
+
+ case PROPERTY_ID_FONT_STYLENAME:
+ rValue >>= rFont.StyleName;
+ break;
+
+ case PROPERTY_ID_FONT_FAMILY:
+ rValue >>= rFont.Family;
+ break;
+
+ case PROPERTY_ID_FONT_CHARSET:
+ rValue >>= rFont.CharSet;
+ break;
+
+ case PROPERTY_ID_FONT_CHARWIDTH:
+ rValue >>= rFont.CharacterWidth;
+ break;
+
+ case PROPERTY_ID_FONT_KERNING:
+ rValue >>= rFont.Kerning;
+ break;
+
+ case PROPERTY_ID_FONT_ORIENTATION:
+ rValue >>= rFont.Orientation;
+ break;
+
+ case PROPERTY_ID_FONT_PITCH:
+ rValue >>= rFont.Pitch;
+ break;
+
+ case PROPERTY_ID_FONT_TYPE:
+ rValue >>= rFont.Type;
+ break;
+
+ case PROPERTY_ID_FONT_WIDTH:
+ rValue >>= rFont.Width;
+ break;
+
+ case PROPERTY_ID_FONT_HEIGHT:
+ {
+ float nHeight = 0;
+ rValue >>= nHeight;
+ rFont.Height = static_cast<sal_Int16>(nHeight);
+ }
+ break;
+
+ case PROPERTY_ID_FONT_WEIGHT:
+ rValue >>= rFont.Weight;
+ break;
+
+ case PROPERTY_ID_FONT_SLANT:
+ rValue >>= rFont.Slant;
+ break;
+
+ case PROPERTY_ID_FONT_UNDERLINE:
+ rValue >>= rFont.Underline;
+ break;
+
+ case PROPERTY_ID_FONT_STRIKEOUT:
+ rValue >>= rFont.Strikeout;
+ break;
+
+ case PROPERTY_ID_FONT_WORDLINEMODE:
+ {
+ bool bWordLineMode = false;
+ rValue >>= bWordLineMode;
+ rFont.WordLineMode = bWordLineMode;
+ }
+ break;
+
+ default:
+ assert(false); // isFontAggregateProperty
+ }
+ }
+
+ void FontControlModel::setFastPropertyValue_NoBroadcast_impl(
+ ::cppu::OPropertySetHelper & rBase,
+ void (::cppu::OPropertySetHelper::*pSet)(sal_Int32, Any const&),
+ sal_Int32 nHandle, const Any& rValue)
+ {
+ if (isFontAggregateProperty(nHandle))
+ {
+ // need to fire an event for PROPERTY_ID_FONT too apparently, so:
+ FontDescriptor font(getFont());
+
+ // first set new value on backup copy
+ setFastPropertyValue_NoBroadcast_implimpl(font, nHandle, rValue);
+
+ // then set that as the actual property - will eventually call
+ // this method recursively again...
+ (rBase.*pSet)(PROPERTY_ID_FONT, Any(font));
+#ifndef NDEBUG
+ // verify that the nHandle property has the new value
+ Any tmp;
+ getFastPropertyValue(tmp, nHandle);
+ assert(tmp == rValue || PROPERTY_ID_FONT_HEIGHT == nHandle /*rounded*/);
+#endif
+ }
+ else
+ {
+ switch (nHandle)
+ {
+ case PROPERTY_ID_TEXTCOLOR:
+ m_aTextColor = rValue;
+ break;
+
+ case PROPERTY_ID_TEXTLINECOLOR:
+ m_aTextLineColor = rValue;
+ break;
+
+ case PROPERTY_ID_FONTEMPHASISMARK:
+ rValue >>= m_nFontEmphasis;
+ break;
+
+ case PROPERTY_ID_FONTRELIEF:
+ rValue >>= m_nFontRelief;
+ break;
+
+ case PROPERTY_ID_FONT:
+ rValue >>= m_aFont;
+ break;
+
+ default:
+ SAL_WARN("forms.component", "invalid property: " << nHandle);
+ }
+ }
+ }
+
+
+ Any FontControlModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+ {
+ Any aReturn;
+ // some defaults which are the same, not matter if we have toolkit-compatible
+ // defaults or not
+ bool bHandled = false;
+ switch( _nHandle )
+ {
+ case PROPERTY_ID_TEXTCOLOR:
+ case PROPERTY_ID_TEXTLINECOLOR:
+ // void
+ bHandled = true;
+ break;
+
+ case PROPERTY_ID_FONTEMPHASISMARK:
+ aReturn <<= css::awt::FontEmphasisMark::NONE;
+ bHandled = true;
+ break;
+
+ case PROPERTY_ID_FONTRELIEF:
+ aReturn <<= css::awt::FontRelief::NONE;
+ bHandled = true;
+ break;
+ }
+ if ( bHandled )
+ return aReturn;
+
+ if ( m_bToolkitCompatibleDefaults )
+ {
+ EmptyFontDescriptor aEmpty;
+ if ( PROPERTY_ID_FONT == _nHandle )
+ return Any( FontDescriptor(aEmpty) );
+ return lcl_extractFontDescriptorAggregate( _nHandle, aEmpty );
+ }
+
+ switch( _nHandle )
+ {
+ case PROPERTY_ID_FONT:
+ aReturn <<= ::comphelper::getDefaultFont();
+ break;
+
+ case PROPERTY_ID_FONT_WORDLINEMODE:
+ aReturn <<= false;
+ break;
+
+ case PROPERTY_ID_FONT_NAME:
+ case PROPERTY_ID_FONT_STYLENAME:
+ aReturn <<= OUString();
+ break;
+
+ case PROPERTY_ID_FONT_FAMILY:
+ case PROPERTY_ID_FONT_CHARSET:
+ case PROPERTY_ID_FONT_SLANT:
+ case PROPERTY_ID_FONT_UNDERLINE:
+ case PROPERTY_ID_FONT_STRIKEOUT:
+ aReturn <<= sal_Int16(1);
+ break;
+
+ case PROPERTY_ID_FONT_KERNING:
+ aReturn <<= false;
+ break;
+
+ case PROPERTY_ID_FONT_PITCH:
+ case PROPERTY_ID_FONT_TYPE:
+ case PROPERTY_ID_FONT_WIDTH:
+ aReturn <<= sal_Int16(0);
+ break;
+
+ case PROPERTY_ID_FONT_HEIGHT:
+ case PROPERTY_ID_FONT_WEIGHT:
+ case PROPERTY_ID_FONT_CHARWIDTH:
+ case PROPERTY_ID_FONT_ORIENTATION:
+ aReturn <<= float(0);
+ break;
+
+ default:
+ OSL_FAIL( "FontControlModel::getPropertyDefaultByHandle: invalid property!" );
+ }
+
+ return aReturn;
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/imgprod.cxx b/forms/source/component/imgprod.cxx
new file mode 100644
index 000000000..5446b2082
--- /dev/null
+++ b/forms/source/component/imgprod.cxx
@@ -0,0 +1,491 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "imgprod.hxx"
+
+#include <osl/diagnose.h>
+#include <tools/debug.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/cvtgrf.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <com/sun/star/awt/ImageStatus.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+
+#include <svtools/imageresourceaccess.hxx>
+#include <comphelper/processfactory.hxx>
+
+namespace {
+
+class ImgProdLockBytes : public SvLockBytes
+{
+ css::uno::Reference< css::io::XInputStream > xStmRef;
+ css::uno::Sequence<sal_Int8> maSeq;
+
+public:
+
+ ImgProdLockBytes( SvStream* pStm, bool bOwner );
+ explicit ImgProdLockBytes( css::uno::Reference< css::io::XInputStream > const & rStreamRef );
+
+ virtual ErrCode ReadAt( sal_uInt64 nPos, void* pBuffer, std::size_t nCount, std::size_t * pRead ) const override;
+ virtual ErrCode WriteAt( sal_uInt64 nPos, const void* pBuffer, std::size_t nCount, std::size_t * pWritten ) override;
+ virtual ErrCode Flush() const override;
+ virtual ErrCode SetSize( sal_uInt64 nSize ) override;
+ virtual ErrCode Stat( SvLockBytesStat* ) const override;
+};
+
+}
+
+ImgProdLockBytes::ImgProdLockBytes( SvStream* pStm, bool bOwner ) :
+ SvLockBytes( pStm, bOwner )
+{
+}
+
+
+ImgProdLockBytes::ImgProdLockBytes( css::uno::Reference< css::io::XInputStream > const & rStmRef ) :
+ xStmRef( rStmRef )
+{
+ if( !xStmRef.is() )
+ return;
+
+ const sal_uInt32 nBytesToRead = 65535;
+ sal_uInt32 nRead;
+
+ do
+ {
+ css::uno::Sequence< sal_Int8 > aReadSeq;
+
+ nRead = xStmRef->readSomeBytes( aReadSeq, nBytesToRead );
+
+ if( nRead )
+ {
+ const sal_uInt32 nOldLength = maSeq.getLength();
+ maSeq.realloc( nOldLength + nRead );
+ memcpy( maSeq.getArray() + nOldLength, aReadSeq.getConstArray(), aReadSeq.getLength() );
+ }
+ }
+ while( nBytesToRead == nRead );
+}
+
+ErrCode ImgProdLockBytes::ReadAt(sal_uInt64 const nPos,
+ void* pBuffer, std::size_t nCount, std::size_t * pRead) const
+{
+ if( GetStream() )
+ {
+ const_cast<SvStream*>(GetStream())->ResetError();
+ const ErrCode nErr = SvLockBytes::ReadAt( nPos, pBuffer, nCount, pRead );
+ const_cast<SvStream*>(GetStream())->ResetError();
+ return nErr;
+ }
+ else
+ {
+ const std::size_t nSeqLen = maSeq.getLength();
+
+ if( nPos < nSeqLen )
+ {
+ if( ( nPos + nCount ) > nSeqLen )
+ nCount = nSeqLen - nPos;
+
+ memcpy( pBuffer, maSeq.getConstArray() + nPos, nCount );
+ *pRead = nCount;
+ }
+ else
+ *pRead = 0;
+
+ return ERRCODE_NONE;
+ }
+}
+
+
+ErrCode ImgProdLockBytes::WriteAt(sal_uInt64 const nPos,
+ const void* pBuffer, std::size_t nCount, std::size_t * pWritten)
+{
+ if( GetStream() )
+ return SvLockBytes::WriteAt( nPos, pBuffer, nCount, pWritten );
+ else
+ {
+ DBG_ASSERT( xStmRef.is(), "ImgProdLockBytes::WriteAt: xInputStream has no reference..." );
+ return ERRCODE_IO_CANTWRITE;
+ }
+}
+
+
+ErrCode ImgProdLockBytes::Flush() const
+{
+ return ERRCODE_NONE;
+}
+
+
+ErrCode ImgProdLockBytes::SetSize(sal_uInt64 const nSize)
+{
+ if( GetStream() )
+ return SvLockBytes::SetSize( nSize );
+ else
+ {
+ OSL_FAIL( "ImgProdLockBytes::SetSize not supported for xInputStream..." );
+ return ERRCODE_IO_CANTWRITE;
+ }
+}
+
+
+ErrCode ImgProdLockBytes::Stat( SvLockBytesStat* pStat ) const
+{
+ if( GetStream() )
+ return SvLockBytes::Stat( pStat );
+ else
+ {
+ DBG_ASSERT( xStmRef.is(), "ImgProdLockBytes::Stat: xInputStream has no reference..." );
+ pStat->nSize = maSeq.getLength();
+ return ERRCODE_NONE;
+ }
+}
+
+
+ImageProducer::ImageProducer()
+ : mnTransIndex(0)
+ , mbConsInit(false)
+{
+ mpGraphic.reset( new Graphic );
+}
+
+ImageProducer::~ImageProducer()
+{
+}
+
+
+// XInterface
+css::uno::Any ImageProducer::queryInterface( const css::uno::Type & rType )
+{
+ css::uno::Any aRet = ::cppu::queryInterface( rType,
+ static_cast< css::lang::XInitialization* >(this),
+ static_cast< css::awt::XImageProducer* >(this) );
+ return (aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType ));
+}
+
+
+void ImageProducer::addConsumer( const css::uno::Reference< css::awt::XImageConsumer >& rxConsumer )
+{
+ DBG_ASSERT( rxConsumer.is(), "::AddConsumer(...): No consumer referenced!" );
+ if( rxConsumer.is() )
+ maConsList.push_back( rxConsumer );
+}
+
+
+void ImageProducer::removeConsumer( const css::uno::Reference< css::awt::XImageConsumer >& rxConsumer )
+{
+ ConsumerList_t::reverse_iterator riter = std::find(maConsList.rbegin(),maConsList.rend(),rxConsumer);
+
+ if (riter != maConsList.rend())
+ maConsList.erase(riter.base()-1);
+}
+
+
+void ImageProducer::SetImage( const OUString& rPath )
+{
+ maURL = rPath;
+ mpGraphic->Clear();
+ mbConsInit = false;
+ mpStm.reset();
+
+ if ( ::svt::GraphicAccess::isSupportedURL( maURL ) )
+ {
+ mpStm = ::svt::GraphicAccess::getImageStream( ::comphelper::getProcessComponentContext(), maURL );
+ }
+ else if( !maURL.isEmpty() )
+ {
+ std::unique_ptr<SvStream> pIStm = ::utl::UcbStreamHelper::CreateStream( maURL, StreamMode::STD_READ );
+ if (pIStm)
+ mpStm.reset( new SvStream( new ImgProdLockBytes( pIStm.release(), true ) ) );
+ }
+}
+
+
+void ImageProducer::SetImage( SvStream& rStm )
+{
+ maURL.clear();
+ mpGraphic->Clear();
+ mbConsInit = false;
+
+ mpStm.reset( new SvStream( new ImgProdLockBytes( &rStm, false ) ) );
+}
+
+
+void ImageProducer::setImage( css::uno::Reference< css::io::XInputStream > const & rInputStmRef )
+{
+ maURL.clear();
+ mpGraphic->Clear();
+ mbConsInit = false;
+ mpStm.reset();
+
+ if( rInputStmRef.is() )
+ mpStm.reset( new SvStream( new ImgProdLockBytes( rInputStmRef ) ) );
+}
+
+
+void ImageProducer::NewDataAvailable()
+{
+ if( ( GraphicType::NONE == mpGraphic->GetType() ) || mpGraphic->GetReaderContext() )
+ startProduction();
+}
+
+
+void ImageProducer::startProduction()
+{
+ if( maConsList.empty() && !maDoneHdl.IsSet() )
+ return;
+
+ bool bNotifyEmptyGraphics = false;
+
+ // valid stream or filled graphic? => update consumers
+ if( mpStm || ( mpGraphic->GetType() != GraphicType::NONE ) )
+ {
+ // if we already have a graphic, we don't have to import again;
+ // graphic is cleared if a new Stream is set
+ if( ( mpGraphic->GetType() == GraphicType::NONE ) || mpGraphic->GetReaderContext() )
+ {
+ if ( ImplImportGraphic( *mpGraphic ) )
+ maDoneHdl.Call( mpGraphic.get() );
+ }
+
+ if( mpGraphic->GetType() != GraphicType::NONE )
+ ImplUpdateData( *mpGraphic );
+ else
+ bNotifyEmptyGraphics = true;
+ }
+ else
+ bNotifyEmptyGraphics = true;
+
+ if ( !bNotifyEmptyGraphics )
+ return;
+
+ // reset image
+ // create temporary list to hold interfaces
+ ConsumerList_t aTmp = maConsList;
+
+ // iterate through interfaces
+ for (auto const& elem : aTmp)
+ {
+ elem->init( 0, 0 );
+ elem->complete( css::awt::ImageStatus::IMAGESTATUS_STATICIMAGEDONE, this );
+ }
+
+ maDoneHdl.Call( nullptr );
+}
+
+
+bool ImageProducer::ImplImportGraphic( Graphic& rGraphic )
+{
+ if (!mpStm)
+ return false;
+
+ if( ERRCODE_IO_PENDING == mpStm->GetError() )
+ mpStm->ResetError();
+
+ mpStm->Seek( 0 );
+
+ bool bRet = GraphicConverter::Import( *mpStm, rGraphic ) == ERRCODE_NONE;
+
+ if( ERRCODE_IO_PENDING == mpStm->GetError() )
+ mpStm->ResetError();
+
+ return bRet;
+}
+
+
+void ImageProducer::ImplUpdateData( const Graphic& rGraphic )
+{
+ ImplInitConsumer( rGraphic );
+
+ if( mbConsInit && !maConsList.empty() )
+ {
+ // create temporary list to hold interfaces
+ ConsumerList_t aTmp = maConsList;
+
+ ImplUpdateConsumer( rGraphic );
+ mbConsInit = false;
+
+ // iterate through interfaces
+ for (auto const& elem : aTmp)
+ elem->complete( css::awt::ImageStatus::IMAGESTATUS_STATICIMAGEDONE, this );
+ }
+}
+
+
+void ImageProducer::ImplInitConsumer( const Graphic& rGraphic )
+{
+ sal_uInt32 nRMask = 0;
+ sal_uInt32 nGMask = 0;
+ sal_uInt32 nBMask = 0;
+ sal_uInt32 nAMask = 0;
+ sal_uInt32 nWidth = 0;
+ sal_uInt32 nHeight = 0;
+ sal_uInt8 nBitCount = 0;
+ css::uno::Sequence< sal_Int32 > aRGBPal;
+ rGraphic.GetBitmapEx().GetColorModel(aRGBPal, nRMask, nGMask, nBMask, nAMask, mnTransIndex, nWidth, nHeight, nBitCount);
+
+ // create temporary list to hold interfaces
+ ConsumerList_t aTmp = maConsList;
+
+ // iterate through interfaces
+ for (auto const& elem : aTmp)
+ {
+ elem->init( nWidth, nHeight );
+ elem->setColorModel( nBitCount,aRGBPal, nRMask, nGMask, nBMask, nAMask );
+ }
+
+ mbConsInit = true;
+}
+
+
+void ImageProducer::ImplUpdateConsumer( const Graphic& rGraphic )
+{
+ BitmapEx aBmpEx( rGraphic.GetBitmapEx() );
+ Bitmap aBmp( aBmpEx.GetBitmap() );
+ BitmapReadAccess* pBmpAcc = aBmp.AcquireReadAccess();
+
+ if( !pBmpAcc )
+ return;
+
+ Bitmap aMask( aBmpEx.GetAlpha() );
+ BitmapReadAccess* pMskAcc = !aMask.IsEmpty() ? aMask.AcquireReadAccess() : nullptr;
+ const tools::Long nWidth = pBmpAcc->Width();
+ const tools::Long nHeight = pBmpAcc->Height();
+ const tools::Long nStartX = 0;
+ const tools::Long nEndX = nWidth - 1;
+ const tools::Long nStartY = 0;
+ const tools::Long nEndY = nHeight - 1;
+ const tools::Long nPartWidth = nEndX - nStartX + 1;
+ const tools::Long nPartHeight = nEndY - nStartY + 1;
+
+ if( !pMskAcc )
+ {
+ aMask = Bitmap(aBmp.GetSizePixel(), vcl::PixelFormat::N1_BPP);
+ aMask.Erase( COL_BLACK );
+ pMskAcc = aMask.AcquireReadAccess();
+ }
+
+ // create temporary list to hold interfaces
+ ConsumerList_t aTmp = maConsList;
+
+ if( pBmpAcc->HasPalette() )
+ {
+ const BitmapColor aWhite( pMskAcc->GetBestMatchingColor( COL_WHITE ) );
+
+ if( mnTransIndex < 256 )
+ {
+ css::uno::Sequence<sal_Int8> aData( nPartWidth * nPartHeight );
+ sal_Int8* pTmp = aData.getArray();
+
+ for( tools::Long nY = nStartY; nY <= nEndY; nY++ )
+ {
+ Scanline pScanlineMask = pMskAcc->GetScanline( nY );
+ Scanline pScanline = pBmpAcc->GetScanline( nY );
+ for( tools::Long nX = nStartX; nX <= nEndX; nX++ )
+ {
+ if( pMskAcc->GetPixelFromData( pScanlineMask, nX ) == aWhite )
+ *pTmp++ = sal::static_int_cast< sal_Int8 >(
+ mnTransIndex );
+ else
+ *pTmp++ = pBmpAcc->GetPixelFromData( pScanline, nX ).GetIndex();
+ }
+ }
+
+ // iterate through interfaces
+ for (auto const& elem : aTmp)
+ elem->setPixelsByBytes( nStartX, nStartY, nPartWidth, nPartHeight, aData, 0UL, nPartWidth );
+ }
+ else
+ {
+ css::uno::Sequence<sal_Int32> aData( nPartWidth * nPartHeight );
+ sal_Int32* pTmp = aData.getArray();
+
+ for( tools::Long nY = nStartY; nY <= nEndY; nY++ )
+ {
+ Scanline pScanlineMask = pMskAcc->GetScanline( nY );
+ Scanline pScanline = pBmpAcc->GetScanline( nY );
+ for( tools::Long nX = nStartX; nX <= nEndX; nX++ )
+ {
+ if( pMskAcc->GetPixelFromData( pScanlineMask, nX ) == aWhite )
+ *pTmp++ = mnTransIndex;
+ else
+ *pTmp++ = pBmpAcc->GetPixelFromData( pScanline, nX ).GetIndex();
+ }
+ }
+
+ // iterate through interfaces
+ for (auto const& elem : aTmp)
+ elem->setPixelsByLongs( nStartX, nStartY, nPartWidth, nPartHeight, aData, 0UL, nPartWidth );
+ }
+ }
+ else
+ {
+ css::uno::Sequence<sal_Int32> aData( nPartWidth * nPartHeight );
+ const BitmapColor aWhite( pMskAcc->GetBestMatchingColor( COL_WHITE ) );
+ sal_Int32* pTmp = aData.getArray();
+
+ for( tools::Long nY = nStartY; nY <= nEndY; nY++ )
+ {
+ Scanline pScanlineMask = pMskAcc->GetScanline( nY );
+ Scanline pScanline = pBmpAcc->GetScanline( nY );
+ for( tools::Long nX = nStartX; nX <= nEndX; nX++, pTmp++ )
+ {
+ const BitmapColor aCol( pBmpAcc->GetPixelFromData( pScanline, nX ) );
+
+ *pTmp = static_cast<sal_Int32>(aCol.GetRed()) << 24;
+ *pTmp |= static_cast<sal_Int32>(aCol.GetGreen()) << 16;
+ *pTmp |= static_cast<sal_Int32>(aCol.GetBlue()) << 8;
+
+ if( pMskAcc->GetPixelFromData( pScanlineMask, nX ) != aWhite )
+ *pTmp |= 0x000000ffUL;
+ }
+ }
+
+ // iterate through interfaces
+ for (auto const& elem : aTmp)
+ elem->setPixelsByLongs( nStartX, nStartY, nPartWidth, nPartHeight, aData, 0UL, nPartWidth );
+ }
+
+ Bitmap::ReleaseAccess( pBmpAcc );
+ Bitmap::ReleaseAccess( pMskAcc );
+}
+
+
+void ImageProducer::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ if ( aArguments.getLength() == 1 )
+ {
+ css::uno::Any aArg = aArguments.getConstArray()[0];
+ OUString aURL;
+ if ( aArg >>= aURL )
+ {
+ SetImage( aURL );
+ }
+ }
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_ImageProducer_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ImageProducer());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/imgprod.hxx b/forms/source/component/imgprod.hxx
new file mode 100644
index 000000000..0e746eb6b
--- /dev/null
+++ b/forms/source/component/imgprod.hxx
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/link.hxx>
+#include <com/sun/star/awt/XImageConsumer.hpp>
+#include <com/sun/star/awt/XImageProducer.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <cppuhelper/weak.hxx>
+#include <memory>
+#include <vector>
+
+
+class SvStream;
+class Graphic;
+namespace com::sun::star::io { class XInputStream; }
+
+
+class ImageProducer : public css::awt::XImageProducer,
+ public css::lang::XInitialization,
+ public ::cppu::OWeakObject
+{
+private:
+
+ typedef std::vector< css::uno::Reference< css::awt::XImageConsumer > > ConsumerList_t;
+
+ OUString maURL;
+ ConsumerList_t maConsList;
+ std::unique_ptr<Graphic>
+ mpGraphic;
+ std::unique_ptr<SvStream>
+ mpStm;
+ sal_uInt32 mnTransIndex;
+ bool mbConsInit;
+ Link<Graphic*,void> maDoneHdl;
+
+ bool ImplImportGraphic( Graphic& rGraphic );
+ void ImplUpdateData( const Graphic& rGraphic );
+ void ImplInitConsumer( const Graphic& rGraphic );
+ void ImplUpdateConsumer( const Graphic& rGraphic );
+
+public:
+
+ ImageProducer();
+ virtual ~ImageProducer() override;
+
+ void SetImage( const OUString& rPath );
+ void SetImage( SvStream& rStm );
+
+ void NewDataAvailable();
+
+ void SetDoneHdl( const Link<Graphic*,void>& i_rHdl ) { maDoneHdl = i_rHdl; }
+
+ // css::uno::XInterface
+ css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override;
+ void SAL_CALL acquire() noexcept override { OWeakObject::acquire(); }
+ void SAL_CALL release() noexcept override { OWeakObject::release(); }
+
+ // MT: ???
+ void setImage( css::uno::Reference< css::io::XInputStream > const & rStmRef );
+
+ // css::awt::XImageProducer
+ void SAL_CALL addConsumer( const css::uno::Reference< css::awt::XImageConsumer >& rxConsumer ) override;
+ void SAL_CALL removeConsumer( const css::uno::Reference< css::awt::XImageConsumer >& rxConsumer ) override;
+ void SAL_CALL startProduction( ) override;
+
+ // css::lang::XInitialization
+ void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/navigationbar.cxx b/forms/source/component/navigationbar.cxx
new file mode 100644
index 000000000..6209952b6
--- /dev/null
+++ b/forms/source/component/navigationbar.cxx
@@ -0,0 +1,487 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "navigationbar.hxx"
+#include <property.hxx>
+#include <services.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+
+#include <comphelper/streamsection.hxx>
+#include <comphelper/basicio.hxx>
+#include <tools/debug.hxx>
+
+using namespace comphelper;
+
+namespace frm
+{
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::io;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::container;
+ using namespace ::comphelper;
+
+ namespace WritingMode2 = ::com::sun::star::text::WritingMode2;
+
+ ONavigationBarModel::ONavigationBarModel( const Reference< XComponentContext >& _rxFactory )
+ :OControlModel( _rxFactory, OUString() )
+ ,FontControlModel( true )
+ {
+
+ m_nClassId = FormComponentType::NAVIGATIONBAR;
+ implInitPropertyContainer();
+
+ getPropertyDefaultByHandle( PROPERTY_ID_DEFAULTCONTROL ) >>= m_sDefaultControl;
+ getPropertyDefaultByHandle( PROPERTY_ID_ICONSIZE ) >>= m_nIconSize;
+ getPropertyDefaultByHandle( PROPERTY_ID_BORDER ) >>= m_nBorder;
+ getPropertyDefaultByHandle( PROPERTY_ID_DELAY ) >>= m_nDelay;
+ getPropertyDefaultByHandle( PROPERTY_ID_ENABLED ) >>= m_bEnabled;
+ getPropertyDefaultByHandle( PROPERTY_ID_ENABLEVISIBLE ) >>= m_bEnableVisible;
+ getPropertyDefaultByHandle( PROPERTY_ID_SHOW_POSITION ) >>= m_bShowPosition;
+ getPropertyDefaultByHandle( PROPERTY_ID_SHOW_NAVIGATION ) >>= m_bShowNavigation;
+ getPropertyDefaultByHandle( PROPERTY_ID_SHOW_RECORDACTIONS ) >>= m_bShowActions;
+ getPropertyDefaultByHandle( PROPERTY_ID_SHOW_FILTERSORT ) >>= m_bShowFilterSort;
+ getPropertyDefaultByHandle( PROPERTY_ID_WRITING_MODE ) >>= m_nWritingMode;
+ getPropertyDefaultByHandle( PROPERTY_ID_CONTEXT_WRITING_MODE ) >>= m_nContextWritingMode;
+ }
+
+
+ ONavigationBarModel::ONavigationBarModel( const ONavigationBarModel* _pOriginal, const Reference< XComponentContext >& _rxFactory )
+ :OControlModel( _pOriginal, _rxFactory )
+ ,FontControlModel( _pOriginal )
+ {
+
+ implInitPropertyContainer();
+
+ m_aTabStop = _pOriginal->m_aTabStop;
+ m_aBackgroundColor = _pOriginal->m_aBackgroundColor;
+ m_sDefaultControl = _pOriginal->m_sDefaultControl;
+ m_sHelpText = _pOriginal->m_sHelpText;
+ m_sHelpURL = _pOriginal->m_sHelpURL;
+ m_bEnabled = _pOriginal->m_bEnabled;
+ m_bEnableVisible = _pOriginal->m_bEnableVisible;
+ m_nIconSize = _pOriginal->m_nIconSize;
+ m_nBorder = _pOriginal->m_nBorder;
+ m_nDelay = _pOriginal->m_nDelay;
+ m_bShowPosition = _pOriginal->m_bShowPosition;
+ m_bShowNavigation = _pOriginal->m_bShowNavigation;
+ m_bShowActions = _pOriginal->m_bShowActions;
+ m_bShowFilterSort = _pOriginal->m_bShowFilterSort;
+ m_nWritingMode = _pOriginal->m_nWritingMode;
+ m_nContextWritingMode = _pOriginal->m_nContextWritingMode;
+ }
+
+
+ void ONavigationBarModel::implInitPropertyContainer()
+ {
+ registerProperty( PROPERTY_DEFAULTCONTROL, PROPERTY_ID_DEFAULTCONTROL, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_sDefaultControl, cppu::UnoType<decltype(m_sDefaultControl)>::get() );
+ registerProperty( PROPERTY_HELPTEXT, PROPERTY_ID_HELPTEXT, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_sHelpText, cppu::UnoType<decltype(m_sHelpText)>::get() );
+ registerProperty( PROPERTY_HELPURL, PROPERTY_ID_HELPURL, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_sHelpURL, cppu::UnoType<decltype(m_sHelpURL)>::get() );
+ registerProperty( PROPERTY_ENABLED, PROPERTY_ID_ENABLED, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bEnabled, cppu::UnoType<decltype(m_bEnabled)>::get() );
+ registerProperty( PROPERTY_ENABLEVISIBLE, PROPERTY_ID_ENABLEVISIBLE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bEnableVisible, cppu::UnoType<decltype(m_bEnableVisible)>::get() );
+ registerProperty( PROPERTY_ICONSIZE, PROPERTY_ID_ICONSIZE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_nIconSize, cppu::UnoType<decltype(m_nIconSize)>::get() );
+ registerProperty( PROPERTY_BORDER, PROPERTY_ID_BORDER, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_nBorder, cppu::UnoType<decltype(m_nBorder)>::get() );
+ registerProperty( PROPERTY_DELAY, PROPERTY_ID_DELAY, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_nDelay, cppu::UnoType<decltype(m_nDelay)>::get() );
+ registerProperty( PROPERTY_SHOW_POSITION, PROPERTY_ID_SHOW_POSITION, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bShowPosition, cppu::UnoType<decltype(m_bShowPosition)>::get() );
+ registerProperty( PROPERTY_SHOW_NAVIGATION, PROPERTY_ID_SHOW_NAVIGATION, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bShowNavigation, cppu::UnoType<decltype(m_bShowNavigation)>::get() );
+ registerProperty( PROPERTY_SHOW_RECORDACTIONS, PROPERTY_ID_SHOW_RECORDACTIONS, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bShowActions, cppu::UnoType<decltype(m_bShowActions)>::get() );
+ registerProperty( PROPERTY_SHOW_FILTERSORT, PROPERTY_ID_SHOW_FILTERSORT, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bShowFilterSort, cppu::UnoType<decltype(m_bShowFilterSort)>::get() );
+ registerProperty( PROPERTY_WRITING_MODE, PROPERTY_ID_WRITING_MODE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_nWritingMode, cppu::UnoType<decltype(m_nWritingMode)>::get() );
+
+ registerProperty( PROPERTY_CONTEXT_WRITING_MODE, PROPERTY_ID_CONTEXT_WRITING_MODE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT | PropertyAttribute::TRANSIENT,
+ &m_nContextWritingMode, cppu::UnoType<decltype(m_nContextWritingMode)>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_TABSTOP, PROPERTY_ID_TABSTOP, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT | PropertyAttribute::MAYBEVOID,
+ &m_aTabStop, cppu::UnoType<sal_Bool>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_BACKGROUNDCOLOR, PROPERTY_ID_BACKGROUNDCOLOR, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT | PropertyAttribute::MAYBEVOID,
+ &m_aBackgroundColor, cppu::UnoType<sal_Int32>::get() );
+ }
+
+
+ ONavigationBarModel::~ONavigationBarModel()
+ {
+ if ( !OComponentHelper::rBHelper.bDisposed )
+ {
+ acquire();
+ dispose();
+ }
+
+ }
+
+
+ Any SAL_CALL ONavigationBarModel::queryAggregation( const Type& _rType )
+ {
+ Any aReturn = ONavigationBarModel_BASE::queryInterface( _rType );
+
+ if ( !aReturn.hasValue() )
+ aReturn = OControlModel::queryAggregation( _rType );
+
+ return aReturn;
+ }
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( ONavigationBarModel, OControlModel, ONavigationBarModel_BASE )
+
+
+ css::uno::Reference< css::util::XCloneable > SAL_CALL ONavigationBarModel::createClone()
+{
+ rtl::Reference<ONavigationBarModel> pClone = new ONavigationBarModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+ OUString SAL_CALL ONavigationBarModel::getImplementationName()
+ {
+ return "com.sun.star.comp.form.ONavigationBarModel";
+ }
+
+
+ Sequence< OUString > SAL_CALL ONavigationBarModel::getSupportedServiceNames()
+ {
+ Sequence< OUString > aSupported = OControlModel::getSupportedServiceNames_Static();
+ aSupported.realloc( aSupported.getLength() + 2 );
+
+ OUString* pArray = aSupported.getArray();
+ pArray[ aSupported.getLength() - 2 ] = "com.sun.star.awt.UnoControlModel";
+ pArray[ aSupported.getLength() - 1 ] = FRM_SUN_COMPONENT_NAVTOOLBAR;
+ return aSupported;
+ }
+
+ OUString SAL_CALL ONavigationBarModel::getServiceName()
+ {
+ return FRM_SUN_COMPONENT_NAVTOOLBAR;
+ }
+
+ #define PERSIST_TABSTOP 0x0001
+ #define PERSIST_BACKGROUND 0x0002
+ #define PERSIST_TEXTCOLOR 0x0004
+ #define PERSIST_TEXTLINECOLOR 0x0008
+
+ #define PERSIST_ENABLED 0x0001
+ #define PERSIST_LARGEICONS 0x0002
+ // leaf a leap here - this will allow for two more icon size values to be stored compatibly
+ #define PERSIST_SHOW_POSITION 0x0008
+ #define PERSIST_SHOW_NAVIGATION 0x0010
+ #define PERSIST_SHOW_ACTIONS 0x0020
+ #define PERSIST_SHOW_FILTERSORT 0x0040
+
+
+ void SAL_CALL ONavigationBarModel::write( const Reference< XObjectOutputStream >& _rxOutStream )
+ {
+ // open a section for compatibility - if we later on write additional members,
+ // then older versions can skip them
+ OStreamSection aEnsureBlockCompat( _rxOutStream );
+
+ // base class
+ OControlModel::write( _rxOutStream );
+
+ {
+ OStreamSection aEnsureCompat( _rxOutStream );
+ // determine which properties are not void and need to be written
+ sal_Int32 nNonVoids = 0;
+ if ( m_aTabStop.hasValue() )
+ nNonVoids |= PERSIST_TABSTOP;
+ if ( m_aBackgroundColor.hasValue() )
+ nNonVoids |= PERSIST_BACKGROUND;
+ if ( hasTextColor() )
+ nNonVoids |= PERSIST_TEXTCOLOR;
+ if ( hasTextLineColor() )
+ nNonVoids |= PERSIST_TEXTLINECOLOR;
+
+ _rxOutStream->writeLong( nNonVoids );
+
+ // the maybeboid anys
+ if ( nNonVoids & PERSIST_TABSTOP )
+ {
+ bool bTabStop( false );
+ m_aTabStop >>= bTabStop;
+ _rxOutStream->writeBoolean( bTabStop );
+ }
+ if ( nNonVoids & PERSIST_BACKGROUND )
+ {
+ sal_Int32 nBackgroundColor = 0;
+ m_aBackgroundColor >>= nBackgroundColor;
+ _rxOutStream->writeLong( nBackgroundColor );
+ }
+ if ( nNonVoids & PERSIST_TEXTCOLOR )
+ {
+ _rxOutStream->writeLong( sal_Int32(getTextColor()) );
+ }
+ if ( nNonVoids & PERSIST_TEXTLINECOLOR )
+ {
+ _rxOutStream->writeLong( sal_Int32(getTextLineColor()) );
+ }
+ }
+
+ {
+ OStreamSection aEnsureCompat( _rxOutStream );
+ ::comphelper::operator<<( _rxOutStream, getFont() );
+ }
+
+ // our boolean flags
+ sal_Int32 nFlags = 0;
+ if ( m_bEnabled ) nFlags |= PERSIST_ENABLED;
+ if ( m_nIconSize ) nFlags |= PERSIST_LARGEICONS; // at the moment, this is quasi boolean
+ if ( m_bShowPosition ) nFlags |= PERSIST_SHOW_POSITION;
+ if ( m_bShowNavigation ) nFlags |= PERSIST_SHOW_NAVIGATION;
+ if ( m_bShowActions ) nFlags |= PERSIST_SHOW_ACTIONS;
+ if ( m_bShowFilterSort ) nFlags |= PERSIST_SHOW_FILTERSORT;
+ _rxOutStream->writeLong( nFlags );
+
+ // our strings
+ _rxOutStream->writeUTF( m_sHelpText );
+ _rxOutStream->writeUTF( m_sHelpURL );
+ _rxOutStream->writeUTF( m_sDefaultControl );
+
+ // misc
+ _rxOutStream->writeShort( m_nBorder );
+ _rxOutStream->writeLong ( m_nDelay );
+ }
+
+
+ void SAL_CALL ONavigationBarModel::read( const Reference< XObjectInputStream >& _rxInStream )
+ {
+ OStreamSection aEnsureBlockCompat( _rxInStream );
+
+ // base class
+ OControlModel::read( _rxInStream );
+
+ {
+ OStreamSection aEnsureCompat( _rxInStream );
+ // determine which properties were non-void
+ sal_Int32 nNonVoids = _rxInStream->readLong( );
+
+ // the maybeboid anys
+ if ( nNonVoids & PERSIST_TABSTOP )
+ m_aTabStop <<= _rxInStream->readBoolean();
+ else
+ m_aTabStop.clear();
+
+ if ( nNonVoids & PERSIST_BACKGROUND )
+ m_aBackgroundColor <<= _rxInStream->readLong();
+ else
+ m_aBackgroundColor.clear();
+
+ if ( nNonVoids & PERSIST_TEXTCOLOR )
+ setTextColor( ::Color(ColorTransparency, _rxInStream->readLong()) );
+ else
+ clearTextColor();
+
+ if ( nNonVoids & PERSIST_TEXTLINECOLOR )
+ setTextLineColor( ::Color(ColorTransparency, _rxInStream->readLong()) );
+ else
+ clearTextLineColor();
+ }
+
+ {
+ OStreamSection aEnsureCompat( _rxInStream );
+ FontDescriptor aFont;
+ ::comphelper::operator>>( _rxInStream, aFont );
+ setFont( aFont );
+ }
+
+ // our boolean flags
+ sal_Int32 nFlags = _rxInStream->readLong( );
+ m_bEnabled = ( nFlags & PERSIST_ENABLED ) != 0;
+ m_nIconSize = ( nFlags & PERSIST_LARGEICONS ) ? 1 : 0;
+ m_bShowPosition = ( nFlags & PERSIST_SHOW_POSITION ) != 0;
+ m_bShowNavigation = ( nFlags & PERSIST_SHOW_NAVIGATION ) != 0;
+ m_bShowActions = ( nFlags & PERSIST_SHOW_ACTIONS ) != 0;
+ m_bShowFilterSort = ( nFlags & PERSIST_SHOW_FILTERSORT ) != 0;
+
+ // our strings
+ m_sHelpText = _rxInStream->readUTF( );
+ m_sHelpURL = _rxInStream->readUTF( );
+ m_sDefaultControl = _rxInStream->readUTF( );
+
+ // misc
+ m_nBorder = _rxInStream->readShort();
+ m_nDelay = _rxInStream->readLong();
+ }
+
+
+ void SAL_CALL ONavigationBarModel::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+ {
+ if ( isRegisteredProperty( _nHandle ) )
+ {
+ OPropertyContainerHelper::getFastPropertyValue( _rValue, _nHandle );
+ }
+ else if ( isFontRelatedProperty( _nHandle ) )
+ {
+ FontControlModel::getFastPropertyValue( _rValue, _nHandle );
+ }
+ else
+ {
+ OControlModel::getFastPropertyValue( _rValue, _nHandle );
+ }
+ }
+
+
+ sal_Bool SAL_CALL ONavigationBarModel::convertFastPropertyValue( Any& _rConvertedValue, Any& _rOldValue,
+ sal_Int32 _nHandle, const Any& _rValue )
+ {
+ bool bModified = false;
+
+ if ( isRegisteredProperty( _nHandle ) )
+ {
+ bModified = OPropertyContainerHelper::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+ }
+ else if ( isFontRelatedProperty( _nHandle ) )
+ {
+ bModified = FontControlModel::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+ }
+ else
+ {
+ bModified = OControlModel::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+ }
+
+ return bModified;
+ }
+
+
+ void SAL_CALL ONavigationBarModel::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue )
+ {
+ if ( isRegisteredProperty( _nHandle ) )
+ {
+ OPropertyContainerHelper::setFastPropertyValue( _nHandle, _rValue );
+ }
+ else if ( isFontRelatedProperty( _nHandle ) )
+ {
+ FontControlModel::setFastPropertyValue_NoBroadcast_impl(
+ *this, &ONavigationBarModel::setDependentFastPropertyValue,
+ _nHandle, _rValue);
+ }
+ else
+ {
+ OControlModel::setFastPropertyValue_NoBroadcast( _nHandle, _rValue );
+ }
+ }
+
+
+ Any ONavigationBarModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+ {
+ Any aDefault;
+
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_TABSTOP:
+ case PROPERTY_ID_BACKGROUNDCOLOR:
+ /* void */
+ break;
+ case PROPERTY_ID_WRITING_MODE:
+ case PROPERTY_ID_CONTEXT_WRITING_MODE:
+ aDefault <<= WritingMode2::CONTEXT;
+ break;
+
+ case PROPERTY_ID_ENABLED:
+ case PROPERTY_ID_ENABLEVISIBLE:
+ case PROPERTY_ID_SHOW_POSITION:
+ case PROPERTY_ID_SHOW_NAVIGATION:
+ case PROPERTY_ID_SHOW_RECORDACTIONS:
+ case PROPERTY_ID_SHOW_FILTERSORT:
+ aDefault <<= true;
+ break;
+
+ case PROPERTY_ID_ICONSIZE:
+ aDefault <<= sal_Int16(0);
+ break;
+
+ case PROPERTY_ID_DEFAULTCONTROL:
+ aDefault <<= OUString( "com.sun.star.form.control.NavigationToolBar" );
+ break;
+
+ case PROPERTY_ID_HELPTEXT:
+ case PROPERTY_ID_HELPURL:
+ aDefault <<= OUString();
+ break;
+
+ case PROPERTY_ID_BORDER:
+ aDefault <<= sal_Int16(0);
+ break;
+
+ case PROPERTY_ID_DELAY:
+ aDefault <<= sal_Int32(20);
+ break;
+
+ default:
+ if ( isFontRelatedProperty( _nHandle ) )
+ aDefault = FontControlModel::getPropertyDefaultByHandle( _nHandle );
+ else
+ aDefault = OControlModel::getPropertyDefaultByHandle( _nHandle );
+ }
+ return aDefault;
+ }
+
+
+ void ONavigationBarModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+ {
+ OControlModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 1);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+
+ // properties which the OPropertyContainerHelper is responsible for
+ Sequence< Property > aContainedProperties;
+ describeProperties( aContainedProperties );
+
+ // properties which the FontControlModel is responsible for
+ Sequence< Property > aFontProperties;
+ describeFontRelatedProperties( aFontProperties );
+
+ _rProps = concatSequences(
+ aContainedProperties,
+ aFontProperties,
+ _rProps
+ );
+ }
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_form_ONavigationBarModel_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ONavigationBarModel(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/navigationbar.hxx b/forms/source/component/navigationbar.hxx
new file mode 100644
index 000000000..ceb84dbe8
--- /dev/null
+++ b/forms/source/component/navigationbar.hxx
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+#include <comphelper/propertycontainerhelper.hxx>
+#include <cppuhelper/implbase1.hxx>
+#include <formcontrolfont.hxx>
+
+
+using namespace comphelper;
+
+namespace frm
+{
+
+ typedef ::cppu::ImplHelper1 < css::awt::XControlModel
+ > ONavigationBarModel_BASE;
+
+ class ONavigationBarModel
+ :public OControlModel
+ ,public FontControlModel
+ ,public OPropertyContainerHelper
+ ,public ONavigationBarModel_BASE
+ {
+ // <properties>
+ css::uno::Any m_aTabStop;
+ css::uno::Any m_aBackgroundColor;
+ OUString m_sDefaultControl;
+ OUString m_sHelpText;
+ OUString m_sHelpURL;
+ sal_Int16 m_nIconSize;
+ sal_Int16 m_nBorder;
+ sal_Int32 m_nDelay;
+ bool m_bEnabled;
+ bool m_bEnableVisible;
+ bool m_bShowPosition;
+ bool m_bShowNavigation;
+ bool m_bShowActions;
+ bool m_bShowFilterSort;
+ sal_Int16 m_nWritingMode;
+ sal_Int16 m_nContextWritingMode;
+ // </properties>
+
+ public:
+ ONavigationBarModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ ONavigationBarModel(
+ const ONavigationBarModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~ONavigationBarModel() override;
+
+ // UNO
+ DECLARE_UNO3_AGG_DEFAULTS( ONavigationBarModel, OControlModel )
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XTypeProvider
+ DECLARE_XTYPEPROVIDER()
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // XPropertySet
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle ) const override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(css::uno::Any& rConvertedValue, css::uno::Any& rOldValue,
+ sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const css::uno::Any& rValue) override;
+
+ // XPropertyState
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 nHandle ) const override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+ // prevent method hiding
+ using OControlModel::disposing;
+ using OControlModel::getFastPropertyValue;
+
+ protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ private:
+ void implInitPropertyContainer();
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/propertybaghelper.cxx b/forms/source/component/propertybaghelper.cxx
new file mode 100644
index 000000000..af19ae139
--- /dev/null
+++ b/forms/source/component/propertybaghelper.cxx
@@ -0,0 +1,339 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <propertybaghelper.hxx>
+
+#include <property.hxx>
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyExistException.hpp>
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/beans/NotRemoveableException.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+
+#include <tools/diagnose_ex.h>
+
+#include <comphelper/sequence.hxx>
+
+
+#define NEW_HANDLE_BASE 10000
+
+
+namespace frm
+{
+
+
+ using ::com::sun::star::lang::DisposedException;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::beans::Property;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::beans::PropertyExistException;
+ using ::com::sun::star::beans::PropertyValue;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::beans::XMultiPropertySet;
+ using ::com::sun::star::beans::XPropertySetInfo;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::beans::NotRemoveableException;
+ using ::com::sun::star::beans::UnknownPropertyException;
+
+ namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute;
+
+
+ //= helper
+
+ namespace
+ {
+
+ ::comphelper::IPropertyInfoService& lcl_getPropertyInfos()
+ {
+ static ConcreteInfoService s_aPropInfos;
+ return s_aPropInfos;
+ }
+ }
+
+ PropertyBagHelper::PropertyBagHelper( IPropertyBagHelperContext& _rContext )
+ :m_rContext( _rContext )
+ ,m_bDisposed( false )
+ {
+ }
+
+
+ PropertyBagHelper::~PropertyBagHelper()
+ {
+ }
+
+
+ void PropertyBagHelper::dispose()
+ {
+ m_bDisposed = true;
+ }
+
+
+ void PropertyBagHelper::impl_nts_checkDisposed_throw() const
+ {
+ if ( m_bDisposed )
+ throw DisposedException();
+ }
+
+
+ void PropertyBagHelper::impl_nts_invalidatePropertySetInfo()
+ {
+ m_pPropertyArrayHelper.reset();
+ }
+
+
+ sal_Int32 PropertyBagHelper::impl_findFreeHandle( const OUString& _rPropertyName )
+ {
+ ::comphelper::OPropertyArrayAggregationHelper& rPropInfo( impl_ts_getArrayHelper() );
+
+ // check the preferred handle
+ sal_Int32 nHandle = lcl_getPropertyInfos().getPreferredPropertyId( _rPropertyName );
+ if ( ( nHandle != -1 ) && rPropInfo.fillPropertyMembersByHandle( nullptr, nullptr, nHandle ) )
+ nHandle = -1;
+
+ // search a free handle in <math>F_1009</math>
+ if ( nHandle == -1 )
+ {
+ sal_Int32 const nPrime = 1009;
+ sal_Int32 nFactor = 11;
+ sal_Int32 nNum = nFactor;
+ while ( nNum != 1 )
+ {
+ if ( !rPropInfo.fillPropertyMembersByHandle( nullptr, nullptr, nNum + NEW_HANDLE_BASE ) )
+ {
+ // handle not used, yet
+ nHandle = nNum + NEW_HANDLE_BASE;
+ break;
+ }
+ nNum = ( nNum * nFactor ) % nPrime;
+ }
+ }
+
+ // search a free handle greater NEW_HANDLE_BASE
+ if ( nHandle == -1 )
+ {
+ nHandle = NEW_HANDLE_BASE + 1009;
+ while ( rPropInfo.fillPropertyMembersByHandle( nullptr, nullptr, nHandle ) )
+ ++nHandle;
+ }
+
+ return nHandle;
+ }
+
+
+ ::comphelper::OPropertyArrayAggregationHelper& PropertyBagHelper::impl_ts_getArrayHelper() const
+ {
+ OPropertyArrayAggregationHelper* p = m_pPropertyArrayHelper.get();
+ if ( !p )
+ {
+ ::osl::MutexGuard aGuard( m_rContext.getMutex() );
+ p = m_pPropertyArrayHelper.get();
+ if ( !p )
+ {
+ // our own fixed and our aggregate's properties
+ Sequence< Property > aFixedProps;
+ Sequence< Property > aAggregateProps;
+ m_rContext.describeFixedAndAggregateProperties( aFixedProps, aAggregateProps );
+
+ // our dynamic properties
+ Sequence< Property > aDynamicProps;
+ m_aDynamicProperties.describeProperties( aDynamicProps );
+
+ Sequence< Property > aOwnProps(
+ ::comphelper::concatSequences( aFixedProps, aDynamicProps ) );
+
+ p = new OPropertyArrayAggregationHelper( aOwnProps, aAggregateProps, &lcl_getPropertyInfos(), NEW_HANDLE_BASE );
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ const_cast< PropertyBagHelper* >( this )->m_pPropertyArrayHelper.reset( p );
+ }
+ } // if ( !p )
+ else
+ {
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ }
+ return *p;
+ }
+
+
+ void PropertyBagHelper::addProperty( const OUString& _rName, ::sal_Int16 _nAttributes, const Any& _rInitialValue )
+ {
+ ::osl::MutexGuard aGuard( m_rContext.getMutex() );
+ impl_nts_checkDisposed_throw();
+
+
+ // check name sanity
+ ::comphelper::OPropertyArrayAggregationHelper& aPropInfo( impl_ts_getArrayHelper() );
+ if ( aPropInfo.hasPropertyByName( _rName ) )
+ throw PropertyExistException( _rName, m_rContext.getPropertiesInterface() );
+
+
+ // normalize the REMOVABLE attribute - the FormComponent service
+ // requires that all dynamic properties are REMOVABLE
+ _nAttributes |= PropertyAttribute::REMOVABLE;
+
+
+ // find a free handle
+ sal_Int32 nHandle = impl_findFreeHandle( _rName );
+
+
+ // register the property, and invalidate our property meta data
+ m_aDynamicProperties.addProperty( _rName, nHandle, _nAttributes, _rInitialValue );
+ impl_nts_invalidatePropertySetInfo();
+ }
+
+
+ void PropertyBagHelper::removeProperty( const OUString& _rName )
+ {
+ ::osl::MutexGuard aGuard( m_rContext.getMutex() );
+ impl_nts_checkDisposed_throw();
+
+ // check whether it's removable at all
+ Reference< XMultiPropertySet > xMe( m_rContext.getPropertiesInterface(), css::uno::UNO_SET_THROW );
+ Reference< XPropertySetInfo > xPSI( xMe->getPropertySetInfo(), css::uno::UNO_SET_THROW );
+ Property aProperty( xPSI->getPropertyByName( _rName ) );
+ if ( ( aProperty.Attributes & PropertyAttribute::REMOVABLE ) == 0 )
+ throw NotRemoveableException( _rName, xMe );
+
+ m_aDynamicProperties.removeProperty( _rName );
+ impl_nts_invalidatePropertySetInfo();
+ }
+
+
+ namespace
+ {
+
+ struct SelectNameOfProperty
+ {
+ const OUString& operator()( const Property& _rProp ) const { return _rProp.Name; }
+ };
+
+
+ struct SelectNameOfPropertyValue
+ {
+ const OUString& operator()( const PropertyValue& _rProp ) const { return _rProp.Name; }
+ };
+
+
+ struct SelectValueOfPropertyValue
+ {
+ const Any& operator()( const PropertyValue& _rProp ) const { return _rProp.Value; }
+ };
+
+
+ struct PropertyValueLessByName
+ {
+ bool operator()( const PropertyValue& _lhs, const PropertyValue& _rhs ) const
+ {
+ return _lhs.Name < _rhs.Name;
+ }
+ };
+ }
+
+
+ Sequence< PropertyValue > PropertyBagHelper::getPropertyValues()
+ {
+ ::osl::MutexGuard aGuard( m_rContext.getMutex() );
+ impl_nts_checkDisposed_throw();
+
+ Reference< XMultiPropertySet > xMe( m_rContext.getPropertiesInterface(), css::uno::UNO_SET_THROW );
+ Reference< XPropertySetInfo > xPSI( xMe->getPropertySetInfo(), css::uno::UNO_SET_THROW );
+
+ const Sequence< Property > aProperties( xPSI->getProperties() );
+ Sequence< OUString > aPropertyNames( aProperties.getLength() );
+ ::std::transform( aProperties.begin(), aProperties.end(),
+ aPropertyNames.getArray(), SelectNameOfProperty() );
+
+ Sequence< Any > aValues;
+ try
+ {
+ aValues = xMe->getPropertyValues( aPropertyNames );
+
+ if ( aValues.getLength() != aPropertyNames.getLength() )
+ throw RuntimeException();
+ }
+ catch( const RuntimeException& ) { throw; }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ Sequence< PropertyValue > aPropertyValues( aValues.getLength() );
+ PropertyValue* pPropertyValue = aPropertyValues.getArray();
+
+ const OUString* pName = aPropertyNames.getConstArray();
+ const OUString* pNameEnd = aPropertyNames.getConstArray() + aPropertyNames.getLength();
+ const Any* pValue = aValues.getConstArray();
+ for ( ; pName != pNameEnd; ++pName, ++pValue, ++pPropertyValue )
+ {
+ pPropertyValue->Name = *pName;
+ pPropertyValue->Value = *pValue;
+ }
+
+ return aPropertyValues;
+ }
+
+
+ void PropertyBagHelper::setPropertyValues( const Sequence< PropertyValue >& _rProps )
+ {
+ ::osl::ClearableMutexGuard aGuard( m_rContext.getMutex() );
+ impl_nts_checkDisposed_throw();
+
+ sal_Int32 nPropertyValues = _rProps.getLength();
+
+ // XMultiPropertySet::setPropertyValues expects its arguments to be sorted by name
+ // while XPropertyAccess::setPropertyValues doesn't. So first of all, sort.
+ Sequence< PropertyValue > aSortedProps( _rProps );
+ ::std::sort( aSortedProps.getArray(), aSortedProps.getArray() + nPropertyValues, PropertyValueLessByName() );
+
+ // also, XPropertyAccess::setPropertyValues is expected to throw an UnknownPropertyException
+ // for unsupported properties, while XMultiPropertySet::setPropertyValues is expected to ignore
+ // those. So, check for unsupported properties first.
+ ::comphelper::OPropertyArrayAggregationHelper& rArrayHelper( impl_ts_getArrayHelper() );
+ for ( const PropertyValue* pProperties = aSortedProps.getConstArray();
+ pProperties != aSortedProps.getConstArray() + nPropertyValues;
+ ++pProperties
+ )
+ {
+ if ( !rArrayHelper.hasPropertyByName( pProperties->Name ) )
+ throw UnknownPropertyException( pProperties->Name, m_rContext.getPropertiesInterface() );
+ }
+
+ // Now finally split into a Name and a Value sequence, and forward to
+ // XMultiPropertySet::setPropertyValues
+ Sequence< OUString > aNames( nPropertyValues );
+ ::std::transform( aSortedProps.getConstArray(), aSortedProps.getConstArray() + nPropertyValues,
+ aNames.getArray(), SelectNameOfPropertyValue() );
+
+ Sequence< Any > aValues( nPropertyValues );
+ ::std::transform( aSortedProps.getConstArray(), aSortedProps.getConstArray() + nPropertyValues,
+ aValues.getArray(), SelectValueOfPropertyValue() );
+
+ Reference< XMultiPropertySet > xMe( m_rContext.getPropertiesInterface(), css::uno::UNO_SET_THROW );
+
+ aGuard.clear();
+ xMe->setPropertyValues( aNames, aValues );
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/refvaluecomponent.cxx b/forms/source/component/refvaluecomponent.cxx
new file mode 100644
index 000000000..14ff8eaef
--- /dev/null
+++ b/forms/source/component/refvaluecomponent.cxx
@@ -0,0 +1,294 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "refvaluecomponent.hxx"
+#include <property.hxx>
+
+#include <comphelper/property.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+#include <vector>
+
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::form::binding;
+
+
+ //=
+
+
+ OReferenceValueComponent::OReferenceValueComponent( const Reference< XComponentContext >& _rxFactory, const OUString& _rUnoControlModelTypeName, const OUString& _rDefault )
+ :OBoundControlModel( _rxFactory, _rUnoControlModelTypeName, _rDefault, false, true, true )
+ ,m_eDefaultChecked( TRISTATE_FALSE )
+ {
+ }
+
+
+ OReferenceValueComponent::OReferenceValueComponent( const OReferenceValueComponent* _pOriginal, const Reference< XComponentContext>& _rxFactory )
+ :OBoundControlModel( _pOriginal, _rxFactory )
+ {
+ m_sReferenceValue = _pOriginal->m_sReferenceValue;
+ m_sNoCheckReferenceValue = _pOriginal->m_sNoCheckReferenceValue;
+ m_eDefaultChecked = _pOriginal->m_eDefaultChecked;
+
+ calculateExternalValueType();
+ }
+
+
+ OReferenceValueComponent::~OReferenceValueComponent()
+ {
+ }
+
+
+ void OReferenceValueComponent::setReferenceValue( const OUString& _rRefValue )
+ {
+ m_sReferenceValue = _rRefValue;
+ calculateExternalValueType();
+ }
+
+
+ void SAL_CALL OReferenceValueComponent::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+ {
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_REFVALUE: _rValue <<= m_sReferenceValue; break;
+ case PROPERTY_ID_DEFAULT_STATE: _rValue <<= static_cast<sal_Int16>(m_eDefaultChecked); break;
+
+ case PROPERTY_ID_UNCHECKED_REFVALUE:
+ _rValue <<= m_sNoCheckReferenceValue;
+ break;
+
+ default:
+ OBoundControlModel::getFastPropertyValue( _rValue, _nHandle );
+ }
+ }
+
+
+ void SAL_CALL OReferenceValueComponent::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue )
+ {
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_REFVALUE :
+ OSL_VERIFY( _rValue >>= m_sReferenceValue );
+ calculateExternalValueType();
+ break;
+
+ case PROPERTY_ID_UNCHECKED_REFVALUE:
+ OSL_VERIFY( _rValue >>= m_sNoCheckReferenceValue );
+ break;
+
+ case PROPERTY_ID_DEFAULT_STATE:
+ {
+ sal_Int16 nDefaultChecked;
+ if (!(_rValue >>= nDefaultChecked) || nDefaultChecked < 0
+ || nDefaultChecked > 2)
+ {
+ throw css::lang::IllegalArgumentException(
+ ("DefaultState property value must be a SHORT in the range"
+ " 0--2"),
+ css::uno::Reference<css::uno::XInterface>(), -1);
+ }
+ m_eDefaultChecked = static_cast<ToggleState>(nDefaultChecked);
+ resetNoBroadcast();
+ }
+ break;
+
+ default:
+ OBoundControlModel::setFastPropertyValue_NoBroadcast( _nHandle, _rValue );
+ }
+ }
+
+
+ sal_Bool SAL_CALL OReferenceValueComponent::convertFastPropertyValue( Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+ {
+ bool bModified = false;
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_REFVALUE:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_sReferenceValue );
+ break;
+
+ case PROPERTY_ID_UNCHECKED_REFVALUE:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_sNoCheckReferenceValue );
+ break;
+
+ case PROPERTY_ID_DEFAULT_STATE:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, static_cast<sal_Int16>(m_eDefaultChecked) );
+ break;
+
+ default:
+ bModified = OBoundControlModel::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+ break;
+ }
+ return bModified;
+ }
+
+
+ Any OReferenceValueComponent::getDefaultForReset() const
+ {
+ return Any( static_cast<sal_Int16>(m_eDefaultChecked) );
+ }
+
+
+ void OReferenceValueComponent::describeFixedProperties( Sequence< Property >& _rProps ) const
+ {
+ OBoundControlModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 3);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_REFVALUE, PROPERTY_ID_REFVALUE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_STATE, PROPERTY_ID_DEFAULT_STATE, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_UNCHECKED_REFVALUE, PROPERTY_ID_UNCHECKED_REFVALUE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+ }
+
+
+ Sequence< Type > OReferenceValueComponent::getSupportedBindingTypes()
+ {
+ ::std::vector< Type > aTypes;
+
+ if ( !m_sReferenceValue.isEmpty() )
+ aTypes.push_back( cppu::UnoType<OUString>::get() );
+
+ aTypes.push_back( cppu::UnoType<sal_Bool>::get() );
+
+ return comphelper::containerToSequence(aTypes);
+ }
+
+
+ Any OReferenceValueComponent::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+ {
+ sal_Int16 nState = TRISTATE_INDET;
+
+ bool bExternalState = false;
+ OUString sExternalValue;
+ if ( _rExternalValue >>= bExternalState )
+ {
+ nState = ::sal::static_int_cast< sal_Int16 >( bExternalState ? TRISTATE_TRUE : TRISTATE_FALSE );
+ }
+ else if ( _rExternalValue >>= sExternalValue )
+ {
+ if ( sExternalValue == m_sReferenceValue )
+ nState = TRISTATE_TRUE;
+ else
+ {
+ if ( sExternalValue == m_sNoCheckReferenceValue )
+ nState = TRISTATE_FALSE;
+ else
+ nState = TRISTATE_INDET;
+ }
+ }
+ else if ( !_rExternalValue.hasValue() )
+ {
+ nState = TRISTATE_INDET;
+ }
+ else
+ {
+ OSL_FAIL( "OReferenceValueComponent::translateExternalValueToControlValue: unexpected value type!" );
+ }
+
+ return Any( nState );
+ }
+
+
+ Any OReferenceValueComponent::translateControlValueToExternalValue( ) const
+ {
+ Any aExternalValue;
+
+ try
+ {
+ Any aControlValue( m_xAggregateSet->getPropertyValue( PROPERTY_STATE ) );
+ sal_Int16 nControlValue = TRISTATE_INDET;
+ aControlValue >>= nControlValue;
+
+ bool bBooleanExchange = getExternalValueType().getTypeClass() == TypeClass_BOOLEAN;
+ bool bStringExchange = getExternalValueType().getTypeClass() == TypeClass_STRING;
+ OSL_ENSURE( bBooleanExchange || bStringExchange,
+ "OReferenceValueComponent::translateControlValueToExternalValue: unexpected value exchange type!" );
+
+ switch( nControlValue )
+ {
+ case TRISTATE_TRUE:
+ if ( bBooleanExchange )
+ {
+ aExternalValue <<= true;
+ }
+ else if ( bStringExchange )
+ {
+ aExternalValue <<= m_sReferenceValue;
+ }
+ break;
+
+ case TRISTATE_FALSE:
+ if ( bBooleanExchange )
+ {
+ aExternalValue <<= false;
+ }
+ else if ( bStringExchange )
+ {
+ aExternalValue <<= m_sNoCheckReferenceValue;
+ }
+ break;
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.component", "OReferenceValueComponent::translateControlValueToExternalValue" );
+ }
+
+ return aExternalValue;
+ }
+
+
+ Any OReferenceValueComponent::translateControlValueToValidatableValue( ) const
+ {
+ if ( !m_xAggregateSet.is() )
+ return Any();
+
+ Any aControlValue( m_xAggregateSet->getPropertyValue( PROPERTY_STATE ) );
+ sal_Int16 nControlValue = TRISTATE_INDET;
+ aControlValue >>= nControlValue;
+
+ Any aValidatableValue;
+ switch ( nControlValue )
+ {
+ case TRISTATE_TRUE:
+ aValidatableValue <<= true;
+ break;
+ case TRISTATE_FALSE:
+ aValidatableValue <<= false;
+ break;
+ }
+ return aValidatableValue;
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/refvaluecomponent.hxx b/forms/source/component/refvaluecomponent.hxx
new file mode 100644
index 000000000..79f343888
--- /dev/null
+++ b/forms/source/component/refvaluecomponent.hxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+#include <togglestate.hxx>
+
+
+namespace frm
+{
+
+ /** an OBoundControlModel which features the exchange of a reference value
+ */
+ class OReferenceValueComponent : public OBoundControlModel
+ {
+ private:
+ // <properties>
+ OUString m_sReferenceValue; // the reference value to use for data exchange
+ OUString m_sNoCheckReferenceValue; // the reference value to be exchanged when the control is not checked
+ ToggleState m_eDefaultChecked; // the default check state
+ // </properties>
+
+ protected:
+ const OUString& getReferenceValue() const { return m_sReferenceValue; }
+ void setReferenceValue( const OUString& _rRefValue );
+
+ const OUString& getNoCheckReferenceValue() const { return m_sNoCheckReferenceValue; }
+
+ ToggleState getDefaultChecked() const { return m_eDefaultChecked; }
+ void setDefaultChecked( ToggleState _eChecked ) { m_eDefaultChecked = _eChecked; }
+
+ protected:
+ OReferenceValueComponent(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory,
+ const OUString& _rUnoControlModelTypeName,
+ const OUString& _rDefault
+ );
+ OReferenceValueComponent(
+ const OReferenceValueComponent* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+
+ virtual ~OReferenceValueComponent() override;
+
+ // OPropertySet and friends
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle) const override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(
+ css::uno::Any& _rConvertedValue, css::uno::Any& _rOldValue, sal_Int32 _nHandle, const css::uno::Any& _rValue ) override;
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+ using ::cppu::OPropertySetHelper::getFastPropertyValue;
+
+ // OBoundControlModel overridables
+ virtual css::uno::Sequence< css::uno::Type >
+ getSupportedBindingTypes() override;
+ virtual css::uno::Any translateExternalValueToControlValue( const css::uno::Any& _rExternalValue ) const override;
+ virtual css::uno::Any translateControlValueToExternalValue( ) const override;
+
+ virtual css::uno::Any translateControlValueToValidatableValue( ) const override;
+
+ virtual css::uno::Any getDefaultForReset() const override;
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/scrollbar.cxx b/forms/source/component/scrollbar.cxx
new file mode 100644
index 000000000..f39bb32a6
--- /dev/null
+++ b/forms/source/component/scrollbar.cxx
@@ -0,0 +1,317 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "scrollbar.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/streamsection.hxx>
+#include <comphelper/basicio.hxx>
+#include <rtl/math.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+namespace frm
+{
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::io;
+ using namespace ::com::sun::star::form::binding;
+
+
+ //= helper
+
+ Any translateExternalDoubleToControlIntValue(
+ const Any& _rExternalValue, const Reference< XPropertySet >& _rxProperties,
+ const OUString& _rMinValueName, const OUString& _rMaxValueName )
+ {
+ OSL_ENSURE( _rxProperties.is(), "translateExternalDoubleToControlIntValue: no aggregate!?" );
+
+ sal_Int32 nControlValue( 0 );
+ double nExternalValue = 0;
+ if ( _rExternalValue >>= nExternalValue )
+ {
+ if ( std::isinf( nExternalValue ) )
+ {
+ // set the minimum or maximum of the scroll values
+ OUString sLimitPropertyName = std::signbit( nExternalValue )
+ ? _rMinValueName : _rMaxValueName;
+ if ( _rxProperties.is() )
+ _rxProperties->getPropertyValue( sLimitPropertyName ) >>= nControlValue;
+ }
+ else
+ {
+ nControlValue = static_cast<sal_Int32>(::rtl::math::round( nExternalValue ));
+ }
+ }
+ else
+ {
+ if ( _rxProperties.is() )
+ _rxProperties->getPropertyValue( _rMinValueName ) >>= nControlValue;
+ }
+
+ return Any( nControlValue );
+ }
+
+
+ Any translateControlIntToExternalDoubleValue( const Any& _rControlIntValue )
+ {
+ Any aExternalDoubleValue;
+ sal_Int32 nScrollValue = 0;
+ if ( _rControlIntValue >>= nScrollValue )
+ aExternalDoubleValue <<= static_cast<double>(nScrollValue);
+ else
+ {
+ OSL_FAIL( "translateControlIntToExternalDoubleValue: no integer scroll value!" );
+ // aExternalDoubleValue is void here, which is okay for this purpose ...
+ }
+
+ return aExternalDoubleValue;
+ }
+
+ OScrollBarModel::OScrollBarModel( const Reference<XComponentContext>& _rxFactory )
+ :OBoundControlModel( _rxFactory, VCL_CONTROLMODEL_SCROLLBAR, VCL_CONTROL_SCROLLBAR, true, true, false )
+ ,m_nDefaultScrollValue( 0 )
+ {
+
+ m_nClassId = FormComponentType::SCROLLBAR;
+ initValueProperty( PROPERTY_SCROLL_VALUE, PROPERTY_ID_SCROLL_VALUE );
+ }
+
+
+ OScrollBarModel::OScrollBarModel( const OScrollBarModel* _pOriginal, const Reference< XComponentContext >& _rxFactory )
+ :OBoundControlModel( _pOriginal, _rxFactory )
+ {
+ m_nDefaultScrollValue = _pOriginal->m_nDefaultScrollValue;
+ }
+
+
+ OScrollBarModel::~OScrollBarModel( )
+ {
+ }
+
+ OUString SAL_CALL OScrollBarModel::getImplementationName()
+ {
+ return "com.sun.star.comp.forms.OScrollBarModel";
+ }
+
+ // note that we're passing OControlModel as "base class". This is because
+ // OBoundControlModel, our real base class, claims to support the DataAwareControlModel
+ // service, which isn't really true for us. We only derive from this class
+ // to benefit from the functionality for binding to spreadsheet cells
+ Sequence< OUString > SAL_CALL OScrollBarModel::getSupportedServiceNames()
+ {
+ Sequence< OUString > aOwnNames { FRM_SUN_COMPONENT_SCROLLBAR, BINDABLE_INTEGER_VALUE_RANGE };
+
+ return ::comphelper::combineSequences(
+ getAggregateServiceNames(),
+ ::comphelper::concatSequences(
+ OControlModel::getSupportedServiceNames_Static(),
+ aOwnNames)
+ );
+ }
+
+ css::uno::Reference< css::util::XCloneable > SAL_CALL OScrollBarModel::createClone()
+{
+ rtl::Reference<OScrollBarModel> pClone = new OScrollBarModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+ void OScrollBarModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+ {
+ OControlModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 3);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_SCROLL_VALUE, PROPERTY_ID_DEFAULT_SCROLL_VALUE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROLSOURCEPROPERTY, PROPERTY_ID_CONTROLSOURCEPROPERTY, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+ }
+
+
+ void OScrollBarModel::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+ {
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_SCROLL_VALUE:
+ _rValue <<= m_nDefaultScrollValue;
+ break;
+
+ default:
+ OBoundControlModel::getFastPropertyValue( _rValue, _nHandle );
+ }
+ }
+
+
+ void OScrollBarModel::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue )
+ {
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_SCROLL_VALUE:
+ OSL_VERIFY( _rValue >>= m_nDefaultScrollValue );
+ resetNoBroadcast();
+ break;
+
+ default:
+ OBoundControlModel::setFastPropertyValue_NoBroadcast( _nHandle, _rValue );
+ }
+ }
+
+
+ sal_Bool OScrollBarModel::convertFastPropertyValue(
+ Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+ {
+ bool bModified( false );
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_SCROLL_VALUE:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_nDefaultScrollValue );
+ break;
+
+ default:
+ bModified = OBoundControlModel::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+ break;
+ }
+ return bModified;
+ }
+
+
+ Any OScrollBarModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+ {
+ Any aReturn;
+
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_SCROLL_VALUE:
+ aReturn <<= sal_Int32(0);
+ break;
+
+ default:
+ aReturn = OBoundControlModel::getPropertyDefaultByHandle( _nHandle );
+ break;
+ }
+
+ return aReturn;
+ }
+
+
+ Any OScrollBarModel::translateDbColumnToControlValue( )
+ {
+ OSL_FAIL( "OScrollBarModel::commitControlValueToDbColumn: never to be called (we're not bound)!" );
+ return Any();
+ }
+
+
+ bool OScrollBarModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+ {
+ OSL_FAIL( "OScrollBarModel::commitControlValueToDbColumn: never to be called (we're not bound)!" );
+ return true;
+ }
+
+
+ Any OScrollBarModel::getDefaultForReset() const
+ {
+ return Any( m_nDefaultScrollValue );
+ }
+
+
+ OUString SAL_CALL OScrollBarModel::getServiceName()
+ {
+ return FRM_SUN_COMPONENT_SCROLLBAR;
+ }
+
+
+ void SAL_CALL OScrollBarModel::write( const Reference< XObjectOutputStream >& _rxOutStream )
+ {
+ OBoundControlModel::write( _rxOutStream );
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ OStreamSection aSection( _rxOutStream );
+
+ // version
+ _rxOutStream->writeShort( 0x0001 );
+
+ // properties
+ _rxOutStream << m_nDefaultScrollValue;
+ writeHelpTextCompatibly( _rxOutStream );
+ }
+
+
+ void SAL_CALL OScrollBarModel::read( const Reference< XObjectInputStream>& _rxInStream )
+ {
+ OBoundControlModel::read( _rxInStream );
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // version
+ {
+ OStreamSection aSection( _rxInStream );
+
+ sal_uInt16 nVersion = _rxInStream->readShort();
+ if ( nVersion == 0x0001 )
+ {
+ _rxInStream >> m_nDefaultScrollValue;
+ readHelpTextCompatibly( _rxInStream );
+ }
+ else
+ defaultCommonProperties();
+
+ // here, everything in the stream section which is left will be skipped
+ }
+ }
+
+
+ Any OScrollBarModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+ {
+ return translateExternalDoubleToControlIntValue( _rExternalValue, m_xAggregateSet,
+ "ScrollValueMin",
+ "ScrollValueMax" );
+ }
+
+
+ Any OScrollBarModel::translateControlValueToExternalValue( ) const
+ {
+ // by definition, the base class simply obtains the property value
+ return translateControlIntToExternalDoubleValue( OBoundControlModel::translateControlValueToExternalValue() );
+ }
+
+
+ Sequence< Type > OScrollBarModel::getSupportedBindingTypes()
+ {
+ return Sequence< Type >( & cppu::UnoType<double>::get(), 1 );
+ }
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_forms_OScrollBarModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OScrollBarModel(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/scrollbar.hxx b/forms/source/component/scrollbar.hxx
new file mode 100644
index 000000000..0acb91a3a
--- /dev/null
+++ b/forms/source/component/scrollbar.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <FormComponent.hxx>
+
+namespace frm
+{
+
+ class OScrollBarModel :public OBoundControlModel
+ {
+ private:
+ // <properties>
+ sal_Int32 m_nDefaultScrollValue;
+ // </properties>
+
+ public:
+ OScrollBarModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OScrollBarModel(
+ const OScrollBarModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OScrollBarModel() override;
+
+ protected:
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual ::css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // XCloneable
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ // XPropertyState
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 _nHandle ) const override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+ // OPropertySetHelper
+ virtual void SAL_CALL getFastPropertyValue( css::uno::Any& _rValue, sal_Int32 _nHandle ) const override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const css::uno::Any& _rValue ) override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any& _rConvertedValue, css::uno::Any& _rOldValue, sal_Int32 _nHandle, const css::uno::Any& _rValue ) override;
+
+ // OBoundControlModel
+ virtual css::uno::Any translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+ virtual css::uno::Any getDefaultForReset() const override;
+
+ virtual css::uno::Sequence< css::uno::Type >
+ getSupportedBindingTypes() override;
+ virtual css::uno::Any translateExternalValueToControlValue( const css::uno::Any& _rExternalValue ) const override;
+ virtual css::uno::Any translateControlValueToExternalValue( ) const override;
+
+ // prevent method hiding
+ using OBoundControlModel::disposing;
+ using OBoundControlModel::getFastPropertyValue;
+
+ };
+
+ css::uno::Any translateExternalDoubleToControlIntValue(
+ const css::uno::Any& _rExternalValue, const css::uno::Reference< css::beans::XPropertySet >& _rxProperties,
+ const OUString& _rMinValueName, const OUString& _rMaxValueName );
+
+ css::uno::Any translateControlIntToExternalDoubleValue( const css::uno::Any& _rControlIntValue );
+
+} // namespacefrm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/spinbutton.cxx b/forms/source/component/spinbutton.cxx
new file mode 100644
index 000000000..a20b17573
--- /dev/null
+++ b/forms/source/component/spinbutton.cxx
@@ -0,0 +1,271 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "spinbutton.hxx"
+#include "scrollbar.hxx"
+#include <comphelper/property.hxx>
+#include <comphelper/streamsection.hxx>
+#include <comphelper/basicio.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <property.hxx>
+#include <services.hxx>
+
+
+namespace frm
+{
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::io;
+ using namespace ::com::sun::star::form::binding;
+
+
+ //= OSpinButtonModel
+
+ OSpinButtonModel::OSpinButtonModel( const Reference<XComponentContext>& _rxFactory )
+ :OBoundControlModel( _rxFactory, VCL_CONTROLMODEL_SPINBUTTON, VCL_CONTROL_SPINBUTTON, true, true, false )
+ ,m_nDefaultSpinValue( 0 )
+ {
+
+ m_nClassId = FormComponentType::SPINBUTTON;
+ initValueProperty( PROPERTY_SPIN_VALUE, PROPERTY_ID_SPIN_VALUE );
+ }
+
+
+ OSpinButtonModel::OSpinButtonModel( const OSpinButtonModel* _pOriginal, const Reference< XComponentContext >& _rxFactory )
+ :OBoundControlModel( _pOriginal, _rxFactory )
+ {
+ m_nDefaultSpinValue = _pOriginal->m_nDefaultSpinValue;
+ }
+
+
+ OSpinButtonModel::~OSpinButtonModel( )
+ {
+ }
+
+ OUString SAL_CALL OSpinButtonModel::getImplementationName()
+ {
+ return "com.sun.star.comp.forms.OSpinButtonModel";
+ }
+
+ // note that we're passing OControlModel as "base class". This is because
+ // OBoundControlModel, our real base class, claims to support the DataAwareControlModel
+ // service, which isn't really true for us. We only derive from this class
+ // to benefit from the functionality for binding to spreadsheet cells
+ Sequence< OUString > SAL_CALL OSpinButtonModel::getSupportedServiceNames()
+ {
+ Sequence< OUString > aOwnNames { FRM_SUN_COMPONENT_SPINBUTTON, BINDABLE_INTEGER_VALUE_RANGE };
+
+ return ::comphelper::combineSequences(
+ getAggregateServiceNames(),
+ ::comphelper::concatSequences(
+ OControlModel::getSupportedServiceNames_Static(),
+ aOwnNames
+ )
+ );
+ }
+
+ css::uno::Reference< css::util::XCloneable > SAL_CALL OSpinButtonModel::createClone()
+{
+ rtl::Reference<OSpinButtonModel> pClone = new OSpinButtonModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+ void OSpinButtonModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+ {
+ OControlModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 3);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_SPIN_VALUE, PROPERTY_ID_DEFAULT_SPIN_VALUE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROLSOURCEPROPERTY, PROPERTY_ID_CONTROLSOURCEPROPERTY, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+ }
+
+
+ void OSpinButtonModel::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+ {
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_SPIN_VALUE:
+ _rValue <<= m_nDefaultSpinValue;
+ break;
+
+ default:
+ OBoundControlModel::getFastPropertyValue( _rValue, _nHandle );
+ }
+ }
+
+
+ void OSpinButtonModel::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue )
+ {
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_SPIN_VALUE:
+ OSL_VERIFY( _rValue >>= m_nDefaultSpinValue );
+ resetNoBroadcast();
+ break;
+
+ default:
+ OBoundControlModel::setFastPropertyValue_NoBroadcast( _nHandle, _rValue );
+ }
+ }
+
+
+ sal_Bool OSpinButtonModel::convertFastPropertyValue(
+ Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+ {
+ bool bModified( false );
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_SPIN_VALUE:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_nDefaultSpinValue );
+ break;
+
+ default:
+ bModified = OBoundControlModel::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+ break;
+ }
+ return bModified;
+ }
+
+
+ Any OSpinButtonModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+ {
+ Any aReturn;
+
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_SPIN_VALUE:
+ aReturn <<= sal_Int32(0);
+ break;
+
+ default:
+ aReturn = OBoundControlModel::getPropertyDefaultByHandle( _nHandle );
+ break;
+ }
+
+ return aReturn;
+ }
+
+
+ Any OSpinButtonModel::translateDbColumnToControlValue( )
+ {
+ OSL_FAIL( "OSpinButtonModel::commitControlValueToDbColumn: never to be called (we're not bound)!" );
+ return Any();
+ }
+
+
+ bool OSpinButtonModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+ {
+ OSL_FAIL( "OSpinButtonModel::commitControlValueToDbColumn: never to be called (we're not bound)!" );
+ return true;
+ }
+
+
+ Any OSpinButtonModel::getDefaultForReset() const
+ {
+ return Any( m_nDefaultSpinValue );
+ }
+
+
+ OUString SAL_CALL OSpinButtonModel::getServiceName()
+ {
+ return FRM_SUN_COMPONENT_SPINBUTTON;
+ }
+
+
+ void SAL_CALL OSpinButtonModel::write( const Reference< XObjectOutputStream >& _rxOutStream )
+ {
+ OBoundControlModel::write( _rxOutStream );
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ OStreamSection aSection( _rxOutStream );
+
+ // version
+ _rxOutStream->writeShort( 0x0001 );
+
+ // properties
+ _rxOutStream << m_nDefaultSpinValue;
+ writeHelpTextCompatibly( _rxOutStream );
+ }
+
+
+ void SAL_CALL OSpinButtonModel::read( const Reference< XObjectInputStream>& _rxInStream )
+ {
+ OBoundControlModel::read( _rxInStream );
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // version
+ {
+ OStreamSection aSection( _rxInStream );
+
+ sal_uInt16 nVersion = _rxInStream->readShort();
+ if ( nVersion == 0x0001 )
+ {
+ _rxInStream >> m_nDefaultSpinValue;
+ readHelpTextCompatibly( _rxInStream );
+ }
+ else
+ defaultCommonProperties();
+
+ // here, everything in the stream section which is left will be skipped
+ }
+ }
+
+
+ Any OSpinButtonModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+ {
+ return translateExternalDoubleToControlIntValue( _rExternalValue, m_xAggregateSet,
+ "SpinValueMin",
+ "SpinValueMax" );
+ }
+
+
+ Any OSpinButtonModel::translateControlValueToExternalValue( ) const
+ {
+ // by definition, the base class simply obtains the property value
+ return translateControlIntToExternalDoubleValue( OBoundControlModel::translateControlValueToExternalValue() );
+ }
+
+
+ Sequence< Type > OSpinButtonModel::getSupportedBindingTypes()
+ {
+ return Sequence< Type >( &cppu::UnoType<double>::get(), 1 );
+ }
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_forms_OSpinButtonModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OSpinButtonModel(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/spinbutton.hxx b/forms/source/component/spinbutton.hxx
new file mode 100644
index 000000000..b9800274f
--- /dev/null
+++ b/forms/source/component/spinbutton.hxx
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <FormComponent.hxx>
+
+
+namespace frm
+{
+
+ class OSpinButtonModel :public OBoundControlModel
+ {
+ private:
+ // <properties>
+ sal_Int32 m_nDefaultSpinValue;
+ // </properties>
+
+ public:
+ OSpinButtonModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OSpinButtonModel(
+ const OSpinButtonModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OSpinButtonModel() override;
+
+ protected:
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual ::css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override;
+ virtual void SAL_CALL read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override;
+
+ // XCloneable
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ // XPropertyState
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 _nHandle ) const override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+ // OPropertySetHelper
+ virtual void SAL_CALL getFastPropertyValue( css::uno::Any& _rValue, sal_Int32 _nHandle ) const override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const css::uno::Any& _rValue ) override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any& _rConvertedValue, css::uno::Any& _rOldValue, sal_Int32 _nHandle, const css::uno::Any& _rValue ) override;
+
+ // OBoundControlModel
+ virtual css::uno::Any translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+ virtual css::uno::Any getDefaultForReset() const override;
+
+ virtual css::uno::Sequence< css::uno::Type >
+ getSupportedBindingTypes() override;
+ virtual css::uno::Any translateExternalValueToControlValue( const css::uno::Any& _rExternalValue ) const override;
+ virtual css::uno::Any translateControlValueToExternalValue( ) const override;
+
+ // prevent method hiding
+ using OBoundControlModel::disposing;
+ using OBoundControlModel::getFastPropertyValue;
+
+ };
+
+} // namespacefrm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */