Home | History | Annotate | Download | only in views
      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