| /************************************************************** |
| * |
| * 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 |