summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/autocomplete/autocomplete.h4
-rw-r--r--chrome/browser/autocomplete/autocomplete_edit.cc8
-rw-r--r--chrome/browser/autocomplete/autocomplete_edit.h11
-rw-r--r--chrome/browser/autocomplete/autocomplete_popup.cc1205
-rw-r--r--chrome/browser/autocomplete/autocomplete_popup.h384
5 files changed, 791 insertions, 821 deletions
diff --git a/chrome/browser/autocomplete/autocomplete.h b/chrome/browser/autocomplete/autocomplete.h
index d7fd130..8102306 100644
--- a/chrome/browser/autocomplete/autocomplete.h
+++ b/chrome/browser/autocomplete/autocomplete.h
@@ -443,7 +443,7 @@ class AutocompleteProvider
//
// |minimal_changes| is an optimization that lets the provider do less work
// when the |input|'s text hasn't changed. See the body of
- // AutocompletePopup::StartAutocomplete().
+ // AutocompletePopupModel::StartAutocomplete().
//
// If |synchronous_only| is true, no asynchronous work should be scheduled;
// the provider should stop after it has returned all the
@@ -768,7 +768,7 @@ struct AutocompleteLog {
}
// The user's input text in the omnibox.
std::wstring text;
- // Selected index (if selected) or -1 (AutocompletePopup::kNoMatch).
+ // Selected index (if selected) or -1 (AutocompletePopupModel::kNoMatch).
size_t selected_index;
// Inline autocompleted length (if displayed).
size_t inline_autocompleted_length;
diff --git a/chrome/browser/autocomplete/autocomplete_edit.cc b/chrome/browser/autocomplete/autocomplete_edit.cc
index ca18895..4c06a1c 100644
--- a/chrome/browser/autocomplete/autocomplete_edit.cc
+++ b/chrome/browser/autocomplete/autocomplete_edit.cc
@@ -125,7 +125,7 @@ AutocompleteEdit::AutocompleteEdit(const ChromeFont& font,
bool popup_window_mode)
: controller_(controller),
model_(model),
- popup_(new AutocompletePopup(font, this, profile)),
+ popup_(new AutocompletePopupModel(font, this, profile)),
popup_window_mode_(popup_window_mode),
has_focus_(false),
user_input_in_progress_(false),
@@ -455,7 +455,7 @@ void AutocompleteEdit::AcceptInput(WindowOpenDisposition disposition,
}
OpenURL(url, disposition, transition, alternate_nav_url,
- AutocompletePopup::kNoMatch,
+ AutocompletePopupModel::kNoMatch,
is_keyword_hint_ ? std::wstring() : keyword_);
}
@@ -2139,7 +2139,7 @@ void AutocompleteEdit::PasteAndGo() {
// enough of an edge case that we ignore this possibility.
RevertAll();
OpenURL(paste_and_go_url_, CURRENT_TAB, paste_and_go_transition_,
- paste_and_go_alternate_nav_url_, AutocompletePopup::kNoMatch,
+ paste_and_go_alternate_nav_url_, AutocompletePopupModel::kNoMatch,
std::wstring());
}
@@ -2157,7 +2157,7 @@ void AutocompleteEdit::SendOpenNotification(size_t selected_line,
// open).
if (popup_->is_open()) {
scoped_ptr<AutocompleteLog> log(popup_->GetAutocompleteLog());
- if (selected_line != AutocompletePopup::kNoMatch)
+ if (selected_line != AutocompletePopupModel::kNoMatch)
log->selected_index = selected_line;
else if (!has_temporary_text_)
log->inline_autocompleted_length = inline_autocomplete_text_.length();
diff --git a/chrome/browser/autocomplete/autocomplete_edit.h b/chrome/browser/autocomplete/autocomplete_edit.h
index 3b919d0..7d13409 100644
--- a/chrome/browser/autocomplete/autocomplete_edit.h
+++ b/chrome/browser/autocomplete/autocomplete_edit.h
@@ -20,7 +20,7 @@
#include "chrome/views/menu.h"
#include "webkit/glue/window_open_disposition.h"
-class AutocompletePopup;
+class AutocompletePopupModel;
class CommandController;
class Profile;
class TabContents;
@@ -181,8 +181,7 @@ class AutocompleteEdit
// |alternate_nav_url|, if non-empty, contains the alternate navigation URL
// for |url|. See comments on AutocompleteResult::GetAlternateNavURL().
//
- // |selected_line| is passed to AutocompletePopup::LogOpenedURL(); see
- // comments there.
+ // |selected_line| is passed to SendOpenNotification(); see comments there.
//
// If the URL was expanded from a keyword, |keyword| is that keyword.
//
@@ -251,8 +250,8 @@ class AutocompleteEdit
// send us events that we should treat as if they were events on us.
void HandleExternalMsg(UINT msg, UINT flags, const CPoint& screen_point);
- // Called back by the AutocompletePopup when any relevant data changes. This
- // rolls together several separate pieces of data into one call so we can
+ // Called back by the AutocompletePopupModel when any relevant data changes.
+ // This rolls together several separate pieces of data into one call so we can
// update all the UI efficiently:
// |text| is either the new temporary text (if |is_temporary_text| is true)
// from the user manually selecting a different match, or the inline
@@ -577,7 +576,7 @@ class AutocompleteEdit
Controller* controller_;
// The Popup itself.
- scoped_ptr<AutocompletePopup> popup_;
+ scoped_ptr<AutocompletePopupModel> popup_;
// When true, the location bar view is read only and also is has a slightly
// different presentation (font size / color). This is used for popups.
diff --git a/chrome/browser/autocomplete/autocomplete_popup.cc b/chrome/browser/autocomplete/autocomplete_popup.cc
index 087af29..b272143 100644
--- a/chrome/browser/autocomplete/autocomplete_popup.cc
+++ b/chrome/browser/autocomplete/autocomplete_popup.cc
@@ -21,16 +21,8 @@
#include "third_party/icu38/public/common/unicode/ubidi.h"
namespace {
-// The amount of time we'll wait after a provider returns before updating,
-// in order to coalesce results.
-const int kPopupCoalesceMs = 100;
-
-// The maximum time we'll allow the popup to go without updating.
-const int kPopupUpdateMaxDelayMs = 300;
-
// Padding between text and the star indicator, in pixels.
const int kStarPadding = 4;
-
};
// A simple wrapper class for the bidirectional iterator of ICU.
@@ -38,51 +30,17 @@ const int kStarPadding = 4;
// bidirectional texts into visual runs in its display order.
class BiDiLineIterator {
public:
- BiDiLineIterator();
+ BiDiLineIterator() : bidi_(NULL) { }
~BiDiLineIterator();
- // Initialize the bidirectional iterator with the specified text.
- // Parameters
- // * text [in] (const std::wstring&)
- // Represents the text to be iterated with this iterator.
- // * right_to_left [in] (bool)
- // Represents whether or not the default text direction is right-to-left.
- // Possible parameters are listed below:
- // - true, the default text direction is right-to-left.
- // - false, the default text direction is left-to-right.
- // * url [in] (bool)
- // Represents whether or not this text is a URL.
- // Return values
- // * true
- // The bidirectional iterator is created successfully.
- // * false
- // An error occured while creating the bidirectional iterator.
+ // Initializes the bidirectional iterator with the specified text. Returns
+ // whether initialization succeeded.
UBool Open(const std::wstring& text, bool right_to_left, bool url);
- // Retrieve the number of visual runs in the text.
- // Return values
- // * A positive value
- // Represents the number of visual runs.
- // * 0
- // Represents an error.
+ // Returns the number of visual runs in the text, or zero on error.
int CountRuns();
- // Get the logical offset, length, and direction of the specified visual run.
- // Parameters
- // * index [in] (int)
- // Represents the index of the visual run. This value must be less than
- // the return value of the CountRuns() function.
- // * start [out] (int*)
- // Represents the index of the specified visual run, in characters.
- // * length [out] (int*)
- // Represents the length of the specified visual run, in characters.
- // Return values
- // * UBIDI_LTR
- // Represents this run should be rendered in the left-to-right reading
- // order.
- // * UBIDI_RTL
- // Represents this run should be rendered in the right-to-left reading
- // order.
+ // Gets the logical offset, length, and direction of the specified visual run.
UBiDiDirection GetVisualRun(int index, int* start, int* length);
private:
@@ -91,10 +49,6 @@ class BiDiLineIterator {
DISALLOW_EVIL_CONSTRUCTORS(BiDiLineIterator);
};
-BiDiLineIterator::BiDiLineIterator()
- : bidi_(NULL) {
-}
-
BiDiLineIterator::~BiDiLineIterator() {
if (bidi_) {
ubidi_close(bidi_);
@@ -136,13 +90,12 @@ UBiDiDirection BiDiLineIterator::GetVisualRun(int index,
// application language is a right-to-left one.
class MirroringContext {
public:
- MirroringContext();
- ~MirroringContext();
+ MirroringContext() : min_x_(0), center_x_(0), max_x_(0), enabled_(false) { }
// Initializes the bounding region used for mirroring coordinates.
// This class uses the center of this region as an axis for calculating
// mirrored coordinates.
- void Initialize(int min_x, int max_x, bool enabled);
+ void Initialize(int x1, int x2, bool enabled);
// Return the "left" side of the specified region.
// When the application language is a right-to-left one, this function
@@ -150,7 +103,7 @@ class MirroringContext {
// left side of the mirrored region.
// The input region must be in the bounding region specified in the
// Initialize() function.
- int GetLeft(int min_x, int max_x) const;
+ int GetLeft(int x1, int x2) const;
// Returns whether or not we are mirroring the x coordinate.
bool enabled() const {
@@ -166,300 +119,157 @@ class MirroringContext {
DISALLOW_EVIL_CONSTRUCTORS(MirroringContext);
};
-MirroringContext::MirroringContext()
- : min_x_(0),
- center_x_(0),
- max_x_(0),
- enabled_(false) {
-}
-
-MirroringContext::~MirroringContext() {
-}
-
-void MirroringContext::Initialize(int min_x, int max_x, bool enabled) {
- min_x_ = min_x;
- max_x_ = max_x;
- if (min_x_ > max_x_)
- std::swap(min_x_, max_x_);
- center_x_ = min_x + (max_x - min_x) / 2;
+void MirroringContext::Initialize(int x1, int x2, bool enabled) {
+ min_x_ = std::min(x1, x2);
+ max_x_ = std::max(x1, x2);
+ center_x_ = min_x_ + (max_x_ - min_x_) / 2;
enabled_ = enabled;
}
-int MirroringContext::GetLeft(int min_x, int max_x) const {
- if (!enabled_)
- return min_x;
- int mirrored_min_x = center_x_ + (center_x_ - min_x);
- int mirrored_max_x = center_x_ + (center_x_ - max_x);
- return std::min(mirrored_min_x, mirrored_max_x);
+int MirroringContext::GetLeft(int x1, int x2) const {
+ return enabled_ ?
+ (center_x_ + (center_x_ - std::max(x1, x2))) : std::min(x1, x2);
}
-const wchar_t AutocompletePopup::DrawLineInfo::ellipsis_str[] = L"\x2026";
+const wchar_t AutocompletePopupView::DrawLineInfo::ellipsis_str[] = L"\x2026";
-AutocompletePopup::AutocompletePopup(const ChromeFont& font,
- AutocompleteEdit* editor,
- Profile* profile)
- : editor_(editor),
- controller_(new AutocompleteController(this, profile)),
- profile_(profile),
+AutocompletePopupView::AutocompletePopupView(AutocompletePopupModel* model,
+ const ChromeFont& font)
+ : model_(model),
line_info_(font),
- query_in_progress_(false),
- update_pending_(false),
- // Creating the Timers directly instead of using StartTimer() ensures
- // they won't actually start running until we use ResetTimer().
- coalesce_timer_(new Timer(kPopupCoalesceMs, this, false)),
- max_delay_timer_(new Timer(kPopupUpdateMaxDelayMs, this, true)),
- hovered_line_(kNoMatch),
- selected_line_(kNoMatch),
mirroring_context_(new MirroringContext()),
- star_(ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_CONTENT_STAR_ON)) {
+ star_(ResourceBundle::GetSharedInstance().GetBitmapNamed(
+ IDR_CONTENT_STAR_ON)) {
}
-AutocompletePopup::~AutocompletePopup() {
- StopAutocomplete();
-}
+void AutocompletePopupView::InvalidateLine(size_t line) {
+ DCHECK(line < model_->result()->size());
-void AutocompletePopup::SetProfile(Profile* profile) {
- DCHECK(profile);
- profile_ = profile;
- controller_->SetProfile(profile);
+ RECT rc;
+ GetClientRect(&rc);
+ rc.top = LineTopPixel(line);
+ rc.bottom = rc.top + line_info_.line_height;
+ InvalidateRect(&rc, false);
}
-void AutocompletePopup::StartAutocomplete(
- const std::wstring& text,
- const std::wstring& desired_tld,
- bool prevent_inline_autocomplete) {
- // The user is interacting with the edit, so stop tracking hover.
- SetHoveredLine(kNoMatch);
-
- // See if we can avoid rerunning autocomplete when the query hasn't changed
- // much. If the popup isn't open, we threw the past results away, so no
- // shortcuts are possible.
- const AutocompleteInput input(text, desired_tld, prevent_inline_autocomplete);
- bool minimal_changes = false;
- if (is_open()) {
- // When the user hits escape with a temporary selection, the edit asks us
- // to update, but the text it supplies hasn't changed since the last query.
- // Instead of stopping or rerunning the last query, just do an immediate
- // repaint with the new (probably NULL) provider affinity.
- if (input_.Equals(input)) {
- SetDefaultMatchAndUpdate(true);
- return;
+void AutocompletePopupView::UpdatePopupAppearance() {
+ if (model_->result()->empty()) {
+ // No results, close any existing popup.
+ if (m_hWnd) {
+ DestroyWindow();
+ m_hWnd = NULL;
}
-
- // When the user presses or releases the ctrl key, the desired_tld changes,
- // and when the user finishes an IME composition, inline autocomplete may
- // no longer be prevented. In both these cases the text itself hasn't
- // changed since the last query, and some providers can do much less work
- // (and get results back more quickly). Taking advantage of this reduces
- // flicker.
- if (input_.text() == text)
- minimal_changes = true;
- }
- input_ = input;
-
- // If we're starting a brand new query, stop caring about any old query.
- TimerManager* const tm = MessageLoop::current()->timer_manager();
- if (!minimal_changes && query_in_progress_) {
- update_pending_ = false;
- tm->StopTimer(coalesce_timer_.get());
- }
-
- // Start the new query.
- query_in_progress_ = !controller_->Start(input_, minimal_changes, false);
- controller_->GetResult(&latest_result_);
-
- // If we're not ready to show results and the max update interval timer isn't
- // already running, start it now.
- if (query_in_progress_ && !tm->IsTimerRunning(max_delay_timer_.get()))
- tm->ResetTimer(max_delay_timer_.get());
-
- SetDefaultMatchAndUpdate(!query_in_progress_);
-}
-
-void AutocompletePopup::StopAutocomplete() {
- // Close any old query.
- if (query_in_progress_) {
- controller_->Stop();
- query_in_progress_ = false;
- update_pending_ = false;
+ return;
}
- // Reset results. This will force the popup to close.
- latest_result_.Reset();
- CommitLatestResults(true);
-
- // Clear input_ to make sure we don't try and use any of these results for
- // the next query we receive. Strictly speaking this isn't necessary, since
- // the popup isn't open, but it keeps our internal state consistent and
- // serves as future-proofing in case the code in StartAutocomplete() changes.
- input_.Clear();
-}
-
-std::wstring AutocompletePopup::URLsForCurrentSelection(
- PageTransition::Type* transition,
- bool* is_history_what_you_typed_match,
- std::wstring* alternate_nav_url) const {
- // The popup may be out of date, and we always want the latest match. The
- // most common case of this is when the popup is open, the user changes the
- // contents of the edit, and then presses enter before any results have been
- // displayed, but still wants to choose the would-be-default action.
- //
- // Can't call CommitLatestResults(), because
- // latest_result_.default_match_index() may not match selected_line_, and
- // we want to preserve the user's selection.
- if (latest_result_.empty())
- return std::wstring();
+ // Figure the coordinates of the popup:
+ // Get the coordinates of the location bar view; these are returned relative
+ // to its parent.
+ // TODO(pkasting): http://b/1345937 All this use of editor accessors should
+ // die once this class is a true ChromeView.
+ CRect rc;
+ model_->editor()->parent_view()->GetBounds(&rc);
+ // Subtract the top left corner to make the coordinates relative to the
+ // location bar view itself, and convert to screen coordinates.
+ CPoint top_left(-rc.TopLeft());
+ ChromeViews::View::ConvertPointToScreen(model_->editor()->parent_view(),
+ &top_left);
+ rc.OffsetRect(top_left);
+ // Expand by one pixel on each side since that's the amount the location bar
+ // view is inset from the divider line that edges the adjacent buttons.
+ // Deflate the top and bottom by the height of the extra graphics around the
+ // edit.
+ // TODO(pkasting): http://b/972786 This shouldn't be hardcoded to rely on
+ // LocationBarView constants. Instead we should just make the edit be "at the
+ // right coordinates", or something else generic.
+ rc.InflateRect(1, -LocationBarView::kTextVertMargin);
+ // Now rc is the exact width we want and is positioned like the edit would
+ // be, so shift the top and bottom downwards so the new top is where the old
+ // bottom is and the rect has the height we need for all our entries, plus a
+ // one-pixel border on top and bottom.
+ rc.top = rc.bottom;
+ rc.bottom +=
+ static_cast<int>(model_->result()->size()) * line_info_.line_height + 2;
- const AutocompleteResult* result;
- AutocompleteResult::const_iterator match;
- if (update_pending_) {
- // The default match on the latest result should be up-to-date. If the user
- // changed the selection since that result was generated using the arrow
- // keys, Move() will have force updated the popup.
- result = &latest_result_;
- match = result->default_match();
+ if (!m_hWnd) {
+ // To prevent this window from being activated, we create an invisible
+ // window and manually show it without activating it.
+ Create(model_->editor()->m_hWnd, rc, AUTOCOMPLETEPOPUPVIEW_CLASSNAME,
+ WS_POPUP, WS_EX_TOOLWINDOW);
+ // When an IME is attached to the rich-edit control, retrieve its window
+ // handle and show this popup window under the IME windows.
+ // Otherwise, show this popup window under top-most windows.
+ // TODO(hbono): http://b/1111369 if we exclude this popup window from the
+ // display area of IME windows, this workaround becomes unnecessary.
+ HWND ime_window = ImmGetDefaultIMEWnd(model_->editor()->m_hWnd);
+ SetWindowPos(ime_window ? ime_window : HWND_NOTOPMOST, 0, 0, 0, 0,
+ SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
} else {
- result = &result_;
- DCHECK(selected_line_ < result_.size());
- match = result->begin() + selected_line_;
+ // Already open, just resize the window. This is a bit tricky; we want to
+ // repaint the whole window, since the contents may have changed, but
+ // MoveWindow() won't repaint portions that haven't moved or been
+ // added/removed. So we first call InvalidateRect(), so the next repaint
+ // paints the whole window, then tell MoveWindow() to do the actual
+ // repaint, which will also properly repaint Windows formerly under the
+ // popup.
+ InvalidateRect(NULL, false);
+ MoveWindow(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, true);
}
- if (transition)
- *transition = match->transition;
- if (is_history_what_you_typed_match)
- *is_history_what_you_typed_match = match->is_history_what_you_typed_match;
- if (alternate_nav_url && manually_selected_match_.empty())
- *alternate_nav_url = result->GetAlternateNavURL(input_, match);
- return match->destination_url;
-}
-
-std::wstring AutocompletePopup::URLsForDefaultMatch(
- const std::wstring& text,
- const std::wstring& desired_tld,
- PageTransition::Type* transition,
- bool* is_history_what_you_typed_match,
- std::wstring* alternate_nav_url) {
- // Cancel any existing query.
- StopAutocomplete();
-
- // Run the new query and get only the synchronously available results.
- const AutocompleteInput input(text, desired_tld, true);
- const bool done = controller_->Start(input, false, true);
- DCHECK(done);
- controller_->GetResult(&result_);
- if (result_.empty())
- return std::wstring();
- // Get the URLs for the default match.
- result_.SetDefaultMatch(manually_selected_match_);
- const AutocompleteResult::const_iterator match = result_.default_match();
- const std::wstring url(match->destination_url); // Need to copy since we
- // reset result_ below.
- if (transition)
- *transition = match->transition;
- if (is_history_what_you_typed_match)
- *is_history_what_you_typed_match = match->is_history_what_you_typed_match;
- if (alternate_nav_url && manually_selected_match_.empty())
- *alternate_nav_url = result_.GetAlternateNavURL(input, match);
- result_.Reset();
-
- return url;
-}
-
-AutocompleteLog* AutocompletePopup::GetAutocompleteLog() {
- return new AutocompleteLog(input_.text(), selected_line_, 0, result_);
+ // TODO(pkasting): http://b/1111369 We should call ImmSetCandidateWindow() on
+ // the model_->editor()'s IME context here, and exclude ourselves from its
+ // display area. Not clear what to pass for the lpCandidate->ptCurrentPos
+ // member, though...
}
-void AutocompletePopup::Move(int count) {
- // The user is using the keyboard to change the selection, so stop tracking
- // hover.
- SetHoveredLine(kNoMatch);
-
- // Force the popup to open/update, so the user is interacting with the
- // latest results.
- CommitLatestResults(false);
- if (result_.empty())
- return;
-
- // Clamp the new line to [0, result_.count() - 1].
- const size_t new_line = selected_line_ + count;
- SetSelectedLine(((count < 0) && (new_line >= selected_line_)) ?
- 0 : std::min(new_line, result_.size() - 1));
-}
+void AutocompletePopupView::OnHoverEnabledOrDisabled(bool disabled) {
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ if (disabled) {
+ // Save the current mouse position to check against for re-enabling.
+ GetCursorPos(&last_hover_coordinates_); // Returns screen coordinates
-void AutocompletePopup::TryDeletingCurrentItem() {
- // We could use URLsForCurrentSelection() here, but it seems better to try
- // and shift-delete the actual selection, rather than any "in progress, not
- // yet visible" one.
- if (selected_line_ == kNoMatch)
- return;
- const AutocompleteMatch& match = result_.match_at(selected_line_);
- if (match.deletable) {
- query_in_progress_ = true;
- size_t selected_line = selected_line_;
- match.provider->DeleteMatch(match); // This will synchronously call back
- // to OnAutocompleteUpdate()
- CommitLatestResults(false);
- if (!result_.empty()) {
- // Move the selection to the next choice after the deleted one, but clear
- // the manual selection so this choice doesn't act "sticky".
- //
- // It might also be correct to only call Clear() here when
- // manually_selected_match_ didn't already have a provider() (i.e. when
- // there was no existing manual selection). It's not clear what the user
- // wants when they shift-delete something they've arrowed to. If they
- // arrowed down just to shift-delete it, we should probably Clear(); if
- // they arrowed to do something else, then got a bad match while typing,
- // we probably shouldn't.
- SetSelectedLine(std::min(result_.size() - 1, selected_line));
- manually_selected_match_.Clear();
- }
+ // Cancel existing registration for WM_MOUSELEAVE notifications.
+ tme.dwFlags = TME_CANCEL | TME_LEAVE;
+ } else {
+ // Register for WM_MOUSELEAVE notifications.
+ tme.dwFlags = TME_LEAVE;
}
+ tme.hwndTrack = m_hWnd;
+ tme.dwHoverTime = HOVER_DEFAULT; // Not actually used
+ TrackMouseEvent(&tme);
}
-void AutocompletePopup::OnAutocompleteUpdate(bool updated_result,
- bool query_complete) {
- DCHECK(query_in_progress_);
- if (updated_result)
- controller_->GetResult(&latest_result_);
- query_in_progress_ = !query_complete;
-
- SetDefaultMatchAndUpdate(query_complete);
-}
-
-void AutocompletePopup::Run() {
- CommitLatestResults(false);
-}
-
-void AutocompletePopup::OnLButtonDown(UINT keys, const CPoint& point) {
+void AutocompletePopupView::OnLButtonDown(UINT keys, const CPoint& point) {
const size_t new_hovered_line = PixelToLine(point.y);
- SetHoveredLine(new_hovered_line);
- SetSelectedLine(new_hovered_line);
+ model_->SetHoveredLine(new_hovered_line);
+ model_->SetSelectedLine(new_hovered_line);
}
-void AutocompletePopup::OnMButtonDown(UINT keys, const CPoint& point) {
- SetHoveredLine(PixelToLine(point.y));
+void AutocompletePopupView::OnMButtonDown(UINT keys, const CPoint& point) {
+ model_->SetHoveredLine(PixelToLine(point.y));
}
-void AutocompletePopup::OnLButtonUp(UINT keys, const CPoint& point) {
+void AutocompletePopupView::OnLButtonUp(UINT keys, const CPoint& point) {
OnButtonUp(point, CURRENT_TAB);
}
-void AutocompletePopup::OnMButtonUp(UINT keys, const CPoint& point) {
+void AutocompletePopupView::OnMButtonUp(UINT keys, const CPoint& point) {
OnButtonUp(point, NEW_BACKGROUND_TAB);
}
-LRESULT AutocompletePopup::OnMouseActivate(HWND window,
- UINT hit_test,
- UINT mouse_message) {
+LRESULT AutocompletePopupView::OnMouseActivate(HWND window,
+ UINT hit_test,
+ UINT mouse_message) {
return MA_NOACTIVATE;
}
-void AutocompletePopup::OnMouseLeave() {
+void AutocompletePopupView::OnMouseLeave() {
// The mouse has left the window, so no line is hovered.
- SetHoveredLine(kNoMatch);
+ model_->SetHoveredLine(AutocompletePopupModel::kNoMatch);
}
-void AutocompletePopup::OnMouseMove(UINT keys, const CPoint& point) {
+void AutocompletePopupView::OnMouseMove(UINT keys, const CPoint& point) {
// Track hover when
// (a) The left or middle button is down (the user is interacting via the
// mouse)
@@ -470,22 +280,22 @@ void AutocompletePopup::OnMouseMove(UINT keys, const CPoint& point) {
CPoint screen_point(point);
ClientToScreen(&screen_point);
if (action_button_pressed || (last_hover_coordinates_ != screen_point) ||
- (hovered_line_ != kNoMatch)) {
+ (model_->hovered_line() != AutocompletePopupModel::kNoMatch)) {
// Determine the hovered line from the y coordinate of the event. We don't
// need to check whether the x coordinates are within the window since if
// they weren't someone else would have received the WM_MOUSEMOVE.
const size_t new_hovered_line = PixelToLine(point.y);
- SetHoveredLine(new_hovered_line);
+ model_->SetHoveredLine(new_hovered_line);
// When the user has the left button down, update their selection
// immediately (don't wait for mouseup).
if (keys & MK_LBUTTON)
- SetSelectedLine(new_hovered_line);
+ model_->SetSelectedLine(new_hovered_line);
}
}
-void AutocompletePopup::OnPaint(HDC other_dc) {
- DCHECK(!result_.empty()); // Shouldn't be drawing an empty popup
+void AutocompletePopupView::OnPaint(HDC other_dc) {
+ DCHECK(!model_->result()->empty()); // Shouldn't be drawing an empty popup
CPaintDC dc(m_hWnd);
@@ -496,8 +306,8 @@ void AutocompletePopup::OnPaint(HDC other_dc) {
DrawBorder(rc, dc);
bool all_descriptions_empty = true;
- for (AutocompleteResult::const_iterator i(result_.begin());
- i != result_.end(); ++i) {
+ for (AutocompleteResult::const_iterator i(model_->result()->begin());
+ i != model_->result()->end(); ++i) {
if (!i->description.empty()) {
all_descriptions_empty = false;
break;
@@ -510,273 +320,36 @@ void AutocompletePopup::OnPaint(HDC other_dc) {
for (size_t i = first_line; i <= last_line; ++i) {
DrawLineInfo::LineStatus status;
// Selection should take precedence over hover.
- if (i == selected_line_)
+ if (i == model_->selected_line())
status = DrawLineInfo::SELECTED;
- else if (i == hovered_line_)
+ else if (i == model_->hovered_line())
status = DrawLineInfo::HOVERED;
else
status = DrawLineInfo::NORMAL;
DrawEntry(dc, rc, i, status, all_descriptions_empty,
- result_.match_at(i).starred);
- }
-}
-
-void AutocompletePopup::OnButtonUp(const CPoint& point,
- WindowOpenDisposition disposition) {
- const size_t selected_line = PixelToLine(point.y);
- const AutocompleteMatch& match = result_.match_at(selected_line);
- // OpenURL() may close the popup, which will clear the result set and, by
- // extension, |match| and its contents. So copy the relevant strings out to
- // make sure they stay alive until the call completes.
- const std::wstring url(match.destination_url);
- std::wstring keyword;
- const bool is_keyword_hint = GetKeywordForMatch(match, &keyword);
- editor_->OpenURL(url, disposition, match.transition, std::wstring(),
- selected_line, is_keyword_hint ? std::wstring() : keyword);
-}
-
-void AutocompletePopup::SetDefaultMatchAndUpdate(bool immediately) {
- if (!latest_result_.SetDefaultMatch(manually_selected_match_) &&
- !query_in_progress_) {
- // We don't clear the provider affinity because the user didn't do
- // something to indicate that they want a different provider, we just
- // couldn't find the specific match they wanted.
- manually_selected_match_.destination_url.clear();
- manually_selected_match_.is_history_what_you_typed_match = false;
- }
-
- if (immediately) {
- CommitLatestResults(true);
- } else if (!update_pending_) {
- // Coalesce the results for the next kPopupCoalesceMs milliseconds.
- update_pending_ = true;
- MessageLoop::current()->timer_manager()->ResetTimer(coalesce_timer_.get());
+ model_->result()->match_at(i).starred);
}
-
- // Update the edit with the possibly new data for this match.
- // NOTE: This must be done after the code above, so that our internal state
- // will be consistent when the edit calls back to URLsForCurrentSelection().
- std::wstring inline_autocomplete_text;
- std::wstring keyword;
- bool is_keyword_hint = false;
- bool can_show_search_hint = true;
- const AutocompleteResult::const_iterator match(
- latest_result_.default_match());
- if (match != latest_result_.end()) {
- if ((match->inline_autocomplete_offset != std::wstring::npos) &&
- (match->inline_autocomplete_offset < match->fill_into_edit.length())) {
- inline_autocomplete_text =
- match->fill_into_edit.substr(match->inline_autocomplete_offset);
- }
- // Warm up DNS Prefetch Cache.
- chrome_browser_net::DnsPrefetchUrlString(match->destination_url);
- // We could prefetch the alternate nav URL, if any, but because there can be
- // many of these as a user types an initial series of characters, the OS DNS
- // cache could suffer eviction problems for minimal gain.
-
- is_keyword_hint = GetKeywordForMatch(*match, &keyword);
- can_show_search_hint = (match->type == AutocompleteMatch::SEARCH);
- }
- editor_->OnPopupDataChanged(inline_autocomplete_text, false,
- manually_selected_match_ /* ignored */, keyword, is_keyword_hint,
- can_show_search_hint);
-}
-
-void AutocompletePopup::CommitLatestResults(bool force) {
- if (!force && !update_pending_)
- return;
-
- update_pending_ = false;
-
- // If we're going to trim the window size to no longer include the hovered
- // line, turn hover off first. We need to do this before changing result_
- // since SetHover() should be able to trust that the old and new hovered
- // lines are valid.
- //
- // Practically, this only currently happens when we're closing the window by
- // setting latest_result_ to an empty list.
- if ((hovered_line_ != kNoMatch) && (latest_result_.size() <= hovered_line_))
- SetHoveredLine(kNoMatch);
-
- result_.CopyFrom(latest_result_);
- selected_line_ = (result_.default_match() == result_.end()) ?
- kNoMatch : (result_.default_match() - result_.begin());
-
- UpdatePopupAppearance();
-
- // The max update interval timer either needs to be reset (if more updates
- // are to come) or stopped (when we're done with the query). The coalesce
- // timer should always just be stopped.
- TimerManager* const tm = MessageLoop::current()->timer_manager();
- tm->StopTimer(coalesce_timer_.get());
- if (query_in_progress_)
- tm->ResetTimer(max_delay_timer_.get());
- else
- tm->StopTimer(max_delay_timer_.get());
}
-void AutocompletePopup::UpdatePopupAppearance() {
- if (result_.empty()) {
- // No results, close any existing popup.
- if (m_hWnd) {
- DestroyWindow();
- m_hWnd = NULL;
- }
- return;
- }
-
- // Figure the coordinates of the popup:
- // Get the coordinates of the location bar view; these are returned relative
- // to its parent.
- CRect rc;
- editor_->parent_view()->GetBounds(&rc);
- // Subtract the top left corner to make the coordinates relative to the
- // location bar view itself, and convert to screen coordinates.
- CPoint top_left(-rc.TopLeft());
- ChromeViews::View::ConvertPointToScreen(editor_->parent_view(), &top_left);
- rc.OffsetRect(top_left);
- // Expand by one pixel on each side since that's the amount the location bar
- // view is inset from the divider line that edges the adjacent buttons.
- // Deflate the top and bottom by the height of the extra graphics around the
- // edit.
- // TODO(pkasting): http://b/972786 This shouldn't be hardcoded to rely on
- // LocationBarView constants. Instead we should just make the edit be "at the
- // right coordinates", or something else generic.
- rc.InflateRect(1, -LocationBarView::kTextVertMargin);
- // Now rc is the exact width we want and is positioned like the edit would
- // be, so shift the top and bottom downwards so the new top is where the old
- // bottom is and the rect has the height we need for all our entries, plus a
- // one-pixel border on top and bottom.
- rc.top = rc.bottom;
- rc.bottom += static_cast<int>(result_.size()) * line_info_.line_height + 2;
-
- if (!m_hWnd) {
- // To prevent this window from being activated, we create an invisible
- // window and manually show it without activating it.
- Create(editor_->m_hWnd, rc, AUTOCOMPLETEPOPUP_CLASSNAME, WS_POPUP,
- WS_EX_TOOLWINDOW);
- // When an IME is attached to the rich-edit control, retrieve its window
- // handle and show this popup window under the IME windows.
- // Otherwise, show this popup window under top-most windows.
- // TODO(hbono): http://b/1111369 if we exclude this popup window from the
- // display area of IME windows, this workaround becomes unnecessary.
- HWND ime_window = ImmGetDefaultIMEWnd(editor_->m_hWnd);
- SetWindowPos(ime_window ? ime_window : HWND_NOTOPMOST, 0, 0, 0, 0,
- SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
- } else {
- // Already open, just resize the window. This is a bit tricky; we want to
- // repaint the whole window, since the contents may have changed, but
- // MoveWindow() won't repaint portions that haven't moved or been
- // added/removed. So we first call InvalidateRect(), so the next repaint
- // paints the whole window, then tell MoveWindow() to do the actual
- // repaint, which will also properly repaint Windows formerly under the
- // popup.
- InvalidateRect(NULL, false);
- MoveWindow(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, true);
- }
-
- // TODO(pkasting): http://b/1111369 We should call ImmSetCandidateWindow() on
- // the editor_'s IME context here, and exclude ourselves from its display
- // area. Not clear what to pass for the lpCandidate->ptCurrentPos member,
- // though...
+void AutocompletePopupView::OnButtonUp(const CPoint& point,
+ WindowOpenDisposition disposition) {
+ model_->OpenLine(PixelToLine(point.y), disposition);
}
-int AutocompletePopup::LineTopPixel(size_t line) const {
- DCHECK(line <= result_.size());
+int AutocompletePopupView::LineTopPixel(size_t line) const {
+ DCHECK(line <= model_->result()->size());
// The popup has a 1 px top border.
return line_info_.line_height * static_cast<int>(line) + 1;
}
-size_t AutocompletePopup::PixelToLine(int y) const {
+size_t AutocompletePopupView::PixelToLine(int y) const {
const size_t line = std::max(y - 1, 0) / line_info_.line_height;
- return (line < result_.size()) ? line : (result_.size() - 1);
-}
-
-void AutocompletePopup::SetHoveredLine(size_t line) {
- const bool is_disabling = (line == kNoMatch);
- DCHECK(is_disabling || (line < result_.size()));
-
- if (line == hovered_line_)
- return; // Nothing to do
-
- const bool is_enabling = (hovered_line_ == kNoMatch);
- if (is_enabling || is_disabling) {
- TRACKMOUSEEVENT tme;
- tme.cbSize = sizeof(TRACKMOUSEEVENT);
- if (is_disabling) {
- // Save the current mouse position to check against for re-enabling.
- GetCursorPos(&last_hover_coordinates_); // Returns screen coordinates
-
- // Cancel existing registration for WM_MOUSELEAVE notifications.
- tme.dwFlags = TME_CANCEL | TME_LEAVE;
- } else {
- // Register for WM_MOUSELEAVE notifications.
- tme.dwFlags = TME_LEAVE;
- }
- tme.hwndTrack = m_hWnd;
- tme.dwHoverTime = HOVER_DEFAULT; // Not actually used
- TrackMouseEvent(&tme);
- }
-
- // Make sure the old hovered line is redrawn. No need to redraw the selected
- // line since selection overrides hover so the appearance won't change.
- if (!is_enabling && (hovered_line_ != selected_line_))
- InvalidateLine(hovered_line_);
-
- // Change the hover to the new line and make sure it's redrawn.
- hovered_line_ = line;
- if (!is_disabling && (hovered_line_ != selected_line_))
- InvalidateLine(hovered_line_);
-}
-
-void AutocompletePopup::SetSelectedLine(size_t line) {
- DCHECK(line < result_.size());
- if (result_.empty())
- return;
-
- if (line == selected_line_)
- return; // Nothing to do
-
- // Update the edit with the new data for this match.
- const AutocompleteMatch& match = result_.match_at(line);
- std::wstring keyword;
- const bool is_keyword_hint = GetKeywordForMatch(match, &keyword);
- editor_->OnPopupDataChanged(match.fill_into_edit, true,
- manually_selected_match_, keyword,
- is_keyword_hint,
- (match.type == AutocompleteMatch::SEARCH));
-
- // Track the user's selection until they cancel it.
- manually_selected_match_.destination_url = match.destination_url;
- manually_selected_match_.provider_affinity = match.provider;
- manually_selected_match_.is_history_what_you_typed_match =
- match.is_history_what_you_typed_match;
-
- // Repaint old and new selected lines immediately, so that the edit doesn't
- // appear to update [much] faster than the popup. We must not update
- // |selected_line_| before calling OnPopupDataChanged() (since the edit may
- // call us back to get data about the old selection), and we must not call
- // UpdateWindow() before updating |selected_line_| (since the paint routine
- // relies on knowing the correct selected line).
- InvalidateLine(selected_line_);
- selected_line_ = line;
- InvalidateLine(selected_line_);
- UpdateWindow();
-}
-
-void AutocompletePopup::InvalidateLine(size_t line) {
- DCHECK(line < result_.size());
-
- RECT rc;
- GetClientRect(&rc);
- rc.top = LineTopPixel(line);
- rc.bottom = rc.top + line_info_.line_height;
- InvalidateRect(&rc, false);
+ return std::min(line, model_->result()->size() - 1);
}
// Draws a light border around the inside of the window with the given client
// rectangle and DC.
-void AutocompletePopup::DrawBorder(const RECT& rc, HDC dc) {
+void AutocompletePopupView::DrawBorder(const RECT& rc, HDC dc) {
HPEN hpen = CreatePen(PS_SOLID, 1, RGB(199, 202, 206));
HGDIOBJ old_pen = SelectObject(dc, hpen);
@@ -793,16 +366,16 @@ void AutocompletePopup::DrawBorder(const RECT& rc, HDC dc) {
DeleteObject(hpen);
}
-int AutocompletePopup::DrawString(HDC dc,
- int x,
- int y,
- int max_x,
- const wchar_t* text,
- int length,
- int style,
- const DrawLineInfo::LineStatus status,
- const MirroringContext* context,
- bool text_direction_is_rtl) const {
+int AutocompletePopupView::DrawString(HDC dc,
+ int x,
+ int y,
+ int max_x,
+ const wchar_t* text,
+ int length,
+ int style,
+ const DrawLineInfo::LineStatus status,
+ const MirroringContext* context,
+ bool text_direction_is_rtl) const {
if (length <= 0)
return 0;
@@ -858,7 +431,7 @@ int AutocompletePopup::DrawString(HDC dc,
return text_x - x;
}
-void AutocompletePopup::DrawMatchFragments(
+void AutocompletePopupView::DrawMatchFragments(
HDC dc,
const std::wstring& text,
const ACMatchClassifications& classifications,
@@ -963,12 +536,12 @@ void AutocompletePopup::DrawMatchFragments(
}
}
-void AutocompletePopup::DrawEntry(HDC dc,
- const RECT& client_rect,
- size_t line,
- DrawLineInfo::LineStatus status,
- bool all_descriptions_empty,
- bool starred) const {
+void AutocompletePopupView::DrawEntry(HDC dc,
+ const RECT& client_rect,
+ size_t line,
+ DrawLineInfo::LineStatus status,
+ bool all_descriptions_empty,
+ bool starred) const {
// Calculate outer bounds of entry, and fill background.
const int top_pixel = LineTopPixel(line);
const RECT rc = {1, top_pixel, client_rect.right - client_rect.left - 1,
@@ -1004,7 +577,7 @@ void AutocompletePopup::DrawEntry(HDC dc,
// the HISTORY_SEARCH shortcut, the description section is eliminated, and
// all the available width is used for the content section.
int star_x;
- const AutocompleteMatch& match = result_.match_at(line);
+ const AutocompleteMatch& match = model_->result()->match_at(line);
if ((description_width < (line_info_.ave_char_width * 20)) ||
all_descriptions_empty ||
(match.type == AutocompleteMatch::HISTORY_SEARCH)) {
@@ -1024,7 +597,7 @@ void AutocompletePopup::DrawEntry(HDC dc,
(line_info_.line_height - star_->height()) / 2 + top_pixel);
}
-void AutocompletePopup::DrawStar(HDC dc, int x, int y) const {
+void AutocompletePopupView::DrawStar(HDC dc, int x, int y) const {
ChromeCanvas canvas(star_->width(), star_->height(), false);
// Make the background completely transparent.
canvas.drawColor(SK_ColorBLACK, SkPorterDuff::kClear_Mode);
@@ -1033,39 +606,7 @@ void AutocompletePopup::DrawStar(HDC dc, int x, int y) const {
dc, mirroring_context_->GetLeft(x, x + star_->width()), y, NULL);
}
-bool AutocompletePopup::GetKeywordForMatch(const AutocompleteMatch& match,
- std::wstring* keyword) {
- // Assume we have no keyword until we find otherwise.
- keyword->clear();
-
- // If the current match is a keyword, return that as the selected keyword.
- if (match.template_url && match.template_url->url() &&
- match.template_url->url()->SupportsReplacement()) {
- keyword->assign(match.template_url->keyword());
- return false;
- }
-
- // See if the current match's fill_into_edit corresponds to a keyword.
- if (!profile_->GetTemplateURLModel())
- return false;
- profile_->GetTemplateURLModel()->Load();
- const std::wstring keyword_hint(
- TemplateURLModel::CleanUserInputKeyword(match.fill_into_edit));
- if (keyword_hint.empty())
- return false;
-
- // Don't provide a hint if this keyword doesn't support replacement.
- const TemplateURL* const template_url =
- profile_->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword_hint);
- if (!template_url || !template_url->url() ||
- !template_url->url()->SupportsReplacement())
- return false;
-
- keyword->assign(keyword_hint);
- return true;
-}
-
-AutocompletePopup::DrawLineInfo::DrawLineInfo(const ChromeFont& font) {
+AutocompletePopupView::DrawLineInfo::DrawLineInfo(const ChromeFont& font) {
// Create regular and bold fonts.
regular_font = font.DeriveFont(-1);
bold_font = regular_font.DeriveFont(0, ChromeFont::BOLD);
@@ -1105,14 +646,15 @@ AutocompletePopup::DrawLineInfo::DrawLineInfo(const ChromeFont& font) {
}
}
-AutocompletePopup::DrawLineInfo::~DrawLineInfo() {
+AutocompletePopupView::DrawLineInfo::~DrawLineInfo() {
for (int i = 0; i < MAX_STATUS_ENTRIES; ++i)
DeleteObject(brushes[i]);
}
// static
-double AutocompletePopup::DrawLineInfo::LuminosityContrast(COLORREF color1,
- COLORREF color2) {
+double AutocompletePopupView::DrawLineInfo::LuminosityContrast(
+ COLORREF color1,
+ COLORREF color2) {
// This algorithm was adapted from the following text at
// http://juicystudio.com/article/luminositycontrastratioalgorithm.php :
//
@@ -1132,7 +674,7 @@ double AutocompletePopup::DrawLineInfo::LuminosityContrast(COLORREF color1,
}
// static
-double AutocompletePopup::DrawLineInfo::Luminosity(COLORREF color) {
+double AutocompletePopupView::DrawLineInfo::Luminosity(COLORREF color) {
// See comments in LuminosityContrast().
const double linearised_r =
pow(static_cast<double>(GetRValue(color)) / 255.0, 2.2);
@@ -1144,9 +686,9 @@ double AutocompletePopup::DrawLineInfo::Luminosity(COLORREF color) {
(0.0722 * linearised_b);
}
-COLORREF AutocompletePopup::DrawLineInfo::AlphaBlend(COLORREF foreground,
- COLORREF background,
- BYTE alpha) {
+COLORREF AutocompletePopupView::DrawLineInfo::AlphaBlend(COLORREF foreground,
+ COLORREF background,
+ BYTE alpha) {
if (alpha == 0)
return background;
else if (alpha == 0xff)
@@ -1161,3 +703,426 @@ COLORREF AutocompletePopup::DrawLineInfo::AlphaBlend(COLORREF foreground,
(GetBValue(background) * (0xff - alpha))) / 0xff);
}
+namespace {
+// The amount of time we'll wait after a provider returns before updating,
+// in order to coalesce results.
+const int kPopupCoalesceMs = 100;
+
+// The maximum time we'll allow the popup to go without updating.
+const int kPopupUpdateMaxDelayMs = 300;
+};
+
+AutocompletePopupModel::AutocompletePopupModel(const ChromeFont& font,
+ AutocompleteEdit* editor,
+ Profile* profile)
+ : view_(new AutocompletePopupView(this, font)),
+ editor_(editor),
+ controller_(new AutocompleteController(this, profile)),
+ profile_(profile),
+ query_in_progress_(false),
+ update_pending_(false),
+ // Creating the Timers directly instead of using StartTimer() ensures
+ // they won't actually start running until we use ResetTimer().
+ coalesce_timer_(new Timer(kPopupCoalesceMs, this, false)),
+ max_delay_timer_(new Timer(kPopupUpdateMaxDelayMs, this, true)),
+ hovered_line_(kNoMatch),
+ selected_line_(kNoMatch) {
+}
+
+AutocompletePopupModel::~AutocompletePopupModel() {
+ StopAutocomplete();
+}
+
+void AutocompletePopupModel::SetProfile(Profile* profile) {
+ DCHECK(profile);
+ profile_ = profile;
+ controller_->SetProfile(profile);
+}
+
+void AutocompletePopupModel::StartAutocomplete(
+ const std::wstring& text,
+ const std::wstring& desired_tld,
+ bool prevent_inline_autocomplete) {
+ // The user is interacting with the edit, so stop tracking hover.
+ SetHoveredLine(kNoMatch);
+
+ // See if we can avoid rerunning autocomplete when the query hasn't changed
+ // much. If the popup isn't open, we threw the past results away, so no
+ // shortcuts are possible.
+ const AutocompleteInput input(text, desired_tld, prevent_inline_autocomplete);
+ bool minimal_changes = false;
+ if (is_open()) {
+ // When the user hits escape with a temporary selection, the edit asks us
+ // to update, but the text it supplies hasn't changed since the last query.
+ // Instead of stopping or rerunning the last query, just do an immediate
+ // repaint with the new (probably NULL) provider affinity.
+ if (input_.Equals(input)) {
+ SetDefaultMatchAndUpdate(true);
+ return;
+ }
+
+ // When the user presses or releases the ctrl key, the desired_tld changes,
+ // and when the user finishes an IME composition, inline autocomplete may
+ // no longer be prevented. In both these cases the text itself hasn't
+ // changed since the last query, and some providers can do much less work
+ // (and get results back more quickly). Taking advantage of this reduces
+ // flicker.
+ if (input_.text() == text)
+ minimal_changes = true;
+ }
+ input_ = input;
+
+ // If we're starting a brand new query, stop caring about any old query.
+ TimerManager* const tm = MessageLoop::current()->timer_manager();
+ if (!minimal_changes && query_in_progress_) {
+ update_pending_ = false;
+ tm->StopTimer(coalesce_timer_.get());
+ }
+
+ // Start the new query.
+ query_in_progress_ = !controller_->Start(input_, minimal_changes, false);
+ controller_->GetResult(&latest_result_);
+
+ // If we're not ready to show results and the max update interval timer isn't
+ // already running, start it now.
+ if (query_in_progress_ && !tm->IsTimerRunning(max_delay_timer_.get()))
+ tm->ResetTimer(max_delay_timer_.get());
+
+ SetDefaultMatchAndUpdate(!query_in_progress_);
+}
+
+void AutocompletePopupModel::StopAutocomplete() {
+ // Close any old query.
+ if (query_in_progress_) {
+ controller_->Stop();
+ query_in_progress_ = false;
+ update_pending_ = false;
+ }
+
+ // Reset results. This will force the popup to close.
+ latest_result_.Reset();
+ CommitLatestResults(true);
+
+ // Clear input_ to make sure we don't try and use any of these results for
+ // the next query we receive. Strictly speaking this isn't necessary, since
+ // the popup isn't open, but it keeps our internal state consistent and
+ // serves as future-proofing in case the code in StartAutocomplete() changes.
+ input_.Clear();
+}
+
+void AutocompletePopupModel::SetHoveredLine(size_t line) {
+ const bool is_disabling = (line == kNoMatch);
+ DCHECK(is_disabling || (line < result_.size()));
+
+ if (line == hovered_line_)
+ return; // Nothing to do
+
+ // Make sure the old hovered line is redrawn. No need to redraw the selected
+ // line since selection overrides hover so the appearance won't change.
+ const bool is_enabling = (hovered_line_ == kNoMatch);
+ if (!is_enabling && (hovered_line_ != selected_line_))
+ view_->InvalidateLine(hovered_line_);
+
+ // Change the hover to the new line and make sure it's redrawn.
+ hovered_line_ = line;
+ if (!is_disabling && (hovered_line_ != selected_line_))
+ view_->InvalidateLine(hovered_line_);
+
+ if (is_enabling || is_disabling)
+ view_->OnHoverEnabledOrDisabled(is_disabling);
+}
+
+void AutocompletePopupModel::SetSelectedLine(size_t line) {
+ DCHECK(line < result_.size());
+ if (result_.empty())
+ return;
+
+ if (line == selected_line_)
+ return; // Nothing to do
+
+ // Update the edit with the new data for this match.
+ const AutocompleteMatch& match = result_.match_at(line);
+ std::wstring keyword;
+ const bool is_keyword_hint = GetKeywordForMatch(match, &keyword);
+ editor_->OnPopupDataChanged(match.fill_into_edit, true,
+ manually_selected_match_, keyword,
+ is_keyword_hint,
+ (match.type == AutocompleteMatch::SEARCH));
+
+ // Track the user's selection until they cancel it.
+ manually_selected_match_.destination_url = match.destination_url;
+ manually_selected_match_.provider_affinity = match.provider;
+ manually_selected_match_.is_history_what_you_typed_match =
+ match.is_history_what_you_typed_match;
+
+ // Repaint old and new selected lines immediately, so that the edit doesn't
+ // appear to update [much] faster than the popup. We must not update
+ // |selected_line_| before calling OnPopupDataChanged() (since the edit may
+ // call us back to get data about the old selection), and we must not call
+ // UpdateWindow() before updating |selected_line_| (since the paint routine
+ // relies on knowing the correct selected line).
+ view_->InvalidateLine(selected_line_);
+ selected_line_ = line;
+ view_->InvalidateLine(selected_line_);
+ view_->UpdateWindow();
+}
+
+std::wstring AutocompletePopupModel::URLsForCurrentSelection(
+ PageTransition::Type* transition,
+ bool* is_history_what_you_typed_match,
+ std::wstring* alternate_nav_url) const {
+ // The popup may be out of date, and we always want the latest match. The
+ // most common case of this is when the popup is open, the user changes the
+ // contents of the edit, and then presses enter before any results have been
+ // displayed, but still wants to choose the would-be-default action.
+ //
+ // Can't call CommitLatestResults(), because
+ // latest_result_.default_match_index() may not match selected_line_, and
+ // we want to preserve the user's selection.
+ if (latest_result_.empty())
+ return std::wstring();
+
+ const AutocompleteResult* result;
+ AutocompleteResult::const_iterator match;
+ if (update_pending_) {
+ // The default match on the latest result should be up-to-date. If the user
+ // changed the selection since that result was generated using the arrow
+ // keys, Move() will have force updated the popup.
+ result = &latest_result_;
+ match = result->default_match();
+ } else {
+ result = &result_;
+ DCHECK(selected_line_ < result_.size());
+ match = result->begin() + selected_line_;
+ }
+ if (transition)
+ *transition = match->transition;
+ if (is_history_what_you_typed_match)
+ *is_history_what_you_typed_match = match->is_history_what_you_typed_match;
+ if (alternate_nav_url && manually_selected_match_.empty())
+ *alternate_nav_url = result->GetAlternateNavURL(input_, match);
+ return match->destination_url;
+}
+
+std::wstring AutocompletePopupModel::URLsForDefaultMatch(
+ const std::wstring& text,
+ const std::wstring& desired_tld,
+ PageTransition::Type* transition,
+ bool* is_history_what_you_typed_match,
+ std::wstring* alternate_nav_url) {
+ // Cancel any existing query.
+ StopAutocomplete();
+
+ // Run the new query and get only the synchronously available results.
+ const AutocompleteInput input(text, desired_tld, true);
+ const bool done = controller_->Start(input, false, true);
+ DCHECK(done);
+ controller_->GetResult(&result_);
+ if (result_.empty())
+ return std::wstring();
+
+ // Get the URLs for the default match.
+ result_.SetDefaultMatch(manually_selected_match_);
+ const AutocompleteResult::const_iterator match = result_.default_match();
+ const std::wstring url(match->destination_url); // Need to copy since we
+ // reset result_ below.
+ if (transition)
+ *transition = match->transition;
+ if (is_history_what_you_typed_match)
+ *is_history_what_you_typed_match = match->is_history_what_you_typed_match;
+ if (alternate_nav_url && manually_selected_match_.empty())
+ *alternate_nav_url = result_.GetAlternateNavURL(input, match);
+ result_.Reset();
+
+ return url;
+}
+
+AutocompleteLog* AutocompletePopupModel::GetAutocompleteLog() {
+ return new AutocompleteLog(input_.text(), selected_line_, 0, result_);
+}
+
+void AutocompletePopupModel::Move(int count) {
+ // The user is using the keyboard to change the selection, so stop tracking
+ // hover.
+ SetHoveredLine(kNoMatch);
+
+ // Force the popup to open/update, so the user is interacting with the
+ // latest results.
+ CommitLatestResults(false);
+ if (result_.empty())
+ return;
+
+ // Clamp the new line to [0, result_.count() - 1].
+ const size_t new_line = selected_line_ + count;
+ SetSelectedLine(((count < 0) && (new_line >= selected_line_)) ?
+ 0 : std::min(new_line, result_.size() - 1));
+}
+
+void AutocompletePopupModel::TryDeletingCurrentItem() {
+ // We could use URLsForCurrentSelection() here, but it seems better to try
+ // and shift-delete the actual selection, rather than any "in progress, not
+ // yet visible" one.
+ if (selected_line_ == kNoMatch)
+ return;
+ const AutocompleteMatch& match = result_.match_at(selected_line_);
+ if (match.deletable) {
+ query_in_progress_ = true;
+ size_t selected_line = selected_line_;
+ match.provider->DeleteMatch(match); // This will synchronously call back
+ // to OnAutocompleteUpdate()
+ CommitLatestResults(false);
+ if (!result_.empty()) {
+ // Move the selection to the next choice after the deleted one, but clear
+ // the manual selection so this choice doesn't act "sticky".
+ //
+ // It might also be correct to only call Clear() here when
+ // manually_selected_match_ didn't already have a provider() (i.e. when
+ // there was no existing manual selection). It's not clear what the user
+ // wants when they shift-delete something they've arrowed to. If they
+ // arrowed down just to shift-delete it, we should probably Clear(); if
+ // they arrowed to do something else, then got a bad match while typing,
+ // we probably shouldn't.
+ SetSelectedLine(std::min(result_.size() - 1, selected_line));
+ manually_selected_match_.Clear();
+ }
+ }
+}
+
+void AutocompletePopupModel::OpenLine(size_t line,
+ WindowOpenDisposition disposition) {
+ const AutocompleteMatch& match = result_.match_at(line);
+ // OpenURL() may close the popup, which will clear the result set and, by
+ // extension, |match| and its contents. So copy the relevant strings out to
+ // make sure they stay alive until the call completes.
+ const std::wstring url(match.destination_url);
+ std::wstring keyword;
+ const bool is_keyword_hint = GetKeywordForMatch(match, &keyword);
+ editor_->OpenURL(url, disposition, match.transition, std::wstring(), line,
+ is_keyword_hint ? std::wstring() : keyword);
+}
+
+void AutocompletePopupModel::OnAutocompleteUpdate(bool updated_result,
+ bool query_complete) {
+ DCHECK(query_in_progress_);
+ if (updated_result)
+ controller_->GetResult(&latest_result_);
+ query_in_progress_ = !query_complete;
+
+ SetDefaultMatchAndUpdate(query_complete);
+}
+
+void AutocompletePopupModel::Run() {
+ CommitLatestResults(false);
+}
+
+void AutocompletePopupModel::SetDefaultMatchAndUpdate(bool immediately) {
+ if (!latest_result_.SetDefaultMatch(manually_selected_match_) &&
+ !query_in_progress_) {
+ // We don't clear the provider affinity because the user didn't do
+ // something to indicate that they want a different provider, we just
+ // couldn't find the specific match they wanted.
+ manually_selected_match_.destination_url.clear();
+ manually_selected_match_.is_history_what_you_typed_match = false;
+ }
+
+ if (immediately) {
+ CommitLatestResults(true);
+ } else if (!update_pending_) {
+ // Coalesce the results for the next kPopupCoalesceMs milliseconds.
+ update_pending_ = true;
+ MessageLoop::current()->timer_manager()->ResetTimer(coalesce_timer_.get());
+ }
+
+ // Update the edit with the possibly new data for this match.
+ // NOTE: This must be done after the code above, so that our internal state
+ // will be consistent when the edit calls back to URLsForCurrentSelection().
+ std::wstring inline_autocomplete_text;
+ std::wstring keyword;
+ bool is_keyword_hint = false;
+ bool can_show_search_hint = true;
+ const AutocompleteResult::const_iterator match(
+ latest_result_.default_match());
+ if (match != latest_result_.end()) {
+ if ((match->inline_autocomplete_offset != std::wstring::npos) &&
+ (match->inline_autocomplete_offset < match->fill_into_edit.length())) {
+ inline_autocomplete_text =
+ match->fill_into_edit.substr(match->inline_autocomplete_offset);
+ }
+ // Warm up DNS Prefetch Cache.
+ chrome_browser_net::DnsPrefetchUrlString(match->destination_url);
+ // We could prefetch the alternate nav URL, if any, but because there can be
+ // many of these as a user types an initial series of characters, the OS DNS
+ // cache could suffer eviction problems for minimal gain.
+
+ is_keyword_hint = GetKeywordForMatch(*match, &keyword);
+ can_show_search_hint = (match->type == AutocompleteMatch::SEARCH);
+ }
+ editor_->OnPopupDataChanged(inline_autocomplete_text, false,
+ manually_selected_match_ /* ignored */, keyword, is_keyword_hint,
+ can_show_search_hint);
+}
+
+void AutocompletePopupModel::CommitLatestResults(bool force) {
+ if (!force && !update_pending_)
+ return;
+
+ update_pending_ = false;
+
+ // If we're going to trim the window size to no longer include the hovered
+ // line, turn hover off first. We need to do this before changing result_
+ // since SetHoveredLine() should be able to trust that the old and new hovered
+ // lines are valid.
+ //
+ // Practically, this only currently happens when we're closing the window by
+ // setting latest_result_ to an empty list.
+ if ((hovered_line_ != kNoMatch) && (latest_result_.size() <= hovered_line_))
+ SetHoveredLine(kNoMatch);
+
+ result_.CopyFrom(latest_result_);
+ selected_line_ = (result_.default_match() == result_.end()) ?
+ kNoMatch : (result_.default_match() - result_.begin());
+
+ view_->UpdatePopupAppearance();
+
+ // The max update interval timer either needs to be reset (if more updates
+ // are to come) or stopped (when we're done with the query). The coalesce
+ // timer should always just be stopped.
+ TimerManager* const tm = MessageLoop::current()->timer_manager();
+ tm->StopTimer(coalesce_timer_.get());
+ if (query_in_progress_)
+ tm->ResetTimer(max_delay_timer_.get());
+ else
+ tm->StopTimer(max_delay_timer_.get());
+}
+
+bool AutocompletePopupModel::GetKeywordForMatch(const AutocompleteMatch& match,
+ std::wstring* keyword) {
+ // Assume we have no keyword until we find otherwise.
+ keyword->clear();
+
+ // If the current match is a keyword, return that as the selected keyword.
+ if (match.template_url && match.template_url->url() &&
+ match.template_url->url()->SupportsReplacement()) {
+ keyword->assign(match.template_url->keyword());
+ return false;
+ }
+
+ // See if the current match's fill_into_edit corresponds to a keyword.
+ if (!profile_->GetTemplateURLModel())
+ return false;
+ profile_->GetTemplateURLModel()->Load();
+ const std::wstring keyword_hint(
+ TemplateURLModel::CleanUserInputKeyword(match.fill_into_edit));
+ if (keyword_hint.empty())
+ return false;
+
+ // Don't provide a hint if this keyword doesn't support replacement.
+ const TemplateURL* const template_url =
+ profile_->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword_hint);
+ if (!template_url || !template_url->url() ||
+ !template_url->url()->SupportsReplacement())
+ return false;
+
+ keyword->assign(keyword_hint);
+ return true;
+}
diff --git a/chrome/browser/autocomplete/autocomplete_popup.h b/chrome/browser/autocomplete/autocomplete_popup.h
index 556ea49..395f774 100644
--- a/chrome/browser/autocomplete/autocomplete_popup.h
+++ b/chrome/browser/autocomplete/autocomplete_popup.h
@@ -17,25 +17,28 @@
#include "chrome/common/gfx/chrome_font.h"
#include "chrome/views/view.h"
-#define AUTOCOMPLETEPOPUP_CLASSNAME L"Chrome_AutocompletePopup"
-
class AutocompleteEdit;
+class AutocompletePopupModel;
+class AutocompletePopupView;
class Profile;
-class SkBitmap;
class MirroringContext;
+class SkBitmap;
+
+// TODO(pkasting): http://b/1343512 The names and contents of the classes in
+// this file are temporary. I am in hack-and-slash mode right now.
+
+#define AUTOCOMPLETEPOPUPVIEW_CLASSNAME L"Chrome_AutocompletePopupView"
// This class implements a popup window used by AutocompleteEdit to display
// autocomplete results.
-class AutocompletePopup
- : public CWindowImpl<AutocompletePopup, CWindow, CControlWinTraits>,
- public ACControllerListener,
- public Task {
+class AutocompletePopupView
+ : public CWindowImpl<AutocompletePopupView, CWindow, CControlWinTraits> {
public:
- DECLARE_WND_CLASS_EX(AUTOCOMPLETEPOPUP_CLASSNAME,
+ DECLARE_WND_CLASS_EX(AUTOCOMPLETEPOPUPVIEW_CLASSNAME,
((win_util::GetWinVersion() < win_util::WINVERSION_XP) ?
0 : CS_DROPSHADOW), COLOR_WINDOW)
- BEGIN_MSG_MAP(AutocompletePopup)
+ BEGIN_MSG_MAP(AutocompletePopupView)
MSG_WM_ERASEBKGND(OnEraseBkgnd)
MSG_WM_LBUTTONDOWN(OnLButtonDown)
MSG_WM_MBUTTONDOWN(OnMButtonDown)
@@ -47,114 +50,20 @@ class AutocompletePopup
MSG_WM_PAINT(OnPaint)
END_MSG_MAP()
- AutocompletePopup(const ChromeFont& font,
- AutocompleteEdit* editor,
- Profile* profile);
- ~AutocompletePopup();
-
- // Invoked when the profile has changed.
- void SetProfile(Profile* profile);
-
- // Gets autocomplete results for the given text. If there are results, opens
- // the popup if necessary and fills it with the new data. Otherwise, closes
- // the popup if necessary.
- //
- // |prevent_inline_autocomplete| is true if the generated result set should
- // not require inline autocomplete for the default match. This is difficult
- // to explain in the abstract; the practical use case is that after the user
- // deletes text in the edit, the HistoryURLProvider should make sure not to
- //promote a match requiring inline autocomplete too highly.
- void StartAutocomplete(const std::wstring& text,
- const std::wstring& desired_tld,
- bool prevent_inline_autocomplete);
-
- // Closes the window and cancels any pending asynchronous queries
- void StopAutocomplete();
-
- // Returns true if no autocomplete query is currently running.
- bool query_in_progress() const { return query_in_progress_; }
+ AutocompletePopupView(AutocompletePopupModel* model, const ChromeFont& font);
// Returns true if the popup is currently open.
bool is_open() const { return m_hWnd != NULL; }
- // Returns the URL for the selected match. If an update is in progress,
- // "selected" means "default in the latest results". If there are no
- // results, returns the empty string.
- //
- // If |transition_type| is non-NULL, it will be set to the appropriate
- // transition type for the selected entry (TYPED or GENERATED).
- //
- // If |is_history_what_you_typed_match| is non-NULL, it will be set based on
- // the selected entry's is_history_what_you_typed value.
- //
- // If |alternate_nav_url| is non-NULL, it will be set to the alternate
- // navigation URL for |url| if one exists, or left unchanged otherwise. See
- // comments on AutocompleteResult::GetAlternateNavURL().
- std::wstring URLsForCurrentSelection(
- PageTransition::Type* transition,
- bool* is_history_what_you_typed_match,
- std::wstring* alternate_nav_url) const;
-
- // This is sort of a hybrid between StartAutocomplete() and
- // URLForCurrentSelection(). When the popup isn't open and the user hits
- // enter, we want to get the default result for the user's input immediately,
- // and not open the popup, continue running autocomplete, etc. Therefore,
- // this does a query for only the synchronously available results for the
- // provided input parameters, sets |transition|,
- // |is_history_what_you_typed_match|, and |alternate_nav_url| (if applicable)
- // based on the default match, and returns its url. |transition|,
- // |is_history_what_you_typed_match| and/or |alternate_nav_url| may be null,
- // in which case they are not updated.
- //
- // If there are no matches for |text|, leaves the outparams unset and returns
- // the empty string.
- std::wstring URLsForDefaultMatch(const std::wstring& text,
- const std::wstring& desired_tld,
- PageTransition::Type* transition,
- bool* is_history_what_you_typed_match,
- std::wstring* alternate_nav_url);
-
- // Returns a pointer to a heap-allocated AutocompleteLog containing the
- // current input text, selected match, and result set. The caller is
- // responsible for deleting the object.
- AutocompleteLog* GetAutocompleteLog();
-
- // Immediately updates and opens the popup if necessary, then moves the
- // current selection down (|count| > 0) or up (|count| < 0), clamping to the
- // first or last result if necessary. If |count| == 0, the selection will be
- // unchanged, but the popup will still redraw and modify the text in the
- // AutocompleteEdit.
- void Move(int count);
-
- // Called when the user hits shift-delete. This should determine if the item
- // can be removed from history, and if so, remove it and update the popup.
- void TryDeletingCurrentItem();
-
- // ACControllerListener - called when more autocomplete data is available or
- // when the query is complete.
- //
- // When the input for the current query has a provider affinity, we try to
- // keep the current result set's default match as the new default match.
- virtual void OnAutocompleteUpdate(bool updated_result, bool query_complete);
-
- // Task - called when either timer fires. Calls CommitLatestResults().
- virtual void Run();
-
- // Returns the AutocompleteController used by this popup.
- AutocompleteController* autocomplete_controller() const {
- return controller_.get();
- }
-
- const AutocompleteResult* latest_result() const {
- return &latest_result_;
- }
+ // Invalidates one line of the autocomplete popup.
+ void InvalidateLine(size_t line);
- // The match the user has manually chosen, if any.
- AutocompleteResult::Selection manually_selected_match_;
+ // Redraws the popup window to match any changes in result_; this may mean
+ // opening or closing the window.
+ void UpdatePopupAppearance();
- // The token value for selected_line_, hover_line_ and functions dealing with
- // a "line number" that indicates "no line".
- static const size_t kNoMatch = -1;
+ // Called by the model when hover is enabled or disabled.
+ void OnHoverEnabledOrDisabled(bool disabled);
private:
// Caches GDI objects and information for drawing.
@@ -218,20 +127,6 @@ class AutocompletePopup
// disposition.
void OnButtonUp(const CPoint& point, WindowOpenDisposition disposition);
- // Sets the correct default match in latest_result_, then updates the popup
- // appearance to match. If |immediately| is true this update happens
- // synchronously; otherwise, it's deferred until the next scheduled update.
- void SetDefaultMatchAndUpdate(bool immediately);
-
- // If an update is pending or |force| is true, immediately updates result_ to
- // match latest_result_, and calls UpdatePopup() to reflect those changes
- // back to the user.
- void CommitLatestResults(bool force);
-
- // Redraws the popup window to match any changes in result_; this may mean
- // opening or closing the window.
- void UpdatePopupAppearance();
-
// Gives the topmost y coordinate within |line|, which should be within the
// range of valid lines.
int LineTopPixel(size_t line) const;
@@ -243,58 +138,13 @@ class AutocompletePopup
// bottom edges of the window, respectively.
size_t PixelToLine(int y) const;
- // Call to change the hovered line. |line| should be within the range of
- // valid lines (to enable hover) or kNoMatch (to disable hover).
- void SetHoveredLine(size_t line);
-
- // Call to change the selected line. This will update all state and repaint
- // the necessary parts of the window, as well as updating the edit with the
- // new temporary text. |line| should be within the range of valid lines.
- // NOTE: This assumes the popup is open, and thus both old and new values for
- // the selected line should not be kNoMatch.
- void SetSelectedLine(size_t line);
-
- // Invalidates one line of the autocomplete popup.
- void InvalidateLine(size_t line);
-
// Draws a light border around the inside of the window with the given client
// rectangle and DC.
void DrawBorder(const RECT& rc, HDC dc);
- // Draw a string at the specified location with the specified style.
- // This function is a wrapper function of the DrawText() function to handle
- // bidirectional strings.
- // Parameters
- // * dc [in] (HDC)
- // Represents the display context to render the given string.
- // * x [in] (int)
- // Specifies the left of the bounding rectangle,
- // * y [in] (int)
- // Specifies the top of the bounding rectangle,
- // * max_x [in] (int)
- // Specifies the right of the bounding rectangle.
- // * text [in] (const wchar_t*)
- // Specifies the pointer to the string to be rendered.
- // * length [in] (int)
- // Specifies the number of characters in the string.
- // * style [in] (int)
- // Specifies the classifications for this string.
- // This value is a combination of the following values:
- // - ACMatchClassifications::NONE
- // - ACMatchClassifications::URL
- // - ACMatchClassifications::MATCH
- // - ACMatchClassifications::DIM
- // * status [in] (const DrawLineInfo::LineStatus)
- // Specifies the classifications for this line.
- // * context [in] (const MirroringContext*)
- // Specifies the context used for mirroring the x-coordinates.
- // * text_direction_is_rtl [in] (bool)
- // Determines whether we need to render the string as an RTL string, which
- // impacts, for example, which side leading/trailing whitespace and
- // punctuation appear on.
- // Return Values
- // * a positive value
- // Represents the width of the displayed string, in pixels.
+ // Draws a single run of text with a particular style. Handles both LTR and
+ // RTL text as well as eliding. Returns the width, in pixels, of the string
+ // as it was actually displayed.
int DrawString(HDC dc,
int x,
int y,
@@ -327,6 +177,176 @@ class AutocompletePopup
// Draws the star at the specified location
void DrawStar(HDC dc, int x, int y) const;
+ AutocompletePopupModel* model_;
+
+ // Cached GDI information for drawing.
+ DrawLineInfo line_info_;
+
+ // Bitmap for the star. This is owned by the ResourceBundle.
+ SkBitmap* star_;
+
+ // A context used for mirroring regions.
+ scoped_ptr<MirroringContext> mirroring_context_;
+
+ // When hovered_line_ is kNoMatch, this holds the screen coordinates of the
+ // mouse position when hover tracking was turned off. If the mouse moves to a
+ // point over the popup that has different coordinates, hover tracking will be
+ // re-enabled. When hovered_line_ is a valid line, the value here is
+ // out-of-date and should be ignored.
+ CPoint last_hover_coordinates_;
+};
+
+class AutocompletePopupModel : public ACControllerListener, public Task {
+ public:
+ AutocompletePopupModel(const ChromeFont& font,
+ AutocompleteEdit* editor,
+ Profile* profile);
+ ~AutocompletePopupModel();
+
+ // Invoked when the profile has changed.
+ void SetProfile(Profile* profile);
+
+ // Gets autocomplete results for the given text. If there are results, opens
+ // the popup if necessary and fills it with the new data. Otherwise, closes
+ // the popup if necessary.
+ //
+ // |prevent_inline_autocomplete| is true if the generated result set should
+ // not require inline autocomplete for the default match. This is difficult
+ // to explain in the abstract; the practical use case is that after the user
+ // deletes text in the edit, the HistoryURLProvider should make sure not to
+ //promote a match requiring inline autocomplete too highly.
+ void StartAutocomplete(const std::wstring& text,
+ const std::wstring& desired_tld,
+ bool prevent_inline_autocomplete);
+
+ // Closes the window and cancels any pending asynchronous queries
+ void StopAutocomplete();
+
+ // Returns true if the popup is currently open.
+ bool is_open() const { return view_->is_open(); }
+
+ // TODO(pkasting): http://b/134593 This is temporary and should die.
+ const AutocompleteEdit* editor() const { return editor_; }
+
+ // Returns the AutocompleteController used by this popup.
+ AutocompleteController* autocomplete_controller() const {
+ return controller_.get();
+ }
+
+ // Returns true if no autocomplete query is currently running.
+ bool query_in_progress() const { return query_in_progress_; }
+
+ const AutocompleteResult* result() const {
+ return &result_;
+ }
+
+ const AutocompleteResult* latest_result() const {
+ return &latest_result_;
+ }
+
+ size_t hovered_line() const {
+ return hovered_line_;
+ }
+
+ // Call to change the hovered line. |line| should be within the range of
+ // valid lines (to enable hover) or kNoMatch (to disable hover).
+ void SetHoveredLine(size_t line);
+
+ size_t selected_line() const {
+ return selected_line_;
+ }
+
+ // Call to change the selected line. This will update all state and repaint
+ // the necessary parts of the window, as well as updating the edit with the
+ // new temporary text. |line| should be within the range of valid lines.
+ // NOTE: This assumes the popup is open, and thus both old and new values for
+ // the selected line should not be kNoMatch.
+ void SetSelectedLine(size_t line);
+
+ // Returns the URL for the selected match. If an update is in progress,
+ // "selected" means "default in the latest results". If there are no
+ // results, returns the empty string.
+ //
+ // If |transition_type| is non-NULL, it will be set to the appropriate
+ // transition type for the selected entry (TYPED or GENERATED).
+ //
+ // If |is_history_what_you_typed_match| is non-NULL, it will be set based on
+ // the selected entry's is_history_what_you_typed value.
+ //
+ // If |alternate_nav_url| is non-NULL, it will be set to the alternate
+ // navigation URL for |url| if one exists, or left unchanged otherwise. See
+ // comments on AutocompleteResult::GetAlternateNavURL().
+ std::wstring URLsForCurrentSelection(
+ PageTransition::Type* transition,
+ bool* is_history_what_you_typed_match,
+ std::wstring* alternate_nav_url) const;
+
+ // This is sort of a hybrid between StartAutocomplete() and
+ // URLForCurrentSelection(). When the popup isn't open and the user hits
+ // enter, we want to get the default result for the user's input immediately,
+ // and not open the popup, continue running autocomplete, etc. Therefore,
+ // this does a query for only the synchronously available results for the
+ // provided input parameters, sets |transition|,
+ // |is_history_what_you_typed_match|, and |alternate_nav_url| (if applicable)
+ // based on the default match, and returns its url. |transition|,
+ // |is_history_what_you_typed_match| and/or |alternate_nav_url| may be null,
+ // in which case they are not updated.
+ //
+ // If there are no matches for |text|, leaves the outparams unset and returns
+ // the empty string.
+ std::wstring URLsForDefaultMatch(const std::wstring& text,
+ const std::wstring& desired_tld,
+ PageTransition::Type* transition,
+ bool* is_history_what_you_typed_match,
+ std::wstring* alternate_nav_url);
+
+ // Returns a pointer to a heap-allocated AutocompleteLog containing the
+ // current input text, selected match, and result set. The caller is
+ // responsible for deleting the object.
+ AutocompleteLog* GetAutocompleteLog();
+
+ // Immediately updates and opens the popup if necessary, then moves the
+ // current selection down (|count| > 0) or up (|count| < 0), clamping to the
+ // first or last result if necessary. If |count| == 0, the selection will be
+ // unchanged, but the popup will still redraw and modify the text in the
+ // AutocompleteEdit.
+ void Move(int count);
+
+ // Called when the user hits shift-delete. This should determine if the item
+ // can be removed from history, and if so, remove it and update the popup.
+ void TryDeletingCurrentItem();
+
+ // Called by the view to open the URL corresponding to a particular line.
+ void OpenLine(size_t line, WindowOpenDisposition disposition);
+
+ // ACControllerListener - called when more autocomplete data is available or
+ // when the query is complete.
+ //
+ // When the input for the current query has a provider affinity, we try to
+ // keep the current result set's default match as the new default match.
+ virtual void OnAutocompleteUpdate(bool updated_result, bool query_complete);
+
+ // Task - called when either timer fires. Calls CommitLatestResults().
+ virtual void Run();
+
+ // The match the user has manually chosen, if any.
+ AutocompleteResult::Selection manually_selected_match_;
+
+ // The token value for selected_line_, hover_line_ and functions dealing with
+ // a "line number" that indicates "no line".
+ static const size_t kNoMatch = -1;
+
+ private:
+ // Sets the correct default match in latest_result_, then updates the popup
+ // appearance to match. If |immediately| is true this update happens
+ // synchronously; otherwise, it's deferred until the next scheduled update.
+ void SetDefaultMatchAndUpdate(bool immediately);
+
+ // If an update is pending or |force| is true, immediately updates result_ to
+ // match latest_result_, and calls UpdatePopupAppearance() to reflect those
+ // changes back to the user.
+ void CommitLatestResults(bool force);
+
// Gets the selected keyword or keyword hint for the given match. Returns
// true if |keyword| represents a keyword hint, or false if |keyword|
// represents a selected keyword. (|keyword| will always be set [though
@@ -335,15 +355,14 @@ class AutocompletePopup
bool GetKeywordForMatch(const AutocompleteMatch& match,
std::wstring* keyword);
+ scoped_ptr<AutocompletePopupView> view_;
+
AutocompleteEdit* editor_;
scoped_ptr<AutocompleteController> controller_;
// Profile for current tab.
Profile* profile_;
- // Cached GDI information for drawing.
- DrawLineInfo line_info_;
-
// The input for the current query.
AutocompleteInput input_;
@@ -380,22 +399,9 @@ class AutocompletePopup
// this will be kNoMatch, even if the cursor is over the popup contents.
size_t hovered_line_;
- // When hover_line_ is kNoMatch, this holds the screen coordinates of the
- // mouse position when hover tracking was turned off. If the mouse moves to a
- // point over the popup that has different coordinates, hover tracking will be
- // re-enabled. When hover_line_ is a valid line, the value here is
- // out-of-date and should be ignored.
- CPoint last_hover_coordinates_;
-
// The currently selected line. This is kNoMatch when nothing is selected,
// which should only be true when the popup is closed.
size_t selected_line_;
-
- // Bitmap for the star. This is owned by the ResourceBundle.
- SkBitmap* star_;
-
- // A context used for mirroring regions.
- scoped_ptr<MirroringContext> mirroring_context_;
};
#endif // CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_POPUP_H_