LibreOffice Module sfx2 (master) 1
CommandPopup.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9
11
12#include <sfx2/msgpool.hxx>
13#include <sfx2/bindings.hxx>
14#include <sfx2/msg.hxx>
15#include <sfx2/viewfrm.hxx>
16
19
20#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
21#include <com/sun/star/util/URL.hpp>
22#include <com/sun/star/util/URLTransformer.hpp>
23#include <com/sun/star/i18n/CharacterClassification.hpp>
24
26#include <vcl/event.hxx>
27#include <vcl/svapp.hxx>
29
30using namespace css;
31
32MenuContentHandler::MenuContentHandler(uno::Reference<frame::XFrame> const& xFrame)
35 , m_xCharacterClassification(i18n::CharacterClassification::create(m_xContext))
36 , m_xURLTransformer(util::URLTransformer::create(m_xContext))
37 , m_sModuleLongName(vcl::CommandInfoProvider::GetModuleIdentifier(xFrame))
38{
39 uno::Reference<ui::XModuleUIConfigurationManagerSupplier> xModuleConfigSupplier;
40 xModuleConfigSupplier.set(ui::theModuleUIConfigurationManagerSupplier::get(m_xContext));
41
42 uno::Reference<ui::XUIConfigurationManager> xConfigurationManager;
43 xConfigurationManager = xModuleConfigSupplier->getUIConfigurationManager(m_sModuleLongName);
44
45 uno::Reference<container::XIndexAccess> xConfigData;
46 xConfigData = xConfigurationManager->getSettings("private:resource/menubar/menubar", false);
47
49}
50
52 uno::Reference<container::XIndexAccess> const& xIndexAccess, MenuContent& rMenuContent)
53{
54 for (sal_Int32 n = 0; n < xIndexAccess->getCount(); n++)
55 {
56 MenuContent aNewContent;
57 uno::Sequence<beans::PropertyValue> aProperties;
58 uno::Reference<container::XIndexAccess> xIndexContainer;
59
60 if (!(xIndexAccess->getByIndex(n) >>= aProperties))
61 continue;
62
63 bool bIsVisible = true;
64 bool bIsEnabled = true;
65
66 for (auto const& rProperty : std::as_const(aProperties))
67 {
68 OUString aPropertyName = rProperty.Name;
69 if (aPropertyName == "CommandURL")
70 rProperty.Value >>= aNewContent.m_aCommandURL;
71 else if (aPropertyName == "ItemDescriptorContainer")
72 rProperty.Value >>= xIndexContainer;
73 else if (aPropertyName == "IsVisible")
74 rProperty.Value >>= bIsVisible;
75 else if (aPropertyName == "Enabled")
76 rProperty.Value >>= bIsEnabled;
77 }
78
79 if (!bIsEnabled || !bIsVisible)
80 continue;
81
82 auto aCommandProperties = vcl::CommandInfoProvider::GetCommandProperties(
83 aNewContent.m_aCommandURL, m_sModuleLongName);
84 OUString aLabel = vcl::CommandInfoProvider::GetLabelForCommand(aCommandProperties);
85 aNewContent.m_aMenuLabel = aLabel;
87
88 if (!rMenuContent.m_aFullLabelWithPath.isEmpty())
89 aNewContent.m_aFullLabelWithPath = rMenuContent.m_aFullLabelWithPath + " / ";
90 aNewContent.m_aFullLabelWithPath += aNewContent.m_aMenuLabel;
91
93 aNewContent.m_aCommandURL, aCommandProperties, m_xFrame);
94
95 if (xIndexContainer.is())
96 gatherMenuContent(xIndexContainer, aNewContent);
97
98 rMenuContent.m_aSubMenuContent.push_back(aNewContent);
99 }
100}
101
102void MenuContentHandler::findInMenu(OUString const& rText,
103 std::unique_ptr<weld::TreeView>& rpCommandTreeView,
104 std::vector<CurrentEntry>& rCommandList)
105{
106 m_aAdded.clear();
107
108 OUString aLowerCaseText = toLower(rText);
109
110 auto aTextStartCriterium = [](MenuContent const& rMenuContent, OUString const& rSearchText) {
111 return rMenuContent.m_aSearchableMenuLabel.startsWith(rSearchText);
112 };
113
114 findInMenuRecursive(m_aMenuContent, aLowerCaseText, rpCommandTreeView, rCommandList,
115 aTextStartCriterium);
116
117 auto aTextAllCriterium = [](MenuContent const& rMenuContent, OUString const& rSearchText) {
118 return rMenuContent.m_aSearchableMenuLabel.indexOf(rSearchText) > 0;
119 };
120
121 findInMenuRecursive(m_aMenuContent, aLowerCaseText, rpCommandTreeView, rCommandList,
122 aTextAllCriterium);
123}
124
126 MenuContent const& rMenuContent, OUString const& rText,
127 std::unique_ptr<weld::TreeView>& rpCommandTreeView, std::vector<CurrentEntry>& rCommandList,
128 std::function<bool(MenuContent const&, OUString const&)> const& rSearchCriterium)
129{
130 for (MenuContent const& aSubContent : rMenuContent.m_aSubMenuContent)
131 {
132 if (rSearchCriterium(aSubContent, rText))
133 {
134 addCommandIfPossible(aSubContent, rpCommandTreeView, rCommandList);
135 }
136 findInMenuRecursive(aSubContent, rText, rpCommandTreeView, rCommandList, rSearchCriterium);
137 }
138}
139
141 MenuContent const& rMenuContent, const std::unique_ptr<weld::TreeView>& rpCommandTreeView,
142 std::vector<CurrentEntry>& rCommandList)
143{
144 if (m_aAdded.find(rMenuContent.m_aFullLabelWithPath) != m_aAdded.end())
145 return;
146
147 OUString sCommandURL = rMenuContent.m_aCommandURL;
148 util::URL aCommandURL;
149 aCommandURL.Complete = sCommandURL;
150
151 if (!m_xURLTransformer->parseStrict(aCommandURL))
152 return;
153
154 auto* pViewFrame = SfxViewFrame::Current();
155 if (!pViewFrame)
156 return;
157
158 SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool(pViewFrame);
159 const SfxSlot* pSlot = rSlotPool.GetUnoSlot(aCommandURL.Path);
160 if (!pSlot)
161 return;
162
163 std::unique_ptr<SfxPoolItem> pState;
164 SfxItemState eState = pViewFrame->GetBindings().QueryState(pSlot->GetSlotId(), pState);
165 if (eState == SfxItemState::DISABLED)
166 return;
167
168 auto xGraphic = vcl::CommandInfoProvider::GetXGraphicForCommand(sCommandURL, m_xFrame);
169 rCommandList.emplace_back(sCommandURL, rMenuContent.m_aTooltip);
170
171 auto pIter = rpCommandTreeView->make_iterator();
172 rpCommandTreeView->insert(nullptr, -1, &rMenuContent.m_aFullLabelWithPath, nullptr, nullptr,
173 nullptr, false, pIter.get());
174 rpCommandTreeView->set_image(*pIter, xGraphic);
175 m_aAdded.insert(rMenuContent.m_aFullLabelWithPath);
176}
177
178OUString MenuContentHandler::toLower(OUString const& rString)
179{
180 const css::lang::Locale& rLocale = Application::GetSettings().GetUILanguageTag().getLocale();
181
182 return m_xCharacterClassification->toLower(rString, 0, rString.getLength(), rLocale);
183}
184
185CommandListBox::CommandListBox(weld::Window* pParent, uno::Reference<frame::XFrame> const& xFrame)
186 : mxBuilder(Application::CreateBuilder(pParent, "sfx/ui/commandpopup.ui"))
187 , mxPopover(mxBuilder->weld_popover("CommandPopup"))
188 , mpEntry(mxBuilder->weld_entry("command_entry"))
189 , mpCommandTreeView(mxBuilder->weld_tree_view("command_treeview"))
190 , mpMenuContentHandler(std::make_unique<MenuContentHandler>(xFrame))
191{
192 mpEntry->connect_changed(LINK(this, CommandListBox, ModifyHdl));
193 mpEntry->connect_key_press(LINK(this, CommandListBox, TreeViewKeyPress));
194 mpCommandTreeView->connect_query_tooltip(LINK(this, CommandListBox, QueryTooltip));
195 mpCommandTreeView->connect_row_activated(LINK(this, CommandListBox, RowActivated));
196
197 Size aFrameSize = pParent->get_size();
198
199 // Set size of the pop-over window
200 tools::Long nWidth = std::max(tools::Long(400), aFrameSize.Width() / 3);
201 mpCommandTreeView->set_size_request(nWidth, 400);
202
203 // Set the location of the pop-over window
204 tools::Rectangle aRect(Point(aFrameSize.Width() / 2, 0), Size(0, 0));
205 mxPopover->popup_at_rect(pParent, aRect);
206 mpEntry->grab_focus();
207}
208
209IMPL_LINK_NOARG(CommandListBox, QueryTooltip, const weld::TreeIter&, OUString)
210{
211 size_t nSelected = mpCommandTreeView->get_selected_index();
212 if (nSelected < maCommandList.size())
213 {
214 auto const& rCurrent = maCommandList[nSelected];
215 return rCurrent.m_aTooltip;
216 }
217 return OUString();
218}
219
221{
222 OUString aCommandURL;
223 int nSelected = mpCommandTreeView->get_selected_index();
224 if (nSelected != -1 && nSelected < int(maCommandList.size()))
225 {
226 auto const& rCurrent = maCommandList[nSelected];
227 aCommandURL = rCurrent.m_aCommandURL;
228 }
229 dispatchCommandAndClose(aCommandURL);
230 return true;
231}
232
233IMPL_LINK(CommandListBox, TreeViewKeyPress, const KeyEvent&, rKeyEvent, bool)
234{
235 if (rKeyEvent.GetKeyCode().GetCode() == KEY_DOWN || rKeyEvent.GetKeyCode().GetCode() == KEY_UP)
236 {
237 int nDirection = rKeyEvent.GetKeyCode().GetCode() == KEY_DOWN ? 1 : -1;
238 int nNewIndex = mpCommandTreeView->get_selected_index() + nDirection;
239 nNewIndex = std::clamp(nNewIndex, 0, mpCommandTreeView->n_children() - 1);
240 mpCommandTreeView->select(nNewIndex);
241 mpCommandTreeView->set_cursor(nNewIndex);
242 return true;
243 }
244 else if (rKeyEvent.GetKeyCode().GetCode() == KEY_RETURN)
245 {
246 RowActivated(*mpCommandTreeView);
247 return true;
248 }
249
250 return false;
251}
252
254{
255 mpCommandTreeView->clear();
256 maCommandList.clear();
257
258 OUString sText = mpEntry->get_text();
259 if (sText.isEmpty())
260 return;
261
262 mpCommandTreeView->freeze();
263 mpMenuContentHandler->findInMenu(sText, mpCommandTreeView, maCommandList);
264 mpCommandTreeView->thaw();
265
266 if (mpCommandTreeView->n_children() > 0)
267 {
268 mpCommandTreeView->set_cursor(0);
269 mpCommandTreeView->select(0);
270 }
271
272 mpEntry->grab_focus();
273}
274
275void CommandListBox::dispatchCommandAndClose(OUString const& rCommand)
276{
277 mxPopover->popdown();
278
279 if (!rCommand.isEmpty())
280 comphelper::dispatchCommand(rCommand, uno::Sequence<beans::PropertyValue>());
281}
282
284 css::uno::Reference<css::frame::XFrame> const& xFrame)
285{
286 auto pCommandListBox = std::make_unique<CommandListBox>(pParent, xFrame);
287 pCommandListBox->connect_closed(LINK(this, CommandPopupHandler, PopupModeEnd));
288 mpListBox = std::move(pCommandListBox);
289}
290
291IMPL_LINK_NOARG(CommandPopupHandler, PopupModeEnd, weld::Popover&, void) { mpListBox.reset(); }
292
293/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
IMPL_LINK(CommandListBox, TreeViewKeyPress, const KeyEvent &, rKeyEvent, bool)
IMPL_LINK_NOARG(CommandListBox, QueryTooltip, const weld::TreeIter &, OUString)
Reference< XComponentContext > m_xContext
PropertiesInfo aProperties
css::uno::Reference< css::lang::XComponent > m_xFrame
const LanguageTag & GetUILanguageTag() const
static const AllSettings & GetSettings()
std::unique_ptr< weld::Entry > mpEntry
void dispatchCommandAndClose(OUString const &rCommand)
std::unique_ptr< weld::Popover > mxPopover
CommandListBox(weld::Window *pParent, css::uno::Reference< css::frame::XFrame > const &xFrame)
std::unique_ptr< weld::TreeView > mpCommandTreeView
void showPopup(weld::Window *pParent, css::uno::Reference< css::frame::XFrame > const &xFrame)
std::unique_ptr< CommandListBox > mpListBox
const css::lang::Locale & getLocale(bool bResolveSystem=true) const
void findInMenu(OUString const &rText, std::unique_ptr< weld::TreeView > &rpCommandTreeView, std::vector< CurrentEntry > &rCommandList)
MenuContent m_aMenuContent
css::uno::Reference< css::frame::XFrame > m_xFrame
void addCommandIfPossible(MenuContent const &rMenuContent, const std::unique_ptr< weld::TreeView > &rpCommandTreeView, std::vector< CurrentEntry > &rCommandList)
std::unordered_set< OUString > m_aAdded
void findInMenuRecursive(MenuContent const &rMenuContent, OUString const &rText, std::unique_ptr< weld::TreeView > &rpCommandTreeView, std::vector< CurrentEntry > &rCommandList, std::function< bool(MenuContent const &, OUString const &)> const &rSearchCriterium)
css::uno::Reference< css::util::XURLTransformer > m_xURLTransformer
void gatherMenuContent(css::uno::Reference< css::container::XIndexAccess > const &xIndexAccess, MenuContent &rMenuContent)
css::uno::Reference< css::uno::XComponentContext > m_xContext
OUString toLower(OUString const &rString)
MenuContentHandler(css::uno::Reference< css::frame::XFrame > const &xFrame)
css::uno::Reference< css::i18n::XCharacterClassification > m_xCharacterClassification
OUString m_sModuleLongName
static SfxSlotPool & GetSlotPool(SfxViewFrame *pFrame=nullptr)
Definition: msgpool.cxx:316
const SfxSlot * GetUnoSlot(const OUString &rUnoName) const
Definition: msgpool.cxx:300
Definition: msg.hxx:184
sal_uInt16 GetSlotId() const
Definition: msg.hxx:253
static SAL_WARN_UNUSED_RESULT SfxViewFrame * Current()
Definition: viewfrm.cxx:1975
constexpr tools::Long Width() const
virtual Size get_size() const=0
sal_Int64 n
constexpr sal_uInt16 KEY_RETURN
constexpr sal_uInt16 KEY_UP
constexpr sal_uInt16 KEY_DOWN
bool dispatchCommand(const OUString &rCommand, const uno::Reference< css::frame::XFrame > &rFrame, const css::uno::Sequence< css::beans::PropertyValue > &rArguments, const uno::Reference< css::frame::XDispatchResultListener > &rListener)
Reference< XComponentContext > getProcessComponentContext()
css::uno::Reference< css::deployment::XPackageRegistry > create(css::uno::Reference< css::deployment::XPackageRegistry > const &xRootRegistry, OUString const &context, OUString const &cachePath, css::uno::Reference< css::uno::XComponentContext > const &xComponentContext)
long Long
Sequence< beans::PropertyValue > GetCommandProperties(const OUString &rsCommandName, const OUString &rsModuleName)
OUString GetTooltipForCommand(const OUString &rsCommandName, const css::uno::Sequence< css::beans::PropertyValue > &rProperties, const Reference< frame::XFrame > &rxFrame)
Reference< graphic::XGraphic > GetXGraphicForCommand(const OUString &rsCommandName, const Reference< frame::XFrame > &rxFrame, vcl::ImageType eImageType)
OUString GetLabelForCommand(const css::uno::Sequence< css::beans::PropertyValue > &rProperties)
VCL_DLLPUBLIC OUString GetModuleIdentifier(const css::uno::Reference< css::frame::XFrame > &rxFrame)
SfxItemState
OUString m_aCommandURL
std::vector< MenuContent > m_aSubMenuContent
OUString m_aMenuLabel
OUString m_aSearchableMenuLabel
OUString m_aTooltip
OUString m_aFullLabelWithPath
Reference< XFrame > xFrame
bool bIsEnabled
OUString aLabel