summaryrefslogtreecommitdiffstats
path: root/framework/source/uielement
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /framework/source/uielement
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'framework/source/uielement')
-rw-r--r--framework/source/uielement/FixedImageToolbarController.cxx139
-rw-r--r--framework/source/uielement/FixedTextToolbarController.cxx127
-rw-r--r--framework/source/uielement/addonstoolbarwrapper.cxx171
-rw-r--r--framework/source/uielement/buttontoolbarcontroller.cxx280
-rw-r--r--framework/source/uielement/comboboxtoolbarcontroller.cxx333
-rw-r--r--framework/source/uielement/complextoolbarcontroller.cxx326
-rw-r--r--framework/source/uielement/controlmenucontroller.cxx327
-rw-r--r--framework/source/uielement/dropdownboxtoolbarcontroller.cxx282
-rw-r--r--framework/source/uielement/edittoolbarcontroller.cxx217
-rw-r--r--framework/source/uielement/fontmenucontroller.cxx218
-rw-r--r--framework/source/uielement/fontsizemenucontroller.cxx285
-rw-r--r--framework/source/uielement/footermenucontroller.cxx73
-rw-r--r--framework/source/uielement/genericstatusbarcontroller.cxx157
-rw-r--r--framework/source/uielement/generictoolbarcontroller.cxx323
-rw-r--r--framework/source/uielement/headermenucontroller.cxx237
-rw-r--r--framework/source/uielement/imagebuttontoolbarcontroller.cxx148
-rw-r--r--framework/source/uielement/langselectionmenucontroller.cxx294
-rw-r--r--framework/source/uielement/langselectionstatusbarcontroller.cxx359
-rw-r--r--framework/source/uielement/macrosmenucontroller.cxx170
-rw-r--r--framework/source/uielement/menubarmanager.cxx1572
-rw-r--r--framework/source/uielement/menubarmerger.cxx430
-rw-r--r--framework/source/uielement/menubarwrapper.cxx352
-rw-r--r--framework/source/uielement/newmenucontroller.cxx513
-rw-r--r--framework/source/uielement/objectmenucontroller.cxx136
-rw-r--r--framework/source/uielement/popuptoolbarcontroller.cxx799
-rw-r--r--framework/source/uielement/progressbarwrapper.cxx310
-rw-r--r--framework/source/uielement/recentfilesmenucontroller.cxx411
-rw-r--r--framework/source/uielement/resourcemenucontroller.cxx584
-rw-r--r--framework/source/uielement/spinfieldtoolbarcontroller.cxx452
-rw-r--r--framework/source/uielement/statusbar.cxx89
-rw-r--r--framework/source/uielement/statusbaritem.cxx236
-rw-r--r--framework/source/uielement/statusbarmanager.cxx656
-rw-r--r--framework/source/uielement/statusbarmerger.cxx237
-rw-r--r--framework/source/uielement/statusbarwrapper.cxx171
-rw-r--r--framework/source/uielement/statusindicatorinterfacewrapper.cxx102
-rw-r--r--framework/source/uielement/styletoolbarcontroller.cxx243
-rw-r--r--framework/source/uielement/subtoolbarcontroller.cxx547
-rw-r--r--framework/source/uielement/thesaurusmenucontroller.cxx185
-rw-r--r--framework/source/uielement/togglebuttontoolbarcontroller.cxx270
-rw-r--r--framework/source/uielement/toolbarmanager.cxx2365
-rw-r--r--framework/source/uielement/toolbarmerger.cxx645
-rw-r--r--framework/source/uielement/toolbarmodemenucontroller.cxx294
-rw-r--r--framework/source/uielement/toolbarsmenucontroller.cxx794
-rw-r--r--framework/source/uielement/toolbarwrapper.cxx328
-rw-r--r--framework/source/uielement/uicommanddescription.cxx725
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: */