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