diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /framework/source/uielement | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream/1%7.0.4.tar.xz libreoffice-upstream/1%7.0.4.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 '')
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: */ |