1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
36 using namespace css::uno
;
38 namespace sfx2
{ namespace sidebar
{
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
;
64 ResourceManager::ResourceManager()
67 maProcessedApplications(),
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())
85 if (iDeck
->msId
.equals(rsDeckId
))
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
))
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())
109 if (iDeck
->msId
.equals(rsDeckId
))
111 iDeck
->mbIsEnabled
= bIsEnabled
;
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())
131 const DeckDescriptor
& rDeckDescriptor (*iDeck
);
132 if (rDeckDescriptor
.maContextList
.GetMatch(rContext
) == NULL
)
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
);
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())
168 if ( ! rPanelDescriptor
.msDeckId
.equals(rsDeckId
))
171 const ContextList::Entry
* pEntry
= rPanelDescriptor
.maContextList
.GetMatch(rContext
);
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
);
194 void ResourceManager::ReadDeckList()
196 const utl::OConfigurationTreeRoot
aDeckRootNode(
197 comphelper::getProcessComponentContext(),
198 OUString("org.openoffice.Office.UI.Sidebar/Content/DeckList"),
200 if (!aDeckRootNode
.isValid())
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())
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");
229 rDeckDescriptor
.maContextList
,
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"),
245 if (!aPanelRootNode
.isValid())
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())
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
;
291 if (aValue
>>= aValues
)
292 nCount
= aValues
.getLength();
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.
312 OSL_FAIL("expecting three or four values per ContextList entry, separated by comma");
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");
324 const OUString
sInitialState(sValue
.getToken(0, ',', nCharacterIndex
).trim());
326 // The fourth argument is optional.
327 const OUString
sMenuCommandOverride(
330 : sValue
.getToken(0, ',', nCharacterIndex
).trim());
332 const OUString
sMenuCommand(
333 sMenuCommandOverride
.getLength() > 0
334 ? (sMenuCommandOverride
== "none"
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
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
);
379 OSL_FAIL("application name not recognized");
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");
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;
406 OSL_FAIL("unrecognized state");
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(
418 EnumContext::GetApplicationName(*iApplication
),
419 EnumContext::GetContextName(eContext
)),
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)
433 if (maProcessedApplications
.find(sModuleName
) != maProcessedApplications
.end())
435 // Addons for this application have already been read.
436 // There is nothing more to do.
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())
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())
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" )
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
));
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",
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
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
)
585 } } // end of namespace sfx2::sidebar
587 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */