use insert function instead of for loop
[LibreOffice.git] / sfx2 / source / sidebar / TabBar.cxx
blob8993b10fd92da8bb5daa9005a01cc5e33828a843
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
30 #include <utility>
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"
40 using namespace css;
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)
54 , mxFrame(rxFrame)
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);
76 UpdateMenus();
78 gDefaultWidth = m_xContainer->get_preferred_size().Width();
80 // we have this widget just so we can measure best width for static TabBar::GetDefaultWidth
81 mxMeasureBox->hide();
83 SetBackground(Wallpaper(Theme::GetColor(Theme::Color_TabBarBackground)));
85 #if OSL_DEBUG_LEVEL >= 2
86 SetText(OUString("TabBar"));
87 #endif
90 TabBar::~TabBar()
92 disposeOnce();
95 void TabBar::dispose()
97 maItems.clear();
98 mxMeasureBox.reset();
99 mxSubMenu.reset();
100 mxMainMenu.reset();
101 mxMenuButton.reset();
102 m_xContainer->move(mxContents.get(), mxTempToplevel.get());
103 mxContents.reset();
104 mxTempToplevel.reset();
105 mxAuxBuilder.reset();
106 InterimItemWindow::dispose();
109 sal_Int32 TabBar::GetDefaultWidth()
111 if (!gDefaultWidth)
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())
124 return;
126 // Remove the current buttons.
127 maItems.clear();
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);
134 continue;
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);
148 UpdateButtonIcons();
149 UpdateMenus();
152 void TabBar::UpdateButtonIcons()
154 for (auto const& item : maItems)
156 std::shared_ptr<DeckDescriptor> xDeckDescriptor = mrParentSidebarController.GetResourceManager()->GetDeckDescriptor(item->msDeckId);
157 if (!xDeckDescriptor)
158 continue;
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);
167 UpdateMenus();
170 void TabBar::RemoveDeckHighlight()
172 for (auto const& item : maItems)
173 item->mxButton->set_item_active(u"toggle"_ustr, false);
174 UpdateMenus();
177 void TabBar::DataChanged(const DataChangedEvent& rDataChangedEvent)
179 SetBackground(Theme::GetColor(Theme::Color_TabBarBackground));
180 UpdateButtonIcons();
181 UpdateMenus();
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())
204 return true;
205 if(pData->GetNotchDelta()<0)
207 if(pItem+1 == maItems.end())
208 return true;
209 ++pItem;
211 else
213 if(pItem == maItems.begin())
214 return true;
215 --pItem;
219 (*pItem)->maDeckActivationFunctor((*pItem)->msDeckId);
220 GrabFocusToDocument();
222 catch(const css::uno::Exception&) {};
223 return true;
227 return false;
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,
247 mxFrame);
250 TabBar::Item::Item(TabBar& rTabBar)
251 : mrTabBar(rTabBar)
252 , mxBuilder(Application::CreateBuilder(rTabBar.GetContainer(), u"sfx/ui/tabbutton.ui"_ustr))
253 , mxButton(mxBuilder->weld_toolbar(u"button"_ustr))
254 , mbIsHidden(false)
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);
294 if (xDeckDescriptor)
296 xDeckDescriptor->mbIsEnabled = ! maItems[nIndex]->mbIsHidden;
298 Context aContext;
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 );
306 UpdateMenus();
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.");
326 return;
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)
351 continue;
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())
364 if (bCurrentDeck)
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,
369 TRISTATE_FALSE);
370 mxSubMenu->set_active(sSubIdent, true);
372 else
374 OUString sSubIdent("customize" + OUString::number(nIndex));
375 mxSubMenu->insert(nIndex, sSubIdent, sDisplayName, nullptr, nullptr, nullptr,
376 TRISTATE_TRUE);
377 mxSubMenu->set_active(sSubIdent, bEnabled && bActive);
381 ++nIndex;
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())
391 bHideLock = false;
392 else
393 bHideUnLock = false;
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: */