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 <unotools/confignode.hxx>
26 #include <comphelper/componentcontext.hxx>
27 #include <comphelper/processfactory.hxx>
28 #include <comphelper/namedvaluecollection.hxx>
29 #include <comphelper/types.hxx>
30 #include <rtl/ustrbuf.hxx>
31 #include <tools/diagnose_ex.h>
32 
33 #include <com/sun/star/frame/XModuleManager.hpp>
34 
35 #include <map>
36 
37 
38 #define A2S(pString) (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(pString)))
39 
40 using ::rtl::OUString;
41 using namespace css;
42 using namespace cssu;
43 
44 namespace sfx2 { namespace sidebar {
45 
46 #define gsPrivateResourceToolpanelPrefix "private:resource/toolpanel/"
47 
48 
49 
50 class ResourceManager::Deleter
51 {
52 public:
53     void operator() (ResourceManager* pObject)
54     {
55         delete pObject;
56     }
57 };
58 
59 
60 ResourceManager& ResourceManager::Instance (void)
61 {
62     static ResourceManager maInstance;
63     return maInstance;
64 }
65 
66 
67 
68 
69 ResourceManager::ResourceManager (void)
70     : maDecks(),
71       maPanels(),
72       maProcessedApplications()
73 {
74     ReadDeckList();
75     ReadPanelList();
76 }
77 
78 
79 
80 
81 ResourceManager::~ResourceManager (void)
82 {
83     maPanels.clear();
84     maDecks.clear();
85 }
86 
87 
88 
89 
90 const DeckDescriptor* ResourceManager::GetBestMatchingDeck (
91     const EnumContext& rContext,
92     const Reference<frame::XFrame>& rxFrame)
93 {
94     ReadLegacyAddons(rxFrame);
95 
96     sal_Int32 nBestMatch (EnumContext::NoMatch);
97     const DeckContainer::const_iterator iEnd (maDecks.end());
98     DeckContainer::const_iterator iBestDeck (iEnd);
99 
100     for (DeckContainer::const_iterator iDeck(maDecks.begin());
101          iDeck!=iEnd;
102          ++iDeck)
103     {
104         const sal_Int32 nMatch (rContext.EvaluateMatch(iDeck->maContexts));
105         if (nMatch < nBestMatch)
106         {
107             // Found a better matching deck.
108             nBestMatch = nMatch;
109             iBestDeck = iDeck;
110             if (nBestMatch == EnumContext::OptimalMatch)
111             {
112                 // We will not find a better match.
113                 break;
114             }
115         }
116     }
117     if (iBestDeck != iEnd)
118         return &*iBestDeck;
119     else
120         return NULL;
121 }
122 
123 
124 
125 
126 const DeckDescriptor* ResourceManager::GetDeckDescriptor (
127     const ::rtl::OUString& rsDeckId) const
128 {
129     for (DeckContainer::const_iterator
130              iDeck(maDecks.begin()),
131              iEnd(maDecks.end());
132          iDeck!=iEnd;
133          ++iDeck)
134     {
135         if (iDeck->msId.equals(rsDeckId))
136             return &*iDeck;
137     }
138     return NULL;
139 }
140 
141 
142 
143 
144 const PanelDescriptor* ResourceManager::GetPanelDescriptor (
145     const ::rtl::OUString& rsPanelId) const
146 {
147     for (PanelContainer::const_iterator
148              iPanel(maPanels.begin()),
149              iEnd(maPanels.end());
150          iPanel!=iEnd;
151          ++iPanel)
152     {
153         if (iPanel->msId.equals(rsPanelId))
154             return &*iPanel;
155     }
156     return NULL;
157 }
158 
159 
160 
161 
162 void ResourceManager::SetIsDeckEnabled (
163     const ::rtl::OUString& rsDeckId,
164     const bool bIsEnabled)
165 {
166     for (DeckContainer::iterator
167              iDeck(maDecks.begin()),
168              iEnd(maDecks.end());
169          iDeck!=iEnd;
170          ++iDeck)
171     {
172         if (iDeck->msId.equals(rsDeckId))
173         {
174             iDeck->mbIsEnabled = bIsEnabled;
175             return;
176         }
177     }
178 }
179 
180 
181 
182 
183 const ResourceManager::IdContainer& ResourceManager::GetMatchingDecks (
184     IdContainer& rDeckIds,
185     const EnumContext& rContext,
186     const Reference<frame::XFrame>& rxFrame)
187 {
188     ReadLegacyAddons(rxFrame);
189 
190     for (DeckContainer::const_iterator
191              iDeck(maDecks.begin()),
192              iEnd (maDecks.end());
193          iDeck!=iEnd;
194          ++iDeck)
195     {
196         if (rContext.EvaluateMatch(iDeck->maContexts) != EnumContext::NoMatch)
197             rDeckIds.push_back(iDeck->msId);
198     }
199 
200     return rDeckIds;
201 }
202 
203 
204 
205 
206 const ResourceManager::IdContainer& ResourceManager::GetMatchingPanels (
207     IdContainer& rPanelIds,
208     const EnumContext& rContext,
209     const ::rtl::OUString& rsDeckId,
210     const Reference<frame::XFrame>& rxFrame)
211 {
212     ReadLegacyAddons(rxFrame);
213 
214     ::std::multimap<sal_Int32,OUString> aOrderedIds;
215     for (PanelContainer::const_iterator
216              iPanel(maPanels.begin()),
217              iEnd(maPanels.end());
218          iPanel!=iEnd;
219          ++iPanel)
220     {
221         const PanelDescriptor& rPanelDescriptor (*iPanel);
222         if (rPanelDescriptor.msDeckId.equals(rsDeckId))
223             if (rContext.EvaluateMatch(rPanelDescriptor.maContexts) != EnumContext::NoMatch)
224                 aOrderedIds.insert(::std::multimap<sal_Int32,OUString>::value_type(
225                         iPanel->mnOrderIndex,
226                         iPanel->msId));
227     }
228 
229     for (::std::multimap<sal_Int32,OUString>::const_iterator
230              iId(aOrderedIds.begin()),
231              iEnd(aOrderedIds.end());
232          iId!=iEnd;
233          ++iId)
234     {
235         rPanelIds.push_back(iId->second);
236     }
237 
238     return rPanelIds;
239 }
240 
241 
242 
243 
244 void ResourceManager::ReadDeckList (void)
245 {
246     const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory());
247     const ::utl::OConfigurationTreeRoot aDeckRootNode (
248         aContext,
249         A2S("org.openoffice.Office.UI.Sidebar/Content/DeckList"),
250         false);
251     if ( ! aDeckRootNode.isValid() )
252         return;
253 
254     const Sequence<OUString> aDeckNodeNames (aDeckRootNode.getNodeNames());
255     const sal_Int32 nCount (aDeckNodeNames.getLength());
256     maDecks.resize(nCount);
257     sal_Int32 nWriteIndex(0);
258     for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex)
259     {
260         const ::utl::OConfigurationNode aDeckNode (aDeckRootNode.openNode(aDeckNodeNames[nReadIndex]));
261         if ( ! aDeckNode.isValid())
262             continue;
263 
264         DeckDescriptor& rDeckDescriptor (maDecks[nWriteIndex++]);
265 
266         rDeckDescriptor.msTitle = ::comphelper::getString(aDeckNode.getNodeValue("Title"));
267         rDeckDescriptor.msId = ::comphelper::getString(aDeckNode.getNodeValue("Id"));
268         rDeckDescriptor.msIconURL = ::comphelper::getString(aDeckNode.getNodeValue("IconURL"));
269         rDeckDescriptor.msHighContrastIconURL = ::comphelper::getString(aDeckNode.getNodeValue("HighContrastIconURL"));
270         rDeckDescriptor.msHelpURL = ::comphelper::getString(aDeckNode.getNodeValue("HelpURL"));
271         rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle;
272         rDeckDescriptor.mbIsEnabled = true;
273         ReadContextList(aDeckNode.openNode("ContextList"), rDeckDescriptor.maContexts);
274     }
275 
276     // When there where invalid nodes then we have to adapt the size
277     // of the deck vector.
278     if (nWriteIndex<nCount)
279         maDecks.resize(nWriteIndex);
280 }
281 
282 
283 
284 
285 void ResourceManager::ReadPanelList (void)
286 {
287     const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory());
288     const ::utl::OConfigurationTreeRoot aPanelRootNode (
289         aContext,
290         A2S("org.openoffice.Office.UI.Sidebar/Content/PanelList"),
291         false);
292     if ( ! aPanelRootNode.isValid() )
293         return;
294 
295     const Sequence<OUString> aPanelNodeNames (aPanelRootNode.getNodeNames());
296     const sal_Int32 nCount (aPanelNodeNames.getLength());
297     maPanels.resize(nCount);
298     sal_Int32 nWriteIndex (0);
299     for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex)
300     {
301         const ::utl::OConfigurationNode aPanelNode (aPanelRootNode.openNode(aPanelNodeNames[nReadIndex]));
302         if ( ! aPanelNode.isValid())
303             continue;
304 
305         PanelDescriptor& rPanelDescriptor (maPanels[nWriteIndex++]);
306 
307         rPanelDescriptor.msTitle = ::comphelper::getString(
308             aPanelNode.getNodeValue("Title"));
309         rPanelDescriptor.mbIsTitleBarOptional = ::comphelper::getBOOL(
310             aPanelNode.getNodeValue("TitleBarIsOptional"));
311         rPanelDescriptor.msId = ::comphelper::getString(
312             aPanelNode.getNodeValue("Id"));
313         rPanelDescriptor.msDeckId = ::comphelper::getString(
314             aPanelNode.getNodeValue("DeckId"));
315         rPanelDescriptor.msHelpURL = ::comphelper::getString(
316             aPanelNode.getNodeValue("HelpURL"));
317         rPanelDescriptor.msLayout = ::comphelper::getString(
318             aPanelNode.getNodeValue("Layout"));
319         rPanelDescriptor.msImplementationURL = ::comphelper::getString(
320             aPanelNode.getNodeValue("ImplementationURL"));
321         rPanelDescriptor.mnOrderIndex = ::comphelper::getINT32(
322             aPanelNode.getNodeValue("OrderIndex"));
323         ReadContextList(aPanelNode.openNode("ContextList"), rPanelDescriptor.maContexts);
324     }
325 
326     // When there where invalid nodes then we have to adapt the size
327     // of the deck vector.
328     if (nWriteIndex<nCount)
329         maPanels.resize(nWriteIndex);
330 }
331 
332 
333 
334 
335 void ResourceManager::ReadContextList (
336     const ::utl::OConfigurationNode& rNode,
337     ::std::vector<EnumContext>& rContextContainer) const
338 {
339     const Sequence<OUString> aChildNodeNames (rNode.getNodeNames());
340     const sal_Int32 nCount (aChildNodeNames.getLength());
341     rContextContainer.resize(nCount);
342     for (sal_Int32 nIndex(0); nIndex<nCount; ++nIndex)
343     {
344         const ::utl::OConfigurationNode aChildNode (rNode.openNode(aChildNodeNames[nIndex]));
345         rContextContainer[nIndex] = EnumContext(
346             ::comphelper::getString(aChildNode.getNodeValue("Application")),
347             ::comphelper::getString(aChildNode.getNodeValue("ApplicationContext")));
348     }
349 }
350 
351 
352 
353 
354 void ResourceManager::ReadLegacyAddons (const Reference<frame::XFrame>& rxFrame)
355 {
356     // Get module name for given frame.
357     ::rtl::OUString sModuleName (GetModuleName(rxFrame));
358     if (sModuleName.getLength() == 0)
359         return;
360     if (maProcessedApplications.find(sModuleName) != maProcessedApplications.end())
361     {
362         // Addons for this application have already been read.
363         // There is nothing more to do.
364         return;
365     }
366 
367     // Mark module as processed.  Even when there is an error that
368     // prevents the configuration data from being read, this error
369     // will not be triggered a second time.
370     maProcessedApplications.insert(sModuleName);
371 
372     // Get access to the configuration root node for the application.
373     ::utl::OConfigurationTreeRoot aLegacyRootNode (GetLegacyAddonRootNode(sModuleName));
374     if ( ! aLegacyRootNode.isValid())
375         return;
376 
377     // Process child nodes.
378     ::std::vector<OUString> aMatchingNodeNames;
379     GetToolPanelNodeNames(aMatchingNodeNames, aLegacyRootNode);
380     const sal_Int32 nCount (aMatchingNodeNames.size());
381     size_t nDeckWriteIndex (maDecks.size());
382     size_t nPanelWriteIndex (maPanels.size());
383     maDecks.resize(maDecks.size() + nCount);
384     maPanels.resize(maPanels.size() + nCount);
385     for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex)
386     {
387         const OUString& rsNodeName (aMatchingNodeNames[nReadIndex]);
388         const ::utl::OConfigurationNode aChildNode (aLegacyRootNode.openNode(rsNodeName));
389         if ( ! aChildNode.isValid())
390             continue;
391 
392         DeckDescriptor& rDeckDescriptor (maDecks[nDeckWriteIndex++]);
393         rDeckDescriptor.msTitle = ::comphelper::getString(aChildNode.getNodeValue("UIName"));
394         rDeckDescriptor.msId = rsNodeName;
395         rDeckDescriptor.msIconURL = ::comphelper::getString(aChildNode.getNodeValue("ImageURL"));
396         rDeckDescriptor.msHighContrastIconURL = rDeckDescriptor.msIconURL;
397         rDeckDescriptor.msHelpURL = ::comphelper::getString(aChildNode.getNodeValue("HelpURL"));
398         rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle;
399         rDeckDescriptor.maContexts.resize(1);
400         rDeckDescriptor.maContexts.front() = EnumContext(sModuleName, A2S("any"));
401         rDeckDescriptor.mbIsEnabled = true;
402 
403         PanelDescriptor& rPanelDescriptor (maPanels[nPanelWriteIndex++]);
404         rPanelDescriptor.msTitle = ::comphelper::getString(aChildNode.getNodeValue("UIName"));
405         rPanelDescriptor.mbIsTitleBarOptional = false;
406         rPanelDescriptor.msId = rsNodeName;
407         rPanelDescriptor.msDeckId = rsNodeName;
408         rPanelDescriptor.msHelpURL = ::comphelper::getString(aChildNode.getNodeValue("HelpURL"));
409         rPanelDescriptor.maContexts.resize(1);
410         rPanelDescriptor.maContexts.front() = EnumContext(sModuleName, A2S("any"));
411         rPanelDescriptor.msLayout = A2S("full");
412         rPanelDescriptor.msImplementationURL = rsNodeName;
413     }
414 
415     // When there where invalid nodes then we have to adapt the size
416     // of the deck and panel vectors.
417     if (nDeckWriteIndex < maDecks.size())
418         maDecks.resize(nDeckWriteIndex);
419     if (nPanelWriteIndex < maPanels.size())
420         maPanels.resize(nPanelWriteIndex);
421 }
422 
423 
424 
425 
426 ::rtl::OUString ResourceManager::GetModuleName (
427     const cssu::Reference<css::frame::XFrame>& rxFrame)
428 {
429     try
430     {
431         const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory());
432         const Reference<frame::XModuleManager> xModuleManager (
433             aContext.createComponent("com.sun.star.frame.ModuleManager" ),
434             UNO_QUERY_THROW );
435         return xModuleManager->identify(rxFrame);
436     }
437     catch (const Exception&)
438     {
439         DBG_UNHANDLED_EXCEPTION();
440     }
441     return OUString();
442 }
443 
444 
445 
446 
447 ::utl::OConfigurationTreeRoot ResourceManager::GetLegacyAddonRootNode (
448     const ::rtl::OUString& rsModuleName) const
449 {
450     try
451     {
452         const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory());
453         const Reference<container::XNameAccess> xModuleAccess (
454             aContext.createComponent("com.sun.star.frame.ModuleManager"),
455             UNO_QUERY_THROW);
456         const ::comphelper::NamedValueCollection aModuleProperties (xModuleAccess->getByName(rsModuleName));
457         const ::rtl::OUString sWindowStateRef (aModuleProperties.getOrDefault(
458                 "ooSetupFactoryWindowStateConfigRef",
459                 ::rtl::OUString()));
460 
461         ::rtl::OUStringBuffer aPathComposer;
462         aPathComposer.appendAscii("org.openoffice.Office.UI.");
463         aPathComposer.append(sWindowStateRef);
464         aPathComposer.appendAscii("/UIElements/States");
465 
466         return ::utl::OConfigurationTreeRoot(aContext, aPathComposer.makeStringAndClear(), false);
467     }
468     catch( const Exception& )
469     {
470         DBG_UNHANDLED_EXCEPTION();
471     }
472 
473     return ::utl::OConfigurationTreeRoot();
474 }
475 
476 
477 
478 
479 void ResourceManager::GetToolPanelNodeNames (
480     ::std::vector<OUString>& rMatchingNames,
481     const ::utl::OConfigurationTreeRoot aRoot) const
482 {
483     Sequence<OUString> aChildNodeNames (aRoot.getNodeNames());
484     const sal_Int32 nCount (aChildNodeNames.getLength());
485     for (sal_Int32 nIndex(0); nIndex<nCount; ++nIndex)
486     {
487         if (aChildNodeNames[nIndex].matchAsciiL(
488                 RTL_CONSTASCII_STRINGPARAM( "private:resource/toolpanel/")))
489             rMatchingNames.push_back(aChildNodeNames[nIndex]);
490     }
491 }
492 
493 
494 
495 } } // end of namespace sfx2::sidebar
496