1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/views/accessible_pane_view.h"
6
7#include "base/message_loop/message_loop.h"
8#include "ui/accessibility/ax_view_state.h"
9#include "ui/views/focus/focus_search.h"
10#include "ui/views/focus/view_storage.h"
11#include "ui/views/widget/widget.h"
12
13namespace views {
14
15// Create tiny subclass of FocusSearch that overrides GetParent and Contains,
16// delegating these to methods in AccessiblePaneView. This is needed so that
17// subclasses of AccessiblePaneView can customize the focus search logic and
18// include views that aren't part of the AccessiblePaneView's view
19// hierarchy in the focus order.
20class AccessiblePaneViewFocusSearch : public FocusSearch {
21 public:
22  explicit AccessiblePaneViewFocusSearch(AccessiblePaneView* pane_view)
23      : FocusSearch(pane_view, true, true),
24        accessible_pane_view_(pane_view) {}
25
26 protected:
27  virtual View* GetParent(View* v) OVERRIDE {
28    return accessible_pane_view_->ContainsForFocusSearch(root(), v) ?
29        accessible_pane_view_->GetParentForFocusSearch(v) : NULL;
30  }
31
32  // Returns true if |v| is contained within the hierarchy rooted at |root|.
33  // Subclasses can override this if they need custom focus search behavior.
34  virtual bool Contains(View* root, const View* v) OVERRIDE {
35    return accessible_pane_view_->ContainsForFocusSearch(root, v);
36  }
37
38 private:
39  AccessiblePaneView* accessible_pane_view_;
40  DISALLOW_COPY_AND_ASSIGN(AccessiblePaneViewFocusSearch);
41};
42
43AccessiblePaneView::AccessiblePaneView()
44    : pane_has_focus_(false),
45      allow_deactivate_on_esc_(false),
46      focus_manager_(NULL),
47      home_key_(ui::VKEY_HOME, ui::EF_NONE),
48      end_key_(ui::VKEY_END, ui::EF_NONE),
49      escape_key_(ui::VKEY_ESCAPE, ui::EF_NONE),
50      left_key_(ui::VKEY_LEFT, ui::EF_NONE),
51      right_key_(ui::VKEY_RIGHT, ui::EF_NONE),
52      method_factory_(this) {
53  focus_search_.reset(new AccessiblePaneViewFocusSearch(this));
54  last_focused_view_storage_id_ = ViewStorage::GetInstance()->CreateStorageID();
55}
56
57AccessiblePaneView::~AccessiblePaneView() {
58  if (pane_has_focus_) {
59    focus_manager_->RemoveFocusChangeListener(this);
60  }
61}
62
63bool AccessiblePaneView::SetPaneFocus(views::View* initial_focus) {
64  if (!visible())
65    return false;
66
67  if (!focus_manager_)
68    focus_manager_ = GetFocusManager();
69
70  View* focused_view = focus_manager_->GetFocusedView();
71  if (focused_view && !ContainsForFocusSearch(this, focused_view)) {
72    ViewStorage* view_storage = ViewStorage::GetInstance();
73    view_storage->RemoveView(last_focused_view_storage_id_);
74    view_storage->StoreView(last_focused_view_storage_id_, focused_view);
75  }
76
77  // Use the provided initial focus if it's visible and enabled, otherwise
78  // use the first focusable child.
79  if (!initial_focus ||
80      !ContainsForFocusSearch(this, initial_focus) ||
81      !initial_focus->visible() ||
82      !initial_focus->enabled()) {
83    initial_focus = GetFirstFocusableChild();
84  }
85
86  // Return false if there are no focusable children.
87  if (!initial_focus)
88    return false;
89
90  focus_manager_->SetFocusedView(initial_focus);
91
92  // If we already have pane focus, we're done.
93  if (pane_has_focus_)
94    return true;
95
96  // Otherwise, set accelerators and start listening for focus change events.
97  pane_has_focus_ = true;
98  ui::AcceleratorManager::HandlerPriority normal =
99      ui::AcceleratorManager::kNormalPriority;
100  focus_manager_->RegisterAccelerator(home_key_, normal, this);
101  focus_manager_->RegisterAccelerator(end_key_, normal, this);
102  focus_manager_->RegisterAccelerator(escape_key_, normal, this);
103  focus_manager_->RegisterAccelerator(left_key_, normal, this);
104  focus_manager_->RegisterAccelerator(right_key_, normal, this);
105  focus_manager_->AddFocusChangeListener(this);
106
107  return true;
108}
109
110bool AccessiblePaneView::SetPaneFocusAndFocusDefault() {
111  return SetPaneFocus(GetDefaultFocusableChild());
112}
113
114views::View* AccessiblePaneView::GetDefaultFocusableChild() {
115  return NULL;
116}
117
118View* AccessiblePaneView::GetParentForFocusSearch(View* v) {
119  return v->parent();
120}
121
122bool AccessiblePaneView::ContainsForFocusSearch(View* root, const View* v) {
123  return root->Contains(v);
124}
125
126void AccessiblePaneView::RemovePaneFocus() {
127  focus_manager_->RemoveFocusChangeListener(this);
128  pane_has_focus_ = false;
129
130  focus_manager_->UnregisterAccelerator(home_key_, this);
131  focus_manager_->UnregisterAccelerator(end_key_, this);
132  focus_manager_->UnregisterAccelerator(escape_key_, this);
133  focus_manager_->UnregisterAccelerator(left_key_, this);
134  focus_manager_->UnregisterAccelerator(right_key_, this);
135}
136
137views::View* AccessiblePaneView::GetFirstFocusableChild() {
138  FocusTraversable* dummy_focus_traversable;
139  views::View* dummy_focus_traversable_view;
140  return focus_search_->FindNextFocusableView(
141      NULL, false, views::FocusSearch::DOWN, false,
142      &dummy_focus_traversable, &dummy_focus_traversable_view);
143}
144
145views::View* AccessiblePaneView::GetLastFocusableChild() {
146  FocusTraversable* dummy_focus_traversable;
147  views::View* dummy_focus_traversable_view;
148  return focus_search_->FindNextFocusableView(
149      this, true, views::FocusSearch::DOWN, false,
150      &dummy_focus_traversable, &dummy_focus_traversable_view);
151}
152
153////////////////////////////////////////////////////////////////////////////////
154// View overrides:
155
156views::FocusTraversable* AccessiblePaneView::GetPaneFocusTraversable() {
157  if (pane_has_focus_)
158    return this;
159  else
160    return NULL;
161}
162
163bool AccessiblePaneView::AcceleratorPressed(
164    const ui::Accelerator& accelerator) {
165
166  views::View* focused_view = focus_manager_->GetFocusedView();
167  if (!ContainsForFocusSearch(this, focused_view))
168    return false;
169
170  switch (accelerator.key_code()) {
171    case ui::VKEY_ESCAPE: {
172      RemovePaneFocus();
173      View* last_focused_view = ViewStorage::GetInstance()->RetrieveView(
174          last_focused_view_storage_id_);
175      if (last_focused_view) {
176        focus_manager_->SetFocusedViewWithReason(
177            last_focused_view, FocusManager::kReasonFocusRestore);
178      } else if (allow_deactivate_on_esc_) {
179        focused_view->GetWidget()->Deactivate();
180      }
181      return true;
182    }
183    case ui::VKEY_LEFT:
184      focus_manager_->AdvanceFocus(true);
185      return true;
186    case ui::VKEY_RIGHT:
187      focus_manager_->AdvanceFocus(false);
188      return true;
189    case ui::VKEY_HOME:
190      focus_manager_->SetFocusedViewWithReason(
191          GetFirstFocusableChild(), views::FocusManager::kReasonFocusTraversal);
192      return true;
193    case ui::VKEY_END:
194      focus_manager_->SetFocusedViewWithReason(
195          GetLastFocusableChild(), views::FocusManager::kReasonFocusTraversal);
196      return true;
197    default:
198      return false;
199  }
200}
201
202void AccessiblePaneView::SetVisible(bool flag) {
203  if (visible() && !flag && pane_has_focus_) {
204    RemovePaneFocus();
205    focus_manager_->RestoreFocusedView();
206  }
207  View::SetVisible(flag);
208}
209
210void AccessiblePaneView::GetAccessibleState(ui::AXViewState* state) {
211  state->role = ui::AX_ROLE_PANE;
212}
213
214void AccessiblePaneView::RequestFocus() {
215  SetPaneFocusAndFocusDefault();
216}
217
218////////////////////////////////////////////////////////////////////////////////
219// FocusChangeListener overrides:
220
221void AccessiblePaneView::OnWillChangeFocus(views::View* focused_before,
222                                           views::View* focused_now) {
223  //  Act when focus has changed.
224}
225
226void AccessiblePaneView::OnDidChangeFocus(views::View* focused_before,
227                                          views::View* focused_now) {
228  if (!focused_now)
229    return;
230
231  views::FocusManager::FocusChangeReason reason =
232      focus_manager_->focus_change_reason();
233
234  if (!ContainsForFocusSearch(this, focused_now) ||
235      reason == views::FocusManager::kReasonDirectFocusChange) {
236    // We should remove pane focus (i.e. make most of the controls
237    // not focusable again) because the focus has left the pane,
238    // or because the focus changed within the pane due to the user
239    // directly focusing to a specific view (e.g., clicking on it).
240    RemovePaneFocus();
241  }
242}
243
244////////////////////////////////////////////////////////////////////////////////
245// FocusTraversable overrides:
246
247views::FocusSearch* AccessiblePaneView::GetFocusSearch() {
248  DCHECK(pane_has_focus_);
249  return focus_search_.get();
250}
251
252views::FocusTraversable* AccessiblePaneView::GetFocusTraversableParent() {
253  DCHECK(pane_has_focus_);
254  return NULL;
255}
256
257views::View* AccessiblePaneView::GetFocusTraversableParentView() {
258  DCHECK(pane_has_focus_);
259  return NULL;
260}
261
262}  // namespace views
263