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