diff options
Diffstat (limited to 'svtools/source/uno')
24 files changed, 7751 insertions, 0 deletions
diff --git a/svtools/source/uno/addrtempuno.cxx b/svtools/source/uno/addrtempuno.cxx new file mode 100644 index 000000000..54cb28afc --- /dev/null +++ b/svtools/source/uno/addrtempuno.cxx @@ -0,0 +1,213 @@ +/* -*- 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 <svtools/addresstemplate.hxx> +#include <svtools/genericunodialog.hxx> +#include <comphelper/proparrhlp.hxx> +#include <comphelper/propertysequence.hxx> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/util/AliasProgrammaticPair.hpp> +#include <com/sun/star/sdbc/XDataSource.hpp> +#include <vcl/svapp.hxx> + +using namespace svt; + +namespace { + +#define UNODIALOG_PROPERTY_ID_ALIASES 100 +constexpr OUStringLiteral UNODIALOG_PROPERTY_ALIASES = u"FieldMapping"; + + using namespace css::uno; + using namespace css::lang; + using namespace css::util; + using namespace css::beans; + using namespace css::sdbc; + + class OAddressBookSourceDialogUno + :public OGenericUnoDialog + ,public ::comphelper::OPropertyArrayUsageHelper< OAddressBookSourceDialogUno > + { + private: + Sequence< AliasProgrammaticPair > m_aAliases; + Reference< XDataSource > m_xDataSource; + OUString m_sDataSourceName; + OUString m_sTable; + + public: + explicit OAddressBookSourceDialogUno(const Reference< XComponentContext >& _rxORB); + + // XTypeProvider + virtual 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 Reference< XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + virtual void SAL_CALL initialize(const Sequence< Any >& aArguments) override; + + protected: + // 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; + + virtual void executedDialog(sal_Int16 _nExecutionResult) override; + }; + + + OAddressBookSourceDialogUno::OAddressBookSourceDialogUno(const Reference< XComponentContext >& _rxORB) + :OGenericUnoDialog(_rxORB) + { + registerProperty(UNODIALOG_PROPERTY_ALIASES, UNODIALOG_PROPERTY_ID_ALIASES, PropertyAttribute::READONLY, + &m_aAliases, cppu::UnoType<decltype(m_aAliases)>::get()); + } + + + Sequence<sal_Int8> SAL_CALL OAddressBookSourceDialogUno::getImplementationId( ) + { + return css::uno::Sequence<sal_Int8>(); + } + + + OUString SAL_CALL OAddressBookSourceDialogUno::getImplementationName() + { + return "com.sun.star.comp.svtools.OAddressBookSourceDialogUno"; + } + + + css::uno::Sequence<OUString> SAL_CALL OAddressBookSourceDialogUno::getSupportedServiceNames() + { + return { "com.sun.star.ui.AddressBookSourceDialog" }; + } + + + Reference<XPropertySetInfo> SAL_CALL OAddressBookSourceDialogUno::getPropertySetInfo() + { + Reference<XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + ::cppu::IPropertyArrayHelper& OAddressBookSourceDialogUno::getInfoHelper() + { + return *getArrayHelper(); + } + + ::cppu::IPropertyArrayHelper* OAddressBookSourceDialogUno::createArrayHelper( ) const + { + Sequence< Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); + } + + void OAddressBookSourceDialogUno::executedDialog(sal_Int16 _nExecutionResult) + { + OGenericUnoDialog::executedDialog(_nExecutionResult); + + if ( _nExecutionResult && m_xDialog ) + static_cast<AddressBookSourceDialog*>(m_xDialog.get())->getFieldMapping(m_aAliases); + } + + void SAL_CALL OAddressBookSourceDialogUno::initialize(const Sequence< Any >& rArguments) + { + if( rArguments.getLength() == 5 ) + { + Reference<css::awt::XWindow> xParentWindow; + Reference<css::beans::XPropertySet> xDataSource; + OUString sDataSourceName; + OUString sCommand; + OUString sTitle; + if ( (rArguments[0] >>= xParentWindow) + && (rArguments[1] >>= xDataSource) + && (rArguments[2] >>= sDataSourceName) + && (rArguments[3] >>= sCommand) + && (rArguments[4] >>= sTitle) ) + { + + // convert the parameters for creating the dialog to PropertyValues + Sequence<Any> aArguments(comphelper::InitAnyPropertySequence( + { + {"ParentWindow", Any(xParentWindow)}, + {"DataSource", Any(xDataSource)}, + {"DataSourceName", Any(sDataSourceName)}, + {"Command", Any(sCommand)}, // the table to use + {"Title", Any(sTitle)} + })); + OGenericUnoDialog::initialize(aArguments); + return; + } + } + OGenericUnoDialog::initialize(rArguments); + } + + void OAddressBookSourceDialogUno::implInitialize(const css::uno::Any& _rValue) + { + PropertyValue aVal; + if (_rValue >>= aVal) + { + if (aVal.Name == "DataSource") + { + bool bSuccess = aVal.Value >>= m_xDataSource; + OSL_ENSURE( bSuccess, "OAddressBookSourceDialogUno::implInitialize: invalid type for DataSource!" ); + return; + } + + if (aVal.Name == "DataSourceName") + { + bool bSuccess = aVal.Value >>= m_sDataSourceName; + OSL_ENSURE( bSuccess, "OAddressBookSourceDialogUno::implInitialize: invalid type for DataSourceName!" ); + return; + } + + if (aVal.Name == "Command") + { + bool bSuccess = aVal.Value >>= m_sTable; + OSL_ENSURE( bSuccess, "OAddressBookSourceDialogUno::implInitialize: invalid type for Command!" ); + return; + } + } + + OGenericUnoDialog::implInitialize( _rValue ); + } + + std::unique_ptr<weld::DialogController> OAddressBookSourceDialogUno::createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) + { + weld::Window* pParent = Application::GetFrameWeld(rParent); + if ( m_xDataSource.is() && !m_sTable.isEmpty() ) + return std::make_unique<AddressBookSourceDialog>(pParent, m_aContext, m_xDataSource, m_sDataSourceName, m_sTable, m_aAliases); + return std::make_unique<AddressBookSourceDialog>(pParent, m_aContext); + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_svtools_OAddressBookSourceDialogUno_get_implementation( + css::uno::XComponentContext * context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new OAddressBookSourceDialogUno(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/fpicker.cxx b/svtools/source/uno/fpicker.cxx new file mode 100644 index 000000000..dbb69d601 --- /dev/null +++ b/svtools/source/uno/fpicker.cxx @@ -0,0 +1,171 @@ +/* -*- 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 <rtl/ustring.hxx> + +#include <com/sun/star/lang/XMultiComponentFactory.hpp> + +#include <svl/pickerhistoryaccess.hxx> +#include <officecfg/Office/Common.hxx> + +#include <vcl/svapp.hxx> + +#include "fpicker.hxx" + +using css::uno::Reference; +using css::uno::Sequence; + +/* + * FilePicker implementation. + */ +static OUString FilePicker_getSystemPickerServiceName() +{ +#ifdef UNX + OUString aDesktopEnvironment (Application::GetDesktopEnvironment()); + if (aDesktopEnvironment.equalsIgnoreAsciiCase("macosx")) + return "com.sun.star.ui.dialogs.AquaFilePicker"; + else + return "com.sun.star.ui.dialogs.SystemFilePicker"; +#endif +#ifdef _WIN32 + return "com.sun.star.ui.dialogs.Win32FilePicker"; +#endif +} + +Reference< css::uno::XInterface > FilePicker_CreateInstance ( + Reference< css::uno::XComponentContext > const & context) +{ + Reference< css::uno::XInterface > xResult; + + if (!context.is()) + return xResult; + + Reference< css::lang::XMultiComponentFactory > xFactory (context->getServiceManager()); + if (xFactory.is() && officecfg::Office::Common::Misc::UseSystemFileDialog::get()) + { + xResult.set( Application::createFilePicker( context ) ); + + if (!xResult.is()) + { + try + { + xResult = xFactory->createInstanceWithContext ( + FilePicker_getSystemPickerServiceName(), + context); + } + catch (css::uno::Exception const &) + { + // Handled below (see @ fallback). + } + } + } + + + if (!xResult.is() && xFactory.is()) + { + // Always fall back to OfficeFilePicker. + xResult = xFactory->createInstanceWithContext ( + "com.sun.star.ui.dialogs.OfficeFilePicker", + context); + } + if (xResult.is()) + { + // Add to FilePicker history. + svt::addFilePicker (xResult); + } + return xResult; +} + +OUString FilePicker_getImplementationName() +{ + return "com.sun.star.comp.svt.FilePicker"; +} + +Sequence< OUString > FilePicker_getSupportedServiceNames() +{ + Sequence< OUString > aServiceNames { "com.sun.star.ui.dialogs.FilePicker" }; + return aServiceNames; +} + +/* + * FolderPicker implementation. + */ +static OUString FolderPicker_getSystemPickerServiceName() +{ +#ifdef UNX + OUString aDesktopEnvironment (Application::GetDesktopEnvironment()); + if (aDesktopEnvironment.equalsIgnoreAsciiCase("macosx")) + return "com.sun.star.ui.dialogs.AquaFolderPicker"; +#endif + return "com.sun.star.ui.dialogs.SystemFolderPicker"; +} + +Reference< css::uno::XInterface > FolderPicker_CreateInstance ( + Reference< css::uno::XComponentContext > const & context) +{ + Reference< css::uno::XInterface > xResult; + + if (!context.is()) + return xResult; + + Reference< css::lang::XMultiComponentFactory > xFactory (context->getServiceManager()); + if (xFactory.is() && officecfg::Office::Common::Misc::UseSystemFileDialog::get()) + { + xResult.set( Application::createFolderPicker( context ) ); + if (!xResult.is()) + { + try + { + xResult = xFactory->createInstanceWithContext ( + FolderPicker_getSystemPickerServiceName(), + context); + } + catch (css::uno::Exception const &) + { + // Handled below (see @ fallback). + } + } + } + if (!xResult.is() && xFactory.is() ) + { + // Always fall back to OfficeFolderPicker. + xResult = xFactory->createInstanceWithContext ( + "com.sun.star.ui.dialogs.OfficeFolderPicker", + context); + } + if (xResult.is()) + { + // Add to FolderPicker history. + svt::addFolderPicker (xResult); + } + return xResult; +} + +OUString FolderPicker_getImplementationName() +{ + return "com.sun.star.comp.svt.FolderPicker"; +} + +Sequence< OUString > FolderPicker_getSupportedServiceNames() +{ + Sequence< OUString > aServiceNames { "com.sun.star.ui.dialogs.FolderPicker" }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/fpicker.hxx b/svtools/source/uno/fpicker.hxx new file mode 100644 index 000000000..9fcbacd54 --- /dev/null +++ b/svtools/source/uno/fpicker.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> +#include <rtl/ustring.hxx> + +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> + +namespace com::sun::star { + namespace lang { class XMultiServiceFactory; } + namespace uno { class XInterface; } +} + +css::uno::Reference<css::uno::XInterface> FilePicker_CreateInstance( + css::uno::Reference< css::uno::XComponentContext > const & context); +css::uno::Sequence<OUString> FilePicker_getSupportedServiceNames(); +OUString FilePicker_getImplementationName(); + +css::uno::Reference<css::uno::XInterface> FolderPicker_CreateInstance( + css::uno::Reference< css::uno::XComponentContext > const & context); +css::uno::Sequence<OUString> FolderPicker_getSupportedServiceNames(); +OUString FolderPicker_getImplementationName(); + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/framestatuslistener.cxx b/svtools/source/uno/framestatuslistener.cxx new file mode 100644 index 000000000..0ad4271a1 --- /dev/null +++ b/svtools/source/uno/framestatuslistener.cxx @@ -0,0 +1,269 @@ +/* -*- 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 <framestatuslistener.hxx> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <cppuhelper/queryinterface.hxx> +#include <vcl/svapp.hxx> + +using namespace ::cppu; +using namespace css::awt; +using namespace css::uno; +using namespace css::util; +using namespace css::beans; +using namespace css::lang; +using namespace css::frame; + +namespace svt +{ + +FrameStatusListener::FrameStatusListener( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& xFrame ) : + OWeakObject() + , m_bDisposed( false ) + , m_xFrame( xFrame ) + , m_xContext( rxContext ) +{ +} + +FrameStatusListener::~FrameStatusListener() +{ +} + +// XInterface +Any SAL_CALL FrameStatusListener::queryInterface( const Type& rType ) +{ + Any a = ::cppu::queryInterface( + rType , + static_cast< XComponent* >( this ), + static_cast< XFrameActionListener* >( this ), + static_cast< XStatusListener* >( this ), + static_cast< XEventListener* >( static_cast< XStatusListener* >( this )), + static_cast< XEventListener* >( static_cast< XFrameActionListener* >( this ))); + + if ( a.hasValue() ) + return a; + + return OWeakObject::queryInterface( rType ); +} + +void SAL_CALL FrameStatusListener::acquire() noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL FrameStatusListener::release() noexcept +{ + OWeakObject::release(); +} + +// XComponent +void SAL_CALL FrameStatusListener::dispose() +{ + Reference< XComponent > xThis = this; + + SolarMutexGuard aSolarMutexGuard; + if ( m_bDisposed ) + throw DisposedException(); + + for (auto const& listener : m_aListenerMap) + { + try + { + Reference< XDispatch > xDispatch( listener.second ); + Reference< XURLTransformer > xURLTransformer( css::util::URLTransformer::create( m_xContext ) ); + css::util::URL aTargetURL; + aTargetURL.Complete = listener.first; + xURLTransformer->parseStrict( aTargetURL ); + + if ( xDispatch.is() ) + xDispatch->removeStatusListener( this, aTargetURL ); + } + catch (const Exception&) + { + } + } + + m_bDisposed = true; +} + +void SAL_CALL FrameStatusListener::addEventListener( const Reference< XEventListener >& ) +{ + // helper class for status updates - no need to support listener +} + +void SAL_CALL FrameStatusListener::removeEventListener( const Reference< XEventListener >& ) +{ + // helper class for status updates - no need to support listener +} + +// XEventListener +void SAL_CALL FrameStatusListener::disposing( const EventObject& Source ) +{ + Reference< XInterface > xSource( Source.Source ); + + SolarMutexGuard aSolarMutexGuard; + + for (auto & listener : m_aListenerMap) + { + // Compare references and release dispatch references if they are equal. + Reference< XInterface > xIfac( listener.second, UNO_QUERY ); + if ( xSource == xIfac ) + listener.second.clear(); + } + + Reference< XInterface > xIfac( m_xFrame, UNO_QUERY ); + if ( xIfac == xSource ) + m_xFrame.clear(); +} + +void FrameStatusListener::frameAction( const FrameActionEvent& Action ) +{ + if ( Action.Action == FrameAction_CONTEXT_CHANGED ) + bindListener(); +} + +void FrameStatusListener::addStatusListener( const OUString& aCommandURL ) +{ + Reference< XDispatch > xDispatch; + Reference< XStatusListener > xStatusListener; + css::util::URL aTargetURL; + + { + SolarMutexGuard aSolarMutexGuard; + URLToDispatchMap::iterator pIter = m_aListenerMap.find( aCommandURL ); + + // Already in the list of status listener. Do nothing. + if ( pIter != m_aListenerMap.end() ) + return; + + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + if ( m_xContext.is() && xDispatchProvider.is() ) + { + Reference< XURLTransformer > xURLTransformer( css::util::URLTransformer::create( m_xContext ) ); + aTargetURL.Complete = aCommandURL; + xURLTransformer->parseStrict( aTargetURL ); + xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + + xStatusListener = this; + URLToDispatchMap::iterator aIter = m_aListenerMap.find( aCommandURL ); + if ( aIter != m_aListenerMap.end() ) + { + Reference< XDispatch > xOldDispatch( aIter->second ); + aIter->second = xDispatch; + + try + { + if ( xOldDispatch.is() ) + xOldDispatch->removeStatusListener( xStatusListener, aTargetURL ); + } + catch (const Exception&) + { + } + } + else + m_aListenerMap.emplace( aCommandURL, xDispatch ); + } + } + + // Call without locked mutex as we are called back from dispatch implementation + try + { + if ( xDispatch.is() ) + xDispatch->addStatusListener( xStatusListener, aTargetURL ); + } + catch (const Exception&) + { + } +} + + +void FrameStatusListener::bindListener() +{ + std::vector< Listener > aDispatchVector; + Reference< XStatusListener > xStatusListener; + + { + SolarMutexGuard aSolarMutexGuard; + + // Collect all registered command URL's and store them temporary + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + if ( m_xContext.is() && xDispatchProvider.is() ) + { + xStatusListener = this; + for (auto & listener : m_aListenerMap) + { + Reference< XURLTransformer > xURLTransformer( css::util::URLTransformer::create( m_xContext ) ); + css::util::URL aTargetURL; + aTargetURL.Complete = listener.first; + xURLTransformer->parseStrict( aTargetURL ); + + Reference< XDispatch > xDispatch( listener.second ); + if ( xDispatch.is() ) + { + // We already have a dispatch object => we have to requery. + // Release old dispatch object and remove it as listener + try + { + xDispatch->removeStatusListener( xStatusListener, aTargetURL ); + } + catch (const Exception&) + { + } + } + + // Query for dispatch object. Old dispatch will be released with this, too. + try + { + xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + } + catch (const Exception&) + { + } + listener.second = xDispatch; + + Listener aListener( aTargetURL, xDispatch ); + aDispatchVector.push_back( aListener ); + } + } + } + + // Call without locked mutex as we are called back from dispatch implementation + if ( !xStatusListener.is() ) + return; + + try + { + for (Listener & rListener : aDispatchVector) + { + if ( rListener.xDispatch.is() ) + rListener.xDispatch->addStatusListener( xStatusListener, rListener.aURL ); + } + } + catch (const Exception&) + { + } +} + +} // svt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/genericunodialog.cxx b/svtools/source/uno/genericunodialog.cxx new file mode 100644 index 000000000..b6a493654 --- /dev/null +++ b/svtools/source/uno/genericunodialog.cxx @@ -0,0 +1,263 @@ +/* -*- 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 <svtools/genericunodialog.hxx> + +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/ucb/AlreadyInitializedException.hpp> + +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <osl/diagnose.h> +#include <tools/diagnose_ex.h> +#include <osl/mutex.hxx> +#include <vcl/svapp.hxx> + +using namespace css::uno; +using namespace css::lang; +using namespace css::beans; +using namespace css::ucb; + + +namespace svt +{ + + +OGenericUnoDialog::OGenericUnoDialog(const Reference< XComponentContext >& _rxContext) + :OPropertyContainer(GetBroadcastHelper()) + ,m_bExecuting(false) + ,m_bTitleAmbiguous(true) + ,m_bInitialized( false ) + ,m_aContext(_rxContext) +{ + registerProperty(UNODIALOG_PROPERTY_TITLE, UNODIALOG_PROPERTY_ID_TITLE, PropertyAttribute::TRANSIENT, + &m_sTitle, cppu::UnoType<decltype(m_sTitle)>::get()); + registerProperty(UNODIALOG_PROPERTY_PARENT, UNODIALOG_PROPERTY_ID_PARENT, PropertyAttribute::TRANSIENT, + &m_xParent, cppu::UnoType<decltype(m_xParent)>::get()); +} + + +OGenericUnoDialog::~OGenericUnoDialog() +{ + if (m_xDialog) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + if (m_xDialog) + destroyDialog(); + } +} + + +Any SAL_CALL OGenericUnoDialog::queryInterface(const Type& _rType) +{ + Any aReturn = OGenericUnoDialogBase::queryInterface(_rType); + + if (!aReturn.hasValue()) + aReturn = ::cppu::queryInterface(_rType + ,static_cast<XPropertySet*>(this) + ,static_cast<XMultiPropertySet*>(this) + ,static_cast<XFastPropertySet*>(this) + ); + + return aReturn; +} + + +Sequence<Type> SAL_CALL OGenericUnoDialog::getTypes( ) +{ + return ::comphelper::concatSequences( + OGenericUnoDialogBase::getTypes(), + getBaseTypes() + ); +} + +sal_Bool SAL_CALL OGenericUnoDialog::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + + +void OGenericUnoDialog::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue ) +{ + // TODO: need some handling if we're currently executing ... + + OPropertyContainer::setFastPropertyValue_NoBroadcast(nHandle, rValue); + + if (UNODIALOG_PROPERTY_ID_TITLE == nHandle) + { + // from now on m_sTitle is valid + m_bTitleAmbiguous = false; + + if (m_xDialog) + m_xDialog->set_title(m_sTitle); + } +} + + +sal_Bool OGenericUnoDialog::convertFastPropertyValue( Any& rConvertedValue, Any& rOldValue, sal_Int32 nHandle, const Any& rValue) +{ + switch (nHandle) + { + case UNODIALOG_PROPERTY_ID_PARENT: + { + Reference<css::awt::XWindow> xNew(rValue, css::uno::UNO_QUERY); + if (xNew != m_xParent) + { + rConvertedValue <<= xNew; + rOldValue <<= m_xParent; + return true; + } + return false; + } + } + return OPropertyContainer::convertFastPropertyValue(rConvertedValue, rOldValue, nHandle, rValue); +} + + +void SAL_CALL OGenericUnoDialog::setTitle( const OUString& _rTitle ) +{ + UnoDialogEntryGuard aGuard( *this ); + + try + { + setPropertyValue(UNODIALOG_PROPERTY_TITLE, Any(_rTitle)); + } + catch(RuntimeException&) + { + // allowed to pass + throw; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + // not allowed to pass + } +} + + +bool OGenericUnoDialog::impl_ensureDialog_lck() +{ + if (m_xDialog) + return true; + + // get the parameters for the dialog from the current settings + + // the title + OUString sTitle = m_sTitle; + + auto xDialog(createDialog(m_xParent)); + OSL_ENSURE(xDialog, "OGenericUnoDialog::impl_ensureDialog_lck: createDialog returned nonsense!"); + if (!xDialog) + return false; + + // do some initialisations + if (!m_bTitleAmbiguous) + xDialog->set_title(sTitle); + + m_xDialog = std::move(xDialog); + + return true; +} + +sal_Int16 SAL_CALL OGenericUnoDialog::execute() +{ + // both creation and execution of the dialog must be guarded with the SolarMutex, so be generous here + SolarMutexGuard aSolarGuard; + + // create the dialog, if necessary + { + UnoDialogEntryGuard aGuard( *this ); + + if (m_bExecuting) + throw RuntimeException( + "already executing the dialog (recursive call)", + *this + ); + + m_bExecuting = true; + + if ( !impl_ensureDialog_lck() ) + return 0; + } + + // start execution + sal_Int16 nReturn(0); + if (m_xDialog) + nReturn = m_xDialog->run(); + + { + ::osl::MutexGuard aGuard(m_aMutex); + + // get the settings of the dialog + executedDialog( nReturn ); + + m_bExecuting = false; + } + + // outta here + return nReturn; +} + +void OGenericUnoDialog::implInitialize(const Any& _rValue) +{ + try + { + PropertyValue aProperty; + NamedValue aValue; + if ( _rValue >>= aProperty ) + { + setPropertyValue( aProperty.Name, aProperty.Value ); + } + else if ( _rValue >>= aValue ) + { + setPropertyValue( aValue.Name, aValue.Value ); + } + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } +} + +void SAL_CALL OGenericUnoDialog::initialize( const Sequence< Any >& aArguments ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bInitialized ) + throw AlreadyInitializedException( OUString(), *this ); + + for (const Any& rArgument : aArguments) + implInitialize(rArgument); + + m_bInitialized = true; +} + +void OGenericUnoDialog::destroyDialog() +{ + SolarMutexGuard aSolarGuard; + m_xDialog.reset(); +} + +} // namespace svt + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/miscservices.cxx b/svtools/source/uno/miscservices.cxx new file mode 100644 index 000000000..625b336c5 --- /dev/null +++ b/svtools/source/uno/miscservices.cxx @@ -0,0 +1,77 @@ +/* -*- 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/types.h> + +#include <cppuhelper/factory.hxx> +#include <cppuhelper/implementationentry.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include "fpicker.hxx" + +using namespace ::com::sun::star; +using namespace css::uno; +using namespace css::lang; + +namespace +{ + const struct ::cppu::ImplementationEntry s_aServiceEntries[] = + { + { + // FilePicker should not use a constructor, it is only a + // trampoline to a real impl. + FilePicker_CreateInstance, + FilePicker_getImplementationName, + FilePicker_getSupportedServiceNames, + ::cppu::createSingleComponentFactory, nullptr, 0 + }, + { + // FolderPicker should not use a constructor, it is only a + // trampoline to a real impl. + FolderPicker_CreateInstance, + FolderPicker_getImplementationName, + FolderPicker_getSupportedServiceNames, + ::cppu::createSingleComponentFactory, nullptr, 0 + }, + { nullptr, nullptr, nullptr, nullptr, nullptr, 0 } + }; +} + +extern "C" +{ + +SAL_DLLPUBLIC_EXPORT void * svt_component_getFactory( + const char * pImplementationName, void * _pServiceManager, void * pRegistryKey) +{ + void * pResult = nullptr; + if (_pServiceManager) + { + Reference< XMultiServiceFactory > xHoldAlive(static_cast< XMultiServiceFactory * >(_pServiceManager)); + + pResult = cppu::component_getFactoryHelper(pImplementationName, + _pServiceManager, + pRegistryKey, + s_aServiceEntries); + } + return pResult; +} + +} // extern "C" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/popupmenucontrollerbase.cxx b/svtools/source/uno/popupmenucontrollerbase.cxx new file mode 100644 index 000000000..3e5d47c36 --- /dev/null +++ b/svtools/source/uno/popupmenucontrollerbase.cxx @@ -0,0 +1,363 @@ +/* -*- 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 <svtools/popupmenucontrollerbase.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/util/URLTransformer.hpp> + +#include <vcl/svapp.hxx> +#include <osl/mutex.hxx> +#include <cppuhelper/supportsservice.hxx> + +using namespace com::sun::star; +using namespace css::uno; +using namespace css::lang; +using namespace css::frame; +using namespace css::beans; +using namespace css::util; + +namespace svt +{ + +namespace { + +struct PopupMenuControllerBaseDispatchInfo +{ + Reference< XDispatch > mxDispatch; + const URL maURL; + const Sequence< PropertyValue > maArgs; + + PopupMenuControllerBaseDispatchInfo( const Reference< XDispatch >& xDispatch, const URL& rURL, const Sequence< PropertyValue >& rArgs ) + : mxDispatch( xDispatch ), maURL( rURL ), maArgs( rArgs ) {} +}; + +} + +PopupMenuControllerBase::PopupMenuControllerBase( const Reference< XComponentContext >& xContext ) : + ::cppu::BaseMutex(), + PopupMenuControllerBaseType(m_aMutex), + m_bInitialized( false ) +{ + if ( xContext.is() ) + m_xURLTransformer.set( util::URLTransformer::create( xContext ) ); +} + +PopupMenuControllerBase::~PopupMenuControllerBase() +{ +} + +// protected function +void PopupMenuControllerBase::throwIfDisposed() +{ + if (rBHelper.bDisposed || rBHelper.bInDispose) + throw css::lang::DisposedException(); +} + +// protected function +void PopupMenuControllerBase::resetPopupMenu( css::uno::Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + if ( rPopupMenu.is() && rPopupMenu->getItemCount() > 0 ) + { + rPopupMenu->clear(); + } +} + +void SAL_CALL PopupMenuControllerBase::disposing() +{ + // Reset our members and set disposed flag + osl::MutexGuard aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + m_xPopupMenu.clear(); +} + +// XServiceInfo +sal_Bool SAL_CALL PopupMenuControllerBase::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +// XEventListener +void SAL_CALL PopupMenuControllerBase::disposing( const EventObject& ) +{ + osl::MutexGuard aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + m_xPopupMenu.clear(); +} + +// XMenuListener +void SAL_CALL PopupMenuControllerBase::itemHighlighted( const awt::MenuEvent& ) +{ +} + +void SAL_CALL PopupMenuControllerBase::itemSelected( const awt::MenuEvent& rEvent ) +{ + throwIfDisposed(); + + osl::MutexGuard aLock( m_aMutex ); + + if( m_xPopupMenu.is() ) + { + Sequence<PropertyValue> aArgs; + dispatchCommand( m_xPopupMenu->getCommand( rEvent.MenuId ), aArgs ); + } +} + +void PopupMenuControllerBase::dispatchCommand( const OUString& sCommandURL, + const css::uno::Sequence< css::beans::PropertyValue >& rArgs, + const OUString& sTarget ) +{ + osl::MutexGuard aLock( m_aMutex ); + + throwIfDisposed(); + + try + { + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY_THROW ); + URL aURL; + aURL.Complete = sCommandURL; + m_xURLTransformer->parseStrict( aURL ); + + Reference< XDispatch > xDispatch( xDispatchProvider->queryDispatch( aURL, sTarget, 0 ), UNO_SET_THROW ); + + Application::PostUserEvent( LINK(nullptr, PopupMenuControllerBase, ExecuteHdl_Impl), new PopupMenuControllerBaseDispatchInfo( xDispatch, aURL, rArgs ) ); + + } + catch( Exception& ) + { + } + +} + +IMPL_STATIC_LINK( PopupMenuControllerBase, ExecuteHdl_Impl, void*, p, void ) +{ + PopupMenuControllerBaseDispatchInfo* pDispatchInfo = static_cast<PopupMenuControllerBaseDispatchInfo*>(p); + pDispatchInfo->mxDispatch->dispatch( pDispatchInfo->maURL, pDispatchInfo->maArgs ); + delete pDispatchInfo; +} + +void SAL_CALL PopupMenuControllerBase::itemActivated( const awt::MenuEvent& ) +{ +} + +void SAL_CALL PopupMenuControllerBase::itemDeactivated( const awt::MenuEvent& ) +{ +} + +void SAL_CALL PopupMenuControllerBase::updatePopupMenu() +{ + { + osl::MutexGuard aLock(m_aMutex); + throwIfDisposed(); + } + + updateCommand( m_aCommandURL ); +} + +void PopupMenuControllerBase::updateCommand( const OUString& rCommandURL ) +{ + osl::ClearableMutexGuard aLock( m_aMutex ); + Reference< XStatusListener > xStatusListener(this); + Reference< XDispatch > xDispatch( m_xDispatch ); + URL aTargetURL; + aTargetURL.Complete = rCommandURL; + m_xURLTransformer->parseStrict( aTargetURL ); + aLock.clear(); + + // Add/remove status listener to get a status update once + if ( xDispatch.is() ) + { + xDispatch->addStatusListener( xStatusListener, aTargetURL ); + xDispatch->removeStatusListener( xStatusListener, aTargetURL ); + } +} + + +// XDispatchProvider +Reference< XDispatch > SAL_CALL +PopupMenuControllerBase::queryDispatch( + const URL& /*aURL*/, + const OUString& /*sTarget*/, + sal_Int32 /*nFlags*/ ) +{ + // must be implemented by subclass + osl::MutexGuard aLock( m_aMutex ); + throwIfDisposed(); + + return Reference< XDispatch >(); +} + +Sequence< Reference< XDispatch > > SAL_CALL PopupMenuControllerBase::queryDispatches( const Sequence< DispatchDescriptor >& lDescriptor ) +{ + // Create return list - which must have same size then the given descriptor + // It's not allowed to pack it! + { + osl::MutexGuard aLock(m_aMutex); + throwIfDisposed(); + } + + sal_Int32 nCount = lDescriptor.getLength(); + uno::Sequence< uno::Reference< frame::XDispatch > > lDispatcher( nCount ); + + // Step over all descriptors and try to get any dispatcher for it. + std::transform(lDescriptor.begin(), lDescriptor.end(), lDispatcher.getArray(), + [this](const DispatchDescriptor& rDesc) -> uno::Reference< frame::XDispatch > { + return queryDispatch(rDesc.FeatureURL, rDesc.FrameName, rDesc.SearchFlags); }); + + return lDispatcher; +} + +// XDispatch +void SAL_CALL +PopupMenuControllerBase::dispatch( + const URL& /*aURL*/, + const Sequence< PropertyValue >& /*seqProperties*/ ) +{ + // must be implemented by subclass + osl::MutexGuard aLock( m_aMutex ); + throwIfDisposed(); +} + +void SAL_CALL +PopupMenuControllerBase::addStatusListener( + const Reference< XStatusListener >& xControl, + const URL& aURL ) +{ + osl::ResettableMutexGuard aLock( m_aMutex ); + throwIfDisposed(); + aLock.clear(); + + bool bStatusUpdate( false ); + rBHelper.addListener( cppu::UnoType<decltype(xControl)>::get(), xControl ); + + aLock.reset(); + if ( aURL.Complete.startsWith( m_aBaseURL ) ) + bStatusUpdate = true; + aLock.clear(); + + if ( bStatusUpdate ) + { + // Dummy update for popup menu controllers + FeatureStateEvent aEvent; + aEvent.FeatureURL = aURL; + aEvent.IsEnabled = true; + aEvent.Requery = false; + aEvent.State = Any(); + xControl->statusChanged( aEvent ); + } +} + +void SAL_CALL PopupMenuControllerBase::removeStatusListener( + const Reference< XStatusListener >& xControl, + const URL& /*aURL*/ ) +{ + rBHelper.removeListener( cppu::UnoType<decltype(xControl)>::get(), xControl ); +} + +OUString PopupMenuControllerBase::determineBaseURL( const OUString& aURL ) +{ + // Just use the main part of the URL for popup menu controllers + sal_Int32 nSchemePart( 0 ); + OUString aMainURL( "vnd.sun.star.popup:" ); + + nSchemePart = aURL.indexOf( ':' ); + if (( nSchemePart > 0 ) && + ( aURL.getLength() > ( nSchemePart+1 ))) + { + sal_Int32 nQueryPart = aURL.indexOf( '?', nSchemePart ); + if ( nQueryPart > 0 ) + aMainURL += aURL.subView( nSchemePart, nQueryPart-nSchemePart ); + else if ( nQueryPart == -1 ) + aMainURL += aURL.subView( nSchemePart+1 ); + } + + return aMainURL; +} + +// XInitialization +void SAL_CALL PopupMenuControllerBase::initialize( const Sequence< Any >& aArguments ) +{ + osl::MutexGuard aLock( m_aMutex ); + + bool bInitialized( m_bInitialized ); + if ( bInitialized ) + return; + + PropertyValue aPropValue; + OUString aCommandURL; + Reference< XFrame > xFrame; + + for ( const auto& rArgument : aArguments ) + { + if ( rArgument >>= aPropValue ) + { + if ( aPropValue.Name == "Frame" ) + aPropValue.Value >>= xFrame; + else if ( aPropValue.Name == "CommandURL" ) + aPropValue.Value >>= aCommandURL; + else if ( aPropValue.Name == "ModuleIdentifier" ) + aPropValue.Value >>= m_aModuleName; + } + } + + if ( xFrame.is() && !aCommandURL.isEmpty() ) + { + m_xFrame = xFrame; + m_aCommandURL = aCommandURL; + m_aBaseURL = determineBaseURL( aCommandURL ); + m_bInitialized = true; + } +} +// XPopupMenuController +void SAL_CALL PopupMenuControllerBase::setPopupMenu( const Reference< awt::XPopupMenu >& xPopupMenu ) +{ + osl::MutexGuard aLock( m_aMutex ); + throwIfDisposed(); + + if ( !m_xFrame.is() || m_xPopupMenu.is() ) + return; + + // Create popup menu on demand + SolarMutexGuard aSolarMutexGuard; + + m_xPopupMenu = xPopupMenu; + m_xPopupMenu->addMenuListener( Reference< awt::XMenuListener >(this) ); + + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + + URL aTargetURL; + aTargetURL.Complete = m_aCommandURL; + m_xURLTransformer->parseStrict( aTargetURL ); + m_xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + + impl_setPopupMenu(); + + updatePopupMenu(); +} +void PopupMenuControllerBase::impl_setPopupMenu() +{ +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/popupwindowcontroller.cxx b/svtools/source/uno/popupwindowcontroller.cxx new file mode 100644 index 000000000..83419b4d9 --- /dev/null +++ b/svtools/source/uno/popupwindowcontroller.cxx @@ -0,0 +1,270 @@ +/* -*- 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 <cppuhelper/supportsservice.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> + +#include <svtools/popupwindowcontroller.hxx> +#include <svtools/toolbarmenu.hxx> + +using namespace ::com::sun::star; +using namespace css::uno; +using namespace css::lang; + + +namespace svt +{ + +class PopupWindowControllerImpl +{ +public: + PopupWindowControllerImpl(); + ~PopupWindowControllerImpl() COVERITY_NOEXCEPT_FALSE; + + void SetPopupWindow( vcl::Window* pPopupWindow, ToolBox* pToolBox ); + void SetFloatingWindow(); + DECL_LINK( WindowEventListener, VclWindowEvent&, void ); + +private: + VclPtr<vcl::Window> mpPopupWindow, mpFloatingWindow; + VclPtr<ToolBox> mpToolBox; +}; + +PopupWindowControllerImpl::PopupWindowControllerImpl() +{ +} + +PopupWindowControllerImpl::~PopupWindowControllerImpl() COVERITY_NOEXCEPT_FALSE +{ + SetPopupWindow(nullptr,nullptr); + SetFloatingWindow(); +} + +void PopupWindowControllerImpl::SetPopupWindow( vcl::Window* pPopupWindow, ToolBox* pToolBox ) +{ + if( mpPopupWindow ) + { + mpPopupWindow->RemoveEventListener( LINK( this, PopupWindowControllerImpl, WindowEventListener ) ); + mpPopupWindow.disposeAndClear(); + } + mpPopupWindow = pPopupWindow; + mpToolBox = pToolBox; + + if( mpPopupWindow ) + { + mpPopupWindow->AddEventListener( LINK( this, PopupWindowControllerImpl, WindowEventListener )); + } +} + +void PopupWindowControllerImpl::SetFloatingWindow() +{ + if( mpFloatingWindow ) + { + mpFloatingWindow->RemoveEventListener( LINK( this, PopupWindowControllerImpl, WindowEventListener ) ); + mpFloatingWindow.disposeAndClear(); + } + mpFloatingWindow = mpPopupWindow; + mpPopupWindow.clear(); +} + +IMPL_LINK( PopupWindowControllerImpl, WindowEventListener, VclWindowEvent&, rWindowEvent, void ) +{ + switch( rWindowEvent.GetId() ) + { + case VclEventId::WindowEndPopupMode: + { + EndPopupModeData* pData = static_cast< EndPopupModeData* >( rWindowEvent.GetData() ); + if( pData && pData->mbTearoff ) + { + vcl::Window::GetDockingManager()->SetFloatingMode( mpPopupWindow.get(), true ); + vcl::Window::GetDockingManager()->SetPosSizePixel( mpPopupWindow.get(), + pData->maFloatingPos.X(), + pData->maFloatingPos.Y(), + 0, 0, + PosSizeFlags::Pos ); + SetFloatingWindow(); + mpFloatingWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + } + SetPopupWindow(nullptr,nullptr); + break; + } + case VclEventId::WindowPrepareToggleFloating: + { + if ( mpFloatingWindow && rWindowEvent.GetWindow() == mpFloatingWindow.get() ) + { + bool* pData = static_cast< bool* >( rWindowEvent.GetData() ); + *pData = false; + } + break; + } + case VclEventId::WindowClose: + { + SetPopupWindow(nullptr,nullptr); + SetFloatingWindow(); + break; + } + case VclEventId::WindowShow: + { + if( mpPopupWindow ) + { + if( mpToolBox ) + mpToolBox->CallEventListeners( VclEventId::DropdownOpen, static_cast<void*>(mpPopupWindow) ); + mpPopupWindow->CallEventListeners( VclEventId::WindowGetFocus ); + break; + } + break; + } + case VclEventId::WindowHide: + { + if( mpPopupWindow ) + { + mpPopupWindow->CallEventListeners( VclEventId::WindowLoseFocus ); + if( mpToolBox ) + mpToolBox->CallEventListeners( VclEventId::DropdownClose, static_cast<void*>(mpPopupWindow) ); + } + break; + } + default: break; + } +} + + + + +PopupWindowController::PopupWindowController( const Reference< uno::XComponentContext >& rxContext, + const Reference< frame::XFrame >& xFrame, + const OUString& aCommandURL ) +: PopupWindowController_Base( rxContext, xFrame, aCommandURL ) +, mxImpl( new PopupWindowControllerImpl() ) +{ +} + +PopupWindowController::~PopupWindowController() +{ +} + +// XServiceInfo +sal_Bool SAL_CALL PopupWindowController::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +// XComponent +void SAL_CALL PopupWindowController::dispose() +{ + mxInterimPopover.clear(); + mxPopoverContainer.reset(); + mxImpl.reset(); + svt::ToolboxController::dispose(); +} + +// XStatusListener +void SAL_CALL PopupWindowController::statusChanged( const frame::FeatureStateEvent& rEvent ) +{ + SolarMutexGuard aSolarLock; + + bool bValue = false; + rEvent.State >>= bValue; + + if (m_pToolbar) + { + OString sId = m_aCommandURL.toUtf8(); + m_pToolbar->set_item_active(sId, bValue); + m_pToolbar->set_item_sensitive(sId, rEvent.IsEnabled); + return; + } + + ToolBox* pToolBox = nullptr; + ToolBoxItemId nItemId; + if ( getToolboxId( nItemId, &pToolBox ) ) + { + pToolBox->CheckItem( nItemId, bValue ); + pToolBox->EnableItem( nItemId, rEvent.IsEnabled ); + } +} + +VclPtr<vcl::Window> PopupWindowController::createVclPopupWindow(vcl::Window* /*pParent*/) +{ + return nullptr; +} + +Reference< awt::XWindow > SAL_CALL PopupWindowController::createPopupWindow() +{ + if (m_pToolbar) + { + mxPopoverContainer->unsetPopover(); + mxPopoverContainer->setPopover(weldPopupWindow()); + return Reference<awt::XWindow>(); + } + + VclPtr< ToolBox > pToolBox = dynamic_cast< ToolBox* >( VCLUnoHelper::GetWindow( getParent() ) ); + if( pToolBox ) + { + vcl::Window* pItemWindow = pToolBox->GetItemWindow( pToolBox->GetDownItemId() ); + VclPtr<vcl::Window> pWin = createVclPopupWindow( pItemWindow ? pItemWindow : pToolBox ); + if( pWin ) + { + FloatWinPopupFlags eFloatFlags = FloatWinPopupFlags::GrabFocus | + FloatWinPopupFlags::AllMouseButtonClose | + FloatWinPopupFlags::NoMouseUpClose; + + WinBits nWinBits; + if ( pWin->GetType() == WindowType::DOCKINGWINDOW ) + nWinBits = static_cast< DockingWindow* >( pWin.get() )->GetFloatStyle(); + else + nWinBits = pWin->GetStyle(); + + if ( nWinBits & ( WB_SIZEABLE | WB_CLOSEABLE ) ) + eFloatFlags |= FloatWinPopupFlags::AllowTearOff; + + pWin->EnableDocking(); + mxImpl->SetPopupWindow(pWin,pToolBox); + vcl::Window::GetDockingManager()->StartPopupMode( pToolBox, pWin, eFloatFlags ); + } + } + return Reference< awt::XWindow >(); +} + +void SAL_CALL PopupWindowController::click() +{ + if (m_pToolbar) + { + if (m_pToolbar->get_menu_item_active(m_aCommandURL.toUtf8())) + createPopupWindow(); + else + mxPopoverContainer->unsetPopover(); + } + + svt::ToolboxController::click(); +} + +void PopupWindowController::EndPopupMode() +{ + if (m_pToolbar) + m_pToolbar->set_menu_item_active(m_aCommandURL.toUtf8(), false); + else if (mxInterimPopover) + mxInterimPopover->EndPopupMode(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/statusbarcontroller.cxx b/svtools/source/uno/statusbarcontroller.cxx new file mode 100644 index 000000000..fae89f287 --- /dev/null +++ b/svtools/source/uno/statusbarcontroller.cxx @@ -0,0 +1,594 @@ +/* -*- 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 <svtools/statusbarcontroller.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/ui/XStatusbarItem.hpp> +#include <cppuhelper/queryinterface.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <vcl/status.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <comphelper/processfactory.hxx> + +using namespace ::cppu; +using namespace css::awt; +using namespace css::uno; +using namespace css::util; +using namespace css::beans; +using namespace css::lang; +using namespace css::frame; + +namespace svt +{ + +StatusbarController::StatusbarController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& xFrame, + const OUString& aCommandURL, + unsigned short nID ) : + OWeakObject() + , m_bInitialized( false ) + , m_bDisposed( false ) + , m_nID( nID ) + , m_xFrame( xFrame ) + , m_xContext( rxContext ) + , m_aCommandURL( aCommandURL ) + , m_aListenerContainer( m_aMutex ) +{ +} + +StatusbarController::StatusbarController() : + OWeakObject() + , m_bInitialized( false ) + , m_bDisposed( false ) + , m_nID( 0 ) + , m_aListenerContainer( m_aMutex ) +{ +} + +StatusbarController::~StatusbarController() +{ +} + +Reference< XFrame > StatusbarController::getFrameInterface() const +{ + SolarMutexGuard aSolarMutexGuard; + return m_xFrame; +} + +Reference< XURLTransformer > StatusbarController::getURLTransformer() const +{ + SolarMutexGuard aSolarMutexGuard; + if ( !m_xURLTransformer.is() && m_xContext.is() ) + { + m_xURLTransformer = css::util::URLTransformer::create( m_xContext ); + } + + return m_xURLTransformer; +} + +// XInterface +Any SAL_CALL StatusbarController::queryInterface( const Type& rType ) +{ + Any a = ::cppu::queryInterface( + rType , + static_cast< XStatusbarController* >( this ), + static_cast< XStatusListener* >( this ), + static_cast< XEventListener* >( this ), + static_cast< XInitialization* >( this ), + static_cast< XComponent* >( this ), + static_cast< XUpdatable* >( this )); + + if ( a.hasValue() ) + return a; + + return OWeakObject::queryInterface( rType ); +} + +void SAL_CALL StatusbarController::acquire() noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL StatusbarController::release() noexcept +{ + OWeakObject::release(); +} + +void SAL_CALL StatusbarController::initialize( const Sequence< Any >& aArguments ) +{ + bool bInitialized( true ); + + { + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + bInitialized = m_bInitialized; + } + + if ( bInitialized ) + return; + + SolarMutexGuard aSolarMutexGuard; + m_bInitialized = true; + + PropertyValue aPropValue; + for ( const auto& rArgument : aArguments ) + { + if ( rArgument >>= aPropValue ) + { + if ( aPropValue.Name == "Frame" ) + aPropValue.Value >>= m_xFrame; + else if ( aPropValue.Name == "CommandURL" ) + aPropValue.Value >>= m_aCommandURL; + else if ( aPropValue.Name == "ServiceManager" ) + { + Reference<XMultiServiceFactory> xMSF; + aPropValue.Value >>= xMSF; + if( xMSF.is() ) + m_xContext = comphelper::getComponentContext(xMSF); + } + else if ( aPropValue.Name == "ParentWindow" ) + aPropValue.Value >>= m_xParentWindow; + else if ( aPropValue.Name == "Identifier" ) + aPropValue.Value >>= m_nID; + else if ( aPropValue.Name == "StatusbarItem" ) + aPropValue.Value >>= m_xStatusbarItem; + } + } + + if ( !m_aCommandURL.isEmpty() ) + m_aListenerMap.emplace( m_aCommandURL, Reference< XDispatch >() ); +} + +void SAL_CALL StatusbarController::update() +{ + { + SolarMutexGuard aSolarMutexGuard; + if ( m_bDisposed ) + throw DisposedException(); + } + + // Bind all registered listeners to their dispatch objects + bindListener(); +} + +// XComponent +void SAL_CALL StatusbarController::dispose() +{ + Reference< XComponent > xThis = this; + + { + SolarMutexGuard aSolarMutexGuard; + if ( m_bDisposed ) + throw DisposedException(); + } + + css::lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + SolarMutexGuard aSolarMutexGuard; + Reference< XStatusListener > xStatusListener = this; + Reference< XURLTransformer > xURLTransformer = getURLTransformer(); + css::util::URL aTargetURL; + for (auto const& listener : m_aListenerMap) + { + try + { + Reference< XDispatch > xDispatch(listener.second); + aTargetURL.Complete = listener.first; + xURLTransformer->parseStrict( aTargetURL ); + + if ( xDispatch.is() && xStatusListener.is() ) + xDispatch->removeStatusListener( xStatusListener, aTargetURL ); + } + catch ( Exception& ) + { + } + } + + // clear hash map + m_aListenerMap.clear(); + + // release references + m_xURLTransformer.clear(); + m_xContext.clear(); + m_xFrame.clear(); + m_xParentWindow.clear(); + m_xStatusbarItem.clear(); + + m_bDisposed = true; +} + +void SAL_CALL StatusbarController::addEventListener( const Reference< XEventListener >& xListener ) +{ + m_aListenerContainer.addInterface( cppu::UnoType<XEventListener>::get(), xListener ); +} + +void SAL_CALL StatusbarController::removeEventListener( const Reference< XEventListener >& aListener ) +{ + m_aListenerContainer.removeInterface( cppu::UnoType<XEventListener>::get(), aListener ); +} + +// XEventListener +void SAL_CALL StatusbarController::disposing( const EventObject& Source ) +{ + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + return; + + Reference< XFrame > xFrame( Source.Source, UNO_QUERY ); + if ( xFrame.is() ) + { + if ( xFrame == m_xFrame ) + m_xFrame.clear(); + return; + } + + Reference< XDispatch > xDispatch( Source.Source, UNO_QUERY ); + if ( !xDispatch.is() ) + return; + + for (auto & listener : m_aListenerMap) + { + // Compare references and release dispatch references if they are equal. + if ( xDispatch == listener.second ) + listener.second.clear(); + } +} + +// XStatusListener +void SAL_CALL StatusbarController::statusChanged( const FeatureStateEvent& Event ) +{ + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + return; + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( m_xParentWindow ); + if ( pWindow && pWindow->GetType() == WindowType::STATUSBAR && m_nID != 0 ) + { + OUString aStrValue; + StatusBar* pStatusBar = static_cast<StatusBar *>(pWindow.get()); + + if ( Event.State >>= aStrValue ) + pStatusBar->SetItemText( m_nID, aStrValue ); + else if ( !Event.State.hasValue() ) + pStatusBar->SetItemText( m_nID, "" ); + } +} + +// XStatusbarController +sal_Bool SAL_CALL StatusbarController::mouseButtonDown( + const css::awt::MouseEvent& ) +{ + return false; +} + +sal_Bool SAL_CALL StatusbarController::mouseMove( + const css::awt::MouseEvent& ) +{ + return false; +} + +sal_Bool SAL_CALL StatusbarController::mouseButtonUp( + const css::awt::MouseEvent& ) +{ + return false; +} + +void SAL_CALL StatusbarController::command( + const css::awt::Point&, + ::sal_Int32, + sal_Bool, + const css::uno::Any& ) +{ +} + +void SAL_CALL StatusbarController::paint( + const css::uno::Reference< css::awt::XGraphics >&, + const css::awt::Rectangle&, + ::sal_Int32 ) +{ +} + +void SAL_CALL StatusbarController::click( const css::awt::Point& ) +{ +} + +void SAL_CALL StatusbarController::doubleClick( const css::awt::Point& ) +{ + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + return; + + Sequence< PropertyValue > aArgs; + execute( aArgs ); +} + +void StatusbarController::addStatusListener( const OUString& aCommandURL ) +{ + Reference< XDispatch > xDispatch; + Reference< XStatusListener > xStatusListener; + css::util::URL aTargetURL; + + { + SolarMutexGuard aSolarMutexGuard; + URLToDispatchMap::iterator pIter = m_aListenerMap.find( aCommandURL ); + + // Already in the list of status listener. Do nothing. + if ( pIter != m_aListenerMap.end() ) + return; + + // Check if we are already initialized. Implementation starts adding itself as status listener when + // initialize is called. + if ( !m_bInitialized ) + { + // Put into the unordered_map of status listener. Will be activated when initialized is called + m_aListenerMap.emplace( aCommandURL, Reference< XDispatch >() ); + return; + } + else + { + // Add status listener directly as initialize has already been called. + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + if ( m_xContext.is() && xDispatchProvider.is() ) + { + Reference< XURLTransformer > xURLTransformer = getURLTransformer(); + aTargetURL.Complete = aCommandURL; + xURLTransformer->parseStrict( aTargetURL ); + xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + + xStatusListener = this; + URLToDispatchMap::iterator aIter = m_aListenerMap.find( aCommandURL ); + if ( aIter != m_aListenerMap.end() ) + { + Reference< XDispatch > xOldDispatch( aIter->second ); + aIter->second = xDispatch; + + try + { + if ( xOldDispatch.is() ) + xOldDispatch->removeStatusListener( xStatusListener, aTargetURL ); + } + catch ( Exception& ) + { + } + } + else + m_aListenerMap.emplace( aCommandURL, xDispatch ); + } + } + } + + // Call without locked mutex as we are called back from dispatch implementation + try + { + if ( xDispatch.is() ) + xDispatch->addStatusListener( xStatusListener, aTargetURL ); + } + catch ( Exception& ) + { + } +} + +void StatusbarController::bindListener() +{ + std::vector< Listener > aDispatchVector; + Reference< XStatusListener > xStatusListener; + + { + SolarMutexGuard aSolarMutexGuard; + + if ( !m_bInitialized ) + return; + + // Collect all registered command URL's and store them temporary + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + if ( m_xContext.is() && xDispatchProvider.is() ) + { + xStatusListener = this; + for (auto & listener : m_aListenerMap) + { + Reference< XURLTransformer > xURLTransformer = getURLTransformer(); + css::util::URL aTargetURL; + aTargetURL.Complete = listener.first; + xURLTransformer->parseStrict( aTargetURL ); + + Reference< XDispatch > xDispatch(listener.second); + if ( xDispatch.is() ) + { + // We already have a dispatch object => we have to requery. + // Release old dispatch object and remove it as listener + try + { + xDispatch->removeStatusListener( xStatusListener, aTargetURL ); + } + catch ( Exception& ) + { + } + } + + listener.second.clear(); + xDispatch.clear(); + + // Query for dispatch object. Old dispatch will be released with this, too. + try + { + xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + } + catch ( Exception& ) + { + } + listener.second = xDispatch; + + Listener aListener( aTargetURL, xDispatch ); + aDispatchVector.push_back( aListener ); + } + } + } + + // Call without locked mutex as we are called back from dispatch implementation + if ( !xStatusListener.is() ) + return; + + for (Listener & rListener : aDispatchVector) + { + try + { + if ( rListener.xDispatch.is() ) + rListener.xDispatch->addStatusListener( xStatusListener, rListener.aURL ); + else if ( rListener.aURL.Complete == m_aCommandURL ) + { + // Send status changed for the main URL, if we cannot get a valid dispatch object. + // UI disables the button. Catch exception as we release our mutex, it is possible + // that someone else already disposed this instance! + FeatureStateEvent aFeatureStateEvent; + aFeatureStateEvent.IsEnabled = false; + aFeatureStateEvent.FeatureURL = rListener.aURL; + aFeatureStateEvent.State = Any(); + xStatusListener->statusChanged( aFeatureStateEvent ); + } + } + catch ( ... ){} + } +} + +::tools::Rectangle StatusbarController::getControlRect() const +{ + ::tools::Rectangle aRect; + + { + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_xParentWindow.is() ) + { + VclPtr< StatusBar > pStatusBar = dynamic_cast< StatusBar* >( VCLUnoHelper::GetWindow( m_xParentWindow ) ); + if ( pStatusBar && pStatusBar->GetType() == WindowType::STATUSBAR ) + aRect = pStatusBar->GetItemRect( m_nID ); + } + } + + return aRect; +} + +void StatusbarController::execute( const css::uno::Sequence< css::beans::PropertyValue >& aArgs ) +{ + Reference< XDispatch > xDispatch; + Reference< XURLTransformer > xURLTransformer; + OUString aCommandURL; + + { + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_bInitialized && + m_xFrame.is() && + m_xContext.is() && + !m_aCommandURL.isEmpty() ) + { + xURLTransformer = getURLTransformer(); + aCommandURL = m_aCommandURL; + URLToDispatchMap::iterator pIter = m_aListenerMap.find( m_aCommandURL ); + if ( pIter != m_aListenerMap.end() ) + xDispatch = pIter->second; + } + } + + if ( !(xDispatch.is() && xURLTransformer.is()) ) + return; + + try + { + css::util::URL aTargetURL; + + aTargetURL.Complete = aCommandURL; + xURLTransformer->parseStrict( aTargetURL ); + xDispatch->dispatch( aTargetURL, aArgs ); + } + catch ( DisposedException& ) + { + } +} + +void StatusbarController::execute( + const OUString& aCommandURL, + const Sequence< css::beans::PropertyValue >& aArgs ) +{ + Reference< XDispatch > xDispatch; + css::util::URL aTargetURL; + + { + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_bInitialized && + m_xFrame.is() && + m_xContext.is() && + !m_aCommandURL.isEmpty() ) + { + Reference< XURLTransformer > xURLTransformer( getURLTransformer() ); + aTargetURL.Complete = aCommandURL; + xURLTransformer->parseStrict( aTargetURL ); + + URLToDispatchMap::iterator pIter = m_aListenerMap.find( aCommandURL ); + if ( pIter != m_aListenerMap.end() ) + xDispatch = pIter->second; + else + { + Reference< css::frame::XDispatchProvider > xDispatchProvider( + m_xFrame->getController(), UNO_QUERY ); + if ( xDispatchProvider.is() ) + xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + } + } + } + + if ( xDispatch.is() ) + { + try + { + xDispatch->dispatch( aTargetURL, aArgs ); + } + catch ( DisposedException& ) + { + } + } +} + +} // svt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/svtxgridcontrol.cxx b/svtools/source/uno/svtxgridcontrol.cxx new file mode 100644 index 000000000..4aee46f2c --- /dev/null +++ b/svtools/source/uno/svtxgridcontrol.cxx @@ -0,0 +1,909 @@ +/* -*- 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 "svtxgridcontrol.hxx" +#include <com/sun/star/view/SelectionType.hpp> +#include <table/tablecontrolinterface.hxx> +#include <table/gridtablerenderer.hxx> +#include <table/tablecontrol.hxx> +#include "unocontroltablemodel.hxx" +#include <sal/log.hxx> +#include <tools/diagnose_ex.h> +#include <toolkit/helper/property.hxx> +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/awt/grid/GridInvalidDataException.hpp> +#include <com/sun/star/awt/grid/GridInvalidModelException.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/util/Color.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + +#include <vcl/svapp.hxx> + +#include <algorithm> + +using css::uno::Reference; +using css::uno::Exception; +using css::uno::UNO_QUERY; +using css::uno::UNO_QUERY_THROW; +using css::uno::Any; +using css::uno::Sequence; +using css::awt::grid::XGridSelectionListener; +using css::style::VerticalAlignment; +using css::style::VerticalAlignment_TOP; +using css::view::SelectionType; +using css::view::SelectionType_NONE; +using css::view::SelectionType_RANGE; +using css::view::SelectionType_SINGLE; +using css::view::SelectionType_MULTI; +using css::awt::grid::XGridDataModel; +using css::awt::grid::GridInvalidDataException; +using css::lang::EventObject; +using css::lang::IndexOutOfBoundsException; +using css::awt::grid::XGridColumnModel; +using css::awt::grid::GridSelectionEvent; +using css::awt::grid::XGridColumn; +using css::container::ContainerEvent; +using css::awt::grid::GridDataEvent; +using css::awt::grid::GridInvalidModelException; + +namespace AccessibleEventId = css::accessibility::AccessibleEventId; +namespace AccessibleStateType = css::accessibility::AccessibleStateType; + +using namespace ::svt::table; + + +SVTXGridControl::SVTXGridControl() + :m_xTableModel( std::make_shared<UnoControlTableModel>() ) + ,m_bTableModelInitCompleted( false ) + ,m_aSelectionListeners( *this ) +{ +} + + +SVTXGridControl::~SVTXGridControl() +{ +} + + +void SVTXGridControl::SetWindow( const VclPtr< vcl::Window > &pWindow ) +{ + SVTXGridControl_Base::SetWindow( pWindow ); + impl_checkTableModelInit(); +} + + +void SVTXGridControl::impl_checkColumnIndex_throw( ::svt::table::TableControl const & i_table, sal_Int32 const i_columnIndex ) const +{ + if ( ( i_columnIndex < 0 ) || ( i_columnIndex >= i_table.GetColumnCount() ) ) + throw IndexOutOfBoundsException( OUString(), *const_cast< SVTXGridControl* >( this ) ); +} + + +void SVTXGridControl::impl_checkRowIndex_throw( ::svt::table::TableControl const & i_table, sal_Int32 const i_rowIndex ) const +{ + if ( ( i_rowIndex < 0 ) || ( i_rowIndex >= i_table.GetRowCount() ) ) + throw IndexOutOfBoundsException( OUString(), *const_cast< SVTXGridControl* >( this ) ); +} + + +sal_Int32 SAL_CALL SVTXGridControl::getRowAtPoint(::sal_Int32 x, ::sal_Int32 y) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getRowAtPoint: no control (anymore)!", -1 ); + + TableCell const tableCell = pTable->getTableControlInterface().hitTest( Point( x, y ) ); + return ( tableCell.nRow >= 0 ) ? tableCell.nRow : -1; +} + + +sal_Int32 SAL_CALL SVTXGridControl::getColumnAtPoint(::sal_Int32 x, ::sal_Int32 y) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getColumnAtPoint: no control (anymore)!", -1 ); + + TableCell const tableCell = pTable->getTableControlInterface().hitTest( Point( x, y ) ); + return ( tableCell.nColumn >= 0 ) ? tableCell.nColumn : -1; +} + + +sal_Int32 SAL_CALL SVTXGridControl::getCurrentColumn( ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getCurrentColumn: no control (anymore)!", -1 ); + + sal_Int32 const nColumn = pTable->GetCurrentColumn(); + return ( nColumn >= 0 ) ? nColumn : -1; +} + + +sal_Int32 SAL_CALL SVTXGridControl::getCurrentRow( ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getCurrentRow: no control (anymore)!", -1 ); + + sal_Int32 const nRow = pTable->GetCurrentRow(); + return ( nRow >= 0 ) ? nRow : -1; +} + + +void SAL_CALL SVTXGridControl::goToCell( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::getCurrentRow: no control (anymore)!" ); + + impl_checkColumnIndex_throw( *pTable, i_columnIndex ); + impl_checkRowIndex_throw( *pTable, i_rowIndex ); + + pTable->GoTo( i_columnIndex, i_rowIndex ); +} + + +void SAL_CALL SVTXGridControl::addSelectionListener(const Reference< XGridSelectionListener > & listener) +{ + m_aSelectionListeners.addInterface(listener); +} + + +void SAL_CALL SVTXGridControl::removeSelectionListener(const Reference< XGridSelectionListener > & listener) +{ + m_aSelectionListeners.removeInterface(listener); +} + + +void SVTXGridControl::setProperty( const OUString& PropertyName, const Any& aValue) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::setProperty: no control (anymore)!" ); + + switch( GetPropertyId( PropertyName ) ) + { + case BASEPROPERTY_ROW_HEADER_WIDTH: + { + sal_Int32 rowHeaderWidth( -1 ); + aValue >>= rowHeaderWidth; + if ( rowHeaderWidth <= 0 ) + { + SAL_WARN( "svtools.uno", "SVTXGridControl::setProperty: illegal row header width!" ); + break; + } + + m_xTableModel->setRowHeaderWidth( rowHeaderWidth ); + // TODO: the model should broadcast this change itself, and the table should invalidate itself as needed + pTable->Invalidate(); + } + break; + + case BASEPROPERTY_COLUMN_HEADER_HEIGHT: + { + sal_Int32 columnHeaderHeight = 0; + if ( !aValue.hasValue() ) + { + columnHeaderHeight = pTable->PixelToLogic(Size(0, pTable->GetTextHeight() + 3), MapMode(MapUnit::MapAppFont)).Height(); + } + else + { + aValue >>= columnHeaderHeight; + } + if ( columnHeaderHeight <= 0 ) + { + SAL_WARN( "svtools.uno", "SVTXGridControl::setProperty: illegal column header width!" ); + break; + } + + m_xTableModel->setColumnHeaderHeight( columnHeaderHeight ); + // TODO: the model should broadcast this change itself, and the table should invalidate itself as needed + pTable->Invalidate(); + } + break; + + case BASEPROPERTY_USE_GRID_LINES: + { + GridTableRenderer* pGridRenderer = dynamic_cast< GridTableRenderer* >( + m_xTableModel->getRenderer().get() ); + if ( !pGridRenderer ) + { + SAL_WARN( "svtools.uno", "SVTXGridControl::setProperty(UseGridLines): invalid renderer!" ); + break; + } + + bool bUseGridLines = false; + OSL_VERIFY( aValue >>= bUseGridLines ); + pGridRenderer->useGridLines( bUseGridLines ); + pTable->Invalidate(); + } + break; + + case BASEPROPERTY_ROW_HEIGHT: + { + sal_Int32 rowHeight = 0; + if ( !aValue.hasValue() ) + { + rowHeight = pTable->PixelToLogic(Size(0, pTable->GetTextHeight() + 3), MapMode(MapUnit::MapAppFont)).Height(); + } + else + { + aValue >>= rowHeight; + } + m_xTableModel->setRowHeight( rowHeight ); + if ( rowHeight <= 0 ) + { + SAL_WARN( "svtools.uno", "SVTXGridControl::setProperty: illegal row height!" ); + break; + } + + // TODO: the model should broadcast this change itself, and the table should invalidate itself as needed + pTable->Invalidate(); + } + break; + + case BASEPROPERTY_BACKGROUNDCOLOR: + { + // let the base class handle this for the TableControl + VCLXWindow::setProperty( PropertyName, aValue ); + // and forward to the grid control's data window + if ( pTable->IsBackground() ) + pTable->getDataWindow().SetBackground( pTable->GetBackground() ); + else + pTable->getDataWindow().SetBackground(); + } + break; + + case BASEPROPERTY_GRID_SELECTIONMODE: + { + SelectionType eSelectionType; + if( aValue >>= eSelectionType ) + { + SelectionMode eSelMode; + switch( eSelectionType ) + { + case SelectionType_SINGLE: eSelMode = SelectionMode::Single; break; + case SelectionType_RANGE: eSelMode = SelectionMode::Range; break; + case SelectionType_MULTI: eSelMode = SelectionMode::Multiple; break; + default: eSelMode = SelectionMode::NONE; break; + } + if( pTable->getSelEngine()->GetSelectionMode() != eSelMode ) + pTable->getSelEngine()->SetSelectionMode( eSelMode ); + } + break; + } + case BASEPROPERTY_HSCROLL: + { + bool bHScroll = true; + if( aValue >>= bHScroll ) + m_xTableModel->setHorizontalScrollbarVisibility( bHScroll ? ScrollbarShowAlways : ScrollbarShowSmart ); + break; + } + + case BASEPROPERTY_VSCROLL: + { + bool bVScroll = true; + if( aValue >>= bVScroll ) + { + m_xTableModel->setVerticalScrollbarVisibility( bVScroll ? ScrollbarShowAlways : ScrollbarShowSmart ); + } + break; + } + + case BASEPROPERTY_GRID_SHOWROWHEADER: + { + bool rowHeader = true; + if( aValue >>= rowHeader ) + { + m_xTableModel->setRowHeaders(rowHeader); + } + break; + } + + case BASEPROPERTY_GRID_ROW_BACKGROUND_COLORS: + m_xTableModel->setRowBackgroundColors( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_GRID_LINE_COLOR: + m_xTableModel->setLineColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_GRID_HEADER_BACKGROUND: + m_xTableModel->setHeaderBackgroundColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_GRID_HEADER_TEXT_COLOR: + m_xTableModel->setHeaderTextColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_ACTIVE_SEL_BACKGROUND_COLOR: + m_xTableModel->setActiveSelectionBackColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_INACTIVE_SEL_BACKGROUND_COLOR: + m_xTableModel->setInactiveSelectionBackColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_ACTIVE_SEL_TEXT_COLOR: + m_xTableModel->setActiveSelectionTextColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_INACTIVE_SEL_TEXT_COLOR: + m_xTableModel->setInactiveSelectionTextColor( aValue ); + pTable->Invalidate(); + break; + + + case BASEPROPERTY_TEXTCOLOR: + m_xTableModel->setTextColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_TEXTLINECOLOR: + m_xTableModel->setTextLineColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_VERTICALALIGN: + { + VerticalAlignment eAlign( VerticalAlignment_TOP ); + if ( aValue >>= eAlign ) + m_xTableModel->setVerticalAlign( eAlign ); + break; + } + + case BASEPROPERTY_GRID_SHOWCOLUMNHEADER: + { + bool colHeader = true; + if( aValue >>= colHeader ) + { + m_xTableModel->setColumnHeaders(colHeader); + } + break; + } + case BASEPROPERTY_GRID_DATAMODEL: + { + Reference< XGridDataModel > const xDataModel( aValue, UNO_QUERY ); + if ( !xDataModel.is() ) + throw GridInvalidDataException("Invalid data model.", *this ); + + m_xTableModel->setDataModel( xDataModel ); + impl_checkTableModelInit(); + } + break; + + case BASEPROPERTY_GRID_COLUMNMODEL: + { + // obtain new col model + Reference< XGridColumnModel > const xColumnModel( aValue, UNO_QUERY ); + if ( !xColumnModel.is() ) + throw GridInvalidModelException("Invalid column model.", *this ); + + // remove all old columns + m_xTableModel->removeAllColumns(); + + // announce to the TableModel + m_xTableModel->setColumnModel( xColumnModel ); + impl_checkTableModelInit(); + + // add new columns + impl_updateColumnsFromModel_nothrow(); + break; + } + default: + VCLXWindow::setProperty( PropertyName, aValue ); + break; + } +} + + +void SVTXGridControl::impl_checkTableModelInit() +{ + if ( !(!m_bTableModelInitCompleted && m_xTableModel->hasColumnModel() && m_xTableModel->hasDataModel()) ) + return; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + if ( !pTable ) + return; + + pTable->SetModel( PTableModel( m_xTableModel ) ); + + m_bTableModelInitCompleted = true; + + // ensure default columns exist, if they have not previously been added + Reference< XGridDataModel > const xDataModel( m_xTableModel->getDataModel(), css::uno::UNO_SET_THROW ); + Reference< XGridColumnModel > const xColumnModel( m_xTableModel->getColumnModel(), css::uno::UNO_SET_THROW ); + + sal_Int32 const nDataColumnCount = xDataModel->getColumnCount(); + if ( ( nDataColumnCount > 0 ) && ( xColumnModel->getColumnCount() == 0 ) ) + xColumnModel->setDefaultColumns( nDataColumnCount ); + // this will trigger notifications, which in turn will let us update our m_xTableModel +} + +namespace +{ + void lcl_convertColor( ::std::optional< ::Color > const & i_color, Any & o_colorValue ) + { + if ( !i_color ) + o_colorValue.clear(); + else + o_colorValue <<= sal_Int32(*i_color); + } +} + +Any SVTXGridControl::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getProperty: no control (anymore)!", Any() ); + + Any aPropertyValue; + + const sal_uInt16 nPropId = GetPropertyId( PropertyName ); + switch(nPropId) + { + case BASEPROPERTY_GRID_SELECTIONMODE: + { + SelectionType eSelectionType; + + SelectionMode eSelMode = pTable->getSelEngine()->GetSelectionMode(); + switch( eSelMode ) + { + case SelectionMode::Single: eSelectionType = SelectionType_SINGLE; break; + case SelectionMode::Range: eSelectionType = SelectionType_RANGE; break; + case SelectionMode::Multiple:eSelectionType = SelectionType_MULTI; break; + default: eSelectionType = SelectionType_NONE; break; + } + aPropertyValue <<= eSelectionType; + break; + } + + case BASEPROPERTY_GRID_SHOWROWHEADER: + aPropertyValue <<= m_xTableModel->hasRowHeaders(); + break; + + case BASEPROPERTY_GRID_SHOWCOLUMNHEADER: + aPropertyValue <<= m_xTableModel->hasColumnHeaders(); + break; + + case BASEPROPERTY_GRID_DATAMODEL: + aPropertyValue <<= m_xTableModel->getDataModel(); + break; + + case BASEPROPERTY_GRID_COLUMNMODEL: + aPropertyValue <<= m_xTableModel->getColumnModel(); + break; + + case BASEPROPERTY_HSCROLL: + { + bool const bHasScrollbar = ( m_xTableModel->getHorizontalScrollbarVisibility() != ScrollbarShowNever ); + aPropertyValue <<= bHasScrollbar; + break; + } + + case BASEPROPERTY_VSCROLL: + { + bool const bHasScrollbar = ( m_xTableModel->getVerticalScrollbarVisibility() != ScrollbarShowNever ); + aPropertyValue <<= bHasScrollbar; + break; + } + + case BASEPROPERTY_USE_GRID_LINES: + { + GridTableRenderer* pGridRenderer = dynamic_cast< GridTableRenderer* >( + m_xTableModel->getRenderer().get() ); + if ( !pGridRenderer ) + { + SAL_WARN( "svtools.uno", "SVTXGridControl::getProperty(UseGridLines): invalid renderer!" ); + break; + } + + aPropertyValue <<= pGridRenderer->useGridLines(); + } + break; + + case BASEPROPERTY_GRID_ROW_BACKGROUND_COLORS: + { + ::std::optional< ::std::vector< ::Color > > aColors( m_xTableModel->getRowBackgroundColors() ); + if ( !aColors ) + aPropertyValue.clear(); + else + { + Sequence< css::util::Color > aAPIColors( aColors->size() ); + std::transform(aColors->begin(), aColors->end(), aAPIColors.getArray(), + [](const auto& color) { return sal_Int32(color); }); + aPropertyValue <<= aAPIColors; + } + } + break; + + case BASEPROPERTY_GRID_LINE_COLOR: + lcl_convertColor( m_xTableModel->getLineColor(), aPropertyValue ); + break; + + case BASEPROPERTY_GRID_HEADER_BACKGROUND: + lcl_convertColor( m_xTableModel->getHeaderBackgroundColor(), aPropertyValue ); + break; + + case BASEPROPERTY_GRID_HEADER_TEXT_COLOR: + lcl_convertColor( m_xTableModel->getHeaderTextColor(), aPropertyValue ); + break; + + case BASEPROPERTY_ACTIVE_SEL_BACKGROUND_COLOR: + lcl_convertColor( m_xTableModel->getActiveSelectionBackColor(), aPropertyValue ); + break; + + case BASEPROPERTY_INACTIVE_SEL_BACKGROUND_COLOR: + lcl_convertColor( m_xTableModel->getInactiveSelectionBackColor(), aPropertyValue ); + break; + + case BASEPROPERTY_ACTIVE_SEL_TEXT_COLOR: + lcl_convertColor( m_xTableModel->getActiveSelectionTextColor(), aPropertyValue ); + break; + + case BASEPROPERTY_INACTIVE_SEL_TEXT_COLOR: + lcl_convertColor( m_xTableModel->getInactiveSelectionTextColor(), aPropertyValue ); + break; + + case BASEPROPERTY_TEXTCOLOR: + lcl_convertColor( m_xTableModel->getTextColor(), aPropertyValue ); + break; + + case BASEPROPERTY_TEXTLINECOLOR: + lcl_convertColor( m_xTableModel->getTextLineColor(), aPropertyValue ); + break; + + default: + aPropertyValue = VCLXWindow::getProperty( PropertyName ); + break; + } + + return aPropertyValue; +} + + +void SAL_CALL SVTXGridControl::rowsInserted( const GridDataEvent& i_event ) +{ + SolarMutexGuard aGuard; + m_xTableModel->notifyRowsInserted( i_event ); +} + + +void SAL_CALL + SVTXGridControl::rowsRemoved( const GridDataEvent& i_event ) +{ + SolarMutexGuard aGuard; + m_xTableModel->notifyRowsRemoved( i_event ); +} + + +void SAL_CALL SVTXGridControl::dataChanged( const GridDataEvent& i_event ) +{ + SolarMutexGuard aGuard; + + m_xTableModel->notifyDataChanged( i_event ); + + // if the data model is sortable, a dataChanged event is also fired in case the sort order changed. + // So, just in case, invalidate the column header area, too. + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::dataChanged: no control (anymore)!" ); + pTable->getTableControlInterface().invalidate( TableArea::ColumnHeaders ); +} + + +void SAL_CALL SVTXGridControl::rowHeadingChanged( const GridDataEvent& ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::rowHeadingChanged: no control (anymore)!" ); + + // TODO: we could do better than this - invalidate the header area only + pTable->getTableControlInterface().invalidate( TableArea::RowHeaders ); +} + + +void SAL_CALL SVTXGridControl::elementInserted( const ContainerEvent& i_event ) +{ + SolarMutexGuard aGuard; + + Reference< XGridColumn > const xGridColumn( i_event.Element, UNO_QUERY_THROW ); + + sal_Int32 nIndex( m_xTableModel->getColumnCount() ); + OSL_VERIFY( i_event.Accessor >>= nIndex ); + m_xTableModel->insertColumn( nIndex, xGridColumn ); +} + + +void SAL_CALL SVTXGridControl::elementRemoved( const ContainerEvent& i_event ) +{ + SolarMutexGuard aGuard; + + sal_Int32 nIndex( -1 ); + OSL_VERIFY( i_event.Accessor >>= nIndex ); + m_xTableModel->removeColumn( nIndex ); +} + + +void SAL_CALL SVTXGridControl::elementReplaced( const ContainerEvent& ) +{ + OSL_ENSURE( false, "SVTXGridControl::elementReplaced: not implemented!" ); + // at the moment, the XGridColumnModel API does not allow replacing columns + // TODO: replace the respective column in our table model +} + + +void SAL_CALL SVTXGridControl::disposing( const EventObject& Source ) +{ + VCLXWindow::disposing( Source ); +} + + +void SAL_CALL SVTXGridControl::selectRow( ::sal_Int32 i_rowIndex ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::selectRow: no control (anymore)!" ); + + impl_checkRowIndex_throw( *pTable, i_rowIndex ); + + pTable->SelectRow( i_rowIndex, true ); +} + + +void SAL_CALL SVTXGridControl::selectAllRows() +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::selectAllRows: no control (anymore)!" ); + + pTable->SelectAllRows( true ); +} + + +void SAL_CALL SVTXGridControl::deselectRow( ::sal_Int32 i_rowIndex ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::deselectRow: no control (anymore)!" ); + + impl_checkRowIndex_throw( *pTable, i_rowIndex ); + + pTable->SelectRow( i_rowIndex, false ); +} + + +void SAL_CALL SVTXGridControl::deselectAllRows() +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::deselectAllRows: no control (anymore)!" ); + + pTable->SelectAllRows( false ); +} + + +Sequence< ::sal_Int32 > SAL_CALL SVTXGridControl::getSelectedRows() +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getSelectedRows: no control (anymore)!", Sequence< sal_Int32 >() ); + + sal_Int32 selectionCount = pTable->GetSelectedRowCount(); + Sequence< sal_Int32 > selectedRows( selectionCount ); + auto selectedRowsRange = asNonConstRange(selectedRows); + for ( sal_Int32 i=0; i<selectionCount; ++i ) + selectedRowsRange[i] = pTable->GetSelectedRowIndex(i); + return selectedRows; +} + + +sal_Bool SAL_CALL SVTXGridControl::hasSelectedRows() +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::hasSelectedRows: no control (anymore)!", true ); + + return pTable->GetSelectedRowCount() > 0; +} + + +sal_Bool SAL_CALL SVTXGridControl::isRowSelected( ::sal_Int32 index ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::isRowSelected: no control (anymore)!", false ); + + return pTable->IsRowSelected( index ); +} + + +void SVTXGridControl::dispose() +{ + EventObject aObj; + aObj.Source = static_cast<cppu::OWeakObject*>(this); + m_aSelectionListeners.disposeAndClear( aObj ); + VCLXWindow::dispose(); +} + + +void SVTXGridControl::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + SolarMutexGuard aGuard; + + Reference< XWindow > xKeepAlive( this ); + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::ProcessWindowEvent: no control (anymore)!" ); + + bool handled = false; + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::TableRowSelect: + { + if ( m_aSelectionListeners.getLength() ) + ImplCallItemListeners(); + handled = true; + } + break; + + case VclEventId::ControlGetFocus: + { + // TODO: this doesn't belong here. It belongs into the TableControl/_Impl, so A11Y also + // works when the control is used outside the UNO context + if ( pTable->GetRowCount()>0 ) + { + pTable->commitCellEventIfAccessibleAlive( + AccessibleEventId::STATE_CHANGED, + Any( AccessibleStateType::FOCUSED ), + Any() + ); + pTable->commitTableEventIfAccessibleAlive( + AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, + Any(), + Any() + ); + } + else + { + pTable->commitTableEventIfAccessibleAlive( + AccessibleEventId::STATE_CHANGED, + Any( AccessibleStateType::FOCUSED ), + Any() + ); + } + } + break; + + case VclEventId::ControlLoseFocus: + { + // TODO: this doesn't belong here. It belongs into the TableControl/_Impl, so A11Y also + // works when the control is used outside the UNO context + if ( pTable->GetRowCount()>0 ) + { + pTable->commitCellEventIfAccessibleAlive( + AccessibleEventId::STATE_CHANGED, + Any(), + Any( AccessibleStateType::FOCUSED ) + ); + } + else + { + pTable->commitTableEventIfAccessibleAlive( + AccessibleEventId::STATE_CHANGED, + Any(), + Any( AccessibleStateType::FOCUSED ) + ); + } + } + break; + + default: break; + } + + if ( !handled ) + VCLXWindow::ProcessWindowEvent( rVclWindowEvent ); +} + + +void SVTXGridControl::setEnable( sal_Bool bEnable ) +{ + SolarMutexGuard aGuard; + + m_xTableModel->setEnabled( bEnable ); + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + pWindow->Enable( bEnable ); + pWindow->EnableInput( bEnable ); + pWindow->Invalidate(); + } +} + + +void SVTXGridControl::ImplCallItemListeners() +{ + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::ImplCallItemListeners: no control (anymore)!" ); + + if ( m_aSelectionListeners.getLength() ) + { + GridSelectionEvent aEvent; + aEvent.Source = static_cast<cppu::OWeakObject*>(this); + + sal_Int32 const nSelectedRowCount( pTable->GetSelectedRowCount() ); + aEvent.SelectedRowIndexes.realloc( nSelectedRowCount ); + auto pSelectedRowIndexes = aEvent.SelectedRowIndexes.getArray(); + for ( sal_Int32 i=0; i<nSelectedRowCount; ++i ) + pSelectedRowIndexes[i] = pTable->GetSelectedRowIndex( i ); + m_aSelectionListeners.selectionChanged( aEvent ); + } +} + + +void SVTXGridControl::impl_updateColumnsFromModel_nothrow() +{ + Reference< XGridColumnModel > const xColumnModel( m_xTableModel->getColumnModel() ); + ENSURE_OR_RETURN_VOID( xColumnModel.is(), "no model!" ); + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "no table!" ); + + try + { + const Sequence< Reference< XGridColumn > > columns = xColumnModel->getColumns(); + for ( auto const & colRef : columns ) + { + if ( !colRef.is() ) + { + SAL_WARN( "svtools.uno", "illegal column!" ); + continue; + } + + m_xTableModel->appendColumn( colRef ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/svtxgridcontrol.hxx b/svtools/source/uno/svtxgridcontrol.hxx new file mode 100644 index 000000000..9e4fd85fe --- /dev/null +++ b/svtools/source/uno/svtxgridcontrol.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 "unocontroltablemodel.hxx" +#include <table/tablecontrol.hxx> +#include <com/sun/star/awt/grid/XGridControl.hpp> +#include <com/sun/star/awt/grid/XGridRowSelection.hpp> +#include <com/sun/star/awt/grid/XGridDataListener.hpp> +#include <com/sun/star/awt/grid/GridDataEvent.hpp> +#include <com/sun/star/awt/grid/XGridSelectionListener.hpp> +#include <com/sun/star/container/XContainerListener.hpp> +#include <toolkit/awt/vclxwindow.hxx> +#include <cppuhelper/implbase.hxx> +#include <toolkit/helper/listenermultiplexer.hxx> + + +namespace svt::table { + class TableControl; +} + +typedef ::cppu::ImplInheritanceHelper < VCLXWindow + , css::awt::grid::XGridControl + , css::awt::grid::XGridRowSelection + , css::awt::grid::XGridDataListener + , css::container::XContainerListener + > SVTXGridControl_Base; +class SVTXGridControl final : public SVTXGridControl_Base +{ +public: + SVTXGridControl(); + virtual ~SVTXGridControl() override; + + // XGridDataListener + virtual void SAL_CALL rowsInserted( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL rowsRemoved( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL dataChanged( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL rowHeadingChanged( const css::awt::grid::GridDataEvent& Event ) override; + + // XContainerListener + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // XGridControl + virtual ::sal_Int32 SAL_CALL getRowAtPoint(::sal_Int32 x, ::sal_Int32 y) override; + virtual ::sal_Int32 SAL_CALL getColumnAtPoint(::sal_Int32 x, ::sal_Int32 y) override; + virtual ::sal_Int32 SAL_CALL getCurrentColumn( ) override; + virtual ::sal_Int32 SAL_CALL getCurrentRow( ) override; + virtual void SAL_CALL goToCell( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex ) override; + + // XGridRowSelection + virtual void SAL_CALL selectRow( ::sal_Int32 i_rowIndex ) override; + virtual void SAL_CALL selectAllRows() override; + virtual void SAL_CALL deselectRow( ::sal_Int32 i_rowIndex ) override; + virtual void SAL_CALL deselectAllRows() override; + virtual css::uno::Sequence< ::sal_Int32 > SAL_CALL getSelectedRows() override; + virtual sal_Bool SAL_CALL hasSelectedRows() override; + virtual sal_Bool SAL_CALL isRowSelected(::sal_Int32 index) override; + virtual void SAL_CALL addSelectionListener(const css::uno::Reference< css::awt::grid::XGridSelectionListener > & listener) override; + virtual void SAL_CALL removeSelectionListener(const css::uno::Reference< css::awt::grid::XGridSelectionListener > & listener) override; + + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + css::uno::Any SAL_CALL getProperty( const OUString& PropertyName ) override; + + // css::lang::XComponent + void SAL_CALL dispose( ) override; + + // XWindow + void SAL_CALL setEnable( sal_Bool bEnable ) override; + +private: + // VCLXWindow + virtual void SetWindow( const VclPtr< vcl::Window > &pWindow ) override; + + void impl_updateColumnsFromModel_nothrow(); + void impl_checkTableModelInit(); + + void impl_checkColumnIndex_throw( ::svt::table::TableControl const & i_table, sal_Int32 const i_columnIndex ) const; + void impl_checkRowIndex_throw( ::svt::table::TableControl const & i_table, sal_Int32 const i_rowIndex ) const; + + virtual void ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) override; + void ImplCallItemListeners(); + + std::shared_ptr< ::svt::table::UnoControlTableModel > m_xTableModel; + bool m_bTableModelInitCompleted; + SelectionListenerMultiplexer m_aSelectionListeners; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/toolboxcontroller.cxx b/svtools/source/uno/toolboxcontroller.cxx new file mode 100644 index 000000000..21e186baf --- /dev/null +++ b/svtools/source/uno/toolboxcontroller.cxx @@ -0,0 +1,802 @@ +/* -*- 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 <svtools/toolboxcontroller.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <vcl/commandinfoprovider.hxx> +#include <vcl/svapp.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/toolbox.hxx> +#include <vcl/weldutils.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> + +const int TOOLBARCONTROLLER_PROPHANDLE_SUPPORTSVISIBLE = 1; +constexpr OUStringLiteral TOOLBARCONTROLLER_PROPNAME_SUPPORTSVISIBLE = u"SupportsVisible"; + + +using namespace ::cppu; +using namespace css::awt; +using namespace css::uno; +using namespace css::util; +using namespace css::beans; +using namespace css::lang; +using namespace css::frame; + +namespace svt +{ + +ToolboxController::ToolboxController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& xFrame, + const OUString& aCommandURL ) : + OPropertyContainer( GetBroadcastHelper() ) + , m_bSupportVisible( false ) + , m_bInitialized( false ) + , m_bDisposed( false ) + , m_bSidebar( false ) + , m_nToolBoxId( SAL_MAX_UINT16 ) + , m_xFrame( xFrame ) + , m_xContext( rxContext ) + , m_aCommandURL( aCommandURL ) + , m_aListenerContainer( m_aMutex ) + , m_pToolbar(nullptr) + , m_pBuilder(nullptr) +{ + OSL_ASSERT( m_xContext.is() ); + registerProperty( TOOLBARCONTROLLER_PROPNAME_SUPPORTSVISIBLE, + TOOLBARCONTROLLER_PROPHANDLE_SUPPORTSVISIBLE, + css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY, + &m_bSupportVisible, cppu::UnoType<decltype(m_bSupportVisible)>::get()); + + try + { + m_xUrlTransformer = URLTransformer::create( rxContext ); + } + catch(const Exception&) + { + } +} + +ToolboxController::ToolboxController() : + OPropertyContainer(GetBroadcastHelper()) + , m_bSupportVisible(false) + , m_bInitialized( false ) + , m_bDisposed( false ) + , m_bSidebar( false ) + , m_nToolBoxId( SAL_MAX_UINT16 ) + , m_aListenerContainer( m_aMutex ) + , m_pToolbar(nullptr) + , m_pBuilder(nullptr) +{ + registerProperty( TOOLBARCONTROLLER_PROPNAME_SUPPORTSVISIBLE, + TOOLBARCONTROLLER_PROPHANDLE_SUPPORTSVISIBLE, + css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY, + &m_bSupportVisible, cppu::UnoType<decltype(m_bSupportVisible)>::get()); +} + +ToolboxController::~ToolboxController() +{ +} + +Reference< XFrame > ToolboxController::getFrameInterface() const +{ + SolarMutexGuard aSolarMutexGuard; + return m_xFrame; +} + +const Reference< XComponentContext > & ToolboxController::getContext() const +{ + SolarMutexGuard aSolarMutexGuard; + return m_xContext; +} + +Reference< XLayoutManager > ToolboxController::getLayoutManager() const +{ + Reference< XLayoutManager > xLayoutManager; + Reference< XPropertySet > xPropSet; + { + SolarMutexGuard aSolarMutexGuard; + xPropSet.set( m_xFrame, UNO_QUERY ); + } + + if ( xPropSet.is() ) + { + try + { + xLayoutManager.set(xPropSet->getPropertyValue("LayoutManager"),UNO_QUERY); + } + catch ( Exception& ) + { + } + } + + return xLayoutManager; +} + +// XInterface +Any SAL_CALL ToolboxController::queryInterface( const Type& rType ) +{ + css::uno::Any a(ToolboxController_Base::queryInterface(rType)); + return a.hasValue() ? a : OPropertyContainer::queryInterface(rType); +} + +void SAL_CALL ToolboxController::acquire() noexcept +{ + ToolboxController_Base::acquire(); +} + +void SAL_CALL ToolboxController::release() noexcept +{ + ToolboxController_Base::release(); +} + +css::uno::Sequence<css::uno::Type> ToolboxController::getTypes() +{ + return comphelper::concatSequences(ToolboxController_Base::getTypes(), + getBaseTypes()); +} + +void SAL_CALL ToolboxController::initialize( const Sequence< Any >& aArguments ) +{ + bool bInitialized( true ); + + { + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + bInitialized = m_bInitialized; + } + + if ( bInitialized ) + return; + + SolarMutexGuard aSolarMutexGuard; + m_bInitialized = true; + m_bSupportVisible = false; + PropertyValue aPropValue; + for ( const auto& rArgument : aArguments ) + { + if ( rArgument >>= aPropValue ) + { + if ( aPropValue.Name == "Frame" ) + m_xFrame.set(aPropValue.Value,UNO_QUERY); + else if ( aPropValue.Name == "CommandURL" ) + aPropValue.Value >>= m_aCommandURL; + else if ( aPropValue.Name == "ServiceManager" ) + { + Reference<XMultiServiceFactory> xMSF(aPropValue.Value, UNO_QUERY); + if (xMSF.is()) + m_xContext = comphelper::getComponentContext(xMSF); + } + else if ( aPropValue.Name == "ParentWindow" ) + m_xParentWindow.set(aPropValue.Value,UNO_QUERY); + else if ( aPropValue.Name == "ModuleIdentifier" ) + aPropValue.Value >>= m_sModuleName; + else if ( aPropValue.Name == "Identifier" ) + { + sal_uInt16 nTmp; + if (aPropValue.Value >>= nTmp) + m_nToolBoxId = ToolBoxItemId(nTmp); + } + else if ( aPropValue.Name == "IsSidebar" ) + aPropValue.Value >>= m_bSidebar; + } + } + + try + { + if ( !m_xUrlTransformer.is() && m_xContext.is() ) + m_xUrlTransformer = URLTransformer::create( m_xContext ); + } + catch(const Exception&) + { + } + + if ( !m_aCommandURL.isEmpty() ) + m_aListenerMap.emplace( m_aCommandURL, Reference< XDispatch >() ); + + if (weld::TransportAsXWindow* pTunnel = dynamic_cast<weld::TransportAsXWindow*>(getParent().get())) + { + m_pToolbar = dynamic_cast<weld::Toolbar*>(pTunnel->getWidget()); + assert(m_pToolbar && "must be a toolbar"); + m_pBuilder = pTunnel->getBuilder(); + } +} + +void SAL_CALL ToolboxController::update() +{ + { + SolarMutexGuard aSolarMutexGuard; + if ( m_bDisposed ) + throw DisposedException(); + } + + // Bind all registered listeners to their dispatch objects + bindListener(); +} + +// XComponent +void SAL_CALL ToolboxController::dispose() +{ + Reference< XComponent > xThis(this); + + { + SolarMutexGuard aSolarMutexGuard; + if ( m_bDisposed ) + throw DisposedException(); + } + + css::lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + SolarMutexGuard aSolarMutexGuard; + Reference< XStatusListener > xStatusListener(this); + for (auto const& listener : m_aListenerMap) + { + try + { + Reference< XDispatch > xDispatch( listener.second ); + + css::util::URL aTargetURL; + aTargetURL.Complete = listener.first; + if ( m_xUrlTransformer.is() ) + m_xUrlTransformer->parseStrict( aTargetURL ); + + if ( xDispatch.is() && xStatusListener.is() ) + xDispatch->removeStatusListener( xStatusListener, aTargetURL ); + } + catch ( Exception& ) + { + } + + } + + m_bDisposed = true; +} + +void SAL_CALL ToolboxController::addEventListener( const Reference< XEventListener >& xListener ) +{ + m_aListenerContainer.addInterface( cppu::UnoType<XEventListener>::get(), xListener ); +} + +void SAL_CALL ToolboxController::removeEventListener( const Reference< XEventListener >& aListener ) +{ + m_aListenerContainer.removeInterface( cppu::UnoType<XEventListener>::get(), aListener ); +} + +// XEventListener +void SAL_CALL ToolboxController::disposing( const EventObject& Source ) +{ + Reference< XInterface > xSource( Source.Source ); + + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + return; + + for (auto & listener : m_aListenerMap) + { + // Compare references and release dispatch references if they are equal. + Reference< XInterface > xIfac(listener.second, UNO_QUERY); + if ( xSource == xIfac ) + listener.second.clear(); + } + + Reference< XInterface > xIfac( m_xFrame, UNO_QUERY ); + if ( xIfac == xSource ) + m_xFrame.clear(); +} + +// XStatusListener +void SAL_CALL ToolboxController::statusChanged( const FeatureStateEvent& ) +{ + // must be implemented by sub class +} + +// XToolbarController +void SAL_CALL ToolboxController::execute( sal_Int16 KeyModifier ) +{ + Reference< XDispatch > xDispatch; + OUString aCommandURL; + + { + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_bInitialized && + m_xFrame.is() && + !m_aCommandURL.isEmpty() ) + { + aCommandURL = m_aCommandURL; + URLToDispatchMap::iterator pIter = m_aListenerMap.find( m_aCommandURL ); + if ( pIter != m_aListenerMap.end() ) + xDispatch = pIter->second; + } + } + + if ( !xDispatch.is() ) + return; + + try + { + css::util::URL aTargetURL; + + // Provide key modifier information to dispatch function + Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier) }; + + aTargetURL.Complete = aCommandURL; + if ( m_xUrlTransformer.is() ) + m_xUrlTransformer->parseStrict( aTargetURL ); + xDispatch->dispatch( aTargetURL, aArgs ); + } + catch ( DisposedException& ) + { + } +} + +void SAL_CALL ToolboxController::click() +{ +} + +void SAL_CALL ToolboxController::doubleClick() +{ +} + +Reference< XWindow > SAL_CALL ToolboxController::createPopupWindow() +{ + return Reference< XWindow >(); +} + +Reference< XWindow > SAL_CALL ToolboxController::createItemWindow( const Reference< XWindow >& ) +{ + return Reference< XWindow >(); +} + +void ToolboxController::addStatusListener( const OUString& aCommandURL ) +{ + Reference< XDispatch > xDispatch; + Reference< XStatusListener > xStatusListener; + css::util::URL aTargetURL; + + { + SolarMutexGuard aSolarMutexGuard; + URLToDispatchMap::iterator pIter = m_aListenerMap.find( aCommandURL ); + + // Already in the list of status listener. Do nothing. + if ( pIter != m_aListenerMap.end() ) + return; + + // Check if we are already initialized. Implementation starts adding itself as status listener when + // initialize is called. + if ( !m_bInitialized ) + { + // Put into the unordered_map of status listener. Will be activated when initialized is called + m_aListenerMap.emplace( aCommandURL, Reference< XDispatch >() ); + return; + } + else + { + // Add status listener directly as initialize has already been called. + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + if ( m_xContext.is() && xDispatchProvider.is() ) + { + aTargetURL.Complete = aCommandURL; + if ( m_xUrlTransformer.is() ) + m_xUrlTransformer->parseStrict( aTargetURL ); + xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + + xStatusListener = this; + URLToDispatchMap::iterator aIter = m_aListenerMap.find( aCommandURL ); + if ( aIter != m_aListenerMap.end() ) + { + Reference< XDispatch > xOldDispatch( aIter->second ); + aIter->second = xDispatch; + + try + { + if ( xOldDispatch.is() ) + xOldDispatch->removeStatusListener( xStatusListener, aTargetURL ); + } + catch ( Exception& ) + { + } + } + else + m_aListenerMap.emplace( aCommandURL, xDispatch ); + } + } + } + + // Call without locked mutex as we are called back from dispatch implementation + try + { + if ( xDispatch.is() ) + xDispatch->addStatusListener( xStatusListener, aTargetURL ); + } + catch ( Exception& ) + { + } +} + +void ToolboxController::removeStatusListener( const OUString& aCommandURL ) +{ + SolarMutexGuard aSolarMutexGuard; + + URLToDispatchMap::iterator pIter = m_aListenerMap.find( aCommandURL ); + if ( pIter == m_aListenerMap.end() ) + return; + + Reference< XDispatch > xDispatch( pIter->second ); + Reference< XStatusListener > xStatusListener(this); + m_aListenerMap.erase( pIter ); + + try + { + css::util::URL aTargetURL; + aTargetURL.Complete = aCommandURL; + if ( m_xUrlTransformer.is() ) + m_xUrlTransformer->parseStrict( aTargetURL ); + + if ( xDispatch.is() && xStatusListener.is() ) + xDispatch->removeStatusListener( xStatusListener, aTargetURL ); + } + catch ( Exception& ) + { + } +} + +void ToolboxController::bindListener() +{ + std::vector< Listener > aDispatchVector; + Reference< XStatusListener > xStatusListener; + + { + SolarMutexGuard aSolarMutexGuard; + + if ( !m_bInitialized ) + return; + + // Collect all registered command URL's and store them temporary + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + if ( m_xContext.is() && xDispatchProvider.is() ) + { + xStatusListener = this; + for (auto & listener : m_aListenerMap) + { + css::util::URL aTargetURL; + aTargetURL.Complete = listener.first; + if ( m_xUrlTransformer.is() ) + m_xUrlTransformer->parseStrict( aTargetURL ); + + Reference< XDispatch > xDispatch(listener.second); + if ( xDispatch.is() ) + { + // We already have a dispatch object => we have to requery. + // Release old dispatch object and remove it as listener + try + { + xDispatch->removeStatusListener( xStatusListener, aTargetURL ); + } + catch ( Exception& ) + { + } + } + + listener.second.clear(); + xDispatch.clear(); + + // Query for dispatch object. Old dispatch will be released with this, too. + try + { + xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + } + catch ( Exception& ) + { + } + + // it may be a command alias + if (!xDispatch.is()) + { + try + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(listener.first, + vcl::CommandInfoProvider::GetModuleIdentifier(m_xFrame)); + OUString sRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties); + + if (!sRealCommand.isEmpty()) + { + aTargetURL.Complete = sRealCommand; + if ( m_xUrlTransformer.is() ) + m_xUrlTransformer->parseStrict( aTargetURL ); + + xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + } + } + catch ( Exception& ) + { + } + } + + listener.second = xDispatch; + + Listener aListener( aTargetURL, xDispatch ); + aDispatchVector.push_back( aListener ); + } + } + } + + // Call without locked mutex as we are called back from dispatch implementation + if ( !xStatusListener.is() ) + return; + + try + { + for (Listener & rListener : aDispatchVector) + { + if ( rListener.xDispatch.is() ) + rListener.xDispatch->addStatusListener( xStatusListener, rListener.aURL ); + else if ( rListener.aURL.Complete == m_aCommandURL ) + { + try + { + // Send status changed for the main URL, if we cannot get a valid dispatch object. + // UI disables the button. Catch exception as we release our mutex, it is possible + // that someone else already disposed this instance! + FeatureStateEvent aFeatureStateEvent; + aFeatureStateEvent.IsEnabled = false; + aFeatureStateEvent.FeatureURL = rListener.aURL; + aFeatureStateEvent.State = Any(); + xStatusListener->statusChanged( aFeatureStateEvent ); + } + catch ( Exception& ) + { + } + } + } + } + catch ( Exception& ) + { + } +} + +void ToolboxController::unbindListener() +{ + SolarMutexGuard aSolarMutexGuard; + + if ( !m_bInitialized ) + return; + + // Collect all registered command URL's and store them temporary + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + if ( !(m_xContext.is() && xDispatchProvider.is()) ) + return; + + Reference< XStatusListener > xStatusListener(this); + for (auto & listener : m_aListenerMap) + { + css::util::URL aTargetURL; + aTargetURL.Complete = listener.first; + if ( m_xUrlTransformer.is() ) + m_xUrlTransformer->parseStrict( aTargetURL ); + + Reference< XDispatch > xDispatch(listener.second); + if ( xDispatch.is() ) + { + // We already have a dispatch object => we have to requery. + // Release old dispatch object and remove it as listener + try + { + xDispatch->removeStatusListener( xStatusListener, aTargetURL ); + } + catch ( Exception& ) + { + } + } + listener.second.clear(); + } +} + +void ToolboxController::updateStatus() +{ + bindListener(); +} + +void ToolboxController::updateStatus( const OUString& aCommandURL ) +{ + Reference< XDispatch > xDispatch; + Reference< XStatusListener > xStatusListener; + css::util::URL aTargetURL; + + { + SolarMutexGuard aSolarMutexGuard; + + if ( !m_bInitialized ) + return; + + // Try to find a dispatch object for the requested command URL + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + xStatusListener = this; + if ( m_xContext.is() && xDispatchProvider.is() ) + { + aTargetURL.Complete = aCommandURL; + if ( m_xUrlTransformer.is() ) + m_xUrlTransformer->parseStrict( aTargetURL ); + xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + } + } + + if ( !(xDispatch.is() && xStatusListener.is()) ) + return; + + // Catch exception as we release our mutex, it is possible that someone else + // has already disposed this instance! + // Add/remove status listener to get an update status information from the + // requested command. + try + { + xDispatch->addStatusListener( xStatusListener, aTargetURL ); + xDispatch->removeStatusListener( xStatusListener, aTargetURL ); + } + catch ( Exception& ) + { + } +} + + +void ToolboxController::dispatchCommand( const OUString& sCommandURL, const Sequence< PropertyValue >& rArgs, const OUString &sTarget ) +{ + try + { + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY_THROW ); + URL aURL; + aURL.Complete = sCommandURL; + getURLTransformer()->parseStrict( aURL ); + + Reference< XDispatch > xDispatch( xDispatchProvider->queryDispatch( aURL, sTarget, 0 ), UNO_SET_THROW ); + + std::unique_ptr<DispatchInfo> pDispatchInfo(new DispatchInfo( xDispatch, aURL, rArgs )); + if ( Application::PostUserEvent( LINK(nullptr, ToolboxController, ExecuteHdl_Impl), + pDispatchInfo.get() ) ) + pDispatchInfo.release(); + + } + catch( Exception& ) + { + } +} + + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL ToolboxController::getPropertySetInfo() +{ + Reference<XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +::cppu::IPropertyArrayHelper& ToolboxController::getInfoHelper() +{ + return *getArrayHelper(); +} + + +::cppu::IPropertyArrayHelper* ToolboxController::createArrayHelper( ) const +{ + css::uno::Sequence< Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); +} + +sal_Bool SAL_CALL ToolboxController::convertFastPropertyValue( css::uno::Any& aConvertedValue , + css::uno::Any& aOldValue , + sal_Int32 nHandle , + const css::uno::Any& aValue ) +{ + switch (nHandle) + { + case TOOLBARCONTROLLER_PROPHANDLE_SUPPORTSVISIBLE: + { + bool aNewValue(false); + aValue >>= aNewValue; + if (aNewValue != m_bSupportVisible) + { + aConvertedValue <<= aNewValue; + aOldValue <<= m_bSupportVisible; + return true; + } + return false; + } + } + return OPropertyContainer::convertFastPropertyValue(aConvertedValue, aOldValue, nHandle, aValue); +} + +void SAL_CALL ToolboxController::setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& aValue ) +{ + OPropertyContainer::setFastPropertyValue_NoBroadcast(nHandle, aValue); + if (TOOLBARCONTROLLER_PROPHANDLE_SUPPORTSVISIBLE == nHandle) + { + bool rValue(false); + if (( aValue >>= rValue ) && m_bInitialized) + m_bSupportVisible = rValue; + } +} + + +IMPL_STATIC_LINK( ToolboxController, ExecuteHdl_Impl, void*, p, void ) +{ + DispatchInfo* pDispatchInfo = static_cast<DispatchInfo*>(p); + pDispatchInfo->mxDispatch->dispatch( pDispatchInfo->maURL, pDispatchInfo->maArgs ); + delete pDispatchInfo; +} + +void ToolboxController::enable( bool bEnable ) +{ + ToolBox* pToolBox = nullptr; + ToolBoxItemId nItemId; + if( getToolboxId( nItemId, &pToolBox ) ) + { + pToolBox->EnableItem( nItemId, bEnable ); + } +} + +bool ToolboxController::getToolboxId( ToolBoxItemId& rItemId, ToolBox** ppToolBox ) +{ + if( (m_nToolBoxId != ToolBoxItemId(SAL_MAX_UINT16)) && (ppToolBox == nullptr) ) + return false; + + ToolBox* pToolBox = static_cast< ToolBox* >( VCLUnoHelper::GetWindow( getParent() ) ); + + if( (m_nToolBoxId == ToolBoxItemId(SAL_MAX_UINT16)) && pToolBox ) + { + const ToolBox::ImplToolItems::size_type nCount = pToolBox->GetItemCount(); + for ( ToolBox::ImplToolItems::size_type nPos = 0; nPos < nCount; ++nPos ) + { + const ToolBoxItemId nItemId = pToolBox->GetItemId( nPos ); + if ( pToolBox->GetItemCommand( nItemId ) == m_aCommandURL ) + { + m_nToolBoxId = nItemId; + break; + } + } + } + + if( ppToolBox ) + *ppToolBox = pToolBox; + + rItemId = m_nToolBoxId; + + return (rItemId != ToolBoxItemId(SAL_MAX_UINT16)) && (( ppToolBox == nullptr) || (*ppToolBox != nullptr) ); +} +//end + +} // svt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/unocontroltablemodel.cxx b/svtools/source/uno/unocontroltablemodel.cxx new file mode 100644 index 000000000..14f9c6977 --- /dev/null +++ b/svtools/source/uno/unocontroltablemodel.cxx @@ -0,0 +1,835 @@ +/* -*- 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 "unocontroltablemodel.hxx" +#include "unogridcolumnfacade.hxx" + +#include <table/defaultinputhandler.hxx> +#include <table/gridtablerenderer.hxx> + +#include <com/sun/star/awt/grid/XSortableGridData.hpp> +#include <com/sun/star/util/Color.hpp> +#include <o3tl/safeint.hxx> +#include <sal/log.hxx> +#include <cppuhelper/weakref.hxx> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> + + +namespace svt::table +{ + + + using css::uno::Reference; + using css::uno::Sequence; + using css::uno::UNO_QUERY_THROW; + using css::uno::UNO_QUERY; + using css::awt::grid::XGridColumn; + using css::uno::Exception; + using css::awt::grid::XGridDataModel; + using css::awt::grid::XGridColumnModel; + using css::uno::Any; + using css::style::VerticalAlignment_TOP; + using css::style::VerticalAlignment; + using css::uno::WeakReference; + using css::awt::grid::GridDataEvent; + using css::awt::grid::XSortableGridData; + using css::beans::Pair; + + + //= UnoControlTableModel +#define DBG_CHECK_ME() \ + DBG_TESTSOLARMUTEX(); \ + + UnoControlTableModel::UnoControlTableModel() + :aColumns ( ) + ,bHasColumnHeaders ( true ) + ,bHasRowHeaders ( false ) + ,eVScrollMode ( ScrollbarShowNever ) + ,eHScrollMode ( ScrollbarShowNever ) + ,pRenderer ( ) + ,pInputHandler ( ) + ,nRowHeight ( 10 ) + ,nColumnHeaderHeight ( 10 ) + ,nRowHeaderWidth ( 10 ) + ,m_aGridLineColor ( ) + ,m_aHeaderBackgroundColor ( ) + ,m_aHeaderTextColor ( ) + ,m_aActiveSelectionBackColor ( ) + ,m_aInactiveSelectionBackColor ( ) + ,m_aActiveSelectionTextColor ( ) + ,m_aInactiveSelectionTextColor ( ) + ,m_aTextColor ( ) + ,m_aTextLineColor ( ) + ,m_aRowColors ( ) + ,m_eVerticalAlign ( VerticalAlignment_TOP ) + ,bEnabled ( true ) + { + pRenderer = std::make_shared<GridTableRenderer>( *this ); + pInputHandler = std::make_shared<DefaultInputHandler>(); + } + + + UnoControlTableModel::~UnoControlTableModel() + { + } + + + TableSize UnoControlTableModel::getColumnCount() const + { + DBG_CHECK_ME(); + return static_cast<TableSize>(aColumns.size()); + } + + + TableSize UnoControlTableModel::getRowCount() const + { + DBG_CHECK_ME(); + + TableSize nRowCount = 0; + try + { + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + ENSURE_OR_THROW( xDataModel.is(), "no data model anymore!" ); + nRowCount = xDataModel->getRowCount(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return nRowCount; + } + + + bool UnoControlTableModel::hasColumnHeaders() const + { + DBG_CHECK_ME(); + return bHasColumnHeaders; + } + + + bool UnoControlTableModel::hasRowHeaders() const + { + DBG_CHECK_ME(); + return bHasRowHeaders; + } + + + void UnoControlTableModel::setRowHeaders(bool _bRowHeaders) + { + DBG_CHECK_ME(); + if ( bHasRowHeaders == _bRowHeaders ) + return; + + bHasRowHeaders = _bRowHeaders; + impl_notifyTableMetricsChanged(); + } + + + void UnoControlTableModel::setColumnHeaders(bool _bColumnHeaders) + { + DBG_CHECK_ME(); + if ( bHasColumnHeaders == _bColumnHeaders ) + return; + + bHasColumnHeaders = _bColumnHeaders; + impl_notifyTableMetricsChanged(); + } + + + PColumnModel UnoControlTableModel::getColumnModel( ColPos column ) + { + DBG_CHECK_ME(); + ENSURE_OR_RETURN( ( column >= 0 ) && ( column < getColumnCount() ), + "DefaultTableModel::getColumnModel: invalid index!", PColumnModel() ); + return aColumns[ column ]; + } + + + void UnoControlTableModel::appendColumn( Reference< XGridColumn > const & i_column ) + { + DBG_CHECK_ME(); + insertColumn( aColumns.size(), i_column ); + } + + + void UnoControlTableModel::insertColumn( ColPos const i_position, Reference< XGridColumn > const & i_column ) + { + DBG_CHECK_ME(); + ENSURE_OR_RETURN_VOID( ( i_position >= 0 ) && ( o3tl::make_unsigned( i_position ) <= aColumns.size() ), + "UnoControlTableModel::insertColumn: illegal position!" ); + + const PColumnModel pColumn = std::make_shared<UnoGridColumnFacade>( *this, i_column ); + aColumns.insert( aColumns.begin() + i_position, pColumn ); + + // notify listeners + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->columnInserted(); + } + } + + + void UnoControlTableModel::removeColumn( ColPos const i_position ) + { + DBG_CHECK_ME(); + ENSURE_OR_RETURN_VOID( ( i_position >= 0 ) && ( o3tl::make_unsigned( i_position ) <= aColumns.size() ), + "UnoControlTableModel::removeColumn: illegal position!" ); + + // remove the column + ColumnModels::iterator pos = aColumns.begin() + i_position; + const PColumnModel pColumn = *pos; + aColumns.erase( pos ); + + // notify listeners + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->columnRemoved(); + } + + // dispose the column + UnoGridColumnFacade* pColumnImpl = dynamic_cast< UnoGridColumnFacade* >( pColumn.get() ); + OSL_ENSURE( pColumnImpl != nullptr, "UnoControlTableModel::removeColumn: illegal column implementation!" ); + if ( pColumnImpl ) + pColumnImpl->dispose(); + } + + + void UnoControlTableModel::removeAllColumns() + { + DBG_CHECK_ME(); + if ( aColumns.empty() ) + return; + + // dispose the column instances + for (auto const& col : aColumns) + { + UnoGridColumnFacade* pColumn = dynamic_cast< UnoGridColumnFacade* >( col.get() ); + if ( !pColumn ) + { + SAL_WARN( "svtools.uno", "UnoControlTableModel::removeAllColumns: illegal column implementation!" ); + continue; + } + + pColumn->dispose(); + } + aColumns.clear(); + + // notify listeners + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->allColumnsRemoved(); + } + } + + + void UnoControlTableModel::impl_notifyTableMetricsChanged() const + { + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->tableMetricsChanged(); + } + } + + + PTableRenderer UnoControlTableModel::getRenderer() const + { + DBG_CHECK_ME(); + return pRenderer; + } + + + PTableInputHandler UnoControlTableModel::getInputHandler() const + { + DBG_CHECK_ME(); + return pInputHandler; + } + + + TableMetrics UnoControlTableModel::getRowHeight() const + { + DBG_CHECK_ME(); + return nRowHeight; + } + + + void UnoControlTableModel::setRowHeight(TableMetrics _nRowHeight) + { + DBG_CHECK_ME(); + if ( nRowHeight == _nRowHeight ) + return; + + nRowHeight = _nRowHeight; + impl_notifyTableMetricsChanged(); + } + + + TableMetrics UnoControlTableModel::getColumnHeaderHeight() const + { + DBG_CHECK_ME(); + DBG_ASSERT( hasColumnHeaders(), "DefaultTableModel::getColumnHeaderHeight: invalid call!" ); + return nColumnHeaderHeight; + } + + + TableMetrics UnoControlTableModel::getRowHeaderWidth() const + { + DBG_CHECK_ME(); + DBG_ASSERT( hasRowHeaders(), "DefaultTableModel::getRowHeaderWidth: invalid call!" ); + return nRowHeaderWidth; + } + + void UnoControlTableModel::setColumnHeaderHeight(TableMetrics _nHeight) + { + DBG_CHECK_ME(); + if ( nColumnHeaderHeight == _nHeight ) + return; + + nColumnHeaderHeight = _nHeight; + impl_notifyTableMetricsChanged(); + } + + + void UnoControlTableModel::setRowHeaderWidth(TableMetrics _nWidth) + { + DBG_CHECK_ME(); + if ( nRowHeaderWidth == _nWidth ) + return; + + nRowHeaderWidth = _nWidth; + impl_notifyTableMetricsChanged(); + } + + + ScrollbarVisibility UnoControlTableModel::getVerticalScrollbarVisibility() const + { + DBG_CHECK_ME(); + return eVScrollMode; + } + + + ScrollbarVisibility UnoControlTableModel::getHorizontalScrollbarVisibility() const + { + DBG_CHECK_ME(); + return eHScrollMode; + } + + + void UnoControlTableModel::addTableModelListener( const PTableModelListener& i_listener ) + { + DBG_CHECK_ME(); + ENSURE_OR_RETURN_VOID( !!i_listener, "illegal NULL listener" ); + m_aListeners.push_back( i_listener ); + } + + + void UnoControlTableModel::removeTableModelListener( const PTableModelListener& i_listener ) + { + DBG_CHECK_ME(); + auto lookup = std::find(m_aListeners.begin(), m_aListeners.end(), i_listener); + if (lookup != m_aListeners.end()) + { + m_aListeners.erase( lookup ); + return; + } + OSL_ENSURE( false, "UnoControlTableModel::removeTableModelListener: listener is not registered - sure you're doing the right thing here?" ); + } + + + void UnoControlTableModel::setVerticalScrollbarVisibility( ScrollbarVisibility const i_visibility ) + { + DBG_CHECK_ME(); + eVScrollMode = i_visibility; + } + + + void UnoControlTableModel::setHorizontalScrollbarVisibility( ScrollbarVisibility const i_visibility ) + { + DBG_CHECK_ME(); + eHScrollMode = i_visibility; + } + + + void UnoControlTableModel::setDataModel( Reference< XGridDataModel > const & i_gridDataModel ) + { + DBG_CHECK_ME(); + m_aDataModel = i_gridDataModel; + // TODO: register as listener, so we're notified of row/data changes, and can multiplex them to our + // own listeners + } + + + Reference< XGridDataModel > UnoControlTableModel::getDataModel() const + { + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + return xDataModel; + } + + + bool UnoControlTableModel::hasDataModel() const + { + return getDataModel().is(); + } + + + void UnoControlTableModel::setColumnModel( Reference< XGridColumnModel > const & i_gridColumnModel ) + { + DBG_CHECK_ME(); + m_aColumnModel = i_gridColumnModel; + } + + + Reference< XGridColumnModel > UnoControlTableModel::getColumnModel() const + { + Reference< XGridColumnModel > const xColumnModel( m_aColumnModel ); + return xColumnModel; + } + + + bool UnoControlTableModel::hasColumnModel() const + { + return getColumnModel().is(); + } + + + void UnoControlTableModel::getCellContent( ColPos const i_col, RowPos const i_row, Any& o_cellContent ) + { + DBG_CHECK_ME(); + + o_cellContent.clear(); + try + { + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + ENSURE_OR_RETURN_VOID( xDataModel.is(), "UnoControlTableModel::getCellContent: no data model anymore!" ); + + PColumnModel const pColumn = getColumnModel( i_col ); + UnoGridColumnFacade* pColumnImpl = dynamic_cast< UnoGridColumnFacade* >( pColumn.get() ); + ENSURE_OR_RETURN_VOID( pColumnImpl != nullptr, "UnoControlTableModel::getCellContent: no (valid) column at this position!" ); + sal_Int32 const nDataColumnIndex = pColumnImpl->getDataColumnIndex() >= 0 ? pColumnImpl->getDataColumnIndex() : i_col; + + if ( nDataColumnIndex >= xDataModel->getColumnCount() ) + { + // this is allowed, in case the column model has been dynamically extended, but the data model does + // not (yet?) know about it. + // So, handle it gracefully. + #if OSL_DEBUG_LEVEL > 0 + Reference< XGridColumnModel > const xColumnModel( m_aColumnModel ); + OSL_ENSURE( xColumnModel.is() && i_col < xColumnModel->getColumnCount(), + "UnoControlTableModel::getCellContent: request a column's value which the ColumnModel doesn't know about!" ); + #endif + } + else + { + o_cellContent = xDataModel->getCellData( nDataColumnIndex, i_row ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + + void UnoControlTableModel::getCellToolTip( ColPos const i_col, RowPos const i_row, Any& o_cellToolTip ) + { + DBG_CHECK_ME(); + try + { + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + ENSURE_OR_THROW( xDataModel.is(), "no data model anymore!" ); + + o_cellToolTip = xDataModel->getCellToolTip( i_col, i_row ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + + Any UnoControlTableModel::getRowHeading( RowPos const i_rowPos ) const + { + DBG_CHECK_ME(); + + Any aRowHeading; + + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + ENSURE_OR_RETURN( xDataModel.is(), "UnoControlTableModel::getRowHeading: no data model anymore!", aRowHeading ); + + try + { + aRowHeading = xDataModel->getRowHeading( i_rowPos ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return aRowHeading; + } + + + namespace + { + void lcl_setColor( Any const & i_color, ::std::optional< ::Color > & o_convertedColor ) + { + if ( !i_color.hasValue() ) + o_convertedColor.reset(); + else + { + Color nColor = COL_TRANSPARENT; + if ( i_color >>= nColor ) + { + o_convertedColor = nColor; + } + else + { + OSL_ENSURE( false, "lcl_setColor: could not extract color value!" ); + } + } + } + } + + + ::std::optional< ::Color > UnoControlTableModel::getLineColor() const + { + DBG_CHECK_ME(); + return m_aGridLineColor; + } + + + void UnoControlTableModel::setLineColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aGridLineColor ); + } + + + ::std::optional< ::Color > UnoControlTableModel::getHeaderBackgroundColor() const + { + DBG_CHECK_ME(); + return m_aHeaderBackgroundColor; + } + + + void UnoControlTableModel::setHeaderBackgroundColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aHeaderBackgroundColor ); + } + + + ::std::optional< ::Color > UnoControlTableModel::getHeaderTextColor() const + { + DBG_CHECK_ME(); + return m_aHeaderTextColor; + } + + + ::std::optional< ::Color > UnoControlTableModel::getActiveSelectionBackColor() const + { + DBG_CHECK_ME(); + return m_aActiveSelectionBackColor; + } + + + ::std::optional< ::Color > UnoControlTableModel::getInactiveSelectionBackColor() const + { + DBG_CHECK_ME(); + return m_aInactiveSelectionBackColor; + } + + + ::std::optional< ::Color > UnoControlTableModel::getActiveSelectionTextColor() const + { + DBG_CHECK_ME(); + return m_aActiveSelectionTextColor; + } + + + ::std::optional< ::Color > UnoControlTableModel::getInactiveSelectionTextColor() const + { + DBG_CHECK_ME(); + return m_aInactiveSelectionTextColor; + } + + + void UnoControlTableModel::setHeaderTextColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aHeaderTextColor ); + } + + + void UnoControlTableModel::setActiveSelectionBackColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aActiveSelectionBackColor ); + } + + + void UnoControlTableModel::setInactiveSelectionBackColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aInactiveSelectionBackColor ); + } + + + void UnoControlTableModel::setActiveSelectionTextColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aActiveSelectionTextColor ); + } + + + void UnoControlTableModel::setInactiveSelectionTextColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aInactiveSelectionTextColor ); + } + + + ::std::optional< ::Color > UnoControlTableModel::getTextColor() const + { + DBG_CHECK_ME(); + return m_aTextColor; + } + + + void UnoControlTableModel::setTextColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aTextColor ); + } + + + ::std::optional< ::Color > UnoControlTableModel::getTextLineColor() const + { + DBG_CHECK_ME(); + return m_aTextColor; + } + + + void UnoControlTableModel::setTextLineColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aTextLineColor ); + } + + + ::std::optional< ::std::vector< ::Color > > UnoControlTableModel::getRowBackgroundColors() const + { + DBG_CHECK_ME(); + return m_aRowColors; + } + + + void UnoControlTableModel::setRowBackgroundColors( css::uno::Any const & i_APIValue ) + { + DBG_CHECK_ME(); + Sequence< css::util::Color > aAPIColors; + if ( !( i_APIValue >>= aAPIColors ) ) + m_aRowColors.reset(); + else + { + ::std::vector< ::Color > aColors( aAPIColors.getLength() ); + std::transform(std::cbegin(aAPIColors), std::cend(aAPIColors), aColors.begin(), + [](const css::util::Color& rAPIColor) -> ::Color { return Color(ColorTransparency, rAPIColor); }); + m_aRowColors = aColors; + } + } + + + VerticalAlignment UnoControlTableModel::getVerticalAlign() const + { + DBG_CHECK_ME(); + return m_eVerticalAlign; + } + + + void UnoControlTableModel::setVerticalAlign( VerticalAlignment _xAlign ) + { + DBG_CHECK_ME(); + m_eVerticalAlign = _xAlign; + } + + + ColPos UnoControlTableModel::getColumnPos( UnoGridColumnFacade const & i_column ) const + { + DBG_CHECK_ME(); + ColPos nPos = 0; + for (auto const& col : aColumns) + { + if ( &i_column == col.get() ) + return nPos; + ++nPos; + } + OSL_ENSURE( false, "UnoControlTableModel::getColumnPos: column not found!" ); + return COL_INVALID; + } + + + ITableDataSort* UnoControlTableModel::getSortAdapter() + { + DBG_CHECK_ME(); + + Reference< XSortableGridData > const xSortAccess( getDataModel(), UNO_QUERY ); + if ( xSortAccess.is() ) + return this; + return nullptr; + } + + + bool UnoControlTableModel::isEnabled() const + { + DBG_CHECK_ME(); + return bEnabled; + } + + + void UnoControlTableModel::setEnabled( bool _bEnabled ) + { + DBG_CHECK_ME(); + bEnabled = _bEnabled; + } + + + void UnoControlTableModel::sortByColumn( ColPos const i_column, ColumnSortDirection const i_sortDirection ) + { + DBG_CHECK_ME(); + + try + { + Reference< XSortableGridData > const xSortAccess( getDataModel(), UNO_QUERY_THROW ); + xSortAccess->sortByColumn( i_column, i_sortDirection == ColumnSortAscending ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + + ColumnSort UnoControlTableModel::getCurrentSortOrder() const + { + DBG_CHECK_ME(); + + ColumnSort currentSort; + try + { + Reference< XSortableGridData > const xSortAccess( getDataModel(), UNO_QUERY_THROW ); + Pair< ::sal_Int32, sal_Bool > const aCurrentSortOrder( xSortAccess->getCurrentSortOrder() ); + currentSort.nColumnPos = aCurrentSortOrder.First; + currentSort.eSortDirection = aCurrentSortOrder.Second ? ColumnSortAscending : ColumnSortDescending; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return currentSort; + } + + + void UnoControlTableModel::notifyColumnChange( ColPos const i_columnPos, ColumnAttributeGroup const i_attributeGroup ) const + { + DBG_CHECK_ME(); + ENSURE_OR_RETURN_VOID( ( i_columnPos >= 0 ) && ( i_columnPos < getColumnCount() ), + "UnoControlTableModel::notifyColumnChange: invalid column index!" ); + + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->columnChanged( i_columnPos, i_attributeGroup ); + } + } + + + void UnoControlTableModel::notifyRowsInserted( GridDataEvent const & i_event ) const + { + // check sanity of the event + ENSURE_OR_RETURN_VOID( i_event.FirstRow >= 0, "UnoControlTableModel::notifyRowsInserted: invalid first row!" ); + ENSURE_OR_RETURN_VOID( i_event.LastRow >= i_event.FirstRow, "UnoControlTableModel::notifyRowsInserted: invalid row indexes!" ); + + // check own sanity + Reference< XGridColumnModel > const xColumnModel( m_aColumnModel ); + ENSURE_OR_RETURN_VOID( xColumnModel.is(), "UnoControlTableModel::notifyRowsInserted: no column model anymore!" ); + + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + ENSURE_OR_RETURN_VOID( xDataModel.is(), "UnoControlTableModel::notifyRowsInserted: no data model anymore!" ); + + // implicitly add columns to the column model + // TODO: is this really a good idea? + sal_Int32 const dataColumnCount = xDataModel->getColumnCount(); + OSL_ENSURE( dataColumnCount > 0, "UnoControlTableModel::notifyRowsInserted: no columns at all?" ); + + sal_Int32 const modelColumnCount = xColumnModel->getColumnCount(); + if ( ( modelColumnCount == 0 ) && ( dataColumnCount > 0 ) ) + { + // TODO: shouldn't we clear the mutexes guard for this call? + xColumnModel->setDefaultColumns( dataColumnCount ); + } + + // multiplex the event to our own listeners + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->rowsInserted( i_event.FirstRow, i_event.LastRow ); + } + } + + + void UnoControlTableModel::notifyRowsRemoved( GridDataEvent const & i_event ) const + { + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->rowsRemoved( i_event.FirstRow, i_event.LastRow ); + } + } + + + void UnoControlTableModel::notifyDataChanged( css::awt::grid::GridDataEvent const & i_event ) const + { + RowPos const firstRow = i_event.FirstRow == -1 ? 0 : i_event.FirstRow; + RowPos const lastRow = i_event.FirstRow == -1 ? getRowCount() - 1 : i_event.LastRow; + + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->cellsUpdated( firstRow, lastRow ); + } + } + + + void UnoControlTableModel::notifyAllDataChanged() const + { + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->cellsUpdated( 0, getRowCount() - 1 ); + } + } + + +} // svt::table + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/unocontroltablemodel.hxx b/svtools/source/uno/unocontroltablemodel.hxx new file mode 100644 index 000000000..4a6935762 --- /dev/null +++ b/svtools/source/uno/unocontroltablemodel.hxx @@ -0,0 +1,181 @@ +/* -*- 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 <table/tablemodel.hxx> +#include <table/tablesort.hxx> +#include <tools/color.hxx> + +#include <com/sun/star/awt/grid/GridDataEvent.hpp> +#include <com/sun/star/awt/grid/XGridColumnModel.hpp> +#include <com/sun/star/awt/grid/XGridDataModel.hpp> +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/style/VerticalAlignment.hpp> +#include <cppuhelper/weakref.hxx> + + +namespace svt::table +{ + + + //= UnoControlTableModel + + class UnoGridColumnFacade; + class UnoControlTableModel : public ITableModel, public ITableDataSort + { + public: + UnoControlTableModel(); + virtual ~UnoControlTableModel() override; + + public: + // ITableModel overridables + virtual TableSize getColumnCount() const override; + virtual TableSize getRowCount() const override; + virtual bool hasColumnHeaders() const override; + virtual bool hasRowHeaders() const override; + virtual PColumnModel getColumnModel( ColPos column ) override; + virtual PTableRenderer getRenderer() const override; + virtual PTableInputHandler getInputHandler() const override; + virtual TableMetrics getRowHeight() const override; + virtual TableMetrics getColumnHeaderHeight() const override; + virtual TableMetrics getRowHeaderWidth() const override; + virtual ScrollbarVisibility getVerticalScrollbarVisibility() const override; + virtual ScrollbarVisibility getHorizontalScrollbarVisibility() const override; + virtual void addTableModelListener( const PTableModelListener& i_listener ) override; + virtual void removeTableModelListener( const PTableModelListener& i_listener ) override; + virtual void getCellContent( ColPos const i_col, RowPos const i_row, css::uno::Any& o_cellContent ) override; + virtual void getCellToolTip( ColPos const i_col, RowPos const i_row, css::uno::Any & o_cellToolTip ) override; + virtual css::uno::Any getRowHeading( RowPos const i_rowPos ) const override; + virtual ::std::optional< ::Color > getLineColor() const override; + virtual ::std::optional< ::Color > getHeaderBackgroundColor() const override; + virtual ::std::optional< ::Color > getHeaderTextColor() const override; + virtual ::std::optional< ::Color > getActiveSelectionBackColor() const override; + virtual ::std::optional< ::Color > getInactiveSelectionBackColor() const override; + virtual ::std::optional< ::Color > getActiveSelectionTextColor() const override; + virtual ::std::optional< ::Color > getInactiveSelectionTextColor() const override; + virtual ::std::optional< ::Color > getTextColor() const override; + virtual ::std::optional< ::Color > getTextLineColor() const override; + virtual ::std::optional< ::std::vector< ::Color > > + getRowBackgroundColors() const override; + virtual css::style::VerticalAlignment + getVerticalAlign() const override; + virtual ITableDataSort* getSortAdapter() override; + virtual bool isEnabled() const override; + + // ITableDataSort overridables + virtual void sortByColumn( ColPos const i_column, ColumnSortDirection const i_sortDirection ) override; + virtual ColumnSort getCurrentSortOrder() const override; + + // column write access + void appendColumn( css::uno::Reference< css::awt::grid::XGridColumn > const & i_column ); + void insertColumn( ColPos const i_position, css::uno::Reference< css::awt::grid::XGridColumn > const & i_column ); + void removeColumn( ColPos const i_position ); + void removeAllColumns(); + + // other operations + void setVerticalScrollbarVisibility( ScrollbarVisibility const i_visibility ); + void setHorizontalScrollbarVisibility( ScrollbarVisibility const i_visibility ); + + void setDataModel( css::uno::Reference< css::awt::grid::XGridDataModel > const & i_gridDataModel ); + bool hasDataModel() const; + css::uno::Reference< css::awt::grid::XGridDataModel > + getDataModel() const; + void setColumnModel( css::uno::Reference< css::awt::grid::XGridColumnModel > const & i_gridColumnModel ); + bool hasColumnModel() const; + css::uno::Reference< css::awt::grid::XGridColumnModel > + getColumnModel() const; + + void setRowHeaders(bool _bRowHeaders); + void setColumnHeaders(bool _bColumnHeaders); + + void setRowHeight( TableMetrics _nHeight ); + void setRowHeaderWidth( TableMetrics _nWidth ); + void setColumnHeaderHeight( TableMetrics _nHeight ); + + void setLineColor( css::uno::Any const & i_color ); + void setHeaderBackgroundColor( css::uno::Any const & i_color ); + void setHeaderTextColor( css::uno::Any const & i_color ); + void setActiveSelectionBackColor( css::uno::Any const & i_color ); + void setInactiveSelectionBackColor( css::uno::Any const & i_color ); + void setActiveSelectionTextColor( css::uno::Any const & i_color ); + void setInactiveSelectionTextColor( css::uno::Any const & i_color ); + void setTextColor( css::uno::Any const & i_color ); + void setTextLineColor( css::uno::Any const & i_color ); + void setRowBackgroundColors( css::uno::Any const & i_APIValue ); + + void setVerticalAlign(css::style::VerticalAlignment _rAlign); + void setEnabled( bool _bEnabled ); + + // multiplexing of XGridDataListener events + void notifyRowsInserted( css::awt::grid::GridDataEvent const & i_event ) const; + void notifyRowsRemoved( css::awt::grid::GridDataEvent const & i_event ) const; + void notifyDataChanged( css::awt::grid::GridDataEvent const & i_event ) const; + + /// retrieves the index of a column within the model + ColPos getColumnPos( UnoGridColumnFacade const & i_column ) const; + + /// notifies a change in a column belonging to the model + void notifyColumnChange( ColPos const i_columnPos, ColumnAttributeGroup const i_attributeGroup ) const; + + /** notifies a change in all data represented by the model. To be used if you cannot specified the changed data + in more detail. + */ + void notifyAllDataChanged() const; + + private: + void impl_notifyTableMetricsChanged() const; + + typedef ::std::vector< PTableModelListener > ModellListeners; + typedef ::std::vector< PColumnModel > ColumnModels; + + ColumnModels aColumns; + bool bHasColumnHeaders; + bool bHasRowHeaders; + ScrollbarVisibility eVScrollMode; + ScrollbarVisibility eHScrollMode; + PTableRenderer pRenderer; + PTableInputHandler pInputHandler; + TableMetrics nRowHeight; + TableMetrics nColumnHeaderHeight; + TableMetrics nRowHeaderWidth; + ::std::optional< ::Color > m_aGridLineColor; + ::std::optional< ::Color > m_aHeaderBackgroundColor; + ::std::optional< ::Color > m_aHeaderTextColor; + ::std::optional< ::Color > m_aActiveSelectionBackColor; + ::std::optional< ::Color > m_aInactiveSelectionBackColor; + ::std::optional< ::Color > m_aActiveSelectionTextColor; + ::std::optional< ::Color > m_aInactiveSelectionTextColor; + ::std::optional< ::Color > m_aTextColor; + ::std::optional< ::Color > m_aTextLineColor; + ::std::optional< ::std::vector< ::Color > > m_aRowColors; + css::style::VerticalAlignment m_eVerticalAlign; + bool bEnabled; + ModellListeners m_aListeners; + css::uno::WeakReference< css::awt::grid::XGridDataModel > m_aDataModel; + css::uno::WeakReference< css::awt::grid::XGridColumnModel > m_aColumnModel; + }; + + +} // svt::table + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/unoevent.cxx b/svtools/source/uno/unoevent.cxx new file mode 100644 index 000000000..30f4cbecb --- /dev/null +++ b/svtools/source/uno/unoevent.cxx @@ -0,0 +1,450 @@ +/* -*- 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/PropertyValue.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/string_view.hxx> +#include <osl/diagnose.h> +#include <sfx2/event.hxx> +#include <svtools/unoevent.hxx> +#include <svl/macitem.hxx> + +using namespace ::com::sun::star; +using namespace css::uno; + +using css::container::NoSuchElementException; +using css::container::XNameReplace; +using css::lang::IllegalArgumentException; +using css::beans::PropertyValue; + + +constexpr OUStringLiteral sAPI_ServiceName = u"com.sun.star.container.XNameReplace"; +constexpr OUStringLiteral sEventType = u"EventType"; +constexpr OUStringLiteral sMacroName = u"MacroName"; +constexpr OUStringLiteral sLibrary = u"Library"; +constexpr OUStringLiteral sStarBasic = u"StarBasic"; +constexpr OUStringLiteral sScript = u"Script"; +constexpr OUStringLiteral sNone = u"None"; + +namespace { + +void getAnyFromMacro(Any& rAny, const SvxMacro& rMacro) +{ + bool bRetValueOK = false; // do we have a ret value? + + if (rMacro.HasMacro()) + { + switch (rMacro.GetScriptType()) + { + case STARBASIC: + { + // create sequence + Sequence<PropertyValue> aSequence( + // create type + { comphelper::makePropertyValue(sEventType, OUString(sStarBasic)), + // macro name + comphelper::makePropertyValue(sMacroName, rMacro.GetMacName()), + // library name + comphelper::makePropertyValue(sLibrary, rMacro.GetLibName()) }); + + rAny <<= aSequence; + bRetValueOK = true; + break; + } + case EXTENDED_STYPE: + { + // create sequence + Sequence<PropertyValue> aSequence( + // create type + { comphelper::makePropertyValue(sEventType, OUString(sScript)), + // macro name + comphelper::makePropertyValue(sScript, rMacro.GetMacName()) }); + + rAny <<= aSequence; + bRetValueOK = true; + break; + } + case JAVASCRIPT: + default: + OSL_FAIL("not implemented"); + } + } + // else: bRetValueOK not set + + // if we don't have a return value, make an empty one + if ( bRetValueOK) + return; + + // create "None" macro + Sequence<PropertyValue> aSequence{ comphelper::makePropertyValue(sEventType, OUString(sNone)) }; + rAny <<= aSequence; +} + +/// @throws IllegalArgumentException +void getMacroFromAny( + SvxMacro& rMacro, + const Any& rAny) +{ + // get sequence + Sequence<PropertyValue> aSequence; + rAny >>= aSequence; + + // process ... + bool bTypeOK = false; + bool bNone = false; // true if EventType=="None" + enum ScriptType eType = EXTENDED_STYPE; + OUString sScriptVal; + OUString sMacroVal; + OUString sLibVal; + for (const PropertyValue& aValue : std::as_const(aSequence)) + { + if (aValue.Name == sEventType) + { + OUString sTmp; + aValue.Value >>= sTmp; + if (sTmp == sStarBasic) + { + eType = STARBASIC; + bTypeOK = true; + } + else if (sTmp == "JavaScript") + { + eType = JAVASCRIPT; + bTypeOK = true; + } + else if (sTmp == sScript) + { + eType = EXTENDED_STYPE; + bTypeOK = true; + } + else if (sTmp == sNone) + { + bNone = true; + bTypeOK = true; + } + // else: unknown script type + } + else if (aValue.Name == sMacroName) + { + aValue.Value >>= sMacroVal; + } + else if (aValue.Name == sLibrary) + { + aValue.Value >>= sLibVal; + } + else if (aValue.Name == sScript) + { + aValue.Value >>= sScriptVal; + } + // else: unknown PropertyValue -> ignore + } + + if (!bTypeOK) + { + // no valid type: abort + throw IllegalArgumentException(); + } + + if (bNone) + { + // return empty macro + rMacro = SvxMacro( "", "" ); + } + else + { + if (eType == STARBASIC) + { + // create macro and return + SvxMacro aMacro(sMacroVal, sLibVal, eType); + rMacro = aMacro; + } + else if (eType == EXTENDED_STYPE) + { + SvxMacro aMacro(sScriptVal, sScript); + rMacro = aMacro; + } + else + { + // we can't process type: abort + // TODO: JavaScript macros + throw IllegalArgumentException(); + } + } +} + +} + +SvBaseEventDescriptor::SvBaseEventDescriptor( const SvEventDescription* pSupportedMacroItems ) : + mpSupportedMacroItems(pSupportedMacroItems), + mnMacroItems(0) +{ + assert(pSupportedMacroItems != nullptr && "Need a list of supported events!"); + + for( ; mpSupportedMacroItems[mnMacroItems].mnEvent != SvMacroItemId::NONE; mnMacroItems++) ; +} + + +SvBaseEventDescriptor::~SvBaseEventDescriptor() +{ +} + +void SvBaseEventDescriptor::replaceByName( + const OUString& rName, + const Any& rElement ) +{ + SvMacroItemId nMacroID = getMacroID(rName); + + // error checking + if (SvMacroItemId::NONE == nMacroID) + throw NoSuchElementException(); + if (rElement.getValueType() != getElementType()) + throw IllegalArgumentException(); + + // get sequence + Sequence<PropertyValue> aSequence; + rElement >>= aSequence; + + // perform replace (in subclass) + SvxMacro aMacro("",""); + getMacroFromAny(aMacro, rElement); + replaceByName(nMacroID, aMacro); +} + +Any SvBaseEventDescriptor::getByName( + const OUString& rName ) +{ + SvMacroItemId nMacroID = getMacroID(rName); + + // error checking + if (SvMacroItemId::NONE == nMacroID) + throw NoSuchElementException(); + + // perform get (in subclass) + Any aAny; + SvxMacro aMacro( "", "" ); + getByName(aMacro, nMacroID); + getAnyFromMacro(aAny, aMacro); + return aAny; +} + +Sequence<OUString> SvBaseEventDescriptor::getElementNames() +{ + // create and fill sequence + Sequence<OUString> aSequence(mnMacroItems); + auto aSequenceRange = asNonConstRange(aSequence); + for( sal_Int16 i = 0; i < mnMacroItems; i++) + { + aSequenceRange[i] = OUString::createFromAscii( mpSupportedMacroItems[i].mpEventName ); + } + + return aSequence; +} + +sal_Bool SvBaseEventDescriptor::hasByName( + const OUString& rName ) +{ + SvMacroItemId nMacroID = getMacroID(rName); + return (nMacroID != SvMacroItemId::NONE); +} + +Type SvBaseEventDescriptor::getElementType() +{ + return cppu::UnoType<Sequence<PropertyValue>>::get(); +} + +sal_Bool SvBaseEventDescriptor::hasElements() +{ + return mnMacroItems != 0; +} + +sal_Bool SvBaseEventDescriptor::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence<OUString> SvBaseEventDescriptor::getSupportedServiceNames() +{ + return { sAPI_ServiceName }; +} + +SvMacroItemId SvBaseEventDescriptor::mapNameToEventID(std::u16string_view rName) const +{ + // iterate over known event names + for(sal_Int16 i = 0; i < mnMacroItems; i++) + { + if( o3tl::equalsAscii(rName, mpSupportedMacroItems[i].mpEventName)) + { + return mpSupportedMacroItems[i].mnEvent; + } + } + + // not found -> return zero + return SvMacroItemId::NONE; +} + +SvMacroItemId SvBaseEventDescriptor::getMacroID(std::u16string_view rName) const +{ + return mapNameToEventID(rName); +} + +SvEventDescriptor::SvEventDescriptor( + XInterface& rParent, + const SvEventDescription* pSupportedMacroItems) : + SvBaseEventDescriptor(pSupportedMacroItems), + xParentRef(&rParent) +{ +} + + +SvEventDescriptor::~SvEventDescriptor() +{ + // automatically release xParentRef ! +} + +void SvEventDescriptor::replaceByName( + const SvMacroItemId nEvent, + const SvxMacro& rMacro) +{ + SvxMacroItem aItem(getMacroItemWhich()); + aItem.SetMacroTable(getMacroItem().GetMacroTable()); + aItem.SetMacro(nEvent, rMacro); + setMacroItem(aItem); +} + +void SvEventDescriptor::getByName( + SvxMacro& rMacro, + const SvMacroItemId nEvent ) +{ + const SvxMacroItem& rItem = getMacroItem(); + if( rItem.HasMacro( nEvent ) ) + rMacro = rItem.GetMacro(nEvent); + else + { + SvxMacro aEmptyMacro("", ""); + rMacro = aEmptyMacro; + } +} + +SvDetachedEventDescriptor::SvDetachedEventDescriptor( + const SvEventDescription* pSupportedMacroItems) : + SvBaseEventDescriptor(pSupportedMacroItems) +{ + aMacros.resize(mnMacroItems); +} + +SvDetachedEventDescriptor::~SvDetachedEventDescriptor() +{ +} + +sal_Int16 SvDetachedEventDescriptor::getIndex(const SvMacroItemId nID) const +{ + // iterate over supported events + sal_Int16 nIndex = 0; + while ( (mpSupportedMacroItems[nIndex].mnEvent != nID) && + (mpSupportedMacroItems[nIndex].mnEvent != SvMacroItemId::NONE) ) + { + nIndex++; + } + return (mpSupportedMacroItems[nIndex].mnEvent == nID) ? nIndex : -1; +} + +OUString SvDetachedEventDescriptor::getImplementationName() +{ + return "SvDetachedEventDescriptor"; +} + + +void SvDetachedEventDescriptor::replaceByName( + const SvMacroItemId nEvent, + const SvxMacro& rMacro) +{ + sal_Int16 nIndex = getIndex(nEvent); + if (-1 == nIndex) + throw IllegalArgumentException(); + + aMacros[nIndex].reset( new SvxMacro(rMacro.GetMacName(), rMacro.GetLibName(), + rMacro.GetScriptType() ) ); +} + + +void SvDetachedEventDescriptor::getByName( + SvxMacro& rMacro, + const SvMacroItemId nEvent ) +{ + sal_Int16 nIndex = getIndex(nEvent); + if (-1 == nIndex ) + throw NoSuchElementException(); + + if( aMacros[nIndex] ) + rMacro = *aMacros[nIndex]; +} + +bool SvDetachedEventDescriptor::hasById( + const SvMacroItemId nEvent ) const /// item ID of event +{ + sal_Int16 nIndex = getIndex(nEvent); + if (-1 == nIndex) + throw IllegalArgumentException(); + + return (nullptr != aMacros[nIndex]) && aMacros[nIndex]->HasMacro(); +} + + +SvMacroTableEventDescriptor::SvMacroTableEventDescriptor(const SvEventDescription* pSupportedMacroItems) : + SvDetachedEventDescriptor(pSupportedMacroItems) +{ +} + +SvMacroTableEventDescriptor::SvMacroTableEventDescriptor( + const SvxMacroTableDtor& rMacroTable, + const SvEventDescription* pSupportedMacroItems) : + SvDetachedEventDescriptor(pSupportedMacroItems) +{ + assert(mpSupportedMacroItems); + for(sal_Int16 i = 0; mpSupportedMacroItems[i].mnEvent != SvMacroItemId::NONE; i++) + { + const SvMacroItemId nEvent = mpSupportedMacroItems[i].mnEvent; + const SvxMacro* pMacro = rMacroTable.Get(nEvent); + if (nullptr != pMacro) + replaceByName(nEvent, *pMacro); + } +} + +SvMacroTableEventDescriptor::~SvMacroTableEventDescriptor() +{ +} + +void SvMacroTableEventDescriptor::copyMacrosIntoTable( + SvxMacroTableDtor& rMacroTable) +{ + for(sal_Int16 i = 0; mpSupportedMacroItems[i].mnEvent != SvMacroItemId::NONE; i++) + { + const SvMacroItemId nEvent = mpSupportedMacroItems[i].mnEvent; + if (hasById(nEvent)) + { + SvxMacro& rMacro = rMacroTable.Insert(nEvent, SvxMacro("", "")); + getByName(rMacro, nEvent); + } + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/unogridcolumnfacade.cxx b/svtools/source/uno/unogridcolumnfacade.cxx new file mode 100644 index 000000000..c41600cee --- /dev/null +++ b/svtools/source/uno/unogridcolumnfacade.cxx @@ -0,0 +1,310 @@ +/* -*- 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 "unogridcolumnfacade.hxx" +#include "unocontroltablemodel.hxx" + +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/awt/grid/XGridColumnListener.hpp> + +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/svapp.hxx> +#include <cppuhelper/implbase.hxx> + + +namespace svt::table +{ + + + using css::uno::Reference; + using css::awt::grid::XGridColumn; + using css::uno::Exception; + using css::awt::grid::XGridColumnListener; + using css::lang::EventObject; + using css::awt::grid::GridColumnEvent; + using css::style::HorizontalAlignment_LEFT; + using css::style::HorizontalAlignment; + + + namespace + { + template< class T1, class T2 > + void lcl_set( Reference< XGridColumn > const & i_column, void ( SAL_CALL XGridColumn::*i_setter )( T1 ), + T2 i_value ) + { + try + { + (i_column.get()->*i_setter) ( i_value ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + template< class ATTRIBUTE_TYPE > + ATTRIBUTE_TYPE lcl_get( Reference< XGridColumn > const & i_column, ATTRIBUTE_TYPE ( SAL_CALL XGridColumn::*i_getter )() ) + { + ATTRIBUTE_TYPE value = ATTRIBUTE_TYPE(); + try + { + value = (i_column.get()->*i_getter)(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return value; + } + } + + + //= ColumnChangeMultiplexer + + typedef ::cppu::WeakImplHelper < XGridColumnListener + > ColumnChangeMultiplexer_Base; + class ColumnChangeMultiplexer :public ColumnChangeMultiplexer_Base + { + public: + explicit ColumnChangeMultiplexer( UnoGridColumnFacade& i_colImpl ); + ColumnChangeMultiplexer(const ColumnChangeMultiplexer&) = delete; + ColumnChangeMultiplexer& operator=(const ColumnChangeMultiplexer&) = delete; + + void dispose(); + + protected: + virtual ~ColumnChangeMultiplexer() override; + + // XGridColumnListener + virtual void SAL_CALL columnChanged( const GridColumnEvent& i_event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const EventObject& i_event ) override; + + private: + UnoGridColumnFacade* m_pColumnImplementation; + }; + + + ColumnChangeMultiplexer::ColumnChangeMultiplexer( UnoGridColumnFacade& i_colImpl ) + :m_pColumnImplementation( &i_colImpl ) + { + } + + + ColumnChangeMultiplexer::~ColumnChangeMultiplexer() + { + } + + + void ColumnChangeMultiplexer::dispose() + { + DBG_TESTSOLARMUTEX(); + m_pColumnImplementation = nullptr; + } + + + void SAL_CALL ColumnChangeMultiplexer::columnChanged( const GridColumnEvent& i_event ) + { + if ( i_event.AttributeName == "DataColumnIndex" ) + { + SolarMutexGuard aGuard; + if ( m_pColumnImplementation != nullptr ) + m_pColumnImplementation->dataColumnIndexChanged(); + return; + } + + ColumnAttributeGroup nChangedAttributes( ColumnAttributeGroup::NONE ); + + if ( i_event.AttributeName == "HorizontalAlign" ) + nChangedAttributes |= ColumnAttributeGroup::APPEARANCE; + + if ( i_event.AttributeName == "ColumnWidth" + || i_event.AttributeName == "MaxWidth" + || i_event.AttributeName == "MinWidth" + || i_event.AttributeName == "PreferredWidth" + || i_event.AttributeName == "Resizeable" + || i_event.AttributeName == "Flexibility" + ) + nChangedAttributes |= ColumnAttributeGroup::WIDTH; + + OSL_ENSURE( nChangedAttributes != ColumnAttributeGroup::NONE, + "ColumnChangeMultiplexer::columnChanged: unknown column attributed changed!" ); + + SolarMutexGuard aGuard; + if ( m_pColumnImplementation != nullptr ) + m_pColumnImplementation->columnChanged( nChangedAttributes ); + } + + + void SAL_CALL ColumnChangeMultiplexer::disposing( const EventObject& ) + { + } + + + //= UnoGridColumnFacade + + + UnoGridColumnFacade::UnoGridColumnFacade( UnoControlTableModel const & i_owner, Reference< XGridColumn > const & i_gridColumn ) + :m_pOwner( &i_owner ) + ,m_nDataColumnIndex( -1 ) + ,m_xGridColumn( i_gridColumn, css::uno::UNO_SET_THROW ) + ,m_pChangeMultiplexer( new ColumnChangeMultiplexer( *this ) ) + { + m_xGridColumn->addGridColumnListener( m_pChangeMultiplexer ); + impl_updateDataColumnIndex_nothrow(); + } + + + UnoGridColumnFacade::~UnoGridColumnFacade() + { + } + + + void UnoGridColumnFacade::dispose() + { + DBG_TESTSOLARMUTEX(); + ENSURE_OR_RETURN_VOID( m_pOwner != nullptr, "UnoGridColumnFacade::dispose: already disposed!" ); + + m_xGridColumn->removeGridColumnListener( m_pChangeMultiplexer ); + m_pChangeMultiplexer->dispose(); + m_pChangeMultiplexer.clear(); + m_xGridColumn.clear(); + m_pOwner = nullptr; + } + + + void UnoGridColumnFacade::impl_updateDataColumnIndex_nothrow() + { + m_nDataColumnIndex = -1; + ENSURE_OR_RETURN_VOID( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!" ); + try + { + m_nDataColumnIndex = m_xGridColumn->getDataColumnIndex(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + + void UnoGridColumnFacade::dataColumnIndexChanged() + { + DBG_TESTSOLARMUTEX(); + impl_updateDataColumnIndex_nothrow(); + if ( m_pOwner != nullptr ) + m_pOwner->notifyAllDataChanged(); + } + + + void UnoGridColumnFacade::columnChanged( ColumnAttributeGroup const i_attributeGroup ) + { + DBG_TESTSOLARMUTEX(); + if ( m_pOwner != nullptr ) + m_pOwner->notifyColumnChange( m_pOwner->getColumnPos( *this ), i_attributeGroup ); + } + + + OUString UnoGridColumnFacade::getName() const + { + OUString sName; + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", sName ); + try + { + sName = m_xGridColumn->getTitle(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return sName; + } + + + OUString UnoGridColumnFacade::getHelpText() const + { + OUString sHelpText; + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", sHelpText ); + try + { + sHelpText = m_xGridColumn->getHelpText(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return sHelpText; + } + + + bool UnoGridColumnFacade::isResizable() const + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", false ); + return lcl_get( m_xGridColumn, &XGridColumn::getResizeable ); + } + + + sal_Int32 UnoGridColumnFacade::getFlexibility() const + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", 1 ); + return lcl_get( m_xGridColumn, &XGridColumn::getFlexibility ); + } + + + TableMetrics UnoGridColumnFacade::getWidth() const + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", 0 ); + return lcl_get( m_xGridColumn, &XGridColumn::getColumnWidth ); + } + + + void UnoGridColumnFacade::setWidth( TableMetrics _nWidth ) + { + ENSURE_OR_RETURN_VOID( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!" ); + lcl_set( m_xGridColumn, &XGridColumn::setColumnWidth, _nWidth ); + } + + + TableMetrics UnoGridColumnFacade::getMinWidth() const + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", 0 ); + return lcl_get( m_xGridColumn, &XGridColumn::getMinWidth ); + } + + + TableMetrics UnoGridColumnFacade::getMaxWidth() const + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", 0 ); + return lcl_get( m_xGridColumn, &XGridColumn::getMaxWidth ); + } + + + css::style::HorizontalAlignment UnoGridColumnFacade::getHorizontalAlign() + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", HorizontalAlignment_LEFT ); + return lcl_get( m_xGridColumn, &XGridColumn::getHorizontalAlign ); + } + + +} // svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/unogridcolumnfacade.hxx b/svtools/source/uno/unogridcolumnfacade.hxx new file mode 100644 index 000000000..672397276 --- /dev/null +++ b/svtools/source/uno/unogridcolumnfacade.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 <table/tablemodel.hxx> + +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/style/HorizontalAlignment.hpp> + +#include <rtl/ref.hxx> + + +namespace svt::table +{ + + + //= UnoGridColumnFacade + + class ColumnChangeMultiplexer; + class UnoControlTableModel; + class UnoGridColumnFacade :public IColumnModel + { + public: + UnoGridColumnFacade( + UnoControlTableModel const & i_owner, + css::uno::Reference< css::awt::grid::XGridColumn > const & i_gridColumn + ); + virtual ~UnoGridColumnFacade() override; + UnoGridColumnFacade(const UnoGridColumnFacade&) = delete; + UnoGridColumnFacade& operator=(const UnoGridColumnFacade&) = delete; + + // IColumnModel overridables + virtual OUString getName() const override; + virtual OUString getHelpText() const override; + virtual bool isResizable() const override; + virtual sal_Int32 getFlexibility() const override; + virtual TableMetrics getWidth() const override; + virtual void setWidth( TableMetrics _nWidth ) override; + virtual TableMetrics getMinWidth() const override; + virtual TableMetrics getMaxWidth() const override; + virtual css::style::HorizontalAlignment getHorizontalAlign() override; + + /** disposes the column wrapper + + Note that the XGridColumn which is wrapped by the instance is <strong>not</strong> disposed, as we + do not own it. + */ + void dispose(); + + sal_Int32 + getDataColumnIndex() const { return m_nDataColumnIndex; } + + // callbacks for the XGridColumnListener + void columnChanged( ColumnAttributeGroup const i_attributeGroup ); + void dataColumnIndexChanged(); + + private: + void impl_updateDataColumnIndex_nothrow(); + + private: + UnoControlTableModel const * m_pOwner; + sal_Int32 m_nDataColumnIndex; + css::uno::Reference< css::awt::grid::XGridColumn > m_xGridColumn; + ::rtl::Reference< ColumnChangeMultiplexer > m_pChangeMultiplexer; + }; + + +} // svt::table + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/unoiface.cxx b/svtools/source/uno/unoiface.cxx new file mode 100644 index 000000000..71bc18150 --- /dev/null +++ b/svtools/source/uno/unoiface.cxx @@ -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 . + */ + +#include <unoiface.hxx> +#include "svtxgridcontrol.hxx" +#include <table/tablecontrol.hxx> + +// help function for the toolkit... + +extern "C" { + +SAL_DLLPUBLIC_EXPORT vcl::Window* CreateWindow( rtl::Reference<VCLXWindow>* ppNewComp, const css::awt::WindowDescriptor* pDescriptor, vcl::Window* pParent, WinBits nWinBits ) +{ + vcl::Window* pWindow = nullptr; + OUString aServiceName( pDescriptor->WindowServiceName ); + if ( aServiceName.equalsIgnoreAsciiCase( "Grid" ) ) + { + if ( pParent ) + { + pWindow = VclPtr< ::svt::table::TableControl>::Create(pParent, nWinBits); + *ppNewComp = new SVTXGridControl; + } + else + { + *ppNewComp = nullptr; + return nullptr; + } + } + return pWindow; +} + +} // extern "C" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/unoimap.cxx b/svtools/source/uno/unoimap.cxx new file mode 100644 index 000000000..4d64cc8b4 --- /dev/null +++ b/svtools/source/uno/unoimap.cxx @@ -0,0 +1,720 @@ +/* -*- 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/container/XIndexContainer.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/document/XEventsSupplier.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/awt/Point.hpp> +#include <com/sun/star/drawing/PointSequence.hpp> +#include <comphelper/servicehelper.hxx> +#include <comphelper/propertysethelper.hxx> +#include <comphelper/propertysetinfo.hxx> +#include <cppuhelper/weakagg.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <algorithm> +#include <osl/diagnose.h> +#include <rtl/ref.hxx> +#include <svtools/unoevent.hxx> +#include <svtools/unoimap.hxx> +#include <vcl/imap.hxx> +#include <vcl/imapcirc.hxx> +#include <vcl/imaprect.hxx> +#include <vcl/imappoly.hxx> + +using namespace comphelper; +using namespace cppu; +using namespace com::sun::star; +using namespace css::uno; +using namespace css::lang; +using namespace css::container; +using namespace css::beans; +using namespace css::document; +using namespace css::drawing; + +const sal_Int32 HANDLE_URL = 1; +const sal_Int32 HANDLE_DESCRIPTION = 2; +const sal_Int32 HANDLE_TARGET = 3; +const sal_Int32 HANDLE_NAME = 4; +const sal_Int32 HANDLE_ISACTIVE = 5; +const sal_Int32 HANDLE_POLYGON = 6; +const sal_Int32 HANDLE_CENTER = 7; +const sal_Int32 HANDLE_RADIUS = 8; +const sal_Int32 HANDLE_BOUNDARY = 9; +const sal_Int32 HANDLE_TITLE = 10; + +namespace { + +class SvUnoImageMapObject : public OWeakAggObject, + public XEventsSupplier, + public XServiceInfo, + public PropertySetHelper, + public XTypeProvider, + public XUnoTunnel +{ +public: + SvUnoImageMapObject( IMapObjectType nType, const SvEventDescription* pSupportedMacroItems ); + SvUnoImageMapObject( const IMapObject& rMapObject, const SvEventDescription* pSupportedMacroItems ); + + UNO3_GETIMPLEMENTATION_DECL( SvUnoImageMapObject ) + + std::unique_ptr<IMapObject> createIMapObject() const; + + rtl::Reference<SvMacroTableEventDescriptor> mxEvents; + + // overridden helpers from PropertySetHelper + virtual void _setPropertyValues( const PropertyMapEntry** ppEntries, const Any* pValues ) override; + virtual void _getPropertyValues( const PropertyMapEntry** ppEntries, Any* pValue ) override; + + // XInterface + virtual Any SAL_CALL queryAggregation( const Type & rType ) override; + virtual Any SAL_CALL queryInterface( const Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XTypeProvider + virtual Sequence< Type > SAL_CALL getTypes( ) override; + virtual Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; + + // XEventsSupplier + virtual Reference< css::container::XNameReplace > SAL_CALL getEvents( ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + +private: + static rtl::Reference<PropertySetInfo> createPropertySetInfo( IMapObjectType nType ); + + + IMapObjectType mnType; + + OUString maURL; + OUString maAltText; + OUString maDesc; + OUString maTarget; + OUString maName; + bool mbIsActive; + awt::Rectangle maBoundary; + awt::Point maCenter; + sal_Int32 mnRadius; + PointSequence maPolygon; +}; + +} + +UNO3_GETIMPLEMENTATION_IMPL( SvUnoImageMapObject ); + +rtl::Reference<PropertySetInfo> SvUnoImageMapObject::createPropertySetInfo( IMapObjectType nType ) +{ + switch( nType ) + { + case IMapObjectType::Polygon: + { + static PropertyMapEntry const aPolygonObj_Impl[] = + { + { OUString("URL"), HANDLE_URL, cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("Title"), HANDLE_TITLE, cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("Description"), HANDLE_DESCRIPTION, cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("Target"), HANDLE_TARGET, cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("Name"), HANDLE_NAME, cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("IsActive"), HANDLE_ISACTIVE, cppu::UnoType<bool>::get(), 0, 0 }, + { OUString("Polygon"), HANDLE_POLYGON, cppu::UnoType<PointSequence>::get(), 0, 0 }, + }; + + return rtl::Reference<PropertySetInfo>(new PropertySetInfo( aPolygonObj_Impl )); + } + case IMapObjectType::Circle: + { + static PropertyMapEntry const aCircleObj_Impl[] = + { + { OUString("URL"), HANDLE_URL, cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("Title"), HANDLE_TITLE, cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("Description"), HANDLE_DESCRIPTION, cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("Target"), HANDLE_TARGET, cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("Name"), HANDLE_NAME, cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("IsActive"), HANDLE_ISACTIVE, cppu::UnoType<bool>::get(), 0, 0 }, + { OUString("Center"), HANDLE_CENTER, cppu::UnoType<awt::Point>::get(), 0, 0 }, + { OUString("Radius"), HANDLE_RADIUS, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + }; + + return rtl::Reference<PropertySetInfo>(new PropertySetInfo( aCircleObj_Impl )); + } + case IMapObjectType::Rectangle: + default: + { + static PropertyMapEntry const aRectangleObj_Impl[] = + { + { OUString("URL"), HANDLE_URL, cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("Title"), HANDLE_TITLE, cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("Description"), HANDLE_DESCRIPTION, cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("Target"), HANDLE_TARGET, cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("Name"), HANDLE_NAME, cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("IsActive"), HANDLE_ISACTIVE, cppu::UnoType<bool>::get(), 0, 0 }, + { OUString("Boundary"), HANDLE_BOUNDARY, cppu::UnoType<awt::Rectangle>::get(), 0, 0 }, + }; + + return rtl::Reference<PropertySetInfo>(new PropertySetInfo( aRectangleObj_Impl )); + } + } +} + +SvUnoImageMapObject::SvUnoImageMapObject( IMapObjectType nType, const SvEventDescription* pSupportedMacroItems ) +: PropertySetHelper( createPropertySetInfo( nType ) ), + mnType( nType ) +, mbIsActive( true ) +, mnRadius( 0 ) +{ + mxEvents = new SvMacroTableEventDescriptor( pSupportedMacroItems ); +} + +SvUnoImageMapObject::SvUnoImageMapObject( const IMapObject& rMapObject, const SvEventDescription* pSupportedMacroItems ) +: PropertySetHelper( createPropertySetInfo( rMapObject.GetType() ) ), + mnType( rMapObject.GetType() ) +, mbIsActive( true ) +, mnRadius( 0 ) +{ + maURL = rMapObject.GetURL(); + maAltText = rMapObject.GetAltText(); + maDesc = rMapObject.GetDesc(); + maTarget = rMapObject.GetTarget(); + maName = rMapObject.GetName(); + mbIsActive = rMapObject.IsActive(); + + switch( mnType ) + { + case IMapObjectType::Rectangle: + { + const tools::Rectangle aRect( static_cast<const IMapRectangleObject*>(&rMapObject)->GetRectangle(false) ); + maBoundary.X = aRect.Left(); + maBoundary.Y = aRect.Top(); + maBoundary.Width = aRect.GetWidth(); + maBoundary.Height = aRect.GetHeight(); + } + break; + case IMapObjectType::Circle: + { + mnRadius = static_cast<const IMapCircleObject*>(&rMapObject)->GetRadius(false); + const Point aPoint( static_cast<const IMapCircleObject*>(&rMapObject)->GetCenter(false) ); + + maCenter.X = aPoint.X(); + maCenter.Y = aPoint.Y(); + } + break; + case IMapObjectType::Polygon: + default: + { + const tools::Polygon aPoly( static_cast<const IMapPolygonObject*>(&rMapObject)->GetPolygon(false) ); + + const sal_uInt16 nCount = aPoly.GetSize(); + maPolygon.realloc( nCount ); + awt::Point* pPoints = maPolygon.getArray(); + + for( sal_uInt16 nPoint = 0; nPoint < nCount; nPoint++ ) + { + const Point& rPoint = aPoly.GetPoint( nPoint ); + pPoints->X = rPoint.X(); + pPoints->Y = rPoint.Y(); + + pPoints++; + } + } + } + + mxEvents = new SvMacroTableEventDescriptor( rMapObject.GetMacroTable(), pSupportedMacroItems ); +} + +std::unique_ptr<IMapObject> SvUnoImageMapObject::createIMapObject() const +{ + const OUString aURL( maURL ); + const OUString aAltText( maAltText ); + const OUString aDesc( maDesc ); + const OUString aTarget( maTarget ); + const OUString aName( maName ); + + std::unique_ptr<IMapObject> pNewIMapObject; + + switch( mnType ) + { + case IMapObjectType::Rectangle: + { + const tools::Rectangle aRect( maBoundary.X, maBoundary.Y, maBoundary.X + maBoundary.Width - 1, maBoundary.Y + maBoundary.Height - 1 ); + pNewIMapObject.reset(new IMapRectangleObject( aRect, aURL, aAltText, aDesc, aTarget, aName, mbIsActive, false )); + } + break; + + case IMapObjectType::Circle: + { + const Point aCenter( maCenter.X, maCenter.Y ); + pNewIMapObject.reset(new IMapCircleObject( aCenter, mnRadius, aURL, aAltText, aDesc, aTarget, aName, mbIsActive, false )); + } + break; + + case IMapObjectType::Polygon: + default: + { + const sal_uInt16 nCount = static_cast<sal_uInt16>(maPolygon.getLength()); + + tools::Polygon aPoly( nCount ); + for( sal_uInt16 nPoint = 0; nPoint < nCount; nPoint++ ) + { + Point aPoint( maPolygon[nPoint].X, maPolygon[nPoint].Y ); + aPoly.SetPoint( aPoint, nPoint ); + } + + aPoly.Optimize( PolyOptimizeFlags::CLOSE ); + pNewIMapObject.reset(new IMapPolygonObject( aPoly, aURL, aAltText, aDesc, aTarget, aName, mbIsActive, false )); + } + break; + } + + SvxMacroTableDtor aMacroTable; + mxEvents->copyMacrosIntoTable(aMacroTable); + pNewIMapObject->SetMacroTable( aMacroTable ); + + return pNewIMapObject; +} + +// XInterface + +Any SAL_CALL SvUnoImageMapObject::queryInterface( const Type & rType ) +{ + return OWeakAggObject::queryInterface( rType ); +} + +Any SAL_CALL SvUnoImageMapObject::queryAggregation( const Type & rType ) +{ + Any aAny; + + if( rType == cppu::UnoType<XServiceInfo>::get()) + aAny <<= Reference< XServiceInfo >(this); + else if( rType == cppu::UnoType<XTypeProvider>::get()) + aAny <<= Reference< XTypeProvider >(this); + else if( rType == cppu::UnoType<XPropertySet>::get()) + aAny <<= Reference< XPropertySet >(this); + else if( rType == cppu::UnoType<XEventsSupplier>::get()) + aAny <<= Reference< XEventsSupplier >(this); + else if( rType == cppu::UnoType<XMultiPropertySet>::get()) + aAny <<= Reference< XMultiPropertySet >(this); + else if( rType == cppu::UnoType<XUnoTunnel>::get()) + aAny <<= Reference< XUnoTunnel >(this); + else + aAny = OWeakAggObject::queryAggregation( rType ); + + return aAny; +} + +void SAL_CALL SvUnoImageMapObject::acquire() noexcept +{ + OWeakAggObject::acquire(); +} + +void SAL_CALL SvUnoImageMapObject::release() noexcept +{ + OWeakAggObject::release(); +} + +uno::Sequence< uno::Type > SAL_CALL SvUnoImageMapObject::getTypes() +{ + static const uno::Sequence< uno::Type > aTypes { + cppu::UnoType<XAggregation>::get(), + cppu::UnoType<XEventsSupplier>::get(), + cppu::UnoType<XServiceInfo>::get(), + cppu::UnoType<XPropertySet>::get(), + cppu::UnoType<XMultiPropertySet>::get(), + cppu::UnoType<XTypeProvider>::get(), + cppu::UnoType<XUnoTunnel>::get() }; + return aTypes; +} + +uno::Sequence< sal_Int8 > SAL_CALL SvUnoImageMapObject::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XServiceInfo +sal_Bool SAL_CALL SvUnoImageMapObject::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL SvUnoImageMapObject::getSupportedServiceNames() +{ + Sequence< OUString > aSNS( 2 ); + aSNS.getArray()[0] = "com.sun.star.image.ImageMapObject"; + switch( mnType ) + { + case IMapObjectType::Polygon: + default: + aSNS.getArray()[1] = "com.sun.star.image.ImageMapPolygonObject"; + break; + case IMapObjectType::Rectangle: + aSNS.getArray()[1] = "com.sun.star.image.ImageMapRectangleObject"; + break; + case IMapObjectType::Circle: + aSNS.getArray()[1] = "com.sun.star.image.ImageMapCircleObject"; + break; + } + return aSNS; +} + +OUString SAL_CALL SvUnoImageMapObject::getImplementationName() +{ + switch( mnType ) + { + case IMapObjectType::Polygon: + default: + return "org.openoffice.comp.svt.ImageMapPolygonObject"; + case IMapObjectType::Circle: + return "org.openoffice.comp.svt.ImageMapCircleObject"; + case IMapObjectType::Rectangle: + return "org.openoffice.comp.svt.ImageMapRectangleObject"; + } +} + +// overridden helpers from PropertySetHelper +void SvUnoImageMapObject::_setPropertyValues( const PropertyMapEntry** ppEntries, const Any* pValues ) +{ + bool bOk = false; + + while( *ppEntries ) + { + switch( (*ppEntries)->mnHandle ) + { + case HANDLE_URL: + bOk = *pValues >>= maURL; + break; + case HANDLE_TITLE: + bOk = *pValues >>= maAltText; + break; + case HANDLE_DESCRIPTION: + bOk = *pValues >>= maDesc; + break; + case HANDLE_TARGET: + bOk = *pValues >>= maTarget; + break; + case HANDLE_NAME: + bOk = *pValues >>= maName; + break; + case HANDLE_ISACTIVE: + bOk = *pValues >>= mbIsActive; + break; + case HANDLE_BOUNDARY: + bOk = *pValues >>= maBoundary; + break; + case HANDLE_CENTER: + bOk = *pValues >>= maCenter; + break; + case HANDLE_RADIUS: + bOk = *pValues >>= mnRadius; + break; + case HANDLE_POLYGON: + bOk = *pValues >>= maPolygon; + break; + default: + OSL_FAIL( "SvUnoImageMapObject::_setPropertyValues: unexpected property handle" ); + break; + } + + if( !bOk ) + throw IllegalArgumentException(); + + ppEntries++; + pValues++; + } +} + +void SvUnoImageMapObject::_getPropertyValues( const PropertyMapEntry** ppEntries, Any* pValues ) +{ + while( *ppEntries ) + { + switch( (*ppEntries)->mnHandle ) + { + case HANDLE_URL: + *pValues <<= maURL; + break; + case HANDLE_TITLE: + *pValues <<= maAltText; + break; + case HANDLE_DESCRIPTION: + *pValues <<= maDesc; + break; + case HANDLE_TARGET: + *pValues <<= maTarget; + break; + case HANDLE_NAME: + *pValues <<= maName; + break; + case HANDLE_ISACTIVE: + *pValues <<= mbIsActive; + break; + case HANDLE_BOUNDARY: + *pValues <<= maBoundary; + break; + case HANDLE_CENTER: + *pValues <<= maCenter; + break; + case HANDLE_RADIUS: + *pValues <<= mnRadius; + break; + case HANDLE_POLYGON: + *pValues <<= maPolygon; + break; + default: + OSL_FAIL( "SvUnoImageMapObject::_getPropertyValues: unexpected property handle" ); + break; + } + + ppEntries++; + pValues++; + } +} + + +Reference< XNameReplace > SAL_CALL SvUnoImageMapObject::getEvents() +{ + return mxEvents; +} + +namespace { + +class SvUnoImageMap : public WeakImplHelper< XIndexContainer, XServiceInfo, XUnoTunnel > +{ +public: + explicit SvUnoImageMap(); + SvUnoImageMap( const ImageMap& rMap, const SvEventDescription* pSupportedMacroItems ); + + void fillImageMap( ImageMap& rMap ) const; + /// @throws IllegalArgumentException + static SvUnoImageMapObject* getObject( const Any& aElement ); + + UNO3_GETIMPLEMENTATION_DECL( SvUnoImageMap ) + + // XIndexContainer + virtual void SAL_CALL insertByIndex( sal_Int32 Index, const Any& Element ) override; + virtual void SAL_CALL removeByIndex( sal_Int32 Index ) override; + + // XIndexReplace + virtual void SAL_CALL replaceByIndex( sal_Int32 Index, const Any& Element ) override; + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount( ) override; + virtual Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XElementAccess + virtual Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + +private: + OUString maName; + + std::vector< rtl::Reference<SvUnoImageMapObject> > maObjectList; +}; + +} + +UNO3_GETIMPLEMENTATION_IMPL( SvUnoImageMap ); + +SvUnoImageMap::SvUnoImageMap() +{ +} + +SvUnoImageMap::SvUnoImageMap( const ImageMap& rMap, const SvEventDescription* pSupportedMacroItems ) +{ + maName = rMap.GetName(); + + const std::size_t nCount = rMap.GetIMapObjectCount(); + for( std::size_t nPos = 0; nPos < nCount; nPos++ ) + { + IMapObject* pMapObject = rMap.GetIMapObject( nPos ); + rtl::Reference<SvUnoImageMapObject> xUnoObj = new SvUnoImageMapObject( *pMapObject, pSupportedMacroItems ); + maObjectList.push_back( xUnoObj ); + } +} + +SvUnoImageMapObject* SvUnoImageMap::getObject( const Any& aElement ) +{ + Reference< XInterface > xObject; + aElement >>= xObject; + + SvUnoImageMapObject* pObject = comphelper::getFromUnoTunnel<SvUnoImageMapObject>( xObject ); + if( nullptr == pObject ) + throw IllegalArgumentException(); + + return pObject; +} + +// XIndexContainer +void SAL_CALL SvUnoImageMap::insertByIndex( sal_Int32 nIndex, const Any& Element ) +{ + SvUnoImageMapObject* pObject = getObject( Element ); + const sal_Int32 nCount = maObjectList.size(); + if( nullptr == pObject || nIndex > nCount ) + throw IndexOutOfBoundsException(); + + if( nIndex == nCount ) + maObjectList.emplace_back(pObject ); + else + { + auto aIter = maObjectList.begin(); + std::advance(aIter, nIndex); + maObjectList.insert( aIter, pObject ); + } +} + +void SAL_CALL SvUnoImageMap::removeByIndex( sal_Int32 nIndex ) +{ + const sal_Int32 nCount = maObjectList.size(); + if( nIndex >= nCount ) + throw IndexOutOfBoundsException(); + + if( nCount - 1 == nIndex ) + { + maObjectList.pop_back(); + } + else + { + auto aIter = maObjectList.begin(); + std::advance(aIter, nIndex); + maObjectList.erase( aIter ); + } +} + +// XIndexReplace +void SAL_CALL SvUnoImageMap::replaceByIndex( sal_Int32 nIndex, const Any& Element ) +{ + SvUnoImageMapObject* pObject = getObject( Element ); + const sal_Int32 nCount = maObjectList.size(); + if( nullptr == pObject || nIndex >= nCount ) + throw IndexOutOfBoundsException(); + + auto aIter = maObjectList.begin(); + std::advance(aIter, nIndex); + *aIter = pObject; +} + +// XIndexAccess +sal_Int32 SAL_CALL SvUnoImageMap::getCount( ) +{ + return maObjectList.size(); +} + +Any SAL_CALL SvUnoImageMap::getByIndex( sal_Int32 nIndex ) +{ + const sal_Int32 nCount = maObjectList.size(); + if( nIndex >= nCount ) + throw IndexOutOfBoundsException(); + + auto aIter = maObjectList.begin(); + std::advance(aIter, nIndex); + + Reference< XPropertySet > xObj( *aIter ); + return Any( xObj ); +} + +// XElementAccess +Type SAL_CALL SvUnoImageMap::getElementType( ) +{ + return cppu::UnoType<XPropertySet>::get(); +} + +sal_Bool SAL_CALL SvUnoImageMap::hasElements( ) +{ + return (!maObjectList.empty()); +} + +// XServiceInfo +OUString SAL_CALL SvUnoImageMap::getImplementationName( ) +{ + return "org.openoffice.comp.svt.SvUnoImageMap"; +} + +sal_Bool SAL_CALL SvUnoImageMap::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL SvUnoImageMap::getSupportedServiceNames( ) +{ + return { "com.sun.star.image.ImageMap" }; +} + +void SvUnoImageMap::fillImageMap( ImageMap& rMap ) const +{ + rMap.ClearImageMap(); + + rMap.SetName( maName ); + + for (auto const& elem : maObjectList) + { + std::unique_ptr<IMapObject> pNewMapObject = elem->createIMapObject(); + rMap.InsertIMapObject( std::move(pNewMapObject) ); + } +} + + +// factory helper methods + + +Reference< XInterface > SvUnoImageMapRectangleObject_createInstance( const SvEventDescription* pSupportedMacroItems ) +{ + return static_cast<XWeak*>(new SvUnoImageMapObject( IMapObjectType::Rectangle, pSupportedMacroItems )); +} + +Reference< XInterface > SvUnoImageMapCircleObject_createInstance( const SvEventDescription* pSupportedMacroItems ) +{ + return static_cast<XWeak*>(new SvUnoImageMapObject( IMapObjectType::Circle, pSupportedMacroItems )); +} + +Reference< XInterface > SvUnoImageMapPolygonObject_createInstance( const SvEventDescription* pSupportedMacroItems ) +{ + return static_cast<XWeak*>(new SvUnoImageMapObject( IMapObjectType::Polygon, pSupportedMacroItems )); +} + +Reference< XInterface > SvUnoImageMap_createInstance() +{ + return static_cast<XWeak*>(new SvUnoImageMap); +} + +Reference< XInterface > SvUnoImageMap_createInstance( const ImageMap& rMap, const SvEventDescription* pSupportedMacroItems ) +{ + return static_cast<XWeak*>(new SvUnoImageMap( rMap, pSupportedMacroItems )); +} + +bool SvUnoImageMap_fillImageMap( const Reference< XInterface >& xImageMap, ImageMap& rMap ) +{ + SvUnoImageMap* pUnoImageMap = comphelper::getFromUnoTunnel<SvUnoImageMap>( xImageMap ); + if( nullptr == pUnoImageMap ) + return false; + + pUnoImageMap->fillImageMap( rMap ); + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/wizard/unowizard.cxx b/svtools/source/uno/wizard/unowizard.cxx new file mode 100644 index 000000000..0a73eb49a --- /dev/null +++ b/svtools/source/uno/wizard/unowizard.cxx @@ -0,0 +1,452 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <string_view> + +#include "wizardshell.hxx" + +#include <com/sun/star/container/NoSuchElementException.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/ucb/AlreadyInitializedException.hpp> +#include <com/sun/star/ui/dialogs/XWizard.hpp> +#include <com/sun/star/ui/dialogs/XWizardController.hpp> +#include <com/sun/star/ui/dialogs/WizardButton.hpp> +#include <com/sun/star/util/InvalidStateException.hpp> + +#include <comphelper/proparrhlp.hxx> +#include <cppuhelper/implbase.hxx> +#include <svtools/genericunodialog.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/diagnose_ex.h> +#include <osl/mutex.hxx> +#include <vcl/svapp.hxx> +#include <tools/urlobj.hxx> + +using namespace ::com::sun::star; +using namespace ::svt::uno; + +namespace { + + using css::uno::Reference; + using css::uno::XInterface; + using css::uno::UNO_QUERY; + using css::uno::Any; + using css::uno::Sequence; + using css::ui::dialogs::XWizard; + using css::beans::XPropertySetInfo; + using css::uno::XComponentContext; + using css::beans::Property; + using css::lang::IllegalArgumentException; + using css::ucb::AlreadyInitializedException; + using css::ui::dialogs::XWizardController; + using css::ui::dialogs::XWizardPage; + using css::container::NoSuchElementException; + using css::util::InvalidStateException; + using css::awt::XWindow; + + namespace WizardButton = css::ui::dialogs::WizardButton; + + WizardButtonFlags lcl_convertWizardButtonToWZB( const sal_Int16 i_nWizardButton ) + { + switch ( i_nWizardButton ) + { + case WizardButton::NONE: return WizardButtonFlags::NONE; + case WizardButton::NEXT: return WizardButtonFlags::NEXT; + case WizardButton::PREVIOUS: return WizardButtonFlags::PREVIOUS; + case WizardButton::FINISH: return WizardButtonFlags::FINISH; + case WizardButton::CANCEL: return WizardButtonFlags::CANCEL; + case WizardButton::HELP: return WizardButtonFlags::HELP; + } + OSL_FAIL( "lcl_convertWizardButtonToWZB: invalid WizardButton constant!" ); + return WizardButtonFlags::NONE; + } + + typedef ::cppu::ImplInheritanceHelper < ::svt::OGenericUnoDialog + , ui::dialogs::XWizard + > Wizard_Base; + class Wizard; + typedef ::comphelper::OPropertyArrayUsageHelper< Wizard > Wizard_PBase; + class Wizard : public Wizard_Base + , public Wizard_PBase + { + public: + explicit Wizard( const css::uno::Reference< css::uno::XComponentContext >& i_rContext ); + + // lang::XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // beans::XPropertySet + virtual css::uno::Reference< beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + // ui::dialogs::XWizard + virtual OUString SAL_CALL getHelpURL() override; + virtual void SAL_CALL setHelpURL( const OUString& _helpurl ) override; + virtual css::uno::Reference< awt::XWindow > SAL_CALL getDialogWindow() override; + virtual css::uno::Reference< ui::dialogs::XWizardPage > SAL_CALL getCurrentPage( ) override; + virtual void SAL_CALL enableButton( ::sal_Int16 WizardButton, sal_Bool Enable ) override; + virtual void SAL_CALL setDefaultButton( ::sal_Int16 WizardButton ) override; + virtual sal_Bool SAL_CALL travelNext( ) override; + virtual sal_Bool SAL_CALL travelPrevious( ) override; + virtual void SAL_CALL enablePage( ::sal_Int16 PageID, sal_Bool Enable ) override; + virtual void SAL_CALL updateTravelUI( ) override; + virtual sal_Bool SAL_CALL advanceTo( ::sal_Int16 PageId ) override; + virtual sal_Bool SAL_CALL goBackTo( ::sal_Int16 PageId ) override; + virtual void SAL_CALL activatePath( ::sal_Int16 PathIndex, sal_Bool Final ) override; + + // ui::dialogs::XExecutableDialog + virtual void SAL_CALL setTitle( const OUString& aTitle ) override; + virtual ::sal_Int16 SAL_CALL execute( ) override; + + // lang::XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + protected: + virtual ~Wizard() override; + + protected: + virtual std::unique_ptr<weld::DialogController> createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) override; + + private: + css::uno::Sequence< css::uno::Sequence< sal_Int16 > > m_aWizardSteps; + css::uno::Reference< ui::dialogs::XWizardController > m_xController; + OUString m_sHelpURL; + }; + + Wizard::Wizard( const Reference< XComponentContext >& _rxContext ) + :Wizard_Base( _rxContext ) + { + } + + OUString lcl_getHelpURL( std::string_view sHelpId ) + { + OUStringBuffer aBuffer; + OUString aTmp( + OStringToOUString( sHelpId, RTL_TEXTENCODING_UTF8 ) ); + INetURLObject aHID( aTmp ); + if ( aHID.GetProtocol() == INetProtocol::NotValid ) + aBuffer.append( INET_HID_SCHEME ); + aBuffer.append( aTmp ); + return aBuffer.makeStringAndClear(); + } + + Wizard::~Wizard() + { + if (m_xDialog) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if (m_xDialog) + { + m_sHelpURL = lcl_getHelpURL(m_xDialog->get_help_id()); + destroyDialog(); + } + } + } + + void lcl_checkPaths( const Sequence< Sequence< sal_Int16 > >& i_rPaths, const Reference< XInterface >& i_rContext ) + { + // need at least one path + if ( !i_rPaths.hasElements() ) + throw IllegalArgumentException( OUString(), i_rContext, 2 ); + + // each path must be of length 1, at least + sal_Int32 i = 0; + for ( const Sequence< sal_Int16 >& rPath : i_rPaths ) + { + if ( !rPath.hasElements() ) + throw IllegalArgumentException( OUString(), i_rContext, 2 ); + + // page IDs must be in ascending order + auto pPageId = std::adjacent_find(rPath.begin(), rPath.end(), std::greater_equal<sal_Int16>()); + if (pPageId != rPath.end()) + { + throw IllegalArgumentException( + "Path " + OUString::number(i) + + ": invalid page ID sequence - each page ID must be greater than the previous one.", + i_rContext, 2 ); + } + ++i; + } + + // if we have one path, that's okay + if ( i_rPaths.getLength() == 1 ) + return; + + // if we have multiple paths, they must start with the same page id + const sal_Int16 nFirstPageId = i_rPaths[0][0]; + if (std::any_of(i_rPaths.begin(), i_rPaths.end(), + [nFirstPageId](const Sequence< sal_Int16 >& rPath) { return rPath[0] != nFirstPageId; })) + throw IllegalArgumentException( + "All paths must start with the same page id.", + i_rContext, 2 ); + } + + void SAL_CALL Wizard::initialize( const Sequence< Any >& i_Arguments ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bInitialized ) + throw AlreadyInitializedException( OUString(), *this ); + + if ( i_Arguments.getLength() != 2 ) + throw IllegalArgumentException( OUString(), *this, -1 ); + + // the second argument must be a XWizardController, for each constructor + m_xController.set( i_Arguments[1], UNO_QUERY ); + if ( !m_xController.is() ) + throw IllegalArgumentException( OUString(), *this, 2 ); + + // the first arg is either a single path (short[]), or multiple paths (short[][]) + Sequence< sal_Int16 > aSinglePath; + i_Arguments[0] >>= aSinglePath; + Sequence< Sequence< sal_Int16 > > aMultiplePaths; + i_Arguments[0] >>= aMultiplePaths; + + if ( !aMultiplePaths.hasElements() ) + { + aMultiplePaths = { aSinglePath }; + } + lcl_checkPaths( aMultiplePaths, *this ); + // if we survived this, the paths are valid, and we're done here ... + m_aWizardSteps = aMultiplePaths; + + m_bInitialized = true; + } + + OString lcl_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 ); + } + + std::unique_ptr<weld::DialogController> Wizard::createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) + { + auto xDialog = std::make_unique<WizardShell>(Application::GetFrameWeld(rParent), m_xController, m_aWizardSteps); + xDialog->set_help_id(lcl_getHelpId(m_sHelpURL)); + xDialog->setTitleBase( m_sTitle ); + return xDialog; + } + + OUString SAL_CALL Wizard::getImplementationName() + { + return "com.sun.star.comp.svtools.uno.Wizard"; + } + + Sequence< OUString > SAL_CALL Wizard::getSupportedServiceNames() + { + return { "com.sun.star.ui.dialogs.Wizard" }; + } + + Reference< XPropertySetInfo > SAL_CALL Wizard::getPropertySetInfo() + { + return createPropertySetInfo( getInfoHelper() ); + } + + ::cppu::IPropertyArrayHelper& SAL_CALL Wizard::getInfoHelper() + { + return *getArrayHelper(); + } + + ::cppu::IPropertyArrayHelper* Wizard::createArrayHelper( ) const + { + Sequence< Property > aProps; + describeProperties( aProps ); + return new ::cppu::OPropertyArrayHelper( aProps ); + } + + OUString SAL_CALL Wizard::getHelpURL() + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + if (!m_xDialog) + return m_sHelpURL; + + return lcl_getHelpURL(m_xDialog->get_help_id()); + } + + void SAL_CALL Wizard::setHelpURL( const OUString& i_HelpURL ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + if (!m_xDialog) + m_sHelpURL = i_HelpURL; + else + m_xDialog->set_help_id(lcl_getHelpId(i_HelpURL)); + } + + Reference< XWindow > SAL_CALL Wizard::getDialogWindow() + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + ENSURE_OR_RETURN( m_xDialog, "Wizard::getDialogWindow: illegal call (execution did not start, yet)!", nullptr ); + return m_xDialog->getDialog()->GetXWindow(); + } + + void SAL_CALL Wizard::enableButton( ::sal_Int16 i_WizardButton, sal_Bool i_Enable ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get()); + ENSURE_OR_RETURN_VOID( pWizardImpl, "Wizard::enableButtons: invalid dialog implementation!" ); + + pWizardImpl->enableButtons( lcl_convertWizardButtonToWZB( i_WizardButton ), i_Enable ); + } + + void SAL_CALL Wizard::setDefaultButton( ::sal_Int16 i_WizardButton ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get()); + ENSURE_OR_RETURN_VOID( pWizardImpl, "Wizard::setDefaultButton: invalid dialog implementation!" ); + + pWizardImpl->defaultButton( lcl_convertWizardButtonToWZB( i_WizardButton ) ); + } + + sal_Bool SAL_CALL Wizard::travelNext( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get()); + ENSURE_OR_RETURN_FALSE( pWizardImpl, "Wizard::travelNext: invalid dialog implementation!" ); + + return pWizardImpl->travelNext(); + } + + sal_Bool SAL_CALL Wizard::travelPrevious( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get()); + ENSURE_OR_RETURN_FALSE( pWizardImpl, "Wizard::travelPrevious: invalid dialog implementation!" ); + + return pWizardImpl->travelPrevious(); + } + + void SAL_CALL Wizard::enablePage( ::sal_Int16 i_PageID, sal_Bool i_Enable ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get()); + ENSURE_OR_RETURN_VOID( pWizardImpl, "Wizard::enablePage: invalid dialog implementation!" ); + + if ( !pWizardImpl->knowsPage( i_PageID ) ) + throw NoSuchElementException( OUString(), *this ); + + if ( i_PageID == pWizardImpl->getCurrentPage() ) + throw InvalidStateException( OUString(), *this ); + + pWizardImpl->enablePage( i_PageID, i_Enable ); + } + + void SAL_CALL Wizard::updateTravelUI( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get()); + ENSURE_OR_RETURN_VOID( pWizardImpl, "Wizard::updateTravelUI: invalid dialog implementation!" ); + + pWizardImpl->updateTravelUI(); + } + + sal_Bool SAL_CALL Wizard::advanceTo( ::sal_Int16 i_PageId ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get()); + ENSURE_OR_RETURN_FALSE( pWizardImpl, "Wizard::advanceTo: invalid dialog implementation!" ); + + return pWizardImpl->advanceTo( i_PageId ); + } + + sal_Bool SAL_CALL Wizard::goBackTo( ::sal_Int16 i_PageId ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get()); + ENSURE_OR_RETURN_FALSE( pWizardImpl, "Wizard::goBackTo: invalid dialog implementation!" ); + + return pWizardImpl->goBackTo( i_PageId ); + } + + Reference< XWizardPage > SAL_CALL Wizard::getCurrentPage( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get()); + ENSURE_OR_RETURN( pWizardImpl, "Wizard::getCurrentPage: invalid dialog implementation!", Reference< XWizardPage >() ); + + return pWizardImpl->getCurrentWizardPage(); + } + + void SAL_CALL Wizard::activatePath( ::sal_Int16 i_PathIndex, sal_Bool i_Final ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( ( i_PathIndex < 0 ) || ( i_PathIndex >= m_aWizardSteps.getLength() ) ) + throw NoSuchElementException( OUString(), *this ); + + WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get()); + ENSURE_OR_RETURN_VOID( pWizardImpl, "Wizard::activatePath: invalid dialog implementation!" ); + + pWizardImpl->activatePath( i_PathIndex, i_Final ); + } + + void SAL_CALL Wizard::setTitle( const OUString& i_Title ) + { + // simply disambiguate + Wizard_Base::OGenericUnoDialog::setTitle( i_Title ); + } + + ::sal_Int16 SAL_CALL Wizard::execute( ) + { + return Wizard_Base::OGenericUnoDialog::execute(); + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_svtools_uno_Wizard_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new Wizard(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/wizard/wizardpagecontroller.cxx b/svtools/source/uno/wizard/wizardpagecontroller.cxx new file mode 100644 index 000000000..69b864fbf --- /dev/null +++ b/svtools/source/uno/wizard/wizardpagecontroller.cxx @@ -0,0 +1,130 @@ +/* -*- 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 "wizardpagecontroller.hxx" +#include "wizardshell.hxx" + +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/diagnose_ex.h> + + +namespace svt::uno +{ + + + using css::uno::Reference; + using css::uno::UNO_SET_THROW; + using css::uno::Exception; + using css::ui::dialogs::XWizardController; + using css::awt::XWindow; + + using namespace ::com::sun::star; + + + //= WizardPageController + + + WizardPageController::WizardPageController(weld::Container* pParent, const Reference< XWizardController >& i_rController, + const sal_Int16 i_nPageId ) + :m_xController( i_rController ) + ,m_xWizardPage() + { + ENSURE_OR_THROW( m_xController.is(), "no controller" ); + try + { + // Plug a toplevel SalFrame into the native page which can host our awt widgetry + m_xWizardPage.set(m_xController->createPage(pParent->CreateChildFrame(), i_nPageId), UNO_SET_THROW); + + Reference< XWindow > xPageWindow(m_xWizardPage->getWindow(), UNO_SET_THROW); + xPageWindow->setVisible( true ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + WizardPageController::~WizardPageController() + { + try + { + if ( m_xWizardPage.is() ) + m_xWizardPage->dispose(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + void WizardPageController::initializePage() + { + if ( !m_xWizardPage.is() ) + return; + + try + { + m_xWizardPage->activatePage(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + bool WizardPageController::commitPage( vcl::WizardTypes::CommitPageReason i_eReason ) + { + if ( !m_xWizardPage.is() ) + return true; + + try + { + return m_xWizardPage->commitPage( WizardShell::convertCommitReasonToTravelType( i_eReason ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + + return true; + } + + bool WizardPageController::canAdvance() const + { + if ( !m_xWizardPage.is() ) + return true; + + try + { + return m_xWizardPage->canAdvance(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + + return true; + } + + +} // namespace svt::uno + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/wizard/wizardpagecontroller.hxx b/svtools/source/uno/wizard/wizardpagecontroller.hxx new file mode 100644 index 000000000..68d724591 --- /dev/null +++ b/svtools/source/uno/wizard/wizardpagecontroller.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/wizardmachine.hxx> +#include <com/sun/star/ui/dialogs/XWizardController.hpp> + +namespace svt::uno +{ + + + class WizardShell; + + + //= WizardPageController + + class WizardPageController : public vcl::IWizardPageController + { + public: + WizardPageController( + weld::Container* pParent, + const css::uno::Reference< css::ui::dialogs::XWizardController >& i_rController, + const sal_Int16 i_nPageId + ); + virtual ~WizardPageController(); + + // IWizardPageController overridables + virtual void initializePage() override; + virtual bool commitPage( vcl::WizardTypes::CommitPageReason _eReason ) override; + virtual bool canAdvance() const override; + + const css::uno::Reference< css::ui::dialogs::XWizardPage >& + getWizardPage() const { return m_xWizardPage; } + + private: + const css::uno::Reference< css::ui::dialogs::XWizardController > m_xController; + css::uno::Reference< css::ui::dialogs::XWizardPage > m_xWizardPage; + }; + + +} // namespace svt::uno + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/wizard/wizardshell.cxx b/svtools/source/uno/wizard/wizardshell.cxx new file mode 100644 index 000000000..7c946f0cf --- /dev/null +++ b/svtools/source/uno/wizard/wizardshell.cxx @@ -0,0 +1,257 @@ +/* -*- 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 "wizardshell.hxx" +#include "wizardpagecontroller.hxx" + +#include <tools/diagnose_ex.h> + +#include <com/sun/star/ui/dialogs/WizardTravelType.hpp> + +using vcl::RoadmapWizardTypes::WizardPath; + +namespace svt::uno +{ + + + using css::uno::Reference; + using css::uno::Exception; + using css::uno::Sequence; + using css::ui::dialogs::XWizardController; + using css::ui::dialogs::XWizardPage; + + namespace WizardTravelType = css::ui::dialogs::WizardTravelType; + + + namespace + { + + sal_Int16 lcl_determineFirstPageID( const Sequence< Sequence< sal_Int16 > >& i_rPaths ) + { + ENSURE_OR_THROW( i_rPaths.hasElements() && i_rPaths[0].hasElements(), "illegal paths" ); + return i_rPaths[0][0]; + } + } + + //= WizardShell + WizardShell::WizardShell(weld::Window* i_pParent, const Reference< XWizardController >& i_rController, + const Sequence< Sequence< sal_Int16 > >& i_rPaths) + :WizardShell_Base( i_pParent ) + ,m_xController( i_rController ) + ,m_nFirstPageID( lcl_determineFirstPageID( i_rPaths ) ) + { + ENSURE_OR_THROW( m_xController.is(), "invalid controller" ); + + // declare the paths + for ( sal_Int32 i=0; i<i_rPaths.getLength(); ++i ) + { + const Sequence< sal_Int16 >& rPath( i_rPaths[i] ); + WizardPath aPath( rPath.getLength() ); + std::transform(rPath.begin(), rPath.end(), aPath.begin(), + [this](const sal_Int16 nPageId) -> WizardPath::value_type { return impl_pageIdToState(nPageId); }); + declarePath( i, aPath ); + } + + // create the first page, to know the page size + GetOrCreatePage( impl_pageIdToState( i_rPaths[0][0] ) ); + m_xAssistant->set_current_page(0); + + // some defaults + enableAutomaticNextButtonState(); + } + + short WizardShell::run() + { + ActivatePage(); + return WizardShell_Base::run(); + } + + sal_Int16 WizardShell::convertCommitReasonToTravelType( const CommitPageReason i_eReason ) + { + switch ( i_eReason ) + { + case vcl::WizardTypes::eTravelForward: + return WizardTravelType::FORWARD; + + case vcl::WizardTypes::eTravelBackward: + return WizardTravelType::BACKWARD; + + case vcl::WizardTypes::eFinish: + return WizardTravelType::FINISH; + + default: + break; + } + OSL_FAIL( "WizardShell::convertCommitReasonToTravelType: unsupported CommitPageReason!" ); + return WizardTravelType::FINISH; + } + + + void WizardShell::enterState( WizardState i_nState ) + { + WizardShell_Base::enterState( i_nState ); + + if ( !m_xController.is() ) + return; + + try + { + m_xController->onActivatePage( impl_stateToPageId( i_nState ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + + bool WizardShell::leaveState( WizardState i_nState ) + { + if ( !WizardShell_Base::leaveState( i_nState ) ) + return false; + + if ( !m_xController.is() ) + return true; + + try + { + m_xController->onDeactivatePage( impl_stateToPageId( i_nState ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + + return true; + } + + + PWizardPageController WizardShell::impl_getController(BuilderPage* i_pPage) const + { + Page2ControllerMap::const_iterator pos = m_aPageControllers.find( i_pPage ); + ENSURE_OR_RETURN( pos != m_aPageControllers.end(), "WizardShell::impl_getController: no controller for this page!", PWizardPageController() ); + return pos->second; + } + + + Reference< XWizardPage > WizardShell::getCurrentWizardPage() const + { + const WizardState eState = getCurrentState(); + + PWizardPageController pController( impl_getController( GetPage( eState ) ) ); + ENSURE_OR_RETURN( pController, "WizardShell::getCurrentWizardPage: invalid page/controller!", nullptr ); + + return pController->getWizardPage(); + } + + void WizardShell::enablePage( const sal_Int16 i_nPageID, const bool i_bEnable ) + { + enableState( impl_pageIdToState( i_nPageID ), i_bEnable ); + } + + namespace + { + class EmptyPage : public BuilderPage + { + public: + EmptyPage(weld::Widget* pParent, weld::DialogController* pController) + : BuilderPage(pParent, pController, "svt/ui/emptypage.ui", "EmptyPage") + { + m_xContainer->set_size_request(m_xContainer->get_approximate_digit_width() * 70, + m_xContainer->get_text_height() * 10); + } + weld::Container* GetContainer() const { return m_xContainer.get(); } + }; + } + + std::unique_ptr<BuilderPage> WizardShell::createPage( WizardState i_nState ) + { + ENSURE_OR_RETURN( m_xController.is(), "WizardShell::createPage: no WizardController!", nullptr ); + + sal_Int16 nPageId = impl_stateToPageId(i_nState); + + OString sIdent(OString::number(nPageId)); + weld::Container* pPageContainer = m_xAssistant->append_page(sIdent); + + auto xPage = std::make_unique<EmptyPage>(pPageContainer, this); + auto pController = std::make_shared<WizardPageController>(xPage->GetContainer(), m_xController, nPageId); + + m_aPageControllers[xPage.get()] = pController; + + return xPage; + } + + vcl::IWizardPageController* WizardShell::getPageController(BuilderPage* i_pCurrentPage) const + { + return impl_getController( i_pCurrentPage ).get(); + } + + OUString WizardShell::getStateDisplayName( WizardState i_nState ) const + { + try + { + if ( m_xController.is() ) + return m_xController->getPageTitle( impl_stateToPageId( i_nState ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + // fallback for ill-behaved clients: the numeric state + return OUString::number(i_nState); + } + + + bool WizardShell::canAdvance() const + { + try + { + if ( m_xController.is() && !m_xController->canAdvance() ) + return false; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + + return WizardShell_Base::canAdvance(); + } + + + bool WizardShell::onFinish() + { + try + { + if ( m_xController.is() && !m_xController->confirmFinish() ) + return false; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + + return WizardShell_Base::onFinish(); + } + + +} // namespace svt::uno + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/uno/wizard/wizardshell.hxx b/svtools/source/uno/wizard/wizardshell.hxx new file mode 100644 index 000000000..a563ab954 --- /dev/null +++ b/svtools/source/uno/wizard/wizardshell.hxx @@ -0,0 +1,129 @@ +/* -*- 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/ui/dialogs/XWizardController.hpp> +#include <vcl/roadmapwizard.hxx> +#include <map> +#include <memory> + +using vcl::WizardTypes::WizardState; +using vcl::WizardTypes::CommitPageReason; + +namespace svt::uno +{ + + + class WizardPageController; + typedef std::shared_ptr< WizardPageController > PWizardPageController; + + + //= WizardShell + + typedef ::vcl::RoadmapWizardMachine WizardShell_Base; + class WizardShell : public WizardShell_Base + { + public: + WizardShell( + weld::Window* pParent, + const css::uno::Reference< css::ui::dialogs::XWizardController >& i_rController, + const css::uno::Sequence< css::uno::Sequence< sal_Int16 > >& i_rPaths + ); + + // Dialog overridables + virtual short run() override; + + // OWizardMachine overridables + virtual std::unique_ptr<BuilderPage> createPage( WizardState i_nState ) override; + virtual void enterState( WizardState i_nState ) override; + virtual bool leaveState( WizardState i_nState ) override; + virtual OUString getStateDisplayName( WizardState i_nState ) const override; + virtual bool canAdvance() const override; + virtual bool onFinish() override; + virtual vcl::IWizardPageController* getPageController(BuilderPage* pCurrentPage) const override; + + static sal_Int16 convertCommitReasonToTravelType( const CommitPageReason i_eReason ); + + // operations + bool advanceTo( const sal_Int16 i_nPageId ) + { + return skipUntil( impl_pageIdToState( i_nPageId ) ); + } + bool goBackTo( const sal_Int16 i_nPageId ) + { + return skipBackwardUntil( impl_pageIdToState( i_nPageId ) ); + } + using WizardShell_Base::travelNext; + using WizardShell_Base::travelPrevious; + + void activatePath( const sal_Int16 i_nPathID, const bool i_bFinal ) + { + WizardShell_Base::activatePath( vcl::RoadmapWizardTypes::PathId( i_nPathID ), i_bFinal ); + } + + css::uno::Reference< css::ui::dialogs::XWizardPage > + getCurrentWizardPage() const; + + sal_Int16 getCurrentPage() const + { + return impl_stateToPageId( getCurrentState() ); + } + + void enablePage( const sal_Int16 i_PageID, const bool i_Enable ); + + bool knowsPage( const sal_Int16 i_nPageID ) const + { + return knowsState( impl_pageIdToState( i_nPageID ) ); + } + + private: + sal_Int16 impl_stateToPageId( const WizardState i_nState ) const + { + return static_cast< sal_Int16 >( i_nState + m_nFirstPageID ); + } + + WizardState impl_pageIdToState( const sal_Int16 i_nPageId ) const + { + return static_cast<WizardState>(i_nPageId - m_nFirstPageID); + } + + PWizardPageController impl_getController(BuilderPage* i_pPage) const; + + // prevent outside access to some base class members + using WizardShell_Base::skip; + using WizardShell_Base::skipUntil; + using WizardShell_Base::skipBackwardUntil; + using WizardShell_Base::getCurrentState; + using WizardShell_Base::activatePath; + + private: + typedef std::map<BuilderPage*, PWizardPageController> Page2ControllerMap; + + const css::uno::Reference< css::ui::dialogs::XWizardController > m_xController; + const sal_Int16 m_nFirstPageID; + Page2ControllerMap m_aPageControllers; + }; + + +} // namespace svt::uno + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |