summaryrefslogtreecommitdiffstats
path: root/chrome/browser/find_in_page_controller.cc
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/find_in_page_controller.cc
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/find_in_page_controller.cc')
-rw-r--r--chrome/browser/find_in_page_controller.cc706
1 files changed, 706 insertions, 0 deletions
diff --git a/chrome/browser/find_in_page_controller.cc b/chrome/browser/find_in_page_controller.cc
new file mode 100644
index 0000000..1129dc1
--- /dev/null
+++ b/chrome/browser/find_in_page_controller.cc
@@ -0,0 +1,706 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/find_in_page_controller.h"
+
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/find_in_page_view.h"
+#include "chrome/browser/find_notification_details.h"
+#include "chrome/browser/tab_contents.h"
+#include "chrome/browser/view_ids.h"
+#include "chrome/browser/views/bookmark_bar_view.h"
+#include "chrome/views/external_focus_tracker.h"
+#include "chrome/views/focus_manager.h"
+#include "chrome/views/native_scroll_bar.h"
+#include "chrome/views/hwnd_view_container.h"
+#include "chrome/views/view_storage.h"
+
+int FindInPageController::request_id_counter_ = 0;
+
+// The minimum space between the FindInPage window and the search result.
+static const int kMinFindWndDistanceFromSelection = 5;
+
+// The amount of space we expect the window border to take up.
+static const int kWindowBorderWidth = 3;
+
+////////////////////////////////////////////////////////////////////////////////
+// FindInPageController, public:
+
+FindInPageController::FindInPageController(TabContents* parent_tab,
+ HWND parent_hwnd)
+ : parent_tab_(parent_tab),
+ current_request_id_(request_id_counter_++),
+ parent_hwnd_(parent_hwnd),
+ find_dialog_animation_offset_(0),
+ show_on_tab_selection_(false),
+ focus_manager_(NULL),
+ old_accel_target_for_esc_(NULL) {
+ // Start listening to focus changes, so we can register and unregister our
+ // own handler for Escape.
+ SetFocusChangeListener(parent_hwnd);
+
+ // Don't let HWNDViewContainer manage our lifetime. We want our lifetime to
+ // coincide with WebContents.
+ HWNDViewContainer::set_delete_on_destroy(false);
+
+ view_ = new FindInPageView(this);
+
+ ChromeViews::FocusManager* focus_manager;
+ focus_manager = ChromeViews::FocusManager::GetFocusManager(parent_hwnd_);
+ DCHECK(focus_manager);
+
+ // Stores the currently focused view, and tracks focus changes so that we can
+ // restore focus when the find box is closed.
+ focus_tracker_.reset(new ChromeViews::ExternalFocusTracker(view_,
+ focus_manager));
+
+ // Figure out where to place the dialog, initialize and set the position.
+ gfx::Rect find_dlg_rect = GetDialogPosition(gfx::Rect());
+ set_window_style(WS_CHILD | WS_CLIPCHILDREN);
+ set_window_ex_style(WS_EX_TOPMOST);
+ HWNDViewContainer::Init(parent_hwnd, find_dlg_rect, view_, false);
+
+ // Start the process of animating the opening of the window.
+ animation_.reset(new SlideAnimation(this));
+ animation_->Show();
+
+ // We need to be notified about when results from a find operation are
+ // available.
+ NotificationService::current()->
+ AddObserver(this, NOTIFY_FIND_RESULT_AVAILABLE,
+ Source<TabContents>(parent_tab));
+}
+
+FindInPageController::~FindInPageController() {
+ Close();
+}
+
+// TODO(brettw) this should not be so complicated. The view should really be in
+// charge of these regions. CustomFrameWindow will do this for us. It will also
+// let us set a path for the window region which will avoid some logic here.
+void FindInPageController::UpdateWindowEdges(const gfx::Rect& new_pos) {
+ int w = new_pos.width();
+ int h = new_pos.height();
+
+ // This polygon array represents the outline of the background image for the
+ // dialog. Basically, it encompasses only the visible pixels of the
+ // concatenated find_dlg_LMR_bg images (where LMR = [left | middle | right]).
+ static const POINT polygon[] = {
+ {0, 0}, {0, 1}, {2, 3}, {2, 29}, {4, 31},
+ {4, 32}, {w+0, 32},
+ {w+0, 31}, {w+1, 31}, {w+3, 29}, {w+3, 3}, {w+6, 0}
+ };
+
+ // Find the largest x and y value in the polygon.
+ int max_x = 0, max_y = 0;
+ for (int i = 0; i < arraysize(polygon); i++) {
+ max_x = std::max(max_x, static_cast<int>(polygon[i].x));
+ max_y = std::max(max_y, static_cast<int>(polygon[i].y));
+ }
+
+ // We then create the polygon and use SetWindowRgn to force the window to draw
+ // only within that area. This region may get reduced in size below.
+ HRGN region = CreatePolygonRgn(polygon, arraysize(polygon), ALTERNATE);
+
+ // Are we animating?
+ if (find_dialog_animation_offset_ > 0) {
+ // The animation happens in two steps: First, we clip the window and then in
+ // GetDialogPosition we offset the window position so that it still looks
+ // attached to the toolbar as it grows. We clip the window by creating a
+ // rectangle region (that gradually increases as the animation progresses)
+ // and find the intersection between the two regions using CombineRgn.
+
+ // |y| shrinks as the animation progresses from the height of the view down
+ // to 0 (and reverses when closing).
+ int y = find_dialog_animation_offset_;
+ // |y| shrinking means the animation (visible) region gets larger. In other
+ // words: the rectangle grows upward (when the dialog is opening).
+ HRGN animation_region = CreateRectRgn(0, y, max_x, max_y);
+ // |region| will contain the intersected parts after calling this function:
+ CombineRgn(region, animation_region, region, RGN_AND);
+ DeleteObject(animation_region);
+
+ // Next, we need to increase the region a little bit to account for the
+ // curved edges that the view will draw to make it look like grows out of
+ // the toolbar.
+ POINT left_curve[] = {
+ {0, y+0}, {0, y+1}, {2, y+3}, {2, y+0}, {0, y+0}
+ };
+ POINT right_curve[] = {
+ {w+3, y+3}, {w+6, y+0}, {w+3, y+0}, {w+3, y+3}
+ };
+
+ // Combine the region for the curve on the left with our main region.
+ HRGN r = CreatePolygonRgn(left_curve, arraysize(left_curve), ALTERNATE);
+ CombineRgn(region, r, region, RGN_OR);
+ DeleteObject(r);
+
+ // Combine the region for the curve on the right with our main region.
+ r = CreatePolygonRgn(right_curve, arraysize(right_curve), ALTERNATE);
+ CombineRgn(region, r, region, RGN_OR);
+ DeleteObject(r);
+ }
+
+ // Now see if we need to truncate the region because parts of it obscures
+ // the main window border.
+ gfx::Rect dialog_bounds;
+ GetDialogBounds(&dialog_bounds);
+
+ // Calculate how much our current position overlaps our boundaries. If we
+ // overlap, it means we have too little space to draw the whole dialog and
+ // we allow overwriting the scrollbar before we start truncating our dialog.
+ //
+ // TODO(brettw) this constant is evil. This is the amount of room we've added
+ // to the window size, when we set the region, it can change the size.
+ static const int kAddedWidth = 14;
+ int difference = (curr_pos_relative_.right() - kAddedWidth) -
+ dialog_bounds.width() -
+ ChromeViews::NativeScrollBar::GetVerticalScrollBarWidth() +
+ 1;
+ if (difference > 0) {
+ POINT exclude[4];
+ exclude[0].x = max_x - difference; // Top left corner.
+ exclude[0].y = 0;
+
+ exclude[1].x = max_x; // Top right corner.
+ exclude[1].y = 0;
+
+ exclude[2].x = max_x; // Bottom right corner.
+ exclude[2].y = max_y;
+
+ exclude[3].x = max_x - difference; // Bottom left corner.
+ exclude[3].y = max_y;
+
+ // Subtract this region from the original region.
+ HRGN exclude_rgn = CreatePolygonRgn(exclude, arraysize(exclude), ALTERNATE);
+ int result = CombineRgn(region, region, exclude_rgn, RGN_DIFF);
+ DeleteObject(exclude_rgn);
+ }
+
+ // The system now owns the region, so we do not delete it.
+ SetWindowRgn(region, TRUE); // TRUE = Redraw.
+}
+
+void FindInPageController::Show() {
+ // Note: This function is called when the user presses Ctrl+F or switches back
+ // to the parent tab of the Find window (assuming the Find window has been
+ // opened at least once). If the Find window is already visible, we should
+ // just forward the command to the view so that it will select all text and
+ // grab focus. If the window is not visible, however, there are two scenarios:
+ if (!IsVisible() && !animation_->IsAnimating()) {
+ if (show_on_tab_selection_) {
+ // The tab just got re-selected and we need to show the window again
+ // (without animation). We also want to reset the window location so that
+ // we don't surprise the user by popping up to the left for no apparent
+ // reason.
+ gfx::Rect new_pos = GetDialogPosition(gfx::Rect());
+ SetDialogPosition(new_pos);
+ } else {
+ // The Find window was dismissed and we need to start the animation again.
+ animation_->Show();
+ }
+ }
+
+ view_->OnShow();
+}
+
+void FindInPageController::EndFindSession() {
+ if (IsVisible()) {
+ show_on_tab_selection_ = false;
+ animation_->Hide();
+
+ // We reset the match count here so that we don't show old results when the
+ // user has navigated to another page. We could alternatively achieve the
+ // same effect by nulling the search string, but then the user looses the
+ // last search that was entered, which can be frustrating if searching for
+ // the same string on multiple pages.
+ view_->ResetMatchCount();
+
+ // When we hide the window, we need to notify the renderer that we are done
+ // for now, so that we can abort the scoping effort and clear all the
+ // tick-marks and highlighting.
+ StopFinding(false); // false = don't clear selection on page.
+
+ // When we get dismissed we restore the focus to where it belongs.
+ RestoreSavedFocus();
+ }
+}
+
+void FindInPageController::Close() {
+ // We may already have been destroyed if the selection resulted in a tab
+ // switch which will have reactivated the browser window and closed us, so
+ // we need to check to see if we're still a window before trying to destroy
+ // ourself.
+ if (IsWindow())
+ DestroyWindow();
+}
+
+void FindInPageController::DidBecomeSelected() {
+ if (!IsVisible() && show_on_tab_selection_) {
+ Show();
+ show_on_tab_selection_ = false;
+ }
+}
+
+void FindInPageController::DidBecomeUnselected() {
+ if (::IsWindow(GetHWND()) && IsVisible()) {
+ // Finish any existing animations.
+ if (animation_->IsAnimating()) {
+ show_on_tab_selection_ = animation_->IsShowing();
+ animation_->End();
+ } else {
+ show_on_tab_selection_ = true;
+ }
+
+ ShowWindow(SW_HIDE);
+ }
+}
+
+void FindInPageController::StartFinding(bool forward_direction) {
+ if (find_string_.empty())
+ return;
+
+ bool find_next = last_find_string_ == find_string_;
+ if (!find_next)
+ current_request_id_ = request_id_counter_++;
+
+ last_find_string_ = find_string_;
+
+ parent_tab_->StartFinding(current_request_id_,
+ find_string_,
+ forward_direction,
+ false, // case sensitive
+ find_next);
+}
+
+void FindInPageController::StopFinding(bool clear_selection) {
+ last_find_string_ = L"";
+ parent_tab_->StopFinding(clear_selection);
+}
+
+void FindInPageController::MoveWindowIfNecessary(
+ const gfx::Rect& selection_rect) {
+ gfx::Rect new_pos = GetDialogPosition(selection_rect);
+ SetDialogPosition(new_pos);
+
+ // May need to redraw our frame to accomodate bookmark bar
+ // styles.
+ view_->SchedulePaint();
+}
+
+void FindInPageController::RespondToResize(const gfx::Size& new_size) {
+ if (!IsVisible())
+ return;
+
+ // We are only interested in changes to width.
+ if (window_size_.width() == new_size.width())
+ return;
+
+ // Save the new size so we can compare later and ignore future invocations
+ // of RespondToResize.
+ window_size_ = new_size;
+
+ gfx::Rect new_pos = GetDialogPosition(gfx::Rect());
+ SetDialogPosition(new_pos);
+}
+
+void FindInPageController::SetParent(HWND new_parent) {
+ DCHECK(new_parent);
+ if (parent_hwnd_ != new_parent) {
+ // Sync up the focus listener with the new focus manager.
+ SetFocusChangeListener(new_parent);
+
+ parent_hwnd_ = new_parent;
+ ::SetParent(GetHWND(), new_parent);
+
+ // The MSDN documentation specifies that you need to manually update the
+ // UI state after changing the parent.
+ ::SendMessage(new_parent,
+ WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, 0), 0);
+
+ // We have a new focus manager now, so start tracking with that.
+ focus_tracker_.reset(new ChromeViews::ExternalFocusTracker(view_,
+ focus_manager_));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FindInPageController, ChromeViews::HWNDViewContainer implementation:
+
+void FindInPageController::OnFinalMessage(HWND window) {
+ // We are exiting, so we no longer need to monitor focus changes.
+ focus_manager_->RemoveFocusChangeListener(this);
+
+ // Destroy the focus tracker now, otherwise by the time we're destroyed the
+ // focus manager the focus tracker is referencing may have already been
+ // destroyed resulting in the focus tracker trying to reference a deleted
+ // focus manager.
+ focus_tracker_.reset(NULL);
+
+ NotificationService::current()->
+ RemoveObserver(this, NOTIFY_FIND_RESULT_AVAILABLE,
+ Source<TabContents>(parent_tab_));
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// FindInPageController, ChromeViews::FocusChangeListener implementation:
+
+void FindInPageController::FocusWillChange(ChromeViews::View* focused_before,
+ ChromeViews::View* focused_now) {
+ // First we need to determine if one or both of the views passed in are child
+ // views of our view.
+ bool our_view_before = focused_before && view_->IsParentOf(focused_before);
+ bool our_view_now = focused_now && view_->IsParentOf(focused_now);
+
+ // When both our_view_before and our_view_now are false, it means focus is
+ // changing hands elsewhere in the application (and we shouldn't do anything).
+ // Similarly, when both are true, focus is changing hands within the Find
+ // window (and again, we should not do anything). We therefore only need to
+ // look at when we gain initial focus and when we loose it.
+ if (!our_view_before && our_view_now) {
+ // We are gaining focus from outside the Find window so we must register
+ // a handler for Escape.
+ RegisterEscAccelerator();
+ } else if (our_view_before && !our_view_now) {
+ // We are losing focus to something outside our window so we restore the
+ // original handler for Escape.
+ UnregisterEscAccelerator();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FindInPageController, ChromeViews::AcceleratorTarget implementation:
+
+bool FindInPageController::AcceleratorPressed(
+ const ChromeViews::Accelerator& accelerator) {
+ DCHECK(accelerator.GetKeyCode() == VK_ESCAPE); // We only expect Escape key.
+ // This will end the Find session and hide the window, causing it to loose
+ // focus and in the process unregister us as the handler for the Escape
+ // accelerator through the FocusWillChange event.
+ EndFindSession();
+
+ return true;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// FindInPageController, AnimationDelegate implementation:
+
+void FindInPageController::AnimationProgressed(
+ const Animation* animation) {
+ // First, we calculate how many pixels to slide the window.
+ find_dialog_animation_offset_ =
+ static_cast<int>((1.0 - animation_->GetCurrentValue()) *
+ view_->GetHeight());
+
+ // This call makes sure it appears in the right location, the size and shape
+ // is correct and that it slides in the right direction.
+ gfx::Rect find_dlg_rect = GetDialogPosition(gfx::Rect());
+ SetDialogPosition(find_dlg_rect);
+
+ // Let the view know if we are animating, and at which offset to draw the
+ // edges.
+ view_->animation_offset(find_dialog_animation_offset_);
+ view_->SchedulePaint();
+}
+
+void FindInPageController::AnimationEnded(
+ const Animation* animation) {
+ if (!animation_->IsShowing()) {
+ // Animation has finished closing.
+ find_dialog_animation_offset_ = 0;
+ ShowWindow(SW_HIDE);
+ } else {
+ // Animation has finished opening.
+ }
+}
+
+void FindInPageController::GetDialogBounds(gfx::Rect* bounds) {
+ DCHECK(bounds);
+
+ // We need to find the View for the toolbar because we want to visually
+ // extend it (draw our dialog slightly overlapping its border).
+ ChromeViews::View* root_view = ChromeViews::GetRootViewForHWND(parent_hwnd_);
+ ChromeViews::View* toolbar = NULL;
+ BookmarkBarView* bookmark_bar = NULL;
+ if (root_view) {
+ toolbar = root_view->GetViewByID(VIEW_ID_TOOLBAR);
+ bookmark_bar = static_cast<BookmarkBarView*>(
+ root_view->GetViewByID(VIEW_ID_BOOKMARK_BAR));
+ }
+
+ // To figure out what area we have to work with we need to know the rect for
+ // the browser window and the page content area starts and get a pointer to
+ // the toolbar to see where to draw the FindInPage dialog. If any of this
+ // fails, we return an empty rect.
+ CRect browser_client_rect, browser_window_rect, content_window_rect;
+ if (!::IsWindow(parent_hwnd_) ||
+ !::GetWindowRect(parent_tab_->GetContentHWND(), &content_window_rect) ||
+ !::GetWindowRect(parent_hwnd_, &browser_window_rect) ||
+ !::GetClientRect(parent_hwnd_, &browser_client_rect) ||
+ !toolbar) {
+ *bounds = gfx::Rect();
+ return;
+ }
+
+ // Start with browser's client rect, then change it below.
+ *bounds = gfx::Rect(browser_client_rect);
+
+ // Find the dimensions of the toolbar and the BookmarkBar.
+ CRect toolbar_bounds, bookmark_bar_bounds;
+ if (toolbar)
+ toolbar->GetBounds(&toolbar_bounds);
+
+ // If the bookmarks bar is available, we need to update our
+ // position and paint accordingly
+ if (bookmark_bar) {
+ if (bookmark_bar->IsAlwaysShown()) {
+ // If it's always on, don't try to blend with the toolbar.
+ view_->SetToolbarBlend(false);
+ } else {
+ // Else it's on, but hidden (in which case we should try
+ // to blend with the toolbar.
+ view_->SetToolbarBlend(true);
+ }
+
+ // If we're not in the New Tab page style, align ourselves with
+ // the bookmarks bar (this works even if the bar is hidden).
+ if (!bookmark_bar->IsNewTabPage() ||
+ bookmark_bar->IsAlwaysShown()) {
+ bookmark_bar->GetBounds(&bookmark_bar_bounds);
+ }
+ } else {
+ view_->SetToolbarBlend(true);
+ }
+
+ // Figure out at which y coordinate to draw the FindInPage window. If we have
+ // a toolbar (chrome) we want to overlap it by one pixel so that we look like
+ // we are part of the chrome (which will also draw our window on top of any
+ // info-bars, if present). If there is no chrome, then we have a constrained
+ // window or a Chrome application so we want to draw at the top of the page
+ // content (right beneath the title bar).
+ int y_pos_offset = 0;
+ if (!toolbar_bounds.IsRectEmpty()) {
+ // We have a toolbar (chrome), so overlap it by one pixel.
+ y_pos_offset = toolbar_bounds.BottomRight().y - 1;
+ // If there is a bookmark bar attached to the toolbar we should appear
+ // attached to it instead of the toolbar.
+ if (!bookmark_bar_bounds.IsRectEmpty())
+ y_pos_offset += bookmark_bar_bounds.Height() - 1;
+ } else {
+ // There is no toolbar, so this is probably a constrained window or a Chrome
+ // Application. This means we draw the Find window at the top of the page
+ // content window. We subtract 1 to overlap the light-blue line that is part
+ // of the title bar (so that we don't look detached by 1 pixel).
+ WINDOWINFO wi;
+ wi.cbSize = sizeof(WINDOWINFO);
+ GetWindowInfo(parent_hwnd_, &wi);
+ y_pos_offset = content_window_rect.TopLeft().y - wi.rcClient.top - 1;
+ }
+
+ bounds->Offset(0, y_pos_offset);
+
+ // We also want to stay well within limits of the vertical scrollbar and not
+ // draw on the window border (frame) itself either.
+ int width = ChromeViews::NativeScrollBar::GetVerticalScrollBarWidth();
+ width += kWindowBorderWidth;
+ bounds->set_x(bounds->x() + width);
+ bounds->set_width(bounds->width() - (2 * width));
+}
+
+gfx::Rect FindInPageController::GetDialogPosition(
+ gfx::Rect avoid_overlapping_rect) {
+ // Find the area we have to work with (after accounting for scrollbars, etc).
+ gfx::Rect dialog_bounds;
+ GetDialogBounds(&dialog_bounds);
+ if (dialog_bounds.IsEmpty())
+ return gfx::Rect();
+
+ // Ask the view how large an area it needs to draw on.
+ CSize prefsize;
+ view_->GetPreferredSize(&prefsize);
+
+ // Place the view in the top right corner of the dialog boundaries (top left
+ // for RTL languages).
+ gfx::Rect view_location;
+ int x = view_->UILayoutIsRightToLeft() ?
+ dialog_bounds.x() : dialog_bounds.width() - prefsize.cx;
+ int y = dialog_bounds.y();
+ view_location.SetRect(x, y, prefsize.cx, prefsize.cy);
+
+ // Make sure we don't go out of bounds to the left (right in RTL) if the
+ // window is too small to fit our dialog.
+ if (view_->UILayoutIsRightToLeft()) {
+ int boundary = dialog_bounds.width() - prefsize.cx;
+ view_location.set_x(std::min(view_location.x(), boundary));
+ } else {
+ view_location.set_x(std::max(view_location.x(), dialog_bounds.x()));
+ }
+
+ gfx::Rect new_pos = view_location;
+
+ // When we get Find results back, we specify a selection rect, which we
+ // should strive to avoid overlapping. But first, we need to offset the
+ // selection rect (if one was provided).
+ if (!avoid_overlapping_rect.IsEmpty()) {
+ // For comparison (with the Intersects function below) we need to account
+ // for the fact that we draw the Find dialog relative to the window,
+ // whereas the selection rect is relative to the page.
+ RECT frame_rect = {0}, webcontents_rect = {0};
+ ::GetWindowRect(parent_hwnd_, &frame_rect);
+ ::GetWindowRect(parent_tab_->GetContainerHWND(), &webcontents_rect);
+ avoid_overlapping_rect.Offset(0, webcontents_rect.top - frame_rect.top);
+ }
+
+ // If the selection rectangle intersects the current position on screen then
+ // we try to move our dialog to the left (right for RTL) of the selection
+ // rectangle.
+ if (!avoid_overlapping_rect.IsEmpty() &&
+ avoid_overlapping_rect.Intersects(new_pos)) {
+ if (view_->UILayoutIsRightToLeft()) {
+ new_pos.set_x(avoid_overlapping_rect.x() +
+ avoid_overlapping_rect.width() +
+ (2 * kMinFindWndDistanceFromSelection));
+
+ // If we moved it off-screen to the right, we won't move it at all.
+ if (new_pos.x() + new_pos.width() > dialog_bounds.width())
+ new_pos = view_location; // Reset.
+ } else {
+ new_pos.set_x(avoid_overlapping_rect.x() - new_pos.width() -
+ kMinFindWndDistanceFromSelection);
+
+ // If we moved it off-screen to the left, we won't move it at all.
+ if (new_pos.x() < 0)
+ new_pos = view_location; // Reset.
+ }
+ }
+
+ // While we are animating, the Find window will grow bottoms up so we need to
+ // re-position the dialog so that it appears to grow out of the toolbar.
+ if (find_dialog_animation_offset_ > 0)
+ new_pos.Offset(0, std::min(0, -find_dialog_animation_offset_));
+
+ return new_pos;
+}
+
+void FindInPageController::SetDialogPosition(const gfx::Rect& new_pos) {
+ if (new_pos.IsEmpty())
+ return;
+
+ // Make sure the window edges are clipped to just the visible region. We need
+ // to do this before changing position, so that when we animate the closure
+ // of it it doesn't look like the window crumbles into the toolbar.
+ UpdateWindowEdges(new_pos);
+
+ ::SetWindowPos(GetHWND(), HWND_TOP,
+ new_pos.x(), new_pos.y(), new_pos.width(), new_pos.height(),
+ SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_SHOWWINDOW);
+
+ curr_pos_relative_ = new_pos;
+}
+
+void FindInPageController::SetFocusChangeListener(HWND parent_hwnd) {
+ // When tabs get torn off the tab-strip they get a new window with a new
+ // FocusManager, which means we need to clean up old listener and start a new
+ // one with the new FocusManager.
+ if (focus_manager_) {
+ if (old_accel_target_for_esc_)
+ UnregisterEscAccelerator();
+ focus_manager_->RemoveFocusChangeListener(this);
+ }
+
+ // Register as a listener with the new focus manager.
+ focus_manager_ =
+ ChromeViews::FocusManager::GetFocusManager(parent_hwnd);
+ DCHECK(focus_manager_);
+ focus_manager_->AddFocusChangeListener(this);
+}
+
+void FindInPageController::RestoreSavedFocus() {
+ if (focus_tracker_.get() == NULL)
+ parent_tab_->Focus();
+ else
+ focus_tracker_->FocusLastFocusedExternalView();
+}
+
+void FindInPageController::RegisterEscAccelerator() {
+ ChromeViews::Accelerator escape(VK_ESCAPE, false, false, false);
+
+ ChromeViews::AcceleratorTarget* old_target =
+ focus_manager_->RegisterAccelerator(escape, this);
+
+ // We can get a FocusWillChange event setting focus to something that is
+ // already focused, without loosing focus first, for example when you switch
+ // to another application and come back. We must take care not to overwrite
+ // what the old accelerator target is in that case.
+ if (old_target != this)
+ old_accel_target_for_esc_ = old_target;
+}
+
+void FindInPageController::UnregisterEscAccelerator() {
+ // This DCHECK can happen if we get FocusWillChange events in incorrect order
+ // so that we think we are loosing focus twice.
+ DCHECK(old_accel_target_for_esc_ != NULL);
+ ChromeViews::Accelerator escape(VK_ESCAPE, false, false, false);
+ focus_manager_->RegisterAccelerator(escape, old_accel_target_for_esc_);
+ old_accel_target_for_esc_ = NULL;
+}
+
+void FindInPageController::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type) {
+ case NOTIFY_FIND_RESULT_AVAILABLE: {
+ Details<FindNotificationDetails> find_details(details);
+ // Ignore responses for requests other than the one we have most recently
+ // issued. That way we won't act on stale results when the user has
+ // already typed in another query.
+ if (view_ && find_details->request_id() == current_request_id_) {
+ view_->UpdateMatchCount(find_details->number_of_matches(),
+ find_details->final_update());
+ view_->UpdateActiveMatchOrdinal(find_details->active_match_ordinal());
+ view_->UpdateResultLabel();
+
+ // We now need to check if the window is obscuring the search results.
+ if (!find_details->selection_rect().IsEmpty())
+ MoveWindowIfNecessary(find_details->selection_rect());
+
+ // Once we find a match we no longer want to keep track of what had
+ // focus. EndFindSession will then set the focus to the page content.
+ if (find_details->number_of_matches() > 0)
+ focus_tracker_.reset(NULL);
+ }
+ break;
+ }
+ default:
+ NOTREACHED() << L"Notification not handled";
+ return;
+ }
+}