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