1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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()
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
);
79 else if ( It
->nKind
== SfxCfgKind::FUNCTION_SCRIPT
&& It
->pObject
)
81 OUString
* pScriptURI
= static_cast<OUString
*>(It
->pObject
);
84 else if ( It
->nKind
== SfxCfgKind::GROUP_SCRIPTCONTAINER
&& It
->pObject
)
86 css::uno::XInterface
* xi
= static_cast<css::uno::XInterface
*>(It
->pObject
);
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();
107 m_xContext
= xContext
;
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
;
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
);
155 m_xModuleCategoryInfo
->getByName(sGroupID
) >>= sGroupName
;
156 if (sGroupName
.isEmpty())
159 catch(const css::container::NoSuchElementException
&)
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
&)
199 catch(const css::uno::Exception
&)
202 // Reveal the updated UI to user
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 auto aProperties
= vcl::CommandInfoProvider::GetCommandProperties(rInfo
.Command
, m_sModuleLongName
);
221 OUString sUIName
= getCommandName(rInfo
.Command
);
222 OUString sLabel
= vcl::CommandInfoProvider::GetLabelForCommand(aProperties
);
223 OUString sTooltipLabel
= vcl::CommandInfoProvider::GetTooltipForCommand(rInfo
.Command
, aProperties
, m_xFrame
);
224 OUString sPopupLabel
=
225 (vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties
))
226 .replaceFirst("~", "");
228 // Apply the search filter
229 if (!filterTerm
.isEmpty()
230 && !textSearch
.searchForward( sUIName
)
231 && !textSearch
.searchForward( sLabel
)
232 && !textSearch
.searchForward( sTooltipLabel
)
233 && !textSearch
.searchForward( sPopupLabel
) )
238 css::uno::Reference
<css::graphic::XGraphic
> xImage
;
239 if (pCurrentSaveInData
)
240 xImage
= pCurrentSaveInData
->GetImage(rInfo
.Command
);
242 m_aGroupInfo
.push_back( std::make_unique
<SfxGroupInfo_Impl
>( SfxCfgKind::FUNCTION_SLOT
, 0 ) );
243 SfxGroupInfo_Impl
* pGrpInfo
= m_aGroupInfo
.back().get();
244 pGrpInfo
->sCommand
= rInfo
.Command
;
245 pGrpInfo
->sLabel
= sUIName
;
246 pFunctionListBox
->append(OUString::number(reinterpret_cast<sal_Int64
>(m_aGroupInfo
.back().get())), sUIName
, xImage
);
250 OUString
CommandCategoryListBox::getCommandName(const OUString
& sCommand
)
255 css::uno::Reference
< css::container::XNameAccess
> xModuleConf
;
256 m_xUICmdDescription
->getByName(m_sModuleLongName
) >>= xModuleConf
;
257 if (xModuleConf
.is())
259 ::comphelper::SequenceAsHashMap
lProps(xModuleConf
->getByName(sCommand
));
260 sUIName
= lProps
.getUnpackedValueOrDefault("Name", OUString());
263 catch(const css::uno::RuntimeException
&)
265 catch(css::uno::Exception
&)
268 // fallback for missing UINames !?
269 if (sUIName
.isEmpty())
277 void CommandCategoryListBox::categorySelected(CuiConfigFunctionListBox
* pFunctionListBox
,
278 const OUString
& filterTerm
, SaveInData
*pCurrentSaveInData
)
280 SfxGroupInfo_Impl
*pInfo
= reinterpret_cast<SfxGroupInfo_Impl
*>(m_xControl
->get_active_id().toInt64());
281 std::vector
<std::unique_ptr
<weld::TreeIter
>> aNodesToExpand
;
282 pFunctionListBox
->freeze();
283 pFunctionListBox
->ClearAll();
285 switch ( pInfo
->nKind
)
287 case SfxCfgKind::GROUP_ALLFUNCTIONS
:
289 css::uno::Reference
< css::frame::XDispatchInformationProvider
>
290 xProvider( m_xFrame
, css::uno::UNO_QUERY
);
291 sal_Int32 nEntryCount
= m_xControl
->get_count();
293 for (sal_Int32 nCurPos
= 0; nCurPos
< nEntryCount
; ++nCurPos
)
295 SfxGroupInfo_Impl
*pCurrentInfo
=
296 reinterpret_cast<SfxGroupInfo_Impl
*>(m_xControl
->get_id(nCurPos
).toInt64());
298 if (!pCurrentInfo
) //separator
301 if (pCurrentInfo
->nKind
== SfxCfgKind::GROUP_FUNCTION
)
303 css::uno::Sequence
< css::frame::DispatchInformation
> lCommands
;
306 lCommands
= xProvider
->getConfigurableDispatchInformation(
307 pCurrentInfo
->nUniqueID
);
308 FillFunctionsList( lCommands
, pFunctionListBox
, filterTerm
, pCurrentSaveInData
);
310 catch( css::container::NoSuchElementException
& )
318 case SfxCfgKind::GROUP_FUNCTION
:
320 sal_uInt16 nGroup
= pInfo
->nUniqueID
;
321 css::uno::Reference
< css::frame::XDispatchInformationProvider
>
322 xProvider (m_xFrame
, css::uno::UNO_QUERY_THROW
);
323 css::uno::Sequence
< css::frame::DispatchInformation
> lCommands
=
324 xProvider
->getConfigurableDispatchInformation(nGroup
);
325 FillFunctionsList( lCommands
, pFunctionListBox
, filterTerm
, pCurrentSaveInData
);
328 case SfxCfgKind::GROUP_SCRIPTCONTAINER
: //Macros
330 SAL_INFO("cui.customize", "** ** About to initialise SF Scripts");
331 // Add Scripting Framework entries
332 css::uno::Reference
< css::script::browse::XBrowseNode
> rootNode
;
335 css::uno::Reference
< css::script::browse::XBrowseNodeFactory
> xFac
336 = css::script::browse::theBrowseNodeFactory::get( m_xContext
);
337 rootNode
.set( xFac
->createView( css::script::browse::BrowseNodeFactoryViewTypes::MACROSELECTOR
) );
339 catch( css::uno::Exception
const & )
341 TOOLS_WARN_EXCEPTION("cui.customize", "Caught some exception whilst retrieving browse nodes from factory");
342 // TODO exception handling
345 if ( rootNode
.is() && rootNode
->hasChildNodes() )
347 //We call acquire on the XBrowseNode so that it does not
348 //get autodestructed and become invalid when accessed later.
351 m_aGroupInfo
.push_back(
352 std::make_unique
<SfxGroupInfo_Impl
>(
353 SfxCfgKind::GROUP_SCRIPTCONTAINER
, 0, static_cast<void *>(rootNode
.get()) ) );
355 // Add main macro groups
356 const css::uno::Sequence
<css::uno::Reference
<css::script::browse::XBrowseNode
>> aChildNodes
= rootNode
->getChildNodes();
357 for ( auto const & childGroup
: aChildNodes
)
360 childGroup
->acquire();
362 if ( childGroup
->hasChildNodes() )
364 if ( childGroup
->getName() == "user" )
366 sUIName
= CuiResId( RID_SVXSTR_MYMACROS
);
368 else if ( childGroup
->getName() == "share" )
370 sUIName
= CuiResId( RID_SVXSTR_PRODMACROS
);
374 sUIName
= childGroup
->getName();
377 if (sUIName
.isEmpty())
382 m_aGroupInfo
.push_back(
383 std::make_unique
<SfxGroupInfo_Impl
>(
384 SfxCfgKind::GROUP_SCRIPTCONTAINER
, 0 ) );
385 std::unique_ptr
<weld::TreeIter
> xMacroGroup(pFunctionListBox
->tree_append(OUString::number(reinterpret_cast<sal_Int64
>(m_aGroupInfo
.back().get())), sUIName
));
388 // tdf#128010: Do not nag user asking to enable JRE: if it's disabled,
389 // simply don't show relevant entries (user chose to not use JRE)
390 css::uno::ContextLayer
layer(
391 comphelper::NoEnableJavaInteractionContext());
392 //Add the children and the grand children
393 addChildren(xMacroGroup
.get(), childGroup
, pFunctionListBox
, filterTerm
,
394 pCurrentSaveInData
, aNodesToExpand
);
397 // Remove the main group if empty
398 if (!pFunctionListBox
->iter_has_child(*xMacroGroup
))
400 pFunctionListBox
->remove(*xMacroGroup
);
402 else if (!filterTerm
.isEmpty())
404 aNodesToExpand
.emplace_back(std::move(xMacroGroup
));
412 case SfxCfgKind::GROUP_STYLES
:
414 const std::vector
< SfxStyleInfo_Impl
> lStyleFamilies
= pStylesInfo
->getStyleFamilies();
416 for ( const auto & pIt
: lStyleFamilies
)
418 if ( pIt
.sLabel
.isEmpty() )
424 m_aGroupInfo
.push_back( std::make_unique
<SfxGroupInfo_Impl
>( SfxCfgKind::GROUP_STYLES
, 0 ) );
425 // pIt.sLabel is Name of the style family
426 std::unique_ptr
<weld::TreeIter
> xFuncEntry(pFunctionListBox
->tree_append(OUString::number(reinterpret_cast<sal_Int64
>(m_aGroupInfo
.back().get())), pIt
.sLabel
));
428 const std::vector
< SfxStyleInfo_Impl
> lStyles
= pStylesInfo
->getStyles(pIt
.sFamily
);
430 // Setup search filter parameters
431 m_searchOptions
.searchString
= filterTerm
;
432 utl::TextSearch
textSearch( m_searchOptions
);
434 // Insert children (styles)
435 for ( const auto & pStyleIt
: lStyles
)
437 OUString sUIName
= pStyleIt
.sLabel
;
438 sal_Int32 aStartPos
= 0;
439 sal_Int32 aEndPos
= sUIName
.getLength();
441 // Apply the search filter
442 if (!filterTerm
.isEmpty()
443 && !textSearch
.SearchForward( sUIName
, &aStartPos
, &aEndPos
) )
448 SfxStyleInfo_Impl
* pStyle
= new SfxStyleInfo_Impl(pStyleIt
);
450 m_aGroupInfo
.push_back(
451 std::make_unique
<SfxGroupInfo_Impl
>(
452 SfxCfgKind::GROUP_STYLES
, 0, pStyle
) );
454 m_aGroupInfo
.back()->sCommand
= pStyle
->sCommand
;
455 m_aGroupInfo
.back()->sLabel
= pStyle
->sLabel
;
457 pFunctionListBox
->append(OUString::number(reinterpret_cast<sal_Int64
>(m_aGroupInfo
.back().get())), sUIName
, xFuncEntry
.get());
460 // Remove the style group from the list if no children
461 if (!pFunctionListBox
->iter_has_child(*xFuncEntry
))
463 pFunctionListBox
->remove(*xFuncEntry
);
465 else if (!filterTerm
.isEmpty())
467 aNodesToExpand
.emplace_back(std::move(xFuncEntry
));
474 // Do nothing, the list box will stay empty
475 SAL_INFO( "cui.customize", "Ignoring unexpected SfxCfgKind: " << static_cast<int>(pInfo
->nKind
) );
479 pFunctionListBox
->thaw();
481 if (pFunctionListBox
->n_children())
482 pFunctionListBox
->select(0);
485 for (const auto& it
: aNodesToExpand
)
486 pFunctionListBox
->expand_row(*it
);
489 void CommandCategoryListBox::SetStylesInfo(SfxStylesInfo_Impl
* pStyles
)
491 pStylesInfo
= pStyles
;
494 void CommandCategoryListBox::addChildren(
495 const weld::TreeIter
* parentEntry
, const css::uno::Reference
< css::script::browse::XBrowseNode
> &parentNode
,
496 CuiConfigFunctionListBox
* pFunctionListBox
, const OUString
& filterTerm
, SaveInData
*pCurrentSaveInData
,
497 std::vector
<std::unique_ptr
<weld::TreeIter
>> &rNodesToExpand
)
499 // Setup search filter parameters
500 m_searchOptions
.searchString
= filterTerm
;
501 utl::TextSearch
textSearch( m_searchOptions
);
503 const css::uno::Sequence
<css::uno::Reference
<css::script::browse::XBrowseNode
>> aChildNodes
= parentNode
->getChildNodes();
504 for (auto const & child
: aChildNodes
)
506 // Acquire to prevent auto-destruction
509 if (child
->hasChildNodes())
511 OUString sUIName
= child
->getName();
513 m_aGroupInfo
.push_back( std::make_unique
<SfxGroupInfo_Impl
>(SfxCfgKind::GROUP_SCRIPTCONTAINER
,
514 0, static_cast<void *>( child
.get())));
515 std::unique_ptr
<weld::TreeIter
> xNewEntry(pFunctionListBox
->tree_append(OUString::number(reinterpret_cast<sal_Int64
>(m_aGroupInfo
.back().get())), sUIName
, parentEntry
));
517 addChildren(xNewEntry
.get(), child
, pFunctionListBox
, filterTerm
, pCurrentSaveInData
, rNodesToExpand
);
519 // Remove the group if empty
520 if (!pFunctionListBox
->iter_has_child(*xNewEntry
))
521 pFunctionListBox
->remove(*xNewEntry
);
523 rNodesToExpand
.emplace_back(std::move(xNewEntry
));
525 else if ( child
->getType() == css::script::browse::BrowseNodeTypes::SCRIPT
)
527 // Prepare for filtering
528 OUString sUIName
= child
->getName();
529 sal_Int32 aStartPos
= 0;
530 sal_Int32 aEndPos
= sUIName
.getLength();
532 // Apply the search filter
533 if (!filterTerm
.isEmpty()
534 && !textSearch
.SearchForward( sUIName
, &aStartPos
, &aEndPos
) )
539 OUString uri
, description
;
541 css::uno::Reference
< css::beans::XPropertySet
>xPropSet( child
, css::uno::UNO_QUERY
);
548 css::uno::Any value
= xPropSet
->getPropertyValue("URI");
553 value
= xPropSet
->getPropertyValue("Description");
554 value
>>= description
;
556 catch (css::uno::Exception
&) {
557 // do nothing, the description will be empty
560 if (description
.isEmpty())
562 description
= CuiResId( RID_SVXSTR_NOMACRODESC
);
565 OUString
* pScriptURI
= new OUString( uri
);
567 css::uno::Reference
<css::graphic::XGraphic
> xImage
;
568 if (pCurrentSaveInData
)
569 xImage
= pCurrentSaveInData
->GetImage(uri
);
571 m_aGroupInfo
.push_back( std::make_unique
<SfxGroupInfo_Impl
>( SfxCfgKind::FUNCTION_SCRIPT
, 0, pScriptURI
));
572 m_aGroupInfo
.back()->sCommand
= uri
;
573 m_aGroupInfo
.back()->sLabel
= sUIName
;
574 m_aGroupInfo
.back()->sHelpText
= description
;
575 pFunctionListBox
->append(OUString::number(reinterpret_cast<sal_Int64
>(m_aGroupInfo
.back().get())), sUIName
, xImage
, parentEntry
);
580 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */