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