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 bool bApplicationIsDrawAndImpress = false; 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 bApplicationIsDrawAndImpress = true; 437 } 438 else 439 { 440 OSL_ASSERT("application name not recognized"); 441 continue; 442 } 443 } 444 445 const EnumContext::Context eContext (EnumContext::GetContextEnum(sContextName)); 446 if (eContext == EnumContext::Context_Unknown) 447 { 448 OSL_ASSERT("context name not recognized"); 449 continue; 450 } 451 452 bool bIsInitiallyVisible; 453 if (sInitialState.equalsAscii("visible")) 454 bIsInitiallyVisible = true; 455 else if (sInitialState.equalsAscii("hidden")) 456 bIsInitiallyVisible = false; 457 else 458 { 459 OSL_ASSERT("unrecognized state"); 460 continue; 461 } 462 463 if (bApplicationIsDrawAndImpress) 464 { 465 // Add the context description for both Draw and Impress. 466 rContextList.AddContextDescription( 467 Context( 468 EnumContext::GetApplicationName(EnumContext::Application_Draw), 469 EnumContext::GetContextName(eContext)), 470 bIsInitiallyVisible, 471 sMenuCommand); 472 rContextList.AddContextDescription( 473 Context( 474 EnumContext::GetApplicationName(EnumContext::Application_Impress), 475 EnumContext::GetContextName(eContext)), 476 bIsInitiallyVisible, 477 sMenuCommand); 478 } 479 else 480 { 481 rContextList.AddContextDescription( 482 Context( 483 EnumContext::GetApplicationName(eApplication), 484 EnumContext::GetContextName(eContext)), 485 bIsInitiallyVisible, 486 sMenuCommand); 487 } 488 } 489 } 490 491 492 493 494 void ResourceManager::ReadLegacyAddons (const Reference<frame::XFrame>& rxFrame) 495 { 496 // Get module name for given frame. 497 ::rtl::OUString sModuleName (GetModuleName(rxFrame)); 498 if (sModuleName.getLength() == 0) 499 return; 500 if (maProcessedApplications.find(sModuleName) != maProcessedApplications.end()) 501 { 502 // Addons for this application have already been read. 503 // There is nothing more to do. 504 return; 505 } 506 507 // Mark module as processed. Even when there is an error that 508 // prevents the configuration data from being read, this error 509 // will not be triggered a second time. 510 maProcessedApplications.insert(sModuleName); 511 512 // Get access to the configuration root node for the application. 513 ::utl::OConfigurationTreeRoot aLegacyRootNode (GetLegacyAddonRootNode(sModuleName)); 514 if ( ! aLegacyRootNode.isValid()) 515 return; 516 517 // Process child nodes. 518 ::std::vector<OUString> aMatchingNodeNames; 519 GetToolPanelNodeNames(aMatchingNodeNames, aLegacyRootNode); 520 const sal_Int32 nCount (aMatchingNodeNames.size()); 521 size_t nDeckWriteIndex (maDecks.size()); 522 size_t nPanelWriteIndex (maPanels.size()); 523 maDecks.resize(maDecks.size() + nCount); 524 maPanels.resize(maPanels.size() + nCount); 525 for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex) 526 { 527 const OUString& rsNodeName (aMatchingNodeNames[nReadIndex]); 528 const ::utl::OConfigurationNode aChildNode (aLegacyRootNode.openNode(rsNodeName)); 529 if ( ! aChildNode.isValid()) 530 continue; 531 532 DeckDescriptor& rDeckDescriptor (maDecks[nDeckWriteIndex++]); 533 rDeckDescriptor.msTitle = ::comphelper::getString(aChildNode.getNodeValue("UIName")); 534 rDeckDescriptor.msId = rsNodeName; 535 rDeckDescriptor.msIconURL = ::comphelper::getString(aChildNode.getNodeValue("ImageURL")); 536 rDeckDescriptor.msHighContrastIconURL = rDeckDescriptor.msIconURL; 537 rDeckDescriptor.msHelpURL = ::comphelper::getString(aChildNode.getNodeValue("HelpURL")); 538 rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle; 539 rDeckDescriptor.maContextList.AddContextDescription(Context(sModuleName, A2S("any")), true, OUString()); 540 rDeckDescriptor.mbIsEnabled = true; 541 542 PanelDescriptor& rPanelDescriptor (maPanels[nPanelWriteIndex++]); 543 rPanelDescriptor.msTitle = ::comphelper::getString(aChildNode.getNodeValue("UIName")); 544 rPanelDescriptor.mbIsTitleBarOptional = true; 545 rPanelDescriptor.msId = rsNodeName; 546 rPanelDescriptor.msDeckId = rsNodeName; 547 rPanelDescriptor.msHelpURL = ::comphelper::getString(aChildNode.getNodeValue("HelpURL")); 548 rPanelDescriptor.maContextList.AddContextDescription(Context(sModuleName, A2S("any")), true, OUString()); 549 rPanelDescriptor.msImplementationURL = rsNodeName; 550 } 551 552 // When there where invalid nodes then we have to adapt the size 553 // of the deck and panel vectors. 554 if (nDeckWriteIndex < maDecks.size()) 555 maDecks.resize(nDeckWriteIndex); 556 if (nPanelWriteIndex < maPanels.size()) 557 maPanels.resize(nPanelWriteIndex); 558 } 559 560 561 562 563 ::rtl::OUString ResourceManager::GetModuleName ( 564 const cssu::Reference<css::frame::XFrame>& rxFrame) 565 { 566 try 567 { 568 const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory()); 569 const Reference<frame::XModuleManager> xModuleManager ( 570 aContext.createComponent("com.sun.star.frame.ModuleManager" ), 571 UNO_QUERY_THROW ); 572 return xModuleManager->identify(rxFrame); 573 } 574 catch (const Exception&) 575 { 576 DBG_UNHANDLED_EXCEPTION(); 577 } 578 return OUString(); 579 } 580 581 582 583 584 ::utl::OConfigurationTreeRoot ResourceManager::GetLegacyAddonRootNode ( 585 const ::rtl::OUString& rsModuleName) const 586 { 587 try 588 { 589 const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory()); 590 const Reference<container::XNameAccess> xModuleAccess ( 591 aContext.createComponent("com.sun.star.frame.ModuleManager"), 592 UNO_QUERY_THROW); 593 const ::comphelper::NamedValueCollection aModuleProperties (xModuleAccess->getByName(rsModuleName)); 594 const ::rtl::OUString sWindowStateRef (aModuleProperties.getOrDefault( 595 "ooSetupFactoryWindowStateConfigRef", 596 ::rtl::OUString())); 597 598 ::rtl::OUStringBuffer aPathComposer; 599 aPathComposer.appendAscii("org.openoffice.Office.UI."); 600 aPathComposer.append(sWindowStateRef); 601 aPathComposer.appendAscii("/UIElements/States"); 602 603 return ::utl::OConfigurationTreeRoot(aContext, aPathComposer.makeStringAndClear(), false); 604 } 605 catch( const Exception& ) 606 { 607 DBG_UNHANDLED_EXCEPTION(); 608 } 609 610 return ::utl::OConfigurationTreeRoot(); 611 } 612 613 614 615 616 void ResourceManager::GetToolPanelNodeNames ( 617 ::std::vector<OUString>& rMatchingNames, 618 const ::utl::OConfigurationTreeRoot aRoot) const 619 { 620 Sequence<OUString> aChildNodeNames (aRoot.getNodeNames()); 621 const sal_Int32 nCount (aChildNodeNames.getLength()); 622 for (sal_Int32 nIndex(0); nIndex<nCount; ++nIndex) 623 { 624 if (aChildNodeNames[nIndex].matchAsciiL( 625 RTL_CONSTASCII_STRINGPARAM( "private:resource/toolpanel/"))) 626 rMatchingNames.push_back(aChildNodeNames[nIndex]); 627 } 628 } 629 630 631 632 } } // end of namespace sfx2::sidebar 633