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 <rtl/ustrbuf.hxx> 31 #include <tools/diagnose_ex.h> 32 33 #include <com/sun/star/frame/XModuleManager.hpp> 34 35 #include <map> 36 37 38 #define A2S(pString) (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(pString))) 39 40 using ::rtl::OUString; 41 using namespace css; 42 using namespace cssu; 43 44 namespace sfx2 { namespace sidebar { 45 46 #define gsPrivateResourceToolpanelPrefix "private:resource/toolpanel/" 47 48 49 50 class ResourceManager::Deleter 51 { 52 public: 53 void operator() (ResourceManager* pObject) 54 { 55 delete pObject; 56 } 57 }; 58 59 60 ResourceManager& ResourceManager::Instance (void) 61 { 62 static ResourceManager maInstance; 63 return maInstance; 64 } 65 66 67 68 69 ResourceManager::ResourceManager (void) 70 : maDecks(), 71 maPanels(), 72 maProcessedApplications() 73 { 74 ReadDeckList(); 75 ReadPanelList(); 76 } 77 78 79 80 81 ResourceManager::~ResourceManager (void) 82 { 83 maPanels.clear(); 84 maDecks.clear(); 85 } 86 87 88 89 90 const DeckDescriptor* ResourceManager::GetBestMatchingDeck ( 91 const EnumContext& rContext, 92 const Reference<frame::XFrame>& rxFrame) 93 { 94 ReadLegacyAddons(rxFrame); 95 96 sal_Int32 nBestMatch (EnumContext::NoMatch); 97 const DeckContainer::const_iterator iEnd (maDecks.end()); 98 DeckContainer::const_iterator iBestDeck (iEnd); 99 100 for (DeckContainer::const_iterator iDeck(maDecks.begin()); 101 iDeck!=iEnd; 102 ++iDeck) 103 { 104 const sal_Int32 nMatch (rContext.EvaluateMatch(iDeck->maContexts)); 105 if (nMatch < nBestMatch) 106 { 107 // Found a better matching deck. 108 nBestMatch = nMatch; 109 iBestDeck = iDeck; 110 if (nBestMatch == EnumContext::OptimalMatch) 111 { 112 // We will not find a better match. 113 break; 114 } 115 } 116 } 117 if (iBestDeck != iEnd) 118 return &*iBestDeck; 119 else 120 return NULL; 121 } 122 123 124 125 126 const DeckDescriptor* ResourceManager::GetDeckDescriptor ( 127 const ::rtl::OUString& rsDeckId) const 128 { 129 for (DeckContainer::const_iterator 130 iDeck(maDecks.begin()), 131 iEnd(maDecks.end()); 132 iDeck!=iEnd; 133 ++iDeck) 134 { 135 if (iDeck->msId.equals(rsDeckId)) 136 return &*iDeck; 137 } 138 return NULL; 139 } 140 141 142 143 144 const PanelDescriptor* ResourceManager::GetPanelDescriptor ( 145 const ::rtl::OUString& rsPanelId) const 146 { 147 for (PanelContainer::const_iterator 148 iPanel(maPanels.begin()), 149 iEnd(maPanels.end()); 150 iPanel!=iEnd; 151 ++iPanel) 152 { 153 if (iPanel->msId.equals(rsPanelId)) 154 return &*iPanel; 155 } 156 return NULL; 157 } 158 159 160 161 162 void ResourceManager::SetIsDeckEnabled ( 163 const ::rtl::OUString& rsDeckId, 164 const bool bIsEnabled) 165 { 166 for (DeckContainer::iterator 167 iDeck(maDecks.begin()), 168 iEnd(maDecks.end()); 169 iDeck!=iEnd; 170 ++iDeck) 171 { 172 if (iDeck->msId.equals(rsDeckId)) 173 { 174 iDeck->mbIsEnabled = bIsEnabled; 175 return; 176 } 177 } 178 } 179 180 181 182 183 const ResourceManager::IdContainer& ResourceManager::GetMatchingDecks ( 184 IdContainer& rDeckIds, 185 const EnumContext& rContext, 186 const Reference<frame::XFrame>& rxFrame) 187 { 188 ReadLegacyAddons(rxFrame); 189 190 for (DeckContainer::const_iterator 191 iDeck(maDecks.begin()), 192 iEnd (maDecks.end()); 193 iDeck!=iEnd; 194 ++iDeck) 195 { 196 if (rContext.EvaluateMatch(iDeck->maContexts) != EnumContext::NoMatch) 197 rDeckIds.push_back(iDeck->msId); 198 } 199 200 return rDeckIds; 201 } 202 203 204 205 206 const ResourceManager::IdContainer& ResourceManager::GetMatchingPanels ( 207 IdContainer& rPanelIds, 208 const EnumContext& rContext, 209 const ::rtl::OUString& rsDeckId, 210 const Reference<frame::XFrame>& rxFrame) 211 { 212 ReadLegacyAddons(rxFrame); 213 214 ::std::multimap<sal_Int32,OUString> aOrderedIds; 215 for (PanelContainer::const_iterator 216 iPanel(maPanels.begin()), 217 iEnd(maPanels.end()); 218 iPanel!=iEnd; 219 ++iPanel) 220 { 221 const PanelDescriptor& rPanelDescriptor (*iPanel); 222 if (rPanelDescriptor.msDeckId.equals(rsDeckId)) 223 if (rContext.EvaluateMatch(rPanelDescriptor.maContexts) != EnumContext::NoMatch) 224 aOrderedIds.insert(::std::multimap<sal_Int32,OUString>::value_type( 225 iPanel->mnOrderIndex, 226 iPanel->msId)); 227 } 228 229 for (::std::multimap<sal_Int32,OUString>::const_iterator 230 iId(aOrderedIds.begin()), 231 iEnd(aOrderedIds.end()); 232 iId!=iEnd; 233 ++iId) 234 { 235 rPanelIds.push_back(iId->second); 236 } 237 238 return rPanelIds; 239 } 240 241 242 243 244 void ResourceManager::ReadDeckList (void) 245 { 246 const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory()); 247 const ::utl::OConfigurationTreeRoot aDeckRootNode ( 248 aContext, 249 A2S("org.openoffice.Office.UI.Sidebar/Content/DeckList"), 250 false); 251 if ( ! aDeckRootNode.isValid() ) 252 return; 253 254 const Sequence<OUString> aDeckNodeNames (aDeckRootNode.getNodeNames()); 255 const sal_Int32 nCount (aDeckNodeNames.getLength()); 256 maDecks.resize(nCount); 257 sal_Int32 nWriteIndex(0); 258 for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex) 259 { 260 const ::utl::OConfigurationNode aDeckNode (aDeckRootNode.openNode(aDeckNodeNames[nReadIndex])); 261 if ( ! aDeckNode.isValid()) 262 continue; 263 264 DeckDescriptor& rDeckDescriptor (maDecks[nWriteIndex++]); 265 266 rDeckDescriptor.msTitle = ::comphelper::getString(aDeckNode.getNodeValue("Title")); 267 rDeckDescriptor.msId = ::comphelper::getString(aDeckNode.getNodeValue("Id")); 268 rDeckDescriptor.msIconURL = ::comphelper::getString(aDeckNode.getNodeValue("IconURL")); 269 rDeckDescriptor.msHighContrastIconURL = ::comphelper::getString(aDeckNode.getNodeValue("HighContrastIconURL")); 270 rDeckDescriptor.msHelpURL = ::comphelper::getString(aDeckNode.getNodeValue("HelpURL")); 271 rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle; 272 rDeckDescriptor.mbIsEnabled = true; 273 ReadContextList(aDeckNode.openNode("ContextList"), rDeckDescriptor.maContexts); 274 } 275 276 // When there where invalid nodes then we have to adapt the size 277 // of the deck vector. 278 if (nWriteIndex<nCount) 279 maDecks.resize(nWriteIndex); 280 } 281 282 283 284 285 void ResourceManager::ReadPanelList (void) 286 { 287 const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory()); 288 const ::utl::OConfigurationTreeRoot aPanelRootNode ( 289 aContext, 290 A2S("org.openoffice.Office.UI.Sidebar/Content/PanelList"), 291 false); 292 if ( ! aPanelRootNode.isValid() ) 293 return; 294 295 const Sequence<OUString> aPanelNodeNames (aPanelRootNode.getNodeNames()); 296 const sal_Int32 nCount (aPanelNodeNames.getLength()); 297 maPanels.resize(nCount); 298 sal_Int32 nWriteIndex (0); 299 for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex) 300 { 301 const ::utl::OConfigurationNode aPanelNode (aPanelRootNode.openNode(aPanelNodeNames[nReadIndex])); 302 if ( ! aPanelNode.isValid()) 303 continue; 304 305 PanelDescriptor& rPanelDescriptor (maPanels[nWriteIndex++]); 306 307 rPanelDescriptor.msTitle = ::comphelper::getString( 308 aPanelNode.getNodeValue("Title")); 309 rPanelDescriptor.mbIsTitleBarOptional = ::comphelper::getBOOL( 310 aPanelNode.getNodeValue("TitleBarIsOptional")); 311 rPanelDescriptor.msId = ::comphelper::getString( 312 aPanelNode.getNodeValue("Id")); 313 rPanelDescriptor.msDeckId = ::comphelper::getString( 314 aPanelNode.getNodeValue("DeckId")); 315 rPanelDescriptor.msHelpURL = ::comphelper::getString( 316 aPanelNode.getNodeValue("HelpURL")); 317 rPanelDescriptor.msLayout = ::comphelper::getString( 318 aPanelNode.getNodeValue("Layout")); 319 rPanelDescriptor.msImplementationURL = ::comphelper::getString( 320 aPanelNode.getNodeValue("ImplementationURL")); 321 rPanelDescriptor.mnOrderIndex = ::comphelper::getINT32( 322 aPanelNode.getNodeValue("OrderIndex")); 323 ReadContextList(aPanelNode.openNode("ContextList"), rPanelDescriptor.maContexts); 324 } 325 326 // When there where invalid nodes then we have to adapt the size 327 // of the deck vector. 328 if (nWriteIndex<nCount) 329 maPanels.resize(nWriteIndex); 330 } 331 332 333 334 335 void ResourceManager::ReadContextList ( 336 const ::utl::OConfigurationNode& rNode, 337 ::std::vector<EnumContext>& rContextContainer) const 338 { 339 const Sequence<OUString> aChildNodeNames (rNode.getNodeNames()); 340 const sal_Int32 nCount (aChildNodeNames.getLength()); 341 rContextContainer.resize(nCount); 342 for (sal_Int32 nIndex(0); nIndex<nCount; ++nIndex) 343 { 344 const ::utl::OConfigurationNode aChildNode (rNode.openNode(aChildNodeNames[nIndex])); 345 rContextContainer[nIndex] = EnumContext( 346 ::comphelper::getString(aChildNode.getNodeValue("Application")), 347 ::comphelper::getString(aChildNode.getNodeValue("ApplicationContext"))); 348 } 349 } 350 351 352 353 354 void ResourceManager::ReadLegacyAddons (const Reference<frame::XFrame>& rxFrame) 355 { 356 // Get module name for given frame. 357 ::rtl::OUString sModuleName (GetModuleName(rxFrame)); 358 if (sModuleName.getLength() == 0) 359 return; 360 if (maProcessedApplications.find(sModuleName) != maProcessedApplications.end()) 361 { 362 // Addons for this application have already been read. 363 // There is nothing more to do. 364 return; 365 } 366 367 // Mark module as processed. Even when there is an error that 368 // prevents the configuration data from being read, this error 369 // will not be triggered a second time. 370 maProcessedApplications.insert(sModuleName); 371 372 // Get access to the configuration root node for the application. 373 ::utl::OConfigurationTreeRoot aLegacyRootNode (GetLegacyAddonRootNode(sModuleName)); 374 if ( ! aLegacyRootNode.isValid()) 375 return; 376 377 // Process child nodes. 378 ::std::vector<OUString> aMatchingNodeNames; 379 GetToolPanelNodeNames(aMatchingNodeNames, aLegacyRootNode); 380 const sal_Int32 nCount (aMatchingNodeNames.size()); 381 size_t nDeckWriteIndex (maDecks.size()); 382 size_t nPanelWriteIndex (maPanels.size()); 383 maDecks.resize(maDecks.size() + nCount); 384 maPanels.resize(maPanels.size() + nCount); 385 for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex) 386 { 387 const OUString& rsNodeName (aMatchingNodeNames[nReadIndex]); 388 const ::utl::OConfigurationNode aChildNode (aLegacyRootNode.openNode(rsNodeName)); 389 if ( ! aChildNode.isValid()) 390 continue; 391 392 DeckDescriptor& rDeckDescriptor (maDecks[nDeckWriteIndex++]); 393 rDeckDescriptor.msTitle = ::comphelper::getString(aChildNode.getNodeValue("UIName")); 394 rDeckDescriptor.msId = rsNodeName; 395 rDeckDescriptor.msIconURL = ::comphelper::getString(aChildNode.getNodeValue("ImageURL")); 396 rDeckDescriptor.msHighContrastIconURL = rDeckDescriptor.msIconURL; 397 rDeckDescriptor.msHelpURL = ::comphelper::getString(aChildNode.getNodeValue("HelpURL")); 398 rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle; 399 rDeckDescriptor.maContexts.resize(1); 400 rDeckDescriptor.maContexts.front() = EnumContext(sModuleName, A2S("any")); 401 rDeckDescriptor.mbIsEnabled = true; 402 403 PanelDescriptor& rPanelDescriptor (maPanels[nPanelWriteIndex++]); 404 rPanelDescriptor.msTitle = ::comphelper::getString(aChildNode.getNodeValue("UIName")); 405 rPanelDescriptor.mbIsTitleBarOptional = false; 406 rPanelDescriptor.msId = rsNodeName; 407 rPanelDescriptor.msDeckId = rsNodeName; 408 rPanelDescriptor.msHelpURL = ::comphelper::getString(aChildNode.getNodeValue("HelpURL")); 409 rPanelDescriptor.maContexts.resize(1); 410 rPanelDescriptor.maContexts.front() = EnumContext(sModuleName, A2S("any")); 411 rPanelDescriptor.msLayout = A2S("full"); 412 rPanelDescriptor.msImplementationURL = rsNodeName; 413 } 414 415 // When there where invalid nodes then we have to adapt the size 416 // of the deck and panel vectors. 417 if (nDeckWriteIndex < maDecks.size()) 418 maDecks.resize(nDeckWriteIndex); 419 if (nPanelWriteIndex < maPanels.size()) 420 maPanels.resize(nPanelWriteIndex); 421 } 422 423 424 425 426 ::rtl::OUString ResourceManager::GetModuleName ( 427 const cssu::Reference<css::frame::XFrame>& rxFrame) 428 { 429 try 430 { 431 const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory()); 432 const Reference<frame::XModuleManager> xModuleManager ( 433 aContext.createComponent("com.sun.star.frame.ModuleManager" ), 434 UNO_QUERY_THROW ); 435 return xModuleManager->identify(rxFrame); 436 } 437 catch (const Exception&) 438 { 439 DBG_UNHANDLED_EXCEPTION(); 440 } 441 return OUString(); 442 } 443 444 445 446 447 ::utl::OConfigurationTreeRoot ResourceManager::GetLegacyAddonRootNode ( 448 const ::rtl::OUString& rsModuleName) const 449 { 450 try 451 { 452 const ::comphelper::ComponentContext aContext (::comphelper::getProcessServiceFactory()); 453 const Reference<container::XNameAccess> xModuleAccess ( 454 aContext.createComponent("com.sun.star.frame.ModuleManager"), 455 UNO_QUERY_THROW); 456 const ::comphelper::NamedValueCollection aModuleProperties (xModuleAccess->getByName(rsModuleName)); 457 const ::rtl::OUString sWindowStateRef (aModuleProperties.getOrDefault( 458 "ooSetupFactoryWindowStateConfigRef", 459 ::rtl::OUString())); 460 461 ::rtl::OUStringBuffer aPathComposer; 462 aPathComposer.appendAscii("org.openoffice.Office.UI."); 463 aPathComposer.append(sWindowStateRef); 464 aPathComposer.appendAscii("/UIElements/States"); 465 466 return ::utl::OConfigurationTreeRoot(aContext, aPathComposer.makeStringAndClear(), false); 467 } 468 catch( const Exception& ) 469 { 470 DBG_UNHANDLED_EXCEPTION(); 471 } 472 473 return ::utl::OConfigurationTreeRoot(); 474 } 475 476 477 478 479 void ResourceManager::GetToolPanelNodeNames ( 480 ::std::vector<OUString>& rMatchingNames, 481 const ::utl::OConfigurationTreeRoot aRoot) const 482 { 483 Sequence<OUString> aChildNodeNames (aRoot.getNodeNames()); 484 const sal_Int32 nCount (aChildNodeNames.getLength()); 485 for (sal_Int32 nIndex(0); nIndex<nCount; ++nIndex) 486 { 487 if (aChildNodeNames[nIndex].matchAsciiL( 488 RTL_CONSTASCII_STRINGPARAM( "private:resource/toolpanel/"))) 489 rMatchingNames.push_back(aChildNodeNames[nIndex]); 490 } 491 } 492 493 494 495 } } // end of namespace sfx2::sidebar 496