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.msTitleBarIconURL = ::comphelper::getString( 283 aDeckNode.getNodeValue("TitleBarIconURL")); 284 rDeckDescriptor.msHighContrastTitleBarIconURL = ::comphelper::getString( 285 aDeckNode.getNodeValue("HighContrastTitleBarIconURL")); 286 rDeckDescriptor.msHelpURL = ::comphelper::getString( 287 aDeckNode.getNodeValue("HelpURL")); 288 rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle; 289 rDeckDescriptor.mbIsEnabled = true; 290 rDeckDescriptor.mnOrderIndex = ::comphelper::getINT32( 291 aDeckNode.getNodeValue("OrderIndex")); 292 293 ReadContextList( 294 aDeckNode, 295 rDeckDescriptor.maContextList, 296 OUString()); 297 } 298 299 // When there where invalid nodes then we have to adapt the size 300 // of the deck vector. 301 if (nWriteIndex<nCount) 302 maDecks.resize(nWriteIndex); 303 } 304 305 306 307 308 void ResourceManager::ReadPanelList (void) 309 { 310 const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory()); 311 const ::utl::OConfigurationTreeRoot aPanelRootNode ( 312 aContext, 313 A2S("org.openoffice.Office.UI.Sidebar/Content/PanelList"), 314 false); 315 if ( ! aPanelRootNode.isValid() ) 316 return; 317 318 const Sequence<OUString> aPanelNodeNames (aPanelRootNode.getNodeNames()); 319 const sal_Int32 nCount (aPanelNodeNames.getLength()); 320 maPanels.resize(nCount); 321 sal_Int32 nWriteIndex (0); 322 for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex) 323 { 324 const ::utl::OConfigurationNode aPanelNode (aPanelRootNode.openNode(aPanelNodeNames[nReadIndex])); 325 if ( ! aPanelNode.isValid()) 326 continue; 327 328 PanelDescriptor& rPanelDescriptor (maPanels[nWriteIndex++]); 329 330 rPanelDescriptor.msTitle = ::comphelper::getString( 331 aPanelNode.getNodeValue("Title")); 332 rPanelDescriptor.mbIsTitleBarOptional = ::comphelper::getBOOL( 333 aPanelNode.getNodeValue("TitleBarIsOptional")); 334 rPanelDescriptor.msId = ::comphelper::getString( 335 aPanelNode.getNodeValue("Id")); 336 rPanelDescriptor.msDeckId = ::comphelper::getString( 337 aPanelNode.getNodeValue("DeckId")); 338 rPanelDescriptor.msTitleBarIconURL = ::comphelper::getString( 339 aPanelNode.getNodeValue("TitleBarIconURL")); 340 rPanelDescriptor.msHighContrastTitleBarIconURL = ::comphelper::getString( 341 aPanelNode.getNodeValue("HighContrastTitleBarIconURL")); 342 rPanelDescriptor.msHelpURL = ::comphelper::getString( 343 aPanelNode.getNodeValue("HelpURL")); 344 rPanelDescriptor.msImplementationURL = ::comphelper::getString( 345 aPanelNode.getNodeValue("ImplementationURL")); 346 rPanelDescriptor.mnOrderIndex = ::comphelper::getINT32( 347 aPanelNode.getNodeValue("OrderIndex")); 348 rPanelDescriptor.mbWantsCanvas = ::comphelper::getBOOL( 349 aPanelNode.getNodeValue("WantsCanvas")); 350 const OUString sDefaultMenuCommand (::comphelper::getString( 351 aPanelNode.getNodeValue("DefaultMenuCommand"))); 352 353 ReadContextList( 354 aPanelNode, 355 rPanelDescriptor.maContextList, 356 sDefaultMenuCommand); 357 } 358 359 // When there where invalid nodes then we have to adapt the size 360 // of the deck vector. 361 if (nWriteIndex<nCount) 362 maPanels.resize(nWriteIndex); 363 } 364 365 366 367 368 void ResourceManager::ReadContextList ( 369 const ::utl::OConfigurationNode& rParentNode, 370 ContextList& rContextList, 371 const OUString& rsDefaultMenuCommand) const 372 { 373 const Any aValue = rParentNode.getNodeValue("ContextList"); 374 Sequence<OUString> aValues; 375 sal_Int32 nCount; 376 if (aValue >>= aValues) 377 nCount = aValues.getLength(); 378 else 379 nCount = 0; 380 381 for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex) 382 { 383 const OUString sValue (aValues[nIndex]); 384 sal_Int32 nCharacterIndex (0); 385 const OUString sApplicationName (sValue.getToken(0, ',', nCharacterIndex).trim()); 386 if (nCharacterIndex < 0) 387 { 388 if (sApplicationName.getLength() == 0) 389 { 390 // This is a valid case: in the XML file the separator 391 // was used as terminator. Using it in the last line 392 // creates an additional but empty entry. 393 break; 394 } 395 else 396 { 397 OSL_ASSERT("expecting three or four values per ContextList entry, separated by comma"); 398 continue; 399 } 400 } 401 402 const OUString sContextName (sValue.getToken(0, ',', nCharacterIndex).trim()); 403 if (nCharacterIndex < 0) 404 { 405 OSL_ASSERT("expecting three or four values per ContextList entry, separated by comma"); 406 continue; 407 } 408 409 const OUString sInitialState (sValue.getToken(0, ',', nCharacterIndex).trim()); 410 411 // The fourth argument is optional. 412 const OUString sMenuCommandOverride ( 413 nCharacterIndex<0 414 ? OUString() 415 : sValue.getToken(0, ',', nCharacterIndex).trim()); 416 const OUString sMenuCommand ( 417 sMenuCommandOverride.getLength()>0 418 ? (sMenuCommandOverride.equalsAscii("none") 419 ? OUString() 420 : sMenuCommandOverride) 421 : rsDefaultMenuCommand); 422 423 // Setup a list of application enums. Note that the 424 // application name may result in more than one value (eg 425 // DrawImpress will result in two enums, one for Draw and one 426 // for Impress). 427 ::std::vector<EnumContext::Application> aApplications; 428 EnumContext::Application eApplication (EnumContext::GetApplicationEnum(sApplicationName)); 429 if (eApplication == EnumContext::Application_None 430 && !sApplicationName.equals(EnumContext::GetApplicationName(EnumContext::Application_None))) 431 { 432 // Handle some special names: abbreviations that make 433 // context descriptions more readable. 434 if (sApplicationName.equalsAscii("Writer")) 435 aApplications.push_back(EnumContext::Application_Writer); 436 else if (sApplicationName.equalsAscii("Calc")) 437 aApplications.push_back(EnumContext::Application_Calc); 438 else if (sApplicationName.equalsAscii("Draw")) 439 aApplications.push_back(EnumContext::Application_Draw); 440 else if (sApplicationName.equalsAscii("Impress")) 441 aApplications.push_back(EnumContext::Application_Impress); 442 else if (sApplicationName.equalsAscii("DrawImpress")) 443 { 444 // A special case among the special names: it is 445 // common to use the same context descriptions for 446 // both Draw and Impress. This special case helps to 447 // avoid duplication in the .xcu file. 448 aApplications.push_back(EnumContext::Application_Draw); 449 aApplications.push_back(EnumContext::Application_Impress); 450 } 451 else if (sApplicationName.equalsAscii("WriterVariants")) 452 { 453 // Another special case for all Writer variants. 454 aApplications.push_back(EnumContext::Application_Writer); 455 aApplications.push_back(EnumContext::Application_WriterGlobal); 456 aApplications.push_back(EnumContext::Application_WriterWeb); 457 aApplications.push_back(EnumContext::Application_WriterXML); 458 } 459 else 460 { 461 OSL_ASSERT("application name not recognized"); 462 continue; 463 } 464 } 465 else 466 { 467 // No conversion of the application name necessary. 468 aApplications.push_back(eApplication); 469 } 470 471 // Setup the actual context enum. 472 const EnumContext::Context eContext (EnumContext::GetContextEnum(sContextName)); 473 if (eContext == EnumContext::Context_Unknown) 474 { 475 OSL_ASSERT("context name not recognized"); 476 continue; 477 } 478 479 // Setup the flag that controls whether a deck/pane is 480 // initially visible/expanded. 481 bool bIsInitiallyVisible; 482 if (sInitialState.equalsAscii("visible")) 483 bIsInitiallyVisible = true; 484 else if (sInitialState.equalsAscii("hidden")) 485 bIsInitiallyVisible = false; 486 else 487 { 488 OSL_ASSERT("unrecognized state"); 489 continue; 490 } 491 492 // Add context descriptors. 493 for (::std::vector<EnumContext::Application>::const_iterator 494 iApplication(aApplications.begin()), 495 iEnd(aApplications.end()); 496 iApplication!=iEnd; 497 ++iApplication) 498 { 499 if (*iApplication != EnumContext::Application_None) 500 rContextList.AddContextDescription( 501 Context( 502 EnumContext::GetApplicationName(*iApplication), 503 EnumContext::GetContextName(eContext)), 504 bIsInitiallyVisible, 505 sMenuCommand); 506 } 507 } 508 } 509 510 511 512 513 void ResourceManager::ReadLegacyAddons (const Reference<frame::XFrame>& rxFrame) 514 { 515 // Get module name for given frame. 516 ::rtl::OUString sModuleName (GetModuleName(rxFrame)); 517 if (sModuleName.getLength() == 0) 518 return; 519 if (maProcessedApplications.find(sModuleName) != maProcessedApplications.end()) 520 { 521 // Addons for this application have already been read. 522 // There is nothing more to do. 523 return; 524 } 525 526 // Mark module as processed. Even when there is an error that 527 // prevents the configuration data from being read, this error 528 // will not be triggered a second time. 529 maProcessedApplications.insert(sModuleName); 530 531 // Get access to the configuration root node for the application. 532 ::utl::OConfigurationTreeRoot aLegacyRootNode (GetLegacyAddonRootNode(sModuleName)); 533 if ( ! aLegacyRootNode.isValid()) 534 return; 535 536 // Process child nodes. 537 ::std::vector<OUString> aMatchingNodeNames; 538 GetToolPanelNodeNames(aMatchingNodeNames, aLegacyRootNode); 539 const sal_Int32 nCount (aMatchingNodeNames.size()); 540 size_t nDeckWriteIndex (maDecks.size()); 541 size_t nPanelWriteIndex (maPanels.size()); 542 maDecks.resize(maDecks.size() + nCount); 543 maPanels.resize(maPanels.size() + nCount); 544 for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex) 545 { 546 const OUString& rsNodeName (aMatchingNodeNames[nReadIndex]); 547 const ::utl::OConfigurationNode aChildNode (aLegacyRootNode.openNode(rsNodeName)); 548 if ( ! aChildNode.isValid()) 549 continue; 550 551 DeckDescriptor& rDeckDescriptor (maDecks[nDeckWriteIndex++]); 552 rDeckDescriptor.msTitle = ::comphelper::getString(aChildNode.getNodeValue("UIName")); 553 rDeckDescriptor.msId = rsNodeName; 554 rDeckDescriptor.msIconURL = ::comphelper::getString(aChildNode.getNodeValue("ImageURL")); 555 rDeckDescriptor.msHighContrastIconURL = rDeckDescriptor.msIconURL; 556 rDeckDescriptor.msHelpURL = ::comphelper::getString(aChildNode.getNodeValue("HelpURL")); 557 rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle; 558 rDeckDescriptor.maContextList.AddContextDescription(Context(sModuleName, A2S("any")), true, OUString()); 559 rDeckDescriptor.mbIsEnabled = true; 560 561 PanelDescriptor& rPanelDescriptor (maPanels[nPanelWriteIndex++]); 562 rPanelDescriptor.msTitle = ::comphelper::getString(aChildNode.getNodeValue("UIName")); 563 rPanelDescriptor.mbIsTitleBarOptional = true; 564 rPanelDescriptor.msId = rsNodeName; 565 rPanelDescriptor.msDeckId = rsNodeName; 566 rPanelDescriptor.msHelpURL = ::comphelper::getString(aChildNode.getNodeValue("HelpURL")); 567 rPanelDescriptor.maContextList.AddContextDescription(Context(sModuleName, A2S("any")), true, OUString()); 568 rPanelDescriptor.msImplementationURL = rsNodeName; 569 } 570 571 // When there where invalid nodes then we have to adapt the size 572 // of the deck and panel vectors. 573 if (nDeckWriteIndex < maDecks.size()) 574 maDecks.resize(nDeckWriteIndex); 575 if (nPanelWriteIndex < maPanels.size()) 576 maPanels.resize(nPanelWriteIndex); 577 } 578 579 580 581 582 ::rtl::OUString ResourceManager::GetModuleName ( 583 const cssu::Reference<css::frame::XFrame>& rxFrame) 584 { 585 if ( ! rxFrame.is() || ! rxFrame->getController().is()) 586 return OUString(); 587 588 try 589 { 590 const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory()); 591 const Reference<frame::XModuleManager> xModuleManager ( 592 aContext.createComponent("com.sun.star.frame.ModuleManager"), 593 UNO_QUERY_THROW); 594 return xModuleManager->identify(rxFrame); 595 } 596 catch (const Exception&) 597 { 598 DBG_UNHANDLED_EXCEPTION(); 599 } 600 return OUString(); 601 } 602 603 604 605 606 ::utl::OConfigurationTreeRoot ResourceManager::GetLegacyAddonRootNode ( 607 const ::rtl::OUString& rsModuleName) const 608 { 609 try 610 { 611 const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory()); 612 const Reference<container::XNameAccess> xModuleAccess ( 613 aContext.createComponent("com.sun.star.frame.ModuleManager"), 614 UNO_QUERY_THROW); 615 const ::comphelper::NamedValueCollection aModuleProperties (xModuleAccess->getByName(rsModuleName)); 616 const ::rtl::OUString sWindowStateRef (aModuleProperties.getOrDefault( 617 "ooSetupFactoryWindowStateConfigRef", 618 ::rtl::OUString())); 619 620 ::rtl::OUStringBuffer aPathComposer; 621 aPathComposer.appendAscii("org.openoffice.Office.UI."); 622 aPathComposer.append(sWindowStateRef); 623 aPathComposer.appendAscii("/UIElements/States"); 624 625 return ::utl::OConfigurationTreeRoot(aContext, aPathComposer.makeStringAndClear(), false); 626 } 627 catch( const Exception& ) 628 { 629 DBG_UNHANDLED_EXCEPTION(); 630 } 631 632 return ::utl::OConfigurationTreeRoot(); 633 } 634 635 636 637 638 void ResourceManager::GetToolPanelNodeNames ( 639 ::std::vector<OUString>& rMatchingNames, 640 const ::utl::OConfigurationTreeRoot aRoot) const 641 { 642 Sequence<OUString> aChildNodeNames (aRoot.getNodeNames()); 643 const sal_Int32 nCount (aChildNodeNames.getLength()); 644 for (sal_Int32 nIndex(0); nIndex<nCount; ++nIndex) 645 { 646 if (aChildNodeNames[nIndex].matchAsciiL( 647 RTL_CONSTASCII_STRINGPARAM( "private:resource/toolpanel/"))) 648 rMatchingNames.push_back(aChildNodeNames[nIndex]); 649 } 650 } 651 652 653 654 } } // end of namespace sfx2::sidebar 655