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