xref: /core/cui/source/customize/cfgutil.cxx (revision 33111d13)
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  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <cfgutil.hxx>
21 
22 #include <com/sun/star/beans/XPropertySet.hpp>
23 #include <com/sun/star/container/XEnumerationAccess.hpp>
24 #include <com/sun/star/container/XEnumeration.hpp>
25 #include <com/sun/star/document/XScriptInvocationContext.hpp>
26 #include <com/sun/star/frame/ModuleManager.hpp>
27 #include <com/sun/star/frame/Desktop.hpp>
28 #include <com/sun/star/frame/theUICommandDescription.hpp>
29 #include <com/sun/star/frame/XDispatchInformationProvider.hpp>
30 #include <com/sun/star/script/browse/XBrowseNode.hpp>
31 #include <com/sun/star/script/browse/BrowseNodeTypes.hpp>
32 #include <com/sun/star/script/browse/theBrowseNodeFactory.hpp>
33 #include <com/sun/star/script/browse/BrowseNodeFactoryViewTypes.hpp>
34 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
35 #include <com/sun/star/uno/RuntimeException.hpp>
36 #include <com/sun/star/ui/theUICategoryDescription.hpp>
37 
38 #include <basic/basmgr.hxx>
39 #include <tools/urlobj.hxx>
40 #include <strings.hrc>
41 #include <bitmaps.hlst>
42 #include <sfx2/minfitem.hxx>
43 #include <comphelper/SetFlagContextHelper.hxx>
44 #include <comphelper/documentinfo.hxx>
45 #include <comphelper/lok.hxx>
46 #include <comphelper/processfactory.hxx>
47 #include <comphelper/sequenceashashmap.hxx>
48 #include <svtools/imagemgr.hxx>
49 #include <sal/log.hxx>
50 #include <osl/diagnose.h>
51 #include <dialmgr.hxx>
52 #include <comphelper/diagnose_ex.hxx>
53 #include <vcl/commandevent.hxx>
54 #include <vcl/commandinfoprovider.hxx>
55 #include <vcl/help.hxx>
56 #include <vcl/svapp.hxx>
57 #include <o3tl/string_view.hxx>
58 
59 #include <sfx2/sidebar/ResourceManager.hxx>
60 #include <sfx2/sidebar/Context.hxx>
61 #include <unotools/viewoptions.hxx>
62 
63 using namespace ::com::sun::star;
64 using namespace ::com::sun::star::uno;
65 using namespace ::com::sun::star::script;
66 using namespace ::com::sun::star::frame;
67 using namespace ::com::sun::star::document;
68 
SfxStylesInfo_Impl()69 SfxStylesInfo_Impl::SfxStylesInfo_Impl()
70 {}
71 
init(const OUString & rModuleName,const css::uno::Reference<css::frame::XModel> & xModel)72 void SfxStylesInfo_Impl::init(const OUString& rModuleName, const css::uno::Reference< css::frame::XModel >& xModel)
73 {
74     m_aModuleName = rModuleName;
75     m_xDoc = xModel;
76 }
77 
78 const char CMDURL_STYLEPROT_ONLY[] = ".uno:StyleApply?";
79 const char CMDURL_SPART_ONLY    [] = "Style:string=";
80 const char CMDURL_FPART_ONLY    [] = "FamilyName:string=";
81 
82 constexpr OUString STYLEPROP_UINAME = u"DisplayName"_ustr;
83 constexpr OUString MACRO_SELECTOR_CONFIGNAME = u"MacroSelectorDialog"_ustr;
84 constexpr OUString LAST_RUN_MACRO_INFO = u"LastRunMacro"_ustr;
85 
generateCommand(std::u16string_view sFamily,std::u16string_view sStyle)86 OUString SfxStylesInfo_Impl::generateCommand(
87     std::u16string_view sFamily, std::u16string_view sStyle)
88 {
89     return OUString::Concat(".uno:StyleApply?Style:string=")
90            + sStyle
91            + "&FamilyName:string="
92            + sFamily;
93 }
94 
parseStyleCommand(SfxStyleInfo_Impl & aStyle)95 bool SfxStylesInfo_Impl::parseStyleCommand(SfxStyleInfo_Impl& aStyle)
96 {
97     static const sal_Int32 LEN_STYLEPROT = strlen(CMDURL_STYLEPROT_ONLY);
98     static const sal_Int32 LEN_SPART     = strlen(CMDURL_SPART_ONLY);
99     static const sal_Int32 LEN_FPART     = strlen(CMDURL_FPART_ONLY);
100 
101     if (!aStyle.sCommand.startsWith(CMDURL_STYLEPROT_ONLY))
102         return false;
103 
104     aStyle.sFamily.clear();
105     aStyle.sStyle.clear();
106 
107     sal_Int32       nCmdLen  = aStyle.sCommand.getLength();
108     OUString sCmdArgs = aStyle.sCommand.copy(LEN_STYLEPROT, nCmdLen-LEN_STYLEPROT);
109     sal_Int32       i        = sCmdArgs.indexOf('&');
110     if (i<0)
111         return false;
112 
113     OUString sArg = sCmdArgs.copy(0, i);
114     if (sArg.startsWith(CMDURL_SPART_ONLY))
115         aStyle.sStyle = sArg.copy(LEN_SPART);
116     else if (sArg.startsWith(CMDURL_FPART_ONLY))
117         aStyle.sFamily = sArg.copy(LEN_FPART);
118 
119     sArg = sCmdArgs.copy(i+1, sCmdArgs.getLength()-i-1);
120     if (sArg.startsWith(CMDURL_SPART_ONLY))
121         aStyle.sStyle = sArg.copy(LEN_SPART);
122     else if (sArg.startsWith(CMDURL_FPART_ONLY))
123         aStyle.sFamily = sArg.copy(LEN_FPART);
124 
125     return !(aStyle.sFamily.isEmpty() || aStyle.sStyle.isEmpty());
126 }
127 
getLabel4Style(SfxStyleInfo_Impl & aStyle)128 void SfxStylesInfo_Impl::getLabel4Style(SfxStyleInfo_Impl& aStyle)
129 {
130     try
131     {
132         css::uno::Reference< css::style::XStyleFamiliesSupplier > xModel(m_xDoc, css::uno::UNO_QUERY);
133 
134         css::uno::Reference< css::container::XNameAccess > xFamilies;
135         if (xModel.is())
136             xFamilies = xModel->getStyleFamilies();
137 
138         css::uno::Reference< css::container::XNameAccess > xStyleSet;
139         if (xFamilies.is())
140             xFamilies->getByName(aStyle.sFamily) >>= xStyleSet;
141 
142         css::uno::Reference< css::beans::XPropertySet > xStyle;
143         if (xStyleSet.is())
144             xStyleSet->getByName(aStyle.sStyle) >>= xStyle;
145 
146         aStyle.sLabel.clear();
147         if (xStyle.is())
148             xStyle->getPropertyValue(STYLEPROP_UINAME) >>= aStyle.sLabel;
149     }
150     catch(const css::uno::RuntimeException&)
151         { throw; }
152     catch(const css::uno::Exception&)
153         { aStyle.sLabel.clear(); }
154 
155     if (aStyle.sLabel.isEmpty())
156     {
157         aStyle.sLabel = aStyle.sCommand;
158     }
159 }
160 
getStyleFamilies() const161 std::vector< SfxStyleInfo_Impl > SfxStylesInfo_Impl::getStyleFamilies() const
162 {
163     // It's an optional interface!
164     css::uno::Reference< css::style::XStyleFamiliesSupplier > xModel(m_xDoc, css::uno::UNO_QUERY);
165     if (!xModel.is())
166         return std::vector< SfxStyleInfo_Impl >();
167 
168     css::uno::Reference< css::container::XNameAccess > xCont = xModel->getStyleFamilies();
169     const css::uno::Sequence< OUString > lFamilyNames = xCont->getElementNames();
170     std::vector< SfxStyleInfo_Impl > lFamilies;
171     for (const auto& aFamily : lFamilyNames)
172     {
173         if ((aFamily == "CellStyles" && m_aModuleName != "com.sun.star.sheet.SpreadsheetDocument") ||
174              aFamily == "cell" || aFamily == "table" || aFamily == "Default")
175             continue;
176 
177         SfxStyleInfo_Impl aFamilyInfo;
178         aFamilyInfo.sFamily = aFamily;
179 
180         try
181         {
182             css::uno::Reference< css::beans::XPropertySet > xFamilyInfo;
183             xCont->getByName(aFamilyInfo.sFamily) >>= xFamilyInfo;
184             if (!xFamilyInfo.is())
185             {
186                 // TODO_AS currently there is no support for an UIName property .. use internal family name instead
187                 aFamilyInfo.sLabel = aFamilyInfo.sFamily;
188             }
189             else
190                 xFamilyInfo->getPropertyValue(STYLEPROP_UINAME) >>= aFamilyInfo.sLabel;
191         }
192         catch(const css::uno::RuntimeException&)
193             { throw; }
194         catch(const css::uno::Exception&)
195             { return std::vector< SfxStyleInfo_Impl >(); }
196 
197         lFamilies.push_back(aFamilyInfo);
198     }
199 
200     return lFamilies;
201 }
202 
getStyles(const OUString & sFamily)203 std::vector< SfxStyleInfo_Impl > SfxStylesInfo_Impl::getStyles(const OUString& sFamily)
204 {
205     css::uno::Sequence< OUString > lStyleNames;
206     css::uno::Reference< css::style::XStyleFamiliesSupplier > xModel(m_xDoc, css::uno::UNO_QUERY_THROW);
207     css::uno::Reference< css::container::XNameAccess > xFamilies = xModel->getStyleFamilies();
208     css::uno::Reference< css::container::XNameAccess > xStyleSet;
209     try
210     {
211         xFamilies->getByName(sFamily) >>= xStyleSet;
212         lStyleNames = xStyleSet->getElementNames();
213     }
214     catch(const css::uno::RuntimeException&)
215         { throw; }
216     catch(const css::uno::Exception&)
217         { return std::vector< SfxStyleInfo_Impl >(); }
218 
219     std::vector< SfxStyleInfo_Impl > lStyles;
220     sal_Int32                          c      = lStyleNames.getLength();
221     sal_Int32                          i      = 0;
222     for (i=0; i<c; ++i)
223     {
224         SfxStyleInfo_Impl aStyleInfo;
225         aStyleInfo.sFamily  = sFamily;
226         aStyleInfo.sStyle   = lStyleNames[i];
227         aStyleInfo.sCommand = SfxStylesInfo_Impl::generateCommand(aStyleInfo.sFamily, aStyleInfo.sStyle);
228 
229         try
230         {
231             css::uno::Reference< css::beans::XPropertySet > xStyle;
232             xStyleSet->getByName(aStyleInfo.sStyle) >>= xStyle;
233             if (!xStyle.is())
234                 continue;
235             xStyle->getPropertyValue(u"DisplayName"_ustr) >>= aStyleInfo.sLabel;
236         }
237         catch(const css::uno::RuntimeException&)
238             { throw; }
239         catch(const css::uno::Exception&)
240             { continue; }
241 
242         lStyles.push_back(aStyleInfo);
243     }
244     return lStyles;
245 }
246 
GetCommandHelpText()247 OUString CuiConfigFunctionListBox::GetCommandHelpText()
248 {
249     SfxGroupInfo_Impl *pData = weld::fromId<SfxGroupInfo_Impl*>(get_selected_id());
250     if (pData)
251     {
252         if ( pData->nKind == SfxCfgKind::FUNCTION_SLOT )
253         {
254             return Application::GetHelp()->GetHelpText(pData->sCommand);
255         }
256         else if ( pData->nKind == SfxCfgKind::FUNCTION_SCRIPT )
257         {
258             return pData->sHelpText;
259         }
260     }
261     return OUString();
262 }
263 
GetCurCommand() const264 OUString CuiConfigFunctionListBox::GetCurCommand() const
265 {
266     SfxGroupInfo_Impl *pData = weld::fromId<SfxGroupInfo_Impl*>(get_selected_id());
267     if (!pData)
268         return OUString();
269     return pData->sCommand;
270 }
271 
GetCurLabel() const272 OUString CuiConfigFunctionListBox::GetCurLabel() const
273 {
274     SfxGroupInfo_Impl *pData = weld::fromId<SfxGroupInfo_Impl*>(get_selected_id());
275     if (!pData)
276         return OUString();
277     if (!pData->sLabel.isEmpty())
278         return pData->sLabel;
279     return pData->sCommand;
280 }
281 
CuiConfigFunctionListBox(std::unique_ptr<weld::TreeView> xTreeView)282 CuiConfigFunctionListBox::CuiConfigFunctionListBox(std::unique_ptr<weld::TreeView> xTreeView)
283     : m_xTreeView(std::move(xTreeView))
284     , m_xScratchIter(m_xTreeView->make_iterator())
285 {
286     m_xTreeView->make_sorted();
287     m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 35, m_xTreeView->get_height_rows(9));
288     m_xTreeView->connect_query_tooltip(LINK(this, CuiConfigFunctionListBox, QueryTooltip));
289 }
290 
~CuiConfigFunctionListBox()291 CuiConfigFunctionListBox::~CuiConfigFunctionListBox()
292 {
293     ClearAll();
294 }
295 
IMPL_LINK(CuiConfigFunctionListBox,QueryTooltip,const weld::TreeIter &,rIter,OUString)296 IMPL_LINK(CuiConfigFunctionListBox, QueryTooltip, const weld::TreeIter&, rIter, OUString)
297 {
298     SfxGroupInfo_Impl *pData = weld::fromId<SfxGroupInfo_Impl*>(m_xTreeView->get_id(rIter));
299     if (!pData)
300         return OUString();
301     OUString aLabel = CuiResId(RID_CUISTR_COMMANDLABEL) + ": ";
302     OUString aName = CuiResId(RID_CUISTR_COMMANDNAME) + ": ";
303     OUString aTip = CuiResId(RID_CUISTR_COMMANDTIP) + ": ";
304     return  aLabel + pData->sLabel + "\n" + aName + pData->sCommand+ "\n" + aTip + pData->sTooltip;
305 }
306 
ClearAll()307 void CuiConfigFunctionListBox::ClearAll()
308 /*  Description
309     Deletes all entries in the FunctionListBox, all UserData and all
310     possibly existing MacroInfo.
311 */
312 {
313     sal_uInt16 nCount = aArr.size();
314     for ( sal_uInt16 i=0; i<nCount; ++i )
315     {
316         SfxGroupInfo_Impl *pData = aArr[i].get();
317 
318         if ( pData->nKind == SfxCfgKind::FUNCTION_SCRIPT )
319         {
320             OUString* pScriptURI = static_cast<OUString*>(pData->pObject);
321             delete pScriptURI;
322         }
323 
324         if ( pData->nKind == SfxCfgKind::GROUP_SCRIPTCONTAINER )
325         {
326             XInterface* xi = static_cast<XInterface *>(pData->pObject);
327             if (xi != nullptr)
328             {
329                 xi->release();
330             }
331         }
332     }
333 
334     aArr.clear();
335     m_xTreeView->clear();
336 }
337 
GetSelectedScriptURI() const338 OUString CuiConfigFunctionListBox::GetSelectedScriptURI() const
339 {
340     SfxGroupInfo_Impl *pData = weld::fromId<SfxGroupInfo_Impl*>(get_selected_id());
341     if (pData && pData->nKind == SfxCfgKind::FUNCTION_SCRIPT)
342         return *static_cast<OUString*>(pData->pObject);
343     return OUString();
344 }
345 
346 struct SvxConfigGroupBoxResource_Impl
347 {
348     OUString m_sMyMacros;
349     OUString m_sProdMacros;
350     OUString m_sDlgMacros;
351     OUString m_aStrGroupStyles;
352     OUString m_aStrGroupSidebarDecks;
353 
354     SvxConfigGroupBoxResource_Impl();
355 };
356 
SvxConfigGroupBoxResource_Impl()357 SvxConfigGroupBoxResource_Impl::SvxConfigGroupBoxResource_Impl() :
358     m_sMyMacros(CuiResId(RID_CUISTR_MYMACROS)),
359     m_sProdMacros(CuiResId(RID_CUISTR_PRODMACROS)),
360     m_sDlgMacros(CuiResId(RID_CUISTR_PRODMACROS)),
361     m_aStrGroupStyles(CuiResId(RID_CUISTR_GROUP_STYLES)),
362     m_aStrGroupSidebarDecks(CuiResId(RID_CUISTR_GROUP_SIDEBARDECKS))
363 {
364 }
365 
SetStylesInfo(SfxStylesInfo_Impl * pStyles)366 void CuiConfigGroupListBox::SetStylesInfo(SfxStylesInfo_Impl* pStyles)
367 {
368     m_pStylesInfo = pStyles;
369 }
370 
371 namespace
372 {
373 
374     /** examines a component whether it supports XEmbeddedScripts, or provides access to such a
375         component by implementing XScriptInvocationContext.
376         @return
377             the model which supports the embedded scripts, or <NULL/> if it cannot find such a
378             model
379     */
lcl_getDocumentWithScripts_throw(const Reference<XInterface> & _rxComponent)380     Reference< XModel > lcl_getDocumentWithScripts_throw( const Reference< XInterface >& _rxComponent )
381     {
382         Reference< XEmbeddedScripts > xScripts( _rxComponent, UNO_QUERY );
383         if ( !xScripts.is() )
384         {
385             Reference< XScriptInvocationContext > xContext( _rxComponent, UNO_QUERY );
386             if ( xContext.is() )
387                 xScripts = xContext->getScriptContainer();
388         }
389 
390         return Reference< XModel >( xScripts, UNO_QUERY );
391     }
392 
393 
lcl_getScriptableDocument_nothrow(const Reference<XFrame> & _rxFrame)394     Reference< XModel > lcl_getScriptableDocument_nothrow( const Reference< XFrame >& _rxFrame )
395     {
396         Reference< XModel > xDocument;
397 
398         // examine our associated frame
399         try
400         {
401             OSL_ENSURE( _rxFrame.is(), "lcl_getScriptableDocument_nothrow: you need to pass a frame to this dialog/tab page!" );
402             if ( _rxFrame.is() )
403             {
404                 // first try the model in the frame
405                 Reference< XController > xController( _rxFrame->getController(), UNO_SET_THROW );
406                 xDocument = lcl_getDocumentWithScripts_throw( xController->getModel() );
407 
408                 if ( !xDocument.is() )
409                 {
410                     // if there is no suitable document in the frame, try the controller
411                     xDocument = lcl_getDocumentWithScripts_throw( _rxFrame->getController() );
412                 }
413             }
414         }
415         catch( const Exception& )
416         {
417         }
418 
419         return xDocument;
420     }
421 }
422 
CuiConfigGroupListBox(std::unique_ptr<weld::TreeView> xTreeView)423 CuiConfigGroupListBox::CuiConfigGroupListBox(std::unique_ptr<weld::TreeView> xTreeView)
424     : xImp(new SvxConfigGroupBoxResource_Impl())
425     , m_pFunctionListBox(nullptr)
426     , m_pStylesInfo(nullptr)
427     , m_xTreeView(std::move(xTreeView))
428     , m_xScratchIter(m_xTreeView->make_iterator())
429 {
430     m_xTreeView->connect_expanding(LINK(this, CuiConfigGroupListBox, ExpandingHdl));
431     m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 35, m_xTreeView->get_height_rows(9));
432 }
433 
~CuiConfigGroupListBox()434 CuiConfigGroupListBox::~CuiConfigGroupListBox()
435 {
436     ClearAll();
437 }
438 
ClearAll()439 void CuiConfigGroupListBox::ClearAll()
440 {
441     sal_uInt16 nCount = aArr.size();
442     for ( sal_uInt16 i=0; i<nCount; ++i )
443     {
444         SfxGroupInfo_Impl *pData = aArr[i].get();
445         if (pData->nKind == SfxCfgKind::GROUP_STYLES && pData->pObject)
446         {
447             SfxStyleInfo_Impl* pStyle = static_cast<SfxStyleInfo_Impl*>(pData->pObject);
448             delete pStyle;
449         }
450         else if (pData->nKind == SfxCfgKind::FUNCTION_SCRIPT && pData->pObject )
451         {
452             OUString* pScriptURI = static_cast<OUString*>(pData->pObject);
453             delete pScriptURI;
454         }
455         else if (pData->nKind == SfxCfgKind::GROUP_SCRIPTCONTAINER)
456         {
457             XInterface* xi = static_cast<XInterface *>(pData->pObject);
458             if (xi != nullptr)
459             {
460                 xi->release();
461             }
462         }
463     }
464 
465     aArr.clear();
466     m_xTreeView->clear();
467 }
468 
InitModule()469 sal_Int32 CuiConfigGroupListBox::InitModule()
470 {
471     try
472     {
473         // return the number of added groups
474         css::uno::Reference< css::frame::XDispatchInformationProvider > xProvider(m_xFrame, css::uno::UNO_QUERY_THROW);
475         css::uno::Sequence< sal_Int16 > lGroups = xProvider->getSupportedCommandGroups();
476         sal_Int32                       c1      = lGroups.getLength();
477         sal_Int32                       i1      = 0;
478         sal_Int32                       nAddedGroups = 0;
479 
480         for (i1=0; i1<c1; ++i1)
481         {
482             sal_Int16      nGroupID   = lGroups[i1];
483             OUString sGroupID   = OUString::number(nGroupID);
484             OUString sGroupName ;
485 
486             try
487             {
488                 m_xModuleCategoryInfo->getByName(sGroupID) >>= sGroupName;
489                 if (sGroupName.isEmpty())
490                     continue;
491             }
492             catch(const css::container::NoSuchElementException&)
493                 { continue; }
494 
495             aArr.push_back( std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::GROUP_FUNCTION, nGroupID ) );
496             m_xTreeView->append(weld::toId(aArr.back().get()), sGroupName);
497             nAddedGroups++;
498         }
499         return nAddedGroups;
500     }
501     catch(const css::uno::RuntimeException&)
502         { throw; }
503     catch(const css::uno::Exception&)
504         {}
505     return 0;
506 }
507 
FillScriptList(const css::uno::Reference<css::script::browse::XBrowseNode> & xRootNode,const weld::TreeIter * pParentEntry)508 void CuiConfigGroupListBox::FillScriptList(const css::uno::Reference< css::script::browse::XBrowseNode >& xRootNode,
509                                            const weld::TreeIter* pParentEntry)
510 {
511     try {
512         if ( xRootNode->hasChildNodes() )
513         {
514             // tdf#120362: Don't ask to enable disabled Java when filling script list
515             css::uno::ContextLayer layer(comphelper::NoEnableJavaInteractionContext());
516 
517             const Sequence< Reference< browse::XBrowseNode > > children =
518                 xRootNode->getChildNodes();
519             bool bIsRootNode = false;
520 
521             OUString user(u"user"_ustr);
522             OUString share(u"share"_ustr);
523             if ( xRootNode->getName() == "Root" )
524             {
525                 bIsRootNode = true;
526             }
527 
528             //To mimic current starbasic behaviour we
529             //need to make sure that only the current document
530             //is displayed in the config tree. Tests below
531             //set the bDisplay flag to FALSE if the current
532             //node is a first level child of the Root and is NOT
533             //either the current document, user or share
534             OUString currentDocTitle;
535             Reference< XModel > xDocument( lcl_getScriptableDocument_nothrow( m_xFrame ) );
536             if ( xDocument.is() )
537             {
538                 currentDocTitle = ::comphelper::DocumentInfo::getDocumentTitle( xDocument );
539             }
540 
541             for ( Reference< browse::XBrowseNode > const & theChild : children )
542             {
543                 bool bDisplay = true;
544                 OUString uiName = theChild->getName();
545                 if ( bIsRootNode )
546                 {
547                     if (  ! (uiName == user  || uiName == share ||
548                              uiName == currentDocTitle ) )
549                     {
550                         bDisplay=false;
551                     }
552                     else
553                     {
554                         if ( uiName == user )
555                         {
556                             uiName = xImp->m_sMyMacros;
557                         }
558                         else if ( uiName == share )
559                         {
560                             uiName = xImp->m_sProdMacros;
561                         }
562                     }
563                 }
564                 if (theChild->getType() != browse::BrowseNodeTypes::SCRIPT  && bDisplay )
565                 {
566 //                  We call acquire on the XBrowseNode so that it does not
567 //                  get autodestructed and become invalid when accessed later.
568                     theChild->acquire();
569 
570                     bool bChildOnDemand = false;
571 
572                     if ( theChild->hasChildNodes() )
573                     {
574                         const Sequence< Reference< browse::XBrowseNode > > grandchildren =
575                             theChild->getChildNodes();
576 
577                         for ( const auto& rxNode : grandchildren )
578                         {
579                             if ( rxNode->getType() == browse::BrowseNodeTypes::CONTAINER )
580                             {
581                                 bChildOnDemand = true;
582                                 break;
583                             }
584                         }
585                     }
586 
587                     OUString aImage = GetImage(theChild, m_xContext, bIsRootNode);
588 
589                     aArr.push_back( std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::GROUP_SCRIPTCONTAINER,
590                             0, static_cast<void *>( theChild.get())));
591 
592                     OUString sId(weld::toId(aArr.back().get()));
593                     m_xTreeView->insert(pParentEntry, -1, &uiName, &sId, nullptr, nullptr, bChildOnDemand, m_xScratchIter.get());
594                     m_xTreeView->set_image(*m_xScratchIter, aImage);
595                 }
596             }
597         }
598     }
599     catch (RuntimeException&) {
600         // do nothing, the entry will not be displayed in the UI
601     }
602 }
603 
FillFunctionsList(const css::uno::Sequence<DispatchInformation> & xCommands)604 void CuiConfigGroupListBox::FillFunctionsList(const css::uno::Sequence<DispatchInformation>& xCommands)
605 {
606     m_pFunctionListBox->freeze();
607     for (const auto & rInfo : xCommands)
608     {
609         auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rInfo.Command, m_sModuleLongName);
610 
611         OUString sUIName = MapCommand2UIName(rInfo.Command);
612         aArr.push_back( std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::FUNCTION_SLOT, 0 ) );
613         SfxGroupInfo_Impl* pGrpInfo = aArr.back().get();
614         pGrpInfo->sCommand = rInfo.Command;
615         pGrpInfo->sLabel   = sUIName;
616         pGrpInfo->sTooltip = vcl::CommandInfoProvider::GetTooltipForCommand(rInfo.Command, aProperties, m_xFrame);
617         m_pFunctionListBox->append(weld::toId(pGrpInfo), sUIName);
618     }
619     m_pFunctionListBox->thaw();
620 }
621 
Init(const css::uno::Reference<css::uno::XComponentContext> & xContext,const css::uno::Reference<css::frame::XFrame> & xFrame,const OUString & sModuleLongName,bool bEventMode)622 void CuiConfigGroupListBox::Init(const css::uno::Reference< css::uno::XComponentContext >& xContext,
623     const css::uno::Reference< css::frame::XFrame >& xFrame,
624     const OUString& sModuleLongName,
625     bool bEventMode)
626 {
627     m_xTreeView->freeze();
628     ClearAll(); // Remove all old entries from treelist box
629 
630     m_xContext = xContext;
631     m_xFrame = xFrame;
632     sal_Int32 nAddedGroups = 0;
633     if( bEventMode )
634     {
635         m_sModuleLongName = sModuleLongName;
636         m_xGlobalCategoryInfo = css::ui::theUICategoryDescription::get( m_xContext );
637         m_xModuleCategoryInfo.set(m_xGlobalCategoryInfo->getByName(m_sModuleLongName), css::uno::UNO_QUERY_THROW);
638         m_xUICmdDescription   = css::frame::theUICommandDescription::get( m_xContext );
639 
640         nAddedGroups = InitModule();
641     }
642 
643     SAL_INFO("cui.customize", "** ** About to initialise SF Scripts");
644     // Add Scripting Framework entries
645     Reference< browse::XBrowseNode > rootNode;
646     try
647     {
648         Reference< browse::XBrowseNodeFactory > xFac = browse::theBrowseNodeFactory::get( m_xContext );
649         rootNode.set( xFac->createView( browse::BrowseNodeFactoryViewTypes::MACROSELECTOR ) );
650     }
651     catch( const Exception& )
652     {
653         TOOLS_WARN_EXCEPTION("cui.customize", "Caught some exception whilst retrieving browse nodes from factory");
654         // TODO exception handling
655     }
656 
657     m_xTreeView->thaw();
658     m_xTreeView->make_sorted();
659     m_xTreeView->make_unsorted();
660     m_xTreeView->freeze();
661 
662     // add All Commands to the top
663     if ( bEventMode && nAddedGroups )
664     {
665         aArr.insert(aArr.begin(), std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::GROUP_ALLFUNCTIONS, 0));
666         OUString sId(weld::toId(aArr.front().get()));
667         OUString s(CuiResId(RID_CUISTR_ALLFUNCTIONS));
668         m_xTreeView->insert(nullptr, 0, &s, &sId, nullptr, nullptr, false, nullptr);
669     }
670 
671     // add application macros to the end
672     if ( rootNode.is() )
673     {
674         if ( bEventMode )
675         {
676                 //We call acquire on the XBrowseNode so that it does not
677                 //get autodestructed and become invalid when accessed later.
678             rootNode->acquire();
679 
680             aArr.push_back( std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::GROUP_SCRIPTCONTAINER, 0,
681                     static_cast<void *>(rootNode.get())));
682             OUString aTitle(xImp->m_sDlgMacros);
683             OUString sId(weld::toId(aArr.back().get()));
684             m_xTreeView->insert(nullptr, -1, &aTitle, &sId, nullptr, nullptr, true, nullptr);
685         }
686         else
687         {
688              //We are only showing scripts not slot APIs so skip
689              //Root node and show location nodes
690             FillScriptList(rootNode, nullptr);
691         }
692     }
693 
694     // add styles and sidebar decks to the end
695     if ( bEventMode )
696     {
697         aArr.push_back( std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::GROUP_STYLES, 0, nullptr ) ); // TODO last parameter should contain user data
698         OUString sStyle(xImp->m_aStrGroupStyles);
699         OUString sId(weld::toId(aArr.back().get()));
700         m_xTreeView->insert(nullptr, -1, &sStyle, &sId, nullptr, nullptr, true, nullptr);
701 
702         aArr.push_back( std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::GROUP_SIDEBARDECKS, 0));
703         OUString sSidebarDecks(xImp->m_aStrGroupSidebarDecks);
704         sId = weld::toId(aArr.back().get());
705         m_xTreeView->insert(nullptr, -1, &sSidebarDecks, &sId, nullptr, nullptr, false, nullptr);
706     }
707 
708     m_xTreeView->thaw();
709     m_xTreeView->scroll_to_row(0);
710     m_xTreeView->select(0);
711 }
712 
GetImage(const Reference<browse::XBrowseNode> & node,Reference<XComponentContext> const & xCtx,bool bIsRootNode)713 OUString CuiConfigGroupListBox::GetImage(
714     const Reference< browse::XBrowseNode >& node,
715     Reference< XComponentContext > const & xCtx,
716     bool bIsRootNode)
717 {
718     OUString aImage;
719     if ( bIsRootNode )
720     {
721         if (node->getName() == "user" || node->getName() == "share" )
722         {
723             aImage = RID_CUIBMP_HARDDISK;
724         }
725         else
726         {
727             OUString factoryURL;
728             OUString nodeName = node->getName();
729             Reference<XInterface> xDocumentModel = getDocumentModel(xCtx, nodeName );
730             if ( xDocumentModel.is() )
731             {
732                 Reference< frame::XModuleManager2 > xModuleManager( frame::ModuleManager::create(xCtx) );
733                 // get the long name of the document:
734                 OUString appModule( xModuleManager->identify(
735                                     xDocumentModel ) );
736                 Sequence<beans::PropertyValue> moduleDescr;
737                 Any aAny = xModuleManager->getByName(appModule);
738                 if( !( aAny >>= moduleDescr ) )
739                 {
740                     throw RuntimeException(u"SFTreeListBox::Init: failed to get PropertyValue"_ustr);
741                 }
742                 for ( sal_Int32 pos = moduleDescr.getLength(); pos--; )
743                 {
744                     if (moduleDescr[pos].Name == "ooSetupFactoryEmptyDocumentURL")
745                     {
746                         moduleDescr[pos].Value >>= factoryURL;
747                         SAL_INFO("cui.customize", "factory url for doc images is " << factoryURL);
748                         break;
749                     }
750                 }
751             }
752             if( !factoryURL.isEmpty() )
753             {
754                 aImage = SvFileInformationManager::GetFileImageId(INetURLObject(factoryURL));
755             }
756             else
757             {
758                 aImage = RID_CUIBMP_DOC;
759             }
760         }
761     }
762     else
763     {
764         if( node->getType() == browse::BrowseNodeTypes::SCRIPT )
765             aImage = RID_CUIBMP_MACRO;
766         else
767             aImage = RID_CUIBMP_LIB;
768     }
769     return aImage;
770 }
771 
772 Reference< XInterface  >
getDocumentModel(Reference<XComponentContext> const & xCtx,std::u16string_view docName)773 CuiConfigGroupListBox::getDocumentModel( Reference< XComponentContext > const & xCtx, std::u16string_view docName )
774 {
775     Reference< XInterface > xModel;
776     Reference< frame::XDesktop2 > desktop = frame::Desktop::create( xCtx );
777 
778     Reference< container::XEnumerationAccess > componentsAccess =
779         desktop->getComponents();
780     Reference< container::XEnumeration > components =
781         componentsAccess->createEnumeration();
782     while (components->hasMoreElements())
783     {
784         Reference< frame::XModel > model(
785             components->nextElement(), UNO_QUERY );
786         if ( model.is() )
787         {
788             OUString sTdocUrl =
789                 ::comphelper::DocumentInfo::getDocumentTitle( model );
790             if( sTdocUrl == docName )
791             {
792                 xModel = model;
793                 break;
794             }
795         }
796     }
797     return xModel;
798 }
799 
MapCommand2UIName(const OUString & sCommand)800 OUString CuiConfigGroupListBox::MapCommand2UIName(const OUString& sCommand)
801 {
802     OUString sUIName;
803     try
804     {
805         css::uno::Reference< css::container::XNameAccess > xModuleConf;
806         m_xUICmdDescription->getByName(m_sModuleLongName) >>= xModuleConf;
807         if (xModuleConf.is())
808         {
809             ::comphelper::SequenceAsHashMap lProps(xModuleConf->getByName(sCommand));
810             sUIName = lProps.getUnpackedValueOrDefault(u"Name"_ustr, OUString());
811         }
812     }
813     catch(const css::uno::RuntimeException&)
814         { throw; }
815     catch(css::uno::Exception&)
816         { sUIName.clear(); }
817 
818     // fallback for missing UINames !?
819     if (sUIName.isEmpty())
820     {
821         sUIName = sCommand;
822     }
823 
824     return sUIName;
825 }
826 
GroupSelected()827 void CuiConfigGroupListBox::GroupSelected()
828 /*  Description
829     A function group or a basic module has been selected.
830     All functions/macros are displayed in the functionlistbox.
831 */
832 {
833     std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator());
834     if (!m_xTreeView->get_selected(xIter.get()))
835         return;
836 
837     SfxGroupInfo_Impl *pInfo = weld::fromId<SfxGroupInfo_Impl*>(m_xTreeView->get_id(*xIter));
838     m_pFunctionListBox->freeze();
839     m_pFunctionListBox->ClearAll();
840 
841     switch ( pInfo->nKind )
842     {
843         case SfxCfgKind::GROUP_ALLFUNCTIONS:
844         {
845             css::uno::Reference< css::frame::XDispatchInformationProvider > xProvider( m_xFrame, UNO_QUERY );
846             bool bValidIter = m_xTreeView->get_iter_first(*xIter);
847             while (bValidIter)
848             {
849                 SfxGroupInfo_Impl *pCurrentInfo = weld::fromId<SfxGroupInfo_Impl*>(m_xTreeView->get_id(*xIter));
850                 if (pCurrentInfo->nKind == SfxCfgKind::GROUP_FUNCTION)
851                 {
852                     css::uno::Sequence< css::frame::DispatchInformation > lCommands;
853                     try
854                     {
855                         lCommands = xProvider->getConfigurableDispatchInformation( pCurrentInfo->nUniqueID );
856                         FillFunctionsList( lCommands );
857                     }
858                     catch ( container::NoSuchElementException& )
859                     {
860                     }
861                 }
862                 bValidIter = m_xTreeView->iter_next(*xIter);
863             }
864             break;
865         }
866 
867         case SfxCfgKind::GROUP_FUNCTION :
868         {
869             sal_uInt16                                                          nGroup    = pInfo->nUniqueID;
870             css::uno::Reference< css::frame::XDispatchInformationProvider > xProvider (m_xFrame, css::uno::UNO_QUERY_THROW);
871             css::uno::Sequence< css::frame::DispatchInformation >           lCommands = xProvider->getConfigurableDispatchInformation(nGroup);
872             FillFunctionsList( lCommands );
873             break;
874         }
875 
876         case SfxCfgKind::GROUP_SCRIPTCONTAINER:
877         {
878             if (!m_xTreeView->iter_has_child(*xIter))
879             {
880                 Reference< browse::XBrowseNode > rootNode(
881                     static_cast< browse::XBrowseNode* >( pInfo->pObject ) ) ;
882 
883                 try {
884                     if ( rootNode->hasChildNodes() )
885                     {
886                         const Sequence< Reference< browse::XBrowseNode > > children =
887                             rootNode->getChildNodes();
888 
889                         for ( const Reference< browse::XBrowseNode >& childNode : children )
890                         {
891                             if (childNode->getType() == browse::BrowseNodeTypes::SCRIPT)
892                             {
893                                 OUString uri, description;
894 
895                                 Reference < beans::XPropertySet >xPropSet( childNode, UNO_QUERY );
896                                 if (!xPropSet.is())
897                                 {
898                                     continue;
899                                 }
900 
901                                 Any value =
902                                     xPropSet->getPropertyValue(u"URI"_ustr);
903                                 value >>= uri;
904 
905                                 try
906                                 {
907                                     value = xPropSet->getPropertyValue(u"Description"_ustr);
908                                     value >>= description;
909                                 }
910                                 catch (Exception &) {
911                                     // do nothing, the description will be empty
912                                 }
913 
914                                 OUString* pScriptURI = new OUString( uri );
915 
916                                 OUString aImage = GetImage(childNode, Reference< XComponentContext >(), false);
917                                 m_pFunctionListBox->aArr.push_back( std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::FUNCTION_SCRIPT, 0, pScriptURI ));
918                                 m_pFunctionListBox->aArr.back()->sCommand = uri;
919                                 m_pFunctionListBox->aArr.back()->sLabel = childNode->getName();
920                                 m_pFunctionListBox->aArr.back()->sHelpText = description;
921 
922                                 OUString sId(weld::toId(m_pFunctionListBox->aArr.back().get()));
923                                 m_pFunctionListBox->append(sId, childNode->getName(), aImage);
924                             }
925                         }
926                     }
927                 }
928                 catch (RuntimeException&) {
929                     // do nothing, the entry will not be displayed in the UI
930                 }
931             }
932             break;
933         }
934 
935         case SfxCfgKind::GROUP_STYLES :
936         {
937             SfxStyleInfo_Impl* pFamily = static_cast<SfxStyleInfo_Impl*>(pInfo->pObject);
938             if (pFamily)
939             {
940                 const std::vector< SfxStyleInfo_Impl > lStyles = m_pStylesInfo->getStyles(pFamily->sFamily);
941                 for (auto const& lStyle : lStyles)
942                 {
943                     SfxStyleInfo_Impl* pStyle = new SfxStyleInfo_Impl(lStyle);
944                     m_pFunctionListBox->aArr.push_back(std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::GROUP_STYLES, 0, pStyle));
945                     m_pFunctionListBox->aArr.back()->sCommand = pStyle->sCommand;
946                     m_pFunctionListBox->aArr.back()->sLabel = pStyle->sLabel;
947                     OUString sId(weld::toId(m_pFunctionListBox->aArr.back().get()));
948                     m_pFunctionListBox->append(sId, pStyle->sLabel);
949                 }
950             }
951             break;
952         }
953 
954         case SfxCfgKind::GROUP_SIDEBARDECKS:
955         {
956             sfx2::sidebar::ResourceManager aResourceManager;
957             sfx2::sidebar::Context aContext(m_sModuleLongName, OUString());
958             sfx2::sidebar::ResourceManager::DeckContextDescriptorContainer aDecks;
959             aResourceManager.GetMatchingDecks(aDecks, aContext, false, m_xFrame->getController());
960 
961             for (auto const& rDeck : aDecks)
962             {
963                 const OUString sCommand = ".uno:SidebarDeck." + rDeck.msId;
964                 m_pFunctionListBox->aArr.push_back(std::make_unique<SfxGroupInfo_Impl>(
965                                                        SfxCfgKind::GROUP_SIDEBARDECKS, 0,
966                                                        nullptr));
967                 m_pFunctionListBox->aArr.back()->sCommand = sCommand;
968                 m_pFunctionListBox->aArr.back()->sLabel = rDeck.msId;
969                 m_pFunctionListBox->aArr.back()->sTooltip =
970                         vcl::CommandInfoProvider::GetCommandShortcut(sCommand, m_xFrame);
971                 m_pFunctionListBox->append(weld::toId(m_pFunctionListBox->aArr.back().get()),
972                                            rDeck.msId);
973             }
974 
975             break;
976         }
977 
978         default:
979             // Do nothing, the list box will stay empty
980             SAL_INFO( "cui.customize", "Ignoring unexpected SfxCfgKind: " <<  static_cast<int>(pInfo->nKind) );
981             break;
982     }
983 
984     m_pFunctionListBox->thaw();
985 
986     if (m_pFunctionListBox->n_children())
987         m_pFunctionListBox->select(0);
988 }
989 
990 /*  Description
991     A basic or a library is opened.
992 */
IMPL_LINK(CuiConfigGroupListBox,ExpandingHdl,const weld::TreeIter &,rIter,bool)993 IMPL_LINK(CuiConfigGroupListBox, ExpandingHdl, const weld::TreeIter&, rIter, bool)
994 {
995     SfxGroupInfo_Impl *pInfo = weld::fromId<SfxGroupInfo_Impl*>(m_xTreeView->get_id(rIter));
996     switch ( pInfo->nKind )
997     {
998         case SfxCfgKind::GROUP_SCRIPTCONTAINER:
999         {
1000             if (!m_xTreeView->iter_has_child(rIter))
1001             {
1002                 Reference< browse::XBrowseNode > rootNode(
1003                     static_cast< browse::XBrowseNode* >( pInfo->pObject ) ) ;
1004                 FillScriptList(rootNode, &rIter);
1005             }
1006             break;
1007         }
1008 
1009         case SfxCfgKind::GROUP_STYLES:
1010         {
1011             if (!m_xTreeView->iter_has_child(rIter))
1012             {
1013                 const std::vector<SfxStyleInfo_Impl> lStyleFamilies = m_pStylesInfo->getStyleFamilies();
1014                 for (auto const& lStyleFamily : lStyleFamilies)
1015                 {
1016                     SfxStyleInfo_Impl* pFamily = new SfxStyleInfo_Impl(lStyleFamily);
1017                     aArr.push_back( std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::GROUP_STYLES, 0, pFamily ));
1018                     OUString sId(weld::toId(aArr.back().get()));
1019                     m_xTreeView->insert(&rIter, -1, &pFamily->sLabel, &sId, nullptr, nullptr, false, nullptr);
1020                 }
1021             }
1022             break;
1023         }
1024 
1025         default:
1026             OSL_FAIL( "Wrong group type!" );
1027             break;
1028     }
1029     return true;
1030 }
1031 
1032 #if HAVE_FEATURE_SCRIPTING
SelectMacro(const SfxMacroInfoItem * pItem)1033 void CuiConfigGroupListBox::SelectMacro( const SfxMacroInfoItem *pItem )
1034 {
1035     auto const rMacro = pItem->GetQualifiedName();
1036     sal_Int32 nIdx {rMacro.lastIndexOf('.')};
1037     const std::u16string_view aMethod( rMacro.subView(nIdx + 1) );
1038     std::u16string_view aLib;
1039     std::u16string_view aModule;
1040     if ( nIdx>0 )
1041     {
1042         // string contains at least 2 tokens
1043         nIdx = rMacro.lastIndexOf('.', nIdx);
1044         if (nIdx != -1)
1045         {
1046             // string contains at least 3 tokens
1047             aLib = o3tl::getToken(rMacro, 0, '.' );
1048             sal_Int32 nIdx2 = nIdx + 1;
1049             aModule = o3tl::getToken(rMacro, 0, '.', nIdx2 );
1050         }
1051     }
1052 
1053     std::unique_ptr<weld::TreeIter> xIter = m_xTreeView->make_iterator();
1054     if (!m_xTreeView->get_iter_first(*xIter))
1055         return;
1056 
1057     do
1058     {
1059         OUString aEntryBas = m_xTreeView->get_text(*xIter);
1060         if (aEntryBas == xImp->m_sDlgMacros)
1061         {
1062             m_xTreeView->expand_row(*xIter);
1063             std::unique_ptr<weld::TreeIter> xLocationIter = m_xTreeView->make_iterator(xIter.get());
1064             if (m_xTreeView->iter_children(*xLocationIter))
1065             {
1066                 do
1067                 {
1068                     m_xTreeView->expand_row(*xLocationIter);
1069                     std::unique_ptr<weld::TreeIter> xLibIter = m_xTreeView->make_iterator(xLocationIter.get());
1070                     if (m_xTreeView->iter_children(*xLibIter))
1071                     {
1072                         do
1073                         {
1074                             OUString aEntryLib = m_xTreeView->get_text(*xLibIter);
1075                             if (aEntryLib == aLib)
1076                             {
1077                                 m_xTreeView->expand_row(*xLibIter);
1078                                 std::unique_ptr<weld::TreeIter> xModIter = m_xTreeView->make_iterator(xLibIter.get());
1079                                 if (m_xTreeView->iter_children(*xModIter))
1080                                 {
1081                                     do
1082                                     {
1083                                         OUString aEntryMod = m_xTreeView->get_text(*xModIter);
1084                                         if ( aEntryMod == aModule )
1085                                         {
1086                                             m_xTreeView->expand_row(*xModIter);
1087                                             m_xTreeView->scroll_to_row(*xModIter);
1088                                             m_xTreeView->select(*xModIter);
1089                                             GroupSelected();
1090                                             for (int i = 0, nCount = m_pFunctionListBox->n_children(); i < nCount; ++i)
1091                                             {
1092                                                 OUString aEntryMethod = m_pFunctionListBox->get_text(i);
1093                                                 if (aEntryMethod == aMethod)
1094                                                 {
1095                                                     m_pFunctionListBox->select(i);
1096                                                     m_pFunctionListBox->scroll_to_row(i);
1097                                                     return;
1098                                                 }
1099                                             }
1100                                             m_xTreeView->collapse_row(*xModIter);
1101                                         }
1102                                     } while (m_xTreeView->iter_next_sibling(*xModIter));
1103                                 }
1104                                 m_xTreeView->collapse_row(*xLibIter);
1105                             }
1106                         } while (m_xTreeView->iter_next_sibling(*xLibIter));
1107                     }
1108                     m_xTreeView->collapse_row(*xLocationIter);
1109                 } while (m_xTreeView->iter_next_sibling(*xLocationIter));
1110             }
1111             // If the macro can't be located, preselect the "Application Macros" category:
1112             m_xTreeView->scroll_to_row(*xIter);
1113             m_xTreeView->select(*xIter);
1114             return;
1115         }
1116     } while (m_xTreeView->iter_next_sibling(*xIter));
1117 }
1118 #endif
1119 
1120 /*
1121  * Implementation of SvxScriptSelectorDialog
1122  *
1123  * This dialog is used for selecting Slot API commands
1124  * and Scripting Framework Scripts.
1125  */
1126 
SvxScriptSelectorDialog(weld::Window * pParent,const css::uno::Reference<css::frame::XFrame> & xFrame)1127 SvxScriptSelectorDialog::SvxScriptSelectorDialog(
1128     weld::Window* pParent, const css::uno::Reference< css::frame::XFrame >& xFrame)
1129     : GenericDialogController(pParent, u"cui/ui/macroselectordialog.ui"_ustr, u"MacroSelectorDialog"_ustr)
1130     , m_xDialogDescription(m_xBuilder->weld_label(u"helpmacro"_ustr))
1131     , m_xCategories(new CuiConfigGroupListBox(m_xBuilder->weld_tree_view(u"categories"_ustr)))
1132     , m_xCommands(new CuiConfigFunctionListBox(m_xBuilder->weld_tree_view(u"commands"_ustr)))
1133     , m_xLibraryFT(m_xBuilder->weld_label(u"libraryft"_ustr))
1134     , m_xMacronameFT(m_xBuilder->weld_label(u"macronameft"_ustr))
1135     , m_xOKButton(m_xBuilder->weld_button(u"ok"_ustr))
1136     , m_xCancelButton(m_xBuilder->weld_button(u"cancel"_ustr))
1137     , m_xDescriptionText(m_xBuilder->weld_text_view(u"description"_ustr))
1138     , m_xDescriptionFrame(m_xBuilder->weld_frame(u"descriptionframe"_ustr))
1139 {
1140     m_xCancelButton->show();
1141     m_xDialogDescription->show();
1142     m_xOKButton->show();
1143 
1144     m_xLibraryFT->set_visible(true);
1145     m_xMacronameFT->set_visible(true);
1146 
1147     const OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(xFrame));
1148     m_xCategories->SetFunctionListBox(m_xCommands.get());
1149     m_xCategories->Init(comphelper::getProcessComponentContext(), xFrame, aModuleName, /*bShowSlots*/false);
1150 
1151     m_xCategories->connect_changed(
1152             LINK( this, SvxScriptSelectorDialog, SelectHdl ) );
1153     m_xCommands->connect_changed( LINK( this, SvxScriptSelectorDialog, SelectHdl ) );
1154     m_xCommands->connect_row_activated( LINK( this, SvxScriptSelectorDialog, FunctionDoubleClickHdl ) );
1155     m_xCommands->connect_popup_menu( LINK( this, SvxScriptSelectorDialog, ContextMenuHdl ) );
1156 
1157     m_xOKButton->connect_clicked( LINK( this, SvxScriptSelectorDialog, ClickHdl ) );
1158     m_xCancelButton->connect_clicked( LINK( this, SvxScriptSelectorDialog, ClickHdl ) );
1159 
1160     m_sDefaultDesc = m_xDescriptionText->get_text();
1161 
1162     // Support style commands
1163     uno::Reference<frame::XController> xController;
1164     uno::Reference<frame::XModel> xModel;
1165     if (xFrame.is())
1166         xController = xFrame->getController();
1167     if (xController.is())
1168         xModel = xController->getModel();
1169 
1170     m_aStylesInfo.init(aModuleName, xModel);
1171     m_xCategories->SetStylesInfo(&m_aStylesInfo);
1172 
1173     // The following call is a workaround to make scroll_to_row work as expected in kf5/x11
1174     m_xDialog->resize_to_request();
1175 
1176     LoadLastUsedMacro();
1177     UpdateUI();
1178 
1179     if (comphelper::LibreOfficeKit::isActive())
1180         m_xDescriptionFrame->hide();
1181 }
1182 
~SvxScriptSelectorDialog()1183 SvxScriptSelectorDialog::~SvxScriptSelectorDialog()
1184 {
1185 }
1186 
IMPL_LINK(SvxScriptSelectorDialog,SelectHdl,weld::TreeView &,rCtrl,void)1187 IMPL_LINK(SvxScriptSelectorDialog, SelectHdl, weld::TreeView&, rCtrl, void)
1188 {
1189     if (&rCtrl == &m_xCategories->get_widget())
1190     {
1191         m_xCategories->GroupSelected();
1192     }
1193     UpdateUI();
1194 }
1195 
IMPL_LINK_NOARG(SvxScriptSelectorDialog,FunctionDoubleClickHdl,weld::TreeView &,bool)1196 IMPL_LINK_NOARG(SvxScriptSelectorDialog, FunctionDoubleClickHdl, weld::TreeView&, bool)
1197 {
1198     if (m_xOKButton->get_sensitive())
1199         ClickHdl(*m_xOKButton);
1200     return true;
1201 }
1202 
IMPL_LINK(SvxScriptSelectorDialog,ContextMenuHdl,const CommandEvent &,rCEvt,bool)1203 IMPL_LINK(SvxScriptSelectorDialog, ContextMenuHdl, const CommandEvent&, rCEvt, bool)
1204 {
1205     weld::TreeView& xTreeView = m_xCommands->get_widget();
1206     if (rCEvt.GetCommand() != CommandEventId::ContextMenu || !xTreeView.n_children())
1207          return false;
1208 
1209     std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(&xTreeView, u"modules/BasicIDE/ui/sortmenu.ui"_ustr));
1210     std::unique_ptr<weld::Menu> xPopup(xBuilder->weld_menu(u"sortmenu"_ustr));
1211     std::unique_ptr<weld::Menu> xDropMenu(xBuilder->weld_menu(u"sortsubmenu"_ustr));
1212     xDropMenu->set_active(u"alphabetically"_ustr, xTreeView.get_sort_order());
1213     xDropMenu->set_active(u"properorder"_ustr, !xTreeView.get_sort_order());
1214 
1215     OUString sCommand(xPopup->popup_at_rect(&xTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))));
1216     if (sCommand == "alphabetically")
1217     {
1218         xTreeView.make_sorted();
1219     }
1220     else if (sCommand == "properorder")
1221     {
1222         xTreeView.make_unsorted();
1223         m_xCategories->GroupSelected();
1224     }
1225     else if (!sCommand.isEmpty())
1226     {
1227          SAL_WARN("cui.customize", "Unknown context menu action: " << sCommand );
1228     }
1229 
1230     return true;
1231 }
1232 
1233 // Check if command is selected and enable the OK button accordingly
1234 // Grab the help text for this id if available and update the description field
1235 void
UpdateUI()1236 SvxScriptSelectorDialog::UpdateUI()
1237 {
1238     OUString url = GetScriptURL();
1239     if ( !url.isEmpty() )
1240     {
1241         OUString sMessage = m_xCommands->GetCommandHelpText();
1242         m_xDescriptionText->set_text(sMessage.isEmpty() ? m_sDefaultDesc : sMessage);
1243         m_xOKButton->set_sensitive(true);
1244     }
1245     else
1246     {
1247         m_xDescriptionText->set_text(m_sDefaultDesc);
1248         m_xOKButton->set_sensitive(false);
1249     }
1250 }
1251 
IMPL_LINK(SvxScriptSelectorDialog,ClickHdl,weld::Button &,rButton,void)1252 IMPL_LINK(SvxScriptSelectorDialog, ClickHdl, weld::Button&, rButton, void)
1253 {
1254     if (&rButton == m_xCancelButton.get())
1255     {
1256         m_xDialog->response(RET_CANCEL);
1257     }
1258     else if (&rButton == m_xOKButton.get())
1259     {
1260         SaveLastUsedMacro();
1261         m_xDialog->response(RET_OK);
1262     }
1263 }
1264 
1265 void
SetRunLabel()1266 SvxScriptSelectorDialog::SetRunLabel()
1267 {
1268     m_xOKButton->set_label(CuiResId(RID_CUISTR_SELECTOR_RUN));
1269 }
1270 
1271 OUString
GetScriptURL() const1272 SvxScriptSelectorDialog::GetScriptURL() const
1273 {
1274     OUString result;
1275 
1276     std::unique_ptr<weld::TreeIter> xIter = m_xCommands->make_iterator();
1277     if (m_xCommands->get_selected(xIter.get()))
1278     {
1279         SfxGroupInfo_Impl *pData = weld::fromId<SfxGroupInfo_Impl*>(m_xCommands->get_id(*xIter));
1280         if  (   ( pData->nKind == SfxCfgKind::FUNCTION_SLOT )
1281             ||  ( pData->nKind == SfxCfgKind::FUNCTION_SCRIPT )
1282             ||  ( pData->nKind == SfxCfgKind::GROUP_STYLES )
1283             )
1284         {
1285             result = pData->sCommand;
1286         }
1287     }
1288 
1289     return result;
1290 }
1291 
1292 void
SaveLastUsedMacro()1293 SvxScriptSelectorDialog::SaveLastUsedMacro()
1294 {
1295     // Gets the current selection in the dialog as a series of selected entries
1296     OUString sMacroInfo;
1297     sMacroInfo = m_xCommands->get_selected_text();
1298     weld::TreeView& xCategories = m_xCategories->get_widget();
1299     std::unique_ptr<weld::TreeIter> xIter = xCategories.make_iterator();
1300 
1301     if (!xCategories.get_selected(xIter.get()))
1302         return;
1303 
1304     do
1305     {
1306         sMacroInfo = xCategories.get_text(*xIter) + "|" + sMacroInfo;
1307     } while (xCategories.iter_parent(*xIter));
1308 
1309     SvtViewOptions( EViewType::Dialog, MACRO_SELECTOR_CONFIGNAME ).SetUserItem(
1310         LAST_RUN_MACRO_INFO, Any(sMacroInfo));
1311 }
1312 
1313 void
LoadLastUsedMacro()1314 SvxScriptSelectorDialog::LoadLastUsedMacro()
1315 {
1316     SvtViewOptions aDlgOpt( EViewType::Dialog, MACRO_SELECTOR_CONFIGNAME );
1317     if (!aDlgOpt.Exists())
1318         return;
1319 
1320     OUString sMacroInfo;
1321     aDlgOpt.GetUserItem(LAST_RUN_MACRO_INFO) >>= sMacroInfo;
1322     if (sMacroInfo.isEmpty())
1323         return;
1324 
1325     // Counts how many entries exist in the macro info string
1326     sal_Int16 nInfoParts = 0;
1327     sal_Int16 nLastIndex = sMacroInfo.indexOf('|');
1328     if (nLastIndex > -1)
1329     {
1330         nInfoParts = 1;
1331         while ( nLastIndex != -1 )
1332         {
1333             nInfoParts++;
1334             nLastIndex = sMacroInfo.indexOf('|', nLastIndex + 1);
1335         }
1336     }
1337 
1338     weld::TreeView& xCategories = m_xCategories->get_widget();
1339     std::unique_ptr<weld::TreeIter> xIter = xCategories.make_iterator();
1340     if (!xCategories.get_iter_first(*xIter))
1341         return;
1342 
1343     // Expand the nodes in the category tree
1344     OUString sNodeToExpand;
1345     bool bIsIterValid;
1346     sal_Int16 nOpenedNodes = 0;
1347     for (sal_Int16 i=0; i<nInfoParts - 1; i++)
1348     {
1349         sNodeToExpand = sMacroInfo.getToken(i, '|');
1350         bIsIterValid = true;
1351         while (bIsIterValid && xCategories.get_text(*xIter) != sNodeToExpand)
1352             bIsIterValid = xCategories.iter_next_sibling(*xIter);
1353 
1354         if (bIsIterValid)
1355         {
1356             xCategories.expand_row(*xIter);
1357             nOpenedNodes++;
1358         }
1359         if (xCategories.iter_has_child(*xIter))
1360             (void)xCategories.iter_children(*xIter);
1361         else if (nOpenedNodes < nInfoParts - 1)
1362             // If the number of levels in the tree is smaller than the
1363             // number of parts in the macro info string, then return
1364             return;
1365     }
1366     xCategories.select(*xIter);
1367     xCategories.scroll_to_row(*xIter);
1368     m_xCategories->GroupSelected();
1369 
1370     // Select the macro in the command tree
1371     weld::TreeView& xCommands = m_xCommands->get_widget();
1372     xIter = xCommands.make_iterator();
1373     if (!xCommands.get_iter_first(*xIter))
1374         return;
1375 
1376     OUString sMacroName = sMacroInfo.getToken(nInfoParts - 1, '|');
1377     bIsIterValid = true;
1378     while (bIsIterValid && xCommands.get_text(*xIter) != sMacroName)
1379         bIsIterValid = xCommands.iter_next_sibling(*xIter);
1380 
1381     if (bIsIterValid)
1382     {
1383         xCommands.scroll_to_row(*xIter);
1384         xCommands.select(*xIter);
1385     }
1386 }
1387 
1388 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1389