[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 1 | // 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] | d46dccd | 2009-07-29 23:00:25 | [diff] [blame] | 7 | #include "app/l10n_util.h" |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 8 | #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] | 57c6a65 | 2009-05-04 07:58:34 | [diff] [blame] | 12 | #include "chrome/browser/tab_contents/tab_contents.h" |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 13 | |
[email protected] | d46dccd | 2009-07-29 23:00:25 | [diff] [blame] | 14 | // The minimum space between the FindInPage window and the search result. |
| 15 | static const int kMinFindWndDistanceFromSelection = 5; |
| 16 | |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 17 | FindBarController::FindBarController(FindBar* find_bar) |
[email protected] | c07cfb80 | 2009-05-14 15:53:13 | [diff] [blame] | 18 | : find_bar_(find_bar), |
| 19 | tab_contents_(NULL), |
| 20 | last_reported_matchcount_(0) { |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 21 | } |
| 22 | |
| 23 | FindBarController::~FindBarController() { |
[email protected] | 57c6a65 | 2009-05-04 07:58:34 | [diff] [blame] | 24 | DCHECK(!tab_contents_); |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 25 | } |
| 26 | |
| 27 | void FindBarController::Show() { |
| 28 | // Only show the animation if we're not already showing a find bar for the |
[email protected] | 57c6a65 | 2009-05-04 07:58:34 | [diff] [blame] | 29 | // selected TabContents. |
| 30 | if (!tab_contents_->find_ui_active()) { |
| 31 | tab_contents_->set_find_ui_active(true); |
[email protected] | fb6de39 | 2010-01-08 18:35:54 | [diff] [blame] | 32 | find_bar_->Show(true); |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 33 | } |
| 34 | find_bar_->SetFocusAndSelection(); |
| 35 | } |
| 36 | |
| 37 | void FindBarController::EndFindSession() { |
| 38 | find_bar_->Hide(true); |
| 39 | |
[email protected] | 57c6a65 | 2009-05-04 07:58:34 | [diff] [blame] | 40 | // |tab_contents_| can be NULL for a number of reasons, for example when the |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 41 | // tab is closing. We must guard against that case. See issue 8030. |
[email protected] | 57c6a65 | 2009-05-04 07:58:34 | [diff] [blame] | 42 | if (tab_contents_) { |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 43 | // 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] | 57c6a65 | 2009-05-04 07:58:34 | [diff] [blame] | 46 | tab_contents_->StopFinding(false); // false = don't clear selection on |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 47 | // page. |
[email protected] | 57c6a65 | 2009-05-04 07:58:34 | [diff] [blame] | 48 | find_bar_->ClearResults(tab_contents_->find_result()); |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 49 | |
| 50 | // When we get dismissed we restore the focus to where it belongs. |
| 51 | find_bar_->RestoreSavedFocus(); |
| 52 | } |
| 53 | } |
| 54 | |
[email protected] | 57c6a65 | 2009-05-04 07:58:34 | [diff] [blame] | 55 | void FindBarController::ChangeTabContents(TabContents* contents) { |
| 56 | if (tab_contents_) { |
[email protected] | f987e3d | 2009-05-22 01:53:02 | [diff] [blame] | 57 | registrar_.RemoveAll(); |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 58 | find_bar_->StopAnimation(); |
| 59 | } |
| 60 | |
[email protected] | 57c6a65 | 2009-05-04 07:58:34 | [diff] [blame] | 61 | tab_contents_ = contents; |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 62 | |
[email protected] | 57c6a65 | 2009-05-04 07:58:34 | [diff] [blame] | 63 | // Hide any visible find window from the previous tab if NULL |tab_contents| |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 64 | // is passed in or if the find UI is not active in the new tab. |
| 65 | if (find_bar_->IsFindBarVisible() && |
[email protected] | 57c6a65 | 2009-05-04 07:58:34 | [diff] [blame] | 66 | (!tab_contents_ || !tab_contents_->find_ui_active())) { |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 67 | find_bar_->Hide(false); |
| 68 | } |
| 69 | |
[email protected] | c07cfb80 | 2009-05-14 15:53:13 | [diff] [blame] | 70 | if (!tab_contents_) |
| 71 | return; |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 72 | |
[email protected] | f987e3d | 2009-05-22 01:53:02 | [diff] [blame] | 73 | 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] | 732286a | 2009-04-13 22:16:03 | [diff] [blame] | 77 | |
[email protected] | 6a4f5af2 | 2009-09-23 22:43:00 | [diff] [blame] | 78 | #if !defined(OS_MACOSX) |
[email protected] | c07cfb80 | 2009-05-14 15:53:13 | [diff] [blame] | 79 | // 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] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 85 | |
[email protected] | c07cfb80 | 2009-05-14 15:53:13 | [diff] [blame] | 86 | // 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] | 6a4f5af2 | 2009-09-23 22:43:00 | [diff] [blame] | 92 | #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] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 97 | |
[email protected] | c07cfb80 | 2009-05-14 15:53:13 | [diff] [blame] | 98 | 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] | fb6de39 | 2010-01-08 18:35:54 | [diff] [blame] | 104 | find_bar_->Show(false); |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 105 | } |
[email protected] | c07cfb80 | 2009-05-14 15:53:13 | [diff] [blame] | 106 | |
| 107 | UpdateFindBarForCurrentResult(); |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 108 | } |
| 109 | |
| 110 | //////////////////////////////////////////////////////////////////////////////// |
[email protected] | 5c9e97a | 2009-09-09 23:48:30 | [diff] [blame] | 111 | // FindBarHost, NotificationObserver implementation: |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 112 | |
| 113 | void 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] | 57c6a65 | 2009-05-04 07:58:34 | [diff] [blame] | 119 | if (Source<TabContents>(source).ptr() == tab_contents_) { |
[email protected] | c07cfb80 | 2009-05-14 15:53:13 | [diff] [blame] | 120 | UpdateFindBarForCurrentResult(); |
[email protected] | c7622f5 | 2009-05-06 04:42:16 | [diff] [blame] | 121 | if (tab_contents_->find_result().final_update() && |
| 122 | tab_contents_->find_result().number_of_matches() == 0) { |
[email protected] | 101c90e | 2009-10-26 21:14:44 | [diff] [blame] | 123 | 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] | c7622f5 | 2009-05-06 04:42:16 | [diff] [blame] | 127 | } |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 128 | } |
| 129 | } else if (type == NotificationType::NAV_ENTRY_COMMITTED) { |
| 130 | NavigationController* source_controller = |
| 131 | Source<NavigationController>(source).ptr(); |
[email protected] | 57c6a65 | 2009-05-04 07:58:34 | [diff] [blame] | 132 | if (source_controller == &tab_contents_->controller()) { |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 133 | 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] | aead92e | 2009-04-02 20:07:15 | [diff] [blame] | 139 | 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] | 57c6a65 | 2009-05-04 07:58:34 | [diff] [blame] | 146 | tab_contents_->set_find_op_aborted(true); |
[email protected] | aead92e | 2009-04-02 20:07:15 | [diff] [blame] | 147 | } |
[email protected] | 92e175a | 2009-03-12 17:03:37 | [diff] [blame] | 148 | } |
| 149 | } |
| 150 | } |
| 151 | } |
[email protected] | c07cfb80 | 2009-05-14 15:53:13 | [diff] [blame] | 152 | |
[email protected] | d46dccd | 2009-07-29 23:00:25 | [diff] [blame] | 153 | // static |
| 154 | gfx::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] | c07cfb80 | 2009-05-14 15:53:13 | [diff] [blame] | 193 | void 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 | } |