2 * This file is part of the LibreOffice project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 * This file incorporates work covered by the following license notice:
10 * Licensed to the Apache Software Foundation (ASF) under one or more
11 * contributor license agreements. See the NOTICE file distributed
12 * with this work for additional information regarding copyright
13 * ownership. The ASF licenses this file to you under the Apache
14 * License, Version 2.0 (the "License"); you may not use this file
15 * except in compliance with the License. You may obtain a copy of
16 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 #include "ResourceManager.hxx"
20 #include "sfx2/sidebar/Tools.hxx"
22 #include <unotools/confignode.hxx>
23 #include <comphelper/componentcontext.hxx>
24 #include <comphelper/processfactory.hxx>
25 #include <comphelper/namedvaluecollection.hxx>
26 #include <comphelper/types.hxx>
27 #include <comphelper/stlunosequence.hxx>
29 #include <rtl/ustrbuf.hxx>
30 #include <tools/diagnose_ex.h>
32 #include <com/sun/star/frame/XModuleManager.hpp>
36 using ::rtl::OUString
;
40 namespace sfx2
{ namespace sidebar
{
42 class ResourceManager::Deleter
45 void operator() (ResourceManager
* pObject
)
51 ResourceManager
& ResourceManager::Instance (void)
53 static ResourceManager maInstance
;
60 ResourceManager::ResourceManager (void)
63 maProcessedApplications()
72 ResourceManager::~ResourceManager (void)
81 const DeckDescriptor
* ResourceManager::GetDeckDescriptor (
82 const ::rtl::OUString
& rsDeckId
) const
84 for (DeckContainer::const_iterator
85 iDeck(maDecks
.begin()),
90 if (iDeck
->msId
.equals(rsDeckId
))
99 const PanelDescriptor
* ResourceManager::GetPanelDescriptor (
100 const ::rtl::OUString
& rsPanelId
) const
102 for (PanelContainer::const_iterator
103 iPanel(maPanels
.begin()),
104 iEnd(maPanels
.end());
108 if (iPanel
->msId
.equals(rsPanelId
))
117 void ResourceManager::SetIsDeckEnabled (
118 const ::rtl::OUString
& rsDeckId
,
119 const bool bIsEnabled
)
121 for (DeckContainer::iterator
122 iDeck(maDecks
.begin()),
127 if (iDeck
->msId
.equals(rsDeckId
))
129 iDeck
->mbIsEnabled
= bIsEnabled
;
138 const ResourceManager::DeckContextDescriptorContainer
& ResourceManager::GetMatchingDecks (
139 DeckContextDescriptorContainer
& rDecks
,
140 const Context
& rContext
,
141 const bool bIsDocumentReadOnly
,
142 const Reference
<frame::XFrame
>& rxFrame
)
144 ReadLegacyAddons(rxFrame
);
146 ::std::multimap
<sal_Int32
,DeckContextDescriptor
> aOrderedIds
;
147 for (DeckContainer::const_iterator
148 iDeck(maDecks
.begin()),
149 iEnd (maDecks
.end());
153 const DeckDescriptor
& rDeckDescriptor (*iDeck
);
154 if (rDeckDescriptor
.maContextList
.GetMatch(rContext
) == NULL
)
156 DeckContextDescriptor aDeckContextDescriptor
;
157 aDeckContextDescriptor
.msId
= rDeckDescriptor
.msId
;
158 aDeckContextDescriptor
.mbIsEnabled
=
159 ! bIsDocumentReadOnly
160 || IsDeckEnabled(rDeckDescriptor
.msId
, rContext
, rxFrame
);
161 aOrderedIds
.insert(::std::multimap
<sal_Int32
,DeckContextDescriptor
>::value_type(
162 rDeckDescriptor
.mnOrderIndex
,
163 aDeckContextDescriptor
));
166 for (::std::multimap
<sal_Int32
,DeckContextDescriptor
>::const_iterator
167 iId(aOrderedIds
.begin()),
168 iEnd(aOrderedIds
.end());
172 rDecks
.push_back(iId
->second
);
181 const ResourceManager::PanelContextDescriptorContainer
& ResourceManager::GetMatchingPanels (
182 PanelContextDescriptorContainer
& rPanelIds
,
183 const Context
& rContext
,
184 const ::rtl::OUString
& rsDeckId
,
185 const Reference
<frame::XFrame
>& rxFrame
)
187 ReadLegacyAddons(rxFrame
);
189 ::std::multimap
<sal_Int32
,PanelContextDescriptor
> aOrderedIds
;
190 for (PanelContainer::const_iterator
191 iPanel(maPanels
.begin()),
192 iEnd(maPanels
.end());
196 const PanelDescriptor
& rPanelDescriptor (*iPanel
);
197 if ( ! rPanelDescriptor
.msDeckId
.equals(rsDeckId
))
200 const ContextList::Entry
* pEntry
= rPanelDescriptor
.maContextList
.GetMatch(rContext
);
204 PanelContextDescriptor aPanelContextDescriptor
;
205 aPanelContextDescriptor
.msId
= rPanelDescriptor
.msId
;
206 aPanelContextDescriptor
.msMenuCommand
= pEntry
->msMenuCommand
;
207 aPanelContextDescriptor
.mbIsInitiallyVisible
= pEntry
->mbIsInitiallyVisible
;
208 aPanelContextDescriptor
.mbShowForReadOnlyDocuments
= rPanelDescriptor
.mbShowForReadOnlyDocuments
;
209 aOrderedIds
.insert(::std::multimap
<sal_Int32
,PanelContextDescriptor
>::value_type(
210 rPanelDescriptor
.mnOrderIndex
,
211 aPanelContextDescriptor
));
214 for (::std::multimap
<sal_Int32
,PanelContextDescriptor
>::const_iterator
215 iId(aOrderedIds
.begin()),
216 iEnd(aOrderedIds
.end());
220 rPanelIds
.push_back(iId
->second
);
229 void ResourceManager::ReadDeckList (void)
231 const ::utl::OConfigurationTreeRoot
aDeckRootNode (
232 ::comphelper::getProcessComponentContext(),
233 A2S("org.openoffice.Office.UI.Sidebar/Content/DeckList"),
235 if ( ! aDeckRootNode
.isValid() )
238 const Sequence
<OUString
> aDeckNodeNames (aDeckRootNode
.getNodeNames());
239 const sal_Int32
nCount (aDeckNodeNames
.getLength());
240 maDecks
.resize(nCount
);
241 sal_Int32
nWriteIndex(0);
242 for (sal_Int32
nReadIndex(0); nReadIndex
<nCount
; ++nReadIndex
)
244 const ::utl::OConfigurationNode
aDeckNode (aDeckRootNode
.openNode(aDeckNodeNames
[nReadIndex
]));
245 if ( ! aDeckNode
.isValid())
248 DeckDescriptor
& rDeckDescriptor (maDecks
[nWriteIndex
++]);
250 rDeckDescriptor
.msTitle
= ::comphelper::getString(
251 aDeckNode
.getNodeValue("Title"));
252 rDeckDescriptor
.msId
= ::comphelper::getString(
253 aDeckNode
.getNodeValue("Id"));
254 rDeckDescriptor
.msIconURL
= ::comphelper::getString(
255 aDeckNode
.getNodeValue("IconURL"));
256 rDeckDescriptor
.msHighContrastIconURL
= ::comphelper::getString(
257 aDeckNode
.getNodeValue("HighContrastIconURL"));
258 rDeckDescriptor
.msTitleBarIconURL
= ::comphelper::getString(
259 aDeckNode
.getNodeValue("TitleBarIconURL"));
260 rDeckDescriptor
.msHighContrastTitleBarIconURL
= ::comphelper::getString(
261 aDeckNode
.getNodeValue("HighContrastTitleBarIconURL"));
262 rDeckDescriptor
.msHelpURL
= ::comphelper::getString(
263 aDeckNode
.getNodeValue("HelpURL"));
264 rDeckDescriptor
.msHelpText
= rDeckDescriptor
.msTitle
;
265 rDeckDescriptor
.mbIsEnabled
= true;
266 rDeckDescriptor
.mnOrderIndex
= ::comphelper::getINT32(
267 aDeckNode
.getNodeValue("OrderIndex"));
271 rDeckDescriptor
.maContextList
,
275 // When there where invalid nodes then we have to adapt the size
276 // of the deck vector.
277 if (nWriteIndex
<nCount
)
278 maDecks
.resize(nWriteIndex
);
284 void ResourceManager::ReadPanelList (void)
286 const ::utl::OConfigurationTreeRoot
aPanelRootNode (
287 ::comphelper::getProcessComponentContext(),
288 A2S("org.openoffice.Office.UI.Sidebar/Content/PanelList"),
290 if ( ! aPanelRootNode
.isValid() )
293 const Sequence
<OUString
> aPanelNodeNames (aPanelRootNode
.getNodeNames());
294 const sal_Int32
nCount (aPanelNodeNames
.getLength());
295 maPanels
.resize(nCount
);
296 sal_Int32
nWriteIndex (0);
297 for (sal_Int32
nReadIndex(0); nReadIndex
<nCount
; ++nReadIndex
)
299 const ::utl::OConfigurationNode
aPanelNode (aPanelRootNode
.openNode(aPanelNodeNames
[nReadIndex
]));
300 if ( ! aPanelNode
.isValid())
303 PanelDescriptor
& rPanelDescriptor (maPanels
[nWriteIndex
++]);
305 rPanelDescriptor
.msTitle
= ::comphelper::getString(
306 aPanelNode
.getNodeValue("Title"));
307 rPanelDescriptor
.mbIsTitleBarOptional
= ::comphelper::getBOOL(
308 aPanelNode
.getNodeValue("TitleBarIsOptional"));
309 rPanelDescriptor
.msId
= ::comphelper::getString(
310 aPanelNode
.getNodeValue("Id"));
311 rPanelDescriptor
.msDeckId
= ::comphelper::getString(
312 aPanelNode
.getNodeValue("DeckId"));
313 rPanelDescriptor
.msTitleBarIconURL
= ::comphelper::getString(
314 aPanelNode
.getNodeValue("TitleBarIconURL"));
315 rPanelDescriptor
.msHighContrastTitleBarIconURL
= ::comphelper::getString(
316 aPanelNode
.getNodeValue("HighContrastTitleBarIconURL"));
317 rPanelDescriptor
.msHelpURL
= ::comphelper::getString(
318 aPanelNode
.getNodeValue("HelpURL"));
319 rPanelDescriptor
.msImplementationURL
= ::comphelper::getString(
320 aPanelNode
.getNodeValue("ImplementationURL"));
321 rPanelDescriptor
.mnOrderIndex
= ::comphelper::getINT32(
322 aPanelNode
.getNodeValue("OrderIndex"));
323 rPanelDescriptor
.mbShowForReadOnlyDocuments
= ::comphelper::getBOOL(
324 aPanelNode
.getNodeValue("ShowForReadOnlyDocument"));
325 rPanelDescriptor
.mbWantsCanvas
= ::comphelper::getBOOL(
326 aPanelNode
.getNodeValue("WantsCanvas"));
327 const OUString
sDefaultMenuCommand (::comphelper::getString(
328 aPanelNode
.getNodeValue("DefaultMenuCommand")));
332 rPanelDescriptor
.maContextList
,
333 sDefaultMenuCommand
);
336 // When there where invalid nodes then we have to adapt the size
337 // of the deck vector.
338 if (nWriteIndex
<nCount
)
339 maPanels
.resize(nWriteIndex
);
345 void ResourceManager::ReadContextList (
346 const ::utl::OConfigurationNode
& rParentNode
,
347 ContextList
& rContextList
,
348 const OUString
& rsDefaultMenuCommand
) const
350 const Any aValue
= rParentNode
.getNodeValue("ContextList");
351 Sequence
<OUString
> aValues
;
353 if (aValue
>>= aValues
)
354 nCount
= aValues
.getLength();
358 for (sal_Int32 nIndex
=0; nIndex
<nCount
; ++nIndex
)
360 const OUString
sValue (aValues
[nIndex
]);
361 sal_Int32
nCharacterIndex (0);
362 const OUString
sApplicationName (sValue
.getToken(0, ',', nCharacterIndex
).trim());
363 if (nCharacterIndex
< 0)
365 if (sApplicationName
.getLength() == 0)
367 // This is a valid case: in the XML file the separator
368 // was used as terminator. Using it in the last line
369 // creates an additional but empty entry.
374 OSL_ASSERT("expecting three or four values per ContextList entry, separated by comma");
379 const OUString
sContextName (sValue
.getToken(0, ',', nCharacterIndex
).trim());
380 if (nCharacterIndex
< 0)
382 OSL_ASSERT("expecting three or four values per ContextList entry, separated by comma");
386 const OUString
sInitialState (sValue
.getToken(0, ',', nCharacterIndex
).trim());
388 // The fourth argument is optional.
389 const OUString
sMenuCommandOverride (
392 : sValue
.getToken(0, ',', nCharacterIndex
).trim());
393 const OUString
sMenuCommand (
394 sMenuCommandOverride
.getLength()>0
395 ? (sMenuCommandOverride
.equalsAscii("none")
397 : sMenuCommandOverride
)
398 : rsDefaultMenuCommand
);
400 // Setup a list of application enums. Note that the
401 // application name may result in more than one value (eg
402 // DrawImpress will result in two enums, one for Draw and one
404 ::std::vector
<EnumContext::Application
> aApplications
;
405 EnumContext::Application
eApplication (EnumContext::GetApplicationEnum(sApplicationName
));
406 if (eApplication
== EnumContext::Application_None
407 && !sApplicationName
.equals(EnumContext::GetApplicationName(EnumContext::Application_None
)))
409 // Handle some special names: abbreviations that make
410 // context descriptions more readable.
411 if (sApplicationName
.equalsAscii("Writer"))
412 aApplications
.push_back(EnumContext::Application_Writer
);
413 else if (sApplicationName
.equalsAscii("Calc"))
414 aApplications
.push_back(EnumContext::Application_Calc
);
415 else if (sApplicationName
.equalsAscii("Draw"))
416 aApplications
.push_back(EnumContext::Application_Draw
);
417 else if (sApplicationName
.equalsAscii("Impress"))
418 aApplications
.push_back(EnumContext::Application_Impress
);
419 else if (sApplicationName
.equalsAscii("DrawImpress"))
421 // A special case among the special names: it is
422 // common to use the same context descriptions for
423 // both Draw and Impress. This special case helps to
424 // avoid duplication in the .xcu file.
425 aApplications
.push_back(EnumContext::Application_Draw
);
426 aApplications
.push_back(EnumContext::Application_Impress
);
428 else if (sApplicationName
.equalsAscii("WriterVariants"))
430 // Another special case for all Writer variants.
431 aApplications
.push_back(EnumContext::Application_Writer
);
432 aApplications
.push_back(EnumContext::Application_WriterGlobal
);
433 aApplications
.push_back(EnumContext::Application_WriterWeb
);
434 aApplications
.push_back(EnumContext::Application_WriterXML
);
435 aApplications
.push_back(EnumContext::Application_WriterForm
);
436 aApplications
.push_back(EnumContext::Application_WriterReport
);
440 OSL_ASSERT("application name not recognized");
446 // No conversion of the application name necessary.
447 aApplications
.push_back(eApplication
);
450 // Setup the actual context enum.
451 const EnumContext::Context
eContext (EnumContext::GetContextEnum(sContextName
));
452 if (eContext
== EnumContext::Context_Unknown
)
454 OSL_ASSERT("context name not recognized");
458 // Setup the flag that controls whether a deck/pane is
459 // initially visible/expanded.
460 bool bIsInitiallyVisible
;
461 if (sInitialState
.equalsAscii("visible"))
462 bIsInitiallyVisible
= true;
463 else if (sInitialState
.equalsAscii("hidden"))
464 bIsInitiallyVisible
= false;
467 OSL_ASSERT("unrecognized state");
471 // Add context descriptors.
472 for (::std::vector
<EnumContext::Application
>::const_iterator
473 iApplication(aApplications
.begin()),
474 iEnd(aApplications
.end());
478 if (*iApplication
!= EnumContext::Application_None
)
479 rContextList
.AddContextDescription(
481 EnumContext::GetApplicationName(*iApplication
),
482 EnumContext::GetContextName(eContext
)),
492 void ResourceManager::ReadLegacyAddons (const Reference
<frame::XFrame
>& rxFrame
)
494 // Get module name for given frame.
495 ::rtl::OUString
sModuleName (Tools::GetModuleName(rxFrame
));
496 if (sModuleName
.getLength() == 0)
498 if (maProcessedApplications
.find(sModuleName
) != maProcessedApplications
.end())
500 // Addons for this application have already been read.
501 // There is nothing more to do.
505 // Mark module as processed. Even when there is an error that
506 // prevents the configuration data from being read, this error
507 // will not be triggered a second time.
508 maProcessedApplications
.insert(sModuleName
);
510 // Get access to the configuration root node for the application.
511 ::utl::OConfigurationTreeRoot
aLegacyRootNode (GetLegacyAddonRootNode(sModuleName
));
512 if ( ! aLegacyRootNode
.isValid())
515 // Process child nodes.
516 ::std::vector
<OUString
> aMatchingNodeNames
;
517 GetToolPanelNodeNames(aMatchingNodeNames
, aLegacyRootNode
);
518 const sal_Int32
nCount (aMatchingNodeNames
.size());
519 size_t nDeckWriteIndex (maDecks
.size());
520 size_t nPanelWriteIndex (maPanels
.size());
521 maDecks
.resize(maDecks
.size() + nCount
);
522 maPanels
.resize(maPanels
.size() + nCount
);
523 for (sal_Int32
nReadIndex(0); nReadIndex
<nCount
; ++nReadIndex
)
525 const OUString
& rsNodeName (aMatchingNodeNames
[nReadIndex
]);
526 const ::utl::OConfigurationNode
aChildNode (aLegacyRootNode
.openNode(rsNodeName
));
527 if ( ! aChildNode
.isValid())
530 if ( rsNodeName
== "private:resource/toolpanel/DrawingFramework/CustomAnimations" ||
531 rsNodeName
== "private:resource/toolpanel/DrawingFramework/Layouts" ||
532 rsNodeName
== "private:resource/toolpanel/DrawingFramework/MasterPages" ||
533 rsNodeName
== "private:resource/toolpanel/DrawingFramework/SlideTransitions" ||
534 rsNodeName
== "private:resource/toolpanel/DrawingFramework/TableDesign" )
537 DeckDescriptor
& rDeckDescriptor (maDecks
[nDeckWriteIndex
++]);
538 rDeckDescriptor
.msTitle
= ::comphelper::getString(aChildNode
.getNodeValue("UIName"));
539 rDeckDescriptor
.msId
= rsNodeName
;
540 rDeckDescriptor
.msIconURL
= ::comphelper::getString(aChildNode
.getNodeValue("ImageURL"));
541 rDeckDescriptor
.msHighContrastIconURL
= rDeckDescriptor
.msIconURL
;
542 rDeckDescriptor
.msTitleBarIconURL
= OUString();
543 rDeckDescriptor
.msHighContrastTitleBarIconURL
= OUString();
544 rDeckDescriptor
.msHelpURL
= ::comphelper::getString(aChildNode
.getNodeValue("HelpURL"));
545 rDeckDescriptor
.msHelpText
= rDeckDescriptor
.msTitle
;
546 rDeckDescriptor
.mbIsEnabled
= true;
547 rDeckDescriptor
.mnOrderIndex
= 100000 + nReadIndex
;
548 rDeckDescriptor
.maContextList
.AddContextDescription(Context(sModuleName
, A2S("any")), true, OUString());
550 PanelDescriptor
& rPanelDescriptor (maPanels
[nPanelWriteIndex
++]);
551 rPanelDescriptor
.msTitle
= ::comphelper::getString(aChildNode
.getNodeValue("UIName"));
552 rPanelDescriptor
.mbIsTitleBarOptional
= true;
553 rPanelDescriptor
.msId
= rsNodeName
;
554 rPanelDescriptor
.msDeckId
= rsNodeName
;
555 rPanelDescriptor
.msTitleBarIconURL
= OUString();
556 rPanelDescriptor
.msHighContrastTitleBarIconURL
= OUString();
557 rPanelDescriptor
.msHelpURL
= ::comphelper::getString(aChildNode
.getNodeValue("HelpURL"));
558 rPanelDescriptor
.msImplementationURL
= rsNodeName
;
559 rPanelDescriptor
.mnOrderIndex
= 100000 + nReadIndex
;
560 rPanelDescriptor
.mbShowForReadOnlyDocuments
= false;
561 rPanelDescriptor
.mbWantsCanvas
= false;
562 rPanelDescriptor
.maContextList
.AddContextDescription(Context(sModuleName
, A2S("any")), true, OUString());
565 // When there where invalid nodes then we have to adapt the size
566 // of the deck and panel vectors.
567 if (nDeckWriteIndex
< maDecks
.size())
568 maDecks
.resize(nDeckWriteIndex
);
569 if (nPanelWriteIndex
< maPanels
.size())
570 maPanels
.resize(nPanelWriteIndex
);
576 void ResourceManager::StorePanelExpansionState (
577 const ::rtl::OUString
& rsPanelId
,
578 const bool bExpansionState
,
579 const Context
& rContext
)
581 for (PanelContainer::iterator
582 iPanel(maPanels
.begin()),
583 iEnd(maPanels
.end());
587 if (iPanel
->msId
.equals(rsPanelId
))
589 ContextList::Entry
* pEntry (
590 iPanel
->maContextList
.GetMatch (rContext
));
592 pEntry
->mbIsInitiallyVisible
= bExpansionState
;
600 ::utl::OConfigurationTreeRoot
ResourceManager::GetLegacyAddonRootNode (
601 const ::rtl::OUString
& rsModuleName
) const
605 const ::comphelper::ComponentContext
aContext (::comphelper::getProcessServiceFactory());
606 const Reference
<container::XNameAccess
> xModuleAccess (
607 aContext
.createComponent("com.sun.star.frame.ModuleManager"),
609 const ::comphelper::NamedValueCollection
aModuleProperties (xModuleAccess
->getByName(rsModuleName
));
610 const ::rtl::OUString
sWindowStateRef (aModuleProperties
.getOrDefault(
611 "ooSetupFactoryWindowStateConfigRef",
614 ::rtl::OUStringBuffer aPathComposer
;
615 aPathComposer
.appendAscii("org.openoffice.Office.UI.");
616 aPathComposer
.append(sWindowStateRef
);
617 aPathComposer
.appendAscii("/UIElements/States");
619 return ::utl::OConfigurationTreeRoot(::comphelper::getProcessComponentContext(),
620 aPathComposer
.makeStringAndClear(), false);
622 catch( const Exception
& )
624 DBG_UNHANDLED_EXCEPTION();
627 return ::utl::OConfigurationTreeRoot();
633 void ResourceManager::GetToolPanelNodeNames (
634 ::std::vector
<OUString
>& rMatchingNames
,
635 const ::utl::OConfigurationTreeRoot aRoot
) const
637 Sequence
<OUString
> aChildNodeNames (aRoot
.getNodeNames());
638 const sal_Int32
nCount (aChildNodeNames
.getLength());
639 for (sal_Int32
nIndex(0); nIndex
<nCount
; ++nIndex
)
641 if (aChildNodeNames
[nIndex
].matchAsciiL(
642 RTL_CONSTASCII_STRINGPARAM( "private:resource/toolpanel/")))
643 rMatchingNames
.push_back(aChildNodeNames
[nIndex
]);
650 bool ResourceManager::IsDeckEnabled (
651 const OUString
& rsDeckId
,
652 const Context
& rContext
,
653 const Reference
<frame::XFrame
>& rxFrame
) const
655 // Check if any panel that matches the current context can be
657 ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors
;
658 ResourceManager::Instance().GetMatchingPanels(
659 aPanelContextDescriptors
,
664 for (ResourceManager::PanelContextDescriptorContainer::const_iterator
665 iPanel(aPanelContextDescriptors
.begin()),
666 iEnd(aPanelContextDescriptors
.end());
670 if (iPanel
->mbShowForReadOnlyDocuments
)
678 } } // end of namespace sfx2::sidebar