find_bar_host.cc revision dc0f95d653279beabeb9817299e2902918ba123e
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
24namespace browser {
25
26// Declared in browser_dialogs.h so others don't have to depend on our header.
27FindBar* CreateFindBar(BrowserView* browser_view) {
28  return new FindBarHost(browser_view);
29}
30
31}  // namespace browser
32
33////////////////////////////////////////////////////////////////////////////////
34// FindBarHost, public:
35
36FindBarHost::FindBarHost(BrowserView* browser_view)
37    : DropdownBarHost(browser_view),
38      find_bar_controller_(NULL) {
39  Init(new FindBarView(this));
40}
41
42FindBarHost::~FindBarHost() {
43}
44
45bool 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
82FindBarController* FindBarHost::GetFindBarController() const {
83  return find_bar_controller_;
84}
85
86void FindBarHost::SetFindBarController(FindBarController* find_bar_controller) {
87  find_bar_controller_ = find_bar_controller;
88}
89
90void FindBarHost::Show(bool animate) {
91  DropdownBarHost::Show(animate);
92}
93
94void FindBarHost::Hide(bool animate) {
95  DropdownBarHost::Hide(animate);
96}
97
98void FindBarHost::SetFocusAndSelection() {
99  DropdownBarHost::SetFocusAndSelection();
100}
101
102void FindBarHost::ClearResults(const FindNotificationDetails& results) {
103  find_bar_view()->UpdateForResult(results, string16());
104}
105
106void FindBarHost::StopAnimation() {
107  DropdownBarHost::StopAnimation();
108}
109
110void 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
128void FindBarHost::SetFindText(const string16& find_text) {
129  find_bar_view()->SetFindText(find_text);
130}
131
132void 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
151bool FindBarHost::IsFindBarVisible() {
152  return DropdownBarHost::IsVisible();
153}
154
155void 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
164FindBarTesting* FindBarHost::GetFindBarTesting() {
165  return this;
166}
167
168////////////////////////////////////////////////////////////////////////////////
169// FindBarWin, views::AcceleratorTarget implementation:
170
171bool 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
191bool 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
217string16 FindBarHost::GetFindText() {
218  return find_bar_view()->GetFindText();
219}
220
221string16 FindBarHost::GetFindSelectedText() {
222  return find_bar_view()->GetFindSelectedText();
223}
224
225string16 FindBarHost::GetMatchCountText() {
226  return find_bar_view()->GetMatchCountText();
227}
228
229////////////////////////////////////////////////////////////////////////////////
230// Overridden from DropdownBarHost:
231
232gfx::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
272void 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
284void 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
291void 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
299void 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
310FindBarView* FindBarHost::find_bar_view() {
311  return static_cast<FindBarView*>(view());
312}
313