Branch libreoffice-5-0-4
[LibreOffice.git] / sfx2 / source / sidebar / ResourceManager.cxx
blob78fe32af6de0a600a07b8508c15fd419185d0ecf
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "ResourceManager.hxx"
21 #include <sfx2/sidebar/Tools.hxx>
23 #include <unotools/confignode.hxx>
24 #include <comphelper/processfactory.hxx>
25 #include <comphelper/namedvaluecollection.hxx>
26 #include <comphelper/types.hxx>
28 #include <rtl/ustrbuf.hxx>
29 #include <tools/diagnose_ex.h>
31 #include <com/sun/star/frame/ModuleManager.hpp>
33 #include <map>
35 using namespace css;
36 using namespace css::uno;
38 namespace sfx2 { namespace sidebar {
40 namespace
43 OUString getString(utl::OConfigurationNode const & aNode, const char* pNodeName)
45 return comphelper::getString(aNode.getNodeValue(pNodeName));
47 sal_Int32 getInt32(utl::OConfigurationNode const & aNode, const char* pNodeName)
49 return comphelper::getINT32(aNode.getNodeValue(pNodeName));
51 bool getBool(utl::OConfigurationNode const & aNode, const char* pNodeName)
53 return comphelper::getBOOL(aNode.getNodeValue(pNodeName));
56 } //end anonymous namespace
58 ResourceManager& ResourceManager::Instance()
60 static ResourceManager maInstance;
61 return maInstance;
64 ResourceManager::ResourceManager()
65 : maDecks(),
66 maPanels(),
67 maProcessedApplications(),
68 maMiscOptions()
70 ReadDeckList();
71 ReadPanelList();
74 ResourceManager::~ResourceManager()
78 const DeckDescriptor* ResourceManager::GetDeckDescriptor(const OUString& rsDeckId) const
80 DeckContainer::const_iterator iDeck;
81 for (iDeck = maDecks.begin(); iDeck != maDecks.end(); ++iDeck)
83 if (iDeck->mbExperimental && !maMiscOptions.IsExperimentalMode())
84 continue;
85 if (iDeck->msId.equals(rsDeckId))
86 return &*iDeck;
88 return NULL;
91 const PanelDescriptor* ResourceManager::GetPanelDescriptor(const OUString& rsPanelId) const
93 PanelContainer::const_iterator iPanel;
94 for (iPanel = maPanels.begin(); iPanel != maPanels.end(); ++iPanel)
96 if (iPanel->msId.equals(rsPanelId))
97 return &*iPanel;
99 return NULL;
102 void ResourceManager::SetIsDeckEnabled(const OUString& rsDeckId, const bool bIsEnabled)
104 DeckContainer::iterator iDeck;
105 for (iDeck = maDecks.begin(); iDeck != maDecks.end(); ++iDeck)
107 if (iDeck->mbExperimental && !maMiscOptions.IsExperimentalMode())
108 continue;
109 if (iDeck->msId.equals(rsDeckId))
111 iDeck->mbIsEnabled = bIsEnabled;
112 return;
117 const ResourceManager::DeckContextDescriptorContainer& ResourceManager::GetMatchingDecks (
118 DeckContextDescriptorContainer& rDecks,
119 const Context& rContext,
120 const bool bIsDocumentReadOnly,
121 const Reference<frame::XFrame>& rxFrame)
123 ReadLegacyAddons(rxFrame);
125 std::multimap<sal_Int32,DeckContextDescriptor> aOrderedIds;
126 DeckContainer::const_iterator iDeck;
127 for (iDeck = maDecks.begin(); iDeck != maDecks.end(); ++iDeck)
129 if (iDeck->mbExperimental && !maMiscOptions.IsExperimentalMode())
130 continue;
131 const DeckDescriptor& rDeckDescriptor (*iDeck);
132 if (rDeckDescriptor.maContextList.GetMatch(rContext) == NULL)
133 continue;
134 DeckContextDescriptor aDeckContextDescriptor;
135 aDeckContextDescriptor.msId = rDeckDescriptor.msId;
136 aDeckContextDescriptor.mbIsEnabled =
137 ! bIsDocumentReadOnly
138 || IsDeckEnabled(rDeckDescriptor.msId, rContext, rxFrame);
139 aOrderedIds.insert(::std::multimap<sal_Int32,DeckContextDescriptor>::value_type(
140 rDeckDescriptor.mnOrderIndex,
141 aDeckContextDescriptor));
144 std::multimap<sal_Int32,DeckContextDescriptor>::const_iterator iId;
145 for (iId = aOrderedIds.begin(); iId != aOrderedIds.end(); ++iId)
147 rDecks.push_back(iId->second);
150 return rDecks;
153 const ResourceManager::PanelContextDescriptorContainer& ResourceManager::GetMatchingPanels (
154 PanelContextDescriptorContainer& rPanelIds,
155 const Context& rContext,
156 const OUString& rsDeckId,
157 const Reference<frame::XFrame>& rxFrame)
159 ReadLegacyAddons(rxFrame);
161 std::multimap<sal_Int32, PanelContextDescriptor> aOrderedIds;
162 PanelContainer::const_iterator iPanel;
163 for (iPanel = maPanels.begin(); iPanel != maPanels.end(); ++iPanel)
165 const PanelDescriptor& rPanelDescriptor (*iPanel);
166 if (rPanelDescriptor.mbExperimental && !maMiscOptions.IsExperimentalMode())
167 continue;
168 if ( ! rPanelDescriptor.msDeckId.equals(rsDeckId))
169 continue;
171 const ContextList::Entry* pEntry = rPanelDescriptor.maContextList.GetMatch(rContext);
172 if (pEntry == NULL)
173 continue;
175 PanelContextDescriptor aPanelContextDescriptor;
176 aPanelContextDescriptor.msId = rPanelDescriptor.msId;
177 aPanelContextDescriptor.msMenuCommand = pEntry->msMenuCommand;
178 aPanelContextDescriptor.mbIsInitiallyVisible = pEntry->mbIsInitiallyVisible;
179 aPanelContextDescriptor.mbShowForReadOnlyDocuments = rPanelDescriptor.mbShowForReadOnlyDocuments;
180 aOrderedIds.insert(std::multimap<sal_Int32, PanelContextDescriptor>::value_type(
181 rPanelDescriptor.mnOrderIndex,
182 aPanelContextDescriptor));
185 std::multimap<sal_Int32,PanelContextDescriptor>::const_iterator iId;
186 for (iId = aOrderedIds.begin(); iId != aOrderedIds.end(); ++iId)
188 rPanelIds.push_back(iId->second);
191 return rPanelIds;
194 void ResourceManager::ReadDeckList()
196 const utl::OConfigurationTreeRoot aDeckRootNode(
197 comphelper::getProcessComponentContext(),
198 OUString("org.openoffice.Office.UI.Sidebar/Content/DeckList"),
199 false);
200 if (!aDeckRootNode.isValid())
201 return;
203 const Sequence<OUString> aDeckNodeNames (aDeckRootNode.getNodeNames());
204 const sal_Int32 nCount(aDeckNodeNames.getLength());
205 maDecks.resize(nCount);
206 sal_Int32 nWriteIndex(0);
207 for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex)
209 const utl::OConfigurationNode aDeckNode(aDeckRootNode.openNode(aDeckNodeNames[nReadIndex]));
210 if (!aDeckNode.isValid())
211 continue;
213 DeckDescriptor& rDeckDescriptor (maDecks[nWriteIndex++]);
215 rDeckDescriptor.msTitle = getString(aDeckNode, "Title");
216 rDeckDescriptor.msId = getString(aDeckNode, "Id");
217 rDeckDescriptor.msIconURL = getString(aDeckNode, "IconURL");
218 rDeckDescriptor.msHighContrastIconURL = getString(aDeckNode, "HighContrastIconURL");
219 rDeckDescriptor.msTitleBarIconURL = getString(aDeckNode, "TitleBarIconURL");
220 rDeckDescriptor.msHighContrastTitleBarIconURL = getString(aDeckNode, "HighContrastTitleBarIconURL");
221 rDeckDescriptor.msHelpURL = getString(aDeckNode, "HelpURL");
222 rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle;
223 rDeckDescriptor.mbIsEnabled = true;
224 rDeckDescriptor.mnOrderIndex = getInt32(aDeckNode, "OrderIndex");
225 rDeckDescriptor.mbExperimental = getBool(aDeckNode, "IsExperimental");
227 ReadContextList(
228 aDeckNode,
229 rDeckDescriptor.maContextList,
230 OUString());
233 // When there where invalid nodes then we have to adapt the size
234 // of the deck vector.
235 if (nWriteIndex<nCount)
236 maDecks.resize(nWriteIndex);
239 void ResourceManager::ReadPanelList()
241 const utl::OConfigurationTreeRoot aPanelRootNode(
242 comphelper::getProcessComponentContext(),
243 OUString("org.openoffice.Office.UI.Sidebar/Content/PanelList"),
244 false);
245 if (!aPanelRootNode.isValid())
246 return;
248 const Sequence<OUString> aPanelNodeNames (aPanelRootNode.getNodeNames());
249 const sal_Int32 nCount (aPanelNodeNames.getLength());
250 maPanels.resize(nCount);
251 sal_Int32 nWriteIndex (0);
252 for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex)
254 const utl::OConfigurationNode aPanelNode (aPanelRootNode.openNode(aPanelNodeNames[nReadIndex]));
255 if (!aPanelNode.isValid())
256 continue;
258 PanelDescriptor& rPanelDescriptor (maPanels[nWriteIndex++]);
260 rPanelDescriptor.msTitle = getString(aPanelNode, "Title");
261 rPanelDescriptor.mbIsTitleBarOptional = getBool(aPanelNode, "TitleBarIsOptional");
262 rPanelDescriptor.msId = getString(aPanelNode, "Id");
263 rPanelDescriptor.msDeckId = getString(aPanelNode, "DeckId");
264 rPanelDescriptor.msTitleBarIconURL = getString(aPanelNode, "TitleBarIconURL");
265 rPanelDescriptor.msHighContrastTitleBarIconURL = getString(aPanelNode, "HighContrastTitleBarIconURL");
266 rPanelDescriptor.msHelpURL = getString(aPanelNode, "HelpURL");
267 rPanelDescriptor.msImplementationURL = getString(aPanelNode, "ImplementationURL");
268 rPanelDescriptor.mnOrderIndex = getInt32(aPanelNode, "OrderIndex");
269 rPanelDescriptor.mbShowForReadOnlyDocuments = getBool(aPanelNode, "ShowForReadOnlyDocument");
270 rPanelDescriptor.mbWantsCanvas = getBool(aPanelNode, "WantsCanvas");
271 rPanelDescriptor.mbExperimental = getBool(aPanelNode, "IsExperimental");
272 const OUString sDefaultMenuCommand(getString(aPanelNode, "DefaultMenuCommand"));
274 ReadContextList(aPanelNode, rPanelDescriptor.maContextList, sDefaultMenuCommand);
277 // When there where invalid nodes then we have to adapt the size
278 // of the deck vector.
279 if (nWriteIndex<nCount)
280 maPanels.resize(nWriteIndex);
283 void ResourceManager::ReadContextList (
284 const utl::OConfigurationNode& rParentNode,
285 ContextList& rContextList,
286 const OUString& rsDefaultMenuCommand)
288 const Any aValue = rParentNode.getNodeValue("ContextList");
289 Sequence<OUString> aValues;
290 sal_Int32 nCount;
291 if (aValue >>= aValues)
292 nCount = aValues.getLength();
293 else
294 nCount = 0;
296 for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex)
298 const OUString sValue (aValues[nIndex]);
299 sal_Int32 nCharacterIndex (0);
300 const OUString sApplicationName (sValue.getToken(0, ',', nCharacterIndex).trim());
301 if (nCharacterIndex < 0)
303 if (sApplicationName.getLength() == 0)
305 // This is a valid case: in the XML file the separator
306 // was used as terminator. Using it in the last line
307 // creates an additional but empty entry.
308 break;
310 else
312 OSL_FAIL("expecting three or four values per ContextList entry, separated by comma");
313 continue;
317 const OUString sContextName(sValue.getToken(0, ',', nCharacterIndex).trim());
318 if (nCharacterIndex < 0)
320 OSL_FAIL("expecting three or four values per ContextList entry, separated by comma");
321 continue;
324 const OUString sInitialState(sValue.getToken(0, ',', nCharacterIndex).trim());
326 // The fourth argument is optional.
327 const OUString sMenuCommandOverride(
328 nCharacterIndex < 0
329 ? OUString()
330 : sValue.getToken(0, ',', nCharacterIndex).trim());
332 const OUString sMenuCommand(
333 sMenuCommandOverride.getLength() > 0
334 ? (sMenuCommandOverride == "none"
335 ? OUString()
336 : sMenuCommandOverride)
337 : rsDefaultMenuCommand);
339 // Setup a list of application enums. Note that the
340 // application name may result in more than one value (eg
341 // DrawImpress will result in two enums, one for Draw and one
342 // for Impress).
343 std::vector<EnumContext::Application> aApplications;
344 EnumContext::Application eApplication (EnumContext::GetApplicationEnum(sApplicationName));
345 if (eApplication == EnumContext::Application_None
346 && !sApplicationName.equals(EnumContext::GetApplicationName(EnumContext::Application_None)))
348 // Handle some special names: abbreviations that make
349 // context descriptions more readable.
350 if (sApplicationName == "Writer")
351 aApplications.push_back(EnumContext::Application_Writer);
352 else if (sApplicationName == "Calc")
353 aApplications.push_back(EnumContext::Application_Calc);
354 else if (sApplicationName == "Draw")
355 aApplications.push_back(EnumContext::Application_Draw);
356 else if (sApplicationName == "Impress")
357 aApplications.push_back(EnumContext::Application_Impress);
358 else if (sApplicationName == "DrawImpress")
360 // A special case among the special names: it is
361 // common to use the same context descriptions for
362 // both Draw and Impress. This special case helps to
363 // avoid duplication in the .xcu file.
364 aApplications.push_back(EnumContext::Application_Draw);
365 aApplications.push_back(EnumContext::Application_Impress);
367 else if (sApplicationName == "WriterVariants")
369 // Another special case for all Writer variants.
370 aApplications.push_back(EnumContext::Application_Writer);
371 aApplications.push_back(EnumContext::Application_WriterGlobal);
372 aApplications.push_back(EnumContext::Application_WriterWeb);
373 aApplications.push_back(EnumContext::Application_WriterXML);
374 aApplications.push_back(EnumContext::Application_WriterForm);
375 aApplications.push_back(EnumContext::Application_WriterReport);
377 else
379 OSL_FAIL("application name not recognized");
380 continue;
383 else
385 // No conversion of the application name necessary.
386 aApplications.push_back(eApplication);
389 // Setup the actual context enum.
390 const EnumContext::Context eContext (EnumContext::GetContextEnum(sContextName));
391 if (eContext == EnumContext::Context_Unknown)
393 OSL_FAIL("context name not recognized");
394 continue;
397 // Setup the flag that controls whether a deck/pane is
398 // initially visible/expanded.
399 bool bIsInitiallyVisible;
400 if (sInitialState == "visible")
401 bIsInitiallyVisible = true;
402 else if (sInitialState == "hidden")
403 bIsInitiallyVisible = false;
404 else
406 OSL_FAIL("unrecognized state");
407 continue;
410 // Add context descriptors.
411 std::vector<EnumContext::Application>::const_iterator iApplication;
412 for (iApplication = aApplications.begin(); iApplication != aApplications.end(); ++iApplication)
414 if (*iApplication != EnumContext::Application_None)
416 rContextList.AddContextDescription(
417 Context(
418 EnumContext::GetApplicationName(*iApplication),
419 EnumContext::GetContextName(eContext)),
420 bIsInitiallyVisible,
421 sMenuCommand);
427 void ResourceManager::ReadLegacyAddons (const Reference<frame::XFrame>& rxFrame)
429 // Get module name for given frame.
430 OUString sModuleName (Tools::GetModuleName(rxFrame));
431 if (sModuleName.getLength() == 0)
432 return;
433 if (maProcessedApplications.find(sModuleName) != maProcessedApplications.end())
435 // Addons for this application have already been read.
436 // There is nothing more to do.
437 return;
440 // Mark module as processed. Even when there is an error that
441 // prevents the configuration data from being read, this error
442 // will not be triggered a second time.
443 maProcessedApplications.insert(sModuleName);
445 // Get access to the configuration root node for the application.
446 utl::OConfigurationTreeRoot aLegacyRootNode (GetLegacyAddonRootNode(sModuleName));
447 if (!aLegacyRootNode.isValid())
448 return;
450 // Process child nodes.
451 std::vector<OUString> aMatchingNodeNames;
452 GetToolPanelNodeNames(aMatchingNodeNames, aLegacyRootNode);
453 const sal_Int32 nCount (aMatchingNodeNames.size());
454 size_t nDeckWriteIndex (maDecks.size());
455 size_t nPanelWriteIndex (maPanels.size());
456 maDecks.resize(maDecks.size() + nCount);
457 maPanels.resize(maPanels.size() + nCount);
458 for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex)
460 const OUString& rsNodeName (aMatchingNodeNames[nReadIndex]);
461 const utl::OConfigurationNode aChildNode (aLegacyRootNode.openNode(rsNodeName));
462 if (!aChildNode.isValid())
463 continue;
465 if ( rsNodeName == "private:resource/toolpanel/DrawingFramework/CustomAnimations" ||
466 rsNodeName == "private:resource/toolpanel/DrawingFramework/Layouts" ||
467 rsNodeName == "private:resource/toolpanel/DrawingFramework/MasterPages" ||
468 rsNodeName == "private:resource/toolpanel/DrawingFramework/SlideTransitions" ||
469 rsNodeName == "private:resource/toolpanel/DrawingFramework/TableDesign" )
470 continue;
472 DeckDescriptor& rDeckDescriptor (maDecks[nDeckWriteIndex++]);
473 rDeckDescriptor.msTitle = getString(aChildNode, "UIName");
474 rDeckDescriptor.msId = rsNodeName;
475 rDeckDescriptor.msIconURL = getString(aChildNode, "ImageURL");
476 rDeckDescriptor.msHighContrastIconURL = rDeckDescriptor.msIconURL;
477 rDeckDescriptor.msTitleBarIconURL.clear();
478 rDeckDescriptor.msHighContrastTitleBarIconURL.clear();
479 rDeckDescriptor.msHelpURL = getString(aChildNode, "HelpURL");
480 rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle;
481 rDeckDescriptor.mbIsEnabled = true;
482 rDeckDescriptor.mnOrderIndex = 100000 + nReadIndex;
483 rDeckDescriptor.maContextList.AddContextDescription(Context(sModuleName, OUString("any")), true, OUString());
485 PanelDescriptor& rPanelDescriptor (maPanels[nPanelWriteIndex++]);
486 rPanelDescriptor.msTitle = getString(aChildNode, "UIName");
487 rPanelDescriptor.mbIsTitleBarOptional = true;
488 rPanelDescriptor.msId = rsNodeName;
489 rPanelDescriptor.msDeckId = rsNodeName;
490 rPanelDescriptor.msTitleBarIconURL.clear();
491 rPanelDescriptor.msHighContrastTitleBarIconURL.clear();
492 rPanelDescriptor.msHelpURL = getString(aChildNode, "HelpURL");
493 rPanelDescriptor.msImplementationURL = rsNodeName;
494 rPanelDescriptor.mnOrderIndex = 100000 + nReadIndex;
495 rPanelDescriptor.mbShowForReadOnlyDocuments = false;
496 rPanelDescriptor.mbWantsCanvas = false;
497 rPanelDescriptor.maContextList.AddContextDescription(Context(sModuleName, OUString("any")), true, OUString());
500 // When there where invalid nodes then we have to adapt the size
501 // of the deck and panel vectors.
502 if (nDeckWriteIndex < maDecks.size())
503 maDecks.resize(nDeckWriteIndex);
504 if (nPanelWriteIndex < maPanels.size())
505 maPanels.resize(nPanelWriteIndex);
508 void ResourceManager::StorePanelExpansionState (
509 const OUString& rsPanelId,
510 const bool bExpansionState,
511 const Context& rContext)
513 PanelContainer::iterator iPanel;
514 for (iPanel = maPanels.begin(); iPanel != maPanels.end(); ++iPanel)
516 if (iPanel->msId.equals(rsPanelId))
518 ContextList::Entry* pEntry(iPanel->maContextList.GetMatch(rContext));
519 if (pEntry != NULL)
520 pEntry->mbIsInitiallyVisible = bExpansionState;
525 utl::OConfigurationTreeRoot ResourceManager::GetLegacyAddonRootNode (const OUString& rsModuleName)
529 const Reference<XComponentContext> xContext(comphelper::getProcessComponentContext());
530 const Reference<frame::XModuleManager2> xModuleAccess = frame::ModuleManager::create(xContext);
531 const comphelper::NamedValueCollection aModuleProperties(xModuleAccess->getByName(rsModuleName));
532 const OUString sWindowStateRef(aModuleProperties.getOrDefault(
533 "ooSetupFactoryWindowStateConfigRef",
534 OUString()));
536 OUStringBuffer aPathComposer;
537 aPathComposer.appendAscii("org.openoffice.Office.UI.");
538 aPathComposer.append(sWindowStateRef);
539 aPathComposer.appendAscii("/UIElements/States");
541 return utl::OConfigurationTreeRoot(xContext, aPathComposer.makeStringAndClear(), false);
543 catch (const Exception&)
545 DBG_UNHANDLED_EXCEPTION();
548 return utl::OConfigurationTreeRoot();
551 void ResourceManager::GetToolPanelNodeNames (
552 std::vector<OUString>& rMatchingNames,
553 const utl::OConfigurationTreeRoot& aRoot)
555 Sequence<OUString> aChildNodeNames (aRoot.getNodeNames());
556 const sal_Int32 nCount (aChildNodeNames.getLength());
557 for (sal_Int32 nIndex(0); nIndex<nCount; ++nIndex)
559 if (aChildNodeNames[nIndex].startsWith( "private:resource/toolpanel/" ))
560 rMatchingNames.push_back(aChildNodeNames[nIndex]);
564 bool ResourceManager::IsDeckEnabled (
565 const OUString& rsDeckId,
566 const Context& rContext,
567 const Reference<frame::XFrame>& rxFrame)
569 // Check if any panel that matches the current context can be
570 // displayed.
571 ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors;
572 ResourceManager::Instance().GetMatchingPanels(aPanelContextDescriptors,
573 rContext, rsDeckId, rxFrame);
575 ResourceManager::PanelContextDescriptorContainer::const_iterator iPanel;
576 for (iPanel = aPanelContextDescriptors.begin(); iPanel != aPanelContextDescriptors.end(); ++iPanel)
578 if (iPanel->mbShowForReadOnlyDocuments)
579 return true;
582 return false;
585 } } // end of namespace sfx2::sidebar
587 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */