summaryrefslogtreecommitdiffstats
path: root/sfx2/source/sidebar/TabBar.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sfx2/source/sidebar/TabBar.cxx')
-rw-r--r--sfx2/source/sidebar/TabBar.cxx376
1 files changed, 376 insertions, 0 deletions
diff --git a/sfx2/source/sidebar/TabBar.cxx b/sfx2/source/sidebar/TabBar.cxx
new file mode 100644
index 000000000..59649ec15
--- /dev/null
+++ b/sfx2/source/sidebar/TabBar.cxx
@@ -0,0 +1,376 @@
+/* -*- 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/DeckDescriptor.hxx>
+#include <sfx2/sidebar/Theme.hxx>
+#include <sidebar/Tools.hxx>
+#include <sfx2/sidebar/FocusManager.hxx>
+#include <sfx2/sidebar/SidebarController.hxx>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <o3tl/safeint.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <svtools/acceleratorexecute.hxx>
+#include <osl/diagnose.h>
+
+using namespace css;
+using namespace css::uno;
+
+static int gDefaultWidth;
+
+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
+ )
+ : InterimItemWindow(pParentWindow, "sfx/ui/tabbar.ui", "TabBar")
+ , mxFrame(rxFrame)
+ , mxAuxBuilder(Application::CreateBuilder(m_xContainer.get(), "sfx/ui/tabbarcontents.ui"))
+ , mxTempToplevel(mxAuxBuilder->weld_box("toplevel"))
+ , mxContents(mxAuxBuilder->weld_widget("TabBarContents"))
+ , mxMenuButton(mxAuxBuilder->weld_menu_button("menubutton"))
+ , mxMainMenu(mxAuxBuilder->weld_menu("mainmenu"))
+ , mxSubMenu(mxAuxBuilder->weld_menu("submenu"))
+ , mxMeasureBox(mxAuxBuilder->weld_widget("measure"))
+ , maDeckActivationFunctor(rDeckActivationFunctor)
+ , maPopupMenuProvider(rPopupMenuProvider)
+ , pParentSidebarController(rParentSidebarController)
+{
+ InitControlBase(mxMenuButton.get());
+
+ mxTempToplevel->move(mxContents.get(), m_xContainer.get());
+
+ gDefaultWidth = m_xContainer->get_preferred_size().Width();
+
+ // we have this widget just so we can measure best width for static TabBar::GetDefaultWidth
+ mxMeasureBox->hide();
+
+ SetBackground(Wallpaper(Theme::GetColor(Theme::Color_TabBarBackground)));
+
+ mxMenuButton->connect_toggled(LINK(this, TabBar, OnToolboxClicked));
+
+#ifdef DEBUG
+ SetText(OUString("TabBar"));
+#endif
+}
+
+TabBar::~TabBar()
+{
+ disposeOnce();
+}
+
+void TabBar::dispose()
+{
+ m_xContainer->move(mxContents.get(), mxTempToplevel.get());
+ maItems.clear();
+ mxMeasureBox.reset();
+ mxSubMenu.reset();
+ mxMainMenu.reset();
+ mxMenuButton.reset();
+ mxAuxBuilder.reset();
+ InterimItemWindow::dispose();
+}
+
+sal_Int32 TabBar::GetDefaultWidth()
+{
+ if (!gDefaultWidth)
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "sfx/ui/tabbarcontents.ui"));
+ std::unique_ptr<weld::Widget> xContainer(xBuilder->weld_widget("TabBarContents"));
+ gDefaultWidth = xContainer->get_preferred_size().Width();
+ }
+ return gDefaultWidth;
+}
+
+void TabBar::SetDecks(const ResourceManager::DeckContextDescriptorContainer& rDecks)
+{
+ // invisible with LOK, so keep empty to avoid invalidations
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+
+ // Remove the current buttons.
+ maItems.clear();
+ for (auto const& deck : rDecks)
+ {
+ std::shared_ptr<DeckDescriptor> xDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(deck.msId);
+ if (xDescriptor == nullptr)
+ {
+ OSL_ASSERT(xDescriptor!=nullptr);
+ continue;
+ }
+
+ maItems.emplace_back(std::make_unique<Item>(*this));
+ auto& xItem(maItems.back());
+ xItem->msDeckId = xDescriptor->msId;
+ CreateTabItem(*xItem->mxButton, *xDescriptor);
+ xItem->mxButton->connect_clicked(LINK(xItem.get(), TabBar::Item, HandleClick));
+ xItem->maDeckActivationFunctor = maDeckActivationFunctor;
+ xItem->mbIsHidden = !xDescriptor->mbIsEnabled;
+ xItem->mbIsHiddenByDefault = xItem->mbIsHidden; // the default is the state while creating
+
+ xItem->mxButton->set_sensitive(deck.mbIsEnabled);
+ }
+
+ UpdateButtonIcons();
+}
+
+void TabBar::UpdateButtonIcons()
+{
+ for (auto const& item : maItems)
+ {
+ std::shared_ptr<DeckDescriptor> xDeckDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(item->msDeckId);
+ if (!xDeckDescriptor)
+ continue;
+ item->mxButton->set_item_image("toggle", GetItemImage(*xDeckDescriptor));
+ }
+}
+
+void TabBar::HighlightDeck(std::u16string_view rsDeckId)
+{
+ for (auto const& item : maItems)
+ item->mxButton->set_item_active("toggle", item->msDeckId == rsDeckId);
+}
+
+void TabBar::RemoveDeckHighlight()
+{
+ for (auto const& item : maItems)
+ item->mxButton->set_item_active("toggle", false);
+}
+
+void TabBar::DataChanged(const DataChangedEvent& rDataChangedEvent)
+{
+ SetBackground(Theme::GetColor(Theme::Color_TabBarBackground));
+ UpdateButtonIcons();
+
+ InterimItemWindow::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 InterimItemWindow::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(),
+ [] (const auto& item) { return item->mxButton->get_item_active("toggle"); });
+ 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;
+}
+
+void TabBar::CreateTabItem(weld::Toolbar& rItem, const DeckDescriptor& rDeckDescriptor)
+{
+ rItem.set_accessible_name(rDeckDescriptor.msTitle);
+ rItem.set_accessible_description(rDeckDescriptor.msHelpText);
+ rItem.set_tooltip_text(rDeckDescriptor.msHelpText);
+ const OUString sCommand = ".uno:SidebarDeck." + rDeckDescriptor.msId;
+ OUString sShortcut = vcl::CommandInfoProvider::GetCommandShortcut(sCommand, mxFrame);
+ if (!sShortcut.isEmpty())
+ sShortcut = u" (" + sShortcut + u")";
+ rItem.set_item_tooltip_text("toggle", rDeckDescriptor.msHelpText + sShortcut);
+}
+
+css::uno::Reference<css::graphic::XGraphic> TabBar::GetItemImage(const DeckDescriptor& rDeckDescriptor) const
+{
+ return Tools::GetImage(
+ rDeckDescriptor.msIconURL,
+ rDeckDescriptor.msHighContrastIconURL,
+ mxFrame);
+}
+
+TabBar::Item::Item(TabBar& rTabBar)
+ : mrTabBar(rTabBar)
+ , mxBuilder(Application::CreateBuilder(rTabBar.GetContainer(), "sfx/ui/tabbutton.ui"))
+ , mxButton(mxBuilder->weld_toolbar("button"))
+ , mbIsHidden(false)
+ , mbIsHiddenByDefault(false)
+{
+}
+
+TabBar::Item::~Item()
+{
+ mrTabBar.GetContainer()->move(mxButton.get(), nullptr);
+}
+
+IMPL_LINK_NOARG(TabBar::Item, HandleClick, const OString&, void)
+{
+ // tdf#143146 copy the functor and arg before calling
+ // GrabFocusToDocument which may destroy this object
+ auto aDeckActivationFunctor = maDeckActivationFunctor;
+ auto sDeckId = msDeckId;
+
+ mrTabBar.GrabFocusToDocument();
+ try
+ {
+ aDeckActivationFunctor(sDeckId);
+ }
+ 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 );
+ }
+}
+
+void TabBar::RestoreHideFlags()
+{
+ for (auto & item : maItems)
+ {
+ if (item->mbIsHidden != item->mbIsHiddenByDefault)
+ {
+ item->mbIsHidden = item->mbIsHiddenByDefault;
+ std::shared_ptr<DeckDescriptor> xDeckDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(item->msDeckId);
+ if (xDeckDescriptor)
+ xDeckDescriptor->mbIsEnabled = !item->mbIsHidden;
+
+ }
+ }
+}
+
+void TabBar::UpdateFocusManager(FocusManager& rFocusManager)
+{
+ std::vector<weld::Widget*> aButtons;
+ aButtons.reserve(maItems.size()+1);
+ aButtons.push_back(mxMenuButton.get());
+ for (auto const& item : maItems)
+ {
+ aButtons.push_back(item->mxButton.get());
+ }
+ rFocusManager.SetButtons(aButtons);
+}
+
+IMPL_LINK_NOARG(TabBar, OnToolboxClicked, weld::Toggleable&, void)
+{
+ if (!mxMenuButton->get_active())
+ return;
+
+ std::vector<DeckMenuData> aMenuData;
+
+ for (auto const& item : maItems)
+ {
+ std::shared_ptr<DeckDescriptor> xDeckDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(item->msDeckId);
+
+ if (!xDeckDescriptor)
+ continue;
+
+ DeckMenuData aData;
+ aData.msDisplayName = xDeckDescriptor->msTitle;
+ aData.mbIsCurrentDeck = item->mxButton->get_item_active("toggle");
+ aData.mbIsActive = !item->mbIsHidden;
+ aData.mbIsEnabled = item->mxButton->get_sensitive();
+ aMenuData.push_back(aData);
+ }
+
+ for (int i = mxMainMenu->n_children() - 1; i >= 0; --i)
+ {
+ OString sIdent = mxMainMenu->get_id(i);
+ if (sIdent.startsWith("select"))
+ mxMainMenu->remove(sIdent);
+ }
+ for (int i = mxSubMenu->n_children() - 1; i >= 0; --i)
+ {
+ OString sIdent = mxSubMenu->get_id(i);
+ if (sIdent.indexOf("customize") != -1)
+ mxSubMenu->remove(sIdent);
+ }
+
+ maPopupMenuProvider(*mxMainMenu, *mxSubMenu, aMenuData);
+}
+
+void TabBar::EnableMenuButton(const bool bEnable)
+{
+ mxMenuButton->set_sensitive(bEnable);
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */