diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /extensions/source/propctrlr | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.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 '')
112 files changed, 30811 insertions, 0 deletions
diff --git a/extensions/source/propctrlr/MasterDetailLinkDialog.cxx b/extensions/source/propctrlr/MasterDetailLinkDialog.cxx new file mode 100644 index 000000000..5f38856fc --- /dev/null +++ b/extensions/source/propctrlr/MasterDetailLinkDialog.cxx @@ -0,0 +1,133 @@ +/* -*- 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/log.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <vcl/svapp.hxx> +#include "MasterDetailLinkDialog.hxx" +#include "formlinkdialog.hxx" + +namespace pcr +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + + MasterDetailLinkDialog::MasterDetailLinkDialog(const Reference< XComponentContext >& _rxContext ) + :OGenericUnoDialog( _rxContext ) + { + } + + Sequence<sal_Int8> SAL_CALL MasterDetailLinkDialog::getImplementationId( ) + { + return css::uno::Sequence<sal_Int8>(); + } + + + OUString SAL_CALL MasterDetailLinkDialog::getImplementationName() + { + return "org.openoffice.comp.form.ui.MasterDetailLinkDialog"; + } + + + css::uno::Sequence<OUString> SAL_CALL MasterDetailLinkDialog::getSupportedServiceNames() + { + return { "com.sun.star.form.MasterDetailLinkDialog" }; + } + + + Reference<XPropertySetInfo> SAL_CALL MasterDetailLinkDialog::getPropertySetInfo() + { + Reference<XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + + ::cppu::IPropertyArrayHelper& MasterDetailLinkDialog::getInfoHelper() + { + return *getArrayHelper(); + } + + + ::cppu::IPropertyArrayHelper* MasterDetailLinkDialog::createArrayHelper( ) const + { + Sequence< Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); + } + + std::unique_ptr<weld::DialogController> MasterDetailLinkDialog::createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) + { + return std::make_unique<FormLinkDialog>(Application::GetFrameWeld(rParent), m_xDetail, + m_xMaster, m_aContext, m_sExplanation, + m_sDetailLabel, m_sMasterLabel); + } + + void MasterDetailLinkDialog::implInitialize(const Any& _rValue) + { + PropertyValue aProperty; + if (_rValue >>= aProperty) + { + if (aProperty.Name == "Detail") + { + if ( ! (aProperty.Value >>= m_xDetail) ) + SAL_WARN("extensions.propctrlr", "implInitialize: unable to get property Detail"); + return; + } + else if (aProperty.Name == "Master") + { + if ( ! (aProperty.Value >>= m_xMaster) ) + SAL_WARN("extensions.propctrlr", "implInitialize: unable to get property Master"); + return; + } + else if (aProperty.Name == "Explanation") + { + if ( ! (aProperty.Value >>= m_sExplanation) ) + SAL_WARN("extensions.propctrlr", "implInitialize: unable to get property Explanation"); + return; + } + else if (aProperty.Name == "DetailLabel") + { + if ( ! (aProperty.Value >>= m_sDetailLabel) ) + SAL_WARN("extensions.propctrlr", "implInitialize: unable to get property DetailLabel"); + return; + } + else if (aProperty.Name == "MasterLabel") + { + if ( ! (aProperty.Value >>= m_sMasterLabel) ) + SAL_WARN("extensions.propctrlr", "implInitialize: unable to get property MasterLabel"); + return; + } + } + MasterDetailLinkDialog_DBase::implInitialize(_rValue); + } + + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_MasterDetailLinkDialog_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::MasterDetailLinkDialog(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/MasterDetailLinkDialog.hxx b/extensions/source/propctrlr/MasterDetailLinkDialog.hxx new file mode 100644 index 000000000..97911436a --- /dev/null +++ b/extensions/source/propctrlr/MasterDetailLinkDialog.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 <comphelper/proparrhlp.hxx> +#include <svtools/genericunodialog.hxx> + +namespace pcr +{ + + + class MasterDetailLinkDialog; + typedef ::svt::OGenericUnoDialog MasterDetailLinkDialog_DBase; + typedef ::comphelper::OPropertyArrayUsageHelper< MasterDetailLinkDialog > MasterDetailLinkDialog_PBase; + + class MasterDetailLinkDialog : public MasterDetailLinkDialog_DBase + ,public MasterDetailLinkDialog_PBase + { + public: + explicit MasterDetailLinkDialog(const css::uno::Reference< css::uno::XComponentContext >& _rxContext); + + private: + // XTypeProvider + virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId( ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + // OGenericUnoDialog overridables + virtual std::unique_ptr<weld::DialogController> createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) override; + virtual void implInitialize(const css::uno::Any& _rValue) override; + + css::uno::Reference< css::beans::XPropertySet> m_xDetail; + css::uno::Reference< css::beans::XPropertySet> m_xMaster; + OUString m_sExplanation; + OUString m_sDetailLabel; + OUString m_sMasterLabel; + }; + + +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/browserline.cxx b/extensions/source/propctrlr/browserline.cxx new file mode 100644 index 000000000..814c24f4b --- /dev/null +++ b/extensions/source/propctrlr/browserline.cxx @@ -0,0 +1,405 @@ +/* -*- 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 "browserline.hxx" + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/inspection/PropertyLineElement.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> + +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/string.hxx> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <vcl/weldutils.hxx> + +namespace pcr +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::inspection::XPropertyControl; + using ::com::sun::star::inspection::XPropertyControlContext; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::graphic::GraphicProvider; + using ::com::sun::star::graphic::XGraphicProvider; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::beans::PropertyValue; + using ::com::sun::star::graphic::XGraphic; + + namespace PropertyLineElement = ::com::sun::star::inspection::PropertyLineElement; + + OBrowserLine::OBrowserLine(const OUString& rEntryName, weld::Container* pParent, weld::SizeGroup* pLabelGroup, + weld::Container* pInitialControlParent) + : m_sEntryName(rEntryName) + , m_xBuilder(Application::CreateBuilder(pParent, "modules/spropctrlr/ui/browserline.ui")) + , m_xContainer(m_xBuilder->weld_container("BrowserLine")) + , m_xFtTitle(m_xBuilder->weld_label("label")) + , m_xBrowseButton(m_xBuilder->weld_button("browse")) + , m_xAdditionalBrowseButton(m_xBuilder->weld_button("morebrowse")) + , m_pInitialControlParent(pInitialControlParent) // controls start with this as their parent and need to be moved into m_xContainer + , m_pParent(pParent) + , m_pControlWindow( nullptr ) + , m_pBrowseButton(nullptr) + , m_pAdditionalBrowseButton( nullptr ) + , m_pClickListener( nullptr ) + , m_nNameWidth(0) + , m_nEnableFlags( 0xFFFF ) + , m_bIndentTitle( false ) + , m_bReadOnly( false ) + { + pLabelGroup->add_widget(m_xFtTitle.get()); + } + + OBrowserLine::~OBrowserLine() + { + implHideBrowseButton(true); + implHideBrowseButton(false); + m_pParent->move(m_xContainer.get(), nullptr); + } + + void OBrowserLine::IndentTitle( bool _bIndent ) + { + if ( m_bIndentTitle != _bIndent ) + { + m_bIndentTitle = _bIndent; + } + } + + void OBrowserLine::SetComponentHelpIds(const OString& rHelpId) + { + if (m_pControlWindow) + m_pControlWindow->set_help_id(rHelpId); + + if ( m_pBrowseButton ) + { + m_pBrowseButton->set_help_id(rHelpId); + + if ( m_pAdditionalBrowseButton ) + { + m_pAdditionalBrowseButton->set_help_id(rHelpId); + } + } + } + + void OBrowserLine::setControl( const Reference< XPropertyControl >& rxControl ) + { + m_xControl = rxControl; + auto xWindow = m_xControl->getControlWindow(); + if (weld::TransportAsXWindow* pTunnel = dynamic_cast<weld::TransportAsXWindow*>(xWindow.get())) + m_pControlWindow = pTunnel->getWidget(); + else + m_pControlWindow = nullptr; + DBG_ASSERT( m_pControlWindow, "OBrowserLine::setControl: setting NULL controls/windows is not allowed!" ); + + if ( m_pControlWindow ) + { + m_pInitialControlParent->move(m_pControlWindow, m_xContainer.get()); + m_pControlWindow->set_grid_left_attach(1); + m_xFtTitle->set_mnemonic_widget(m_pControlWindow); + m_pControlWindow->show(); + } + } + + bool OBrowserLine::GrabFocus() + { + bool bRes=false; + + if (m_pControlWindow && m_pControlWindow->get_sensitive()) + { + m_pControlWindow->grab_focus(); + bRes = true; + } + else if ( m_pAdditionalBrowseButton && m_pAdditionalBrowseButton->get_sensitive() ) + { + m_pAdditionalBrowseButton->grab_focus(); + bRes = true; + } + else if ( m_pBrowseButton && m_pBrowseButton->get_sensitive() ) + { + m_pBrowseButton->grab_focus(); + bRes = true; + } + return bRes; + } + + void OBrowserLine::Show(bool bFlag) + { + m_xFtTitle->set_visible(bFlag); + if (m_pControlWindow) + m_pControlWindow->set_visible( bFlag ); + if ( m_pBrowseButton ) + m_pBrowseButton->set_visible( bFlag ); + if ( m_pAdditionalBrowseButton ) + m_pAdditionalBrowseButton->set_visible( bFlag ); + } + + void OBrowserLine::Hide() + { + Show(false); + } + + void OBrowserLine::SetTitle(const OUString& rNewTitle ) + { + if ( GetTitle() == rNewTitle ) + return; + m_xFtTitle->set_label( rNewTitle ); + if (m_pControlWindow) + m_pControlWindow->set_accessible_name(rNewTitle); + if ( m_pBrowseButton ) + m_pBrowseButton->set_accessible_name( rNewTitle ); + FullFillTitleString(); + } + + void OBrowserLine::FullFillTitleString() + { + OUStringBuffer aText(m_xFtTitle->get_label()); + + int n10DotsWidth = m_xFtTitle->get_pixel_size("..........").Width(); + int nTextWidth = m_xFtTitle->get_pixel_size(OUString::unacquired(aText)).Width(); + int nDiff = m_nNameWidth - nTextWidth; + int nExtraChars = (nDiff * 10) / n10DotsWidth; + for (int i = 0; i < nExtraChars; ++i) + aText.append("."); + + // for Issue 69452 + if (AllSettings::GetLayoutRTL()) + { + sal_Unicode const cRTL_mark = 0x200F; + aText.append( cRTL_mark ); + } + + m_xFtTitle->set_label(aText.makeStringAndClear()); + } + + OUString OBrowserLine::GetTitle() const + { + OUString sDisplayName = m_xFtTitle->get_label(); + + // for Issue 69452 + if (AllSettings::GetLayoutRTL()) + { + sal_Unicode const cRTL_mark = 0x200F; + sDisplayName = comphelper::string::stripEnd(sDisplayName, cRTL_mark); + } + + sDisplayName = comphelper::string::stripEnd(sDisplayName, '.'); + + return sDisplayName; + } + + void OBrowserLine::SetReadOnly( bool _bReadOnly ) + { + if ( m_bReadOnly != _bReadOnly ) + { + m_bReadOnly = _bReadOnly; + implUpdateEnabledDisabled(); + } + } + + namespace + { + void implSetBitIfAffected(sal_uInt16& nEnabledBits, sal_Int16 _nAffectedMask, sal_Int16 _nTestBit, bool _bSet) + { + if ( _nAffectedMask & _nTestBit ) + { + if ( _bSet ) + nEnabledBits |= _nTestBit; + else + nEnabledBits &= ~_nTestBit; + } + } + + void implEnable(weld::Widget* pWindow, bool bEnable) + { + // tdf#138131 get_sensitive comparison as bodge for + // vcl's recursive Enable behavior + if (pWindow && pWindow->get_sensitive() != bEnable) + pWindow->set_sensitive(bEnable); + } + + void implEnable(weld::Widget* pWindow, sal_uInt16 nEnabledBits, sal_uInt16 nMatchBits) + { + bool bEnable = ((nEnabledBits & nMatchBits) == nMatchBits); + implEnable(pWindow, bEnable); + } + } + + void OBrowserLine::implUpdateEnabledDisabled() + { + implEnable( m_xFtTitle.get(), m_nEnableFlags, PropertyLineElement::CompleteLine ); + if ( m_pControlWindow ) + implEnable( m_pControlWindow, m_nEnableFlags, PropertyLineElement::CompleteLine | PropertyLineElement::InputControl ); + + if ( m_bReadOnly ) + { + implEnable( m_pBrowseButton, false ); + implEnable( m_pAdditionalBrowseButton, false ); + } + else + { + implEnable( m_pBrowseButton, m_nEnableFlags, PropertyLineElement::CompleteLine | PropertyLineElement::PrimaryButton ); + implEnable( m_pAdditionalBrowseButton, m_nEnableFlags, PropertyLineElement::CompleteLine | PropertyLineElement::SecondaryButton ); + } + } + + void OBrowserLine::EnablePropertyLine( bool _bEnable ) + { + implSetBitIfAffected( m_nEnableFlags, PropertyLineElement::CompleteLine, PropertyLineElement::CompleteLine, _bEnable ); + implUpdateEnabledDisabled(); + } + + + void OBrowserLine::EnablePropertyControls( sal_Int16 _nControls, bool _bEnable ) + { + implSetBitIfAffected( m_nEnableFlags, _nControls, PropertyLineElement::InputControl, _bEnable ); + implSetBitIfAffected( m_nEnableFlags, _nControls, PropertyLineElement::PrimaryButton, _bEnable ); + implSetBitIfAffected( m_nEnableFlags, _nControls, PropertyLineElement::SecondaryButton, _bEnable ); + implUpdateEnabledDisabled(); + } + + weld::Button& OBrowserLine::impl_ensureButton(bool bPrimary) + { + weld::Button* pButton; + if (bPrimary) + pButton = m_pBrowseButton; + else + pButton = m_pAdditionalBrowseButton; + + if (!pButton ) + { + if (bPrimary) + pButton = m_pBrowseButton = m_xBrowseButton.get(); + else + pButton = m_pAdditionalBrowseButton = m_xAdditionalBrowseButton.get(); + pButton->connect_focus_in(LINK(this, OBrowserLine, OnButtonFocus)); + pButton->connect_clicked(LINK(this, OBrowserLine, OnButtonClicked)); + } + + pButton->show(); + + return *pButton; + } + + void OBrowserLine::ShowBrowseButton( const OUString& rImageURL, bool bPrimary ) + { + weld::Button& rButton( impl_ensureButton( bPrimary ) ); + + OSL_PRECOND( !rImageURL.isEmpty(), "OBrowserLine::ShowBrowseButton: use the other version if you don't have an image!" ); + Reference<XGraphic> xGraphic; + try + { + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference< XGraphicProvider > xGraphicProvider( GraphicProvider::create(xContext) ); + + Sequence aMediaProperties{ comphelper::makePropertyValue("URL", rImageURL) }; + + xGraphic = Reference<XGraphic>(xGraphicProvider->queryGraphic(aMediaProperties), css::uno::UNO_SET_THROW); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + + rButton.set_image(xGraphic); + } + + void OBrowserLine::ShowBrowseButton(const css::uno::Reference<css::graphic::XGraphic>& rGraphic, bool bPrimary) + { + weld::Button& rButton( impl_ensureButton( bPrimary ) ); + rButton.set_image(rGraphic); + } + + void OBrowserLine::ShowBrowseButton( bool bPrimary ) + { + impl_ensureButton(bPrimary); + } + + void OBrowserLine::implHideBrowseButton(bool bPrimary) + { + if (bPrimary) + { + if (m_pBrowseButton) + { + m_pBrowseButton->hide(); + m_pBrowseButton->connect_focus_in(Link<weld::Widget&, void>()); + m_pBrowseButton = nullptr; + } + } + else + { + if (m_pAdditionalBrowseButton) + { + m_pAdditionalBrowseButton->hide(); + m_pAdditionalBrowseButton->connect_focus_in(Link<weld::Widget&, void>()); + m_pAdditionalBrowseButton = nullptr; + } + } + } + + void OBrowserLine::HideBrowseButton(bool bPrimary) + { + implHideBrowseButton(bPrimary); + } + + void OBrowserLine::SetTitleWidth(sal_uInt16 nWidth) + { + int nMinDotsWidth = m_xFtTitle->get_pixel_size("...").Width(); + if (m_nNameWidth != nWidth + nMinDotsWidth) + m_nNameWidth = nWidth + nMinDotsWidth; + FullFillTitleString(); + } + + void OBrowserLine::SetClickListener( IButtonClickListener* _pListener ) + { + m_pClickListener = _pListener; + } + + IMPL_LINK(OBrowserLine, OnButtonClicked, weld::Button&, rButton, void) + { + if ( m_pClickListener ) + m_pClickListener->buttonClicked(this, &rButton == m_pBrowseButton); + } + + IMPL_LINK_NOARG( OBrowserLine, OnButtonFocus, weld::Widget&, void ) + { + if ( m_xControl.is() ) + { + try + { + Reference< XPropertyControlContext > xContext( m_xControl->getControlContext(), css::uno::UNO_SET_THROW ); + xContext->focusGained( m_xControl ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + } + +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/browserline.hxx b/extensions/source/propctrlr/browserline.hxx new file mode 100644 index 000000000..db33b7a55 --- /dev/null +++ b/extensions/source/propctrlr/browserline.hxx @@ -0,0 +1,127 @@ +/* -*- 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/inspection/XPropertyControl.hpp> +#include <vcl/weld.hxx> + +namespace com::sun::star::inspection::PropertyLineElement +{ + const sal_Int16 CompleteLine = 0x4000; +} + + +namespace pcr +{ + + + class OBrowserLine; + + + class IButtonClickListener + { + public: + virtual void buttonClicked( OBrowserLine* pLine, bool bPrimary ) = 0; + + protected: + ~IButtonClickListener() {} + }; + + + class OBrowserLine + { + private: + OUString m_sEntryName; + std::unique_ptr<weld::Builder> m_xBuilder; + std::unique_ptr<weld::Container> m_xContainer; + std::unique_ptr<weld::Label> m_xFtTitle; + std::unique_ptr<weld::Button> m_xBrowseButton; + std::unique_ptr<weld::Button> m_xAdditionalBrowseButton; + css::uno::Reference< css::inspection::XPropertyControl > + m_xControl; + weld::Container* m_pInitialControlParent; + weld::Container* m_pParent; + weld::Widget* m_pControlWindow; + weld::Button* m_pBrowseButton; + weld::Button* m_pAdditionalBrowseButton; + IButtonClickListener* m_pClickListener; + sal_uInt16 m_nNameWidth; + sal_uInt16 m_nEnableFlags; + bool m_bIndentTitle; + bool m_bReadOnly; + + public: + OBrowserLine(const OUString& rEntryName, weld::Container* pParent, weld::SizeGroup* pLabelGroup, + weld::Container* pInitialControlParent); + ~OBrowserLine(); + + void setControl( const css::uno::Reference< css::inspection::XPropertyControl >& rxControl ); + const css::uno::Reference< css::inspection::XPropertyControl >& getControl() const + { + return m_xControl; + } + weld::Widget* getControlWindow() const + { + return m_pControlWindow; + } + + const OUString& GetEntryName() const { return m_sEntryName; } + + void SetComponentHelpIds(const OString& rHelpId); + + void SetTitle(const OUString& rString ); + void FullFillTitleString(); + OUString GetTitle() const; + void SetTitleWidth(sal_uInt16); + + int GetRowHeight() const { return m_xContainer->get_preferred_size().Height(); } + void Show(bool bFlag=true); + void Hide(); + + bool GrabFocus(); + void ShowBrowseButton( const OUString& rImageURL, bool bPrimary ); + void ShowBrowseButton( const css::uno::Reference<css::graphic::XGraphic>& rGraphic, bool bPrimary ); + void ShowBrowseButton( bool bPrimary ); + void HideBrowseButton( bool bPrimary ); + + void EnablePropertyControls( sal_Int16 nControls, bool bEnable ); + void EnablePropertyLine( bool bEnable ); + + void SetReadOnly( bool bReadOnly ); + + void SetClickListener( IButtonClickListener* pListener ); + + void IndentTitle( bool bIndent ); + + private: + DECL_LINK( OnButtonClicked, weld::Button&, void ); + DECL_LINK( OnButtonFocus, weld::Widget&, void ); + + void implHideBrowseButton(bool bPrimary); + void implUpdateEnabledDisabled(); + + weld::Button& impl_ensureButton(bool bPrimary); + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/browserlistbox.cxx b/extensions/source/propctrlr/browserlistbox.cxx new file mode 100644 index 000000000..04e9e44b3 --- /dev/null +++ b/extensions/source/propctrlr/browserlistbox.cxx @@ -0,0 +1,817 @@ +/* -*- 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 "browserlistbox.hxx" +#include "pcrcommon.hxx" +#include "proplinelistener.hxx" +#include "propcontrolobserver.hxx" +#include "linedescriptor.hxx" +#include "inspectorhelpwindow.hxx" + +#include <sal/log.hxx> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/inspection/PropertyControlType.hpp> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <comphelper/asyncnotification.hxx> +#include <cppuhelper/implbase.hxx> +#include <vcl/svapp.hxx> +#include <osl/mutex.hxx> + + +namespace pcr +{ + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::inspection::XPropertyControlContext; + using ::com::sun::star::uno::Reference; + using ::com::sun::star::inspection::XPropertyControl; + using ::com::sun::star::lang::DisposedException; + using ::com::sun::star::lang::XComponent; + using ::com::sun::star::uno::UNO_QUERY; + + namespace PropertyControlType = ::com::sun::star::inspection::PropertyControlType; + + namespace { + + enum ControlEventType + { + FOCUS_GAINED, + VALUE_CHANGED, + ACTIVATE_NEXT + }; + + struct ControlEvent : public ::comphelper::AnyEvent + { + Reference< XPropertyControl > xControl; + ControlEventType eType; + + ControlEvent( const Reference< XPropertyControl >& _rxControl, ControlEventType _eType ) + :xControl( _rxControl ) + ,eType( _eType ) + { + } + }; + + class SharedNotifier + { + private: + static ::osl::Mutex& getMutex(); + static ::rtl::Reference< ::comphelper::AsyncEventNotifier > s_pNotifier; + + public: + SharedNotifier(const SharedNotifier&) = delete; + SharedNotifier& operator=(const SharedNotifier&) = delete; + static const ::rtl::Reference< ::comphelper::AsyncEventNotifier >& + getNotifier(); + }; + + } + + ::rtl::Reference< ::comphelper::AsyncEventNotifier > SharedNotifier::s_pNotifier; + + + ::osl::Mutex& SharedNotifier::getMutex() + { + static ::osl::Mutex s_aMutex; + return s_aMutex; + } + + + const ::rtl::Reference< ::comphelper::AsyncEventNotifier >& SharedNotifier::getNotifier() + { + ::osl::MutexGuard aGuard( getMutex() ); + if ( !s_pNotifier.is() ) + { + s_pNotifier.set( + new ::comphelper::AsyncEventNotifier("browserlistbox")); + s_pNotifier->launch(); + //TODO: a protocol is missing how to join with the launched + // thread before exit(3), to ensure the thread is no longer + // relying on any infrastructure while that infrastructure is + // being shut down in atexit handlers + } + return s_pNotifier; + } + + + /** implementation for of <type scope="css::inspection">XPropertyControlContext</type> + which forwards all events to a non-UNO version of this interface + */ + typedef ::cppu::WeakImplHelper< XPropertyControlContext > PropertyControlContext_Impl_Base; + class PropertyControlContext_Impl :public PropertyControlContext_Impl_Base + ,public ::comphelper::IEventProcessor + { + public: + enum NotificationMode + { + eSynchronously, + eAsynchronously + }; + + private: + OBrowserListBox* m_pContext; + NotificationMode m_eMode; + + public: + /** creates an instance + @param _rContextImpl + the instance to delegate events to + */ + explicit PropertyControlContext_Impl( OBrowserListBox& _rContextImpl ); + + /** disposes the context. + + When you call this method, all subsequent callbacks to the + <type scope="css::inspection">XPropertyControlContext</type> methods + will throw a <type scope="css::lang">DisposedException</type>. + */ + void dispose(); + + /** sets the notification mode, so that notifications received from the controls are + forwarded to our OBrowserListBox either synchronously or asynchronously + @param _eMode + the new notification mode + */ + void setNotificationMode( NotificationMode _eMode ); + + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + protected: + virtual ~PropertyControlContext_Impl() override; + + // XPropertyControlObserver + virtual void SAL_CALL focusGained( const Reference< XPropertyControl >& Control ) override; + virtual void SAL_CALL valueChanged( const Reference< XPropertyControl >& Control ) override; + // XPropertyControlContext + virtual void SAL_CALL activateNextControl( const Reference< XPropertyControl >& CurrentControl ) override; + + // IEventProcessor + virtual void processEvent( const ::comphelper::AnyEvent& _rEvent ) override; + + private: + /** processes the given event, i.e. notifies it to our OBrowserListBox + @param _rEvent + the event no notify + @precond + our mutex (well, the SolarMutex) is locked + */ + void impl_processEvent_throw( const ::comphelper::AnyEvent& _rEvent ); + + /** checks whether the instance is already disposed + */ + bool impl_isDisposed_nothrow() const { return m_pContext == nullptr; } + + /** notifies the given event originating from the given control + @throws DisposedException + @param _rxControl + @param _eType + */ + void impl_notify_throw( const Reference< XPropertyControl >& _rxControl, ControlEventType _eType ); + }; + + PropertyControlContext_Impl::PropertyControlContext_Impl( OBrowserListBox& _rContextImpl ) + : m_pContext( &_rContextImpl ) + , m_eMode( eAsynchronously ) + { + } + + PropertyControlContext_Impl::~PropertyControlContext_Impl() + { + if ( !impl_isDisposed_nothrow() ) + dispose(); + } + + void PropertyControlContext_Impl::dispose() + { + SolarMutexGuard aGuard; + if ( impl_isDisposed_nothrow() ) + return; + + SharedNotifier::getNotifier()->removeEventsForProcessor( this ); + m_pContext = nullptr; + } + + void PropertyControlContext_Impl::setNotificationMode( NotificationMode _eMode ) + { + SolarMutexGuard aGuard; + m_eMode = _eMode; + } + + void PropertyControlContext_Impl::impl_notify_throw( const Reference< XPropertyControl >& _rxControl, ControlEventType _eType ) + { + ::comphelper::AnyEventRef pEvent; + + { + SolarMutexGuard aGuard; + if ( impl_isDisposed_nothrow() ) + throw DisposedException( OUString(), *this ); + pEvent = new ControlEvent( _rxControl, _eType ); + + if ( m_eMode == eSynchronously ) + { + impl_processEvent_throw( *pEvent ); + return; + } + } + + SharedNotifier::getNotifier()->addEvent( pEvent, this ); + } + + void SAL_CALL PropertyControlContext_Impl::focusGained( const Reference< XPropertyControl >& Control ) + { + impl_notify_throw( Control, FOCUS_GAINED ); + } + + void SAL_CALL PropertyControlContext_Impl::valueChanged( const Reference< XPropertyControl >& Control ) + { + impl_notify_throw( Control, VALUE_CHANGED ); + } + + void SAL_CALL PropertyControlContext_Impl::activateNextControl( const Reference< XPropertyControl >& CurrentControl ) + { + impl_notify_throw( CurrentControl, ACTIVATE_NEXT ); + } + + void SAL_CALL PropertyControlContext_Impl::acquire() noexcept + { + PropertyControlContext_Impl_Base::acquire(); + } + + void SAL_CALL PropertyControlContext_Impl::release() noexcept + { + PropertyControlContext_Impl_Base::release(); + } + + void PropertyControlContext_Impl::processEvent( const ::comphelper::AnyEvent& _rEvent ) + { + SolarMutexGuard aGuard; + if ( impl_isDisposed_nothrow() ) + return; + + try + { + impl_processEvent_throw( _rEvent ); + } + catch( const Exception& ) + { + // can't handle otherwise, since our caller (the notification thread) does not allow + // for exceptions (it could itself abort only) + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + void PropertyControlContext_Impl::impl_processEvent_throw( const ::comphelper::AnyEvent& _rEvent ) + { + const ControlEvent& rControlEvent = static_cast< const ControlEvent& >( _rEvent ); + switch ( rControlEvent.eType ) + { + case FOCUS_GAINED: + m_pContext->focusGained( rControlEvent.xControl ); + break; + case VALUE_CHANGED: + m_pContext->valueChanged( rControlEvent.xControl ); + break; + case ACTIVATE_NEXT: + m_pContext->activateNextControl( rControlEvent.xControl ); + break; + } + } + + OBrowserListBox::OBrowserListBox(weld::Builder& rBuilder, weld::Container* pContainer) + : m_xScrolledWindow(rBuilder.weld_scrolled_window("scrolledwindow")) + , m_xLinesPlayground(rBuilder.weld_container("playground")) + , m_xSizeGroup(rBuilder.create_size_group()) + , m_xHelpWindow(new InspectorHelpWindow(rBuilder)) + , m_pInitialControlParent(pContainer) + , m_pLineListener(nullptr) + , m_pControlObserver( nullptr ) + , m_nTheNameSize(0) + , m_nRowHeight(0) + , m_pControlContextImpl( new PropertyControlContext_Impl( *this ) ) + { + m_xScrolledWindow->set_size_request(-1, m_xScrolledWindow->get_text_height() * 20); + } + + OBrowserListBox::~OBrowserListBox() + { + OSL_ENSURE( !IsModified(), "OBrowserListBox::~OBrowserListBox: still modified - should have been committed before!" ); + + // doing the commit here, while we, as well as our owner, as well as some other components, + // are already "half dead" (means within their dtor) is potentially dangerous. + // By definition, CommitModified has to be called (if necessary) before destruction + m_pControlContextImpl->dispose(); + m_pControlContextImpl.clear(); + + Clear(); + } + + bool OBrowserListBox::IsModified() const + { + bool bModified = false; + + if (m_xScrolledWindow->get_visible() && m_xActiveControl.is()) + bModified = m_xActiveControl->isModified(); + + return bModified; + } + + void OBrowserListBox::CommitModified( ) + { + if ( !(IsModified() && m_xActiveControl.is()) ) + return; + + // for the time of this commit, notify all events synchronously + // #i63814# + m_pControlContextImpl->setNotificationMode( PropertyControlContext_Impl::eSynchronously ); + try + { + m_xActiveControl->notifyModifiedValue(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + m_pControlContextImpl->setNotificationMode( PropertyControlContext_Impl::eAsynchronously ); + } + + void OBrowserListBox::SetListener( IPropertyLineListener* _pListener ) + { + m_pLineListener = _pListener; + } + + void OBrowserListBox::SetObserver( IPropertyControlObserver* _pObserver ) + { + m_pControlObserver = _pObserver; + } + + void OBrowserListBox::EnableHelpSection( bool _bEnable ) + { + m_xHelpWindow->Show( _bEnable ); + } + + bool OBrowserListBox::HasHelpSection() const + { + return m_xHelpWindow->IsVisible(); + } + + void OBrowserListBox::SetHelpText( const OUString& _rHelpText ) + { + OSL_ENSURE( HasHelpSection(), "OBrowserListBox::SetHelpText: help section not visible!" ); + m_xHelpWindow->SetText( _rHelpText ); + } + + void OBrowserListBox::UpdatePlayGround() + { + for (auto& line : m_aLines) + line.pLine->SetTitleWidth(m_nTheNameSize); + } + + void OBrowserListBox::SetPropertyValue(const OUString& _rEntryName, const Any& _rValue, bool _bUnknownValue ) + { + ListBoxLines::iterator line = std::find_if(m_aLines.begin(), m_aLines.end(), + [&_rEntryName](const ListBoxLine& rLine) { return rLine.aName == _rEntryName; }); + + if ( line != m_aLines.end() ) + { + if ( _bUnknownValue ) + { + Reference< XPropertyControl > xControl( line->pLine->getControl() ); + OSL_ENSURE( xControl.is(), "OBrowserListBox::SetPropertyValue: illegal control!" ); + if ( xControl.is() ) + xControl->setValue( Any() ); + } + else + impl_setControlAsPropertyValue( *line, _rValue ); + } + } + + sal_uInt16 OBrowserListBox::GetPropertyPos( std::u16string_view _rEntryName ) const + { + sal_uInt16 nPos = 0; + for (auto const& line : m_aLines) + { + if ( line.aName == _rEntryName ) + { + return nPos; + } + ++nPos; + } + + return EDITOR_LIST_ENTRY_NOTFOUND; + } + + bool OBrowserListBox::impl_getBrowserLineForName( const OUString& _rEntryName, BrowserLinePointer& _out_rpLine ) const + { + ListBoxLines::const_iterator line = std::find_if(m_aLines.begin(), m_aLines.end(), + [&_rEntryName](const ListBoxLine& rLine) { return rLine.aName == _rEntryName; }); + + if ( line != m_aLines.end() ) + _out_rpLine = line->pLine; + else + _out_rpLine.reset(); + return bool(_out_rpLine); + } + + void OBrowserListBox::EnablePropertyControls( const OUString& _rEntryName, sal_Int16 _nControls, bool _bEnable ) + { + BrowserLinePointer pLine; + if ( impl_getBrowserLineForName( _rEntryName, pLine ) ) + pLine->EnablePropertyControls( _nControls, _bEnable ); + } + + void OBrowserListBox::EnablePropertyLine( const OUString& _rEntryName, bool _bEnable ) + { + BrowserLinePointer pLine; + if ( impl_getBrowserLineForName( _rEntryName, pLine ) ) + pLine->EnablePropertyLine( _bEnable ); + } + + Reference< XPropertyControl > OBrowserListBox::GetPropertyControl( const OUString& _rEntryName ) + { + BrowserLinePointer pLine; + if ( impl_getBrowserLineForName( _rEntryName, pLine ) ) + return pLine->getControl(); + return nullptr; + } + + void OBrowserListBox::InsertEntry(const OLineDescriptor& rPropertyData, sal_uInt16 _nPos) + { + // create a new line + BrowserLinePointer pBrowserLine = std::make_shared<OBrowserLine>(rPropertyData.sName, m_xLinesPlayground.get(), + m_xSizeGroup.get(), m_pInitialControlParent); + + // check that the name is unique + for (auto const& line : m_aLines) + { + if (line.aName == rPropertyData.sName) + { + // already have another line for this name! + assert(false); + } + } + + ListBoxLine aNewLine( rPropertyData.sName, pBrowserLine, rPropertyData.xPropertyHandler ); + ListBoxLines::size_type nInsertPos = _nPos; + if ( _nPos >= m_aLines.size() ) + { + nInsertPos = m_aLines.size(); + m_aLines.push_back( aNewLine ); + } + else + m_aLines.insert( m_aLines.begin() + _nPos, aNewLine ); + + pBrowserLine->SetTitleWidth(m_nTheNameSize); + + // initialize the entry + ChangeEntry(rPropertyData, nInsertPos); + + m_nRowHeight = std::max(m_nRowHeight, pBrowserLine->GetRowHeight() + 6); // 6 is spacing of the "playground" in browserpage.ui + m_xScrolledWindow->vadjustment_set_step_increment(m_nRowHeight); + } + + void OBrowserListBox::ShowEntry(sal_uInt16 nPos) + { + if (nPos == 0) + { + // special case the simple entry 0 situation + m_xScrolledWindow->vadjustment_set_value(0); + return; + } + + if (nPos >= m_aLines.size()) + return; + + unsigned const nWinHeight = m_xScrolledWindow->vadjustment_get_page_size(); + + auto nThumbPos = m_xScrolledWindow->vadjustment_get_value(); + int const nWinTop = nThumbPos; + int const nWinBottom = nWinTop + nWinHeight; + + auto nCtrlPosY = nPos * m_nRowHeight; + + int const nSelectedItemTop = nCtrlPosY; + int const nSelectedItemBottom = nCtrlPosY + m_nRowHeight; + bool const shouldScrollDown = nSelectedItemBottom >= nWinBottom; + bool const shouldScrollUp = nSelectedItemTop <= nWinTop; + bool const isNeedToScroll = shouldScrollDown || shouldScrollUp; + + if (!isNeedToScroll) + return; + + if (shouldScrollDown) + { + int nOffset = nSelectedItemBottom - nWinBottom; + nThumbPos += nOffset; + } + else + { + int nOffset = nWinTop - nSelectedItemTop; + nThumbPos -= nOffset; + if(nThumbPos < 0) + nThumbPos = 0; + } + m_xScrolledWindow->vadjustment_set_value(nThumbPos); + } + + void OBrowserListBox::buttonClicked( OBrowserLine* _pLine, bool _bPrimary ) + { + DBG_ASSERT( _pLine, "OBrowserListBox::buttonClicked: invalid browser line!" ); + if ( _pLine && m_pLineListener ) + { + m_pLineListener->Clicked( _pLine->GetEntryName(), _bPrimary ); + } + } + + void OBrowserListBox::impl_setControlAsPropertyValue( const ListBoxLine& _rLine, const Any& _rPropertyValue ) + { + Reference< XPropertyControl > xControl( _rLine.pLine->getControl() ); + try + { + if ( _rPropertyValue.getValueType().equals( _rLine.pLine->getControl()->getValueType() ) ) + { + xControl->setValue( _rPropertyValue ); + } + else + { + SAL_WARN_IF( !_rLine.xHandler.is(), "extensions.propctrlr", + "OBrowserListBox::impl_setControlAsPropertyValue: no handler -> no conversion (property: '" + << _rLine.pLine->GetEntryName() << "')!" ); + if ( _rLine.xHandler.is() ) + { + Any aControlValue = _rLine.xHandler->convertToControlValue( + _rLine.pLine->GetEntryName(), _rPropertyValue, xControl->getValueType() ); + xControl->setValue( aControlValue ); + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + Any OBrowserListBox::impl_getControlAsPropertyValue( const ListBoxLine& _rLine ) + { + Reference< XPropertyControl > xControl( _rLine.pLine->getControl() ); + Any aPropertyValue; + try + { + SAL_WARN_IF( !_rLine.xHandler.is(), "extensions.propctrlr", + "OBrowserListBox::impl_getControlAsPropertyValue: no handler -> no conversion (property: '" + << _rLine.pLine->GetEntryName() << "')!" ); + if ( _rLine.xHandler.is() ) + aPropertyValue = _rLine.xHandler->convertToPropertyValue( _rLine.pLine->GetEntryName(), xControl->getValue() ); + else + aPropertyValue = xControl->getValue(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + return aPropertyValue; + } + + sal_uInt16 OBrowserListBox::impl_getControlPos( const Reference< XPropertyControl >& _rxControl ) const + { + sal_uInt16 nPos = 0; + for (auto const& search : m_aLines) + { + if ( search.pLine->getControl().get() == _rxControl.get() ) + return nPos; + ++nPos; + } + OSL_FAIL( "OBrowserListBox::impl_getControlPos: invalid control - not part of any of our lines!" ); + return sal_uInt16(-1); + } + + + void OBrowserListBox::focusGained( const Reference< XPropertyControl >& _rxControl ) + { + DBG_TESTSOLARMUTEX(); + + DBG_ASSERT( _rxControl.is(), "OBrowserListBox::focusGained: invalid event source!" ); + if ( !_rxControl.is() ) + return; + + if ( m_pControlObserver ) + m_pControlObserver->focusGained( _rxControl ); + + m_xActiveControl = _rxControl; + ShowEntry( impl_getControlPos( m_xActiveControl ) ); + } + + + void OBrowserListBox::valueChanged( const Reference< XPropertyControl >& _rxControl ) + { + DBG_TESTSOLARMUTEX(); + + DBG_ASSERT( _rxControl.is(), "OBrowserListBox::valueChanged: invalid event source!" ); + if ( !_rxControl.is() ) + return; + + if ( m_pControlObserver ) + m_pControlObserver->valueChanged( _rxControl ); + + if ( m_pLineListener ) + { + const ListBoxLine& rLine = m_aLines[ impl_getControlPos( _rxControl ) ]; + m_pLineListener->Commit( + rLine.pLine->GetEntryName(), + impl_getControlAsPropertyValue( rLine ) + ); + } + } + + + void OBrowserListBox::activateNextControl( const Reference< XPropertyControl >& _rxCurrentControl ) + { + DBG_TESTSOLARMUTEX(); + + sal_uInt16 nLine = impl_getControlPos( _rxCurrentControl ); + + // cycle forwards, 'til we've the next control which can grab the focus + ++nLine; + while ( static_cast< size_t >( nLine ) < m_aLines.size() ) + { + if ( m_aLines[nLine].pLine->GrabFocus() ) + break; + ++nLine; + } + + // wrap around? + if ( ( static_cast< size_t >( nLine ) >= m_aLines.size() ) && ( !m_aLines.empty() ) ) + m_aLines[0].pLine->GrabFocus(); + } + + + namespace + { + + void lcl_implDisposeControl_nothrow( const Reference< XPropertyControl >& _rxControl ) + { + if ( !_rxControl.is() ) + return; + try + { + _rxControl->setControlContext( nullptr ); + Reference< XComponent > xControlComponent( _rxControl, UNO_QUERY ); + if ( xControlComponent.is() ) + xControlComponent->dispose(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + } + + void OBrowserListBox::Clear() + { + for (auto const& line : m_aLines) + { + // hide the line + line.pLine->Hide(); + // reset the listener + lcl_implDisposeControl_nothrow( line.pLine->getControl() ); + } + + clearContainer( m_aLines ); + } + + bool OBrowserListBox::RemoveEntry( const OUString& _rName ) + { + ListBoxLines::iterator it = std::find_if(m_aLines.begin(), m_aLines.end(), + [&_rName](const ListBoxLine& rLine) { return rLine.aName == _rName; }); + + if ( it == m_aLines.end() ) + return false; + + m_aLines.erase( it ); + + return true; + } + + void OBrowserListBox::ChangeEntry( const OLineDescriptor& rPropertyData, ListBoxLines::size_type nPos ) + { + OSL_PRECOND( rPropertyData.Control.is(), "OBrowserListBox::ChangeEntry: invalid control!" ); + if ( !rPropertyData.Control.is() ) + return; + + if ( nPos == EDITOR_LIST_REPLACE_EXISTING ) + nPos = GetPropertyPos( rPropertyData.sName ); + + if ( nPos >= m_aLines.size() ) + return; + + // the current line and control + ListBoxLine& rLine = m_aLines[nPos]; + + // the old control and some data about it + Reference< XPropertyControl > xControl = rLine.pLine->getControl(); + + // clean up the old control + lcl_implDisposeControl_nothrow( xControl ); + + // set the new control at the line + rLine.pLine->setControl( rPropertyData.Control ); + xControl = rLine.pLine->getControl(); + + if ( xControl.is() ) + xControl->setControlContext( m_pControlContextImpl ); + + // the initial property value + if ( rPropertyData.bUnknownValue ) + xControl->setValue( Any() ); + else + impl_setControlAsPropertyValue( rLine, rPropertyData.aValue ); + + rLine.pLine->SetTitle(rPropertyData.DisplayName); + rLine.xHandler = rPropertyData.xPropertyHandler; + + if ( rPropertyData.HasPrimaryButton ) + { + if ( !rPropertyData.PrimaryButtonImageURL.isEmpty() ) + rLine.pLine->ShowBrowseButton( rPropertyData.PrimaryButtonImageURL, true ); + else if ( rPropertyData.PrimaryButtonImage.is() ) + rLine.pLine->ShowBrowseButton( rPropertyData.PrimaryButtonImage, true ); + else + rLine.pLine->ShowBrowseButton( true ); + + if ( rPropertyData.HasSecondaryButton ) + { + if ( !rPropertyData.SecondaryButtonImageURL.isEmpty() ) + rLine.pLine->ShowBrowseButton( rPropertyData.SecondaryButtonImageURL, false ); + else if ( rPropertyData.SecondaryButtonImage.is() ) + rLine.pLine->ShowBrowseButton( rPropertyData.SecondaryButtonImage, false ); + else + rLine.pLine->ShowBrowseButton( false ); + } + else + rLine.pLine->HideBrowseButton( false ); + + rLine.pLine->SetClickListener( this ); + } + else + { + rLine.pLine->HideBrowseButton( true ); + rLine.pLine->HideBrowseButton( false ); + } + + DBG_ASSERT( ( rPropertyData.IndentLevel == 0 ) || ( rPropertyData.IndentLevel == 1 ), + "OBrowserListBox::ChangeEntry: unsupported indent level!" ); + rLine.pLine->IndentTitle( rPropertyData.IndentLevel > 0 ); + + rLine.pLine->SetComponentHelpIds( + HelpIdUrl::getHelpId( rPropertyData.HelpURL ) + ); + + if ( rPropertyData.bReadOnly ) + { + rLine.pLine->SetReadOnly( true ); + + // user controls (i.e. the ones not provided by the usual + // XPropertyControlFactory) have no chance to know that they should be read-only, + // since XPropertyHandler::describePropertyLine does not transport this + // information. + // So, we manually switch this to read-only. + if ( xControl.is() && ( xControl->getControlType() == PropertyControlType::Unknown ) ) + { + weld::Widget* pWindow = rLine.pLine->getControlWindow(); + weld::Entry* pControlWindowAsEdit = dynamic_cast<weld::Entry*>(pWindow); + if (pControlWindowAsEdit) + pControlWindowAsEdit->set_editable(false); + else + pWindow->set_sensitive(false); + } + } + + sal_uInt16 nTextWidth = m_xLinesPlayground->get_pixel_size(rPropertyData.DisplayName).Width(); + if (m_nTheNameSize< nTextWidth) + { + m_nTheNameSize = nTextWidth; + UpdatePlayGround(); + } + } +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/browserlistbox.hxx b/extensions/source/propctrlr/browserlistbox.hxx new file mode 100644 index 000000000..1a7232612 --- /dev/null +++ b/extensions/source/propctrlr/browserlistbox.hxx @@ -0,0 +1,163 @@ +/* -*- 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 "browserline.hxx" + +#include <com/sun/star/inspection/XPropertyControl.hpp> +#include <com/sun/star/inspection/XPropertyHandler.hpp> +#include <vcl/weld.hxx> +#include <rtl/ref.hxx> + +#include <memory> +#include <vector> + +#define EDITOR_LIST_REPLACE_EXISTING \ + std::numeric_limits<ListBoxLines::size_type>::max() + +namespace pcr +{ + + + class IPropertyLineListener; + class IPropertyControlObserver; + struct OLineDescriptor; + class InspectorHelpWindow; + class PropertyControlContext_Impl; + + + // administrative structures for OBrowserListBox + + typedef std::shared_ptr< OBrowserLine > BrowserLinePointer; + struct ListBoxLine + { + OUString aName; + BrowserLinePointer pLine; + css::uno::Reference< css::inspection::XPropertyHandler > + xHandler; + + ListBoxLine( const OUString& rName, const BrowserLinePointer& _pLine, const css::uno::Reference< css::inspection::XPropertyHandler >& _rxHandler ) + : aName( rName ), + pLine( _pLine ), + xHandler( _rxHandler ) + { + } + }; + typedef std::vector< ListBoxLine > ListBoxLines; + + + class OBrowserListBox final : public IButtonClickListener + { + std::unique_ptr<weld::ScrolledWindow> m_xScrolledWindow; + std::unique_ptr<weld::Container> m_xLinesPlayground; + std::unique_ptr<weld::SizeGroup> m_xSizeGroup; + std::unique_ptr<InspectorHelpWindow> m_xHelpWindow; + weld::Container* m_pInitialControlParent; + ListBoxLines m_aLines; + IPropertyLineListener* m_pLineListener; + IPropertyControlObserver* m_pControlObserver; + css::uno::Reference< css::inspection::XPropertyControl > + m_xActiveControl; + sal_uInt16 m_nTheNameSize; + int m_nRowHeight; + ::rtl::Reference< PropertyControlContext_Impl > + m_pControlContextImpl; + + void UpdatePlayGround(); + void ShowEntry(sal_uInt16 nPos); + + public: + explicit OBrowserListBox(weld::Builder& rBuilder, weld::Container* pContainer); + ~OBrowserListBox(); + + void SetListener( IPropertyLineListener* _pListener ); + void SetObserver( IPropertyControlObserver* _pObserver ); + + void EnableHelpSection( bool _bEnable ); + bool HasHelpSection() const; + void SetHelpText( const OUString& _rHelpText ); + + void Clear(); + + void InsertEntry( const OLineDescriptor&, sal_uInt16 nPos ); + bool RemoveEntry( const OUString& _rName ); + void ChangeEntry( const OLineDescriptor&, ListBoxLines::size_type nPos ); + + void SetPropertyValue( const OUString& rEntryName, const css::uno::Any& rValue, bool _bUnknownValue ); + sal_uInt16 GetPropertyPos( std::u16string_view rEntryName ) const; + css::uno::Reference< css::inspection::XPropertyControl > + GetPropertyControl( const OUString& rEntryName ); + void EnablePropertyControls( const OUString& _rEntryName, sal_Int16 _nControls, bool _bEnable ); + void EnablePropertyLine( const OUString& _rEntryName, bool _bEnable ); + + bool IsModified( ) const; + void CommitModified( ); + + /// @throws css::uno::RuntimeException + void focusGained( const css::uno::Reference< css::inspection::XPropertyControl >& Control ); + /// @throws css::uno::RuntimeException + void valueChanged( const css::uno::Reference< css::inspection::XPropertyControl >& Control ); + /// @throws css::uno::RuntimeException + void activateNextControl( const css::uno::Reference< css::inspection::XPropertyControl >& CurrentControl ); + + private: + // IButtonClickListener + void buttonClicked( OBrowserLine* _pLine, bool _bPrimary ) override; + + /** retrieves the index of a given control in our line list + @param _rxControl + The control to lookup. Must denote a control of one of the lines in ->m_aLines + */ + sal_uInt16 impl_getControlPos( const css::uno::Reference< css::inspection::XPropertyControl >& _rxControl ) const; + + /** sets the given property value at the given control, after converting it as necessary + @param _rLine + The line whose at which the value is to be set. + @param _rPropertyValue + the property value to set. If it's not compatible with the control value, + it will be converted, using <member>XPropertyHandler::convertToControlValue</member> + */ + static void impl_setControlAsPropertyValue( const ListBoxLine& _rLine, const css::uno::Any& _rPropertyValue ); + + /** retrieves the value for the given control, as a property value, after converting it as necessary + @param _rLine + The line whose at which the value is to be set. + */ + static css::uno::Any + impl_getControlAsPropertyValue( const ListBoxLine& _rLine ); + + /** retrieves the ->BrowserLinePointer for a given entry name + @param _rEntryName + the name whose line is to be looked up + @param _out_rpLine + contains, upon return, the found browser line, if any + @return + <TRUE/> if and only if a non-<NULL/> line for the given entry name could be + found. + */ + bool impl_getBrowserLineForName( const OUString& _rEntryName, BrowserLinePointer& _out_rpLine ) const; + }; + + +} // namespace pcr + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/browserpage.cxx b/extensions/source/propctrlr/browserpage.cxx new file mode 100644 index 000000000..5502418bd --- /dev/null +++ b/extensions/source/propctrlr/browserpage.cxx @@ -0,0 +1,41 @@ +/* -*- 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 <vcl/svapp.hxx> +#include "browserpage.hxx" + +namespace pcr +{ + OBrowserPage::OBrowserPage(weld::Container* pParent, weld::Container* pInitialControlContainer) + : m_pParent(pParent) + , m_xBuilder(Application::CreateBuilder(pParent, "modules/spropctrlr/ui/browserpage.ui")) + , m_xContainer(m_xBuilder->weld_container("BrowserPage")) + , m_xListBox(new OBrowserListBox(*m_xBuilder, pInitialControlContainer)) + { + } + + OBrowserPage::~OBrowserPage() + { + if (m_pParent) + detach(); + assert(!m_pParent); + } +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/browserpage.hxx b/extensions/source/propctrlr/browserpage.hxx new file mode 100644 index 000000000..0db6cc3c7 --- /dev/null +++ b/extensions/source/propctrlr/browserpage.hxx @@ -0,0 +1,61 @@ +/* -*- 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 <o3tl/deleter.hxx> +#include "browserlistbox.hxx" + +namespace pcr +{ + class OBrowserPage + { + private: + weld::Container* m_pParent; + std::unique_ptr<weld::Builder> m_xBuilder; + std::unique_ptr<weld::Container> m_xContainer; + std::unique_ptr<OBrowserListBox, o3tl::default_delete<OBrowserListBox>> m_xListBox; + + public: + // TODO inherit from BuilderPage + explicit OBrowserPage(weld::Container* pParent, weld::Container* pContainer); + ~OBrowserPage(); + + void SetHelpId(const OString& rHelpId) { m_xContainer->set_help_id(rHelpId); } + + OBrowserListBox& getListBox() { return *m_xListBox; } + const OBrowserListBox& getListBox() const { return *m_xListBox; } + + void detach() + { + assert(m_pParent && "already attached"); + m_pParent->move(m_xContainer.get(), nullptr); + m_pParent = nullptr; + } + + void reattach(weld::Container* pNewParent) + { + assert(!m_pParent && "already attached"); + m_pParent = pNewParent; + m_pParent->move(m_xContainer.get(), pNewParent); + } + }; +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/browserview.cxx b/extensions/source/propctrlr/browserview.cxx new file mode 100644 index 000000000..e7b7cb690 --- /dev/null +++ b/extensions/source/propctrlr/browserview.cxx @@ -0,0 +1,64 @@ +/* -*- 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 "browserview.hxx" +#include "propertyeditor.hxx" +#include <helpids.h> +#include <memory> + +namespace pcr +{ + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + + OPropertyBrowserView::OPropertyBrowserView(const css::uno::Reference<css::uno::XComponentContext>& rContext, weld::Builder& rBuilder) + : m_xPropBox(new OPropertyEditor(rContext, rBuilder)) + , m_nActivePage(0) + { + m_xPropBox->SetHelpId(HID_FM_PROPDLG_TABCTR); + m_xPropBox->setPageActivationHandler(LINK(this, OPropertyBrowserView, OnPageActivation)); + } + + IMPL_LINK(OPropertyBrowserView, OnPageActivation, const OString&, rNewPage, void) + { + m_nActivePage = rNewPage.toUInt32(); + m_aPageActivationHandler.Call(nullptr); + } + + OPropertyBrowserView::~OPropertyBrowserView() + { + sal_uInt16 nTmpPage = m_xPropBox->GetCurPage(); + if (nTmpPage) + m_nActivePage = nTmpPage; + } + + void OPropertyBrowserView::activatePage(sal_uInt16 _nPage) + { + m_nActivePage = _nPage; + getPropertyBox().SetPage(m_nActivePage); + } + + css::awt::Size OPropertyBrowserView::getMinimumSize() const + { + ::Size aSize = m_xPropBox->get_preferred_size(); + return css::awt::Size(aSize.Width(), aSize.Height()); + } +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/browserview.hxx b/extensions/source/propctrlr/browserview.hxx new file mode 100644 index 000000000..7a0711fb1 --- /dev/null +++ b/extensions/source/propctrlr/browserview.hxx @@ -0,0 +1,56 @@ +/* -*- 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/Size.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <o3tl/deleter.hxx> +#include <vcl/weld.hxx> + +namespace pcr +{ + class OPropertyEditor; + class OPropertyBrowserView final + { + std::unique_ptr<OPropertyEditor, o3tl::default_delete<OPropertyEditor>> m_xPropBox; + sal_uInt16 m_nActivePage; + Link<LinkParamNone*,void> m_aPageActivationHandler; + + public: + explicit OPropertyBrowserView(const css::uno::Reference<css::uno::XComponentContext>& rContext, weld::Builder& rBuilder); + ~OPropertyBrowserView(); + + OPropertyEditor& getPropertyBox() { return *m_xPropBox; } + + // page handling + sal_uInt16 getActivePage() const { return m_nActivePage; } + void activatePage(sal_uInt16 _nPage); + + void setPageActivationHandler(const Link<LinkParamNone*,void>& _rHdl) { m_aPageActivationHandler = _rHdl; } + + css::awt::Size getMinimumSize() const; + + private: + DECL_LINK(OnPageActivation, const OString&, void); + }; + +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/buttonnavigationhandler.cxx b/extensions/source/propctrlr/buttonnavigationhandler.cxx new file mode 100644 index 000000000..618d9db46 --- /dev/null +++ b/extensions/source/propctrlr/buttonnavigationhandler.cxx @@ -0,0 +1,268 @@ +/* -*- 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 "buttonnavigationhandler.hxx" +#include "formstrings.hxx" +#include "formmetadata.hxx" +#include "pushbuttonnavigation.hxx" + +#include <com/sun/star/form/inspection/FormComponentPropertyHandler.hpp> + +namespace pcr +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::script; + using namespace ::com::sun::star::frame; + using namespace ::com::sun::star::inspection; + + ButtonNavigationHandler::ButtonNavigationHandler( const Reference< XComponentContext >& _rxContext ) + :PropertyHandlerComponent( _rxContext ) + { + + m_xSlaveHandler = css::form::inspection::FormComponentPropertyHandler::create( m_xContext ); + } + + + ButtonNavigationHandler::~ButtonNavigationHandler( ) + { + } + + + OUString ButtonNavigationHandler::getImplementationName( ) + { + return "com.sun.star.comp.extensions.ButtonNavigationHandler"; + } + + + Sequence< OUString > ButtonNavigationHandler::getSupportedServiceNames( ) + { + return { "com.sun.star.form.inspection.ButtonNavigationHandler" }; + } + + + void SAL_CALL ButtonNavigationHandler::inspect( const Reference< XInterface >& _rxIntrospectee ) + { + PropertyHandlerComponent::inspect( _rxIntrospectee ); + m_xSlaveHandler->inspect( _rxIntrospectee ); + } + + + PropertyState SAL_CALL ButtonNavigationHandler::getPropertyState( const OUString& _rPropertyName ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + PropertyState eState = PropertyState_DIRECT_VALUE; + switch ( nPropId ) + { + case PROPERTY_ID_BUTTONTYPE: + { + PushButtonNavigation aHelper( m_xComponent ); + eState = aHelper.getCurrentButtonTypeState(); + } + break; + case PROPERTY_ID_TARGET_URL: + { + PushButtonNavigation aHelper( m_xComponent ); + eState = aHelper.getCurrentTargetURLState(); + } + break; + + default: + OSL_FAIL( "ButtonNavigationHandler::getPropertyState: cannot handle this property!" ); + break; + } + + return eState; + } + + + Any SAL_CALL ButtonNavigationHandler::getPropertyValue( const OUString& _rPropertyName ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + Any aReturn; + switch ( nPropId ) + { + case PROPERTY_ID_BUTTONTYPE: + { + PushButtonNavigation aHelper( m_xComponent ); + aReturn = aHelper.getCurrentButtonType(); + } + break; + + case PROPERTY_ID_TARGET_URL: + { + PushButtonNavigation aHelper( m_xComponent ); + aReturn = aHelper.getCurrentTargetURL(); + } + break; + + default: + OSL_FAIL( "ButtonNavigationHandler::getPropertyValue: cannot handle this property!" ); + break; + } + + return aReturn; + } + + + void SAL_CALL ButtonNavigationHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + switch ( nPropId ) + { + case PROPERTY_ID_BUTTONTYPE: + { + PushButtonNavigation aHelper( m_xComponent ); + aHelper.setCurrentButtonType( _rValue ); + } + break; + + case PROPERTY_ID_TARGET_URL: + { + PushButtonNavigation aHelper( m_xComponent ); + aHelper.setCurrentTargetURL( _rValue ); + } + break; + + default: + OSL_FAIL( "ButtonNavigationHandler::setPropertyValue: cannot handle this id!" ); + } + } + + + bool ButtonNavigationHandler::isNavigationCapableButton( const Reference< XPropertySet >& _rxComponent ) + { + Reference< XPropertySetInfo > xPSI; + if ( _rxComponent.is() ) + xPSI = _rxComponent->getPropertySetInfo(); + + return xPSI.is() + && xPSI->hasPropertyByName( PROPERTY_TARGET_URL ) + && xPSI->hasPropertyByName( PROPERTY_BUTTONTYPE ); + } + + + Sequence< Property > ButtonNavigationHandler::doDescribeSupportedProperties() const + { + std::vector< Property > aProperties; + + if ( isNavigationCapableButton( m_xComponent ) ) + { + addStringPropertyDescription( aProperties, PROPERTY_TARGET_URL ); + implAddPropertyDescription( aProperties, PROPERTY_BUTTONTYPE, ::cppu::UnoType<sal_Int32>::get() ); + } + + if ( aProperties.empty() ) + return Sequence< Property >(); + return comphelper::containerToSequence(aProperties); + } + + + Sequence< OUString > SAL_CALL ButtonNavigationHandler::getActuatingProperties( ) + { + Sequence< OUString > aActuating{ PROPERTY_BUTTONTYPE, PROPERTY_TARGET_URL }; + return aActuating; + } + + + InteractiveSelectionResult SAL_CALL ButtonNavigationHandler::onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, Any& _rData, const Reference< XObjectInspectorUI >& _rxInspectorUI ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + InteractiveSelectionResult eReturn( InteractiveSelectionResult_Cancelled ); + + switch ( nPropId ) + { + case PROPERTY_ID_TARGET_URL: + eReturn = m_xSlaveHandler->onInteractivePropertySelection( _rPropertyName, _bPrimary, _rData, _rxInspectorUI ); + break; + default: + eReturn = PropertyHandlerComponent::onInteractivePropertySelection( _rPropertyName, _bPrimary, _rData, _rxInspectorUI ); + break; + } + + return eReturn; + } + + + void SAL_CALL ButtonNavigationHandler::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& /*_rNewValue*/, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool /*_bFirstTimeInit*/ ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwRuntime( _rActuatingPropertyName ) ); + switch ( nPropId ) + { + case PROPERTY_ID_BUTTONTYPE: + { + PushButtonNavigation aHelper( m_xComponent ); + _rxInspectorUI->enablePropertyUI( PROPERTY_TARGET_URL, aHelper.currentButtonTypeIsOpenURL() ); + } + break; + + case PROPERTY_ID_TARGET_URL: + { + PushButtonNavigation aHelper( m_xComponent ); + _rxInspectorUI->enablePropertyUI( PROPERTY_TARGET_FRAME, aHelper.hasNonEmptyCurrentTargetURL() ); + } + break; + + default: + OSL_FAIL( "ButtonNavigationHandler::actuatingPropertyChanged: cannot handle this id!" ); + } + } + + + LineDescriptor SAL_CALL ButtonNavigationHandler::describePropertyLine( const OUString& _rPropertyName, const Reference< XPropertyControlFactory >& _rxControlFactory ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + LineDescriptor aReturn; + + switch ( nPropId ) + { + case PROPERTY_ID_TARGET_URL: + aReturn = m_xSlaveHandler->describePropertyLine( _rPropertyName, _rxControlFactory ); + break; + default: + aReturn = PropertyHandlerComponent::describePropertyLine( _rPropertyName, _rxControlFactory ); + break; + } + + return aReturn; + } + + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_ButtonNavigationHandler_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::ButtonNavigationHandler(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/buttonnavigationhandler.hxx b/extensions/source/propctrlr/buttonnavigationhandler.hxx new file mode 100644 index 000000000..c5e01e1df --- /dev/null +++ b/extensions/source/propctrlr/buttonnavigationhandler.hxx @@ -0,0 +1,71 @@ +/* -*- 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 "propertyhandler.hxx" + + +namespace pcr +{ + + /** a property handler for any virtual string properties + */ + class ButtonNavigationHandler : public PropertyHandlerComponent + { + private: + css::uno::Reference< css::inspection::XPropertyHandler > + m_xSlaveHandler; + + public: + explicit ButtonNavigationHandler( + const css::uno::Reference< css::uno::XComponentContext >& _rxContext + ); + + protected: + virtual ~ButtonNavigationHandler() override; + + static bool isNavigationCapableButton( const css::uno::Reference< css::beans::XPropertySet >& _rxComponent ); + + protected: + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override; + + // XPropertyHandler overriables + virtual void SAL_CALL inspect( const css::uno::Reference< css::uno::XInterface >& _rxIntrospectee ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override; + virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override; + virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& _rPropertyName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getActuatingProperties( ) override; + virtual css::inspection::InteractiveSelectionResult + SAL_CALL onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, css::uno::Any& _rData, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) override; + virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) override; + virtual css::inspection::LineDescriptor SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override; + + // PropertyHandler overridables + virtual css::uno::Sequence< css::beans::Property > + doDescribeSupportedProperties() const override; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/cellbindinghandler.cxx b/extensions/source/propctrlr/cellbindinghandler.cxx new file mode 100644 index 000000000..633a3574b --- /dev/null +++ b/extensions/source/propctrlr/cellbindinghandler.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 "cellbindinghandler.hxx" +#include "formstrings.hxx" +#include "formmetadata.hxx" +#include "cellbindinghelper.hxx" + +#include <com/sun/star/form/binding/XValueBinding.hpp> +#include <com/sun/star/lang/NullPointerException.hpp> +#include <com/sun/star/table/CellAddress.hpp> +#include <com/sun/star/inspection/XObjectInspectorUI.hpp> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> + + +namespace pcr +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::table; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::script; + using namespace ::com::sun::star::frame; + using namespace ::com::sun::star::inspection; + using namespace ::com::sun::star::form::binding; + using namespace ::comphelper; + + CellBindingPropertyHandler::CellBindingPropertyHandler( const Reference< XComponentContext >& _rxContext ) + :PropertyHandlerComponent( _rxContext ) + ,m_pCellExchangeConverter( new DefaultEnumRepresentation( *m_pInfoService, ::cppu::UnoType<sal_Int16>::get(), PROPERTY_ID_CELL_EXCHANGE_TYPE ) ) + { + } + + + OUString CellBindingPropertyHandler::getImplementationName( ) + { + return "com.sun.star.comp.extensions.CellBindingPropertyHandler"; + } + + + Sequence< OUString > CellBindingPropertyHandler::getSupportedServiceNames( ) + { + return { "com.sun.star.form.inspection.CellBindingPropertyHandler" }; + } + + + void CellBindingPropertyHandler::onNewComponent() + { + PropertyHandlerComponent::onNewComponent(); + + Reference< XModel > xDocument( impl_getContextDocument_nothrow() ); + DBG_ASSERT( xDocument.is(), "CellBindingPropertyHandler::onNewComponent: no document!" ); + if ( CellBindingHelper::isSpreadsheetDocument( xDocument ) ) + m_pHelper.reset( new CellBindingHelper( m_xComponent, xDocument ) ); + } + + + CellBindingPropertyHandler::~CellBindingPropertyHandler( ) + { + } + + + Sequence< OUString > SAL_CALL CellBindingPropertyHandler::getActuatingProperties( ) + { + Sequence< OUString > aInterestingProperties{ PROPERTY_LIST_CELL_RANGE, + PROPERTY_BOUND_CELL, + PROPERTY_CONTROLSOURCE }; + return aInterestingProperties; + } + + + void SAL_CALL CellBindingPropertyHandler::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nActuatingPropId( impl_getPropertyId_throwRuntime( _rActuatingPropertyName ) ); + OSL_PRECOND(m_pHelper, + "CellBindingPropertyHandler::actuatingPropertyChanged: inconsistency!"); + // if we survived impl_getPropertyId_throwRuntime, we should have a helper, since no helper implies no properties + + OSL_PRECOND( _rxInspectorUI.is(), "FormComponentPropertyHandler::actuatingPropertyChanged: no access to the UI!" ); + if ( !_rxInspectorUI.is() ) + throw NullPointerException(); + + std::vector< PropertyId > aDependentProperties; + + switch ( nActuatingPropId ) + { + // ----- BoundCell ----- + case PROPERTY_ID_BOUND_CELL: + { + // the SQL-data-binding related properties need to be enabled if and only if + // there is *no* valid cell binding + Reference< XValueBinding > xBinding; + _rNewValue >>= xBinding; + + if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_CELL_EXCHANGE_TYPE ) ) + _rxInspectorUI->enablePropertyUI( PROPERTY_CELL_EXCHANGE_TYPE, xBinding.is() ); + if ( impl_componentHasProperty_throw( PROPERTY_CONTROLSOURCE ) ) + _rxInspectorUI->enablePropertyUI( PROPERTY_CONTROLSOURCE, !xBinding.is() ); + + if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_FILTERPROPOSAL ) ) + _rxInspectorUI->enablePropertyUI( PROPERTY_FILTERPROPOSAL, !xBinding.is() ); + if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_EMPTY_IS_NULL ) ) + _rxInspectorUI->enablePropertyUI( PROPERTY_EMPTY_IS_NULL, !xBinding.is() ); + + aDependentProperties.push_back( PROPERTY_ID_BOUNDCOLUMN ); + + if ( !xBinding.is() && m_pHelper->getCurrentBinding().is() ) + { + // ensure that the "transfer selection as" property is reset. Since we can't remember + // it at the object itself, but derive it from the binding only, we have to normalize + // it now that there *is* no binding anymore. + setPropertyValue( PROPERTY_CELL_EXCHANGE_TYPE, Any( sal_Int16(0) ) ); + } + } + break; + + // ----- CellRange ----- + case PROPERTY_ID_LIST_CELL_RANGE: + { + // the list source related properties need to be enabled if and only if + // there is *no* valid external list source for the control + Reference< XListEntrySource > xSource; + _rNewValue >>= xSource; + + _rxInspectorUI->enablePropertyUI( PROPERTY_STRINGITEMLIST, !xSource.is() ); + _rxInspectorUI->enablePropertyUI( PROPERTY_LISTSOURCE, !xSource.is() ); + _rxInspectorUI->enablePropertyUI( PROPERTY_LISTSOURCETYPE, !xSource.is() ); + + aDependentProperties.push_back( PROPERTY_ID_BOUNDCOLUMN ); + + // also reset the list entries if the cell range is reset + // #i28319# + if ( !_bFirstTimeInit ) + { + try + { + if ( !xSource.is() ) + { + setPropertyValue( PROPERTY_STRINGITEMLIST, Any( Sequence< OUString >() ) ); + setPropertyValue( PROPERTY_TYPEDITEMLIST, Any( Sequence< Any >() ) ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( + "extensions.propctrlr", + "ListCellRange: caught an exception while resetting the string items!"); + } + } + } + break; // case PROPERTY_ID_LIST_CELL_RANGE + + // ----- DataField ----- + case PROPERTY_ID_CONTROLSOURCE: + { + OUString sControlSource; + _rNewValue >>= sControlSource; + if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_BOUND_CELL ) ) + _rxInspectorUI->enablePropertyUI( PROPERTY_BOUND_CELL, sControlSource.isEmpty() ); + } + break; // case PROPERTY_ID_CONTROLSOURCE + + default: + OSL_FAIL( "CellBindingPropertyHandler::actuatingPropertyChanged: did not register for this property!" ); + } + + for (auto const& dependentProperty : aDependentProperties) + { + impl_updateDependentProperty_nothrow( dependentProperty, _rxInspectorUI ); + } + } + + + void CellBindingPropertyHandler::impl_updateDependentProperty_nothrow( PropertyId _nPropId, const Reference< XObjectInspectorUI >& _rxInspectorUI ) const + { + try + { + switch ( _nPropId ) + { + // ----- BoundColumn ----- + case PROPERTY_ID_BOUNDCOLUMN: + { + CellBindingPropertyHandler* pNonConstThis = const_cast< CellBindingPropertyHandler* >( this ); + Reference< XValueBinding > xBinding( pNonConstThis->getPropertyValue( PROPERTY_BOUND_CELL ), UNO_QUERY ); + Reference< XListEntrySource > xListSource( pNonConstThis->getPropertyValue( PROPERTY_LIST_CELL_RANGE ), UNO_QUERY ); + + if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_BOUNDCOLUMN ) ) + _rxInspectorUI->enablePropertyUI( PROPERTY_BOUNDCOLUMN, !xBinding.is() && !xListSource.is() ); + } + break; // case PROPERTY_ID_BOUNDCOLUMN + + } // switch + + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "CellBindingPropertyHandler::impl_updateDependentProperty_nothrow" ); + } + } + + + Any SAL_CALL CellBindingPropertyHandler::getPropertyValue( const OUString& _rPropertyName ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + OSL_ENSURE(m_pHelper, "CellBindingPropertyHandler::getPropertyValue: inconsistency!"); + // if we survived impl_getPropertyId_throwUnknownProperty, we should have a helper, since no helper implies no properties + + Any aReturn; + switch ( nPropId ) + { + case PROPERTY_ID_BOUND_CELL: + { + Reference< XValueBinding > xBinding( m_pHelper->getCurrentBinding() ); + if ( !CellBindingHelper::isCellBinding( xBinding ) ) + xBinding.clear(); + + aReturn <<= xBinding; + } + break; + + case PROPERTY_ID_LIST_CELL_RANGE: + { + Reference< XListEntrySource > xSource( m_pHelper->getCurrentListSource() ); + if ( !CellBindingHelper::isCellRangeListSource( xSource ) ) + xSource.clear(); + + aReturn <<= xSource; + } + break; + + case PROPERTY_ID_CELL_EXCHANGE_TYPE: + { + Reference< XValueBinding > xBinding( m_pHelper->getCurrentBinding() ); + aReturn <<= static_cast<sal_Int16>( CellBindingHelper::isCellIntegerBinding( xBinding ) ? 1 : 0 ); + } + break; + + default: + OSL_FAIL( "CellBindingPropertyHandler::getPropertyValue: cannot handle this!" ); + break; + } + return aReturn; + } + + + void SAL_CALL CellBindingPropertyHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + OSL_ENSURE(m_pHelper, "CellBindingPropertyHandler::setPropertyValue: inconsistency!"); + // if we survived impl_getPropertyId_throwUnknownProperty, we should have a helper, since no helper implies no properties + + try + { + Any aOldValue = getPropertyValue( _rPropertyName ); + + switch ( nPropId ) + { + case PROPERTY_ID_BOUND_CELL: + { + Reference< XValueBinding > xBinding; + _rValue >>= xBinding; + m_pHelper->setBinding( xBinding ); + } + break; + + case PROPERTY_ID_LIST_CELL_RANGE: + { + Reference< XListEntrySource > xSource; + _rValue >>= xSource; + m_pHelper->setListSource( xSource ); + } + break; + + case PROPERTY_ID_CELL_EXCHANGE_TYPE: + { + sal_Int16 nExchangeType = 0; + OSL_VERIFY( _rValue >>= nExchangeType ); + + Reference< XValueBinding > xBinding = m_pHelper->getCurrentBinding( ); + if ( xBinding.is() ) + { + bool bNeedIntegerBinding = ( nExchangeType == 1 ); + if ( bNeedIntegerBinding != CellBindingHelper::isCellIntegerBinding( xBinding ) ) + { + CellAddress aAddress; + if ( m_pHelper->getAddressFromCellBinding( xBinding, aAddress ) ) + { + xBinding = m_pHelper->createCellBindingFromAddress( aAddress, bNeedIntegerBinding ); + m_pHelper->setBinding( xBinding ); + } + } + } + } + break; + + default: + OSL_FAIL( "CellBindingPropertyHandler::setPropertyValue: cannot handle this!" ); + break; + } + + impl_setContextDocumentModified_nothrow(); + + Any aNewValue( getPropertyValue( _rPropertyName ) ); + firePropertyChange( _rPropertyName, nPropId, aOldValue, aNewValue ); + // TODO/UNOize: can't we make this a part of the base class, for all those "virtual" + // properties? Base class'es |setPropertyValue| could call some |doSetPropertyValue|, + // and handle the listener notification itself + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "CellBindingPropertyHandler::setPropertyValue" ); + } + } + + + Any SAL_CALL CellBindingPropertyHandler::convertToPropertyValue( const OUString& _rPropertyName, const Any& _rControlValue ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + Any aPropertyValue; + + OSL_ENSURE( + m_pHelper, + "CellBindingPropertyHandler::convertToPropertyValue: we have no SupportedProperties!"); + if (!m_pHelper) + return aPropertyValue; + + PropertyId nPropId( m_pInfoService->getPropertyId( _rPropertyName ) ); + + OUString sControlValue; + OSL_VERIFY( _rControlValue >>= sControlValue ); + switch( nPropId ) + { + case PROPERTY_ID_LIST_CELL_RANGE: + aPropertyValue <<= m_pHelper->createCellListSourceFromStringAddress( sControlValue ); + break; + + case PROPERTY_ID_BOUND_CELL: + { + // if we have the possibility of an integer binding, then we must preserve + // this property's value (e.g. if the current binding is an integer binding, then + // the newly created one must be, too) + bool bIntegerBinding = false; + if ( m_pHelper->isCellIntegerBindingAllowed() ) + { + sal_Int16 nCurrentBindingType = 0; + getPropertyValue( PROPERTY_CELL_EXCHANGE_TYPE ) >>= nCurrentBindingType; + bIntegerBinding = ( nCurrentBindingType != 0 ); + } + aPropertyValue <<= m_pHelper->createCellBindingFromStringAddress( sControlValue, bIntegerBinding ); + } + break; + + case PROPERTY_ID_CELL_EXCHANGE_TYPE: + m_pCellExchangeConverter->getValueFromDescription( sControlValue, aPropertyValue ); + break; + + default: + OSL_FAIL( "CellBindingPropertyHandler::convertToPropertyValue: cannot handle this!" ); + break; + } + + return aPropertyValue; + } + + + Any SAL_CALL CellBindingPropertyHandler::convertToControlValue( const OUString& _rPropertyName, + const Any& _rPropertyValue, const Type& /*_rControlValueType*/ ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + Any aControlValue; + + OSL_ENSURE( + m_pHelper, + "CellBindingPropertyHandler::convertToControlValue: we have no SupportedProperties!"); + if (!m_pHelper) + return aControlValue; + + PropertyId nPropId( m_pInfoService->getPropertyId( _rPropertyName ) ); + + switch ( nPropId ) + { + case PROPERTY_ID_BOUND_CELL: + { + Reference< XValueBinding > xBinding; + bool bSuccess = _rPropertyValue >>= xBinding; + OSL_ENSURE( bSuccess, "CellBindingPropertyHandler::convertToControlValue: invalid value (1)!" ); + + // the only value binding we support so far is linking to spreadsheet cells + aControlValue <<= m_pHelper->getStringAddressFromCellBinding( xBinding ); + } + break; + + case PROPERTY_ID_LIST_CELL_RANGE: + { + Reference< XListEntrySource > xSource; + bool bSuccess = _rPropertyValue >>= xSource; + OSL_ENSURE( bSuccess, "CellBindingPropertyHandler::convertToControlValue: invalid value (2)!" ); + + // the only value binding we support so far is linking to spreadsheet cells + aControlValue <<= m_pHelper->getStringAddressFromCellListSource( xSource ); + } + break; + + case PROPERTY_ID_CELL_EXCHANGE_TYPE: + aControlValue <<= m_pCellExchangeConverter->getDescriptionForValue( _rPropertyValue ); + break; + + default: + OSL_FAIL( "CellBindingPropertyHandler::convertToControlValue: cannot handle this!" ); + break; + } + + return aControlValue; + } + + + Sequence< Property > CellBindingPropertyHandler::doDescribeSupportedProperties() const + { + std::vector< Property > aProperties; + + bool bAllowCellLinking = m_pHelper && m_pHelper->isCellBindingAllowed(); + bool bAllowCellIntLinking = m_pHelper && m_pHelper->isCellIntegerBindingAllowed(); + bool bAllowListCellRange = m_pHelper && m_pHelper->isListCellRangeAllowed(); + if ( bAllowCellLinking || bAllowListCellRange || bAllowCellIntLinking ) + { + sal_Int32 nPos = ( bAllowCellLinking ? 1 : 0 ) + + ( bAllowListCellRange ? 1 : 0 ) + + ( bAllowCellIntLinking ? 1 : 0 ); + aProperties.resize( nPos ); + + if ( bAllowCellLinking ) + { + aProperties[ --nPos ] = Property( PROPERTY_BOUND_CELL, PROPERTY_ID_BOUND_CELL, + ::cppu::UnoType<OUString>::get(), 0 ); + } + if ( bAllowCellIntLinking ) + { + aProperties[ --nPos ] = Property( PROPERTY_CELL_EXCHANGE_TYPE, PROPERTY_ID_CELL_EXCHANGE_TYPE, + ::cppu::UnoType<sal_Int16>::get(), 0 ); + } + if ( bAllowListCellRange ) + { + aProperties[ --nPos ] = Property( PROPERTY_LIST_CELL_RANGE, PROPERTY_ID_LIST_CELL_RANGE, + ::cppu::UnoType<OUString>::get(), 0 ); + } + } + + if ( aProperties.empty() ) + return Sequence< Property >(); + return comphelper::containerToSequence(aProperties); + } + + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_CellBindingPropertyHandler_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::CellBindingPropertyHandler(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/cellbindinghandler.hxx b/extensions/source/propctrlr/cellbindinghandler.hxx new file mode 100644 index 000000000..4e81b0624 --- /dev/null +++ b/extensions/source/propctrlr/cellbindinghandler.hxx @@ -0,0 +1,92 @@ +/* -*- 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 "propertyhandler.hxx" + +#include <rtl/ref.hxx> + +#include <memory> + + +namespace pcr +{ + + + class CellBindingHelper; + class IPropertyEnumRepresentation; + + class CellBindingPropertyHandler : public PropertyHandlerComponent + { + private: + std::unique_ptr< CellBindingHelper > m_pHelper; + ::rtl::Reference< IPropertyEnumRepresentation > m_pCellExchangeConverter; + + public: + explicit CellBindingPropertyHandler( + const css::uno::Reference< css::uno::XComponentContext >& _rxContext + ); + + protected: + virtual ~CellBindingPropertyHandler() override; + + protected: + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override; + + // XPropertyHandler overriables + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override; + virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override; + virtual css::uno::Any SAL_CALL convertToPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rControlValue ) override; + virtual css::uno::Any SAL_CALL convertToControlValue( const OUString& _rPropertyName, const css::uno::Any& _rPropertyValue, const css::uno::Type& _rControlValueType ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getActuatingProperties( ) override; + virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) override; + + // PropertyHandler overridables + virtual css::uno::Sequence< css::beans::Property > + doDescribeSupportedProperties() const override; + virtual void onNewComponent() override; + + private: + /** updates a property (UI) whose state depends on more than one other property + + ->actuatingPropertyChanged is called for certain properties in whose changes + we expressed interes (->getActuatingProperty). Now such a property change can + result in simple UI updates, for instance another property being enabled or disabled. + + However, it can also result in a more complex change: The current (UI) state might + depend on the value of more than one other property. Those dependent properties (their + UI, more precisely) are updated in this method. + + @param _nPropid + the ->PropertyId of the dependent property whose UI state is to be updated + + @param _rxInspectorUI + provides access to the property browser UI. Must not be <NULL/>. + */ + void impl_updateDependentProperty_nothrow( PropertyId _nPropId, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) const; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/cellbindinghelper.cxx b/extensions/source/propctrlr/cellbindinghelper.cxx new file mode 100644 index 000000000..3c66fc8c9 --- /dev/null +++ b/extensions/source/propctrlr/cellbindinghelper.cxx @@ -0,0 +1,540 @@ +/* -*- 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 "cellbindinghelper.hxx" +#include <com/sun/star/form/binding/XBindableValue.hpp> +#include <com/sun/star/form/binding/XListEntrySink.hpp> +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/form/XGridColumnFactory.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/form/XFormsSupplier.hpp> +#include <com/sun/star/form/XForm.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/sheet/XSpreadsheet.hpp> +#include <unotools/transliterationwrapper.hxx> +#include <osl/diagnose.h> +#include <tools/diagnose_ex.h> +#include "formstrings.hxx" + +#include <algorithm> + + +namespace pcr +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::frame; + using namespace ::com::sun::star::sheet; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::drawing; + using namespace ::com::sun::star::table; + using namespace ::com::sun::star::form; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::i18n; + using namespace ::com::sun::star::form::binding; + + namespace + { + + struct StringCompare + { + private: + OUString m_sReference; + + public: + explicit StringCompare( const OUString& _rReference ) : m_sReference( _rReference ) { } + + bool operator()( std::u16string_view _rCompare ) + { + return ( _rCompare == m_sReference ); + } + }; + } + + + CellBindingHelper::CellBindingHelper( const Reference< XPropertySet >& _rxControlModel, const Reference< XModel >& _rxContextDocument ) + :m_xControlModel( _rxControlModel ) + { + OSL_ENSURE( m_xControlModel.is(), "CellBindingHelper::CellBindingHelper: invalid control model!" ); + + m_xDocument.set(_rxContextDocument, css::uno::UNO_QUERY); + OSL_ENSURE( m_xDocument.is(), "CellBindingHelper::CellBindingHelper: This is no spreadsheet document!" ); + + OSL_ENSURE( isSpreadsheetDocumentWhichSupplies( SERVICE_ADDRESS_CONVERSION ), + "CellBindingHelper::CellBindingHelper: the document cannot convert address representations!" ); + } + + + bool CellBindingHelper::isSpreadsheetDocument( const Reference< XModel >& _rxContextDocument ) + { + return Reference< XSpreadsheetDocument >::query( _rxContextDocument ).is(); + } + + + sal_Int16 CellBindingHelper::getControlSheetIndex( Reference< XSpreadsheet >& _out_rxSheet ) const + { + sal_Int16 nSheetIndex = -1; + // every sheet has a draw page, and every draw page has a forms collection. + // Our control, OTOH, belongs to a forms collection. Match these... + try + { + // for determining the draw page, we need the forms collection which + // the object belongs to. This is the first object up the hierarchy which is + // *no* XForm (and, well, no XGridColumnFactory) + Reference< XChild > xCheck( m_xControlModel, UNO_QUERY ); + Reference< XForm > xParentAsForm; if ( xCheck.is() ) xParentAsForm.set(xCheck->getParent(), css::uno::UNO_QUERY); + Reference< XGridColumnFactory > xParentAsGrid; if ( xCheck.is() ) xParentAsGrid.set(xCheck->getParent(), css::uno::UNO_QUERY); + + while ( ( xParentAsForm.is() || xParentAsGrid.is() ) && xCheck.is() ) + { + xCheck.set(xCheck->getParent(), css::uno::UNO_QUERY); + xParentAsForm.set(xCheck.is() ? xCheck->getParent() : Reference< XForm >(), css::uno::UNO_QUERY); + xParentAsGrid.set(xCheck.is() ? xCheck->getParent() : Reference< XGridColumnFactory >(), css::uno::UNO_QUERY); + } + Reference< XInterface > xFormsCollection( xCheck.is() ? xCheck->getParent() : Reference< XInterface >() ); + + // now iterate through the sheets + Reference< XIndexAccess > xSheets( m_xDocument->getSheets(), UNO_QUERY ); + if ( xSheets.is() && xFormsCollection.is() ) + { + for ( sal_Int32 i = 0; i < xSheets->getCount(); ++i ) + { + Reference< XDrawPageSupplier > xSuppPage( xSheets->getByIndex( i ), UNO_QUERY_THROW ); + Reference< XFormsSupplier > xSuppForms( xSuppPage->getDrawPage(), UNO_QUERY_THROW ); + + if ( xSuppForms->getForms() == xFormsCollection ) + { // found it + nSheetIndex = static_cast<sal_Int16>(i); + _out_rxSheet.set( xSuppPage, UNO_QUERY_THROW ); + break; + } + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + + return nSheetIndex; + } + + + bool CellBindingHelper::convertStringAddress( const OUString& _rAddressDescription, CellAddress& /* [out] */ _rAddress ) const + { + Any aAddress; + return doConvertAddressRepresentations( + PROPERTY_UI_REPRESENTATION, + Any( _rAddressDescription ), + PROPERTY_ADDRESS, + aAddress, + false + ) + && ( aAddress >>= _rAddress ); + } + + + bool CellBindingHelper::doConvertAddressRepresentations( const OUString& _rInputProperty, const Any& _rInputValue, + const OUString& _rOutputProperty, Any& _rOutputValue, bool _bIsRange ) const + { + bool bSuccess = false; + + Reference< XPropertySet > xConverter( + createDocumentDependentInstance( + _bIsRange ? OUString(SERVICE_RANGEADDRESS_CONVERSION) : OUString(SERVICE_ADDRESS_CONVERSION), + OUString(), + Any() + ), + UNO_QUERY + ); + OSL_ENSURE( xConverter.is(), "CellBindingHelper::doConvertAddressRepresentations: could not get a converter service!" ); + if ( xConverter.is() ) + { + try + { + Reference< XSpreadsheet > xSheet; + xConverter->setPropertyValue( PROPERTY_REFERENCE_SHEET, Any( static_cast<sal_Int32>(getControlSheetIndex( xSheet )) ) ); + xConverter->setPropertyValue( _rInputProperty, _rInputValue ); + _rOutputValue = xConverter->getPropertyValue( _rOutputProperty ); + bSuccess = true; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "CellBindingHelper::doConvertAddressRepresentations" ); + } + } + + return bSuccess; + } + + + bool CellBindingHelper::convertStringAddress( const OUString& _rAddressDescription, + CellRangeAddress& /* [out] */ _rAddress ) const + { + Any aAddress; + return doConvertAddressRepresentations( + PROPERTY_UI_REPRESENTATION, + Any( _rAddressDescription ), + PROPERTY_ADDRESS, + aAddress, + true + ) + && ( aAddress >>= _rAddress ); + } + + + Reference< XValueBinding > CellBindingHelper::createCellBindingFromAddress( const CellAddress& _rAddress, bool _bSupportIntegerExchange ) const + { + Reference< XValueBinding > xBinding( createDocumentDependentInstance( + _bSupportIntegerExchange ? OUString(SERVICE_SHEET_CELL_INT_BINDING) : OUString(SERVICE_SHEET_CELL_BINDING), + PROPERTY_BOUND_CELL, + Any( _rAddress ) + ), UNO_QUERY ); + + return xBinding; + } + + + Reference< XValueBinding > CellBindingHelper::createCellBindingFromStringAddress( const OUString& _rAddress, bool _bSupportIntegerExchange ) const + { + Reference< XValueBinding > xBinding; + if ( !m_xDocument.is() ) + // very bad ... + return xBinding; + + // get the UNO representation of the address + CellAddress aAddress; + if ( _rAddress.isEmpty() || !convertStringAddress( _rAddress, aAddress ) ) + return xBinding; + + return createCellBindingFromAddress( aAddress, _bSupportIntegerExchange ); + } + + + Reference< XListEntrySource > CellBindingHelper::createCellListSourceFromStringAddress( const OUString& _rAddress ) const + { + Reference< XListEntrySource > xSource; + + CellRangeAddress aRangeAddress; + if ( _rAddress.isEmpty() || !convertStringAddress( _rAddress, aRangeAddress ) ) + return xSource; + + // create a range object for this address + xSource.set(createDocumentDependentInstance( + SERVICE_SHEET_CELLRANGE_LISTSOURCE, + PROPERTY_LIST_CELL_RANGE, + Any( aRangeAddress ) + ), css::uno::UNO_QUERY); + + return xSource; + } + + + Reference< XInterface > CellBindingHelper::createDocumentDependentInstance( const OUString& _rService, const OUString& _rArgumentName, + const Any& _rArgumentValue ) const + { + Reference< XInterface > xReturn; + + Reference< XMultiServiceFactory > xDocumentFactory( m_xDocument, UNO_QUERY ); + OSL_ENSURE( xDocumentFactory.is(), "CellBindingHelper::createDocumentDependentInstance: no document service factory!" ); + if ( xDocumentFactory.is() ) + { + try + { + if ( !_rArgumentName.isEmpty() ) + { + Sequence aArgs{ Any(NamedValue(_rArgumentName, _rArgumentValue)) }; + xReturn = xDocumentFactory->createInstanceWithArguments( _rService, aArgs ); + } + else + { + xReturn = xDocumentFactory->createInstance( _rService ); + } + } + catch ( const Exception& ) + { + OSL_FAIL( "CellBindingHelper::createDocumentDependentInstance: could not create the binding at the document!" ); + } + } + return xReturn; + } + + + bool CellBindingHelper::getAddressFromCellBinding( + const Reference< XValueBinding >& _rxBinding, CellAddress& _rAddress ) const + { + OSL_PRECOND( !_rxBinding.is() || isCellBinding( _rxBinding ), "CellBindingHelper::getAddressFromCellBinding: this is no cell binding!" ); + + bool bReturn = false; + if ( !m_xDocument.is() ) + // very bad ... + return bReturn; + + try + { + Reference< XPropertySet > xBindingProps( _rxBinding, UNO_QUERY ); + OSL_ENSURE( xBindingProps.is() || !_rxBinding.is(), "CellBindingHelper::getAddressFromCellBinding: no property set for the binding!" ); + if ( xBindingProps.is() ) + { + bReturn = ( xBindingProps->getPropertyValue( PROPERTY_BOUND_CELL ) >>= _rAddress ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "CellBindingHelper::getAddressFromCellBinding" ); + } + + return bReturn; + } + + + OUString CellBindingHelper::getStringAddressFromCellBinding( const Reference< XValueBinding >& _rxBinding ) const + { + CellAddress aAddress; + OUString sAddress; + if ( getAddressFromCellBinding( _rxBinding, aAddress ) ) + { + Any aStringAddress; + doConvertAddressRepresentations( PROPERTY_ADDRESS, Any( aAddress ), + PROPERTY_UI_REPRESENTATION, aStringAddress, false ); + + aStringAddress >>= sAddress; + } + + return sAddress; + } + + + OUString CellBindingHelper::getStringAddressFromCellListSource( const Reference< XListEntrySource >& _rxSource ) const + { + OSL_PRECOND( !_rxSource.is() || isCellRangeListSource( _rxSource ), "CellBindingHelper::getStringAddressFromCellListSource: this is no cell list source!" ); + + OUString sAddress; + if ( !m_xDocument.is() ) + // very bad ... + return sAddress; + + try + { + Reference< XPropertySet > xSourceProps( _rxSource, UNO_QUERY ); + OSL_ENSURE( xSourceProps.is() || !_rxSource.is(), "CellBindingHelper::getStringAddressFromCellListSource: no property set for the list source!" ); + if ( xSourceProps.is() ) + { + CellRangeAddress aRangeAddress; + xSourceProps->getPropertyValue( PROPERTY_LIST_CELL_RANGE ) >>= aRangeAddress; + + Any aStringAddress; + doConvertAddressRepresentations( PROPERTY_ADDRESS, Any( aRangeAddress ), + PROPERTY_UI_REPRESENTATION, aStringAddress, true ); + aStringAddress >>= sAddress; + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "CellBindingHelper::getStringAddressFromCellListSource" ); + } + + return sAddress; + } + + + bool CellBindingHelper::isSpreadsheetDocumentWhichSupplies( const OUString& _rService ) const + { + bool bYesItIs = false; + + Reference< XServiceInfo > xSI( m_xDocument, UNO_QUERY ); + if ( xSI.is() && xSI->supportsService( SERVICE_SPREADSHEET_DOCUMENT ) ) + { + Reference< XMultiServiceFactory > xDocumentFactory( m_xDocument, UNO_QUERY ); + OSL_ENSURE( xDocumentFactory.is(), "CellBindingHelper::isSpreadsheetDocumentWhichSupplies: spreadsheet document, but no factory?" ); + + if ( xDocumentFactory.is() ) + { + const Sequence<OUString> aAvailableServices = xDocumentFactory->getAvailableServiceNames( ); + + bYesItIs = std::any_of( + aAvailableServices.begin(), + aAvailableServices.end(), + StringCompare( _rService ) + ); + } + } + + return bYesItIs; + } + + + bool CellBindingHelper::isListCellRangeAllowed( ) const + { + bool bAllow( false ); + + Reference< XListEntrySink > xSink( m_xControlModel, UNO_QUERY ); + if ( xSink.is() ) + { + bAllow = isSpreadsheetDocumentWhichSupplies( SERVICE_SHEET_CELLRANGE_LISTSOURCE ); + } + + return bAllow; + } + + + bool CellBindingHelper::isCellIntegerBindingAllowed( ) const + { + bool bAllow( true ); + + // first, we only offer this for controls which allow bindings in general + Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY ); + if ( !xBindable.is() ) + bAllow = false; + + // then, we must live in a spreadsheet document which can provide the special + // service needed for exchanging integer values + if ( bAllow ) + bAllow = isSpreadsheetDocumentWhichSupplies( SERVICE_SHEET_CELL_INT_BINDING ); + + // then, we only offer this for list boxes + if ( bAllow ) + { + try + { + sal_Int16 nClassId = FormComponentType::CONTROL; + m_xControlModel->getPropertyValue( PROPERTY_CLASSID ) >>= nClassId; + if ( FormComponentType::LISTBOX != nClassId ) + bAllow = false; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "CellBindingHelper::isCellIntegerBindingAllowed" ); + // are there really control models which survive isCellBindingAllowed, but don't have a ClassId + // property? + bAllow = false; + } + } + + return bAllow; + } + + + bool CellBindingHelper::isCellBindingAllowed( ) const + { + bool bAllow( false ); + + Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY ); + if ( xBindable.is() ) + { + // the control can potentially be bound to an external value + // Does it live within a Calc document, and is able to supply CellBindings? + bAllow = isSpreadsheetDocumentWhichSupplies( SERVICE_SHEET_CELL_BINDING ); + } + + // disallow for some types + // TODO: shouldn't the XBindableValue supply a list of supported types, and we can distinguish + // using this list? The current behavior below is somewhat hackish... + if ( bAllow ) + { + try + { + sal_Int16 nClassId = FormComponentType::CONTROL; + m_xControlModel->getPropertyValue( PROPERTY_CLASSID ) >>= nClassId; + if ( ( FormComponentType::DATEFIELD == nClassId ) || ( FormComponentType::TIMEFIELD == nClassId ) ) + bAllow = false; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "CellBindingHelper::isCellBindingAllowed" ); + bAllow = false; + } + } + return bAllow; + } + + + bool CellBindingHelper::isCellBinding( const Reference< XValueBinding >& _rxBinding ) + { + return doesComponentSupport( _rxBinding, SERVICE_SHEET_CELL_BINDING ); + } + + + bool CellBindingHelper::isCellIntegerBinding( const Reference< XValueBinding >& _rxBinding ) + { + return doesComponentSupport( _rxBinding, SERVICE_SHEET_CELL_INT_BINDING ); + } + + + bool CellBindingHelper::isCellRangeListSource( const Reference< XListEntrySource >& _rxSource ) + { + return doesComponentSupport( _rxSource, SERVICE_SHEET_CELLRANGE_LISTSOURCE ); + } + + + bool CellBindingHelper::doesComponentSupport( const Reference< XInterface >& _rxComponent, const OUString& _rService ) + { + Reference< XServiceInfo > xSI( _rxComponent, UNO_QUERY ); + bool bDoes = xSI.is() && xSI->supportsService( _rService ); + return bDoes; + } + + + Reference< XValueBinding > CellBindingHelper::getCurrentBinding( ) const + { + Reference< XValueBinding > xBinding; + Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY ); + if ( xBindable.is() ) + xBinding = xBindable->getValueBinding(); + return xBinding; + } + + + Reference< XListEntrySource > CellBindingHelper::getCurrentListSource( ) const + { + Reference< XListEntrySource > xSource; + Reference< XListEntrySink > xSink( m_xControlModel, UNO_QUERY ); + if ( xSink.is() ) + xSource = xSink->getListEntrySource(); + return xSource; + } + + + void CellBindingHelper::setBinding( const Reference< XValueBinding >& _rxBinding ) + { + Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY ); + OSL_PRECOND( xBindable.is(), "CellBindingHelper::setBinding: the object is not bindable!" ); + if ( xBindable.is() ) + xBindable->setValueBinding( _rxBinding ); + } + + + void CellBindingHelper::setListSource( const Reference< XListEntrySource >& _rxSource ) + { + Reference< XListEntrySink > xSink( m_xControlModel, UNO_QUERY ); + OSL_PRECOND( xSink.is(), "CellBindingHelper::setListSource: the object is no list entry sink!" ); + if ( xSink.is() ) + xSink->setListEntrySource( _rxSource ); + } + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/cellbindinghelper.hxx b/extensions/source/propctrlr/cellbindinghelper.hxx new file mode 100644 index 000000000..a2c85cd65 --- /dev/null +++ b/extensions/source/propctrlr/cellbindinghelper.hxx @@ -0,0 +1,272 @@ +/* -*- 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/frame/XModel.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include <com/sun/star/table/CellAddress.hpp> +#include <com/sun/star/table/CellRangeAddress.hpp> +#include <com/sun/star/form/binding/XValueBinding.hpp> +#include <com/sun/star/form/binding/XListEntrySource.hpp> +#include <com/sun/star/sheet/XSpreadsheet.hpp> + + +namespace pcr +{ + + /** encapsulates functionality related to binding a form control to a spreadsheet cell + */ + class CellBindingHelper final + { + css::uno::Reference< css::beans::XPropertySet > + m_xControlModel; // the model we work for + css::uno::Reference< css::sheet::XSpreadsheetDocument > + m_xDocument; // the document where the model lives + + public: + /** ctor + @param _rxControlModel + the control model which is or will be bound + */ + CellBindingHelper( + const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel, + const css::uno::Reference< css::frame::XModel >& _rxContextDocument + ); + + /** determines whether the given model is a spreadsheet document model + + <p>If this method returns <FALSE/>, you cannot instantiate a CellBindingHelper with + the document, since then no of its functionality will be available.</p> + */ + static bool isSpreadsheetDocument( + const css::uno::Reference< css::frame::XModel >& _rxContextDocument + ); + + /** gets a cell binding for the given address + @precond + isCellBindingAllowed returns <TRUE/> + */ + css::uno::Reference< css::form::binding::XValueBinding > + createCellBindingFromStringAddress( + const OUString& _rAddress, + bool _bSupportIntegerExchange + ) const; + + /** creates a cell binding (supporting integer exchange, if requested) for + the given address object + */ + css::uno::Reference< css::form::binding::XValueBinding > + createCellBindingFromAddress( + const css::table::CellAddress& _rAddress, + bool _bSupportIntegerExchange + ) const; + + /** gets a cell range list source binding for the given address + */ + css::uno::Reference< css::form::binding::XListEntrySource > + createCellListSourceFromStringAddress( const OUString& _rAddress ) const; + + /** creates a string representation for the given value binding's address + + <p>If the sheet of the bound cell is the same as the sheet which our control belongs + to, then the sheet name is omitted in the resulting string representation.</p> + + @precond + The binding is a valid cell binding, or <NULL/> + @see isCellBinding + */ + OUString getStringAddressFromCellBinding( + const css::uno::Reference< css::form::binding::XValueBinding >& _rxBinding + ) const; + + /** creates an address object for the given value binding's address + + @precond + The binding is a valid cell binding, or <NULL/> + @return + <FALSE/> if and only if an error occurred and no valid address could be obtained + @see isCellBinding + */ + bool getAddressFromCellBinding( + const css::uno::Reference< css::form::binding::XValueBinding >& _rxBinding, + css::table::CellAddress& _rAddress + ) const; + + /** creates a string representation for the given list source's range address + + <p>If the sheet of the cell range which acts as list source is the same as the + sheet which our control belongs to, then the sheet name is omitted in the + resulting string representation.</p> + + @precond + The object is a valid cell range list source, or <NULL/> + @see isCellRangeListSource + */ + OUString getStringAddressFromCellListSource( + const css::uno::Reference< css::form::binding::XListEntrySource >& _rxSource + ) const; + + /** returns the current binding of our control model, if any. + */ + css::uno::Reference< css::form::binding::XValueBinding > + getCurrentBinding( ) const; + + /** returns the current external list source of the control model, if any + */ + css::uno::Reference< css::form::binding::XListEntrySource > + getCurrentListSource( ) const; + + /** sets a new binding for our control model + @precond + the control model is bindable (which is implied by <member>isCellBindingAllowed</member> + returning <TRUE/>) + */ + void setBinding( + const css::uno::Reference< css::form::binding::XValueBinding >& _rxBinding + ); + + /** sets a list source for our control model + @precond + the control model is a list sink (which is implied by <member>isListCellRangeAllowed</member> + returning <TRUE/>) + */ + void setListSource( + const css::uno::Reference< css::form::binding::XListEntrySource >& _rxSource + ); + + /** checks whether it's possible to bind the control model to a spreadsheet cell + */ + bool isCellBindingAllowed( ) const; + + /** checks whether it's possible to bind the control model to a spreadsheet cell, + with exchanging integer values + */ + bool isCellIntegerBindingAllowed( ) const; + + /** checks whether it's possible to bind the control model to range of spreadsheet cells + supplying the list entries + */ + bool isListCellRangeAllowed( ) const; + + /** checks whether a given binding is a spreadsheet cell binding + */ + static bool isCellBinding( + const css::uno::Reference< css::form::binding::XValueBinding >& _rxBinding + ); + + /** checks whether a given binding is a spreadsheet cell binding, exchanging + integer values + */ + static bool isCellIntegerBinding( + const css::uno::Reference< css::form::binding::XValueBinding >& _rxBinding + ); + + /** checks whether a given list source is a spreadsheet cell list source + */ + static bool isCellRangeListSource( + const css::uno::Reference< css::form::binding::XListEntrySource >& _rxSource + ); + + /** retrieves the index of the sheet which our control belongs to + @return the index of the sheet which our control belongs to or -1, if an error occurred + */ + sal_Int16 getControlSheetIndex( + css::uno::Reference< css::sheet::XSpreadsheet >& _out_rxSheet + ) const; + + private: + /** creates an address object from a string representation of a cell address + */ + bool convertStringAddress( + const OUString& _rAddressDescription, + css::table::CellAddress& /* [out] */ _rAddress + ) const; + + /** creates an address range object from a string representation of a cell range address + */ + bool convertStringAddress( + const OUString& _rAddressDescription, + css::table::CellRangeAddress& /* [out] */ _rAddress + ) const; + + /** determines if our document is a spreadsheet document, *and* can supply + the given service + */ + bool isSpreadsheetDocumentWhichSupplies( const OUString& _rService ) const; + + /** checks whether a given component supports a given service + */ + static bool doesComponentSupport( + const css::uno::Reference< css::uno::XInterface >& _rxComponent, + const OUString& _rService + ); + + /** uses the document (it's factory interface, respectively) to create a component instance + @param _rService + the service name + @param _rArgumentName + the name of the single argument to pass during creation. May be empty, in this case + no arguments are passed + @param _rArgumentValue + the value of the instantiation argument. Not evaluated if <arg>_rArgumentName</arg> + is empty. + */ + css::uno::Reference< css::uno::XInterface > + createDocumentDependentInstance( + const OUString& _rService, + const OUString& _rArgumentName, + const css::uno::Any& _rArgumentValue + ) const; + + /** converts an address representation into another one + + @param _rInputProperty + the input property name for the conversion service + @param _rInputValue + the input property value for the conversion service + @param _rOutputProperty + the output property name for the conversion service + @param _rOutputValue + the output property value for the conversion service + @param _bIsRange + if <TRUE/>, the RangeAddressConversion service will be used, else + the AddressConversion service + + @return + <TRUE/> if any only if the conversion was successful + + @see css::table::CellAddressConversion + @see css::table::CellRangeAddressConversion + */ + bool doConvertAddressRepresentations( + const OUString& _rInputProperty, + const css::uno::Any& _rInputValue, + const OUString& _rOutputProperty, + css::uno::Any& _rOutputValue, + bool _bIsRange + ) const; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/commoncontrol.cxx b/extensions/source/propctrlr/commoncontrol.cxx new file mode 100644 index 000000000..243bbd813 --- /dev/null +++ b/extensions/source/propctrlr/commoncontrol.cxx @@ -0,0 +1,134 @@ +/* -*- 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 "commoncontrol.hxx" +#include <tools/diagnose_ex.h> + + +namespace pcr +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::inspection::XPropertyControlContext; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::inspection::XPropertyControl; + + CommonBehaviourControlHelper::CommonBehaviourControlHelper( sal_Int16 _nControlType, XPropertyControl& _rAntiImpl ) + :m_nControlType( _nControlType ) + ,m_rAntiImpl( _rAntiImpl ) + ,m_bModified( false ) + { + } + + + CommonBehaviourControlHelper::~CommonBehaviourControlHelper() + { + } + + void CommonBehaviourControlHelper::setControlContext( const Reference< XPropertyControlContext >& _controlcontext ) + { + m_xContext = _controlcontext; + } + + void CommonBehaviourControlHelper::notifyModifiedValue( ) + { + if ( isModified() && m_xContext.is() ) + { + try + { + m_xContext->valueChanged( &m_rAntiImpl ); + m_bModified = false; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + } + + void CommonBehaviourControlHelper::editChanged() + { + setModified(); + } + + IMPL_LINK_NOARG( CommonBehaviourControlHelper, EditModifiedHdl, weld::Entry&, void ) + { + editChanged(); + } + + IMPL_LINK_NOARG( CommonBehaviourControlHelper, ModifiedHdl, weld::ComboBox&, void ) + { + setModified(); + // notify as soon as the Data source is changed, don't wait until we lose focus + // because the Content dropdown cannot be populated after it is popped up + // and going from Data source direct to Content may give focus-lost to + // Content after the popup attempt is made + notifyModifiedValue(); + } + + IMPL_LINK_NOARG( CommonBehaviourControlHelper, MetricModifiedHdl, weld::MetricSpinButton&, void ) + { + setModified(); + } + + IMPL_LINK_NOARG( CommonBehaviourControlHelper, FormattedModifiedHdl, weld::FormattedSpinButton&, void ) + { + setModified(); + } + + IMPL_LINK_NOARG( CommonBehaviourControlHelper, TimeModifiedHdl, weld::FormattedSpinButton&, void ) + { + setModified(); + } + + IMPL_LINK_NOARG( CommonBehaviourControlHelper, DateModifiedHdl, SvtCalendarBox&, void ) + { + setModified(); + } + + IMPL_LINK_NOARG( CommonBehaviourControlHelper, ColorModifiedHdl, ColorListBox&, void ) + { + setModified(); + } + + IMPL_LINK_NOARG( CommonBehaviourControlHelper, GetFocusHdl, weld::Widget&, void ) + { + try + { + if ( m_xContext.is() ) + m_xContext->focusGained( &m_rAntiImpl ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + IMPL_LINK_NOARG( CommonBehaviourControlHelper, LoseFocusHdl, weld::Widget&, void ) + { + // TODO/UNOize: should this be outside the default control's implementations? If somebody + // has an own control implementation, which does *not* do this - would this be allowed? + // If not, then we must move this logic out of here. + notifyModifiedValue(); + } + +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/commoncontrol.hxx b/extensions/source/propctrlr/commoncontrol.hxx new file mode 100644 index 000000000..746f2f56f --- /dev/null +++ b/extensions/source/propctrlr/commoncontrol.hxx @@ -0,0 +1,208 @@ +/* -*- 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/inspection/XPropertyControl.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/basemutex.hxx> +#include <tools/link.hxx> +#include <vcl/weld.hxx> +#include <vcl/weldutils.hxx> + +class NotifyEvent; +class ColorListBox; +class SvtCalendarBox; + +namespace pcr +{ + + //= CommonBehaviourControlHelper + + /** A helper class for implementing the <type scope="css::inspection">XPropertyControl</type> + or one of its derived interfaces. + + This class is used as a base class the CommonBehaviourControl template. + */ + class CommonBehaviourControlHelper + { + private: + sal_Int16 m_nControlType; + css::uno::Reference< css::inspection::XPropertyControlContext > + m_xContext; + css::inspection::XPropertyControl& + m_rAntiImpl; + bool m_bModified; + + public: + /** creates the instance + @param nControlType + the type of the control - one of the <type scope="css::inspection">PropertyControlType</type> + constants + @param pAntiImpl + Reference to the instance as whose "impl-class" we act i.e. the CommonBehaviourControl<> template, + which is why we hold it without acquiring it/ + */ + CommonBehaviourControlHelper( + sal_Int16 nControlType, + css::inspection::XPropertyControl& rAntiImpl); + + virtual ~CommonBehaviourControlHelper(); + + virtual void setModified() { m_bModified = true; } + + virtual void editChanged(); + + // XPropertyControl + /// @throws css::uno::RuntimeException + ::sal_Int16 getControlType() const { return m_nControlType; } + /// @throws css::uno::RuntimeException + const css::uno::Reference< css::inspection::XPropertyControlContext >& getControlContext() const { return m_xContext; } + /// @throws css::uno::RuntimeException + void setControlContext( const css::uno::Reference< css::inspection::XPropertyControlContext >& controlcontext ); + /// @throws css::uno::RuntimeException + bool isModified( ) const { return m_bModified; } + /// @throws css::uno::RuntimeException + void notifyModifiedValue( ); + + virtual weld::Widget* getWidget() = 0; + + /// may be used by derived classes, they forward the event to the PropCtrListener + DECL_LINK( ModifiedHdl, weld::ComboBox&, void ); + DECL_LINK( ColorModifiedHdl, ColorListBox&, void ); + DECL_LINK( EditModifiedHdl, weld::Entry&, void ); + DECL_LINK( MetricModifiedHdl, weld::MetricSpinButton&, void ); + DECL_LINK( FormattedModifiedHdl, weld::FormattedSpinButton&, void ); + DECL_LINK( TimeModifiedHdl, weld::FormattedSpinButton&, void ); + DECL_LINK( DateModifiedHdl, SvtCalendarBox&, void ); + DECL_LINK( GetFocusHdl, weld::Widget&, void ); + DECL_LINK( LoseFocusHdl, weld::Widget&, void ); + }; + + + //= CommonBehaviourControl + + /** implements a base class for <type scope="css::inspection">XPropertyControl</type> + implementations + + @param TControlInterface + an interface class which is derived from (or identical to) <type scope="css::inspection">XPropertyControl</type> + @param TControlWindow + a class which is derived from weld::Widget + */ + template < class TControlInterface, class TControlWindow > + class CommonBehaviourControl :public ::cppu::BaseMutex + ,public ::cppu::WeakComponentImplHelper< TControlInterface > + ,public CommonBehaviourControlHelper + { + protected: + typedef ::cppu::WeakComponentImplHelper< TControlInterface > ComponentBaseClass; + + inline CommonBehaviourControl(sal_Int16 nControlType, + std::unique_ptr<weld::Builder> xBuilder, + std::unique_ptr<TControlWindow> xWidget, + bool bReadOnly); + + virtual ~CommonBehaviourControl() override + { + clear_widgetry(); + } + + // XPropertyControl - delegated to ->m_aImplControl + virtual ::sal_Int16 SAL_CALL getControlType() override + { return CommonBehaviourControlHelper::getControlType(); } + virtual css::uno::Reference< css::inspection::XPropertyControlContext > SAL_CALL getControlContext() override + { return CommonBehaviourControlHelper::getControlContext(); } + virtual void SAL_CALL setControlContext( const css::uno::Reference< css::inspection::XPropertyControlContext >& controlcontext ) override + { CommonBehaviourControlHelper::setControlContext( controlcontext ); } + virtual css::uno::Reference< css::awt::XWindow > SAL_CALL getControlWindow() override + { return new weld::TransportAsXWindow(getWidget()); } + virtual sal_Bool SAL_CALL isModified( ) override + { return CommonBehaviourControlHelper::isModified(); } + virtual void SAL_CALL notifyModifiedValue( ) override + { CommonBehaviourControlHelper::notifyModifiedValue(); } + + void clear_widgetry() + { + if (!m_xControlWindow) + return; + weld::Widget* pWidget = getWidget(); + std::unique_ptr<weld::Container> xParent(pWidget->weld_parent()); + xParent->move(pWidget, nullptr); + m_xControlWindow.reset(); + m_xBuilder.reset(); + } + + // XComponent + virtual void SAL_CALL disposing() override + { + clear_widgetry(); + } + + TControlWindow* getTypedControlWindow() + { return m_xControlWindow.get(); } + const TControlWindow* getTypedControlWindow() const + { return m_xControlWindow.get(); } + + virtual void SetModifyHandler() + { + m_xControlWindow->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) ); + m_xControlWindow->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) ); + } + + /** checks whether the instance is already disposed + @throws DisposedException + if the instance is already disposed + */ + inline void impl_checkDisposed_throw(); + protected: + std::unique_ptr<weld::Builder> m_xBuilder; + private: + std::unique_ptr<TControlWindow> m_xControlWindow; + }; + + //= CommonBehaviourControl - implementation + template< class TControlInterface, class TControlWindow > + inline CommonBehaviourControl< TControlInterface, TControlWindow >::CommonBehaviourControl(sal_Int16 nControlType, + std::unique_ptr<weld::Builder> xBuilder, + std::unique_ptr<TControlWindow> xWidget, + bool bReadOnly) + : ComponentBaseClass( m_aMutex ) + , CommonBehaviourControlHelper( nControlType, *this ) + , m_xBuilder(std::move(xBuilder)) + , m_xControlWindow(std::move(xWidget)) + { + if (bReadOnly) + { + // disable widget by default, entries will override to enable the widget but set it non-editable + m_xControlWindow->set_sensitive(false); + } + } + + template< class TControlInterface, class TControlWindow > + inline void CommonBehaviourControl< TControlInterface, TControlWindow >::impl_checkDisposed_throw() + { + if ( ComponentBaseClass::rBHelper.bDisposed ) + throw css::lang::DisposedException( OUString(), *this ); + } + +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/composeduiupdate.cxx b/extensions/source/propctrlr/composeduiupdate.cxx new file mode 100644 index 000000000..0e32ef19a --- /dev/null +++ b/extensions/source/propctrlr/composeduiupdate.cxx @@ -0,0 +1,786 @@ +/* -*- 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 "composeduiupdate.hxx" +#include "pcrcommon.hxx" + +#include <com/sun/star/inspection/XObjectInspectorUI.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/NullPointerException.hpp> +#include <com/sun/star/inspection/PropertyLineElement.hpp> +#include <osl/diagnose.h> +#include <osl/mutex.hxx> +#include <rtl/ref.hxx> +#include <cppuhelper/implbase.hxx> + +#include <algorithm> +#include <map> +#include <set> + + +namespace pcr +{ + + + using ::com::sun::star::lang::DisposedException; + using ::com::sun::star::lang::NullPointerException; + using ::com::sun::star::inspection::XPropertyHandler; + using ::com::sun::star::uno::Reference; + using ::com::sun::star::inspection::XObjectInspectorUI; + using ::com::sun::star::inspection::XPropertyControl; + using ::com::sun::star::inspection::XPropertyControlObserver; + + namespace PropertyLineElement = ::com::sun::star::inspection::PropertyLineElement; + + namespace + { + struct HandlerLess + { + bool operator()( const Reference< XPropertyHandler >& lhs, const Reference< XPropertyHandler >& rhs) const + { + return lhs.get() < rhs.get(); + } + }; + + + typedef std::set< OUString > StringBag; + typedef std::map< sal_Int16, StringBag > MapIntToStringBag; + } + + + // callbacks for CachedInspectorUI + + typedef void (ComposedPropertyUIUpdate::*FNotifySingleUIChange)(); + + typedef ::cppu::WeakImplHelper < css::inspection::XObjectInspectorUI + > CachedInspectorUI_Base; + + namespace { + + struct CachedInspectorUI : public CachedInspectorUI_Base + { + private: + ::osl::Mutex m_aMutex; + bool m_bDisposed; + ComposedPropertyUIUpdate& + m_rMaster; + FNotifySingleUIChange m_pUIChangeNotification; + + // enablePropertyUI cache + StringBag aEnabledProperties; + StringBag aDisabledProperties; + + // show/hidePropertyUI cache + StringBag aShownProperties; + StringBag aHiddenProperties; + + // rebuildPropertyUI cache + StringBag aRebuiltProperties; + + // showCategory cache + StringBag aShownCategories; + StringBag aHiddenCategories; + + // enablePropertyUIElements cache + MapIntToStringBag aEnabledElements; + MapIntToStringBag aDisabledElements; + + public: + typedef StringBag& (CachedInspectorUI::*FGetStringBag)(); + + // enablePropertyUI cache + StringBag& getEnabledProperties() { return aEnabledProperties; } + StringBag& getDisabledProperties() { return aDisabledProperties; } + + // show/hidePropertyUI cache + StringBag& getShownProperties() { return aShownProperties; } + StringBag& getHiddenProperties() { return aHiddenProperties; } + + // rebuildPropertyUI cache + StringBag& getRebuiltProperties() { return aRebuiltProperties; } + + // showCategory cache + StringBag& getShownCategories() { return aShownCategories; } + StringBag& getHiddenCategories() { return aHiddenCategories; } + + // enablePropertyUIElements + StringBag& getEnabledInputControls() { return aEnabledElements[ PropertyLineElement::InputControl ]; } + StringBag& getDisabledInputControls() { return aDisabledElements[ PropertyLineElement::InputControl ]; } + StringBag& getEnabledPrimaryButtons() { return aEnabledElements[ PropertyLineElement::PrimaryButton ]; } + StringBag& getDisabledPrimaryButtons() { return aDisabledElements[ PropertyLineElement::PrimaryButton ]; } + StringBag& getEnabledSecondaryButtons() { return aEnabledElements[ PropertyLineElement::SecondaryButton ]; } + StringBag& getDisabledSecondaryButtons() { return aDisabledElements[ PropertyLineElement::SecondaryButton ]; } + + public: + CachedInspectorUI( ComposedPropertyUIUpdate& _rMaster, FNotifySingleUIChange _pUIChangeNotification ); + CachedInspectorUI(const CachedInspectorUI&) = delete; + CachedInspectorUI& operator=(const CachedInspectorUI&) = delete; + + /// disposes the instance + void dispose(); + + // XObjectInspectorUI overridables + virtual void SAL_CALL enablePropertyUI( const OUString& _rPropertyName, sal_Bool _bEnable ) override; + virtual void SAL_CALL enablePropertyUIElements( const OUString& _rPropertyName, ::sal_Int16 _nElements, sal_Bool _bEnable ) override; + virtual void SAL_CALL rebuildPropertyUI( const OUString& _rPropertyName ) override; + virtual void SAL_CALL showPropertyUI( const OUString& _rPropertyName ) override; + virtual void SAL_CALL hidePropertyUI( const OUString& _rPropertyName ) override; + virtual void SAL_CALL showCategory( const OUString& _rCategory, sal_Bool _bShow ) override; + virtual Reference< XPropertyControl > SAL_CALL getPropertyControl( const OUString& _rPropertyName ) override; + virtual void SAL_CALL registerControlObserver( const Reference< XPropertyControlObserver >& Observer ) override; + virtual void SAL_CALL revokeControlObserver( const Reference< XPropertyControlObserver >& Observer ) override; + virtual void SAL_CALL setHelpSectionText( const OUString& HelpText ) override; + + protected: + virtual ~CachedInspectorUI() override; + + /// throws an exception if the component is already disposed + void checkDisposed() const; + + private: + void impl_markElementEnabledOrDisabled( const OUString& _rPropertyName, sal_Int16 _nElementIdOrZero, bool _bEnable ); + + /** calls <member>m_pUIChangeNotification</member> at <member>m_rMaster</member> + */ + void impl_notifySingleUIChange() const; + + private: + class MethodGuard; + friend class MethodGuard; + class MethodGuard : public ::osl::MutexGuard + { + public: + explicit MethodGuard( CachedInspectorUI& rInstance ) + : ::osl::MutexGuard( rInstance.m_aMutex ) + { + rInstance.checkDisposed(); + } + }; + }; + + } + + CachedInspectorUI::CachedInspectorUI( ComposedPropertyUIUpdate& _rMaster, FNotifySingleUIChange _pUIChangeNotification ) + :m_bDisposed( false ) + ,m_rMaster( _rMaster ) + ,m_pUIChangeNotification( _pUIChangeNotification ) + { + } + + + CachedInspectorUI::~CachedInspectorUI() + { + } + + + void CachedInspectorUI::dispose() + { + ::osl::MutexGuard aGuard( m_aMutex ); + m_bDisposed = true; + + clearContainer( aEnabledProperties ); + clearContainer( aDisabledProperties ); + clearContainer( aRebuiltProperties ); + clearContainer( aShownProperties ); + clearContainer( aHiddenProperties ); + clearContainer( aShownCategories ); + clearContainer( aHiddenCategories ); + clearContainer( aEnabledElements ); + clearContainer( aDisabledElements ); + } + + + void CachedInspectorUI::checkDisposed() const + { + if (m_bDisposed) + throw DisposedException(); + } + + + namespace + { + void lcl_markStringKeyPositiveOrNegative( const OUString& _rKeyName, StringBag& _rPositives, StringBag& _rNegatives, bool _bMarkPositive ) + { + if ( _bMarkPositive ) + { + _rPositives.insert( _rKeyName ); + // if the same key has been remember as in the "negative" list before, clear this information, since it's overruled + _rNegatives.erase( _rKeyName ); + } + else + _rNegatives.insert( _rKeyName ); + } + } + + + void CachedInspectorUI::enablePropertyUI( const OUString& _rPropertyName, sal_Bool _bEnable ) + { + MethodGuard aGuard( *this ); + if ( !m_rMaster.shouldContinuePropertyHandling( _rPropertyName ) ) + return; + + lcl_markStringKeyPositiveOrNegative( _rPropertyName, aEnabledProperties, aDisabledProperties, _bEnable ); + impl_notifySingleUIChange(); + } + + + void CachedInspectorUI::impl_markElementEnabledOrDisabled( const OUString& _rPropertyName, sal_Int16 _nElementIdOrZero, bool _bEnable ) + { + if ( _nElementIdOrZero == 0 ) + return; + + lcl_markStringKeyPositiveOrNegative( + _rPropertyName, + aEnabledElements[ _nElementIdOrZero ], + aDisabledElements[ _nElementIdOrZero ], + _bEnable + ); + } + + + void CachedInspectorUI::impl_notifySingleUIChange() const + { + (m_rMaster.*m_pUIChangeNotification)(); + } + + + void CachedInspectorUI::enablePropertyUIElements( const OUString& _rPropertyName, sal_Int16 _nElements, sal_Bool _bEnable ) + { + MethodGuard aGuard( *this ); + if ( !m_rMaster.shouldContinuePropertyHandling( _rPropertyName ) ) + return; + + impl_markElementEnabledOrDisabled( _rPropertyName, _nElements & PropertyLineElement::InputControl, _bEnable ); + impl_markElementEnabledOrDisabled( _rPropertyName, _nElements & PropertyLineElement::PrimaryButton, _bEnable ); + impl_markElementEnabledOrDisabled( _rPropertyName, _nElements & PropertyLineElement::SecondaryButton, _bEnable ); + + impl_notifySingleUIChange(); + } + + + void CachedInspectorUI::rebuildPropertyUI( const OUString& _rPropertyName ) + { + MethodGuard aGuard( *this ); + if ( !m_rMaster.shouldContinuePropertyHandling( _rPropertyName ) ) + return; + + aRebuiltProperties.insert( _rPropertyName ); + + impl_notifySingleUIChange(); + } + + + void CachedInspectorUI::showPropertyUI( const OUString& _rPropertyName ) + { + MethodGuard aGuard( *this ); + if ( !m_rMaster.shouldContinuePropertyHandling( _rPropertyName ) ) + return; + + aShownProperties.insert( _rPropertyName ); + // if the same category has been hidden before, clear this information, since it's overruled + aHiddenProperties.erase( _rPropertyName ); + + impl_notifySingleUIChange(); + } + + + void CachedInspectorUI::hidePropertyUI( const OUString& _rPropertyName ) + { + MethodGuard aGuard( *this ); + if ( !m_rMaster.shouldContinuePropertyHandling( _rPropertyName ) ) + return; + + aHiddenProperties.insert( _rPropertyName ); + impl_notifySingleUIChange(); + } + + + void CachedInspectorUI::showCategory( const OUString& _rCategory, sal_Bool _bShow ) + { + MethodGuard aGuard( *this ); + + lcl_markStringKeyPositiveOrNegative( _rCategory, aShownCategories, aHiddenCategories, _bShow ); + impl_notifySingleUIChange(); + } + + + Reference< XPropertyControl > SAL_CALL CachedInspectorUI::getPropertyControl( const OUString& _rPropertyName ) + { + MethodGuard aGuard( *this ); + if ( !m_rMaster.shouldContinuePropertyHandling( _rPropertyName ) ) + return Reference< XPropertyControl >(); + + return m_rMaster.getDelegatorUI()->getPropertyControl( _rPropertyName ); + } + + + void SAL_CALL CachedInspectorUI::registerControlObserver( const Reference< XPropertyControlObserver >& Observer ) + { + OSL_FAIL( "CachedInspectorUI::registerControlObserver: not expected to be called!" ); + // CachedInspectorUI is used as context for the controls, and we don't expect them to + // register listeners themself + m_rMaster.getDelegatorUI()->registerControlObserver( Observer ); + } + + + void SAL_CALL CachedInspectorUI::revokeControlObserver( const Reference< XPropertyControlObserver >& Observer ) + { + OSL_FAIL( "CachedInspectorUI::revokeControlObserver: not expected to be called!" ); + // CachedInspectorUI is used as context for the controls, and we don't expect them to + // register listeners themself + m_rMaster.getDelegatorUI()->revokeControlObserver( Observer ); + } + + + void SAL_CALL CachedInspectorUI::setHelpSectionText( const OUString& HelpText ) + { + m_rMaster.getDelegatorUI()->setHelpSectionText( HelpText ); + } + + + // HandlerMap + + typedef std::map < Reference< XPropertyHandler > + , ::rtl::Reference< CachedInspectorUI > + , HandlerLess + > ImplMapHandlerToUI; + struct MapHandlerToUI + { + ImplMapHandlerToUI aHandlers; + }; + + ComposedPropertyUIUpdate::ComposedPropertyUIUpdate( const Reference< XObjectInspectorUI >& _rxDelegatorUI, + IPropertyExistenceCheck* _pPropertyCheck ) + :m_pCollectedUIs( new MapHandlerToUI ) + ,m_xDelegatorUI( _rxDelegatorUI ) + ,m_nSuspendCounter( 0 ) + ,m_pPropertyCheck( _pPropertyCheck ) + { + if ( !m_xDelegatorUI.is() ) + throw NullPointerException(); + } + + + ComposedPropertyUIUpdate::~ComposedPropertyUIUpdate( ) + { + } + + + Reference< XObjectInspectorUI > ComposedPropertyUIUpdate::getUIForPropertyHandler( const Reference< XPropertyHandler >& _rxHandler ) + { + impl_checkDisposed(); + + ::rtl::Reference< CachedInspectorUI >& rUI = m_pCollectedUIs->aHandlers[ _rxHandler ]; + if ( !rUI.is() ) + rUI = new CachedInspectorUI( *this, &ComposedPropertyUIUpdate::callback_inspectorUIChanged_throw ); + return rUI; + } + + + namespace + { + + // an STL-compatible structure which collects strings from a CachedInspectorUI instances + struct StringBagCollector + { + private: + StringBag& m_rBag; + CachedInspectorUI::FGetStringBag m_pGetter; + + public: + StringBagCollector( StringBag& _rBag, CachedInspectorUI::FGetStringBag _pGetter ) :m_rBag( _rBag ), m_pGetter( _pGetter ) { } + + void operator()( const ImplMapHandlerToUI::value_type& _rUI ) + { + StringBag& rBag( ((_rUI.second.get())->*m_pGetter)() ); + m_rBag.insert( rBag.begin(), rBag.end() ); + } + + static void collectAll( StringBag& _rAll, const ImplMapHandlerToUI& _rMap, CachedInspectorUI::FGetStringBag _pGetter ) + { + std::for_each( _rMap.begin(), _rMap.end(), StringBagCollector( _rAll, _pGetter ) ); + } + }; + + + // an STL-compatible structure which cleans a certain string bag in a CachedInspectorUI instances + struct StringBagClearer + { + private: + CachedInspectorUI::FGetStringBag m_pGetter; + + public: + explicit StringBagClearer( CachedInspectorUI::FGetStringBag _pGetter ) :m_pGetter( _pGetter ) { } + + void operator()( const ImplMapHandlerToUI::value_type& _rUI ) + { + clearContainer( ((_rUI.second.get())->*m_pGetter)() ); + } + + static void clearAll( const ImplMapHandlerToUI& _rMap, CachedInspectorUI::FGetStringBag _pGetter ) + { + std::for_each( _rMap.begin(), _rMap.end(), StringBagClearer( _pGetter ) ); + } + }; + + // a typedef for a ->XObjectInspectorUI member function taking a string + typedef void ( SAL_CALL XObjectInspectorUI::*FPropertyUISetter )( const OUString& ); + + + // an STL-compatible struct which calls a certain member method (taking a string) at a + // given ->XObjectInspectorUI instance + struct PropertyUIOperator + { + private: + Reference< XObjectInspectorUI > m_xUpdater; + FPropertyUISetter m_pSetter; + + public: + PropertyUIOperator( const Reference< XObjectInspectorUI >& _rxInspectorUI, FPropertyUISetter _pSetter ) + :m_xUpdater( _rxInspectorUI ) + ,m_pSetter( _pSetter ) + { + } + + void operator()( const OUString& _rPropertyName ) + { + ((m_xUpdater.get())->*m_pSetter)( _rPropertyName ); + } + + static void forEach( const StringBag& _rProperties, const Reference< XObjectInspectorUI >& _rxDelegatorUI, FPropertyUISetter _pSetter ) + { + std::for_each( _rProperties.begin(), _rProperties.end(), PropertyUIOperator( _rxDelegatorUI, _pSetter ) ); + } + }; + + + // an interface which encapsulates access to a single aspect of the ->XObjectInspectorUI, + // where this aspect is given by a string key, and has a boolean value. + class IStringKeyBooleanUIUpdate + { + public: + virtual void updateUIForKey( const OUString& _rKey, bool _bFlag ) const = 0; + + virtual ~IStringKeyBooleanUIUpdate() { } + }; + + + // FPropertyUIFlagSetter + + /** an implementation of the ->IStringKeyBooleanUIUpdate interface which, + for a fixed ->XObjectInspectorUI instance and a fixed UI element (->PropertyLineElement), + updates this element for a given property with a given boolean flag + (->XObjectInspectorUI::enablePropertyUIElements) + */ + class EnablePropertyUIElement : public IStringKeyBooleanUIUpdate + { + private: + Reference< XObjectInspectorUI > m_xUIUpdate; + sal_Int16 m_nElement; + + public: + EnablePropertyUIElement( const Reference< XObjectInspectorUI >& _rxUIUpdate, sal_Int16 _nElement ) + :m_xUIUpdate( _rxUIUpdate ) + ,m_nElement( _nElement ) + { + } + // IStringKeyBooleanUIUpdate + virtual void updateUIForKey( const OUString& _rKey, bool _bFlag ) const override; + }; + + + void EnablePropertyUIElement::updateUIForKey( const OUString& _rKey, bool _bFlag ) const + { + m_xUIUpdate->enablePropertyUIElements( _rKey, m_nElement, _bFlag ); + } + + + // a ->XObjectInspectorUI method taking a string and a boolean + typedef void ( SAL_CALL XObjectInspectorUI::*FPropertyUIFlagSetter )( const OUString&, sal_Bool ); + + + // an implementation of the ->IStringKeyBooleanUIUpdate interface which calls + // an arbitrary ->XObjectInspectorUI method taking a string and a boolean flag + class DefaultStringKeyBooleanUIUpdate : public IStringKeyBooleanUIUpdate + { + private: + Reference< XObjectInspectorUI > m_xUIUpdate; + FPropertyUIFlagSetter m_pSetter; + + public: + DefaultStringKeyBooleanUIUpdate( const Reference< XObjectInspectorUI >& _rxUIUpdate, FPropertyUIFlagSetter _pSetter ); + // IStringKeyBooleanUIUpdate + virtual void updateUIForKey( const OUString& _rKey, bool _bFlag ) const override; + }; + + + DefaultStringKeyBooleanUIUpdate::DefaultStringKeyBooleanUIUpdate( const Reference< XObjectInspectorUI >& _rxUIUpdate, FPropertyUIFlagSetter _pSetter ) + :m_xUIUpdate( _rxUIUpdate ) + ,m_pSetter( _pSetter ) + { + } + + + void DefaultStringKeyBooleanUIUpdate::updateUIForKey( const OUString& _rKey, bool _bFlag ) const + { + ((m_xUIUpdate.get())->*m_pSetter)( _rKey, _bFlag ); + } + + + // an STL-compatible structure which applies a ->IStringKeyBooleanUIUpdate::updateUIForKey + // operation with a fixed boolean value, for a given string value + struct BooleanUIAspectUpdate + { + private: + const IStringKeyBooleanUIUpdate& m_rUpdater; + bool m_bFlag; + + public: + BooleanUIAspectUpdate( const IStringKeyBooleanUIUpdate& _rUpdater, bool _bFlag ) + :m_rUpdater( _rUpdater ) + ,m_bFlag( _bFlag ) + { + } + + void operator()( const OUString& _rPropertyName ) + { + m_rUpdater.updateUIForKey( _rPropertyName, m_bFlag ); + } + + static void forEach( const StringBag& _rProperties, const IStringKeyBooleanUIUpdate& _rUpdater, bool _bFlag ) + { + std::for_each( _rProperties.begin(), _rProperties.end(), BooleanUIAspectUpdate( _rUpdater, _bFlag ) ); + } + }; + + + // BooleanUIAspectUpdate + + // an STL-compatible structure subtracting a given string from a fixed ->StringBag + struct StringBagComplement + { + private: + StringBag& m_rMinuend; + + public: + explicit StringBagComplement( StringBag& _rMinuend ) :m_rMinuend( _rMinuend ) { } + + void operator()( const OUString& _rPropertyToSubtract ) + { + m_rMinuend.erase( _rPropertyToSubtract ); + } + + static void subtract( StringBag& _rMinuend, const StringBag& _rSubtrahend ) + { + std::for_each( _rSubtrahend.begin(), _rSubtrahend.end(), StringBagComplement( _rMinuend ) ); + } + }; + + + // BooleanUIAspectUpdate + + void lcl_fireUIStateFlag( + const IStringKeyBooleanUIUpdate& _rUIUpdate, + const ImplMapHandlerToUI& _rHandlerUIs, + CachedInspectorUI::FGetStringBag _pGetPositives, + CachedInspectorUI::FGetStringBag _pGetNegatives + ) + { + // all strings which are in the "positive" list of one handler + StringBag aAllPositives; + StringBagCollector::collectAll( aAllPositives, _rHandlerUIs, _pGetPositives ); + + // all strings which are in the "negative" list of one handler + StringBag aAllNegatives; + StringBagCollector::collectAll( aAllNegatives, _rHandlerUIs, _pGetNegatives ); + + // propagate the "negative" flags to the delegator UI + BooleanUIAspectUpdate::forEach( aAllNegatives, _rUIUpdate, false ); + + // propagate the "positive" flags to the delegator UI, for all elements where _no_ + // "negative" flag exists + StringBagComplement::subtract( aAllPositives, aAllNegatives ); + BooleanUIAspectUpdate::forEach( aAllPositives, _rUIUpdate, true ); + + // the "positive" request can be cleared no, only negative requests + // (such as "disable a property" or "hide a category") need to be preserved for the next round + StringBagClearer::clearAll( _rHandlerUIs, _pGetPositives ); + } + } + + + void ComposedPropertyUIUpdate::impl_fireEnablePropertyUI_throw() + { + lcl_fireUIStateFlag( + DefaultStringKeyBooleanUIUpdate( m_xDelegatorUI, &XObjectInspectorUI::enablePropertyUI ), + m_pCollectedUIs->aHandlers, + &CachedInspectorUI::getEnabledProperties, + &CachedInspectorUI::getDisabledProperties + ); + } + + + void ComposedPropertyUIUpdate::impl_fireRebuildPropertyUI_throw() + { + // collect all properties for which a rebuild request has been made + StringBag aAllRebuilt; + StringBagCollector::collectAll( aAllRebuilt, m_pCollectedUIs->aHandlers, &CachedInspectorUI::getRebuiltProperties ); + + // rebuild all those properties + PropertyUIOperator::forEach( aAllRebuilt, m_xDelegatorUI, &XObjectInspectorUI::rebuildPropertyUI ); + + // clear the "properties to rebuild" at all handlers, since the request has been fulfilled now. + StringBagClearer::clearAll( m_pCollectedUIs->aHandlers, &CachedInspectorUI::getRebuiltProperties ); + } + + + void ComposedPropertyUIUpdate::impl_fireShowHidePropertyUI_throw() + { + // all properties which have been shown by at least one handler + StringBag aAllShown; + StringBagCollector::collectAll( aAllShown, m_pCollectedUIs->aHandlers, &CachedInspectorUI::getShownProperties ); + // all properties which have been hidden by at least one handler + StringBag aAllHidden; + StringBagCollector::collectAll( aAllHidden, m_pCollectedUIs->aHandlers, &CachedInspectorUI::getHiddenProperties ); + + // hide properties as necessary + PropertyUIOperator::forEach( aAllHidden, m_xDelegatorUI, &XObjectInspectorUI::hidePropertyUI ); + + // for those properties which are hidden, ignore all "show" requests which other handlers might have had + StringBagComplement::subtract( aAllShown, aAllHidden ); + + // show properties + PropertyUIOperator::forEach( aAllShown, m_xDelegatorUI, &XObjectInspectorUI::showPropertyUI ); + } + + + void ComposedPropertyUIUpdate::impl_fireShowCategory_throw() + { + lcl_fireUIStateFlag( + DefaultStringKeyBooleanUIUpdate( m_xDelegatorUI, &XObjectInspectorUI::showCategory ), + m_pCollectedUIs->aHandlers, + &CachedInspectorUI::getShownCategories, + &CachedInspectorUI::getHiddenCategories + ); + } + + + void ComposedPropertyUIUpdate::impl_fireEnablePropertyUIElements_throw() + { + lcl_fireUIStateFlag( + EnablePropertyUIElement( m_xDelegatorUI, PropertyLineElement::InputControl ), + m_pCollectedUIs->aHandlers, + &CachedInspectorUI::getEnabledInputControls, + &CachedInspectorUI::getDisabledInputControls + ); + + lcl_fireUIStateFlag( + EnablePropertyUIElement( m_xDelegatorUI, PropertyLineElement::PrimaryButton ), + m_pCollectedUIs->aHandlers, + &CachedInspectorUI::getEnabledPrimaryButtons, + &CachedInspectorUI::getDisabledPrimaryButtons + ); + + lcl_fireUIStateFlag( + EnablePropertyUIElement( m_xDelegatorUI, PropertyLineElement::SecondaryButton ), + m_pCollectedUIs->aHandlers, + &CachedInspectorUI::getEnabledSecondaryButtons, + &CachedInspectorUI::getDisabledSecondaryButtons + ); + } + + + void ComposedPropertyUIUpdate::impl_fireAll_throw() + { + OSL_PRECOND( !impl_isDisposed(), "ComposedPropertyUIUpdate::impl_fireAll_throw: already disposed, this will crash!" ); + + impl_fireEnablePropertyUI_throw(); + impl_fireShowHidePropertyUI_throw(); + impl_fireRebuildPropertyUI_throw(); + impl_fireShowCategory_throw(); + impl_fireEnablePropertyUIElements_throw(); + } + + + void ComposedPropertyUIUpdate::suspendAutoFire() + { + impl_checkDisposed(); + osl_atomic_increment( &m_nSuspendCounter ); + } + + + void ComposedPropertyUIUpdate::resumeAutoFire() + { + impl_checkDisposed(); + if ( 0 == osl_atomic_decrement( &m_nSuspendCounter ) ) + impl_fireAll_throw(); + } + + + void ComposedPropertyUIUpdate::impl_checkDisposed() const + { + if ( impl_isDisposed() ) + throw DisposedException(); + } + + + void ComposedPropertyUIUpdate::callback_inspectorUIChanged_throw() + { + if ( 0 == m_nSuspendCounter ) + impl_fireAll_throw(); + } + + + Reference< XObjectInspectorUI > const & ComposedPropertyUIUpdate::getDelegatorUI() const + { + impl_checkDisposed(); + return m_xDelegatorUI; + } + + + void ComposedPropertyUIUpdate::dispose() + { + if ( impl_isDisposed() ) + return; + + OSL_ENSURE( m_nSuspendCounter == 0, "ComposedPropertyUIUpdate::dispose: still suspended, the changes will be lost!" ); + + for (auto const& singleUI : m_pCollectedUIs->aHandlers) + { + singleUI.second->dispose(); + } + m_pCollectedUIs.reset(); + m_xDelegatorUI.set( nullptr ); + } + + + bool ComposedPropertyUIUpdate::shouldContinuePropertyHandling( const OUString& _rName ) const + { + if ( !m_pPropertyCheck ) + return true; + if ( m_pPropertyCheck->hasPropertyByName( _rName ) ) + return true; + return false; + } + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/composeduiupdate.hxx b/extensions/source/propctrlr/composeduiupdate.hxx new file mode 100644 index 000000000..5e356af1f --- /dev/null +++ b/extensions/source/propctrlr/composeduiupdate.hxx @@ -0,0 +1,208 @@ +/* -*- 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/inspection/XObjectInspectorUI.hpp> +#include <com/sun/star/inspection/XPropertyHandler.hpp> + +#include <memory> + + +namespace pcr +{ + + struct MapHandlerToUI; + + /** callback for a ComposedPropertyUIUpdate checking a given property for existence + */ + class SAL_NO_VTABLE IPropertyExistenceCheck + { + public: + /// @throws css::uno::RuntimeException + virtual bool hasPropertyByName( const OUString& _rName ) = 0; + + protected: + ~IPropertyExistenceCheck() {} + }; + + /** helper class composing requests to a ->XObjectInspectorUI interface, coming + from multiple sources + + Usually, a handler tells the browser UI to enable to disable, or show or hide, certain + elements. Now when multiple handlers do this, their instructions must be combined: + If one handler disables a certain element, but others enable it, it must in the + result still be disabled. Similar for showing/hiding elements. + + ->ComposedPropertyUIUpdate implements this combination. It does so by providing a dedicated + ->XObjectInspectorUI instance for every participating handler, and remembering the UI + state on a per-handler basis. Upon request (->fire), the combined UI state is + forwarded to another ->XObjectInspectorUI instance, the so-called delegator UI. + */ + class ComposedPropertyUIUpdate + { + private: + std::unique_ptr< MapHandlerToUI > m_pCollectedUIs; + css::uno::Reference< css::inspection::XObjectInspectorUI > + m_xDelegatorUI; + oslInterlockedCount m_nSuspendCounter; + IPropertyExistenceCheck* m_pPropertyCheck; + + public: + /** constructs a ->ComposedPropertyUIUpdate instance + @param _rxDelegatorUI + a ->XObjectInspectorUI instance to which composed UI requests should be forwarded. Must + not be <NULL/>. + @param _pPropertyCheck + an instance checking properties for existence. If this is not <NULL/>, it will be invoked + whenever one of the ->XObjectInspectorUI methods is called, to check the passed property + name.<br/> + Beware of lifetime issues. The instance pointed to by <arg>_pPropertyCheck</arg> must + live at least as long as the ->ComposedPropertyUIUpdate instance you're going to create. + @throws css::lang::NullPointerException + if ->_rxDelegatorUI is <NULL/> + */ + ComposedPropertyUIUpdate( + const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxDelegatorUI, + IPropertyExistenceCheck* _pPropertyCheck ); + ~ComposedPropertyUIUpdate(); + + /** returns the delegator UI + @throw css::lang::DisposedException + */ + css::uno::Reference< css::inspection::XObjectInspectorUI > const & getDelegatorUI() const; + + /** returns a ->XObjectInspectorUI instance belonging to a given property handler + + In every call to an ->XPropertyHandler method which requires a ->XObjectInspectorUI, + the same UI instance should be used. The instance here will cache all requests passed + to it, and ->ComposedPropertyUIUpdate::fire will use the combination of all + cached UI states of all handlers to update the delegator UI. + */ + css::uno::Reference< css::inspection::XObjectInspectorUI > + getUIForPropertyHandler( const css::uno::Reference< css::inspection::XPropertyHandler >& _rxHandler ); + + /** Suspends automatic firing of UI changes + + normally, as soon as any of the property handlers does a request for an + arbitrary UI change, the set of collected UI changes is evaluated, and the combined + UI state is fired to the delegator UI. + + You can disable this automatic firing by calling ->suspendAutoFire. As longs as auto + firing is suspended, only explicit ->fire calls trigger the notification to the + delegator UI. + + Note that calls to ->suspendAutoFire are cumulative, that is, if you make multiple calls + they must be accompanied by an equal number of calls to ->resumeAutoFire, to enable + auto-firing again. + + @seealso resumeAutoFire + */ + void suspendAutoFire(); + + /** Suspends automatic firing of UI changes + + @seealso suspendAutoFire + */ + void resumeAutoFire(); + + /** disposes the instance, so it becomes non-functional. + + All cached handlers and cached ->XObjectInspectorUI instances will be released, + the latter will also be disposed, so that if anybody still holds a reference to them + and tries to operate them will get a DisposedException. + */ + void dispose(); + + /** invokes m_pPropertyCheck to check whether a given property should be handled + */ + bool shouldContinuePropertyHandling( const OUString& _rName ) const; + + private: + /// determines whether the instance is already disposed + bool impl_isDisposed() const { return !m_pCollectedUIs; } + + /// throws an exception if the component is already disposed + void impl_checkDisposed() const; + + /** fires the collected UI changes to our delegator UI + + All operations for any elements are forwarded: + <ul><li>If an element has been hidden at least once, it's also hidden at the delegator UI.</li> + <li>If an element has been shown at least once, and never been hidden, it's also + shown at the delegator UI.</li> + <li>If an element has never been shown or hidden, it's also not touched at the delegator UI.</li> + <li>The same holds if you replace "hidden" in the last three items with "disabled", + and "shown" with "enabled".</li> + <li>If an element should have been rebuilt (->XObjectInspectorUI::rebuiltPropertyUI) + at least once, it's rebuilt at the delegator UI, too.<br/> + After that, the request to rebuild the UI for this property is cleared, so subsequent + calls to ->fire will not trigger a new rebuilt request. + </ul> + + @precond + instance is not disposed + */ + void impl_fireAll_throw(); + + /// fires the combination of ->XObjectInspectorUI::enablePropertyUI calls + void impl_fireEnablePropertyUI_throw(); + + /// fires the combination of ->XObjectInspectorUI::enablePropertyUIElements calls + void impl_fireEnablePropertyUIElements_throw(); + + /// fires the combination of ->XObjectInspectorUI::rebuildPropertyUI calls + void impl_fireRebuildPropertyUI_throw(); + + /// fires the combination of ->XObjectInspectorUI::showPropertyUI and ->XObjectInspectorUI::hidePropertyUI calls + void impl_fireShowHidePropertyUI_throw(); + + /// fires the combination of ->XObjectInspectorUI::showCategory calls + void impl_fireShowCategory_throw(); + + /** callback for when a single property handler requested any change in the inspector UI + */ + void callback_inspectorUIChanged_throw(); + + private: + ComposedPropertyUIUpdate( const ComposedPropertyUIUpdate& ) = delete; + ComposedPropertyUIUpdate& operator=( const ComposedPropertyUIUpdate& ) = delete; + }; + + class ComposedUIAutoFireGuard + { + private: + ComposedPropertyUIUpdate& m_rUIUpdate; + public: + explicit ComposedUIAutoFireGuard( ComposedPropertyUIUpdate& _rUIUpdate ) + :m_rUIUpdate( _rUIUpdate ) + { + m_rUIUpdate.suspendAutoFire(); + } + ~ComposedUIAutoFireGuard() COVERITY_NOEXCEPT_FALSE + { + m_rUIUpdate.resumeAutoFire(); + } + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/controlfontdialog.cxx b/extensions/source/propctrlr/controlfontdialog.cxx new file mode 100644 index 000000000..08088e074 --- /dev/null +++ b/extensions/source/propctrlr/controlfontdialog.cxx @@ -0,0 +1,148 @@ +/* -*- 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 <com/sun/star/beans/PropertyAttribute.hpp> +#include "controlfontdialog.hxx" + +#include <comphelper/propertyvalue.hxx> +#include <vcl/svapp.hxx> +#include "fontdialog.hxx" +#include "formstrings.hxx" +#include "pcrcommon.hxx" + +namespace pcr +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + + OControlFontDialog::OControlFontDialog(const Reference< XComponentContext >& _rxContext ) + :OGenericUnoDialog( _rxContext ) + ,m_pItemPoolDefaults(nullptr) + { + registerProperty(PROPERTY_INTROSPECTEDOBJECT, OWN_PROPERTY_ID_INTROSPECTEDOBJECT, + PropertyAttribute::BOUND | PropertyAttribute::TRANSIENT, + &m_xControlModel, cppu::UnoType<decltype(m_xControlModel)>::get()); + } + + + OControlFontDialog::~OControlFontDialog() + { + if (m_xDialog) + { + ::osl::MutexGuard aGuard(m_aMutex); + if (m_xDialog) + { + destroyDialog(); + ControlCharacterDialog::destroyItemSet(m_pFontItems, m_pItemPool, m_pItemPoolDefaults); + } + } + } + + + Sequence<sal_Int8> SAL_CALL OControlFontDialog::getImplementationId( ) + { + return css::uno::Sequence<sal_Int8>(); + } + + + OUString SAL_CALL OControlFontDialog::getImplementationName() + { + return "org.openoffice.comp.form.ui.OControlFontDialog"; + } + + + css::uno::Sequence<OUString> SAL_CALL OControlFontDialog::getSupportedServiceNames() + { + return { "com.sun.star.form.ControlFontDialog" }; + } + + void OControlFontDialog::initialize( const Sequence< Any >& aArguments ) + { + Reference<XPropertySet> xGridModel; + if (aArguments.getLength() == 1 && (aArguments[0] >>= xGridModel)) + { + Sequence aNewArguments{ Any(comphelper::makePropertyValue("IntrospectedObject", + xGridModel)) }; + OControlFontDialog_DBase::initialize(aNewArguments); + } + else + OControlFontDialog_DBase::initialize(aArguments); + } + + + Reference<XPropertySetInfo> SAL_CALL OControlFontDialog::getPropertySetInfo() + { + Reference<XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + + ::cppu::IPropertyArrayHelper& OControlFontDialog::getInfoHelper() + { + return *getArrayHelper(); + } + + + ::cppu::IPropertyArrayHelper* OControlFontDialog::createArrayHelper( ) const + { + Sequence< Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); + } + + std::unique_ptr<weld::DialogController> OControlFontDialog::createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) + { + ControlCharacterDialog::createItemSet(m_pFontItems, m_pItemPool, m_pItemPoolDefaults); + + OSL_ENSURE(m_xControlModel.is(), "OControlFontDialog::createDialog: no introspectee set!"); + if (m_xControlModel.is()) + ControlCharacterDialog::translatePropertiesToItems(m_xControlModel, m_pFontItems.get()); + // TODO: we need a mechanism to prevent that somebody creates us, sets an introspectee, executes us, + // sets a new introspectee and re-executes us. In this case, the dialog returned here (upon the first + // execute) will be re-used upon the second execute, and thus it won't be initialized correctly. + + return std::make_unique<ControlCharacterDialog>(Application::GetFrameWeld(rParent), *m_pFontItems); + } + + void OControlFontDialog::executedDialog(sal_Int16 _nExecutionResult) + { + OSL_ENSURE(m_xDialog, "OControlFontDialog::executedDialog: no dialog anymore?!!"); + if (m_xDialog && (RET_OK == _nExecutionResult) && m_xControlModel.is()) + { + const SfxItemSet* pOutput = static_cast<ControlCharacterDialog*>(m_xDialog.get())->GetOutputItemSet(); + if (pOutput) + ControlCharacterDialog::translateItemsToProperties( *pOutput, m_xControlModel ); + } + } + + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_OControlFontDialog_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::OControlFontDialog(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/controlfontdialog.hxx b/extensions/source/propctrlr/controlfontdialog.hxx new file mode 100644 index 000000000..8dc520102 --- /dev/null +++ b/extensions/source/propctrlr/controlfontdialog.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 <comphelper/proparrhlp.hxx> +#include <svtools/genericunodialog.hxx> + +class SfxItemSet; +class SfxItemPool; +class SfxPoolItem; + +namespace pcr +{ + + class OControlFontDialog; + typedef ::svt::OGenericUnoDialog OControlFontDialog_DBase; + typedef ::comphelper::OPropertyArrayUsageHelper< OControlFontDialog > OControlFontDialog_PBase; + + class OControlFontDialog + :public OControlFontDialog_DBase + ,public OControlFontDialog_PBase + { + protected: + // <properties> + css::uno::Reference< css::beans::XPropertySet > + m_xControlModel; + // </properties> + + std::unique_ptr<SfxItemSet> m_pFontItems; // item set for the dialog + rtl::Reference<SfxItemPool> m_pItemPool; // item pool for the item set for the dialog + std::vector<SfxPoolItem*>* + m_pItemPoolDefaults; // pool defaults + + public: + explicit OControlFontDialog(const css::uno::Reference< css::uno::XComponentContext >& _rxContext); + virtual ~OControlFontDialog() override; + + // XTypeProvider + virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId( ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + protected: + // OGenericUnoDialog overridables + virtual std::unique_ptr<weld::DialogController> createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) override; + virtual void executedDialog(sal_Int16 _nExecutionResult) override; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/controltype.hxx b/extensions/source/propctrlr/controltype.hxx new file mode 100644 index 000000000..89a400a76 --- /dev/null +++ b/extensions/source/propctrlr/controltype.hxx @@ -0,0 +1,35 @@ +/* -*- 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 pcr::ControlType +{ + const sal_Int16 FIXEDLINE = sal_Int16(100); + const sal_Int16 FORMATTEDFIELD = sal_Int16(101); + const sal_Int16 PROGRESSBAR = sal_Int16(102); + + // need only those which are not already covered as FormComponentType + +} // namespace pcr::ControlType + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/defaultforminspection.cxx b/extensions/source/propctrlr/defaultforminspection.cxx new file mode 100644 index 000000000..1fe8ee60e --- /dev/null +++ b/extensions/source/propctrlr/defaultforminspection.cxx @@ -0,0 +1,215 @@ +/* -*- 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 "defaultforminspection.hxx" +#include "pcrcommon.hxx" +#include <helpids.h> +#include <strings.hrc> +#include "modulepcr.hxx" +#include "formmetadata.hxx" + +#include <com/sun/star/ucb/AlreadyInitializedException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <sal/macros.h> + + +namespace pcr +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::XInterface; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::inspection::PropertyCategoryDescriptor; + using ::com::sun::star::ucb::AlreadyInitializedException; + using ::com::sun::star::lang::IllegalArgumentException; + + DefaultFormComponentInspectorModel::DefaultFormComponentInspectorModel( bool _bUseFormFormComponentHandlers ) + :m_bUseFormComponentHandlers( _bUseFormFormComponentHandlers ) + ,m_bConstructed( false ) + ,m_pInfoService( new OPropertyInfoService ) + { + } + + + DefaultFormComponentInspectorModel::~DefaultFormComponentInspectorModel() + { + } + + + OUString SAL_CALL DefaultFormComponentInspectorModel::getImplementationName( ) + { + return "org.openoffice.comp.extensions.DefaultFormComponentInspectorModel"; + } + + + Sequence< OUString > SAL_CALL DefaultFormComponentInspectorModel::getSupportedServiceNames( ) + { + return { "com.sun.star.form.inspection.DefaultFormComponentInspectorModel" }; + } + + + Sequence< Any > SAL_CALL DefaultFormComponentInspectorModel::getHandlerFactories() + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // service names for all our handlers + static struct + { + const char* serviceName; + bool isFormOnly; + } const aFactories[] = { + + // a generic handler for form component properties (must precede the ButtonNavigationHandler) + { "com.sun.star.form.inspection.FormComponentPropertyHandler", false }, + + // generic virtual edit properties + { "com.sun.star.form.inspection.EditPropertyHandler", false }, + + // a handler which virtualizes the ButtonType property, to provide additional types like + // "move to next record" + { "com.sun.star.form.inspection.ButtonNavigationHandler", false }, + + // a handler for script events bound to form components or dialog elements + { "com.sun.star.form.inspection.EventHandler", false }, + + // a handler which introduces virtual properties for binding controls to spreadsheet cells + { "com.sun.star.form.inspection.CellBindingPropertyHandler", false }, + + // properties related to binding to an XForms DOM node + { "com.sun.star.form.inspection.XMLFormsPropertyHandler", true }, + + // properties related to the XSD data against which a control content is validated + { "com.sun.star.form.inspection.XSDValidationPropertyHandler", true }, + + // a handler which cares for XForms submissions + { "com.sun.star.form.inspection.SubmissionPropertyHandler", true }, + + // a handler which cares for geometry properties of form controls + { "com.sun.star.form.inspection.FormGeometryHandler", true } + }; + + sal_Int32 nFactories = SAL_N_ELEMENTS( aFactories ); + Sequence< Any > aReturn( nFactories ); + Any* pReturn = aReturn.getArray(); + for ( sal_Int32 i = 0; i < nFactories; ++i ) + { + if ( aFactories[i].isFormOnly && !m_bUseFormComponentHandlers ) + continue; + *pReturn++ <<= OUString::createFromAscii( aFactories[i].serviceName ); + } + aReturn.realloc( pReturn - aReturn.getArray() ); + + return aReturn; + } + + + Sequence< PropertyCategoryDescriptor > SAL_CALL DefaultFormComponentInspectorModel::describeCategories( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + static struct + { + const char* programmaticName; + TranslateId uiNameResId; + rtl::OUStringConstExpr helpId; + } const aCategories[] = { + { "General", RID_STR_PROPPAGE_DEFAULT, HID_FM_PROPDLG_TAB_GENERAL }, + { "Data", RID_STR_PROPPAGE_DATA, HID_FM_PROPDLG_TAB_DATA }, + { "Events", RID_STR_EVENTS, HID_FM_PROPDLG_TAB_EVT } + }; + + sal_Int32 nCategories = SAL_N_ELEMENTS( aCategories ); + Sequence< PropertyCategoryDescriptor > aReturn( nCategories ); + PropertyCategoryDescriptor* pReturn = aReturn.getArray(); + for ( sal_Int32 i=0; i<nCategories; ++i, ++pReturn ) + { + pReturn->ProgrammaticName = OUString::createFromAscii( aCategories[i].programmaticName ); + pReturn->UIName = PcrRes( aCategories[i].uiNameResId ); + pReturn->HelpURL = HelpIdUrl::getHelpURL( aCategories[i].helpId.asView() ); + } + + return aReturn; + } + + + ::sal_Int32 SAL_CALL DefaultFormComponentInspectorModel::getPropertyOrderIndex( const OUString& _rPropertyName ) + { + sal_Int32 nPropertyId( m_pInfoService->getPropertyId( _rPropertyName ) ); + if ( nPropertyId == -1 ) + { + if ( _rPropertyName.indexOf( ';' ) != -1 ) + // it's an event. Just give it an arbitrary number - events will be on a separate + // page, and by definition, if two properties have the same OrderIndex, then + // they will be ordered as they appear in the handler's getSupportedProperties. + return 1000; + return 0; + } + return m_pInfoService->getPropertyPos( nPropertyId ); + } + + + void SAL_CALL DefaultFormComponentInspectorModel::initialize( const Sequence< Any >& _arguments ) + { + if ( m_bConstructed ) + throw AlreadyInitializedException(); + + StlSyntaxSequence< Any > arguments( _arguments ); + if ( arguments.empty() ) + { // constructor: "createDefault()" + m_bConstructed = true; + return; + } + + if ( arguments.size() == 2 ) + { // constructor: "createWithHelpSection( long, long )" + sal_Int32 nMinHelpTextLines( 0 ), nMaxHelpTextLines( 0 ); + if ( !( arguments[0] >>= nMinHelpTextLines ) || !( arguments[1] >>= nMaxHelpTextLines ) ) + throw IllegalArgumentException( OUString(), *this, 0 ); + createWithHelpSection( nMinHelpTextLines, nMaxHelpTextLines ); + return; + } + + throw IllegalArgumentException( OUString(), *this, 0 ); + } + + + void DefaultFormComponentInspectorModel::createWithHelpSection( sal_Int32 _nMinHelpTextLines, sal_Int32 _nMaxHelpTextLines ) + { + if ( ( _nMinHelpTextLines <= 0 ) || ( _nMaxHelpTextLines <= 0 ) || ( _nMinHelpTextLines > _nMaxHelpTextLines ) ) + throw IllegalArgumentException( OUString(), *this, 0 ); + + enableHelpSectionProperties( _nMinHelpTextLines, _nMaxHelpTextLines ); + m_bConstructed = true; + } + + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_DefaultFormComponentInspectorModel_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::DefaultFormComponentInspectorModel(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/defaultforminspection.hxx b/extensions/source/propctrlr/defaultforminspection.hxx new file mode 100644 index 000000000..e118e9da6 --- /dev/null +++ b/extensions/source/propctrlr/defaultforminspection.hxx @@ -0,0 +1,66 @@ +/* -*- 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 "inspectormodelbase.hxx" + +#include <memory> + + +namespace pcr +{ + + + class OPropertyInfoService; + + class DefaultFormComponentInspectorModel final : public ImplInspectorModel + { + bool m_bUseFormComponentHandlers; + bool m_bConstructed; + + /// access to property meta data + std::unique_ptr< OPropertyInfoService > m_pInfoService; + + virtual ~DefaultFormComponentInspectorModel() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XObjectInspectorModel + virtual css::uno::Sequence< css::uno::Any > SAL_CALL getHandlerFactories() override; + virtual css::uno::Sequence< css::inspection::PropertyCategoryDescriptor > SAL_CALL describeCategories( ) override; + virtual ::sal_Int32 SAL_CALL getPropertyOrderIndex( const OUString& PropertyName ) override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + public: + explicit DefaultFormComponentInspectorModel( bool _bUseFormFormComponentHandlers = true ); + + private: + // Service constructors + void createWithHelpSection( sal_Int32 _nMinHelpTextLines, sal_Int32 _nMaxHelpTextLines ); + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/defaulthelpprovider.cxx b/extensions/source/propctrlr/defaulthelpprovider.cxx new file mode 100644 index 000000000..8f1e22b82 --- /dev/null +++ b/extensions/source/propctrlr/defaulthelpprovider.cxx @@ -0,0 +1,184 @@ +/* -*- 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 "defaulthelpprovider.hxx" +#include "pcrcommon.hxx" + +#include <com/sun/star/ucb/AlreadyInitializedException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/window.hxx> +#include <tools/diagnose_ex.h> +#include <cppuhelper/supportsservice.hxx> + + +namespace pcr +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::inspection::XPropertyControl; + using ::com::sun::star::uno::RuntimeException; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::inspection::XObjectInspectorUI; + using ::com::sun::star::uno::XInterface; + using ::com::sun::star::ucb::AlreadyInitializedException; + using ::com::sun::star::lang::IllegalArgumentException; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::awt::XWindow; + + DefaultHelpProvider::DefaultHelpProvider() + :m_bConstructed( false ) + { + } + + + DefaultHelpProvider::~DefaultHelpProvider() + { + } + + + Sequence< OUString > SAL_CALL DefaultHelpProvider::getSupportedServiceNames() + { + return { "com.sun.star.inspection.DefaultHelpProvider" }; + } + + OUString SAL_CALL DefaultHelpProvider::getImplementationName() + { + return "org.openoffice.comp.extensions.DefaultHelpProvider"; + } + + sal_Bool SAL_CALL DefaultHelpProvider::supportsService(const OUString& aServiceName) + { + return cppu::supportsService(this, aServiceName); + } + + + void SAL_CALL DefaultHelpProvider::focusGained( const Reference< XPropertyControl >& Control ) + { + if ( !m_xInspectorUI.is() ) + throw RuntimeException( OUString(), *this ); + + try + { + m_xInspectorUI->setHelpSectionText( impl_getHelpText_nothrow( Control ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + + void SAL_CALL DefaultHelpProvider::valueChanged( const Reference< XPropertyControl >& ) + { + // not interested in + } + + + void SAL_CALL DefaultHelpProvider::initialize( const Sequence< Any >& _arguments ) + { + if ( m_bConstructed ) + throw AlreadyInitializedException(); + + StlSyntaxSequence< Any > arguments( _arguments ); + if ( arguments.size() == 1 ) + { // constructor: "create( XObjectInspectorUI )" + Reference< XObjectInspectorUI > xUI( arguments[0], UNO_QUERY ); + create( xUI ); + return; + } + + throw IllegalArgumentException( OUString(), *this, 0 ); + } + + + void DefaultHelpProvider::create( const Reference< XObjectInspectorUI >& _rxUI ) + { + if ( !_rxUI.is() ) + throw IllegalArgumentException( OUString(), *this, 1 ); + + try + { + m_xInspectorUI = _rxUI; + m_xInspectorUI->registerControlObserver( this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + + m_bConstructed = true; + } + + + vcl::Window* DefaultHelpProvider::impl_getVclControlWindow_nothrow( const Reference< XPropertyControl >& _rxControl ) + { + vcl::Window* pControlWindow = nullptr; + OSL_PRECOND( _rxControl.is(), "DefaultHelpProvider::impl_getVclControlWindow_nothrow: illegal control!" ); + if ( !_rxControl.is() ) + return pControlWindow; + + try + { + Reference< XWindow > xControlWindow( _rxControl->getControlWindow(), css::uno::UNO_SET_THROW ); + pControlWindow = VCLUnoHelper::GetWindow( xControlWindow ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + + return pControlWindow; + } + + + OUString DefaultHelpProvider::impl_getHelpText_nothrow( const Reference< XPropertyControl >& _rxControl ) + { + OUString sHelpText; + OSL_PRECOND( _rxControl.is(), "DefaultHelpProvider::impl_getHelpText_nothrow: illegal control!" ); + if ( !_rxControl.is() ) + return sHelpText; + + vcl::Window* pControlWindow( impl_getVclControlWindow_nothrow( _rxControl ) ); + OSL_ENSURE( pControlWindow, "DefaultHelpProvider::impl_getHelpText_nothrow: could not determine the VCL window!" ); + if ( !pControlWindow ) + return sHelpText; + + sHelpText = pControlWindow->GetHelpText(); + return sHelpText; + } + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_DefaultHelpProvider_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::DefaultHelpProvider()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/defaulthelpprovider.hxx b/extensions/source/propctrlr/defaulthelpprovider.hxx new file mode 100644 index 000000000..f647937dd --- /dev/null +++ b/extensions/source/propctrlr/defaulthelpprovider.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/inspection/XPropertyControlObserver.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/inspection/XObjectInspectorUI.hpp> + +#include <cppuhelper/implbase.hxx> + +namespace vcl { class Window; } + + +namespace pcr +{ + + + //= DefaultHelpProvider + + typedef ::cppu::WeakImplHelper < css::inspection::XPropertyControlObserver + , css::lang::XInitialization + , css::lang::XServiceInfo + > DefaultHelpProvider_Base; + class DefaultHelpProvider final : public DefaultHelpProvider_Base + { + private: + bool m_bConstructed; + css::uno::Reference< css::inspection::XObjectInspectorUI > + m_xInspectorUI; + + public: + DefaultHelpProvider(); + + private: + virtual ~DefaultHelpProvider() 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; + + // XPropertyControlObserver + virtual void SAL_CALL focusGained( const css::uno::Reference< css::inspection::XPropertyControl >& Control ) override; + virtual void SAL_CALL valueChanged( const css::uno::Reference< css::inspection::XPropertyControl >& Control ) override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // Service constructors + void create( const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxUI ); + + static vcl::Window* impl_getVclControlWindow_nothrow( const css::uno::Reference< css::inspection::XPropertyControl >& _rxControl ); + static OUString impl_getHelpText_nothrow( const css::uno::Reference< css::inspection::XPropertyControl >& _rxControl ); + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/editpropertyhandler.cxx b/extensions/source/propctrlr/editpropertyhandler.cxx new file mode 100644 index 000000000..22ad04988 --- /dev/null +++ b/extensions/source/propctrlr/editpropertyhandler.cxx @@ -0,0 +1,308 @@ +/* -*- 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 "editpropertyhandler.hxx" +#include "formstrings.hxx" +#include "formmetadata.hxx" + +#include <com/sun/star/inspection/XObjectInspectorUI.hpp> +#include <com/sun/star/lang/NullPointerException.hpp> +#include <tools/diagnose_ex.h> + +#define TEXTTYPE_SINGLELINE 0 +#define TEXTTYPE_MULTILINE 1 +#define TEXTTYPE_RICHTEXT 2 + + +namespace pcr +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::script; + using namespace ::com::sun::star::frame; + using namespace ::com::sun::star::inspection; + + + //= EditPropertyHandler + + + EditPropertyHandler::EditPropertyHandler( const Reference< XComponentContext >& _rxContext ) + :PropertyHandlerComponent( _rxContext ) + { + } + + + EditPropertyHandler::~EditPropertyHandler( ) + { + } + + + OUString EditPropertyHandler::getImplementationName( ) + { + return "com.sun.star.comp.extensions.EditPropertyHandler"; + } + + + Sequence< OUString > EditPropertyHandler::getSupportedServiceNames( ) + { + return { "com.sun.star.form.inspection.EditPropertyHandler" }; + } + + + Any SAL_CALL EditPropertyHandler::getPropertyValue( const OUString& _rPropertyName ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + Any aReturn; + try + { + switch ( nPropId ) + { + case PROPERTY_ID_SHOW_SCROLLBARS: + { + bool bHasVScroll = false; + m_xComponent->getPropertyValue( PROPERTY_VSCROLL ) >>= bHasVScroll; + bool bHasHScroll = false; + m_xComponent->getPropertyValue( PROPERTY_HSCROLL ) >>= bHasHScroll; + + aReturn <<= static_cast<sal_Int32>( ( bHasVScroll ? 2 : 0 ) + ( bHasHScroll ? 1 : 0 ) ); + } + break; + + case PROPERTY_ID_TEXTTYPE: + { + sal_Int32 nTextType = TEXTTYPE_SINGLELINE; + bool bRichText = false; + OSL_VERIFY( m_xComponent->getPropertyValue( PROPERTY_RICHTEXT ) >>= bRichText ); + if ( bRichText ) + nTextType = TEXTTYPE_RICHTEXT; + else + { + bool bMultiLine = false; + OSL_VERIFY( m_xComponent->getPropertyValue( PROPERTY_MULTILINE ) >>= bMultiLine ); + if ( bMultiLine ) + nTextType = TEXTTYPE_MULTILINE; + else + nTextType = TEXTTYPE_SINGLELINE; + } + aReturn <<= nTextType; + } + break; + + + default: + OSL_FAIL( "EditPropertyHandler::getPropertyValue: cannot handle this property!" ); + break; + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EditPropertyHandler::getPropertyValue" ); + } + + return aReturn; + } + + + void SAL_CALL EditPropertyHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + try + { + switch ( nPropId ) + { + case PROPERTY_ID_SHOW_SCROLLBARS: + { + sal_Int32 nScrollbars = 0; + _rValue >>= nScrollbars; + + bool bHasVScroll = 0 != ( nScrollbars & 2 ); + bool bHasHScroll = 0 != ( nScrollbars & 1 ); + + m_xComponent->setPropertyValue( PROPERTY_VSCROLL, Any( bHasVScroll ) ); + m_xComponent->setPropertyValue( PROPERTY_HSCROLL, Any( bHasHScroll ) ); + } + break; + + case PROPERTY_ID_TEXTTYPE: + { + bool bMultiLine = false; + bool bRichText = false; + sal_Int32 nTextType = TEXTTYPE_SINGLELINE; + OSL_VERIFY( _rValue >>= nTextType ); + switch ( nTextType ) + { + case TEXTTYPE_SINGLELINE: bMultiLine = bRichText = false; break; + case TEXTTYPE_MULTILINE: bMultiLine = true; bRichText = false; break; + case TEXTTYPE_RICHTEXT: bMultiLine = true; bRichText = true; break; + default: + OSL_FAIL( "EditPropertyHandler::setPropertyValue: invalid text type!" ); + } + + m_xComponent->setPropertyValue( PROPERTY_MULTILINE, Any( bMultiLine ) ); + m_xComponent->setPropertyValue( PROPERTY_RICHTEXT, Any( bRichText ) ); + } + break; + + default: + OSL_FAIL( "EditPropertyHandler::setPropertyValue: cannot handle this id!" ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EditPropertyHandler::setPropertyValue" ); + } + } + + + bool EditPropertyHandler::implHaveBothScrollBarProperties() const + { + // have a "Scrollbars" property if the object supports both "HScroll" and "VScroll" + Reference< XPropertySetInfo > xPSI; + if ( m_xComponent.is() ) + xPSI = m_xComponent->getPropertySetInfo(); + + return xPSI.is() + && xPSI->hasPropertyByName( PROPERTY_HSCROLL ) + && xPSI->hasPropertyByName( PROPERTY_VSCROLL ); + } + + + bool EditPropertyHandler::implHaveTextTypeProperty() const + { + // have a "Scrollbars" property if the object supports both "HScroll" and "VScroll" + Reference< XPropertySetInfo > xPSI; + if ( m_xComponent.is() ) + xPSI = m_xComponent->getPropertySetInfo(); + + return xPSI.is() + && xPSI->hasPropertyByName( PROPERTY_RICHTEXT ) + && xPSI->hasPropertyByName( PROPERTY_MULTILINE ); + } + + + Sequence< Property > EditPropertyHandler::doDescribeSupportedProperties() const + { + std::vector< Property > aProperties; + + if ( implHaveBothScrollBarProperties() ) + addInt32PropertyDescription( aProperties, PROPERTY_SHOW_SCROLLBARS ); + + if ( implHaveTextTypeProperty() ) + addInt32PropertyDescription( aProperties, PROPERTY_TEXTTYPE ); + + if ( aProperties.empty() ) + return Sequence< Property >(); + return comphelper::containerToSequence(aProperties); + } + + + Sequence< OUString > SAL_CALL EditPropertyHandler::getSupersededProperties( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + std::vector< OUString > aSuperseded; + if ( implHaveBothScrollBarProperties() ) + { + aSuperseded.push_back( PROPERTY_HSCROLL ); + aSuperseded.push_back( PROPERTY_VSCROLL ); + } + if ( implHaveTextTypeProperty() ) + { + aSuperseded.push_back( PROPERTY_RICHTEXT ); + aSuperseded.push_back( PROPERTY_MULTILINE ); + } + if ( aSuperseded.empty() ) + return Sequence< OUString >(); + return comphelper::containerToSequence(aSuperseded); + } + + + Sequence< OUString > SAL_CALL EditPropertyHandler::getActuatingProperties( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + std::vector< OUString > aInterestingActuatingProps; + if ( implHaveTextTypeProperty() ) + aInterestingActuatingProps.push_back( PROPERTY_TEXTTYPE ); + aInterestingActuatingProps.push_back( PROPERTY_MULTILINE ); + return comphelper::containerToSequence(aInterestingActuatingProps); + } + + + void SAL_CALL EditPropertyHandler::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool ) + { + if ( !_rxInspectorUI.is() ) + throw NullPointerException(); + + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nActuatingPropId( impl_getPropertyId_throwRuntime( _rActuatingPropertyName ) ); + switch ( nActuatingPropId ) + { + case PROPERTY_ID_TEXTTYPE: + { + sal_Int32 nTextType = TEXTTYPE_SINGLELINE; + getPropertyValue( PROPERTY_TEXTTYPE ) >>= nTextType; + + if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_WORDBREAK ) ) + _rxInspectorUI->enablePropertyUI( PROPERTY_WORDBREAK, nTextType == TEXTTYPE_RICHTEXT ); + _rxInspectorUI->enablePropertyUI( PROPERTY_MAXTEXTLEN, nTextType != TEXTTYPE_RICHTEXT ); + _rxInspectorUI->enablePropertyUI( PROPERTY_ECHO_CHAR, nTextType == TEXTTYPE_SINGLELINE ); + _rxInspectorUI->enablePropertyUI( PROPERTY_FONT, nTextType != TEXTTYPE_RICHTEXT ); + _rxInspectorUI->enablePropertyUI( PROPERTY_ALIGN, nTextType != TEXTTYPE_RICHTEXT ); + _rxInspectorUI->enablePropertyUI( PROPERTY_DEFAULT_TEXT, nTextType != TEXTTYPE_RICHTEXT ); + _rxInspectorUI->enablePropertyUI( PROPERTY_SHOW_SCROLLBARS, nTextType != TEXTTYPE_SINGLELINE ); + _rxInspectorUI->enablePropertyUI( PROPERTY_LINEEND_FORMAT, nTextType != TEXTTYPE_SINGLELINE ); + _rxInspectorUI->enablePropertyUI( PROPERTY_VERTICAL_ALIGN, nTextType == TEXTTYPE_SINGLELINE ); + + _rxInspectorUI->showCategory( "Data", nTextType != TEXTTYPE_RICHTEXT ); + } + break; + + case PROPERTY_ID_MULTILINE: + { + bool bIsMultiline = false; + _rNewValue >>= bIsMultiline; + + _rxInspectorUI->enablePropertyUI( PROPERTY_SHOW_SCROLLBARS, bIsMultiline ); + _rxInspectorUI->enablePropertyUI( PROPERTY_ECHO_CHAR, !bIsMultiline ); + _rxInspectorUI->enablePropertyUI( PROPERTY_LINEEND_FORMAT, bIsMultiline ); + } + break; + + default: + OSL_FAIL( "EditPropertyHandler::actuatingPropertyChanged: cannot handle this id!" ); + } + } + + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_EditPropertyHandler_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::EditPropertyHandler(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/editpropertyhandler.hxx b/extensions/source/propctrlr/editpropertyhandler.hxx new file mode 100644 index 000000000..33ad8df98 --- /dev/null +++ b/extensions/source/propctrlr/editpropertyhandler.hxx @@ -0,0 +1,68 @@ +/* -*- 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 "propertyhandler.hxx" + + +namespace pcr +{ + + + //= EditPropertyHandler + + class EditPropertyHandler; + /** a property handler for any virtual string properties + */ + class EditPropertyHandler : public PropertyHandlerComponent + { + public: + explicit EditPropertyHandler( + const css::uno::Reference< css::uno::XComponentContext >& _rxContext + ); + + protected: + virtual ~EditPropertyHandler() override; + + protected: + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override; + + // XPropertyHandler overriables + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override; + virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupersededProperties( ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getActuatingProperties( ) override; + virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool ) override; + + // PropertyHandler overridables + virtual css::uno::Sequence< css::beans::Property > + doDescribeSupportedProperties() const override; + private: + bool implHaveBothScrollBarProperties() const; + bool implHaveTextTypeProperty() const; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/eformshelper.cxx b/extensions/source/propctrlr/eformshelper.cxx new file mode 100644 index 000000000..620368e9d --- /dev/null +++ b/extensions/source/propctrlr/eformshelper.cxx @@ -0,0 +1,762 @@ +/* -*- 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 <string_view> + +#include "eformshelper.hxx" +#include "formstrings.hxx" +#include <strings.hrc> +#include "modulepcr.hxx" +#include "propeventtranslation.hxx" +#include "formbrowsertools.hxx" + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/xforms/XFormsUIHelper1.hpp> +#include <com/sun/star/xsd/DataTypeClass.hpp> +#include <com/sun/star/form/binding/XListEntrySink.hpp> +#include <tools/diagnose_ex.h> + +#include <algorithm> +#include <o3tl/functional.hxx> + +namespace pcr +{ + + + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::form::binding; + using namespace ::com::sun::star::xsd; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::form; + + + //= file-local helpers + + namespace + { + + OUString composeModelElementUIName( std::u16string_view _rModelName, std::u16string_view _rElementName ) + { + OUString a = OUString::Concat("[") + + _rModelName + "] " + + _rElementName; + return a; + } + } + + + //= EFormsHelper + + + EFormsHelper::EFormsHelper( ::osl::Mutex& _rMutex, const Reference< XPropertySet >& _rxControlModel, const Reference< frame::XModel >& _rxContextDocument ) + :m_xControlModel( _rxControlModel ) + ,m_aPropertyListeners( _rMutex ) + { + OSL_ENSURE( _rxControlModel.is(), "EFormsHelper::EFormsHelper: invalid control model!" ); + m_xBindableControl.set(_rxControlModel, css::uno::UNO_QUERY); + + m_xDocument.set(_rxContextDocument, css::uno::UNO_QUERY); + OSL_ENSURE( m_xDocument.is(), "EFormsHelper::EFormsHelper: invalid document!" ); + + } + + + bool EFormsHelper::isEForm( const Reference< frame::XModel >& _rxContextDocument ) + { + try + { + Reference< xforms::XFormsSupplier > xDocument( _rxContextDocument, UNO_QUERY ); + if ( !xDocument.is() ) + return false; + + return xDocument->getXForms().is(); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::isEForm" ); + } + return false; + } + + + bool EFormsHelper::canBindToDataType( sal_Int32 _nDataType ) const + { + if ( !m_xBindableControl.is() ) + // cannot bind at all + return false; + + // some types cannot be bound, independent from the control type + if ( ( DataTypeClass::hexBinary == _nDataType ) + || ( DataTypeClass::base64Binary == _nDataType ) + || ( DataTypeClass::QName == _nDataType ) + || ( DataTypeClass::NOTATION == _nDataType ) + ) + return false; + + bool bCan = false; + try + { + // classify the control model + sal_Int16 nControlType = FormComponentType::CONTROL; + OSL_VERIFY( m_xControlModel->getPropertyValue( PROPERTY_CLASSID ) >>= nControlType ); + + // some lists + sal_Int16 const nNumericCompatibleTypes[] = { DataTypeClass::DECIMAL, DataTypeClass::FLOAT, DataTypeClass::DOUBLE, 0 }; + sal_Int16 const nDateCompatibleTypes[] = { DataTypeClass::DATE, 0 }; + sal_Int16 const nTimeCompatibleTypes[] = { DataTypeClass::TIME, 0 }; + sal_Int16 const nCheckboxCompatibleTypes[] = { DataTypeClass::BOOLEAN, DataTypeClass::STRING, DataTypeClass::anyURI, 0 }; + sal_Int16 const nRadiobuttonCompatibleTypes[] = { DataTypeClass::STRING, DataTypeClass::anyURI, 0 }; + sal_Int16 const nFormattedCompatibleTypes[] = { DataTypeClass::DECIMAL, DataTypeClass::FLOAT, DataTypeClass::DOUBLE, DataTypeClass::DATETIME, DataTypeClass::DATE, DataTypeClass::TIME, 0 }; + + sal_Int16 const * pCompatibleTypes = nullptr; + switch ( nControlType ) + { + case FormComponentType::SPINBUTTON: + case FormComponentType::NUMERICFIELD: + pCompatibleTypes = nNumericCompatibleTypes; + break; + case FormComponentType::DATEFIELD: + pCompatibleTypes = nDateCompatibleTypes; + break; + case FormComponentType::TIMEFIELD: + pCompatibleTypes = nTimeCompatibleTypes; + break; + case FormComponentType::CHECKBOX: + pCompatibleTypes = nCheckboxCompatibleTypes; + break; + case FormComponentType::RADIOBUTTON: + pCompatibleTypes = nRadiobuttonCompatibleTypes; + break; + + case FormComponentType::TEXTFIELD: + { + // both the normal text field, and the formatted field, claim to be a TEXTFIELD + // need to distinguish by service name + Reference< XServiceInfo > xSI( m_xControlModel, UNO_QUERY ); + OSL_ENSURE( xSI.is(), "EFormsHelper::canBindToDataType: a control model which has no service info?" ); + if ( xSI.is() ) + { + if ( xSI->supportsService( SERVICE_COMPONENT_FORMATTEDFIELD ) ) + { + pCompatibleTypes = nFormattedCompatibleTypes; + break; + } + } + [[fallthrough]]; + } + case FormComponentType::LISTBOX: + case FormComponentType::COMBOBOX: + // edit fields and list/combo boxes can be bound to anything + bCan = true; + } + + if ( !bCan && pCompatibleTypes ) + { + if ( _nDataType == -1 ) + { + // the control can be bound to at least one type, and exactly this is being asked for + bCan = true; + } + else + { + while ( *pCompatibleTypes && !bCan ) + bCan = ( *pCompatibleTypes++ == _nDataType ); + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::canBindToDataType" ); + } + + return bCan; + } + + + bool EFormsHelper::isListEntrySink() const + { + bool bIs = false; + try + { + Reference< XListEntrySink > xAsSink( m_xControlModel, UNO_QUERY ); + bIs = xAsSink.is(); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::isListEntrySink" ); + } + return bIs; + } + + + void EFormsHelper::impl_switchBindingListening_throw( bool _bDoListening, const Reference< XPropertyChangeListener >& _rxListener ) + { + Reference< XPropertySet > xBindingProps; + if ( m_xBindableControl.is() ) + xBindingProps.set(m_xBindableControl->getValueBinding(), css::uno::UNO_QUERY); + if ( !xBindingProps.is() ) + return; + + if ( _bDoListening ) + { + xBindingProps->addPropertyChangeListener( OUString(), _rxListener ); + } + else + { + xBindingProps->removePropertyChangeListener( OUString(), _rxListener ); + } + } + + + void EFormsHelper::registerBindingListener( const Reference< XPropertyChangeListener >& _rxBindingListener ) + { + if ( !_rxBindingListener.is() ) + return; + impl_toggleBindingPropertyListening_throw( true, _rxBindingListener ); + } + + + void EFormsHelper::impl_toggleBindingPropertyListening_throw( bool _bDoListen, const Reference< XPropertyChangeListener >& _rxConcreteListenerOrNull ) + { + if ( !_bDoListen ) + { + ::comphelper::OInterfaceIteratorHelper3 aListenerIterator(m_aPropertyListeners); + while ( aListenerIterator.hasMoreElements() ) + { + PropertyEventTranslation* pTranslator = dynamic_cast< PropertyEventTranslation* >( aListenerIterator.next().get() ); + OSL_ENSURE( pTranslator, "EFormsHelper::impl_toggleBindingPropertyListening_throw: invalid listener element in my container!" ); + if ( !pTranslator ) + continue; + + Reference< XPropertyChangeListener > xEventSourceTranslator( pTranslator ); + if ( _rxConcreteListenerOrNull.is() ) + { + if ( pTranslator->getDelegator() == _rxConcreteListenerOrNull ) + { + impl_switchBindingListening_throw( false, xEventSourceTranslator ); + m_aPropertyListeners.removeInterface( xEventSourceTranslator ); + break; + } + } + else + { + impl_switchBindingListening_throw( false, xEventSourceTranslator ); + } + } + } + else + { + if ( _rxConcreteListenerOrNull.is() ) + { + Reference< XPropertyChangeListener > xEventSourceTranslator( new PropertyEventTranslation( _rxConcreteListenerOrNull, m_xBindableControl ) ); + m_aPropertyListeners.addInterface( xEventSourceTranslator ); + impl_switchBindingListening_throw( true, xEventSourceTranslator ); + } + else + { + ::comphelper::OInterfaceIteratorHelper3 aListenerIterator(m_aPropertyListeners); + while ( aListenerIterator.hasMoreElements() ) + impl_switchBindingListening_throw( true, aListenerIterator.next() ); + } + } + } + + + void EFormsHelper::revokeBindingListener( const Reference< XPropertyChangeListener >& _rxBindingListener ) + { + impl_toggleBindingPropertyListening_throw( false, _rxBindingListener ); + } + + + void EFormsHelper::getFormModelNames( std::vector< OUString >& /* [out] */ _rModelNames ) const + { + if ( !m_xDocument.is() ) + return; + + try + { + _rModelNames.resize( 0 ); + + Reference< XNameContainer > xForms( m_xDocument->getXForms() ); + OSL_ENSURE( xForms.is(), "EFormsHelper::getFormModelNames: invalid forms container!" ); + if ( xForms.is() ) + { + const Sequence< OUString > aModelNames = xForms->getElementNames(); + _rModelNames.resize( aModelNames.getLength() ); + std::copy( aModelNames.begin(), aModelNames.end(), _rModelNames.begin() ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getFormModelNames" ); + } + } + + + void EFormsHelper::getBindingNames( const OUString& _rModelName, std::vector< OUString >& /* [out] */ _rBindingNames ) const + { + _rBindingNames.resize( 0 ); + try + { + Reference< xforms::XModel > xModel( getFormModelByName( _rModelName ) ); + if ( xModel.is() ) + { + Reference< XNameAccess > xBindings( xModel->getBindings(), UNO_QUERY ); + OSL_ENSURE( xBindings.is(), "EFormsHelper::getBindingNames: invalid bindings container obtained from the model!" ); + if ( xBindings.is() ) + { + const Sequence< OUString > aNames = xBindings->getElementNames(); + _rBindingNames.resize( aNames.getLength() ); + std::copy( aNames.begin(), aNames.end(), _rBindingNames.begin() ); + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getBindingNames" ); + } + } + + + Reference< xforms::XModel > EFormsHelper::getFormModelByName( const OUString& _rModelName ) const + { + Reference< xforms::XModel > xReturn; + try + { + Reference< XNameContainer > xForms( m_xDocument->getXForms() ); + OSL_ENSURE( xForms.is(), "EFormsHelper::getFormModelByName: invalid forms container!" ); + if ( xForms.is() ) + OSL_VERIFY( xForms->getByName( _rModelName ) >>= xReturn ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getFormModelByName" ); + } + return xReturn; + } + + + Reference< xforms::XModel > EFormsHelper::getCurrentFormModel() const + { + Reference< xforms::XModel > xModel; + try + { + Reference< XPropertySet > xBinding( getCurrentBinding() ); + if ( xBinding.is() ) + { + OSL_VERIFY( xBinding->getPropertyValue( PROPERTY_MODEL ) >>= xModel ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getCurrentFormModel" ); + } + return xModel; + } + + + OUString EFormsHelper::getCurrentFormModelName() const + { + OUString sModelName; + try + { + Reference< xforms::XModel > xFormsModel( getCurrentFormModel() ); + if ( xFormsModel.is() ) + sModelName = xFormsModel->getID(); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getCurrentFormModel" ); + } + return sModelName; + } + + + Reference< XPropertySet > EFormsHelper::getCurrentBinding() const + { + Reference< XPropertySet > xBinding; + + try + { + if ( m_xBindableControl.is() ) + xBinding.set(m_xBindableControl->getValueBinding(), css::uno::UNO_QUERY); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getCurrentBinding" ); + } + + return xBinding; + } + + + OUString EFormsHelper::getCurrentBindingName() const + { + OUString sBindingName; + try + { + Reference< XPropertySet > xBinding( getCurrentBinding() ); + if ( xBinding.is() ) + xBinding->getPropertyValue( PROPERTY_BINDING_ID ) >>= sBindingName; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getCurrentBindingName" ); + } + return sBindingName; + } + + + Reference< XListEntrySource > EFormsHelper::getCurrentListSourceBinding() const + { + Reference< XListEntrySource > xReturn; + try + { + Reference< XListEntrySink > xAsSink( m_xControlModel, UNO_QUERY ); + OSL_ENSURE( xAsSink.is(), "EFormsHelper::getCurrentListSourceBinding: you should have used isListEntrySink before!" ); + if ( xAsSink.is() ) + xReturn = xAsSink->getListEntrySource(); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getCurrentListSourceBinding" ); + } + return xReturn; + } + + + void EFormsHelper::setListSourceBinding( const Reference< XListEntrySource >& _rxListSource ) + { + try + { + Reference< XListEntrySink > xAsSink( m_xControlModel, UNO_QUERY ); + OSL_ENSURE( xAsSink.is(), "EFormsHelper::setListSourceBinding: you should have used isListEntrySink before!" ); + if ( xAsSink.is() ) + xAsSink->setListEntrySource( _rxListSource ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::setListSourceBinding" ); + } + } + + + void EFormsHelper::setBinding( const Reference< css::beans::XPropertySet >& _rxBinding ) + { + if ( !m_xBindableControl.is() ) + return; + + try + { + Reference< XPropertySet > xOldBinding( m_xBindableControl->getValueBinding(), UNO_QUERY ); + + Reference< XValueBinding > xBinding( _rxBinding, UNO_QUERY ); + OSL_ENSURE( xBinding.is() || !_rxBinding.is(), "EFormsHelper::setBinding: invalid binding!" ); + + impl_toggleBindingPropertyListening_throw( false, nullptr ); + m_xBindableControl->setValueBinding( xBinding ); + impl_toggleBindingPropertyListening_throw( true, nullptr ); + + std::set< OUString > aSet; + firePropertyChanges( xOldBinding, _rxBinding, aSet ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::setBinding" ); + } + } + + + Reference< XPropertySet > EFormsHelper::getOrCreateBindingForModel( const OUString& _rTargetModel, const OUString& _rBindingName ) const + { + OSL_ENSURE( !_rBindingName.isEmpty(), "EFormsHelper::getOrCreateBindingForModel: invalid binding name!" ); + return implGetOrCreateBinding( _rTargetModel, _rBindingName ); + } + + + Reference< XPropertySet > EFormsHelper::implGetOrCreateBinding( const OUString& _rTargetModel, const OUString& _rBindingName ) const + { + OSL_ENSURE( !( _rTargetModel.isEmpty() && !_rBindingName.isEmpty() ), "EFormsHelper::implGetOrCreateBinding: no model, but a binding name?" ); + + Reference< XPropertySet > xBinding; + try + { + OUString sTargetModel( _rTargetModel ); + // determine the model which the binding should belong to + if ( sTargetModel.isEmpty() ) + { + std::vector< OUString > aModelNames; + getFormModelNames( aModelNames ); + if ( !aModelNames.empty() ) + sTargetModel = *aModelNames.begin(); + OSL_ENSURE( !sTargetModel.isEmpty(), "EFormsHelper::implGetOrCreateBinding: unable to obtain a default model!" ); + } + Reference< xforms::XModel > xModel( getFormModelByName( sTargetModel ) ); + Reference< XNameAccess > xBindingNames( xModel.is() ? xModel->getBindings() : Reference< XSet >(), UNO_QUERY ); + if ( xBindingNames.is() ) + { + // get or create the binding instance + if ( !_rBindingName.isEmpty() ) + { + if ( xBindingNames->hasByName( _rBindingName ) ) + OSL_VERIFY( xBindingNames->getByName( _rBindingName ) >>= xBinding ); + else + { + xBinding = xModel->createBinding( ); + if ( xBinding.is() ) + { + xBinding->setPropertyValue( PROPERTY_BINDING_ID, Any( _rBindingName ) ); + xModel->getBindings()->insert( Any( xBinding ) ); + } + } + } + else + { + xBinding = xModel->createBinding( ); + if ( xBinding.is() ) + { + // find a nice name for it + OUString sBaseName(PcrRes(RID_STR_BINDING_NAME) + " "); + OUString sNewName; + sal_Int32 nNumber = 1; + do + { + sNewName = sBaseName + OUString::number( nNumber++ ); + } + while ( xBindingNames->hasByName( sNewName ) ); + Reference< XNamed > xName( xBinding, UNO_QUERY_THROW ); + xName->setName( sNewName ); + // and insert into the model + xModel->getBindings()->insert( Any( xBinding ) ); + } + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + + return xBinding; + } + + + namespace + { + + struct PropertyBagInserter + { + private: + PropertyBag& m_rProperties; + + public: + explicit PropertyBagInserter( PropertyBag& rProperties ) : m_rProperties( rProperties ) { } + + void operator()( const Property& _rProp ) + { + m_rProperties.insert( _rProp ); + } + }; + + + Reference< XPropertySetInfo > collectPropertiesGetInfo( const Reference< XPropertySet >& _rxProps, PropertyBag& _rBag ) + { + Reference< XPropertySetInfo > xInfo; + if ( _rxProps.is() ) + xInfo = _rxProps->getPropertySetInfo(); + if ( xInfo.is() ) + { + const Sequence< Property > aProperties = xInfo->getProperties(); + std::for_each( aProperties.begin(), aProperties.end(), + PropertyBagInserter( _rBag ) + ); + } + return xInfo; + } + } + + + OUString EFormsHelper::getModelElementUIName( const EFormsHelper::ModelElementType _eType, const Reference< XPropertySet >& _rxElement ) + { + OUString sUIName; + try + { + // determine the model which the element belongs to + Reference< xforms::XFormsUIHelper1 > xHelper; + if ( _rxElement.is() ) + _rxElement->getPropertyValue( PROPERTY_MODEL ) >>= xHelper; + OSL_ENSURE( xHelper.is(), "EFormsHelper::getModelElementUIName: invalid element or model!" ); + if ( xHelper.is() ) + { + OUString sElementName = ( _eType == Submission ) ? xHelper->getSubmissionName( _rxElement, true ) : xHelper->getBindingName( _rxElement, true ); + Reference< xforms::XModel > xModel( xHelper, UNO_QUERY_THROW ); + sUIName = composeModelElementUIName( xModel->getID(), sElementName ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getModelElementUIName" ); + } + + return sUIName; + } + + + Reference< XPropertySet > EFormsHelper::getModelElementFromUIName( const EFormsHelper::ModelElementType _eType, const OUString& _rUIName ) const + { + const MapStringToPropertySet& rMapUINameToElement( ( _eType == Submission ) ? m_aSubmissionUINames : m_aBindingUINames ); + MapStringToPropertySet::const_iterator pos = rMapUINameToElement.find( _rUIName ); + OSL_ENSURE( pos != rMapUINameToElement.end(), "EFormsHelper::getModelElementFromUIName: didn't find it!" ); + + return ( pos != rMapUINameToElement.end() ) ? pos->second : Reference< XPropertySet >(); + } + + + void EFormsHelper::getAllElementUINames( const ModelElementType _eType, std::vector< OUString >& /* [out] */ _rElementNames, bool _bPrepentEmptyEntry ) + { + MapStringToPropertySet& rMapUINameToElement( ( _eType == Submission ) ? m_aSubmissionUINames : m_aBindingUINames ); + rMapUINameToElement.clear(); + _rElementNames.resize( 0 ); + + if ( _bPrepentEmptyEntry ) + rMapUINameToElement[ OUString() ].clear(); + + try + { + // obtain the model names + std::vector< OUString > aModels; + getFormModelNames( aModels ); + _rElementNames.reserve( aModels.size() * 2 ); // heuristics + + // for every model, obtain the element + for (auto const& modelName : aModels) + { + Reference< xforms::XModel > xModel = getFormModelByName(modelName); + OSL_ENSURE( xModel.is(), "EFormsHelper::getAllElementUINames: inconsistency in the models!" ); + Reference< xforms::XFormsUIHelper1 > xHelper( xModel, UNO_QUERY ); + + Reference< XIndexAccess > xElements; + if ( xModel.is() ) + xElements.set(( _eType == Submission ) ? xModel->getSubmissions() : xModel->getBindings(), css::uno::UNO_QUERY); + if ( !xElements.is() ) + break; + + sal_Int32 nElementCount = xElements->getCount(); + for ( sal_Int32 i = 0; i < nElementCount; ++i ) + { + Reference< XPropertySet > xElement( xElements->getByIndex( i ), UNO_QUERY ); + OSL_ENSURE( xElement.is(), "EFormsHelper::getAllElementUINames: empty element!" ); + if ( !xElement.is() ) + continue; +#if OSL_DEBUG_LEVEL > 0 + { + Reference< xforms::XModel > xElementsModel; + xElement->getPropertyValue( PROPERTY_MODEL ) >>= xElementsModel; + OSL_ENSURE( xElementsModel == xModel, "EFormsHelper::getAllElementUINames: inconsistency in the model-element relationship!" ); + if ( xElementsModel != xModel ) + xElement->setPropertyValue( PROPERTY_MODEL, Any( xModel ) ); + } +#endif + OUString sElementName = ( _eType == Submission ) ? xHelper->getSubmissionName( xElement, true ) : xHelper->getBindingName( xElement, true ); + OUString sUIName = composeModelElementUIName( modelName, sElementName ); + + OSL_ENSURE( rMapUINameToElement.find( sUIName ) == rMapUINameToElement.end(), "EFormsHelper::getAllElementUINames: duplicate name!" ); + rMapUINameToElement.emplace( sUIName, xElement ); + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getAllElementUINames" ); + } + + _rElementNames.resize( rMapUINameToElement.size() ); + std::transform( rMapUINameToElement.begin(), rMapUINameToElement.end(), _rElementNames.begin(), + ::o3tl::select1st< MapStringToPropertySet::value_type >() ); + } + + + void EFormsHelper::firePropertyChange( const OUString& _rName, const Any& _rOldValue, const Any& _rNewValue ) const + { + if ( m_aPropertyListeners.getLength() == 0 ) + return; + + if ( _rOldValue == _rNewValue ) + return; + + try + { + PropertyChangeEvent aEvent; + + aEvent.Source = m_xBindableControl.get(); + aEvent.PropertyName = _rName; + aEvent.OldValue = _rOldValue; + aEvent.NewValue = _rNewValue; + + const_cast< EFormsHelper* >( this )->m_aPropertyListeners.notifyEach( &XPropertyChangeListener::propertyChange, aEvent ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::firePropertyChange" ); + } + } + + + void EFormsHelper::firePropertyChanges( const Reference< XPropertySet >& _rxOldProps, const Reference< XPropertySet >& _rxNewProps, std::set< OUString >& _rFilter ) const + { + if ( m_aPropertyListeners.getLength() == 0 ) + return; + + try + { + PropertyBag aProperties; + Reference< XPropertySetInfo > xOldInfo = collectPropertiesGetInfo( _rxOldProps, aProperties ); + Reference< XPropertySetInfo > xNewInfo = collectPropertiesGetInfo( _rxNewProps, aProperties ); + + for (auto const& property : aProperties) + { + if ( _rFilter.find( property.Name ) != _rFilter.end() ) + continue; + + Any aOldValue( nullptr, property.Type ); + if ( xOldInfo.is() && xOldInfo->hasPropertyByName( property.Name ) ) + aOldValue = _rxOldProps->getPropertyValue( property.Name ); + + Any aNewValue( nullptr, property.Type ); + if ( xNewInfo.is() && xNewInfo->hasPropertyByName( property.Name ) ) + aNewValue = _rxNewProps->getPropertyValue( property.Name ); + + firePropertyChange( property.Name, aOldValue, aNewValue ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::firePropertyChanges" ); + } + } + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/eformshelper.hxx b/extensions/source/propctrlr/eformshelper.hxx new file mode 100644 index 000000000..e88cb7491 --- /dev/null +++ b/extensions/source/propctrlr/eformshelper.hxx @@ -0,0 +1,255 @@ +/* -*- 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 "pcrcommon.hxx" + +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/xforms/XModel.hpp> +#include <com/sun/star/xforms/XFormsSupplier.hpp> +#include <com/sun/star/form/binding/XBindableValue.hpp> +#include <com/sun/star/form/binding/XListEntrySource.hpp> +#include <osl/mutex.hxx> +#include <rtl/ustring.hxx> + +#include <vector> +#include <set> +#include <map> + + +namespace pcr +{ + + + typedef std::map< OUString, css::uno::Reference< css::beans::XPropertySet >, std::less< OUString > > + MapStringToPropertySet; + + + //= EFormsHelper + + class EFormsHelper + { + protected: + css::uno::Reference< css::beans::XPropertySet > + m_xControlModel; + css::uno::Reference< css::form::binding::XBindableValue > + m_xBindableControl; + css::uno::Reference< css::xforms::XFormsSupplier > + m_xDocument; + PropertyChangeListeners + m_aPropertyListeners; + MapStringToPropertySet + m_aSubmissionUINames; // only filled upon request + MapStringToPropertySet + m_aBindingUINames; // only filled upon request + + public: + EFormsHelper( + ::osl::Mutex& _rMutex, + const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel, + const css::uno::Reference< css::frame::XModel >& _rxContextDocument + ); + + /** determines whether the given document is an eForm + + If this method returns <FALSE/>, you cannot instantiate an EFormsHelper with + this document, since none of its functionality will be available then. + */ + static bool + isEForm( + const css::uno::Reference< css::frame::XModel >& _rxContextDocument + ); + + /** registers a listener to be notified when any aspect of the binding changes. + + The listener will be registered at the current binding of the control model. If the binding + changes (see <method>setBinding</method>), the listener will be revoked from the old binding, + registered at the new binding, and for all properties which differ between both bindings, + the listener will be notified. + @see revokeBindingListener + */ + void registerBindingListener( + const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxBindingListener + ); + + /** revokes the binding listener which has previously been registered + @see registerBindingListener + */ + void revokeBindingListener( + const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxBindingListener + ); + + /** checks whether it's possible to bind the control model to a given XSD data type + + @param _nDataType + the data type which should be bound. If this is -1, <TRUE/> is returned if the control model + can be bound to <em>any</em> data type. + */ + bool canBindToDataType( sal_Int32 _nDataType = -1 ) const; + + /** checks whether the control model can be bound to any XSD data type + */ + bool canBindToAnyDataType() const { return canBindToDataType(); } + + /** checks whether the control model is a source for list entries, as supplied by XML data bindings + */ + bool isListEntrySink() const; + + /** retrieves the names of all XForms models in the document the control lives in + */ + void getFormModelNames( std::vector< OUString >& /* [out] */ _rModelNames ) const; + + /** retrieves the names of all bindings for a given model + @see getFormModelNames + */ + void getBindingNames( const OUString& _rModelName, std::vector< OUString >& /* [out] */ _rBindingNames ) const; + + /// retrieves the XForms model (within the control model's document) with the given name + css::uno::Reference< css::xforms::XModel > + getFormModelByName( const OUString& _rModelName ) const; + + /** retrieves the model which the active binding of the control model belongs to + */ + css::uno::Reference< css::xforms::XModel > + getCurrentFormModel() const; + + /** retrieves the name of the model which the active binding of the control model belongs to + */ + OUString + getCurrentFormModelName() const; + + /** retrieves the binding instance which is currently attached to the control model + */ + css::uno::Reference< css::beans::XPropertySet > + getCurrentBinding() const; + + /** retrieves the name of the binding instance which is currently attached to the control model + */ + OUString + getCurrentBindingName() const; + + /** sets a new binding at the control model + */ + void setBinding( const css::uno::Reference< css::beans::XPropertySet >& _rxBinding ); + + /** retrieves the binding instance which is currently used as list source for the control model + @see isListEntrySink + */ + css::uno::Reference< css::form::binding::XListEntrySource > + getCurrentListSourceBinding() const; + + /** sets a new list source at the control model + @see isListEntrySink + */ + void setListSourceBinding( const css::uno::Reference< css::form::binding::XListEntrySource >& _rxListSource ); + + /** retrieves a given binding for a given model, or creates a new one + + @param _rTargetModel + the name of the model to create a binding for. Must not be empty + @param _rBindingName + the name of the binding to retrieve. If the model denoted by <arg>_rTargetModel</arg> does not + have a binding with this name, a new binding is created and returned. + */ + css::uno::Reference< css::beans::XPropertySet > + getOrCreateBindingForModel( const OUString& _rTargetModel, const OUString& _rBindingName ) const; + + /** types of sub-elements of a model + */ + enum ModelElementType + { + Submission, + Binding + }; + + /** retrieves the name of a model's sub-element, as to be shown in the UI + @see getModelElementFromUIName + @see getAllElementUINames + */ + static OUString + getModelElementUIName( + const ModelElementType _eType, + const css::uno::Reference< css::beans::XPropertySet >& _rxElement + ); + + /** retrieves the submission object for an UI name + + Note that <member>getAllElementUINames</member> must have been called before, for the given element type + + @see getModelElementUIName + @see getAllElementUINames + */ + css::uno::Reference< css::beans::XPropertySet > + getModelElementFromUIName( + const ModelElementType _eType, + const OUString& _rUIName + ) const; + + /** retrieves the UI names of all elements of all models in our document + @param _eType + the type of elements for which the names should be retrieved + @param _rElementNames + the array of element names + @see getModelElementUIName + @see getModelElementFromUIName + */ + void getAllElementUINames( + const ModelElementType _eType, + std::vector< OUString >& /* [out] */ _rElementNames, + bool _bPrepentEmptyEntry + ); + + protected: + void firePropertyChanges( + const css::uno::Reference< css::beans::XPropertySet >& _rxOldProps, + const css::uno::Reference< css::beans::XPropertySet >& _rxNewProps, + std::set< OUString >& _rFilter + ) const; + + /** fires a change in a single property, if the property value changed, and if we have a listener + interested in property changes + */ + void firePropertyChange( + const OUString& _rName, + const css::uno::Any& _rOldValue, + const css::uno::Any& _rNewValue + ) const; + + private: + void impl_switchBindingListening_throw( bool _bDoListening, const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ); + + /// implementation for both <member>getOrCreateBindingForModel</member> + css::uno::Reference< css::beans::XPropertySet > + implGetOrCreateBinding( const OUString& _rTargetModel, const OUString& _rBindingName ) const; + + void + impl_toggleBindingPropertyListening_throw( bool _bDoListen, const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxConcreteListenerOrNull ); + + private: + EFormsHelper( const EFormsHelper& ) = delete; + EFormsHelper& operator=( const EFormsHelper& ) = delete; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/eformspropertyhandler.cxx b/extensions/source/propctrlr/eformspropertyhandler.cxx new file mode 100644 index 000000000..42ca7ed1a --- /dev/null +++ b/extensions/source/propctrlr/eformspropertyhandler.cxx @@ -0,0 +1,598 @@ +/* -*- 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 "eformspropertyhandler.hxx" +#include "formstrings.hxx" +#include "formmetadata.hxx" +#include <propctrlr.h> +#include "eformshelper.hxx" +#include "handlerhelper.hxx" + +#include <com/sun/star/lang/NullPointerException.hpp> +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <com/sun/star/inspection/PropertyControlType.hpp> +#include <com/sun/star/inspection/XObjectInspectorUI.hpp> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> + + +namespace pcr +{ + + + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::xforms; + using namespace ::com::sun::star::script; + using namespace ::com::sun::star::ui::dialogs; + using namespace ::com::sun::star::form::binding; + using namespace ::com::sun::star::inspection; + + + //= EFormsPropertyHandler + + + EFormsPropertyHandler::EFormsPropertyHandler( const Reference< XComponentContext >& _rxContext ) + :PropertyHandlerComponent( _rxContext ) + ,m_bSimulatingModelChange( false ) + { + } + + + EFormsPropertyHandler::~EFormsPropertyHandler( ) + { + } + + + OUString EFormsPropertyHandler::getImplementationName( ) + { + return "com.sun.star.comp.extensions.EFormsPropertyHandler"; + } + + + Sequence< OUString > EFormsPropertyHandler::getSupportedServiceNames( ) + { + return { "com.sun.star.form.inspection.XMLFormsPropertyHandler" }; + } + + + OUString EFormsPropertyHandler::getModelNamePropertyValue() const + { + OUString sModelName = m_pHelper->getCurrentFormModelName(); + if ( sModelName.isEmpty() ) + sModelName = m_sBindingLessModelName; + return sModelName; + } + + + Any SAL_CALL EFormsPropertyHandler::getPropertyValue( const OUString& _rPropertyName ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + OSL_ENSURE( + m_pHelper, + "EFormsPropertyHandler::getPropertyValue: we don't have any SupportedProperties!"); + // if we survived impl_getPropertyId_throwUnknownProperty, we should have a helper, since no helper implies no properties + + Any aReturn; + try + { + switch ( nPropId ) + { + case PROPERTY_ID_LIST_BINDING: + aReturn <<= m_pHelper->getCurrentListSourceBinding(); + break; + + case PROPERTY_ID_XML_DATA_MODEL: + aReturn <<= getModelNamePropertyValue(); + break; + + case PROPERTY_ID_BINDING_NAME: + aReturn <<= m_pHelper->getCurrentBindingName(); + break; + + case PROPERTY_ID_BIND_EXPRESSION: + case PROPERTY_ID_XSD_CONSTRAINT: + case PROPERTY_ID_XSD_CALCULATION: + case PROPERTY_ID_XSD_REQUIRED: + case PROPERTY_ID_XSD_RELEVANT: + case PROPERTY_ID_XSD_READONLY: + { + Reference< XPropertySet > xBindingProps( m_pHelper->getCurrentBinding() ); + if ( xBindingProps.is() ) + { + aReturn = xBindingProps->getPropertyValue( _rPropertyName ); + DBG_ASSERT( aReturn.getValueType().equals( ::cppu::UnoType<OUString>::get() ), + "EFormsPropertyHandler::getPropertyValue: invalid BindingExpression value type!" ); + } + else + aReturn <<= OUString(); + } + break; + + default: + OSL_FAIL( "EFormsPropertyHandler::getPropertyValue: cannot handle this property!" ); + break; + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsPropertyHandler::getPropertyValue: caught exception!" + "(have been asked for the \"" <<_rPropertyName << "\" property.)"); + } + return aReturn; + } + + + void SAL_CALL EFormsPropertyHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + OSL_ENSURE( + m_pHelper, + "EFormsPropertyHandler::setPropertyValue: we don't have any SupportedProperties!"); + // if we survived impl_getPropertyId_throwUnknownProperty, we should have a helper, since no helper implies no properties + + try + { + Any aOldValue = getPropertyValue( _rPropertyName ); + + switch ( nPropId ) + { + case PROPERTY_ID_LIST_BINDING: + { + Reference< XListEntrySource > xSource; + OSL_VERIFY( _rValue >>= xSource ); + m_pHelper->setListSourceBinding( xSource ); + } + break; + + case PROPERTY_ID_XML_DATA_MODEL: + { + OSL_VERIFY( _rValue >>= m_sBindingLessModelName ); + + // if the model changed, reset the binding to NULL + if ( m_pHelper->getCurrentFormModelName() != m_sBindingLessModelName ) + { + OUString sOldBindingName = m_pHelper->getCurrentBindingName(); + m_pHelper->setBinding( nullptr ); + firePropertyChange( PROPERTY_BINDING_NAME, PROPERTY_ID_BINDING_NAME, + Any( sOldBindingName ), Any( OUString() ) ); + } + } + break; + + case PROPERTY_ID_BINDING_NAME: + { + OUString sNewBindingName; + OSL_VERIFY( _rValue >>= sNewBindingName ); + + bool bPreviouslyEmptyModel = !m_pHelper->getCurrentFormModel().is(); + + Reference< XPropertySet > xNewBinding; + if ( !sNewBindingName.isEmpty() ) + // obtain the binding with this name, for the current model + xNewBinding = m_pHelper->getOrCreateBindingForModel( getModelNamePropertyValue(), sNewBindingName ); + + m_pHelper->setBinding( xNewBinding ); + + if ( bPreviouslyEmptyModel ) + { // simulate a property change for the model property + // This is because we "simulate" the Model property by remembering the + // value ourself. Other instances might, however, not know this value, + // but prefer to retrieve it somewhere else - e.g. from the EFormsHelper + + // The really correct solution would be if *all* property handlers + // obtain a "current property value" for *all* properties from a central + // instance. Then, handler A could ask it for the value of property + // X, and this request would be re-routed to handler B, which ultimately + // knows the current value. + // However, there's no such mechanism in place currently. + m_bSimulatingModelChange = true; + firePropertyChange( PROPERTY_XML_DATA_MODEL, PROPERTY_ID_XML_DATA_MODEL, + Any( OUString() ), Any( getModelNamePropertyValue() ) ); + m_bSimulatingModelChange = false; + } + } + break; + + case PROPERTY_ID_BIND_EXPRESSION: + { + Reference< XPropertySet > xBinding( m_pHelper->getCurrentBinding() ); + OSL_ENSURE( xBinding.is(), "You should not reach this without an active binding!" ); + if ( xBinding.is() ) + xBinding->setPropertyValue( PROPERTY_BIND_EXPRESSION, _rValue ); + } + break; + + case PROPERTY_ID_XSD_REQUIRED: + case PROPERTY_ID_XSD_RELEVANT: + case PROPERTY_ID_XSD_READONLY: + case PROPERTY_ID_XSD_CONSTRAINT: + case PROPERTY_ID_XSD_CALCULATION: + { + Reference< XPropertySet > xBindingProps( m_pHelper->getCurrentBinding() ); + DBG_ASSERT( xBindingProps.is(), "EFormsPropertyHandler::setPropertyValue: how can I set a property if there's no binding?" ); + if ( xBindingProps.is() ) + { + DBG_ASSERT( _rValue.getValueType().equals( ::cppu::UnoType<OUString>::get() ), + "EFormsPropertyHandler::setPropertyValue: invalid value type!" ); + xBindingProps->setPropertyValue( _rPropertyName, _rValue ); + } + } + break; + + default: + OSL_FAIL( "EFormsPropertyHandler::setPropertyValue: cannot handle this property!" ); + break; + } + + impl_setContextDocumentModified_nothrow(); + + Any aNewValue( getPropertyValue( _rPropertyName ) ); + firePropertyChange( _rPropertyName, nPropId, aOldValue, aNewValue ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsPropertyHandler::setPropertyValue" ); + } + } + + + void EFormsPropertyHandler::onNewComponent() + { + PropertyHandlerComponent::onNewComponent(); + + Reference< frame::XModel > xDocument( impl_getContextDocument_nothrow() ); + DBG_ASSERT( xDocument.is(), "EFormsPropertyHandler::onNewComponent: no document!" ); + if ( EFormsHelper::isEForm( xDocument ) ) + m_pHelper.reset( new EFormsHelper( m_aMutex, m_xComponent, xDocument ) ); + else + m_pHelper.reset(); + } + + + Sequence< Property > EFormsPropertyHandler::doDescribeSupportedProperties() const + { + std::vector< Property > aProperties; + + if (m_pHelper) + { + if ( m_pHelper->canBindToAnyDataType() ) + { + aProperties.reserve( 7 ); + addStringPropertyDescription( aProperties, PROPERTY_XML_DATA_MODEL ); + addStringPropertyDescription( aProperties, PROPERTY_BINDING_NAME ); + addStringPropertyDescription( aProperties, PROPERTY_BIND_EXPRESSION ); + addStringPropertyDescription( aProperties, PROPERTY_XSD_REQUIRED ); + addStringPropertyDescription( aProperties, PROPERTY_XSD_RELEVANT ); + addStringPropertyDescription( aProperties, PROPERTY_XSD_READONLY ); + addStringPropertyDescription( aProperties, PROPERTY_XSD_CONSTRAINT ); + addStringPropertyDescription( aProperties, PROPERTY_XSD_CALCULATION ); + } + if ( m_pHelper->isListEntrySink() ) + { + implAddPropertyDescription( aProperties, PROPERTY_LIST_BINDING, + cppu::UnoType<XListEntrySource>::get() ); + } + } + + if ( aProperties.empty() ) + return Sequence< Property >(); + return comphelper::containerToSequence(aProperties); + } + + + Any SAL_CALL EFormsPropertyHandler::convertToPropertyValue( const OUString& _rPropertyName, const Any& _rControlValue ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + Any aReturn; + + OSL_ENSURE( + m_pHelper, + "EFormsPropertyHandler::convertToPropertyValue: we have no SupportedProperties!"); + if (!m_pHelper) + return aReturn; + + PropertyId nPropId( m_pInfoService->getPropertyId( _rPropertyName ) ); + + OUString sControlValue; + switch ( nPropId ) + { + case PROPERTY_ID_LIST_BINDING: + { + OSL_VERIFY( _rControlValue >>= sControlValue ); + Reference< XListEntrySource > xListSource( m_pHelper->getModelElementFromUIName( EFormsHelper::Binding, sControlValue ), UNO_QUERY ); + OSL_ENSURE( xListSource.is() || !m_pHelper->getModelElementFromUIName( EFormsHelper::Binding, sControlValue ).is(), + "EFormsPropertyHandler::convertToPropertyValue: there's a binding which is no ListEntrySource!" ); + aReturn <<= xListSource; + } + break; + + default: + aReturn = PropertyHandlerComponent::convertToPropertyValue( _rPropertyName, _rControlValue ); + break; + } + + return aReturn; + } + + + Any SAL_CALL EFormsPropertyHandler::convertToControlValue( const OUString& _rPropertyName, const Any& _rPropertyValue, const Type& _rControlValueType ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + Any aReturn; + + OSL_ENSURE(m_pHelper, + "EFormsPropertyHandler::convertToControlValue: we have no SupportedProperties!"); + if (!m_pHelper) + return aReturn; + + PropertyId nPropId( m_pInfoService->getPropertyId( _rPropertyName ) ); + + OSL_ENSURE( _rControlValueType.getTypeClass() == TypeClass_STRING, + "EFormsPropertyHandler::convertToControlValue: all our controls should use strings for value exchange!" ); + + switch ( nPropId ) + { + case PROPERTY_ID_LIST_BINDING: + { + Reference< XPropertySet > xListSourceBinding( _rPropertyValue, UNO_QUERY ); + if ( xListSourceBinding.is() ) + aReturn <<= EFormsHelper::getModelElementUIName( EFormsHelper::Binding, xListSourceBinding ); + } + break; + + default: + aReturn = PropertyHandlerComponent::convertToControlValue( _rPropertyName, _rPropertyValue, _rControlValueType ); + break; + } + + return aReturn; + } + + + Sequence< OUString > SAL_CALL EFormsPropertyHandler::getActuatingProperties( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if (!m_pHelper) + return Sequence< OUString >(); + + std::vector< OUString > aInterestedInActuations( 2 ); + aInterestedInActuations[ 0 ] = PROPERTY_XML_DATA_MODEL; + aInterestedInActuations[ 1 ] = PROPERTY_BINDING_NAME; + return comphelper::containerToSequence(aInterestedInActuations); + } + + + Sequence< OUString > SAL_CALL EFormsPropertyHandler::getSupersededProperties( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if (!m_pHelper) + return Sequence< OUString >(); + + Sequence<OUString> aReturn { PROPERTY_INPUT_REQUIRED }; + return aReturn; + } + + LineDescriptor SAL_CALL EFormsPropertyHandler::describePropertyLine( const OUString& _rPropertyName, + const Reference< XPropertyControlFactory >& _rxControlFactory ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !_rxControlFactory.is() ) + throw NullPointerException(); + if (!m_pHelper) + throw RuntimeException(); + + LineDescriptor aDescriptor; + sal_Int16 nControlType = PropertyControlType::TextField; + std::vector< OUString > aListEntries; + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + switch ( nPropId ) + { + case PROPERTY_ID_LIST_BINDING: + nControlType = PropertyControlType::ListBox; + m_pHelper->getAllElementUINames(EFormsHelper::Binding, aListEntries, true); + break; + + case PROPERTY_ID_XML_DATA_MODEL: + nControlType = PropertyControlType::ListBox; + m_pHelper->getFormModelNames( aListEntries ); + break; + + case PROPERTY_ID_BINDING_NAME: + { + nControlType = PropertyControlType::ComboBox; + OUString sCurrentModel( getModelNamePropertyValue() ); + if ( !sCurrentModel.isEmpty() ) + m_pHelper->getBindingNames( sCurrentModel, aListEntries ); + } + break; + + case PROPERTY_ID_BIND_EXPRESSION: aDescriptor.PrimaryButtonId = UID_PROP_DLG_BIND_EXPRESSION; break; + case PROPERTY_ID_XSD_REQUIRED: aDescriptor.PrimaryButtonId = UID_PROP_DLG_XSD_REQUIRED; break; + case PROPERTY_ID_XSD_RELEVANT: aDescriptor.PrimaryButtonId = UID_PROP_DLG_XSD_RELEVANT; break; + case PROPERTY_ID_XSD_READONLY: aDescriptor.PrimaryButtonId = UID_PROP_DLG_XSD_READONLY; break; + case PROPERTY_ID_XSD_CONSTRAINT: aDescriptor.PrimaryButtonId = UID_PROP_DLG_XSD_CONSTRAINT; break; + case PROPERTY_ID_XSD_CALCULATION: aDescriptor.PrimaryButtonId = UID_PROP_DLG_XSD_CALCULATION; break; + + default: + OSL_FAIL( "EFormsPropertyHandler::describePropertyLine: cannot handle this property!" ); + break; + } + + switch ( nControlType ) + { + case PropertyControlType::ListBox: + aDescriptor.Control = PropertyHandlerHelper::createListBoxControl( _rxControlFactory, std::move(aListEntries), false, true ); + break; + case PropertyControlType::ComboBox: + aDescriptor.Control = PropertyHandlerHelper::createComboBoxControl( _rxControlFactory, std::move(aListEntries), true ); + break; + default: + aDescriptor.Control = _rxControlFactory->createPropertyControl( nControlType, false ); + break; + } + + aDescriptor.DisplayName = m_pInfoService->getPropertyTranslation( nPropId ); + aDescriptor.Category = "Data"; + aDescriptor.HelpURL = HelpIdUrl::getHelpURL( m_pInfoService->getPropertyHelpId( nPropId ) ); + return aDescriptor; + } + + InteractiveSelectionResult SAL_CALL EFormsPropertyHandler::onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool /*_bPrimary*/, Any& _rData, const Reference< XObjectInspectorUI >& _rxInspectorUI ) + { + if ( !_rxInspectorUI.is() ) + throw NullPointerException(); + + ::osl::MutexGuard aGuard( m_aMutex ); + OSL_ENSURE(m_pHelper, "EFormsPropertyHandler::onInteractivePropertySelection: we do not " + "have any SupportedProperties!"); + if (!m_pHelper) + return InteractiveSelectionResult_Cancelled; + + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + OSL_ENSURE( ( PROPERTY_ID_BINDING_NAME == nPropId ) + || ( PROPERTY_ID_BIND_EXPRESSION == nPropId ) + || ( PROPERTY_ID_XSD_REQUIRED == nPropId ) + || ( PROPERTY_ID_XSD_RELEVANT == nPropId ) + || ( PROPERTY_ID_XSD_READONLY == nPropId ) + || ( PROPERTY_ID_XSD_CONSTRAINT == nPropId ) + || ( PROPERTY_ID_XSD_CALCULATION == nPropId ), "EFormsPropertyHandler::onInteractivePropertySelection: unexpected!" ); + + try + { + Reference< XExecutableDialog > xDialog; + xDialog.set( m_xContext->getServiceManager()->createInstanceWithContext( "com.sun.star.xforms.ui.dialogs.AddCondition", m_xContext ), UNO_QUERY ); + Reference< XPropertySet > xDialogProps( xDialog, UNO_QUERY_THROW ); + + // the model for the dialog to work with + Reference< xforms::XModel > xModel( m_pHelper->getCurrentFormModel() ); + // the binding for the dialog to work with + Reference< XPropertySet > xBinding( m_pHelper->getCurrentBinding() ); + // the aspect of the binding which the dialog should modify + const OUString& sFacetName( _rPropertyName ); + + OSL_ENSURE( xModel.is() && xBinding.is() && !sFacetName.isEmpty(), + "EFormsPropertyHandler::onInteractivePropertySelection: something is missing for the dialog initialization!" ); + if ( !xModel.is() || !xBinding.is() || sFacetName.isEmpty() ) + return InteractiveSelectionResult_Cancelled; + + xDialogProps->setPropertyValue("FormModel", Any( xModel ) ); + xDialogProps->setPropertyValue("Binding", Any( xBinding ) ); + xDialogProps->setPropertyValue("FacetName", Any( sFacetName ) ); + + if ( !xDialog->execute() ) + // cancelled + return InteractiveSelectionResult_Cancelled; + + _rData = xDialogProps->getPropertyValue("ConditionValue"); + return InteractiveSelectionResult_ObtainedValue; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsPropertyHandler::onInteractivePropertySelection" ); + } + + // something went wrong here ...(but has been asserted already) + return InteractiveSelectionResult_Cancelled; + } + + + void SAL_CALL EFormsPropertyHandler::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyHandlerComponent::addPropertyChangeListener( _rxListener ); + if (m_pHelper) + m_pHelper->registerBindingListener( _rxListener ); + } + + + void SAL_CALL EFormsPropertyHandler::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if (m_pHelper) + m_pHelper->revokeBindingListener( _rxListener ); + PropertyHandlerComponent::removePropertyChangeListener( _rxListener ); + } + + + void SAL_CALL EFormsPropertyHandler::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool ) + { + if ( !_rxInspectorUI.is() ) + throw NullPointerException(); + + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nActuatingPropId( impl_getPropertyId_throwRuntime( _rActuatingPropertyName ) ); + OSL_PRECOND(m_pHelper, "EFormsPropertyHandler::actuatingPropertyChanged: inconsistency!"); + // if we survived impl_getPropertyId_throwRuntime, we should have a helper, since no helper implies no properties + + DBG_ASSERT( _rxInspectorUI.is(), "EFormsPropertyHandler::actuatingPropertyChanged: invalid callback!" ); + if ( !_rxInspectorUI.is() ) + return; + + switch ( nActuatingPropId ) + { + case PROPERTY_ID_XML_DATA_MODEL: + { + if ( m_bSimulatingModelChange ) + break; + OUString sDataModelName; + OSL_VERIFY( _rNewValue >>= sDataModelName ); + bool bBoundToSomeModel = !sDataModelName.isEmpty(); + _rxInspectorUI->rebuildPropertyUI( PROPERTY_BINDING_NAME ); + _rxInspectorUI->enablePropertyUI( PROPERTY_BINDING_NAME, bBoundToSomeModel ); + [[fallthrough]]; + } + + case PROPERTY_ID_BINDING_NAME: + { + bool bHaveABinding = !m_pHelper->getCurrentBindingName().isEmpty(); + _rxInspectorUI->enablePropertyUI( PROPERTY_BIND_EXPRESSION, bHaveABinding ); + _rxInspectorUI->enablePropertyUI( PROPERTY_XSD_REQUIRED, bHaveABinding ); + _rxInspectorUI->enablePropertyUI( PROPERTY_XSD_RELEVANT, bHaveABinding ); + _rxInspectorUI->enablePropertyUI( PROPERTY_XSD_READONLY, bHaveABinding ); + _rxInspectorUI->enablePropertyUI( PROPERTY_XSD_CONSTRAINT, bHaveABinding ); + _rxInspectorUI->enablePropertyUI( PROPERTY_XSD_CALCULATION, bHaveABinding ); + _rxInspectorUI->enablePropertyUI( PROPERTY_XSD_DATA_TYPE, bHaveABinding ); + } + break; + + default: + OSL_FAIL( "EFormsPropertyHandler::actuatingPropertyChanged: cannot handle this property!" ); + break; + } + } + + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_EFormsPropertyHandler_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::EFormsPropertyHandler(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/eformspropertyhandler.hxx b/extensions/source/propctrlr/eformspropertyhandler.hxx new file mode 100644 index 000000000..8945c24f4 --- /dev/null +++ b/extensions/source/propctrlr/eformspropertyhandler.hxx @@ -0,0 +1,95 @@ +/* -*- 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 "propertyhandler.hxx" + +#include <memory> + + +namespace pcr +{ + + + class EFormsHelper; + + //= EFormsPropertyHandler + + class EFormsPropertyHandler; + class EFormsPropertyHandler : public PropertyHandlerComponent + { + private: + std::unique_ptr< EFormsHelper > m_pHelper; + /** current value of the Model property, if there is no binding, yet + */ + OUString m_sBindingLessModelName; + /** are we currently simulating a propertyChange event of the Model property? + */ + bool m_bSimulatingModelChange; + + public: + explicit EFormsPropertyHandler( + const css::uno::Reference< css::uno::XComponentContext >& _rxContext + ); + + protected: + virtual ~EFormsPropertyHandler() override; + + protected: + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override; + + // XPropertyHandler overriables + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override; + virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override; + virtual css::uno::Sequence< OUString > + SAL_CALL getActuatingProperties( ) override; + virtual css::uno::Sequence< OUString > + SAL_CALL getSupersededProperties( ) override; + virtual css::inspection::LineDescriptor + SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override; + virtual css::inspection::InteractiveSelectionResult + SAL_CALL onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, css::uno::Any& _rData, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) override; + virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool ) override; + virtual css::uno::Any SAL_CALL convertToPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rControlValue ) override; + virtual css::uno::Any SAL_CALL convertToControlValue( const OUString& _rPropertyName, const css::uno::Any& _rPropertyValue, const css::uno::Type& _rControlValueType ) override; + virtual void SAL_CALL addPropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override; + + // PropertyHandler overridables + virtual css::uno::Sequence< css::beans::Property > + doDescribeSupportedProperties() const override; + virtual void onNewComponent() override; + + protected: + /** returns the value of the PROPERTY_XML_DATA_MODEL property. + + An extra method is necessary here, which respects both the value set at our helper, + and <member>m_sBindingLessModelName</member> + */ + OUString getModelNamePropertyValue() const; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/enumrepresentation.hxx b/extensions/source/propctrlr/enumrepresentation.hxx new file mode 100644 index 000000000..b3ba04c9a --- /dev/null +++ b/extensions/source/propctrlr/enumrepresentation.hxx @@ -0,0 +1,62 @@ +/* -*- 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/uno/Any.hxx> +#include <rtl/ustring.hxx> +#include <salhelper/simplereferenceobject.hxx> + +#include <vector> + + +namespace pcr +{ + + + //= IPropertyEnumRepresentation + + class SAL_NO_VTABLE IPropertyEnumRepresentation : public salhelper::SimpleReferenceObject + { + public: + /** retrieves all descriptions of all possible values of the enumeration property + */ + virtual std::vector< OUString > getDescriptions( + ) const = 0; + + /** converts a given description into a property value + */ + virtual void getValueFromDescription( + const OUString& _rDescription, + css::uno::Any& _out_rValue + ) const = 0; + + /** converts a given property value into a description + */ + virtual OUString getDescriptionForValue( + const css::uno::Any& _rEnumValue + ) const = 0; + + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/eventhandler.cxx b/extensions/source/propctrlr/eventhandler.cxx new file mode 100644 index 000000000..d07f524bf --- /dev/null +++ b/extensions/source/propctrlr/eventhandler.cxx @@ -0,0 +1,1112 @@ +/* -*- 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 "eventhandler.hxx" +#include <helpids.h> +#include <propctrlr.h> +#include "formbrowsertools.hxx" +#include <strings.hrc> +#include "formstrings.hxx" +#include "handlerhelper.hxx" +#include "modulepcr.hxx" +#include "pcrcommon.hxx" +#include "propertycontrolextender.hxx" + +#include <com/sun/star/awt/XTabControllerModel.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/UnknownPropertyException.hpp> +#include <com/sun/star/beans/theIntrospection.hpp> +#include <com/sun/star/beans/XIntrospectionAccess.hpp> +#include <com/sun/star/container/NoSuchElementException.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XNameReplace.hpp> +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/form/XForm.hpp> +#include <com/sun/star/form/runtime/FormController.hpp> +#include <com/sun/star/inspection/PropertyControlType.hpp> +#include <com/sun/star/lang/NullPointerException.hpp> +#include <com/sun/star/script/XEventAttacherManager.hpp> +#include <com/sun/star/script/XScriptEventsSupplier.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp> + +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/evtmethodhelper.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/types.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustrbuf.hxx> +#include <svx/svxdlg.hxx> +#include <tools/diagnose_ex.h> + +#include <map> +#include <algorithm> +#include <iterator> +#include <string_view> + +namespace pcr +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::TypeClass_STRING; + using ::com::sun::star::uno::Type; + using ::com::sun::star::beans::theIntrospection; + using ::com::sun::star::beans::XPropertyChangeListener; + using ::com::sun::star::beans::Property; + using ::com::sun::star::beans::PropertyState; + using ::com::sun::star::beans::PropertyState_DIRECT_VALUE; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::script::ScriptEventDescriptor; + using ::com::sun::star::script::XScriptEventsSupplier; + using ::com::sun::star::lang::NullPointerException; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::container::XChild; + using ::com::sun::star::container::XIndexAccess; + using ::com::sun::star::script::XEventAttacherManager; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::XInterface; + using ::com::sun::star::beans::XIntrospection; + using ::com::sun::star::beans::XIntrospectionAccess; + using ::com::sun::star::container::XNameContainer; + using ::com::sun::star::awt::XTabControllerModel; + using ::com::sun::star::form::XForm; + using ::com::sun::star::form::runtime::FormController; + using ::com::sun::star::form::runtime::XFormController; + using ::com::sun::star::beans::UnknownPropertyException; + using ::com::sun::star::container::NoSuchElementException; + using ::com::sun::star::beans::XPropertySetInfo; + using ::com::sun::star::container::XNameReplace; + using ::com::sun::star::beans::PropertyValue; + using ::com::sun::star::inspection::LineDescriptor; + using ::com::sun::star::inspection::XPropertyControlFactory; + using ::com::sun::star::inspection::InteractiveSelectionResult; + using ::com::sun::star::inspection::InteractiveSelectionResult_Cancelled; + using ::com::sun::star::inspection::InteractiveSelectionResult_Success; + using ::com::sun::star::inspection::XObjectInspectorUI; + using ::com::sun::star::beans::PropertyChangeEvent; + using ::com::sun::star::frame::XFrame; + using ::com::sun::star::frame::XModel; + using ::com::sun::star::frame::XController; + using ::com::sun::star::uno::UNO_SET_THROW; + using com::sun::star::uri::UriReferenceFactory; + using com::sun::star::uri::XUriReferenceFactory; + using com::sun::star::uri::XVndSunStarScriptUrlReference; + using ::com::sun::star::lang::XEventListener; + + namespace PropertyControlType = css::inspection::PropertyControlType; + namespace PropertyAttribute = css::beans::PropertyAttribute; + namespace FormComponentType = css::form::FormComponentType; + + EventDescription::EventDescription( EventId _nId, const char* _pListenerNamespaceAscii, const char* _pListenerClassAsciiName, + const char* _pListenerMethodAsciiName, TranslateId pDisplayNameResId, const OUString& _sHelpId, const OString& _sUniqueBrowseId ) + :sDisplayName(PcrRes( pDisplayNameResId )) + ,sListenerMethodName( OUString::createFromAscii( _pListenerMethodAsciiName ) ) + ,sHelpId( _sHelpId ) + ,sUniqueBrowseId( _sUniqueBrowseId ) + ,nId( _nId ) + { + OUStringBuffer aQualifiedListenerClass; + aQualifiedListenerClass.append( "com.sun.star." ); + aQualifiedListenerClass.appendAscii( _pListenerNamespaceAscii ); + aQualifiedListenerClass.append( "." ); + aQualifiedListenerClass.appendAscii( _pListenerClassAsciiName ); + sListenerClassName = aQualifiedListenerClass.makeStringAndClear(); + } + + namespace + { + #define DESCRIBE_EVENT( map, asciinamespace, asciilistener, asciimethod, id_postfix ) \ + map.emplace( \ + asciimethod, \ + EventDescription( ++nEventId, asciinamespace, asciilistener, asciimethod, RID_STR_EVT_##id_postfix, HID_EVT_##id_postfix, UID_BRWEVT_##id_postfix ) ) + + bool lcl_getEventDescriptionForMethod( const OUString& _rMethodName, EventDescription& _out_rDescription ) + { + static EventMap s_aKnownEvents = []() { + EventMap aMap; + sal_Int32 nEventId = 0; + + DESCRIBE_EVENT(aMap, "form", "XApproveActionListener", "approveAction", APPROVEACTIONPERFORMED); + DESCRIBE_EVENT(aMap, "awt", "XActionListener", "actionPerformed", ACTIONPERFORMED); + DESCRIBE_EVENT(aMap, "form", "XChangeListener", "changed", CHANGED); + DESCRIBE_EVENT(aMap, "awt", "XTextListener", "textChanged", TEXTCHANGED); + DESCRIBE_EVENT(aMap, "awt", "XItemListener", "itemStateChanged", ITEMSTATECHANGED); + DESCRIBE_EVENT(aMap, "awt", "XFocusListener", "focusGained", FOCUSGAINED); + DESCRIBE_EVENT(aMap, "awt", "XFocusListener", "focusLost", FOCUSLOST); + DESCRIBE_EVENT(aMap, "awt", "XKeyListener", "keyPressed", KEYTYPED); + DESCRIBE_EVENT(aMap, "awt", "XKeyListener", "keyReleased", KEYUP); + DESCRIBE_EVENT(aMap, "awt", "XMouseListener", "mouseEntered", MOUSEENTERED); + DESCRIBE_EVENT(aMap, "awt", "XMouseMotionListener", "mouseDragged", MOUSEDRAGGED); + DESCRIBE_EVENT(aMap, "awt", "XMouseMotionListener", "mouseMoved", MOUSEMOVED); + DESCRIBE_EVENT(aMap, "awt", "XMouseListener", "mousePressed", MOUSEPRESSED); + DESCRIBE_EVENT(aMap, "awt", "XMouseListener", "mouseReleased", MOUSERELEASED); + DESCRIBE_EVENT(aMap, "awt", "XMouseListener", "mouseExited", MOUSEEXITED); + DESCRIBE_EVENT(aMap, "form", "XResetListener", "approveReset", APPROVERESETTED); + DESCRIBE_EVENT(aMap, "form", "XResetListener", "resetted", RESETTED); + DESCRIBE_EVENT(aMap, "form", "XSubmitListener", "approveSubmit", SUBMITTED); + DESCRIBE_EVENT(aMap, "form", "XUpdateListener", "approveUpdate", BEFOREUPDATE); + DESCRIBE_EVENT(aMap, "form", "XUpdateListener", "updated", AFTERUPDATE); + DESCRIBE_EVENT(aMap, "form", "XLoadListener", "loaded", LOADED); + DESCRIBE_EVENT(aMap, "form", "XLoadListener", "reloading", RELOADING); + DESCRIBE_EVENT(aMap, "form", "XLoadListener", "reloaded", RELOADED); + DESCRIBE_EVENT(aMap, "form", "XLoadListener", "unloading", UNLOADING); + DESCRIBE_EVENT(aMap, "form", "XLoadListener", "unloaded", UNLOADED); + DESCRIBE_EVENT(aMap, "form", "XConfirmDeleteListener", "confirmDelete", CONFIRMDELETE); + DESCRIBE_EVENT(aMap, "sdb", "XRowSetApproveListener", "approveRowChange", APPROVEROWCHANGE); + DESCRIBE_EVENT(aMap, "sdbc", "XRowSetListener", "rowChanged", ROWCHANGE); + DESCRIBE_EVENT(aMap, "sdb", "XRowSetApproveListener", "approveCursorMove", POSITIONING); + DESCRIBE_EVENT(aMap, "sdbc", "XRowSetListener", "cursorMoved", POSITIONED); + DESCRIBE_EVENT(aMap, "form", "XDatabaseParameterListener", "approveParameter", APPROVEPARAMETER); + DESCRIBE_EVENT(aMap, "sdb", "XSQLErrorListener", "errorOccured", ERROROCCURRED); + DESCRIBE_EVENT(aMap, "awt", "XAdjustmentListener", "adjustmentValueChanged", ADJUSTMENTVALUECHANGED); + + return aMap; + }(); + + EventMap::const_iterator pos = s_aKnownEvents.find( _rMethodName ); + if ( pos == s_aKnownEvents.end() ) + return false; + + _out_rDescription = pos->second; + return true; + } + + OUString lcl_getEventPropertyName( std::u16string_view _rListenerClassName, std::u16string_view _rMethodName ) + { + return _rListenerClassName + OUStringChar(';') + _rMethodName; + } + + ScriptEventDescriptor lcl_getAssignedScriptEvent( const EventDescription& _rEvent, const std::vector< ScriptEventDescriptor >& _rAllAssignedMacros ) + { + ScriptEventDescriptor aScriptEvent; + // for the case there is actually no event assigned, initialize at least ListenerType and MethodName, + // so this ScriptEventDescriptor properly describes the given event + aScriptEvent.ListenerType = _rEvent.sListenerClassName; + aScriptEvent.EventMethod = _rEvent.sListenerMethodName; + + for ( const ScriptEventDescriptor& rSED : _rAllAssignedMacros ) + { + if ( rSED.ListenerType != _rEvent.sListenerClassName + || rSED.EventMethod != _rEvent.sListenerMethodName + ) + continue; + + if ( rSED.ScriptCode.isEmpty() + || rSED.ScriptType.isEmpty() + ) + { + OSL_FAIL( "lcl_getAssignedScriptEvent: me thinks this should not happen!" ); + continue; + } + + aScriptEvent = rSED; + + if ( aScriptEvent.ScriptType != "StarBasic" ) + continue; + + // this is an old-style macro specification: + // [document|application]:Library.Module.Function + // we need to translate this to the new-style macro specification + // vnd.sun.star.script:Library.Module.Function?language=Basic&location=[document|application] + + sal_Int32 nPrefixLen = aScriptEvent.ScriptCode.indexOf( ':' ); + OSL_ENSURE( nPrefixLen > 0, "lcl_getAssignedScriptEvent: illegal location!" ); + std::u16string_view sLocation = aScriptEvent.ScriptCode.subView( 0, nPrefixLen ); + std::u16string_view sMacroPath = aScriptEvent.ScriptCode.subView( nPrefixLen + 1 ); + + aScriptEvent.ScriptCode = + OUString::Concat("vnd.sun.star.script:") + + sMacroPath + + "?language=Basic&location=" + + sLocation; + + // also, this new-style spec requires the script code to be "Script" instead of "StarBasic" + aScriptEvent.ScriptType = "Script"; + } + return aScriptEvent; + } + + OUString lcl_getQualifiedKnownListenerName( const ScriptEventDescriptor& _rFormComponentEventDescriptor ) + { + EventDescription aKnownEvent; + if ( lcl_getEventDescriptionForMethod( _rFormComponentEventDescriptor.EventMethod, aKnownEvent ) ) + return aKnownEvent.sListenerClassName; + OSL_FAIL( "lcl_getQualifiedKnownListenerName: unknown method name!" ); + // somebody assigned an script to a form component event which we don't know + // Speaking strictly, this is not really an error - it is possible to do + // this programmatically -, but it should rarely happen, since it's not possible + // via UI + return _rFormComponentEventDescriptor.ListenerType; + } + + typedef std::set< Type, TypeLessByName > TypeBag; + + void lcl_addListenerTypesFor_throw( const Reference< XInterface >& _rxComponent, + const Reference< XIntrospection >& _rxIntrospection, TypeBag& _out_rTypes ) + { + if ( !_rxComponent.is() ) + return; + OSL_PRECOND( _rxIntrospection.is(), "lcl_addListenerTypesFor_throw: this will crash!" ); + + Reference< XIntrospectionAccess > xIntrospectionAccess( + _rxIntrospection->inspect( Any( _rxComponent ) ), UNO_SET_THROW ); + + const Sequence< Type > aListeners( xIntrospectionAccess->getSupportedListeners() ); + + std::copy( aListeners.begin(), aListeners.end(), + std::insert_iterator< TypeBag >( _out_rTypes, _out_rTypes.begin() ) ); + } + } + + typedef ::cppu::WeakImplHelper < css::container::XNameReplace + > EventHolder_Base; + + namespace { + + /* A UNO component holding assigned event descriptions, for use with a SvxMacroAssignDlg */ + class EventHolder : public EventHolder_Base + { + private: + typedef std::unordered_map< OUString, ScriptEventDescriptor > EventMap; + typedef std::map< EventId, OUString > EventMapIndexAccess; + + EventMap m_aEventNameAccess; + EventMapIndexAccess m_aEventIndexAccess; + + public: + EventHolder( ); + + void addEvent( EventId _nId, const OUString& _rEventName, const ScriptEventDescriptor& _rScriptEvent ); + + /** effectively the same as getByName, but instead of converting the ScriptEventDescriptor to the weird + format used by the macro assignment dialog, it is returned directly + */ + ScriptEventDescriptor getNormalizedDescriptorByName( const OUString& _rEventName ) const; + + // XNameReplace + virtual void SAL_CALL replaceByName( const OUString& _rName, const Any& aElement ) override; + virtual Any SAL_CALL getByName( const OUString& _rName ) override; + virtual Sequence< OUString > SAL_CALL getElementNames( ) override; + virtual sal_Bool SAL_CALL hasByName( const OUString& _rName ) override; + virtual Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + + protected: + virtual ~EventHolder( ) override; + + private: + ScriptEventDescriptor const & impl_getDescriptor_throw( const OUString& _rEventName ) const; + }; + + } + + EventHolder::EventHolder() + { + } + + EventHolder::~EventHolder() + { + m_aEventNameAccess.clear(); + m_aEventIndexAccess.clear(); + } + + void EventHolder::addEvent( EventId _nId, const OUString& _rEventName, const ScriptEventDescriptor& _rScriptEvent ) + { + std::pair< EventMap::iterator, bool > insertionResult = + m_aEventNameAccess.emplace( _rEventName, _rScriptEvent ); + OSL_ENSURE( insertionResult.second, "EventHolder::addEvent: there already was a MacroURL for this event!" ); + m_aEventIndexAccess[ _nId ] = _rEventName; + } + + ScriptEventDescriptor EventHolder::getNormalizedDescriptorByName( const OUString& _rEventName ) const + { + return impl_getDescriptor_throw( _rEventName ); + } + + ScriptEventDescriptor const & EventHolder::impl_getDescriptor_throw( const OUString& _rEventName ) const + { + EventMap::const_iterator pos = m_aEventNameAccess.find( _rEventName ); + if ( pos == m_aEventNameAccess.end() ) + throw NoSuchElementException( OUString(), *const_cast< EventHolder* >( this ) ); + return pos->second; + } + + void SAL_CALL EventHolder::replaceByName( const OUString& _rName, const Any& _rElement ) + { + EventMap::iterator pos = m_aEventNameAccess.find( _rName ); + if ( pos == m_aEventNameAccess.end() ) + throw NoSuchElementException( OUString(), *this ); + + Sequence< PropertyValue > aScriptDescriptor; + OSL_VERIFY( _rElement >>= aScriptDescriptor ); + + ::comphelper::NamedValueCollection aExtractor( aScriptDescriptor ); + + pos->second.ScriptType = aExtractor.getOrDefault( "EventType", OUString() ); + pos->second.ScriptCode = aExtractor.getOrDefault( "Script", OUString() ); + } + + Any SAL_CALL EventHolder::getByName( const OUString& _rName ) + { + ScriptEventDescriptor aDescriptor( impl_getDescriptor_throw( _rName ) ); + + Sequence< PropertyValue > aScriptDescriptor{ + comphelper::makePropertyValue("EventType", aDescriptor.ScriptType), + comphelper::makePropertyValue("Script", aDescriptor.ScriptCode) + }; + + return Any( aScriptDescriptor ); + } + + Sequence< OUString > SAL_CALL EventHolder::getElementNames( ) + { + Sequence< OUString > aReturn( m_aEventIndexAccess.size() ); + OUString* pReturn = aReturn.getArray(); + + // SvxMacroAssignDlg has a weird API: It expects a XNameReplace, means a container whose + // main access method is by name. In its UI, it shows the possible events in exactly the + // order in which XNameAccess::getElementNames returns them. + // However, SvxMacroAssignDlg *also* takes an index for the initial selection, which is + // relative to the sequence returned by XNameAccess::getElementNames. + // This is IMO weird, since it mixes index access with name access, which decreases efficiency + // of the implementation. + // Well, it means we're forced to return the events in getElementNames in exactly the same as they + // appear in the property browser UI. + for (auto const& elem : m_aEventIndexAccess) + { + *pReturn = elem.second; + ++pReturn; + } + return aReturn; + } + + sal_Bool SAL_CALL EventHolder::hasByName( const OUString& _rName ) + { + EventMap::const_iterator pos = m_aEventNameAccess.find( _rName ); + return pos != m_aEventNameAccess.end(); + } + + Type SAL_CALL EventHolder::getElementType( ) + { + return cppu::UnoType<Sequence< PropertyValue >>::get(); + } + + sal_Bool SAL_CALL EventHolder::hasElements( ) + { + return !m_aEventNameAccess.empty(); + } + + + EventHandler::EventHandler( const Reference< XComponentContext >& _rxContext ) + :EventHandler_Base( m_aMutex ) + ,m_xContext( _rxContext ) + ,m_aPropertyListeners( m_aMutex ) + ,m_bEventsMapInitialized( false ) + ,m_bIsDialogElement( false ) + ,m_nGridColumnType( -1 ) + { + } + + EventHandler::~EventHandler() + { + } + + OUString SAL_CALL EventHandler::getImplementationName( ) + { + return "com.sun.star.comp.extensions.EventHandler"; + } + + sal_Bool SAL_CALL EventHandler::supportsService( const OUString& ServiceName ) + { + return cppu::supportsService(this, ServiceName); + } + + Sequence< OUString > SAL_CALL EventHandler::getSupportedServiceNames( ) + { + return { "com.sun.star.form.inspection.EventHandler" }; + } + + void SAL_CALL EventHandler::inspect( const Reference< XInterface >& _rxIntrospectee ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( !_rxIntrospectee.is() ) + throw NullPointerException(); + + m_xComponent.set( _rxIntrospectee, UNO_QUERY_THROW ); + + m_bEventsMapInitialized = false; + EventMap().swap(m_aEvents); + + m_bIsDialogElement = false; + m_nGridColumnType = -1; + try + { + Reference< XPropertySetInfo > xPSI( m_xComponent->getPropertySetInfo() ); + m_bIsDialogElement = xPSI.is() + && xPSI->hasPropertyByName( PROPERTY_WIDTH ) + && xPSI->hasPropertyByName( PROPERTY_HEIGHT ) + && xPSI->hasPropertyByName( PROPERTY_POSITIONX ) + && xPSI->hasPropertyByName( PROPERTY_POSITIONY ); + + Reference< XChild > xAsChild( _rxIntrospectee, UNO_QUERY ); + if ( xAsChild.is() && !Reference< XForm >( _rxIntrospectee, UNO_QUERY ).is() ) + { + if ( FormComponentType::GRIDCONTROL == classifyComponent( xAsChild->getParent() ) ) + { + m_nGridColumnType = classifyComponent( _rxIntrospectee ); + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + Any SAL_CALL EventHandler::getPropertyValue( const OUString& _rPropertyName ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + const EventDescription& rEvent = impl_getEventForName_throw( _rPropertyName ); + + std::vector< ScriptEventDescriptor > aEvents; + impl_getComponentScriptEvents_nothrow( aEvents ); + + ScriptEventDescriptor aPropertyValue; + for ( const ScriptEventDescriptor& rSCD : aEvents ) + { + if ( rEvent.sListenerClassName == rSCD.ListenerType + && rEvent.sListenerMethodName == rSCD.EventMethod + ) + { + aPropertyValue = rSCD; + break; + } + } + + return Any( aPropertyValue ); + } + + void SAL_CALL EventHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + const EventDescription& rEvent = impl_getEventForName_throw( _rPropertyName ); + + ScriptEventDescriptor aNewScriptEvent; + OSL_VERIFY( _rValue >>= aNewScriptEvent ); + + ScriptEventDescriptor aOldScriptEvent; + OSL_VERIFY( getPropertyValue( _rPropertyName ) >>= aOldScriptEvent ); + if ( aOldScriptEvent == aNewScriptEvent ) + return; + + if ( m_bIsDialogElement ) + impl_setDialogElementScriptEvent_nothrow( aNewScriptEvent ); + else + impl_setFormComponentScriptEvent_nothrow( aNewScriptEvent ); + + PropertyHandlerHelper::setContextDocumentModified( m_xContext ); + + PropertyChangeEvent aEvent; + aEvent.Source = m_xComponent; + aEvent.PropertyHandle = rEvent.nId; + aEvent.PropertyName = _rPropertyName; + aEvent.OldValue <<= aOldScriptEvent; + aEvent.NewValue <<= aNewScriptEvent; + m_aPropertyListeners.notifyEach( &XPropertyChangeListener::propertyChange, aEvent ); + } + + Any SAL_CALL EventHandler::convertToPropertyValue( const OUString& _rPropertyName, const Any& _rControlValue ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + OUString sNewScriptCode; + OSL_VERIFY( _rControlValue >>= sNewScriptCode ); + + std::vector< ScriptEventDescriptor > aAllAssignedEvents; + impl_getComponentScriptEvents_nothrow( aAllAssignedEvents ); + + const EventDescription& rEvent = impl_getEventForName_throw( _rPropertyName ); + ScriptEventDescriptor aAssignedScript = lcl_getAssignedScriptEvent( rEvent, aAllAssignedEvents ); + + OSL_ENSURE( sNewScriptCode.isEmpty(), "EventHandler::convertToPropertyValue: cannot convert a non-empty display name!" ); + // Usually, there is no possibility for the user to change the content of an event binding directly in the + // input field, this instead is done with the macro assignment dialog. + // The only exception is the user pressing "DEL" while the control has the focus, in this case, we reset the + // control content to an empty string. So this is the only scenario where this method is allowed to be called. + + // Strictly, we would be able to convert the display value to a property value, + // using the "name (location, language)" format we used in convertToControlValue. However, + // there is no need for this code... + + aAssignedScript.ScriptCode = sNewScriptCode; + return Any( aAssignedScript ); + } + + Any SAL_CALL EventHandler::convertToControlValue( const OUString& /*_rPropertyName*/, const Any& _rPropertyValue, const Type& _rControlValueType ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + ScriptEventDescriptor aScriptEvent; + OSL_VERIFY( _rPropertyValue >>= aScriptEvent ); + + OSL_ENSURE( _rControlValueType.getTypeClass() == TypeClass_STRING, + "EventHandler::convertToControlValue: unexpected ControlValue type class!" ); + + OUString sScript( aScriptEvent.ScriptCode ); + if ( !sScript.isEmpty() ) + { + // format is: "name (location, language)" + try + { + // parse + Reference< XUriReferenceFactory > xUriRefFac = UriReferenceFactory::create( m_xContext ); + Reference< XVndSunStarScriptUrlReference > xScriptUri( xUriRefFac->parse( sScript ), UNO_QUERY_THROW ); + + OUStringBuffer aComposeBuffer; + + // name + aComposeBuffer.append( xScriptUri->getName() ); + + // location + const OUString sLocation = xScriptUri->getParameter( "location" ); + const OUString sLanguage = xScriptUri->getParameter( "language" ); + + if ( !(sLocation.isEmpty() && sLanguage.isEmpty()) ) + { + aComposeBuffer.append( " (" ); + + // location + OSL_ENSURE( !sLocation.isEmpty(), "EventHandler::convertToControlValue: unexpected: no location!" ); + if ( !sLocation.isEmpty() ) + { + aComposeBuffer.append( sLocation ); + aComposeBuffer.append( ", " ); + } + + // language + if ( !sLanguage.isEmpty() ) + { + aComposeBuffer.append( sLanguage ); + } + + aComposeBuffer.append( ')' ); + } + + sScript = aComposeBuffer.makeStringAndClear(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + return Any( sScript ); + } + + PropertyState SAL_CALL EventHandler::getPropertyState( const OUString& /*_rPropertyName*/ ) + { + return PropertyState_DIRECT_VALUE; + } + + void SAL_CALL EventHandler::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !_rxListener.is() ) + throw NullPointerException(); + m_aPropertyListeners.addInterface( _rxListener ); + } + + void SAL_CALL EventHandler::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + m_aPropertyListeners.removeInterface( _rxListener ); + } + + Sequence< Property > SAL_CALL EventHandler::getSupportedProperties() + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !m_bEventsMapInitialized ) + { + m_bEventsMapInitialized = true; + try + { + std::vector< Type > aListeners; + impl_getComponentListenerTypes_nothrow( aListeners ); + + OUString sListenerClassName; + + // loop through all listeners and all methods, and see which we can present at the UI + for ( const Type& rListener : aListeners ) + { + // the programmatic name of the listener, to be used as "property" name + sListenerClassName = rListener.getTypeName(); + OSL_ENSURE( !sListenerClassName.isEmpty(), "EventHandler::getSupportedProperties: strange - no listener name ..." ); + if ( sListenerClassName.isEmpty() ) + continue; + + // loop through all methods + const Sequence<OUString> aEventMethods = comphelper::getEventMethodsForType( rListener ); + for (const OUString& rMethod : aEventMethods) + { + EventDescription aEvent; + if ( !lcl_getEventDescriptionForMethod( rMethod, aEvent ) ) + continue; + + if ( !impl_filterMethod_nothrow( aEvent ) ) + continue; + + m_aEvents.emplace( + lcl_getEventPropertyName( sListenerClassName, rMethod ), aEvent ); + } + } + + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + // sort them by ID - this is the relative ordering in the UI + std::map< EventId, Property > aOrderedProperties; + for (auto const& event : m_aEvents) + { + aOrderedProperties[ event.second.nId ] = Property( + event.first, event.second.nId, + ::cppu::UnoType<OUString>::get(), + PropertyAttribute::BOUND ); + } + + return comphelper::mapValuesToSequence( aOrderedProperties ); + } + + Sequence< OUString > SAL_CALL EventHandler::getSupersededProperties( ) + { + // none + return Sequence< OUString >( ); + } + + Sequence< OUString > SAL_CALL EventHandler::getActuatingProperties( ) + { + // none + return Sequence< OUString >( ); + } + + LineDescriptor SAL_CALL EventHandler::describePropertyLine( const OUString& _rPropertyName, + const Reference< XPropertyControlFactory >& _rxControlFactory ) + { + if ( !_rxControlFactory.is() ) + throw NullPointerException(); + + ::osl::MutexGuard aGuard( m_aMutex ); + + LineDescriptor aDescriptor; + + aDescriptor.Control = _rxControlFactory->createPropertyControl( PropertyControlType::TextField, true ); + new PropertyControlExtender( aDescriptor.Control ); + + const EventDescription& rEvent = impl_getEventForName_throw( _rPropertyName ); + aDescriptor.DisplayName = rEvent.sDisplayName; + aDescriptor.HelpURL = HelpIdUrl::getHelpURL( rEvent.sHelpId ); + aDescriptor.PrimaryButtonId = OStringToOUString(rEvent.sUniqueBrowseId, RTL_TEXTENCODING_UTF8); + aDescriptor.HasPrimaryButton = true; + aDescriptor.Category = "Events"; + return aDescriptor; + } + + sal_Bool SAL_CALL EventHandler::isComposable( const OUString& /*_rPropertyName*/ ) + { + return false; + } + + InteractiveSelectionResult SAL_CALL EventHandler::onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool /*_bPrimary*/, Any& /*_rData*/, const Reference< XObjectInspectorUI >& _rxInspectorUI ) + { + if ( !_rxInspectorUI.is() ) + throw NullPointerException(); + + ::osl::MutexGuard aGuard( m_aMutex ); + const EventDescription& rForEvent = impl_getEventForName_throw( _rPropertyName ); + + std::vector< ScriptEventDescriptor > aAllAssignedEvents; + impl_getComponentScriptEvents_nothrow( aAllAssignedEvents ); + + // SvxMacroAssignDlg-compatible structure holding all event/assignments + ::rtl::Reference< EventHolder > pEventHolder( new EventHolder ); + + for (auto const& event : m_aEvents) + { + // the script which is assigned to the current event (if any) + ScriptEventDescriptor aAssignedScript = lcl_getAssignedScriptEvent( event.second, aAllAssignedEvents ); + pEventHolder->addEvent( event.second.nId, event.second.sListenerMethodName, aAssignedScript ); + } + + // the initial selection in the dialog + const Sequence< OUString > aNames( pEventHolder->getElementNames() ); + const OUString* pChosenEvent = std::find( aNames.begin(), aNames.end(), rForEvent.sListenerMethodName ); + sal_uInt16 nInitialSelection = static_cast<sal_uInt16>( pChosenEvent - aNames.begin() ); + + // the dialog + SvxAbstractDialogFactory* pFactory = SvxAbstractDialogFactory::Create(); + + ScopedVclPtr<VclAbstractDialog> pDialog( pFactory->CreateSvxMacroAssignDlg( + PropertyHandlerHelper::getDialogParentFrame( m_xContext ), + impl_getContextFrame_nothrow(), + m_bIsDialogElement, + pEventHolder, + nInitialSelection + ) ); + + if ( !pDialog ) + return InteractiveSelectionResult_Cancelled; + + // DF definite problem here + // OK & Cancel seem to be both returning 0 + if ( pDialog->Execute() == RET_CANCEL ) + return InteractiveSelectionResult_Cancelled; + + try + { + for (auto const& event : m_aEvents) + { + ScriptEventDescriptor aScriptDescriptor( pEventHolder->getNormalizedDescriptorByName( event.second.sListenerMethodName ) ); + + // set the new "property value" + setPropertyValue( + lcl_getEventPropertyName( event.second.sListenerClassName, event.second.sListenerMethodName ), + Any( aScriptDescriptor ) + ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + + return InteractiveSelectionResult_Success; + } + + void SAL_CALL EventHandler::actuatingPropertyChanged( const OUString& /*_rActuatingPropertyName*/, const Any& /*_rNewValue*/, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& /*_rxInspectorUI*/, sal_Bool /*_bFirstTimeInit*/ ) + { + OSL_FAIL( "EventHandler::actuatingPropertyChanged: no actuating properties -> no callback (well, this is how it *should* be!)" ); + } + + IMPLEMENT_FORWARD_XCOMPONENT( EventHandler, EventHandler_Base ) + + void SAL_CALL EventHandler::disposing() + { + EventMap().swap(m_aEvents); + m_xComponent.clear(); + } + + sal_Bool SAL_CALL EventHandler::suspend( sal_Bool /*_bSuspend*/ ) + { + return true; + } + + Reference< XFrame > EventHandler::impl_getContextFrame_nothrow() const + { + Reference< XFrame > xContextFrame; + + try + { + Reference< XModel > xContextDocument( PropertyHandlerHelper::getContextDocument(m_xContext), UNO_QUERY_THROW ); + Reference< XController > xController( xContextDocument->getCurrentController(), UNO_SET_THROW ); + xContextFrame.set( xController->getFrame(), UNO_SET_THROW ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + + return xContextFrame; + } + + sal_Int32 EventHandler::impl_getComponentIndexInParent_throw() const + { + Reference< XChild > xChild( m_xComponent, UNO_QUERY_THROW ); + Reference< XIndexAccess > xParentAsIndexAccess( xChild->getParent(), UNO_QUERY_THROW ); + + // get the index of the inspected object within its parent container + sal_Int32 nElements = xParentAsIndexAccess->getCount(); + for ( sal_Int32 i=0; i<nElements; ++i ) + { + Reference< XInterface > xElement( xParentAsIndexAccess->getByIndex( i ), UNO_QUERY_THROW ); + if ( xElement == m_xComponent ) + return i; + } + throw NoSuchElementException(); + } + + void EventHandler::impl_getFormComponentScriptEvents_nothrow( std::vector < ScriptEventDescriptor >& _out_rEvents ) const + { + _out_rEvents.clear(); + try + { + Reference< XChild > xChild( m_xComponent, UNO_QUERY_THROW ); + Reference< XEventAttacherManager > xEventManager( xChild->getParent(), UNO_QUERY_THROW ); + comphelper::sequenceToContainer(_out_rEvents, xEventManager->getScriptEvents( impl_getComponentIndexInParent_throw() )); + + // the form component script API has unqualified listener names, but for normalization + // purpose, we want fully qualified ones + for ( ScriptEventDescriptor& rSED : _out_rEvents) + { + rSED.ListenerType = lcl_getQualifiedKnownListenerName( rSED ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + void EventHandler::impl_getComponentListenerTypes_nothrow( std::vector< Type >& _out_rTypes ) const + { + _out_rTypes.clear(); + try + { + // we use a set to avoid duplicates + TypeBag aListeners; + + Reference< XIntrospection > xIntrospection = theIntrospection::get( m_xContext ); + + // --- model listeners + lcl_addListenerTypesFor_throw( + m_xComponent, xIntrospection, aListeners ); + + // --- "secondary component" (usually: "control" listeners) + { + Reference< XInterface > xSecondaryComponent( impl_getSecondaryComponentForEventInspection_throw() ); + lcl_addListenerTypesFor_throw( xSecondaryComponent, xIntrospection, aListeners ); + ::comphelper::disposeComponent( xSecondaryComponent ); + } + + // now that they're disambiguated, copy these types into our member + _out_rTypes.insert( _out_rTypes.end(), aListeners.begin(), aListeners.end() ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + void EventHandler::impl_getDialogElementScriptEvents_nothrow( std::vector < ScriptEventDescriptor >& _out_rEvents ) const + { + _out_rEvents.clear(); + try + { + Reference< XScriptEventsSupplier > xEventsSupplier( m_xComponent, UNO_QUERY_THROW ); + Reference< XNameContainer > xEvents( xEventsSupplier->getEvents(), UNO_SET_THROW ); + Sequence< OUString > aEventNames( xEvents->getElementNames() ); + + sal_Int32 nEventCount = aEventNames.getLength(); + _out_rEvents.resize( nEventCount ); + + for( sal_Int32 i = 0; i < nEventCount; ++i ) + OSL_VERIFY( xEvents->getByName( aEventNames[i] ) >>= _out_rEvents[i] ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + Reference< XInterface > EventHandler::impl_getSecondaryComponentForEventInspection_throw( ) const + { + Reference< XInterface > xReturn; + + // if it's a form, create a form controller for the additional events + Reference< XForm > xComponentAsForm( m_xComponent, UNO_QUERY ); + if ( xComponentAsForm.is() ) + { + Reference< XTabControllerModel > xComponentAsTCModel( m_xComponent, UNO_QUERY_THROW ); + Reference< XFormController > xController = FormController::create( m_xContext ); + xController->setModel( xComponentAsTCModel ); + + xReturn = xController; + } + else + { + OUString sControlService; + OSL_VERIFY( m_xComponent->getPropertyValue( PROPERTY_DEFAULTCONTROL ) >>= sControlService ); + + xReturn = m_xContext->getServiceManager()->createInstanceWithContext( sControlService, m_xContext ); + } + return xReturn; + } + + const EventDescription& EventHandler::impl_getEventForName_throw( const OUString& _rPropertyName ) const + { + EventMap::const_iterator pos = m_aEvents.find( _rPropertyName ); + if ( pos == m_aEvents.end() ) + throw UnknownPropertyException(_rPropertyName); + return pos->second; + } + + namespace + { + bool lcl_endsWith( const OUString& _rText, const OUString& _rCheck ) + { + sal_Int32 nTextLen = _rText.getLength(); + sal_Int32 nCheckLen = _rCheck.getLength(); + if ( nCheckLen > nTextLen ) + return false; + + return _rText.indexOf( _rCheck ) == ( nTextLen - nCheckLen ); + } + } + + void EventHandler::impl_setFormComponentScriptEvent_nothrow( const ScriptEventDescriptor& _rScriptEvent ) + { + try + { + OUString sScriptCode( _rScriptEvent.ScriptCode ); + OUString sScriptType( _rScriptEvent.ScriptType ); + bool bResetScript = sScriptCode.isEmpty(); + + sal_Int32 nObjectIndex = impl_getComponentIndexInParent_throw(); + Reference< XChild > xChild( m_xComponent, UNO_QUERY_THROW ); + Reference< XEventAttacherManager > xEventManager( xChild->getParent(), UNO_QUERY_THROW ); + std::vector< ScriptEventDescriptor > aEvents; + comphelper::sequenceToContainer( aEvents, xEventManager->getScriptEvents( nObjectIndex ) ); + + // is there already a registered script for this event? + sal_Int32 eventCount = aEvents.size(), event = 0; + for ( event = 0; event < eventCount; ++event ) + { + ScriptEventDescriptor* pEvent = &aEvents[event]; + if ( ( pEvent->EventMethod == _rScriptEvent.EventMethod ) + && ( lcl_endsWith( _rScriptEvent.ListenerType, pEvent->ListenerType ) ) + // (strange enough, the events we get from getScriptEvents are not fully qualified) + ) + { + // yes + if ( !bResetScript ) + { + // set to something non-empty -> overwrite + pEvent->ScriptCode = sScriptCode; + pEvent->ScriptType = sScriptType; + } + else + { + // set to empty -> remove from vector + aEvents.erase(aEvents.begin() + event ); + --eventCount; + } + break; + } + } + if ( ( event >= eventCount ) && !bResetScript ) + { + // no, did not find it -> append + aEvents.push_back( _rScriptEvent ); + } + + xEventManager->revokeScriptEvents( nObjectIndex ); + xEventManager->registerScriptEvents( nObjectIndex, comphelper::containerToSequence(aEvents) ); + + PropertyHandlerHelper::setContextDocumentModified( m_xContext ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + void EventHandler::impl_setDialogElementScriptEvent_nothrow( const ScriptEventDescriptor& _rScriptEvent ) + { + try + { + OUString sScriptCode( _rScriptEvent.ScriptCode ); + bool bResetScript = sScriptCode.isEmpty(); + + Reference< XScriptEventsSupplier > xEventsSupplier( m_xComponent, UNO_QUERY_THROW ); + Reference< XNameContainer > xEvents( xEventsSupplier->getEvents(), UNO_SET_THROW ); + + OUString sCompleteName = + _rScriptEvent.ListenerType + + "::" + + _rScriptEvent.EventMethod; + + bool bExists = xEvents->hasByName( sCompleteName ); + + if ( bResetScript ) + { + if ( bExists ) + xEvents->removeByName( sCompleteName ); + } + else + { + Any aNewValue; aNewValue <<= _rScriptEvent; + + if ( bExists ) + xEvents->replaceByName( sCompleteName, aNewValue ); + else + xEvents->insertByName( sCompleteName, aNewValue ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + bool EventHandler::impl_filterMethod_nothrow( const EventDescription& _rEvent ) const + { + // some (control-triggered) events do not make sense for certain grid control columns. However, + // our mechanism to retrieve control-triggered events does not know about this, so we do some + // late filtering here. + switch ( m_nGridColumnType ) + { + case FormComponentType::COMBOBOX: + if ( UID_BRWEVT_ACTIONPERFORMED == _rEvent.sUniqueBrowseId ) + return false; + break; + case FormComponentType::LISTBOX: + if ( ( UID_BRWEVT_CHANGED == _rEvent.sUniqueBrowseId ) + || ( UID_BRWEVT_ACTIONPERFORMED == _rEvent.sUniqueBrowseId ) + ) + return false; + break; + } + + return true; + } + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_EventHandler_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::EventHandler(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/eventhandler.hxx b/extensions/source/propctrlr/eventhandler.hxx new file mode 100644 index 000000000..4506f1b06 --- /dev/null +++ b/extensions/source/propctrlr/eventhandler.hxx @@ -0,0 +1,241 @@ +/* -*- 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 "pcrcommon.hxx" + +#include <com/sun/star/script/ScriptEventDescriptor.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/inspection/XPropertyHandler.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/compbase.hxx> +#include <unotools/resmgr.hxx> + +#include <unordered_map> + +namespace pcr +{ + + + //= EventDescription + + typedef sal_Int32 EventId; + struct EventDescription + { + public: + OUString sDisplayName; + OUString sListenerClassName; + OUString sListenerMethodName; + OUString sHelpId; + OString sUniqueBrowseId; + EventId nId; + + EventDescription() + :nId( 0 ) + { + } + + EventDescription( + EventId _nId, + const char* _pListenerNamespaceAscii, + const char* _pListenerClassAsciiName, + const char* _pListenerMethodAsciiName, + TranslateId pDisplayNameResId, + const OUString& _sHelpId, + const OString& _sUniqueBrowseId ); + }; + + typedef std::unordered_map< OUString, EventDescription > EventMap; + + + //= EventHandler + + typedef ::cppu::WeakComponentImplHelper < css::inspection::XPropertyHandler + , css::lang::XServiceInfo + > EventHandler_Base; + class EventHandler final : public EventHandler_Base + { + private: + mutable ::osl::Mutex m_aMutex; + + /// the context in which the instance was created + css::uno::Reference< css::uno::XComponentContext > m_xContext; + /// the properties of the object we're handling + css::uno::Reference< css::beans::XPropertySet > m_xComponent; + /// our XPropertyChangeListener(s) + PropertyChangeListeners m_aPropertyListeners; + /// cache of the events we found at our introspectee + EventMap m_aEvents; + /// has m_aEvents been initialized? + bool m_bEventsMapInitialized; + /// is our introspectee a dialog element? + bool m_bIsDialogElement; + // TODO: move different handling into different derived classes? + /// (FormComponent) type of the grid column being inspected, or -1 if we're not inspecting a grid column + sal_Int16 m_nGridColumnType; + + public: + explicit EventHandler( + const css::uno::Reference< css::uno::XComponentContext >& _rxContext + ); + + virtual ~EventHandler() override; + + private: + // XPropertyHandler overridables + virtual void SAL_CALL inspect( const css::uno::Reference< css::uno::XInterface >& _rxIntrospectee ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override; + virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override; + virtual css::uno::Any SAL_CALL convertToPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rControlValue ) override; + virtual css::uno::Any SAL_CALL convertToControlValue( const OUString& _rPropertyName, const css::uno::Any& _rPropertyValue, const css::uno::Type& _rControlValueType ) override; + virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& _rPropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override; + virtual css::uno::Sequence< css::beans::Property > SAL_CALL getSupportedProperties() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupersededProperties( ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getActuatingProperties( ) override; + virtual css::inspection::LineDescriptor SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override; + virtual sal_Bool SAL_CALL isComposable( const OUString& _rPropertyName ) override; + virtual css::inspection::InteractiveSelectionResult SAL_CALL onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, css::uno::Any& _rData, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) override; + virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) override; + virtual sal_Bool SAL_CALL suspend( sal_Bool _bSuspend ) override; + + // XComponent + DECLARE_XCOMPONENT() + virtual void SAL_CALL disposing() 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; + + /** returns the script events associated with our introspectee + @param _out_rEvents + Takes, upon successful return, the events currently associated with the introspectee + @precond + Our introspectee is a form component + */ + void impl_getFormComponentScriptEvents_nothrow( + std::vector< css::script::ScriptEventDescriptor >& _out_rEvents + ) const; + + /** returns the script events associated with our introspectee + @param _out_rEvents + Takes, upon successful return, the events currently associated with the introspectee + @precond + Our introspectee is a dialog element + */ + void impl_getDialogElementScriptEvents_nothrow( + std::vector< css::script::ScriptEventDescriptor >& _out_rEvents + ) const; + + /** returns the script events associated with our introspectee + @param _out_rEvents + Takes, the events currently associated with the introspectee + */ + void impl_getComponentScriptEvents_nothrow( + std::vector< css::script::ScriptEventDescriptor >& _out_rEvents + ) const + { + if ( m_bIsDialogElement ) + impl_getDialogElementScriptEvents_nothrow( _out_rEvents ); + else + impl_getFormComponentScriptEvents_nothrow( _out_rEvents ); + } + + /** returns the types of the listeners which can be registered at our introspectee + @param _out_rTypes + Takes, upon successful return, the types of possible listeners at the introspectee + */ + void impl_getComponentListenerTypes_nothrow( + std::vector< css::uno::Type >& _out_rTypes + ) const; + + /** returns a secondary component to be used for event inspection + + In the UI, we want to mix events for the control model with events for the control. + Since our introspectee is a model, this method creates a control for it (if possible). + + @return + the secondary component whose events should be mixed with the introspectee's events + The caller takes the ownership of the component (if not <NULL/>). + + @throws + if an unexpected error occurs during creation of the secondary component. + A <NULL/> component to be returned is not unexpected, but allowed + + @precond + ->m_xComponent is not <NULL/> + */ + css::uno::Reference< css::uno::XInterface > + impl_getSecondaryComponentForEventInspection_throw( ) const; + + /** returns the event description for the given (programmatic) property name + @param _rPropertyName + the name whose event description should be looked up + @return + the event description for the property name + @throws css::beans::UnknownPropertyException + if our introspectee does not have an event with the given logical name (see ->getSupportedProperties) + */ + const EventDescription& + impl_getEventForName_throw( const OUString& _rPropertyName ) const; + + /** returns the index of our component within its parent, if this parent can be + obtained (XChild::getParent) and supports an ->XIndexAccess interface + */ + sal_Int32 impl_getComponentIndexInParent_throw() const; + + /** sets a given script event as event handler at a form component + + @param _rScriptEvent + the script event to set + */ + void impl_setFormComponentScriptEvent_nothrow( const css::script::ScriptEventDescriptor& _rScriptEvent ); + + /** sets a given script event as event handler at a dialog component + + @param _rScriptEvent + the script event to set + */ + void impl_setDialogElementScriptEvent_nothrow( const css::script::ScriptEventDescriptor& _rScriptEvent ); + + /** returns the frame associated with our context document + */ + css::uno::Reference< css::frame::XFrame > + impl_getContextFrame_nothrow() const; + + /** approves or denies a certain method to be included in the UI + @return + <TRUE/> if and only if the given method is allowed. + */ + bool impl_filterMethod_nothrow( const EventDescription& _rEvent ) const; + + EventHandler( const EventHandler& ) = delete; + EventHandler& operator=( const EventHandler& ) = delete; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/fontdialog.cxx b/extensions/source/propctrlr/fontdialog.cxx new file mode 100644 index 000000000..133367953 --- /dev/null +++ b/extensions/source/propctrlr/fontdialog.cxx @@ -0,0 +1,572 @@ +/* -*- 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 "fontdialog.hxx" +#include <vcl/outdev.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/unohelp.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <comphelper/types.hxx> +#include <comphelper/extract.hxx> +#include <com/sun/star/awt/FontDescriptor.hpp> +#include <com/sun/star/awt/FontWeight.hpp> +#include <com/sun/star/awt/FontSlant.hpp> +#include <com/sun/star/awt/FontUnderline.hpp> +#include <com/sun/star/awt/FontStrikeout.hpp> +#include "formstrings.hxx" +#include "fontitemids.hxx" +#include <editeng/charreliefitem.hxx> +#include <editeng/emphasismarkitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/wrlmitem.hxx> +#include <editeng/cmapitem.hxx> +#include <editeng/contouritem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/flstitem.hxx> +#include <svtools/ctrltool.hxx> +#include <tools/diagnose_ex.h> +#include <com/sun/star/beans/XPropertyState.hpp> +#include <svx/svxids.hrc> +#include <svx/svxdlg.hxx> +#include <svx/dialogs.hrc> +#include <svx/flagsdef.hxx> + + +namespace pcr +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + + + //= OFontPropertyExtractor + + namespace { + + class OFontPropertyExtractor + { + protected: + css::uno::Reference< css::beans::XPropertySet > + m_xPropValueAccess; + css::uno::Reference< css::beans::XPropertyState > + m_xPropStateAccess; + + public: + explicit OFontPropertyExtractor( const css::uno::Reference< css::beans::XPropertySet >& + _rxProps ); + + public: + bool getCheckFontProperty(const OUString& _rPropName, css::uno::Any& _rValue); + OUString getStringFontProperty(const OUString& _rPropName, const OUString& _rDefault); + sal_Int16 getInt16FontProperty(const OUString& _rPropName, const sal_Int16 _nDefault); + sal_Int32 getInt32FontProperty(const OUString& _rPropName, const sal_Int32 _nDefault); + float getFloatFontProperty(const OUString& _rPropName, const float _nDefault); + + void invalidateItem( + const OUString& _rPropName, + sal_uInt16 _nItemId, + SfxItemSet& _rSet, + bool _bForceInvalidation = false); + }; + + } + + OFontPropertyExtractor::OFontPropertyExtractor(const Reference< XPropertySet >& _rxProps) + :m_xPropValueAccess(_rxProps) + ,m_xPropStateAccess(_rxProps, UNO_QUERY) + { + OSL_ENSURE(m_xPropValueAccess.is(), "OFontPropertyExtractor::OFontPropertyExtractor: invalid property set!"); + } + + + bool OFontPropertyExtractor::getCheckFontProperty(const OUString& _rPropName, Any& _rValue) + { + _rValue = m_xPropValueAccess->getPropertyValue(_rPropName); + if (m_xPropStateAccess.is()) + return PropertyState_DEFAULT_VALUE == m_xPropStateAccess->getPropertyState(_rPropName); + + return false; + } + + + OUString OFontPropertyExtractor::getStringFontProperty(const OUString& _rPropName, const OUString& _rDefault) + { + Any aValue; + if (getCheckFontProperty(_rPropName, aValue)) + return _rDefault; + + return ::comphelper::getString(aValue); + } + + + sal_Int16 OFontPropertyExtractor::getInt16FontProperty(const OUString& _rPropName, const sal_Int16 _nDefault) + { + Any aValue; + if (getCheckFontProperty(_rPropName, aValue)) + return _nDefault; + + sal_Int32 nValue(_nDefault); + ::cppu::enum2int(nValue, aValue); + return static_cast<sal_Int16>(nValue); + } + + + sal_Int32 OFontPropertyExtractor::getInt32FontProperty(const OUString& _rPropName, const sal_Int32 _nDefault) + { + Any aValue; + if (getCheckFontProperty(_rPropName, aValue)) + return _nDefault; + + sal_Int32 nValue(_nDefault); + ::cppu::enum2int(nValue, aValue); + return nValue; + } + + + float OFontPropertyExtractor::getFloatFontProperty(const OUString& _rPropName, const float _nDefault) + { + Any aValue; + if (getCheckFontProperty(_rPropName, aValue)) + return _nDefault; + + return ::comphelper::getFloat(aValue); + } + + + void OFontPropertyExtractor::invalidateItem(const OUString& _rPropName, sal_uInt16 _nItemId, SfxItemSet& _rSet, bool _bForceInvalidation) + { + if ( _bForceInvalidation + || ( m_xPropStateAccess.is() + && (PropertyState_AMBIGUOUS_VALUE == m_xPropStateAccess->getPropertyState(_rPropName)) + ) + ) + _rSet.InvalidateItem(_nItemId); + } + + //= ControlCharacterDialog + ControlCharacterDialog::ControlCharacterDialog(weld::Window* pParent, const SfxItemSet& _rCoreSet) + : SfxTabDialogController(pParent, "modules/spropctrlr/ui/controlfontdialog.ui", "ControlFontDialog", &_rCoreSet) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + AddTabPage("font", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_CHAR_NAME), nullptr ); + AddTabPage("fonteffects", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_CHAR_EFFECTS), nullptr ); + } + + ControlCharacterDialog::~ControlCharacterDialog() + { + } + + void ControlCharacterDialog::translatePropertiesToItems(const Reference< XPropertySet >& _rxModel, SfxItemSet* _pSet) + { + OSL_ENSURE(_pSet && _rxModel.is(), "ControlCharacterDialog::translatePropertiesToItems: invalid arguments!"); + if (!_pSet || !_rxModel.is()) + return; + + try + { + OFontPropertyExtractor aPropExtractor(_rxModel); + + // some items, which may be in default state, have to be filled with non-void information + vcl::Font aDefaultVCLFont = Application::GetDefaultDevice()->GetSettings().GetStyleSettings().GetAppFont(); + css::awt::FontDescriptor aDefaultFont = VCLUnoHelper::CreateFontDescriptor(aDefaultVCLFont); + + // get the current properties + OUString aFontName = aPropExtractor.getStringFontProperty(PROPERTY_FONT_NAME, aDefaultFont.Name); + OUString aFontStyleName = aPropExtractor.getStringFontProperty(PROPERTY_FONT_STYLENAME, aDefaultFont.StyleName); + sal_Int16 nFontFamily = aPropExtractor.getInt16FontProperty(PROPERTY_FONT_FAMILY, aDefaultFont.Family); + sal_Int16 nFontCharset = aPropExtractor.getInt16FontProperty(PROPERTY_FONT_CHARSET, aDefaultFont.CharSet); + float nFontHeight = aPropExtractor.getFloatFontProperty(PROPERTY_FONT_HEIGHT, static_cast<float>(aDefaultFont.Height)); + float nFontWeight = aPropExtractor.getFloatFontProperty(PROPERTY_FONT_WEIGHT, aDefaultFont.Weight); + css::awt::FontSlant nFontSlant = static_cast<css::awt::FontSlant>(aPropExtractor.getInt16FontProperty(PROPERTY_FONT_SLANT, static_cast<sal_Int16>(aDefaultFont.Slant))); + sal_Int16 nFontLineStyle = aPropExtractor.getInt16FontProperty(PROPERTY_FONT_UNDERLINE, aDefaultFont.Underline); + sal_Int16 nFontStrikeout = aPropExtractor.getInt16FontProperty(PROPERTY_FONT_STRIKEOUT, aDefaultFont.Strikeout); + + sal_Int32 nTextLineColor = aPropExtractor.getInt32FontProperty(PROPERTY_TEXTLINECOLOR, sal_uInt32(COL_AUTO)); + sal_Int16 nFontRelief = aPropExtractor.getInt16FontProperty(PROPERTY_FONT_RELIEF, static_cast<sal_Int16>(aDefaultVCLFont.GetRelief())); + sal_Int16 nFontEmphasisMark = aPropExtractor.getInt16FontProperty(PROPERTY_FONT_EMPHASIS_MARK, static_cast<sal_uInt16>(aDefaultVCLFont.GetEmphasisMark())); + + Any aValue; + bool bWordLineMode = aPropExtractor.getCheckFontProperty(PROPERTY_WORDLINEMODE, aValue) ? aDefaultFont.WordLineMode : ::cppu::any2bool(aValue); + sal_Int32 nColor32 = aPropExtractor.getInt32FontProperty(PROPERTY_TEXTCOLOR, 0); + + // build SfxItems with the values + SvxFontItem aFontItem(static_cast<FontFamily>(nFontFamily), aFontName, aFontStyleName, PITCH_DONTKNOW, nFontCharset, CFID_FONT); + + nFontHeight = static_cast<float>(o3tl::convert(nFontHeight, o3tl::Length::pt, o3tl::Length::twip)); + + SvxFontHeightItem aSvxFontHeightItem(static_cast<sal_uInt32>(nFontHeight),100,CFID_HEIGHT); + + FontWeight eWeight=vcl::unohelper::ConvertFontWeight(nFontWeight); + FontItalic eItalic=vcl::unohelper::ConvertFontSlant(nFontSlant); + FontLineStyle eUnderline=static_cast<FontLineStyle>(nFontLineStyle); + FontStrikeout eStrikeout=static_cast<FontStrikeout>(nFontStrikeout); + + SvxWeightItem aWeightItem(eWeight,CFID_WEIGHT); + SvxPostureItem aPostureItem(eItalic,CFID_POSTURE); + + SvxCrossedOutItem aCrossedOutItem(eStrikeout,CFID_STRIKEOUT); + SvxWordLineModeItem aWordLineModeItem(bWordLineMode, CFID_WORDLINEMODE); + + SvxUnderlineItem aUnderlineItem(eUnderline,CFID_UNDERLINE); + aUnderlineItem.SetColor(Color(ColorTransparency, nTextLineColor)); + + SvxColorItem aSvxColorItem(Color(ColorTransparency, nColor32),CFID_CHARCOLOR); + SvxLanguageItem aLanguageItem(Application::GetSettings().GetUILanguageTag().getLanguageType(), CFID_LANGUAGE); + + // the 2 CJK props + SvxCharReliefItem aFontReliefItem(static_cast<FontRelief>(nFontRelief), CFID_RELIEF); + SvxEmphasisMarkItem aEmphasisMarkitem(static_cast<FontEmphasisMark>(nFontEmphasisMark), CFID_EMPHASIS); + + _pSet->Put(aFontItem); + _pSet->Put(aSvxFontHeightItem); + _pSet->Put(aWeightItem); + _pSet->Put(aPostureItem); + _pSet->Put(aLanguageItem); + _pSet->Put(aUnderlineItem); + _pSet->Put(aCrossedOutItem); + _pSet->Put(aWordLineModeItem); + _pSet->Put(aSvxColorItem); + _pSet->Put(aFontReliefItem); + _pSet->Put(aEmphasisMarkitem); + + aPropExtractor.invalidateItem(PROPERTY_FONT_NAME, CFID_FONT, *_pSet); + aPropExtractor.invalidateItem(PROPERTY_FONT_HEIGHT, CFID_HEIGHT, *_pSet); + aPropExtractor.invalidateItem(PROPERTY_FONT_WEIGHT, CFID_WEIGHT, *_pSet, css::awt::FontWeight::DONTKNOW == nFontWeight); + aPropExtractor.invalidateItem(PROPERTY_FONT_SLANT, CFID_POSTURE, *_pSet, css::awt::FontSlant_DONTKNOW == nFontSlant); + aPropExtractor.invalidateItem(PROPERTY_FONT_UNDERLINE, CFID_UNDERLINE, *_pSet, css::awt::FontUnderline::DONTKNOW == nFontLineStyle); + aPropExtractor.invalidateItem(PROPERTY_FONT_STRIKEOUT, CFID_STRIKEOUT, *_pSet, css::awt::FontStrikeout::DONTKNOW == nFontStrikeout); + aPropExtractor.invalidateItem(PROPERTY_WORDLINEMODE, CFID_WORDLINEMODE, *_pSet); + aPropExtractor.invalidateItem(PROPERTY_TEXTCOLOR, CFID_CHARCOLOR, *_pSet); + aPropExtractor.invalidateItem(PROPERTY_FONT_RELIEF, CFID_RELIEF, *_pSet); + aPropExtractor.invalidateItem(PROPERTY_FONT_EMPHASIS_MARK, CFID_EMPHASIS, *_pSet); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "ControlCharacterDialog::translatePropertiesToItems"); + } + + _pSet->DisableItem(SID_ATTR_CHAR_CJK_FONT); + _pSet->DisableItem(SID_ATTR_CHAR_CJK_FONTHEIGHT); + _pSet->DisableItem(SID_ATTR_CHAR_CJK_LANGUAGE); + _pSet->DisableItem(SID_ATTR_CHAR_CJK_POSTURE); + _pSet->DisableItem(SID_ATTR_CHAR_CJK_WEIGHT); + + _pSet->DisableItem(SID_ATTR_CHAR_CASEMAP); + _pSet->DisableItem(SID_ATTR_CHAR_CONTOUR); + _pSet->DisableItem(SID_ATTR_CHAR_SHADOWED); + } + + namespace + { + void lcl_pushBackPropertyValue( std::vector< NamedValue >& _out_properties, const OUString& _name, const Any& _value ) + { + _out_properties.push_back( NamedValue( _name, _value ) ); + } + } + + void ControlCharacterDialog::translateItemsToProperties( const SfxItemSet& _rSet, std::vector< NamedValue >& _out_properties ) + { + _out_properties.clear(); + + try + { + + // font name + SfxItemState eState = _rSet.GetItemState(CFID_FONT); + + if ( eState == SfxItemState::SET ) + { + const SvxFontItem& rFontItem = + static_cast<const SvxFontItem&>(_rSet.Get(CFID_FONT)); + + lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_NAME , Any(rFontItem.GetFamilyName())); + lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_STYLENAME, Any(rFontItem.GetStyleName())); + lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_FAMILY , Any(static_cast<sal_Int16>(rFontItem.GetFamily()))); + lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_CHARSET , Any(static_cast<sal_Int16>(rFontItem.GetCharSet()))); + } + + + // font height + eState = _rSet.GetItemState(CFID_HEIGHT); + + if ( eState == SfxItemState::SET ) + { + const SvxFontHeightItem& rSvxFontHeightItem = + static_cast<const SvxFontHeightItem&>(_rSet.Get(CFID_HEIGHT)); + + float nHeight = static_cast<float>(o3tl::convert(rSvxFontHeightItem.GetHeight(), o3tl::Length::twip, o3tl::Length::pt)); + lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_HEIGHT,Any(nHeight)); + + } + + + // font weight + eState = _rSet.GetItemState(CFID_WEIGHT); + + if ( eState == SfxItemState::SET ) + { + const SvxWeightItem& rWeightItem = + static_cast<const SvxWeightItem&>(_rSet.Get(CFID_WEIGHT)); + + float nWeight = vcl::unohelper::ConvertFontWeight(rWeightItem.GetWeight()); + lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_WEIGHT,Any(nWeight)); + } + + + // font slant + eState = _rSet.GetItemState(CFID_POSTURE); + + if ( eState == SfxItemState::SET ) + { + const SvxPostureItem& rPostureItem = + static_cast<const SvxPostureItem&>(_rSet.Get(CFID_POSTURE)); + + css::awt::FontSlant eSlant = vcl::unohelper::ConvertFontSlant(rPostureItem.GetPosture()); + lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_SLANT, Any(static_cast<sal_Int16>(eSlant))); + } + + + // font underline + eState = _rSet.GetItemState(CFID_UNDERLINE); + + if ( eState == SfxItemState::SET ) + { + const SvxUnderlineItem& rUnderlineItem = + static_cast<const SvxUnderlineItem&>(_rSet.Get(CFID_UNDERLINE)); + + sal_Int16 nUnderline = static_cast<sal_Int16>(rUnderlineItem.GetLineStyle()); + lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_UNDERLINE,Any(nUnderline)); + + // the text line color is transported in this item, too + Color nColor = rUnderlineItem.GetColor(); + + Any aUnoColor; + if (COL_AUTO != nColor) + aUnoColor <<= nColor; + + lcl_pushBackPropertyValue( _out_properties, PROPERTY_TEXTLINECOLOR, aUnoColor ); + } + + + // font strikeout + eState = _rSet.GetItemState(CFID_STRIKEOUT); + + if ( eState == SfxItemState::SET ) + { + const SvxCrossedOutItem& rCrossedOutItem = + static_cast<const SvxCrossedOutItem&>(_rSet.Get(CFID_STRIKEOUT)); + + sal_Int16 nStrikeout = static_cast<sal_Int16>(rCrossedOutItem.GetStrikeout()); + lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_STRIKEOUT,Any(nStrikeout)); + } + + + // font wordline mode + eState = _rSet.GetItemState(CFID_WORDLINEMODE); + + if ( eState == SfxItemState::SET ) + { + const SvxWordLineModeItem& rWordLineModeItem = + static_cast<const SvxWordLineModeItem&>(_rSet.Get(CFID_WORDLINEMODE)); + + lcl_pushBackPropertyValue( _out_properties, PROPERTY_WORDLINEMODE, css::uno::Any(rWordLineModeItem.GetValue())); + } + + + // text color + eState = _rSet.GetItemState(CFID_CHARCOLOR); + + if ( eState == SfxItemState::SET ) + { + const SvxColorItem& rColorItem = + static_cast<const SvxColorItem&>(_rSet.Get(CFID_CHARCOLOR)); + + Color nColor = rColorItem.GetValue(); + + Any aUnoColor; + if (COL_AUTO != nColor) + aUnoColor <<= nColor; + + lcl_pushBackPropertyValue( _out_properties, PROPERTY_TEXTCOLOR, aUnoColor ); + } + + + // font relief + eState = _rSet.GetItemState(CFID_RELIEF); + + if ( eState == SfxItemState::SET ) + { + const SvxCharReliefItem& rReliefItem = + static_cast<const SvxCharReliefItem&>(_rSet.Get(CFID_RELIEF)); + + lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_RELIEF, Any(static_cast<sal_Int16>(rReliefItem.GetValue())) ); + } + + + // font emphasis mark + eState = _rSet.GetItemState(CFID_EMPHASIS); + + if ( eState == SfxItemState::SET ) + { + const SvxEmphasisMarkItem& rEmphMarkItem = + static_cast<const SvxEmphasisMarkItem&>(_rSet.Get(CFID_EMPHASIS)); + + lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_EMPHASIS_MARK, Any(static_cast<sal_Int16>(rEmphMarkItem.GetEmphasisMark())) ); + } + } + catch (const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + void ControlCharacterDialog::translateItemsToProperties( const SfxItemSet& _rSet, const Reference< XPropertySet >& _rxModel) + { + OSL_ENSURE( _rxModel.is(), "ControlCharacterDialog::translateItemsToProperties: invalid arguments!" ); + if ( !_rxModel.is()) + return; + + std::vector< NamedValue > aPropertyValues; + translateItemsToProperties( _rSet, aPropertyValues ); + try + { + for ( const NamedValue& rNV : aPropertyValues ) + _rxModel->setPropertyValue( rNV.Name, rNV.Value ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + void ControlCharacterDialog::createItemSet(std::unique_ptr<SfxItemSet>& _rpSet, rtl::Reference<SfxItemPool>& _rpPool, std::vector<SfxPoolItem*>*& _rpDefaults) + { + // just to be sure... + _rpSet = nullptr; + _rpPool = nullptr; + _rpDefaults = nullptr; + + // create and initialize the defaults + _rpDefaults = new std::vector<SfxPoolItem*>(CFID_LAST_ITEM_ID - CFID_FIRST_ITEM_ID + 1); + + vcl::Font aDefaultVCLFont = Application::GetDefaultDevice()->GetSettings().GetStyleSettings().GetAppFont(); + + SfxPoolItem** pCounter = _rpDefaults->data(); // want to modify this without affecting the out param _rppDefaults + *pCounter++ = new SvxFontItem(aDefaultVCLFont.GetFamilyType(), aDefaultVCLFont.GetFamilyName(), aDefaultVCLFont.GetStyleName(), aDefaultVCLFont.GetPitch(), aDefaultVCLFont.GetCharSet(), CFID_FONT); + *pCounter++ = new SvxFontHeightItem(aDefaultVCLFont.GetFontHeight(), 100, CFID_HEIGHT); + *pCounter++ = new SvxWeightItem(aDefaultVCLFont.GetWeight(), CFID_WEIGHT); + *pCounter++ = new SvxPostureItem(aDefaultVCLFont.GetItalic(), CFID_POSTURE); + *pCounter++ = new SvxLanguageItem(Application::GetSettings().GetUILanguageTag().getLanguageType(), CFID_LANGUAGE); + *pCounter++ = new SvxUnderlineItem(aDefaultVCLFont.GetUnderline(), CFID_UNDERLINE); + *pCounter++ = new SvxCrossedOutItem(aDefaultVCLFont.GetStrikeout(), CFID_STRIKEOUT); + *pCounter++ = new SvxWordLineModeItem(aDefaultVCLFont.IsWordLineMode(), CFID_WORDLINEMODE); + *pCounter++ = new SvxColorItem(aDefaultVCLFont.GetColor(), CFID_CHARCOLOR); + *pCounter++ = new SvxCharReliefItem(aDefaultVCLFont.GetRelief(), CFID_RELIEF); + *pCounter++ = new SvxEmphasisMarkItem(aDefaultVCLFont.GetEmphasisMark(), CFID_EMPHASIS); + + *pCounter++ = new SvxFontItem(aDefaultVCLFont.GetFamilyType(), aDefaultVCLFont.GetFamilyName(), aDefaultVCLFont.GetStyleName(), aDefaultVCLFont.GetPitch(), aDefaultVCLFont.GetCharSet(), CFID_CJK_FONT); + *pCounter++ = new SvxFontHeightItem(aDefaultVCLFont.GetFontHeight(), 100, CFID_CJK_HEIGHT); + *pCounter++ = new SvxWeightItem(aDefaultVCLFont.GetWeight(), CFID_CJK_WEIGHT); + *pCounter++ = new SvxPostureItem(aDefaultVCLFont.GetItalic(), CFID_CJK_POSTURE); + *pCounter++ = new SvxLanguageItem(Application::GetSettings().GetUILanguageTag().getLanguageType(), CFID_CJK_LANGUAGE); + + *pCounter++ = new SvxCaseMapItem(SvxCaseMap::NotMapped, CFID_CASEMAP); + *pCounter++ = new SvxContourItem(false, CFID_CONTOUR); + *pCounter++ = new SvxShadowedItem(false, CFID_SHADOWED); + + *pCounter++ = new SvxFontListItem (new FontList(Application::GetDefaultDevice()), CFID_FONTLIST); + + // create the pool + static SfxItemInfo const aItemInfos[CFID_LAST_ITEM_ID - CFID_FIRST_ITEM_ID + 1] = + { + { SID_ATTR_CHAR_FONT, false }, + { SID_ATTR_CHAR_FONTHEIGHT, false }, + { SID_ATTR_CHAR_WEIGHT, false }, + { SID_ATTR_CHAR_POSTURE, false }, + { SID_ATTR_CHAR_LANGUAGE, false }, + { SID_ATTR_CHAR_UNDERLINE, false }, + { SID_ATTR_CHAR_STRIKEOUT, false }, + { SID_ATTR_CHAR_WORDLINEMODE, false }, + { SID_ATTR_CHAR_COLOR, false }, + { SID_ATTR_CHAR_RELIEF, false }, + { SID_ATTR_CHAR_EMPHASISMARK, false }, + { 0, false }, + { 0, false }, + { 0, false }, + { 0, false }, + { 0, false }, + { 0, false }, + { 0, false }, + { 0, false }, + { SID_ATTR_CHAR_FONTLIST, false } + }; + + _rpPool = new SfxItemPool("PCRControlFontItemPool", CFID_FIRST_ITEM_ID, CFID_LAST_ITEM_ID, + aItemInfos, _rpDefaults); + _rpPool->FreezeIdRanges(); + + // and, finally, the set + _rpSet.reset(new SfxItemSet(*_rpPool)); + } + + void ControlCharacterDialog::destroyItemSet(std::unique_ptr<SfxItemSet>& _rpSet, rtl::Reference<SfxItemPool>& _rpPool, std::vector<SfxPoolItem*>*& _rpDefaults) + { + // from the pool, get and remember the font list (needs to be deleted) + const SvxFontListItem& rFontListItem = static_cast<const SvxFontListItem&>(_rpPool->GetDefaultItem(CFID_FONTLIST)); + const FontList* pFontList = rFontListItem.GetFontList(); + + // _first_ delete the set (referring the pool) + _rpSet.reset(); + + // delete the pool + _rpPool->ReleaseDefaults(true); + // the "true" means delete the items, too + _rpPool = nullptr; + + // reset the defaults ptr + _rpDefaults = nullptr; + // no need to explicitly delete the defaults, this has been done by the ReleaseDefaults + + delete pFontList; + } + + void ControlCharacterDialog::PageCreated(const OString& rId, SfxTabPage& rPage) + { + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + if (rId == "font") + { + aSet.Put (static_cast<const SvxFontListItem&>(GetInputSetImpl()->Get(CFID_FONTLIST))); + aSet.Put (SfxUInt16Item(SID_DISABLE_CTL,DISABLE_HIDE_LANGUAGE)); + rPage.PageCreated(aSet); + } + } +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/fontdialog.hxx b/extensions/source/propctrlr/fontdialog.hxx new file mode 100644 index 000000000..eaba29db0 --- /dev/null +++ b/extensions/source/propctrlr/fontdialog.hxx @@ -0,0 +1,68 @@ +/* -*- 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 <sfx2/tabdlg.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/NamedValue.hpp> + + +namespace pcr +{ + + + //= ControlCharacterDialog + class ControlCharacterDialog : public SfxTabDialogController + { + public: + ControlCharacterDialog(weld::Window* pParent, const SfxItemSet& rCoreSet); + virtual ~ControlCharacterDialog() override; + + /// creates an item set to be used with this dialog + static void createItemSet(std::unique_ptr<SfxItemSet>& _rpSet, rtl::Reference<SfxItemPool>& _rpPool, std::vector<SfxPoolItem*>*& _rpDefaults); + + /// destroys an item previously created with <method>createItemSet</method> + static void destroyItemSet(std::unique_ptr<SfxItemSet>& _rpSet, rtl::Reference<SfxItemPool>& _rpPool, std::vector<SfxPoolItem*>*& _rpDefaults); + + /// fills the given item set with values obtained from the given property set + static void translatePropertiesToItems( + const css::uno::Reference< css::beans::XPropertySet >& _rxModel, + SfxItemSet* _pSet); + + /** fills the given property set with values obtained from the given item set + */ + static void translateItemsToProperties( + const SfxItemSet& _rSet, + const css::uno::Reference< css::beans::XPropertySet >& _rxModel); + + /** fills the given property set with values obtained from the given item set + */ + static void translateItemsToProperties( + const SfxItemSet& _rSet, + std::vector< css::beans::NamedValue >& _out_properties ); + + protected: + virtual void PageCreated(const OString& rId, SfxTabPage& rPage) override; + }; + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/fontitemids.hxx b/extensions/source/propctrlr/fontitemids.hxx new file mode 100644 index 000000000..c9b3a455f --- /dev/null +++ b/extensions/source/propctrlr/fontitemids.hxx @@ -0,0 +1,48 @@ +/* -*- 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 + +#define CFID_FONT 1 +#define CFID_HEIGHT 2 +#define CFID_WEIGHT 3 +#define CFID_POSTURE 4 +#define CFID_LANGUAGE 5 +#define CFID_UNDERLINE 6 +#define CFID_STRIKEOUT 7 +#define CFID_WORDLINEMODE 8 +#define CFID_CHARCOLOR 9 +#define CFID_RELIEF 10 +#define CFID_EMPHASIS 11 + +#define CFID_CJK_FONT 12 +#define CFID_CJK_HEIGHT 13 +#define CFID_CJK_WEIGHT 14 +#define CFID_CJK_POSTURE 15 +#define CFID_CJK_LANGUAGE 16 +#define CFID_CASEMAP 17 +#define CFID_CONTOUR 18 +#define CFID_SHADOWED 19 + +#define CFID_FONTLIST 20 + +#define CFID_FIRST_ITEM_ID CFID_FONT +#define CFID_LAST_ITEM_ID CFID_FONTLIST + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/formbrowsertools.cxx b/extensions/source/propctrlr/formbrowsertools.cxx new file mode 100644 index 000000000..b31aa0ce4 --- /dev/null +++ b/extensions/source/propctrlr/formbrowsertools.cxx @@ -0,0 +1,132 @@ +/* -*- 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 "formbrowsertools.hxx" +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <osl/diagnose.h> +#include <strings.hrc> +#include "modulepcr.hxx" +#include "formstrings.hxx" + + +namespace pcr +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::form; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + + + OUString GetUIHeadlineName(sal_Int16 nClassId, const Any& aUnoObj) + { + OUString sClassName; + switch (nClassId) + { + case FormComponentType::TEXTFIELD: + { + Reference< XInterface > xIFace; + aUnoObj >>= xIFace; + sClassName = PcrRes(RID_STR_PROPTITLE_EDIT); + if (xIFace.is()) + { // we have a chance to check if it's a formatted field model + Reference< XServiceInfo > xInfo(xIFace, UNO_QUERY); + if (xInfo.is() && (xInfo->supportsService(SERVICE_COMPONENT_FORMATTEDFIELD))) + sClassName = PcrRes(RID_STR_PROPTITLE_FORMATTED); + else if (!xInfo.is()) + { + // couldn't distinguish between formatted and edit with the service name, so try with the properties + Reference< XPropertySet > xProps(xIFace, UNO_QUERY); + if (xProps.is()) + { + Reference< XPropertySetInfo > xPropsInfo = xProps->getPropertySetInfo(); + if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(PROPERTY_FORMATSSUPPLIER)) + sClassName = PcrRes(RID_STR_PROPTITLE_FORMATTED); + } + } + } + } + break; + + case FormComponentType::COMMANDBUTTON: + sClassName = PcrRes(RID_STR_PROPTITLE_PUSHBUTTON); break; + case FormComponentType::RADIOBUTTON: + sClassName = PcrRes(RID_STR_PROPTITLE_RADIOBUTTON); break; + case FormComponentType::CHECKBOX: + sClassName = PcrRes(RID_STR_PROPTITLE_CHECKBOX); break; + case FormComponentType::LISTBOX: + sClassName = PcrRes(RID_STR_PROPTITLE_LISTBOX); break; + case FormComponentType::COMBOBOX: + sClassName = PcrRes(RID_STR_PROPTITLE_COMBOBOX); break; + case FormComponentType::GROUPBOX: + sClassName = PcrRes(RID_STR_PROPTITLE_GROUPBOX); break; + case FormComponentType::IMAGEBUTTON: + sClassName = PcrRes(RID_STR_PROPTITLE_IMAGEBUTTON); break; + case FormComponentType::FIXEDTEXT: + sClassName = PcrRes(RID_STR_PROPTITLE_FIXEDTEXT); break; + case FormComponentType::GRIDCONTROL: + sClassName = PcrRes(RID_STR_PROPTITLE_DBGRID); break; + case FormComponentType::FILECONTROL: + sClassName = PcrRes(RID_STR_PROPTITLE_FILECONTROL); break; + + case FormComponentType::DATEFIELD: + sClassName = PcrRes(RID_STR_PROPTITLE_DATEFIELD); break; + case FormComponentType::TIMEFIELD: + sClassName = PcrRes(RID_STR_PROPTITLE_TIMEFIELD); break; + case FormComponentType::NUMERICFIELD: + sClassName = PcrRes(RID_STR_PROPTITLE_NUMERICFIELD); break; + case FormComponentType::CURRENCYFIELD: + sClassName = PcrRes(RID_STR_PROPTITLE_CURRENCYFIELD); break; + case FormComponentType::PATTERNFIELD: + sClassName = PcrRes(RID_STR_PROPTITLE_PATTERNFIELD); break; + case FormComponentType::IMAGECONTROL: + sClassName = PcrRes(RID_STR_PROPTITLE_IMAGECONTROL); break; + case FormComponentType::HIDDENCONTROL: + sClassName = PcrRes(RID_STR_PROPTITLE_HIDDENCONTROL); break; + + case FormComponentType::CONTROL: + default: + sClassName = PcrRes(RID_STR_PROPTITLE_UNKNOWNCONTROL); break; + } + + return sClassName; + } + + + sal_Int16 classifyComponent( const Reference< XInterface >& _rxComponent ) + { + Reference< XPropertySet > xComponentProps( _rxComponent, UNO_QUERY_THROW ); + Reference< XPropertySetInfo > xPSI( xComponentProps->getPropertySetInfo(), UNO_SET_THROW ); + + sal_Int16 nControlType( FormComponentType::CONTROL ); + if ( xPSI->hasPropertyByName( PROPERTY_CLASSID ) ) + { + OSL_VERIFY( xComponentProps->getPropertyValue( PROPERTY_CLASSID ) >>= nControlType ); + } + return nControlType; + } + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/formbrowsertools.hxx b/extensions/source/propctrlr/formbrowsertools.hxx new file mode 100644 index 000000000..08d48747c --- /dev/null +++ b/extensions/source/propctrlr/formbrowsertools.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 <com/sun/star/uno/Any.hxx> +#include <com/sun/star/beans/Property.hpp> +#include <rtl/ustring.hxx> + +#include <set> + + +namespace pcr +{ + + + OUString GetUIHeadlineName(sal_Int16 _nClassId, const css::uno::Any& _rUnoObject); + sal_Int16 classifyComponent( const css::uno::Reference< css::uno::XInterface >& _rxComponent ); + + + struct FindPropertyByHandle + { + private: + sal_Int32 m_nId; + + public: + explicit FindPropertyByHandle( sal_Int32 _nId ) : m_nId ( _nId ) { } + bool operator()( const css::beans::Property& _rProp ) const + { + return m_nId == _rProp.Handle; + } + }; + + + struct FindPropertyByName + { + private: + OUString m_sName; + + public: + explicit FindPropertyByName( const OUString& _rName ) : m_sName( _rName ) { } + bool operator()( const css::beans::Property& _rProp ) const + { + return m_sName == _rProp.Name; + } + }; + + + struct PropertyLessByName + { + bool operator() (const css::beans::Property& _rLhs, const css::beans::Property& _rRhs) const + { + return _rLhs.Name < _rRhs.Name; + } + }; + + + struct TypeLessByName + { + bool operator() (const css::uno::Type& _rLhs, const css::uno::Type& _rRhs) const + { + return _rLhs.getTypeName() < _rRhs.getTypeName(); + } + }; + + + typedef std::set< css::beans::Property, PropertyLessByName > PropertyBag; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/formcomponenthandler.cxx b/extensions/source/propctrlr/formcomponenthandler.cxx new file mode 100644 index 000000000..d5f397662 --- /dev/null +++ b/extensions/source/propctrlr/formcomponenthandler.cxx @@ -0,0 +1,3305 @@ +/* -*- 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 "controltype.hxx" +#include "modulepcr.hxx" +#include <propctrlr.h> +#include <helpids.h> +#include "fontdialog.hxx" +#include "formcomponenthandler.hxx" +#include "formlinkdialog.hxx" +#include "formmetadata.hxx" +#include <strings.hrc> +#include <showhide.hrc> +#include <yesno.hrc> +#include "formstrings.hxx" +#include "handlerhelper.hxx" +#include "listselectiondlg.hxx" +#include "pcrcommon.hxx" +#include "selectlabeldialog.hxx" +#include "standardcontrol.hxx" +#include "taborder.hxx" +#include "usercontrol.hxx" + +#include <com/sun/star/lang/NullPointerException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/form/XForm.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/sdb/OrderDialog.hpp> +#include <com/sun/star/sdb/FilterDialog.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/DatabaseContext.hpp> +#include <com/sun/star/form/XGridColumnFactory.hpp> +#include <com/sun/star/sdb/SQLContext.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/form/ListSourceType.hpp> +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> +#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp> +#include <com/sun/star/awt/XTabControllerModel.hpp> +#include <com/sun/star/form/FormSubmitEncoding.hpp> +#include <com/sun/star/awt/VisualEffect.hpp> +#include <com/sun/star/form/FormButtonType.hpp> +#include <com/sun/star/inspection/PropertyControlType.hpp> +#include <com/sun/star/util/MeasureUnit.hpp> +#include <com/sun/star/inspection/XObjectInspectorUI.hpp> +#include <com/sun/star/inspection/PropertyLineElement.hpp> +#include <com/sun/star/resource/XStringResourceManager.hpp> +#include <com/sun/star/resource/MissingResourceException.hpp> +#include <com/sun/star/report/XReportDefinition.hpp> +#include <com/sun/star/graphic/GraphicObject.hpp> +#include <com/sun/star/text/WritingMode2.hpp> + +#include <comphelper/extract.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbconversion.hxx> +#include <connectivity/dbexception.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <sfx2/app.hxx> +#include <sfx2/basedlgs.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/filedlghelper.hxx> +#include <svl/ctloptions.hxx> +#include <svtools/colrdlg.hxx> +#include <svl/filenotation.hxx> +#include <svl/intitem.hxx> +#include <svl/itemset.hxx> +#include <svl/numformat.hxx> +#include <unotools/moduleoptions.hxx> +#include <svl/numuno.hxx> +#include <svl/urihelper.hxx> +#include <svx/dialogs.hrc> +#include <svx/numinf.hxx> +#include <svx/svxdlg.hxx> +#include <svx/svxids.hrc> +#include <vcl/graph.hxx> +#include <vcl/unohelp.hxx> +#include <tools/diagnose_ex.h> +#include <sal/macros.h> +#include <sal/log.hxx> + +#include <limits> +#include <memory> +#include <string_view> + +namespace pcr +{ + + + using namespace ::com::sun::star; + using namespace uno; + using namespace lang; + using namespace beans; + using namespace frame; + using namespace script; + using namespace form; + using namespace util; + using namespace awt; + using namespace sdb; + using namespace sdbc; + using namespace sdbcx; + using namespace report; + using namespace container; + using namespace ui::dialogs; + using namespace inspection; + using namespace ::dbtools; + + namespace WritingMode2 = ::com::sun::star::text::WritingMode2; + + + //= FormComponentPropertyHandler + +#define PROPERTY_ID_ROWSET 1 + + FormComponentPropertyHandler::FormComponentPropertyHandler( const Reference< XComponentContext >& _rxContext ) + :PropertyHandlerComponent( _rxContext ) + ,::comphelper::OPropertyContainer(PropertyHandlerComponent::rBHelper) + ,m_sDefaultValueString( PcrRes(RID_STR_STANDARD) ) + ,m_eComponentClass( eUnknown ) + ,m_bComponentIsSubForm( false ) + ,m_bHaveListSource( false ) + ,m_bHaveCommand( false ) + ,m_nClassId( 0 ) + { + registerProperty(PROPERTY_ROWSET,PROPERTY_ID_ROWSET,0,&m_xRowSet,cppu::UnoType<decltype(m_xRowSet)>::get()); + } + + + FormComponentPropertyHandler::~FormComponentPropertyHandler() + { + } + + IMPLEMENT_FORWARD_XINTERFACE2(FormComponentPropertyHandler,PropertyHandlerComponent,::comphelper::OPropertyContainer) + + OUString FormComponentPropertyHandler::getImplementationName( ) + { + return "com.sun.star.comp.extensions.FormComponentPropertyHandler"; + } + + + Sequence< OUString > FormComponentPropertyHandler::getSupportedServiceNames( ) + { + return { "com.sun.star.form.inspection.FormComponentPropertyHandler" }; + } + + namespace { + + // TODO: -> export from toolkit + struct LanguageDependentProp + { + const char* pPropName; + sal_Int32 nPropNameLength; + }; + + } + + const LanguageDependentProp aLanguageDependentProp[] = + { + { "Text", 4 }, + { "Label", 5 }, + { "Title", 5 }, + { "HelpText", 8 }, + { "CurrencySymbol", 14 }, + { "StringItemList", 14 }, + { nullptr, 0 } + }; + + namespace + { + bool lcl_isLanguageDependentProperty( const OUString& aName ) + { + bool bRet = false; + + const LanguageDependentProp* pLangDepProp = aLanguageDependentProp; + while( pLangDepProp->pPropName != nullptr ) + { + if( aName.equalsAsciiL( pLangDepProp->pPropName, pLangDepProp->nPropNameLength )) + { + bRet = true; + break; + } + pLangDepProp++; + } + return bRet; + } + + Reference< resource::XStringResourceResolver > lcl_getStringResourceResolverForProperty + ( const Reference< XPropertySet >& _xComponent, const OUString& _rPropertyName, + const Any& _rPropertyValue ) + { + Reference< resource::XStringResourceResolver > xRet; + const TypeClass eType = _rPropertyValue.getValueType().getTypeClass(); + if ( (eType == TypeClass_STRING || eType == TypeClass_SEQUENCE) && + lcl_isLanguageDependentProperty( _rPropertyName ) ) + { + Reference< resource::XStringResourceResolver > xStringResourceResolver; + try + { + xStringResourceResolver.set( _xComponent->getPropertyValue( "ResourceResolver" ),UNO_QUERY); + if( xStringResourceResolver.is() && + xStringResourceResolver->getLocales().hasElements() ) + { + xRet = xStringResourceResolver; + } + } + catch(const UnknownPropertyException&) + { + // nii + } + } + + return xRet; + } + } + + + Any FormComponentPropertyHandler::impl_getPropertyValue_throw( const OUString& _rPropertyName ) const + { + const PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + // tdf#117159 crash with chart in database report + if (!m_xComponent) + return Any(); + + Any aPropertyValue( m_xComponent->getPropertyValue( _rPropertyName ) ); + + Reference< resource::XStringResourceResolver > xStringResourceResolver + = lcl_getStringResourceResolverForProperty( m_xComponent, _rPropertyName, aPropertyValue ); + if( xStringResourceResolver.is() ) + { + TypeClass eType = aPropertyValue.getValueType().getTypeClass(); + if( eType == TypeClass_STRING ) + { + OUString aPropStr; + aPropertyValue >>= aPropStr; + if( aPropStr.getLength() > 1 ) + { + OUString aPureIdStr = aPropStr.copy( 1 ); + if( xStringResourceResolver->hasEntryForId( aPureIdStr ) ) + { + OUString aResourceStr = xStringResourceResolver->resolveString( aPureIdStr ); + aPropertyValue <<= aResourceStr; + } + } + } + // StringItemList? + else if( eType == TypeClass_SEQUENCE ) + { + Sequence< OUString > aStrings; + aPropertyValue >>= aStrings; + + std::vector< OUString > aResolvedStrings; + aResolvedStrings.reserve( aStrings.getLength() ); + try + { + for ( const OUString& rIdStr : std::as_const(aStrings) ) + { + OUString aPureIdStr = rIdStr.copy( 1 ); + if( xStringResourceResolver->hasEntryForId( aPureIdStr ) ) + aResolvedStrings.push_back(xStringResourceResolver->resolveString( aPureIdStr )); + else + aResolvedStrings.push_back(rIdStr); + } + } + catch( const resource::MissingResourceException & ) + {} + aPropertyValue <<= comphelper::containerToSequence(aResolvedStrings); + } + } + else + impl_normalizePropertyValue_nothrow( aPropertyValue, nPropId ); + + return aPropertyValue; + } + + Any SAL_CALL FormComponentPropertyHandler::getPropertyValue( const OUString& _rPropertyName ) + { + if( _rPropertyName == PROPERTY_ROWSET ) + return ::comphelper::OPropertyContainer::getPropertyValue( _rPropertyName ); + + ::osl::MutexGuard aGuard( m_aMutex ); + return impl_getPropertyValue_throw( _rPropertyName ); + } + + void SAL_CALL FormComponentPropertyHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue ) + { + if( _rPropertyName == PROPERTY_ROWSET ) + { + ::comphelper::OPropertyContainer::setPropertyValue( _rPropertyName, _rValue ); + return; + } + + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); // check if property is known by the handler + + Reference< graphic::XGraphicObject > xGrfObj; + if ( PROPERTY_ID_IMAGE_URL == nPropId && ( _rValue >>= xGrfObj ) ) + { + DBG_ASSERT( xGrfObj.is(), "FormComponentPropertyHandler::setPropertyValue() xGrfObj is invalid"); + m_xComponent->setPropertyValue(PROPERTY_GRAPHIC, uno::Any(xGrfObj->getGraphic())); + } + else if ( PROPERTY_ID_FONT == nPropId ) + { + // special handling, the value is a faked value we generated ourself in impl_executeFontDialog_nothrow + Sequence< NamedValue > aFontPropertyValues; + if( ! (_rValue >>= aFontPropertyValues) ) + SAL_WARN("extensions.propctrlr", "setPropertyValue: unable to get property " << PROPERTY_ID_FONT); + + for ( const NamedValue& fontPropertyValue : std::as_const(aFontPropertyValues) ) + m_xComponent->setPropertyValue( fontPropertyValue.Name, fontPropertyValue.Value ); + } + else + { + Any aValue = _rValue; + + Reference< resource::XStringResourceResolver > xStringResourceResolver + = lcl_getStringResourceResolverForProperty( m_xComponent, _rPropertyName, _rValue ); + if( xStringResourceResolver.is() ) + { + Reference< resource::XStringResourceManager > + xStringResourceManager( xStringResourceResolver, UNO_QUERY ); + if( xStringResourceManager.is() ) + { + Any aPropertyValue( m_xComponent->getPropertyValue( _rPropertyName ) ); + TypeClass eType = aPropertyValue.getValueType().getTypeClass(); + if( eType == TypeClass_STRING ) + { + OUString aPropStr; + aPropertyValue >>= aPropStr; + if( aPropStr.getLength() > 1 ) + { + OUString aPureIdStr = aPropStr.copy( 1 ); + OUString aValueStr; + _rValue >>= aValueStr; + xStringResourceManager->setString( aPureIdStr, aValueStr ); + aValue = aPropertyValue; // set value to force modified + } + } + // StringItemList? + else if( eType == TypeClass_SEQUENCE ) + { + static const char aDot[] = "."; + + // Put strings into resource using new ids + Sequence< OUString > aNewStrings; + _rValue >>= aNewStrings; + + const sal_Int32 nNewCount = aNewStrings.getLength(); + + // Create new Ids + std::unique_ptr<OUString[]> pNewPureIds(new OUString[nNewCount]); + Any aNameAny = m_xComponent->getPropertyValue(PROPERTY_NAME); + OUString sControlName; + aNameAny >>= sControlName; + OUString aIdStrBase = aDot + + sControlName + + aDot + + _rPropertyName; + sal_Int32 i; + for ( i = 0; i < nNewCount; ++i ) + { + sal_Int32 nUniqueId = xStringResourceManager->getUniqueNumericId(); + OUString aPureIdStr = OUString::number( nUniqueId ) + aIdStrBase; + pNewPureIds[i] = aPureIdStr; + // Force usage of next Unique Id + xStringResourceManager->setString( aPureIdStr, OUString() ); + } + + // Move strings to new Ids for all locales + const Sequence< Locale > aLocaleSeq = xStringResourceManager->getLocales(); + Sequence< OUString > aOldIdStrings; + aPropertyValue >>= aOldIdStrings; + try + { + const OUString* pOldIdStrings = aOldIdStrings.getConstArray(); + sal_Int32 nOldIdCount = aOldIdStrings.getLength(); + for ( i = 0; i < nNewCount; ++i ) + { + OUString aOldPureIdStr; + if( i < nOldIdCount ) + { + OUString aOldIdStr = pOldIdStrings[i]; + aOldPureIdStr = aOldIdStr.copy( 1 ); + } + OUString aNewPureIdStr = pNewPureIds[i]; + + for ( const Locale& rLocale : aLocaleSeq ) + { + OUString aResourceStr; + if( !aOldPureIdStr.isEmpty() ) + { + if( xStringResourceManager->hasEntryForIdAndLocale( aOldPureIdStr, rLocale ) ) + { + aResourceStr = xStringResourceManager-> + resolveStringForLocale( aOldPureIdStr, rLocale ); + } + } + xStringResourceManager->setStringForLocale( aNewPureIdStr, aResourceStr, rLocale ); + } + } + } + catch( const resource::MissingResourceException & ) + {} + + + // Set new strings for current locale and create + // new Id sequence as new property value + Sequence< OUString > aNewIdStrings; + aNewIdStrings.realloc( nNewCount ); + OUString* pNewIdStrings = aNewIdStrings.getArray(); + for ( i = 0; i < nNewCount; ++i ) + { + const OUString& aPureIdStr = pNewPureIds[i]; + const OUString& aStr = aNewStrings[i]; + xStringResourceManager->setString( aPureIdStr, aStr ); + + pNewIdStrings[i] = "&" + aPureIdStr; + } + aValue <<= aNewIdStrings; + + // Remove old ids from resource for all locales + for( const OUString& rIdStr : std::as_const(aOldIdStrings) ) + { + OUString aPureIdStr = rIdStr.copy( 1 ); + for ( const Locale& rLocale : aLocaleSeq ) + { + try + { + xStringResourceManager->removeIdForLocale( aPureIdStr, rLocale ); + } + catch( const resource::MissingResourceException & ) + {} + } + } + } + } + } + + m_xComponent->setPropertyValue( _rPropertyName, aValue ); + } + } + + Any SAL_CALL FormComponentPropertyHandler::convertToPropertyValue( const OUString& _rPropertyName, const Any& _rControlValue ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + Property aProperty( impl_getPropertyFromId_throw( nPropId ) ); + + Any aPropertyValue( _rControlValue ); + if ( !aPropertyValue.hasValue() ) + { + if ( ( aProperty.Attributes & PropertyAttribute::MAYBEVOID ) == 0 ) + // default construct an instance of the proper type + aPropertyValue = Any( nullptr, aProperty.Type ); + // nothing to do + return aPropertyValue; + } + + /// care for the special "default" string, translate it to VOID + if ( m_aPropertiesWithDefListEntry.find( _rPropertyName ) != m_aPropertiesWithDefListEntry.end() ) + { + // it's a control with a string list + OUString sStringValue; + if ( _rControlValue >>= sStringValue ) + { // note that ColorListBoxes might transfer values either as string or as css.util.Color, + // so this check here is important + if ( sStringValue == m_sDefaultValueString ) + return Any(); + } + } + + switch ( nPropId ) + { + case PROPERTY_ID_DATASOURCE: + { + OUString sControlValue; + if( ! (_rControlValue >>= sControlValue) ) + SAL_WARN("extensions.propctrlr", "convertToPropertyValue: unable to get property " << PROPERTY_ID_DATASOURCE); + + if ( !sControlValue.isEmpty() ) + { + Reference< XDatabaseContext > xDatabaseContext = sdb::DatabaseContext::create( m_xContext ); + if ( !xDatabaseContext->hasByName( sControlValue ) ) + { + ::svt::OFileNotation aTransformer(sControlValue); + aPropertyValue <<= aTransformer.get( ::svt::OFileNotation::N_URL ); + } + } + } + break; // case PROPERTY_ID_DATASOURCE + + case PROPERTY_ID_SHOW_POSITION: + case PROPERTY_ID_SHOW_NAVIGATION: + case PROPERTY_ID_SHOW_RECORDACTIONS: + case PROPERTY_ID_SHOW_FILTERSORT: + { + OUString sControlValue; + if( ! (_rControlValue >>= sControlValue) ) + SAL_WARN("extensions.propctrlr", "convertToControlValue: unable to get property for Show/Hide"); + + static_assert(SAL_N_ELEMENTS(RID_RSC_ENUM_SHOWHIDE) == 2, "FormComponentPropertyHandler::convertToPropertyValue: broken resource for Show/Hide!"); + bool bShow = sControlValue == PcrRes(RID_RSC_ENUM_SHOWHIDE[1]); + + aPropertyValue <<= bShow; + } + break; + + case PROPERTY_ID_TARGET_URL: + case PROPERTY_ID_IMAGE_URL: + { + OUString sControlValue; + if( ! (_rControlValue >>= sControlValue) ) + SAL_WARN("extensions.propctrlr", "convertToPropertyValue: unable to get property for URLs"); + // Don't convert a placeholder + if ( nPropId == PROPERTY_ID_IMAGE_URL && sControlValue == PcrRes(RID_EMBED_IMAGE_PLACEHOLDER) ) + aPropertyValue <<= sControlValue; + else + { + INetURLObject aDocURL( impl_getDocumentURL_nothrow() ); + aPropertyValue <<= URIHelper::SmartRel2Abs( aDocURL, sControlValue, Link<OUString *, bool>(), false, true ); + } + } + break; + + case PROPERTY_ID_DATEMIN: + case PROPERTY_ID_DATEMAX: + case PROPERTY_ID_DEFAULT_DATE: + case PROPERTY_ID_DATE: + { + util::Date aDate; + if( ! (_rControlValue >>= aDate) ) + SAL_WARN("extensions.propctrlr", "convertToControlValue: unable to get property for date"); + aPropertyValue <<= aDate; + } + break; + + case PROPERTY_ID_TIMEMIN: + case PROPERTY_ID_TIMEMAX: + case PROPERTY_ID_DEFAULT_TIME: + case PROPERTY_ID_TIME: + { + util::Time aTime; + if( ! (_rControlValue >>= aTime) ) + SAL_WARN("extensions.propctrlr", "convertToControlValue: unable to get property for time"); + aPropertyValue <<= aTime; + } + break; + + case PROPERTY_ID_WRITING_MODE: + { + aPropertyValue = PropertyHandlerComponent::convertToPropertyValue( _rPropertyName, _rControlValue ); + + sal_Int16 nNormalizedValue( 2 ); + if( ! (aPropertyValue >>= nNormalizedValue) ) + SAL_WARN("extensions.propctrlr", "convertToControlValue: unable to get property for " << PROPERTY_ID_WRITING_MODE); + + sal_Int16 nWritingMode = WritingMode2::CONTEXT; + switch ( nNormalizedValue ) + { + case 0: nWritingMode = WritingMode2::LR_TB; break; + case 1: nWritingMode = WritingMode2::RL_TB; break; + case 2: nWritingMode = WritingMode2::CONTEXT; break; + default: + OSL_FAIL( "FormComponentPropertyHandler::convertToPropertyValue: unexpected 'normalized value' for WritingMode!" ); + nWritingMode = WritingMode2::CONTEXT; + break; + } + + aPropertyValue <<= nWritingMode; + } + break; + + default: + aPropertyValue = PropertyHandlerComponent::convertToPropertyValue( _rPropertyName, _rControlValue ); + break; // default + + } // switch ( nPropId ) + + return aPropertyValue; + } + + Any SAL_CALL FormComponentPropertyHandler::convertToControlValue( const OUString& _rPropertyName, const Any& _rPropertyValue, const Type& _rControlValueType ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + sal_Int32 nPropId = m_pInfoService->getPropertyId( _rPropertyName ); + DBG_ASSERT( nPropId != -1, "FormComponentPropertyHandler::convertToPropertyValue: not one of my properties!!" ); + + impl_getPropertyFromId_throw( nPropId ); + + Any aControlValue( _rPropertyValue ); + if ( !aControlValue.hasValue() ) + { + // if the property is represented with a list box or color list box, we need to + // translate this into the string "Default" + if ( m_aPropertiesWithDefListEntry.find( _rPropertyName ) != m_aPropertiesWithDefListEntry.end() ) + aControlValue <<= m_sDefaultValueString; + + return aControlValue; + } + + switch ( nPropId ) + { + + case PROPERTY_ID_SHOW_POSITION: + case PROPERTY_ID_SHOW_NAVIGATION: + case PROPERTY_ID_SHOW_RECORDACTIONS: + case PROPERTY_ID_SHOW_FILTERSORT: + { + static_assert(SAL_N_ELEMENTS(RID_RSC_ENUM_SHOWHIDE) == 2, "FormComponentPropertyHandler::convertToPropertyValue: broken resource for Show/Hide!"); + OUString sControlValue = ::comphelper::getBOOL(_rPropertyValue) + ? PcrRes(RID_RSC_ENUM_SHOWHIDE[1]) + : PcrRes(RID_RSC_ENUM_SHOWHIDE[0]); + aControlValue <<= sControlValue; + } + break; + + + case PROPERTY_ID_DATASOURCE: + { + OSL_ENSURE( _rControlValueType.getTypeClass() == TypeClass_STRING, + "FormComponentPropertyHandler::convertToControlValue: wrong ControlValueType!" ); + + OUString sDataSource; + _rPropertyValue >>= sDataSource; + if ( !sDataSource.isEmpty() ) + { + ::svt::OFileNotation aTransformer( sDataSource ); + sDataSource = aTransformer.get( ::svt::OFileNotation::N_SYSTEM ); + } + aControlValue <<= sDataSource; + } + break; + + + case PROPERTY_ID_CONTROLLABEL: + { + OUString sControlValue; + + Reference< XPropertySet > xSet; + _rPropertyValue >>= xSet; + Reference< XPropertySetInfo > xPSI; + if ( xSet.is() ) + xPSI = xSet->getPropertySetInfo(); + if ( xPSI.is() && xPSI->hasPropertyByName( PROPERTY_LABEL ) ) + { + OUString sLabel; + if( ! (xSet->getPropertyValue( PROPERTY_LABEL) >>= sLabel) ) + SAL_WARN("extensions.propctrlr", "convertToPropertyValue: unable to get property " << PROPERTY_LABEL); + sControlValue = "<" + sLabel + ">"; + } + + aControlValue <<= sControlValue; + } + break; + + + case PROPERTY_ID_DATEMIN: + case PROPERTY_ID_DATEMAX: + case PROPERTY_ID_DEFAULT_DATE: + case PROPERTY_ID_DATE: + { + sal_Int32 nDate = 0; + if( ! (_rPropertyValue >>= nDate) ) + SAL_WARN("extensions.propctrlr", "convertToControlValue: unable to get property for dates"); + aControlValue <<= DBTypeConversion::toDate( nDate ); + } + break; + + case PROPERTY_ID_TIMEMIN: + case PROPERTY_ID_TIMEMAX: + case PROPERTY_ID_DEFAULT_TIME: + case PROPERTY_ID_TIME: + { + sal_Int64 nTime = 0; + if( ! (_rPropertyValue >>= nTime) ) + SAL_WARN("extensions.propctrlr", "convertToControlValue: unable to get property for times"); + aControlValue <<= DBTypeConversion::toTime( nTime ); + } + break; + + case PROPERTY_ID_WRITING_MODE: + { + sal_Int16 nWritingMode( WritingMode2::CONTEXT ); + if( ! (_rPropertyValue >>= nWritingMode) ) + SAL_WARN("extensions.propctrlr", "convertToControlValue: unable to get property " << PROPERTY_ID_WRITING_MODE); + + sal_Int16 nNormalized = 2; + switch ( nWritingMode ) + { + case WritingMode2::LR_TB: nNormalized = 0; break; + case WritingMode2::RL_TB: nNormalized = 1; break; + case WritingMode2::CONTEXT: nNormalized = 2; break; + default: + OSL_FAIL( "FormComponentPropertyHandler::convertToControlValue: unsupported API value for WritingMode!" ); + nNormalized = 2; + break; + } + + aControlValue = PropertyHandlerComponent::convertToControlValue( _rPropertyName, Any( nNormalized ), _rControlValueType ); + } + break; + + case PROPERTY_ID_FONT: + { + FontDescriptor aFont; + if( ! (_rPropertyValue >>= aFont) ) + SAL_WARN("extensions.propctrlr", "convertToControlValue: unable to get property " << PROPERTY_ID_FONT); + + OUStringBuffer displayName; + if ( aFont.Name.isEmpty() ) + { + displayName.append( PcrRes(RID_STR_FONT_DEFAULT) ); + } + else + { + // font name + displayName.append( aFont.Name ); + displayName.append( ", " ); + + // font style + ::FontWeight eWeight = vcl::unohelper::ConvertFontWeight( aFont.Weight ); + TranslateId pStyleResID = RID_STR_FONTSTYLE_REGULAR; + if ( aFont.Slant == FontSlant_ITALIC ) + { + if ( eWeight > WEIGHT_NORMAL ) + pStyleResID = RID_STR_FONTSTYLE_BOLD_ITALIC; + else + pStyleResID = RID_STR_FONTSTYLE_ITALIC; + } + else + { + if ( eWeight > WEIGHT_NORMAL ) + pStyleResID = RID_STR_FONTSTYLE_BOLD; + } + displayName.append(PcrRes(pStyleResID)); + + // font size + if ( aFont.Height ) + { + displayName.append( ", " ); + displayName.append( sal_Int32( aFont.Height ) ); + } + } + + aControlValue <<= displayName.makeStringAndClear(); + } + break; + + default: + aControlValue = PropertyHandlerComponent::convertToControlValue( _rPropertyName, _rPropertyValue, _rControlValueType ); + break; + + } // switch ( nPropId ) + + return aControlValue; + } + + PropertyState SAL_CALL FormComponentPropertyHandler::getPropertyState( const OUString& _rPropertyName ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_xPropertyState.is() ) + return m_xPropertyState->getPropertyState( _rPropertyName ); + return PropertyState_DIRECT_VALUE; + } + + void SAL_CALL FormComponentPropertyHandler::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyHandlerComponent::addPropertyChangeListener( _rxListener ); + if ( m_xComponent.is() ) + m_xComponent->addPropertyChangeListener( OUString(), _rxListener ); + } + + void SAL_CALL FormComponentPropertyHandler::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_xComponent.is() ) + m_xComponent->removePropertyChangeListener( OUString(), _rxListener ); + PropertyHandlerComponent::removePropertyChangeListener( _rxListener ); + } + + Sequence< Property > FormComponentPropertyHandler::doDescribeSupportedProperties() const + { + if ( !m_xComponentPropertyInfo.is() ) + return Sequence< Property >(); + + std::vector< Property > aProperties; + + Sequence< Property > aAllProperties( m_xComponentPropertyInfo->getProperties() ); + aProperties.reserve( aAllProperties.getLength() ); + + // filter the properties + PropertyId nPropId( 0 ); + OUString sDisplayName; + + for ( Property & rProperty : asNonConstRange(aAllProperties) ) + { + nPropId = m_pInfoService->getPropertyId( rProperty.Name ); + if ( nPropId == -1 ) + continue; + rProperty.Handle = nPropId; + + sDisplayName = m_pInfoService->getPropertyTranslation( nPropId ); + if ( sDisplayName.isEmpty() ) + continue; + + sal_uInt32 nPropertyUIFlags = m_pInfoService->getPropertyUIFlags( nPropId ); + bool bIsVisibleForForms = ( nPropertyUIFlags & PROP_FLAG_FORM_VISIBLE ) != 0; + bool bIsVisibleForDialogs = ( nPropertyUIFlags & PROP_FLAG_DIALOG_VISIBLE ) != 0; + + // depending on whether we're working for a form or a UNO dialog, some + // properties are not displayed + if ( ( m_eComponentClass == eFormControl && !bIsVisibleForForms ) + || ( m_eComponentClass == eDialogControl && !bIsVisibleForDialogs ) + ) + continue; + + // some generic sanity checks + if ( impl_shouldExcludeProperty_nothrow( rProperty ) ) + continue; + + switch ( nPropId ) + { + case PROPERTY_ID_BORDER: + case PROPERTY_ID_TABSTOP: + // BORDER and TABSTOP are normalized (see impl_normalizePropertyValue_nothrow) + // to not allow VOID values + rProperty.Attributes &= ~PropertyAttribute::MAYBEVOID; + break; + + case PROPERTY_ID_LISTSOURCE: + // no cursor source if no Base is installed. + if ( SvtModuleOptions().IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) ) + const_cast< FormComponentPropertyHandler* >( this )->m_bHaveListSource = true; + break; + + case PROPERTY_ID_COMMAND: + // no cursor source if no Base is installed. + if ( SvtModuleOptions().IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) ) + const_cast< FormComponentPropertyHandler* >( this )->m_bHaveCommand = true; + break; + } // switch ( nPropId ) + + aProperties.push_back( rProperty ); + } + + if ( aProperties.empty() ) + return Sequence< Property >(); + return comphelper::containerToSequence(aProperties); + } + + Sequence< OUString > SAL_CALL FormComponentPropertyHandler::getSupersededProperties( ) + { + return Sequence< OUString >( ); + } + + Sequence< OUString > SAL_CALL FormComponentPropertyHandler::getActuatingProperties( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + return + { + PROPERTY_DATASOURCE, + PROPERTY_COMMAND, + PROPERTY_COMMANDTYPE, + PROPERTY_LISTSOURCE, + PROPERTY_LISTSOURCETYPE, + PROPERTY_SUBMIT_ENCODING, + PROPERTY_REPEAT, + PROPERTY_TABSTOP, + PROPERTY_BORDER, + PROPERTY_CONTROLSOURCE, + PROPERTY_DROPDOWN, + PROPERTY_IMAGE_URL, + PROPERTY_TARGET_URL, + PROPERTY_STRINGITEMLIST, + PROPERTY_BUTTONTYPE, + PROPERTY_ESCAPE_PROCESSING, + PROPERTY_TRISTATE, + PROPERTY_DECIMAL_ACCURACY, + PROPERTY_SHOWTHOUSANDSEP, + PROPERTY_FORMATKEY, + PROPERTY_EMPTY_IS_NULL, + PROPERTY_TOGGLE + }; + } + + LineDescriptor SAL_CALL FormComponentPropertyHandler::describePropertyLine( const OUString& _rPropertyName, + const Reference< XPropertyControlFactory >& _rxControlFactory ) + { + if ( !_rxControlFactory.is() ) + throw NullPointerException(); + + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + Property aProperty( impl_getPropertyFromId_throw( nPropId ) ); + + + // for the MultiLine property, we have different UI translations depending on the control + // type + if ( nPropId == PROPERTY_ID_MULTILINE ) + { + if ( ( m_nClassId == FormComponentType::FIXEDTEXT ) + || ( m_nClassId == FormComponentType::COMMANDBUTTON ) + || ( m_nClassId == FormComponentType::RADIOBUTTON ) + || ( m_nClassId == FormComponentType::CHECKBOX ) + ) + nPropId = PROPERTY_ID_WORDBREAK; + } + + OUString sDisplayName = m_pInfoService->getPropertyTranslation( nPropId ); + if ( sDisplayName.isEmpty() ) + { + OSL_FAIL( "FormComponentPropertyHandler::describePropertyLine: did getSupportedProperties not work properly?" ); + throw UnknownPropertyException(); + } + + + LineDescriptor aDescriptor; + aDescriptor.HelpURL = HelpIdUrl::getHelpURL( m_pInfoService->getPropertyHelpId( nPropId ) ); + aDescriptor.DisplayName = sDisplayName; + + // for the moment, assume a text field + sal_Int16 nControlType = PropertyControlType::TextField; + bool bReadOnly = false; + aDescriptor.Control.clear(); + + + bool bNeedDefaultStringIfVoidAllowed = false; + + TypeClass eType = aProperty.Type.getTypeClass(); + + switch ( nPropId ) + { + case PROPERTY_ID_DEFAULT_SELECT_SEQ: + case PROPERTY_ID_SELECTEDITEMS: + aDescriptor.PrimaryButtonId = UID_PROP_DLG_SELECTION; + break; + + case PROPERTY_ID_FILTER: + aDescriptor.PrimaryButtonId = UID_PROP_DLG_FILTER; + break; + + case PROPERTY_ID_SORT: + aDescriptor.PrimaryButtonId = UID_PROP_DLG_ORDER; + break; + + case PROPERTY_ID_MASTERFIELDS: + case PROPERTY_ID_DETAILFIELDS: + nControlType = PropertyControlType::StringListField; + aDescriptor.PrimaryButtonId = UID_PROP_DLG_FORMLINKFIELDS; + break; + + case PROPERTY_ID_COMMAND: + aDescriptor.PrimaryButtonId = UID_PROP_DLG_SQLCOMMAND; + break; + + case PROPERTY_ID_TABINDEX: + { + Reference< XControlContainer > xControlContext( impl_getContextControlContainer_nothrow() ); + if ( xControlContext.is() ) + aDescriptor.PrimaryButtonId = UID_PROP_DLG_TABINDEX; + nControlType = PropertyControlType::NumericField; + }; + break; + + case PROPERTY_ID_FONT: + bReadOnly = true; + aDescriptor.PrimaryButtonId = UID_PROP_DLG_FONT_TYPE; + break; + + case PROPERTY_ID_TARGET_URL: + case PROPERTY_ID_IMAGE_URL: + { + std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/urlcontrol.ui", m_xContext)); + auto pURLBox = std::make_unique<SvtURLBox>(xBuilder->weld_combo_box("urlcontrol")); + rtl::Reference<OFileUrlControl> pControl = new OFileUrlControl(std::move(pURLBox), std::move(xBuilder), false); + pControl->SetModifyHandler(); + aDescriptor.Control = pControl; + + aDescriptor.PrimaryButtonId = PROPERTY_ID_TARGET_URL == nPropId + ? OUString(UID_PROP_DLG_ATTR_TARGET_URL) + : OUString(UID_PROP_DLG_IMAGE_URL); + break; + } + + case PROPERTY_ID_ECHO_CHAR: + nControlType = PropertyControlType::CharacterField; + break; + + case PROPERTY_ID_BACKGROUNDCOLOR: + case PROPERTY_ID_FILLCOLOR: + case PROPERTY_ID_SYMBOLCOLOR: + case PROPERTY_ID_BORDERCOLOR: + case PROPERTY_ID_GRIDLINECOLOR: + case PROPERTY_ID_HEADERBACKGROUNDCOLOR: + case PROPERTY_ID_HEADERTEXTCOLOR: + case PROPERTY_ID_ACTIVESELECTIONBACKGROUNDCOLOR: + case PROPERTY_ID_ACTIVESELECTIONTEXTCOLOR: + case PROPERTY_ID_INACTIVESELECTIONBACKGROUNDCOLOR: + case PROPERTY_ID_INACTIVESELECTIONTEXTCOLOR: + nControlType = PropertyControlType::ColorListBox; + + switch( nPropId ) + { + case PROPERTY_ID_BACKGROUNDCOLOR: + aDescriptor.PrimaryButtonId = UID_PROP_DLG_BACKGROUNDCOLOR; break; + case PROPERTY_ID_FILLCOLOR: + aDescriptor.PrimaryButtonId = UID_PROP_DLG_FILLCOLOR; break; + case PROPERTY_ID_SYMBOLCOLOR: + aDescriptor.PrimaryButtonId = UID_PROP_DLG_SYMBOLCOLOR; break; + case PROPERTY_ID_BORDERCOLOR: + aDescriptor.PrimaryButtonId = UID_PROP_DLG_BORDERCOLOR; break; + case PROPERTY_ID_GRIDLINECOLOR: + aDescriptor.PrimaryButtonId = HID_PROP_GRIDLINECOLOR; break; + case PROPERTY_ID_HEADERBACKGROUNDCOLOR: + aDescriptor.PrimaryButtonId = HID_PROP_HEADERBACKGROUNDCOLOR; break; + case PROPERTY_ID_HEADERTEXTCOLOR: + aDescriptor.PrimaryButtonId = HID_PROP_HEADERTEXTCOLOR; break; + case PROPERTY_ID_ACTIVESELECTIONBACKGROUNDCOLOR: + aDescriptor.PrimaryButtonId = HID_PROP_ACTIVESELECTIONBACKGROUNDCOLOR; break; + case PROPERTY_ID_ACTIVESELECTIONTEXTCOLOR: + aDescriptor.PrimaryButtonId = HID_PROP_ACTIVESELECTIONTEXTCOLOR; break; + case PROPERTY_ID_INACTIVESELECTIONBACKGROUNDCOLOR: + aDescriptor.PrimaryButtonId = HID_PROP_INACTIVESELECTIONBACKGROUNDCOLOR; break; + case PROPERTY_ID_INACTIVESELECTIONTEXTCOLOR: + aDescriptor.PrimaryButtonId = HID_PROP_INACTIVESELECTIONTEXTCOLOR; break; + } + break; + + case PROPERTY_ID_LABEL: + case PROPERTY_ID_URL: + nControlType = PropertyControlType::MultiLineTextField; + break; + + case PROPERTY_ID_DEFAULT_TEXT: + { + if (FormComponentType::FILECONTROL == m_nClassId) + nControlType = PropertyControlType::TextField; + else + nControlType = PropertyControlType::MultiLineTextField; + } + break; + + case PROPERTY_ID_TEXT: + if ( impl_componentHasProperty_throw( PROPERTY_MULTILINE ) ) + nControlType = PropertyControlType::MultiLineTextField; + break; + + case PROPERTY_ID_CONTROLLABEL: + bReadOnly = true; + aDescriptor.PrimaryButtonId = UID_PROP_DLG_CONTROLLABEL; + break; + + case PROPERTY_ID_FORMATKEY: + case PROPERTY_ID_EFFECTIVE_MIN: + case PROPERTY_ID_EFFECTIVE_MAX: + case PROPERTY_ID_EFFECTIVE_DEFAULT: + case PROPERTY_ID_EFFECTIVE_VALUE: + { + // and the supplier is really available + Reference< XNumberFormatsSupplier > xSupplier; + m_xComponent->getPropertyValue( PROPERTY_FORMATSSUPPLIER ) >>= xSupplier; + if (xSupplier.is()) + { + Reference< XUnoTunnel > xTunnel(xSupplier,UNO_QUERY); + DBG_ASSERT(xTunnel.is(), "FormComponentPropertyHandler::describePropertyLine : xTunnel is invalid!"); + if (auto pSupplier = comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>(xTunnel)) + { + bool bIsFormatKey = (PROPERTY_ID_FORMATKEY == nPropId); + + bReadOnly = bIsFormatKey; + + if ( bIsFormatKey ) + { + std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/formattedsample.ui", m_xContext)); + auto pContainer = xBuilder->weld_container("formattedsample"); + rtl::Reference<OFormatSampleControl> pControl = new OFormatSampleControl(std::move(pContainer), std::move(xBuilder), false); + pControl->SetModifyHandler(); + + pControl->SetFormatSupplier(pSupplier); + + aDescriptor.Control = pControl; + + aDescriptor.PrimaryButtonId = UID_PROP_DLG_NUMBER_FORMAT; + } + else + { + std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/formattedcontrol.ui", m_xContext)); + auto pSpinButton = xBuilder->weld_formatted_spin_button("formattedcontrol"); + rtl::Reference<OFormattedNumericControl> pControl = new OFormattedNumericControl(std::move(pSpinButton), std::move(xBuilder), false); + pControl->SetModifyHandler(); + + FormatDescription aDesc; + aDesc.pSupplier = pSupplier; + Any aFormatKeyValue = m_xComponent->getPropertyValue(PROPERTY_FORMATKEY); + if ( !( aFormatKeyValue >>= aDesc.nKey ) ) + aDesc.nKey = 0; + + pControl->SetFormatDescription( aDesc ); + + aDescriptor.Control = pControl; + } + } + } + } + break; + + case PROPERTY_ID_DATEMIN: + case PROPERTY_ID_DATEMAX: + case PROPERTY_ID_DEFAULT_DATE: + case PROPERTY_ID_DATE: + nControlType = PropertyControlType::DateField; + break; + + case PROPERTY_ID_TIMEMIN: + case PROPERTY_ID_TIMEMAX: + case PROPERTY_ID_DEFAULT_TIME: + case PROPERTY_ID_TIME: + nControlType = PropertyControlType::TimeField; + break; + + case PROPERTY_ID_VALUEMIN: + case PROPERTY_ID_VALUEMAX: + case PROPERTY_ID_DEFAULT_VALUE: + case PROPERTY_ID_VALUE: + { + std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/formattedcontrol.ui", m_xContext)); + auto pSpinButton = xBuilder->weld_formatted_spin_button("formattedcontrol"); + rtl::Reference<OFormattedNumericControl> pControl = new OFormattedNumericControl(std::move(pSpinButton), std::move(xBuilder), false); + pControl->SetModifyHandler(); + aDescriptor.Control = pControl; + + // we don't set a formatter so the control uses a default (which uses the application + // language and a default numeric format) + // but we set the decimal digits + pControl->SetDecimalDigits( + ::comphelper::getINT16( m_xComponent->getPropertyValue( PROPERTY_DECIMAL_ACCURACY ) ) + ); + + // and the default value for the property + try + { + if (m_xPropertyState.is() && ((PROPERTY_ID_VALUEMIN == nPropId) || (PROPERTY_ID_VALUEMAX == nPropId))) + { + double nDefault = 0; + if ( m_xPropertyState->getPropertyDefault( aProperty.Name ) >>= nDefault ) + pControl->SetDefaultValue(nDefault); + } + } + catch (const Exception&) + { + // just ignore it + } + + break; + } + + default: + if ( TypeClass_BYTE <= eType && eType <= TypeClass_DOUBLE ) + { + sal_Int16 nDigits = 0; + sal_Int16 nValueUnit = -1; + sal_Int16 nDisplayUnit = -1; + if ( m_eComponentClass == eFormControl ) + { + if ( ( nPropId == PROPERTY_ID_WIDTH ) + || ( nPropId == PROPERTY_ID_ROWHEIGHT ) + || ( nPropId == PROPERTY_ID_HEIGHT ) + ) + { + nValueUnit = MeasureUnit::MM_10TH; + nDisplayUnit = impl_getDocumentMeasurementUnit_throw(); + nDigits = 2; + } + } + + Optional< double > aValueNotPresent( false, 0 ); + aDescriptor.Control = PropertyHandlerHelper::createNumericControl( + _rxControlFactory, nDigits, aValueNotPresent, aValueNotPresent ); + + Reference< XNumericControl > xNumericControl( aDescriptor.Control, UNO_QUERY_THROW ); + if ( nValueUnit != -1 ) + xNumericControl->setValueUnit( nValueUnit ); + if ( nDisplayUnit != -1 ) + xNumericControl->setDisplayUnit( nDisplayUnit ); + } + break; + } + + if ( eType == TypeClass_SEQUENCE ) + nControlType = PropertyControlType::StringListField; + + // boolean values + if ( eType == TypeClass_BOOLEAN ) + { + if ( ( nPropId == PROPERTY_ID_SHOW_POSITION ) + || ( nPropId == PROPERTY_ID_SHOW_NAVIGATION ) + || ( nPropId == PROPERTY_ID_SHOW_RECORDACTIONS ) + || ( nPropId == PROPERTY_ID_SHOW_FILTERSORT ) + ) + { + aDescriptor.Control = PropertyHandlerHelper::createListBoxControl(_rxControlFactory, RID_RSC_ENUM_SHOWHIDE, SAL_N_ELEMENTS(RID_RSC_ENUM_SHOWHIDE), false); + } + else + aDescriptor.Control = PropertyHandlerHelper::createListBoxControl(_rxControlFactory, RID_RSC_ENUM_YESNO, SAL_N_ELEMENTS(RID_RSC_ENUM_YESNO), false); + bNeedDefaultStringIfVoidAllowed = true; + } + + + // enum properties + sal_uInt32 nPropertyUIFlags = m_pInfoService->getPropertyUIFlags( nPropId ); + bool bIsEnumProperty = ( nPropertyUIFlags & PROP_FLAG_ENUM ) != 0; + if ( bIsEnumProperty || ( PROPERTY_ID_TARGET_FRAME == nPropId ) ) + { + std::vector< OUString > aEnumValues = m_pInfoService->getPropertyEnumRepresentations( nPropId ); + std::vector< OUString >::const_iterator pStart = aEnumValues.begin(); + std::vector< OUString >::const_iterator pEnd = aEnumValues.end(); + + // for a checkbox: if "ambiguous" is not allowed, remove this from the sequence + if ( ( PROPERTY_ID_DEFAULT_STATE == nPropId ) + || ( PROPERTY_ID_STATE == nPropId ) + ) + { + if ( impl_componentHasProperty_throw( PROPERTY_TRISTATE ) ) + { + if ( !::comphelper::getBOOL( m_xComponent->getPropertyValue( PROPERTY_TRISTATE ) ) ) + { // remove the last sequence element + if ( pEnd > pStart ) + --pEnd; + } + } + else + --pEnd; + } + + if ( PROPERTY_ID_LISTSOURCETYPE == nPropId ) + if ( FormComponentType::COMBOBOX == m_nClassId ) + // remove the first sequence element -> value list not possible for combo boxes + ++pStart; + + // copy the sequence + std::vector< OUString > aListEntries( pEnd - pStart ); + std::copy( pStart, pEnd, aListEntries.begin() ); + + // create the control + if ( PROPERTY_ID_TARGET_FRAME == nPropId ) + aDescriptor.Control = PropertyHandlerHelper::createComboBoxControl( _rxControlFactory, std::move(aListEntries), false ); + else + { + aDescriptor.Control = PropertyHandlerHelper::createListBoxControl( _rxControlFactory, std::move(aListEntries), false, false ); + bNeedDefaultStringIfVoidAllowed = true; + } + } + + + switch( nPropId ) + { + case PROPERTY_ID_REPEAT_DELAY: + { + std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/numericfield.ui", m_xContext)); + auto pSpinButton = xBuilder->weld_metric_spin_button("numericfield", FieldUnit::MILLISECOND); + rtl::Reference<ONumericControl> pControl = new ONumericControl(std::move(pSpinButton), std::move(xBuilder), bReadOnly); + pControl->SetModifyHandler(); + pControl->setMinValue( Optional< double >( true, 0 ) ); + pControl->setMaxValue( Optional< double >( true, std::numeric_limits< double >::max() ) ); + aDescriptor.Control = pControl; + } + break; + + case PROPERTY_ID_TABINDEX: + case PROPERTY_ID_BOUNDCOLUMN: + case PROPERTY_ID_VISIBLESIZE: + case PROPERTY_ID_MAXTEXTLEN: + case PROPERTY_ID_LINEINCREMENT: + case PROPERTY_ID_BLOCKINCREMENT: + case PROPERTY_ID_SPININCREMENT: + { + Optional< double > aMinValue( true, 0 ); + Optional< double > aMaxValue( true, 0x7FFFFFFF ); + + if ( nPropId == PROPERTY_ID_MAXTEXTLEN || nPropId == PROPERTY_ID_BOUNDCOLUMN ) + aMinValue.Value = -1; + else if ( nPropId == PROPERTY_ID_VISIBLESIZE ) + aMinValue.Value = 1; + else + aMinValue.Value = 0; + + aDescriptor.Control = PropertyHandlerHelper::createNumericControl( + _rxControlFactory, 0, aMinValue, aMaxValue ); + } + break; + + case PROPERTY_ID_DECIMAL_ACCURACY: + { + Optional< double > aMinValue( true, 0 ); + Optional< double > aMaxValue( true, 20 ); + + aDescriptor.Control = PropertyHandlerHelper::createNumericControl( + _rxControlFactory, 0, aMinValue, aMaxValue ); + } + break; + + + // DataSource + case PROPERTY_ID_DATASOURCE: + { + aDescriptor.PrimaryButtonId = UID_PROP_DLG_ATTR_DATASOURCE; + + std::vector< OUString > aListEntries; + + Reference< XDatabaseContext > xDatabaseContext = sdb::DatabaseContext::create( m_xContext ); + const Sequence< OUString > aDatasources = xDatabaseContext->getElementNames(); + aListEntries.resize( aDatasources.getLength() ); + std::copy( aDatasources.begin(), aDatasources.end(), aListEntries.begin() ); + aDescriptor.Control = PropertyHandlerHelper::createComboBoxControl( + _rxControlFactory, std::move(aListEntries), true ); + } + break; + + case PROPERTY_ID_CONTROLSOURCE: + { + std::vector< OUString > aFieldNames; + impl_initFieldList_nothrow( aFieldNames ); + aDescriptor.Control = PropertyHandlerHelper::createComboBoxControl( + _rxControlFactory, std::move(aFieldNames), false ); + } + break; + + case PROPERTY_ID_COMMAND: + impl_describeCursorSource_nothrow( aDescriptor, _rxControlFactory ); + break; + + case PROPERTY_ID_LISTSOURCE: + impl_describeListSourceUI_throw( aDescriptor, _rxControlFactory ); + break; + } + + if ( !aDescriptor.Control.is() ) + aDescriptor.Control = _rxControlFactory->createPropertyControl( nControlType, bReadOnly ); + + if ( ( aProperty.Attributes & PropertyAttribute::MAYBEVOID ) != 0 ) + { + // insert the string "Default" string, if necessary + if (bNeedDefaultStringIfVoidAllowed) + { + Reference< XStringListControl > xStringList( aDescriptor.Control, UNO_QUERY_THROW ); + xStringList->prependListEntry( m_sDefaultValueString ); + m_aPropertiesWithDefListEntry.insert( _rPropertyName ); + } + } + + if ( !aDescriptor.PrimaryButtonId.isEmpty() ) + aDescriptor.HasPrimaryButton = true; + if ( !aDescriptor.SecondaryButtonId.isEmpty() ) + aDescriptor.HasSecondaryButton = true; + + bool bIsDataProperty = ( nPropertyUIFlags & PROP_FLAG_DATA_PROPERTY ) != 0; + aDescriptor.Category = bIsDataProperty ? std::u16string_view(u"Data") : std::u16string_view(u"General"); + return aDescriptor; + } + + InteractiveSelectionResult SAL_CALL FormComponentPropertyHandler::onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool /*_bPrimary*/, Any& _rData, const Reference< XObjectInspectorUI >& _rxInspectorUI ) + { + if ( !_rxInspectorUI.is() ) + throw NullPointerException(); + + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + InteractiveSelectionResult eResult = InteractiveSelectionResult_Cancelled; + switch ( nPropId ) + { + case PROPERTY_ID_DEFAULT_SELECT_SEQ: + case PROPERTY_ID_SELECTEDITEMS: + if ( impl_dialogListSelection_nothrow( _rPropertyName, aGuard ) ) + eResult = InteractiveSelectionResult_Success; + break; + + case PROPERTY_ID_FILTER: + case PROPERTY_ID_SORT: + { + OUString sClause; + if ( impl_dialogFilterOrSort_nothrow( PROPERTY_ID_FILTER == nPropId, sClause, aGuard ) ) + { + _rData <<= sClause; + eResult = InteractiveSelectionResult_ObtainedValue; + } + } + break; + + case PROPERTY_ID_MASTERFIELDS: + case PROPERTY_ID_DETAILFIELDS: + if ( impl_dialogLinkedFormFields_nothrow( aGuard ) ) + eResult = InteractiveSelectionResult_Success; + break; + + case PROPERTY_ID_FORMATKEY: + if ( impl_dialogFormatting_nothrow( _rData, aGuard ) ) + eResult = InteractiveSelectionResult_ObtainedValue; + break; + + case PROPERTY_ID_IMAGE_URL: + if ( impl_browseForImage_nothrow( _rData, aGuard ) ) + eResult = InteractiveSelectionResult_ObtainedValue; + break; + + case PROPERTY_ID_TARGET_URL: + if ( impl_browseForTargetURL_nothrow( _rData, aGuard ) ) + eResult = InteractiveSelectionResult_ObtainedValue; + break; + + case PROPERTY_ID_FONT: + if ( impl_executeFontDialog_nothrow( _rData, aGuard ) ) + eResult = InteractiveSelectionResult_ObtainedValue; + break; + + case PROPERTY_ID_DATASOURCE: + if ( impl_browseForDatabaseDocument_throw( _rData, aGuard ) ) + eResult = InteractiveSelectionResult_ObtainedValue; + break; + + case PROPERTY_ID_BACKGROUNDCOLOR: + case PROPERTY_ID_FILLCOLOR: + case PROPERTY_ID_SYMBOLCOLOR: + case PROPERTY_ID_BORDERCOLOR: + case PROPERTY_ID_GRIDLINECOLOR: + case PROPERTY_ID_HEADERBACKGROUNDCOLOR: + case PROPERTY_ID_HEADERTEXTCOLOR: + case PROPERTY_ID_ACTIVESELECTIONBACKGROUNDCOLOR: + case PROPERTY_ID_ACTIVESELECTIONTEXTCOLOR: + case PROPERTY_ID_INACTIVESELECTIONBACKGROUNDCOLOR: + case PROPERTY_ID_INACTIVESELECTIONTEXTCOLOR: + if ( impl_dialogColorChooser_throw( nPropId, _rData, aGuard ) ) + eResult = InteractiveSelectionResult_ObtainedValue; + break; + + case PROPERTY_ID_CONTROLLABEL: + if ( impl_dialogChooseLabelControl_nothrow( _rData, aGuard ) ) + eResult = InteractiveSelectionResult_ObtainedValue; + break; + + case PROPERTY_ID_TABINDEX: + if ( impl_dialogChangeTabOrder_nothrow( aGuard ) ) + eResult = InteractiveSelectionResult_Success; + break; + + case PROPERTY_ID_COMMAND: + case PROPERTY_ID_LISTSOURCE: + if ( impl_doDesignSQLCommand_nothrow( _rxInspectorUI, nPropId ) ) + eResult = InteractiveSelectionResult_Pending; + break; + default: + OSL_FAIL( "FormComponentPropertyHandler::onInteractivePropertySelection: request for a property which does not have dedicated UI!" ); + break; + } + return eResult; + } + + namespace + { + void lcl_rebuildAndResetCommand( const Reference< XObjectInspectorUI >& _rxInspectorUI, const Reference< XPropertyHandler >& _rxHandler ) + { + OSL_PRECOND( _rxInspectorUI.is(), "lcl_rebuildAndResetCommand: invalid BrowserUI!" ); + OSL_PRECOND( _rxHandler.is(), "lcl_rebuildAndResetCommand: invalid handler!" ); + _rxInspectorUI->rebuildPropertyUI( PROPERTY_COMMAND ); + _rxHandler->setPropertyValue( PROPERTY_COMMAND, Any( OUString() ) ); + } + } + + void SAL_CALL FormComponentPropertyHandler::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) + { + if ( !_rxInspectorUI.is() ) + throw NullPointerException(); + + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nActuatingPropId( impl_getPropertyId_nothrow( _rActuatingPropertyName ) ); + + std::vector< PropertyId > aDependentProperties; + + switch ( nActuatingPropId ) + { + // ----- EscapeProcessing ----- + case PROPERTY_ID_ESCAPE_PROCESSING: + aDependentProperties.push_back( PROPERTY_ID_FILTER ); + aDependentProperties.push_back( PROPERTY_ID_SORT ); + break; // case PROPERTY_ID_ESCAPE_PROCESSING + + // ----- CommandType ----- + case PROPERTY_ID_COMMANDTYPE: + // available commands (tables or queries) might have changed + if ( !_bFirstTimeInit && m_bHaveCommand ) + lcl_rebuildAndResetCommand( _rxInspectorUI, this ); + aDependentProperties.push_back( PROPERTY_ID_COMMAND ); + break; // case PROPERTY_ID_COMMANDTYPE + + // ----- DataSourceName ----- + case PROPERTY_ID_DATASOURCE: + // reset the connection, now that we have a new data source + m_xRowSetConnection.clear(); + + // available list source values (tables or queries) might have changed + if ( !_bFirstTimeInit && m_bHaveListSource ) + _rxInspectorUI->rebuildPropertyUI( PROPERTY_LISTSOURCE ); + + // available commands (tables or queries) might have changed + if ( !_bFirstTimeInit && m_bHaveCommand ) + lcl_rebuildAndResetCommand( _rxInspectorUI, this ); + + // Command also depends on DataSource + aDependentProperties.push_back( PROPERTY_ID_COMMAND ); + [[fallthrough]]; + + // ----- Command ----- + case PROPERTY_ID_COMMAND: + aDependentProperties.push_back( PROPERTY_ID_FILTER ); + aDependentProperties.push_back( PROPERTY_ID_SORT ); + if ( m_bComponentIsSubForm ) + aDependentProperties.push_back( PROPERTY_ID_DETAILFIELDS ); + break; + + // ----- ListSourceType ----- + case PROPERTY_ID_LISTSOURCETYPE: + if ( !_bFirstTimeInit && m_bHaveListSource ) + // available list source values (tables or queries) might have changed + _rxInspectorUI->rebuildPropertyUI( PROPERTY_LISTSOURCE ); + aDependentProperties.push_back( PROPERTY_ID_STRINGITEMLIST ); + aDependentProperties.push_back( PROPERTY_ID_TYPEDITEMLIST ); + aDependentProperties.push_back( PROPERTY_ID_BOUNDCOLUMN ); + [[fallthrough]]; + + // ----- StringItemList ----- + case PROPERTY_ID_STRINGITEMLIST: + aDependentProperties.push_back( PROPERTY_ID_TYPEDITEMLIST ); + aDependentProperties.push_back( PROPERTY_ID_SELECTEDITEMS ); + aDependentProperties.push_back( PROPERTY_ID_DEFAULT_SELECT_SEQ ); + break; + + // ----- ListSource ----- + case PROPERTY_ID_LISTSOURCE: + aDependentProperties.push_back( PROPERTY_ID_STRINGITEMLIST ); + aDependentProperties.push_back( PROPERTY_ID_TYPEDITEMLIST ); + break; + + // ----- DataField ----- + case PROPERTY_ID_CONTROLSOURCE: + { + OUString sControlSource; + _rNewValue >>= sControlSource; + if ( impl_componentHasProperty_throw( PROPERTY_FILTERPROPOSAL ) ) + _rxInspectorUI->enablePropertyUI( PROPERTY_FILTERPROPOSAL, !sControlSource.isEmpty() ); + if ( impl_componentHasProperty_throw( PROPERTY_EMPTY_IS_NULL ) ) + _rxInspectorUI->enablePropertyUI( PROPERTY_EMPTY_IS_NULL, !sControlSource.isEmpty() ); + + aDependentProperties.push_back( PROPERTY_ID_BOUNDCOLUMN ); + aDependentProperties.push_back( PROPERTY_ID_SCALEIMAGE ); + aDependentProperties.push_back( PROPERTY_ID_SCALE_MODE ); + aDependentProperties.push_back( PROPERTY_ID_INPUT_REQUIRED ); + } + break; + + case PROPERTY_ID_EMPTY_IS_NULL: + aDependentProperties.push_back( PROPERTY_ID_INPUT_REQUIRED ); + break; + + // ----- SubmitEncoding ----- + case PROPERTY_ID_SUBMIT_ENCODING: + { + FormSubmitEncoding eEncoding = FormSubmitEncoding_URL; + if( ! (_rNewValue >>= eEncoding) ) + SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_SUBMIT_ENCODING); + _rxInspectorUI->enablePropertyUI( PROPERTY_SUBMIT_METHOD, eEncoding == FormSubmitEncoding_URL ); + } + break; + + // ----- Repeat ----- + case PROPERTY_ID_REPEAT: + { + bool bIsRepeating = false; + if( ! (_rNewValue >>= bIsRepeating) ) + SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_REPEAT); + _rxInspectorUI->enablePropertyUI( PROPERTY_REPEAT_DELAY, bIsRepeating ); + } + break; + + // ----- TabStop ----- + case PROPERTY_ID_TABSTOP: + { + if ( !impl_componentHasProperty_throw( PROPERTY_TABINDEX ) ) + break; + bool bHasTabStop = false; + _rNewValue >>= bHasTabStop; + _rxInspectorUI->enablePropertyUI( PROPERTY_TABINDEX, bHasTabStop ); + } + break; + + // ----- Border ----- + case PROPERTY_ID_BORDER: + { + sal_Int16 nBordeType = VisualEffect::NONE; + if( ! (_rNewValue >>= nBordeType) ) + SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_BORDER); + _rxInspectorUI->enablePropertyUI( PROPERTY_BORDERCOLOR, nBordeType == VisualEffect::FLAT ); + } + break; + + // ----- DropDown ----- + case PROPERTY_ID_DROPDOWN: + { + if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_LINECOUNT ) ) + { + bool bDropDown = true; + _rNewValue >>= bDropDown; + _rxInspectorUI->enablePropertyUI( PROPERTY_LINECOUNT, bDropDown ); + } + } + break; + + // ----- ImageURL ----- + case PROPERTY_ID_IMAGE_URL: + { + if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_IMAGEPOSITION ) ) + { + OUString sImageURL; + if( ! (_rNewValue >>= sImageURL) ) + SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_IMAGE_URL); + _rxInspectorUI->enablePropertyUI( PROPERTY_IMAGEPOSITION, !sImageURL.isEmpty() ); + } + + aDependentProperties.push_back( PROPERTY_ID_SCALEIMAGE ); + aDependentProperties.push_back( PROPERTY_ID_SCALE_MODE ); + } + break; + + // ----- ButtonType ----- + case PROPERTY_ID_BUTTONTYPE: + { + FormButtonType eButtonType( FormButtonType_PUSH ); + if( ! (_rNewValue >>= eButtonType) ) + SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_BUTTONTYPE); + _rxInspectorUI->enablePropertyUI( PROPERTY_TARGET_URL, FormButtonType_URL == eButtonType ); + [[fallthrough]]; + } + + // ----- TargetURL ----- + case PROPERTY_ID_TARGET_URL: + aDependentProperties.push_back( PROPERTY_ID_TARGET_FRAME ); + break; // case PROPERTY_ID_TARGET_URL + + // ----- TriState ----- + case PROPERTY_ID_TRISTATE: + if ( !_bFirstTimeInit ) + _rxInspectorUI->rebuildPropertyUI( m_eComponentClass == eFormControl ? OUString(PROPERTY_DEFAULT_STATE) : OUString(PROPERTY_STATE) ); + break; // case PROPERTY_ID_TRISTATE + + // ----- DecimalAccuracy ----- + case PROPERTY_ID_DECIMAL_ACCURACY: + // ----- ShowThousandsSeparator ----- + case PROPERTY_ID_SHOWTHOUSANDSEP: + { + bool bAccuracy = (PROPERTY_ID_DECIMAL_ACCURACY == nActuatingPropId); + sal_uInt16 nNewDigits = 0; + if ( bAccuracy ) + { + if( ! (_rNewValue >>= nNewDigits) ) + SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_DECIMAL_ACCURACY); + } + else + { + bool bUseSep = false; + if( ! (_rNewValue >>= bUseSep) ) + SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_SHOWTHOUSANDSEP); + } + + // propagate the changes to the min/max/default fields + OUString aAffectedProps[] = { OUString(PROPERTY_VALUE), OUString(PROPERTY_DEFAULT_VALUE), OUString(PROPERTY_VALUEMIN), OUString(PROPERTY_VALUEMAX) }; + for (const OUString & aAffectedProp : aAffectedProps) + { + Reference< XPropertyControl > xControl; + try + { + xControl = _rxInspectorUI->getPropertyControl( aAffectedProp ); + } + catch( const UnknownPropertyException& ) {} + if ( xControl.is() ) + { + OFormattedNumericControl* pControl = dynamic_cast< OFormattedNumericControl* >( xControl.get() ); + DBG_ASSERT( pControl, "FormComponentPropertyHandler::actuatingPropertyChanged: invalid control!" ); + if (pControl) + { + if ( bAccuracy ) + pControl->SetDecimalDigits( nNewDigits ); + } + } + } + } + break; + + // ----- FormatKey ----- + case PROPERTY_ID_FORMATKEY: + { + FormatDescription aNewDesc; + + Reference< XNumberFormatsSupplier > xSupplier; + if( ! (m_xComponent->getPropertyValue( PROPERTY_FORMATSSUPPLIER ) >>= xSupplier) ) + SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_FORMATKEY); + + Reference< XUnoTunnel > xTunnel( xSupplier, UNO_QUERY ); + DBG_ASSERT(xTunnel.is(), "FormComponentPropertyHandler::actuatingPropertyChanged: xTunnel is invalid!"); + if ( xTunnel.is() ) + { + SvNumberFormatsSupplierObj* pSupplier = reinterpret_cast<SvNumberFormatsSupplierObj*>(xTunnel->getSomething(SvNumberFormatsSupplierObj::getUnoTunnelId())); + // the same again + + aNewDesc.pSupplier = pSupplier; + if ( !( _rNewValue >>= aNewDesc.nKey ) ) + aNewDesc.nKey = 0; + + // give each control which has to know this an own copy of the description + OUString aFormattedPropertyControls[] = { + OUString(PROPERTY_EFFECTIVE_MIN), OUString(PROPERTY_EFFECTIVE_MAX), OUString(PROPERTY_EFFECTIVE_DEFAULT), OUString(PROPERTY_EFFECTIVE_VALUE) + }; + for (const OUString & aFormattedPropertyControl : aFormattedPropertyControls) + { + Reference< XPropertyControl > xControl; + try + { + xControl = _rxInspectorUI->getPropertyControl( aFormattedPropertyControl ); + } + catch( const UnknownPropertyException& ) {} + if ( xControl.is() ) + { + OFormattedNumericControl* pControl = dynamic_cast< OFormattedNumericControl* >( xControl.get() ); + DBG_ASSERT( pControl, "FormComponentPropertyHandler::actuatingPropertyChanged: invalid control!" ); + if ( pControl ) + pControl->SetFormatDescription( aNewDesc ); + } + } + } + } + break; + + case PROPERTY_ID_TOGGLE: + { + bool bIsToggleButton = false; + if( ! (_rNewValue >>= bIsToggleButton) ) + SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_TOGGLE); + _rxInspectorUI->enablePropertyUI( PROPERTY_DEFAULT_STATE, bIsToggleButton ); + } + break; + case -1: + throw RuntimeException(); + break; + default: + OSL_FAIL( "FormComponentPropertyHandler::actuatingPropertyChanged: did not register for this property!" ); + break; + + } // switch ( nActuatingPropId ) + + for (auto const& dependentProperty : aDependentProperties) + { + if ( impl_isSupportedProperty_nothrow(dependentProperty) ) + impl_updateDependentProperty_nothrow(dependentProperty, _rxInspectorUI); + } + } + + void FormComponentPropertyHandler::impl_updateDependentProperty_nothrow( PropertyId _nPropId, const Reference< XObjectInspectorUI >& _rxInspectorUI ) const + { + try + { + switch ( _nPropId ) + { + // ----- StringItemList ----- + case PROPERTY_ID_STRINGITEMLIST: + { + ListSourceType eLSType = ListSourceType_VALUELIST; + if( ! (impl_getPropertyValue_throw( PROPERTY_LISTSOURCETYPE ) >>= eLSType) ) + SAL_WARN("extensions.propctrlr", "impl_updateDependentProperty_nothrow: unable to get property " << PROPERTY_LISTSOURCETYPE); + + OUString sListSource; + { + Sequence< OUString > aListSource; + Any aListSourceValue( impl_getPropertyValue_throw( PROPERTY_LISTSOURCE ) ); + if ( aListSourceValue >>= aListSource ) + { + if ( aListSource.hasElements() ) + sListSource = aListSource[0]; + } + else + if( ! (aListSourceValue >>= sListSource) ) + SAL_WARN("extensions.propctrlr", "impl_updateDependentProperty_nothrow: unable to get property " << PROPERTY_LISTSOURCE); + } + + bool bIsEnabled = ( ( eLSType == ListSourceType_VALUELIST ) + || ( sListSource.isEmpty() ) + ); + _rxInspectorUI->enablePropertyUI( PROPERTY_STRINGITEMLIST, bIsEnabled ); + } + break; // case PROPERTY_ID_STRINGITEMLIST + + // ----- TypedItemList ----- + case PROPERTY_ID_TYPEDITEMLIST: + { + /* TODO: anything? */ + } + break; // case PROPERTY_ID_TYPEDITEMLIST + + // ----- BoundColumn ----- + case PROPERTY_ID_BOUNDCOLUMN: + { + ListSourceType eLSType = ListSourceType_VALUELIST; + if( ! (impl_getPropertyValue_throw( PROPERTY_LISTSOURCETYPE ) >>= eLSType) ) + SAL_WARN("extensions.propctrlr", "impl_updateDependentProperty_nothrow: unable to get property " << PROPERTY_LISTSOURCETYPE); + + _rxInspectorUI->enablePropertyUI( PROPERTY_BOUNDCOLUMN, + ( eLSType != ListSourceType_VALUELIST ) + ); + } + break; // case PROPERTY_ID_BOUNDCOLUMN + + // ----- ScaleImage, ScaleMode ----- + case PROPERTY_ID_SCALEIMAGE: + case PROPERTY_ID_SCALE_MODE: + { + OUString sControlSource; + if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_CONTROLSOURCE ) ) + impl_getPropertyValue_throw( PROPERTY_CONTROLSOURCE ) >>= sControlSource; + + OUString sImageURL; + impl_getPropertyValue_throw( PROPERTY_IMAGE_URL ) >>= sImageURL; + + _rxInspectorUI->enablePropertyUI( impl_getPropertyNameFromId_nothrow( _nPropId ), + ( !sControlSource.isEmpty() ) || ( !sImageURL.isEmpty() ) + ); + } + break; // case PROPERTY_ID_SCALEIMAGE, PROPERTY_ID_SCALE_MODE + + // ----- InputRequired ----- + case PROPERTY_ID_INPUT_REQUIRED: + { + OUString sControlSource; + if( ! (impl_getPropertyValue_throw( PROPERTY_CONTROLSOURCE ) >>= sControlSource) ) + SAL_WARN("extensions.propctrlr", "impl_updateDependentProperty_nothrow: unable to get property " << PROPERTY_CONTROLSOURCE); + + bool bEmptyIsNULL = false; + bool bHasEmptyIsNULL = impl_componentHasProperty_throw( PROPERTY_EMPTY_IS_NULL ); + if ( bHasEmptyIsNULL ) + if( ! (impl_getPropertyValue_throw( PROPERTY_EMPTY_IS_NULL ) >>= bEmptyIsNULL) ) + SAL_WARN("extensions.propctrlr", "impl_updateDependentProperty_nothrow: unable to get property " << PROPERTY_EMPTY_IS_NULL); + + // if the control is not bound to a DB field, there is no sense in having the "Input required" + // property + // Also, if an empty input of this control are *not* written as NULL, but as empty strings, + // then "Input required" does not make sense, too (since there's always an input, even if the control + // is empty). + _rxInspectorUI->enablePropertyUI( PROPERTY_INPUT_REQUIRED, + ( !sControlSource.isEmpty() ) && ( !bHasEmptyIsNULL || bEmptyIsNULL ) + ); + } + break; + + // ----- SelectedItems, DefaultSelection ----- + case PROPERTY_ID_SELECTEDITEMS: + case PROPERTY_ID_DEFAULT_SELECT_SEQ: + { + Sequence< OUString > aEntries; + impl_getPropertyValue_throw( PROPERTY_STRINGITEMLIST ) >>= aEntries; + bool isEnabled = aEntries.hasElements(); + + if ( ( m_nClassId == FormComponentType::LISTBOX ) && ( m_eComponentClass == eFormControl ) ) + { + ListSourceType eLSType = ListSourceType_VALUELIST; + impl_getPropertyValue_throw( PROPERTY_LISTSOURCETYPE ) >>= eLSType; + isEnabled &= ( eLSType == ListSourceType_VALUELIST ); + } + _rxInspectorUI->enablePropertyUIElements( impl_getPropertyNameFromId_nothrow( _nPropId ), + PropertyLineElement::PrimaryButton, isEnabled ); + } + break; // case PROPERTY_ID_DEFAULT_SELECT_SEQ + + // ----- TargetFrame ------ + case PROPERTY_ID_TARGET_FRAME: + { + OUString sTargetURL; + impl_getPropertyValue_throw( PROPERTY_TARGET_URL ) >>= sTargetURL; + FormButtonType eButtonType( FormButtonType_URL ); + if ( 0 != m_nClassId ) + { + if( ! (impl_getPropertyValue_throw( PROPERTY_BUTTONTYPE ) >>= eButtonType) ) + SAL_WARN("extensions.propctrlr", "impl_updateDependentProperty_nothrow: unable to get property " << PROPERTY_BUTTONTYPE); + } + // if m_nClassId is 0, then we're inspecting a form. In this case, eButtonType is always + // FormButtonType_URL here + _rxInspectorUI->enablePropertyUI( PROPERTY_TARGET_FRAME, + ( eButtonType == FormButtonType_URL ) && ( !sTargetURL.isEmpty() ) + ); + } + break; + + // ----- Order ------ + case PROPERTY_ID_SORT: + // ----- Filter ------ + case PROPERTY_ID_FILTER: + { + Reference< XConnection > xConnection; + bool bAllowEmptyDS = ::dbtools::isEmbeddedInDatabase( m_xComponent, xConnection ); + + // if there's no escape processing, we cannot enter any values for this property + bool bDoEscapeProcessing( false ); + impl_getPropertyValue_throw( PROPERTY_ESCAPE_PROCESSING ) >>= bDoEscapeProcessing; + _rxInspectorUI->enablePropertyUI( + impl_getPropertyNameFromId_nothrow( _nPropId ), + bDoEscapeProcessing + ); + + // also care for the browse button - enabled if we have escape processing, and a valid + // data source signature + _rxInspectorUI->enablePropertyUIElements( + impl_getPropertyNameFromId_nothrow( _nPropId ), + PropertyLineElement::PrimaryButton, + impl_hasValidDataSourceSignature_nothrow( m_xComponent, bAllowEmptyDS ) + && bDoEscapeProcessing + ); + } + break; // case PROPERTY_ID_FILTER: + + // ----- Command ----- + case PROPERTY_ID_COMMAND: + { + sal_Int32 nCommandType( CommandType::COMMAND ); + if( ! (impl_getPropertyValue_throw( PROPERTY_COMMANDTYPE ) >>= nCommandType) ) + SAL_WARN("extensions.propctrlr", "impl_updateDependentProperty_nothrow: unable to get property " << PROPERTY_COMMANDTYPE); + + impl_ensureRowsetConnection_nothrow(); + Reference< XConnection > xConnection = m_xRowSetConnection.getTyped(); + bool bAllowEmptyDS = false; + if ( !xConnection.is() ) + bAllowEmptyDS = ::dbtools::isEmbeddedInDatabase( m_xComponent, xConnection ); + + bool doEnable = ( nCommandType == CommandType::COMMAND ) + && ( m_xRowSetConnection.is() + || xConnection.is() + || impl_hasValidDataSourceSignature_nothrow( m_xComponent, bAllowEmptyDS) + ); + + _rxInspectorUI->enablePropertyUIElements( + PROPERTY_COMMAND, + PropertyLineElement::PrimaryButton, + doEnable + ); + } + break; // case PROPERTY_ID_COMMAND + + // ----- DetailFields ----- + case PROPERTY_ID_DETAILFIELDS: + { + Reference< XConnection > xConnection; + bool bAllowEmptyDS = ::dbtools::isEmbeddedInDatabase( m_xComponent, xConnection ); + + // both our current form, and its parent form, need to have a valid + // data source signature + bool bDoEnableMasterDetailFields = + impl_hasValidDataSourceSignature_nothrow( m_xComponent, bAllowEmptyDS ) + && impl_hasValidDataSourceSignature_nothrow( Reference< XPropertySet >( m_xObjectParent, UNO_QUERY ), bAllowEmptyDS ); + + // in opposite to the other properties, here in real *two* properties are + // affected + _rxInspectorUI->enablePropertyUIElements( PROPERTY_DETAILFIELDS, PropertyLineElement::PrimaryButton, bDoEnableMasterDetailFields ); + _rxInspectorUI->enablePropertyUIElements( PROPERTY_MASTERFIELDS, PropertyLineElement::PrimaryButton, bDoEnableMasterDetailFields ); + } + break; + + default: + OSL_FAIL( "FormComponentPropertyHandler::impl_updateDependentProperty_nothrow: unexpected property to update!" ); + break; + + } // switch + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::impl_updateDependentProperty_nothrow" ); + } + } + + void SAL_CALL FormComponentPropertyHandler::disposing() + { + PropertyHandlerComponent::disposing(); + if ( m_xCommandDesigner.is() && m_xCommandDesigner->isActive() ) + m_xCommandDesigner->dispose(); + } + + sal_Bool SAL_CALL FormComponentPropertyHandler::suspend( sal_Bool _bSuspend ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( _bSuspend ) + if ( m_xCommandDesigner.is() && m_xCommandDesigner->isActive() ) + return m_xCommandDesigner->suspend(); + return true; + } + + void FormComponentPropertyHandler::onNewComponent() + { + PropertyHandlerComponent::onNewComponent(); + if ( !m_xComponentPropertyInfo.is() && m_xComponent.is() ) + throw NullPointerException(); + + m_xPropertyState.set( m_xComponent, UNO_QUERY ); + m_eComponentClass = eUnknown; + m_bComponentIsSubForm = m_bHaveListSource = m_bHaveCommand = false; + m_nClassId = 0; + + try + { + // component class + m_eComponentClass = eUnknown; + + if ( impl_componentHasProperty_throw( PROPERTY_WIDTH ) + && impl_componentHasProperty_throw( PROPERTY_HEIGHT ) + && impl_componentHasProperty_throw( PROPERTY_POSITIONX ) + && impl_componentHasProperty_throw( PROPERTY_POSITIONY ) + && impl_componentHasProperty_throw( PROPERTY_STEP ) + && impl_componentHasProperty_throw( PROPERTY_TABINDEX ) + ) + { + m_eComponentClass = eDialogControl; + } + else + { + m_eComponentClass = eFormControl; + } + + + // (database) sub form? + Reference< XForm > xAsForm( m_xComponent, UNO_QUERY ); + if ( xAsForm.is() ) + { + Reference< XForm > xFormsParent( xAsForm->getParent(), css::uno::UNO_QUERY ); + m_bComponentIsSubForm = xFormsParent.is(); + } + + + // ClassId + Reference< XChild > xCompAsChild( m_xComponent, UNO_QUERY ); + if ( xCompAsChild.is() ) + m_xObjectParent = xCompAsChild->getParent(); + + + // ClassId + impl_classifyControlModel_throw(); + } + catch( const RuntimeException& ) + { + throw; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::onNewComponent" ); + } + } + + void FormComponentPropertyHandler::impl_classifyControlModel_throw( ) + { + if ( impl_componentHasProperty_throw( PROPERTY_CLASSID ) ) + { + if( ! (m_xComponent->getPropertyValue( PROPERTY_CLASSID ) >>= m_nClassId) ) + SAL_WARN("extensions.propctrlr", "impl_classifyControlModel_throw: unable to get property " << PROPERTY_CLASSID); + } + else if ( eDialogControl == m_eComponentClass ) + { + Reference< XServiceInfo > xServiceInfo( m_xComponent, UNO_QUERY ); + if ( xServiceInfo.is() ) + { + // it's a control model, and can tell about it's supported services + m_nClassId = FormComponentType::CONTROL; + + const char* aControlModelServiceNames[] = + { + "UnoControlButtonModel", + "UnoControlCheckBoxModel", + "UnoControlComboBoxModel", + "UnoControlCurrencyFieldModel", + "UnoControlDateFieldModel", + "UnoControlEditModel", + "UnoControlFileControlModel", + "UnoControlFixedTextModel", + "UnoControlGroupBoxModel", + "UnoControlImageControlModel", + "UnoControlListBoxModel", + "UnoControlNumericFieldModel", + "UnoControlPatternFieldModel", + "UnoControlRadioButtonModel", + "UnoControlScrollBarModel", + "UnoControlSpinButtonModel", + "UnoControlTimeFieldModel", + + "UnoControlFixedLineModel", + "UnoControlFormattedFieldModel", + "UnoControlProgressBarModel" + }; + const sal_Int16 nClassIDs[] = + { + FormComponentType::COMMANDBUTTON, + FormComponentType::CHECKBOX, + FormComponentType::COMBOBOX, + FormComponentType::CURRENCYFIELD, + FormComponentType::DATEFIELD, + FormComponentType::TEXTFIELD, + FormComponentType::FILECONTROL, + FormComponentType::FIXEDTEXT, + FormComponentType::GROUPBOX, + FormComponentType::IMAGECONTROL, + FormComponentType::LISTBOX, + FormComponentType::NUMERICFIELD, + FormComponentType::PATTERNFIELD, + FormComponentType::RADIOBUTTON, + FormComponentType::SCROLLBAR, + FormComponentType::SPINBUTTON, + FormComponentType::TIMEFIELD, + + ControlType::FIXEDLINE, + ControlType::FORMATTEDFIELD, + ControlType::PROGRESSBAR + }; + + sal_Int32 nKnownControlTypes = SAL_N_ELEMENTS( aControlModelServiceNames ); + OSL_ENSURE( nKnownControlTypes == SAL_N_ELEMENTS( nClassIDs ), + "FormComponentPropertyHandler::impl_classifyControlModel_throw: inconsistence" ); + + for ( sal_Int32 i = 0; i < nKnownControlTypes; ++i ) + { + OUString sServiceName = "com.sun.star.awt." + + OUString::createFromAscii( aControlModelServiceNames[ i ] ); + + if ( xServiceInfo->supportsService( sServiceName ) ) + { + m_nClassId = nClassIDs[ i ]; + break; + } + } + } + } + } + + void FormComponentPropertyHandler::impl_normalizePropertyValue_nothrow( Any& _rValue, PropertyId _nPropId ) const + { + switch ( _nPropId ) + { + case PROPERTY_ID_TABSTOP: + if ( !_rValue.hasValue() ) + { + switch ( m_nClassId ) + { + case FormComponentType::COMMANDBUTTON: + case FormComponentType::RADIOBUTTON: + case FormComponentType::CHECKBOX: + case FormComponentType::TEXTFIELD: + case FormComponentType::LISTBOX: + case FormComponentType::COMBOBOX: + case FormComponentType::FILECONTROL: + case FormComponentType::DATEFIELD: + case FormComponentType::TIMEFIELD: + case FormComponentType::NUMERICFIELD: + case ControlType::FORMATTEDFIELD: + case FormComponentType::CURRENCYFIELD: + case FormComponentType::PATTERNFIELD: + _rValue <<= true; + break; + default: + _rValue <<= false; + break; + } + } + break; + } + } + + bool FormComponentPropertyHandler::isReportModel() const + { + Reference<XModel> xModel(impl_getContextDocument_nothrow()); + Reference<XReportDefinition> xReportDef(xModel, css::uno::UNO_QUERY); + return xReportDef.is(); + } + + bool FormComponentPropertyHandler::impl_shouldExcludeProperty_nothrow( const Property& _rProperty ) const + { + OSL_ENSURE( _rProperty.Handle == m_pInfoService->getPropertyId( _rProperty.Name ), + "FormComponentPropertyHandler::impl_shouldExcludeProperty_nothrow: inconsistency in the property!" ); + + if ( _rProperty.Handle == PROPERTY_ID_CONTROLLABEL ) + // prevent that this is caught below + return false; + + if ( ( _rProperty.Type.getTypeClass() == TypeClass_INTERFACE ) + || ( _rProperty.Type.getTypeClass() == TypeClass_UNKNOWN ) + ) + return true; + + if ( ( _rProperty.Attributes & PropertyAttribute::TRANSIENT ) && ( m_eComponentClass != eDialogControl ) ) + // strange enough, dialog controls declare a lot of their properties as transient + return true; + + if ( _rProperty.Attributes & PropertyAttribute::READONLY ) + return true; + + switch ( _rProperty.Handle ) + { + case PROPERTY_ID_MASTERFIELDS: + case PROPERTY_ID_DETAILFIELDS: + if ( !m_bComponentIsSubForm ) + // no master and detail fields for forms which are no sub forms + return true; + break; + + case PROPERTY_ID_DATASOURCE: + { + // don't show DataSource if the component is part of an embedded form document + Reference< XConnection > xConn; + if ( isEmbeddedInDatabase( m_xComponent, xConn ) ) + return true; + } + break; + + case PROPERTY_ID_TEXT: + // don't show the "Text" property of formatted fields + if ( ControlType::FORMATTEDFIELD == m_nClassId ) + return true; + break; + + case PROPERTY_ID_FORMATKEY: + case PROPERTY_ID_EFFECTIVE_MIN: + case PROPERTY_ID_EFFECTIVE_MAX: + case PROPERTY_ID_EFFECTIVE_DEFAULT: + case PROPERTY_ID_EFFECTIVE_VALUE: + // only if the set has a formats supplier, too + if ( !impl_componentHasProperty_throw( PROPERTY_FORMATSSUPPLIER ) ) + return true; + // (form) date and time fields also have a formats supplier, but the format itself + // is reflected in another property + if ( ( FormComponentType::DATEFIELD == m_nClassId ) + || ( FormComponentType::TIMEFIELD == m_nClassId ) + ) + return true; + break; + + case PROPERTY_ID_SCALEIMAGE: + if ( impl_componentHasProperty_throw( PROPERTY_SCALE_MODE ) ) + // ScaleImage is superseded by ScaleMode + return true; + break; + + case PROPERTY_ID_WRITING_MODE: + if ( !SvtCTLOptions().IsCTLFontEnabled() ) + return true; + break; + } + + sal_uInt32 nPropertyUIFlags = m_pInfoService->getPropertyUIFlags( _rProperty.Handle ); + + // don't show experimental properties unless allowed to do so + if ( ( nPropertyUIFlags & PROP_FLAG_EXPERIMENTAL ) != 0 ) + return true; + + // no data properties if no Base is installed. + if ( ( nPropertyUIFlags & PROP_FLAG_DATA_PROPERTY ) != 0 ) + if ( !SvtModuleOptions().IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) ) + return true; + + if ((nPropertyUIFlags & PROP_FLAG_REPORT_INVISIBLE) != 0 && isReportModel()) + return true; + + return false; + } + + Reference< XRowSet > FormComponentPropertyHandler::impl_getRowSet_throw( ) const + { + Reference< XRowSet > xRowSet = m_xRowSet; + if ( !xRowSet.is() ) + { + xRowSet.set( m_xComponent, UNO_QUERY ); + if ( !xRowSet.is() ) + { + xRowSet.set( m_xObjectParent, UNO_QUERY ); + if ( !xRowSet.is() ) + { + // are we inspecting a grid column? + if (Reference< XGridColumnFactory >( m_xObjectParent, UNO_QUERY) .is()) + { // yes + Reference< XChild > xParentAsChild( m_xObjectParent, UNO_QUERY ); + if ( xParentAsChild.is() ) + xRowSet.set( xParentAsChild->getParent(), UNO_QUERY ); + } + } + if ( !xRowSet.is() ) + xRowSet = m_xRowSet; + } + DBG_ASSERT( xRowSet.is(), "FormComponentPropertyHandler::impl_getRowSet_throw: could not obtain the rowset for the introspectee!" ); + } + return xRowSet; + } + + + Reference< XRowSet > FormComponentPropertyHandler::impl_getRowSet_nothrow( ) const + { + Reference< XRowSet > xReturn; + try + { + xReturn = impl_getRowSet_throw(); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::impl_getRowSet_nothrow" ); + } + return xReturn; + } + + + void FormComponentPropertyHandler::impl_initFieldList_nothrow( std::vector< OUString >& _rFieldNames ) const + { + clearContainer( _rFieldNames ); + try + { + weld::WaitObject aWaitCursor(impl_getDefaultDialogFrame_nothrow()); + + // get the form of the control we're inspecting + Reference< XPropertySet > xFormSet( impl_getRowSet_throw(), UNO_QUERY ); + if ( !xFormSet.is() ) + return; + + OUString sObjectName; + if( ! (xFormSet->getPropertyValue( PROPERTY_COMMAND ) >>= sObjectName) ) + SAL_WARN("extensions.propctrlr", "impl_initFieldList_nothrow: unable to get property " << PROPERTY_COMMAND); + // when there is no command we don't need to ask for columns + if ( !sObjectName.isEmpty() && impl_ensureRowsetConnection_nothrow() ) + { + OUString aDatabaseName; + if( ! (xFormSet->getPropertyValue( PROPERTY_DATASOURCE ) >>= aDatabaseName) ) + SAL_WARN("extensions.propctrlr", "impl_initFieldList_nothrow: unable to get property " << PROPERTY_DATASOURCE); + sal_Int32 nObjectType = CommandType::COMMAND; + if( ! (xFormSet->getPropertyValue( PROPERTY_COMMANDTYPE ) >>= nObjectType) ) + SAL_WARN("extensions.propctrlr", "impl_initFieldList_nothrow: unable to get property " << PROPERTY_COMMANDTYPE); + + const Sequence<OUString> aNames = ::dbtools::getFieldNamesByCommandDescriptor( m_xRowSetConnection, nObjectType, sObjectName ); + _rFieldNames.insert( _rFieldNames.end(), aNames.begin(), aNames.end() ); + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::impl_initFieldList_nothrow" ); + } + } + + void FormComponentPropertyHandler::impl_displaySQLError_nothrow( const ::dbtools::SQLExceptionInfo& _rErrorDescriptor ) const + { + auto pTopLevel = impl_getDefaultDialogFrame_nothrow(); + ::dbtools::showError(_rErrorDescriptor, pTopLevel ? pTopLevel->GetXWindow() : nullptr, m_xContext); + } + + bool FormComponentPropertyHandler::impl_ensureRowsetConnection_nothrow() const + { + if ( !m_xRowSetConnection.is() ) + { + uno::Reference<sdbc::XConnection> xConnection; + Any any = m_xContext->getValueByName( "ActiveConnection" ); + any >>= xConnection; + m_xRowSetConnection.reset(xConnection,::dbtools::SharedConnection::NoTakeOwnership); + } + if ( m_xRowSetConnection.is() ) + return true; + + Reference< XRowSet > xRowSet( impl_getRowSet_throw() ); + Reference< XPropertySet > xRowSetProps( xRowSet, UNO_QUERY ); + + // connect the row set - this is delegated to elsewhere - while observing errors + SQLExceptionInfo aError; + try + { + if ( xRowSetProps.is() ) + { + weld::WaitObject aWaitCursor(impl_getDefaultDialogFrame_nothrow()); + m_xRowSetConnection = ::dbtools::ensureRowSetConnection( xRowSet, m_xContext, nullptr ); + } + } + catch ( const SQLException& ) { aError = SQLExceptionInfo( ::cppu::getCaughtException() ); } + catch ( const WrappedTargetException& e ) { aError = SQLExceptionInfo( e.TargetException ); } + catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); } + + // report errors, if necessary + if ( aError.isValid() ) + { + OUString sDataSourceName; + try + { + xRowSetProps->getPropertyValue( PROPERTY_DATASOURCE ) >>= sDataSourceName; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::impl_ensureRowsetConnection_nothrow: caught an exception during error handling!" ); + } + // additional info about what happened + INetURLObject aParser( sDataSourceName ); + if ( aParser.GetProtocol() != INetProtocol::NotValid ) + sDataSourceName = aParser.getBase( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ); + OUString sInfo(PcrRes(RID_STR_UNABLETOCONNECT).replaceAll("$name$", sDataSourceName)); + SQLContext aContext; + aContext.Message = sInfo; + aContext.NextException = aError.get(); + impl_displaySQLError_nothrow( aContext ); + } + + return m_xRowSetConnection.is(); + } + + + void FormComponentPropertyHandler::impl_describeCursorSource_nothrow( LineDescriptor& _out_rProperty, const Reference< XPropertyControlFactory >& _rxControlFactory ) const + { + try + { + weld::WaitObject aWaitCursor(impl_getDefaultDialogFrame_nothrow()); + + + // Set the UI data + _out_rProperty.DisplayName = m_pInfoService->getPropertyTranslation( PROPERTY_ID_COMMAND ); + + _out_rProperty.HelpURL = HelpIdUrl::getHelpURL( m_pInfoService->getPropertyHelpId( PROPERTY_ID_COMMAND ) ); + _out_rProperty.PrimaryButtonId = UID_PROP_DLG_SQLCOMMAND; + + + sal_Int32 nCommandType = CommandType::COMMAND; + impl_getPropertyValue_throw( PROPERTY_COMMANDTYPE ) >>= nCommandType; + + switch ( nCommandType ) + { + case CommandType::TABLE: + case CommandType::QUERY: + { + std::vector< OUString > aNames; + if ( impl_ensureRowsetConnection_nothrow() ) + { + if ( nCommandType == CommandType::TABLE ) + impl_fillTableNames_throw( aNames ); + else + impl_fillQueryNames_throw( aNames ); + } + _out_rProperty.Control = PropertyHandlerHelper::createComboBoxControl( _rxControlFactory, std::move(aNames), true ); + } + break; + + default: + _out_rProperty.Control = _rxControlFactory->createPropertyControl( PropertyControlType::MultiLineTextField, false ); + break; + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::impl_describeCursorSource_nothrow"); + } + } + + + void FormComponentPropertyHandler::impl_fillTableNames_throw( std::vector< OUString >& _out_rNames ) const + { + OSL_PRECOND( m_xRowSetConnection.is(), "FormComponentPropertyHandler::impl_fillTableNames_throw: need a connection!" ); + _out_rNames.resize( 0 ); + + Reference< XTablesSupplier > xSupplyTables( m_xRowSetConnection, UNO_QUERY ); + Reference< XNameAccess > xTableNames; + if ( xSupplyTables.is() ) + xTableNames = xSupplyTables->getTables(); + DBG_ASSERT( xTableNames.is(), "FormComponentPropertyHandler::impl_fillTableNames_throw: no way to obtain the tables of the connection!" ); + if ( !xTableNames.is() ) + return; + + const Sequence<OUString> aNames = xTableNames->getElementNames(); + _out_rNames.insert( _out_rNames.end(), aNames.begin(), aNames.end() ); + } + + + void FormComponentPropertyHandler::impl_fillQueryNames_throw( std::vector< OUString >& _out_rNames ) const + { + OSL_PRECOND( m_xRowSetConnection.is(), "FormComponentPropertyHandler::impl_fillQueryNames_throw: need a connection!" ); + _out_rNames.resize( 0 ); + + Reference< XQueriesSupplier > xSupplyQueries( m_xRowSetConnection, UNO_QUERY ); + Reference< XNameAccess > xQueryNames; + if ( xSupplyQueries.is() ) + { + xQueryNames = xSupplyQueries->getQueries(); + impl_fillQueryNames_throw(xQueryNames,_out_rNames); + } + } + + void FormComponentPropertyHandler::impl_fillQueryNames_throw( const Reference< XNameAccess >& _xQueryNames,std::vector< OUString >& _out_rNames,std::u16string_view _sName ) const + { + DBG_ASSERT( _xQueryNames.is(), "FormComponentPropertyHandler::impl_fillQueryNames_throw: no way to obtain the queries of the connection!" ); + if ( !_xQueryNames.is() ) + return; + + bool bAdd = !_sName.empty(); + + const Sequence<OUString> aQueryNames =_xQueryNames->getElementNames(); + for ( const OUString& rQueryName : aQueryNames ) + { + OUStringBuffer sTemp; + if ( bAdd ) + { + sTemp.append(_sName); + sTemp.append("/"); + } + sTemp.append(rQueryName); + Reference< XNameAccess > xSubQueries(_xQueryNames->getByName(rQueryName),UNO_QUERY); + if ( xSubQueries.is() ) + impl_fillQueryNames_throw(xSubQueries,_out_rNames,sTemp.makeStringAndClear()); + else + _out_rNames.push_back( sTemp.makeStringAndClear() ); + } + } + + + void FormComponentPropertyHandler::impl_describeListSourceUI_throw( LineDescriptor& _out_rDescriptor, const Reference< XPropertyControlFactory >& _rxControlFactory ) const + { + OSL_PRECOND( m_xComponent.is(), "FormComponentPropertyHandler::impl_describeListSourceUI_throw: no component!" ); + + + // read out ListSourceTypes + Any aListSourceType( m_xComponent->getPropertyValue( PROPERTY_LISTSOURCETYPE ) ); + + sal_Int32 nListSourceType = sal_Int32(ListSourceType_VALUELIST); + ::cppu::enum2int( nListSourceType, aListSourceType ); + ListSourceType eListSourceType = static_cast<ListSourceType>(nListSourceType); + + _out_rDescriptor.DisplayName = m_pInfoService->getPropertyTranslation( PROPERTY_ID_LISTSOURCE ); + _out_rDescriptor.HelpURL = HelpIdUrl::getHelpURL( m_pInfoService->getPropertyHelpId( PROPERTY_ID_LISTSOURCE ) ); + + + // set enums + switch( eListSourceType ) + { + case ListSourceType_VALUELIST: + _out_rDescriptor.Control = _rxControlFactory->createPropertyControl( PropertyControlType::StringListField, false ); + break; + + case ListSourceType_TABLEFIELDS: + case ListSourceType_TABLE: + case ListSourceType_QUERY: + { + std::vector< OUString > aListEntries; + if ( impl_ensureRowsetConnection_nothrow() ) + { + if ( eListSourceType == ListSourceType_QUERY ) + impl_fillQueryNames_throw( aListEntries ); + else + impl_fillTableNames_throw( aListEntries ); + } + _out_rDescriptor.Control = PropertyHandlerHelper::createComboBoxControl( _rxControlFactory, std::move(aListEntries), false ); + } + break; + case ListSourceType_SQL: + case ListSourceType_SQLPASSTHROUGH: + impl_ensureRowsetConnection_nothrow(); + _out_rDescriptor.HasPrimaryButton = m_xRowSetConnection.is(); + break; + default: break; + } + } + + bool FormComponentPropertyHandler::impl_dialogListSelection_nothrow( const OUString& _rProperty, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const + { + OSL_PRECOND(m_pInfoService, "FormComponentPropertyHandler::impl_dialogListSelection_" + "nothrow: no property meta data!"); + + OUString sPropertyUIName( m_pInfoService->getPropertyTranslation( m_pInfoService->getPropertyId( _rProperty ) ) ); + ListSelectionDialog aDialog(impl_getDefaultDialogFrame_nothrow(), m_xComponent, _rProperty, sPropertyUIName); + _rClearBeforeDialog.clear(); + return ( RET_OK == aDialog.run() ); + } + + bool FormComponentPropertyHandler::impl_dialogFilterOrSort_nothrow( bool _bFilter, OUString& _out_rSelectedClause, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const + { + OSL_PRECOND( Reference< XRowSet >( m_xComponent, UNO_QUERY ).is(), + "FormComponentPropertyHandler::impl_dialogFilterOrSort_nothrow: to be called for forms only!" ); + + _out_rSelectedClause.clear(); + bool bSuccess = false; + SQLExceptionInfo aErrorInfo; + try + { + if ( !impl_ensureRowsetConnection_nothrow() ) + return false; + + // get a composer for the statement which the form is currently based on + Reference< XSingleSelectQueryComposer > xComposer( ::dbtools::getCurrentSettingsComposer( m_xComponent, m_xContext, nullptr ) ); + OSL_ENSURE( xComposer.is(), "FormComponentPropertyHandler::impl_dialogFilterOrSort_nothrow: could not obtain a composer!" ); + if ( !xComposer.is() ) + return false; + + OUString sPropertyUIName( m_pInfoService->getPropertyTranslation( _bFilter ? PROPERTY_ID_FILTER : PROPERTY_ID_SORT ) ); + + // create the dialog + Reference< XExecutableDialog > xDialog; + if ( _bFilter) + { + xDialog.set( sdb::FilterDialog::createDefault(m_xContext) ); + } + else + { + xDialog.set( sdb::OrderDialog::createDefault(m_xContext) ); + } + + + // initialize the dialog + Reference< XPropertySet > xDialogProps( xDialog, UNO_QUERY_THROW ); + xDialogProps->setPropertyValue("QueryComposer", Any( xComposer ) ); + xDialogProps->setPropertyValue("RowSet", Any( m_xComponent ) ); + if (auto pTopLevel = impl_getDefaultDialogFrame_nothrow()) + xDialogProps->setPropertyValue("ParentWindow", Any(pTopLevel->GetXWindow())); + xDialogProps->setPropertyValue("Title", Any( sPropertyUIName ) ); + + _rClearBeforeDialog.clear(); + bSuccess = ( xDialog->execute() != 0 ); + if ( bSuccess ) + _out_rSelectedClause = _bFilter ? xComposer->getFilter() : xComposer->getOrder(); + } + catch (const SQLContext& e) { aErrorInfo = e; } + catch (const SQLWarning& e) { aErrorInfo = e; } + catch (const SQLException& e) { aErrorInfo = e; } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::impl_dialogFilterOrSort_nothrow" ); + } + + if ( aErrorInfo.isValid() ) + impl_displaySQLError_nothrow( aErrorInfo ); + + return bSuccess; + } + + + bool FormComponentPropertyHandler::impl_dialogLinkedFormFields_nothrow( ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const + { + Reference< XForm > xDetailForm( m_xComponent, UNO_QUERY ); + Reference< XForm > xMasterForm( m_xObjectParent, UNO_QUERY ); + uno::Reference<beans::XPropertySet> xMasterProp(m_xObjectParent,uno::UNO_QUERY); + OSL_PRECOND( xDetailForm.is() && xMasterForm.is(), "FormComponentPropertyHandler::impl_dialogLinkedFormFields_nothrow: no forms!" ); + if ( !xDetailForm.is() || !xMasterForm.is() ) + return false; + + FormLinkDialog aDialog(impl_getDefaultDialogFrame_nothrow(), m_xComponent, xMasterProp, m_xContext); + _rClearBeforeDialog.clear(); + return ( RET_OK == aDialog.run() ); + } + + bool FormComponentPropertyHandler::impl_dialogFormatting_nothrow( Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const + { + bool bChanged = false; + try + { + // create the itemset for the dialog + SfxItemSet aCoreSet( + SfxGetpApp()->GetPool(), + svl::Items< + SID_ATTR_NUMBERFORMAT_VALUE, SID_ATTR_NUMBERFORMAT_INFO>); + // ripped this somewhere ... don't understand it :( + + // get the number formats supplier + Reference< XNumberFormatsSupplier > xSupplier; + m_xComponent->getPropertyValue( PROPERTY_FORMATSSUPPLIER ) >>= xSupplier; + + DBG_ASSERT(xSupplier.is(), "FormComponentPropertyHandler::impl_dialogFormatting_nothrow: invalid call !" ); + Reference< XUnoTunnel > xTunnel( xSupplier, UNO_QUERY_THROW ); + SvNumberFormatsSupplierObj* pSupplier = + reinterpret_cast< SvNumberFormatsSupplierObj* >( xTunnel->getSomething( SvNumberFormatsSupplierObj::getUnoTunnelId() ) ); + DBG_ASSERT( pSupplier != nullptr, "FormComponentPropertyHandler::impl_dialogFormatting_nothrow: invalid call !" ); + + sal_Int32 nFormatKey = 0; + impl_getPropertyValue_throw( PROPERTY_FORMATKEY ) >>= nFormatKey; + aCoreSet.Put( SfxUInt32Item( SID_ATTR_NUMBERFORMAT_VALUE, nFormatKey ) ); + + SvNumberFormatter* pFormatter = pSupplier->GetNumberFormatter(); + double dPreviewVal = OFormatSampleControl::getPreviewValue(pFormatter,nFormatKey); + SvxNumberInfoItem aFormatter( pFormatter, dPreviewVal, PcrRes(RID_STR_TEXT_FORMAT), SID_ATTR_NUMBERFORMAT_INFO ); + aCoreSet.Put( aFormatter ); + + // a tab dialog with a single page + SfxSingleTabDialogController aDialog(impl_getDefaultDialogFrame_nothrow(), &aCoreSet, + "cui/ui/formatnumberdialog.ui", "FormatNumberDialog"); + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ::CreateTabPage fnCreatePage = pFact->GetTabPageCreatorFunc( RID_SVXPAGE_NUMBERFORMAT ); + if ( !fnCreatePage ) + throw RuntimeException(); // caught below + + aDialog.SetTabPage((*fnCreatePage)(aDialog.get_content_area(), &aDialog, &aCoreSet)); + + _rClearBeforeDialog.clear(); + if ( RET_OK == aDialog.run() ) + { + const SfxItemSet* pResult = aDialog.GetOutputItemSet(); + + if (const SvxNumberInfoItem* pInfoItem = pResult->GetItem( SID_ATTR_NUMBERFORMAT_INFO )) + { + for (sal_uInt32 key : pInfoItem->GetDelFormats()) + pFormatter->DeleteEntry(key); + } + + if ( const SfxUInt32Item* pItem = pResult->GetItemIfSet( SID_ATTR_NUMBERFORMAT_VALUE, false ) ) + { + _out_rNewValue <<= static_cast<sal_Int32>( pItem->GetValue() ); + bChanged = true; + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::impl_dialogFormatting_nothrow" ); + } + return bChanged; + } + + bool FormComponentPropertyHandler::impl_browseForImage_nothrow( Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const + { + bool bIsLink = true;// reflect the legacy behavior + OUString aStrTrans = m_pInfoService->getPropertyTranslation( PROPERTY_ID_IMAGE_URL ); + + weld::Window* pWin = impl_getDefaultDialogFrame_nothrow(); + ::sfx2::FileDialogHelper aFileDlg( + ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW, + FileDialogFlags::Graphic, pWin); + aFileDlg.SetContext(sfx2::FileDialogHelper::FormsInsertImage); + aFileDlg.SetTitle(aStrTrans); + // non-linked images ( e.g. those located in the document + // stream ) only if document is available + bool bHandleNonLink; + { + Reference< XModel > xModel( impl_getContextDocument_nothrow() ); + bHandleNonLink = xModel.is(); + // Not implemented in reports + if (bHandleNonLink) + { + Reference< XReportDefinition > xReportDef( xModel, css::uno::UNO_QUERY ); + bHandleNonLink = !xReportDef.is(); + } + } + + Reference< XFilePickerControlAccess > xController(aFileDlg.GetFilePicker(), UNO_QUERY); + DBG_ASSERT(xController.is(), "FormComponentPropertyHandler::impl_browseForImage_nothrow: missing the controller interface on the file picker!"); + if (xController.is()) + { + // do a preview by default + xController->setValue(ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, 0, css::uno::Any(true)); + + xController->setValue(ExtendedFilePickerElementIds::CHECKBOX_LINK, 0, css::uno::Any(bIsLink)); + xController->enableControl(ExtendedFilePickerElementIds::CHECKBOX_LINK, bHandleNonLink ); + + } + + OUString sCurValue; + if( ! (impl_getPropertyValue_throw( PROPERTY_IMAGE_URL ) >>= sCurValue) ) + SAL_WARN("extensions.propctrlr", "impl_browseForImage_nothrow: unable to get property " << PROPERTY_IMAGE_URL); + if (!sCurValue.isEmpty()) + { + aFileDlg.SetDisplayDirectory( sCurValue ); + // TODO: need to set the display directory _and_ the default name + } + + _rClearBeforeDialog.clear(); + bool bSuccess = ( ERRCODE_NONE == aFileDlg.Execute() ); + if ( bSuccess ) + { + if ( bHandleNonLink && xController.is() ) + { + xController->getValue(ExtendedFilePickerElementIds::CHECKBOX_LINK, 0) >>= bIsLink; + } + if ( !bIsLink ) + { + Graphic aGraphic; + aFileDlg.GetGraphic(aGraphic); + + Reference< graphic::XGraphicObject > xGrfObj = graphic::GraphicObject::create( m_xContext ); + xGrfObj->setGraphic( aGraphic.GetXGraphic() ); + + _out_rNewValue <<= xGrfObj; + + } + else + _out_rNewValue <<= aFileDlg.GetPath(); + } + return bSuccess; + } + + bool FormComponentPropertyHandler::impl_browseForTargetURL_nothrow( Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const + { + weld::Window* pWin = impl_getDefaultDialogFrame_nothrow(); + ::sfx2::FileDialogHelper aFileDlg( + ui::dialogs::TemplateDescription::FILEOPEN_READONLY_VERSION, + FileDialogFlags::NONE, pWin); + + OUString sURL; + if( ! (impl_getPropertyValue_throw( PROPERTY_TARGET_URL ) >>= sURL) ) + SAL_WARN("extensions.propctrlr", "impl_browseForTargetURL_nothrow: unable to get property " << PROPERTY_TARGET_URL); + INetURLObject aParser( sURL ); + if ( INetProtocol::File == aParser.GetProtocol() ) + // set the initial directory only for file-URLs. Everything else + // is considered to be potentially expensive + aFileDlg.SetDisplayDirectory( sURL ); + + _rClearBeforeDialog.clear(); + bool bSuccess = ( ERRCODE_NONE == aFileDlg.Execute() ); + if ( bSuccess ) + _out_rNewValue <<= aFileDlg.GetPath(); + return bSuccess; + } + + bool FormComponentPropertyHandler::impl_executeFontDialog_nothrow( Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const + { + bool bSuccess = false; + + // create an item set for use with the dialog + std::unique_ptr<SfxItemSet> pSet; + rtl::Reference<SfxItemPool> pPool; + std::vector<SfxPoolItem*>* pDefaults = nullptr; + ControlCharacterDialog::createItemSet(pSet, pPool, pDefaults); + ControlCharacterDialog::translatePropertiesToItems(m_xComponent, pSet.get()); + + { // do this in an own block. The dialog needs to be destroyed before we call + // destroyItemSet + ControlCharacterDialog aDlg(impl_getDefaultDialogFrame_nothrow(), *pSet); + _rClearBeforeDialog.clear(); + if (RET_OK == aDlg.run()) + { + const SfxItemSet* pOut = aDlg.GetOutputItemSet(); + if ( pOut ) + { + std::vector< NamedValue > aFontPropertyValues; + ControlCharacterDialog::translateItemsToProperties( *pOut, aFontPropertyValues ); + _out_rNewValue <<= comphelper::containerToSequence(aFontPropertyValues); + bSuccess = true; + } + } + } + + ControlCharacterDialog::destroyItemSet(pSet, pPool, pDefaults); + return bSuccess; + } + + + bool FormComponentPropertyHandler::impl_browseForDatabaseDocument_throw( Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const + { + weld::Window* pWin = impl_getDefaultDialogFrame_nothrow(); + ::sfx2::FileDialogHelper aFileDlg( + ui::dialogs::TemplateDescription::FILEOPEN_READONLY_VERSION, FileDialogFlags::NONE, + "sdatabase", SfxFilterFlags::NONE, SfxFilterFlags::NONE, pWin); + + OUString sDataSource; + if( ! (impl_getPropertyValue_throw( PROPERTY_DATASOURCE ) >>= sDataSource) ) + SAL_WARN("extensions.propctrlr", "impl_browseForDatabaseDocument_throw: unable to get property " << PROPERTY_DATASOURCE); + INetURLObject aParser( sDataSource ); + if ( INetProtocol::File == aParser.GetProtocol() ) + // set the initial directory only for file-URLs. Everything else + // is considered to be potentially expensive + aFileDlg.SetDisplayDirectory( sDataSource ); + + std::shared_ptr<const SfxFilter> pFilter = SfxFilter::GetFilterByName("StarOffice XML (Base)"); + OSL_ENSURE(pFilter,"Filter: StarOffice XML (Base) could not be found!"); + if ( pFilter ) + { + aFileDlg.SetCurrentFilter(pFilter->GetUIName()); + //aFileDlg.AddFilter(pFilter->GetFilterName(),pFilter->GetDefaultExtension()); + } + + _rClearBeforeDialog.clear(); + bool bSuccess = ( ERRCODE_NONE == aFileDlg.Execute() ); + if ( bSuccess ) + _out_rNewValue <<= aFileDlg.GetPath(); + return bSuccess; + } + + bool FormComponentPropertyHandler::impl_dialogColorChooser_throw( sal_Int32 _nColorPropertyId, Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const + { + ::Color aColor; + if( ! (impl_getPropertyValue_throw( impl_getPropertyNameFromId_nothrow( _nColorPropertyId )) >>= aColor) ) + SAL_WARN("extensions.propctrlr", "impl_dialogColorChooser_throw: unable to get property " << _nColorPropertyId); + SvColorDialog aColorDlg; + aColorDlg.SetColor( aColor ); + + _rClearBeforeDialog.clear(); + weld::Window* pParent = impl_getDefaultDialogFrame_nothrow(); + if (!aColorDlg.Execute(pParent)) + return false; + + _out_rNewValue <<= aColorDlg.GetColor(); + return true; + } + + bool FormComponentPropertyHandler::impl_dialogChooseLabelControl_nothrow( Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const + { + weld::Window* pParent = impl_getDefaultDialogFrame_nothrow(); + OSelectLabelDialog dlgSelectLabel(pParent, m_xComponent); + _rClearBeforeDialog.clear(); + bool bSuccess = (RET_OK == dlgSelectLabel.run()); + if ( bSuccess ) + _out_rNewValue <<= dlgSelectLabel.GetSelected(); + return bSuccess; + } + + + Reference< XControlContainer > FormComponentPropertyHandler::impl_getContextControlContainer_nothrow() const + { + Reference< XControlContainer > xControlContext; + Any any = m_xContext->getValueByName( "ControlContext" ); + any >>= xControlContext; + return xControlContext; + } + + + bool FormComponentPropertyHandler::impl_dialogChangeTabOrder_nothrow( ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const + { + OSL_PRECOND( impl_getContextControlContainer_nothrow().is(), "FormComponentPropertyHandler::impl_dialogChangeTabOrder_nothrow: invalid control context!" ); + + Reference< XTabControllerModel > xTabControllerModel( impl_getRowSet_nothrow(), UNO_QUERY ); + TabOrderDialog aDialog(impl_getDefaultDialogFrame_nothrow(), xTabControllerModel, + impl_getContextControlContainer_nothrow(), m_xContext); + _rClearBeforeDialog.clear(); + return RET_OK == aDialog.run(); + } + + namespace + { + + //- ISQLCommandPropertyUI + + class ISQLCommandPropertyUI : public ISQLCommandAdapter + { + public: + /** returns the empty-string-terminated list of names of properties + whose UI is to be disabled while the SQL command property is + being edited. + */ + virtual OUString* getPropertiesToDisable() = 0; + }; + + + //- SQLCommandPropertyUI + + class SQLCommandPropertyUI : public ISQLCommandPropertyUI + { + protected: + explicit SQLCommandPropertyUI( const Reference< XPropertySet >& _rxObject ) + : m_xObject(_rxObject) + { + if ( !m_xObject.is() ) + throw NullPointerException(); + } + + protected: + Reference< XPropertySet > m_xObject; + }; + + + //- FormSQLCommandUI - declaration + + class FormSQLCommandUI : public SQLCommandPropertyUI + { + public: + explicit FormSQLCommandUI( const Reference< XPropertySet >& _rxForm ); + + // ISQLCommandAdapter + virtual OUString getSQLCommand() const override; + virtual bool getEscapeProcessing() const override; + virtual void setSQLCommand( const OUString& _rCommand ) const override; + virtual void setEscapeProcessing( const bool _bEscapeProcessing ) const override; + + // ISQLCommandPropertyUI + virtual OUString* getPropertiesToDisable() override; + }; + + + //- FormSQLCommandUI - implementation + + + FormSQLCommandUI::FormSQLCommandUI( const Reference< XPropertySet >& _rxForm ) + :SQLCommandPropertyUI( _rxForm ) + { + } + + + OUString FormSQLCommandUI::getSQLCommand() const + { + OUString sCommand; + if( ! (m_xObject->getPropertyValue( PROPERTY_COMMAND ) >>= sCommand) ) + SAL_WARN("extensions.propctrlr", "getSQLCommand: unable to get property " << PROPERTY_COMMAND); + return sCommand; + } + + + bool FormSQLCommandUI::getEscapeProcessing() const + { + bool bEscapeProcessing( false ); + if( ! (m_xObject->getPropertyValue( PROPERTY_ESCAPE_PROCESSING ) >>= bEscapeProcessing) ) + SAL_WARN("extensions.propctrlr", "getSQLCommand: unable to get property " << PROPERTY_ESCAPE_PROCESSING); + return bEscapeProcessing; + } + + + void FormSQLCommandUI::setSQLCommand( const OUString& _rCommand ) const + { + m_xObject->setPropertyValue( PROPERTY_COMMAND, Any( _rCommand ) ); + } + + + void FormSQLCommandUI::setEscapeProcessing( const bool _bEscapeProcessing ) const + { + m_xObject->setPropertyValue( PROPERTY_ESCAPE_PROCESSING, Any( _bEscapeProcessing ) ); + } + + + OUString* FormSQLCommandUI::getPropertiesToDisable() + { + static OUString s_aCommandProps[] = { + OUString(PROPERTY_DATASOURCE), + OUString(PROPERTY_COMMAND), + OUString(PROPERTY_COMMANDTYPE), + OUString(PROPERTY_ESCAPE_PROCESSING), + OUString() + }; + return s_aCommandProps; + } + + //- ValueListCommandUI - declaration + + class ValueListCommandUI : public SQLCommandPropertyUI + { + public: + explicit ValueListCommandUI( const Reference< XPropertySet >& _rxListOrCombo ); + + // ISQLCommandAdapter + virtual OUString getSQLCommand() const override; + virtual bool getEscapeProcessing() const override; + virtual void setSQLCommand( const OUString& _rCommand ) const override; + virtual void setEscapeProcessing( const bool _bEscapeProcessing ) const override; + + // ISQLCommandPropertyUI + virtual OUString* getPropertiesToDisable() override; + private: + mutable bool m_bPropertyValueIsList; + }; + + + //- ValueListCommandUI - implementation + + + ValueListCommandUI::ValueListCommandUI( const Reference< XPropertySet >& _rxListOrCombo ) + :SQLCommandPropertyUI( _rxListOrCombo ) + ,m_bPropertyValueIsList( false ) + { + } + + + OUString ValueListCommandUI::getSQLCommand() const + { + OUString sValue; + m_bPropertyValueIsList = false; + + // for combo boxes, the property is a mere string + Any aValue( m_xObject->getPropertyValue( PROPERTY_LISTSOURCE ) ); + if ( aValue >>= sValue ) + return sValue; + + Sequence< OUString > aValueList; + if ( aValue >>= aValueList ) + { + m_bPropertyValueIsList = true; + if ( aValueList.hasElements() ) + sValue = aValueList[0]; + return sValue; + } + + OSL_FAIL( "ValueListCommandUI::getSQLCommand: unexpected property type!" ); + return sValue; + } + + + bool ValueListCommandUI::getEscapeProcessing() const + { + ListSourceType eType = ListSourceType_SQL; + if( ! (m_xObject->getPropertyValue( PROPERTY_LISTSOURCETYPE ) >>= eType) ) + SAL_WARN("extensions.propctrlr", "getEscapeProcessing: unable to get property " << PROPERTY_LISTSOURCETYPE); + OSL_ENSURE( ( eType == ListSourceType_SQL ) || ( eType == ListSourceType_SQLPASSTHROUGH ), + "ValueListCommandUI::getEscapeProcessing: unexpected list source type!" ); + return ( eType == ListSourceType_SQL ); + } + + + void ValueListCommandUI::setSQLCommand( const OUString& _rCommand ) const + { + Any aValue; + if ( m_bPropertyValueIsList ) + aValue <<= Sequence< OUString >( &_rCommand, 1 ); + else + aValue <<= _rCommand; + m_xObject->setPropertyValue( PROPERTY_LISTSOURCE, aValue ); + } + + + void ValueListCommandUI::setEscapeProcessing( const bool _bEscapeProcessing ) const + { + m_xObject->setPropertyValue( PROPERTY_LISTSOURCETYPE, Any( + _bEscapeProcessing ? ListSourceType_SQL : ListSourceType_SQLPASSTHROUGH ) ); + } + + + OUString* ValueListCommandUI::getPropertiesToDisable() + { + static OUString s_aListSourceProps[] = { + OUString(PROPERTY_LISTSOURCETYPE), + OUString(PROPERTY_LISTSOURCE), + OUString() + }; + return s_aListSourceProps; + } + } + + + bool FormComponentPropertyHandler::impl_doDesignSQLCommand_nothrow( const Reference< XObjectInspectorUI >& _rxInspectorUI, PropertyId _nDesignForProperty ) + { + try + { + if ( m_xCommandDesigner.is() ) + { + if ( m_xCommandDesigner->isActive() ) + { + m_xCommandDesigner->raise(); + return true; + } + m_xCommandDesigner->dispose(); + m_xCommandDesigner.set( nullptr ); + } + + if ( !impl_ensureRowsetConnection_nothrow() ) + return false; + + Reference< XPropertySet > xComponentProperties( m_xComponent, UNO_SET_THROW ); + + ::rtl::Reference< ISQLCommandPropertyUI > xCommandUI; + switch ( _nDesignForProperty ) + { + case PROPERTY_ID_COMMAND: + xCommandUI = new FormSQLCommandUI( xComponentProperties ); + break; + case PROPERTY_ID_LISTSOURCE: + xCommandUI = new ValueListCommandUI( xComponentProperties ); + break; + default: + OSL_FAIL( "FormComponentPropertyHandler::OnDesignerClosed: invalid property id!" ); + return false; + } + + m_xCommandDesigner.set( new SQLCommandDesigner( m_xContext, xCommandUI, m_xRowSetConnection, LINK( this, FormComponentPropertyHandler, OnDesignerClosed ) ) ); + + DBG_ASSERT( _rxInspectorUI.is(), "FormComponentPropertyHandler::OnDesignerClosed: no access to the property browser ui!" ); + if ( m_xCommandDesigner->isActive() && _rxInspectorUI.is() ) + { + m_xBrowserUI = _rxInspectorUI; + // disable everything which would affect this property + const OUString* pToDisable = xCommandUI->getPropertiesToDisable(); + while ( !pToDisable->isEmpty() ) + { + m_xBrowserUI->enablePropertyUIElements( *pToDisable++, PropertyLineElement::All, false ); + } + + // but enable the browse button for the property itself - so it can be used to raise the query designer + OUString sPropertyName( impl_getPropertyNameFromId_nothrow( _nDesignForProperty ) ); + m_xBrowserUI->enablePropertyUIElements( sPropertyName, PropertyLineElement::PrimaryButton, true ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + return m_xCommandDesigner.is(); + } + + + IMPL_LINK_NOARG( FormComponentPropertyHandler, OnDesignerClosed, SQLCommandDesigner&, void ) + { + OSL_ENSURE( m_xBrowserUI.is() && m_xCommandDesigner.is(), "FormComponentPropertyHandler::OnDesignerClosed: too many NULLs!" ); + if ( !(m_xBrowserUI.is() && m_xCommandDesigner.is()) ) + return; + + try + { + ::rtl::Reference< ISQLCommandPropertyUI > xCommandUI( + dynamic_cast< ISQLCommandPropertyUI* >( m_xCommandDesigner->getPropertyAdapter().get() ) ); + if ( !xCommandUI.is() ) + throw NullPointerException(); + + const OUString* pToEnable = xCommandUI->getPropertiesToDisable(); + while ( !pToEnable->isEmpty() ) + { + m_xBrowserUI->enablePropertyUIElements( *pToEnable++, PropertyLineElement::All, true ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + + bool FormComponentPropertyHandler::impl_hasValidDataSourceSignature_nothrow( const Reference< XPropertySet >& _xFormProperties, bool _bAllowEmptyDataSourceName ) + { + bool bHas = false; + if ( _xFormProperties.is() ) + { + try + { + OUString sPropertyValue; + // first, we need the name of an existent data source + if ( _xFormProperties->getPropertySetInfo()->hasPropertyByName(PROPERTY_DATASOURCE) ) + _xFormProperties->getPropertyValue( PROPERTY_DATASOURCE ) >>= sPropertyValue; + bHas = ( !sPropertyValue.isEmpty() ) || _bAllowEmptyDataSourceName; + + // then, the command should not be empty + if ( bHas ) + { + if ( _xFormProperties->getPropertySetInfo()->hasPropertyByName(PROPERTY_COMMAND) ) + _xFormProperties->getPropertyValue( PROPERTY_COMMAND ) >>= sPropertyValue; + bHas = !sPropertyValue.isEmpty(); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::impl_hasValidDataSourceSignature_nothrow" ); + } + } + return bHas; + } + + OUString FormComponentPropertyHandler::impl_getDocumentURL_nothrow() const + { + OUString sURL; + try + { + Reference< XModel > xDocument( impl_getContextDocument_nothrow() ); + if ( xDocument.is() ) + sURL = xDocument->getURL(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + return sURL; + } + + ::cppu::IPropertyArrayHelper* FormComponentPropertyHandler::createArrayHelper( ) const + { + uno::Sequence< beans::Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); + + } + + ::cppu::IPropertyArrayHelper & FormComponentPropertyHandler::getInfoHelper() + { + return *getArrayHelper(); + } + + uno::Reference< beans::XPropertySetInfo > SAL_CALL FormComponentPropertyHandler::getPropertySetInfo( ) + { + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); + } + + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_FormComponentPropertyHandler_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::FormComponentPropertyHandler(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/formcomponenthandler.hxx b/extensions/source/propctrlr/formcomponenthandler.hxx new file mode 100644 index 000000000..bc7367abb --- /dev/null +++ b/extensions/source/propctrlr/formcomponenthandler.hxx @@ -0,0 +1,435 @@ +/* -*- 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 "propertyhandler.hxx" +#include "sqlcommanddesign.hxx" +#include <comphelper/uno3.hxx> +#include <comphelper/proparrhlp.hxx> +#include <comphelper/propertycontainer.hxx> +#include <com/sun/star/beans/XPropertyState.hpp> +#include <com/sun/star/sdbc/XRowSet.hpp> +#include <com/sun/star/awt/XControlContainer.hpp> +#include <connectivity/dbtools.hxx> + +#include <set> + + +namespace pcr +{ + + + //= ComponentClassification + + enum ComponentClassification + { + eFormControl, + eDialogControl, + eUnknown + }; + + + //= FormComponentPropertyHandler + + class FormComponentPropertyHandler; + typedef ::comphelper::OPropertyArrayUsageHelper<FormComponentPropertyHandler> FormComponentPropertyHandler_PROP; + /** default ->XPropertyHandler for all form components. + */ + class FormComponentPropertyHandler : public PropertyHandlerComponent, + public ::comphelper::OPropertyContainer, + public FormComponentPropertyHandler_PROP + { + private: + /// access to property states + css::uno::Reference< css::beans::XPropertyState > m_xPropertyState; + /// the parent of our component + css::uno::Reference< css::uno::XInterface > m_xObjectParent; + + /// the database connection. Owned by us if and only if we created it ourself. + mutable ::dbtools::SharedConnection m_xRowSetConnection; + css::uno::Reference< css::sdbc::XRowSet > m_xRowSet; + /** helper component encapsulating the handling for the QueryDesign component for + interactively designing an SQL command + */ + ::rtl::Reference< SQLCommandDesigner > m_xCommandDesigner; + css::uno::Reference< css::inspection::XObjectInspectorUI > m_xBrowserUI; + + /// the string indicating a "default" (VOID) value in list-like controls + OUString m_sDefaultValueString; + /// all properties to whose control's we added ->m_sDefaultValueString + std::set< OUString > m_aPropertiesWithDefListEntry; + /// type of our component + ComponentClassification m_eComponentClass; + /// is our component a (database) sub form? + bool m_bComponentIsSubForm : 1; + /// our component has a "ListSource" property + bool m_bHaveListSource : 1; + /// our component has a "Command" property + bool m_bHaveCommand : 1; + /// the class id of the component - if applicable + sal_Int16 m_nClassId; + + public: + explicit FormComponentPropertyHandler( + const css::uno::Reference< css::uno::XComponentContext >& _rxContext + ); + + DECLARE_XINTERFACE( ) + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + protected: + virtual ~FormComponentPropertyHandler() override; + + protected: + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override; + + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + // XPropertyHandler overridables + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override; + virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override; + virtual css::uno::Any SAL_CALL convertToPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rControlValue ) override; + virtual css::uno::Any SAL_CALL convertToControlValue( const OUString& _rPropertyName, const css::uno::Any& _rPropertyValue, const css::uno::Type& _rControlValueType ) override; + virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& _rPropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupersededProperties() override; + virtual css::uno::Sequence< OUString > SAL_CALL getActuatingProperties() override; + virtual css::inspection::LineDescriptor SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override; + virtual css::inspection::InteractiveSelectionResult + SAL_CALL onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, css::uno::Any& _rData, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) override; + virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) override; + virtual sal_Bool SAL_CALL suspend( sal_Bool _bSuspend ) override; + + // XComponent + virtual void SAL_CALL disposing() override; + + // PropertyHandler + virtual css::uno::Sequence< css::beans::Property > + doDescribeSupportedProperties() const override; + virtual void onNewComponent() override; + + private: + /** classifies our component, in case it's a control model, by ClassId + + Note that UNO dialog controls are also classified, though they don't have the ClassId property + */ + void impl_classifyControlModel_throw(); + + bool isReportModel() const; + + /** const-version of ->getPropertyValue + */ + css::uno::Any impl_getPropertyValue_throw( const OUString& _rPropertyName ) const; + + // some property values are faked, and not used in the way they're provided by our component + void impl_normalizePropertyValue_nothrow( css::uno::Any& _rValue, PropertyId _nPropId ) const; + + /** determines whether we should exclude a given property from our "supported properties" + */ + bool impl_shouldExcludeProperty_nothrow( const css::beans::Property& _rProperty ) const; + + /** initializes the list of field names, if we're handling a control which supports the + DataField property + */ + void impl_initFieldList_nothrow( std::vector< OUString >& rFieldNames ) const; + + /** obtains the RowSet to which our component belongs + + If the component is a RowSet itself, it's returned directly. Else, the parent + is examined for the XRowSet interface. If the parent is no XRowSet, then + a check is made whether our component is a grid control column, and if so, + the parent of the grid control is examined for the XRowSet interface. + + Normally, at least one of those methods should succeed. + */ + css::uno::Reference< css::sdbc::XRowSet > impl_getRowSet_throw( ) const; + + /** nothrow-version of ->impl_getRowSet_throw + */ + css::uno::Reference< css::sdbc::XRowSet > impl_getRowSet_nothrow( ) const; + + /** connects the row set belonging to our introspected data aware form component, + and remembers the connection in ->m_xRowSetConnection. + + If the row set already is connected, ->m_xRowSetConnection will be set, too, but + not take the ownership of the connection. + + If ->m_xRowSetConnection is already set, nothing happens, so if you want to + force creation of a connection, you need to clear ->m_xRowSetConnection. + */ + bool impl_ensureRowsetConnection_nothrow() const; + + /** fills an ->LineDescriptor with information to represent a cursor source + of our form - that is, a table, a query, or an SQL statement. + + As an example, if our form has currently a CommandType of TABLE, then the + value list in the LineDescriptor will contain a list of all tables + of the data source which the form is bound to. + + @seealso impl_fillTableNames_throw + @seealso impl_fillQueryNames_throw + */ + void impl_describeCursorSource_nothrow( + css::inspection::LineDescriptor& _out_rProperty, + const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory + ) const; + + /** describes the UI for selecting a table name + + @precond + m_xRowSetConnection is not <NULL/> + */ + void impl_fillTableNames_throw( std::vector< OUString >& _out_rNames ) const; + + /** describes the UI for selecting a query name + + @precond + m_xRowSetConnection is not <NULL/> + */ + void impl_fillQueryNames_throw( std::vector< OUString >& _out_rNames ) const; + + /** describes the UI for selecting a query name + + @precond + m_xRowSetConnection is not <NULL/> + */ + void impl_fillQueryNames_throw( const css::uno::Reference< css::container::XNameAccess >& _xQueryNames + ,std::vector< OUString >& _out_rNames + ,std::u16string_view _sName = std::u16string_view() ) const; + + /** describes the UI for selecting a ListSource (for list-like form controls) + @precond + ->m_xRowSetConnection is not <NULL/> + @precond + ->m_xComponent is not <NULL/> + */ + void impl_describeListSourceUI_throw( + css::inspection::LineDescriptor& _out_rDescriptor, + const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory + ) const; + + /** displays a database-related error to the user + */ + void impl_displaySQLError_nothrow( const ::dbtools::SQLExceptionInfo& _rErrorDescriptor ) const; + + /** let's the user chose a selection of entries from a string list, and stores this + selection in the given property + @return + <TRUE/> if and only if the user successfully changed the property + */ + bool impl_dialogListSelection_nothrow( const OUString& _rProperty, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const; + + /** executes a dialog for choosing a filter or sort criterion for a database form + @param _bFilter + <TRUE/> if the Filter property should be used, <FALSE/> if it's the Order + property + @param _out_rSelectedClause + the filter or order clause as chosen by the user + @precond + we're really inspecting a database form (well, a RowSet at least) + @return + <TRUE/> if and only if the user successfully chose a clause + */ + bool impl_dialogFilterOrSort_nothrow( bool _bFilter, OUString& _out_rSelectedClause, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const; + + /** executes a dialog which allows the user to choose the columns linking + a sub to a master form, and sets the respective MasterFields / SlaveFields + properties at the form. + @precond + we're inspecting (sub) database form + @return + <TRUE/> if and only if the user successfully enter master and slave fields + */ + bool impl_dialogLinkedFormFields_nothrow( ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const; + + /** executes a dialog which allows the user to modify the FormatKey + property of our component, by choosing a (number) format. + @precond + Our component actually has a FormatKey property. + @param _out_rNewValue + the new property value, if the user chose a new formatting + @return + <TRUE/> if and only if a new formatting has been chosen by the user. + In this case, ->_out_rNewValue is filled with the new property value + */ + bool impl_dialogFormatting_nothrow( css::uno::Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const; + + /** executes a dialog which allows to the user to change the ImageURL property + of our component by browsing for an image file. + @precond + our component actually has an ImageURL property + @param _out_rNewValue + the new property value, if the user chose a new image url + @return + <TRUE/> if and only if a new image URL has been chosen by the user. + In this case, ->_out_rNewValue is filled with the new property value + */ + bool impl_browseForImage_nothrow( css::uno::Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const; + + /** executes a dialog which allows the user to change the TargetURL property of + our component + @precond + our component actually has a TargetURL property + @param _out_rNewValue + the new property value, if the user chose a new TargetURL + @return + <TRUE/> if and only if a new TargetURL has been chosen by the user. + In this case, ->_out_rNewValue is filled with the new property value + */ + bool impl_browseForTargetURL_nothrow( css::uno::Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const; + + /** executes a dialog which allows the user to change the font, plus related properties, + of our component + @precond + our component actually has a Font property + @param _out_rNewValue + a value describing the new font, as <code>Sequence< NamedValue ></code> + @return + <TRUE/> if and only if the user successfully changed the font of our component + */ + bool impl_executeFontDialog_nothrow( css::uno::Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const; + + /** allows the user browsing for a database document + @precond + our component actually has a DataSource property + @param _out_rNewValue + the new property value, if the user chose a new DataSource + @return + <TRUE/> if and only if a new DataSource has been chosen by the user. + In this case, ->_out_rNewValue is filled with the new property value + */ + bool impl_browseForDatabaseDocument_throw( css::uno::Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const; + + /** raises a dialog which allows the user to choose a color + @param _nColorPropertyId + the ID of the color property + @param _out_rNewValue + the chosen color value + @return + <TRUE/> if and only if a color was chosen by the user + */ + bool impl_dialogColorChooser_throw( sal_Int32 _nColorPropertyId, css::uno::Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const; + + /** raises a dialog which allows the user to choose a label control for our component + @param _out_rNewValue + the chosen label control, if any + @return + <TRUE/> if and only if a label control was chosen by the user + */ + bool impl_dialogChooseLabelControl_nothrow( css::uno::Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const; + + /** raises a dialog which lets the user chose the tab order of controls of a form + @precond + we have a view control container in which our controls live + @return + <TRUE/> if and only if the user successfully changed the tab order + @seealso impl_getContextControlContainer_nothrow + */ + bool impl_dialogChangeTabOrder_nothrow( ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const; + + /** retrieves the context for controls, whose model(s) we're inspecting + + If we're inspecting a control model, this is usually part of a set of controls + and control models, where the controls live in a certain context (a ->XControlContainer). + If we know this context, we can enable additional special functionality. + + The ->XComponentContext in which we were created is examined for a value + named "ControlContext", and this value is returned. + */ + css::uno::Reference< css::awt::XControlContainer > + impl_getContextControlContainer_nothrow() const; + + /** opens a query design window for interactively designing the SQL command of a + database form + @param _rxUIUpdate + access to the property browser UI + @param _nDesignForProperty + the ID for the property for which the designer is opened + @return + <TRUE/> if the window was successfully opened, or was previously open, + <FALSE/> otherwise + */ + bool impl_doDesignSQLCommand_nothrow( + const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, + PropertyId _nDesignForProperty + ); + + /** updates a property (UI) whose state depends on more than one other property + + ->actuatingPropertyChanged is called for certain properties in whose changes + we expressed interes (->getActuatingProperty). Now such a property change can + result in simple UI updates, for instance another property being enabled or disabled. + + However, it can also result in a more complex change: The current (UI) state might + depend on the value of more than one other property. Those dependent properties (their + UI, more precisely) are updated in this method. + + @param _nPropid + the ->PropertyId of the dependent property whose UI state is to be updated + + @param _rxInspectorUI + provides access to the property browser UI. Must not be <NULL/>. + */ + void impl_updateDependentProperty_nothrow( PropertyId _nPropId, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) const; + + /** determines whether the given form has a valid data source signature. + + Valid here means that the DataSource property denotes an existing data source, and the + Command property is not empty. No check is made whether the value of the Command property + denotes an existent object, since this would be way too expensive. + + @param _xFormProperties + the form to check. Must not be <NULL/>. + @param _bAllowEmptyDataSourceName + determine whether an empty data source name is allowed (<TRUE/>), and should not + lead to rejection + */ + static bool impl_hasValidDataSourceSignature_nothrow( + const css::uno::Reference< css::beans::XPropertySet >& _xFormProperties, + bool _bAllowEmptyDataSourceName ); + + /** returns the URL of our context document + @return + */ + OUString impl_getDocumentURL_nothrow() const; + + private: + DECL_LINK( OnDesignerClosed, SQLCommandDesigner&, void ); + + private: + FormComponentPropertyHandler( const FormComponentPropertyHandler& ) = delete; + FormComponentPropertyHandler& operator=( const FormComponentPropertyHandler& ) = delete; + + private: + using ::comphelper::OPropertyContainer::addPropertyChangeListener; + using ::comphelper::OPropertyContainer::removePropertyChangeListener; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/formcontroller.cxx b/extensions/source/propctrlr/formcontroller.cxx new file mode 100644 index 000000000..6e94aa0d1 --- /dev/null +++ b/extensions/source/propctrlr/formcontroller.cxx @@ -0,0 +1,241 @@ +/* -*- 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 "formcontroller.hxx" +#include "pcrcommon.hxx" +#include "formstrings.hxx" +#include "defaultforminspection.hxx" + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/util/VetoException.hpp> +#include <cppuhelper/typeprovider.hxx> + + +namespace pcr +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::TypeClass_INTERFACE; + using ::com::sun::star::uno::TypeClass_STRING; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::inspection::XObjectInspectorModel; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::uno::XInterface; + using ::com::sun::star::beans::XPropertySetInfo; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::beans::Property; + using ::com::sun::star::uno::Any; + using ::com::sun::star::lang::IllegalArgumentException; + using ::com::sun::star::uno::Type; + using ::com::sun::star::util::VetoException; + using ::com::sun::star::beans::PropertyVetoException; + using ::com::sun::star::uno::UNO_QUERY; + + namespace PropertyAttribute = css::beans::PropertyAttribute; + + + //= FormController + + + FormController::FormController( const Reference< XComponentContext >& _rxContext, + const OUString& sImplementationName, + const css::uno::Sequence<OUString>& aSupportedServiceNames, + bool _bUseFormFormComponentHandlers ) + :OPropertyBrowserController( _rxContext ) + ,FormController_PropertyBase1( m_aBHelper ) + ,m_sImplementationName( sImplementationName ) + ,m_aSupportedServiceNames( aSupportedServiceNames ) + { + osl_atomic_increment( &m_refCount ); + { + Reference< XObjectInspectorModel > xModel( + *(new DefaultFormComponentInspectorModel( _bUseFormFormComponentHandlers )), + UNO_QUERY_THROW + ); + setInspectorModel( xModel ); + } + osl_atomic_decrement( &m_refCount ); + } + + + FormController::~FormController() + { + } + + + IMPLEMENT_FORWARD_XINTERFACE2( FormController, OPropertyBrowserController, FormController_PropertyBase1 ) + + + Sequence< Type > SAL_CALL FormController::getTypes( ) + { + ::cppu::OTypeCollection aTypes( + cppu::UnoType<XPropertySet>::get(), + cppu::UnoType<XMultiPropertySet>::get(), + cppu::UnoType<XFastPropertySet>::get(), + OPropertyBrowserController::getTypes()); + return aTypes.getTypes(); + } + + + IMPLEMENT_GET_IMPLEMENTATION_ID( FormController ) + + + OUString SAL_CALL FormController::getImplementationName( ) + { + return m_sImplementationName; + } + + + Sequence< OUString > SAL_CALL FormController::getSupportedServiceNames( ) + { + Sequence< OUString > aSupported( m_aSupportedServiceNames ); + aSupported.realloc( aSupported.getLength() + 1 ); + aSupported.getArray()[ aSupported.getLength() - 1 ] = "com.sun.star.inspection.ObjectInspector"; + return aSupported; + } + + + Reference< XPropertySetInfo > SAL_CALL FormController::getPropertySetInfo( ) + { + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); + } + + + ::cppu::IPropertyArrayHelper& SAL_CALL FormController::getInfoHelper() + { + return *getArrayHelper(); + } + + + ::cppu::IPropertyArrayHelper* FormController::createArrayHelper( ) const + { + Sequence< Property > aProps{ + Property( + PROPERTY_CURRENTPAGE, + OWN_PROPERTY_ID_CURRENTPAGE, + ::cppu::UnoType<OUString>::get(), + PropertyAttribute::TRANSIENT + ), + Property( + PROPERTY_INTROSPECTEDOBJECT, + OWN_PROPERTY_ID_INTROSPECTEDOBJECT, + cppu::UnoType<XPropertySet>::get(), + PropertyAttribute::TRANSIENT | PropertyAttribute::CONSTRAINED + ) + }; + return new ::cppu::OPropertyArrayHelper( aProps ); + } + + + sal_Bool SAL_CALL FormController::convertFastPropertyValue( Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, const Any& rValue ) + { + switch ( nHandle ) + { + case OWN_PROPERTY_ID_INTROSPECTEDOBJECT: + if ( rValue.getValueTypeClass() != TypeClass_INTERFACE ) + throw IllegalArgumentException(); + break; + case OWN_PROPERTY_ID_CURRENTPAGE: + if ( rValue.getValueTypeClass() != TypeClass_STRING ) + throw IllegalArgumentException(); + break; + } + + getFastPropertyValue( rOldValue, nHandle ); + rConvertedValue = rValue; + return true; + } + + + void SAL_CALL FormController::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue) + { + switch ( _nHandle ) + { + case OWN_PROPERTY_ID_INTROSPECTEDOBJECT: + { + Reference< XObjectInspectorModel > xModel( getInspectorModel() ); + if ( xModel.is() ) + { + try + { + m_xCurrentInspectee.set( _rValue, UNO_QUERY ); + Sequence< Reference< XInterface > > aObjects; + if ( m_xCurrentInspectee.is() ) + { + aObjects = { m_xCurrentInspectee }; + } + + Reference< XObjectInspector > xInspector( *this, UNO_QUERY_THROW ); + xInspector->inspect( aObjects ); + } + catch( const VetoException& e ) + { + throw PropertyVetoException( e.Message, e.Context ); + } + } + } + break; + case OWN_PROPERTY_ID_CURRENTPAGE: + restoreViewData( _rValue ); + break; + } + } + + + void SAL_CALL FormController::getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle ) const + { + switch ( nHandle ) + { + case OWN_PROPERTY_ID_INTROSPECTEDOBJECT: + rValue <<= m_xCurrentInspectee; + break; + + case OWN_PROPERTY_ID_CURRENTPAGE: + rValue = const_cast< FormController* >( this )->getViewData(); + break; + } + } + + + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_FormController_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::FormController( context, + "org.openoffice.comp.extensions.FormController", + { "com.sun.star.form.PropertyBrowserController" }, + true ) ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_DialogController_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::FormController( context, + "org.openoffice.comp.extensions.DialogController", + { "com.sun.star.awt.PropertyBrowserController" }, + false ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/formcontroller.hxx b/extensions/source/propctrlr/formcontroller.hxx new file mode 100644 index 000000000..8c285160e --- /dev/null +++ b/extensions/source/propctrlr/formcontroller.hxx @@ -0,0 +1,103 @@ +/* -*- 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 "propcontroller.hxx" + +#include <cppuhelper/propshlp.hxx> +#include <comphelper/proparrhlp.hxx> +#include <comphelper/uno3.hxx> + + +namespace pcr +{ + + + //= FormController + + class FormController; + typedef ::cppu::OPropertySetHelper FormController_PropertyBase1; + typedef ::comphelper::OPropertyArrayUsageHelper< FormController > FormController_PropertyBase2; + + /** Legacy implementation of com.sun.star.form.PropertyBrowserController + + Nowadays only a wrapper around an ObjectInspector using a + DefaultFormComponentInspectorModel. + */ + class FormController :public OPropertyBrowserController + ,public FormController_PropertyBase1 + ,public FormController_PropertyBase2 + { + private: + css::uno::Reference< css::beans::XPropertySet > + m_xCurrentInspectee; + OUString m_sImplementationName; + css::uno::Sequence<OUString> m_aSupportedServiceNames; + public: + FormController( + const css::uno::Reference< css::uno::XComponentContext >& _rxContext, + const OUString& sImplementName, + const css::uno::Sequence<OUString>& aSupportedServiceNames, + bool _bUseFormFormComponentHandlers + ); + + protected: + virtual ~FormController() override; + + DECLARE_XINTERFACE() + DECLARE_XTYPEPROVIDER() + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XPropertySet and friends + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) 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; + virtual void SAL_CALL getFastPropertyValue( + css::uno::Any& rValue, sal_Int32 nHandle + ) const override; + private: + using FormController_PropertyBase1::getFastPropertyValue; + }; + + + //= DialogController + + /** Legacy implementation of com.sun.star.awt.PropertyBrowserController + */ + class DialogController + { + private: + DialogController( const DialogController& ) = delete; + DialogController& operator=( const DialogController& ) = delete; + }; + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/formgeometryhandler.cxx b/extensions/source/propctrlr/formgeometryhandler.cxx new file mode 100644 index 000000000..26c432405 --- /dev/null +++ b/extensions/source/propctrlr/formgeometryhandler.cxx @@ -0,0 +1,820 @@ +/* -*- 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 "propertyhandler.hxx" +#include "formmetadata.hxx" +#include "formstrings.hxx" +#include "handlerhelper.hxx" +#include "cellbindinghelper.hxx" + +#include <com/sun/star/inspection/XObjectInspectorUI.hpp> +#include <com/sun/star/awt/XControlModel.hpp> +#include <com/sun/star/drawing/XControlShape.hpp> +#include <com/sun/star/container/XMap.hpp> +#include <com/sun/star/inspection/XNumericControl.hpp> +#include <com/sun/star/util/MeasureUnit.hpp> +#include <com/sun/star/text/TextContentAnchorType.hpp> +#include <com/sun/star/lang/NullPointerException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sheet/XSpreadsheet.hpp> +#include <com/sun/star/table/XColumnRowRange.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/form/XGridColumnFactory.hpp> + +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/interfacecontainer.hxx> +#include <comphelper/interfacecontainer2.hxx> +#include <comphelper/componentbase.hxx> +#include <rtl/ref.hxx> +#include <tools/diagnose_ex.h> + +namespace pcr +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::XInterface; + using ::com::sun::star::uno::UNO_QUERY; + 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::uno::RuntimeException; + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::beans::Property; + using ::com::sun::star::awt::XControlModel; + using ::com::sun::star::drawing::XControlShape; + using ::com::sun::star::container::XMap; + using ::com::sun::star::inspection::LineDescriptor; + using ::com::sun::star::inspection::XPropertyControlFactory; + using ::com::sun::star::lang::NullPointerException; + using ::com::sun::star::beans::Optional; + using ::com::sun::star::inspection::XNumericControl; + using ::com::sun::star::drawing::XShape; + using ::com::sun::star::beans::PropertyChangeEvent; + using ::com::sun::star::lang::EventObject; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::beans::XPropertyChangeListener; + using ::com::sun::star::text::TextContentAnchorType; + using ::com::sun::star::text::TextContentAnchorType_AT_PARAGRAPH; + using ::com::sun::star::text::TextContentAnchorType_AS_CHARACTER; + using ::com::sun::star::beans::XPropertySetInfo; + using ::com::sun::star::inspection::XObjectInspectorUI; + using ::com::sun::star::lang::XServiceInfo; + using ::com::sun::star::sheet::XSpreadsheet; + using ::com::sun::star::table::XColumnRowRange; + using ::com::sun::star::table::XTableColumns; + using ::com::sun::star::table::XTableRows; + using ::com::sun::star::container::XIndexAccess; + using ::com::sun::star::container::XChild; + using ::com::sun::star::form::XGridColumnFactory; + + namespace MeasureUnit = css::util::MeasureUnit; + + #define ANCHOR_TO_SHEET 0 + #define ANCHOR_TO_CELL 1 + + + //= BroadcastHelperBase + + namespace { + + class BroadcastHelperBase + { + protected: + explicit BroadcastHelperBase( ::osl::Mutex& _rMutex ) + :maBHelper( _rMutex ) + { + } + + protected: + ::cppu::OBroadcastHelper& getBroadcastHelper() { return maBHelper; } + + private: + ::cppu::OBroadcastHelper maBHelper; + }; + + } + + //= ShapeGeometryChangeNotifier - declaration + + /** helper class to work around the ...unfortunate implementation of property change broadcasts + in the XShape implementation, which broadcasts way too generous and unspecified + */ + typedef ::comphelper::ComponentBase ShapeGeometryChangeNotifier_CBase; + typedef ::cppu::WeakImplHelper < css::beans::XPropertyChangeListener + > ShapeGeometryChangeNotifier_IBase; + + namespace { + + class ShapeGeometryChangeNotifier :public BroadcastHelperBase + ,public ShapeGeometryChangeNotifier_CBase + ,public ShapeGeometryChangeNotifier_IBase + { + public: + ShapeGeometryChangeNotifier( ::cppu::OWeakObject& _rParent, ::osl::Mutex& _rParentMutex, const Reference< XShape >& _shape ) + :BroadcastHelperBase( _rParentMutex ) + ,ShapeGeometryChangeNotifier_CBase( BroadcastHelperBase::getBroadcastHelper(), ::comphelper::ComponentBase::NoInitializationNeeded() ) + ,m_rParent( _rParent ) + ,m_aPropertyChangeListeners( _rParentMutex ) + ,m_xShape( _shape ) + { + ENSURE_OR_THROW( m_xShape.is(), "illegal shape!" ); + impl_init_nothrow(); + } + + // property change broadcasting + void addPropertyChangeListener( const Reference< XPropertyChangeListener >& _listener ) + { + m_aPropertyChangeListeners.addInterface( _listener ); + } + void removePropertyChangeListener( const Reference< XPropertyChangeListener >& _listener ) + { + m_aPropertyChangeListeners.removeInterface( _listener ); + } + + // XComponent equivalent + void dispose() + { + ::osl::MutexGuard aGuard( getMutex() ); + impl_dispose_nothrow(); + } + + // XInterface + virtual void SAL_CALL acquire( ) noexcept override + { + m_rParent.acquire(); + } + + virtual void SAL_CALL release( ) noexcept override + { + m_rParent.release(); + } + + // XPropertyChangeListener + virtual void SAL_CALL propertyChange( const PropertyChangeEvent& _event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const EventObject& _event ) override; + + protected: + virtual ~ShapeGeometryChangeNotifier() override + { + if ( !getBroadcastHelper().bDisposed ) + { + acquire(); + dispose(); + } + } + + protected: + ::cppu::OBroadcastHelper& getBroadcastHelper() { return BroadcastHelperBase::getBroadcastHelper(); } + + private: + void impl_init_nothrow(); + void impl_dispose_nothrow(); + + private: + ::cppu::OWeakObject& m_rParent; + ::comphelper::OInterfaceContainerHelper2 m_aPropertyChangeListeners; + Reference< XShape > m_xShape; + }; + + } + + //= FormGeometryHandler - declaration + + namespace { + + class FormGeometryHandler; + + } + + /** a property handler for any virtual string properties + */ + + namespace { + + class FormGeometryHandler : public PropertyHandlerComponent + { + public: + explicit FormGeometryHandler( + const Reference< XComponentContext >& _rxContext + ); + + protected: + virtual ~FormGeometryHandler() override; + + protected: + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override; + + // XPropertyHandler overriables + virtual Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override; + virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const Any& _rValue ) override; + virtual LineDescriptor SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override; + virtual void SAL_CALL addPropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override; + virtual Sequence< OUString > SAL_CALL getActuatingProperties( ) override; + virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& _rOldValue, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) override; + + // OComponentHandler overridables + virtual void SAL_CALL disposing() override; + + // PropertyHandler overridables + virtual Sequence< Property > doDescribeSupportedProperties() const override; + + protected: + virtual void onNewComponent() override; + + private: + bool impl_haveTextAnchorType_nothrow() const; + bool impl_haveSheetAnchorType_nothrow() const; + void impl_setSheetAnchorType_nothrow( const sal_Int32 _nAnchorType ) const; + + private: + Reference< XControlShape > m_xAssociatedShape; + Reference< XPropertySet > m_xShapeProperties; + ::rtl::Reference< ShapeGeometryChangeNotifier > m_xChangeNotifier; + }; + + } + + //= FormGeometryHandler - implementation + + + FormGeometryHandler::FormGeometryHandler( const Reference< XComponentContext >& _rxContext ) + :PropertyHandlerComponent( _rxContext ) + { + } + + + FormGeometryHandler::~FormGeometryHandler( ) + { + if ( !rBHelper.bDisposed ) + { + acquire(); + dispose(); + } + + } + + + void FormGeometryHandler::onNewComponent() + { + if ( m_xChangeNotifier.is() ) + { + m_xChangeNotifier->dispose(); + m_xChangeNotifier.clear(); + } + m_xAssociatedShape.clear(); + m_xShapeProperties.clear(); + + PropertyHandlerComponent::onNewComponent(); + + try + { + Reference< XControlModel > xControlModel( m_xComponent, UNO_QUERY ); + if ( xControlModel.is() ) + { + // do not ask the map for shapes for grid control columns... + Reference< XChild > xCompChild( m_xComponent, UNO_QUERY_THROW ); + Reference< XGridColumnFactory > xCheckGrid( xCompChild->getParent(), UNO_QUERY ); + if ( !xCheckGrid.is() ) + { + Reference< XMap > xControlMap; + Any any = m_xContext->getValueByName( "ControlShapeAccess" ); + any >>= xControlMap; + m_xAssociatedShape.set( xControlMap->get( Any( xControlModel ) ), UNO_QUERY_THROW ); + m_xShapeProperties.set( m_xAssociatedShape, UNO_QUERY_THROW ); + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + + if ( m_xAssociatedShape.is() ) + m_xChangeNotifier = new ShapeGeometryChangeNotifier( *this, m_aMutex, m_xAssociatedShape ); + } + + + OUString FormGeometryHandler::getImplementationName( ) + { + return "com.sun.star.comp.extensions.FormGeometryHandler"; + } + + + Sequence< OUString > FormGeometryHandler::getSupportedServiceNames( ) + { + return { "com.sun.star.form.inspection.FormGeometryHandler" }; + } + + + Any SAL_CALL FormGeometryHandler::getPropertyValue( const OUString& _rPropertyName ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + ENSURE_OR_THROW2( m_xAssociatedShape.is(), "internal error: properties, but no shape!", *this ); + ENSURE_OR_THROW2( m_xShapeProperties.is(), "internal error: no shape properties!", *this ); + + Any aReturn; + try + { + switch ( nPropId ) + { + case PROPERTY_ID_POSITIONX: + aReturn <<= m_xAssociatedShape->getPosition().X; + break; + case PROPERTY_ID_POSITIONY: + aReturn <<= m_xAssociatedShape->getPosition().Y; + break; + case PROPERTY_ID_WIDTH: + aReturn <<= m_xAssociatedShape->getSize().Width; + break; + case PROPERTY_ID_HEIGHT: + aReturn <<= m_xAssociatedShape->getSize().Height; + break; + case PROPERTY_ID_TEXT_ANCHOR_TYPE: + aReturn = m_xShapeProperties->getPropertyValue( PROPERTY_ANCHOR_TYPE ); + OSL_ENSURE( aReturn.hasValue(), "FormGeometryHandler::getPropertyValue: illegal anchor type!" ); + break; + case PROPERTY_ID_SHEET_ANCHOR_TYPE: + { + Reference< XSpreadsheet > xAnchorSheet( m_xShapeProperties->getPropertyValue( PROPERTY_ANCHOR ), UNO_QUERY ); + aReturn <<= sal_Int32( xAnchorSheet.is() ? ANCHOR_TO_SHEET : ANCHOR_TO_CELL ); + } + break; + + default: + OSL_FAIL( "FormGeometryHandler::getPropertyValue: huh?" ); + break; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + return aReturn; + } + + + void SAL_CALL FormGeometryHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + ENSURE_OR_THROW2( m_xAssociatedShape.is(), "internal error: properties, but no shape!", *this ); + ENSURE_OR_THROW2( m_xShapeProperties.is(), "internal error: properties, but no shape!", *this ); + + try + { + switch ( nPropId ) + { + case PROPERTY_ID_POSITIONX: + case PROPERTY_ID_POSITIONY: + { + sal_Int32 nPosition(0); + OSL_VERIFY( _rValue >>= nPosition ); + + css::awt::Point aPos( m_xAssociatedShape->getPosition() ); + if ( nPropId == PROPERTY_ID_POSITIONX ) + aPos.X = nPosition; + else + aPos.Y = nPosition; + m_xAssociatedShape->setPosition( aPos ); + } + break; + + case PROPERTY_ID_WIDTH: + case PROPERTY_ID_HEIGHT: + { + sal_Int32 nSize(0); + OSL_VERIFY( _rValue >>= nSize ); + + css::awt::Size aSize( m_xAssociatedShape->getSize() ); + if ( nPropId == PROPERTY_ID_WIDTH ) + aSize.Width = nSize; + else + aSize.Height = nSize; + m_xAssociatedShape->setSize( aSize ); + } + break; + + case PROPERTY_ID_TEXT_ANCHOR_TYPE: + m_xShapeProperties->setPropertyValue( PROPERTY_ANCHOR_TYPE, _rValue ); + break; + + case PROPERTY_ID_SHEET_ANCHOR_TYPE: + { + sal_Int32 nSheetAnchorType = 0; + OSL_VERIFY( _rValue >>= nSheetAnchorType ); + impl_setSheetAnchorType_nothrow( nSheetAnchorType ); + } + break; + + default: + OSL_FAIL( "FormGeometryHandler::getPropertyValue: huh?" ); + break; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + + LineDescriptor SAL_CALL FormGeometryHandler::describePropertyLine( const OUString& _rPropertyName, + const Reference< XPropertyControlFactory >& _rxControlFactory ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + LineDescriptor aLineDesc( PropertyHandler::describePropertyLine( _rPropertyName, _rxControlFactory ) ); + try + { + bool bIsSize = false; + switch ( nPropId ) + { + case PROPERTY_ID_WIDTH: + case PROPERTY_ID_HEIGHT: + bIsSize = true; + [[fallthrough]]; + case PROPERTY_ID_POSITIONX: + case PROPERTY_ID_POSITIONY: + { + Optional< double > aZero( true, 0 ); + Optional< double > aValueNotPresent( false, 0 ); + aLineDesc.Control = PropertyHandlerHelper::createNumericControl( + _rxControlFactory, 2, bIsSize ? aZero : aValueNotPresent, aValueNotPresent ); + + Reference< XNumericControl > xNumericControl( aLineDesc.Control, UNO_QUERY_THROW ); + xNumericControl->setValueUnit( MeasureUnit::MM_100TH ); + xNumericControl->setDisplayUnit( impl_getDocumentMeasurementUnit_throw() ); + } + break; + + case PROPERTY_ID_TEXT_ANCHOR_TYPE: + case PROPERTY_ID_SHEET_ANCHOR_TYPE: + // default handling from PropertyHandler is sufficient + break; + + default: + OSL_FAIL( "FormGeometryHandler::describePropertyLine: huh?" ); + break; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + return aLineDesc; + } + + + void SAL_CALL FormGeometryHandler::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _listener ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + OSL_PRECOND( m_xChangeNotifier.is(), "FormGeometryHandler::addPropertyChangeListener: no notified, implies no shape!?" ); + if ( m_xChangeNotifier.is() ) + m_xChangeNotifier->addPropertyChangeListener( _listener ); + } + + + void SAL_CALL FormGeometryHandler::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _listener ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + OSL_PRECOND( m_xChangeNotifier.is(), "FormGeometryHandler::removePropertyChangeListener: no notified, implies no shape!?" ); + if ( m_xChangeNotifier.is() ) + m_xChangeNotifier->removePropertyChangeListener( _listener ); + } + + + Sequence< OUString > SAL_CALL FormGeometryHandler::getActuatingProperties( ) + { + Sequence< OUString > aInterestedIn { PROPERTY_TEXT_ANCHOR_TYPE }; + return aInterestedIn; + } + + + void SAL_CALL FormGeometryHandler::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool /*_bFirstTimeInit*/ ) + { + if ( !_rxInspectorUI.is() ) + throw NullPointerException(); + + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nActuatingPropId( impl_getPropertyId_nothrow( _rActuatingPropertyName ) ); + + switch ( nActuatingPropId ) + { + case PROPERTY_ID_TEXT_ANCHOR_TYPE: + { + TextContentAnchorType eAnchorType( TextContentAnchorType_AT_PARAGRAPH ); + OSL_VERIFY( _rNewValue >>= eAnchorType ); + _rxInspectorUI->enablePropertyUI( PROPERTY_POSITIONX, eAnchorType != TextContentAnchorType_AS_CHARACTER ); + } + break; + case -1: + throw RuntimeException(); + break; + default: + OSL_FAIL( "FormGeometryHandler::actuatingPropertyChanged: not registered for this property!" ); + break; + } + } + + + Sequence< Property > FormGeometryHandler::doDescribeSupportedProperties() const + { + if ( !m_xAssociatedShape.is() ) + return Sequence< Property >(); + + std::vector< Property > aProperties; + + addInt32PropertyDescription( aProperties, PROPERTY_POSITIONX ); + addInt32PropertyDescription( aProperties, PROPERTY_POSITIONY ); + addInt32PropertyDescription( aProperties, PROPERTY_WIDTH ); + addInt32PropertyDescription( aProperties, PROPERTY_HEIGHT ); + + if ( impl_haveTextAnchorType_nothrow() ) + implAddPropertyDescription( aProperties, PROPERTY_TEXT_ANCHOR_TYPE, ::cppu::UnoType< TextContentAnchorType >::get() ); + + if ( impl_haveSheetAnchorType_nothrow() ) + addInt32PropertyDescription( aProperties, PROPERTY_SHEET_ANCHOR_TYPE ); + + return comphelper::containerToSequence(aProperties); + } + + + void SAL_CALL FormGeometryHandler::disposing() + { + PropertyHandlerComponent::disposing(); + + if ( m_xChangeNotifier.is() ) + { + m_xChangeNotifier->dispose(); + m_xChangeNotifier.clear(); + } + } + + + bool FormGeometryHandler::impl_haveTextAnchorType_nothrow() const + { + ENSURE_OR_THROW( m_xShapeProperties.is(), "not to be called without shape properties" ); + try + { + Reference< XPropertySetInfo > xPSI( m_xShapeProperties->getPropertySetInfo(), UNO_SET_THROW ); + if ( xPSI->hasPropertyByName( PROPERTY_ANCHOR_TYPE ) ) + return true; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + return false; + } + + + bool FormGeometryHandler::impl_haveSheetAnchorType_nothrow() const + { + ENSURE_OR_THROW( m_xShapeProperties.is(), "not to be called without shape properties" ); + try + { + Reference< XPropertySetInfo > xPSI( m_xShapeProperties->getPropertySetInfo(), UNO_SET_THROW ); + if ( !xPSI->hasPropertyByName( PROPERTY_ANCHOR ) ) + return false; + Reference< XServiceInfo > xSI( m_xAssociatedShape, UNO_QUERY_THROW ); + if ( xSI->supportsService("com.sun.star.sheet.Shape") ) + return true; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + return false; + } + + + namespace + { + sal_Int32 lcl_getLowerBoundRowOrColumn( const Reference< XIndexAccess >& _rxRowsOrColumns, const bool _bRows, + const css::awt::Point& _rRelativePosition ) + { + sal_Int32 nAccumulated = 0; + + const sal_Int32& rRelativePos = _bRows ? _rRelativePosition.Y : _rRelativePosition.X; + + sal_Int32 nElements = _rxRowsOrColumns->getCount(); + sal_Int32 currentPos = 0; + for ( currentPos=0; currentPos<nElements; ++currentPos ) + { + Reference< XPropertySet > xRowOrColumn( _rxRowsOrColumns->getByIndex( currentPos ), UNO_QUERY_THROW ); + + bool bIsVisible = true; + OSL_VERIFY( xRowOrColumn->getPropertyValue( PROPERTY_IS_VISIBLE ) >>= bIsVisible ); + if ( !bIsVisible ) + continue; + + sal_Int32 nHeightOrWidth( 0 ); + OSL_VERIFY( xRowOrColumn->getPropertyValue( _bRows ? OUString(PROPERTY_HEIGHT) : OUString(PROPERTY_WIDTH) ) >>= nHeightOrWidth ); + + if ( nAccumulated + nHeightOrWidth > rRelativePos ) + break; + + nAccumulated += nHeightOrWidth; + } + + return currentPos; + } + } + + + void FormGeometryHandler::impl_setSheetAnchorType_nothrow( const sal_Int32 _nAnchorType ) const + { + ENSURE_OR_THROW( m_xShapeProperties.is(), "illegal to be called without shape properties." ); + try + { + CellBindingHelper aHelper( m_xComponent, impl_getContextDocument_nothrow() ); + // find the sheet which the control belongs to + Reference< XSpreadsheet > xSheet; + aHelper.getControlSheetIndex( xSheet ); + + switch ( _nAnchorType ) + { + case ANCHOR_TO_SHEET: + OSL_ENSURE( xSheet.is(), + "FormGeometryHandler::impl_setSheetAnchorType_nothrow: sheet not found!" ); + if ( xSheet.is() ) + { + css::awt::Point aPreservePosition( m_xAssociatedShape->getPosition() ); + m_xShapeProperties->setPropertyValue( PROPERTY_ANCHOR, Any( xSheet ) ); + m_xAssociatedShape->setPosition( aPreservePosition ); + } + break; + + case ANCHOR_TO_CELL: + { + Reference< XColumnRowRange > xColsRows( xSheet, UNO_QUERY_THROW ); + + // get the current anchor + Reference< XSpreadsheet > xCurrentAnchor; + OSL_VERIFY( m_xShapeProperties->getPropertyValue( PROPERTY_ANCHOR ) >>= xCurrentAnchor ); + OSL_ENSURE( xCurrentAnchor.is(), "FormGeometryHandler::impl_setSheetAnchorType_nothrow: only to be called when currently anchored to a sheet!" ); + + // get the current position + css::awt::Point aRelativePosition( m_xAssociatedShape->getPosition() ); + + Reference< XTableColumns > xCols( xColsRows->getColumns(), UNO_SET_THROW ); + sal_Int32 nNewAnchorCol = lcl_getLowerBoundRowOrColumn( xCols, false, aRelativePosition ); + + Reference< XTableRows > xRows( xColsRows->getRows(), UNO_SET_THROW ); + sal_Int32 nNewAnchorRow = lcl_getLowerBoundRowOrColumn( xRows, true, aRelativePosition ); + + Any aNewAnchorCell( xSheet->getCellByPosition( nNewAnchorCol, nNewAnchorRow ) ); + m_xShapeProperties->setPropertyValue( PROPERTY_ANCHOR, aNewAnchorCell ); + } + break; + + default: + OSL_FAIL( "FormGeometryHandler::impl_setSheetAnchorType_nothrow: illegal anchor type!" ); + break; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + + //= ShapeGeometryChangeNotifier - implementation + + namespace + { + struct EventTranslation + { + OUString sPropertyName; + Any aNewPropertyValue; + + EventTranslation( const OUString& _propertyName, const Any& _newPropertyValue ) + :sPropertyName( _propertyName ) + ,aNewPropertyValue( _newPropertyValue ) + { + } + }; + } + + + void SAL_CALL ShapeGeometryChangeNotifier::propertyChange( const PropertyChangeEvent& _event ) + { + ::comphelper::ComponentMethodGuard aGuard( *this ); + + std::vector< EventTranslation > aEventTranslations; + aEventTranslations.reserve(2); + + if ( _event.PropertyName == "Position" ) + { + css::awt::Point aPos = m_xShape->getPosition(); + aEventTranslations.push_back( EventTranslation( PROPERTY_POSITIONX, Any( aPos.X ) ) ); + aEventTranslations.push_back( EventTranslation( PROPERTY_POSITIONY, Any( aPos.Y ) ) ); + } + else if ( _event.PropertyName == "Size" ) + { + css::awt::Size aSize = m_xShape->getSize(); + aEventTranslations.push_back( EventTranslation( PROPERTY_WIDTH, Any( aSize.Width ) ) ); + aEventTranslations.push_back( EventTranslation( PROPERTY_HEIGHT, Any( aSize.Height ) ) ); + } + else if ( _event.PropertyName == PROPERTY_ANCHOR_TYPE ) + { + aEventTranslations.push_back( EventTranslation( PROPERTY_TEXT_ANCHOR_TYPE, _event.NewValue ) ); + } + else if ( _event.PropertyName == PROPERTY_ANCHOR ) + { + aEventTranslations.push_back( EventTranslation( PROPERTY_SHEET_ANCHOR_TYPE, _event.NewValue ) ); + } + + PropertyChangeEvent aTranslatedEvent( _event ); + aTranslatedEvent.Source = m_rParent; + + aGuard.clear(); + for (auto const& eventTranslation : aEventTranslations) + { + aTranslatedEvent.PropertyName = eventTranslation.sPropertyName; + aTranslatedEvent.NewValue = eventTranslation.aNewPropertyValue; + m_aPropertyChangeListeners.notifyEach( &XPropertyChangeListener::propertyChange, aTranslatedEvent ); + } + } + + + void SAL_CALL ShapeGeometryChangeNotifier::disposing( const EventObject& /*_event*/ ) + { + ::comphelper::ComponentMethodGuard aGuard( *this ); + impl_dispose_nothrow(); + } + + + void ShapeGeometryChangeNotifier::impl_init_nothrow() + { + osl_atomic_increment( &m_refCount ); + try + { + Reference< XPropertySet > xShapeProperties( m_xShape, UNO_QUERY_THROW ); + xShapeProperties->addPropertyChangeListener( OUString(), this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + osl_atomic_decrement( &m_refCount ); + } + + + void ShapeGeometryChangeNotifier::impl_dispose_nothrow() + { + try + { + Reference< XPropertySet > xShapeProperties( m_xShape, UNO_QUERY_THROW ); + xShapeProperties->removePropertyChangeListener( OUString(), this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + + getBroadcastHelper().bDisposed = true; + } + + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_FormGeometryHandler_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::FormGeometryHandler(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/formlinkdialog.cxx b/extensions/source/propctrlr/formlinkdialog.cxx new file mode 100644 index 000000000..319ade956 --- /dev/null +++ b/extensions/source/propctrlr/formlinkdialog.cxx @@ -0,0 +1,630 @@ +/* -*- 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 "formlinkdialog.hxx" + +#include "modulepcr.hxx" +#include <strings.hrc> +#include "formstrings.hxx" +#include <sal/log.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/svapp.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/dbexception.hxx> +#include <comphelper/sequence.hxx> + +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbcx/XKeysSupplier.hpp> +#include <com/sun/star/sdbcx/KeyType.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdbc/XRowSet.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/SQLContext.hpp> +#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> + +namespace pcr +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + 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; + + + //= FieldLinkRow + + class FieldLinkRow + { + private: + std::unique_ptr<weld::ComboBox> m_xDetailColumn; + std::unique_ptr<weld::ComboBox> m_xMasterColumn; + + Link<FieldLinkRow&,void> m_aLinkChangeHandler; + + public: + FieldLinkRow(std::unique_ptr<weld::ComboBox> xDetailColumn, + std::unique_ptr<weld::ComboBox> xMasterColumn); + + + void SetLinkChangeHandler( const Link<FieldLinkRow&,void>& _rHdl ) { m_aLinkChangeHandler = _rHdl; } + + enum LinkParticipant + { + eDetailField, + eMasterField + }; + /** retrieves the selected field name for either the master or the detail field + @return <TRUE/> if and only a valid field is selected + */ + bool GetFieldName( LinkParticipant _eWhich, OUString& /* [out] */ _rName ) const; + void SetFieldName( LinkParticipant _eWhich, const OUString& _rName ); + + void fillList( LinkParticipant _eWhich, const Sequence< OUString >& _rFieldNames ); + + void Show() + { + m_xDetailColumn->show(); + m_xMasterColumn->show(); + } + + private: + DECL_LINK( OnFieldNameChanged, weld::ComboBox&, void ); + }; + + + FieldLinkRow::FieldLinkRow(std::unique_ptr<weld::ComboBox> xDetailColumn, + std::unique_ptr<weld::ComboBox> xMasterColumn) + : m_xDetailColumn(std::move(xDetailColumn)) + , m_xMasterColumn(std::move(xMasterColumn)) + { + m_xDetailColumn->connect_changed( LINK( this, FieldLinkRow, OnFieldNameChanged ) ); + m_xMasterColumn->connect_changed( LINK( this, FieldLinkRow, OnFieldNameChanged ) ); + } + + void FieldLinkRow::fillList( LinkParticipant _eWhich, const Sequence< OUString >& _rFieldNames ) + { + weld::ComboBox* pBox = ( _eWhich == eDetailField ) ? m_xDetailColumn.get() : m_xMasterColumn.get(); + + const OUString* pFieldName = _rFieldNames.getConstArray(); + const OUString* pFieldNameEnd = pFieldName + _rFieldNames.getLength(); + for ( ; pFieldName != pFieldNameEnd; ++pFieldName ) + pBox->append_text( *pFieldName ); + } + + bool FieldLinkRow::GetFieldName( LinkParticipant _eWhich, OUString& /* [out] */ _rName ) const + { + const weld::ComboBox* pBox = ( _eWhich == eDetailField ) ? m_xDetailColumn.get() : m_xMasterColumn.get(); + _rName = pBox->get_active_text(); + return !_rName.isEmpty(); + } + + void FieldLinkRow::SetFieldName( LinkParticipant _eWhich, const OUString& _rName ) + { + weld::ComboBox* pBox = ( _eWhich == eDetailField ) ? m_xDetailColumn.get() : m_xMasterColumn.get(); + pBox->set_entry_text( _rName ); + } + + IMPL_LINK_NOARG( FieldLinkRow, OnFieldNameChanged, weld::ComboBox&, void ) + { + m_aLinkChangeHandler.Call( *this ); + } + + //= FormLinkDialog + + FormLinkDialog::FormLinkDialog(weld::Window* _pParent, const Reference< XPropertySet >& _rxDetailForm, + const Reference< XPropertySet >& _rxMasterForm, const Reference< XComponentContext >& _rxContext, + const OUString& _sExplanation, + const OUString& _sDetailLabel, + const OUString& _sMasterLabel) + : GenericDialogController(_pParent, "modules/spropctrlr/ui/formlinksdialog.ui", "FormLinks") + , m_xContext ( _rxContext ) + , m_xDetailForm( _rxDetailForm ) + , m_xMasterForm( _rxMasterForm ) + , m_sDetailLabel(_sDetailLabel) + , m_sMasterLabel(_sMasterLabel) + , m_xExplanation(m_xBuilder->weld_label("explanationLabel")) + , m_xDetailLabel(m_xBuilder->weld_label("detailLabel")) + , m_xMasterLabel(m_xBuilder->weld_label("masterLabel")) + , m_xRow1(std::make_unique<FieldLinkRow>(m_xBuilder->weld_combo_box("detailCombobox1"), + m_xBuilder->weld_combo_box("masterCombobox1"))) + , m_xRow2(std::make_unique<FieldLinkRow>(m_xBuilder->weld_combo_box("detailCombobox2"), + m_xBuilder->weld_combo_box("masterCombobox2"))) + , m_xRow3(std::make_unique<FieldLinkRow>(m_xBuilder->weld_combo_box("detailCombobox3"), + m_xBuilder->weld_combo_box("masterCombobox3"))) + , m_xRow4(std::make_unique<FieldLinkRow>(m_xBuilder->weld_combo_box("detailCombobox4"), + m_xBuilder->weld_combo_box("masterCombobox4"))) + , m_xOK(m_xBuilder->weld_button("ok")) + , m_xSuggest(m_xBuilder->weld_button("suggestButton")) + { + m_xRow1->Show(); + m_xRow2->Show(); + m_xRow3->Show(); + m_xRow4->Show(); + m_xDialog->set_size_request(600, -1); + + if ( !_sExplanation.isEmpty() ) + m_xExplanation->set_label(_sExplanation); + + m_xSuggest->connect_clicked(LINK(this, FormLinkDialog, OnSuggest)); + m_xRow1->SetLinkChangeHandler( LINK( this, FormLinkDialog, OnFieldChanged ) ); + m_xRow2->SetLinkChangeHandler( LINK( this, FormLinkDialog, OnFieldChanged ) ); + m_xRow3->SetLinkChangeHandler( LINK( this, FormLinkDialog, OnFieldChanged ) ); + m_xRow4->SetLinkChangeHandler( LINK( this, FormLinkDialog, OnFieldChanged ) ); + + Application::PostUserEvent(LINK(this, FormLinkDialog, OnInitialize)); + + updateOkButton(); + } + + FormLinkDialog::~FormLinkDialog() + { + } + + void FormLinkDialog::commitLinkPairs() + { + // collect the field lists from the rows + std::vector< OUString > aDetailFields; aDetailFields.reserve( 4 ); + std::vector< OUString > aMasterFields; aMasterFields.reserve( 4 ); + + const FieldLinkRow* aRows[] = { + m_xRow1.get(), m_xRow2.get(), m_xRow3.get(), m_xRow4.get() + }; + + for (const FieldLinkRow* aRow : aRows) + { + OUString sDetailField, sMasterField; + aRow->GetFieldName( FieldLinkRow::eDetailField, sDetailField ); + aRow->GetFieldName( FieldLinkRow::eMasterField, sMasterField ); + if ( sDetailField.isEmpty() && sMasterField.isEmpty() ) + continue; + + aDetailFields.push_back( sDetailField ); + aMasterFields.push_back( sMasterField ); + } + + // and set as property values + try + { + if ( m_xDetailForm.is() ) + { + m_xDetailForm->setPropertyValue( PROPERTY_DETAILFIELDS, Any( Sequence< OUString >( aDetailFields.data(), aDetailFields.size() ) ) ); + m_xDetailForm->setPropertyValue( PROPERTY_MASTERFIELDS, Any( Sequence< OUString >( aMasterFields.data(), aMasterFields.size() ) ) ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION("extensions.propctrlr", + "caught an exception while setting the properties!"); + } + } + + short FormLinkDialog::run() + { + short nResult = GenericDialogController::run(); + + if ( RET_OK == nResult ) + commitLinkPairs(); + + return nResult; + } + + void FormLinkDialog::initializeFieldLists() + { + Sequence< OUString > sDetailFields; + getFormFields( m_xDetailForm, sDetailFields ); + + Sequence< OUString > sMasterFields; + getFormFields( m_xMasterForm, sMasterFields ); + + FieldLinkRow* aRows[] = { + m_xRow1.get(), m_xRow2.get(), m_xRow3.get(), m_xRow4.get() + }; + for (FieldLinkRow* aRow : aRows) + { + aRow->fillList( FieldLinkRow::eDetailField, sDetailFields ); + aRow->fillList( FieldLinkRow::eMasterField, sMasterFields ); + } + + } + + + void FormLinkDialog::initializeColumnLabels() + { + // label for the detail form + OUString sDetailType = getFormDataSourceType( m_xDetailForm ); + if ( sDetailType.isEmpty() ) + { + if ( m_sDetailLabel.isEmpty() ) + { + m_sDetailLabel = PcrRes(STR_DETAIL_FORM); + } + sDetailType = m_sDetailLabel; + } + m_xDetailLabel->set_label( sDetailType ); + + // label for the master form + OUString sMasterType = getFormDataSourceType( m_xMasterForm ); + if ( sMasterType.isEmpty() ) + { + if ( m_sMasterLabel.isEmpty() ) + { + m_sMasterLabel = PcrRes(STR_MASTER_FORM); + } + sMasterType = m_sMasterLabel; + } + m_xMasterLabel->set_label( sMasterType ); + } + + void FormLinkDialog::initializeFieldRowsFrom( std::vector< OUString >& _rDetailFields, std::vector< OUString >& _rMasterFields ) + { + // our UI does allow 4 fields max + _rDetailFields.resize( 4 ); + _rMasterFields.resize( 4 ); + + FieldLinkRow* aRows[] = { + m_xRow1.get(), m_xRow2.get(), m_xRow3.get(), m_xRow4.get() + }; + for ( sal_Int32 i = 0; i < 4; ++i ) + { + aRows[ i ]->SetFieldName( FieldLinkRow::eDetailField, _rDetailFields[i] ); + aRows[ i ]->SetFieldName( FieldLinkRow::eMasterField, _rMasterFields[i] ); + } + } + + + void FormLinkDialog::initializeLinks() + { + try + { + Sequence< OUString > aDetailFields; + Sequence< OUString > aMasterFields; + + if ( m_xDetailForm.is() ) + { + m_xDetailForm->getPropertyValue( PROPERTY_DETAILFIELDS ) >>= aDetailFields; + m_xDetailForm->getPropertyValue( PROPERTY_MASTERFIELDS ) >>= aMasterFields; + } + + std::vector< OUString > aDetailFields1; + comphelper::sequenceToContainer(aDetailFields1, aDetailFields); + std::vector< OUString > aMasterFields1; + comphelper::sequenceToContainer(aMasterFields1, aMasterFields); + initializeFieldRowsFrom( aDetailFields1, aMasterFields1 ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormLinkDialog::initializeLinks" ); + } + } + + + void FormLinkDialog::updateOkButton() + { + // in all rows, there must be either two valid selections, or none at all + // If there is at least one row with exactly one valid selection, then the + // OKButton needs to be disabled + bool bEnable = true; + + const FieldLinkRow* aRows[] = { + m_xRow1.get(), m_xRow2.get(), m_xRow3.get(), m_xRow4.get() + }; + + for ( sal_Int32 i = 0; ( i < 4 ) && bEnable; ++i ) + { + OUString sNotInterestedInRightNow; + if ( aRows[ i ]->GetFieldName( FieldLinkRow::eDetailField, sNotInterestedInRightNow ) + != aRows[ i ]->GetFieldName( FieldLinkRow::eMasterField, sNotInterestedInRightNow ) + ) + bEnable = false; + } + + m_xOK->set_sensitive(bEnable); + } + + OUString FormLinkDialog::getFormDataSourceType( const Reference< XPropertySet >& _rxForm ) + { + OUString sReturn; + if ( !_rxForm.is() ) + return sReturn; + + try + { + sal_Int32 nCommandType = CommandType::COMMAND; + OUString sCommand; + + _rxForm->getPropertyValue( PROPERTY_COMMANDTYPE ) >>= nCommandType; + _rxForm->getPropertyValue( PROPERTY_COMMAND ) >>= sCommand; + + if ( ( nCommandType == CommandType::TABLE ) + || ( nCommandType == CommandType::QUERY ) + ) + sReturn = sCommand; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormLinkDialog::getFormDataSourceType" ); + } + return sReturn; + } + + void FormLinkDialog::getFormFields( const Reference< XPropertySet >& _rxForm, Sequence< OUString >& /* [out] */ _rNames ) const + { + _rNames.realloc( 0 ); + + ::dbtools::SQLExceptionInfo aErrorInfo; + OUString sCommand; + try + { + weld::WaitObject aWaitCursor(m_xDialog.get()); + + OSL_ENSURE( _rxForm.is(), "FormLinkDialog::getFormFields: invalid form!" ); + + sal_Int32 nCommandType = CommandType::COMMAND; + + _rxForm->getPropertyValue( PROPERTY_COMMANDTYPE ) >>= nCommandType; + _rxForm->getPropertyValue( PROPERTY_COMMAND ) >>= sCommand; + + Reference< XConnection > xConnection; + ensureFormConnection( _rxForm, xConnection ); + + _rNames = ::dbtools::getFieldNamesByCommandDescriptor( + xConnection, + nCommandType, + sCommand, + &aErrorInfo + ); + } + catch (const SQLContext& e) { aErrorInfo = e; } + catch (const SQLWarning& e) { aErrorInfo = e; } + catch (const SQLException& e ) { aErrorInfo = e; } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormLinkDialog::getFormFields: caught a non-SQL exception!" ); + } + + if ( !aErrorInfo.isValid() ) + return; + + OUString sErrorMessage; + { + sErrorMessage = PcrRes(STR_ERROR_RETRIEVING_COLUMNS); + sErrorMessage = sErrorMessage.replaceFirst("#", sCommand); + } + + SQLContext aContext; + aContext.Message = sErrorMessage; + aContext.NextException = aErrorInfo.get(); + ::dbtools::showError(aContext, m_xDialog->GetXWindow(), m_xContext); + } + + void FormLinkDialog::ensureFormConnection( const Reference< XPropertySet >& _rxFormProps, Reference< XConnection >& /* [out] */ _rxConnection ) const + { + OSL_PRECOND( _rxFormProps.is(), "FormLinkDialog::ensureFormConnection: invalid form!" ); + if ( !_rxFormProps.is() ) + return; + if ( _rxFormProps->getPropertySetInfo()->hasPropertyByName(PROPERTY_ACTIVE_CONNECTION) ) + _rxConnection.set(_rxFormProps->getPropertyValue(PROPERTY_ACTIVE_CONNECTION),UNO_QUERY); + + if ( !_rxConnection.is() ) + _rxConnection = ::dbtools::connectRowset( Reference< XRowSet >( _rxFormProps, UNO_QUERY ), m_xContext, nullptr ); + } + + + void FormLinkDialog::getConnectionMetaData( const Reference< XPropertySet >& _rxFormProps, Reference< XDatabaseMetaData >& /* [out] */ _rxMeta ) + { + if ( _rxFormProps.is() ) + { + Reference< XConnection > xConnection; + if ( !::dbtools::isEmbeddedInDatabase( _rxFormProps, xConnection ) ) + _rxFormProps->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xConnection; + if ( xConnection.is() ) + _rxMeta = xConnection->getMetaData(); + } + } + + + Reference< XPropertySet > FormLinkDialog::getCanonicUnderlyingTable( const Reference< XPropertySet >& _rxFormProps ) const + { + Reference< XPropertySet > xTable; + try + { + Reference< XTablesSupplier > xTablesInForm( ::dbtools::getCurrentSettingsComposer( _rxFormProps, m_xContext, nullptr ), UNO_QUERY ); + Reference< XNameAccess > xTables; + if ( xTablesInForm.is() ) + xTables = xTablesInForm->getTables(); + Sequence< OUString > aTableNames; + if ( xTables.is() ) + aTableNames = xTables->getElementNames(); + + if ( aTableNames.getLength() == 1 ) + { + xTables->getByName( aTableNames[ 0 ] ) >>= xTable; + OSL_ENSURE( xTable.is(), "FormLinkDialog::getCanonicUnderlyingTable: invalid table!" ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormLinkDialog::getCanonicUnderlyingTable" ); + } + return xTable; + } + + + bool FormLinkDialog::getExistingRelation( const Reference< XPropertySet >& _rxLHS, const Reference< XPropertySet >& /*_rxRHS*/, + // TODO: fix the usage of _rxRHS. This is issue #i81956#. + std::vector< OUString >& _rLeftFields, std::vector< OUString >& _rRightFields ) + { + try + { + Reference< XKeysSupplier > xSuppKeys( _rxLHS, UNO_QUERY ); + Reference< XIndexAccess > xKeys; + if ( xSuppKeys.is() ) + xKeys = xSuppKeys->getKeys(); + + if ( xKeys.is() ) + { + Reference< XPropertySet > xKey; + Reference< XColumnsSupplier > xKeyColSupp( xKey, UNO_QUERY ); + Reference< XIndexAccess > xKeyColumns; + Reference< XPropertySet > xKeyColumn; + OUString sColumnName, sRelatedColumnName; + + const sal_Int32 keyCount = xKeys->getCount(); + for ( sal_Int32 key = 0; key < keyCount; ++key ) + { + xKeys->getByIndex( key ) >>= xKey; + sal_Int32 nKeyType = 0; + xKey->getPropertyValue("Type") >>= nKeyType; + if ( nKeyType != KeyType::FOREIGN ) + continue; + + xKeyColumns.clear(); + xKeyColSupp.set(xKey, css::uno::UNO_QUERY); + if ( xKeyColSupp.is() ) + xKeyColumns.set(xKeyColSupp->getColumns(), css::uno::UNO_QUERY); + OSL_ENSURE( xKeyColumns.is(), "FormLinkDialog::getExistingRelation: could not obtain the columns for the key!" ); + + if ( !xKeyColumns.is() ) + continue; + + const sal_Int32 columnCount = xKeyColumns->getCount(); + _rLeftFields.resize( columnCount ); + _rRightFields.resize( columnCount ); + for ( sal_Int32 column = 0; column < columnCount; ++column ) + { + xKeyColumn.clear(); + xKeyColumns->getByIndex( column ) >>= xKeyColumn; + OSL_ENSURE( xKeyColumn.is(), "FormLinkDialog::getExistingRelation: invalid key column!" ); + if ( xKeyColumn.is() ) + { + xKeyColumn->getPropertyValue( PROPERTY_NAME ) >>= sColumnName; + xKeyColumn->getPropertyValue("RelatedColumn") >>= sRelatedColumnName; + + _rLeftFields[ column ] = sColumnName; + _rRightFields[ column ] = sRelatedColumnName; + } + } + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormLinkDialog::getExistingRelation" ); + } + + return ( !_rLeftFields.empty() ) && ( !_rLeftFields[ 0 ].isEmpty() ); + } + + + void FormLinkDialog::initializeSuggest() + { + if ( !m_xDetailForm.is() || !m_xMasterForm.is() ) + return; + + try + { + // only show the button when both forms are based on the same data source + OUString sMasterDS, sDetailDS; + m_xMasterForm->getPropertyValue( PROPERTY_DATASOURCE ) >>= sMasterDS; + m_xDetailForm->getPropertyValue( PROPERTY_DATASOURCE ) >>= sDetailDS; + bool bEnable = ( sMasterDS == sDetailDS ); + + // only show the button when the connection supports relations + if ( bEnable ) + { + Reference< XDatabaseMetaData > xMeta; + getConnectionMetaData( m_xDetailForm, xMeta ); + OSL_ENSURE( xMeta.is(), "FormLinkDialog::initializeSuggest: unable to retrieve the meta data for the connection!" ); + try + { + bEnable = xMeta.is() && xMeta->supportsIntegrityEnhancementFacility(); + } + catch(const Exception&) + { + bEnable = false; + } + } + + // only enable the button if there is a "canonic" table underlying both forms + Reference< XPropertySet > xDetailTable, xMasterTable; + if ( bEnable ) + { + xDetailTable = getCanonicUnderlyingTable( m_xDetailForm ); + xMasterTable = getCanonicUnderlyingTable( m_xMasterForm ); + bEnable = xDetailTable.is() && xMasterTable.is(); + } + + // only enable the button if there is a relation between both tables + m_aRelationDetailColumns.clear(); + m_aRelationMasterColumns.clear(); + if ( bEnable ) + { + bEnable = getExistingRelation( xDetailTable, xMasterTable, m_aRelationDetailColumns, m_aRelationMasterColumns ); + SAL_WARN_IF( m_aRelationMasterColumns.size() != m_aRelationDetailColumns.size(), + "extensions.propctrlr", + "FormLinkDialog::initializeSuggest: nonsense!" ); + if ( m_aRelationMasterColumns.empty() ) + { // okay, there is no relation "pointing" (via a foreign key) from the detail table to the master table + // but perhaps the other way round (would make less sense, but who knows ...) + bEnable = getExistingRelation( xMasterTable, xDetailTable, m_aRelationMasterColumns, m_aRelationDetailColumns ); + } + } + + // only enable the button if the relation contains at most 4 field pairs + if ( bEnable ) + { + bEnable = ( m_aRelationMasterColumns.size() <= 4 ); + } + + m_xSuggest->set_sensitive(bEnable); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormLinkDialog::initializeSuggest" ); + } + } + + IMPL_LINK_NOARG( FormLinkDialog, OnSuggest, weld::Button&, void ) + { + initializeFieldRowsFrom( m_aRelationDetailColumns, m_aRelationMasterColumns ); + } + + IMPL_LINK_NOARG( FormLinkDialog, OnFieldChanged, FieldLinkRow&, void ) + { + updateOkButton(); + } + + IMPL_LINK_NOARG( FormLinkDialog, OnInitialize, void*, void ) + { + initializeColumnLabels(); + initializeFieldLists(); + initializeLinks(); + initializeSuggest(); + } + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/formlinkdialog.hxx b/extensions/source/propctrlr/formlinkdialog.hxx new file mode 100644 index 000000000..80cc086cc --- /dev/null +++ b/extensions/source/propctrlr/formlinkdialog.hxx @@ -0,0 +1,127 @@ +/* -*- 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 <vcl/weld.hxx> + +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <memory> + + +namespace pcr +{ + class FieldLinkRow; + + //= FormLinkDialog + + class FormLinkDialog : public weld::GenericDialogController + { + private: + css::uno::Reference< css::uno::XComponentContext > + m_xContext; + css::uno::Reference< css::beans::XPropertySet > + m_xDetailForm; + css::uno::Reference< css::beans::XPropertySet > + m_xMasterForm; + + std::vector< OUString > m_aRelationDetailColumns; + std::vector< OUString > m_aRelationMasterColumns; + + OUString m_sDetailLabel; + OUString m_sMasterLabel; + + std::unique_ptr<weld::Label> m_xExplanation; + std::unique_ptr<weld::Label> m_xDetailLabel; + std::unique_ptr<weld::Label> m_xMasterLabel; + std::unique_ptr<FieldLinkRow> m_xRow1; + std::unique_ptr<FieldLinkRow> m_xRow2; + std::unique_ptr<FieldLinkRow> m_xRow3; + std::unique_ptr<FieldLinkRow> m_xRow4; + std::unique_ptr<weld::Button> m_xOK; + std::unique_ptr<weld::Button> m_xSuggest; + + public: + FormLinkDialog( + weld::Window* _pParent, + const css::uno::Reference< css::beans::XPropertySet >& _rxDetailForm, + const css::uno::Reference< css::beans::XPropertySet >& _rxMasterForm, + const css::uno::Reference< css::uno::XComponentContext >& _rxContext, + const OUString& _sExplanation = OUString(), + const OUString& _sDetailLabel = OUString(), + const OUString& _sMasterLabel = OUString() + ); + + virtual ~FormLinkDialog() override; + + // Dialog overridables + virtual short run() override; + + private: + DECL_LINK( OnSuggest, weld::Button&, void ); + DECL_LINK( OnFieldChanged, FieldLinkRow&, void ); + DECL_LINK( OnInitialize, void*, void); + + void updateOkButton(); + void initializeFieldLists(); + void initializeColumnLabels(); + void initializeLinks(); + void initializeSuggest(); + void commitLinkPairs(); + + void initializeFieldRowsFrom( + std::vector< OUString >& _rDetailFields, + std::vector< OUString >& _rMasterFields + ); + + static OUString getFormDataSourceType( + const css::uno::Reference< css::beans::XPropertySet >& _rxForm + ); + + void getFormFields( + const css::uno::Reference< css::beans::XPropertySet >& _rxForm, + css::uno::Sequence< OUString >& /* [out] */ _rNames + ) const; + + void ensureFormConnection( + const css::uno::Reference< css::beans::XPropertySet >& _rxFormProps, + css::uno::Reference< css::sdbc::XConnection >& /* [out] */ _rxConnection + ) const; + + static void getConnectionMetaData( + const css::uno::Reference< css::beans::XPropertySet >& _rxFormProps, + css::uno::Reference< css::sdbc::XDatabaseMetaData >& /* [out] */ _rxMeta + ); + + css::uno::Reference< css::beans::XPropertySet > + getCanonicUnderlyingTable( const css::uno::Reference< css::beans::XPropertySet >& _rxFormProps ) const; + static bool getExistingRelation( + const css::uno::Reference< css::beans::XPropertySet >& _rxLHS, + const css::uno::Reference< css::beans::XPropertySet >& _rxRHS, + std::vector< OUString >& /* [out] */ _rLeftFields, + std::vector< OUString >& /* [out] */ _rRightFields + ); + }; + +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/formmetadata.cxx b/extensions/source/propctrlr/formmetadata.cxx new file mode 100644 index 000000000..13a7b32e7 --- /dev/null +++ b/extensions/source/propctrlr/formmetadata.cxx @@ -0,0 +1,694 @@ +/* -*- 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 "formmetadata.hxx" +#include "formstrings.hxx" +#include "modulepcr.hxx" +#include <command.hrc> +#include <helpids.h> +#include <strings.hrc> +#include <stringarrays.hrc> +#include <comphelper/extract.hxx> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <sal/macros.h> +#include <algorithm> + +namespace pcr +{ + using namespace ::com::sun::star::uno; + + + //= OPropertyInfoImpl + + struct OPropertyInfoImpl + { + OUString sName; + OUString sTranslation; + OUString sHelpId; + sal_Int32 nId; + sal_uInt16 nPos; + sal_uInt32 nUIFlags; + + OPropertyInfoImpl( + const OUString& rName, + sal_Int32 _nId, + const OUString& aTranslation, + sal_uInt16 nPosId, + const OUString&, + sal_uInt32 _nUIFlags); + }; + + + OPropertyInfoImpl::OPropertyInfoImpl(const OUString& _rName, sal_Int32 _nId, + const OUString& aString, sal_uInt16 nP, const OUString& sHid, sal_uInt32 _nUIFlags) + :sName(_rName) + ,sTranslation(aString) + ,sHelpId(sHid) + ,nId(_nId) + ,nPos(nP) + ,nUIFlags(_nUIFlags) + { + } + + namespace { + + // Compare PropertyInfo + struct PropertyInfoLessByName + { + bool operator()( const OPropertyInfoImpl& _rLHS, const OPropertyInfoImpl& _rRHS ) + { + return _rLHS.sName.compareTo( _rRHS.sName ) < 0; + } + }; + + } + + //= OPropertyInfoService + +#define DEF_INFO( ident, uinameres, pos, helpid, flags ) \ + OPropertyInfoImpl( PROPERTY_##ident, PROPERTY_ID_##ident, \ + PcrRes( RID_STR_##uinameres ), pos, HID_PROP_##helpid, flags ) + +#define DEF_INFO_1( ident, uinameres, pos, helpid, flag1 ) \ + DEF_INFO( ident, uinameres, pos, helpid, PROP_FLAG_##flag1 ) + +#define DEF_INFO_2( ident, uinameres, pos, helpid, flag1, flag2 ) \ + DEF_INFO( ident, uinameres, pos, helpid, PROP_FLAG_##flag1 | PROP_FLAG_##flag2 ) + +#define DEF_INFO_3( ident, uinameres, pos, helpid, flag1, flag2, flag3 ) \ + DEF_INFO( ident, uinameres, pos, helpid, PROP_FLAG_##flag1 | PROP_FLAG_##flag2 | PROP_FLAG_##flag3 ) + +#define DEF_INFO_4( ident, uinameres, pos, helpid, flag1, flag2, flag3, flag4 ) \ + DEF_INFO( ident, uinameres, pos, helpid, PROP_FLAG_##flag1 | PROP_FLAG_##flag2 | PROP_FLAG_##flag3 | PROP_FLAG_##flag4 ) + + sal_uInt16 OPropertyInfoService::s_nCount = 0; + OPropertyInfoImpl* OPropertyInfoService::s_pPropertyInfos = nullptr; + + const OPropertyInfoImpl* OPropertyInfoService::getPropertyInfo() + { + if ( s_pPropertyInfos ) + return s_pPropertyInfos; + + static OPropertyInfoImpl aPropertyInfos[] = + { + /* + DEF_INFO_?( propname and id, resource id, pos, help id, flags ), + */ + DEF_INFO_3( NAME, NAME, 0, NAME, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( TITLE, TITLE, 1, TITLE, FORM_VISIBLE, DIALOG_VISIBLE ), + DEF_INFO_3( LABEL, LABEL, 2, LABEL, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( CONTROLLABEL, LABELCONTROL, 3, CONTROLLABEL, FORM_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( WRITING_MODE, WRITING_MODE, 4, WRITING_MODE, FORM_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_3( GROUP_NAME, GROUP_NAME, 5, GROUP_NAME, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( TEXT, TEXT, 6, TEXT, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( MAXTEXTLEN, MAXTEXTLEN, 7, MAXTEXTLEN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( EDITMASK, EDITMASK, 8, EDITMASK, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( LITERALMASK, LITERALMASK, 9, LITERALMASK, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( STRICTFORMAT, STRICTFORMAT, 10, STRICTFORMAT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( ENABLED, ENABLED, 11, ENABLED, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( ENABLE_VISIBLE, ENABLE_VISIBLE, 12, ENABLE_VISIBLE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( READONLY, READONLY, 13, READONLY, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( PRINTABLE, PRINTABLE, 14, PRINTABLE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( STEP, STEP, 15, STEP, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( WHEEL_BEHAVIOR, WHEEL_BEHAVIOR, 16, WHEEL_BEHAVIOR, FORM_VISIBLE | PROP_FLAG_REPORT_INVISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_3( TABSTOP, TABSTOP, 17, TABSTOP, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( TABINDEX, TABINDEX, 18, TABINDEX, FORM_VISIBLE, DIALOG_VISIBLE ), + + DEF_INFO_2( BOUND_CELL, BOUND_CELL, 19, BOUND_CELL, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_3( CELL_EXCHANGE_TYPE,CELL_EXCHANGE_TYPE, 20, CELL_EXCHANGE_TYPE,FORM_VISIBLE, DATA_PROPERTY, ENUM ), + DEF_INFO_2( LIST_CELL_RANGE, LIST_CELL_RANGE, 21, LIST_CELL_RANGE, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_3( CONTROLSOURCE, CONTROLSOURCE, 22, CONTROLSOURCE, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ), + DEF_INFO_3( EMPTY_IS_NULL, EMPTY_IS_NULL, 23, EMPTY_IS_NULL, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ), + DEF_INFO_3( INPUT_REQUIRED, INPUT_REQUIRED, 24, INPUT_REQUIRED, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ), + DEF_INFO_3( REFVALUE, REFVALUE, 25, REFVALUE, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ), + DEF_INFO_3( UNCHECKEDREFVALUE, UNCHECKEDREFVALUE, 26, UNCHECKEDREFVALUE, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ), + DEF_INFO_3( DATASOURCE, DATASOURCE, 27, DATASOURCE, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ), + DEF_INFO_4( COMMANDTYPE, CURSORSOURCETYPE, 28, CURSORSOURCETYPE, FORM_VISIBLE, DATA_PROPERTY, ENUM, COMPOSEABLE ), + DEF_INFO_3( COMMAND, CURSORSOURCE, 29, CURSORSOURCE, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ), + DEF_INFO_3( ESCAPE_PROCESSING, ESCAPE_PROCESSING, 30, ESCAPE_PROCESSING, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ), + DEF_INFO_3( FILTER, FILTER, 31, FILTER, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ), + DEF_INFO_3( SORT, SORT_CRITERIA, 32, SORT_CRITERIA, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ), + DEF_INFO_2( MASTERFIELDS, MASTERFIELDS, 33, MASTERFIELDS, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( DETAILFIELDS, SLAVEFIELDS, 34, SLAVEFIELDS, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_3( ALLOWADDITIONS, ALLOW_ADDITIONS, 35, ALLOW_ADDITIONS, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ), + DEF_INFO_3( ALLOWEDITS, ALLOW_EDITS, 36, ALLOW_EDITS, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ), + DEF_INFO_3( ALLOWDELETIONS, ALLOW_DELETIONS, 37, ALLOW_DELETIONS, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ), + DEF_INFO_3( INSERTONLY, DATAENTRY, 38, DATAENTRY, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ), + DEF_INFO_4( NAVIGATION, NAVIGATION, 39, NAVIGATION, FORM_VISIBLE, DATA_PROPERTY, ENUM, COMPOSEABLE ), + DEF_INFO_4( CYCLE, CYCLE, 40, CYCLE, FORM_VISIBLE, DATA_PROPERTY, ENUM, COMPOSEABLE ), + DEF_INFO_3( FILTERPROPOSAL, FILTERPROPOSAL, 41, FILTERPROPOSAL, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ), + DEF_INFO_4( LISTSOURCETYPE, LISTSOURCETYPE, 42, LISTSOURCETYPE, FORM_VISIBLE, DATA_PROPERTY, ENUM, COMPOSEABLE ), + DEF_INFO_3( LISTSOURCE, LISTSOURCE, 43, LISTSOURCE, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ), + DEF_INFO_3( BOUNDCOLUMN, BOUNDCOLUMN, 44, BOUNDCOLUMN, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ), + + // <!-----------------> + // XML node binding + DEF_INFO_2( LIST_BINDING, LIST_BINDING, 45, LIST_BINDING, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XML_DATA_MODEL, XML_DATA_MODEL, 46, XML_DATA_MODEL, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( BINDING_NAME, BINDING_NAME, 47, BINDING_NAME, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( BIND_EXPRESSION, BIND_EXPRESSION, 48, BIND_EXPRESSION, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_REQUIRED, XSD_REQUIRED, 49, XSD_REQUIRED, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_RELEVANT, XSD_RELEVANT, 50, XSD_RELEVANT, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_READONLY, XSD_READONLY, 51, XSD_READONLY, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_CONSTRAINT, XSD_CONSTRAINT, 52, XSD_CONSTRAINT, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_CALCULATION, XSD_CALCULATION, 53, XSD_CALCULATION, FORM_VISIBLE, DATA_PROPERTY ), + + // data type + DEF_INFO_2( XSD_DATA_TYPE, XSD_DATA_TYPE, 54, XSD_DATA_TYPE, FORM_VISIBLE, DATA_PROPERTY ), + // data types facets + // common + DEF_INFO_3( XSD_WHITESPACES, XSD_WHITESPACES, 55, XSD_WHITESPACES, FORM_VISIBLE, DATA_PROPERTY, ENUM ), + DEF_INFO_2( XSD_PATTERN, XSD_PATTERN, 56, XSD_PATTERN, FORM_VISIBLE, DATA_PROPERTY ), + // string + DEF_INFO_2( XSD_LENGTH, XSD_LENGTH, 57, XSD_LENGTH, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_MIN_LENGTH, XSD_MIN_LENGTH, 58, XSD_MIN_LENGTH, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_MAX_LENGTH, XSD_MAX_LENGTH, 59, XSD_MAX_LENGTH, FORM_VISIBLE, DATA_PROPERTY ), + // decimal + DEF_INFO_2( XSD_TOTAL_DIGITS, XSD_TOTAL_DIGITS, 60, XSD_TOTAL_DIGITS, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_FRACTION_DIGITS,XSD_FRACTION_DIGITS,61,XSD_FRACTION_DIGITS,FORM_VISIBLE, DATA_PROPERTY ), + // int value types (year, month, day) + DEF_INFO_2( XSD_MAX_INCLUSIVE_INT, XSD_MAX_INCLUSIVE, 62, XSD_MAX_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_MAX_EXCLUSIVE_INT, XSD_MAX_EXCLUSIVE, 63, XSD_MAX_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_MIN_INCLUSIVE_INT, XSD_MIN_INCLUSIVE, 64, XSD_MIN_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_MIN_EXCLUSIVE_INT, XSD_MIN_EXCLUSIVE, 65, XSD_MIN_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + // double value types (double, float, decimal) + DEF_INFO_2( XSD_MAX_INCLUSIVE_DOUBLE, XSD_MAX_INCLUSIVE, 66, XSD_MAX_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_MAX_EXCLUSIVE_DOUBLE, XSD_MAX_EXCLUSIVE, 67, XSD_MAX_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_MIN_INCLUSIVE_DOUBLE, XSD_MIN_INCLUSIVE, 68, XSD_MIN_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_MIN_EXCLUSIVE_DOUBLE, XSD_MIN_EXCLUSIVE, 69, XSD_MIN_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + // date value type + DEF_INFO_2( XSD_MAX_INCLUSIVE_DATE, XSD_MAX_INCLUSIVE, 70, XSD_MAX_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_MAX_EXCLUSIVE_DATE, XSD_MAX_EXCLUSIVE, 71, XSD_MAX_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_MIN_INCLUSIVE_DATE, XSD_MIN_INCLUSIVE, 72, XSD_MIN_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_MIN_EXCLUSIVE_DATE, XSD_MIN_EXCLUSIVE, 73, XSD_MIN_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + // time value type + DEF_INFO_2( XSD_MAX_INCLUSIVE_TIME, XSD_MAX_INCLUSIVE, 74, XSD_MAX_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_MAX_EXCLUSIVE_TIME, XSD_MAX_EXCLUSIVE, 75, XSD_MAX_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_MIN_INCLUSIVE_TIME, XSD_MIN_INCLUSIVE, 76, XSD_MIN_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_MIN_EXCLUSIVE_TIME, XSD_MIN_EXCLUSIVE, 77, XSD_MIN_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + // dateTime value type + DEF_INFO_2( XSD_MAX_INCLUSIVE_DATE_TIME, XSD_MAX_INCLUSIVE, 78, XSD_MAX_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_MAX_EXCLUSIVE_DATE_TIME, XSD_MAX_EXCLUSIVE, 79, XSD_MAX_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_MIN_INCLUSIVE_DATE_TIME, XSD_MIN_INCLUSIVE, 80, XSD_MIN_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + DEF_INFO_2( XSD_MIN_EXCLUSIVE_DATE_TIME, XSD_MIN_EXCLUSIVE, 81, XSD_MIN_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ), + // <!-----------------> + + DEF_INFO_2( HIDDEN_VALUE, VALUE, 82, HIDDEN_VALUE, FORM_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( VALUE, VALUE, 83, VALUE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( VALUEMIN, VALUEMIN, 84, VALUEMIN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( VALUEMAX, VALUEMAX, 85, VALUEMAX, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( VALUESTEP, VALUESTEP, 86, VALUESTEP, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( DEFAULT_VALUE, DEFAULTVALUE, 87, DEFAULT_LONG_VALUE,FORM_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( DECIMAL_ACCURACY, DECIMAL_ACCURACY, 88, DECIMAL_ACCURACY, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( SHOWTHOUSANDSEP, SHOWTHOUSANDSEP, 89, SHOWTHOUSANDSEP, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + + DEF_INFO_3( CURRENCYSYMBOL, CURRENCYSYMBOL, 90, CURRENCYSYMBOL, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( CURRSYM_POSITION, CURRSYM_POSITION, 91, CURRSYM_POSITION, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + + DEF_INFO_2( DATE, DATE, 92, DATE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( DATEMIN, DATEMIN, 93, DATEMIN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( DATEMAX, DATEMAX, 94, DATEMAX, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_4( DATEFORMAT, DATEFORMAT, 95, DATEFORMAT, FORM_VISIBLE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_2( DEFAULT_DATE, DEFAULTDATE, 96, DEFAULT_DATE, FORM_VISIBLE, COMPOSEABLE ), + + DEF_INFO_2( TIME, TIME, 97, TIME, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( TIMEMIN, TIMEMIN, 98, TIMEMIN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( TIMEMAX, TIMEMAX, 99, TIMEMAX, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_4( TIMEFORMAT, TIMEFORMAT, 100, TIMEFORMAT, FORM_VISIBLE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_2( DEFAULT_TIME, DEFAULTTIME, 101, DEFAULT_TIME, FORM_VISIBLE, COMPOSEABLE ), + + DEF_INFO_1( EFFECTIVE_VALUE, VALUE, 102, VALUE, DIALOG_VISIBLE ), + DEF_INFO_3( EFFECTIVE_MIN, VALUEMIN, 103, EFFECTIVEMIN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( EFFECTIVE_MAX, VALUEMAX, 104, EFFECTIVEMAX, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( EFFECTIVE_DEFAULT, DEFAULTVALUE, 105, EFFECTIVEDEFAULT, FORM_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( FORMATKEY, FORMATKEY, 106, FORMATKEY, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + + DEF_INFO_3( PROGRESSVALUE, PROGRESSVALUE, 107, PROGRESSVALUE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( PROGRESSVALUE_MIN, PROGRESSVALUE_MIN, 108, PROGRESSVALUE_MIN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( PROGRESSVALUE_MAX, PROGRESSVALUE_MAX, 109, PROGRESSVALUE_MAX, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + + DEF_INFO_2( SCROLLVALUE, SCROLLVALUE, 110, SCROLLVALUE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( SCROLLVALUE_MIN, SCROLLVALUE_MIN, 111, SCROLLVALUE_MIN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( SCROLLVALUE_MAX, SCROLLVALUE_MAX, 112, SCROLLVALUE_MAX, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( SCROLL_WIDTH, SCROLL_WIDTH, 113, SCROLL_WIDTH, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( SCROLL_HEIGHT, SCROLL_HEIGHT, 114, SCROLL_HEIGHT, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( SCROLL_TOP, SCROLL_TOP, 115, SCROLL_TOP, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( SCROLL_LEFT, SCROLL_LEFT, 116, SCROLL_LEFT, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( DEFAULT_SCROLLVALUE,DEFAULT_SCROLLVALUE,117,DEFAULT_SCROLLVALUE,FORM_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( LINEINCREMENT, LINEINCREMENT, 118, LINEINCREMENT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( BLOCKINCREMENT, BLOCKINCREMENT, 119, BLOCKINCREMENT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + + DEF_INFO_2( SPINVALUE, VALUE, 120, SPINVALUE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( SPINVALUE_MIN, VALUEMIN, 121, SPINVALUE_MIN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( SPINVALUE_MAX, VALUEMAX, 122, SPINVALUE_MAX, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( DEFAULT_SPINVALUE,DEFAULTVALUE, 123, DEFAULT_SPINVALUE, FORM_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( SPININCREMENT, VALUESTEP, 124, SPININCREMENT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + + DEF_INFO_3( SPIN, SPIN, 125, SPIN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( REPEAT, REPEAT, 126, REPEAT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( REPEAT_DELAY, REPEAT_DELAY, 127, REPEAT_DELAY, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( VISIBLESIZE, VISIBLESIZE, 128, VISIBLESIZE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_4( ORIENTATION, ORIENTATION, 129, ORIENTATION, FORM_VISIBLE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_3( FOCUSONCLICK, FOCUSONCLICK, 130, FOCUSONCLICK, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( TOGGLE, TOGGLE, 131, TOGGLE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( DEFAULT_STATE, DEFAULT_STATE, 132, DEFAULT_STATE, FORM_VISIBLE, ENUM, COMPOSEABLE ), + + DEF_INFO_3( TEXT_ANCHOR_TYPE, ANCHOR_TYPE, 133, ANCHOR_TYPE, FORM_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_3( SHEET_ANCHOR_TYPE, ANCHOR_TYPE, 134, ANCHOR_TYPE, FORM_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_3( POSITIONX, POSITIONX, 135, POSITIONX, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( POSITIONY, POSITIONY, 136, POSITIONY, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( WIDTH, WIDTH, 137, WIDTH, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( HEIGHT, HEIGHT, 138, HEIGHT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + + DEF_INFO_1( LISTINDEX, LISTINDEX, 139, LISTINDEX, FORM_VISIBLE ), + DEF_INFO_3( STRINGITEMLIST, STRINGITEMLIST, 140, STRINGITEMLIST, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( DEFAULT_TEXT, DEFAULTTEXT, 141, DEFAULTVALUE, FORM_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( FONT, FONT, 142, FONT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_4( VISUALEFFECT, VISUALEFFECT, 143, VISUALEFFECT, FORM_VISIBLE, DIALOG_VISIBLE, ENUM_ONE, COMPOSEABLE ), + DEF_INFO_4( ALIGN, ALIGN, 144, ALIGN, FORM_VISIBLE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_4( VERTICAL_ALIGN, VERTICAL_ALIGN, 145, VERTICAL_ALIGN, FORM_VISIBLE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_3( ROWHEIGHT, ROWHEIGHT, 146, ROWHEIGHT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( BACKGROUNDCOLOR, BACKGROUNDCOLOR, 147, BACKGROUNDCOLOR, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( SYMBOLCOLOR, SYMBOLCOLOR, 148, SYMBOLCOLOR, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( FILLCOLOR, FILLCOLOR, 149, FILLCOLOR, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( LINECOLOR, LINECOLOR, 150, LINECOLOR, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_4( BORDER, BORDER, 151, BORDER, FORM_VISIBLE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_3( BORDERCOLOR, BORDERCOLOR, 152, BORDERCOLOR, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( ICONSIZE, ICONSIZE, 153, ICONSIZE, FORM_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_2( SHOW_POSITION, SHOW_POSITION, 154, SHOW_POSITION, FORM_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( SHOW_NAVIGATION, SHOW_NAVIGATION, 155, SHOW_NAVIGATION, FORM_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( SHOW_RECORDACTIONS,SHOW_RECORDACTIONS, 156, SHOW_RECORDACTIONS,FORM_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( SHOW_FILTERSORT, SHOW_FILTERSORT, 157, SHOW_FILTERSORT, FORM_VISIBLE, COMPOSEABLE ), + + DEF_INFO_3( DROPDOWN, DROPDOWN, 158, DROPDOWN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( LINECOUNT, LINECOUNT, 159, LINECOUNT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( AUTOCOMPLETE, AUTOCOMPLETE, 160, AUTOCOMPLETE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( MULTILINE, MULTILINE, 161, MULTILINE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( WORDBREAK, WORDBREAK, 162, WORDBREAK, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( TEXTTYPE, TEXTTYPE, 163, TEXTTYPE, FORM_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_3( LINEEND_FORMAT, LINEEND_FORMAT, 164, LINEEND_FORMAT, FORM_VISIBLE, ENUM_ONE, COMPOSEABLE ), + DEF_INFO_3( MULTISELECTION, MULTISELECTION, 165, MULTISELECTION, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_4( SHOW_SCROLLBARS, SHOW_SCROLLBARS, 166, SHOW_SCROLLBARS, FORM_VISIBLE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_3( HSCROLL, HSCROLL, 167, HSCROLL, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( VSCROLL, VSCROLL, 168, VSCROLL, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( BUTTONTYPE, BUTTONTYPE, 169, BUTTONTYPE, FORM_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_2( XFORMS_BUTTONTYPE, BUTTONTYPE, 170, BUTTONTYPE, FORM_VISIBLE, ENUM ), + DEF_INFO_1( SUBMISSION_ID, SUBMISSION_ID, 171, SUBMISSION_ID, FORM_VISIBLE ), + DEF_INFO_2( PUSHBUTTONTYPE, PUSHBUTTONTYPE, 172, PUSHBUTTONTYPE, DIALOG_VISIBLE, ENUM ), + DEF_INFO_2( TARGET_URL, TARGET_URL, 173, TARGET_URL, FORM_VISIBLE, COMPOSEABLE ), + DEF_INFO_1( TARGET_FRAME, TARGET_FRAME, 174, TARGET_FRAME, FORM_VISIBLE ), + DEF_INFO_2( SUBMIT_ACTION, SUBMIT_ACTION, 175, SUBMIT_ACTION, FORM_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( SUBMIT_TARGET, SUBMIT_TARGET, 176, SUBMIT_TARGET, FORM_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( SUBMIT_ENCODING, SUBMIT_ENCODING, 177, SUBMIT_ENCODING, FORM_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_3( SUBMIT_METHOD, SUBMIT_METHOD, 178, SUBMIT_METHOD, FORM_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_3( STATE, STATE, 179, STATE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_3( DEFAULTBUTTON, DEFAULT_BUTTON, 180, DEFAULT_BUTTON, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( IMAGE_URL, IMAGE_URL, 181, IMAGE_URL, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_4( IMAGEPOSITION, IMAGEPOSITION, 182, IMAGEPOSITION, FORM_VISIBLE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_3( SCALEIMAGE, SCALEIMAGE, 183, SCALEIMAGE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_4( SCALE_MODE, SCALEIMAGE, 184, SCALEIMAGE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE, ENUM ), + DEF_INFO_2( DEFAULT_SELECT_SEQ,DEFAULT_SELECT_SEQ, 185, DEFAULT_SELECT_SEQ,FORM_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( SELECTEDITEMS, SELECTEDITEMS, 186, SELECTEDITEMS, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( ECHO_CHAR, ECHO_CHAR, 187, ECHO_CHAR, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( HIDEINACTIVESELECTION, HIDEINACTIVESELECTION, 188, HIDEINACTIVESELECTION, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( TRISTATE, TRISTATE, 189, TRISTATE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( HASNAVIGATION, NAVIGATION, 190, NAVIGATIONBAR, FORM_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( RECORDMARKER, RECORDMARKER, 191, RECORDMARKER, FORM_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( TAG, TAG, 192, TAG, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( HELPTEXT, HELPTEXT, 193, HELPTEXT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( HELPURL, HELPURL, 194, HELPURL, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( SELECTION_TYPE, SELECTION_TYPE, 195, SELECTION_TYPE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_2( ROOT_DISPLAYED, ROOT_DISPLAYED, 196, ROOT_DISPLAYED, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( SHOWS_HANDLES, SHOWS_HANDLES, 197, SHOWS_HANDLES, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( SHOWS_ROOT_HANDLES, SHOWS_ROOT_HANDLES, 198, SHOWS_ROOT_HANDLES, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( EDITABLE, EDITABLE, 199, EDITABLE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( INVOKES_STOP_NOT_EDITING, INVOKES_STOP_NOT_EDITING, 200, INVOKES_STOP_NOT_EDITING, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( DECORATION, DECORATION, 201, DECORATION, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( NOLABEL, NOLABEL, 202, NOLABEL, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_3( SELECTIONMODEL, SELECTIONMODEL, 203, SELECTIONMODEL, DIALOG_VISIBLE, ENUM, COMPOSEABLE ), + DEF_INFO_2( USEGRIDLINE, USEGRIDLINE, 204, USEGRIDLINE, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( GRIDLINECOLOR, GRIDLINECOLOR, 205, GRIDLINECOLOR, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( SHOWCOLUMNHEADER, SHOWCOLUMNHEADER, 206, SHOWCOLUMNHEADER, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( SHOWROWHEADER, SHOWROWHEADER, 207, SHOWROWHEADER, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( HEADERBACKGROUNDCOLOR, HEADERBACKGROUNDCOLOR, 208, HEADERBACKGROUNDCOLOR, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( HEADERTEXTCOLOR, HEADERTEXTCOLOR, 209, HEADERTEXTCOLOR, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( ACTIVESELECTIONBACKGROUNDCOLOR, ACTIVESELECTIONBACKGROUNDCOLOR, 210, ACTIVESELECTIONBACKGROUNDCOLOR, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( ACTIVESELECTIONTEXTCOLOR, ACTIVESELECTIONTEXTCOLOR, 211, ACTIVESELECTIONTEXTCOLOR, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( INACTIVESELECTIONBACKGROUNDCOLOR, INACTIVESELECTIONBACKGROUNDCOLOR, 212, INACTIVESELECTIONBACKGROUNDCOLOR, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( INACTIVESELECTIONTEXTCOLOR, INACTIVESELECTIONTEXTCOLOR, 213, INACTIVESELECTIONTEXTCOLOR, DIALOG_VISIBLE, COMPOSEABLE ), + DEF_INFO_2( URL, URL, 214, URL, DIALOG_VISIBLE, COMPOSEABLE ), + + DEF_INFO_3( AUTOGROW, AUTOGROW, 215, AUTOGROW, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE) + }; + + s_pPropertyInfos = aPropertyInfos; + s_nCount = SAL_N_ELEMENTS(aPropertyInfos); + + // sort + std::sort( s_pPropertyInfos, s_pPropertyInfos + s_nCount, PropertyInfoLessByName() ); + +#if OSL_DEBUG_LEVEL > 0 + for ( const OPropertyInfoImpl* pCheck = s_pPropertyInfos; pCheck != s_pPropertyInfos + s_nCount - 1; ++pCheck ) + { + OSL_ENSURE( pCheck->sName != ( pCheck + 1 )->sName, "OPropertyInfoService::getPropertyInfo: duplicate entry in the table!" ); + } +#endif + + return s_pPropertyInfos; + } + + + sal_Int32 OPropertyInfoService::getPropertyId(const OUString& _rName) const + { + const OPropertyInfoImpl* pInfo = getPropertyInfo(_rName); + return pInfo ? pInfo->nId : -1; + } + + + OUString OPropertyInfoService::getPropertyTranslation(sal_Int32 _nId) const + { + const OPropertyInfoImpl* pInfo = getPropertyInfo(_nId); + return pInfo ? pInfo->sTranslation : OUString(); + } + + OUString OPropertyInfoService::getPropertyHelpId(sal_Int32 _nId) const + { + const OPropertyInfoImpl* pInfo = getPropertyInfo(_nId); + return pInfo ? pInfo->sHelpId : OUString(); + } + + sal_Int16 OPropertyInfoService::getPropertyPos(sal_Int32 _nId) const + { + const OPropertyInfoImpl* pInfo = getPropertyInfo(_nId); + return pInfo ? pInfo->nPos : 0xFFFF; + } + + sal_uInt32 OPropertyInfoService::getPropertyUIFlags(sal_Int32 _nId) const + { + const OPropertyInfoImpl* pInfo = getPropertyInfo(_nId); + return pInfo ? pInfo->nUIFlags : 0; + } + + std::vector< OUString > OPropertyInfoService::getPropertyEnumRepresentations(sal_Int32 _nId) const + { + OSL_ENSURE( ( ( getPropertyUIFlags( _nId ) & PROP_FLAG_ENUM ) != 0 ) || ( _nId == PROPERTY_ID_TARGET_FRAME ), + "OPropertyInfoService::getPropertyEnumRepresentations: this is no enum property!" ); + + if (_nId == PROPERTY_ID_SUBMIT_METHOD) + { + return { "Get", "Post" }; + } + const TranslateId* pStringItemsResId = nullptr; + int nElements = 0; + switch ( _nId ) + { + case PROPERTY_ID_IMAGEPOSITION: + pStringItemsResId = RID_RSC_ENUM_IMAGE_POSITION; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_IMAGE_POSITION); + break; + case PROPERTY_ID_BORDER: + pStringItemsResId = RID_RSC_ENUM_BORDER_TYPE; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_BORDER_TYPE); + break; + case PROPERTY_ID_ICONSIZE: + pStringItemsResId = RID_RSC_ENUM_ICONSIZE_TYPE; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_ICONSIZE_TYPE); + break; + case PROPERTY_ID_COMMANDTYPE: + pStringItemsResId = RID_RSC_ENUM_COMMAND_TYPE; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_COMMAND_TYPE); + break; + case PROPERTY_ID_LISTSOURCETYPE: + pStringItemsResId = RID_RSC_ENUM_LISTSOURCE_TYPE; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_LISTSOURCE_TYPE); + break; + case PROPERTY_ID_ALIGN: + pStringItemsResId = RID_RSC_ENUM_ALIGNMENT; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_ALIGNMENT); + break; + case PROPERTY_ID_VERTICAL_ALIGN: + pStringItemsResId = RID_RSC_ENUM_VERTICAL_ALIGN; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_VERTICAL_ALIGN); + break; + case PROPERTY_ID_BUTTONTYPE: + pStringItemsResId = RID_RSC_ENUM_BUTTONTYPE; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_BUTTONTYPE); + break; + case PROPERTY_ID_PUSHBUTTONTYPE: + pStringItemsResId = RID_RSC_ENUM_PUSHBUTTONTYPE; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_PUSHBUTTONTYPE); + break; + case PROPERTY_ID_SUBMIT_ENCODING: + pStringItemsResId = RID_RSC_ENUM_SUBMIT_ENCODING; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_SUBMIT_ENCODING); + break; + case PROPERTY_ID_DATEFORMAT: + pStringItemsResId = RID_RSC_ENUM_DATEFORMAT_LIST; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_DATEFORMAT_LIST); + break; + case PROPERTY_ID_TIMEFORMAT: + pStringItemsResId = RID_RSC_ENUM_TIMEFORMAT_LIST; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_TIMEFORMAT_LIST); + break; + case PROPERTY_ID_DEFAULT_STATE: + case PROPERTY_ID_STATE: + pStringItemsResId = RID_RSC_ENUM_CHECKED; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_CHECKED); + break; + case PROPERTY_ID_CYCLE: + pStringItemsResId = RID_RSC_ENUM_CYCLE; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_CYCLE); + break; + case PROPERTY_ID_NAVIGATION: + pStringItemsResId = RID_RSC_ENUM_NAVIGATION; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_NAVIGATION); + break; + case PROPERTY_ID_TARGET_FRAME: + pStringItemsResId = RID_RSC_ENUM_SUBMIT_TARGET; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_SUBMIT_TARGET); + break; + case PROPERTY_ID_ORIENTATION: + pStringItemsResId = RID_RSC_ENUM_ORIENTATION; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_ORIENTATION); + break; + case PROPERTY_ID_CELL_EXCHANGE_TYPE: + pStringItemsResId = RID_RSC_ENUM_CELL_EXCHANGE_TYPE; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_CELL_EXCHANGE_TYPE); + break; + case PROPERTY_ID_SHOW_SCROLLBARS: + pStringItemsResId = RID_RSC_ENUM_SCROLLBARS; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_SCROLLBARS); + break; + case PROPERTY_ID_VISUALEFFECT: + pStringItemsResId = RID_RSC_ENUM_VISUALEFFECT; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_VISUALEFFECT); + break; + case PROPERTY_ID_TEXTTYPE: + pStringItemsResId = RID_RSC_ENUM_TEXTTYPE; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_TEXTTYPE); + break; + case PROPERTY_ID_LINEEND_FORMAT: + pStringItemsResId = RID_RSC_ENUM_LINEEND_FORMAT; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_LINEEND_FORMAT); + break; + case PROPERTY_ID_XSD_WHITESPACES: + pStringItemsResId = RID_RSC_ENUM_WHITESPACE_HANDLING; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_WHITESPACE_HANDLING); + break; + case PROPERTY_ID_SELECTION_TYPE: + case PROPERTY_ID_SELECTIONMODEL: + pStringItemsResId = RID_RSC_ENUM_SELECTION_TYPE; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_SELECTION_TYPE); + break; + case PROPERTY_ID_SCALE_MODE: + pStringItemsResId = RID_RSC_ENUM_SCALE_MODE; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_SCALE_MODE); + break; + case PROPERTY_ID_WRITING_MODE: + pStringItemsResId = RID_RSC_ENUM_WRITING_MODE; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_WRITING_MODE); + break; + case PROPERTY_ID_WHEEL_BEHAVIOR: + pStringItemsResId = RID_RSC_ENUM_WHEEL_BEHAVIOR; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_WHEEL_BEHAVIOR); + break; + case PROPERTY_ID_TEXT_ANCHOR_TYPE: + pStringItemsResId = RID_RSC_ENUM_TEXT_ANCHOR_TYPE; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_TEXT_ANCHOR_TYPE); + break; + case PROPERTY_ID_SHEET_ANCHOR_TYPE: + pStringItemsResId = RID_RSC_ENUM_SHEET_ANCHOR_TYPE; + nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_SHEET_ANCHOR_TYPE); + break; + default: + OSL_FAIL( "OPropertyInfoService::getPropertyEnumRepresentations: unknown enum property!" ); + break; + } + + std::vector< OUString > aReturn; + + aReturn.reserve(nElements); + for (int i = 0; i < nElements; ++i) + { + aReturn.push_back(PcrRes(pStringItemsResId[i])); + } + + return aReturn; + } + + bool OPropertyInfoService::isComposeable( const OUString& _rPropertyName ) const + { + sal_Int32 nId = getPropertyId( _rPropertyName ); + if ( nId == -1 ) + return false; + + sal_uInt32 nFlags = getPropertyUIFlags( nId ); + return ( nFlags & PROP_FLAG_COMPOSEABLE ) != 0; + } + + + const OPropertyInfoImpl* OPropertyInfoService::getPropertyInfo(const OUString& _rName) + { + // Initialization + if(!s_pPropertyInfos) + getPropertyInfo(); + OPropertyInfoImpl aSearch(_rName, 0, OUString(), 0, "", 0); + + const OPropertyInfoImpl* pInfo = std::lower_bound( + s_pPropertyInfos, s_pPropertyInfos + s_nCount, aSearch, PropertyInfoLessByName() ); + + if ( pInfo == s_pPropertyInfos + s_nCount ) + return nullptr; + + if ( pInfo->sName != _rName ) + return nullptr; + + return pInfo; + } + + + const OPropertyInfoImpl* OPropertyInfoService::getPropertyInfo(sal_Int32 _nId) + { + // Initialization + if(!s_pPropertyInfos) + getPropertyInfo(); + + // TODO: a real structure which allows quick access by name as well as by id + for (sal_uInt16 i = 0; i < s_nCount; i++) + if (s_pPropertyInfos[i].nId == _nId) + return &s_pPropertyInfos[i]; + + return nullptr; + } + + + //= DefaultEnumRepresentation + + + DefaultEnumRepresentation::DefaultEnumRepresentation( const IPropertyInfoService& _rInfo, const Type& _rType, sal_Int32 _nPropertyId ) + :m_rMetaData( _rInfo ) + ,m_aType( _rType ) + ,m_nPropertyId( _nPropertyId ) + { + } + + + DefaultEnumRepresentation::~DefaultEnumRepresentation() + { + } + + + std::vector< OUString > DefaultEnumRepresentation::getDescriptions() const + { + return m_rMetaData.getPropertyEnumRepresentations( m_nPropertyId ); + } + + + void DefaultEnumRepresentation::getValueFromDescription( const OUString& _rDescription, Any& _out_rValue ) const + { + sal_uInt32 nPropertyUIFlags = m_rMetaData.getPropertyUIFlags( m_nPropertyId ); + std::vector< OUString > aEnumStrings = m_rMetaData.getPropertyEnumRepresentations( m_nPropertyId ); + std::vector< OUString >::const_iterator pos = std::find( aEnumStrings.begin(), aEnumStrings.end(), _rDescription ); + if ( pos != aEnumStrings.end() ) + { + sal_Int32 nPos = pos - aEnumStrings.begin(); + if ( ( nPropertyUIFlags & PROP_FLAG_ENUM_ONE ) == PROP_FLAG_ENUM_ONE ) + // enum value starting with 1 + ++nPos; + + switch ( m_aType.getTypeClass() ) + { + case TypeClass_ENUM: + _out_rValue = ::cppu::int2enum( nPos, m_aType ); + break; + + case TypeClass_SHORT: + _out_rValue <<= static_cast<sal_Int16>(nPos); + break; + + case TypeClass_UNSIGNED_SHORT: + _out_rValue <<= static_cast<sal_uInt16>(nPos); + break; + + case TypeClass_UNSIGNED_LONG: + _out_rValue <<= static_cast<sal_uInt32>(nPos); + break; + + default: + _out_rValue <<= nPos; + break; + } + } + else + { + OSL_FAIL( "DefaultEnumRepresentation::getValueFromDescription: could not translate the enum string!" ); + _out_rValue.clear(); + } + } + + + OUString DefaultEnumRepresentation::getDescriptionForValue( const Any& _rEnumValue ) const + { + OUString sReturn; + sal_Int32 nIntValue = -1; + OSL_VERIFY( ::cppu::enum2int( nIntValue, _rEnumValue ) ); + + sal_uInt32 nUIFlags = m_rMetaData.getPropertyUIFlags( m_nPropertyId ); + if ( ( nUIFlags & PROP_FLAG_ENUM_ONE ) == PROP_FLAG_ENUM_ONE ) + // enum value starting with 1 + --nIntValue; + + std::vector< OUString > aEnumStrings = m_rMetaData.getPropertyEnumRepresentations( m_nPropertyId ); + if ( ( nIntValue >= 0 ) && ( o3tl::make_unsigned(nIntValue) < aEnumStrings.size() ) ) + { + sReturn = aEnumStrings[ nIntValue ]; + } + else + { + OSL_FAIL( "DefaultEnumRepresentation::getDescriptionForValue: could not translate an enum value" ); + } + return sReturn; + } + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/formmetadata.hxx b/extensions/source/propctrlr/formmetadata.hxx new file mode 100644 index 000000000..0f7aa67d2 --- /dev/null +++ b/extensions/source/propctrlr/formmetadata.hxx @@ -0,0 +1,345 @@ +/* -*- 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 "propertyinfo.hxx" +#include "enumrepresentation.hxx" + + +namespace pcr +{ + + + struct OPropertyInfoImpl; + + + //= OPropertyInfoService + + class OPropertyInfoService final + :public IPropertyInfoService + { + static sal_uInt16 s_nCount; + static OPropertyInfoImpl* s_pPropertyInfos; + // TODO: a real structure which allows quick access by name as well as by id + + public: + // IPropertyInfoService + virtual sal_Int32 getPropertyId(const OUString& _rName) const override; + virtual OUString getPropertyTranslation(sal_Int32 _nId) const override; + virtual OUString getPropertyHelpId(sal_Int32 _nId) const override; + virtual sal_Int16 getPropertyPos(sal_Int32 _nId) const override; + virtual sal_uInt32 getPropertyUIFlags(sal_Int32 _nId) const override; + virtual std::vector< OUString > getPropertyEnumRepresentations(sal_Int32 _nId) const override; + + bool isComposeable( const OUString& _rPropertyName ) const; + + private: + static const OPropertyInfoImpl* getPropertyInfo(); + + static const OPropertyInfoImpl* getPropertyInfo(const OUString& _rName); + static const OPropertyInfoImpl* getPropertyInfo(sal_Int32 _nId); + }; + + + //= DefaultEnumRepresentation + + /** an implementation of the IPropertyEnumRepresentation + + To be used with properties which, in formmetadata.cxx, are declared as ENUM. + */ + class DefaultEnumRepresentation : public IPropertyEnumRepresentation + { + private: + const IPropertyInfoService& m_rMetaData; + css::uno::Type m_aType; + const sal_Int32 m_nPropertyId; + + public: + /** constructs an instance + + @param _rInfo + An instance implementing IPropertyInfoService. Must live at least as + long as the DefaultEnumRepresentation should live. + */ + DefaultEnumRepresentation( const IPropertyInfoService& _rInfo, const css::uno::Type& _rType, sal_Int32 _nPropertyId ); + + protected: + virtual ~DefaultEnumRepresentation() override; + + protected: + // IPropertyEnumRepresentation implementqation + virtual std::vector< OUString > + getDescriptions() const override; + virtual void getValueFromDescription( const OUString& _rDescription, css::uno::Any& _out_rValue ) const override; + virtual OUString getDescriptionForValue( const css::uno::Any& _rEnumValue ) const override; + + private: + DefaultEnumRepresentation( const DefaultEnumRepresentation& ) = delete; + DefaultEnumRepresentation& operator=( const DefaultEnumRepresentation& ) = delete; + }; + + + //= UI flags (for all browsable properties) + + +#define PROP_FLAG_NONE 0x00000000 // no special flag +#define PROP_FLAG_FORM_VISIBLE 0x00000001 // the property is visible when inspecting a form object +#define PROP_FLAG_DIALOG_VISIBLE 0x00000002 // the property is visible when inspecting a dialog object +#define PROP_FLAG_DATA_PROPERTY 0x00000004 // the property is to appear on the "Data" page +#define PROP_FLAG_ENUM 0x00000020 // the property is some kind of enum property, i.e. its + // value is chosen from a fixed list of possible values +#define PROP_FLAG_ENUM_ONE 0x00000060 // the property is an enum property starting with 1 + // (note that this includes PROP_FLAG_ENUM) +#define PROP_FLAG_COMPOSEABLE 0x00000080 // the property is "composeable", i.e. an intersection of property + // sets should expose it, if all elements do +#define PROP_FLAG_EXPERIMENTAL 0x00000100 // the property is experimental, i.e. should not appear in the + // UI, unless experimental properties are enabled by a configuration + // option +#define PROP_FLAG_REPORT_INVISIBLE 0x00000200 // the property should not appear in the Report Designer UI + + + //= property ids (for all browsable properties) + + + #define PROPERTY_ID_NAME 1 + #define PROPERTY_ID_LABEL 2 + #define PROPERTY_ID_CONTROLLABEL 3 + #define PROPERTY_ID_MAXTEXTLEN 4 + #define PROPERTY_ID_EDITMASK 5 + #define PROPERTY_ID_LITERALMASK 6 + #define PROPERTY_ID_STRICTFORMAT 7 + #define PROPERTY_ID_ENABLED 8 + #define PROPERTY_ID_READONLY 9 + #define PROPERTY_ID_PRINTABLE 10 + #define PROPERTY_ID_CONTROLSOURCE 11 + #define PROPERTY_ID_TABSTOP 12 + #define PROPERTY_ID_TABINDEX 13 + #define PROPERTY_ID_DATASOURCE 14 + #define PROPERTY_ID_COMMAND 15 + #define PROPERTY_ID_COMMANDTYPE 16 + #define PROPERTY_ID_FILTER 17 + #define PROPERTY_ID_SORT 18 + #define PROPERTY_ID_INSERTONLY 19 + #define PROPERTY_ID_ALLOWADDITIONS 20 + #define PROPERTY_ID_ALLOWEDITS 21 + #define PROPERTY_ID_ALLOWDELETIONS 22 + #define PROPERTY_ID_GROUP_NAME 23 + #define PROPERTY_ID_NAVIGATION 24 + #define PROPERTY_ID_CYCLE 25 + #define PROPERTY_ID_HIDDEN_VALUE 26 + #define PROPERTY_ID_VALUEMIN 27 + #define PROPERTY_ID_VALUEMAX 28 + #define PROPERTY_ID_VALUESTEP 29 + #define PROPERTY_ID_DEFAULT_VALUE 30 + #define PROPERTY_ID_DECIMAL_ACCURACY 31 + #define PROPERTY_ID_SHOWTHOUSANDSEP 32 + #define PROPERTY_ID_REFVALUE 33 + #define PROPERTY_ID_CURRENCYSYMBOL 34 + #define PROPERTY_ID_CURRSYM_POSITION 35 + #define PROPERTY_ID_DATEMIN 36 + #define PROPERTY_ID_DATEMAX 37 + #define PROPERTY_ID_DATEFORMAT 38 + #define PROPERTY_ID_SELECTEDITEMS 39 + #define PROPERTY_ID_DEFAULT_DATE 40 + #define PROPERTY_ID_TIMEMIN 41 + #define PROPERTY_ID_TIMEMAX 42 + #define PROPERTY_ID_TIMEFORMAT 43 + #define PROPERTY_ID_DEFAULT_TIME 44 + #define PROPERTY_ID_EFFECTIVE_MIN 45 + #define PROPERTY_ID_EFFECTIVE_MAX 46 + #define PROPERTY_ID_EFFECTIVE_DEFAULT 47 + #define PROPERTY_ID_FORMATKEY 48 + #define PROPERTY_ID_CLASSID 50 + #define PROPERTY_ID_HEIGHT 51 + #define PROPERTY_ID_WIDTH 52 + #define PROPERTY_ID_BOUNDCOLUMN 53 + #define PROPERTY_ID_LISTSOURCETYPE 54 + #define PROPERTY_ID_LISTSOURCE 55 + #define PROPERTY_ID_LISTINDEX 56 + #define PROPERTY_ID_STRINGITEMLIST 57 + #define PROPERTY_ID_DEFAULT_TEXT 58 + #define PROPERTY_ID_FONT 59 + #define PROPERTY_ID_ALIGN 60 + #define PROPERTY_ID_ROWHEIGHT 61 + #define PROPERTY_ID_BACKGROUNDCOLOR 62 + #define PROPERTY_ID_FILLCOLOR 63 + #define PROPERTY_ID_ESCAPE_PROCESSING 64 + #define PROPERTY_ID_LINECOLOR 65 + #define PROPERTY_ID_BORDER 66 + #define PROPERTY_ID_DROPDOWN 67 + #define PROPERTY_ID_AUTOCOMPLETE 68 + #define PROPERTY_ID_LINECOUNT 69 + #define PROPERTY_ID_WORDBREAK 70 + #define PROPERTY_ID_MULTILINE 71 + #define PROPERTY_ID_MULTISELECTION 72 + #define PROPERTY_ID_AUTOLINEBREAK 73 + #define PROPERTY_ID_HSCROLL 74 + #define PROPERTY_ID_VSCROLL 75 + #define PROPERTY_ID_SPIN 76 + #define PROPERTY_ID_BUTTONTYPE 77 + #define PROPERTY_ID_TARGET_URL 78 + #define PROPERTY_ID_TARGET_FRAME 79 + #define PROPERTY_ID_SUBMIT_ACTION 80 + #define PROPERTY_ID_SUBMIT_TARGET 81 + #define PROPERTY_ID_SUBMIT_METHOD 82 + #define PROPERTY_ID_SUBMIT_ENCODING 83 + #define PROPERTY_ID_DEFAULT_STATE 84 + #define PROPERTY_ID_DEFAULTBUTTON 85 + #define PROPERTY_ID_IMAGE_URL 86 + #define PROPERTY_ID_DEFAULT_SELECT_SEQ 87 + #define PROPERTY_ID_ECHO_CHAR 88 + #define PROPERTY_ID_EMPTY_IS_NULL 89 + #define PROPERTY_ID_TRISTATE 90 + #define PROPERTY_ID_MASTERFIELDS 91 + #define PROPERTY_ID_DETAILFIELDS 92 + #define PROPERTY_ID_RECORDMARKER 93 + #define PROPERTY_ID_FILTERPROPOSAL 94 + #define PROPERTY_ID_TAG 95 + #define PROPERTY_ID_HELPTEXT 96 + #define PROPERTY_ID_HELPURL 97 + #define PROPERTY_ID_HASNAVIGATION 98 + #define PROPERTY_ID_POSITIONX 99 + #define PROPERTY_ID_POSITIONY 100 + #define PROPERTY_ID_TITLE 101 + #define PROPERTY_ID_STEP 102 + #define PROPERTY_ID_PROGRESSVALUE 103 + #define PROPERTY_ID_PROGRESSVALUE_MIN 104 + #define PROPERTY_ID_PROGRESSVALUE_MAX 105 + #define PROPERTY_ID_SCROLLVALUE 106 + #define PROPERTY_ID_SCROLLVALUE_MAX 107 + #define PROPERTY_ID_LINEINCREMENT 108 + #define PROPERTY_ID_BLOCKINCREMENT 109 + #define PROPERTY_ID_VISIBLESIZE 110 + #define PROPERTY_ID_ORIENTATION 111 + #define PROPERTY_ID_IMAGEPOSITION 112 + #define PROPERTY_ID_DATE 113 + #define PROPERTY_ID_STATE 114 + #define PROPERTY_ID_TIME 115 + #define PROPERTY_ID_VALUE 116 + #define PROPERTY_ID_SCALEIMAGE 117 + #define PROPERTY_ID_PUSHBUTTONTYPE 118 + #define PROPERTY_ID_EFFECTIVE_VALUE 119 + #define PROPERTY_ID_TEXT 120 + #define PROPERTY_ID_BOUND_CELL 121 + #define PROPERTY_ID_LIST_CELL_RANGE 122 + #define PROPERTY_ID_CELL_EXCHANGE_TYPE 123 + #define PROPERTY_ID_SCROLLVALUE_MIN 124 + #define PROPERTY_ID_DEFAULT_SCROLLVALUE 125 + #define PROPERTY_ID_REPEAT_DELAY 126 + #define PROPERTY_ID_SYMBOLCOLOR 127 + #define PROPERTY_ID_SPINVALUE 128 + #define PROPERTY_ID_SPINVALUE_MIN 129 + #define PROPERTY_ID_SPINVALUE_MAX 130 + #define PROPERTY_ID_DEFAULT_SPINVALUE 131 + #define PROPERTY_ID_SPININCREMENT 132 + #define PROPERTY_ID_REPEAT 133 + #define PROPERTY_ID_SHOW_SCROLLBARS 134 + #define PROPERTY_ID_ICONSIZE 135 + #define PROPERTY_ID_SHOW_POSITION 136 + #define PROPERTY_ID_SHOW_NAVIGATION 137 + #define PROPERTY_ID_SHOW_RECORDACTIONS 138 + #define PROPERTY_ID_SHOW_FILTERSORT 139 + #define PROPERTY_ID_TEXTTYPE 140 + #define PROPERTY_ID_LINEEND_FORMAT 141 + #define PROPERTY_ID_TOGGLE 142 + #define PROPERTY_ID_FOCUSONCLICK 143 + #define PROPERTY_ID_HIDEINACTIVESELECTION 144 + #define PROPERTY_ID_VISUALEFFECT 145 + #define PROPERTY_ID_BORDERCOLOR 146 + #define PROPERTY_ID_XML_DATA_MODEL 147 + #define PROPERTY_ID_BIND_EXPRESSION 148 + #define PROPERTY_ID_XSD_REQUIRED 149 + #define PROPERTY_ID_XSD_RELEVANT 150 + #define PROPERTY_ID_XSD_READONLY 151 + #define PROPERTY_ID_XSD_CONSTRAINT 152 + #define PROPERTY_ID_XSD_CALCULATION 153 + #define PROPERTY_ID_XSD_DATA_TYPE 154 + #define PROPERTY_ID_XSD_WHITESPACES 155 + #define PROPERTY_ID_XSD_PATTERN 156 + #define PROPERTY_ID_XSD_LENGTH 157 + #define PROPERTY_ID_XSD_MIN_LENGTH 158 + #define PROPERTY_ID_XSD_MAX_LENGTH 159 + #define PROPERTY_ID_XSD_TOTAL_DIGITS 160 + #define PROPERTY_ID_XSD_FRACTION_DIGITS 161 + #define PROPERTY_ID_XSD_MAX_INCLUSIVE_INT 162 + #define PROPERTY_ID_XSD_MAX_EXCLUSIVE_INT 163 + #define PROPERTY_ID_XSD_MIN_INCLUSIVE_INT 164 + #define PROPERTY_ID_XSD_MIN_EXCLUSIVE_INT 165 + #define PROPERTY_ID_XSD_MAX_INCLUSIVE_DOUBLE 166 + #define PROPERTY_ID_XSD_MAX_EXCLUSIVE_DOUBLE 167 + #define PROPERTY_ID_XSD_MIN_INCLUSIVE_DOUBLE 168 + #define PROPERTY_ID_XSD_MIN_EXCLUSIVE_DOUBLE 169 + #define PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE 170 + #define PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE 171 + #define PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE 172 + #define PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE 173 + #define PROPERTY_ID_XSD_MAX_INCLUSIVE_TIME 174 + #define PROPERTY_ID_XSD_MAX_EXCLUSIVE_TIME 175 + #define PROPERTY_ID_XSD_MIN_INCLUSIVE_TIME 176 + #define PROPERTY_ID_XSD_MIN_EXCLUSIVE_TIME 177 + #define PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE_TIME 178 + #define PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE_TIME 179 + #define PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE_TIME 180 + #define PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE_TIME 181 + #define PROPERTY_ID_UNCHECKEDREFVALUE 182 + #define PROPERTY_ID_SUBMISSION_ID 183 + #define PROPERTY_ID_XFORMS_BUTTONTYPE 184 + #define PROPERTY_ID_LIST_BINDING 185 + #define PROPERTY_ID_VERTICAL_ALIGN 186 + #define PROPERTY_ID_BINDING_NAME 187 + #define PROPERTY_ID_DECORATION 188 + #define PROPERTY_ID_SELECTION_TYPE 189 + #define PROPERTY_ID_ROOT_DISPLAYED 190 + #define PROPERTY_ID_SHOWS_HANDLES 191 + #define PROPERTY_ID_SHOWS_ROOT_HANDLES 192 + #define PROPERTY_ID_EDITABLE 193 + #define PROPERTY_ID_INVOKES_STOP_NOT_EDITING 194 + #define PROPERTY_ID_NOLABEL 195 + #define PROPERTY_ID_SCALE_MODE 196 + #define PROPERTY_ID_INPUT_REQUIRED 197 + #define PROPERTY_ID_WRITING_MODE 198 + #define PROPERTY_ID_ENABLE_VISIBLE 199 + #define PROPERTY_ID_WHEEL_BEHAVIOR 200 + #define PROPERTY_ID_TEXT_ANCHOR_TYPE 201 + #define PROPERTY_ID_SHEET_ANCHOR_TYPE 202 + #define PROPERTY_ID_SCROLL_WIDTH 203 + #define PROPERTY_ID_SCROLL_HEIGHT 204 + #define PROPERTY_ID_SCROLL_TOP 205 + #define PROPERTY_ID_SCROLL_LEFT 206 + #define PROPERTY_ID_TYPEDITEMLIST 207 + #define PROPERTY_ID_SELECTIONMODEL 208 + #define PROPERTY_ID_USEGRIDLINE 209 + #define PROPERTY_ID_GRIDLINECOLOR 210 + #define PROPERTY_ID_SHOWCOLUMNHEADER 211 + #define PROPERTY_ID_SHOWROWHEADER 212 + #define PROPERTY_ID_HEADERBACKGROUNDCOLOR 213 + #define PROPERTY_ID_HEADERTEXTCOLOR 214 + #define PROPERTY_ID_ACTIVESELECTIONBACKGROUNDCOLOR 215 + #define PROPERTY_ID_ACTIVESELECTIONTEXTCOLOR 216 + #define PROPERTY_ID_INACTIVESELECTIONBACKGROUNDCOLOR 217 + #define PROPERTY_ID_INACTIVESELECTIONTEXTCOLOR 218 + #define PROPERTY_ID_URL 219 + #define PROPERTY_ID_AUTOGROW 220 + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/formstrings.hxx b/extensions/source/propctrlr/formstrings.hxx new file mode 100644 index 000000000..df151ab95 --- /dev/null +++ b/extensions/source/propctrlr/formstrings.hxx @@ -0,0 +1,302 @@ +/* -*- 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 <rtl/ustring.hxx> + +inline constexpr OUStringLiteral PROPERTY_DEFAULTCONTROL = u"DefaultControl"; +inline constexpr OUStringLiteral PROPERTY_INTROSPECTEDOBJECT = u"IntrospectedObject"; +inline constexpr OUStringLiteral PROPERTY_CURRENTPAGE = u"CurrentPage"; +inline constexpr OUStringLiteral PROPERTY_CONTROLCONTEXT = u"ControlContext"; + +// properties +inline constexpr OUStringLiteral PROPERTY_CLASSID = u"ClassId"; +inline constexpr OUStringLiteral PROPERTY_CONTROLLABEL = u"LabelControl"; +inline constexpr OUStringLiteral PROPERTY_LABEL = u"Label"; +inline constexpr OUStringLiteral PROPERTY_TABINDEX = u"TabIndex"; +inline constexpr OUStringLiteral PROPERTY_WHEEL_BEHAVIOR = u"MouseWheelBehavior"; +inline constexpr OUStringLiteral PROPERTY_TAG = u"Tag"; +inline constexpr OUStringLiteral PROPERTY_NAME = u"Name"; +inline constexpr OUStringLiteral PROPERTY_GROUP_NAME = u"GroupName"; +inline constexpr OUStringLiteral PROPERTY_VALUE = u"Value"; +inline constexpr OUStringLiteral PROPERTY_TEXT = u"Text"; +inline constexpr OUStringLiteral PROPERTY_NAVIGATION = u"NavigationBarMode"; +inline constexpr OUStringLiteral PROPERTY_CYCLE = u"Cycle"; +inline constexpr OUStringLiteral PROPERTY_CONTROLSOURCE = u"DataField"; +inline constexpr OUStringLiteral PROPERTY_INPUT_REQUIRED = u"InputRequired"; +inline constexpr OUStringLiteral PROPERTY_ENABLED = u"Enabled"; +inline constexpr OUStringLiteral PROPERTY_ENABLE_VISIBLE = u"EnableVisible"; +inline constexpr OUStringLiteral PROPERTY_READONLY = u"ReadOnly"; +inline constexpr OUStringLiteral PROPERTY_FILTER = u"Filter"; +inline constexpr OUStringLiteral PROPERTY_WIDTH = u"Width"; +inline constexpr OUStringLiteral PROPERTY_MULTILINE = u"MultiLine"; +inline constexpr OUStringLiteral PROPERTY_WORDBREAK = u"WordBreak"; +inline constexpr OUStringLiteral PROPERTY_TARGET_URL = u"TargetURL"; +inline constexpr OUStringLiteral PROPERTY_TARGET_FRAME = u"TargetFrame"; +inline constexpr OUStringLiteral PROPERTY_MAXTEXTLEN = u"MaxTextLen"; +inline constexpr OUStringLiteral PROPERTY_EDITMASK = u"EditMask"; +inline constexpr OUStringLiteral PROPERTY_SPIN = u"Spin"; +inline constexpr OUStringLiteral PROPERTY_TRISTATE = u"TriState"; +inline constexpr OUStringLiteral PROPERTY_HIDDEN_VALUE = u"HiddenValue"; +inline constexpr OUStringLiteral PROPERTY_BUTTONTYPE = u"ButtonType"; +inline constexpr OUStringLiteral PROPERTY_XFORMS_BUTTONTYPE = u"XFormsButtonType"; +inline constexpr OUStringLiteral PROPERTY_STRINGITEMLIST = u"StringItemList"; +inline constexpr OUStringLiteral PROPERTY_TYPEDITEMLIST = u"TypedItemList"; +inline constexpr OUStringLiteral PROPERTY_DEFAULT_TEXT = u"DefaultText"; +inline constexpr OUStringLiteral PROPERTY_DEFAULT_STATE = u"DefaultState"; +inline constexpr OUStringLiteral PROPERTY_FORMATKEY = u"FormatKey"; +inline constexpr OUStringLiteral PROPERTY_FORMATSSUPPLIER = u"FormatsSupplier"; +inline constexpr OUStringLiteral PROPERTY_SUBMIT_ACTION = u"SubmitAction"; +inline constexpr OUStringLiteral PROPERTY_SUBMIT_TARGET = u"SubmitTarget"; +inline constexpr OUStringLiteral PROPERTY_SUBMIT_METHOD = u"SubmitMethod"; +inline constexpr OUStringLiteral PROPERTY_SUBMIT_ENCODING = u"SubmitEncoding"; +inline constexpr OUStringLiteral PROPERTY_IMAGE_URL = u"ImageURL"; +inline constexpr OUStringLiteral PROPERTY_GRAPHIC = u"Graphic"; +inline constexpr OUStringLiteral PROPERTY_EMPTY_IS_NULL = u"ConvertEmptyToNull"; +inline constexpr OUStringLiteral PROPERTY_LISTSOURCETYPE = u"ListSourceType"; +inline constexpr OUStringLiteral PROPERTY_LISTSOURCE = u"ListSource"; +inline constexpr OUStringLiteral PROPERTY_DEFAULT_SELECT_SEQ = u"DefaultSelection"; +inline constexpr OUStringLiteral PROPERTY_MULTISELECTION = u"MultiSelection"; +inline constexpr OUStringLiteral PROPERTY_ALIGN = u"Align"; +inline constexpr OUStringLiteral PROPERTY_VERTICAL_ALIGN = u"VerticalAlign"; +inline constexpr OUStringLiteral PROPERTY_DEFAULT_DATE = u"DefaultDate"; +inline constexpr OUStringLiteral PROPERTY_DEFAULT_TIME = u"DefaultTime"; +inline constexpr OUStringLiteral PROPERTY_DEFAULT_VALUE = u"DefaultValue"; +inline constexpr OUStringLiteral PROPERTY_DECIMAL_ACCURACY = u"DecimalAccuracy"; +inline constexpr OUStringLiteral PROPERTY_REFVALUE = u"RefValue"; +inline constexpr OUStringLiteral PROPERTY_UNCHECKEDREFVALUE = u"SecondaryRefValue"; +inline constexpr OUStringLiteral PROPERTY_VALUEMIN = u"ValueMin"; +inline constexpr OUStringLiteral PROPERTY_VALUEMAX = u"ValueMax"; +inline constexpr OUStringLiteral PROPERTY_STRICTFORMAT = u"StrictFormat"; +inline constexpr OUStringLiteral PROPERTY_ALLOWADDITIONS = u"AllowInserts"; +inline constexpr OUStringLiteral PROPERTY_ALLOWEDITS = u"AllowUpdates"; +inline constexpr OUStringLiteral PROPERTY_ALLOWDELETIONS = u"AllowDeletes"; +inline constexpr OUStringLiteral PROPERTY_MASTERFIELDS = u"MasterFields"; +inline constexpr OUStringLiteral PROPERTY_LITERALMASK = u"LiteralMask"; +inline constexpr OUStringLiteral PROPERTY_VALUESTEP = u"ValueStep"; +inline constexpr OUStringLiteral PROPERTY_SHOWTHOUSANDSEP = u"ShowThousandsSeparator"; +inline constexpr OUStringLiteral PROPERTY_CURRENCYSYMBOL = u"CurrencySymbol"; +inline constexpr OUStringLiteral PROPERTY_DATEFORMAT = u"DateFormat"; +inline constexpr OUStringLiteral PROPERTY_DATEMIN = u"DateMin"; +inline constexpr OUStringLiteral PROPERTY_DATEMAX = u"DateMax"; +inline constexpr OUStringLiteral PROPERTY_TIMEFORMAT = u"TimeFormat"; +inline constexpr OUStringLiteral PROPERTY_TIMEMIN = u"TimeMin"; +inline constexpr OUStringLiteral PROPERTY_TIMEMAX = u"TimeMax"; +inline constexpr OUStringLiteral PROPERTY_LINECOUNT = u"LineCount"; +inline constexpr OUStringLiteral PROPERTY_BOUNDCOLUMN = u"BoundColumn"; +inline constexpr OUStringLiteral PROPERTY_BACKGROUNDCOLOR = u"BackgroundColor"; +inline constexpr OUStringLiteral PROPERTY_FILLCOLOR = u"FillColor"; +inline constexpr OUStringLiteral PROPERTY_TEXTCOLOR = u"TextColor"; +inline constexpr OUStringLiteral PROPERTY_LINECOLOR = u"LineColor"; +inline constexpr OUStringLiteral PROPERTY_BORDER = u"Border"; +inline constexpr OUStringLiteral PROPERTY_ICONSIZE = u"IconSize"; +inline constexpr OUStringLiteral PROPERTY_DROPDOWN = u"Dropdown"; +inline constexpr OUStringLiteral PROPERTY_HSCROLL = u"HScroll"; +inline constexpr OUStringLiteral PROPERTY_VSCROLL = u"VScroll"; +inline constexpr OUStringLiteral PROPERTY_SHOW_SCROLLBARS = u"ShowScrollbars"; +inline constexpr OUStringLiteral PROPERTY_TABSTOP = u"Tabstop"; +inline constexpr OUStringLiteral PROPERTY_AUTOCOMPLETE = u"Autocomplete"; +inline constexpr OUStringLiteral PROPERTY_PRINTABLE = u"Printable"; +inline constexpr OUStringLiteral PROPERTY_ECHO_CHAR = u"EchoChar"; +inline constexpr OUStringLiteral PROPERTY_ROWHEIGHT = u"RowHeight"; +inline constexpr OUStringLiteral PROPERTY_HELPTEXT = u"HelpText"; +inline constexpr OUStringLiteral PROPERTY_FONT = u"FontDescriptor"; +inline constexpr OUStringLiteral PROPERTY_FONT_NAME = u"FontName"; +inline constexpr OUStringLiteral PROPERTY_FONT_STYLENAME = u"FontStyleName"; +inline constexpr OUStringLiteral PROPERTY_FONT_FAMILY = u"FontFamily"; +inline constexpr OUStringLiteral PROPERTY_FONT_CHARSET = u"FontCharset"; +inline constexpr OUStringLiteral PROPERTY_FONT_HEIGHT = u"FontHeight"; +inline constexpr OUStringLiteral PROPERTY_FONT_WEIGHT = u"FontWeight"; +inline constexpr OUStringLiteral PROPERTY_FONT_SLANT = u"FontSlant"; +inline constexpr OUStringLiteral PROPERTY_FONT_UNDERLINE = u"FontUnderline"; +inline constexpr OUStringLiteral PROPERTY_FONT_STRIKEOUT = u"FontStrikeout"; +inline constexpr OUStringLiteral PROPERTY_FONT_RELIEF = u"FontRelief"; +inline constexpr OUStringLiteral PROPERTY_FONT_EMPHASIS_MARK = u"FontEmphasisMark"; +inline constexpr OUStringLiteral PROPERTY_TEXTLINECOLOR = u"TextLineColor"; +inline constexpr OUStringLiteral PROPERTY_HELPURL = u"HelpURL"; +inline constexpr OUStringLiteral PROPERTY_RECORDMARKER = u"HasRecordMarker"; +inline constexpr OUStringLiteral PROPERTY_EFFECTIVE_DEFAULT = u"EffectiveDefault"; +inline constexpr OUStringLiteral PROPERTY_EFFECTIVE_MIN = u"EffectiveMin"; +inline constexpr OUStringLiteral PROPERTY_EFFECTIVE_MAX = u"EffectiveMax"; +inline constexpr OUStringLiteral PROPERTY_FILTERPROPOSAL = u"UseFilterValueProposal"; +inline constexpr OUStringLiteral PROPERTY_CURRSYM_POSITION = u"PrependCurrencySymbol"; +inline constexpr OUStringLiteral PROPERTY_COMMAND = u"Command"; +inline constexpr OUStringLiteral PROPERTY_COMMANDTYPE = u"CommandType"; +inline constexpr OUStringLiteral PROPERTY_INSERTONLY = u"IgnoreResult"; +inline constexpr OUStringLiteral PROPERTY_ESCAPE_PROCESSING = u"EscapeProcessing"; +inline constexpr OUStringLiteral PROPERTY_TITLE = u"Title"; +inline constexpr OUStringLiteral PROPERTY_SORT = u"Order"; +inline constexpr OUStringLiteral PROPERTY_DATASOURCE = u"DataSourceName"; +inline constexpr OUStringLiteral PROPERTY_DETAILFIELDS = u"DetailFields"; +inline constexpr OUStringLiteral PROPERTY_DEFAULTBUTTON = u"DefaultButton"; +inline constexpr OUStringLiteral PROPERTY_LISTINDEX = u"ListIndex"; +inline constexpr OUStringLiteral PROPERTY_HEIGHT = u"Height"; +inline constexpr OUStringLiteral PROPERTY_HASNAVIGATION = u"HasNavigationBar"; +inline constexpr OUStringLiteral PROPERTY_POSITIONX = u"PositionX"; +inline constexpr OUStringLiteral PROPERTY_POSITIONY = u"PositionY"; +inline constexpr OUStringLiteral PROPERTY_AUTOGROW = u"AutoGrow"; +inline constexpr OUStringLiteral PROPERTY_STEP = u"Step"; +inline constexpr OUStringLiteral PROPERTY_WORDLINEMODE = u"FontWordLineMode"; +inline constexpr OUStringLiteral PROPERTY_PROGRESSVALUE = u"ProgressValue"; +inline constexpr OUStringLiteral PROPERTY_PROGRESSVALUE_MIN = u"ProgressValueMin"; +inline constexpr OUStringLiteral PROPERTY_PROGRESSVALUE_MAX = u"ProgressValueMax"; +inline constexpr OUStringLiteral PROPERTY_SCROLLVALUE = u"ScrollValue"; +inline constexpr OUStringLiteral PROPERTY_DEFAULT_SCROLLVALUE = u"DefaultScrollValue"; +inline constexpr OUStringLiteral PROPERTY_SCROLLVALUE_MIN = u"ScrollValueMin"; +inline constexpr OUStringLiteral PROPERTY_SCROLLVALUE_MAX = u"ScrollValueMax"; +inline constexpr OUStringLiteral PROPERTY_SCROLL_WIDTH = u"ScrollWidth"; +inline constexpr OUStringLiteral PROPERTY_SCROLL_HEIGHT = u"ScrollHeight"; +inline constexpr OUStringLiteral PROPERTY_SCROLL_TOP = u"ScrollTop"; +inline constexpr OUStringLiteral PROPERTY_SCROLL_LEFT = u"ScrollLeft"; +inline constexpr OUStringLiteral PROPERTY_LINEINCREMENT = u"LineIncrement"; +inline constexpr OUStringLiteral PROPERTY_BLOCKINCREMENT = u"BlockIncrement"; +inline constexpr OUStringLiteral PROPERTY_VISIBLESIZE = u"VisibleSize"; +inline constexpr OUStringLiteral PROPERTY_ORIENTATION = u"Orientation"; +inline constexpr OUStringLiteral PROPERTY_IMAGEPOSITION = u"ImagePosition"; +inline constexpr OUStringLiteral PROPERTY_ACTIVE_CONNECTION = u"ActiveConnection"; +inline constexpr OUStringLiteral PROPERTY_ACTIVECOMMAND = u"ActiveCommand"; +inline constexpr OUStringLiteral PROPERTY_DATE = u"Date"; +inline constexpr OUStringLiteral PROPERTY_STATE = u"State"; +inline constexpr OUStringLiteral PROPERTY_TIME = u"Time"; +inline constexpr OUStringLiteral PROPERTY_SCALEIMAGE = u"ScaleImage"; +inline constexpr OUStringLiteral PROPERTY_SCALE_MODE = u"ScaleMode"; +inline constexpr OUStringLiteral PROPERTY_PUSHBUTTONTYPE = u"PushButtonType"; +inline constexpr OUStringLiteral PROPERTY_EFFECTIVE_VALUE = u"EffectiveValue"; +inline constexpr OUStringLiteral PROPERTY_SELECTEDITEMS = u"SelectedItems"; +inline constexpr OUStringLiteral PROPERTY_REPEAT = u"Repeat"; +inline constexpr OUStringLiteral PROPERTY_REPEAT_DELAY = u"RepeatDelay"; +inline constexpr OUStringLiteral PROPERTY_SYMBOLCOLOR = u"SymbolColor"; +inline constexpr OUStringLiteral PROPERTY_SPINVALUE = u"SpinValue"; +inline constexpr OUStringLiteral PROPERTY_SPINVALUE_MIN = u"SpinValueMin"; +inline constexpr OUStringLiteral PROPERTY_SPINVALUE_MAX = u"SpinValueMax"; +inline constexpr OUStringLiteral PROPERTY_DEFAULT_SPINVALUE = u"DefaultSpinValue"; +inline constexpr OUStringLiteral PROPERTY_SPININCREMENT = u"SpinIncrement"; +inline constexpr OUStringLiteral PROPERTY_SHOW_POSITION = u"ShowPosition"; +inline constexpr OUStringLiteral PROPERTY_SHOW_NAVIGATION = u"ShowNavigation"; +inline constexpr OUStringLiteral PROPERTY_SHOW_RECORDACTIONS = u"ShowRecordActions"; +inline constexpr OUStringLiteral PROPERTY_SHOW_FILTERSORT = u"ShowFilterSort"; +inline constexpr OUStringLiteral PROPERTY_LINEEND_FORMAT = u"LineEndFormat"; +inline constexpr OUStringLiteral PROPERTY_DECORATION = u"Decoration"; +inline constexpr OUStringLiteral PROPERTY_NOLABEL = u"NoLabel"; +inline constexpr OUStringLiteral PROPERTY_URL = u"URL"; + +inline constexpr OUStringLiteral PROPERTY_SELECTION_TYPE = u"SelectionType"; +inline constexpr OUStringLiteral PROPERTY_ROOT_DISPLAYED = u"RootDisplayed"; +inline constexpr OUStringLiteral PROPERTY_SHOWS_HANDLES = u"ShowsHandles"; +inline constexpr OUStringLiteral PROPERTY_SHOWS_ROOT_HANDLES = u"ShowsRootHandles"; +inline constexpr OUStringLiteral PROPERTY_EDITABLE = u"Editable"; +inline constexpr OUStringLiteral PROPERTY_INVOKES_STOP_NOT_EDITING = u"InvokesStopNodeEditing"; + +inline constexpr OUStringLiteral PROPERTY_TOGGLE = u"Toggle"; +inline constexpr OUStringLiteral PROPERTY_FOCUSONCLICK = u"FocusOnClick"; +inline constexpr OUStringLiteral PROPERTY_HIDEINACTIVESELECTION = u"HideInactiveSelection"; +inline constexpr OUStringLiteral PROPERTY_VISUALEFFECT = u"VisualEffect"; +inline constexpr OUStringLiteral PROPERTY_BORDERCOLOR = u"BorderColor"; + +inline constexpr OUStringLiteral PROPERTY_ADDRESS = u"Address"; +inline constexpr OUStringLiteral PROPERTY_REFERENCE_SHEET = u"ReferenceSheet"; +inline constexpr OUStringLiteral PROPERTY_UI_REPRESENTATION = u"UserInterfaceRepresentation"; + +inline constexpr OUStringLiteral PROPERTY_XML_DATA_MODEL = u"XMLDataModel"; +inline constexpr OUStringLiteral PROPERTY_BINDING_NAME = u"BindingName"; +inline constexpr OUStringLiteral PROPERTY_BIND_EXPRESSION = u"BindingExpression"; +inline constexpr OUStringLiteral PROPERTY_LIST_BINDING = u"ListBinding"; +inline constexpr OUStringLiteral PROPERTY_XSD_REQUIRED = u"RequiredExpression"; +inline constexpr OUStringLiteral PROPERTY_XSD_RELEVANT = u"RelevantExpression"; +inline constexpr OUStringLiteral PROPERTY_XSD_READONLY = u"ReadonlyExpression"; +inline constexpr OUStringLiteral PROPERTY_XSD_CONSTRAINT = u"ConstraintExpression"; +inline constexpr OUStringLiteral PROPERTY_XSD_CALCULATION = u"CalculateExpression"; +inline constexpr OUStringLiteral PROPERTY_XSD_DATA_TYPE = u"Type"; +inline constexpr OUStringLiteral PROPERTY_XSD_WHITESPACES = u"WhiteSpace"; +inline constexpr OUStringLiteral PROPERTY_XSD_PATTERN = u"Pattern"; +inline constexpr OUStringLiteral PROPERTY_XSD_LENGTH = u"Length"; +inline constexpr OUStringLiteral PROPERTY_XSD_MIN_LENGTH = u"MinLength"; +inline constexpr OUStringLiteral PROPERTY_XSD_MAX_LENGTH = u"MaxLength"; +inline constexpr OUStringLiteral PROPERTY_XSD_TOTAL_DIGITS = u"TotalDigits"; +inline constexpr OUStringLiteral PROPERTY_XSD_FRACTION_DIGITS = u"FractionDigits"; +inline constexpr OUStringLiteral PROPERTY_XSD_MAX_INCLUSIVE_INT = u"MaxInclusiveInt"; +inline constexpr OUStringLiteral PROPERTY_XSD_MAX_EXCLUSIVE_INT = u"MaxExclusiveInt"; +inline constexpr OUStringLiteral PROPERTY_XSD_MIN_INCLUSIVE_INT = u"MinInclusiveInt"; +inline constexpr OUStringLiteral PROPERTY_XSD_MIN_EXCLUSIVE_INT = u"MinExclusiveInt"; +inline constexpr OUStringLiteral PROPERTY_XSD_MAX_INCLUSIVE_DOUBLE = u"MaxInclusiveDouble"; +inline constexpr OUStringLiteral PROPERTY_XSD_MAX_EXCLUSIVE_DOUBLE = u"MaxExclusiveDouble"; +inline constexpr OUStringLiteral PROPERTY_XSD_MIN_INCLUSIVE_DOUBLE = u"MinInclusiveDouble"; +inline constexpr OUStringLiteral PROPERTY_XSD_MIN_EXCLUSIVE_DOUBLE = u"MinExclusiveDouble"; +inline constexpr OUStringLiteral PROPERTY_XSD_MAX_INCLUSIVE_DATE = u"MaxInclusiveDate"; +inline constexpr OUStringLiteral PROPERTY_XSD_MAX_EXCLUSIVE_DATE = u"MaxExclusiveDate"; +inline constexpr OUStringLiteral PROPERTY_XSD_MIN_INCLUSIVE_DATE = u"MinInclusiveDate"; +inline constexpr OUStringLiteral PROPERTY_XSD_MIN_EXCLUSIVE_DATE = u"MinExclusiveDate"; +inline constexpr OUStringLiteral PROPERTY_XSD_MAX_INCLUSIVE_TIME = u"MaxInclusiveTime"; +inline constexpr OUStringLiteral PROPERTY_XSD_MAX_EXCLUSIVE_TIME = u"MaxExclusiveTime"; +inline constexpr OUStringLiteral PROPERTY_XSD_MIN_INCLUSIVE_TIME = u"MinInclusiveTime"; +inline constexpr OUStringLiteral PROPERTY_XSD_MIN_EXCLUSIVE_TIME = u"MinExclusiveTime"; +inline constexpr OUStringLiteral PROPERTY_XSD_MAX_INCLUSIVE_DATE_TIME = u"MaxInclusiveDateTime"; +inline constexpr OUStringLiteral PROPERTY_XSD_MAX_EXCLUSIVE_DATE_TIME = u"MaxExclusiveDateTime"; +inline constexpr OUStringLiteral PROPERTY_XSD_MIN_INCLUSIVE_DATE_TIME = u"MinInclusiveDateTime"; +inline constexpr OUStringLiteral PROPERTY_XSD_MIN_EXCLUSIVE_DATE_TIME = u"MinExclusiveDateTime"; +inline constexpr OUStringLiteral PROPERTY_SUBMISSION_ID = u"SubmissionID"; +inline constexpr OUStringLiteral PROPERTY_BINDING_ID = u"BindingID"; +inline constexpr OUStringLiteral PROPERTY_WRITING_MODE = u"WritingMode"; +inline constexpr OUStringLiteral PROPERTY_TEXT_ANCHOR_TYPE = u"TextAnchorType"; +inline constexpr OUStringLiteral PROPERTY_SHEET_ANCHOR_TYPE = u"SheetAnchorType"; +inline constexpr OUStringLiteral PROPERTY_ANCHOR_TYPE = u"AnchorType"; +inline constexpr OUStringLiteral PROPERTY_ANCHOR = u"Anchor"; +inline constexpr OUStringLiteral PROPERTY_IS_VISIBLE = u"IsVisible"; + +inline constexpr OUStringLiteral PROPERTY_MODEL = u"Model"; + +inline constexpr OUStringLiteral PROPERTY_CELL_EXCHANGE_TYPE = u"ExchangeSelectionIndex"; +inline constexpr OUStringLiteral PROPERTY_BOUND_CELL = u"BoundCell"; +inline constexpr OUStringLiteral PROPERTY_LIST_CELL_RANGE = u"CellRange"; +inline constexpr OUStringLiteral PROPERTY_TEXTTYPE = u"TextType"; +inline constexpr OUStringLiteral PROPERTY_RICHTEXT = u"RichText"; +inline constexpr OUStringLiteral PROPERTY_ROWSET = u"RowSet"; +inline constexpr OUStringLiteral PROPERTY_SELECTIONMODEL = u"SelectionModel"; +inline constexpr OUStringLiteral PROPERTY_USEGRIDLINE = u"UseGridLines"; +inline constexpr OUStringLiteral PROPERTY_GRIDLINECOLOR = u"GridLineColor"; +inline constexpr OUStringLiteral PROPERTY_SHOWCOLUMNHEADER = u"ShowColumnHeader"; +inline constexpr OUStringLiteral PROPERTY_SHOWROWHEADER = u"ShowRowHeader"; +inline constexpr OUStringLiteral PROPERTY_HEADERBACKGROUNDCOLOR = u"HeaderBackgroundColor"; +inline constexpr OUStringLiteral PROPERTY_HEADERTEXTCOLOR = u"HeaderTextColor"; +inline constexpr OUStringLiteral PROPERTY_ACTIVESELECTIONBACKGROUNDCOLOR = u"ActiveSelectionBackgroundColor"; +inline constexpr OUStringLiteral PROPERTY_ACTIVESELECTIONTEXTCOLOR = u"ActiveSelectionTextColor"; +inline constexpr OUStringLiteral PROPERTY_INACTIVESELECTIONBACKGROUNDCOLOR = u"InactiveSelectionBackgroundColor"; +inline constexpr OUStringLiteral PROPERTY_INACTIVESELECTIONTEXTCOLOR = u"InactiveSelectionTextColor"; + +// services +inline constexpr OUStringLiteral SERVICE_COMPONENT_GROUPBOX = u"com.sun.star.form.component.GroupBox"; +inline constexpr OUStringLiteral SERVICE_COMPONENT_FIXEDTEXT = u"com.sun.star.form.component.FixedText"; +inline constexpr OUStringLiteral SERVICE_COMPONENT_FORMATTEDFIELD = u"com.sun.star.form.component.FormattedField"; + +inline constexpr OUStringLiteral SERVICE_TEXT_DOCUMENT = u"com.sun.star.text.TextDocument"; +inline constexpr OUStringLiteral SERVICE_WEB_DOCUMENT = u"com.sun.star.text.WebDocument"; +inline constexpr OUStringLiteral SERVICE_SPREADSHEET_DOCUMENT = u"com.sun.star.sheet.SpreadsheetDocument"; +inline constexpr OUStringLiteral SERVICE_DRAWING_DOCUMENT = u"com.sun.star.drawing.DrawingDocument"; +inline constexpr OUStringLiteral SERVICE_PRESENTATION_DOCUMENT = u"com.sun.star.presentation.PresentationDocument"; + +inline constexpr OUStringLiteral SERVICE_SHEET_CELL_BINDING = u"com.sun.star.table.CellValueBinding"; +inline constexpr OUStringLiteral SERVICE_SHEET_CELL_INT_BINDING = u"com.sun.star.table.ListPositionCellBinding"; +inline constexpr OUStringLiteral SERVICE_SHEET_CELLRANGE_LISTSOURCE = u"com.sun.star.table.CellRangeListSource"; +inline constexpr OUStringLiteral SERVICE_ADDRESS_CONVERSION = u"com.sun.star.table.CellAddressConversion"; +inline constexpr OUStringLiteral SERVICE_RANGEADDRESS_CONVERSION = u"com.sun.star.table.CellRangeAddressConversion"; + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/genericpropertyhandler.cxx b/extensions/source/propctrlr/genericpropertyhandler.cxx new file mode 100644 index 000000000..8320e5154 --- /dev/null +++ b/extensions/source/propctrlr/genericpropertyhandler.cxx @@ -0,0 +1,619 @@ +/* -*- 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 "enumrepresentation.hxx" +#include "genericpropertyhandler.hxx" +#include "handlerhelper.hxx" + +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/lang/NullPointerException.hpp> +#include <com/sun/star/reflection/XEnumTypeDescription.hpp> +#include <com/sun/star/beans/theIntrospection.hpp> +#include <com/sun/star/inspection/PropertyControlType.hpp> +#include <com/sun/star/inspection/XHyperlinkControl.hpp> +#include <com/sun/star/awt/XActionListener.hpp> +#include <com/sun/star/script/Converter.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/frame/Desktop.hpp> + +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/extract.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/types.hxx> +#include <o3tl/safeint.hxx> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> + +#include <algorithm> + +namespace pcr +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::script; + using namespace ::com::sun::star::frame; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::reflection; + using namespace ::com::sun::star::inspection; + using ::com::sun::star::awt::XActionListener; + using ::com::sun::star::awt::ActionEvent; + + namespace { + + class EnumRepresentation : public IPropertyEnumRepresentation + { + private: + Reference< XEnumTypeDescription > m_xTypeDescription; + Type m_aEnumType; + + public: + EnumRepresentation( const Reference< XComponentContext >& _rxContext, const Type& _rEnumType ); + EnumRepresentation(const EnumRepresentation&) = delete; + EnumRepresentation& operator=(const EnumRepresentation&) = delete; + + // IPropertyEnumRepresentation implementqation + virtual std::vector< OUString > + getDescriptions() const override; + virtual void getValueFromDescription( const OUString& _rDescription, css::uno::Any& _out_rValue ) const override; + virtual OUString getDescriptionForValue( const css::uno::Any& _rEnumValue ) const override; + + private: + void impl_getValues( Sequence< sal_Int32 >& _out_rValues ) const; + }; + + } + + EnumRepresentation::EnumRepresentation( const Reference< XComponentContext >& _rxContext, const Type& _rEnumType ) + :m_aEnumType( _rEnumType ) + { + try + { + if ( _rxContext.is() ) + { + Reference< XHierarchicalNameAccess > xTypeDescProv( + _rxContext->getValueByName("/singletons/com.sun.star.reflection.theTypeDescriptionManager"), + UNO_QUERY_THROW ); + + m_xTypeDescription.set( xTypeDescProv->getByHierarchicalName( m_aEnumType.getTypeName() ), UNO_QUERY_THROW ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EnumRepresentation::EnumRepresentation" ); + } + } + + std::vector< OUString > EnumRepresentation::getDescriptions() const + { + Sequence< OUString > aNames; + try + { + if ( m_xTypeDescription.is() ) + aNames = m_xTypeDescription->getEnumNames(); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EnumRepresentation::getDescriptions" ); + } + + return std::vector< OUString >( std::cbegin(aNames), std::cend(aNames) ); + } + + void EnumRepresentation::impl_getValues( Sequence< sal_Int32 >& _out_rValues ) const + { + _out_rValues.realloc( 0 ); + try + { + if ( m_xTypeDescription.is() ) + _out_rValues = m_xTypeDescription->getEnumValues(); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EnumRepresentation::impl_getValues" ); + } + } + + void EnumRepresentation::getValueFromDescription( const OUString& _rDescription, Any& _out_rValue ) const + { + std::vector< OUString > aDescriptions( getDescriptions() ); + + sal_Int32 index = std::find( aDescriptions.begin(), aDescriptions.end(), + _rDescription ) - aDescriptions.begin(); + + Sequence< sal_Int32 > aValues; + impl_getValues( aValues ); + + if ( ( index >= 0 ) && ( index < aValues.getLength() ) ) + _out_rValue = ::cppu::int2enum( aValues[ index ], m_aEnumType ); + else + { + OSL_FAIL( "EnumRepresentation::getValueFromDescription: cannot convert!" ); + _out_rValue.clear(); + } + } + + OUString EnumRepresentation::getDescriptionForValue( const Any& _rEnumValue ) const + { + OUString sDescription; + + sal_Int32 nAsInt = 0; + OSL_VERIFY( ::cppu::enum2int( nAsInt, _rEnumValue ) ); + + Sequence< sal_Int32 > aValues; + impl_getValues( aValues ); + + sal_Int32 index = std::find( std::cbegin(aValues), std::cend(aValues), nAsInt ) - std::cbegin(aValues); + + std::vector< OUString > aDescriptions( getDescriptions() ); + if ( ( index >= 0 ) && ( o3tl::make_unsigned(index) < aDescriptions.size() ) ) + sDescription = aDescriptions[ index ]; + else + { + OSL_FAIL( "EnumRepresentation::getDescriptionForValue: cannot convert!" ); + } + return sDescription; + } + + typedef ::cppu::WeakImplHelper < XActionListener + > UrlClickHandler_Base; + + namespace { + + class UrlClickHandler : public UrlClickHandler_Base + { + Reference<XComponentContext> m_xContext; + public: + UrlClickHandler( const Reference<XComponentContext>& _rContext, const Reference< XHyperlinkControl >& _rxControl ); + + protected: + virtual ~UrlClickHandler() override; + + // XActionListener + virtual void SAL_CALL actionPerformed( const ActionEvent& rEvent ) override; + + // XEventListener + virtual void SAL_CALL disposing( const EventObject& Source ) override; + + protected: + void impl_dispatch_throw( const OUString& _rURL ); + }; + + } + + UrlClickHandler::UrlClickHandler( const Reference<XComponentContext>& _rContext, const Reference< XHyperlinkControl >& _rxControl ) + :m_xContext( _rContext ) + { + if ( !_rxControl.is() ) + throw NullPointerException(); + + osl_atomic_increment( &m_refCount ); + { + _rxControl->addActionListener( this ); + } + osl_atomic_decrement( &m_refCount ); + OSL_ENSURE( m_refCount > 0, "UrlClickHandler::UrlClickHandler: leaking!" ); + + } + + UrlClickHandler::~UrlClickHandler() + { + } + + void SAL_CALL UrlClickHandler::actionPerformed( const ActionEvent& rEvent ) + { + Reference< XPropertyControl > xControl( rEvent.Source, UNO_QUERY_THROW ); + Any aControlValue( xControl->getValue() ); + + OUString sURL; + if ( aControlValue.hasValue() && !( aControlValue >>= sURL ) ) + throw RuntimeException( OUString(), *this ); + + if ( sURL.isEmpty() ) + return; + + impl_dispatch_throw( sURL ); + } + + void SAL_CALL UrlClickHandler::disposing( const EventObject& /*Source*/ ) + { + // not interested in + } + + void UrlClickHandler::impl_dispatch_throw( const OUString& _rURL ) + { + Reference< XURLTransformer > xTransformer( URLTransformer::create(m_xContext) ); + URL aURL; aURL.Complete = ".uno:OpenHyperlink"; + xTransformer->parseStrict( aURL ); + + Reference< XDesktop2 > xDispProv = Desktop::create( m_xContext ); + Reference< XDispatch > xDispatch( xDispProv->queryDispatch( aURL, OUString(), 0 ), UNO_SET_THROW ); + + Sequence aDispatchArgs{ comphelper::makePropertyValue("URL", _rURL) }; + + xDispatch->dispatch( aURL, aDispatchArgs ); + } + + + GenericPropertyHandler::GenericPropertyHandler( const Reference< XComponentContext >& _rxContext ) + :GenericPropertyHandler_Base( m_aMutex ) + ,m_xContext( _rxContext ) + ,m_aPropertyListeners( m_aMutex ) + ,m_bPropertyMapInitialized( false ) + { + m_xTypeConverter = Converter::create(_rxContext); + } + + GenericPropertyHandler::~GenericPropertyHandler() + { + } + + OUString SAL_CALL GenericPropertyHandler::getImplementationName( ) + { + return "com.sun.star.comp.extensions.GenericPropertyHandler"; + } + + sal_Bool SAL_CALL GenericPropertyHandler::supportsService( const OUString& ServiceName ) + { + return cppu::supportsService(this, ServiceName); + } + + Sequence< OUString > SAL_CALL GenericPropertyHandler::getSupportedServiceNames( ) + { + return { "com.sun.star.inspection.GenericPropertyHandler" }; + } + + void SAL_CALL GenericPropertyHandler::inspect( const Reference< XInterface >& _rxIntrospectee ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( !_rxIntrospectee.is() ) + throw NullPointerException(); + + // revoke old property change listeners + ::comphelper::OInterfaceIteratorHelper2 iterRemove( m_aPropertyListeners ); + ::comphelper::OInterfaceIteratorHelper2 iterReAdd( m_aPropertyListeners ); // this holds a copy of the container ... + while ( iterRemove.hasMoreElements() ) + m_xComponent->removePropertyChangeListener( OUString(), static_cast< XPropertyChangeListener* >( iterRemove.next() ) ); + + m_xComponentIntrospectionAccess.clear(); + m_xComponent.clear(); + m_xPropertyState.clear(); + + // create an introspection adapter for the component + Reference< XIntrospection > xIntrospection = theIntrospection::get( m_xContext ); + + Reference< XIntrospectionAccess > xIntrospectionAccess( xIntrospection->inspect( Any( _rxIntrospectee ) ) ); + if ( !xIntrospectionAccess.is() ) + throw RuntimeException("The introspection service could not handle the given component.", *this ); + + m_xComponent.set( xIntrospectionAccess->queryAdapter( cppu::UnoType<XPropertySet>::get() ), UNO_QUERY_THROW ); + // now that we survived so far, remember m_xComponentIntrospectionAccess + m_xComponentIntrospectionAccess = xIntrospectionAccess; + m_xPropertyState.set(m_xComponent, css::uno::UNO_QUERY); + + m_bPropertyMapInitialized = false; + m_aProperties.clear(); + + // re-add the property change listeners + while ( iterReAdd.hasMoreElements() ) + m_xComponent->addPropertyChangeListener( OUString(), static_cast< XPropertyChangeListener* >( iterReAdd.next() ) ); + } + + Any SAL_CALL GenericPropertyHandler::getPropertyValue( const OUString& _rPropertyName ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !m_xComponent.is() ) + throw UnknownPropertyException(_rPropertyName); + + return m_xComponent->getPropertyValue( _rPropertyName ); + } + + void SAL_CALL GenericPropertyHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !m_xComponent.is() ) + throw UnknownPropertyException(_rPropertyName); + + m_xComponent->setPropertyValue( _rPropertyName, _rValue ); + } + + ::rtl::Reference< IPropertyEnumRepresentation > GenericPropertyHandler::impl_getEnumConverter( const Type& _rEnumType ) + { + ::rtl::Reference< IPropertyEnumRepresentation >& rConverter = m_aEnumConverters[ _rEnumType ]; + if ( !rConverter.is() ) + rConverter = new EnumRepresentation( m_xContext, _rEnumType ); + return rConverter; + } + + Any SAL_CALL GenericPropertyHandler::convertToPropertyValue( const OUString& _rPropertyName, const Any& _rControlValue ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + impl_ensurePropertyMap(); + + PropertyMap::const_iterator pos = m_aProperties.find( _rPropertyName ); + if ( pos == m_aProperties.end() ) + throw UnknownPropertyException(_rPropertyName); + + Any aPropertyValue; + if ( !_rControlValue.hasValue() ) + // NULL is converted to NULL + return aPropertyValue; + + if ( pos->second.Type.getTypeClass() == TypeClass_ENUM ) + { + OUString sControlValue; + OSL_VERIFY( _rControlValue >>= sControlValue ); + impl_getEnumConverter( pos->second.Type )->getValueFromDescription( sControlValue, aPropertyValue ); + } + else + aPropertyValue = PropertyHandlerHelper::convertToPropertyValue( m_xContext, m_xTypeConverter, pos->second, _rControlValue ); + + return aPropertyValue; + } + + Any SAL_CALL GenericPropertyHandler::convertToControlValue( const OUString& _rPropertyName, const Any& _rPropertyValue, const Type& _rControlValueType ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + impl_ensurePropertyMap(); + + PropertyMap::const_iterator pos = m_aProperties.find( _rPropertyName ); + if ( pos == m_aProperties.end() ) + throw UnknownPropertyException(_rPropertyName); + + Any aControlValue; + if ( !_rPropertyValue.hasValue() ) + // NULL is converted to NULL + return aControlValue; + + if ( pos->second.Type.getTypeClass() == TypeClass_ENUM ) + { + aControlValue <<= impl_getEnumConverter( pos->second.Type )->getDescriptionForValue( _rPropertyValue ); + } + else + aControlValue = PropertyHandlerHelper::convertToControlValue( m_xContext, m_xTypeConverter, _rPropertyValue, _rControlValueType ); + return aControlValue; + } + + PropertyState SAL_CALL GenericPropertyHandler::getPropertyState( const OUString& _rPropertyName ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyState eState = PropertyState_DIRECT_VALUE; + if ( m_xPropertyState.is() ) + eState = m_xPropertyState->getPropertyState( _rPropertyName ); + return eState; + } + + void SAL_CALL GenericPropertyHandler::addPropertyChangeListener(const Reference< XPropertyChangeListener >& _rxListener) + { + if ( !_rxListener.is() ) + throw NullPointerException(); + + ::osl::MutexGuard aGuard( m_aMutex ); + m_aPropertyListeners.addInterface( _rxListener ); + if ( m_xComponent.is() ) + { + try + { + m_xComponent->addPropertyChangeListener( OUString(), _rxListener ); + } + catch( const UnknownPropertyException& ) + { + OSL_FAIL( "GenericPropertyHandler::addPropertyChangeListener:\nThe inspected component does not allow registering for all properties at once! This violates the interface contract!" ); + } + } + } + + void SAL_CALL GenericPropertyHandler::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_xComponent.is() ) + { + try + { + m_xComponent->removePropertyChangeListener( OUString(), _rxListener ); + } + catch( const UnknownPropertyException& ) + { + OSL_FAIL( "GenericPropertyHandler::removePropertyChangeListener:\nThe inspected component does not allow de-registering for all properties at once! This violates the interface contract!" ); + } + } + m_aPropertyListeners.removeInterface( _rxListener ); + } + + void GenericPropertyHandler::impl_ensurePropertyMap() + { + if ( m_bPropertyMapInitialized ) + return; + + m_bPropertyMapInitialized = true; + try + { + Reference< XPropertySetInfo > xPSI; + if ( m_xComponent.is() ) + xPSI = m_xComponent->getPropertySetInfo(); + Sequence< Property > aProperties; + if ( xPSI.is() ) + aProperties = xPSI->getProperties(); + DBG_ASSERT( aProperties.hasElements(), "GenericPropertyHandler::getSupportedProperties: no properties!" ); + + for ( auto const & property : std::as_const(aProperties) ) + { + switch ( property.Type.getTypeClass() ) + { + case TypeClass_BOOLEAN: + case TypeClass_BYTE: + case TypeClass_SHORT: + case TypeClass_UNSIGNED_SHORT: + case TypeClass_LONG: + case TypeClass_UNSIGNED_LONG: + case TypeClass_HYPER: + case TypeClass_UNSIGNED_HYPER: + case TypeClass_FLOAT: + case TypeClass_DOUBLE: + case TypeClass_ENUM: + case TypeClass_STRING: + // allowed, we can handle this type + break; + + case TypeClass_SEQUENCE: + { + TypeClass eElementTypeClass = ::comphelper::getSequenceElementType( property.Type ).getTypeClass(); + if ( ( eElementTypeClass != TypeClass_STRING ) + && ( eElementTypeClass != TypeClass_BYTE ) + && ( eElementTypeClass != TypeClass_SHORT ) + && ( eElementTypeClass != TypeClass_UNSIGNED_SHORT ) + && ( eElementTypeClass != TypeClass_LONG ) + && ( eElementTypeClass != TypeClass_UNSIGNED_LONG ) + ) + // can only handle the above + continue; + } + break; + + default: + // next property, we don't support this type + continue; + } + + m_aProperties.emplace( property.Name, property ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "GenericPropertyHandler::impl_ensurePropertyMap" ); + } + } + + Sequence< Property > SAL_CALL GenericPropertyHandler::getSupportedProperties() + { + ::osl::MutexGuard aGuard( m_aMutex ); + impl_ensurePropertyMap(); + + return comphelper::mapValuesToSequence( m_aProperties ); + } + + Sequence< OUString > SAL_CALL GenericPropertyHandler::getSupersededProperties( ) + { + // no superseded properties at all. This handler offers the very basic PropertyHandler + // functionality, so it's much more likely that other handlers want to supersede + // *our* properties... + return Sequence< OUString >( ); + } + + Sequence< OUString > SAL_CALL GenericPropertyHandler::getActuatingProperties( ) + { + // This basic PropertyHandler implementation is too dumb^Wgeneric to know + // anything about property dependencies + return Sequence< OUString >( ); + } + + LineDescriptor SAL_CALL GenericPropertyHandler::describePropertyLine( const OUString& _rPropertyName, + const Reference< XPropertyControlFactory >& _rxControlFactory ) + { + if ( !_rxControlFactory.is() ) + throw NullPointerException(); + + ::osl::MutexGuard aGuard( m_aMutex ); + impl_ensurePropertyMap(); + + PropertyMap::const_iterator pos = m_aProperties.find( _rPropertyName ); + if ( pos == m_aProperties.end() ) + throw UnknownPropertyException(_rPropertyName); + + LineDescriptor aDescriptor; + aDescriptor.DisplayName = _rPropertyName; + switch ( pos->second.Type.getTypeClass() ) + { + case TypeClass_ENUM: + aDescriptor.Control = PropertyHandlerHelper::createListBoxControl( _rxControlFactory, + impl_getEnumConverter( pos->second.Type )->getDescriptions(), + PropertyHandlerHelper::requiresReadOnlyControl( pos->second.Attributes ), + false ); + break; + case TypeClass_STRING: + { + // some special handling for URL properties + bool bIsURLProperty = _rPropertyName.endsWith( "URL" ); + if ( bIsURLProperty ) + { + aDescriptor.Control = _rxControlFactory->createPropertyControl( + PropertyControlType::HyperlinkField, PropertyHandlerHelper::requiresReadOnlyControl( pos->second.Attributes ) ); + + Reference< XHyperlinkControl > xControl( aDescriptor.Control, UNO_QUERY_THROW ); + new UrlClickHandler( m_xContext, xControl ); + } + } + break; + default: + break; + } + // fallback + if ( !aDescriptor.Control.is() ) + PropertyHandlerHelper::describePropertyLine( pos->second, aDescriptor, _rxControlFactory ); + + aDescriptor.Category = "General"; + return aDescriptor; + } + + sal_Bool SAL_CALL GenericPropertyHandler::isComposable( const OUString& /*_rPropertyName*/ ) + { + return false; + } + + InteractiveSelectionResult SAL_CALL GenericPropertyHandler::onInteractivePropertySelection( const OUString& /*_rPropertyName*/, sal_Bool /*_bPrimary*/, Any& /*_rData*/, const Reference< XObjectInspectorUI >& /*_rxInspectorUI*/ ) + { + OSL_FAIL( "GenericPropertyHandler::onInteractivePropertySelection: I'm too dumb to know anything about property browse buttons!" ); + return InteractiveSelectionResult_Cancelled; + } + + void SAL_CALL GenericPropertyHandler::actuatingPropertyChanged( const OUString& /*_rActuatingPropertyName*/, const Any& /*_rNewValue*/, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& /*_rxInspectorUI*/, sal_Bool /*_bFirstTimeInit*/ ) + { + OSL_FAIL( "GenericPropertyHandler::actuatingPropertyChanged: no no no, I did not register for any actuating properties!" ); + } + + sal_Bool SAL_CALL GenericPropertyHandler::suspend( sal_Bool /*_bSuspend*/ ) + { + return true; + } + + void SAL_CALL GenericPropertyHandler::disposing() + { + m_aPropertyListeners.clear(); + // not disposeAndClear: the listeners are (virtually) listeners at our introspectee, not + // at this handler instance + } + + IMPLEMENT_FORWARD_XCOMPONENT( GenericPropertyHandler, GenericPropertyHandler_Base ); + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_GenericPropertyHandler_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::GenericPropertyHandler(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/genericpropertyhandler.hxx b/extensions/source/propctrlr/genericpropertyhandler.hxx new file mode 100644 index 000000000..b2114be3c --- /dev/null +++ b/extensions/source/propctrlr/genericpropertyhandler.hxx @@ -0,0 +1,139 @@ +/* -*- 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 "pcrcommontypes.hxx" +#include "pcrcommon.hxx" + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertyState.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/beans/XIntrospectionAccess.hpp> +#include <com/sun/star/inspection/XPropertyHandler.hpp> +#include <com/sun/star/script/XTypeConverter.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <comphelper/interfacecontainer2.hxx> +#include <cppuhelper/compbase.hxx> +#include <rtl/ref.hxx> + +#include <map> + + +namespace pcr +{ + + + struct TypeLess + { + bool operator()( const css::uno::Type& _rLHS, const css::uno::Type& _rRHS ) const + { + return _rLHS.getTypeName() < _rRHS.getTypeName(); + } + }; + + class IPropertyEnumRepresentation; + + //= GenericPropertyHandler + + typedef ::cppu::WeakComponentImplHelper < css::inspection::XPropertyHandler + , css::lang::XServiceInfo + > GenericPropertyHandler_Base; + class GenericPropertyHandler final : public GenericPropertyHandler_Base + { + mutable ::osl::Mutex m_aMutex; + + /// the service factory for creating services + css::uno::Reference< css::uno::XComponentContext > m_xContext; + /// need this to keep alive as long as m_xComponent lives + css::uno::Reference< css::beans::XIntrospectionAccess > m_xComponentIntrospectionAccess; + /// the properties of the object we're handling + css::uno::Reference< css::beans::XPropertySet > m_xComponent; + /// cached interface of ->m_xComponent + css::uno::Reference< css::beans::XPropertyState > m_xPropertyState; + /// type converter, needed on various occasions + css::uno::Reference< css::script::XTypeConverter > m_xTypeConverter; + /// cache of our supported properties + PropertyMap m_aProperties; + /// property change listeners + ::comphelper::OInterfaceContainerHelper2 m_aPropertyListeners; + std::map< css::uno::Type, ::rtl::Reference< IPropertyEnumRepresentation >, TypeLess > + m_aEnumConverters; + + /// has ->m_aProperties been initialized? + bool m_bPropertyMapInitialized : 1; + + public: + explicit GenericPropertyHandler( + const css::uno::Reference< css::uno::XComponentContext >& _rxContext + ); + + virtual ~GenericPropertyHandler() override; + + private: + // XPropertyHandler overridables + virtual void SAL_CALL inspect( const css::uno::Reference< css::uno::XInterface >& _rxIntrospectee ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override; + virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override; + virtual css::uno::Any SAL_CALL convertToPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rControlValue ) override; + virtual css::uno::Any SAL_CALL convertToControlValue( const OUString& _rPropertyName, const css::uno::Any& _rPropertyValue, const css::uno::Type& _rControlValueType ) override; + virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& _rPropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override; + virtual css::uno::Sequence< css::beans::Property > + SAL_CALL getSupportedProperties() override; + virtual css::uno::Sequence< OUString > + SAL_CALL getSupersededProperties() override; + virtual css::uno::Sequence< OUString > SAL_CALL getActuatingProperties() override; + virtual css::inspection::LineDescriptor SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override; + virtual sal_Bool SAL_CALL isComposable( const OUString& _rPropertyName ) override; + virtual css::inspection::InteractiveSelectionResult + SAL_CALL onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, css::uno::Any& _rData, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) override; + virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) override; + virtual sal_Bool SAL_CALL suspend( sal_Bool _bSuspend ) override; + + // XComponent + DECLARE_XCOMPONENT() + virtual void SAL_CALL disposing() 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; + + /** ensures that ->m_aProperties is initialized + @precond + our mutex is locked + */ + void impl_ensurePropertyMap(); + + /** retrieves the enum converter for the given ENUM type + */ + ::rtl::Reference< IPropertyEnumRepresentation > + impl_getEnumConverter( const css::uno::Type& _rEnumType ); + + GenericPropertyHandler( const GenericPropertyHandler& ) = delete; + GenericPropertyHandler& operator=( const GenericPropertyHandler& ) = delete; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/handlerhelper.cxx b/extensions/source/propctrlr/handlerhelper.cxx new file mode 100644 index 000000000..89f149b92 --- /dev/null +++ b/extensions/source/propctrlr/handlerhelper.cxx @@ -0,0 +1,314 @@ +/* -*- 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 "handlerhelper.hxx" +#include <yesno.hrc> +#include "modulepcr.hxx" + +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/inspection/StringRepresentation.hpp> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/inspection/LineDescriptor.hpp> +#include <com/sun/star/inspection/PropertyControlType.hpp> +#include <com/sun/star/inspection/XStringListControl.hpp> +#include <com/sun/star/inspection/XNumericControl.hpp> +#include <tools/diagnose_ex.h> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <vcl/weldutils.hxx> + +#include <algorithm> + + +namespace pcr +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::script; + using namespace ::com::sun::star::inspection; + + + //= PropertyHandlerHelper + + + void PropertyHandlerHelper::describePropertyLine( const Property& _rProperty, + LineDescriptor& /* [out] */ _out_rDescriptor, const Reference< XPropertyControlFactory >& _rxControlFactory ) + { + // display the pure property name - no L10N + _out_rDescriptor.DisplayName = _rProperty.Name; + + OSL_PRECOND( _rxControlFactory.is(), "PropertyHandlerHelper::describePropertyLine: no factory -> no control!" ); + if ( !_rxControlFactory.is() ) + return; + + bool bReadOnlyControl = requiresReadOnlyControl( _rProperty.Attributes ); + + // special handling for booleans (this will become a list) + if ( _rProperty.Type.getTypeClass() == TypeClass_BOOLEAN ) + { + _out_rDescriptor.Control = createListBoxControl(_rxControlFactory, RID_RSC_ENUM_YESNO, SAL_N_ELEMENTS(RID_RSC_ENUM_YESNO), bReadOnlyControl); + return; + } + + sal_Int16 nControlType = PropertyControlType::TextField; + switch ( _rProperty.Type.getTypeClass() ) + { + case TypeClass_BYTE: + case TypeClass_SHORT: + case TypeClass_UNSIGNED_SHORT: + case TypeClass_LONG: + case TypeClass_UNSIGNED_LONG: + case TypeClass_HYPER: + case TypeClass_UNSIGNED_HYPER: + case TypeClass_FLOAT: + case TypeClass_DOUBLE: + nControlType = PropertyControlType::NumericField; + break; + + case TypeClass_SEQUENCE: + nControlType = PropertyControlType::StringListField; + break; + + default: + OSL_FAIL( "PropertyHandlerHelper::describePropertyLine: don't know how to represent this at the UI!" ); + [[fallthrough]]; + + case TypeClass_STRING: + nControlType = PropertyControlType::TextField; + break; + } + + // create a control + _out_rDescriptor.Control = _rxControlFactory->createPropertyControl( nControlType, bReadOnlyControl ); + } + + + namespace + { + Reference< XPropertyControl > lcl_implCreateListLikeControl( + const Reference< XPropertyControlFactory >& _rxControlFactory, + std::vector< OUString >&& _rInitialListEntries, + bool _bReadOnlyControl, + bool _bSorted, + bool _bTrueIfListBoxFalseIfComboBox + ) + { + Reference< XStringListControl > xListControl( + _rxControlFactory->createPropertyControl( + _bTrueIfListBoxFalseIfComboBox ? PropertyControlType::ListBox : PropertyControlType::ComboBox, _bReadOnlyControl + ), + UNO_QUERY_THROW + ); + + if ( _bSorted ) + std::sort( _rInitialListEntries.begin(), _rInitialListEntries.end() ); + + for (auto const& initialEntry : _rInitialListEntries) + xListControl->appendListEntry(initialEntry); + return xListControl; + } + } + + Reference< XPropertyControl > PropertyHandlerHelper::createListBoxControl( const Reference< XPropertyControlFactory >& _rxControlFactory, + std::vector< OUString >&& _rInitialListEntries, bool _bReadOnlyControl, bool _bSorted ) + { + return lcl_implCreateListLikeControl(_rxControlFactory, std::move(_rInitialListEntries), _bReadOnlyControl, _bSorted, true); + } + + Reference< XPropertyControl > PropertyHandlerHelper::createListBoxControl( const Reference< XPropertyControlFactory >& _rxControlFactory, + const TranslateId* pTransIds, size_t nElements, bool _bReadOnlyControl ) + { + std::vector<OUString> aInitialListEntries; + for (size_t i = 0; i < nElements; ++i) + aInitialListEntries.push_back(PcrRes(pTransIds[i])); + return lcl_implCreateListLikeControl(_rxControlFactory, std::move(aInitialListEntries), _bReadOnlyControl, /*_bSorted*/false, true); + } + + Reference< XPropertyControl > PropertyHandlerHelper::createComboBoxControl( const Reference< XPropertyControlFactory >& _rxControlFactory, + std::vector< OUString >&& _rInitialListEntries, bool _bSorted ) + { + return lcl_implCreateListLikeControl( _rxControlFactory, std::move(_rInitialListEntries), /*_bReadOnlyControl*/false, _bSorted, false ); + } + + + Reference< XPropertyControl > PropertyHandlerHelper::createNumericControl( const Reference< XPropertyControlFactory >& _rxControlFactory, + sal_Int16 _nDigits, const Optional< double >& _rMinValue, const Optional< double >& _rMaxValue ) + { + Reference< XNumericControl > xNumericControl( + _rxControlFactory->createPropertyControl( PropertyControlType::NumericField, /*_bReadOnlyControl*/false ), + UNO_QUERY_THROW + ); + + xNumericControl->setDecimalDigits( _nDigits ); + xNumericControl->setMinValue( _rMinValue ); + xNumericControl->setMaxValue( _rMaxValue ); + + return xNumericControl; + } + + + Any PropertyHandlerHelper::convertToPropertyValue( const Reference< XComponentContext >& _rxContext,const Reference< XTypeConverter >& _rxTypeConverter, + const Property& _rProperty, const Any& _rControlValue ) + { + Any aPropertyValue( _rControlValue ); + if ( !aPropertyValue.hasValue() ) + // NULL is converted to NULL + return aPropertyValue; + + if ( aPropertyValue.getValueType().equals( _rProperty.Type ) ) + // nothing to do, type is already as desired + return aPropertyValue; + + if ( _rControlValue.getValueType().getTypeClass() == TypeClass_STRING ) + { + OUString sControlValue; + _rControlValue >>= sControlValue; + + Reference< XStringRepresentation > xConversionHelper = StringRepresentation::create( _rxContext,_rxTypeConverter ); + aPropertyValue = xConversionHelper->convertToPropertyValue( sControlValue, _rProperty.Type ); + } + else + { + try + { + if ( _rxTypeConverter.is() ) + aPropertyValue = _rxTypeConverter->convertTo( _rControlValue, _rProperty.Type ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION("extensions.propctrlr", + "caught an exception while converting via TypeConverter!"); + } + } + + return aPropertyValue; + } + + + Any PropertyHandlerHelper::convertToControlValue( const Reference< XComponentContext >& _rxContext,const Reference< XTypeConverter >& _rxTypeConverter, + const Any& _rPropertyValue, const Type& _rControlValueType ) + { + Any aControlValue( _rPropertyValue ); + if ( !aControlValue.hasValue() ) + // NULL is converted to NULL + return aControlValue; + + if ( _rControlValueType.getTypeClass() == TypeClass_STRING ) + { + Reference< XStringRepresentation > xConversionHelper = StringRepresentation::create( _rxContext,_rxTypeConverter ); + aControlValue <<= xConversionHelper->convertToControlValue( _rPropertyValue ); + } + else + { + try + { + if ( _rxTypeConverter.is() ) + aControlValue = _rxTypeConverter->convertTo( _rPropertyValue, _rControlValueType ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION("extensions.propctrlr", + "caught an exception while converting via TypeConverter!"); + } + } + + return aControlValue; + } + + + void PropertyHandlerHelper::setContextDocumentModified( const Reference<XComponentContext> & rContext ) + { + try + { + Reference< XModifiable > xDocumentModifiable( getContextDocument_throw(rContext), UNO_QUERY_THROW ); + xDocumentModifiable->setModified( true ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + Reference< XInterface > PropertyHandlerHelper::getContextDocument( const Reference<XComponentContext> & rContext ) + { + Reference< XInterface > xI; + try + { + xI = getContextDocument_throw( rContext ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "PropertyHandler::getContextValueByName" ); + } + return xI; + } + + Reference< XInterface > PropertyHandlerHelper::getContextDocument_throw( const Reference<XComponentContext> & rContext ) + { + Reference< XInterface > xI; + Any aReturn = rContext->getValueByName( "ContextDocument" ); + aReturn >>= xI; + return xI; + } + + weld::Window* PropertyHandlerHelper::getDialogParentFrame(const Reference<XComponentContext>& rContext) + { + weld::Window* pInspectorWindow = nullptr; + try + { + Reference< XWindow > xInspectorWindow(rContext->getValueByName( "DialogParentWindow" ), UNO_QUERY_THROW); + pInspectorWindow = Application::GetFrameWeld(xInspectorWindow); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + return pInspectorWindow; + } + + std::unique_ptr<weld::Builder> PropertyHandlerHelper::makeBuilder(const OUString& rUIFile, const Reference<XComponentContext>& rContext) + { + Reference<XWindow> xWindow(rContext->getValueByName("BuilderParent"), UNO_QUERY_THROW); + weld::TransportAsXWindow& rTunnel = dynamic_cast<weld::TransportAsXWindow&>(*xWindow); + return Application::CreateBuilder(rTunnel.getWidget(), rUIFile); + } + + void PropertyHandlerHelper::setBuilderParent(const css::uno::Reference<css::uno::XComponentContext>& rContext, weld::Widget* pParent) + { + Reference<css::container::XNameContainer> xName(rContext, UNO_QUERY_THROW); + Reference<XWindow> xWindow(new weld::TransportAsXWindow(pParent)); + xName->insertByName("BuilderParent", Any(xWindow)); + } + + void PropertyHandlerHelper::clearBuilderParent(const css::uno::Reference<css::uno::XComponentContext>& rContext) + { + Reference<css::container::XNameContainer> xName(rContext, UNO_QUERY_THROW); + xName->removeByName("BuilderParent"); + } + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/handlerhelper.hxx b/extensions/source/propctrlr/handlerhelper.hxx new file mode 100644 index 000000000..555b8ee82 --- /dev/null +++ b/extensions/source/propctrlr/handlerhelper.hxx @@ -0,0 +1,231 @@ +/* -*- 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/Property.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/script/XTypeConverter.hpp> +#include <com/sun/star/inspection/XPropertyControlFactory.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/Optional.hpp> +#include <unotools/resmgr.hxx> + +#include <memory> +#include <vector> + +namespace weld { class Builder; class Widget; class Window; } +namespace com::sun::star { + namespace inspection { + struct LineDescriptor; + } +} + +namespace pcr +{ + + + //= PropertyHandlerHelper + + class PropertyHandlerHelper + { + public: + /** helper for implementing XPropertyHandler::describePropertyLine in a generic way + */ + static void describePropertyLine( + const css::beans::Property& _rProperty, + css::inspection::LineDescriptor& /* [out] */ _out_rDescriptor, + const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory + ); + + /** helper for implementing XPropertyHandler::convertToPropertyValue + */ + static css::uno::Any convertToPropertyValue( + const css::uno::Reference< css::uno::XComponentContext >& _rxContext, + const css::uno::Reference< css::script::XTypeConverter >& _rxTypeConverter, + const css::beans::Property& _rProperty, + const css::uno::Any& _rControlValue + ); + + /// helper for implementing XPropertyHandler::convertToControlValue + static css::uno::Any convertToControlValue( + const css::uno::Reference< css::uno::XComponentContext >& _rxContext, + const css::uno::Reference< css::script::XTypeConverter >& _rxTypeConverter, + const css::uno::Any& _rPropertyValue, + const css::uno::Type& _rControlValueType + ); + + /** creates an <member scope="css::inspection">PropertyControlType::ListBox</member>-type control + and fills it with initial values + + @param _rxControlFactory + A control factory. Must not be <NULL/>. + + @param _rInitialListEntries + the initial values of the control + + @param _bReadOnlyControl + determines whether the control should be read-only + + @param _bSorted + determines whether the list entries should be sorted + + @return + the newly created control + */ + static css::uno::Reference< css::inspection::XPropertyControl > + createListBoxControl( + const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory, + std::vector< OUString >&& _rInitialListEntries, + bool _bReadOnlyControl, + bool _bSorted + ); + + /** creates an <member scope="css::inspection">PropertyControlType::ListBox</member>-type control + and fills it with initial values. + + @param _rxControlFactory + A control factory. Must not be <NULL/>. + + @param pTransIds + the initial translation ids for the value of the control + + @param nElements + the count of initial values of the control + + @param _bReadOnlyControl + determines whether the control should be read-only + + @return + the newly created control + */ + static css::uno::Reference< css::inspection::XPropertyControl > + createListBoxControl( + const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory, + const TranslateId* pTransIds, size_t nElements, + bool _bReadOnlyControl + ); + + /** creates an <member scope="css::inspection">PropertyControlType::ComboBox</member>-type control + and fills it with initial values + + @param _rxControlFactory + A control factory. Must not be <NULL/>. + + @param _rInitialListEntries + the initial values of the control + + @param _bSorted + determines whether the list entries should be sorted + + @return + the newly created control + */ + static css::uno::Reference< css::inspection::XPropertyControl > + createComboBoxControl( + const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory, + std::vector< OUString >&& _rInitialListEntries, + bool _bSorted + ); + + /** creates an <member scope="css::inspection">PropertyControlType::NumericField</member>-type control + and initializes it + + @param _rxControlFactory + A control factory. Must not be <NULL/>. + @param _nDigits + number of decimal digits for the control + (<member scope="css::inspection">XNumericControl::DecimalDigits</member>) + @param _rMinValue + minimum value which can be entered in the control + (<member scope="css::inspection">XNumericControl::MinValue</member>) + @param _rMaxValue + maximum value which can be entered in the control + (<member scope="css::inspection">XNumericControl::MaxValue</member>) + + @return + the newly created control + */ + static css::uno::Reference< css::inspection::XPropertyControl > + createNumericControl( + const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory, + sal_Int16 _nDigits, + const css::beans::Optional< double >& _rMinValue, + const css::beans::Optional< double >& _rMaxValue + ); + + /** marks the document passed in our UNO context as modified + + The method looks up a value called "ContextDocument" in the given UNO component context, + queries it for the ->css::util::XModifiable interface, and calls its + setModified method. If either of those steps fails, this is asserted in a non-product + version, and silently ignore otherwise. + + @param _rContext + the component context which was used to create the component calling this method + */ + static void setContextDocumentModified( + const css::uno::Reference< css::uno::XComponentContext > & _rContext + ); + + static css::uno::Reference< css::uno::XInterface > getContextDocument( const css::uno::Reference<css::uno::XComponentContext> & _rContext ); + + /// @throws css::uno::RuntimeException + static css::uno::Reference< css::uno::XInterface > getContextDocument_throw( const css::uno::Reference<css::uno::XComponentContext> & _rContext ); + + /** gets the window of the ObjectInspector in which a property handler lives + + The method looks up a value called "DialogParentWindow" in the given UNO component context, + queries it for XWindow, and returns the respective weld::Window*. If either of those steps fails, + this is asserted in a non-product version, and silently ignore otherwise. + + @param _rContext + the component context which was used to create the component calling this method + */ + static weld::Window* getDialogParentFrame( const css::uno::Reference< css::uno::XComponentContext > & _rContext ); + + + /** determines whether given PropertyAttributes require a to-be-created + <type scope="css::inspection">XPropertyControl</type> to be read-only + + @param _nPropertyAttributes + the attributes of the property which should be reflected by a to-be-created + <type scope="css::inspection">XPropertyControl</type> + */ + static bool requiresReadOnlyControl( sal_Int16 _nPropertyAttributes ) + { + return ( _nPropertyAttributes & css::beans::PropertyAttribute::READONLY ) != 0; + } + + static std::unique_ptr<weld::Builder> makeBuilder(const OUString& rUIFile, const css::uno::Reference<css::uno::XComponentContext>& rContext); + + static void setBuilderParent(const css::uno::Reference<css::uno::XComponentContext>& rContext, weld::Widget* pParent); + + static void clearBuilderParent(const css::uno::Reference<css::uno::XComponentContext>& rContext); + + private: + PropertyHandlerHelper( const PropertyHandlerHelper& ) = delete; + PropertyHandlerHelper& operator=( const PropertyHandlerHelper& ) = delete; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/inspectorhelpwindow.cxx b/extensions/source/propctrlr/inspectorhelpwindow.cxx new file mode 100644 index 000000000..a57c4ffed --- /dev/null +++ b/extensions/source/propctrlr/inspectorhelpwindow.cxx @@ -0,0 +1,42 @@ +/* -*- 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 "inspectorhelpwindow.hxx" + + +namespace pcr +{ + //= InspectorHelpWindow + InspectorHelpWindow::InspectorHelpWindow(weld::Builder& rBuilder) + : m_xHelpFrame(rBuilder.weld_widget("helpframe")) + , m_xHelpText(rBuilder.weld_text_view("helptext")) + { + } + + InspectorHelpWindow::~InspectorHelpWindow() + { + } + + void InspectorHelpWindow::SetText(const OUString& rStr) + { + m_xHelpText->set_text(rStr); + } + +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/inspectorhelpwindow.hxx b/extensions/source/propctrlr/inspectorhelpwindow.hxx new file mode 100644 index 000000000..98b8efbab --- /dev/null +++ b/extensions/source/propctrlr/inspectorhelpwindow.hxx @@ -0,0 +1,44 @@ +/* -*- 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 <vcl/weld.hxx> + +namespace pcr +{ + //= InspectorHelpWindow + class InspectorHelpWindow + { + private: + std::unique_ptr<weld::Widget> m_xHelpFrame; + std::unique_ptr<weld::TextView> m_xHelpText; + + public: + explicit InspectorHelpWindow(weld::Builder& rBuilder); + ~InspectorHelpWindow(); + + void SetText(const OUString& rStr); + + void Show(bool bShow) { m_xHelpFrame->set_visible(bShow); } + bool IsVisible() const { return m_xHelpFrame->get_visible(); } + }; + +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/inspectormodelbase.cxx b/extensions/source/propctrlr/inspectormodelbase.cxx new file mode 100644 index 000000000..f8780ff99 --- /dev/null +++ b/extensions/source/propctrlr/inspectormodelbase.cxx @@ -0,0 +1,246 @@ +/* -*- 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 <memory> +#include "inspectormodelbase.hxx" + +#include <com/sun/star/beans/PropertyAttribute.hpp> + +#include <comphelper/propertycontainerhelper.hxx> +#include <cppuhelper/supportsservice.hxx> + + +namespace pcr +{ + + +#define MODEL_PROPERTY_ID_HAS_HELP_SECTION 2000 +#define MODEL_PROPERTY_ID_MIN_HELP_TEXT_LINES 2001 +#define MODEL_PROPERTY_ID_MAX_HELP_TEXT_LINES 2002 +#define MODEL_PROPERTY_ID_IS_READ_ONLY 2003 + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::beans::XPropertySetInfo; + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::beans::Property; + + namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute; + + + //= InspectorModelProperties + + /** helper class for implementing the property set related functionality + of an ImplInspectorModel + */ + class InspectorModelProperties : public ::comphelper::OPropertyContainerHelper + { + private: + ::osl::Mutex& m_rMutex; + bool m_bHasHelpSection; + sal_Int32 m_nMinHelpTextLines; + sal_Int32 m_nMaxHelpTextLines; + bool m_bIsReadOnly; + std::unique_ptr< ::cppu::IPropertyArrayHelper > + m_pPropertyInfo; + + public: + explicit InspectorModelProperties( ::osl::Mutex& _rMutex ); + + using ::comphelper::OPropertyContainerHelper::convertFastPropertyValue; + using ::comphelper::OPropertyContainerHelper::setFastPropertyValue; + using ::comphelper::OPropertyContainerHelper::getFastPropertyValue; + + public: + bool hasHelpSection() const { return m_bHasHelpSection; } + bool isReadOnly() const { return m_bIsReadOnly; } + sal_Int32 getMinHelpTextLines() const { return m_nMinHelpTextLines; } + sal_Int32 getMaxHelpTextLines() const { return m_nMaxHelpTextLines; } + + css::uno::Reference< css::beans::XPropertySetInfo > + getPropertySetInfo(); + ::cppu::IPropertyArrayHelper& + getInfoHelper(); + + void constructWithHelpSection( sal_Int32 _nMinHelpTextLines, sal_Int32 _nMaxHelpTextLines ); + }; + + + //= InspectorModelProperties + + + InspectorModelProperties::InspectorModelProperties( ::osl::Mutex& _rMutex ) + :m_rMutex( _rMutex ) + ,m_bHasHelpSection( false ) + ,m_nMinHelpTextLines( 3 ) + ,m_nMaxHelpTextLines( 8 ) + ,m_bIsReadOnly( false ) + { + registerProperty( + "HasHelpSection", + MODEL_PROPERTY_ID_HAS_HELP_SECTION, + PropertyAttribute::READONLY, + &m_bHasHelpSection, cppu::UnoType<decltype(m_bHasHelpSection)>::get() + ); + registerProperty( + "MinHelpTextLines", + MODEL_PROPERTY_ID_MIN_HELP_TEXT_LINES, + PropertyAttribute::READONLY, + &m_nMinHelpTextLines, cppu::UnoType<decltype(m_nMinHelpTextLines)>::get() + ); + registerProperty( + "MaxHelpTextLines", + MODEL_PROPERTY_ID_MAX_HELP_TEXT_LINES, + PropertyAttribute::READONLY, + &m_nMaxHelpTextLines, cppu::UnoType<decltype(m_nMaxHelpTextLines)>::get() + ); + registerProperty( + "IsReadOnly", + MODEL_PROPERTY_ID_IS_READ_ONLY, + PropertyAttribute::BOUND, + &m_bIsReadOnly, cppu::UnoType<decltype(m_bIsReadOnly)>::get() + ); + } + + + void InspectorModelProperties::constructWithHelpSection( sal_Int32 _nMinHelpTextLines, sal_Int32 _nMaxHelpTextLines ) + { + m_bHasHelpSection = true; + m_nMinHelpTextLines = _nMinHelpTextLines; + m_nMaxHelpTextLines = _nMaxHelpTextLines; + // no need to notify this, those properties are not bound. Also, the method should + // only be used during construction phase, where we don't expect to have any listeners. + } + + + ::cppu::IPropertyArrayHelper& InspectorModelProperties::getInfoHelper() + { + ::osl::MutexGuard aGuard( m_rMutex ); + if (m_pPropertyInfo == nullptr) + { + Sequence< Property > aProperties; + describeProperties( aProperties ); + + m_pPropertyInfo.reset( new ::cppu::OPropertyArrayHelper( aProperties ) ); + } + return *m_pPropertyInfo; + } + + + Reference< XPropertySetInfo > InspectorModelProperties::getPropertySetInfo() + { + return ::cppu::OPropertySetHelper::createPropertySetInfo( getInfoHelper() ); + } + + + //= ImplInspectorModel + + ImplInspectorModel::ImplInspectorModel() + :ImplInspectorModel_PBase( GetBroadcastHelper() ) + ,m_pProperties( new InspectorModelProperties( m_aMutex ) ) + { + } + + + ImplInspectorModel::~ImplInspectorModel() + { + } + + + IMPLEMENT_FORWARD_XINTERFACE2( ImplInspectorModel, ImplInspectorModel_Base, ImplInspectorModel_PBase ) + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( ImplInspectorModel, ImplInspectorModel_Base, ImplInspectorModel_PBase ) + + + Reference< XPropertySetInfo > SAL_CALL ImplInspectorModel::getPropertySetInfo( ) + { + return m_pProperties->getPropertySetInfo(); + } + + + ::cppu::IPropertyArrayHelper& SAL_CALL ImplInspectorModel::getInfoHelper() + { + return m_pProperties->getInfoHelper(); + } + + + sal_Bool SAL_CALL ImplInspectorModel::convertFastPropertyValue( Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, const Any& rValue ) + { + return m_pProperties->convertFastPropertyValue( rConvertedValue, rOldValue, nHandle, rValue ); + } + + + void SAL_CALL ImplInspectorModel::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue ) + { + m_pProperties->setFastPropertyValue( nHandle, rValue ); + } + + + void SAL_CALL ImplInspectorModel::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const + { + m_pProperties->getFastPropertyValue( rValue, nHandle ); + } + + + sal_Bool SAL_CALL ImplInspectorModel::getHasHelpSection() + { + return m_pProperties->hasHelpSection(); + } + + + ::sal_Int32 SAL_CALL ImplInspectorModel::getMinHelpTextLines() + { + return m_pProperties->getMinHelpTextLines(); + } + + + ::sal_Int32 SAL_CALL ImplInspectorModel::getMaxHelpTextLines() + { + return m_pProperties->getMaxHelpTextLines(); + } + + + sal_Bool SAL_CALL ImplInspectorModel::getIsReadOnly() + { + return m_pProperties->isReadOnly(); + } + + + void SAL_CALL ImplInspectorModel::setIsReadOnly( sal_Bool IsReadOnly ) + { + setFastPropertyValue( MODEL_PROPERTY_ID_IS_READ_ONLY, Any( IsReadOnly ) ); + } + + sal_Bool SAL_CALL ImplInspectorModel::supportsService( const OUString& ServiceName ) + { + return cppu::supportsService(this, ServiceName); + } + + + void ImplInspectorModel::enableHelpSectionProperties( sal_Int32 _nMinHelpTextLines, sal_Int32 _nMaxHelpTextLines ) + { + m_pProperties->constructWithHelpSection( _nMinHelpTextLines, _nMaxHelpTextLines ); + } + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/inspectormodelbase.hxx b/extensions/source/propctrlr/inspectormodelbase.hxx new file mode 100644 index 000000000..527668b2f --- /dev/null +++ b/extensions/source/propctrlr/inspectormodelbase.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 <com/sun/star/inspection/XObjectInspectorModel.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/propshlp.hxx> + +#include <comphelper/broadcasthelper.hxx> +#include <comphelper/uno3.hxx> + +#include <memory> + + +namespace pcr +{ + + + class InspectorModelProperties; + + //= ImplInspectorModel + + typedef ::cppu::WeakImplHelper < css::inspection::XObjectInspectorModel + , css::lang::XInitialization + , css::lang::XServiceInfo + > ImplInspectorModel_Base; + typedef ::cppu::OPropertySetHelper ImplInspectorModel_PBase; + + class ImplInspectorModel + :public ::comphelper::OMutexAndBroadcastHelper + ,public ImplInspectorModel_Base + ,public ImplInspectorModel_PBase + { + std::unique_ptr< InspectorModelProperties > m_pProperties; + + protected: + virtual ~ImplInspectorModel() override; + + public: + ImplInspectorModel(); + + DECLARE_XINTERFACE() + DECLARE_XTYPEPROVIDER() + + // css::beans::XPropertySet and friends + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() 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; + virtual void SAL_CALL getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle ) const override; + + // css::inspection::XObjectInspectorModel + virtual sal_Bool SAL_CALL getHasHelpSection() override; + virtual ::sal_Int32 SAL_CALL getMinHelpTextLines() override; + virtual ::sal_Int32 SAL_CALL getMaxHelpTextLines() override; + virtual sal_Bool SAL_CALL getIsReadOnly() override; + virtual void SAL_CALL setIsReadOnly( sal_Bool IsReadOnly ) override; + + // css::lang::XServiceInfo + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + + protected: + void enableHelpSectionProperties( sal_Int32 _nMinHelpTextLines, sal_Int32 _nMaxHelpTextLines ); + + private: + using ImplInspectorModel_PBase::getFastPropertyValue; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/linedescriptor.hxx b/extensions/source/propctrlr/linedescriptor.hxx new file mode 100644 index 000000000..a73eda7cf --- /dev/null +++ b/extensions/source/propctrlr/linedescriptor.hxx @@ -0,0 +1,51 @@ +/* -*- 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/inspection/LineDescriptor.hpp> +#include <com/sun/star/inspection/XPropertyHandler.hpp> + +namespace pcr +{ + //= OLineDescriptor + struct OLineDescriptor : public css::inspection::LineDescriptor + { + OUString sName; // the name of the property + css::uno::Reference< css::inspection::XPropertyHandler > + xPropertyHandler; // the handler for this property + css::uno::Any aValue; // the current value of the property + + bool bUnknownValue : 1; // is the property value currently "unknown"? (PropertyState_AMBIGUOUS) + bool bReadOnly : 1; + + OLineDescriptor() + :bUnknownValue( false ) + ,bReadOnly( false ) + { + } + + void assignFrom( const css::inspection::LineDescriptor& _rhs ) + { + css::inspection::LineDescriptor::operator=( _rhs ); + } + }; + +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/listselectiondlg.cxx b/extensions/source/propctrlr/listselectiondlg.cxx new file mode 100644 index 000000000..18ab1d778 --- /dev/null +++ b/extensions/source/propctrlr/listselectiondlg.cxx @@ -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 . + */ + +#include "listselectiondlg.hxx" + +#include "formstrings.hxx" +#include <comphelper/sequence.hxx> +#include <tools/diagnose_ex.h> + +namespace pcr +{ + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + + ListSelectionDialog::ListSelectionDialog(weld::Window* pParent, const Reference< XPropertySet >& _rxListBox, + const OUString& _rPropertyName, const OUString& _rPropertyUIName) + : GenericDialogController(pParent, "modules/spropctrlr/ui/listselectdialog.ui", "ListSelectDialog") + , m_xListBox ( _rxListBox ) + , m_sPropertyName( _rPropertyName ) + , m_xFrame(m_xBuilder->weld_frame("frame")) + , m_xEntries(m_xBuilder->weld_tree_view("treeview")) + { + OSL_PRECOND( m_xListBox.is(), "ListSelectionDialog::ListSelectionDialog: invalid list box!" ); + + m_xEntries->set_size_request(m_xEntries->get_approximate_digit_width() * 40, m_xEntries->get_height_rows(9)); + + m_xDialog->set_title(_rPropertyUIName); + m_xFrame->set_label(_rPropertyUIName); + + initialize( ); + } + + ListSelectionDialog::~ListSelectionDialog() + { + } + + short ListSelectionDialog::run() + { + short nResult = GenericDialogController::run(); + + if ( RET_OK == nResult ) + commitSelection(); + + return nResult; + } + + + void ListSelectionDialog::initialize( ) + { + if ( !m_xListBox.is() ) + return; + + try + { + // initialize the multi-selection flag + bool bMultiSelection = false; + OSL_VERIFY( m_xListBox->getPropertyValue( PROPERTY_MULTISELECTION ) >>= bMultiSelection ); + m_xEntries->set_selection_mode(bMultiSelection ? SelectionMode::Single : SelectionMode::Multiple); + + // fill the list box with all entries + Sequence< OUString > aListEntries; + OSL_VERIFY( m_xListBox->getPropertyValue( PROPERTY_STRINGITEMLIST ) >>= aListEntries ); + fillEntryList( aListEntries ); + + // select entries according to the property + Sequence< sal_Int16 > aSelection; + OSL_VERIFY( m_xListBox->getPropertyValue( m_sPropertyName ) >>= aSelection ); + selectEntries( aSelection ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "ListSelectionDialog::initialize" ); + } + } + + void ListSelectionDialog::commitSelection() + { + if ( !m_xListBox.is() ) + return; + + std::vector< sal_Int16 > aSelection; + collectSelection( aSelection ); + + try + { + m_xListBox->setPropertyValue( m_sPropertyName, Any( comphelper::containerToSequence(aSelection) ) ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "ListSelectionDialog::commitSelection" ); + } + } + + void ListSelectionDialog::fillEntryList( const Sequence< OUString >& _rListEntries ) + { + m_xEntries->freeze(); + m_xEntries->clear(); + for (auto const & entry : _rListEntries) + m_xEntries->append_text(entry); + m_xEntries->thaw(); + } + + void ListSelectionDialog::collectSelection( std::vector< sal_Int16 >& /* [out] */ _rSelection ) + { + auto aSelection = m_xEntries->get_selected_rows(); + _rSelection.resize(aSelection.size()); + for (auto row : aSelection) + _rSelection.push_back(row); + } + + void ListSelectionDialog::selectEntries( const Sequence< sal_Int16 >& /* [in ] */ _rSelection ) + { + m_xEntries->unselect_all(); + for (auto const & selection : _rSelection) + m_xEntries->select(selection); + } + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/listselectiondlg.hxx b/extensions/source/propctrlr/listselectiondlg.hxx new file mode 100644 index 000000000..51469c091 --- /dev/null +++ b/extensions/source/propctrlr/listselectiondlg.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 <vcl/weld.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> + +namespace pcr +{ + class ListSelectionDialog : public weld::GenericDialogController + { + private: + css::uno::Reference<css::beans::XPropertySet> m_xListBox; + OUString m_sPropertyName; + std::unique_ptr<weld::Frame> m_xFrame; + std::unique_ptr<weld::TreeView> m_xEntries; + + public: + ListSelectionDialog( + weld::Window* _pParent, + const css::uno::Reference< css::beans::XPropertySet >& _rxListBox, + const OUString& _rPropertyName, + const OUString& _rPropertyUIName + ); + virtual ~ListSelectionDialog() override; + + virtual short run() override; + + private: + void initialize( ); + void commitSelection(); + + void fillEntryList ( const css::uno::Sequence< OUString >& _rListEntries ); + + void selectEntries ( const css::uno::Sequence< sal_Int16 >& /* [in ] */ _rSelection ); + void collectSelection( std::vector< sal_Int16 >& /* [out] */ _rSelection ); + }; + +} // namespacepcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/modulepcr.cxx b/extensions/source/propctrlr/modulepcr.cxx new file mode 100644 index 000000000..da8336e10 --- /dev/null +++ b/extensions/source/propctrlr/modulepcr.cxx @@ -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 . + */ + +#include "modulepcr.hxx" + +#include <unotools/resmgr.hxx> + +namespace pcr +{ +OUString PcrRes(TranslateId aId) { return Translate::get(aId, Translate::Create("pcr")); } + +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/modulepcr.hxx b/extensions/source/propctrlr/modulepcr.hxx new file mode 100644 index 000000000..d0b854893 --- /dev/null +++ b/extensions/source/propctrlr/modulepcr.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 <rtl/ustring.hxx> +#include <unotools/resmgr.hxx> + +namespace pcr +{ +OUString PcrRes(TranslateId pId); +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/newdatatype.cxx b/extensions/source/propctrlr/newdatatype.cxx new file mode 100644 index 000000000..09c774d3a --- /dev/null +++ b/extensions/source/propctrlr/newdatatype.cxx @@ -0,0 +1,79 @@ +/* -*- 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 "newdatatype.hxx" + +namespace pcr +{ + + + //= NewDataTypeDialog + + + NewDataTypeDialog::NewDataTypeDialog(weld::Window* pParent, const OUString& _rNameBase, const std::vector< OUString >& _rProhibitedNames) + : GenericDialogController(pParent, "modules/spropctrlr/ui/datatypedialog.ui", "DataTypeDialog") + , m_aProhibitedNames( _rProhibitedNames.begin(), _rProhibitedNames.end() ) + , m_xName(m_xBuilder->weld_entry("entry")) + , m_xOK(m_xBuilder->weld_button("ok")) + { + m_xName->connect_changed(LINK(this, NewDataTypeDialog, OnNameModified)); + + // find an initial name + // for this, first remove trailing digits + sal_Int32 nStripUntil = _rNameBase.getLength(); + while ( nStripUntil > 0 ) + { + sal_Unicode nChar = _rNameBase[ --nStripUntil ]; + if ( ( nChar < '0' ) || ( nChar > '9' ) ) + { + if ( nChar == ' ' ) + --nStripUntil; // strip the space, too + break; + } + } + + OUString sNameBase = OUString::Concat(_rNameBase.subView( 0, nStripUntil ? nStripUntil + 1 : 0 )) + " "; + OUString sInitialName; + sal_Int32 nPostfixNumber = 1; + do + { + sInitialName = sNameBase + OUString::number(nPostfixNumber++); + } + while ( m_aProhibitedNames.find( sInitialName ) != m_aProhibitedNames.end() ); + + m_xName->set_text(sInitialName); + OnNameModified(*m_xName); + } + + NewDataTypeDialog::~NewDataTypeDialog() + { + } + + IMPL_LINK_NOARG(NewDataTypeDialog, OnNameModified, weld::Entry&, void) + { + OUString sCurrentName = GetName(); + bool bNameIsOK = ( !sCurrentName.isEmpty() ) + && ( m_aProhibitedNames.find( sCurrentName ) == m_aProhibitedNames.end() ); + + m_xOK->set_sensitive(bNameIsOK); + } +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/newdatatype.hxx b/extensions/source/propctrlr/newdatatype.hxx new file mode 100644 index 000000000..e6d46f1af --- /dev/null +++ b/extensions/source/propctrlr/newdatatype.hxx @@ -0,0 +1,53 @@ +/* -*- 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 <vcl/weld.hxx> + +#include <set> +#include <vector> + + +namespace pcr +{ + //= NewDataTypeDialog + class NewDataTypeDialog : public weld::GenericDialogController + { + private: + std::set<OUString> m_aProhibitedNames; + + std::unique_ptr<weld::Entry> m_xName; + std::unique_ptr<weld::Button> m_xOK; + public: + NewDataTypeDialog(weld::Window* _pParent, const OUString& _rNameBase, + const std::vector< OUString >& _rProhibitedNames ); + virtual ~NewDataTypeDialog() override; + + OUString GetName() const { return m_xName->get_text(); } + + private: + DECL_LINK(OnNameModified, weld::Entry&, void); + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/objectinspectormodel.cxx b/extensions/source/propctrlr/objectinspectormodel.cxx new file mode 100644 index 000000000..adad6e66f --- /dev/null +++ b/extensions/source/propctrlr/objectinspectormodel.cxx @@ -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 . + */ + +#include "pcrcommon.hxx" +#include "inspectormodelbase.hxx" + +#include <com/sun/star/ucb/AlreadyInitializedException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + + +namespace pcr +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::uno::Any; + using ::com::sun::star::inspection::PropertyCategoryDescriptor; + using ::com::sun::star::uno::XInterface; + using ::com::sun::star::lang::IllegalArgumentException; + using ::com::sun::star::ucb::AlreadyInitializedException; + + + //= ObjectInspectorModel + + namespace { + + class ObjectInspectorModel : public ImplInspectorModel + { + private: + Sequence< Any > m_aFactories; + + public: + ObjectInspectorModel(); + + // XObjectInspectorModel + virtual Sequence< Any > SAL_CALL getHandlerFactories() override; + virtual Sequence< PropertyCategoryDescriptor > SAL_CALL describeCategories( ) override; + virtual ::sal_Int32 SAL_CALL getPropertyOrderIndex( const OUString& PropertyName ) override; + + // XInitialization + virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + protected: + void createDefault(); + void createWithHandlerFactories( const Sequence< Any >& _rFactories ); + void createWithHandlerFactoriesAndHelpSection( const Sequence< Any >& _rFactories, sal_Int32 _nMinHelpTextLines, sal_Int32 _nMaxHelpTextLines ); + + private: + /** checks a given condition to be <TRUE/>, and throws an IllegalArgumentException if not + */ + void impl_verifyArgument_throw( bool _bCondition, sal_Int16 _nArgumentPosition ); + }; + + } + + //= ObjectInspectorModel + + ObjectInspectorModel::ObjectInspectorModel() + { + } + + + Sequence< Any > SAL_CALL ObjectInspectorModel::getHandlerFactories() + { + return m_aFactories; + } + + + Sequence< PropertyCategoryDescriptor > SAL_CALL ObjectInspectorModel::describeCategories( ) + { + // no category info provided by this default implementation + return Sequence< PropertyCategoryDescriptor >( ); + } + + + ::sal_Int32 SAL_CALL ObjectInspectorModel::getPropertyOrderIndex( const OUString& /*PropertyName*/ ) + { + // no ordering provided by this default implementation + return 0; + } + + + void SAL_CALL ObjectInspectorModel::initialize( const Sequence< Any >& _arguments ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_aFactories.hasElements() ) + throw AlreadyInitializedException(); + + StlSyntaxSequence< Any > arguments( _arguments ); + if ( arguments.empty() ) + { // constructor: "createDefault()" + createDefault(); + return; + } + + Sequence< Any > factories; + impl_verifyArgument_throw( arguments[0] >>= factories, 1 ); + + if ( arguments.size() == 1 ) + { // constructor: "createWithHandlerFactories( any[] )" + createWithHandlerFactories( factories ); + return; + } + + if ( arguments.size() == 3 ) + { // constructor: "createWithHandlerFactoriesAndHelpSection( any[], long, long )" + sal_Int32 nMinHelpTextLines( 0 ), nMaxHelpTextLines( 0 ); + impl_verifyArgument_throw( arguments[1] >>= nMinHelpTextLines, 2 ); + impl_verifyArgument_throw( arguments[2] >>= nMaxHelpTextLines, 3 ); + createWithHandlerFactoriesAndHelpSection( factories, nMinHelpTextLines, nMaxHelpTextLines ); + return; + } + + impl_verifyArgument_throw( false, 2 ); + } + + + OUString SAL_CALL ObjectInspectorModel::getImplementationName( ) + { + return "org.openoffice.comp.extensions.ObjectInspectorModel"; + } + + + Sequence< OUString > SAL_CALL ObjectInspectorModel::getSupportedServiceNames( ) + { + return { "com.sun.star.inspection.ObjectInspectorModel" }; + } + + + void ObjectInspectorModel::createDefault() + { + m_aFactories = { Any(OUString( "com.sun.star.inspection.GenericPropertyHandler" )) }; + } + + + void ObjectInspectorModel::createWithHandlerFactories( const Sequence< Any >& _rFactories ) + { + impl_verifyArgument_throw( _rFactories.hasElements(), 1 ); + m_aFactories = _rFactories; + } + + + void ObjectInspectorModel::createWithHandlerFactoriesAndHelpSection( const Sequence< Any >& _rFactories, sal_Int32 _nMinHelpTextLines, sal_Int32 _nMaxHelpTextLines ) + { + impl_verifyArgument_throw( _rFactories.hasElements(), 1 ); + impl_verifyArgument_throw( _nMinHelpTextLines >= 1, 2 ); + impl_verifyArgument_throw( _nMaxHelpTextLines >= 1, 3 ); + impl_verifyArgument_throw( _nMinHelpTextLines <= _nMaxHelpTextLines, 2 ); + + m_aFactories = _rFactories; + enableHelpSectionProperties( _nMinHelpTextLines, _nMaxHelpTextLines ); + } + + + void ObjectInspectorModel::impl_verifyArgument_throw( bool _bCondition, sal_Int16 _nArgumentPosition ) + { + if ( !_bCondition ) + throw IllegalArgumentException( OUString(), *this, _nArgumentPosition ); + } + + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_ObjectInspectorModel_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::ObjectInspectorModel()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/pcr.component b/extensions/source/propctrlr/pcr.component new file mode 100644 index 000000000..3d0214ded --- /dev/null +++ b/extensions/source/propctrlr/pcr.component @@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="StringRepresentation" + constructor="extensions_propctrlr_StringRepresentation_get_implementation"> + <service name="com.sun.star.inspection.StringRepresentation"/> + </implementation> + <implementation name="com.sun.star.comp.extensions.ButtonNavigationHandler" + constructor="extensions_propctrlr_ButtonNavigationHandler_get_implementation"> + <service name="com.sun.star.form.inspection.ButtonNavigationHandler"/> + </implementation> + <implementation name="com.sun.star.comp.extensions.CellBindingPropertyHandler" + constructor="extensions_propctrlr_CellBindingPropertyHandler_get_implementation"> + <service name="com.sun.star.form.inspection.CellBindingPropertyHandler"/> + </implementation> + <implementation name="com.sun.star.comp.extensions.EFormsPropertyHandler" + constructor="extensions_propctrlr_EFormsPropertyHandler_get_implementation"> + <service name="com.sun.star.form.inspection.XMLFormsPropertyHandler"/> + </implementation> + <implementation name="com.sun.star.comp.extensions.EditPropertyHandler" + constructor="extensions_propctrlr_EditPropertyHandler_get_implementation"> + <service name="com.sun.star.form.inspection.EditPropertyHandler"/> + </implementation> + <implementation name="com.sun.star.comp.extensions.EventHandler" + constructor="extensions_propctrlr_EventHandler_get_implementation"> + <service name="com.sun.star.form.inspection.EventHandler"/> + </implementation> + <implementation name="com.sun.star.comp.extensions.FormComponentPropertyHandler" + constructor="extensions_propctrlr_FormComponentPropertyHandler_get_implementation"> + <service name="com.sun.star.form.inspection.FormComponentPropertyHandler"/> + </implementation> + <implementation name="com.sun.star.comp.extensions.FormGeometryHandler" + constructor="extensions_propctrlr_FormGeometryHandler_get_implementation"> + <service name="com.sun.star.form.inspection.FormGeometryHandler"/> + </implementation> + <implementation name="com.sun.star.comp.extensions.GenericPropertyHandler" + constructor="extensions_propctrlr_GenericPropertyHandler_get_implementation"> + <service name="com.sun.star.inspection.GenericPropertyHandler"/> + </implementation> + <implementation name="com.sun.star.comp.extensions.SubmissionPropertyHandler" + constructor="extensions_propctrlr_SubmissionPropertyHandler_get_implementation"> + <service name="com.sun.star.form.inspection.SubmissionPropertyHandler"/> + </implementation> + <implementation name="com.sun.star.comp.extensions.XSDValidationPropertyHandler" + constructor="extensions_propctrlr_XSDValidationPropertyHandler_get_implementation"> + <service name="com.sun.star.form.inspection.XSDValidationPropertyHandler"/> + </implementation> + <implementation name="org.openoffice.comp.extensions.DefaultFormComponentInspectorModel" + constructor="extensions_propctrlr_DefaultFormComponentInspectorModel_get_implementation"> + <service name="com.sun.star.form.inspection.DefaultFormComponentInspectorModel"/> + </implementation> + <implementation name="org.openoffice.comp.extensions.DefaultHelpProvider" + constructor="extensions_propctrlr_DefaultHelpProvider_get_implementation"> + <service name="com.sun.star.inspection.DefaultHelpProvider"/> + </implementation> + <implementation name="org.openoffice.comp.extensions.DialogController" + constructor="extensions_propctrlr_DialogController_get_implementation"> + <service name="com.sun.star.awt.PropertyBrowserController"/> + </implementation> + <implementation name="org.openoffice.comp.extensions.FormController" + constructor="extensions_propctrlr_FormController_get_implementation"> + <service name="com.sun.star.form.PropertyBrowserController"/> + </implementation> + <implementation name="org.openoffice.comp.extensions.ObjectInspector" + constructor="extensions_propctrlr_OPropertyBrowserController_get_implementation"> + <service name="com.sun.star.inspection.ObjectInspector"/> + </implementation> + <implementation name="org.openoffice.comp.extensions.ObjectInspectorModel" + constructor="extensions_propctrlr_ObjectInspectorModel_get_implementation"> + <service name="com.sun.star.inspection.ObjectInspectorModel"/> + </implementation> + <implementation name="org.openoffice.comp.form.ui.MasterDetailLinkDialog" + constructor="extensions_propctrlr_MasterDetailLinkDialog_get_implementation"> + <service name="com.sun.star.form.MasterDetailLinkDialog"/> + </implementation> + <implementation name="org.openoffice.comp.form.ui.OControlFontDialog" + constructor="extensions_propctrlr_OControlFontDialog_get_implementation"> + <service name="com.sun.star.form.ControlFontDialog"/> + </implementation> + <implementation name="org.openoffice.comp.form.ui.OTabOrderDialog" + constructor="extensions_propcrltr_OTabOrderDialog_get_implementation"> + <service name="com.sun.star.form.ui.TabOrderDialog"/> + <service name="com.sun.star.form.TabOrderDialog"/> + </implementation> +</component> diff --git a/extensions/source/propctrlr/pcrcommon.cxx b/extensions/source/propctrlr/pcrcommon.cxx new file mode 100644 index 000000000..4151e6827 --- /dev/null +++ b/extensions/source/propctrlr/pcrcommon.cxx @@ -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 . + */ + +#include "pcrcommon.hxx" + +#include <com/sun/star/util/MeasureUnit.hpp> +#include <rtl/ustrbuf.hxx> +#include <tools/urlobj.hxx> + + +namespace pcr +{ + + + using namespace ::com::sun::star::util; + + + //= HelpIdUrl + + + OString HelpIdUrl::getHelpId( std::u16string_view _rHelpURL ) + { + INetURLObject aHID( _rHelpURL ); + if ( aHID.GetProtocol() == INetProtocol::Hid ) + return OUStringToOString( aHID.GetURLPath(), RTL_TEXTENCODING_UTF8 ); + else + return OUStringToOString( _rHelpURL, RTL_TEXTENCODING_UTF8 ); + } + + + OUString HelpIdUrl::getHelpURL( std::u16string_view sHelpId ) + { + OUStringBuffer aBuffer; + INetURLObject aHID( sHelpId ); + if ( aHID.GetProtocol() == INetProtocol::NotValid ) + aBuffer.append( INET_HID_SCHEME ); + aBuffer.append( sHelpId ); + return aBuffer.makeStringAndClear(); + } + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/pcrcommon.hxx b/extensions/source/propctrlr/pcrcommon.hxx new file mode 100644 index 000000000..f9ec2838c --- /dev/null +++ b/extensions/source/propctrlr/pcrcommon.hxx @@ -0,0 +1,121 @@ +/* -*- 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 + +#define EDITOR_LIST_APPEND (SAL_MAX_UINT16) +#define EDITOR_LIST_ENTRY_NOTFOUND (SAL_MAX_UINT16) + +#include <sal/config.h> + +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <comphelper/interfacecontainer3.hxx> + +namespace pcr +{ + + + #define OWN_PROPERTY_ID_INTROSPECTEDOBJECT 0x0010 + #define OWN_PROPERTY_ID_CURRENTPAGE 0x0011 + #define OWN_PROPERTY_ID_CONTROLCONTEXT 0x0012 + #define OWN_PROPERTY_ID_TABBINGMODEL 0x0013 + + + //= types + + typedef ::comphelper::OInterfaceContainerHelper3 < css::beans::XPropertyChangeListener + > PropertyChangeListeners; + + + //= helper + + // small helper to make the "swap" call on an STL container a single-line call, which + // in its canonic form "aFoo.swap( Container() )" doesn't compile with GCC + template< class CONTAINER > + void clearContainer( CONTAINER& _rContainer ) + { + CONTAINER().swap(_rContainer); + } + + + //= HelpIdUrl + + /// small helper to translate help ids into help urls + class HelpIdUrl + { + public: + static OString getHelpId( std::u16string_view _rHelpURL ); + static OUString getHelpURL( std::u16string_view ); + }; + + + //= StlSyntaxSequence + + template< class ELEMENT > + class StlSyntaxSequence : public css::uno::Sequence< ELEMENT > + { + private: + typedef css::uno::Sequence< ELEMENT > UnoBase; + + public: + StlSyntaxSequence() : UnoBase() { } + explicit StlSyntaxSequence( const UnoBase& rSeq ) : UnoBase( rSeq ) { } + explicit StlSyntaxSequence( sal_Int32 len ) : UnoBase( len ) { } + + typedef const ELEMENT* const_iterator; + typedef ELEMENT* iterator; + + const_iterator begin() const { return UnoBase::getConstArray(); } + const_iterator end() const { return UnoBase::getConstArray() + UnoBase::getLength(); } + + iterator begin() { return UnoBase::getArray(); } + iterator end() { return UnoBase::getArray() + UnoBase::getLength(); } + + sal_Int32 size() const { return UnoBase::getLength(); } + bool empty() const { return !UnoBase::hasElements(); } + }; + + + //= UNO helpers + +#define DECLARE_XCOMPONENT() \ + virtual void SAL_CALL dispose( ) override; \ + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; \ + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + +#define IMPLEMENT_FORWARD_XCOMPONENT( classname, baseclass ) \ + void SAL_CALL classname::dispose( ) \ + { \ + baseclass::WeakComponentImplHelperBase::dispose(); \ + } \ + void SAL_CALL classname::addEventListener( const css::uno::Reference< css::lang::XEventListener >& Listener ) \ + { \ + baseclass::WeakComponentImplHelperBase::addEventListener( Listener ); \ + } \ + void SAL_CALL classname::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& Listener ) \ + { \ + baseclass::WeakComponentImplHelperBase::removeEventListener( Listener ); \ + } \ + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/pcrcommontypes.hxx b/extensions/source/propctrlr/pcrcommontypes.hxx new file mode 100644 index 000000000..e2aa3570e --- /dev/null +++ b/extensions/source/propctrlr/pcrcommontypes.hxx @@ -0,0 +1,33 @@ +/* -*- 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/Property.hpp> +#include <rtl/ustring.hxx> + +#include <unordered_map> + +namespace pcr +{ +typedef std::unordered_map<OUString, css::beans::Property> PropertyMap; + +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/pcrstrings.hxx b/extensions/source/propctrlr/pcrstrings.hxx new file mode 100644 index 000000000..3bb0a888c --- /dev/null +++ b/extensions/source/propctrlr/pcrstrings.hxx @@ -0,0 +1,35 @@ +/* -*- 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 <rtl/ustring.hxx> + +namespace pcr +{ + + + // properties + constexpr OUStringLiteral PROPERTY_TABBINGMODEL = u"TabbingModel"; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/pcrunodialogs.cxx b/extensions/source/propctrlr/pcrunodialogs.cxx new file mode 100644 index 000000000..9083cb284 --- /dev/null +++ b/extensions/source/propctrlr/pcrunodialogs.cxx @@ -0,0 +1,143 @@ +/* -*- 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 <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <vcl/svapp.hxx> +#include "pcrunodialogs.hxx" +#include "formstrings.hxx" +#include "pcrstrings.hxx" +#include "taborder.hxx" +#include "pcrcommon.hxx" + + +namespace pcr +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + + + //= OTabOrderDialog + + + OTabOrderDialog::OTabOrderDialog( const Reference< XComponentContext >& _rxContext ) + :OGenericUnoDialog( _rxContext ) + { + registerProperty( PROPERTY_CONTROLCONTEXT, OWN_PROPERTY_ID_CONTROLCONTEXT, + PropertyAttribute::BOUND | PropertyAttribute::TRANSIENT, + &m_xControlContext, cppu::UnoType<decltype(m_xControlContext)>::get() ); + + registerProperty( PROPERTY_TABBINGMODEL, OWN_PROPERTY_ID_TABBINGMODEL, + PropertyAttribute::BOUND | PropertyAttribute::TRANSIENT, + &m_xTabbingModel, cppu::UnoType<decltype(m_xTabbingModel)>::get() ); + } + + OTabOrderDialog::~OTabOrderDialog() + { + if (m_xDialog) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if (m_xDialog) + destroyDialog(); + } + } + + Sequence<sal_Int8> SAL_CALL OTabOrderDialog::getImplementationId( ) + { + return css::uno::Sequence<sal_Int8>(); + } + + OUString SAL_CALL OTabOrderDialog::getImplementationName() + { + return "org.openoffice.comp.form.ui.OTabOrderDialog"; + } + + + css::uno::Sequence<OUString> SAL_CALL OTabOrderDialog::getSupportedServiceNames() + { + return { "com.sun.star.form.ui.TabOrderDialog", "com.sun.star.form.TabOrderDialog" }; + } + + + Reference<XPropertySetInfo> SAL_CALL OTabOrderDialog::getPropertySetInfo() + { + Reference<XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + ::cppu::IPropertyArrayHelper& OTabOrderDialog::getInfoHelper() + { + return *getArrayHelper(); + } + + ::cppu::IPropertyArrayHelper* OTabOrderDialog::createArrayHelper( ) const + { + Sequence< Property > aProps; + describeProperties( aProps ); + return new ::cppu::OPropertyArrayHelper( aProps ); + } + + std::unique_ptr<weld::DialogController> OTabOrderDialog::createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) + { + return std::make_unique<TabOrderDialog>(Application::GetFrameWeld(rParent), m_xTabbingModel, m_xControlContext, m_aContext); + } + + void OTabOrderDialog::initialize( const Sequence< Any >& aArguments ) + { + Reference<css::awt::XTabControllerModel> xTabbingModel; + Reference<css::awt::XControlContainer> xControlContext; + Reference<css::awt::XWindow> xParentWindow; + if (aArguments.getLength() == 3 && (aArguments[0] >>= xTabbingModel) && (aArguments[1] >>= xControlContext) && (aArguments[2] >>= xParentWindow)) + { + Sequence< Any > aNewArguments{ + Any(NamedValue( + "TabbingModel", + Any( xTabbingModel ) + )), + Any(NamedValue( + "ControlContext", + Any( xControlContext ) + )), + Any(NamedValue( + "ParentWindow", + Any( xParentWindow ) + )) + }; + OTabOrderDialog_DBase::initialize(aNewArguments); + } + else + OTabOrderDialog_DBase::initialize(aArguments); + } + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propcrltr_OTabOrderDialog_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::OTabOrderDialog(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/pcrunodialogs.hxx b/extensions/source/propctrlr/pcrunodialogs.hxx new file mode 100644 index 000000000..87c99db35 --- /dev/null +++ b/extensions/source/propctrlr/pcrunodialogs.hxx @@ -0,0 +1,79 @@ +/* -*- 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 <svtools/genericunodialog.hxx> +#include <com/sun/star/awt/XTabControllerModel.hpp> +#include <com/sun/star/awt/XControlContainer.hpp> +#include <comphelper/proparrhlp.hxx> + + +namespace pcr +{ + + + //= OTabOrderDialog + + class OTabOrderDialog; + typedef ::svt::OGenericUnoDialog OTabOrderDialog_DBase; + typedef ::comphelper::OPropertyArrayUsageHelper< OTabOrderDialog > OTabOrderDialog_PBase; + + class OTabOrderDialog + :public OTabOrderDialog_DBase + ,public OTabOrderDialog_PBase + { + protected: + // <properties> + css::uno::Reference< css::awt::XTabControllerModel > + m_xTabbingModel; + css::uno::Reference< css::awt::XControlContainer > + m_xControlContext; + // </properties> + + public: + explicit OTabOrderDialog( const css::uno::Reference< css::uno::XComponentContext >& _rxContext ); + virtual ~OTabOrderDialog() override; + + // XTypeProvider + virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId( ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + protected: + // OGenericUnoDialog overridables + virtual std::unique_ptr<weld::DialogController> createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) override; + }; + + +} // namespacepcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/propcontroller.cxx b/extensions/source/propctrlr/propcontroller.cxx new file mode 100644 index 000000000..6bbcdcf5d --- /dev/null +++ b/extensions/source/propctrlr/propcontroller.cxx @@ -0,0 +1,1652 @@ +/* -*- 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 "propcontroller.hxx" +#include "handlerhelper.hxx" +#include "standardcontrol.hxx" +#include "linedescriptor.hxx" +#include <strings.hrc> +#include "propertyeditor.hxx" +#include "modulepcr.hxx" +#include "formstrings.hxx" +#include "formbrowsertools.hxx" +#include "propertycomposer.hxx" + +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/inspection/PropertyControlType.hpp> +#include <com/sun/star/ucb/AlreadyInitializedException.hpp> +#include <com/sun/star/lang/XSingleComponentFactory.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/util/VetoException.hpp> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <vcl/weldutils.hxx> +#include <osl/mutex.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <algorithm> +#include <sal/log.hxx> + +namespace pcr +{ + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::script; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::frame; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::inspection; + using namespace ::com::sun::star::ucb; + using namespace ::comphelper; + + //= OPropertyBrowserController + OPropertyBrowserController::OPropertyBrowserController( const Reference< XComponentContext >& _rxContext ) + :m_xContext(_rxContext) + ,m_aDisposeListeners( m_aMutex ) + ,m_aControlObservers( m_aMutex ) + ,m_bContainerFocusListening( false ) + ,m_bSuspendingPropertyHandlers( false ) + ,m_bConstructed( false ) + ,m_bBindingIntrospectee( false ) + { + } + + OPropertyBrowserController::~OPropertyBrowserController() + { + // stop listening for property changes + acquire(); + stopInspection( true ); + } + + IMPLEMENT_FORWARD_REFCOUNT( OPropertyBrowserController, OPropertyBrowserController_Base ) + + Any SAL_CALL OPropertyBrowserController::queryInterface( const Type& _rType ) + { + Any aReturn = OPropertyBrowserController_Base::queryInterface( _rType ); + if ( !aReturn.hasValue() ) + aReturn = ::cppu::queryInterface( + _rType, + static_cast< XObjectInspectorUI* >( this ) + ); + return aReturn; + } + + + void OPropertyBrowserController::startContainerWindowListening() + { + if (m_bContainerFocusListening) + return; + + if (m_xFrame.is()) + { + Reference< XWindow > xContainerWindow = m_xFrame->getContainerWindow(); + if (xContainerWindow.is()) + { + xContainerWindow->addFocusListener(this); + m_bContainerFocusListening = true; + } + } + + DBG_ASSERT(m_bContainerFocusListening, "OPropertyBrowserController::startContainerWindowListening: unable to start listening (inconsistence)!"); + } + + + void OPropertyBrowserController::stopContainerWindowListening() + { + if (!m_bContainerFocusListening) + return; + + if (m_xFrame.is()) + { + Reference< XWindow > xContainerWindow = m_xFrame->getContainerWindow(); + if (xContainerWindow.is()) + { + xContainerWindow->removeFocusListener(this); + m_bContainerFocusListening = false; + } + } + + DBG_ASSERT(!m_bContainerFocusListening, "OPropertyBrowserController::stopContainerWindowListening: unable to stop listening (inconsistence)!"); + } + + + Reference< XObjectInspectorModel > SAL_CALL OPropertyBrowserController::getInspectorModel() + { + return m_xModel; + } + + + void OPropertyBrowserController::impl_initializeView_nothrow() + { + OSL_PRECOND( haveView(), "OPropertyBrowserController::impl_initializeView_nothrow: not to be called when we have no view!" ); + if ( !haveView() ) + return; + + if ( !m_xModel.is() ) + // allowed + return; + + try + { + getPropertyBox().EnableHelpSection( m_xModel->getHasHelpSection() ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + + bool OPropertyBrowserController::impl_isReadOnlyModel_throw() const + { + if ( !m_xModel.is() ) + return false; + + return m_xModel->getIsReadOnly(); + } + + + void OPropertyBrowserController::impl_startOrStopModelListening_nothrow( bool _bDoListen ) const + { + try + { + Reference< XPropertySet > xModelProperties( m_xModel, UNO_QUERY ); + if ( !xModelProperties.is() ) + // okay, so the model doesn't want to change its properties + // dynamically - fine with us + return; + + void (SAL_CALL XPropertySet::*pListenerOperation)( const OUString&, const Reference< XPropertyChangeListener >& ) + = _bDoListen ? &XPropertySet::addPropertyChangeListener : &XPropertySet::removePropertyChangeListener; + + (xModelProperties.get()->*pListenerOperation)( + OUString( "IsReadOnly" ), + const_cast< OPropertyBrowserController* >( this ) + ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + + void OPropertyBrowserController::impl_bindToNewModel_nothrow( const Reference< XObjectInspectorModel >& _rxInspectorModel ) + { + impl_startOrStopModelListening_nothrow( false ); + m_xModel = _rxInspectorModel; + impl_startOrStopModelListening_nothrow( true ); + + // initialize the view, if we already have one + if ( haveView() ) + impl_initializeView_nothrow(); + + // inspect again, if we already have inspectees + if ( !m_aInspectedObjects.empty() ) + impl_rebindToInspectee_nothrow( std::vector(m_aInspectedObjects) ); + } + + + void SAL_CALL OPropertyBrowserController::setInspectorModel( const Reference< XObjectInspectorModel >& _inspectorModel ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( m_xModel == _inspectorModel ) + return; + + impl_bindToNewModel_nothrow( _inspectorModel ); + } + + + Reference< XObjectInspectorUI > SAL_CALL OPropertyBrowserController::getInspectorUI() + { + // we're derived from this interface, though we do not expose it in queryInterface and getTypes. + return this; + } + + + void SAL_CALL OPropertyBrowserController::inspect( const Sequence< Reference< XInterface > >& _rObjects ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( m_bSuspendingPropertyHandlers || !suspendAll_nothrow() ) + { // we already are trying to suspend the component (this is somewhere up the stack) + // OR one of our property handlers raised a veto against closing. Well, we *need* to close + // it in order to inspect another object. + throw VetoException(); + } + if ( m_bBindingIntrospectee ) + throw VetoException(); + + m_bBindingIntrospectee = true; + impl_rebindToInspectee_nothrow( InterfaceArray( _rObjects.begin(), _rObjects.end() ) ); + m_bBindingIntrospectee = false; + + } + + + Reference< XDispatch > SAL_CALL OPropertyBrowserController::queryDispatch( const URL& /*URL*/, const OUString& /*TargetFrameName*/, ::sal_Int32 /*SearchFlags*/ ) + { + // we don't have any dispatches at all, right now + return Reference< XDispatch >(); + } + + + Sequence< Reference< XDispatch > > SAL_CALL OPropertyBrowserController::queryDispatches( const Sequence< DispatchDescriptor >& Requests ) + { + Sequence< Reference< XDispatch > > aReturn; + sal_Int32 nLen = Requests.getLength(); + aReturn.realloc( nLen ); + + Reference< XDispatch >* pReturn = aReturn.getArray(); + const Reference< XDispatch >* pReturnEnd = aReturn.getArray() + nLen; + const DispatchDescriptor* pDescripts = Requests.getConstArray(); + + for ( ; pReturn != pReturnEnd; ++ pReturn, ++pDescripts ) + *pReturn = queryDispatch( pDescripts->FeatureURL, pDescripts->FrameName, pDescripts->SearchFlags ); + + return aReturn; + } + + + void SAL_CALL OPropertyBrowserController::initialize( const Sequence< Any >& _arguments ) + { + if ( m_bConstructed ) + throw AlreadyInitializedException(); + + StlSyntaxSequence< Any > arguments( _arguments ); + if ( arguments.empty() ) + { // constructor: "createDefault()" + m_bConstructed = true; + return; + } + + Reference< XObjectInspectorModel > xModel; + if ( arguments.size() == 1 ) + { // constructor: "createWithModel( XObjectInspectorModel )" + if ( !( arguments[0] >>= xModel ) ) + throw IllegalArgumentException( OUString(), *this, 0 ); + createWithModel( xModel ); + return; + } + + throw IllegalArgumentException( OUString(), *this, 0 ); + } + + + void OPropertyBrowserController::createWithModel( const Reference< XObjectInspectorModel >& _rxModel ) + { + osl_atomic_increment( &m_refCount ); + { + setInspectorModel( _rxModel ); + } + osl_atomic_decrement( &m_refCount ); + + m_bConstructed = true; + } + + + void SAL_CALL OPropertyBrowserController::attachFrame( const Reference< XFrame >& _rxFrame ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + if (_rxFrame.is() && haveView()) + throw RuntimeException("Unable to attach to a second frame.",*this); + + // revoke as focus listener from the old container window + stopContainerWindowListening(); + + m_xPropView.reset(); + m_xBuilder.reset(); + + m_xFrame = _rxFrame; + if (!m_xFrame.is()) + return; + + // TODO: this construction perhaps should be done outside. Don't know the exact meaning of attachFrame. + // Maybe it is intended to only announce the frame to the controller, and the instance doing this + // announcement is responsible for calling setComponent, too. + Reference<XWindow> xContainerWindow = m_xFrame->getContainerWindow(); + + OUString sUIFile("modules/spropctrlr/ui/formproperties.ui"); + std::unique_ptr<weld::Builder> xBuilder; + + if (weld::TransportAsXWindow* pTunnel = dynamic_cast<weld::TransportAsXWindow*>(xContainerWindow.get())) + { + xBuilder = Application::CreateBuilder(pTunnel->getWidget(), sUIFile); + } + else + { + VclPtr<vcl::Window> pParentWin = VCLUnoHelper::GetWindow(xContainerWindow); + if (!pParentWin) + throw RuntimeException("The frame is invalid. Unable to extract the container window.",*this); + xBuilder = Application::CreateInterimBuilder(pParentWin, sUIFile, true); + } + + Construct(xContainerWindow, std::move(xBuilder)); + + startContainerWindowListening(); + + UpdateUI(); + } + + sal_Bool SAL_CALL OPropertyBrowserController::attachModel( const Reference< XModel >& _rxModel ) + { + Reference< XObjectInspectorModel > xModel( _rxModel, UNO_QUERY ); + if ( !xModel.is() ) + return false; + + setInspectorModel( xModel ); + return getInspectorModel() == _rxModel; + } + + + bool OPropertyBrowserController::suspendAll_nothrow() + { + // if there is a handle inside its "onInteractivePropertySelection" method, + // then veto + // Normally, we could expect every handler to do this itself, but being + // realistic, it's safer to handle this here in general. + if ( m_xInteractiveHandler.is() ) + return false; + + m_bSuspendingPropertyHandlers = true; + bool bHandlerVeto = !suspendPropertyHandlers_nothrow( true ); + m_bSuspendingPropertyHandlers = false; + return !bHandlerVeto; + } + + + bool OPropertyBrowserController::suspendPropertyHandlers_nothrow( bool _bSuspend ) + { + PropertyHandlerArray aAllHandlers; // will contain every handler exactly once + for (auto const& propertyHandler : m_aPropertyHandlers) + { + if ( std::find( aAllHandlers.begin(), aAllHandlers.end(), propertyHandler.second ) != aAllHandlers.end() ) + // already visited this particular handler (m_aPropertyHandlers usually contains + // the same handler more than once) + continue; + aAllHandlers.push_back(propertyHandler.second); + } + + for (auto const& handler : aAllHandlers) + { + try + { + if ( !handler->suspend( _bSuspend ) ) + if ( _bSuspend ) + // if we're not suspending, but reactivating, ignore the error + return false; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "OPropertyBrowserController::suspendPropertyHandlers_nothrow" ); + } + } + return true; + } + + + sal_Bool SAL_CALL OPropertyBrowserController::suspend( sal_Bool _bSuspend ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + OSL_ENSURE( haveView(), "OPropertyBrowserController::suspend: don't have a view anymore!" ); + + if ( !_bSuspend ) + { // this means a "suspend" is to be "revoked" + suspendPropertyHandlers_nothrow( false ); + // we ourself cannot revoke our suspend + return false; + } + + if ( !suspendAll_nothrow() ) + return false; + + // commit the editor's content + if ( haveView() ) + getPropertyBox().CommitModified(); + + // stop listening + stopContainerWindowListening(); + + // outta here + return true; + } + + + Any SAL_CALL OPropertyBrowserController::getViewData( ) + { + return Any( m_sPageSelection ); + } + + + void SAL_CALL OPropertyBrowserController::restoreViewData( const Any& Data ) + { + OUString sPageSelection; + if ( ( Data >>= sPageSelection ) && !sPageSelection.isEmpty() ) + { + m_sPageSelection = sPageSelection; + selectPageFromViewData(); + } + } + + Reference< XModel > SAL_CALL OPropertyBrowserController::getModel( ) + { + // have no model + return Reference< XModel >(); + } + + Reference< XFrame > SAL_CALL OPropertyBrowserController::getFrame( ) + { + return m_xFrame; + } + + void SAL_CALL OPropertyBrowserController::dispose() + { + SolarMutexGuard aSolarGuard; + + // stop inspecting the current object + stopInspection( false ); + + // say our dispose listeners goodbye + css::lang::EventObject aEvt; + aEvt.Source = static_cast< ::cppu::OWeakObject* >(this); + m_aDisposeListeners.disposeAndClear(aEvt); + m_aControlObservers.disposeAndClear(aEvt); + + m_xPropView.reset(); + m_xBuilder.reset(); + + if ( m_xView.is() ) + m_xView->removeEventListener( static_cast< XPropertyChangeListener* >( this ) ); + m_xView.clear( ); + + m_aInspectedObjects.clear(); + impl_bindToNewModel_nothrow( nullptr ); + } + + void SAL_CALL OPropertyBrowserController::addEventListener( const Reference< XEventListener >& _rxListener ) + { + m_aDisposeListeners.addInterface(_rxListener); + } + + void SAL_CALL OPropertyBrowserController::removeEventListener( const Reference< XEventListener >& _rxListener ) + { + m_aDisposeListeners.removeInterface(_rxListener); + } + + OUString SAL_CALL OPropertyBrowserController::getImplementationName( ) + { + return "org.openoffice.comp.extensions.ObjectInspector"; + } + + sal_Bool SAL_CALL OPropertyBrowserController::supportsService( const OUString& ServiceName ) + { + return cppu::supportsService(this, ServiceName); + } + + + Sequence< OUString > SAL_CALL OPropertyBrowserController::getSupportedServiceNames( ) + { + return { "com.sun.star.inspection.ObjectInspector" }; + } + + + void SAL_CALL OPropertyBrowserController::focusGained( const FocusEvent& _rSource ) + { + Reference< XWindow > xSourceWindow(_rSource.Source, UNO_QUERY); + Reference< XWindow > xContainerWindow; + if (m_xFrame.is()) + xContainerWindow = m_xFrame->getContainerWindow(); + + if ( xContainerWindow.get() == xSourceWindow.get() ) + { // our container window got the focus + if ( haveView() ) + getPropertyBox().GrabFocus(); + } + } + + + void SAL_CALL OPropertyBrowserController::focusLost( const FocusEvent& /*_rSource*/ ) + { + // not interested in + } + + + void SAL_CALL OPropertyBrowserController::disposing( const EventObject& _rSource ) + { + if ( m_xView.is() && ( m_xView == _rSource.Source ) ) + { + m_xView = nullptr; + m_xPropView.reset(); + m_xBuilder.reset(); + } + + auto it = std::find_if(m_aInspectedObjects.begin(), m_aInspectedObjects.end(), + [&_rSource](const InterfaceArray::value_type& rxObj) { return rxObj == _rSource.Source; }); + if (it != m_aInspectedObjects.end()) + m_aInspectedObjects.erase(it); + } + + + IMPL_LINK_NOARG(OPropertyBrowserController, OnPageActivation, LinkParamNone*, void) + { + updateViewDataFromActivePage(); + } + + + void OPropertyBrowserController::updateViewDataFromActivePage() + { + if (!haveView()) + return; + + OUString sOldSelection = m_sPageSelection; + m_sPageSelection.clear(); + + const sal_uInt16 nCurrentPage = m_xPropView->getActivePage(); + if ( sal_uInt16(-1) != nCurrentPage ) + { + for (auto const& pageId : m_aPageIds) + { + if ( nCurrentPage == pageId.second ) + { + m_sPageSelection = pageId.first; + break; + } + } + } + + if ( !m_sPageSelection.isEmpty() ) + m_sLastValidPageSelection = m_sPageSelection; + else if ( !sOldSelection.isEmpty() ) + m_sLastValidPageSelection = sOldSelection; + } + + + sal_uInt16 OPropertyBrowserController::impl_getPageIdForCategory_nothrow( const OUString& _rCategoryName ) const + { + sal_uInt16 nPageId = sal_uInt16(-1); + HashString2Int16::const_iterator pagePos = m_aPageIds.find( _rCategoryName ); + if ( pagePos != m_aPageIds.end() ) + nPageId = pagePos->second; + return nPageId; + } + + void OPropertyBrowserController::selectPageFromViewData() + { + sal_uInt16 nNewPage = impl_getPageIdForCategory_nothrow( m_sPageSelection ); + + if ( haveView() && ( nNewPage != sal_uInt16(-1) ) ) + m_xPropView->activatePage( nNewPage ); + + // just in case ... + updateViewDataFromActivePage(); + } + + void OPropertyBrowserController::Construct(const Reference<XWindow>& rContainerWindow, std::unique_ptr<weld::Builder> xBuilder) + { + DBG_ASSERT(!haveView(), "OPropertyBrowserController::Construct: already have a view!"); + assert(xBuilder && "OPropertyBrowserController::Construct: invalid parent window!"); + + m_xBuilder = std::move(xBuilder); + + m_xPropView.reset(new OPropertyBrowserView(m_xContext, *m_xBuilder)); + m_xPropView->setPageActivationHandler(LINK(this, OPropertyBrowserController, OnPageActivation)); + + // add as dispose listener for our view. The view is disposed by the frame we're plugged into, + // and this disposal _deletes_ the view, so it would be deadly if we use our m_xPropView member + // after that + m_xView = rContainerWindow; + if (m_xView.is()) + m_xView->addEventListener( static_cast< XPropertyChangeListener* >( this ) ); + + getPropertyBox().SetLineListener(this); + getPropertyBox().SetControlObserver(this); + impl_initializeView_nothrow(); + } + + void SAL_CALL OPropertyBrowserController::propertyChange( const PropertyChangeEvent& _rEvent ) + { + if ( _rEvent.Source == m_xModel ) + { + if ( _rEvent.PropertyName == "IsReadOnly" ) + // this is a huge cudgel, admitted. + // The problem is that in case we were previously read-only, all our controls + // were created read-only, too. We cannot simply switch them to not-read-only. + // Even if they had an API for this, we do not know whether they were + // originally created read-only, or if they are read-only just because + // the model was. + impl_rebindToInspectee_nothrow( std::vector(m_aInspectedObjects) ); + return; + } + + if ( m_sCommittingProperty == _rEvent.PropertyName ) + return; + + if ( !haveView() ) + return; + + Any aNewValue( _rEvent.NewValue ); + if ( impl_hasPropertyHandlerFor_nothrow( _rEvent.PropertyName ) ) + { + // forward the new value to the property box, to reflect the change in the UI + aNewValue = impl_getPropertyValue_throw( _rEvent.PropertyName ); + + // check whether the state is ambiguous. This is interesting in case we display the properties + // for multiple objects at once: In this case, we'll get a notification from one of the objects, + // but need to care for the "composed" value, which can be "ambiguous". + PropertyHandlerRef xHandler( impl_getHandlerForProperty_throw( _rEvent.PropertyName ), UNO_SET_THROW ); + PropertyState ePropertyState( xHandler->getPropertyState( _rEvent.PropertyName ) ); + bool bAmbiguousValue = ( PropertyState_AMBIGUOUS_VALUE == ePropertyState ); + + getPropertyBox().SetPropertyValue( _rEvent.PropertyName, aNewValue, bAmbiguousValue ); + } + + // if it's an actuating property, then update the UI for any dependent + // properties + if ( impl_isActuatingProperty_nothrow( _rEvent.PropertyName ) ) + impl_broadcastPropertyChange_nothrow( _rEvent.PropertyName, aNewValue, _rEvent.OldValue, false ); + } + + Reference< XPropertyControl > SAL_CALL OPropertyBrowserController::createPropertyControl( ::sal_Int16 ControlType, sal_Bool bCreateReadOnly ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + Reference< XPropertyControl > xControl; + + // read-only-ness + bCreateReadOnly |= impl_isReadOnlyModel_throw() ? 1 : 0; + + switch ( ControlType ) + { + case PropertyControlType::MultiLineTextField: + case PropertyControlType::StringListField: + { + bool bMultiLineTextField = ControlType == PropertyControlType::MultiLineTextField; + std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/multiline.ui", m_xContext)); + auto pContainer = xBuilder->weld_container("multiline"); + rtl::Reference<OMultilineEditControl> pControl = new OMultilineEditControl(std::move(pContainer), std::move(xBuilder), + bMultiLineTextField ? eMultiLineText : eStringList, bCreateReadOnly); + pControl->SetModifyHandler(); + xControl = pControl; + break; + } + + case PropertyControlType::ListBox: + { + std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/listbox.ui", m_xContext)); + auto pComboBox = xBuilder->weld_combo_box("listbox"); + rtl::Reference<OListboxControl> pControl = new OListboxControl(std::move(pComboBox), std::move(xBuilder), bCreateReadOnly); + pControl->SetModifyHandler(); + xControl = pControl; + break; + } + + case PropertyControlType::ComboBox: + { + std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/combobox.ui", m_xContext)); + auto pComboBox = xBuilder->weld_combo_box("combobox"); + rtl::Reference<OComboboxControl> pControl = new OComboboxControl(std::move(pComboBox), std::move(xBuilder), bCreateReadOnly); + pControl->SetModifyHandler(); + xControl = pControl; + break; + } + + case PropertyControlType::TextField: + case PropertyControlType::CharacterField: + { + bool bCharacterField = ControlType == PropertyControlType::CharacterField; + std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/textfield.ui", m_xContext)); + auto pEntry = xBuilder->weld_entry("textfield"); + rtl::Reference<OEditControl> pControl = new OEditControl(std::move(pEntry), std::move(xBuilder), bCharacterField, bCreateReadOnly); + pControl->SetModifyHandler(); + xControl = pControl; + break; + } + + case PropertyControlType::NumericField: + { + std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/numericfield.ui", m_xContext)); + auto pSpinButton = xBuilder->weld_metric_spin_button("numericfield", FieldUnit::NONE); + rtl::Reference<ONumericControl> pControl = new ONumericControl(std::move(pSpinButton), std::move(xBuilder), bCreateReadOnly); + pControl->SetModifyHandler(); + xControl = pControl; + break; + } + + case PropertyControlType::DateTimeField: + { + std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/datetimefield.ui", m_xContext)); + auto pContainer = xBuilder->weld_container("datetimefield"); + rtl::Reference<ODateTimeControl> pControl = new ODateTimeControl(std::move(pContainer), std::move(xBuilder), bCreateReadOnly); + pControl->SetModifyHandler(); + xControl = pControl; + break; + } + + case PropertyControlType::DateField: + { + std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/datefield.ui", m_xContext)); + auto pContainer = xBuilder->weld_container("datefield"); + rtl::Reference<ODateControl> pControl = new ODateControl(std::move(pContainer), std::move(xBuilder), bCreateReadOnly); + pControl->SetModifyHandler(); + xControl = pControl; + break; + } + + case PropertyControlType::TimeField: + { + std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/timefield.ui", m_xContext)); + auto pTimeSpinButton = xBuilder->weld_formatted_spin_button("timefield"); + rtl::Reference<OTimeControl> pControl = new OTimeControl(std::move(pTimeSpinButton), std::move(xBuilder), bCreateReadOnly); + pControl->SetModifyHandler(); + xControl = pControl; + break; + } + + case PropertyControlType::ColorListBox: + { + auto lambda = [this]{ return PropertyHandlerHelper::getDialogParentFrame(m_xContext); }; + std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/colorlistbox.ui", m_xContext)); + auto pMenuButton = xBuilder->weld_menu_button("colorlistbox"); + rtl::Reference<OColorControl> pControl = new OColorControl(std::make_unique<ColorListBox>(std::move(pMenuButton), lambda), std::move(xBuilder), bCreateReadOnly); + pControl->SetModifyHandler(); + xControl = pControl; + break; + } + + case PropertyControlType::HyperlinkField: + { + std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/hyperlinkfield.ui", m_xContext)); + auto pContainer = xBuilder->weld_container("hyperlinkfield"); + rtl::Reference<OHyperlinkControl> pControl = new OHyperlinkControl(std::move(pContainer), std::move(xBuilder), bCreateReadOnly); + pControl->SetModifyHandler(); + xControl = pControl; + break; + } + + default: + throw IllegalArgumentException( OUString(), *this, 1 ); + } + + return xControl; + } + + + void OPropertyBrowserController::impl_toggleInspecteeListening_nothrow( bool _bOn ) + { + for (auto const& inspectedObject : m_aInspectedObjects) + { + try + { + Reference< XComponent > xComp( inspectedObject, UNO_QUERY ); + if ( xComp.is() ) + { + if ( _bOn ) + xComp->addEventListener( static_cast< XPropertyChangeListener* >( this ) ); + else + xComp->removeEventListener( static_cast< XPropertyChangeListener* >( this ) ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + } + + + void OPropertyBrowserController::stopInspection( bool _bCommitModified ) + { + if ( haveView() ) + { + if ( _bCommitModified ) + // commit the editor's content + getPropertyBox().CommitModified(); + + // hide the property box so that it does not flicker + getPropertyBox().Hide(); + + // clear the property box + getPropertyBox().ClearAll(); + } + + // destroy the view first + if ( haveView() ) + { + // remove the pages + for (auto const& pageId : m_aPageIds) + getPropertyBox().RemovePage( pageId.second ); + clearContainer( m_aPageIds ); + } + + clearContainer( m_aProperties ); + + // de-register as dispose-listener from our inspected objects + impl_toggleInspecteeListening_nothrow( false ); + + // handlers are obsolete, so is our "composer" for their UI requests + if (m_pUIRequestComposer) + m_pUIRequestComposer->dispose(); + m_pUIRequestComposer.reset(); + + // clean up the property handlers + PropertyHandlerArray aAllHandlers; // will contain every handler exactly once + for (auto const& propertyHandler : m_aPropertyHandlers) + if ( std::find( aAllHandlers.begin(), aAllHandlers.end(), propertyHandler.second ) == aAllHandlers.end() ) + aAllHandlers.push_back( propertyHandler.second ); + + for (auto const& handler : aAllHandlers) + { + try + { + handler->removePropertyChangeListener( this ); + handler->dispose(); + } + catch( const DisposedException& ) + { + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + clearContainer( m_aPropertyHandlers ); + clearContainer( m_aDependencyHandlers ); + } + + + bool OPropertyBrowserController::impl_hasPropertyHandlerFor_nothrow( const OUString& _rPropertyName ) const + { + PropertyHandlerRepository::const_iterator handlerPos = m_aPropertyHandlers.find( _rPropertyName ); + return ( handlerPos != m_aPropertyHandlers.end() ); + } + + + OPropertyBrowserController::PropertyHandlerRef const & OPropertyBrowserController::impl_getHandlerForProperty_throw( const OUString& _rPropertyName ) const + { + PropertyHandlerRepository::const_iterator handlerPos = m_aPropertyHandlers.find( _rPropertyName ); + if ( handlerPos == m_aPropertyHandlers.end() ) + throw RuntimeException(); + return handlerPos->second; + } + + + Any OPropertyBrowserController::impl_getPropertyValue_throw( const OUString& _rPropertyName ) + { + PropertyHandlerRef handler = impl_getHandlerForProperty_throw( _rPropertyName ); + return handler->getPropertyValue( _rPropertyName ); + } + + + void OPropertyBrowserController::impl_rebindToInspectee_nothrow( InterfaceArray&& _rObjects ) + { + try + { + // stop inspecting the old object(s) + stopInspection( true ); + + // inspect the new object(s) + m_aInspectedObjects = std::move(_rObjects); + doInspection(); + + // update the user interface + UpdateUI(); + } + + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION("extensions.propctrlr", ""); + } + } + + + void OPropertyBrowserController::doInspection() + { + try + { + + // obtain the properties of the object + std::vector< Property > aProperties; + + PropertyHandlerArray aPropertyHandlers; + getPropertyHandlers( m_aInspectedObjects, aPropertyHandlers ); + + PropertyHandlerArray::iterator aHandler( aPropertyHandlers.begin() ); + while ( aHandler != aPropertyHandlers.end() ) + { + DBG_ASSERT( aHandler->get(), "OPropertyBrowserController::doInspection: invalid handler!" ); + + StlSyntaxSequence< Property > aThisHandlersProperties( (*aHandler)->getSupportedProperties() ); + + if ( aThisHandlersProperties.empty() ) + { + // this handler doesn't know anything about the current inspectee -> ignore it + (*aHandler)->dispose(); + aHandler = aPropertyHandlers.erase( aHandler ); + continue; + } + + // append these properties to our "all properties" array + aProperties.reserve( aProperties.size() + aThisHandlersProperties.size() ); + for (const auto & aThisHandlersProperty : aThisHandlersProperties) + { + auto noPrevious = std::none_of( + aProperties.begin(), + aProperties.end(), + FindPropertyByName( aThisHandlersProperty.Name ) + ); + if ( noPrevious ) + { + aProperties.push_back( aThisHandlersProperty ); + continue; + } + + // there already was another (previous) handler which supported this property. + // Don't add it to aProperties, again. + + // Also, ensure that handlers which previously expressed interest in *changes* + // of this property are not notified. + // This is 'cause we have a new handler which is responsible for this property, + // which means it can give it a completely different meaning than the previous + // handler for this property is prepared for. + std::pair< PropertyHandlerMultiRepository::iterator, PropertyHandlerMultiRepository::iterator > + aDepHandlers = m_aDependencyHandlers.equal_range( aThisHandlersProperty.Name ); + m_aDependencyHandlers.erase( aDepHandlers.first, aDepHandlers.second ); + } + + // determine the superseded properties + StlSyntaxSequence< OUString > aSupersededByThisHandler( (*aHandler)->getSupersededProperties() ); + for (const auto & superseded : aSupersededByThisHandler) + { + std::vector< Property >::iterator existent = std::find_if( + aProperties.begin(), + aProperties.end(), + FindPropertyByName( superseded ) + ); + if ( existent != aProperties.end() ) + // one of the properties superseded by this handler was supported by a previous + // one -> erase + aProperties.erase( existent ); + } + + // be notified of changes which this handler is responsible for + (*aHandler)->addPropertyChangeListener( this ); + + // remember this handler for every of the properties which it is responsible + // for + for (const auto & aThisHandlersProperty : aThisHandlersProperties) + { + m_aPropertyHandlers[ aThisHandlersProperty.Name ] = *aHandler; + // note that this implies that if two handlers support the same property, + // the latter wins + } + + // see if the handler expresses interest in any actuating properties + StlSyntaxSequence< OUString > aInterestingActuations( (*aHandler)->getActuatingProperties() ); + for (const auto & aInterestingActuation : aInterestingActuations) + { + m_aDependencyHandlers.emplace( aInterestingActuation, *aHandler ); + } + + ++aHandler; + } + + // create a new composer for UI requests coming from the handlers + m_pUIRequestComposer.reset( new ComposedPropertyUIUpdate( getInspectorUI(), this ) ); + + // sort the properties by relative position, as indicated by the model + sal_Int32 nPos = 0; + for (auto const& sourceProps : aProperties) + { + sal_Int32 nRelativePropertyOrder = nPos; + if ( m_xModel.is() ) + nRelativePropertyOrder = m_xModel->getPropertyOrderIndex( sourceProps.Name ); + m_aProperties.emplace(nRelativePropertyOrder, sourceProps); + ++nPos; + } + + // be notified when one of our inspectees dies + impl_toggleInspecteeListening_nothrow( true ); + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION("extensions.propctrlr", ""); + } + } + + + css::awt::Size SAL_CALL OPropertyBrowserController::getMinimumSize() + { + css::awt::Size aSize; + if( m_xPropView ) + return m_xPropView->getMinimumSize(); + else + return aSize; + } + + + css::awt::Size SAL_CALL OPropertyBrowserController::getPreferredSize() + { + return getMinimumSize(); + } + + + css::awt::Size SAL_CALL OPropertyBrowserController::calcAdjustedSize( const css::awt::Size& _rNewSize ) + { + awt::Size aMinSize = getMinimumSize( ); + awt::Size aAdjustedSize( _rNewSize ); + if ( aAdjustedSize.Width < aMinSize.Width ) + aAdjustedSize.Width = aMinSize.Width; + if ( aAdjustedSize.Height < aMinSize.Height ) + aAdjustedSize.Height = aMinSize.Height; + return aAdjustedSize; + } + + + void OPropertyBrowserController::describePropertyLine( const Property& _rProperty, OLineDescriptor& _rDescriptor ) + { + try + { + PropertyHandlerRepository::const_iterator handler = m_aPropertyHandlers.find( _rProperty.Name ); + if ( handler == m_aPropertyHandlers.end() ) + throw RuntimeException(); // caught below + + _rDescriptor.assignFrom( handler->second->describePropertyLine( _rProperty.Name, this ) ); + + + _rDescriptor.xPropertyHandler = handler->second; + _rDescriptor.sName = _rProperty.Name; + _rDescriptor.aValue = _rDescriptor.xPropertyHandler->getPropertyValue( _rProperty.Name ); + + if ( _rDescriptor.DisplayName.isEmpty() ) + { + #ifdef DBG_UTIL + SAL_WARN( "extensions.propctrlr", "OPropertyBrowserController::describePropertyLine: handler did not provide a display name for '" + <<_rProperty.Name << "'!" ); + #endif + _rDescriptor.DisplayName = _rProperty.Name; + } + + PropertyState ePropertyState( _rDescriptor.xPropertyHandler->getPropertyState( _rProperty.Name ) ); + if ( PropertyState_AMBIGUOUS_VALUE == ePropertyState ) + { + _rDescriptor.bUnknownValue = true; + _rDescriptor.aValue.clear(); + } + + _rDescriptor.bReadOnly = impl_isReadOnlyModel_throw(); + + // for ui-testing try and distinguish different instances of the controls + auto xWindow = _rDescriptor.Control->getControlWindow(); + if (weld::TransportAsXWindow* pTunnel = dynamic_cast<weld::TransportAsXWindow*>(xWindow.get())) + { + weld::Widget* m_pControlWindow = pTunnel->getWidget(); + if (m_pControlWindow) + m_pControlWindow->set_buildable_name(m_pControlWindow->get_buildable_name() + "-" + _rDescriptor.DisplayName.toUtf8()); + } + + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "OPropertyBrowserController::describePropertyLine" ); + } + } + + + void OPropertyBrowserController::impl_buildCategories_throw() + { + OSL_PRECOND( m_aPageIds.empty(), "OPropertyBrowserController::impl_buildCategories_throw: duplicate call!" ); + + StlSyntaxSequence< PropertyCategoryDescriptor > aCategories; + if ( m_xModel.is() ) + aCategories = StlSyntaxSequence< PropertyCategoryDescriptor >(m_xModel->describeCategories()); + + for (auto const& category : aCategories) + { + OSL_ENSURE( m_aPageIds.find( category.ProgrammaticName ) == m_aPageIds.end(), + "OPropertyBrowserController::impl_buildCategories_throw: duplicate programmatic name!" ); + + m_aPageIds[ category.ProgrammaticName ] = + getPropertyBox().AppendPage( category.UIName, HelpIdUrl::getHelpId( category.HelpURL ) ); + } + } + + + void OPropertyBrowserController::UpdateUI() + { + try + { + if ( !haveView() ) + // too early, will return later + return; + + // create our tab pages + impl_buildCategories_throw(); + // (and allow for pages to be actually unused) + std::set< sal_uInt16 > aUsedPages; + + // when building the UI below, remember which properties are actuating, + // to allow for an initial actuatingPropertyChanged call + std::vector< OUString > aActuatingProperties; + std::vector< Any > aActuatingPropertyValues; + + // ask the handlers to describe the property UI, and insert the resulting + // entries into our list boxes + for (auto const& property : m_aProperties) + { + OLineDescriptor aDescriptor; + describePropertyLine( property.second, aDescriptor ); + + bool bIsActuatingProperty = impl_isActuatingProperty_nothrow( property.second.Name ); + + SAL_WARN_IF( aDescriptor.Category.isEmpty(), "extensions.propctrlr", + "OPropertyBrowserController::UpdateUI: empty category provided for property '" + << property.second.Name << "'!"); + // finally insert this property control + sal_uInt16 nTargetPageId = impl_getPageIdForCategory_nothrow( aDescriptor.Category ); + if ( nTargetPageId == sal_uInt16(-1) ) + { + // this category does not yet exist. This is allowed, as an inspector model might be lazy, and not provide + // any category information of its own. In this case, we have a fallback ... + m_aPageIds[ aDescriptor.Category ] = + getPropertyBox().AppendPage( aDescriptor.Category, OString() ); + nTargetPageId = impl_getPageIdForCategory_nothrow( aDescriptor.Category ); + } + + getPropertyBox().InsertEntry( aDescriptor, nTargetPageId ); + aUsedPages.insert( nTargetPageId ); + + // if it's an actuating property, remember it + if ( bIsActuatingProperty ) + { + aActuatingProperties.push_back( property.second.Name ); + aActuatingPropertyValues.push_back( impl_getPropertyValue_throw( property.second.Name ) ); + } + } + + // update any dependencies for the actuating properties which we encountered + { + std::vector< Any >::const_iterator aPropertyValue = aActuatingPropertyValues.begin(); + for (auto const& actuatingProperty : aActuatingProperties) + { + impl_broadcastPropertyChange_nothrow( actuatingProperty, *aPropertyValue, *aPropertyValue, true ); + ++aPropertyValue; + } + } + + // remove any unused pages (which we did not encounter properties for) + HashString2Int16 aSurvivingPageIds; + for (auto const& pageId : m_aPageIds) + { + if ( aUsedPages.find( pageId.second ) == aUsedPages.end() ) + getPropertyBox().RemovePage( pageId.second ); + else + aSurvivingPageIds.insert(pageId); + } + m_aPageIds.swap( aSurvivingPageIds ); + + getPropertyBox().Show(); + + // activate the first page + if ( !m_aPageIds.empty() ) + { + Sequence< PropertyCategoryDescriptor > aCategories( m_xModel->describeCategories() ); + if ( aCategories.hasElements() ) + m_xPropView->activatePage( m_aPageIds[ aCategories[0].ProgrammaticName ] ); + else + // allowed: if we default-created the pages ... + m_xPropView->activatePage( m_aPageIds.begin()->second ); + } + + // activate the previously active page (if possible) + if ( !m_sLastValidPageSelection.isEmpty() ) + m_sPageSelection = m_sLastValidPageSelection; + selectPageFromViewData(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + + void OPropertyBrowserController::Clicked( const OUString& _rName, bool _bPrimary ) + { + try + { + // since the browse buttons do not get the focus when clicked with the mouse, + // we need to commit the changes in the current property field + getPropertyBox().CommitModified(); + + PropertyHandlerRepository::const_iterator handler = m_aPropertyHandlers.find( _rName ); + DBG_ASSERT( handler != m_aPropertyHandlers.end(), "OPropertyBrowserController::Clicked: a property without handler? This will crash!" ); + + ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer ); + + Any aData; + m_xInteractiveHandler = handler->second; + InteractiveSelectionResult eResult = + handler->second->onInteractivePropertySelection( _rName, _bPrimary, aData, + m_pUIRequestComposer->getUIForPropertyHandler( handler->second ) ); + + switch ( eResult ) + { + case InteractiveSelectionResult_Cancelled: + case InteractiveSelectionResult_Success: + // okay, nothing to do + break; + case InteractiveSelectionResult_ObtainedValue: + handler->second->setPropertyValue( _rName, aData ); + break; + case InteractiveSelectionResult_Pending: + // also okay, we expect that the handler has disabled the UI as necessary + break; + default: + OSL_FAIL( "OPropertyBrowserController::Clicked: unknown result value!" ); + break; + } + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + m_xInteractiveHandler = nullptr; + } + + + bool OPropertyBrowserController::hasPropertyByName( const OUString& _rName ) + { + for (auto const& property : m_aProperties) + if ( property.second.Name == _rName ) + return true; + return false; + } + + + void OPropertyBrowserController::Commit( const OUString& rName, const Any& _rValue ) + { + try + { + OUString sPlcHolder = PcrRes(RID_EMBED_IMAGE_PLACEHOLDER); + bool bIsPlaceHolderValue = false; + + if ( rName == PROPERTY_IMAGE_URL ) + { + // if the prop value is the PlaceHolder + // can ignore it + OUString sVal; + _rValue >>= sVal; + if ( sVal == sPlcHolder ) + bIsPlaceHolderValue = true; + } + m_sCommittingProperty = rName; + + bool bIsActuatingProperty = impl_isActuatingProperty_nothrow( rName ); + + Any aOldValue; + if ( bIsActuatingProperty ) + aOldValue = impl_getPropertyValue_throw( rName ); + + // do we have a dedicated handler for this property, which we can delegate some tasks to? + PropertyHandlerRef handler = impl_getHandlerForProperty_throw( rName ); + + + // set the value ( only if it's not a placeholder ) + if ( !bIsPlaceHolderValue ) + handler->setPropertyValue( rName, _rValue ); + + + // re-retrieve the value + Any aNormalizedValue = handler->getPropertyValue( rName ); + + // care for any inter-property dependencies + if ( bIsActuatingProperty ) + impl_broadcastPropertyChange_nothrow( rName, aNormalizedValue, aOldValue, false ); + + // and display it again. This ensures proper formatting + getPropertyBox().SetPropertyValue( rName, aNormalizedValue, false ); + } + catch(const PropertyVetoException& eVetoException) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xPropView->getPropertyBox().getWidget(), + VclMessageType::Info, VclButtonsType::Ok, + eVetoException.Message)); + xInfoBox->run(); + PropertyHandlerRef handler = impl_getHandlerForProperty_throw( rName ); + Any aNormalizedValue = handler->getPropertyValue( rName ); + getPropertyBox().SetPropertyValue( rName, aNormalizedValue, false ); + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION("extensions.propctrlr", ""); + } + + m_sCommittingProperty.clear(); + } + + + void OPropertyBrowserController::focusGained( const Reference< XPropertyControl >& Control ) + { + m_aControlObservers.notifyEach( &XPropertyControlObserver::focusGained, Control ); + } + + + void OPropertyBrowserController::valueChanged( const Reference< XPropertyControl >& Control ) + { + m_aControlObservers.notifyEach( &XPropertyControlObserver::valueChanged, Control ); + } + + + namespace + { + Reference< XPropertyHandler > lcl_createHandler( const Reference<XComponentContext>& _rContext, const Any& _rFactoryDescriptor ) + { + Reference< XPropertyHandler > xHandler; + + OUString sServiceName; + Reference< XSingleServiceFactory > xServiceFac; + Reference< XSingleComponentFactory > xComponentFac; + + if ( _rFactoryDescriptor >>= sServiceName ) + xHandler.set( _rContext->getServiceManager()->createInstanceWithContext( sServiceName, _rContext ), UNO_QUERY ); + else if ( _rFactoryDescriptor >>= xServiceFac ) + xHandler.set(xServiceFac->createInstance(), css::uno::UNO_QUERY); + else if ( _rFactoryDescriptor >>= xComponentFac ) + xHandler.set(xComponentFac->createInstanceWithContext( _rContext ), css::uno::UNO_QUERY); + OSL_ENSURE(xHandler.is(),"lcl_createHandler: Can not create handler"); + return xHandler; + } + } + + + void OPropertyBrowserController::getPropertyHandlers( const InterfaceArray& _rObjects, PropertyHandlerArray& _rHandlers ) + { + _rHandlers.resize( 0 ); + if ( _rObjects.empty() ) + return; + + Sequence< Any > aHandlerFactories; + if ( m_xModel.is() ) + aHandlerFactories = m_xModel->getHandlerFactories(); + + for ( auto const & handlerFactory : std::as_const(aHandlerFactories) ) + { + if ( _rObjects.size() == 1 ) + { // we're inspecting only one object -> one handler + Reference< XPropertyHandler > xHandler( lcl_createHandler( m_xContext, handlerFactory ) ); + if ( xHandler.is() ) + { + xHandler->inspect( _rObjects[0] ); + _rHandlers.push_back( xHandler ); + } + } + else + { + // create a single handler for every single object + std::vector< Reference< XPropertyHandler > > aSingleHandlers( _rObjects.size() ); + std::vector< Reference< XPropertyHandler > >::iterator pHandler = aSingleHandlers.begin(); + + for (auto const& elem : _rObjects) + { + *pHandler = lcl_createHandler( m_xContext, handlerFactory ); + if ( pHandler->is() ) + { + (*pHandler)->inspect(elem); + ++pHandler; + } + } + aSingleHandlers.resize( pHandler - aSingleHandlers.begin() ); + + // then create a handler which composes information out of those single handlers + if ( !aSingleHandlers.empty() ) + _rHandlers.push_back( new PropertyComposer( std::move(aSingleHandlers) ) ); + } + } + + // note that the handlers will not be used by our caller, if they indicate that there are no + // properties they feel responsible for + } + + + bool OPropertyBrowserController::impl_findObjectProperty_nothrow( const OUString& _rName, OrderedPropertyMap::const_iterator* _pProperty ) + { + OrderedPropertyMap::const_iterator search = std::find_if(m_aProperties.begin(), m_aProperties.end(), + [&_rName](const OrderedPropertyMap::value_type& rEntry) { return rEntry.second.Name == _rName; }); + if ( _pProperty ) + *_pProperty = search; + return ( search != m_aProperties.end() ); + } + + + void OPropertyBrowserController::rebuildPropertyUI( const OUString& _rPropertyName ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !haveView() ) + throw RuntimeException(); + + OrderedPropertyMap::const_iterator propertyPos; + if ( !impl_findObjectProperty_nothrow( _rPropertyName, &propertyPos ) ) + return; + + OLineDescriptor aDescriptor; + try + { + describePropertyLine( propertyPos->second, aDescriptor ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "OPropertyBrowserController::rebuildPropertyUI" ); + } + + getPropertyBox().ChangeEntry( aDescriptor ); + } + + + void OPropertyBrowserController::enablePropertyUI( const OUString& _rPropertyName, sal_Bool _bEnable ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !haveView() ) + throw RuntimeException(); + + if ( !impl_findObjectProperty_nothrow( _rPropertyName ) ) + return; + + getPropertyBox().EnablePropertyLine( _rPropertyName, _bEnable ); + } + + + void OPropertyBrowserController::enablePropertyUIElements( const OUString& _rPropertyName, sal_Int16 _nElements, sal_Bool _bEnable ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !haveView() ) + throw RuntimeException(); + + if ( !impl_findObjectProperty_nothrow( _rPropertyName ) ) + return; + + getPropertyBox().EnablePropertyControls( _rPropertyName, _nElements, _bEnable ); + } + + + void OPropertyBrowserController::showPropertyUI( const OUString& _rPropertyName ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !haveView() ) + throw RuntimeException(); + + // look up the property in our object properties + OrderedPropertyMap::const_iterator propertyPos; + if ( !impl_findObjectProperty_nothrow( _rPropertyName, &propertyPos ) ) + return; + + if ( getPropertyBox().GetPropertyPos( _rPropertyName ) != EDITOR_LIST_ENTRY_NOTFOUND ) + { + rebuildPropertyUI( _rPropertyName ); + return; + } + + OLineDescriptor aDescriptor; + describePropertyLine( propertyPos->second, aDescriptor ); + + // look for the position to insert the property + + // side note: The methods GetPropertyPos and InsertEntry of the OPropertyEditor work + // only on the current page. This implies that it's impossible to use this method here + // to show property lines which are *not* on the current page. + // This is sufficient for now, but should be changed in the future. + + // by definition, the properties in m_aProperties are in the order in which they appear in the UI + // So all we need is a predecessor of pProperty in m_aProperties + sal_uInt16 nUIPos = EDITOR_LIST_ENTRY_NOTFOUND; + do + { + if ( propertyPos != m_aProperties.begin() ) + --propertyPos; + nUIPos = getPropertyBox().GetPropertyPos( propertyPos->second.Name ); + } + while ( ( nUIPos == EDITOR_LIST_ENTRY_NOTFOUND ) && ( propertyPos != m_aProperties.begin() ) ); + + if ( nUIPos == EDITOR_LIST_ENTRY_NOTFOUND ) + // insert at the very top + nUIPos = 0; + else + // insert right after the predecessor we found + ++nUIPos; + + getPropertyBox().InsertEntry( + aDescriptor, impl_getPageIdForCategory_nothrow( aDescriptor.Category ), nUIPos ); + } + + + void OPropertyBrowserController::hidePropertyUI( const OUString& _rPropertyName ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !haveView() ) + throw RuntimeException(); + + if ( !impl_findObjectProperty_nothrow( _rPropertyName ) ) + return; + + getPropertyBox().RemoveEntry( _rPropertyName ); + } + + + void OPropertyBrowserController::showCategory( const OUString& rCategory, sal_Bool bShow ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !haveView() ) + throw RuntimeException(); + + sal_uInt16 nPageId = impl_getPageIdForCategory_nothrow( rCategory ); + OSL_ENSURE( nPageId != sal_uInt16(-1), "OPropertyBrowserController::showCategory: invalid category!" ); + + getPropertyBox().ShowPropertyPage( nPageId, bShow ); + } + + + Reference< XPropertyControl > SAL_CALL OPropertyBrowserController::getPropertyControl( const OUString& _rPropertyName ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !haveView() ) + throw RuntimeException(); + + Reference< XPropertyControl > xControl( getPropertyBox().GetPropertyControl( _rPropertyName ) ); + return xControl; + } + + + void SAL_CALL OPropertyBrowserController::registerControlObserver( const Reference< XPropertyControlObserver >& Observer ) + { + m_aControlObservers.addInterface( Observer ); + } + + + void SAL_CALL OPropertyBrowserController::revokeControlObserver( const Reference< XPropertyControlObserver >& Observer ) + { + m_aControlObservers.removeInterface( Observer ); + } + + + void SAL_CALL OPropertyBrowserController::setHelpSectionText( const OUString& _rHelpText ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( !haveView() ) + throw DisposedException(); + + if ( !getPropertyBox().HasHelpSection() ) + throw NoSupportException(); + + getPropertyBox().SetHelpText( _rHelpText ); + } + + + void OPropertyBrowserController::impl_broadcastPropertyChange_nothrow( const OUString& _rPropertyName, const Any& _rNewValue, const Any& _rOldValue, bool _bFirstTimeInit ) const + { + // are there one or more handlers which are interested in the actuation? + std::pair< PropertyHandlerMultiRepository::const_iterator, PropertyHandlerMultiRepository::const_iterator > aInterestedHandlers = + m_aDependencyHandlers.equal_range( _rPropertyName ); + if ( aInterestedHandlers.first == aInterestedHandlers.second ) + // none of our handlers is interested in this + return; + + ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer ); + try + { + // collect the responses from all interested handlers + PropertyHandlerMultiRepository::const_iterator handler = aInterestedHandlers.first; + while ( handler != aInterestedHandlers.second ) + { + handler->second->actuatingPropertyChanged( _rPropertyName, _rNewValue, _rOldValue, + m_pUIRequestComposer->getUIForPropertyHandler( handler->second ), + _bFirstTimeInit ); + ++handler; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_OPropertyBrowserController_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::OPropertyBrowserController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/propcontroller.hxx b/extensions/source/propctrlr/propcontroller.hxx new file mode 100644 index 000000000..a56a83979 --- /dev/null +++ b/extensions/source/propctrlr/propcontroller.hxx @@ -0,0 +1,368 @@ +/* -*- 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 "composeduiupdate.hxx" +#include "proplinelistener.hxx" +#include "propcontrolobserver.hxx" +#include "browserview.hxx" + +#include <com/sun/star/awt/XFocusListener.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XEventListener.hpp> +#include <com/sun/star/awt/XLayoutConstrains.hpp> +#include <com/sun/star/inspection/XPropertyControlFactory.hpp> +#include <com/sun/star/inspection/XObjectInspector.hpp> +#include <com/sun/star/inspection/XObjectInspectorUI.hpp> +#include <com/sun/star/inspection/XPropertyHandler.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <comphelper/interfacecontainer2.hxx> +#include <comphelper/uno3.hxx> +#include <cppuhelper/implbase.hxx> +#include <comphelper/broadcasthelper.hxx> +#include <vcl/weld.hxx> + +#include <map> +#include <memory> +#include <unordered_map> +#include <vector> + +namespace pcr +{ + class OPropertyEditor; + struct OLineDescriptor; + + typedef ::cppu::WeakImplHelper < css::lang::XServiceInfo + , css::awt::XFocusListener + , css::awt::XLayoutConstrains + , css::beans::XPropertyChangeListener + , css::inspection::XPropertyControlFactory + , css::inspection::XObjectInspector + , css::lang::XInitialization + > OPropertyBrowserController_Base; + + class OPropertyBrowserController + :public ::comphelper::OMutexAndBroadcastHelper + ,public OPropertyBrowserController_Base + ,public css::inspection::XObjectInspectorUI + // that's intentionally *not* part of the OPropertyBrowserController_Base + // We do not want this to be available in queryInterface, getTypes, and the like. + ,public IPropertyLineListener + ,public IPropertyControlObserver + ,public IPropertyExistenceCheck + { + private: + typedef std::multimap< sal_Int32, css::beans::Property > OrderedPropertyMap; + typedef std::vector< css::uno::Reference< css::uno::XInterface > > + InterfaceArray; + + protected: + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + private: + css::uno::Reference< css::frame::XFrame > m_xFrame; + css::uno::Reference< css::awt::XWindow > m_xView; + + ::comphelper::OInterfaceContainerHelper2 m_aDisposeListeners; + ::comphelper::OInterfaceContainerHelper2 m_aControlObservers; + // meta data about the properties + std::unique_ptr<weld::Builder> m_xBuilder; + std::unique_ptr<OPropertyBrowserView> m_xPropView; + + OUString m_sPageSelection; + OUString m_sLastValidPageSelection; + + typedef css::uno::Reference< css::inspection::XPropertyHandler > + PropertyHandlerRef; + typedef std::vector< PropertyHandlerRef > PropertyHandlerArray; + typedef std::unordered_map< OUString, PropertyHandlerRef > + PropertyHandlerRepository; + typedef std::unordered_multimap< OUString, PropertyHandlerRef > + PropertyHandlerMultiRepository; + PropertyHandlerRepository m_aPropertyHandlers; + PropertyHandlerMultiRepository m_aDependencyHandlers; + PropertyHandlerRef m_xInteractiveHandler; + + std::unique_ptr< ComposedPropertyUIUpdate > m_pUIRequestComposer; + + /// our InspectorModel + css::uno::Reference< css::inspection::XObjectInspectorModel > + m_xModel; + /// the object(s) we're currently inspecting + InterfaceArray m_aInspectedObjects; + /// the properties of the currently inspected object(s) + OrderedPropertyMap m_aProperties; + /// the property we're just committing + OUString m_sCommittingProperty; + + typedef std::unordered_map< OUString, sal_uInt16 > HashString2Int16; + HashString2Int16 m_aPageIds; + + bool m_bContainerFocusListening; + bool m_bSuspendingPropertyHandlers; + bool m_bConstructed; + bool m_bBindingIntrospectee; + + protected: + DECLARE_XINTERFACE() + + // 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; + + // XController + virtual void SAL_CALL attachFrame( const css::uno::Reference< css::frame::XFrame >& xFrame ) override; + virtual sal_Bool SAL_CALL attachModel( const css::uno::Reference< css::frame::XModel >& xModel ) override; + virtual sal_Bool SAL_CALL suspend( sal_Bool bSuspend ) override; + virtual css::uno::Any SAL_CALL getViewData( ) override; + virtual void SAL_CALL restoreViewData( const css::uno::Any& Data ) override; + virtual css::uno::Reference< css::frame::XModel > SAL_CALL getModel( ) override; + virtual css::uno::Reference< css::frame::XFrame > SAL_CALL getFrame( ) override; + + // XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + + // XFocusListener + virtual void SAL_CALL focusGained( const css::awt::FocusEvent& _rSource ) override; + virtual void SAL_CALL focusLost( const css::awt::FocusEvent& _rSource ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // XLayoutConstrains + virtual css::awt::Size SAL_CALL getMinimumSize( ) override; + virtual css::awt::Size SAL_CALL getPreferredSize( ) override; + virtual css::awt::Size SAL_CALL calcAdjustedSize( const css::awt::Size& rNewSize ) override; + + // XPropertyChangeListener + virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& _rEvent ) override; + + /** XPropertyControlFactory + */ + virtual css::uno::Reference< css::inspection::XPropertyControl > SAL_CALL createPropertyControl( ::sal_Int16 ControlType, sal_Bool CreateReadOnly ) override; + + public: + explicit OPropertyBrowserController( + const css::uno::Reference< css::uno::XComponentContext >& _rxContext); + + protected: + virtual ~OPropertyBrowserController() override; + + // IPropertyLineListener + virtual void Clicked( const OUString& _rName, bool _bPrimary ) override; + virtual void Commit( const OUString& _rName, const css::uno::Any& _rVal ) override; + + // IPropertyControlObserver + virtual void focusGained( const css::uno::Reference< css::inspection::XPropertyControl >& Control ) override; + virtual void valueChanged( const css::uno::Reference< css::inspection::XPropertyControl >& Control ) override; + + // IPropertyExistenceCheck + virtual bool hasPropertyByName( const OUString& _rName ) override; + + // XObjectInspectorUI + virtual void SAL_CALL enablePropertyUI( const OUString& _rPropertyName, sal_Bool _bEnable ) override; + virtual void SAL_CALL enablePropertyUIElements( const OUString& _rPropertyName, ::sal_Int16 _nElements, sal_Bool _bEnable ) override; + virtual void SAL_CALL rebuildPropertyUI( const OUString& _rPropertyName ) override; + virtual void SAL_CALL showPropertyUI( const OUString& _rPropertyName ) override; + virtual void SAL_CALL hidePropertyUI( const OUString& _rPropertyName ) override; + virtual void SAL_CALL showCategory( const OUString& _rCategory, sal_Bool _bShow ) override; + virtual css::uno::Reference< css::inspection::XPropertyControl > SAL_CALL getPropertyControl( const OUString& _rPropertyName ) override; + virtual void SAL_CALL registerControlObserver( const css::uno::Reference< css::inspection::XPropertyControlObserver >& Observer ) override; + virtual void SAL_CALL revokeControlObserver( const css::uno::Reference< css::inspection::XPropertyControlObserver >& Observer ) override; + virtual void SAL_CALL setHelpSectionText( const OUString& HelpText ) override; + + // XObjectInspector + virtual css::uno::Reference< css::inspection::XObjectInspectorModel > SAL_CALL getInspectorModel() override; + virtual void SAL_CALL setInspectorModel( const css::uno::Reference< css::inspection::XObjectInspectorModel >& _inspectormodel ) override; + virtual css::uno::Reference< css::inspection::XObjectInspectorUI > SAL_CALL getInspectorUI() override; + virtual void SAL_CALL inspect( const css::uno::Sequence< css::uno::Reference< css::uno::XInterface > >& Objects ) override; + + // XDispatchProvider + virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch( const css::util::URL& URL, const OUString& TargetFrameName, ::sal_Int32 SearchFlags ) override; + virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& Requests ) override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + private: + void UpdateUI(); + + void startContainerWindowListening(); + void stopContainerWindowListening(); + + // stop the inspection + void stopInspection( bool _bCommitModified ); + + bool haveView() const { return bool(m_xPropView); } + OPropertyEditor& getPropertyBox() { return m_xPropView->getPropertyBox(); } + + // does the inspection of the objects as indicated by our model + void doInspection(); + + // bind the browser to m_xIntrospecteeAsProperty + void impl_rebindToInspectee_nothrow( InterfaceArray&& _rObjects ); + + /** retrieves special property handlers for our introspectee + */ + void getPropertyHandlers( const InterfaceArray& _rObjects, PropertyHandlerArray& _rHandlers ); + + /** called when a property changed, to broadcast any handlers which might have + registered for this property + + @param _bFirstTimeInit + if set to <FALSE/>, this is a real change in the property value, not just a call + for purposes of initialization. + */ + void impl_broadcastPropertyChange_nothrow( const OUString& _rPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, bool _bFirstTimeInit ) const; + + /** determines whether the given property is an actuating property, that is, at least one + handler expressed interest in changes to this property's value. + */ + bool impl_isActuatingProperty_nothrow( const OUString& _rPropertyName ) const + { + return ( m_aDependencyHandlers.find( _rPropertyName ) != m_aDependencyHandlers.end() ); + } + + /** retrieves the value of the given property, by asking the appropriate XPropertyHandler + @param _rPropertyName + the name whose handler is to be obtained. Must be the name of a property + for which a handler is registered. + @throws + RuntimeException if there is no handler for the given property + @return + the value of this property + */ + css::uno::Any + impl_getPropertyValue_throw( const OUString& _rPropertyName ); + + /// calls XPropertyHandler::suspend for all our property handlers + bool suspendPropertyHandlers_nothrow( bool _bSuspend ); + + /// suspends the complete inspector + bool suspendAll_nothrow(); + + /** selects a page according to our current view data + */ + void selectPageFromViewData(); + + /** updates our view data from the currently active page + */ + void updateViewDataFromActivePage(); + + /// describes the UI for the given property + void describePropertyLine( const css::beans::Property& _rPropertyName, OLineDescriptor& _rDescriptor ); + + /** retrieves the position of the property given by name in m_aProperties + @return + <TRUE/> if and only if the property could be found. In this case, <arg>_pProperty</arg> (if + not <NULL/> contains the iterator pointing to this property. + */ + bool impl_findObjectProperty_nothrow( const OUString& _rName, OrderedPropertyMap::const_iterator* _pProperty = nullptr ); + + void Construct(const css::uno::Reference<css::awt::XWindow>& rContainerWindow, std::unique_ptr<weld::Builder> xBuilder); + + /** retrieves the property handler for a given property name + @param _rPropertyName + the name whose handler is to be obtained. Must be the name of a property + for which a handler is registered. + @throws + RuntimeException if there is no handler for the given property + @return + the handler which is responsible for the given property + */ + PropertyHandlerRef const & + impl_getHandlerForProperty_throw( const OUString& _rPropertyName ) const; + + /** determines whether we have a handler for the given property + @param _rPropertyName + the name of the property for which the existence of a handler should be checked + */ + bool + impl_hasPropertyHandlerFor_nothrow( const OUString& _rPropertyName ) const; + + /** builds up m_aPageIds from InspectorModel::describeCategories, and insert all the + respective tab pages into our view + @precond + m_aPageIds is empty + @throws css::uno::RuntimeException + if one of the callees of this method throws this exception + */ + void + impl_buildCategories_throw(); + + /** retrieves the id of the tab page which represents a given category. + @param _rCategoryName + the programmatic name of a category. + @return + the id of the tab page, or <code>(sal_uInt16)-1</code> if there + is no tab page for the given category + */ + sal_uInt16 + impl_getPageIdForCategory_nothrow( const OUString& _rCategoryName ) const; + + /** adds or removes ourself as XEventListener to/from all our inspectees + */ + void impl_toggleInspecteeListening_nothrow( bool _bOn ); + + /** binds the instance to a new model + */ + void impl_bindToNewModel_nothrow( const css::uno::Reference< css::inspection::XObjectInspectorModel >& _rxInspectorModel ); + + /** initializes our view, as indicated by the model's view-relevant properties + + It's allowed to call this method when no model exists, yet. In this case, nothing + happens. + */ + void impl_initializeView_nothrow(); + + /** determines whether the view should be readonly. + + Effectively, this means that the method simply checks the IsReadOnly attribute of the model. + If there is no model, <FALSE/> is returned. + + @throws css::uno::RuntimeException + in case asking the model for its IsReadOnly attribute throws a css::uno::RuntimeException + itself. + */ + bool impl_isReadOnlyModel_throw() const; + + /** starts or stops listening at the model + */ + void impl_startOrStopModelListening_nothrow( bool _bDoListen ) const; + + private: + DECL_LINK(OnPageActivation, LinkParamNone*, void); + + private: + // constructors + void createWithModel( const css::uno::Reference< css::inspection::XObjectInspectorModel >& _rxModel ); + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/propcontrolobserver.hxx b/extensions/source/propctrlr/propcontrolobserver.hxx new file mode 100644 index 000000000..93e11053d --- /dev/null +++ b/extensions/source/propctrlr/propcontrolobserver.hxx @@ -0,0 +1,46 @@ +/* -*- 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/inspection/XPropertyControl.hpp> + + +namespace pcr +{ + + + //= IPropertyControlObserver + + /** non-UNO version of the XPropertyControlObserver + */ + class IPropertyControlObserver + { + public: + virtual void focusGained( const css::uno::Reference< css::inspection::XPropertyControl >& Control ) = 0; + virtual void valueChanged( const css::uno::Reference< css::inspection::XPropertyControl >& Control ) = 0; + + protected: + ~IPropertyControlObserver() {} + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/propertycomposer.cxx b/extensions/source/propctrlr/propertycomposer.cxx new file mode 100644 index 000000000..d77280d9f --- /dev/null +++ b/extensions/source/propctrlr/propertycomposer.cxx @@ -0,0 +1,484 @@ +/* -*- 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 "propertycomposer.hxx" + +#include <com/sun/star/lang/NullPointerException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <comphelper/sequence.hxx> +#include <osl/diagnose.h> +#include <tools/diagnose_ex.h> + +#include <algorithm> +#include <iterator> + + +namespace pcr +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::inspection; + + + //= helper + + namespace + { + + struct SetPropertyValue + { + OUString sPropertyName; + const Any& rValue; + SetPropertyValue( const OUString& _rPropertyName, const Any& _rValue ) : sPropertyName( _rPropertyName ), rValue( _rValue ) { } + void operator()( const Reference< XPropertyHandler >& _rHandler ) + { + _rHandler->setPropertyValue( sPropertyName, rValue ); + } + }; + + + template < class BagType > + void putIntoBag( const Sequence< typename BagType::value_type >& _rArray, BagType& /* [out] */ _rBag ) + { + std::copy( _rArray.begin(), _rArray.end(), + std::insert_iterator< BagType >( _rBag, _rBag.begin() ) ); + } + + + template < class BagType > + void copyBagToArray( const BagType& /* [out] */ _rBag, Sequence< typename BagType::value_type >& _rArray ) + { + _rArray.realloc( _rBag.size() ); + std::copy( _rBag.begin(), _rBag.end(), _rArray.getArray() ); + } + } + + + //= PropertyComposer + + + // TODO: there are various places where we determine the first handler in our array which + // supports a given property id. This is, at the moment, done with searching all handlers, + // which is O( n * k ) at worst (n being the number of handlers, k being the maximum number + // of supported properties per handler). Shouldn't we cache this? So that it is O( log k )? + + + PropertyComposer::PropertyComposer( std::vector< Reference< XPropertyHandler > >&& _rSlaveHandlers ) + :PropertyComposer_Base ( m_aMutex ) + ,m_aSlaveHandlers ( std::move(_rSlaveHandlers) ) + ,m_aPropertyListeners ( m_aMutex ) + ,m_bSupportedPropertiesAreKnown ( false ) + { + if ( m_aSlaveHandlers.empty() ) + throw IllegalArgumentException(); + + osl_atomic_increment( &m_refCount ); + { + Reference< XPropertyChangeListener > xMeMyselfAndI( this ); + for (auto const& slaveHandler : m_aSlaveHandlers) + { + if ( !slaveHandler.is() ) + throw NullPointerException(); + slaveHandler->addPropertyChangeListener( xMeMyselfAndI ); + } + } + osl_atomic_decrement( &m_refCount ); + } + + + void SAL_CALL PropertyComposer::inspect( const Reference< XInterface >& _rxIntrospectee ) + { + MethodGuard aGuard( *this ); + + for (auto const& slaveHandler : m_aSlaveHandlers) + { + slaveHandler->inspect( _rxIntrospectee ); + } + } + + + Any SAL_CALL PropertyComposer::getPropertyValue( const OUString& _rPropertyName ) + { + MethodGuard aGuard( *this ); + return m_aSlaveHandlers[0]->getPropertyValue( _rPropertyName ); + } + + + void SAL_CALL PropertyComposer::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue ) + { + MethodGuard aGuard( *this ); + std::for_each( m_aSlaveHandlers.begin(), m_aSlaveHandlers.end(), SetPropertyValue( _rPropertyName, _rValue ) ); + } + + + Any SAL_CALL PropertyComposer::convertToPropertyValue( const OUString& _rPropertyName, const Any& _rControlValue ) + { + MethodGuard aGuard( *this ); + return m_aSlaveHandlers[0]->convertToPropertyValue( _rPropertyName, _rControlValue ); + } + + + Any SAL_CALL PropertyComposer::convertToControlValue( const OUString& _rPropertyName, const Any& _rPropertyValue, const Type& _rControlValueType ) + { + MethodGuard aGuard( *this ); + return m_aSlaveHandlers[0]->convertToControlValue( _rPropertyName, _rPropertyValue, _rControlValueType ); + } + + + PropertyState SAL_CALL PropertyComposer::getPropertyState( const OUString& _rPropertyName ) + { + MethodGuard aGuard( *this ); + + // assume DIRECT for the moment. This will stay this way if *all* slaves + // tell the property has DIRECT state, and if *all* values equal + PropertyState eState = PropertyState_DIRECT_VALUE; + + // check the master state + Reference< XPropertyHandler > xPrimary( *m_aSlaveHandlers.begin() ); + Any aPrimaryValue = xPrimary->getPropertyValue( _rPropertyName ); + eState = xPrimary->getPropertyState( _rPropertyName ); + + // loop through the secondary sets + PropertyState eSecondaryState = PropertyState_DIRECT_VALUE; + for ( HandlerArray::const_iterator loop = m_aSlaveHandlers.begin() + 1; + loop != m_aSlaveHandlers.end(); + ++loop + ) + { + // the secondary state + eSecondaryState = (*loop)->getPropertyState( _rPropertyName ); + + // the secondary value + Any aSecondaryValue( (*loop)->getPropertyValue( _rPropertyName ) ); + + if ( ( PropertyState_AMBIGUOUS_VALUE == eSecondaryState ) // secondary is ambiguous + || ( aPrimaryValue != aSecondaryValue ) // unequal values + ) + { + eState = PropertyState_AMBIGUOUS_VALUE; + break; + } + } + + return eState; + } + + + void SAL_CALL PropertyComposer::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener ) + { + MethodGuard aGuard( *this ); + m_aPropertyListeners.addInterface( _rxListener ); + } + + + void SAL_CALL PropertyComposer::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener ) + { + MethodGuard aGuard( *this ); + m_aPropertyListeners.removeInterface( _rxListener ); + } + + + Sequence< Property > SAL_CALL PropertyComposer::getSupportedProperties() + { + MethodGuard aGuard( *this ); + + if ( !m_bSupportedPropertiesAreKnown ) + { + // we support a property if and only if all of our slaves support it + + // initially, use all the properties of an arbitrary handler (we take the first one) + putIntoBag( (*m_aSlaveHandlers.begin())->getSupportedProperties(), m_aSupportedProperties ); + + // now intersect with the properties of *all* other handlers + for ( HandlerArray::const_iterator loop = m_aSlaveHandlers.begin() + 1; + loop != m_aSlaveHandlers.end(); + ++loop + ) + { + // the properties supported by the current handler + PropertyBag aThisRound; + putIntoBag( (*loop)->getSupportedProperties(), aThisRound ); + + // the intersection of those properties with all we already have + PropertyBag aIntersection; + std::set_intersection( aThisRound.begin(), aThisRound.end(), m_aSupportedProperties.begin(), m_aSupportedProperties.end(), + std::insert_iterator< PropertyBag >( aIntersection, aIntersection.begin() ), PropertyLessByName() ); + + m_aSupportedProperties.swap( aIntersection ); + if ( m_aSupportedProperties.empty() ) + break; + } + + // remove those properties which are not composable + for ( PropertyBag::iterator check = m_aSupportedProperties.begin(); + check != m_aSupportedProperties.end(); + ) + { + bool bIsComposable = isComposable( check->Name ); + if ( !bIsComposable ) + { + check = m_aSupportedProperties.erase( check ); + } + else + ++check; + } + + m_bSupportedPropertiesAreKnown = true; + } + + return comphelper::containerToSequence( m_aSupportedProperties ); + } + + + static void uniteStringArrays( const PropertyComposer::HandlerArray& _rHandlers, Sequence< OUString > (SAL_CALL XPropertyHandler::*pGetter)( ), + Sequence< OUString >& /* [out] */ _rUnion ) + { + std::set< OUString > aUnitedBag; + + Sequence< OUString > aThisRound; + for (auto const& handler : _rHandlers) + { + aThisRound = (handler.get()->*pGetter)(); + putIntoBag( aThisRound, aUnitedBag ); + } + + copyBagToArray( aUnitedBag, _rUnion ); + } + + + Sequence< OUString > SAL_CALL PropertyComposer::getSupersededProperties( ) + { + MethodGuard aGuard( *this ); + + // we supersede those properties which are superseded by at least one of our slaves + Sequence< OUString > aSuperseded; + uniteStringArrays( m_aSlaveHandlers, &XPropertyHandler::getSupersededProperties, aSuperseded ); + return aSuperseded; + } + + + Sequence< OUString > SAL_CALL PropertyComposer::getActuatingProperties( ) + { + MethodGuard aGuard( *this ); + + // we're interested in those properties which at least one handler wants to have + Sequence< OUString > aActuating; + uniteStringArrays( m_aSlaveHandlers, &XPropertyHandler::getActuatingProperties, aActuating ); + return aActuating; + } + + + LineDescriptor SAL_CALL PropertyComposer::describePropertyLine( const OUString& _rPropertyName, + const Reference< XPropertyControlFactory >& _rxControlFactory ) + { + MethodGuard aGuard( *this ); + return m_aSlaveHandlers[0]->describePropertyLine( _rPropertyName, _rxControlFactory ); + } + + + sal_Bool SAL_CALL PropertyComposer::isComposable( const OUString& _rPropertyName ) + { + MethodGuard aGuard( *this ); + return m_aSlaveHandlers[0]->isComposable( _rPropertyName ); + } + + + InteractiveSelectionResult SAL_CALL PropertyComposer::onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, Any& _rData, const Reference< XObjectInspectorUI >& _rxInspectorUI ) + { + if ( !_rxInspectorUI.is() ) + throw NullPointerException(); + + MethodGuard aGuard( *this ); + + impl_ensureUIRequestComposer( _rxInspectorUI ); + ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer ); + + // ask the first of the handlers + InteractiveSelectionResult eResult = (*m_aSlaveHandlers.begin())->onInteractivePropertySelection( + _rPropertyName, + _bPrimary, + _rData, + m_pUIRequestComposer->getUIForPropertyHandler( *m_aSlaveHandlers.begin() ) + ); + + switch ( eResult ) + { + case InteractiveSelectionResult_Cancelled: + // fine + break; + + case InteractiveSelectionResult_Success: + case InteractiveSelectionResult_Pending: + OSL_FAIL( "PropertyComposer::onInteractivePropertySelection: no chance to forward the new value to the other handlers!" ); + // This means that we cannot know the new property value, which either has already been set + // at the first component ("Success"), or will be set later on once the asynchronous input + // is finished ("Pending"). So, we also cannot forward this new property value to the other + // handlers. + // We would need to be a listener at the property at the first component, but even this wouldn't + // be sufficient, since the property handler is free to change *any* property during a dedicated + // property UI. + eResult = InteractiveSelectionResult_Cancelled; + break; + + case InteractiveSelectionResult_ObtainedValue: + // OK. Our own caller will pass this as setPropertyValue, and we will then pass it to + // all slave handlers + break; + + default: + OSL_FAIL( "OPropertyBrowserController::onInteractivePropertySelection: unknown result value!" ); + break; + } + + return eResult; + } + + + void PropertyComposer::impl_ensureUIRequestComposer( const Reference< XObjectInspectorUI >& _rxInspectorUI ) + { + OSL_ENSURE(!m_pUIRequestComposer + || m_pUIRequestComposer->getDelegatorUI().get() == _rxInspectorUI.get(), + "PropertyComposer::impl_ensureUIRequestComposer: somebody's changing the horse " + "in the mid of the race!"); + + if (!m_pUIRequestComposer) + m_pUIRequestComposer.reset( new ComposedPropertyUIUpdate( _rxInspectorUI, this ) ); + } + + + void SAL_CALL PropertyComposer::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& _rOldValue, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) + { + if ( !_rxInspectorUI.is() ) + throw NullPointerException(); + + MethodGuard aGuard( *this ); + + impl_ensureUIRequestComposer( _rxInspectorUI ); + ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer ); + + // ask all handlers which expressed interest in this particular property, and "compose" their + // commands for the UIUpdater + for (auto const& slaveHandler : m_aSlaveHandlers) + { + // TODO: make this cheaper (cache it?) + const StlSyntaxSequence< OUString > aThisHandlersActuatingProps( slaveHandler->getActuatingProperties() ); + for (const auto & aThisHandlersActuatingProp : aThisHandlersActuatingProps) + { + if ( aThisHandlersActuatingProp == _rActuatingPropertyName ) + { + slaveHandler->actuatingPropertyChanged( _rActuatingPropertyName, _rNewValue, _rOldValue, + m_pUIRequestComposer->getUIForPropertyHandler(slaveHandler), + _bFirstTimeInit ); + break; + } + } + } + } + + + IMPLEMENT_FORWARD_XCOMPONENT( PropertyComposer, PropertyComposer_Base ) + + + void SAL_CALL PropertyComposer::disposing() + { + MethodGuard aGuard( *this ); + + // dispose our slave handlers + for (auto const& slaveHandler : m_aSlaveHandlers) + { + slaveHandler->removePropertyChangeListener( this ); + slaveHandler->dispose(); + } + + clearContainer( m_aSlaveHandlers ); + + if (m_pUIRequestComposer) + m_pUIRequestComposer->dispose(); + m_pUIRequestComposer.reset(); + } + + + void SAL_CALL PropertyComposer::propertyChange( const PropertyChangeEvent& evt ) + { + if ( !impl_isSupportedProperty_nothrow( evt.PropertyName ) ) + // A slave handler might fire events for more properties than we support. Ignore those. + return; + + PropertyChangeEvent aTranslatedEvent( evt ); + try + { + aTranslatedEvent.NewValue = getPropertyValue( evt.PropertyName ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + m_aPropertyListeners.notifyEach( &XPropertyChangeListener::propertyChange, aTranslatedEvent ); + } + + + void SAL_CALL PropertyComposer::disposing( const EventObject& Source ) + { + MethodGuard aGuard( *this ); + m_aPropertyListeners.disposeAndClear( Source ); + } + + + sal_Bool SAL_CALL PropertyComposer::suspend( sal_Bool _bSuspend ) + { + MethodGuard aGuard( *this ); + for ( PropertyComposer::HandlerArray::const_iterator loop = m_aSlaveHandlers.begin(); + loop != m_aSlaveHandlers.end(); + ++loop + ) + { + if ( !(*loop)->suspend( _bSuspend ) ) + { + if ( _bSuspend && ( loop != m_aSlaveHandlers.begin() ) ) + { + // if we tried to suspend, but one of the slave handlers vetoed, + // re-activate the handlers which actually did *not* veto + // the suspension + do + { + --loop; + (*loop)->suspend( false ); + } + while ( loop != m_aSlaveHandlers.begin() ); + } + return false; + } + } + return true; + } + + + bool PropertyComposer::hasPropertyByName( const OUString& _rName ) + { + return impl_isSupportedProperty_nothrow( _rName ); + } + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/propertycomposer.hxx b/extensions/source/propctrlr/propertycomposer.hxx new file mode 100644 index 000000000..5bcc58e23 --- /dev/null +++ b/extensions/source/propctrlr/propertycomposer.hxx @@ -0,0 +1,142 @@ +/* -*- 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 "pcrcommon.hxx" +#include "composeduiupdate.hxx" +#include "formbrowsertools.hxx" + +#include <com/sun/star/inspection/XPropertyHandler.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/basemutex.hxx> + +#include <memory> +#include <vector> +#include <set> + + +namespace pcr +{ + + + //= PropertyComposer + + typedef ::cppu::WeakComponentImplHelper < css::inspection::XPropertyHandler + , css::beans::XPropertyChangeListener + > PropertyComposer_Base; + /** implements an <type>XPropertyHandler</type> which composes its information + from a set of other property handlers + */ + class PropertyComposer :public ::cppu::BaseMutex + ,public PropertyComposer_Base + ,public IPropertyExistenceCheck + { + public: + typedef std::vector< css::uno::Reference< css::inspection::XPropertyHandler > > + HandlerArray; + + private: + HandlerArray m_aSlaveHandlers; + std::unique_ptr< ComposedPropertyUIUpdate > m_pUIRequestComposer; + PropertyChangeListeners m_aPropertyListeners; + bool m_bSupportedPropertiesAreKnown; + PropertyBag m_aSupportedProperties; + + public: + /** constructs an <type>XPropertyHandler</type> which composes its information from a set + of other property handlers + + @param _rSlaveHandlers + the set of slave handlers to invoke. Must not be <NULL/> + */ + explicit PropertyComposer( std::vector< css::uno::Reference< css::inspection::XPropertyHandler > >&& _rSlaveHandlers ); + + public: + // XPropertyHandler overridables + virtual void SAL_CALL inspect( const css::uno::Reference< css::uno::XInterface >& _rxIntrospectee ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override; + virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override; + virtual css::uno::Any SAL_CALL convertToPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rControlValue ) override; + virtual css::uno::Any SAL_CALL convertToControlValue( const OUString& _rPropertyName, const css::uno::Any& _rPropertyValue, const css::uno::Type& _rControlValueType ) override; + virtual css::beans::PropertyState + SAL_CALL getPropertyState( const OUString& _rPropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override; + virtual css::uno::Sequence< css::beans::Property > + SAL_CALL getSupportedProperties() override; + virtual css::uno::Sequence< OUString > + SAL_CALL getSupersededProperties( ) override; + virtual css::uno::Sequence< OUString > + SAL_CALL getActuatingProperties( ) override; + virtual css::inspection::LineDescriptor + SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override; + virtual sal_Bool SAL_CALL isComposable( const OUString& _rPropertyName ) override; + virtual css::inspection::InteractiveSelectionResult + SAL_CALL onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, css::uno::Any& _rData, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) override; + virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) override; + virtual sal_Bool SAL_CALL suspend( sal_Bool _bSuspend ) override; + + // XComponent + DECLARE_XCOMPONENT() + 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& Source ) override; + + // IPropertyExistenceCheck + virtual bool hasPropertyByName( const OUString& _rName ) override; + + private: + /** ensures that m_pUIRequestComposer exists + */ + void impl_ensureUIRequestComposer( const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ); + + /** checks whether a given property exists in <member>m_aSupportedProperties</member> + */ + bool impl_isSupportedProperty_nothrow( const OUString& _rPropertyName ) + { + css::beans::Property aDummy; aDummy.Name = _rPropertyName; + return m_aSupportedProperties.find( aDummy ) != m_aSupportedProperties.end(); + } + + private: + class MethodGuard; + friend class MethodGuard; + class MethodGuard : public ::osl::MutexGuard + { + public: + explicit MethodGuard( PropertyComposer& _rInstance ) + : ::osl::MutexGuard( _rInstance.m_aMutex ) + { + if ( _rInstance.m_aSlaveHandlers.empty() ) + throw css::lang::DisposedException( OUString(), _rInstance ); + } + }; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/propertycontrolextender.cxx b/extensions/source/propctrlr/propertycontrolextender.cxx new file mode 100644 index 000000000..142e288dd --- /dev/null +++ b/extensions/source/propctrlr/propertycontrolextender.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 "propertycontrolextender.hxx" + +#include <com/sun/star/awt/KeyFunction.hpp> + +#include <tools/diagnose_ex.h> + + +namespace pcr +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_SET_THROW; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::Any; + using ::com::sun::star::awt::XWindow; + using ::com::sun::star::awt::KeyEvent; + using ::com::sun::star::inspection::XPropertyControl; + using ::com::sun::star::lang::EventObject; + using ::com::sun::star::inspection::XPropertyControlContext; + + namespace KeyFunction = ::com::sun::star::awt::KeyFunction; + + + //= PropertyControlExtender_Data + + struct PropertyControlExtender_Data + { + Reference< XPropertyControl > xControl; + Reference< XWindow > xControlWindow; + }; + + + //= PropertyControlExtender + + + PropertyControlExtender::PropertyControlExtender( const Reference< XPropertyControl >& _rxObservedControl ) + :m_pData( new PropertyControlExtender_Data ) + { + try + { + m_pData->xControl.set( _rxObservedControl, UNO_SET_THROW ); + m_pData->xControlWindow.set( m_pData->xControl->getControlWindow(), UNO_SET_THROW ); + m_pData->xControlWindow->addKeyListener( this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + + PropertyControlExtender::~PropertyControlExtender() + { + } + + + void SAL_CALL PropertyControlExtender::keyPressed( const KeyEvent& _event ) + { + OSL_ENSURE( _event.Source == m_pData->xControlWindow, "PropertyControlExtender::keyPressed: where does this come from?" ); + if ( ( _event.KeyFunc != KeyFunction::DELETE ) + || ( _event.Modifiers != 0 ) + ) + return; + + try + { + Reference< XPropertyControl > xControl( m_pData->xControl, UNO_SET_THROW ); + + // reset the value + xControl->setValue( Any() ); + + // and notify the change + // don't use XPropertyControl::notifyModifiedValue. It only notifies when the control content + // is recognized as being modified by the user, which is not the case, since we just modified + // it programmatically. + Reference< XPropertyControlContext > xControlContext( xControl->getControlContext(), UNO_SET_THROW ); + xControlContext->valueChanged( xControl ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + + void SAL_CALL PropertyControlExtender::keyReleased( const KeyEvent& /*_event*/ ) + { + // not interested in + } + + + void SAL_CALL PropertyControlExtender::disposing( const EventObject& Source ) + { + OSL_ENSURE( Source.Source == m_pData->xControlWindow, "PropertyControlExtender::disposing: where does this come from?" ); + (void)Source.Source; + m_pData->xControlWindow.clear(); + m_pData->xControl.clear(); + } + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/propertycontrolextender.hxx b/extensions/source/propctrlr/propertycontrolextender.hxx new file mode 100644 index 000000000..ed7c4a06c --- /dev/null +++ b/extensions/source/propctrlr/propertycontrolextender.hxx @@ -0,0 +1,63 @@ +/* -*- 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/XKeyListener.hpp> +#include <com/sun/star/inspection/XPropertyControl.hpp> + +#include <cppuhelper/implbase.hxx> + +#include <memory> + + +namespace pcr +{ + + + //= PropertyControlExtender + + struct PropertyControlExtender_Data; + typedef ::cppu::WeakImplHelper < css::awt::XKeyListener + > PropertyControlExtender_Base; + class PropertyControlExtender : public PropertyControlExtender_Base + { + public: + explicit PropertyControlExtender( + const css::uno::Reference< css::inspection::XPropertyControl >& _rxObservedControl + ); + + // XKeyListener + virtual void SAL_CALL keyPressed( const css::awt::KeyEvent& e ) override; + virtual void SAL_CALL keyReleased( const css::awt::KeyEvent& e ) override; + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + protected: + virtual ~PropertyControlExtender() override; + + private: + std::unique_ptr< PropertyControlExtender_Data > m_pData; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/propertyeditor.cxx b/extensions/source/propctrlr/propertyeditor.cxx new file mode 100644 index 000000000..2fcf55649 --- /dev/null +++ b/extensions/source/propctrlr/propertyeditor.cxx @@ -0,0 +1,380 @@ +/* -*- 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 "handlerhelper.hxx" +#include "propertyeditor.hxx" +#include "browserpage.hxx" +#include "linedescriptor.hxx" + +#include <tools/debug.hxx> +#include <osl/diagnose.h> + +namespace pcr +{ + using ::com::sun::star::uno::Any; + using ::com::sun::star::inspection::XPropertyControl; + using ::com::sun::star::uno::Reference; + + OPropertyEditor::OPropertyEditor(const css::uno::Reference<css::uno::XComponentContext>& rContext, weld::Builder& rBuilder) + : m_xContainer(rBuilder.weld_container("box")) + , m_xTabControl(rBuilder.weld_notebook("tabcontrol")) + , m_xControlHoldingParent(rBuilder.weld_container("controlparent")) // controls initially have this parent before they are moved + , m_xContext(rContext) + , m_pListener(nullptr) + , m_pObserver(nullptr) + , m_nNextId(1) + , m_bHasHelpSection(false) + { + PropertyHandlerHelper::setBuilderParent(rContext, m_xControlHoldingParent.get()); + + m_xTabControl->connect_leave_page(LINK(this, OPropertyEditor, OnPageDeactivate)); + m_xTabControl->connect_enter_page(LINK(this, OPropertyEditor, OnPageActivate)); + } + + OPropertyEditor::~OPropertyEditor() + { + PropertyHandlerHelper::clearBuilderParent(m_xContext); + ClearAll(); + } + + void OPropertyEditor::ClearAll() + { + m_nNextId=1; + + m_aPropertyPageIds.clear(); + m_aShownPages.clear(); + m_aHiddenPages.clear(); + + int nCount = m_xTabControl->get_n_pages(); + for (int i = nCount - 1; i >= 0; --i) + { + OString sID = m_xTabControl->get_page_ident(i); + m_xTabControl->remove_page(sID); + } + + assert(m_xTabControl->get_n_pages() == 0); + } + + Size OPropertyEditor::get_preferred_size() const + { + return m_xTabControl->get_preferred_size(); + } + + void OPropertyEditor::CommitModified() + { + // commit all of my pages, if necessary + for (const auto& page : m_aShownPages) + { + OBrowserPage* pPage = page.second.xPage.get(); + if (pPage && pPage->getListBox().IsModified() ) + pPage->getListBox().CommitModified(); + } + } + + OBrowserPage* OPropertyEditor::getPage(const OUString& rPropertyName) + { + OBrowserPage* pPage = nullptr; + MapStringToPageId::const_iterator aPropertyPageIdPos = m_aPropertyPageIds.find(rPropertyName); + if (aPropertyPageIdPos != m_aPropertyPageIds.end()) + pPage = getPage(aPropertyPageIdPos->second); + return pPage; + } + + const OBrowserPage* OPropertyEditor::getPage( const OUString& _rPropertyName ) const + { + return const_cast< OPropertyEditor* >( this )->getPage( _rPropertyName ); + } + + OBrowserPage* OPropertyEditor::getPage(sal_uInt16 rPageId) + { + OBrowserPage* pPage = nullptr; + auto aPagePos = m_aShownPages.find(rPageId); + if (aPagePos != m_aShownPages.end()) + pPage = aPagePos->second.xPage.get(); + return pPage; + } + + const OBrowserPage* OPropertyEditor::getPage(sal_uInt16 rPageId) const + { + return const_cast<OPropertyEditor*>(this)->getPage(rPageId); + } + + sal_uInt16 OPropertyEditor::AppendPage(const OUString& rText, const OString& rHelpId) + { + // obtain a new id + sal_uInt16 nId = m_nNextId++; + // insert the id + OString sIdent = OString::number(nId); + m_xTabControl->append_page(sIdent, rText); + + // create a new page + auto xPage = std::make_unique<OBrowserPage>(m_xTabControl->get_page(sIdent), m_xControlHoldingParent.get()); + // some knittings + xPage->getListBox().SetListener(m_pListener); + xPage->getListBox().SetObserver(m_pObserver); + xPage->getListBox().EnableHelpSection(m_bHasHelpSection); + xPage->SetHelpId(rHelpId); + + m_aShownPages[nId] = PropertyPage(m_xTabControl->get_n_pages() - 1, rText, std::move(xPage)); + + // immediately activate the page + m_xTabControl->set_current_page(sIdent); + + return nId; + } + + void OPropertyEditor::SetHelpId( const OString& rHelpId ) + { + m_xTabControl->set_help_id(rHelpId); + } + + void OPropertyEditor::RemovePage(sal_uInt16 nID) + { + auto aPagePos = m_aShownPages.find(nID); + if (aPagePos == m_aShownPages.end()) + return; + + m_aShownPages.erase(aPagePos); + OString sIdent(OString::number(nID)); + m_xTabControl->remove_page(sIdent); + } + + void OPropertyEditor::SetPage(sal_uInt16 nId) + { + m_xTabControl->set_current_page(OString::number(nId)); + } + + sal_uInt16 OPropertyEditor::GetCurPage() const + { + return m_xTabControl->get_current_page_ident().toUInt32(); + } + + void OPropertyEditor::forEachPage( PageOperation _pOperation ) + { + int nCount = m_xTabControl->get_n_pages(); + for (int i = 0; i < nCount; ++i) + { + sal_uInt16 nID = m_xTabControl->get_page_ident(i).toUInt32(); + OBrowserPage* pPage = getPage(nID); + if (!pPage) + continue; + (this->*_pOperation)( *pPage, nullptr ); + } + } + + void OPropertyEditor::setPageLineListener( OBrowserPage& rPage, const void* ) + { + rPage.getListBox().SetListener( m_pListener ); + } + + void OPropertyEditor::SetLineListener(IPropertyLineListener* pListener) + { + m_pListener = pListener; + forEachPage( &OPropertyEditor::setPageLineListener ); + } + + void OPropertyEditor::setPageControlObserver( OBrowserPage& rPage, const void* ) + { + rPage.getListBox().SetObserver( m_pObserver ); + } + + void OPropertyEditor::SetControlObserver( IPropertyControlObserver* _pObserver ) + { + m_pObserver = _pObserver; + forEachPage( &OPropertyEditor::setPageControlObserver ); + } + + void OPropertyEditor::EnableHelpSection( bool bEnable ) + { + m_bHasHelpSection = bEnable; + forEachPage( &OPropertyEditor::enableHelpSection ); + } + + void OPropertyEditor::SetHelpText( const OUString& rHelpText ) + { + int nCount = m_xTabControl->get_n_pages(); + for (int i = 0; i < nCount; ++i) + { + sal_uInt16 nID = m_xTabControl->get_page_ident(i).toUInt32(); + OBrowserPage* pPage = getPage(nID); + if (!pPage) + continue; + setHelpSectionText( *pPage, &rHelpText ); + } + } + + void OPropertyEditor::enableHelpSection( OBrowserPage& rPage, const void* ) + { + rPage.getListBox().EnableHelpSection( m_bHasHelpSection ); + } + + void OPropertyEditor::setHelpSectionText( OBrowserPage& rPage, const void* pPointerToOUString ) + { + OSL_ENSURE( pPointerToOUString, "OPropertyEditor::setHelpSectionText: invalid argument!" ); + if ( !pPointerToOUString ) + return; + + const OUString& rText( *static_cast<const OUString*>(pPointerToOUString) ); + rPage.getListBox().SetHelpText( rText ); + } + + void OPropertyEditor::InsertEntry( const OLineDescriptor& rData, sal_uInt16 nPageId, sal_uInt16 nPos ) + { + // let the current page handle this + OBrowserPage* pPage = getPage(nPageId); + DBG_ASSERT( pPage, "OPropertyEditor::InsertEntry: don't have such a page!" ); + if ( !pPage ) + return; + + pPage->getListBox().InsertEntry( rData, nPos ); + + OSL_ENSURE( m_aPropertyPageIds.find( rData.sName ) == m_aPropertyPageIds.end(), + "OPropertyEditor::InsertEntry: property already present in the map!" ); + m_aPropertyPageIds.emplace( rData.sName, nPageId ); + } + + void OPropertyEditor::RemoveEntry( const OUString& rName ) + { + OBrowserPage* pPage = getPage( rName ); + if ( pPage ) + { + OSL_VERIFY( pPage->getListBox().RemoveEntry( rName ) ); + + OSL_ENSURE( m_aPropertyPageIds.find( rName ) != m_aPropertyPageIds.end(), + "OPropertyEditor::RemoveEntry: property not present in the map!" ); + m_aPropertyPageIds.erase( rName ); + } + } + + void OPropertyEditor::ChangeEntry( const OLineDescriptor& rData ) + { + OBrowserPage* pPage = getPage( rData.sName ); + if ( pPage ) + pPage->getListBox().ChangeEntry( rData, EDITOR_LIST_REPLACE_EXISTING ); + } + + void OPropertyEditor::SetPropertyValue( const OUString& rEntryName, const Any& _rValue, bool _bUnknownValue ) + { + OBrowserPage* pPage = getPage( rEntryName ); + if ( pPage ) + pPage->getListBox().SetPropertyValue( rEntryName, _rValue, _bUnknownValue ); + } + + sal_uInt16 OPropertyEditor::GetPropertyPos( const OUString& rEntryName ) const + { + sal_uInt16 nVal=EDITOR_LIST_ENTRY_NOTFOUND; + const OBrowserPage* pPage = getPage( rEntryName ); + if ( pPage ) + nVal = pPage->getListBox().GetPropertyPos( rEntryName ); + return nVal; + } + + void OPropertyEditor::ShowPropertyPage(sal_uInt16 nPageId, bool bShow) + { + assert((m_aHiddenPages.find(nPageId) != m_aHiddenPages.end() || + m_aShownPages.find(nPageId) != m_aShownPages.end()) && "page doesn't exist"); + OString sIdent(OString::number(nPageId)); + if (!bShow) + { + auto aPagePos = m_aShownPages.find(nPageId); + if (aPagePos != m_aShownPages.end()) + { + aPagePos->second.xPage->detach(); + m_xTabControl->remove_page(sIdent); + + m_aHiddenPages[nPageId] = std::move(aPagePos->second); + m_aShownPages.erase(aPagePos); + } + } + else + { + auto aPagePos = m_aHiddenPages.find(nPageId); + if (aPagePos != m_aHiddenPages.end()) + { + m_xTabControl->insert_page(sIdent, aPagePos->second.sLabel, aPagePos->second.nPos); + aPagePos->second.xPage->reattach(m_xTabControl->get_page(sIdent)); + + m_aShownPages[nPageId] = std::move(aPagePos->second); + m_aHiddenPages.erase(aPagePos); + } + } + } + + void OPropertyEditor::EnablePropertyControls( const OUString& rEntryName, sal_Int16 nControls, bool bEnable ) + { + for (const auto& rPage : m_aShownPages) + { + OBrowserPage* pPage = rPage.second.xPage.get(); + if (pPage) + pPage->getListBox().EnablePropertyControls( rEntryName, nControls, bEnable ); + } + } + + void OPropertyEditor::EnablePropertyLine( const OUString& rEntryName, bool bEnable ) + { + for (const auto& rPage : m_aShownPages) + { + OBrowserPage* pPage = rPage.second.xPage.get(); + if (pPage) + pPage->getListBox().EnablePropertyLine( rEntryName, bEnable ); + } + } + + Reference< XPropertyControl > OPropertyEditor::GetPropertyControl(const OUString& rEntryName) + { + Reference< XPropertyControl > xControl; + // let the current page handle this + OBrowserPage* pPage = getPage(m_xTabControl->get_current_page_ident().toUInt32()); + if (pPage) + xControl = pPage->getListBox().GetPropertyControl(rEntryName); + return xControl; + } + + IMPL_LINK(OPropertyEditor, OnPageActivate, const OString&, rNewPage, void) + { + m_aPageActivationHandler.Call(rNewPage); + } + + IMPL_LINK(OPropertyEditor, OnPageDeactivate, const OString&, rIdent, bool) + { + // commit the data on the current (to-be-deactivated) tab page + // (79404) + OBrowserPage* pCurrentPage = getPage(rIdent.toUInt32()); + if (!pCurrentPage) + return true; + + if (pCurrentPage->getListBox().IsModified()) + pCurrentPage->getListBox().CommitModified(); + + return true; + } + + OPropertyEditor::PropertyPage::PropertyPage() + : nPos(0) + { + } + + OPropertyEditor::PropertyPage::PropertyPage(sal_uInt16 nPagePos, const OUString& rLabel, std::unique_ptr<OBrowserPage> pPage) + : nPos(nPagePos), sLabel(rLabel), xPage(std::move(pPage)) + { + } + +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/propertyeditor.hxx b/extensions/source/propctrlr/propertyeditor.hxx new file mode 100644 index 000000000..1f28510ab --- /dev/null +++ b/extensions/source/propctrlr/propertyeditor.hxx @@ -0,0 +1,134 @@ +/* -*- 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 "browserpage.hxx" +#include "pcrcommon.hxx" + +#include <com/sun/star/inspection/XPropertyControl.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <vcl/weld.hxx> +#include <map> + +namespace pcr +{ + class IPropertyLineListener; + class IPropertyControlObserver; + class OBrowserPage; + struct OLineDescriptor; + class OBrowserListBox; + + //= OPropertyEditor + class OPropertyEditor final + { + private: + typedef std::map< OUString, sal_uInt16 > MapStringToPageId; + struct PropertyPage + { + sal_uInt16 nPos; + OUString sLabel; + std::unique_ptr<OBrowserPage> xPage; + PropertyPage(); + PropertyPage(sal_uInt16 nPagePos, const OUString& rLabel, std::unique_ptr<OBrowserPage> pPage); + }; + + std::unique_ptr<weld::Container> m_xContainer; + std::unique_ptr<weld::Notebook> m_xTabControl; + // controls initially have this parent before they are moved + std::unique_ptr<weld::Container> m_xControlHoldingParent; + css::uno::Reference<css::uno::XComponentContext> m_xContext; + IPropertyLineListener* m_pListener; + IPropertyControlObserver* m_pObserver; + sal_uInt16 m_nNextId; + Link<const OString&,void> m_aPageActivationHandler; + bool m_bHasHelpSection; + + MapStringToPageId m_aPropertyPageIds; + std::map<sal_uInt16, PropertyPage> m_aShownPages; + std::map<sal_uInt16, PropertyPage> m_aHiddenPages; + + public: + explicit OPropertyEditor(const css::uno::Reference<css::uno::XComponentContext>& rContext, weld::Builder& rBuilder); + ~OPropertyEditor(); + + void SetLineListener( IPropertyLineListener* ); + void SetControlObserver( IPropertyControlObserver* ); + + void EnableHelpSection( bool _bEnable ); + bool HasHelpSection() const { return m_bHasHelpSection; } + void SetHelpText( const OUString& _rHelpText ); + + void SetHelpId( const OString& sHelpId ); + sal_uInt16 AppendPage( const OUString& r, const OString& _rHelpId ); + void SetPage( sal_uInt16 ); + void RemovePage(sal_uInt16 nID); + sal_uInt16 GetCurPage() const; + void ClearAll(); + + void SetPropertyValue(const OUString& _rEntryName, const css::uno::Any& _rValue, bool _bUnknownValue ); + sal_uInt16 GetPropertyPos(const OUString& rEntryName ) const; + css::uno::Reference< css::inspection::XPropertyControl > + GetPropertyControl( const OUString& rEntryName ); + void EnablePropertyLine( const OUString& _rEntryName, bool _bEnable ); + void EnablePropertyControls( const OUString& _rEntryName, sal_Int16 _nControls, bool _bEnable ); + + void ShowPropertyPage( sal_uInt16 _nPageId, bool _bShow ); + + void InsertEntry( const OLineDescriptor&, sal_uInt16 _nPageId, sal_uInt16 nPos = EDITOR_LIST_APPEND ); + void RemoveEntry( const OUString& _rName ); + void ChangeEntry( const OLineDescriptor& ); + + void setPageActivationHandler(const Link<const OString&,void>& _rHdl) { m_aPageActivationHandler = _rHdl; } + + Size get_preferred_size() const; + + weld::Widget* getWidget() const { return m_xTabControl.get(); } + + void Show() { m_xTabControl->show(); } + void Hide() { m_xTabControl->hide(); } + void GrabFocus() { m_xTabControl->grab_focus(); } + + void CommitModified(); + + private: + OBrowserPage* getPage( sal_uInt16 _rPageId ); + const OBrowserPage* getPage( sal_uInt16 _rPageId ) const; + + OBrowserPage* getPage( const OUString& _rPropertyName ); + const OBrowserPage* getPage( const OUString& _rPropertyName ) const; + + typedef void (OPropertyEditor::*PageOperation)( OBrowserPage&, const void* ); + void forEachPage( PageOperation _pOperation ); + + void setPageLineListener( OBrowserPage& _rPage, const void* ); + void setPageControlObserver( OBrowserPage& _rPage, const void* ); + void enableHelpSection( OBrowserPage& _rPage, const void* ); + static void setHelpSectionText( OBrowserPage& _rPage, const void* _pPointerToOUString ); + + DECL_LINK(OnPageDeactivate, const OString&, bool); + DECL_LINK(OnPageActivate, const OString&, void); + }; + + +} // namespace pcr + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/propertyhandler.cxx b/extensions/source/propctrlr/propertyhandler.cxx new file mode 100644 index 000000000..53cf29fc6 --- /dev/null +++ b/extensions/source/propctrlr/propertyhandler.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 <memory> +#include "propertyhandler.hxx" +#include "formmetadata.hxx" +#include "formbrowsertools.hxx" +#include "handlerhelper.hxx" +#include "formstrings.hxx" + +#include <com/sun/star/lang/NullPointerException.hpp> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/script/Converter.hpp> + +#include <cppuhelper/supportsservice.hxx> +#include <tools/debug.hxx> +#include <unotools/confignode.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/syslocale.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <rtl/ref.hxx> + +#include <algorithm> + +namespace pcr +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::script; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::frame; + using namespace ::com::sun::star::inspection; + using namespace ::comphelper; + + + PropertyHandler::PropertyHandler( const Reference< XComponentContext >& _rxContext ) + :PropertyHandler_Base( m_aMutex ) + ,m_bSupportedPropertiesAreKnown( false ) + ,m_aPropertyListeners( m_aMutex ) + ,m_xContext( _rxContext ) + ,m_pInfoService ( new OPropertyInfoService ) + { + + m_xTypeConverter = Converter::create(_rxContext); + } + + PropertyHandler::~PropertyHandler() + { + } + + void SAL_CALL PropertyHandler::inspect( const Reference< XInterface >& _rxIntrospectee ) + { + if ( !_rxIntrospectee.is() ) + throw NullPointerException(); + + ::osl::MutexGuard aGuard( m_aMutex ); + + Reference< XPropertySet > xNewComponent( _rxIntrospectee, UNO_QUERY ); + if ( xNewComponent == m_xComponent ) + return; + + // remove all old property change listeners + ::comphelper::OInterfaceIteratorHelper3 removeListener(m_aPropertyListeners); + ::comphelper::OInterfaceIteratorHelper3 readdListener(m_aPropertyListeners); // will copy the container as needed + while ( removeListener.hasMoreElements() ) + removePropertyChangeListener( removeListener.next() ); + OSL_ENSURE( m_aPropertyListeners.getLength() == 0, "PropertyHandler::inspect: derived classes are expected to forward the removePropertyChangeListener call to their base class (me)!" ); + + // remember the new component, and give derived classes the chance to react on it + m_xComponent = xNewComponent; + onNewComponent(); + + // add the listeners, again + while ( readdListener.hasMoreElements() ) + addPropertyChangeListener( readdListener.next() ); + } + + void PropertyHandler::onNewComponent() + { + if ( m_xComponent.is() ) + m_xComponentPropertyInfo = m_xComponent->getPropertySetInfo(); + else + m_xComponentPropertyInfo.clear(); + + m_bSupportedPropertiesAreKnown = false; + m_aSupportedProperties.realloc( 0 ); + } + + Sequence< Property > SAL_CALL PropertyHandler::getSupportedProperties() + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !m_bSupportedPropertiesAreKnown ) + { + m_aSupportedProperties = StlSyntaxSequence<css::beans::Property>(doDescribeSupportedProperties()); + m_bSupportedPropertiesAreKnown = true; + } + return m_aSupportedProperties; + } + + Sequence< OUString > SAL_CALL PropertyHandler::getSupersededProperties( ) + { + return Sequence< OUString >(); + } + + Sequence< OUString > SAL_CALL PropertyHandler::getActuatingProperties( ) + { + return Sequence< OUString >(); + } + + Any SAL_CALL PropertyHandler::convertToPropertyValue( const OUString& _rPropertyName, const Any& _rControlValue ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId = m_pInfoService->getPropertyId( _rPropertyName ); + Property aProperty( impl_getPropertyFromName_throw( _rPropertyName ) ); + + Any aPropertyValue; + if ( !_rControlValue.hasValue() ) + // NULL is converted to NULL + return aPropertyValue; + + if ( ( m_pInfoService->getPropertyUIFlags( nPropId ) & PROP_FLAG_ENUM ) != 0 ) + { + OUString sControlValue; + OSL_VERIFY( _rControlValue >>= sControlValue ); + ::rtl::Reference< IPropertyEnumRepresentation > aEnumConversion( + new DefaultEnumRepresentation( *m_pInfoService, aProperty.Type, nPropId ) ); + // TODO/UNOize: cache those converters? + aEnumConversion->getValueFromDescription( sControlValue, aPropertyValue ); + } + else + aPropertyValue = PropertyHandlerHelper::convertToPropertyValue( + m_xContext, m_xTypeConverter, aProperty, _rControlValue ); + return aPropertyValue; + } + + Any SAL_CALL PropertyHandler::convertToControlValue( const OUString& _rPropertyName, const Any& _rPropertyValue, const Type& _rControlValueType ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId = m_pInfoService->getPropertyId( _rPropertyName ); + + if ( ( m_pInfoService->getPropertyUIFlags( nPropId ) & PROP_FLAG_ENUM ) != 0 ) + { + DBG_ASSERT( _rControlValueType.getTypeClass() == TypeClass_STRING, "PropertyHandler::convertToControlValue: ENUM, but not STRING?" ); + + ::rtl::Reference< IPropertyEnumRepresentation > aEnumConversion( + new DefaultEnumRepresentation( *m_pInfoService, _rPropertyValue.getValueType(), nPropId ) ); + // TODO/UNOize: cache those converters? + return Any( aEnumConversion->getDescriptionForValue( _rPropertyValue ) ); + } + + return PropertyHandlerHelper::convertToControlValue( + m_xContext, m_xTypeConverter, _rPropertyValue, _rControlValueType ); + } + + PropertyState SAL_CALL PropertyHandler::getPropertyState( const OUString& /*_rPropertyName*/ ) + { + return PropertyState_DIRECT_VALUE; + } + + LineDescriptor SAL_CALL PropertyHandler::describePropertyLine( const OUString& _rPropertyName, + const Reference< XPropertyControlFactory >& _rxControlFactory ) + { + if ( !_rxControlFactory.is() ) + throw NullPointerException(); + + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + const Property& rProperty( impl_getPropertyFromId_throw( nPropId ) ); + + LineDescriptor aDescriptor; + if ( ( m_pInfoService->getPropertyUIFlags( nPropId ) & PROP_FLAG_ENUM ) != 0 ) + { + aDescriptor.Control = PropertyHandlerHelper::createListBoxControl( + _rxControlFactory, m_pInfoService->getPropertyEnumRepresentations( nPropId ), + PropertyHandlerHelper::requiresReadOnlyControl( rProperty.Attributes ), false ); + } + else + PropertyHandlerHelper::describePropertyLine( rProperty, aDescriptor, _rxControlFactory ); + + aDescriptor.HelpURL = HelpIdUrl::getHelpURL( m_pInfoService->getPropertyHelpId( nPropId ) ); + aDescriptor.DisplayName = m_pInfoService->getPropertyTranslation( nPropId ); + + if ( ( m_pInfoService->getPropertyUIFlags( nPropId ) & PROP_FLAG_DATA_PROPERTY ) != 0 ) + aDescriptor.Category = "Data"; + else + aDescriptor.Category = "General"; + return aDescriptor; + } + + sal_Bool SAL_CALL PropertyHandler::isComposable( const OUString& _rPropertyName ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + return m_pInfoService->isComposeable( _rPropertyName ); + } + + InteractiveSelectionResult SAL_CALL PropertyHandler::onInteractivePropertySelection( const OUString& /*_rPropertyName*/, sal_Bool /*_bPrimary*/, Any& /*_rData*/, const Reference< XObjectInspectorUI >& /*_rxInspectorUI*/ ) + { + OSL_FAIL( "PropertyHandler::onInteractivePropertySelection: not implemented!" ); + return InteractiveSelectionResult_Cancelled; + } + + void SAL_CALL PropertyHandler::actuatingPropertyChanged( const OUString& /*_rActuatingPropertyName*/, const Any& /*_rNewValue*/, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& /*_rxInspectorUI*/, sal_Bool /*_bFirstTimeInit*/ ) + { + OSL_FAIL( "PropertyHandler::actuatingPropertyChanged: not implemented!" ); + } + + void SAL_CALL PropertyHandler::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !_rxListener.is() ) + throw NullPointerException(); + m_aPropertyListeners.addInterface( _rxListener ); + } + + void SAL_CALL PropertyHandler::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + m_aPropertyListeners.removeInterface( _rxListener ); + } + + sal_Bool SAL_CALL PropertyHandler::suspend( sal_Bool /*_bSuspend*/ ) + { + return true; + } + + IMPLEMENT_FORWARD_XCOMPONENT( PropertyHandler, PropertyHandler_Base ) + + void SAL_CALL PropertyHandler::disposing() + { + m_xComponent.clear(); + m_aPropertyListeners.clear(); + m_xTypeConverter.clear(); + m_aSupportedProperties.realloc( 0 ); + } + + void PropertyHandler::firePropertyChange( const OUString& _rPropName, PropertyId _nPropId, const Any& _rOldValue, const Any& _rNewValue ) + { + PropertyChangeEvent aEvent; + aEvent.Source = m_xComponent; + aEvent.PropertyHandle = _nPropId; + aEvent.PropertyName = _rPropName; + aEvent.OldValue = _rOldValue; + aEvent.NewValue = _rNewValue; + m_aPropertyListeners.notifyEach( &XPropertyChangeListener::propertyChange, aEvent ); + } + + const Property* PropertyHandler::impl_getPropertyFromId_nothrow( PropertyId _nPropId ) const + { + const_cast< PropertyHandler* >( this )->getSupportedProperties(); + const Property* pFound = std::find_if( m_aSupportedProperties.begin(), m_aSupportedProperties.end(), + FindPropertyByHandle( _nPropId ) + ); + if ( pFound != m_aSupportedProperties.end() ) + return pFound; + return nullptr; + } + + const Property& PropertyHandler::impl_getPropertyFromId_throw( PropertyId _nPropId ) const + { + const Property* pProperty = impl_getPropertyFromId_nothrow( _nPropId ); + if ( !pProperty ) + throw UnknownPropertyException(); + + return *pProperty; + } + + const Property& PropertyHandler::impl_getPropertyFromName_throw( const OUString& _rPropertyName ) const + { + const_cast< PropertyHandler* >( this )->getSupportedProperties(); + StlSyntaxSequence< Property >::const_iterator pFound = std::find_if( m_aSupportedProperties.begin(), m_aSupportedProperties.end(), + FindPropertyByName( _rPropertyName ) + ); + if ( pFound == m_aSupportedProperties.end() ) + throw UnknownPropertyException(_rPropertyName); + + return *pFound; + } + + void PropertyHandler::implAddPropertyDescription( std::vector< Property >& _rProperties, const OUString& _rPropertyName, const Type& _rType, sal_Int16 _nAttribs ) const + { + _rProperties.push_back( Property( + _rPropertyName, + m_pInfoService->getPropertyId( _rPropertyName ), + _rType, + _nAttribs + ) ); + } + + weld::Window* PropertyHandler::impl_getDefaultDialogFrame_nothrow() const + { + return PropertyHandlerHelper::getDialogParentFrame(m_xContext); + } + + PropertyId PropertyHandler::impl_getPropertyId_throwUnknownProperty( const OUString& _rPropertyName ) const + { + PropertyId nPropId = m_pInfoService->getPropertyId( _rPropertyName ); + if ( nPropId == -1 ) + throw UnknownPropertyException(_rPropertyName); + return nPropId; + } + + PropertyId PropertyHandler::impl_getPropertyId_throwRuntime( const OUString& _rPropertyName ) const + { + PropertyId nPropId = m_pInfoService->getPropertyId( _rPropertyName ); + if ( nPropId == -1 ) + throw RuntimeException(); + return nPropId; + } + + PropertyId PropertyHandler::impl_getPropertyId_nothrow( const OUString& _rPropertyName ) const + { + return m_pInfoService->getPropertyId( _rPropertyName ); + } + + void PropertyHandler::impl_setContextDocumentModified_nothrow() const + { + Reference< XModifiable > xModifiable( impl_getContextDocument_nothrow(), UNO_QUERY ); + if ( xModifiable.is() ) + xModifiable->setModified( true ); + } + + bool PropertyHandler::impl_componentHasProperty_throw( const OUString& _rPropName ) const + { + return m_xComponentPropertyInfo.is() && m_xComponentPropertyInfo->hasPropertyByName( _rPropName ); + } + + sal_Int16 PropertyHandler::impl_getDocumentMeasurementUnit_throw() const + { + FieldUnit eUnit = FieldUnit::NONE; + + Reference< XServiceInfo > xDocumentSI( impl_getContextDocument_nothrow(), UNO_QUERY ); + OSL_ENSURE( xDocumentSI.is(), "PropertyHandlerHelper::impl_getDocumentMeasurementUnit_throw: No context document - where do I live?" ); + if ( xDocumentSI.is() ) + { + // determine the application type we live in + OUString sConfigurationLocation; + OUString sConfigurationProperty; + if ( xDocumentSI->supportsService( SERVICE_WEB_DOCUMENT ) ) + { // writer + sConfigurationLocation = "/org.openoffice.Office.WriterWeb/Layout/Other"; + sConfigurationProperty = "MeasureUnit"; + } + else if ( xDocumentSI->supportsService( SERVICE_TEXT_DOCUMENT ) ) + { // writer + sConfigurationLocation = "/org.openoffice.Office.Writer/Layout/Other"; + sConfigurationProperty = "MeasureUnit"; + } + else if ( xDocumentSI->supportsService( SERVICE_SPREADSHEET_DOCUMENT ) ) + { // calc + sConfigurationLocation = "/org.openoffice.Office.Calc/Layout/Other/MeasureUnit"; + sConfigurationProperty = "Metric"; + } + else if ( xDocumentSI->supportsService( SERVICE_DRAWING_DOCUMENT ) ) + { + sConfigurationLocation = "/org.openoffice.Office.Draw/Layout/Other/MeasureUnit"; + sConfigurationProperty = "Metric"; + } + else if ( xDocumentSI->supportsService( SERVICE_PRESENTATION_DOCUMENT ) ) + { + sConfigurationLocation = "/org.openoffice.Office.Impress/Layout/Other/MeasureUnit"; + sConfigurationProperty = "Metric"; + } + + // read the measurement unit from the configuration + if ( !(sConfigurationLocation.isEmpty() || sConfigurationProperty.isEmpty()) ) + { + ::utl::OConfigurationTreeRoot aConfigTree( ::utl::OConfigurationTreeRoot::createWithComponentContext( + m_xContext, sConfigurationLocation, -1, ::utl::OConfigurationTreeRoot::CM_READONLY ) ); + sal_Int32 nUnitAsInt = sal_Int32(FieldUnit::NONE); + aConfigTree.getNodeValue( sConfigurationProperty ) >>= nUnitAsInt; + + // if this denotes a valid (and accepted) unit, then use it + if ( ( nUnitAsInt > sal_Int32(FieldUnit::NONE) ) && ( nUnitAsInt <= sal_Int32(FieldUnit::MM_100TH) ) ) + eUnit = static_cast< FieldUnit >( nUnitAsInt ); + } + } + + if ( FieldUnit::NONE == eUnit ) + { + MeasurementSystem eSystem = SvtSysLocale().GetLocaleData().getMeasurementSystemEnum(); + eUnit = MeasurementSystem::Metric == eSystem ? FieldUnit::CM : FieldUnit::INCH; + } + + return VCLUnoHelper::ConvertToMeasurementUnit( eUnit, 1 ); + } + + PropertyHandlerComponent::PropertyHandlerComponent( const Reference< XComponentContext >& _rxContext ) + :PropertyHandler( _rxContext ) + { + } + + IMPLEMENT_FORWARD_XINTERFACE2( PropertyHandlerComponent, PropertyHandler, PropertyHandlerComponent_Base ) + IMPLEMENT_FORWARD_XTYPEPROVIDER2( PropertyHandlerComponent, PropertyHandler, PropertyHandlerComponent_Base ) + + sal_Bool SAL_CALL PropertyHandlerComponent::supportsService( const OUString& ServiceName ) + { + return cppu::supportsService(this, ServiceName); + } + +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/propertyhandler.hxx b/extensions/source/propctrlr/propertyhandler.hxx new file mode 100644 index 000000000..3491a89be --- /dev/null +++ b/extensions/source/propctrlr/propertyhandler.hxx @@ -0,0 +1,369 @@ +/* -*- 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 "pcrcommon.hxx" + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/beans/PropertyState.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/Property.hpp> +#include <com/sun/star/script/XTypeConverter.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/inspection/XPropertyHandler.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/implbase1.hxx> +#include <comphelper/uno3.hxx> + +#include <memory> +#include <vector> + +namespace com::sun::star { + namespace inspection { + struct LineDescriptor; + class XPropertyControlFactory; + } +} + +namespace weld { class Window; } + +namespace pcr +{ + + + typedef sal_Int32 PropertyId; + + + //= PropertyHandler + + class OPropertyInfoService; + typedef ::cppu::WeakComponentImplHelper < css::inspection::XPropertyHandler + > PropertyHandler_Base; + /** the base class for property handlers + */ + class PropertyHandler : public ::cppu::BaseMutex + , public PropertyHandler_Base + { + private: + /// cache for getSupportedProperties + mutable StlSyntaxSequence< css::beans::Property > + m_aSupportedProperties; + mutable bool m_bSupportedPropertiesAreKnown; + + private: + /// the property listener which has been registered + PropertyChangeListeners m_aPropertyListeners; + + protected: + /// the context in which the instance was created + css::uno::Reference< css::uno::XComponentContext > m_xContext; + /// the component we're inspecting + css::uno::Reference< css::beans::XPropertySet > m_xComponent; + /// info about our component's properties + css::uno::Reference< css::beans::XPropertySetInfo > m_xComponentPropertyInfo; + /// type converter, needed on various occasions + css::uno::Reference< css::script::XTypeConverter > m_xTypeConverter; + /// access to property meta data + std::unique_ptr< OPropertyInfoService > m_pInfoService; + + protected: + explicit PropertyHandler( + const css::uno::Reference< css::uno::XComponentContext >& _rxContext + ); + virtual ~PropertyHandler() override; + + // default implementations for XPropertyHandler + virtual void SAL_CALL inspect( const css::uno::Reference< css::uno::XInterface >& _rxIntrospectee ) override; + virtual css::uno::Sequence< css::beans::Property > SAL_CALL getSupportedProperties() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupersededProperties( ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getActuatingProperties( ) override; + virtual css::uno::Any SAL_CALL convertToPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rControlValue ) override; + virtual css::uno::Any SAL_CALL convertToControlValue( const OUString& _rPropertyName, const css::uno::Any& _rPropertyValue, const css::uno::Type& _rControlValueType ) override; + virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& _rPropertyName ) override; + virtual css::inspection::LineDescriptor SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override; + virtual sal_Bool SAL_CALL isComposable( const OUString& _rPropertyName ) override; + virtual css::inspection::InteractiveSelectionResult SAL_CALL onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, css::uno::Any& _rData, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) override; + virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) override; + virtual void SAL_CALL addPropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override; + virtual sal_Bool SAL_CALL suspend( sal_Bool _bSuspend ) override; + + // XComponent + DECLARE_XCOMPONENT() + virtual void SAL_CALL disposing() override; + + // own overridables + virtual css::uno::Sequence< css::beans::Property > + doDescribeSupportedProperties() const = 0; + + /// called when XPropertyHandler::inspect has been called, and we thus have a new component to inspect + virtual void onNewComponent(); + + protected: + /** fires the change in a property value to our listener (if any) + @see addPropertyChangeListener + */ + void firePropertyChange( const OUString& _rPropName, PropertyId _nPropId, + const css::uno::Any& _rOldValue, const css::uno::Any& _rNewValue ); + + /** retrieves a window which can be used as parent for dialogs + */ + weld::Window* impl_getDefaultDialogFrame_nothrow() const; + + /** retrieves the property id for a given property name + @throw css::beans::UnknownPropertyException + if the property name is not known to our ->m_pInfoService + */ + PropertyId impl_getPropertyId_throwUnknownProperty( const OUString& _rPropertyName ) const; + + /** retrieves the property id for a given property name + @throw css::uno::RuntimeException + if the property name is not known to our ->m_pInfoService + */ + PropertyId impl_getPropertyId_throwRuntime( const OUString& _rPropertyName ) const; + + + /** retrieves the property id for a given property name + @returns -1 + if the property name is not known to our ->m_pInfoService + */ + PropertyId impl_getPropertyId_nothrow( const OUString& _rPropertyName ) const; + + // helper for implementing doDescribeSupportedProperties + /** adds a description for the given string property to the given property vector + Most probably to be called from within getSupportedProperties + */ + inline void addStringPropertyDescription( + std::vector< css::beans::Property >& _rProperties, + const OUString& _rPropertyName + ) const; + + /** adds a description for the given int32 property to the given property vector + */ + inline void addInt32PropertyDescription( + std::vector< css::beans::Property >& _rProperties, + const OUString& _rPropertyName, + sal_Int16 _nAttribs = 0 + ) const; + + /** adds a description for the given int16 property to the given property vector + */ + inline void addInt16PropertyDescription( + std::vector< css::beans::Property >& _rProperties, + const OUString& _rPropertyName, + sal_Int16 _nAttribs = 0 + ) const; + + /** adds a description for the given double property to the given property vector + */ + inline void addDoublePropertyDescription( + std::vector< css::beans::Property >& _rProperties, + const OUString& _rPropertyName, + sal_Int16 _nAttribs + ) const; + + /** adds a description for the given date property to the given property vector + */ + inline void addDatePropertyDescription( + std::vector< css::beans::Property >& _rProperties, + const OUString& _rPropertyName, + sal_Int16 _nAttribs + ) const; + + /** adds a description for the given time property to the given property vector + */ + inline void addTimePropertyDescription( + std::vector< css::beans::Property >& _rProperties, + const OUString& _rPropertyName, + sal_Int16 _nAttribs + ) const; + + /** adds a description for the given DateTime property to the given property vector + */ + inline void addDateTimePropertyDescription( + std::vector< css::beans::Property >& _rProperties, + const OUString& _rPropertyName, + sal_Int16 _nAttribs + ) const; + + /// adds a Property, given by name only, to a given vector of Properties + void implAddPropertyDescription( + std::vector< css::beans::Property >& _rProperties, + const OUString& _rPropertyName, + const css::uno::Type& _rType, + sal_Int16 _nAttribs = 0 + ) const; + + + // helper for accessing and maintaining meta data about our supported properties + + /** retrieves a property given by handle + + @return + a pointer to the descriptor for the given properties, if it is one of our + supported properties, <NULL/> else. + + @see doDescribeSupportedProperties + @see impl_getPropertyFromId_throw + */ + const css::beans::Property* + impl_getPropertyFromId_nothrow( PropertyId _nPropId ) const; + + /** retrieves a property given by handle + + @throws UnknownPropertyException + if the handler does not support a property with the given handle + + @seealso doDescribeSupportedProperties + @see impl_getPropertyFromId_nothrow + */ + const css::beans::Property& + impl_getPropertyFromId_throw( PropertyId _nPropId ) const; + + /** determines whether a given property id is part of our supported properties + @see getSupportedProperties + @see doDescribeSupportedProperties + */ + bool impl_isSupportedProperty_nothrow( PropertyId _nPropId ) const + { + return impl_getPropertyFromId_nothrow( _nPropId ) != nullptr; + } + + /** retrieves a property given by name + + @throws UnknownPropertyException + if the handler does not support a property with the given name + + @seealso doDescribeSupportedProperties + */ + const css::beans::Property& + impl_getPropertyFromName_throw( const OUString& _rPropertyName ) const; + + /** get the name of a property given by handle + */ + inline OUString + impl_getPropertyNameFromId_nothrow( PropertyId _nPropId ) const; + + /** returns the value of the ContextDocument property in the ComponentContext which was used to create + this handler. + */ + css::uno::Reference< css::frame::XModel > + impl_getContextDocument_nothrow() const + { + return css::uno::Reference< css::frame::XModel >( + m_xContext->getValueByName( "ContextDocument" ), css::uno::UNO_QUERY ); + } + + /** marks the context document as modified + + @see impl_getContextDocument_nothrow + */ + void impl_setContextDocumentModified_nothrow() const; + + /// determines whether our component has a given property + bool impl_componentHasProperty_throw( const OUString& _rPropName ) const; + + /** determines the default measure unit for the document in which our component lives + */ + sal_Int16 impl_getDocumentMeasurementUnit_throw() const; + + private: + PropertyHandler( const PropertyHandler& ) = delete; + PropertyHandler& operator=( const PropertyHandler& ) = delete; + }; + + + inline void PropertyHandler::addStringPropertyDescription( std::vector< css::beans::Property >& _rProperties, const OUString& _rPropertyName ) const + { + implAddPropertyDescription( _rProperties, _rPropertyName, ::cppu::UnoType<OUString>::get() ); + } + + inline void PropertyHandler::addInt32PropertyDescription( std::vector< css::beans::Property >& _rProperties, const OUString& _rPropertyName, sal_Int16 _nAttribs ) const + { + implAddPropertyDescription( _rProperties, _rPropertyName, ::cppu::UnoType<sal_Int32>::get(), _nAttribs ); + } + + inline void PropertyHandler::addInt16PropertyDescription( std::vector< css::beans::Property >& _rProperties, const OUString& _rPropertyName, sal_Int16 _nAttribs ) const + { + implAddPropertyDescription( _rProperties, _rPropertyName, ::cppu::UnoType<sal_Int16>::get(), _nAttribs ); + } + + inline void PropertyHandler::addDoublePropertyDescription( std::vector< css::beans::Property >& _rProperties, const OUString& _rPropertyName, sal_Int16 _nAttribs ) const + { + implAddPropertyDescription( _rProperties, _rPropertyName, ::cppu::UnoType<double>::get(), _nAttribs ); + } + + inline void PropertyHandler::addDatePropertyDescription( std::vector< css::beans::Property >& _rProperties, const OUString& _rPropertyName, sal_Int16 _nAttribs ) const + { + implAddPropertyDescription( _rProperties, _rPropertyName, ::cppu::UnoType<css::util::Date>::get(), _nAttribs ); + } + + inline void PropertyHandler::addTimePropertyDescription( std::vector< css::beans::Property >& _rProperties, const OUString& _rPropertyName, sal_Int16 _nAttribs ) const + { + implAddPropertyDescription( _rProperties, _rPropertyName, ::cppu::UnoType<css::util::Time>::get(), _nAttribs ); + } + + inline void PropertyHandler::addDateTimePropertyDescription( std::vector< css::beans::Property >& _rProperties, const OUString& _rPropertyName, sal_Int16 _nAttribs ) const + { + implAddPropertyDescription( _rProperties, _rPropertyName, ::cppu::UnoType<css::util::DateTime>::get(), _nAttribs ); + } + + inline OUString PropertyHandler::impl_getPropertyNameFromId_nothrow( PropertyId _nPropId ) const + { + const css::beans::Property* pProp = impl_getPropertyFromId_nothrow( _nPropId ); + return pProp ? pProp->Name : OUString(); + } + + + //= PropertyHandlerComponent + + typedef ::cppu::ImplHelper1 < css::lang::XServiceInfo + > PropertyHandlerComponent_Base; + /** PropertyHandler implementation which additionally supports XServiceInfo + */ + class PropertyHandlerComponent :public PropertyHandler + ,public PropertyHandlerComponent_Base + { + protected: + explicit PropertyHandlerComponent( + const css::uno::Reference< css::uno::XComponentContext >& _rxContext + ); + + DECLARE_XINTERFACE() + DECLARE_XTYPEPROVIDER() + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override = 0; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) final override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override = 0; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/propertyinfo.hxx b/extensions/source/propctrlr/propertyinfo.hxx new file mode 100644 index 000000000..592557c2a --- /dev/null +++ b/extensions/source/propctrlr/propertyinfo.hxx @@ -0,0 +1,50 @@ +/* -*- 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> +#include <rtl/ustring.hxx> +#include <vector> + + +namespace pcr +{ + + + //= IPropertyInfoService + + class SAL_NO_VTABLE IPropertyInfoService + { + public: + virtual sal_Int32 getPropertyId(const OUString& _rName) const = 0; + virtual OUString getPropertyTranslation(sal_Int32 _nId) const = 0; + virtual OUString getPropertyHelpId(sal_Int32 _nId) const = 0; + virtual sal_Int16 getPropertyPos(sal_Int32 _nId) const = 0; + virtual sal_uInt32 getPropertyUIFlags(sal_Int32 _nId) const = 0; + virtual std::vector< OUString > getPropertyEnumRepresentations(sal_Int32 _nId) const = 0; + + virtual ~IPropertyInfoService() { } + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/propeventtranslation.cxx b/extensions/source/propctrlr/propeventtranslation.cxx new file mode 100644 index 000000000..736c1e06f --- /dev/null +++ b/extensions/source/propctrlr/propeventtranslation.cxx @@ -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 . + */ + +#include "propeventtranslation.hxx" + +#include <com/sun/star/lang/DisposedException.hpp> + + +namespace pcr +{ + + + using ::com::sun::star::beans::PropertyChangeEvent; + using ::com::sun::star::uno::RuntimeException; + using ::com::sun::star::lang::EventObject; + using ::com::sun::star::uno::Reference; + using ::com::sun::star::beans::XPropertyChangeListener; + using ::com::sun::star::uno::XInterface; + using ::com::sun::star::lang::DisposedException; + + + //= PropertyEventTranslation + + + PropertyEventTranslation::PropertyEventTranslation( const Reference< XPropertyChangeListener >& _rxDelegator, + const Reference< XInterface >& _rxTranslatedEventSource ) + :m_xDelegator( _rxDelegator ) + ,m_xTranslatedEventSource( _rxTranslatedEventSource ) + { + if ( !m_xDelegator.is() ) + throw RuntimeException(); + } + + + void SAL_CALL PropertyEventTranslation::propertyChange( const PropertyChangeEvent& evt ) + { + if ( !m_xDelegator.is() ) + throw DisposedException(); + + if ( !m_xTranslatedEventSource.is() ) + m_xDelegator->propertyChange( evt ); + else + { + PropertyChangeEvent aTranslatedEvent( evt ); + aTranslatedEvent.Source = m_xTranslatedEventSource; + m_xDelegator->propertyChange( aTranslatedEvent ); + } + } + + + void SAL_CALL PropertyEventTranslation::disposing( const EventObject& Source ) + { + if ( !m_xDelegator.is() ) + throw DisposedException(); + + if ( !m_xTranslatedEventSource.is() ) + m_xDelegator->disposing( Source ); + else + { + EventObject aTranslatedEvent( Source ); + aTranslatedEvent.Source = m_xTranslatedEventSource; + m_xDelegator->disposing( aTranslatedEvent ); + } + + m_xDelegator.clear(); + m_xTranslatedEventSource.clear(); + } + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/propeventtranslation.hxx b/extensions/source/propctrlr/propeventtranslation.hxx new file mode 100644 index 000000000..43155b8c6 --- /dev/null +++ b/extensions/source/propctrlr/propeventtranslation.hxx @@ -0,0 +1,70 @@ +/* -*- 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/XPropertyChangeListener.hpp> +#include <cppuhelper/implbase.hxx> + + +namespace pcr +{ + + + //= PropertyEventTranslation + + typedef ::cppu::WeakImplHelper < css::beans::XPropertyChangeListener + > PropertyEventTranslation_Base; + + class PropertyEventTranslation : public PropertyEventTranslation_Base + { + css::uno::Reference< css::beans::XPropertyChangeListener > + m_xDelegator; + css::uno::Reference< css::uno::XInterface > + m_xTranslatedEventSource; + + public: + /** constructs the object + @throws NullPointerException + if <arg>_rxDelegator</arg> is <NULL/> + */ + PropertyEventTranslation( + const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxDelegator, + const css::uno::Reference< css::uno::XInterface >& _rxTranslatedEventSource + ); + + const css::uno::Reference< css::beans::XPropertyChangeListener >& + getDelegator() const { return m_xDelegator; } + + protected: + // XPropertyChangeListener + virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override; + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + private: + PropertyEventTranslation( const PropertyEventTranslation& ) = delete; + PropertyEventTranslation& operator=( const PropertyEventTranslation& ) = delete; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/proplinelistener.hxx b/extensions/source/propctrlr/proplinelistener.hxx new file mode 100644 index 000000000..6931a1f33 --- /dev/null +++ b/extensions/source/propctrlr/proplinelistener.hxx @@ -0,0 +1,43 @@ +/* -*- 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 <rtl/ustring.hxx> +#include <com/sun/star/uno/Any.hxx> + +namespace pcr +{ + + + class IPropertyLineListener + { + public: + virtual void Clicked( const OUString& _rName, bool _bPrimary ) = 0; + virtual void Commit( const OUString& _rName, const css::uno::Any& _rVal ) = 0; + + protected: + ~IPropertyLineListener() {} + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/pushbuttonnavigation.cxx b/extensions/source/propctrlr/pushbuttonnavigation.cxx new file mode 100644 index 000000000..449ffd583 --- /dev/null +++ b/extensions/source/propctrlr/pushbuttonnavigation.cxx @@ -0,0 +1,298 @@ +/* -*- 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 "pushbuttonnavigation.hxx" +#include <com/sun/star/beans/XPropertyState.hpp> +#include "formstrings.hxx" +#include <comphelper/extract.hxx> +#include <comphelper/property.hxx> +#include <o3tl/string_view.hxx> +#include <osl/diagnose.h> +#include <tools/diagnose_ex.h> + + +namespace pcr +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::form; + + + namespace + { + const sal_Int32 s_nFirstVirtualButtonType = 1 + sal_Int32(FormButtonType_URL); + + const char* pNavigationURLs[] = + { + ".uno:FormController/moveToFirst", + ".uno:FormController/moveToPrev", + ".uno:FormController/moveToNext", + ".uno:FormController/moveToLast", + ".uno:FormController/saveRecord", + ".uno:FormController/undoRecord", + ".uno:FormController/moveToNew", + ".uno:FormController/deleteRecord", + ".uno:FormController/refreshForm", + nullptr + }; + + sal_Int32 lcl_getNavigationURLIndex( std::u16string_view _rNavURL ) + { + const char** pLookup = pNavigationURLs; + while ( *pLookup ) + { + if ( o3tl::equalsAscii( _rNavURL, *pLookup ) ) + return pLookup - pNavigationURLs; + ++pLookup; + } + return -1; + } + + const char* lcl_getNavigationURL( sal_Int32 _nButtonTypeIndex ) + { + const char** pLookup = pNavigationURLs; + while ( _nButtonTypeIndex-- && *pLookup++ ) + ; + OSL_ENSURE( *pLookup, "lcl_getNavigationURL: invalid index!" ); + return *pLookup; + } + } + + + //= PushButtonNavigation + + + PushButtonNavigation::PushButtonNavigation( const Reference< XPropertySet >& _rxControlModel ) + :m_xControlModel( _rxControlModel ) + ,m_bIsPushButton( false ) + { + OSL_ENSURE( m_xControlModel.is(), "PushButtonNavigation::PushButtonNavigation: invalid control model!" ); + + try + { + m_bIsPushButton = ::comphelper::hasProperty( PROPERTY_BUTTONTYPE, m_xControlModel ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "PushButtonNavigation::PushButtonNavigation" ); + } + } + + + FormButtonType PushButtonNavigation::implGetCurrentButtonType() const + { + sal_Int32 nButtonType = sal_Int32(FormButtonType_PUSH); + if ( !m_xControlModel.is() ) + return static_cast<FormButtonType>(nButtonType); + OSL_VERIFY( ::cppu::enum2int( nButtonType, m_xControlModel->getPropertyValue( PROPERTY_BUTTONTYPE ) ) ); + + if ( nButtonType == sal_Int32(FormButtonType_URL) ) + { + // there's a chance that this is a "virtual" button type + // (which are realized by special URLs) + OUString sTargetURL; + m_xControlModel->getPropertyValue( PROPERTY_TARGET_URL ) >>= sTargetURL; + + sal_Int32 nNavigationURLIndex = lcl_getNavigationURLIndex( sTargetURL ); + if ( nNavigationURLIndex >= 0) + // it actually *is* a virtual button type + nButtonType = s_nFirstVirtualButtonType + nNavigationURLIndex; + } + return static_cast<FormButtonType>(nButtonType); + } + + + Any PushButtonNavigation::getCurrentButtonType() const + { + OSL_ENSURE( m_bIsPushButton, "PushButtonNavigation::getCurrentButtonType: not expected to be called for forms!" ); + Any aReturn; + + try + { + aReturn <<= implGetCurrentButtonType(); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "PushButtonNavigation::getCurrentButtonType" ); + } + return aReturn; + } + + + void PushButtonNavigation::setCurrentButtonType( const Any& _rValue ) const + { + OSL_ENSURE( m_bIsPushButton, "PushButtonNavigation::setCurrentButtonType: not expected to be called for forms!" ); + if ( !m_xControlModel.is() ) + return; + + try + { + sal_Int32 nButtonType = sal_Int32(FormButtonType_PUSH); + OSL_VERIFY( ::cppu::enum2int( nButtonType, _rValue ) ); + OUString sTargetURL; + + bool bIsVirtualButtonType = nButtonType >= s_nFirstVirtualButtonType; + if ( bIsVirtualButtonType ) + { + const char* pURL = lcl_getNavigationURL( nButtonType - s_nFirstVirtualButtonType ); + sTargetURL = OUString::createFromAscii( pURL ); + + nButtonType = sal_Int32(FormButtonType_URL); + } + + m_xControlModel->setPropertyValue( PROPERTY_BUTTONTYPE, Any( static_cast< FormButtonType >( nButtonType ) ) ); + m_xControlModel->setPropertyValue( PROPERTY_TARGET_URL, Any( sTargetURL ) ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "PushButtonNavigation::setCurrentButtonType" ); + } + } + + + PropertyState PushButtonNavigation::getCurrentButtonTypeState( ) const + { + OSL_ENSURE( m_bIsPushButton, "PushButtonNavigation::getCurrentButtonTypeState: not expected to be called for forms!" ); + PropertyState eState = PropertyState_DIRECT_VALUE; + + try + { + Reference< XPropertyState > xStateAccess( m_xControlModel, UNO_QUERY ); + if ( xStateAccess.is() ) + { + // let's see what the model says about the ButtonType property + eState = xStateAccess->getPropertyState( PROPERTY_BUTTONTYPE ); + if ( eState == PropertyState_DIRECT_VALUE ) + { + sal_Int32 nRealButtonType = sal_Int32(FormButtonType_PUSH); + OSL_VERIFY( ::cppu::enum2int( nRealButtonType, m_xControlModel->getPropertyValue( PROPERTY_BUTTONTYPE ) ) ); + // perhaps it's one of the virtual button types? + if ( sal_Int32(FormButtonType_URL) == nRealButtonType ) + { + // yes, it is -> rely on the state of the URL property + eState = xStateAccess->getPropertyState( PROPERTY_TARGET_URL ); + } + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "PushButtonNavigation::getCurrentButtonTypeState" ); + } + + return eState; + } + + + Any PushButtonNavigation::getCurrentTargetURL() const + { + Any aReturn; + if ( !m_xControlModel.is() ) + return aReturn; + + try + { + aReturn = m_xControlModel->getPropertyValue( PROPERTY_TARGET_URL ); + if ( m_bIsPushButton ) + { + FormButtonType nCurrentButtonType = implGetCurrentButtonType(); + bool bIsVirtualButtonType = nCurrentButtonType >= FormButtonType(s_nFirstVirtualButtonType); + if ( bIsVirtualButtonType ) + { + // pretend (to the user) that there's no URL set - since + // virtual button types imply a special (technical) URL which + // the user should not see + aReturn <<= OUString(); + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "PushButtonNavigation::getCurrentTargetURL" ); + } + return aReturn; + } + + + void PushButtonNavigation::setCurrentTargetURL( const Any& _rValue ) const + { + if ( !m_xControlModel.is() ) + return; + + try + { + m_xControlModel->setPropertyValue( PROPERTY_TARGET_URL, _rValue ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "PushButtonNavigation::setCurrentTargetURL" ); + } + } + + + PropertyState PushButtonNavigation::getCurrentTargetURLState( ) const + { + PropertyState eState = PropertyState_DIRECT_VALUE; + + try + { + Reference< XPropertyState > xStateAccess( m_xControlModel, UNO_QUERY ); + if ( xStateAccess.is() ) + { + eState = xStateAccess->getPropertyState( PROPERTY_TARGET_URL ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "PushButtonNavigation::setCurrentTargetURL" ); + } + + return eState; + } + + + bool PushButtonNavigation::currentButtonTypeIsOpenURL() const + { + FormButtonType nButtonType( FormButtonType_PUSH ); + try + { + nButtonType = implGetCurrentButtonType(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + return nButtonType == FormButtonType_URL; + } + + + bool PushButtonNavigation::hasNonEmptyCurrentTargetURL() const + { + OUString sTargetURL; + OSL_VERIFY( getCurrentTargetURL() >>= sTargetURL ); + return !sTargetURL.isEmpty(); + } + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/pushbuttonnavigation.hxx b/extensions/source/propctrlr/pushbuttonnavigation.hxx new file mode 100644 index 000000000..7248fb27d --- /dev/null +++ b/extensions/source/propctrlr/pushbuttonnavigation.hxx @@ -0,0 +1,98 @@ +/* -*- 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/beans/PropertyState.hpp> +#include <com/sun/star/form/FormButtonType.hpp> + + +namespace pcr +{ + + + //= PushButtonNavigation + + class PushButtonNavigation final + { + css::uno::Reference< css::beans::XPropertySet > + m_xControlModel; + bool m_bIsPushButton; + + public: + /** ctor + @param _rxControlModel + the control model which is or will be bound + */ + explicit PushButtonNavigation( + const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel + ); + + /** returns the current value of the "ButtonType" property, taking into account + the "virtual" button types such as "move-to-next-record button". + */ + css::uno::Any + getCurrentButtonType() const; + + /** sets the current value of the "ButtonType" property, taking into account + the "virtual" button types such as "move-to-next-record button". + */ + void setCurrentButtonType( const css::uno::Any& _rValue ) const; + + /** retrieves the current state of the "ButtonType" property, taking into account + the "virtual" button types such as "move-to-next-record button". + */ + css::beans::PropertyState + getCurrentButtonTypeState( ) const; + + /** returns the current value of the "TargetURL" property, taking into account + that some URLs are special values caused by "virtual" ButtonTypes + */ + css::uno::Any + getCurrentTargetURL() const; + + /** sets the current value of the "TargetURL" property, taking into account + that some URLs are special values caused by "virtual" ButtonTypes + */ + void setCurrentTargetURL( const css::uno::Any& _rValue ) const; + + /** retrieves the current state of the "TargetURL" property, taking into account + that some URLs are special values caused by "virtual" ButtonTypes + */ + css::beans::PropertyState + getCurrentTargetURLState( ) const; + + /** determines whether the current button type is FormButtonType_URL + */ + bool currentButtonTypeIsOpenURL() const; + + /** determines whether the TargetURL property does currently denote a non-empty string + */ + bool hasNonEmptyCurrentTargetURL() const; + + private: + css::form::FormButtonType implGetCurrentButtonType() const; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/selectlabeldialog.cxx b/extensions/source/propctrlr/selectlabeldialog.cxx new file mode 100644 index 000000000..c8c0a82a3 --- /dev/null +++ b/extensions/source/propctrlr/selectlabeldialog.cxx @@ -0,0 +1,279 @@ +/* -*- 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 "selectlabeldialog.hxx" +#include <strings.hrc> +#include <bitmaps.hlst> +#include "formbrowsertools.hxx" +#include "formstrings.hxx" +#include "modulepcr.hxx" +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <comphelper/property.hxx> +#include <comphelper/types.hxx> +#include <sal/log.hxx> +#include <tools/debug.hxx> + + +namespace pcr +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::form; + using namespace ::com::sun::star::sdbc; + using namespace ::com::sun::star::lang; + + + // OSelectLabelDialog + OSelectLabelDialog::OSelectLabelDialog(weld::Window* pParent, Reference< XPropertySet > const & _xControlModel) + : GenericDialogController(pParent, "modules/spropctrlr/ui/labelselectiondialog.ui", "LabelSelectionDialog") + , m_xControlModel(_xControlModel) + , m_bLastSelected(false) + , m_bHaveAssignableControl(false) + , m_xMainDesc(m_xBuilder->weld_label("label")) + , m_xControlTree(m_xBuilder->weld_tree_view("control")) + , m_xScratchIter(m_xControlTree->make_iterator()) + , m_xNoAssignment(m_xBuilder->weld_check_button("noassignment")) + { + m_xControlTree->connect_changed(LINK(this, OSelectLabelDialog, OnEntrySelected)); + m_xControlTree->set_size_request(-1, m_xControlTree->get_height_rows(8)); + + // fill the description + OUString sDescription = m_xMainDesc->get_label(); + sal_Int16 nClassID = FormComponentType::CONTROL; + if (::comphelper::hasProperty(PROPERTY_CLASSID, m_xControlModel)) + nClassID = ::comphelper::getINT16(m_xControlModel->getPropertyValue(PROPERTY_CLASSID)); + + sDescription = sDescription.replaceAll("$controlclass$", + GetUIHeadlineName(nClassID, Any(m_xControlModel))); + OUString sName = ::comphelper::getString(m_xControlModel->getPropertyValue(PROPERTY_NAME)); + sDescription = sDescription.replaceAll("$controlname$", sName); + m_xMainDesc->set_label(sDescription); + + // search for the root of the form hierarchy + Reference< XChild > xCont(m_xControlModel, UNO_QUERY); + Reference< XInterface > xSearch( xCont.is() ? xCont->getParent() : Reference< XInterface > ()); + Reference< XResultSet > xParentAsResultSet(xSearch, UNO_QUERY); + while (xParentAsResultSet.is()) + { + xCont.set(xSearch, UNO_QUERY); + xSearch = xCont.is() ? xCont->getParent() : Reference< XInterface > (); + xParentAsResultSet.set(xSearch, UNO_QUERY); + } + + // and insert all entries below this root into the listbox + if (xSearch.is()) + { + // check which service the allowed components must support + sal_Int16 nClassId = 0; + try { nClassId = ::comphelper::getINT16(m_xControlModel->getPropertyValue(PROPERTY_CLASSID)); } catch(...) { } + m_sRequiredService = (FormComponentType::RADIOBUTTON == nClassId) ? OUString(SERVICE_COMPONENT_GROUPBOX) : OUString(SERVICE_COMPONENT_FIXEDTEXT); + m_aRequiredControlImage = (FormComponentType::RADIOBUTTON == nClassId) ? OUString(RID_EXTBMP_GROUPBOX) : OUString(RID_EXTBMP_FIXEDTEXT); + + // calc the currently set label control (so InsertEntries can calc m_xInitialSelection) + Any aCurrentLabelControl( m_xControlModel->getPropertyValue(PROPERTY_CONTROLLABEL) ); + DBG_ASSERT((aCurrentLabelControl.getValueTypeClass() == TypeClass_INTERFACE) || !aCurrentLabelControl.hasValue(), + + "OSelectLabelDialog::OSelectLabelDialog : invalid ControlLabel property !"); + if (aCurrentLabelControl.hasValue()) + aCurrentLabelControl >>= m_xInitialLabelControl; + + // insert the root + OUString sRootName(PcrRes(RID_STR_FORMS)); + m_xControlTree->insert(nullptr, -1, &sRootName, nullptr, + nullptr, nullptr, false, m_xScratchIter.get()); + m_xControlTree->set_image(*m_xScratchIter, RID_EXTBMP_FORMS); + + // build the tree + m_xInitialSelection.reset(); + m_bHaveAssignableControl = false; + std::unique_ptr<weld::TreeIter> xRoot = m_xControlTree->make_iterator(); + m_xControlTree->get_iter_first(*xRoot); + InsertEntries(xSearch, *xRoot); + m_xControlTree->expand_row(*xRoot); + } + + if (m_xInitialSelection) + { + m_xControlTree->scroll_to_row(*m_xInitialSelection); + m_xControlTree->select(*m_xInitialSelection); + } + else + { + m_xControlTree->scroll_to_row(0); + m_xControlTree->unselect_all(); + m_xNoAssignment->set_active(true); + } + + if (!m_bHaveAssignableControl) + { // no controls which can be assigned + m_xNoAssignment->set_active(true); + m_xNoAssignment->set_sensitive(false); + } + + m_xLastSelected = m_xControlTree->make_iterator(nullptr); + + m_xNoAssignment->connect_toggled(LINK(this, OSelectLabelDialog, OnNoAssignmentClicked)); + OnNoAssignmentClicked(*m_xNoAssignment); + } + + OSelectLabelDialog::~OSelectLabelDialog() + { + } + + sal_Int32 OSelectLabelDialog::InsertEntries(const Reference< XInterface > & _xContainer, const weld::TreeIter& rContainerEntry) + { + Reference< XIndexAccess > xContainer(_xContainer, UNO_QUERY); + if (!xContainer.is()) + return 0; + + sal_Int32 nChildren = 0; + OUString sName; + Reference< XPropertySet > xAsSet; + for (sal_Int32 i=0; i<xContainer->getCount(); ++i) + { + xContainer->getByIndex(i) >>= xAsSet; + if (!xAsSet.is()) + { + SAL_INFO("extensions.propctrlr", "OSelectLabelDialog::InsertEntries : strange : a form component which isn't a property set !"); + continue; + } + + if (!::comphelper::hasProperty(PROPERTY_NAME, xAsSet)) + // we need at least a name for displaying ... + continue; + sName = ::comphelper::getString(xAsSet->getPropertyValue(PROPERTY_NAME)); + + // we need to check if the control model supports the required service + Reference< XServiceInfo > xInfo(xAsSet, UNO_QUERY); + if (!xInfo.is()) + continue; + + if (!xInfo->supportsService(m_sRequiredService)) + { // perhaps it is a container + Reference< XIndexAccess > xCont(xAsSet, UNO_QUERY); + if (xCont.is() && xCont->getCount()) + { // yes -> step down + m_xControlTree->insert(&rContainerEntry, -1, &sName, nullptr, + nullptr, nullptr, false, m_xScratchIter.get()); + m_xControlTree->set_image(*m_xScratchIter, RID_EXTBMP_FORM); + auto xIter = m_xControlTree->make_iterator(&rContainerEntry); + m_xControlTree->iter_nth_child(*xIter, nChildren); + sal_Int32 nContChildren = InsertEntries(xCont, *xIter); + if (nContChildren) + { + m_xControlTree->expand_row(*xIter); + ++nChildren; + } + else + { // oops, no valid children -> remove the entry + m_xControlTree->remove(*xIter); + } + } + continue; + } + + // get the label + if (!::comphelper::hasProperty(PROPERTY_LABEL, xAsSet)) + continue; + + OUString sDisplayName = + ::comphelper::getString(xAsSet->getPropertyValue(PROPERTY_LABEL)) + + " (" + sName + ")"; + + // all requirements met -> insert + m_xUserData.emplace_back(new Reference<XPropertySet>(xAsSet)); + OUString sId(weld::toId(m_xUserData.back().get())); + m_xControlTree->insert(&rContainerEntry, -1, &sDisplayName, &sId, nullptr, nullptr, false, m_xScratchIter.get()); + m_xControlTree->set_image(*m_xScratchIter, m_aRequiredControlImage); + + if (m_xInitialLabelControl == xAsSet) + { + m_xInitialSelection = m_xControlTree->make_iterator(&rContainerEntry); + m_xControlTree->iter_nth_child(*m_xInitialSelection, nChildren); + } + + ++nChildren; + m_bHaveAssignableControl = true; + } + + return nChildren; + } + + IMPL_LINK(OSelectLabelDialog, OnEntrySelected, weld::TreeView&, rLB, void) + { + DBG_ASSERT(&rLB == m_xControlTree.get(), "OSelectLabelDialog::OnEntrySelected : where did this come from ?"); + std::unique_ptr<weld::TreeIter> xIter = m_xControlTree->make_iterator(); + bool bSelected = m_xControlTree->get_selected(xIter.get()); + OUString sData = bSelected ? m_xControlTree->get_id(*xIter) : OUString(); + if (!sData.isEmpty()) + m_xSelectedControl.set(*weld::fromId<Reference<XPropertySet>*>(sData)); + m_xNoAssignment->set_active(sData.isEmpty()); + } + + IMPL_LINK(OSelectLabelDialog, OnNoAssignmentClicked, weld::Toggleable&, rButton, void) + { + DBG_ASSERT(&rButton == m_xNoAssignment.get(), "OSelectLabelDialog::OnNoAssignmentClicked : where did this come from ?"); + + if (m_xNoAssignment->get_active()) + { + m_bLastSelected = m_xControlTree->get_selected(m_xLastSelected.get()); + } + else + { + DBG_ASSERT(m_bHaveAssignableControl, "OSelectLabelDialog::OnNoAssignmentClicked"); + // search the first assignable entry + auto xSearch = m_xControlTree->make_iterator(nullptr); + bool bSearch = m_xControlTree->get_iter_first(*xSearch); + while (bSearch) + { + if (m_xControlTree->get_id(*xSearch).toInt64()) + break; + bSearch = m_xControlTree->iter_next(*xSearch); + } + // and select it + if (bSearch) + { + m_xControlTree->copy_iterator(*xSearch, *m_xLastSelected); + m_xControlTree->select(*m_xLastSelected); + m_bLastSelected = true; + } + } + + if (m_bLastSelected) + { + if (!m_xNoAssignment->get_active()) + m_xControlTree->select(*m_xLastSelected); + else + m_xControlTree->unselect(*m_xLastSelected); + } + } +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/selectlabeldialog.hxx b/extensions/source/propctrlr/selectlabeldialog.hxx new file mode 100644 index 000000000..9affa3512 --- /dev/null +++ b/extensions/source/propctrlr/selectlabeldialog.hxx @@ -0,0 +1,62 @@ +/* -*- 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 <vcl/weld.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> + +namespace pcr +{ + // OSelectLabelDialog + class OSelectLabelDialog final : public weld::GenericDialogController + { + css::uno::Reference< css::beans::XPropertySet > m_xControlModel; + OUString m_sRequiredService; + OUString m_aRequiredControlImage; + std::unique_ptr<weld::TreeIter> m_xInitialSelection; + // the entry data of the listbox entries + std::vector<std::unique_ptr<css::uno::Reference<css::beans::XPropertySet>>> m_xUserData; + css::uno::Reference< css::beans::XPropertySet > m_xInitialLabelControl; + + css::uno::Reference< css::beans::XPropertySet > m_xSelectedControl; + std::unique_ptr<weld::TreeIter> m_xLastSelected; + bool m_bLastSelected; + bool m_bHaveAssignableControl; + + std::unique_ptr<weld::Label> m_xMainDesc; + std::unique_ptr<weld::TreeView> m_xControlTree; + std::unique_ptr<weld::TreeIter> m_xScratchIter; + std::unique_ptr<weld::CheckButton> m_xNoAssignment; + + public: + OSelectLabelDialog(weld::Window* pParent, css::uno::Reference< css::beans::XPropertySet > const & _xControlModel); + virtual ~OSelectLabelDialog() override; + + css::uno::Reference< css::beans::XPropertySet > GetSelected() const { return m_xNoAssignment->get_active() ? css::uno::Reference< css::beans::XPropertySet > () : m_xSelectedControl; } + + private: + sal_Int32 InsertEntries(const css::uno::Reference< css::uno::XInterface >& _xContainer, const weld::TreeIter& rContainerEntry); + + DECL_LINK(OnEntrySelected, weld::TreeView&, void); + DECL_LINK(OnNoAssignmentClicked, weld::Toggleable&, void); + }; +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/sqlcommanddesign.cxx b/extensions/source/propctrlr/sqlcommanddesign.cxx new file mode 100644 index 000000000..73e205be4 --- /dev/null +++ b/extensions/source/propctrlr/sqlcommanddesign.cxx @@ -0,0 +1,355 @@ +/* -*- 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 "sqlcommanddesign.hxx" +#include "formstrings.hxx" +#include <command.hrc> +#include "modulepcr.hxx" +#include "unourl.hxx" + +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XTitle.hpp> +#include <com/sun/star/frame/XComponentLoader.hpp> +#include <com/sun/star/lang/NullPointerException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/frame/FrameSearchFlag.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/sdb/CommandType.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <tools/diagnose_ex.h> +#include <osl/diagnose.h> + + +namespace pcr +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::beans::PropertyChangeEvent; + using ::com::sun::star::uno::RuntimeException; + using ::com::sun::star::frame::XFrame; + using ::com::sun::star::awt::XTopWindow; + using ::com::sun::star::awt::XWindow; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::beans::PropertyValue; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::lang::XComponent; + using ::com::sun::star::frame::XComponentLoader; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::frame::XTitle; + using ::com::sun::star::lang::EventObject; + using ::com::sun::star::lang::NullPointerException; + using ::com::sun::star::lang::DisposedException; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::frame::XFrames; + using ::com::sun::star::util::XCloseable; + using ::com::sun::star::lang::XMultiServiceFactory; + using ::com::sun::star::frame::XDispatchProvider; + using ::com::sun::star::frame::XDispatch; + using ::com::sun::star::frame::Desktop; + using ::com::sun::star::frame::XDesktop2; + + namespace FrameSearchFlag = ::com::sun::star::frame::FrameSearchFlag; + namespace CommandType = ::com::sun::star::sdb::CommandType; + + + //= ISQLCommandAdapter + + + ISQLCommandAdapter::~ISQLCommandAdapter() + { + } + + + //= SQLCommandDesigner + + + SQLCommandDesigner::SQLCommandDesigner( const Reference< XComponentContext >& _rxContext, + const ::rtl::Reference< ISQLCommandAdapter >& _rxPropertyAdapter, + const ::dbtools::SharedConnection& _rConnection, const Link<SQLCommandDesigner&,void>& _rCloseLink ) + :m_xContext( _rxContext ) + ,m_xConnection( _rConnection ) + ,m_xObjectAdapter( _rxPropertyAdapter ) + ,m_aCloseLink( _rCloseLink ) + { + if ( m_xContext.is() ) + m_xORB = m_xContext->getServiceManager(); + if ( !m_xORB.is() || !_rxPropertyAdapter.is() || !m_xConnection.is() ) + throw NullPointerException(); + + impl_doOpenDesignerFrame_nothrow(); + } + + + SQLCommandDesigner::~SQLCommandDesigner() + { + } + + + void SAL_CALL SQLCommandDesigner::propertyChange( const PropertyChangeEvent& Event ) + { + OSL_ENSURE( m_xDesigner.is() && ( Event.Source == m_xDesigner ), "SQLCommandDesigner::propertyChange: where did this come from?" ); + + if ( !(m_xDesigner.is() && ( Event.Source == m_xDesigner )) ) + return; + + try + { + if ( PROPERTY_ACTIVECOMMAND == Event.PropertyName ) + { + OUString sCommand; + OSL_VERIFY( Event.NewValue >>= sCommand ); + m_xObjectAdapter->setSQLCommand( sCommand ); + } + else if ( PROPERTY_ESCAPE_PROCESSING == Event.PropertyName ) + { + bool bEscapeProcessing( false ); + OSL_VERIFY( Event.NewValue >>= bEscapeProcessing ); + m_xObjectAdapter->setEscapeProcessing( bEscapeProcessing ); + } + } + catch( const RuntimeException& ) { throw; } + catch( const Exception& ) + { + // not allowed to leave, so silence it + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + + void SAL_CALL SQLCommandDesigner::disposing( const EventObject& Source ) + { + if ( m_xDesigner.is() && ( Source.Source == m_xDesigner ) ) + { + m_aCloseLink.Call( *this ); + m_xDesigner.clear(); + } + } + + + void SQLCommandDesigner::dispose() + { + if ( impl_isDisposed() ) + return; + + if ( isActive() ) + impl_closeDesigner_nothrow(); + + m_xConnection.clear(); + m_xContext.clear(); + m_xORB.clear(); + } + + + void SQLCommandDesigner::impl_checkDisposed_throw() const + { + if ( impl_isDisposed() ) + throw DisposedException(); + } + + + void SQLCommandDesigner::raise() const + { + impl_checkDisposed_throw(); + impl_raise_nothrow(); + } + + + bool SQLCommandDesigner::suspend() const + { + impl_checkDisposed_throw(); + return impl_trySuspendDesigner_nothrow(); + } + + + void SQLCommandDesigner::impl_raise_nothrow() const + { + OSL_PRECOND( isActive(), "SQLCommandDesigner::impl_raise_nothrow: not active!" ); + if ( !isActive() ) + return; + + try + { + // activate the frame for this component + Reference< XFrame > xFrame( m_xDesigner->getFrame(), css::uno::UNO_SET_THROW ); + Reference< XWindow > xWindow( xFrame->getContainerWindow(), css::uno::UNO_SET_THROW ); + Reference< XTopWindow > xTopWindow( xWindow, UNO_QUERY_THROW ); + + xTopWindow->toFront(); + xWindow->setFocus(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + + void SQLCommandDesigner::impl_doOpenDesignerFrame_nothrow() + { + OSL_PRECOND( !isActive(), + "SQLCommandDesigner::impl_doOpenDesignerFrame_nothrow: already active!" ); + OSL_PRECOND( m_xConnection.is(), "SQLCommandDesigner::impl_doOpenDesignerFrame_nothrow: this will crash!" ); + osl_atomic_increment(&m_refCount); + + try + { + // for various reasons, we don't want the new frame to appear in the desktop's frame list + // thus, we create a blank frame at the desktop, remove it from the desktop's frame list + // immediately, and then load the component into this blank (and now parent-less) frame + Reference< XComponentLoader > xLoader( impl_createEmptyParentlessTask_nothrow(), UNO_QUERY_THROW ); + const bool bEscapeProcessing = m_xObjectAdapter->getEscapeProcessing(); + Sequence< PropertyValue > aArgs{ + comphelper::makePropertyValue(PROPERTY_ACTIVE_CONNECTION, m_xConnection.getTyped()), + comphelper::makePropertyValue(PROPERTY_COMMAND, m_xObjectAdapter->getSQLCommand()), + comphelper::makePropertyValue(PROPERTY_COMMANDTYPE, CommandType::COMMAND), + comphelper::makePropertyValue(PROPERTY_ESCAPE_PROCESSING, bEscapeProcessing), + comphelper::makePropertyValue("GraphicalDesign", bEscapeProcessing) + }; + + Reference< XComponent > xQueryDesign = xLoader->loadComponentFromURL( + ".component:DB/QueryDesign", + "_self", + FrameSearchFlag::TASKS | FrameSearchFlag::CREATE, + aArgs + ); + + // remember this newly loaded component - we need to care for it e.g. when we're suspended + m_xDesigner.set(xQueryDesign, css::uno::UNO_QUERY); + OSL_ENSURE( m_xDesigner.is() || !xQueryDesign.is(), "SQLCommandDesigner::impl_doOpenDesignerFrame_nothrow: the component is expected to be a controller!" ); + if ( m_xDesigner.is() ) + { + Reference< XPropertySet > xQueryDesignProps( m_xDesigner, UNO_QUERY ); + OSL_ENSURE( xQueryDesignProps.is(), "SQLCommandDesigner::impl_doOpenDesignerFrame_nothrow: the controller should have properties!" ); + if ( xQueryDesignProps.is() ) + { + xQueryDesignProps->addPropertyChangeListener( PROPERTY_ACTIVECOMMAND, this ); + xQueryDesignProps->addPropertyChangeListener( PROPERTY_ESCAPE_PROCESSING, this ); + } + } + + // get the frame which we just opened and set its title + Reference< XTitle> xTitle(xQueryDesign,UNO_QUERY); + if ( xTitle.is() ) + { + OUString sDisplayName = PcrRes(RID_RSC_ENUM_COMMAND_TYPE[CommandType::COMMAND]); + xTitle->setTitle(sDisplayName); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + m_xDesigner.clear(); + } + osl_atomic_decrement(&m_refCount); + } + + + Reference< XFrame > SQLCommandDesigner::impl_createEmptyParentlessTask_nothrow( ) const + { + OSL_PRECOND( m_xORB.is(), "SQLCommandDesigner::impl_createEmptyParentlessTask_nothrow: this will crash!" ); + + Reference< XFrame > xFrame; + try + { + Reference< XDesktop2 > xDesktop = Desktop::create(m_xContext); + + Reference< XFrames > xDesktopFramesCollection( xDesktop->getFrames(), css::uno::UNO_SET_THROW ); + xFrame = xDesktop->findFrame( "_blank", FrameSearchFlag::CREATE ); + OSL_ENSURE( xFrame.is(), "SQLCommandDesigner::impl_createEmptyParentlessTask_nothrow: could not create an empty frame!" ); + xDesktopFramesCollection->remove( xFrame ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + return xFrame; + } + + + void SQLCommandDesigner::impl_closeDesigner_nothrow() + { + OSL_PRECOND( isActive(), "SQLCommandDesigner::impl_closeDesigner_nothrow: invalid call!" ); + // close it + try + { + // do not listen anymore... + Reference< XPropertySet > xProps( m_xDesigner, UNO_QUERY ); + if ( xProps.is() ) + xProps->removePropertyChangeListener( PROPERTY_ACTIVECOMMAND, this ); + + // we need to close the frame via the "user interface", by dispatching a close command, + // instead of calling XCloseable::close directly. The latter method would also close + // the frame, but not care for things like shutting down the office when the last + // frame is gone ... + const UnoURL aCloseURL( ".uno:CloseDoc", + Reference< XMultiServiceFactory >( m_xORB, UNO_QUERY ) ); + + Reference< XDispatchProvider > xProvider( m_xDesigner->getFrame(), UNO_QUERY_THROW ); + Reference< XDispatch > xDispatch( xProvider->queryDispatch( aCloseURL, "_top", FrameSearchFlag::SELF ) ); + OSL_ENSURE( xDispatch.is(), "SQLCommandDesigner::impl_closeDesigner_nothrow: no dispatcher for the CloseDoc command!" ); + if ( xDispatch.is() ) + { + xDispatch->dispatch( aCloseURL, Sequence< PropertyValue >( ) ); + } + else + { + // fallback: use the XCloseable::close (with all possible disadvantages) + Reference< XCloseable > xClose( m_xDesigner->getFrame(), UNO_QUERY ); + if ( xClose.is() ) + xClose->close( true ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + + m_xDesigner.clear(); + } + + + bool SQLCommandDesigner::impl_trySuspendDesigner_nothrow() const + { + OSL_PRECOND( isActive(), "SQLCommandDesigner::impl_trySuspendDesigner_nothrow: no active designer, this will crash!" ); + bool bAllow = true; + try + { + bAllow = m_xDesigner->suspend( true ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + return bAllow; + } + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/sqlcommanddesign.hxx b/extensions/source/propctrlr/sqlcommanddesign.hxx new file mode 100644 index 000000000..a72ba11b0 --- /dev/null +++ b/extensions/source/propctrlr/sqlcommanddesign.hxx @@ -0,0 +1,193 @@ +/* -*- 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/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <connectivity/dbtools.hxx> +#include <tools/link.hxx> +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> +#include <salhelper/simplereferenceobject.hxx> + + +namespace pcr +{ + + + class ISQLCommandAdapter; + + //= SQLCommandDesigner + + typedef ::cppu::WeakImplHelper < css::beans::XPropertyChangeListener + > SQLCommandDesigner_Base; + /** encapsulates the code for calling and managing a query design frame, used + for interactively designing the Command property of a ->RowSet + */ + class SQLCommandDesigner final : public SQLCommandDesigner_Base + { + private: + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::lang::XMultiComponentFactory > m_xORB; + ::dbtools::SharedConnection m_xConnection; + css::uno::Reference< css::frame::XController > m_xDesigner; + ::rtl::Reference< ISQLCommandAdapter > m_xObjectAdapter; + Link<SQLCommandDesigner&,void> m_aCloseLink; + + public: + /** creates the instance, and immediately opens the SQL command design frame + + @param _rxContext + our component context. Must not be <NULL/>, and must provide a non-<NULL/> XMultiComponentFactory + @param _rxPropertyAdapter + an adapter to the object's SQL command related properties + @param _rConnection + the current connection of ->_rxRowSet. Must not be <NULL/>. + @param _rCloseLink + link to call when the component has been closed + @throws css::lang::NullPointerException + if any of the arguments (except ->_rCloseLink) is <NULL/>, or if the component context + does not provide a valid component factory. + */ + SQLCommandDesigner( + const css::uno::Reference< css::uno::XComponentContext >& _rxContext, + const ::rtl::Reference< ISQLCommandAdapter >& _rxPropertyAdapter, + const ::dbtools::SharedConnection& _rConnection, + const Link<SQLCommandDesigner&,void>& _rCloseLink + ); + + /** determines whether the SQL Command designer is currently active, i.e. + if there currently exists a frame which allows the user entering the SQL command + */ + bool isActive() const { return m_xDesigner.is(); } + + /** returns the property adapter used by the instance + */ + const ::rtl::Reference< ISQLCommandAdapter >& getPropertyAdapter() const { return m_xObjectAdapter; } + + /** raises the designer window to top + @precond + the designer is active (->isActive) + @precond + the instance is not disposed + */ + void raise() const; + + /** suspends the designer + @precond + the designer is active (->isActive) + @precond + the instance is not disposed + */ + bool suspend() const; + + /** disposes the instance so that it becomes non-functional + */ + void dispose(); + + private: + // XPropertyChangeListener + virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + virtual ~SQLCommandDesigner() override; + + /** opens a new frame for interactively designing an SQL command + @precond + the designer is not currently active (see ->isActive) + @precond + ->m_xConnection is not <NULL/> + */ + void impl_doOpenDesignerFrame_nothrow(); + + /** impl-version of ->raise + */ + void impl_raise_nothrow() const; + + /** determines whether we are already disposed + */ + bool impl_isDisposed() const + { + return !m_xContext.is(); + } + /** checks whether we are already disposed + @throws css::lang::DisposedException + if we in fact are disposed + */ + void impl_checkDisposed_throw() const; + + /** create an empty top-level frame, which does not belong to the desktop's frame list + @precond + ->m_xORB is not <NULL/> + */ + css::uno::Reference< css::frame::XFrame > + impl_createEmptyParentlessTask_nothrow() const; + + /** closes the component denoted by m_xDesigner + @precond + our designer component is actually active (->isActive) + @precond + we're not disposed already + */ + void impl_closeDesigner_nothrow(); + + /** suspends our designer component + @precond + the designer component is actually active (->isActive) + @return + <TRUE/> if the suspension was successful, <FALSE/> if it was vetoed + */ + bool impl_trySuspendDesigner_nothrow() const; + + SQLCommandDesigner( const SQLCommandDesigner& ) = delete; + SQLCommandDesigner& operator=( const SQLCommandDesigner& ) = delete; + }; + + + //= ISQLCommandAdapter + + /** an adapter to forward changed SQL command property values to a component + */ + class ISQLCommandAdapter : public salhelper::SimpleReferenceObject + { + public: + /// retrieves the current SQL command of the component + virtual OUString getSQLCommand() const = 0; + /// retrieves the current value of the EscapeProcessing property of the component + virtual bool getEscapeProcessing() const = 0; + + /// sets a new SQL command + virtual void setSQLCommand( const OUString& _rCommand ) const = 0; + /// sets a new EscapeProcessing property value + virtual void setEscapeProcessing( const bool _bEscapeProcessing ) const = 0; + + virtual ~ISQLCommandAdapter() override; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/standardcontrol.cxx b/extensions/source/propctrlr/standardcontrol.cxx new file mode 100644 index 000000000..95b4143c7 --- /dev/null +++ b/extensions/source/propctrlr/standardcontrol.cxx @@ -0,0 +1,865 @@ +/* -*- 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 "standardcontrol.hxx" +#include "pcrcommon.hxx" + +#include <com/sun/star/beans/IllegalTypeException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/Color.hpp> +#include <com/sun/star/util/MeasureUnit.hpp> +#include <com/sun/star/inspection/PropertyControlType.hpp> +#include <comphelper/string.hxx> +#include <o3tl/float_int_conversion.hxx> +#include <toolkit/helper/vclunohelper.hxx> + + +// ugly dependencies for the OColorControl +#include <svx/svxids.hrc> + +#include <tools/datetime.hxx> +#include <unotools/datetime.hxx> +#include <o3tl/string_view.hxx> + +#include <limits> +#include <memory> + +namespace pcr +{ + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::inspection; + + //= OTimeControl + OTimeControl::OTimeControl(std::unique_ptr<weld::FormattedSpinButton> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly) + : OTimeControl_Base(PropertyControlType::TimeField, std::move(xBuilder), std::move(xWidget), bReadOnly) + , m_xFormatter(new weld::TimeFormatter(*getTypedControlWindow())) + { + m_xFormatter->SetExtFormat(ExtTimeFieldFormat::LongDuration); + } + + void SAL_CALL OTimeControl::setValue( const Any& _rValue ) + { + util::Time aUNOTime; + if ( !( _rValue >>= aUNOTime ) ) + { + getTypedControlWindow()->set_text(""); + m_xFormatter->SetTime(tools::Time(tools::Time::EMPTY)); + } + else + { + m_xFormatter->SetTime(::tools::Time(aUNOTime)); + } + } + + Any SAL_CALL OTimeControl::getValue() + { + Any aPropValue; + if ( !getTypedControlWindow()->get_text().isEmpty() ) + { + aPropValue <<= m_xFormatter->GetTime().GetUNOTime(); + } + return aPropValue; + } + + Type SAL_CALL OTimeControl::getValueType() + { + return ::cppu::UnoType<util::Time>::get(); + } + + //= ODateControl + ODateControl::ODateControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly) + : ODateControl_Base(PropertyControlType::DateField, std::move(xBuilder), std::move(xWidget), bReadOnly) + , m_xEntry(m_xBuilder->weld_entry("entry")) + , m_xCalendarBox(std::make_unique<SvtCalendarBox>(m_xBuilder->weld_menu_button("button"), false)) + { + m_xEntryFormatter.reset(new weld::DateFormatter(*m_xEntry)); + + m_xEntryFormatter->SetStrictFormat(true); + m_xEntryFormatter->SetMin(::Date(1, 1, 1600)); + m_xEntryFormatter->SetMax(::Date(1, 1, 9999)); + + m_xEntryFormatter->SetExtDateFormat(ExtDateFieldFormat::SystemShortYYYY); + m_xEntryFormatter->EnableEmptyField(true); + + m_xCalendarBox->connect_activated(LINK(this, ODateControl, ActivateHdl)); + + m_xCalendarBox->get_button().connect_toggled(LINK(this, ODateControl, ToggleHdl)); + } + + void SAL_CALL ODateControl::disposing() + { + m_xEntryFormatter.reset(); + m_xEntry.reset(); + m_xCalendarBox.reset(); + ODateControl_Base::disposing(); + } + + void SAL_CALL ODateControl::setValue( const Any& _rValue ) + { + util::Date aUNODate; + if ( !( _rValue >>= aUNODate ) ) + { + m_xEntry->set_text(OUString()); + } + else + { + ::Date aDate( aUNODate.Day, aUNODate.Month, aUNODate.Year ); + m_xEntryFormatter->SetDate(aDate); + } + } + + IMPL_LINK_NOARG(ODateControl, ActivateHdl, SvtCalendarBox&, void) + { + m_xEntryFormatter->SetDate(m_xCalendarBox->get_date()); + setModified(); + m_xEntry->grab_focus(); + } + + IMPL_LINK(ODateControl, ToggleHdl, weld::Toggleable&, rToggle, void) + { + if (!rToggle.get_active()) + return; + ::Date aDate = m_xEntryFormatter->GetDate(); + if (aDate.IsEmpty()) + { + // with an empty date preselect today in the calendar + aDate = ::Date(::Date::SYSTEM); + } + m_xCalendarBox->set_date(aDate); + } + + Any SAL_CALL ODateControl::getValue() + { + Any aPropValue; + if (!m_xEntry->get_text().isEmpty()) + { + ::Date aDate(m_xEntryFormatter->GetDate()); + aPropValue <<= aDate.GetUNODate(); + } + return aPropValue; + } + + Type SAL_CALL ODateControl::getValueType() + { + return ::cppu::UnoType<util::Date>::get(); + } + + //= OEditControl + OEditControl::OEditControl(std::unique_ptr<weld::Entry> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bPW, bool bReadOnly) + : OEditControl_Base( bPW ? PropertyControlType::CharacterField : PropertyControlType::TextField, std::move(xBuilder), std::move(xWidget), bReadOnly ) + { + m_bIsPassword = bPW; + + auto pWidget = getTypedControlWindow(); + pWidget->set_sensitive(true); + pWidget->set_editable(!bReadOnly); + + if (m_bIsPassword) + pWidget->set_max_length( 1 ); + } + + void SAL_CALL OEditControl::setValue( const Any& _rValue ) + { + OUString sText; + if ( m_bIsPassword ) + { + sal_Int16 nValue = 0; + _rValue >>= nValue; + if ( nValue ) + { + sText = OUString(static_cast<sal_Unicode>(nValue)); + } + } + else + _rValue >>= sText; + + getTypedControlWindow()->set_text( sText ); + } + + Any SAL_CALL OEditControl::getValue() + { + Any aPropValue; + + OUString sText( getTypedControlWindow()->get_text() ); + if ( m_bIsPassword ) + { + if ( !sText.isEmpty() ) + aPropValue <<= static_cast<sal_Int16>(sText[0]); + } + else + aPropValue <<= sText; + + return aPropValue; + } + + Type SAL_CALL OEditControl::getValueType() + { + return m_bIsPassword ? ::cppu::UnoType<sal_Int16>::get() : ::cppu::UnoType<OUString>::get(); + } + + void OEditControl::setModified() + { + OEditControl_Base::setModified(); + + // for password controls, we fire a commit for every single change + if ( m_bIsPassword ) + notifyModifiedValue(); + } + + static sal_Int64 ImplCalcLongValue( double nValue, sal_uInt16 nDigits ) + { + double n = nValue; + for ( sal_uInt16 d = 0; d < nDigits; ++d ) + n *= 10; + + return o3tl::saturating_cast<sal_Int64>(n); + } + + static double ImplCalcDoubleValue(sal_Int64 nValue, sal_uInt16 nDigits ) + { + double n = nValue; + for ( sal_uInt16 d = 0; d < nDigits; ++d ) + n /= 10; + return n; + } + + ODateTimeControl::ODateTimeControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly) + : ODateTimeControl_Base(PropertyControlType::DateTimeField, std::move(xBuilder), std::move(xWidget), bReadOnly) + , m_xDate(std::make_unique<SvtCalendarBox>(m_xBuilder->weld_menu_button("datefield"))) + , m_xTime(m_xBuilder->weld_formatted_spin_button("timefield")) + , m_xFormatter(new weld::TimeFormatter(*m_xTime)) + { + m_xFormatter->SetExtFormat(ExtTimeFieldFormat::LongDuration); + } + + void SAL_CALL ODateTimeControl::setValue( const Any& _rValue ) + { + if ( !_rValue.hasValue() ) + { + m_xDate->set_date(::Date(::Date::SYSTEM)); + m_xTime->set_text(""); + m_xFormatter->SetTime(tools::Time(tools::Time::EMPTY)); + } + else + { + util::DateTime aUNODateTime; + OSL_VERIFY( _rValue >>= aUNODateTime ); + + ::DateTime aDateTime( ::DateTime::EMPTY ); + ::utl::typeConvert( aUNODateTime, aDateTime ); + + m_xDate->set_date(aDateTime); + m_xFormatter->SetTime(aDateTime); + } + } + + Any SAL_CALL ODateTimeControl::getValue() + { + Any aPropValue; + if (!m_xTime->get_text().isEmpty()) + { + ::DateTime aDateTime(m_xDate->get_date(), m_xFormatter->GetTime()); + + util::DateTime aUNODateTime; + ::utl::typeConvert( aDateTime, aUNODateTime ); + + aPropValue <<= aUNODateTime; + } + return aPropValue; + } + + Type SAL_CALL ODateTimeControl::getValueType() + { + return ::cppu::UnoType<util::DateTime>::get(); + } + + //= OHyperlinkControl + OHyperlinkControl::OHyperlinkControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly) + : OHyperlinkControl_Base(PropertyControlType::HyperlinkField, std::move(xBuilder), std::move(xWidget), bReadOnly) + , m_xEntry(m_xBuilder->weld_entry("entry")) + , m_xButton(m_xBuilder->weld_button("button")) + , m_aActionListeners(m_aMutex) + { + auto pWidget = getTypedControlWindow(); + pWidget->set_sensitive(true); + m_xEntry->set_editable(!bReadOnly); + + m_xButton->connect_clicked(LINK(this, OHyperlinkControl, OnHyperlinkClicked)); + } + + Any SAL_CALL OHyperlinkControl::getValue() + { + OUString sText = m_xEntry->get_text(); + return Any( sText ); + } + + void SAL_CALL OHyperlinkControl::setValue( const Any& _value ) + { + OUString sText; + _value >>= sText; + m_xEntry->set_text( sText ); + } + + Type SAL_CALL OHyperlinkControl::getValueType() + { + return ::cppu::UnoType<OUString>::get(); + } + + void SAL_CALL OHyperlinkControl::addActionListener( const Reference< XActionListener >& listener ) + { + if ( listener.is() ) + m_aActionListeners.addInterface( listener ); + } + + void SAL_CALL OHyperlinkControl::removeActionListener( const Reference< XActionListener >& listener ) + { + m_aActionListeners.removeInterface( listener ); + } + + void SAL_CALL OHyperlinkControl::disposing() + { + m_xButton.reset(); + m_xEntry.reset(); + OHyperlinkControl_Base::disposing(); + + EventObject aEvent( *this ); + m_aActionListeners.disposeAndClear( aEvent ); + } + + IMPL_LINK_NOARG( OHyperlinkControl, OnHyperlinkClicked, weld::Button&, void ) + { + ActionEvent aEvent( *this, "clicked" ); + m_aActionListeners.forEach< XActionListener >( + [&aEvent] (uno::Reference<awt::XActionListener> const& xListener) + { return xListener->actionPerformed(aEvent); }); + } + + //= ONumericControl + ONumericControl::ONumericControl(std::unique_ptr<weld::MetricSpinButton> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly) + : ONumericControl_Base(PropertyControlType::NumericField, std::move(xBuilder), std::move(xWidget), bReadOnly) + , m_eValueUnit( FieldUnit::NONE ) + , m_nFieldToUNOValueFactor( 1 ) + { + Optional< double > value( getMaxValue() ); + value.Value = -value.Value; + setMinValue( value ); + } + + ::sal_Int16 SAL_CALL ONumericControl::getDecimalDigits() + { + return getTypedControlWindow()->get_digits(); + } + + void SAL_CALL ONumericControl::setDecimalDigits( ::sal_Int16 decimaldigits ) + { + weld::MetricSpinButton* pControlWindow = getTypedControlWindow(); + sal_Int64 min, max; + pControlWindow->get_range(min, max, FieldUnit::NONE); + pControlWindow->set_digits(decimaldigits); + pControlWindow->set_range(min, max, FieldUnit::NONE); + } + + Optional< double > SAL_CALL ONumericControl::getMinValue() + { + Optional< double > aReturn( true, 0 ); + + sal_Int64 minValue = getTypedControlWindow()->get_min(FieldUnit::NONE); + if ( minValue == std::numeric_limits<sal_Int64>::min() ) + aReturn.IsPresent = false; + else + aReturn.Value = static_cast<double>(minValue); + + return aReturn; + } + + void SAL_CALL ONumericControl::setMinValue( const Optional< double >& _minvalue ) + { + if ( !_minvalue.IsPresent ) + getTypedControlWindow()->set_min( std::numeric_limits<sal_Int64>::min(), FieldUnit::NONE ); + else + getTypedControlWindow()->set_min( impl_apiValueToFieldValue_nothrow( _minvalue.Value ) , m_eValueUnit); + } + + Optional< double > SAL_CALL ONumericControl::getMaxValue() + { + Optional< double > aReturn( true, 0 ); + + sal_Int64 maxValue = getTypedControlWindow()->get_max(FieldUnit::NONE); + if ( maxValue == std::numeric_limits<sal_Int64>::max() ) + aReturn.IsPresent = false; + else + aReturn.Value = static_cast<double>(maxValue); + + return aReturn; + } + + void SAL_CALL ONumericControl::setMaxValue( const Optional< double >& _maxvalue ) + { + if ( !_maxvalue.IsPresent ) + getTypedControlWindow()->set_max( std::numeric_limits<sal_Int64>::max(), FieldUnit::NONE ); + else + getTypedControlWindow()->set_max( impl_apiValueToFieldValue_nothrow( _maxvalue.Value ), m_eValueUnit ); + } + + ::sal_Int16 SAL_CALL ONumericControl::getDisplayUnit() + { + return VCLUnoHelper::ConvertToMeasurementUnit( getTypedControlWindow()->get_unit(), 1 ); + } + + void SAL_CALL ONumericControl::setDisplayUnit( ::sal_Int16 _displayunit ) + { + if ( ( _displayunit < MeasureUnit::MM_100TH ) || ( _displayunit > MeasureUnit::PERCENT ) ) + throw IllegalArgumentException(); + if ( ( _displayunit == MeasureUnit::MM_100TH ) + || ( _displayunit == MeasureUnit::MM_10TH ) + || ( _displayunit == MeasureUnit::INCH_1000TH ) + || ( _displayunit == MeasureUnit::INCH_100TH ) + || ( _displayunit == MeasureUnit::INCH_10TH ) + || ( _displayunit == MeasureUnit::PERCENT ) + ) + throw IllegalArgumentException(); + + sal_Int16 nDummyFactor = 1; + FieldUnit eFieldUnit = VCLUnoHelper::ConvertToFieldUnit( _displayunit, nDummyFactor ); + if ( nDummyFactor != 1 ) + // everything which survived the checks above should result in a factor of 1, i.e., + // it should have a direct counterpart as FieldUnit + throw RuntimeException(); + getTypedControlWindow()->set_unit(eFieldUnit); + } + + ::sal_Int16 SAL_CALL ONumericControl::getValueUnit() + { + return VCLUnoHelper::ConvertToMeasurementUnit( m_eValueUnit, m_nFieldToUNOValueFactor ); + } + + void SAL_CALL ONumericControl::setValueUnit( ::sal_Int16 _valueunit ) + { + if ( ( _valueunit < MeasureUnit::MM_100TH ) || ( _valueunit > MeasureUnit::PERCENT ) ) + throw IllegalArgumentException(); + m_eValueUnit = VCLUnoHelper::ConvertToFieldUnit( _valueunit, m_nFieldToUNOValueFactor ); + } + + void SAL_CALL ONumericControl::setValue( const Any& _rValue ) + { + if ( !_rValue.hasValue() ) + { + getTypedControlWindow()->set_text( "" ); + } + else + { + double nValue( 0 ); + OSL_VERIFY( _rValue >>= nValue ); + auto nControlValue = impl_apiValueToFieldValue_nothrow( nValue ); + getTypedControlWindow()->set_value( nControlValue, m_eValueUnit ); + } + } + + sal_Int64 ONumericControl::impl_apiValueToFieldValue_nothrow( double _nApiValue ) const + { + sal_Int64 nControlValue = ImplCalcLongValue( _nApiValue, getTypedControlWindow()->get_digits() ); + nControlValue /= m_nFieldToUNOValueFactor; + return nControlValue; + } + + double ONumericControl::impl_fieldValueToApiValue_nothrow(sal_Int64 nFieldValue) const + { + double nApiValue = ImplCalcDoubleValue( nFieldValue, getTypedControlWindow()->get_digits() ); + nApiValue *= m_nFieldToUNOValueFactor; + return nApiValue; + } + + Any SAL_CALL ONumericControl::getValue() + { + Any aPropValue; + if ( !getTypedControlWindow()->get_text().isEmpty() ) + { + double nValue = impl_fieldValueToApiValue_nothrow( getTypedControlWindow()->get_value( m_eValueUnit ) ); + aPropValue <<= nValue; + } + return aPropValue; + } + + Type SAL_CALL ONumericControl::getValueType() + { + return ::cppu::UnoType<double>::get(); + } + + //= OColorControl + OColorControl::OColorControl(std::unique_ptr<ColorListBox> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly) + : OColorControl_Base(PropertyControlType::ColorListBox, std::move(xBuilder), std::move(xWidget), bReadOnly) + { + getTypedControlWindow()->SetSlotId(SID_FM_CTL_PROPERTIES); + } + + void SAL_CALL OColorControl::setValue( const Any& _rValue ) + { + css::util::Color nColor = sal_uInt32(COL_TRANSPARENT); + if (_rValue.hasValue()) + _rValue >>= nColor; + getTypedControlWindow()->SelectEntry(::Color(ColorTransparency, nColor)); + } + + Any SAL_CALL OColorControl::getValue() + { + Any aPropValue; + ::Color aRgbCol = getTypedControlWindow()->GetSelectEntryColor(); + if (aRgbCol == COL_TRANSPARENT) + return aPropValue; + aPropValue <<= aRgbCol; + return aPropValue; + } + + Type SAL_CALL OColorControl::getValueType() + { + return ::cppu::UnoType<sal_Int32>::get(); + } + + void OColorControl::setModified() + { + OColorControl_Base::setModified(); + + // fire a commit + notifyModifiedValue(); + } + + //= OListboxControl + OListboxControl::OListboxControl(std::unique_ptr<weld::ComboBox> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly) + : OListboxControl_Base(PropertyControlType::ListBox, std::move(xBuilder), std::move(xWidget), bReadOnly) + { + } + + Any SAL_CALL OListboxControl::getValue() + { + OUString sControlValue( getTypedControlWindow()->get_active_text() ); + + Any aPropValue; + if ( !sControlValue.isEmpty() ) + aPropValue <<= sControlValue; + return aPropValue; + } + + Type SAL_CALL OListboxControl::getValueType() + { + return ::cppu::UnoType<OUString>::get(); + } + + void SAL_CALL OListboxControl::setValue( const Any& _rValue ) + { + if ( !_rValue.hasValue() ) + getTypedControlWindow()->set_active(-1); + else + { + OUString sSelection; + _rValue >>= sSelection; + + if (getTypedControlWindow()->find_text(sSelection) == -1) + getTypedControlWindow()->insert_text(0, sSelection); + + if (sSelection != getTypedControlWindow()->get_active_text()) + getTypedControlWindow()->set_active_text(sSelection); + } + } + + void SAL_CALL OListboxControl::clearList() + { + getTypedControlWindow()->clear(); + } + + void SAL_CALL OListboxControl::prependListEntry( const OUString& NewEntry ) + { + getTypedControlWindow()->insert_text(0, NewEntry); + } + + void SAL_CALL OListboxControl::appendListEntry( const OUString& NewEntry ) + { + getTypedControlWindow()->append_text(NewEntry); + } + + Sequence< OUString > SAL_CALL OListboxControl::getListEntries() + { + const sal_Int32 nCount = getTypedControlWindow()->get_count(); + Sequence< OUString > aRet(nCount); + OUString* pIter = aRet.getArray(); + for (sal_Int32 i = 0; i < nCount ; ++i,++pIter) + *pIter = getTypedControlWindow()->get_text(i); + + return aRet; + } + + void OListboxControl::setModified() + { + OListboxControl_Base::setModified(); + + // fire a commit + notifyModifiedValue(); + } + + //= OComboboxControl + OComboboxControl::OComboboxControl(std::unique_ptr<weld::ComboBox> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly) + : OComboboxControl_Base(PropertyControlType::ComboBox, std::move(xBuilder), std::move(xWidget), bReadOnly) + { + getTypedControlWindow()->connect_changed( LINK( this, OComboboxControl, OnEntrySelected ) ); + } + + void SAL_CALL OComboboxControl::setValue( const Any& _rValue ) + { + OUString sText; + _rValue >>= sText; + weld::ComboBox* pControlWindow = getTypedControlWindow(); + // tdf#138701 leave current cursor valid if the contents won't change + if (pControlWindow->get_active_text() != sText) + pControlWindow->set_entry_text(sText); + } + + Any SAL_CALL OComboboxControl::getValue() + { + return Any( getTypedControlWindow()->get_active_text() ); + } + + Type SAL_CALL OComboboxControl::getValueType() + { + return ::cppu::UnoType<OUString>::get(); + } + + void SAL_CALL OComboboxControl::clearList() + { + getTypedControlWindow()->clear(); + } + + void SAL_CALL OComboboxControl::prependListEntry( const OUString& NewEntry ) + { + getTypedControlWindow()->insert_text(0, NewEntry); + } + + void SAL_CALL OComboboxControl::appendListEntry( const OUString& NewEntry ) + { + getTypedControlWindow()->append_text( NewEntry ); + } + + Sequence< OUString > SAL_CALL OComboboxControl::getListEntries( ) + { + const sal_Int32 nCount = getTypedControlWindow()->get_count(); + Sequence< OUString > aRet(nCount); + OUString* pIter = aRet.getArray(); + for (sal_Int32 i = 0; i < nCount ; ++i,++pIter) + *pIter = getTypedControlWindow()->get_text(i); + + return aRet; + } + + IMPL_LINK_NOARG( OComboboxControl, OnEntrySelected, weld::ComboBox&, void ) + { + // fire a commit + notifyModifiedValue(); + } + + namespace + { + + StlSyntaxSequence< OUString > lcl_convertMultiLineToList( std::u16string_view _rCompsedTextWithLineBreaks ) + { + sal_Int32 nLines = comphelper::string::getTokenCount(_rCompsedTextWithLineBreaks, '\n'); + StlSyntaxSequence< OUString > aStrings( nLines ); + if (nLines) + { + StlSyntaxSequence< OUString >::iterator stringItem = aStrings.begin(); + sal_Int32 nIdx {0}; + do + { + *stringItem = o3tl::getToken(_rCompsedTextWithLineBreaks, 0, '\n', nIdx ); + ++stringItem; + } + while (nIdx>0); + } + return aStrings; + } + + OUString lcl_convertListToMultiLine( const StlSyntaxSequence< OUString >& _rStrings ) + { + OUStringBuffer sMultiLineText; + for ( StlSyntaxSequence< OUString >::const_iterator item = _rStrings.begin(); + item != _rStrings.end(); + ) + { + sMultiLineText.append(*item); + if ( ++item != _rStrings.end() ) + sMultiLineText.append("\n"); + } + return sMultiLineText.makeStringAndClear(); + } + + + OUString lcl_convertListToDisplayText( const StlSyntaxSequence< OUString >& _rStrings ) + { + OUStringBuffer aComposed; + for ( StlSyntaxSequence< OUString >::const_iterator strings = _rStrings.begin(); + strings != _rStrings.end(); + ++strings + ) + { + if ( strings != _rStrings.begin() ) + aComposed.append( ';' ); + aComposed.append( '\"' ); + aComposed.append( *strings ); + aComposed.append( '\"' ); + } + return aComposed.makeStringAndClear(); + } + } + + void OMultilineEditControl::CheckEntryTextViewMisMatch() + { + // if there are newlines or something else which the entry cannot show, then make + // just the multiline dropdown editable as the canonical source for text + m_xEntry->set_sensitive(m_xEntry->get_text() == m_xTextView->get_text()); + } + + void OMultilineEditControl::SetStringListValue(const StlSyntaxSequence<OUString>& rStrings) + { + m_xEntry->set_text(lcl_convertListToDisplayText(rStrings)); + m_xTextView->set_text(lcl_convertListToMultiLine(rStrings)); + CheckEntryTextViewMisMatch(); + } + + StlSyntaxSequence<OUString> OMultilineEditControl::GetStringListValue() const + { + return lcl_convertMultiLineToList(m_xTextView->get_text()); + } + + void OMultilineEditControl::SetTextValue(const OUString& rText) + { + OSL_PRECOND( m_nOperationMode == eMultiLineText, "OMultilineEditControl::SetTextValue: illegal call!" ); + + m_xTextView->set_text(rText); + m_xEntry->set_text(rText); + CheckEntryTextViewMisMatch(); + } + + OUString OMultilineEditControl::GetTextValue() const + { + OSL_PRECOND( m_nOperationMode == eMultiLineText, "OMultilineEditControl::GetTextValue: illegal call!" ); + return m_xTextView->get_text(); + } + + //= OMultilineEditControl + OMultilineEditControl::OMultilineEditControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, MultiLineOperationMode eMode, bool bReadOnly) + : OMultilineEditControl_Base(eMode == eMultiLineText ? PropertyControlType::MultiLineTextField : PropertyControlType::StringListField, + std::move(xBuilder), std::move(xWidget), bReadOnly) + , m_nOperationMode(eMode) + , m_xEntry(m_xBuilder->weld_entry("entry")) + , m_xButton(m_xBuilder->weld_menu_button("button")) + , m_xPopover(m_xBuilder->weld_widget("popover")) + , m_xTextView(m_xBuilder->weld_text_view("textview")) + , m_xOk(m_xBuilder->weld_button("ok")) + { + m_xButton->set_popover(m_xPopover.get()); + m_xTextView->set_size_request(m_xTextView->get_approximate_digit_width() * 30, m_xTextView->get_height_rows(8)); + m_xOk->connect_clicked(LINK(this, OMultilineEditControl, ButtonHandler)); + } + + IMPL_LINK_NOARG(OMultilineEditControl, TextViewModifiedHdl, weld::TextView&, void) + { + // tdf#139070 during editing update the entry to look like how it will + // look once editing is finished so that the default behaviour of vcl + // to strip newlines and the default behaviour of gtk to show a newline + // symbol is suppressed + OUString sText = m_xTextView->get_text(); + auto aSeq = lcl_convertMultiLineToList(sText); + if (aSeq.getLength() > 1) + m_xEntry->set_text(lcl_convertListToDisplayText(aSeq)); + else + m_xEntry->set_text(sText); + CheckEntryTextViewMisMatch(); + setModified(); + } + + void OMultilineEditControl::editChanged() + { + m_xTextView->set_text(m_xEntry->get_text()); + CheckEntryTextViewMisMatch(); + setModified(); + } + + IMPL_LINK_NOARG(OMultilineEditControl, ButtonHandler, weld::Button&, void) + { + m_xButton->set_active(false); + notifyModifiedValue(); + } + + void SAL_CALL OMultilineEditControl::setValue( const Any& _rValue ) + { + impl_checkDisposed_throw(); + + switch (m_nOperationMode) + { + case eMultiLineText: + { + OUString sText; + if ( !( _rValue >>= sText ) && _rValue.hasValue() ) + throw IllegalTypeException(); + SetTextValue(sText); + break; + } + case eStringList: + { + Sequence< OUString > aStringLines; + if ( !( _rValue >>= aStringLines ) && _rValue.hasValue() ) + throw IllegalTypeException(); + SetStringListValue( StlSyntaxSequence<OUString>(aStringLines) ); + break; + } + } + } + + Any SAL_CALL OMultilineEditControl::getValue() + { + impl_checkDisposed_throw(); + + Any aValue; + switch (m_nOperationMode) + { + case eMultiLineText: + aValue <<= GetTextValue(); + break; + case eStringList: + aValue <<= GetStringListValue(); + break; + } + return aValue; + } + + Type SAL_CALL OMultilineEditControl::getValueType() + { + if (m_nOperationMode == eMultiLineText) + return ::cppu::UnoType<OUString>::get(); + return cppu::UnoType<Sequence< OUString >>::get(); + } + +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/standardcontrol.hxx b/extensions/source/propctrlr/standardcontrol.hxx new file mode 100644 index 000000000..fcd194886 --- /dev/null +++ b/extensions/source/propctrlr/standardcontrol.hxx @@ -0,0 +1,412 @@ +/* -*- 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 "commoncontrol.hxx" +#include "pcrcommon.hxx" + +#include <com/sun/star/inspection/XNumericControl.hpp> +#include <com/sun/star/inspection/XStringListControl.hpp> +#include <com/sun/star/inspection/XHyperlinkControl.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <comphelper/interfacecontainer2.hxx> +#include <svtools/ctrlbox.hxx> +#include <svx/colorbox.hxx> + +namespace pcr +{ + //= OTimeControl + typedef CommonBehaviourControl<css::inspection::XPropertyControl, weld::FormattedSpinButton> OTimeControl_Base; + class OTimeControl : public OTimeControl_Base + { + std::unique_ptr<weld::TimeFormatter> m_xFormatter; + public: + OTimeControl(std::unique_ptr<weld::FormattedSpinButton> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly); + + virtual void SAL_CALL disposing() override + { + m_xFormatter.reset(); + OTimeControl_Base::disposing(); + } + + // XPropertyControl + virtual css::uno::Any SAL_CALL getValue() override; + virtual void SAL_CALL setValue( const css::uno::Any& _value ) override; + virtual css::uno::Type SAL_CALL getValueType() override; + + virtual void SetModifyHandler() override + { + OTimeControl_Base::SetModifyHandler(); + getTypedControlWindow()->connect_value_changed( LINK( this, CommonBehaviourControlHelper, TimeModifiedHdl ) ); + } + + virtual weld::Widget* getWidget() override { return getTypedControlWindow(); } + }; + + //= ODateControl + typedef CommonBehaviourControl<css::inspection::XPropertyControl, weld::Container> ODateControl_Base; + class ODateControl : public ODateControl_Base + { + std::unique_ptr<weld::Entry> m_xEntry; + std::unique_ptr<SvtCalendarBox> m_xCalendarBox; + std::unique_ptr<weld::DateFormatter> m_xEntryFormatter; + + DECL_LINK(ActivateHdl, SvtCalendarBox&, void); + DECL_LINK(ToggleHdl, weld::Toggleable&, void); + + public: + ODateControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly); + + // XPropertyControl + virtual css::uno::Any SAL_CALL getValue() override; + virtual void SAL_CALL setValue( const css::uno::Any& _value ) override; + virtual css::uno::Type SAL_CALL getValueType() override; + + virtual void SetModifyHandler() override + { + ODateControl_Base::SetModifyHandler(); + + m_xEntry->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) ); + m_xEntryFormatter->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) ); + m_xCalendarBox->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) ); + m_xCalendarBox->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) ); + + m_xEntryFormatter->connect_changed(LINK(this, CommonBehaviourControlHelper, EditModifiedHdl)); + } + + virtual void SAL_CALL disposing() override; + + virtual weld::Widget* getWidget() override { return getTypedControlWindow(); } + }; + + //= OEditControl + typedef CommonBehaviourControl<css::inspection::XPropertyControl, weld::Entry> OEditControl_Base; + class OEditControl final : public OEditControl_Base + { + bool m_bIsPassword : 1; + + public: + OEditControl(std::unique_ptr<weld::Entry> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bPassWord, bool bReadOnly); + + // XPropertyControl + virtual css::uno::Any SAL_CALL getValue() override; + virtual void SAL_CALL setValue( const css::uno::Any& _value ) override; + virtual css::uno::Type SAL_CALL getValueType() override; + + virtual void SetModifyHandler() override + { + OEditControl_Base::SetModifyHandler(); + getTypedControlWindow()->connect_changed( LINK( this, CommonBehaviourControlHelper, EditModifiedHdl ) ); + } + + private: + // CommonBehaviourControlHelper::modified + virtual void setModified() override; + virtual weld::Widget* getWidget() override { return getTypedControlWindow(); } + }; + + //= ODateTimeControl + typedef CommonBehaviourControl<css::inspection::XPropertyControl, weld::Container> ODateTimeControl_Base; + class ODateTimeControl : public ODateTimeControl_Base + { + private: + std::unique_ptr<SvtCalendarBox> m_xDate; + std::unique_ptr<weld::FormattedSpinButton> m_xTime; + std::unique_ptr<weld::TimeFormatter> m_xFormatter; + + public: + ODateTimeControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly); + + virtual void SetModifyHandler() override + { + m_xDate->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) ); + m_xDate->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) ); + m_xTime->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) ); + m_xTime->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) ); + + m_xDate->connect_selected( LINK( this, CommonBehaviourControlHelper, DateModifiedHdl ) ); + m_xTime->connect_value_changed( LINK( this, CommonBehaviourControlHelper, TimeModifiedHdl ) ); + } + + virtual void SAL_CALL disposing() override + { + m_xFormatter.reset(); + m_xTime.reset(); + m_xDate.reset(); + ODateTimeControl_Base::disposing(); + } + + // XPropertyControl + virtual css::uno::Any SAL_CALL getValue() override; + virtual void SAL_CALL setValue( const css::uno::Any& _value ) override; + virtual css::uno::Type SAL_CALL getValueType() override; + + virtual weld::Widget* getWidget() override { return getTypedControlWindow(); } + }; + + //= OHyperlinkControl + typedef CommonBehaviourControl<css::inspection::XHyperlinkControl, weld::Container> OHyperlinkControl_Base; + class OHyperlinkControl final : public OHyperlinkControl_Base + { + private: + std::unique_ptr<weld::Entry> m_xEntry; + std::unique_ptr<weld::Button> m_xButton; + + ::comphelper::OInterfaceContainerHelper2 m_aActionListeners; + + public: + OHyperlinkControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly); + + // XPropertyControl + virtual css::uno::Any SAL_CALL getValue() override; + virtual void SAL_CALL setValue( const css::uno::Any& _value ) override; + virtual css::uno::Type SAL_CALL getValueType() override; + + virtual void SetModifyHandler() override + { + m_xEntry->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) ); + m_xEntry->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) ); + m_xButton->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) ); + m_xButton->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) ); + + m_xEntry->connect_changed( LINK( this, CommonBehaviourControlHelper, EditModifiedHdl ) ); + } + + virtual weld::Widget* getWidget() override { return getTypedControlWindow(); } + + // XHyperlinkControl + virtual void SAL_CALL addActionListener( const css::uno::Reference< css::awt::XActionListener >& listener ) override; + virtual void SAL_CALL removeActionListener( const css::uno::Reference< css::awt::XActionListener >& listener ) override; + + private: + // XComponent + virtual void SAL_CALL disposing() override; + + DECL_LINK(OnHyperlinkClicked, weld::Button&, void); + }; + + //= ONumericControl + typedef CommonBehaviourControl<css::inspection::XNumericControl, weld::MetricSpinButton> ONumericControl_Base; + class ONumericControl : public ONumericControl_Base + { + private: + FieldUnit m_eValueUnit; + sal_Int16 m_nFieldToUNOValueFactor; + + public: + ONumericControl(std::unique_ptr<weld::MetricSpinButton> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly); + + // XPropertyControl + virtual css::uno::Any SAL_CALL getValue() override; + virtual void SAL_CALL setValue( const css::uno::Any& _value ) override; + virtual css::uno::Type SAL_CALL getValueType() override; + + // XNumericControl + virtual ::sal_Int16 SAL_CALL getDecimalDigits() override; + virtual void SAL_CALL setDecimalDigits( ::sal_Int16 _decimaldigits ) override; + virtual css::beans::Optional< double > SAL_CALL getMinValue() override; + virtual void SAL_CALL setMinValue( const css::beans::Optional< double >& _minvalue ) override; + virtual css::beans::Optional< double > SAL_CALL getMaxValue() override; + virtual void SAL_CALL setMaxValue( const css::beans::Optional< double >& _maxvalue ) override; + virtual ::sal_Int16 SAL_CALL getDisplayUnit() override; + virtual void SAL_CALL setDisplayUnit( ::sal_Int16 _displayunit ) override; + virtual ::sal_Int16 SAL_CALL getValueUnit() override; + virtual void SAL_CALL setValueUnit( ::sal_Int16 _valueunit ) override; + + virtual void SetModifyHandler() override + { + ONumericControl_Base::SetModifyHandler(); + weld::MetricSpinButton* pSpinButton = getTypedControlWindow(); + pSpinButton->connect_value_changed( LINK( this, CommonBehaviourControlHelper, MetricModifiedHdl ) ); + pSpinButton->get_widget().connect_changed( LINK( this, CommonBehaviourControlHelper, EditModifiedHdl ) ); + } + + private: + virtual weld::Widget* getWidget() override { return &getTypedControlWindow()->get_widget(); } + + /** converts an API value (<code>double</code>, as passed into <code>set[Max|Min|]Value) into + a <code>int</code> value which can be passed to our NumericField. + + The conversion respects our decimal digits as well as our value factor (<member>m_nFieldToUNOValueFactor</member>). + */ + sal_Int64 impl_apiValueToFieldValue_nothrow( double nApiValue ) const; + + /** converts a control value, as obtained from our Numeric field, into a value which can passed + to outer callers via our UNO API. + */ + double impl_fieldValueToApiValue_nothrow(sal_Int64 nFieldValue) const; + }; + + //= OColorControl + typedef CommonBehaviourControl<css::inspection::XPropertyControl, ColorListBox> OColorControl_Base; + class OColorControl : public OColorControl_Base + { + public: + OColorControl(std::unique_ptr<ColorListBox> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly); + + // XPropertyControl + virtual css::uno::Any SAL_CALL getValue() override; + virtual void SAL_CALL setValue( const css::uno::Any& _value ) override; + virtual css::uno::Type SAL_CALL getValueType() override; + + virtual void SetModifyHandler() override + { + OColorControl_Base::SetModifyHandler(); + getTypedControlWindow()->SetSelectHdl(LINK(this, CommonBehaviourControlHelper, ColorModifiedHdl)); + } + + protected: + // CommonBehaviourControlHelper::setModified + virtual void setModified() override; + + private: + virtual weld::Widget* getWidget() override { return &getTypedControlWindow()->get_widget(); } + }; + + //= OListboxControl + typedef CommonBehaviourControl<css::inspection::XStringListControl, weld::ComboBox> OListboxControl_Base; + class OListboxControl : public OListboxControl_Base + { + public: + OListboxControl(std::unique_ptr<weld::ComboBox> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly); + + // XPropertyControl + virtual css::uno::Any SAL_CALL getValue() override; + virtual void SAL_CALL setValue( const css::uno::Any& _value ) override; + virtual css::uno::Type SAL_CALL getValueType() override; + + // XStringListControl + virtual void SAL_CALL clearList( ) override; + virtual void SAL_CALL prependListEntry( const OUString& NewEntry ) override; + virtual void SAL_CALL appendListEntry( const OUString& NewEntry ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getListEntries( ) override; + + virtual void SetModifyHandler() override + { + OListboxControl_Base::SetModifyHandler(); + getTypedControlWindow()->connect_changed(LINK(this, CommonBehaviourControlHelper, ModifiedHdl)); + } + + protected: + // CommonBehaviourControlHelper::setModified + virtual void setModified() override; + virtual weld::Widget* getWidget() override { return getTypedControlWindow(); } + }; + + //= OComboboxControl + typedef CommonBehaviourControl< css::inspection::XStringListControl, weld::ComboBox > OComboboxControl_Base; + class OComboboxControl final : public OComboboxControl_Base + { + public: + OComboboxControl(std::unique_ptr<weld::ComboBox> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly); + + // XPropertyControl + virtual css::uno::Any SAL_CALL getValue() override; + virtual void SAL_CALL setValue( const css::uno::Any& _value ) override; + virtual css::uno::Type SAL_CALL getValueType() override; + + // XStringListControl + virtual void SAL_CALL clearList( ) override; + virtual void SAL_CALL prependListEntry( const OUString& NewEntry ) override; + virtual void SAL_CALL appendListEntry( const OUString& NewEntry ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getListEntries( ) override; + + virtual void SetModifyHandler() override + { + OComboboxControl_Base::SetModifyHandler(); + getTypedControlWindow()->connect_changed(LINK(this, CommonBehaviourControlHelper, ModifiedHdl)); + } + + // CommonBehaviourControlHelper::setModified + virtual weld::Widget* getWidget() override { return getTypedControlWindow(); } + + private: + DECL_LINK( OnEntrySelected, weld::ComboBox&, void ); + }; + + + //= DropDownEditControl + + enum MultiLineOperationMode + { + eStringList, + eMultiLineText + }; + + //= OMultilineEditControl + typedef CommonBehaviourControl<css::inspection::XPropertyControl, weld::Container> OMultilineEditControl_Base; + class OMultilineEditControl : public OMultilineEditControl_Base + { + private: + MultiLineOperationMode m_nOperationMode; + std::unique_ptr<weld::Entry> m_xEntry; + std::unique_ptr<weld::MenuButton> m_xButton; + std::unique_ptr<weld::Widget> m_xPopover; + std::unique_ptr<weld::TextView> m_xTextView; + std::unique_ptr<weld::Button> m_xOk; + + void SetTextValue(const OUString& rText); + OUString GetTextValue() const; + + void SetStringListValue( const StlSyntaxSequence< OUString >& _rStrings ); + StlSyntaxSequence< OUString > + GetStringListValue() const; + + DECL_LINK(ButtonHandler, weld::Button&, void); + DECL_LINK(TextViewModifiedHdl, weld::TextView&, void); + + void CheckEntryTextViewMisMatch(); + + public: + OMultilineEditControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, MultiLineOperationMode eMode, bool bReadOnly); + + // XPropertyControl + virtual css::uno::Any SAL_CALL getValue() override; + virtual void SAL_CALL setValue( const css::uno::Any& _value ) override; + virtual css::uno::Type SAL_CALL getValueType() override; + virtual weld::Widget* getWidget() override { return getTypedControlWindow(); } + + virtual void editChanged() override; + + virtual void SetModifyHandler() override + { + m_xEntry->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) ); + m_xEntry->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) ); + m_xButton->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) ); + m_xButton->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) ); + + m_xEntry->connect_changed( LINK( this, CommonBehaviourControlHelper, EditModifiedHdl ) ); + m_xTextView->connect_changed( LINK( this, OMultilineEditControl, TextViewModifiedHdl ) ); + } + + virtual void SAL_CALL disposing() override + { + m_xOk.reset(); + m_xTextView.reset(); + m_xButton.reset(); + m_xEntry.reset(); + OMultilineEditControl_Base::disposing(); + } + + }; + +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/stringrepresentation.cxx b/extensions/source/propctrlr/stringrepresentation.cxx new file mode 100644 index 000000000..143ab1363 --- /dev/null +++ b/extensions/source/propctrlr/stringrepresentation.cxx @@ -0,0 +1,604 @@ +/* -*- 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 <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/inspection/XStringRepresentation.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/script/CannotConvertException.hpp> +#include <com/sun/star/script/XTypeConverter.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/reflection/XConstantsTypeDescription.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <connectivity/dbconversion.hxx> +#include <osl/diagnose.h> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <yesno.hrc> +#include <comphelper/types.hxx> +#include <o3tl/string_view.hxx> +#include "modulepcr.hxx" + +#include <algorithm> + +namespace pcr{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace { + +class StringRepresentation: + public ::cppu::WeakImplHelper< + lang::XServiceInfo, + inspection::XStringRepresentation, + lang::XInitialization> +{ +public: + explicit StringRepresentation(uno::Reference< uno::XComponentContext > const & context); + StringRepresentation (const StringRepresentation&) = delete; + StringRepresentation& operator=(const StringRepresentation&) = delete; + + // lang::XServiceInfo: + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString & ServiceName) override; + virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // inspection::XStringRepresentation: + virtual OUString SAL_CALL convertToControlValue(const uno::Any & PropertyValue) override; + virtual uno::Any SAL_CALL convertToPropertyValue(const OUString & ControlValue, const uno::Type & ControlValueType) override; + + // lang::XInitialization: + virtual void SAL_CALL initialize(const uno::Sequence< uno::Any > & aArguments) override; + +private: + virtual ~StringRepresentation() override {} + + /** converts a generic value into a string representation + + If you want to convert values whose string representation does not depend + on a concrete property, use this version + + @return <TRUE/> + if and only if the value could be converted + */ + static bool convertGenericValueToString( + const uno::Any& _rValue, + OUString& _rStringRep + ); + + /** converts string representation into generic value + + If you want to convert values whose string representation does not depend + on a concrete property, use this version + + @return <TRUE/> + if and only if the value could be converted + */ + static bool convertStringToGenericValue( + const OUString& _rStringRep, + uno::Any& _rValue, + const uno::Type& _rTargetType + ); + + /** uses the simple convert method from the type converter + * + * \param _rValue the value to be converted + * \return the converted string. + */ + OUString convertSimpleToString( const uno::Any& _rValue ); + + /** converts a string into his constant value if it exists, otherwise the type converter is used. + * \param _rValue the value to be converted + * \param _ePropertyType the type of the property to be converted into + * \return the converted value + */ + uno::Any convertStringToSimple( const OUString& _rValue,const uno::TypeClass& _ePropertyType ); + + uno::Reference< uno::XComponentContext > m_xContext; + uno::Reference< script::XTypeConverter > m_xTypeConverter; + uno::Reference< reflection::XConstantsTypeDescription > m_xTypeDescription; + uno::Sequence< OUString > m_aValues; + uno::Sequence< uno::Reference< reflection::XConstantTypeDescription> > m_aConstants; + +}; + +} + +StringRepresentation::StringRepresentation(uno::Reference< uno::XComponentContext > const & context) : + m_xContext(context) +{} + +// com.sun.star.uno.XServiceInfo: +OUString SAL_CALL StringRepresentation::getImplementationName() +{ + return "StringRepresentation"; +} + +sal_Bool SAL_CALL StringRepresentation::supportsService(OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +uno::Sequence< OUString > SAL_CALL StringRepresentation::getSupportedServiceNames() +{ + return { "com.sun.star.inspection.StringRepresentation" }; +} + +// inspection::XStringRepresentation: +OUString SAL_CALL StringRepresentation::convertToControlValue(const uno::Any & PropertyValue) +{ + OUString sReturn; + if ( !convertGenericValueToString( PropertyValue, sReturn ) ) + { + sReturn = convertSimpleToString( PropertyValue ); +#ifdef DBG_UTIL + if ( sReturn.isEmpty() && PropertyValue.hasValue() ) + { + SAL_WARN( "extensions.propctrlr", "StringRepresentation::convertPropertyValueToStringRepresentation: cannot convert values of type '" + << PropertyValue.getValueType().getTypeName() + << "'!" ); + } +#endif + } + + return sReturn; +} + +uno::Any SAL_CALL StringRepresentation::convertToPropertyValue(const OUString & ControlValue, const uno::Type & ControlValueType) +{ + uno::Any aReturn; + + uno::TypeClass ePropertyType = ControlValueType.getTypeClass(); + switch ( ePropertyType ) + { + case uno::TypeClass_FLOAT: + case uno::TypeClass_DOUBLE: + case uno::TypeClass_BYTE: + case uno::TypeClass_SHORT: + case uno::TypeClass_LONG: + case uno::TypeClass_HYPER: + case uno::TypeClass_UNSIGNED_SHORT: + case uno::TypeClass_UNSIGNED_LONG: + case uno::TypeClass_UNSIGNED_HYPER: + try + { + aReturn = convertStringToSimple(ControlValue, ePropertyType); + } + catch( const script::CannotConvertException& ) { } + catch( const lang::IllegalArgumentException& ) { } + break; + + default: + #if OSL_DEBUG_LEVEL > 0 + bool bCanConvert = + #endif + convertStringToGenericValue( ControlValue, aReturn, ControlValueType ); + + #if OSL_DEBUG_LEVEL > 0 + // could not convert ... + if ( !bCanConvert && !ControlValue.isEmpty() ) + { + SAL_WARN( "extensions.propctrlr", "StringRepresentation::convertStringRepresentationToPropertyValue: cannot convert into values of type '" + << ControlValueType.getTypeName() << "'!" ); + } + #endif + } + + return aReturn; +} + +namespace { + +// This comparison functor assumes an underlying set of constants with pairwise +// unequal values that are all of UNO SHORT or LONG type: +struct CompareConstants { + bool operator ()( + css::uno::Reference< css::reflection::XConstantTypeDescription > const & + c1, + css::uno::Reference< css::reflection::XConstantTypeDescription > const & + c2) const + { + return c1->getConstantValue().get<sal_Int32>() + < c2->getConstantValue().get<sal_Int32>(); + } +}; + +} + +// lang::XInitialization: +void SAL_CALL StringRepresentation::initialize(const uno::Sequence< uno::Any > & aArguments) +{ + sal_Int32 nLength = aArguments.getLength(); + if ( !nLength ) + return; + + const uno::Any* pIter = aArguments.getConstArray(); + m_xTypeConverter.set(*pIter++,uno::UNO_QUERY); + if ( nLength != 3 ) + return; + + OUString sConstantName; + *pIter++ >>= sConstantName; + *pIter >>= m_aValues; + + if ( !m_xContext.is() ) + return; + + uno::Reference< container::XHierarchicalNameAccess > xTypeDescProv( + m_xContext->getValueByName("/singletons/com.sun.star.reflection.theTypeDescriptionManager"), + uno::UNO_QUERY_THROW ); + + m_xTypeDescription.set( xTypeDescProv->getByHierarchicalName( sConstantName ), uno::UNO_QUERY_THROW ); + uno::Sequence< + uno::Reference< reflection::XConstantTypeDescription > > + cs(m_xTypeDescription->getConstants()); + auto [begin, end] = asNonConstRange(cs); + std::sort(begin, end, CompareConstants()); + m_aConstants = cs; +} + +OUString StringRepresentation::convertSimpleToString( const uno::Any& _rValue ) +{ + OUString sReturn; + if ( m_xTypeConverter.is() && _rValue.hasValue() ) + { + try + { + if ( m_aConstants.hasElements() ) + { + sal_Int16 nConstantValue = 0; + if ( _rValue >>= nConstantValue ) + { + const uno::Reference< reflection::XConstantTypeDescription>* pIter = m_aConstants.getConstArray(); + const uno::Reference< reflection::XConstantTypeDescription>* pEnd = pIter + m_aConstants.getLength(); + for(sal_Int32 i = 0;pIter != pEnd;++pIter,++i) + { + if ( (*pIter)->getConstantValue() == _rValue ) + { + OSL_ENSURE(i < m_aValues.getLength() ,"StringRepresentation::convertSimpleToString: Index is not in range of m_aValues"); + sReturn = m_aValues[i]; + break; + } + } + } + } + + if ( sReturn.isEmpty() ) + m_xTypeConverter->convertToSimpleType( _rValue, uno::TypeClass_STRING ) >>= sReturn; + } + catch( const script::CannotConvertException& ) { } + catch( const lang::IllegalArgumentException& ) { } + } + return sReturn; +} + + +namespace +{ + struct ConvertIntegerFromAndToString + { + OUString operator()( sal_Int32 _rIntValue ) const + { + return OUString::number( _rIntValue ); + } + sal_Int32 operator()( std::u16string_view _rStringValue ) const + { + return o3tl::toInt32(_rStringValue); + } + }; + + struct StringIdentity + { + OUString operator()( const OUString& _rValue ) const + { + return _rValue; + } + }; + + template < class ElementType, class Transformer > + OUString composeSequenceElements( const Sequence< ElementType >& _rElements, const Transformer& _rTransformer ) + { + OUStringBuffer sCompose; + + // loop through the elements and concatenate the string representations of the integers + // (separated by a line break) + for (const auto& rElement : _rElements) + { + sCompose.append(OUString(_rTransformer(rElement))); + sCompose.append("\n"); + } + sCompose.stripEnd('\n'); + + return sCompose.makeStringAndClear(); + } + + template < class ElementType, class Transformer > + void splitComposedStringToSequence( std::u16string_view _rComposed, Sequence< ElementType >& _out_SplitUp, const Transformer& _rTransformer ) + { + _out_SplitUp.realloc( 0 ); + if ( _rComposed.empty() ) + return; + sal_Int32 tokenPos = 0; + do + { + _out_SplitUp.realloc( _out_SplitUp.getLength() + 1 ); + _out_SplitUp.getArray()[ _out_SplitUp.getLength() - 1 ] = static_cast<ElementType>(_rTransformer( OUString(o3tl::getToken(_rComposed, 0, '\n', tokenPos )) )); + } + while ( tokenPos != -1 ); + } +} + + +bool StringRepresentation::convertGenericValueToString( const uno::Any& _rValue, OUString& _rStringRep ) +{ + bool bCanConvert = true; + + switch ( _rValue.getValueTypeClass() ) + { + case uno::TypeClass_STRING: + _rValue >>= _rStringRep; + break; + + case uno::TypeClass_BOOLEAN: + { + bool bValue = false; + _rValue >>= bValue; + _rStringRep = bValue ? PcrRes(RID_RSC_ENUM_YESNO[1]) + : PcrRes(RID_RSC_ENUM_YESNO[0]); + } + break; + + // some sequence types + case uno::TypeClass_SEQUENCE: + { + Sequence< OUString > aStringValues; + Sequence< sal_Int8 > aInt8Values; + Sequence< sal_uInt16 > aUInt16Values; + Sequence< sal_Int16 > aInt16Values; + Sequence< sal_uInt32 > aUInt32Values; + Sequence< sal_Int32 > aInt32Values; + + // string sequences + if ( _rValue >>= aStringValues ) + { + _rStringRep = composeSequenceElements( aStringValues, StringIdentity() ); + } + // byte sequences + else if ( _rValue >>= aInt8Values ) + { + _rStringRep = composeSequenceElements( aInt8Values, ConvertIntegerFromAndToString() ); + } + // uInt16 sequences + else if ( _rValue >>= aUInt16Values ) + { + _rStringRep = composeSequenceElements( aUInt16Values, ConvertIntegerFromAndToString() ); + } + // Int16 sequences + else if ( _rValue >>= aInt16Values ) + { + _rStringRep = composeSequenceElements( aInt16Values, ConvertIntegerFromAndToString() ); + } + // uInt32 sequences + else if ( _rValue >>= aUInt32Values ) + { + _rStringRep = composeSequenceElements( aUInt32Values, ConvertIntegerFromAndToString() ); + } + // Int32 sequences + else if ( _rValue >>= aInt32Values ) + { + _rStringRep = composeSequenceElements( aInt32Values, ConvertIntegerFromAndToString() ); + } + else + bCanConvert = false; + } + break; + case uno::TypeClass_CONSTANT: + break; + + // some structs + case uno::TypeClass_STRUCT: + OSL_FAIL( "StringRepresentation::convertGenericValueToString(STRUCT): this is dead code - isn't it?" ); + if ( _rValue.getValueType().equals( cppu::UnoType< util::Date >::get() )) + { + // weird enough, the string representation of dates, as used + // by the control displaying dates, and thus as passed through the layers, + // is YYYYMMDD. + util::Date aUnoDate; + _rValue >>= aUnoDate; + _rStringRep = ::dbtools::DBTypeConversion::toDateString(aUnoDate); + } + else if ( _rValue.getValueType().equals( cppu::UnoType< util::Time >::get() )) + { + // similar for time (HHMMSSHH) + util::Time aUnoTime; + _rValue >>= aUnoTime; + _rStringRep = ::dbtools::DBTypeConversion::toTimeString(aUnoTime); + } + else if ( _rValue.getValueType().equals( cppu::UnoType< util::DateTime >::get() )) + { + util::DateTime aUnoDateTime; + _rValue >>= aUnoDateTime; + _rStringRep = ::dbtools::DBTypeConversion::toDateTimeString(aUnoDateTime); + } + else + bCanConvert = false; + break; + + default: + bCanConvert = false; + break; + } + + return bCanConvert; +} + +uno::Any StringRepresentation::convertStringToSimple( const OUString& _rValue,const uno::TypeClass& _ePropertyType ) +{ + uno::Any aReturn; + if ( m_xTypeConverter.is() && !_rValue.isEmpty() ) + { + try + { + if ( m_aConstants.hasElements() && m_aValues.hasElements() ) + { + const OUString* pIter = m_aValues.getConstArray(); + const OUString* pEnd = pIter + m_aValues.getLength(); + for(sal_Int32 i = 0;pIter != pEnd;++pIter,++i) + { + if ( *pIter == _rValue ) + { + OSL_ENSURE(i < m_aConstants.getLength() ,"StringRepresentation::convertSimpleToString: Index is not in range of m_aValues"); + aReturn = m_aConstants[i]->getConstantValue(); + break; + } + } + } + + if ( !aReturn.hasValue() ) + aReturn = m_xTypeConverter->convertToSimpleType( Any( _rValue ), _ePropertyType ); + } + catch( const script::CannotConvertException& ) { } + catch( const lang::IllegalArgumentException& ) { } + } + return aReturn; +} + +bool StringRepresentation::convertStringToGenericValue( const OUString& _rStringRep, uno::Any& _rValue, const uno::Type& _rTargetType ) +{ + bool bCanConvert = true; + + switch ( _rTargetType.getTypeClass() ) + { + case uno::TypeClass_STRING: + _rValue <<= _rStringRep; + break; + + case uno::TypeClass_BOOLEAN: + { + _rValue <<= PcrRes(RID_RSC_ENUM_YESNO[0]) != _rStringRep; + } + break; + + case uno::TypeClass_SEQUENCE: + { + uno::Type aElementType = ::comphelper::getSequenceElementType( _rTargetType ); + + switch ( aElementType.getTypeClass() ) + { + case uno::TypeClass_STRING: + { + Sequence< OUString > aElements; + splitComposedStringToSequence( _rStringRep, aElements, StringIdentity() ); + _rValue <<= aElements; + } + break; + case uno::TypeClass_SHORT: + { + Sequence< sal_Int16 > aElements; + splitComposedStringToSequence( _rStringRep, aElements, ConvertIntegerFromAndToString() ); + _rValue <<= aElements; + } + break; + case uno::TypeClass_UNSIGNED_SHORT: + { + Sequence< sal_uInt16 > aElements; + splitComposedStringToSequence( _rStringRep, aElements, ConvertIntegerFromAndToString() ); + _rValue <<= aElements; + } + break; + case uno::TypeClass_LONG: + { + Sequence< sal_Int32 > aElements; + splitComposedStringToSequence( _rStringRep, aElements, ConvertIntegerFromAndToString() ); + _rValue <<= aElements; + } + break; + case uno::TypeClass_UNSIGNED_LONG: + { + Sequence< sal_uInt32 > aElements; + splitComposedStringToSequence( _rStringRep, aElements, ConvertIntegerFromAndToString() ); + _rValue <<= aElements; + } + break; + case uno::TypeClass_BYTE: + { + Sequence< sal_Int8 > aElements; + splitComposedStringToSequence( _rStringRep, aElements, ConvertIntegerFromAndToString() ); + _rValue <<= aElements; + } + break; + default: + bCanConvert = false; + break; + } + } + break; + + case uno::TypeClass_STRUCT: + OSL_FAIL( "StringRepresentation::convertStringToGenericValue(STRUCT): this is dead code - isn't it?" ); + if ( _rTargetType.equals( cppu::UnoType< util::Date >::get() )) + { + // weird enough, the string representation of dates, as used + // by the control displaying dates, and thus as passed through the layers, + // is YYYYMMDD. + + _rValue <<= ::dbtools::DBTypeConversion::toDate(_rStringRep); + } + else if ( _rTargetType.equals( cppu::UnoType< util::Time >::get() )) + { + // similar for time (HHMMSSHH) + _rValue <<= ::dbtools::DBTypeConversion::toTime(_rStringRep); + } + else if ( _rTargetType.equals( cppu::UnoType< util::DateTime >::get() )) + { + _rValue <<= ::dbtools::DBTypeConversion::toDateTime(_rStringRep); + } + else + bCanConvert = false; + break; + + default: + bCanConvert = false; + break; + } + + return bCanConvert; +} + + +} // pcr + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_StringRepresentation_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::StringRepresentation(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/submissionhandler.cxx b/extensions/source/propctrlr/submissionhandler.cxx new file mode 100644 index 000000000..87609e905 --- /dev/null +++ b/extensions/source/propctrlr/submissionhandler.cxx @@ -0,0 +1,431 @@ +/* -*- 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 "submissionhandler.hxx" +#include "formmetadata.hxx" +#include "formstrings.hxx" +#include "handlerhelper.hxx" + +#include <com/sun/star/form/FormButtonType.hpp> +#include <com/sun/star/form/submission/XSubmissionSupplier.hpp> +#include <com/sun/star/inspection/XObjectInspectorUI.hpp> +#include <com/sun/star/lang/NullPointerException.hpp> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> + + +namespace pcr +{ + + + using namespace ::comphelper; + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::script; + using namespace ::com::sun::star::form; + using namespace ::com::sun::star::xforms; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::inspection; + + + //= SubmissionHelper + + + SubmissionHelper::SubmissionHelper( ::osl::Mutex& _rMutex, const Reference< XPropertySet >& _rxIntrospectee, const Reference< frame::XModel >& _rxContextDocument ) + :EFormsHelper( _rMutex, _rxIntrospectee, _rxContextDocument ) + { + OSL_ENSURE( canTriggerSubmissions( _rxIntrospectee, _rxContextDocument ), + "SubmissionHelper::SubmissionHelper: you should not have instantiated me!" ); + } + + + bool SubmissionHelper::canTriggerSubmissions( const Reference< XPropertySet >& _rxControlModel, + const Reference< frame::XModel >& _rxContextDocument ) + { + if ( !EFormsHelper::isEForm( _rxContextDocument ) ) + return false; + + try + { + Reference< submission::XSubmissionSupplier > xSubmissionSupp( _rxControlModel, UNO_QUERY ); + if ( xSubmissionSupp.is() ) + return true; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "SubmissionHelper::canTriggerSubmissions" ); + } + return false; + } + + + //= SubmissionPropertyHandler + + + SubmissionPropertyHandler::SubmissionPropertyHandler( const Reference< XComponentContext >& _rxContext ) + :PropertyHandlerComponent( _rxContext ) + ,OPropertyChangeListener( m_aMutex ) + { + } + + + SubmissionPropertyHandler::~SubmissionPropertyHandler( ) + { + disposeAdapter(); + } + + + OUString SubmissionPropertyHandler::getImplementationName( ) + { + return "com.sun.star.comp.extensions.SubmissionPropertyHandler"; + } + + + Sequence< OUString > SubmissionPropertyHandler::getSupportedServiceNames( ) + { + return { "com.sun.star.form.inspection.SubmissionPropertyHandler" }; + } + + + Any SAL_CALL SubmissionPropertyHandler::getPropertyValue( const OUString& _rPropertyName ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + OSL_ENSURE(m_pHelper, "SubmissionPropertyHandler::getPropertyValue: inconsistency!"); + // if we survived impl_getPropertyId_throwUnknownProperty, we should have a helper, since no helper implies no properties + + Any aReturn; + try + { + switch ( nPropId ) + { + case PROPERTY_ID_SUBMISSION_ID: + { + Reference< submission::XSubmissionSupplier > xSubmissionSupp( m_xComponent, UNO_QUERY ); + OSL_ENSURE( xSubmissionSupp.is(), "SubmissionPropertyHandler::getPropertyValue: this should never happen ..." ); + // this handler is not intended for components which are no XSubmissionSupplier + Reference< submission::XSubmission > xSubmission; + if ( xSubmissionSupp.is() ) + xSubmission = xSubmissionSupp->getSubmission( ); + aReturn <<= xSubmission; + } + break; + + case PROPERTY_ID_XFORMS_BUTTONTYPE: + { + FormButtonType eType = FormButtonType_PUSH; + OSL_VERIFY( m_xComponent->getPropertyValue( PROPERTY_BUTTONTYPE ) >>= eType ); + if ( ( eType != FormButtonType_PUSH ) && ( eType != FormButtonType_SUBMIT ) ) + eType = FormButtonType_PUSH; + aReturn <<= eType; + } + break; + + default: + OSL_FAIL( "SubmissionPropertyHandler::getPropertyValue: cannot handle this property!" ); + break; + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "SubmissionPropertyHandler::getPropertyValue" ); + } + + return aReturn; + } + + + void SAL_CALL SubmissionPropertyHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + OSL_ENSURE(m_pHelper, "SubmissionPropertyHandler::setPropertyValue: inconsistency!"); + // if we survived impl_getPropertyId_throwUnknownProperty, we should have a helper, since no helper implies no properties + + try + { + switch ( nPropId ) + { + case PROPERTY_ID_SUBMISSION_ID: + { + Reference< submission::XSubmission > xSubmission; + OSL_VERIFY( _rValue >>= xSubmission ); + + Reference< submission::XSubmissionSupplier > xSubmissionSupp( m_xComponent, UNO_QUERY ); + OSL_ENSURE( xSubmissionSupp.is(), "SubmissionPropertyHandler::setPropertyValue: this should never happen ..." ); + // this handler is not intended for components which are no XSubmissionSupplier + if ( xSubmissionSupp.is() ) + { + xSubmissionSupp->setSubmission( xSubmission ); + impl_setContextDocumentModified_nothrow(); + } + } + break; + + case PROPERTY_ID_XFORMS_BUTTONTYPE: + m_xComponent->setPropertyValue( PROPERTY_BUTTONTYPE, _rValue ); + break; + + default: + OSL_FAIL( "SubmissionPropertyHandler::setPropertyValue: cannot handle this id!" ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "SubmissionPropertyHandler::setPropertyValue" ); + } + } + + + Sequence< OUString > SAL_CALL SubmissionPropertyHandler::getActuatingProperties( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if (!m_pHelper) + return Sequence< OUString >(); + + Sequence<OUString> aReturn { PROPERTY_XFORMS_BUTTONTYPE }; + return aReturn; + } + + + Sequence< OUString > SAL_CALL SubmissionPropertyHandler::getSupersededProperties( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if (!m_pHelper) + return Sequence< OUString >(); + + Sequence< OUString > aReturn{ PROPERTY_TARGET_URL, + PROPERTY_TARGET_FRAME, + PROPERTY_BUTTONTYPE }; + return aReturn; + } + + + void SubmissionPropertyHandler::onNewComponent() + { + if ( m_xPropChangeMultiplexer.is() ) + { + m_xPropChangeMultiplexer->dispose(); + m_xPropChangeMultiplexer.clear(); + } + + PropertyHandlerComponent::onNewComponent(); + + Reference< frame::XModel > xDocument( impl_getContextDocument_nothrow() ); + DBG_ASSERT( xDocument.is(), "SubmissionPropertyHandler::onNewComponent: no document!" ); + + m_pHelper.reset(); + + if ( SubmissionHelper::canTriggerSubmissions( m_xComponent, xDocument ) ) + { + m_pHelper.reset( new SubmissionHelper( m_aMutex, m_xComponent, xDocument ) ); + + m_xPropChangeMultiplexer = new OPropertyChangeMultiplexer( this, m_xComponent ); + m_xPropChangeMultiplexer->addProperty( PROPERTY_BUTTONTYPE ); + } + } + + + Sequence< Property > SubmissionPropertyHandler::doDescribeSupportedProperties() const + { + std::vector< Property > aProperties; + if (m_pHelper) + { + implAddPropertyDescription( aProperties, PROPERTY_SUBMISSION_ID, cppu::UnoType<submission::XSubmission>::get() ); + implAddPropertyDescription( aProperties, PROPERTY_XFORMS_BUTTONTYPE, ::cppu::UnoType<FormButtonType>::get() ); + } + if ( aProperties.empty() ) + return Sequence< Property >(); + return comphelper::containerToSequence(aProperties); + } + + + LineDescriptor SAL_CALL SubmissionPropertyHandler::describePropertyLine( const OUString& _rPropertyName, + const Reference< XPropertyControlFactory >& _rxControlFactory ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !_rxControlFactory.is() ) + throw NullPointerException(); + if (!m_pHelper) + throw RuntimeException(); + + std::vector< OUString > aListEntries; + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + switch ( nPropId ) + { + case PROPERTY_ID_SUBMISSION_ID: + m_pHelper->getAllElementUINames(EFormsHelper::Submission, aListEntries, false); + break; + + case PROPERTY_ID_XFORMS_BUTTONTYPE: + { + // available options are nearly the same as for the "normal" button type, but only the + // first two options + aListEntries = m_pInfoService->getPropertyEnumRepresentations( PROPERTY_ID_BUTTONTYPE ); + aListEntries.resize( 2 ); + } + break; + + default: + OSL_FAIL( "SubmissionPropertyHandler::describePropertyLine: cannot handle this id!" ); + return LineDescriptor(); + } + + LineDescriptor aDescriptor; + aDescriptor.Control = PropertyHandlerHelper::createListBoxControl( _rxControlFactory, std::move(aListEntries), false, true ); + aDescriptor.DisplayName = m_pInfoService->getPropertyTranslation( nPropId ); + aDescriptor.Category = "General"; + aDescriptor.HelpURL = HelpIdUrl::getHelpURL( m_pInfoService->getPropertyHelpId( nPropId ) ); + return aDescriptor; + } + + + void SAL_CALL SubmissionPropertyHandler::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool ) + { + if ( !_rxInspectorUI.is() ) + throw NullPointerException(); + + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nActuatingPropId( impl_getPropertyId_throwRuntime( _rActuatingPropertyName ) ); + OSL_PRECOND(m_pHelper, + "SubmissionPropertyHandler::actuatingPropertyChanged: inconsistency!"); + // if we survived impl_getPropertyId_throwRuntime, we should have a helper, since no helper implies no properties + + switch ( nActuatingPropId ) + { + case PROPERTY_ID_XFORMS_BUTTONTYPE: + { + FormButtonType eType = FormButtonType_PUSH; + OSL_VERIFY( _rNewValue >>= eType ); + _rxInspectorUI->enablePropertyUI( PROPERTY_SUBMISSION_ID, eType == FormButtonType_SUBMIT ); + } + break; + + default: + OSL_FAIL( "SubmissionPropertyHandler::actuatingPropertyChanged: cannot handle this id!" ); + } + } + + + Any SAL_CALL SubmissionPropertyHandler::convertToPropertyValue( const OUString& _rPropertyName, const Any& _rControlValue ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + Any aPropertyValue; + + OSL_ENSURE( + m_pHelper, + "SubmissionPropertyHandler::convertToPropertyValue: we have no SupportedProperties!"); + if (!m_pHelper) + return aPropertyValue; + + OUString sControlValue; + OSL_VERIFY( _rControlValue >>= sControlValue ); + + PropertyId nPropId( m_pInfoService->getPropertyId( _rPropertyName ) ); + switch ( nPropId ) + { + case PROPERTY_ID_SUBMISSION_ID: + { + Reference< XSubmission > xSubmission( m_pHelper->getModelElementFromUIName( EFormsHelper::Submission, sControlValue ), UNO_QUERY ); + aPropertyValue <<= xSubmission; + } + break; + + case PROPERTY_ID_XFORMS_BUTTONTYPE: + { + ::rtl::Reference< IPropertyEnumRepresentation > aEnumConversion( + new DefaultEnumRepresentation( *m_pInfoService, ::cppu::UnoType<FormButtonType>::get(), PROPERTY_ID_BUTTONTYPE ) ); + // TODO/UNOize: make aEnumConversion a member? + aEnumConversion->getValueFromDescription( sControlValue, aPropertyValue ); + } + break; + + default: + OSL_FAIL( "SubmissionPropertyHandler::convertToPropertyValue: cannot handle this id!" ); + } + + return aPropertyValue; + } + + + Any SAL_CALL SubmissionPropertyHandler::convertToControlValue( const OUString& _rPropertyName, const Any& _rPropertyValue, const Type& _rControlValueType ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + Any aControlValue; + + OSL_ENSURE( + m_pHelper, + "SubmissionPropertyHandler::convertToControlValue: we have no SupportedProperties!"); + if (!m_pHelper) + return aControlValue; + + OSL_ENSURE( _rControlValueType.getTypeClass() == TypeClass_STRING, + "SubmissionPropertyHandler::convertToControlValue: all our controls should use strings for value exchange!" ); + + PropertyId nPropId( m_pInfoService->getPropertyId( _rPropertyName ) ); + switch ( nPropId ) + { + case PROPERTY_ID_SUBMISSION_ID: + { + Reference< XPropertySet > xSubmission( _rPropertyValue, UNO_QUERY ); + if ( xSubmission.is() ) + aControlValue <<= EFormsHelper::getModelElementUIName( EFormsHelper::Submission, xSubmission ); + } + break; + + case PROPERTY_ID_XFORMS_BUTTONTYPE: + { + ::rtl::Reference< IPropertyEnumRepresentation > aEnumConversion( + new DefaultEnumRepresentation( *m_pInfoService, _rPropertyValue.getValueType(), PROPERTY_ID_BUTTONTYPE ) ); + // TODO/UNOize: make aEnumConversion a member? + aControlValue <<= aEnumConversion->getDescriptionForValue( _rPropertyValue ); + } + break; + + default: + OSL_FAIL( "SubmissionPropertyHandler::convertToControlValue: cannot handle this id!" ); + } + + return aControlValue; + } + + + void SubmissionPropertyHandler::_propertyChanged( const PropertyChangeEvent& _rEvent ) + { + if ( _rEvent.PropertyName == PROPERTY_BUTTONTYPE ) + firePropertyChange( PROPERTY_XFORMS_BUTTONTYPE, PROPERTY_ID_XFORMS_BUTTONTYPE, _rEvent.OldValue, _rEvent.NewValue ); + } + + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_SubmissionPropertyHandler_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::SubmissionPropertyHandler(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/submissionhandler.hxx b/extensions/source/propctrlr/submissionhandler.hxx new file mode 100644 index 000000000..f263041b2 --- /dev/null +++ b/extensions/source/propctrlr/submissionhandler.hxx @@ -0,0 +1,110 @@ +/* -*- 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 "propertyhandler.hxx" +#include "eformshelper.hxx" + +#include <comphelper/propmultiplex.hxx> +#include <rtl/ref.hxx> + +namespace comphelper +{ + class OPropertyChangeMultiplexer; +} + + +namespace pcr +{ + + + //= SubmissionHelper + + class SubmissionHelper : public EFormsHelper + { + public: + SubmissionHelper( + osl::Mutex& _rMutex, + const css::uno::Reference< css::beans::XPropertySet >& _rxIntrospectee, + const css::uno::Reference< css::frame::XModel >& _rxContextDocument + ); + + /** determines whether the given control model is able to trigger submissions + + Instances of the <type>SubmissionHelper</type> class should not be instantiated + for components where this method returned <FALSE/> + */ + static bool canTriggerSubmissions( + const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel, + const css::uno::Reference< css::frame::XModel >& _rxContextDocument + ); + }; + + + //= SubmissionPropertyHandler + + /** a property handler for any virtual string properties + */ + class SubmissionPropertyHandler : public PropertyHandlerComponent, public ::comphelper::OPropertyChangeListener + { + private: + std::unique_ptr< SubmissionHelper > m_pHelper; + rtl::Reference<::comphelper::OPropertyChangeMultiplexer> m_xPropChangeMultiplexer; + + public: + explicit SubmissionPropertyHandler( + const css::uno::Reference< css::uno::XComponentContext >& _rxContext + ); + + virtual ~SubmissionPropertyHandler() override; + protected: + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override; + + // XPropertyHandler overriables + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override; + virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override; + virtual css::uno::Sequence< OUString > + SAL_CALL getActuatingProperties( ) override; + virtual css::uno::Sequence< OUString > + SAL_CALL getSupersededProperties( ) override; + virtual css::inspection::LineDescriptor + SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override; + virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool ) override; + virtual css::uno::Any SAL_CALL convertToPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rControlValue ) override; + virtual css::uno::Any SAL_CALL convertToControlValue( const OUString& _rPropertyName, const css::uno::Any& _rPropertyValue, const css::uno::Type& _rControlValueType ) override; + + // PropertyHandler overridables + virtual css::uno::Sequence< css::beans::Property > + doDescribeSupportedProperties() const override; + virtual void onNewComponent() override; + + private: + // OPropertyChangeListener + virtual void _propertyChanged(const css::beans::PropertyChangeEvent& _rEvent) override; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/taborder.cxx b/extensions/source/propctrlr/taborder.cxx new file mode 100644 index 000000000..6ada66b5a --- /dev/null +++ b/extensions/source/propctrlr/taborder.cxx @@ -0,0 +1,320 @@ +/* -*- 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 "taborder.hxx" + +#include <bitmaps.hlst> +#include "formstrings.hxx" +#include <comphelper/types.hxx> +#include <comphelper/property.hxx> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/form/runtime/FormController.hpp> +#include <osl/diagnose.h> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> + +namespace pcr +{ + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::form; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::datatransfer; + + namespace { + + OUString GetImage( const Reference< XPropertySet >& _rxSet ) + { + OUString sImageId = RID_EXTBMP_CONTROL; + // TODO: classify controls also in Basic propbrw + if ( _rxSet.is() && ::comphelper::hasProperty( PROPERTY_CLASSID, _rxSet ) ) + { + switch( ::comphelper::getINT16( _rxSet->getPropertyValue( PROPERTY_CLASSID ) ) ) + { + case FormComponentType::COMMANDBUTTON: sImageId = RID_EXTBMP_BUTTON; break; + case FormComponentType::FIXEDTEXT: sImageId = RID_EXTBMP_FIXEDTEXT; break; + case FormComponentType::TEXTFIELD: sImageId = RID_EXTBMP_EDITBOX; break; + case FormComponentType::RADIOBUTTON: sImageId = RID_EXTBMP_RADIOBUTTON; break; + case FormComponentType::CHECKBOX: sImageId = RID_EXTBMP_CHECKBOX; break; + case FormComponentType::LISTBOX: sImageId = RID_EXTBMP_LISTBOX; break; + case FormComponentType::COMBOBOX: sImageId = RID_EXTBMP_COMBOBOX; break; + case FormComponentType::GROUPBOX: sImageId = RID_EXTBMP_GROUPBOX; break; + case FormComponentType::IMAGEBUTTON: sImageId = RID_EXTBMP_IMAGEBUTTON; break; + case FormComponentType::FILECONTROL: sImageId = RID_EXTBMP_FILECONTROL; break; + case FormComponentType::HIDDENCONTROL: sImageId = RID_EXTBMP_HIDDEN; break; + case FormComponentType::DATEFIELD: sImageId = RID_EXTBMP_DATEFIELD; break; + case FormComponentType::TIMEFIELD: sImageId = RID_EXTBMP_TIMEFIELD; break; + case FormComponentType::NUMERICFIELD: sImageId = RID_EXTBMP_NUMERICFIELD; break; + case FormComponentType::CURRENCYFIELD: sImageId = RID_EXTBMP_CURRENCYFIELD; break; + case FormComponentType::PATTERNFIELD: sImageId = RID_EXTBMP_PATTERNFIELD; break; + case FormComponentType::IMAGECONTROL: sImageId = RID_EXTBMP_IMAGECONTROL; break; + case FormComponentType::GRIDCONTROL: sImageId = RID_EXTBMP_GRID; break; + case FormComponentType::SCROLLBAR: sImageId = RID_EXTBMP_SCROLLBAR; break; + case FormComponentType::SPINBUTTON: sImageId = RID_EXTBMP_SPINBUTTON; break; + case FormComponentType::NAVIGATIONBAR: sImageId = RID_EXTBMP_NAVIGATIONBAR; break; + default: + OSL_FAIL( "TabOrderDialog::GetImage: unknown control type" ); + } + } + + return sImageId; + } + + //= OSimpleTabModel + + class OSimpleTabModel : public ::cppu::WeakImplHelper< XTabControllerModel> + { + Sequence< Reference< XControlModel > > m_aModels; + + public: + explicit OSimpleTabModel( const Sequence< Reference< XControlModel > >& _rModels ) + :m_aModels( _rModels ) + { + } + + // XTabControllerModel + virtual void SAL_CALL setControlModels(const Sequence< Reference< XControlModel > >& rModels) override {m_aModels = rModels;} + virtual Sequence< Reference< XControlModel > > SAL_CALL getControlModels() override {return m_aModels;} + virtual void SAL_CALL setGroup(const Sequence< Reference< XControlModel > >& /*Group*/, const OUString& /*GroupName*/) override {} + virtual sal_Int32 SAL_CALL getGroupCount() override {return 0;} + virtual void SAL_CALL getGroup(sal_Int32 /*nGroup*/, Sequence< Reference< XControlModel > >& /*Group*/, OUString& /*Name*/) override {} + virtual void SAL_CALL getGroupByName(const OUString& /*Name*/, Sequence< Reference< XControlModel > >& /*Group*/) override {} + virtual sal_Bool SAL_CALL getGroupControl() override {return false;} ; + virtual void SAL_CALL setGroupControl(sal_Bool /*GroupControl*/) override {}; + }; + + } + + //= TabOrderDialog + TabOrderDialog::TabOrderDialog(weld::Window* _pParent, const Reference< XTabControllerModel >& _rxTabModel, + const Reference< XControlContainer >& _rxControlCont, const Reference< XComponentContext >& _rxORB) + : GenericDialogController( _pParent, "modules/spropctrlr/ui/taborder.ui", "TabOrderDialog") + , m_xModel( _rxTabModel ) + , m_xControlContainer( _rxControlCont ) + , m_xORB( _rxORB ) + , m_xLB_Controls(m_xBuilder->weld_tree_view("CTRLtree")) + , m_xPB_OK(m_xBuilder->weld_button("ok")) + , m_xPB_MoveUp(m_xBuilder->weld_button("upB")) + , m_xPB_MoveDown(m_xBuilder->weld_button("downB")) + , m_xPB_AutoOrder(m_xBuilder->weld_button("autoB")) + { + m_xLB_Controls->set_size_request(m_xLB_Controls->get_approximate_digit_width() * 60, + m_xLB_Controls->get_height_rows(10)); + m_xLB_Controls->set_selection_mode(SelectionMode::Multiple); + + m_xLB_Controls->connect_model_changed(LINK(this, TabOrderDialog, ModelHasMoved)); + m_xPB_MoveUp->connect_clicked( LINK( this, TabOrderDialog, MoveUpClickHdl ) ); + m_xPB_MoveDown->connect_clicked( LINK( this, TabOrderDialog, MoveDownClickHdl ) ); + m_xPB_AutoOrder->connect_clicked( LINK( this, TabOrderDialog, AutoOrderClickHdl ) ); + m_xPB_OK->connect_clicked( LINK( this, TabOrderDialog, OKClickHdl ) ); + m_xPB_OK->set_sensitive(false); + + if ( m_xModel.is() ) + m_xTempModel = new OSimpleTabModel( m_xModel->getControlModels() ); + + if ( m_xTempModel.is() && m_xControlContainer.is() ) + FillList(); + + if (m_xLB_Controls->n_children() < 2) + { + m_xPB_MoveUp->set_sensitive(false); + m_xPB_MoveDown->set_sensitive(false); + m_xPB_AutoOrder->set_sensitive(false); + } + + } + + void TabOrderDialog::SetModified() + { + m_xPB_OK->set_sensitive(true); + } + + TabOrderDialog::~TabOrderDialog() + { + } + + void TabOrderDialog::FillList() + { + DBG_ASSERT( m_xTempModel.is() && m_xControlContainer.is(), "TabOrderDialog::FillList: invalid call!" ); + if ( !m_xTempModel.is() || !m_xControlContainer.is() ) + return; + + m_xLB_Controls->clear(); + + try + { + OUString aName; + OUString aImage; + + const Sequence<Reference<css::awt::XControlModel>> aControlModels = m_xTempModel->getControlModels(); + for ( auto const& rControlModel : aControlModels ) + { + Reference< XPropertySet > xControl( rControlModel, UNO_QUERY ); + Reference< XPropertySetInfo > xPI; + if ( xControl.is() ) + xPI = xControl->getPropertySetInfo(); + + if ( xPI.is() ) + { + if ( xPI->hasPropertyByName( PROPERTY_TABSTOP ) ) + { + aName = ::comphelper::getString( xControl->getPropertyValue( PROPERTY_NAME ) ); + // TODO: do Basic controls have a name? + aImage = GetImage( xControl ); + OUString sId(weld::toId(xControl.get())); + m_xLB_Controls->append(sId, aName, aImage); + } + } + else + { + // no property set -> no tab order + OSL_FAIL( "TabOrderDialog::FillList: invalid control encountered!" ); + m_xLB_Controls->clear(); + break; + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "TabOrderDialog::FillList" ); + } + + // select first entry + if (m_xLB_Controls->n_children()) + m_xLB_Controls->select(0); + } + + IMPL_LINK_NOARG( TabOrderDialog, MoveUpClickHdl, weld::Button&, void ) + { + MoveSelection(-1); + } + + IMPL_LINK_NOARG( TabOrderDialog, MoveDownClickHdl, weld::Button&, void ) + { + MoveSelection(1); + } + + IMPL_LINK_NOARG( TabOrderDialog, AutoOrderClickHdl, weld::Button&, void ) + { + try + { + Reference< css::form::runtime::XFormController > xTabController = css::form::runtime::FormController::create( m_xORB ); + + xTabController->setModel( m_xTempModel ); + xTabController->setContainer( m_xControlContainer ); + xTabController->autoTabOrder(); + + SetModified(); + FillList(); + + ::comphelper::disposeComponent( xTabController ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "TabOrderDialog::AutoOrderClickHdl" ); + } + } + + IMPL_LINK_NOARG( TabOrderDialog, OKClickHdl, weld::Button&, void ) + { + int nEntryCount = m_xLB_Controls->n_children(); + Sequence< Reference< XControlModel > > aSortedControlModelSeq( nEntryCount ); + const Sequence< Reference< XControlModel > > aControlModels( m_xTempModel->getControlModels()); + Reference< XControlModel > * pSortedControlModels = aSortedControlModelSeq.getArray(); + + for (int i = 0; i < nEntryCount; ++i) + { + XPropertySet* pEntry = weld::fromId<XPropertySet*>(m_xLB_Controls->get_id(i)); + for( auto const& rControlModel : aControlModels ) + { + Reference< XPropertySet > xSet(rControlModel, UNO_QUERY); + if (xSet.get() == pEntry) + { + pSortedControlModels[i] = rControlModel; + break; + } + } + } + + // TODO: UNO action (to bracket all the single actions which are being created) + m_xModel->setControlModels( aSortedControlModelSeq ); + + m_xDialog->response(RET_OK); + } + + IMPL_LINK_NOARG(TabOrderDialog, ModelHasMoved, weld::TreeView&, void) + { + SetModified(); + } + + void TabOrderDialog::MoveSelection(int nRelPos) + { + std::vector<int> aRows(m_xLB_Controls->get_selected_rows()); + if (aRows.empty()) + return; + + m_xLB_Controls->unselect_all(); + for (int i = 0; i < abs(nRelPos); ++i) + { + SetModified(); + + // move entries + if (nRelPos < 0) + { + std::sort(aRows.begin(), aRows.end()); + + int nFirstSelPos = aRows[0]; + if (nFirstSelPos == 0) return; + + for (auto row : aRows) + { + int nInsertPos = row - 1; + m_xLB_Controls->swap(nInsertPos, row); + } + + for (auto row : aRows) + m_xLB_Controls->select(row - 1); + } + else if (nRelPos > 0) + { + std::sort(aRows.rbegin(), aRows.rend()); + + int nLastSelPos = aRows[0]; + if( (nLastSelPos + nRelPos - i) > (m_xLB_Controls->n_children()-1) ) return; + + for (auto row : aRows) + { + int nInsertPos = row + 1; + m_xLB_Controls->swap(nInsertPos, row); + } + + for (auto row : aRows) + m_xLB_Controls->select(row + 1); + } + } + } + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/taborder.hxx b/extensions/source/propctrlr/taborder.hxx new file mode 100644 index 000000000..e43f010e8 --- /dev/null +++ b/extensions/source/propctrlr/taborder.hxx @@ -0,0 +1,74 @@ +/* -*- 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/XTabControllerModel.hpp> +#include <com/sun/star/awt/XControlContainer.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <vcl/weld.hxx> + +namespace pcr +{ + //= TabOrderDialog + class TabOrderDialog : public weld::GenericDialogController + { + css::uno::Reference< css::awt::XTabControllerModel > + m_xTempModel; + css::uno::Reference< css::awt::XTabControllerModel > + m_xModel; + css::uno::Reference< css::awt::XControlContainer > + m_xControlContainer; + css::uno::Reference< css::uno::XComponentContext > + m_xORB; + + std::unique_ptr<weld::TreeView> m_xLB_Controls; + std::unique_ptr<weld::Button> m_xPB_OK; + std::unique_ptr<weld::Button> m_xPB_MoveUp; + std::unique_ptr<weld::Button> m_xPB_MoveDown; + std::unique_ptr<weld::Button> m_xPB_AutoOrder; + + DECL_LINK( ModelHasMoved, weld::TreeView&, void ); + DECL_LINK( MoveUpClickHdl, weld::Button&, void ); + DECL_LINK( MoveDownClickHdl, weld::Button&, void ); + DECL_LINK( AutoOrderClickHdl, weld::Button&, void ); + DECL_LINK( OKClickHdl, weld::Button&, void ); + + void FillList(); + void MoveSelection(int nRelPos); + + public: + TabOrderDialog( + weld::Window* pParent, + const css::uno::Reference< css::awt::XTabControllerModel >& _rxTabModel, + const css::uno::Reference< css::awt::XControlContainer >& _rxControlCont, + const css::uno::Reference< css::uno::XComponentContext >& _rxORB + ); + + virtual ~TabOrderDialog() override; + + void SetModified(); + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/unourl.cxx b/extensions/source/propctrlr/unourl.cxx new file mode 100644 index 000000000..88c963012 --- /dev/null +++ b/extensions/source/propctrlr/unourl.cxx @@ -0,0 +1,65 @@ +/* -*- 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 "unourl.hxx" +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <comphelper/processfactory.hxx> +#include <osl/diagnose.h> +#include <tools/diagnose_ex.h> + + +namespace pcr +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::util; + + + //= UnoURL + + UnoURL::UnoURL( const OUString& _rCompleteURL, const Reference< XMultiServiceFactory >& _rxORB ) + { + m_aURL.Complete = _rCompleteURL; + + OSL_ENSURE( _rxORB.is(), "UnoURL::UnoURL: invalid ORB!" ); + Reference< XURLTransformer > xTransform; + try + { + if ( _rxORB.is() ) + { + xTransform.set( URLTransformer::create(comphelper::getComponentContext(_rxORB)) ); + OSL_ENSURE( xTransform.is(), "UnoURL::UnoURL: could not create a URL transformer!" ); + if ( xTransform.is() ) + xTransform->parseStrict( m_aURL ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "UnoURL::UnoURL" ); + } + } + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/unourl.hxx b/extensions/source/propctrlr/unourl.hxx new file mode 100644 index 000000000..824c865ea --- /dev/null +++ b/extensions/source/propctrlr/unourl.hxx @@ -0,0 +1,50 @@ +/* -*- 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 <rtl/ustring.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/util/URL.hpp> + + +namespace pcr +{ + + + //= UnoURL + + class UnoURL + { + private: + css::util::URL m_aURL; + + public: + UnoURL( + const OUString& _rCompleteURL, + const css::uno::Reference< css::lang::XMultiServiceFactory >& _rxORB + ); + + operator const css::util::URL& () const { return m_aURL; } + }; + + +} // namespacepcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/usercontrol.cxx b/extensions/source/propctrlr/usercontrol.cxx new file mode 100644 index 000000000..8cccb48e8 --- /dev/null +++ b/extensions/source/propctrlr/usercontrol.cxx @@ -0,0 +1,276 @@ +/* -*- 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 "usercontrol.hxx" + +#include <com/sun/star/inspection/PropertyControlType.hpp> +#include <svl/numuno.hxx> +#include <vcl/GraphicObject.hxx> +#include <vcl/event.hxx> +#include <tools/debug.hxx> +#include <svl/numformat.hxx> +#include <svl/zformat.hxx> +#include <connectivity/dbconversion.hxx> +#include "modulepcr.hxx" +#include <strings.hrc> + + +namespace pcr +{ + + + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::Type; + + namespace PropertyControlType = ::com::sun::star::inspection::PropertyControlType; + + IMPL_LINK(OFormatSampleControl, KeyInputHdl, const KeyEvent&, rKeyEvent, bool) + { + // want to handle two keys myself : Del/Backspace should empty the window (setting my prop to "standard" this way) + sal_uInt16 nKey = rKeyEvent.GetKeyCode().GetCode(); + if ((KEY_DELETE == nKey) || (KEY_BACKSPACE == nKey)) + { + m_xSpinButton->set_text(""); + m_xEntry->set_text(""); + setModified(); + } + + return true; + } + + void OFormatSampleControl::SetFormatSupplier( const SvNumberFormatsSupplierObj* pSupplier ) + { + Formatter& rFieldFormatter = m_xSpinButton->GetFormatter(); + if (pSupplier) + { + rFieldFormatter.TreatAsNumber(true); + + SvNumberFormatter* pFormatter = pSupplier->GetNumberFormatter(); + rFieldFormatter.SetFormatter(pFormatter); + rFieldFormatter.SetValue( 1234.56789 ); + } + else + { + rFieldFormatter.TreatAsNumber(false); + rFieldFormatter.SetFormatter(nullptr); + m_xSpinButton->set_text( "" ); + } + + m_xEntry->set_text(m_xSpinButton->get_text()); + } + + OFormatSampleControl::OFormatSampleControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly) + : OFormatSampleControl_Base(PropertyControlType::Unknown, std::move(xBuilder), std::move(xWidget), bReadOnly) + , m_xSpinButton(m_xBuilder->weld_formatted_spin_button("sample")) + , m_xEntry(m_xBuilder->weld_entry("entry")) + { + Formatter& rFieldFormatter = m_xSpinButton->GetFormatter(); + rFieldFormatter.TreatAsNumber(true); + rFieldFormatter.ClearMinValue(); + rFieldFormatter.ClearMaxValue(); + m_xEntry->connect_key_press(LINK(this, OFormatSampleControl, KeyInputHdl)); + } + + void SAL_CALL OFormatSampleControl::setValue( const Any& _rValue ) + { + sal_Int32 nFormatKey = 0; + if ( _rValue >>= nFormatKey ) + { + // else set the new format key, the text will be reformatted + Formatter& rFieldFormatter = m_xSpinButton->GetFormatter(); + rFieldFormatter.SetFormatKey(nFormatKey); + + SvNumberFormatter* pNF = rFieldFormatter.GetFormatter(); + const SvNumberformat* pEntry = pNF->GetEntry( nFormatKey ); + OSL_ENSURE( pEntry, "OFormatSampleControl::setValue: invalid format entry!" ); + + const bool bIsTextFormat = ( pEntry && pEntry->IsTextFormat() ); + if ( bIsTextFormat ) + m_xSpinButton->set_text( PcrRes( RID_STR_TEXT_FORMAT ) ); + else + rFieldFormatter.SetValue( pEntry ? getPreviewValue( *pEntry ) : 1234.56789 ); + } + else + m_xSpinButton->set_text( "" ); + + m_xEntry->set_text(m_xSpinButton->get_text()); + } + + double OFormatSampleControl::getPreviewValue( const SvNumberformat& i_rEntry ) + { + double nValue = 1234.56789; + switch ( i_rEntry.GetType() & ~SvNumFormatType::DEFINED ) + { + case SvNumFormatType::DATE: + { + Date aCurrentDate( Date::SYSTEM ); + static css::util::Date STANDARD_DB_DATE(30,12,1899); + nValue = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDate(aCurrentDate.GetDate()),STANDARD_DB_DATE); + } + break; + case SvNumFormatType::TIME: + case SvNumFormatType::DATETIME: + { + tools::Time aCurrentTime( tools::Time::SYSTEM ); + nValue = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toTime(aCurrentTime.GetTime())); + } + break; + default: + break; + } + return nValue; + } + + + double OFormatSampleControl::getPreviewValue(SvNumberFormatter const * _pNF, sal_Int32 _nFormatKey) + { + const SvNumberformat* pEntry = _pNF->GetEntry(_nFormatKey); + DBG_ASSERT( pEntry, "OFormattedNumericControl::SetFormatDescription: invalid format key!" ); + double nValue = 1234.56789; + if ( pEntry ) + nValue = getPreviewValue( *pEntry ); + return nValue; + } + + Any SAL_CALL OFormatSampleControl::getValue() + { + Any aPropValue; + if ( !m_xSpinButton->get_text().isEmpty() ) + { + Formatter& rFieldFormatter = m_xSpinButton->GetFormatter(); + aPropValue <<= rFieldFormatter.GetValue(); + } + return aPropValue; + } + + Type SAL_CALL OFormatSampleControl::getValueType() + { + return ::cppu::UnoType<sal_Int32>::get(); + } + + OFormattedNumericControl::OFormattedNumericControl(std::unique_ptr<weld::FormattedSpinButton> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly) + : OFormattedNumericControl_Base(PropertyControlType::Unknown, std::move(xBuilder), std::move(xWidget), bReadOnly) + { + Formatter& rFormatter = getTypedControlWindow()->GetFormatter(); + rFormatter.TreatAsNumber(true); + rFormatter.ClearMinValue(); + rFormatter.ClearMaxValue(); + } + + OFormattedNumericControl::~OFormattedNumericControl() + { + } + + void SAL_CALL OFormattedNumericControl::setValue( const Any& _rValue ) + { + double nValue( 0 ); + if ( _rValue >>= nValue ) + getTypedControlWindow()->GetFormatter().SetValue(nValue); + else + getTypedControlWindow()->set_text(""); + } + + Any SAL_CALL OFormattedNumericControl::getValue() + { + Any aPropValue; + if ( !getTypedControlWindow()->get_text().isEmpty() ) + aPropValue <<= getTypedControlWindow()->GetFormatter().GetValue(); + return aPropValue; + } + + Type SAL_CALL OFormattedNumericControl::getValueType() + { + return ::cppu::UnoType<double>::get(); + } + + void OFormattedNumericControl::SetFormatDescription(const FormatDescription& rDesc) + { + bool bFallback = true; + + Formatter& rFieldFormatter = getTypedControlWindow()->GetFormatter(); + if (rDesc.pSupplier) + { + rFieldFormatter.TreatAsNumber(true); + + SvNumberFormatter* pFormatter = rDesc.pSupplier->GetNumberFormatter(); + if (pFormatter != rFieldFormatter.GetFormatter()) + rFieldFormatter.SetFormatter(pFormatter); + rFieldFormatter.SetFormatKey(rDesc.nKey); + + const SvNumberformat* pEntry = rFieldFormatter.GetFormatter()->GetEntry(rFieldFormatter.GetFormatKey()); + DBG_ASSERT( pEntry, "OFormattedNumericControl::SetFormatDescription: invalid format key!" ); + if ( pEntry ) + { + bFallback = false; + } + + } + + if ( bFallback ) + { + rFieldFormatter.TreatAsNumber(false); + rFieldFormatter.SetFormatter(nullptr); + getTypedControlWindow()->set_text(""); + } + } + + //= OFileUrlControl + OFileUrlControl::OFileUrlControl(std::unique_ptr<SvtURLBox> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly) + : OFileUrlControl_Base(PropertyControlType::Unknown, std::move(xBuilder), std::move(xWidget), bReadOnly) + { + getTypedControlWindow()->DisableHistory(); + getTypedControlWindow()->SetPlaceHolder( PcrRes( RID_EMBED_IMAGE_PLACEHOLDER ) ) ; + } + + OFileUrlControl::~OFileUrlControl() + { + } + + void SAL_CALL OFileUrlControl::setValue(const Any& rValue) + { + OUString sURL; + SvtURLBox* pControlWindow = getTypedControlWindow(); + bool bSuccess = rValue >>= sURL; + if (bSuccess && GraphicObject::isGraphicObjectUniqueIdURL(sURL)) + sURL = pControlWindow->GetPlaceHolder(); + pControlWindow->set_entry_text(sURL); + } + + Any SAL_CALL OFileUrlControl::getValue() + { + Any aPropValue; + if (!getTypedControlWindow()->get_active_text().isEmpty()) + aPropValue <<= getTypedControlWindow()->GetURL(); + return aPropValue; + } + + Type SAL_CALL OFileUrlControl::getValueType() + { + return ::cppu::UnoType<OUString>::get(); + } + + IMPL_LINK_NOARG(OFileUrlControl, URLModifiedHdl, weld::ComboBox&, void) + { + editChanged(); + } + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/usercontrol.hxx b/extensions/source/propctrlr/usercontrol.hxx new file mode 100644 index 000000000..86b53e7f1 --- /dev/null +++ b/extensions/source/propctrlr/usercontrol.hxx @@ -0,0 +1,148 @@ +/* -*- 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 "commoncontrol.hxx" +#include <svtools/inettbc.hxx> +#include <svl/zforlist.hxx> + +class SvNumberFormatsSupplierObj; + +namespace pcr +{ + //= OFormatSampleControl + typedef CommonBehaviourControl<css::inspection::XPropertyControl, weld::Container> OFormatSampleControl_Base; + class OFormatSampleControl : public OFormatSampleControl_Base + { + private: + std::unique_ptr<weld::FormattedSpinButton> m_xSpinButton; + std::unique_ptr<weld::Entry> m_xEntry; + + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + + public: + OFormatSampleControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly); + + // XPropertyControl + virtual css::uno::Any SAL_CALL getValue() override; + virtual void SAL_CALL setValue( const css::uno::Any& _value ) override; + virtual css::uno::Type SAL_CALL getValueType() override; + + virtual void SAL_CALL disposing() override + { + m_xEntry.reset(); + m_xSpinButton.reset(); + OFormatSampleControl_Base::disposing(); + } + + virtual void SetModifyHandler() override + { + m_xEntry->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) ); + m_xEntry->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) ); + m_xSpinButton->connect_value_changed(LINK(this, CommonBehaviourControlHelper, FormattedModifiedHdl)); + m_xSpinButton->connect_changed(LINK(this, CommonBehaviourControlHelper, EditModifiedHdl)); + } + + void SetFormatSupplier(const SvNumberFormatsSupplierObj* pSupplier); + + virtual weld::Widget* getWidget() override { return getTypedControlWindow(); } + + /** returns the default preview value for the given format key + * + * \param _pNF the number formatter + * \param _nFormatKey the format key + * \return current date or time or the value 1234.56789 + */ + static double getPreviewValue(SvNumberFormatter const * pNF, sal_Int32 nFormatKey); + + private: + static double getPreviewValue( const SvNumberformat& i_rEntry ); + }; + + //= FormatDescription + struct FormatDescription + { + SvNumberFormatsSupplierObj* pSupplier; + sal_Int32 nKey; + }; + + //= OFormattedNumericControl + typedef CommonBehaviourControl<css::inspection::XPropertyControl, weld::FormattedSpinButton> OFormattedNumericControl_Base; + class OFormattedNumericControl : public OFormattedNumericControl_Base + { + public: + OFormattedNumericControl(std::unique_ptr<weld::FormattedSpinButton> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly); + + // XPropertyControl + virtual css::uno::Any SAL_CALL getValue() override; + virtual void SAL_CALL setValue( const css::uno::Any& _value ) override; + virtual css::uno::Type SAL_CALL getValueType() override; + + void SetFormatDescription( const FormatDescription& rDesc ); + + // make some FormattedField methods available + void SetDecimalDigits(sal_uInt16 nPrecision) { getTypedControlWindow()->GetFormatter().SetDecimalDigits(nPrecision); } + void SetDefaultValue(double dDef) { getTypedControlWindow()->GetFormatter().SetDefaultValue(dDef); } + + virtual void SetModifyHandler() override + { + OFormattedNumericControl_Base::SetModifyHandler(); + getTypedControlWindow()->connect_value_changed(LINK(this, CommonBehaviourControlHelper, FormattedModifiedHdl)); + getTypedControlWindow()->connect_changed(LINK(this, CommonBehaviourControlHelper, EditModifiedHdl)); + } + + virtual weld::Widget* getWidget() override { return getTypedControlWindow(); } + + protected: + virtual ~OFormattedNumericControl() override; + }; + + //= OFileUrlControl + typedef CommonBehaviourControl<css::inspection::XPropertyControl, SvtURLBox> OFileUrlControl_Base; + class OFileUrlControl : public OFileUrlControl_Base + { + private: + DECL_LINK(URLModifiedHdl, weld::ComboBox&, void); + public: + OFileUrlControl(std::unique_ptr<SvtURLBox> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly); + + // XPropertyControl + virtual css::uno::Any SAL_CALL getValue() override; + virtual void SAL_CALL setValue( const css::uno::Any& _value ) override; + virtual css::uno::Type SAL_CALL getValueType() override; + + virtual void SetModifyHandler() override + { + OFileUrlControl_Base::SetModifyHandler(); + SvtURLBox* pControlWindow = getTypedControlWindow(); + // tdf#140239 and tdf#141084 don't notify that the control has changed content until focus-out + pControlWindow->connect_focus_out(LINK(this, CommonBehaviourControlHelper, LoseFocusHdl)); + pControlWindow->connect_changed(LINK(this, OFileUrlControl, URLModifiedHdl)); + } + + virtual weld::Widget* getWidget() override { return getTypedControlWindow()->getWidget(); } + + protected: + virtual ~OFileUrlControl() override; + }; + +} // namespace pcr + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/xsddatatypes.cxx b/extensions/source/propctrlr/xsddatatypes.cxx new file mode 100644 index 000000000..fdaea8e21 --- /dev/null +++ b/extensions/source/propctrlr/xsddatatypes.cxx @@ -0,0 +1,185 @@ +/* -*- 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 "xsddatatypes.hxx" + +#include <com/sun/star/xsd/DataTypeClass.hpp> +#include <com/sun/star/xsd/XDataType.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <tools/debug.hxx> +#include <osl/diagnose.h> +#include <tools/diagnose_ex.h> + + +namespace pcr +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::xsd; + using namespace ::com::sun::star::beans; + + template< typename INTERFACE, typename ARGUMENT > + static ARGUMENT getSave( INTERFACE* pObject, ARGUMENT ( SAL_CALL INTERFACE::*pGetter )( ) ) + { + ARGUMENT aReturn = ARGUMENT(); + try + { + aReturn = (pObject->*pGetter)( ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDDataType: getSave" ); + } + return aReturn; + } + + XSDDataType::XSDDataType( const Reference< XDataType >& _rxDataType ) + :m_xDataType( _rxDataType ) + { + DBG_ASSERT( m_xDataType.is(), "XSDDataType::XSDDataType: invalid UNO object!" ); + if ( m_xDataType.is() ) + m_xFacetInfo = m_xDataType->getPropertySetInfo(); + } + + + XSDDataType::~XSDDataType() + { + } + + + sal_Int16 XSDDataType::classify() const + { + sal_Int16 nTypeClass = DataTypeClass::STRING; + try + { + if ( m_xDataType.is() ) + nTypeClass = m_xDataType->getTypeClass(); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDDataType::classify" ); + } + return nTypeClass; + } + + + bool XSDDataType::isBasicType() const + { + return getSave( m_xDataType.get(), &XDataType::getIsBasic ); + } + + + OUString XSDDataType::getName() const + { + return getSave( m_xDataType.get(), &XDataType::getName ); + } + + + void XSDDataType::setFacet( const OUString& _rFacetName, const Any& _rValue ) + { + try + { + m_xDataType->setPropertyValue( _rFacetName, _rValue ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDDataType::setFacet: caught an exception - sure this is the right data type class for this property?" ); + } + } + + + bool XSDDataType::hasFacet( const OUString& _rFacetName ) const + { + bool bReturn = false; + try + { + bReturn = m_xFacetInfo.is() && m_xFacetInfo->hasPropertyByName( _rFacetName ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDDataType::hasFacet" ); + } + return bReturn; + } + + Any XSDDataType::getFacet( const OUString& _rFacetName ) + { + Any aReturn; + try + { + aReturn = m_xDataType->getPropertyValue( _rFacetName ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDDataType::getFacet: caught an exception - sure this is the right data type class for this property?" ); + } + return aReturn; + } + + + namespace + { + void lcl_copyProperties( const Reference< XPropertySet >& _rxSource, const Reference< XPropertySet >& _rxDest ) + { + Reference< XPropertySetInfo > xSourceInfo; + if ( _rxSource.is() ) + xSourceInfo = _rxSource->getPropertySetInfo(); + Reference< XPropertySetInfo > xDestInfo; + if ( _rxDest.is() ) + xDestInfo = _rxDest->getPropertySetInfo(); + OSL_ENSURE( xSourceInfo.is() && xDestInfo.is(), "lcl_copyProperties: invalid property set( info)s!" ); + if ( !xSourceInfo.is() || !xDestInfo.is() ) + return; + + Sequence< Property > aProperties( xSourceInfo->getProperties() ); + const Property* pProperties = aProperties.getConstArray(); + const Property* pPropertiesEnd = pProperties + aProperties.getLength(); + for ( ; pProperties != pPropertiesEnd; ++pProperties ) + { + if ( xDestInfo->hasPropertyByName( pProperties->Name ) ) + _rxDest->setPropertyValue( pProperties->Name, _rxSource->getPropertyValue( pProperties->Name ) ); + } + } + } + + + void XSDDataType::copyFacetsFrom( const ::rtl::Reference< XSDDataType >& _pSourceType ) + { + OSL_ENSURE( _pSourceType.is(), "XSDDataType::copyFacetsFrom: invalid source type!" ); + if ( !_pSourceType.is() ) + return; + + try + { + Reference< XPropertySet > xSource = _pSourceType->getUnoDataType(); + Reference< XPropertySet > xDest = getUnoDataType(); + lcl_copyProperties( xSource, xDest ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDDataType::copyFacetsFrom" ); + } + } + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/xsddatatypes.hxx b/extensions/source/propctrlr/xsddatatypes.hxx new file mode 100644 index 000000000..0413d0250 --- /dev/null +++ b/extensions/source/propctrlr/xsddatatypes.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 <com/sun/star/uno/Reference.hxx> +#include <rtl/ref.hxx> +#include <salhelper/simplereferenceobject.hxx> + +namespace com::sun::star { + namespace xsd { + class XDataType; + } + namespace beans { + class XPropertySetInfo; + } +} + + +namespace pcr +{ + + + //= XSDDataType + + class XSDDataType : public salhelper::SimpleReferenceObject + { + private: + css::uno::Reference< css::xsd::XDataType > + m_xDataType; + css::uno::Reference< css::beans::XPropertySetInfo > + m_xFacetInfo; + + public: + explicit XSDDataType( + const css::uno::Reference< css::xsd::XDataType >& _rxDataType + ); + + /// retrieves the underlying UNO component + const css::uno::Reference< css::xsd::XDataType >& + getUnoDataType() const { return m_xDataType; } + + /// classifies the data typ + sal_Int16 classify() const; + + // attribute access + OUString getName() const; + bool isBasicType() const; + + /// determines whether a given facet exists at the type + bool hasFacet( const OUString& _rFacetName ) const; + /// retrieves a facet value + css::uno::Any getFacet( const OUString& _rFacetName ); + /// sets a facet value + void setFacet( const OUString& _rFacetName, const css::uno::Any& _rFacetValue ); + + /** copies as much facets (values, respectively) from a give data type instance + */ + void copyFacetsFrom( const ::rtl::Reference< XSDDataType >& _pSourceType ); + + protected: + virtual ~XSDDataType() override; + + private: + XSDDataType( const XSDDataType& ) = delete; + XSDDataType& operator=( const XSDDataType& ) = delete; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/xsdvalidationhelper.cxx b/extensions/source/propctrlr/xsdvalidationhelper.cxx new file mode 100644 index 000000000..b5b96cc28 --- /dev/null +++ b/extensions/source/propctrlr/xsdvalidationhelper.cxx @@ -0,0 +1,406 @@ +/* -*- 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 "xsdvalidationhelper.hxx" +#include "xsddatatypes.hxx" +#include "formstrings.hxx" + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/xsd/DataTypeClass.hpp> +#include <com/sun/star/util/NumberFormat.hpp> +#include <com/sun/star/util/XNumberFormatTypes.hpp> +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> +#include <com/sun/star/xforms/XDataTypeRepository.hpp> +#include <unotools/syslocale.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <tools/diagnose_ex.h> + + +namespace pcr +{ + + + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::xsd; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::xforms; + + namespace NumberFormat = ::com::sun::star::util::NumberFormat; + + + //= XSDValidationHelper + + + XSDValidationHelper::XSDValidationHelper( ::osl::Mutex& _rMutex, const Reference< XPropertySet >& _rxIntrospectee, const Reference< frame::XModel >& _rxContextDocument ) + :EFormsHelper( _rMutex, _rxIntrospectee, _rxContextDocument ) + ,m_bInspectingFormattedField( false ) + { + try + { + Reference< XPropertySetInfo > xPSI; + Reference< XServiceInfo > xSI( _rxIntrospectee, UNO_QUERY ); + if ( m_xControlModel.is() ) + xPSI = m_xControlModel->getPropertySetInfo(); + if ( xPSI.is() + && xPSI->hasPropertyByName( PROPERTY_FORMATKEY ) + && xPSI->hasPropertyByName( PROPERTY_FORMATSSUPPLIER ) + && xSI.is() + && xSI->supportsService( SERVICE_COMPONENT_FORMATTEDFIELD ) + ) + m_bInspectingFormattedField = true; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION("extensions.propctrlr", + "caught an exception while examining the introspectee!"); + } + } + + + void XSDValidationHelper::getAvailableDataTypeNames( std::vector< OUString >& /* [out] */ _rNames ) const + { + _rNames.resize( 0 ); + + try + { + Reference< XDataTypeRepository > xRepository = getDataTypeRepository(); + if ( xRepository.is() ) + { + const Sequence<OUString> aElements = xRepository->getElementNames(); + + _rNames.resize( aElements.getLength() ); + std::copy( aElements.begin(), aElements.end(), _rNames.begin() ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDValidationHelper::getAvailableDataTypeNames" ); + } + } + + + Reference< XDataTypeRepository > XSDValidationHelper::getDataTypeRepository() const + { + Reference< XDataTypeRepository > xRepository; + + Reference< xforms::XModel > xModel( getCurrentFormModel( ) ); + if ( xModel.is() ) + xRepository = xModel->getDataTypeRepository(); + + return xRepository; + } + + + Reference< XDataTypeRepository > XSDValidationHelper::getDataTypeRepository( const OUString& _rModelName ) const + { + Reference< XDataTypeRepository > xRepository; + + Reference< xforms::XModel > xModel( getFormModelByName( _rModelName ) ); + if ( xModel.is() ) + xRepository = xModel->getDataTypeRepository(); + + return xRepository; + } + + + Reference< XDataType > XSDValidationHelper::getDataType( const OUString& _rName ) const + { + Reference< XDataType > xDataType; + + if ( !_rName.isEmpty() ) + { + Reference< XDataTypeRepository > xRepository = getDataTypeRepository(); + if ( xRepository.is() ) + xDataType = xRepository->getDataType( _rName ); + } + return xDataType; + } + + + OUString XSDValidationHelper::getValidatingDataTypeName( ) const + { + OUString sDataTypeName; + try + { + Reference< XPropertySet > xBinding( getCurrentBinding() ); + // it's allowed here to not (yet) have a binding + if ( xBinding.is() ) + { + OSL_VERIFY( xBinding->getPropertyValue( PROPERTY_XSD_DATA_TYPE ) >>= sDataTypeName ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDValidationHelper::getValidatingDataTypeName" ); + } + return sDataTypeName; + } + + + ::rtl::Reference< XSDDataType > XSDValidationHelper::getDataTypeByName( const OUString& _rName ) const + { + ::rtl::Reference< XSDDataType > pReturn; + + try + { + Reference< XDataType > xValidatedAgainst; + + if ( !_rName.isEmpty() ) + xValidatedAgainst = getDataType( _rName ); + + if ( xValidatedAgainst.is() ) + pReturn = new XSDDataType( xValidatedAgainst ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDValidationHelper::getDataTypeByName" ); + } + + return pReturn; + } + + + ::rtl::Reference< XSDDataType > XSDValidationHelper::getValidatingDataType( ) const + { + return getDataTypeByName( getValidatingDataTypeName() ); + } + + + bool XSDValidationHelper::cloneDataType( const ::rtl::Reference< XSDDataType >& _pDataType, const OUString& _rNewName ) const + { + OSL_ENSURE( _pDataType.is(), "XSDValidationHelper::removeDataTypeFromRepository: invalid data type!" ); + if ( !_pDataType.is() ) + return false; + + try + { + Reference< XDataTypeRepository > xRepository( getDataTypeRepository() ); + OSL_ENSURE( xRepository.is(), "XSDValidationHelper::removeDataTypeFromRepository: invalid data type repository!" ); + if ( !xRepository.is() ) + return false; + + Reference< XDataType > xDataType( _pDataType->getUnoDataType() ); + OSL_ENSURE( xDataType.is(), "XSDValidationHelper::removeDataTypeFromRepository: invalid data type (II)!" ); + if ( !xDataType.is() ) + return false; + + xRepository->cloneDataType( xDataType->getName(), _rNewName ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDValidationHelper::cloneDataType" ); + } + return true; + } + + + bool XSDValidationHelper::removeDataTypeFromRepository( const OUString& _rName ) const + { + try + { + Reference< XDataTypeRepository > xRepository( getDataTypeRepository() ); + OSL_ENSURE( xRepository.is(), "XSDValidationHelper::removeDataTypeFromRepository: invalid data type repository!" ); + if ( !xRepository.is() ) + return false; + + if ( !xRepository->hasByName( _rName ) ) + { + OSL_FAIL( "XSDValidationHelper::removeDataTypeFromRepository: invalid repository and/or data type!" ); + return false; + } + + xRepository->revokeDataType( _rName ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDValidationHelper::removeDataTypeFromRepository" ); + return false; + } + return true; + } + + + void XSDValidationHelper::setValidatingDataTypeByName( const OUString& _rName ) const + { + try + { + Reference< XPropertySet > xBinding( getCurrentBinding() ); + OSL_ENSURE( xBinding.is(), "XSDValidationHelper::setValidatingDataTypeByName: no active binding - how this?" ); + + if ( xBinding.is() ) + { + // get the old data type - this is necessary for notifying property changes + OUString sOldDataTypeName; + OSL_VERIFY( xBinding->getPropertyValue( PROPERTY_XSD_DATA_TYPE ) >>= sOldDataTypeName ); + Reference< XPropertySet > xOldType; + try { + xOldType = getDataType( sOldDataTypeName ); + } catch( const Exception& ) { } + + // set the new data type name + xBinding->setPropertyValue( PROPERTY_XSD_DATA_TYPE, Any( _rName ) ); + + // retrieve the new data type object + Reference< XPropertySet > xNewType = getDataType( _rName ); + + // fire any changes in the properties which result from this new type + std::set< OUString > aFilter; aFilter.insert( PROPERTY_NAME ); + firePropertyChanges( xOldType, xNewType, aFilter ); + + // fire the change in the Data Type property + OUString sNewDataTypeName; + OSL_VERIFY( xBinding->getPropertyValue( PROPERTY_XSD_DATA_TYPE ) >>= sNewDataTypeName ); + firePropertyChange( PROPERTY_XSD_DATA_TYPE, Any( sOldDataTypeName ), Any( sNewDataTypeName ) ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); + } + } + + + void XSDValidationHelper::copyDataType( const OUString& _rFromModel, const OUString& _rToModel, + const OUString& _rDataTypeName ) const + { + if ( _rFromModel == _rToModel ) + // nothing to do (me thinks) + return; + + try + { + Reference< XDataTypeRepository > xFromRepository, xToRepository; + if ( !_rFromModel.isEmpty() ) + xFromRepository = getDataTypeRepository( _rFromModel ); + if ( !_rToModel.isEmpty() ) + xToRepository = getDataTypeRepository( _rToModel ); + + if ( !xFromRepository.is() || !xToRepository.is() ) + return; + + if ( !xFromRepository->hasByName( _rDataTypeName ) || xToRepository->hasByName( _rDataTypeName ) ) + // not existent in the source, or already existent (by name) in the destination + return; + + // determine the built-in type belonging to the source type + ::rtl::Reference< XSDDataType > pSourceType = new XSDDataType( xFromRepository->getDataType( _rDataTypeName ) ); + OUString sTargetBaseType = getBasicTypeNameForClass( pSourceType->classify(), xToRepository ); + + // create the target type + Reference< XDataType > xTargetType = xToRepository->cloneDataType( sTargetBaseType, _rDataTypeName ); + ::rtl::Reference< XSDDataType > pTargetType = new XSDDataType( xTargetType ); + + // copy the facets + pTargetType->copyFacetsFrom( pSourceType ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDValidationHelper::copyDataType" ); + } + } + + + void XSDValidationHelper::findDefaultFormatForIntrospectee() + { + try + { + ::rtl::Reference< XSDDataType > xDataType = getValidatingDataType(); + if ( xDataType.is() ) + { + // find a NumberFormat type corresponding to the DataTypeClass + sal_Int16 nNumberFormatType = NumberFormat::NUMBER; + switch ( xDataType->classify() ) + { + case DataTypeClass::DATETIME: + nNumberFormatType = NumberFormat::DATETIME; + break; + case DataTypeClass::DATE: + nNumberFormatType = NumberFormat::DATE; + break; + case DataTypeClass::TIME: + nNumberFormatType = NumberFormat::TIME; + break; + case DataTypeClass::STRING: + case DataTypeClass::anyURI: + case DataTypeClass::QName: + case DataTypeClass::NOTATION: + nNumberFormatType = NumberFormat::TEXT; + break; + } + + // get the number formatter from the introspectee + Reference< XNumberFormatsSupplier > xSupplier; + Reference< XNumberFormatTypes > xFormatTypes; + OSL_VERIFY( m_xControlModel->getPropertyValue( PROPERTY_FORMATSSUPPLIER ) >>= xSupplier ); + if ( xSupplier.is() ) + xFormatTypes.set(xSupplier->getNumberFormats(), css::uno::UNO_QUERY); + OSL_ENSURE( xFormatTypes.is(), "XSDValidationHelper::findDefaultFormatForIntrospectee: no number formats for the introspectee!" ); + if ( !xFormatTypes.is() ) + return; + + // and the standard format for the given NumberFormat type + sal_Int32 nDesiredFormat = xFormatTypes->getStandardFormat( nNumberFormatType, SvtSysLocale().GetLanguageTag().getLocale() ); + + // set this at the introspectee + m_xControlModel->setPropertyValue( PROPERTY_FORMATKEY, Any( nDesiredFormat ) ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDValidationHelper::findDefaultFormatForIntrospectee" ); + } + } + + + OUString XSDValidationHelper::getBasicTypeNameForClass( sal_Int16 _nClass ) const + { + return getBasicTypeNameForClass( _nClass, getDataTypeRepository() ); + } + + + OUString XSDValidationHelper::getBasicTypeNameForClass( sal_Int16 _nClass, const Reference< XDataTypeRepository >& _rxRepository ) + { + OUString sReturn; + OSL_ENSURE( _rxRepository.is(), "XSDValidationHelper::getBasicTypeNameForClass: invalid repository!" ); + if ( !_rxRepository.is() ) + return sReturn; + + try + { + Reference< XDataType > xDataType = _rxRepository->getBasicDataType( _nClass ); + OSL_ENSURE( xDataType.is(), "XSDValidationHelper::getBasicTypeNameForClass: invalid data type returned!" ); + if ( xDataType.is() ) + sReturn = xDataType->getName(); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDValidationHelper::getBasicTypeNameForClass" ); + } + + return sReturn; + } + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/xsdvalidationhelper.hxx b/extensions/source/propctrlr/xsdvalidationhelper.hxx new file mode 100644 index 000000000..2a77ff584 --- /dev/null +++ b/extensions/source/propctrlr/xsdvalidationhelper.hxx @@ -0,0 +1,137 @@ +/* -*- 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 "eformshelper.hxx" +#include "xsddatatypes.hxx" + +#include <com/sun/star/xsd/XDataType.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <rtl/ref.hxx> + + +namespace pcr +{ + + + class XSDDataType; + + //= XSDValidationHelper + + class XSDValidationHelper : public EFormsHelper + { + private: + bool m_bInspectingFormattedField; + public: + bool isInspectingFormattedField() const { return m_bInspectingFormattedField; } + + public: + XSDValidationHelper( + ::osl::Mutex& _rMutex, + const css::uno::Reference< css::beans::XPropertySet >& _rxIntrospectee, + const css::uno::Reference< css::frame::XModel >& _rxContextDocument + ); + + /** retrieves the names of all XForms models in the document the control lives in + */ + void getAvailableDataTypeNames( std::vector< OUString >& /* [out] */ _rNames ) const; + + /** retrieves a particular data type given by name + */ + ::rtl::Reference< XSDDataType > + getDataTypeByName( const OUString& _rName ) const; + + /** retrieves the DataType instance which the control model is currently validated against + + If there is a binding set at our control model, which at the same time acts as validator, + and if this validator is bound to an XDataType, then this data type is retrieved here. + */ + ::rtl::Reference< XSDDataType > + getValidatingDataType( ) const; + + /** retrieves the name of the data type which the control model is currently validated against + + @seealso getValidatingDataType + */ + OUString + getValidatingDataTypeName( ) const; + + /** binds the validator to a new data type + + To be called with an active binding only. + */ + void setValidatingDataTypeByName( const OUString& _rName ) const; + + /** removes the data type given by name from the data type repository + */ + bool removeDataTypeFromRepository( const OUString& _rName ) const; + + /** creates a new data type, which is a clone of an existing data type + */ + bool cloneDataType( const ::rtl::Reference< XSDDataType >& _pDataType, const OUString& _rNewName ) const; + + /** retrieves the name of the basic data type which has the given class + */ + OUString + getBasicTypeNameForClass( sal_Int16 _eClass ) const; + + /** copy a data type from one model to another + + If a data type with the given name already exists in the target model, then nothing + happens. In particular, the facets of the data type are not copied. + */ + void copyDataType( const OUString& _rFromModel, const OUString& _rToModel, + const OUString& _rDataTypeName ) const; + + /** finds (and sets) a default format for the formatted field we're inspecting, + according to the current data type the control value is evaluated against + */ + void findDefaultFormatForIntrospectee(); + + private: + /** retrieves the data type repository associated with the current model + */ + css::uno::Reference< css::xforms::XDataTypeRepository > + getDataTypeRepository() const; + + /** retrieves the data type repository associated with any model + */ + css::uno::Reference< css::xforms::XDataTypeRepository > + getDataTypeRepository( const OUString& _rModelName ) const; + + /** retrieves the data type object for the given name + */ + css::uno::Reference< css::xsd::XDataType > + getDataType( const OUString& _rName ) const; + + /** retrieves the name of the basic data type which has the given class, in the given repository + */ + static OUString + getBasicTypeNameForClass( + sal_Int16 _nClass, + const css::uno::Reference< css::xforms::XDataTypeRepository >& _rxRepository + ); + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/xsdvalidationpropertyhandler.cxx b/extensions/source/propctrlr/xsdvalidationpropertyhandler.cxx new file mode 100644 index 000000000..1f8351cdb --- /dev/null +++ b/extensions/source/propctrlr/xsdvalidationpropertyhandler.cxx @@ -0,0 +1,673 @@ +/* -*- 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 "xsdvalidationpropertyhandler.hxx" +#include "formstrings.hxx" +#include "formmetadata.hxx" +#include "xsddatatypes.hxx" +#include "modulepcr.hxx" +#include <strings.hrc> +#include <propctrlr.h> +#include "newdatatype.hxx" +#include "xsdvalidationhelper.hxx" +#include "pcrcommon.hxx" +#include "handlerhelper.hxx" + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/lang/NullPointerException.hpp> +#include <com/sun/star/xsd/WhiteSpaceTreatment.hpp> +#include <com/sun/star/xsd/DataTypeClass.hpp> +#include <com/sun/star/inspection/PropertyControlType.hpp> +#include <com/sun/star/beans/Optional.hpp> +#include <com/sun/star/inspection/XObjectInspectorUI.hpp> +#include <com/sun/star/inspection/PropertyLineElement.hpp> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <tools/debug.hxx> +#include <sal/macros.h> + +#include <algorithm> +#include <limits> + + +namespace pcr +{ + + + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::xforms; + using namespace ::com::sun::star::xsd; + using namespace ::com::sun::star::script; + using namespace ::com::sun::star::inspection; + + using ::com::sun::star::beans::PropertyAttribute::MAYBEVOID; + + + //= XSDValidationPropertyHandler + + XSDValidationPropertyHandler::XSDValidationPropertyHandler( const Reference< XComponentContext >& _rxContext ) + :PropertyHandlerComponent( _rxContext ) + { + } + + + XSDValidationPropertyHandler::~XSDValidationPropertyHandler() + { + } + + + OUString XSDValidationPropertyHandler::getImplementationName( ) + { + return "com.sun.star.comp.extensions.XSDValidationPropertyHandler"; + } + + + Sequence< OUString > XSDValidationPropertyHandler::getSupportedServiceNames( ) + { + return{ "com.sun.star.form.inspection.XSDValidationPropertyHandler" }; + } + + + Any SAL_CALL XSDValidationPropertyHandler::getPropertyValue( const OUString& _rPropertyName ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + OSL_ENSURE(m_pHelper, "XSDValidationPropertyHandler::getPropertyValue: inconsistency!"); + // if we survived impl_getPropertyId_throwUnknownProperty, we should have a helper, since no helper implies no properties + + Any aReturn; + ::rtl::Reference< XSDDataType > pType = m_pHelper->getValidatingDataType(); + switch ( nPropId ) + { + // common facets + case PROPERTY_ID_XSD_DATA_TYPE: aReturn = pType.is() ? pType->getFacet( PROPERTY_NAME ) : Any( OUString() ); break; + case PROPERTY_ID_XSD_WHITESPACES:aReturn = pType.is() ? pType->getFacet( PROPERTY_XSD_WHITESPACES ) : Any( WhiteSpaceTreatment::Preserve ); break; + case PROPERTY_ID_XSD_PATTERN: aReturn = pType.is() ? pType->getFacet( PROPERTY_XSD_PATTERN ) : Any( OUString() ); break; + + // all other properties are simply forwarded, if they exist at the given type + default: + { + if ( pType.is() && pType->hasFacet( _rPropertyName ) ) + aReturn = pType->getFacet( _rPropertyName ); + } + break; + } + + return aReturn; + } + + + void SAL_CALL XSDValidationPropertyHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + OSL_ENSURE(m_pHelper, "XSDValidationPropertyHandler::getPropertyValue: inconsistency!"); + // if we survived impl_getPropertyId_throwUnknownProperty, we should have a helper, since no helper implies no properties + + if ( PROPERTY_ID_XSD_DATA_TYPE == nPropId ) + { + OUString sTypeName; + OSL_VERIFY( _rValue >>= sTypeName ); + m_pHelper->setValidatingDataTypeByName( sTypeName ); + impl_setContextDocumentModified_nothrow(); + return; + } + + ::rtl::Reference< XSDDataType > pType = m_pHelper->getValidatingDataType(); + if ( !pType.is() ) + { + OSL_FAIL( "XSDValidationPropertyHandler::setPropertyValue: you're trying to set a type facet, without a current type!" ); + return; + } + + pType->setFacet( _rPropertyName, _rValue ); + impl_setContextDocumentModified_nothrow(); + } + + + void XSDValidationPropertyHandler::onNewComponent() + { + PropertyHandlerComponent::onNewComponent(); + + Reference< frame::XModel > xDocument( impl_getContextDocument_nothrow() ); + DBG_ASSERT( xDocument.is(), "XSDValidationPropertyHandler::onNewComponent: no document!" ); + if ( EFormsHelper::isEForm( xDocument ) ) + m_pHelper.reset( new XSDValidationHelper( m_aMutex, m_xComponent, xDocument ) ); + else + m_pHelper.reset(); + } + + + Sequence< Property > XSDValidationPropertyHandler::doDescribeSupportedProperties() const + { + std::vector< Property > aProperties; + + if (m_pHelper) + { + bool bAllowBinding = m_pHelper->canBindToAnyDataType(); + + if ( bAllowBinding ) + { + aProperties.reserve( 12 ); + + addStringPropertyDescription( aProperties, PROPERTY_XSD_DATA_TYPE ); + addInt16PropertyDescription ( aProperties, PROPERTY_XSD_WHITESPACES ); + addStringPropertyDescription( aProperties, PROPERTY_XSD_PATTERN ); + + // string facets + addInt32PropertyDescription( aProperties, PROPERTY_XSD_LENGTH, MAYBEVOID ); + addInt32PropertyDescription( aProperties, PROPERTY_XSD_MIN_LENGTH, MAYBEVOID ); + addInt32PropertyDescription( aProperties, PROPERTY_XSD_MAX_LENGTH, MAYBEVOID ); + + // decimal facets + addInt32PropertyDescription( aProperties, PROPERTY_XSD_TOTAL_DIGITS, MAYBEVOID ); + addInt32PropertyDescription( aProperties, PROPERTY_XSD_FRACTION_DIGITS, MAYBEVOID ); + + // facets for different types + addInt16PropertyDescription( aProperties, PROPERTY_XSD_MAX_INCLUSIVE_INT, MAYBEVOID ); + addInt16PropertyDescription( aProperties, PROPERTY_XSD_MAX_EXCLUSIVE_INT, MAYBEVOID ); + addInt16PropertyDescription( aProperties, PROPERTY_XSD_MIN_INCLUSIVE_INT, MAYBEVOID ); + addInt16PropertyDescription( aProperties, PROPERTY_XSD_MIN_EXCLUSIVE_INT, MAYBEVOID ); + addDoublePropertyDescription( aProperties, PROPERTY_XSD_MAX_INCLUSIVE_DOUBLE, MAYBEVOID ); + addDoublePropertyDescription( aProperties, PROPERTY_XSD_MAX_EXCLUSIVE_DOUBLE, MAYBEVOID ); + addDoublePropertyDescription( aProperties, PROPERTY_XSD_MIN_INCLUSIVE_DOUBLE, MAYBEVOID ); + addDoublePropertyDescription( aProperties, PROPERTY_XSD_MIN_EXCLUSIVE_DOUBLE, MAYBEVOID ); + addDatePropertyDescription( aProperties, PROPERTY_XSD_MAX_INCLUSIVE_DATE, MAYBEVOID ); + addDatePropertyDescription( aProperties, PROPERTY_XSD_MAX_EXCLUSIVE_DATE, MAYBEVOID ); + addDatePropertyDescription( aProperties, PROPERTY_XSD_MIN_INCLUSIVE_DATE, MAYBEVOID ); + addDatePropertyDescription( aProperties, PROPERTY_XSD_MIN_EXCLUSIVE_DATE, MAYBEVOID ); + addTimePropertyDescription( aProperties, PROPERTY_XSD_MAX_INCLUSIVE_TIME, MAYBEVOID ); + addTimePropertyDescription( aProperties, PROPERTY_XSD_MAX_EXCLUSIVE_TIME, MAYBEVOID ); + addTimePropertyDescription( aProperties, PROPERTY_XSD_MIN_INCLUSIVE_TIME, MAYBEVOID ); + addTimePropertyDescription( aProperties, PROPERTY_XSD_MIN_EXCLUSIVE_TIME, MAYBEVOID ); + addDateTimePropertyDescription( aProperties, PROPERTY_XSD_MAX_INCLUSIVE_DATE_TIME, MAYBEVOID ); + addDateTimePropertyDescription( aProperties, PROPERTY_XSD_MAX_EXCLUSIVE_DATE_TIME, MAYBEVOID ); + addDateTimePropertyDescription( aProperties, PROPERTY_XSD_MIN_INCLUSIVE_DATE_TIME, MAYBEVOID ); + addDateTimePropertyDescription( aProperties, PROPERTY_XSD_MIN_EXCLUSIVE_DATE_TIME, MAYBEVOID ); + } + } + + return comphelper::containerToSequence( aProperties ); + } + + + Sequence< OUString > SAL_CALL XSDValidationPropertyHandler::getSupersededProperties( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + std::vector< OUString > aSuperfluous; + if (m_pHelper) + { + aSuperfluous.push_back( OUString(PROPERTY_CONTROLSOURCE) ); + aSuperfluous.push_back( OUString(PROPERTY_EMPTY_IS_NULL) ); + aSuperfluous.push_back( OUString(PROPERTY_FILTERPROPOSAL) ); + aSuperfluous.push_back( OUString(PROPERTY_LISTSOURCETYPE) ); + aSuperfluous.push_back( OUString(PROPERTY_LISTSOURCE) ); + aSuperfluous.push_back( OUString(PROPERTY_BOUNDCOLUMN) ); + + bool bAllowBinding = m_pHelper->canBindToAnyDataType(); + + if ( bAllowBinding ) + { + aSuperfluous.push_back( OUString(PROPERTY_MAXTEXTLEN) ); + aSuperfluous.push_back( OUString(PROPERTY_VALUEMIN) ); + aSuperfluous.push_back( OUString(PROPERTY_VALUEMAX) ); + aSuperfluous.push_back( OUString(PROPERTY_DECIMAL_ACCURACY) ); + aSuperfluous.push_back( OUString(PROPERTY_TIMEMIN) ); + aSuperfluous.push_back( OUString(PROPERTY_TIMEMAX) ); + aSuperfluous.push_back( OUString(PROPERTY_DATEMIN) ); + aSuperfluous.push_back( OUString(PROPERTY_DATEMAX) ); + aSuperfluous.push_back( OUString(PROPERTY_EFFECTIVE_MIN) ); + aSuperfluous.push_back( OUString(PROPERTY_EFFECTIVE_MAX) ); + } + } + + return comphelper::containerToSequence( aSuperfluous ); + } + + + Sequence< OUString > SAL_CALL XSDValidationPropertyHandler::getActuatingProperties( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + std::vector< OUString > aInterestedInActuations; + if (m_pHelper) + { + aInterestedInActuations.push_back( OUString(PROPERTY_XSD_DATA_TYPE) ); + aInterestedInActuations.push_back( OUString(PROPERTY_XML_DATA_MODEL) ); + } + return comphelper::containerToSequence( aInterestedInActuations ); + } + + + namespace + { + void showPropertyUI( const Reference< XObjectInspectorUI >& _rxInspectorUI, const OUString& _rPropertyName, bool _bShow ) + { + if ( _bShow ) + _rxInspectorUI->showPropertyUI( _rPropertyName ); + else + _rxInspectorUI->hidePropertyUI( _rPropertyName ); + } + } + + + LineDescriptor SAL_CALL XSDValidationPropertyHandler::describePropertyLine( const OUString& _rPropertyName, + const Reference< XPropertyControlFactory >& _rxControlFactory ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !_rxControlFactory.is() ) + throw NullPointerException(); + if (!m_pHelper) + throw RuntimeException(); + + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + LineDescriptor aDescriptor; + if ( nPropId != PROPERTY_ID_XSD_DATA_TYPE ) + aDescriptor.IndentLevel = 1; + + // collect some information about the to-be-created control + sal_Int16 nControlType = PropertyControlType::TextField; + std::vector< OUString > aListEntries; + Optional< double > aMinValue( false, 0 ); + Optional< double > aMaxValue( false, 0 ); + + switch ( nPropId ) + { + case PROPERTY_ID_XSD_DATA_TYPE: + nControlType = PropertyControlType::ListBox; + + implGetAvailableDataTypeNames( aListEntries ); + + aDescriptor.PrimaryButtonId = UID_PROP_ADD_DATA_TYPE; + aDescriptor.SecondaryButtonId = UID_PROP_REMOVE_DATA_TYPE; + aDescriptor.HasPrimaryButton = aDescriptor.HasSecondaryButton = true; + aDescriptor.PrimaryButtonImageURL = "private:graphicrepository/extensions/res/buttonplus.png"; + aDescriptor.SecondaryButtonImageURL = "private:graphicrepository/extensions/res/buttonminus.png"; + break; + + case PROPERTY_ID_XSD_WHITESPACES: + { + nControlType = PropertyControlType::ListBox; + aListEntries = m_pInfoService->getPropertyEnumRepresentations( PROPERTY_ID_XSD_WHITESPACES ); + } + break; + + case PROPERTY_ID_XSD_PATTERN: + nControlType = PropertyControlType::TextField; + break; + + case PROPERTY_ID_XSD_LENGTH: + case PROPERTY_ID_XSD_MIN_LENGTH: + case PROPERTY_ID_XSD_MAX_LENGTH: + nControlType = PropertyControlType::NumericField; + break; + + case PROPERTY_ID_XSD_TOTAL_DIGITS: + case PROPERTY_ID_XSD_FRACTION_DIGITS: + nControlType = PropertyControlType::NumericField; + break; + + case PROPERTY_ID_XSD_MAX_INCLUSIVE_INT: + case PROPERTY_ID_XSD_MAX_EXCLUSIVE_INT: + case PROPERTY_ID_XSD_MIN_INCLUSIVE_INT: + case PROPERTY_ID_XSD_MIN_EXCLUSIVE_INT: + { + nControlType = PropertyControlType::NumericField; + + // handle limits for various 'INT' types according to + // their actual semantics (year, month, day) + + ::rtl::Reference< XSDDataType > xDataType( m_pHelper->getValidatingDataType() ); + sal_Int16 nTypeClass = xDataType.is() ? xDataType->classify() : DataTypeClass::STRING; + + aMinValue.IsPresent = aMaxValue.IsPresent = true; + aMinValue.Value = DataTypeClass::gYear == nTypeClass ? 0 : 1; + aMaxValue.Value = std::numeric_limits< sal_Int32 >::max(); + if ( DataTypeClass::gMonth == nTypeClass ) + aMaxValue.Value = 12; + else if ( DataTypeClass::gDay == nTypeClass ) + aMaxValue.Value = 31; + } + break; + + case PROPERTY_ID_XSD_MAX_INCLUSIVE_DOUBLE: + case PROPERTY_ID_XSD_MAX_EXCLUSIVE_DOUBLE: + case PROPERTY_ID_XSD_MIN_INCLUSIVE_DOUBLE: + case PROPERTY_ID_XSD_MIN_EXCLUSIVE_DOUBLE: + nControlType = PropertyControlType::NumericField; + // TODO/eForms: do we have "auto-digits"? + break; + + case PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE: + case PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE: + case PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE: + case PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE: + nControlType = PropertyControlType::DateField; + break; + + case PROPERTY_ID_XSD_MAX_INCLUSIVE_TIME: + case PROPERTY_ID_XSD_MAX_EXCLUSIVE_TIME: + case PROPERTY_ID_XSD_MIN_INCLUSIVE_TIME: + case PROPERTY_ID_XSD_MIN_EXCLUSIVE_TIME: + nControlType = PropertyControlType::TimeField; + break; + + case PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE_TIME: + case PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE_TIME: + case PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE_TIME: + case PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE_TIME: + nControlType = PropertyControlType::DateTimeField; + break; + + default: + OSL_FAIL( "XSDValidationPropertyHandler::describePropertyLine: cannot handle this property!" ); + break; + } + + switch ( nControlType ) + { + case PropertyControlType::ListBox: + aDescriptor.Control = PropertyHandlerHelper::createListBoxControl( _rxControlFactory, std::move(aListEntries), false, false ); + break; + case PropertyControlType::NumericField: + aDescriptor.Control = PropertyHandlerHelper::createNumericControl( _rxControlFactory, 0, aMinValue, aMaxValue ); + break; + default: + aDescriptor.Control = _rxControlFactory->createPropertyControl( nControlType, false ); + break; + } + + aDescriptor.Category = "Data"; + aDescriptor.DisplayName = m_pInfoService->getPropertyTranslation( nPropId ); + aDescriptor.HelpURL = HelpIdUrl::getHelpURL( m_pInfoService->getPropertyHelpId( nPropId ) ); + + return aDescriptor; + } + + + InteractiveSelectionResult SAL_CALL XSDValidationPropertyHandler::onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, Any& /*_rData*/, const Reference< XObjectInspectorUI >& _rxInspectorUI ) + { + if ( !_rxInspectorUI.is() ) + throw NullPointerException(); + + ::osl::MutexGuard aGuard( m_aMutex ); + OSL_ENSURE(m_pHelper, "XSDValidationPropertyHandler::onInteractivePropertySelection: we " + "don't have any SupportedProperties!"); + if (!m_pHelper) + return InteractiveSelectionResult_Cancelled; + + PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); + + switch ( nPropId ) + { + case PROPERTY_ID_XSD_DATA_TYPE: + { + if ( _bPrimary ) + { + OUString sNewDataTypeName; + if ( implPrepareCloneDataCurrentType( sNewDataTypeName ) ) + { + implDoCloneCurrentDataType( sNewDataTypeName ); + return InteractiveSelectionResult_Success; + } + } + else + return implPrepareRemoveCurrentDataType() && implDoRemoveCurrentDataType() ? InteractiveSelectionResult_Success : InteractiveSelectionResult_Cancelled; + } + break; + + default: + OSL_FAIL( "XSDValidationPropertyHandler::onInteractivePropertySelection: unexpected property to build a dedicated UI!" ); + break; + } + return InteractiveSelectionResult_Cancelled; + } + + + void SAL_CALL XSDValidationPropertyHandler::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyHandlerComponent::addPropertyChangeListener( _rxListener ); + if (m_pHelper) + m_pHelper->registerBindingListener( _rxListener ); + } + + + void SAL_CALL XSDValidationPropertyHandler::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if (m_pHelper) + m_pHelper->revokeBindingListener( _rxListener ); + PropertyHandlerComponent::removePropertyChangeListener( _rxListener ); + } + + + bool XSDValidationPropertyHandler::implPrepareCloneDataCurrentType( OUString& _rNewName ) + { + OSL_PRECOND( + m_pHelper, + "XSDValidationPropertyHandler::implPrepareCloneDataCurrentType: this will crash!"); + + ::rtl::Reference< XSDDataType > pType = m_pHelper->getValidatingDataType(); + if ( !pType.is() ) + { + OSL_FAIL( "XSDValidationPropertyHandler::implPrepareCloneDataCurrentType: invalid current data type!" ); + return false; + } + + std::vector< OUString > aExistentNames; + m_pHelper->getAvailableDataTypeNames( aExistentNames ); + + NewDataTypeDialog aDialog( nullptr, pType->getName(), aExistentNames ); // TODO/eForms: proper parent + if (aDialog.run() != RET_OK) + return false; + + _rNewName = aDialog.GetName(); + return true; + } + + + void XSDValidationPropertyHandler::implDoCloneCurrentDataType( const OUString& _rNewName ) + { + OSL_PRECOND(m_pHelper, + "XSDValidationPropertyHandler::implDoCloneCurrentDataType: this will crash!"); + + ::rtl::Reference< XSDDataType > pType = m_pHelper->getValidatingDataType(); + if ( !pType.is() ) + return; + + if ( !m_pHelper->cloneDataType( pType, _rNewName ) ) + return; + + m_pHelper->setValidatingDataTypeByName( _rNewName ); + } + + bool XSDValidationPropertyHandler::implPrepareRemoveCurrentDataType() + { + OSL_PRECOND( + m_pHelper, + "XSDValidationPropertyHandler::implPrepareRemoveCurrentDataType: this will crash!"); + + ::rtl::Reference< XSDDataType > pType = m_pHelper->getValidatingDataType(); + if ( !pType.is() ) + { + OSL_FAIL( "XSDValidationPropertyHandler::implPrepareRemoveCurrentDataType: invalid current data type!" ); + return false; + } + + // confirmation message + OUString sConfirmation( PcrRes( RID_STR_CONFIRM_DELETE_DATA_TYPE ) ); + sConfirmation = sConfirmation.replaceFirst( "#type#", pType->getName() ); + + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr, // TODO/eForms: proper parent + VclMessageType::Question, VclButtonsType::YesNo, + sConfirmation)); + return xQueryBox->run() == RET_YES; + } + + bool XSDValidationPropertyHandler::implDoRemoveCurrentDataType() + { + OSL_PRECOND(m_pHelper, + "XSDValidationPropertyHandler::implDoRemoveCurrentDataType: this will crash!"); + + ::rtl::Reference< XSDDataType > pType = m_pHelper->getValidatingDataType(); + if ( !pType.is() ) + return false; + + // set a new data type at the binding, which is the "basic" type for the one + // we are going to delete + // (do this before the actual deletion, so the old type is still valid for property change + // notifications) + m_pHelper->setValidatingDataTypeByName( m_pHelper->getBasicTypeNameForClass( pType->classify() ) ); + // now remove the type + m_pHelper->removeDataTypeFromRepository( pType->getName() ); + + return true; + } + + + void SAL_CALL XSDValidationPropertyHandler::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& _rOldValue, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) + { + if ( !_rxInspectorUI.is() ) + throw NullPointerException(); + + ::osl::MutexGuard aGuard( m_aMutex ); + PropertyId nActuatingPropId( impl_getPropertyId_throwRuntime( _rActuatingPropertyName ) ); + if (!m_pHelper) + throw RuntimeException(); + // if we survived impl_getPropertyId_throwRuntime, we should have a helper, since no helper implies no properties + + switch ( nActuatingPropId ) + { + case PROPERTY_ID_XSD_DATA_TYPE: + { + ::rtl::Reference< XSDDataType > xDataType( m_pHelper->getValidatingDataType() ); + + // is removal of this type possible? + bool bIsBasicType = xDataType.is() && xDataType->isBasicType(); + _rxInspectorUI->enablePropertyUIElements( PROPERTY_XSD_DATA_TYPE, PropertyLineElement::PrimaryButton, xDataType.is() ); + _rxInspectorUI->enablePropertyUIElements( PROPERTY_XSD_DATA_TYPE, PropertyLineElement::SecondaryButton, xDataType.is() && !bIsBasicType ); + + + // show the facets which are available at the data type + OUString aFacets[] = { + OUString(PROPERTY_XSD_WHITESPACES), OUString(PROPERTY_XSD_PATTERN), + OUString(PROPERTY_XSD_LENGTH), OUString(PROPERTY_XSD_MIN_LENGTH), OUString(PROPERTY_XSD_MAX_LENGTH), OUString(PROPERTY_XSD_TOTAL_DIGITS), + OUString(PROPERTY_XSD_FRACTION_DIGITS), + OUString(PROPERTY_XSD_MAX_INCLUSIVE_INT), + OUString(PROPERTY_XSD_MAX_EXCLUSIVE_INT), + OUString(PROPERTY_XSD_MIN_INCLUSIVE_INT), + OUString(PROPERTY_XSD_MIN_EXCLUSIVE_INT), + OUString(PROPERTY_XSD_MAX_INCLUSIVE_DOUBLE), + OUString(PROPERTY_XSD_MAX_EXCLUSIVE_DOUBLE), + OUString(PROPERTY_XSD_MIN_INCLUSIVE_DOUBLE), + OUString(PROPERTY_XSD_MIN_EXCLUSIVE_DOUBLE), + OUString(PROPERTY_XSD_MAX_INCLUSIVE_DATE), + OUString(PROPERTY_XSD_MAX_EXCLUSIVE_DATE), + OUString(PROPERTY_XSD_MIN_INCLUSIVE_DATE), + OUString(PROPERTY_XSD_MIN_EXCLUSIVE_DATE), + OUString(PROPERTY_XSD_MAX_INCLUSIVE_TIME), + OUString(PROPERTY_XSD_MAX_EXCLUSIVE_TIME), + OUString(PROPERTY_XSD_MIN_INCLUSIVE_TIME), + OUString(PROPERTY_XSD_MIN_EXCLUSIVE_TIME), + OUString(PROPERTY_XSD_MAX_INCLUSIVE_DATE_TIME), + OUString(PROPERTY_XSD_MAX_EXCLUSIVE_DATE_TIME), + OUString(PROPERTY_XSD_MIN_INCLUSIVE_DATE_TIME), + OUString(PROPERTY_XSD_MIN_EXCLUSIVE_DATE_TIME) + }; + + size_t i=0; + const OUString* pLoop = nullptr; + for ( i = 0, pLoop = aFacets; + i < SAL_N_ELEMENTS( aFacets ); + ++i, ++pLoop + ) + { + showPropertyUI( _rxInspectorUI, *pLoop, xDataType.is() && xDataType->hasFacet( *pLoop ) ); + _rxInspectorUI->enablePropertyUI( *pLoop, !bIsBasicType ); + } + } + break; + + case PROPERTY_ID_XML_DATA_MODEL: + { + // The data type which the current binding works with may not be present in the + // new model. Thus, transfer it. + OUString sOldModelName; _rOldValue >>= sOldModelName; + OUString sNewModelName; _rNewValue >>= sNewModelName; + OUString sDataType = m_pHelper->getValidatingDataTypeName(); + m_pHelper->copyDataType( sOldModelName, sNewModelName, sDataType ); + + // the list of available data types depends on the chosen model, so update this + if ( !_bFirstTimeInit ) + _rxInspectorUI->rebuildPropertyUI( PROPERTY_XSD_DATA_TYPE ); + } + break; + + default: + OSL_FAIL( "XSDValidationPropertyHandler::actuatingPropertyChanged: cannot handle this property!" ); + return; + } + + // in both cases, we need to care for the current value of the XSD_DATA_TYPE property, + // and update the FormatKey of the formatted field we're inspecting (if any) + if ( !_bFirstTimeInit && m_pHelper->isInspectingFormattedField() ) + m_pHelper->findDefaultFormatForIntrospectee(); + } + + + void XSDValidationPropertyHandler::implGetAvailableDataTypeNames( std::vector< OUString >& /* [out] */ _rNames ) const + { + OSL_PRECOND( + m_pHelper, + "XSDValidationPropertyHandler::implGetAvailableDataTypeNames: this will crash!"); + // start with *all* types which are available at the model + std::vector< OUString > aAllTypes; + m_pHelper->getAvailableDataTypeNames( aAllTypes ); + _rNames.clear(); + _rNames.reserve( aAllTypes.size() ); + + // then allow only those which are "compatible" with our control + for (auto const& dataType : aAllTypes) + { + ::rtl::Reference< XSDDataType > pType = m_pHelper->getDataTypeByName(dataType); + if ( pType.is() && m_pHelper->canBindToDataType( pType->classify() ) ) + _rNames.push_back(dataType); + } + } + + +} // namespace pcr + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +extensions_propctrlr_XSDValidationPropertyHandler_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new pcr::XSDValidationPropertyHandler(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/extensions/source/propctrlr/xsdvalidationpropertyhandler.hxx b/extensions/source/propctrlr/xsdvalidationpropertyhandler.hxx new file mode 100644 index 000000000..32803b215 --- /dev/null +++ b/extensions/source/propctrlr/xsdvalidationpropertyhandler.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 "propertyhandler.hxx" + +#include <memory> + + +namespace pcr +{ + + + class XSDValidationHelper; + + //= XSDValidationPropertyHandler + + class XSDValidationPropertyHandler : public PropertyHandlerComponent + { + private: + std::unique_ptr< XSDValidationHelper > m_pHelper; + + public: + explicit XSDValidationPropertyHandler( + const css::uno::Reference< css::uno::XComponentContext >& _rxContext + ); + + protected: + virtual ~XSDValidationPropertyHandler() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override; + + // XPropertyHandler overriables + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override; + virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override; + virtual css::uno::Sequence< OUString > + SAL_CALL getSupersededProperties( ) override; + virtual css::uno::Sequence< OUString > + SAL_CALL getActuatingProperties( ) override; + virtual css::inspection::LineDescriptor + SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override; + virtual css::inspection::InteractiveSelectionResult + SAL_CALL onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, css::uno::Any& _rData, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) override; + virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool ) override; + virtual void SAL_CALL addPropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override; + + // PropertyHandler overridables + virtual css::uno::Sequence< css::beans::Property > + doDescribeSupportedProperties() const override; + virtual void onNewComponent() override; + + private: + bool implPrepareRemoveCurrentDataType(); + bool implDoRemoveCurrentDataType(); + + bool implPrepareCloneDataCurrentType( OUString& _rNewName ); + void implDoCloneCurrentDataType( const OUString& _rNewName ); + + /** retrieves the names of the data types which our introspectee can be validated against + */ + void implGetAvailableDataTypeNames( std::vector< OUString >& /* [out] */ _rNames ) const; + }; + + +} // namespace pcr + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |