diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/find_in_page_controller.cc | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_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.cc | 706 |
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; + } +} |