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/FocusManager.hxx>
21 #include <sfx2/sidebar/Panel.hxx>
22 #include <sfx2/sidebar/DeckTitleBar.hxx>
23 #include <sfx2/sidebar/PanelTitleBar.hxx>
24 #include <sfx2/sidebar/Tools.hxx>
25 #include <sfx2/sidebar/TitleBar.hxx>
26 #include <vcl/button.hxx>
27 #include <vcl/event.hxx>
28 #include <vcl/toolbox.hxx>
29 #include <vcl/svapp.hxx>
30 #include <toolkit/helper/vclunohelper.hxx>
31
32 #include <sfx2/app.hxx>
33
34 namespace sfx2 { namespace sidebar {
35
FocusLocation(const PanelComponent eComponent,const sal_Int32 nIndex)36 FocusManager::FocusLocation::FocusLocation (const PanelComponent eComponent, const sal_Int32 nIndex)
37 : meComponent(eComponent),
38 mnIndex(nIndex)
39 {
40 }
41
FocusManager(const std::function<void (const Panel &)> & rShowPanelFunctor,const std::function<bool (const sal_Int32)> & rIsDeckOpenFunctor)42 FocusManager::FocusManager(const std::function<void(const Panel&)>& rShowPanelFunctor,
43 const std::function<bool(const sal_Int32)>& rIsDeckOpenFunctor)
44 : mpDeckTitleBar(),
45 maPanels(),
46 maButtons(),
47 maShowPanelFunctor(rShowPanelFunctor),
48 mbIsDeckOpenFunctor(rIsDeckOpenFunctor)
49 {
50 }
51
~FocusManager()52 FocusManager::~FocusManager()
53 {
54 Clear();
55 }
56
GrabFocus()57 void FocusManager::GrabFocus()
58 {
59 FocusDeckTitle();
60 }
61
GrabFocusPanel()62 void FocusManager::GrabFocusPanel()
63 {
64 FocusPanel(0, false);
65 }
66
Clear()67 void FocusManager::Clear()
68 {
69 SetDeckTitle(nullptr);
70 ClearPanels();
71 ClearButtons();
72 }
73
ClearPanels()74 void FocusManager::ClearPanels()
75 {
76 std::vector<VclPtr<Panel> > aPanels;
77 aPanels.swap(maPanels);
78 for (auto const& panel : aPanels)
79 {
80 UnregisterWindow(*panel);
81 if (panel->GetTitleBar())
82 {
83 UnregisterWindow(*panel->GetTitleBar());
84 UnregisterWindow(panel->GetTitleBar()->GetToolBox());
85 }
86
87 panel->RemoveChildEventListener(LINK(this, FocusManager, ChildEventListener));
88 }
89 }
90
ClearButtons()91 void FocusManager::ClearButtons()
92 {
93 std::vector<VclPtr<Button> > aButtons;
94 aButtons.swap(maButtons);
95 for (auto const& button : aButtons)
96 {
97 UnregisterWindow(*button);
98 }
99 }
100
SetDeckTitle(DeckTitleBar * pDeckTitleBar)101 void FocusManager::SetDeckTitle (DeckTitleBar* pDeckTitleBar)
102 {
103 if (mpDeckTitleBar != nullptr)
104 {
105 UnregisterWindow(*mpDeckTitleBar);
106 UnregisterWindow(mpDeckTitleBar->GetToolBox());
107 }
108 mpDeckTitleBar = pDeckTitleBar;
109
110 if (mpDeckTitleBar != nullptr)
111 {
112 RegisterWindow(*mpDeckTitleBar);
113 RegisterWindow(mpDeckTitleBar->GetToolBox());
114 }
115 }
116
SetPanels(const SharedPanelContainer & rPanels)117 void FocusManager::SetPanels (const SharedPanelContainer& rPanels)
118 {
119 ClearPanels();
120 for (auto const& panel : rPanels)
121 {
122 RegisterWindow(*panel);
123 if (panel->GetTitleBar())
124 {
125 RegisterWindow(*panel->GetTitleBar());
126 RegisterWindow(panel->GetTitleBar()->GetToolBox());
127 }
128
129 // Register also as child event listener at the panel.
130 panel->AddChildEventListener(LINK(this, FocusManager, ChildEventListener));
131
132 maPanels.emplace_back(panel.get());
133 }
134 }
135
SetButtons(const::std::vector<Button * > & rButtons)136 void FocusManager::SetButtons (const ::std::vector<Button*>& rButtons)
137 {
138 ClearButtons();
139 for (auto const& button : rButtons)
140 {
141 RegisterWindow(*button);
142 maButtons.emplace_back(button);
143 }
144 }
145
RegisterWindow(vcl::Window & rWindow)146 void FocusManager::RegisterWindow (vcl::Window& rWindow)
147 {
148 rWindow.AddEventListener(LINK(this, FocusManager, WindowEventListener));
149 }
150
UnregisterWindow(vcl::Window & rWindow)151 void FocusManager::UnregisterWindow (vcl::Window& rWindow)
152 {
153 rWindow.RemoveEventListener(LINK(this, FocusManager, WindowEventListener));
154 }
155
GetFocusLocation(const vcl::Window & rWindow) const156 FocusManager::FocusLocation FocusManager::GetFocusLocation (const vcl::Window& rWindow) const
157 {
158 // Check the deck title.
159 if (mpDeckTitleBar != nullptr)
160 {
161 if (mpDeckTitleBar == &rWindow)
162 return FocusLocation(PC_DeckTitle, -1);
163 else if (&mpDeckTitleBar->GetToolBox() == &rWindow)
164 return FocusLocation(PC_DeckToolBox, -1);
165 }
166
167 // Search the panels.
168 for (size_t nIndex = 0; nIndex < maPanels.size(); ++nIndex)
169 {
170 if (maPanels[nIndex] == &rWindow)
171 return FocusLocation(PC_PanelContent, nIndex);
172 VclPtr<TitleBar> pTitleBar = maPanels[nIndex]->GetTitleBar();
173 if (pTitleBar == &rWindow)
174 return FocusLocation(PC_PanelTitle, nIndex);
175 if (pTitleBar!=nullptr && &pTitleBar->GetToolBox()==&rWindow)
176 return FocusLocation(PC_PanelToolBox, nIndex);
177 }
178
179 // Search the buttons.
180 for (size_t nIndex=0; nIndex < maButtons.size(); ++nIndex)
181 {
182 if (maButtons[nIndex] == &rWindow)
183 return FocusLocation(PC_TabBar, nIndex);
184 }
185 return FocusLocation(PC_None, -1);
186 }
187
FocusDeckTitle()188 void FocusManager::FocusDeckTitle()
189 {
190 if (mpDeckTitleBar != nullptr)
191 {
192 if (IsDeckTitleVisible())
193 {
194 mpDeckTitleBar->GrabFocus();
195 }
196 else if (mpDeckTitleBar->GetToolBox().GetItemCount() > 0)
197 {
198 ToolBox& rToolBox = mpDeckTitleBar->GetToolBox();
199 rToolBox.GrabFocus();
200 rToolBox.Invalidate();
201 }
202 else
203 FocusPanel(0, false);
204 }
205 else
206 FocusPanel(0, false);
207 }
208
IsDeckTitleVisible() const209 bool FocusManager::IsDeckTitleVisible() const
210 {
211 return mpDeckTitleBar != nullptr && mpDeckTitleBar->IsVisible();
212 }
213
IsPanelTitleVisible(const sal_Int32 nPanelIndex) const214 bool FocusManager::IsPanelTitleVisible (const sal_Int32 nPanelIndex) const
215 {
216 if (nPanelIndex<0 || nPanelIndex>=static_cast<sal_Int32>(maPanels.size()))
217 return false;
218
219 VclPtr<TitleBar> pTitleBar = maPanels[nPanelIndex]->GetTitleBar();
220 if (!pTitleBar)
221 return false;
222 return pTitleBar->IsVisible();
223 }
224
FocusPanel(const sal_Int32 nPanelIndex,const bool bFallbackToDeckTitle)225 void FocusManager::FocusPanel (
226 const sal_Int32 nPanelIndex,
227 const bool bFallbackToDeckTitle)
228 {
229 if (nPanelIndex<0 || nPanelIndex>=static_cast<sal_Int32>(maPanels.size()))
230 {
231 if (bFallbackToDeckTitle)
232 FocusDeckTitle();
233 return;
234 }
235
236 Panel& rPanel (*maPanels[nPanelIndex]);
237 VclPtr<TitleBar> pTitleBar = rPanel.GetTitleBar();
238 if (pTitleBar && pTitleBar->IsVisible())
239 {
240 rPanel.SetExpanded(true);
241 pTitleBar->GrabFocus();
242 }
243 else if (bFallbackToDeckTitle)
244 {
245 // The panel title is not visible, fall back to the deck
246 // title.
247 // Make sure that the desk title is visible here to prevent a
248 // loop when both the title of panel 0 and the deck title are
249 // not present.
250 if (IsDeckTitleVisible())
251 FocusDeckTitle();
252 else
253 FocusPanelContent(nPanelIndex);
254 }
255 else
256 FocusPanelContent(nPanelIndex);
257
258 if (maShowPanelFunctor)
259 maShowPanelFunctor(rPanel);
260 }
261
FocusPanelContent(const sal_Int32 nPanelIndex)262 void FocusManager::FocusPanelContent (const sal_Int32 nPanelIndex)
263 {
264 if (!maPanels[nPanelIndex]->IsExpanded())
265 maPanels[nPanelIndex]->SetExpanded(true);
266
267 VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(maPanels[nPanelIndex]->GetElementWindow());
268 if (pWindow)
269 pWindow->GrabFocus();
270 }
271
FocusButton(const sal_Int32 nButtonIndex)272 void FocusManager::FocusButton (const sal_Int32 nButtonIndex)
273 {
274 maButtons[nButtonIndex]->GrabFocus();
275 maButtons[nButtonIndex]->Invalidate();
276 }
277
ClickButton(const sal_Int32 nButtonIndex)278 void FocusManager::ClickButton (const sal_Int32 nButtonIndex)
279 {
280 if (mbIsDeckOpenFunctor)
281 {
282 if (!mbIsDeckOpenFunctor(-1) || !mbIsDeckOpenFunctor(nButtonIndex-1))
283 maButtons[nButtonIndex]->Click();
284 }
285 if (nButtonIndex > 0)
286 FocusPanel(0, true);
287 maButtons[nButtonIndex]->GetParent()->Invalidate();
288 }
289
RemoveWindow(vcl::Window & rWindow)290 void FocusManager::RemoveWindow (vcl::Window& rWindow)
291 {
292 auto iPanel (::std::find(maPanels.begin(), maPanels.end(), &rWindow));
293 if (iPanel != maPanels.end())
294 {
295 UnregisterWindow(rWindow);
296 if ((*iPanel)->GetTitleBar() != nullptr)
297 {
298 UnregisterWindow(*(*iPanel)->GetTitleBar());
299 UnregisterWindow((*iPanel)->GetTitleBar()->GetToolBox());
300 }
301 maPanels.erase(iPanel);
302 return;
303 }
304
305 auto iButton (::std::find(maButtons.begin(), maButtons.end(), &rWindow));
306 if (iButton != maButtons.end())
307 {
308 UnregisterWindow(rWindow);
309 maButtons.erase(iButton);
310 return;
311 }
312 }
313
MoveFocusInsidePanel(const FocusLocation & rFocusLocation,const sal_Int32 nDirection)314 void FocusManager::MoveFocusInsidePanel (
315 const FocusLocation& rFocusLocation,
316 const sal_Int32 nDirection)
317 {
318 const bool bHasToolBoxItem (
319 maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetToolBox().GetItemCount() > 0);
320 switch (rFocusLocation.meComponent)
321 {
322 case PC_PanelTitle:
323 if (nDirection > 0 && bHasToolBoxItem)
324 maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetToolBox().GrabFocus();
325 else
326 FocusPanelContent(rFocusLocation.mnIndex);
327 break;
328
329 case PC_PanelToolBox:
330 if (nDirection < 0 && bHasToolBoxItem)
331 maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GrabFocus();
332 else
333 FocusPanelContent(rFocusLocation.mnIndex);
334 break;
335
336 default: break;
337 }
338 }
339
MoveFocusInsideDeckTitle(const FocusLocation & rFocusLocation,const sal_Int32 nDirection)340 void FocusManager::MoveFocusInsideDeckTitle (
341 const FocusLocation& rFocusLocation,
342 const sal_Int32 nDirection)
343 {
344 // Note that when the title bar of the first (and only) panel is
345 // not visible then the deck title takes its place and the focus
346 // is moved between a) deck title, b) deck closer and c) content
347 // of panel 0.
348 const bool bHasToolBoxItem (
349 mpDeckTitleBar->GetToolBox().GetItemCount() > 0);
350 switch (rFocusLocation.meComponent)
351 {
352 case PC_DeckTitle:
353 if (nDirection<0 && ! IsPanelTitleVisible(0))
354 FocusPanelContent(0);
355 else if (bHasToolBoxItem)
356 mpDeckTitleBar->GetToolBox().GrabFocus();
357 break;
358
359 case PC_DeckToolBox:
360 if (nDirection>0 && ! IsPanelTitleVisible(0))
361 FocusPanelContent(0);
362 else
363 mpDeckTitleBar->GrabFocus();
364 break;
365
366 default: break;
367 }
368 }
369
HandleKeyEvent(const vcl::KeyCode & rKeyCode,const vcl::Window & rWindow)370 void FocusManager::HandleKeyEvent (
371 const vcl::KeyCode& rKeyCode,
372 const vcl::Window& rWindow)
373 {
374 const FocusLocation aLocation (GetFocusLocation(rWindow));
375
376 switch (rKeyCode.GetCode())
377 {
378 case KEY_ESCAPE:
379 switch (aLocation.meComponent)
380 {
381 case PC_TabBar:
382 case PC_DeckTitle:
383 case PC_DeckToolBox:
384 case PC_PanelTitle:
385 case PC_PanelToolBox:
386 {
387 vcl::Window* pFocusWin = Application::GetFocusWindow();
388 if (pFocusWin)
389 pFocusWin->GrabFocusToDocument();
390 break;
391 }
392
393 default:
394 break;
395 }
396 return;
397
398 case KEY_SPACE:
399 switch (aLocation.meComponent)
400 {
401 case PC_PanelTitle:
402 // Toggle panel between expanded and collapsed.
403 maPanels[aLocation.mnIndex]->SetExpanded( ! maPanels[aLocation.mnIndex]->IsExpanded());
404 maPanels[aLocation.mnIndex]->GetTitleBar()->Invalidate();
405 break;
406
407 default:
408 break;
409 }
410 return;
411
412 case KEY_RETURN:
413 switch (aLocation.meComponent)
414 {
415 case PC_DeckToolBox:
416 FocusButton(0);
417 break;
418
419 case PC_PanelTitle:
420 // Enter the panel.
421 FocusPanelContent(aLocation.mnIndex);
422 break;
423
424 case PC_TabBar:
425 // Activate the button.
426 ClickButton(aLocation.mnIndex);
427 break;
428
429 default:
430 break;
431 }
432 return;
433
434 case KEY_TAB:
435 {
436 const sal_Int32 nDirection (
437 rKeyCode.IsShift()
438 ? -1
439 : +1);
440 switch (aLocation.meComponent)
441 {
442 case PC_PanelTitle:
443 case PC_PanelToolBox:
444 case PC_PanelContent:
445 MoveFocusInsidePanel(aLocation, nDirection);
446 break;
447
448 case PC_DeckTitle:
449 case PC_DeckToolBox:
450 MoveFocusInsideDeckTitle(aLocation, nDirection);
451 break;
452
453 default:
454 break;
455 }
456 break;
457 }
458
459 case KEY_LEFT:
460 case KEY_UP:
461 switch (aLocation.meComponent)
462 {
463 case PC_PanelTitle:
464 case PC_PanelToolBox:
465 case PC_PanelContent:
466 // Go to previous panel or the deck title.
467 if (aLocation.mnIndex > 0)
468 FocusPanel(aLocation.mnIndex-1, true);
469 else if (IsDeckTitleVisible())
470 FocusDeckTitle();
471 else
472 {
473 // Focus the last button.
474 sal_Int32 nIndex(maButtons.size()-1);
475 while(!maButtons[nIndex]->IsVisible() && --nIndex > 0);
476 FocusButton(nIndex);
477 }
478 break;
479
480 case PC_DeckTitle:
481 case PC_DeckToolBox:
482 {
483 // Focus the last button.
484 sal_Int32 nIndex(maButtons.size()-1);
485 while(!maButtons[nIndex]->IsVisible() && --nIndex > 0);
486 FocusButton(nIndex);
487 break;
488 }
489
490 case PC_TabBar:
491 // Go to previous tab bar item.
492 if (aLocation.mnIndex == 0)
493 FocusPanel(maPanels.size()-1, true);
494 else
495 {
496 sal_Int32 nIndex((aLocation.mnIndex + maButtons.size() - 1) % maButtons.size());
497 while(!maButtons[nIndex]->IsVisible() && --nIndex > 0);
498 FocusButton(nIndex);
499 }
500 break;
501
502 default:
503 break;
504 }
505 break;
506
507 case KEY_RIGHT:
508 case KEY_DOWN:
509 switch(aLocation.meComponent)
510 {
511 case PC_PanelTitle:
512 case PC_PanelToolBox:
513 case PC_PanelContent:
514 // Go to next panel.
515 if (aLocation.mnIndex < static_cast<sal_Int32>(maPanels.size())-1)
516 FocusPanel(aLocation.mnIndex+1, false);
517 else
518 FocusButton(0);
519 break;
520
521 case PC_DeckTitle:
522 case PC_DeckToolBox:
523 // Focus the first panel.
524 if (IsPanelTitleVisible(0))
525 FocusPanel(0, false);
526 else
527 FocusButton(0);
528 break;
529
530 case PC_TabBar:
531 // Go to next tab bar item.
532 if (aLocation.mnIndex < static_cast<sal_Int32>(maButtons.size())-1)
533 {
534 sal_Int32 nIndex(aLocation.mnIndex + 1);
535 while(!maButtons[nIndex]->IsVisible() && ++nIndex < static_cast<sal_Int32>(maButtons.size()));
536 if (nIndex < static_cast<sal_Int32>(maButtons.size()))
537 {
538 FocusButton(nIndex);
539 break;
540 }
541 }
542 if (IsDeckTitleVisible())
543 FocusDeckTitle();
544 else
545 FocusPanel(0, true);
546 break;
547
548 default:
549 break;
550 }
551 break;
552 }
553 }
554
IMPL_LINK(FocusManager,WindowEventListener,VclWindowEvent &,rWindowEvent,void)555 IMPL_LINK(FocusManager, WindowEventListener, VclWindowEvent&, rWindowEvent, void)
556 {
557 vcl::Window* pSource = rWindowEvent.GetWindow();
558 if (pSource == nullptr)
559 return;
560
561 switch (rWindowEvent.GetId())
562 {
563 case VclEventId::WindowKeyInput:
564 {
565 KeyEvent* pKeyEvent = static_cast<KeyEvent*>(rWindowEvent.GetData());
566 HandleKeyEvent(pKeyEvent->GetKeyCode(), *pSource);
567 break;
568 }
569
570 case VclEventId::ObjectDying:
571 RemoveWindow(*pSource);
572 break;
573
574 case VclEventId::WindowGetFocus:
575 case VclEventId::WindowLoseFocus:
576 pSource->Invalidate();
577 break;
578
579 default:
580 break;
581 }
582 }
583
IMPL_LINK(FocusManager,ChildEventListener,VclWindowEvent &,rEvent,void)584 IMPL_LINK(FocusManager, ChildEventListener, VclWindowEvent&, rEvent, void)
585 {
586 vcl::Window* pSource = rEvent.GetWindow();
587 if (pSource == nullptr)
588 return;
589
590 switch (rEvent.GetId())
591 {
592 case VclEventId::WindowKeyInput:
593 {
594 KeyEvent* pKeyEvent = static_cast<KeyEvent*>(rEvent.GetData());
595
596 // Go up the window hierarchy to find out whether the
597 // parent of the event source is known to us.
598 vcl::Window* pWindow = pSource;
599 FocusLocation aLocation (PC_None, -1);
600 while (true)
601 {
602 if (pWindow == nullptr)
603 break;
604 aLocation = GetFocusLocation(*pWindow);
605 if (aLocation.meComponent != PC_None)
606 break;
607 pWindow = pWindow->GetParent();
608 }
609
610 if (aLocation.meComponent != PC_None)
611 {
612 switch (pKeyEvent->GetKeyCode().GetCode())
613 {
614 case KEY_ESCAPE:
615 // Return focus to tab bar sidebar settings button or panel title.
616 if (!IsDeckTitleVisible() && maPanels.size() == 1)
617 FocusButton(0);
618 else
619 FocusPanel(aLocation.mnIndex, true);
620 break;
621
622 default:
623 break;
624 }
625 }
626 return;
627 }
628
629 default:
630 break;
631 }
632 }
633
634 } } // end of namespace sfx2::sidebar
635
636 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
637