1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <sfx2/sidebar/TabBar.hxx> 21 #include <sfx2/sidebar/TabItem.hxx> 22 #include <sfx2/sidebar/ControlFactory.hxx> 23 #include <sfx2/sidebar/DeckDescriptor.hxx> 24 #include <sfx2/sidebar/Paint.hxx> 25 #include <sfx2/sidebar/Theme.hxx> 26 #include <sfx2/sidebar/Tools.hxx> 27 #include <sfx2/sidebar/FocusManager.hxx> 28 #include <sfx2/sidebar/SidebarController.hxx> 29 #include <sfx2/strings.hrc> 30 31 #include <sfx2/sfxresid.hxx> 32 33 #include <comphelper/processfactory.hxx> 34 #include <vcl/commandevent.hxx> 35 #include <vcl/event.hxx> 36 #include <vcl/gradient.hxx> 37 #include <vcl/image.hxx> 38 #include <vcl/svapp.hxx> 39 #include <vcl/wrkwin.hxx> 40 #include <tools/svborder.hxx> 41 #include <svtools/acceleratorexecute.hxx> 42 43 #include <com/sun/star/graphic/XGraphicProvider.hpp> 44 45 #include <sfx2/app.hxx> 46 47 using namespace css; 48 using namespace css::uno; 49 50 namespace sfx2 { namespace sidebar { 51 52 TabBar::TabBar(vcl::Window* pParentWindow, 53 const Reference<frame::XFrame>& rxFrame, 54 const std::function<void (const OUString&)>& rDeckActivationFunctor, 55 const PopupMenuProvider& rPopupMenuProvider, 56 SidebarController* rParentSidebarController 57 ) 58 : Window(pParentWindow, WB_DIALOGCONTROL), 59 mxFrame(rxFrame), 60 mpMenuButton(ControlFactory::CreateMenuButton(this)), 61 maItems(), 62 maDeckActivationFunctor(rDeckActivationFunctor), 63 maPopupMenuProvider(rPopupMenuProvider), 64 pParentSidebarController(rParentSidebarController) 65 { 66 67 SetBackground(Theme::GetPaint(Theme::Paint_TabBarBackground).GetWallpaper()); 68 69 mpMenuButton->SetModeImage(Theme::GetImage(Theme::Image_TabBarMenu)); 70 mpMenuButton->SetClickHdl(LINK(this, TabBar, OnToolboxClicked)); 71 mpMenuButton->SetQuickHelpText(SfxResId(SFX_STR_SIDEBAR_SETTINGS)); 72 Layout(); 73 74 #ifdef DEBUG 75 SetText(OUString("TabBar")); 76 #endif 77 } 78 79 TabBar::~TabBar() 80 { 81 disposeOnce(); 82 } 83 84 void TabBar::dispose() 85 { 86 for (auto & item : maItems) 87 item.mpButton.disposeAndClear(); 88 maItems.clear(); 89 mpMenuButton.disposeAndClear(); 90 vcl::Window::dispose(); 91 } 92 93 void TabBar::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rUpdateArea) 94 { 95 Window::Paint(rRenderContext, rUpdateArea); 96 97 const sal_Int32 nHorizontalPadding(Theme::GetInteger(Theme::Int_TabMenuSeparatorPadding)); 98 rRenderContext.SetLineColor(Theme::GetColor(Theme::Color_TabMenuSeparator)); 99 rRenderContext.DrawLine(Point(nHorizontalPadding, mnMenuSeparatorY), 100 Point(GetSizePixel().Width() - nHorizontalPadding, mnMenuSeparatorY)); 101 } 102 103 sal_Int32 TabBar::GetDefaultWidth() 104 { 105 return Theme::GetInteger(Theme::Int_TabItemWidth) 106 + Theme::GetInteger(Theme::Int_TabBarLeftPadding) 107 + Theme::GetInteger(Theme::Int_TabBarRightPadding); 108 } 109 110 void TabBar::SetDecks(const ResourceManager::DeckContextDescriptorContainer& rDecks) 111 { 112 // Remove the current buttons. 113 { 114 for (auto & item : maItems) 115 { 116 item.mpButton.disposeAndClear(); 117 } 118 maItems.clear(); 119 } 120 maItems.resize(rDecks.size()); 121 sal_Int32 nIndex (0); 122 for (auto const& deck : rDecks) 123 { 124 std::shared_ptr<DeckDescriptor> xDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(deck.msId); 125 if (xDescriptor == nullptr) 126 { 127 OSL_ASSERT(xDescriptor!=nullptr); 128 continue; 129 } 130 131 Item& rItem (maItems[nIndex++]); 132 rItem.msDeckId = xDescriptor->msId; 133 rItem.mpButton.disposeAndClear(); 134 rItem.mpButton = CreateTabItem(*xDescriptor); 135 rItem.mpButton->SetClickHdl(LINK(&rItem, TabBar::Item, HandleClick)); 136 rItem.maDeckActivationFunctor = maDeckActivationFunctor; 137 rItem.mbIsHidden = ! xDescriptor->mbIsEnabled; 138 rItem.mbIsHiddenByDefault = rItem.mbIsHidden; // the default is the state while creating 139 140 rItem.mpButton->Enable(deck.mbIsEnabled); 141 } 142 143 UpdateButtonIcons(); 144 Layout(); 145 } 146 147 void TabBar::UpdateButtonIcons() 148 { 149 Image aImage = Theme::GetImage(Theme::Image_TabBarMenu); 150 mpMenuButton->SetModeImage(aImage); 151 152 for (auto const& item : maItems) 153 { 154 std::shared_ptr<DeckDescriptor> xDeckDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(item.msDeckId); 155 156 if (xDeckDescriptor) 157 { 158 aImage = GetItemImage(*xDeckDescriptor); 159 item.mpButton->SetModeImage(aImage); 160 } 161 } 162 163 Invalidate(); 164 } 165 166 void TabBar::Layout() 167 { 168 const SvBorder aPadding ( 169 Theme::GetInteger(Theme::Int_TabBarLeftPadding), 170 Theme::GetInteger(Theme::Int_TabBarTopPadding), 171 Theme::GetInteger(Theme::Int_TabBarRightPadding), 172 Theme::GetInteger(Theme::Int_TabBarBottomPadding)); 173 sal_Int32 nX (aPadding.Top()); 174 sal_Int32 nY (aPadding.Left()); 175 const Size aTabItemSize ( 176 Theme::GetInteger(Theme::Int_TabItemWidth) * GetDPIScaleFactor(), 177 Theme::GetInteger(Theme::Int_TabItemHeight) * GetDPIScaleFactor()); 178 179 // Place the menu button and the separator. 180 if (mpMenuButton != nullptr) 181 { 182 mpMenuButton->SetPosSizePixel( 183 Point(nX,nY), 184 aTabItemSize); 185 mpMenuButton->Show(); 186 nY += mpMenuButton->GetSizePixel().Height() + 1 + Theme::GetInteger(Theme::Int_TabMenuPadding); 187 mnMenuSeparatorY = nY - Theme::GetInteger(Theme::Int_TabMenuPadding)/2 - 1; 188 } 189 190 // Place the deck selection buttons. 191 for (auto const& item : maItems) 192 { 193 Button& rButton (*item.mpButton); 194 rButton.Show( ! item.mbIsHidden); 195 196 if (item.mbIsHidden) 197 continue; 198 199 // Place and size the icon. 200 rButton.SetPosSizePixel( 201 Point(nX,nY), 202 aTabItemSize); 203 rButton.Show(); 204 205 nY += rButton.GetSizePixel().Height() + 1 + aPadding.Bottom(); 206 } 207 Invalidate(); 208 } 209 210 void TabBar::HighlightDeck (const OUString& rsDeckId) 211 { 212 for (auto const& item : maItems) 213 { 214 if (item.msDeckId == rsDeckId) 215 item.mpButton->Check(); 216 else 217 item.mpButton->Check(false); 218 } 219 } 220 221 void TabBar::RemoveDeckHighlight () 222 { 223 for (auto const& item : maItems) 224 { 225 item.mpButton->Check(false); 226 } 227 } 228 229 void TabBar::DataChanged (const DataChangedEvent& rDataChangedEvent) 230 { 231 SetBackground(Theme::GetPaint(Theme::Paint_TabBarBackground).GetWallpaper()); 232 UpdateButtonIcons(); 233 234 Window::DataChanged(rDataChangedEvent); 235 } 236 237 bool TabBar::EventNotify(NotifyEvent& rEvent) 238 { 239 MouseNotifyEvent nType = rEvent.GetType(); 240 if(MouseNotifyEvent::KEYINPUT == nType) 241 { 242 const vcl::KeyCode& rKeyCode = rEvent.GetKeyEvent()->GetKeyCode(); 243 if (!mpAccel) 244 { 245 mpAccel = svt::AcceleratorExecute::createAcceleratorHelper(); 246 mpAccel->init(comphelper::getProcessComponentContext(), mxFrame); 247 } 248 const OUString aCommand(mpAccel->findCommand(svt::AcceleratorExecute::st_VCLKey2AWTKey(rKeyCode))); 249 if (".uno:Sidebar" == aCommand) 250 return vcl::Window::EventNotify(rEvent); 251 return true; 252 } 253 else if(MouseNotifyEvent::COMMAND == nType) 254 { 255 const CommandEvent& rCommandEvent = *rEvent.GetCommandEvent(); 256 if(rCommandEvent.GetCommand() == CommandEventId::Wheel) 257 { 258 const CommandWheelData* pData = rCommandEvent.GetWheelData(); 259 if(!pData->GetModifier() && (pData->GetMode() == CommandWheelMode::SCROLL)) 260 { 261 auto pItem = std::find_if(maItems.begin(), maItems.end(), 262 [] (Item const& rItem) { return rItem.mpButton->IsChecked(); }); 263 if(pItem == maItems.end()) 264 return true; 265 if(pData->GetNotchDelta()<0) 266 { 267 if(pItem+1 == maItems.end()) 268 return true; 269 ++pItem; 270 } 271 else 272 { 273 if(pItem == maItems.begin()) 274 return true; 275 --pItem; 276 } 277 try 278 { 279 pItem->maDeckActivationFunctor(pItem->msDeckId); 280 } 281 catch(const css::uno::Exception&) {}; 282 return true; 283 } 284 } 285 } 286 return false; 287 } 288 289 VclPtr<RadioButton> TabBar::CreateTabItem(const DeckDescriptor& rDeckDescriptor) 290 { 291 VclPtr<RadioButton> pItem = ControlFactory::CreateTabItem(this); 292 pItem->SetAccessibleName(rDeckDescriptor.msTitle); 293 pItem->SetAccessibleDescription(rDeckDescriptor.msHelpText); 294 pItem->SetHelpText(rDeckDescriptor.msHelpText); 295 pItem->SetQuickHelpText(rDeckDescriptor.msHelpText); 296 return pItem; 297 } 298 299 Image TabBar::GetItemImage(const DeckDescriptor& rDeckDescriptor) const 300 { 301 return Tools::GetImage( 302 rDeckDescriptor.msIconURL, 303 rDeckDescriptor.msHighContrastIconURL, 304 mxFrame); 305 } 306 307 IMPL_LINK_NOARG(TabBar::Item, HandleClick, Button*, void) 308 { 309 vcl::Window* pFocusWin = Application::GetFocusWindow(); 310 pFocusWin->GrabFocusToDocument(); 311 try 312 { 313 maDeckActivationFunctor(msDeckId); 314 } 315 catch(const css::uno::Exception&) 316 {} // workaround for #i123198# 317 } 318 319 OUString const & TabBar::GetDeckIdForIndex (const sal_Int32 nIndex) const 320 { 321 if (nIndex<0 || static_cast<size_t>(nIndex)>=maItems.size()) 322 throw RuntimeException(); 323 return maItems[nIndex].msDeckId; 324 } 325 326 void TabBar::ToggleHideFlag (const sal_Int32 nIndex) 327 { 328 if (nIndex<0 || static_cast<size_t>(nIndex) >= maItems.size()) 329 throw RuntimeException(); 330 331 maItems[nIndex].mbIsHidden = ! maItems[nIndex].mbIsHidden; 332 333 std::shared_ptr<DeckDescriptor> xDeckDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(maItems[nIndex].msDeckId); 334 if (xDeckDescriptor) 335 { 336 xDeckDescriptor->mbIsEnabled = ! maItems[nIndex].mbIsHidden; 337 338 Context aContext; 339 aContext.msApplication = pParentSidebarController->GetCurrentContext().msApplication; 340 // leave aContext.msContext on default 'any' ... this func is used only for decks 341 // and we don't have context-sensitive decks anyway 342 343 xDeckDescriptor->maContextList.ToggleVisibilityForContext( 344 aContext, xDeckDescriptor->mbIsEnabled ); 345 } 346 347 Layout(); 348 } 349 350 void TabBar::RestoreHideFlags() 351 { 352 bool bNeedsLayout(false); 353 for (auto & item : maItems) 354 { 355 if (item.mbIsHidden != item.mbIsHiddenByDefault) 356 { 357 item.mbIsHidden = item.mbIsHiddenByDefault; 358 bNeedsLayout = true; 359 360 std::shared_ptr<DeckDescriptor> xDeckDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(item.msDeckId); 361 if (xDeckDescriptor) 362 xDeckDescriptor->mbIsEnabled = ! item.mbIsHidden; 363 364 } 365 } 366 if (bNeedsLayout) 367 Layout(); 368 } 369 370 void TabBar::UpdateFocusManager(FocusManager& rFocusManager) 371 { 372 std::vector<Button*> aButtons; 373 aButtons.reserve(maItems.size()+1); 374 375 aButtons.push_back(mpMenuButton.get()); 376 for (auto const& item : maItems) 377 { 378 aButtons.push_back(item.mpButton.get()); 379 } 380 rFocusManager.SetButtons(aButtons); 381 } 382 383 IMPL_LINK_NOARG(TabBar, OnToolboxClicked, Button*, void) 384 { 385 if (!mpMenuButton) 386 return; 387 388 std::vector<DeckMenuData> aMenuData; 389 390 for (auto const& item : maItems) 391 { 392 std::shared_ptr<DeckDescriptor> xDeckDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(item.msDeckId); 393 394 if (xDeckDescriptor) 395 { 396 DeckMenuData aData; 397 aData.msDisplayName = xDeckDescriptor->msTitle; 398 aData.mbIsCurrentDeck = item.mpButton->IsChecked(); 399 aData.mbIsActive = !item.mbIsHidden; 400 aData.mbIsEnabled = item.mpButton->IsEnabled(); 401 402 aMenuData.push_back(aData); 403 } 404 } 405 406 maPopupMenuProvider( 407 tools::Rectangle( 408 mpMenuButton->GetPosPixel(), 409 mpMenuButton->GetSizePixel()), 410 aMenuData); 411 mpMenuButton->Check(false); 412 } 413 414 void TabBar::EnableMenuButton(const bool bEnable) 415 { 416 mpMenuButton->Enable(bEnable); 417 } 418 419 } } // end of namespace sfx2::sidebar 420 421 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 422
