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 "SidebarController.hxx" 25 #include "Deck.hxx" 26 #include "DeckTitleBar.hxx" 27 #include "Panel.hxx" 28 #include "PanelTitleBar.hxx" 29 #include "SidebarPanel.hxx" 30 #include "SidebarResource.hxx" 31 #include "TabBar.hxx" 32 #include "sfx2/sidebar/Theme.hxx" 33 #include "sfx2/sidebar/SidebarChildWindow.hxx" 34 #include "sfx2/sidebar/Tools.hxx" 35 #include "SidebarDockingWindow.hxx" 36 #include "Context.hxx" 37 38 #include "sfxresid.hxx" 39 #include "sfx2/sfxsids.hrc" 40 #include "sfx2/titledockwin.hxx" 41 #include "sfxlocal.hrc" 42 #include <vcl/floatwin.hxx> 43 #include <vcl/fixed.hxx> 44 #include "splitwin.hxx" 45 #include <svl/smplhint.hxx> 46 #include <tools/link.hxx> 47 #include <toolkit/helper/vclunohelper.hxx> 48 49 #include <comphelper/componentfactory.hxx> 50 #include <comphelper/processfactory.hxx> 51 #include <comphelper/componentcontext.hxx> 52 #include <comphelper/namedvaluecollection.hxx> 53 54 #include <com/sun/star/frame/XDispatchProvider.hpp> 55 #include <com/sun/star/lang/XInitialization.hpp> 56 #include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp> 57 #include <com/sun/star/ui/ContextChangeEventObject.hpp> 58 #include <com/sun/star/ui/XUIElementFactory.hpp> 59 #include <com/sun/star/util/XURLTransformer.hpp> 60 #include <com/sun/star/util/URL.hpp> 61 #include <com/sun/star/rendering/XSpriteCanvas.hpp> 62 63 #include <boost/bind.hpp> 64 #include <boost/function.hpp> 65 #include <boost/scoped_array.hpp> 66 67 68 using namespace css; 69 using namespace cssu; 70 using ::rtl::OUString; 71 72 73 #undef VERBOSE 74 75 namespace 76 { 77 const static OUString gsReadOnlyCommandName (A2S(".uno:EditDoc")); 78 const static sal_Int32 gnMaximumSidebarWidth (400); 79 const static sal_Int32 gnWidthCloseThreshold (70); 80 const static sal_Int32 gnWidthOpenThreshold (40); 81 } 82 83 84 namespace sfx2 { namespace sidebar { 85 86 SidebarController::SidebarControllerContainer SidebarController::maSidebarControllerContainer; 87 88 namespace { 89 enum MenuId 90 { 91 MID_UNLOCK_TASK_PANEL = 1, 92 MID_LOCK_TASK_PANEL, 93 MID_CUSTOMIZATION, 94 MID_RESTORE_DEFAULT, 95 MID_FIRST_PANEL, 96 MID_FIRST_HIDE = 1000 97 }; 98 99 /** When in doubt, show this deck. 100 */ 101 static const ::rtl::OUString gsDefaultDeckId(A2S("PropertyDeck")); 102 } 103 104 105 SidebarController::SidebarController ( 106 SidebarDockingWindow* pParentWindow, 107 const cssu::Reference<css::frame::XFrame>& rxFrame) 108 : SidebarControllerInterfaceBase(m_aMutex), 109 mpCurrentDeck(), 110 mpParentWindow(pParentWindow), 111 mpTabBar(new TabBar( 112 mpParentWindow, 113 rxFrame, 114 ::boost::bind(&SidebarController::OpenThenSwitchToDeck, this, _1), 115 ::boost::bind(&SidebarController::ShowPopupMenu, this, _1,_2))), 116 mxFrame(rxFrame), 117 maCurrentContext(OUString(), OUString()), 118 maRequestedContext(), 119 mnRequestedForceFlags(SwitchFlag_NoForce), 120 msCurrentDeckId(gsDefaultDeckId), 121 msCurrentDeckTitle(), 122 maPropertyChangeForwarder(::boost::bind(&SidebarController::BroadcastPropertyChange, this)), 123 maContextChangeUpdate(::boost::bind(&SidebarController::UpdateConfigurations, this)), 124 maAsynchronousDeckSwitch(), 125 mbIsDeckRequestedOpen(), 126 mbIsDeckOpen(), 127 mbCanDeckBeOpened(true), 128 mnSavedSidebarWidth(pParentWindow->GetSizePixel().Width()), 129 maFocusManager(::boost::bind(&SidebarController::ShowPanel, this, _1)), 130 mxReadOnlyModeDispatch(), 131 mbIsDocumentReadOnly(false), 132 mpSplitWindow(NULL), 133 mnWidthOnSplitterButtonDown(0), 134 mpCloseIndicator() 135 { 136 if (pParentWindow == NULL) 137 { 138 OSL_ASSERT(pParentWindow!=NULL); 139 return; 140 } 141 142 // Listen for context change events. 143 cssu::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer ( 144 css::ui::ContextChangeEventMultiplexer::get( 145 ::comphelper::getProcessComponentContext())); 146 if (xMultiplexer.is()) 147 xMultiplexer->addContextChangeEventListener( 148 static_cast<css::ui::XContextChangeEventListener*>(this), 149 mxFrame->getController()); 150 151 // Listen for window events. 152 mpParentWindow->AddEventListener(LINK(this, SidebarController, WindowEventHandler)); 153 154 // Listen for theme property changes. 155 Theme::GetPropertySet()->addPropertyChangeListener( 156 A2S(""), 157 static_cast<css::beans::XPropertyChangeListener*>(this)); 158 159 // Get the dispatch object as preparation to listen for changes of 160 // the read-only state. 161 const util::URL aURL (Tools::GetURL(gsReadOnlyCommandName)); 162 mxReadOnlyModeDispatch = Tools::GetDispatch(mxFrame, aURL); 163 if (mxReadOnlyModeDispatch.is()) 164 mxReadOnlyModeDispatch->addStatusListener(this, aURL); 165 166 SwitchToDeck(A2S("default")); 167 168 WeakReference<SidebarController> xWeakController (this); 169 maSidebarControllerContainer.insert( 170 SidebarControllerContainer::value_type( 171 rxFrame, 172 xWeakController)); 173 } 174 175 176 177 178 SidebarController::~SidebarController (void) 179 { 180 } 181 182 183 184 185 SidebarController* SidebarController::GetSidebarControllerForFrame ( 186 const cssu::Reference<css::frame::XFrame>& rxFrame) 187 { 188 SidebarControllerContainer::iterator iEntry (maSidebarControllerContainer.find(rxFrame)); 189 if (iEntry == maSidebarControllerContainer.end()) 190 return NULL; 191 192 cssu::Reference<XInterface> xController (iEntry->second.get()); 193 if ( ! xController.is()) 194 return NULL; 195 196 return dynamic_cast<SidebarController*>(xController.get()); 197 } 198 199 200 201 202 void SAL_CALL SidebarController::disposing (void) 203 { 204 SidebarControllerContainer::iterator iEntry (maSidebarControllerContainer.find(mxFrame)); 205 if (iEntry != maSidebarControllerContainer.end()) 206 maSidebarControllerContainer.erase(iEntry); 207 208 maFocusManager.Clear(); 209 210 cssu::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer ( 211 css::ui::ContextChangeEventMultiplexer::get( 212 ::comphelper::getProcessComponentContext())); 213 if (xMultiplexer.is()) 214 xMultiplexer->removeAllContextChangeEventListeners( 215 static_cast<css::ui::XContextChangeEventListener*>(this)); 216 217 if (mxReadOnlyModeDispatch.is()) 218 mxReadOnlyModeDispatch->removeStatusListener(this, Tools::GetURL(gsReadOnlyCommandName)); 219 if (mpSplitWindow != NULL) 220 { 221 mpSplitWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler)); 222 mpSplitWindow = NULL; 223 } 224 225 if (mpParentWindow != NULL) 226 { 227 mpParentWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler)); 228 mpParentWindow = NULL; 229 } 230 231 if (mpCurrentDeck) 232 { 233 mpCurrentDeck->Dispose(); 234 mpCurrentDeck->PrintWindowTree(); 235 mpCurrentDeck.reset(); 236 } 237 238 mpTabBar.reset(); 239 240 Theme::GetPropertySet()->removePropertyChangeListener( 241 A2S(""), 242 static_cast<css::beans::XPropertyChangeListener*>(this)); 243 244 maContextChangeUpdate.CancelRequest(); 245 maAsynchronousDeckSwitch.CancelRequest(); 246 } 247 248 249 250 251 void SAL_CALL SidebarController::notifyContextChangeEvent (const css::ui::ContextChangeEventObject& rEvent) 252 throw(cssu::RuntimeException) 253 { 254 // Update to the requested new context asynchronously to avoid 255 // subtle errors caused by SFX2 which in rare cases can not 256 // properly handle a synchronous update. 257 maRequestedContext = Context( 258 rEvent.ApplicationName, 259 rEvent.ContextName); 260 if (maRequestedContext != maCurrentContext) 261 { 262 maAsynchronousDeckSwitch.CancelRequest(); 263 maContextChangeUpdate.RequestCall(); 264 } 265 } 266 267 268 269 270 void SAL_CALL SidebarController::disposing (const css::lang::EventObject& rEventObject) 271 throw(cssu::RuntimeException) 272 { 273 (void)rEventObject; 274 275 dispose(); 276 } 277 278 279 280 281 void SAL_CALL SidebarController::propertyChange (const css::beans::PropertyChangeEvent& rEvent) 282 throw(cssu::RuntimeException) 283 { 284 (void)rEvent; 285 286 maPropertyChangeForwarder.RequestCall(); 287 } 288 289 290 291 292 void SAL_CALL SidebarController::statusChanged (const css::frame::FeatureStateEvent& rEvent) 293 throw(cssu::RuntimeException) 294 { 295 bool bIsReadWrite (true); 296 if (rEvent.IsEnabled) 297 rEvent.State >>= bIsReadWrite; 298 299 if (mbIsDocumentReadOnly != !bIsReadWrite) 300 { 301 mbIsDocumentReadOnly = !bIsReadWrite; 302 303 // Force the current deck to update its panel list. 304 if ( ! mbIsDocumentReadOnly) 305 msCurrentDeckId = gsDefaultDeckId; 306 mnRequestedForceFlags |= SwitchFlag_ForceSwitch; 307 maAsynchronousDeckSwitch.CancelRequest(); 308 maContextChangeUpdate.RequestCall(); 309 } 310 } 311 312 313 314 315 void SAL_CALL SidebarController::requestLayout (void) 316 throw(cssu::RuntimeException) 317 { 318 if (mpCurrentDeck) 319 mpCurrentDeck->RequestLayout(); 320 RestrictWidth(); 321 } 322 323 324 325 326 void SidebarController::BroadcastPropertyChange (void) 327 { 328 DataChangedEvent aEvent (DATACHANGED_USER); 329 mpParentWindow->NotifyAllChilds(aEvent); 330 mpParentWindow->Invalidate(INVALIDATE_CHILDREN); 331 } 332 333 334 335 336 void SidebarController::NotifyResize (void) 337 { 338 if( !bool(mpTabBar)) 339 { 340 OSL_ASSERT( bool(mpTabBar)); 341 return; 342 } 343 344 Window* pParentWindow = mpTabBar->GetParent(); 345 346 const sal_Int32 nWidth (pParentWindow->GetSizePixel().Width()); 347 const sal_Int32 nHeight (pParentWindow->GetSizePixel().Height()); 348 349 mbIsDeckOpen = (nWidth > TabBar::GetDefaultWidth()); 350 351 if (mnSavedSidebarWidth <= 0) 352 mnSavedSidebarWidth = nWidth; 353 354 bool bIsDeckVisible; 355 if (mbCanDeckBeOpened) 356 { 357 const bool bIsOpening (nWidth > mnWidthOnSplitterButtonDown); 358 if (bIsOpening) 359 bIsDeckVisible = nWidth >= TabBar::GetDefaultWidth() + gnWidthOpenThreshold; 360 else 361 bIsDeckVisible = nWidth >= TabBar::GetDefaultWidth() + gnWidthCloseThreshold; 362 mbIsDeckRequestedOpen = bIsDeckVisible; 363 UpdateCloseIndicator(!bIsDeckVisible); 364 } 365 else 366 bIsDeckVisible = false; 367 368 // Place the deck. 369 if (mpCurrentDeck) 370 { 371 if (bIsDeckVisible) 372 { 373 mpCurrentDeck->SetPosSizePixel(0,0, nWidth-TabBar::GetDefaultWidth(), nHeight); 374 mpCurrentDeck->Show(); 375 mpCurrentDeck->RequestLayout(); 376 } 377 else 378 mpCurrentDeck->Hide(); 379 } 380 381 // Place the tab bar. 382 mpTabBar->SetPosSizePixel(nWidth-TabBar::GetDefaultWidth(),0,TabBar::GetDefaultWidth(),nHeight); 383 mpTabBar->Show(); 384 385 // Determine if the closer of the deck can be shown. 386 if (mpCurrentDeck) 387 { 388 DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar(); 389 if (pTitleBar != NULL && pTitleBar->IsVisible()) 390 pTitleBar->SetCloserVisible(CanModifyChildWindowWidth()); 391 } 392 393 RestrictWidth(); 394 } 395 396 397 398 399 void SidebarController::ProcessNewWidth (const sal_Int32 nNewWidth) 400 { 401 if ( ! mbIsDeckRequestedOpen) 402 return; 403 404 if (mbIsDeckRequestedOpen.get()) 405 { 406 // Deck became large enough to be shown. Show it. 407 mnSavedSidebarWidth = nNewWidth; 408 RequestOpenDeck(); 409 } 410 else 411 { 412 // Deck became too small. Close it completely. 413 // If window is wider than the tab bar then mark the deck as being visible, even when it its not. 414 // This is to trigger an adjustment of the width to the width of the tab bar. 415 mbIsDeckOpen = true; 416 RequestCloseDeck(); 417 418 if (mnWidthOnSplitterButtonDown > TabBar::GetDefaultWidth()) 419 mnSavedSidebarWidth = mnWidthOnSplitterButtonDown; 420 } 421 } 422 423 424 425 426 void SidebarController::UpdateConfigurations (void) 427 { 428 if (maCurrentContext != maRequestedContext 429 || mnRequestedForceFlags!=SwitchFlag_NoForce) 430 { 431 maCurrentContext = maRequestedContext; 432 433 // Find the set of decks that could be displayed for the new context. 434 ResourceManager::DeckContextDescriptorContainer aDecks; 435 ResourceManager::Instance().GetMatchingDecks ( 436 aDecks, 437 maCurrentContext, 438 mbIsDocumentReadOnly, 439 mxFrame); 440 441 // Notify the tab bar about the updated set of decks. 442 mpTabBar->SetDecks(aDecks); 443 444 // Find the new deck. By default that is the same as the old 445 // one. If that is not set or not enabled, then choose the 446 // first enabled deck. 447 OUString sNewDeckId; 448 for (ResourceManager::DeckContextDescriptorContainer::const_iterator 449 iDeck(aDecks.begin()), 450 iEnd(aDecks.end()); 451 iDeck!=iEnd; 452 ++iDeck) 453 { 454 if (iDeck->mbIsEnabled) 455 { 456 if (iDeck->msId.equals(msCurrentDeckId)) 457 { 458 sNewDeckId = msCurrentDeckId; 459 break; 460 } 461 else if (sNewDeckId.getLength() == 0) 462 sNewDeckId = iDeck->msId; 463 } 464 } 465 466 if (sNewDeckId.getLength() == 0) 467 { 468 // We did not find a valid deck. 469 RequestCloseDeck(); 470 return; 471 } 472 473 // Tell the tab bar to highlight the button associated 474 // with the deck. 475 mpTabBar->HighlightDeck(sNewDeckId); 476 477 SwitchToDeck( 478 *ResourceManager::Instance().GetDeckDescriptor(sNewDeckId), 479 maCurrentContext); 480 } 481 } 482 483 484 485 486 void SidebarController::OpenThenSwitchToDeck ( 487 const ::rtl::OUString& rsDeckId) 488 { 489 RequestOpenDeck(); 490 SwitchToDeck(rsDeckId); 491 mpTabBar->Invalidate(); 492 } 493 494 495 496 497 void SidebarController::RequestSwitchToDeck ( 498 const ::rtl::OUString& rsDeckId) 499 { 500 maContextChangeUpdate.CancelRequest(); 501 maAsynchronousDeckSwitch.RequestCall( 502 ::boost::bind(&SidebarController::OpenThenSwitchToDeck, this, rsDeckId)); 503 } 504 505 506 507 508 void SidebarController::SwitchToDeck ( 509 const ::rtl::OUString& rsDeckId) 510 { 511 if ( ! msCurrentDeckId.equals(rsDeckId) 512 || ! mbIsDeckOpen 513 || mnRequestedForceFlags!=SwitchFlag_NoForce) 514 { 515 const DeckDescriptor* pDeckDescriptor = ResourceManager::Instance().GetDeckDescriptor(rsDeckId); 516 if (pDeckDescriptor != NULL) 517 SwitchToDeck(*pDeckDescriptor, maCurrentContext); 518 } 519 } 520 521 522 523 524 void SidebarController::SwitchToDeck ( 525 const DeckDescriptor& rDeckDescriptor, 526 const Context& rContext) 527 { 528 maFocusManager.Clear(); 529 530 const bool bForceNewDeck ((mnRequestedForceFlags&SwitchFlag_ForceNewDeck)!=0); 531 const bool bForceNewPanels ((mnRequestedForceFlags&SwitchFlag_ForceNewPanels)!=0); 532 mnRequestedForceFlags = SwitchFlag_NoForce; 533 534 if ( ! msCurrentDeckId.equals(rDeckDescriptor.msId) 535 || bForceNewDeck) 536 { 537 // When the deck changes then destroy the deck and all panels 538 // and create everything new. 539 if (mpCurrentDeck) 540 { 541 mpCurrentDeck->Dispose(); 542 mpCurrentDeck.reset(); 543 } 544 545 msCurrentDeckId = rDeckDescriptor.msId; 546 } 547 mpTabBar->HighlightDeck(msCurrentDeckId); 548 549 // Determine the panels to display in the deck. 550 ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors; 551 ResourceManager::Instance().GetMatchingPanels( 552 aPanelContextDescriptors, 553 rContext, 554 rDeckDescriptor.msId, 555 mxFrame); 556 557 if (aPanelContextDescriptors.empty()) 558 { 559 // There are no panels to be displayed in the current context. 560 if (EnumContext::GetContextEnum(rContext.msContext) != EnumContext::Context_Empty) 561 { 562 // Switch to the "empty" context and try again. 563 SwitchToDeck( 564 rDeckDescriptor, 565 Context( 566 rContext.msApplication, 567 EnumContext::GetContextName(EnumContext::Context_Empty))); 568 return; 569 } 570 else 571 { 572 // This is already the "empty" context. Looks like we have 573 // to live with an empty deck. 574 } 575 } 576 577 // Provide a configuration and Deck object. 578 if ( ! mpCurrentDeck) 579 { 580 mpCurrentDeck.reset( 581 new Deck( 582 rDeckDescriptor, 583 mpParentWindow, 584 ::boost::bind(&SidebarController::RequestCloseDeck, this))); 585 msCurrentDeckTitle = rDeckDescriptor.msTitle; 586 587 } 588 if ( ! mpCurrentDeck) 589 return; 590 591 #ifdef DEBUG 592 // Show the context name in the deck title bar. 593 DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar(); 594 if (pTitleBar != NULL) 595 pTitleBar->SetTitle(rDeckDescriptor.msTitle+A2S(" (")+maCurrentContext.msContext+A2S(")")); 596 #endif 597 598 // Update the panel list. 599 const sal_Int32 nNewPanelCount (aPanelContextDescriptors.size()); 600 SharedPanelContainer aNewPanels; 601 const SharedPanelContainer& rCurrentPanels (mpCurrentDeck->GetPanels()); 602 aNewPanels.resize(nNewPanelCount); 603 sal_Int32 nWriteIndex (0); 604 bool bHasPanelSetChanged (false); 605 for (sal_Int32 nReadIndex=0; nReadIndex<nNewPanelCount; ++nReadIndex) 606 { 607 const ResourceManager::PanelContextDescriptor& rPanelContexDescriptor ( 608 aPanelContextDescriptors[nReadIndex]); 609 610 // Determine if the panel can be displayed. 611 const bool bIsPanelVisible (!mbIsDocumentReadOnly || rPanelContexDescriptor.mbShowForReadOnlyDocuments); 612 if ( ! bIsPanelVisible) 613 continue; 614 615 // Find the corresponding panel among the currently active 616 // panels. 617 SharedPanelContainer::const_iterator iPanel; 618 if (bForceNewPanels) 619 { 620 // All panels have to be created in any case. There is no 621 // point in searching already existing panels. 622 iPanel = rCurrentPanels.end(); 623 } 624 else 625 { 626 iPanel = ::std::find_if( 627 rCurrentPanels.begin(), 628 rCurrentPanels.end(), 629 ::boost::bind(&Panel::HasIdPredicate, _1, ::boost::cref(rPanelContexDescriptor.msId))); 630 } 631 if (iPanel != rCurrentPanels.end()) 632 { 633 // Panel already exists in current deck. Reuse it. 634 aNewPanels[nWriteIndex] = *iPanel; 635 aNewPanels[nWriteIndex]->SetExpanded(rPanelContexDescriptor.mbIsInitiallyVisible); 636 } 637 else 638 { 639 // Panel does not yet exist or creation of new panels is forced. 640 // Create it. 641 aNewPanels[nWriteIndex] = CreatePanel( 642 rPanelContexDescriptor.msId, 643 mpCurrentDeck->GetPanelParentWindow(), 644 rPanelContexDescriptor.mbIsInitiallyVisible, 645 rContext); 646 bHasPanelSetChanged = true; 647 } 648 if( bool(aNewPanels[nWriteIndex])) 649 { 650 // Depending on the context we have to change the command 651 // for the "more options" dialog. 652 PanelTitleBar* pTitleBar = aNewPanels[nWriteIndex]->GetTitleBar(); 653 if (pTitleBar != NULL) 654 { 655 pTitleBar->SetMoreOptionsCommand( 656 rPanelContexDescriptor.msMenuCommand, 657 mxFrame); 658 } 659 660 ++nWriteIndex; 661 } 662 663 } 664 aNewPanels.resize(nWriteIndex); 665 666 // Activate the deck and the new set of panels. 667 mpCurrentDeck->SetPosSizePixel( 668 0, 669 0, 670 mpParentWindow->GetSizePixel().Width()-TabBar::GetDefaultWidth(), 671 mpParentWindow->GetSizePixel().Height()); 672 mpCurrentDeck->SetPanels(aNewPanels); 673 mpCurrentDeck->Show(); 674 675 mpParentWindow->SetText(rDeckDescriptor.msTitle); 676 677 if (bHasPanelSetChanged) 678 NotifyResize(); 679 680 // Tell the focus manager about the new panels and tab bar 681 // buttons. 682 maFocusManager.SetDeckTitle(mpCurrentDeck->GetTitleBar()); 683 maFocusManager.SetPanels(aNewPanels); 684 mpTabBar->UpdateFocusManager(maFocusManager); 685 UpdateTitleBarIcons(); 686 } 687 688 689 690 691 SharedPanel SidebarController::CreatePanel ( 692 const OUString& rsPanelId, 693 ::Window* pParentWindow, 694 const bool bIsInitiallyExpanded, 695 const Context& rContext) 696 { 697 const PanelDescriptor* pPanelDescriptor = ResourceManager::Instance().GetPanelDescriptor(rsPanelId); 698 if (pPanelDescriptor == NULL) 699 return SharedPanel(); 700 701 // Create the panel which is the parent window of the UIElement. 702 SharedPanel pPanel (new Panel( 703 *pPanelDescriptor, 704 pParentWindow, 705 bIsInitiallyExpanded, 706 ::boost::bind(&Deck::RequestLayout, mpCurrentDeck.get()), 707 ::boost::bind(&SidebarController::GetCurrentContext, this))); 708 709 // Create the XUIElement. 710 Reference<ui::XUIElement> xUIElement (CreateUIElement( 711 pPanel->GetComponentInterface(), 712 pPanelDescriptor->msImplementationURL, 713 pPanelDescriptor->mbWantsCanvas, 714 rContext)); 715 if (xUIElement.is()) 716 { 717 // Initialize the panel and add it to the active deck. 718 pPanel->SetUIElement(xUIElement); 719 } 720 else 721 { 722 pPanel.reset(); 723 } 724 725 return pPanel; 726 } 727 728 729 730 731 Reference<ui::XUIElement> SidebarController::CreateUIElement ( 732 const Reference<awt::XWindowPeer>& rxWindow, 733 const ::rtl::OUString& rsImplementationURL, 734 const bool bWantsCanvas, 735 const Context& rContext) 736 { 737 try 738 { 739 const ::comphelper::ComponentContext aComponentContext (::comphelper::getProcessServiceFactory()); 740 const Reference<ui::XUIElementFactory> xUIElementFactory ( 741 aComponentContext.createComponent("com.sun.star.ui.UIElementFactoryManager"), 742 UNO_QUERY_THROW); 743 744 // Create the XUIElement. 745 ::comphelper::NamedValueCollection aCreationArguments; 746 aCreationArguments.put("Frame", makeAny(mxFrame)); 747 aCreationArguments.put("ParentWindow", makeAny(rxWindow)); 748 SfxDockingWindow* pSfxDockingWindow = dynamic_cast<SfxDockingWindow*>(mpParentWindow); 749 if (pSfxDockingWindow != NULL) 750 aCreationArguments.put("SfxBindings", makeAny(sal_uInt64(&pSfxDockingWindow->GetBindings()))); 751 aCreationArguments.put("Theme", Theme::GetPropertySet()); 752 aCreationArguments.put("Sidebar", makeAny(Reference<ui::XSidebar>(static_cast<ui::XSidebar*>(this)))); 753 if (bWantsCanvas) 754 { 755 Reference<rendering::XSpriteCanvas> xCanvas (VCLUnoHelper::GetWindow(rxWindow)->GetSpriteCanvas()); 756 aCreationArguments.put("Canvas", makeAny(xCanvas)); 757 } 758 aCreationArguments.put("ApplicationName", makeAny(rContext.msApplication)); 759 aCreationArguments.put("ContextName", makeAny(rContext.msContext)); 760 761 Reference<ui::XUIElement> xUIElement( 762 xUIElementFactory->createUIElement( 763 rsImplementationURL, 764 Sequence<beans::PropertyValue>(aCreationArguments.getPropertyValues())), 765 UNO_QUERY_THROW); 766 767 return xUIElement; 768 } 769 catch(Exception& rException) 770 { 771 OSL_TRACE("caught exception: %s", 772 OUStringToOString(rException.Message, RTL_TEXTENCODING_ASCII_US).getStr()); 773 // For some reason we can not create the actual panel. 774 // Probably because its factory was not properly registered. 775 // TODO: provide feedback to developer to better pinpoint the 776 // source of the error. 777 778 return NULL; 779 } 780 } 781 782 783 784 785 IMPL_LINK(SidebarController, WindowEventHandler, VclWindowEvent*, pEvent) 786 { 787 if (pEvent==NULL) 788 return sal_False; 789 790 if (pEvent->GetWindow() == mpParentWindow) 791 { 792 switch (pEvent->GetId()) 793 { 794 case VCLEVENT_WINDOW_SHOW: 795 case VCLEVENT_WINDOW_RESIZE: 796 NotifyResize(); 797 break; 798 799 case VCLEVENT_WINDOW_DATACHANGED: 800 // Force an update of deck and tab bar to reflect 801 // changes in theme (high contrast mode). 802 Theme::HandleDataChange(); 803 UpdateTitleBarIcons(); 804 mpParentWindow->Invalidate(); 805 mnRequestedForceFlags |= SwitchFlag_ForceNewDeck | SwitchFlag_ForceNewPanels; 806 maAsynchronousDeckSwitch.CancelRequest(); 807 maContextChangeUpdate.RequestCall(); 808 break; 809 810 case SFX_HINT_DYING: 811 dispose(); 812 break; 813 814 case VCLEVENT_WINDOW_PAINT: 815 OSL_TRACE("Paint"); 816 break; 817 818 default: 819 break; 820 } 821 } 822 else if (pEvent->GetWindow()==mpSplitWindow && mpSplitWindow!=NULL) 823 { 824 switch (pEvent->GetId()) 825 { 826 case VCLEVENT_WINDOW_MOUSEBUTTONDOWN: 827 mnWidthOnSplitterButtonDown = mpParentWindow->GetSizePixel().Width(); 828 break; 829 830 case VCLEVENT_WINDOW_MOUSEBUTTONUP: 831 { 832 ProcessNewWidth(mpParentWindow->GetSizePixel().Width()); 833 mnWidthOnSplitterButtonDown = 0; 834 break; 835 } 836 837 case SFX_HINT_DYING: 838 dispose(); 839 break; 840 } 841 } 842 843 return sal_True; 844 } 845 846 847 848 849 void SidebarController::ShowPopupMenu ( 850 const Rectangle& rButtonBox, 851 const ::std::vector<TabBar::DeckMenuData>& rMenuData) const 852 { 853 ::boost::shared_ptr<PopupMenu> pMenu = CreatePopupMenu(rMenuData); 854 pMenu->SetSelectHdl(LINK(this, SidebarController, OnMenuItemSelected)); 855 856 // pass toolbox button rect so the menu can stay open on button up 857 Rectangle aBox (rButtonBox); 858 aBox.Move(mpTabBar->GetPosPixel().X(), 0); 859 pMenu->Execute(mpParentWindow, aBox, POPUPMENU_EXECUTE_DOWN); 860 } 861 862 863 864 865 void SidebarController::ShowDetailMenu (const ::rtl::OUString& rsMenuCommand) const 866 { 867 try 868 { 869 const util::URL aURL (Tools::GetURL(rsMenuCommand)); 870 Reference<frame::XDispatch> xDispatch (Tools::GetDispatch(mxFrame, aURL)); 871 if (xDispatch.is()) 872 xDispatch->dispatch(aURL, Sequence<beans::PropertyValue>()); 873 } 874 catch(Exception& rException) 875 { 876 OSL_TRACE("caught exception: %s", 877 OUStringToOString(rException.Message, RTL_TEXTENCODING_ASCII_US).getStr()); 878 } 879 } 880 881 882 883 884 ::boost::shared_ptr<PopupMenu> SidebarController::CreatePopupMenu ( 885 const ::std::vector<TabBar::DeckMenuData>& rMenuData) const 886 { 887 // Create the top level popup menu. 888 ::boost::shared_ptr<PopupMenu> pMenu (new PopupMenu()); 889 FloatingWindow* pMenuWindow = dynamic_cast<FloatingWindow*>(pMenu->GetWindow()); 890 if (pMenuWindow != NULL) 891 { 892 pMenuWindow->SetPopupModeFlags(pMenuWindow->GetPopupModeFlags() | FLOATWIN_POPUPMODE_NOMOUSEUPCLOSE); 893 } 894 895 // Create sub menu for customization (hiding of deck tabs.) 896 PopupMenu* pCustomizationMenu = new PopupMenu(); 897 898 SidebarResource aLocalResource; 899 900 // Add one entry for every tool panel element to individually make 901 // them visible or hide them. 902 sal_Int32 nIndex (0); 903 for(::std::vector<TabBar::DeckMenuData>::const_iterator 904 iItem(rMenuData.begin()), 905 iEnd(rMenuData.end()); 906 iItem!=iEnd; 907 ++iItem,++nIndex) 908 { 909 const sal_Int32 nMenuIndex (nIndex+MID_FIRST_PANEL); 910 pMenu->InsertItem(nMenuIndex, iItem->msDisplayName, MIB_RADIOCHECK); 911 pMenu->CheckItem(nMenuIndex, iItem->mbIsCurrentDeck ? sal_True : sal_False); 912 pMenu->EnableItem(nMenuIndex, (iItem->mbIsEnabled&&iItem->mbIsActive) ? sal_True : sal_False); 913 914 const sal_Int32 nSubMenuIndex (nIndex+MID_FIRST_HIDE); 915 if (iItem->mbIsCurrentDeck) 916 { 917 // Don't allow the currently visible deck to be disabled. 918 pCustomizationMenu->InsertItem(nSubMenuIndex, iItem->msDisplayName, MIB_RADIOCHECK); 919 pCustomizationMenu->CheckItem(nSubMenuIndex, sal_True); 920 } 921 else 922 { 923 pCustomizationMenu->InsertItem(nSubMenuIndex, iItem->msDisplayName, MIB_CHECKABLE); 924 pCustomizationMenu->CheckItem(nSubMenuIndex, iItem->mbIsActive ? sal_True : sal_False); 925 } 926 } 927 928 pMenu->InsertSeparator(); 929 930 // Add entry for docking or un-docking the tool panel. 931 if (mpParentWindow->IsFloatingMode()) 932 pMenu->InsertItem(MID_LOCK_TASK_PANEL, String(SfxResId(STR_SFX_DOCK))); 933 else 934 pMenu->InsertItem(MID_UNLOCK_TASK_PANEL, String(SfxResId(STR_SFX_UNDOCK))); 935 936 pCustomizationMenu->InsertSeparator(); 937 pCustomizationMenu->InsertItem(MID_RESTORE_DEFAULT, String(SfxResId(STRING_RESTORE))); 938 939 pMenu->InsertItem(MID_CUSTOMIZATION, String(SfxResId(STRING_CUSTOMIZATION))); 940 pMenu->SetPopupMenu(MID_CUSTOMIZATION, pCustomizationMenu); 941 942 pMenu->RemoveDisabledEntries(sal_False, sal_False); 943 944 return pMenu; 945 } 946 947 948 949 950 IMPL_LINK(SidebarController, OnMenuItemSelected, Menu*, pMenu) 951 { 952 if (pMenu == NULL) 953 { 954 OSL_ENSURE(pMenu!=NULL, "sfx2::sidebar::SidebarController::OnMenuItemSelected: illegal menu!"); 955 return 0; 956 } 957 958 pMenu->Deactivate(); 959 const sal_Int32 nIndex (pMenu->GetCurItemId()); 960 switch (nIndex) 961 { 962 case MID_UNLOCK_TASK_PANEL: 963 mpParentWindow->SetFloatingMode(sal_True); 964 break; 965 966 case MID_LOCK_TASK_PANEL: 967 mpParentWindow->SetFloatingMode(sal_False); 968 break; 969 970 case MID_RESTORE_DEFAULT: 971 mpTabBar->RestoreHideFlags(); 972 break; 973 974 default: 975 { 976 try 977 { 978 if (nIndex >= MID_FIRST_PANEL && nIndex<MID_FIRST_HIDE) 979 SwitchToDeck(mpTabBar->GetDeckIdForIndex(nIndex - MID_FIRST_PANEL)); 980 else if (nIndex >=MID_FIRST_HIDE) 981 if (pMenu->GetItemBits(nIndex) == MIB_CHECKABLE) 982 mpTabBar->ToggleHideFlag(nIndex-MID_FIRST_HIDE); 983 } 984 catch (RuntimeException&) 985 { 986 } 987 } 988 break; 989 } 990 991 return 1; 992 } 993 994 995 996 997 void SidebarController::RequestCloseDeck (void) 998 { 999 mbIsDeckRequestedOpen = false; 1000 UpdateDeckOpenState(); 1001 } 1002 1003 1004 1005 1006 void SidebarController::RequestOpenDeck (void) 1007 { 1008 mbIsDeckRequestedOpen = true; 1009 UpdateDeckOpenState(); 1010 } 1011 1012 1013 1014 1015 void SidebarController::UpdateDeckOpenState (void) 1016 { 1017 if ( ! mbIsDeckRequestedOpen) 1018 // No state requested. 1019 return; 1020 1021 // Update (change) the open state when it either has not yet been initialized 1022 // or when its value differs from the requested state. 1023 if ( ! mbIsDeckOpen 1024 || mbIsDeckOpen.get() != mbIsDeckRequestedOpen.get()) 1025 { 1026 if (mbIsDeckRequestedOpen.get()) 1027 { 1028 if (mnSavedSidebarWidth <= TabBar::GetDefaultWidth()) 1029 SetChildWindowWidth(SidebarChildWindow::GetDefaultWidth(mpParentWindow)); 1030 else 1031 SetChildWindowWidth(mnSavedSidebarWidth); 1032 } 1033 else 1034 { 1035 if ( ! mpParentWindow->IsFloatingMode()) 1036 mnSavedSidebarWidth = SetChildWindowWidth(TabBar::GetDefaultWidth()); 1037 if (mnWidthOnSplitterButtonDown > TabBar::GetDefaultWidth()) 1038 mnSavedSidebarWidth = mnWidthOnSplitterButtonDown; 1039 mpParentWindow->SetStyle(mpParentWindow->GetStyle() & ~WB_SIZEABLE); 1040 } 1041 1042 mbIsDeckOpen = mbIsDeckRequestedOpen.get(); 1043 if (mbIsDeckOpen.get() && mpCurrentDeck) 1044 mpCurrentDeck->Show(mbIsDeckOpen.get()); 1045 NotifyResize(); 1046 } 1047 } 1048 1049 1050 1051 1052 FocusManager& SidebarController::GetFocusManager (void) 1053 { 1054 return maFocusManager; 1055 } 1056 1057 1058 1059 1060 bool SidebarController::CanModifyChildWindowWidth (void) 1061 { 1062 SfxSplitWindow* pSplitWindow = GetSplitWindow(); 1063 if (pSplitWindow == NULL) 1064 return false; 1065 1066 sal_uInt16 nRow (0xffff); 1067 sal_uInt16 nColumn (0xffff); 1068 if (pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow)) 1069 { 1070 sal_uInt16 nRowCount (pSplitWindow->GetWindowCount(nColumn)); 1071 return nRowCount==1; 1072 } 1073 else 1074 return false; 1075 } 1076 1077 1078 1079 1080 sal_Int32 SidebarController::SetChildWindowWidth (const sal_Int32 nNewWidth) 1081 { 1082 SfxSplitWindow* pSplitWindow = GetSplitWindow(); 1083 if (pSplitWindow == NULL) 1084 return 0; 1085 1086 sal_uInt16 nRow (0xffff); 1087 sal_uInt16 nColumn (0xffff); 1088 pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow); 1089 const long nColumnWidth (pSplitWindow->GetLineSize(nColumn)); 1090 1091 Window* pWindow = mpParentWindow; 1092 const Point aWindowPosition (pWindow->GetPosPixel()); 1093 const Size aWindowSize (pWindow->GetSizePixel()); 1094 1095 pSplitWindow->MoveWindow( 1096 mpParentWindow, 1097 Size(nNewWidth, aWindowSize.Height()), 1098 nColumn, 1099 nRow); 1100 static_cast<SplitWindow*>(pSplitWindow)->Split(); 1101 1102 return static_cast<sal_Int32>(nColumnWidth); 1103 } 1104 1105 1106 1107 1108 void SidebarController::RestrictWidth (void) 1109 { 1110 SfxSplitWindow* pSplitWindow = GetSplitWindow(); 1111 if (pSplitWindow != NULL) 1112 { 1113 const sal_uInt16 nId (pSplitWindow->GetItemId(mpParentWindow)); 1114 const sal_uInt16 nSetId (pSplitWindow->GetSet(nId)); 1115 pSplitWindow->SetItemSizeRange( 1116 nSetId, 1117 Range(TabBar::GetDefaultWidth(), gnMaximumSidebarWidth)); 1118 } 1119 } 1120 1121 1122 1123 1124 SfxSplitWindow* SidebarController::GetSplitWindow (void) 1125 { 1126 if (mpParentWindow != NULL) 1127 { 1128 SfxSplitWindow* pSplitWindow = dynamic_cast<SfxSplitWindow*>(mpParentWindow->GetParent()); 1129 if (pSplitWindow != mpSplitWindow) 1130 { 1131 if (mpSplitWindow != NULL) 1132 mpSplitWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler)); 1133 1134 mpSplitWindow = pSplitWindow; 1135 1136 if (mpSplitWindow != NULL) 1137 mpSplitWindow->AddEventListener(LINK(this, SidebarController, WindowEventHandler)); 1138 } 1139 return mpSplitWindow; 1140 } 1141 else 1142 return NULL; 1143 } 1144 1145 1146 1147 1148 void SidebarController::UpdateCloseIndicator (const bool bCloseAfterDrag) 1149 { 1150 if (mpParentWindow == NULL) 1151 return; 1152 1153 if (bCloseAfterDrag) 1154 { 1155 // Make sure that the indicator exists. 1156 if ( ! mpCloseIndicator) 1157 { 1158 mpCloseIndicator.reset(new FixedImage(mpParentWindow)); 1159 FixedImage* pFixedImage = static_cast<FixedImage*>(mpCloseIndicator.get()); 1160 const Image aImage (Theme::GetImage(Theme::Image_CloseIndicator)); 1161 pFixedImage->SetImage(aImage); 1162 pFixedImage->SetSizePixel(aImage.GetSizePixel()); 1163 pFixedImage->SetBackground(Theme::GetWallpaper(Theme::Paint_DeckBackground)); 1164 } 1165 1166 // Place and show the indicator. 1167 const Size aWindowSize (mpParentWindow->GetSizePixel()); 1168 const Size aImageSize (mpCloseIndicator->GetSizePixel()); 1169 mpCloseIndicator->SetPosPixel( 1170 Point( 1171 aWindowSize.Width() - TabBar::GetDefaultWidth() - aImageSize.Width(), 1172 (aWindowSize.Height() - aImageSize.Height())/2)); 1173 mpCloseIndicator->Show(); 1174 } 1175 else 1176 { 1177 // Hide but don't delete the indicator. 1178 if (mpCloseIndicator) 1179 mpCloseIndicator->Hide(); 1180 } 1181 } 1182 1183 1184 1185 1186 void SidebarController::UpdateTitleBarIcons (void) 1187 { 1188 if ( ! mpCurrentDeck) 1189 return; 1190 1191 const bool bIsHighContrastModeActive (Theme::IsHighContrastMode()); 1192 const ResourceManager& rResourceManager (ResourceManager::Instance()); 1193 1194 // Update the deck icon. 1195 const DeckDescriptor* pDeckDescriptor = rResourceManager.GetDeckDescriptor(mpCurrentDeck->GetId()); 1196 if (pDeckDescriptor != NULL && mpCurrentDeck->GetTitleBar()) 1197 { 1198 const OUString sIconURL( 1199 bIsHighContrastModeActive 1200 ? pDeckDescriptor->msHighContrastTitleBarIconURL 1201 : pDeckDescriptor->msTitleBarIconURL); 1202 mpCurrentDeck->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame)); 1203 } 1204 1205 // Update the panel icons. 1206 const SharedPanelContainer& rPanels (mpCurrentDeck->GetPanels()); 1207 for (SharedPanelContainer::const_iterator 1208 iPanel(rPanels.begin()), iEnd(rPanels.end()); 1209 iPanel!=iEnd; 1210 ++iPanel) 1211 { 1212 if ( ! *iPanel) 1213 continue; 1214 if ((*iPanel)->GetTitleBar() == NULL) 1215 continue; 1216 const PanelDescriptor* pPanelDescriptor = rResourceManager.GetPanelDescriptor((*iPanel)->GetId()); 1217 if (pPanelDescriptor == NULL) 1218 continue; 1219 const OUString sIconURL ( 1220 bIsHighContrastModeActive 1221 ? pPanelDescriptor->msHighContrastTitleBarIconURL 1222 : pPanelDescriptor->msTitleBarIconURL); 1223 (*iPanel)->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame)); 1224 } 1225 } 1226 1227 1228 1229 1230 void SidebarController::ShowPanel (const Panel& rPanel) 1231 { 1232 if (mpCurrentDeck) 1233 mpCurrentDeck->ShowPanel(rPanel); 1234 } 1235 1236 1237 1238 1239 Context SidebarController::GetCurrentContext (void) const 1240 { 1241 return maCurrentContext; 1242 } 1243 1244 1245 } } // end of namespace sfx2::sidebar 1246