/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::cppu; using namespace ::com::sun::star; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::util; using namespace ::com::sun::star::beans; using namespace ::com::sun::star::frame; using namespace ::com::sun::star::container; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::ui; const sal_uInt16 ADDONMENU_MERGE_ITEMID_START = 1500; const sal_uInt16 ITEMID_ADDONLIST = 6678; // used to be a SID in sfx2, now just a unique id... namespace framework { constexpr OUStringLiteral aCmdHelpIndex = u".uno:HelpIndex"; constexpr OUStringLiteral aCmdToolsMenu = u".uno:ToolsMenu"; constexpr OUStringLiteral aCmdHelpMenu = u".uno:HelpMenu"; constexpr OUStringLiteral aSpecialWindowCommand = u".uno:WindowList"; MenuBarManager::MenuBarManager( const Reference< XComponentContext >& rxContext, const Reference< XFrame >& rFrame, const Reference< XURLTransformer >& _xURLTransformer, const Reference< XDispatchProvider >& rDispatchProvider, const OUString& rModuleIdentifier, Menu* pMenu, bool bDelete, bool bHasMenuBar ): WeakComponentImplHelper( m_aMutex ) , m_bRetrieveImages( false ) , m_bAcceleratorCfg( false ) , m_bHasMenuBar( bHasMenuBar ) , m_xContext(rxContext) , m_xURLTransformer(_xURLTransformer) , m_sIconTheme( SvtMiscOptions().GetIconTheme() ) , m_aAsyncSettingsTimer( "framework::MenuBarManager::Deactivate m_aAsyncSettingsTimer" ) { m_xPopupMenuControllerFactory = frame::thePopupMenuControllerFactory::get(m_xContext); FillMenuManager( pMenu, rFrame, rDispatchProvider, rModuleIdentifier, bDelete ); } Any SAL_CALL MenuBarManager::getMenuHandle( const Sequence< sal_Int8 >& /*ProcessId*/, sal_Int16 SystemType ) { SolarMutexGuard aSolarGuard; if ( 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(aSystemMenuData.hMenu)); } #else (void) SystemType; #endif } return a; } MenuBarManager::~MenuBarManager() { // stop asynchronous settings timer m_xDeferredItemContainer.clear(); m_aAsyncSettingsTimer.Stop(); SAL_WARN_IF( OWeakObject::m_refCount != 0, "fwk.uielement", "Who wants to delete an object with refcount > 0!" ); } void MenuBarManager::Destroy() { SolarMutexGuard aGuard; if ( rBHelper.bDisposed ) return; // stop asynchronous settings timer and // release deferred item container reference m_aAsyncSettingsTimer.Stop(); m_xDeferredItemContainer.clear(); RemoveListener(); m_aMenuItemHandlerVector.clear(); if ( m_bDeleteMenu ) { m_pVCLMenu.disposeAndClear(); } } // XComponent void SAL_CALL MenuBarManager::disposing() { Reference< XComponent > xThis( this ); SolarMutexGuard g; Destroy(); if ( m_xDocImageManager.is() ) { try { m_xDocImageManager->removeConfigurationListener( Reference< XUIConfigurationListener >(this) ); } catch ( const Exception& ) { } } if ( m_xModuleImageManager.is() ) { try { m_xModuleImageManager->removeConfigurationListener( Reference< XUIConfigurationListener >(this) ); } catch ( const Exception& ) { } } m_xDocImageManager.clear(); m_xModuleImageManager.clear(); m_xGlobalAcceleratorManager.clear(); m_xModuleAcceleratorManager.clear(); m_xDocAcceleratorManager.clear(); m_xPopupMenuControllerFactory.clear(); m_xContext.clear(); } void SAL_CALL MenuBarManager::elementInserted( const css::ui::ConfigurationEvent& Event ) { SolarMutexGuard g; /* SAFE AREA ----------------------------------------------------------------------------------------------- */ if ( rBHelper.bDisposed || rBHelper.bInDispose ) return; sal_Int16 nImageType = sal_Int16(); if (( Event.aInfo >>= nImageType ) && nImageType == 0 ) RequestImages(); } void SAL_CALL MenuBarManager::elementRemoved( const css::ui::ConfigurationEvent& Event ) { elementInserted(Event); } void SAL_CALL MenuBarManager::elementReplaced( const css::ui::ConfigurationEvent& Event ) { elementInserted(Event); } // XFrameActionListener void SAL_CALL MenuBarManager::frameAction( const FrameActionEvent& Action ) { SolarMutexGuard g; if ( 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.subView( 4 ); } else if ( aItemText.startsWith("($2)") ) { aItemText = FwkResId(STR_CLOSEDOC_ANDRETURN) + aItemText.subView( 4 ); } else if ( aItemText.startsWith("($3)") ) { aItemText = FwkResId(STR_SAVECOPYDOC) + aItemText.subView( 4 ); } m_pVCLMenu->SetItemText( menuItemHandler->nItemId, aItemText ); } if ( menuItemHandler->bMadeInvisible ) m_pVCLMenu->ShowItem( menuItemHandler->nItemId ); } else if ( Event.State >>= aVisibilityStatus ) { // Visibility m_pVCLMenu->ShowItem( menuItemHandler->nItemId, aVisibilityStatus.bVisible ); menuItemHandler->bMadeInvisible = !aVisibilityStatus.bVisible; } else if ( menuItemHandler->bMadeInvisible ) m_pVCLMenu->ShowItem( menuItemHandler->nItemId ); } if ( Event.Requery ) { // Release dispatch object - will be required on the next activate! menuItemHandler->xMenuItemDispatch.clear(); } } } } // Helper to retrieve own structure from item ID MenuBarManager::MenuItemHandler* MenuBarManager::GetMenuItemHandler( sal_uInt16 nItemId ) { SolarMutexGuard g; for (auto const& menuItemHandler : m_aMenuItemHandlerVector) { if ( menuItemHandler->nItemId == nItemId ) return menuItemHandler.get(); } return nullptr; } // Helper to set request images flag void MenuBarManager::RequestImages() { m_bRetrieveImages = true; for (auto const& menuItemHandler : m_aMenuItemHandlerVector) { if ( menuItemHandler->xSubMenuManager.is() ) { MenuBarManager* pMenuBarManager = static_cast(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(this); xEventListener->disposing( aEventObject ); } // We now provide a popup menu controller to external code. // Therefore the life-time must be explicitly handled via // dispose!! try { Reference< XComponent > xComponent( menuItemHandler->xPopupMenuController, UNO_QUERY ); if ( xComponent.is() ) xComponent->dispose(); } catch ( const RuntimeException& ) { throw; } catch ( const Exception& ) { } // Release references to controller and popup menu menuItemHandler->xPopupMenuController.clear(); menuItemHandler->xPopupMenu.clear(); } Reference< XComponent > xComponent( menuItemHandler->xSubMenuManager, UNO_QUERY ); if ( xComponent.is() ) xComponent->dispose(); } try { if ( m_xFrame.is() ) m_xFrame->removeFrameActionListener( Reference< XFrameActionListener >(this) ); } catch ( const Exception& ) { } m_xFrame = nullptr; } void SAL_CALL MenuBarManager::disposing( const EventObject& Source ) { MenuItemHandler* pMenuItemDisposing = nullptr; SolarMutexGuard g; for (auto const& menuItemHandler : m_aMenuItemHandlerVector) { if ( menuItemHandler->xMenuItemDispatch.is() && menuItemHandler->xMenuItemDispatch == Source.Source ) { // disposing called from menu item dispatcher, remove listener pMenuItemDisposing = menuItemHandler.get(); break; } } if ( pMenuItemDisposing ) { // Release references to the dispatch object URL aTargetURL; aTargetURL.Complete = pMenuItemDisposing->aMenuItemURL; m_xURLTransformer->parseStrict( aTargetURL ); pMenuItemDisposing->xMenuItemDispatch->removeStatusListener( static_cast< XStatusListener* >( this ), aTargetURL ); pMenuItemDisposing->xMenuItemDispatch.clear(); if ( pMenuItemDisposing->xPopupMenu.is() ) { Reference< css::lang::XEventListener > xEventListener( pMenuItemDisposing->xPopupMenuController, UNO_QUERY ); if ( xEventListener.is() ) xEventListener->disposing( Source ); { // Remove popup menu from menu structure as we release our reference to // the controller. m_pVCLMenu->SetPopupMenu( pMenuItemDisposing->nItemId, nullptr ); } pMenuItemDisposing->xPopupMenuController.clear(); pMenuItemDisposing->xPopupMenu.clear(); } return; } else if ( Source.Source == m_xFrame ) { // Our frame gets disposed. We have to remove all our listeners RemoveListener(); } else if ( Source.Source == Reference< XInterface >( m_xDocImageManager, UNO_QUERY )) m_xDocImageManager.clear(); else if ( Source.Source == Reference< XInterface >( m_xModuleImageManager, UNO_QUERY )) m_xModuleImageManager.clear(); } static void lcl_CheckForChildren(Menu* pMenu, sal_uInt16 nItemId) { if (PopupMenu* pThisPopup = pMenu->GetPopupMenu( nItemId )) pMenu->EnableItem( nItemId, pThisPopup->GetItemCount() != 0 ); } // vcl handler namespace { class QuietInteractionContext: public cppu::WeakImplHelper< css::uno::XCurrentContext > { public: explicit QuietInteractionContext( css::uno::Reference< css::uno::XCurrentContext > 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 = officecfg::Office::Common::View::Menu::DontHideDisabledEntry::get(); const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); bool bShowMenuImages = rSettings.GetUseImagesInMenus(); bool bShowShortcuts = m_bHasMenuBar || rSettings.GetContextMenuShortcuts(); bool bHasDisabledEntries = SvtCommandOptions().HasEntries( SvtCommandOptions::CMDOPTION_DISABLED ); SolarMutexGuard g; MenuFlags nFlag = pMenu->GetMenuFlags(); if ( bDontHide ) nFlag &= ~MenuFlags::HideDisabledEntries; else nFlag |= MenuFlags::HideDisabledEntries; pMenu->SetMenuFlags( nFlag ); if ( m_bActive ) return false; m_bActive = true; // Check if some modes have changed so we have to update our menu images OUString sIconTheme = SvtMiscOptions().GetIconTheme(); if ( m_bRetrieveImages || bShowMenuImages != m_bShowMenuImages || sIconTheme != m_sIconTheme ) { m_bShowMenuImages = bShowMenuImages; m_bRetrieveImages = false; m_sIconTheme = sIconTheme; FillMenuImages( m_xFrame, pMenu, bShowMenuImages ); } // Try to map commands to labels for ( sal_uInt16 nPos = 0; nPos < pMenu->GetItemCount(); nPos++ ) { sal_uInt16 nItemId = pMenu->GetItemId( nPos ); if (( pMenu->GetItemType( nPos ) != MenuItemType::SEPARATOR ) && ( pMenu->GetItemText( nItemId ).isEmpty() )) { OUString aCommand = pMenu->GetItemCommand( nItemId ); if ( !aCommand.isEmpty() ) { pMenu->SetItemText( nItemId, RetrieveLabelFromCommand( aCommand )); } } } // Try to set accelerator keys { if ( bShowShortcuts ) RetrieveShortcuts( m_aMenuItemHandlerVector ); for (auto const& menuItemHandler : m_aMenuItemHandlerVector) { if ( !bShowShortcuts ) { pMenu->SetAccelKey( menuItemHandler->nItemId, vcl::KeyCode() ); } else if ( menuItemHandler->aMenuItemURL == aCmdHelpIndex ) { // Set key code, workaround for hard-coded shortcut F1 mapped to .uno:HelpIndex // Only non-popup menu items can have a short-cut vcl::KeyCode aKeyCode( KEY_F1 ); pMenu->SetAccelKey( menuItemHandler->nItemId, aKeyCode ); } else if ( pMenu->GetPopupMenu( menuItemHandler->nItemId ) == nullptr ) pMenu->SetAccelKey( menuItemHandler->nItemId, menuItemHandler->aKeyCode ); } } URL aTargetURL; // Use provided dispatch provider => fallback to frame as dispatch provider Reference< XDispatchProvider > xDispatchProvider; if ( m_xDispatchProvider.is() ) xDispatchProvider = m_xDispatchProvider; else xDispatchProvider.set( m_xFrame, UNO_QUERY ); if ( xDispatchProvider.is() ) { SvtCommandOptions aCmdOptions; for (auto const& menuItemHandler : m_aMenuItemHandlerVector) { if (menuItemHandler) { if ( !menuItemHandler->xMenuItemDispatch.is() && !menuItemHandler->xSubMenuManager.is() ) { Reference< XDispatch > xMenuItemDispatch; aTargetURL.Complete = menuItemHandler->aMenuItemURL; m_xURLTransformer->parseStrict( aTargetURL ); if ( bHasDisabledEntries ) { if ( aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, aTargetURL.Path )) pMenu->HideItem( menuItemHandler->nItemId ); } if ( aTargetURL.Complete.startsWith( ".uno:StyleApply?" ) ) xMenuItemDispatch = new StyleDispatcher( m_xFrame, m_xURLTransformer, aTargetURL ); else xMenuItemDispatch = xDispatchProvider->queryDispatch( aTargetURL, menuItemHandler->aTargetFrame, 0 ); bool bPopupMenu( false ); if ( !menuItemHandler->xPopupMenuController.is() && m_xPopupMenuControllerFactory->hasController( menuItemHandler->aMenuItemURL, m_aModuleIdentifier ) ) { if( xMenuItemDispatch.is() || menuItemHandler->aMenuItemURL != ".uno:RecentFileList" ) bPopupMenu = CreatePopupMenuController(menuItemHandler.get(), m_xDispatchProvider, m_aModuleIdentifier); } else if ( menuItemHandler->xPopupMenuController.is() ) { // Force update of popup menu menuItemHandler->xPopupMenuController->updatePopupMenu(); bPopupMenu = true; if (PopupMenu* pThisPopup = pMenu->GetPopupMenu( menuItemHandler->nItemId )) pMenu->EnableItem( menuItemHandler->nItemId, pThisPopup->GetItemCount() != 0 ); } lcl_CheckForChildren(pMenu, menuItemHandler->nItemId); if ( xMenuItemDispatch.is() ) { menuItemHandler->xMenuItemDispatch = xMenuItemDispatch; menuItemHandler->aParsedItemURL = aTargetURL.Complete; if ( !bPopupMenu ) { xMenuItemDispatch->addStatusListener( static_cast< XStatusListener* >( this ), aTargetURL ); // For the menubar, we have to keep status listening to support Ubuntu's HUD. if ( !m_bHasMenuBar ) xMenuItemDispatch->removeStatusListener( static_cast< XStatusListener* >( this ), aTargetURL ); } } else if ( !bPopupMenu ) pMenu->EnableItem( menuItemHandler->nItemId, false ); } else if ( menuItemHandler->xPopupMenuController.is() ) { // Force update of popup menu menuItemHandler->xPopupMenuController->updatePopupMenu(); lcl_CheckForChildren(pMenu, menuItemHandler->nItemId); } else if ( menuItemHandler->xMenuItemDispatch.is() ) { // We need an update to reflect the current state try { aTargetURL.Complete = menuItemHandler->aMenuItemURL; m_xURLTransformer->parseStrict( aTargetURL ); menuItemHandler->xMenuItemDispatch->addStatusListener( static_cast< XStatusListener* >( this ), aTargetURL ); menuItemHandler->xMenuItemDispatch->removeStatusListener( static_cast< XStatusListener* >( this ), aTargetURL ); } catch ( const Exception& ) { } } else if ( menuItemHandler->xSubMenuManager.is() ) lcl_CheckForChildren(pMenu, menuItemHandler->nItemId); } } } } return true; } IMPL_LINK( MenuBarManager, Deactivate, Menu *, pMenu, bool ) { if ( pMenu == m_pVCLMenu ) { m_bActive = false; if ( pMenu->IsMenuBar() && m_xDeferredItemContainer.is() ) { // Start timer to handle settings asynchronous // Changing the menu inside this handler leads to // a crash under X! m_aAsyncSettingsTimer.SetInvokeHandler(LINK(this, MenuBarManager, AsyncSettingsHdl)); m_aAsyncSettingsTimer.SetTimeout(10); m_aAsyncSettingsTimer.Start(); } } return true; } IMPL_LINK_NOARG( MenuBarManager, AsyncSettingsHdl, Timer*, void) { SolarMutexGuard g; Reference< XInterface > xSelfHold( static_cast< ::cppu::OWeakObject* >( this ), UNO_QUERY_THROW ); m_aAsyncSettingsTimer.Stop(); if ( !m_bActive && m_xDeferredItemContainer.is() ) { SetItemContainer( m_xDeferredItemContainer ); m_xDeferredItemContainer.clear(); } } IMPL_LINK( MenuBarManager, Select, Menu *, pMenu, bool ) { URL aTargetURL; Sequence aArgs; Reference< XDispatch > xDispatch; { SolarMutexGuard g; sal_uInt16 nCurItemId = pMenu->GetCurItemId(); sal_uInt16 nCurPos = pMenu->GetItemPos( nCurItemId ); if ( pMenu == m_pVCLMenu && pMenu->GetItemType( nCurPos ) != MenuItemType::SEPARATOR ) { MenuItemHandler* pMenuItemHandler = GetMenuItemHandler( nCurItemId ); if ( pMenuItemHandler && pMenuItemHandler->xMenuItemDispatch.is() ) { aTargetURL.Complete = pMenuItemHandler->aMenuItemURL; m_xURLTransformer->parseStrict( aTargetURL ); if ( pMenu->GetUserValue( nCurItemId ) ) { // addon menu item selected aArgs = { comphelper::makePropertyValue("Referer", OUString("private:user")) }; } xDispatch = pMenuItemHandler->xMenuItemDispatch; } } } // tdf#126054 don't let dispatch destroy this until after function completes rtl::Reference 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, const css::uno::Reference< css::frame::XDispatchProvider >& rDispatchProvider, const OUString& rModuleIdentifier ) { OUString aItemCommand( pMenuItemHandler->aMenuItemURL ); // Try instantiate a popup menu controller. It is stored in the menu item handler. if ( !m_xPopupMenuControllerFactory.is() ) return false; auto aSeq( comphelper::InitAnyPropertySequence( { { "DispatchProvider", makeAny(rDispatchProvider) }, { "ModuleIdentifier", makeAny(rModuleIdentifier) }, { "Frame", makeAny(m_xFrame) }, { "InToolbar", makeAny(!m_bHasMenuBar) } } ) ); Reference< XPopupMenuController > xPopupMenuController( m_xPopupMenuControllerFactory->createInstanceWithArgumentsAndContext( aItemCommand, aSeq, m_xContext ), UNO_QUERY ); if ( xPopupMenuController.is() ) { // Provide our awt popup menu to the popup menu controller pMenuItemHandler->xPopupMenuController = xPopupMenuController; xPopupMenuController->setPopupMenu( pMenuItemHandler->xPopupMenu ); return true; } return false; } void MenuBarManager::FillMenuManager( Menu* pMenu, const Reference< XFrame >& rFrame, const Reference< XDispatchProvider >& rDispatchProvider, const OUString& rModuleIdentifier, bool bDelete ) { m_xFrame = rFrame; m_bActive = false; m_bDeleteMenu = bDelete; m_pVCLMenu = pMenu; m_xDispatchProvider = rDispatchProvider; const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); m_bShowMenuImages = rSettings.GetUseImagesInMenus(); m_bRetrieveImages = false; // Set module identifier when provided from outside if (!rModuleIdentifier.isEmpty()) m_aModuleIdentifier = rModuleIdentifier; else m_aModuleIdentifier = vcl::CommandInfoProvider::GetModuleIdentifier(m_xFrame); // Add root as ui configuration listener RetrieveImageManagers(); if ( pMenu->IsMenuBar() && rFrame.is() ) { // First merge all addon popup menus into our structure sal_uInt16 nPos = 0; for ( nPos = 0; nPos < pMenu->GetItemCount(); nPos++ ) { sal_uInt16 nItemId = pMenu->GetItemId( nPos ); OUString aCommand = pMenu->GetItemCommand( nItemId ); if ( aCommand == aSpecialWindowCommand || aCommand == aCmdHelpMenu ) { // Retrieve addon popup menus and add them to our menu bar framework::AddonMenuManager::MergeAddonPopupMenus( rFrame, nPos, static_cast(pMenu) ); break; } } // Merge the Add-Ons help menu items into the Office help menu framework::AddonMenuManager::MergeAddonHelpMenu( rFrame, static_cast(pMenu) ); } bool bAccessibilityEnabled( Application::GetSettings().GetMiscSettings().GetEnableATToolSupport() ); sal_uInt16 nItemCount = pMenu->GetItemCount(); OUString aItemCommand; m_aMenuItemHandlerVector.reserve(nItemCount); for ( sal_uInt16 i = 0; i < nItemCount; i++ ) { sal_uInt16 nItemId = FillItemCommand(aItemCommand,pMenu, i ); if (( pMenu->IsMenuBar() || bAccessibilityEnabled ) && ( pMenu->GetItemText( nItemId ).isEmpty() )) { if ( !aItemCommand.isEmpty() ) pMenu->SetItemText( nItemId, RetrieveLabelFromCommand( aItemCommand )); } // Command can be just an alias to another command. auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aItemCommand, m_aModuleIdentifier); OUString aRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties); if ( !aRealCommand.isEmpty() ) aItemCommand = aRealCommand; Reference< XDispatch > xDispatch; Reference< XStatusListener > xStatusListener; VclPtr pPopup = pMenu->GetPopupMenu( nItemId ); // overwrite the show icons on menu option? MenuItemBits nBits = pMenu->GetItemBits( nItemId ) & ( MenuItemBits::ICON | MenuItemBits::TEXT ); bool bItemShowMenuImages = ( m_bShowMenuImages && nBits != MenuItemBits::TEXT ) || nBits & MenuItemBits::ICON; if ( pPopup ) { // Retrieve module identifier from Help Command entry OUString aModuleIdentifier( rModuleIdentifier ); if (!pMenu->GetHelpCommand(nItemId).isEmpty()) { aModuleIdentifier = pMenu->GetHelpCommand( nItemId ); pMenu->SetHelpCommand( nItemId, "" ); } // Retrieve possible attributes struct Reference< XDispatchProvider > xPopupMenuDispatchProvider( rDispatchProvider ); MenuAttributes* pAttributes = static_cast(pMenu->GetUserValue( nItemId )); if ( pAttributes ) xPopupMenuDispatchProvider = pAttributes->xDispatchProvider; if ( m_xPopupMenuControllerFactory.is() && m_xPopupMenuControllerFactory->hasController( aItemCommand, aModuleIdentifier ) ) { // Check if we have to create a popup menu for a uno based popup menu controller. // We have to set an empty popup menu into our menu structure so the controller also // works with inplace OLE. MenuItemHandler* pItemHandler = new MenuItemHandler( nItemId, xStatusListener, xDispatch ); rtl::Reference pVCLXPopupMenu = new VCLXPopupMenu(pPopup); pItemHandler->xPopupMenu = pVCLXPopupMenu; pItemHandler->aMenuItemURL = aItemCommand; m_aMenuItemHandlerVector.push_back( std::unique_ptr(pItemHandler) ); if ( bAccessibilityEnabled || pMenu->IsMenuBar()) { if ( CreatePopupMenuController( pItemHandler, xPopupMenuDispatchProvider, aModuleIdentifier )) pItemHandler->xPopupMenuController->updatePopupMenu(); } lcl_CheckForChildren(pMenu, nItemId); } else { // Check if this is the tools menu. Add menu item if needed if ( aItemCommand == aCmdToolsMenu && AddonMenuManager::HasAddonMenuElements() ) { // Create addon popup menu if there exist elements and this is the tools popup menu VclPtr pSubMenu = AddonMenuManager::CreateAddonMenu(rFrame); if ( pSubMenu && ( pSubMenu->GetItemCount() > 0 )) { if ( pPopup->GetItemType( pPopup->GetItemCount() - 1 ) != MenuItemType::SEPARATOR ) pPopup->InsertSeparator(); pPopup->InsertItem( ITEMID_ADDONLIST, OUString() ); pPopup->SetPopupMenu( ITEMID_ADDONLIST, pSubMenu ); pPopup->SetItemCommand( ITEMID_ADDONLIST, ".uno:Addons" ); } else pSubMenu.disposeAndClear(); } rtl::Reference pSubMenuManager = new MenuBarManager( m_xContext, rFrame, m_xURLTransformer, xPopupMenuDispatchProvider, aModuleIdentifier, pPopup, false, m_bHasMenuBar ); AddMenu(pSubMenuManager.get(), aItemCommand, nItemId); } } else if ( pMenu->GetItemType( i ) != MenuItemType::SEPARATOR ) { if ( bItemShowMenuImages ) m_bRetrieveImages = true; std::unique_ptr pItemHandler(new MenuItemHandler( nItemId, xStatusListener, xDispatch )); // Retrieve possible attributes struct MenuAttributes* pAttributes = static_cast(pMenu->GetUserValue( nItemId )); if ( pAttributes ) pItemHandler->aTargetFrame = pAttributes->aTargetFrame; pItemHandler->aMenuItemURL = aItemCommand; if ( m_xPopupMenuControllerFactory.is() && m_xPopupMenuControllerFactory->hasController( aItemCommand, m_aModuleIdentifier ) ) { // Check if we have to create a popup menu for a uno based popup menu controller. // We have to set an empty popup menu into our menu structure so the controller also // works with inplace OLE. rtl::Reference pVCLXPopupMenu = new VCLXPopupMenu; PopupMenu* pPopupMenu = static_cast(pVCLXPopupMenu->GetMenu()); pMenu->SetPopupMenu( pItemHandler->nItemId, pPopupMenu ); pItemHandler->xPopupMenu = pVCLXPopupMenu; if ( bAccessibilityEnabled && CreatePopupMenuController( pItemHandler.get(), m_xDispatchProvider, m_aModuleIdentifier ) ) { pItemHandler->xPopupMenuController->updatePopupMenu(); } lcl_CheckForChildren(pMenu, pItemHandler->nItemId); } m_aMenuItemHandlerVector.push_back( std::move(pItemHandler) ); } } if ( m_bHasMenuBar && bAccessibilityEnabled ) { RetrieveShortcuts( m_aMenuItemHandlerVector ); for (auto const& menuItemHandler : m_aMenuItemHandlerVector) { // Set key code, workaround for hard-coded shortcut F1 mapped to .uno:HelpIndex // Only non-popup menu items can have a short-cut if ( menuItemHandler->aMenuItemURL == aCmdHelpIndex ) { vcl::KeyCode aKeyCode( KEY_F1 ); pMenu->SetAccelKey( menuItemHandler->nItemId, aKeyCode ); } else if ( pMenu->GetPopupMenu( menuItemHandler->nItemId ) == nullptr ) pMenu->SetAccelKey( menuItemHandler->nItemId, menuItemHandler->aKeyCode ); } } SetHdl(); } void MenuBarManager::impl_RetrieveShortcutsFromConfiguration( const Reference< XAcceleratorConfiguration >& rAccelCfg, const Sequence< OUString >& rCommands, std::vector< std::unique_ptr >& 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 >& aMenuShortCuts ) { Reference< XAcceleratorConfiguration > xDocAccelCfg( m_xDocAcceleratorManager ); Reference< XAcceleratorConfiguration > xModuleAccelCfg( m_xModuleAcceleratorManager ); Reference< XAcceleratorConfiguration > xGlobalAccelCfg( m_xGlobalAcceleratorManager ); if ( !m_bAcceleratorCfg ) { // Retrieve references on demand m_bAcceleratorCfg = true; if ( !xDocAccelCfg.is() ) { Reference< XController > xController = m_xFrame->getController(); Reference< XModel > xModel; if ( xController.is() ) { xModel = xController->getModel(); if ( xModel.is() ) { Reference< XUIConfigurationManagerSupplier > xSupplier( xModel, UNO_QUERY ); if ( xSupplier.is() ) { Reference< XUIConfigurationManager > xDocUICfgMgr = xSupplier->getUIConfigurationManager(); if ( xDocUICfgMgr.is() ) { xDocAccelCfg = xDocUICfgMgr->getShortCutManager(); m_xDocAcceleratorManager = xDocAccelCfg; } } } } } if ( !xModuleAccelCfg.is() ) { Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier = theModuleUIConfigurationManagerSupplier::get( m_xContext ); try { Reference< XUIConfigurationManager > xUICfgMgr = xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleIdentifier ); if ( xUICfgMgr.is() ) { xModuleAccelCfg = xUICfgMgr->getShortCutManager(); m_xModuleAcceleratorManager = xModuleAccelCfg; } } catch ( const RuntimeException& ) { throw; } catch ( const Exception& ) { } } if ( !xGlobalAccelCfg.is() ) try { xGlobalAccelCfg = GlobalAcceleratorConfiguration::create( m_xContext ); m_xGlobalAcceleratorManager = xGlobalAccelCfg; } catch ( const css::uno::DeploymentException& ) { SAL_WARN("fwk.uielement", "GlobalAcceleratorConfiguration" " not available. This should happen only on mobile platforms."); } } vcl::KeyCode aEmptyKeyCode; Sequence< OUString > aSeq( aMenuShortCuts.size() ); auto aSeqRange = asNonConstRange(aSeq); const sal_uInt32 nCount = aMenuShortCuts.size(); for ( sal_uInt32 i = 0; i < nCount; ++i ) { aSeqRange[i] = aMenuShortCuts[i]->aMenuItemURL; aMenuShortCuts[i]->aKeyCode = aEmptyKeyCode; } if ( m_xGlobalAcceleratorManager.is() ) impl_RetrieveShortcutsFromConfiguration( xGlobalAccelCfg, aSeq, aMenuShortCuts ); if ( m_xModuleAcceleratorManager.is() ) impl_RetrieveShortcutsFromConfiguration( xModuleAccelCfg, aSeq, aMenuShortCuts ); if ( m_xDocAcceleratorManager.is() ) impl_RetrieveShortcutsFromConfiguration( xDocAccelCfg, aSeq, aMenuShortCuts ); } void MenuBarManager::RetrieveImageManagers() { if ( !m_xDocImageManager.is() ) { Reference< XController > xController = m_xFrame->getController(); Reference< XModel > xModel; if ( xController.is() ) { xModel = xController->getModel(); if ( xModel.is() ) { Reference< XUIConfigurationManagerSupplier > xSupplier( xModel, UNO_QUERY ); if ( xSupplier.is() ) { Reference< XUIConfigurationManager > xDocUICfgMgr = xSupplier->getUIConfigurationManager(); m_xDocImageManager.set( xDocUICfgMgr->getImageManager(), UNO_QUERY ); m_xDocImageManager->addConfigurationListener( Reference< XUIConfigurationListener >(this) ); } } } } if ( !m_xModuleImageManager.is() ) { Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier = theModuleUIConfigurationManagerSupplier::get( m_xContext ); Reference< XUIConfigurationManager > xUICfgMgr = xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleIdentifier ); m_xModuleImageManager.set( xUICfgMgr->getImageManager(), UNO_QUERY ); m_xModuleImageManager->addConfigurationListener( Reference< XUIConfigurationListener >(this) ); } } void MenuBarManager::FillMenuWithConfiguration( sal_uInt16& nId, Menu* pMenu, const OUString& rModuleIdentifier, const Reference< XIndexAccess >& rItemContainer, const Reference< XURLTransformer >& rTransformer ) { Reference< XDispatchProvider > xEmptyDispatchProvider; MenuBarManager::FillMenu( nId, pMenu, rModuleIdentifier, rItemContainer, xEmptyDispatchProvider ); // Merge add-on menu entries into the menu bar MenuBarManager::MergeAddonMenus( pMenu, AddonsOptions().GetMergeMenuInstructions(), rModuleIdentifier ); bool bHasDisabledEntries = SvtCommandOptions().HasEntries( SvtCommandOptions::CMDOPTION_DISABLED ); if ( !bHasDisabledEntries ) return; sal_uInt16 nCount = pMenu->GetItemCount(); for ( sal_uInt16 i = 0; i < nCount; i++ ) { sal_uInt16 nID = pMenu->GetItemId( i ); if ( nID > 0 ) { PopupMenu* pPopupMenu = pMenu->GetPopupMenu( nID ); if ( pPopupMenu ) { if ( MustBeHidden( pPopupMenu, rTransformer )) pMenu->HideItem( nId ); } } } } void MenuBarManager::FillMenu( sal_uInt16& nId, Menu* pMenu, const OUString& rModuleIdentifier, const Reference< XIndexAccess >& rItemContainer, const Reference< XDispatchProvider >& rDispatchProvider ) { // Fill menu bar with container contents for ( sal_Int32 n = 0; n < rItemContainer->getCount(); n++ ) { Sequence< PropertyValue > aProps; OUString aCommandURL; OUString aLabel; OUString aModuleIdentifier( rModuleIdentifier ); sal_uInt16 nType = 0; Reference< XIndexAccess > xIndexContainer; Reference< XDispatchProvider > xDispatchProvider( rDispatchProvider ); sal_Int16 nStyle = 0; try { if ( rItemContainer->getByIndex( n ) >>= aProps ) { bool bShow = true; bool bEnabled = true; for ( beans::PropertyValue const & rProp : std::as_const(aProps) ) { OUString aPropName = rProp.Name; if ( aPropName == "CommandURL" ) rProp.Value >>= aCommandURL; else if ( aPropName == "ItemDescriptorContainer" ) rProp.Value >>= xIndexContainer; else if ( aPropName == "Label" ) rProp.Value >>= aLabel; else if ( aPropName == "Type" ) rProp.Value >>= nType; else if ( aPropName == "ModuleIdentifier" ) rProp.Value >>= aModuleIdentifier; else if ( aPropName == "DispatchProvider" ) rProp.Value >>= xDispatchProvider; else if ( aPropName == "Style" ) rProp.Value >>= nStyle; else if ( aPropName == "IsVisible" ) rProp.Value >>= bShow; else if ( aPropName == "Enabled" ) rProp.Value >>= bEnabled; } if (!aCommandURL.isEmpty() && vcl::CommandInfoProvider::IsExperimental(aCommandURL, rModuleIdentifier) && !officecfg::Office::Common::Misc::ExperimentalMode::get()) { continue; } if ( nType == css::ui::ItemType::DEFAULT ) { pMenu->InsertItem( nId, aLabel ); pMenu->SetItemCommand( nId, aCommandURL ); if ( nStyle ) { MenuItemBits nBits = pMenu->GetItemBits( nId ); if ( nStyle & css::ui::ItemStyle::ICON ) nBits |= MenuItemBits::ICON; if ( nStyle & css::ui::ItemStyle::TEXT ) nBits |= MenuItemBits::TEXT; if ( nStyle & css::ui::ItemStyle::RADIO_CHECK ) nBits |= MenuItemBits::RADIOCHECK; pMenu->SetItemBits( nId, nBits ); } if ( !bShow ) pMenu->HideItem( nId ); if ( !bEnabled) pMenu->EnableItem( nId, false ); if ( xIndexContainer.is() ) { VclPtr pNewPopupMenu = VclPtr::Create(); pMenu->SetPopupMenu( nId, pNewPopupMenu ); if ( xDispatchProvider.is() ) { // Use attributes struct to transport special dispatch provider void* nAttributePtr = MenuAttributes::CreateAttribute(xDispatchProvider); pMenu->SetUserValue(nId, nAttributePtr, MenuAttributes::ReleaseAttribute); } // Use help command to transport module identifier if ( !aModuleIdentifier.isEmpty() ) pMenu->SetHelpCommand( nId, aModuleIdentifier ); ++nId; FillMenu( nId, pNewPopupMenu, aModuleIdentifier, xIndexContainer, xDispatchProvider ); } else ++nId; } else { pMenu->InsertSeparator(); ++nId; } } } catch ( const IndexOutOfBoundsException& ) { break; } } } void MenuBarManager::MergeAddonMenus( Menu* pMenuBar, const MergeMenuInstructionContainer& aMergeInstructionContainer, const OUString& rModuleIdentifier ) { // set start value for the item ID for the new addon menu items sal_uInt16 nItemId = ADDONMENU_MERGE_ITEMID_START; const sal_uInt32 nCount = aMergeInstructionContainer.size(); for ( sal_uInt32 i = 0; i < nCount; i++ ) { const MergeMenuInstruction& rMergeInstruction = aMergeInstructionContainer[i]; if ( MenuBarMerger::IsCorrectContext( rMergeInstruction.aMergeContext, rModuleIdentifier )) { ::std::vector< OUString > aMergePath; // retrieve the merge path from the merge point string MenuBarMerger::RetrieveReferencePath( rMergeInstruction.aMergePoint, aMergePath ); // convert the sequence/sequence property value to a more convenient vector<> AddonMenuContainer aMergeMenuItems; MenuBarMerger::GetSubMenu( rMergeInstruction.aMergeMenu, aMergeMenuItems ); // try to find the reference point for our merge operation Menu* pMenu = pMenuBar; ReferencePathInfo aResult = MenuBarMerger::FindReferencePath( aMergePath, pMenu ); if ( aResult.eResult == RP_OK ) { // normal merge operation MenuBarMerger::ProcessMergeOperation( aResult.pPopupMenu, aResult.nPos, nItemId, rMergeInstruction.aMergeCommand, rMergeInstruction.aMergeCommandParameter, rModuleIdentifier, aMergeMenuItems ); } else { // fallback MenuBarMerger::ProcessFallbackOperation( aResult, nItemId, rMergeInstruction.aMergeCommand, rMergeInstruction.aMergeFallback, aMergePath, rModuleIdentifier, aMergeMenuItems ); } } } } void MenuBarManager::SetItemContainer( const Reference< XIndexAccess >& rItemContainer ) { SolarMutexGuard aSolarMutexGuard; Reference< XFrame > xFrame = m_xFrame; // Clear MenuBarManager structures { // Check active state as we cannot change our VCL menu during activation by the user if ( m_bActive ) { m_xDeferredItemContainer = rItemContainer; return; } RemoveListener(); m_aMenuItemHandlerVector.clear(); m_pVCLMenu->Clear(); sal_uInt16 nId = 1; // Fill menu bar with container contents FillMenuWithConfiguration( nId, m_pVCLMenu, m_aModuleIdentifier, rItemContainer, m_xURLTransformer ); // Refill menu manager again Reference< XDispatchProvider > xDispatchProvider; FillMenuManager( m_pVCLMenu, xFrame, xDispatchProvider, m_aModuleIdentifier, false ); // add itself as frame action listener m_xFrame->addFrameActionListener( Reference< XFrameActionListener >(this) ); } } void MenuBarManager::GetPopupController( PopupControllerCache& rPopupController ) { SolarMutexGuard aSolarMutexGuard; for (auto const& menuItemHandler : m_aMenuItemHandlerVector) { if ( menuItemHandler->xPopupMenuController.is() ) { Reference< XDispatchProvider > xDispatchProvider( menuItemHandler->xPopupMenuController, UNO_QUERY ); PopupControllerEntry aPopupControllerEntry; aPopupControllerEntry.m_xDispatchProvider = xDispatchProvider; // Just use the main part of the URL for popup menu controllers sal_Int32 nSchemePart( 0 ); OUString aMenuURL( menuItemHandler->aMenuItemURL ); nSchemePart = aMenuURL.indexOf( ':' ); if (( nSchemePart > 0 ) && ( aMenuURL.getLength() > ( nSchemePart+1 ))) { OUString aMainURL( "vnd.sun.star.popup:" ); sal_Int32 nQueryPart = aMenuURL.indexOf( '?', nSchemePart ); if ( nQueryPart > 0 ) aMainURL += aMenuURL.subView( nSchemePart, nQueryPart-nSchemePart ); else if ( nQueryPart == -1 ) aMainURL += aMenuURL.subView( nSchemePart+1 ); rPopupController.emplace( aMainURL, aPopupControllerEntry ); } } if ( menuItemHandler->xSubMenuManager.is() ) { MenuBarManager* pMenuBarManager = static_cast(menuItemHandler->xSubMenuManager.get()); if ( pMenuBarManager ) pMenuBarManager->GetPopupController( rPopupController ); } } } void MenuBarManager::AddMenu(MenuBarManager* pSubMenuManager,const OUString& _sItemCommand,sal_uInt16 _nItemId) { Reference< XStatusListener > xSubMenuManager( pSubMenuManager ); m_xFrame->addFrameActionListener( Reference< XFrameActionListener >( xSubMenuManager, UNO_QUERY )); Reference< XDispatch > xDispatch; std::unique_ptr pMenuItemHandler(new MenuItemHandler( _nItemId, xSubMenuManager, xDispatch )); pMenuItemHandler->aMenuItemURL = _sItemCommand; m_aMenuItemHandlerVector.push_back( std::move(pMenuItemHandler) ); } sal_uInt16 MenuBarManager::FillItemCommand(OUString& _rItemCommand, Menu* _pMenu,sal_uInt16 _nIndex) const { sal_uInt16 nItemId = _pMenu->GetItemId( _nIndex ); _rItemCommand = _pMenu->GetItemCommand( nItemId ); if ( _rItemCommand.isEmpty() ) { _rItemCommand = "slot:" + OUString::number( nItemId ); _pMenu->SetItemCommand( nItemId, _rItemCommand ); } return nItemId; } void MenuBarManager::SetHdl() { m_pVCLMenu->SetActivateHdl( LINK( this, MenuBarManager, Activate )); m_pVCLMenu->SetDeactivateHdl( LINK( this, MenuBarManager, Deactivate )); m_pVCLMenu->SetSelectHdl( LINK( this, MenuBarManager, Select )); if ( !m_xURLTransformer.is() && m_xContext.is() ) m_xURLTransformer.set( URLTransformer::create( m_xContext) ); } void MenuBarManager::FillMenuImages(Reference< XFrame > const & _xFrame, Menu* _pMenu,bool bShowMenuImages) { AddonsOptions aAddonOptions; for ( sal_uInt16 nPos = 0; nPos < _pMenu->GetItemCount(); nPos++ ) { sal_uInt16 nId = _pMenu->GetItemId( nPos ); if ( _pMenu->GetItemType( nPos ) != MenuItemType::SEPARATOR ) { // overwrite the show icons on menu option? MenuItemBits nBits = _pMenu->GetItemBits( nId ) & ( MenuItemBits::ICON | MenuItemBits::TEXT ); bool bTmpShowMenuImages = ( bShowMenuImages && nBits != MenuItemBits::TEXT ) || nBits & MenuItemBits::ICON; if ( bTmpShowMenuImages ) { OUString aMenuItemCommand = _pMenu->GetItemCommand( nId ); Image aImage = vcl::CommandInfoProvider::GetImageForCommand(aMenuItemCommand, _xFrame); if ( !aImage ) aImage = Image(aAddonOptions.GetImageFromURL(aMenuItemCommand, false)); _pMenu->SetItemImage( nId, aImage ); } else _pMenu->SetItemImage( nId, Image() ); } } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */