1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 #include "precompiled_sfx2.hxx"
23 
24 #include "ResourceManager.hxx"
25 #include "Tools.hxx"
26 
27 #include <unotools/confignode.hxx>
28 #include <comphelper/componentcontext.hxx>
29 #include <comphelper/processfactory.hxx>
30 #include <comphelper/namedvaluecollection.hxx>
31 #include <comphelper/types.hxx>
32 #include <comphelper/stlunosequence.hxx>
33 
34 #include <rtl/ustrbuf.hxx>
35 #include <tools/diagnose_ex.h>
36 
37 #include <com/sun/star/frame/XModuleManager.hpp>
38 
39 #include <map>
40 
41 
42 
43 using ::rtl::OUString;
44 using namespace css;
45 using namespace cssu;
46 
47 namespace sfx2 { namespace sidebar {
48 
49 #define gsPrivateResourceToolpanelPrefix "private:resource/toolpanel/"
50 
51 
52 
53 class ResourceManager::Deleter
54 {
55 public:
56     void operator() (ResourceManager* pObject)
57     {
58         delete pObject;
59     }
60 };
61 
62 
63 ResourceManager& ResourceManager::Instance (void)
64 {
65     static ResourceManager maInstance;
66     return maInstance;
67 }
68 
69 
70 
71 
72 ResourceManager::ResourceManager (void)
73     : maDecks(),
74       maPanels(),
75       maProcessedApplications()
76 {
77     ReadDeckList();
78     ReadPanelList();
79 }
80 
81 
82 
83 
84 ResourceManager::~ResourceManager (void)
85 {
86     maPanels.clear();
87     maDecks.clear();
88 }
89 
90 
91 
92 
93 const DeckDescriptor* ResourceManager::GetBestMatchingDeck (
94     const Context& rContext,
95     const Reference<frame::XFrame>& rxFrame)
96 {
97     ReadLegacyAddons(rxFrame);
98 
99     for (DeckContainer::const_iterator iDeck(maDecks.begin()), iEnd(maDecks.end());
100          iDeck!=iEnd;
101          ++iDeck)
102     {
103         if (iDeck->maContextList.GetMatch(rContext) != NULL)
104             return &*iDeck;
105     }
106     return NULL;
107 }
108 
109 
110 
111 
112 const DeckDescriptor* ResourceManager::GetDeckDescriptor (
113     const ::rtl::OUString& rsDeckId) const
114 {
115     for (DeckContainer::const_iterator
116              iDeck(maDecks.begin()),
117              iEnd(maDecks.end());
118          iDeck!=iEnd;
119          ++iDeck)
120     {
121         if (iDeck->msId.equals(rsDeckId))
122             return &*iDeck;
123     }
124     return NULL;
125 }
126 
127 
128 
129 
130 const PanelDescriptor* ResourceManager::GetPanelDescriptor (
131     const ::rtl::OUString& rsPanelId) const
132 {
133     for (PanelContainer::const_iterator
134              iPanel(maPanels.begin()),
135              iEnd(maPanels.end());
136          iPanel!=iEnd;
137          ++iPanel)
138     {
139         if (iPanel->msId.equals(rsPanelId))
140             return &*iPanel;
141     }
142     return NULL;
143 }
144 
145 
146 
147 
148 void ResourceManager::SetIsDeckEnabled (
149     const ::rtl::OUString& rsDeckId,
150     const bool bIsEnabled)
151 {
152     for (DeckContainer::iterator
153              iDeck(maDecks.begin()),
154              iEnd(maDecks.end());
155          iDeck!=iEnd;
156          ++iDeck)
157     {
158         if (iDeck->msId.equals(rsDeckId))
159         {
160             iDeck->mbIsEnabled = bIsEnabled;
161             return;
162         }
163     }
164 }
165 
166 
167 
168 
169 const ResourceManager::IdContainer& ResourceManager::GetMatchingDecks (
170     IdContainer& rDeckIds,
171     const Context& rContext,
172     const Reference<frame::XFrame>& rxFrame)
173 {
174     ReadLegacyAddons(rxFrame);
175 
176     ::std::multimap<sal_Int32,OUString> aOrderedIds;
177     for (DeckContainer::const_iterator
178              iDeck(maDecks.begin()),
179              iEnd (maDecks.end());
180          iDeck!=iEnd;
181          ++iDeck)
182     {
183         const DeckDescriptor& rDeckDescriptor (*iDeck);
184         if (rDeckDescriptor.maContextList.GetMatch(rContext) != NULL)
185             aOrderedIds.insert(::std::multimap<sal_Int32,OUString>::value_type(
186                     rDeckDescriptor.mnOrderIndex,
187                     rDeckDescriptor.msId));
188     }
189 
190     for (::std::multimap<sal_Int32,OUString>::const_iterator
191              iId(aOrderedIds.begin()),
192              iEnd(aOrderedIds.end());
193          iId!=iEnd;
194          ++iId)
195     {
196         rDeckIds.push_back(iId->second);
197     }
198 
199     return rDeckIds;
200 }
201 
202 
203 
204 
205 const ResourceManager::PanelContextDescriptorContainer& ResourceManager::GetMatchingPanels (
206     PanelContextDescriptorContainer& rPanelIds,
207     const Context& rContext,
208     const ::rtl::OUString& rsDeckId,
209     const Reference<frame::XFrame>& rxFrame)
210 {
211     ReadLegacyAddons(rxFrame);
212 
213     ::std::multimap<sal_Int32,PanelContextDescriptor> aOrderedIds;
214     for (PanelContainer::const_iterator
215              iPanel(maPanels.begin()),
216              iEnd(maPanels.end());
217          iPanel!=iEnd;
218          ++iPanel)
219     {
220         const PanelDescriptor& rPanelDescriptor (*iPanel);
221         if (rPanelDescriptor.msDeckId.equals(rsDeckId))
222         {
223             const ContextList::Entry* pEntry = rPanelDescriptor.maContextList.GetMatch(rContext);
224             if (pEntry != NULL)
225             {
226                 PanelContextDescriptor aPanelContextDescriptor;
227                 aPanelContextDescriptor.msId = rPanelDescriptor.msId;
228                 aPanelContextDescriptor.msMenuCommand = pEntry->msMenuCommand;
229                 aPanelContextDescriptor.mbIsInitiallyVisible = pEntry->mbIsInitiallyVisible;
230                 aOrderedIds.insert(::std::multimap<sal_Int32,PanelContextDescriptor>::value_type(
231                         rPanelDescriptor.mnOrderIndex,
232                         aPanelContextDescriptor));
233             }
234         }
235     }
236 
237     for (::std::multimap<sal_Int32,PanelContextDescriptor>::const_iterator
238              iId(aOrderedIds.begin()),
239              iEnd(aOrderedIds.end());
240          iId!=iEnd;
241          ++iId)
242     {
243         rPanelIds.push_back(iId->second);
244     }
245 
246     return rPanelIds;
247 }
248 
249 
250 
251 
252 void ResourceManager::ReadDeckList (void)
253 {
254     const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory());
255     const ::utl::OConfigurationTreeRoot aDeckRootNode (
256         aContext,
257         A2S("org.openoffice.Office.UI.Sidebar/Content/DeckList"),
258         false);
259     if ( ! aDeckRootNode.isValid() )
260         return;
261 
262     const Sequence<OUString> aDeckNodeNames (aDeckRootNode.getNodeNames());
263     const sal_Int32 nCount (aDeckNodeNames.getLength());
264     maDecks.resize(nCount);
265     sal_Int32 nWriteIndex(0);
266     for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex)
267     {
268         const ::utl::OConfigurationNode aDeckNode (aDeckRootNode.openNode(aDeckNodeNames[nReadIndex]));
269         if ( ! aDeckNode.isValid())
270             continue;
271 
272         DeckDescriptor& rDeckDescriptor (maDecks[nWriteIndex++]);
273 
274         rDeckDescriptor.msTitle = ::comphelper::getString(
275             aDeckNode.getNodeValue("Title"));
276         rDeckDescriptor.msId = ::comphelper::getString(
277             aDeckNode.getNodeValue("Id"));
278         rDeckDescriptor.msIconURL = ::comphelper::getString(
279             aDeckNode.getNodeValue("IconURL"));
280         rDeckDescriptor.msHighContrastIconURL = ::comphelper::getString(
281             aDeckNode.getNodeValue("HighContrastIconURL"));
282         rDeckDescriptor.msTitleBarIconURL = ::comphelper::getString(
283             aDeckNode.getNodeValue("TitleBarIconURL"));
284         rDeckDescriptor.msHighContrastTitleBarIconURL = ::comphelper::getString(
285             aDeckNode.getNodeValue("HighContrastTitleBarIconURL"));
286         rDeckDescriptor.msHelpURL = ::comphelper::getString(
287             aDeckNode.getNodeValue("HelpURL"));
288         rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle;
289         rDeckDescriptor.mbIsEnabled = true;
290         rDeckDescriptor.mnOrderIndex = ::comphelper::getINT32(
291             aDeckNode.getNodeValue("OrderIndex"));
292 
293         ReadContextList(
294             aDeckNode,
295             rDeckDescriptor.maContextList,
296             OUString());
297     }
298 
299     // When there where invalid nodes then we have to adapt the size
300     // of the deck vector.
301     if (nWriteIndex<nCount)
302         maDecks.resize(nWriteIndex);
303 }
304 
305 
306 
307 
308 void ResourceManager::ReadPanelList (void)
309 {
310     const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory());
311     const ::utl::OConfigurationTreeRoot aPanelRootNode (
312         aContext,
313         A2S("org.openoffice.Office.UI.Sidebar/Content/PanelList"),
314         false);
315     if ( ! aPanelRootNode.isValid() )
316         return;
317 
318     const Sequence<OUString> aPanelNodeNames (aPanelRootNode.getNodeNames());
319     const sal_Int32 nCount (aPanelNodeNames.getLength());
320     maPanels.resize(nCount);
321     sal_Int32 nWriteIndex (0);
322     for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex)
323     {
324         const ::utl::OConfigurationNode aPanelNode (aPanelRootNode.openNode(aPanelNodeNames[nReadIndex]));
325         if ( ! aPanelNode.isValid())
326             continue;
327 
328         PanelDescriptor& rPanelDescriptor (maPanels[nWriteIndex++]);
329 
330         rPanelDescriptor.msTitle = ::comphelper::getString(
331             aPanelNode.getNodeValue("Title"));
332         rPanelDescriptor.mbIsTitleBarOptional = ::comphelper::getBOOL(
333             aPanelNode.getNodeValue("TitleBarIsOptional"));
334         rPanelDescriptor.msId = ::comphelper::getString(
335             aPanelNode.getNodeValue("Id"));
336         rPanelDescriptor.msDeckId = ::comphelper::getString(
337             aPanelNode.getNodeValue("DeckId"));
338         rPanelDescriptor.msTitleBarIconURL = ::comphelper::getString(
339             aPanelNode.getNodeValue("TitleBarIconURL"));
340         rPanelDescriptor.msHighContrastTitleBarIconURL = ::comphelper::getString(
341             aPanelNode.getNodeValue("HighContrastTitleBarIconURL"));
342         rPanelDescriptor.msHelpURL = ::comphelper::getString(
343             aPanelNode.getNodeValue("HelpURL"));
344         rPanelDescriptor.msImplementationURL = ::comphelper::getString(
345             aPanelNode.getNodeValue("ImplementationURL"));
346         rPanelDescriptor.mnOrderIndex = ::comphelper::getINT32(
347             aPanelNode.getNodeValue("OrderIndex"));
348         rPanelDescriptor.mbWantsCanvas = ::comphelper::getBOOL(
349             aPanelNode.getNodeValue("WantsCanvas"));
350         const OUString sDefaultMenuCommand (::comphelper::getString(
351                 aPanelNode.getNodeValue("DefaultMenuCommand")));
352 
353         ReadContextList(
354             aPanelNode,
355             rPanelDescriptor.maContextList,
356             sDefaultMenuCommand);
357     }
358 
359     // When there where invalid nodes then we have to adapt the size
360     // of the deck vector.
361     if (nWriteIndex<nCount)
362         maPanels.resize(nWriteIndex);
363 }
364 
365 
366 
367 
368 void ResourceManager::ReadContextList (
369     const ::utl::OConfigurationNode& rParentNode,
370     ContextList& rContextList,
371     const OUString& rsDefaultMenuCommand) const
372 {
373     const Any aValue = rParentNode.getNodeValue("ContextList");
374     Sequence<OUString> aValues;
375     sal_Int32 nCount;
376     if (aValue >>= aValues)
377         nCount = aValues.getLength();
378     else
379         nCount = 0;
380 
381     for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex)
382     {
383         const OUString sValue (aValues[nIndex]);
384         sal_Int32 nCharacterIndex (0);
385         const OUString sApplicationName (sValue.getToken(0, ',', nCharacterIndex).trim());
386         if (nCharacterIndex < 0)
387         {
388             if (sApplicationName.getLength() == 0)
389             {
390                 // This is a valid case: in the XML file the separator
391                 // was used as terminator.  Using it in the last line
392                 // creates an additional but empty entry.
393                 break;
394             }
395             else
396             {
397                 OSL_ASSERT("expecting three or four values per ContextList entry, separated by comma");
398                 continue;
399             }
400         }
401 
402         const OUString sContextName (sValue.getToken(0, ',', nCharacterIndex).trim());
403         if (nCharacterIndex < 0)
404         {
405             OSL_ASSERT("expecting three or four values per ContextList entry, separated by comma");
406             continue;
407         }
408 
409         const OUString sInitialState (sValue.getToken(0, ',', nCharacterIndex).trim());
410 
411         // The fourth argument is optional.
412         const OUString sMenuCommandOverride (
413             nCharacterIndex<0
414                 ? OUString()
415                 : sValue.getToken(0, ',', nCharacterIndex).trim());
416         const OUString sMenuCommand (
417             sMenuCommandOverride.getLength()>0
418                 ? (sMenuCommandOverride.equalsAscii("none")
419                     ? OUString()
420                     : sMenuCommandOverride)
421                 : rsDefaultMenuCommand);
422 
423         // Setup a list of application enums.  Note that the
424         // application name may result in more than one value (eg
425         // DrawImpress will result in two enums, one for Draw and one
426         // for Impress).
427         ::std::vector<EnumContext::Application> aApplications;
428         EnumContext::Application eApplication (EnumContext::GetApplicationEnum(sApplicationName));
429         if (eApplication == EnumContext::Application_None
430             && !sApplicationName.equals(EnumContext::GetApplicationName(EnumContext::Application_None)))
431         {
432             // Handle some special names: abbreviations that make
433             // context descriptions more readable.
434             if (sApplicationName.equalsAscii("Writer"))
435                 aApplications.push_back(EnumContext::Application_Writer);
436             else if (sApplicationName.equalsAscii("Calc"))
437                 aApplications.push_back(EnumContext::Application_Calc);
438             else if (sApplicationName.equalsAscii("Draw"))
439                 aApplications.push_back(EnumContext::Application_Draw);
440             else if (sApplicationName.equalsAscii("Impress"))
441                 aApplications.push_back(EnumContext::Application_Impress);
442             else if (sApplicationName.equalsAscii("DrawImpress"))
443             {
444                 // A special case among the special names:  it is
445                 // common to use the same context descriptions for
446                 // both Draw and Impress.  This special case helps to
447                 // avoid duplication in the .xcu file.
448                 aApplications.push_back(EnumContext::Application_Draw);
449                 aApplications.push_back(EnumContext::Application_Impress);
450             }
451             else if (sApplicationName.equalsAscii("WriterVariants"))
452             {
453                 // Another special case for all Writer variants.
454                 aApplications.push_back(EnumContext::Application_Writer);
455                 aApplications.push_back(EnumContext::Application_WriterGlobal);
456                 aApplications.push_back(EnumContext::Application_WriterWeb);
457                 aApplications.push_back(EnumContext::Application_WriterXML);
458             }
459             else
460             {
461                 OSL_ASSERT("application name not recognized");
462                 continue;
463             }
464         }
465         else
466         {
467             // No conversion of the application name necessary.
468             aApplications.push_back(eApplication);
469         }
470 
471         // Setup the actual context enum.
472         const EnumContext::Context eContext (EnumContext::GetContextEnum(sContextName));
473         if (eContext == EnumContext::Context_Unknown)
474         {
475             OSL_ASSERT("context name not recognized");
476             continue;
477         }
478 
479         // Setup the flag that controls whether a deck/pane is
480         // initially visible/expanded.
481         bool bIsInitiallyVisible;
482         if (sInitialState.equalsAscii("visible"))
483             bIsInitiallyVisible = true;
484         else if (sInitialState.equalsAscii("hidden"))
485             bIsInitiallyVisible = false;
486         else
487         {
488             OSL_ASSERT("unrecognized state");
489             continue;
490         }
491 
492         // Add context descriptors.
493         for (::std::vector<EnumContext::Application>::const_iterator
494                  iApplication(aApplications.begin()),
495                  iEnd(aApplications.end());
496              iApplication!=iEnd;
497              ++iApplication)
498         {
499             if (*iApplication != EnumContext::Application_None)
500                 rContextList.AddContextDescription(
501                     Context(
502                         EnumContext::GetApplicationName(*iApplication),
503                         EnumContext::GetContextName(eContext)),
504                     bIsInitiallyVisible,
505                     sMenuCommand);
506         }
507     }
508 }
509 
510 
511 
512 
513 void ResourceManager::ReadLegacyAddons (const Reference<frame::XFrame>& rxFrame)
514 {
515     // Get module name for given frame.
516     ::rtl::OUString sModuleName (GetModuleName(rxFrame));
517     if (sModuleName.getLength() == 0)
518         return;
519     if (maProcessedApplications.find(sModuleName) != maProcessedApplications.end())
520     {
521         // Addons for this application have already been read.
522         // There is nothing more to do.
523         return;
524     }
525 
526     // Mark module as processed.  Even when there is an error that
527     // prevents the configuration data from being read, this error
528     // will not be triggered a second time.
529     maProcessedApplications.insert(sModuleName);
530 
531     // Get access to the configuration root node for the application.
532     ::utl::OConfigurationTreeRoot aLegacyRootNode (GetLegacyAddonRootNode(sModuleName));
533     if ( ! aLegacyRootNode.isValid())
534         return;
535 
536     // Process child nodes.
537     ::std::vector<OUString> aMatchingNodeNames;
538     GetToolPanelNodeNames(aMatchingNodeNames, aLegacyRootNode);
539     const sal_Int32 nCount (aMatchingNodeNames.size());
540     size_t nDeckWriteIndex (maDecks.size());
541     size_t nPanelWriteIndex (maPanels.size());
542     maDecks.resize(maDecks.size() + nCount);
543     maPanels.resize(maPanels.size() + nCount);
544     for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex)
545     {
546         const OUString& rsNodeName (aMatchingNodeNames[nReadIndex]);
547         const ::utl::OConfigurationNode aChildNode (aLegacyRootNode.openNode(rsNodeName));
548         if ( ! aChildNode.isValid())
549             continue;
550 
551         DeckDescriptor& rDeckDescriptor (maDecks[nDeckWriteIndex++]);
552         rDeckDescriptor.msTitle = ::comphelper::getString(aChildNode.getNodeValue("UIName"));
553         rDeckDescriptor.msId = rsNodeName;
554         rDeckDescriptor.msIconURL = ::comphelper::getString(aChildNode.getNodeValue("ImageURL"));
555         rDeckDescriptor.msHighContrastIconURL = rDeckDescriptor.msIconURL;
556         rDeckDescriptor.msHelpURL = ::comphelper::getString(aChildNode.getNodeValue("HelpURL"));
557         rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle;
558         rDeckDescriptor.maContextList.AddContextDescription(Context(sModuleName, A2S("any")), true, OUString());
559         rDeckDescriptor.mbIsEnabled = true;
560 
561         PanelDescriptor& rPanelDescriptor (maPanels[nPanelWriteIndex++]);
562         rPanelDescriptor.msTitle = ::comphelper::getString(aChildNode.getNodeValue("UIName"));
563         rPanelDescriptor.mbIsTitleBarOptional = true;
564         rPanelDescriptor.msId = rsNodeName;
565         rPanelDescriptor.msDeckId = rsNodeName;
566         rPanelDescriptor.msHelpURL = ::comphelper::getString(aChildNode.getNodeValue("HelpURL"));
567         rPanelDescriptor.maContextList.AddContextDescription(Context(sModuleName, A2S("any")), true, OUString());
568         rPanelDescriptor.msImplementationURL = rsNodeName;
569     }
570 
571     // When there where invalid nodes then we have to adapt the size
572     // of the deck and panel vectors.
573     if (nDeckWriteIndex < maDecks.size())
574         maDecks.resize(nDeckWriteIndex);
575     if (nPanelWriteIndex < maPanels.size())
576         maPanels.resize(nPanelWriteIndex);
577 }
578 
579 
580 
581 
582 ::rtl::OUString ResourceManager::GetModuleName (
583     const cssu::Reference<css::frame::XFrame>& rxFrame)
584 {
585     if ( ! rxFrame.is() || ! rxFrame->getController().is())
586         return OUString();
587 
588     try
589     {
590         const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory());
591         const Reference<frame::XModuleManager> xModuleManager (
592             aContext.createComponent("com.sun.star.frame.ModuleManager"),
593             UNO_QUERY_THROW);
594         return xModuleManager->identify(rxFrame);
595     }
596     catch (const Exception&)
597     {
598         DBG_UNHANDLED_EXCEPTION();
599     }
600     return OUString();
601 }
602 
603 
604 
605 
606 ::utl::OConfigurationTreeRoot ResourceManager::GetLegacyAddonRootNode (
607     const ::rtl::OUString& rsModuleName) const
608 {
609     try
610     {
611         const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory());
612         const Reference<container::XNameAccess> xModuleAccess (
613             aContext.createComponent("com.sun.star.frame.ModuleManager"),
614             UNO_QUERY_THROW);
615         const ::comphelper::NamedValueCollection aModuleProperties (xModuleAccess->getByName(rsModuleName));
616         const ::rtl::OUString sWindowStateRef (aModuleProperties.getOrDefault(
617                 "ooSetupFactoryWindowStateConfigRef",
618                 ::rtl::OUString()));
619 
620         ::rtl::OUStringBuffer aPathComposer;
621         aPathComposer.appendAscii("org.openoffice.Office.UI.");
622         aPathComposer.append(sWindowStateRef);
623         aPathComposer.appendAscii("/UIElements/States");
624 
625         return ::utl::OConfigurationTreeRoot(aContext, aPathComposer.makeStringAndClear(), false);
626     }
627     catch( const Exception& )
628     {
629         DBG_UNHANDLED_EXCEPTION();
630     }
631 
632     return ::utl::OConfigurationTreeRoot();
633 }
634 
635 
636 
637 
638 void ResourceManager::GetToolPanelNodeNames (
639     ::std::vector<OUString>& rMatchingNames,
640     const ::utl::OConfigurationTreeRoot aRoot) const
641 {
642     Sequence<OUString> aChildNodeNames (aRoot.getNodeNames());
643     const sal_Int32 nCount (aChildNodeNames.getLength());
644     for (sal_Int32 nIndex(0); nIndex<nCount; ++nIndex)
645     {
646         if (aChildNodeNames[nIndex].matchAsciiL(
647                 RTL_CONSTASCII_STRINGPARAM( "private:resource/toolpanel/")))
648             rMatchingNames.push_back(aChildNodeNames[nIndex]);
649     }
650 }
651 
652 
653 
654 } } // end of namespace sfx2::sidebar
655