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