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