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 "chrome/browser/ui/views/find_bar_host.h" 6 7 #include <algorithm> 8 9 #include "chrome/browser/ui/find_bar/find_bar_controller.h" 10 #include "chrome/browser/ui/find_bar/find_tab_helper.h" 11 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" 12 #include "chrome/browser/ui/view_ids.h" 13 #include "chrome/browser/ui/views/find_bar_view.h" 14 #include "chrome/browser/ui/views/frame/browser_view.h" 15 #include "content/browser/renderer_host/render_view_host.h" 16 #include "content/browser/tab_contents/tab_contents.h" 17 #include "content/browser/tab_contents/tab_contents_view.h" 18 #include "ui/base/keycodes/keyboard_codes.h" 19 #include "views/focus/external_focus_tracker.h" 20 #include "views/focus/view_storage.h" 21 #include "views/widget/root_view.h" 22 #include "views/widget/widget.h" 23 24 namespace browser { 25 26 // Declared in browser_dialogs.h so others don't have to depend on our header. 27 FindBar* CreateFindBar(BrowserView* browser_view) { 28 return new FindBarHost(browser_view); 29 } 30 31 } // namespace browser 32 33 //////////////////////////////////////////////////////////////////////////////// 34 // FindBarHost, public: 35 36 FindBarHost::FindBarHost(BrowserView* browser_view) 37 : DropdownBarHost(browser_view), 38 find_bar_controller_(NULL) { 39 Init(new FindBarView(this)); 40 } 41 42 FindBarHost::~FindBarHost() { 43 } 44 45 bool FindBarHost::MaybeForwardKeyEventToWebpage( 46 const views::KeyEvent& key_event) { 47 if (!ShouldForwardKeyEventToWebpageNative(key_event)) { 48 // Native implementation says not to forward these events. 49 return false; 50 } 51 52 switch (key_event.key_code()) { 53 case ui::VKEY_DOWN: 54 case ui::VKEY_UP: 55 case ui::VKEY_PRIOR: 56 case ui::VKEY_NEXT: 57 break; 58 case ui::VKEY_HOME: 59 case ui::VKEY_END: 60 if (key_event.IsControlDown()) 61 break; 62 // Fall through. 63 default: 64 return false; 65 } 66 67 TabContentsWrapper* contents = find_bar_controller_->tab_contents(); 68 if (!contents) 69 return false; 70 71 RenderViewHost* render_view_host = contents->render_view_host(); 72 73 // Make sure we don't have a text field element interfering with keyboard 74 // input. Otherwise Up and Down arrow key strokes get eaten. "Nom Nom Nom". 75 render_view_host->ClearFocusedNode(); 76 NativeWebKeyboardEvent event = GetKeyboardEvent(contents->tab_contents(), 77 key_event); 78 render_view_host->ForwardKeyboardEvent(event); 79 return true; 80 } 81 82 FindBarController* FindBarHost::GetFindBarController() const { 83 return find_bar_controller_; 84 } 85 86 void FindBarHost::SetFindBarController(FindBarController* find_bar_controller) { 87 find_bar_controller_ = find_bar_controller; 88 } 89 90 void FindBarHost::Show(bool animate) { 91 DropdownBarHost::Show(animate); 92 } 93 94 void FindBarHost::Hide(bool animate) { 95 DropdownBarHost::Hide(animate); 96 } 97 98 void FindBarHost::SetFocusAndSelection() { 99 DropdownBarHost::SetFocusAndSelection(); 100 } 101 102 void FindBarHost::ClearResults(const FindNotificationDetails& results) { 103 find_bar_view()->UpdateForResult(results, string16()); 104 } 105 106 void FindBarHost::StopAnimation() { 107 DropdownBarHost::StopAnimation(); 108 } 109 110 void FindBarHost::MoveWindowIfNecessary(const gfx::Rect& selection_rect, 111 bool no_redraw) { 112 // We only move the window if one is active for the current TabContents. If we 113 // don't check this, then SetWidgetPosition below will end up making the Find 114 // Bar visible. 115 if (!find_bar_controller_->tab_contents() || 116 !find_bar_controller_-> 117 tab_contents()->find_tab_helper()->find_ui_active()) { 118 return; 119 } 120 121 gfx::Rect new_pos = GetDialogPosition(selection_rect); 122 SetDialogPosition(new_pos, no_redraw); 123 124 // May need to redraw our frame to accommodate bookmark bar styles. 125 view()->SchedulePaint(); 126 } 127 128 void FindBarHost::SetFindText(const string16& find_text) { 129 find_bar_view()->SetFindText(find_text); 130 } 131 132 void FindBarHost::UpdateUIForFindResult(const FindNotificationDetails& result, 133 const string16& find_text) { 134 // Make sure match count is clear. It may get set again in UpdateForResult 135 // if enough data is available. 136 find_bar_view()->ClearMatchCount(); 137 138 if (!find_text.empty()) 139 find_bar_view()->UpdateForResult(result, find_text); 140 141 // We now need to check if the window is obscuring the search results. 142 if (!result.selection_rect().IsEmpty()) 143 MoveWindowIfNecessary(result.selection_rect(), false); 144 145 // Once we find a match we no longer want to keep track of what had 146 // focus. EndFindSession will then set the focus to the page content. 147 if (result.number_of_matches() > 0) 148 ResetFocusTracker(); 149 } 150 151 bool FindBarHost::IsFindBarVisible() { 152 return DropdownBarHost::IsVisible(); 153 } 154 155 void FindBarHost::RestoreSavedFocus() { 156 if (focus_tracker() == NULL) { 157 // TODO(brettw) Focus() should be on TabContentsView. 158 find_bar_controller_->tab_contents()->tab_contents()->Focus(); 159 } else { 160 focus_tracker()->FocusLastFocusedExternalView(); 161 } 162 } 163 164 FindBarTesting* FindBarHost::GetFindBarTesting() { 165 return this; 166 } 167 168 //////////////////////////////////////////////////////////////////////////////// 169 // FindBarWin, views::AcceleratorTarget implementation: 170 171 bool FindBarHost::AcceleratorPressed(const views::Accelerator& accelerator) { 172 ui::KeyboardCode key = accelerator.GetKeyCode(); 173 if (key == ui::VKEY_RETURN && accelerator.IsCtrlDown()) { 174 // Ctrl+Enter closes the Find session and navigates any link that is active. 175 find_bar_controller_->EndFindSession(FindBarController::kActivateSelection); 176 } else if (key == ui::VKEY_ESCAPE) { 177 // This will end the Find session and hide the window, causing it to loose 178 // focus and in the process unregister us as the handler for the Escape 179 // accelerator through the FocusWillChange event. 180 find_bar_controller_->EndFindSession(FindBarController::kKeepSelection); 181 } else { 182 NOTREACHED() << "Unknown accelerator"; 183 } 184 185 return true; 186 } 187 188 //////////////////////////////////////////////////////////////////////////////// 189 // FindBarTesting implementation: 190 191 bool FindBarHost::GetFindBarWindowInfo(gfx::Point* position, 192 bool* fully_visible) { 193 if (!find_bar_controller_ || 194 #if defined(OS_WIN) 195 !::IsWindow(host()->GetNativeView())) { 196 #else 197 false) { 198 // TODO(sky): figure out linux side. 199 // This is tricky due to asynchronous nature of x11. 200 // See bug http://crbug.com/28629. 201 #endif 202 if (position) 203 *position = gfx::Point(); 204 if (fully_visible) 205 *fully_visible = false; 206 return false; 207 } 208 209 gfx::Rect window_rect = host()->GetWindowScreenBounds(); 210 if (position) 211 *position = window_rect.origin(); 212 if (fully_visible) 213 *fully_visible = IsVisible() && !IsAnimating(); 214 return true; 215 } 216 217 string16 FindBarHost::GetFindText() { 218 return find_bar_view()->GetFindText(); 219 } 220 221 string16 FindBarHost::GetFindSelectedText() { 222 return find_bar_view()->GetFindSelectedText(); 223 } 224 225 string16 FindBarHost::GetMatchCountText() { 226 return find_bar_view()->GetMatchCountText(); 227 } 228 229 //////////////////////////////////////////////////////////////////////////////// 230 // Overridden from DropdownBarHost: 231 232 gfx::Rect FindBarHost::GetDialogPosition(gfx::Rect avoid_overlapping_rect) { 233 // Find the area we have to work with (after accounting for scrollbars, etc). 234 gfx::Rect widget_bounds; 235 GetWidgetBounds(&widget_bounds); 236 if (widget_bounds.IsEmpty()) 237 return gfx::Rect(); 238 239 // Ask the view how large an area it needs to draw on. 240 gfx::Size prefsize = view()->GetPreferredSize(); 241 242 // Place the view in the top right corner of the widget boundaries (top left 243 // for RTL languages). 244 gfx::Rect view_location; 245 int x = widget_bounds.x(); 246 if (!base::i18n::IsRTL()) 247 x += widget_bounds.width() - prefsize.width(); 248 int y = widget_bounds.y(); 249 view_location.SetRect(x, y, prefsize.width(), prefsize.height()); 250 251 // When we get Find results back, we specify a selection rect, which we 252 // should strive to avoid overlapping. But first, we need to offset the 253 // selection rect (if one was provided). 254 if (!avoid_overlapping_rect.IsEmpty()) { 255 // For comparison (with the Intersects function below) we need to account 256 // for the fact that we draw the Find widget relative to the Chrome frame, 257 // whereas the selection rect is relative to the page. 258 GetWidgetPositionNative(&avoid_overlapping_rect); 259 } 260 261 gfx::Rect new_pos = FindBarController::GetLocationForFindbarView( 262 view_location, widget_bounds, avoid_overlapping_rect); 263 264 // While we are animating, the Find window will grow bottoms up so we need to 265 // re-position the widget so that it appears to grow out of the toolbar. 266 if (animation_offset() > 0) 267 new_pos.Offset(0, std::min(0, -animation_offset())); 268 269 return new_pos; 270 } 271 272 void FindBarHost::SetDialogPosition(const gfx::Rect& new_pos, bool no_redraw) { 273 if (new_pos.IsEmpty()) 274 return; 275 276 // Make sure the window edges are clipped to just the visible region. We need 277 // to do this before changing position, so that when we animate the closure 278 // of it it doesn't look like the window crumbles into the toolbar. 279 UpdateWindowEdges(new_pos); 280 281 SetWidgetPositionNative(new_pos, no_redraw); 282 } 283 284 void FindBarHost::GetWidgetBounds(gfx::Rect* bounds) { 285 DCHECK(bounds); 286 // The BrowserView does Layout for the components that we care about 287 // positioning relative to, so we ask it to tell us where we should go. 288 *bounds = browser_view()->GetFindBarBoundingBox(); 289 } 290 291 void FindBarHost::RegisterAccelerators() { 292 DropdownBarHost::RegisterAccelerators(); 293 294 // Register for Ctrl+Return. 295 views::Accelerator escape(ui::VKEY_RETURN, false, true, false); 296 focus_manager()->RegisterAccelerator(escape, this); 297 } 298 299 void FindBarHost::UnregisterAccelerators() { 300 // Unregister Ctrl+Return. 301 views::Accelerator escape(ui::VKEY_RETURN, false, true, false); 302 focus_manager()->UnregisterAccelerator(escape, this); 303 304 DropdownBarHost::UnregisterAccelerators(); 305 } 306 307 //////////////////////////////////////////////////////////////////////////////// 308 // private: 309 310 FindBarView* FindBarHost::find_bar_view() { 311 return static_cast<FindBarView*>(view()); 312 } 313