blob: 9b5bc7d75f6a9bfe13997df76a1601a051758e7e [file] [log] [blame]
[email protected]92e175a2009-03-12 17:03:371// Copyright (c) 2009 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/find_bar_controller.h"
6
[email protected]d46dccd2009-07-29 23:00:257#include "app/l10n_util.h"
[email protected]92e175a2009-03-12 17:03:378#include "build/build_config.h"
9#include "chrome/browser/find_bar.h"
10#include "chrome/browser/tab_contents/navigation_entry.h"
11#include "chrome/common/notification_service.h"
[email protected]57c6a652009-05-04 07:58:3412#include "chrome/browser/tab_contents/tab_contents.h"
[email protected]92e175a2009-03-12 17:03:3713
[email protected]d46dccd2009-07-29 23:00:2514// The minimum space between the FindInPage window and the search result.
15static const int kMinFindWndDistanceFromSelection = 5;
16
[email protected]92e175a2009-03-12 17:03:3717FindBarController::FindBarController(FindBar* find_bar)
[email protected]c07cfb802009-05-14 15:53:1318 : find_bar_(find_bar),
19 tab_contents_(NULL),
20 last_reported_matchcount_(0) {
[email protected]92e175a2009-03-12 17:03:3721}
22
23FindBarController::~FindBarController() {
[email protected]57c6a652009-05-04 07:58:3424 DCHECK(!tab_contents_);
[email protected]92e175a2009-03-12 17:03:3725}
26
27void FindBarController::Show() {
28 // Only show the animation if we're not already showing a find bar for the
[email protected]57c6a652009-05-04 07:58:3429 // selected TabContents.
30 if (!tab_contents_->find_ui_active()) {
31 tab_contents_->set_find_ui_active(true);
[email protected]fb6de392010-01-08 18:35:5432 find_bar_->Show(true);
[email protected]92e175a2009-03-12 17:03:3733 }
34 find_bar_->SetFocusAndSelection();
35}
36
37void FindBarController::EndFindSession() {
38 find_bar_->Hide(true);
39
[email protected]57c6a652009-05-04 07:58:3440 // |tab_contents_| can be NULL for a number of reasons, for example when the
[email protected]92e175a2009-03-12 17:03:3741 // tab is closing. We must guard against that case. See issue 8030.
[email protected]57c6a652009-05-04 07:58:3442 if (tab_contents_) {
[email protected]92e175a2009-03-12 17:03:3743 // When we hide the window, we need to notify the renderer that we are done
44 // for now, so that we can abort the scoping effort and clear all the
45 // tickmarks and highlighting.
[email protected]57c6a652009-05-04 07:58:3446 tab_contents_->StopFinding(false); // false = don't clear selection on
[email protected]92e175a2009-03-12 17:03:3747 // page.
[email protected]57c6a652009-05-04 07:58:3448 find_bar_->ClearResults(tab_contents_->find_result());
[email protected]92e175a2009-03-12 17:03:3749
50 // When we get dismissed we restore the focus to where it belongs.
51 find_bar_->RestoreSavedFocus();
52 }
53}
54
[email protected]57c6a652009-05-04 07:58:3455void FindBarController::ChangeTabContents(TabContents* contents) {
56 if (tab_contents_) {
[email protected]f987e3d2009-05-22 01:53:0257 registrar_.RemoveAll();
[email protected]92e175a2009-03-12 17:03:3758 find_bar_->StopAnimation();
59 }
60
[email protected]57c6a652009-05-04 07:58:3461 tab_contents_ = contents;
[email protected]92e175a2009-03-12 17:03:3762
[email protected]57c6a652009-05-04 07:58:3463 // Hide any visible find window from the previous tab if NULL |tab_contents|
[email protected]92e175a2009-03-12 17:03:3764 // is passed in or if the find UI is not active in the new tab.
65 if (find_bar_->IsFindBarVisible() &&
[email protected]57c6a652009-05-04 07:58:3466 (!tab_contents_ || !tab_contents_->find_ui_active())) {
[email protected]92e175a2009-03-12 17:03:3767 find_bar_->Hide(false);
68 }
69
[email protected]c07cfb802009-05-14 15:53:1370 if (!tab_contents_)
71 return;
[email protected]92e175a2009-03-12 17:03:3772
[email protected]f987e3d2009-05-22 01:53:0273 registrar_.Add(this, NotificationType::FIND_RESULT_AVAILABLE,
74 Source<TabContents>(tab_contents_));
75 registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
76 Source<NavigationController>(&tab_contents_->controller()));
[email protected]732286a2009-04-13 22:16:0377
[email protected]6a4f5af22009-09-23 22:43:0078#if !defined(OS_MACOSX)
[email protected]c07cfb802009-05-14 15:53:1379 // Find out what we should show in the find text box. Usually, this will be
80 // the last search in this tab, but if no search has been issued in this tab
81 // we use the last search string (from any tab).
82 string16 find_string = tab_contents_->find_text();
83 if (find_string.empty())
84 find_string = tab_contents_->find_prepopulate_text();
[email protected]92e175a2009-03-12 17:03:3785
[email protected]c07cfb802009-05-14 15:53:1386 // Update the find bar with existing results and search text, regardless of
87 // whether or not the find bar is visible, so that if it's subsequently
88 // shown it is showing the right state for this tab. We update the find text
89 // _first_ since the FindBarView checks its emptiness to see if it should
90 // clear the result count display when there's nothing in the box.
91 find_bar_->SetFindText(find_string);
[email protected]6a4f5af22009-09-23 22:43:0092#else
93 // Having a per-tab find_string is not compatible with OS X's find pasteboard,
94 // so we always have the same find text in all find bars. This is done through
95 // the find pasteboard mechanism, so don't set the text here.
96#endif
[email protected]92e175a2009-03-12 17:03:3797
[email protected]c07cfb802009-05-14 15:53:1398 if (tab_contents_->find_ui_active()) {
99 // A tab with a visible find bar just got selected and we need to show the
100 // find bar but without animation since it was already animated into its
101 // visible state. We also want to reset the window location so that
102 // we don't surprise the user by popping up to the left for no apparent
103 // reason.
[email protected]fb6de392010-01-08 18:35:54104 find_bar_->Show(false);
[email protected]92e175a2009-03-12 17:03:37105 }
[email protected]c07cfb802009-05-14 15:53:13106
107 UpdateFindBarForCurrentResult();
[email protected]92e175a2009-03-12 17:03:37108}
109
110////////////////////////////////////////////////////////////////////////////////
[email protected]5c9e97a2009-09-09 23:48:30111// FindBarHost, NotificationObserver implementation:
[email protected]92e175a2009-03-12 17:03:37112
113void FindBarController::Observe(NotificationType type,
114 const NotificationSource& source,
115 const NotificationDetails& details) {
116 if (type == NotificationType::FIND_RESULT_AVAILABLE) {
117 // Don't update for notifications from TabContentses other than the one we
118 // are actively tracking.
[email protected]57c6a652009-05-04 07:58:34119 if (Source<TabContents>(source).ptr() == tab_contents_) {
[email protected]c07cfb802009-05-14 15:53:13120 UpdateFindBarForCurrentResult();
[email protected]c7622f52009-05-06 04:42:16121 if (tab_contents_->find_result().final_update() &&
122 tab_contents_->find_result().number_of_matches() == 0) {
[email protected]101c90e2009-10-26 21:14:44123 const string16& last_search = tab_contents_->previous_find_text();
124 const string16& current_search = tab_contents_->find_text();
125 if (last_search.find(current_search) != 0)
126 find_bar_->AudibleAlert();
[email protected]c7622f52009-05-06 04:42:16127 }
[email protected]92e175a2009-03-12 17:03:37128 }
129 } else if (type == NotificationType::NAV_ENTRY_COMMITTED) {
130 NavigationController* source_controller =
131 Source<NavigationController>(source).ptr();
[email protected]57c6a652009-05-04 07:58:34132 if (source_controller == &tab_contents_->controller()) {
[email protected]92e175a2009-03-12 17:03:37133 NavigationController::LoadCommittedDetails* commit_details =
134 Details<NavigationController::LoadCommittedDetails>(details).ptr();
135 PageTransition::Type transition_type =
136 commit_details->entry->transition_type();
137 // We hide the FindInPage window when the user navigates away, except on
138 // reload.
[email protected]aead92e2009-04-02 20:07:15139 if (find_bar_->IsFindBarVisible()) {
140 if (PageTransition::StripQualifier(transition_type) !=
141 PageTransition::RELOAD) {
142 EndFindSession();
143 } else {
144 // On Reload we want to make sure FindNext is converted to a full Find
145 // to make sure highlights for inactive matches are repainted.
[email protected]57c6a652009-05-04 07:58:34146 tab_contents_->set_find_op_aborted(true);
[email protected]aead92e2009-04-02 20:07:15147 }
[email protected]92e175a2009-03-12 17:03:37148 }
149 }
150 }
151}
[email protected]c07cfb802009-05-14 15:53:13152
[email protected]d46dccd2009-07-29 23:00:25153// static
154gfx::Rect FindBarController::GetLocationForFindbarView(
155 gfx::Rect view_location,
156 const gfx::Rect& dialog_bounds,
157 const gfx::Rect& avoid_overlapping_rect) {
158 if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) {
159 int boundary = dialog_bounds.width() - view_location.width();
160 view_location.set_x(std::min(view_location.x(), boundary));
161 } else {
162 view_location.set_x(std::max(view_location.x(), dialog_bounds.x()));
163 }
164
165 gfx::Rect new_pos = view_location;
166
167 // If the selection rectangle intersects the current position on screen then
168 // we try to move our dialog to the left (right for RTL) of the selection
169 // rectangle.
170 if (!avoid_overlapping_rect.IsEmpty() &&
171 avoid_overlapping_rect.Intersects(new_pos)) {
172 if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) {
173 new_pos.set_x(avoid_overlapping_rect.x() +
174 avoid_overlapping_rect.width() +
175 (2 * kMinFindWndDistanceFromSelection));
176
177 // If we moved it off-screen to the right, we won't move it at all.
178 if (new_pos.x() + new_pos.width() > dialog_bounds.width())
179 new_pos = view_location; // Reset.
180 } else {
181 new_pos.set_x(avoid_overlapping_rect.x() - new_pos.width() -
182 kMinFindWndDistanceFromSelection);
183
184 // If we moved it off-screen to the left, we won't move it at all.
185 if (new_pos.x() < 0)
186 new_pos = view_location; // Reset.
187 }
188 }
189
190 return new_pos;
191}
192
[email protected]c07cfb802009-05-14 15:53:13193void FindBarController::UpdateFindBarForCurrentResult() {
194 const FindNotificationDetails& find_result = tab_contents_->find_result();
195
196 // Avoid bug 894389: When a new search starts (and finds something) it reports
197 // an interim match count result of 1 before the scoping effort starts. This
198 // is to provide feedback as early as possible that we will find something.
199 // As you add letters to the search term, this creates a flashing effect when
200 // we briefly show "1 of 1" matches because there is a slight delay until
201 // the scoping effort starts updating the match count. We avoid this flash by
202 // ignoring interim results of 1 if we already have a positive number.
203 if (find_result.number_of_matches() > -1) {
204 if (last_reported_matchcount_ > 0 &&
205 find_result.number_of_matches() == 1 &&
206 !find_result.final_update())
207 return; // Don't let interim result override match count.
208 last_reported_matchcount_ = find_result.number_of_matches();
209 }
210
211 find_bar_->UpdateUIForFindResult(find_result, tab_contents_->find_text());
212}