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 "SidebarDockingWindow.hxx" 33 #include "Context.hxx" 34 #include "Tools.hxx" 35 36 #include "sfxresid.hxx" 37 #include "sfx2/sfxsids.hrc" 38 #include "sfx2/titledockwin.hxx" 39 #include "sfxlocal.hrc" 40 #include <vcl/floatwin.hxx> 41 #include "splitwin.hxx" 42 #include <svl/smplhint.hxx> 43 #include <tools/link.hxx> 44 #include <comphelper/componentfactory.hxx> 45 #include <comphelper/processfactory.hxx> 46 #include <comphelper/componentcontext.hxx> 47 #include <comphelper/namedvaluecollection.hxx> 48 49 #include <com/sun/star/frame/XDispatchProvider.hpp> 50 #include <com/sun/star/lang/XInitialization.hpp> 51 #include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp> 52 #include <com/sun/star/ui/ContextChangeEventObject.hpp> 53 #include <com/sun/star/ui/XUIElementFactory.hpp> 54 #include <com/sun/star/util/XURLTransformer.hpp> 55 #include <com/sun/star/util/URL.hpp> 56 57 #include <boost/bind.hpp> 58 #include <boost/function.hpp> 59 #include <boost/scoped_array.hpp> 60 61 62 using namespace css; 63 using namespace cssu; 64 using ::rtl::OUString; 65 66 67 68 namespace sfx2 { namespace sidebar { 69 70 namespace { 71 enum MenuId 72 { 73 MID_UNLOCK_TASK_PANEL = 1, 74 MID_LOCK_TASK_PANEL, 75 MID_CUSTOMIZATION, 76 MID_RESTORE_DEFAULT, 77 MID_FIRST_PANEL, 78 MID_FIRST_HIDE = 1000 79 }; 80 } 81 82 83 SidebarController::SidebarController ( 84 SidebarDockingWindow* pParentWindow, 85 const cssu::Reference<css::frame::XFrame>& rxFrame) 86 : SidebarControllerInterfaceBase(m_aMutex), 87 mpCurrentDeck(), 88 mpParentWindow(pParentWindow), 89 mpTabBar(new TabBar( 90 mpParentWindow, 91 rxFrame, 92 ::boost::bind(&SidebarController::SwitchToDeck, this, _1), 93 ::boost::bind(&SidebarController::ShowPopupMenu, this, _1,_2,_3))), 94 mxFrame(rxFrame), 95 maCurrentContext(OUString(), OUString()), 96 msCurrentDeckId(A2S("PropertyDeck")), 97 maPropertyChangeForwarder(::boost::bind(&SidebarController::BroadcastPropertyChange, this)), 98 mbIsDeckClosed(false), 99 mnSavedSidebarWidth(pParentWindow->GetSizePixel().Width()) 100 { 101 if (pParentWindow == NULL) 102 { 103 OSL_ASSERT(pParentWindow!=NULL); 104 return; 105 } 106 107 // Listen for context change events. 108 cssu::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer ( 109 css::ui::ContextChangeEventMultiplexer::get( 110 ::comphelper::getProcessComponentContext())); 111 if (xMultiplexer.is()) 112 xMultiplexer->addContextChangeEventListener( 113 static_cast<css::ui::XContextChangeEventListener*>(this), 114 mxFrame->getController()); 115 116 // Listen for window events. 117 mpParentWindow->AddEventListener(LINK(this, SidebarController, WindowEventHandler)); 118 119 // Listen for theme property changes. 120 Theme::GetPropertySet()->addPropertyChangeListener( 121 A2S(""), 122 static_cast<css::beans::XPropertyChangeListener*>(this)); 123 124 SwitchToDeck(A2S("default")); 125 } 126 127 128 129 130 SidebarController::~SidebarController (void) 131 { 132 } 133 134 135 136 137 void SAL_CALL SidebarController::disposing (void) 138 { 139 cssu::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer ( 140 css::ui::ContextChangeEventMultiplexer::get( 141 ::comphelper::getProcessComponentContext())); 142 if (xMultiplexer.is()) 143 xMultiplexer->removeAllContextChangeEventListeners( 144 static_cast<css::ui::XContextChangeEventListener*>(this)); 145 146 if (mpParentWindow != NULL) 147 { 148 mpParentWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler)); 149 mpParentWindow = NULL; 150 } 151 152 if (mpCurrentDeck) 153 { 154 mpCurrentDeck->Dispose(); 155 OSL_TRACE("deleting deck window subtree"); 156 mpCurrentDeck->PrintWindowTree(); 157 mpCurrentDeck.reset(); 158 } 159 160 mpTabBar.reset(); 161 162 Theme::GetPropertySet()->removePropertyChangeListener( 163 A2S(""), 164 static_cast<css::beans::XPropertyChangeListener*>(this)); 165 } 166 167 168 169 170 void SAL_CALL SidebarController::notifyContextChangeEvent (const css::ui::ContextChangeEventObject& rEvent) 171 throw(cssu::RuntimeException) 172 { 173 UpdateConfigurations( 174 Context( 175 rEvent.ApplicationName, 176 rEvent.ContextName)); 177 } 178 179 180 181 182 void SAL_CALL SidebarController::disposing (const css::lang::EventObject& rEventObject) 183 throw(cssu::RuntimeException) 184 { 185 (void)rEventObject; 186 187 dispose(); 188 } 189 190 191 192 193 void SAL_CALL SidebarController::propertyChange (const css::beans::PropertyChangeEvent& rEvent) 194 throw(cssu::RuntimeException) 195 { 196 (void)rEvent; 197 198 maPropertyChangeForwarder.RequestCall(); 199 } 200 201 202 203 204 void SAL_CALL SidebarController::requestLayout (void) 205 throw(cssu::RuntimeException) 206 { 207 if (mpCurrentDeck) 208 mpCurrentDeck->RequestLayout(); 209 RestrictWidth(); 210 } 211 212 213 214 215 void SidebarController::BroadcastPropertyChange (void) 216 { 217 DataChangedEvent aEvent (DATACHANGED_USER); 218 mpParentWindow->NotifyAllChilds(aEvent); 219 mpParentWindow->Invalidate(INVALIDATE_CHILDREN); 220 } 221 222 223 224 225 void SidebarController::NotifyResize (void) 226 { 227 if (mpTabBar == NULL) 228 { 229 OSL_ASSERT(mpTabBar!=NULL); 230 return; 231 } 232 233 Window* pParentWindow = mpTabBar->GetParent(); 234 235 const sal_Int32 nWidth (pParentWindow->GetSizePixel().Width()); 236 const sal_Int32 nHeight (pParentWindow->GetSizePixel().Height()); 237 238 // Place the deck. 239 if (mpCurrentDeck) 240 { 241 mpCurrentDeck->SetPosSizePixel(0,0, nWidth-TabBar::GetDefaultWidth(), nHeight); 242 mpCurrentDeck->Show(); 243 mpCurrentDeck->RequestLayout(); 244 } 245 246 // Place the tab bar. 247 mpTabBar->SetPosSizePixel(nWidth-TabBar::GetDefaultWidth(),0,TabBar::GetDefaultWidth(),nHeight); 248 mpTabBar->Show(); 249 250 // Determine if the closer of the deck can be shown. 251 if (mpCurrentDeck) 252 { 253 DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar(); 254 if (pTitleBar != NULL && pTitleBar->IsVisible()) 255 pTitleBar->SetCloserVisible(CanModifyChildWindowWidth()); 256 } 257 258 if (nWidth > TabBar::GetDefaultWidth()) 259 mnSavedSidebarWidth = nWidth; 260 261 RestrictWidth(); 262 #ifdef DEBUG 263 if (mpCurrentDeck) 264 { 265 mpCurrentDeck->PrintWindowTree(); 266 sal_Int32 nPanelIndex (0); 267 for (SharedPanelContainer::const_iterator 268 iPanel(mpCurrentDeck->GetPanels().begin()), 269 iEnd(mpCurrentDeck->GetPanels().end()); 270 iPanel!=iEnd; 271 ++iPanel,++nPanelIndex) 272 { 273 OSL_TRACE("panel %d:", nPanelIndex); 274 (*iPanel)->PrintWindowTree(); 275 } 276 } 277 #endif 278 } 279 280 281 282 283 void SidebarController::UpdateConfigurations (const Context& rContext) 284 { 285 if (maCurrentContext != rContext) 286 { 287 maCurrentContext = rContext; 288 289 // Notify the tab bar about the updated set of decks. 290 ResourceManager::IdContainer aDeckIds; 291 ResourceManager::Instance().GetMatchingDecks ( 292 aDeckIds, 293 rContext, 294 mxFrame); 295 mpTabBar->SetDecks(aDeckIds); 296 297 // Check if the current deck is among the matching decks. 298 bool bCurrentDeckMatches (false); 299 for (ResourceManager::IdContainer::const_iterator 300 iDeck(aDeckIds.begin()), 301 iEnd(aDeckIds.end()); 302 iDeck!=iEnd; 303 ++iDeck) 304 { 305 if (iDeck->equals(msCurrentDeckId)) 306 { 307 bCurrentDeckMatches = true; 308 break; 309 } 310 } 311 312 DeckDescriptor const* pDeckDescriptor = NULL; 313 if ( ! bCurrentDeckMatches) 314 pDeckDescriptor = ResourceManager::Instance().GetBestMatchingDeck(rContext, mxFrame); 315 else 316 pDeckDescriptor = ResourceManager::Instance().GetDeckDescriptor(msCurrentDeckId); 317 if (pDeckDescriptor != NULL) 318 { 319 msCurrentDeckId = pDeckDescriptor->msId; 320 SwitchToDeck(*pDeckDescriptor, rContext); 321 } 322 323 #ifdef DEBUG 324 // Show the context name in the deck title bar. 325 if (mpCurrentDeck) 326 { 327 DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar(); 328 if (pTitleBar != NULL) 329 pTitleBar->SetTitle(msCurrentDeckTitle+A2S(" (")+rContext.msContext+A2S(")")); 330 } 331 #endif 332 } 333 } 334 335 336 337 338 void SidebarController::SwitchToDeck ( 339 const ::rtl::OUString& rsDeckId) 340 { 341 if ( ! msCurrentDeckId.equals(rsDeckId) || mbIsDeckClosed) 342 { 343 const DeckDescriptor* pDeckDescriptor = ResourceManager::Instance().GetDeckDescriptor(rsDeckId); 344 if (pDeckDescriptor != NULL) 345 SwitchToDeck(*pDeckDescriptor, maCurrentContext); 346 } 347 } 348 349 350 351 352 void SidebarController::SwitchToDeck ( 353 const DeckDescriptor& rDeckDescriptor, 354 const Context& rContext) 355 { 356 if ( ! msCurrentDeckId.equals(rDeckDescriptor.msId)) 357 { 358 // When the deck changes then destroy the deck and all panels 359 // and create everything new. 360 if (mpCurrentDeck) 361 { 362 mpCurrentDeck->Dispose(); 363 mpCurrentDeck.reset(); 364 } 365 366 msCurrentDeckId = rDeckDescriptor.msId; 367 } 368 369 // Reopen the deck when necessary. 370 OpenDeck(); 371 372 // Determine the panels to display in the deck. 373 ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors; 374 ResourceManager::Instance().GetMatchingPanels( 375 aPanelContextDescriptors, 376 rContext, 377 rDeckDescriptor.msId, 378 mxFrame); 379 380 if (aPanelContextDescriptors.empty()) 381 { 382 // There are no panels to be displayed in the current context. 383 if (EnumContext::GetContextEnum(rContext.msContext) != EnumContext::Context_Empty) 384 { 385 // Switch to the "empty" context and try again. 386 SwitchToDeck( 387 rDeckDescriptor, 388 Context( 389 rContext.msApplication, 390 EnumContext::GetContextName(EnumContext::Context_Empty))); 391 return; 392 } 393 else 394 { 395 // This is already the "empty" context. Looks like we have 396 // to live with an empty deck. 397 } 398 } 399 400 if (mpCurrentDeck 401 && ArePanelSetsEqual(mpCurrentDeck->GetPanels(), aPanelContextDescriptors)) 402 { 403 // Requested set of panels is identical to the current set of 404 // panels => Nothing to do. 405 return; 406 } 407 408 // Provide a configuration and Deck object. 409 if ( ! mpCurrentDeck) 410 { 411 mpCurrentDeck.reset( 412 new Deck( 413 rDeckDescriptor, 414 mpParentWindow, 415 ::boost::bind(&SidebarController::CloseDeck, this))); 416 msCurrentDeckTitle = rDeckDescriptor.msTitle; 417 } 418 if ( ! mpCurrentDeck) 419 return; 420 421 // Update the panel list. 422 const sal_Int32 nNewPanelCount (aPanelContextDescriptors.size()); 423 SharedPanelContainer aNewPanels; 424 const SharedPanelContainer& rCurrentPanels (mpCurrentDeck->GetPanels()); 425 aNewPanels.resize(nNewPanelCount); 426 sal_Int32 nWriteIndex (0); 427 bool bHasPanelSetChanged (false); 428 for (sal_Int32 nReadIndex=0; nReadIndex<nNewPanelCount; ++nReadIndex) 429 { 430 const ResourceManager::PanelContextDescriptor& rPanelContexDescriptor ( 431 aPanelContextDescriptors[nReadIndex]); 432 433 // Find the corresponding panel among the currently active 434 // panels. 435 SharedPanelContainer::const_iterator iPanel (::std::find_if( 436 rCurrentPanels.begin(), 437 rCurrentPanels.end(), 438 ::boost::bind(&Panel::HasIdPredicate, _1, ::boost::cref(rPanelContexDescriptor.msId)))); 439 if (iPanel != rCurrentPanels.end()) 440 { 441 // Panel already exists in current deck. Reuse it. 442 aNewPanels[nWriteIndex] = *iPanel; 443 OSL_TRACE(" reusing panel %s", S2A(rPanelContexDescriptor.msId)); 444 } 445 else 446 { 447 // Panel does not yet exist. Create it. 448 aNewPanels[nWriteIndex] = CreatePanel( 449 rPanelContexDescriptor.msId, 450 mpCurrentDeck->GetPanelParentWindow(), 451 rPanelContexDescriptor.msMenuCommand); 452 OSL_TRACE(" creating panel %s", S2A(rPanelContexDescriptor.msId)); 453 bHasPanelSetChanged = true; 454 } 455 if (aNewPanels[nWriteIndex] != NULL) 456 { 457 // Depending on the context we have to collapse the panel. 458 aNewPanels[nWriteIndex]->SetExpanded(rPanelContexDescriptor.mbIsInitiallyVisible); 459 460 ++nWriteIndex; 461 } 462 463 } 464 aNewPanels.resize(nWriteIndex); 465 466 // Activate the deck and the new set of panels. 467 mpCurrentDeck->SetPosSizePixel( 468 0, 469 0, 470 mpParentWindow->GetSizePixel().Width()-TabBar::GetDefaultWidth(), 471 mpParentWindow->GetSizePixel().Height()); 472 mpCurrentDeck->SetPanels(aNewPanels); 473 mpCurrentDeck->Show(); 474 475 // Tell the tab bar to highlight the button associated with the 476 // deck. 477 mpTabBar->HighlightDeck(rDeckDescriptor.msId); 478 479 mpParentWindow->SetText(rDeckDescriptor.msTitle); 480 481 if (bHasPanelSetChanged) 482 NotifyResize(); 483 } 484 485 486 487 488 bool SidebarController::ArePanelSetsEqual ( 489 const SharedPanelContainer& rCurrentPanels, 490 const ResourceManager::PanelContextDescriptorContainer& rRequestedPanels) 491 { 492 OSL_TRACE("current panel list:"); 493 for (SharedPanelContainer::const_iterator 494 iPanel(rCurrentPanels.begin()), 495 iEnd(rCurrentPanels.end()); 496 iPanel!=iEnd; 497 ++iPanel) 498 { 499 OSL_TRACE(" panel %s", S2A((*iPanel)->GetId())); 500 } 501 502 OSL_TRACE("requested panels: "); 503 for (ResourceManager::PanelContextDescriptorContainer::const_iterator 504 iId(rRequestedPanels.begin()), 505 iEnd(rRequestedPanels.end()); 506 iId!=iEnd; 507 ++iId) 508 { 509 OSL_TRACE(" panel %s", S2A(iId->msId)); 510 } 511 512 if (rCurrentPanels.size() != rRequestedPanels.size()) 513 return false; 514 for (sal_Int32 nIndex=0,nCount=rCurrentPanels.size(); nIndex<nCount; ++nIndex) 515 { 516 if (rCurrentPanels[nIndex] == NULL) 517 return false; 518 if ( ! rCurrentPanels[nIndex]->GetId().equals(rRequestedPanels[nIndex].msId)) 519 return false; 520 } 521 return true; 522 } 523 524 525 526 527 SharedPanel SidebarController::CreatePanel ( 528 const OUString& rsPanelId, 529 ::Window* pParentWindow, 530 const OUString& rsMenuCommand) 531 { 532 const PanelDescriptor* pPanelDescriptor = ResourceManager::Instance().GetPanelDescriptor(rsPanelId); 533 if (pPanelDescriptor == NULL) 534 return SharedPanel(); 535 536 #ifdef DEBUG 537 // Prevent the panel not being created in the same memory of an old panel. 538 ::boost::scoped_array<char> pUnused (new char[sizeof(Panel)]); 539 OSL_TRACE("allocated memory at %x", pUnused.get()); 540 #endif 541 542 // Create the panel which is the parent window of the UIElement. 543 SharedPanel pPanel (new Panel( 544 *pPanelDescriptor, 545 pParentWindow, 546 ::boost::bind(&Deck::RequestLayout, mpCurrentDeck.get()), 547 rsMenuCommand.getLength()>0 548 ? ::boost::bind(&SidebarController::ShowDetailMenu,this,rsMenuCommand) 549 : ::boost::function<void(void)>())); 550 551 // Create the XUIElement. 552 Reference<ui::XUIElement> xUIElement (CreateUIElement( 553 pPanel->GetComponentInterface(), 554 pPanelDescriptor->msImplementationURL)); 555 if (xUIElement.is()) 556 { 557 // Initialize the panel and add it to the active deck. 558 pPanel->SetUIElement(xUIElement); 559 } 560 else 561 { 562 pPanel.reset(); 563 } 564 565 return pPanel; 566 } 567 568 569 570 571 Reference<ui::XUIElement> SidebarController::CreateUIElement ( 572 const Reference<awt::XWindowPeer>& rxWindow, 573 const ::rtl::OUString& rsImplementationURL) 574 { 575 try 576 { 577 const ::comphelper::ComponentContext aComponentContext (::comphelper::getProcessServiceFactory()); 578 const Reference<ui::XUIElementFactory> xUIElementFactory ( 579 aComponentContext.createComponent("com.sun.star.ui.UIElementFactoryManager"), 580 UNO_QUERY_THROW); 581 582 // Create the XUIElement. 583 ::comphelper::NamedValueCollection aCreationArguments; 584 aCreationArguments.put("Frame", makeAny(mxFrame)); 585 aCreationArguments.put("ParentWindow", makeAny(rxWindow)); 586 SfxDockingWindow* pSfxDockingWindow = dynamic_cast<SfxDockingWindow*>(mpParentWindow); 587 if (pSfxDockingWindow != NULL) 588 aCreationArguments.put("SfxBindings", makeAny(sal_uInt64(&pSfxDockingWindow->GetBindings()))); 589 aCreationArguments.put("Theme", Theme::GetPropertySet()); 590 aCreationArguments.put("Sidebar", makeAny(Reference<ui::XSidebar>(static_cast<ui::XSidebar*>(this)))); 591 592 Reference<ui::XUIElement> xUIElement( 593 xUIElementFactory->createUIElement( 594 rsImplementationURL, 595 Sequence<beans::PropertyValue>(aCreationArguments.getPropertyValues())), 596 UNO_QUERY_THROW); 597 598 return xUIElement; 599 } 600 catch(Exception& rException) 601 { 602 OSL_TRACE("caught exception: %s", 603 OUStringToOString(rException.Message, RTL_TEXTENCODING_ASCII_US).getStr()); 604 // For some reason we can not create the actual panel. 605 // Probably because its factory was not properly registered. 606 // TODO: provide feedback to developer to better pinpoint the 607 // source of the error. 608 609 return NULL; 610 } 611 } 612 613 614 615 616 IMPL_LINK(SidebarController, WindowEventHandler, VclWindowEvent*, pEvent) 617 { 618 if (pEvent != NULL) 619 { 620 switch (pEvent->GetId()) 621 { 622 case VCLEVENT_WINDOW_GETFOCUS: 623 case VCLEVENT_WINDOW_LOSEFOCUS: 624 break; 625 626 case VCLEVENT_WINDOW_SHOW: 627 case VCLEVENT_WINDOW_RESIZE: 628 NotifyResize(); 629 break; 630 631 case VCLEVENT_WINDOW_DATACHANGED: 632 // Force an update of deck and tab bar to reflect 633 // changes in theme (high contrast mode). 634 Theme::HandleDataChange(); 635 mpParentWindow->Invalidate(); 636 break; 637 638 case SFX_HINT_DYING: 639 dispose(); 640 break; 641 642 default: 643 break; 644 } 645 } 646 647 return sal_True; 648 } 649 650 651 652 653 void SidebarController::ShowPopupMenu ( 654 const Rectangle& rButtonBox, 655 const ::std::vector<TabBar::DeckMenuData>& rDeckSelectionData, 656 const ::std::vector<TabBar::DeckMenuData>& rDeckShowData) const 657 { 658 ::boost::shared_ptr<PopupMenu> pMenu = CreatePopupMenu(rDeckSelectionData, rDeckShowData); 659 pMenu->SetSelectHdl(LINK(this, SidebarController, OnMenuItemSelected)); 660 661 // pass toolbox button rect so the menu can stay open on button up 662 Rectangle aBox (rButtonBox); 663 aBox.Move(mpTabBar->GetPosPixel().X(), 0); 664 pMenu->Execute(mpParentWindow, aBox, POPUPMENU_EXECUTE_DOWN); 665 } 666 667 668 669 670 void SidebarController::ShowDetailMenu (const ::rtl::OUString& rsMenuCommand) const 671 { 672 try 673 { 674 util::URL aURL; 675 aURL.Complete = rsMenuCommand; 676 677 const ::comphelper::ComponentContext aComponentContext (::comphelper::getProcessServiceFactory()); 678 const Reference<util::XURLTransformer> xParser ( 679 aComponentContext.createComponent("com.sun.star.util.URLTransformer"), 680 UNO_QUERY_THROW); 681 xParser->parseStrict(aURL); 682 Reference<frame::XDispatchProvider> xProvider (mxFrame, UNO_QUERY_THROW); 683 Reference<frame::XDispatch> xDispatch (xProvider->queryDispatch(aURL, OUString(), 0)); 684 if (xDispatch.is()) 685 xDispatch->dispatch(aURL, Sequence<beans::PropertyValue>()); 686 } 687 catch(Exception& rException) 688 { 689 OSL_TRACE("caught exception: %s", 690 OUStringToOString(rException.Message, RTL_TEXTENCODING_ASCII_US).getStr()); 691 } 692 } 693 694 695 696 697 ::boost::shared_ptr<PopupMenu> SidebarController::CreatePopupMenu ( 698 const ::std::vector<TabBar::DeckMenuData>& rDeckSelectionData, 699 const ::std::vector<TabBar::DeckMenuData>& rDeckShowData) const 700 { 701 ::boost::shared_ptr<PopupMenu> pMenu (new PopupMenu()); 702 FloatingWindow* pMenuWindow = dynamic_cast<FloatingWindow*>(pMenu->GetWindow()); 703 if (pMenuWindow != NULL) 704 { 705 pMenuWindow->SetPopupModeFlags(pMenuWindow->GetPopupModeFlags() | FLOATWIN_POPUPMODE_NOMOUSEUPCLOSE); 706 } 707 708 SidebarResource aLocalResource; 709 710 // Add one entry for every tool panel element to individually make 711 // them visible or hide them. 712 { 713 sal_Int32 nIndex (MID_FIRST_PANEL); 714 for(::std::vector<TabBar::DeckMenuData>::const_iterator 715 iItem(rDeckSelectionData.begin()), 716 iEnd(rDeckSelectionData.end()); 717 iItem!=iEnd; 718 ++iItem) 719 { 720 pMenu->InsertItem(nIndex, iItem->get<0>(), MIB_RADIOCHECK); 721 pMenu->CheckItem(nIndex, iItem->get<2>()); 722 ++nIndex; 723 } 724 } 725 726 pMenu->InsertSeparator(); 727 728 // Add entry for docking or un-docking the tool panel. 729 if (mpParentWindow->IsFloatingMode()) 730 pMenu->InsertItem(MID_LOCK_TASK_PANEL, String(SfxResId(STR_SFX_DOCK))); 731 else 732 pMenu->InsertItem(MID_UNLOCK_TASK_PANEL, String(SfxResId(STR_SFX_UNDOCK))); 733 734 // Add sub menu for customization (hiding of deck tabs.) 735 PopupMenu* pCustomizationMenu = new PopupMenu(); 736 { 737 sal_Int32 nIndex (MID_FIRST_HIDE); 738 for(::std::vector<TabBar::DeckMenuData>::const_iterator 739 iItem(rDeckShowData.begin()), 740 iEnd(rDeckShowData.end()); 741 iItem!=iEnd; 742 ++iItem) 743 { 744 pCustomizationMenu->InsertItem(nIndex, iItem->get<0>(), MIB_CHECKABLE); 745 pCustomizationMenu->CheckItem(nIndex, iItem->get<2>()); 746 ++nIndex; 747 } 748 } 749 750 pCustomizationMenu->InsertSeparator(); 751 pCustomizationMenu->InsertItem(MID_RESTORE_DEFAULT, String(SfxResId(STRING_RESTORE))); 752 753 pMenu->InsertItem(MID_CUSTOMIZATION, String(SfxResId(STRING_CUSTOMIZATION))); 754 pMenu->SetPopupMenu(MID_CUSTOMIZATION, pCustomizationMenu); 755 756 pMenu->RemoveDisabledEntries(sal_False, sal_False); 757 758 return pMenu; 759 } 760 761 762 763 764 IMPL_LINK(SidebarController, OnMenuItemSelected, Menu*, pMenu) 765 { 766 if (pMenu == NULL) 767 { 768 OSL_ENSURE(pMenu!=NULL, "sfx2::sidebar::SidebarController::OnMenuItemSelected: illegal menu!"); 769 return 0; 770 } 771 772 pMenu->Deactivate(); 773 const sal_Int32 nIndex (pMenu->GetCurItemId()); 774 switch (nIndex) 775 { 776 case MID_UNLOCK_TASK_PANEL: 777 mpParentWindow->SetFloatingMode(sal_True); 778 break; 779 780 case MID_LOCK_TASK_PANEL: 781 mpParentWindow->SetFloatingMode(sal_False); 782 break; 783 784 case MID_RESTORE_DEFAULT: 785 mpTabBar->RestoreHideFlags(); 786 break; 787 788 default: 789 { 790 try 791 { 792 if (nIndex >= MID_FIRST_PANEL && nIndex<MID_FIRST_HIDE) 793 SwitchToDeck(mpTabBar->GetDeckIdForIndex(nIndex - MID_FIRST_PANEL)); 794 else if (nIndex >=MID_FIRST_HIDE) 795 mpTabBar->ToggleHideFlag(nIndex-MID_FIRST_HIDE); 796 } 797 catch (RuntimeException&) 798 { 799 } 800 } 801 break; 802 } 803 804 return 1; 805 } 806 807 808 809 810 void SidebarController::CloseDeck (void) 811 { 812 if ( ! mbIsDeckClosed) 813 { 814 mbIsDeckClosed = true; 815 if ( ! mpParentWindow->IsFloatingMode()) 816 mnSavedSidebarWidth = SetChildWindowWidth(TabBar::GetDefaultWidth()); 817 mpParentWindow->SetStyle(mpParentWindow->GetStyle() & ~WB_SIZEABLE); 818 819 if (mpCurrentDeck) 820 mpCurrentDeck->Hide(); 821 822 NotifyResize(); 823 } 824 } 825 826 827 828 829 void SidebarController::OpenDeck (void) 830 { 831 if (mbIsDeckClosed) 832 { 833 mbIsDeckClosed = false; 834 SetChildWindowWidth(mnSavedSidebarWidth); 835 836 if (mpCurrentDeck) 837 mpCurrentDeck->Show(); 838 839 NotifyResize(); 840 } 841 } 842 843 844 845 846 bool SidebarController::CanModifyChildWindowWidth (void) const 847 { 848 SfxSplitWindow* pSplitWindow = dynamic_cast<SfxSplitWindow*>(mpParentWindow->GetParent()); 849 if (pSplitWindow == NULL) 850 { 851 OSL_ASSERT(pSplitWindow!=NULL); 852 return 0; 853 } 854 855 sal_uInt16 nRow (0xffff); 856 sal_uInt16 nColumn (0xffff); 857 pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow); 858 859 sal_uInt16 nRowCount (pSplitWindow->GetWindowCount(nColumn)); 860 861 return nRowCount == 1; 862 } 863 864 865 866 867 sal_Int32 SidebarController::SetChildWindowWidth (const sal_Int32 nNewWidth) 868 { 869 SfxSplitWindow* pSplitWindow = dynamic_cast<SfxSplitWindow*>(mpParentWindow->GetParent()); 870 if (pSplitWindow == NULL) 871 return 0; 872 873 sal_uInt16 nRow (0xffff); 874 sal_uInt16 nColumn (0xffff); 875 pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow); 876 const long nColumnWidth (pSplitWindow->GetLineSize(nColumn)); 877 878 Window* pWindow = mpParentWindow; 879 const Point aWindowPosition (pWindow->GetPosPixel()); 880 const Size aWindowSize (pWindow->GetSizePixel()); 881 882 pSplitWindow->MoveWindow( 883 mpParentWindow, 884 Size(nNewWidth, aWindowSize.Height()), 885 nColumn, 886 nRow); 887 888 return static_cast<sal_Int32>(nColumnWidth); 889 } 890 891 892 893 894 void SidebarController::RestrictWidth (void) 895 { 896 SfxSplitWindow* pSplitWindow = dynamic_cast<SfxSplitWindow*>(mpParentWindow->GetParent()); 897 if (pSplitWindow != NULL) 898 { 899 const sal_uInt16 nId (pSplitWindow->GetItemId(mpParentWindow)); 900 const sal_uInt16 nSetId (pSplitWindow->GetSet(nId)); 901 // Minimum width is always that of the tabbar. 902 const sal_Int32 nMinimumWidth (TabBar::GetDefaultWidth()); 903 // Maximum width depends on whether the deck is open or closed. 904 const sal_Int32 nMaximumWidth ( 905 mbIsDeckClosed 906 ? TabBar::GetDefaultWidth() 907 : 400); 908 pSplitWindow->SetItemSizeRange( 909 nSetId, 910 Range(nMinimumWidth, nMaximumWidth)); 911 if (nMinimumWidth == nMaximumWidth) 912 pSplitWindow->SetItemSize(nSetId, nMinimumWidth); 913 } 914 } 915 916 917 } } // end of namespace sfx2::sidebar 918