diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /framework/source/uielement | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
45 files changed, 17912 insertions, 0 deletions
diff --git a/framework/source/uielement/FixedImageToolbarController.cxx b/framework/source/uielement/FixedImageToolbarController.cxx new file mode 100644 index 000000000..303913432 --- /dev/null +++ b/framework/source/uielement/FixedImageToolbarController.cxx @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <uielement/FixedImageToolbarController.hxx> + +#include <vcl/graph.hxx> +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> +#include <vcl/InterimItemWindow.hxx> +#include <svtools/miscopt.hxx> +#include <svtools/imgdef.hxx> +#include <framework/addonsoptions.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::util; + +namespace framework +{ +class FixedImageControl final : public InterimItemWindow +{ +public: + FixedImageControl(vcl::Window* pParent, const OUString& rCommand); + virtual ~FixedImageControl() override; + virtual void dispose() override; + DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool); + +private: + std::unique_ptr<weld::Image> m_xWidget; +}; + +FixedImageControl::FixedImageControl(vcl::Window* pParent, const OUString& rCommand) + : InterimItemWindow(pParent, "svt/ui/fixedimagecontrol.ui", "FixedImageControl") + , m_xWidget(m_xBuilder->weld_image("image")) +{ + InitControlBase(m_xWidget.get()); + + m_xWidget->connect_key_press(LINK(this, FixedImageControl, KeyInputHdl)); + + bool bBigImages(SvtMiscOptions().AreCurrentSymbolsLarge()); + auto xImage + = Graphic(AddonsOptions().GetImageFromURL(rCommand, bBigImages, true)).GetXGraphic(); + m_xWidget->set_image(xImage); + + SetSizePixel(get_preferred_size()); +} + +IMPL_LINK(FixedImageControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +FixedImageControl::~FixedImageControl() { disposeOnce(); } + +void FixedImageControl::dispose() +{ + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +FixedImageToolbarController::FixedImageToolbarController( + const Reference<XComponentContext>& rxContext, const Reference<XFrame>& rFrame, + ToolBox* pToolbar, ToolBoxItemId nID, const OUString& rCommand) + : ComplexToolbarController(rxContext, rFrame, pToolbar, nID, rCommand) + , m_eSymbolSize(SvtMiscOptions().GetCurrentSymbolsSize()) +{ + m_pFixedImageControl = VclPtr<FixedImageControl>::Create(m_xToolbar, rCommand); + m_xToolbar->SetItemWindow(m_nID, m_pFixedImageControl); + + SvtMiscOptions().AddListenerLink(LINK(this, FixedImageToolbarController, MiscOptionsChanged)); +} + +void SAL_CALL FixedImageToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + SvtMiscOptions().RemoveListenerLink( + LINK(this, FixedImageToolbarController, MiscOptionsChanged)); + m_xToolbar->SetItemWindow(m_nID, nullptr); + m_pFixedImageControl.disposeAndClear(); + ComplexToolbarController::dispose(); +} + +void FixedImageToolbarController::executeControlCommand(const css::frame::ControlCommand&) {} + +void FixedImageToolbarController::CheckAndUpdateImages() +{ + SolarMutexGuard aSolarMutexGuard; + + SvtMiscOptions aMiscOptions; + const sal_Int16 eNewSymbolSize = aMiscOptions.GetCurrentSymbolsSize(); + + if (m_eSymbolSize == eNewSymbolSize) + return; + + m_eSymbolSize = eNewSymbolSize; + + // Refresh images if requested + ::Size aSize(16, 16); + if (m_eSymbolSize == SFX_SYMBOLS_SIZE_LARGE) + { + aSize.setWidth(26); + aSize.setHeight(26); + } + else if (m_eSymbolSize == SFX_SYMBOLS_SIZE_32) + { + aSize.setWidth(32); + aSize.setHeight(32); + } + m_pFixedImageControl->SetSizePixel(aSize); +} + +IMPL_LINK_NOARG(FixedImageToolbarController, MiscOptionsChanged, LinkParamNone*, void) +{ + CheckAndUpdateImages(); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/FixedTextToolbarController.cxx b/framework/source/uielement/FixedTextToolbarController.cxx new file mode 100644 index 000000000..02bfd4c5d --- /dev/null +++ b/framework/source/uielement/FixedTextToolbarController.cxx @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <uielement/FixedTextToolbarController.hxx> + +#include <comphelper/propertyvalue.hxx> +#include <vcl/toolbox.hxx> +#include <vcl/InterimItemWindow.hxx> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::util; + +namespace framework +{ +class FixedTextControl final : public InterimItemWindow +{ +public: + FixedTextControl(vcl::Window* pParent); + virtual ~FixedTextControl() override; + virtual void dispose() override; + OUString get_label() const { return m_xWidget->get_label(); } + void set_label(const OUString& rLabel) { return m_xWidget->set_label(rLabel); } + DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool); + +private: + std::unique_ptr<weld::Label> m_xWidget; +}; + +FixedTextControl::FixedTextControl(vcl::Window* pParent) + : InterimItemWindow(pParent, "svt/ui/fixedtextcontrol.ui", "FixedTextControl") + , m_xWidget(m_xBuilder->weld_label("label")) +{ + InitControlBase(m_xWidget.get()); + + m_xWidget->connect_key_press(LINK(this, FixedTextControl, KeyInputHdl)); +} + +IMPL_LINK(FixedTextControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +FixedTextControl::~FixedTextControl() { disposeOnce(); } + +void FixedTextControl::dispose() +{ + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +FixedTextToolbarController::FixedTextToolbarController( + const Reference<XComponentContext>& rxContext, const Reference<XFrame>& rFrame, + ToolBox* pToolbar, ToolBoxItemId nID, const OUString& aCommand) + : ComplexToolbarController(rxContext, rFrame, pToolbar, nID, aCommand) +{ + m_pFixedTextControl = VclPtr<FixedTextControl>::Create(m_xToolbar); + m_xToolbar->SetItemWindow(m_nID, m_pFixedTextControl); + m_xToolbar->SetItemBits(m_nID, ToolBoxItemBits::AUTOSIZE | m_xToolbar->GetItemBits(m_nID)); +} + +void SAL_CALL FixedTextToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + m_xToolbar->SetItemWindow(m_nID, nullptr); + m_pFixedTextControl.disposeAndClear(); + ComplexToolbarController::dispose(); +} + +Sequence<PropertyValue> FixedTextToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const +{ + const OUString aSelectedText = m_pFixedTextControl->get_label(); + + // Add key modifier to argument list + Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier), + comphelper::makePropertyValue("Text", aSelectedText) }; + return aArgs; +} + +void FixedTextToolbarController::executeControlCommand( + const css::frame::ControlCommand& rControlCommand) +{ + SolarMutexGuard aSolarMutexGuard; + + if (rControlCommand.Command != "SetText") + return; + + for (const NamedValue& rArg : rControlCommand.Arguments) + { + if (rArg.Name == "Text") + { + OUString aText; + rArg.Value >>= aText; + m_pFixedTextControl->set_label(aText); + m_pFixedTextControl->SetSizePixel(m_pFixedTextControl->get_preferred_size()); + + // send notification + notifyTextChanged(aText); + break; + } + } +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/addonstoolbarwrapper.cxx b/framework/source/uielement/addonstoolbarwrapper.cxx new file mode 100644 index 000000000..6547d1c6b --- /dev/null +++ b/framework/source/uielement/addonstoolbarwrapper.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 <uielement/addonstoolbarwrapper.hxx> +#include <uielement/toolbarmanager.hxx> + +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/ui/UIElementType.hpp> + +#include <toolkit/helper/vclunohelper.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> + +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::frame; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace com::sun::star::awt; +using namespace ::com::sun::star::ui; + +namespace framework +{ + +AddonsToolBarWrapper::AddonsToolBarWrapper( const Reference< XComponentContext >& xContext ) : + UIElementWrapperBase( UIElementType::TOOLBAR ), + m_xContext( xContext ), + m_bCreatedImages( false ) +{ +} + +AddonsToolBarWrapper::~AddonsToolBarWrapper() +{ +} + +// XComponent +void SAL_CALL AddonsToolBarWrapper::dispose() +{ + Reference< XComponent > xThis(this); + + css::lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + SolarMutexGuard g; + + if ( m_xToolBarManager.is() ) + m_xToolBarManager->dispose(); + m_xToolBarManager.clear(); + + m_bDisposed = true; +} + +// XInitialization +void SAL_CALL AddonsToolBarWrapper::initialize( const Sequence< Any >& aArguments ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_bInitialized ) + return; + + UIElementWrapperBase::initialize( aArguments ); + + for ( const Any& rArg : aArguments ) + { + PropertyValue aPropValue; + if ( rArg >>= aPropValue ) + { + if ( aPropValue.Name == "ConfigurationData" ) + aPropValue.Value >>= m_aConfigData; + } + } + + Reference< XFrame > xFrame( m_xWeakFrame ); + if ( !(xFrame.is() && m_aConfigData.hasElements()) ) + return; + + // Create VCL based toolbar which will be filled with settings data + VclPtr<ToolBox> pToolBar; + rtl::Reference<ToolBarManager> pToolBarManager; + { + SolarMutexGuard aSolarMutexGuard; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xFrame->getContainerWindow() ); + if ( pWindow ) + { + sal_uLong nStyles = WB_BORDER | WB_SCROLL | WB_MOVEABLE | WB_3DLOOK | WB_DOCKABLE | WB_SIZEABLE | WB_CLOSEABLE; + + pToolBar = VclPtr<ToolBox>::Create( pWindow, nStyles ); + pToolBar->SetLineSpacing(true); + pToolBarManager = new ToolBarManager( m_xContext, xFrame, m_aResourceURL, pToolBar ); + m_xToolBarManager = pToolBarManager; + } + } + + try + { + if ( m_aConfigData.hasElements() && pToolBar && pToolBarManager ) + { + // Fill toolbar with container contents + pToolBarManager->FillAddonToolbar( m_aConfigData ); + pToolBar->EnableCustomize(); + ::Size aActSize( pToolBar->GetSizePixel() ); + ::Size aSize( pToolBar->CalcWindowSizePixel() ); + aSize.setWidth( aActSize.Width() ); + pToolBar->SetSizePixel( aSize ); + } + } + catch ( const NoSuchElementException& ) + { + } +} + +// XUIElement interface +Reference< XInterface > SAL_CALL AddonsToolBarWrapper::getRealInterface() +{ + SolarMutexGuard g; + + if ( m_xToolBarManager.is() ) + { + ToolBarManager* pToolBarManager = static_cast< ToolBarManager *>( m_xToolBarManager.get() ); + if ( pToolBarManager ) + { + vcl::Window* pWindow = pToolBarManager->GetToolBar(); + return Reference< XInterface >( VCLUnoHelper::GetInterface( pWindow ), UNO_QUERY ); + } + } + + return Reference< XInterface >(); +} + +// allow late population of images for add-on toolbars +void AddonsToolBarWrapper::populateImages() +{ + SolarMutexGuard g; + + if (m_bCreatedImages) + return; + + if ( m_xToolBarManager.is() ) + { + ToolBarManager* pToolBarManager = static_cast< ToolBarManager *>( m_xToolBarManager.get() ); + if (pToolBarManager) + { + pToolBarManager->RequestImages(); + m_bCreatedImages = true; + } + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/buttontoolbarcontroller.cxx b/framework/source/uielement/buttontoolbarcontroller.cxx new file mode 100644 index 000000000..4972b6da7 --- /dev/null +++ b/framework/source/uielement/buttontoolbarcontroller.cxx @@ -0,0 +1,280 @@ +/* -*- 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 <uielement/buttontoolbarcontroller.hxx> + +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include <cppuhelper/queryinterface.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> + +using namespace ::com::sun::star; +using namespace css::awt; +using namespace css::uno; +using namespace css::beans; +using namespace css::lang; +using namespace css::frame; +using namespace css::util; + +namespace framework +{ + +ButtonToolbarController::ButtonToolbarController( + uno::Reference< uno::XComponentContext > xContext, + ToolBox* pToolBar, + OUString aCommand ) : + m_bInitialized( false ), + m_bDisposed( false ), + m_aCommandURL(std::move( aCommand )), + m_xContext(std::move( xContext )), + m_pToolbar( pToolBar ) +{ +} + +ButtonToolbarController::~ButtonToolbarController() +{ +} + + // XInterface +uno::Any SAL_CALL ButtonToolbarController::queryInterface( const uno::Type& rType ) +{ + Any a = ::cppu::queryInterface( + rType , + static_cast< frame::XStatusListener* >( this ), + static_cast< frame::XToolbarController* >( this ), + static_cast< lang::XInitialization* >( this ), + static_cast< lang::XComponent* >( this ), + static_cast< util::XUpdatable* >( this )); + + if ( a.hasValue() ) + return a; + + return cppu::OWeakObject::queryInterface( rType ); +} + +void SAL_CALL ButtonToolbarController::acquire() noexcept +{ + cppu::OWeakObject::acquire(); +} + +void SAL_CALL ButtonToolbarController::release() noexcept +{ + cppu::OWeakObject::release(); +} + +// XInitialization +void SAL_CALL ButtonToolbarController::initialize( + const css::uno::Sequence< css::uno::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 css::uno::Any& rArg : aArguments ) + { + if ( rArg >>= 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> xServiceManager(aPropValue.Value,UNO_QUERY); + m_xContext = comphelper::getComponentContext(xServiceManager); + } + } + } +} + +// XComponent +void SAL_CALL ButtonToolbarController::dispose() +{ + Reference< XComponent > xThis = this; + + { + SolarMutexGuard aSolarMutexGuard; + if ( m_bDisposed ) + throw DisposedException(); + + m_xContext.clear(); + m_xURLTransformer.clear(); + m_xFrame.clear(); + m_pToolbar.clear(); + m_bDisposed = true; + } +} + +void SAL_CALL ButtonToolbarController::addEventListener( + const css::uno::Reference< css::lang::XEventListener >& ) +{ + // do nothing +} + +void SAL_CALL ButtonToolbarController::removeEventListener( + const css::uno::Reference< css::lang::XEventListener >& ) +{ + // do nothing +} + +// XUpdatable +void SAL_CALL ButtonToolbarController::update() +{ + SolarMutexGuard aSolarMutexGuard; + if ( m_bDisposed ) + throw DisposedException(); +} + +// XEventListener +void SAL_CALL ButtonToolbarController::disposing( + const css::lang::EventObject& Source ) +{ + uno::Reference< uno::XInterface > xSource( Source.Source ); + + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + return; + + uno::Reference< uno::XInterface > xIfac( m_xFrame, uno::UNO_QUERY ); + if ( xIfac == xSource ) + m_xFrame.clear(); +} + +void SAL_CALL ButtonToolbarController::statusChanged( const css::frame::FeatureStateEvent& ) +{ + // do nothing + if ( m_bDisposed ) + throw DisposedException(); +} + +// XToolbarController +void SAL_CALL ButtonToolbarController::execute( sal_Int16 KeyModifier ) +{ + uno::Reference< frame::XDispatch > xDispatch; + uno::Reference< frame::XFrame > xFrame; + uno::Reference< util::XURLTransformer > xURLTransformer; + OUString aCommandURL; + css::util::URL aTargetURL; + + { + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_bInitialized && + m_xFrame.is() && + m_xContext.is() && + !m_aCommandURL.isEmpty() ) + { + if ( !m_xURLTransformer.is() ) + { + m_xURLTransformer = util::URLTransformer::create( m_xContext ); + } + + xFrame = m_xFrame; + aCommandURL = m_aCommandURL; + xURLTransformer = m_xURLTransformer; + } + } + + uno::Reference< frame::XDispatchProvider > xDispatchProvider( xFrame, uno::UNO_QUERY ); + if ( xDispatchProvider.is() ) + { + aTargetURL.Complete = aCommandURL; + xURLTransformer->parseStrict( aTargetURL ); + xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + } + + if ( !xDispatch.is() ) + return; + + try + { + // Provide key modifier information to dispatch function + Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier) }; + + xDispatch->dispatch( aTargetURL, aArgs ); + } + catch ( const DisposedException& ) + { + } +} + +void SAL_CALL ButtonToolbarController::click() +{ + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + sal_Int16 nKeyModifier( static_cast<sal_Int16>(m_pToolbar->GetModifier()) ); + execute( nKeyModifier ); +} + +void SAL_CALL ButtonToolbarController::doubleClick() +{ + // do nothing + if ( m_bDisposed ) + throw DisposedException(); +} + +uno::Reference< awt::XWindow > SAL_CALL ButtonToolbarController::createPopupWindow() +{ + if ( m_bDisposed ) + throw DisposedException(); + + return uno::Reference< awt::XWindow >(); +} + +uno::Reference< awt::XWindow > SAL_CALL ButtonToolbarController::createItemWindow( + const css::uno::Reference< css::awt::XWindow >& ) +{ + if ( m_bDisposed ) + throw DisposedException(); + + return uno::Reference< awt::XWindow >(); +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/comboboxtoolbarcontroller.cxx b/framework/source/uielement/comboboxtoolbarcontroller.cxx new file mode 100644 index 000000000..77d6e3e8c --- /dev/null +++ b/framework/source/uielement/comboboxtoolbarcontroller.cxx @@ -0,0 +1,333 @@ +/* -*- 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 <uielement/comboboxtoolbarcontroller.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <vcl/InterimItemWindow.hxx> +#include <svtools/toolboxcontroller.hxx> +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> + +using namespace ::com::sun::star; +using namespace css::uno; +using namespace css::beans; +using namespace css::lang; +using namespace css::frame; +using namespace css::util; + +namespace framework +{ + +// Wrapper class to notify controller about events from combobox. +// Unfortunately the events are notified through virtual methods instead +// of Listeners. + +class ComboBoxControl final : public InterimItemWindow +{ +public: + ComboBoxControl(vcl::Window* pParent, ComboboxToolbarController* pComboboxToolbarController); + virtual ~ComboBoxControl() override; + virtual void dispose() override; + + void set_active_or_entry_text(const OUString& rText); + OUString get_active_text() const { return m_xWidget->get_active_text(); } + + void clear() { m_xWidget->clear(); } + void remove(int nIndex) { m_xWidget->remove(nIndex); } + void append_text(const OUString& rStr) { m_xWidget->append_text(rStr); } + void insert_text(int pos, const OUString& rStr) { m_xWidget->insert_text(pos, rStr); } + int get_count() const { return m_xWidget->get_count(); } + int find_text(const OUString& rStr) const { return m_xWidget->find_text(rStr); } + + DECL_LINK(FocusInHdl, weld::Widget&, void); + DECL_LINK(FocusOutHdl, weld::Widget&, void); + DECL_LINK(ModifyHdl, weld::ComboBox&, void); + DECL_LINK(ActivateHdl, weld::ComboBox&, bool); + DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool); + +private: + std::unique_ptr<weld::ComboBox> m_xWidget; + ComboboxToolbarController* m_pComboboxToolbarController; +}; + +ComboBoxControl::ComboBoxControl(vcl::Window* pParent, ComboboxToolbarController* pComboboxToolbarController) + : InterimItemWindow(pParent, "svt/ui/combocontrol.ui", "ComboControl") + , m_xWidget(m_xBuilder->weld_combo_box("combobox")) + , m_pComboboxToolbarController(pComboboxToolbarController) +{ + InitControlBase(m_xWidget.get()); + + m_xWidget->connect_focus_in(LINK(this, ComboBoxControl, FocusInHdl)); + m_xWidget->connect_focus_out(LINK(this, ComboBoxControl, FocusOutHdl)); + m_xWidget->connect_changed(LINK(this, ComboBoxControl, ModifyHdl)); + m_xWidget->connect_entry_activate(LINK(this, ComboBoxControl, ActivateHdl)); + m_xWidget->connect_key_press(LINK(this, ComboBoxControl, KeyInputHdl)); + + m_xWidget->set_entry_width_chars(1); // so a smaller than default width can be used by ComboboxToolbarController + SetSizePixel(get_preferred_size()); +} + +IMPL_LINK(ComboBoxControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +void ComboBoxControl::set_active_or_entry_text(const OUString& rText) +{ + const int nFound = m_xWidget->find_text(rText); + if (nFound != -1) + m_xWidget->set_active(nFound); + else + m_xWidget->set_entry_text(rText); +} + +ComboBoxControl::~ComboBoxControl() +{ + disposeOnce(); +} + +void ComboBoxControl::dispose() +{ + m_pComboboxToolbarController = nullptr; + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +IMPL_LINK_NOARG(ComboBoxControl, ModifyHdl, weld::ComboBox&, void) +{ + if (m_pComboboxToolbarController) + { + if (m_xWidget->get_count() && m_xWidget->changed_by_direct_pick()) + m_pComboboxToolbarController->Select(); + else + m_pComboboxToolbarController->Modify(); + } +} + +IMPL_LINK_NOARG(ComboBoxControl, FocusInHdl, weld::Widget&, void) +{ + if (m_pComboboxToolbarController) + m_pComboboxToolbarController->GetFocus(); +} + +IMPL_LINK_NOARG(ComboBoxControl, FocusOutHdl, weld::Widget&, void) +{ + if (m_pComboboxToolbarController) + m_pComboboxToolbarController->LoseFocus(); +} + +IMPL_LINK_NOARG(ComboBoxControl, ActivateHdl, weld::ComboBox&, bool) +{ + if (m_pComboboxToolbarController) + m_pComboboxToolbarController->Activate(); + return true; +} + +ComboboxToolbarController::ComboboxToolbarController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + ToolBox* pToolbar, + ToolBoxItemId nID, + sal_Int32 nWidth, + const OUString& aCommand ) : + ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand ) + , m_pComboBox( nullptr ) +{ + m_pComboBox = VclPtr<ComboBoxControl>::Create(m_xToolbar, this); + if ( nWidth == 0 ) + nWidth = 100; + + // ComboBoxControl ctor has set a suitable height already + auto nHeight = m_pComboBox->GetSizePixel().Height(); + + m_pComboBox->SetSizePixel( ::Size( nWidth, nHeight )); + m_xToolbar->SetItemWindow( m_nID, m_pComboBox ); +} + +ComboboxToolbarController::~ComboboxToolbarController() +{ +} + +void SAL_CALL ComboboxToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + + m_xToolbar->SetItemWindow( m_nID, nullptr ); + m_pComboBox.disposeAndClear(); + + ComplexToolbarController::dispose(); +} + +Sequence<PropertyValue> ComboboxToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const +{ + OUString aSelectedText = m_pComboBox->get_active_text(); + + // Add key modifier to argument list + Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier), + comphelper::makePropertyValue("Text", aSelectedText) }; + return aArgs; +} + +void ComboboxToolbarController::Select() +{ + vcl::Window::PointerState aState = m_pComboBox->GetPointerState(); + + sal_uInt16 nKeyModifier = sal_uInt16( aState.mnState & KEY_MODIFIERS_MASK ); + execute( nKeyModifier ); +} + +void ComboboxToolbarController::Modify() +{ + notifyTextChanged(m_pComboBox->get_active_text()); +} + +void ComboboxToolbarController::GetFocus() +{ + notifyFocusGet(); +} + +void ComboboxToolbarController::LoseFocus() +{ + notifyFocusLost(); +} + +void ComboboxToolbarController::Activate() +{ + // Call execute only with non-empty text + if (!m_pComboBox->get_active_text().isEmpty()) + execute(0); +} + +void ComboboxToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand ) +{ + if ( rControlCommand.Command == "SetText" ) + { + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Text" ) + { + OUString aText; + rArg.Value >>= aText; + m_pComboBox->set_active_or_entry_text(aText); + + // send notification + notifyTextChanged( aText ); + break; + } + } + } + else if ( rControlCommand.Command == "SetList" ) + { + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "List" ) + { + Sequence< OUString > aList; + m_pComboBox->clear(); + + rArg.Value >>= aList; + for (OUString const & rName : std::as_const(aList)) + m_pComboBox->append_text(rName); + + // send notification + uno::Sequence< beans::NamedValue > aInfo { { "List", css::uno::Any(aList) } }; + addNotifyInfo( "ListChanged", + getDispatchFromCommand( m_aCommandURL ), + aInfo ); + + break; + } + } + } + else if ( rControlCommand.Command == "AddEntry" ) + { + OUString aText; + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Text" ) + { + if ( rArg.Value >>= aText ) + m_pComboBox->append_text(aText); + break; + } + } + } + else if ( rControlCommand.Command == "InsertEntry" ) + { + sal_Int32 nPos(-1); + OUString aText; + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Pos" ) + { + sal_Int32 nTmpPos = 0; + if ( rArg.Value >>= nTmpPos ) + { + if (( nTmpPos >= 0 ) && + ( nTmpPos < m_pComboBox->get_count() )) + nPos = nTmpPos; + } + } + else if ( rArg.Name == "Text" ) + rArg.Value >>= aText; + } + + m_pComboBox->insert_text(nPos, aText); + } + else if ( rControlCommand.Command == "RemoveEntryPos" ) + { + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Pos" ) + { + sal_Int32 nPos( -1 ); + if ( rArg.Value >>= nPos ) + { + if (0 <= nPos && nPos < m_pComboBox->get_count()) + m_pComboBox->remove(nPos); + } + break; + } + } + } + else if ( rControlCommand.Command == "RemoveEntryText" ) + { + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Text") + { + OUString aText; + if ( rArg.Value >>= aText ) + { + auto nPos = m_pComboBox->find_text(aText); + if (nPos != -1) + m_pComboBox->remove(nPos); + } + break; + } + } + } +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/complextoolbarcontroller.cxx b/framework/source/uielement/complextoolbarcontroller.cxx new file mode 100644 index 000000000..c3f9f191d --- /dev/null +++ b/framework/source/uielement/complextoolbarcontroller.cxx @@ -0,0 +1,326 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <uielement/complextoolbarcontroller.hxx> + +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/frame/status/ItemStatus.hpp> +#include <com/sun/star/frame/status/Visibility.hpp> +#include <com/sun/star/frame/XControlNotificationListener.hpp> +#include <com/sun/star/frame/XFrame.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <svtools/toolboxcontroller.hxx> +#include <vcl/svapp.hxx> +#include <vcl/mnemonic.hxx> +#include <vcl/toolbox.hxx> + +using namespace ::com::sun::star; +using namespace css::awt; +using namespace css::uno; +using namespace css::beans; +using namespace css::lang; +using namespace css::frame; +using namespace css::frame::status; +using namespace css::util; + +namespace framework +{ + +ComplexToolbarController::ComplexToolbarController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + ToolBox* pToolbar, + ToolBoxItemId nID, + const OUString& aCommand ) : + svt::ToolboxController( rxContext, rFrame, aCommand ) + , m_xToolbar( pToolbar ) + , m_nID( nID ) + , m_bMadeInvisible( false ) +{ + m_xURLTransformer.set( URLTransformer::create(m_xContext) ); +} + +ComplexToolbarController::~ComplexToolbarController() +{ +} + +void SAL_CALL ComplexToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + + m_xToolbar->SetItemWindow( m_nID, nullptr ); + svt::ToolboxController::dispose(); + + m_xURLTransformer.clear(); + m_xToolbar.clear(); + m_nID = ToolBoxItemId(0); +} + +Sequence<PropertyValue> ComplexToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const +{ + // Add key modifier to argument list + Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier) }; + return aArgs; +} + +void SAL_CALL ComplexToolbarController::execute( sal_Int16 KeyModifier ) +{ + Reference< XDispatch > xDispatch; + Reference< XURLTransformer > xURLTransformer; + css::util::URL aTargetURL; + Sequence<PropertyValue> aArgs; + + { + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_bInitialized && + m_xFrame.is() && + !m_aCommandURL.isEmpty() ) + { + xURLTransformer = m_xURLTransformer; + xDispatch = getDispatchFromCommand( m_aCommandURL ); + aTargetURL = getInitializedURL(); + aArgs = getExecuteArgs(KeyModifier); + } + } + + if ( xDispatch.is() && !aTargetURL.Complete.isEmpty() ) + { + // Execute dispatch asynchronously + ExecuteInfo* pExecuteInfo = new ExecuteInfo; + pExecuteInfo->xDispatch = xDispatch; + pExecuteInfo->aTargetURL = aTargetURL; + pExecuteInfo->aArgs = aArgs; + Application::PostUserEvent( LINK(nullptr, ComplexToolbarController , ExecuteHdl_Impl), pExecuteInfo ); + } +} + +void ComplexToolbarController::statusChanged( const FeatureStateEvent& Event ) +{ + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + return; + + if ( !m_xToolbar ) + return; + + m_xToolbar->EnableItem( m_nID, Event.IsEnabled ); + + ToolBoxItemBits nItemBits = m_xToolbar->GetItemBits( m_nID ); + nItemBits &= ~ToolBoxItemBits::CHECKABLE; + TriState eTri = TRISTATE_FALSE; + + bool bValue; + OUString aStrValue; + ItemStatus aItemState; + Visibility aItemVisibility; + ControlCommand aControlCommand; + + if ( Event.State >>= bValue ) + { + // Boolean, treat it as checked/unchecked + if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + m_xToolbar->CheckItem( m_nID, bValue ); + if ( bValue ) + eTri = TRISTATE_TRUE; + nItemBits |= ToolBoxItemBits::CHECKABLE; + } + else if ( Event.State >>= aStrValue ) + { + OUString aText( MnemonicGenerator::EraseAllMnemonicChars( aStrValue ) ); + m_xToolbar->SetItemText( m_nID, aText ); + m_xToolbar->SetQuickHelpText( m_nID, aText ); + + if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + } + else if ( Event.State >>= aItemState ) + { + eTri = TRISTATE_INDET; + nItemBits |= ToolBoxItemBits::CHECKABLE; + if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + } + else if ( Event.State >>= aItemVisibility ) + { + m_xToolbar->ShowItem( m_nID, aItemVisibility.bVisible ); + m_bMadeInvisible = !aItemVisibility.bVisible; + } + else if ( Event.State >>= aControlCommand ) + { + if (aControlCommand.Command == "SetQuickHelpText") + { + for (NamedValue const & rArg : std::as_const(aControlCommand.Arguments)) + { + if (rArg.Name == "HelpText") + { + OUString aHelpText; + rArg.Value >>= aHelpText; + m_xToolbar->SetQuickHelpText(m_nID, aHelpText); + break; + } + } + } + else + { + executeControlCommand( aControlCommand ); + } + if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + } + + else if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + + m_xToolbar->SetItemState( m_nID, eTri ); + m_xToolbar->SetItemBits( m_nID, nItemBits ); +} + +IMPL_STATIC_LINK( ComplexToolbarController, ExecuteHdl_Impl, void*, p, void ) +{ + ExecuteInfo* pExecuteInfo = static_cast<ExecuteInfo*>(p); + SolarMutexReleaser aReleaser; + try + { + // Asynchronous execution as this can lead to our own destruction! + // Framework can recycle our current frame and the layout manager disposes all user interface + // elements if a component gets detached from its frame! + pExecuteInfo->xDispatch->dispatch( pExecuteInfo->aTargetURL, pExecuteInfo->aArgs ); + } + catch ( const Exception& ) + { + } + + delete pExecuteInfo; +} + +IMPL_STATIC_LINK( ComplexToolbarController, Notify_Impl, void*, p, void ) +{ + NotifyInfo* pNotifyInfo = static_cast<NotifyInfo*>(p); + SolarMutexReleaser aReleaser; + try + { + // Asynchronous execution: As this can lead to our own destruction! + // Framework can recycle our current frame and the layout manager disposes all user interface + // elements if a component gets detached from its frame! + frame::ControlEvent aEvent; + aEvent.aURL = pNotifyInfo->aSourceURL; + aEvent.Event = pNotifyInfo->aEventName; + aEvent.aInformation = pNotifyInfo->aInfoSeq; + pNotifyInfo->xNotifyListener->controlEvent( aEvent ); + } + catch ( const Exception& ) + { + } + + delete pNotifyInfo; +} + +void ComplexToolbarController::addNotifyInfo( + const OUString& aEventName, + const uno::Reference< frame::XDispatch >& xDispatch, + const uno::Sequence< beans::NamedValue >& rInfo ) +{ + uno::Reference< frame::XControlNotificationListener > xControlNotify( xDispatch, uno::UNO_QUERY ); + + if ( !xControlNotify.is() ) + return; + + // Execute notification asynchronously + NotifyInfo* pNotifyInfo = new NotifyInfo; + + pNotifyInfo->aEventName = aEventName; + pNotifyInfo->xNotifyListener = xControlNotify; + pNotifyInfo->aSourceURL = getInitializedURL(); + + // Add frame as source to the information sequence + sal_Int32 nCount = rInfo.getLength(); + uno::Sequence< beans::NamedValue > aInfoSeq( rInfo ); + aInfoSeq.realloc( nCount+1 ); + auto pInfoSeq = aInfoSeq.getArray(); + pInfoSeq[nCount].Name = "Source"; + pInfoSeq[nCount].Value <<= getFrameInterface(); + pNotifyInfo->aInfoSeq = aInfoSeq; + + Application::PostUserEvent( LINK(nullptr, ComplexToolbarController, Notify_Impl), pNotifyInfo ); +} + +uno::Reference< frame::XDispatch > ComplexToolbarController::getDispatchFromCommand( const OUString& aCommand ) const +{ + uno::Reference< frame::XDispatch > xDispatch; + + if ( m_bInitialized && m_xFrame.is() && !aCommand.isEmpty() ) + { + URLToDispatchMap::const_iterator pIter = m_aListenerMap.find( aCommand ); + if ( pIter != m_aListenerMap.end() ) + xDispatch = pIter->second; + } + + return xDispatch; +} + +const css::util::URL& ComplexToolbarController::getInitializedURL() +{ + if ( m_aURL.Complete.isEmpty() ) + { + m_aURL.Complete = m_aCommandURL; + m_xURLTransformer->parseStrict( m_aURL ); + } + return m_aURL; +} + +void ComplexToolbarController::notifyFocusGet() +{ + // send focus get notification + uno::Sequence< beans::NamedValue > aInfo; + addNotifyInfo( "FocusSet", + getDispatchFromCommand( m_aCommandURL ), + aInfo ); +} + +void ComplexToolbarController::notifyFocusLost() +{ + // send focus lost notification + uno::Sequence< beans::NamedValue > aInfo; + addNotifyInfo( "FocusLost", + getDispatchFromCommand( m_aCommandURL ), + aInfo ); +} + +void ComplexToolbarController::notifyTextChanged( const OUString& aText ) +{ + // send text changed notification + uno::Sequence< beans::NamedValue > aInfo { { "Text", css::uno::Any(aText) } }; + addNotifyInfo( "TextChanged", + getDispatchFromCommand( m_aCommandURL ), + aInfo ); +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/controlmenucontroller.cxx b/framework/source/uielement/controlmenucontroller.cxx new file mode 100644 index 000000000..2c31081bb --- /dev/null +++ b/framework/source/uielement/controlmenucontroller.cxx @@ -0,0 +1,327 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XStatusListener.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> + +#include <cppuhelper/supportsservice.hxx> +#include <vcl/graph.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/image.hxx> +#include <svtools/popupmenucontrollerbase.hxx> +#include <osl/mutex.hxx> +#include <unordered_map> + +#include <classes/fwkresid.hxx> +#include <bitmaps.hlst> +#include <strings.hrc> + +static const char* aCommands[] = +{ + ".uno:ConvertToEdit", + ".uno:ConvertToButton", + ".uno:ConvertToFixed", + ".uno:ConvertToList", + ".uno:ConvertToCheckBox", + ".uno:ConvertToRadio", + ".uno:ConvertToGroup", + ".uno:ConvertToCombo", + ".uno:ConvertToImageBtn", + ".uno:ConvertToFileControl", + ".uno:ConvertToDate", + ".uno:ConvertToTime", + ".uno:ConvertToNumeric", + ".uno:ConvertToCurrency", + ".uno:ConvertToPattern", + ".uno:ConvertToImageControl", + ".uno:ConvertToFormatted", + ".uno:ConvertToScrollBar", + ".uno:ConvertToSpinButton", + ".uno:ConvertToNavigationBar" +}; + +static TranslateId aLabels[] = +{ + RID_STR_PROPTITLE_EDIT, + RID_STR_PROPTITLE_PUSHBUTTON, + RID_STR_PROPTITLE_FIXEDTEXT, + RID_STR_PROPTITLE_LISTBOX, + RID_STR_PROPTITLE_CHECKBOX, + RID_STR_PROPTITLE_RADIOBUTTON, + RID_STR_PROPTITLE_GROUPBOX, + RID_STR_PROPTITLE_COMBOBOX, + RID_STR_PROPTITLE_IMAGEBUTTON, + RID_STR_PROPTITLE_FILECONTROL, + RID_STR_PROPTITLE_DATEFIELD, + RID_STR_PROPTITLE_TIMEFIELD, + RID_STR_PROPTITLE_NUMERICFIELD, + RID_STR_PROPTITLE_CURRENCYFIELD, + RID_STR_PROPTITLE_PATTERNFIELD, + RID_STR_PROPTITLE_IMAGECONTROL, + RID_STR_PROPTITLE_FORMATTED, + RID_STR_PROPTITLE_SCROLLBAR, + RID_STR_PROPTITLE_SPINBUTTON, + RID_STR_PROPTITLE_NAVBAR +}; + +const rtl::OUStringConstExpr aImgIds[] +{ + RID_SVXBMP_EDITBOX, + RID_SVXBMP_BUTTON, + RID_SVXBMP_FIXEDTEXT, + RID_SVXBMP_LISTBOX, + RID_SVXBMP_CHECKBOX, + RID_SVXBMP_RADIOBUTTON, + RID_SVXBMP_GROUPBOX, + RID_SVXBMP_COMBOBOX, + RID_SVXBMP_IMAGEBUTTON, + RID_SVXBMP_FILECONTROL, + RID_SVXBMP_DATEFIELD, + RID_SVXBMP_TIMEFIELD, + RID_SVXBMP_NUMERICFIELD, + RID_SVXBMP_CURRENCYFIELD, + RID_SVXBMP_PATTERNFIELD, + RID_SVXBMP_IMAGECONTROL, + RID_SVXBMP_FORMATTEDFIELD, + RID_SVXBMP_SCROLLBAR, + RID_SVXBMP_SPINBUTTON, + RID_SVXBMP_NAVIGATIONBAR +}; + +using namespace css; +using namespace css::uno; +using namespace css::lang; +using namespace css::frame; +using namespace css::beans; + +namespace { + +class ControlMenuController : public svt::PopupMenuControllerBase +{ + using svt::PopupMenuControllerBase::disposing; + +public: + explicit ControlMenuController( const uno::Reference< uno::XComponentContext >& xContext ); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.ControlMenuController"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.frame.PopupMenuController"}; + } + + // XPopupMenuController + virtual void SAL_CALL updatePopupMenu() override; + + // XInitialization + virtual void SAL_CALL initialize( const uno::Sequence< uno::Any >& aArguments ) override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const frame::FeatureStateEvent& Event ) override; + + // XMenuListener + virtual void SAL_CALL itemActivated( const awt::MenuEvent& rEvent ) override; + + // XEventListener + virtual void SAL_CALL disposing( const lang::EventObject& Source ) override; + +private: + class UrlToDispatchMap : public std::unordered_map< OUString, + uno::Reference< frame::XDispatch > > + { + public: + void free() + { + UrlToDispatchMap().swap( *this );// get rid of reserved capacity + } + }; + + void updateImagesPopupMenu(Reference<awt::XPopupMenu> const& rPopupMenu); + void fillPopupMenu(uno::Reference<awt::XPopupMenu> const& rPopupMenu); + + bool m_bShowMenuImages : 1; + UrlToDispatchMap m_aURLToDispatchMap; +}; + +ControlMenuController::ControlMenuController(const css::uno::Reference< css::uno::XComponentContext >& xContext) + : svt::PopupMenuControllerBase(xContext) +{ + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + m_bShowMenuImages = rSettings.GetUseImagesInMenus(); + +} + +// private function +void ControlMenuController::updateImagesPopupMenu(Reference<awt::XPopupMenu> const& rPopupMenu) +{ + if (!rPopupMenu) + return; + for (size_t i=0; i < SAL_N_ELEMENTS(aCommands); ++i) + { + sal_Int16 nItemId = i + 1; + if (m_bShowMenuImages) + { + Image aImage(StockImage::Yes, OUString(aImgIds[i])); + Graphic aGraphic(aImage); + rPopupMenu->setItemImage(nItemId, aGraphic.GetXGraphic(), false); + } + else + rPopupMenu->setItemImage(nItemId, nullptr, false); + } +} + +// private function +void ControlMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + resetPopupMenu( rPopupMenu ); + + for (size_t i=0; i < SAL_N_ELEMENTS(aCommands); ++i) + { + sal_Int16 nItemId = i + 1; + OUString sCommand(OUString::createFromAscii(aCommands[i])); + rPopupMenu->insertItem(nItemId, FwkResId(aLabels[i]), 0, i); + rPopupMenu->setCommand(nItemId, sCommand); + rPopupMenu->enableItem(nItemId, false); + } + + updateImagesPopupMenu(rPopupMenu); + + rPopupMenu->hideDisabledEntries(true); +} + +} + +// XEventListener +void SAL_CALL ControlMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + osl::MutexGuard aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL ControlMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + osl::MutexGuard aLock( m_aMutex ); + + if (!m_xPopupMenu) + return; + + sal_Int16 nItemId = 0; + for (size_t i=0; i < SAL_N_ELEMENTS(aCommands); ++i) + { + if ( Event.FeatureURL.Complete.equalsAscii( aCommands[i] )) + { + nItemId = i + 1; + break; + } + } + + if (!nItemId) + return; + + m_xPopupMenu->enableItem(nItemId, Event.IsEnabled); +} + +// XMenuListener +void SAL_CALL ControlMenuController::itemActivated( const css::awt::MenuEvent& ) +{ + osl::MutexGuard aLock( m_aMutex ); + + SolarMutexGuard aSolarMutexGuard; + + // Check if some modes have changed so we have to update our menu images + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + bool bShowMenuImages = rSettings.GetUseImagesInMenus(); + + if (bShowMenuImages != m_bShowMenuImages) + { + m_bShowMenuImages = bShowMenuImages; + updateImagesPopupMenu(m_xPopupMenu); + } +} + +// XPopupMenuController +void SAL_CALL ControlMenuController::updatePopupMenu() +{ + osl::MutexGuard aLock( m_aMutex ); + + throwIfDisposed(); + + if ( !(m_xFrame.is() && m_xPopupMenu.is()) ) + return; + + css::util::URL aTargetURL; + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + fillPopupMenu( m_xPopupMenu ); + m_aURLToDispatchMap.free(); + + for (const char* aCommand : aCommands) + { + aTargetURL.Complete = OUString::createFromAscii( aCommand ); + m_xURLTransformer->parseStrict( aTargetURL ); + + Reference< XDispatch > xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + if ( xDispatch.is() ) + { + xDispatch->addStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + xDispatch->removeStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + m_aURLToDispatchMap.emplace( aTargetURL.Complete, xDispatch ); + } + } +} + +// XInitialization +void SAL_CALL ControlMenuController::initialize( const Sequence< Any >& aArguments ) +{ + osl::MutexGuard aLock( m_aMutex ); + svt::PopupMenuControllerBase::initialize(aArguments); + m_aBaseURL.clear(); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ControlMenuController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new ControlMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/dropdownboxtoolbarcontroller.cxx b/framework/source/uielement/dropdownboxtoolbarcontroller.cxx new file mode 100644 index 000000000..b3478a7b1 --- /dev/null +++ b/framework/source/uielement/dropdownboxtoolbarcontroller.cxx @@ -0,0 +1,282 @@ +/* -*- 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 <uielement/dropdownboxtoolbarcontroller.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <vcl/InterimItemWindow.hxx> +#include <svtools/toolboxcontroller.hxx> +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> + +using namespace ::com::sun::star; +using namespace css::awt; +using namespace css::uno; +using namespace css::beans; +using namespace css::lang; +using namespace css::frame; +using namespace css::util; + +namespace framework +{ + +// Wrapper class to notify controller about events from ListBox. +// Unfortunaltly the events are notified through virtual methods instead +// of Listeners. + +class ListBoxControl final : public InterimItemWindow +{ +public: + ListBoxControl(vcl::Window* pParent, DropdownToolbarController* pListBoxListener); + virtual ~ListBoxControl() override; + virtual void dispose() override; + + void set_active(int nPos) { m_xWidget->set_active(nPos); } + void append_text(const OUString& rStr) { m_xWidget->append_text(rStr); } + void insert_text(int nPos, const OUString& rStr) { m_xWidget->insert_text(nPos, rStr); } + int get_count() const { return m_xWidget->get_count(); } + int find_text(const OUString& rStr) const { return m_xWidget->find_text(rStr); } + OUString get_active_text() const { return m_xWidget->get_active_text(); } + void clear() { return m_xWidget->clear(); } + void remove(int nPos) { m_xWidget->remove(nPos); } + + DECL_LINK(FocusInHdl, weld::Widget&, void); + DECL_LINK(FocusOutHdl, weld::Widget&, void); + DECL_LINK(ModifyHdl, weld::ComboBox&, void); + DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool); + +private: + std::unique_ptr<weld::ComboBox> m_xWidget; + DropdownToolbarController* m_pListBoxListener; +}; + +ListBoxControl::ListBoxControl(vcl::Window* pParent, DropdownToolbarController* pListBoxListener) + : InterimItemWindow(pParent, "svt/ui/listcontrol.ui", "ListControl") + , m_xWidget(m_xBuilder->weld_combo_box("listbox")) + , m_pListBoxListener( pListBoxListener ) +{ + InitControlBase(m_xWidget.get()); + + m_xWidget->connect_focus_in(LINK(this, ListBoxControl, FocusInHdl)); + m_xWidget->connect_focus_out(LINK(this, ListBoxControl, FocusOutHdl)); + m_xWidget->connect_changed(LINK(this, ListBoxControl, ModifyHdl)); + m_xWidget->connect_key_press(LINK(this, ListBoxControl, KeyInputHdl)); + + m_xWidget->set_size_request(42, -1); // so a later narrow size request can stick + SetSizePixel(get_preferred_size()); +} + +IMPL_LINK(ListBoxControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +ListBoxControl::~ListBoxControl() +{ + disposeOnce(); +} + +void ListBoxControl::dispose() +{ + m_pListBoxListener = nullptr; + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +IMPL_LINK_NOARG(ListBoxControl, ModifyHdl, weld::ComboBox&, void) +{ + if (m_pListBoxListener) + m_pListBoxListener->Select(); +} + +IMPL_LINK_NOARG(ListBoxControl, FocusInHdl, weld::Widget&, void) +{ + if (m_pListBoxListener) + m_pListBoxListener->GetFocus(); +} + +IMPL_LINK_NOARG(ListBoxControl, FocusOutHdl, weld::Widget&, void) +{ + if (m_pListBoxListener) + m_pListBoxListener->LoseFocus(); +} + +DropdownToolbarController::DropdownToolbarController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + ToolBox* pToolbar, + ToolBoxItemId nID, + sal_Int32 nWidth, + const OUString& aCommand ) : + ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand ) + , m_pListBoxControl( nullptr ) +{ + m_pListBoxControl = VclPtr<ListBoxControl>::Create(m_xToolbar, this); + if ( nWidth == 0 ) + nWidth = 100; + + // ListBoxControl ctor has set a suitable height already + auto nHeight = m_pListBoxControl->GetSizePixel().Height(); + + m_pListBoxControl->SetSizePixel( ::Size( nWidth, nHeight )); + m_xToolbar->SetItemWindow( m_nID, m_pListBoxControl ); +} + +DropdownToolbarController::~DropdownToolbarController() +{ +} + +void SAL_CALL DropdownToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + + m_xToolbar->SetItemWindow( m_nID, nullptr ); + m_pListBoxControl.disposeAndClear(); + + ComplexToolbarController::dispose(); +} + +Sequence<PropertyValue> DropdownToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const +{ + OUString aSelectedText = m_pListBoxControl->get_active_text(); + + // Add key modifier to argument list + Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier), + comphelper::makePropertyValue("Text", aSelectedText) }; + return aArgs; +} + +void DropdownToolbarController::Select() +{ + if (m_pListBoxControl->get_count() > 0) + execute(0); +} + +void DropdownToolbarController::GetFocus() +{ + notifyFocusGet(); +} + +void DropdownToolbarController::LoseFocus() +{ + notifyFocusLost(); +} + +void DropdownToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand ) +{ + if ( rControlCommand.Command == "SetList" ) + { + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "List" ) + { + Sequence< OUString > aList; + m_pListBoxControl->clear(); + + rArg.Value >>= aList; + for (OUString const & rName : std::as_const(aList)) + m_pListBoxControl->append_text(rName); + + m_pListBoxControl->set_active(0); + + // send notification + uno::Sequence< beans::NamedValue > aInfo { { "List", css::uno::Any(aList) } }; + addNotifyInfo( "ListChanged", + getDispatchFromCommand( m_aCommandURL ), + aInfo ); + + break; + } + } + } + else if ( rControlCommand.Command == "AddEntry" ) + { + OUString aText; + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Text" ) + { + if ( rArg.Value >>= aText ) + m_pListBoxControl->append_text(aText); + break; + } + } + } + else if ( rControlCommand.Command == "InsertEntry" ) + { + sal_Int32 nPos(-1); + OUString aText; + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Pos" ) + { + sal_Int32 nTmpPos = 0; + if ( rArg.Value >>= nTmpPos ) + { + if (( nTmpPos >= 0 ) && + ( nTmpPos < m_pListBoxControl->get_count() )) + nPos = nTmpPos; + } + } + else if ( rArg.Name == "Text" ) + rArg.Value >>= aText; + } + + m_pListBoxControl->insert_text(nPos, aText); + } + else if ( rControlCommand.Command == "RemoveEntryPos" ) + { + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Pos" ) + { + sal_Int32 nPos( -1 ); + if ( rArg.Value >>= nPos ) + { + if ( 0 <= nPos && nPos < m_pListBoxControl->get_count() ) + m_pListBoxControl->remove(nPos); + } + break; + } + } + } + else if ( rControlCommand.Command == "RemoveEntryText" ) + { + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Text" ) + { + OUString aText; + if ( rArg.Value >>= aText ) + { + auto nPos = m_pListBoxControl->find_text(aText); + if (nPos != -1) + m_pListBoxControl->remove(nPos); + } + break; + } + } + } +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/edittoolbarcontroller.cxx b/framework/source/uielement/edittoolbarcontroller.cxx new file mode 100644 index 000000000..1f974e77c --- /dev/null +++ b/framework/source/uielement/edittoolbarcontroller.cxx @@ -0,0 +1,217 @@ +/* -*- 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 <uielement/edittoolbarcontroller.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <vcl/InterimItemWindow.hxx> +#include <svtools/toolboxcontroller.hxx> +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> +#include <vcl/event.hxx> + +using namespace ::com::sun::star; +using namespace css::uno; +using namespace css::beans; +using namespace css::lang; +using namespace css::frame; +using namespace css::util; + +namespace framework +{ + +// Wrapper class to notify controller about events from edit. +// Unfortunaltly the events are notified through virtual methods instead +// of Listeners. + +class EditControl final : public InterimItemWindow +{ +public: + EditControl(vcl::Window* pParent, EditToolbarController* pEditToolbarController); + virtual ~EditControl() override; + virtual void dispose() override; + + OUString get_text() const { return m_xWidget->get_text(); } + void set_text(const OUString& rText) { m_xWidget->set_text(rText); } + +private: + std::unique_ptr<weld::Entry> m_xWidget; + EditToolbarController* m_pEditToolbarController; + + DECL_LINK(FocusInHdl, weld::Widget&, void); + DECL_LINK(FocusOutHdl, weld::Widget&, void); + DECL_LINK(ModifyHdl, weld::Entry&, void); + DECL_LINK(ActivateHdl, weld::Entry&, bool); + DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool); +}; + +EditControl::EditControl(vcl::Window* pParent, EditToolbarController* pEditToolbarController) + : InterimItemWindow(pParent, "svt/ui/editcontrol.ui", "EditControl") + , m_xWidget(m_xBuilder->weld_entry("entry")) + , m_pEditToolbarController(pEditToolbarController) +{ + OString sEmpty; + m_xWidget->set_help_id(sEmpty); + m_xContainer->set_help_id(sEmpty); + + InitControlBase(m_xWidget.get()); + + m_xWidget->connect_focus_in(LINK(this, EditControl, FocusInHdl)); + m_xWidget->connect_focus_out(LINK(this, EditControl, FocusOutHdl)); + m_xWidget->connect_changed(LINK(this, EditControl, ModifyHdl)); + m_xWidget->connect_activate(LINK(this, EditControl, ActivateHdl)); + m_xWidget->connect_key_press(LINK(this, EditControl, KeyInputHdl)); + + SetSizePixel(get_preferred_size()); +} + +IMPL_LINK(EditControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +EditControl::~EditControl() +{ + disposeOnce(); +} + +void EditControl::dispose() +{ + m_pEditToolbarController = nullptr; + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +IMPL_LINK_NOARG(EditControl, ModifyHdl, weld::Entry&, void) +{ + if (m_pEditToolbarController) + m_pEditToolbarController->Modify(); +} + +IMPL_LINK_NOARG(EditControl, FocusInHdl, weld::Widget&, void) +{ + if (m_pEditToolbarController) + m_pEditToolbarController->GetFocus(); +} + +IMPL_LINK_NOARG(EditControl, FocusOutHdl, weld::Widget&, void) +{ + if ( m_pEditToolbarController ) + m_pEditToolbarController->LoseFocus(); +} + +IMPL_LINK_NOARG(EditControl, ActivateHdl, weld::Entry&, bool) +{ + if (m_pEditToolbarController) + m_pEditToolbarController->Activate(); + return true; +} + +EditToolbarController::EditToolbarController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + ToolBox* pToolbar, + ToolBoxItemId nID, + sal_Int32 nWidth, + const OUString& aCommand ) : + ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand ) + , m_pEditControl( nullptr ) +{ + m_pEditControl = VclPtr<EditControl>::Create(m_xToolbar, this); + if ( nWidth == 0 ) + nWidth = 100; + + // EditControl ctor has set a suitable height already + auto nHeight = m_pEditControl->GetSizePixel().Height(); + + m_pEditControl->SetSizePixel( ::Size( nWidth, nHeight )); + m_xToolbar->SetItemWindow( m_nID, m_pEditControl ); +} + +EditToolbarController::~EditToolbarController() +{ +} + +void SAL_CALL EditToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + + m_xToolbar->SetItemWindow( m_nID, nullptr ); + m_pEditControl.disposeAndClear(); + + ComplexToolbarController::dispose(); +} + +Sequence<PropertyValue> EditToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const +{ + OUString aSelectedText = m_pEditControl->get_text(); + + // Add key modifier to argument list + Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier), + comphelper::makePropertyValue("Text", aSelectedText) }; + return aArgs; +} + +void EditToolbarController::Modify() +{ + notifyTextChanged(m_pEditControl->get_text()); +} + +void EditToolbarController::GetFocus() +{ + notifyFocusGet(); +} + +void EditToolbarController::LoseFocus() +{ + notifyFocusLost(); +} + +void EditToolbarController::Activate() +{ + // Call execute only with non-empty text + if (!m_pEditControl->get_text().isEmpty()) + execute(0); +} + +void EditToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand ) +{ + if ( !rControlCommand.Command.startsWith( "SetText" )) + return; + + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name.startsWith( "Text" )) + { + OUString aText; + rArg.Value >>= aText; + m_pEditControl->set_text(aText); + + // send notification + notifyTextChanged( aText ); + break; + } + } +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/fontmenucontroller.cxx b/framework/source/uielement/fontmenucontroller.cxx new file mode 100644 index 000000000..c0dd19e2e --- /dev/null +++ b/framework/source/uielement/fontmenucontroller.cxx @@ -0,0 +1,218 @@ +/* -*- 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 <uielement/fontmenucontroller.hxx> + +#include <services.h> + +#include <com/sun/star/awt/MenuItemStyle.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> + +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/i18nhelp.hxx> +#include <tools/urlobj.hxx> +#include <vcl/mnemonic.hxx> +#include <osl/mutex.hxx> +#include <cppuhelper/supportsservice.hxx> + +// Defines + +using namespace css::uno; +using namespace css::lang; +using namespace css::frame; +using namespace css::beans; +using namespace css::util; + +static bool lcl_I18nCompareString(const OUString& rStr1, const OUString& rStr2) +{ + const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper(); + return rI18nHelper.CompareString( rStr1, rStr2 ) < 0; +} + +namespace framework +{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL FontMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.FontMenuController"; +} + +sal_Bool SAL_CALL FontMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL FontMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + +FontMenuController::FontMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + svt::PopupMenuControllerBase( xContext ) +{ +} + +FontMenuController::~FontMenuController() +{ +} + +// private function +void FontMenuController::fillPopupMenu( const Sequence< OUString >& rFontNameSeq, Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + SolarMutexGuard aSolarMutexGuard; + + resetPopupMenu( rPopupMenu ); + + std::vector<OUString> aVector; + aVector.reserve(rFontNameSeq.getLength()); + for ( OUString const & s : rFontNameSeq ) + { + aVector.push_back(MnemonicGenerator::EraseAllMnemonicChars(s)); + } + sort(aVector.begin(), aVector.end(), lcl_I18nCompareString ); + + static const OUStringLiteral aFontNameCommandPrefix( u".uno:CharFontName?CharFontName.FamilyName:string=" ); + const sal_Int16 nCount = static_cast<sal_Int16>(aVector.size()); + for ( sal_Int16 i = 0; i < nCount; i++ ) + { + const OUString& rName = aVector[i]; + m_xPopupMenu->insertItem( i+1, rName, css::awt::MenuItemStyle::RADIOCHECK | css::awt::MenuItemStyle::AUTOCHECK, i ); + if ( rName == m_aFontFamilyName ) + m_xPopupMenu->checkItem( i+1, true ); + OUString aFontNameCommand = aFontNameCommandPrefix + INetURLObject::encode( rName, INetURLObject::PART_HTTP_QUERY, INetURLObject::EncodeMechanism::All ); + m_xPopupMenu->setCommand(i + 1, aFontNameCommand); // Store font name into item command. + } +} + +// XEventListener +void SAL_CALL FontMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + osl::MutexGuard aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + m_xFontListDispatch.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL FontMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + css::awt::FontDescriptor aFontDescriptor; + Sequence< OUString > aFontNameSeq; + + if ( Event.State >>= aFontDescriptor ) + { + osl::MutexGuard aLock( m_aMutex ); + m_aFontFamilyName = aFontDescriptor.Name; + } + else if ( Event.State >>= aFontNameSeq ) + { + osl::MutexGuard aLock( m_aMutex ); + if ( m_xPopupMenu.is() ) + fillPopupMenu( aFontNameSeq, m_xPopupMenu ); + } +} + +// XMenuListener +void SAL_CALL FontMenuController::itemActivated( const css::awt::MenuEvent& ) +{ + osl::MutexGuard aLock( m_aMutex ); + + if ( !m_xPopupMenu.is() ) + return; + + // find new font name and set check mark! + sal_uInt16 nChecked = 0; + sal_uInt16 nItemCount = m_xPopupMenu->getItemCount(); + for( sal_uInt16 i = 0; i < nItemCount; i++ ) + { + sal_uInt16 nItemId = m_xPopupMenu->getItemId( i ); + + if ( m_xPopupMenu->isItemChecked( nItemId ) ) + nChecked = nItemId; + + OUString aText = m_xPopupMenu->getItemText( nItemId ); + + // TODO: must be replaced by implementation of VCL, when available + sal_Int32 nIndex = aText.indexOf( '~' ); + if ( nIndex >= 0 ) + aText = aText.replaceAt( nIndex, 1, u"" ); + // TODO: must be replaced by implementation of VCL, when available + + if ( aText == m_aFontFamilyName ) + { + m_xPopupMenu->checkItem( nItemId, true ); + return; + } + } + + if ( nChecked ) + m_xPopupMenu->checkItem( nChecked, false ); +} + +// XPopupMenuController +void FontMenuController::impl_setPopupMenu() +{ + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + + css::util::URL aTargetURL; + // Register for font list updates to get the current font list from the controller + aTargetURL.Complete = ".uno:FontNameList"; + m_xURLTransformer->parseStrict( aTargetURL ); + m_xFontListDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); +} + +void SAL_CALL FontMenuController::updatePopupMenu() +{ + svt::PopupMenuControllerBase::updatePopupMenu(); + + osl::ClearableMutexGuard aLock( m_aMutex ); + Reference< XDispatch > xDispatch( m_xFontListDispatch ); + css::util::URL aTargetURL; + aTargetURL.Complete = ".uno:FontNameList"; + m_xURLTransformer->parseStrict( aTargetURL ); + aLock.clear(); + + if ( xDispatch.is() ) + { + xDispatch->addStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + xDispatch->removeStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_FontMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new framework::FontMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/fontsizemenucontroller.cxx b/framework/source/uielement/fontsizemenucontroller.cxx new file mode 100644 index 000000000..4c5c68688 --- /dev/null +++ b/framework/source/uielement/fontsizemenucontroller.cxx @@ -0,0 +1,285 @@ +/* -*- 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 <uielement/fontsizemenucontroller.hxx> + +#include <services.h> + +#include <com/sun/star/awt/MenuItemStyle.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/view/XPrintable.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> + +#include <vcl/svapp.hxx> +#include <vcl/i18nhelp.hxx> +#include <vcl/print.hxx> +#include <vcl/settings.hxx> +#include <svtools/ctrltool.hxx> +#include <osl/mutex.hxx> +#include <memory> +#include <cppuhelper/supportsservice.hxx> + +// Defines + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace com::sun::star::view; + +namespace framework +{ + +OUString SAL_CALL FontSizeMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.FontSizeMenuController"; +} + +sal_Bool SAL_CALL FontSizeMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL FontSizeMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + +FontSizeMenuController::FontSizeMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + svt::PopupMenuControllerBase( xContext ) +{ +} + +FontSizeMenuController::~FontSizeMenuController() +{ +} + +// private function +OUString FontSizeMenuController::retrievePrinterName( css::uno::Reference< css::frame::XFrame > const & rFrame ) +{ + OUString aPrinterName; + + if ( rFrame.is() ) + { + Reference< XController > xController = m_xFrame->getController(); + if ( xController.is() ) + { + Reference< XPrintable > xPrintable( xController->getModel(), UNO_QUERY ); + if ( xPrintable.is() ) + { + const Sequence< PropertyValue > aPrinterSeq = xPrintable->getPrinter(); + for ( PropertyValue const & prop : aPrinterSeq ) + { + if ( prop.Name == "Name" ) + { + prop.Value >>= aPrinterName; + break; + } + } + } + } + } + + return aPrinterName; +} + +// private function +void FontSizeMenuController::setCurHeight( tools::Long nHeight, Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + // check menu item + sal_uInt16 nChecked = 0; + sal_uInt16 nItemCount = rPopupMenu->getItemCount(); + for( sal_uInt16 i = 0; i < nItemCount; i++ ) + { + sal_uInt16 nItemId = rPopupMenu->getItemId( i ); + + if ( m_aHeightArray[i] == nHeight ) + { + rPopupMenu->checkItem( nItemId, true ); + return; + } + + if ( rPopupMenu->isItemChecked( nItemId ) ) + nChecked = nItemId; + } + + if ( nChecked ) + rPopupMenu->checkItem( nChecked, false ); +} + +// private function +void FontSizeMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + resetPopupMenu( rPopupMenu ); + + std::unique_ptr<FontList> pFontList; + ScopedVclPtr<Printer> pInfoPrinter; + OUString aPrinterName; + + SolarMutexGuard aSolarMutexGuard; + + // try to retrieve printer name of document + aPrinterName = retrievePrinterName( m_xFrame ); + if ( !aPrinterName.isEmpty() ) + { + pInfoPrinter.disposeAndReset(VclPtr<Printer>::Create( aPrinterName )); + if ( pInfoPrinter && pInfoPrinter->GetFontFaceCollectionCount() > 0 ) + pFontList.reset(new FontList( pInfoPrinter.get() )); + } + + if ( !pFontList ) + pFontList.reset(new FontList( Application::GetDefaultDevice() )); + + // setup font size array + m_aHeightArray.clear(); + + sal_uInt16 nPos = 0; // Id is nPos+1 + static const OUStringLiteral aFontHeightCommand( u".uno:FontHeight?FontHeight.Height:float=" ); + + // first insert font size names (for simplified/traditional chinese) + FontSizeNames aFontSizeNames( Application::GetSettings().GetUILanguageTag().getLanguageType() ); + OUString aCommand; + + if (!aFontSizeNames.IsEmpty()) + { + // for scalable fonts all font size names + sal_Int32 nCount = aFontSizeNames.Count(); + for( sal_Int32 i = 0; i < nCount; i++ ) + { + OUString aSizeName = aFontSizeNames.GetIndexName( i ); + sal_Int32 nSize = aFontSizeNames.GetIndexSize( i ); + m_aHeightArray.push_back(nSize); + rPopupMenu->insertItem(nPos + 1, aSizeName, css::awt::MenuItemStyle::RADIOCHECK | css::awt::MenuItemStyle::AUTOCHECK, nPos); + + // Create dispatchable .uno command and set it + float fPoint = float(nSize) / 10; + aCommand = aFontHeightCommand + OUString::number( fPoint ); + rPopupMenu->setCommand(nPos + 1, aCommand); + + ++nPos; + } + } + + // then insert numerical font size values + const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper(); + const int* pAry = FontList::GetStdSizeAry(); + const int* pTempAry = pAry; + while ( *pTempAry ) + { + m_aHeightArray.push_back(*pTempAry); + rPopupMenu->insertItem(nPos + 1, rI18nHelper.GetNum(*pTempAry, 1, true, false), + css::awt::MenuItemStyle::RADIOCHECK | css::awt::MenuItemStyle::AUTOCHECK, nPos); + + // Create dispatchable .uno command and set it + float fPoint = float(*pTempAry) / 10; + aCommand = aFontHeightCommand + OUString::number( fPoint ); + rPopupMenu->setCommand(nPos + 1, aCommand); + + ++nPos; + pTempAry++; + } + + setCurHeight( tools::Long( m_aFontHeight.Height * 10), rPopupMenu ); +} + +// XEventListener +void SAL_CALL FontSizeMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + osl::MutexGuard aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + m_xCurrentFontDispatch.clear(); + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL FontSizeMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + css::awt::FontDescriptor aFontDescriptor; + css::frame::status::FontHeight aFontHeight; + + if ( Event.State >>= aFontDescriptor ) + { + osl::MutexGuard aLock( m_aMutex ); + + if ( m_xPopupMenu.is() ) + fillPopupMenu( m_xPopupMenu ); + } + else if ( Event.State >>= aFontHeight ) + { + osl::MutexGuard aLock( m_aMutex ); + m_aFontHeight = aFontHeight; + + if ( m_xPopupMenu.is() ) + { + SolarMutexGuard aSolarMutexGuard; + setCurHeight( tools::Long( m_aFontHeight.Height * 10), m_xPopupMenu ); + } + } +} + +// XPopupMenuController +void FontSizeMenuController::impl_setPopupMenu() +{ + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + css::util::URL aTargetURL; + // Register for font name updates which gives us info about the current font! + aTargetURL.Complete = ".uno:CharFontName"; + m_xURLTransformer->parseStrict( aTargetURL ); + m_xCurrentFontDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); +} + +void SAL_CALL FontSizeMenuController::updatePopupMenu() +{ + osl::ClearableMutexGuard aLock( m_aMutex ); + + throwIfDisposed(); + + Reference< XDispatch > xDispatch( m_xCurrentFontDispatch ); + css::util::URL aTargetURL; + aTargetURL.Complete = ".uno:CharFontName"; + m_xURLTransformer->parseStrict( aTargetURL ); + aLock.clear(); + + if ( xDispatch.is() ) + { + xDispatch->addStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + xDispatch->removeStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + } + + svt::PopupMenuControllerBase::updatePopupMenu(); +} +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_FontSizeMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new framework::FontSizeMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/footermenucontroller.cxx b/framework/source/uielement/footermenucontroller.cxx new file mode 100644 index 000000000..9a941a825 --- /dev/null +++ b/framework/source/uielement/footermenucontroller.cxx @@ -0,0 +1,73 @@ +/* -*- 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 <uielement/footermenucontroller.hxx> + +#include <services.h> + +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <cppuhelper/supportsservice.hxx> + +// Defines + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace com::sun::star::style; +using namespace com::sun::star::container; + +namespace framework +{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL FooterMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.FooterMenuController"; +} + +sal_Bool SAL_CALL FooterMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL FooterMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + +FooterMenuController::FooterMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + HeaderMenuController( xContext,true ) +{ +} + +FooterMenuController::~FooterMenuController() +{ +} +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_FooterMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new framework::FooterMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/genericstatusbarcontroller.cxx b/framework/source/uielement/genericstatusbarcontroller.cxx new file mode 100644 index 000000000..4a5aa4605 --- /dev/null +++ b/framework/source/uielement/genericstatusbarcontroller.cxx @@ -0,0 +1,157 @@ +/* -*- 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 <uielement/genericstatusbarcontroller.hxx> +#include <uielement/statusbarmerger.hxx> + +#include <osl/diagnose.h> +#include <vcl/svapp.hxx> + +#include <com/sun/star/ui/ItemStyle.hpp> +#include <com/sun/star/ui/XStatusbarItem.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/awt/ImageDrawMode.hpp> +#include <com/sun/star/awt/XGraphics2.hpp> +#include <com/sun/star/graphic/GraphicType.hpp> + +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; + +namespace framework +{ + +GenericStatusbarController::GenericStatusbarController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rxFrame, + const Reference< ui::XStatusbarItem >& rxItem, + AddonStatusbarItemData *pItemData ) + : svt::StatusbarController( rxContext, rxFrame, OUString(), 0 ) + , m_bEnabled( false ) + , m_bOwnerDraw( false ) + , m_pItemData( pItemData ) +{ + m_xStatusbarItem = rxItem; + if ( m_xStatusbarItem.is() ) + { + assert(m_aCommandURL.pData); + m_aCommandURL = m_xStatusbarItem->getCommand(); + m_nID = m_xStatusbarItem->getItemId(); + m_bOwnerDraw = ( m_xStatusbarItem->getStyle() & ui::ItemStyle::OWNER_DRAW ) == ui::ItemStyle::OWNER_DRAW; + if ( !m_bOwnerDraw && m_pItemData && m_pItemData->aLabel.getLength() ) + m_xStatusbarItem->setText( m_pItemData->aLabel ); + } +} + +GenericStatusbarController::~GenericStatusbarController() +{ +} + +void SAL_CALL GenericStatusbarController::dispose() +{ + svt::StatusbarController::dispose(); + + SolarMutexGuard aGuard; + m_pItemData = nullptr; + m_xGraphic.clear(); + m_xStatusbarItem.clear(); + +} + +void SAL_CALL GenericStatusbarController::statusChanged( + const FeatureStateEvent& rEvent) +{ + SolarMutexGuard aGuard; + + if ( m_bDisposed || !m_xStatusbarItem.is() ) + return; + + m_bEnabled = rEvent.IsEnabled; + + OUString aStrValue; + Reference< graphic::XGraphic > aGraphic; + + if ( rEvent.State >>= aStrValue ) + { + if ( !m_bOwnerDraw ) + m_xStatusbarItem->setText( aStrValue ); + else + { + if ( aStrValue.getLength() ) + { + m_xStatusbarItem->setQuickHelpText( aStrValue ); + } + } + } + else if ( ( rEvent.State >>= aGraphic ) && m_bOwnerDraw ) + { + m_xGraphic = aGraphic; + } + + // when the status is updated, and the controller is responsible for + // painting the statusbar item content, we must trigger a repaint + if ( m_bOwnerDraw && m_xStatusbarItem->getVisible() ) + { + m_xStatusbarItem->repaint(); + } +} + +void SAL_CALL GenericStatusbarController::paint( + const Reference< awt::XGraphics >& xGraphics, + const awt::Rectangle& rOutputRectangle, + ::sal_Int32 /*nStyle*/ ) +{ + SolarMutexGuard aGuard; + + const Reference< awt::XGraphics2 > xGraphics2(xGraphics, UNO_QUERY); + + if ( !m_xStatusbarItem.is() || !xGraphics2.is() ) + return; + + Reference< beans::XPropertySet > xGraphicProps( m_xGraphic, UNO_QUERY ); + + if ( xGraphicProps.is() && m_xGraphic->getType() != graphic::GraphicType::EMPTY ) + { + awt::Size aGraphicSize; + xGraphicProps->getPropertyValue( "SizePixel" ) >>= aGraphicSize; + OSL_ENSURE( aGraphicSize.Height > 0 && aGraphicSize.Width > 0, "Empty status bar graphic!" ); + + sal_Int32 nOffset = m_xStatusbarItem->getOffset( ); + awt::Point aPos; + aPos.X = ( rOutputRectangle.Width + nOffset ) / 2 - aGraphicSize.Width / 2; + aPos.Y = rOutputRectangle.Height / 2 - aGraphicSize.Height / 2; + + xGraphics2->drawImage( rOutputRectangle.X + aPos.X, + rOutputRectangle.Y + aPos.Y, + aGraphicSize.Width, + aGraphicSize.Height, + m_bEnabled ? awt::ImageDrawMode::NONE : awt::ImageDrawMode::DISABLE, + m_xGraphic ); + } + else + { + xGraphics2->clear( rOutputRectangle ); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/generictoolbarcontroller.cxx b/framework/source/uielement/generictoolbarcontroller.cxx new file mode 100644 index 000000000..25f2e0157 --- /dev/null +++ b/framework/source/uielement/generictoolbarcontroller.cxx @@ -0,0 +1,323 @@ +/* -*- 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 <framework/generictoolbarcontroller.hxx> + +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/frame/status/ItemStatus.hpp> +#include <com/sun/star/frame/status/Visibility.hpp> +#include <com/sun/star/frame/ControlCommand.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> +#include <vcl/weld.hxx> +#include <tools/urlobj.hxx> +#include <strings.hrc> +#include <classes/fwkresid.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::frame::status; + +namespace framework +{ + +static bool isEnumCommand( std::u16string_view rCommand ) +{ + INetURLObject aURL( rCommand ); + + return ( aURL.GetProtocol() == INetProtocol::Uno ) && + ( aURL.GetURLPath().indexOf( '.' ) != -1); +} + +static OUString getEnumCommand( std::u16string_view rCommand ) +{ + INetURLObject aURL( rCommand ); + + OUString aEnumCommand; + OUString aURLPath = aURL.GetURLPath(); + sal_Int32 nIndex = aURLPath.indexOf( '.' ); + if (( nIndex > 0 ) && ( nIndex < aURLPath.getLength() )) + aEnumCommand = aURLPath.copy( nIndex+1 ); + + return aEnumCommand; +} + +static OUString getMasterCommand( const OUString& rCommand ) +{ + OUString aMasterCommand( rCommand ); + INetURLObject aURL( rCommand ); + if ( aURL.GetProtocol() == INetProtocol::Uno ) + { + sal_Int32 nIndex = aURL.GetURLPath().indexOf( '.' ); + if ( nIndex ) + { + aURL.SetURLPath( aURL.GetURLPath().subView( 0, nIndex ) ); + aMasterCommand = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + } + return aMasterCommand; +} + +GenericToolbarController::GenericToolbarController( const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + ToolBox* pToolbar, + ToolBoxItemId nID, + const OUString& aCommand ) : + svt::ToolboxController( rxContext, rFrame, aCommand ) + , m_xToolbar( pToolbar ) + , m_nID( nID ) + , m_bEnumCommand( isEnumCommand( aCommand )) + , m_bMadeInvisible( false ) + , m_aEnumCommand( getEnumCommand( aCommand )) +{ + if ( m_bEnumCommand ) + addStatusListener( getMasterCommand( aCommand ) ); + + addStatusListener( aCommand ); + + // Initialization is done through ctor + m_bInitialized = true; +} + +GenericToolbarController::GenericToolbarController( const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + weld::Toolbar& rToolbar, + const OUString& aCommand ) : + GenericToolbarController( rxContext, rFrame, nullptr, ToolBoxItemId(0), aCommand ) +{ + m_pToolbar = &rToolbar; +} + +GenericToolbarController::~GenericToolbarController() +{ +} + +void SAL_CALL GenericToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + + svt::ToolboxController::dispose(); + + m_pToolbar = nullptr; + m_xToolbar.clear(); + m_nID = ToolBoxItemId(0); +} + +void SAL_CALL GenericToolbarController::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; + + css::util::URL aTargetURL; + + // Add key modifier to argument list + Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier) }; + + // handle also command aliases + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(m_aCommandURL, + vcl::CommandInfoProvider::GetModuleIdentifier(m_xFrame)); + OUString sRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties); + + aTargetURL.Complete = sRealCommand.isEmpty() ? aCommandURL : sRealCommand; + if ( m_xUrlTransformer.is() ) + m_xUrlTransformer->parseStrict( aTargetURL ); + + // Execute dispatch asynchronously + ExecuteInfo* pExecuteInfo = new ExecuteInfo; + pExecuteInfo->xDispatch = xDispatch; + pExecuteInfo->aTargetURL = aTargetURL; + pExecuteInfo->aArgs = aArgs; + Application::PostUserEvent( LINK(nullptr, GenericToolbarController , ExecuteHdl_Impl), pExecuteInfo ); +} + +void GenericToolbarController::statusChanged( const FeatureStateEvent& Event ) +{ + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + return; + + if ( m_pToolbar ) + { + OString sId = m_aCommandURL.toUtf8(); + + m_pToolbar->set_item_sensitive(sId, Event.IsEnabled); + + bool bValue; + OUString aStrValue; + + if ( Event.State >>= bValue ) + { + // Boolean, treat it as checked/unchecked + m_pToolbar->set_item_active(sId, bValue); + } + else if ( Event.State >>= aStrValue ) + { + m_pToolbar->set_item_label(sId, aStrValue); + } + else + m_pToolbar->set_item_active(sId, false); + + return; + } + + if ( !m_xToolbar ) + return; + + m_xToolbar->EnableItem( m_nID, Event.IsEnabled ); + + ToolBoxItemBits nItemBits = m_xToolbar->GetItemBits( m_nID ); + nItemBits &= ~ToolBoxItemBits::CHECKABLE; + TriState eTri = TRISTATE_FALSE; + + bool bValue; + OUString aStrValue; + ItemStatus aItemState; + Visibility aItemVisibility; + ControlCommand aControlCommand; + + if (( Event.State >>= bValue ) && !m_bEnumCommand ) + { + // Boolean, treat it as checked/unchecked + if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + m_xToolbar->CheckItem( m_nID, bValue ); + if ( bValue ) + eTri = TRISTATE_TRUE; + nItemBits |= ToolBoxItemBits::CHECKABLE; + } + else if ( Event.State >>= aStrValue ) + { + if ( m_bEnumCommand ) + { + bValue = aStrValue == m_aEnumCommand; + + m_xToolbar->CheckItem( m_nID, bValue ); + if ( bValue ) + eTri = TRISTATE_TRUE; + nItemBits |= ToolBoxItemBits::CHECKABLE; + } + else + { + // Replacement for place holders + if ( aStrValue.startsWith("($1)") ) + { + aStrValue = FwkResId(STR_UPDATEDOC) + " " + aStrValue.subView( 4 ); + } + else if ( aStrValue.startsWith("($2)") ) + { + aStrValue = FwkResId(STR_CLOSEDOC_ANDRETURN) + aStrValue.subView( 4 ); + } + else if ( aStrValue.startsWith("($3)") ) + { + aStrValue = FwkResId(STR_SAVECOPYDOC) + aStrValue.subView( 4 ); + } + m_xToolbar->SetItemText( m_nID, aStrValue ); + // tdf#124267 strip mnemonic from tooltip + m_xToolbar->SetQuickHelpText(m_nID, aStrValue.replaceFirst("~", "")); + } + + if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + } + else if (( Event.State >>= aItemState ) && !m_bEnumCommand ) + { + eTri = TRISTATE_INDET; + nItemBits |= ToolBoxItemBits::CHECKABLE; + if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + } + else if ( Event.State >>= aItemVisibility ) + { + m_xToolbar->ShowItem( m_nID, aItemVisibility.bVisible ); + m_bMadeInvisible = !aItemVisibility.bVisible; + } + else if ( Event.State >>= aControlCommand ) + { + if (aControlCommand.Command == "SetQuickHelpText") + { + for ( NamedValue const & rArg : std::as_const(aControlCommand.Arguments) ) + { + if (rArg.Name == "HelpText") + { + OUString aHelpText; + rArg.Value >>= aHelpText; + m_xToolbar->SetQuickHelpText(m_nID, aHelpText); + break; + } + } + } + if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + } + else if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + + m_xToolbar->SetItemState( m_nID, eTri ); + m_xToolbar->SetItemBits( m_nID, nItemBits ); +} + +IMPL_STATIC_LINK( GenericToolbarController, ExecuteHdl_Impl, void*, p, void ) +{ + ExecuteInfo* pExecuteInfo = static_cast<ExecuteInfo*>(p); + SolarMutexReleaser aReleaser; + try + { + // Asynchronous execution as this can lead to our own destruction! + // Framework can recycle our current frame and the layout manager disposes all user interface + // elements if a component gets detached from its frame! + pExecuteInfo->xDispatch->dispatch( pExecuteInfo->aTargetURL, pExecuteInfo->aArgs ); + } + catch ( const Exception& ) + { + } + + delete pExecuteInfo; +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/headermenucontroller.cxx b/framework/source/uielement/headermenucontroller.cxx new file mode 100644 index 000000000..cbcdc33bc --- /dev/null +++ b/framework/source/uielement/headermenucontroller.cxx @@ -0,0 +1,237 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <uielement/headermenucontroller.hxx> + +#include <services.h> + +#include <strings.hrc> +#include <classes/fwkresid.hxx> + +#include <com/sun/star/awt/MenuItemStyle.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <vcl/svapp.hxx> +#include <rtl/ustrbuf.hxx> +#include <osl/mutex.hxx> +#include <cppuhelper/supportsservice.hxx> + +// Defines + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace com::sun::star::style; +using namespace com::sun::star::container; + +const sal_uInt16 ALL_MENUITEM_ID = 1; + +namespace framework +{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL HeaderMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.HeaderMenuController"; +} + +sal_Bool SAL_CALL HeaderMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL HeaderMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + +HeaderMenuController::HeaderMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext, bool _bFooter ) : + svt::PopupMenuControllerBase( xContext ) + ,m_bFooter(_bFooter) +{ +} + +HeaderMenuController::~HeaderMenuController() +{ +} + +// private function +void HeaderMenuController::fillPopupMenu( const Reference< css::frame::XModel >& rModel, Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + SolarMutexGuard aSolarMutexGuard; + + resetPopupMenu( rPopupMenu ); + + Reference< XStyleFamiliesSupplier > xStyleFamiliesSupplier( rModel, UNO_QUERY ); + if (!xStyleFamiliesSupplier.is()) + return; + + Reference< XNameAccess > xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies(); + + OUString aCmd( ".uno:InsertPageHeader" ); + OUString aHeaderFooterIsOnStr( "HeaderIsOn" ); + if ( m_bFooter ) + { + aCmd = ".uno:InsertPageFooter"; + aHeaderFooterIsOnStr = "FooterIsOn"; + } + static const OUStringLiteral aIsPhysicalStr( u"IsPhysical" ); + static const OUStringLiteral aDisplayNameStr( u"DisplayName" ); + + try + { + Reference< XNameContainer > xNameContainer; + if ( xStyleFamilies->getByName("PageStyles") >>= xNameContainer ) + { + Sequence< OUString > aSeqNames = xNameContainer->getElementNames(); + + sal_uInt16 nId = 2; + sal_uInt16 nCount = 0; + bool bAllOneState( true ); + bool bLastCheck( true ); + bool bFirstChecked( false ); + bool bFirstItemInserted( false ); + for ( sal_Int32 n = 0; n < aSeqNames.getLength(); n++ ) + { + OUString aName = aSeqNames[n]; + Reference< XPropertySet > xPropSet( xNameContainer->getByName( aName ), UNO_QUERY ); + if ( xPropSet.is() ) + { + bool bIsPhysical( false ); + if (( xPropSet->getPropertyValue( aIsPhysicalStr ) >>= bIsPhysical ) && bIsPhysical ) + { + OUString aDisplayName; + bool bHeaderIsOn( false ); + xPropSet->getPropertyValue( aDisplayNameStr ) >>= aDisplayName; + xPropSet->getPropertyValue( aHeaderFooterIsOnStr ) >>= bHeaderIsOn; + + OUStringBuffer aStrBuf( aCmd ); + aStrBuf.append( "?PageStyle:string="); + aStrBuf.append( aDisplayName ); + aStrBuf.append( "&On:bool=" ); + if ( !bHeaderIsOn ) + aStrBuf.append( "true" ); + else + aStrBuf.append( "false" ); + OUString aCommand( aStrBuf.makeStringAndClear() ); + rPopupMenu->insertItem(nId, aDisplayName, css::awt::MenuItemStyle::CHECKABLE, nCount); + if ( !bFirstItemInserted ) + { + bFirstItemInserted = true; + bFirstChecked = bHeaderIsOn; + } + + rPopupMenu->setCommand(nId, aCommand); + rPopupMenu->checkItem(nId, bHeaderIsOn); + ++nId; + + // Check if all entries have the same state + if( bAllOneState && n && bHeaderIsOn != bLastCheck ) + bAllOneState = false; + bLastCheck = bHeaderIsOn; + ++nCount; + } + } + } + + if ( bAllOneState && ( nCount > 1 )) + { + // Insert special item for all command + rPopupMenu->insertItem(ALL_MENUITEM_ID, FwkResId(STR_MENU_HEADFOOTALL), 0, 0); + + OUStringBuffer aStrBuf( aCmd ); + aStrBuf.append( "?On:bool=" ); + + // Command depends on check state of first menu item entry + if ( !bFirstChecked ) + aStrBuf.append( "true" ); + else + aStrBuf.append( "false" ); + + rPopupMenu->setCommand(1, aStrBuf.makeStringAndClear()); + rPopupMenu->insertSeparator(1); + } + } + } + catch ( const css::container::NoSuchElementException& ) + { + } +} + +// XEventListener +void SAL_CALL HeaderMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + osl::MutexGuard aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL HeaderMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + Reference< css::frame::XModel > xModel; + + if ( Event.State >>= xModel ) + { + osl::MutexGuard aLock( m_aMutex ); + m_xModel = xModel; + if ( m_xPopupMenu.is() ) + fillPopupMenu( xModel, m_xPopupMenu ); + } +} + +// XMenuListener +void SAL_CALL HeaderMenuController::updatePopupMenu() +{ + osl::ResettableMutexGuard aLock( m_aMutex ); + + throwIfDisposed(); + + Reference< css::frame::XModel > xModel( m_xModel ); + aLock.clear(); + + if ( !xModel.is() ) + svt::PopupMenuControllerBase::updatePopupMenu(); + + aLock.reset(); + if ( m_xPopupMenu.is() && m_xModel.is() ) + fillPopupMenu( m_xModel, m_xPopupMenu ); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_HeaderMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new framework::HeaderMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/imagebuttontoolbarcontroller.cxx b/framework/source/uielement/imagebuttontoolbarcontroller.cxx new file mode 100644 index 000000000..1ab3c0cf8 --- /dev/null +++ b/framework/source/uielement/imagebuttontoolbarcontroller.cxx @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <uielement/imagebuttontoolbarcontroller.hxx> + +#include <framework/addonsoptions.hxx> + +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <comphelper/getexpandeduri.hxx> +#include <comphelper/processfactory.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <vcl/svapp.hxx> +#include <vcl/graph.hxx> +#include <vcl/graphicfilter.hxx> +#include <vcl/toolbox.hxx> +#include <svtools/miscopt.hxx> +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::util; + +const ::Size aImageSizeSmall( 16, 16 ); +const ::Size aImageSizeBig( 26, 26 ); + +namespace framework +{ + +static void SubstituteVariables( OUString& aURL ) +{ + aURL = comphelper::getExpandedUri( + comphelper::getProcessComponentContext(), aURL); +} + +ImageButtonToolbarController::ImageButtonToolbarController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + ToolBox* pToolbar, + ToolBoxItemId nID, + const OUString& aCommand ) : + ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand ) +{ + bool bBigImages( SvtMiscOptions().AreCurrentSymbolsLarge() ); + + Image aImage(AddonsOptions().GetImageFromURL(aCommand, bBigImages, true)); + + // Height will be controlled by scaling according to button height + m_xToolbar->SetItemImage( m_nID, aImage ); +} + +ImageButtonToolbarController::~ImageButtonToolbarController() +{ +} + +void SAL_CALL ImageButtonToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + ComplexToolbarController::dispose(); +} + +void ImageButtonToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand ) +{ + SolarMutexGuard aSolarMutexGuard; + // i73486 to be downward compatible use old and "wrong" also! + if( rControlCommand.Command != "SetImag" && + rControlCommand.Command != "SetImage" ) + return; + + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "URL" ) + { + OUString aURL; + rArg.Value >>= aURL; + + SubstituteVariables( aURL ); + + Image aImage; + if ( ReadImageFromURL( SvtMiscOptions().AreCurrentSymbolsLarge(), + aURL, + aImage )) + { + m_xToolbar->SetItemImage( m_nID, aImage ); + + // send notification + uno::Sequence< beans::NamedValue > aInfo { { "URL", css::uno::Any(aURL) } }; + addNotifyInfo( "ImageChanged", + getDispatchFromCommand( m_aCommandURL ), + aInfo ); + break; + } + } + } +} + +bool ImageButtonToolbarController::ReadImageFromURL( bool bBigImage, const OUString& aImageURL, Image& aImage ) +{ + std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( aImageURL, StreamMode::STD_READ )); + if ( !pStream || ( pStream->GetErrorCode() != ERRCODE_NONE )) + return false; + + // Use graphic class to also support more graphic formats (bmp,png,...) + Graphic aGraphic; + + GraphicFilter& rGF = GraphicFilter::GetGraphicFilter(); + rGF.ImportGraphic( aGraphic, u"", *pStream ); + + BitmapEx aBitmapEx = aGraphic.GetBitmapEx(); + + const ::Size aSize = bBigImage ? aImageSizeBig : aImageSizeSmall; // Sizes used for toolbar images + + ::Size aBmpSize = aBitmapEx.GetSizePixel(); + if ( !aBmpSize.IsEmpty() ) + { + ::Size aNoScaleSize( aBmpSize.Width(), aSize.Height() ); + if ( aBmpSize != aNoScaleSize ) + aBitmapEx.Scale( aNoScaleSize, BmpScaleFlag::BestQuality ); + aImage = Image( aBitmapEx ); + return true; + } + + return false; +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/langselectionmenucontroller.cxx b/framework/source/uielement/langselectionmenucontroller.cxx new file mode 100644 index 000000000..94918c021 --- /dev/null +++ b/framework/source/uielement/langselectionmenucontroller.cxx @@ -0,0 +1,294 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <uielement/langselectionmenucontroller.hxx> + +#include <services.h> + +#include <com/sun/star/awt/MenuItemStyle.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> + +#include <vcl/svapp.hxx> + +#include <svl/languageoptions.hxx> +#include <svtools/langtab.hxx> +#include <classes/fwkresid.hxx> + +#include <strings.hrc> + +#include <helper/mischelper.hxx> +#include <osl/mutex.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <map> +#include <set> + +// Defines + +using namespace ::com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; + +namespace framework +{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL LanguageSelectionMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.LanguageSelectionMenuController"; +} + +sal_Bool SAL_CALL LanguageSelectionMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL LanguageSelectionMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + + +LanguageSelectionMenuController::LanguageSelectionMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) + : svt::PopupMenuControllerBase(xContext) + , m_bShowMenu(true) + , m_nScriptType(SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX) + , m_aLangGuessHelper(xContext) +{ +} + +LanguageSelectionMenuController::~LanguageSelectionMenuController() +{ +} + +// XEventListener +void SAL_CALL LanguageSelectionMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + osl::MutexGuard aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + m_xLanguageDispatch.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL LanguageSelectionMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + SolarMutexGuard aSolarMutexGuard; + + if (rBHelper.bDisposed || rBHelper.bInDispose) + return; + + m_bShowMenu = true; + m_nScriptType = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX; //set the default value + + Sequence< OUString > aSeq; + + if ( Event.State >>= aSeq ) + { + if ( aSeq.getLength() == 4 ) + { + // Retrieve all other values from the sequence and + // store it members! + m_aCurLang = aSeq[0]; + m_nScriptType = static_cast< SvtScriptType >(aSeq[1].toInt32()); + m_aKeyboardLang = aSeq[2]; + m_aGuessedTextLang = aSeq[3]; + } + } + else if ( !Event.State.hasValue() ) + { + m_bShowMenu = false; // no language -> no sub-menu entries -> disable menu + } +} + +// XPopupMenuController +void LanguageSelectionMenuController::impl_setPopupMenu() +{ + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + + css::util::URL aTargetURL; + + // Register for language updates + aTargetURL.Complete = m_aLangStatusCommandURL; + m_xURLTransformer->parseStrict( aTargetURL ); + m_xLanguageDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + + // Register for setting languages and opening language dialog + aTargetURL.Complete = m_aMenuCommandURL_Lang; + m_xURLTransformer->parseStrict( aTargetURL ); + m_xMenuDispatch_Lang = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + + // Register for opening character dialog + aTargetURL.Complete = m_aMenuCommandURL_Font; + m_xURLTransformer->parseStrict( aTargetURL ); + m_xMenuDispatch_Font = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + + // Register for opening character dialog with preselected paragraph + aTargetURL.Complete = m_aMenuCommandURL_CharDlgForParagraph; + m_xURLTransformer->parseStrict( aTargetURL ); + m_xMenuDispatch_CharDlgForParagraph = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); +} + +void LanguageSelectionMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu , const Mode eMode ) +{ + SolarMutexGuard aSolarMutexGuard; + + resetPopupMenu( rPopupMenu ); + if (!m_bShowMenu) + return; + + OUString aCmd_Dialog; + OUString aCmd_Language; + if( eMode == MODE_SetLanguageSelectionMenu ) + { + aCmd_Dialog += ".uno:FontDialog?Page:string=font"; + aCmd_Language += ".uno:LanguageStatus?Language:string=Current_"; + } + else if ( eMode == MODE_SetLanguageParagraphMenu ) + { + aCmd_Dialog += ".uno:FontDialogForParagraph"; + aCmd_Language += ".uno:LanguageStatus?Language:string=Paragraph_"; + } + else if ( eMode == MODE_SetLanguageAllTextMenu ) + { + aCmd_Dialog += ".uno:LanguageStatus?Language:string=*"; + aCmd_Language += ".uno:LanguageStatus?Language:string=Default_"; + } + + // get languages to be displayed in the menu + std::set< OUString > aLangItems; + FillLangItems( aLangItems, m_xFrame, m_aLangGuessHelper, + m_nScriptType, m_aCurLang, m_aKeyboardLang, m_aGuessedTextLang ); + + // now add menu entries + // the different menus purpose will be handled by the different string + // for aCmd_Dialog and aCmd_Language + sal_Int16 nItemId = 0; // in this control the item id is not important for executing the command + static const OUStringLiteral sAsterisk(u"*"); // multiple languages in current selection + const OUString sNone( SvtLanguageTable::GetLanguageString( LANGUAGE_NONE )); + for (auto const& langItem : aLangItems) + { + if (langItem != sNone && + langItem != sAsterisk && + !langItem.isEmpty()) // 'no language found' from language guessing + { + ++nItemId; + rPopupMenu->insertItem(nItemId, langItem, css::awt::MenuItemStyle::CHECKABLE, nItemId - 1); + OUString aCmd = aCmd_Language + langItem; + rPopupMenu->setCommand(nItemId, aCmd); + bool bChecked = langItem == m_aCurLang && eMode == MODE_SetLanguageSelectionMenu; + //make a sign for the current language + rPopupMenu->checkItem(nItemId, bChecked); + } + } + + // entry for LANGUAGE_NONE + ++nItemId; + rPopupMenu->insertItem(nItemId, FwkResId(STR_LANGSTATUS_NONE), 0, nItemId - 1); + OUString aCmd = aCmd_Language + "LANGUAGE_NONE"; + rPopupMenu->setCommand(nItemId, aCmd); + + // entry for 'Reset to default language' + ++nItemId; + rPopupMenu->insertItem(nItemId, FwkResId(STR_RESET_TO_DEFAULT_LANGUAGE), 0, nItemId - 1); + aCmd = aCmd_Language + "RESET_LANGUAGES"; + rPopupMenu->setCommand(nItemId, aCmd); + + // entry for opening the Format/Character dialog + ++nItemId; + rPopupMenu->insertItem(nItemId, FwkResId(STR_LANGSTATUS_MORE), 0, nItemId - 1); + rPopupMenu->setCommand(nItemId, aCmd_Dialog); +} + +void SAL_CALL LanguageSelectionMenuController::updatePopupMenu() +{ + svt::PopupMenuControllerBase::updatePopupMenu(); + + // Force status update to get information about the current languages + osl::ClearableMutexGuard aLock( m_aMutex ); + Reference< XDispatch > xDispatch( m_xLanguageDispatch ); + css::util::URL aTargetURL; + aTargetURL.Complete = m_aLangStatusCommandURL; + m_xURLTransformer->parseStrict( aTargetURL ); + aLock.clear(); + + if ( xDispatch.is() ) + { + xDispatch->addStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + xDispatch->removeStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + } + + // TODO: Fill menu with the information retrieved by the status update + + if ( m_aCommandURL == ".uno:SetLanguageSelectionMenu" ) + { + fillPopupMenu(m_xPopupMenu, MODE_SetLanguageSelectionMenu ); + } + else if ( m_aCommandURL == ".uno:SetLanguageParagraphMenu" ) + { + fillPopupMenu(m_xPopupMenu, MODE_SetLanguageParagraphMenu ); + } + else if ( m_aCommandURL == ".uno:SetLanguageAllTextMenu" ) + { + fillPopupMenu(m_xPopupMenu, MODE_SetLanguageAllTextMenu ); + } +} + +// XInitialization +void SAL_CALL LanguageSelectionMenuController::initialize( const Sequence< Any >& aArguments ) +{ + osl::MutexGuard aLock( m_aMutex ); + + bool bInitialized( m_bInitialized ); + if ( !bInitialized ) + { + svt::PopupMenuControllerBase::initialize(aArguments); + + if ( m_bInitialized ) + { + m_aLangStatusCommandURL = ".uno:LanguageStatus"; + m_aMenuCommandURL_Lang = m_aLangStatusCommandURL; + m_aMenuCommandURL_Font = ".uno:FontDialog"; + m_aMenuCommandURL_CharDlgForParagraph = ".uno:FontDialogForParagraph"; + } + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_LanguageSelectionMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new framework::LanguageSelectionMenuController(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/langselectionstatusbarcontroller.cxx b/framework/source/uielement/langselectionstatusbarcontroller.cxx new file mode 100644 index 000000000..807806364 --- /dev/null +++ b/framework/source/uielement/langselectionstatusbarcontroller.cxx @@ -0,0 +1,359 @@ +/* -*- 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 <classes/fwkresid.hxx> +#include <services.h> +#include <strings.hrc> +#include <vcl/svapp.hxx> + +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/awt/PopupMenu.hpp> +#include <com/sun/star/awt/PopupMenuDirection.hpp> +#include <svtools/langtab.hxx> +#include <svtools/statusbarcontroller.hxx> +#include <sal/types.h> +#include <sal/log.hxx> +#include <com/sun/star/document/XDocumentLanguages.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/ui/XStatusbarItem.hpp> + +#include <com/sun/star/frame/XFrame.hpp> + +#include <com/sun/star/awt/Command.hpp> +#include <svl/languageoptions.hxx> + +#include <helper/mischelper.hxx> + +#include <rtl/ustrbuf.hxx> + +#include <map> +#include <set> + +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace css::uno; +using namespace css::lang; +using namespace css::frame; +using namespace css::i18n; +using namespace css::document; +using namespace framework; + +namespace { + +class LangSelectionStatusbarController: + public svt::StatusbarController +{ +public: + explicit LangSelectionStatusbarController( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + LangSelectionStatusbarController(const LangSelectionStatusbarController&) = delete; + LangSelectionStatusbarController& operator=(const LangSelectionStatusbarController&) = delete; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + // XStatusbarController + virtual void SAL_CALL command( const css::awt::Point& aPos, + ::sal_Int32 nCommand, + sal_Bool bMouseEvent, + const css::uno::Any& aData ) override; + virtual void SAL_CALL click( const css::awt::Point& aPos ) override; + +private: + virtual ~LangSelectionStatusbarController() override {} + + bool m_bShowMenu; // if the menu is to be displayed or not (depending on the selected object/text) + SvtScriptType m_nScriptType; // the flags for the different script types available in the selection, LATIN = 0x0001, ASIAN = 0x0002, COMPLEX = 0x0004 + OUString m_aCurLang; // the language of the current selection, "*" if there are more than one languages + OUString m_aKeyboardLang; // the keyboard language + OUString m_aGuessedTextLang; // the 'guessed' language for the selection, "" if none could be guessed + LanguageGuessingHelper m_aLangGuessHelper; + + /// @throws css::uno::RuntimeException + void LangMenu( const css::awt::Point& aPos ); +}; + +LangSelectionStatusbarController::LangSelectionStatusbarController( const uno::Reference< uno::XComponentContext >& xContext ) : + svt::StatusbarController( xContext, uno::Reference< frame::XFrame >(), OUString(), 0 ), + m_bShowMenu( true ), + m_nScriptType( SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX ), + m_aLangGuessHelper( xContext ) +{ +} + +void SAL_CALL LangSelectionStatusbarController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) +{ + SolarMutexGuard aSolarMutexGuard; + + svt::StatusbarController::initialize( aArguments ); + + if ( m_xStatusbarItem.is() ) + { + m_xStatusbarItem->setText( FwkResId(STR_LANGSTATUS_MULTIPLE_LANGUAGES) ); + m_xStatusbarItem->setQuickHelpText(FwkResId(STR_LANGSTATUS_HINT)); + } +} + +void LangSelectionStatusbarController::LangMenu( + const css::awt::Point& aPos ) +{ + if (!m_bShowMenu) + return; + + const Reference<XServiceInfo> xService(m_xFrame->getController()->getModel(), UNO_QUERY); + bool bWriter = xService.is() && xService->supportsService("com.sun.star.text.GenericTextDocument"); + //add context menu + Reference< awt::XPopupMenu > xPopupMenu( awt::PopupMenu::create( m_xContext ) ); + //sub menu that contains all items except the last two items: Separator + Set Language for Paragraph + Reference< awt::XPopupMenu > subPopupMenu( awt::PopupMenu::create( m_xContext ) ); + + // get languages to be displayed in the menu + std::set< OUString > aLangItems; + FillLangItems( aLangItems, m_xFrame, m_aLangGuessHelper, + m_nScriptType, m_aCurLang, m_aKeyboardLang, m_aGuessedTextLang ); + + // add first few entries to main menu + sal_Int16 nItemId = static_cast< sal_Int16 >(MID_LANG_SEL_1); + static const OUStringLiteral sAsterisk(u"*"); // multiple languages in current selection + const OUString sNone( SvtLanguageTable::GetLanguageString( LANGUAGE_NONE )); + std::map< sal_Int16, OUString > aLangMap; + for (auto const& langItem : aLangItems) + { + if ( langItem != sNone && + langItem != sAsterisk && + !langItem.isEmpty()) // 'no language found' from language guessing + { + SAL_WARN_IF( MID_LANG_SEL_1 > nItemId || nItemId > MID_LANG_SEL_9, + "fwk.uielement", "nItemId outside of expected range!" ); + xPopupMenu->insertItem( nItemId, langItem, 0, nItemId ); + if ( langItem == m_aCurLang ) + { + //make a sign for the current language + xPopupMenu->checkItem( nItemId, true ); + } + aLangMap[ nItemId ] = langItem; + ++nItemId; + } + } + + if (bWriter) + { + xPopupMenu->insertItem( MID_LANG_SEL_NONE, FwkResId(STR_LANGSTATUS_NONE), 0, MID_LANG_SEL_NONE ); + if ( sNone == m_aCurLang ) + xPopupMenu->checkItem( MID_LANG_SEL_NONE, true ); + xPopupMenu->insertItem( MID_LANG_SEL_RESET, FwkResId(STR_RESET_TO_DEFAULT_LANGUAGE), 0, MID_LANG_SEL_RESET ); + xPopupMenu->insertItem( MID_LANG_SEL_MORE, FwkResId(STR_LANGSTATUS_MORE), 0, MID_LANG_SEL_MORE ); + + // add entries to submenu ('set language for paragraph') + nItemId = static_cast< sal_Int16 >(MID_LANG_PARA_1); + for (auto const& langItem : aLangItems) + { + if( langItem != sNone && + langItem != sAsterisk && + !langItem.isEmpty()) // 'no language found' from language guessing + { + SAL_WARN_IF( MID_LANG_PARA_1 > nItemId || nItemId > MID_LANG_PARA_9, + "fwk.uielement", "nItemId outside of expected range!" ); + subPopupMenu->insertItem( nItemId, langItem, 0, nItemId ); + aLangMap[nItemId] = langItem; + ++nItemId; + } + } + subPopupMenu->insertItem( MID_LANG_PARA_NONE, FwkResId(STR_LANGSTATUS_NONE), 0, MID_LANG_PARA_NONE ); + subPopupMenu->insertItem( MID_LANG_PARA_RESET, FwkResId(STR_RESET_TO_DEFAULT_LANGUAGE), 0, MID_LANG_PARA_RESET ); + subPopupMenu->insertItem( MID_LANG_PARA_MORE, FwkResId(STR_LANGSTATUS_MORE), 0, MID_LANG_PARA_MORE ); + + // add last two entries to main menu + xPopupMenu->insertSeparator( MID_LANG_PARA_SEPARATOR ); + xPopupMenu->insertItem( MID_LANG_PARA_STRING, FwkResId(STR_SET_LANGUAGE_FOR_PARAGRAPH), 0, MID_LANG_PARA_STRING ); + xPopupMenu->setPopupMenu( MID_LANG_PARA_STRING, subPopupMenu ); + } + else + { + xPopupMenu->insertItem( MID_LANG_DEF_NONE, FwkResId(STR_LANGSTATUS_NONE), 0, MID_LANG_DEF_NONE ); + if ( sNone == m_aCurLang ) + xPopupMenu->checkItem( MID_LANG_DEF_NONE, true ); + xPopupMenu->insertItem( MID_LANG_DEF_RESET, FwkResId(STR_RESET_TO_DEFAULT_LANGUAGE), 0, MID_LANG_DEF_RESET ); + xPopupMenu->insertItem( MID_LANG_DEF_MORE, FwkResId(STR_LANGSTATUS_MORE), 0, MID_LANG_DEF_MORE ); + } + + // now display the popup menu and execute every command ... + + Reference< awt::XWindowPeer > xParent( m_xParentWindow, UNO_QUERY ); + css::awt::Rectangle aRect( aPos.X, aPos.Y, 0, 0 ); + sal_Int16 nId = xPopupMenu->execute( xParent, aRect, css::awt::PopupMenuDirection::EXECUTE_UP+16 ); + //click "More..." + if ( !(nId && m_xFrame.is()) ) + return; + + OUStringBuffer aBuff; + //set selected language as current language for selection + const OUString aSelectedLang = aLangMap[nId]; + + if (MID_LANG_SEL_1 <= nId && nId <= MID_LANG_SEL_9) + { + if (bWriter) + aBuff.append( ".uno:LanguageStatus?Language:string=Current_" ); + else + aBuff.append( ".uno:LanguageStatus?Language:string=Default_" ); + + aBuff.append( aSelectedLang ); + } + else if (nId == MID_LANG_SEL_NONE) + { + //set None as current language for selection + aBuff.append( ".uno:LanguageStatus?Language:string=Current_LANGUAGE_NONE" ); + } + else if (nId == MID_LANG_SEL_RESET) + { + // reset language attributes for selection + aBuff.append( ".uno:LanguageStatus?Language:string=Current_RESET_LANGUAGES" ); + } + else if (nId == MID_LANG_SEL_MORE) + { + //open the dialog "format/character" for current selection + aBuff.append( ".uno:FontDialog?Page:string=font" ); + } + else if (nId == MID_LANG_DEF_NONE) + { + aBuff.append( ".uno:LanguageStatus?Language:string=Default_LANGUAGE_NONE" ); + } + else if (nId == MID_LANG_DEF_RESET) + { + aBuff.append( ".uno:LanguageStatus?Language:string=Default_RESET_LANGUAGES" ); + } + else if (nId == MID_LANG_DEF_MORE) + { + aBuff.append( ".uno:LanguageStatus?Language:string=*" ); + } + else if (MID_LANG_PARA_1 <= nId && nId <= MID_LANG_PARA_9) + { + aBuff.append( ".uno:LanguageStatus?Language:string=Paragraph_" ); + aBuff.append( aSelectedLang ); + } + else if (nId == MID_LANG_PARA_NONE) + { + //set None as language for current paragraph + aBuff.append( ".uno:LanguageStatus?Language:string=Paragraph_LANGUAGE_NONE" ); + } + else if (nId == MID_LANG_PARA_RESET) + { + // reset language attributes for paragraph + aBuff.append( ".uno:LanguageStatus?Language:string=Paragraph_RESET_LANGUAGES" ); + } + else if (nId == MID_LANG_PARA_MORE) + { + //open the dialog "format/character" for current paragraph + aBuff.append( ".uno:FontDialogForParagraph" ); + } + + const Sequence< beans::PropertyValue > aDummyArgs; + execute( aBuff.makeStringAndClear(), aDummyArgs ); +} + +void SAL_CALL LangSelectionStatusbarController::command( + const css::awt::Point& aPos, + ::sal_Int32 nCommand, + sal_Bool /*bMouseEvent*/, + const css::uno::Any& /*aData*/ ) +{ + if ( nCommand & ::awt::Command::CONTEXTMENU ) + { + LangMenu( aPos ); + } +} + +void SAL_CALL LangSelectionStatusbarController::click( + const css::awt::Point& aPos ) +{ + LangMenu( aPos ); +} + +// XStatusListener +void SAL_CALL LangSelectionStatusbarController::statusChanged( const FeatureStateEvent& Event ) +{ + // This function will be called when observed data changes, + // for example the selection or keyboard language. + // - It displays the language in use in the status bar + // - and it stores the relevant data for creating the menu + // at some later point in the member variables + // m_nScriptType, m_aCurLang, m_aKeyboardLang, m_aGuessedText + + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + return; + + m_bShowMenu = true; + m_nScriptType = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX; //set the default value + + if ( !m_xStatusbarItem.is() ) + return; + + OUString aStrValue; + Sequence< OUString > aSeq; + + if ( Event.State >>= aStrValue ) + { + m_xStatusbarItem->setText( aStrValue ); + m_xStatusbarItem->setQuickHelpText(FwkResId(STR_LANGSTATUS_HINT)); + m_aCurLang = aStrValue; + } + else if ( Event.State >>= aSeq ) + { + if ( aSeq.getLength() == 4 ) + { + OUString aStatusText = aSeq[0]; + if (aStatusText == "*") + { + aStatusText = FwkResId(STR_LANGSTATUS_MULTIPLE_LANGUAGES); + } + m_xStatusbarItem->setText( aStatusText ); + m_xStatusbarItem->setQuickHelpText(FwkResId(STR_LANGSTATUS_HINT)); + + // Retrieve all other values from the sequence and + // store it members! + m_aCurLang = aSeq[0]; + m_nScriptType = static_cast< SvtScriptType >( aSeq[1].toInt32() ); + m_aKeyboardLang = aSeq[2]; + m_aGuessedTextLang = aSeq[3]; + } + } + else if ( !Event.State.hasValue() ) + { + m_xStatusbarItem->setText( OUString() ); + m_xStatusbarItem->setQuickHelpText(u""); + m_bShowMenu = false; // no language -> no menu + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_LangSelectionStatusbarController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new LangSelectionStatusbarController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/macrosmenucontroller.cxx b/framework/source/uielement/macrosmenucontroller.cxx new file mode 100644 index 000000000..a0745b1e4 --- /dev/null +++ b/framework/source/uielement/macrosmenucontroller.cxx @@ -0,0 +1,170 @@ +/* -*- 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 <uielement/macrosmenucontroller.hxx> +#include <services.h> +#include <com/sun/star/container/XContentEnumerationAccess.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <officecfg/Office/Common.hxx> +#include <vcl/svapp.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <osl/mutex.hxx> +#include <cppuhelper/supportsservice.hxx> + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace com::sun::star::style; +using namespace com::sun::star::container; + +namespace framework +{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL MacrosMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.MacrosMenuController"; +} + +sal_Bool SAL_CALL MacrosMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL MacrosMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + +MacrosMenuController::MacrosMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + svt::PopupMenuControllerBase( xContext ), + m_xContext( xContext) +{ +} + +MacrosMenuController::~MacrosMenuController() +{ +} + +// private function +void MacrosMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + bool bMacrosDisabled = officecfg::Office::Common::Security::Scripting::DisableMacrosExecution::get(); + if (bMacrosDisabled) + return; + + SolarMutexGuard aSolarMutexGuard; + + resetPopupMenu(rPopupMenu); + assert(rPopupMenu->getItemCount() == 0); + + // insert basic + OUString aCommand(".uno:MacroDialog"); + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommand, m_aModuleName); + OUString aDisplayName = vcl::CommandInfoProvider::GetMenuLabelForCommand(aProperties); + rPopupMenu->insertItem(2, aDisplayName, 0, 0); + rPopupMenu->setCommand(2, aCommand); + + // insert providers but not basic or java + addScriptItems(rPopupMenu, 4); +} + +// XEventListener +void SAL_CALL MacrosMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + osl::MutexGuard aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + m_xContext.clear(); + + if ( m_xPopupMenu.is() ) + { + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + } + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL MacrosMenuController::statusChanged( const FeatureStateEvent& ) +{ + osl::MutexGuard aLock( m_aMutex ); + if ( m_xPopupMenu.is() ) + { + fillPopupMenu( m_xPopupMenu ); + } +} + +void MacrosMenuController::addScriptItems(const Reference<css::awt::XPopupMenu>& rPopupMenu, sal_uInt16 startItemId) +{ + static const OUStringLiteral aCmdBase(u".uno:ScriptOrganizer?ScriptOrganizer.Language:string="); + static const OUStringLiteral ellipsis( u"..." ); + static const OUStringLiteral providerKey(u"com.sun.star.script.provider.ScriptProviderFor"); + sal_uInt16 itemId = startItemId; + Reference< XContentEnumerationAccess > xEnumAccess( m_xContext->getServiceManager(), UNO_QUERY_THROW ); + Reference< XEnumeration > xEnum = xEnumAccess->createContentEnumeration ( "com.sun.star.script.provider.LanguageScriptProvider" ); + + sal_Int16 nPos = rPopupMenu->getItemCount(); + + while ( xEnum->hasMoreElements() ) + { + Reference< XServiceInfo > xServiceInfo; + if ( !( xEnum->nextElement() >>= xServiceInfo ) ) + { + break; + } + const Sequence< OUString > serviceNames = xServiceInfo->getSupportedServiceNames(); + + for ( OUString const & serviceName : serviceNames ) + { + if ( serviceName.startsWith( providerKey ) ) + { + OUString aCommand = aCmdBase; + OUString aDisplayName = serviceName.copy( providerKey.getLength() ); + if( aDisplayName == "Java" || aDisplayName == "Basic" ) + { + // no entries for Java & Basic added elsewhere + break; + } + aCommand += aDisplayName; + aDisplayName += ellipsis; + rPopupMenu->insertItem(itemId, aDisplayName, 0, nPos++); + rPopupMenu->setCommand(itemId, aCommand); + itemId++; + break; + } + } + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_MacrosMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new framework::MacrosMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/menubarmanager.cxx b/framework/source/uielement/menubarmanager.cxx new file mode 100644 index 000000000..c7efdb920 --- /dev/null +++ b/framework/source/uielement/menubarmanager.cxx @@ -0,0 +1,1572 @@ +/* -*- 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 <uielement/menubarmanager.hxx> +#include <uielement/styletoolbarcontroller.hxx> +#include <menuconfiguration.hxx> +#include <addonmenu.hxx> +#include <framework/addonsoptions.hxx> +#include <classes/fwkresid.hxx> +#include <strings.hrc> + +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/XCurrentContext.hpp> +#include <com/sun/star/frame/XPopupMenuController.hpp> +#include <com/sun/star/frame/thePopupMenuControllerFactory.hpp> +#include <com/sun/star/lang/SystemDependent.hpp> +#include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp> +#include <com/sun/star/ui/ItemType.hpp> +#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/ItemStyle.hpp> +#include <com/sun/star/frame/status/Visibility.hpp> +#include <com/sun/star/util/URLTransformer.hpp> + +#include <comphelper/propertysequence.hxx> +#include <comphelper/propertyvalue.hxx> +#include <officecfg/Office/Common.hxx> +#include <svtools/javainteractionhandler.hxx> +#include <uno/current_context.hxx> +#include <unotools/cmdoptions.hxx> +#include <toolkit/awt/vclxmenu.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <vcl/sysdata.hxx> +#include <vcl/menu.hxx> +#include <vcl/settings.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <sal/log.hxx> +#include <svtools/acceleratorexecute.hxx> +#include <svtools/miscopt.hxx> +#include <uielement/menubarmerger.hxx> +#include <tools/urlobj.hxx> + +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ui; + +const sal_uInt16 ADDONMENU_MERGE_ITEMID_START = 1500; +const sal_uInt16 ITEMID_ADDONLIST = 6678; // used to be a SID in sfx2, now just a unique id... + +namespace framework +{ + +constexpr OUStringLiteral aCmdHelpIndex = u".uno:HelpIndex"; +constexpr OUStringLiteral aCmdToolsMenu = u".uno:ToolsMenu"; +constexpr OUStringLiteral aCmdHelpMenu = u".uno:HelpMenu"; +constexpr OUStringLiteral aSpecialWindowCommand = u".uno:WindowList"; + +MenuBarManager::MenuBarManager( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + const Reference< XURLTransformer >& _xURLTransformer, + const Reference< XDispatchProvider >& rDispatchProvider, + const OUString& rModuleIdentifier, + Menu* pMenu, bool bDelete, bool bHasMenuBar ): + m_bRetrieveImages( false ) + , m_bAcceleratorCfg( false ) + , m_bHasMenuBar( bHasMenuBar ) + , m_xContext(rxContext) + , m_xURLTransformer(_xURLTransformer) + , m_sIconTheme( SvtMiscOptions().GetIconTheme() ) + , m_aAsyncSettingsTimer( "framework::MenuBarManager::Deactivate m_aAsyncSettingsTimer" ) +{ + m_xPopupMenuControllerFactory = frame::thePopupMenuControllerFactory::get(m_xContext); + FillMenuManager( pMenu, rFrame, rDispatchProvider, rModuleIdentifier, bDelete ); +} + +Any SAL_CALL MenuBarManager::getMenuHandle( const Sequence< sal_Int8 >& /*ProcessId*/, sal_Int16 SystemType ) +{ + SolarMutexGuard aSolarGuard; + + if ( m_bDisposed ) + throw css::lang::DisposedException(); + + Any a; + + if ( m_pVCLMenu ) + { + SystemMenuData aSystemMenuData; + + m_pVCLMenu->GetSystemMenuData( &aSystemMenuData ); +#ifdef _WIN32 + if( SystemType == SystemDependent::SYSTEM_WIN32 ) + { + a <<= sal_Int64( + reinterpret_cast<sal_IntPtr>(aSystemMenuData.hMenu)); + } +#else + (void) SystemType; +#endif + } + + return a; +} + +MenuBarManager::~MenuBarManager() +{ + // stop asynchronous settings timer + m_xDeferredItemContainer.clear(); + m_aAsyncSettingsTimer.Stop(); + + SAL_WARN_IF( OWeakObject::m_refCount != 0, "fwk.uielement", "Who wants to delete an object with refcount > 0!" ); +} + +// XComponent +void MenuBarManager::disposing(std::unique_lock<std::mutex>& ) +{ + Reference< XComponent > xThis( this ); + + SolarMutexGuard g; + + // stop asynchronous settings timer and + // release deferred item container reference + m_aAsyncSettingsTimer.Stop(); + m_xDeferredItemContainer.clear(); + RemoveListener(); + + m_aMenuItemHandlerVector.clear(); + + if ( m_bDeleteMenu ) + { + m_pVCLMenu.disposeAndClear(); + } + + if ( m_xDocImageManager.is() ) + { + try + { + m_xDocImageManager->removeConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + catch ( const Exception& ) + { + } + } + if ( m_xModuleImageManager.is() ) + { + try + { + m_xModuleImageManager->removeConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + catch ( const Exception& ) + { + } + } + m_xDocImageManager.clear(); + m_xModuleImageManager.clear(); + m_xGlobalAcceleratorManager.clear(); + m_xModuleAcceleratorManager.clear(); + m_xDocAcceleratorManager.clear(); + m_xPopupMenuControllerFactory.clear(); + m_xContext.clear(); +} + +void SAL_CALL MenuBarManager::elementInserted( const css::ui::ConfigurationEvent& Event ) +{ + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + return; + + sal_Int16 nImageType = sal_Int16(); + if (( Event.aInfo >>= nImageType ) && nImageType == 0 ) + RequestImages(); +} + +void SAL_CALL MenuBarManager::elementRemoved( const css::ui::ConfigurationEvent& Event ) +{ + elementInserted(Event); +} + +void SAL_CALL MenuBarManager::elementReplaced( const css::ui::ConfigurationEvent& Event ) +{ + elementInserted(Event); +} + +// XFrameActionListener +void SAL_CALL MenuBarManager::frameAction( const FrameActionEvent& Action ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw css::lang::DisposedException(); + + if ( Action.Action != FrameAction_CONTEXT_CHANGED ) + return; + + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + // Clear dispatch reference as we will requery it later + if ( menuItemHandler->xMenuItemDispatch.is() ) + { + URL aTargetURL; + aTargetURL.Complete = menuItemHandler->aMenuItemURL; + m_xURLTransformer->parseStrict( aTargetURL ); + + menuItemHandler->xMenuItemDispatch->removeStatusListener( this, aTargetURL ); + } + menuItemHandler->xMenuItemDispatch.clear(); + } +} + +// XStatusListener +void SAL_CALL MenuBarManager::statusChanged( const FeatureStateEvent& Event ) +{ + OUString aFeatureURL = Event.FeatureURL.Complete; + + SolarMutexGuard aSolarGuard; + { + if ( m_bDisposed ) + return; + + // We have to check all menu entries as there can be identical entries in a popup menu. + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + if ( menuItemHandler->aParsedItemURL == aFeatureURL ) + { + bool bCheckmark( false ); + bool bMenuItemEnabled( m_pVCLMenu->IsItemEnabled( menuItemHandler->nItemId )); + bool bEnabledItem( Event.IsEnabled ); + OUString aItemText; + status::Visibility aVisibilityStatus; + + #ifdef UNIX + //enable some slots hardly, because UNIX clipboard does not notify all changes + // Can be removed if follow up task will be fixed directly within applications. + // Note: PasteSpecial is handled specifically by calc + // Calc also disables Paste under some circumstances, do not override. + /* TODO: is this workaround even needed anymore? Was introduced + * in 2009 with commit 426ab2c0e8f6e3fe2b766f74f6b8da873d860260 + * as some "metropatch" and the other places it touched seem to + * be gone. */ + if ( (menuItemHandler->aMenuItemURL == ".uno:Paste" && + m_aModuleIdentifier != "com.sun.star.sheet.SpreadsheetDocument") + || menuItemHandler->aMenuItemURL == ".uno:PasteClipboard" ) // special for draw/impress + bEnabledItem = true; + #endif + + // Enable/disable item + if ( bEnabledItem != bMenuItemEnabled ) + { + m_pVCLMenu->EnableItem( menuItemHandler->nItemId, bEnabledItem ); + + // Remove "checked" mark for disabled menu items. + // Initially disabled but checkable menu items do not receive + // checked/unchecked state, so can appear inconsistently after + // enabling/disabling. Since we can not pass checked state for disabled + // items, we will just reset checked state for them, anyway correct state + // will be transferred from controller once item enabled. + if ( !bEnabledItem && m_pVCLMenu->IsItemChecked( menuItemHandler->nItemId ) ) + m_pVCLMenu->CheckItem( menuItemHandler->nItemId, false ); + } + + if ( Event.State >>= bCheckmark ) + { + // Checkmark or RadioButton + m_pVCLMenu->CheckItem( menuItemHandler->nItemId, bCheckmark ); + // If not already designated RadioButton set as CheckMark + MenuItemBits nBits = m_pVCLMenu->GetItemBits( menuItemHandler->nItemId ); + if (!(nBits & MenuItemBits::RADIOCHECK)) + m_pVCLMenu->SetItemBits( menuItemHandler->nItemId, nBits | MenuItemBits::CHECKABLE ); + + if ( menuItemHandler->bMadeInvisible ) + m_pVCLMenu->ShowItem( menuItemHandler->nItemId ); + } + else if ( Event.State >>= aItemText ) + { + INetURLObject aURL( aFeatureURL ); + OUString aEnumPart = aURL.GetURLPath().getToken( 1, '.' ); + if ( !aEnumPart.isEmpty() && aURL.GetProtocol() == INetProtocol::Uno ) + { + // Checkmark or RadioButton + m_pVCLMenu->CheckItem( menuItemHandler->nItemId, aItemText == aEnumPart ); + // If not already designated RadioButton set as CheckMark + MenuItemBits nBits = m_pVCLMenu->GetItemBits( menuItemHandler->nItemId ); + if (!(nBits & MenuItemBits::RADIOCHECK)) + m_pVCLMenu->SetItemBits( menuItemHandler->nItemId, nBits | MenuItemBits::CHECKABLE ); + } + else + { + // Replacement for place holders + if ( aItemText.startsWith("($1)") ) + { + aItemText = FwkResId(STR_UPDATEDOC) + " " + aItemText.subView( 4 ); + } + else if ( aItemText.startsWith("($2)") ) + { + aItemText = FwkResId(STR_CLOSEDOC_ANDRETURN) + aItemText.subView( 4 ); + } + else if ( aItemText.startsWith("($3)") ) + { + aItemText = FwkResId(STR_SAVECOPYDOC) + aItemText.subView( 4 ); + } + + m_pVCLMenu->SetItemText( menuItemHandler->nItemId, aItemText ); + } + + if ( menuItemHandler->bMadeInvisible ) + m_pVCLMenu->ShowItem( menuItemHandler->nItemId ); + } + else if ( Event.State >>= aVisibilityStatus ) + { + // Visibility + m_pVCLMenu->ShowItem( menuItemHandler->nItemId, aVisibilityStatus.bVisible ); + menuItemHandler->bMadeInvisible = !aVisibilityStatus.bVisible; + } + else if ( menuItemHandler->bMadeInvisible ) + m_pVCLMenu->ShowItem( menuItemHandler->nItemId ); + } + + if ( Event.Requery ) + { + // Release dispatch object - will be required on the next activate! + menuItemHandler->xMenuItemDispatch.clear(); + } + } + } +} + +// Helper to retrieve own structure from item ID +MenuBarManager::MenuItemHandler* MenuBarManager::GetMenuItemHandler( sal_uInt16 nItemId ) +{ + SolarMutexGuard g; + + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + if ( menuItemHandler->nItemId == nItemId ) + return menuItemHandler.get(); + } + + return nullptr; +} + +// Helper to set request images flag +void MenuBarManager::RequestImages() +{ + + m_bRetrieveImages = true; + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + if ( menuItemHandler->xSubMenuManager.is() ) + { + MenuBarManager* pMenuBarManager = static_cast<MenuBarManager*>(menuItemHandler->xSubMenuManager.get()); + pMenuBarManager->RequestImages(); + } + } +} + +// Helper to reset objects to prepare shutdown +void MenuBarManager::RemoveListener() +{ + SolarMutexGuard g; + + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + if ( menuItemHandler->xMenuItemDispatch.is() ) + { + URL aTargetURL; + aTargetURL.Complete = menuItemHandler->aMenuItemURL; + m_xURLTransformer->parseStrict( aTargetURL ); + + menuItemHandler->xMenuItemDispatch->removeStatusListener( + static_cast< XStatusListener* >( this ), aTargetURL ); + } + + menuItemHandler->xMenuItemDispatch.clear(); + + if ( menuItemHandler->xPopupMenu.is() ) + { + { + // Remove popup menu from menu structure + m_pVCLMenu->SetPopupMenu( menuItemHandler->nItemId, nullptr ); + } + + Reference< css::lang::XEventListener > xEventListener( menuItemHandler->xPopupMenuController, UNO_QUERY ); + if ( xEventListener.is() ) + { + EventObject aEventObject; + aEventObject.Source = static_cast<OWeakObject *>(this); + xEventListener->disposing( aEventObject ); + } + + // We now provide a popup menu controller to external code. + // Therefore the life-time must be explicitly handled via + // dispose!! + try + { + Reference< XComponent > xComponent( menuItemHandler->xPopupMenuController, UNO_QUERY ); + if ( xComponent.is() ) + xComponent->dispose(); + } + catch ( const RuntimeException& ) + { + throw; + } + catch ( const Exception& ) + { + } + + // Release references to controller and popup menu + menuItemHandler->xPopupMenuController.clear(); + menuItemHandler->xPopupMenu.clear(); + } + + Reference< XComponent > xComponent( menuItemHandler->xSubMenuManager, UNO_QUERY ); + if ( xComponent.is() ) + xComponent->dispose(); + } + + try + { + if ( m_xFrame.is() ) + m_xFrame->removeFrameActionListener( Reference< XFrameActionListener >(this) ); + } + catch ( const Exception& ) + { + } + + m_xFrame = nullptr; +} + +void SAL_CALL MenuBarManager::disposing( const EventObject& Source ) +{ + MenuItemHandler* pMenuItemDisposing = nullptr; + + SolarMutexGuard g; + + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + if ( menuItemHandler->xMenuItemDispatch.is() && + menuItemHandler->xMenuItemDispatch == Source.Source ) + { + // disposing called from menu item dispatcher, remove listener + pMenuItemDisposing = menuItemHandler.get(); + break; + } + } + + if ( pMenuItemDisposing ) + { + // Release references to the dispatch object + URL aTargetURL; + aTargetURL.Complete = pMenuItemDisposing->aMenuItemURL; + + m_xURLTransformer->parseStrict( aTargetURL ); + + pMenuItemDisposing->xMenuItemDispatch->removeStatusListener( + static_cast< XStatusListener* >( this ), aTargetURL ); + pMenuItemDisposing->xMenuItemDispatch.clear(); + if ( pMenuItemDisposing->xPopupMenu.is() ) + { + Reference< css::lang::XEventListener > xEventListener( pMenuItemDisposing->xPopupMenuController, UNO_QUERY ); + if ( xEventListener.is() ) + xEventListener->disposing( Source ); + + { + // Remove popup menu from menu structure as we release our reference to + // the controller. + m_pVCLMenu->SetPopupMenu( pMenuItemDisposing->nItemId, nullptr ); + } + + pMenuItemDisposing->xPopupMenuController.clear(); + pMenuItemDisposing->xPopupMenu.clear(); + } + return; + } + else if ( Source.Source == m_xFrame ) + { + // Our frame gets disposed. We have to remove all our listeners + RemoveListener(); + } + else if ( Source.Source == Reference< XInterface >( m_xDocImageManager, UNO_QUERY )) + m_xDocImageManager.clear(); + else if ( Source.Source == Reference< XInterface >( m_xModuleImageManager, UNO_QUERY )) + m_xModuleImageManager.clear(); +} + +static void lcl_CheckForChildren(Menu* pMenu, sal_uInt16 nItemId) +{ + if (PopupMenu* pThisPopup = pMenu->GetPopupMenu( nItemId )) + pMenu->EnableItem( nItemId, pThisPopup->GetItemCount() != 0 ); +} + +// vcl handler + +namespace { + +class QuietInteractionContext: + public cppu::WeakImplHelper< css::uno::XCurrentContext > +{ +public: + explicit QuietInteractionContext( + css::uno::Reference< css::uno::XCurrentContext > context): + context_(std::move(context)) {} + QuietInteractionContext(const QuietInteractionContext&) = delete; + QuietInteractionContext& operator=(const QuietInteractionContext&) = delete; + +private: + virtual ~QuietInteractionContext() override {} + + virtual css::uno::Any SAL_CALL getValueByName( + OUString const & Name) override + { + return Name != JAVA_INTERACTION_HANDLER_NAME && context_.is() + ? context_->getValueByName(Name) + : css::uno::Any(); + } + + css::uno::Reference< css::uno::XCurrentContext > + context_; +}; + +} + +IMPL_LINK( MenuBarManager, Activate, Menu *, pMenu, bool ) +{ + if ( pMenu != m_pVCLMenu ) + return true; + + css::uno::ContextLayer layer( + new QuietInteractionContext( + css::uno::getCurrentContext())); + + // set/unset hiding disabled menu entries + bool bDontHide = officecfg::Office::Common::View::Menu::DontHideDisabledEntry::get(); + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + bool bShowMenuImages = rSettings.GetUseImagesInMenus(); + bool bShowShortcuts = m_bHasMenuBar || rSettings.GetContextMenuShortcuts(); + bool bHasDisabledEntries = SvtCommandOptions().HasEntries( SvtCommandOptions::CMDOPTION_DISABLED ); + + SolarMutexGuard g; + + MenuFlags nFlag = pMenu->GetMenuFlags(); + if ( bDontHide ) + nFlag &= ~MenuFlags::HideDisabledEntries; + else + nFlag |= MenuFlags::HideDisabledEntries; + pMenu->SetMenuFlags( nFlag ); + + if ( m_bActive ) + return false; + + m_bActive = true; + + // Check if some modes have changed so we have to update our menu images + OUString sIconTheme = SvtMiscOptions().GetIconTheme(); + + if ( m_bRetrieveImages || + bShowMenuImages != m_bShowMenuImages || + sIconTheme != m_sIconTheme ) + { + m_bShowMenuImages = bShowMenuImages; + m_bRetrieveImages = false; + m_sIconTheme = sIconTheme; + FillMenuImages( m_xFrame, pMenu, bShowMenuImages ); + } + + // Try to map commands to labels + for ( sal_uInt16 nPos = 0; nPos < pMenu->GetItemCount(); nPos++ ) + { + sal_uInt16 nItemId = pMenu->GetItemId( nPos ); + if (( pMenu->GetItemType( nPos ) != MenuItemType::SEPARATOR ) && + ( pMenu->GetItemText( nItemId ).isEmpty() )) + { + OUString aCommand = pMenu->GetItemCommand( nItemId ); + if ( !aCommand.isEmpty() ) { + pMenu->SetItemText( nItemId, RetrieveLabelFromCommand( aCommand )); + } + } + } + + // Try to set accelerator keys + { + if ( bShowShortcuts ) + RetrieveShortcuts( m_aMenuItemHandlerVector ); + + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + if ( !bShowShortcuts ) + { + pMenu->SetAccelKey( menuItemHandler->nItemId, vcl::KeyCode() ); + } + else if ( menuItemHandler->aMenuItemURL == aCmdHelpIndex ) + { + // Set key code, workaround for hard-coded shortcut F1 mapped to .uno:HelpIndex + // Only non-popup menu items can have a short-cut + vcl::KeyCode aKeyCode( KEY_F1 ); + pMenu->SetAccelKey( menuItemHandler->nItemId, aKeyCode ); + } + else if ( pMenu->GetPopupMenu( menuItemHandler->nItemId ) == nullptr ) + pMenu->SetAccelKey( menuItemHandler->nItemId, menuItemHandler->aKeyCode ); + } + } + + URL aTargetURL; + + // Use provided dispatch provider => fallback to frame as dispatch provider + Reference< XDispatchProvider > xDispatchProvider; + if ( m_xDispatchProvider.is() ) + xDispatchProvider = m_xDispatchProvider; + else + xDispatchProvider.set( m_xFrame, UNO_QUERY ); + + if ( !xDispatchProvider.is() ) + return true; + + SvtCommandOptions aCmdOptions; + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + if (menuItemHandler) + { + if ( !menuItemHandler->xMenuItemDispatch.is() && + !menuItemHandler->xSubMenuManager.is() ) + { + Reference< XDispatch > xMenuItemDispatch; + + aTargetURL.Complete = menuItemHandler->aMenuItemURL; + + m_xURLTransformer->parseStrict( aTargetURL ); + + if ( bHasDisabledEntries ) + { + if ( aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, aTargetURL.Path )) + pMenu->HideItem( menuItemHandler->nItemId ); + } + + if ( aTargetURL.Complete.startsWith( ".uno:StyleApply?" ) ) + xMenuItemDispatch = new StyleDispatcher( m_xFrame, m_xURLTransformer, aTargetURL ); + else + xMenuItemDispatch = xDispatchProvider->queryDispatch( aTargetURL, menuItemHandler->aTargetFrame, 0 ); + + bool bPopupMenu( false ); + if ( !menuItemHandler->xPopupMenuController.is() && + m_xPopupMenuControllerFactory->hasController( menuItemHandler->aMenuItemURL, m_aModuleIdentifier ) ) + { + if( xMenuItemDispatch.is() || menuItemHandler->aMenuItemURL != ".uno:RecentFileList" ) + bPopupMenu = CreatePopupMenuController(menuItemHandler.get(), m_xDispatchProvider, m_aModuleIdentifier); + } + else if ( menuItemHandler->xPopupMenuController.is() ) + { + // Force update of popup menu + menuItemHandler->xPopupMenuController->updatePopupMenu(); + bPopupMenu = true; + if (PopupMenu* pThisPopup = pMenu->GetPopupMenu( menuItemHandler->nItemId )) + pMenu->EnableItem( menuItemHandler->nItemId, pThisPopup->GetItemCount() != 0 ); + } + lcl_CheckForChildren(pMenu, menuItemHandler->nItemId); + + if ( xMenuItemDispatch.is() ) + { + menuItemHandler->xMenuItemDispatch = xMenuItemDispatch; + menuItemHandler->aParsedItemURL = aTargetURL.Complete; + + if ( !bPopupMenu ) + { + xMenuItemDispatch->addStatusListener( static_cast< XStatusListener* >( this ), aTargetURL ); + // For the menubar, we have to keep status listening to support Ubuntu's HUD. + if ( !m_bHasMenuBar ) + xMenuItemDispatch->removeStatusListener( static_cast< XStatusListener* >( this ), aTargetURL ); + } + } + else if ( !bPopupMenu ) + pMenu->EnableItem( menuItemHandler->nItemId, false ); + } + else if ( menuItemHandler->xPopupMenuController.is() ) + { + // Force update of popup menu + menuItemHandler->xPopupMenuController->updatePopupMenu(); + lcl_CheckForChildren(pMenu, menuItemHandler->nItemId); + } + else if ( menuItemHandler->xMenuItemDispatch.is() ) + { + // We need an update to reflect the current state + try + { + aTargetURL.Complete = menuItemHandler->aMenuItemURL; + m_xURLTransformer->parseStrict( aTargetURL ); + + menuItemHandler->xMenuItemDispatch->addStatusListener( + static_cast< XStatusListener* >( this ), aTargetURL ); + menuItemHandler->xMenuItemDispatch->removeStatusListener( + static_cast< XStatusListener* >( this ), aTargetURL ); + } + catch ( const Exception& ) + { + } + } + else if ( menuItemHandler->xSubMenuManager.is() ) + lcl_CheckForChildren(pMenu, menuItemHandler->nItemId); + } + } + + return true; +} + +IMPL_LINK( MenuBarManager, Deactivate, Menu *, pMenu, bool ) +{ + if ( pMenu == m_pVCLMenu ) + { + m_bActive = false; + if ( pMenu->IsMenuBar() && m_xDeferredItemContainer.is() ) + { + // Start timer to handle settings asynchronous + // Changing the menu inside this handler leads to + // a crash under X! + m_aAsyncSettingsTimer.SetInvokeHandler(LINK(this, MenuBarManager, AsyncSettingsHdl)); + m_aAsyncSettingsTimer.SetTimeout(10); + m_aAsyncSettingsTimer.Start(); + } + } + + return true; +} + +IMPL_LINK_NOARG( MenuBarManager, AsyncSettingsHdl, Timer*, void) +{ + SolarMutexGuard g; + Reference< XInterface > xSelfHold( + static_cast< ::cppu::OWeakObject* >( this ), UNO_QUERY_THROW ); + + m_aAsyncSettingsTimer.Stop(); + if ( !m_bActive && m_xDeferredItemContainer.is() ) + { + SetItemContainer( m_xDeferredItemContainer ); + m_xDeferredItemContainer.clear(); + } +} + +IMPL_LINK( MenuBarManager, Select, Menu *, pMenu, bool ) +{ + URL aTargetURL; + Sequence<PropertyValue> aArgs; + Reference< XDispatch > xDispatch; + + { + SolarMutexGuard g; + + sal_uInt16 nCurItemId = pMenu->GetCurItemId(); + sal_uInt16 nCurPos = pMenu->GetItemPos( nCurItemId ); + if ( pMenu == m_pVCLMenu && + pMenu->GetItemType( nCurPos ) != MenuItemType::SEPARATOR ) + { + MenuItemHandler* pMenuItemHandler = GetMenuItemHandler( nCurItemId ); + if ( pMenuItemHandler && pMenuItemHandler->xMenuItemDispatch.is() ) + { + aTargetURL.Complete = pMenuItemHandler->aMenuItemURL; + m_xURLTransformer->parseStrict( aTargetURL ); + + if ( pMenu->GetUserValue( nCurItemId ) ) + { + // addon menu item selected + aArgs = { comphelper::makePropertyValue("Referer", OUString("private:user")) }; + } + + xDispatch = pMenuItemHandler->xMenuItemDispatch; + } + } + } + + // tdf#126054 don't let dispatch destroy this until after function completes + rtl::Reference<MenuBarManager> xKeepAlive(this); + if (xDispatch.is()) + { + SolarMutexReleaser aReleaser; + xDispatch->dispatch( aTargetURL, aArgs ); + } + + if ( !m_bHasMenuBar ) + // Standalone (non-native) popup menu doesn't fire deactivate event + // in this case, so we have to reset the active flag here. + m_bActive = false; + + return true; +} + +bool MenuBarManager::MustBeHidden( PopupMenu* pPopupMenu, const Reference< XURLTransformer >& rTransformer ) +{ + if ( !pPopupMenu ) + return true; + + URL aTargetURL; + SvtCommandOptions aCmdOptions; + + sal_uInt16 nCount = pPopupMenu->GetItemCount(); + sal_uInt16 nHideCount( 0 ); + + for ( sal_uInt16 i = 0; i < nCount; i++ ) + { + sal_uInt16 nId = pPopupMenu->GetItemId( i ); + if ( nId > 0 ) + { + PopupMenu* pSubPopupMenu = pPopupMenu->GetPopupMenu( nId ); + if ( pSubPopupMenu ) + { + if ( MustBeHidden( pSubPopupMenu, rTransformer )) + { + pPopupMenu->HideItem( nId ); + ++nHideCount; + } + } + else + { + aTargetURL.Complete = pPopupMenu->GetItemCommand( nId ); + rTransformer->parseStrict( aTargetURL ); + + if ( aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, aTargetURL.Path )) + ++nHideCount; + } + } + else + ++nHideCount; + } + + return ( nCount == nHideCount ); +} + +OUString MenuBarManager::RetrieveLabelFromCommand(const OUString& rCmdURL) +{ + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCmdURL, m_aModuleIdentifier); + if ( !m_bHasMenuBar ) + { + // This is a context menu, prefer "PopupLabel" over "Label". + return vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties); + } + return vcl::CommandInfoProvider::GetMenuLabelForCommand(aProperties); +} + +bool MenuBarManager::CreatePopupMenuController( MenuItemHandler* pMenuItemHandler, + const css::uno::Reference< css::frame::XDispatchProvider >& rDispatchProvider, + const OUString& rModuleIdentifier ) +{ + OUString aItemCommand( pMenuItemHandler->aMenuItemURL ); + + // Try instantiate a popup menu controller. It is stored in the menu item handler. + if ( !m_xPopupMenuControllerFactory.is() ) + return false; + + auto aSeq( comphelper::InitAnyPropertySequence( { + { "DispatchProvider", Any(rDispatchProvider) }, + { "ModuleIdentifier", Any(rModuleIdentifier) }, + { "Frame", Any(m_xFrame) }, + { "InToolbar", Any(!m_bHasMenuBar) } + } ) ); + + Reference< XPopupMenuController > xPopupMenuController( + m_xPopupMenuControllerFactory->createInstanceWithArgumentsAndContext( + aItemCommand, + aSeq, + m_xContext ), + UNO_QUERY ); + + if ( xPopupMenuController.is() ) + { + // Provide our awt popup menu to the popup menu controller + pMenuItemHandler->xPopupMenuController = xPopupMenuController; + xPopupMenuController->setPopupMenu( pMenuItemHandler->xPopupMenu ); + return true; + } + + return false; +} + +void MenuBarManager::FillMenuManager( Menu* pMenu, const Reference< XFrame >& rFrame, + const Reference< XDispatchProvider >& rDispatchProvider, + const OUString& rModuleIdentifier, bool bDelete ) +{ + m_xFrame = rFrame; + m_bActive = false; + m_bDeleteMenu = bDelete; + m_pVCLMenu = pMenu; + m_xDispatchProvider = rDispatchProvider; + + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + m_bShowMenuImages = rSettings.GetUseImagesInMenus(); + m_bRetrieveImages = false; + + // Set module identifier when provided from outside + if (!rModuleIdentifier.isEmpty()) + m_aModuleIdentifier = rModuleIdentifier; + else + m_aModuleIdentifier = vcl::CommandInfoProvider::GetModuleIdentifier(m_xFrame); + + // Add root as ui configuration listener + RetrieveImageManagers(); + + if ( pMenu->IsMenuBar() && rFrame.is() ) + { + // First merge all addon popup menus into our structure + sal_uInt16 nPos = 0; + for ( nPos = 0; nPos < pMenu->GetItemCount(); nPos++ ) + { + sal_uInt16 nItemId = pMenu->GetItemId( nPos ); + OUString aCommand = pMenu->GetItemCommand( nItemId ); + if ( aCommand == aSpecialWindowCommand || aCommand == aCmdHelpMenu ) + { + // Retrieve addon popup menus and add them to our menu bar + framework::AddonMenuManager::MergeAddonPopupMenus( rFrame, nPos, static_cast<MenuBar *>(pMenu) ); + break; + } + } + + // Merge the Add-Ons help menu items into the Office help menu + framework::AddonMenuManager::MergeAddonHelpMenu( rFrame, static_cast<MenuBar *>(pMenu) ); + } + + bool bAccessibilityEnabled( Application::GetSettings().GetMiscSettings().GetEnableATToolSupport() ); + sal_uInt16 nItemCount = pMenu->GetItemCount(); + OUString aItemCommand; + m_aMenuItemHandlerVector.reserve(nItemCount); + for ( sal_uInt16 i = 0; i < nItemCount; i++ ) + { + sal_uInt16 nItemId = FillItemCommand(aItemCommand,pMenu, i ); + + if (( pMenu->IsMenuBar() || bAccessibilityEnabled ) && + ( pMenu->GetItemText( nItemId ).isEmpty() )) + { + if ( !aItemCommand.isEmpty() ) + pMenu->SetItemText( nItemId, RetrieveLabelFromCommand( aItemCommand )); + } + + // Command can be just an alias to another command. + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aItemCommand, m_aModuleIdentifier); + OUString aRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties); + if ( !aRealCommand.isEmpty() ) + aItemCommand = aRealCommand; + + Reference< XDispatch > xDispatch; + Reference< XStatusListener > xStatusListener; + VclPtr<PopupMenu> pPopup = pMenu->GetPopupMenu( nItemId ); + // overwrite the show icons on menu option? + MenuItemBits nBits = pMenu->GetItemBits( nItemId ) & ( MenuItemBits::ICON | MenuItemBits::TEXT ); + bool bItemShowMenuImages = ( m_bShowMenuImages && nBits != MenuItemBits::TEXT ) || nBits & MenuItemBits::ICON; + + if ( pPopup ) + { + // Retrieve module identifier from Help Command entry + OUString aModuleIdentifier( rModuleIdentifier ); + if (!pMenu->GetHelpCommand(nItemId).isEmpty()) + { + aModuleIdentifier = pMenu->GetHelpCommand( nItemId ); + pMenu->SetHelpCommand( nItemId, "" ); + } + + // Retrieve possible attributes struct + Reference< XDispatchProvider > xPopupMenuDispatchProvider( rDispatchProvider ); + MenuAttributes* pAttributes = static_cast<MenuAttributes *>(pMenu->GetUserValue( nItemId )); + if ( pAttributes ) + xPopupMenuDispatchProvider = pAttributes->xDispatchProvider; + + if ( m_xPopupMenuControllerFactory.is() && + m_xPopupMenuControllerFactory->hasController( aItemCommand, aModuleIdentifier ) + ) + { + // Check if we have to create a popup menu for a uno based popup menu controller. + // We have to set an empty popup menu into our menu structure so the controller also + // works with inplace OLE. + MenuItemHandler* pItemHandler = new MenuItemHandler( nItemId, xStatusListener, xDispatch ); + rtl::Reference<VCLXPopupMenu> pVCLXPopupMenu = new VCLXPopupMenu(pPopup); + pItemHandler->xPopupMenu = pVCLXPopupMenu; + pItemHandler->aMenuItemURL = aItemCommand; + m_aMenuItemHandlerVector.push_back( std::unique_ptr<MenuItemHandler>(pItemHandler) ); + + if ( bAccessibilityEnabled || pMenu->IsMenuBar()) + { + if ( CreatePopupMenuController( pItemHandler, xPopupMenuDispatchProvider, aModuleIdentifier )) + pItemHandler->xPopupMenuController->updatePopupMenu(); + } + lcl_CheckForChildren(pMenu, nItemId); + } + else + { + // Check if this is the tools menu. Add menu item if needed + if ( aItemCommand == aCmdToolsMenu && AddonMenuManager::HasAddonMenuElements() ) + { + // Create addon popup menu if there exist elements and this is the tools popup menu + VclPtr<PopupMenu> pSubMenu = AddonMenuManager::CreateAddonMenu(rFrame); + if ( pSubMenu && ( pSubMenu->GetItemCount() > 0 )) + { + if ( pPopup->GetItemType( pPopup->GetItemCount() - 1 ) != MenuItemType::SEPARATOR ) + pPopup->InsertSeparator(); + + pPopup->InsertItem( ITEMID_ADDONLIST, OUString() ); + pPopup->SetPopupMenu( ITEMID_ADDONLIST, pSubMenu ); + pPopup->SetItemCommand( ITEMID_ADDONLIST, ".uno:Addons" ); + } + else + pSubMenu.disposeAndClear(); + } + + rtl::Reference<MenuBarManager> pSubMenuManager = new MenuBarManager( m_xContext, rFrame, m_xURLTransformer, + xPopupMenuDispatchProvider, aModuleIdentifier, pPopup, false, m_bHasMenuBar ); + + AddMenu(pSubMenuManager.get(), aItemCommand, nItemId); + } + } + else if ( pMenu->GetItemType( i ) != MenuItemType::SEPARATOR ) + { + if ( bItemShowMenuImages ) + m_bRetrieveImages = true; + + std::unique_ptr<MenuItemHandler> pItemHandler(new MenuItemHandler( nItemId, xStatusListener, xDispatch )); + // Retrieve possible attributes struct + MenuAttributes* pAttributes = static_cast<MenuAttributes *>(pMenu->GetUserValue( nItemId )); + if ( pAttributes ) + pItemHandler->aTargetFrame = pAttributes->aTargetFrame; + pItemHandler->aMenuItemURL = aItemCommand; + + if ( m_xPopupMenuControllerFactory.is() && + m_xPopupMenuControllerFactory->hasController( aItemCommand, m_aModuleIdentifier ) ) + { + // Check if we have to create a popup menu for a uno based popup menu controller. + // We have to set an empty popup menu into our menu structure so the controller also + // works with inplace OLE. + rtl::Reference<VCLXPopupMenu> pVCLXPopupMenu = new VCLXPopupMenu; + PopupMenu* pPopupMenu = static_cast<PopupMenu *>(pVCLXPopupMenu->GetMenu()); + pMenu->SetPopupMenu( pItemHandler->nItemId, pPopupMenu ); + pItemHandler->xPopupMenu = pVCLXPopupMenu; + + if ( bAccessibilityEnabled && CreatePopupMenuController( pItemHandler.get(), m_xDispatchProvider, m_aModuleIdentifier ) ) + { + pItemHandler->xPopupMenuController->updatePopupMenu(); + } + + lcl_CheckForChildren(pMenu, pItemHandler->nItemId); + } + + m_aMenuItemHandlerVector.push_back( std::move(pItemHandler) ); + } + } + + if ( m_bHasMenuBar && bAccessibilityEnabled ) + { + RetrieveShortcuts( m_aMenuItemHandlerVector ); + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + // Set key code, workaround for hard-coded shortcut F1 mapped to .uno:HelpIndex + // Only non-popup menu items can have a short-cut + if ( menuItemHandler->aMenuItemURL == aCmdHelpIndex ) + { + vcl::KeyCode aKeyCode( KEY_F1 ); + pMenu->SetAccelKey( menuItemHandler->nItemId, aKeyCode ); + } + else if ( pMenu->GetPopupMenu( menuItemHandler->nItemId ) == nullptr ) + pMenu->SetAccelKey( menuItemHandler->nItemId, menuItemHandler->aKeyCode ); + } + } + + SetHdl(); +} + +void MenuBarManager::impl_RetrieveShortcutsFromConfiguration( + const Reference< XAcceleratorConfiguration >& rAccelCfg, + const Sequence< OUString >& rCommands, + std::vector< std::unique_ptr<MenuItemHandler> >& aMenuShortCuts ) +{ + if ( !rAccelCfg.is() ) + return; + + try + { + css::awt::KeyEvent aKeyEvent; + Sequence< Any > aSeqKeyCode = rAccelCfg->getPreferredKeyEventsForCommandList( rCommands ); + for ( sal_Int32 i = 0; i < aSeqKeyCode.getLength(); i++ ) + { + if ( aSeqKeyCode[i] >>= aKeyEvent ) + aMenuShortCuts[i]->aKeyCode = svt::AcceleratorExecute::st_AWTKey2VCLKey( aKeyEvent ); + } + } + catch ( const IllegalArgumentException& ) + { + } +} + +void MenuBarManager::RetrieveShortcuts( std::vector< std::unique_ptr<MenuItemHandler> >& aMenuShortCuts ) +{ + Reference< XAcceleratorConfiguration > xDocAccelCfg( m_xDocAcceleratorManager ); + Reference< XAcceleratorConfiguration > xModuleAccelCfg( m_xModuleAcceleratorManager ); + Reference< XAcceleratorConfiguration > xGlobalAccelCfg( m_xGlobalAcceleratorManager ); + + if ( !m_bAcceleratorCfg ) + { + // Retrieve references on demand + m_bAcceleratorCfg = true; + if ( !xDocAccelCfg.is() ) + { + Reference< XController > xController = m_xFrame->getController(); + Reference< XModel > xModel; + if ( xController.is() ) + { + xModel = xController->getModel(); + if ( xModel.is() ) + { + Reference< XUIConfigurationManagerSupplier > xSupplier( xModel, UNO_QUERY ); + if ( xSupplier.is() ) + { + Reference< XUIConfigurationManager > xDocUICfgMgr = xSupplier->getUIConfigurationManager(); + if ( xDocUICfgMgr.is() ) + { + xDocAccelCfg = xDocUICfgMgr->getShortCutManager(); + m_xDocAcceleratorManager = xDocAccelCfg; + } + } + } + } + } + + if ( !xModuleAccelCfg.is() ) + { + Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier = + theModuleUIConfigurationManagerSupplier::get( m_xContext ); + try + { + Reference< XUIConfigurationManager > xUICfgMgr = xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleIdentifier ); + if ( xUICfgMgr.is() ) + { + xModuleAccelCfg = xUICfgMgr->getShortCutManager(); + m_xModuleAcceleratorManager = xModuleAccelCfg; + } + } + catch ( const RuntimeException& ) + { + throw; + } + catch ( const Exception& ) + { + } + } + + if ( !xGlobalAccelCfg.is() ) try + { + xGlobalAccelCfg = GlobalAcceleratorConfiguration::create( m_xContext ); + m_xGlobalAcceleratorManager = xGlobalAccelCfg; + } + catch ( const css::uno::DeploymentException& ) + { + SAL_WARN("fwk.uielement", "GlobalAcceleratorConfiguration" + " not available. This should happen only on mobile platforms."); + } + } + + vcl::KeyCode aEmptyKeyCode; + Sequence< OUString > aSeq( aMenuShortCuts.size() ); + auto aSeqRange = asNonConstRange(aSeq); + const sal_uInt32 nCount = aMenuShortCuts.size(); + for ( sal_uInt32 i = 0; i < nCount; ++i ) + { + aSeqRange[i] = aMenuShortCuts[i]->aMenuItemURL; + aMenuShortCuts[i]->aKeyCode = aEmptyKeyCode; + } + + if ( m_xGlobalAcceleratorManager.is() ) + impl_RetrieveShortcutsFromConfiguration( xGlobalAccelCfg, aSeq, aMenuShortCuts ); + if ( m_xModuleAcceleratorManager.is() ) + impl_RetrieveShortcutsFromConfiguration( xModuleAccelCfg, aSeq, aMenuShortCuts ); + if ( m_xDocAcceleratorManager.is() ) + impl_RetrieveShortcutsFromConfiguration( xDocAccelCfg, aSeq, aMenuShortCuts ); +} + +void MenuBarManager::RetrieveImageManagers() +{ + if ( !m_xDocImageManager.is() ) + { + Reference< XController > xController = m_xFrame->getController(); + Reference< XModel > xModel; + if ( xController.is() ) + { + xModel = xController->getModel(); + if ( xModel.is() ) + { + Reference< XUIConfigurationManagerSupplier > xSupplier( xModel, UNO_QUERY ); + if ( xSupplier.is() ) + { + Reference< XUIConfigurationManager > xDocUICfgMgr = xSupplier->getUIConfigurationManager(); + m_xDocImageManager.set( xDocUICfgMgr->getImageManager(), UNO_QUERY ); + m_xDocImageManager->addConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + } + } + } + + if ( !m_xModuleImageManager.is() ) + { + Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier = + theModuleUIConfigurationManagerSupplier::get( m_xContext ); + Reference< XUIConfigurationManager > xUICfgMgr = xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleIdentifier ); + m_xModuleImageManager.set( xUICfgMgr->getImageManager(), UNO_QUERY ); + m_xModuleImageManager->addConfigurationListener( Reference< XUIConfigurationListener >(this) ); + } +} + +void MenuBarManager::FillMenuWithConfiguration( + sal_uInt16& nId, + Menu* pMenu, + const OUString& rModuleIdentifier, + const Reference< XIndexAccess >& rItemContainer, + const Reference< XURLTransformer >& rTransformer ) +{ + Reference< XDispatchProvider > xEmptyDispatchProvider; + MenuBarManager::FillMenu( nId, pMenu, rModuleIdentifier, rItemContainer, xEmptyDispatchProvider ); + + // Merge add-on menu entries into the menu bar + MenuBarManager::MergeAddonMenus( pMenu, + AddonsOptions().GetMergeMenuInstructions(), + rModuleIdentifier ); + + bool bHasDisabledEntries = SvtCommandOptions().HasEntries( SvtCommandOptions::CMDOPTION_DISABLED ); + if ( !bHasDisabledEntries ) + return; + + sal_uInt16 nCount = pMenu->GetItemCount(); + for ( sal_uInt16 i = 0; i < nCount; i++ ) + { + sal_uInt16 nID = pMenu->GetItemId( i ); + if ( nID > 0 ) + { + PopupMenu* pPopupMenu = pMenu->GetPopupMenu( nID ); + if ( pPopupMenu ) + { + if ( MustBeHidden( pPopupMenu, rTransformer )) + pMenu->HideItem( nId ); + } + } + } +} + +void MenuBarManager::FillMenu( + sal_uInt16& nId, + Menu* pMenu, + const OUString& rModuleIdentifier, + const Reference< XIndexAccess >& rItemContainer, + const Reference< XDispatchProvider >& rDispatchProvider ) +{ + // Fill menu bar with container contents + for ( sal_Int32 n = 0; n < rItemContainer->getCount(); n++ ) + { + Sequence< PropertyValue > aProps; + OUString aCommandURL; + OUString aLabel; + OUString aModuleIdentifier( rModuleIdentifier ); + sal_uInt16 nType = 0; + Reference< XIndexAccess > xIndexContainer; + Reference< XDispatchProvider > xDispatchProvider( rDispatchProvider ); + sal_Int16 nStyle = 0; + try + { + if ( rItemContainer->getByIndex( n ) >>= aProps ) + { + bool bShow = true; + bool bEnabled = true; + + for ( beans::PropertyValue const & rProp : std::as_const(aProps) ) + { + OUString aPropName = rProp.Name; + if ( aPropName == "CommandURL" ) + rProp.Value >>= aCommandURL; + else if ( aPropName == "ItemDescriptorContainer" ) + rProp.Value >>= xIndexContainer; + else if ( aPropName == "Label" ) + rProp.Value >>= aLabel; + else if ( aPropName == "Type" ) + rProp.Value >>= nType; + else if ( aPropName == "ModuleIdentifier" ) + rProp.Value >>= aModuleIdentifier; + else if ( aPropName == "DispatchProvider" ) + rProp.Value >>= xDispatchProvider; + else if ( aPropName == "Style" ) + rProp.Value >>= nStyle; + else if ( aPropName == "IsVisible" ) + rProp.Value >>= bShow; + else if ( aPropName == "Enabled" ) + rProp.Value >>= bEnabled; + } + + if (!aCommandURL.isEmpty() && vcl::CommandInfoProvider::IsExperimental(aCommandURL, rModuleIdentifier) && + !officecfg::Office::Common::Misc::ExperimentalMode::get()) + { + continue; + } + + if ( nType == css::ui::ItemType::DEFAULT ) + { + pMenu->InsertItem( nId, aLabel ); + pMenu->SetItemCommand( nId, aCommandURL ); + + if ( nStyle ) + { + MenuItemBits nBits = pMenu->GetItemBits( nId ); + if ( nStyle & css::ui::ItemStyle::ICON ) + nBits |= MenuItemBits::ICON; + if ( nStyle & css::ui::ItemStyle::TEXT ) + nBits |= MenuItemBits::TEXT; + if ( nStyle & css::ui::ItemStyle::RADIO_CHECK ) + nBits |= MenuItemBits::RADIOCHECK; + pMenu->SetItemBits( nId, nBits ); + } + + if ( !bShow ) + pMenu->HideItem( nId ); + + if ( !bEnabled) + pMenu->EnableItem( nId, false ); + + if ( xIndexContainer.is() ) + { + VclPtr<PopupMenu> pNewPopupMenu = VclPtr<PopupMenu>::Create(); + pMenu->SetPopupMenu( nId, pNewPopupMenu ); + + if ( xDispatchProvider.is() ) + { + // Use attributes struct to transport special dispatch provider + void* nAttributePtr = MenuAttributes::CreateAttribute(xDispatchProvider); + pMenu->SetUserValue(nId, nAttributePtr, MenuAttributes::ReleaseAttribute); + } + + // Use help command to transport module identifier + if ( !aModuleIdentifier.isEmpty() ) + pMenu->SetHelpCommand( nId, aModuleIdentifier ); + + ++nId; + FillMenu( nId, pNewPopupMenu, aModuleIdentifier, xIndexContainer, xDispatchProvider ); + } + else + ++nId; + } + else + { + pMenu->InsertSeparator(); + ++nId; + } + } + } + catch ( const IndexOutOfBoundsException& ) + { + break; + } + } +} + +void MenuBarManager::MergeAddonMenus( + Menu* pMenuBar, + const MergeMenuInstructionContainer& aMergeInstructionContainer, + const OUString& rModuleIdentifier ) +{ + // set start value for the item ID for the new addon menu items + sal_uInt16 nItemId = ADDONMENU_MERGE_ITEMID_START; + + const sal_uInt32 nCount = aMergeInstructionContainer.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + const MergeMenuInstruction& rMergeInstruction = aMergeInstructionContainer[i]; + + if ( MenuBarMerger::IsCorrectContext( rMergeInstruction.aMergeContext, rModuleIdentifier )) + { + ::std::vector< OUString > aMergePath; + + // retrieve the merge path from the merge point string + MenuBarMerger::RetrieveReferencePath( rMergeInstruction.aMergePoint, aMergePath ); + + // convert the sequence/sequence property value to a more convenient vector<> + AddonMenuContainer aMergeMenuItems; + MenuBarMerger::GetSubMenu( rMergeInstruction.aMergeMenu, aMergeMenuItems ); + + // try to find the reference point for our merge operation + Menu* pMenu = pMenuBar; + ReferencePathInfo aResult = MenuBarMerger::FindReferencePath( aMergePath, pMenu ); + + if ( aResult.eResult == RP_OK ) + { + // normal merge operation + MenuBarMerger::ProcessMergeOperation( aResult.pPopupMenu, + aResult.nPos, + nItemId, + rMergeInstruction.aMergeCommand, + rMergeInstruction.aMergeCommandParameter, + rModuleIdentifier, + aMergeMenuItems ); + } + else + { + // fallback + MenuBarMerger::ProcessFallbackOperation( aResult, + nItemId, + rMergeInstruction.aMergeCommand, + rMergeInstruction.aMergeFallback, + aMergePath, + rModuleIdentifier, + aMergeMenuItems ); + } + } + } +} + +void MenuBarManager::SetItemContainer( const Reference< XIndexAccess >& rItemContainer ) +{ + SolarMutexGuard aSolarMutexGuard; + + Reference< XFrame > xFrame = m_xFrame; + + // Clear MenuBarManager structures + { + // Check active state as we cannot change our VCL menu during activation by the user + if ( m_bActive ) + { + m_xDeferredItemContainer = rItemContainer; + return; + } + + RemoveListener(); + m_aMenuItemHandlerVector.clear(); + m_pVCLMenu->Clear(); + + sal_uInt16 nId = 1; + + // Fill menu bar with container contents + FillMenuWithConfiguration( nId, m_pVCLMenu, m_aModuleIdentifier, rItemContainer, m_xURLTransformer ); + + // Refill menu manager again + Reference< XDispatchProvider > xDispatchProvider; + FillMenuManager( m_pVCLMenu, xFrame, xDispatchProvider, m_aModuleIdentifier, false ); + + // add itself as frame action listener + m_xFrame->addFrameActionListener( Reference< XFrameActionListener >(this) ); + } +} + +void MenuBarManager::GetPopupController( PopupControllerCache& rPopupController ) +{ + + SolarMutexGuard aSolarMutexGuard; + + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + if ( menuItemHandler->xPopupMenuController.is() ) + { + Reference< XDispatchProvider > xDispatchProvider( menuItemHandler->xPopupMenuController, UNO_QUERY ); + + PopupControllerEntry aPopupControllerEntry; + aPopupControllerEntry.m_xDispatchProvider = xDispatchProvider; + + // Just use the main part of the URL for popup menu controllers + sal_Int32 nSchemePart( 0 ); + OUString aMenuURL( menuItemHandler->aMenuItemURL ); + + nSchemePart = aMenuURL.indexOf( ':' ); + if (( nSchemePart > 0 ) && + ( aMenuURL.getLength() > ( nSchemePart+1 ))) + { + OUString aMainURL( "vnd.sun.star.popup:" ); + sal_Int32 nQueryPart = aMenuURL.indexOf( '?', nSchemePart ); + if ( nQueryPart > 0 ) + aMainURL += aMenuURL.subView( nSchemePart, nQueryPart-nSchemePart ); + else if ( nQueryPart == -1 ) + aMainURL += aMenuURL.subView( nSchemePart+1 ); + + rPopupController.emplace( aMainURL, aPopupControllerEntry ); + } + } + if ( menuItemHandler->xSubMenuManager.is() ) + { + MenuBarManager* pMenuBarManager = static_cast<MenuBarManager*>(menuItemHandler->xSubMenuManager.get()); + if ( pMenuBarManager ) + pMenuBarManager->GetPopupController( rPopupController ); + } + } +} + +void MenuBarManager::AddMenu(MenuBarManager* pSubMenuManager,const OUString& _sItemCommand,sal_uInt16 _nItemId) +{ + Reference< XStatusListener > xSubMenuManager( pSubMenuManager ); + m_xFrame->addFrameActionListener( Reference< XFrameActionListener >( xSubMenuManager, UNO_QUERY )); + + Reference< XDispatch > xDispatch; + std::unique_ptr<MenuItemHandler> pMenuItemHandler(new MenuItemHandler( + _nItemId, + xSubMenuManager, + xDispatch )); + pMenuItemHandler->aMenuItemURL = _sItemCommand; + m_aMenuItemHandlerVector.push_back( std::move(pMenuItemHandler) ); +} + +sal_uInt16 MenuBarManager::FillItemCommand(OUString& _rItemCommand, Menu* _pMenu,sal_uInt16 _nIndex) const +{ + sal_uInt16 nItemId = _pMenu->GetItemId( _nIndex ); + + _rItemCommand = _pMenu->GetItemCommand( nItemId ); + if ( _rItemCommand.isEmpty() ) + { + _rItemCommand = "slot:" + OUString::number( nItemId ); + _pMenu->SetItemCommand( nItemId, _rItemCommand ); + } + return nItemId; +} + +void MenuBarManager::SetHdl() +{ + m_pVCLMenu->SetActivateHdl( LINK( this, MenuBarManager, Activate )); + m_pVCLMenu->SetDeactivateHdl( LINK( this, MenuBarManager, Deactivate )); + m_pVCLMenu->SetSelectHdl( LINK( this, MenuBarManager, Select )); + + if ( !m_xURLTransformer.is() && m_xContext.is() ) + m_xURLTransformer.set( URLTransformer::create( m_xContext) ); +} + +void MenuBarManager::FillMenuImages(Reference< XFrame > const & _xFrame, Menu* _pMenu,bool bShowMenuImages) +{ + AddonsOptions aAddonOptions; + + for ( sal_uInt16 nPos = 0; nPos < _pMenu->GetItemCount(); nPos++ ) + { + sal_uInt16 nId = _pMenu->GetItemId( nPos ); + if ( _pMenu->GetItemType( nPos ) != MenuItemType::SEPARATOR ) + { + // overwrite the show icons on menu option? + MenuItemBits nBits = _pMenu->GetItemBits( nId ) & ( MenuItemBits::ICON | MenuItemBits::TEXT ); + bool bTmpShowMenuImages = ( bShowMenuImages && nBits != MenuItemBits::TEXT ) || nBits & MenuItemBits::ICON; + + if ( bTmpShowMenuImages ) + { + OUString aMenuItemCommand = _pMenu->GetItemCommand( nId ); + Image aImage = vcl::CommandInfoProvider::GetImageForCommand(aMenuItemCommand, _xFrame); + if ( !aImage ) + aImage = Image(aAddonOptions.GetImageFromURL(aMenuItemCommand, false)); + + _pMenu->SetItemImage( nId, aImage ); + } + else + _pMenu->SetItemImage( nId, Image() ); + } + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/menubarmerger.cxx b/framework/source/uielement/menubarmerger.cxx new file mode 100644 index 000000000..c5730d2ef --- /dev/null +++ b/framework/source/uielement/menubarmerger.cxx @@ -0,0 +1,430 @@ +/* -*- 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 <uielement/menubarmerger.hxx> +#include <framework/addonsoptions.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <o3tl/string_view.hxx> + +using namespace ::com::sun::star; + +const char SEPARATOR_STRING[] = "private:separator"; + +const char16_t MERGECOMMAND_ADDAFTER[] = u"AddAfter"; +const char16_t MERGECOMMAND_ADDBEFORE[] = u"AddBefore"; +const char16_t MERGECOMMAND_REPLACE[] = u"Replace"; +const char16_t MERGECOMMAND_REMOVE[] = u"Remove"; + +const char16_t MERGEFALLBACK_ADDPATH[] = u"AddPath"; +const char16_t MERGEFALLBACK_IGNORE[] = u"Ignore"; + +namespace framework +{ + +/** + Check whether a module identifier is part of a context + defined by a colon separated list of module identifier. + + @param + rContext + + Describes a context string list where all contexts + are delimited by a colon. For more information about + the module identifier used as context strings see the + IDL description of css::frame::XModuleManager + + @param + rModuleIdentifier + + A string describing a module identifier. See IDL + description of css::frame::XModuleManager. + +*/ +bool MenuBarMerger::IsCorrectContext( + std::u16string_view rContext, std::u16string_view rModuleIdentifier ) +{ + return ( rContext.empty() || ( rContext.find( rModuleIdentifier ) != std::u16string_view::npos )); +} + +void MenuBarMerger::RetrieveReferencePath( + std::u16string_view rReferencePathString, + ::std::vector< OUString >& rReferencePath ) +{ + const char aDelimiter = '\\'; + + rReferencePath.clear(); + sal_Int32 nIndex( 0 ); + do + { + OUString aToken( o3tl::getToken(rReferencePathString, 0, aDelimiter, nIndex ) ); + if ( !aToken.isEmpty() ) + rReferencePath.push_back( aToken ); + } + while ( nIndex >= 0 ); +} + +ReferencePathInfo MenuBarMerger::FindReferencePath( + const ::std::vector< OUString >& rReferencePath, + Menu* pMenu ) +{ + sal_uInt32 i( 0 ); + const sal_uInt32 nCount( rReferencePath.size() ); + + ReferencePathInfo aResult; + if ( !nCount ) + { + aResult.pPopupMenu = nullptr; + aResult.nPos = 0; + aResult.nLevel = -1; + aResult.eResult = RP_MENUITEM_NOT_FOUND; + return aResult; + } + + Menu* pCurrMenu( pMenu ); + RPResultInfo eResult( RP_OK ); + + sal_Int32 nLevel( - 1 ); + sal_uInt16 nPos( MENU_ITEM_NOTFOUND ); + do + { + ++nLevel; + OUString aCmd( rReferencePath[i] ); + + if ( i == nCount-1 ) + { + // Check last reference path element. Must be a leave (menu item). + sal_uInt16 nTmpPos = FindMenuItem( aCmd, pCurrMenu ); + if ( nTmpPos != MENU_ITEM_NOTFOUND ) + nPos = nTmpPos; + eResult = ( nTmpPos != MENU_ITEM_NOTFOUND ) ? RP_OK : RP_MENUITEM_NOT_FOUND; + } + else + { + // Check reference path element. Must be a node (popup menu)! + sal_uInt16 nTmpPos = FindMenuItem( aCmd, pCurrMenu ); + if ( nTmpPos != MENU_ITEM_NOTFOUND ) + { + sal_uInt16 nItemId = pCurrMenu->GetItemId( nTmpPos ); + Menu* pTmpMenu = pCurrMenu->GetPopupMenu( nItemId ); + if ( pTmpMenu != nullptr ) + pCurrMenu = pTmpMenu; + else + { + nPos = nTmpPos; + eResult = RP_MENUITEM_INSTEAD_OF_POPUPMENU_FOUND; + } + } + else + eResult = RP_POPUPMENU_NOT_FOUND; + } + i++; + } + while ((i < nCount) && (eResult == RP_OK)); + + aResult.pPopupMenu = pCurrMenu; + aResult.nPos = nPos; + aResult.nLevel = nLevel; + aResult.eResult = eResult; + + return aResult; +} + +sal_uInt16 MenuBarMerger::FindMenuItem( std::u16string_view rCmd, Menu const * pCurrMenu ) +{ + for ( sal_uInt16 i = 0; i < pCurrMenu->GetItemCount(); i++ ) + { + const sal_uInt16 nItemId = pCurrMenu->GetItemId( i ); + if ( nItemId > 0 ) + { + if ( rCmd == pCurrMenu->GetItemCommand( nItemId ) ) + return i; + } + } + + return MENU_ITEM_NOTFOUND; +} + +bool MenuBarMerger::CreateSubMenu( + Menu* pSubMenu, + sal_uInt16& nItemId, + const OUString& rModuleIdentifier, + const AddonMenuContainer& rAddonSubMenu ) +{ + const sal_uInt32 nSize = rAddonSubMenu.size(); + for ( sal_uInt32 i = 0; i < nSize; i++ ) + { + const AddonMenuItem& rMenuItem = rAddonSubMenu[i]; + + if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier )) + { + if ( rMenuItem.aURL == SEPARATOR_STRING ) + { + pSubMenu->InsertSeparator(); + } + else + { + pSubMenu->InsertItem(nItemId, rMenuItem.aTitle); + pSubMenu->SetItemCommand( nItemId, rMenuItem.aURL ); + if ( !rMenuItem.aSubMenu.empty() ) + { + VclPtr<PopupMenu> pPopupMenu = VclPtr<PopupMenu>::Create(); + pSubMenu->SetPopupMenu( nItemId, pPopupMenu ); + ++nItemId; + + CreateSubMenu( pPopupMenu, nItemId, rModuleIdentifier, rMenuItem.aSubMenu ); + } + else + ++nItemId; + } + } + } + + return true; +} + +bool MenuBarMerger::MergeMenuItems( + Menu* pMenu, + sal_uInt16 nPos, + sal_uInt16 nModIndex, + sal_uInt16& nItemId, + const OUString& rModuleIdentifier, + const AddonMenuContainer& rAddonMenuItems ) +{ + sal_uInt16 nIndex( 0 ); + const sal_uInt32 nSize = rAddonMenuItems.size(); + for ( sal_uInt32 i = 0; i < nSize; i++ ) + { + const AddonMenuItem& rMenuItem = rAddonMenuItems[i]; + + if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier )) + { + if ( rMenuItem.aURL == SEPARATOR_STRING ) + { + pMenu->InsertSeparator(OString(), nPos+nModIndex+nIndex); + } + else + { + pMenu->InsertItem(nItemId, rMenuItem.aTitle, MenuItemBits::NONE, OString(), nPos+nModIndex+nIndex); + pMenu->SetItemCommand( nItemId, rMenuItem.aURL ); + if ( !rMenuItem.aSubMenu.empty() ) + { + VclPtr<PopupMenu> pSubMenu = VclPtr<PopupMenu>::Create(); + pMenu->SetPopupMenu( nItemId, pSubMenu ); + ++nItemId; + + CreateSubMenu( pSubMenu, nItemId, rModuleIdentifier, rMenuItem.aSubMenu ); + } + else + ++nItemId; + } + ++nIndex; + } + } + + return true; +} + +bool MenuBarMerger::ReplaceMenuItem( + Menu* pMenu, + sal_uInt16 nPos, + sal_uInt16& rItemId, + const OUString& rModuleIdentifier, + const AddonMenuContainer& rAddonMenuItems ) +{ + // There is no replace available. Therefore we first have to + // remove the old menu entry, + pMenu->RemoveItem( nPos ); + + return MergeMenuItems( pMenu, nPos, 0, rItemId, rModuleIdentifier, rAddonMenuItems ); +} + +bool MenuBarMerger::RemoveMenuItems( + Menu* pMenu, + sal_uInt16 nPos, + std::u16string_view rMergeCommandParameter ) +{ + const sal_uInt16 nParam( sal_uInt16( o3tl::toInt32(rMergeCommandParameter) )); + sal_uInt16 nCount = std::max( nParam, sal_uInt16(1) ); + + sal_uInt16 i = 0; + while (( nPos < pMenu->GetItemCount() ) && ( i < nCount )) + { + pMenu->RemoveItem( nPos ); + ++i; + } + + return true; +} + +bool MenuBarMerger::ProcessMergeOperation( + Menu* pMenu, + sal_uInt16 nPos, + sal_uInt16& nItemId, + std::u16string_view rMergeCommand, + std::u16string_view rMergeCommandParameter, + const OUString& rModuleIdentifier, + const AddonMenuContainer& rAddonMenuItems ) +{ + sal_uInt16 nModIndex( 0 ); + + if ( rMergeCommand == MERGECOMMAND_ADDBEFORE ) + { + nModIndex = 0; + return MergeMenuItems( pMenu, nPos, nModIndex, nItemId, rModuleIdentifier, rAddonMenuItems ); + } + else if ( rMergeCommand == MERGECOMMAND_ADDAFTER ) + { + nModIndex = 1; + return MergeMenuItems( pMenu, nPos, nModIndex, nItemId, rModuleIdentifier, rAddonMenuItems ); + } + else if ( rMergeCommand == MERGECOMMAND_REPLACE ) + { + return ReplaceMenuItem( pMenu, nPos, nItemId, rModuleIdentifier, rAddonMenuItems ); + } + else if ( rMergeCommand == MERGECOMMAND_REMOVE ) + { + return RemoveMenuItems( pMenu, nPos, rMergeCommandParameter ); + } + + return false; +} + +bool MenuBarMerger::ProcessFallbackOperation( + const ReferencePathInfo& aRefPathInfo, + sal_uInt16& rItemId, + std::u16string_view rMergeCommand, + std::u16string_view rMergeFallback, + const ::std::vector< OUString >& rReferencePath, + const std::u16string_view rModuleIdentifier, + const AddonMenuContainer& rAddonMenuItems ) +{ + if (( rMergeFallback == MERGEFALLBACK_IGNORE ) || + ( rMergeCommand == MERGECOMMAND_REPLACE ) || + ( rMergeCommand == MERGECOMMAND_REMOVE ) ) + { + return true; + } + else if ( rMergeFallback == MERGEFALLBACK_ADDPATH ) + { + Menu* pCurrMenu( aRefPathInfo.pPopupMenu ); + sal_Int32 nLevel( aRefPathInfo.nLevel ); + const sal_Int32 nSize( rReferencePath.size() ); + bool bFirstLevel( true ); + + while ( nLevel < nSize ) + { + if ( nLevel == nSize-1 ) + { + const sal_uInt32 nCount = rAddonMenuItems.size(); + for ( sal_uInt32 i = 0; i < nCount; ++i ) + { + const AddonMenuItem& rMenuItem = rAddonMenuItems[i]; + if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier )) + { + if ( rMenuItem.aURL == SEPARATOR_STRING ) + pCurrMenu->InsertSeparator(); + else + { + pCurrMenu->InsertItem(rItemId, rMenuItem.aTitle); + pCurrMenu->SetItemCommand( rItemId, rMenuItem.aURL ); + ++rItemId; + } + } + } + } + else + { + const OUString aCmd( rReferencePath[nLevel] ); + + VclPtr<PopupMenu> pPopupMenu = VclPtr<PopupMenu>::Create(); + + if ( bFirstLevel && ( aRefPathInfo.eResult == RP_MENUITEM_INSTEAD_OF_POPUPMENU_FOUND )) + { + // special case: menu item without popup + sal_uInt16 nInsPos = aRefPathInfo.nPos; + sal_uInt16 nSetItemId = pCurrMenu->GetItemId( nInsPos ); + pCurrMenu->SetItemCommand( nSetItemId, aCmd ); + pCurrMenu->SetPopupMenu( nSetItemId, pPopupMenu ); + } + else + { + // normal case: insert a new item with popup + pCurrMenu->InsertItem(rItemId, OUString()); + pCurrMenu->SetItemCommand( rItemId, aCmd ); + pCurrMenu->SetPopupMenu( rItemId, pPopupMenu ); + } + + pCurrMenu = pPopupMenu; + ++rItemId; + bFirstLevel = false; + } + ++nLevel; + } + return true; + } + + return false; +} + +void MenuBarMerger::GetMenuEntry( + const uno::Sequence< beans::PropertyValue >& rAddonMenuEntry, + AddonMenuItem& rAddonMenuItem ) +{ + // Reset submenu member + rAddonMenuItem.aSubMenu.clear(); + + for ( const beans::PropertyValue& rProp : rAddonMenuEntry ) + { + OUString aMenuEntryPropName = rProp.Name; + if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_URL ) + rProp.Value >>= rAddonMenuItem.aURL; + else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_TITLE ) + rProp.Value >>= rAddonMenuItem.aTitle; + else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_SUBMENU ) + { + uno::Sequence< uno::Sequence< beans::PropertyValue > > aSubMenu; + rProp.Value >>= aSubMenu; + GetSubMenu( aSubMenu, rAddonMenuItem.aSubMenu ); + } + else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_CONTEXT ) + rProp.Value >>= rAddonMenuItem.aContext; + } +} + +void MenuBarMerger::GetSubMenu( + const uno::Sequence< uno::Sequence< beans::PropertyValue > >& rSubMenuEntries, + AddonMenuContainer& rSubMenu ) +{ + rSubMenu.clear(); + + const sal_Int32 nCount = rSubMenuEntries.getLength(); + rSubMenu.reserve(rSubMenu.size() + nCount); + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + const uno::Sequence< beans::PropertyValue >& rMenuEntry = rSubMenuEntries[ i ]; + + AddonMenuItem aMenuItem; + GetMenuEntry( rMenuEntry, aMenuItem ); + rSubMenu.push_back( aMenuItem ); + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/menubarwrapper.cxx b/framework/source/uielement/menubarwrapper.cxx new file mode 100644 index 000000000..3bbd71ed7 --- /dev/null +++ b/framework/source/uielement/menubarwrapper.cxx @@ -0,0 +1,352 @@ +/* -*- 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 <uielement/menubarwrapper.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/ui/UIElementType.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/util/URLTransformer.hpp> + +#include <comphelper/sequence.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <toolkit/awt/vclxmenu.hxx> +#include <utility> +#include <vcl/svapp.hxx> + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::frame; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace com::sun::star::awt; +using namespace com::sun::star::util; +using namespace ::com::sun::star::ui; + +namespace framework +{ + +void SAL_CALL MenuBarWrapper::acquire() noexcept \ +{ \ + /* Don't use mutex in methods of XInterface! */ \ + UIConfigElementWrapperBase::acquire(); \ +} \ + \ +void SAL_CALL MenuBarWrapper::release() noexcept \ +{ \ + /* Don't use mutex in methods of XInterface! */ \ + UIConfigElementWrapperBase::release(); \ +} + +css::uno::Any SAL_CALL MenuBarWrapper::queryInterface( const css::uno::Type& aType ) +{ + /* Attention: Don't use mutex or guard in this method!!! Is a method of XInterface. */ + /* Ask for my own supported interfaces ... */ + css::uno::Any aReturn = ::cppu::queryInterface( aType, + static_cast< css::lang::XTypeProvider* >( this ), + static_cast< css::ui::XUIElement* >( this ), + static_cast< css::ui::XUIElementSettings* >( this ), + static_cast< css::beans::XMultiPropertySet* >( this ), + static_cast< css::beans::XFastPropertySet* >( this ), + static_cast< css::beans::XPropertySet* >( this ), + static_cast< css::lang::XInitialization* >( this ), + static_cast< css::lang::XComponent* >( this ), + static_cast< css::util::XUpdatable* >( this ), + static_cast< css::ui::XUIConfigurationListener* >( this ), + static_cast< css::container::XNameAccess* >( static_cast< css::container::XElementAccess* >( this ) ) + ); + /* If searched interface not supported by this class ... */ + if ( !aReturn.hasValue() ) + { + /* ... ask baseclass for interfaces! */ + aReturn = UIConfigElementWrapperBase::queryInterface( aType ); + } + /* Return result of this search. */ + return aReturn; +} + +css::uno::Sequence< sal_Int8 > SAL_CALL MenuBarWrapper::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +css::uno::Sequence< css::uno::Type > SAL_CALL MenuBarWrapper::getTypes() +{ + /* Attention: "TYPES" will expand to "(...)"! */ + static cppu::OTypeCollection ourTypeCollection { + cppu::UnoType<css::lang::XTypeProvider>::get() , + cppu::UnoType<css::ui::XUIElement>::get() , + cppu::UnoType<css::ui::XUIElementSettings>::get() , + cppu::UnoType<css::beans::XMultiPropertySet>::get() , + cppu::UnoType<css::beans::XFastPropertySet>::get() , + cppu::UnoType<css::beans::XPropertySet>::get() , + cppu::UnoType<css::lang::XInitialization>::get() , + cppu::UnoType<css::lang::XComponent>::get() , + cppu::UnoType<css::util::XUpdatable>::get() , + cppu::UnoType<css::ui::XUIConfigurationListener>::get() , + cppu::UnoType<css::container::XNameAccess>::get() }; + return ourTypeCollection.getTypes(); +} + +MenuBarWrapper::MenuBarWrapper( + css::uno::Reference< css::uno::XComponentContext > xContext + ) +: UIConfigElementWrapperBase( UIElementType::MENUBAR ), + m_bRefreshPopupControllerCache( true ), + m_xContext(std::move( xContext )) +{ +} + +MenuBarWrapper::~MenuBarWrapper() +{ +} + +void SAL_CALL MenuBarWrapper::dispose() +{ + Reference< XComponent > xThis(this); + + css::lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + SolarMutexGuard g; + + m_xMenuBarManager->dispose(); + m_xMenuBarManager.clear(); + m_xConfigSource.clear(); + m_xConfigData.clear(); + + m_xMenuBar.clear(); + m_bDisposed = true; +} + +// XInitialization +void SAL_CALL MenuBarWrapper::initialize( const Sequence< Any >& aArguments ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_bInitialized ) + return; + + OUString aModuleIdentifier; + UIConfigElementWrapperBase::initialize( aArguments ); + + Reference< XFrame > xFrame( m_xWeakFrame ); + if ( !(xFrame.is() && m_xConfigSource.is()) ) + return; + + // Create VCL menubar which will be filled with settings data + VclPtr<MenuBar> pVCLMenuBar; + { + SolarMutexGuard aSolarMutexGuard; + pVCLMenuBar = VclPtr<MenuBar>::Create(); + } + + Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext ); + + try + { + aModuleIdentifier = xModuleManager->identify( xFrame ); + } + catch( const Exception& ) + { + } + + Reference< XURLTransformer > xTrans; + try + { + xTrans.set( URLTransformer::create(m_xContext) ); + m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false ); + if ( m_xConfigData.is() ) + { + // Fill menubar with container contents + sal_uInt16 nId = 1; + MenuBarManager::FillMenuWithConfiguration( nId, pVCLMenuBar, aModuleIdentifier, m_xConfigData, xTrans ); + } + } + catch ( const NoSuchElementException& ) + { + } + + bool bMenuOnly( false ); + for ( const Any& rArg : aArguments ) + { + PropertyValue aPropValue; + if ( rArg >>= aPropValue ) + { + if ( aPropValue.Name == "MenuOnly" ) + aPropValue.Value >>= bMenuOnly; + } + } + + if ( !bMenuOnly ) + { + // Initialize menubar manager with our vcl menu bar. There are some situations where we only want to get the menu without any + // interaction which is done by the menu bar manager. This must be requested by a special property called "MenuOnly". Be careful + // a menu bar created with this property is not fully supported. It must be attached to a real menu bar manager to have full + // support. This feature is currently used for "Inplace editing"! + Reference< XDispatchProvider > xDispatchProvider; + + m_xMenuBarManager = new MenuBarManager( m_xContext, + xFrame, + xTrans, + xDispatchProvider, + aModuleIdentifier, + pVCLMenuBar, + false ); + } + + // Initialize toolkit menu bar implementation to have awt::XMenuBar for data exchange. + // Don't use this toolkit menu bar or one of its functions. It is only used as a data container! + m_xMenuBar = new VCLXMenuBar( pVCLMenuBar ); +} + +// XUIElementSettings +void SAL_CALL MenuBarWrapper::updateSettings() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !m_xMenuBarManager.is() ) + return; + + if ( m_xConfigSource.is() && m_bPersistent ) + { + try + { + MenuBarManager* pMenuBarManager = static_cast< MenuBarManager *>( m_xMenuBarManager.get() ); + + m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false ); + if ( m_xConfigData.is() ) + pMenuBarManager->SetItemContainer( m_xConfigData ); + } + catch ( const NoSuchElementException& ) + { + } + } + else if ( !m_bPersistent ) + { + // Transient menubar: do nothing + } +} +void MenuBarWrapper::impl_fillNewData() +{ + // Transient menubar => Fill menubar with new data + MenuBarManager* pMenuBarManager = static_cast< MenuBarManager *>( m_xMenuBarManager.get() ); + + if ( pMenuBarManager ) + pMenuBarManager->SetItemContainer( m_xConfigData ); +} + +void MenuBarWrapper::fillPopupControllerCache() +{ + if ( m_bRefreshPopupControllerCache ) + { + MenuBarManager* pMenuBarManager = static_cast< MenuBarManager *>( m_xMenuBarManager.get() ); + if ( pMenuBarManager ) + pMenuBarManager->GetPopupController( m_aPopupControllerCache ); + if ( !m_aPopupControllerCache.empty() ) + m_bRefreshPopupControllerCache = false; + } +} + +// XElementAccess +Type SAL_CALL MenuBarWrapper::getElementType() +{ + return cppu::UnoType<XDispatchProvider>::get(); +} + +sal_Bool SAL_CALL MenuBarWrapper::hasElements() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + fillPopupControllerCache(); + return ( !m_aPopupControllerCache.empty() ); +} + +// XNameAccess +Any SAL_CALL MenuBarWrapper::getByName( + const OUString& aName ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + fillPopupControllerCache(); + + PopupControllerCache::const_iterator pIter = m_aPopupControllerCache.find( aName ); + if ( pIter == m_aPopupControllerCache.end() ) + throw container::NoSuchElementException(); + + uno::Reference< frame::XDispatchProvider > xDispatchProvider = pIter->second.m_xDispatchProvider; + return uno::Any( xDispatchProvider ); +} + +Sequence< OUString > SAL_CALL MenuBarWrapper::getElementNames() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + fillPopupControllerCache(); + + return comphelper::mapKeysToSequence( m_aPopupControllerCache ); +} + +sal_Bool SAL_CALL MenuBarWrapper::hasByName( + const OUString& aName ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + fillPopupControllerCache(); + + PopupControllerCache::const_iterator pIter = m_aPopupControllerCache.find( aName ); + if ( pIter != m_aPopupControllerCache.end() ) + return true; + else + return false; +} + +// XUIElement +Reference< XInterface > SAL_CALL MenuBarWrapper::getRealInterface() +{ + if ( m_bDisposed ) + throw DisposedException(); + + return Reference< XInterface >( m_xMenuBarManager, UNO_QUERY ); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/newmenucontroller.cxx b/framework/source/uielement/newmenucontroller.cxx new file mode 100644 index 000000000..d282509d9 --- /dev/null +++ b/framework/source/uielement/newmenucontroller.cxx @@ -0,0 +1,513 @@ +/* -*- 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 <uielement/newmenucontroller.hxx> +#include <menuconfiguration.hxx> + +#include <services.h> + +#include <com/sun/star/awt/MenuItemType.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <svtools/acceleratorexecute.hxx> +#include <svtools/imagemgr.hxx> +#include <toolkit/awt/vclxmenu.hxx> +#include <tools/urlobj.hxx> +#include <unotools/dynamicmenuoptions.hxx> +#include <unotools/moduleoptions.hxx> +#include <osl/mutex.hxx> +#include <cppuhelper/supportsservice.hxx> + +// Defines +constexpr OUStringLiteral aSlotNewDocDirect = u".uno:AddDirect"; +constexpr OUStringLiteral aSlotAutoPilot = u".uno:AutoPilotMenu"; + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace com::sun::star::container; +using namespace com::sun::star::ui; + +namespace framework +{ + +OUString SAL_CALL NewMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.NewMenuController"; +} + +sal_Bool SAL_CALL NewMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL NewMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + +void NewMenuController::setMenuImages( PopupMenu* pPopupMenu, bool bSetImages ) +{ + sal_uInt16 nItemCount = pPopupMenu->GetItemCount(); + Reference< XFrame > xFrame( m_xFrame ); + + for ( sal_uInt16 i = 0; i < nItemCount; i++ ) + { + sal_uInt16 nItemId = pPopupMenu->GetItemId( i ); + if ( nItemId != 0 ) + { + if ( bSetImages ) + { + OUString aImageId; + OUString aCmd( pPopupMenu->GetItemCommand( nItemId ) ); + void* nAttributePtr = pPopupMenu->GetUserValue( nItemId ); + MenuAttributes* pAttributes = static_cast<MenuAttributes *>(nAttributePtr); + if (pAttributes) + aImageId = pAttributes->aImageId; + + INetURLObject aURLObj( aImageId.isEmpty() ? aCmd : aImageId ); + Image aImage = SvFileInformationManager::GetImageNoDefault( aURLObj ); + if ( !aImage ) + aImage = vcl::CommandInfoProvider::GetImageForCommand(aCmd, xFrame); + + if ( !!aImage ) + pPopupMenu->SetItemImage( nItemId, aImage ); + } + else + pPopupMenu->SetItemImage( nItemId, Image() ); + } + } +} + +void NewMenuController::determineAndSetNewDocAccel(const css::awt::KeyEvent& rKeyCode) +{ + sal_uInt16 nCount(m_xPopupMenu->getItemCount()); + sal_uInt16 nId( 0 ); + bool bFound( false ); + OUString aCommand; + + if ( !m_aEmptyDocURL.isEmpty() ) + { + // Search for the empty document URL + + for ( sal_uInt16 i = 0; i < nCount; i++ ) + { + if (m_xPopupMenu->getItemType(i) != css::awt::MenuItemType_SEPARATOR) + { + nId = m_xPopupMenu->getItemId(i); + aCommand = m_xPopupMenu->getCommand(nId); + if ( aCommand.startsWith( m_aEmptyDocURL ) ) + { + m_xPopupMenu->setAcceleratorKeyEvent(nId, rKeyCode); + bFound = true; + break; + } + } + } + } + + if ( bFound ) + return; + + // Search for the default module name + OUString aDefaultModuleName( SvtModuleOptions().GetDefaultModuleName() ); + if ( aDefaultModuleName.isEmpty() ) + return; + + for ( sal_uInt16 i = 0; i < nCount; i++ ) + { + if (m_xPopupMenu->getItemType(i) != css::awt::MenuItemType_SEPARATOR) + { + nId = m_xPopupMenu->getItemId(i); + aCommand = m_xPopupMenu->getCommand(nId); + if ( aCommand.indexOf( aDefaultModuleName ) >= 0 ) + { + m_xPopupMenu->setAcceleratorKeyEvent(nId, rKeyCode); + break; + } + } + } +} + +void NewMenuController::setAccelerators() +{ + if ( !m_bModuleIdentified ) + return; + + Reference< XAcceleratorConfiguration > xDocAccelCfg( m_xDocAcceleratorManager ); + Reference< XAcceleratorConfiguration > xModuleAccelCfg( m_xModuleAcceleratorManager ); + Reference< XAcceleratorConfiguration > xGlobalAccelCfg( m_xGlobalAcceleratorManager ); + + if ( !m_bAcceleratorCfg ) + { + // Retrieve references on demand + m_bAcceleratorCfg = true; + if ( !xDocAccelCfg.is() ) + { + Reference< XController > xController = m_xFrame->getController(); + Reference< XModel > xModel; + if ( xController.is() ) + { + xModel = xController->getModel(); + if ( xModel.is() ) + { + Reference< XUIConfigurationManagerSupplier > xSupplier( xModel, UNO_QUERY ); + if ( xSupplier.is() ) + { + Reference< XUIConfigurationManager > xDocUICfgMgr = xSupplier->getUIConfigurationManager(); + if ( xDocUICfgMgr.is() ) + { + xDocAccelCfg = xDocUICfgMgr->getShortCutManager(); + m_xDocAcceleratorManager = xDocAccelCfg; + } + } + } + } + } + + if ( !xModuleAccelCfg.is() ) + { + Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier = + theModuleUIConfigurationManagerSupplier::get( m_xContext ); + Reference< XUIConfigurationManager > xUICfgMgr = xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleIdentifier ); + if ( xUICfgMgr.is() ) + { + xModuleAccelCfg = xUICfgMgr->getShortCutManager(); + m_xModuleAcceleratorManager = xModuleAccelCfg; + } + } + + if ( !xGlobalAccelCfg.is() ) + { + xGlobalAccelCfg = GlobalAcceleratorConfiguration::create( m_xContext ); + m_xGlobalAcceleratorManager = xGlobalAccelCfg; + } + } + + vcl::KeyCode aEmptyKeyCode; + sal_uInt16 nItemCount(m_xPopupMenu->getItemCount()); + std::vector< vcl::KeyCode > aMenuShortCuts; + std::vector< OUString > aCmds; + std::vector< sal_uInt16 > aIds; + for ( sal_uInt16 i = 0; i < nItemCount; i++ ) + { + if (m_xPopupMenu->getItemType(i) != css::awt::MenuItemType_SEPARATOR) + { + sal_uInt16 nId(m_xPopupMenu->getItemId(i)); + aIds.push_back( nId ); + aMenuShortCuts.push_back( aEmptyKeyCode ); + aCmds.push_back(m_xPopupMenu->getCommand(nId)); + } + } + + sal_uInt32 nSeqCount( aIds.size() ); + + if ( m_bNewMenu ) + nSeqCount+=1; + + Sequence< OUString > aSeq( nSeqCount ); + auto aSeqRange = asNonConstRange(aSeq); + + // Add a special command for our "New" menu. + if ( m_bNewMenu ) + { + aSeqRange[nSeqCount-1] = m_aCommandURL; + aMenuShortCuts.push_back( aEmptyKeyCode ); + } + + const sal_uInt32 nCount = aCmds.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + aSeqRange[i] = aCmds[i]; + + if ( m_xGlobalAcceleratorManager.is() ) + retrieveShortcutsFromConfiguration( xGlobalAccelCfg, aSeq, aMenuShortCuts ); + if ( m_xModuleAcceleratorManager.is() ) + retrieveShortcutsFromConfiguration( xModuleAccelCfg, aSeq, aMenuShortCuts ); + if ( m_xDocAcceleratorManager.is() ) + retrieveShortcutsFromConfiguration( xDocAccelCfg, aSeq, aMenuShortCuts ); + + const sal_uInt32 nCount2 = aIds.size(); + for ( sal_uInt32 i = 0; i < nCount2; i++ ) + m_xPopupMenu->setAcceleratorKeyEvent(aIds[i], svt::AcceleratorExecute::st_VCLKey2AWTKey(aMenuShortCuts[i])); + + // Special handling for "New" menu short-cut should be set at the + // document which will be opened using it. + if ( m_bNewMenu ) + { + if ( aMenuShortCuts[nSeqCount-1] != aEmptyKeyCode ) + determineAndSetNewDocAccel(svt::AcceleratorExecute::st_VCLKey2AWTKey(aMenuShortCuts[nSeqCount-1])); + } +} + +void NewMenuController::retrieveShortcutsFromConfiguration( + const Reference< XAcceleratorConfiguration >& rAccelCfg, + const Sequence< OUString >& rCommands, + std::vector< vcl::KeyCode >& aMenuShortCuts ) +{ + if ( !rAccelCfg.is() ) + return; + + try + { + css::awt::KeyEvent aKeyEvent; + Sequence< Any > aSeqKeyCode = rAccelCfg->getPreferredKeyEventsForCommandList( rCommands ); + for ( sal_Int32 i = 0; i < aSeqKeyCode.getLength(); i++ ) + { + if ( aSeqKeyCode[i] >>= aKeyEvent ) + aMenuShortCuts[i] = svt::AcceleratorExecute::st_AWTKey2VCLKey( aKeyEvent ); + } + } + catch ( const IllegalArgumentException& ) + { + } +} + +NewMenuController::NewMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + svt::PopupMenuControllerBase( xContext ), + m_bShowImages( true ), + m_bNewMenu( false ), + m_bModuleIdentified( false ), + m_bAcceleratorCfg( false ), + m_aTargetFrame( "_default" ), + m_xContext( xContext ) +{ +} + +NewMenuController::~NewMenuController() +{ +} + +// private function +void NewMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getFromUnoTunnel<VCLXMenu>( rPopupMenu )); + PopupMenu* pVCLPopupMenu = nullptr; + + SolarMutexGuard aSolarMutexGuard; + + resetPopupMenu( rPopupMenu ); + if ( pPopupMenu ) + pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu()); + + if ( !pVCLPopupMenu ) + return; + + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + URL aTargetURL; + aTargetURL.Complete = m_bNewMenu ? OUString(aSlotNewDocDirect) : OUString(aSlotAutoPilot); + m_xURLTransformer->parseStrict( aTargetURL ); + Reference< XDispatch > xMenuItemDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + if(xMenuItemDispatch == nullptr) + return; + + const std::vector< SvtDynMenuEntry > aDynamicMenuEntries = + SvtDynamicMenuOptions::GetMenu( m_bNewMenu ? EDynamicMenuType::NewMenu : EDynamicMenuType::WizardMenu ); + + sal_uInt16 nItemId = 1; + + for ( const auto& aDynamicMenuEntry : aDynamicMenuEntries ) + { + if ( aDynamicMenuEntry.sTitle.isEmpty() && aDynamicMenuEntry.sURL.isEmpty() ) + continue; + + if ( aDynamicMenuEntry.sURL == "private:separator" ) + rPopupMenu->insertSeparator(-1); + else + { + rPopupMenu->insertItem(nItemId, aDynamicMenuEntry.sTitle, 0, -1); + rPopupMenu->setCommand(nItemId, aDynamicMenuEntry.sURL); + + void* nAttributePtr = MenuAttributes::CreateAttribute( aDynamicMenuEntry.sTargetName, aDynamicMenuEntry.sImageIdentifier ); + pPopupMenu->setUserValue(nItemId, nAttributePtr, MenuAttributes::ReleaseAttribute); + + nItemId++; + } + } + + if ( m_bShowImages ) + setMenuImages( pVCLPopupMenu, m_bShowImages ); +} + +// XEventListener +void SAL_CALL NewMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + osl::MutexGuard aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + m_xContext.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL NewMenuController::statusChanged( const FeatureStateEvent& ) +{ +} + +// XMenuListener +void SAL_CALL NewMenuController::itemSelected( const css::awt::MenuEvent& rEvent ) +{ + Reference< css::awt::XPopupMenu > xPopupMenu; + Reference< XComponentContext > xContext; + + { + osl::MutexGuard aLock(m_aMutex); + xPopupMenu = m_xPopupMenu; + xContext = m_xContext; + } + + if ( !xPopupMenu.is() ) + return; + + VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getFromUnoTunnel<VCLXMenu>( xPopupMenu )); + if ( !pPopupMenu ) + return; + + OUString aURL; + OUString aTargetFrame( m_aTargetFrame ); + + { + SolarMutexGuard aSolarMutexGuard; + aURL = pPopupMenu->getCommand(rEvent.MenuId); + void* nAttributePtr = pPopupMenu->getUserValue(rEvent.MenuId); + MenuAttributes* pAttributes = static_cast<MenuAttributes *>(nAttributePtr); + if (pAttributes) + aTargetFrame = pAttributes->aTargetFrame; + } + + Sequence< PropertyValue > aArgsList{ comphelper::makePropertyValue("Referer", + OUString( "private:user" )) }; + + dispatchCommand( aURL, aArgsList, aTargetFrame ); +} + +void SAL_CALL NewMenuController::itemActivated( const css::awt::MenuEvent& ) +{ + SolarMutexGuard aSolarMutexGuard; + if ( !(m_xFrame.is() && m_xPopupMenu.is()) ) + return; + + VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getFromUnoTunnel<VCLXMenu>( m_xPopupMenu )); + if ( !pPopupMenu ) + return; + + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + bool bShowImages( rSettings.GetUseImagesInMenus() ); + OUString aIconTheme( rSettings.DetermineIconTheme() ); + + PopupMenu* pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu()); + + if ( m_bShowImages != bShowImages || m_aIconTheme != aIconTheme ) + { + m_bShowImages = bShowImages; + m_aIconTheme = aIconTheme; + setMenuImages( pVCLPopupMenu, m_bShowImages ); + } + + setAccelerators(); +} + +// XPopupMenuController +void NewMenuController::impl_setPopupMenu() +{ + + if ( m_xPopupMenu.is() ) + fillPopupMenu( m_xPopupMenu ); + + // Identify module that we are attach to. It's our context that we need to know. + Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext ); + try + { + m_aModuleIdentifier = xModuleManager->identify( m_xFrame ); + m_bModuleIdentified = true; + + if ( !m_aModuleIdentifier.isEmpty() ) + { + Sequence< PropertyValue > aSeq; + + if ( xModuleManager->getByName( m_aModuleIdentifier ) >>= aSeq ) + { + for ( PropertyValue const & prop : std::as_const(aSeq) ) + { + if ( prop.Name == "ooSetupFactoryEmptyDocumentURL" ) + { + prop.Value >>= m_aEmptyDocURL; + break; + } + } + } + } + } + catch ( const RuntimeException& ) + { + throw; + } + catch ( const Exception& ) + { + } +} + +// XInitialization +void SAL_CALL NewMenuController::initialize( const Sequence< Any >& aArguments ) +{ + osl::MutexGuard aLock( m_aMutex ); + + bool bInitialized( m_bInitialized ); + if ( bInitialized ) + return; + + svt::PopupMenuControllerBase::initialize( aArguments ); + + if ( m_bInitialized ) + { + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + + m_bShowImages = rSettings.GetUseImagesInMenus(); + m_aIconTheme = rSettings.DetermineIconTheme(); + m_bNewMenu = m_aCommandURL == aSlotNewDocDirect; + } +} + +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_NewMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new framework::NewMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/objectmenucontroller.cxx b/framework/source/uielement/objectmenucontroller.cxx new file mode 100644 index 000000000..62b5586ee --- /dev/null +++ b/framework/source/uielement/objectmenucontroller.cxx @@ -0,0 +1,136 @@ +/* -*- 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 <stdtypes.h> + +#include <com/sun/star/embed/VerbAttributes.hpp> +#include <com/sun/star/embed/VerbDescriptor.hpp> + +#include <svtools/popupmenucontrollerbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <vcl/svapp.hxx> +#include <osl/mutex.hxx> + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace framework; + +namespace { + +class ObjectMenuController : public svt::PopupMenuControllerBase +{ + using svt::PopupMenuControllerBase::disposing; + +public: + explicit ObjectMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.ObjectMenuController"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.frame.PopupMenuController"}; + } + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + +private: + void fillPopupMenu( const css::uno::Sequence< css::embed::VerbDescriptor >& rVerbCommandSeq, css::uno::Reference< css::awt::XPopupMenu > const & rPopupMenu ); +}; + +ObjectMenuController::ObjectMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + svt::PopupMenuControllerBase( xContext ) +{ +} + +// private function +void ObjectMenuController::fillPopupMenu( const Sequence< css::embed::VerbDescriptor >& rVerbCommandSeq, Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + const css::embed::VerbDescriptor* pVerbCommandArray = rVerbCommandSeq.getConstArray(); + + SolarMutexGuard aSolarMutexGuard; + + resetPopupMenu( rPopupMenu ); + + static const OUStringLiteral aVerbCommand( u".uno:ObjectMenue?VerbID:short=" ); + for ( sal_Int32 i = 0; i < rVerbCommandSeq.getLength(); i++ ) + { + const css::embed::VerbDescriptor& rVerb = pVerbCommandArray[i]; + if ( rVerb.VerbAttributes & css::embed::VerbAttributes::MS_VERBATTR_ONCONTAINERMENU ) + { + m_xPopupMenu->insertItem( i+1, rVerb.VerbName, 0, i ); + OUString aCommand = aVerbCommand + OUString::number( rVerb.VerbID ); + m_xPopupMenu->setCommand( i+1, aCommand ); // Store verb command + } + } +} + +// XEventListener +void SAL_CALL ObjectMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + osl::MutexGuard aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL ObjectMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + Sequence < css::embed::VerbDescriptor > aVerbCommandSeq; + if ( Event.State >>= aVerbCommandSeq ) + { + osl::MutexGuard aLock( m_aMutex ); + if ( m_xPopupMenu.is() ) + fillPopupMenu( aVerbCommandSeq, m_xPopupMenu ); + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ObjectMenuController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new ObjectMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/popuptoolbarcontroller.cxx b/framework/source/uielement/popuptoolbarcontroller.cxx new file mode 100644 index 000000000..6cf02e5e4 --- /dev/null +++ b/framework/source/uielement/popuptoolbarcontroller.cxx @@ -0,0 +1,799 @@ +/* -*- 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 <bitmaps.hlst> + +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/propertyvalue.hxx> +#include <menuconfiguration.hxx> +#include <svtools/imagemgr.hxx> +#include <svtools/toolboxcontroller.hxx> +#include <toolkit/awt/vclxmenu.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/diagnose_ex.h> +#include <tools/urlobj.hxx> +#include <utility> +#include <vcl/commandinfoprovider.hxx> +#include <vcl/menu.hxx> +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> + +#include <com/sun/star/awt/PopupMenuDirection.hpp> +#include <com/sun/star/awt/XPopupMenu.hpp> +#include <com/sun/star/frame/thePopupMenuControllerFactory.hpp> +#include <com/sun/star/frame/XPopupMenuController.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/frame/XSubToolbarController.hpp> +#include <com/sun/star/frame/XUIControllerFactory.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/ucb/CommandFailedException.hpp> +#include <com/sun/star/ucb/ContentCreationException.hpp> +#include <com/sun/star/util/XModifiable.hpp> + +using namespace framework; + +namespace +{ + +typedef cppu::ImplInheritanceHelper< svt::ToolboxController, + css::lang::XServiceInfo > + ToolBarBase; + +class PopupMenuToolbarController : public ToolBarBase +{ +public: + // XComponent + virtual void SAL_CALL dispose() override; + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + // XToolbarController + virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createPopupWindow() override; + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override; + +protected: + PopupMenuToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + OUString aPopupCommand = OUString() ); + virtual void functionExecuted( const OUString &rCommand ); + virtual ToolBoxItemBits getDropDownStyle() const; + void createPopupMenuController(); + + bool m_bHasController; + bool m_bResourceURL; + OUString m_aPopupCommand; + css::uno::Reference< css::awt::XPopupMenu > m_xPopupMenu; + +private: + css::uno::Reference< css::frame::XUIControllerFactory > m_xPopupMenuFactory; + css::uno::Reference< css::frame::XPopupMenuController > m_xPopupMenuController; +}; + +PopupMenuToolbarController::PopupMenuToolbarController( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + OUString aPopupCommand ) + : ToolBarBase( xContext, css::uno::Reference< css::frame::XFrame >(), /*aCommandURL*/OUString() ) + , m_bHasController( false ) + , m_bResourceURL( false ) + , m_aPopupCommand(std::move( aPopupCommand )) +{ +} + +void SAL_CALL PopupMenuToolbarController::dispose() +{ + svt::ToolboxController::dispose(); + + osl::MutexGuard aGuard( m_aMutex ); + if( m_xPopupMenuController.is() ) + { + css::uno::Reference< css::lang::XComponent > xComponent( + m_xPopupMenuController, css::uno::UNO_QUERY ); + if( xComponent.is() ) + { + try + { + xComponent->dispose(); + } + catch (...) + {} + } + m_xPopupMenuController.clear(); + } + + m_xContext.clear(); + m_xPopupMenuFactory.clear(); + m_xPopupMenu.clear(); +} + +void SAL_CALL PopupMenuToolbarController::initialize( + const css::uno::Sequence< css::uno::Any >& aArguments ) +{ + ToolboxController::initialize( aArguments ); + + osl::MutexGuard aGuard( m_aMutex ); + if ( !m_aPopupCommand.getLength() ) + m_aPopupCommand = m_aCommandURL; + + try + { + m_xPopupMenuFactory.set( + css::frame::thePopupMenuControllerFactory::get( m_xContext ) ); + m_bHasController = m_xPopupMenuFactory->hasController( + m_aPopupCommand, getModuleName() ); + } + catch (const css::uno::Exception&) + { + TOOLS_INFO_EXCEPTION( "fwk.uielement", "" ); + } + + if ( !m_bHasController && m_aPopupCommand.startsWith( "private:resource/" ) ) + { + m_bResourceURL = true; + m_bHasController = true; + } + + SolarMutexGuard aSolarLock; + ToolBox* pToolBox = nullptr; + ToolBoxItemId nItemId; + if ( getToolboxId( nItemId, &pToolBox ) ) + { + ToolBoxItemBits nCurStyle( pToolBox->GetItemBits( nItemId ) ); + ToolBoxItemBits nSetStyle( getDropDownStyle() ); + pToolBox->SetItemBits( nItemId, + m_bHasController ? + nCurStyle | nSetStyle : + nCurStyle & ~nSetStyle ); + } + +} + +void SAL_CALL PopupMenuToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + if ( m_bResourceURL ) + return; + + ToolBox* pToolBox = nullptr; + ToolBoxItemId nItemId; + if ( getToolboxId( nItemId, &pToolBox ) ) + { + SolarMutexGuard aSolarLock; + pToolBox->EnableItem( nItemId, rEvent.IsEnabled ); + bool bValue; + if ( rEvent.State >>= bValue ) + pToolBox->CheckItem( nItemId, bValue ); + } +} + +css::uno::Reference< css::awt::XWindow > SAL_CALL +PopupMenuToolbarController::createPopupWindow() +{ + css::uno::Reference< css::awt::XWindow > xRet; + + osl::MutexGuard aGuard( m_aMutex ); + if ( !m_bHasController ) + return xRet; + + createPopupMenuController(); + + SolarMutexGuard aSolarLock; + VclPtr< ToolBox > pToolBox = static_cast< ToolBox* >( VCLUnoHelper::GetWindow( getParent() ) ); + if ( !pToolBox ) + return xRet; + + pToolBox->SetItemDown( m_nToolBoxId, true ); + WindowAlign eAlign( pToolBox->GetAlign() ); + + // If the parent ToolBox is in popup mode (e.g. sub toolbar, overflow popup), + // its ToolBarManager can be disposed along with our controller, destroying + // m_xPopupMenu, while the latter still in execute. This should be fixed at a + // different level, for now just hold it here so it won't crash. + css::uno::Reference< css::awt::XPopupMenu > xPopupMenu ( m_xPopupMenu ); + sal_uInt16 nId = xPopupMenu->execute( + css::uno::Reference< css::awt::XWindowPeer >( getParent(), css::uno::UNO_QUERY ), + VCLUnoHelper::ConvertToAWTRect( pToolBox->GetItemRect( m_nToolBoxId ) ), + ( eAlign == WindowAlign::Top || eAlign == WindowAlign::Bottom ) ? + css::awt::PopupMenuDirection::EXECUTE_DOWN : + css::awt::PopupMenuDirection::EXECUTE_RIGHT ); + pToolBox->SetItemDown( m_nToolBoxId, false ); + + if ( nId ) + functionExecuted( xPopupMenu->getCommand( nId ) ); + + return xRet; +} + +void PopupMenuToolbarController::functionExecuted( const OUString &/*rCommand*/) +{ +} + +ToolBoxItemBits PopupMenuToolbarController::getDropDownStyle() const +{ + return ToolBoxItemBits::DROPDOWN; +} + +void PopupMenuToolbarController::createPopupMenuController() +{ + if( !m_bHasController ) + return; + + if ( m_xPopupMenuController.is() ) + { + m_xPopupMenuController->updatePopupMenu(); + } + else + { + css::uno::Sequence<css::uno::Any> aArgs { + css::uno::Any(comphelper::makePropertyValue("Frame", m_xFrame)), + css::uno::Any(comphelper::makePropertyValue("ModuleIdentifier", m_sModuleName)), + css::uno::Any(comphelper::makePropertyValue("InToolbar", true)) + }; + + try + { + m_xPopupMenu.set( + m_xContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.awt.PopupMenu", m_xContext ), + css::uno::UNO_QUERY_THROW ); + + if (m_bResourceURL) + { + sal_Int32 nAppendIndex = aArgs.getLength(); + aArgs.realloc(nAppendIndex + 1); + aArgs.getArray()[nAppendIndex] <<= comphelper::makePropertyValue("ResourceURL", m_aPopupCommand); + + m_xPopupMenuController.set( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.comp.framework.ResourceMenuController", aArgs, m_xContext), css::uno::UNO_QUERY_THROW ); + } + else + { + m_xPopupMenuController.set( m_xPopupMenuFactory->createInstanceWithArgumentsAndContext( + m_aPopupCommand, aArgs, m_xContext), css::uno::UNO_QUERY_THROW ); + } + + m_xPopupMenuController->setPopupMenu( m_xPopupMenu ); + } + catch ( const css::uno::Exception & ) + { + TOOLS_INFO_EXCEPTION( "fwk.uielement", "" ); + m_xPopupMenu.clear(); + } + } +} + +class GenericPopupToolbarController : public PopupMenuToolbarController +{ +public: + GenericPopupToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::uno::Any >& rxArgs ); + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& rxArgs ) override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const & rServiceName) override; + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + +private: + bool m_bSplitButton, m_bReplaceWithLast; + void functionExecuted(const OUString &rCommand) override; + ToolBoxItemBits getDropDownStyle() const override; +}; + +GenericPopupToolbarController::GenericPopupToolbarController( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Sequence< css::uno::Any >& rxArgs ) + : PopupMenuToolbarController( xContext ) + , m_bReplaceWithLast( false ) +{ + css::beans::PropertyValue aPropValue; + for ( const auto& arg: rxArgs ) + { + if ( ( arg >>= aPropValue ) && aPropValue.Name == "Value" ) + { + sal_Int32 nIdx{ 0 }; + OUString aValue; + aPropValue.Value >>= aValue; + m_aPopupCommand = aValue.getToken(0, ';', nIdx); + m_bReplaceWithLast = aValue.getToken(0, ';', nIdx).toBoolean(); + break; + } + } + m_bSplitButton = m_bReplaceWithLast || !m_aPopupCommand.isEmpty(); +} + +OUString GenericPopupToolbarController::getImplementationName() +{ + return "com.sun.star.comp.framework.GenericPopupToolbarController"; +} + +sal_Bool GenericPopupToolbarController::supportsService(OUString const & rServiceName) +{ + return cppu::supportsService( this, rServiceName ); +} + +css::uno::Sequence<OUString> GenericPopupToolbarController::getSupportedServiceNames() +{ + return {"com.sun.star.frame.ToolbarController"}; +} + +void GenericPopupToolbarController::initialize( const css::uno::Sequence< css::uno::Any >& rxArgs ) +{ + PopupMenuToolbarController::initialize( rxArgs ); + if ( m_bReplaceWithLast ) + // Create early, so we can use the menu is statusChanged method. + createPopupMenuController(); +} + +void GenericPopupToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + SolarMutexGuard aGuard; + + if ( m_bReplaceWithLast && !rEvent.IsEnabled && m_xPopupMenu.is() ) + { + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( getToolboxId( nId, &pToolBox ) && pToolBox->IsItemEnabled( nId ) ) + { + Menu* pVclMenu = comphelper::getFromUnoTunnel<VCLXMenu>( m_xPopupMenu )->GetMenu(); + pVclMenu->Activate(); + pVclMenu->Deactivate(); + } + + for (sal_uInt16 i = 0, nCount = m_xPopupMenu->getItemCount(); i < nCount; ++i ) + { + sal_uInt16 nItemId = m_xPopupMenu->getItemId(i); + if (nItemId && m_xPopupMenu->isItemEnabled(nItemId) && !m_xPopupMenu->getPopupMenu(nItemId).is()) + { + functionExecuted(m_xPopupMenu->getCommand(nItemId)); + return; + } + } + } + + PopupMenuToolbarController::statusChanged( rEvent ); +} + +void GenericPopupToolbarController::functionExecuted( const OUString& rCommand ) +{ + if ( !m_bReplaceWithLast ) + return; + + removeStatusListener( m_aCommandURL ); + + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCommand, m_sModuleName); + OUString aRealCommand( vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties) ); + m_aCommandURL = aRealCommand.isEmpty() ? rCommand : aRealCommand; + addStatusListener( m_aCommandURL ); + + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( getToolboxId( nId, &pToolBox ) ) + { + pToolBox->SetItemCommand( nId, rCommand ); + pToolBox->SetHelpText( nId, OUString() ); // Will retrieve the new one from help. + pToolBox->SetItemText(nId, vcl::CommandInfoProvider::GetLabelForCommand(aProperties)); + pToolBox->SetQuickHelpText(nId, vcl::CommandInfoProvider::GetTooltipForCommand(rCommand, aProperties, m_xFrame)); + + Image aImage = vcl::CommandInfoProvider::GetImageForCommand(rCommand, m_xFrame, pToolBox->GetImageSize()); + if ( !!aImage ) + pToolBox->SetItemImage( nId, aImage ); + } +} + +ToolBoxItemBits GenericPopupToolbarController::getDropDownStyle() const +{ + return m_bSplitButton ? ToolBoxItemBits::DROPDOWN : ToolBoxItemBits::DROPDOWNONLY; +} + +class SaveToolbarController : public cppu::ImplInheritanceHelper< PopupMenuToolbarController, + css::frame::XSubToolbarController, + css::util::XModifyListener > +{ +public: + explicit SaveToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XSubToolbarController + // Make ToolBarManager ask our controller for updated image, in case of icon theme change. + virtual sal_Bool SAL_CALL opensSubToolbar() override; + virtual OUString SAL_CALL getSubToolbarName() override; + virtual void SAL_CALL functionSelected( const OUString& aCommand ) override; + virtual void SAL_CALL updateImage() override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override; + + // XModifyListener + virtual void SAL_CALL modified( const css::lang::EventObject& rEvent ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& rEvent ) override; + + // XComponent + virtual void SAL_CALL dispose() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( OUString const & rServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +private: + bool m_bReadOnly; + bool m_bModified; + css::uno::Reference< css::frame::XStorable > m_xStorable; + css::uno::Reference< css::util::XModifiable > m_xModifiable; +}; + +SaveToolbarController::SaveToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext ) + : ImplInheritanceHelper( rxContext, ".uno:SaveAsMenu" ) + , m_bReadOnly( false ) + , m_bModified( false ) +{ +} + +void SaveToolbarController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) +{ + PopupMenuToolbarController::initialize( aArguments ); + + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( !getToolboxId( nId, &pToolBox ) ) + return; + + css::uno::Reference< css::frame::XController > xController = m_xFrame->getController(); + if ( xController.is() ) + m_xModifiable.set( xController->getModel(), css::uno::UNO_QUERY ); + + if ( m_xModifiable.is() && pToolBox->GetItemCommand( nId ) == m_aCommandURL ) + // Will also enable the save as only mode. + m_xStorable.set( m_xModifiable, css::uno::UNO_QUERY ); + else if ( !m_xModifiable.is() ) + // Can be in table/query design. + m_xModifiable.set( xController, css::uno::UNO_QUERY ); + else + // Simple save button, without the dropdown. + pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) & ~ ToolBoxItemBits::DROPDOWN ); + + if ( m_xModifiable.is() ) + { + m_xModifiable->addModifyListener( this ); + modified( css::lang::EventObject() ); + } +} + +sal_Bool SaveToolbarController::opensSubToolbar() +{ + return true; +} + +OUString SaveToolbarController::getSubToolbarName() +{ + return OUString(); +} + +void SaveToolbarController::functionSelected( const OUString& /*aCommand*/ ) +{ +} + +void SaveToolbarController::updateImage() +{ + SolarMutexGuard aGuard; + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( !getToolboxId( nId, &pToolBox ) ) + return; + + vcl::ImageType eImageType = pToolBox->GetImageSize(); + + Image aImage; + + if ( m_bReadOnly ) + { + aImage = vcl::CommandInfoProvider::GetImageForCommand(".uno:SaveAs", m_xFrame, eImageType); + } + else if ( m_bModified ) + { + if (eImageType == vcl::ImageType::Size26) + aImage = Image(StockImage::Yes, BMP_SAVEMODIFIED_LARGE); + else if (eImageType == vcl::ImageType::Size32) + aImage = Image(StockImage::Yes, BMP_SAVEMODIFIED_EXTRALARGE); + else + aImage = Image(StockImage::Yes, BMP_SAVEMODIFIED_SMALL); + } + + if ( !aImage ) + aImage = vcl::CommandInfoProvider::GetImageForCommand(m_aCommandURL, m_xFrame, eImageType); + + if ( !!aImage ) + pToolBox->SetItemImage( nId, aImage ); +} + +void SaveToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( !getToolboxId( nId, &pToolBox ) ) + return; + + bool bLastReadOnly = m_bReadOnly; + m_bReadOnly = m_xStorable.is() && m_xStorable->isReadonly(); + if ( bLastReadOnly != m_bReadOnly ) + { + OUString sCommand = m_bReadOnly ? OUString( ".uno:SaveAs" ) : m_aCommandURL; + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(sCommand, + vcl::CommandInfoProvider::GetModuleIdentifier(m_xFrame)); + pToolBox->SetQuickHelpText( nId, + vcl::CommandInfoProvider::GetTooltipForCommand(sCommand, aProperties, m_xFrame) ); + pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) & ~( m_bReadOnly ? ToolBoxItemBits::DROPDOWN : ToolBoxItemBits::DROPDOWNONLY ) ); + pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ( m_bReadOnly ? ToolBoxItemBits::DROPDOWNONLY : ToolBoxItemBits::DROPDOWN ) ); + updateImage(); + } + + if ( !m_bReadOnly ) + pToolBox->EnableItem( nId, rEvent.IsEnabled ); +} + +void SaveToolbarController::modified( const css::lang::EventObject& /*rEvent*/ ) +{ + bool bLastModified = m_bModified; + m_bModified = m_xModifiable->isModified(); + if ( bLastModified != m_bModified ) + updateImage(); +} + +void SaveToolbarController::disposing( const css::lang::EventObject& rEvent ) +{ + if ( rEvent.Source == m_xModifiable ) + { + m_xModifiable.clear(); + m_xStorable.clear(); + } + else + PopupMenuToolbarController::disposing( rEvent ); +} + +void SaveToolbarController::dispose() +{ + PopupMenuToolbarController::dispose(); + if ( m_xModifiable.is() ) + { + m_xModifiable->removeModifyListener( this ); + m_xModifiable.clear(); + } + m_xStorable.clear(); +} + +OUString SaveToolbarController::getImplementationName() +{ + return "com.sun.star.comp.framework.SaveToolbarController"; +} + +sal_Bool SaveToolbarController::supportsService( OUString const & rServiceName ) +{ + return cppu::supportsService( this, rServiceName ); +} + +css::uno::Sequence< OUString > SaveToolbarController::getSupportedServiceNames() +{ + return {"com.sun.star.frame.ToolbarController"}; +} + +class NewToolbarController : public cppu::ImplInheritanceHelper<PopupMenuToolbarController, css::frame::XSubToolbarController> +{ +public: + explicit NewToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const & rServiceName) override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XSubToolbarController + // Make ToolBarManager ask our controller for updated image, in case of icon theme change. + sal_Bool SAL_CALL opensSubToolbar() override { return true; } + OUString SAL_CALL getSubToolbarName() override { return OUString(); } + void SAL_CALL functionSelected( const OUString& ) override {} + void SAL_CALL updateImage() override; + +private: + void functionExecuted( const OUString &rCommand ) override; + void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override; + void SAL_CALL execute( sal_Int16 KeyModifier ) override; + sal_uInt16 getMenuIdForCommand( std::u16string_view rCommand ); + + sal_uInt16 m_nMenuId; +}; + +NewToolbarController::NewToolbarController( + const css::uno::Reference< css::uno::XComponentContext >& xContext ) + : ImplInheritanceHelper( xContext ) + , m_nMenuId( 0 ) +{ +} + +OUString NewToolbarController::getImplementationName() +{ + return "org.apache.openoffice.comp.framework.NewToolbarController"; +} + +sal_Bool NewToolbarController::supportsService(OUString const & rServiceName) +{ + return cppu::supportsService( this, rServiceName ); +} + +css::uno::Sequence<OUString> NewToolbarController::getSupportedServiceNames() +{ + return {"com.sun.star.frame.ToolbarController"}; +} + +void SAL_CALL NewToolbarController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) +{ + PopupMenuToolbarController::initialize( aArguments ); + + osl::MutexGuard aGuard( m_aMutex ); + createPopupMenuController(); +} + +void SAL_CALL NewToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + if ( rEvent.IsEnabled ) + { + OUString aState; + rEvent.State >>= aState; + try + { + // set the image even if the state is not a string + // the toolbar item command will be used as a fallback + functionExecuted( aState ); + } + catch (const css::ucb::CommandFailedException&) + { + } + catch (const css::ucb::ContentCreationException&) + { + } + } + + enable( rEvent.IsEnabled ); +} + +void SAL_CALL NewToolbarController::execute( sal_Int16 /*KeyModifier*/ ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + OUString aURL, aTarget; + if ( m_xPopupMenu.is() && m_nMenuId ) + { + SolarMutexGuard aSolarMutexGuard; + aURL = m_xPopupMenu->getCommand(m_nMenuId); + + // TODO investigate how to wrap Get/SetUserValue in css::awt::XMenu + VCLXMenu* pMenu = comphelper::getFromUnoTunnel<VCLXMenu>(m_xPopupMenu); + MenuAttributes* pMenuAttributes(static_cast<MenuAttributes*>(pMenu->getUserValue(m_nMenuId))); + if ( pMenuAttributes ) + aTarget = pMenuAttributes->aTargetFrame; + else + aTarget = "_default"; + } + else + aURL = m_aCommandURL; + + css::uno::Sequence< css::beans::PropertyValue > aArgs{ comphelper::makePropertyValue( + "Referer", OUString( "private:user" )) }; + + dispatchCommand( aURL, aArgs, aTarget ); +} + +void NewToolbarController::functionExecuted( const OUString &rCommand ) +{ + m_nMenuId = getMenuIdForCommand( rCommand ); + updateImage(); +} + +sal_uInt16 NewToolbarController::getMenuIdForCommand( std::u16string_view rCommand ) +{ + if ( m_xPopupMenu.is() && !rCommand.empty() ) + { + sal_uInt16 nCount = m_xPopupMenu->getItemCount(); + for ( sal_uInt16 n = 0; n < nCount; ++n ) + { + sal_uInt16 nId = m_xPopupMenu->getItemId(n); + OUString aCmd(m_xPopupMenu->getCommand(nId)); + + // match even if the menu command is more detailed + // (maybe an additional query) #i28667# + if ( aCmd.match( rCommand ) ) + return nId; + } + } + + return 0; +} + +void SAL_CALL NewToolbarController::updateImage() +{ + SolarMutexGuard aSolarLock; + VclPtr< ToolBox> pToolBox = static_cast< ToolBox* >( VCLUnoHelper::GetWindow( getParent() ) ); + if ( !pToolBox ) + return; + + OUString aURL, aImageId; + if ( m_xPopupMenu.is() && m_nMenuId ) + { + aURL = m_xPopupMenu->getCommand(m_nMenuId); + VCLXMenu* pMenu = comphelper::getFromUnoTunnel<VCLXMenu>(m_xPopupMenu); + MenuAttributes* pMenuAttributes(static_cast<MenuAttributes*>(pMenu->getUserValue(m_nMenuId))); + if ( pMenuAttributes ) + aImageId = pMenuAttributes->aImageId; + } + else + aURL = m_aCommandURL; + + INetURLObject aURLObj( aImageId.isEmpty() ? aURL : aImageId ); + vcl::ImageType eImageType( pToolBox->GetImageSize() ); + Image aImage = SvFileInformationManager::GetImageNoDefault( aURLObj, eImageType ); + if ( !aImage ) + aImage = vcl::CommandInfoProvider::GetImageForCommand( aURL, m_xFrame, eImageType ); + + if ( !aImage ) + return; + + pToolBox->SetItemImage( m_nToolBoxId, aImage ); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_GenericPopupToolbarController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &args) +{ + return cppu::acquire(new GenericPopupToolbarController(context, args)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_SaveToolbarController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SaveToolbarController(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +org_apache_openoffice_comp_framework_NewToolbarController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new NewToolbarController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/progressbarwrapper.cxx b/framework/source/uielement/progressbarwrapper.cxx new file mode 100644 index 000000000..ad147111f --- /dev/null +++ b/framework/source/uielement/progressbarwrapper.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 <uielement/progressbarwrapper.hxx> + +#include <uielement/statusindicatorinterfacewrapper.hxx> + +#include <com/sun/star/ui/UIElementType.hpp> +#include <com/sun/star/lang/DisposedException.hpp> + +#include <vcl/status.hxx> +#include <vcl/svapp.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +using namespace ::com::sun::star; + +namespace framework{ + +ProgressBarWrapper::ProgressBarWrapper() : +UIElementWrapperBase( css::ui::UIElementType::PROGRESSBAR ) + , m_bOwnsInstance( false ) + , m_nRange( 100 ) + , m_nValue( 0 ) +{ +} + +ProgressBarWrapper::~ProgressBarWrapper() +{ +} + +// public interfaces +void ProgressBarWrapper::setStatusBar( const uno::Reference< awt::XWindow >& rStatusBar, bool bOwnsInstance ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + if ( m_bOwnsInstance ) + { + // dispose XWindow reference of our status bar + try + { + if ( m_xStatusBar.is() ) + m_xStatusBar->dispose(); + } + catch ( const uno::Exception& ) + { + } + m_xStatusBar.clear(); + } + + m_bOwnsInstance = bOwnsInstance; + m_xStatusBar = rStatusBar; +} + +uno::Reference< awt::XWindow > ProgressBarWrapper::getStatusBar() const +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return uno::Reference< awt::XWindow >(); + + return m_xStatusBar; +} + +// wrapped methods of css::task::XStatusIndicator +void ProgressBarWrapper::start( const OUString& Text, ::sal_Int32 Range ) +{ + uno::Reference< awt::XWindow > xWindow; + sal_Int32 nValue( 0 ); + + { + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + xWindow = m_xStatusBar; + m_nValue = 0; + m_nRange = Range; + nValue = m_nValue; + } + + if ( !xWindow.is() ) + return; + + SolarMutexGuard aSolarMutexGuard; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( !(pWindow && pWindow->GetType() == WindowType::STATUSBAR) ) + return; + + StatusBar* pStatusBar = static_cast<StatusBar *>(pWindow.get()); + if ( !pStatusBar->IsProgressMode() ) + pStatusBar->StartProgressMode( Text ); + else + { + pStatusBar->SetUpdateMode( false ); + pStatusBar->EndProgressMode(); + pStatusBar->StartProgressMode( Text ); + pStatusBar->SetProgressValue( sal_uInt16( nValue )); + pStatusBar->SetUpdateMode( true ); + } + pStatusBar->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); +} + +void ProgressBarWrapper::end() +{ + uno::Reference< awt::XWindow > xWindow; + + { + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + xWindow = m_xStatusBar; + m_nRange = 100; + m_nValue = 0; + } + + if ( xWindow.is() ) + { + SolarMutexGuard aSolarMutexGuard; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->GetType() == WindowType::STATUSBAR ) + { + StatusBar* pStatusBar = static_cast<StatusBar *>(pWindow.get()); + if ( pStatusBar->IsProgressMode() ) + pStatusBar->EndProgressMode(); + } + } +} + +void ProgressBarWrapper::setText( const OUString& Text ) +{ + uno::Reference< awt::XWindow > xWindow; + sal_Int32 nValue( 0 ); + + { + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + xWindow = m_xStatusBar; + m_aText = Text; + nValue = m_nValue; + } + + if ( !xWindow.is() ) + return; + + SolarMutexGuard aSolarMutexGuard; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( !(pWindow && pWindow->GetType() == WindowType::STATUSBAR) ) + return; + + StatusBar* pStatusBar = static_cast<StatusBar *>(pWindow.get()); + if( pStatusBar->IsProgressMode() ) + { + pStatusBar->SetUpdateMode( false ); + pStatusBar->EndProgressMode(); + pStatusBar->StartProgressMode( Text ); + pStatusBar->SetProgressValue( sal_uInt16( nValue )); + pStatusBar->SetUpdateMode( true ); + } + else + pStatusBar->SetText( Text ); +} + +void ProgressBarWrapper::setValue( ::sal_Int32 nValue ) +{ + uno::Reference< awt::XWindow > xWindow; + OUString aText; + bool bSetValue( false ); + + { + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + xWindow = m_xStatusBar; + + double fVal( 0 ); + if ( m_nRange > 0 ) + { + fVal = ( double( nValue ) / double( m_nRange )) * 100; + fVal = std::clamp( fVal, 0.0, 100.0 ); + } + + if ( m_nValue != sal_Int32( fVal )) + { + m_nValue = sal_Int32( fVal ); + bSetValue = true; + } + + nValue = m_nValue; + aText = m_aText; + } + + if ( xWindow.is() && bSetValue ) + { + SolarMutexGuard aSolarMutexGuard; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->GetType() == WindowType::STATUSBAR ) + { + StatusBar* pStatusBar = static_cast<StatusBar *>(pWindow.get()); + if ( !pStatusBar->IsProgressMode() ) + pStatusBar->StartProgressMode( aText ); + pStatusBar->SetProgressValue( sal_uInt16( nValue )); + } + } +} + +void ProgressBarWrapper::reset() +{ + setText( OUString() ); + setValue( 0 ); +} + +// XInitialization +void SAL_CALL ProgressBarWrapper::initialize( const uno::Sequence< uno::Any >& ) +{ + // dummy - do nothing +} + +// XUpdatable +void SAL_CALL ProgressBarWrapper::update() +{ + // dummy - do nothing +} + +// XComponent +void SAL_CALL ProgressBarWrapper::dispose() +{ + uno::Reference< lang::XComponent > xThis(this); + + { + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + } + + { + lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + SolarMutexGuard g; + if ( m_bOwnsInstance ) + { + try + { + if ( m_xStatusBar.is() ) + m_xStatusBar->dispose(); + } + catch ( const lang::DisposedException& ) + { + } + } + + m_xStatusBar.clear(); + m_bDisposed = true; + } +} + +// XUIElement +uno::Reference< uno::XInterface > SAL_CALL ProgressBarWrapper::getRealInterface() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return uno::Reference< uno::XInterface >(); + else + { + uno::Reference< uno::XInterface > xComp( m_xProgressBarIfacWrapper ); + if ( !xComp.is() ) + { + rtl::Reference<StatusIndicatorInterfaceWrapper> pWrapper = + new StatusIndicatorInterfaceWrapper( uno::Reference< lang::XComponent >(this) ); + xComp.set(static_cast< cppu::OWeakObject* >( pWrapper.get() ), + uno::UNO_QUERY ); + m_xProgressBarIfacWrapper = xComp; + } + + return xComp; + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/recentfilesmenucontroller.cxx b/framework/source/uielement/recentfilesmenucontroller.cxx new file mode 100644 index 000000000..dc4001a1f --- /dev/null +++ b/framework/source/uielement/recentfilesmenucontroller.cxx @@ -0,0 +1,411 @@ +/* -*- 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 <strings.hrc> +#include <classes/fwkresid.hxx> + +#include <comphelper/propertyvalue.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <osl/mutex.hxx> +#include <svtools/imagemgr.hxx> +#include <svtools/popupmenucontrollerbase.hxx> +#include <tools/urlobj.hxx> +#include <unotools/historyoptions.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <vcl/graph.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <o3tl/string_view.hxx> + +using namespace css; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; + +#define MAX_MENU_ITEMS 99 + +namespace { + +constexpr OUStringLiteral CMD_CLEAR_LIST = u".uno:ClearRecentFileList"; +constexpr OUStringLiteral CMD_OPEN_AS_TEMPLATE = u".uno:OpenTemplate"; +constexpr OUStringLiteral CMD_OPEN_REMOTE = u".uno:OpenRemote"; + +class RecentFilesMenuController : public svt::PopupMenuControllerBase +{ + using svt::PopupMenuControllerBase::disposing; + +public: + RecentFilesMenuController( const uno::Reference< uno::XComponentContext >& xContext, + const uno::Sequence< uno::Any >& args ); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.RecentFilesMenuController"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.frame.PopupMenuController"}; + } + + // XStatusListener + virtual void SAL_CALL statusChanged( const frame::FeatureStateEvent& Event ) override; + + // XMenuListener + virtual void SAL_CALL itemSelected( const awt::MenuEvent& rEvent ) override; + virtual void SAL_CALL itemActivated( const awt::MenuEvent& rEvent ) override; + + // XDispatchProvider + virtual uno::Reference< frame::XDispatch > SAL_CALL queryDispatch( const util::URL& aURL, const OUString& sTarget, sal_Int32 nFlags ) override; + + // XDispatch + virtual void SAL_CALL dispatch( const util::URL& aURL, const uno::Sequence< beans::PropertyValue >& seqProperties ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + +private: + virtual void impl_setPopupMenu() override; + void fillPopupMenu( css::uno::Reference< css::awt::XPopupMenu > const & rPopupMenu ); + void executeEntry( sal_Int32 nIndex ); + + std::vector<std::pair<OUString, bool>> m_aRecentFilesItems; + bool m_bDisabled : 1; + bool m_bShowToolbarEntries; +}; + +RecentFilesMenuController::RecentFilesMenuController( const uno::Reference< uno::XComponentContext >& xContext, + const uno::Sequence< uno::Any >& args ) : + svt::PopupMenuControllerBase( xContext ), + m_bDisabled( false ), + m_bShowToolbarEntries( false ) +{ + css::beans::PropertyValue aPropValue; + for ( uno::Any const & arg : args ) + { + arg >>= aPropValue; + if ( aPropValue.Name == "InToolbar" ) + { + aPropValue.Value >>= m_bShowToolbarEntries; + break; + } + } +} + +void InsertItem(const css::uno::Reference<css::awt::XPopupMenu>& rPopupMenu, + const OUString& rCommand, + const css::uno::Reference<css::frame::XFrame>& rFrame) +{ + sal_uInt16 nItemId = rPopupMenu->getItemCount() + 1; + + if (rFrame.is()) + { + OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame)); + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCommand, aModuleName); + OUString aLabel(vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties)); + OUString aTooltip(vcl::CommandInfoProvider::GetTooltipForCommand(rCommand, aProperties, rFrame)); + css::uno::Reference<css::graphic::XGraphic> xGraphic(vcl::CommandInfoProvider::GetXGraphicForCommand(rCommand, rFrame)); + + rPopupMenu->insertItem(nItemId, aLabel, 0, -1); + rPopupMenu->setItemImage(nItemId, xGraphic, false); + rPopupMenu->setHelpText(nItemId, aTooltip); + } + else + rPopupMenu->insertItem(nItemId, OUString(), 0, -1); + + rPopupMenu->setCommand(nItemId, rCommand); +} + + +// private function +void RecentFilesMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + SolarMutexGuard aSolarMutexGuard; + + resetPopupMenu( rPopupMenu ); + + std::vector< SvtHistoryOptions::HistoryItem > aHistoryList = SvtHistoryOptions::GetList( EHistoryType::PickList ); + + int nPickListMenuItems = std::min<sal_Int32>( aHistoryList.size(), MAX_MENU_ITEMS ); + m_aRecentFilesItems.clear(); + + if (( nPickListMenuItems > 0 ) && !m_bDisabled ) + { + for ( int i = 0; i < nPickListMenuItems; i++ ) + { + const SvtHistoryOptions::HistoryItem& rPickListEntry = aHistoryList[i]; + m_aRecentFilesItems.emplace_back(rPickListEntry.sURL, rPickListEntry.isReadOnly); + } + } + + if ( !m_aRecentFilesItems.empty() ) + { + const sal_uInt32 nCount = m_aRecentFilesItems.size(); + StyleSettings aIconSettings; + bool bIsIconsAllowed = aIconSettings.GetUseImagesInMenus(); + + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + + OUStringBuffer aMenuShortCut; + if ( i <= 9 ) + { + if ( i == 9 ) + aMenuShortCut.append( "1~0. " ); + else + { + aMenuShortCut.append( "~N. " ); + aMenuShortCut[ 1 ] = sal_Unicode( i + '1' ); + } + } + else + { + aMenuShortCut.append( sal_Int32( i + 1 ) ); + aMenuShortCut.append( ". " ); + } + + OUString aURLString = "vnd.sun.star.popup:RecentFileList?entry=" + OUString::number(i); + + // Abbreviate URL + OUString aMenuTitle; + INetURLObject const aURL(m_aRecentFilesItems[i].first); + OUString aTipHelpText( aURL.getFSysPath( FSysStyle::Detect ) ); + + if ( aURL.GetProtocol() == INetProtocol::File ) + { + // Do handle file URL differently: don't show the protocol, just the file name + aMenuTitle = aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset); + } + else + { + // In all other URLs show the protocol name before the file name + aMenuTitle = INetURLObject::GetSchemeName(aURL.GetProtocol()) + ": " + aURL.getName(); + } + + aMenuShortCut.append( aMenuTitle ); + + rPopupMenu->insertItem(sal_uInt16( i+1 ), aMenuShortCut.makeStringAndClear(), 0, -1); + + if ( bIsIconsAllowed ) { + // tdf#146219: don't use SvFileInformationManager::GetImageId, + // which needs to access the URL to detect if it's a directory + BitmapEx aThumbnail(SvFileInformationManager::GetFileImageId(aURL)); + rPopupMenu->setItemImage(sal_uInt16(i + 1), Graphic(aThumbnail).GetXGraphic(), false); + } + + rPopupMenu->setTipHelpText(sal_uInt16(i + 1), aTipHelpText); + rPopupMenu->setCommand(sal_uInt16(i + 1), aURLString); + } + + rPopupMenu->insertSeparator(-1); + // Clear List menu entry + rPopupMenu->insertItem(sal_uInt16(nCount + 1), FwkResId(STR_CLEAR_RECENT_FILES), 0, -1); + rPopupMenu->setCommand(sal_uInt16(nCount + 1), CMD_CLEAR_LIST); + rPopupMenu->setHelpText(sal_uInt16(nCount + 1), FwkResId(STR_CLEAR_RECENT_FILES_HELP)); + + // Open remote menu entry + if ( m_bShowToolbarEntries ) + { + rPopupMenu->insertSeparator(-1); + InsertItem(rPopupMenu, CMD_OPEN_AS_TEMPLATE, m_xFrame); + InsertItem(rPopupMenu, CMD_OPEN_REMOTE, m_xFrame); + } + } + else + { + if ( m_bShowToolbarEntries ) + { + InsertItem(rPopupMenu, CMD_OPEN_AS_TEMPLATE, m_xFrame); + InsertItem(rPopupMenu, CMD_OPEN_REMOTE, m_xFrame); + } + else + { + // Add InsertSeparator(), otherwise it will display + // the first item icon of recent files instead of displaying no icon. + rPopupMenu->insertSeparator(-1); + // No recent documents => insert "no documents" string + // Do not disable it, otherwise the Toolbar controller and MenuButton + // will display SV_RESID_STRING_NOSELECTIONPOSSIBLE instead of STR_NODOCUMENT + rPopupMenu->insertItem(1, FwkResId(STR_NODOCUMENT), static_cast<sal_Int16>(MenuItemBits::NOSELECT), -1); + } + } +} + +void RecentFilesMenuController::executeEntry( sal_Int32 nIndex ) +{ + if (( nIndex < 0 ) || + ( nIndex >= sal::static_int_cast<sal_Int32>( m_aRecentFilesItems.size() ))) + return; + + Sequence< PropertyValue > aArgsList{ + comphelper::makePropertyValue("Referer", OUString( "private:user" )), + + // documents in the picklist will never be opened as templates + comphelper::makePropertyValue("AsTemplate", false), + + // Type detection needs to know which app we are opening it from. + comphelper::makePropertyValue("DocumentService", m_aModuleName) + }; + if (m_aRecentFilesItems[nIndex].second) // tdf#149170 only add if true + { + aArgsList.realloc(aArgsList.size()+1); + aArgsList.getArray()[aArgsList.size()-1] = comphelper::makePropertyValue("ReadOnly", true); + } + dispatchCommand(m_aRecentFilesItems[nIndex].first, aArgsList, "_default"); +} + +// XEventListener +void SAL_CALL RecentFilesMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + osl::MutexGuard aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL RecentFilesMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + osl::MutexGuard aLock( m_aMutex ); + m_bDisabled = !Event.IsEnabled; +} + +void SAL_CALL RecentFilesMenuController::itemSelected( const css::awt::MenuEvent& rEvent ) +{ + Reference< css::awt::XPopupMenu > xPopupMenu; + + { + osl::MutexGuard aLock(m_aMutex); + xPopupMenu = m_xPopupMenu; + } + + if ( !xPopupMenu.is() ) + return; + + const OUString aCommand( xPopupMenu->getCommand( rEvent.MenuId ) ); + + if ( aCommand == CMD_CLEAR_LIST ) + { + SvtHistoryOptions::Clear( EHistoryType::PickList ); + dispatchCommand( + "vnd.org.libreoffice.recentdocs:ClearRecentFileList", + css::uno::Sequence< css::beans::PropertyValue >() ); + } + else if ( aCommand == CMD_OPEN_REMOTE ) + { + Sequence< PropertyValue > aArgsList( 0 ); + dispatchCommand( CMD_OPEN_REMOTE, aArgsList ); + } + else if ( aCommand == CMD_OPEN_AS_TEMPLATE ) + { + Sequence< PropertyValue > aArgsList( 0 ); + dispatchCommand( CMD_OPEN_AS_TEMPLATE, aArgsList ); + } + else + executeEntry( rEvent.MenuId-1 ); +} + +void SAL_CALL RecentFilesMenuController::itemActivated( const css::awt::MenuEvent& ) +{ + osl::MutexGuard aLock( m_aMutex ); + impl_setPopupMenu(); +} + +// XPopupMenuController +void RecentFilesMenuController::impl_setPopupMenu() +{ + if ( m_xPopupMenu.is() ) + fillPopupMenu( m_xPopupMenu ); +} + +// XDispatchProvider +Reference< XDispatch > SAL_CALL RecentFilesMenuController::queryDispatch( + const URL& aURL, + const OUString& /*sTarget*/, + sal_Int32 /*nFlags*/ ) +{ + osl::MutexGuard aLock( m_aMutex ); + + throwIfDisposed(); + + if ( aURL.Complete.startsWith( m_aBaseURL ) ) + return Reference< XDispatch >( this ); + else + return Reference< XDispatch >(); +} + +// XDispatch +void SAL_CALL RecentFilesMenuController::dispatch( + const URL& aURL, + const Sequence< PropertyValue >& /*seqProperties*/ ) +{ + osl::MutexGuard aLock( m_aMutex ); + + throwIfDisposed(); + + if ( !aURL.Complete.startsWith( m_aBaseURL ) ) + return; + + // Parse URL to retrieve entry argument and its value + sal_Int32 nQueryPart = aURL.Complete.indexOf( '?', m_aBaseURL.getLength() ); + if ( nQueryPart <= 0 ) + return; + + static const OUStringLiteral aEntryArgStr( u"entry=" ); + sal_Int32 nEntryArg = aURL.Complete.indexOf( aEntryArgStr, nQueryPart ); + sal_Int32 nEntryPos = nEntryArg + aEntryArgStr.getLength(); + if (( nEntryArg <= 0 ) || ( nEntryPos >= aURL.Complete.getLength() )) + return; + + sal_Int32 nAddArgs = aURL.Complete.indexOf( '&', nEntryPos ); + std::u16string_view aEntryArg; + + if ( nAddArgs < 0 ) + aEntryArg = aURL.Complete.subView( nEntryPos ); + else + aEntryArg = aURL.Complete.subView( nEntryPos, nAddArgs-nEntryPos ); + + sal_Int32 nEntry = o3tl::toInt32(aEntryArg); + executeEntry( nEntry ); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_RecentFilesMenuController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &args) +{ + return cppu::acquire(new RecentFilesMenuController(context, args)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/resourcemenucontroller.cxx b/framework/source/uielement/resourcemenucontroller.cxx new file mode 100644 index 000000000..7a1364b41 --- /dev/null +++ b/framework/source/uielement/resourcemenucontroller.cxx @@ -0,0 +1,584 @@ +/* -*- 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 <uielement/menubarmanager.hxx> + +#include <cppuhelper/implbase.hxx> +#include <svtools/popupmenucontrollerbase.hxx> +#include <toolkit/awt/vclxmenu.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <sal/log.hxx> + +#include <com/sun/star/embed/VerbAttributes.hpp> +#include <com/sun/star/embed/VerbDescriptor.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/util/URL.hpp> + +namespace { + +class ResourceMenuController : public cppu::ImplInheritanceHelper< svt::PopupMenuControllerBase, css::ui::XUIConfigurationListener > +{ +public: + ResourceMenuController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::uno::Any >& rxArgs, bool bToolbarContainer ); + + // XPopupMenuController + virtual void SAL_CALL updatePopupMenu() override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& rEvent ) override; + + // XUIConfigurationListener + virtual void SAL_CALL elementInserted( const css::ui::ConfigurationEvent& rEvent ) override; + virtual void SAL_CALL elementRemoved( const css::ui::ConfigurationEvent& rEvent ) override; + virtual void SAL_CALL elementReplaced( const css::ui::ConfigurationEvent& rEvent ) override; + + // XMenuListener + virtual void SAL_CALL itemActivated( const css::awt::MenuEvent& rEvent ) override; + virtual void SAL_CALL itemSelected( const css::awt::MenuEvent& rEvent ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +private: + OUString m_aMenuURL; + bool m_bContextMenu; + bool m_bInToolbar; + bool m_bToolbarContainer; + sal_uInt16 m_nNewMenuId; + rtl::Reference< framework::MenuBarManager > m_xMenuBarManager; + css::uno::Reference< css::frame::XDispatchProvider > m_xDispatchProvider; + css::uno::Reference< css::container::XIndexAccess > m_xMenuContainer; + css::uno::Reference< css::ui::XUIConfigurationManager > m_xConfigManager, m_xModuleConfigManager; + void addVerbs( const css::uno::Sequence< css::embed::VerbDescriptor >& rVerbs ); + virtual void SAL_CALL disposing() override; + +protected: + css::uno::Reference< css::uno::XComponentContext > m_xContext; +}; + +ResourceMenuController::ResourceMenuController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::uno::Any >& rxArgs, bool bToolbarContainer ) : + ImplInheritanceHelper( rxContext ), + m_bContextMenu( false ), + m_bInToolbar( false ), + m_bToolbarContainer( bToolbarContainer ), + m_nNewMenuId( 1 ), + m_xContext( rxContext ) +{ + for ( const auto& arg: rxArgs ) + { + css::beans::PropertyValue aPropValue; + if ( arg >>= aPropValue ) + { + if ( aPropValue.Name == "Value" ) + { + OUString aMenuName; + aPropValue.Value >>= aMenuName; + if ( aMenuName.isEmpty() ) + continue; + + if ( m_bToolbarContainer ) + m_aMenuURL = "private:resource/toolbar/" + aMenuName; + else + m_aMenuURL = "private:resource/popupmenu/" + aMenuName; + } + else if ( aPropValue.Name == "ResourceURL" ) + aPropValue.Value >>= m_aMenuURL; + else if ( aPropValue.Name == "Frame" ) + aPropValue.Value >>= m_xFrame; + else if ( aPropValue.Name == "ModuleIdentifier" ) + aPropValue.Value >>= m_aModuleName; + else if ( aPropValue.Name == "DispatchProvider" ) + aPropValue.Value >>= m_xDispatchProvider; + else if ( aPropValue.Name == "IsContextMenu" ) + aPropValue.Value >>= m_bContextMenu; + else if ( aPropValue.Name == "InToolbar" ) + aPropValue.Value >>= m_bInToolbar; + } + } + if ( m_xFrame.is() ) + // No need to initialize again through initialize method. + m_bInitialized = true; +} + +void ResourceMenuController::updatePopupMenu() +{ + if ( ( m_xMenuContainer.is() && !m_bContextMenu ) || m_aMenuURL.isEmpty() ) + return; + + if ( m_aModuleName.isEmpty() ) + { + try + { + css::uno::Reference< css::frame::XModuleManager > xModuleManager( css::frame::ModuleManager::create( m_xContext ) ); + m_aModuleName = xModuleManager->identify( m_xFrame ); + } + catch( const css::uno::Exception& ) + {} + } + + if ( !m_xConfigManager.is() ) + { + try + { + css::uno::Reference< css::frame::XController > xController( m_xFrame->getController() ); + css::uno::Reference< css::frame::XModel > xModel( xController->getModel() ); + css::uno::Reference< css::ui::XUIConfigurationManagerSupplier > xSupplier( xModel, css::uno::UNO_QUERY_THROW ); + m_xConfigManager.set( xSupplier->getUIConfigurationManager() ); + css::uno::Reference< css::ui::XUIConfiguration > xConfig( m_xConfigManager, css::uno::UNO_QUERY_THROW ); + xConfig->addConfigurationListener( this ); + } + catch( const css::uno::RuntimeException& ) + {} + } + + if ( !m_xModuleConfigManager.is() ) + { + try + { + css::uno::Reference< css::ui::XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier( + css::ui::theModuleUIConfigurationManagerSupplier::get( m_xContext ) ); + m_xModuleConfigManager.set( xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleName ) ); + css::uno::Reference< css::ui::XUIConfiguration > xConfig( m_xModuleConfigManager, css::uno::UNO_QUERY_THROW ); + xConfig->addConfigurationListener( this ); + } + catch ( const css::container::NoSuchElementException& ) + { + SAL_WARN( "fwk.uielement", "Invalid module identifier: " << m_aModuleName ); + } + catch( const css::uno::RuntimeException& ) + {} + } + + if ( !m_xMenuContainer.is() && m_xConfigManager.is() ) + { + try + { + m_xMenuContainer.set( m_xConfigManager->getSettings( m_aMenuURL, false ) ); + } + catch ( const css::container::NoSuchElementException& ) + { + // Not an error - element may exist only in the module. + } + catch ( const css::lang::IllegalArgumentException& ) + { + SAL_WARN( "fwk.uielement", "The given URL is not valid: " << m_aMenuURL ); + return; + } + } + + if ( !m_xMenuContainer.is() && m_xModuleConfigManager.is() ) + { + try + { + m_xMenuContainer.set( m_xModuleConfigManager->getSettings( m_aMenuURL, false ) ); + } + catch ( const css::container::NoSuchElementException& ) + { + SAL_WARN( "fwk.uielement", "Can not find settings for " << m_aMenuURL ); + return; + } + catch ( const css::lang::IllegalArgumentException& ) + { + SAL_WARN( "fwk.uielement", "The given URL is not valid: " << m_aMenuURL ); + return; + } + } + + if ( !m_xMenuContainer.is() ) + return; + + // Clear previous content. + if ( m_xMenuBarManager.is() ) + { + m_xMenuBarManager->dispose(); + m_xMenuBarManager.clear(); + } + resetPopupMenu( m_xPopupMenu ); + m_nNewMenuId = 1; + + // Now fill the menu with the configuration data. + framework::MenuBarManager::FillMenu( m_nNewMenuId, comphelper::getFromUnoTunnel<VCLXMenu>( m_xPopupMenu )->GetMenu(), m_aModuleName, m_xMenuContainer, m_xDispatchProvider ); + + // For context menus, add object verbs. + if ( !m_bContextMenu ) + return; + + css::util::URL aObjectMenuURL; + aObjectMenuURL.Complete = ".uno:ObjectMenue"; + m_xURLTransformer->parseStrict( aObjectMenuURL ); + css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider( m_xFrame, css::uno::UNO_QUERY ); + css::uno::Reference< css::frame::XDispatch > xDispatch( xDispatchProvider->queryDispatch( aObjectMenuURL, OUString(), 0 ) ); + if ( xDispatch.is() ) + { + xDispatch->addStatusListener( this, aObjectMenuURL ); + xDispatch->removeStatusListener( this, aObjectMenuURL ); + } +} + +void ResourceMenuController::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + css::uno::Sequence< css::embed::VerbDescriptor > aVerbs; + if ( rEvent.IsEnabled && ( rEvent.State >>= aVerbs ) ) + addVerbs( aVerbs ); +} + +void ResourceMenuController::addVerbs( const css::uno::Sequence< css::embed::VerbDescriptor >& rVerbs ) +{ + // Check if the document is read-only. + css::uno::Reference< css::frame::XController > xController( m_xFrame->getController() ); + css::uno::Reference< css::frame::XStorable > xStorable; + if ( xController.is() ) + xStorable.set( xController->getModel(), css::uno::UNO_QUERY ); + + bool bReadOnly = xStorable.is() && xStorable->isReadonly(); + VCLXMenu* pAwtMenu = comphelper::getFromUnoTunnel<VCLXMenu>( m_xPopupMenu ); + Menu* pVCLMenu = pAwtMenu->GetMenu(); + + for ( const auto& rVerb : rVerbs ) + { + if ( !( rVerb.VerbAttributes & css::embed::VerbAttributes::MS_VERBATTR_ONCONTAINERMENU ) || + ( bReadOnly && !( rVerb.VerbAttributes & css::embed::VerbAttributes::MS_VERBATTR_NEVERDIRTIES ) ) ) + continue; + + pVCLMenu->InsertItem( m_nNewMenuId, rVerb.VerbName ); + pVCLMenu->SetItemCommand( m_nNewMenuId, ".uno:ObjectMenue?VerbID:short=" + OUString::number( rVerb.VerbID ) ); + ++m_nNewMenuId; + } +} + +void ResourceMenuController::itemActivated( const css::awt::MenuEvent& /*rEvent*/ ) +{ + // Must initialize MenuBarManager here, because we want to let the app do context menu interception before. + if ( !m_xMenuBarManager.is() ) + { + VCLXMenu* pAwtMenu = comphelper::getFromUnoTunnel<VCLXMenu>( m_xPopupMenu ); + m_xMenuBarManager.set( new framework::MenuBarManager( + m_xContext, m_xFrame, m_xURLTransformer, m_xDispatchProvider, m_aModuleName, pAwtMenu->GetMenu(), false, !m_bContextMenu && !m_bInToolbar ) ); + m_xFrame->addFrameActionListener( m_xMenuBarManager ); + } +} + +void ResourceMenuController::itemSelected( const css::awt::MenuEvent& /*rEvent*/ ) +{ + // Must override this, because we are managed by MenuBarManager, so don't want the handler found in the base class. +} + +void ResourceMenuController::elementInserted( const css::ui::ConfigurationEvent& rEvent ) +{ + if ( rEvent.ResourceURL == m_aMenuURL ) + m_xMenuContainer.clear(); +} + +void ResourceMenuController::elementRemoved( const css::ui::ConfigurationEvent& rEvent ) +{ + elementInserted( rEvent ); +} + +void ResourceMenuController::elementReplaced( const css::ui::ConfigurationEvent& rEvent ) +{ + elementInserted( rEvent ); +} + +void ResourceMenuController::disposing( const css::lang::EventObject& rEvent ) +{ + if ( rEvent.Source == m_xConfigManager ) + m_xConfigManager.clear(); + else if ( rEvent.Source == m_xModuleConfigManager ) + m_xModuleConfigManager.clear(); + else + { + if ( m_xMenuBarManager.is() ) + { + if (m_xFrame.is()) + m_xFrame->removeFrameActionListener( m_xMenuBarManager ); + + m_xMenuBarManager->dispose(); + m_xMenuBarManager.clear(); + } + svt::PopupMenuControllerBase::disposing( rEvent ); + } +} + +void ResourceMenuController::disposing() +{ + css::uno::Reference< css::ui::XUIConfiguration > xConfig( m_xConfigManager, css::uno::UNO_QUERY ); + if ( xConfig.is() ) + xConfig->removeConfigurationListener( this ); + + css::uno::Reference< css::ui::XUIConfiguration > xModuleConfig( m_xModuleConfigManager, css::uno::UNO_QUERY ); + if ( xModuleConfig.is() ) + xModuleConfig->removeConfigurationListener( this ); + + m_xConfigManager.clear(); + m_xModuleConfigManager.clear(); + m_xMenuContainer.clear(); + m_xDispatchProvider.clear(); + if ( m_xMenuBarManager.is() ) + { + if (m_xFrame.is()) + m_xFrame->removeFrameActionListener( m_xMenuBarManager ); + + m_xMenuBarManager->dispose(); + m_xMenuBarManager.clear(); + } + + svt::PopupMenuControllerBase::disposing(); +} + +OUString ResourceMenuController::getImplementationName() +{ + if ( m_bToolbarContainer ) + return "com.sun.star.comp.framework.ToolbarAsMenuController"; + + return "com.sun.star.comp.framework.ResourceMenuController"; +} + +css::uno::Sequence< OUString > ResourceMenuController::getSupportedServiceNames() +{ + return { "com.sun.star.frame.PopupMenuController" }; +} + +class SaveAsMenuController : public ResourceMenuController +{ +public: + SaveAsMenuController( const css::uno::Reference< css::uno::XComponentContext >& rContext, + const css::uno::Sequence< css::uno::Any >& rArgs ); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + +private: + virtual void impl_setPopupMenu() override; +}; + +SaveAsMenuController::SaveAsMenuController( const css::uno::Reference< css::uno::XComponentContext >& rContext, + const css::uno::Sequence< css::uno::Any >& rArgs ) + : ResourceMenuController( rContext, rArgs, false ) +{ +} + +void InsertItem(const css::uno::Reference<css::awt::XPopupMenu>& rPopupMenu, + const OUString& rCommand) +{ + sal_uInt16 nItemId = rPopupMenu->getItemCount() + 1; + rPopupMenu->insertItem(nItemId, OUString(), 0, -1); + rPopupMenu->setCommand(nItemId, rCommand); +} + +void SaveAsMenuController::impl_setPopupMenu() +{ + SolarMutexGuard aGuard; + + InsertItem(m_xPopupMenu, ".uno:SaveAs"); + InsertItem(m_xPopupMenu, ".uno:ExportTo"); + InsertItem(m_xPopupMenu, ".uno:SaveACopy"); + InsertItem(m_xPopupMenu, ".uno:SaveAsTemplate"); + m_xPopupMenu->insertSeparator(-1); + InsertItem(m_xPopupMenu, ".uno:SaveAsRemote"); +} + +OUString SaveAsMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.SaveAsMenuController"; +} + +class WindowListMenuController : public ResourceMenuController +{ +public: + WindowListMenuController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::uno::Any >& rxArgs ) + : ResourceMenuController(rxContext, rxArgs, false) {} + + // XMenuListener + void SAL_CALL itemActivated( const css::awt::MenuEvent& rEvent ) override; + void SAL_CALL itemSelected( const css::awt::MenuEvent& rEvent ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + +private: + void impl_setPopupMenu() override; +}; + +constexpr sal_uInt16 START_ITEMID_WINDOWLIST = 4600; +constexpr sal_uInt16 END_ITEMID_WINDOWLIST = 4699; + +void WindowListMenuController::itemActivated( const css::awt::MenuEvent& rEvent ) +{ + ResourceMenuController::itemActivated( rEvent ); + + // update window list + ::std::vector< OUString > aNewWindowListVector; + + css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( m_xContext ); + + sal_uInt16 nActiveItemId = 0; + sal_uInt16 nItemId = START_ITEMID_WINDOWLIST; + + css::uno::Reference< css::frame::XFrame > xCurrentFrame = xDesktop->getCurrentFrame(); + css::uno::Reference< css::container::XIndexAccess > xList = xDesktop->getFrames(); + sal_Int32 nFrameCount = xList->getCount(); + aNewWindowListVector.reserve(nFrameCount); + for (sal_Int32 i=0; i<nFrameCount; ++i ) + { + css::uno::Reference< css::frame::XFrame > xFrame; + xList->getByIndex(i) >>= xFrame; + + if (xFrame.is()) + { + if ( xFrame == xCurrentFrame ) + nActiveItemId = nItemId; + + VclPtr<vcl::Window> pWin = VCLUnoHelper::GetWindow( xFrame->getContainerWindow() ); + OUString sWindowTitle; + if ( pWin && pWin->IsVisible() ) + sWindowTitle = pWin->GetText(); + + // tdf#101658 In case the frame is embedded somewhere, LO has no control over it. + // So we just skip it. + if ( sWindowTitle.isEmpty() ) + continue; + + aNewWindowListVector.push_back( sWindowTitle ); + ++nItemId; + } + } + + { + SolarMutexGuard g; + + VCLXMenu* pAwtMenu = comphelper::getFromUnoTunnel<VCLXMenu>( m_xPopupMenu ); + Menu* pVCLMenu = pAwtMenu->GetMenu(); + int nItemCount = pVCLMenu->GetItemCount(); + + if ( nItemCount > 0 ) + { + // remove all old window list entries from menu + sal_uInt16 nPos = pVCLMenu->GetItemPos( START_ITEMID_WINDOWLIST ); + for ( sal_uInt16 n = nPos; n < pVCLMenu->GetItemCount(); ) + pVCLMenu->RemoveItem( n ); + + if ( pVCLMenu->GetItemType( pVCLMenu->GetItemCount()-1 ) == MenuItemType::SEPARATOR ) + pVCLMenu->RemoveItem( pVCLMenu->GetItemCount()-1 ); + } + + if ( !aNewWindowListVector.empty() ) + { + // append new window list entries to menu + pVCLMenu->InsertSeparator(); + nItemId = START_ITEMID_WINDOWLIST; + const sal_uInt32 nCount = aNewWindowListVector.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + pVCLMenu->InsertItem( nItemId, aNewWindowListVector.at( i ), MenuItemBits::RADIOCHECK ); + if ( nItemId == nActiveItemId ) + pVCLMenu->CheckItem( nItemId ); + ++nItemId; + } + } + } +} + +void WindowListMenuController::itemSelected( const css::awt::MenuEvent& rEvent ) +{ + if ( rEvent.MenuId < START_ITEMID_WINDOWLIST || rEvent.MenuId > END_ITEMID_WINDOWLIST ) + return; + + // window list menu item selected + css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( m_xContext ); + + sal_uInt16 nTaskId = START_ITEMID_WINDOWLIST; + css::uno::Reference< css::container::XIndexAccess > xList = xDesktop->getFrames(); + sal_Int32 nCount = xList->getCount(); + for ( sal_Int32 i=0; i<nCount; ++i ) + { + css::uno::Reference< css::frame::XFrame > xFrame; + xList->getByIndex(i) >>= xFrame; + if ( xFrame.is() && nTaskId == rEvent.MenuId ) + { + VclPtr<vcl::Window> pWin = VCLUnoHelper::GetWindow( xFrame->getContainerWindow() ); + pWin->GrabFocus(); + pWin->ToTop( ToTopFlags::RestoreWhenMin ); + break; + } + + nTaskId++; + } +} + +void WindowListMenuController::impl_setPopupMenu() +{ + // Make this controller work also with initially empty + // menu, which PopupMenu::ImplExecute doesn't allow. + if (m_xPopupMenu.is() && !m_xPopupMenu->getItemCount()) + m_xPopupMenu->insertSeparator(0); +} + +OUString WindowListMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.WindowListMenuController"; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ResourceMenuController_get_implementation( + css::uno::XComponentContext* context, + css::uno::Sequence< css::uno::Any > const & args ) +{ + return cppu::acquire( new ResourceMenuController( context, args, false ) ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ToolbarAsMenuController_get_implementation( + css::uno::XComponentContext* context, + css::uno::Sequence< css::uno::Any > const & args ) +{ + return cppu::acquire( new ResourceMenuController( context, args, true ) ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_WindowListMenuController_get_implementation( + css::uno::XComponentContext* context, + css::uno::Sequence< css::uno::Any > const & args ) +{ + return cppu::acquire( new WindowListMenuController( context, args ) ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_SaveAsMenuController_get_implementation( + css::uno::XComponentContext* context, + css::uno::Sequence< css::uno::Any > const & args ) +{ + return cppu::acquire( new SaveAsMenuController( context, args ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/spinfieldtoolbarcontroller.cxx b/framework/source/uielement/spinfieldtoolbarcontroller.cxx new file mode 100644 index 000000000..fdcac34fd --- /dev/null +++ b/framework/source/uielement/spinfieldtoolbarcontroller.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 <stdio.h> + +#include <uielement/spinfieldtoolbarcontroller.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <svtools/toolboxcontroller.hxx> +#include <vcl/InterimItemWindow.hxx> +#include <vcl/event.hxx> +#include <vcl/formatter.hxx> +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> +#include <o3tl/char16_t2wchar_t.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::util; + +namespace framework +{ + +// Wrapper class to notify controller about events from combobox. +// Unfortunaltly the events are notified through virtual methods instead +// of Listeners. + +class SpinfieldControl final : public InterimItemWindow +{ +public: + SpinfieldControl(vcl::Window* pParent, SpinfieldToolbarController* pSpinfieldToolbarController); + virtual ~SpinfieldControl() override; + virtual void dispose() override; + + Formatter& GetFormatter() + { + return m_xWidget->GetFormatter(); + } + + OUString get_entry_text() const { return m_xWidget->get_text(); } + + DECL_LINK(ValueChangedHdl, weld::FormattedSpinButton&, void); + DECL_LINK(FormatOutputHdl, LinkParamNone*, bool); + DECL_LINK(ParseInputHdl, sal_Int64*, TriState); + DECL_LINK(ModifyHdl, weld::Entry&, void); + DECL_LINK(ActivateHdl, weld::Entry&, bool); + DECL_LINK(FocusInHdl, weld::Widget&, void); + DECL_LINK(FocusOutHdl, weld::Widget&, void); + DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool); + +private: + std::unique_ptr<weld::FormattedSpinButton> m_xWidget; + SpinfieldToolbarController* m_pSpinfieldToolbarController; +}; + +SpinfieldControl::SpinfieldControl(vcl::Window* pParent, SpinfieldToolbarController* pSpinfieldToolbarController) + : InterimItemWindow(pParent, "svt/ui/spinfieldcontrol.ui", "SpinFieldControl") + , m_xWidget(m_xBuilder->weld_formatted_spin_button("spinbutton")) + , m_pSpinfieldToolbarController(pSpinfieldToolbarController) +{ + InitControlBase(m_xWidget.get()); + + m_xWidget->connect_focus_in(LINK(this, SpinfieldControl, FocusInHdl)); + m_xWidget->connect_focus_out(LINK(this, SpinfieldControl, FocusOutHdl)); + Formatter& rFormatter = m_xWidget->GetFormatter(); + rFormatter.SetOutputHdl(LINK(this, SpinfieldControl, FormatOutputHdl)); + rFormatter.SetInputHdl(LINK(this, SpinfieldControl, ParseInputHdl)); + m_xWidget->connect_value_changed(LINK(this, SpinfieldControl, ValueChangedHdl)); + m_xWidget->connect_changed(LINK(this, SpinfieldControl, ModifyHdl)); + m_xWidget->connect_activate(LINK(this, SpinfieldControl, ActivateHdl)); + m_xWidget->connect_key_press(LINK(this, SpinfieldControl, KeyInputHdl)); + + // so a later narrow size request can stick + m_xWidget->set_width_chars(3); + m_xWidget->set_size_request(42, -1); + + SetSizePixel(get_preferred_size()); +} + +IMPL_LINK(SpinfieldControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +IMPL_LINK(SpinfieldControl, ParseInputHdl, sal_Int64*, result, TriState) +{ + *result = m_xWidget->get_text().toDouble() * weld::SpinButton::Power10(m_xWidget->GetFormatter().GetDecimalDigits()); + return TRISTATE_TRUE; +} + +SpinfieldControl::~SpinfieldControl() +{ + disposeOnce(); +} + +void SpinfieldControl::dispose() +{ + m_pSpinfieldToolbarController = nullptr; + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +IMPL_LINK_NOARG(SpinfieldControl, ValueChangedHdl, weld::FormattedSpinButton&, void) +{ + if (m_pSpinfieldToolbarController) + m_pSpinfieldToolbarController->execute(0); +} + +IMPL_LINK_NOARG(SpinfieldControl, ModifyHdl, weld::Entry&, void) +{ + if (m_pSpinfieldToolbarController) + m_pSpinfieldToolbarController->Modify(); +} + +IMPL_LINK_NOARG(SpinfieldControl, FocusInHdl, weld::Widget&, void) +{ + if (m_pSpinfieldToolbarController) + m_pSpinfieldToolbarController->GetFocus(); +} + +IMPL_LINK_NOARG(SpinfieldControl, FocusOutHdl, weld::Widget&, void) +{ + if (m_pSpinfieldToolbarController) + m_pSpinfieldToolbarController->LoseFocus(); +} + +IMPL_LINK_NOARG(SpinfieldControl, ActivateHdl, weld::Entry&, bool) +{ + bool bConsumed = false; + if (m_pSpinfieldToolbarController) + { + m_pSpinfieldToolbarController->Activate(); + bConsumed = true; + } + return bConsumed; +} + +IMPL_LINK_NOARG(SpinfieldControl, FormatOutputHdl, LinkParamNone*, bool) +{ + OUString aText = m_pSpinfieldToolbarController->FormatOutputString(m_xWidget->GetFormatter().GetValue()); + m_xWidget->set_text(aText); + return true; +} + +SpinfieldToolbarController::SpinfieldToolbarController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + ToolBox* pToolbar, + ToolBoxItemId nID, + sal_Int32 nWidth, + const OUString& aCommand ) : + ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand ) + , m_bFloat( false ) + , m_nMax( 0.0 ) + , m_nMin( 0.0 ) + , m_nValue( 0.0 ) + , m_nStep( 0.0 ) + , m_pSpinfieldControl( nullptr ) +{ + m_pSpinfieldControl = VclPtr<SpinfieldControl>::Create(m_xToolbar, this); + if ( nWidth == 0 ) + nWidth = 100; + + // SpinFieldControl ctor has set a suitable height already + auto nHeight = m_pSpinfieldControl->GetSizePixel().Height(); + + m_pSpinfieldControl->SetSizePixel( ::Size( nWidth, nHeight )); + m_xToolbar->SetItemWindow( m_nID, m_pSpinfieldControl ); +} + +SpinfieldToolbarController::~SpinfieldToolbarController() +{ +} + +void SAL_CALL SpinfieldToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + + m_xToolbar->SetItemWindow( m_nID, nullptr ); + m_pSpinfieldControl.disposeAndClear(); + + ComplexToolbarController::dispose(); +} + +Sequence<PropertyValue> SpinfieldToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const +{ + OUString aSpinfieldText = m_pSpinfieldControl->get_entry_text(); + + // Add key modifier to argument list + auto aArgs0 = comphelper::makePropertyValue("KeyModifier", KeyModifier); + auto aArgs1 = comphelper::makePropertyValue("Value", m_bFloat ? Any(aSpinfieldText.toDouble()) + : Any(aSpinfieldText.toInt32())); + return { aArgs0, aArgs1 }; +} + +void SpinfieldToolbarController::Modify() +{ + notifyTextChanged(m_pSpinfieldControl->get_entry_text()); +} + +void SpinfieldToolbarController::GetFocus() +{ + notifyFocusGet(); +} + +void SpinfieldToolbarController::LoseFocus() +{ + notifyFocusLost(); +} + +void SpinfieldToolbarController::Activate() +{ + // Call execute only with non-empty text + if (!m_pSpinfieldControl->get_entry_text().isEmpty()) + execute(0); +} + +void SpinfieldToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand ) +{ + OUString aValue; + OUString aMax; + OUString aMin; + OUString aStep; + bool bFloatValue( false ); + + if ( rControlCommand.Command == "SetStep" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "Step" ) + { + sal_Int32 nValue; + double fValue; + bool bFloat( false ); + if ( impl_getValue( arg.Value, nValue, fValue, bFloat )) + aStep = bFloat ? OUString( OUString::number( fValue )) : + OUString( OUString::number( nValue )); + break; + } + } + } + else if ( rControlCommand.Command == "SetValue" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "Value" ) + { + sal_Int32 nValue; + double fValue; + bool bFloat( false ); + + if ( impl_getValue( arg.Value, nValue, fValue, bFloat )) + { + aValue = bFloat ? OUString( OUString::number( fValue )) : + OUString( OUString::number( nValue )); + bFloatValue = bFloat; + } + break; + } + } + } + else if ( rControlCommand.Command == "SetValues" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + sal_Int32 nValue; + double fValue; + bool bFloat( false ); + + OUString aName = arg.Name; + if ( impl_getValue( arg.Value, nValue, fValue, bFloat )) + { + if ( aName == "Value" ) + { + aValue = bFloat ? OUString( OUString::number( fValue )) : + OUString( OUString::number( nValue )); + bFloatValue = bFloat; + } + else if ( aName == "Step" ) + aStep = bFloat ? OUString( OUString::number( fValue )) : + OUString( OUString::number( nValue )); + else if ( aName == "LowerLimit" ) + aMin = bFloat ? OUString( OUString::number( fValue )) : + OUString( OUString::number( nValue )); + else if ( aName == "UpperLimit" ) + aMax = bFloat ? OUString( OUString::number( fValue )) : + OUString( OUString::number( nValue )); + } + else if ( aName == "OutputFormat" ) + arg.Value >>= m_aOutFormat; + } + } + else if ( rControlCommand.Command == "SetLowerLimit" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "LowerLimit" ) + { + sal_Int32 nValue; + double fValue; + bool bFloat( false ); + if ( impl_getValue( arg.Value, nValue, fValue, bFloat )) + aMin = bFloat ? OUString( OUString::number( fValue )) : + OUString( OUString::number( nValue )); + break; + } + } + } + else if ( rControlCommand.Command == "SetUpperLimit" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "UpperLimit" ) + { + sal_Int32 nValue; + double fValue; + bool bFloat( false ); + if ( impl_getValue( arg.Value, nValue, fValue, bFloat )) + aMax = bFloat ? OUString( OUString::number( fValue )) : + OUString( OUString::number( nValue )); + break; + } + } + } + else if ( rControlCommand.Command == "SetOutputFormat" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "OutputFormat" ) + { + arg.Value >>= m_aOutFormat; + break; + } + } + } + + Formatter& rFormatter = m_pSpinfieldControl->GetFormatter(); + + // Check values and set members + if (bFloatValue) + rFormatter.SetDecimalDigits(2); + if ( !aValue.isEmpty() ) + { + m_bFloat = bFloatValue; + m_nValue = aValue.toDouble(); + rFormatter.SetValue(m_nValue); + } + if ( !aMax.isEmpty() ) + { + m_nMax = aMax.toDouble(); + rFormatter.SetMaxValue(m_nMax); + } + if ( !aMin.isEmpty() ) + { + m_nMin = aMin.toDouble(); + rFormatter.SetMinValue(m_nMin); + } + if ( !aStep.isEmpty() ) + { + m_nStep = aStep.toDouble(); + rFormatter.SetSpinSize(m_nStep); + } +} + +bool SpinfieldToolbarController::impl_getValue( + const Any& rAny, sal_Int32& nValue, double& fValue, bool& bFloat ) +{ + using ::com::sun::star::uno::TypeClass; + + bool bValueValid( false ); + + bFloat = false; + TypeClass aTypeClass = rAny.getValueTypeClass(); + if (( aTypeClass == TypeClass( typelib_TypeClass_LONG )) || + ( aTypeClass == TypeClass( typelib_TypeClass_SHORT )) || + ( aTypeClass == TypeClass( typelib_TypeClass_BYTE ))) + bValueValid = rAny >>= nValue; + else if (( aTypeClass == TypeClass( typelib_TypeClass_FLOAT )) || + ( aTypeClass == TypeClass( typelib_TypeClass_DOUBLE ))) + { + bValueValid = rAny >>= fValue; + bFloat = true; + } + + return bValueValid; +} + +OUString SpinfieldToolbarController::FormatOutputString( double fValue ) +{ + if ( m_aOutFormat.isEmpty() ) + { + if ( m_bFloat ) + return OUString::number( fValue ); + else + return OUString::number( sal_Int32( fValue )); + } + else + { +#ifdef _WIN32 + sal_Unicode aBuffer[128]; + + aBuffer[0] = 0; + if ( m_bFloat ) + _snwprintf( o3tl::toW(aBuffer), SAL_N_ELEMENTS(aBuffer), o3tl::toW(m_aOutFormat.getStr()), fValue ); + else + _snwprintf( o3tl::toW(aBuffer), SAL_N_ELEMENTS(aBuffer), o3tl::toW(m_aOutFormat.getStr()), sal_Int32( fValue )); + + return OUString(aBuffer); +#else + // Currently we have no support for a format string using sal_Unicode. wchar_t + // is 32 bit on Unix platform! + char aBuffer[128]; + + OString aFormat = OUStringToOString( m_aOutFormat, osl_getThreadTextEncoding() ); + if ( m_bFloat ) + snprintf( aBuffer, 128, aFormat.getStr(), fValue ); + else + snprintf( aBuffer, 128, aFormat.getStr(), static_cast<tools::Long>( fValue )); + + sal_Int32 nSize = strlen( aBuffer ); + OString aTmp( aBuffer, nSize ); + return OStringToOUString( aTmp, osl_getThreadTextEncoding() ); +#endif + } +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/statusbar.cxx b/framework/source/uielement/statusbar.cxx new file mode 100644 index 000000000..7189e6615 --- /dev/null +++ b/framework/source/uielement/statusbar.cxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <uielement/statusbar.hxx> + +#include <vcl/svapp.hxx> + +namespace framework +{ + +FrameworkStatusBar::FrameworkStatusBar( + vcl::Window* pParent, + WinBits nWinBits ) : + StatusBar( pParent, nWinBits ), + m_pMgr( nullptr ) +{ + // set optimal size + SetOutputSizePixel( CalcWindowSizePixel() ); +} + +void FrameworkStatusBar::SetStatusBarManager( StatusBarManager* pStatusBarManager ) +{ + SolarMutexGuard aSolarMutexGuard; + m_pMgr = pStatusBarManager; +} + +void FrameworkStatusBar::UserDraw(const UserDrawEvent& rUDEvt) +{ + if ( m_pMgr ) + m_pMgr->UserDraw( rUDEvt ); +} + +void FrameworkStatusBar::Command( const CommandEvent& rEvt ) +{ + if ( m_pMgr ) + m_pMgr->Command( rEvt ); +} + +void FrameworkStatusBar::StateChanged( StateChangedType ) +{ +} + +void FrameworkStatusBar::DataChanged( const DataChangedEvent& rDCEvt ) +{ + StatusBar::DataChanged( rDCEvt ); + if ( m_pMgr ) + m_pMgr->DataChanged( rDCEvt ); +} + +void FrameworkStatusBar::MouseMove( const MouseEvent& rMEvt ) +{ + StatusBar::MouseMove( rMEvt ); + if ( m_pMgr ) + m_pMgr->MouseMove( rMEvt ); +} + +void FrameworkStatusBar::MouseButtonDown( const MouseEvent& rMEvt ) +{ + StatusBar::MouseButtonDown( rMEvt ); + if ( m_pMgr ) + m_pMgr->MouseButtonDown( rMEvt ); +} + +void FrameworkStatusBar::MouseButtonUp( const MouseEvent& rMEvt ) +{ + StatusBar::MouseButtonUp( rMEvt ); + if ( m_pMgr ) + m_pMgr->MouseButtonUp( rMEvt ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/statusbaritem.cxx b/framework/source/uielement/statusbaritem.cxx new file mode 100644 index 000000000..baf321fd6 --- /dev/null +++ b/framework/source/uielement/statusbaritem.cxx @@ -0,0 +1,236 @@ +/* -*- 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 <uielement/statusbaritem.hxx> +#include <utility> +#include <vcl/status.hxx> +#include <vcl/svapp.hxx> + +#include <com/sun/star/ui/ItemStyle.hpp> + +using namespace com::sun::star::ui; + +namespace framework +{ + +namespace +{ +sal_uInt16 impl_convertItemBitsToItemStyle( StatusBarItemBits nItemBits ) +{ + sal_uInt16 nStyle( 0 ); + + if ( nItemBits & StatusBarItemBits::Right ) + nStyle |= ItemStyle::ALIGN_RIGHT; + else if ( nItemBits & StatusBarItemBits::Left ) + nStyle |= ItemStyle::ALIGN_LEFT; + else + nStyle |= ItemStyle::ALIGN_CENTER; + + if ( nItemBits & StatusBarItemBits::Flat ) + nStyle |= ItemStyle::DRAW_FLAT; + else if ( nItemBits & StatusBarItemBits::Out ) + nStyle |= ItemStyle::DRAW_OUT3D; + else + nStyle |= ItemStyle::DRAW_IN3D; + + if ( nItemBits & StatusBarItemBits::AutoSize ) + nStyle |= ItemStyle::AUTO_SIZE; + + if ( nItemBits & StatusBarItemBits::UserDraw ) + nStyle |= ItemStyle::OWNER_DRAW; + + return nStyle; +} +} + +StatusbarItem::StatusbarItem( + StatusBar *pStatusBar, + sal_uInt16 nId, + OUString aCommand ) + : m_pStatusBar( pStatusBar ) + , m_nId( nId ) + , m_nStyle( 0 ) + , m_aCommand(std::move( aCommand )) +{ + if ( m_pStatusBar ) + m_nStyle = impl_convertItemBitsToItemStyle( + m_pStatusBar->GetItemBits( m_nId ) ); +} + +StatusbarItem::~StatusbarItem() +{ +} + +void StatusbarItem::disposing(std::unique_lock<std::mutex>&) +{ + m_pStatusBar = nullptr; +} + +OUString SAL_CALL StatusbarItem::getCommand() +{ + std::unique_lock aGuard( m_aMutex ); + return m_aCommand; +} + +::sal_uInt16 SAL_CALL StatusbarItem::getItemId() +{ + std::unique_lock aGuard( m_aMutex ); + return m_nId; +} + +::sal_uInt32 SAL_CALL StatusbarItem::getWidth() +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + return m_pStatusBar->GetItemWidth( m_nId ); + + return ::sal_uInt32(0); +} + +::sal_uInt16 SAL_CALL StatusbarItem::getStyle() +{ + std::unique_lock aGuard( m_aMutex ); + return m_nStyle; +} + +::sal_Int32 SAL_CALL StatusbarItem::getOffset() +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + return m_pStatusBar->GetItemOffset( m_nId ); + + return 0; +} + +css::awt::Rectangle SAL_CALL StatusbarItem::getItemRect() +{ + SolarMutexGuard aGuard; + css::awt::Rectangle aAWTRect; + if ( m_pStatusBar ) + { + tools::Rectangle aRect = m_pStatusBar->GetItemRect( m_nId ); + return css::awt::Rectangle( aRect.Left(), + aRect.Top(), + aRect.GetWidth(), + aRect.GetHeight() ); + } + + return aAWTRect; +} + +OUString SAL_CALL StatusbarItem::getText() +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + return m_pStatusBar->GetItemText( m_nId ); + + return OUString(); +} + +void SAL_CALL StatusbarItem::setText( const OUString& rText ) +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + m_pStatusBar->SetItemText( m_nId, rText ); +} + +OUString SAL_CALL StatusbarItem::getHelpText() +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + return m_pStatusBar->GetHelpText( m_nId ); + + return OUString(); +} + +void SAL_CALL StatusbarItem::setHelpText( const OUString& rHelpText ) +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + m_pStatusBar->SetHelpText( m_nId, rHelpText ); +} + +OUString SAL_CALL StatusbarItem::getQuickHelpText() +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + return m_pStatusBar->GetHelpText( m_nId ); + + return OUString(); +} + +void SAL_CALL StatusbarItem::setQuickHelpText( const OUString& rQuickHelpText ) +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + m_pStatusBar->SetQuickHelpText( m_nId, rQuickHelpText ); +} + +OUString SAL_CALL StatusbarItem::getAccessibleName() +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + return m_pStatusBar->GetAccessibleName( m_nId ); + + return OUString(); +} + +void SAL_CALL StatusbarItem::setAccessibleName( const OUString& rAccessibleName ) +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + m_pStatusBar->SetAccessibleName( m_nId, rAccessibleName ); +} + +sal_Bool SAL_CALL StatusbarItem::getVisible() +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + return m_pStatusBar->IsItemVisible( m_nId ); + + return false; +} + +void SAL_CALL StatusbarItem::setVisible( sal_Bool bVisible ) +{ + SolarMutexGuard aGuard; + if ( !m_pStatusBar ) + return; + + if ( bool(bVisible) != m_pStatusBar->IsItemVisible( m_nId ) ) + { + if ( bVisible ) + m_pStatusBar->ShowItem( m_nId ); + else + m_pStatusBar->HideItem( m_nId ); + } +} + +void SAL_CALL StatusbarItem::repaint( ) +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + { + m_pStatusBar->RedrawItem( m_nId ); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/statusbarmanager.cxx b/framework/source/uielement/statusbarmanager.cxx new file mode 100644 index 000000000..dbc305c69 --- /dev/null +++ b/framework/source/uielement/statusbarmanager.cxx @@ -0,0 +1,656 @@ +/* -*- 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 <uielement/statusbarmanager.hxx> +#include <uielement/genericstatusbarcontroller.hxx> + +#include <framework/sfxhelperfunctions.hxx> +#include <framework/addonsoptions.hxx> +#include <uielement/statusbarmerger.hxx> +#include <uielement/statusbaritem.hxx> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/frame/theStatusbarControllerFactory.hpp> +#include <com/sun/star/ui/ItemStyle.hpp> +#include <com/sun/star/ui/ItemType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/awt/Command.hpp> +#include <com/sun/star/ui/XStatusbarItem.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <svtools/statusbarcontroller.hxx> +#include <tools/debug.hxx> + +#include <utility> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <vcl/status.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/commandinfoprovider.hxx> + +#include <cassert> + +using namespace ::com::sun::star; + +namespace framework +{ + +namespace +{ + +template< class MAP > +struct lcl_UpdateController +{ + void operator()( typename MAP::value_type &rElement ) const + { + try + { + if ( rElement.second.is() ) + rElement.second->update(); + } + catch ( uno::Exception& ) + { + } + } +}; + +template< class MAP > +struct lcl_RemoveController +{ + void operator()( typename MAP::value_type &rElement ) const + { + try + { + if ( rElement.second.is() ) + rElement.second->dispose(); + } + catch ( uno::Exception& ) + { + } + } +}; + +StatusBarItemBits impl_convertItemStyleToItemBits( sal_Int16 nStyle ) +{ + StatusBarItemBits nItemBits( StatusBarItemBits::NONE ); + + if (( nStyle & css::ui::ItemStyle::ALIGN_RIGHT ) == css::ui::ItemStyle::ALIGN_RIGHT ) + nItemBits |= StatusBarItemBits::Right; + else if ( nStyle & css::ui::ItemStyle::ALIGN_LEFT ) + nItemBits |= StatusBarItemBits::Left; + else + nItemBits |= StatusBarItemBits::Center; + + if (( nStyle & css::ui::ItemStyle::DRAW_FLAT ) == css::ui::ItemStyle::DRAW_FLAT ) + nItemBits |= StatusBarItemBits::Flat; + else if ( nStyle & css::ui::ItemStyle::DRAW_OUT3D ) + nItemBits |= StatusBarItemBits::Out; + else + nItemBits |= StatusBarItemBits::In; + + if (( nStyle & css::ui::ItemStyle::AUTO_SIZE ) == css::ui::ItemStyle::AUTO_SIZE ) + nItemBits |= StatusBarItemBits::AutoSize; + if ( nStyle & css::ui::ItemStyle::OWNER_DRAW ) + nItemBits |= StatusBarItemBits::UserDraw; + + if ( nStyle & css::ui::ItemStyle::MANDATORY ) + nItemBits |= StatusBarItemBits::Mandatory; + + return nItemBits; +} + +} + +StatusBarManager::StatusBarManager( + uno::Reference< uno::XComponentContext > xContext, + uno::Reference< frame::XFrame > rFrame, + StatusBar* pStatusBar ) : + m_bDisposed( false ), + m_bFrameActionRegistered( false ), + m_bUpdateControllers( false ), + m_pStatusBar( pStatusBar ), + m_xFrame(std::move( rFrame )), + m_xContext(std::move( xContext )) +{ + + m_xStatusbarControllerFactory = frame::theStatusbarControllerFactory::get( + ::comphelper::getProcessComponentContext()); + + m_pStatusBar->AdjustItemWidthsForHiDPI(); + m_pStatusBar->SetClickHdl( LINK( this, StatusBarManager, Click ) ); + m_pStatusBar->SetDoubleClickHdl( LINK( this, StatusBarManager, DoubleClick ) ); +} + +StatusBarManager::~StatusBarManager() +{ +} + +StatusBar* StatusBarManager::GetStatusBar() const +{ + SolarMutexGuard g; + return m_pStatusBar; +} + +void StatusBarManager::frameAction( const frame::FrameActionEvent& Action ) +{ + SolarMutexGuard g; + if ( Action.Action == frame::FrameAction_CONTEXT_CHANGED ) + UpdateControllers(); +} + +void SAL_CALL StatusBarManager::disposing( const lang::EventObject& Source ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + RemoveControllers(); + + if ( Source.Source == uno::Reference< uno::XInterface >( m_xFrame, uno::UNO_QUERY )) + m_xFrame.clear(); + + m_xContext.clear(); +} + +// XComponent +void SAL_CALL StatusBarManager::dispose() +{ + uno::Reference< lang::XComponent > xThis(this ); + + { + lang::EventObject aEvent( xThis ); + std::unique_lock aGuard(m_mutex); + m_aListenerContainer.disposeAndClear( aGuard, aEvent ); + } + { + SolarMutexGuard g; + if ( m_bDisposed ) + return; + + RemoveControllers(); + + // destroy the item data + for ( sal_uInt16 n = 0; n < m_pStatusBar->GetItemCount(); n++ ) + { + AddonStatusbarItemData *pUserData = static_cast< AddonStatusbarItemData *>( + m_pStatusBar->GetItemData( m_pStatusBar->GetItemId( n ) ) ); + delete pUserData; + } + + m_pStatusBar.disposeAndClear(); + + if ( m_bFrameActionRegistered && m_xFrame.is() ) + { + try + { + m_xFrame->removeFrameActionListener( uno::Reference< frame::XFrameActionListener >(this) ); + } + catch ( const uno::Exception& ) + { + } + } + + m_xFrame.clear(); + m_xContext.clear(); + + m_bDisposed = true; + } +} + +void SAL_CALL StatusBarManager::addEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw lang::DisposedException(); + + std::unique_lock aGuard(m_mutex); + m_aListenerContainer.addInterface( aGuard, xListener ); +} + +void SAL_CALL StatusBarManager::removeEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + std::unique_lock aGuard(m_mutex); + m_aListenerContainer.removeInterface( aGuard, xListener ); +} + +// XUIConfigurationListener +void SAL_CALL StatusBarManager::elementInserted( const css::ui::ConfigurationEvent& ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; +} + +void SAL_CALL StatusBarManager::elementRemoved( const css::ui::ConfigurationEvent& ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; +} + +void SAL_CALL StatusBarManager::elementReplaced( const css::ui::ConfigurationEvent& ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; +} + +void StatusBarManager::UpdateControllers() +{ + if ( !m_bUpdateControllers ) + { + m_bUpdateControllers = true; + std::for_each( m_aControllerMap.begin(), + m_aControllerMap.end(), + lcl_UpdateController< StatusBarControllerMap >() ); + } + m_bUpdateControllers = false; +} + +void StatusBarManager::RemoveControllers() +{ + DBG_TESTSOLARMUTEX(); + assert(!m_bDisposed); + + std::for_each( m_aControllerMap.begin(), + m_aControllerMap.end(), + lcl_RemoveController< StatusBarControllerMap >() ); + m_aControllerMap.clear(); +} + +void StatusBarManager::CreateControllers() +{ + uno::Reference< awt::XWindow > xStatusbarWindow = VCLUnoHelper::GetInterface( m_pStatusBar ); + + for ( sal_uInt16 i = 0; i < m_pStatusBar->GetItemCount(); i++ ) + { + sal_uInt16 nId = m_pStatusBar->GetItemId( i ); + if ( nId == 0 ) + continue; + + OUString aCommandURL( m_pStatusBar->GetItemCommand( nId )); + bool bInit( true ); + uno::Reference< frame::XStatusbarController > xController; + AddonStatusbarItemData *pItemData = static_cast< AddonStatusbarItemData *>( m_pStatusBar->GetItemData( nId ) ); + uno::Reference< ui::XStatusbarItem > xStatusbarItem = new StatusbarItem( m_pStatusBar, nId, aCommandURL ); + + beans::PropertyValue aPropValue; + std::vector< uno::Any > aPropVector; + + aPropValue.Name = "CommandURL"; + aPropValue.Value <<= aCommandURL; + aPropVector.push_back( uno::Any( aPropValue ) ); + + aPropValue.Name = "ModuleIdentifier"; + aPropValue.Value <<= OUString(); + aPropVector.push_back( uno::Any( aPropValue ) ); + + aPropValue.Name = "Frame"; + aPropValue.Value <<= m_xFrame; + aPropVector.push_back( uno::Any( aPropValue ) ); + + // TODO remove this + aPropValue.Name = "ServiceManager"; + aPropValue.Value <<= uno::Reference<lang::XMultiServiceFactory>(m_xContext->getServiceManager(), uno::UNO_QUERY_THROW); + aPropVector.push_back( uno::Any( aPropValue ) ); + + aPropValue.Name = "ParentWindow"; + aPropValue.Value <<= xStatusbarWindow; + aPropVector.push_back( uno::Any( aPropValue ) ); + + // TODO still needing with the css::ui::XStatusbarItem? + aPropValue.Name = "Identifier"; + aPropValue.Value <<= nId; + aPropVector.push_back( uno::Any( aPropValue ) ); + + aPropValue.Name = "StatusbarItem"; + aPropValue.Value <<= xStatusbarItem; + aPropVector.push_back( uno::Any( aPropValue ) ); + + uno::Sequence< uno::Any > aArgs( comphelper::containerToSequence( aPropVector ) ); + + // 1) UNO Statusbar controllers, registered in Controllers.xcu + if ( m_xStatusbarControllerFactory.is() && + m_xStatusbarControllerFactory->hasController( aCommandURL, "" )) + { + xController.set(m_xStatusbarControllerFactory->createInstanceWithArgumentsAndContext( + aCommandURL, aArgs, m_xContext ), + uno::UNO_QUERY ); + bInit = false; // Initialization is done through the factory service + } + + if ( !xController.is() ) + { + // 2) Old SFX2 Statusbar controllers + xController = CreateStatusBarController( m_xFrame, m_pStatusBar, nId, aCommandURL ); + if ( !xController ) + { + // 3) Is Add-on? Generic statusbar controller + if ( pItemData ) + { + xController = new GenericStatusbarController( m_xContext, + m_xFrame, + xStatusbarItem, + pItemData ); + } + else + { + // 4) Default Statusbar controller + xController = new svt::StatusbarController( m_xContext, m_xFrame, aCommandURL, nId ); + } + } + } + + m_aControllerMap[nId] = xController; + if ( bInit ) + { + xController->initialize( aArgs ); + } + } + + // add frame action listeners + if ( !m_bFrameActionRegistered && m_xFrame.is() ) + { + m_bFrameActionRegistered = true; + m_xFrame->addFrameActionListener( uno::Reference< frame::XFrameActionListener >(this) ); + } +} + +void StatusBarManager::FillStatusBar( const uno::Reference< container::XIndexAccess >& rItemContainer ) +{ + SolarMutexGuard g; + + if ( m_bDisposed || !m_pStatusBar ) + return; + + sal_uInt16 nId( 1 ); + + RemoveControllers(); + + // reset and fill command map + m_pStatusBar->Clear(); + m_aControllerMap.clear();// TODO already done in RemoveControllers + + for ( sal_Int32 n = 0; n < rItemContainer->getCount(); n++ ) + { + uno::Sequence< beans::PropertyValue > aProps; + OUString aCommandURL; + sal_Int16 nOffset( 0 ); + sal_Int16 nStyle( 0 ); + sal_Int16 nWidth( 0 ); + sal_uInt16 nType( css::ui::ItemType::DEFAULT ); + + try + { + if ( rItemContainer->getByIndex( n ) >>= aProps ) + { + for ( beans::PropertyValue const & prop : std::as_const(aProps) ) + { + if ( prop.Name == "CommandURL" ) + { + prop.Value >>= aCommandURL; + } + else if ( prop.Name == "Style" ) + { + prop.Value >>= nStyle; + } + else if ( prop.Name == "Type" ) + { + prop.Value >>= nType; + } + else if ( prop.Name == "Width" ) + { + prop.Value >>= nWidth; + } + else if ( prop.Name == "Offset" ) + { + prop.Value >>= nOffset; + } + } + + if (( nType == css::ui::ItemType::DEFAULT ) && !aCommandURL.isEmpty() ) + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommandURL, ""); + OUString aString(vcl::CommandInfoProvider::GetLabelForCommand(aProperties)); + StatusBarItemBits nItemBits( impl_convertItemStyleToItemBits( nStyle )); + + m_pStatusBar->InsertItem( nId, nWidth, nItemBits, nOffset ); + m_pStatusBar->SetItemCommand( nId, aCommandURL ); + m_pStatusBar->SetAccessibleName( nId, aString ); + ++nId; + } + } + } + catch ( const css::lang::IndexOutOfBoundsException& ) + { + break; + } + } + + // Statusbar Merging + constexpr sal_uInt16 STATUSBAR_ITEM_STARTID = 1000; + MergeStatusbarInstructionContainer aMergeInstructions = AddonsOptions().GetMergeStatusbarInstructions(); + if ( !aMergeInstructions.empty() ) + { + const sal_uInt32 nCount = aMergeInstructions.size(); + sal_uInt16 nItemId( STATUSBAR_ITEM_STARTID ); + + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + MergeStatusbarInstruction &rInstruction = aMergeInstructions[i]; + if ( !StatusbarMerger::IsCorrectContext( rInstruction.aMergeContext ) ) + continue; + + AddonStatusbarItemContainer aItems; + StatusbarMerger::ConvertSeqSeqToVector( rInstruction.aMergeStatusbarItems, aItems ); + + sal_uInt16 nRefPos = StatusbarMerger::FindReferencePos( m_pStatusBar, rInstruction.aMergePoint ); + if ( nRefPos != STATUSBAR_ITEM_NOTFOUND ) + { + StatusbarMerger::ProcessMergeOperation( m_pStatusBar, + nRefPos, + nItemId, + rInstruction.aMergeCommand, + rInstruction.aMergeCommandParameter, + aItems ); + } + else + { + StatusbarMerger::ProcessMergeFallback( m_pStatusBar, + nItemId, + rInstruction.aMergeCommand, + rInstruction.aMergeCommandParameter, + aItems ); + } + } + } + + // Create controllers + CreateControllers(); + + // Notify controllers that they are now correctly initialized and can start listening + UpdateControllers(); +} + +void StatusBarManager::DataChanged( const DataChangedEvent& rDCEvt ) +{ + SolarMutexClearableGuard aGuard; + + if ((( rDCEvt.GetType() == DataChangedEventType::SETTINGS ) || + ( rDCEvt.GetType() == DataChangedEventType::FONTS ) || + ( rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION ) || + ( rDCEvt.GetType() == DataChangedEventType::DISPLAY )) && + ( rDCEvt.GetFlags() & AllSettingsFlags::STYLE )) + { + css::uno::Reference< css::frame::XLayoutManager > xLayoutManager; + css::uno::Reference< css::beans::XPropertySet > xPropSet( m_xFrame, css::uno::UNO_QUERY ); + if ( xPropSet.is() ) + xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager; + if ( xLayoutManager.is() ) + { + aGuard.clear(); + xLayoutManager->doLayout(); + } + } +} + +void StatusBarManager::UserDraw( const UserDrawEvent& rUDEvt ) +{ + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + return; + + sal_uInt16 nId( rUDEvt.GetItemId() ); + StatusBarControllerMap::const_iterator it = m_aControllerMap.find( nId ); + if (( nId <= 0 ) || ( it == m_aControllerMap.end() )) + return; + + uno::Reference< frame::XStatusbarController > xController( it->second ); + if (xController.is() && rUDEvt.GetRenderContext()) + { + uno::Reference< awt::XGraphics > xGraphics = rUDEvt.GetRenderContext()->CreateUnoGraphics(); + + awt::Rectangle aRect( rUDEvt.GetRect().Left(), + rUDEvt.GetRect().Top(), + rUDEvt.GetRect().GetWidth(), + rUDEvt.GetRect().GetHeight() ); + aGuard.clear(); + xController->paint(xGraphics, aRect, 0); + } +} + +void StatusBarManager::Command( const CommandEvent& rEvt ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + if ( rEvt.GetCommand() != CommandEventId::ContextMenu ) + return; + + sal_uInt16 nId = m_pStatusBar->GetItemId( rEvt.GetMousePosPixel() ); + StatusBarControllerMap::const_iterator it = m_aControllerMap.find( nId ); + if (( nId > 0 ) && ( it != m_aControllerMap.end() )) + { + uno::Reference< frame::XStatusbarController > xController( it->second ); + if ( xController.is() ) + { + awt::Point aPos; + aPos.X = rEvt.GetMousePosPixel().X(); + aPos.Y = rEvt.GetMousePosPixel().Y(); + xController->command( aPos, awt::Command::CONTEXTMENU, true, uno::Any() ); + } + } +} + +void StatusBarManager::MouseMove( const MouseEvent& rMEvt ) +{ + MouseButton(rMEvt,&frame::XStatusbarController::mouseMove); +} + +void StatusBarManager::MouseButton( const MouseEvent& rMEvt ,sal_Bool ( SAL_CALL frame::XStatusbarController::*_pMethod )(const css::awt::MouseEvent&)) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + sal_uInt16 nId = m_pStatusBar->GetItemId( rMEvt.GetPosPixel() ); + StatusBarControllerMap::const_iterator it = m_aControllerMap.find( nId ); + if (( nId <= 0 ) || ( it == m_aControllerMap.end() )) + return; + + uno::Reference< frame::XStatusbarController > xController( it->second ); + if ( xController.is() ) + { + css::awt::MouseEvent aMouseEvent; + aMouseEvent.Buttons = rMEvt.GetButtons(); + aMouseEvent.X = rMEvt.GetPosPixel().X(); + aMouseEvent.Y = rMEvt.GetPosPixel().Y(); + aMouseEvent.ClickCount = rMEvt.GetClicks(); + (xController.get()->*_pMethod)( aMouseEvent); + } +} + +void StatusBarManager::MouseButtonDown( const MouseEvent& rMEvt ) +{ + MouseButton(rMEvt,&frame::XStatusbarController::mouseButtonDown); +} + +void StatusBarManager::MouseButtonUp( const MouseEvent& rMEvt ) +{ + MouseButton(rMEvt,&frame::XStatusbarController::mouseButtonUp); +} + +IMPL_LINK_NOARG(StatusBarManager, Click, StatusBar*, void) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + sal_uInt16 nId = m_pStatusBar->GetCurItemId(); + StatusBarControllerMap::const_iterator it = m_aControllerMap.find( nId ); + if (( nId > 0 ) && ( it != m_aControllerMap.end() )) + { + uno::Reference< frame::XStatusbarController > xController( it->second ); + if ( xController.is() ) + { + const Point aVCLPos = m_pStatusBar->GetPointerPosPixel(); + const awt::Point aAWTPoint( aVCLPos.X(), aVCLPos.Y() ); + xController->click( aAWTPoint ); + } + } +} + +IMPL_LINK_NOARG(StatusBarManager, DoubleClick, StatusBar*, void) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + sal_uInt16 nId = m_pStatusBar->GetCurItemId(); + StatusBarControllerMap::const_iterator it = m_aControllerMap.find( nId ); + if (( nId > 0 ) && ( it != m_aControllerMap.end() )) + { + uno::Reference< frame::XStatusbarController > xController( it->second ); + if ( xController.is() ) + { + const Point aVCLPos = m_pStatusBar->GetPointerPosPixel(); + const awt::Point aAWTPoint( aVCLPos.X(), aVCLPos.Y() ); + xController->doubleClick( aAWTPoint ); + } + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/statusbarmerger.cxx b/framework/source/uielement/statusbarmerger.cxx new file mode 100644 index 000000000..c8e6633be --- /dev/null +++ b/framework/source/uielement/statusbarmerger.cxx @@ -0,0 +1,237 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include <uielement/statusbarmerger.hxx> +#include <o3tl/string_view.hxx> + +using com::sun::star::beans::PropertyValue; +using com::sun::star::uno::Sequence; + +namespace framework +{ +namespace { + +const char16_t MERGECOMMAND_ADDAFTER[] = u"AddAfter"; +const char16_t MERGECOMMAND_ADDBEFORE[] = u"AddBefore"; +const char16_t MERGECOMMAND_REPLACE[] = u"Replace"; +const char16_t MERGECOMMAND_REMOVE[] = u"Remove"; + +void lcl_ConvertSequenceToValues( + const Sequence< PropertyValue > &rSequence, + AddonStatusbarItem &rItem ) +{ + OUString sAlignment; + bool bAutoSize = false; + bool bOwnerDraw = false; + bool bMandatory = true; + + for ( PropertyValue const & aPropVal : rSequence ) + { + if ( aPropVal.Name == "URL" ) + aPropVal.Value >>= rItem.aCommandURL; + else if ( aPropVal.Name == "Title" ) + aPropVal.Value >>= rItem.aLabel; + else if ( aPropVal.Name == "Context" ) + aPropVal.Value >>= rItem.aContext; + else if ( aPropVal.Name == "Alignment" ) + aPropVal.Value >>= sAlignment; + else if ( aPropVal.Name == "AutoSize" ) + aPropVal.Value >>= bAutoSize; + else if ( aPropVal.Name == "OwnerDraw" ) + aPropVal.Value >>= bOwnerDraw; + else if ( aPropVal.Name == "Mandatory" ) + aPropVal.Value >>= bMandatory; + else if ( aPropVal.Name == "Width" ) + { + sal_Int32 aWidth = 0; + aPropVal.Value >>= aWidth; + rItem.nWidth = sal_uInt16( aWidth ); + } + } + + StatusBarItemBits nItemBits(StatusBarItemBits::NONE); + if ( bAutoSize ) + nItemBits |= StatusBarItemBits::AutoSize; + if ( bOwnerDraw ) + nItemBits |= StatusBarItemBits::UserDraw; + if ( bMandatory ) + nItemBits |= StatusBarItemBits::Mandatory; + if ( sAlignment == "center" ) + nItemBits |= StatusBarItemBits::Center; + else if ( sAlignment == "right" ) + nItemBits |= StatusBarItemBits::Right; + else + // if unset, defaults to left alignment + nItemBits |= StatusBarItemBits::Left; + rItem.nItemBits = nItemBits; +} + +void lcl_CreateStatusbarItem( StatusBar* pStatusbar, + sal_uInt16 nPos, + sal_uInt16 nItemId, + const AddonStatusbarItem& rAddonItem ) +{ + pStatusbar->InsertItem( nItemId, + rAddonItem.nWidth, + rAddonItem.nItemBits, + STATUSBAR_OFFSET, + nPos ); + pStatusbar->SetItemCommand( nItemId, rAddonItem.aCommandURL ); + pStatusbar->SetQuickHelpText( nItemId, rAddonItem.aLabel ); + pStatusbar->SetAccessibleName( nItemId, rAddonItem.aLabel ); + + // add-on specific data + AddonStatusbarItemData *pUserData = new AddonStatusbarItemData; + pUserData->aLabel = rAddonItem.aLabel; + pStatusbar->SetItemData( nItemId, pUserData ); +} + +bool lcl_MergeItems( StatusBar* pStatusbar, + sal_uInt16 nPos, + sal_uInt16 nModIndex, + sal_uInt16& rItemId, + const AddonStatusbarItemContainer& rAddonItems ) +{ + const sal_uInt16 nSize( rAddonItems.size() ); + for ( sal_Int32 i = 0; i < nSize; i++ ) + { + const AddonStatusbarItem& rItem = rAddonItems[i]; + if ( !StatusbarMerger::IsCorrectContext( rItem.aContext ) ) + continue; + + sal_uInt16 nInsPos = nPos + nModIndex + i; + if ( nInsPos > pStatusbar->GetItemCount() ) + nInsPos = STATUSBAR_APPEND; + + lcl_CreateStatusbarItem( pStatusbar, nInsPos, rItemId, rItem ); + ++rItemId; + } + + return true; +} + +bool lcl_ReplaceItem( StatusBar* pStatusbar, + sal_uInt16 nPos, + sal_uInt16& rItemId, + const AddonStatusbarItemContainer& rAddonToolbarItems ) +{ + pStatusbar->RemoveItem( pStatusbar->GetItemId( nPos ) ); + return lcl_MergeItems( pStatusbar, nPos, 0, rItemId, rAddonToolbarItems ); +} + +bool lcl_RemoveItems( StatusBar* pStatusbar, + sal_uInt16 nPos, + std::u16string_view rMergeCommandParameter ) +{ + sal_Int32 nCount = o3tl::toInt32(rMergeCommandParameter); + if ( nCount > 0 ) + { + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + if ( nPos < pStatusbar->GetItemCount() ) + pStatusbar->RemoveItem( nPos ); + } + } + return true; +} + +} + +bool StatusbarMerger::IsCorrectContext( + std::u16string_view rContext ) +{ + return rContext.empty(); +} + +bool StatusbarMerger::ConvertSeqSeqToVector( + const Sequence< Sequence< PropertyValue > > &rSequence, + AddonStatusbarItemContainer& rContainer ) +{ + for ( auto const & i : rSequence ) + { + AddonStatusbarItem aStatusBarItem; + lcl_ConvertSequenceToValues( i, aStatusBarItem ); + rContainer.push_back( aStatusBarItem ); + } + + return true; +} + +sal_uInt16 StatusbarMerger::FindReferencePos( + StatusBar* pStatusbar, + std::u16string_view rReferencePoint ) +{ + for ( sal_uInt16 nPos = 0; nPos < pStatusbar->GetItemCount(); nPos++ ) + { + const OUString rCmd = pStatusbar->GetItemCommand( pStatusbar->GetItemId( nPos ) ); + if ( rReferencePoint == rCmd ) + return nPos; + } + + return STATUSBAR_ITEM_NOTFOUND; +} + +bool StatusbarMerger::ProcessMergeOperation( + StatusBar* pStatusbar, + sal_uInt16 nPos, + sal_uInt16& rItemId, + std::u16string_view rMergeCommand, + std::u16string_view rMergeCommandParameter, + const AddonStatusbarItemContainer& rItems ) +{ + if ( rMergeCommand == MERGECOMMAND_ADDAFTER ) + return lcl_MergeItems( pStatusbar, nPos, 1, rItemId, rItems ); + else if ( rMergeCommand == MERGECOMMAND_ADDBEFORE ) + return lcl_MergeItems( pStatusbar, nPos, 0, rItemId, rItems ); + else if ( rMergeCommand == MERGECOMMAND_REPLACE ) + return lcl_ReplaceItem( pStatusbar, nPos, rItemId, rItems ); + else if ( rMergeCommand == MERGECOMMAND_REMOVE ) + return lcl_RemoveItems( pStatusbar, nPos, rMergeCommandParameter ); + + return false; +} + +bool StatusbarMerger::ProcessMergeFallback( + StatusBar* pStatusbar, + sal_uInt16& rItemId, + std::u16string_view rMergeCommand, + std::u16string_view rMergeFallback, + const AddonStatusbarItemContainer& rItems ) +{ + // fallback IGNORE or REPLACE/REMOVE item not found + if (( rMergeFallback == u"Ignore" ) || + ( rMergeCommand == MERGECOMMAND_REPLACE ) || + ( rMergeCommand == MERGECOMMAND_REMOVE ) ) + { + return true; + } + else if (( rMergeCommand == MERGECOMMAND_ADDBEFORE ) || + ( rMergeCommand == MERGECOMMAND_ADDAFTER ) ) + { + if ( rMergeFallback == u"AddFirst" ) + return lcl_MergeItems( pStatusbar, 0, 0, rItemId, rItems ); + else if ( rMergeFallback == u"AddLast" ) + return lcl_MergeItems( pStatusbar, STATUSBAR_APPEND, 0, rItemId, rItems ); + } + + return false; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/statusbarwrapper.cxx b/framework/source/uielement/statusbarwrapper.cxx new file mode 100644 index 000000000..7627b2f0a --- /dev/null +++ b/framework/source/uielement/statusbarwrapper.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 <uielement/statusbarwrapper.hxx> + +#include <uielement/statusbar.hxx> + +#include <com/sun/star/ui/UIElementType.hpp> + +#include <toolkit/helper/vclunohelper.hxx> + +#include <tools/solar.h> +#include <utility> +#include <vcl/svapp.hxx> + +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::frame; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace com::sun::star::awt; +using namespace ::com::sun::star::ui; + +namespace framework +{ + +StatusBarWrapper::StatusBarWrapper( + css::uno::Reference< css::uno::XComponentContext > xContext + ) + : UIConfigElementWrapperBase( UIElementType::STATUSBAR ), + m_xContext(std::move( xContext )) +{ +} + +StatusBarWrapper::~StatusBarWrapper() +{ +} + +void SAL_CALL StatusBarWrapper::dispose() +{ + Reference< XComponent > xThis(this); + + css::lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + SolarMutexGuard g; + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_xStatusBarManager.is() ) + m_xStatusBarManager->dispose(); + m_xStatusBarManager.clear(); + m_xConfigSource.clear(); + m_xConfigData.clear(); + m_xContext.clear(); + + m_bDisposed = true; + +} + +// XInitialization +void SAL_CALL StatusBarWrapper::initialize( const Sequence< Any >& aArguments ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_bInitialized ) + return; + + UIConfigElementWrapperBase::initialize( aArguments ); + + Reference< XFrame > xFrame( m_xWeakFrame ); + if ( !(xFrame.is() && m_xConfigSource.is()) ) + return; + + // Create VCL based toolbar which will be filled with settings data + StatusBar* pStatusBar( nullptr ); + rtl::Reference<StatusBarManager> pStatusBarManager; + { + SolarMutexGuard aSolarMutexGuard; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xFrame->getContainerWindow() ); + if ( pWindow ) + { + sal_uLong nStyles = WinBits( WB_LEFT | WB_3DLOOK ); + + pStatusBar = VclPtr<FrameworkStatusBar>::Create( pWindow, nStyles ); + pStatusBarManager = new StatusBarManager( m_xContext, xFrame, pStatusBar ); + static_cast<FrameworkStatusBar*>(pStatusBar)->SetStatusBarManager( pStatusBarManager.get() ); + m_xStatusBarManager = pStatusBarManager; + } + } + + try + { + m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false ); + if ( m_xConfigData.is() && pStatusBar && pStatusBarManager ) + { + // Fill statusbar with container contents + pStatusBarManager->FillStatusBar( m_xConfigData ); + } + } + catch ( const NoSuchElementException& ) + { + } +} + +// XUIElementSettings +void SAL_CALL StatusBarWrapper::updateSettings() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !(m_bPersistent && + m_xConfigSource.is() && + m_xStatusBarManager.is()) ) + return; + + try + { + StatusBarManager* pStatusBarManager = static_cast< StatusBarManager *>( m_xStatusBarManager.get() ); + + m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false ); + if ( m_xConfigData.is() ) + pStatusBarManager->FillStatusBar( m_xConfigData ); + } + catch ( const NoSuchElementException& ) + { + } +} + +Reference< XInterface > SAL_CALL StatusBarWrapper::getRealInterface() +{ + SolarMutexGuard g; + + if ( m_xStatusBarManager.is() ) + { + StatusBarManager* pStatusBarManager = static_cast< StatusBarManager *>( m_xStatusBarManager.get() ); + if ( pStatusBarManager ) + { + vcl::Window* pWindow = pStatusBarManager->GetStatusBar(); + if ( pWindow ) + return Reference< XInterface >( VCLUnoHelper::GetInterface( pWindow ), UNO_QUERY ); + } + } + + return Reference< XInterface >(); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/statusindicatorinterfacewrapper.cxx b/framework/source/uielement/statusindicatorinterfacewrapper.cxx new file mode 100644 index 000000000..ba796036b --- /dev/null +++ b/framework/source/uielement/statusindicatorinterfacewrapper.cxx @@ -0,0 +1,102 @@ +/* -*- 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 <uielement/statusindicatorinterfacewrapper.hxx> +#include <uielement/progressbarwrapper.hxx> + +using namespace cppu; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; + +namespace framework +{ + +StatusIndicatorInterfaceWrapper::StatusIndicatorInterfaceWrapper( + const css::uno::Reference< css::lang::XComponent >& rStatusIndicatorImpl ) : + m_xStatusIndicatorImpl( rStatusIndicatorImpl ) +{ +} + +StatusIndicatorInterfaceWrapper::~StatusIndicatorInterfaceWrapper() +{ +} + +void SAL_CALL StatusIndicatorInterfaceWrapper::start( + const OUString& sText, + sal_Int32 nRange ) +{ + Reference< XComponent > xComp( m_xStatusIndicatorImpl ); + if ( xComp.is() ) + { + ProgressBarWrapper* pProgressBar = static_cast<ProgressBarWrapper*>(xComp.get()); + if ( pProgressBar ) + pProgressBar->start( sText, nRange ); + } +} + +void SAL_CALL StatusIndicatorInterfaceWrapper::end() +{ + Reference< XComponent > xComp( m_xStatusIndicatorImpl ); + if ( xComp.is() ) + { + ProgressBarWrapper* pProgressBar = static_cast<ProgressBarWrapper*>(xComp.get()); + if ( pProgressBar ) + pProgressBar->end(); + } +} + +void SAL_CALL StatusIndicatorInterfaceWrapper::reset() +{ + Reference< XComponent > xComp( m_xStatusIndicatorImpl ); + if ( xComp.is() ) + { + ProgressBarWrapper* pProgressBar = static_cast<ProgressBarWrapper*>(xComp.get()); + if ( pProgressBar ) + pProgressBar->reset(); + } +} + +void SAL_CALL StatusIndicatorInterfaceWrapper::setText( + const OUString& sText ) +{ + Reference< XComponent > xComp( m_xStatusIndicatorImpl ); + if ( xComp.is() ) + { + ProgressBarWrapper* pProgressBar = static_cast<ProgressBarWrapper*>(xComp.get()); + if ( pProgressBar ) + pProgressBar->setText( sText ); + } +} + +void SAL_CALL StatusIndicatorInterfaceWrapper::setValue( + sal_Int32 nValue ) +{ + Reference< XComponent > xComp( m_xStatusIndicatorImpl ); + if ( xComp.is() ) + { + ProgressBarWrapper* pProgressBar = static_cast<ProgressBarWrapper*>(xComp.get()); + if ( pProgressBar ) + pProgressBar->setValue( nValue ); + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/styletoolbarcontroller.cxx b/framework/source/uielement/styletoolbarcontroller.cxx new file mode 100644 index 000000000..31aed5336 --- /dev/null +++ b/framework/source/uielement/styletoolbarcontroller.cxx @@ -0,0 +1,243 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#include <uielement/styletoolbarcontroller.hxx> + +#include <tools/urlobj.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> +#include <sal/log.hxx> +#include <o3tl/string_view.hxx> + +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/frame/status/Template.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> + +namespace { + +OUString MapFamilyToCommand( std::u16string_view rFamily ) +{ + if ( rFamily == u"ParagraphStyles" || + rFamily == u"CellStyles" || // In sc + rFamily == u"graphics" ) // In sd + return ".uno:ParaStyle"; + else if ( rFamily == u"CharacterStyles" ) + return ".uno:CharStyle"; + else if ( rFamily == u"PageStyles" ) + return ".uno:PageStyle"; + else if ( rFamily == u"FrameStyles" ) + return ".uno:FrameStyle"; + else if ( rFamily == u"NumberingStyles" ) + return ".uno:ListStyle"; + else if ( rFamily == u"TableStyles" ) + return ".uno:TableStyle"; + + return OUString(); +} + +OUString GetDisplayFromInternalName( const css::uno::Reference< css::frame::XFrame >& rFrame, + const OUString& rStyleName, + const OUString& rFamilyName ) +{ + try + { + css::uno::Reference< css::frame::XController > xController( + rFrame->getController(), css::uno::UNO_SET_THROW ); + css::uno::Reference< css::style::XStyleFamiliesSupplier > xStylesSupplier( + xController->getModel(), css::uno::UNO_QUERY_THROW ); + css::uno::Reference< css::container::XNameAccess > xFamilies( + xStylesSupplier->getStyleFamilies(), css::uno::UNO_SET_THROW ); + + css::uno::Reference< css::container::XNameAccess > xStyleSet; + xFamilies->getByName( rFamilyName ) >>= xStyleSet; + css::uno::Reference< css::beans::XPropertySet > xStyle; + xStyleSet->getByName( rStyleName ) >>= xStyle; + + OUString aDisplayName; + if ( xStyle.is() ) + xStyle->getPropertyValue( "DisplayName" ) >>= aDisplayName; + return aDisplayName; + } + catch ( const css::uno::Exception& ) + { + // We couldn't get the display name. As a last resort we'll + // try to use the internal name, as was specified in the URL. + } + + return rStyleName; +} + +} + +namespace framework { + +StyleDispatcher::StyleDispatcher( const css::uno::Reference< css::frame::XFrame >& rFrame, + css::uno::Reference< css::util::XURLTransformer > xUrlTransformer, + const css::util::URL& rURL ) + : m_aCommand( rURL.Complete ) + , m_xUrlTransformer(std::move( xUrlTransformer )) + , m_xFrame( rFrame, css::uno::UNO_QUERY ) +{ + SAL_WARN_IF( !m_aCommand.startsWith( ".uno:StyleApply?" ), "fwk.uielement", "Wrong dispatcher!" ); + + OUString aParams = rURL.Arguments; + OUString aStyleName, aFamilyName; + sal_Int32 nIndex = 0; + do + { + std::u16string_view aParam = o3tl::getToken(aParams, 0, '&', nIndex ); + + sal_Int32 nParamIndex = 0; + std::u16string_view aParamName = o3tl::getToken(aParam, 0, '=', nParamIndex ); + if ( nParamIndex < 0 ) + break; + + if ( aParamName == u"Style:string" ) + { + std::u16string_view aValue = o3tl::getToken(aParam, 0, '=', nParamIndex ); + aStyleName = INetURLObject::decode( aValue, INetURLObject::DecodeMechanism::WithCharset ); + } + else if ( aParamName == u"FamilyName:string" ) + { + aFamilyName = o3tl::getToken(aParam, 0, '=', nParamIndex ); + } + + } while ( nIndex >= 0 ); + + m_aStatusCommand = MapFamilyToCommand( aFamilyName ); + if ( m_aStatusCommand.isEmpty() || aStyleName.isEmpty() ) + { + // We can't provide status updates for this command, but just executing + // the command should still work (given that the command is valid). + SAL_WARN( "fwk.uielement", "Unable to parse as a style command: " << m_aCommand ); + return; + } + + m_aStyleName = GetDisplayFromInternalName( rFrame, aStyleName, aFamilyName ); + if ( m_xFrame.is() ) + { + css::util::URL aStatusURL; + aStatusURL.Complete = m_aStatusCommand; + m_xUrlTransformer->parseStrict( aStatusURL ); + m_xStatusDispatch = m_xFrame->queryDispatch( aStatusURL, OUString(), 0 ); + } +} + +void StyleDispatcher::dispatch( const css::util::URL& rURL, + const css::uno::Sequence< css::beans::PropertyValue >& rArguments ) +{ + if ( !m_xFrame.is() ) + return; + + css::uno::Reference< css::frame::XDispatch > xDispatch( m_xFrame->queryDispatch( rURL, OUString(), 0 ) ); + if ( xDispatch.is() ) + xDispatch->dispatch( rURL, rArguments ); +} + +void StyleDispatcher::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& rListener, + const css::util::URL& /*rURL*/ ) +{ + if ( m_xStatusDispatch.is() ) + { + if ( !m_xOwner.is() ) + m_xOwner.set( rListener ); + + css::util::URL aStatusURL; + aStatusURL.Complete = m_aStatusCommand; + m_xUrlTransformer->parseStrict( aStatusURL ); + m_xStatusDispatch->addStatusListener( this, aStatusURL ); + } +} + +void StyleDispatcher::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*rListener*/, + const css::util::URL& /*rURL*/ ) +{ + if ( m_xStatusDispatch.is() ) + { + css::util::URL aStatusURL; + aStatusURL.Complete = m_aStatusCommand; + m_xUrlTransformer->parseStrict( aStatusURL ); + m_xStatusDispatch->removeStatusListener( this, aStatusURL ); + } +} + +void StyleDispatcher::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + css::frame::status::Template aTemplate; + rEvent.State >>= aTemplate; + + css::frame::FeatureStateEvent aEvent; + aEvent.FeatureURL.Complete = m_aCommand; + m_xUrlTransformer->parseStrict( aEvent.FeatureURL ); + + aEvent.IsEnabled = rEvent.IsEnabled; + aEvent.Requery = rEvent.Requery; + aEvent.State <<= m_aStyleName == aTemplate.StyleName; + m_xOwner->statusChanged( aEvent ); +} + +void StyleDispatcher::disposing( const css::lang::EventObject& /*rSource*/ ) +{ + m_xStatusDispatch.clear(); +} + +StyleToolbarController::StyleToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rContext, + const css::uno::Reference< css::frame::XFrame >& rFrame, + const OUString& rCommand ) + : ToolboxController( rContext, rFrame, rCommand ) +{ +} + +void StyleToolbarController::update() +{ + if ( m_bDisposed ) + throw css::lang::DisposedException(); + + css::util::URL aURL; + aURL.Complete = m_aCommandURL; + m_xUrlTransformer->parseStrict( aURL ); + + auto& xDispatcher = m_aListenerMap[m_aCommandURL]; + if ( xDispatcher.is() ) + xDispatcher->removeStatusListener( this, aURL ); + + xDispatcher.set( new StyleDispatcher( m_xFrame, m_xUrlTransformer, aURL ) ); + xDispatcher->addStatusListener( this, aURL ); +} + +void StyleToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + SolarMutexGuard aGuard; + + if ( m_bDisposed ) + throw css::lang::DisposedException(); + + ToolBox* pToolBox = nullptr; + ToolBoxItemId nItemId; + if ( getToolboxId( nItemId, &pToolBox ) ) + { + bool bChecked = false; + rEvent.State >>= bChecked; + pToolBox->CheckItem( nItemId, bChecked ); + pToolBox->EnableItem( nItemId, rEvent.IsEnabled ); + } +} + +void StyleToolbarController::dispose() +{ + ToolboxController::dispose(); + m_aListenerMap.clear(); // Break the cycle with StyleDispatcher. +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/framework/source/uielement/subtoolbarcontroller.cxx b/framework/source/uielement/subtoolbarcontroller.cxx new file mode 100644 index 000000000..de345f545 --- /dev/null +++ b/framework/source/uielement/subtoolbarcontroller.cxx @@ -0,0 +1,547 @@ +/* -*- 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 <comphelper/propertysequence.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weakref.hxx> +#include <svtools/popupwindowcontroller.hxx> +#include <svtools/toolbarmenu.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/gen.hxx> +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <vcl/weldutils.hxx> + +#include <com/sun/star/awt/XDockableWindow.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/frame/XSubToolbarController.hpp> +#include <com/sun/star/frame/status/Visibility.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/ui/theUIElementFactoryManager.hpp> +#include <com/sun/star/container/NoSuchElementException.hpp> + +typedef cppu::ImplInheritanceHelper< svt::PopupWindowController, + css::frame::XSubToolbarController, + css::awt::XDockableWindowListener> ToolBarBase; + +namespace { + +class SubToolBarController : public ToolBarBase +{ + OUString m_aSubTbName; + OUString m_aLastCommand; + css::uno::Reference< css::ui::XUIElement > m_xUIElement; + void disposeUIElement(); +public: + explicit SubToolBarController( const rtl::Reference< com::sun::star::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::uno::Any >& rxArgs ); + virtual ~SubToolBarController() override; + + void PopoverDestroyed(); + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& rxArgs ) override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + // XToolbarController + virtual void SAL_CALL execute( sal_Int16 nKeyModifier ) override; + + // PopupWindowController + virtual VclPtr<vcl::Window> createVclPopupWindow(vcl::Window* pParent) override; + virtual std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override; + + // XSubToolbarController + virtual sal_Bool SAL_CALL opensSubToolbar() override; + virtual OUString SAL_CALL getSubToolbarName() override; + virtual void SAL_CALL functionSelected( const OUString& rCommand ) override; + virtual void SAL_CALL updateImage() override; + + // XDockableWindowListener + virtual void SAL_CALL startDocking( const css::awt::DockingEvent& e ) override; + virtual css::awt::DockingData SAL_CALL docking( const css::awt::DockingEvent& e ) override; + virtual void SAL_CALL endDocking( const css::awt::EndDockingEvent& e ) override; + virtual sal_Bool SAL_CALL prepareToggleFloatingMode( const css::lang::EventObject& e ) override; + virtual void SAL_CALL toggleFloatingMode( const css::lang::EventObject& e ) override; + virtual void SAL_CALL closed( const css::lang::EventObject& e ) override; + virtual void SAL_CALL endPopupMode( const css::awt::EndPopupModeEvent& e ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& e ) override; + + // XComponent + virtual void SAL_CALL dispose() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +} + +SubToolBarController::SubToolBarController( + const rtl::Reference< com::sun::star::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::uno::Any >& rxArgs +) : ToolBarBase( + rxContext, + rtl::Reference< css::frame::XFrame >(), + "" + ) +{ + for ( css::uno::Any const & arg : rxArgs ) + { + css::beans::PropertyValue aPropValue; + arg >>= aPropValue; + if ( aPropValue.Name == "Value" ) + { + sal_Int32 nIdx{ 0 }; + OUString aValue; + aPropValue.Value >>= aValue; + m_aSubTbName = aValue.getToken(0, ';', nIdx); + m_aCommandURL = m_aSubTbName; + m_aLastCommand = aValue.getToken(0, ';', nIdx); + break; + } + } + if ( !m_aLastCommand.isEmpty() ) + addStatusListener( m_aLastCommand ); +} + +SubToolBarController::~SubToolBarController() +{ + disposeUIElement(); + m_xUIElement = nullptr; +} + +void SubToolBarController::disposeUIElement() +{ + if ( m_xUIElement.is() ) + { + css::uno::Reference< css::lang::XComponent > xComponent( m_xUIElement, css::uno::UNO_QUERY ); + xComponent->dispose(); + } +} + +void SubToolBarController::statusChanged( const css::frame::FeatureStateEvent& Event ) +{ + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + return; + + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( !getToolboxId( nId, &pToolBox ) ) + return; + + ToolBoxItemBits nItemBits = pToolBox->GetItemBits( nId ); + nItemBits &= ~ToolBoxItemBits::CHECKABLE; + TriState eTri = TRISTATE_FALSE; + + if ( Event.FeatureURL.Complete == m_aCommandURL ) + { + pToolBox->EnableItem( nId, Event.IsEnabled ); + + OUString aStrValue; + css::frame::status::Visibility aItemVisibility; + if ( Event.State >>= aStrValue ) + { + // Enum command, such as the current custom shape, + // toggle checked state. + if ( m_aLastCommand == OUStringConcatenation( m_aCommandURL + "." + aStrValue ) ) + { + eTri = TRISTATE_TRUE; + nItemBits |= ToolBoxItemBits::CHECKABLE; + } + } + else if ( Event.State >>= aItemVisibility ) + { + pToolBox->ShowItem( nId, aItemVisibility.bVisible ); + } + } + else + { + bool bValue; + if ( Event.State >>= bValue ) + { + // Boolean, treat it as checked/unchecked + if ( bValue ) + eTri = TRISTATE_TRUE; + nItemBits |= ToolBoxItemBits::CHECKABLE; + } + } + + pToolBox->SetItemState( nId, eTri ); + pToolBox->SetItemBits( nId, nItemBits ); +} + +void SubToolBarController::execute( sal_Int16 nKeyModifier ) +{ + if ( !m_aLastCommand.isEmpty() ) + { + auto aArgs( comphelper::InitPropertySequence( { + { "KeyModifier", css::uno::Any( nKeyModifier ) } + } ) ); + dispatchCommand( m_aLastCommand, aArgs ); + } +} + +namespace { +class SubToolbarControl final : public WeldToolbarPopup +{ +public: + explicit SubToolbarControl(SubToolBarController& rController, weld::Widget* pParent); + virtual ~SubToolbarControl() override; + + virtual void GrabFocus() override; + + weld::Container* GetContainer() { return m_xTargetContainer.get(); } + +private: + SubToolBarController& m_rController; + std::unique_ptr<weld::Container> m_xTargetContainer; +}; +} + +SubToolbarControl::SubToolbarControl(SubToolBarController& rController, + weld::Widget* pParent) + : WeldToolbarPopup(rController.getFrameInterface(), pParent, "svt/ui/subtoolbar.ui", "subtoolbar") + , m_rController(rController) + , m_xTargetContainer(m_xBuilder->weld_container("container")) +{ +} + +void SubToolbarControl::GrabFocus() +{ + // TODO +} + +SubToolbarControl::~SubToolbarControl() +{ + m_rController.PopoverDestroyed(); +} + +std::unique_ptr<WeldToolbarPopup> SubToolBarController::weldPopupWindow() +{ + SolarMutexGuard aGuard; + + auto pPopup = std::make_unique<SubToolbarControl>(*this, m_pToolbar); + + css::uno::Reference< css::frame::XFrame > xFrame ( getFrameInterface() ); + + // create element with factory + static css::uno::WeakReference< css::ui::XUIElementFactoryManager > xWeakUIElementFactory; + css::uno::Reference< css::ui::XUIElementFactoryManager > xUIElementFactory = xWeakUIElementFactory; + if ( !xUIElementFactory.is() ) + { + xUIElementFactory = css::ui::theUIElementFactoryManager::get( m_xContext ); + xWeakUIElementFactory = xUIElementFactory; + } + + css::uno::Reference< css::awt::XWindow > xParent = new weld::TransportAsXWindow(pPopup->GetContainer()); + + auto aPropSeq( comphelper::InitPropertySequence( { + { "Frame", css::uno::Any( xFrame ) }, + { "ParentWindow", css::uno::Any( xParent ) }, + { "Persistent", css::uno::Any( false ) }, + { "PopupMode", css::uno::Any( true ) } + } ) ); + + try + { + m_xUIElement = xUIElementFactory->createUIElement( "private:resource/toolbar/" + m_aSubTbName, aPropSeq ); + } + catch ( css::container::NoSuchElementException& ) + {} + catch ( css::lang::IllegalArgumentException& ) + {} + + return pPopup; +} + +VclPtr<vcl::Window> SubToolBarController::createVclPopupWindow(vcl::Window* /*pParent*/) +{ + SolarMutexGuard aGuard; + + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( getToolboxId( nId, &pToolBox ) ) + { + css::uno::Reference< css::frame::XFrame > xFrame ( getFrameInterface() ); + + // create element with factory + static css::uno::WeakReference< css::ui::XUIElementFactoryManager > xWeakUIElementFactory; + css::uno::Reference< css::ui::XUIElement > xUIElement; + css::uno::Reference< css::ui::XUIElementFactoryManager > xUIElementFactory = xWeakUIElementFactory; + if ( !xUIElementFactory.is() ) + { + xUIElementFactory = css::ui::theUIElementFactoryManager::get( m_xContext ); + xWeakUIElementFactory = xUIElementFactory; + } + + auto aPropSeq( comphelper::InitPropertySequence( { + { "Frame", css::uno::Any( xFrame ) }, + { "ParentWindow", css::uno::Any( m_xParentWindow ) }, + { "Persistent", css::uno::Any( false ) }, + { "PopupMode", css::uno::Any( true ) } + } ) ); + + try + { + xUIElement = xUIElementFactory->createUIElement( "private:resource/toolbar/" + m_aSubTbName, aPropSeq ); + } + catch ( css::container::NoSuchElementException& ) + {} + catch ( css::lang::IllegalArgumentException& ) + {} + + if ( xUIElement.is() ) + { + css::uno::Reference< css::awt::XWindow > xSubToolBar( xUIElement->getRealInterface(), css::uno::UNO_QUERY ); + if ( xSubToolBar.is() ) + { + css::uno::Reference< css::awt::XDockableWindow > xDockWindow( xSubToolBar, css::uno::UNO_QUERY ); + xDockWindow->addDockableWindowListener( css::uno::Reference< css::awt::XDockableWindowListener >(this) ); + xDockWindow->enableDocking( true ); + + // keep reference to UIElement to avoid its destruction + disposeUIElement(); + m_xUIElement = xUIElement; + + VclPtr<vcl::Window> pTbxWindow = VCLUnoHelper::GetWindow( xSubToolBar ); + if ( pTbxWindow && pTbxWindow->GetType() == WindowType::TOOLBOX ) + { + ToolBox* pToolBar = static_cast< ToolBox* >( pTbxWindow.get() ); + // calc and set size for popup mode + Size aSize = pToolBar->CalcPopupWindowSizePixel(); + pToolBar->SetSizePixel( aSize ); + // open subtoolbox in popup mode + vcl::Window::GetDockingManager()->StartPopupMode( pToolBox, pToolBar ); + } + } + } + } + return nullptr; +} + +sal_Bool SubToolBarController::opensSubToolbar() +{ + return !m_aLastCommand.isEmpty(); +} + +OUString SubToolBarController::getSubToolbarName() +{ + return m_aSubTbName; +} + +void SubToolBarController::functionSelected( const OUString& rCommand ) +{ + if ( !m_aLastCommand.isEmpty() && m_aLastCommand != rCommand ) + { + removeStatusListener( m_aLastCommand ); + m_aLastCommand = rCommand; + addStatusListener( m_aLastCommand ); + updateImage(); + } +} + +void SubToolBarController::updateImage() +{ + SolarMutexGuard aGuard; + if ( !m_aLastCommand.isEmpty() ) + { + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( getToolboxId( nId, &pToolBox ) ) + { + vcl::ImageType eImageType = pToolBox->GetImageSize(); + Image aImage = vcl::CommandInfoProvider::GetImageForCommand(m_aLastCommand, getFrameInterface(), eImageType); + if ( !!aImage ) + pToolBox->SetItemImage( nId, aImage ); + } + } +} + +void SubToolBarController::startDocking( const css::awt::DockingEvent& ) +{ +} + +css::awt::DockingData SubToolBarController::docking( const css::awt::DockingEvent& ) +{ + return css::awt::DockingData(); +} + +void SubToolBarController::endDocking( const css::awt::EndDockingEvent& ) +{ +} + +sal_Bool SubToolBarController::prepareToggleFloatingMode( const css::lang::EventObject& ) +{ + return false; +} + +void SubToolBarController::toggleFloatingMode( const css::lang::EventObject& ) +{ +} + +void SubToolBarController::closed( const css::lang::EventObject& ) +{ +} + +void SubToolBarController::endPopupMode( const css::awt::EndPopupModeEvent& e ) +{ + SolarMutexGuard aGuard; + + OUString aSubToolBarResName; + if ( m_xUIElement.is() ) + { + css::uno::Reference< css::beans::XPropertySet > xPropSet( m_xUIElement, css::uno::UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + xPropSet->getPropertyValue("ResourceURL") >>= aSubToolBarResName; + } + catch ( css::beans::UnknownPropertyException& ) + {} + catch ( css::lang::WrappedTargetException& ) + {} + } + disposeUIElement(); + } + m_xUIElement = nullptr; + + // if the toolbar was teared-off recreate it and place it at the given position + if( !e.bTearoff ) + return; + + css::uno::Reference< css::ui::XUIElement > xUIElement; + css::uno::Reference< css::frame::XLayoutManager > xLayoutManager = getLayoutManager(); + + if ( !xLayoutManager.is() ) + return; + + xLayoutManager->createElement( aSubToolBarResName ); + xUIElement = xLayoutManager->getElement( aSubToolBarResName ); + if ( !xUIElement.is() ) + return; + + css::uno::Reference< css::awt::XWindow > xSubToolBar( xUIElement->getRealInterface(), css::uno::UNO_QUERY ); + css::uno::Reference< css::beans::XPropertySet > xProp( xUIElement, css::uno::UNO_QUERY ); + if ( !(xSubToolBar.is() && xProp.is()) ) + return; + + try + { + VclPtr<vcl::Window> pTbxWindow = VCLUnoHelper::GetWindow( xSubToolBar ); + if ( pTbxWindow && pTbxWindow->GetType() == WindowType::TOOLBOX ) + { + OUString aPersistentString( "Persistent" ); + css::uno::Any a = xProp->getPropertyValue( aPersistentString ); + xProp->setPropertyValue( aPersistentString, css::uno::Any( false ) ); + + xLayoutManager->hideElement( aSubToolBarResName ); + xLayoutManager->floatWindow( aSubToolBarResName ); + + xLayoutManager->setElementPos( aSubToolBarResName, e.FloatingPosition ); + xLayoutManager->showElement( aSubToolBarResName ); + + xProp->setPropertyValue("Persistent", a ); + } + } + catch ( css::uno::RuntimeException& ) + { + throw; + } + catch ( css::uno::Exception& ) + {} +} + +void SubToolBarController::disposing( const css::lang::EventObject& e ) +{ + svt::ToolboxController::disposing( e ); +} + +void SubToolBarController::initialize( const css::uno::Sequence< css::uno::Any >& rxArgs ) +{ + svt::PopupWindowController::initialize( rxArgs ); + + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( getToolboxId( nId, &pToolBox ) ) + { + if ( m_aLastCommand.isEmpty() ) + pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY ); + else + pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWN ); + } + + if (m_pToolbar) + { + mxPopoverContainer.reset(new ToolbarPopupContainer(m_pToolbar)); + m_pToolbar->set_item_popover(m_aCommandURL.toUtf8(), mxPopoverContainer->getTopLevel()); + } + + updateImage(); +} + +void SubToolBarController::PopoverDestroyed() +{ + disposeUIElement(); + m_xUIElement = nullptr; +} + +void SubToolBarController::dispose() +{ + if ( m_bDisposed ) + return; + + svt::PopupWindowController::dispose(); + disposeUIElement(); + m_xUIElement = nullptr; +} + +OUString SubToolBarController::getImplementationName() +{ + return "com.sun.star.comp.framework.SubToolBarController"; +} + +sal_Bool SubToolBarController::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService( this, rServiceName ); +} + +css::uno::Sequence< OUString > SubToolBarController::getSupportedServiceNames() +{ + return {"com.sun.star.frame.ToolbarController"}; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_SubToolBarController_get_implementation( + css::uno::XComponentContext* rxContext, + css::uno::Sequence<css::uno::Any> const & rxArgs ) +{ + return cppu::acquire( new SubToolBarController( rxContext, rxArgs ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/thesaurusmenucontroller.cxx b/framework/source/uielement/thesaurusmenucontroller.cxx new file mode 100644 index 000000000..fb7f3175a --- /dev/null +++ b/framework/source/uielement/thesaurusmenucontroller.cxx @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <sal/log.hxx> +#include <svl/lngmisc.hxx> +#include <svtools/popupmenucontrollerbase.hxx> +#include <tools/diagnose_ex.h> +#include <unotools/lingucfg.hxx> +#include <vcl/commandinfoprovider.hxx> + +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/linguistic2/LinguServiceManager.hpp> + +namespace { + +class ThesaurusMenuController : public svt::PopupMenuControllerBase +{ +public: + explicit ThesaurusMenuController( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +private: + void fillPopupMenu(); + void getMeanings( std::vector< OUString >& rSynonyms, const OUString& rWord, const css::lang::Locale& rLocale, size_t nMaxSynonms ); + OUString getThesImplName( const css::lang::Locale& rLocale ) const; + css::uno::Reference< css::linguistic2::XLinguServiceManager2 > m_xLinguServiceManager; + css::uno::Reference< css::linguistic2::XThesaurus > m_xThesaurus; + OUString m_aLastWord; +}; + +} + +ThesaurusMenuController::ThesaurusMenuController( const css::uno::Reference< css::uno::XComponentContext >& rxContext ) : + svt::PopupMenuControllerBase( rxContext ), + m_xLinguServiceManager( css::linguistic2::LinguServiceManager::create( rxContext ) ), + m_xThesaurus( m_xLinguServiceManager->getThesaurus() ) +{ +} + +void ThesaurusMenuController::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + rEvent.State >>= m_aLastWord; + m_xPopupMenu->clear(); + if ( rEvent.IsEnabled ) + fillPopupMenu(); +} + +void ThesaurusMenuController::fillPopupMenu() +{ + sal_Int32 nIdx{ 0 }; + OUString aText = m_aLastWord.getToken(0, '#', nIdx); + OUString aIsoLang = m_aLastWord.getToken(0, '#', nIdx); + if ( aText.isEmpty() || aIsoLang.isEmpty() ) + return; + + std::vector< OUString > aSynonyms; + css::lang::Locale aLocale = LanguageTag::convertToLocale( aIsoLang ); + getMeanings( aSynonyms, aText, aLocale, 7 /*max number of synonyms to retrieve*/ ); + + m_xPopupMenu->enableAutoMnemonics(false); + if ( aSynonyms.empty() ) + return; + + SvtLinguConfig aCfg; + css::uno::Reference<css::graphic::XGraphic> xGraphic; + OUString aThesImplName( getThesImplName( aLocale ) ); + OUString aSynonymsImageUrl( aCfg.GetSynonymsContextImage( aThesImplName ) ); + if (!aThesImplName.isEmpty() && !aSynonymsImageUrl.isEmpty()) + { + try + { + css::uno::Reference<css::uno::XComponentContext> xContext(::comphelper::getProcessComponentContext()); + css::uno::Reference<css::graphic::XGraphicProvider> xProvider(css::graphic::GraphicProvider::create(xContext)); + xGraphic = xProvider->queryGraphic({ comphelper::makePropertyValue("URL", aSynonymsImageUrl) }); + } + catch (const css::uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("fwk"); + } + } + + sal_uInt16 nId = 1; + for ( const auto& aSynonym : aSynonyms ) + { + OUString aItemText( linguistic::GetThesaurusReplaceText( aSynonym ) ); + m_xPopupMenu->insertItem(nId, aItemText, 0, -1); + m_xPopupMenu->setCommand(nId, ".uno:ThesaurusFromContext?WordReplace:string=" + aItemText); + + if (xGraphic.is()) + m_xPopupMenu->setItemImage(nId, xGraphic, false); + + nId++; + } + + m_xPopupMenu->insertSeparator(-1); + OUString aThesaurusDialogCmd( ".uno:ThesaurusDialog" ); + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aThesaurusDialogCmd, m_aModuleName); + m_xPopupMenu->insertItem(nId, vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties), 0, -1); + m_xPopupMenu->setCommand(nId, aThesaurusDialogCmd); +} + +void ThesaurusMenuController::getMeanings( std::vector< OUString >& rSynonyms, const OUString& rWord, + const css::lang::Locale& rLocale, size_t nMaxSynonms ) +{ + rSynonyms.clear(); + if ( !(m_xThesaurus.is() && m_xThesaurus->hasLocale( rLocale ) && !rWord.isEmpty() && nMaxSynonms > 0) ) + return; + + try + { + const css::uno::Sequence< css::uno::Reference< css::linguistic2::XMeaning > > aMeaningSeq( + m_xThesaurus->queryMeanings( rWord, rLocale, css::uno::Sequence< css::beans::PropertyValue >() ) ); + + for ( const auto& xMeaning : aMeaningSeq ) + { + const css::uno::Sequence< OUString > aSynonymSeq( xMeaning->querySynonyms() ); + for ( const auto& aSynonym : aSynonymSeq ) + { + rSynonyms.push_back( aSynonym ); + if ( rSynonyms.size() == nMaxSynonms ) + return; + } + } + } + catch ( const css::uno::Exception& ) + { + SAL_WARN( "fwk.uielement", "Failed to get synonyms" ); + } +} + +OUString ThesaurusMenuController::getThesImplName( const css::lang::Locale& rLocale ) const +{ + css::uno::Sequence< OUString > aServiceNames = + m_xLinguServiceManager->getConfiguredServices( "com.sun.star.linguistic2.Thesaurus", rLocale ); + SAL_WARN_IF( aServiceNames.getLength() > 1, "fwk.uielement", "Only one thesaurus is allowed per locale, but found more!" ); + if ( aServiceNames.getLength() == 1 ) + return aServiceNames[0]; + + return OUString(); +} + +OUString ThesaurusMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.ThesaurusMenuController"; +} + +css::uno::Sequence< OUString > ThesaurusMenuController::getSupportedServiceNames() +{ + return { "com.sun.star.frame.PopupMenuController" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ThesaurusMenuController_get_implementation( + css::uno::XComponentContext* xContext, + css::uno::Sequence< css::uno::Any > const & ) +{ + return cppu::acquire( new ThesaurusMenuController( xContext ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/togglebuttontoolbarcontroller.cxx b/framework/source/uielement/togglebuttontoolbarcontroller.cxx new file mode 100644 index 000000000..c17b08efe --- /dev/null +++ b/framework/source/uielement/togglebuttontoolbarcontroller.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 <uielement/togglebuttontoolbarcontroller.hxx> + +#include <comphelper/propertyvalue.hxx> +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> +#include <vcl/menu.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::util; + +namespace framework +{ + +ToggleButtonToolbarController::ToggleButtonToolbarController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + ToolBox* pToolbar, + ToolBoxItemId nID, + Style eStyle, + const OUString& aCommand ) : + ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand ) +{ + if ( eStyle == Style::DropDownButton ) + m_xToolbar->SetItemBits( m_nID, ToolBoxItemBits::DROPDOWNONLY | m_xToolbar->GetItemBits( m_nID ) ); + else // Style::ToggleDropDownButton + m_xToolbar->SetItemBits( m_nID, ToolBoxItemBits::DROPDOWN | m_xToolbar->GetItemBits( m_nID ) ); +} + +ToggleButtonToolbarController::~ToggleButtonToolbarController() +{ +} + +void SAL_CALL ToggleButtonToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + ComplexToolbarController::dispose(); +} + +Sequence<PropertyValue> ToggleButtonToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const +{ + Sequence<PropertyValue> aArgs{ // Add key modifier to argument list + comphelper::makePropertyValue("KeyModifier", KeyModifier), + comphelper::makePropertyValue("Text", m_aCurrentSelection) }; + return aArgs; +} + +uno::Reference< awt::XWindow > SAL_CALL ToggleButtonToolbarController::createPopupWindow() +{ + uno::Reference< awt::XWindow > xWindow; + + SolarMutexGuard aSolarMutexGuard; + + // create popup menu + ScopedVclPtrInstance<::PopupMenu> aPopup; + const sal_uInt32 nCount = m_aDropdownMenuList.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + const OUString & rLabel = m_aDropdownMenuList[i].mLabel; + aPopup->InsertItem( sal_uInt16( i+1 ), rLabel ); + if ( rLabel == m_aCurrentSelection ) + aPopup->CheckItem( sal_uInt16( i+1 ) ); + else + aPopup->CheckItem( sal_uInt16( i+1 ), false ); + + if ( !m_aDropdownMenuList[i].mTipHelpText.isEmpty() ) + aPopup->SetTipHelpText( sal_uInt16( i+1 ), m_aDropdownMenuList[i].mTipHelpText ); + } + + m_xToolbar->SetItemDown( m_nID, true ); + aPopup->SetSelectHdl( LINK( this, ToggleButtonToolbarController, MenuSelectHdl )); + aPopup->Execute( m_xToolbar, m_xToolbar->GetItemRect( m_nID )); + m_xToolbar->SetItemDown( m_nID, false ); + + return xWindow; +} + +void ToggleButtonToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand ) +{ + SolarMutexGuard aSolarMutexGuard; + + if ( rControlCommand.Command == "SetList" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "List" ) + { + Sequence< OUString > aList; + m_aDropdownMenuList.clear(); + m_aCurrentSelection.clear(); + + arg.Value >>= aList; + for ( OUString const & label : std::as_const(aList) ) + { + m_aDropdownMenuList.push_back( DropdownMenuItem() ); + m_aDropdownMenuList.back().mLabel = label; + } + + // send notification + uno::Sequence< beans::NamedValue > aInfo { { "List", css::uno::Any(aList) } }; + addNotifyInfo( "ListChanged", + getDispatchFromCommand( m_aCommandURL ), + aInfo ); + + break; + } + } + } + else if ( rControlCommand.Command == "CheckItemPos" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "Pos" ) + { + sal_Int32 nPos( -1 ); + + arg.Value >>= nPos; + if ( nPos >= 0 && + ( sal::static_int_cast< sal_uInt32 >(nPos) + < m_aDropdownMenuList.size() ) ) + { + m_aCurrentSelection = m_aDropdownMenuList[nPos].mLabel; + + // send notification + uno::Sequence< beans::NamedValue > aInfo { { "ItemChecked", css::uno::Any(nPos) } }; + addNotifyInfo( "Pos", + getDispatchFromCommand( m_aCommandURL ), + aInfo ); + } + break; + } + } + } + else if ( rControlCommand.Command == "AddEntry" ) + { + OUString aText; + OUString aTipHelpText; + + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "Text" ) + { + arg.Value >>= aText; + } + else if ( arg.Name == "TipHelpText" ) + { + arg.Value >>= aTipHelpText; + } + } + + if (!aText.isEmpty()) + { + m_aDropdownMenuList.push_back( DropdownMenuItem() ); + m_aDropdownMenuList.back().mLabel = aText; + m_aDropdownMenuList.back().mTipHelpText = aTipHelpText; + } + } + else if ( rControlCommand.Command == "InsertEntry" ) + { + sal_Int32 nPos(0); + sal_Int32 nSize = sal_Int32( m_aDropdownMenuList.size() ); + OUString aText; + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "Pos" ) + { + sal_Int32 nTmpPos = 0; + if ( arg.Value >>= nTmpPos ) + { + if (( nTmpPos >= 0 ) && ( nTmpPos < nSize )) + nPos = nTmpPos; + } + } + else if ( arg.Name == "Text" ) + arg.Value >>= aText; + } + + std::vector< DropdownMenuItem >::iterator aIter = m_aDropdownMenuList.begin(); + aIter += nPos; + aIter = m_aDropdownMenuList.insert(aIter, DropdownMenuItem()); + if (aIter != m_aDropdownMenuList.end()) + aIter->mLabel = aText; + } + else if ( rControlCommand.Command == "RemoveEntryPos" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "Pos" ) + { + sal_Int32 nPos( -1 ); + if ( arg.Value >>= nPos ) + { + if ( nPos < sal_Int32( m_aDropdownMenuList.size() )) + { + m_aDropdownMenuList.erase(m_aDropdownMenuList.begin() + nPos); + } + } + break; + } + } + } + else if ( rControlCommand.Command == "RemoveEntryText" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "Text" ) + { + OUString aText; + if ( arg.Value >>= aText ) + { + sal_Int32 nSize = sal_Int32( m_aDropdownMenuList.size() ); + for ( sal_Int32 j = 0; j < nSize; j++ ) + { + if ( m_aDropdownMenuList[j].mLabel == aText ) + { + m_aDropdownMenuList.erase(m_aDropdownMenuList.begin() + j); + break; + } + } + } + break; + } + } + } + else if ( rControlCommand.Command == "createPopupMenu" ) + { + createPopupWindow(); + } +} + +IMPL_LINK( ToggleButtonToolbarController, MenuSelectHdl, Menu *, pMenu, bool ) +{ + SolarMutexGuard aGuard; + + sal_uInt16 nItemId = pMenu->GetCurItemId(); + if ( nItemId > 0 && nItemId <= m_aDropdownMenuList.size() ) + { + m_aCurrentSelection = m_aDropdownMenuList[nItemId-1].mLabel; + + execute( 0 ); + } + return false; +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/toolbarmanager.cxx b/framework/source/uielement/toolbarmanager.cxx new file mode 100644 index 000000000..3cce6eef4 --- /dev/null +++ b/framework/source/uielement/toolbarmanager.cxx @@ -0,0 +1,2365 @@ +/* + * 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 <sal/log.hxx> + +#include <cassert> + +#include <uielement/toolbarmanager.hxx> + +#include <framework/generictoolbarcontroller.hxx> +#include <officecfg/Office/Common.hxx> +#include <uielement/styletoolbarcontroller.hxx> +#include <properties.h> +#include <framework/sfxhelperfunctions.hxx> +#include <classes/fwkresid.hxx> +#include <classes/resource.hxx> +#include <strings.hrc> +#include <uielement/toolbarmerger.hxx> + +#include <com/sun/star/ui/ItemType.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/awt/XDockableWindow.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/ui/DockingArea.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/frame/theToolbarControllerFactory.hpp> +#include <com/sun/star/ui/ItemStyle.hpp> +#include <com/sun/star/ui/XUIElementSettings.hpp> +#include <com/sun/star/ui/XUIConfigurationPersistence.hpp> +#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/ImageType.hpp> +#include <com/sun/star/ui/UIElementType.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/util/URLTransformer.hpp> + +#include <svtools/toolboxcontroller.hxx> +#include <unotools/cmdoptions.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <unotools/mediadescriptor.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/sequence.hxx> +#include <svtools/miscopt.hxx> +#include <svtools/imgdef.hxx> +#include <utility> +#include <vcl/event.hxx> +#include <vcl/graph.hxx> +#include <vcl/svapp.hxx> +#include <vcl/menu.hxx> +#include <vcl/syswin.hxx> +#include <vcl/taskpanelist.hxx> +#include <vcl/toolbox.hxx> +#include <vcl/settings.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <vcl/weldutils.hxx> +#include <tools/debug.hxx> + +// namespaces + +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::graphic; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::ui; +using namespace ::com::sun::star; + +namespace framework +{ + +const char ITEM_DESCRIPTOR_COMMANDURL[] = "CommandURL"; +const char ITEM_DESCRIPTOR_VISIBLE[] = "IsVisible"; + +const sal_uInt16 STARTID_CUSTOMIZE_POPUPMENU = 1000; + +static css::uno::Reference< css::frame::XLayoutManager > getLayoutManagerFromFrame( + css::uno::Reference< css::frame::XFrame > const & rFrame ) +{ + css::uno::Reference< css::frame::XLayoutManager > xLayoutManager; + + Reference< XPropertySet > xPropSet( rFrame, UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager; + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + } + + return xLayoutManager; +} +namespace +{ + +sal_Int16 getCurrentImageType() +{ + SvtMiscOptions aMiscOptions; + sal_Int16 nImageType = css::ui::ImageType::SIZE_DEFAULT; + if (aMiscOptions.GetCurrentSymbolsSize() == SFX_SYMBOLS_SIZE_LARGE) + nImageType |= css::ui::ImageType::SIZE_LARGE; + else if (aMiscOptions.GetCurrentSymbolsSize() == SFX_SYMBOLS_SIZE_32) + nImageType |= css::ui::ImageType::SIZE_32; + return nImageType; +} + +class VclToolBarManager : public ToolBarManagerImpl +{ + DECL_LINK(Click, ToolBox*, void); + +public: + VclToolBarManager(VclPtr<ToolBox> pToolbar) + : m_pToolBar(std::move(pToolbar)) + , m_bAddedToTaskPaneList(true) + , m_pManager(nullptr) + {} + + ~VclToolBarManager() + { + OSL_ASSERT( !m_bAddedToTaskPaneList ); + } + + virtual void Init() override + { + vcl::Window* pWindow = m_pToolBar; + while ( pWindow && !pWindow->IsSystemWindow() ) + pWindow = pWindow->GetParent(); + + if ( pWindow ) + static_cast<SystemWindow *>(pWindow)->GetTaskPaneList()->AddWindow( m_pToolBar ); + } + + virtual void Destroy() override + { + OSL_ASSERT( m_pToolBar != nullptr ); + SolarMutexGuard g; + if ( m_bAddedToTaskPaneList ) + { + vcl::Window* pWindow = m_pToolBar; + while ( pWindow && !pWindow->IsSystemWindow() ) + pWindow = pWindow->GetParent(); + + if ( pWindow ) + static_cast<SystemWindow *>(pWindow)->GetTaskPaneList()->RemoveWindow( m_pToolBar ); + m_bAddedToTaskPaneList = false; + } + + // Delete the additional add-ons data + for ( ToolBox::ImplToolItems::size_type i = 0; i < m_pToolBar->GetItemCount(); i++ ) + { + ToolBoxItemId nItemId = m_pToolBar->GetItemId( i ); + if ( nItemId > ToolBoxItemId(0) ) + delete static_cast< AddonsParams* >( m_pToolBar->GetItemData( nItemId )); + } + + // #i93173# note we can still be in one of the toolbar's handlers + m_pToolBar->SetSelectHdl( Link<ToolBox *, void>() ); + m_pToolBar->SetActivateHdl( Link<ToolBox *, void>() ); + m_pToolBar->SetDeactivateHdl( Link<ToolBox *, void>() ); + m_pToolBar->SetClickHdl( Link<ToolBox *, void>() ); + m_pToolBar->SetDropdownClickHdl( Link<ToolBox *, void>() ); + m_pToolBar->SetDoubleClickHdl( Link<ToolBox *, void>() ); + m_pToolBar->SetStateChangedHdl( Link<StateChangedType const *, void>() ); + m_pToolBar->SetDataChangedHdl( Link<DataChangedEvent const *, void>() ); + + m_pToolBar.disposeAndClear(); + } + + virtual css::uno::Reference<css::awt::XWindow> GetInterface() override + { + return VCLUnoHelper::GetInterface(m_pToolBar); + } + + virtual css::uno::Reference<css::frame::XStatusListener> CreateToolBoxController( + const css::uno::Reference<css::frame::XFrame>& rFrame, + ToolBoxItemId nId, + const OUString& rCommandURL ) override + { + rtl::Reference<svt::ToolboxController> pController + = ::framework::CreateToolBoxController( rFrame, m_pToolBar, nId, rCommandURL ); + css::uno::Reference<css::frame::XStatusListener> xListener; + if (pController) + xListener = pController; + return xListener; + } + + virtual void ConnectCallbacks(ToolBarManager* pManager) override + { + m_pManager = pManager; + m_pToolBar->SetSelectHdl( LINK( pManager, ToolBarManager, Select) ); + m_pToolBar->SetClickHdl( LINK( this, VclToolBarManager, Click ) ); + m_pToolBar->SetDropdownClickHdl( LINK( pManager, ToolBarManager, DropdownClick ) ); + m_pToolBar->SetDoubleClickHdl( LINK( pManager, ToolBarManager, DoubleClick ) ); + m_pToolBar->SetStateChangedHdl( LINK( pManager, ToolBarManager, StateChanged ) ); + m_pToolBar->SetDataChangedHdl( LINK( pManager, ToolBarManager, DataChanged ) ); + + m_pToolBar->SetMenuButtonHdl( LINK( pManager, ToolBarManager, MenuButton ) ); + m_pToolBar->SetMenuExecuteHdl( LINK( pManager, ToolBarManager, MenuPreExecute ) ); + m_pToolBar->GetMenu()->SetSelectHdl( LINK( pManager, ToolBarManager, MenuSelect ) ); + } + + virtual void InsertItem(ToolBoxItemId nId, + const OUString& rString, + const OUString& rCommandURL, + const OUString& rTooltip, + const OUString& rLabel, + ToolBoxItemBits nItemBits) override + { + m_pToolBar->InsertItem( nId, rString, nItemBits ); + m_pToolBar->SetItemCommand( nId, rCommandURL ); + m_pToolBar->SetQuickHelpText(nId, rTooltip); + m_pToolBar->SetItemText( nId, rLabel ); + m_pToolBar->EnableItem( nId ); + m_pToolBar->SetItemState( nId, TRISTATE_FALSE ); + } + + virtual void InsertSeparator() override + { + m_pToolBar->InsertSeparator(); + } + + virtual void InsertSpace() override + { + m_pToolBar->InsertSpace(); + } + + virtual void InsertBreak() override + { + m_pToolBar->InsertBreak(); + } + + virtual ToolBoxItemId GetItemId(sal_uInt16 nPos) override + { + return m_pToolBar->GetItemId(nPos); + } + + virtual ToolBoxItemId GetCurItemId() override + { + return m_pToolBar->GetCurItemId(); + } + + virtual OUString GetItemCommand(ToolBoxItemId nId) override + { + return m_pToolBar->GetItemCommand(nId); + } + + virtual sal_uInt16 GetItemCount() override + { + return m_pToolBar->GetItemCount(); + } + + virtual void SetItemCheckable(ToolBoxItemId nId) override + { + m_pToolBar->SetItemBits( nId, m_pToolBar->GetItemBits( nId ) | ToolBoxItemBits::CHECKABLE ); + } + + virtual void HideItem(ToolBoxItemId nId, const OUString& /*rCommandURL*/) override + { + m_pToolBar->HideItem( nId ); + } + + virtual bool IsItemVisible(ToolBoxItemId nId, const OUString& /*rCommandURL*/) override + { + return m_pToolBar->IsItemVisible(nId); + } + + virtual void Clear() override + { + m_pToolBar->Clear(); + } + + virtual void SetName(const OUString& rName) override + { + m_pToolBar->SetText( rName ); + } + + virtual void SetHelpId(const OString& rHelpId) override + { + m_pToolBar->SetHelpId( rHelpId ); + } + + virtual bool WillUsePopupMode() override + { + return m_pToolBar->WillUsePopupMode(); + } + + virtual bool IsReallyVisible() override + { + return m_pToolBar->IsReallyVisible(); + } + + virtual void SetIconSize(ToolBoxButtonSize eSize) override + { + m_pToolBar->SetToolboxButtonSize(eSize); + } + + virtual vcl::ImageType GetImageSize() override + { + return m_pToolBar->GetImageSize(); + } + + virtual void SetMenuType(ToolBoxMenuType eType) override + { + m_pToolBar->SetMenuType( eType ); + } + + virtual void MergeToolbar(ToolBoxItemId & rItemId, + const OUString& rModuleIdentifier, + CommandToInfoMap& rCommandMap, + MergeToolbarInstruction& rInstruction) override + { + ReferenceToolbarPathInfo aRefPoint = ToolBarMerger::FindReferencePoint( m_pToolBar, rInstruction.aMergePoint ); + + // convert the sequence< sequence< propertyvalue > > structure to + // something we can better handle. A vector with item data + AddonToolbarItemContainer aItems; + ToolBarMerger::ConvertSeqSeqToVector( rInstruction.aMergeToolbarItems, aItems ); + + if ( aRefPoint.bResult ) + { + ToolBarMerger::ProcessMergeOperation( m_pToolBar, + aRefPoint.nPos, + rItemId, + rCommandMap, + rModuleIdentifier, + rInstruction.aMergeCommand, + rInstruction.aMergeCommandParameter, + aItems ); + } + else + { + ToolBarMerger::ProcessMergeFallback( m_pToolBar, + rItemId, + rCommandMap, + rModuleIdentifier, + rInstruction.aMergeCommand, + rInstruction.aMergeFallback, + aItems ); + } + } + + virtual void SetItemImage(ToolBoxItemId nId, + const OUString& /*rCommandURL*/, + const Image& rImage) override + { + m_pToolBar->SetItemImage(nId, rImage); + } + + virtual void UpdateSize() override + { + ::Size aSize = m_pToolBar->CalcWindowSizePixel(); + m_pToolBar->SetOutputSizePixel( aSize ); + } + + virtual void SetItemWindow(ToolBoxItemId nItemId, vcl::Window* pNewWindow) override + { + m_pToolBar->SetItemWindow( nItemId, pNewWindow ); + } + +private: + VclPtr<ToolBox> m_pToolBar; + bool m_bAddedToTaskPaneList; + ToolBarManager* m_pManager; +}; + +IMPL_LINK_NOARG(VclToolBarManager, Click, ToolBox*, void) +{ + m_pManager->OnClick(); +} + +class WeldToolBarManager : public ToolBarManagerImpl +{ + DECL_LINK(Click, const OString&, void); + DECL_LINK(ToggleMenuHdl, const OString&, void); + +public: + WeldToolBarManager(weld::Toolbar* pToolbar, + weld::Builder* pBuilder) + : m_pWeldedToolBar(pToolbar) + , m_pBuilder(pBuilder) + , m_pManager(nullptr) + , m_nCurrentId(0) + {} + + virtual void Init() override {} + + virtual void Destroy() override {} + + virtual css::uno::Reference<css::awt::XWindow> GetInterface() override + { + return new weld::TransportAsXWindow(m_pWeldedToolBar); + } + + virtual css::uno::Reference<css::frame::XStatusListener> CreateToolBoxController( + const css::uno::Reference<css::frame::XFrame>& rFrame, + ToolBoxItemId /*nId*/, + const OUString& rCommandURL ) override + { + css::uno::Reference<css::frame::XToolbarController> xController + = ::framework::CreateWeldToolBoxController(rFrame, m_pWeldedToolBar, m_pBuilder, rCommandURL); + css::uno::Reference<css::frame::XStatusListener> xListener; + if (xController.is()) + xListener = css::uno::Reference<css::frame::XStatusListener>( xController, UNO_QUERY ); + return xListener; + } + + virtual void ConnectCallbacks(ToolBarManager* pManager) override + { + m_pManager = pManager; + m_pWeldedToolBar->connect_clicked(LINK(this, WeldToolBarManager, Click)); + m_pWeldedToolBar->connect_menu_toggled(LINK(this, WeldToolBarManager, ToggleMenuHdl)); + } + + virtual void InsertItem(ToolBoxItemId nId, + const OUString& /*rString*/, + const OUString& rCommandURL, + const OUString& rTooltip, + const OUString& rLabel, + ToolBoxItemBits /*nItemBits*/) override + { + OString sId = OUStringToOString(rCommandURL, RTL_TEXTENCODING_UTF8); + m_aCommandToId[sId] = nId; + m_aIdToCommand[nId] = sId; + m_aCommandOrder.push_back(sId); + + m_pWeldedToolBar->insert_item(m_aCommandOrder.size(), rCommandURL); + m_pWeldedToolBar->set_item_tooltip_text(sId, rTooltip); + m_pWeldedToolBar->set_item_label( sId, rLabel ); + m_pWeldedToolBar->set_item_sensitive( sId, true ); + m_pWeldedToolBar->set_item_active( sId, false ); + } + + virtual void InsertSeparator() override + { + m_pWeldedToolBar->append_separator(""); + } + + virtual void InsertSpace() override {} + + virtual void InsertBreak() override {} + + virtual ToolBoxItemId GetItemId(sal_uInt16 nPos) override + { + return m_aCommandToId[m_aCommandOrder[nPos]]; + } + + virtual ToolBoxItemId GetCurItemId() override + { + return m_nCurrentId; + } + + virtual OUString GetItemCommand(ToolBoxItemId nId) override + { + return OStringToOUString(m_aIdToCommand[nId], RTL_TEXTENCODING_UTF8); + } + + virtual sal_uInt16 GetItemCount() override + { + return m_aCommandOrder.size(); + } + + virtual void SetItemCheckable(ToolBoxItemId /*nId*/) override {} + + virtual void HideItem(ToolBoxItemId /*nId*/, const OUString& rCommandURL) override + { + OString sId = OUStringToOString(rCommandURL, RTL_TEXTENCODING_UTF8); + m_pWeldedToolBar->set_item_visible(sId, false); + } + + virtual bool IsItemVisible(ToolBoxItemId /*nId*/, const OUString& rCommandURL) override + { + OString sId = OUStringToOString(rCommandURL, RTL_TEXTENCODING_UTF8); + return m_pWeldedToolBar->get_item_visible(sId); + } + + virtual void Clear() override {} + + virtual void SetName(const OUString& /*rName*/) override {} + + virtual void SetHelpId(const OString& /*rHelpId*/) override {} + + virtual bool WillUsePopupMode() override { return true; } + + virtual bool IsReallyVisible() override { return true; } + + virtual void SetIconSize(ToolBoxButtonSize /*eSize*/) override {} + + virtual vcl::ImageType GetImageSize() override + { + return vcl::ImageType::Size32; + } + + virtual void SetMenuType(ToolBoxMenuType /*eType*/) override {} + + virtual void MergeToolbar(ToolBoxItemId & /*rItemId*/, + const OUString& /*rModuleIdentifier*/, + CommandToInfoMap& /*rCommandMap*/, + MergeToolbarInstruction& /*rInstruction*/) override {} + + virtual void SetItemImage(ToolBoxItemId /*nId*/, + const OUString& rCommandURL, + const Image& rImage) override + { + OString sId = OUStringToOString(rCommandURL, RTL_TEXTENCODING_UTF8); + m_pWeldedToolBar->set_item_image(sId, Graphic(rImage).GetXGraphic()); + } + + virtual void UpdateSize() override {} + + virtual void SetItemWindow(ToolBoxItemId /*nItemId*/, vcl::Window* /*pNewWindow*/) override {} + +private: + weld::Toolbar* m_pWeldedToolBar; + weld::Builder* m_pBuilder; + ToolBarManager* m_pManager; + ToolBoxItemId m_nCurrentId; + std::map<const OString, ToolBoxItemId> m_aCommandToId; + std::map<ToolBoxItemId, OString> m_aIdToCommand; + std::vector<OString> m_aCommandOrder; +}; + +IMPL_LINK(WeldToolBarManager, Click, const OString&, rCommand, void) +{ + m_nCurrentId = m_aCommandToId[rCommand]; + m_pManager->OnClick(true); +} + +IMPL_LINK(WeldToolBarManager, ToggleMenuHdl, const OString&, rCommand, void) +{ + m_nCurrentId = m_aCommandToId[rCommand]; + m_pManager->OnDropdownClick(false); +} + +} // end anonymous namespace + +// XInterface, XTypeProvider, XServiceInfo + +ToolBarManager::ToolBarManager( const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + OUString aResourceName, + ToolBox* pToolBar ) : + m_bDisposed( false ), + m_bFrameActionRegistered( false ), + m_bUpdateControllers( false ), + m_eSymbolSize(SvtMiscOptions().GetCurrentSymbolsSize()), + m_pImpl( new VclToolBarManager( pToolBar ) ), + m_pToolBar( pToolBar ), + m_aResourceName(std::move( aResourceName )), + m_xFrame( rFrame ), + m_xContext( rxContext ), + m_aAsyncUpdateControllersTimer( "framework::ToolBarManager m_aAsyncUpdateControllersTimer" ), + m_sIconTheme( SvtMiscOptions().GetIconTheme() ) +{ + Init(); +} + +ToolBarManager::ToolBarManager( const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + OUString aResourceName, + weld::Toolbar* pToolBar, + weld::Builder* pBuilder ) : + m_bDisposed( false ), + m_bFrameActionRegistered( false ), + m_bUpdateControllers( false ), + m_eSymbolSize( SvtMiscOptions().GetCurrentSymbolsSize() ), + m_pImpl( new WeldToolBarManager( pToolBar, pBuilder ) ), + m_pToolBar( nullptr ), + m_aResourceName(std::move( aResourceName )), + m_xFrame( rFrame ), + m_xContext( rxContext ), + m_aAsyncUpdateControllersTimer( "framework::ToolBarManager m_aAsyncUpdateControllersTimer" ), + m_sIconTheme( SvtMiscOptions().GetIconTheme() ) +{ + Init(); +} + +void ToolBarManager::Init() +{ + OSL_ASSERT( m_xContext.is() ); + + m_pImpl->Init(); + + m_xToolbarControllerFactory = frame::theToolbarControllerFactory::get( m_xContext ); + m_xURLTransformer = URLTransformer::create( m_xContext ); + + m_pImpl->ConnectCallbacks(this); + + if (m_eSymbolSize == SFX_SYMBOLS_SIZE_LARGE) + m_pImpl->SetIconSize(ToolBoxButtonSize::Large); + else if (m_eSymbolSize == SFX_SYMBOLS_SIZE_32) + m_pImpl->SetIconSize(ToolBoxButtonSize::Size32); + else + m_pImpl->SetIconSize(ToolBoxButtonSize::Small); + + // enables a menu for clipped items and customization + SvtCommandOptions aCmdOptions; + ToolBoxMenuType nMenuType = ToolBoxMenuType::ClippedItems; + if ( !aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, "CreateDialog")) + nMenuType |= ToolBoxMenuType::Customize; + + m_pImpl->SetMenuType( nMenuType ); + + // set name for testtool, the useful part is after the last '/' + sal_Int32 idx = m_aResourceName.lastIndexOf('/'); + idx++; // will become 0 if '/' not found: use full string + std::u16string_view aToolbarName = m_aResourceName.subView( idx ); + OString aHelpIdAsString = ".HelpId:" + OUStringToOString( aToolbarName, RTL_TEXTENCODING_UTF8 ); + m_pImpl->SetHelpId( aHelpIdAsString ); + + m_aAsyncUpdateControllersTimer.SetTimeout( 50 ); + m_aAsyncUpdateControllersTimer.SetInvokeHandler( LINK( this, ToolBarManager, AsyncUpdateControllersHdl ) ); + + SvtMiscOptions().AddListenerLink( LINK( this, ToolBarManager, MiscOptionsChanged ) ); +} + +ToolBarManager::~ToolBarManager() +{ + assert(!m_aAsyncUpdateControllersTimer.IsActive()); + assert(!m_pToolBar); // must be disposed by ToolbarLayoutManager +} + +void ToolBarManager::Destroy() +{ + m_pImpl->Destroy(); + + SvtMiscOptions().RemoveListenerLink( LINK( this, ToolBarManager, MiscOptionsChanged ) ); +} + +ToolBox* ToolBarManager::GetToolBar() const +{ + SolarMutexGuard g; + return m_pToolBar; +} + +void ToolBarManager::CheckAndUpdateImages() +{ + SolarMutexGuard g; + bool bRefreshImages = false; + + SvtMiscOptions aMiscOptions; + sal_Int16 eNewSymbolSize = aMiscOptions.GetCurrentSymbolsSize(); + + if (m_eSymbolSize != eNewSymbolSize ) + { + bRefreshImages = true; + m_eSymbolSize = eNewSymbolSize; + } + + const OUString& sCurrentIconTheme = aMiscOptions.GetIconTheme(); + if ( m_sIconTheme != sCurrentIconTheme ) + { + bRefreshImages = true; + m_sIconTheme = sCurrentIconTheme; + } + + // Refresh images if requested + if ( bRefreshImages ) + RefreshImages(); +} + +void ToolBarManager::RefreshImages() +{ + SolarMutexGuard g; + + if (m_eSymbolSize == SFX_SYMBOLS_SIZE_LARGE) + m_pImpl->SetIconSize(ToolBoxButtonSize::Large); + else if (m_eSymbolSize == SFX_SYMBOLS_SIZE_32) + m_pImpl->SetIconSize(ToolBoxButtonSize::Size32); + else + m_pImpl->SetIconSize(ToolBoxButtonSize::Small); + + for ( auto const& it : m_aControllerMap ) + { + Reference< XSubToolbarController > xController( it.second, UNO_QUERY ); + if ( xController.is() && xController->opensSubToolbar() ) + { + // The button should show the last function that was selected from the + // dropdown. The controller should know better than us what it was. + xController->updateImage(); + } + else + { + OUString aCommandURL = m_pImpl->GetItemCommand( it.first ); + vcl::ImageType eImageType = m_pImpl->GetImageSize(); + Image aImage = vcl::CommandInfoProvider::GetImageForCommand(aCommandURL, m_xFrame, eImageType); + // Try also to query for add-on images before giving up and use an + // empty image. + bool bBigImages = eImageType != vcl::ImageType::Size16; + if ( !aImage ) + aImage = Image(framework::AddonsOptions().GetImageFromURL(aCommandURL, bBigImages)); + m_pImpl->SetItemImage( it.first, aCommandURL, aImage ); + } + } + + m_pImpl->UpdateSize(); +} + +void ToolBarManager::UpdateControllers() +{ + + if( officecfg::Office::Common::Misc::DisableUICustomization::get() ) + { + Any a; + Reference< XLayoutManager > xLayoutManager; + Reference< XPropertySet > xFramePropSet( m_xFrame, UNO_QUERY ); + if ( xFramePropSet.is() ) + a = xFramePropSet->getPropertyValue("LayoutManager"); + a >>= xLayoutManager; + Reference< XDockableWindow > xDockable( m_pImpl->GetInterface(), UNO_QUERY ); + if ( xLayoutManager.is() && xDockable.is() ) + { + css::awt::Point aPoint; + aPoint.X = aPoint.Y = SAL_MAX_INT32; + xLayoutManager->dockWindow( m_aResourceName, DockingArea_DOCKINGAREA_DEFAULT, aPoint ); + xLayoutManager->lockWindow( m_aResourceName ); + } + } + + if ( !m_bUpdateControllers ) + { + m_bUpdateControllers = true; + for (auto const& controller : m_aControllerMap) + { + try + { + Reference< XUpdatable > xUpdatable( controller.second, UNO_QUERY ); + if ( xUpdatable.is() ) + xUpdatable->update(); + } + catch (const Exception&) + { + } + } + } + m_bUpdateControllers = false; +} + +//for update toolbar controller via Support Visible +void ToolBarManager::UpdateController( const css::uno::Reference< css::frame::XToolbarController >& xController) +{ + + if ( !m_bUpdateControllers ) + { + m_bUpdateControllers = true; + try + { if(xController.is()) + { + Reference< XUpdatable > xUpdatable( xController, UNO_QUERY ); + if ( xUpdatable.is() ) + xUpdatable->update(); + } + } + catch (const Exception&) + { + } + + } + m_bUpdateControllers = false; +} + +void ToolBarManager::frameAction( const FrameActionEvent& Action ) +{ + SolarMutexGuard g; + if ( Action.Action == FrameAction_CONTEXT_CHANGED && !m_bDisposed ) + { + m_aAsyncUpdateControllersTimer.Start(); + } +} + +void SAL_CALL ToolBarManager::disposing( const EventObject& Source ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + RemoveControllers(); + + if ( m_xDocImageManager.is() ) + { + try + { + m_xDocImageManager->removeConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + + if ( m_xModuleImageManager.is() ) + { + try + { + m_xModuleImageManager->removeConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + + m_xDocImageManager.clear(); + m_xModuleImageManager.clear(); + + if ( Source.Source == Reference< XInterface >( m_xFrame, UNO_QUERY )) + m_xFrame.clear(); + + m_xContext.clear(); +} + +// XComponent +void SAL_CALL ToolBarManager::dispose() +{ + Reference< XComponent > xThis(this); + + { + EventObject aEvent( xThis ); + std::unique_lock aGuard(m_mutex); + m_aListenerContainer.disposeAndClear( aGuard, aEvent ); + } + { + SolarMutexGuard g; + + if (m_bDisposed) + { + return; + } + + RemoveControllers(); + + if ( m_xDocImageManager.is() ) + { + try + { + m_xDocImageManager->removeConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + m_xDocImageManager.clear(); + if ( m_xModuleImageManager.is() ) + { + try + { + m_xModuleImageManager->removeConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + m_xModuleImageManager.clear(); + + if ( m_aOverflowManager.is() ) + { + m_aOverflowManager->dispose(); + m_aOverflowManager.clear(); + } + + // We have to destroy our toolbar instance now. + Destroy(); + m_pToolBar.clear(); + + if ( m_bFrameActionRegistered && m_xFrame.is() ) + { + try + { + m_xFrame->removeFrameActionListener( Reference< XFrameActionListener >(this) ); + } + catch (const Exception&) + { + } + } + + m_xFrame.clear(); + m_xContext.clear(); + + // stop timer to prevent timer events after dispose + // do it last because other calls could restart timer in StateChanged() + m_aAsyncUpdateControllersTimer.Stop(); + + m_bDisposed = true; + } +} + +void SAL_CALL ToolBarManager::addEventListener( const Reference< XEventListener >& xListener ) +{ + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + std::unique_lock aGuard(m_mutex); + m_aListenerContainer.addInterface( aGuard, xListener ); +} + +void SAL_CALL ToolBarManager::removeEventListener( const Reference< XEventListener >& xListener ) +{ + std::unique_lock aGuard(m_mutex); + m_aListenerContainer.removeInterface( aGuard, xListener ); +} + +// XUIConfigurationListener +void SAL_CALL ToolBarManager::elementInserted( const css::ui::ConfigurationEvent& Event ) +{ + impl_elementChanged(false,Event); +} + +void SAL_CALL ToolBarManager::elementRemoved( const css::ui::ConfigurationEvent& Event ) +{ + impl_elementChanged(true,Event); +} +void ToolBarManager::impl_elementChanged(bool const isRemove, + const css::ui::ConfigurationEvent& Event) +{ + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + return; + + Reference< XNameAccess > xNameAccess; + sal_Int16 nImageType = sal_Int16(); + sal_Int16 nCurrentImageType = getCurrentImageType(); + + if (!(( Event.aInfo >>= nImageType ) && + ( nImageType == nCurrentImageType ) && + ( Event.Element >>= xNameAccess ))) + return; + + sal_Int16 nImageInfo( 1 ); + Reference< XInterface > xIfacDocImgMgr( m_xDocImageManager, UNO_QUERY ); + if ( xIfacDocImgMgr == Event.Source ) + nImageInfo = 0; + + const Sequence< OUString > aSeq = xNameAccess->getElementNames(); + for ( OUString const & commandName : aSeq ) + { + CommandToInfoMap::iterator pIter = m_aCommandMap.find( commandName ); + if ( pIter != m_aCommandMap.end() && ( pIter->second.nImageInfo >= nImageInfo )) + { + if (isRemove) + { + Image aImage; + if (( pIter->second.nImageInfo == 0 ) && ( pIter->second.nImageInfo == nImageInfo )) + { + // Special case: An image from the document image manager has been removed. + // It is possible that we have an image at our module image manager. Before + // we can remove our image we have to ask our module image manager. + Sequence< OUString > aCmdURLSeq{ pIter->first }; + Sequence< Reference< XGraphic > > aGraphicSeq; + aGraphicSeq = m_xModuleImageManager->getImages( nImageType, aCmdURLSeq ); + aImage = Image( aGraphicSeq[0] ); + } + + setToolBarImage(aImage,pIter); + } // if (isRemove) + else + { + Reference< XGraphic > xGraphic; + if ( xNameAccess->getByName( commandName ) >>= xGraphic ) + { + Image aImage( xGraphic ); + setToolBarImage(aImage,pIter); + } + pIter->second.nImageInfo = nImageInfo; + } + } + } +} +void ToolBarManager::setToolBarImage(const Image& rImage, + const CommandToInfoMap::const_iterator& rIter) +{ + const ::std::vector<ToolBoxItemId>& rIDs = rIter->second.aIds; + m_pImpl->SetItemImage( rIter->second.nId, rIter->first, rImage ); + for (auto const& it : rIDs) + { + m_pImpl->SetItemImage(it, rIter->first, rImage); + } +} + +void SAL_CALL ToolBarManager::elementReplaced( const css::ui::ConfigurationEvent& Event ) +{ + impl_elementChanged(false,Event); +} + +void ToolBarManager::RemoveControllers() +{ + DBG_TESTSOLARMUTEX(); + assert(!m_bDisposed); + + m_aSubToolBarControllerMap.clear(); + + // i90033 + // Remove item window pointers from the toolbar. They were + // destroyed by the dispose() at the XComponent. This is needed + // as VCL code later tries to access the item window data in certain + // dtors where the item window is already invalid! + for ( ToolBox::ImplToolItems::size_type i = 0; i < m_pImpl->GetItemCount(); i++ ) + { + ToolBoxItemId nItemId = m_pImpl->GetItemId( i ); + if ( nItemId > ToolBoxItemId(0) ) + { + Reference< XComponent > xComponent( m_aControllerMap[ nItemId ], UNO_QUERY ); + if ( xComponent.is() ) + { + try + { + xComponent->dispose(); + } + catch (const Exception&) + { + } + } + m_pImpl->SetItemWindow(nItemId, nullptr); + } + } + m_aControllerMap.clear(); +} + +void ToolBarManager::CreateControllers() +{ + Reference< XWindow > xToolbarWindow = m_pImpl->GetInterface(); + + css::util::URL aURL; + bool bHasDisabledEntries = SvtCommandOptions().HasEntries( SvtCommandOptions::CMDOPTION_DISABLED ); + SvtCommandOptions aCmdOptions; + + for ( ToolBox::ImplToolItems::size_type i = 0; i < m_pImpl->GetItemCount(); i++ ) + { + ToolBoxItemId nId = m_pImpl->GetItemId( i ); + if ( nId == ToolBoxItemId(0) ) + continue; + + bool bInit( true ); + bool bCreate( true ); + Reference< XStatusListener > xController; + + rtl::Reference<svt::ToolboxController> pController; + + OUString aCommandURL( m_pImpl->GetItemCommand( nId ) ); + // Command can be just an alias to another command. + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommandURL, m_aModuleIdentifier); + OUString aRealCommandURL( vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties) ); + if ( !aRealCommandURL.isEmpty() ) + aCommandURL = aRealCommandURL; + + if ( bHasDisabledEntries ) + { + aURL.Complete = aCommandURL; + m_xURLTransformer->parseStrict( aURL ); + if ( aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, aURL.Path )) + { + m_aControllerMap[ nId ] = xController; + m_pImpl->HideItem( nId, aCommandURL ); + continue; + } + } + + if ( m_xToolbarControllerFactory.is() && + m_xToolbarControllerFactory->hasController( aCommandURL, m_aModuleIdentifier )) + { + PropertyValue aPropValue; + std::vector< Any > aPropertyVector; + + aPropValue.Name = "ModuleIdentifier"; + aPropValue.Value <<= m_aModuleIdentifier; + aPropertyVector.push_back( Any( aPropValue )); + aPropValue.Name = "Frame"; + aPropValue.Value <<= m_xFrame; + aPropertyVector.push_back( Any( aPropValue )); + aPropValue.Name = "ServiceManager"; + Reference<XMultiServiceFactory> xMSF(m_xContext->getServiceManager(), UNO_QUERY_THROW); + aPropValue.Value <<= xMSF; + aPropertyVector.push_back( Any( aPropValue )); + aPropValue.Name = "ParentWindow"; + aPropValue.Value <<= xToolbarWindow; + aPropertyVector.push_back( Any( aPropValue )); + aPropValue.Name = "Identifier"; + aPropValue.Value <<= sal_uInt16(nId); + aPropertyVector.push_back( uno::Any( aPropValue ) ); + + Sequence< Any > aArgs( comphelper::containerToSequence( aPropertyVector )); + xController.set( m_xToolbarControllerFactory->createInstanceWithArgumentsAndContext( aCommandURL, aArgs, m_xContext ), + UNO_QUERY ); + bInit = false; // Initialization is done through the factory service + } + + if (( aCommandURL == ".uno:OpenUrl" ) && ( !m_pImpl->IsItemVisible(nId, aCommandURL))) + bCreate = false; + + if ( !xController.is() && bCreate ) + { + xController = m_pImpl->CreateToolBoxController( m_xFrame, nId, aCommandURL ); + if ( !xController ) + { + if ( aCommandURL.startsWith( ".uno:StyleApply?" ) ) + { + xController.set( new StyleToolbarController( m_xContext, m_xFrame, aCommandURL )); + m_pImpl->SetItemCheckable( nId ); + } + else if ( aCommandURL.startsWith( "private:resource/" ) ) + { + xController.set( m_xContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.comp.framework.GenericPopupToolbarController", m_xContext ), UNO_QUERY ); + } + else if ( m_pToolBar && m_pToolBar->GetItemData( nId ) != nullptr ) + { + // retrieve additional parameters + OUString aControlType = static_cast< AddonsParams* >( m_pToolBar->GetItemData( nId ))->aControlType; + sal_uInt16 nWidth = static_cast< AddonsParams* >( m_pToolBar->GetItemData( nId ))->nWidth; + + Reference< XStatusListener > xStatusListener( + ToolBarMerger::CreateController( m_xContext, + m_xFrame, + m_pToolBar, + aCommandURL, + nId, + nWidth, + aControlType ).get(), UNO_QUERY ); + + xController = xStatusListener; + } + else if (m_pToolBar) + { + xController.set( + new GenericToolbarController( m_xContext, m_xFrame, m_pToolBar, nId, aCommandURL )); + + // Accessibility support: Set toggle button role for specific commands + sal_Int32 nProps = vcl::CommandInfoProvider::GetPropertiesForCommand(aCommandURL, m_aModuleIdentifier); + if ( nProps & UICOMMANDDESCRIPTION_PROPERTIES_TOGGLEBUTTON ) + m_pImpl->SetItemCheckable( nId ); + } + } + } + + // Associate ID and controller to be able to retrieve + // the controller from the ID later. + m_aControllerMap[ nId ] = xController; + + // Fill sub-toolbars into our hash-map + Reference< XSubToolbarController > xSubToolBar( xController, UNO_QUERY ); + if ( xSubToolBar.is() && xSubToolBar->opensSubToolbar() ) + { + OUString aSubToolBarName = xSubToolBar->getSubToolbarName(); + if ( !aSubToolBarName.isEmpty() ) + { + SubToolBarToSubToolBarControllerMap::iterator pIter = + m_aSubToolBarControllerMap.find( aSubToolBarName ); + if ( pIter == m_aSubToolBarControllerMap.end() ) + { + SubToolBarControllerVector aSubToolBarVector; + aSubToolBarVector.push_back( xSubToolBar ); + m_aSubToolBarControllerMap.emplace( + aSubToolBarName, aSubToolBarVector ); + } + else + pIter->second.push_back( xSubToolBar ); + } + } + + Reference< XInitialization > xInit( xController, UNO_QUERY ); + if ( xInit.is() ) + { + if ( bInit ) + { + Reference<XMultiServiceFactory> xMSF(m_xContext->getServiceManager(), UNO_QUERY_THROW); + Sequence< Any > aArgs { + Any( comphelper::makePropertyValue("Frame", m_xFrame) ), + Any( comphelper::makePropertyValue("CommandURL", aCommandURL) ), + Any( comphelper::makePropertyValue("ServiceManager", xMSF) ), + Any( comphelper::makePropertyValue("ParentWindow", xToolbarWindow) ), + Any( comphelper::makePropertyValue("ModuleIdentifier", m_aModuleIdentifier) ), + Any( comphelper::makePropertyValue("Identifier", sal_uInt16(nId)) ), + }; + + xInit->initialize( aArgs ); + + if (pController) + { + if (aCommandURL == ".uno:SwitchXFormsDesignMode" || aCommandURL == ".uno:ViewDataSourceBrowser") + pController->setFastPropertyValue_NoBroadcast(1, Any(true)); + } + } + + // Request an item window from the toolbar controller and set it at the VCL toolbar + Reference< XToolbarController > xTbxController( xController, UNO_QUERY ); + if ( xTbxController.is() && xToolbarWindow.is() ) + { + Reference< XWindow > xWindow = xTbxController->createItemWindow( xToolbarWindow ); + if ( xWindow.is() ) + { + VclPtr<vcl::Window> pItemWin = VCLUnoHelper::GetWindow( xWindow ); + if ( pItemWin ) + { + WindowType nType = pItemWin->GetType(); + if ( m_pToolBar && (nType == WindowType::LISTBOX || nType == WindowType::MULTILISTBOX || nType == WindowType::COMBOBOX) ) + pItemWin->SetAccessibleName( m_pToolBar->GetItemText( nId ) ); + m_pImpl->SetItemWindow( nId, pItemWin ); + } + } + } + } + + //for update Controller via support visible state + Reference< XPropertySet > xPropSet( xController, UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + bool bSupportVisible = true; + Any a( xPropSet->getPropertyValue("SupportsVisible") ); + a >>= bSupportVisible; + if (bSupportVisible) + { + Reference< XToolbarController > xTbxController( xController, UNO_QUERY ); + UpdateController(xTbxController); + } + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + } + } + + AddFrameActionListener(); +} + +void ToolBarManager::AddFrameActionListener() +{ + if ( !m_bFrameActionRegistered && m_xFrame.is() ) + { + m_bFrameActionRegistered = true; + m_xFrame->addFrameActionListener( Reference< XFrameActionListener >(this) ); + } +} + +ToolBoxItemBits ToolBarManager::ConvertStyleToToolboxItemBits( sal_Int32 nStyle ) +{ + ToolBoxItemBits nItemBits( ToolBoxItemBits::NONE ); + if ( nStyle & css::ui::ItemStyle::RADIO_CHECK ) + nItemBits |= ToolBoxItemBits::RADIOCHECK; + if ( nStyle & css::ui::ItemStyle::ALIGN_LEFT ) + nItemBits |= ToolBoxItemBits::LEFT; + if ( nStyle & css::ui::ItemStyle::AUTO_SIZE ) + nItemBits |= ToolBoxItemBits::AUTOSIZE; + if ( nStyle & css::ui::ItemStyle::DROP_DOWN ) + nItemBits |= ToolBoxItemBits::DROPDOWN; + if ( nStyle & css::ui::ItemStyle::REPEAT ) + nItemBits |= ToolBoxItemBits::REPEAT; + if ( nStyle & css::ui::ItemStyle::DROPDOWN_ONLY ) + nItemBits |= ToolBoxItemBits::DROPDOWNONLY; + if ( nStyle & css::ui::ItemStyle::TEXT ) + nItemBits |= ToolBoxItemBits::TEXT_ONLY; + if ( nStyle & css::ui::ItemStyle::ICON ) + nItemBits |= ToolBoxItemBits::ICON_ONLY; + + return nItemBits; +} + +void ToolBarManager::InitImageManager() +{ + Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext ); + if ( !m_xDocImageManager.is() ) + { + Reference< XModel > xModel( GetModelFromFrame() ); + if ( xModel.is() ) + { + Reference< XUIConfigurationManagerSupplier > xSupplier( xModel, UNO_QUERY ); + if ( xSupplier.is() ) + { + Reference< XUIConfigurationManager > xDocUICfgMgr = xSupplier->getUIConfigurationManager(); + m_xDocImageManager.set( xDocUICfgMgr->getImageManager(), UNO_QUERY ); + m_xDocImageManager->addConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + } + } + + try + { + m_aModuleIdentifier = xModuleManager->identify( Reference< XInterface >( m_xFrame, UNO_QUERY ) ); + } + catch (const Exception&) + { + } + + if ( !m_xModuleImageManager.is() ) + { + Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier = + theModuleUIConfigurationManagerSupplier::get( m_xContext ); + Reference< XUIConfigurationManager > xUICfgMgr = xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleIdentifier ); + m_xModuleImageManager.set( xUICfgMgr->getImageManager(), UNO_QUERY ); + m_xModuleImageManager->addConfigurationListener( Reference< XUIConfigurationListener >(this) ); + } +} + +void ToolBarManager::FillToolbar( const Reference< XIndexAccess >& rItemContainer ) +{ + OString aTbxName = OUStringToOString( m_aResourceName, RTL_TEXTENCODING_ASCII_US ); + SAL_INFO( "fwk.uielement", "framework (cd100003) ::ToolBarManager::FillToolbar " << aTbxName ); + + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + InitImageManager(); + + RemoveControllers(); + + // reset and fill command map + m_pImpl->Clear(); + m_aControllerMap.clear(); + m_aCommandMap.clear(); + + ToolBoxItemId nId( 1 ); + CommandInfo aCmdInfo; + for ( sal_Int32 n = 0; n < rItemContainer->getCount(); n++ ) + { + Sequence< PropertyValue > aProps; + OUString aCommandURL; + OUString aLabel; + OUString aTooltip; + sal_uInt16 nType( css::ui::ItemType::DEFAULT ); + sal_uInt32 nStyle( 0 ); + + try + { + if ( rItemContainer->getByIndex( n ) >>= aProps ) + { + bool bIsVisible( true ); + for ( PropertyValue const & prop : std::as_const(aProps) ) + { + if ( prop.Name == ITEM_DESCRIPTOR_COMMANDURL ) + prop.Value >>= aCommandURL; + else if ( prop.Name == "Label" ) + prop.Value >>= aLabel; + else if ( prop.Name == "Tooltip" ) + prop.Value >>= aTooltip; + else if ( prop.Name == "Type" ) + prop.Value >>= nType; + else if ( prop.Name == ITEM_DESCRIPTOR_VISIBLE ) + prop.Value >>= bIsVisible; + else if ( prop.Name == "Style" ) + prop.Value >>= nStyle; + } + + if (!aCommandURL.isEmpty() && vcl::CommandInfoProvider::IsExperimental(aCommandURL, m_aModuleIdentifier) && + !officecfg::Office::Common::Misc::ExperimentalMode::get()) + { + continue; + } + + if (( nType == css::ui::ItemType::DEFAULT ) && !aCommandURL.isEmpty() ) + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommandURL, m_aModuleIdentifier); + if (!aProperties.hasElements()) // E.g., user-provided macro command? + aProperties = aProps; // Use existing info, including user-provided Label + OUString aString(vcl::CommandInfoProvider::GetLabelForCommand(aProperties)); + + ToolBoxItemBits nItemBits = ConvertStyleToToolboxItemBits( nStyle ); + + if ( aTooltip.isEmpty() ) + aTooltip = vcl::CommandInfoProvider::GetTooltipForCommand(aCommandURL, aProperties, m_xFrame); + + if ( aLabel.isEmpty() ) + aLabel = aString; + + m_pImpl->InsertItem(nId, aString, aCommandURL, aTooltip, aLabel, nItemBits); + + // Fill command map. It stores all our commands and from what + // image manager we got our image. So we can decide if we have to use an + // image from a notification message. + auto pIter = m_aCommandMap.emplace( aCommandURL, aCmdInfo ); + if ( pIter.second ) + { + aCmdInfo.nId = nId; + pIter.first->second.nId = nId; + } + else + { + pIter.first->second.aIds.push_back( nId ); + } + + if ( !bIsVisible ) + m_pImpl->HideItem( nId, aCommandURL ); + + ++nId; + } + else if ( nType == css::ui::ItemType::SEPARATOR_LINE ) + { + m_pImpl->InsertSeparator(); + } + else if ( nType == css::ui::ItemType::SEPARATOR_SPACE ) + { + m_pImpl->InsertSpace(); + } + else if ( nType == css::ui::ItemType::SEPARATOR_LINEBREAK ) + { + m_pImpl->InsertBreak(); + } + } + } + catch (const css::lang::IndexOutOfBoundsException&) + { + break; + } + } + + // Support add-on toolbar merging here. Working directly on the toolbar object is much + // simpler and faster. + constexpr sal_uInt16 TOOLBAR_ITEM_STARTID = 1000; + + MergeToolbarInstructionContainer aMergeInstructionContainer; + + // Retrieve the toolbar name from the resource name + OUString aToolbarName( m_aResourceName ); + sal_Int32 nIndex = aToolbarName.lastIndexOf( '/' ); + if (( nIndex > 0 ) && ( nIndex < aToolbarName.getLength() )) + aToolbarName = aToolbarName.copy( nIndex+1 ); + + AddonsOptions().GetMergeToolbarInstructions( aToolbarName, aMergeInstructionContainer ); + + if ( !aMergeInstructionContainer.empty() ) + { + ToolBoxItemId nItemId( TOOLBAR_ITEM_STARTID ); + const sal_uInt32 nCount = aMergeInstructionContainer.size(); + for ( sal_uInt32 i=0; i < nCount; i++ ) + { + MergeToolbarInstruction& rInstruction = aMergeInstructionContainer[i]; + if ( ToolBarMerger::IsCorrectContext( rInstruction.aMergeContext, m_aModuleIdentifier )) + { + m_pImpl->MergeToolbar(nItemId, m_aModuleIdentifier, m_aCommandMap, rInstruction); + } + } + } + + // Request images for all toolbar items. Must be done before CreateControllers as + // some controllers need access to the image. + RequestImages(); + + // Create controllers after we set the images. There are controllers which needs + // an image at the toolbar at creation time! + CreateControllers(); + + // Notify controllers that they are now correctly initialized and can start listening + // toolbars that will open in popup mode will be updated immediately to avoid flickering + if( m_pImpl->WillUsePopupMode() ) + UpdateControllers(); + else if ( m_pImpl->IsReallyVisible() ) + { + m_aAsyncUpdateControllersTimer.Start(); + } + + // Try to retrieve UIName from the container property set and set it as the title + // if it is not empty. + Reference< XPropertySet > xPropSet( rItemContainer, UNO_QUERY ); + if ( !xPropSet.is() ) + return; + + try + { + OUString aUIName; + xPropSet->getPropertyValue("UIName") >>= aUIName; + if ( !aUIName.isEmpty() ) + m_pImpl->SetName( aUIName ); + } + catch (const Exception&) + { + } +} + +void ToolBarManager::FillAddonToolbar( const Sequence< Sequence< PropertyValue > >& rAddonToolbar ) +{ + if (!m_pToolBar) + return; + + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + InitImageManager(); + + RemoveControllers(); + + // reset and fill command map + m_pToolBar->Clear(); + m_aControllerMap.clear(); + m_aCommandMap.clear(); + + ToolBoxItemId nId( 1 ); + CommandInfo aCmdInfo; + for ( const Sequence< PropertyValue >& rSeq : rAddonToolbar ) + { + OUString aURL; + OUString aTitle; + OUString aContext; + OUString aTarget; + OUString aControlType; + sal_uInt16 nWidth( 0 ); + + ToolBarMerger::ConvertSequenceToValues( rSeq, aURL, aTitle, aTarget, aContext, aControlType, nWidth ); + + if ( ToolBarMerger::IsCorrectContext( aContext, m_aModuleIdentifier ) ) + { + if ( aURL == "private:separator" ) + { + ToolBox::ImplToolItems::size_type nCount = m_pToolBar->GetItemCount(); + if ( nCount > 0 && m_pToolBar->GetItemType( nCount-1 ) != ToolBoxItemType::SEPARATOR ) + m_pToolBar->InsertSeparator(); + } + else + { + m_pToolBar->InsertItem( nId, aTitle ); + + OUString aShortcut(vcl::CommandInfoProvider::GetCommandShortcut(aURL, m_xFrame)); + if (!aShortcut.isEmpty()) + m_pToolBar->SetQuickHelpText(nId, aTitle + " (" + aShortcut + ")"); + + // Create AddonsParams to hold additional information we will need in the future + AddonsParams* pRuntimeItemData = new AddonsParams; + pRuntimeItemData->aControlType = aControlType; + pRuntimeItemData->nWidth = nWidth; + m_pToolBar->SetItemData( nId, pRuntimeItemData ); + m_pToolBar->SetItemCommand( nId, aURL ); + + // Fill command map. It stores all our commands and from what + // image manager we got our image. So we can decide if we have to use an + // image from a notification message. + auto pIter = m_aCommandMap.emplace( aURL, aCmdInfo ); + if ( pIter.second ) + { + aCmdInfo.nId = nId; + pIter.first->second.nId = nId; + } + else + { + pIter.first->second.aIds.push_back( nId ); + } + ++nId; + } + } + } + + // Don't setup images yet, AddonsToolbarWrapper::populateImages does that. + // (But some controllers might need an image at the toolbar at creation time!) + CreateControllers(); + + // Notify controllers that they are now correctly initialized and can start listening. + UpdateControllers(); +} + +void ToolBarManager::FillOverflowToolbar( ToolBox const * pParent ) +{ + if (!m_pToolBar) + return; + + CommandInfo aCmdInfo; + bool bInsertSeparator = false; + for ( ToolBox::ImplToolItems::size_type i = 0; i < pParent->GetItemCount(); ++i ) + { + ToolBoxItemId nId = pParent->GetItemId( i ); + if ( pParent->IsItemClipped( nId ) ) + { + if ( bInsertSeparator ) + { + m_pToolBar->InsertSeparator(); + bInsertSeparator = false; + } + + const OUString aCommandURL( pParent->GetItemCommand( nId ) ); + m_pToolBar->InsertItem( nId, pParent->GetItemText( nId ) ); + m_pToolBar->SetItemCommand( nId, aCommandURL ); + m_pToolBar->SetQuickHelpText( nId, pParent->GetQuickHelpText( nId ) ); + + // Handle possible add-on controls. + AddonsParams* pAddonParams = static_cast< AddonsParams* >( pParent->GetItemData( nId ) ); + if ( pAddonParams ) + m_pToolBar->SetItemData( nId, new AddonsParams( *pAddonParams ) ); + + // Fill command map. It stores all our commands and from what + // image manager we got our image. So we can decide if we have to use an + // image from a notification message. + auto pIter = m_aCommandMap.emplace( aCommandURL, aCmdInfo ); + if ( pIter.second ) + { + aCmdInfo.nId = nId; + pIter.first->second.nId = nId; + } + else + { + pIter.first->second.aIds.push_back( nId ); + } + } + else + { + ToolBoxItemType eType = pParent->GetItemType( i ); + if ( m_pToolBar->GetItemCount() && + ( eType == ToolBoxItemType::SEPARATOR || eType == ToolBoxItemType::BREAK ) ) + bInsertSeparator = true; + } + } + + InitImageManager(); + + // Request images for all toolbar items. Must be done before CreateControllers as + // some controllers need access to the image. + RequestImages(); + + // Create controllers after we set the images. There are controllers which needs + // an image at the toolbar at creation time! + CreateControllers(); + + // Notify controllers that they are now correctly initialized and can start listening + // toolbars that will open in popup mode will be updated immediately to avoid flickering + UpdateControllers(); +} + +void ToolBarManager::RequestImages() +{ + + // Request images from image manager + Sequence< OUString > aCmdURLSeq( comphelper::mapKeysToSequence(m_aCommandMap) ); + Sequence< Reference< XGraphic > > aDocGraphicSeq; + Sequence< Reference< XGraphic > > aModGraphicSeq; + + SvtMiscOptions aMiscOptions; + + sal_Int16 nImageType = getCurrentImageType(); + + if ( m_xDocImageManager.is() ) + aDocGraphicSeq = m_xDocImageManager->getImages(nImageType, aCmdURLSeq); + aModGraphicSeq = m_xModuleImageManager->getImages(nImageType, aCmdURLSeq); + + sal_uInt32 i = 0; + CommandToInfoMap::iterator pIter = m_aCommandMap.begin(); + CommandToInfoMap::iterator pEnd = m_aCommandMap.end(); + while ( pIter != pEnd ) + { + Image aImage; + if ( aDocGraphicSeq.hasElements() ) + aImage = Image( aDocGraphicSeq[i] ); + if ( !aImage ) + { + aImage = Image( aModGraphicSeq[i] ); + // Try also to query for add-on images before giving up and use an + // empty image. + if ( !aImage ) + aImage = Image(framework::AddonsOptions().GetImageFromURL(aCmdURLSeq[i], aMiscOptions.AreCurrentSymbolsLarge())); + + pIter->second.nImageInfo = 1; // mark image as module based + } + else + { + pIter->second.nImageInfo = 0; // mark image as document based + } + setToolBarImage(aImage,pIter); + ++pIter; + ++i; + } +} + +void ToolBarManager::notifyRegisteredControllers( const OUString& aUIElementName, const OUString& aCommand ) +{ + SolarMutexClearableGuard aGuard; + if ( m_aSubToolBarControllerMap.empty() ) + return; + + SubToolBarToSubToolBarControllerMap::const_iterator pIter = + m_aSubToolBarControllerMap.find( aUIElementName ); + + if ( pIter == m_aSubToolBarControllerMap.end() ) + return; + + const SubToolBarControllerVector& rSubToolBarVector = pIter->second; + if ( rSubToolBarVector.empty() ) + return; + + SubToolBarControllerVector aNotifyVector = rSubToolBarVector; + aGuard.clear(); + + const sal_uInt32 nCount = aNotifyVector.size(); + for ( sal_uInt32 i=0; i < nCount; i++ ) + { + try + { + Reference< XSubToolbarController > xController = aNotifyVector[i]; + if ( xController.is() ) + xController->functionSelected( aCommand ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + } +} + +void ToolBarManager::HandleClick(ClickAction eClickAction) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + ToolBoxItemId nId( m_pImpl->GetCurItemId() ); + ToolBarControllerMap::const_iterator pIter = m_aControllerMap.find( nId ); + if ( pIter == m_aControllerMap.end() ) + return; + + Reference< XToolbarController > xController( pIter->second, UNO_QUERY ); + + if ( xController.is() ) + { + switch (eClickAction) + { + case ClickAction::Click: + xController->click(); + break; + + case ClickAction::DblClick: + xController->doubleClick(); + break; + + case ClickAction::Execute: + xController->execute(0); + break; + } + } +} + +void ToolBarManager::OnClick(bool bUseExecute) +{ + if (bUseExecute) + HandleClick(ClickAction::Execute); + else + HandleClick(ClickAction::Click); +} + +IMPL_LINK_NOARG(ToolBarManager, DropdownClick, ToolBox *, void) +{ + OnDropdownClick(true); +} + +void ToolBarManager::OnDropdownClick(bool bCreatePopupWindow) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + ToolBoxItemId nId( m_pImpl->GetCurItemId() ); + ToolBarControllerMap::const_iterator pIter = m_aControllerMap.find( nId ); + if ( pIter == m_aControllerMap.end() ) + return; + + Reference< XToolbarController > xController( pIter->second, UNO_QUERY ); + + if ( xController.is() ) + { + if (bCreatePopupWindow) + { + Reference< XWindow > xWin = xController->createPopupWindow(); + if ( xWin.is() ) + xWin->setFocus(); + } + else + { + xController->click(); + } + } +} + +IMPL_LINK_NOARG(ToolBarManager, DoubleClick, ToolBox *, void) +{ + HandleClick(ClickAction::DblClick); +} + +Reference< XModel > ToolBarManager::GetModelFromFrame() const +{ + Reference< XController > xController = m_xFrame->getController(); + Reference< XModel > xModel; + if ( xController.is() ) + xModel = xController->getModel(); + + return xModel; +} + +bool ToolBarManager::IsPluginMode() const +{ + bool bPluginMode( false ); + + if ( m_xFrame.is() ) + { + Reference< XModel > xModel = GetModelFromFrame(); + if ( xModel.is() ) + { + Sequence< PropertyValue > aSeq = xModel->getArgs(); + utl::MediaDescriptor aMediaDescriptor( aSeq ); + bPluginMode = aMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_VIEWONLY, false ); + } + } + + return bPluginMode; +} + +void ToolBarManager::AddCustomizeMenuItems(ToolBox const * pToolBar) +{ + if (!m_pToolBar) + return; + + // No config menu entries if command ".uno:ConfigureDialog" is not enabled + Reference< XDispatch > xDisp; + css::util::URL aURL; + if ( m_xFrame.is() ) + { + Reference< XDispatchProvider > xProv( m_xFrame, UNO_QUERY ); + aURL.Complete = ".uno:ConfigureDialog"; + m_xURLTransformer->parseStrict( aURL ); + if ( xProv.is() ) + xDisp = xProv->queryDispatch( aURL, OUString(), 0 ); + + if ( !xDisp.is() || IsPluginMode() ) + return; + } + + // popup menu for quick customization + bool bHideDisabledEntries = !officecfg::Office::Common::View::Menu::DontHideDisabledEntry::get(); + + ::PopupMenu *pMenu = pToolBar->GetMenu(); + + // copy all menu items 'Visible buttons, Customize toolbar, Dock toolbar, + // Dock all Toolbars) from the loaded resource into the toolbar menu + sal_uInt16 nGroupLen = pMenu->GetItemCount(); + if (nGroupLen) + pMenu->InsertSeparator(); + + VclPtr<PopupMenu> xVisibleItemsPopupMenu; + + if (!m_aResourceName.startsWith("private:resource/toolbar/addon_")) + { + pMenu->InsertItem(MENUITEM_TOOLBAR_VISIBLEBUTTON, FwkResId(STR_TOOLBAR_VISIBLE_BUTTONS)); + xVisibleItemsPopupMenu = VclPtr<PopupMenu>::Create(); + pMenu->SetPopupMenu(MENUITEM_TOOLBAR_VISIBLEBUTTON, xVisibleItemsPopupMenu); + + if (m_pToolBar->IsCustomize()) + { + pMenu->InsertItem(MENUITEM_TOOLBAR_CUSTOMIZETOOLBAR, FwkResId(STR_TOOLBAR_CUSTOMIZE_TOOLBAR)); + pMenu->SetItemCommand(MENUITEM_TOOLBAR_CUSTOMIZETOOLBAR, ".uno:ConfigureToolboxVisible"); + } + pMenu->InsertSeparator(); + } + + if (pToolBar->IsFloatingMode()) + { + pMenu->InsertItem(MENUITEM_TOOLBAR_DOCKTOOLBAR, FwkResId(STR_TOOLBAR_DOCK_TOOLBAR)); + pMenu->SetAccelKey(MENUITEM_TOOLBAR_DOCKTOOLBAR, vcl::KeyCode(KEY_F10, true, true, false, false)); + } + else + { + pMenu->InsertItem(MENUITEM_TOOLBAR_UNDOCKTOOLBAR, FwkResId(STR_TOOLBAR_UNDOCK_TOOLBAR)); + pMenu->SetAccelKey(MENUITEM_TOOLBAR_UNDOCKTOOLBAR, vcl::KeyCode(KEY_F10, true, true, false, false)); + } + + pMenu->InsertItem(MENUITEM_TOOLBAR_DOCKALLTOOLBAR, FwkResId(STR_TOOLBAR_DOCK_ALL_TOOLBARS)); + pMenu->InsertSeparator(); + pMenu->InsertItem(MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION, FwkResId(STR_TOOLBAR_LOCK_TOOLBAR), MenuItemBits::CHECKABLE); + pMenu->InsertItem(MENUITEM_TOOLBAR_CLOSE, FwkResId(STR_TOOLBAR_CLOSE_TOOLBAR)); + + if (m_pToolBar->IsCustomize()) + { + bool bIsFloating( false ); + + DockingManager* pDockMgr = vcl::Window::GetDockingManager(); + if ( pDockMgr ) + bIsFloating = pDockMgr->IsFloating( m_pToolBar ); + + if ( !bIsFloating ) + { + pMenu->EnableItem(MENUITEM_TOOLBAR_DOCKALLTOOLBAR, false); + Reference< XDockableWindow > xDockable( VCLUnoHelper::GetInterface( m_pToolBar ), UNO_QUERY ); + if( xDockable.is() ) + pMenu->CheckItem(MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION, xDockable->isLocked()); + } + else + pMenu->EnableItem(MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION, false); + + if (officecfg::Office::Common::Misc::DisableUICustomization::get()) + { + pMenu->EnableItem(MENUITEM_TOOLBAR_VISIBLEBUTTON, false); + pMenu->EnableItem(MENUITEM_TOOLBAR_CUSTOMIZETOOLBAR, false); + pMenu->EnableItem(MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION, false); + } + + // Disable menu item CLOSE if the toolbar has no closer + if( !(pToolBar->GetFloatStyle() & WB_CLOSEABLE) ) + pMenu->EnableItem(MENUITEM_TOOLBAR_CLOSE, false); + + // Temporary stores a Command --> Url map to update contextual menu with the + // correct icons. The popup icons are by default the same as those in the + // toolbar. They are not correct for contextual popup menu. + std::map< OUString, Image > commandToImage; + + if (xVisibleItemsPopupMenu) + { + // Go through all toolbar items and add them to the context menu + for ( ToolBox::ImplToolItems::size_type nPos = 0; nPos < m_pToolBar->GetItemCount(); ++nPos ) + { + if ( m_pToolBar->GetItemType(nPos) == ToolBoxItemType::BUTTON ) + { + ToolBoxItemId nId = m_pToolBar->GetItemId(nPos); + OUString aCommandURL = m_pToolBar->GetItemCommand( nId ); + xVisibleItemsPopupMenu->InsertItem( STARTID_CUSTOMIZE_POPUPMENU+nPos, m_pToolBar->GetItemText( nId ), MenuItemBits::CHECKABLE ); + xVisibleItemsPopupMenu->CheckItem( STARTID_CUSTOMIZE_POPUPMENU+nPos, m_pToolBar->IsItemVisible( nId ) ); + xVisibleItemsPopupMenu->SetItemCommand( STARTID_CUSTOMIZE_POPUPMENU+nPos, aCommandURL ); + Image aImage(vcl::CommandInfoProvider::GetImageForCommand(aCommandURL, m_xFrame)); + commandToImage[aCommandURL] = aImage; + xVisibleItemsPopupMenu->SetItemImage( STARTID_CUSTOMIZE_POPUPMENU+nPos, aImage ); + vcl::KeyCode aKeyCodeShortCut = vcl::CommandInfoProvider::GetCommandKeyCodeShortcut( aCommandURL, m_xFrame ); + xVisibleItemsPopupMenu->SetAccelKey( STARTID_CUSTOMIZE_POPUPMENU+nPos, aKeyCodeShortCut ); + } + else + { + xVisibleItemsPopupMenu->InsertSeparator(); + } + } + } + + // Now we go through all the contextual menu to update the icons + // and accelerator key shortcuts + std::map< OUString, Image >::iterator it; + for ( sal_uInt16 nPos = 0; nPos < pMenu->GetItemCount(); ++nPos ) + { + sal_uInt16 nId = pMenu->GetItemId( nPos ); + OUString cmdUrl = pMenu->GetItemCommand( nId ); + it = commandToImage.find( cmdUrl ); + if (it != commandToImage.end()) { + pMenu->SetItemImage( nId, it->second ); + } + vcl::KeyCode aKeyCodeShortCut = vcl::CommandInfoProvider::GetCommandKeyCodeShortcut( cmdUrl, m_xFrame ); + if ( aKeyCodeShortCut.GetFullCode() != 0 ) + pMenu->SetAccelKey( nId, aKeyCodeShortCut ); + } + } + + // Set the title of the menu + pMenu->SetText( pToolBar->GetText() ); + + if ( bHideDisabledEntries ) + pMenu->RemoveDisabledEntries(); +} + +IMPL_LINK( ToolBarManager, MenuButton, ToolBox*, pToolBar, void ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + assert( !m_aOverflowManager.is() ); + + VclPtrInstance<ToolBox> pOverflowToolBar( pToolBar, WB_BORDER | WB_SCROLL ); + pOverflowToolBar->SetLineSpacing(true); + m_aOverflowManager.set( new ToolBarManager( m_xContext, m_xFrame, OUString(), pOverflowToolBar ) ); + m_aOverflowManager->FillOverflowToolbar( pToolBar ); + + ::Size aActSize( pOverflowToolBar->GetSizePixel() ); + ::Size aSize( pOverflowToolBar->CalcWindowSizePixel() ); + aSize.setWidth( aActSize.Width() ); + pOverflowToolBar->SetOutputSizePixel( aSize ); + + aSize = pOverflowToolBar->CalcPopupWindowSizePixel(); + pOverflowToolBar->SetSizePixel( aSize ); + + pOverflowToolBar->EnableDocking(); + pOverflowToolBar->AddEventListener( LINK( this, ToolBarManager, OverflowEventListener ) ); + vcl::Window::GetDockingManager()->StartPopupMode( pToolBar, pOverflowToolBar, FloatWinPopupFlags::AllMouseButtonClose ); + + // send HOME key to subtoolbar in order to select first item if keyboard activated + if(pToolBar->IsKeyEvent() ) + { + ::KeyEvent aEvent( 0, vcl::KeyCode( KEY_HOME ) ); + pOverflowToolBar->KeyInput(aEvent); + } +} + +IMPL_LINK( ToolBarManager, OverflowEventListener, VclWindowEvent&, rWindowEvent, void ) +{ + if ( rWindowEvent.GetId() != VclEventId::WindowEndPopupMode ) + return; + + if ( m_aOverflowManager.is() ) + { + m_aOverflowManager->dispose(); + m_aOverflowManager.clear(); + } +} + +IMPL_LINK( ToolBarManager, MenuPreExecute, ToolBox*, pToolBar, void ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + AddCustomizeMenuItems( pToolBar ); +} + +IMPL_LINK( ToolBarManager, MenuSelect, Menu*, pMenu, bool ) +{ + // We have to hold a reference to ourself as it is possible that we will be disposed and + // our refcount could be zero (destruction) otherwise. + Reference< XInterface > xKeepAlive( static_cast< OWeakObject* >( this ), UNO_QUERY ); + + { + // The guard must be in its own context as the we can get destroyed when our + // own xInterface reference get destroyed! + SolarMutexGuard g; + + if ( m_bDisposed ) + return true; + + switch ( pMenu->GetCurItemId() ) + { + case MENUITEM_TOOLBAR_CUSTOMIZETOOLBAR: + { + Reference< XDispatch > xDisp; + css::util::URL aURL; + if ( m_xFrame.is() ) + { + Reference< XDispatchProvider > xProv( m_xFrame, UNO_QUERY ); + aURL.Complete = ".uno:ConfigureDialog"; + m_xURLTransformer->parseStrict( aURL ); + if ( xProv.is() ) + xDisp = xProv->queryDispatch( aURL, OUString(), 0 ); + } + + if ( xDisp.is() ) + { + Sequence< PropertyValue > aPropSeq{ comphelper::makePropertyValue( + "ResourceURL", m_aResourceName) }; + + xDisp->dispatch( aURL, aPropSeq ); + } + break; + } + + case MENUITEM_TOOLBAR_UNDOCKTOOLBAR: + { + ExecuteInfo* pExecuteInfo = new ExecuteInfo; + + pExecuteInfo->aToolbarResName = m_aResourceName; + pExecuteInfo->nCmd = EXEC_CMD_UNDOCKTOOLBAR; + pExecuteInfo->xLayoutManager = getLayoutManagerFromFrame( m_xFrame ); + + Application::PostUserEvent( LINK(nullptr, ToolBarManager, ExecuteHdl_Impl), pExecuteInfo ); + break; + } + + case MENUITEM_TOOLBAR_DOCKTOOLBAR: + { + ExecuteInfo* pExecuteInfo = new ExecuteInfo; + + pExecuteInfo->aToolbarResName = m_aResourceName; + pExecuteInfo->nCmd = EXEC_CMD_DOCKTOOLBAR; + pExecuteInfo->xLayoutManager = getLayoutManagerFromFrame( m_xFrame ); + + Application::PostUserEvent( LINK(nullptr, ToolBarManager, ExecuteHdl_Impl), pExecuteInfo ); + break; + } + + case MENUITEM_TOOLBAR_DOCKALLTOOLBAR: + { + ExecuteInfo* pExecuteInfo = new ExecuteInfo; + + pExecuteInfo->aToolbarResName = m_aResourceName; + pExecuteInfo->nCmd = EXEC_CMD_DOCKALLTOOLBARS; + pExecuteInfo->xLayoutManager = getLayoutManagerFromFrame( m_xFrame ); + + Application::PostUserEvent( LINK(nullptr, ToolBarManager, ExecuteHdl_Impl), pExecuteInfo ); + break; + } + + case MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION: + { + Reference< XLayoutManager > xLayoutManager = getLayoutManagerFromFrame( m_xFrame ); + if ( xLayoutManager.is() ) + { + Reference< XDockableWindow > xDockable( VCLUnoHelper::GetInterface( m_pToolBar ), UNO_QUERY ); + + if( xDockable->isLocked() ) + xLayoutManager->unlockWindow( m_aResourceName ); + else + xLayoutManager->lockWindow( m_aResourceName ); + } + break; + } + + case MENUITEM_TOOLBAR_CLOSE: + { + ExecuteInfo* pExecuteInfo = new ExecuteInfo; + + pExecuteInfo->aToolbarResName = m_aResourceName; + pExecuteInfo->nCmd = EXEC_CMD_CLOSETOOLBAR; + pExecuteInfo->xLayoutManager = getLayoutManagerFromFrame( m_xFrame ); + pExecuteInfo->xWindow = VCLUnoHelper::GetInterface( m_pToolBar ); + + Application::PostUserEvent( LINK(nullptr, ToolBarManager, ExecuteHdl_Impl), pExecuteInfo ); + break; + } + + default: + { + sal_uInt16 nId = pMenu->GetCurItemId(); + if(( nId > 0 ) && ( nId < TOOLBOX_MENUITEM_START )) + // Items in the "enable/disable" sub-menu + { + // toggle toolbar button visibility + OUString aCommand = pMenu->GetItemCommand( nId ); + + Reference< XLayoutManager > xLayoutManager = getLayoutManagerFromFrame( m_xFrame ); + if ( xLayoutManager.is() ) + { + Reference< XUIElementSettings > xUIElementSettings( xLayoutManager->getElement( m_aResourceName ), UNO_QUERY ); + if ( xUIElementSettings.is() ) + { + Reference< XIndexContainer > xItemContainer( xUIElementSettings->getSettings( true ), UNO_QUERY ); + sal_Int32 nCount = xItemContainer->getCount(); + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + Sequence< PropertyValue > aProp; + sal_Int32 nVisibleIndex( -1 ); + OUString aCommandURL; + bool bVisible( false ); + + if ( xItemContainer->getByIndex( i ) >>= aProp ) + { + for ( sal_Int32 j = 0; j < aProp.getLength(); j++ ) + { + if ( aProp[j].Name == ITEM_DESCRIPTOR_COMMANDURL ) + { + aProp[j].Value >>= aCommandURL; + } + else if ( aProp[j].Name == ITEM_DESCRIPTOR_VISIBLE ) + { + aProp[j].Value >>= bVisible; + nVisibleIndex = j; + } + } + + if (( aCommandURL == aCommand ) && ( nVisibleIndex >= 0 )) + { + // We have found the requested item, toggle the visible flag + // and write back the configuration settings to the toolbar + aProp.getArray()[nVisibleIndex].Value <<= !bVisible; + try + { + xItemContainer->replaceByIndex( i, Any( aProp )); + xUIElementSettings->setSettings( xItemContainer ); + Reference< XPropertySet > xPropSet( xUIElementSettings, UNO_QUERY ); + if ( xPropSet.is() ) + { + Reference< XUIConfigurationPersistence > xUICfgMgr; + if (( xPropSet->getPropertyValue("ConfigurationSource") >>= xUICfgMgr ) && ( xUICfgMgr.is() )) + xUICfgMgr->store(); + } + } + catch (const Exception&) + { + } + + break; + } + } + } + } + } + } + break; + } + } + } + + return true; +} + +IMPL_LINK_NOARG(ToolBarManager, Select, ToolBox *, void) +{ + if ( m_bDisposed ) + return; + + sal_Int16 nKeyModifier( static_cast<sal_Int16>(m_pToolBar->GetModifier()) ); + ToolBoxItemId nId( m_pToolBar->GetCurItemId() ); + + ToolBarControllerMap::const_iterator pIter = m_aControllerMap.find( nId ); + if ( pIter != m_aControllerMap.end() ) + { + Reference< XToolbarController > xController( pIter->second, UNO_QUERY ); + + if ( xController.is() ) + xController->execute( nKeyModifier ); + } +} + +IMPL_LINK( ToolBarManager, StateChanged, StateChangedType const *, pStateChangedType, void ) +{ + if ( m_bDisposed ) + return; + + if ( *pStateChangedType == StateChangedType::ControlBackground ) + { + CheckAndUpdateImages(); + } + else if ( *pStateChangedType == StateChangedType::Visible ) + { + if ( m_pToolBar->IsReallyVisible() ) + { + m_aAsyncUpdateControllersTimer.Start(); + } + } + else if ( *pStateChangedType == StateChangedType::InitShow ) + { + m_aAsyncUpdateControllersTimer.Start(); + } +} + +IMPL_LINK( ToolBarManager, DataChanged, DataChangedEvent const *, pDataChangedEvent, void ) +{ + if ((( pDataChangedEvent->GetType() == DataChangedEventType::SETTINGS ) || + ( pDataChangedEvent->GetType() == DataChangedEventType::DISPLAY )) && + ( pDataChangedEvent->GetFlags() & AllSettingsFlags::STYLE )) + { + CheckAndUpdateImages(); + } + + for ( ToolBox::ImplToolItems::size_type nPos = 0; nPos < m_pToolBar->GetItemCount(); ++nPos ) + { + const ToolBoxItemId nId = m_pToolBar->GetItemId(nPos); + vcl::Window* pWindow = m_pToolBar->GetItemWindow( nId ); + if ( pWindow ) + { + const DataChangedEvent& rDCEvt( *pDataChangedEvent ); + pWindow->DataChanged( rDCEvt ); + } + } + + if ( !m_pToolBar->IsFloatingMode() && + m_pToolBar->IsVisible() ) + { + // Resize toolbar, layout manager is resize listener and will calc + // the layout automatically. + ::Size aSize( m_pToolBar->CalcWindowSizePixel() ); + m_pToolBar->SetOutputSizePixel( aSize ); + } +} + +IMPL_LINK_NOARG(ToolBarManager, MiscOptionsChanged, LinkParamNone*, void) +{ + CheckAndUpdateImages(); +} + +IMPL_LINK_NOARG(ToolBarManager, AsyncUpdateControllersHdl, Timer *, void) +{ + // The guard must be in its own context as the we can get destroyed when our + // own xInterface reference get destroyed! + Reference< XComponent > xThis(this); + + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + // Request to update our controllers + m_aAsyncUpdateControllersTimer.Stop(); + UpdateControllers(); +} + +IMPL_STATIC_LINK( ToolBarManager, ExecuteHdl_Impl, void*, p, void ) +{ + ExecuteInfo* pExecuteInfo = static_cast<ExecuteInfo*>(p); + try + { + // Asynchronous execution as this can lead to our own destruction! + if (( pExecuteInfo->nCmd == EXEC_CMD_CLOSETOOLBAR ) && + ( pExecuteInfo->xLayoutManager.is() ) && + ( pExecuteInfo->xWindow.is() )) + { + // Use docking window close to close the toolbar. The toolbar layout manager is + // listener and will react correctly according to the context sensitive + // flag of our toolbar. + VclPtr<vcl::Window> pWin = VCLUnoHelper::GetWindow( pExecuteInfo->xWindow ); + DockingWindow* pDockWin = dynamic_cast< DockingWindow* >( pWin.get() ); + if ( pDockWin ) + pDockWin->Close(); + } + else if (( pExecuteInfo->nCmd == EXEC_CMD_UNDOCKTOOLBAR ) && + ( pExecuteInfo->xLayoutManager.is() )) + { + pExecuteInfo->xLayoutManager->floatWindow( pExecuteInfo->aToolbarResName ); + } + else if (( pExecuteInfo->nCmd == EXEC_CMD_DOCKTOOLBAR ) && + ( pExecuteInfo->xLayoutManager.is() )) + { + css::awt::Point aPoint; + aPoint.X = aPoint.Y = SAL_MAX_INT32; + pExecuteInfo->xLayoutManager->dockWindow( pExecuteInfo->aToolbarResName, + DockingArea_DOCKINGAREA_DEFAULT, + aPoint ); + } + else if (( pExecuteInfo->nCmd == EXEC_CMD_DOCKALLTOOLBARS ) && + ( pExecuteInfo->xLayoutManager.is() )) + { + pExecuteInfo->xLayoutManager->dockAllWindows( UIElementType::TOOLBAR ); + } + } + catch (const Exception&) + { + } + + delete pExecuteInfo; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/toolbarmerger.cxx b/framework/source/uielement/toolbarmerger.cxx new file mode 100644 index 000000000..2657873c3 --- /dev/null +++ b/framework/source/uielement/toolbarmerger.cxx @@ -0,0 +1,645 @@ +/* -*- 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 <uielement/toolbarmerger.hxx> +#include <framework/generictoolbarcontroller.hxx> + +#include <uielement/buttontoolbarcontroller.hxx> +#include <uielement/comboboxtoolbarcontroller.hxx> +#include <uielement/dropdownboxtoolbarcontroller.hxx> +#include <uielement/edittoolbarcontroller.hxx> +#include <uielement/imagebuttontoolbarcontroller.hxx> +#include <uielement/spinfieldtoolbarcontroller.hxx> +#include <uielement/togglebuttontoolbarcontroller.hxx> +#include <uielement/FixedTextToolbarController.hxx> +#include <uielement/FixedImageToolbarController.hxx> +#include <o3tl/string_view.hxx> + +namespace framework +{ + +const char MERGE_TOOLBAR_URL[] = "URL"; +const char MERGE_TOOLBAR_TITLE[] = "Title"; +const char MERGE_TOOLBAR_CONTEXT[] = "Context"; +const char MERGE_TOOLBAR_TARGET[] = "Target"; +const char MERGE_TOOLBAR_CONTROLTYPE[] = "ControlType"; +const char MERGE_TOOLBAR_WIDTH[] = "Width"; + +const char16_t MERGECOMMAND_ADDAFTER[] = u"AddAfter"; +const char16_t MERGECOMMAND_ADDBEFORE[] = u"AddBefore"; +const char16_t MERGECOMMAND_REPLACE[] = u"Replace"; +const char16_t MERGECOMMAND_REMOVE[] = u"Remove"; + +const char16_t MERGEFALLBACK_ADDLAST[] = u"AddLast"; +const char16_t MERGEFALLBACK_ADDFIRST[] = u"AddFirst"; +const char16_t MERGEFALLBACK_IGNORE[] = u"Ignore"; + +const char16_t TOOLBARCONTROLLER_BUTTON[] = u"Button"; +const char16_t TOOLBARCONTROLLER_COMBOBOX[] = u"Combobox"; +const char16_t TOOLBARCONTROLLER_EDIT[] = u"Editfield"; +const char16_t TOOLBARCONTROLLER_SPINFIELD[] = u"Spinfield"; +const char16_t TOOLBARCONTROLLER_IMGBUTTON[] = u"ImageButton"; +const char16_t TOOLBARCONTROLLER_DROPDOWNBOX[] = u"Dropdownbox"; +const char16_t TOOLBARCONTROLLER_DROPDOWNBTN[] = u"DropdownButton"; +const char16_t TOOLBARCONTROLLER_TOGGLEDDBTN[] = u"ToggleDropdownButton"; +const char16_t TOOLBARCONTROLLER_FIXEDIMAGE[] = u"FixedImage"; +const char16_t TOOLBARCONTROLLER_FIXEDTEXT[] = u"FixedText"; + +const char TOOLBOXITEM_SEPARATOR_STR[] = "private:separator"; + +using namespace ::com::sun::star; + +/** + Check whether a module identifier is part of a context + defined by a colon separated list of module identifier. + + @param + rContext + + Describes a context string list where all contexts + are delimited by a colon. For more information about + the module identifier used as context strings see the + IDL description of css::frame::XModuleManager + + @param + rModuleIdentifier + + A string describing a module identifier. See IDL + description of css::frame::XModuleManager. + + @result + The result is true if the rContext is an empty string + or rModuleIdentifier is part of the context string. + +*/ +bool ToolBarMerger::IsCorrectContext( + std::u16string_view rContext, + std::u16string_view rModuleIdentifier ) +{ + return ( rContext.empty() || ( rContext.find( rModuleIdentifier ) != std::u16string_view::npos )); +} + +/** + Converts a sequence, sequence of property values to + a vector of structs. + + @param + rSequence + + Provides a sequence, sequence of property values. + + @param + rContainer + + A vector of AddonToolbarItems which will hold the + conversion from the rSequence argument. +*/ +void ToolBarMerger::ConvertSeqSeqToVector( + const uno::Sequence< uno::Sequence< beans::PropertyValue > >& rSequence, + AddonToolbarItemContainer& rContainer ) +{ + sal_Int32 nLen( rSequence.getLength() ); + for ( sal_Int32 i = 0; i < nLen; i++ ) + { + AddonToolbarItem aAddonToolbarItem; + ConvertSequenceToValues( rSequence[i], + aAddonToolbarItem.aCommandURL, + aAddonToolbarItem.aLabel, + aAddonToolbarItem.aTarget, + aAddonToolbarItem.aContext, + aAddonToolbarItem.aControlType, + aAddonToolbarItem.nWidth ); + rContainer.push_back( aAddonToolbarItem ); + } +} + +/** + Converts a sequence of property values to single + values. + + @param + rSequence + + Provides a sequence of property values. + + @param + rCommandURL + + Contains the value of the property with + Name="CommandURL". + + @param + rLabel + + Contains the value of the property with + Name="Title" + + @param + rTarget + + Contains the value of the property with + Name="Target" + + @param + rContext + + Contains the value of the property with + Name="Context" + + @param + rControlType + + Contains the value of the property with + Name="ControlType" + + @result + All possible mapping between sequence of property + values and the single values are done. + +*/ +void ToolBarMerger::ConvertSequenceToValues( + const uno::Sequence< beans::PropertyValue >& rSequence, + OUString& rCommandURL, + OUString& rLabel, + OUString& rTarget, + OUString& rContext, + OUString& rControlType, + sal_uInt16& rWidth ) +{ + for ( beans::PropertyValue const & prop : rSequence ) + { + if ( prop.Name == MERGE_TOOLBAR_URL ) + prop.Value >>= rCommandURL; + else if ( prop.Name == MERGE_TOOLBAR_TITLE ) + prop.Value >>= rLabel; + else if ( prop.Name == MERGE_TOOLBAR_CONTEXT ) + prop.Value >>= rContext; + else if ( prop.Name == MERGE_TOOLBAR_TARGET ) + prop.Value >>= rTarget; + else if ( prop.Name == MERGE_TOOLBAR_CONTROLTYPE ) + prop.Value >>= rControlType; + else if ( prop.Name == MERGE_TOOLBAR_WIDTH ) + { + sal_Int32 aValue = 0; + prop.Value >>= aValue; + rWidth = sal_uInt16( aValue ); + } + } +} + +/** + Tries to find the reference point provided and delivers + position and result of the search process. + + @param + pToolbar + + Must be a valid pointer to a toolbar with items which + should be searched. + + @param + rReferencePoint + + A command URL which should be the reference point for + the coming merge operation. + + @result + Provides information about the search result, the + position of the reference point and the toolbar used. +*/ +ReferenceToolbarPathInfo ToolBarMerger::FindReferencePoint( + const ToolBox* pToolbar, + std::u16string_view rReferencePoint ) +{ + ReferenceToolbarPathInfo aResult; + aResult.bResult = false; + aResult.nPos = ToolBox::ITEM_NOTFOUND; + + const ToolBox::ImplToolItems::size_type nSize( pToolbar->GetItemCount() ); + + for ( ToolBox::ImplToolItems::size_type i = 0; i < nSize; i++ ) + { + const ToolBoxItemId nItemId = pToolbar->GetItemId( i ); + if ( nItemId > ToolBoxItemId(0) ) + { + const OUString rCmd = pToolbar->GetItemCommand( nItemId ); + if ( rCmd == rReferencePoint ) + { + aResult.bResult = true; + aResult.nPos = i; + return aResult; + } + } + } + + return aResult; +} + +/** + Processes a merge operation. + + @param + pToolbar + + A valid pointer to the toolbar where the merge + operation is applied to. + + @param + nPos + + The reference position of the toolbar item for + the merge operation. Value must be between + 0 and number of toolbar items - 1. + + @param + rItemId + + A unique item ID. + + @param + rModuleIdentifier + + The current application module context. + + @param + rMergeCommand + + A merge command. + + @param + rMergeCommandParameter. + + An optional argument for the merge command. + + @param + rItems + + Toolbar items which are associated to the merge + command. + + @result + Returns true for a successful operation otherwise + false. +*/ +bool ToolBarMerger::ProcessMergeOperation( + ToolBox* pToolbar, + ToolBox::ImplToolItems::size_type nPos, + ToolBoxItemId& rItemId, + CommandToInfoMap& rCommandMap, + std::u16string_view rModuleIdentifier, + std::u16string_view rMergeCommand, + std::u16string_view rMergeCommandParameter, + const AddonToolbarItemContainer& rItems ) +{ + if ( rMergeCommand == MERGECOMMAND_ADDAFTER ) + MergeItems( pToolbar, nPos, 1, rItemId, rCommandMap, rModuleIdentifier, rItems ); + else if ( rMergeCommand == MERGECOMMAND_ADDBEFORE ) + MergeItems( pToolbar, nPos, 0, rItemId, rCommandMap, rModuleIdentifier, rItems ); + else if ( rMergeCommand == MERGECOMMAND_REPLACE ) + ReplaceItem( pToolbar, nPos, rItemId, rCommandMap, rModuleIdentifier, rItems ); + else if ( rMergeCommand == MERGECOMMAND_REMOVE ) + RemoveItems( pToolbar, nPos, rMergeCommandParameter ); + else + return false; + return true; +} + +/** + Processes a merge fallback operation. + + @param + pToolbar + + A valid pointer to the toolbar where the merge + fall back operation is applied to. + + @param + nPos + + The reference position of the toolbar item for + the merge operation. Value must be between + 0 and number of toolbar items - 1. + + @param + rItemId + + A unique item ID. + + @param + rModuleIdentifier + + The current application module context. + + @param + rMergeCommand + + A merge command. + + @param + rItems + + Toolbar items which are associated to the merge + command. + + @result + Returns true for a successful operation otherwise + false. +*/ +bool ToolBarMerger::ProcessMergeFallback( + ToolBox* pToolbar, + ToolBoxItemId& rItemId, + CommandToInfoMap& rCommandMap, + std::u16string_view rModuleIdentifier, + std::u16string_view rMergeCommand, + std::u16string_view rMergeFallback, + const AddonToolbarItemContainer& rItems ) +{ + if (( rMergeFallback == MERGEFALLBACK_IGNORE ) || + ( rMergeCommand == MERGECOMMAND_REPLACE ) || + ( rMergeCommand == MERGECOMMAND_REMOVE ) ) + { + return true; + } + else if (( rMergeCommand == MERGECOMMAND_ADDBEFORE ) || + ( rMergeCommand == MERGECOMMAND_ADDAFTER ) ) + { + if ( rMergeFallback == MERGEFALLBACK_ADDFIRST ) + MergeItems( pToolbar, 0, 0, rItemId, rCommandMap, rModuleIdentifier, rItems ); + else if ( rMergeFallback == MERGEFALLBACK_ADDLAST ) + MergeItems( pToolbar, ToolBox::APPEND, 0, rItemId, rCommandMap, rModuleIdentifier, rItems ); + else + return false; + return true; + } + + return false; +} + +/** + Merges (adds) toolbar items into an existing toolbar. + + @param + pToolbar + + A valid pointer to the toolbar where the merge + fall back operation is applied to. + + @param + nPos + + The reference position of the toolbar item for + the merge operation. Value must be between + 0 and number of toolbar items - 1. + + @param + rItemId + + A unique item ID. + + @param + rModuleIdentifier + + The current application module context. + + @param + rItems + + Toolbar items which are associated to the merge + command. +*/ +void ToolBarMerger::MergeItems( + ToolBox* pToolbar, + ToolBox::ImplToolItems::size_type nPos, + sal_uInt16 nModIndex, + ToolBoxItemId& rItemId, + CommandToInfoMap& rCommandMap, + std::u16string_view rModuleIdentifier, + const AddonToolbarItemContainer& rAddonToolbarItems ) +{ + const sal_Int32 nSize( rAddonToolbarItems.size() ); + + for ( sal_Int32 i = 0; i < nSize; i++ ) + { + const AddonToolbarItem& rItem = rAddonToolbarItems[i]; + if ( IsCorrectContext( rItem.aContext, rModuleIdentifier )) + { + ToolBox::ImplToolItems::size_type nInsPos = nPos; + if (nInsPos != ToolBox::APPEND) + { + nInsPos += nModIndex+i; + if ( nInsPos > pToolbar->GetItemCount() ) + nInsPos = ToolBox::APPEND; + } + + if ( rItem.aCommandURL == TOOLBOXITEM_SEPARATOR_STR ) + pToolbar->InsertSeparator( nInsPos ); + else + { + CommandToInfoMap::iterator pIter = rCommandMap.find( rItem.aCommandURL ); + if ( pIter == rCommandMap.end()) + { + CommandInfo aCmdInfo; + aCmdInfo.nId = rItemId; + const CommandToInfoMap::value_type aValue( rItem.aCommandURL, aCmdInfo ); + rCommandMap.insert( aValue ); + } + else + { + pIter->second.aIds.push_back( rItemId ); + } + + ToolBarMerger::CreateToolbarItem( pToolbar, nInsPos, rItemId, rItem ); + } + + ++rItemId; + } + } +} + +/** + Replaces a toolbar item with new items for an + existing toolbar. + + @param + pToolbar + + A valid pointer to the toolbar where the merge + fall back operation is applied to. + + @param + nPos + + The reference position of the toolbar item for + the merge operation. Value must be between + 0 and number of toolbar items - 1. + + @param + rItemId + + A unique item ID. + + @param + rModuleIdentifier + + The current application module context. + + @param + rItems + + Toolbar items which are associated to the merge + command. +*/ +void ToolBarMerger::ReplaceItem( + ToolBox* pToolbar, + ToolBox::ImplToolItems::size_type nPos, + ToolBoxItemId& rItemId, + CommandToInfoMap& rCommandMap, + std::u16string_view rModuleIdentifier, + const AddonToolbarItemContainer& rAddonToolbarItems ) +{ + pToolbar->RemoveItem( nPos ); + MergeItems( pToolbar, nPos, 0, rItemId, rCommandMap, rModuleIdentifier, rAddonToolbarItems ); +} + +/** + Removes toolbar items from an existing toolbar. + + @param + pToolbar + + A valid pointer to the toolbar where the merge + fall back operation is applied to. + + @param + nPos + + The reference position of the toolbar item for + the merge operation. Value must be between + 0 and number of toolbar items - 1. + + @param + rMergeCommandParameter. + + An optional argument for the merge command. +*/ +void ToolBarMerger::RemoveItems( + ToolBox* pToolbar, + ToolBox::ImplToolItems::size_type nPos, + std::u16string_view rMergeCommandParameter ) +{ + sal_Int32 nCount = o3tl::toInt32(rMergeCommandParameter); + if ( nCount > 0 ) + { + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + if ( nPos < pToolbar->GetItemCount() ) + pToolbar->RemoveItem( nPos ); + } + } +} + +/** + Removes toolbar items from an existing toolbar. + + @param + pToolbar + + A valid pointer to the toolbar where the merge + fall back operation is applied to. + + @param + nPos + + The reference position of the toolbar item for + the merge operation. Value must be between + 0 and number of toolbar items - 1. + + @param + rMergeCommandParameter. + + An optional argument for the merge command. + + @result + Returns true for a successful operation otherwise + false. +*/ +rtl::Reference<::cppu::OWeakObject> ToolBarMerger::CreateController( + const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Reference< frame::XFrame > & xFrame, + ToolBox* pToolbar, + const OUString& rCommandURL, + ToolBoxItemId nId, + sal_uInt16 nWidth, + std::u16string_view rControlType ) +{ + rtl::Reference<::cppu::OWeakObject> pResult; + + if ( rControlType == TOOLBARCONTROLLER_BUTTON ) + pResult = new ButtonToolbarController( rxContext, pToolbar, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_COMBOBOX ) + pResult = new ComboboxToolbarController( rxContext, xFrame, pToolbar, nId, nWidth, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_EDIT ) + pResult = new EditToolbarController( rxContext, xFrame, pToolbar, nId, nWidth, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_SPINFIELD ) + pResult = new SpinfieldToolbarController( rxContext, xFrame, pToolbar, nId, nWidth, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_IMGBUTTON ) + pResult = new ImageButtonToolbarController( rxContext, xFrame, pToolbar, nId, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_DROPDOWNBOX ) + pResult = new DropdownToolbarController( rxContext, xFrame, pToolbar, nId, nWidth, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_DROPDOWNBTN ) + pResult = new ToggleButtonToolbarController( rxContext, xFrame, pToolbar, nId, + ToggleButtonToolbarController::Style::DropDownButton, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_FIXEDIMAGE ) + pResult = new FixedImageToolbarController( rxContext, xFrame, pToolbar, nId, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_FIXEDTEXT ) + pResult = new FixedTextToolbarController( rxContext, xFrame, pToolbar, nId, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_TOGGLEDDBTN ) + pResult = new ToggleButtonToolbarController( rxContext, xFrame, pToolbar, nId, + ToggleButtonToolbarController::Style::ToggleDropDownButton, rCommandURL ); + else + pResult = new GenericToolbarController( rxContext, xFrame, pToolbar, nId, rCommandURL ); + + return pResult; +} + +void ToolBarMerger::CreateToolbarItem( ToolBox* pToolbar, ToolBox::ImplToolItems::size_type nPos, ToolBoxItemId nItemId, const AddonToolbarItem& rItem ) +{ + assert(pToolbar->GetItemData(nItemId) == nullptr); // that future would contain a double free + + pToolbar->InsertItem( nItemId, rItem.aLabel, ToolBoxItemBits::NONE, nPos ); + pToolbar->SetItemCommand( nItemId, rItem.aCommandURL ); + pToolbar->SetQuickHelpText( nItemId, rItem.aLabel ); + pToolbar->SetItemText( nItemId, rItem.aLabel ); + pToolbar->EnableItem( nItemId ); + pToolbar->SetItemState( nItemId, TRISTATE_FALSE ); + + // Use the user data to store add-on specific data with the toolbar item + AddonsParams* pAddonParams = new AddonsParams; + pAddonParams->aControlType = rItem.aControlType; + pAddonParams->nWidth = rItem.nWidth; + pToolbar->SetItemData( nItemId, pAddonParams ); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/toolbarmodemenucontroller.cxx b/framework/source/uielement/toolbarmodemenucontroller.cxx new file mode 100644 index 000000000..1740bf1f1 --- /dev/null +++ b/framework/source/uielement/toolbarmodemenucontroller.cxx @@ -0,0 +1,294 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <uielement/toolbarmodemenucontroller.hxx> +#include <services.h> + +#include <com/sun/star/awt/MenuItemStyle.hpp> +#include <com/sun/star/ui/UIElementType.hpp> +#include <com/sun/star/frame/XModuleManager.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> + + +#include <officecfg/Office/Common.hxx> +#include <vcl/svapp.hxx> +#include <vcl/EnumContext.hxx> +#include <rtl/ustrbuf.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/types.hxx> +#include <unotools/confignode.hxx> +#include <cppuhelper/supportsservice.hxx> + +// Defines + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::ui; + +namespace framework +{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL ToolbarModeMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.ToolbarModeMenuController"; +} + +sal_Bool SAL_CALL ToolbarModeMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL ToolbarModeMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + + +ToolbarModeMenuController::ToolbarModeMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + svt::PopupMenuControllerBase( xContext ), + m_xContext( xContext ) +{ +} + +ToolbarModeMenuController::~ToolbarModeMenuController() +{ +} + +void ToolbarModeMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + if ( officecfg::Office::Common::Misc::DisableUICustomization::get() ) + return; + + SolarMutexGuard aSolarMutexGuard; + resetPopupMenu( rPopupMenu ); + + const Reference<XComponentContext> xContext (::comphelper::getProcessComponentContext() ); + const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( xContext ); + vcl::EnumContext::Application eApp = vcl::EnumContext::GetApplicationEnum(xModuleManager->identify(m_xFrame)); + + OUStringBuffer aPath("org.openoffice.Office.UI.ToolbarMode/Applications/"); + switch ( eApp ) + { + case vcl::EnumContext::Application::Writer: + aPath.append("Writer"); + break; + case vcl::EnumContext::Application::Calc: + aPath.append("Calc"); + break; + case vcl::EnumContext::Application::Impress: + aPath.append("Impress"); + break; + case vcl::EnumContext::Application::Draw: + aPath.append("Draw"); + break; + case vcl::EnumContext::Application::Formula: + aPath.append("Formula"); + break; + case vcl::EnumContext::Application::Base: + aPath.append("Base"); + break; + default: + break; + } + aPath.append("/Modes"); + + const utl::OConfigurationTreeRoot aModesNode( + m_xContext, + aPath.makeStringAndClear(), + false); + if ( !aModesNode.isValid() ) + return; + + const Sequence<OUString> aModeNodeNames (aModesNode.getNodeNames()); + const sal_Int32 nCount(aModeNodeNames.getLength()); + tools::Long nCountToolbar = 0; + + for ( sal_Int32 nReadIndex = 0; nReadIndex < nCount; ++nReadIndex ) + { + const utl::OConfigurationNode aModeNode(aModesNode.openNode(aModeNodeNames[nReadIndex])); + if ( !aModeNode.isValid() ) + continue; + + OUString aLabel = comphelper::getString( aModeNode.getNodeValue( "Label" ) ); + OUString aCommandArg = comphelper::getString( aModeNode.getNodeValue( "CommandArg" ) ); + tools::Long nPosition = comphelper::getINT32( aModeNode.getNodeValue( "MenuPosition" ) ); + bool isExperimental = comphelper::getBOOL( aModeNode.getNodeValue( "IsExperimental" ) ); + bool hasNotebookbar = comphelper::getBOOL( aModeNode.getNodeValue( "HasNotebookbar" ) ); + + // Allow Notebookbar only in experimental mode + if ( isExperimental && !officecfg::Office::Common::Misc::ExperimentalMode::get() ) + continue; + if (!hasNotebookbar) + nCountToolbar++; + + m_xPopupMenu->insertItem( nReadIndex+1, aLabel, css::awt::MenuItemStyle::RADIOCHECK, nPosition ); + rPopupMenu->setCommand( nReadIndex+1, aCommandArg ); + } + rPopupMenu->insertSeparator(nCountToolbar); +} + +// XEventListener +void SAL_CALL ToolbarModeMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + osl::MutexGuard aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL ToolbarModeMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + OUString aFeatureURL( Event.FeatureURL.Complete ); + + // All other status events will be processed here + osl::ClearableMutexGuard aLock( m_aMutex ); + Reference< css::awt::XPopupMenu > xPopupMenu( m_xPopupMenu ); + aLock.clear(); + + if ( !xPopupMenu.is() ) + return; + + SolarMutexGuard aGuard; + + bool bSetCheckmark = false; + bool bCheckmark = false; + for (sal_Int16 i = 0, nCount = xPopupMenu->getItemCount(); i < nCount; ++i) + { + sal_Int16 nId = xPopupMenu->getItemId(i); + if ( nId == 0 ) + continue; + + OUString aCmd = xPopupMenu->getCommand(nId); + if ( aCmd == aFeatureURL ) + { + // Enable/disable item + xPopupMenu->enableItem(nId, Event.IsEnabled); + + // Checkmark + if ( Event.State >>= bCheckmark ) + bSetCheckmark = true; + + if ( bSetCheckmark ) + xPopupMenu->checkItem(nId, bCheckmark); + else + { + OUString aItemText; + + if ( Event.State >>= aItemText ) + xPopupMenu->setItemText(nId, aItemText); + } + } + } +} + +// XMenuListener +void SAL_CALL ToolbarModeMenuController::itemSelected( const css::awt::MenuEvent& rEvent ) +{ + auto aArgs(comphelper::InitPropertySequence({{"Mode", Any(m_xPopupMenu->getCommand(rEvent.MenuId))}})); + dispatchCommand(m_aCommandURL, aArgs); +} + +void SAL_CALL ToolbarModeMenuController::itemActivated( const css::awt::MenuEvent& ) +{ + const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( m_xContext ); + vcl::EnumContext::Application eApp = vcl::EnumContext::GetApplicationEnum(xModuleManager->identify(m_xFrame)); + + OUStringBuffer aPath("org.openoffice.Office.UI.ToolbarMode/Applications/"); + switch ( eApp ) + { + case vcl::EnumContext::Application::Writer: + aPath.append("Writer"); + break; + case vcl::EnumContext::Application::Calc: + aPath.append("Calc"); + break; + case vcl::EnumContext::Application::Impress: + aPath.append("Impress"); + break; + case vcl::EnumContext::Application::Draw: + aPath.append("Draw"); + break; + case vcl::EnumContext::Application::Formula: + aPath.append("Formula"); + break; + case vcl::EnumContext::Application::Base: + aPath.append("Base"); + break; + default: + break; + } + + const utl::OConfigurationTreeRoot aModesNode( + m_xContext, + aPath.makeStringAndClear(), + false); + if ( !aModesNode.isValid() ) + return; + + OUString aMode = comphelper::getString( aModesNode.getNodeValue( "Active" ) ); + + for ( int i = 0; i < m_xPopupMenu->getItemCount(); ++i ) + { + sal_Int16 nItemId(m_xPopupMenu->getItemId(i)); + m_xPopupMenu->checkItem(nItemId, aMode == m_xPopupMenu->getCommand(nItemId)); + } +} + +// XPopupMenuController +void SAL_CALL ToolbarModeMenuController::setPopupMenu( const Reference< css::awt::XPopupMenu >& xPopupMenu ) +{ + osl::MutexGuard aLock( m_aMutex ); + + throwIfDisposed(); + + if ( m_xFrame.is() && !m_xPopupMenu.is() ) + { + // Create popup menu on demand + SolarMutexGuard aSolarMutexGuard; + + m_xPopupMenu = xPopupMenu; + m_xPopupMenu->addMenuListener( Reference< css::awt::XMenuListener >(this) ); + fillPopupMenu( m_xPopupMenu ); + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_ToolbarModeMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new framework::ToolbarModeMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/toolbarsmenucontroller.cxx b/framework/source/uielement/toolbarsmenucontroller.cxx new file mode 100644 index 000000000..730190240 --- /dev/null +++ b/framework/source/uielement/toolbarsmenucontroller.cxx @@ -0,0 +1,794 @@ +/* -*- 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 <uielement/toolbarsmenucontroller.hxx> + +#include <algorithm> +#include <string_view> +#include <unordered_map> + +#include <services.h> +#include <strings.hrc> +#include <classes/fwkresid.hxx> +#include <framework/sfxhelperfunctions.hxx> +#include <uiconfiguration/windowstateproperties.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/awt/MenuItemStyle.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/UIElementType.hpp> +#include <com/sun/star/ui/theWindowStateConfiguration.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <officecfg/Office/Common.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <rtl/ustrbuf.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/window.hxx> +#include <unotools/cmdoptions.hxx> +#include <unotools/collatorwrapper.hxx> +#include <unotools/syslocale.hxx> +#include <cppuhelper/supportsservice.hxx> + +// Defines + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::ui; + +constexpr OUStringLiteral CMD_RESTOREVISIBILITY = u".cmd:RestoreVisibility"; +constexpr OUStringLiteral CMD_LOCKTOOLBARS = u".uno:ToolbarLock"; + +constexpr OUStringLiteral STATIC_CMD_PART = u".uno:AvailableToolbars?Toolbar:string="; +const char STATIC_INTERNAL_CMD_PART[] = ".cmd:"; + +namespace framework +{ + +typedef std::unordered_map< OUString, OUString > ToolbarHashMap; + +namespace { + +struct ToolBarEntry +{ + OUString aUIName; + OUString aCommand; + bool bVisible; + const CollatorWrapper* pCollatorWrapper; +}; + +} + +static bool CompareToolBarEntry( const ToolBarEntry& aOne, const ToolBarEntry& aTwo ) +{ + sal_Int32 nComp = aOne.pCollatorWrapper->compareString( aOne.aUIName, aTwo.aUIName ); + + return nComp < 0; +} + +static Reference< XLayoutManager > getLayoutManagerFromFrame( const Reference< XFrame >& rFrame ) +{ + Reference< XPropertySet > xPropSet( rFrame, UNO_QUERY ); + Reference< XLayoutManager > xLayoutManager; + + try + { + xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager; + } + catch ( const UnknownPropertyException& ) + { + } + + return xLayoutManager; +} + +namespace { + +struct ToolBarInfo +{ + OUString aToolBarResName; + OUString aToolBarUIName; +}; + +} + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL ToolbarsMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.ToolBarsMenuController"; +} + +sal_Bool SAL_CALL ToolbarsMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL ToolbarsMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + +constexpr OUStringLiteral g_aPropUIName( u"UIName" ); +constexpr OUStringLiteral g_aPropResourceURL( u"ResourceURL" ); + +ToolbarsMenuController::ToolbarsMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + svt::PopupMenuControllerBase( xContext ), + m_xContext( xContext ), + m_bResetActive( false ), + m_aIntlWrapper(SvtSysLocale().GetUILanguageTag()) +{ +} + +ToolbarsMenuController::~ToolbarsMenuController() +{ +} + +void ToolbarsMenuController::addCommand( + Reference< css::awt::XPopupMenu > const & rPopupMenu, const OUString& rCommandURL, const OUString& rLabel ) +{ + sal_uInt16 nItemId = m_xPopupMenu->getItemCount()+1; + + OUString aLabel; + if ( rLabel.isEmpty() ) + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCommandURL, m_aModuleName); + aLabel = vcl::CommandInfoProvider::GetMenuLabelForCommand(aProperties); + } + else + aLabel = rLabel; + + rPopupMenu->insertItem( nItemId, aLabel, 0, nItemId ); + rPopupMenu->setCommand( nItemId, rCommandURL ); + + bool bInternal = rCommandURL.startsWith( STATIC_INTERNAL_CMD_PART ); + if ( !bInternal ) + { + if ( !getDispatchFromCommandURL( rCommandURL ).is() ) + m_xPopupMenu->enableItem( nItemId, false ); + } + + SolarMutexGuard aSolarMutexGuard; + + css::uno::Reference<css::graphic::XGraphic> xGraphic; + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + + if ( rSettings.GetUseImagesInMenus() ) + xGraphic = vcl::CommandInfoProvider::GetXGraphicForCommand(rCommandURL, m_xFrame); + + if (xGraphic.is()) + rPopupMenu->setItemImage(nItemId, xGraphic, false); + + m_aCommandVector.push_back( rCommandURL ); +} + +Reference< XDispatch > ToolbarsMenuController::getDispatchFromCommandURL( const OUString& rCommandURL ) +{ + URL aTargetURL; + Reference< XURLTransformer > xURLTransformer; + Reference< XFrame > xFrame; + + { + SolarMutexGuard aSolarMutexGuard; + xURLTransformer = m_xURLTransformer; + xFrame = m_xFrame; + } + + aTargetURL.Complete = rCommandURL; + xURLTransformer->parseStrict( aTargetURL ); + Reference< XDispatchProvider > xDispatchProvider( xFrame, UNO_QUERY ); + if ( xDispatchProvider.is() ) + return xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + else + return Reference< XDispatch >(); +} + +static void fillHashMap( const Sequence< Sequence< css::beans::PropertyValue > >& rSeqToolBars, + ToolbarHashMap& rHashMap ) +{ + for ( Sequence< css::beans::PropertyValue > const & props : rSeqToolBars ) + { + OUString aResourceURL; + OUString aUIName; + for ( css::beans::PropertyValue const & prop : props ) + { + if ( prop.Name == "ResourceURL" ) + prop.Value >>= aResourceURL; + else if ( prop.Name == "UIName" ) + prop.Value >>= aUIName; + } + + if ( !aResourceURL.isEmpty() && + rHashMap.find( aResourceURL ) == rHashMap.end() ) + rHashMap.emplace( aResourceURL, aUIName ); + } +} + +// private function +Sequence< Sequence< css::beans::PropertyValue > > ToolbarsMenuController::getLayoutManagerToolbars( const Reference< css::frame::XLayoutManager >& rLayoutManager ) +{ + std::vector< ToolBarInfo > aToolBarArray; + const Sequence< Reference< XUIElement > > aUIElements = rLayoutManager->getElements(); + for ( Reference< XUIElement > const & xUIElement : aUIElements ) + { + Reference< XPropertySet > xPropSet( xUIElement, UNO_QUERY ); + if ( xPropSet.is() && xUIElement.is() ) + { + try + { + OUString aResName; + sal_Int16 nType( -1 ); + xPropSet->getPropertyValue("Type") >>= nType; + xPropSet->getPropertyValue("ResourceURL") >>= aResName; + + if (( nType == css::ui::UIElementType::TOOLBAR ) && + !aResName.isEmpty() ) + { + ToolBarInfo aToolBarInfo; + + aToolBarInfo.aToolBarResName = aResName; + + SolarMutexGuard aGuard; + Reference< css::awt::XWindow > xWindow( xUIElement->getRealInterface(), UNO_QUERY ); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow ) + aToolBarInfo.aToolBarUIName = pWindow->GetText(); + + aToolBarArray.push_back( aToolBarInfo ); + } + } + catch ( const Exception& ) + { + } + } + } + + Sequence< Sequence< css::beans::PropertyValue > > aSeq( aToolBarArray.size() ); + auto pSeq = aSeq.getArray(); + const sal_uInt32 nCount = aToolBarArray.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + Sequence< css::beans::PropertyValue > aTbSeq{ + comphelper::makePropertyValue(g_aPropUIName, aToolBarArray[i].aToolBarUIName), + comphelper::makePropertyValue(g_aPropResourceURL, aToolBarArray[i].aToolBarResName) + }; + pSeq[i] = aTbSeq; + } + + return aSeq; +} + + +void ToolbarsMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + if( officecfg::Office::Common::Misc::DisableUICustomization::get() ) + return; + + SolarMutexGuard aSolarMutexGuard; + resetPopupMenu( rPopupMenu ); + + m_aCommandVector.clear(); + + // Retrieve layout manager for additional information + Reference< XLayoutManager > xLayoutManager( getLayoutManagerFromFrame( m_xFrame )); + + m_bResetActive = false; + if ( !xLayoutManager.is() ) + return; + + ToolbarHashMap aToolbarHashMap; + + if ( m_xDocCfgMgr.is() ) + { + Sequence< Sequence< css::beans::PropertyValue > > aSeqDocToolBars = + m_xDocCfgMgr->getUIElementsInfo( UIElementType::TOOLBAR ); + fillHashMap( aSeqDocToolBars, aToolbarHashMap ); + } + + if ( m_xModuleCfgMgr.is() ) + { + Sequence< Sequence< css::beans::PropertyValue > > aSeqToolBars = + m_xModuleCfgMgr->getUIElementsInfo( UIElementType::TOOLBAR ); + fillHashMap( aSeqToolBars, aToolbarHashMap ); + } + + std::vector< ToolBarEntry > aSortedTbs; + OUString aStaticCmdPart( STATIC_CMD_PART ); + + Sequence< Sequence< css::beans::PropertyValue > > aSeqFrameToolBars = getLayoutManagerToolbars( xLayoutManager ); + fillHashMap( aSeqFrameToolBars, aToolbarHashMap ); + + for (auto const& toolbar : aToolbarHashMap) + { + OUString aUIName = toolbar.second; + bool bHideFromMenu( false ); + bool bContextSensitive( false ); + if ( aUIName.isEmpty() && + m_xPersistentWindowState.is() ) + { + bool bVisible( false ); + + try + { + Sequence< PropertyValue > aWindowState; + Any a( m_xPersistentWindowState->getByName( toolbar.first )); + + if ( a >>= aWindowState ) + { + for ( PropertyValue const & prop : std::as_const(aWindowState) ) + { + if ( prop.Name == WINDOWSTATE_PROPERTY_UINAME ) + prop.Value >>= aUIName; + else if ( prop.Name == WINDOWSTATE_PROPERTY_HIDEFROMENU ) + prop.Value >>= bHideFromMenu; + else if ( prop.Name == WINDOWSTATE_PROPERTY_CONTEXT ) + prop.Value >>= bContextSensitive; + else if ( prop.Name == WINDOWSTATE_PROPERTY_VISIBLE ) + prop.Value >>= bVisible; + } + } + } + catch ( const Exception& ) + { + } + + // Check if we have to enable/disable "Reset" menu item + if ( bContextSensitive && !bVisible ) + m_bResetActive = true; + + } + + if ( !aUIName.isEmpty() && !bHideFromMenu ) + { + ToolBarEntry aTbEntry; + aTbEntry.aUIName = aUIName; + aTbEntry.aCommand = toolbar.first; + aTbEntry.bVisible = xLayoutManager->isElementVisible( toolbar.first ); + aTbEntry.pCollatorWrapper = m_aIntlWrapper.getCaseCollator(); + aSortedTbs.push_back( aTbEntry ); + } + } + + // sort toolbars + std::sort( aSortedTbs.begin(), aSortedTbs.end(), CompareToolBarEntry ); + + sal_Int16 nIndex( 1 ); + const sal_uInt32 nCount = aSortedTbs.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + sal_uInt16 nItemCount = m_xPopupMenu->getItemCount(); + m_xPopupMenu->insertItem( nIndex, aSortedTbs[i].aUIName, css::awt::MenuItemStyle::CHECKABLE, nItemCount ); + if ( aSortedTbs[i].bVisible ) + m_xPopupMenu->checkItem( nIndex, true ); + + OUStringBuffer aStrBuf( aStaticCmdPart ); + + sal_Int32 n = aSortedTbs[i].aCommand.lastIndexOf( '/' ); + if (( n > 0 ) && (( n+1 ) < aSortedTbs[i].aCommand.getLength() )) + aStrBuf.append( aSortedTbs[i].aCommand.subView(n+1) ); + + OUString aCmd( aStrBuf.makeStringAndClear() ); + + // Store complete uno-command so it can also be dispatched. This is necessary to support + // the test tool! + rPopupMenu->setCommand( nIndex, aCmd ); + ++nIndex; + } + + // Create commands for non-toolbars + + bool bAddCommand( true ); + SvtCommandOptions aCmdOptions; + + if ( aCmdOptions.HasEntries( SvtCommandOptions::CMDOPTION_DISABLED )) + { + if ( aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, + "ConfigureDialog")) + bAddCommand = false; + } + + if ( bAddCommand ) + { + // Create command for configure + if ( m_xPopupMenu->getItemCount() > 0 ) + { + sal_uInt16 nItemCount = m_xPopupMenu->getItemCount(); + m_xPopupMenu->insertSeparator( nItemCount+1 ); + } + + addCommand( m_xPopupMenu, ".uno:ConfigureDialog", "" ); + } + + // Add separator if no configure has been added + if ( !bAddCommand ) + { + // Create command for configure + if ( m_xPopupMenu->getItemCount() > 0 ) + { + sal_uInt16 nItemCount = m_xPopupMenu->getItemCount(); + m_xPopupMenu->insertSeparator( nItemCount+1 ); + } + } + + OUString aLabelStr(FwkResId(STR_RESTORE_TOOLBARS)); + addCommand( m_xPopupMenu, CMD_RESTOREVISIBILITY, aLabelStr ); + aLabelStr = FwkResId(STR_LOCK_TOOLBARS); + addCommand( m_xPopupMenu, CMD_LOCKTOOLBARS, aLabelStr ); +} + +// XEventListener +void SAL_CALL ToolbarsMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + osl::MutexGuard aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + m_xDocCfgMgr.clear(); + m_xModuleCfgMgr.clear(); + m_xContext.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL ToolbarsMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + OUString aFeatureURL( Event.FeatureURL.Complete ); + + // All other status events will be processed here + osl::ClearableMutexGuard aLock( m_aMutex ); + Reference< css::awt::XPopupMenu > xPopupMenu( m_xPopupMenu ); + aLock.clear(); + + if ( !xPopupMenu.is() ) + return; + + SolarMutexGuard aGuard; + + bool bSetCheckmark = false; + bool bCheckmark = false; + for (sal_Int16 i = 0, nCount = xPopupMenu->getItemCount(); i < nCount; ++i) + { + sal_Int16 nId = xPopupMenu->getItemId(i); + if ( nId == 0 ) + continue; + + OUString aCmd = xPopupMenu->getCommand(nId); + if ( aCmd == aFeatureURL ) + { + // Enable/disable item + xPopupMenu->enableItem(nId, Event.IsEnabled); + + // Checkmark + if ( Event.State >>= bCheckmark ) + bSetCheckmark = true; + + if ( bSetCheckmark ) + xPopupMenu->checkItem(nId, bCheckmark); + else + { + OUString aItemText; + + if ( Event.State >>= aItemText ) + xPopupMenu->setItemText(nId, aItemText); + } + } + } +} + +// XMenuListener +void SAL_CALL ToolbarsMenuController::itemSelected( const css::awt::MenuEvent& rEvent ) +{ + Reference< css::awt::XPopupMenu > xPopupMenu; + Reference< XComponentContext > xContext; + Reference< XURLTransformer > xURLTransformer; + Reference< XFrame > xFrame; + Reference< XNameAccess > xPersistentWindowState; + + { + osl::MutexGuard aLock(m_aMutex); + xPopupMenu = m_xPopupMenu; + xContext = m_xContext; + xURLTransformer = m_xURLTransformer; + xFrame = m_xFrame; + xPersistentWindowState = m_xPersistentWindowState; + } + + if ( !xPopupMenu.is() ) + return; + + SolarMutexGuard aSolarMutexGuard; + + OUString aCmd(xPopupMenu->getCommand(rEvent.MenuId)); + if ( aCmd.startsWith( STATIC_INTERNAL_CMD_PART ) ) + { + // Command to restore the visibility of all context sensitive toolbars + Reference< XNameReplace > xNameReplace( xPersistentWindowState, UNO_QUERY ); + if ( xPersistentWindowState.is() && xNameReplace.is() ) + { + try + { + Sequence< OUString > aElementNames = xPersistentWindowState->getElementNames(); + sal_Int32 nCount = aElementNames.getLength(); + bool bRefreshToolbars( false ); + + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + try + { + OUString aElementName = aElementNames[i]; + Sequence< PropertyValue > aWindowState; + + if ( xPersistentWindowState->getByName( aElementName ) >>= aWindowState ) + { + bool bVisible( false ); + bool bContextSensitive( false ); + sal_Int32 nVisibleIndex( -1 ); + for ( sal_Int32 j = 0; j < aWindowState.getLength(); j++ ) + { + if ( aWindowState[j].Name == WINDOWSTATE_PROPERTY_VISIBLE ) + { + aWindowState[j].Value >>= bVisible; + nVisibleIndex = j; + } + else if ( aWindowState[j].Name == WINDOWSTATE_PROPERTY_CONTEXT ) + aWindowState[j].Value >>= bContextSensitive; + } + + if ( !bVisible && bContextSensitive && nVisibleIndex >= 0 ) + { + // Default is: Every context sensitive toolbar is visible + aWindowState.getArray()[nVisibleIndex].Value <<= true; + xNameReplace->replaceByName( aElementName, Any( aWindowState )); + bRefreshToolbars = true; + } + } + } + catch ( const NoSuchElementException& ) + { + } + } + + if ( bRefreshToolbars ) + { + Reference< XLayoutManager > xLayoutManager( getLayoutManagerFromFrame( xFrame )); + if ( xLayoutManager.is() ) + { + Reference< XPropertySet > xPropSet( xLayoutManager, UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + xPropSet->setPropertyValue("RefreshContextToolbarVisibility", Any( true )); + } + catch ( const RuntimeException& ) + { + } + catch ( const Exception& ) + { + } + } + } + RefreshToolbars( xFrame ); + } + } + catch ( const RuntimeException& ) + { + throw; + } + catch ( const Exception& ) + { + } + } + } + else if ( aCmd.indexOf( STATIC_CMD_PART ) < 0 ) + { + URL aTargetURL; + Sequence<PropertyValue> aArgs; + + aTargetURL.Complete = aCmd; + xURLTransformer->parseStrict( aTargetURL ); + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + if ( xDispatchProvider.is() ) + { + Reference< XDispatch > xDispatch = xDispatchProvider->queryDispatch( + aTargetURL, OUString(), 0 ); + + ExecuteInfo* pExecuteInfo = new ExecuteInfo; + pExecuteInfo->xDispatch = xDispatch; + pExecuteInfo->aTargetURL = aTargetURL; + pExecuteInfo->aArgs = aArgs; + Application::PostUserEvent( LINK(nullptr, ToolbarsMenuController, ExecuteHdl_Impl), pExecuteInfo ); + } + } + else + { + Reference< XLayoutManager > xLayoutManager( getLayoutManagerFromFrame( xFrame )); + if ( xLayoutManager.is() ) + { + // Extract toolbar name from the combined uno-command. + sal_Int32 nIndex = aCmd.indexOf( '=' ); + if (( nIndex > 0 ) && (( nIndex+1 ) < aCmd.getLength() )) + { + OUString aToolBarResName = OUString::Concat("private:resource/toolbar/") + aCmd.subView(nIndex+1); + + const bool bShow(!xPopupMenu->isItemChecked(rEvent.MenuId)); + if ( bShow ) + { + xLayoutManager->createElement( aToolBarResName ); + xLayoutManager->showElement( aToolBarResName ); + } + else + { + // closing means: + // hide and destroy element + xLayoutManager->hideElement( aToolBarResName ); + xLayoutManager->destroyElement( aToolBarResName ); + } + } + } + } +} + +void SAL_CALL ToolbarsMenuController::itemActivated( const css::awt::MenuEvent& ) +{ + std::vector< OUString > aCmdVector; + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + Reference< XURLTransformer > xURLTransformer( m_xURLTransformer ); + { + osl::MutexGuard aLock( m_aMutex ); + fillPopupMenu( m_xPopupMenu ); + aCmdVector = m_aCommandVector; + } + + // Update status for all commands inside our toolbars popup menu + const sal_uInt32 nCount = aCmdVector.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + bool bInternal = aCmdVector[i].startsWith( STATIC_INTERNAL_CMD_PART ); + + if ( !bInternal ) + { + URL aTargetURL; + aTargetURL.Complete = aCmdVector[i]; + xURLTransformer->parseStrict( aTargetURL ); + Reference< XDispatch > xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + if ( xDispatch.is() ) + { + xDispatch->addStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + xDispatch->removeStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + } + } + else if ( aCmdVector[i] == CMD_RESTOREVISIBILITY ) + { + // Special code to determine the enable/disable state of this command + FeatureStateEvent aFeatureStateEvent; + aFeatureStateEvent.FeatureURL.Complete = aCmdVector[i]; + aFeatureStateEvent.IsEnabled = m_bResetActive; // is context sensitive toolbar non visible + statusChanged( aFeatureStateEvent ); + } + } +} + +// XPopupMenuController +void SAL_CALL ToolbarsMenuController::setPopupMenu( const Reference< css::awt::XPopupMenu >& xPopupMenu ) +{ + osl::MutexGuard aLock( m_aMutex ); + + throwIfDisposed(); + + if ( m_xFrame.is() && !m_xPopupMenu.is() ) + { + // Create popup menu on demand + SolarMutexGuard aSolarMutexGuard; + + m_xPopupMenu = xPopupMenu; + m_xPopupMenu->addMenuListener( Reference< css::awt::XMenuListener >(this) ); + fillPopupMenu( m_xPopupMenu ); + } +} + +// XInitialization +void SAL_CALL ToolbarsMenuController::initialize( const Sequence< Any >& aArguments ) +{ + osl::MutexGuard aLock( m_aMutex ); + bool bInitialized( m_bInitialized ); + if ( bInitialized ) + return; + + svt::PopupMenuControllerBase::initialize(aArguments); + + if ( !m_bInitialized ) + return; + + Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext ); + Reference< XNameAccess > xPersistentWindowStateSupplier = css::ui::theWindowStateConfiguration::get( m_xContext ); + + // Retrieve persistent window state reference for our module + try + { + OUString aModuleIdentifier = xModuleManager->identify( m_xFrame ); + xPersistentWindowStateSupplier->getByName( aModuleIdentifier ) >>= m_xPersistentWindowState; + + Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgSupplier = + theModuleUIConfigurationManagerSupplier::get( m_xContext ); + m_xModuleCfgMgr = xModuleCfgSupplier->getUIConfigurationManager( aModuleIdentifier ); + + Reference< XController > xController = m_xFrame->getController(); + Reference< XModel > xModel; + if ( xController.is() ) + xModel = xController->getModel(); + if ( xModel.is() ) + { + Reference< XUIConfigurationManagerSupplier > xUIConfigurationManagerSupplier( xModel, UNO_QUERY ); + if ( xUIConfigurationManagerSupplier.is() ) + m_xDocCfgMgr = xUIConfigurationManagerSupplier->getUIConfigurationManager(); + } + } + catch ( const Exception& ) + { + } +} + +IMPL_STATIC_LINK( ToolbarsMenuController, ExecuteHdl_Impl, void*, p, void ) +{ + ExecuteInfo* pExecuteInfo = static_cast<ExecuteInfo*>(p); + try + { + // Asynchronous execution as this can lead to our own destruction! + // Framework can recycle our current frame and the layout manager disposes all user interface + // elements if a component gets detached from its frame! + if ( pExecuteInfo->xDispatch.is() ) + { + pExecuteInfo->xDispatch->dispatch( pExecuteInfo->aTargetURL, pExecuteInfo->aArgs ); + } + } + catch ( const Exception& ) + { + } + + delete pExecuteInfo; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_ToolbarsMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new framework::ToolbarsMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/toolbarwrapper.cxx b/framework/source/uielement/toolbarwrapper.cxx new file mode 100644 index 000000000..9bf9a4bd7 --- /dev/null +++ b/framework/source/uielement/toolbarwrapper.cxx @@ -0,0 +1,328 @@ +/* -*- 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 <uielement/toolbarwrapper.hxx> +#include <uielement/toolbarmanager.hxx> + +#include <com/sun/star/ui/UIElementType.hpp> +#include <com/sun/star/lang/DisposedException.hpp> + +#include <toolkit/helper/vclunohelper.hxx> +#include <cppuhelper/queryinterface.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> +#include <vcl/weldutils.hxx> + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::frame; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace com::sun::star::awt; +using namespace ::com::sun::star::ui; + +namespace framework +{ + +ToolBarWrapper::ToolBarWrapper( const Reference< XComponentContext >& rxContext ) : + UIConfigElementWrapperBase( UIElementType::TOOLBAR ), + m_xContext( rxContext ) +{ +} + +ToolBarWrapper::~ToolBarWrapper() +{ + m_xWeldedToolbar.reset(nullptr); + m_xTopLevel.reset(nullptr); + m_xBuilder.reset(nullptr); +} + +// XInterface +void SAL_CALL ToolBarWrapper::acquire() noexcept +{ + UIConfigElementWrapperBase::acquire(); +} + +void SAL_CALL ToolBarWrapper::release() noexcept +{ + UIConfigElementWrapperBase::release(); +} + +uno::Any SAL_CALL ToolBarWrapper::queryInterface( const uno::Type & rType ) +{ + Any a = ::cppu::queryInterface( + rType , + static_cast< css::ui::XUIFunctionListener* >(this) ); + + if( a.hasValue() ) + return a; + + return UIConfigElementWrapperBase::queryInterface( rType ); +} + +// XComponent +void SAL_CALL ToolBarWrapper::dispose() +{ + Reference< XComponent > xThis(this); + + { + SolarMutexGuard g; + if ( m_bDisposed ) + return; + } + + css::lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + SolarMutexGuard g; + + if ( m_xToolBarManager.is() ) + m_xToolBarManager->dispose(); + m_xToolBarManager.clear(); + m_xConfigSource.clear(); + m_xConfigData.clear(); + + m_bDisposed = true; +} + +// XInitialization +void SAL_CALL ToolBarWrapper::initialize( const Sequence< Any >& aArguments ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_bInitialized ) + return; + + UIConfigElementWrapperBase::initialize( aArguments ); + + bool bPopupMode( false ); + Reference< XWindow > xParentWindow; + for ( Any const & arg : aArguments ) + { + PropertyValue aPropValue; + if ( arg >>= aPropValue ) + { + if ( aPropValue.Name == "PopupMode" ) + aPropValue.Value >>= bPopupMode; + else if ( aPropValue.Name == "ParentWindow" ) + xParentWindow.set( aPropValue.Value, UNO_QUERY ); + } + } + + Reference< XFrame > xFrame( m_xWeakFrame ); + if ( !(xFrame.is() && m_xConfigSource.is()) ) + return; + + // Create VCL based toolbar which will be filled with settings data + VclPtr<ToolBox> pToolBar; + rtl::Reference<ToolBarManager> pToolBarManager; + { + SolarMutexGuard aSolarMutexGuard; + if ( !xParentWindow.is() ) + xParentWindow.set( xFrame->getContainerWindow() ); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xParentWindow ); + if ( pWindow ) + { + sal_uLong nStyles = WB_BORDER | WB_SCROLL | WB_MOVEABLE | WB_3DLOOK | WB_DOCKABLE | WB_SIZEABLE | WB_CLOSEABLE; + + pToolBar = VclPtr<ToolBox>::Create( pWindow, nStyles ); + pToolBar->SetLineSpacing(true); + pToolBarManager = new ToolBarManager( m_xContext, xFrame, m_aResourceURL, pToolBar ); + m_xToolBarManager = pToolBarManager; + pToolBar->WillUsePopupMode( bPopupMode ); + } + else if (weld::TransportAsXWindow* pTunnel = dynamic_cast<weld::TransportAsXWindow*>(xParentWindow.get())) + { + m_xBuilder = Application::CreateBuilder(pTunnel->getWidget(), "svt/ui/managedtoolbar.ui"); + m_xTopLevel = m_xBuilder->weld_container("toolbarcontainer"); + m_xWeldedToolbar = m_xBuilder->weld_toolbar("managedtoolbar"); + if ( m_xWeldedToolbar ) + { + pToolBarManager = new ToolBarManager( m_xContext, xFrame, m_aResourceURL, m_xWeldedToolbar.get(), m_xBuilder.get() ); + m_xToolBarManager = pToolBarManager; + } + } + } + + try + { + m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false ); + if ( m_xConfigData.is() && (pToolBar || m_xWeldedToolbar) && pToolBarManager ) + { + // Fill toolbar with container contents + pToolBarManager->FillToolbar( m_xConfigData ); + if (pToolBar) + { + pToolBar->EnableCustomize(); + ::Size aActSize( pToolBar->GetSizePixel() ); + ::Size aSize( pToolBar->CalcWindowSizePixel() ); + aSize.setWidth( aActSize.Width() ); + pToolBar->SetOutputSizePixel( aSize ); + } + } + } + catch ( const NoSuchElementException& ) + { + // No settings in our configuration manager. This means we are + // a transient toolbar which has no persistent settings. + m_bPersistent = false; + if ( pToolBar && pToolBarManager ) + { + pToolBar->EnableCustomize(); + ::Size aActSize( pToolBar->GetSizePixel() ); + ::Size aSize( pToolBar->CalcWindowSizePixel() ); + aSize.setWidth( aActSize.Width() ); + pToolBar->SetOutputSizePixel( aSize ); + } + } +} + +// XEventListener +void SAL_CALL ToolBarWrapper::disposing( const css::lang::EventObject& ) +{ + // nothing todo +} + +// XUpdatable +void SAL_CALL ToolBarWrapper::update() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + ToolBarManager* pToolBarManager = static_cast< ToolBarManager *>( m_xToolBarManager.get() ); + if ( pToolBarManager ) + pToolBarManager->CheckAndUpdateImages(); +} + +// XUIElementSettings +void SAL_CALL ToolBarWrapper::updateSettings() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !m_xToolBarManager.is() ) + return; + + if ( m_xConfigSource.is() && m_bPersistent ) + { + try + { + ToolBarManager* pToolBarManager = static_cast< ToolBarManager *>( m_xToolBarManager.get() ); + + m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false ); + if ( m_xConfigData.is() ) + pToolBarManager->FillToolbar( m_xConfigData ); + } + catch ( const NoSuchElementException& ) + { + } + } + else if ( !m_bPersistent ) + { + // Transient toolbar: do nothing + } +} + +void ToolBarWrapper::impl_fillNewData() +{ + // Transient toolbar => Fill toolbar with new data + ToolBarManager* pToolBarManager = static_cast< ToolBarManager *>( m_xToolBarManager.get() ); + if ( pToolBarManager ) + pToolBarManager->FillToolbar( m_xConfigData ); +} + +// XUIElement interface +Reference< XInterface > SAL_CALL ToolBarWrapper::getRealInterface( ) +{ + SolarMutexGuard g; + + if ( m_xToolBarManager.is() ) + { + ToolBarManager* pToolBarManager = static_cast< ToolBarManager *>( m_xToolBarManager.get() ); + if ( pToolBarManager ) + { + vcl::Window* pWindow = pToolBarManager->GetToolBar(); + return Reference< XInterface >( VCLUnoHelper::GetInterface( pWindow ), UNO_QUERY ); + } + } + + return Reference< XInterface >(); +} + +//XUIFunctionExecute +void SAL_CALL ToolBarWrapper::functionExecute( + const OUString& aUIElementName, + const OUString& aCommand ) +{ + SolarMutexGuard g; + + if ( m_xToolBarManager.is() ) + { + ToolBarManager* pToolBarManager = static_cast< ToolBarManager *>( m_xToolBarManager.get() ); + if ( pToolBarManager ) + pToolBarManager->notifyRegisteredControllers( aUIElementName, aCommand ); + } +} + +void SAL_CALL ToolBarWrapper::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& aValue ) +{ + SolarMutexResettableGuard aLock; + bool bNoClose( m_bNoClose ); + aLock.clear(); + + UIConfigElementWrapperBase::setFastPropertyValue_NoBroadcast( nHandle, aValue ); + + aLock.reset(); + + bool bNewNoClose( m_bNoClose ); + if ( !(m_xToolBarManager.is() && !m_bDisposed && ( bNewNoClose != bNoClose ))) + return; + + ToolBarManager* pToolBarManager = static_cast< ToolBarManager *>( m_xToolBarManager.get() ); + if ( !pToolBarManager ) + return; + + ToolBox* pToolBox = pToolBarManager->GetToolBar(); + if ( !pToolBox ) + return; + + if ( bNewNoClose ) + { + pToolBox->SetStyle( pToolBox->GetStyle() & ~WB_CLOSEABLE ); + pToolBox->SetFloatStyle( pToolBox->GetFloatStyle() & ~WB_CLOSEABLE ); + } + else + { + pToolBox->SetStyle( pToolBox->GetStyle() | WB_CLOSEABLE ); + pToolBox->SetFloatStyle( pToolBox->GetFloatStyle() | WB_CLOSEABLE ); + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/uicommanddescription.cxx b/framework/source/uielement/uicommanddescription.cxx new file mode 100644 index 000000000..8054cf4bf --- /dev/null +++ b/framework/source/uielement/uicommanddescription.cxx @@ -0,0 +1,725 @@ +/* -*- 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 <uielement/uicommanddescription.hxx> + +#include <properties.h> + +#include <helper/mischelper.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XContainer.hpp> + +#include <cppuhelper/implbase.hxx> +#include <unotools/configmgr.hxx> +#include <unotools/syslocale.hxx> + +#include <vcl/mnemonic.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/string.hxx> + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::configuration; +using namespace com::sun::star::container; +using namespace ::com::sun::star::frame; + +// Namespace + +const char CONFIGURATION_ROOT_ACCESS[] = "/org.openoffice.Office.UI."; + +// Special resource URLs to retrieve additional information +constexpr OUStringLiteral PRIVATE_RESOURCE_URL = u"private:"; + +const sal_Int32 COMMAND_PROPERTY_IMAGE = 1; +const sal_Int32 COMMAND_PROPERTY_ROTATE = 2; +const sal_Int32 COMMAND_PROPERTY_MIRROR = 4; + +namespace framework +{ + +// Configuration access class for PopupMenuControllerFactory implementation + +namespace { + +class ConfigurationAccess_UICommand : // Order is necessary for right initialization! + public ::cppu::WeakImplHelper<XNameAccess,XContainerListener> +{ + osl::Mutex m_aMutex; + public: + ConfigurationAccess_UICommand( std::u16string_view aModuleName, const Reference< XNameAccess >& xGenericUICommands, const Reference< XComponentContext >& rxContext ); + virtual ~ConfigurationAccess_UICommand() override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + + virtual sal_Bool SAL_CALL hasElements() override; + + // container.XContainerListener + virtual void SAL_CALL elementInserted( const ContainerEvent& aEvent ) override; + virtual void SAL_CALL elementRemoved ( const ContainerEvent& aEvent ) override; + virtual void SAL_CALL elementReplaced( const ContainerEvent& aEvent ) override; + + // lang.XEventListener + virtual void SAL_CALL disposing( const EventObject& aEvent ) override; + + protected: + css::uno::Any getByNameImpl( const OUString& aName ); + + struct CmdToInfoMap + { + CmdToInfoMap() : bPopup( false ), + bCommandNameCreated( false ), + bIsExperimental( false ), + nProperties( 0 ) {} + + OUString aLabel; + OUString aContextLabel; + OUString aCommandName; + OUString aPopupLabel; + OUString aTooltipLabel; + OUString aTargetURL; + bool bPopup : 1, + bCommandNameCreated : 1; + bool bIsExperimental; + sal_Int32 nProperties; + }; + + Any getSequenceFromCache( const OUString& aCommandURL ); + Any getInfoFromCommand( const OUString& rCommandURL ); + void fillInfoFromResult( CmdToInfoMap& rCmdInfo, const OUString& aLabel ); + Sequence< OUString > getAllCommands(); + void fillCache(); + void addGenericInfoToCache(); + void impl_fill(const Reference< XNameAccess >& _xConfigAccess,bool _bPopup, + std::vector< OUString >& aImageCommandVector, + std::vector< OUString >& aImageRotateVector, + std::vector< OUString >& aImageMirrorVector); + + private: + typedef std::unordered_map< OUString, + CmdToInfoMap > CommandToInfoCache; + + void initializeConfigAccess(); + + OUString m_aConfigCmdAccess; + OUString m_aConfigPopupAccess; + OUString m_aPropProperties; + Reference< XNameAccess > m_xGenericUICommands; + Reference< XMultiServiceFactory > m_xConfigProvider; + Reference< XNameAccess > m_xConfigAccess; + Reference< XContainerListener > m_xConfigListener; + Reference< XNameAccess > m_xConfigAccessPopups; + Reference< XContainerListener > m_xConfigAccessListener; + Sequence< OUString > m_aCommandImageList; + Sequence< OUString > m_aCommandRotateImageList; + Sequence< OUString > m_aCommandMirrorImageList; + CommandToInfoCache m_aCmdInfoCache; + bool m_bConfigAccessInitialized; + bool m_bCacheFilled; + bool m_bGenericDataRetrieved; +}; + +} + + +// XInterface, XTypeProvider + +ConfigurationAccess_UICommand::ConfigurationAccess_UICommand( std::u16string_view aModuleName, const Reference< XNameAccess >& rGenericUICommands, const Reference< XComponentContext>& rxContext ) : + // Create configuration hierarchical access name + m_aConfigCmdAccess( + OUString::Concat(CONFIGURATION_ROOT_ACCESS) + aModuleName + "/UserInterface/Commands"), + m_aConfigPopupAccess( + OUString::Concat(CONFIGURATION_ROOT_ACCESS) + aModuleName + "/UserInterface/Popups"), + m_aPropProperties( "Properties" ), + m_xGenericUICommands( rGenericUICommands ), + m_xConfigProvider( theDefaultProvider::get( rxContext ) ), + m_bConfigAccessInitialized( false ), + m_bCacheFilled( false ), + m_bGenericDataRetrieved( false ) +{ +} + +ConfigurationAccess_UICommand::~ConfigurationAccess_UICommand() +{ + // SAFE + osl::MutexGuard g(m_aMutex); + Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY ); + if ( xContainer.is() ) + xContainer->removeContainerListener(m_xConfigListener); + xContainer.set( m_xConfigAccessPopups, UNO_QUERY ); + if ( xContainer.is() ) + xContainer->removeContainerListener(m_xConfigAccessListener); +} + +// XNameAccess +Any ConfigurationAccess_UICommand::getByNameImpl( const OUString& rCommandURL ) +{ + osl::MutexGuard g(m_aMutex); + if ( !m_bConfigAccessInitialized ) + { + initializeConfigAccess(); + m_bConfigAccessInitialized = true; + fillCache(); + } + + if ( rCommandURL.startsWith( PRIVATE_RESOURCE_URL ) ) + { + // special keys to retrieve information about a set of commands + // SAFE + addGenericInfoToCache(); + + if ( rCommandURL.equalsIgnoreAsciiCase( UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDIMAGELIST )) + return Any( m_aCommandImageList ); + else if ( rCommandURL.equalsIgnoreAsciiCase( UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDROTATEIMAGELIST )) + return Any( m_aCommandRotateImageList ); + else if ( rCommandURL.equalsIgnoreAsciiCase( UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDMIRRORIMAGELIST )) + return Any( m_aCommandMirrorImageList ); + else + return Any(); + } + else + { + // SAFE + return getInfoFromCommand( rCommandURL ); + } +} + +Any SAL_CALL ConfigurationAccess_UICommand::getByName( const OUString& rCommandURL ) +{ + Any aRet( getByNameImpl( rCommandURL ) ); + if( !aRet.hasValue() ) + throw NoSuchElementException(); + + return aRet; +} + +Sequence< OUString > SAL_CALL ConfigurationAccess_UICommand::getElementNames() +{ + return getAllCommands(); +} + +sal_Bool SAL_CALL ConfigurationAccess_UICommand::hasByName( const OUString& rCommandURL ) +{ + return getByNameImpl( rCommandURL ).hasValue(); +} + +// XElementAccess +Type SAL_CALL ConfigurationAccess_UICommand::getElementType() +{ + return cppu::UnoType<Sequence< PropertyValue >>::get(); +} + +sal_Bool SAL_CALL ConfigurationAccess_UICommand::hasElements() +{ + // There must are global commands! + return true; +} + +void ConfigurationAccess_UICommand::fillInfoFromResult( CmdToInfoMap& rCmdInfo, const OUString& aLabel ) +{ + OUString aStr(aLabel.replaceAll("%PRODUCTNAME", utl::ConfigManager::getProductName())); + rCmdInfo.aLabel = aStr; + aStr = comphelper::string::stripEnd(aStr, '.'); // Remove "..." from string + rCmdInfo.aCommandName = MnemonicGenerator::EraseAllMnemonicChars(aStr); + rCmdInfo.bCommandNameCreated = true; +} + +Any ConfigurationAccess_UICommand::getSequenceFromCache( const OUString& aCommandURL ) +{ + CommandToInfoCache::iterator pIter = m_aCmdInfoCache.find( aCommandURL ); + if ( pIter != m_aCmdInfoCache.end() ) + { + if ( !pIter->second.bCommandNameCreated ) + fillInfoFromResult( pIter->second, pIter->second.aLabel ); + + static constexpr OUStringLiteral sLabel = u"Label"; + static constexpr OUStringLiteral sName = u"Name"; + static constexpr OUStringLiteral sPopup = u"Popup"; + static constexpr OUStringLiteral sPopupLabel = u"PopupLabel"; + static constexpr OUStringLiteral sTooltipLabel = u"TooltipLabel"; + static constexpr OUStringLiteral sTargetURL = u"TargetURL"; + static constexpr OUStringLiteral sIsExperimental = u"IsExperimental"; + Sequence< PropertyValue > aPropSeq{ + comphelper::makePropertyValue(sLabel, !pIter->second.aContextLabel.isEmpty() + ? Any(pIter->second.aContextLabel) + : Any(pIter->second.aLabel)), + comphelper::makePropertyValue(sName, pIter->second.aCommandName), + comphelper::makePropertyValue(sPopup, pIter->second.bPopup), + comphelper::makePropertyValue(m_aPropProperties, pIter->second.nProperties), + comphelper::makePropertyValue(sPopupLabel, pIter->second.aPopupLabel), + comphelper::makePropertyValue(sTooltipLabel, pIter->second.aTooltipLabel), + comphelper::makePropertyValue(sTargetURL, pIter->second.aTargetURL), + comphelper::makePropertyValue(sIsExperimental, pIter->second.bIsExperimental) + }; + return Any( aPropSeq ); + } + + return Any(); +} +void ConfigurationAccess_UICommand::impl_fill(const Reference< XNameAccess >& _xConfigAccess,bool _bPopup, + std::vector< OUString >& aImageCommandVector, + std::vector< OUString >& aImageRotateVector, + std::vector< OUString >& aImageMirrorVector) +{ + if ( !_xConfigAccess.is() ) + return; + + Sequence< OUString> aNameSeq = _xConfigAccess->getElementNames(); + const sal_Int32 nCount = aNameSeq.getLength(); + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + try + { + Reference< XNameAccess > xNameAccess(_xConfigAccess->getByName( aNameSeq[i] ),UNO_QUERY); + if ( xNameAccess.is() ) + { + CmdToInfoMap aCmdToInfo; + + aCmdToInfo.bPopup = _bPopup; + xNameAccess->getByName( "Label" ) >>= aCmdToInfo.aLabel; + xNameAccess->getByName( "ContextLabel" ) >>= aCmdToInfo.aContextLabel; + xNameAccess->getByName( "PopupLabel" ) >>= aCmdToInfo.aPopupLabel; + xNameAccess->getByName( "TooltipLabel" ) >>= aCmdToInfo.aTooltipLabel; + xNameAccess->getByName( "TargetURL" ) >>= aCmdToInfo.aTargetURL; + xNameAccess->getByName( "IsExperimental" ) >>= aCmdToInfo.bIsExperimental; + xNameAccess->getByName( m_aPropProperties ) >>= aCmdToInfo.nProperties; + + m_aCmdInfoCache.emplace( aNameSeq[i], aCmdToInfo ); + + if ( aCmdToInfo.nProperties & COMMAND_PROPERTY_IMAGE ) + aImageCommandVector.push_back( aNameSeq[i] ); + if ( aCmdToInfo.nProperties & COMMAND_PROPERTY_ROTATE ) + aImageRotateVector.push_back( aNameSeq[i] ); + if ( aCmdToInfo.nProperties & COMMAND_PROPERTY_MIRROR ) + aImageMirrorVector.push_back( aNameSeq[i] ); + } + } + catch (const css::lang::WrappedTargetException&) + { + } + catch (const css::container::NoSuchElementException&) + { + } + } +} +void ConfigurationAccess_UICommand::fillCache() +{ + + if ( m_bCacheFilled ) + return; + + std::vector< OUString > aImageCommandVector; + std::vector< OUString > aImageRotateVector; + std::vector< OUString > aImageMirrorVector; + + impl_fill(m_xConfigAccess,false,aImageCommandVector,aImageRotateVector,aImageMirrorVector); + impl_fill(m_xConfigAccessPopups,true,aImageCommandVector,aImageRotateVector,aImageMirrorVector); + // Create cached sequences for fast retrieving + m_aCommandImageList = comphelper::containerToSequence( aImageCommandVector ); + m_aCommandRotateImageList = comphelper::containerToSequence( aImageRotateVector ); + m_aCommandMirrorImageList = comphelper::containerToSequence( aImageMirrorVector ); + + m_bCacheFilled = true; +} + +void ConfigurationAccess_UICommand::addGenericInfoToCache() +{ + if ( !m_xGenericUICommands.is() || m_bGenericDataRetrieved ) + return; + + Sequence< OUString > aCommandNameSeq; + try + { + if ( m_xGenericUICommands->getByName( + UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDROTATEIMAGELIST ) >>= aCommandNameSeq ) + m_aCommandRotateImageList = comphelper::concatSequences< OUString >( m_aCommandRotateImageList, aCommandNameSeq ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + + try + { + if ( m_xGenericUICommands->getByName( + UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDMIRRORIMAGELIST ) >>= aCommandNameSeq ) + m_aCommandMirrorImageList = comphelper::concatSequences< OUString >( m_aCommandMirrorImageList, aCommandNameSeq ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + + m_bGenericDataRetrieved = true; +} + +Any ConfigurationAccess_UICommand::getInfoFromCommand( const OUString& rCommandURL ) +{ + Any a; + + try + { + a = getSequenceFromCache( rCommandURL ); + if ( !a.hasValue() ) + { + // First try to ask our global commands configuration access. It also caches maybe + // we find the entry in its cache first. + if ( m_xGenericUICommands.is() && m_xGenericUICommands->hasByName( rCommandURL ) ) + { + try + { + return m_xGenericUICommands->getByName( rCommandURL ); + } + catch (const css::lang::WrappedTargetException&) + { + } + catch (const css::container::NoSuchElementException&) + { + } + } + } + } + catch (const css::container::NoSuchElementException&) + { + } + catch (const css::lang::WrappedTargetException&) + { + } + + return a; +} + +Sequence< OUString > ConfigurationAccess_UICommand::getAllCommands() +{ + // SAFE + osl::MutexGuard g(m_aMutex); + + if ( !m_bConfigAccessInitialized ) + { + initializeConfigAccess(); + m_bConfigAccessInitialized = true; + fillCache(); + } + + if ( m_xConfigAccess.is() ) + { + try + { + Sequence< OUString > aNameSeq = m_xConfigAccess->getElementNames(); + + if ( m_xGenericUICommands.is() ) + { + // Create concat list of supported user interface commands of the module + Sequence< OUString > aGenericNameSeq = m_xGenericUICommands->getElementNames(); + sal_uInt32 nCount1 = aNameSeq.getLength(); + sal_uInt32 nCount2 = aGenericNameSeq.getLength(); + + aNameSeq.realloc( nCount1 + nCount2 ); + OUString* pNameSeq = aNameSeq.getArray(); + const OUString* pGenericSeq = aGenericNameSeq.getConstArray(); + for ( sal_uInt32 i = 0; i < nCount2; i++ ) + pNameSeq[nCount1+i] = pGenericSeq[i]; + } + + return aNameSeq; + } + catch (const css::container::NoSuchElementException&) + { + } + catch (const css::lang::WrappedTargetException&) + { + } + } + + return Sequence< OUString >(); +} + +void ConfigurationAccess_UICommand::initializeConfigAccess() +{ + try + { + Sequence<Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"nodepath", Any(m_aConfigCmdAccess)} + })); + m_xConfigAccess.set( m_xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", aArgs ),UNO_QUERY ); + if ( m_xConfigAccess.is() ) + { + // Add as container listener + Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY ); + if ( xContainer.is() ) + { + m_xConfigListener = new WeakContainerListener(this); + xContainer->addContainerListener(m_xConfigListener); + } + } + + Sequence<Any> aArgs2(comphelper::InitAnyPropertySequence( + { + {"nodepath", Any(m_aConfigPopupAccess)} + })); + m_xConfigAccessPopups.set( m_xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", aArgs2 ),UNO_QUERY ); + if ( m_xConfigAccessPopups.is() ) + { + // Add as container listener + Reference< XContainer > xContainer( m_xConfigAccessPopups, UNO_QUERY ); + if ( xContainer.is() ) + { + m_xConfigAccessListener = new WeakContainerListener(this); + xContainer->addContainerListener(m_xConfigAccessListener); + } + } + } + catch (const WrappedTargetException&) + { + } + catch (const Exception&) + { + } +} + +// container.XContainerListener +void SAL_CALL ConfigurationAccess_UICommand::elementInserted( const ContainerEvent& ) +{ + osl::MutexGuard g(m_aMutex); + m_bCacheFilled = false; + fillCache(); +} + +void SAL_CALL ConfigurationAccess_UICommand::elementRemoved( const ContainerEvent& ) +{ + osl::MutexGuard g(m_aMutex); + m_bCacheFilled = false; + fillCache(); +} + +void SAL_CALL ConfigurationAccess_UICommand::elementReplaced( const ContainerEvent& ) +{ + osl::MutexGuard g(m_aMutex); + m_bCacheFilled = false; + fillCache(); +} + +// lang.XEventListener +void SAL_CALL ConfigurationAccess_UICommand::disposing( const EventObject& aEvent ) +{ + // SAFE + // remove our reference to the config access + osl::MutexGuard g(m_aMutex); + + Reference< XInterface > xIfac1( aEvent.Source, UNO_QUERY ); + Reference< XInterface > xIfac2( m_xConfigAccess, UNO_QUERY ); + if ( xIfac1 == xIfac2 ) + m_xConfigAccess.clear(); + else + { + xIfac1.set( m_xConfigAccessPopups, UNO_QUERY ); + if ( xIfac1 == xIfac2 ) + m_xConfigAccessPopups.clear(); + } +} + +void UICommandDescription::ensureGenericUICommandsForLanguage(const LanguageTag& rLanguage) +{ + auto xGenericUICommands = m_xGenericUICommands.find(rLanguage); + if (xGenericUICommands == m_xGenericUICommands.end()) + { + Reference< XNameAccess > xEmpty; + m_xGenericUICommands[rLanguage] = new ConfigurationAccess_UICommand( u"GenericCommands", xEmpty, m_xContext ); + } +} + +UICommandDescription::UICommandDescription(const Reference< XComponentContext >& rxContext) + : m_aPrivateResourceURL(PRIVATE_RESOURCE_URL) + , m_xContext(rxContext) +{ + SvtSysLocale aSysLocale; + const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag(); + + ensureGenericUICommandsForLanguage(rCurrentLanguage); + + impl_fillElements("ooSetupFactoryCommandConfigRef"); + + // insert generic commands + auto& rMap = m_aUICommandsHashMap[rCurrentLanguage]; + UICommandsHashMap::iterator pIter = rMap.find( "GenericCommands" ); + if ( pIter != rMap.end() ) + pIter->second = m_xGenericUICommands[rCurrentLanguage]; +} + +UICommandDescription::UICommandDescription(const Reference< XComponentContext >& rxContext, bool) + : m_xContext(rxContext) +{ +} + +UICommandDescription::~UICommandDescription() +{ + std::unique_lock g(m_aMutex); + m_aModuleToCommandFileMap.clear(); + m_aUICommandsHashMap.clear(); + m_xGenericUICommands.clear(); +} +void UICommandDescription::impl_fillElements(const char* _pName) +{ + m_xModuleManager.set( ModuleManager::create( m_xContext ) ); + const Sequence< OUString > aElementNames = m_xModuleManager->getElementNames(); + + SvtSysLocale aSysLocale; + + for ( OUString const & aModuleIdentifier : aElementNames ) + { + Sequence< PropertyValue > aSeq; + if ( m_xModuleManager->getByName( aModuleIdentifier ) >>= aSeq ) + { + OUString aCommandStr; + for ( PropertyValue const & prop : std::as_const(aSeq) ) + { + if ( prop.Name.equalsAscii(_pName) ) + { + prop.Value >>= aCommandStr; + break; + } + } + + // Create first mapping ModuleIdentifier ==> Command File + m_aModuleToCommandFileMap.emplace( aModuleIdentifier, aCommandStr ); + + // Create second mapping Command File ==> commands instance + const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag(); + auto& rMap = m_aUICommandsHashMap[rCurrentLanguage]; + UICommandsHashMap::iterator pIter = rMap.find( aCommandStr ); + if ( pIter == rMap.end() ) + rMap.emplace( aCommandStr, Reference< XNameAccess >() ); + } + } // for ( sal_Int32 i = 0; i < aElementNames.(); i++ ) +} + +Any SAL_CALL UICommandDescription::getByName( const OUString& aName ) +{ + SvtSysLocale aSysLocale; + const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag(); + Any a; + + std::unique_lock g(m_aMutex); + + ModuleToCommandFileMap::const_iterator pM2CIter = m_aModuleToCommandFileMap.find( aName ); + if ( pM2CIter != m_aModuleToCommandFileMap.end() ) + { + OUString aCommandFile( pM2CIter->second ); + auto pMapIter = m_aUICommandsHashMap.find( rCurrentLanguage ); + if ( pMapIter == m_aUICommandsHashMap.end() ) + impl_fillElements("ooSetupFactoryCommandConfigRef"); + + auto& rMap = m_aUICommandsHashMap[rCurrentLanguage]; + UICommandsHashMap::iterator pIter = rMap.find( aCommandFile ); + if ( pIter != rMap.end() ) + { + if ( pIter->second.is() ) + a <<= pIter->second; + else + { + ensureGenericUICommandsForLanguage(rCurrentLanguage); + + Reference< XNameAccess > xUICommands = new ConfigurationAccess_UICommand( aCommandFile, + m_xGenericUICommands[rCurrentLanguage], + m_xContext ); + pIter->second = xUICommands; + a <<= xUICommands; + } + } + } + else if ( !m_aPrivateResourceURL.isEmpty() && aName.startsWith( m_aPrivateResourceURL ) ) + { + ensureGenericUICommandsForLanguage(rCurrentLanguage); + + // special keys to retrieve information about a set of commands + return m_xGenericUICommands[rCurrentLanguage]->getByName( aName ); + } + else + { + throw NoSuchElementException(); + } + + return a; +} + +Sequence< OUString > SAL_CALL UICommandDescription::getElementNames() +{ + std::unique_lock g(m_aMutex); + + return comphelper::mapKeysToSequence( m_aModuleToCommandFileMap ); +} + +sal_Bool SAL_CALL UICommandDescription::hasByName( const OUString& aName ) +{ + std::unique_lock g(m_aMutex); + + ModuleToCommandFileMap::const_iterator pIter = m_aModuleToCommandFileMap.find( aName ); + return ( pIter != m_aModuleToCommandFileMap.end() ); +} + +// XElementAccess +Type SAL_CALL UICommandDescription::getElementType() +{ + return cppu::UnoType<XNameAccess>::get(); +} + +sal_Bool SAL_CALL UICommandDescription::hasElements() +{ + // generic UI commands are always available! + return true; +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_UICommandDescription_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new framework::UICommandDescription(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |