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 "SidebarPanel.hxx" 29 #include "SidebarResource.hxx" 30 #include "TabBar.hxx" 31 #include "sfx2/sidebar/Theme.hxx" 32 #include "sfx2/sidebar/SidebarChildWindow.hxx" 33 #include "sfx2/sidebar/Tools.hxx" 34 #include "SidebarDockingWindow.hxx" 35 #include "Context.hxx" 36 37 #include "sfxresid.hxx" 38 #include "sfx2/sfxsids.hrc" 39 #include "sfx2/titledockwin.hxx" 40 #include "sfxlocal.hrc" 41 #include <vcl/floatwin.hxx> 42 #include <vcl/fixed.hxx> 43 #include "splitwin.hxx" 44 #include <svl/smplhint.hxx> 45 #include <tools/link.hxx> 46 #include <toolkit/helper/vclunohelper.hxx> 47 48 #include <comphelper/componentfactory.hxx> 49 #include <comphelper/processfactory.hxx> 50 #include <comphelper/componentcontext.hxx> 51 #include <comphelper/namedvaluecollection.hxx> 52 53 #include <com/sun/star/frame/XDispatchProvider.hpp> 54 #include <com/sun/star/lang/XInitialization.hpp> 55 #include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp> 56 #include <com/sun/star/ui/ContextChangeEventObject.hpp> 57 #include <com/sun/star/ui/XUIElementFactory.hpp> 58 #include <com/sun/star/util/XURLTransformer.hpp> 59 #include <com/sun/star/util/URL.hpp> 60 #include <com/sun/star/rendering/XSpriteCanvas.hpp> 61 62 #include <boost/bind.hpp> 63 #include <boost/function.hpp> 64 #include <boost/scoped_array.hpp> 65 66 67 using namespace css; 68 using namespace cssu; 69 using ::rtl::OUString; 70 71 72 #undef VERBOSE 73 74 namespace 75 { 76 const static OUString gsReadOnlyCommandName (A2S(".uno:EditDoc")); 77 const static sal_Int32 gnMaximumSidebarWidth (400); 78 const static sal_Int32 gnWidthCloseThreshold (70); 79 const static sal_Int32 gnWidthOpenThreshold (40); 80 } 81 82 83 namespace sfx2 { namespace sidebar { 84 85 namespace { 86 enum MenuId 87 { 88 MID_UNLOCK_TASK_PANEL = 1, 89 MID_LOCK_TASK_PANEL, 90 MID_CUSTOMIZATION, 91 MID_RESTORE_DEFAULT, 92 MID_FIRST_PANEL, 93 MID_FIRST_HIDE = 1000 94 }; 95 } 96 97 98 SidebarController::SidebarController ( 99 SidebarDockingWindow* pParentWindow, 100 const cssu::Reference<css::frame::XFrame>& rxFrame) 101 : SidebarControllerInterfaceBase(m_aMutex), 102 mpCurrentDeck(), 103 mpParentWindow(pParentWindow), 104 mpTabBar(new TabBar( 105 mpParentWindow, 106 rxFrame, 107 ::boost::bind(&SidebarController::OpenThenSwitchToDeck, this, _1), 108 ::boost::bind(&SidebarController::ShowPopupMenu, this, _1,_2,_3))), 109 mxFrame(rxFrame), 110 maCurrentContext(OUString(), OUString()), 111 maRequestedContext(), 112 msCurrentDeckId(A2S("PropertyDeck")), 113 msCurrentDeckTitle(), 114 maPropertyChangeForwarder(::boost::bind(&SidebarController::BroadcastPropertyChange, this)), 115 maContextChangeUpdate(::boost::bind(&SidebarController::UpdateConfigurations, this)), 116 mbIsDeckRequestedOpen(), 117 mbIsDeckOpen(), 118 mbCanDeckBeOpened(true), 119 mnSavedSidebarWidth(pParentWindow->GetSizePixel().Width()), 120 maFocusManager(::boost::bind(&SidebarController::ShowPanel, this, _1)), 121 mxReadOnlyModeDispatch(), 122 mbIsDocumentReadOnly(false), 123 mpSplitWindow(NULL), 124 mnWidthOnSplitterButtonDown(0), 125 mpCloseIndicator() 126 { 127 if (pParentWindow == NULL) 128 { 129 OSL_ASSERT(pParentWindow!=NULL); 130 return; 131 } 132 133 // Listen for context change events. 134 cssu::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer ( 135 css::ui::ContextChangeEventMultiplexer::get( 136 ::comphelper::getProcessComponentContext())); 137 if (xMultiplexer.is()) 138 xMultiplexer->addContextChangeEventListener( 139 static_cast<css::ui::XContextChangeEventListener*>(this), 140 mxFrame->getController()); 141 142 // Listen for window events. 143 mpParentWindow->AddEventListener(LINK(this, SidebarController, WindowEventHandler)); 144 145 // Listen for theme property changes. 146 Theme::GetPropertySet()->addPropertyChangeListener( 147 A2S(""), 148 static_cast<css::beans::XPropertyChangeListener*>(this)); 149 150 // Get the dispatch object as preparation to listen for changes of 151 // the read-only state. 152 const util::URL aURL (Tools::GetURL(gsReadOnlyCommandName)); 153 mxReadOnlyModeDispatch = Tools::GetDispatch(mxFrame, aURL); 154 if (mxReadOnlyModeDispatch.is()) 155 mxReadOnlyModeDispatch->addStatusListener(this, aURL); 156 157 SwitchToDeck(A2S("default")); 158 } 159 160 161 162 163 SidebarController::~SidebarController (void) 164 { 165 } 166 167 168 169 170 void SAL_CALL SidebarController::disposing (void) 171 { 172 maFocusManager.Clear(); 173 174 cssu::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer ( 175 css::ui::ContextChangeEventMultiplexer::get( 176 ::comphelper::getProcessComponentContext())); 177 if (xMultiplexer.is()) 178 xMultiplexer->removeAllContextChangeEventListeners( 179 static_cast<css::ui::XContextChangeEventListener*>(this)); 180 181 if (mxReadOnlyModeDispatch.is()) 182 mxReadOnlyModeDispatch->removeStatusListener(this, Tools::GetURL(gsReadOnlyCommandName)); 183 if (mpSplitWindow != NULL) 184 { 185 mpSplitWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler)); 186 mpSplitWindow = NULL; 187 } 188 189 if (mpParentWindow != NULL) 190 { 191 mpParentWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler)); 192 mpParentWindow = NULL; 193 } 194 195 if (mpCurrentDeck) 196 { 197 mpCurrentDeck->Dispose(); 198 mpCurrentDeck->PrintWindowTree(); 199 mpCurrentDeck.reset(); 200 } 201 202 mpTabBar.reset(); 203 204 Theme::GetPropertySet()->removePropertyChangeListener( 205 A2S(""), 206 static_cast<css::beans::XPropertyChangeListener*>(this)); 207 } 208 209 210 211 212 void SAL_CALL SidebarController::notifyContextChangeEvent (const css::ui::ContextChangeEventObject& rEvent) 213 throw(cssu::RuntimeException) 214 { 215 // Update to the requested new context asynchronously to avoid 216 // subtle errors caused by SFX2 which in rare cases can not 217 // properly handle a synchronous update. 218 maRequestedContext = Context( 219 rEvent.ApplicationName, 220 rEvent.ContextName); 221 if (maRequestedContext != maCurrentContext) 222 maContextChangeUpdate.RequestCall(); 223 } 224 225 226 227 228 void SAL_CALL SidebarController::disposing (const css::lang::EventObject& rEventObject) 229 throw(cssu::RuntimeException) 230 { 231 (void)rEventObject; 232 233 dispose(); 234 } 235 236 237 238 239 void SAL_CALL SidebarController::propertyChange (const css::beans::PropertyChangeEvent& rEvent) 240 throw(cssu::RuntimeException) 241 { 242 (void)rEvent; 243 244 maPropertyChangeForwarder.RequestCall(); 245 } 246 247 248 249 250 void SAL_CALL SidebarController::statusChanged (const css::frame::FeatureStateEvent& rEvent) 251 throw(cssu::RuntimeException) 252 { 253 bool bIsReadWrite (true); 254 if (rEvent.IsEnabled) 255 rEvent.State >>= bIsReadWrite; 256 257 if (mbIsDocumentReadOnly != !bIsReadWrite) 258 { 259 mbIsDocumentReadOnly = !bIsReadWrite; 260 261 // Force the current deck to update its panel list. 262 SwitchToDeck(msCurrentDeckId); 263 } 264 } 265 266 267 268 269 void SAL_CALL SidebarController::requestLayout (void) 270 throw(cssu::RuntimeException) 271 { 272 if (mpCurrentDeck) 273 mpCurrentDeck->RequestLayout(); 274 RestrictWidth(); 275 } 276 277 278 279 280 void SidebarController::BroadcastPropertyChange (void) 281 { 282 DataChangedEvent aEvent (DATACHANGED_USER); 283 mpParentWindow->NotifyAllChilds(aEvent); 284 mpParentWindow->Invalidate(INVALIDATE_CHILDREN); 285 } 286 287 288 289 290 void SidebarController::NotifyResize (void) 291 { 292 if (mpTabBar == NULL) 293 { 294 OSL_ASSERT(mpTabBar!=NULL); 295 return; 296 } 297 298 Window* pParentWindow = mpTabBar->GetParent(); 299 300 const sal_Int32 nWidth (pParentWindow->GetSizePixel().Width()); 301 const sal_Int32 nHeight (pParentWindow->GetSizePixel().Height()); 302 303 mbIsDeckOpen = (nWidth > TabBar::GetDefaultWidth()); 304 305 if (mnSavedSidebarWidth <= 0) 306 mnSavedSidebarWidth = nWidth; 307 308 bool bIsDeckVisible; 309 if (mbCanDeckBeOpened) 310 { 311 const bool bIsOpening (nWidth > mnWidthOnSplitterButtonDown); 312 if (bIsOpening) 313 bIsDeckVisible = nWidth >= TabBar::GetDefaultWidth() + gnWidthOpenThreshold; 314 else 315 bIsDeckVisible = nWidth >= TabBar::GetDefaultWidth() + gnWidthCloseThreshold; 316 mbIsDeckRequestedOpen = bIsDeckVisible; 317 UpdateCloseIndicator(!bIsDeckVisible); 318 } 319 else 320 bIsDeckVisible = false; 321 322 // Place the deck. 323 if (mpCurrentDeck) 324 { 325 if (bIsDeckVisible) 326 { 327 mpCurrentDeck->SetPosSizePixel(0,0, nWidth-TabBar::GetDefaultWidth(), nHeight); 328 mpCurrentDeck->Show(); 329 mpCurrentDeck->RequestLayout(); 330 } 331 else 332 mpCurrentDeck->Hide(); 333 } 334 335 // Place the tab bar. 336 mpTabBar->SetPosSizePixel(nWidth-TabBar::GetDefaultWidth(),0,TabBar::GetDefaultWidth(),nHeight); 337 mpTabBar->Show(); 338 339 // Determine if the closer of the deck can be shown. 340 if (mpCurrentDeck) 341 { 342 DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar(); 343 if (pTitleBar != NULL && pTitleBar->IsVisible()) 344 pTitleBar->SetCloserVisible(CanModifyChildWindowWidth()); 345 } 346 347 RestrictWidth(); 348 } 349 350 351 352 353 void SidebarController::ProcessNewWidth (const sal_Int32 nNewWidth) 354 { 355 if ( ! mbIsDeckRequestedOpen) 356 return; 357 358 if (mbIsDeckRequestedOpen.get()) 359 { 360 // Deck became large enough to be shown. Show it. 361 mnSavedSidebarWidth = nNewWidth; 362 RequestOpenDeck(); 363 } 364 else 365 { 366 // Deck became too small. Close it completely. 367 // If window is wider than the tab bar then mark the deck as being visible, even when it its not. 368 // This is to trigger an adjustment of the width to the width of the tab bar. 369 mbIsDeckOpen = true; 370 RequestCloseDeck(); 371 372 if (mnWidthOnSplitterButtonDown > TabBar::GetDefaultWidth()) 373 mnSavedSidebarWidth = mnWidthOnSplitterButtonDown; 374 } 375 } 376 377 378 379 380 void SidebarController::UpdateConfigurations (void) 381 { 382 if (maCurrentContext != maRequestedContext) 383 { 384 maCurrentContext = maRequestedContext; 385 386 // Find the set of decks that could be displayed for the new context. 387 ResourceManager::DeckContextDescriptorContainer aDecks; 388 ResourceManager::Instance().GetMatchingDecks ( 389 aDecks, 390 maCurrentContext, 391 mbIsDocumentReadOnly, 392 mxFrame); 393 394 // Notify the tab bar about the updated set of decks. 395 mpTabBar->SetDecks(aDecks); 396 397 // Find the new deck. By default that is the same as the old 398 // one. If that is not set or not enabled, then choose the 399 // first enabled deck. 400 OUString sNewDeckId; 401 for (ResourceManager::DeckContextDescriptorContainer::const_iterator 402 iDeck(aDecks.begin()), 403 iEnd(aDecks.end()); 404 iDeck!=iEnd; 405 ++iDeck) 406 { 407 if (iDeck->mbIsEnabled) 408 { 409 if (iDeck->msId.equals(msCurrentDeckId)) 410 { 411 sNewDeckId = msCurrentDeckId; 412 break; 413 } 414 else if (sNewDeckId.getLength() == 0) 415 sNewDeckId = iDeck->msId; 416 } 417 } 418 419 if (sNewDeckId.getLength() == 0) 420 { 421 // We did not find a valid deck. 422 RequestCloseDeck(); 423 return; 424 } 425 426 // Tell the tab bar to highlight the button associated 427 // with the deck. 428 mpTabBar->HighlightDeck(sNewDeckId); 429 430 SwitchToDeck( 431 *ResourceManager::Instance().GetDeckDescriptor(sNewDeckId), 432 maCurrentContext); 433 434 #ifdef DEBUG 435 // Show the context name in the deck title bar. 436 if (mpCurrentDeck) 437 { 438 DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar(); 439 if (pTitleBar != NULL) 440 pTitleBar->SetTitle(msCurrentDeckTitle+A2S(" (")+maCurrentContext.msContext+A2S(")")); 441 } 442 #endif 443 } 444 } 445 446 447 448 449 void SidebarController::OpenThenSwitchToDeck ( 450 const ::rtl::OUString& rsDeckId) 451 { 452 RequestOpenDeck(); 453 SwitchToDeck(rsDeckId); 454 } 455 456 457 458 459 void SidebarController::SwitchToDeck ( 460 const ::rtl::OUString& rsDeckId) 461 { 462 if ( ! msCurrentDeckId.equals(rsDeckId) || ! mbIsDeckOpen) 463 { 464 const DeckDescriptor* pDeckDescriptor = ResourceManager::Instance().GetDeckDescriptor(rsDeckId); 465 if (pDeckDescriptor != NULL) 466 SwitchToDeck(*pDeckDescriptor, maCurrentContext); 467 } 468 } 469 470 471 472 473 void SidebarController::SwitchToDeck ( 474 const DeckDescriptor& rDeckDescriptor, 475 const Context& rContext) 476 { 477 maFocusManager.Clear(); 478 479 if ( ! msCurrentDeckId.equals(rDeckDescriptor.msId)) 480 { 481 // When the deck changes then destroy the deck and all panels 482 // and create everything new. 483 if (mpCurrentDeck) 484 { 485 mpCurrentDeck->Dispose(); 486 mpCurrentDeck.reset(); 487 } 488 489 msCurrentDeckId = rDeckDescriptor.msId; 490 } 491 mpTabBar->HighlightDeck(msCurrentDeckId); 492 493 // Determine the panels to display in the deck. 494 ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors; 495 ResourceManager::Instance().GetMatchingPanels( 496 aPanelContextDescriptors, 497 rContext, 498 rDeckDescriptor.msId, 499 mxFrame); 500 501 if (aPanelContextDescriptors.empty()) 502 { 503 // There are no panels to be displayed in the current context. 504 if (EnumContext::GetContextEnum(rContext.msContext) != EnumContext::Context_Empty) 505 { 506 // Switch to the "empty" context and try again. 507 SwitchToDeck( 508 rDeckDescriptor, 509 Context( 510 rContext.msApplication, 511 EnumContext::GetContextName(EnumContext::Context_Empty))); 512 return; 513 } 514 else 515 { 516 // This is already the "empty" context. Looks like we have 517 // to live with an empty deck. 518 } 519 } 520 521 if (mpCurrentDeck 522 && ArePanelSetsEqual(mpCurrentDeck->GetPanels(), aPanelContextDescriptors)) 523 { 524 // Requested set of panels is identical to the current set of 525 // panels => Nothing to do. 526 return; 527 } 528 529 // When the document is read-only, check if there are any panels that can still be displayed. 530 if (mbIsDocumentReadOnly) 531 { 532 } 533 534 535 // Provide a configuration and Deck object. 536 if ( ! mpCurrentDeck) 537 { 538 mpCurrentDeck.reset( 539 new Deck( 540 rDeckDescriptor, 541 mpParentWindow, 542 ::boost::bind(&SidebarController::RequestCloseDeck, this))); 543 msCurrentDeckTitle = rDeckDescriptor.msTitle; 544 } 545 if ( ! mpCurrentDeck) 546 return; 547 548 // Update the panel list. 549 const sal_Int32 nNewPanelCount (aPanelContextDescriptors.size()); 550 SharedPanelContainer aNewPanels; 551 const SharedPanelContainer& rCurrentPanels (mpCurrentDeck->GetPanels()); 552 aNewPanels.resize(nNewPanelCount); 553 sal_Int32 nWriteIndex (0); 554 bool bHasPanelSetChanged (false); 555 for (sal_Int32 nReadIndex=0; nReadIndex<nNewPanelCount; ++nReadIndex) 556 { 557 const ResourceManager::PanelContextDescriptor& rPanelContexDescriptor ( 558 aPanelContextDescriptors[nReadIndex]); 559 560 // Determine if the panel can be displayed. 561 const bool bIsPanelVisible (!mbIsDocumentReadOnly || rPanelContexDescriptor.mbShowForReadOnlyDocuments); 562 if ( ! bIsPanelVisible) 563 continue; 564 565 // Find the corresponding panel among the currently active 566 // panels. 567 SharedPanelContainer::const_iterator iPanel (::std::find_if( 568 rCurrentPanels.begin(), 569 rCurrentPanels.end(), 570 ::boost::bind(&Panel::HasIdPredicate, _1, ::boost::cref(rPanelContexDescriptor.msId)))); 571 if (iPanel != rCurrentPanels.end()) 572 { 573 // Panel already exists in current deck. Reuse it. 574 aNewPanels[nWriteIndex] = *iPanel; 575 aNewPanels[nWriteIndex]->SetExpanded(rPanelContexDescriptor.mbIsInitiallyVisible); 576 } 577 else 578 { 579 // Panel does not yet exist. Create it. 580 aNewPanels[nWriteIndex] = CreatePanel( 581 rPanelContexDescriptor.msId, 582 mpCurrentDeck->GetPanelParentWindow(), 583 rPanelContexDescriptor.mbIsInitiallyVisible, 584 rContext); 585 bHasPanelSetChanged = true; 586 } 587 if (aNewPanels[nWriteIndex] != NULL) 588 { 589 // Depending on the context we have to apply the show menu functor. 590 aNewPanels[nWriteIndex]->SetShowMenuFunctor( 591 rPanelContexDescriptor.msMenuCommand.getLength()>0 592 ? ::boost::bind(&SidebarController::ShowDetailMenu,this,rPanelContexDescriptor.msMenuCommand) 593 : ::boost::function<void(void)>() ); 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>& rDeckSelectionData, 808 const ::std::vector<TabBar::DeckMenuData>& rDeckShowData) const 809 { 810 ::boost::shared_ptr<PopupMenu> pMenu = CreatePopupMenu(rDeckSelectionData, rDeckShowData); 811 pMenu->SetSelectHdl(LINK(this, SidebarController, OnMenuItemSelected)); 812 813 // pass toolbox button rect so the menu can stay open on button up 814 Rectangle aBox (rButtonBox); 815 aBox.Move(mpTabBar->GetPosPixel().X(), 0); 816 pMenu->Execute(mpParentWindow, aBox, POPUPMENU_EXECUTE_DOWN); 817 } 818 819 820 821 822 void SidebarController::ShowDetailMenu (const ::rtl::OUString& rsMenuCommand) const 823 { 824 try 825 { 826 const util::URL aURL (Tools::GetURL(rsMenuCommand)); 827 Reference<frame::XDispatch> xDispatch (Tools::GetDispatch(mxFrame, aURL)); 828 if (xDispatch.is()) 829 xDispatch->dispatch(aURL, Sequence<beans::PropertyValue>()); 830 } 831 catch(Exception& rException) 832 { 833 OSL_TRACE("caught exception: %s", 834 OUStringToOString(rException.Message, RTL_TEXTENCODING_ASCII_US).getStr()); 835 } 836 } 837 838 839 840 841 ::boost::shared_ptr<PopupMenu> SidebarController::CreatePopupMenu ( 842 const ::std::vector<TabBar::DeckMenuData>& rDeckSelectionData, 843 const ::std::vector<TabBar::DeckMenuData>& rDeckShowData) const 844 { 845 ::boost::shared_ptr<PopupMenu> pMenu (new PopupMenu()); 846 FloatingWindow* pMenuWindow = dynamic_cast<FloatingWindow*>(pMenu->GetWindow()); 847 if (pMenuWindow != NULL) 848 { 849 pMenuWindow->SetPopupModeFlags(pMenuWindow->GetPopupModeFlags() | FLOATWIN_POPUPMODE_NOMOUSEUPCLOSE); 850 } 851 852 SidebarResource aLocalResource; 853 854 // Add one entry for every tool panel element to individually make 855 // them visible or hide them. 856 { 857 sal_Int32 nIndex (MID_FIRST_PANEL); 858 for(::std::vector<TabBar::DeckMenuData>::const_iterator 859 iItem(rDeckSelectionData.begin()), 860 iEnd(rDeckSelectionData.end()); 861 iItem!=iEnd; 862 ++iItem) 863 { 864 pMenu->InsertItem(nIndex, iItem->get<0>(), MIB_RADIOCHECK); 865 pMenu->CheckItem(nIndex, iItem->get<2>()); 866 ++nIndex; 867 } 868 } 869 870 pMenu->InsertSeparator(); 871 872 // Add entry for docking or un-docking the tool panel. 873 if (mpParentWindow->IsFloatingMode()) 874 pMenu->InsertItem(MID_LOCK_TASK_PANEL, String(SfxResId(STR_SFX_DOCK))); 875 else 876 pMenu->InsertItem(MID_UNLOCK_TASK_PANEL, String(SfxResId(STR_SFX_UNDOCK))); 877 878 // Add sub menu for customization (hiding of deck tabs.) 879 PopupMenu* pCustomizationMenu = new PopupMenu(); 880 { 881 sal_Int32 nIndex (MID_FIRST_HIDE); 882 for(::std::vector<TabBar::DeckMenuData>::const_iterator 883 iItem(rDeckShowData.begin()), 884 iEnd(rDeckShowData.end()); 885 iItem!=iEnd; 886 ++iItem) 887 { 888 pCustomizationMenu->InsertItem(nIndex, iItem->get<0>(), MIB_CHECKABLE); 889 pCustomizationMenu->CheckItem(nIndex, iItem->get<2>()); 890 ++nIndex; 891 } 892 } 893 894 pCustomizationMenu->InsertSeparator(); 895 pCustomizationMenu->InsertItem(MID_RESTORE_DEFAULT, String(SfxResId(STRING_RESTORE))); 896 897 pMenu->InsertItem(MID_CUSTOMIZATION, String(SfxResId(STRING_CUSTOMIZATION))); 898 pMenu->SetPopupMenu(MID_CUSTOMIZATION, pCustomizationMenu); 899 900 pMenu->RemoveDisabledEntries(sal_False, sal_False); 901 902 return pMenu; 903 } 904 905 906 907 908 IMPL_LINK(SidebarController, OnMenuItemSelected, Menu*, pMenu) 909 { 910 if (pMenu == NULL) 911 { 912 OSL_ENSURE(pMenu!=NULL, "sfx2::sidebar::SidebarController::OnMenuItemSelected: illegal menu!"); 913 return 0; 914 } 915 916 pMenu->Deactivate(); 917 const sal_Int32 nIndex (pMenu->GetCurItemId()); 918 switch (nIndex) 919 { 920 case MID_UNLOCK_TASK_PANEL: 921 mpParentWindow->SetFloatingMode(sal_True); 922 break; 923 924 case MID_LOCK_TASK_PANEL: 925 mpParentWindow->SetFloatingMode(sal_False); 926 break; 927 928 case MID_RESTORE_DEFAULT: 929 mpTabBar->RestoreHideFlags(); 930 break; 931 932 default: 933 { 934 try 935 { 936 if (nIndex >= MID_FIRST_PANEL && nIndex<MID_FIRST_HIDE) 937 SwitchToDeck(mpTabBar->GetDeckIdForIndex(nIndex - MID_FIRST_PANEL)); 938 else if (nIndex >=MID_FIRST_HIDE) 939 mpTabBar->ToggleHideFlag(nIndex-MID_FIRST_HIDE); 940 } 941 catch (RuntimeException&) 942 { 943 } 944 } 945 break; 946 } 947 948 return 1; 949 } 950 951 952 953 954 void SidebarController::RequestCloseDeck (void) 955 { 956 mbIsDeckRequestedOpen = false; 957 UpdateDeckOpenState(); 958 } 959 960 961 962 963 void SidebarController::RequestOpenDeck (void) 964 { 965 mbIsDeckRequestedOpen = true; 966 UpdateDeckOpenState(); 967 } 968 969 970 971 972 void SidebarController::UpdateDeckOpenState (void) 973 { 974 if ( ! mbIsDeckRequestedOpen) 975 // No state requested. 976 return; 977 978 // Update (change) the open state when it either has not yet been initialized 979 // or when its value differs from the requested state. 980 if ( ! mbIsDeckOpen 981 || mbIsDeckOpen.get() != mbIsDeckRequestedOpen.get()) 982 { 983 if (mbIsDeckRequestedOpen.get()) 984 { 985 if (mnSavedSidebarWidth <= TabBar::GetDefaultWidth()) 986 SetChildWindowWidth(SidebarChildWindow::GetDefaultWidth(mpParentWindow)); 987 else 988 SetChildWindowWidth(mnSavedSidebarWidth); 989 } 990 else 991 { 992 if ( ! mpParentWindow->IsFloatingMode()) 993 mnSavedSidebarWidth = SetChildWindowWidth(TabBar::GetDefaultWidth()); 994 if (mnWidthOnSplitterButtonDown > TabBar::GetDefaultWidth()) 995 mnSavedSidebarWidth = mnWidthOnSplitterButtonDown; 996 mpParentWindow->SetStyle(mpParentWindow->GetStyle() & ~WB_SIZEABLE); 997 } 998 999 mbIsDeckOpen = mbIsDeckRequestedOpen.get(); 1000 if (mbIsDeckOpen.get() && mpCurrentDeck) 1001 mpCurrentDeck->Show(mbIsDeckOpen.get()); 1002 NotifyResize(); 1003 } 1004 } 1005 1006 1007 1008 1009 FocusManager& SidebarController::GetFocusManager (void) 1010 { 1011 return maFocusManager; 1012 } 1013 1014 1015 1016 1017 bool SidebarController::CanModifyChildWindowWidth (void) 1018 { 1019 SfxSplitWindow* pSplitWindow = GetSplitWindow(); 1020 if (pSplitWindow == NULL) 1021 return false; 1022 1023 sal_uInt16 nRow (0xffff); 1024 sal_uInt16 nColumn (0xffff); 1025 pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow); 1026 1027 sal_uInt16 nRowCount (pSplitWindow->GetWindowCount(nColumn)); 1028 1029 return nRowCount==1; 1030 } 1031 1032 1033 1034 1035 sal_Int32 SidebarController::SetChildWindowWidth (const sal_Int32 nNewWidth) 1036 { 1037 SfxSplitWindow* pSplitWindow = GetSplitWindow(); 1038 if (pSplitWindow == NULL) 1039 return 0; 1040 1041 sal_uInt16 nRow (0xffff); 1042 sal_uInt16 nColumn (0xffff); 1043 pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow); 1044 const long nColumnWidth (pSplitWindow->GetLineSize(nColumn)); 1045 1046 Window* pWindow = mpParentWindow; 1047 const Point aWindowPosition (pWindow->GetPosPixel()); 1048 const Size aWindowSize (pWindow->GetSizePixel()); 1049 1050 pSplitWindow->MoveWindow( 1051 mpParentWindow, 1052 Size(nNewWidth, aWindowSize.Height()), 1053 nColumn, 1054 nRow); 1055 static_cast<SplitWindow*>(pSplitWindow)->Split(); 1056 1057 return static_cast<sal_Int32>(nColumnWidth); 1058 } 1059 1060 1061 1062 1063 void SidebarController::RestrictWidth (void) 1064 { 1065 SfxSplitWindow* pSplitWindow = GetSplitWindow(); 1066 if (pSplitWindow != NULL) 1067 { 1068 const sal_uInt16 nId (pSplitWindow->GetItemId(mpParentWindow)); 1069 const sal_uInt16 nSetId (pSplitWindow->GetSet(nId)); 1070 pSplitWindow->SetItemSizeRange( 1071 nSetId, 1072 Range(TabBar::GetDefaultWidth(), gnMaximumSidebarWidth)); 1073 } 1074 } 1075 1076 1077 1078 1079 SfxSplitWindow* SidebarController::GetSplitWindow (void) 1080 { 1081 if (mpSplitWindow == NULL) 1082 { 1083 if (mpParentWindow != NULL) 1084 { 1085 mpSplitWindow = dynamic_cast<SfxSplitWindow*>(mpParentWindow->GetParent()); 1086 if (mpSplitWindow != NULL) 1087 mpSplitWindow->AddEventListener(LINK(this, SidebarController, WindowEventHandler)); 1088 } 1089 } 1090 1091 return mpSplitWindow; 1092 } 1093 1094 1095 1096 1097 void SidebarController::UpdateCloseIndicator (const bool bCloseAfterDrag) 1098 { 1099 if (mpParentWindow == NULL) 1100 return; 1101 1102 if (bCloseAfterDrag) 1103 { 1104 // Make sure that the indicator exists. 1105 if ( ! mpCloseIndicator) 1106 { 1107 mpCloseIndicator.reset(new FixedImage(mpParentWindow)); 1108 FixedImage* pFixedImage = static_cast<FixedImage*>(mpCloseIndicator.get()); 1109 const Image aImage (Theme::GetImage(Theme::Image_CloseIndicator)); 1110 pFixedImage->SetImage(aImage); 1111 pFixedImage->SetSizePixel(aImage.GetSizePixel()); 1112 pFixedImage->SetBackground(Theme::GetWallpaper(Theme::Paint_DeckBackground)); 1113 } 1114 1115 // Place and show the indicator. 1116 const Size aWindowSize (mpParentWindow->GetSizePixel()); 1117 const Size aImageSize (mpCloseIndicator->GetSizePixel()); 1118 mpCloseIndicator->SetPosPixel( 1119 Point( 1120 aWindowSize.Width() - TabBar::GetDefaultWidth() - aImageSize.Width(), 1121 (aWindowSize.Height() - aImageSize.Height())/2)); 1122 mpCloseIndicator->Show(); 1123 } 1124 else 1125 { 1126 // Hide but don't delete the indicator. 1127 if (mpCloseIndicator) 1128 mpCloseIndicator->Hide(); 1129 } 1130 } 1131 1132 1133 1134 1135 void SidebarController::UpdateTitleBarIcons (void) 1136 { 1137 if ( ! mpCurrentDeck) 1138 return; 1139 1140 const bool bIsHighContrastModeActive (Theme::IsHighContrastMode()); 1141 const ResourceManager& rResourceManager (ResourceManager::Instance()); 1142 1143 // Update the deck icon. 1144 const DeckDescriptor* pDeckDescriptor = rResourceManager.GetDeckDescriptor(mpCurrentDeck->GetId()); 1145 if (pDeckDescriptor != NULL && mpCurrentDeck->GetTitleBar()) 1146 { 1147 const OUString sIconURL( 1148 bIsHighContrastModeActive 1149 ? pDeckDescriptor->msHighContrastTitleBarIconURL 1150 : pDeckDescriptor->msTitleBarIconURL); 1151 mpCurrentDeck->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame)); 1152 } 1153 1154 // Update the panel icons. 1155 const SharedPanelContainer& rPanels (mpCurrentDeck->GetPanels()); 1156 for (SharedPanelContainer::const_iterator 1157 iPanel(rPanels.begin()), iEnd(rPanels.end()); 1158 iPanel!=iEnd; 1159 ++iPanel) 1160 { 1161 if ( ! *iPanel) 1162 continue; 1163 if ((*iPanel)->GetTitleBar() == NULL) 1164 continue; 1165 const PanelDescriptor* pPanelDescriptor = rResourceManager.GetPanelDescriptor((*iPanel)->GetId()); 1166 if (pPanelDescriptor == NULL) 1167 continue; 1168 const OUString sIconURL ( 1169 bIsHighContrastModeActive 1170 ? pPanelDescriptor->msHighContrastTitleBarIconURL 1171 : pPanelDescriptor->msTitleBarIconURL); 1172 (*iPanel)->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame)); 1173 } 1174 } 1175 1176 1177 1178 1179 void SidebarController::ShowPanel (const Panel& rPanel) 1180 { 1181 if (mpCurrentDeck) 1182 mpCurrentDeck->ShowPanel(rPanel); 1183 } 1184 1185 1186 1187 1188 Context SidebarController::GetCurrentContext (void) const 1189 { 1190 return maCurrentContext; 1191 } 1192 1193 1194 } } // end of namespace sfx2::sidebar 1195