blob: e538bbc35f9348de533ce7231ea45187cff30466 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
#include "precompiled_sfx2.hxx"
#include "FocusManager.hxx"
#include "Panel.hxx"
#include "DeckTitleBar.hxx"
#include "PanelTitleBar.hxx"
#include "sfx2/sidebar/Tools.hxx"
#include "TitleBar.hxx"
#include <vcl/button.hxx>
#include <vcl/toolbox.hxx>
#include <toolkit/helper/vclunohelper.hxx>
namespace sfx2 { namespace sidebar {
FocusManager::FocusLocation::FocusLocation (const PanelComponent eComponent, const sal_Int32 nIndex)
: meComponent(eComponent),
mnIndex(nIndex)
{
}
FocusManager::FocusManager (const ::boost::function<void(const Panel&)>& rShowPanelFunctor)
: mpDeckTitleBar(),
maPanels(),
maButtons(),
maShowPanelFunctor(rShowPanelFunctor),
mbObservingContentControlFocus(false),
mpFirstFocusedContentControl(NULL)
{
}
FocusManager::~FocusManager (void)
{
Clear();
}
void FocusManager::GrabFocus (void)
{
FocusDeckTitle();
}
void FocusManager::Clear (void)
{
SetDeckTitle(NULL);
ClearPanels();
ClearButtons();
}
void FocusManager::ClearPanels (void)
{
::std::vector<Panel*> aPanels;
aPanels.swap(maPanels);
for (::std::vector<Panel*>::iterator iPanel(aPanels.begin()),iEnd(aPanels.end());
iPanel!=iEnd;
++iPanel)
{
UnregisterWindow(**iPanel);
if ((*iPanel)->GetTitleBar() != NULL)
{
UnregisterWindow(*(*iPanel)->GetTitleBar());
UnregisterWindow((*iPanel)->GetTitleBar()->GetToolBox());
}
(*iPanel)->RemoveChildEventListener(LINK(this, FocusManager, ChildEventListener));
}
}
void FocusManager::ClearButtons (void)
{
::std::vector<Button*> aButtons;
aButtons.swap(maButtons);
for (::std::vector<Button*>::iterator iButton(aButtons.begin()),iEnd(aButtons.end());
iButton!=iEnd;
++iButton)
{
UnregisterWindow(**iButton);
}
}
void FocusManager::SetDeckTitle (DeckTitleBar* pDeckTitleBar)
{
if (mpDeckTitleBar != NULL)
{
UnregisterWindow(*mpDeckTitleBar);
UnregisterWindow(mpDeckTitleBar->GetToolBox());
}
mpDeckTitleBar = pDeckTitleBar;
if (mpDeckTitleBar != NULL)
{
RegisterWindow(*mpDeckTitleBar);
RegisterWindow(mpDeckTitleBar->GetToolBox());
}
}
void FocusManager::SetPanels (const SharedPanelContainer& rPanels)
{
ClearPanels();
for(SharedPanelContainer::const_iterator iPanel(rPanels.begin()),iEnd(rPanels.end());
iPanel!=iEnd;
++iPanel)
{
RegisterWindow(**iPanel);
if ((*iPanel)->GetTitleBar() != NULL)
{
RegisterWindow(*(*iPanel)->GetTitleBar());
RegisterWindow((*iPanel)->GetTitleBar()->GetToolBox());
}
// Register also as child event listener at the panel.
(*iPanel)->AddChildEventListener(LINK(this, FocusManager, ChildEventListener));
maPanels.push_back(iPanel->get());
}
}
void FocusManager::SetButtons (const ::std::vector<Button*>& rButtons)
{
ClearButtons();
for (::std::vector<Button*>::const_iterator iButton(rButtons.begin()),iEnd(rButtons.end());
iButton!=iEnd;
++iButton)
{
RegisterWindow(**iButton);
maButtons.push_back(*iButton);
}
}
void FocusManager::RegisterWindow (Window& rWindow)
{
rWindow.AddEventListener(LINK(this, FocusManager, WindowEventListener));
}
void FocusManager::UnregisterWindow (Window& rWindow)
{
rWindow.RemoveEventListener(LINK(this, FocusManager, WindowEventListener));
}
FocusManager::FocusLocation FocusManager::GetFocusLocation (const Window& rWindow) const
{
// Check the deck title.
if (mpDeckTitleBar != NULL)
{
if (mpDeckTitleBar == &rWindow)
return FocusLocation(PC_DeckTitle, -1);
else if (&mpDeckTitleBar->GetToolBox() == &rWindow)
return FocusLocation(PC_DeckToolBox, -1);
}
// Search the panels.
for (sal_Int32 nIndex=0,nCount(maPanels.size()); nIndex<nCount; ++nIndex)
{
if (maPanels[nIndex] == &rWindow)
return FocusLocation(PC_PanelContent, nIndex);
TitleBar* pTitleBar = maPanels[nIndex]->GetTitleBar();
if (pTitleBar == &rWindow)
return FocusLocation(PC_PanelTitle, nIndex);
if (pTitleBar!=NULL && &pTitleBar->GetToolBox()==&rWindow)
return FocusLocation(PC_PanelToolBox, nIndex);
}
// Search the buttons.
for (sal_Int32 nIndex=0,nCount(maButtons.size()); nIndex<nCount; ++nIndex)
if (maButtons[nIndex] == &rWindow)
return FocusLocation(PC_TabBar, nIndex);
return FocusLocation(PC_None, -1);
}
bool FocusManager::IsAnyPanelFocused (void) const
{
for (::std::vector<Panel*>::const_iterator iPanel(maPanels.begin()),iEnd(maPanels.end());
iPanel!=iEnd;
++iPanel)
{
if ((*iPanel)->HasFocus())
return true;
else if ((*iPanel)->HasChildPathFocus())
return true;
}
return false;
}
bool FocusManager::IsAnyButtonFocused (void) const
{
for (::std::vector<Button*>::const_iterator iButton(maButtons.begin()),iEnd(maButtons.end());
iButton!=iEnd;
++iButton)
{
if ((*iButton)->HasFocus())
return true;
}
return false;
}
void FocusManager::FocusDeckTitle (void)
{
if (mpDeckTitleBar != NULL)
{
if (IsDeckTitleVisible())
{
mpDeckTitleBar->GrabFocus();
}
else if (mpDeckTitleBar->GetToolBox().GetItemCount() > 0)
{
ToolBox& rToolBox = mpDeckTitleBar->GetToolBox();
rToolBox.GrabFocus();
rToolBox.Invalidate();
}
else
FocusPanel(0, false);
}
else
FocusPanel(0, false);
}
bool FocusManager::IsDeckTitleVisible (void) const
{
return mpDeckTitleBar != NULL && mpDeckTitleBar->IsVisible();
}
bool FocusManager::IsPanelTitleVisible (const sal_Int32 nPanelIndex) const
{
if (nPanelIndex<0 || nPanelIndex>=static_cast<sal_Int32>(maPanels.size()))
return false;
TitleBar* pTitleBar = maPanels[nPanelIndex]->GetTitleBar();
if (pTitleBar==NULL)
return false;
return pTitleBar->IsVisible();
}
void FocusManager::FocusPanel (
const sal_Int32 nPanelIndex,
const bool bFallbackToDeckTitle)
{
if (nPanelIndex<0 || nPanelIndex>=static_cast<sal_Int32>(maPanels.size()))
{
if (bFallbackToDeckTitle)
FocusDeckTitle();
return;
}
Panel& rPanel (*maPanels[nPanelIndex]);
TitleBar* pTitleBar = rPanel.GetTitleBar();
if (pTitleBar!=NULL && pTitleBar->IsVisible())
{
rPanel.SetExpanded(true);
pTitleBar->GrabFocus();
}
else if (bFallbackToDeckTitle)
{
// The panel title is not visible, fall back to the deck
// title.
// Make sure that the desk title is visible here to prevent a
// loop when both the title of panel 0 and the deck title are
// not present.
if (IsDeckTitleVisible())
FocusDeckTitle();
else
FocusPanelContent(nPanelIndex);
}
else
FocusPanelContent(nPanelIndex);
if (maShowPanelFunctor)
maShowPanelFunctor(rPanel);
}
void FocusManager::FocusPanelContent (const sal_Int32 nPanelIndex)
{
Window* pWindow = VCLUnoHelper::GetWindow(maPanels[nPanelIndex]->GetElementWindow());
if (pWindow != NULL)
{
mbObservingContentControlFocus = true;
pWindow->GrabFocus();
mbObservingContentControlFocus = false;
}
}
void FocusManager::FocusButton (const sal_Int32 nButtonIndex)
{
maButtons[nButtonIndex]->GrabFocus();
maButtons[nButtonIndex]->Invalidate();
}
void FocusManager::ClickButton (const sal_Int32 nButtonIndex)
{
maButtons[nButtonIndex]->Click();
if (nButtonIndex > 0)
if ( ! maPanels.empty())
FocusPanel(0, true);
maButtons[nButtonIndex]->GetParent()->Invalidate();
}
void FocusManager::RemoveWindow (Window& rWindow)
{
::std::vector<Panel*>::iterator iPanel (::std::find(maPanels.begin(), maPanels.end(), &rWindow));
if (iPanel != maPanels.end())
{
UnregisterWindow(rWindow);
if ((*iPanel)->GetTitleBar() != NULL)
{
UnregisterWindow(*(*iPanel)->GetTitleBar());
UnregisterWindow((*iPanel)->GetTitleBar()->GetToolBox());
}
maPanels.erase(iPanel);
return;
}
::std::vector<Button*>::iterator iButton (::std::find(maButtons.begin(), maButtons.end(), &rWindow));
if (iButton != maButtons.end())
{
UnregisterWindow(rWindow);
maButtons.erase(iButton);
return;
}
}
bool FocusManager::MoveFocusInsidePanel (
const FocusLocation aFocusLocation,
const sal_Int32 nDirection)
{
const bool bHasToolBoxItem (
maPanels[aFocusLocation.mnIndex]->GetTitleBar()->GetToolBox().GetItemCount() > 0);
switch (aFocusLocation.meComponent)
{
case PC_PanelTitle:
if (nDirection > 0 && bHasToolBoxItem)
maPanels[aFocusLocation.mnIndex]->GetTitleBar()->GetToolBox().GrabFocus();
else
FocusPanelContent(aFocusLocation.mnIndex);
return true;
case PC_PanelToolBox:
if (nDirection < 0 && bHasToolBoxItem)
maPanels[aFocusLocation.mnIndex]->GetTitleBar()->GrabFocus();
else
FocusPanelContent(aFocusLocation.mnIndex);
return true;
default:
return false;
}
}
bool FocusManager::MoveFocusInsideDeckTitle (
const FocusLocation aFocusLocation,
const sal_Int32 nDirection)
{
// Note that when the title bar of the first (and only) panel is
// not visible then the deck title takes its place and the focus
// is moved between a) deck title, b) deck closer and c) content
// of panel 0.
const bool bHasToolBoxItem (
mpDeckTitleBar->GetToolBox().GetItemCount() > 0);
switch (aFocusLocation.meComponent)
{
case PC_DeckTitle:
if (nDirection<0 && ! IsPanelTitleVisible(0))
FocusPanelContent(0);
else if (bHasToolBoxItem)
mpDeckTitleBar->GetToolBox().GrabFocus();
return true;
case PC_DeckToolBox:
if (nDirection>0 && ! IsPanelTitleVisible(0))
FocusPanelContent(0);
else
mpDeckTitleBar->GrabFocus();
return true;
default:
return false;
}
}
void FocusManager::HandleKeyEvent (
const KeyCode& rKeyCode,
const Window& rWindow)
{
const FocusLocation aLocation (GetFocusLocation(rWindow));
mpLastFocusedWindow = NULL;
switch (rKeyCode.GetCode())
{
case KEY_SPACE:
switch (aLocation.meComponent)
{
case PC_PanelTitle:
// Toggle panel between expanded and collapsed.
maPanels[aLocation.mnIndex]->SetExpanded( ! maPanels[aLocation.mnIndex]->IsExpanded());
break;
case PC_TabBar:
// Activate the button.
ClickButton(aLocation.mnIndex);
break;
default:
break;
}
return;
case KEY_RETURN:
switch (aLocation.meComponent)
{
case PC_DeckToolBox:
FocusButton(0);
break;
case PC_PanelTitle:
// Enter the panel.
FocusPanelContent(aLocation.mnIndex);
break;
case PC_TabBar:
// Activate the button.
ClickButton(aLocation.mnIndex);
break;
default:
break;
}
return;
case KEY_TAB:
{
const sal_Int32 nDirection (
rKeyCode.IsShift()
? -1
: +1);
switch (aLocation.meComponent)
{
case PC_PanelTitle:
case PC_PanelToolBox:
case PC_PanelContent:
MoveFocusInsidePanel(aLocation, nDirection);
break;
case PC_DeckTitle:
case PC_DeckToolBox:
MoveFocusInsideDeckTitle(aLocation, nDirection);
break;
default:
break;
}
break;
}
case KEY_LEFT:
case KEY_UP:
switch (aLocation.meComponent)
{
case PC_PanelTitle:
case PC_PanelToolBox:
case PC_PanelContent:
// Go to previous panel or the deck title.
if (aLocation.mnIndex > 0)
FocusPanel(aLocation.mnIndex-1, true);
else if (IsDeckTitleVisible())
FocusDeckTitle();
else
FocusButton(maButtons.size()-1);
break;
case PC_DeckTitle:
case PC_DeckToolBox:
// Focus the last button.
FocusButton(maButtons.size()-1);
break;
case PC_TabBar:
// Go to previous tab bar item.
if (aLocation.mnIndex == 0)
FocusPanel(maPanels.size()-1, true);
else
FocusButton((aLocation.mnIndex + maButtons.size() - 1) % maButtons.size());
break;
default:
break;
}
break;
case KEY_RIGHT:
case KEY_DOWN:
switch(aLocation.meComponent)
{
case PC_PanelTitle:
case PC_PanelToolBox:
case PC_PanelContent:
// Go to next panel.
if (aLocation.mnIndex < static_cast<sal_Int32>(maPanels.size())-1)
FocusPanel(aLocation.mnIndex+1, false);
else
FocusButton(0);
break;
case PC_DeckTitle:
case PC_DeckToolBox:
// Focus the first panel.
if (IsPanelTitleVisible(0))
FocusPanel(0, false);
else
FocusButton(0);
break;
case PC_TabBar:
// Go to next tab bar item.
if (aLocation.mnIndex < static_cast<sal_Int32>(maButtons.size())-1)
FocusButton(aLocation.mnIndex + 1);
else if (IsDeckTitleVisible())
FocusDeckTitle();
else
FocusPanel(0, true);
break;
default:
break;
}
break;
}
}
IMPL_LINK(FocusManager, WindowEventListener, VclSimpleEvent*, pEvent)
{
if (pEvent == NULL)
return 0;
if ( ! pEvent->ISA(VclWindowEvent))
return 0;
VclWindowEvent* pWindowEvent = static_cast<VclWindowEvent*>(pEvent);
Window* pSource = pWindowEvent->GetWindow();
if (pSource == NULL)
return 0;
switch (pWindowEvent->GetId())
{
case VCLEVENT_WINDOW_KEYINPUT:
{
KeyEvent* pKeyEvent = static_cast<KeyEvent*>(pWindowEvent->GetData());
HandleKeyEvent(pKeyEvent->GetKeyCode(), *pSource);
return 1;
}
case VCLEVENT_OBJECT_DYING:
RemoveWindow(*pSource);
return 1;
case VCLEVENT_WINDOW_GETFOCUS:
case VCLEVENT_WINDOW_LOSEFOCUS:
pSource->Invalidate();
return 1;
default:
break;
}
return 0;
}
IMPL_LINK(FocusManager, ChildEventListener, VclSimpleEvent*, pEvent)
{
if (pEvent == NULL)
return 0;
if ( ! pEvent->ISA(VclWindowEvent))
return 0;
VclWindowEvent* pWindowEvent = static_cast<VclWindowEvent*>(pEvent);
Window* pSource = pWindowEvent->GetWindow();
if (pSource == NULL)
return 0;
switch (pWindowEvent->GetId())
{
case VCLEVENT_WINDOW_KEYINPUT:
{
KeyEvent* pKeyEvent = static_cast<KeyEvent*>(pWindowEvent->GetData());
// Go up the window hierarchy to find out whether the
// parent of the event source is known to us.
Window* pWindow = pSource;
FocusLocation aLocation (PC_None, -1);
while (true)
{
if (pWindow == NULL)
break;
aLocation = GetFocusLocation(*pWindow);
if (aLocation.meComponent != PC_None)
break;
pWindow = pWindow->GetParent();
}
if (aLocation.meComponent != PC_None)
{
switch (pKeyEvent->GetKeyCode().GetCode())
{
case KEY_ESCAPE:
// Return focus back to the panel title.
FocusPanel(aLocation.mnIndex, true);
break;
case KEY_TAB:
if (mpFirstFocusedContentControl!=NULL
&& mpLastFocusedWindow == mpFirstFocusedContentControl)
{
// Move focus back to panel (or deck)
// title.
FocusPanel(aLocation.mnIndex, true);
}
break;
default:
break;
}
}
return 1;
}
case VCLEVENT_WINDOW_GETFOCUS:
// Keep track of focused controls in panel content.
// Remember the first focused control. When it is later
// focused again due to pressing the TAB key then the
// focus is moved to the panel or deck title.
mpLastFocusedWindow = pSource;
if (mbObservingContentControlFocus)
mpFirstFocusedContentControl = pSource;
break;
default:
break;
}
return 0;
}
} } // end of namespace sfx2::sidebar