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