diff options
author | Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> | 2018-05-22 14:44:39 +0900 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2021-05-11 16:08:36 +0200 |
commit | 83d91ebbbda2204af9a09a921055a850a16911e0 (patch) | |
tree | d18f2aca19fbc9d14e8c82392fdd61cef86276c8 /sfx2/source/commandpopup/CommandPopup.cxx | |
parent | 0409c4f5de99a125865af0323036516eb62c5a73 (diff) |
tdf#91874 Command Popup - HUD to search and run LO commands
This adds Command Popup functionality, which is a HUD like pop-up
window, which can be used to search and run commands presented in
the main menu (but not limited to that only).
This is the initial version, which has limitation in searching
and running the command (doesn't work for some currently).
Change-Id: I92cdd3130b8de42ee0863c9e7154e7c7246d9377
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/115380
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Diffstat (limited to 'sfx2/source/commandpopup/CommandPopup.cxx')
-rw-r--r-- | sfx2/source/commandpopup/CommandPopup.cxx | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/sfx2/source/commandpopup/CommandPopup.cxx b/sfx2/source/commandpopup/CommandPopup.cxx new file mode 100644 index 000000000000..aa2555252b26 --- /dev/null +++ b/sfx2/source/commandpopup/CommandPopup.cxx @@ -0,0 +1,258 @@ +/* -*- 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 <commandpopup/CommandPopup.hxx> + +#include <workwin.hxx> + +#include <sfx2/msgpool.hxx> +#include <sfx2/bindings.hxx> + +#include <comphelper/processfactory.hxx> +#include <comphelper/dispatchcommand.hxx> + +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/theUICommandDescription.hpp> +#include <com/sun/star/ui/theUICategoryDescription.hpp> +#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/util/URL.hpp> +#include <com/sun/star/util/URLTransformer.hpp> + +#include <vcl/commandinfoprovider.hxx> + +using namespace css; + +MenuContentHandler::MenuContentHandler(uno::Reference<frame::XFrame> const& xFrame) + : m_xFrame(xFrame) + , m_sModuleLongName(vcl::CommandInfoProvider::GetModuleIdentifier(xFrame)) +{ + auto xComponentContext = comphelper::getProcessComponentContext(); + + uno::Reference<ui::XModuleUIConfigurationManagerSupplier> xModuleConfigSupplier; + xModuleConfigSupplier.set(ui::theModuleUIConfigurationManagerSupplier::get(xComponentContext)); + + uno::Reference<ui::XUIConfigurationManager> xConfigurationManager; + xConfigurationManager = xModuleConfigSupplier->getUIConfigurationManager(m_sModuleLongName); + + uno::Reference<container::XIndexAccess> xConfigData; + xConfigData = xConfigurationManager->getSettings("private:resource/menubar/menubar", false); + + gatherMenuContent(xConfigData, m_aMenuContent); +} + +void MenuContentHandler::gatherMenuContent( + uno::Reference<container::XIndexAccess> const& xIndexAccess, MenuContent& rMenuContent) +{ + for (sal_Int32 n = 0; n < xIndexAccess->getCount(); n++) + { + MenuContent aNewContent; + uno::Sequence<beans::PropertyValue> aProperties; + uno::Reference<container::XIndexAccess> xIndexContainer; + + if (!(xIndexAccess->getByIndex(n) >>= aProperties)) + continue; + + bool bIsVisible = true; + bool bIsEnabled = true; + + for (auto const& rProperty : std::as_const(aProperties)) + { + OUString aPropertyName = rProperty.Name; + if (aPropertyName == "CommandURL") + rProperty.Value >>= aNewContent.m_aCommandURL; + else if (aPropertyName == "ItemDescriptorContainer") + rProperty.Value >>= xIndexContainer; + else if (aPropertyName == "IsVisible") + rProperty.Value >>= bIsVisible; + else if (aPropertyName == "Enabled") + rProperty.Value >>= bIsEnabled; + } + + if (!bIsEnabled || !bIsVisible) + continue; + + auto aCommandProperties = vcl::CommandInfoProvider::GetCommandProperties( + aNewContent.m_aCommandURL, m_sModuleLongName); + OUString aLabel = vcl::CommandInfoProvider::GetLabelForCommand(aCommandProperties); + aNewContent.m_aMenuLabel = aLabel; + + if (!rMenuContent.m_aFullLabelWithPath.isEmpty()) + aNewContent.m_aFullLabelWithPath = rMenuContent.m_aFullLabelWithPath + " / "; + aNewContent.m_aFullLabelWithPath += aNewContent.m_aMenuLabel; + + aNewContent.m_aTooltip = vcl::CommandInfoProvider::GetTooltipForCommand( + aNewContent.m_aCommandURL, aCommandProperties, m_xFrame); + + if (xIndexContainer.is()) + gatherMenuContent(xIndexContainer, aNewContent); + + rMenuContent.m_aSubMenuContent.push_back(aNewContent); + } +} + +void MenuContentHandler::findInMenu(OUString const& rText, + std::unique_ptr<weld::TreeView>& rpCommandTreeView, + std::vector<CurrentEntry>& rCommandList) +{ + findInMenuRecursive(m_aMenuContent, rText, rpCommandTreeView, rCommandList); +} + +void MenuContentHandler::findInMenuRecursive(MenuContent const& rMenuContent, OUString const& rText, + std::unique_ptr<weld::TreeView>& rpCommandTreeView, + std::vector<CurrentEntry>& rCommandList) +{ + for (MenuContent const& aSubContent : rMenuContent.m_aSubMenuContent) + { + if (aSubContent.m_aMenuLabel.toAsciiLowerCase().startsWith(rText)) + { + OUString sCommandURL = aSubContent.m_aCommandURL; + util::URL aCommandURL; + aCommandURL.Complete = sCommandURL; + uno::Reference<uno::XComponentContext> xContext + = comphelper::getProcessComponentContext(); + uno::Reference<util::XURLTransformer> xParser = util::URLTransformer::create(xContext); + xParser->parseStrict(aCommandURL); + + auto* pViewFrame = SfxViewFrame::Current(); + + SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool(pViewFrame); + const SfxSlot* pSlot = rSlotPool.GetUnoSlot(aCommandURL.Path); + if (pSlot) + { + std::unique_ptr<SfxPoolItem> pState; + SfxItemState eState + = pViewFrame->GetBindings().QueryState(pSlot->GetSlotId(), pState); + + if (eState != SfxItemState::DISABLED) + { + auto xGraphic + = vcl::CommandInfoProvider::GetXGraphicForCommand(sCommandURL, m_xFrame); + rCommandList.emplace_back(sCommandURL, aSubContent.m_aTooltip); + + auto pIter = rpCommandTreeView->make_iterator(); + rpCommandTreeView->insert(nullptr, -1, &aSubContent.m_aFullLabelWithPath, + nullptr, nullptr, nullptr, false, pIter.get()); + rpCommandTreeView->set_image(*pIter, xGraphic); + } + } + } + findInMenuRecursive(aSubContent, rText, rpCommandTreeView, rCommandList); + } +} + +CommandListBox::CommandListBox(weld::Window* pParent, uno::Reference<frame::XFrame> const& xFrame) + : mxBuilder(Application::CreateBuilder(pParent, "sfx/ui/commandpopup.ui")) + , mxPopover(mxBuilder->weld_popover("CommandPopup")) + , mpEntry(mxBuilder->weld_entry("command_entry")) + , mpCommandTreeView(mxBuilder->weld_tree_view("command_treeview")) + , mpMenuContentHandler(std::make_unique<MenuContentHandler>(xFrame)) +{ + mpEntry->connect_changed(LINK(this, CommandListBox, ModifyHdl)); + mpEntry->connect_key_press(LINK(this, CommandListBox, TreeViewKeyPress)); + mpCommandTreeView->connect_query_tooltip(LINK(this, CommandListBox, QueryTooltip)); + mpCommandTreeView->connect_row_activated(LINK(this, CommandListBox, RowActivated)); + + Size aFrameSize = pParent->get_size(); + + // Set size of the pop-over window + tools::Long nWidth = std::max(tools::Long(400), aFrameSize.Width() / 3); + mpCommandTreeView->set_size_request(nWidth, 400); + + // Set the location of the pop-over window + tools::Rectangle aRect(Point(aFrameSize.Width() / 2, 0), Size(0, 0)); + mxPopover->popup_at_rect(pParent, aRect); + mpEntry->grab_focus(); +} + +IMPL_LINK_NOARG(CommandListBox, QueryTooltip, const weld::TreeIter&, OUString) +{ + size_t nSelected = mpCommandTreeView->get_selected_index(); + if (nSelected < maCommandList.size()) + { + auto const& rCurrent = maCommandList[nSelected]; + return rCurrent.m_aTooltip; + } + return OUString(); +} + +IMPL_LINK_NOARG(CommandListBox, RowActivated, weld::TreeView&, bool) +{ + OUString aCommandURL; + int nSelected = mpCommandTreeView->get_selected_index(); + if (nSelected < int(maCommandList.size())) + { + auto const& rCurrent = maCommandList[nSelected]; + aCommandURL = rCurrent.m_aCommandURL; + } + dispatchCommandAndClose(aCommandURL); + return true; +} + +IMPL_LINK(CommandListBox, TreeViewKeyPress, const KeyEvent&, rKeyEvent, bool) +{ + if (rKeyEvent.GetKeyCode().GetCode() == KEY_DOWN || rKeyEvent.GetKeyCode().GetCode() == KEY_UP) + { + int nDirection = rKeyEvent.GetKeyCode().GetCode() == KEY_DOWN ? 1 : -1; + int nNewIndex = mpCommandTreeView->get_selected_index() + nDirection; + nNewIndex = std::clamp(nNewIndex, 0, mpCommandTreeView->n_children() - 1); + mpCommandTreeView->select(nNewIndex); + mpCommandTreeView->set_cursor(nNewIndex); + return true; + } + else if (rKeyEvent.GetKeyCode().GetCode() == KEY_RETURN) + { + RowActivated(*mpCommandTreeView); + } + + return false; +} + +IMPL_LINK_NOARG(CommandListBox, ModifyHdl, weld::Entry&, void) +{ + mpCommandTreeView->clear(); + maCommandList.clear(); + + OUString sText = mpEntry->get_text(); + if (sText.isEmpty()) + return; + + mpCommandTreeView->freeze(); + mpMenuContentHandler->findInMenu(sText.toAsciiLowerCase(), mpCommandTreeView, maCommandList); + mpCommandTreeView->thaw(); + + if (mpCommandTreeView->n_children() > 0) + { + mpCommandTreeView->set_cursor(0); + mpCommandTreeView->select(0); + } + + mpEntry->grab_focus(); +} + +void CommandListBox::dispatchCommandAndClose(OUString const& rCommand) +{ + mxPopover->popdown(); + + if (!rCommand.isEmpty()) + comphelper::dispatchCommand(rCommand, uno::Sequence<beans::PropertyValue>()); +} + +void CommandPopupHandler::showPopup(weld::Window* pParent, + css::uno::Reference<css::frame::XFrame> const& xFrame) +{ + auto pCommandListBox = std::make_unique<CommandListBox>(pParent, xFrame); + pCommandListBox->connect_closed(LINK(this, CommandPopupHandler, PopupModeEnd)); + mpListBox = std::move(pCommandListBox); +} + +IMPL_LINK_NOARG(CommandPopupHandler, PopupModeEnd, weld::Popover&, void) { mpListBox.reset(); } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |