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 sal_Int32 nBestMatch (EnumContext::NoMatch); 99 const DeckContainer::const_iterator iEnd (maDecks.end()); 100 DeckContainer::const_iterator iBestDeck (iEnd); 101 102 for (DeckContainer::const_iterator iDeck(maDecks.begin()); 103 iDeck!=iEnd; 104 ++iDeck) 105 { 106 const sal_Int32 nMatch (iDeck->maContextMatcher.EvaluateMatch(rContext)); 107 if (nMatch < nBestMatch) 108 { 109 // Found a better matching deck. 110 nBestMatch = nMatch; 111 iBestDeck = iDeck; 112 if (nBestMatch == EnumContext::OptimalMatch) 113 { 114 // We will not find a better match. 115 break; 116 } 117 } 118 } 119 if (iBestDeck != iEnd) 120 return &*iBestDeck; 121 else 122 return NULL; 123 } 124 125 126 127 128 const DeckDescriptor* ResourceManager::GetDeckDescriptor ( 129 const ::rtl::OUString& rsDeckId) const 130 { 131 for (DeckContainer::const_iterator 132 iDeck(maDecks.begin()), 133 iEnd(maDecks.end()); 134 iDeck!=iEnd; 135 ++iDeck) 136 { 137 if (iDeck->msId.equals(rsDeckId)) 138 return &*iDeck; 139 } 140 return NULL; 141 } 142 143 144 145 146 const PanelDescriptor* ResourceManager::GetPanelDescriptor ( 147 const ::rtl::OUString& rsPanelId) const 148 { 149 for (PanelContainer::const_iterator 150 iPanel(maPanels.begin()), 151 iEnd(maPanels.end()); 152 iPanel!=iEnd; 153 ++iPanel) 154 { 155 if (iPanel->msId.equals(rsPanelId)) 156 return &*iPanel; 157 } 158 return NULL; 159 } 160 161 162 163 164 void ResourceManager::SetIsDeckEnabled ( 165 const ::rtl::OUString& rsDeckId, 166 const bool bIsEnabled) 167 { 168 for (DeckContainer::iterator 169 iDeck(maDecks.begin()), 170 iEnd(maDecks.end()); 171 iDeck!=iEnd; 172 ++iDeck) 173 { 174 if (iDeck->msId.equals(rsDeckId)) 175 { 176 iDeck->mbIsEnabled = bIsEnabled; 177 return; 178 } 179 } 180 } 181 182 183 184 185 const ResourceManager::IdContainer& ResourceManager::GetMatchingDecks ( 186 IdContainer& rDeckIds, 187 const Context& rContext, 188 const Reference<frame::XFrame>& rxFrame) 189 { 190 ReadLegacyAddons(rxFrame); 191 192 ::std::multimap<sal_Int32,OUString> aOrderedIds; 193 for (DeckContainer::const_iterator 194 iDeck(maDecks.begin()), 195 iEnd (maDecks.end()); 196 iDeck!=iEnd; 197 ++iDeck) 198 { 199 const DeckDescriptor& rDeckDescriptor (*iDeck); 200 if (rDeckDescriptor.maContextMatcher.EvaluateMatch(rContext) != EnumContext::NoMatch) 201 aOrderedIds.insert(::std::multimap<sal_Int32,OUString>::value_type( 202 rDeckDescriptor.mnOrderIndex, 203 rDeckDescriptor.msId)); 204 } 205 206 for (::std::multimap<sal_Int32,OUString>::const_iterator 207 iId(aOrderedIds.begin()), 208 iEnd(aOrderedIds.end()); 209 iId!=iEnd; 210 ++iId) 211 { 212 rDeckIds.push_back(iId->second); 213 } 214 215 return rDeckIds; 216 } 217 218 219 220 221 const ResourceManager::IdContainer& ResourceManager::GetMatchingPanels ( 222 IdContainer& rPanelIds, 223 const Context& rContext, 224 const ::rtl::OUString& rsDeckId, 225 const Reference<frame::XFrame>& rxFrame) 226 { 227 ReadLegacyAddons(rxFrame); 228 229 ::std::multimap<sal_Int32,OUString> aOrderedIds; 230 for (PanelContainer::const_iterator 231 iPanel(maPanels.begin()), 232 iEnd(maPanels.end()); 233 iPanel!=iEnd; 234 ++iPanel) 235 { 236 const PanelDescriptor& rPanelDescriptor (*iPanel); 237 if (rPanelDescriptor.msDeckId.equals(rsDeckId)) 238 if (rPanelDescriptor.maContextMatcher.EvaluateMatch(rContext) != EnumContext::NoMatch) 239 aOrderedIds.insert(::std::multimap<sal_Int32,OUString>::value_type( 240 rPanelDescriptor.mnOrderIndex, 241 rPanelDescriptor.msId)); 242 } 243 244 for (::std::multimap<sal_Int32,OUString>::const_iterator 245 iId(aOrderedIds.begin()), 246 iEnd(aOrderedIds.end()); 247 iId!=iEnd; 248 ++iId) 249 { 250 rPanelIds.push_back(iId->second); 251 } 252 253 return rPanelIds; 254 } 255 256 257 258 259 void ResourceManager::ReadDeckList (void) 260 { 261 const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory()); 262 const ::utl::OConfigurationTreeRoot aDeckRootNode ( 263 aContext, 264 A2S("org.openoffice.Office.UI.Sidebar/Content/DeckList"), 265 false); 266 if ( ! aDeckRootNode.isValid() ) 267 return; 268 269 const Sequence<OUString> aDeckNodeNames (aDeckRootNode.getNodeNames()); 270 const sal_Int32 nCount (aDeckNodeNames.getLength()); 271 maDecks.resize(nCount); 272 sal_Int32 nWriteIndex(0); 273 for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex) 274 { 275 const ::utl::OConfigurationNode aDeckNode (aDeckRootNode.openNode(aDeckNodeNames[nReadIndex])); 276 if ( ! aDeckNode.isValid()) 277 continue; 278 279 DeckDescriptor& rDeckDescriptor (maDecks[nWriteIndex++]); 280 281 rDeckDescriptor.msTitle = ::comphelper::getString( 282 aDeckNode.getNodeValue("Title")); 283 rDeckDescriptor.msId = ::comphelper::getString( 284 aDeckNode.getNodeValue("Id")); 285 rDeckDescriptor.msIconURL = ::comphelper::getString( 286 aDeckNode.getNodeValue("IconURL")); 287 rDeckDescriptor.msHighContrastIconURL = ::comphelper::getString( 288 aDeckNode.getNodeValue("HighContrastIconURL")); 289 rDeckDescriptor.msHelpURL = ::comphelper::getString( 290 aDeckNode.getNodeValue("HelpURL")); 291 rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle; 292 rDeckDescriptor.mbIsEnabled = true; 293 rDeckDescriptor.mnOrderIndex = ::comphelper::getINT32( 294 aDeckNode.getNodeValue("OrderIndex")); 295 296 ReadContextMatcher(aDeckNode.openNode("ContextMatchers"), rDeckDescriptor.maContextMatcher); 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.msHelpURL = ::comphelper::getString( 339 aPanelNode.getNodeValue("HelpURL")); 340 rPanelDescriptor.msImplementationURL = ::comphelper::getString( 341 aPanelNode.getNodeValue("ImplementationURL")); 342 rPanelDescriptor.mnOrderIndex = ::comphelper::getINT32( 343 aPanelNode.getNodeValue("OrderIndex")); 344 rPanelDescriptor.mbHasMenu = ::comphelper::getBOOL( 345 aPanelNode.getNodeValue("HasMenu")); 346 rPanelDescriptor.mbWantsCanvas = ::comphelper::getBOOL( 347 aPanelNode.getNodeValue("WantsCanvas")); 348 ReadContextMatcher(aPanelNode.openNode("ContextMatchers"), rPanelDescriptor.maContextMatcher); 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::ReadContextMatcher ( 361 const ::utl::OConfigurationNode& rNode, 362 ContextMatcher& rContextMatcher) const 363 { 364 const Sequence<OUString> aMatcherNodeNames (rNode.getNodeNames()); 365 const sal_Int32 nMatcherCount (aMatcherNodeNames.getLength()); 366 for (sal_Int32 nMatcherIndex(0); nMatcherIndex<nMatcherCount; ++nMatcherIndex) 367 { 368 const ::utl::OConfigurationNode aMatcherNode (rNode.openNode(aMatcherNodeNames[nMatcherIndex])); 369 370 const OUString sApplicationName ( 371 ::comphelper::getString(aMatcherNode.getNodeValue("Application"))); 372 const bool bIsContextListNegated ( 373 ::comphelper::getBOOL(aMatcherNode.getNodeValue("IsContextListNegated"))); 374 375 // Read the context names. 376 Any aContextListValue (aMatcherNode.getNodeValue("ContextList")); 377 Sequence<OUString> aContextList; 378 ::std::vector<OUString> aContextVector; 379 if (aContextListValue >>= aContextList) 380 { 381 aContextVector.reserve(aContextList.getLength()); 382 ::std::copy( 383 ::comphelper::stl_begin(aContextList), 384 ::comphelper::stl_end(aContextList), 385 ::std::back_inserter(aContextVector)); 386 } 387 // Empty list defaults to "any". 388 if (aContextVector.empty()) 389 aContextVector.push_back(A2S("any")); 390 391 rContextMatcher.AddMatcher( 392 sApplicationName, 393 aContextVector, 394 bIsContextListNegated); 395 } 396 } 397 398 399 400 401 void ResourceManager::ReadLegacyAddons (const Reference<frame::XFrame>& rxFrame) 402 { 403 // Get module name for given frame. 404 ::rtl::OUString sModuleName (GetModuleName(rxFrame)); 405 if (sModuleName.getLength() == 0) 406 return; 407 if (maProcessedApplications.find(sModuleName) != maProcessedApplications.end()) 408 { 409 // Addons for this application have already been read. 410 // There is nothing more to do. 411 return; 412 } 413 414 // Mark module as processed. Even when there is an error that 415 // prevents the configuration data from being read, this error 416 // will not be triggered a second time. 417 maProcessedApplications.insert(sModuleName); 418 419 // Get access to the configuration root node for the application. 420 ::utl::OConfigurationTreeRoot aLegacyRootNode (GetLegacyAddonRootNode(sModuleName)); 421 if ( ! aLegacyRootNode.isValid()) 422 return; 423 424 // Process child nodes. 425 ::std::vector<OUString> aMatchingNodeNames; 426 GetToolPanelNodeNames(aMatchingNodeNames, aLegacyRootNode); 427 const sal_Int32 nCount (aMatchingNodeNames.size()); 428 size_t nDeckWriteIndex (maDecks.size()); 429 size_t nPanelWriteIndex (maPanels.size()); 430 maDecks.resize(maDecks.size() + nCount); 431 maPanels.resize(maPanels.size() + nCount); 432 for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex) 433 { 434 const OUString& rsNodeName (aMatchingNodeNames[nReadIndex]); 435 const ::utl::OConfigurationNode aChildNode (aLegacyRootNode.openNode(rsNodeName)); 436 if ( ! aChildNode.isValid()) 437 continue; 438 439 DeckDescriptor& rDeckDescriptor (maDecks[nDeckWriteIndex++]); 440 rDeckDescriptor.msTitle = ::comphelper::getString(aChildNode.getNodeValue("UIName")); 441 rDeckDescriptor.msId = rsNodeName; 442 rDeckDescriptor.msIconURL = ::comphelper::getString(aChildNode.getNodeValue("ImageURL")); 443 rDeckDescriptor.msHighContrastIconURL = rDeckDescriptor.msIconURL; 444 rDeckDescriptor.msHelpURL = ::comphelper::getString(aChildNode.getNodeValue("HelpURL")); 445 rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle; 446 rDeckDescriptor.maContextMatcher.AddMatcher(sModuleName, A2S("any")); 447 rDeckDescriptor.mbIsEnabled = true; 448 449 PanelDescriptor& rPanelDescriptor (maPanels[nPanelWriteIndex++]); 450 rPanelDescriptor.msTitle = ::comphelper::getString(aChildNode.getNodeValue("UIName")); 451 rPanelDescriptor.mbIsTitleBarOptional = true; 452 rPanelDescriptor.msId = rsNodeName; 453 rPanelDescriptor.msDeckId = rsNodeName; 454 rPanelDescriptor.msHelpURL = ::comphelper::getString(aChildNode.getNodeValue("HelpURL")); 455 rPanelDescriptor.maContextMatcher.AddMatcher(sModuleName, A2S("any")); 456 rPanelDescriptor.msImplementationURL = rsNodeName; 457 } 458 459 // When there where invalid nodes then we have to adapt the size 460 // of the deck and panel vectors. 461 if (nDeckWriteIndex < maDecks.size()) 462 maDecks.resize(nDeckWriteIndex); 463 if (nPanelWriteIndex < maPanels.size()) 464 maPanels.resize(nPanelWriteIndex); 465 } 466 467 468 469 470 ::rtl::OUString ResourceManager::GetModuleName ( 471 const cssu::Reference<css::frame::XFrame>& rxFrame) 472 { 473 try 474 { 475 const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory()); 476 const Reference<frame::XModuleManager> xModuleManager ( 477 aContext.createComponent("com.sun.star.frame.ModuleManager" ), 478 UNO_QUERY_THROW ); 479 return xModuleManager->identify(rxFrame); 480 } 481 catch (const Exception&) 482 { 483 DBG_UNHANDLED_EXCEPTION(); 484 } 485 return OUString(); 486 } 487 488 489 490 491 ::utl::OConfigurationTreeRoot ResourceManager::GetLegacyAddonRootNode ( 492 const ::rtl::OUString& rsModuleName) const 493 { 494 try 495 { 496 const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory()); 497 const Reference<container::XNameAccess> xModuleAccess ( 498 aContext.createComponent("com.sun.star.frame.ModuleManager"), 499 UNO_QUERY_THROW); 500 const ::comphelper::NamedValueCollection aModuleProperties (xModuleAccess->getByName(rsModuleName)); 501 const ::rtl::OUString sWindowStateRef (aModuleProperties.getOrDefault( 502 "ooSetupFactoryWindowStateConfigRef", 503 ::rtl::OUString())); 504 505 ::rtl::OUStringBuffer aPathComposer; 506 aPathComposer.appendAscii("org.openoffice.Office.UI."); 507 aPathComposer.append(sWindowStateRef); 508 aPathComposer.appendAscii("/UIElements/States"); 509 510 return ::utl::OConfigurationTreeRoot(aContext, aPathComposer.makeStringAndClear(), false); 511 } 512 catch( const Exception& ) 513 { 514 DBG_UNHANDLED_EXCEPTION(); 515 } 516 517 return ::utl::OConfigurationTreeRoot(); 518 } 519 520 521 522 523 void ResourceManager::GetToolPanelNodeNames ( 524 ::std::vector<OUString>& rMatchingNames, 525 const ::utl::OConfigurationTreeRoot aRoot) const 526 { 527 Sequence<OUString> aChildNodeNames (aRoot.getNodeNames()); 528 const sal_Int32 nCount (aChildNodeNames.getLength()); 529 for (sal_Int32 nIndex(0); nIndex<nCount; ++nIndex) 530 { 531 if (aChildNodeNames[nIndex].matchAsciiL( 532 RTL_CONSTASCII_STRINGPARAM( "private:resource/toolpanel/"))) 533 rMatchingNames.push_back(aChildNodeNames[nIndex]); 534 } 535 } 536 537 538 539 } } // end of namespace sfx2::sidebar 540