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