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 <comphelper/stlunosequence.hxx> 31 32 #include <rtl/ustrbuf.hxx> 33 #include <tools/diagnose_ex.h> 34 35 #include <com/sun/star/frame/XModuleManager.hpp> 36 37 #include <map> 38 39 40 #define A2S(pString) (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(pString))) 41 42 using ::rtl::OUString; 43 using namespace css; 44 using namespace cssu; 45 46 namespace sfx2 { namespace sidebar { 47 48 #define gsPrivateResourceToolpanelPrefix "private:resource/toolpanel/" 49 50 51 52 class ResourceManager::Deleter 53 { 54 public: 55 void operator() (ResourceManager* pObject) 56 { 57 delete pObject; 58 } 59 }; 60 61 62 ResourceManager& ResourceManager::Instance (void) 63 { 64 static ResourceManager maInstance; 65 return maInstance; 66 } 67 68 69 70 71 ResourceManager::ResourceManager (void) 72 : maDecks(), 73 maPanels(), 74 maProcessedApplications() 75 { 76 ReadDeckList(); 77 ReadPanelList(); 78 } 79 80 81 82 83 ResourceManager::~ResourceManager (void) 84 { 85 maPanels.clear(); 86 maDecks.clear(); 87 } 88 89 90 91 92 const DeckDescriptor* ResourceManager::GetBestMatchingDeck ( 93 const Context& rContext, 94 const Reference<frame::XFrame>& rxFrame) 95 { 96 ReadLegacyAddons(rxFrame); 97 98 for (DeckContainer::const_iterator iDeck(maDecks.begin()), iEnd(maDecks.end()); 99 iDeck!=iEnd; 100 ++iDeck) 101 { 102 if (iDeck->maContextList.GetMatch(rContext) != NULL) 103 return &*iDeck; 104 } 105 return NULL; 106 } 107 108 109 110 111 const DeckDescriptor* ResourceManager::GetDeckDescriptor ( 112 const ::rtl::OUString& rsDeckId) const 113 { 114 for (DeckContainer::const_iterator 115 iDeck(maDecks.begin()), 116 iEnd(maDecks.end()); 117 iDeck!=iEnd; 118 ++iDeck) 119 { 120 if (iDeck->msId.equals(rsDeckId)) 121 return &*iDeck; 122 } 123 return NULL; 124 } 125 126 127 128 129 const PanelDescriptor* ResourceManager::GetPanelDescriptor ( 130 const ::rtl::OUString& rsPanelId) const 131 { 132 for (PanelContainer::const_iterator 133 iPanel(maPanels.begin()), 134 iEnd(maPanels.end()); 135 iPanel!=iEnd; 136 ++iPanel) 137 { 138 if (iPanel->msId.equals(rsPanelId)) 139 return &*iPanel; 140 } 141 return NULL; 142 } 143 144 145 146 147 void ResourceManager::SetIsDeckEnabled ( 148 const ::rtl::OUString& rsDeckId, 149 const bool bIsEnabled) 150 { 151 for (DeckContainer::iterator 152 iDeck(maDecks.begin()), 153 iEnd(maDecks.end()); 154 iDeck!=iEnd; 155 ++iDeck) 156 { 157 if (iDeck->msId.equals(rsDeckId)) 158 { 159 iDeck->mbIsEnabled = bIsEnabled; 160 return; 161 } 162 } 163 } 164 165 166 167 168 const ResourceManager::IdContainer& ResourceManager::GetMatchingDecks ( 169 IdContainer& rDeckIds, 170 const Context& rContext, 171 const Reference<frame::XFrame>& rxFrame) 172 { 173 ReadLegacyAddons(rxFrame); 174 175 ::std::multimap<sal_Int32,OUString> aOrderedIds; 176 for (DeckContainer::const_iterator 177 iDeck(maDecks.begin()), 178 iEnd (maDecks.end()); 179 iDeck!=iEnd; 180 ++iDeck) 181 { 182 const DeckDescriptor& rDeckDescriptor (*iDeck); 183 if (rDeckDescriptor.maContextList.GetMatch(rContext) != NULL) 184 aOrderedIds.insert(::std::multimap<sal_Int32,OUString>::value_type( 185 rDeckDescriptor.mnOrderIndex, 186 rDeckDescriptor.msId)); 187 } 188 189 for (::std::multimap<sal_Int32,OUString>::const_iterator 190 iId(aOrderedIds.begin()), 191 iEnd(aOrderedIds.end()); 192 iId!=iEnd; 193 ++iId) 194 { 195 rDeckIds.push_back(iId->second); 196 } 197 198 return rDeckIds; 199 } 200 201 202 203 204 const ResourceManager::PanelContextDescriptorContainer& ResourceManager::GetMatchingPanels ( 205 PanelContextDescriptorContainer& rPanelIds, 206 const Context& rContext, 207 const ::rtl::OUString& rsDeckId, 208 const Reference<frame::XFrame>& rxFrame) 209 { 210 ReadLegacyAddons(rxFrame); 211 212 ::std::multimap<sal_Int32,PanelContextDescriptor> aOrderedIds; 213 for (PanelContainer::const_iterator 214 iPanel(maPanels.begin()), 215 iEnd(maPanels.end()); 216 iPanel!=iEnd; 217 ++iPanel) 218 { 219 const PanelDescriptor& rPanelDescriptor (*iPanel); 220 if (rPanelDescriptor.msDeckId.equals(rsDeckId)) 221 { 222 const ContextList::Entry* pEntry = rPanelDescriptor.maContextList.GetMatch(rContext); 223 if (pEntry != NULL) 224 { 225 PanelContextDescriptor aPanelContextDescriptor; 226 aPanelContextDescriptor.msId = rPanelDescriptor.msId; 227 aPanelContextDescriptor.msMenuCommand = pEntry->msMenuCommand; 228 aPanelContextDescriptor.mbIsInitiallyVisible = pEntry->mbIsInitiallyVisible; 229 aOrderedIds.insert(::std::multimap<sal_Int32,PanelContextDescriptor>::value_type( 230 rPanelDescriptor.mnOrderIndex, 231 aPanelContextDescriptor)); 232 } 233 } 234 } 235 236 for (::std::multimap<sal_Int32,PanelContextDescriptor>::const_iterator 237 iId(aOrderedIds.begin()), 238 iEnd(aOrderedIds.end()); 239 iId!=iEnd; 240 ++iId) 241 { 242 rPanelIds.push_back(iId->second); 243 } 244 245 return rPanelIds; 246 } 247 248 249 250 251 void ResourceManager::ReadDeckList (void) 252 { 253 const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory()); 254 const ::utl::OConfigurationTreeRoot aDeckRootNode ( 255 aContext, 256 A2S("org.openoffice.Office.UI.Sidebar/Content/DeckList"), 257 false); 258 if ( ! aDeckRootNode.isValid() ) 259 return; 260 261 const Sequence<OUString> aDeckNodeNames (aDeckRootNode.getNodeNames()); 262 const sal_Int32 nCount (aDeckNodeNames.getLength()); 263 maDecks.resize(nCount); 264 sal_Int32 nWriteIndex(0); 265 for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex) 266 { 267 const ::utl::OConfigurationNode aDeckNode (aDeckRootNode.openNode(aDeckNodeNames[nReadIndex])); 268 if ( ! aDeckNode.isValid()) 269 continue; 270 271 DeckDescriptor& rDeckDescriptor (maDecks[nWriteIndex++]); 272 273 rDeckDescriptor.msTitle = ::comphelper::getString( 274 aDeckNode.getNodeValue("Title")); 275 rDeckDescriptor.msId = ::comphelper::getString( 276 aDeckNode.getNodeValue("Id")); 277 rDeckDescriptor.msIconURL = ::comphelper::getString( 278 aDeckNode.getNodeValue("IconURL")); 279 rDeckDescriptor.msHighContrastIconURL = ::comphelper::getString( 280 aDeckNode.getNodeValue("HighContrastIconURL")); 281 rDeckDescriptor.msHelpURL = ::comphelper::getString( 282 aDeckNode.getNodeValue("HelpURL")); 283 rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle; 284 rDeckDescriptor.mbIsEnabled = true; 285 rDeckDescriptor.mnOrderIndex = ::comphelper::getINT32( 286 aDeckNode.getNodeValue("OrderIndex")); 287 288 ReadContextList( 289 aDeckNode, 290 rDeckDescriptor.maContextList, 291 OUString()); 292 } 293 294 // When there where invalid nodes then we have to adapt the size 295 // of the deck vector. 296 if (nWriteIndex<nCount) 297 maDecks.resize(nWriteIndex); 298 } 299 300 301 302 303 void ResourceManager::ReadPanelList (void) 304 { 305 const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory()); 306 const ::utl::OConfigurationTreeRoot aPanelRootNode ( 307 aContext, 308 A2S("org.openoffice.Office.UI.Sidebar/Content/PanelList"), 309 false); 310 if ( ! aPanelRootNode.isValid() ) 311 return; 312 313 const Sequence<OUString> aPanelNodeNames (aPanelRootNode.getNodeNames()); 314 const sal_Int32 nCount (aPanelNodeNames.getLength()); 315 maPanels.resize(nCount); 316 sal_Int32 nWriteIndex (0); 317 for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex) 318 { 319 const ::utl::OConfigurationNode aPanelNode (aPanelRootNode.openNode(aPanelNodeNames[nReadIndex])); 320 if ( ! aPanelNode.isValid()) 321 continue; 322 323 PanelDescriptor& rPanelDescriptor (maPanels[nWriteIndex++]); 324 325 rPanelDescriptor.msTitle = ::comphelper::getString( 326 aPanelNode.getNodeValue("Title")); 327 rPanelDescriptor.mbIsTitleBarOptional = ::comphelper::getBOOL( 328 aPanelNode.getNodeValue("TitleBarIsOptional")); 329 rPanelDescriptor.msId = ::comphelper::getString( 330 aPanelNode.getNodeValue("Id")); 331 rPanelDescriptor.msDeckId = ::comphelper::getString( 332 aPanelNode.getNodeValue("DeckId")); 333 rPanelDescriptor.msHelpURL = ::comphelper::getString( 334 aPanelNode.getNodeValue("HelpURL")); 335 rPanelDescriptor.msImplementationURL = ::comphelper::getString( 336 aPanelNode.getNodeValue("ImplementationURL")); 337 rPanelDescriptor.mnOrderIndex = ::comphelper::getINT32( 338 aPanelNode.getNodeValue("OrderIndex")); 339 rPanelDescriptor.mbWantsCanvas = ::comphelper::getBOOL( 340 aPanelNode.getNodeValue("WantsCanvas")); 341 const OUString sDefaultMenuCommand (::comphelper::getString( 342 aPanelNode.getNodeValue("DefaultMenuCommand"))); 343 344 ReadContextList( 345 aPanelNode, 346 rPanelDescriptor.maContextList, 347 sDefaultMenuCommand); 348 } 349 350 // When there where invalid nodes then we have to adapt the size 351 // of the deck vector. 352 if (nWriteIndex<nCount) 353 maPanels.resize(nWriteIndex); 354 } 355 356 357 358 359 void ResourceManager::ReadContextList ( 360 const ::utl::OConfigurationNode& rParentNode, 361 ContextList& rContextList, 362 const OUString& rsDefaultMenuCommand) const 363 { 364 const Any aValue = rParentNode.getNodeValue("ContextList"); 365 Sequence<OUString> aValues; 366 sal_Int32 nCount; 367 if (aValue >>= aValues) 368 nCount = aValues.getLength(); 369 else 370 nCount = 0; 371 372 for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex) 373 { 374 const OUString sValue (aValues[nIndex]); 375 sal_Int32 nCharacterIndex (0); 376 const OUString sApplicationName (sValue.getToken(0, ',', nCharacterIndex).trim()); 377 if (nCharacterIndex < 0) 378 { 379 if (sApplicationName.getLength() == 0) 380 { 381 // This is a valid case: in the XML file the separator 382 // was used as terminator. Using it in the last line 383 // creates an additional but empty entry. 384 break; 385 } 386 else 387 { 388 OSL_ASSERT("expecting three or four values per ContextList entry, separated by comma"); 389 continue; 390 } 391 } 392 393 const OUString sContextName (sValue.getToken(0, ',', nCharacterIndex).trim()); 394 if (nCharacterIndex < 0) 395 { 396 OSL_ASSERT("expecting three or four values per ContextList entry, separated by comma"); 397 continue; 398 } 399 400 const OUString sInitialState (sValue.getToken(0, ',', nCharacterIndex).trim()); 401 402 // The fourth argument is optional. 403 const OUString sMenuCommandOverride ( 404 nCharacterIndex<0 405 ? OUString() 406 : sValue.getToken(0, ',', nCharacterIndex).trim()); 407 const OUString sMenuCommand ( 408 sMenuCommandOverride.getLength()>0 409 ? (sMenuCommandOverride.equalsAscii("none") 410 ? OUString() 411 : sMenuCommandOverride) 412 : rsDefaultMenuCommand); 413 414 EnumContext::Application eApplication (EnumContext::GetApplicationEnum(sApplicationName)); 415 bool bApplicationIsDrawAndImpress = false; 416 if (eApplication == EnumContext::Application_None 417 && !sApplicationName.equals(EnumContext::GetApplicationName(EnumContext::Application_None))) 418 { 419 // Handle some special names: abbreviations that make 420 // context descriptions more readable. 421 if (sApplicationName.equalsAscii("Writer")) 422 eApplication = EnumContext::Application_Writer; 423 else if (sApplicationName.equalsAscii("Calc")) 424 eApplication = EnumContext::Application_Calc; 425 else if (sApplicationName.equalsAscii("Draw")) 426 eApplication = EnumContext::Application_Draw; 427 else if (sApplicationName.equalsAscii("Impress")) 428 eApplication = EnumContext::Application_Impress; 429 else if (sApplicationName.equalsAscii("DrawImpress")) 430 { 431 // A special case among the special names: it is 432 // common to use the same context descriptions for 433 // both Draw and Impress. This special case helps to 434 // avoid duplication in the .xcu file. 435 bApplicationIsDrawAndImpress = true; 436 } 437 else 438 { 439 OSL_ASSERT("application name not recognized"); 440 continue; 441 } 442 } 443 444 const EnumContext::Context eContext (EnumContext::GetContextEnum(sContextName)); 445 if (eContext == EnumContext::Context_Unknown) 446 { 447 OSL_ASSERT("context name not recognized"); 448 continue; 449 } 450 451 bool bIsInitiallyVisible; 452 if (sInitialState.equalsAscii("visible")) 453 bIsInitiallyVisible = true; 454 else if (sInitialState.equalsAscii("hidden")) 455 bIsInitiallyVisible = false; 456 else 457 { 458 OSL_ASSERT("unrecognized state"); 459 continue; 460 } 461 462 if (bApplicationIsDrawAndImpress) 463 { 464 // Add the context description for both Draw and Impress. 465 rContextList.AddContextDescription( 466 Context( 467 EnumContext::GetApplicationName(EnumContext::Application_Draw), 468 EnumContext::GetContextName(eContext)), 469 bIsInitiallyVisible, 470 sMenuCommand); 471 rContextList.AddContextDescription( 472 Context( 473 EnumContext::GetApplicationName(EnumContext::Application_Impress), 474 EnumContext::GetContextName(eContext)), 475 bIsInitiallyVisible, 476 sMenuCommand); 477 } 478 else 479 rContextList.AddContextDescription( 480 Context( 481 EnumContext::GetApplicationName(eApplication), 482 EnumContext::GetContextName(eContext)), 483 bIsInitiallyVisible, 484 sMenuCommand); 485 } 486 } 487 488 489 490 491 void ResourceManager::ReadLegacyAddons (const Reference<frame::XFrame>& rxFrame) 492 { 493 // Get module name for given frame. 494 ::rtl::OUString sModuleName (GetModuleName(rxFrame)); 495 if (sModuleName.getLength() == 0) 496 return; 497 if (maProcessedApplications.find(sModuleName) != maProcessedApplications.end()) 498 { 499 // Addons for this application have already been read. 500 // There is nothing more to do. 501 return; 502 } 503 504 // Mark module as processed. Even when there is an error that 505 // prevents the configuration data from being read, this error 506 // will not be triggered a second time. 507 maProcessedApplications.insert(sModuleName); 508 509 // Get access to the configuration root node for the application. 510 ::utl::OConfigurationTreeRoot aLegacyRootNode (GetLegacyAddonRootNode(sModuleName)); 511 if ( ! aLegacyRootNode.isValid()) 512 return; 513 514 // Process child nodes. 515 ::std::vector<OUString> aMatchingNodeNames; 516 GetToolPanelNodeNames(aMatchingNodeNames, aLegacyRootNode); 517 const sal_Int32 nCount (aMatchingNodeNames.size()); 518 size_t nDeckWriteIndex (maDecks.size()); 519 size_t nPanelWriteIndex (maPanels.size()); 520 maDecks.resize(maDecks.size() + nCount); 521 maPanels.resize(maPanels.size() + nCount); 522 for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex) 523 { 524 const OUString& rsNodeName (aMatchingNodeNames[nReadIndex]); 525 const ::utl::OConfigurationNode aChildNode (aLegacyRootNode.openNode(rsNodeName)); 526 if ( ! aChildNode.isValid()) 527 continue; 528 529 DeckDescriptor& rDeckDescriptor (maDecks[nDeckWriteIndex++]); 530 rDeckDescriptor.msTitle = ::comphelper::getString(aChildNode.getNodeValue("UIName")); 531 rDeckDescriptor.msId = rsNodeName; 532 rDeckDescriptor.msIconURL = ::comphelper::getString(aChildNode.getNodeValue("ImageURL")); 533 rDeckDescriptor.msHighContrastIconURL = rDeckDescriptor.msIconURL; 534 rDeckDescriptor.msHelpURL = ::comphelper::getString(aChildNode.getNodeValue("HelpURL")); 535 rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle; 536 rDeckDescriptor.maContextList.AddContextDescription(Context(sModuleName, A2S("any")), true, OUString()); 537 rDeckDescriptor.mbIsEnabled = true; 538 539 PanelDescriptor& rPanelDescriptor (maPanels[nPanelWriteIndex++]); 540 rPanelDescriptor.msTitle = ::comphelper::getString(aChildNode.getNodeValue("UIName")); 541 rPanelDescriptor.mbIsTitleBarOptional = true; 542 rPanelDescriptor.msId = rsNodeName; 543 rPanelDescriptor.msDeckId = rsNodeName; 544 rPanelDescriptor.msHelpURL = ::comphelper::getString(aChildNode.getNodeValue("HelpURL")); 545 rPanelDescriptor.maContextList.AddContextDescription(Context(sModuleName, A2S("any")), true, OUString()); 546 rPanelDescriptor.msImplementationURL = rsNodeName; 547 } 548 549 // When there where invalid nodes then we have to adapt the size 550 // of the deck and panel vectors. 551 if (nDeckWriteIndex < maDecks.size()) 552 maDecks.resize(nDeckWriteIndex); 553 if (nPanelWriteIndex < maPanels.size()) 554 maPanels.resize(nPanelWriteIndex); 555 } 556 557 558 559 560 ::rtl::OUString ResourceManager::GetModuleName ( 561 const cssu::Reference<css::frame::XFrame>& rxFrame) 562 { 563 try 564 { 565 const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory()); 566 const Reference<frame::XModuleManager> xModuleManager ( 567 aContext.createComponent("com.sun.star.frame.ModuleManager" ), 568 UNO_QUERY_THROW ); 569 return xModuleManager->identify(rxFrame); 570 } 571 catch (const Exception&) 572 { 573 DBG_UNHANDLED_EXCEPTION(); 574 } 575 return OUString(); 576 } 577 578 579 580 581 ::utl::OConfigurationTreeRoot ResourceManager::GetLegacyAddonRootNode ( 582 const ::rtl::OUString& rsModuleName) const 583 { 584 try 585 { 586 const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory()); 587 const Reference<container::XNameAccess> xModuleAccess ( 588 aContext.createComponent("com.sun.star.frame.ModuleManager"), 589 UNO_QUERY_THROW); 590 const ::comphelper::NamedValueCollection aModuleProperties (xModuleAccess->getByName(rsModuleName)); 591 const ::rtl::OUString sWindowStateRef (aModuleProperties.getOrDefault( 592 "ooSetupFactoryWindowStateConfigRef", 593 ::rtl::OUString())); 594 595 ::rtl::OUStringBuffer aPathComposer; 596 aPathComposer.appendAscii("org.openoffice.Office.UI."); 597 aPathComposer.append(sWindowStateRef); 598 aPathComposer.appendAscii("/UIElements/States"); 599 600 return ::utl::OConfigurationTreeRoot(aContext, aPathComposer.makeStringAndClear(), false); 601 } 602 catch( const Exception& ) 603 { 604 DBG_UNHANDLED_EXCEPTION(); 605 } 606 607 return ::utl::OConfigurationTreeRoot(); 608 } 609 610 611 612 613 void ResourceManager::GetToolPanelNodeNames ( 614 ::std::vector<OUString>& rMatchingNames, 615 const ::utl::OConfigurationTreeRoot aRoot) const 616 { 617 Sequence<OUString> aChildNodeNames (aRoot.getNodeNames()); 618 const sal_Int32 nCount (aChildNodeNames.getLength()); 619 for (sal_Int32 nIndex(0); nIndex<nCount; ++nIndex) 620 { 621 if (aChildNodeNames[nIndex].matchAsciiL( 622 RTL_CONSTASCII_STRINGPARAM( "private:resource/toolpanel/"))) 623 rMatchingNames.push_back(aChildNodeNames[nIndex]); 624 } 625 } 626 627 628 629 } } // end of namespace sfx2::sidebar 630