summaryrefslogtreecommitdiffstats
path: root/views/focus/focus_manager.cc
diff options
context:
space:
mode:
authorben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-08 00:34:05 +0000
committerben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-08 00:34:05 +0000
commit2362e4fe2905ab75d3230ebc3e307ae53e2b8362 (patch)
treee6d88357a2021811e0e354f618247217be8bb3da /views/focus/focus_manager.cc
parentdb23ac3e713dc17509b2b15d3ee634968da45715 (diff)
downloadchromium_src-2362e4fe2905ab75d3230ebc3e307ae53e2b8362.zip
chromium_src-2362e4fe2905ab75d3230ebc3e307ae53e2b8362.tar.gz
chromium_src-2362e4fe2905ab75d3230ebc3e307ae53e2b8362.tar.bz2
Move src/chrome/views to src/views. RS=darin http://crbug.com/11387
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15604 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views/focus/focus_manager.cc')
-rw-r--r--views/focus/focus_manager.cc716
1 files changed, 716 insertions, 0 deletions
diff --git a/views/focus/focus_manager.cc b/views/focus/focus_manager.cc
new file mode 100644
index 0000000..5653e7a
--- /dev/null
+++ b/views/focus/focus_manager.cc
@@ -0,0 +1,716 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+
+#include "base/histogram.h"
+#include "base/logging.h"
+#include "base/win_util.h"
+#include "chrome/browser/renderer_host/render_widget_host_view_win.h"
+#include "views/accelerator.h"
+#include "views/focus/focus_manager.h"
+#include "views/focus/view_storage.h"
+#include "views/view.h"
+#include "views/widget/root_view.h"
+#include "views/widget/widget.h"
+
+// The following keys are used in SetProp/GetProp to associate additional
+// information needed for focus tracking with a window.
+
+// Maps to the FocusManager instance for a top level window. See
+// CreateFocusManager/DestoryFocusManager for usage.
+static const wchar_t* const kFocusManagerKey = L"__VIEW_CONTAINER__";
+
+// Maps to the View associated with a window.
+// We register views with window so we can:
+// - keep in sync the native focus with the view focus (when the native
+// component gets the focus, we get the WM_SETFOCUS event and we can focus the
+// associated view).
+// - prevent tab key events from being sent to views.
+static const wchar_t* const kViewKey = L"__CHROME_VIEW__";
+
+// A property set to 1 to indicate whether the focus manager has subclassed that
+// window. We are doing this to ensure we are not subclassing several times.
+// Subclassing twice is not a problem if no one is subclassing the HWND between
+// the 2 subclassings (the 2nd subclassing is ignored since the WinProc is the
+// same as the current one). However if some other app goes and subclasses the
+// HWND between the 2 subclassings, we will end up subclassing twice.
+// This flag lets us test that whether we have or not subclassed yet.
+static const wchar_t* const kFocusSubclassInstalled =
+ L"__FOCUS_SUBCLASS_INSTALLED__";
+
+namespace views {
+
+// Callback installed via InstallFocusSubclass.
+static LRESULT CALLBACK FocusWindowCallback(HWND window, UINT message,
+ WPARAM wParam, LPARAM lParam) {
+ if (!::IsWindow(window)) {
+ // QEMU has reported crashes when calling GetProp (this seems to happen for
+ // some weird messages, not sure what they are).
+ // Here we are just trying to avoid the crasher.
+ NOTREACHED();
+ return 0;
+ }
+
+ WNDPROC original_handler = win_util::GetSuperclassWNDPROC(window);
+ DCHECK(original_handler);
+ FocusManager* focus_manager = FocusManager::GetFocusManager(window);
+ // There are cases when we have no FocusManager for the window. This happens
+ // because we subclass certain windows (such as the TabContents window)
+ // but that window may not have an associated FocusManager.
+ if (focus_manager) {
+ switch (message) {
+ case WM_SETFOCUS:
+ if (!focus_manager->OnSetFocus(window))
+ return 0;
+ break;
+ case WM_NCDESTROY:
+ if (!focus_manager->OnNCDestroy(window))
+ return 0;
+ break;
+ case WM_ACTIVATE: {
+ // We call the DefWindowProc before calling OnActivate as some of our
+ // windows need the OnActivate notifications. The default activation on
+ // the window causes it to focus the main window, and since
+ // FocusManager::OnActivate attempts to restore the focused view, it
+ // needs to be called last so the focus it is setting does not get
+ // overridden.
+ LRESULT result = CallWindowProc(original_handler, window, WM_ACTIVATE,
+ wParam, lParam);
+ if (!focus_manager->OnPostActivate(window,
+ LOWORD(wParam), HIWORD(wParam)))
+ return 0;
+ return result;
+ }
+ default:
+ break;
+ }
+ }
+ return CallWindowProc(original_handler, window, message, wParam, lParam);
+}
+
+// FocusManager -----------------------------------------------------
+
+// static
+FocusManager* FocusManager::CreateFocusManager(HWND window,
+ RootView* root_view) {
+ DCHECK(window);
+ DCHECK(root_view);
+ InstallFocusSubclass(window, NULL);
+ FocusManager* focus_manager = new FocusManager(window, root_view);
+ SetProp(window, kFocusManagerKey, focus_manager);
+
+ return focus_manager;
+}
+
+// static
+void FocusManager::InstallFocusSubclass(HWND window, View* view) {
+ DCHECK(window);
+
+ bool already_subclassed =
+ reinterpret_cast<bool>(GetProp(window,
+ kFocusSubclassInstalled));
+ if (already_subclassed &&
+ !win_util::IsSubclassed(window, &FocusWindowCallback)) {
+ NOTREACHED() << "window sub-classed by someone other than the FocusManager";
+ // Track in UMA so we know if this case happens.
+ UMA_HISTOGRAM_COUNTS("FocusManager.MultipleSubclass", 1);
+ } else {
+ win_util::Subclass(window, &FocusWindowCallback);
+ SetProp(window, kFocusSubclassInstalled, reinterpret_cast<HANDLE>(true));
+ }
+ if (view)
+ SetProp(window, kViewKey, view);
+}
+
+void FocusManager::UninstallFocusSubclass(HWND window) {
+ DCHECK(window);
+ if (win_util::Unsubclass(window, &FocusWindowCallback)) {
+ RemoveProp(window, kViewKey);
+ RemoveProp(window, kFocusSubclassInstalled);
+ }
+}
+
+// static
+FocusManager* FocusManager::GetFocusManager(HWND window) {
+ DCHECK(window);
+
+ // In case parent windows belong to a different process, yet
+ // have the kFocusManagerKey property set, we have to be careful
+ // to also check the process id of the window we're checking.
+ DWORD window_pid = 0, current_pid = GetCurrentProcessId();
+ FocusManager* focus_manager;
+ for (focus_manager = NULL; focus_manager == NULL && IsWindow(window);
+ window = GetParent(window)) {
+ GetWindowThreadProcessId(window, &window_pid);
+ if (current_pid != window_pid)
+ break;
+ focus_manager = reinterpret_cast<FocusManager*>(
+ GetProp(window, kFocusManagerKey));
+ }
+ return focus_manager;
+}
+
+// static
+View* FocusManager::GetViewForWindow(HWND window, bool look_in_parents) {
+ DCHECK(window);
+ do {
+ View* v = reinterpret_cast<View*>(GetProp(window, kViewKey));
+ if (v)
+ return v;
+ } while (look_in_parents && (window = ::GetParent(window)));
+
+ return NULL;
+}
+
+FocusManager::FocusManager(HWND root, RootView* root_view)
+ : root_(root),
+ top_root_view_(root_view),
+ focused_view_(NULL),
+ ignore_set_focus_msg_(false) {
+ stored_focused_view_storage_id_ =
+ ViewStorage::GetSharedInstance()->CreateStorageID();
+ DCHECK(root_);
+}
+
+FocusManager::~FocusManager() {
+ // If there are still registered FocusChange listeners, chances are they were
+ // leaked so warn about them.
+ DCHECK(focus_change_listeners_.empty());
+}
+
+// Message handlers.
+bool FocusManager::OnSetFocus(HWND window) {
+ if (ignore_set_focus_msg_)
+ return true;
+
+ // Focus the view associated with that window.
+ View* v = static_cast<View*>(GetProp(window, kViewKey));
+ if (v && v->IsFocusable()) {
+ v->GetRootView()->FocusView(v);
+ } else {
+ SetFocusedView(NULL);
+ }
+
+ return true;
+}
+
+bool FocusManager::OnNCDestroy(HWND window) {
+ // Window is being destroyed, undo the subclassing.
+ FocusManager::UninstallFocusSubclass(window);
+
+ if (window == root_) {
+ // We are the top window.
+
+ DCHECK(GetProp(window, kFocusManagerKey));
+
+ // Make sure this is called on the window that was set with the
+ // FocusManager.
+ RemoveProp(window, kFocusManagerKey);
+
+ delete this;
+ }
+ return true;
+}
+
+bool FocusManager::OnKeyDown(HWND window, UINT message, WPARAM wparam,
+ LPARAM lparam) {
+ DCHECK((message == WM_KEYDOWN) || (message == WM_SYSKEYDOWN));
+
+ if (!IsWindowVisible(root_)) {
+ // We got a message for a hidden window. Because WidgetWin::Close hides the
+ // window, then destroys it, it it possible to get a message after we've
+ // hidden the window. If we allow the message to be dispatched chances are
+ // we'll crash in some weird place. By returning false we make sure the
+ // message isn't dispatched.
+ return false;
+ }
+
+ // First give the registered keystroke handlers a chance a processing
+ // the message
+ // Do some basic checking to try to catch evil listeners that change the list
+ // from under us.
+ KeystrokeListenerList::size_type original_count =
+ keystroke_listeners_.size();
+ for (int i = 0; i < static_cast<int>(keystroke_listeners_.size()); i++) {
+ if (keystroke_listeners_[i]->ProcessKeyStroke(window, message, wparam,
+ lparam)) {
+ return false;
+ }
+ }
+ DCHECK_EQ(original_count, keystroke_listeners_.size())
+ << "KeystrokeListener list modified during notification";
+
+ int virtual_key_code = static_cast<int>(wparam);
+ // Intercept Tab related messages for focus traversal.
+ // Note that we don't do focus traversal if the root window is not part of the
+ // active window hierarchy as this would mean we have no focused view and
+ // would focus the first focusable view.
+ HWND active_window = ::GetActiveWindow();
+ if ((active_window == root_ || ::IsChild(active_window, root_)) &&
+ (virtual_key_code == VK_TAB) && !win_util::IsCtrlPressed()) {
+ if (!focused_view_ || !focused_view_->CanProcessTabKeyEvents()) {
+ AdvanceFocus(win_util::IsShiftPressed());
+ return false;
+ }
+ }
+
+ // Intercept arrow key messages to switch between grouped views.
+ if (focused_view_ && focused_view_->GetGroup() != -1 &&
+ (virtual_key_code == VK_UP || virtual_key_code == VK_DOWN ||
+ virtual_key_code == VK_LEFT || virtual_key_code == VK_RIGHT)) {
+ bool next = (virtual_key_code == VK_RIGHT || virtual_key_code == VK_DOWN);
+ std::vector<View*> views;
+ focused_view_->GetParent()->GetViewsWithGroup(focused_view_->GetGroup(),
+ &views);
+ std::vector<View*>::const_iterator iter = std::find(views.begin(),
+ views.end(),
+ focused_view_);
+ DCHECK(iter != views.end());
+ int index = static_cast<int>(iter - views.begin());
+ index += next ? 1 : -1;
+ if (index < 0) {
+ index = static_cast<int>(views.size()) - 1;
+ } else if (index >= static_cast<int>(views.size())) {
+ index = 0;
+ }
+ views[index]->RequestFocus();
+ return false;
+ }
+
+ int repeat_count = LOWORD(lparam);
+ int flags = HIWORD(lparam);
+ if (focused_view_ &&
+ !focused_view_->ShouldLookupAccelerators(KeyEvent(
+ Event::ET_KEY_PRESSED, virtual_key_code,
+ repeat_count, flags))) {
+ // This should not be processed as an accelerator.
+ return true;
+ }
+
+ // Process keyboard accelerators.
+ // We process accelerators here as we have no way of knowing if a HWND has
+ // really processed a key event. If the key combination matches an
+ // accelerator, the accelerator is triggered, otherwise we forward the
+ // event to the HWND.
+ Accelerator accelerator(Accelerator(static_cast<int>(virtual_key_code),
+ win_util::IsShiftPressed(),
+ win_util::IsCtrlPressed(),
+ win_util::IsAltPressed()));
+ if (ProcessAccelerator(accelerator)) {
+ // If a shortcut was activated for this keydown message, do not
+ // propagate the message further.
+ return false;
+ }
+ return true;
+}
+
+bool FocusManager::OnKeyUp(HWND window, UINT message, WPARAM wparam,
+ LPARAM lparam) {
+ for (int i = 0; i < static_cast<int>(keystroke_listeners_.size()); ++i) {
+ if (keystroke_listeners_[i]->ProcessKeyStroke(window, message, wparam,
+ lparam)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool FocusManager::OnPostActivate(HWND window,
+ int activation_state,
+ int minimized_state) {
+ if (WA_INACTIVE == LOWORD(activation_state)) {
+ StoreFocusedView();
+ } else {
+ RestoreFocusedView();
+ }
+ return false;
+}
+
+void FocusManager::ValidateFocusedView() {
+ if (focused_view_) {
+ if (!ContainsView(focused_view_)) {
+ focused_view_ = NULL;
+ }
+ }
+}
+
+// Tests whether a view is valid, whether it still belongs to the window
+// hierarchy of the FocusManager.
+bool FocusManager::ContainsView(View* view) {
+ DCHECK(view);
+ RootView* root_view = view->GetRootView();
+ if (!root_view)
+ return false;
+
+ Widget* widget = root_view->GetWidget();
+ if (!widget)
+ return false;
+
+ HWND window = widget->GetNativeView();
+ while (window) {
+ if (window == root_)
+ return true;
+ window = ::GetParent(window);
+ }
+ return false;
+}
+
+void FocusManager::AdvanceFocus(bool reverse) {
+ View* v = GetNextFocusableView(focused_view_, reverse, false);
+ // Note: Do not skip this next block when v == focused_view_. If the user
+ // tabs past the last focusable element in a webpage, we'll get here, and if
+ // the TabContentsContainerView is the only focusable view (possible in
+ // fullscreen mode), we need to run this block in order to cycle around to the
+ // first element on the page.
+ if (v) {
+ v->AboutToRequestFocusFromTabTraversal(reverse);
+ v->RequestFocus();
+ }
+}
+
+View* FocusManager::GetNextFocusableView(View* original_starting_view,
+ bool reverse,
+ bool dont_loop) {
+ FocusTraversable* focus_traversable = NULL;
+
+ // Let's revalidate the focused view.
+ ValidateFocusedView();
+
+ View* starting_view = NULL;
+ if (original_starting_view) {
+ // If the starting view has a focus traversable, use it.
+ // This is the case with WidgetWins for example.
+ focus_traversable = original_starting_view->GetFocusTraversable();
+
+ // Otherwise default to the root view.
+ if (!focus_traversable) {
+ focus_traversable = original_starting_view->GetRootView();
+ starting_view = original_starting_view;
+ }
+ } else {
+ focus_traversable = top_root_view_;
+ }
+
+ // Traverse the FocusTraversable tree down to find the focusable view.
+ View* v = FindFocusableView(focus_traversable, starting_view,
+ reverse, dont_loop);
+ if (v) {
+ return v;
+ } else {
+ // Let's go up in the FocusTraversable tree.
+ FocusTraversable* parent_focus_traversable =
+ focus_traversable->GetFocusTraversableParent();
+ starting_view = focus_traversable->GetFocusTraversableParentView();
+ while (parent_focus_traversable) {
+ FocusTraversable* new_focus_traversable = NULL;
+ View* new_starting_view = NULL;
+ v = parent_focus_traversable ->FindNextFocusableView(
+ starting_view, reverse, FocusTraversable::UP, dont_loop,
+ &new_focus_traversable, &new_starting_view);
+
+ if (new_focus_traversable) {
+ DCHECK(!v);
+
+ // There is a FocusTraversable, traverse it down.
+ v = FindFocusableView(new_focus_traversable, NULL, reverse, dont_loop);
+ }
+
+ if (v)
+ return v;
+
+ starting_view = focus_traversable->GetFocusTraversableParentView();
+ parent_focus_traversable =
+ parent_focus_traversable->GetFocusTraversableParent();
+ }
+
+ if (!dont_loop) {
+ // If we get here, we have reached the end of the focus hierarchy, let's
+ // loop.
+ if (reverse) {
+ // When reversing from the top, the next focusable view is at the end
+ // of the focus hierarchy.
+ return FindLastFocusableView();
+ } else {
+ // Easy, just clear the selection and press tab again.
+ if (original_starting_view) { // Make sure there was at least a view to
+ // start with, to prevent infinitely
+ // looping in empty windows.
+ // By calling with NULL as the starting view, we'll start from the
+ // top_root_view.
+ return GetNextFocusableView(NULL, false, true);
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+View* FocusManager::FindLastFocusableView() {
+ // Just walk the entire focus loop from where we're at until we reach the end.
+ View* new_focused = NULL;
+ View* last_focused = focused_view_;
+ while (new_focused = GetNextFocusableView(last_focused, false, true))
+ last_focused = new_focused;
+ return last_focused;
+}
+
+void FocusManager::SetFocusedView(View* view) {
+ if (focused_view_ != view) {
+ View* prev_focused_view = focused_view_;
+ if (focused_view_)
+ focused_view_->WillLoseFocus();
+
+ if (view)
+ view->WillGainFocus();
+
+ // Notified listeners that the focus changed.
+ FocusChangeListenerList::const_iterator iter;
+ for (iter = focus_change_listeners_.begin();
+ iter != focus_change_listeners_.end(); ++iter) {
+ (*iter)->FocusWillChange(prev_focused_view, view);
+ }
+
+ focused_view_ = view;
+
+ if (prev_focused_view)
+ prev_focused_view->SchedulePaint(); // Remove focus artifacts.
+
+ if (view) {
+ view->SchedulePaint();
+ view->Focus();
+ view->DidGainFocus();
+ }
+ }
+}
+
+void FocusManager::ClearFocus() {
+ SetFocusedView(NULL);
+ ClearHWNDFocus();
+}
+
+void FocusManager::ClearHWNDFocus() {
+ // Keep the top root window focused so we get keyboard events.
+ ignore_set_focus_msg_ = true;
+ ::SetFocus(root_);
+ ignore_set_focus_msg_ = false;
+}
+
+void FocusManager::FocusHWND(HWND hwnd) {
+ ignore_set_focus_msg_ = true;
+ // Only reset focus if hwnd is not already focused.
+ if (hwnd && ::GetFocus() != hwnd)
+ ::SetFocus(hwnd);
+ ignore_set_focus_msg_ = false;
+}
+
+void FocusManager::StoreFocusedView() {
+ ViewStorage* view_storage = ViewStorage::GetSharedInstance();
+ if (!view_storage) {
+ // This should never happen but bug 981648 seems to indicate it could.
+ NOTREACHED();
+ return;
+ }
+
+ // TODO (jcampan): when a TabContents containing a popup is closed, the focus
+ // is stored twice causing an assert. We should find a better alternative than
+ // removing the view from the storage explicitly.
+ view_storage->RemoveView(stored_focused_view_storage_id_);
+
+ if (!focused_view_)
+ return;
+
+ view_storage->StoreView(stored_focused_view_storage_id_, focused_view_);
+
+ View* v = focused_view_;
+ focused_view_ = NULL;
+
+ if (v)
+ v->SchedulePaint(); // Remove focus border.
+}
+
+void FocusManager::RestoreFocusedView() {
+ ViewStorage* view_storage = ViewStorage::GetSharedInstance();
+ if (!view_storage) {
+ // This should never happen but bug 981648 seems to indicate it could.
+ NOTREACHED();
+ return;
+ }
+
+ View* view = view_storage->RetrieveView(stored_focused_view_storage_id_);
+ if (view) {
+ if (ContainsView(view))
+ view->RequestFocus();
+ } else {
+ // Clearing the focus will focus the root window, so we still get key
+ // events.
+ ClearFocus();
+ }
+}
+
+void FocusManager::ClearStoredFocusedView() {
+ ViewStorage* view_storage = ViewStorage::GetSharedInstance();
+ if (!view_storage) {
+ // This should never happen but bug 981648 seems to indicate it could.
+ NOTREACHED();
+ return;
+ }
+ view_storage->RemoveView(stored_focused_view_storage_id_);
+}
+
+FocusManager* FocusManager::GetParentFocusManager() const {
+ HWND parent = ::GetParent(root_);
+ // If we are a top window, we don't have a parent FocusManager.
+ if (!parent)
+ return NULL;
+
+ return GetFocusManager(parent);
+}
+
+// Find the next (previous if reverse is true) focusable view for the specified
+// FocusTraversable, starting at the specified view, traversing down the
+// FocusTraversable hierarchy.
+View* FocusManager::FindFocusableView(FocusTraversable* focus_traversable,
+ View* starting_view,
+ bool reverse,
+ bool dont_loop) {
+ FocusTraversable* new_focus_traversable = NULL;
+ View* new_starting_view = NULL;
+ View* v = focus_traversable->FindNextFocusableView(starting_view,
+ reverse,
+ FocusTraversable::DOWN,
+ dont_loop,
+ &new_focus_traversable,
+ &new_starting_view);
+
+ // Let's go down the FocusTraversable tree as much as we can.
+ while (new_focus_traversable) {
+ DCHECK(!v);
+ focus_traversable = new_focus_traversable;
+ starting_view = new_starting_view;
+ new_focus_traversable = NULL;
+ starting_view = NULL;
+ v = focus_traversable->FindNextFocusableView(starting_view,
+ reverse,
+ FocusTraversable::DOWN,
+ dont_loop,
+ &new_focus_traversable,
+ &new_starting_view);
+ }
+ return v;
+}
+
+AcceleratorTarget* FocusManager::RegisterAccelerator(
+ const Accelerator& accelerator,
+ AcceleratorTarget* target) {
+ AcceleratorMap::const_iterator iter = accelerators_.find(accelerator);
+ AcceleratorTarget* previous_target = NULL;
+ if (iter != accelerators_.end())
+ previous_target = iter->second;
+
+ accelerators_[accelerator] = target;
+
+ return previous_target;
+}
+
+void FocusManager::UnregisterAccelerator(const Accelerator& accelerator,
+ AcceleratorTarget* target) {
+ AcceleratorMap::iterator iter = accelerators_.find(accelerator);
+ if (iter == accelerators_.end()) {
+ NOTREACHED() << "Unregistering non-existing accelerator";
+ return;
+ }
+
+ if (iter->second != target) {
+ NOTREACHED() << "Unregistering accelerator for wrong target";
+ return;
+ }
+
+ accelerators_.erase(iter);
+}
+
+void FocusManager::UnregisterAccelerators(AcceleratorTarget* target) {
+ for (AcceleratorMap::iterator iter = accelerators_.begin();
+ iter != accelerators_.end();) {
+ if (iter->second == target)
+ accelerators_.erase(iter++);
+ else
+ ++iter;
+ }
+}
+
+bool FocusManager::ProcessAccelerator(const Accelerator& accelerator) {
+ FocusManager* focus_manager = this;
+ do {
+ AcceleratorTarget* target =
+ focus_manager->GetTargetForAccelerator(accelerator);
+ if (target) {
+ // If there is focused view, we give it a chance to process that
+ // accelerator.
+ if (!focused_view_ ||
+ !focused_view_->OverrideAccelerator(accelerator)) {
+ if (target->AcceleratorPressed(accelerator))
+ return true;
+ }
+ }
+
+ // When dealing with child windows that have their own FocusManager (such
+ // as ConstrainedWindow), we still want the parent FocusManager to process
+ // the accelerator if the child window did not process it.
+ focus_manager = focus_manager->GetParentFocusManager();
+ } while (focus_manager);
+ return false;
+}
+
+AcceleratorTarget* FocusManager::GetTargetForAccelerator(
+ const Accelerator& accelerator) const {
+ AcceleratorMap::const_iterator iter = accelerators_.find(accelerator);
+ if (iter != accelerators_.end())
+ return iter->second;
+ return NULL;
+}
+
+void FocusManager::ViewRemoved(View* parent, View* removed) {
+ if (focused_view_ && focused_view_ == removed)
+ focused_view_ = NULL;
+}
+
+void FocusManager::AddKeystrokeListener(KeystrokeListener* listener) {
+ DCHECK(std::find(keystroke_listeners_.begin(), keystroke_listeners_.end(),
+ listener) == keystroke_listeners_.end())
+ << "Adding a listener twice.";
+ keystroke_listeners_.push_back(listener);
+}
+
+void FocusManager::RemoveKeystrokeListener(KeystrokeListener* listener) {
+ KeystrokeListenerList::iterator place =
+ std::find(keystroke_listeners_.begin(), keystroke_listeners_.end(),
+ listener);
+ if (place == keystroke_listeners_.end()) {
+ NOTREACHED() << "Removing a listener that isn't registered.";
+ return;
+ }
+ keystroke_listeners_.erase(place);
+}
+
+void FocusManager::AddFocusChangeListener(FocusChangeListener* listener) {
+ DCHECK(std::find(focus_change_listeners_.begin(),
+ focus_change_listeners_.end(), listener) ==
+ focus_change_listeners_.end()) << "Adding a listener twice.";
+ focus_change_listeners_.push_back(listener);
+}
+
+void FocusManager::RemoveFocusChangeListener(FocusChangeListener* listener) {
+ FocusChangeListenerList::iterator place =
+ std::find(focus_change_listeners_.begin(), focus_change_listeners_.end(),
+ listener);
+ if (place == focus_change_listeners_.end()) {
+ NOTREACHED() << "Removing a listener that isn't registered.";
+ return;
+ }
+ focus_change_listeners_.erase(place);
+}
+
+} // namespace views