1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 */ 9 10 #include <sfx2/bindings.hxx> 11 #include <sfx2/viewsh.hxx> 12 #include <sfx2/dispatch.hxx> 13 #include <sfx2/notebookbar/SfxNotebookBar.hxx> 14 #include <vcl/notebookbar.hxx> 15 #include <vcl/syswin.hxx> 16 #include <sfx2/viewfrm.hxx> 17 #include <sfx2/sfxsids.hrc> 18 #include <comphelper/processfactory.hxx> 19 #include <comphelper/lok.hxx> 20 #include <com/sun/star/frame/UnknownModuleException.hpp> 21 #include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp> 22 #include <com/sun/star/ui/XContextChangeEventMultiplexer.hpp> 23 #include <com/sun/star/frame/XLayoutManager.hpp> 24 #include <officecfg/Office/UI/ToolbarMode.hxx> 25 #include <com/sun/star/frame/XModuleManager.hpp> 26 #include <com/sun/star/frame/ModuleManager.hpp> 27 #include <com/sun/star/beans/XPropertySet.hpp> 28 #include <unotools/confignode.hxx> 29 #include <comphelper/types.hxx> 30 #include <framework/addonsoptions.hxx> 31 #include <vcl/NotebookBarAddonsMerger.hxx> 32 #include <vector> 33 #include <map> 34 #include <vcl/WeldedTabbedNotebookbar.hxx> 35 36 using namespace sfx2; 37 using namespace css::uno; 38 using namespace css::ui; 39 using namespace css; 40 41 #define MENUBAR_STR "private:resource/menubar/menubar" 42 43 const char MERGE_NOTEBOOKBAR_URL[] = "URL"; 44 45 bool SfxNotebookBar::m_bLock = false; 46 bool SfxNotebookBar::m_bHide = false; 47 std::map<const SfxViewShell*, std::shared_ptr<WeldedTabbedNotebookbar>> SfxNotebookBar::m_pNotebookBarWeldedWrapper; 48 49 static void NotebookbarAddonValues( 50 std::vector<Image>& aImageValues, 51 std::vector<css::uno::Sequence<css::uno::Sequence<css::beans::PropertyValue>>>& 52 aExtensionValues) 53 { 54 framework::AddonsOptions aAddonsItems; 55 56 for (int nIdx = 0; nIdx < aAddonsItems.GetAddonsNotebookBarCount(); nIdx++) 57 { 58 const css::uno::Sequence<css::uno::Sequence<css::beans::PropertyValue>> aExtension 59 = aAddonsItems.GetAddonsNotebookBarPart(nIdx); 60 for (const css::uno::Sequence<css::beans::PropertyValue>& rExtensionVal : aExtension) 61 { 62 Image aImage; 63 bool isBigImage = true; 64 for (const auto& rProp : rExtensionVal) 65 { 66 if (rProp.Name == MERGE_NOTEBOOKBAR_URL) 67 { 68 OUString sImage; 69 rProp.Value >>= sImage; 70 aImage = Image(framework::AddonsOptions().GetImageFromURL(sImage, isBigImage)); 71 } 72 } 73 aImageValues.push_back(aImage); 74 } 75 aExtensionValues.push_back(aExtension); 76 } 77 } 78 79 static Reference<frame::XLayoutManager> lcl_getLayoutManager( const Reference<frame::XFrame>& xFrame ) 80 { 81 css::uno::Reference<css::frame::XLayoutManager> xLayoutManager; 82 83 if (xFrame.is()) 84 { 85 Reference<css::beans::XPropertySet> xPropSet(xFrame, UNO_QUERY); 86 87 if (xPropSet.is()) 88 { 89 Any aValue = xPropSet->getPropertyValue("LayoutManager"); 90 aValue >>= xLayoutManager; 91 } 92 } 93 94 return xLayoutManager; 95 } 96 97 static OUString lcl_getAppName( vcl::EnumContext::Application eApp ) 98 { 99 switch ( eApp ) 100 { 101 case vcl::EnumContext::Application::Writer: 102 return "Writer"; 103 break; 104 case vcl::EnumContext::Application::Calc: 105 return "Calc"; 106 break; 107 case vcl::EnumContext::Application::Impress: 108 return "Impress"; 109 break; 110 case vcl::EnumContext::Application::Draw: 111 return "Draw"; 112 break; 113 case vcl::EnumContext::Application::Formula: 114 return "Formula"; 115 break; 116 default: 117 return OUString(); 118 break; 119 } 120 } 121 122 static void lcl_setNotebookbarFileName( vcl::EnumContext::Application eApp, const OUString& sFileName ) 123 { 124 std::shared_ptr<comphelper::ConfigurationChanges> aBatch( 125 comphelper::ConfigurationChanges::create( ::comphelper::getProcessComponentContext() ) ); 126 switch ( eApp ) 127 { 128 case vcl::EnumContext::Application::Writer: 129 officecfg::Office::UI::ToolbarMode::ActiveWriter::set( sFileName, aBatch ); 130 break; 131 case vcl::EnumContext::Application::Calc: 132 officecfg::Office::UI::ToolbarMode::ActiveCalc::set( sFileName, aBatch ); 133 break; 134 case vcl::EnumContext::Application::Impress: 135 officecfg::Office::UI::ToolbarMode::ActiveImpress::set( sFileName, aBatch ); 136 break; 137 case vcl::EnumContext::Application::Draw: 138 officecfg::Office::UI::ToolbarMode::ActiveDraw::set( sFileName, aBatch ); 139 break; 140 default: 141 break; 142 } 143 aBatch->commit(); 144 } 145 146 static OUString lcl_getNotebookbarFileName( vcl::EnumContext::Application eApp ) 147 { 148 switch ( eApp ) 149 { 150 case vcl::EnumContext::Application::Writer: 151 return officecfg::Office::UI::ToolbarMode::ActiveWriter::get(); 152 break; 153 case vcl::EnumContext::Application::Calc: 154 return officecfg::Office::UI::ToolbarMode::ActiveCalc::get(); 155 break; 156 case vcl::EnumContext::Application::Impress: 157 return officecfg::Office::UI::ToolbarMode::ActiveImpress::get(); 158 break; 159 case vcl::EnumContext::Application::Draw: 160 return officecfg::Office::UI::ToolbarMode::ActiveDraw::get(); 161 break; 162 163 default: 164 break; 165 } 166 return OUString(); 167 } 168 169 static utl::OConfigurationTreeRoot lcl_getCurrentImplConfigRoot() 170 { 171 return utl::OConfigurationTreeRoot(::comphelper::getProcessComponentContext(), 172 "org.openoffice.Office.UI.ToolbarMode/", 173 true); 174 } 175 176 static utl::OConfigurationNode lcl_getCurrentImplConfigNode(const Reference<css::frame::XFrame>& xFrame, 177 utl::OConfigurationTreeRoot const & rNotebookbarNode ) 178 { 179 if (!rNotebookbarNode.isValid()) 180 return utl::OConfigurationNode(); 181 182 const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( ::comphelper::getProcessComponentContext() ); 183 184 vcl::EnumContext::Application eApp = vcl::EnumContext::GetApplicationEnum( xModuleManager->identify( xFrame ) ); 185 OUString aActive = lcl_getNotebookbarFileName( eApp ); 186 187 const utl::OConfigurationNode aImplsNode = rNotebookbarNode.openNode("Applications/" + lcl_getAppName( eApp) + "/Modes"); 188 const Sequence<OUString> aModeNodeNames( aImplsNode.getNodeNames() ); 189 190 for ( const auto& rModeNodeName : aModeNodeNames ) 191 { 192 const utl::OConfigurationNode aImplNode( aImplsNode.openNode( rModeNodeName ) ); 193 if ( !aImplNode.isValid() ) 194 continue; 195 196 OUString aCommandArg = comphelper::getString( aImplNode.getNodeValue( "CommandArg" ) ); 197 198 if ( aCommandArg == aActive ) 199 { 200 return aImplNode; 201 } 202 } 203 204 return utl::OConfigurationNode(); 205 } 206 207 void SfxNotebookBar::CloseMethod(SfxBindings& rBindings) 208 { 209 SfxFrame& rFrame = rBindings.GetDispatcher_Impl()->GetFrame()->GetFrame(); 210 CloseMethod(rFrame.GetSystemWindow()); 211 } 212 213 void SfxNotebookBar::CloseMethod(SystemWindow* pSysWindow) 214 { 215 if (pSysWindow) 216 { 217 RemoveListeners(pSysWindow); 218 if(pSysWindow->GetNotebookBar()) 219 pSysWindow->CloseNotebookBar(); 220 if (SfxViewFrame::Current()) 221 SfxNotebookBar::ShowMenubar(SfxViewFrame::Current(), true); 222 } 223 } 224 225 void SfxNotebookBar::LockNotebookBar() 226 { 227 m_bHide = true; 228 m_bLock = true; 229 } 230 231 void SfxNotebookBar::UnlockNotebookBar() 232 { 233 m_bHide = false; 234 m_bLock = false; 235 } 236 237 bool SfxNotebookBar::IsActive() 238 { 239 if (m_bHide) 240 return false; 241 242 vcl::EnumContext::Application eApp = vcl::EnumContext::Application::Any; 243 244 if (SfxViewFrame::Current()) 245 { 246 const Reference<frame::XFrame>& xFrame = SfxViewFrame::Current()->GetFrame().GetFrameInterface(); 247 if (!xFrame.is()) 248 return false; 249 250 const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( ::comphelper::getProcessComponentContext() ); 251 try 252 { 253 eApp = vcl::EnumContext::GetApplicationEnum(xModuleManager->identify(xFrame)); 254 } 255 catch (css::frame::UnknownModuleException& e) 256 { 257 SAL_WARN("sfx.appl", "SfxNotebookBar::IsActive(): " + e.Message); 258 return false; 259 } 260 } 261 else 262 return false; 263 264 OUString appName(lcl_getAppName( eApp )); 265 266 if (appName.isEmpty()) 267 return false; 268 269 270 OUString aPath = "org.openoffice.Office.UI.ToolbarMode/Applications/" + appName; 271 272 const utl::OConfigurationTreeRoot aAppNode( 273 ::comphelper::getProcessComponentContext(), 274 aPath, 275 false); 276 if ( !aAppNode.isValid() ) 277 return false; 278 279 OUString aActive = comphelper::getString( aAppNode.getNodeValue( "Active" ) ); 280 281 const utl::OConfigurationNode aModesNode = aAppNode.openNode("Modes"); 282 const Sequence<OUString> aModeNodeNames( aModesNode.getNodeNames() ); 283 284 for ( const auto& rModeNodeName : aModeNodeNames ) 285 { 286 const utl::OConfigurationNode aModeNode( aModesNode.openNode( rModeNodeName ) ); 287 if ( !aModeNode.isValid() ) 288 continue; 289 290 OUString aCommandArg = comphelper::getString( aModeNode.getNodeValue( "CommandArg" ) ); 291 292 if ( aCommandArg == aActive ) 293 { 294 return comphelper::getBOOL( aModeNode.getNodeValue( "HasNotebookbar" ) ); 295 } 296 } 297 return false; 298 } 299 300 void SfxNotebookBar::ExecMethod(SfxBindings& rBindings, const OUString& rUIName) 301 { 302 // Save active UI file name 303 if ( !rUIName.isEmpty() && SfxViewFrame::Current() ) 304 { 305 const Reference<frame::XFrame>& xFrame = SfxViewFrame::Current()->GetFrame().GetFrameInterface(); 306 if (xFrame.is()) 307 { 308 const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( ::comphelper::getProcessComponentContext() ); 309 vcl::EnumContext::Application eApp = vcl::EnumContext::GetApplicationEnum(xModuleManager->identify(xFrame)); 310 lcl_setNotebookbarFileName( eApp, rUIName ); 311 } 312 } 313 314 // trigger the StateMethod 315 rBindings.Invalidate(SID_NOTEBOOKBAR); 316 rBindings.Update(); 317 } 318 319 bool SfxNotebookBar::StateMethod(SfxBindings& rBindings, const OUString& rUIFile, 320 bool bReloadNotebookbar) 321 { 322 SfxFrame& rFrame = rBindings.GetDispatcher_Impl()->GetFrame()->GetFrame(); 323 return StateMethod(rFrame.GetSystemWindow(), rFrame.GetFrameInterface(), rUIFile, 324 bReloadNotebookbar); 325 } 326 327 bool SfxNotebookBar::StateMethod(SystemWindow* pSysWindow, 328 const Reference<css::frame::XFrame>& xFrame, 329 const OUString& rUIFile, bool bReloadNotebookbar) 330 { 331 if (!pSysWindow) 332 { 333 if (SfxViewFrame::Current() && SfxViewFrame::Current()->GetWindow().GetSystemWindow()) 334 pSysWindow = SfxViewFrame::Current()->GetWindow().GetSystemWindow(); 335 else 336 return false; 337 } 338 339 if (IsActive()) 340 { 341 css::uno::Reference<css::uno::XComponentContext> xContext = comphelper::getProcessComponentContext(); 342 const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( xContext ); 343 OUString aModuleName = xModuleManager->identify( xFrame ); 344 vcl::EnumContext::Application eApp = vcl::EnumContext::GetApplicationEnum( aModuleName ); 345 OUString sFile = lcl_getNotebookbarFileName( eApp ); 346 OUString sNewFile = rUIFile + sFile; 347 OUString sCurrentFile; 348 VclPtr<NotebookBar> pNotebookBar = pSysWindow->GetNotebookBar(); 349 if ( pNotebookBar ) 350 sCurrentFile = pNotebookBar->GetUIFilePath(); 351 352 bool bChangedFile = sNewFile != sCurrentFile; 353 354 if ((!sFile.isEmpty() && bChangedFile) || !pNotebookBar || !pNotebookBar->IsVisible() 355 || bReloadNotebookbar || comphelper::LibreOfficeKit::isActive()) 356 { 357 const SfxViewShell* pViewShell = SfxViewShell::Current(); 358 359 // Notebookbar was loaded too early what caused: 360 // * in LOK: Paste Special feature was incorrectly initialized 361 // Skip first request so Notebookbar will be initialized after document was loaded 362 static std::map<const void*, bool> bSkippedFirstInit; 363 if (comphelper::LibreOfficeKit::isActive() && eApp == vcl::EnumContext::Application::Writer 364 && bSkippedFirstInit.find(pViewShell) == bSkippedFirstInit.end()) 365 { 366 bSkippedFirstInit[pViewShell] = true; 367 return false; 368 } 369 370 RemoveListeners(pSysWindow); 371 372 OUString aBuf = rUIFile + sFile; 373 374 //Addons For Notebookbar 375 std::vector<Image> aImageValues; 376 std::vector<css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > > aExtensionValues; 377 NotebookBarAddonsItem aNotebookBarAddonsItem; 378 NotebookbarAddonValues(aImageValues , aExtensionValues); 379 aNotebookBarAddonsItem.aAddonValues = aExtensionValues; 380 aNotebookBarAddonsItem.aImageValues = aImageValues; 381 382 // setup if necessary 383 if (comphelper::LibreOfficeKit::isActive()) 384 { 385 // update the current LOK language and locale for the dialog tunneling 386 comphelper::LibreOfficeKit::setLanguageTag(pViewShell->GetLOKLanguageTag()); 387 comphelper::LibreOfficeKit::setLocale(pViewShell->GetLOKLocale()); 388 } 389 390 pSysWindow->SetNotebookBar(aBuf, xFrame, aNotebookBarAddonsItem , bReloadNotebookbar); 391 pNotebookBar = pSysWindow->GetNotebookBar(); 392 pNotebookBar->Show(); 393 394 395 bool hasWeldedWrapper = m_pNotebookBarWeldedWrapper.find(pViewShell) != m_pNotebookBarWeldedWrapper.end(); 396 if ((!hasWeldedWrapper || bReloadNotebookbar) && pNotebookBar->IsWelded()) 397 { 398 sal_uInt64 nWindowId = reinterpret_cast<sal_uInt64>(pViewShell); 399 m_pNotebookBarWeldedWrapper.emplace(std::make_pair(pViewShell, 400 new WeldedTabbedNotebookbar(pNotebookBar->GetMainContainer(), 401 pNotebookBar->GetUIFilePath(), 402 xFrame, 403 nWindowId))); 404 pNotebookBar->SetDisposeCallback(LINK(nullptr, SfxNotebookBar, VclDisposeHdl), pViewShell); 405 } 406 407 pNotebookBar->GetParent()->Resize(); 408 409 utl::OConfigurationTreeRoot aRoot(lcl_getCurrentImplConfigRoot()); 410 const utl::OConfigurationNode aModeNode(lcl_getCurrentImplConfigNode(xFrame, aRoot)); 411 SfxNotebookBar::ShowMenubar( comphelper::getBOOL( aModeNode.getNodeValue( "HasMenubar" ) ) ); 412 413 SfxViewFrame* pView = SfxViewFrame::Current(); 414 415 if(pView) 416 { 417 pNotebookBar->ControlListenerForCurrentController(true); 418 } 419 } 420 421 return true; 422 } 423 else if (auto pNotebookBar = pSysWindow->GetNotebookBar()) 424 { 425 pNotebookBar->Hide(); 426 pNotebookBar->GetParent()->Resize(); 427 SfxNotebookBar::ShowMenubar(true); 428 } 429 430 return false; 431 } 432 433 void SfxNotebookBar::RemoveListeners(SystemWindow const * pSysWindow) 434 { 435 if (auto pNotebookBar = pSysWindow->GetNotebookBar()) 436 { 437 pNotebookBar->StopListeningAllControllers(); 438 } 439 } 440 441 void SfxNotebookBar::ShowMenubar(bool bShow) 442 { 443 if (m_bLock) 444 return; 445 446 m_bLock = true; 447 448 Reference<frame::XFrame> xFrame; 449 vcl::EnumContext::Application eCurrentApp = vcl::EnumContext::Application::NONE; 450 uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext(); 451 const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( xContext ); 452 453 if ( SfxViewFrame::Current() ) 454 { 455 xFrame = SfxViewFrame::Current()->GetFrame().GetFrameInterface(); 456 eCurrentApp = vcl::EnumContext::GetApplicationEnum( xModuleManager->identify( xFrame ) ); 457 } 458 459 SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst(); 460 while( pViewFrame ) 461 { 462 xFrame = pViewFrame->GetFrame().GetFrameInterface(); 463 if ( xFrame.is() ) 464 { 465 vcl::EnumContext::Application eApp = 466 vcl::EnumContext::GetApplicationEnum( xModuleManager->identify( xFrame ) ); 467 468 if ( eApp == eCurrentApp ) 469 { 470 const Reference<frame::XLayoutManager>& xLayoutManager = 471 lcl_getLayoutManager( xFrame ); 472 473 if (xLayoutManager.is()) 474 { 475 xLayoutManager->lock(); 476 477 if (xLayoutManager->getElement(MENUBAR_STR).is()) 478 { 479 if (xLayoutManager->isElementVisible(MENUBAR_STR) && !bShow) 480 xLayoutManager->hideElement(MENUBAR_STR); 481 else if(!xLayoutManager->isElementVisible(MENUBAR_STR) && bShow) 482 xLayoutManager->showElement(MENUBAR_STR); 483 } 484 485 xLayoutManager->unlock(); 486 } 487 } 488 } 489 490 pViewFrame = SfxViewFrame::GetNext( *pViewFrame ); 491 } 492 m_bLock = false; 493 } 494 495 void SfxNotebookBar::ShowMenubar(SfxViewFrame const * pViewFrame, bool bShow) 496 { 497 if (m_bLock) 498 return; 499 500 m_bLock = true; 501 502 Reference<frame::XFrame> xFrame = pViewFrame->GetFrame().GetFrameInterface(); 503 if (xFrame.is()) 504 { 505 const Reference<frame::XLayoutManager>& xLayoutManager = lcl_getLayoutManager(xFrame); 506 if (xLayoutManager.is()) 507 { 508 xLayoutManager->lock(); 509 510 if (xLayoutManager->getElement(MENUBAR_STR).is()) 511 { 512 if (xLayoutManager->isElementVisible(MENUBAR_STR) && !bShow) 513 xLayoutManager->hideElement(MENUBAR_STR); 514 else if (!xLayoutManager->isElementVisible(MENUBAR_STR) && bShow) 515 xLayoutManager->showElement(MENUBAR_STR); 516 } 517 518 xLayoutManager->unlock(); 519 } 520 } 521 m_bLock = false; 522 } 523 524 void SfxNotebookBar::ToggleMenubar() 525 { 526 if (!SfxViewFrame::Current()) 527 return; 528 529 const Reference<frame::XFrame>& xFrame = SfxViewFrame::Current()->GetFrame().GetFrameInterface(); 530 if (!xFrame.is()) 531 return; 532 533 const Reference<frame::XLayoutManager>& xLayoutManager = 534 lcl_getLayoutManager(xFrame); 535 536 bool bShow = true; 537 if (xLayoutManager.is() && xLayoutManager->getElement(MENUBAR_STR).is()) 538 { 539 if (xLayoutManager->isElementVisible(MENUBAR_STR)) 540 { 541 SfxNotebookBar::ShowMenubar(false); 542 bShow = false; 543 } 544 else 545 SfxNotebookBar::ShowMenubar(true); 546 } 547 548 // Save menubar settings 549 if (IsActive()) 550 { 551 utl::OConfigurationTreeRoot aRoot(lcl_getCurrentImplConfigRoot()); 552 utl::OConfigurationNode aModeNode(lcl_getCurrentImplConfigNode(xFrame, aRoot)); 553 aModeNode.setNodeValue( "HasMenubar", toAny<bool>( bShow ) ); 554 aRoot.commit(); 555 } 556 } 557 558 void SfxNotebookBar::ReloadNotebookBar(const OUString& sUIPath) 559 { 560 if (SfxNotebookBar::IsActive()) 561 { 562 SfxViewShell* pViewShell = SfxViewShell::Current(); 563 sfx2::SfxNotebookBar::StateMethod(pViewShell->GetViewFrame()->GetBindings(), sUIPath, true); 564 } 565 } 566 567 IMPL_STATIC_LINK(SfxNotebookBar, VclDisposeHdl, const SfxViewShell*, pViewShell, void) 568 { 569 m_pNotebookBarWeldedWrapper.erase(pViewShell); 570 } 571 572 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 573
