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