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