Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / cui / source / customize / CommandCategoryListBox.cxx
blob10758d0af47a1de2119e3ba6aca2ca1cc130cf38
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <CommandCategoryListBox.hxx>
22 #include <com/sun/star/uno/XInterface.hpp>
23 #include <com/sun/star/beans/XPropertySet.hpp>
24 #include <com/sun/star/frame/XDispatchInformationProvider.hpp>
25 #include <com/sun/star/frame/theUICommandDescription.hpp>
26 #include <com/sun/star/ui/theUICategoryDescription.hpp>
27 #include <com/sun/star/script/browse/XBrowseNode.hpp>
28 #include <com/sun/star/script/browse/BrowseNodeTypes.hpp>
29 #include <com/sun/star/script/browse/theBrowseNodeFactory.hpp>
30 #include <com/sun/star/script/browse/BrowseNodeFactoryViewTypes.hpp>
31 #include <vcl/commandinfoprovider.hxx>
32 #include <vcl/settings.hxx>
33 #include <vcl/svapp.hxx>
35 // include search util
36 #include <com/sun/star/util/SearchFlags.hpp>
37 #include <com/sun/star/util/SearchAlgorithms2.hpp>
38 #include <tools/diagnose_ex.h>
39 #include <unotools/textsearch.hxx>
41 #include <dialmgr.hxx>
42 #include <strings.hrc>
43 #include <comphelper/processfactory.hxx>
44 #include <comphelper/sequenceashashmap.hxx>
45 #include <comphelper/SetFlagContextHelper.hxx>
46 #include <comphelper/string.hxx>
47 #include <i18nlangtag/languagetag.hxx>
48 #include <i18nutil/searchopt.hxx>
49 #include <sal/log.hxx>
51 #include <cfg.hxx> //for SaveInData
53 CommandCategoryListBox::CommandCategoryListBox(std::unique_ptr<weld::ComboBox> xControl)
54 : pStylesInfo( nullptr )
55 , m_xControl(std::move(xControl))
57 //Initialize search util
58 m_searchOptions.AlgorithmType2 = css::util::SearchAlgorithms2::ABSOLUTE;
59 m_searchOptions.transliterateFlags |= TransliterationFlags::IGNORE_CASE;
60 m_searchOptions.searchFlag |= (css::util::SearchFlags::REG_NOT_BEGINOFLINE
61 | css::util::SearchFlags::REG_NOT_ENDOFLINE);
64 CommandCategoryListBox::~CommandCategoryListBox()
66 ClearAll();
69 void CommandCategoryListBox::ClearAll()
71 // Clear objects from m_aGroupInfo vector to avoid memory leak
72 for (const auto & It : m_aGroupInfo)
74 if ( It->nKind == SfxCfgKind::GROUP_STYLES && It->pObject )
76 SfxStyleInfo_Impl* pStyle = static_cast<SfxStyleInfo_Impl*>(It->pObject);
77 delete pStyle;
79 else if ( It->nKind == SfxCfgKind::FUNCTION_SCRIPT && It->pObject )
81 OUString* pScriptURI = static_cast<OUString*>(It->pObject);
82 delete pScriptURI;
84 else if ( It->nKind == SfxCfgKind::GROUP_SCRIPTCONTAINER && It->pObject)
86 css::uno::XInterface* xi = static_cast<css::uno::XInterface *>(It->pObject);
87 if (xi != nullptr)
89 xi->release();
94 m_aGroupInfo.clear();
95 m_xControl->clear();
98 void CommandCategoryListBox::Init(
99 const css::uno::Reference< css::uno::XComponentContext >& xContext,
100 const css::uno::Reference< css::frame::XFrame >& xFrame,
101 const OUString& sModuleLongName)
103 // User will not see incomplete UI
104 m_xControl->freeze();
105 ClearAll();
107 m_xContext = xContext;
108 m_xFrame = xFrame;
110 m_sModuleLongName = sModuleLongName;
111 m_xGlobalCategoryInfo = css::ui::theUICategoryDescription::get( m_xContext );
112 m_xModuleCategoryInfo.set(m_xGlobalCategoryInfo->getByName(m_sModuleLongName), css::uno::UNO_QUERY_THROW);
113 m_xUICmdDescription = css::frame::theUICommandDescription::get( m_xContext );
115 // Support style commands
116 css::uno::Reference<css::frame::XController> xController;
117 css::uno::Reference<css::frame::XModel> xModel;
118 if (xFrame.is())
119 xController = xFrame->getController();
120 if (xController.is())
121 xModel = xController->getModel();
123 m_aStylesInfo.init(sModuleLongName, xModel);
124 SetStylesInfo(&m_aStylesInfo);
128 css::uno::Reference< css::frame::XDispatchInformationProvider > xProvider(m_xFrame, css::uno::UNO_QUERY_THROW);
129 css::uno::Sequence< sal_Int16 > lGroups = xProvider->getSupportedCommandGroups();
131 sal_Int32 nGroupsLength = lGroups.getLength();
133 if ( nGroupsLength > 0 )
135 // Add the category of "All commands"
136 m_aGroupInfo.push_back( std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::GROUP_ALLFUNCTIONS, 0 ) );
137 m_xControl->append(OUString::number(reinterpret_cast<sal_Int64>(m_aGroupInfo.back().get())), CuiResId(RID_SVXSTR_ALLFUNCTIONS));
140 // Separate the "All commands"category from the actual categories
141 m_xControl->append_separator("");
143 typedef std::pair<OUString, sal_Int16> str_id;
144 std::vector<str_id> aCategories;
146 // Add the actual categories
147 for (sal_Int32 i = 0; i < nGroupsLength; ++i)
149 sal_Int16 nGroupID = lGroups[i];
150 OUString sGroupID = OUString::number(nGroupID);
151 OUString sGroupName;
155 m_xModuleCategoryInfo->getByName(sGroupID) >>= sGroupName;
156 if (sGroupName.isEmpty())
157 continue;
159 catch(const css::container::NoSuchElementException&)
161 continue;
163 aCategories.emplace_back(std::make_pair(sGroupName, nGroupID));
166 auto const sort = comphelper::string::NaturalStringSorter(
167 comphelper::getProcessComponentContext(),
168 Application::GetSettings().GetUILanguageTag().getLocale());
170 std::sort(aCategories.begin(), aCategories.end(),
171 [&sort](const str_id& a, const str_id& b)
172 { return sort.compare(a.first, b.first) < 0; });
174 // Add the actual categories
175 for (const auto &a : aCategories)
177 const OUString& rGroupName = a.first;
178 sal_Int16 nGroupID = a.second;
179 m_aGroupInfo.push_back(std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::GROUP_FUNCTION, nGroupID));
180 m_xControl->append(OUString::number(reinterpret_cast<sal_Int64>(m_aGroupInfo.back().get())), rGroupName);
183 // Separate regular commands from styles and macros
184 m_xControl->append_separator("");
186 // Add macros category
187 m_aGroupInfo.push_back(
188 std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::GROUP_SCRIPTCONTAINER, 0, nullptr) );
189 m_xControl->append(OUString::number(reinterpret_cast<sal_Int64>(m_aGroupInfo.back().get())), CuiResId(RID_SVXSTR_MACROS));
191 // Add styles category
192 //TODO: last param should contain user data?
193 m_aGroupInfo.push_back(
194 std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::GROUP_STYLES, 0, nullptr ) );
195 m_xControl->append(OUString::number(reinterpret_cast<sal_Int64>(m_aGroupInfo.back().get())), CuiResId(RID_SVXSTR_GROUP_STYLES));
197 catch(const css::uno::RuntimeException&)
198 { throw; }
199 catch(const css::uno::Exception&)
202 // Reveal the updated UI to user
203 m_xControl->thaw();
204 m_xControl->set_active(0);
207 void CommandCategoryListBox::FillFunctionsList(
208 const css::uno::Sequence<css::frame::DispatchInformation>& xCommands,
209 CuiConfigFunctionListBox* pFunctionListBox,
210 const OUString& filterTerm,
211 SaveInData *pCurrentSaveInData )
213 // Setup search filter parameters
214 m_searchOptions.searchString = filterTerm;
215 utl::TextSearch textSearch( m_searchOptions );
217 for (const auto & rInfo : xCommands)
219 OUString sUIName = getCommandName(rInfo.Command);
220 OUString sLabel = vcl::CommandInfoProvider::GetLabelForCommand(rInfo.Command, m_sModuleLongName);
221 OUString sTooltipLabel = vcl::CommandInfoProvider::GetTooltipForCommand( rInfo.Command, m_xFrame);
222 OUString sPopupLabel =
223 (vcl::CommandInfoProvider::GetPopupLabelForCommand(rInfo.Command, m_sModuleLongName))
224 .replaceFirst("~", "");
226 // Apply the search filter
227 if (!filterTerm.isEmpty()
228 && !textSearch.searchForward( sUIName )
229 && !textSearch.searchForward( sLabel )
230 && !textSearch.searchForward( sTooltipLabel )
231 && !textSearch.searchForward( sPopupLabel ) )
233 continue;
236 css::uno::Reference<css::graphic::XGraphic> xImage;
237 if (pCurrentSaveInData)
238 xImage = pCurrentSaveInData->GetImage(rInfo.Command);
240 m_aGroupInfo.push_back( std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::FUNCTION_SLOT, 0 ) );
241 SfxGroupInfo_Impl* pGrpInfo = m_aGroupInfo.back().get();
242 pGrpInfo->sCommand = rInfo.Command;
243 pGrpInfo->sLabel = sUIName;
244 pFunctionListBox->append(OUString::number(reinterpret_cast<sal_Int64>(m_aGroupInfo.back().get())), sUIName, xImage);
248 OUString CommandCategoryListBox::getCommandName(const OUString& sCommand)
250 OUString sUIName;
253 css::uno::Reference< css::container::XNameAccess > xModuleConf;
254 m_xUICmdDescription->getByName(m_sModuleLongName) >>= xModuleConf;
255 if (xModuleConf.is())
257 ::comphelper::SequenceAsHashMap lProps(xModuleConf->getByName(sCommand));
258 sUIName = lProps.getUnpackedValueOrDefault("Name", OUString());
261 catch(const css::uno::RuntimeException&)
262 { throw; }
263 catch(css::uno::Exception&)
264 { sUIName.clear(); }
266 // fallback for missing UINames !?
267 if (sUIName.isEmpty())
269 sUIName = sCommand;
272 return sUIName;
275 void CommandCategoryListBox::categorySelected(CuiConfigFunctionListBox* pFunctionListBox,
276 const OUString& filterTerm, SaveInData *pCurrentSaveInData)
278 SfxGroupInfo_Impl *pInfo = reinterpret_cast<SfxGroupInfo_Impl*>(m_xControl->get_active_id().toInt64());
279 std::vector<std::unique_ptr<weld::TreeIter>> aNodesToExpand;
280 pFunctionListBox->freeze();
281 pFunctionListBox->ClearAll();
283 switch ( pInfo->nKind )
285 case SfxCfgKind::GROUP_ALLFUNCTIONS:
287 css::uno::Reference< css::frame::XDispatchInformationProvider >
288 xProvider( m_xFrame, css::uno::UNO_QUERY );
289 sal_Int32 nEntryCount = m_xControl->get_count();
291 for (sal_Int32 nCurPos = 0; nCurPos < nEntryCount; ++nCurPos)
293 SfxGroupInfo_Impl *pCurrentInfo =
294 reinterpret_cast<SfxGroupInfo_Impl*>(m_xControl->get_id(nCurPos).toInt64());
296 if (!pCurrentInfo) //separator
297 continue;
299 if (pCurrentInfo->nKind == SfxCfgKind::GROUP_FUNCTION)
301 css::uno::Sequence< css::frame::DispatchInformation > lCommands;
304 lCommands = xProvider->getConfigurableDispatchInformation(
305 pCurrentInfo->nUniqueID );
306 FillFunctionsList( lCommands, pFunctionListBox, filterTerm, pCurrentSaveInData );
308 catch( css::container::NoSuchElementException& )
314 break;
316 case SfxCfgKind::GROUP_FUNCTION:
318 sal_uInt16 nGroup = pInfo->nUniqueID;
319 css::uno::Reference< css::frame::XDispatchInformationProvider >
320 xProvider (m_xFrame, css::uno::UNO_QUERY_THROW);
321 css::uno::Sequence< css::frame::DispatchInformation > lCommands =
322 xProvider->getConfigurableDispatchInformation(nGroup);
323 FillFunctionsList( lCommands, pFunctionListBox, filterTerm, pCurrentSaveInData );
324 break;
326 case SfxCfgKind::GROUP_SCRIPTCONTAINER: //Macros
328 SAL_INFO("cui.customize", "** ** About to initialise SF Scripts");
329 // Add Scripting Framework entries
330 css::uno::Reference< css::script::browse::XBrowseNode > rootNode;
333 css::uno::Reference< css::script::browse::XBrowseNodeFactory > xFac
334 = css::script::browse::theBrowseNodeFactory::get( m_xContext );
335 rootNode.set( xFac->createView( css::script::browse::BrowseNodeFactoryViewTypes::MACROSELECTOR ) );
337 catch( css::uno::Exception const & )
339 TOOLS_WARN_EXCEPTION("cui.customize", "Caught some exception whilst retrieving browse nodes from factory");
340 // TODO exception handling
343 if ( rootNode.is() && rootNode->hasChildNodes() )
345 //We call acquire on the XBrowseNode so that it does not
346 //get autodestructed and become invalid when accessed later.
347 rootNode->acquire();
349 m_aGroupInfo.push_back(
350 std::make_unique<SfxGroupInfo_Impl>(
351 SfxCfgKind::GROUP_SCRIPTCONTAINER, 0, static_cast<void *>(rootNode.get()) ) );
353 // Add main macro groups
354 const css::uno::Sequence<css::uno::Reference<css::script::browse::XBrowseNode>> aChildNodes = rootNode->getChildNodes();
355 for ( auto const & childGroup : aChildNodes )
357 OUString sUIName;
358 childGroup->acquire();
360 if ( childGroup->hasChildNodes() )
362 if ( childGroup->getName() == "user" )
364 sUIName = CuiResId( RID_SVXSTR_MYMACROS );
366 else if ( childGroup->getName() == "share" )
368 sUIName = CuiResId( RID_SVXSTR_PRODMACROS );
370 else
372 sUIName = childGroup->getName();
375 if (sUIName.isEmpty())
377 continue;
380 m_aGroupInfo.push_back(
381 std::make_unique<SfxGroupInfo_Impl>(
382 SfxCfgKind::GROUP_SCRIPTCONTAINER, 0 ) );
383 std::unique_ptr<weld::TreeIter> xMacroGroup(pFunctionListBox->tree_append(OUString::number(reinterpret_cast<sal_Int64>(m_aGroupInfo.back().get())), sUIName));
386 // tdf#128010: Do not nag user asking to enable JRE: if it's disabled,
387 // simply don't show relevant entries (user chose to not use JRE)
388 css::uno::ContextLayer layer(
389 comphelper::NoEnableJavaInteractionContext());
390 //Add the children and the grand children
391 addChildren(xMacroGroup.get(), childGroup, pFunctionListBox, filterTerm,
392 pCurrentSaveInData, aNodesToExpand);
395 // Remove the main group if empty
396 if (!pFunctionListBox->iter_has_child(*xMacroGroup))
398 pFunctionListBox->remove(*xMacroGroup);
400 else if (!filterTerm.isEmpty())
402 aNodesToExpand.emplace_back(std::move(xMacroGroup));
408 break;
410 case SfxCfgKind::GROUP_STYLES:
412 const std::vector< SfxStyleInfo_Impl > lStyleFamilies = pStylesInfo->getStyleFamilies();
414 for ( const auto & pIt : lStyleFamilies )
416 if ( pIt.sLabel.isEmpty() )
418 continue;
422 m_aGroupInfo.push_back( std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::GROUP_STYLES, 0 ) );
423 // pIt.sLabel is Name of the style family
424 std::unique_ptr<weld::TreeIter> xFuncEntry(pFunctionListBox->tree_append(OUString::number(reinterpret_cast<sal_Int64>(m_aGroupInfo.back().get())), pIt.sLabel));
426 const std::vector< SfxStyleInfo_Impl > lStyles = pStylesInfo->getStyles(pIt.sFamily);
428 // Setup search filter parameters
429 m_searchOptions.searchString = filterTerm;
430 utl::TextSearch textSearch( m_searchOptions );
432 // Insert children (styles)
433 for ( const auto & pStyleIt : lStyles )
435 OUString sUIName = pStyleIt.sLabel;
436 sal_Int32 aStartPos = 0;
437 sal_Int32 aEndPos = sUIName.getLength();
439 // Apply the search filter
440 if (!filterTerm.isEmpty()
441 && !textSearch.SearchForward( sUIName, &aStartPos, &aEndPos ) )
443 continue;
446 SfxStyleInfo_Impl* pStyle = new SfxStyleInfo_Impl(pStyleIt);
448 m_aGroupInfo.push_back(
449 std::make_unique<SfxGroupInfo_Impl>(
450 SfxCfgKind::GROUP_STYLES, 0, pStyle ) );
452 m_aGroupInfo.back()->sCommand = pStyle->sCommand;
453 m_aGroupInfo.back()->sLabel = pStyle->sLabel;
455 pFunctionListBox->append(OUString::number(reinterpret_cast<sal_Int64>(m_aGroupInfo.back().get())), sUIName, xFuncEntry.get());
458 // Remove the style group from the list if no children
459 if (!pFunctionListBox->iter_has_child(*xFuncEntry))
461 pFunctionListBox->remove(*xFuncEntry);
463 else if (!filterTerm.isEmpty())
465 aNodesToExpand.emplace_back(std::move(xFuncEntry));
469 break;
471 default:
472 // Do nothing, the list box will stay empty
473 SAL_INFO( "cui.customize", "Ignoring unexpected SfxCfgKind: " << static_cast<int>(pInfo->nKind) );
474 break;
477 pFunctionListBox->thaw();
479 if (pFunctionListBox->n_children())
480 pFunctionListBox->select(0);
482 //post freeze
483 for (const auto& it : aNodesToExpand)
484 pFunctionListBox->expand_row(*it);
487 void CommandCategoryListBox::SetStylesInfo(SfxStylesInfo_Impl* pStyles)
489 pStylesInfo = pStyles;
492 void CommandCategoryListBox::addChildren(
493 const weld::TreeIter* parentEntry, const css::uno::Reference< css::script::browse::XBrowseNode > &parentNode,
494 CuiConfigFunctionListBox* pFunctionListBox, const OUString& filterTerm , SaveInData *pCurrentSaveInData,
495 std::vector<std::unique_ptr<weld::TreeIter>> &rNodesToExpand)
497 // Setup search filter parameters
498 m_searchOptions.searchString = filterTerm;
499 utl::TextSearch textSearch( m_searchOptions );
501 const css::uno::Sequence<css::uno::Reference<css::script::browse::XBrowseNode>> aChildNodes = parentNode->getChildNodes();
502 for (auto const & child : aChildNodes)
504 // Acquire to prevent auto-destruction
505 child->acquire();
507 if (child->hasChildNodes())
509 OUString sUIName = child->getName();
511 m_aGroupInfo.push_back( std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::GROUP_SCRIPTCONTAINER,
512 0, static_cast<void *>( child.get())));
513 std::unique_ptr<weld::TreeIter> xNewEntry(pFunctionListBox->tree_append(OUString::number(reinterpret_cast<sal_Int64>(m_aGroupInfo.back().get())), sUIName, parentEntry));
515 addChildren(xNewEntry.get(), child, pFunctionListBox, filterTerm, pCurrentSaveInData, rNodesToExpand);
517 // Remove the group if empty
518 if (!pFunctionListBox->iter_has_child(*xNewEntry))
519 pFunctionListBox->remove(*xNewEntry);
520 else
521 rNodesToExpand.emplace_back(std::move(xNewEntry));
523 else if ( child->getType() == css::script::browse::BrowseNodeTypes::SCRIPT )
525 // Prepare for filtering
526 OUString sUIName = child->getName();
527 sal_Int32 aStartPos = 0;
528 sal_Int32 aEndPos = sUIName.getLength();
530 // Apply the search filter
531 if (!filterTerm.isEmpty()
532 && !textSearch.SearchForward( sUIName, &aStartPos, &aEndPos ) )
534 continue;
537 OUString uri, description;
539 css::uno::Reference < css::beans::XPropertySet >xPropSet( child, css::uno::UNO_QUERY );
541 if (!xPropSet.is())
543 continue;
546 css::uno::Any value = xPropSet->getPropertyValue("URI");
547 value >>= uri;
551 value = xPropSet->getPropertyValue("Description");
552 value >>= description;
554 catch (css::uno::Exception &) {
555 // do nothing, the description will be empty
558 if (description.isEmpty())
560 description = CuiResId( RID_SVXSTR_NOMACRODESC );
563 OUString* pScriptURI = new OUString( uri );
565 css::uno::Reference<css::graphic::XGraphic> xImage;
566 if (pCurrentSaveInData)
567 xImage = pCurrentSaveInData->GetImage(uri);
569 m_aGroupInfo.push_back( std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::FUNCTION_SCRIPT, 0, pScriptURI ));
570 m_aGroupInfo.back()->sCommand = uri;
571 m_aGroupInfo.back()->sLabel = sUIName;
572 m_aGroupInfo.back()->sHelpText = description;
573 pFunctionListBox->append(OUString::number(reinterpret_cast<sal_Int64>(m_aGroupInfo.back().get())), sUIName, xImage, parentEntry);
578 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */