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