diff options
Diffstat (limited to 'sfx2/source/sidebar/TabBar.cxx')
-rw-r--r-- | sfx2/source/sidebar/TabBar.cxx | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/sfx2/source/sidebar/TabBar.cxx b/sfx2/source/sidebar/TabBar.cxx new file mode 100644 index 000000000..4f49bd461 --- /dev/null +++ b/sfx2/source/sidebar/TabBar.cxx @@ -0,0 +1,417 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sfx2/sidebar/TabBar.hxx> +#include <sidebar/ControlFactory.hxx> +#include <sidebar/DeckDescriptor.hxx> +#include <sidebar/Paint.hxx> +#include <sfx2/sidebar/Theme.hxx> +#include <sidebar/Tools.hxx> +#include <sfx2/sidebar/FocusManager.hxx> +#include <sfx2/sidebar/SidebarController.hxx> +#include <sfx2/strings.hrc> + +#include <sfx2/sfxresid.hxx> + +#include <comphelper/processfactory.hxx> +#include <o3tl/safeint.hxx> +#include <vcl/button.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <vcl/image.hxx> +#include <vcl/svapp.hxx> +#include <tools/svborder.hxx> +#include <svtools/acceleratorexecute.hxx> + +using namespace css; +using namespace css::uno; + +namespace sfx2::sidebar { + +TabBar::TabBar(vcl::Window* pParentWindow, + const Reference<frame::XFrame>& rxFrame, + const std::function<void (const OUString&)>& rDeckActivationFunctor, + const PopupMenuProvider& rPopupMenuProvider, + SidebarController* rParentSidebarController + ) + : Window(pParentWindow, WB_DIALOGCONTROL), + mxFrame(rxFrame), + mpMenuButton(ControlFactory::CreateMenuButton(this)), + maItems(), + maDeckActivationFunctor(rDeckActivationFunctor), + maPopupMenuProvider(rPopupMenuProvider), + pParentSidebarController(rParentSidebarController) +{ + + SetBackground(Theme::GetPaint(Theme::Paint_TabBarBackground).GetWallpaper()); + + mpMenuButton->SetModeImage(Theme::GetImage(Theme::Image_TabBarMenu)); + mpMenuButton->SetClickHdl(LINK(this, TabBar, OnToolboxClicked)); + mpMenuButton->SetQuickHelpText(SfxResId(SFX_STR_SIDEBAR_SETTINGS)); + Layout(); + +#ifdef DEBUG + SetText(OUString("TabBar")); +#endif +} + +TabBar::~TabBar() +{ + disposeOnce(); +} + +void TabBar::dispose() +{ + for (auto & item : maItems) + item.mpButton.disposeAndClear(); + maItems.clear(); + mpMenuButton.disposeAndClear(); + vcl::Window::dispose(); +} + +void TabBar::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rUpdateArea) +{ + Window::Paint(rRenderContext, rUpdateArea); + + const sal_Int32 nHorizontalPadding(Theme::GetInteger(Theme::Int_TabMenuSeparatorPadding)); + rRenderContext.SetLineColor(Theme::GetColor(Theme::Color_TabMenuSeparator)); + rRenderContext.DrawLine(Point(nHorizontalPadding, mnMenuSeparatorY), + Point(GetSizePixel().Width() - nHorizontalPadding, mnMenuSeparatorY)); +} + +sal_Int32 TabBar::GetDefaultWidth() +{ + return Theme::GetInteger(Theme::Int_TabItemWidth) + + Theme::GetInteger(Theme::Int_TabBarLeftPadding) + + Theme::GetInteger(Theme::Int_TabBarRightPadding); +} + +void TabBar::SetDecks(const ResourceManager::DeckContextDescriptorContainer& rDecks) +{ + // Remove the current buttons. + { + for (auto & item : maItems) + { + item.mpButton.disposeAndClear(); + } + maItems.clear(); + } + maItems.resize(rDecks.size()); + sal_Int32 nIndex (0); + for (auto const& deck : rDecks) + { + std::shared_ptr<DeckDescriptor> xDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(deck.msId); + if (xDescriptor == nullptr) + { + OSL_ASSERT(xDescriptor!=nullptr); + continue; + } + + Item& rItem (maItems[nIndex++]); + rItem.msDeckId = xDescriptor->msId; + rItem.mpButton.disposeAndClear(); + rItem.mpButton = CreateTabItem(*xDescriptor); + rItem.mpButton->SetClickHdl(LINK(&rItem, TabBar::Item, HandleClick)); + rItem.maDeckActivationFunctor = maDeckActivationFunctor; + rItem.mbIsHidden = ! xDescriptor->mbIsEnabled; + rItem.mbIsHiddenByDefault = rItem.mbIsHidden; // the default is the state while creating + + rItem.mpButton->Enable(deck.mbIsEnabled); + } + + UpdateButtonIcons(); + Layout(); +} + +void TabBar::UpdateButtonIcons() +{ + Image aImage = Theme::GetImage(Theme::Image_TabBarMenu); + mpMenuButton->SetModeImage(aImage); + + for (auto const& item : maItems) + { + std::shared_ptr<DeckDescriptor> xDeckDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(item.msDeckId); + + if (xDeckDescriptor) + { + aImage = GetItemImage(*xDeckDescriptor); + item.mpButton->SetModeImage(aImage); + } + } + + Invalidate(); +} + +void TabBar::Layout() +{ + const SvBorder aPadding ( + Theme::GetInteger(Theme::Int_TabBarLeftPadding), + Theme::GetInteger(Theme::Int_TabBarTopPadding), + Theme::GetInteger(Theme::Int_TabBarRightPadding), + Theme::GetInteger(Theme::Int_TabBarBottomPadding)); + sal_Int32 nX (aPadding.Top()); + sal_Int32 nY (aPadding.Left()); + const Size aTabItemSize ( + Theme::GetInteger(Theme::Int_TabItemWidth) * GetDPIScaleFactor(), + Theme::GetInteger(Theme::Int_TabItemHeight) * GetDPIScaleFactor()); + + // Place the menu button and the separator. + if (mpMenuButton != nullptr) + { + mpMenuButton->SetPosSizePixel( + Point(nX,nY), + aTabItemSize); + mpMenuButton->Show(); + nY += mpMenuButton->GetSizePixel().Height() + 1 + Theme::GetInteger(Theme::Int_TabMenuPadding); + mnMenuSeparatorY = nY - Theme::GetInteger(Theme::Int_TabMenuPadding)/2 - 1; + } + + // Place the deck selection buttons. + for (auto const& item : maItems) + { + Button& rButton (*item.mpButton); + rButton.Show( ! item.mbIsHidden); + + if (item.mbIsHidden) + continue; + + // Place and size the icon. + rButton.SetPosSizePixel( + Point(nX,nY), + aTabItemSize); + rButton.Show(); + + nY += rButton.GetSizePixel().Height() + 1 + aPadding.Bottom(); + } + Invalidate(); +} + +void TabBar::HighlightDeck (const OUString& rsDeckId) +{ + for (auto const& item : maItems) + { + if (item.msDeckId == rsDeckId) + item.mpButton->Check(); + else + item.mpButton->Check(false); + } +} + +void TabBar::RemoveDeckHighlight () +{ + for (auto const& item : maItems) + { + item.mpButton->Check(false); + } +} + +void TabBar::DataChanged (const DataChangedEvent& rDataChangedEvent) +{ + SetBackground(Theme::GetPaint(Theme::Paint_TabBarBackground).GetWallpaper()); + UpdateButtonIcons(); + + Window::DataChanged(rDataChangedEvent); +} + +bool TabBar::EventNotify(NotifyEvent& rEvent) +{ + MouseNotifyEvent nType = rEvent.GetType(); + if(MouseNotifyEvent::KEYINPUT == nType) + { + const vcl::KeyCode& rKeyCode = rEvent.GetKeyEvent()->GetKeyCode(); + if (!mpAccel) + { + mpAccel = svt::AcceleratorExecute::createAcceleratorHelper(); + mpAccel->init(comphelper::getProcessComponentContext(), mxFrame); + } + const OUString aCommand(mpAccel->findCommand(svt::AcceleratorExecute::st_VCLKey2AWTKey(rKeyCode))); + if (".uno:Sidebar" == aCommand || + (rKeyCode.IsMod1() && rKeyCode.IsShift() && rKeyCode.GetCode() == KEY_F10)) + return vcl::Window::EventNotify(rEvent); + return true; + } + else if(MouseNotifyEvent::COMMAND == nType) + { + const CommandEvent& rCommandEvent = *rEvent.GetCommandEvent(); + if(rCommandEvent.GetCommand() == CommandEventId::Wheel) + { + const CommandWheelData* pData = rCommandEvent.GetWheelData(); + if(!pData->GetModifier() && (pData->GetMode() == CommandWheelMode::SCROLL)) + { + auto pItem = std::find_if(maItems.begin(), maItems.end(), + [] (Item const& rItem) { return rItem.mpButton->IsChecked(); }); + if(pItem == maItems.end()) + return true; + if(pData->GetNotchDelta()<0) + { + if(pItem+1 == maItems.end()) + return true; + ++pItem; + } + else + { + if(pItem == maItems.begin()) + return true; + --pItem; + } + try + { + pItem->maDeckActivationFunctor(pItem->msDeckId); + } + catch(const css::uno::Exception&) {}; + return true; + } + } + } + return false; +} + +VclPtr<RadioButton> TabBar::CreateTabItem(const DeckDescriptor& rDeckDescriptor) +{ + VclPtr<RadioButton> pItem = ControlFactory::CreateTabItem(this); + pItem->SetAccessibleName(rDeckDescriptor.msTitle); + pItem->SetAccessibleDescription(rDeckDescriptor.msHelpText); + pItem->SetHelpText(rDeckDescriptor.msHelpText); + pItem->SetQuickHelpText(rDeckDescriptor.msHelpText); + return pItem; +} + +Image TabBar::GetItemImage(const DeckDescriptor& rDeckDescriptor) const +{ + return Tools::GetImage( + rDeckDescriptor.msIconURL, + rDeckDescriptor.msHighContrastIconURL, + mxFrame); +} + +IMPL_LINK_NOARG(TabBar::Item, HandleClick, Button*, void) +{ + vcl::Window* pFocusWin = Application::GetFocusWindow(); + pFocusWin->GrabFocusToDocument(); + try + { + maDeckActivationFunctor(msDeckId); + } + catch(const css::uno::Exception&) + {} // workaround for #i123198# +} + +OUString const & TabBar::GetDeckIdForIndex (const sal_Int32 nIndex) const +{ + if (nIndex<0 || o3tl::make_unsigned(nIndex)>=maItems.size()) + throw RuntimeException(); + return maItems[nIndex].msDeckId; +} + +void TabBar::ToggleHideFlag (const sal_Int32 nIndex) +{ + if (nIndex<0 || o3tl::make_unsigned(nIndex) >= maItems.size()) + throw RuntimeException(); + + maItems[nIndex].mbIsHidden = ! maItems[nIndex].mbIsHidden; + + std::shared_ptr<DeckDescriptor> xDeckDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(maItems[nIndex].msDeckId); + if (xDeckDescriptor) + { + xDeckDescriptor->mbIsEnabled = ! maItems[nIndex].mbIsHidden; + + Context aContext; + aContext.msApplication = pParentSidebarController->GetCurrentContext().msApplication; + // leave aContext.msContext on default 'any' ... this func is used only for decks + // and we don't have context-sensitive decks anyway + + xDeckDescriptor->maContextList.ToggleVisibilityForContext( + aContext, xDeckDescriptor->mbIsEnabled ); + } + + Layout(); +} + +void TabBar::RestoreHideFlags() +{ + bool bNeedsLayout(false); + for (auto & item : maItems) + { + if (item.mbIsHidden != item.mbIsHiddenByDefault) + { + item.mbIsHidden = item.mbIsHiddenByDefault; + bNeedsLayout = true; + + std::shared_ptr<DeckDescriptor> xDeckDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(item.msDeckId); + if (xDeckDescriptor) + xDeckDescriptor->mbIsEnabled = ! item.mbIsHidden; + + } + } + if (bNeedsLayout) + Layout(); +} + +void TabBar::UpdateFocusManager(FocusManager& rFocusManager) +{ + std::vector<Button*> aButtons; + aButtons.reserve(maItems.size()+1); + + aButtons.push_back(mpMenuButton.get()); + for (auto const& item : maItems) + { + aButtons.push_back(item.mpButton.get()); + } + rFocusManager.SetButtons(aButtons); +} + +IMPL_LINK_NOARG(TabBar, OnToolboxClicked, Button*, void) +{ + if (!mpMenuButton) + return; + + std::vector<DeckMenuData> aMenuData; + + for (auto const& item : maItems) + { + std::shared_ptr<DeckDescriptor> xDeckDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(item.msDeckId); + + if (xDeckDescriptor) + { + DeckMenuData aData; + aData.msDisplayName = xDeckDescriptor->msTitle; + aData.mbIsCurrentDeck = item.mpButton->IsChecked(); + aData.mbIsActive = !item.mbIsHidden; + aData.mbIsEnabled = item.mpButton->IsEnabled(); + + aMenuData.push_back(aData); + } + } + + maPopupMenuProvider( + tools::Rectangle( + mpMenuButton->GetPosPixel(), + mpMenuButton->GetSizePixel()), + aMenuData); + mpMenuButton->Check(false); +} + +void TabBar::EnableMenuButton(const bool bEnable) +{ + mpMenuButton->Enable(bEnable); +} + +} // end of namespace sfx2::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |