1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sfx2/sidebar/TabBar.hxx>
21 #include <sidebar/DeckDescriptor.hxx>
22 #include <sfx2/sidebar/Theme.hxx>
23 #include <sidebar/Tools.hxx>
24 #include <sfx2/sidebar/FocusManager.hxx>
25 #include <sfx2/sidebar/SidebarController.hxx>
27 #include <comphelper/lok.hxx>
28 #include <comphelper/processfactory.hxx>
29 #include <o3tl/safeint.hxx>
31 #include <vcl/commandevent.hxx>
32 #include <vcl/commandinfoprovider.hxx>
33 #include <vcl/event.hxx>
34 #include <vcl/svapp.hxx>
35 #include <svtools/acceleratorexecute.hxx>
36 #include <osl/diagnose.h>
38 #include "uiobject.hxx"
41 using namespace css::uno
;
43 static int gDefaultWidth
;
45 namespace sfx2::sidebar
{
47 TabBar::TabBar(vcl::Window
* pParentWindow
,
48 const Reference
<frame::XFrame
>& rxFrame
,
49 std::function
<void (const OUString
&)> aDeckActivationFunctor
,
50 PopupMenuSignalConnectFunction aPopupMenuSignalConnectFunction
,
51 SidebarController
& rParentSidebarController
53 : InterimItemWindow(pParentWindow
, u
"sfx/ui/tabbar.ui"_ustr
, u
"TabBar"_ustr
)
55 , mxAuxBuilder(Application::CreateBuilder(m_xContainer
.get(), u
"sfx/ui/tabbarcontents.ui"_ustr
))
56 , mxTempToplevel(mxAuxBuilder
->weld_box(u
"toplevel"_ustr
))
57 , mxContents(mxAuxBuilder
->weld_widget(u
"TabBarContents"_ustr
))
58 , mxMeasureBox(mxAuxBuilder
->weld_widget(u
"measure"_ustr
))
59 , maDeckActivationFunctor(std::move(aDeckActivationFunctor
))
60 , mrParentSidebarController(rParentSidebarController
)
62 set_id(u
"TabBar"_ustr
); // for uitest
64 InitControlBase(mxMenuButton
.get());
66 mxTempToplevel
->move(mxContents
.get(), m_xContainer
.get());
68 // For Gtk4 defer menu_button until after the contents have been
69 // transferred to its final home (where the old parent is a GtkWindow to
70 // support loading the accelerators in the menu for Gtk3)
71 mxMenuButton
= mxAuxBuilder
->weld_menu_button(u
"menubutton"_ustr
);
72 mxMainMenu
= mxAuxBuilder
->weld_menu(u
"mainmenu"_ustr
);
73 mxSubMenu
= mxAuxBuilder
->weld_menu(u
"submenu"_ustr
);
74 aPopupMenuSignalConnectFunction(*mxMainMenu
, *mxSubMenu
);
78 gDefaultWidth
= m_xContainer
->get_preferred_size().Width();
80 // we have this widget just so we can measure best width for static TabBar::GetDefaultWidth
83 SetBackground(Wallpaper(Theme::GetColor(Theme::Color_TabBarBackground
)));
85 #if OSL_DEBUG_LEVEL >= 2
86 SetText(OUString("TabBar"));
95 void TabBar::dispose()
101 mxMenuButton
.reset();
102 m_xContainer
->move(mxContents
.get(), mxTempToplevel
.get());
104 mxTempToplevel
.reset();
105 mxAuxBuilder
.reset();
106 InterimItemWindow::dispose();
109 sal_Int32
TabBar::GetDefaultWidth()
113 std::unique_ptr
<weld::Builder
> xBuilder(Application::CreateBuilder(nullptr, u
"sfx/ui/tabbarcontents.ui"_ustr
));
114 std::unique_ptr
<weld::Widget
> xContainer(xBuilder
->weld_widget(u
"TabBarContents"_ustr
));
115 gDefaultWidth
= xContainer
->get_preferred_size().Width();
117 return gDefaultWidth
;
120 void TabBar::SetDecks(const ResourceManager::DeckContextDescriptorContainer
& rDecks
)
122 // invisible with LOK, so keep empty to avoid invalidations
123 if (comphelper::LibreOfficeKit::isActive())
126 // Remove the current buttons.
128 for (auto const& deck
: rDecks
)
130 std::shared_ptr
<DeckDescriptor
> xDescriptor
= mrParentSidebarController
.GetResourceManager()->GetDeckDescriptor(deck
.msId
);
131 if (xDescriptor
== nullptr)
133 OSL_ASSERT(xDescriptor
!=nullptr);
137 maItems
.emplace_back(std::make_unique
<Item
>(*this));
138 auto& xItem(maItems
.back());
139 xItem
->msDeckId
= xDescriptor
->msId
;
140 CreateTabItem(*xItem
->mxButton
, *xDescriptor
);
141 xItem
->mxButton
->connect_clicked(LINK(xItem
.get(), TabBar::Item
, HandleClick
));
142 xItem
->maDeckActivationFunctor
= maDeckActivationFunctor
;
143 xItem
->mbIsHidden
= !xDescriptor
->mbIsEnabled
;
145 xItem
->mxButton
->set_visible(deck
.mbIsEnabled
);
152 void TabBar::UpdateButtonIcons()
154 for (auto const& item
: maItems
)
156 std::shared_ptr
<DeckDescriptor
> xDeckDescriptor
= mrParentSidebarController
.GetResourceManager()->GetDeckDescriptor(item
->msDeckId
);
157 if (!xDeckDescriptor
)
159 item
->mxButton
->set_item_image(u
"toggle"_ustr
, GetItemImage(*xDeckDescriptor
));
163 void TabBar::HighlightDeck(std::u16string_view rsDeckId
)
165 for (auto const& item
: maItems
)
166 item
->mxButton
->set_item_active(u
"toggle"_ustr
, item
->msDeckId
== rsDeckId
);
170 void TabBar::RemoveDeckHighlight()
172 for (auto const& item
: maItems
)
173 item
->mxButton
->set_item_active(u
"toggle"_ustr
, false);
177 void TabBar::DataChanged(const DataChangedEvent
& rDataChangedEvent
)
179 SetBackground(Theme::GetColor(Theme::Color_TabBarBackground
));
183 InterimItemWindow::DataChanged(rDataChangedEvent
);
186 bool TabBar::EventNotify(NotifyEvent
& rEvent
)
188 NotifyEventType nType
= rEvent
.GetType();
189 if(NotifyEventType::KEYINPUT
== nType
)
191 return InterimItemWindow::EventNotify(rEvent
);
193 else if(NotifyEventType::COMMAND
== nType
)
195 const CommandEvent
& rCommandEvent
= *rEvent
.GetCommandEvent();
196 if(rCommandEvent
.GetCommand() == CommandEventId::Wheel
)
198 const CommandWheelData
* pData
= rCommandEvent
.GetWheelData();
199 if(!pData
->GetModifier() && (pData
->GetMode() == CommandWheelMode::SCROLL
))
201 auto pItem
= std::find_if(maItems
.begin(), maItems
.end(),
202 [] (const auto& item
) { return item
->mxButton
->get_item_active("toggle"); });
203 if(pItem
== maItems
.end())
205 if(pData
->GetNotchDelta()<0)
207 if(pItem
+1 == maItems
.end())
213 if(pItem
== maItems
.begin())
219 (*pItem
)->maDeckActivationFunctor((*pItem
)->msDeckId
);
220 GrabFocusToDocument();
222 catch(const css::uno::Exception
&) {};
230 void TabBar::CreateTabItem(weld::Toolbar
& rItem
, const DeckDescriptor
& rDeckDescriptor
)
232 rItem
.set_accessible_name(rDeckDescriptor
.msTitle
);
233 rItem
.set_accessible_description(rDeckDescriptor
.msHelpText
);
234 rItem
.set_tooltip_text(rDeckDescriptor
.msHelpText
);
235 const OUString sCommand
= ".uno:SidebarDeck." + rDeckDescriptor
.msId
;
236 OUString sShortcut
= vcl::CommandInfoProvider::GetCommandShortcut(sCommand
, mxFrame
);
237 if (!sShortcut
.isEmpty())
238 sShortcut
= u
" (" + sShortcut
+ u
")";
239 rItem
.set_item_tooltip_text(u
"toggle"_ustr
, rDeckDescriptor
.msHelpText
+ sShortcut
);
242 css::uno::Reference
<css::graphic::XGraphic
> TabBar::GetItemImage(const DeckDescriptor
& rDeckDescriptor
) const
244 return Tools::GetImage(
245 rDeckDescriptor
.msIconURL
,
246 rDeckDescriptor
.msHighContrastIconURL
,
250 TabBar::Item::Item(TabBar
& rTabBar
)
252 , mxBuilder(Application::CreateBuilder(rTabBar
.GetContainer(), u
"sfx/ui/tabbutton.ui"_ustr
))
253 , mxButton(mxBuilder
->weld_toolbar(u
"button"_ustr
))
258 TabBar::Item::~Item()
260 mrTabBar
.GetContainer()->move(mxButton
.get(), nullptr);
263 IMPL_LINK_NOARG(TabBar::Item
, HandleClick
, const OUString
&, void)
265 // tdf#143146 copy the functor and arg before calling
266 // GrabFocusToDocument which may destroy this object
267 DeckActivationFunctor aDeckActivationFunctor
= maDeckActivationFunctor
;
268 auto sDeckId
= msDeckId
;
270 mrTabBar
.GrabFocusToDocument();
273 aDeckActivationFunctor(sDeckId
);
275 catch(const css::uno::Exception
&)
276 {} // workaround for #i123198#
279 OUString
const & TabBar::GetDeckIdForIndex (const sal_Int32 nIndex
) const
281 if (nIndex
<0 || o3tl::make_unsigned(nIndex
)>=maItems
.size())
282 throw RuntimeException();
283 return maItems
[nIndex
]->msDeckId
;
286 void TabBar::ToggleHideFlag (const sal_Int32 nIndex
)
288 if (nIndex
<0 || o3tl::make_unsigned(nIndex
) >= maItems
.size())
289 throw RuntimeException();
291 maItems
[nIndex
]->mbIsHidden
= ! maItems
[nIndex
]->mbIsHidden
;
293 std::shared_ptr
<DeckDescriptor
> xDeckDescriptor
= mrParentSidebarController
.GetResourceManager()->GetDeckDescriptor(maItems
[nIndex
]->msDeckId
);
296 xDeckDescriptor
->mbIsEnabled
= ! maItems
[nIndex
]->mbIsHidden
;
299 aContext
.msApplication
= mrParentSidebarController
.GetCurrentContext().msApplication
;
300 // leave aContext.msContext on default 'any' ... this func is used only for decks
301 // and we don't have context-sensitive decks anyway
303 xDeckDescriptor
->maContextList
.ToggleVisibilityForContext(
304 aContext
, xDeckDescriptor
->mbIsEnabled
);
309 void TabBar::UpdateFocusManager(FocusManager
& rFocusManager
)
311 std::vector
<weld::Widget
*> aButtons
;
312 aButtons
.reserve(maItems
.size()+1);
313 aButtons
.push_back(mxMenuButton
.get());
314 for (auto const& item
: maItems
)
316 aButtons
.push_back(item
->mxButton
.get());
318 rFocusManager
.SetButtons(aButtons
);
321 void TabBar::UpdateMenus()
323 if (Application::GetToolkitName() == u
"gtk4"_ustr
)
325 SAL_WARN("sfx", "Skipping update of sidebar menus to avoid crash due to gtk4 menu brokenness.");
329 for (int i
= mxMainMenu
->n_children() - 1; i
>= 0; --i
)
331 OUString sIdent
= mxMainMenu
->get_id(i
);
332 if (sIdent
.startsWith("select"))
333 mxMainMenu
->remove(sIdent
);
335 for (int i
= mxSubMenu
->n_children() - 1; i
>= 0; --i
)
337 OUString sIdent
= mxSubMenu
->get_id(i
);
338 if (sIdent
.indexOf("customize") != -1)
339 mxSubMenu
->remove(sIdent
);
342 // Add one entry for every tool panel element to individually make
343 // them visible or hide them.
344 sal_Int32
nIndex (0);
345 for (auto const& rItem
: maItems
)
347 std::shared_ptr
<DeckDescriptor
> xDeckDescriptor
348 = mrParentSidebarController
.GetResourceManager()->GetDeckDescriptor(rItem
->msDeckId
);
350 if (!xDeckDescriptor
)
353 const OUString sDisplayName
= xDeckDescriptor
->msTitle
;
354 OUString
sIdent("select" + OUString::number(nIndex
));
355 const bool bCurrentDeck
= rItem
->mxButton
->get_item_active(u
"toggle"_ustr
);
356 const bool bActive
= !rItem
->mbIsHidden
;
357 const bool bEnabled
= rItem
->mxButton
->get_visible();
358 mxMainMenu
->insert(nIndex
, sIdent
, sDisplayName
, nullptr, nullptr, nullptr, TRISTATE_FALSE
);
359 mxMainMenu
->set_active(sIdent
, bCurrentDeck
);
360 mxMainMenu
->set_sensitive(sIdent
, bEnabled
&& bActive
);
362 if (!comphelper::LibreOfficeKit::isActive())
366 // Don't allow the currently visible deck to be disabled.
367 OUString
sSubIdent("nocustomize" + OUString::number(nIndex
));
368 mxSubMenu
->insert(nIndex
, sSubIdent
, sDisplayName
, nullptr, nullptr, nullptr,
370 mxSubMenu
->set_active(sSubIdent
, true);
374 OUString
sSubIdent("customize" + OUString::number(nIndex
));
375 mxSubMenu
->insert(nIndex
, sSubIdent
, sDisplayName
, nullptr, nullptr, nullptr,
377 mxSubMenu
->set_active(sSubIdent
, bEnabled
&& bActive
);
384 bool bHideLock
= true;
385 bool bHideUnLock
= true;
386 // LOK doesn't support docked/undocked; Sidebar is floating but rendered docked in browser.
387 if (!comphelper::LibreOfficeKit::isActive())
389 // Add entry for docking or un-docking the tool panel.
390 if (!mrParentSidebarController
.IsDocked())
395 mxMainMenu
->set_visible(u
"locktaskpanel"_ustr
, !bHideLock
);
396 mxMainMenu
->set_visible(u
"unlocktaskpanel"_ustr
, !bHideUnLock
);
398 // No Restore or Customize options for LoKit.
399 mxMainMenu
->set_visible(u
"customization"_ustr
, !comphelper::LibreOfficeKit::isActive());
402 void TabBar::EnableMenuButton(const bool bEnable
)
404 mxMenuButton
->set_sensitive(bEnable
);
407 FactoryFunction
TabBar::GetUITestFactory() const
409 return TabBarUIObject::create
;
412 } // end of namespace sfx2::sidebar
414 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */