1 // Copyright (c) 2011 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 "base/logging.h" 6 #include "chrome/browser/ui/view_ids.h" 7 #include "chrome/browser/ui/views/accessible_pane_view.h" 8 #include "chrome/browser/ui/views/frame/browser_view.h" 9 #include "chrome/browser/ui/views/location_bar/location_bar_view.h" 10 #include "ui/base/accessibility/accessible_view_state.h" 11 #include "views/controls/button/menu_button.h" 12 #include "views/controls/native/native_view_host.h" 13 #include "views/focus/focus_search.h" 14 #include "views/focus/view_storage.h" 15 #include "views/widget/tooltip_manager.h" 16 #include "views/widget/widget.h" 17 18 AccessiblePaneView::AccessiblePaneView() 19 : pane_has_focus_(false), 20 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), 21 focus_manager_(NULL), 22 home_key_(ui::VKEY_HOME, false, false, false), 23 end_key_(ui::VKEY_END, false, false, false), 24 escape_key_(ui::VKEY_ESCAPE, false, false, false), 25 left_key_(ui::VKEY_LEFT, false, false, false), 26 right_key_(ui::VKEY_RIGHT, false, false, false), 27 last_focused_view_storage_id_(-1) { 28 focus_search_.reset(new views::FocusSearch(this, true, true)); 29 } 30 31 AccessiblePaneView::~AccessiblePaneView() { 32 if (pane_has_focus_) { 33 focus_manager_->RemoveFocusChangeListener(this); 34 } 35 } 36 37 bool AccessiblePaneView::SetPaneFocus(int view_storage_id, 38 views::View* initial_focus) { 39 if (!IsVisible()) 40 return false; 41 42 // Save the storage id to the last focused view. This would be used to request 43 // focus to the view when the traversal is ended. 44 last_focused_view_storage_id_ = view_storage_id; 45 46 if (!focus_manager_) 47 focus_manager_ = GetFocusManager(); 48 49 // Use the provided initial focus if it's visible and enabled, otherwise 50 // use the first focusable child. 51 if (!initial_focus || 52 !Contains(initial_focus) || 53 !initial_focus->IsVisible() || 54 !initial_focus->IsEnabled()) { 55 initial_focus = GetFirstFocusableChild(); 56 } 57 58 // Return false if there are no focusable children. 59 if (!initial_focus) 60 return false; 61 62 // Set focus to the initial view. If it's a location bar, use a special 63 // method that tells it to select all, also. 64 if (initial_focus->GetClassName() == LocationBarView::kViewClassName) { 65 static_cast<LocationBarView*>(initial_focus)->FocusLocation(true); 66 } else { 67 focus_manager_->SetFocusedView(initial_focus); 68 } 69 70 // If we already have pane focus, we're done. 71 if (pane_has_focus_) 72 return true; 73 74 // Otherwise, set accelerators and start listening for focus change events. 75 pane_has_focus_ = true; 76 focus_manager_->RegisterAccelerator(home_key_, this); 77 focus_manager_->RegisterAccelerator(end_key_, this); 78 focus_manager_->RegisterAccelerator(escape_key_, this); 79 focus_manager_->RegisterAccelerator(left_key_, this); 80 focus_manager_->RegisterAccelerator(right_key_, this); 81 focus_manager_->AddFocusChangeListener(this); 82 83 return true; 84 } 85 86 bool AccessiblePaneView::SetPaneFocusAndFocusDefault( 87 int view_storage_id) { 88 return SetPaneFocus(view_storage_id, GetDefaultFocusableChild()); 89 } 90 91 views::View* AccessiblePaneView::GetDefaultFocusableChild() { 92 return NULL; 93 } 94 95 void AccessiblePaneView::RemovePaneFocus() { 96 focus_manager_->RemoveFocusChangeListener(this); 97 pane_has_focus_ = false; 98 99 focus_manager_->UnregisterAccelerator(home_key_, this); 100 focus_manager_->UnregisterAccelerator(end_key_, this); 101 focus_manager_->UnregisterAccelerator(escape_key_, this); 102 focus_manager_->UnregisterAccelerator(left_key_, this); 103 focus_manager_->UnregisterAccelerator(right_key_, this); 104 } 105 106 void AccessiblePaneView::LocationBarSelectAll() { 107 views::View* focused_view = GetFocusManager()->GetFocusedView(); 108 if (focused_view && 109 focused_view->GetClassName() == LocationBarView::kViewClassName) { 110 static_cast<LocationBarView*>(focused_view)->SelectAll(); 111 } 112 } 113 114 void AccessiblePaneView::RestoreLastFocusedView() { 115 views::ViewStorage* view_storage = views::ViewStorage::GetInstance(); 116 views::View* last_focused_view = 117 view_storage->RetrieveView(last_focused_view_storage_id_); 118 if (last_focused_view) { 119 focus_manager_->SetFocusedViewWithReason( 120 last_focused_view, views::FocusManager::kReasonFocusRestore); 121 } else { 122 // Focus the location bar 123 views::View* view = GetAncestorWithClassName(BrowserView::kViewClassName); 124 if (view) { 125 BrowserView* browser_view = static_cast<BrowserView*>(view); 126 browser_view->SetFocusToLocationBar(false); 127 } 128 } 129 } 130 131 views::View* AccessiblePaneView::GetFirstFocusableChild() { 132 FocusTraversable* dummy_focus_traversable; 133 views::View* dummy_focus_traversable_view; 134 return focus_search_->FindNextFocusableView( 135 NULL, false, views::FocusSearch::DOWN, false, 136 &dummy_focus_traversable, &dummy_focus_traversable_view); 137 } 138 139 views::View* AccessiblePaneView::GetLastFocusableChild() { 140 FocusTraversable* dummy_focus_traversable; 141 views::View* dummy_focus_traversable_view; 142 return focus_search_->FindNextFocusableView( 143 this, true, views::FocusSearch::DOWN, false, 144 &dummy_focus_traversable, &dummy_focus_traversable_view); 145 } 146 147 //////////////////////////////////////////////////////////////////////////////// 148 // View overrides: 149 150 views::FocusTraversable* AccessiblePaneView::GetPaneFocusTraversable() { 151 if (pane_has_focus_) 152 return this; 153 else 154 return NULL; 155 } 156 157 bool AccessiblePaneView::AcceleratorPressed( 158 const views::Accelerator& accelerator) { 159 // Special case: don't handle any accelerators for the location bar, 160 // so that it behaves exactly the same whether you focus it with Ctrl+L 161 // or F6 or Alt+D or Alt+Shift+T. 162 views::View* focused_view = focus_manager_->GetFocusedView(); 163 if ((focused_view->GetClassName() == LocationBarView::kViewClassName || 164 focused_view->GetClassName() == views::NativeViewHost::kViewClassName)) { 165 return false; 166 } 167 168 switch (accelerator.GetKeyCode()) { 169 case ui::VKEY_ESCAPE: 170 RemovePaneFocus(); 171 RestoreLastFocusedView(); 172 return true; 173 case ui::VKEY_LEFT: 174 focus_manager_->AdvanceFocus(true); 175 return true; 176 case ui::VKEY_RIGHT: 177 focus_manager_->AdvanceFocus(false); 178 return true; 179 case ui::VKEY_HOME: 180 focus_manager_->SetFocusedViewWithReason( 181 GetFirstFocusableChild(), views::FocusManager::kReasonFocusTraversal); 182 return true; 183 case ui::VKEY_END: 184 focus_manager_->SetFocusedViewWithReason( 185 GetLastFocusableChild(), views::FocusManager::kReasonFocusTraversal); 186 return true; 187 default: 188 return false; 189 } 190 } 191 192 void AccessiblePaneView::SetVisible(bool flag) { 193 if (IsVisible() && !flag && pane_has_focus_) { 194 RemovePaneFocus(); 195 RestoreLastFocusedView(); 196 } 197 View::SetVisible(flag); 198 } 199 200 void AccessiblePaneView::GetAccessibleState(ui::AccessibleViewState* state) { 201 state->role = ui::AccessibilityTypes::ROLE_PANE; 202 } 203 204 //////////////////////////////////////////////////////////////////////////////// 205 // FocusChangeListener overrides: 206 207 void AccessiblePaneView::FocusWillChange(views::View* focused_before, 208 views::View* focused_now) { 209 if (!focused_now) 210 return; 211 212 views::FocusManager::FocusChangeReason reason = 213 focus_manager_->focus_change_reason(); 214 215 if (focused_now->GetClassName() == LocationBarView::kViewClassName && 216 reason == views::FocusManager::kReasonFocusTraversal) { 217 // Tabbing to the location bar should select all. Defer so that it happens 218 // after the focus. 219 MessageLoop::current()->PostTask( 220 FROM_HERE, method_factory_.NewRunnableMethod( 221 &AccessiblePaneView::LocationBarSelectAll)); 222 } 223 224 if (!Contains(focused_now) || 225 reason == views::FocusManager::kReasonDirectFocusChange) { 226 // We should remove pane focus (i.e. make most of the controls 227 // not focusable again) either because the focus is leaving the pane, 228 // or because the focus changed within the pane due to the user 229 // directly focusing to a specific view (e.g., clicking on it). 230 // 231 // Defer rather than calling RemovePaneFocus right away, because we can't 232 // remove |this| as a focus change listener while FocusManager is in the 233 // middle of iterating over the list of listeners. 234 MessageLoop::current()->PostTask( 235 FROM_HERE, method_factory_.NewRunnableMethod( 236 &AccessiblePaneView::RemovePaneFocus)); 237 } 238 } 239 240 //////////////////////////////////////////////////////////////////////////////// 241 // FocusTraversable overrides: 242 243 views::FocusSearch* AccessiblePaneView::GetFocusSearch() { 244 DCHECK(pane_has_focus_); 245 return focus_search_.get(); 246 } 247 248 views::FocusTraversable* AccessiblePaneView::GetFocusTraversableParent() { 249 DCHECK(pane_has_focus_); 250 return NULL; 251 } 252 253 views::View* AccessiblePaneView::GetFocusTraversableParentView() { 254 DCHECK(pane_has_focus_); 255 return NULL; 256 } 257