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