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