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