summaryrefslogtreecommitdiffstats
path: root/framework/source/uielement
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /framework/source/uielement
parentInitial commit. (diff)
downloadlibreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz
libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--framework/source/uielement/FixedImageToolbarController.cxx108
-rw-r--r--framework/source/uielement/FixedTextToolbarController.cxx94
-rw-r--r--framework/source/uielement/addonstoolbarmanager.cxx432
-rw-r--r--framework/source/uielement/addonstoolbarwrapper.cxx173
-rw-r--r--framework/source/uielement/buttontoolbarcontroller.cxx282
-rw-r--r--framework/source/uielement/comboboxtoolbarcontroller.cxx327
-rw-r--r--framework/source/uielement/complextoolbarcontroller.cxx339
-rw-r--r--framework/source/uielement/controlmenucontroller.cxx361
-rw-r--r--framework/source/uielement/dropdownboxtoolbarcontroller.cxx275
-rw-r--r--framework/source/uielement/edittoolbarcontroller.cxx209
-rw-r--r--framework/source/uielement/fontmenucontroller.cxx217
-rw-r--r--framework/source/uielement/fontsizemenucontroller.cxx310
-rw-r--r--framework/source/uielement/footermenucontroller.cxx54
-rw-r--r--framework/source/uielement/genericstatusbarcontroller.cxx157
-rw-r--r--framework/source/uielement/generictoolbarcontroller.cxx373
-rw-r--r--framework/source/uielement/headermenucontroller.cxx228
-rw-r--r--framework/source/uielement/imagebuttontoolbarcontroller.cxx148
-rw-r--r--framework/source/uielement/langselectionmenucontroller.cxx285
-rw-r--r--framework/source/uielement/langselectionstatusbarcontroller.cxx356
-rw-r--r--framework/source/uielement/macrosmenucontroller.cxx159
-rw-r--r--framework/source/uielement/menubarmanager.cxx1889
-rw-r--r--framework/source/uielement/menubarmerger.cxx429
-rw-r--r--framework/source/uielement/menubarwrapper.cxx321
-rw-r--r--framework/source/uielement/newmenucontroller.cxx512
-rw-r--r--framework/source/uielement/objectmenucontroller.cxx147
-rw-r--r--framework/source/uielement/popuptoolbarcontroller.cxx837
-rw-r--r--framework/source/uielement/progressbarwrapper.cxx315
-rw-r--r--framework/source/uielement/recentfilesmenucontroller.cxx388
-rw-r--r--framework/source/uielement/resourcemenucontroller.cxx413
-rw-r--r--framework/source/uielement/spinfieldtoolbarcontroller.cxx494
-rw-r--r--framework/source/uielement/statusbar.cxx89
-rw-r--r--framework/source/uielement/statusbaritem.cxx237
-rw-r--r--framework/source/uielement/statusbarmanager.cxx660
-rw-r--r--framework/source/uielement/statusbarmerger.cxx236
-rw-r--r--framework/source/uielement/statusbarwrapper.cxx170
-rw-r--r--framework/source/uielement/statusindicatorinterfacewrapper.cxx102
-rw-r--r--framework/source/uielement/styletoolbarcontroller.cxx241
-rw-r--r--framework/source/uielement/subtoolbarcontroller.cxx447
-rw-r--r--framework/source/uielement/thesaurusmenucontroller.cxx174
-rw-r--r--framework/source/uielement/togglebuttontoolbarcontroller.cxx273
-rw-r--r--framework/source/uielement/toolbarmanager.cxx1939
-rw-r--r--framework/source/uielement/toolbarmerger.cxx650
-rw-r--r--framework/source/uielement/toolbarmodemenucontroller.cxx361
-rw-r--r--framework/source/uielement/toolbarsmenucontroller.cxx810
-rw-r--r--framework/source/uielement/toolbarwrapper.cxx313
-rw-r--r--framework/source/uielement/uicommanddescription.cxx714
46 files changed, 18048 insertions, 0 deletions
diff --git a/framework/source/uielement/FixedImageToolbarController.cxx b/framework/source/uielement/FixedImageToolbarController.cxx
new file mode 100644
index 000000000..c42bb518a
--- /dev/null
+++ b/framework/source/uielement/FixedImageToolbarController.cxx
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/fixed.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
+{
+FixedImageToolbarController::FixedImageToolbarController(
+ const Reference<XComponentContext>& rxContext, const Reference<XFrame>& rFrame,
+ ToolBox* pToolbar, sal_uInt16 nID, const OUString& aCommand)
+ : ComplexToolbarController(rxContext, rFrame, pToolbar, nID, aCommand)
+ , m_eSymbolSize(SvtMiscOptions().GetCurrentSymbolsSize())
+{
+ m_pFixedImageControl = VclPtr<FixedImage>::Create(m_xToolbar, 0);
+ m_xToolbar->SetItemWindow(m_nID, m_pFixedImageControl);
+
+ bool bBigImages(SvtMiscOptions().AreCurrentSymbolsLarge());
+
+ Image aImage = AddonsOptions().GetImageFromURL(aCommand, bBigImages, true);
+ m_pFixedImageControl->SetImage(aImage);
+ m_pFixedImageControl->SetSizePixel(m_pFixedImageControl->GetOptimalSize());
+
+ 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
+ auto aSize(m_pFixedImageControl->GetOptimalSize());
+ 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);
+ }
+ else
+ {
+ aSize.setWidth(16);
+ aSize.setHeight(16);
+ }
+ 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..531c0f8b6
--- /dev/null
+++ b/framework/source/uielement/FixedTextToolbarController.cxx
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/FixedTextToolbarController.hxx>
+
+#include <vcl/toolbox.hxx>
+#include <vcl/fixed.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
+{
+FixedTextToolbarController::FixedTextToolbarController(
+ const Reference<XComponentContext>& rxContext, const Reference<XFrame>& rFrame,
+ ToolBox* pToolbar, sal_uInt16 nID, const OUString& aCommand)
+ : ComplexToolbarController(rxContext, rFrame, pToolbar, nID, aCommand)
+{
+ m_pFixedTextControl = VclPtr<FixedText>::Create(m_xToolbar, WB_NOMULTILINE | WB_VCENTER
+ | WB_LEFT | WB_NOPOINTERFOCUS);
+ 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
+{
+ Sequence<PropertyValue> aArgs(2);
+ const OUString aSelectedText = m_pFixedTextControl->GetText();
+
+ // Add key modifier to argument list
+ aArgs[0].Name = "KeyModifier";
+ aArgs[0].Value <<= KeyModifier;
+ aArgs[1].Name = "Text";
+ aArgs[1].Value <<= 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->SetText(aText);
+ m_pFixedTextControl->SetSizePixel(m_pFixedTextControl->GetOptimalSize());
+
+ // send notification
+ notifyTextChanged(aText);
+ break;
+ }
+ }
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/addonstoolbarmanager.cxx b/framework/source/uielement/addonstoolbarmanager.cxx
new file mode 100644
index 000000000..e2d612599
--- /dev/null
+++ b/framework/source/uielement/addonstoolbarmanager.cxx
@@ -0,0 +1,432 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/addonstoolbarmanager.hxx>
+#include <uielement/toolbarmerger.hxx>
+
+#include <classes/resource.hxx>
+#include <framework/addonsoptions.hxx>
+
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XToolbarController.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/ui/DockingArea.hpp>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <comphelper/propertysequence.hxx>
+#include <o3tl/safeint.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <svtools/miscopt.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/commandinfoprovider.hxx>
+
+// namespaces
+
+using namespace ::com::sun::star;
+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::util;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::ui;
+
+namespace framework
+{
+
+AddonsToolBarManager::AddonsToolBarManager( const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rFrame,
+ const OUString& rResourceName,
+ ToolBox* pToolBar ) :
+ ToolBarManager( rxContext, rFrame, rResourceName, pToolBar )
+{
+ m_pToolBar->SetMenuType( ToolBoxMenuType::ClippedItems );
+ m_pToolBar->SetSelectHdl( LINK( this, AddonsToolBarManager, Select) );
+ m_pToolBar->SetClickHdl( LINK( this, AddonsToolBarManager, Click ) );
+ m_pToolBar->SetDoubleClickHdl( LINK( this, AddonsToolBarManager, DoubleClick ) );
+ m_pToolBar->SetStateChangedHdl( LINK( this, AddonsToolBarManager, StateChanged ) );
+ m_pToolBar->SetDataChangedHdl( LINK( this, AddonsToolBarManager, DataChanged ) );
+}
+
+AddonsToolBarManager::~AddonsToolBarManager()
+{
+}
+
+static bool IsCorrectContext( const OUString& rModuleIdentifier, const OUString& aContextList )
+{
+ if ( aContextList.isEmpty() )
+ return true;
+
+ if ( !rModuleIdentifier.isEmpty() )
+ {
+ sal_Int32 nIndex = aContextList.indexOf( rModuleIdentifier );
+ return ( nIndex >= 0 );
+ }
+
+ return false;
+}
+
+static Image RetrieveImage( Reference< css::frame::XFrame > const & rFrame,
+ const OUString& aImageId,
+ const OUString& aURL,
+ bool bBigImage
+)
+{
+ vcl::ImageType eImageType = vcl::ImageType::Size16;
+ if (bBigImage)
+ eImageType = vcl::ImageType::Size26;
+
+ Image aImage;
+
+ if ( !aImageId.isEmpty() )
+ {
+ aImage = framework::AddonsOptions().GetImageFromURL( aImageId, bBigImage );
+ if ( !!aImage )
+ return aImage;
+ else
+ aImage = vcl::CommandInfoProvider::GetImageForCommand(aImageId, rFrame, eImageType);
+ if ( !!aImage )
+ return aImage;
+ }
+
+ aImage = framework::AddonsOptions().GetImageFromURL( aURL, bBigImage );
+ if ( !aImage )
+ aImage = vcl::CommandInfoProvider::GetImageForCommand(aImageId, rFrame, eImageType);
+
+ return aImage;
+}
+
+// XComponent
+void SAL_CALL AddonsToolBarManager::dispose()
+{
+ Reference< XComponent > xThis( static_cast< OWeakObject* >(this), UNO_QUERY );
+
+ {
+ // Remove addon specific data from toolbar items.
+ SolarMutexGuard g;
+ for ( ToolBox::ImplToolItems::size_type n = 0; n < m_pToolBar->GetItemCount(); n++ )
+ {
+ sal_uInt16 nId( m_pToolBar->GetItemId( n ) );
+
+ if ( nId > 0 )
+ {
+ AddonsParams* pRuntimeItemData = static_cast<AddonsParams*>(m_pToolBar->GetItemData( nId ));
+ delete pRuntimeItemData;
+ m_pToolBar->SetItemData( nId, nullptr );
+ }
+ }
+ }
+
+ // Base class will destroy our m_pToolBar member
+ ToolBarManager::dispose();
+}
+
+bool AddonsToolBarManager::MenuItemAllowed( sal_uInt16 nId ) const
+{
+ return ( nId != MENUITEM_TOOLBAR_VISIBLEBUTTON ) &&
+ ( nId != MENUITEM_TOOLBAR_CUSTOMIZETOOLBAR );
+}
+
+void AddonsToolBarManager::RefreshImages()
+{
+ bool bBigImages( SvtMiscOptions().AreCurrentSymbolsLarge() );
+ for ( ToolBox::ImplToolItems::size_type nPos = 0; nPos < m_pToolBar->GetItemCount(); nPos++ )
+ {
+ sal_uInt16 nId( m_pToolBar->GetItemId( nPos ) );
+
+ if ( nId > 0 )
+ {
+ OUString aCommandURL = m_pToolBar->GetItemCommand( nId );
+ OUString aImageId;
+ AddonsParams* pRuntimeItemData = static_cast<AddonsParams*>(m_pToolBar->GetItemData( nId ));
+ if ( pRuntimeItemData )
+ aImageId = pRuntimeItemData->aImageId;
+
+ m_pToolBar->SetItemImage(
+ nId,
+ RetrieveImage( m_xFrame, aImageId, aCommandURL, bBigImages )
+ );
+ }
+ }
+ m_pToolBar->SetToolboxButtonSize( bBigImages ? ToolBoxButtonSize::Large : ToolBoxButtonSize::Small );
+ ::Size aSize = m_pToolBar->CalcWindowSizePixel();
+ m_pToolBar->SetOutputSizePixel( aSize );
+}
+
+void AddonsToolBarManager::FillToolbar( const Sequence< Sequence< PropertyValue > >& rAddonToolbar )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ sal_uInt16 nId( 1 );
+
+ RemoveControllers();
+
+ m_pToolBar->Clear();
+ m_aControllerMap.clear();
+
+ OUString aModuleIdentifier;
+ try
+ {
+ Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext );
+ aModuleIdentifier = xModuleManager->identify( m_xFrame );
+ }
+ catch ( const Exception& )
+ {
+ }
+
+ sal_uInt32 nElements( 0 );
+ bool bAppendSeparator( false );
+ Reference< XWindow > xToolbarWindow = VCLUnoHelper::GetInterface( m_pToolBar );
+ for ( const Sequence< PropertyValue >& rSeq : rAddonToolbar )
+ {
+ OUString aURL;
+ OUString aTitle;
+ OUString aImageId;
+ OUString aContext;
+ OUString aTarget;
+ OUString aControlType;
+ sal_uInt16 nWidth( 0 );
+
+ ToolBarMerger::ConvertSequenceToValues( rSeq, aURL, aTitle, aImageId, aTarget, aContext, aControlType, nWidth );
+
+ if ( IsCorrectContext( aModuleIdentifier, aContext ))
+ {
+ if ( aURL == "private:separator" ) // toolbox item separator
+ {
+ ToolBox::ImplToolItems::size_type nCount = m_pToolBar->GetItemCount();
+ if ( nCount > 0 && ( m_pToolBar->GetItemType( nCount-1 ) != ToolBoxItemType::SEPARATOR ) && nElements > 0 )
+ {
+ nElements = 0;
+ m_pToolBar->InsertSeparator();
+ }
+ }
+ else
+ {
+ ToolBox::ImplToolItems::size_type nCount = m_pToolBar->GetItemCount();
+ if ( bAppendSeparator && nCount > 0 && ( m_pToolBar->GetItemType( nCount-1 ) != ToolBoxItemType::SEPARATOR ))
+ {
+ // We have to append a separator first if the last item is not a separator
+ m_pToolBar->InsertSeparator();
+ }
+ bAppendSeparator = false;
+
+
+ m_pToolBar->InsertItem( nId, aTitle );
+
+ OUString aShortcut(vcl::CommandInfoProvider::GetCommandShortcut(aURL, m_xFrame));
+ if (!aShortcut.isEmpty())
+ m_pToolBar->SetQuickHelpText(nId, aTitle + " (" + aShortcut + ")");
+
+ // don't setup images yet, AddonsToolbarWrapper::populateImages does that.
+
+ // Create TbRuntimeItemData to hold additional information we will need in the future
+ AddonsParams* pRuntimeItemData = new AddonsParams;
+ pRuntimeItemData->aImageId = aImageId;
+ pRuntimeItemData->aControlType = aControlType;
+ pRuntimeItemData->nWidth = nWidth;
+ m_pToolBar->SetItemData( nId, pRuntimeItemData );
+ m_pToolBar->SetItemCommand( nId, aURL );
+
+ Reference< XStatusListener > xController;
+
+ bool bMustBeInit( true );
+
+ // Support external toolbar controller for add-ons!
+ if ( m_xToolbarControllerFactory.is() &&
+ m_xToolbarControllerFactory->hasController( aURL, m_aModuleIdentifier ))
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"ModuleIdentifier", uno::Any(m_aModuleIdentifier)},
+ {"Frame", uno::Any(m_xFrame)},
+ {"ServiceManager", uno::Any(Reference<XMultiServiceFactory>(m_xContext->getServiceManager(), UNO_QUERY_THROW))},
+ {"ParentWindow", uno::Any(xToolbarWindow)},
+ {"ItemId", uno::Any(sal_Int32( nId ))}
+ }));
+ try
+ {
+ xController.set( m_xToolbarControllerFactory->createInstanceWithArgumentsAndContext(
+ aURL, aArgs, m_xContext ),
+ UNO_QUERY );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ bMustBeInit = false; // factory called init already!
+ }
+ else
+ {
+ ::cppu::OWeakObject* pController = ToolBarMerger::CreateController( m_xContext, m_xFrame, m_pToolBar, aURL, nId, nWidth, aControlType );
+ xController.set( pController, UNO_QUERY );
+ }
+
+ // insert controller to the map
+ m_aControllerMap[nId] = xController;
+
+ Reference< XInitialization > xInit( xController, UNO_QUERY );
+ if ( xInit.is() && bMustBeInit )
+ {
+ PropertyValue aPropValue;
+ Sequence< Any > aArgs( 3 );
+ aPropValue.Name = "Frame";
+ aPropValue.Value <<= m_xFrame;
+ aArgs[0] <<= aPropValue;
+ aPropValue.Name = "CommandURL";
+ aPropValue.Value <<= aURL;
+ aArgs[1] <<= aPropValue;
+ aPropValue.Name = "ServiceManager";
+ aPropValue.Value <<= Reference<XMultiServiceFactory>(m_xContext->getServiceManager(), UNO_QUERY_THROW);
+ aArgs[2] <<= aPropValue;
+ try
+ {
+ xInit->initialize( aArgs );
+ }
+ catch ( const uno::Exception& )
+ {
+ }
+ }
+
+ // 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 ( nType == WindowType::LISTBOX || nType == WindowType::MULTILISTBOX || nType == WindowType::COMBOBOX )
+ pItemWin->SetAccessibleName( m_pToolBar->GetItemText( nId ) );
+ m_pToolBar->SetItemWindow( nId, pItemWin );
+ }
+ }
+ }
+
+ // Notify controller implementation to its listeners. Controller is now usable from outside.
+ Reference< XUpdatable > xUpdatable( xController, UNO_QUERY );
+ if ( xUpdatable.is() )
+ {
+ try
+ {
+ xUpdatable->update();
+ }
+ catch ( const uno::Exception& )
+ {
+ }
+ }
+
+ ++nId;
+ ++nElements;
+ }
+ }
+ }
+
+ AddFrameActionListener();
+}
+
+IMPL_LINK_NOARG(AddonsToolBarManager, Click, ToolBox *, void)
+{
+ if ( m_bDisposed )
+ return;
+
+ sal_uInt16 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->click();
+ }
+}
+
+IMPL_LINK_NOARG(AddonsToolBarManager, DoubleClick, ToolBox *, void)
+{
+ if ( m_bDisposed )
+ return;
+
+ sal_uInt16 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->doubleClick();
+ }
+}
+
+IMPL_LINK_NOARG(AddonsToolBarManager, Select, ToolBox *, void)
+{
+ if ( m_bDisposed )
+ return;
+
+ sal_Int16 nKeyModifier( static_cast<sal_Int16>(m_pToolBar->GetModifier()) );
+ sal_uInt16 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( AddonsToolBarManager, StateChanged, StateChangedType const *, pStateChangedType, void )
+{
+ if ( *pStateChangedType == StateChangedType::ControlBackground )
+ {
+ CheckAndUpdateImages();
+ }
+}
+
+IMPL_LINK( AddonsToolBarManager, 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 sal_uInt16 nId = m_pToolBar->GetItemId(nPos);
+ vcl::Window* pWindow = m_pToolBar->GetItemWindow( nId );
+ if ( pWindow )
+ {
+ const DataChangedEvent& rDCEvt( *pDataChangedEvent );
+ pWindow->DataChanged( rDCEvt );
+ }
+ }
+}
+
+}
+
+/* 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..5d08fb5c7
--- /dev/null
+++ b/framework/source/uielement/addonstoolbarwrapper.cxx
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/addonstoolbarmanager.hxx>
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/ui/UIElementType.hpp>
+
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <svtools/miscopt.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( static_cast< OWeakObject* >(this), UNO_QUERY );
+
+ 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;
+ AddonsToolBarManager* pToolBarManager = nullptr;
+ {
+ 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 AddonsToolBarManager( m_xContext, xFrame, m_aResourceURL, pToolBar );
+ m_xToolBarManager.set( static_cast< OWeakObject *>( pToolBarManager ), UNO_QUERY );
+ }
+ }
+
+ try
+ {
+ if ( m_aConfigData.hasElements() && pToolBar && pToolBarManager )
+ {
+ // Fill toolbar with container contents
+ pToolBarManager->FillToolbar( m_aConfigData );
+ pToolBar->SetOutStyle( SvtMiscOptions().GetToolboxStyle() );
+ 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() )
+ {
+ AddonsToolBarManager* pToolBarManager = static_cast< AddonsToolBarManager *>( 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() )
+ {
+ AddonsToolBarManager* pToolBarManager = static_cast< AddonsToolBarManager *>( m_xToolBarManager.get() );
+ if (pToolBarManager)
+ {
+ pToolBarManager->RefreshImages();
+ 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..6c5c3fdeb
--- /dev/null
+++ b/framework/source/uielement/buttontoolbarcontroller.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/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 <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(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ToolBox* pToolBar,
+ const OUString& aCommand ) :
+ cppu::OWeakObject(),
+ m_bInitialized( false ),
+ m_bDisposed( false ),
+ m_aCommandURL( aCommand ),
+ m_xContext( rxContext ),
+ 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() throw ()
+{
+ cppu::OWeakObject::acquire();
+}
+
+void SAL_CALL ButtonToolbarController::release() throw ()
+{
+ 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
+ {
+ Sequence<PropertyValue> aArgs( 1 );
+
+ // Provide key modifier information to dispatch function
+ aArgs[0].Name = "KeyModifier";
+ aArgs[0].Value <<= 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..1d73f3fc5
--- /dev/null
+++ b/framework/source/uielement/comboboxtoolbarcontroller.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 <uielement/comboboxtoolbarcontroller.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/util/Color.hpp>
+
+#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);
+
+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)
+{
+ 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->set_entry_width_chars(1); // so a smaller than default width can be used by ComboboxToolbarController
+ SetSizePixel(get_preferred_size());
+}
+
+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,
+ sal_uInt16 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
+{
+ Sequence<PropertyValue> aArgs( 2 );
+ OUString aSelectedText = m_pComboBox->get_active_text();
+
+ // Add key modifier to argument list
+ aArgs[0].Name = "KeyModifier";
+ aArgs[0].Value <<= KeyModifier;
+ aArgs[1].Name = "Text";
+ aArgs[1].Value <<= 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::makeAny(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..06da77962
--- /dev/null
+++ b/framework/source/uielement/complextoolbarcontroller.cxx
@@ -0,0 +1,339 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <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 <svtools/toolboxcontroller.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/mnemonic.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/settings.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,
+ sal_uInt16 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 = 0;
+}
+
+Sequence<PropertyValue> ComplexToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const
+{
+ Sequence<PropertyValue> aArgs( 1 );
+
+ // Add key modifier to argument list
+ aArgs[0].Name = "KeyModifier";
+ aArgs[0].Value <<= 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 );
+ aInfoSeq[nCount].Name = "Source";
+ aInfoSeq[nCount].Value <<= getFrameInterface();
+ pNotifyInfo->aInfoSeq = aInfoSeq;
+
+ Application::PostUserEvent( LINK(nullptr, ComplexToolbarController, Notify_Impl), pNotifyInfo );
+}
+
+sal_Int32 ComplexToolbarController::getFontSizePixel( const vcl::Window* pWindow )
+{
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ const vcl::Font& rFont = rSettings.GetAppFont();
+
+ // Calculate height of the application font used by window
+ sal_Int32 nHeight = sal_Int32( rFont.GetFontHeight() );
+ ::Size aPixelSize = pWindow->LogicToPixel(::Size(0, nHeight), MapMode(MapUnit::MapAppFont));
+ return aPixelSize.Height();
+}
+
+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::makeAny(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..4403c89cd
--- /dev/null
+++ b/framework/source/uielement/controlmenucontroller.cxx
@@ -0,0 +1,361 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/builder.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/image.hxx>
+#include <svtools/popupmenucontrollerbase.hxx>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <osl/mutex.hxx>
+#include <memory>
+#include <unordered_map>
+
+#include <bitmaps.hlst>
+
+// See svx/source/form/fmshimp.cxx for other use of this .ui
+
+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 const OUStringLiteral 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:
+ virtual void impl_setPopupMenu() override;
+
+ class UrlToDispatchMap : public std::unordered_map< OUString,
+ uno::Reference< frame::XDispatch > >
+ {
+ public:
+ void free()
+ {
+ UrlToDispatchMap().swap( *this );// get rid of reserved capacity
+ }
+ };
+
+ void updateImagesPopupMenu( PopupMenu* pPopupMenu );
+ void fillPopupMenu( uno::Reference< awt::XPopupMenu > const & rPopupMenu );
+
+ bool m_bShowMenuImages : 1;
+ std::unique_ptr<VclBuilder> m_xBuilder;
+ VclPtr<PopupMenu> m_xResPopupMenu;
+ 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( PopupMenu* pPopupMenu )
+{
+ for (size_t i=0; i < SAL_N_ELEMENTS(aCommands); ++i)
+ {
+ //ident is .uno:Command without .uno:
+ OString sIdent = OString(aCommands[i]).copy(5);
+ sal_uInt16 nId = pPopupMenu->GetItemId(sIdent);
+ if (m_bShowMenuImages)
+ pPopupMenu->SetItemImage(nId, Image(StockImage::Yes, aImgIds[i]));
+ else
+ pPopupMenu->SetItemImage(nId, Image());
+ }
+}
+
+// private function
+void ControlMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu )
+{
+ VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getUnoTunnelImplementation<VCLXMenu>( rPopupMenu ));
+ PopupMenu* pVCLPopupMenu = nullptr;
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ resetPopupMenu( rPopupMenu );
+ if ( pPopupMenu )
+ pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu());
+
+ if (pVCLPopupMenu && m_xResPopupMenu)
+ *pVCLPopupMenu = *m_xResPopupMenu;
+}
+
+// XEventListener
+void SAL_CALL ControlMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(static_cast<OWeakObject *>(this), UNO_QUERY );
+
+ osl::MutexGuard aLock( m_aMutex );
+ m_xFrame.clear();
+ m_xDispatch.clear();
+
+ if ( m_xPopupMenu.is() )
+ m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(static_cast<OWeakObject *>(this), UNO_QUERY ));
+ m_xPopupMenu.clear();
+ m_xResPopupMenu.clear();
+ m_xBuilder.reset();
+}
+
+// XStatusListener
+void SAL_CALL ControlMenuController::statusChanged( const FeatureStateEvent& Event )
+{
+ osl::MutexGuard aLock( m_aMutex );
+
+ OString sIdent;
+ for (size_t i=0; i < SAL_N_ELEMENTS(aCommands); ++i)
+ {
+ if ( Event.FeatureURL.Complete.equalsAscii( aCommands[i] ))
+ {
+ //ident is .uno:Command without .uno:
+ sIdent = OString(aCommands[i]).copy(5);
+ break;
+ }
+ }
+
+ sal_uInt16 nMenuId = 0;
+
+ VCLXPopupMenu* pPopupMenu = nullptr;
+
+ if (!sIdent.isEmpty() && m_xResPopupMenu)
+ {
+ pPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getUnoTunnelImplementation<VCLXMenu>( m_xPopupMenu ));
+ nMenuId = m_xResPopupMenu->GetItemId(sIdent);
+ }
+
+ if (!pPopupMenu)
+ return;
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ PopupMenu* pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu());
+
+ if ( !Event.IsEnabled && pVCLPopupMenu->GetItemPos( nMenuId ) != MENU_ITEM_NOTFOUND )
+ pVCLPopupMenu->RemoveItem( pVCLPopupMenu->GetItemPos( nMenuId ));
+ else if ( Event.IsEnabled && pVCLPopupMenu->GetItemPos( nMenuId ) == MENU_ITEM_NOTFOUND )
+ {
+ sal_Int16 nSourcePos = m_xResPopupMenu->GetItemPos(nMenuId);
+ sal_Int16 nPrevInSource = nSourcePos;
+ sal_uInt16 nPrevInConversion = MENU_ITEM_NOTFOUND;
+ while (nPrevInSource>0)
+ {
+ sal_Int16 nPrevId = m_xResPopupMenu->GetItemId(--nPrevInSource);
+
+ // do we have the source's predecessor in our conversion menu, too ?
+ nPrevInConversion = pVCLPopupMenu->GetItemPos( nPrevId );
+ if ( nPrevInConversion != MENU_ITEM_NOTFOUND )
+ break;
+ }
+
+ if ( MENU_ITEM_NOTFOUND == nPrevInConversion )
+ // none of the items which precede the nSID-slot in the source menu are present in our conversion menu
+ nPrevInConversion = sal::static_int_cast< sal_uInt16 >(-1); // put the item at the first position
+
+ pVCLPopupMenu->InsertItem(nMenuId, m_xResPopupMenu->GetItemText(nMenuId), m_xResPopupMenu->GetItemBits(nMenuId), OString(), ++nPrevInConversion);
+ pVCLPopupMenu->SetItemImage(nMenuId, m_xResPopupMenu->GetItemImage(nMenuId));
+ pVCLPopupMenu->SetHelpId(nMenuId, m_xResPopupMenu->GetHelpId(nMenuId));
+ }
+}
+
+// XMenuListener
+void SAL_CALL ControlMenuController::itemActivated( const css::awt::MenuEvent& )
+{
+ osl::MutexGuard aLock( m_aMutex );
+
+ if ( !m_xPopupMenu.is() )
+ return;
+
+ 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;
+
+ VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getUnoTunnelImplementation<VCLXMenu>( m_xPopupMenu ));
+ if ( pPopupMenu )
+ {
+ PopupMenu* pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu());
+ if (pVCLPopupMenu)
+ updateImagesPopupMenu( pVCLPopupMenu );
+ }
+ }
+}
+
+// XPopupMenuController
+void ControlMenuController::impl_setPopupMenu()
+{
+ if (!m_xResPopupMenu)
+ {
+ m_xBuilder.reset(new VclBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "svx/ui/convertmenu.ui", ""));
+ m_xResPopupMenu = m_xBuilder->get_menu("menu");
+ updateImagesPopupMenu(m_xResPopupMenu);
+ }
+}
+
+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..1603ab6b3
--- /dev/null
+++ b/framework/source/uielement/dropdownboxtoolbarcontroller.cxx
@@ -0,0 +1,275 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/dropdownboxtoolbarcontroller.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#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);
+
+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 )
+{
+ 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->set_size_request(42, -1); // so a later narrow size request can stick
+ SetSizePixel(get_preferred_size());
+}
+
+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,
+ sal_uInt16 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
+{
+ Sequence<PropertyValue> aArgs( 2 );
+ OUString aSelectedText = m_pListBoxControl->get_active_text();
+
+ // Add key modifier to argument list
+ aArgs[0].Name = "KeyModifier";
+ aArgs[0].Value <<= KeyModifier;
+ aArgs[1].Name = "Text";
+ aArgs[1].Value <<= 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::makeAny(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..392943bdd
--- /dev/null
+++ b/framework/source/uielement/edittoolbarcontroller.cxx
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <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);
+};
+
+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);
+
+ 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));
+
+ SetSizePixel(get_preferred_size());
+}
+
+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,
+ sal_uInt16 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
+{
+ Sequence<PropertyValue> aArgs( 2 );
+ OUString aSelectedText = m_pEditControl->get_text();
+
+ // Add key modifier to argument list
+ aArgs[0].Name = "KeyModifier";
+ aArgs[0].Value <<= KeyModifier;
+ aArgs[1].Name = "Text";
+ aArgs[1].Value <<= 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..70c0043ae
--- /dev/null
+++ b/framework/source/uielement/fontmenucontroller.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/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 <toolkit/awt/vclxmenu.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/i18nhelp.hxx>
+#include <tools/urlobj.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <vcl/mnemonic.hxx>
+#include <osl/mutex.hxx>
+
+// Defines
+
+using namespace css::uno;
+using namespace css::lang;
+using namespace css::frame;
+using namespace css::beans;
+using namespace css::util;
+
+using namespace std;
+
+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
+{
+
+DEFINE_XSERVICEINFO_MULTISERVICE_2 ( FontMenuController ,
+ OWeakObject ,
+ SERVICENAME_POPUPMENUCONTROLLER ,
+ IMPLEMENTATIONNAME_FONTMENUCONTROLLER
+ )
+
+DEFINE_INIT_SERVICE ( FontMenuController, {} )
+
+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 )
+{
+ VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getUnoTunnelImplementation<VCLXMenu>( rPopupMenu ));
+ PopupMenu* pVCLPopupMenu = nullptr;
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ resetPopupMenu( rPopupMenu );
+ if ( pPopupMenu )
+ pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu());
+
+ if ( !pVCLPopupMenu )
+ return;
+
+ 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 );
+
+ const OUString aFontNameCommandPrefix( ".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 );
+ // use VCL popup menu pointer to set vital information that are not part of the awt implementation
+ OUStringBuffer aCommandBuffer( aFontNameCommandPrefix );
+ aCommandBuffer.append( INetURLObject::encode( rName, INetURLObject::PART_HTTP_QUERY, INetURLObject::EncodeMechanism::All ));
+ OUString aFontNameCommand = aCommandBuffer.makeStringAndClear();
+ pVCLPopupMenu->SetItemCommand( i+1, aFontNameCommand ); // Store font name into item command.
+ }
+}
+
+// XEventListener
+void SAL_CALL FontMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(static_cast<OWeakObject *>(this), UNO_QUERY );
+
+ 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 >(static_cast<OWeakObject *>(this), UNO_QUERY ));
+ 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, "" );
+ // 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 );
+ }
+}
+
+}
+
+/* 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..79259c2e5
--- /dev/null
+++ b/framework/source/uielement/fontsizemenucontroller.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/fontsizemenucontroller.hxx>
+
+#include <services.h>
+
+#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 <toolkit/awt/vclxmenu.hxx>
+#include <vcl/menu.hxx>
+#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>
+
+// 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
+{
+
+DEFINE_XSERVICEINFO_MULTISERVICE_2 ( FontSizeMenuController ,
+ OWeakObject ,
+ SERVICENAME_POPUPMENUCONTROLLER ,
+ IMPLEMENTATIONNAME_FONTSIZEMENUCONTROLLER
+ )
+
+DEFINE_INIT_SERVICE ( FontSizeMenuController, {} )
+
+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( 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_pHeightArray[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 )
+{
+ VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getUnoTunnelImplementation<VCLXMenu>( rPopupMenu ));
+ PopupMenu* pVCLPopupMenu = nullptr;
+
+ resetPopupMenu( rPopupMenu );
+ if ( pPopupMenu )
+ pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu());
+
+ if ( !pVCLPopupMenu )
+ return;
+
+ 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->GetDevFontCount() > 0 )
+ pFontList.reset(new FontList( pInfoPrinter.get() ));
+ }
+
+ if ( !pFontList )
+ pFontList.reset(new FontList( Application::GetDefaultDevice() ));
+
+ FontMetric aFontMetric = pFontList->Get( m_aFontDescriptor.Name, m_aFontDescriptor.StyleName );
+
+ // setup font size array
+ m_pHeightArray.reset();
+
+ const sal_IntPtr* pTempAry;
+ const sal_IntPtr* pAry = pFontList->GetSizeAry( aFontMetric );
+ sal_uInt16 nSizeCount = 0;
+ while ( pAry[nSizeCount] )
+ nSizeCount++;
+
+ sal_uInt16 nPos = 0;
+ const OUString aFontHeightCommand( ".uno:FontHeight?FontHeight.Height:float=" );
+
+ // first insert font size names (for simplified/traditional chinese)
+ float fPoint;
+ FontSizeNames aFontSizeNames( Application::GetSettings().GetUILanguageTag().getLanguageType() );
+ m_pHeightArray.reset( new long[nSizeCount+aFontSizeNames.Count()] );
+ OUString aCommand;
+
+ if ( !aFontSizeNames.IsEmpty() )
+ {
+ if ( pAry == FontList::GetStdSizeAry() )
+ {
+ // 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_pHeightArray[nPos] = nSize;
+ nPos++; // Id is nPos+1
+ pVCLPopupMenu->InsertItem( nPos, aSizeName, MenuItemBits::RADIOCHECK | MenuItemBits::AUTOCHECK );
+ fPoint = float( m_pHeightArray[nPos-1] ) / 10;
+
+ // Create dispatchable .uno command and set it
+ aCommand = aFontHeightCommand + OUString::number( fPoint );
+ pVCLPopupMenu->SetItemCommand( nPos, aCommand );
+ }
+ }
+ else
+ {
+ // for fixed size fonts only selectable font size names
+ pTempAry = pAry;
+ while ( *pTempAry )
+ {
+ OUString aSizeName = aFontSizeNames.Size2Name( *pTempAry );
+ if ( !aSizeName.isEmpty() )
+ {
+ m_pHeightArray[nPos] = *pTempAry;
+ nPos++; // Id is nPos+1
+ pVCLPopupMenu->InsertItem( nPos, aSizeName, MenuItemBits::RADIOCHECK | MenuItemBits::AUTOCHECK );
+ fPoint = float( m_pHeightArray[nPos-1] ) / 10;
+
+ // Create dispatchable .uno command and set it
+ aCommand = aFontHeightCommand + OUString::number( fPoint );
+ pVCLPopupMenu->SetItemCommand( nPos, aCommand );
+ }
+ pTempAry++;
+ }
+ }
+ }
+
+ // then insert numerical font size values
+ const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
+ pTempAry = pAry;
+ while ( *pTempAry )
+ {
+ m_pHeightArray[nPos] = *pTempAry;
+ nPos++; // Id is nPos+1
+ pVCLPopupMenu->InsertItem( nPos, rI18nHelper.GetNum( *pTempAry, 1, true, false ), MenuItemBits::RADIOCHECK | MenuItemBits::AUTOCHECK );
+ fPoint = float( m_pHeightArray[nPos-1] ) / 10;
+
+ // Create dispatchable .uno command and set it
+ aCommand = aFontHeightCommand + OUString::number( fPoint );
+ pVCLPopupMenu->SetItemCommand( nPos, aCommand );
+
+ pTempAry++;
+ }
+
+ setCurHeight( long( m_aFontHeight.Height * 10), rPopupMenu );
+}
+
+// XEventListener
+void SAL_CALL FontSizeMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(static_cast<OWeakObject *>(this), UNO_QUERY );
+
+ 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 >(static_cast<OWeakObject *>(this), UNO_QUERY ));
+ 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 );
+ m_aFontDescriptor = aFontDescriptor;
+
+ 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( 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();
+}
+}
+
+/* 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..aef746a43
--- /dev/null
+++ b/framework/source/uielement/footermenucontroller.cxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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>
+
+// 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
+{
+
+DEFINE_XSERVICEINFO_MULTISERVICE_2 ( FooterMenuController ,
+ OWeakObject ,
+ SERVICENAME_POPUPMENUCONTROLLER ,
+ IMPLEMENTATIONNAME_FOOTERMENUCONTROLLER
+ )
+
+FooterMenuController::FooterMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) :
+ HeaderMenuController( xContext,true )
+{
+}
+
+FooterMenuController::~FooterMenuController()
+{
+}
+}
+
+/* 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..59515ce14
--- /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 <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_xGraphic()
+{
+ 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..b13739932
--- /dev/null
+++ b/framework/source/uielement/generictoolbarcontroller.cxx
@@ -0,0 +1,373 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/generictoolbarcontroller.hxx>
+
+#include <com/sun/star/util/XURLTransformer.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/frame/status/ItemStatus.hpp>
+#include <com/sun/star/frame/status/Visibility.hpp>
+#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/frame/ControlCommand.hpp>
+
+#include <svtools/toolboxcontroller.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <tools/urlobj.hxx>
+#include <strings.hrc>
+#include <classes/fwkresid.hxx>
+#include <uielement/menubarmanager.hxx>
+
+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::frame::status;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::container;
+
+namespace framework
+{
+
+static bool isEnumCommand( const OUString& rCommand )
+{
+ INetURLObject aURL( rCommand );
+
+ return ( aURL.GetProtocol() == INetProtocol::Uno ) &&
+ ( aURL.GetURLPath().indexOf( '.' ) != -1);
+}
+
+static OUString getEnumCommand( const OUString& 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().copy( 0, nIndex ) );
+ aMasterCommand = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ }
+ }
+ return aMasterCommand;
+}
+
+GenericToolbarController::GenericToolbarController( const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rFrame,
+ ToolBox* pToolbar,
+ sal_uInt16 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 ) );
+}
+
+GenericToolbarController::~GenericToolbarController()
+{
+}
+
+void SAL_CALL GenericToolbarController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ svt::ToolboxController::dispose();
+
+ m_xToolbar.clear();
+ m_nID = 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;
+ Sequence<PropertyValue> aArgs( 1 );
+
+ // Add key modifier to argument list
+ aArgs[0].Name = "KeyModifier";
+ aArgs[0].Value <<= KeyModifier;
+
+ aTargetURL.Complete = aCommandURL;
+ 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_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.copy( 4 );
+ }
+ else if ( aStrValue.startsWith("($2)") )
+ {
+ aStrValue = FwkResId(STR_CLOSEDOC_ANDRETURN) + aStrValue.copy( 4 );
+ }
+ else if ( aStrValue.startsWith("($3)") )
+ {
+ aStrValue = FwkResId(STR_SAVECOPYDOC) + aStrValue.copy( 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;
+}
+
+void MenuToolbarController::dispose()
+{
+ try
+ {
+ if ( m_xMenuManager.is() )
+ m_xMenuManager->dispose();
+ }
+ catch( const Exception& ) {}
+
+ m_xMenuManager.clear();
+ m_xMenuDesc.clear();
+ pMenu.disposeAndClear();
+}
+
+void MenuToolbarController::initialize( const css::uno::Sequence< css::uno::Any >& rArgs )
+{
+ ToolboxController::initialize( rArgs );
+
+ css::uno::Reference< css::container::XIndexAccess > xMenuContainer;
+ try
+ {
+ css::uno::Reference< css::frame::XController > xController( m_xFrame->getController() );
+ css::uno::Reference< css::ui::XUIConfigurationManagerSupplier > xSupplier( xController->getModel(), css::uno::UNO_QUERY_THROW );
+ css::uno::Reference< css::ui::XUIConfigurationManager > xConfigManager( xSupplier->getUIConfigurationManager() );
+ xMenuContainer.set( xConfigManager->getSettings( m_aCommandURL, false ) );
+ }
+ catch( const css::uno::Exception& )
+ {}
+
+ if ( !xMenuContainer.is() )
+ {
+ try
+ {
+ css::uno::Reference< css::ui::XModuleUIConfigurationManagerSupplier > xSupplier(
+ css::ui::theModuleUIConfigurationManagerSupplier::get( m_xContext ) );
+ css::uno::Reference< css::ui::XUIConfigurationManager > xConfigManager(
+ xSupplier->getUIConfigurationManager( m_sModuleName ) );
+ xMenuContainer.set( xConfigManager->getSettings( m_aCommandURL, false ) );
+ }
+ catch( const css::uno::Exception& )
+ {}
+ }
+
+ if ( !(xMenuContainer.is() && xMenuContainer->getCount()) )
+ return;
+
+ Sequence< PropertyValue > aProps;
+ // drop down menu info is currently the first ( and only ) menu in the menusettings container
+ xMenuContainer->getByIndex(0) >>= aProps;
+ for ( const auto& aProp : std::as_const(aProps) )
+ {
+ if ( aProp.Name == "ItemDescriptorContainer" )
+ {
+ aProp.Value >>= m_xMenuDesc;
+ break;
+ }
+ }
+
+ ToolBox* pToolBox = nullptr;
+ sal_uInt16 nId = 0;
+ if ( getToolboxId( nId, &pToolBox ) )
+ pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY );
+}
+
+Reference< XWindow > SAL_CALL
+MenuToolbarController::createPopupWindow()
+{
+ if ( !pMenu )
+ {
+ pMenu = VclPtr<PopupMenu>::Create();
+ css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider( m_xFrame, css::uno::UNO_QUERY );
+ sal_uInt16 m_nMenuId = 1;
+ MenuBarManager::FillMenu( m_nMenuId, pMenu, m_sModuleName, m_xMenuDesc, xDispatchProvider );
+ m_xMenuManager.set( new MenuBarManager( m_xContext, m_xFrame, m_xUrlTransformer, xDispatchProvider, m_sModuleName, pMenu, false, false ) );
+ }
+
+ ToolBox* pToolBox = nullptr;
+ sal_uInt16 nId = 0;
+ if ( !getToolboxId( nId, &pToolBox ) )
+ return nullptr;
+
+ pToolBox->SetItemDown( m_nToolBoxId, true );
+ pMenu->Execute( pToolBox, pToolBox->GetItemRect( nId ), PopupMenuFlags::ExecuteDown );
+ pToolBox->SetItemDown( m_nToolBoxId, false );
+
+ return nullptr;
+}
+} // 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..7e74ef99f
--- /dev/null
+++ b/framework/source/uielement/headermenucontroller.cxx
@@ -0,0 +1,228 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <toolkit/awt/vclxmenu.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/svapp.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/mutex.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
+{
+
+DEFINE_XSERVICEINFO_MULTISERVICE_2 ( HeaderMenuController ,
+ OWeakObject ,
+ SERVICENAME_POPUPMENUCONTROLLER ,
+ IMPLEMENTATIONNAME_HEADERMENUCONTROLLER
+ )
+
+DEFINE_INIT_SERVICE ( HeaderMenuController, {} )
+
+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 )
+{
+ VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getUnoTunnelImplementation<VCLXMenu>( rPopupMenu ));
+ PopupMenu* pVCLPopupMenu = nullptr;
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ resetPopupMenu( rPopupMenu );
+ if ( pPopupMenu )
+ pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu());
+
+ Reference< XStyleFamiliesSupplier > xStyleFamiliesSupplier( rModel, UNO_QUERY );
+ if ( !(pVCLPopupMenu && xStyleFamiliesSupplier.is()))
+ return;
+
+ Reference< XNameAccess > xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
+
+ OUString aCmd( ".uno:InsertPageHeader" );
+ OUString aHeaderFooterIsOnStr( "HeaderIsOn" );
+ if ( m_bFooter )
+ {
+ aCmd = ".uno:InsertPageFooter";
+ aHeaderFooterIsOnStr = "FooterIsOn";
+ }
+ const OUString aIsPhysicalStr( "IsPhysical" );
+ const OUString aDisplayNameStr( "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() );
+ pVCLPopupMenu->InsertItem( nId, aDisplayName, MenuItemBits::CHECKABLE );
+ if ( !bFirstItemInserted )
+ {
+ bFirstItemInserted = true;
+ bFirstChecked = bHeaderIsOn;
+ }
+
+ pVCLPopupMenu->SetItemCommand( nId, aCommand );
+
+ if ( bHeaderIsOn )
+ pVCLPopupMenu->CheckItem( nId );
+ ++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
+ pVCLPopupMenu->InsertItem( ALL_MENUITEM_ID, FwkResId(STR_MENU_HEADFOOTALL), MenuItemBits::NONE, OString(), 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" );
+
+ pVCLPopupMenu->SetItemCommand( 1, aStrBuf.makeStringAndClear() );
+ pVCLPopupMenu->InsertSeparator(OString(), 1);
+ }
+ }
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ }
+}
+
+// XEventListener
+void SAL_CALL HeaderMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(static_cast<OWeakObject *>(this), UNO_QUERY );
+
+ osl::MutexGuard aLock( m_aMutex );
+ m_xFrame.clear();
+ m_xDispatch.clear();
+
+ if ( m_xPopupMenu.is() )
+ m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(static_cast<OWeakObject *>(this), UNO_QUERY ));
+ 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 );
+}
+
+}
+
+/* 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..5c0cbeba8
--- /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,
+ sal_uInt16 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::makeAny(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 ))
+ {
+ // Use graphic class to also support more graphic formats (bmp,png,...)
+ Graphic aGraphic;
+
+ GraphicFilter& rGF = GraphicFilter::GetGraphicFilter();
+ rGF.ImportGraphic( aGraphic, OUString(), *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..a0e60acd8
--- /dev/null
+++ b/framework/source/uielement/langselectionmenucontroller.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 <uielement/langselectionmenucontroller.hxx>
+
+#include <services.h>
+
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+
+#include <toolkit/awt/vclxmenu.hxx>
+#include <vcl/menu.hxx>
+#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 <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
+{
+
+DEFINE_XSERVICEINFO_MULTISERVICE_2 ( LanguageSelectionMenuController ,
+ OWeakObject ,
+ SERVICENAME_POPUPMENUCONTROLLER ,
+ IMPLEMENTATIONNAME_LANGUAGESELECTIONMENUCONTROLLER
+ )
+
+DEFINE_INIT_SERVICE ( LanguageSelectionMenuController, {} )
+
+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(static_cast<OWeakObject *>(this), UNO_QUERY );
+
+ 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 >(static_cast<OWeakObject *>(this), UNO_QUERY ));
+ 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 )
+{
+ VCLXPopupMenu* pVCLPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getUnoTunnelImplementation<VCLXMenu>( rPopupMenu ));
+ PopupMenu* pPopupMenu = nullptr;
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ resetPopupMenu( rPopupMenu );
+ if (!m_bShowMenu)
+ return;
+
+ if ( pVCLPopupMenu )
+ pPopupMenu = static_cast<PopupMenu *>(pVCLPopupMenu->GetMenu());
+
+ 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 = 1; // in this control the item id is not important for executing the command
+ const OUString sAsterisk("*"); // 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
+ {
+ pPopupMenu->InsertItem( nItemId, langItem);
+ OUString aCmd = aCmd_Language + langItem;
+ pPopupMenu->SetItemCommand( nItemId, aCmd );
+ if (langItem == m_aCurLang && eMode == MODE_SetLanguageSelectionMenu )
+ {
+ //make a sign for the current language
+ pPopupMenu->CheckItem( nItemId );
+ }
+ ++nItemId;
+ }
+ }
+
+ // entry for LANGUAGE_NONE
+ ++nItemId;
+ pPopupMenu->InsertItem( nItemId, FwkResId(STR_LANGSTATUS_NONE) );
+ OUString aCmd = aCmd_Language + "LANGUAGE_NONE";
+ pPopupMenu->SetItemCommand( nItemId, aCmd );
+
+ // entry for 'Reset to default language'
+ ++nItemId;
+ pPopupMenu->InsertItem( nItemId, FwkResId(STR_RESET_TO_DEFAULT_LANGUAGE) );
+ aCmd = aCmd_Language + "RESET_LANGUAGES";
+ pPopupMenu->SetItemCommand( nItemId, aCmd );
+
+ // entry for opening the Format/Character dialog
+ ++nItemId;
+ pPopupMenu->InsertItem( nItemId, FwkResId(STR_LANGSTATUS_MORE));
+ pPopupMenu->SetItemCommand( 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 bInitalized( m_bInitialized );
+ if ( !bInitalized )
+ {
+ 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";
+ }
+ }
+}
+
+}
+
+/* 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..2e12c12fe
--- /dev/null
+++ b/framework/source/uielement/langselectionstatusbarcontroller.cxx
@@ -0,0 +1,356 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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);
+ const OUString sAsterisk("*"); // 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_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 );
+
+ // 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_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..e6700d0eb
--- /dev/null
+++ b/framework/source/uielement/macrosmenucontroller.cxx
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/style/XStyleFamiliesSupplier.hpp>
+#include <officecfg/Office/Common.hxx>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/commandinfoprovider.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 com::sun::star::style;
+using namespace com::sun::star::container;
+
+namespace framework
+{
+class
+DEFINE_XSERVICEINFO_MULTISERVICE_2 ( MacrosMenuController ,
+ OWeakObject ,
+ SERVICENAME_POPUPMENUCONTROLLER ,
+ IMPLEMENTATIONNAME_MACROSMENUCONTROLLER
+ )
+
+DEFINE_INIT_SERVICE ( MacrosMenuController, {} )
+
+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;
+
+ VCLXPopupMenu* pVCLPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getUnoTunnelImplementation<VCLXMenu>( rPopupMenu ));
+ PopupMenu* pPopupMenu = nullptr;
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ resetPopupMenu( rPopupMenu );
+ if ( pVCLPopupMenu )
+ pPopupMenu = static_cast<PopupMenu *>(pVCLPopupMenu->GetMenu());
+
+ if (!pPopupMenu)
+ return;
+
+ // insert basic
+ OUString aCommand(".uno:MacroDialog");
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommand, m_aModuleName);
+ OUString aDisplayName = vcl::CommandInfoProvider::GetMenuLabelForCommand(aProperties);
+ pPopupMenu->InsertItem( 2, aDisplayName );
+ pPopupMenu->SetItemCommand( 2, aCommand );
+
+ // insert providers but not basic or java
+ addScriptItems( pPopupMenu, 4);
+}
+
+// XEventListener
+void SAL_CALL MacrosMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(static_cast<OWeakObject *>(this), UNO_QUERY );
+
+ 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 >(static_cast<OWeakObject *>(this), UNO_QUERY ));
+ }
+ 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( PopupMenu* pPopupMenu, sal_uInt16 startItemId )
+{
+ const OUString aCmdBase(".uno:ScriptOrganizer?ScriptOrganizer.Language:string=");
+ const OUString ellipsis( "..." );
+ const OUString providerKey("com.sun.star.script.provider.ScriptProviderFor");
+ const OUString languageProviderName("com.sun.star.script.provider.LanguageScriptProvider");
+ sal_uInt16 itemId = startItemId;
+ Reference< XContentEnumerationAccess > xEnumAccess( m_xContext->getServiceManager(), UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnum = xEnumAccess->createContentEnumeration ( languageProviderName );
+
+ 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;
+ pPopupMenu->InsertItem( itemId, aDisplayName );
+ pPopupMenu->SetItemCommand( itemId, aCommand );
+ itemId++;
+ break;
+ }
+ }
+ }
+}
+
+}
+
+/* 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..f7ff1e18d
--- /dev/null
+++ b/framework/source/uielement/menubarmanager.cxx
@@ -0,0 +1,1889 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/frame/Desktop.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/ImageType.hpp>
+#include <com/sun/star/frame/ModuleManager.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/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <svtools/menuoptions.hxx>
+#include <svtools/javainteractionhandler.hxx>
+#include <uno/current_context.hxx>
+#include <unotools/cmdoptions.hxx>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/window.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;
+
+namespace framework
+{
+
+#define aCmdHelpIndex ".uno:HelpIndex"
+#define aCmdToolsMenu ".uno:ToolsMenu"
+#define aCmdHelpMenu ".uno:HelpMenu"
+#define aSpecialWindowCommand ".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 ):
+ WeakComponentImplHelper( m_aMutex )
+ , m_bRetrieveImages( false )
+ , m_bAcceleratorCfg( false )
+ , m_bModuleIdentified( false )
+ , m_bHasMenuBar( bHasMenuBar )
+ , m_xContext(rxContext)
+ , m_xURLTransformer(_xURLTransformer)
+ , m_sIconTheme( SvtMiscOptions().GetIconTheme() )
+{
+ m_aAsyncSettingsTimer.SetDebugName( "framework::MenuBarManager::Deactivate m_aAsyncSettingsTimer" );
+ m_xPopupMenuControllerFactory = frame::thePopupMenuControllerFactory::get(m_xContext);
+ FillMenuManager( pMenu, rFrame, rDispatchProvider, rModuleIdentifier, bDelete );
+}
+
+MenuBarManager::MenuBarManager(
+ const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rFrame,
+ const Reference< XURLTransformer >& _xURLTransformer,
+ Menu* pAddonMenu,
+ bool popup):
+ WeakComponentImplHelper( m_aMutex )
+ , m_bRetrieveImages( true )
+ , m_bAcceleratorCfg( false )
+ , m_bModuleIdentified( false )
+ , m_bHasMenuBar( true )
+ , m_xContext(rxContext)
+ , m_xURLTransformer(_xURLTransformer)
+ , m_sIconTheme( SvtMiscOptions().GetIconTheme() )
+{
+ m_aAsyncSettingsTimer.SetDebugName( "framework::MenuBarManager::Deactivate m_aAsyncSettingsTimer" );
+ Init(rFrame,pAddonMenu, popup);
+}
+
+Any SAL_CALL MenuBarManager::getMenuHandle( const Sequence< sal_Int8 >& /*ProcessId*/, sal_Int16 SystemType )
+{
+ SolarMutexGuard aSolarGuard;
+
+ if ( rBHelper.bDisposed || rBHelper.bInDispose )
+ 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_xDeferedItemContainer.clear();
+ m_aAsyncSettingsTimer.Stop();
+
+ SAL_WARN_IF( OWeakObject::m_refCount != 0, "fwk.uielement", "Who wants to delete an object with refcount > 0!" );
+}
+
+void MenuBarManager::Destroy()
+{
+ SolarMutexGuard aGuard;
+
+ if ( rBHelper.bDisposed )
+ return;
+
+ // stop asynchronous settings timer and
+ // release deferred item container reference
+ m_aAsyncSettingsTimer.Stop();
+ m_xDeferedItemContainer.clear();
+ RemoveListener();
+
+ m_aMenuItemHandlerVector.clear();
+
+ if ( m_bDeleteMenu )
+ {
+ m_pVCLMenu.disposeAndClear();
+ }
+}
+
+// XComponent
+void SAL_CALL MenuBarManager::disposing()
+{
+ Reference< XComponent > xThis( static_cast< OWeakObject* >(this), UNO_QUERY );
+
+ SolarMutexGuard g;
+ Destroy();
+
+ if ( m_xDocImageManager.is() )
+ {
+ try
+ {
+ m_xDocImageManager->removeConfigurationListener(
+ Reference< XUIConfigurationListener >(
+ static_cast< OWeakObject* >( this ), UNO_QUERY ));
+ }
+ catch ( const Exception& )
+ {
+ }
+ }
+ if ( m_xModuleImageManager.is() )
+ {
+ try
+ {
+ m_xModuleImageManager->removeConfigurationListener(
+ Reference< XUIConfigurationListener >(
+ static_cast< OWeakObject* >( this ), UNO_QUERY ));
+ }
+ 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 ( rBHelper.bDisposed || rBHelper.bInDispose )
+ return;
+
+ sal_Int16 nImageType = sal_Int16();
+ if (( Event.aInfo >>= nImageType ) &&
+ ( nImageType == css::ui::ImageType::SIZE_LARGE ))
+ 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 ( rBHelper.bDisposed || rBHelper.bInDispose )
+ 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 ( rBHelper.bDisposed || rBHelper.bInDispose )
+ 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.copy( 4 );
+ }
+ else if ( aItemText.startsWith("($2)") )
+ {
+ aItemText = FwkResId(STR_CLOSEDOC_ANDRETURN) + aItemText.copy( 4 );
+ }
+ else if ( aItemText.startsWith("($3)") )
+ {
+ aItemText = FwkResId(STR_SAVECOPYDOC) + aItemText.copy( 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 >(
+ static_cast< OWeakObject* >( this ), UNO_QUERY ));
+ }
+ 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 >
+ const & context):
+ context_(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 )
+ {
+ css::uno::ContextLayer layer(
+ new QuietInteractionContext(
+ css::uno::getCurrentContext()));
+
+ // set/unset hiding disabled menu entries
+ bool bDontHide = SvtMenuOptions().IsEntryHidingEnabled();
+ 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;
+
+ if ( m_aMenuItemCommand == aSpecialWindowCommand )
+ UpdateSpecialWindowMenu( pMenu, m_xContext );
+
+ // 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() )
+ {
+ SvtCommandOptions aCmdOptions;
+ for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
+ {
+ if (menuItemHandler)
+ {
+ if ( !menuItemHandler->xMenuItemDispatch.is() &&
+ !menuItemHandler->xSubMenuManager.is() )
+ {
+ // There is no dispatch mechanism for the special window list menu items,
+ // because they are handled directly through XFrame->activate!!!
+ // Don't update dispatches for special file menu items.
+ if ( !( menuItemHandler->nItemId >= START_ITEMID_WINDOWLIST &&
+ menuItemHandler->nItemId < END_ITEMID_WINDOWLIST ) )
+ {
+ 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 if ( m_bIsBookmarkMenu )
+ xMenuItemDispatch = xDispatchProvider->queryDispatch( aTargetURL, menuItemHandler->aTargetFrame, 0 );
+ else
+ xMenuItemDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 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());
+ }
+ 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_xDeferedItemContainer.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_xDeferedItemContainer.is() )
+ {
+ SetItemContainer( m_xDeferedItemContainer );
+ m_xDeferedItemContainer.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 )
+ {
+ if ( nCurItemId >= START_ITEMID_WINDOWLIST &&
+ nCurItemId <= END_ITEMID_WINDOWLIST )
+ {
+ // window list menu item selected
+
+ Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( m_xContext );
+
+ sal_uInt16 nTaskId = START_ITEMID_WINDOWLIST;
+ Reference< XIndexAccess > xList = xDesktop->getFrames();
+ sal_Int32 nCount = xList->getCount();
+ for ( sal_Int32 i=0; i<nCount; ++i )
+ {
+ Reference< XFrame > xFrame;
+ xList->getByIndex(i) >>= xFrame;
+ if ( xFrame.is() && nTaskId == nCurItemId )
+ {
+ VclPtr<vcl::Window> pWin = VCLUnoHelper::GetWindow( xFrame->getContainerWindow() );
+ pWin->GrabFocus();
+ pWin->ToTop( ToTopFlags::RestoreWhenMin );
+ break;
+ }
+
+ nTaskId++;
+ }
+ }
+ else
+ {
+ MenuItemHandler* pMenuItemHandler = GetMenuItemHandler( nCurItemId );
+ if ( pMenuItemHandler && pMenuItemHandler->xMenuItemDispatch.is() )
+ {
+ aTargetURL.Complete = pMenuItemHandler->aMenuItemURL;
+ m_xURLTransformer->parseStrict( aTargetURL );
+
+ if ( m_bIsBookmarkMenu )
+ {
+ // bookmark menu item selected
+ aArgs.realloc( 1 );
+ aArgs[0].Name = "Referer";
+ aArgs[0].Value <<= 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 )
+ {
+ 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 );
+ }
+
+ return true;
+}
+
+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 )
+{
+ OUString aItemCommand( pMenuItemHandler->aMenuItemURL );
+
+ // Try instantiate a popup menu controller. It is stored in the menu item handler.
+ if ( !m_xPopupMenuControllerFactory.is() )
+ return false;
+
+ Sequence< Any > aSeq( 3 );
+ aSeq[0] <<= comphelper::makePropertyValue( "ModuleIdentifier", m_aModuleIdentifier );
+ aSeq[1] <<= comphelper::makePropertyValue( "Frame", m_xFrame );
+ aSeq[2] <<= comphelper::makePropertyValue( "InToolbar", !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_bIsBookmarkMenu = false;
+ m_xDispatchProvider = rDispatchProvider;
+
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ m_bShowMenuImages = rSettings.GetUseImagesInMenus();
+ m_bRetrieveImages = false;
+
+ // 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 );
+
+ // Set module identifier when provided from outside
+ if ( !rModuleIdentifier.isEmpty() )
+ {
+ m_aModuleIdentifier = rModuleIdentifier;
+ m_bModuleIdentified = true;
+ }
+
+ 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, "" );
+ }
+
+ if ( m_xPopupMenuControllerFactory.is() &&
+ pPopup->GetItemCount() == 0 &&
+ 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. Remove old dummy popup menu!
+ MenuItemHandler* pItemHandler = new MenuItemHandler( nItemId, xStatusListener, xDispatch );
+ VCLXPopupMenu* pVCLXPopupMenu = new VCLXPopupMenu;
+ PopupMenu* pNewPopupMenu = static_cast<PopupMenu *>(pVCLXPopupMenu->GetMenu());
+ pMenu->SetPopupMenu( nItemId, pNewPopupMenu );
+ pItemHandler->xPopupMenu = pVCLXPopupMenu;
+ pItemHandler->aMenuItemURL = aItemCommand;
+ m_aMenuItemHandlerVector.push_back( std::unique_ptr<MenuItemHandler>(pItemHandler) );
+ pPopup.disposeAndClear();
+
+ if ( bAccessibilityEnabled )
+ {
+ if ( CreatePopupMenuController( pItemHandler ))
+ pItemHandler->xPopupMenuController->updatePopupMenu();
+ }
+ lcl_CheckForChildren(pMenu, nItemId);
+ }
+ else if ( aItemCommand.startsWith( ADDONSPOPUPMENU_URL_PREFIX_STR ) )
+ {
+ // A special addon popup menu, must be created with a different ctor
+ MenuBarManager* pSubMenuManager = new MenuBarManager( m_xContext, m_xFrame, m_xURLTransformer, pPopup, true );
+ AddMenu(pSubMenuManager,aItemCommand,nItemId);
+ }
+ else
+ {
+ Reference< XDispatchProvider > xPopupMenuDispatchProvider( rDispatchProvider );
+
+ // Retrieve possible attributes struct
+ MenuAttributes* pAttributes = static_cast<MenuAttributes *>(pMenu->GetUserValue( nItemId ));
+ if ( pAttributes )
+ xPopupMenuDispatchProvider = pAttributes->xDispatchProvider;
+
+ // Check if this is the help menu. Add menu item if needed
+ if ( aItemCommand == aCmdHelpMenu )
+ {
+ }
+ else 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();
+ }
+
+ MenuBarManager* pSubMenuManager;
+ if ( nItemId == ITEMID_ADDONLIST )
+ pSubMenuManager = new MenuBarManager( m_xContext, m_xFrame, m_xURLTransformer, pPopup, false );
+ else
+ pSubMenuManager = new MenuBarManager( m_xContext, rFrame, m_xURLTransformer,
+ rDispatchProvider, aModuleIdentifier,
+ pPopup, false, m_bHasMenuBar );
+
+ AddMenu(pSubMenuManager, aItemCommand, nItemId);
+ }
+ }
+ else if ( pMenu->GetItemType( i ) != MenuItemType::SEPARATOR )
+ {
+ if ( bItemShowMenuImages )
+ m_bRetrieveImages = true;
+
+ std::unique_ptr<MenuItemHandler> pItemHandler(new MenuItemHandler( nItemId, xStatusListener, xDispatch ));
+ 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.
+ VCLXPopupMenu* pVCLXPopupMenu = new VCLXPopupMenu;
+ PopupMenu* pPopupMenu = static_cast<PopupMenu *>(pVCLXPopupMenu->GetMenu());
+ pMenu->SetPopupMenu( pItemHandler->nItemId, pPopupMenu );
+ pItemHandler->xPopupMenu = pVCLXPopupMenu;
+
+ if ( bAccessibilityEnabled && CreatePopupMenuController( pItemHandler.get() ) )
+ {
+ 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 )
+{
+ if ( !m_bModuleIdentified )
+ {
+ m_bModuleIdentified = true;
+ Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext );
+
+ try
+ {
+ m_aModuleIdentifier = xModuleManager->identify( m_xFrame );
+ }
+ catch( const Exception& )
+ {
+ }
+ }
+
+ 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 );
+ 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() );
+ const sal_uInt32 nCount = aMenuShortCuts.size();
+ for ( sal_uInt32 i = 0; i < nCount; ++i )
+ {
+ OUString aItemURL = aMenuShortCuts[i]->aMenuItemURL;
+ if( aItemURL.isEmpty() && aMenuShortCuts[i]->xSubMenuManager.is())
+ aItemURL = "-"; // tdf#99527 prevent throw in case of empty commands
+ aSeq[i] = aItemURL;
+ 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 >(
+ static_cast< OWeakObject* >( this ), UNO_QUERY ));
+ }
+ }
+ }
+ }
+
+ Reference< XModuleManager2 > xModuleManager;
+ if ( m_aModuleIdentifier.isEmpty() )
+ xModuleManager.set( ModuleManager::create( m_xContext ) );
+
+ try
+ {
+ if ( xModuleManager.is() )
+ 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 >(
+ static_cast< OWeakObject* >( this ), UNO_QUERY ));
+ }
+}
+
+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) &&
+ !SvtMiscOptions().IsExperimentalMode())
+ {
+ 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;
+
+ if ( !m_bModuleIdentified )
+ {
+ m_bModuleIdentified = true;
+ Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext );
+
+ try
+ {
+ m_aModuleIdentifier = xModuleManager->identify( xFrame );
+ }
+ catch( const Exception& )
+ {
+ }
+ }
+
+ // Clear MenuBarManager structures
+ {
+ // Check active state as we cannot change our VCL menu during activation by the user
+ if ( m_bActive )
+ {
+ m_xDeferedItemContainer = 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 >( static_cast< OWeakObject* >( this ), UNO_QUERY ));
+ }
+}
+
+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 nQueryPart( 0 );
+ sal_Int32 nSchemePart( 0 );
+ OUString aMainURL( "vnd.sun.star.popup:" );
+ OUString aMenuURL( menuItemHandler->aMenuItemURL );
+
+ nSchemePart = aMenuURL.indexOf( ':' );
+ if (( nSchemePart > 0 ) &&
+ ( aMenuURL.getLength() > ( nSchemePart+1 )))
+ {
+ nQueryPart = aMenuURL.indexOf( '?', nSchemePart );
+ if ( nQueryPart > 0 )
+ aMainURL += aMenuURL.copy( nSchemePart, nQueryPart-nSchemePart );
+ else if ( nQueryPart == -1 )
+ aMainURL += aMenuURL.copy( 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( static_cast< OWeakObject *>( pSubMenuManager ), UNO_QUERY );
+ m_xFrame->addFrameActionListener( Reference< XFrameActionListener >( xSubMenuManager, UNO_QUERY ));
+
+ // store menu item command as we later have to know which menu is active (see Activate handler)
+ pSubMenuManager->m_aMenuItemCommand = _sItemCommand;
+ 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::Init(const Reference< XFrame >& rFrame, Menu* pAddonMenu, bool _bHandlePopUp)
+{
+ m_bActive = false;
+ m_bDeleteMenu = false;
+ m_pVCLMenu = pAddonMenu;
+ m_xFrame = rFrame;
+ m_bIsBookmarkMenu = true;
+ m_bShowMenuImages = true;
+
+ m_xPopupMenuControllerFactory = frame::thePopupMenuControllerFactory::get(
+ ::comphelper::getProcessComponentContext());
+
+ Reference< XStatusListener > xStatusListener;
+ Reference< XDispatch > xDispatch;
+ sal_uInt16 nItemCount = pAddonMenu->GetItemCount();
+ OUString aItemCommand;
+ m_aMenuItemHandlerVector.reserve(nItemCount);
+ for ( sal_uInt16 i = 0; i < nItemCount; i++ )
+ {
+ sal_uInt16 nItemId = FillItemCommand(aItemCommand,pAddonMenu, i );
+
+ PopupMenu* pPopupMenu = pAddonMenu->GetPopupMenu( nItemId );
+ if ( pPopupMenu )
+ {
+ Reference< XDispatchProvider > xDispatchProvider;
+ MenuBarManager* pSubMenuManager = new MenuBarManager( m_xContext, rFrame, m_xURLTransformer,
+ xDispatchProvider, OUString(), pPopupMenu,
+ false );
+
+ Reference< XStatusListener > xSubMenuManager( static_cast< OWeakObject *>( pSubMenuManager ), UNO_QUERY );
+
+ // store menu item command as we later have to know which menu is active (see Activate handler)
+ pSubMenuManager->m_aMenuItemCommand = aItemCommand;
+
+ std::unique_ptr<MenuItemHandler> pMenuItemHandler(new MenuItemHandler(
+ nItemId,
+ xSubMenuManager,
+ xDispatch ));
+ m_aMenuItemHandlerVector.push_back( std::move(pMenuItemHandler) );
+ }
+ else
+ {
+ if ( pAddonMenu->GetItemType( i ) != MenuItemType::SEPARATOR )
+ {
+ MenuAttributes* pAddonAttributes = static_cast<MenuAttributes *>(pAddonMenu->GetUserValue( nItemId ));
+ std::unique_ptr<MenuItemHandler> pMenuItemHandler(new MenuItemHandler( nItemId, xStatusListener, xDispatch ));
+
+ if ( pAddonAttributes )
+ {
+ // read additional attributes from attributes struct and AddonMenu implementation
+ // will delete all attributes itself!!
+ pMenuItemHandler->aTargetFrame = pAddonAttributes->aTargetFrame;
+ }
+
+ pMenuItemHandler->aMenuItemURL = aItemCommand;
+ if ( _bHandlePopUp )
+ {
+ // 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.
+ if ( m_xPopupMenuControllerFactory.is() &&
+ m_xPopupMenuControllerFactory->hasController( aItemCommand, m_aModuleIdentifier ) )
+ {
+ VCLXPopupMenu* pVCLXPopupMenu = new VCLXPopupMenu;
+ PopupMenu* pCtlPopupMenu = static_cast<PopupMenu *>(pVCLXPopupMenu->GetMenu());
+ pAddonMenu->SetPopupMenu( pMenuItemHandler->nItemId, pCtlPopupMenu );
+ pMenuItemHandler->xPopupMenu = pVCLXPopupMenu;
+
+ }
+ }
+ m_aMenuItemHandlerVector.push_back( std::move(pMenuItemHandler) );
+ }
+ }
+ }
+
+ SetHdl();
+}
+
+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::UpdateSpecialWindowMenu( Menu* pMenu,const Reference< XComponentContext >& xContext )
+{
+ // update window list
+ ::std::vector< OUString > aNewWindowListVector;
+
+ Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( xContext );
+
+ sal_uInt16 nActiveItemId = 0;
+ sal_uInt16 nItemId = START_ITEMID_WINDOWLIST;
+
+ Reference< XFrame > xCurrentFrame = xDesktop->getCurrentFrame();
+ Reference< XIndexAccess > xList = xDesktop->getFrames();
+ sal_Int32 nFrameCount = xList->getCount();
+ aNewWindowListVector.reserve(nFrameCount);
+ for (sal_Int32 i=0; i<nFrameCount; ++i )
+ {
+ Reference< 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;
+
+ int nItemCount = pMenu->GetItemCount();
+
+ if ( nItemCount > 0 )
+ {
+ // remove all old window list entries from menu
+ sal_uInt16 nPos = pMenu->GetItemPos( START_ITEMID_WINDOWLIST );
+ for ( sal_uInt16 n = nPos; n < pMenu->GetItemCount(); )
+ pMenu->RemoveItem( n );
+
+ if ( pMenu->GetItemType( pMenu->GetItemCount()-1 ) == MenuItemType::SEPARATOR )
+ pMenu->RemoveItem( pMenu->GetItemCount()-1 );
+ }
+
+ if ( !aNewWindowListVector.empty() )
+ {
+ // append new window list entries to menu
+ pMenu->InsertSeparator();
+ nItemId = START_ITEMID_WINDOWLIST;
+ const sal_uInt32 nCount = aNewWindowListVector.size();
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ pMenu->InsertItem( nItemId, aNewWindowListVector.at( i ), MenuItemBits::RADIOCHECK );
+ if ( nItemId == nActiveItemId )
+ pMenu->CheckItem( nItemId );
+ ++nItemId;
+ }
+ }
+ }
+}
+
+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 )
+ {
+ bool bImageSet = false;
+ OUString aImageId;
+
+ ::framework::MenuAttributes* pMenuAttributes =
+ static_cast< ::framework::MenuAttributes*>(_pMenu->GetUserValue( nId ));
+
+ if ( pMenuAttributes )
+ aImageId = pMenuAttributes->aImageId; // Retrieve image id from menu attributes
+
+ if ( !aImageId.isEmpty() )
+ {
+ Image aImage = vcl::CommandInfoProvider::GetImageForCommand(aImageId, _xFrame);
+ if ( !!aImage )
+ {
+ bImageSet = true;
+ _pMenu->SetItemImage( nId, aImage );
+ }
+ }
+
+ if ( !bImageSet )
+ {
+ OUString aMenuItemCommand = _pMenu->GetItemCommand( nId );
+ Image aImage = vcl::CommandInfoProvider::GetImageForCommand(aMenuItemCommand, _xFrame);
+ if ( !aImage )
+ aImage = 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..eb3b291ff
--- /dev/null
+++ b/framework/source/uielement/menubarmerger.cxx
@@ -0,0 +1,429 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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>
+
+using namespace ::com::sun::star;
+
+static const char SEPARATOR_STRING[] = "private:separator";
+
+static const char MERGECOMMAND_ADDAFTER[] = "AddAfter";
+static const char MERGECOMMAND_ADDBEFORE[] = "AddBefore";
+static const char MERGECOMMAND_REPLACE[] = "Replace";
+static const char MERGECOMMAND_REMOVE[] = "Remove";
+
+static const char MERGEFALLBACK_ADDPATH[] = "AddPath";
+static const char MERGEFALLBACK_IGNORE[] = "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( const OUString& rContext, const OUString& rModuleIdentifier )
+{
+ return ( rContext.isEmpty() || ( rContext.indexOf( rModuleIdentifier ) >= 0 ));
+}
+
+void MenuBarMerger::RetrieveReferencePath(
+ const OUString& rReferencePathString,
+ ::std::vector< OUString >& rReferencePath )
+{
+ const char aDelimiter = '\\';
+
+ rReferencePath.clear();
+ sal_Int32 nIndex( 0 );
+ do
+ {
+ OUString aToken = rReferencePathString.getToken( 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( const OUString& 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,
+ const OUString& rMergeCommandParameter )
+{
+ const sal_uInt16 nParam( sal_uInt16( rMergeCommandParameter.toInt32() ));
+ 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,
+ const OUString& rMergeCommand,
+ const OUString& 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,
+ const OUString& rMergeCommand,
+ const OUString& rMergeFallback,
+ const ::std::vector< OUString >& rReferencePath,
+ const OUString& 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] );
+
+ sal_uInt16 nInsPos( MENU_APPEND );
+ VclPtr<PopupMenu> pPopupMenu = VclPtr<PopupMenu>::Create();
+
+ if ( bFirstLevel && ( aRefPathInfo.eResult == RP_MENUITEM_INSTEAD_OF_POPUPMENU_FOUND ))
+ {
+ // special case: menu item without popup
+ 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..48700304f
--- /dev/null
+++ b/framework/source/uielement/menubarwrapper.cxx
@@ -0,0 +1,321 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <toolkit/awt/vclxmenu.hxx>
+#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
+{
+
+// XInterface, XTypeProvider
+DEFINE_XINTERFACE_11 ( MenuBarWrapper ,
+ UIConfigElementWrapperBase ,
+ DIRECT_INTERFACE( css::lang::XTypeProvider ),
+ DIRECT_INTERFACE( css::ui::XUIElement ),
+ DIRECT_INTERFACE( css::ui::XUIElementSettings ),
+ DIRECT_INTERFACE( css::beans::XMultiPropertySet ),
+ DIRECT_INTERFACE( css::beans::XFastPropertySet ),
+ DIRECT_INTERFACE( css::beans::XPropertySet ),
+ DIRECT_INTERFACE( css::lang::XInitialization ),
+ DIRECT_INTERFACE( css::lang::XComponent ),
+ DIRECT_INTERFACE( css::util::XUpdatable ),
+ DIRECT_INTERFACE( css::ui::XUIConfigurationListener ),
+ DERIVED_INTERFACE( css::container::XNameAccess, css::container::XElementAccess )
+ )
+
+DEFINE_XTYPEPROVIDER_11 ( MenuBarWrapper ,
+ css::lang::XTypeProvider ,
+ css::ui::XUIElement ,
+ css::ui::XUIElementSettings ,
+ css::beans::XMultiPropertySet ,
+ css::beans::XFastPropertySet ,
+ css::beans::XPropertySet ,
+ css::lang::XInitialization ,
+ css::lang::XComponent ,
+ css::util::XUpdatable ,
+ css::ui::XUIConfigurationListener ,
+ css::container::XNameAccess
+ )
+
+MenuBarWrapper::MenuBarWrapper(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext
+ )
+: UIConfigElementWrapperBase( UIElementType::MENUBAR ),
+ m_bRefreshPopupControllerCache( true ),
+ m_xContext( rxContext )
+{
+}
+
+MenuBarWrapper::~MenuBarWrapper()
+{
+}
+
+void SAL_CALL MenuBarWrapper::dispose()
+{
+ Reference< XComponent > xThis( static_cast< OWeakObject* >(this), UNO_QUERY );
+
+ 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;
+ VCLXMenuBar* pAwtMenuBar = nullptr;
+ {
+ 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;
+
+ MenuBarManager* pMenuBarManager = new MenuBarManager( m_xContext,
+ xFrame,
+ xTrans,
+ xDispatchProvider,
+ aModuleIdentifier,
+ pVCLMenuBar,
+ false );
+
+ m_xMenuBarManager.set( static_cast< OWeakObject *>( pMenuBarManager ), UNO_QUERY );
+ }
+
+ // 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!
+ pAwtMenuBar = new VCLXMenuBar( pVCLMenuBar );
+ m_xMenuBar = pAwtMenuBar;
+}
+
+// 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::makeAny( 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..a6d5310c2
--- /dev/null
+++ b/framework/source/uielement/newmenucontroller.cxx
@@ -0,0 +1,512 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/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 <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>
+
+// Defines
+#define aSlotNewDocDirect ".uno:AddDirect"
+#define aSlotAutoPilot ".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
+{
+
+DEFINE_XSERVICEINFO_MULTISERVICE_2 ( NewMenuController ,
+ OWeakObject ,
+ SERVICENAME_POPUPMENUCONTROLLER ,
+ IMPLEMENTATIONNAME_NEWMENUCONTROLLER
+ )
+
+DEFINE_INIT_SERVICE ( NewMenuController, {} )
+
+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( PopupMenu* pPopupMenu, const vcl::KeyCode& rKeyCode )
+{
+ sal_uInt16 nCount( pPopupMenu->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 ( pPopupMenu->GetItemType( i ) != MenuItemType::SEPARATOR )
+ {
+ nId = pPopupMenu->GetItemId( i );
+ aCommand = pPopupMenu->GetItemCommand( nId );
+ if ( aCommand.startsWith( m_aEmptyDocURL ) )
+ {
+ pPopupMenu->SetAccelKey( 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 ( pPopupMenu->GetItemType( i ) != MenuItemType::SEPARATOR )
+ {
+ nId = pPopupMenu->GetItemId( i );
+ aCommand = pPopupMenu->GetItemCommand( nId );
+ if ( aCommand.indexOf( aDefaultModuleName ) >= 0 )
+ {
+ pPopupMenu->SetAccelKey( nId, rKeyCode );
+ break;
+ }
+ }
+ }
+}
+
+void NewMenuController::setAccelerators( PopupMenu* pPopupMenu )
+{
+ 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( pPopupMenu->GetItemCount() );
+ std::vector< vcl::KeyCode > aMenuShortCuts;
+ std::vector< OUString > aCmds;
+ std::vector< sal_uInt16 > aIds;
+ for ( sal_uInt16 i = 0; i < nItemCount; i++ )
+ {
+ if ( pPopupMenu->GetItemType( i ) != MenuItemType::SEPARATOR )
+ {
+ sal_uInt16 nId( pPopupMenu->GetItemId( i ));
+ aIds.push_back( nId );
+ aMenuShortCuts.push_back( aEmptyKeyCode );
+ aCmds.push_back( pPopupMenu->GetItemCommand( nId ));
+ }
+ }
+
+ sal_uInt32 nSeqCount( aIds.size() );
+
+ if ( m_bNewMenu )
+ nSeqCount+=1;
+
+ Sequence< OUString > aSeq( nSeqCount );
+
+ // Add a special command for our "New" menu.
+ if ( m_bNewMenu )
+ {
+ aSeq[nSeqCount-1] = m_aCommandURL;
+ aMenuShortCuts.push_back( aEmptyKeyCode );
+ }
+
+ const sal_uInt32 nCount = aCmds.size();
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ aSeq[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++ )
+ pPopupMenu->SetAccelKey( aIds[i], 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( pPopupMenu, 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::getUnoTunnelImplementation<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 = OUString::createFromAscii(m_bNewMenu ? aSlotNewDocDirect : aSlotAutoPilot);
+ m_xURLTransformer->parseStrict( aTargetURL );
+ Reference< XDispatch > xMenuItemDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
+ if(xMenuItemDispatch == nullptr)
+ return;
+
+ const css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > aDynamicMenuEntries =
+ SvtDynamicMenuOptions().GetMenu( m_bNewMenu ? EDynamicMenuType::NewMenu : EDynamicMenuType::WizardMenu );
+
+ OUString aTitle;
+ OUString aURL;
+ OUString aTargetFrame;
+ OUString aImageId;
+ sal_uInt16 nItemId = 1;
+
+ for ( const auto& aDynamicMenuEntry : aDynamicMenuEntries )
+ {
+ for ( const auto& aProperty : aDynamicMenuEntry )
+ {
+ if ( aProperty.Name == DYNAMICMENU_PROPERTYNAME_URL )
+ aProperty.Value >>= aURL;
+ else if ( aProperty.Name == DYNAMICMENU_PROPERTYNAME_TITLE )
+ aProperty.Value >>= aTitle;
+ else if ( aProperty.Name == DYNAMICMENU_PROPERTYNAME_IMAGEIDENTIFIER )
+ aProperty.Value >>= aImageId;
+ else if ( aProperty.Name == DYNAMICMENU_PROPERTYNAME_TARGETNAME )
+ aProperty.Value >>= aTargetFrame;
+ }
+
+ if ( aTitle.isEmpty() && aURL.isEmpty() )
+ continue;
+
+ if ( aURL == "private:separator" )
+ pVCLPopupMenu->InsertSeparator();
+ else
+ {
+ pVCLPopupMenu->InsertItem( nItemId, aTitle );
+ pVCLPopupMenu->SetItemCommand( nItemId, aURL );
+
+ void* nAttributePtr = MenuAttributes::CreateAttribute( aTargetFrame, aImageId );
+ pVCLPopupMenu->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(static_cast<OWeakObject *>(this), UNO_QUERY );
+
+ 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 >(static_cast<OWeakObject *>(this), UNO_QUERY ));
+ 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::getUnoTunnelImplementation<VCLXMenu>( xPopupMenu ));
+ if ( !pPopupMenu )
+ return;
+
+ OUString aURL;
+ OUString aTargetFrame( m_aTargetFrame );
+
+ {
+ SolarMutexGuard aSolarMutexGuard;
+ PopupMenu* pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu());
+ aURL = pVCLPopupMenu->GetItemCommand(rEvent.MenuId);
+ void* nAttributePtr = pVCLPopupMenu->GetUserValue(rEvent.MenuId);
+ MenuAttributes* pAttributes = static_cast<MenuAttributes *>(nAttributePtr);
+ if (pAttributes)
+ aTargetFrame = pAttributes->aTargetFrame;
+ }
+
+ Sequence< PropertyValue > aArgsList( 1 );
+ aArgsList[0].Name = "Referer";
+ aArgsList[0].Value <<= 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::getUnoTunnelImplementation<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( pVCLPopupMenu );
+}
+
+// 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 bInitalized( m_bInitialized );
+ if ( bInitalized )
+ 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;
+ }
+}
+
+}
+
+/* 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..80215acb8
--- /dev/null
+++ b/framework/source/uielement/objectmenucontroller.cxx
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <toolkit/awt/vclxmenu.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <vcl/menu.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();
+ VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getUnoTunnelImplementation<VCLXMenu>( rPopupMenu ));
+ PopupMenu* pVCLPopupMenu = nullptr;
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ resetPopupMenu( rPopupMenu );
+ if ( pPopupMenu )
+ pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu());
+
+ if ( !pVCLPopupMenu )
+ return;
+
+ const OUString aVerbCommand( ".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 );
+ // use VCL popup menu pointer to set vital information that are not part of the awt implementation
+
+ OUString aCommand = aVerbCommand + OUString::number( rVerb.VerbID );
+ pVCLPopupMenu->SetItemCommand( i+1, aCommand ); // Store verb command
+ }
+ }
+}
+
+// XEventListener
+void SAL_CALL ObjectMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(static_cast<OWeakObject *>(this), UNO_QUERY );
+
+ osl::MutexGuard aLock( m_aMutex );
+ m_xFrame.clear();
+ m_xDispatch.clear();
+
+ if ( m_xPopupMenu.is() )
+ m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(static_cast<OWeakObject *>(this), UNO_QUERY ));
+ 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..4095ae4b9
--- /dev/null
+++ b/framework/source/uielement/popuptoolbarcontroller.cxx
@@ -0,0 +1,837 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/miscopt.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 <unotools/moduleoptions.hxx>
+#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,
+ const OUString &rPopupCommand = OUString() );
+ virtual void functionExecuted( const OUString &rCommand );
+ virtual ToolBoxItemBits getDropDownStyle() const;
+ void createPopupMenuController();
+
+ bool m_bHasController;
+ 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,
+ const OUString &rPopupCommand )
+ : ToolBarBase( xContext, css::uno::Reference< css::frame::XFrame >(), /*aCommandURL*/OUString() )
+ , m_bHasController( false )
+ , m_aPopupCommand( rPopupCommand )
+{
+}
+
+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", "" );
+ }
+
+ SolarMutexGuard aSolarLock;
+ VclPtr< ToolBox > pToolBox = static_cast< ToolBox* >( VCLUnoHelper::GetWindow( getParent() ).get() );
+ if ( pToolBox )
+ {
+ ToolBoxItemBits nCurStyle( pToolBox->GetItemBits( m_nToolBoxId ) );
+ ToolBoxItemBits nSetStyle( getDropDownStyle() );
+ pToolBox->SetItemBits( m_nToolBoxId,
+ m_bHasController ?
+ nCurStyle | nSetStyle :
+ nCurStyle & ~nSetStyle );
+ }
+
+}
+
+void SAL_CALL PopupMenuToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ ToolBox* pToolBox = nullptr;
+ sal_uInt16 nItemId = 0;
+ 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() ).get() );
+ 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( 3 );
+ aArgs[0] <<= comphelper::makePropertyValue( "Frame", m_xFrame );
+ aArgs[1] <<= comphelper::makePropertyValue( "ModuleIdentifier", getModuleName() );
+ aArgs[2] <<= comphelper::makePropertyValue( "InToolbar", true );
+
+ try
+ {
+ m_xPopupMenu.set(
+ m_xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.awt.PopupMenu", m_xContext ),
+ css::uno::UNO_QUERY_THROW );
+ 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() )
+ {
+ Menu* pVclMenu = comphelper::getUnoTunnelImplementation<VCLXMenu>( m_xPopupMenu )->GetMenu();
+
+ ToolBox* pToolBox = nullptr;
+ sal_uInt16 nId = 0;
+ if ( getToolboxId( nId, &pToolBox ) && pToolBox->IsItemEnabled( nId ) )
+ {
+ pVclMenu->Activate();
+ pVclMenu->Deactivate();
+ }
+
+ for ( sal_uInt16 i = 0; i < pVclMenu->GetItemCount(); ++i )
+ {
+ sal_uInt16 nItemId = pVclMenu->GetItemId( i );
+ if ( nItemId && pVclMenu->IsItemEnabled( nItemId ) && !pVclMenu->GetPopupMenu( nItemId ) )
+ {
+ functionExecuted( pVclMenu->GetItemCommand( 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;
+ sal_uInt16 nId = 0;
+ 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
+ // Ugly HACK to cause 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;
+ sal_uInt16 nId = 0;
+ 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;
+ sal_uInt16 nId = 0;
+ 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;
+ sal_uInt16 nId = 0;
+ 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 PopupMenuToolbarController
+{
+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;
+
+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;
+ void setItemImage( const OUString &rCommand );
+
+ OUString m_aLastURL;
+};
+
+NewToolbarController::NewToolbarController(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext )
+ : PopupMenuToolbarController( xContext )
+{
+}
+
+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
+ // this will set the image of the default module
+ setItemImage( 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 );
+ if ( !m_aLastURL.getLength() )
+ return;
+
+ OUString aTarget( "_default" );
+ if ( m_xPopupMenu.is() )
+ {
+ // TODO investigate how to wrap Get/SetUserValue in css::awt::XMenu
+ MenuAttributes* pMenuAttributes( nullptr );
+ VCLXPopupMenu* pTkPopupMenu =
+ static_cast<VCLXPopupMenu *>( comphelper::getUnoTunnelImplementation<VCLXMenu>( m_xPopupMenu ) );
+
+ SolarMutexGuard aSolarMutexGuard;
+ PopupMenu* pVCLPopupMenu = pTkPopupMenu ?
+ dynamic_cast< PopupMenu * >( pTkPopupMenu->GetMenu() ) : nullptr;
+
+ if ( pVCLPopupMenu )
+ pMenuAttributes = static_cast< MenuAttributes* >(
+ pVCLPopupMenu->GetUserValue( pVCLPopupMenu->GetCurItemId() ) );
+
+ if ( pMenuAttributes )
+ aTarget = pMenuAttributes->aTargetFrame;
+ }
+
+ css::uno::Sequence< css::beans::PropertyValue > aArgs( 1 );
+ aArgs[0].Name = "Referer";
+ aArgs[0].Value <<= OUString( "private:user" );
+
+ dispatchCommand( m_aLastURL, aArgs, aTarget );
+}
+
+void NewToolbarController::functionExecuted( const OUString &rCommand )
+{
+ setItemImage( rCommand );
+}
+
+/**
+ it return the existing state of the given URL in the popupmenu of this toolbox control.
+
+ If the given URL can be located as an action command of one menu item of the
+ popup menu of this control, we return sal_True. Otherwise we return sal_False.
+ Further we return a fallback URL, in case we have to return sal_False. Because
+ the outside code must select a valid item of the popup menu every time ...
+ and we define it here. By the way this method was written to handle
+ error situations gracefully. E.g. it can be called during creation time
+ but then we have no valid menu. For this case we know another fallback URL.
+ Then we return the private:factory/ URL of the default factory.
+
+ @param rPopupMenu
+ points to the popup menu, on which item we try to locate the given URL
+ Can be NULL! Search will be suppressed then.
+
+ @param sURL
+ the URL for searching
+
+ @param sFallback
+ contains the fallback URL in case we return FALSE
+ Must point to valid memory!
+
+ @param aImage
+ contains the image of the menu for the URL.
+
+ @return sal_True - if URL could be located as an item of the popup menu.
+ sal_False - otherwise.
+*/
+bool Impl_ExistURLInMenu(
+ const css::uno::Reference< css::awt::XPopupMenu > &rPopupMenu,
+ OUString &sURL,
+ OUString &sFallback,
+ Image &aImage )
+{
+ bool bValidFallback( false );
+ sal_uInt16 nCount( 0 );
+ if ( rPopupMenu.is() )
+ {
+ nCount = rPopupMenu->getItemCount();
+ if (nCount != 0 && sURL.getLength() )
+ {
+ for ( sal_uInt16 n = 0; n < nCount; ++n )
+ {
+ sal_uInt16 nId = rPopupMenu->getItemId( n );
+ OUString aCmd( rPopupMenu->getCommand( nId ) );
+
+ if ( !bValidFallback && aCmd.getLength() )
+ {
+ sFallback = aCmd;
+ bValidFallback = true;
+ }
+
+ // match even if the menu command is more detailed
+ // (maybe an additional query) #i28667#
+ if ( aCmd.match( sURL ) )
+ {
+ sURL = aCmd;
+ const css::uno::Reference< css::graphic::XGraphic > xGraphic(
+ rPopupMenu->getItemImage( nId ) );
+ if ( xGraphic.is() )
+ aImage = Image( xGraphic );
+ return true;
+ }
+ }
+ }
+ }
+
+ if ( !bValidFallback )
+ {
+ OUStringBuffer aBuffer;
+ aBuffer.append( "private:factory/" );
+ aBuffer.append( SvtModuleOptions().GetDefaultModuleName() );
+ sFallback = aBuffer.makeStringAndClear();
+ }
+
+ return false;
+}
+
+/** We accept URL's here only, which exist as items of our internal popup menu.
+ All other ones will be ignored and a fallback is used.
+ */
+void NewToolbarController::setItemImage( const OUString &rCommand )
+{
+ SolarMutexGuard aSolarLock;
+ VclPtr< ToolBox> pToolBox = static_cast< ToolBox* >( VCLUnoHelper::GetWindow( getParent() ).get() );
+ if ( !pToolBox )
+ return;
+
+ OUString aURL = rCommand;
+ OUString sFallback;
+ Image aMenuImage;
+
+ bool bValid( Impl_ExistURLInMenu( m_xPopupMenu, aURL, sFallback, aMenuImage ) );
+ if ( !bValid )
+ aURL = sFallback;
+
+ bool bBig = SvtMiscOptions().AreCurrentSymbolsLarge();
+
+ INetURLObject aURLObj( aURL );
+ Size aPreferredSize(bBig ? pToolBox->GetDefaultImageSize() : Size());
+ Image aImage = SvFileInformationManager::GetImageNoDefault(aURLObj, pToolBox->GetImageSize());
+ if (!aImage)
+ {
+ aImage = !!aMenuImage ? aMenuImage : SvFileInformationManager::GetImage(aURLObj, bBig, aPreferredSize);
+ }
+ // if everything failed, just use the image associated with the toolbar item command
+ if ( !aImage )
+ return;
+
+ pToolBox->SetItemImage( m_nToolBoxId, aImage );
+
+ m_aLastURL = aURL;
+}
+
+}
+
+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..537129b96
--- /dev/null
+++ b/framework/source/uielement/progressbarwrapper.cxx
@@ -0,0 +1,315 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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::max( double( 0 ), std::min( fVal, double( 100 )));
+ }
+
+ 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(
+ static_cast< cppu::OWeakObject* >(this),
+ uno::UNO_QUERY );
+
+ {
+ 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() )
+ {
+ StatusIndicatorInterfaceWrapper* pWrapper =
+ new StatusIndicatorInterfaceWrapper(
+ uno::Reference< lang::XComponent >(
+ static_cast< cppu::OWeakObject* >( this ),
+ uno::UNO_QUERY ));
+ xComp.set(static_cast< cppu::OWeakObject* >( pWrapper ),
+ 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..9b75f3fdb
--- /dev/null
+++ b/framework/source/uielement/recentfilesmenucontroller.cxx
@@ -0,0 +1,388 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <cppuhelper/supportsservice.hxx>
+#include <osl/mutex.hxx>
+#include <svtools/popupmenucontrollerbase.hxx>
+#include <tools/urlobj.hxx>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <unotools/historyoptions.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/svapp.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 {
+
+static const char CMD_CLEAR_LIST[] = ".uno:ClearRecentFileList";
+static const char CMD_OPEN_AS_TEMPLATE[] = ".uno:OpenTemplate";
+static const char CMD_OPEN_REMOTE[] = ".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< OUString > 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;
+ }
+ }
+}
+
+// private function
+void RecentFilesMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu )
+{
+ VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getUnoTunnelImplementation<VCLXMenu>( rPopupMenu ));
+ PopupMenu* pVCLPopupMenu = nullptr;
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ resetPopupMenu( rPopupMenu );
+ if ( pPopupMenu )
+ pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu());
+
+ if ( !pVCLPopupMenu )
+ return;
+
+ Sequence< Sequence< PropertyValue > > aHistoryList = SvtHistoryOptions().GetList( ePICKLIST );
+
+ int nPickListMenuItems = std::min<sal_Int32>( aHistoryList.getLength(), MAX_MENU_ITEMS );
+ m_aRecentFilesItems.clear();
+ if (( nPickListMenuItems > 0 ) && !m_bDisabled )
+ {
+ for ( int i = 0; i < nPickListMenuItems; i++ )
+ {
+ const Sequence< PropertyValue >& rPickListEntry = aHistoryList[i];
+ OUString aURL;
+
+ for ( PropertyValue const & prop : rPickListEntry )
+ {
+ if ( prop.Name == HISTORY_PROPERTYNAME_URL )
+ {
+ prop.Value >>= aURL;
+ break;
+ }
+ }
+
+ m_aRecentFilesItems.push_back( aURL );
+ }
+ }
+
+ if ( !m_aRecentFilesItems.empty() )
+ {
+ const sal_uInt32 nCount = m_aRecentFilesItems.size();
+ 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 aURL( m_aRecentFilesItems[i] );
+ 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 );
+
+ pVCLPopupMenu->InsertItem( sal_uInt16( i+1 ), aMenuShortCut.makeStringAndClear() );
+ pVCLPopupMenu->SetTipHelpText( sal_uInt16( i+1 ), aTipHelpText );
+ pVCLPopupMenu->SetItemCommand( sal_uInt16( i+1 ), aURLString );
+ }
+
+ pVCLPopupMenu->InsertSeparator();
+ // Clear List menu entry
+ pVCLPopupMenu->InsertItem( sal_uInt16( nCount + 1 ),
+ FwkResId(STR_CLEAR_RECENT_FILES) );
+ pVCLPopupMenu->SetItemCommand( sal_uInt16( nCount + 1 ),
+ CMD_CLEAR_LIST );
+ pVCLPopupMenu->SetHelpText( sal_uInt16( nCount + 1 ),
+ FwkResId(STR_CLEAR_RECENT_FILES_HELP) );
+
+ // Open remote menu entry
+ if ( m_bShowToolbarEntries )
+ {
+ pVCLPopupMenu->InsertSeparator();
+ pVCLPopupMenu->InsertItem( CMD_OPEN_AS_TEMPLATE, m_xFrame );
+ pVCLPopupMenu->InsertItem( CMD_OPEN_REMOTE, m_xFrame );
+ }
+ }
+ else
+ {
+ if ( m_bShowToolbarEntries )
+ {
+ pVCLPopupMenu->InsertItem( CMD_OPEN_AS_TEMPLATE, m_xFrame );
+ pVCLPopupMenu->InsertItem( CMD_OPEN_REMOTE, m_xFrame );
+ }
+ else
+ {
+ // No recent documents => insert "no document" string
+ pVCLPopupMenu->InsertItem( 1, FwkResId(STR_NODOCUMENT) );
+ // Do not disable it, otherwise the Toolbar controller and MenuButton
+ // will display SV_RESID_STRING_NOSELECTIONPOSSIBLE instead of STR_NODOCUMENT
+ pVCLPopupMenu->SetItemBits( 1, pVCLPopupMenu->GetItemBits( 1 ) | MenuItemBits::NOSELECT );
+ }
+ }
+}
+
+void RecentFilesMenuController::executeEntry( sal_Int32 nIndex )
+{
+ if (!(( nIndex >= 0 ) &&
+ ( nIndex < sal::static_int_cast<sal_Int32>( m_aRecentFilesItems.size() ))))
+ return;
+
+ Sequence< PropertyValue > aArgsList(3);
+ aArgsList[0].Name = "Referer";
+ aArgsList[0].Value <<= OUString( "private:user" );
+
+ // documents in the picklist will never be opened as templates
+ aArgsList[1].Name = "AsTemplate";
+ aArgsList[1].Value <<= false;
+
+ // Type detection needs to know which app we are opening it from.
+ aArgsList[2].Name = "DocumentService";
+ aArgsList[2].Value <<= m_aModuleName;
+
+ dispatchCommand( m_aRecentFilesItems[ nIndex ], aArgsList, "_default" );
+}
+
+// XEventListener
+void SAL_CALL RecentFilesMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(static_cast<OWeakObject *>(this), UNO_QUERY );
+
+ osl::MutexGuard aLock( m_aMutex );
+ m_xFrame.clear();
+ m_xDispatch.clear();
+
+ if ( m_xPopupMenu.is() )
+ m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(static_cast<OWeakObject *>(this), UNO_QUERY ));
+ 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( ePICKLIST );
+ 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 >( static_cast< OWeakObject* >( this ), UNO_QUERY );
+ 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;
+
+ const OUString aEntryArgStr( "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 );
+ OUString aEntryArg;
+
+ if ( nAddArgs < 0 )
+ aEntryArg = aURL.Complete.copy( nEntryPos );
+ else
+ aEntryArg = aURL.Complete.copy( nEntryPos, nAddArgs-nEntryPos );
+
+ sal_Int32 nEntry = aEntryArg.toInt32();
+ 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..0594bfea8
--- /dev/null
+++ b/framework/source/uielement/resourcemenucontroller.cxx
@@ -0,0 +1,413 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/menubarmanager.hxx>
+
+#include <cppuhelper/implbase.hxx>
+#include <svtools/popupmenucontrollerbase.hxx>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+
+#include <com/sun/star/embed/VerbAttributes.hpp>
+#include <com/sun/star/embed/VerbDescriptor.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::container::XIndexAccess > m_xMenuContainer;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ 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;
+};
+
+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 == "Frame" )
+ aPropValue.Value >>= m_xFrame;
+ else if ( aPropValue.Name == "ModuleIdentifier" )
+ aPropValue.Value >>= m_aModuleName;
+ 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.
+ css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider( m_xFrame, css::uno::UNO_QUERY );
+ framework::MenuBarManager::FillMenu( m_nNewMenuId, comphelper::getUnoTunnelImplementation<VCLXMenu>( m_xPopupMenu )->GetMenu(), m_aModuleName, m_xMenuContainer, xDispatchProvider );
+
+ // For context menus, add object verbs.
+ if ( m_bContextMenu )
+ {
+ css::util::URL aObjectMenuURL;
+ aObjectMenuURL.Complete = ".uno:ObjectMenue";
+ m_xURLTransformer->parseStrict( aObjectMenuURL );
+ 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::getUnoTunnelImplementation<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::getUnoTunnelImplementation<VCLXMenu>( m_xPopupMenu );
+ css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider( m_xFrame, css::uno::UNO_QUERY );
+ m_xMenuBarManager.set( new framework::MenuBarManager(
+ m_xContext, m_xFrame, m_xURLTransformer, xDispatchProvider, m_aModuleName, pAwtMenu->GetMenu(), false, !m_bContextMenu && !m_bInToolbar ) );
+ m_xFrame->addFrameActionListener( m_xMenuBarManager.get() );
+ }
+}
+
+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() )
+ {
+ 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();
+ if ( m_xMenuBarManager.is() )
+ {
+ 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 SaveAsMenuController::impl_setPopupMenu()
+{
+ VCLXMenu* pPopupMenu = comphelper::getUnoTunnelImplementation<VCLXMenu>( m_xPopupMenu );
+ Menu* pVCLPopupMenu = nullptr;
+
+ SolarMutexGuard aGuard;
+
+ if ( pPopupMenu )
+ pVCLPopupMenu = pPopupMenu->GetMenu();
+
+ if ( !pVCLPopupMenu )
+ return;
+
+ pVCLPopupMenu->InsertItem( ".uno:SaveAs", nullptr );
+ pVCLPopupMenu->InsertItem( ".uno:ExportTo", nullptr );
+ pVCLPopupMenu->InsertItem( ".uno:SaveAsTemplate", nullptr );
+ pVCLPopupMenu->InsertSeparator();
+ pVCLPopupMenu->InsertItem( ".uno:SaveAsRemote", nullptr );
+}
+
+OUString SaveAsMenuController::getImplementationName()
+{
+ return "com.sun.star.comp.framework.SaveAsMenuController";
+}
+
+}
+
+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_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..db3f9d677
--- /dev/null
+++ b/framework/source/uielement/spinfieldtoolbarcontroller.cxx
@@ -0,0 +1,494 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <svtools/toolboxcontroller.hxx>
+#include <vcl/event.hxx>
+#include <vcl/spinfld.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 : public SpinField
+{
+ public:
+ SpinfieldControl( vcl::Window* pParent, WinBits nStyle, SpinfieldToolbarController* pSpinfieldToolbarController );
+ virtual ~SpinfieldControl() override;
+ virtual void dispose() override;
+
+ virtual void Up() override;
+ virtual void Down() override;
+ virtual void First() override;
+ virtual void Last() override;
+ virtual void Modify() override;
+ virtual void GetFocus() override;
+ virtual void LoseFocus() override;
+ virtual bool PreNotify( NotifyEvent& rNEvt ) override;
+
+ private:
+ SpinfieldToolbarController* m_pSpinfieldToolbarController;
+};
+
+SpinfieldControl::SpinfieldControl( vcl::Window* pParent, WinBits nStyle, SpinfieldToolbarController* pSpinfieldToolbarController ) :
+ SpinField( pParent, nStyle )
+ , m_pSpinfieldToolbarController( pSpinfieldToolbarController )
+{
+}
+
+SpinfieldControl::~SpinfieldControl()
+{
+ disposeOnce();
+}
+
+void SpinfieldControl::dispose()
+{
+ m_pSpinfieldToolbarController = nullptr;
+ SpinField::dispose();
+}
+
+void SpinfieldControl::Up()
+{
+ SpinField::Up();
+ if ( m_pSpinfieldToolbarController )
+ m_pSpinfieldToolbarController->Up();
+}
+
+void SpinfieldControl::Down()
+{
+ SpinField::Down();
+ if ( m_pSpinfieldToolbarController )
+ m_pSpinfieldToolbarController->Down();
+}
+
+void SpinfieldControl::First()
+{
+ SpinField::First();
+ if ( m_pSpinfieldToolbarController )
+ m_pSpinfieldToolbarController->First();
+}
+
+void SpinfieldControl::Last()
+{
+ SpinField::First();
+ if ( m_pSpinfieldToolbarController )
+ m_pSpinfieldToolbarController->Last();
+}
+
+void SpinfieldControl::Modify()
+{
+ SpinField::Modify();
+ if ( m_pSpinfieldToolbarController )
+ m_pSpinfieldToolbarController->Modify();
+}
+
+void SpinfieldControl::GetFocus()
+{
+ SpinField::GetFocus();
+ if ( m_pSpinfieldToolbarController )
+ m_pSpinfieldToolbarController->GetFocus();
+}
+
+void SpinfieldControl::LoseFocus()
+{
+ SpinField::LoseFocus();
+ if ( m_pSpinfieldToolbarController )
+ m_pSpinfieldToolbarController->LoseFocus();
+}
+
+bool SpinfieldControl::PreNotify( NotifyEvent& rNEvt )
+{
+ bool bRet = false;
+ if ( m_pSpinfieldToolbarController )
+ bRet = m_pSpinfieldToolbarController->PreNotify( rNEvt );
+ if ( !bRet )
+ bRet = SpinField::PreNotify( rNEvt );
+
+ return bRet;
+}
+
+SpinfieldToolbarController::SpinfieldToolbarController(
+ const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rFrame,
+ ToolBox* pToolbar,
+ sal_uInt16 nID,
+ sal_Int32 nWidth,
+ const OUString& aCommand ) :
+ ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand )
+ , m_bFloat( false )
+ , m_bMaxSet( false )
+ , m_bMinSet( 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, WB_SPIN|WB_BORDER, this );
+ if ( nWidth == 0 )
+ nWidth = 100;
+
+ // Calculate height of the spin field according to the application font height
+ sal_Int32 nHeight = getFontSizePixel( m_pSpinfieldControl ) + 5 + 1;
+
+ 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
+{
+ Sequence<PropertyValue> aArgs( 2 );
+ OUString aSpinfieldText = m_pSpinfieldControl->GetText();
+
+ // Add key modifier to argument list
+ aArgs[0].Name = "KeyModifier";
+ aArgs[0].Value <<= KeyModifier;
+ aArgs[1].Name = "Value";
+ if ( m_bFloat )
+ aArgs[1].Value <<= aSpinfieldText.toDouble();
+ else
+ aArgs[1].Value <<= aSpinfieldText.toInt32();
+ return aArgs;
+}
+
+void SpinfieldToolbarController::Up()
+{
+ double nValue = m_nValue + m_nStep;
+ if ( m_bMaxSet && nValue > m_nMax )
+ return;
+
+ m_nValue = nValue;
+
+ OUString aText = impl_formatOutputString( m_nValue );
+ m_pSpinfieldControl->SetText( aText );
+ execute( 0 );
+}
+
+void SpinfieldToolbarController::Down()
+{
+ double nValue = m_nValue - m_nStep;
+ if ( m_bMinSet && nValue < m_nMin )
+ return;
+
+ m_nValue = nValue;
+
+ OUString aText = impl_formatOutputString( m_nValue );
+ m_pSpinfieldControl->SetText( aText );
+ execute( 0 );
+}
+
+void SpinfieldToolbarController::First()
+{
+ if ( m_bMinSet )
+ {
+ m_nValue = m_nMin;
+
+ OUString aText = impl_formatOutputString( m_nValue );
+ m_pSpinfieldControl->SetText( aText );
+ execute( 0 );
+ }
+}
+
+void SpinfieldToolbarController::Last()
+{
+ if ( m_bMaxSet )
+ {
+ m_nValue = m_nMax;
+
+ OUString aText = impl_formatOutputString( m_nValue );
+ m_pSpinfieldControl->SetText( aText );
+ execute( 0 );
+ }
+}
+
+void SpinfieldToolbarController::Modify()
+{
+ notifyTextChanged( m_pSpinfieldControl->GetText() );
+}
+
+void SpinfieldToolbarController::GetFocus()
+{
+ notifyFocusGet();
+}
+
+void SpinfieldToolbarController::LoseFocus()
+{
+ notifyFocusLost();
+}
+
+bool SpinfieldToolbarController::PreNotify( NotifyEvent const & rNEvt )
+{
+ if( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
+ {
+ const ::KeyEvent* pKeyEvent = rNEvt.GetKeyEvent();
+ const vcl::KeyCode& rKeyCode = pKeyEvent->GetKeyCode();
+ if(( rKeyCode.GetModifier() | rKeyCode.GetCode()) == KEY_RETURN )
+ {
+ // Call execute only with non-empty text
+ if ( !m_pSpinfieldControl->GetText().isEmpty() )
+ execute( rKeyCode.GetModifier() );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+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;
+ }
+ }
+ }
+
+ // Check values and set members
+ if ( !aValue.isEmpty() )
+ {
+ m_bFloat = bFloatValue;
+ m_nValue = aValue.toDouble();
+
+ OUString aOutString = impl_formatOutputString( m_nValue );
+ m_pSpinfieldControl->SetText( aOutString );
+ notifyTextChanged( aOutString );
+ }
+ if ( !aMax.isEmpty() )
+ {
+ m_nMax = aMax.toDouble();
+ m_bMaxSet = true;
+ }
+ if ( !aMin.isEmpty() )
+ {
+ m_nMin = aMin.toDouble();
+ m_bMinSet = true;
+ }
+ if ( !aStep.isEmpty() )
+ m_nStep = aStep.toDouble();
+}
+
+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::impl_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 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<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..a453f0079
--- /dev/null
+++ b/framework/source/uielement/statusbaritem.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/statusbaritem.hxx>
+#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,
+ const OUString& aCommand )
+ : StatusbarItem_Base( m_aMutex )
+ , m_pStatusBar( pStatusBar )
+ , m_nId( nId )
+ , m_nStyle( 0 )
+ , m_aCommand( aCommand )
+{
+ if ( m_pStatusBar )
+ m_nStyle = impl_convertItemBitsToItemStyle(
+ m_pStatusBar->GetItemBits( m_nId ) );
+}
+
+StatusbarItem::~StatusbarItem()
+{
+}
+
+void SAL_CALL StatusbarItem::disposing()
+{
+ osl::MutexGuard aGuard( m_aMutex );
+ m_pStatusBar = nullptr;
+}
+
+OUString SAL_CALL StatusbarItem::getCommand()
+{
+ osl::MutexGuard aGuard( m_aMutex );
+ return m_aCommand;
+}
+
+::sal_uInt16 SAL_CALL StatusbarItem::getItemId()
+{
+ osl::MutexGuard 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()
+{
+ osl::MutexGuard 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..f97becd5d
--- /dev/null
+++ b/framework/source/uielement/statusbarmanager.cxx
@@ -0,0 +1,660 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <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(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< frame::XFrame >& rFrame,
+ StatusBar* pStatusBar ) :
+ m_bDisposed( false ),
+ m_bFrameActionRegistered( false ),
+ m_bUpdateControllers( false ),
+ m_pStatusBar( pStatusBar ),
+ m_xFrame( rFrame ),
+ m_aListenerContainer( m_mutex ),
+ m_xContext( rxContext )
+{
+
+ 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(
+ static_cast< OWeakObject* >(this), uno::UNO_QUERY );
+
+ lang::EventObject aEvent( xThis );
+ m_aListenerContainer.disposeAndClear( 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 >(
+ static_cast< ::cppu::OWeakObject *>( this ),
+ uno::UNO_QUERY ));
+ }
+ 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();
+
+ m_aListenerContainer.addInterface( cppu::UnoType<lang::XEventListener>::get(), xListener );
+}
+
+void SAL_CALL StatusBarManager::removeEventListener( const uno::Reference< lang::XEventListener >& xListener )
+{
+ m_aListenerContainer.removeInterface( cppu::UnoType<lang::XEventListener>::get(), 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(
+ static_cast< cppu::OWeakObject *>( new StatusbarItem( m_pStatusBar, nId, aCommandURL ) ),
+ uno::UNO_QUERY );
+
+ beans::PropertyValue aPropValue;
+ std::vector< uno::Any > aPropVector;
+
+ aPropValue.Name = "CommandURL";
+ aPropValue.Value <<= aCommandURL;
+ aPropVector.push_back( uno::makeAny( aPropValue ) );
+
+ aPropValue.Name = "ModuleIdentifier";
+ aPropValue.Value <<= OUString();
+ aPropVector.push_back( uno::makeAny( aPropValue ) );
+
+ aPropValue.Name = "Frame";
+ aPropValue.Value <<= m_xFrame;
+ aPropVector.push_back( uno::makeAny( aPropValue ) );
+
+ // TODO remove this
+ aPropValue.Name = "ServiceManager";
+ aPropValue.Value <<= uno::Reference<lang::XMultiServiceFactory>(m_xContext->getServiceManager(), uno::UNO_QUERY_THROW);
+ aPropVector.push_back( uno::makeAny( aPropValue ) );
+
+ aPropValue.Name = "ParentWindow";
+ aPropValue.Value <<= xStatusbarWindow;
+ aPropVector.push_back( uno::makeAny( aPropValue ) );
+
+ // TODO still needing with the css::ui::XStatusbarItem?
+ aPropValue.Name = "Identifier";
+ aPropValue.Value <<= nId;
+ aPropVector.push_back( uno::makeAny( aPropValue ) );
+
+ aPropValue.Name = "StatusbarItem";
+ aPropValue.Value <<= xStatusbarItem;
+ aPropVector.push_back( uno::makeAny( 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
+ svt::StatusbarController* pController = CreateStatusBarController( m_xFrame, m_pStatusBar, nId, aCommandURL );
+ if ( !pController )
+ {
+ // 3) Is Add-on? Generic statusbar controller
+ if ( pItemData )
+ {
+ pController = new GenericStatusbarController( m_xContext,
+ m_xFrame,
+ xStatusbarItem,
+ pItemData );
+ }
+ else
+ {
+ // 4) Default Statusbar controller
+ pController = new svt::StatusbarController( m_xContext, m_xFrame, aCommandURL, nId );
+ }
+ }
+
+ xController = pController;
+ }
+
+ 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 >(
+ static_cast< ::cppu::OWeakObject *>( this ), uno::UNO_QUERY ));
+ }
+}
+
+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
+ const 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..900acecb8
--- /dev/null
+++ b/framework/source/uielement/statusbarmerger.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/statusbarmerger.hxx>
+
+using com::sun::star::beans::PropertyValue;
+using com::sun::star::uno::Sequence;
+
+namespace framework
+{
+namespace {
+
+static const char MERGECOMMAND_ADDAFTER[] = "AddAfter";
+static const char MERGECOMMAND_ADDBEFORE[] = "AddBefore";
+static const char MERGECOMMAND_REPLACE[] = "Replace";
+static const char MERGECOMMAND_REMOVE[] = "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,
+ const OUString& rMergeCommandParameter )
+{
+ sal_Int32 nCount = rMergeCommandParameter.toInt32();
+ if ( nCount > 0 )
+ {
+ for ( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ if ( nPos < pStatusbar->GetItemCount() )
+ pStatusbar->RemoveItem( nPos );
+ }
+ }
+ return true;
+}
+
+}
+
+bool StatusbarMerger::IsCorrectContext(
+ const OUString& rContext )
+{
+ return rContext.isEmpty();
+}
+
+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,
+ const OUString& 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,
+ const OUString& rMergeCommand,
+ const OUString& 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,
+ const OUString& rMergeCommand,
+ const OUString& rMergeFallback,
+ const AddonStatusbarItemContainer& rItems )
+{
+ // fallback IGNORE or REPLACE/REMOVE item not found
+ if (( rMergeFallback == "Ignore" ) ||
+ ( rMergeCommand == MERGECOMMAND_REPLACE ) ||
+ ( rMergeCommand == MERGECOMMAND_REMOVE ) )
+ {
+ return true;
+ }
+ else if (( rMergeCommand == MERGECOMMAND_ADDBEFORE ) ||
+ ( rMergeCommand == MERGECOMMAND_ADDAFTER ) )
+ {
+ if ( rMergeFallback == "AddFirst" )
+ return lcl_MergeItems( pStatusbar, 0, 0, rItemId, rItems );
+ else if ( rMergeFallback == "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..f22736f79
--- /dev/null
+++ b/framework/source/uielement/statusbarwrapper.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/statusbarwrapper.hxx>
+
+#include <uielement/statusbar.hxx>
+
+#include <com/sun/star/ui/UIElementType.hpp>
+
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <tools/solar.h>
+#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(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext
+ )
+ : UIConfigElementWrapperBase( UIElementType::STATUSBAR ),
+ m_xContext( rxContext )
+{
+}
+
+StatusBarWrapper::~StatusBarWrapper()
+{
+}
+
+void SAL_CALL StatusBarWrapper::dispose()
+{
+ Reference< XComponent > xThis( static_cast< OWeakObject* >(this), UNO_QUERY );
+
+ 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 );
+ StatusBarManager* pStatusBarManager( nullptr );
+ {
+ 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 );
+ m_xStatusBarManager.set( static_cast< OWeakObject *>( pStatusBarManager ), UNO_QUERY );
+ }
+ }
+
+ 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..dfc8fa5bf
--- /dev/null
+++ b/framework/source/uielement/styletoolbarcontroller.cxx
@@ -0,0 +1,241 @@
+/* -*- 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 <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <sal/log.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( const OUString& rFamily )
+{
+ if ( rFamily == "ParagraphStyles" ||
+ rFamily == "CellStyles" || // In sc
+ rFamily == "graphics" ) // In sd
+ return ".uno:ParaStyle";
+ else if ( rFamily == "CharacterStyles" )
+ return ".uno:CharStyle";
+ else if ( rFamily == "PageStyles" )
+ return ".uno:PageStyle";
+ else if ( rFamily == "FrameStyles" )
+ return ".uno:FrameStyle";
+ else if ( rFamily == "NumberingStyles" )
+ return ".uno:ListStyle";
+ else if ( rFamily == "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,
+ const css::uno::Reference< css::util::XURLTransformer >& rUrlTransformer,
+ const css::util::URL& rURL )
+ : m_aCommand( rURL.Complete )
+ , m_xUrlTransformer( rUrlTransformer )
+ , 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
+ {
+ OUString aParam = aParams.getToken( 0, '&', nIndex );
+
+ sal_Int32 nParamIndex = 0;
+ OUString aParamName = aParam.getToken( 0, '=', nParamIndex );
+ if ( nParamIndex < 0 )
+ break;
+
+ if ( aParamName == "Style:string" )
+ {
+ OUString aValue = aParam.getToken( 0, '=', nParamIndex );
+ aStyleName = INetURLObject::decode( aValue, INetURLObject::DecodeMechanism::WithCharset );
+ }
+ else if ( aParamName == "FamilyName:string" )
+ {
+ aFamilyName = aParam.getToken( 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;
+ sal_uInt16 nItemId = 0;
+ 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..3bbb2147f
--- /dev/null
+++ b/framework/source/uielement/subtoolbarcontroller.cxx
@@ -0,0 +1,447 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/toolboxcontroller.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/gen.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/commandinfoprovider.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::ToolboxController,
+ css::frame::XSubToolbarController,
+ css::awt::XDockableWindowListener,
+ css::lang::XServiceInfo > 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 css::uno::Sequence< css::uno::Any >& rxArgs );
+ virtual ~SubToolBarController() override;
+
+ // 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;
+ virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createPopupWindow() 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 css::uno::Sequence< css::uno::Any >& rxArgs )
+{
+ 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_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;
+ sal_uInt16 nId = 0;
+ 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 == ( 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::makeAny( nKeyModifier ) }
+ } ) );
+ dispatchCommand( m_aLastCommand, aArgs );
+ }
+}
+
+css::uno::Reference< css::awt::XWindow > SubToolBarController::createPopupWindow()
+{
+ SolarMutexGuard aGuard;
+
+ ToolBox* pToolBox = nullptr;
+ sal_uInt16 nId = 0;
+ 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::makeAny( xFrame ) },
+ { "ParentWindow", css::uno::makeAny( m_xParentWindow ) },
+ { "Persistent", css::uno::makeAny( false ) },
+ { "PopupMode", css::uno::makeAny( 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 >(
+ static_cast< OWeakObject * >( this ), css::uno::UNO_QUERY ) );
+ 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 css::uno::Reference< css::awt::XWindow >();
+}
+
+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;
+ sal_uInt16 nId = 0;
+ 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;
+
+ OUString aPersistentString( "Persistent" );
+ try
+ {
+ VclPtr<vcl::Window> pTbxWindow = VCLUnoHelper::GetWindow( xSubToolBar );
+ if ( pTbxWindow && pTbxWindow->GetType() == WindowType::TOOLBOX )
+ {
+ css::uno::Any a = xProp->getPropertyValue( aPersistentString );
+ xProp->setPropertyValue( aPersistentString, css::uno::makeAny( 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::ToolboxController::initialize( rxArgs );
+
+ ToolBox* pToolBox = nullptr;
+ sal_uInt16 nId = 0;
+ 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 );
+ }
+ updateImage();
+}
+
+void SubToolBarController::dispose()
+{
+ if ( m_bDisposed )
+ return;
+
+ svt::ToolboxController::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*,
+ css::uno::Sequence<css::uno::Any> const & rxArgs )
+{
+ return cppu::acquire( new SubToolBarController( 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..492da8593
--- /dev/null
+++ b/framework/source/uielement/thesaurusmenucontroller.cxx
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <i18nlangtag/languagetag.hxx>
+#include <svl/lngmisc.hxx>
+#include <svtools/popupmenucontrollerbase.hxx>
+#include <unotools/lingucfg.hxx>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/image.hxx>
+#include <vcl/menu.hxx>
+#include <sal/log.hxx>
+
+#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*/ );
+
+ VCLXMenu* pAwtMenu = comphelper::getUnoTunnelImplementation<VCLXMenu>( m_xPopupMenu );
+ Menu* pVCLMenu = pAwtMenu->GetMenu();
+ pVCLMenu->SetMenuFlags( MenuFlags::NoAutoMnemonics );
+ if ( aSynonyms.empty() )
+ return;
+
+ SvtLinguConfig aCfg;
+ Image aImage;
+ OUString aThesImplName( getThesImplName( aLocale ) );
+ OUString aSynonymsImageUrl( aCfg.GetSynonymsContextImage( aThesImplName ) );
+ if ( !aThesImplName.isEmpty() && !aSynonymsImageUrl.isEmpty() )
+ aImage = Image( aSynonymsImageUrl );
+
+ sal_uInt16 nId = 1;
+ for ( const auto& aSynonym : aSynonyms )
+ {
+ OUString aItemText( linguistic::GetThesaurusReplaceText( aSynonym ) );
+ pVCLMenu->InsertItem( nId, aItemText );
+ pVCLMenu->SetItemCommand( nId, ".uno:ThesaurusFromContext?WordReplace:string=" + aItemText );
+
+ if ( !aSynonymsImageUrl.isEmpty() )
+ pVCLMenu->SetItemImage( nId, aImage );
+ nId++;
+ }
+
+ pVCLMenu->InsertSeparator();
+ OUString aThesaurusDialogCmd( ".uno:ThesaurusDialog" );
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aThesaurusDialogCmd, m_aModuleName);
+ pVCLMenu->InsertItem( nId, vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties) );
+ pVCLMenu->SetItemCommand( 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..ee2cb30e9
--- /dev/null
+++ b/framework/source/uielement/togglebuttontoolbarcontroller.cxx
@@ -0,0 +1,273 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <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,
+ sal_uInt16 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( 2 );
+
+ // Add key modifier to argument list
+ aArgs[0].Name = "KeyModifier";
+ aArgs[0].Value <<= KeyModifier;
+ aArgs[1].Name = "Text";
+ aArgs[1].Value <<= 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::makeAny(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::makeAny(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..670b30873
--- /dev/null
+++ b/framework/source/uielement/toolbarmanager.cxx
@@ -0,0 +1,1939 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <uielement/generictoolbarcontroller.hxx>
+#include <uielement/styletoolbarcontroller.hxx>
+#include <properties.h>
+#include <framework/sfxhelperfunctions.hxx>
+#include <classes/fwkresid.hxx>
+#include <classes/resource.hxx>
+#include <strings.hrc>
+#include <framework/addonsoptions.hxx>
+#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/sequence.hxx>
+#include <svtools/miscopt.hxx>
+#include <svtools/imgdef.hxx>
+#include <vcl/event.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 <tools/debug.hxx>
+
+#include <svtools/menuoptions.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
+{
+
+static const char ITEM_DESCRIPTOR_COMMANDURL[] = "CommandURL";
+static const char ITEM_DESCRIPTOR_VISIBLE[] = "IsVisible";
+
+static 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;
+}
+
+} // end anonymous namespace
+
+// XInterface, XTypeProvider, XServiceInfo
+
+ToolBarManager::ToolBarManager( const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rFrame,
+ const OUString& rResourceName,
+ ToolBox* pToolBar ) :
+ m_bDisposed( false ),
+ m_bAddedToTaskPaneList( true ),
+ m_bFrameActionRegistered( false ),
+ m_bUpdateControllers( false ),
+ m_eSymbolSize(SvtMiscOptions().GetCurrentSymbolsSize()),
+ m_pToolBar( pToolBar ),
+ m_aResourceName( rResourceName ),
+ m_xFrame( rFrame ),
+ m_aListenerContainer( m_mutex ),
+ m_xContext( rxContext ),
+ m_sIconTheme( SvtMiscOptions().GetIconTheme() )
+{
+ OSL_ASSERT( m_xContext.is() );
+
+ vcl::Window* pWindow = m_pToolBar;
+ while ( pWindow && !pWindow->IsSystemWindow() )
+ pWindow = pWindow->GetParent();
+
+ if ( pWindow )
+ static_cast<SystemWindow *>(pWindow)->GetTaskPaneList()->AddWindow( m_pToolBar );
+
+ m_xToolbarControllerFactory = frame::theToolbarControllerFactory::get( m_xContext );
+ m_xURLTransformer = URLTransformer::create( m_xContext );
+
+ m_pToolBar->SetSelectHdl( LINK( this, ToolBarManager, Select) );
+ m_pToolBar->SetClickHdl( LINK( this, ToolBarManager, Click ) );
+ m_pToolBar->SetDropdownClickHdl( LINK( this, ToolBarManager, DropdownClick ) );
+ m_pToolBar->SetDoubleClickHdl( LINK( this, ToolBarManager, DoubleClick ) );
+ m_pToolBar->SetStateChangedHdl( LINK( this, ToolBarManager, StateChanged ) );
+ m_pToolBar->SetDataChangedHdl( LINK( this, ToolBarManager, DataChanged ) );
+
+ if (m_eSymbolSize == SFX_SYMBOLS_SIZE_LARGE)
+ m_pToolBar->SetToolboxButtonSize(ToolBoxButtonSize::Large);
+ else if (m_eSymbolSize == SFX_SYMBOLS_SIZE_32)
+ m_pToolBar->SetToolboxButtonSize(ToolBoxButtonSize::Size32);
+ else
+ m_pToolBar->SetToolboxButtonSize(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_pToolBar->SetMenuType( nMenuType );
+ m_pToolBar->SetMenuButtonHdl( LINK( this, ToolBarManager, MenuButton ) );
+ m_pToolBar->SetMenuExecuteHdl( LINK( this, ToolBarManager, MenuPreExecute ) );
+ m_pToolBar->GetMenu()->SetSelectHdl( LINK( this, ToolBarManager, MenuSelect ) );
+
+ // set name for testtool, the useful part is after the last '/'
+ sal_Int32 idx = rResourceName.lastIndexOf('/');
+ idx++; // will become 0 if '/' not found: use full string
+ OString aHelpIdAsString( ".HelpId:" );
+ OUString aToolbarName = rResourceName.copy( idx );
+ aHelpIdAsString += OUStringToOString( aToolbarName, RTL_TEXTENCODING_UTF8 );
+ m_pToolBar->SetHelpId( aHelpIdAsString );
+
+ m_aAsyncUpdateControllersTimer.SetTimeout( 50 );
+ m_aAsyncUpdateControllersTimer.SetInvokeHandler( LINK( this, ToolBarManager, AsyncUpdateControllersHdl ) );
+ m_aAsyncUpdateControllersTimer.SetDebugName( "framework::ToolBarManager m_aAsyncUpdateControllersTimer" );
+
+ SvtMiscOptions().AddListenerLink( LINK( this, ToolBarManager, MiscOptionsChanged ) );
+}
+
+ToolBarManager::~ToolBarManager()
+{
+ assert(!m_aAsyncUpdateControllersTimer.IsActive());
+ assert(!m_pToolBar); // must be disposed by ToolbarLayoutManager
+ OSL_ASSERT( !m_bAddedToTaskPaneList );
+}
+
+void ToolBarManager::Destroy()
+{
+ 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++ )
+ {
+ sal_uInt16 nItemId = m_pToolBar->GetItemId( i );
+ if ( nItemId > 0 )
+ delete static_cast< AddonsParams* >( m_pToolBar->GetItemData( nItemId ));
+ }
+
+ // tdf#119390 this will reparent the toolbar, so focus is restored from a
+ // floating toolbar to the last focused control of the application window.
+ m_pToolBar->SetParentToDefaultWindow();
+ // #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();
+
+ 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;
+
+ vcl::ImageType eImageType = vcl::ImageType::Size16;
+
+ if (m_eSymbolSize == SFX_SYMBOLS_SIZE_LARGE)
+ {
+ m_pToolBar->SetToolboxButtonSize(ToolBoxButtonSize::Large);
+ eImageType = vcl::ImageType::Size26;
+ }
+ else if (m_eSymbolSize == SFX_SYMBOLS_SIZE_32)
+ {
+ eImageType = vcl::ImageType::Size32;
+ m_pToolBar->SetToolboxButtonSize(ToolBoxButtonSize::Size32);
+ }
+ else
+ {
+ m_pToolBar->SetToolboxButtonSize(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_pToolBar->GetItemCommand( it.first );
+ 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 = framework::AddonsOptions().GetImageFromURL(aCommandURL, bBigImages);
+ m_pToolBar->SetItemImage( it.first, aImage );
+ }
+ }
+
+ ::Size aSize = m_pToolBar->CalcWindowSizePixel();
+ m_pToolBar->SetOutputSizePixel( aSize );
+}
+
+void ToolBarManager::UpdateControllers()
+{
+
+ if( SvtMiscOptions().DisableUICustomization() )
+ {
+ Any a;
+ Reference< XLayoutManager > xLayoutManager;
+ Reference< XPropertySet > xFramePropSet( m_xFrame, UNO_QUERY );
+ if ( xFramePropSet.is() )
+ a = xFramePropSet->getPropertyValue("LayoutManager");
+ a >>= xLayoutManager;
+ Reference< XDockableWindow > xDockable( VCLUnoHelper::GetInterface( m_pToolBar ), 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 >(
+ static_cast< OWeakObject* >( this ), UNO_QUERY ));
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+
+ if ( m_xModuleImageManager.is() )
+ {
+ try
+ {
+ m_xModuleImageManager->removeConfigurationListener(
+ Reference< XUIConfigurationListener >(
+ static_cast< OWeakObject* >( this ), UNO_QUERY ));
+ }
+ 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( static_cast< OWeakObject* >(this), UNO_QUERY );
+
+ EventObject aEvent( xThis );
+ m_aListenerContainer.disposeAndClear( aEvent );
+
+ {
+ SolarMutexGuard g;
+
+ if (m_bDisposed)
+ {
+ return;
+ }
+
+ RemoveControllers();
+
+ if ( m_xDocImageManager.is() )
+ {
+ try
+ {
+ m_xDocImageManager->removeConfigurationListener(
+ Reference< XUIConfigurationListener >(
+ static_cast< OWeakObject* >( this ), UNO_QUERY ));
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+ m_xDocImageManager.clear();
+ if ( m_xModuleImageManager.is() )
+ {
+ try
+ {
+ m_xModuleImageManager->removeConfigurationListener(
+ Reference< XUIConfigurationListener >(
+ static_cast< OWeakObject* >( this ), UNO_QUERY ));
+ }
+ 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 >(
+ static_cast< ::cppu::OWeakObject *>( this ), UNO_QUERY ));
+ }
+ 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();
+
+ m_aListenerContainer.addInterface( cppu::UnoType<XEventListener>::get(), xListener );
+}
+
+void SAL_CALL ToolBarManager::removeEventListener( const Reference< XEventListener >& xListener )
+{
+ m_aListenerContainer.removeInterface( cppu::UnoType<XEventListener>::get(), 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( 1 );
+ Sequence< Reference< XGraphic > > aGraphicSeq;
+ aCmdURLSeq[0] = pIter->first;
+ 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<sal_uInt16>& rIDs = rIter->second.aIds;
+ m_pToolBar->SetItemImage( rIter->second.nId, rImage );
+ for (auto const& it : rIDs)
+ {
+ m_pToolBar->SetItemImage(it, 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_pToolBar->GetItemCount(); i++ )
+ {
+ sal_uInt16 nItemId = m_pToolBar->GetItemId( i );
+ if ( nItemId > 0 )
+ {
+ Reference< XComponent > xComponent( m_aControllerMap[ nItemId ], UNO_QUERY );
+ if ( xComponent.is() )
+ {
+ try
+ {
+ xComponent->dispose();
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+ m_pToolBar->SetItemWindow(nItemId, nullptr);
+ }
+ }
+ m_aControllerMap.clear();
+}
+
+void ToolBarManager::CreateControllers()
+{
+ Reference< XWindow > xToolbarWindow = VCLUnoHelper::GetInterface( m_pToolBar );
+
+ css::util::URL aURL;
+ bool bHasDisabledEntries = SvtCommandOptions().HasEntries( SvtCommandOptions::CMDOPTION_DISABLED );
+ SvtCommandOptions aCmdOptions;
+
+ for ( ToolBox::ImplToolItems::size_type i = 0; i < m_pToolBar->GetItemCount(); i++ )
+ {
+ sal_uInt16 nId = m_pToolBar->GetItemId( i );
+ if ( nId == 0 )
+ continue;
+
+ bool bInit( true );
+ bool bCreate( true );
+ Reference< XStatusListener > xController;
+
+ svt::ToolboxController* pController( nullptr );
+
+ OUString aCommandURL( m_pToolBar->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_pToolBar->HideItem( nId );
+ 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( makeAny( aPropValue ));
+ aPropValue.Name = "Frame";
+ aPropValue.Value <<= m_xFrame;
+ aPropertyVector.push_back( makeAny( aPropValue ));
+ aPropValue.Name = "ServiceManager";
+ Reference<XMultiServiceFactory> xMSF(m_xContext->getServiceManager(), UNO_QUERY_THROW);
+ aPropValue.Value <<= xMSF;
+ aPropertyVector.push_back( makeAny( aPropValue ));
+ aPropValue.Name = "ParentWindow";
+ aPropValue.Value <<= xToolbarWindow;
+ aPropertyVector.push_back( makeAny( aPropValue ));
+ aPropValue.Name = "Identifier";
+ aPropValue.Value <<= nId;
+ aPropertyVector.push_back( uno::makeAny( 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_pToolBar->IsItemVisible(nId)))
+ bCreate = false;
+
+ if ( !xController.is() && bCreate )
+ {
+ pController = CreateToolBoxController( m_xFrame, m_pToolBar, nId, aCommandURL );
+ if ( !pController )
+ {
+ if ( 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 ), UNO_QUERY );
+
+ xController = xStatusListener;
+ }
+ else if ( aCommandURL.startsWith( ".uno:StyleApply?" ) )
+ {
+ xController.set( new StyleToolbarController( m_xContext, m_xFrame, aCommandURL ));
+ m_pToolBar->SetItemBits( nId, m_pToolBar->GetItemBits( nId ) | ToolBoxItemBits::CHECKABLE );
+ }
+ else if ( aCommandURL.startsWith( "private:resource/menubar/" ) )
+ {
+ xController.set( new MenuToolbarController );
+ }
+ else
+ {
+ 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_pToolBar->SetItemBits( nId, m_pToolBar->GetItemBits( nId ) | ToolBoxItemBits::CHECKABLE );
+ }
+ }
+ else if ( pController )
+ {
+ xController.set( static_cast< ::cppu::OWeakObject *>( pController ), UNO_QUERY );
+ }
+ }
+
+ // 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 )
+ {
+ PropertyValue aPropValue;
+ std::vector< Any > aPropertyVector;
+
+ aPropValue.Name = "Frame";
+ aPropValue.Value <<= m_xFrame;
+ aPropertyVector.push_back( makeAny( aPropValue ));
+ aPropValue.Name = "CommandURL";
+ aPropValue.Value <<= aCommandURL;
+ aPropertyVector.push_back( makeAny( aPropValue ));
+ aPropValue.Name = "ServiceManager";
+ Reference<XMultiServiceFactory> xMSF(m_xContext->getServiceManager(), UNO_QUERY_THROW);
+ aPropValue.Value <<= xMSF;
+ aPropertyVector.push_back( makeAny( aPropValue ));
+ aPropValue.Name = "ParentWindow";
+ aPropValue.Value <<= xToolbarWindow;
+ aPropertyVector.push_back( makeAny( aPropValue ));
+ aPropValue.Name = "ModuleIdentifier";
+ aPropValue.Value <<= m_aModuleIdentifier;
+ aPropertyVector.push_back( makeAny( aPropValue ));
+ aPropValue.Name = "Identifier";
+ aPropValue.Value <<= nId;
+ aPropertyVector.push_back( uno::makeAny( aPropValue ) );
+
+ Sequence< Any > aArgs( comphelper::containerToSequence( aPropertyVector ));
+ xInit->initialize( aArgs );
+
+ if (pController)
+ {
+ if (aCommandURL == ".uno:SwitchXFormsDesignMode" || aCommandURL == ".uno:ViewDataSourceBrowser")
+ pController->setFastPropertyValue_NoBroadcast(1, makeAny(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 ( nType == WindowType::LISTBOX || nType == WindowType::MULTILISTBOX || nType == WindowType::COMBOBOX )
+ pItemWin->SetAccessibleName( m_pToolBar->GetItemText( nId ) );
+ m_pToolBar->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 >(
+ static_cast< ::cppu::OWeakObject *>( this ), UNO_QUERY ));
+ }
+}
+
+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 >(
+ static_cast< OWeakObject* >( this ), UNO_QUERY ));
+ }
+ }
+ }
+
+ 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 >(
+ static_cast< OWeakObject* >( this ), UNO_QUERY ));
+ }
+}
+
+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_pToolBar->Clear();
+ m_aControllerMap.clear();
+ m_aCommandMap.clear();
+
+ sal_uInt16 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) &&
+ !SvtMiscOptions().IsExperimentalMode())
+ {
+ continue;
+ }
+
+ if (( nType == css::ui::ItemType::DEFAULT ) && !aCommandURL.isEmpty() )
+ {
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommandURL, m_aModuleIdentifier);
+ OUString aString(vcl::CommandInfoProvider::GetLabelForCommand(aProperties));
+
+ ToolBoxItemBits nItemBits = ConvertStyleToToolboxItemBits( nStyle );
+ m_pToolBar->InsertItem( nId, aString, nItemBits );
+ m_pToolBar->SetItemCommand( nId, aCommandURL );
+ if ( !aTooltip.isEmpty() )
+ m_pToolBar->SetQuickHelpText(nId, aTooltip);
+ else
+ m_pToolBar->SetQuickHelpText(nId, vcl::CommandInfoProvider::GetTooltipForCommand(aCommandURL, aProperties, m_xFrame));
+
+ if ( !aLabel.isEmpty() )
+ {
+ m_pToolBar->SetItemText( nId, aLabel );
+ }
+ else
+ {
+ m_pToolBar->SetItemText( nId, aString );
+ }
+ m_pToolBar->EnableItem( nId );
+ m_pToolBar->SetItemState( nId, TRISTATE_FALSE );
+
+ // 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_pToolBar->HideItem( nId );
+
+ ++nId;
+ }
+ else if ( nType == css::ui::ItemType::SEPARATOR_LINE )
+ {
+ m_pToolBar->InsertSeparator();
+ }
+ else if ( nType == css::ui::ItemType::SEPARATOR_SPACE )
+ {
+ m_pToolBar->InsertSpace();
+ }
+ else if ( nType == css::ui::ItemType::SEPARATOR_LINEBREAK )
+ {
+ m_pToolBar->InsertBreak();
+ }
+ }
+ }
+ catch (const css::lang::IndexOutOfBoundsException&)
+ {
+ break;
+ }
+ }
+
+ // Support add-on toolbar merging here. Working directly on the toolbar object is much
+ // simpler and faster.
+ const 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() )
+ {
+ sal_uInt16 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 ))
+ {
+ 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,
+ nItemId,
+ m_aCommandMap,
+ m_aModuleIdentifier,
+ rInstruction.aMergeCommand,
+ rInstruction.aMergeCommandParameter,
+ aItems );
+ }
+ else
+ {
+ ToolBarMerger::ProcessMergeFallback( m_pToolBar,
+ nItemId,
+ m_aCommandMap,
+ m_aModuleIdentifier,
+ rInstruction.aMergeCommand,
+ rInstruction.aMergeFallback,
+ aItems );
+ }
+ }
+ }
+ }
+
+ // 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_pToolBar->WillUsePopupMode() )
+ UpdateControllers();
+ else if ( m_pToolBar->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_pToolBar->SetText( aUIName );
+ }
+ catch (const Exception&)
+ {
+ }
+}
+
+void ToolBarManager::FillOverflowToolbar( ToolBox const * pParent )
+{
+ CommandInfo aCmdInfo;
+ bool bInsertSeparator = false;
+ for ( ToolBox::ImplToolItems::size_type i = 0; i < pParent->GetItemCount(); ++i )
+ {
+ sal_uInt16 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 = 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(void ( SAL_CALL XToolbarController::*_pClick )())
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ sal_uInt16 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.get()->*_pClick)( );
+ }
+}
+
+IMPL_LINK_NOARG(ToolBarManager, Click, ToolBox *, void)
+{
+ HandleClick(&XToolbarController::click);
+}
+
+IMPL_LINK_NOARG(ToolBarManager, DropdownClick, ToolBox *, void)
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ sal_uInt16 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() )
+ {
+ Reference< XWindow > xWin = xController->createPopupWindow();
+ if ( xWin.is() )
+ xWin->setFocus();
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ToolBarManager, DoubleClick, ToolBox *, void)
+{
+ HandleClick(&XToolbarController::doubleClick);
+}
+
+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;
+}
+
+bool ToolBarManager::MenuItemAllowed( sal_uInt16 ) const
+{
+ return true;
+}
+
+void ToolBarManager::AddCustomizeMenuItems(ToolBox const * pToolBar)
+{
+ // 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 = !SvtMenuOptions().IsEntryHidingEnabled();
+
+ ::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 (MenuItemAllowed(MENUITEM_TOOLBAR_VISIBLEBUTTON))
+ {
+ pMenu->InsertItem(MENUITEM_TOOLBAR_VISIBLEBUTTON, FwkResId(STR_TOOLBAR_VISIBLE_BUTTONS));
+ xVisibleItemsPopupMenu = VclPtr<PopupMenu>::Create();
+ pMenu->SetPopupMenu(MENUITEM_TOOLBAR_VISIBLEBUTTON, xVisibleItemsPopupMenu);
+ }
+
+ if (MenuItemAllowed(MENUITEM_TOOLBAR_CUSTOMIZETOOLBAR) && m_pToolBar->IsCustomize())
+ {
+ pMenu->InsertItem(MENUITEM_TOOLBAR_CUSTOMIZETOOLBAR, FwkResId(STR_TOOLBAR_CUSTOMIZE_TOOLBAR));
+ pMenu->SetItemCommand(MENUITEM_TOOLBAR_CUSTOMIZETOOLBAR, ".uno:ConfigureToolboxVisible");
+ }
+
+ if (nGroupLen != pMenu->GetItemCount())
+ {
+ pMenu->InsertSeparator();
+ nGroupLen = pMenu->GetItemCount();
+ }
+
+ if (pToolBar->IsFloatingMode())
+ {
+ if (MenuItemAllowed(MENUITEM_TOOLBAR_DOCKTOOLBAR))
+ {
+ pMenu->InsertItem(MENUITEM_TOOLBAR_DOCKTOOLBAR, FwkResId(STR_TOOLBAR_DOCK_TOOLBAR));
+ pMenu->SetAccelKey(MENUITEM_TOOLBAR_DOCKTOOLBAR, vcl::KeyCode(KEY_F10, true, true, false, false));
+ }
+ }
+ else
+ {
+ if (MenuItemAllowed(MENUITEM_TOOLBAR_UNDOCKTOOLBAR))
+ {
+ pMenu->InsertItem(MENUITEM_TOOLBAR_UNDOCKTOOLBAR, FwkResId(STR_TOOLBAR_UNDOCK_TOOLBAR));
+ pMenu->SetAccelKey(MENUITEM_TOOLBAR_UNDOCKTOOLBAR, vcl::KeyCode(KEY_F10, true, true, false, false));
+ }
+ }
+
+ if (MenuItemAllowed(MENUITEM_TOOLBAR_DOCKALLTOOLBAR))
+ pMenu->InsertItem(MENUITEM_TOOLBAR_DOCKALLTOOLBAR, FwkResId(STR_TOOLBAR_DOCK_ALL_TOOLBARS));
+
+ if (nGroupLen != pMenu->GetItemCount())
+ {
+ pMenu->InsertSeparator();
+ nGroupLen = pMenu->GetItemCount();
+ }
+
+ if (MenuItemAllowed(MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION))
+ pMenu->InsertItem(MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION, FwkResId(STR_TOOLBAR_LOCK_TOOLBAR));
+
+ if (MenuItemAllowed(MENUITEM_TOOLBAR_CLOSE))
+ 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 (SvtMiscOptions().DisableUICustomization())
+ {
+ 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 )
+ {
+ sal_uInt16 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);
+ pOverflowToolBar->SetOutStyle( pToolBar->GetOutStyle() );
+ 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( 1 );
+
+ aPropSeq[ 0 ].Name = "ResourceURL";
+ aPropSeq[ 0 ].Value <<= 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[nVisibleIndex].Value <<= !bVisible;
+ try
+ {
+ xItemContainer->replaceByIndex( i, makeAny( 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()) );
+ sal_uInt16 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 sal_uInt16 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( static_cast< OWeakObject* >(this), UNO_QUERY );
+
+ 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..d6d5b4ad4
--- /dev/null
+++ b/framework/source/uielement/toolbarmerger.cxx
@@ -0,0 +1,650 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/toolbarmerger.hxx>
+#include <uielement/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>
+
+namespace framework
+{
+
+static const char MERGE_TOOLBAR_URL[] = "URL";
+static const char MERGE_TOOLBAR_TITLE[] = "Title";
+static const char MERGE_TOOLBAR_IMAGEID[] = "ImageIdentifier";
+static const char MERGE_TOOLBAR_CONTEXT[] = "Context";
+static const char MERGE_TOOLBAR_TARGET[] = "Target";
+static const char MERGE_TOOLBAR_CONTROLTYPE[] = "ControlType";
+static const char MERGE_TOOLBAR_WIDTH[] = "Width";
+
+static const char MERGECOMMAND_ADDAFTER[] = "AddAfter";
+static const char MERGECOMMAND_ADDBEFORE[] = "AddBefore";
+static const char MERGECOMMAND_REPLACE[] = "Replace";
+static const char MERGECOMMAND_REMOVE[] = "Remove";
+
+static const char MERGEFALLBACK_ADDLAST[] = "AddLast";
+static const char MERGEFALLBACK_ADDFIRST[] = "AddFirst";
+static const char MERGEFALLBACK_IGNORE[] = "Ignore";
+
+static const char TOOLBARCONTROLLER_BUTTON[] = "Button";
+static const char TOOLBARCONTROLLER_COMBOBOX[] = "Combobox";
+static const char TOOLBARCONTROLLER_EDIT[] = "Editfield";
+static const char TOOLBARCONTROLLER_SPINFIELD[] = "Spinfield";
+static const char TOOLBARCONTROLLER_IMGBUTTON[] = "ImageButton";
+static const char TOOLBARCONTROLLER_DROPDOWNBOX[] = "Dropdownbox";
+static const char TOOLBARCONTROLLER_DROPDOWNBTN[] = "DropdownButton";
+static const char TOOLBARCONTROLLER_TOGGLEDDBTN[] = "ToggleDropdownButton";
+static const char TOOLBARCONTROLLER_FIXEDIMAGE[] = "FixedImage";
+static const char TOOLBARCONTROLLER_FIXEDTEXT[] = "FixedText";
+
+static 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(
+ const OUString& rContext,
+ const OUString& rModuleIdentifier )
+{
+ return ( rContext.isEmpty() || ( rContext.indexOf( rModuleIdentifier ) >= 0 ));
+}
+
+/**
+ 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.aImageIdentifier,
+ 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
+ rImageIdentifier
+
+ Contains the value of the property with
+ Name="ImageIdentifier"
+
+ @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& rImageIdentifier,
+ 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_IMAGEID )
+ prop.Value >>= rImageIdentifier;
+ 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,
+ const OUString& 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 sal_uInt16 nItemId = pToolbar->GetItemId( i );
+ if ( nItemId > 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,
+ sal_uInt16& rItemId,
+ CommandToInfoMap& rCommandMap,
+ const OUString& rModuleIdentifier,
+ const OUString& rMergeCommand,
+ const OUString& 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,
+ sal_uInt16& rItemId,
+ CommandToInfoMap& rCommandMap,
+ const OUString& rModuleIdentifier,
+ const OUString& rMergeCommand,
+ const OUString& 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,
+ sal_uInt16& rItemId,
+ CommandToInfoMap& rCommandMap,
+ const OUString& 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,
+ sal_uInt16& rItemId,
+ CommandToInfoMap& rCommandMap,
+ const OUString& 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,
+ const OUString& rMergeCommandParameter )
+{
+ sal_Int32 nCount = rMergeCommandParameter.toInt32();
+ 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.
+*/
+::cppu::OWeakObject* ToolBarMerger::CreateController(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< frame::XFrame > & xFrame,
+ ToolBox* pToolbar,
+ const OUString& rCommandURL,
+ sal_uInt16 nId,
+ sal_uInt16 nWidth,
+ const OUString& rControlType )
+{
+ ::cppu::OWeakObject* pResult( nullptr );
+
+ 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, sal_uInt16 nItemId, const AddonToolbarItem& rItem )
+{
+ 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->aImageId = rItem.aImageIdentifier;
+ 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..8c28cd648
--- /dev/null
+++ b/framework/source/uielement/toolbarmodemenucontroller.cxx
@@ -0,0 +1,361 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/ui/UIElementType.hpp>
+#include <com/sun/star/frame/XModuleManager.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+
+
+#include <toolkit/awt/vclxmenu.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/EnumContext.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <comphelper/types.hxx>
+#include <svtools/miscopt.hxx>
+#include <unotools/confignode.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
+{
+
+DEFINE_XSERVICEINFO_MULTISERVICE_2 ( ToolbarModeMenuController ,
+ OWeakObject ,
+ SERVICENAME_POPUPMENUCONTROLLER ,
+ IMPLEMENTATIONNAME_TOOLBARMODEMENUCONTROLLER
+ )
+
+DEFINE_INIT_SERVICE ( ToolbarModeMenuController, {} )
+
+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 ( SvtMiscOptions().DisableUICustomization() )
+ 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());
+ SvtMiscOptions aMiscOptions;
+ 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" ) );
+ 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 && !aMiscOptions.IsExperimentalMode() )
+ 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(static_cast<OWeakObject *>(this), UNO_QUERY );
+
+ osl::MutexGuard aLock( m_aMutex );
+ m_xFrame.clear();
+ m_xDispatch.clear();
+
+ if ( m_xPopupMenu.is() )
+ m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(static_cast<OWeakObject *>(this), UNO_QUERY ));
+ 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;
+ VCLXPopupMenu* pXPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getUnoTunnelImplementation<VCLXMenu>( xPopupMenu ));
+ PopupMenu* pVCLPopupMenu = pXPopupMenu ? static_cast<PopupMenu *>(pXPopupMenu->GetMenu()) : nullptr;
+
+ SAL_WARN_IF(!pVCLPopupMenu, "fwk.uielement", "worrying lack of popup menu");
+ if (!pVCLPopupMenu)
+ return;
+
+ bool bSetCheckmark = false;
+ bool bCheckmark = false;
+ for ( sal_uInt16 i = 0; i < pVCLPopupMenu->GetItemCount(); i++ )
+ {
+ sal_uInt16 nId = pVCLPopupMenu->GetItemId( i );
+ if ( nId == 0 )
+ continue;
+
+ OUString aCmd = pVCLPopupMenu->GetItemCommand( nId );
+ if ( aCmd == aFeatureURL )
+ {
+ // Enable/disable item
+ pVCLPopupMenu->EnableItem( nId, Event.IsEnabled );
+
+ // Checkmark
+ if ( Event.State >>= bCheckmark )
+ bSetCheckmark = true;
+
+ if ( bSetCheckmark )
+ pVCLPopupMenu->CheckItem( nId, bCheckmark );
+ else
+ {
+ OUString aItemText;
+
+ if ( Event.State >>= aItemText )
+ pVCLPopupMenu->SetItemText( nId, aItemText );
+ }
+ }
+ }
+}
+
+// XMenuListener
+void SAL_CALL ToolbarModeMenuController::itemSelected( const css::awt::MenuEvent& rEvent )
+{
+ Reference< css::awt::XPopupMenu > xPopupMenu;
+ Reference< XURLTransformer > xURLTransformer;
+ Reference< XFrame > xFrame;
+
+ {
+ osl::MutexGuard aLock(m_aMutex);
+ xPopupMenu = m_xPopupMenu;
+ xURLTransformer = m_xURLTransformer;
+ xFrame = m_xFrame;
+ }
+
+ if ( !xPopupMenu.is() )
+ return;
+
+ VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getUnoTunnelImplementation<VCLXMenu>( xPopupMenu ));
+ if ( !pPopupMenu )
+ return;
+
+ SolarMutexGuard aSolarMutexGuard;
+ PopupMenu* pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu());
+ OUString aCmd( pVCLPopupMenu->GetItemCommand( rEvent.MenuId ));
+
+ {
+ URL aTargetURL;
+ Sequence<PropertyValue> aArgs;
+
+ aTargetURL.Complete = ".uno:Notebookbar?File:string=" + 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,ToolbarModeMenuController, ExecuteHdl_Impl), pExecuteInfo );
+ }
+ }
+
+ URL aTargetURL;
+ Sequence<PropertyValue> aArgs;
+
+ aTargetURL.Complete = ".uno:ToolbarMode?Mode:string=" + 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, ToolbarModeMenuController, ExecuteHdl_Impl), pExecuteInfo );
+ }
+}
+
+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 )
+ m_xPopupMenu->checkItem( i+1, aMode == m_xPopupMenu->getCommand( i+1 ) );
+}
+
+// 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 >( static_cast<OWeakObject*>(this), UNO_QUERY ));
+ fillPopupMenu( m_xPopupMenu );
+ }
+}
+
+IMPL_STATIC_LINK( ToolbarModeMenuController, 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;
+}
+
+}
+
+/* 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..be2f42c57
--- /dev/null
+++ b/framework/source/uielement/toolbarsmenucontroller.cxx
@@ -0,0 +1,810 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <vcl/menu.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/image.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/window.hxx>
+#include <unotools/cmdoptions.hxx>
+#include <svtools/miscopt.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/syslocale.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;
+
+static const char CMD_RESTOREVISIBILITY[] = ".cmd:RestoreVisibility";
+
+static const char STATIC_CMD_PART[] = ".uno:AvailableToolbars?Toolbar:string=";
+static 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;
+ bool bContextSensitive;
+ 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;
+};
+
+}
+
+DEFINE_XSERVICEINFO_MULTISERVICE_2 ( ToolbarsMenuController ,
+ OWeakObject ,
+ SERVICENAME_POPUPMENUCONTROLLER ,
+ IMPLEMENTATIONNAME_TOOLBARSMENUCONTROLLER
+ )
+
+DEFINE_INIT_SERVICE ( ToolbarsMenuController, {} )
+
+static constexpr OUStringLiteral g_aPropUIName( "UIName" );
+static constexpr OUStringLiteral g_aPropResourceURL( "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;
+
+ Image aImage;
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+
+ if ( rSettings.GetUseImagesInMenus() )
+ aImage = vcl::CommandInfoProvider::GetImageForCommand(rCommandURL, m_xFrame);
+
+ VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getUnoTunnelImplementation<VCLXMenu>( rPopupMenu ));
+ if ( pPopupMenu )
+ {
+ PopupMenu* pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu());
+ if ( !!aImage )
+ pVCLPopupMenu->SetItemImage( nItemId, aImage );
+ }
+
+ 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< css::beans::PropertyValue > aTbSeq( 2 );
+ aTbSeq[0].Name = g_aPropUIName;
+ aTbSeq[1].Name = g_aPropResourceURL;
+
+ Sequence< Sequence< css::beans::PropertyValue > > aSeq( aToolBarArray.size() );
+ const sal_uInt32 nCount = aToolBarArray.size();
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ aTbSeq[0].Value <<= aToolBarArray[i].aToolBarUIName;
+ aTbSeq[1].Value <<= aToolBarArray[i].aToolBarResName;
+ aSeq[i] = aTbSeq;
+ }
+
+ return aSeq;
+}
+
+
+void ToolbarsMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu )
+{
+ if( SvtMiscOptions().DisableUICustomization() )
+ 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.bContextSensitive = bContextSensitive;
+ 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 );
+
+ {
+ SolarMutexGuard aGuard;
+ VCLXPopupMenu* pXPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getUnoTunnelImplementation<VCLXMenu>( m_xPopupMenu ));
+ PopupMenu* pVCLPopupMenu = pXPopupMenu ? static_cast<PopupMenu *>(pXPopupMenu->GetMenu()) : nullptr;
+ assert(pVCLPopupMenu);
+ if (pVCLPopupMenu)
+ pVCLPopupMenu->SetUserValue( nIndex, reinterpret_cast<void*>( aSortedTbs[i].bContextSensitive ? 1 : 0 ));
+ }
+
+ // use VCL popup menu pointer to set vital information that are not part of the awt implementation
+ OUStringBuffer aStrBuf( aStaticCmdPart );
+
+ sal_Int32 n = aSortedTbs[i].aCommand.lastIndexOf( '/' );
+ if (( n > 0 ) && (( n+1 ) < aSortedTbs[i].aCommand.getLength() ))
+ aStrBuf.append( std::u16string_view(aSortedTbs[i].aCommand).substr(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));
+ OUString aRestoreCmd( CMD_RESTOREVISIBILITY );
+ addCommand( m_xPopupMenu, aRestoreCmd, aLabelStr );
+}
+
+// XEventListener
+void SAL_CALL ToolbarsMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(static_cast<OWeakObject *>(this), UNO_QUERY );
+
+ 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 >(static_cast<OWeakObject *>(this), UNO_QUERY ));
+ 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;
+ VCLXPopupMenu* pXPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getUnoTunnelImplementation<VCLXMenu>( xPopupMenu ));
+ PopupMenu* pVCLPopupMenu = pXPopupMenu ? static_cast<PopupMenu *>(pXPopupMenu->GetMenu()) : nullptr;
+
+ SAL_WARN_IF(!pVCLPopupMenu, "fwk.uielement", "worrying lack of popup menu");
+ if (!pVCLPopupMenu)
+ return;
+
+ bool bSetCheckmark = false;
+ bool bCheckmark = false;
+ for ( sal_uInt16 i = 0; i < pVCLPopupMenu->GetItemCount(); i++ )
+ {
+ sal_uInt16 nId = pVCLPopupMenu->GetItemId( i );
+ if ( nId == 0 )
+ continue;
+
+ OUString aCmd = pVCLPopupMenu->GetItemCommand( nId );
+ if ( aCmd == aFeatureURL )
+ {
+ // Enable/disable item
+ pVCLPopupMenu->EnableItem( nId, Event.IsEnabled );
+
+ // Checkmark
+ if ( Event.State >>= bCheckmark )
+ bSetCheckmark = true;
+
+ if ( bSetCheckmark )
+ pVCLPopupMenu->CheckItem( nId, bCheckmark );
+ else
+ {
+ OUString aItemText;
+
+ if ( Event.State >>= aItemText )
+ pVCLPopupMenu->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;
+
+ VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getUnoTunnelImplementation<VCLXMenu>( xPopupMenu ));
+ if ( !pPopupMenu )
+ return;
+
+ SolarMutexGuard aSolarMutexGuard;
+ PopupMenu* pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu());
+
+ OUString aCmd( pVCLPopupMenu->GetItemCommand( 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[nVisibleIndex].Value <<= true;
+ xNameReplace->replaceByName( aElementName, makeAny( 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", makeAny( 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() ))
+ {
+ OUStringBuffer aBuf( "private:resource/toolbar/" );
+ aBuf.append( std::u16string_view(aCmd).substr(nIndex+1) );
+
+ bool bShow( !pVCLPopupMenu->IsItemChecked( rEvent.MenuId ));
+ OUString aToolBarResName( aBuf.makeStringAndClear() );
+ 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 >( static_cast<OWeakObject*>(this), UNO_QUERY ));
+ fillPopupMenu( m_xPopupMenu );
+ }
+}
+
+// XInitialization
+void SAL_CALL ToolbarsMenuController::initialize( const Sequence< Any >& aArguments )
+{
+ osl::MutexGuard aLock( m_aMutex );
+ bool bInitalized( m_bInitialized );
+ if ( bInitalized )
+ 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
+ OUString aModuleIdentifier;
+ try
+ {
+ 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;
+}
+
+}
+
+/* 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..28367830c
--- /dev/null
+++ b/framework/source/uielement/toolbarwrapper.cxx
@@ -0,0 +1,313 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <svtools/miscopt.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.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()
+{
+}
+
+// XInterface
+void SAL_CALL ToolBarWrapper::acquire() throw()
+{
+ UIConfigElementWrapperBase::acquire();
+}
+
+void SAL_CALL ToolBarWrapper::release() throw()
+{
+ 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( static_cast< OWeakObject* >(this), UNO_QUERY );
+
+ {
+ 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;
+ ToolBarManager* pToolBarManager = nullptr;
+ {
+ 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.set( static_cast< OWeakObject *>( pToolBarManager ), UNO_QUERY );
+ pToolBar->WillUsePopupMode( bPopupMode );
+ }
+ }
+
+ try
+ {
+ m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false );
+ if ( m_xConfigData.is() && pToolBar && pToolBarManager )
+ {
+ // Fill toolbar with container contents
+ pToolBarManager->FillToolbar( m_xConfigData );
+ pToolBar->SetOutStyle( SvtMiscOptions().GetToolboxStyle() );
+ 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->SetOutStyle( SvtMiscOptions().GetToolboxStyle() );
+ 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..6c7b13a3f
--- /dev/null
+++ b/framework/source/uielement/uicommanddescription.cxx
@@ -0,0 +1,714 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/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 <vcl/mnemonic.hxx>
+#include <comphelper/propertysequence.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
+
+static const char CONFIGURATION_ROOT_ACCESS[] = "/org.openoffice.Office.UI.";
+
+// Special resource URLs to retrieve additional information
+static const char PRIVATE_RESOURCE_URL[] = "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( const OUString& 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( const OUString& aModuleName, const Reference< XNameAccess >& rGenericUICommands, const Reference< XComponentContext>& rxContext ) :
+ // Create configuration hierarchical access name
+ m_aConfigCmdAccess( CONFIGURATION_ROOT_ACCESS + aModuleName + "/UserInterface/Commands"),
+ m_aConfigPopupAccess( 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 makeAny( m_aCommandImageList );
+ else if ( rCommandURL.equalsIgnoreAsciiCase( UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDROTATEIMAGELIST ))
+ return makeAny( m_aCommandRotateImageList );
+ else if ( rCommandURL.equalsIgnoreAsciiCase( UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDMIRRORIMAGELIST ))
+ return makeAny( 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 );
+
+ Sequence< PropertyValue > aPropSeq( 8 );
+ aPropSeq[0].Name = "Label";
+ aPropSeq[0].Value = !pIter->second.aContextLabel.isEmpty() ?
+ makeAny( pIter->second.aContextLabel ): makeAny( pIter->second.aLabel );
+ aPropSeq[1].Name = "Name";
+ aPropSeq[1].Value <<= pIter->second.aCommandName;
+ aPropSeq[2].Name = "Popup";
+ aPropSeq[2].Value <<= pIter->second.bPopup;
+ aPropSeq[3].Name = m_aPropProperties;
+ aPropSeq[3].Value <<= pIter->second.nProperties;
+ aPropSeq[4].Name = "PopupLabel";
+ aPropSeq[4].Value <<= pIter->second.aPopupLabel;
+ aPropSeq[5].Name = "TooltipLabel";
+ aPropSeq[5].Value <<= pIter->second.aTooltipLabel;
+ aPropSeq[6].Name = "TargetURL";
+ aPropSeq[6].Value <<= pIter->second.aTargetURL;
+ aPropSeq[7].Name = "IsExperimental";
+ aPropSeq[7].Value <<= pIter->second.bIsExperimental;
+ return makeAny( 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();
+ }
+}
+
+UICommandDescription::UICommandDescription(const Reference< XComponentContext >& rxContext)
+ : UICommandDescription_BASE(m_aMutex)
+ , m_aPrivateResourceURL(PRIVATE_RESOURCE_URL)
+ , m_xContext(rxContext)
+{
+ Reference< XNameAccess > xEmpty;
+ OUString aGenericUICommand( "GenericCommands" );
+ m_xGenericUICommands = new ConfigurationAccess_UICommand( aGenericUICommand, xEmpty, m_xContext );
+
+ impl_fillElements("ooSetupFactoryCommandConfigRef");
+
+ // insert generic commands
+ UICommandsHashMap::iterator pIter = m_aUICommandsHashMap.find( aGenericUICommand );
+ if ( pIter != m_aUICommandsHashMap.end() )
+ pIter->second = m_xGenericUICommands;
+}
+
+UICommandDescription::UICommandDescription(const Reference< XComponentContext >& rxContext, bool)
+ : UICommandDescription_BASE(m_aMutex)
+ , m_xContext(rxContext)
+{
+}
+
+UICommandDescription::~UICommandDescription()
+{
+ osl::MutexGuard g(rBHelper.rMutex);
+ 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();
+
+ 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
+ UICommandsHashMap::iterator pIter = m_aUICommandsHashMap.find( aCommandStr );
+ if ( pIter == m_aUICommandsHashMap.end() )
+ m_aUICommandsHashMap.emplace( aCommandStr, Reference< XNameAccess >() );
+ }
+ } // for ( sal_Int32 i = 0; i < aElementNames.(); i++ )
+}
+
+Any SAL_CALL UICommandDescription::getByName( const OUString& aName )
+{
+ Any a;
+
+ osl::MutexGuard g(rBHelper.rMutex);
+
+ ModuleToCommandFileMap::const_iterator pM2CIter = m_aModuleToCommandFileMap.find( aName );
+ if ( pM2CIter != m_aModuleToCommandFileMap.end() )
+ {
+ OUString aCommandFile( pM2CIter->second );
+ UICommandsHashMap::iterator pIter = m_aUICommandsHashMap.find( aCommandFile );
+ if ( pIter != m_aUICommandsHashMap.end() )
+ {
+ if ( pIter->second.is() )
+ a <<= pIter->second;
+ else
+ {
+ Reference< XNameAccess > xUICommands;
+ ConfigurationAccess_UICommand* pUICommands = new ConfigurationAccess_UICommand( aCommandFile,
+ m_xGenericUICommands,
+ m_xContext );
+ xUICommands.set( static_cast< cppu::OWeakObject* >( pUICommands ),UNO_QUERY );
+ pIter->second = xUICommands;
+ a <<= xUICommands;
+ }
+ }
+ }
+ else if ( !m_aPrivateResourceURL.isEmpty() && aName.startsWith( m_aPrivateResourceURL ) )
+ {
+ // special keys to retrieve information about a set of commands
+ return m_xGenericUICommands->getByName( aName );
+ }
+ else
+ {
+ throw NoSuchElementException();
+ }
+
+ return a;
+}
+
+Sequence< OUString > SAL_CALL UICommandDescription::getElementNames()
+{
+ osl::MutexGuard g(rBHelper.rMutex);
+
+ return comphelper::mapKeysToSequence( m_aModuleToCommandFileMap );
+}
+
+sal_Bool SAL_CALL UICommandDescription::hasByName( const OUString& aName )
+{
+ osl::MutexGuard g(rBHelper.rMutex);
+
+ 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
+
+namespace {
+
+struct Instance {
+ explicit Instance(
+ css::uno::Reference<css::uno::XComponentContext> const & context):
+ instance(static_cast<cppu::OWeakObject *>(
+ new framework::UICommandDescription(context)))
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> instance;
+};
+
+struct Singleton:
+ public rtl::StaticWithArg<
+ Instance, css::uno::Reference<css::uno::XComponentContext>, Singleton>
+{};
+
+}
+
+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(static_cast<cppu::OWeakObject *>(
+ Singleton::get(context).instance.get()));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */