Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / views / find_bar_host.cc
blobb7bfc5b48cc7f41c816c9a25061b5d2a65acf628
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.
5 #include "chrome/browser/ui/views/find_bar_host.h"
7 #include <algorithm>
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/view_ids.h"
12 #include "chrome/browser/ui/views/find_bar_view.h"
13 #include "chrome/browser/ui/views/frame/browser_view.h"
14 #include "content/public/browser/render_view_host.h"
15 #include "content/public/browser/web_contents.h"
16 #include "ui/events/event.h"
17 #include "ui/events/keycodes/keyboard_codes.h"
18 #include "ui/views/focus/external_focus_tracker.h"
19 #include "ui/views/focus/view_storage.h"
20 #include "ui/views/widget/root_view.h"
21 #include "ui/views/widget/widget.h"
23 using content::NativeWebKeyboardEvent;
25 namespace chrome {
27 // Declared in browser_dialogs.h so others don't have to depend on our header.
28 FindBar* CreateFindBar(BrowserView* browser_view) {
29 return new FindBarHost(browser_view);
32 } // namespace chrome
34 ////////////////////////////////////////////////////////////////////////////////
35 // FindBarHost, public:
37 FindBarHost::FindBarHost(BrowserView* browser_view)
38 : DropdownBarHost(browser_view),
39 find_bar_controller_(NULL) {
40 FindBarView* find_bar_view = new FindBarView(this);
41 Init(browser_view->find_bar_host_view(), find_bar_view, find_bar_view);
44 FindBarHost::~FindBarHost() {
47 bool FindBarHost::MaybeForwardKeyEventToWebpage(
48 const ui::KeyEvent& key_event) {
49 switch (key_event.key_code()) {
50 case ui::VKEY_DOWN:
51 case ui::VKEY_UP:
52 case ui::VKEY_PRIOR:
53 case ui::VKEY_NEXT:
54 break;
55 case ui::VKEY_HOME:
56 case ui::VKEY_END:
57 if (key_event.IsControlDown())
58 break;
59 // Fall through.
60 default:
61 return false;
64 content::WebContents* contents = find_bar_controller_->web_contents();
65 if (!contents)
66 return false;
68 content::RenderViewHost* render_view_host = contents->GetRenderViewHost();
70 // Make sure we don't have a text field element interfering with keyboard
71 // input. Otherwise Up and Down arrow key strokes get eaten. "Nom Nom Nom".
72 render_view_host->ClearFocusedElement();
73 NativeWebKeyboardEvent event(key_event);
74 render_view_host->ForwardKeyboardEvent(event);
75 return true;
78 FindBarController* FindBarHost::GetFindBarController() const {
79 return find_bar_controller_;
82 void FindBarHost::SetFindBarController(FindBarController* find_bar_controller) {
83 find_bar_controller_ = find_bar_controller;
86 void FindBarHost::Show(bool animate) {
87 DropdownBarHost::Show(animate);
90 void FindBarHost::Hide(bool animate) {
91 DropdownBarHost::Hide(animate);
94 void FindBarHost::SetFocusAndSelection() {
95 DropdownBarHost::SetFocusAndSelection();
98 void FindBarHost::ClearResults(const FindNotificationDetails& results) {
99 find_bar_view()->UpdateForResult(results, base::string16());
102 void FindBarHost::StopAnimation() {
103 DropdownBarHost::StopAnimation();
106 void FindBarHost::MoveWindowIfNecessary(const gfx::Rect& selection_rect) {
107 // We only move the window if one is active for the current WebContents. If we
108 // don't check this, then SetDialogPosition below will end up making the Find
109 // Bar visible.
110 content::WebContents* web_contents = find_bar_controller_->web_contents();
111 if (!web_contents)
112 return;
114 FindTabHelper* find_tab_helper = FindTabHelper::FromWebContents(web_contents);
115 if (!find_tab_helper || !find_tab_helper->find_ui_active())
116 return;
118 gfx::Rect new_pos = GetDialogPosition(selection_rect);
119 SetDialogPosition(new_pos);
121 // May need to redraw our frame to accommodate bookmark bar styles.
122 view()->Layout(); // Bounds may have changed.
123 view()->SchedulePaint();
126 void FindBarHost::SetFindTextAndSelectedRange(
127 const base::string16& find_text,
128 const gfx::Range& selected_range) {
129 find_bar_view()->SetFindTextAndSelectedRange(find_text, selected_range);
132 base::string16 FindBarHost::GetFindText() {
133 return find_bar_view()->GetFindText();
136 gfx::Range FindBarHost::GetSelectedRange() {
137 return find_bar_view()->GetSelectedRange();
140 void FindBarHost::UpdateUIForFindResult(const FindNotificationDetails& result,
141 const base::string16& find_text) {
142 // Make sure match count is clear. It may get set again in UpdateForResult
143 // if enough data is available.
144 find_bar_view()->ClearMatchCount();
146 if (!find_text.empty())
147 find_bar_view()->UpdateForResult(result, find_text);
149 // We now need to check if the window is obscuring the search results.
150 MoveWindowIfNecessary(result.selection_rect());
152 // Once we find a match we no longer want to keep track of what had
153 // focus. EndFindSession will then set the focus to the page content.
154 if (result.number_of_matches() > 0)
155 ResetFocusTracker();
158 void FindBarHost::AudibleAlert() {
159 #if defined(OS_WIN)
160 MessageBeep(MB_OK);
161 #endif
164 bool FindBarHost::IsFindBarVisible() {
165 return DropdownBarHost::IsVisible();
168 void FindBarHost::RestoreSavedFocus() {
169 if (focus_tracker() == NULL) {
170 // TODO(brettw): Focus() should be on WebContentsView.
171 find_bar_controller_->web_contents()->Focus();
172 } else {
173 focus_tracker()->FocusLastFocusedExternalView();
177 bool FindBarHost::HasGlobalFindPasteboard() {
178 return false;
181 void FindBarHost::UpdateFindBarForChangedWebContents() {
184 FindBarTesting* FindBarHost::GetFindBarTesting() {
185 return this;
188 ////////////////////////////////////////////////////////////////////////////////
189 // FindBarWin, ui::AcceleratorTarget implementation:
191 bool FindBarHost::AcceleratorPressed(const ui::Accelerator& accelerator) {
192 ui::KeyboardCode key = accelerator.key_code();
193 if (key == ui::VKEY_RETURN && accelerator.IsCtrlDown()) {
194 // Ctrl+Enter closes the Find session and navigates any link that is active.
195 find_bar_controller_->EndFindSession(
196 FindBarController::kActivateSelectionOnPage,
197 FindBarController::kClearResultsInFindBox);
198 return true;
199 } else if (key == ui::VKEY_ESCAPE) {
200 // This will end the Find session and hide the window, causing it to loose
201 // focus and in the process unregister us as the handler for the Escape
202 // accelerator through the OnWillChangeFocus event.
203 find_bar_controller_->EndFindSession(
204 FindBarController::kKeepSelectionOnPage,
205 FindBarController::kKeepResultsInFindBox);
206 return true;
207 } else {
208 NOTREACHED() << "Unknown accelerator";
211 return false;
214 bool FindBarHost::CanHandleAccelerators() const {
215 return true;
218 ////////////////////////////////////////////////////////////////////////////////
219 // FindBarTesting implementation:
221 bool FindBarHost::GetFindBarWindowInfo(gfx::Point* position,
222 bool* fully_visible) {
223 if (!find_bar_controller_ ||
224 #if defined(OS_WIN) && !defined(USE_AURA)
225 !::IsWindow(host()->GetNativeView())) {
226 #else
227 false) {
228 // TODO(sky): figure out linux side.
229 // This is tricky due to asynchronous nature of x11.
230 // See bug http://crbug.com/28629.
231 #endif
232 if (position)
233 *position = gfx::Point();
234 if (fully_visible)
235 *fully_visible = false;
236 return false;
239 gfx::Rect window_rect = host()->GetWindowBoundsInScreen();
240 if (position)
241 *position = window_rect.origin();
242 if (fully_visible)
243 *fully_visible = IsVisible() && !IsAnimating();
244 return true;
247 base::string16 FindBarHost::GetFindSelectedText() {
248 return find_bar_view()->GetFindSelectedText();
251 base::string16 FindBarHost::GetMatchCountText() {
252 return find_bar_view()->GetMatchCountText();
255 int FindBarHost::GetWidth() {
256 return view()->width();
259 ////////////////////////////////////////////////////////////////////////////////
260 // Overridden from DropdownBarHost:
262 gfx::Rect FindBarHost::GetDialogPosition(gfx::Rect avoid_overlapping_rect) {
263 // Find the area we have to work with (after accounting for scrollbars, etc).
264 gfx::Rect widget_bounds;
265 GetWidgetBounds(&widget_bounds);
266 if (widget_bounds.IsEmpty())
267 return gfx::Rect();
269 // Ask the view how large an area it needs to draw on.
270 gfx::Size prefsize = view()->GetPreferredSize();
272 // Limit width to the available area.
273 if (widget_bounds.width() < prefsize.width())
274 prefsize.set_width(widget_bounds.width());
276 // Don't show the find bar if |widget_bounds| is not tall enough.
277 if (widget_bounds.height() < prefsize.height())
278 return gfx::Rect();
280 // Place the view in the top right corner of the widget boundaries (top left
281 // for RTL languages).
282 gfx::Rect view_location;
283 int x = widget_bounds.x();
284 if (!base::i18n::IsRTL())
285 x += widget_bounds.width() - prefsize.width();
286 int y = widget_bounds.y();
287 view_location.SetRect(x, y, prefsize.width(), prefsize.height());
289 // When we get Find results back, we specify a selection rect, which we
290 // should strive to avoid overlapping. But first, we need to offset the
291 // selection rect (if one was provided).
292 if (!avoid_overlapping_rect.IsEmpty()) {
293 // For comparison (with the Intersects function below) we need to account
294 // for the fact that we draw the Find widget relative to the Chrome frame,
295 // whereas the selection rect is relative to the page.
296 GetWidgetPositionNative(&avoid_overlapping_rect);
299 gfx::Rect new_pos = FindBarController::GetLocationForFindbarView(
300 view_location, widget_bounds, avoid_overlapping_rect);
302 // While we are animating, the Find window will grow bottoms up so we need to
303 // re-position the widget so that it appears to grow out of the toolbar.
304 if (animation_offset() > 0)
305 new_pos.Offset(0, std::min(0, -animation_offset()));
307 return new_pos;
310 void FindBarHost::SetDialogPosition(const gfx::Rect& new_pos) {
311 if (new_pos.IsEmpty())
312 return;
314 if (!host()->IsVisible())
315 host()->Show();
316 host()->SetBounds(new_pos);
318 // Tell the immersive mode controller about the find bar's new bounds. The
319 // immersive mode controller uses the bounds to keep the top-of-window views
320 // revealed when the mouse is hovered over the find bar.
321 browser_view()->immersive_mode_controller()->OnFindBarVisibleBoundsChanged(
322 host()->GetWindowBoundsInScreen());
325 void FindBarHost::GetWidgetBounds(gfx::Rect* bounds) {
326 DCHECK(bounds);
327 // The BrowserView does Layout for the components that we care about
328 // positioning relative to, so we ask it to tell us where we should go.
329 *bounds = browser_view()->GetFindBarBoundingBox();
332 void FindBarHost::RegisterAccelerators() {
333 DropdownBarHost::RegisterAccelerators();
335 // Register for Ctrl+Return.
336 ui::Accelerator escape(ui::VKEY_RETURN, ui::EF_CONTROL_DOWN);
337 focus_manager()->RegisterAccelerator(
338 escape, ui::AcceleratorManager::kNormalPriority, this);
341 void FindBarHost::UnregisterAccelerators() {
342 // Unregister Ctrl+Return.
343 ui::Accelerator escape(ui::VKEY_RETURN, ui::EF_CONTROL_DOWN);
344 focus_manager()->UnregisterAccelerator(escape, this);
346 DropdownBarHost::UnregisterAccelerators();
349 void FindBarHost::OnVisibilityChanged() {
350 // Tell the immersive mode controller about the find bar's bounds. The
351 // immersive mode controller uses the bounds to keep the top-of-window views
352 // revealed when the mouse is hovered over the find bar.
353 gfx::Rect visible_bounds;
354 if (IsVisible())
355 visible_bounds = host()->GetWindowBoundsInScreen();
356 browser_view()->immersive_mode_controller()->OnFindBarVisibleBoundsChanged(
357 visible_bounds);
360 ////////////////////////////////////////////////////////////////////////////////
361 // private:
363 void FindBarHost::GetWidgetPositionNative(gfx::Rect* avoid_overlapping_rect) {
364 gfx::Rect frame_rect = host()->GetTopLevelWidget()->GetWindowBoundsInScreen();
365 gfx::Rect webcontents_rect =
366 find_bar_controller_->web_contents()->GetViewBounds();
367 avoid_overlapping_rect->Offset(0, webcontents_rect.y() - frame_rect.y());