Branch libreoffice-5-0-4
[LibreOffice.git] / sfx2 / source / sidebar / FocusManager.cxx
blob16452fe3a4dae4b3a7f5cfa97adfc7b320c798e2
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 "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>
30 namespace sfx2 { namespace sidebar {
32 FocusManager::FocusLocation::FocusLocation (const PanelComponent eComponent, const sal_Int32 nIndex)
33 : meComponent(eComponent),
34 mnIndex(nIndex)
38 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 mpLastFocusedWindow(NULL)
49 FocusManager::~FocusManager()
51 Clear();
54 void FocusManager::GrabFocus()
56 FocusDeckTitle();
59 void FocusManager::Clear()
61 SetDeckTitle(NULL);
62 ClearPanels();
63 ClearButtons();
66 void FocusManager::ClearPanels()
68 std::vector<VclPtr<Panel> > aPanels;
69 aPanels.swap(maPanels);
70 for (auto iPanel(aPanels.begin()),iEnd(aPanels.end()); iPanel != iEnd; ++iPanel)
72 UnregisterWindow(**iPanel);
73 if ((*iPanel)->GetTitleBar() != NULL)
75 UnregisterWindow(*(*iPanel)->GetTitleBar());
76 UnregisterWindow((*iPanel)->GetTitleBar()->GetToolBox());
79 (*iPanel)->RemoveChildEventListener(LINK(this, FocusManager, ChildEventListener));
83 void FocusManager::ClearButtons()
85 std::vector<VclPtr<Button> > aButtons;
86 aButtons.swap(maButtons);
87 for (auto iButton = aButtons.begin(); iButton != aButtons.end(); ++iButton)
89 UnregisterWindow(**iButton);
93 void FocusManager::SetDeckTitle (DeckTitleBar* pDeckTitleBar)
95 if (mpDeckTitleBar != nullptr)
97 UnregisterWindow(*mpDeckTitleBar);
98 UnregisterWindow(mpDeckTitleBar->GetToolBox());
100 mpDeckTitleBar = pDeckTitleBar;
102 if (mpDeckTitleBar != nullptr)
104 RegisterWindow(*mpDeckTitleBar);
105 RegisterWindow(mpDeckTitleBar->GetToolBox());
109 void FocusManager::SetPanels (const SharedPanelContainer& rPanels)
111 ClearPanels();
112 for (auto iPanel = rPanels.begin(); iPanel != rPanels.end(); ++iPanel)
114 RegisterWindow(**iPanel);
115 if ((*iPanel)->GetTitleBar() != NULL)
117 RegisterWindow(*(*iPanel)->GetTitleBar());
118 RegisterWindow((*iPanel)->GetTitleBar()->GetToolBox());
121 // Register also as child event listener at the panel.
122 (*iPanel)->AddChildEventListener(LINK(this, FocusManager, ChildEventListener));
124 maPanels.push_back(iPanel->get());
128 void FocusManager::SetButtons (const ::std::vector<Button*>& rButtons)
130 ClearButtons();
131 for (auto iButton = rButtons.begin(); iButton != rButtons.end(); ++iButton)
133 RegisterWindow(**iButton);
134 maButtons.push_back(*iButton);
138 void FocusManager::RegisterWindow (vcl::Window& rWindow)
140 rWindow.AddEventListener(LINK(this, FocusManager, WindowEventListener));
143 void FocusManager::UnregisterWindow (vcl::Window& rWindow)
145 rWindow.RemoveEventListener(LINK(this, FocusManager, WindowEventListener));
148 FocusManager::FocusLocation FocusManager::GetFocusLocation (const vcl::Window& rWindow) const
150 // Check the deck title.
151 if (mpDeckTitleBar != nullptr)
153 if (mpDeckTitleBar == &rWindow)
154 return FocusLocation(PC_DeckTitle, -1);
155 else if (&mpDeckTitleBar->GetToolBox() == &rWindow)
156 return FocusLocation(PC_DeckToolBox, -1);
159 // Search the panels.
160 for (size_t nIndex = 0; nIndex < maPanels.size(); ++nIndex)
162 if (maPanels[nIndex] == &rWindow)
163 return FocusLocation(PC_PanelContent, nIndex);
164 TitleBar* pTitleBar = maPanels[nIndex]->GetTitleBar();
165 if (pTitleBar == &rWindow)
166 return FocusLocation(PC_PanelTitle, nIndex);
167 if (pTitleBar!=NULL && &pTitleBar->GetToolBox()==&rWindow)
168 return FocusLocation(PC_PanelToolBox, nIndex);
171 // Search the buttons.
172 for (size_t nIndex=0; nIndex < maButtons.size(); ++nIndex)
174 if (maButtons[nIndex] == &rWindow)
175 return FocusLocation(PC_TabBar, nIndex);
177 return FocusLocation(PC_None, -1);
180 void FocusManager::FocusDeckTitle()
182 if (mpDeckTitleBar != nullptr)
184 if (IsDeckTitleVisible())
186 mpDeckTitleBar->GrabFocus();
188 else if (mpDeckTitleBar->GetToolBox().GetItemCount() > 0)
190 ToolBox& rToolBox = mpDeckTitleBar->GetToolBox();
191 rToolBox.GrabFocus();
192 rToolBox.Invalidate();
194 else
195 FocusPanel(0, false);
197 else
198 FocusPanel(0, false);
201 bool FocusManager::IsDeckTitleVisible() const
203 return mpDeckTitleBar != nullptr && mpDeckTitleBar->IsVisible();
206 bool FocusManager::IsPanelTitleVisible (const sal_Int32 nPanelIndex) const
208 if (nPanelIndex<0 || nPanelIndex>=static_cast<sal_Int32>(maPanels.size()))
209 return false;
211 TitleBar* pTitleBar = maPanels[nPanelIndex]->GetTitleBar();
212 if (pTitleBar==NULL)
213 return false;
214 return pTitleBar->IsVisible();
217 void FocusManager::FocusPanel (
218 const sal_Int32 nPanelIndex,
219 const bool bFallbackToDeckTitle)
221 if (nPanelIndex<0 || nPanelIndex>=static_cast<sal_Int32>(maPanels.size()))
223 if (bFallbackToDeckTitle)
224 FocusDeckTitle();
225 return;
228 Panel& rPanel (*maPanels[nPanelIndex]);
229 TitleBar* pTitleBar = rPanel.GetTitleBar();
230 if (pTitleBar!=NULL && pTitleBar->IsVisible())
232 rPanel.SetExpanded(true);
233 pTitleBar->GrabFocus();
235 else if (bFallbackToDeckTitle)
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 if (IsDeckTitleVisible())
243 FocusDeckTitle();
244 else
245 FocusPanelContent(nPanelIndex);
247 else
248 FocusPanelContent(nPanelIndex);
250 if (maShowPanelFunctor)
251 maShowPanelFunctor(rPanel);
254 void FocusManager::FocusPanelContent (const sal_Int32 nPanelIndex)
256 vcl::Window* pWindow = VCLUnoHelper::GetWindow(maPanels[nPanelIndex]->GetElementWindow());
257 if (pWindow != NULL)
259 mbObservingContentControlFocus = true;
260 pWindow->GrabFocus();
261 mbObservingContentControlFocus = false;
265 void FocusManager::FocusButton (const sal_Int32 nButtonIndex)
267 maButtons[nButtonIndex]->GrabFocus();
268 maButtons[nButtonIndex]->Invalidate();
271 void FocusManager::ClickButton (const sal_Int32 nButtonIndex)
273 maButtons[nButtonIndex]->Click();
274 if (nButtonIndex > 0)
275 if ( ! maPanels.empty())
276 FocusPanel(0, true);
277 maButtons[nButtonIndex]->GetParent()->Invalidate();
280 void FocusManager::RemoveWindow (vcl::Window& rWindow)
282 auto iPanel (::std::find(maPanels.begin(), maPanels.end(), &rWindow));
283 if (iPanel != maPanels.end())
285 UnregisterWindow(rWindow);
286 if ((*iPanel)->GetTitleBar() != NULL)
288 UnregisterWindow(*(*iPanel)->GetTitleBar());
289 UnregisterWindow((*iPanel)->GetTitleBar()->GetToolBox());
291 maPanels.erase(iPanel);
292 return;
295 auto iButton (::std::find(maButtons.begin(), maButtons.end(), &rWindow));
296 if (iButton != maButtons.end())
298 UnregisterWindow(rWindow);
299 maButtons.erase(iButton);
300 return;
304 bool FocusManager::MoveFocusInsidePanel (
305 const FocusLocation& rFocusLocation,
306 const sal_Int32 nDirection)
308 const bool bHasToolBoxItem (
309 maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetToolBox().GetItemCount() > 0);
310 switch (rFocusLocation.meComponent)
312 case PC_PanelTitle:
313 if (nDirection > 0 && bHasToolBoxItem)
314 maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetToolBox().GrabFocus();
315 else
316 FocusPanelContent(rFocusLocation.mnIndex);
317 return true;
319 case PC_PanelToolBox:
320 if (nDirection < 0 && bHasToolBoxItem)
321 maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GrabFocus();
322 else
323 FocusPanelContent(rFocusLocation.mnIndex);
324 return true;
326 default:
327 return false;
331 bool FocusManager::MoveFocusInsideDeckTitle (
332 const FocusLocation& rFocusLocation,
333 const sal_Int32 nDirection)
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 mpDeckTitleBar->GetToolBox().GetItemCount() > 0);
341 switch (rFocusLocation.meComponent)
343 case PC_DeckTitle:
344 if (nDirection<0 && ! IsPanelTitleVisible(0))
345 FocusPanelContent(0);
346 else if (bHasToolBoxItem)
347 mpDeckTitleBar->GetToolBox().GrabFocus();
348 return true;
350 case PC_DeckToolBox:
351 if (nDirection>0 && ! IsPanelTitleVisible(0))
352 FocusPanelContent(0);
353 else
354 mpDeckTitleBar->GrabFocus();
355 return true;
357 default:
358 return false;
362 void FocusManager::HandleKeyEvent (
363 const vcl::KeyCode& rKeyCode,
364 const vcl::Window& rWindow)
366 const FocusLocation aLocation (GetFocusLocation(rWindow));
367 mpLastFocusedWindow = NULL;
369 switch (rKeyCode.GetCode())
371 case KEY_SPACE:
372 switch (aLocation.meComponent)
374 case PC_PanelTitle:
375 // Toggle panel between expanded and collapsed.
376 maPanels[aLocation.mnIndex]->SetExpanded( ! maPanels[aLocation.mnIndex]->IsExpanded());
377 break;
379 case PC_TabBar:
380 // Activate the button.
381 ClickButton(aLocation.mnIndex);
382 break;
384 default:
385 break;
387 return;
389 case KEY_RETURN:
390 switch (aLocation.meComponent)
392 case PC_DeckToolBox:
393 FocusButton(0);
394 break;
396 case PC_PanelTitle:
397 // Enter the panel.
398 FocusPanelContent(aLocation.mnIndex);
399 break;
401 case PC_TabBar:
402 // Activate the button.
403 ClickButton(aLocation.mnIndex);
404 break;
406 default:
407 break;
409 return;
411 case KEY_TAB:
413 const sal_Int32 nDirection (
414 rKeyCode.IsShift()
415 ? -1
416 : +1);
417 switch (aLocation.meComponent)
419 case PC_PanelTitle:
420 case PC_PanelToolBox:
421 case PC_PanelContent:
422 MoveFocusInsidePanel(aLocation, nDirection);
423 break;
425 case PC_DeckTitle:
426 case PC_DeckToolBox:
427 MoveFocusInsideDeckTitle(aLocation, nDirection);
428 break;
430 default:
431 break;
433 break;
436 case KEY_LEFT:
437 case KEY_UP:
438 switch (aLocation.meComponent)
440 case PC_PanelTitle:
441 case PC_PanelToolBox:
442 case PC_PanelContent:
443 // Go to previous panel or the deck title.
444 if (aLocation.mnIndex > 0)
445 FocusPanel(aLocation.mnIndex-1, true);
446 else if (IsDeckTitleVisible())
447 FocusDeckTitle();
448 else
449 FocusButton(maButtons.size()-1);
450 break;
452 case PC_DeckTitle:
453 case PC_DeckToolBox:
454 // Focus the last button.
455 FocusButton(maButtons.size()-1);
456 break;
458 case PC_TabBar:
459 // Go to previous tab bar item.
460 if (aLocation.mnIndex == 0)
461 FocusPanel(maPanels.size()-1, true);
462 else
463 FocusButton((aLocation.mnIndex + maButtons.size() - 1) % maButtons.size());
464 break;
466 default:
467 break;
469 break;
471 case KEY_RIGHT:
472 case KEY_DOWN:
473 switch(aLocation.meComponent)
475 case PC_PanelTitle:
476 case PC_PanelToolBox:
477 case PC_PanelContent:
478 // Go to next panel.
479 if (aLocation.mnIndex < static_cast<sal_Int32>(maPanels.size())-1)
480 FocusPanel(aLocation.mnIndex+1, false);
481 else
482 FocusButton(0);
483 break;
485 case PC_DeckTitle:
486 case PC_DeckToolBox:
487 // Focus the first panel.
488 if (IsPanelTitleVisible(0))
489 FocusPanel(0, false);
490 else
491 FocusButton(0);
492 break;
494 case PC_TabBar:
495 // Go to next tab bar item.
496 if (aLocation.mnIndex < static_cast<sal_Int32>(maButtons.size())-1)
497 FocusButton(aLocation.mnIndex + 1);
498 else if (IsDeckTitleVisible())
499 FocusDeckTitle();
500 else
501 FocusPanel(0, true);
502 break;
504 default:
505 break;
507 break;
511 IMPL_LINK(FocusManager, WindowEventListener, VclSimpleEvent*, pEvent)
513 if (pEvent == NULL)
514 return 0;
516 if ( ! pEvent->ISA(VclWindowEvent))
517 return 0;
519 VclWindowEvent* pWindowEvent = static_cast<VclWindowEvent*>(pEvent);
520 vcl::Window* pSource = pWindowEvent->GetWindow();
521 if (pSource == NULL)
522 return 0;
524 switch (pWindowEvent->GetId())
526 case VCLEVENT_WINDOW_KEYINPUT:
528 KeyEvent* pKeyEvent = static_cast<KeyEvent*>(pWindowEvent->GetData());
529 HandleKeyEvent(pKeyEvent->GetKeyCode(), *pSource);
530 return 1;
533 case VCLEVENT_OBJECT_DYING:
534 RemoveWindow(*pSource);
535 return 1;
537 case VCLEVENT_WINDOW_GETFOCUS:
538 case VCLEVENT_WINDOW_LOSEFOCUS:
539 pSource->Invalidate();
540 return 1;
542 default:
543 break;
546 return 0;
549 IMPL_LINK(FocusManager, ChildEventListener, VclSimpleEvent*, pEvent)
551 if (pEvent == NULL)
552 return 0;
554 if (!pEvent->ISA(VclWindowEvent))
555 return 0;
557 VclWindowEvent* pWindowEvent = static_cast<VclWindowEvent*>(pEvent);
558 vcl::Window* pSource = pWindowEvent->GetWindow();
559 if (pSource == NULL)
560 return 0;
562 switch (pWindowEvent->GetId())
564 case VCLEVENT_WINDOW_KEYINPUT:
566 KeyEvent* pKeyEvent = static_cast<KeyEvent*>(pWindowEvent->GetData());
568 // Go up the window hierarchy to find out whether the
569 // parent of the event source is known to us.
570 vcl::Window* pWindow = pSource;
571 FocusLocation aLocation (PC_None, -1);
572 while (true)
574 if (pWindow == NULL)
575 break;
576 aLocation = GetFocusLocation(*pWindow);
577 if (aLocation.meComponent != PC_None)
578 break;
579 pWindow = pWindow->GetParent();
582 if (aLocation.meComponent != PC_None)
584 switch (pKeyEvent->GetKeyCode().GetCode())
586 case KEY_ESCAPE:
587 // Return focus back to the panel title.
588 FocusPanel(aLocation.mnIndex, true);
589 break;
591 case KEY_TAB:
592 if (mpFirstFocusedContentControl!=nullptr
593 && mpLastFocusedWindow == mpFirstFocusedContentControl)
595 // Move focus back to panel (or deck)
596 // title.
597 FocusPanel(aLocation.mnIndex, true);
599 break;
601 default:
602 break;
605 return 1;
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 mpLastFocusedWindow = pSource;
614 if (mbObservingContentControlFocus)
615 mpFirstFocusedContentControl = pSource;
616 break;
618 default:
619 break;
622 return 0;
625 } } // end of namespace sfx2::sidebar
627 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */