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