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 "DeckConfiguration.hxx" 27 #include "Panel.hxx" 28 #include "SidebarPanel.hxx" 29 #include "SidebarResource.hxx" 30 #include "TitleBar.hxx" 31 #include "TabBar.hxx" 32 #include "sfx2/sidebar/Theme.hxx" 33 34 #include "sfxresid.hxx" 35 #include "sfx2/sfxsids.hrc" 36 #include "sfx2/dockwin.hxx" 37 #include "sfxlocal.hrc" 38 #include <vcl/floatwin.hxx> 39 #include <vcl/dockwin.hxx> 40 #include <comphelper/componentfactory.hxx> 41 #include <comphelper/componentcontext.hxx> 42 #include <comphelper/namedvaluecollection.hxx> 43 44 #include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp> 45 #include <com/sun/star/ui/ContextChangeEventObject.hpp> 46 47 #include <boost/bind.hpp> 48 #include <boost/foreach.hpp> 49 50 51 using namespace css; 52 using namespace cssu; 53 54 55 #define A2S(pString) (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(pString))) 56 57 namespace sfx2 { namespace sidebar { 58 59 namespace { 60 enum MenuId 61 { 62 MID_UNLOCK_TASK_PANEL = 1, 63 MID_LOCK_TASK_PANEL, 64 MID_CUSTOMIZATION, 65 MID_RESTORE_DEFAULT, 66 MID_FIRST_PANEL, 67 MID_FIRST_HIDE = 1000 68 }; 69 } 70 71 72 SidebarController::SidebarController ( 73 DockingWindow* pParentWindow, 74 const cssu::Reference<css::frame::XFrame>& rxFrame) 75 : SidebarControllerInterfaceBase(m_aMutex), 76 mpCurrentConfiguration(), 77 mpParentWindow(pParentWindow), 78 mpTabBar(new TabBar( 79 mpParentWindow, 80 rxFrame, 81 ::boost::bind(&SidebarController::SwitchToDeck, this, _1), 82 ::boost::bind(&SidebarController::ShowPopupMenu, this, _1))), 83 mxFrame(rxFrame) 84 { 85 if (pParentWindow == NULL) 86 { 87 OSL_ASSERT(pParentWindow!=NULL); 88 return; 89 } 90 91 UpdateConfigurations(Context(A2S("default"), A2S("default"))); 92 93 // Listen for context change events. 94 cssu::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer ( 95 css::ui::ContextChangeEventMultiplexer::get( 96 ::comphelper::getProcessComponentContext())); 97 if (xMultiplexer.is()) 98 xMultiplexer->addContextChangeEventListener( 99 static_cast<css::ui::XContextChangeEventListener*>(this), 100 NULL); 101 102 // Listen for window events. 103 mpParentWindow->AddEventListener(LINK(this, SidebarController, WindowEventHandler)); 104 105 // Listen for theme property changes. 106 Theme::GetPropertySet()->addPropertyChangeListener( 107 A2S(""), 108 static_cast<css::beans::XPropertyChangeListener*>(this)); 109 } 110 111 112 113 114 SidebarController::~SidebarController (void) 115 { 116 } 117 118 119 120 121 void SAL_CALL SidebarController::disposing (void) 122 { 123 cssu::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer ( 124 css::ui::ContextChangeEventMultiplexer::get( 125 ::comphelper::getProcessComponentContext())); 126 if (xMultiplexer.is()) 127 xMultiplexer->removeAllContextChangeEventListeners( 128 static_cast<css::ui::XContextChangeEventListener*>(this)); 129 130 if (mpParentWindow != NULL) 131 { 132 mpParentWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler)); 133 mpParentWindow = NULL; 134 } 135 136 if (mpCurrentConfiguration) 137 { 138 mpCurrentConfiguration->Disable(); 139 mpCurrentConfiguration.reset(); 140 } 141 142 Theme::GetPropertySet()->removePropertyChangeListener( 143 A2S(""), 144 static_cast<css::beans::XPropertyChangeListener*>(this)); 145 } 146 147 148 149 150 void SAL_CALL SidebarController::notifyContextChangeEvent (const css::ui::ContextChangeEventObject& rEvent) 151 throw(cssu::RuntimeException) 152 { 153 UpdateConfigurations(Context(rEvent.ApplicationName, rEvent.ContextName)); 154 } 155 156 157 158 159 void SAL_CALL SidebarController::disposing (const css::lang::EventObject& rEventObject) 160 throw(cssu::RuntimeException) 161 { 162 if (mpCurrentConfiguration) 163 { 164 mpCurrentConfiguration->Disable(); 165 mpCurrentConfiguration.reset(); 166 } 167 if (mpTabBar != NULL) 168 { 169 mpTabBar->Hide(); 170 delete mpTabBar; 171 mpTabBar = NULL; 172 } 173 } 174 175 176 177 178 void SAL_CALL SidebarController::propertyChange (const css::beans::PropertyChangeEvent& rEvent) 179 throw(cssu::RuntimeException) 180 { 181 DataChangedEvent aEvent (DATACHANGED_USER); 182 mpParentWindow->NotifyAllChilds(aEvent); 183 mpParentWindow->Invalidate(INVALIDATE_CHILDREN); 184 } 185 186 187 188 189 void SidebarController::NotifyResize (void) 190 { 191 if (mpCurrentConfiguration != NULL) 192 { 193 if (mpCurrentConfiguration->mpDeck==NULL || mpTabBar==NULL) 194 { 195 OSL_ASSERT(mpCurrentConfiguration->mpDeck!=NULL && mpTabBar!=NULL); 196 } 197 else 198 { 199 Window* pParentWindow = mpCurrentConfiguration->mpDeck->GetParent(); 200 if (pParentWindow==NULL || pParentWindow!=mpTabBar->GetParent()) 201 { 202 OSL_ASSERT(mpCurrentConfiguration->mpDeck->GetParent() != NULL); 203 OSL_ASSERT(mpCurrentConfiguration->mpDeck->GetParent() == mpTabBar->GetParent()); 204 } 205 206 const sal_Int32 nWidth (pParentWindow->GetSizePixel().Width()); 207 const sal_Int32 nHeight (pParentWindow->GetSizePixel().Height()); 208 mpCurrentConfiguration->mpDeck->SetPosSizePixel(0,0, nWidth-TabBar::GetDefaultWidth(), nHeight); 209 mpCurrentConfiguration->mpDeck->Show(); 210 mpTabBar->SetPosSizePixel(nWidth-TabBar::GetDefaultWidth(),0,TabBar::GetDefaultWidth(),nHeight); 211 mpTabBar->Show(); 212 213 mpCurrentConfiguration->mpDeck->RequestLayout(); 214 } 215 } 216 } 217 218 219 220 221 void SidebarController::UpdateConfigurations (const Context& rContext) 222 { 223 maCurrentContext = rContext; 224 225 ResourceManager::DeckContainer aDeckDescriptors; 226 ResourceManager::Instance().GetMatchingDecks ( 227 aDeckDescriptors, 228 rContext, 229 mxFrame); 230 mpTabBar->SetDecks(aDeckDescriptors); 231 232 const DeckDescriptor* pDeckDescriptor (ResourceManager::Instance().GetBestMatchingDeck(rContext, mxFrame)); 233 if (pDeckDescriptor != NULL) 234 SwitchToDeck(*pDeckDescriptor, rContext); 235 } 236 237 238 239 240 void SidebarController::SwitchToDeck ( 241 const DeckDescriptor& rDeckDescriptor) 242 { 243 SwitchToDeck(rDeckDescriptor, maCurrentContext); 244 } 245 246 247 248 249 void SidebarController::SwitchToDeck ( 250 const DeckDescriptor& rDeckDescriptor, 251 const Context& rContext) 252 { 253 // Determine the panels to display in the deck. 254 ResourceManager::PanelContainer aPanelDescriptors; 255 ResourceManager::Instance().GetMatchingPanels( 256 aPanelDescriptors, 257 rContext, 258 rDeckDescriptor.msId, 259 mxFrame); 260 261 // Setup a configuration for the requested deck 262 // and create the deck. 263 ::boost::shared_ptr<DeckConfiguration> pConfiguration (new DeckConfiguration); 264 pConfiguration->mpDeck = new Deck(rDeckDescriptor, mpParentWindow); 265 266 // Create the panels. 267 for (ResourceManager::PanelContainer::const_iterator 268 iPanel(aPanelDescriptors.begin()), 269 iEnd(aPanelDescriptors.end()); 270 iPanel!=iEnd; 271 ++iPanel) 272 { 273 // Create the panel which is the parent window of the UIElement. 274 Panel* pPanel = new Panel( 275 *iPanel, 276 pConfiguration->mpDeck, 277 ::boost::bind(&Deck::RequestLayout,pConfiguration->mpDeck)); 278 279 // Create the XUIElement. 280 Reference<ui::XUIElement> xUIElement (CreateUIElement( 281 pPanel->GetComponentInterface(), 282 iPanel->msImplementationURL, 283 pPanel 284 )); 285 if (xUIElement.is()) 286 { 287 // Initialize the panel and add it to the active deck. 288 pPanel->SetUIElement(xUIElement); 289 pConfiguration->maPanels.push_back(pPanel); 290 } 291 else 292 { 293 delete pPanel; 294 } 295 } 296 297 // Activate the new configuration. 298 MakeConfigurationCurrent(pConfiguration); 299 300 // Tell the tab bar to highlight the button associated with the 301 // deck. 302 mpTabBar->HighlightDeck(rDeckDescriptor.msId); 303 } 304 305 306 307 308 Reference<ui::XUIElement> SidebarController::CreateUIElement ( 309 const Reference<awt::XWindowPeer>& rxWindow, 310 const ::rtl::OUString& rsImplementationURL, 311 Panel* pPanel) const 312 { 313 try 314 { 315 const ::comphelper::ComponentContext aComponentContext (::comphelper::getProcessServiceFactory()); 316 const Reference<ui::XUIElementFactory> xUIElementFactory ( 317 aComponentContext.createComponent("com.sun.star.ui.UIElementFactoryManager"), 318 UNO_QUERY_THROW); 319 320 321 // Create the XUIElement. 322 ::comphelper::NamedValueCollection aCreationArguments; 323 aCreationArguments.put("Frame", makeAny(mxFrame)); 324 aCreationArguments.put("ParentWindow", makeAny(rxWindow)); 325 SfxDockingWindow* pSfxDockingWindow = dynamic_cast<SfxDockingWindow*>(mpParentWindow); 326 if (pSfxDockingWindow != NULL) 327 aCreationArguments.put("SfxBindings", makeAny(sal_uInt64(&pSfxDockingWindow->GetBindings()))); 328 Reference<ui::XUIElement> xUIElement( 329 xUIElementFactory->createUIElement( 330 rsImplementationURL, 331 aCreationArguments.getPropertyValues()), 332 UNO_QUERY_THROW); 333 334 // Provide the new ui element with the XSidebarPanel object 335 // that gives access to a canvas, screen coordinates of the 336 // panel or the theme properties. 337 if (xUIElement.is()) 338 { 339 Reference<lang::XInitialization> xInitialization(xUIElement->getRealInterface(), UNO_QUERY); 340 if (xInitialization.is()) 341 { 342 Sequence<Any> aArguments (1); 343 Reference<ui::XSidebarPanel> xPanel (SidebarPanel::Create(pPanel)); 344 aArguments[0] = Any(xPanel); 345 xInitialization->initialize(aArguments); 346 } 347 } 348 349 return xUIElement; 350 } 351 catch(Exception& rException) 352 { 353 OSL_TRACE("caught exception: %s", 354 OUStringToOString(rException.Message, RTL_TEXTENCODING_ASCII_US).getStr()); 355 // For some reason we can not create the actual panel. 356 // Probably because its factory was not properly registered. 357 // TODO: provide feedback to developer to better pinpoint the 358 // source of the error. 359 360 return NULL; 361 } 362 } 363 364 365 366 367 void SidebarController::MakeConfigurationCurrent (const ::boost::shared_ptr<DeckConfiguration>& rpConfiguration) 368 { 369 if ( ! rpConfiguration || rpConfiguration->mpDeck == NULL) 370 return; 371 372 // Deactivate the current deck and panels. 373 if (mpCurrentConfiguration && mpCurrentConfiguration->mpDeck!=NULL) 374 mpCurrentConfiguration->Disable(); 375 376 mpCurrentConfiguration = rpConfiguration; 377 378 mpCurrentConfiguration->mpDeck->SetPosSizePixel( 379 0, 380 0, 381 mpParentWindow->GetSizePixel().Width()-TabBar::GetDefaultWidth(), 382 mpParentWindow->GetSizePixel().Height()); 383 mpCurrentConfiguration->mpDeck->Show(); 384 mpCurrentConfiguration->Activate(); 385 } 386 387 388 389 390 IMPL_LINK(SidebarController, WindowEventHandler, VclWindowEvent*, pEvent) 391 { 392 if (pEvent != NULL) 393 { 394 ::Window* pWindow = pEvent->GetWindow(); 395 switch (pEvent->GetId()) 396 { 397 case VCLEVENT_WINDOW_GETFOCUS: 398 case VCLEVENT_WINDOW_LOSEFOCUS: 399 break; 400 401 case VCLEVENT_WINDOW_SHOW: 402 case VCLEVENT_WINDOW_RESIZE: 403 NotifyResize(); 404 break; 405 406 case VCLEVENT_WINDOW_DATACHANGED: 407 // Force an update of deck and tab bar to reflect 408 // changes in theme (high contrast mode). 409 Theme::HandleDataChange(); 410 mpParentWindow->Invalidate(); 411 break; 412 413 case SFX_HINT_DYING: 414 dispose(); 415 break; 416 417 default: 418 break; 419 } 420 } 421 422 return sal_True; 423 } 424 425 426 427 428 void SidebarController::ShowPopupMenu (const Rectangle& rButtonBox) const 429 { 430 ::boost::shared_ptr<PopupMenu> pMenu = CreatePopupMenu(); 431 pMenu->SetSelectHdl(LINK(this, SidebarController, OnMenuItemSelected)); 432 433 // pass toolbox button rect so the menu can stay open on button up 434 Rectangle aBox (rButtonBox); 435 aBox.Move(mpTabBar->GetPosPixel().X(), 0); 436 pMenu->Execute(mpParentWindow, aBox, POPUPMENU_EXECUTE_DOWN); 437 } 438 439 440 441 442 ::boost::shared_ptr<PopupMenu> SidebarController::CreatePopupMenu (void) const 443 { 444 ::boost::shared_ptr<PopupMenu> pMenu (new PopupMenu()); 445 FloatingWindow* pMenuWindow = dynamic_cast<FloatingWindow*>(pMenu->GetWindow()); 446 if (pMenuWindow != NULL) 447 { 448 pMenuWindow->SetPopupModeFlags(pMenuWindow->GetPopupModeFlags() | FLOATWIN_POPUPMODE_NOMOUSEUPCLOSE); 449 } 450 451 SidebarResource aLocalResource; 452 453 // Add one entry for every tool panel element to individually make 454 // them visible or hide them. 455 if (mpTabBar != NULL) 456 { 457 mpTabBar->AddPopupMenuEntries(*pMenu, MID_FIRST_PANEL); 458 pMenu->InsertSeparator(); 459 } 460 461 // Add entry for docking or un-docking the tool panel. 462 if (mpParentWindow->IsFloatingMode()) 463 pMenu->InsertItem(MID_LOCK_TASK_PANEL, String(SfxResId(STR_SFX_DOCK))); 464 else 465 pMenu->InsertItem(MID_UNLOCK_TASK_PANEL, String(SfxResId(STR_SFX_UNDOCK))); 466 467 // Add sub menu for customization (hiding of deck tabs.) 468 PopupMenu* pCustomizationMenu = new PopupMenu(); 469 mpTabBar->AddCustomizationMenuEntries(*pCustomizationMenu, MID_FIRST_HIDE); 470 pCustomizationMenu->InsertSeparator(); 471 pCustomizationMenu->InsertItem(MID_RESTORE_DEFAULT, String(SfxResId(STRING_RESTORE))); 472 473 pMenu->InsertItem(MID_CUSTOMIZATION, String(SfxResId(STRING_CUSTOMIZATION))); 474 pMenu->SetPopupMenu(MID_CUSTOMIZATION, pCustomizationMenu); 475 476 pMenu->RemoveDisabledEntries(sal_False, sal_False); 477 478 return pMenu; 479 } 480 481 482 483 484 IMPL_LINK(SidebarController, OnMenuItemSelected, Menu*, pMenu) 485 { 486 if (pMenu == NULL) 487 { 488 OSL_ENSURE(pMenu!=NULL, "TaskPaneController_Impl::OnMenuItemSelected: illegal menu!"); 489 return 0; 490 } 491 492 pMenu->Deactivate(); 493 const sal_Int32 nIndex (pMenu->GetCurItemId()); 494 switch (nIndex) 495 { 496 case MID_UNLOCK_TASK_PANEL: 497 mpParentWindow->SetFloatingMode(sal_True); 498 break; 499 500 case MID_LOCK_TASK_PANEL: 501 mpParentWindow->SetFloatingMode(sal_False); 502 break; 503 504 case MID_RESTORE_DEFAULT: 505 mpTabBar->RestoreHideFlags(); 506 break; 507 508 default: 509 { 510 try 511 { 512 if (nIndex >= MID_FIRST_PANEL && nIndex<MID_FIRST_HIDE) 513 SwitchToDeck(mpTabBar->GetDeckDescriptorForIndex(nIndex - MID_FIRST_PANEL)); 514 else if (nIndex >=MID_FIRST_HIDE) 515 mpTabBar->ToggleHideFlag(nIndex-MID_FIRST_HIDE); 516 } 517 catch (RuntimeException&) 518 { 519 } 520 } 521 break; 522 } 523 524 return 1; 525 } 526 527 528 529 530 531 } } // end of namespace sfx2::sidebar 532