summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/autocomplete/autocomplete_accessibility.cc4
-rw-r--r--chrome/browser/autocomplete/autocomplete_accessibility.h16
-rw-r--r--chrome/browser/autocomplete/autocomplete_edit.cc1934
-rw-r--r--chrome/browser/autocomplete/autocomplete_edit.h501
-rw-r--r--chrome/browser/autocomplete/autocomplete_edit_view.h117
-rw-r--r--chrome/browser/autocomplete/autocomplete_edit_view_win.cc2185
-rw-r--r--chrome/browser/autocomplete/autocomplete_edit_view_win.h458
-rwxr-xr-xchrome/browser/autocomplete/autocomplete_popup_view_win.cc2
-rwxr-xr-xchrome/browser/autocomplete/autocomplete_popup_view_win.h11
-rw-r--r--chrome/browser/autocomplete/edit_drop_target.cc152
-rw-r--r--chrome/browser/autocomplete/edit_drop_target.h58
-rw-r--r--chrome/browser/automation/automation_autocomplete_edit_tracker.h1
-rw-r--r--chrome/browser/browser.vcproj18
-rw-r--r--chrome/browser/views/location_bar_view.cc8
-rw-r--r--chrome/browser/views/location_bar_view.h3
15 files changed, 2806 insertions, 2662 deletions
diff --git a/chrome/browser/autocomplete/autocomplete_accessibility.cc b/chrome/browser/autocomplete/autocomplete_accessibility.cc
index 5d61fab..807be73 100644
--- a/chrome/browser/autocomplete/autocomplete_accessibility.cc
+++ b/chrome/browser/autocomplete/autocomplete_accessibility.cc
@@ -4,13 +4,15 @@
#include "chrome/browser/autocomplete/autocomplete_accessibility.h"
+#include "chrome/browser/autocomplete/autocomplete_edit.h"
+#include "chrome/browser/autocomplete/autocomplete_edit_view_win.h"
#include "chrome/common/l10n_util.h"
#include "chrome/views/accessibility/accessible_wrapper.h"
#include "chrome/views/view.h"
#include "grit/generated_resources.h"
HRESULT AutocompleteAccessibility::Initialize(
- const AutocompleteEditView* edit_box) {
+ const AutocompleteEditViewWin* edit_box) {
if (edit_box == NULL) {
return E_INVALIDARG;
}
diff --git a/chrome/browser/autocomplete/autocomplete_accessibility.h b/chrome/browser/autocomplete/autocomplete_accessibility.h
index cd5f970..8671c1f 100644
--- a/chrome/browser/autocomplete/autocomplete_accessibility.h
+++ b/chrome/browser/autocomplete/autocomplete_accessibility.h
@@ -7,18 +7,19 @@
#include <atlbase.h>
#include <atlcom.h>
-
#include <oleacc.h>
-#include "chrome/browser/autocomplete/autocomplete_edit.h"
+#include "base/basictypes.h"
+
+class AutocompleteEditViewWin;
////////////////////////////////////////////////////////////////////////////////
//
// AutocompleteAccessibility
//
// Class implementing the MSAA IAccessible COM interface for
-// AutocompleteEditView, providing accessibility to be used by screen readers
-// and other assistive technology (AT).
+// AutocompleteEditViewWin, providing accessibility to be used by screen
+// readers and other assistive technology (AT).
//
////////////////////////////////////////////////////////////////////////////////
class ATL_NO_VTABLE AutocompleteAccessibility
@@ -33,7 +34,7 @@ class ATL_NO_VTABLE AutocompleteAccessibility
AutocompleteAccessibility() {}
~AutocompleteAccessibility() {}
- HRESULT Initialize(const AutocompleteEditView* edit_box);
+ HRESULT Initialize(const AutocompleteEditViewWin* edit_box);
// Supported IAccessible methods.
@@ -105,8 +106,9 @@ class ATL_NO_VTABLE AutocompleteAccessibility
CComPtr<IAccessible> default_accessibility_server_;
private:
- const AutocompleteEditView* edit_box_;
+ const AutocompleteEditViewWin* edit_box_;
- DISALLOW_EVIL_CONSTRUCTORS(AutocompleteAccessibility);
+ DISALLOW_COPY_AND_ASSIGN(AutocompleteAccessibility);
};
+
#endif // CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_ACCESSIBILITY_H_
diff --git a/chrome/browser/autocomplete/autocomplete_edit.cc b/chrome/browser/autocomplete/autocomplete_edit.cc
index 3a0f56b..9d3d843a 100644
--- a/chrome/browser/autocomplete/autocomplete_edit.cc
+++ b/chrome/browser/autocomplete/autocomplete_edit.cc
@@ -6,11 +6,8 @@
#include <locale>
-#include "base/base_drag_source.h"
+// TODO(deanm): Cleanup these includes.
#include "base/basictypes.h"
-#include "base/clipboard.h"
-#include "base/iat_patch.h"
-#include "base/lazy_instance.h"
#include "base/ref_counted.h"
#include "base/scoped_clipboard_writer.h"
#include "base/string_util.h"
@@ -31,18 +28,12 @@
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/views/location_bar_view.h"
#include "chrome/common/clipboard_service.h"
-#include "chrome/common/gfx/chrome_canvas.h"
-#include "chrome/common/gfx/utils.h"
-#include "chrome/common/l10n_util.h"
-#include "chrome/common/l10n_util_win.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/os_exchange_data.h"
#include "chrome/common/win_util.h"
#include "googleurl/src/url_util.h"
#include "grit/generated_resources.h"
-#include "skia/ext/skia_utils_win.h"
-
-#pragma comment(lib, "oleacc.lib") // Needed for accessibility support.
+#include "skia/include/SkBitmap.h"
///////////////////////////////////////////////////////////////////////////////
// AutocompleteEditModel
@@ -309,11 +300,7 @@ void AutocompleteEditModel::SendOpenNotification(size_t selected_line,
void AutocompleteEditModel::AcceptKeyword() {
view_->OnBeforePossibleChange();
- // NOTE: We don't need the IME composition hack in SetWindowTextAndCaretPos()
- // here, because any active IME composition will eat <tab> characters,
- // preventing the user from using tab-to-search until the composition is
- // ended.
- view_->SetWindowText(L"");
+ view_->SetWindowTextAndCaretPos(std::wstring(), 0);
is_keyword_hint_ = false;
keyword_ui_state_ = KEYWORD;
view_->OnAfterPossibleChange();
@@ -581,1918 +568,3 @@ GURL AutocompleteEditModel::GetURLForCurrentText(
is_history_what_you_typed_match,
alternate_nav_url);
}
-
-///////////////////////////////////////////////////////////////////////////////
-// Helper classes
-
-AutocompleteEditView::ScopedFreeze::ScopedFreeze(
- AutocompleteEditView* edit,
- ITextDocument* text_object_model)
- : edit_(edit),
- text_object_model_(text_object_model) {
- // Freeze the screen.
- if (text_object_model_) {
- long count;
- text_object_model_->Freeze(&count);
- }
-}
-
-AutocompleteEditView::ScopedFreeze::~ScopedFreeze() {
- // Unfreeze the screen.
- // NOTE: If this destructor is reached while the edit is being destroyed (for
- // example, because we double-clicked the edit of a popup and caused it to
- // transform to an unconstrained window), it will no longer have an HWND, and
- // text_object_model_ may point to a destroyed object, so do nothing here.
- if (edit_->IsWindow() && text_object_model_) {
- long count;
- text_object_model_->Unfreeze(&count);
- if (count == 0) {
- // We need to UpdateWindow() here instead of InvalidateRect() because, as
- // far as I can tell, the edit likes to synchronously erase its background
- // when unfreezing, thus requiring us to synchronously redraw if we don't
- // want flicker.
- edit_->UpdateWindow();
- }
- }
-}
-
-AutocompleteEditView::ScopedSuspendUndo::ScopedSuspendUndo(
- ITextDocument* text_object_model)
- : text_object_model_(text_object_model) {
- // Suspend Undo processing.
- if (text_object_model_)
- text_object_model_->Undo(tomSuspend, NULL);
-}
-
-AutocompleteEditView::ScopedSuspendUndo::~ScopedSuspendUndo() {
- // Resume Undo processing.
- if (text_object_model_)
- text_object_model_->Undo(tomResume, NULL);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// AutocompleteEditView
-
-// TODO (jcampan): these colors should be derived from the system colors to
-// ensure they show properly. Bug #948807.
-// Colors used to emphasize the scheme in the URL.
-
-namespace {
-
-const COLORREF kSecureSchemeColor = RGB(0, 150, 20);
-const COLORREF kInsecureSchemeColor = RGB(200, 0, 0);
-
-// Colors used to strike-out the scheme when it is insecure.
-const SkColor kSchemeStrikeoutColor = SkColorSetRGB(210, 0, 0);
-const SkColor kSchemeSelectedStrikeoutColor =
- SkColorSetRGB(255, 255, 255);
-
-// These are used to hook the CRichEditCtrl's calls to BeginPaint() and
-// EndPaint() and provide a memory DC instead. See OnPaint().
-HWND edit_hwnd = NULL;
-PAINTSTRUCT paint_struct;
-
-HDC BeginPaintIntercept(HWND hWnd, LPPAINTSTRUCT lpPaint) {
- if (!edit_hwnd || (hWnd != edit_hwnd))
- return ::BeginPaint(hWnd, lpPaint);
-
- *lpPaint = paint_struct;
- return paint_struct.hdc;
-}
-
-BOOL EndPaintIntercept(HWND hWnd, const PAINTSTRUCT* lpPaint) {
- return (edit_hwnd && (hWnd == edit_hwnd)) ?
- true : ::EndPaint(hWnd, lpPaint);
-}
-
-// Returns a lazily initialized property bag accessor for saving our state in a
-// TabContents.
-PropertyAccessor<AutocompleteEditState>* GetStateAccessor() {
- static PropertyAccessor<AutocompleteEditState> state;
- return &state;
-}
-
-class PaintPatcher {
- public:
- PaintPatcher() : refcount_(0) { }
- ~PaintPatcher() { DCHECK(refcount_ == 0); }
-
- void RefPatch() {
- if (refcount_ == 0) {
- DCHECK(!begin_paint_.is_patched());
- DCHECK(!end_paint_.is_patched());
- begin_paint_.Patch(L"riched20.dll", "user32.dll", "BeginPaint",
- &BeginPaintIntercept);
- end_paint_.Patch(L"riched20.dll", "user32.dll", "EndPaint",
- &EndPaintIntercept);
- }
- ++refcount_;
- }
-
- void DerefPatch() {
- DCHECK(begin_paint_.is_patched());
- DCHECK(end_paint_.is_patched());
- --refcount_;
- if (refcount_ == 0) {
- begin_paint_.Unpatch();
- end_paint_.Unpatch();
- }
- }
-
- private:
- size_t refcount_;
- iat_patch::IATPatchFunction begin_paint_;
- iat_patch::IATPatchFunction end_paint_;
-
- DISALLOW_COPY_AND_ASSIGN(PaintPatcher);
-};
-
-base::LazyInstance<PaintPatcher> g_paint_patcher(base::LINKER_INITIALIZED);
-
-} // namespace
-
-AutocompleteEditView::AutocompleteEditView(
- const ChromeFont& font,
- AutocompleteEditController* controller,
- ToolbarModel* toolbar_model,
- views::View* parent_view,
- HWND hwnd,
- Profile* profile,
- CommandUpdater* command_updater,
- bool popup_window_mode)
- : model_(new AutocompleteEditModel(this, controller, profile)),
- popup_view_(new AutocompletePopupViewWin(font, this, model_.get(),
- profile)),
- controller_(controller),
- parent_view_(parent_view),
- toolbar_model_(toolbar_model),
- command_updater_(command_updater),
- popup_window_mode_(popup_window_mode),
- tracking_click_(false),
- tracking_double_click_(false),
- double_click_time_(0),
- can_discard_mousemove_(false),
- font_(font),
- possible_drag_(false),
- in_drag_(false),
- initiated_drag_(false),
- drop_highlight_position_(-1),
- background_color_(0),
- scheme_security_level_(ToolbarModel::NORMAL) {
- model_->set_popup_model(popup_view_->model());
-
- saved_selection_for_focus_change_.cpMin = -1;
-
- g_paint_patcher.Pointer()->RefPatch();
-
- Create(hwnd, 0, 0, 0, l10n_util::GetExtendedStyles());
- SetReadOnly(popup_window_mode_);
- SetFont(font_.hfont());
-
- // NOTE: Do not use SetWordBreakProcEx() here, that is no longer supported as
- // of Rich Edit 2.0 onward.
- SendMessage(m_hWnd, EM_SETWORDBREAKPROC, 0,
- reinterpret_cast<LPARAM>(&WordBreakProc));
-
- // Get the metrics for the font.
- HDC dc = ::GetDC(NULL);
- SelectObject(dc, font_.hfont());
- TEXTMETRIC tm = {0};
- GetTextMetrics(dc, &tm);
- font_ascent_ = tm.tmAscent;
- const float kXHeightRatio = 0.7f; // The ratio of a font's x-height to its
- // cap height. Sadly, Windows doesn't
- // provide a true value for a font's
- // x-height in its text metrics, so we
- // approximate.
- font_x_height_ = static_cast<int>((static_cast<float>(font_ascent_ -
- tm.tmInternalLeading) * kXHeightRatio) + 0.5);
- // The distance from the top of the field to the desired baseline of the
- // rendered text.
- const int kTextBaseline = popup_window_mode_ ? 15 : 18;
- font_y_adjustment_ = kTextBaseline - font_ascent_;
- font_descent_ = tm.tmDescent;
-
- // Get the number of twips per pixel, which we need below to offset our text
- // by the desired number of pixels.
- const long kTwipsPerPixel = kTwipsPerInch / GetDeviceCaps(dc, LOGPIXELSY);
- ::ReleaseDC(NULL, dc);
-
- // Set the default character style -- adjust to our desired baseline and make
- // text grey.
- CHARFORMAT cf = {0};
- cf.dwMask = CFM_OFFSET | CFM_COLOR;
- cf.yOffset = -font_y_adjustment_ * kTwipsPerPixel;
- cf.crTextColor = GetSysColor(COLOR_GRAYTEXT);
- SetDefaultCharFormat(cf);
-
- // Set up context menu.
- context_menu_.reset(new Menu(this, Menu::TOPLEFT, m_hWnd));
- if (popup_window_mode_) {
- context_menu_->AppendMenuItemWithLabel(IDS_COPY,
- l10n_util::GetString(IDS_COPY));
- } else {
- context_menu_->AppendMenuItemWithLabel(IDS_UNDO,
- l10n_util::GetString(IDS_UNDO));
- context_menu_->AppendSeparator();
- context_menu_->AppendMenuItemWithLabel(IDS_CUT,
- l10n_util::GetString(IDS_CUT));
- context_menu_->AppendMenuItemWithLabel(IDS_COPY,
- l10n_util::GetString(IDS_COPY));
- context_menu_->AppendMenuItemWithLabel(IDS_PASTE,
- l10n_util::GetString(IDS_PASTE));
- // GetContextualLabel() will override this next label with the
- // IDS_PASTE_AND_SEARCH label as needed.
- context_menu_->AppendMenuItemWithLabel(
- IDS_PASTE_AND_GO, l10n_util::GetString(IDS_PASTE_AND_GO));
- context_menu_->AppendSeparator();
- context_menu_->AppendMenuItemWithLabel(
- IDS_SELECT_ALL, l10n_util::GetString(IDS_SELECT_ALL));
- context_menu_->AppendSeparator();
- context_menu_->AppendMenuItemWithLabel(
- IDS_EDIT_SEARCH_ENGINES, l10n_util::GetString(IDS_EDIT_SEARCH_ENGINES));
- }
-
- // By default RichEdit has a drop target. Revoke it so that we can install our
- // own. Revoke takes care of deleting the existing one.
- RevokeDragDrop(m_hWnd);
-
- // Register our drop target. RichEdit appears to invoke RevokeDropTarget when
- // done so that we don't have to explicitly.
- if (!popup_window_mode_) {
- scoped_refptr<EditDropTarget> drop_target = new EditDropTarget(this);
- RegisterDragDrop(m_hWnd, drop_target.get());
- }
-}
-
-AutocompleteEditView::~AutocompleteEditView() {
- NotificationService::current()->Notify(
- NotificationType::AUTOCOMPLETE_EDIT_DESTROYED,
- Source<AutocompleteEditView>(this),
- NotificationService::NoDetails());
-
- // We balance our reference count and unpatch when the last instance has
- // been destroyed. This prevents us from relying on the AtExit or static
- // destructor sequence to do our unpatching, which is generally fragile.
- g_paint_patcher.Pointer()->DerefPatch();
-}
-
-void AutocompleteEditView::SaveStateToTab(TabContents* tab) {
- DCHECK(tab);
-
- const AutocompleteEditModel::State model_state(
- model_->GetStateForTabSwitch());
-
- CHARRANGE selection;
- GetSelection(selection);
- GetStateAccessor()->SetProperty(tab->property_bag(),
- AutocompleteEditState(
- model_state,
- State(selection, saved_selection_for_focus_change_)));
-}
-
-void AutocompleteEditView::Update(const TabContents* tab_for_state_restoring) {
- const bool visibly_changed_permanent_text =
- model_->UpdatePermanentText(toolbar_model_->GetText());
-
- const ToolbarModel::SecurityLevel security_level =
- toolbar_model_->GetSchemeSecurityLevel();
- const COLORREF background_color =
- LocationBarView::kBackgroundColorByLevel[security_level];
- const bool changed_security_level =
- (security_level != scheme_security_level_);
-
- // Bail early when no visible state will actually change (prevents an
- // unnecessary ScopedFreeze, and thus UpdateWindow()).
- if ((background_color == background_color_) && !changed_security_level &&
- !visibly_changed_permanent_text && !tab_for_state_restoring)
- return;
-
- // Update our local state as desired. We set scheme_security_level_ here so
- // it will already be correct before we get to any RevertAll()s below and use
- // it.
- ScopedFreeze freeze(this, GetTextObjectModel());
- if (background_color_ != background_color) {
- background_color_ = background_color;
- SetBackgroundColor(background_color_);
- }
- scheme_security_level_ = security_level;
-
- // When we're switching to a new tab, restore its state, if any.
- if (tab_for_state_restoring) {
- // Make sure we reset our own state first. The new tab may not have any
- // saved state, or it may not have had input in progress, in which case we
- // won't overwrite all our local state.
- RevertAll();
-
- const AutocompleteEditState* state = GetStateAccessor()->GetProperty(
- tab_for_state_restoring->property_bag());
- if (state) {
- model_->RestoreState(state->model_state);
-
- // Restore user's selection. We do this after restoring the user_text
- // above so we're selecting in the correct string.
- SetSelectionRange(state->view_state.selection);
- saved_selection_for_focus_change_ =
- state->view_state.saved_selection_for_focus_change;
- }
- } else if (visibly_changed_permanent_text) {
- // Not switching tabs, just updating the permanent text. (In the case where
- // we _were_ switching tabs, the RevertAll() above already drew the new
- // permanent text.)
-
- // Tweak: if the edit was previously nonempty and had all the text selected,
- // select all the new text. This makes one particular case better: the
- // user clicks in the box to change it right before the permanent URL is
- // changed. Since the new URL is still fully selected, the user's typing
- // will replace the edit contents as they'd intended.
- //
- // NOTE: The selection can be longer than the text length if the edit is in
- // in rich text mode and the user has selected the "phantom newline" at the
- // end, so use ">=" instead of "==" to see if all the text is selected. In
- // theory we prevent this case from ever occurring, but this is still safe.
- CHARRANGE sel;
- GetSelection(sel);
- const bool was_reversed = (sel.cpMin > sel.cpMax);
- const bool was_sel_all = (sel.cpMin != sel.cpMax) &&
- IsSelectAllForRange(sel);
-
- RevertAll();
-
- if (was_sel_all)
- SelectAll(was_reversed);
- } else if (changed_security_level) {
- // Only the security style changed, nothing else. Redraw our text using it.
- EmphasizeURLComponents();
- }
-}
-
-void AutocompleteEditView::OpenURL(const GURL& url,
- WindowOpenDisposition disposition,
- PageTransition::Type transition,
- const GURL& alternate_nav_url,
- size_t selected_line,
- const std::wstring& keyword) {
- if (!url.is_valid())
- return;
-
- model_->SendOpenNotification(selected_line, keyword);
-
- ScopedFreeze freeze(this, GetTextObjectModel());
- if (disposition != NEW_BACKGROUND_TAB)
- RevertAll(); // Revert the box to its unedited state
- controller_->OnAutocompleteAccept(url, disposition, transition,
- alternate_nav_url);
-}
-
-std::wstring AutocompleteEditView::GetText() const {
- const int len = GetTextLength() + 1;
- std::wstring str;
- GetWindowText(WriteInto(&str, len), len);
- return str;
-}
-
-void AutocompleteEditView::SetUserText(const std::wstring& text,
- const std::wstring& display_text,
- bool update_popup) {
- ScopedFreeze freeze(this, GetTextObjectModel());
- model_->SetUserText(text);
- saved_selection_for_focus_change_.cpMin = -1;
- SetWindowTextAndCaretPos(display_text, display_text.length());
- if (update_popup)
- UpdatePopup();
- TextChanged();
-}
-
-void AutocompleteEditView::SetWindowTextAndCaretPos(const std::wstring& text,
- size_t caret_pos) {
- HIMC imm_context = ImmGetContext(m_hWnd);
- if (imm_context) {
- // In Windows Vista, SetWindowText() automatically completes any ongoing
- // IME composition, and updates the text of the underlying edit control.
- // In Windows XP, however, SetWindowText() gets applied to the IME
- // composition string if it exists, and doesn't update the underlying edit
- // control. To avoid this, we force the IME to complete any outstanding
- // compositions here. This is harmless in Vista and in cases where the IME
- // isn't composing.
- ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
- ImmReleaseContext(m_hWnd, imm_context);
- }
-
- SetWindowText(text.c_str());
- PlaceCaretAt(caret_pos);
-}
-
-bool AutocompleteEditView::IsSelectAll() {
- CHARRANGE selection;
- GetSel(selection);
- return IsSelectAllForRange(selection);
-}
-
-void AutocompleteEditView::SelectAll(bool reversed) {
- if (reversed)
- SetSelection(GetTextLength(), 0);
- else
- SetSelection(0, GetTextLength());
-}
-
-void AutocompleteEditView::RevertAll() {
- ScopedFreeze freeze(this, GetTextObjectModel());
- ClosePopup();
- model_->Revert();
- saved_selection_for_focus_change_.cpMin = -1;
- TextChanged();
-}
-
-void AutocompleteEditView::UpdatePopup() {
- ScopedFreeze freeze(this, GetTextObjectModel());
- model_->SetInputInProgress(true);
-
- if (!model_->has_focus()) {
- // When we're in the midst of losing focus, don't rerun autocomplete. This
- // can happen when losing focus causes the IME to cancel/finalize a
- // composition. We still want to note that user input is in progress, we
- // just don't want to do anything else.
- //
- // Note that in this case the ScopedFreeze above was unnecessary; however,
- // we're inside the callstack of OnKillFocus(), which has already frozen the
- // edit, so this will never result in an unnecessary UpdateWindow() call.
- return;
- }
-
- // Figure out whether the user is trying to compose something in an IME.
- bool ime_composing = false;
- HIMC context = ImmGetContext(m_hWnd);
- if (context) {
- ime_composing = !!ImmGetCompositionString(context, GCS_COMPSTR, NULL, 0);
- ImmReleaseContext(m_hWnd, context);
- }
-
- // Don't inline autocomplete when:
- // * The user is deleting text
- // * The caret/selection isn't at the end of the text
- // * The user has just pasted in something that replaced all the text
- // * The user is trying to compose something in an IME
- CHARRANGE sel;
- GetSel(sel);
- model_->StartAutocomplete((sel.cpMax < GetTextLength()) || ime_composing);
-}
-
-void AutocompleteEditView::ClosePopup() {
- popup_view_->model()->StopAutocomplete();
-}
-
-IAccessible* AutocompleteEditView::GetIAccessible() {
- if (!autocomplete_accessibility_) {
- CComObject<AutocompleteAccessibility>* accessibility = NULL;
- if (!SUCCEEDED(CComObject<AutocompleteAccessibility>::CreateInstance(
- &accessibility)) || !accessibility)
- return NULL;
-
- // Wrap the created object in a smart pointer so it won't leak.
- CComPtr<IAccessible> accessibility_comptr(accessibility);
- if (!SUCCEEDED(accessibility->Initialize(this)))
- return NULL;
-
- // Copy to the class smart pointer, and notify that an instance of
- // IAccessible was allocated for m_hWnd.
- autocomplete_accessibility_ = accessibility_comptr;
- NotifyWinEvent(EVENT_OBJECT_CREATE, m_hWnd, OBJID_CLIENT, CHILDID_SELF);
- }
- // Detach to leave ref counting to the caller.
- return autocomplete_accessibility_.Detach();
-}
-
-void AutocompleteEditView::SetDropHighlightPosition(int position) {
- if (drop_highlight_position_ != position) {
- RepaintDropHighlight(drop_highlight_position_);
- drop_highlight_position_ = position;
- RepaintDropHighlight(drop_highlight_position_);
- }
-}
-
-void AutocompleteEditView::MoveSelectedText(int new_position) {
- const std::wstring selected_text(GetSelectedText());
- CHARRANGE sel;
- GetSel(sel);
- DCHECK((sel.cpMax != sel.cpMin) && (new_position >= 0) &&
- (new_position <= GetTextLength()));
-
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
-
- // Nuke the selected text.
- ReplaceSel(L"", TRUE);
-
- // And insert it into the new location.
- if (new_position >= sel.cpMin)
- new_position -= (sel.cpMax - sel.cpMin);
- PlaceCaretAt(new_position);
- ReplaceSel(selected_text.c_str(), TRUE);
-
- OnAfterPossibleChange();
-}
-
-void AutocompleteEditView::InsertText(int position, const std::wstring& text) {
- DCHECK((position >= 0) && (position <= GetTextLength()));
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
- SetSelection(position, position);
- ReplaceSel(text.c_str());
- OnAfterPossibleChange();
-}
-
-void AutocompleteEditView::OnTemporaryTextMaybeChanged(
- const std::wstring& display_text,
- bool save_original_selection) {
- if (save_original_selection)
- GetSelection(original_selection_);
-
- // Set new text and cursor position. Sometimes this does extra work (e.g.
- // when the new text and the old text are identical), but it's only called
- // when the user manually changes the selected line in the popup, so that's
- // not really a problem. Also, even when the text hasn't changed we'd want to
- // update the caret, because if the user had the cursor in the middle of the
- // text and then arrowed to another entry with the same text, we'd still want
- // to move the caret.
- ScopedFreeze freeze(this, GetTextObjectModel());
- SetWindowTextAndCaretPos(display_text, display_text.length());
- TextChanged();
-}
-
-bool AutocompleteEditView::OnInlineAutocompleteTextMaybeChanged(
- const std::wstring& display_text,
- size_t user_text_length) {
- // Update the text and selection. Because this can be called repeatedly while
- // typing, we've careful not to freeze the edit unless we really need to.
- // Also, unlike in the temporary text case above, here we don't want to update
- // the caret/selection unless we have to, since this might make the user's
- // caret position change without warning during typing.
- if (display_text == GetText())
- return false;
-
- ScopedFreeze freeze(this, GetTextObjectModel());
- // NOTE: We don't need the IME composition hack in SetWindowTextAndCaretPos()
- // here, because UpdatePopup() disables inline autocomplete when a
- // composition is in progress, thus preventing us from reaching this code.
- SetWindowText(display_text.c_str());
- // Set a reversed selection to keep the caret in the same position, which
- // avoids scrolling the user's text.
- SetSelection(static_cast<LONG>(display_text.length()),
- static_cast<LONG>(user_text_length));
- TextChanged();
- return true;
-}
-
-void AutocompleteEditView::OnRevertTemporaryText() {
- SetSelectionRange(original_selection_);
- TextChanged();
-}
-
-void AutocompleteEditView::OnBeforePossibleChange() {
- // Record our state.
- text_before_change_ = GetText();
- GetSelection(sel_before_change_);
-}
-
-bool AutocompleteEditView::OnAfterPossibleChange() {
- // Prevent the user from selecting the "phantom newline" at the end of the
- // edit. If they try, we just silently move the end of the selection back to
- // the end of the real text.
- CHARRANGE new_sel;
- GetSelection(new_sel);
- const int length = GetTextLength();
- if ((new_sel.cpMin > length) || (new_sel.cpMax > length)) {
- if (new_sel.cpMin > length)
- new_sel.cpMin = length;
- if (new_sel.cpMax > length)
- new_sel.cpMax = length;
- SetSelectionRange(new_sel);
- }
- const bool selection_differs = (new_sel.cpMin != sel_before_change_.cpMin) ||
- (new_sel.cpMax != sel_before_change_.cpMax);
- const bool at_end_of_edit =
- (new_sel.cpMin == length) && (new_sel.cpMax == length);
-
- // See if the text or selection have changed since OnBeforePossibleChange().
- const std::wstring new_text(GetText());
- const bool text_differs = (new_text != text_before_change_);
-
- // When the user has deleted text, we don't allow inline autocomplete. Make
- // sure to not flag cases like selecting part of the text and then pasting
- // (or typing) the prefix of that selection. (We detect these by making
- // sure the caret, which should be after any insertion, hasn't moved
- // forward of the old selection start.)
- const bool just_deleted_text =
- (text_before_change_.length() > new_text.length()) &&
- (new_sel.cpMin <= std::min(sel_before_change_.cpMin,
- sel_before_change_.cpMax));
-
-
- const bool something_changed = model_->OnAfterPossibleChange(new_text,
- selection_differs, text_differs, just_deleted_text, at_end_of_edit);
-
- if (something_changed && text_differs)
- TextChanged();
-
- return something_changed;
-}
-
-void AutocompleteEditView::PasteAndGo(const std::wstring& text) {
- if (CanPasteAndGo(text))
- model_->PasteAndGo();
-}
-
-bool AutocompleteEditView::OverrideAccelerator(
- const views::Accelerator& accelerator) {
- // Only override <esc>.
- if ((accelerator.GetKeyCode() != VK_ESCAPE) || accelerator.IsAltDown())
- return false;
-
- ScopedFreeze freeze(this, GetTextObjectModel());
- return model_->OnEscapeKeyPressed();
-}
-
-void AutocompleteEditView::HandleExternalMsg(UINT msg,
- UINT flags,
- const CPoint& screen_point) {
- if (msg == WM_CAPTURECHANGED) {
- SendMessage(msg, 0, NULL);
- return;
- }
-
- CPoint client_point(screen_point);
- ::MapWindowPoints(NULL, m_hWnd, &client_point, 1);
- SendMessage(msg, flags, MAKELPARAM(client_point.x, client_point.y));
-}
-
-bool AutocompleteEditView::IsCommandEnabled(int id) const {
- switch (id) {
- case IDS_UNDO: return !!CanUndo();
- case IDS_CUT: return !!CanCut();
- case IDS_COPY: return !!CanCopy();
- case IDS_PASTE: return !!CanPaste();
- case IDS_PASTE_AND_GO: return CanPasteAndGo(GetClipboardText());
- case IDS_SELECT_ALL: return !!CanSelectAll();
- case IDS_EDIT_SEARCH_ENGINES:
- return command_updater_->IsCommandEnabled(IDC_EDIT_SEARCH_ENGINES);
- default: NOTREACHED(); return false;
- }
-}
-
-bool AutocompleteEditView::GetContextualLabel(int id, std::wstring* out) const {
- if ((id != IDS_PASTE_AND_GO) ||
- // No need to change the default IDS_PASTE_AND_GO label unless this is a
- // search.
- !model_->is_paste_and_search())
- return false;
-
- out->assign(l10n_util::GetString(IDS_PASTE_AND_SEARCH));
- return true;
-}
-
-void AutocompleteEditView::ExecuteCommand(int id) {
- ScopedFreeze freeze(this, GetTextObjectModel());
- if (id == IDS_PASTE_AND_GO) {
- // This case is separate from the switch() below since we don't want to wrap
- // it in OnBefore/AfterPossibleChange() calls.
- model_->PasteAndGo();
- return;
- }
-
- OnBeforePossibleChange();
- switch (id) {
- case IDS_UNDO:
- Undo();
- break;
-
- case IDS_CUT:
- Cut();
- break;
-
- case IDS_COPY:
- Copy();
- break;
-
- case IDS_PASTE:
- Paste();
- break;
-
- case IDS_SELECT_ALL:
- SelectAll(false);
- break;
-
- case IDS_EDIT_SEARCH_ENGINES:
- command_updater_->ExecuteCommand(IDC_EDIT_SEARCH_ENGINES);
- break;
-
- default:
- NOTREACHED();
- break;
- }
- OnAfterPossibleChange();
-}
-
-// static
-int CALLBACK AutocompleteEditView::WordBreakProc(LPTSTR edit_text,
- int current_pos,
- int num_bytes,
- int action) {
- // TODO(pkasting): http://b/1111308 We should let other people, like ICU and
- // GURL, do the work for us here instead of writing all this ourselves.
-
- // Sadly, even though the MSDN docs claim that the third parameter here is a
- // number of characters, they lie. It's a number of bytes.
- const int length = num_bytes / sizeof(wchar_t);
-
- // With no clear guidance from the MSDN docs on how to handle "not found" in
- // the "find the nearest xxx..." cases below, I cap the return values at
- // [0, length]. Since one of these (0) is also a valid position, the return
- // values are thus ambiguous :(
- switch (action) {
- // Find nearest character before current position that begins a word.
- case WB_LEFT:
- case WB_MOVEWORDLEFT: {
- if (current_pos < 2) {
- // Either current_pos == 0, so we have a "not found" case and return 0,
- // or current_pos == 1, and the only character before this position is
- // at 0.
- return 0;
- }
-
- // Look for a delimiter before the previous character; the previous word
- // starts immediately after. (If we looked for a delimiter before the
- // current character, we could stop on the immediate prior character,
- // which would mean we'd return current_pos -- which isn't "before the
- // current position".)
- const int prev_delim =
- WordBreakProc(edit_text, current_pos - 1, num_bytes, WB_LEFTBREAK);
-
- if ((prev_delim == 0) &&
- !WordBreakProc(edit_text, 0, num_bytes, WB_ISDELIMITER)) {
- // Got back 0, but position 0 isn't a delimiter. This was a "not
- // found" 0, so return one of our own.
- return 0;
- }
-
- return prev_delim + 1;
- }
-
- // Find nearest character after current position that begins a word.
- case WB_RIGHT:
- case WB_MOVEWORDRIGHT: {
- if (WordBreakProc(edit_text, current_pos, num_bytes, WB_ISDELIMITER)) {
- // The current character is a delimiter, so the next character starts
- // a new word. Done.
- return current_pos + 1;
- }
-
- // Look for a delimiter after the current character; the next word starts
- // immediately after.
- const int next_delim =
- WordBreakProc(edit_text, current_pos, num_bytes, WB_RIGHTBREAK);
- if (next_delim == length) {
- // Didn't find a delimiter. Return length to signal "not found".
- return length;
- }
-
- return next_delim + 1;
- }
-
- // Determine if the current character delimits words.
- case WB_ISDELIMITER:
- return !!(WordBreakProc(edit_text, current_pos, num_bytes, WB_CLASSIFY) &
- WBF_BREAKLINE);
-
- // Return the classification of the current character.
- case WB_CLASSIFY:
- if (IsWhitespace(edit_text[current_pos])) {
- // Whitespace normally breaks words, but the MSDN docs say that we must
- // not break on the CRs in a "CR, LF" or a "CR, CR, LF" sequence. Just
- // check for an arbitrarily long sequence of CRs followed by LF and
- // report "not a delimiter" for the current CR in that case.
- while ((current_pos < (length - 1)) &&
- (edit_text[current_pos] == 0x13)) {
- if (edit_text[++current_pos] == 0x10)
- return WBF_ISWHITE;
- }
- return WBF_BREAKLINE | WBF_ISWHITE;
- }
-
- // Punctuation normally breaks words, but the first two characters in
- // "://" (end of scheme) should not be breaks, so that "http://" will be
- // treated as one word.
- if (ispunct(edit_text[current_pos], std::locale()) &&
- !SchemeEnd(edit_text, current_pos, length) &&
- !SchemeEnd(edit_text, current_pos - 1, length))
- return WBF_BREAKLINE;
-
- // Normal character, no flags.
- return 0;
-
- // Finds nearest delimiter before current position.
- case WB_LEFTBREAK:
- for (int i = current_pos - 1; i >= 0; --i) {
- if (WordBreakProc(edit_text, i, num_bytes, WB_ISDELIMITER))
- return i;
- }
- return 0;
-
- // Finds nearest delimiter after current position.
- case WB_RIGHTBREAK:
- for (int i = current_pos + 1; i < length; ++i) {
- if (WordBreakProc(edit_text, i, num_bytes, WB_ISDELIMITER))
- return i;
- }
- return length;
- }
-
- NOTREACHED();
- return 0;
-}
-
-// static
-bool AutocompleteEditView::SchemeEnd(LPTSTR edit_text,
- int current_pos,
- int length) {
- return (current_pos >= 0) &&
- ((length - current_pos) > 2) &&
- (edit_text[current_pos] == ':') &&
- (edit_text[current_pos + 1] == '/') &&
- (edit_text[current_pos + 2] == '/');
-}
-
-void AutocompleteEditView::OnChar(TCHAR ch, UINT repeat_count, UINT flags) {
- // Don't let alt-enter beep. Not sure this is necessary, as the standard
- // alt-enter will hit DiscardWMSysChar() and get thrown away, and
- // ctrl-alt-enter doesn't seem to reach here for some reason? At least not on
- // my system... still, this is harmless and maybe necessary in other locales.
- if (ch == VK_RETURN && (flags & KF_ALTDOWN))
- return;
-
- // Escape is processed in OnKeyDown. Don't let any WM_CHAR messages propagate
- // as we don't want the RichEdit to do anything funky.
- if (ch == VK_ESCAPE && !(flags & KF_ALTDOWN))
- return;
-
- if (ch == VK_TAB) {
- // Don't add tabs to the input.
- return;
- }
-
- HandleKeystroke(GetCurrentMessage()->message, ch, repeat_count, flags);
-}
-
-void AutocompleteEditView::OnContextMenu(HWND window, const CPoint& point) {
- if (point.x == -1 || point.y == -1) {
- POINT p;
- GetCaretPos(&p);
- MapWindowPoints(HWND_DESKTOP, &p, 1);
- context_menu_->RunMenuAt(p.x, p.y);
- } else {
- context_menu_->RunMenuAt(point.x, point.y);
- }
-}
-
-void AutocompleteEditView::OnCopy() {
- const std::wstring text(GetSelectedText());
- if (text.empty())
- return;
-
- ScopedClipboardWriter scw(g_browser_process->clipboard_service());
- scw.WriteText(text);
-
- // Check if the user is copying the whole address bar. If they are, we
- // assume they are trying to copy a URL and write this to the clipboard as a
- // hyperlink.
- if (static_cast<int>(text.length()) < GetTextLength())
- return;
-
- // The entire control is selected. Let's see what the user typed. We can't
- // use model_->CurrentTextIsURL() or model_->GetDataForURLExport() because
- // right now the user is probably holding down control to cause the copy,
- // which will screw up our calculation of the desired_tld.
- GURL url;
- if (model_->GetURLForText(text, &url))
- scw.WriteHyperlink(text, url.spec());
-}
-
-void AutocompleteEditView::OnCut() {
- OnCopy();
-
- // This replace selection will have no effect (even on the undo stack) if the
- // current selection is empty.
- ReplaceSel(L"", true);
-}
-
-LRESULT AutocompleteEditView::OnGetObject(UINT uMsg,
- WPARAM wparam,
- LPARAM lparam) {
- // Accessibility readers will send an OBJID_CLIENT message.
- if (lparam == OBJID_CLIENT) {
- // Re-attach for internal re-usage of accessibility pointer.
- autocomplete_accessibility_.Attach(GetIAccessible());
-
- if (autocomplete_accessibility_) {
- return LresultFromObject(IID_IAccessible, wparam,
- autocomplete_accessibility_.p);
- }
- }
- return 0;
-}
-
-LRESULT AutocompleteEditView::OnImeComposition(UINT message,
- WPARAM wparam,
- LPARAM lparam) {
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
- LRESULT result = DefWindowProc(message, wparam, lparam);
- if (!OnAfterPossibleChange() && (lparam & GCS_RESULTSTR)) {
- // The result string changed, but the text in the popup didn't actually
- // change. This means the user finalized the composition. Rerun
- // autocomplete so that we can now trigger inline autocomplete if
- // applicable.
- //
- // Note that if we're in the midst of losing focus, UpdatePopup() won't
- // actually rerun autocomplete, but will just set local state correctly.
- UpdatePopup();
- }
- return result;
-}
-
-void AutocompleteEditView::OnKeyDown(TCHAR key, UINT repeat_count, UINT flags) {
- if (OnKeyDownAllModes(key, repeat_count, flags) || popup_window_mode_ ||
- OnKeyDownOnlyWritable(key, repeat_count, flags))
- return;
-
- // CRichEditCtrl changes its text on WM_KEYDOWN instead of WM_CHAR for many
- // different keys (backspace, ctrl-v, ...), so we call this in both cases.
- HandleKeystroke(GetCurrentMessage()->message, key, repeat_count, flags);
-}
-
-void AutocompleteEditView::OnKeyUp(TCHAR key, UINT repeat_count, UINT flags) {
- if (key == VK_CONTROL)
- model_->OnControlKeyChanged(false);
-
- SetMsgHandled(false);
-}
-
-void AutocompleteEditView::OnKillFocus(HWND focus_wnd) {
- if (m_hWnd == focus_wnd) {
- // Focus isn't actually leaving.
- SetMsgHandled(false);
- return;
- }
-
- // Close the popup.
- ClosePopup();
-
- // Save the user's existing selection to restore it later.
- GetSelection(saved_selection_for_focus_change_);
-
- // Tell the model to reset itself.
- model_->OnKillFocus();
-
- // Let the CRichEditCtrl do its default handling. This will complete any
- // in-progress IME composition. We must do this after setting has_focus_ to
- // false so that UpdatePopup() will know not to rerun autocomplete.
- ScopedFreeze freeze(this, GetTextObjectModel());
- DefWindowProc(WM_KILLFOCUS, reinterpret_cast<WPARAM>(focus_wnd), 0);
-
- // Hide the "Type to search" hint if necessary. We do this after calling
- // DefWindowProc() because processing the resulting IME messages may notify
- // the controller that input is in progress, which could cause the visible
- // hints to change. (I don't know if there's a real scenario where they
- // actually do change, but this is safest.)
- if (model_->show_search_hint() ||
- (model_->is_keyword_hint() && !model_->keyword().empty()))
- controller_->OnChanged();
-
- // Cancel any user selection and scroll the text back to the beginning of the
- // URL. We have to do this after calling DefWindowProc() because otherwise
- // an in-progress IME composition will be completed at the new caret position,
- // resulting in the string jumping unexpectedly to the front of the edit.
- PlaceCaretAt(0);
-}
-
-void AutocompleteEditView::OnLButtonDblClk(UINT keys, const CPoint& point) {
- // Save the double click info for later triple-click detection.
- tracking_double_click_ = true;
- double_click_point_ = point;
- double_click_time_ = GetCurrentMessage()->time;
- possible_drag_ = false;
-
- // Modifying the selection counts as accepting any inline autocompletion, so
- // track "changes" made by clicking the mouse button.
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
- DefWindowProc(WM_LBUTTONDBLCLK, keys,
- MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y));
- OnAfterPossibleChange();
-
- gaining_focus_.reset(); // See NOTE in OnMouseActivate().
-}
-
-void AutocompleteEditView::OnLButtonDown(UINT keys, const CPoint& point) {
- if (gaining_focus_.get()) {
- // This click is giving us focus, so we need to track how much the mouse
- // moves to see if it's a drag or just a click. Clicks should select all
- // the text.
- tracking_click_ = true;
- mouse_down_point_ = point;
-
- // When Chrome was already the activated app, we haven't reached
- // OnSetFocus() yet. When we get there, don't restore the saved selection,
- // since it will just screw up the user's interaction with the edit.
- saved_selection_for_focus_change_.cpMin = -1;
-
- // Crazy hack: In this particular case, the CRichEditCtrl seems to have an
- // internal flag that discards the next WM_LBUTTONDOWN without processing
- // it, so that clicks on the edit when its owning app is not activated are
- // eaten rather than processed (despite whatever the return value of
- // DefWindowProc(WM_MOUSEACTIVATE, ...) may say). This behavior is
- // confusing and we want the click to be treated normally. So, to reset the
- // CRichEditCtrl's internal flag, we pass it an extra WM_LBUTTONDOWN here
- // (as well as a matching WM_LBUTTONUP, just in case we'd be confusing some
- // kind of state tracking otherwise).
- DefWindowProc(WM_LBUTTONDOWN, keys, MAKELPARAM(point.x, point.y));
- DefWindowProc(WM_LBUTTONUP, keys, MAKELPARAM(point.x, point.y));
- }
-
- // Check for triple click, then reset tracker. Should be safe to subtract
- // double_click_time_ from the current message's time even if the timer has
- // wrapped in between.
- const bool is_triple_click = tracking_double_click_ &&
- win_util::IsDoubleClick(double_click_point_, point,
- GetCurrentMessage()->time - double_click_time_);
- tracking_double_click_ = false;
-
- if (!gaining_focus_.get() && !is_triple_click)
- OnPossibleDrag(point);
-
-
- // Modifying the selection counts as accepting any inline autocompletion, so
- // track "changes" made by clicking the mouse button.
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
- DefWindowProc(WM_LBUTTONDOWN, keys,
- MAKELPARAM(ClipXCoordToVisibleText(point.x, is_triple_click),
- point.y));
- OnAfterPossibleChange();
-
- gaining_focus_.reset();
-}
-
-void AutocompleteEditView::OnLButtonUp(UINT keys, const CPoint& point) {
- // default processing should happen first so we can see the result of the
- // selection
- ScopedFreeze freeze(this, GetTextObjectModel());
- DefWindowProc(WM_LBUTTONUP, keys,
- MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y));
-
- // When the user has clicked and released to give us focus, select all.
- if (tracking_click_ && !win_util::IsDrag(mouse_down_point_, point)) {
- // Select all in the reverse direction so as not to scroll the caret
- // into view and shift the contents jarringly.
- SelectAll(true);
- possible_drag_ = false;
- }
-
- tracking_click_ = false;
-
- UpdateDragDone(keys);
-}
-
-LRESULT AutocompleteEditView::OnMouseActivate(HWND window,
- UINT hit_test,
- UINT mouse_message) {
- // First, give other handlers a chance to handle the message to see if we are
- // actually going to activate and gain focus.
- LRESULT result = DefWindowProc(WM_MOUSEACTIVATE,
- reinterpret_cast<WPARAM>(window),
- MAKELPARAM(hit_test, mouse_message));
- // Check if we're getting focus from a left click. We have to do this here
- // rather than in OnLButtonDown() since in many scenarios OnSetFocus() will be
- // reached before OnLButtonDown(), preventing us from detecting this properly
- // there. Also in those cases, we need to already know in OnSetFocus() that
- // we should not restore the saved selection.
- if (!model_->has_focus() && (mouse_message == WM_LBUTTONDOWN) &&
- (result == MA_ACTIVATE)) {
- DCHECK(!gaining_focus_.get());
- gaining_focus_.reset(new ScopedFreeze(this, GetTextObjectModel()));
- // NOTE: Despite |mouse_message| being WM_LBUTTONDOWN here, we're not
- // guaranteed to call OnLButtonDown() later! Specifically, if this is the
- // second click of a double click, we'll reach here but later call
- // OnLButtonDblClk(). Make sure |gaining_focus_| gets reset both places, or
- // we'll have visual glitchiness and then DCHECK failures.
-
- // Don't restore saved selection, it will just screw up our interaction
- // with this edit.
- saved_selection_for_focus_change_.cpMin = -1;
- }
- return result;
-}
-
-void AutocompleteEditView::OnMouseMove(UINT keys, const CPoint& point) {
- if (possible_drag_) {
- StartDragIfNecessary(point);
- // Don't fall through to default mouse handling, otherwise a second
- // drag session may start.
- return;
- }
-
- if (tracking_click_ && !win_util::IsDrag(mouse_down_point_, point))
- return;
-
- tracking_click_ = false;
-
- // Return quickly if this can't change the selection/cursor, so we don't
- // create a ScopedFreeze (and thus do an UpdateWindow()) on every
- // WM_MOUSEMOVE.
- if (!(keys & MK_LBUTTON)) {
- DefWindowProc(WM_MOUSEMOVE, keys, MAKELPARAM(point.x, point.y));
- return;
- }
-
- // Clamp the selection to the visible text so the user can't drag to select
- // the "phantom newline". In theory we could achieve this by clipping the X
- // coordinate, but in practice the edit seems to behave nondeterministically
- // with similar sequences of clipped input coordinates fed to it. Maybe it's
- // reading the mouse cursor position directly?
- //
- // This solution has a minor visual flaw, however: if there's a visible cursor
- // at the edge of the text (only true when there's no selection), dragging the
- // mouse around outside that edge repaints the cursor on every WM_MOUSEMOVE
- // instead of allowing it to blink normally. To fix this, we special-case
- // this exact case and discard the WM_MOUSEMOVE messages instead of passing
- // them along.
- //
- // But even this solution has a flaw! (Argh.) In the case where the user has
- // a selection that starts at the edge of the edit, and proceeds to the middle
- // of the edit, and the user is dragging back past the start edge to remove
- // the selection, there's a redraw problem where the change between having the
- // last few bits of text still selected and having nothing selected can be
- // slow to repaint (which feels noticeably strange). This occurs if you only
- // let the edit receive a single WM_MOUSEMOVE past the edge of the text. I
- // think on each WM_MOUSEMOVE the edit is repainting its previous state, then
- // updating its internal variables to the new state but not repainting. To
- // fix this, we allow one more WM_MOUSEMOVE through after the selection has
- // supposedly been shrunk to nothing; this makes the edit redraw the selection
- // quickly so it feels smooth.
- CHARRANGE selection;
- GetSel(selection);
- const bool possibly_can_discard_mousemove =
- (selection.cpMin == selection.cpMax) &&
- (((selection.cpMin == 0) &&
- (ClipXCoordToVisibleText(point.x, false) > point.x)) ||
- ((selection.cpMin == GetTextLength()) &&
- (ClipXCoordToVisibleText(point.x, false) < point.x)));
- if (!can_discard_mousemove_ || !possibly_can_discard_mousemove) {
- can_discard_mousemove_ = possibly_can_discard_mousemove;
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
- // Force the Y coordinate to the center of the clip rect. The edit
- // behaves strangely when the cursor is dragged vertically: if the cursor
- // is in the middle of the text, drags inside the clip rect do nothing,
- // and drags outside the clip rect act as if the cursor jumped to the
- // left edge of the text. When the cursor is at the right edge, drags of
- // just a few pixels vertically end up selecting the "phantom newline"...
- // sometimes.
- RECT r;
- GetRect(&r);
- DefWindowProc(WM_MOUSEMOVE, keys,
- MAKELPARAM(point.x, (r.bottom - r.top) / 2));
- OnAfterPossibleChange();
- }
-}
-
-void AutocompleteEditView::OnPaint(HDC bogus_hdc) {
- // We need to paint over the top of the edit. If we simply let the edit do
- // its default painting, then do ours into the window DC, the screen is
- // updated in between and we can get flicker. To avoid this, we force the
- // edit to paint into a memory DC, which we also paint onto, then blit the
- // whole thing to the screen.
-
- // Don't paint if not necessary.
- CRect paint_clip_rect;
- if (!GetUpdateRect(&paint_clip_rect, true))
- return;
-
- // Begin painting, and create a memory DC for the edit to paint into.
- CPaintDC paint_dc(m_hWnd);
- CDC memory_dc(CreateCompatibleDC(paint_dc));
- CRect rect;
- GetClientRect(&rect);
- // NOTE: This next call uses |paint_dc| instead of |memory_dc| because
- // |memory_dc| contains a 1x1 monochrome bitmap by default, which will cause
- // |memory_bitmap| to be monochrome, which isn't what we want.
- CBitmap memory_bitmap(CreateCompatibleBitmap(paint_dc, rect.Width(),
- rect.Height()));
- HBITMAP old_bitmap = memory_dc.SelectBitmap(memory_bitmap);
-
- // Tell our intercept functions to supply our memory DC to the edit when it
- // tries to call BeginPaint().
- //
- // The sane way to do this would be to use WM_PRINTCLIENT to ask the edit to
- // paint into our desired DC. Unfortunately, the Rich Edit 3.0 that ships
- // with Windows 2000/XP/Vista doesn't handle WM_PRINTCLIENT correctly; it
- // treats it just like WM_PAINT and calls BeginPaint(), ignoring our provided
- // DC. The Rich Edit 6.0 that ships with Office 2007 handles this better, but
- // has other issues, and we can't redistribute that DLL anyway. So instead,
- // we use this scary hack.
- //
- // NOTE: It's possible to get nested paint calls (!) (try setting the
- // permanent URL to something longer than the edit width, then selecting the
- // contents of the edit, typing a character, and hitting <esc>), so we can't
- // DCHECK(!edit_hwnd_) here. Instead, just save off the old HWND, which most
- // of the time will be NULL.
- HWND old_edit_hwnd = edit_hwnd;
- edit_hwnd = m_hWnd;
- paint_struct = paint_dc.m_ps;
- paint_struct.hdc = memory_dc;
- DefWindowProc(WM_PAINT, reinterpret_cast<WPARAM>(bogus_hdc), 0);
-
- // Make the selection look better.
- EraseTopOfSelection(&memory_dc, rect, paint_clip_rect);
-
- // Draw a slash through the scheme if this is insecure.
- if (insecure_scheme_component_.is_nonempty())
- DrawSlashForInsecureScheme(memory_dc, rect, paint_clip_rect);
-
- // Draw the drop highlight.
- if (drop_highlight_position_ != -1)
- DrawDropHighlight(memory_dc, rect, paint_clip_rect);
-
- // Blit the memory DC to the actual paint DC and clean up.
- BitBlt(paint_dc, rect.left, rect.top, rect.Width(), rect.Height(), memory_dc,
- rect.left, rect.top, SRCCOPY);
- memory_dc.SelectBitmap(old_bitmap);
- edit_hwnd = old_edit_hwnd;
-}
-
-void AutocompleteEditView::OnNonLButtonDown(UINT keys, const CPoint& point) {
- // Interestingly, the edit doesn't seem to cancel triple clicking when the
- // x-buttons (which usually means "thumb buttons") are pressed, so we only
- // call this for M and R down.
- tracking_double_click_ = false;
-
- OnPossibleDrag(point);
-
- SetMsgHandled(false);
-}
-
-void AutocompleteEditView::OnNonLButtonUp(UINT keys, const CPoint& point) {
- UpdateDragDone(keys);
-
- // Let default handler have a crack at this.
- SetMsgHandled(false);
-}
-
-void AutocompleteEditView::OnPaste() {
- // Replace the selection if we have something to paste.
- const std::wstring text(GetClipboardText());
- if (!text.empty()) {
- // If this paste will be replacing all the text, record that, so we can do
- // different behaviors in such a case.
- if (IsSelectAll())
- model_->on_paste_replacing_all();
- ReplaceSel(text.c_str(), true);
- }
-}
-
-void AutocompleteEditView::OnSetFocus(HWND focus_wnd) {
- model_->OnSetFocus(GetKeyState(VK_CONTROL) < 0);
-
- // Notify controller if it needs to show hint UI of some kind.
- ScopedFreeze freeze(this, GetTextObjectModel());
- if (model_->show_search_hint() ||
- (model_->is_keyword_hint() && !model_->keyword().empty()))
- controller_->OnChanged();
-
- // Restore saved selection if available.
- if (saved_selection_for_focus_change_.cpMin != -1) {
- SetSelectionRange(saved_selection_for_focus_change_);
- saved_selection_for_focus_change_.cpMin = -1;
- }
-
- SetMsgHandled(false);
-}
-
-void AutocompleteEditView::OnSysChar(TCHAR ch, UINT repeat_count, UINT flags) {
- // Nearly all alt-<xxx> combos result in beeping rather than doing something
- // useful, so we discard most. Exceptions:
- // * ctrl-alt-<xxx>, which is sometimes important, generates WM_CHAR instead
- // of WM_SYSCHAR, so it doesn't need to be handled here.
- // * alt-space gets translated by the default WM_SYSCHAR handler to a
- // WM_SYSCOMMAND to open the application context menu, so we need to allow
- // it through.
- if (ch == VK_SPACE)
- SetMsgHandled(false);
-}
-
-void AutocompleteEditView::HandleKeystroke(UINT message,
- TCHAR key,
- UINT repeat_count,
- UINT flags) {
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
- DefWindowProc(message, key, MAKELPARAM(repeat_count, flags));
- OnAfterPossibleChange();
-}
-
-bool AutocompleteEditView::OnKeyDownOnlyWritable(TCHAR key,
- UINT repeat_count,
- UINT flags) {
- // NOTE: Annoyingly, ctrl-alt-<key> generates WM_KEYDOWN rather than
- // WM_SYSKEYDOWN, so we need to check (flags & KF_ALTDOWN) in various places
- // in this function even with a WM_SYSKEYDOWN handler.
-
- int count = repeat_count;
- switch (key) {
- case VK_RETURN:
- model_->AcceptInput((flags & KF_ALTDOWN) ?
- NEW_FOREGROUND_TAB : CURRENT_TAB, false);
- return true;
-
- case VK_UP:
- count = -count;
- // FALL THROUGH
- case VK_DOWN:
- if (flags & KF_ALTDOWN)
- return false;
-
- model_->OnUpOrDownKeyPressed(count);
- return true;
-
- // Hijacking Editing Commands
- //
- // We hijack the keyboard short-cuts for Cut, Copy, and Paste here so that
- // they go through our clipboard routines. This allows us to be smarter
- // about how we interact with the clipboard and avoid bugs in the
- // CRichEditCtrl. If we didn't hijack here, the edit control would handle
- // these internally with sending the WM_CUT, WM_COPY, or WM_PASTE messages.
- //
- // Cut: Shift-Delete and Ctrl-x are treated as cut. Ctrl-Shift-Delete and
- // Ctrl-Shift-x are not treated as cut even though the underlying
- // CRichTextEdit would treat them as such.
- // Copy: Ctrl-c is treated as copy. Shift-Ctrl-c is not. (This is handled
- // in OnKeyDownAllModes().)
- // Paste: Shift-Insert and Ctrl-v are tread as paste. Ctrl-Shift-Insert and
- // Ctrl-Shift-v are not.
- //
- // This behavior matches most, but not all Windows programs, and largely
- // conforms to what users expect.
-
- case VK_DELETE:
- if ((flags & KF_ALTDOWN) || GetKeyState(VK_SHIFT) >= 0)
- return false;
- if (GetKeyState(VK_CONTROL) >= 0) {
- // Cut text if possible.
- CHARRANGE selection;
- GetSel(selection);
- if (selection.cpMin != selection.cpMax) {
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
- Cut();
- OnAfterPossibleChange();
- } else if (popup_view_->model()->IsOpen()) {
- // This is a bit overloaded, but we hijack Shift-Delete in this
- // case to delete the current item from the pop-up. We prefer cutting
- // to this when possible since that's the behavior more people expect
- // from Shift-Delete, and it's more commonly useful.
- popup_view_->model()->TryDeletingCurrentItem();
- }
- }
- return true;
-
- case 'X':
- if ((flags & KF_ALTDOWN) || (GetKeyState(VK_CONTROL) >= 0))
- return false;
- if (GetKeyState(VK_SHIFT) >= 0) {
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
- Cut();
- OnAfterPossibleChange();
- }
- return true;
-
- case VK_INSERT:
- case 'V':
- if ((flags & KF_ALTDOWN) ||
- (GetKeyState((key == 'V') ? VK_CONTROL : VK_SHIFT) >= 0))
- return false;
- if (GetKeyState((key == 'V') ? VK_SHIFT : VK_CONTROL) >= 0) {
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
- Paste();
- OnAfterPossibleChange();
- }
- return true;
-
- case VK_BACK: {
- if ((flags & KF_ALTDOWN) || model_->is_keyword_hint() ||
- model_->keyword().empty())
- return false;
-
- {
- CHARRANGE selection;
- GetSel(selection);
- if ((selection.cpMin != selection.cpMax) || (selection.cpMin != 0))
- return false;
- }
-
- // We're showing a keyword and the user pressed backspace at the beginning
- // of the text. Delete the selected keyword.
- ScopedFreeze freeze(this, GetTextObjectModel());
- model_->ClearKeyword(GetText());
- return true;
- }
-
- case VK_TAB: {
- if (model_->is_keyword_hint() && !model_->keyword().empty()) {
- // Accept the keyword.
- ScopedFreeze freeze(this, GetTextObjectModel());
- model_->AcceptKeyword();
- }
- return true;
- }
-
- case 0xbb: // Ctrl-'='. Triggers subscripting (even in plain text mode).
- return true;
-
- default:
- return false;
- }
-}
-
-bool AutocompleteEditView::OnKeyDownAllModes(TCHAR key,
- UINT repeat_count,
- UINT flags) {
- // See KF_ALTDOWN comment atop OnKeyDownOnlyWriteable().
-
- switch (key) {
- case VK_CONTROL:
- model_->OnControlKeyChanged(true);
- return false;
-
- case 'C':
- // See more detailed comments in OnKeyDownOnlyWriteable().
- if ((flags & KF_ALTDOWN) || (GetKeyState(VK_CONTROL) >= 0))
- return false;
- if (GetKeyState(VK_SHIFT) >= 0)
- Copy();
- return true;
-
- default:
- return false;
- }
-}
-
-void AutocompleteEditView::GetSelection(CHARRANGE& sel) const {
- GetSel(sel);
-
- // See if we need to reverse the direction of the selection.
- ITextDocument* const text_object_model = GetTextObjectModel();
- if (!text_object_model)
- return;
- CComPtr<ITextSelection> selection;
- const HRESULT hr = text_object_model->GetSelection(&selection);
- DCHECK(hr == S_OK);
- long flags;
- selection->GetFlags(&flags);
- if (flags & tomSelStartActive)
- std::swap(sel.cpMin, sel.cpMax);
-}
-
-std::wstring AutocompleteEditView::GetSelectedText() const {
- // Figure out the length of the selection.
- CHARRANGE sel;
- GetSel(sel);
-
- // Grab the selected text.
- std::wstring str;
- GetSelText(WriteInto(&str, sel.cpMax - sel.cpMin + 1));
- return str;
-}
-
-void AutocompleteEditView::SetSelection(LONG start, LONG end) {
- SetSel(start, end);
-
- if (start <= end)
- return;
-
- // We need to reverse the direction of the selection.
- ITextDocument* const text_object_model = GetTextObjectModel();
- if (!text_object_model)
- return;
- CComPtr<ITextSelection> selection;
- const HRESULT hr = text_object_model->GetSelection(&selection);
- DCHECK(hr == S_OK);
- selection->SetFlags(tomSelStartActive);
-}
-
-void AutocompleteEditView::PlaceCaretAt(std::wstring::size_type pos) {
- SetSelection(static_cast<LONG>(pos), static_cast<LONG>(pos));
-}
-
-bool AutocompleteEditView::IsSelectAllForRange(const CHARRANGE& sel) const {
- const int text_length = GetTextLength();
- return ((sel.cpMin == 0) && (sel.cpMax >= text_length)) ||
- ((sel.cpMax == 0) && (sel.cpMin >= text_length));
-}
-
-LONG AutocompleteEditView::ClipXCoordToVisibleText(LONG x,
- bool is_triple_click) const {
- // Clip the X coordinate to the left edge of the text. Careful:
- // PosFromChar(0) may return a negative X coordinate if the beginning of the
- // text has scrolled off the edit, so don't go past the clip rect's edge.
- RECT r;
- GetRect(&r);
- const int left_bound = std::max(r.left, PosFromChar(0).x);
- if (x < left_bound)
- return left_bound;
-
- // See if we need to clip to the right edge of the text.
- const int length = GetTextLength();
- // Asking for the coordinate of any character past the end of the text gets
- // the pixel just to the right of the last character.
- const int right_bound = std::min(r.right, PosFromChar(length).x);
- if ((length == 0) || (x < right_bound))
- return x;
-
- // For trailing characters that are 2 pixels wide of less (like "l" in some
- // fonts), we have a problem:
- // * Clicks on any pixel within the character will place the cursor before
- // the character.
- // * Clicks on the pixel just after the character will not allow triple-
- // click to work properly (true for any last character width).
- // So, we move to the last pixel of the character when this is a
- // triple-click, and moving to one past the last pixel in all other
- // scenarios. This way, all clicks that can move the cursor will place it at
- // the end of the text, but triple-click will still work.
- return is_triple_click ? (right_bound - 1) : right_bound;
-}
-
-void AutocompleteEditView::EmphasizeURLComponents() {
- ITextDocument* const text_object_model = GetTextObjectModel();
- ScopedFreeze freeze(this, text_object_model);
- ScopedSuspendUndo suspend_undo(text_object_model);
-
- // Save the selection.
- CHARRANGE saved_sel;
- GetSelection(saved_sel);
-
- // See whether the contents are a URL with a non-empty host portion, which we
- // should emphasize. To check for a URL, rather than using the type returned
- // by Parse(), ask the model, which will check the desired page transition for
- // this input. This can tell us whether an UNKNOWN input string is going to
- // be treated as a search or a navigation, and is the same method the Paste
- // And Go system uses.
- url_parse::Parsed parts;
- AutocompleteInput::Parse(GetText(), model_->GetDesiredTLD(), &parts, NULL);
- const bool emphasize = model_->CurrentTextIsURL() && (parts.host.len > 0);
-
- // Set the baseline emphasis.
- CHARFORMAT cf = {0};
- cf.dwMask = CFM_COLOR;
- cf.dwEffects = 0;
- cf.crTextColor = GetSysColor(emphasize ? COLOR_GRAYTEXT : COLOR_WINDOWTEXT);
- SelectAll(false);
- SetSelectionCharFormat(cf);
-
- if (emphasize) {
- // We've found a host name, give it more emphasis.
- cf.crTextColor = GetSysColor(COLOR_WINDOWTEXT);
- SetSelection(parts.host.begin, parts.host.end());
- SetSelectionCharFormat(cf);
- }
-
- // Emphasize the scheme for security UI display purposes (if necessary).
- insecure_scheme_component_.reset();
- if (!model_->user_input_in_progress() && parts.scheme.is_nonempty() &&
- (scheme_security_level_ != ToolbarModel::NORMAL)) {
- if (scheme_security_level_ == ToolbarModel::SECURE) {
- cf.crTextColor = kSecureSchemeColor;
- } else {
- insecure_scheme_component_.begin = parts.scheme.begin;
- insecure_scheme_component_.len = parts.scheme.len;
- cf.crTextColor = kInsecureSchemeColor;
- }
- SetSelection(parts.scheme.begin, parts.scheme.end());
- SetSelectionCharFormat(cf);
- }
-
- // Restore the selection.
- SetSelectionRange(saved_sel);
-}
-
-void AutocompleteEditView::EraseTopOfSelection(CDC* dc,
- const CRect& client_rect,
- const CRect& paint_clip_rect) {
- // Find the area we care about painting. We could calculate the rect
- // containing just the selected portion, but there's no harm in simply erasing
- // the whole top of the client area, and at least once I saw us manage to
- // select the "phantom newline" briefly, which looks very weird if not clipped
- // off at the same height.
- CRect erase_rect(client_rect.left, client_rect.top, client_rect.right,
- client_rect.top + font_y_adjustment_);
- erase_rect.IntersectRect(erase_rect, paint_clip_rect);
-
- // Erase to the background color.
- if (!erase_rect.IsRectNull())
- dc->FillSolidRect(&erase_rect, background_color_);
-}
-
-void AutocompleteEditView::DrawSlashForInsecureScheme(
- HDC hdc,
- const CRect& client_rect,
- const CRect& paint_clip_rect) {
- DCHECK(insecure_scheme_component_.is_nonempty());
-
- // Calculate the rect, in window coordinates, containing the portion of the
- // scheme where we'll be drawing the slash. Vertically, we draw across one
- // x-height of text, plus an additional 3 stroke diameters (the stroke width
- // plus a half-stroke width of space between the stroke and the text, both
- // above and below the text).
- const int font_top = client_rect.top + font_y_adjustment_;
- const SkScalar kStrokeWidthPixels = SkIntToScalar(2);
- const int kAdditionalSpaceOutsideFont =
- static_cast<int>(ceil(kStrokeWidthPixels * 1.5f));
- const CRect scheme_rect(PosFromChar(insecure_scheme_component_.begin).x,
- font_top + font_ascent_ - font_x_height_ -
- kAdditionalSpaceOutsideFont,
- PosFromChar(insecure_scheme_component_.end()).x,
- font_top + font_ascent_ +
- kAdditionalSpaceOutsideFont);
-
- // Clip to the portion we care about and translate to canvas coordinates
- // (see the canvas creation below) for use later.
- CRect canvas_clip_rect, canvas_paint_clip_rect;
- canvas_clip_rect.IntersectRect(scheme_rect, client_rect);
- canvas_paint_clip_rect.IntersectRect(canvas_clip_rect, paint_clip_rect);
- if (canvas_paint_clip_rect.IsRectNull())
- return; // We don't need to paint any of this region, so just bail early.
- canvas_clip_rect.OffsetRect(-scheme_rect.left, -scheme_rect.top);
- canvas_paint_clip_rect.OffsetRect(-scheme_rect.left, -scheme_rect.top);
-
- // Create a paint context for drawing the antialiased stroke.
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setStrokeWidth(kStrokeWidthPixels);
- paint.setStrokeCap(SkPaint::kRound_Cap);
-
- // Create a canvas as large as |scheme_rect| to do our drawing, and initialize
- // it to fully transparent so any antialiasing will look nice when painted
- // atop the edit.
- ChromeCanvas canvas(scheme_rect.Width(), scheme_rect.Height(), false);
- // TODO (jcampan): This const_cast should not be necessary once the SKIA
- // API has been changed to return a non-const bitmap.
- (const_cast<SkBitmap&>(canvas.getDevice()->accessBitmap(true))).
- eraseARGB(0, 0, 0, 0);
-
- // Calculate the start and end of the stroke, which are just the lower left
- // and upper right corners of the canvas, inset by the radius of the endcap
- // so we don't clip the endcap off.
- const SkScalar kEndCapRadiusPixels = kStrokeWidthPixels / SkIntToScalar(2);
- const SkPoint start_point = {
- kEndCapRadiusPixels,
- SkIntToScalar(scheme_rect.Height()) - kEndCapRadiusPixels };
- const SkPoint end_point = {
- SkIntToScalar(scheme_rect.Width()) - kEndCapRadiusPixels,
- kEndCapRadiusPixels };
-
- // Calculate the selection rectangle in canvas coordinates, which we'll use
- // to clip the stroke so we can draw the unselected and selected portions.
- CHARRANGE sel;
- GetSel(sel);
- const SkRect selection_rect = {
- SkIntToScalar(PosFromChar(sel.cpMin).x - scheme_rect.left),
- SkIntToScalar(0),
- SkIntToScalar(PosFromChar(sel.cpMax).x - scheme_rect.left),
- SkIntToScalar(scheme_rect.Height()) };
-
- // Draw the unselected portion of the stroke.
- canvas.save();
- if (selection_rect.isEmpty() ||
- canvas.clipRect(selection_rect, SkRegion::kDifference_Op)) {
- paint.setColor(kSchemeStrikeoutColor);
- canvas.drawLine(start_point.fX, start_point.fY,
- end_point.fX, end_point.fY, paint);
- }
- canvas.restore();
-
- // Draw the selected portion of the stroke.
- if (!selection_rect.isEmpty() && canvas.clipRect(selection_rect)) {
- paint.setColor(kSchemeSelectedStrikeoutColor);
- canvas.drawLine(start_point.fX, start_point.fY,
- end_point.fX, end_point.fY, paint);
- }
-
- // Now copy what we drew to the target HDC.
- canvas.getTopPlatformDevice().drawToHDC(hdc,
- scheme_rect.left + canvas_paint_clip_rect.left - canvas_clip_rect.left,
- std::max(scheme_rect.top, client_rect.top) + canvas_paint_clip_rect.top -
- canvas_clip_rect.top, &canvas_paint_clip_rect);
-}
-
-void AutocompleteEditView::DrawDropHighlight(HDC hdc,
- const CRect& client_rect,
- const CRect& paint_clip_rect) {
- DCHECK(drop_highlight_position_ != -1);
-
- const int highlight_y = client_rect.top + font_y_adjustment_;
- const int highlight_x = PosFromChar(drop_highlight_position_).x - 1;
- const CRect highlight_rect(highlight_x,
- highlight_y,
- highlight_x + 1,
- highlight_y + font_ascent_ + font_descent_);
-
- // Clip the highlight to the region being painted.
- CRect clip_rect;
- clip_rect.IntersectRect(highlight_rect, paint_clip_rect);
- if (clip_rect.IsRectNull())
- return;
-
- HGDIOBJ last_pen = SelectObject(hdc, CreatePen(PS_SOLID, 1, RGB(0, 0, 0)));
- MoveToEx(hdc, clip_rect.left, clip_rect.top, NULL);
- LineTo(hdc, clip_rect.left, clip_rect.bottom);
- DeleteObject(SelectObject(hdc, last_pen));
-}
-
-void AutocompleteEditView::TextChanged() {
- ScopedFreeze freeze(this, GetTextObjectModel());
- EmphasizeURLComponents();
- controller_->OnChanged();
-}
-
-std::wstring AutocompleteEditView::GetClipboardText() const {
- // Try text format.
- ClipboardService* clipboard = g_browser_process->clipboard_service();
- if (clipboard->IsFormatAvailable(CF_UNICODETEXT)) {
- std::wstring text;
- clipboard->ReadText(&text);
-
- // Note: Unlike in the find popup and textfield view, here we completely
- // remove whitespace strings containing newlines. We assume users are
- // most likely pasting in URLs that may have been split into multiple
- // lines in terminals, email programs, etc., and so linebreaks indicate
- // completely bogus whitespace that would just cause the input to be
- // invalid.
- return CollapseWhitespace(text, true);
- }
-
- // Try bookmark format.
- //
- // It is tempting to try bookmark format first, but the URL we get out of a
- // bookmark has been cannonicalized via GURL. This means if a user copies
- // and pastes from the URL bar to itself, the text will get fixed up and
- // cannonicalized, which is not what the user expects. By pasting in this
- // order, we are sure to paste what the user copied.
- if (clipboard->IsFormatAvailable(Clipboard::GetUrlWFormatType())) {
- std::string url_str;
- clipboard->ReadBookmark(NULL, &url_str);
- // pass resulting url string through GURL to normalize
- GURL url(url_str);
- if (url.is_valid())
- return UTF8ToWide(url.spec());
- }
-
- return std::wstring();
-}
-
-bool AutocompleteEditView::CanPasteAndGo(const std::wstring& text) const {
- return !popup_window_mode_ && model_->CanPasteAndGo(text);
-}
-
-ITextDocument* AutocompleteEditView::GetTextObjectModel() const {
- if (!text_object_model_) {
- // This is lazily initialized, instead of being initialized in the
- // constructor, in order to avoid hurting startup performance.
- CComPtr<IRichEditOle> ole_interface;
- ole_interface.Attach(GetOleInterface());
- text_object_model_ = ole_interface;
- }
- return text_object_model_;
-}
-
-void AutocompleteEditView::StartDragIfNecessary(const CPoint& point) {
- if (initiated_drag_ || !win_util::IsDrag(mouse_down_point_, point))
- return;
-
- scoped_refptr<OSExchangeData> data = new OSExchangeData;
-
- DWORD supported_modes = DROPEFFECT_COPY;
-
- CHARRANGE sel;
- GetSelection(sel);
-
- // We're about to start a drag session, but the edit is expecting a mouse up
- // that it uses to reset internal state. If we don't send a mouse up now,
- // when the mouse moves back into the edit the edit will reset the selection.
- // So, we send the event now which resets the selection. We then restore the
- // selection and start the drag. We always send lbuttonup as otherwise we
- // might trigger a context menu (right up). This seems scary, but doesn't
- // seem to cause problems.
- {
- ScopedFreeze freeze(this, GetTextObjectModel());
- DefWindowProc(WM_LBUTTONUP, 0,
- MAKELPARAM(mouse_down_point_.x, mouse_down_point_.y));
- SetSelectionRange(sel);
- }
-
- const std::wstring start_text(GetText());
- if (IsSelectAllForRange(sel)) {
- // All the text is selected, export as URL.
- GURL url;
- std::wstring title;
- SkBitmap favicon;
- model_->GetDataForURLExport(&url, &title, &favicon);
- drag_utils::SetURLAndDragImage(url, title, favicon, data.get());
- data->SetURL(url, title);
- supported_modes |= DROPEFFECT_LINK;
- UserMetrics::RecordAction(L"Omnibox_DragURL", model_->profile());
- } else {
- supported_modes |= DROPEFFECT_MOVE;
- UserMetrics::RecordAction(L"Omnibox_DragString", model_->profile());
- }
-
- data->SetString(GetSelectedText());
-
- scoped_refptr<BaseDragSource> drag_source(new BaseDragSource);
- DWORD dropped_mode;
- in_drag_ = true;
- if (DoDragDrop(data, drag_source, supported_modes, &dropped_mode) ==
- DRAGDROP_S_DROP) {
- if ((dropped_mode == DROPEFFECT_MOVE) && (start_text == GetText())) {
- ScopedFreeze freeze(this, GetTextObjectModel());
- OnBeforePossibleChange();
- SetSelectionRange(sel);
- ReplaceSel(L"", true);
- OnAfterPossibleChange();
- }
- // else case, not a move or it was a move and the drop was on us.
- // If the drop was on us, EditDropTarget took care of the move so that
- // we don't have to delete the text.
- possible_drag_ = false;
- } else {
- // Drag was canceled or failed. The mouse may still be down and
- // over us, in which case we need possible_drag_ to remain true so
- // that we don't forward mouse move events to the edit which will
- // start another drag.
- //
- // NOTE: we didn't use mouse capture during the mouse down as DoDragDrop
- // does its own capture.
- CPoint cursor_location;
- GetCursorPos(&cursor_location);
-
- CRect client_rect;
- GetClientRect(&client_rect);
-
- CPoint client_origin_on_screen(client_rect.left, client_rect.top);
- ClientToScreen(&client_origin_on_screen);
- client_rect.MoveToXY(client_origin_on_screen.x,
- client_origin_on_screen.y);
- possible_drag_ = (client_rect.PtInRect(cursor_location) &&
- ((GetKeyState(VK_LBUTTON) != 0) ||
- (GetKeyState(VK_MBUTTON) != 0) ||
- (GetKeyState(VK_RBUTTON) != 0)));
- }
-
- in_drag_ = false;
- initiated_drag_ = true;
- tracking_click_ = false;
-}
-
-void AutocompleteEditView::OnPossibleDrag(const CPoint& point) {
- if (possible_drag_)
- return;
-
- mouse_down_point_ = point;
- initiated_drag_ = false;
-
- CHARRANGE selection;
- GetSel(selection);
- if (selection.cpMin != selection.cpMax) {
- const POINT min_sel_location(PosFromChar(selection.cpMin));
- const POINT max_sel_location(PosFromChar(selection.cpMax));
- // NOTE: we don't consider the y location here as we always pass a
- // y-coordinate in the middle to the default handler which always triggers
- // a drag regardless of the y-coordinate.
- possible_drag_ = (point.x >= min_sel_location.x) &&
- (point.x < max_sel_location.x);
- }
-}
-
-void AutocompleteEditView::UpdateDragDone(UINT keys) {
- possible_drag_ = (possible_drag_ &&
- ((keys & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) != 0));
-}
-
-void AutocompleteEditView::RepaintDropHighlight(int position) {
- if ((position != -1) && (position <= GetTextLength())) {
- const POINT min_loc(PosFromChar(position));
- const RECT highlight_bounds = {min_loc.x - 1, font_y_adjustment_,
- min_loc.x + 2, font_ascent_ + font_descent_ + font_y_adjustment_};
- InvalidateRect(&highlight_bounds, false);
- }
-}
diff --git a/chrome/browser/autocomplete/autocomplete_edit.h b/chrome/browser/autocomplete/autocomplete_edit.h
index 782f85d..e4bb2af 100644
--- a/chrome/browser/autocomplete/autocomplete_edit.h
+++ b/chrome/browser/autocomplete/autocomplete_edit.h
@@ -5,34 +5,18 @@
#ifndef CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_EDIT_H_
#define CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_EDIT_H_
-#include <atlbase.h>
-#include <atlapp.h>
-#include <atlcrack.h>
-#include <atlctrls.h>
-#include <atlmisc.h>
-#include <tom.h> // For ITextDocument, a COM interface to CRichEditCtrl.
-
-#include "base/scoped_ptr.h"
#include "chrome/browser/autocomplete/autocomplete.h"
-#include "chrome/browser/toolbar_model.h"
-#include "chrome/common/gfx/chrome_font.h"
#include "chrome/common/page_transition_types.h"
-#include "chrome/views/menu.h"
+#include "googleurl/src/gurl.h"
#include "webkit/glue/window_open_disposition.h"
class AutocompletePopupModel;
-class CommandUpdater;
class Profile;
-class TabContents;
-namespace views {
-class View;
-}
+class SkBitmap;
class AutocompleteEditController;
class AutocompleteEditModel;
class AutocompleteEditView;
-class AutocompletePopupViewWin;
-struct AutocompleteEditState;
// 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.
@@ -439,485 +423,4 @@ class AutocompleteEditModel {
DISALLOW_COPY_AND_ASSIGN(AutocompleteEditModel);
};
-// Provides the implementation of an edit control with a drop-down
-// autocomplete box. The box itself is implemented in autocomplete_popup.cc
-// This file implements the edit box and management for the popup.
-class AutocompleteEditView
- : public CWindowImpl<AutocompleteEditView,
- CRichEditCtrl,
- CWinTraits<WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL |
- ES_NOHIDESEL> >,
- public CRichEditCommands<AutocompleteEditView>,
- public Menu::Delegate {
- public:
- struct State {
- State(const CHARRANGE& selection,
- const CHARRANGE& saved_selection_for_focus_change)
- : selection(selection),
- saved_selection_for_focus_change(saved_selection_for_focus_change) {
- }
-
- const CHARRANGE selection;
- const CHARRANGE saved_selection_for_focus_change;
- };
-
- DECLARE_WND_CLASS(L"Chrome_AutocompleteEditView");
-
- AutocompleteEditView(const ChromeFont& font,
- AutocompleteEditController* controller,
- ToolbarModel* toolbar_model,
- views::View* parent_view,
- HWND hwnd,
- Profile* profile,
- CommandUpdater* command_updater,
- bool popup_window_mode);
- ~AutocompleteEditView();
-
- AutocompleteEditModel* model() { return model_.get(); }
- const AutocompleteEditModel* model() const { return model_.get(); }
-
- views::View* parent_view() const { return parent_view_; }
-
- // For use when switching tabs, this saves the current state onto the tab so
- // that it can be restored during a later call to Update().
- void SaveStateToTab(TabContents* tab);
-
- // Called when any LocationBarView state changes. If
- // |tab_for_state_restoring| is non-NULL, it points to a TabContents whose
- // state we should restore.
- void Update(const TabContents* tab_for_state_restoring);
-
- // Asks the browser to load the specified URL, which is assumed to be one of
- // the popup entries, using the supplied disposition and transition type.
- // |alternate_nav_url|, if non-empty, contains the alternate navigation URL
- // for |url|. See comments on AutocompleteResult::GetAlternateNavURL().
- //
- // |selected_line| is passed to SendOpenNotification(); see comments there.
- //
- // If the URL was expanded from a keyword, |keyword| is that keyword.
- //
- // This may close the popup.
- void OpenURL(const GURL& url,
- WindowOpenDisposition disposition,
- PageTransition::Type transition,
- const GURL& alternate_nav_url,
- size_t selected_line,
- const std::wstring& keyword);
-
- // Returns the current text of the edit control, which could be the
- // "temporary" text set by the popup, the "permanent" text set by the
- // browser, or just whatever the user has currently typed.
- std::wstring GetText() const;
-
- // The user text is the text the user has manually keyed in. When present,
- // this is shown in preference to the permanent text; hitting escape will
- // revert to the permanent text.
- void SetUserText(const std::wstring& text) { SetUserText(text, text, true); }
- void SetUserText(const std::wstring& text,
- const std::wstring& display_text,
- bool update_popup);
-
- // Sets the window text and the caret position.
- void SetWindowTextAndCaretPos(const std::wstring& text, size_t caret_pos);
-
- // Returns true if all text is selected.
- bool IsSelectAll();
-
- // Selects all the text in the edit. Use this in place of SetSelAll() to
- // avoid selecting the "phantom newline" at the end of the edit.
- void SelectAll(bool reversed);
-
- // Reverts the edit and popup back to their unedited state (permanent text
- // showing, popup closed, no user input in progress).
- void RevertAll();
-
- // Updates the autocomplete popup and other state after the text has been
- // changed by the user.
- void UpdatePopup();
-
- // Closes the autocomplete popup, if it's open.
- void ClosePopup();
-
- // Exposes custom IAccessible implementation to the overall MSAA hierarchy.
- IAccessible* GetIAccessible();
-
- void SetDropHighlightPosition(int position);
- int drop_highlight_position() const { return drop_highlight_position_; }
-
- // Returns true if a drag a drop session was initiated by this edit.
- bool in_drag() const { return in_drag_; }
-
- // Moves the selected text to the specified position.
- void MoveSelectedText(int new_position);
-
- // Inserts the text at the specified position.
- void InsertText(int position, const std::wstring& text);
-
- // Called when the temporary text in the model may have changed.
- // |display_text| is the new text to show; |save_original_selection| is true
- // when there wasn't previously a temporary text and thus we need to save off
- // the user's existing selection.
- void OnTemporaryTextMaybeChanged(const std::wstring& display_text,
- bool save_original_selection);
-
- // Called when the inline autocomplete text in the model may have changed.
- // |display_text| is the new text to show; |user_text_length| is the length of
- // the user input portion of that (so, up to but not including the inline
- // autocompletion). Returns whether the display text actually changed.
- bool OnInlineAutocompleteTextMaybeChanged(const std::wstring& display_text,
- size_t user_text_length);
-
- // Called when the temporary text has been reverted by the user. This will
- // reset the user's original selection.
- void OnRevertTemporaryText();
-
- // Every piece of code that can change the edit should call these functions
- // before and after the change. These functions determine if anything
- // meaningful changed, and do any necessary updating and notification.
- void OnBeforePossibleChange();
- // OnAfterPossibleChange() returns true if there was a change that caused it
- // to call UpdatePopup().
- bool OnAfterPossibleChange();
-
- // Invokes CanPasteAndGo with the specified text, and if successful navigates
- // to the appropriate URL. The behavior of this is the same as if the user
- // typed in the specified text and pressed enter.
- void PasteAndGo(const std::wstring& text);
-
- // Called before an accelerator is processed to give us a chance to override
- // it.
- bool OverrideAccelerator(const views::Accelerator& accelerator);
-
- // Handler for external events passed in to us. The View that owns us may
- // send us events that we should treat as if they were events on us.
- void HandleExternalMsg(UINT msg, UINT flags, const CPoint& screen_point);
-
- // CWindowImpl
- BEGIN_MSG_MAP(AutocompleteEdit)
- MSG_WM_CHAR(OnChar)
- MSG_WM_CONTEXTMENU(OnContextMenu)
- MSG_WM_COPY(OnCopy)
- MSG_WM_CUT(OnCut)
- MESSAGE_HANDLER_EX(WM_GETOBJECT, OnGetObject)
- MESSAGE_HANDLER_EX(WM_IME_COMPOSITION, OnImeComposition)
- MSG_WM_KEYDOWN(OnKeyDown)
- MSG_WM_KEYUP(OnKeyUp)
- MSG_WM_KILLFOCUS(OnKillFocus)
- MSG_WM_LBUTTONDBLCLK(OnLButtonDblClk)
- MSG_WM_LBUTTONDOWN(OnLButtonDown)
- MSG_WM_LBUTTONUP(OnLButtonUp)
- MSG_WM_MBUTTONDOWN(OnNonLButtonDown)
- MSG_WM_MBUTTONUP(OnNonLButtonUp)
- MSG_WM_MOUSEACTIVATE(OnMouseActivate)
- MSG_WM_MOUSEMOVE(OnMouseMove)
- MSG_WM_PAINT(OnPaint)
- MSG_WM_PASTE(OnPaste)
- MSG_WM_RBUTTONDOWN(OnNonLButtonDown)
- MSG_WM_RBUTTONUP(OnNonLButtonUp)
- MSG_WM_SETFOCUS(OnSetFocus)
- MSG_WM_SYSCHAR(OnSysChar) // WM_SYSxxx == WM_xxx with ALT down
- MSG_WM_SYSKEYDOWN(OnKeyDown)
- MSG_WM_SYSKEYUP(OnKeyUp)
- DEFAULT_REFLECTION_HANDLER() // avoids black margin area
- END_MSG_MAP()
-
- // Menu::Delegate
- virtual bool IsCommandEnabled(int id) const;
- virtual bool GetContextualLabel(int id, std::wstring* out) const;
- virtual void ExecuteCommand(int id);
-
- private:
- // This object freezes repainting of the edit until the object is destroyed.
- // Some methods of the CRichEditCtrl draw synchronously to the screen. If we
- // don't freeze, the user will see a rapid series of calls to these as
- // flickers.
- //
- // Freezing the control while it is already frozen is permitted; the control
- // will unfreeze once both freezes are released (the freezes stack).
- class ScopedFreeze {
- public:
- ScopedFreeze(AutocompleteEditView* edit, ITextDocument* text_object_model);
- ~ScopedFreeze();
-
- private:
- AutocompleteEditView* const edit_;
- ITextDocument* const text_object_model_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedFreeze);
- };
-
- // This object suspends placing any operations on the edit's undo stack until
- // the object is destroyed. If we don't do this, some of the operations we
- // perform behind the user's back will be undoable by the user, which feels
- // bizarre and confusing.
- class ScopedSuspendUndo {
- public:
- explicit ScopedSuspendUndo(ITextDocument* text_object_model);
- ~ScopedSuspendUndo();
-
- private:
- ITextDocument* const text_object_model_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedSuspendUndo);
- };
-
- // Replacement word-breaking proc for the rich edit control.
- static int CALLBACK WordBreakProc(LPTSTR edit_text,
- int current_pos,
- int num_bytes,
- int action);
-
- // Returns true if |edit_text| starting at |current_pos| is "://".
- static bool SchemeEnd(LPTSTR edit_text, int current_pos, int length);
-
- // Message handlers
- void OnChar(TCHAR ch, UINT repeat_count, UINT flags);
- void OnContextMenu(HWND window, const CPoint& point);
- void OnCopy();
- void OnCut();
- LRESULT OnGetObject(UINT uMsg, WPARAM wparam, LPARAM lparam);
- LRESULT OnImeComposition(UINT message, WPARAM wparam, LPARAM lparam);
- void OnKeyDown(TCHAR key, UINT repeat_count, UINT flags);
- void OnKeyUp(TCHAR key, UINT repeat_count, UINT flags);
- void OnKillFocus(HWND focus_wnd);
- void OnLButtonDblClk(UINT keys, const CPoint& point);
- void OnLButtonDown(UINT keys, const CPoint& point);
- void OnLButtonUp(UINT keys, const CPoint& point);
- LRESULT OnMouseActivate(HWND window, UINT hit_test, UINT mouse_message);
- void OnMouseMove(UINT keys, const CPoint& point);
- void OnNonLButtonDown(UINT keys, const CPoint& point);
- void OnNonLButtonUp(UINT keys, const CPoint& point);
- void OnPaint(HDC bogus_hdc);
- void OnPaste();
- void OnSetFocus(HWND focus_wnd);
- void OnSysChar(TCHAR ch, UINT repeat_count, UINT flags);
-
- // Helper function for OnChar() and OnKeyDown() that handles keystrokes that
- // could change the text in the edit.
- void HandleKeystroke(UINT message, TCHAR key, UINT repeat_count, UINT flags);
-
- // Helper functions for OnKeyDown() that handle accelerators applicable when
- // we're not read-only and all the time, respectively. These return true if
- // they handled the key.
- bool OnKeyDownOnlyWritable(TCHAR key, UINT repeat_count, UINT flags);
- bool OnKeyDownAllModes(TCHAR key, UINT repeat_count, UINT flags);
-
- // Like GetSel(), but returns a range where |cpMin| will be larger than
- // |cpMax| if the cursor is at the start rather than the end of the selection
- // (in other words, tracks selection direction as well as offsets).
- // Note the non-Google-style "non-const-ref" argument, which matches GetSel().
- void GetSelection(CHARRANGE& sel) const;
-
- // Returns the currently selected text of the edit control.
- std::wstring GetSelectedText() const;
-
- // Like SetSel(), but respects the selection direction implied by |start| and
- // |end|: if |end| < |start|, the effective cursor will be placed at the
- // beginning of the selection.
- void SetSelection(LONG start, LONG end);
-
- // Like SetSelection(), but takes a CHARRANGE.
- void SetSelectionRange(const CHARRANGE& sel) {
- SetSelection(sel.cpMin, sel.cpMax);
- }
-
- // Places the caret at the given position. This clears any selection.
- void PlaceCaretAt(size_t pos);
-
- // Returns true if |sel| represents a forward or backward selection of all the
- // text.
- bool IsSelectAllForRange(const CHARRANGE& sel) const;
-
- // Given an X coordinate in client coordinates, returns that coordinate
- // clipped to be within the horizontal bounds of the visible text.
- //
- // This is used in our mouse handlers to work around quirky behaviors of the
- // underlying CRichEditCtrl like not supporting triple-click when the user
- // doesn't click on the text itself.
- //
- // |is_triple_click| should be true iff this is the third click of a triple
- // click. Sadly, we need to clip slightly differently in this case.
- LONG ClipXCoordToVisibleText(LONG x, bool is_triple_click) const;
-
- // Parses the contents of the control for the scheme and the host name.
- // Highlights the scheme in green or red depending on it security level.
- // If a host name is found, it makes it visually stronger.
- void EmphasizeURLComponents();
-
- // Erases the portion of the selection in the font's y-adjustment area. For
- // some reason the edit draws the selection rect here even though it's not
- // part of the font.
- void EraseTopOfSelection(CDC* dc,
- const CRect& client_rect,
- const CRect& paint_clip_rect);
-
- // Draws a slash across the scheme if desired.
- void DrawSlashForInsecureScheme(HDC hdc,
- const CRect& client_rect,
- const CRect& paint_clip_rect);
-
- // Renders the drop highlight.
- void DrawDropHighlight(HDC hdc,
- const CRect& client_rect,
- const CRect& paint_clip_rect);
-
- // Internally invoked whenever the text changes in some way.
- void TextChanged();
-
- // Returns the current clipboard contents as a string that can be pasted in.
- // In addition to just getting CF_UNICODETEXT out, this can also extract URLs
- // from bookmarks on the clipboard.
- std::wstring GetClipboardText() const;
-
- // Determines whether the user can "paste and go", given the specified text.
- bool CanPasteAndGo(const std::wstring& text) const;
-
- // Getter for the text_object_model_, used by the ScopedXXX classes. Note
- // that the pointer returned here is only valid as long as the
- // AutocompleteEdit is still alive. Also, if the underlying call fails, this
- // may return NULL.
- ITextDocument* GetTextObjectModel() const;
-
- // Invoked during a mouse move. As necessary starts a drag and drop session.
- void StartDragIfNecessary(const CPoint& point);
-
- // Invoked during a mouse down. If the mouse location is over the selection
- // this sets possible_drag_ to true to indicate a drag should start if the
- // user moves the mouse far enough to start a drag.
- void OnPossibleDrag(const CPoint& point);
-
- // Invoked when a mouse button is released. If none of the buttons are still
- // down, this sets possible_drag_ to false.
- void UpdateDragDone(UINT keys);
-
- // Redraws the necessary region for a drop highlight at the specified
- // position. This does nothing if position is beyond the bounds of the
- // text.
- void RepaintDropHighlight(int position);
-
- scoped_ptr<AutocompleteEditModel> model_;
-
- scoped_ptr<AutocompletePopupViewWin> popup_view_;
-
- AutocompleteEditController* controller_;
-
- // The parent view for the edit, used to align the popup and for
- // accessibility.
- views::View* parent_view_;
-
- ToolbarModel* toolbar_model_;
-
- // The object that handles additional command functionality exposed on the
- // edit, such as invoking the keyword editor.
- CommandUpdater* command_updater_;
-
- // 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.
- bool popup_window_mode_;
-
- // Non-null when the edit is gaining focus from a left click. This is only
- // needed between when WM_MOUSEACTIVATE and WM_LBUTTONDOWN get processed. It
- // serves two purposes: first, by communicating to OnLButtonDown() that we're
- // gaining focus from a left click, it allows us to work even with the
- // inconsistent order in which various Windows messages get sent (see comments
- // in OnMouseActivate()). Second, by holding the edit frozen, it ensures that
- // when we process WM_SETFOCUS the edit won't first redraw itself with the
- // caret at the beginning, and then have it blink to where the mouse cursor
- // really is shortly afterward.
- scoped_ptr<ScopedFreeze> gaining_focus_;
-
- // When the user clicks to give us focus, we watch to see if they're clicking
- // or dragging. When they're clicking, we select nothing until mouseup, then
- // select all the text in the edit. During this process, tracking_click_ is
- // true and mouse_down_point_ holds the original click location. At other
- // times, tracking_click_ is false, and the contents of mouse_down_point_
- // should be ignored.
- bool tracking_click_;
- CPoint mouse_down_point_;
-
- // We need to know if the user triple-clicks, so track double click points
- // and times so we can see if subsequent clicks are actually triple clicks.
- bool tracking_double_click_;
- CPoint double_click_point_;
- DWORD double_click_time_;
-
- // Used to discard unnecessary WM_MOUSEMOVE events after the first such
- // unnecessary event. See detailed comments in OnMouseMove().
- bool can_discard_mousemove_;
-
- // Variables for tracking state before and after a possible change.
- std::wstring text_before_change_;
- CHARRANGE sel_before_change_;
-
- // Set at the same time the model's original_* members are set, and valid in
- // the same cases.
- CHARRANGE original_selection_;
-
- // Holds the user's selection across focus changes. cpMin holds -1 when
- // there is no saved selection.
- CHARRANGE saved_selection_for_focus_change_;
-
- // The context menu for the edit.
- scoped_ptr<Menu> context_menu_;
-
- // Font we're using. We keep a reference to make sure the font supplied to
- // the constructor doesn't go away before we do.
- ChromeFont font_;
-
- // Metrics about the font, which we keep so we don't need to recalculate them
- // every time we paint. |font_y_adjustment_| is the number of pixels we need
- // to shift the font vertically in order to make its baseline be at our
- // desired baseline in the edit.
- int font_ascent_;
- int font_descent_;
- int font_x_height_;
- int font_y_adjustment_;
-
- // If true, indicates the mouse is down and if the mouse is moved enough we
- // should start a drag.
- bool possible_drag_;
-
- // If true, we're in a call to DoDragDrop.
- bool in_drag_;
-
- // If true indicates we've run a drag and drop session. This is used to
- // avoid starting two drag and drop sessions if the drag is canceled while
- // the mouse is still down.
- bool initiated_drag_;
-
- // Position of the drop highlight. If this is -1, there is no drop highlight.
- int drop_highlight_position_;
-
- // Security UI-related data.
- COLORREF background_color_;
- ToolbarModel::SecurityLevel scheme_security_level_;
-
- // This interface is useful for accessing the CRichEditCtrl at a low level.
- mutable CComQIPtr<ITextDocument> text_object_model_;
-
- // This contains the scheme char start and stop indexes that should be
- // striken-out when displaying an insecure scheme.
- url_parse::Component insecure_scheme_component_;
-
- // Instance of accessibility information and handling.
- mutable CComPtr<IAccessible> autocomplete_accessibility_;
-
- DISALLOW_COPY_AND_ASSIGN(AutocompleteEditView);
-};
-
-// The AutocompleteEditState struct contains enough information about the
-// AutocompleteEditModel and AutocompleteEditView to save/restore a user's
-// typing, caret position, etc. across tab changes. We explicitly don't
-// preserve things like whether the popup was open as this might be weird.
-struct AutocompleteEditState {
- AutocompleteEditState(const AutocompleteEditModel::State model_state,
- const AutocompleteEditView::State view_state)
- : model_state(model_state),
- view_state(view_state) {
- }
-
- const AutocompleteEditModel::State model_state;
- const AutocompleteEditView::State view_state;
-};
-
#endif // CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_EDIT_H_
diff --git a/chrome/browser/autocomplete/autocomplete_edit_view.h b/chrome/browser/autocomplete/autocomplete_edit_view.h
new file mode 100644
index 0000000..93982fe
--- /dev/null
+++ b/chrome/browser/autocomplete/autocomplete_edit_view.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file defines the interface class AutocompleteEditView. Each toolkit
+// will implement the edit view differently, so that code is inheriently
+// platform specific. However, the AutocompleteEditModel needs to do some
+// communication with the view. Since the model is shared between platforms,
+// we need to define an interface that all view implementations will share.
+
+#ifndef CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_EDIT_VIEW_H_
+#define CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_EDIT_VIEW_H_
+
+#include <string>
+
+#include "chrome/common/page_transition_types.h"
+#include "webkit/glue/window_open_disposition.h"
+
+class AutocompleteEditModel;
+class GURL;
+class TabContents;
+
+class AutocompleteEditView {
+ public:
+ // Used by the automation system for getting at the model from the view.
+ virtual AutocompleteEditModel* model() = 0;
+ virtual const AutocompleteEditModel* model() const = 0;
+
+ // For use when switching tabs, this saves the current state onto the tab so
+ // that it can be restored during a later call to Update().
+ virtual void SaveStateToTab(TabContents* tab) = 0;
+
+ // Called when any LocationBarView state changes. If
+ // |tab_for_state_restoring| is non-NULL, it points to a TabContents whose
+ // state we should restore.
+ virtual void Update(const TabContents* tab_for_state_restoring) = 0;
+
+ // Asks the browser to load the specified URL, which is assumed to be one of
+ // the popup entries, using the supplied disposition and transition type.
+ // |alternate_nav_url|, if non-empty, contains the alternate navigation URL
+ // for |url|. See comments on AutocompleteResult::GetAlternateNavURL().
+ //
+ // |selected_line| is passed to SendOpenNotification(); see comments there.
+ //
+ // If the URL was expanded from a keyword, |keyword| is that keyword.
+ //
+ // This may close the popup.
+ virtual void OpenURL(const GURL& url,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition,
+ const GURL& alternate_nav_url,
+ size_t selected_line,
+ const std::wstring& keyword) = 0;
+
+ // Returns the current text of the edit control, which could be the
+ // "temporary" text set by the popup, the "permanent" text set by the
+ // browser, or just whatever the user has currently typed.
+ virtual std::wstring GetText() const = 0;
+
+ // The user text is the text the user has manually keyed in. When present,
+ // this is shown in preference to the permanent text; hitting escape will
+ // revert to the permanent text.
+ virtual void SetUserText(const std::wstring& text) = 0;
+ virtual void SetUserText(const std::wstring& text,
+ const std::wstring& display_text,
+ bool update_popup) = 0;
+
+ // Sets the window text and the caret position.
+ virtual void SetWindowTextAndCaretPos(const std::wstring& text,
+ size_t caret_pos) = 0;
+
+ // Returns true if all text is selected.
+ virtual bool IsSelectAll() = 0;
+
+ // Selects all the text in the edit. Use this in place of SetSelAll() to
+ // avoid selecting the "phantom newline" at the end of the edit.
+ virtual void SelectAll(bool reversed) = 0;
+
+ // Reverts the edit and popup back to their unedited state (permanent text
+ // showing, popup closed, no user input in progress).
+ virtual void RevertAll() = 0;
+
+ // Updates the autocomplete popup and other state after the text has been
+ // changed by the user.
+ virtual void UpdatePopup() = 0;
+
+ // Closes the autocomplete popup, if it's open.
+ virtual void ClosePopup() = 0;
+
+ // Called when the temporary text in the model may have changed.
+ // |display_text| is the new text to show; |save_original_selection| is true
+ // when there wasn't previously a temporary text and thus we need to save off
+ // the user's existing selection.
+ virtual void OnTemporaryTextMaybeChanged(const std::wstring& display_text,
+ bool save_original_selection) = 0;
+
+ // Called when the inline autocomplete text in the model may have changed.
+ // |display_text| is the new text to show; |user_text_length| is the length of
+ // the user input portion of that (so, up to but not including the inline
+ // autocompletion). Returns whether the display text actually changed.
+ virtual bool OnInlineAutocompleteTextMaybeChanged(
+ const std::wstring& display_text, size_t user_text_length) = 0;
+
+ // Called when the temporary text has been reverted by the user. This will
+ // reset the user's original selection.
+ virtual void OnRevertTemporaryText() = 0;
+
+ // Every piece of code that can change the edit should call these functions
+ // before and after the change. These functions determine if anything
+ // meaningful changed, and do any necessary updating and notification.
+ virtual void OnBeforePossibleChange() = 0;
+ // OnAfterPossibleChange() returns true if there was a change that caused it
+ // to call UpdatePopup().
+ virtual bool OnAfterPossibleChange() = 0;
+};
+
+#endif // CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_EDIT_VIEW_H_
diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_win.cc b/chrome/browser/autocomplete/autocomplete_edit_view_win.cc
new file mode 100644
index 0000000..339fc22
--- /dev/null
+++ b/chrome/browser/autocomplete/autocomplete_edit_view_win.cc
@@ -0,0 +1,2185 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/autocomplete/autocomplete_edit_view_win.h"
+
+#include <locale>
+
+#include "base/base_drag_source.h"
+#include "base/base_drop_target.h"
+#include "base/basictypes.h"
+#include "base/clipboard.h"
+#include "base/iat_patch.h"
+#include "base/lazy_instance.h"
+#include "base/ref_counted.h"
+#include "base/scoped_clipboard_writer.h"
+#include "base/string_util.h"
+#include "chrome/app/chrome_dll_resource.h"
+#include "chrome/browser/autocomplete/autocomplete_accessibility.h"
+#include "chrome/browser/autocomplete/autocomplete_popup_model.h"
+#include "chrome/browser/autocomplete/autocomplete_popup_view_win.h"
+#include "chrome/browser/autocomplete/edit_drop_target.h"
+#include "chrome/browser/autocomplete/keyword_provider.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/command_updater.h"
+#include "chrome/browser/drag_utils.h"
+#include "chrome/browser/metrics/user_metrics.h"
+#include "chrome/browser/net/url_fixer_upper.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/search_engines/template_url.h"
+#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/views/location_bar_view.h"
+#include "chrome/common/clipboard_service.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/gfx/utils.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/l10n_util_win.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/os_exchange_data.h"
+#include "chrome/common/win_util.h"
+#include "googleurl/src/url_util.h"
+#include "grit/generated_resources.h"
+#include "skia/ext/skia_utils_win.h"
+
+#pragma comment(lib, "oleacc.lib") // Needed for accessibility support.
+
+///////////////////////////////////////////////////////////////////////////////
+// AutocompleteEditModel
+
+// A single AutocompleteController used solely for making synchronous calls to
+// determine how to deal with the clipboard contents for Paste And Go
+// functionality. We avoid using the popup's controller here because we don't
+// want to interrupt in-progress queries or modify the popup state just
+// because the user right-clicked the edit. We don't need a controller for
+// every edit because this will always be accessed on the main thread, so we
+// won't have thread-safety problems.
+static AutocompleteController* paste_and_go_controller = NULL;
+static int paste_and_go_controller_refcount = 0;
+
+namespace {
+
+// EditDropTarget is the IDropTarget implementation installed on
+// AutocompleteEditViewWin. EditDropTarget prefers URL over plain text. A drop
+// of a URL replaces all the text of the edit and navigates immediately to the
+// URL. A drop of plain text from the same edit either copies or moves the
+// selected text, and a drop of plain text from a source other than the edit
+// does a paste and go.
+class EditDropTarget : public BaseDropTarget {
+ public:
+ explicit EditDropTarget(AutocompleteEditViewWin* edit);
+
+ protected:
+ virtual DWORD OnDragEnter(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect);
+ virtual DWORD OnDragOver(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect);
+ virtual void OnDragLeave(IDataObject* data_object);
+ virtual DWORD OnDrop(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect);
+
+ private:
+ // If dragging a string, the drop highlight position of the edit is reset
+ // based on the mouse position.
+ void UpdateDropHighlightPosition(const POINT& cursor_screen_position);
+
+ // Resets the visual drop indicates we install on the edit.
+ void ResetDropHighlights();
+
+ // The edit we're the drop target for.
+ AutocompleteEditViewWin* edit_;
+
+ // If true, the drag session contains a URL.
+ bool drag_has_url_;
+
+ // If true, the drag session contains a string. If drag_has_url_ is true,
+ // this is false regardless of whether the clipboard has a string.
+ bool drag_has_string_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(EditDropTarget);
+};
+
+// A helper method for determining a valid DROPEFFECT given the allowed
+// DROPEFFECTS. We prefer copy over link.
+DWORD CopyOrLinkDropEffect(DWORD effect) {
+ if (effect & DROPEFFECT_COPY)
+ return DROPEFFECT_COPY;
+ if (effect & DROPEFFECT_LINK)
+ return DROPEFFECT_LINK;
+ return DROPEFFECT_NONE;
+}
+
+EditDropTarget::EditDropTarget(AutocompleteEditViewWin* edit)
+ : BaseDropTarget(edit->m_hWnd),
+ edit_(edit),
+ drag_has_url_(false),
+ drag_has_string_(false) {
+}
+
+DWORD EditDropTarget::OnDragEnter(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect) {
+ OSExchangeData os_data(data_object);
+ drag_has_url_ = os_data.HasURL();
+ drag_has_string_ = !drag_has_url_ && os_data.HasString();
+ if (drag_has_url_) {
+ if (edit_->in_drag()) {
+ // The edit we're associated with originated the drag. No point in
+ // allowing the user to drop back on us.
+ drag_has_url_ = false;
+ }
+ // NOTE: it would be nice to visually show all the text is going to
+ // be replaced by selecting all, but this caused painting problems. In
+ // particular the flashing caret would appear outside the edit! For now
+ // we stick with no visual indicator other than that shown own the mouse
+ // cursor.
+ }
+ return OnDragOver(data_object, key_state, cursor_position, effect);
+}
+
+DWORD EditDropTarget::OnDragOver(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect) {
+ if (drag_has_url_)
+ return CopyOrLinkDropEffect(effect);
+
+ if (drag_has_string_) {
+ UpdateDropHighlightPosition(cursor_position);
+ if (edit_->drop_highlight_position() == -1 && edit_->in_drag())
+ return DROPEFFECT_NONE;
+ if (edit_->in_drag()) {
+ // The edit we're associated with originated the drag. Do the normal drag
+ // behavior.
+ DCHECK((effect & DROPEFFECT_COPY) && (effect & DROPEFFECT_MOVE));
+ return (key_state & MK_CONTROL) ? DROPEFFECT_COPY : DROPEFFECT_MOVE;
+ }
+ // Our edit didn't originate the drag, only allow link or copy.
+ return CopyOrLinkDropEffect(effect);
+ }
+
+ return DROPEFFECT_NONE;
+}
+
+void EditDropTarget::OnDragLeave(IDataObject* data_object) {
+ ResetDropHighlights();
+}
+
+DWORD EditDropTarget::OnDrop(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect) {
+ OSExchangeData os_data(data_object);
+
+ if (drag_has_url_) {
+ GURL url;
+ std::wstring title;
+ if (os_data.GetURLAndTitle(&url, &title)) {
+ edit_->SetUserText(UTF8ToWide(url.spec()));
+ edit_->model()->AcceptInput(CURRENT_TAB, true);
+ return CopyOrLinkDropEffect(effect);
+ }
+ } else if (drag_has_string_) {
+ int string_drop_position = edit_->drop_highlight_position();
+ std::wstring text;
+ if ((string_drop_position != -1 || !edit_->in_drag()) &&
+ os_data.GetString(&text)) {
+ DCHECK(string_drop_position == -1 ||
+ ((string_drop_position >= 0) &&
+ (string_drop_position <= edit_->GetTextLength())));
+ const DWORD drop_operation =
+ OnDragOver(data_object, key_state, cursor_position, effect);
+ if (edit_->in_drag()) {
+ if (drop_operation == DROPEFFECT_MOVE)
+ edit_->MoveSelectedText(string_drop_position);
+ else
+ edit_->InsertText(string_drop_position, text);
+ } else {
+ edit_->PasteAndGo(CollapseWhitespace(text, true));
+ }
+ ResetDropHighlights();
+ return drop_operation;
+ }
+ }
+
+ ResetDropHighlights();
+
+ return DROPEFFECT_NONE;
+}
+
+void EditDropTarget::UpdateDropHighlightPosition(
+ const POINT& cursor_screen_position) {
+ if (drag_has_string_) {
+ POINT client_position = cursor_screen_position;
+ ScreenToClient(edit_->m_hWnd, &client_position);
+ int drop_position = edit_->CharFromPos(client_position);
+ if (edit_->in_drag()) {
+ // Our edit originated the drag, don't allow a drop if over the selected
+ // region.
+ LONG sel_start, sel_end;
+ edit_->GetSel(sel_start, sel_end);
+ if ((sel_start != sel_end) && (drop_position >= sel_start) &&
+ (drop_position <= sel_end))
+ drop_position = -1;
+ } else {
+ // A drop from a source other than the edit replaces all the text, so
+ // we don't show the drop location. See comment in OnDragEnter as to why
+ // we don't try and select all here.
+ drop_position = -1;
+ }
+ edit_->SetDropHighlightPosition(drop_position);
+ }
+}
+
+void EditDropTarget::ResetDropHighlights() {
+ if (drag_has_string_)
+ edit_->SetDropHighlightPosition(-1);
+}
+
+// The AutocompleteEditState struct contains enough information about the
+// AutocompleteEditModel and AutocompleteEditViewWin to save/restore a user's
+// typing, caret position, etc. across tab changes. We explicitly don't
+// preserve things like whether the popup was open as this might be weird.
+struct AutocompleteEditState {
+ AutocompleteEditState(const AutocompleteEditModel::State model_state,
+ const AutocompleteEditViewWin::State view_state)
+ : model_state(model_state),
+ view_state(view_state) {
+ }
+
+ const AutocompleteEditModel::State model_state;
+ const AutocompleteEditViewWin::State view_state;
+};
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// Helper classes
+
+AutocompleteEditViewWin::ScopedFreeze::ScopedFreeze(
+ AutocompleteEditViewWin* edit,
+ ITextDocument* text_object_model)
+ : edit_(edit),
+ text_object_model_(text_object_model) {
+ // Freeze the screen.
+ if (text_object_model_) {
+ long count;
+ text_object_model_->Freeze(&count);
+ }
+}
+
+AutocompleteEditViewWin::ScopedFreeze::~ScopedFreeze() {
+ // Unfreeze the screen.
+ // NOTE: If this destructor is reached while the edit is being destroyed (for
+ // example, because we double-clicked the edit of a popup and caused it to
+ // transform to an unconstrained window), it will no longer have an HWND, and
+ // text_object_model_ may point to a destroyed object, so do nothing here.
+ if (edit_->IsWindow() && text_object_model_) {
+ long count;
+ text_object_model_->Unfreeze(&count);
+ if (count == 0) {
+ // We need to UpdateWindow() here instead of InvalidateRect() because, as
+ // far as I can tell, the edit likes to synchronously erase its background
+ // when unfreezing, thus requiring us to synchronously redraw if we don't
+ // want flicker.
+ edit_->UpdateWindow();
+ }
+ }
+}
+
+AutocompleteEditViewWin::ScopedSuspendUndo::ScopedSuspendUndo(
+ ITextDocument* text_object_model)
+ : text_object_model_(text_object_model) {
+ // Suspend Undo processing.
+ if (text_object_model_)
+ text_object_model_->Undo(tomSuspend, NULL);
+}
+
+AutocompleteEditViewWin::ScopedSuspendUndo::~ScopedSuspendUndo() {
+ // Resume Undo processing.
+ if (text_object_model_)
+ text_object_model_->Undo(tomResume, NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// AutocompleteEditViewWin
+
+// TODO (jcampan): these colors should be derived from the system colors to
+// ensure they show properly. Bug #948807.
+// Colors used to emphasize the scheme in the URL.
+
+namespace {
+
+const COLORREF kSecureSchemeColor = RGB(0, 150, 20);
+const COLORREF kInsecureSchemeColor = RGB(200, 0, 0);
+
+// Colors used to strike-out the scheme when it is insecure.
+const SkColor kSchemeStrikeoutColor = SkColorSetRGB(210, 0, 0);
+const SkColor kSchemeSelectedStrikeoutColor =
+ SkColorSetRGB(255, 255, 255);
+
+// These are used to hook the CRichEditCtrl's calls to BeginPaint() and
+// EndPaint() and provide a memory DC instead. See OnPaint().
+HWND edit_hwnd = NULL;
+PAINTSTRUCT paint_struct;
+
+HDC BeginPaintIntercept(HWND hWnd, LPPAINTSTRUCT lpPaint) {
+ if (!edit_hwnd || (hWnd != edit_hwnd))
+ return ::BeginPaint(hWnd, lpPaint);
+
+ *lpPaint = paint_struct;
+ return paint_struct.hdc;
+}
+
+BOOL EndPaintIntercept(HWND hWnd, const PAINTSTRUCT* lpPaint) {
+ return (edit_hwnd && (hWnd == edit_hwnd)) ?
+ true : ::EndPaint(hWnd, lpPaint);
+}
+
+// Returns a lazily initialized property bag accessor for saving our state in a
+// TabContents.
+PropertyAccessor<AutocompleteEditState>* GetStateAccessor() {
+ static PropertyAccessor<AutocompleteEditState> state;
+ return &state;
+}
+
+class PaintPatcher {
+ public:
+ PaintPatcher() : refcount_(0) { }
+ ~PaintPatcher() { DCHECK(refcount_ == 0); }
+
+ void RefPatch() {
+ if (refcount_ == 0) {
+ DCHECK(!begin_paint_.is_patched());
+ DCHECK(!end_paint_.is_patched());
+ begin_paint_.Patch(L"riched20.dll", "user32.dll", "BeginPaint",
+ &BeginPaintIntercept);
+ end_paint_.Patch(L"riched20.dll", "user32.dll", "EndPaint",
+ &EndPaintIntercept);
+ }
+ ++refcount_;
+ }
+
+ void DerefPatch() {
+ DCHECK(begin_paint_.is_patched());
+ DCHECK(end_paint_.is_patched());
+ --refcount_;
+ if (refcount_ == 0) {
+ begin_paint_.Unpatch();
+ end_paint_.Unpatch();
+ }
+ }
+
+ private:
+ size_t refcount_;
+ iat_patch::IATPatchFunction begin_paint_;
+ iat_patch::IATPatchFunction end_paint_;
+
+ DISALLOW_COPY_AND_ASSIGN(PaintPatcher);
+};
+
+base::LazyInstance<PaintPatcher> g_paint_patcher(base::LINKER_INITIALIZED);
+
+} // namespace
+
+AutocompleteEditViewWin::AutocompleteEditViewWin(
+ const ChromeFont& font,
+ AutocompleteEditController* controller,
+ ToolbarModel* toolbar_model,
+ views::View* parent_view,
+ HWND hwnd,
+ Profile* profile,
+ CommandUpdater* command_updater,
+ bool popup_window_mode)
+ : model_(new AutocompleteEditModel(this, controller, profile)),
+ popup_view_(new AutocompletePopupViewWin(font, this, model_.get(),
+ profile)),
+ controller_(controller),
+ parent_view_(parent_view),
+ toolbar_model_(toolbar_model),
+ command_updater_(command_updater),
+ popup_window_mode_(popup_window_mode),
+ tracking_click_(false),
+ tracking_double_click_(false),
+ double_click_time_(0),
+ can_discard_mousemove_(false),
+ font_(font),
+ possible_drag_(false),
+ in_drag_(false),
+ initiated_drag_(false),
+ drop_highlight_position_(-1),
+ background_color_(0),
+ scheme_security_level_(ToolbarModel::NORMAL) {
+ model_->set_popup_model(popup_view_->model());
+
+ saved_selection_for_focus_change_.cpMin = -1;
+
+ g_paint_patcher.Pointer()->RefPatch();
+
+ Create(hwnd, 0, 0, 0, l10n_util::GetExtendedStyles());
+ SetReadOnly(popup_window_mode_);
+ SetFont(font_.hfont());
+
+ // NOTE: Do not use SetWordBreakProcEx() here, that is no longer supported as
+ // of Rich Edit 2.0 onward.
+ SendMessage(m_hWnd, EM_SETWORDBREAKPROC, 0,
+ reinterpret_cast<LPARAM>(&WordBreakProc));
+
+ // Get the metrics for the font.
+ HDC dc = ::GetDC(NULL);
+ SelectObject(dc, font_.hfont());
+ TEXTMETRIC tm = {0};
+ GetTextMetrics(dc, &tm);
+ font_ascent_ = tm.tmAscent;
+ const float kXHeightRatio = 0.7f; // The ratio of a font's x-height to its
+ // cap height. Sadly, Windows doesn't
+ // provide a true value for a font's
+ // x-height in its text metrics, so we
+ // approximate.
+ font_x_height_ = static_cast<int>((static_cast<float>(font_ascent_ -
+ tm.tmInternalLeading) * kXHeightRatio) + 0.5);
+ // The distance from the top of the field to the desired baseline of the
+ // rendered text.
+ const int kTextBaseline = popup_window_mode_ ? 15 : 18;
+ font_y_adjustment_ = kTextBaseline - font_ascent_;
+ font_descent_ = tm.tmDescent;
+
+ // Get the number of twips per pixel, which we need below to offset our text
+ // by the desired number of pixels.
+ const long kTwipsPerPixel = kTwipsPerInch / GetDeviceCaps(dc, LOGPIXELSY);
+ ::ReleaseDC(NULL, dc);
+
+ // Set the default character style -- adjust to our desired baseline and make
+ // text grey.
+ CHARFORMAT cf = {0};
+ cf.dwMask = CFM_OFFSET | CFM_COLOR;
+ cf.yOffset = -font_y_adjustment_ * kTwipsPerPixel;
+ cf.crTextColor = GetSysColor(COLOR_GRAYTEXT);
+ SetDefaultCharFormat(cf);
+
+ // Set up context menu.
+ context_menu_.reset(new Menu(this, Menu::TOPLEFT, m_hWnd));
+ if (popup_window_mode_) {
+ context_menu_->AppendMenuItemWithLabel(IDS_COPY,
+ l10n_util::GetString(IDS_COPY));
+ } else {
+ context_menu_->AppendMenuItemWithLabel(IDS_UNDO,
+ l10n_util::GetString(IDS_UNDO));
+ context_menu_->AppendSeparator();
+ context_menu_->AppendMenuItemWithLabel(IDS_CUT,
+ l10n_util::GetString(IDS_CUT));
+ context_menu_->AppendMenuItemWithLabel(IDS_COPY,
+ l10n_util::GetString(IDS_COPY));
+ context_menu_->AppendMenuItemWithLabel(IDS_PASTE,
+ l10n_util::GetString(IDS_PASTE));
+ // GetContextualLabel() will override this next label with the
+ // IDS_PASTE_AND_SEARCH label as needed.
+ context_menu_->AppendMenuItemWithLabel(
+ IDS_PASTE_AND_GO, l10n_util::GetString(IDS_PASTE_AND_GO));
+ context_menu_->AppendSeparator();
+ context_menu_->AppendMenuItemWithLabel(
+ IDS_SELECT_ALL, l10n_util::GetString(IDS_SELECT_ALL));
+ context_menu_->AppendSeparator();
+ context_menu_->AppendMenuItemWithLabel(
+ IDS_EDIT_SEARCH_ENGINES, l10n_util::GetString(IDS_EDIT_SEARCH_ENGINES));
+ }
+
+ // By default RichEdit has a drop target. Revoke it so that we can install our
+ // own. Revoke takes care of deleting the existing one.
+ RevokeDragDrop(m_hWnd);
+
+ // Register our drop target. RichEdit appears to invoke RevokeDropTarget when
+ // done so that we don't have to explicitly.
+ if (!popup_window_mode_) {
+ scoped_refptr<EditDropTarget> drop_target = new EditDropTarget(this);
+ RegisterDragDrop(m_hWnd, drop_target.get());
+ }
+}
+
+AutocompleteEditViewWin::~AutocompleteEditViewWin() {
+ NotificationService::current()->Notify(
+ NotificationType::AUTOCOMPLETE_EDIT_DESTROYED,
+ Source<AutocompleteEditViewWin>(this),
+ NotificationService::NoDetails());
+
+ // We balance our reference count and unpatch when the last instance has
+ // been destroyed. This prevents us from relying on the AtExit or static
+ // destructor sequence to do our unpatching, which is generally fragile.
+ g_paint_patcher.Pointer()->DerefPatch();
+}
+
+void AutocompleteEditViewWin::SaveStateToTab(TabContents* tab) {
+ DCHECK(tab);
+
+ const AutocompleteEditModel::State model_state(
+ model_->GetStateForTabSwitch());
+
+ CHARRANGE selection;
+ GetSelection(selection);
+ GetStateAccessor()->SetProperty(tab->property_bag(),
+ AutocompleteEditState(
+ model_state,
+ State(selection, saved_selection_for_focus_change_)));
+}
+
+void AutocompleteEditViewWin::Update(
+ const TabContents* tab_for_state_restoring) {
+ const bool visibly_changed_permanent_text =
+ model_->UpdatePermanentText(toolbar_model_->GetText());
+
+ const ToolbarModel::SecurityLevel security_level =
+ toolbar_model_->GetSchemeSecurityLevel();
+ const COLORREF background_color =
+ LocationBarView::kBackgroundColorByLevel[security_level];
+ const bool changed_security_level =
+ (security_level != scheme_security_level_);
+
+ // Bail early when no visible state will actually change (prevents an
+ // unnecessary ScopedFreeze, and thus UpdateWindow()).
+ if ((background_color == background_color_) && !changed_security_level &&
+ !visibly_changed_permanent_text && !tab_for_state_restoring)
+ return;
+
+ // Update our local state as desired. We set scheme_security_level_ here so
+ // it will already be correct before we get to any RevertAll()s below and use
+ // it.
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ if (background_color_ != background_color) {
+ background_color_ = background_color;
+ SetBackgroundColor(background_color_);
+ }
+ scheme_security_level_ = security_level;
+
+ // When we're switching to a new tab, restore its state, if any.
+ if (tab_for_state_restoring) {
+ // Make sure we reset our own state first. The new tab may not have any
+ // saved state, or it may not have had input in progress, in which case we
+ // won't overwrite all our local state.
+ RevertAll();
+
+ const AutocompleteEditState* state = GetStateAccessor()->GetProperty(
+ tab_for_state_restoring->property_bag());
+ if (state) {
+ model_->RestoreState(state->model_state);
+
+ // Restore user's selection. We do this after restoring the user_text
+ // above so we're selecting in the correct string.
+ SetSelectionRange(state->view_state.selection);
+ saved_selection_for_focus_change_ =
+ state->view_state.saved_selection_for_focus_change;
+ }
+ } else if (visibly_changed_permanent_text) {
+ // Not switching tabs, just updating the permanent text. (In the case where
+ // we _were_ switching tabs, the RevertAll() above already drew the new
+ // permanent text.)
+
+ // Tweak: if the edit was previously nonempty and had all the text selected,
+ // select all the new text. This makes one particular case better: the
+ // user clicks in the box to change it right before the permanent URL is
+ // changed. Since the new URL is still fully selected, the user's typing
+ // will replace the edit contents as they'd intended.
+ //
+ // NOTE: The selection can be longer than the text length if the edit is in
+ // in rich text mode and the user has selected the "phantom newline" at the
+ // end, so use ">=" instead of "==" to see if all the text is selected. In
+ // theory we prevent this case from ever occurring, but this is still safe.
+ CHARRANGE sel;
+ GetSelection(sel);
+ const bool was_reversed = (sel.cpMin > sel.cpMax);
+ const bool was_sel_all = (sel.cpMin != sel.cpMax) &&
+ IsSelectAllForRange(sel);
+
+ RevertAll();
+
+ if (was_sel_all)
+ SelectAll(was_reversed);
+ } else if (changed_security_level) {
+ // Only the security style changed, nothing else. Redraw our text using it.
+ EmphasizeURLComponents();
+ }
+}
+
+void AutocompleteEditViewWin::OpenURL(const GURL& url,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition,
+ const GURL& alternate_nav_url,
+ size_t selected_line,
+ const std::wstring& keyword) {
+ if (!url.is_valid())
+ return;
+
+ model_->SendOpenNotification(selected_line, keyword);
+
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ if (disposition != NEW_BACKGROUND_TAB)
+ RevertAll(); // Revert the box to its unedited state
+ controller_->OnAutocompleteAccept(url, disposition, transition,
+ alternate_nav_url);
+}
+
+std::wstring AutocompleteEditViewWin::GetText() const {
+ const int len = GetTextLength() + 1;
+ std::wstring str;
+ GetWindowText(WriteInto(&str, len), len);
+ return str;
+}
+
+void AutocompleteEditViewWin::SetUserText(const std::wstring& text,
+ const std::wstring& display_text,
+ bool update_popup) {
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ model_->SetUserText(text);
+ saved_selection_for_focus_change_.cpMin = -1;
+ SetWindowTextAndCaretPos(display_text, display_text.length());
+ if (update_popup)
+ UpdatePopup();
+ TextChanged();
+}
+
+void AutocompleteEditViewWin::SetWindowTextAndCaretPos(const std::wstring& text,
+ size_t caret_pos) {
+ HIMC imm_context = ImmGetContext(m_hWnd);
+ if (imm_context) {
+ // In Windows Vista, SetWindowText() automatically completes any ongoing
+ // IME composition, and updates the text of the underlying edit control.
+ // In Windows XP, however, SetWindowText() gets applied to the IME
+ // composition string if it exists, and doesn't update the underlying edit
+ // control. To avoid this, we force the IME to complete any outstanding
+ // compositions here. This is harmless in Vista and in cases where the IME
+ // isn't composing.
+ ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
+ ImmReleaseContext(m_hWnd, imm_context);
+ }
+
+ SetWindowText(text.c_str());
+ PlaceCaretAt(caret_pos);
+}
+
+bool AutocompleteEditViewWin::IsSelectAll() {
+ CHARRANGE selection;
+ GetSel(selection);
+ return IsSelectAllForRange(selection);
+}
+
+void AutocompleteEditViewWin::SelectAll(bool reversed) {
+ if (reversed)
+ SetSelection(GetTextLength(), 0);
+ else
+ SetSelection(0, GetTextLength());
+}
+
+void AutocompleteEditViewWin::RevertAll() {
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ ClosePopup();
+ model_->Revert();
+ saved_selection_for_focus_change_.cpMin = -1;
+ TextChanged();
+}
+
+void AutocompleteEditViewWin::UpdatePopup() {
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ model_->SetInputInProgress(true);
+
+ if (!model_->has_focus()) {
+ // When we're in the midst of losing focus, don't rerun autocomplete. This
+ // can happen when losing focus causes the IME to cancel/finalize a
+ // composition. We still want to note that user input is in progress, we
+ // just don't want to do anything else.
+ //
+ // Note that in this case the ScopedFreeze above was unnecessary; however,
+ // we're inside the callstack of OnKillFocus(), which has already frozen the
+ // edit, so this will never result in an unnecessary UpdateWindow() call.
+ return;
+ }
+
+ // Figure out whether the user is trying to compose something in an IME.
+ bool ime_composing = false;
+ HIMC context = ImmGetContext(m_hWnd);
+ if (context) {
+ ime_composing = !!ImmGetCompositionString(context, GCS_COMPSTR, NULL, 0);
+ ImmReleaseContext(m_hWnd, context);
+ }
+
+ // Don't inline autocomplete when:
+ // * The user is deleting text
+ // * The caret/selection isn't at the end of the text
+ // * The user has just pasted in something that replaced all the text
+ // * The user is trying to compose something in an IME
+ CHARRANGE sel;
+ GetSel(sel);
+ model_->StartAutocomplete((sel.cpMax < GetTextLength()) || ime_composing);
+}
+
+void AutocompleteEditViewWin::ClosePopup() {
+ popup_view_->model()->StopAutocomplete();
+}
+
+IAccessible* AutocompleteEditViewWin::GetIAccessible() {
+ if (!autocomplete_accessibility_) {
+ CComObject<AutocompleteAccessibility>* accessibility = NULL;
+ if (!SUCCEEDED(CComObject<AutocompleteAccessibility>::CreateInstance(
+ &accessibility)) || !accessibility)
+ return NULL;
+
+ // Wrap the created object in a smart pointer so it won't leak.
+ CComPtr<IAccessible> accessibility_comptr(accessibility);
+ if (!SUCCEEDED(accessibility->Initialize(this)))
+ return NULL;
+
+ // Copy to the class smart pointer, and notify that an instance of
+ // IAccessible was allocated for m_hWnd.
+ autocomplete_accessibility_ = accessibility_comptr;
+ NotifyWinEvent(EVENT_OBJECT_CREATE, m_hWnd, OBJID_CLIENT, CHILDID_SELF);
+ }
+ // Detach to leave ref counting to the caller.
+ return autocomplete_accessibility_.Detach();
+}
+
+void AutocompleteEditViewWin::SetDropHighlightPosition(int position) {
+ if (drop_highlight_position_ != position) {
+ RepaintDropHighlight(drop_highlight_position_);
+ drop_highlight_position_ = position;
+ RepaintDropHighlight(drop_highlight_position_);
+ }
+}
+
+void AutocompleteEditViewWin::MoveSelectedText(int new_position) {
+ const std::wstring selected_text(GetSelectedText());
+ CHARRANGE sel;
+ GetSel(sel);
+ DCHECK((sel.cpMax != sel.cpMin) && (new_position >= 0) &&
+ (new_position <= GetTextLength()));
+
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ OnBeforePossibleChange();
+
+ // Nuke the selected text.
+ ReplaceSel(L"", TRUE);
+
+ // And insert it into the new location.
+ if (new_position >= sel.cpMin)
+ new_position -= (sel.cpMax - sel.cpMin);
+ PlaceCaretAt(new_position);
+ ReplaceSel(selected_text.c_str(), TRUE);
+
+ OnAfterPossibleChange();
+}
+
+void AutocompleteEditViewWin::InsertText(int position,
+ const std::wstring& text) {
+ DCHECK((position >= 0) && (position <= GetTextLength()));
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ OnBeforePossibleChange();
+ SetSelection(position, position);
+ ReplaceSel(text.c_str());
+ OnAfterPossibleChange();
+}
+
+void AutocompleteEditViewWin::OnTemporaryTextMaybeChanged(
+ const std::wstring& display_text,
+ bool save_original_selection) {
+ if (save_original_selection)
+ GetSelection(original_selection_);
+
+ // Set new text and cursor position. Sometimes this does extra work (e.g.
+ // when the new text and the old text are identical), but it's only called
+ // when the user manually changes the selected line in the popup, so that's
+ // not really a problem. Also, even when the text hasn't changed we'd want to
+ // update the caret, because if the user had the cursor in the middle of the
+ // text and then arrowed to another entry with the same text, we'd still want
+ // to move the caret.
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ SetWindowTextAndCaretPos(display_text, display_text.length());
+ TextChanged();
+}
+
+bool AutocompleteEditViewWin::OnInlineAutocompleteTextMaybeChanged(
+ const std::wstring& display_text,
+ size_t user_text_length) {
+ // Update the text and selection. Because this can be called repeatedly while
+ // typing, we've careful not to freeze the edit unless we really need to.
+ // Also, unlike in the temporary text case above, here we don't want to update
+ // the caret/selection unless we have to, since this might make the user's
+ // caret position change without warning during typing.
+ if (display_text == GetText())
+ return false;
+
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ // NOTE: We don't need the IME composition hack in SetWindowTextAndCaretPos()
+ // here, because UpdatePopup() disables inline autocomplete when a
+ // composition is in progress, thus preventing us from reaching this code.
+ SetWindowText(display_text.c_str());
+ // Set a reversed selection to keep the caret in the same position, which
+ // avoids scrolling the user's text.
+ SetSelection(static_cast<LONG>(display_text.length()),
+ static_cast<LONG>(user_text_length));
+ TextChanged();
+ return true;
+}
+
+void AutocompleteEditViewWin::OnRevertTemporaryText() {
+ SetSelectionRange(original_selection_);
+ TextChanged();
+}
+
+void AutocompleteEditViewWin::OnBeforePossibleChange() {
+ // Record our state.
+ text_before_change_ = GetText();
+ GetSelection(sel_before_change_);
+}
+
+bool AutocompleteEditViewWin::OnAfterPossibleChange() {
+ // Prevent the user from selecting the "phantom newline" at the end of the
+ // edit. If they try, we just silently move the end of the selection back to
+ // the end of the real text.
+ CHARRANGE new_sel;
+ GetSelection(new_sel);
+ const int length = GetTextLength();
+ if ((new_sel.cpMin > length) || (new_sel.cpMax > length)) {
+ if (new_sel.cpMin > length)
+ new_sel.cpMin = length;
+ if (new_sel.cpMax > length)
+ new_sel.cpMax = length;
+ SetSelectionRange(new_sel);
+ }
+ const bool selection_differs = (new_sel.cpMin != sel_before_change_.cpMin) ||
+ (new_sel.cpMax != sel_before_change_.cpMax);
+ const bool at_end_of_edit =
+ (new_sel.cpMin == length) && (new_sel.cpMax == length);
+
+ // See if the text or selection have changed since OnBeforePossibleChange().
+ const std::wstring new_text(GetText());
+ const bool text_differs = (new_text != text_before_change_);
+
+ // When the user has deleted text, we don't allow inline autocomplete. Make
+ // sure to not flag cases like selecting part of the text and then pasting
+ // (or typing) the prefix of that selection. (We detect these by making
+ // sure the caret, which should be after any insertion, hasn't moved
+ // forward of the old selection start.)
+ const bool just_deleted_text =
+ (text_before_change_.length() > new_text.length()) &&
+ (new_sel.cpMin <= std::min(sel_before_change_.cpMin,
+ sel_before_change_.cpMax));
+
+
+ const bool something_changed = model_->OnAfterPossibleChange(new_text,
+ selection_differs, text_differs, just_deleted_text, at_end_of_edit);
+
+ if (something_changed && text_differs)
+ TextChanged();
+
+ return something_changed;
+}
+
+void AutocompleteEditViewWin::PasteAndGo(const std::wstring& text) {
+ if (CanPasteAndGo(text))
+ model_->PasteAndGo();
+}
+
+bool AutocompleteEditViewWin::OverrideAccelerator(
+ const views::Accelerator& accelerator) {
+ // Only override <esc>.
+ if ((accelerator.GetKeyCode() != VK_ESCAPE) || accelerator.IsAltDown())
+ return false;
+
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ return model_->OnEscapeKeyPressed();
+}
+
+void AutocompleteEditViewWin::HandleExternalMsg(UINT msg,
+ UINT flags,
+ const CPoint& screen_point) {
+ if (msg == WM_CAPTURECHANGED) {
+ SendMessage(msg, 0, NULL);
+ return;
+ }
+
+ CPoint client_point(screen_point);
+ ::MapWindowPoints(NULL, m_hWnd, &client_point, 1);
+ SendMessage(msg, flags, MAKELPARAM(client_point.x, client_point.y));
+}
+
+bool AutocompleteEditViewWin::IsCommandEnabled(int id) const {
+ switch (id) {
+ case IDS_UNDO: return !!CanUndo();
+ case IDS_CUT: return !!CanCut();
+ case IDS_COPY: return !!CanCopy();
+ case IDS_PASTE: return !!CanPaste();
+ case IDS_PASTE_AND_GO: return CanPasteAndGo(GetClipboardText());
+ case IDS_SELECT_ALL: return !!CanSelectAll();
+ case IDS_EDIT_SEARCH_ENGINES:
+ return command_updater_->IsCommandEnabled(IDC_EDIT_SEARCH_ENGINES);
+ default: NOTREACHED(); return false;
+ }
+}
+
+bool AutocompleteEditViewWin::GetContextualLabel(int id,
+ std::wstring* out) const {
+ if ((id != IDS_PASTE_AND_GO) ||
+ // No need to change the default IDS_PASTE_AND_GO label unless this is a
+ // search.
+ !model_->is_paste_and_search())
+ return false;
+
+ out->assign(l10n_util::GetString(IDS_PASTE_AND_SEARCH));
+ return true;
+}
+
+void AutocompleteEditViewWin::ExecuteCommand(int id) {
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ if (id == IDS_PASTE_AND_GO) {
+ // This case is separate from the switch() below since we don't want to wrap
+ // it in OnBefore/AfterPossibleChange() calls.
+ model_->PasteAndGo();
+ return;
+ }
+
+ OnBeforePossibleChange();
+ switch (id) {
+ case IDS_UNDO:
+ Undo();
+ break;
+
+ case IDS_CUT:
+ Cut();
+ break;
+
+ case IDS_COPY:
+ Copy();
+ break;
+
+ case IDS_PASTE:
+ Paste();
+ break;
+
+ case IDS_SELECT_ALL:
+ SelectAll(false);
+ break;
+
+ case IDS_EDIT_SEARCH_ENGINES:
+ command_updater_->ExecuteCommand(IDC_EDIT_SEARCH_ENGINES);
+ break;
+
+ default:
+ NOTREACHED();
+ break;
+ }
+ OnAfterPossibleChange();
+}
+
+// static
+int CALLBACK AutocompleteEditViewWin::WordBreakProc(LPTSTR edit_text,
+ int current_pos,
+ int num_bytes,
+ int action) {
+ // TODO(pkasting): http://b/1111308 We should let other people, like ICU and
+ // GURL, do the work for us here instead of writing all this ourselves.
+
+ // Sadly, even though the MSDN docs claim that the third parameter here is a
+ // number of characters, they lie. It's a number of bytes.
+ const int length = num_bytes / sizeof(wchar_t);
+
+ // With no clear guidance from the MSDN docs on how to handle "not found" in
+ // the "find the nearest xxx..." cases below, I cap the return values at
+ // [0, length]. Since one of these (0) is also a valid position, the return
+ // values are thus ambiguous :(
+ switch (action) {
+ // Find nearest character before current position that begins a word.
+ case WB_LEFT:
+ case WB_MOVEWORDLEFT: {
+ if (current_pos < 2) {
+ // Either current_pos == 0, so we have a "not found" case and return 0,
+ // or current_pos == 1, and the only character before this position is
+ // at 0.
+ return 0;
+ }
+
+ // Look for a delimiter before the previous character; the previous word
+ // starts immediately after. (If we looked for a delimiter before the
+ // current character, we could stop on the immediate prior character,
+ // which would mean we'd return current_pos -- which isn't "before the
+ // current position".)
+ const int prev_delim =
+ WordBreakProc(edit_text, current_pos - 1, num_bytes, WB_LEFTBREAK);
+
+ if ((prev_delim == 0) &&
+ !WordBreakProc(edit_text, 0, num_bytes, WB_ISDELIMITER)) {
+ // Got back 0, but position 0 isn't a delimiter. This was a "not
+ // found" 0, so return one of our own.
+ return 0;
+ }
+
+ return prev_delim + 1;
+ }
+
+ // Find nearest character after current position that begins a word.
+ case WB_RIGHT:
+ case WB_MOVEWORDRIGHT: {
+ if (WordBreakProc(edit_text, current_pos, num_bytes, WB_ISDELIMITER)) {
+ // The current character is a delimiter, so the next character starts
+ // a new word. Done.
+ return current_pos + 1;
+ }
+
+ // Look for a delimiter after the current character; the next word starts
+ // immediately after.
+ const int next_delim =
+ WordBreakProc(edit_text, current_pos, num_bytes, WB_RIGHTBREAK);
+ if (next_delim == length) {
+ // Didn't find a delimiter. Return length to signal "not found".
+ return length;
+ }
+
+ return next_delim + 1;
+ }
+
+ // Determine if the current character delimits words.
+ case WB_ISDELIMITER:
+ return !!(WordBreakProc(edit_text, current_pos, num_bytes, WB_CLASSIFY) &
+ WBF_BREAKLINE);
+
+ // Return the classification of the current character.
+ case WB_CLASSIFY:
+ if (IsWhitespace(edit_text[current_pos])) {
+ // Whitespace normally breaks words, but the MSDN docs say that we must
+ // not break on the CRs in a "CR, LF" or a "CR, CR, LF" sequence. Just
+ // check for an arbitrarily long sequence of CRs followed by LF and
+ // report "not a delimiter" for the current CR in that case.
+ while ((current_pos < (length - 1)) &&
+ (edit_text[current_pos] == 0x13)) {
+ if (edit_text[++current_pos] == 0x10)
+ return WBF_ISWHITE;
+ }
+ return WBF_BREAKLINE | WBF_ISWHITE;
+ }
+
+ // Punctuation normally breaks words, but the first two characters in
+ // "://" (end of scheme) should not be breaks, so that "http://" will be
+ // treated as one word.
+ if (ispunct(edit_text[current_pos], std::locale()) &&
+ !SchemeEnd(edit_text, current_pos, length) &&
+ !SchemeEnd(edit_text, current_pos - 1, length))
+ return WBF_BREAKLINE;
+
+ // Normal character, no flags.
+ return 0;
+
+ // Finds nearest delimiter before current position.
+ case WB_LEFTBREAK:
+ for (int i = current_pos - 1; i >= 0; --i) {
+ if (WordBreakProc(edit_text, i, num_bytes, WB_ISDELIMITER))
+ return i;
+ }
+ return 0;
+
+ // Finds nearest delimiter after current position.
+ case WB_RIGHTBREAK:
+ for (int i = current_pos + 1; i < length; ++i) {
+ if (WordBreakProc(edit_text, i, num_bytes, WB_ISDELIMITER))
+ return i;
+ }
+ return length;
+ }
+
+ NOTREACHED();
+ return 0;
+}
+
+// static
+bool AutocompleteEditViewWin::SchemeEnd(LPTSTR edit_text,
+ int current_pos,
+ int length) {
+ return (current_pos >= 0) &&
+ ((length - current_pos) > 2) &&
+ (edit_text[current_pos] == ':') &&
+ (edit_text[current_pos + 1] == '/') &&
+ (edit_text[current_pos + 2] == '/');
+}
+
+void AutocompleteEditViewWin::OnChar(TCHAR ch, UINT repeat_count, UINT flags) {
+ // Don't let alt-enter beep. Not sure this is necessary, as the standard
+ // alt-enter will hit DiscardWMSysChar() and get thrown away, and
+ // ctrl-alt-enter doesn't seem to reach here for some reason? At least not on
+ // my system... still, this is harmless and maybe necessary in other locales.
+ if (ch == VK_RETURN && (flags & KF_ALTDOWN))
+ return;
+
+ // Escape is processed in OnKeyDown. Don't let any WM_CHAR messages propagate
+ // as we don't want the RichEdit to do anything funky.
+ if (ch == VK_ESCAPE && !(flags & KF_ALTDOWN))
+ return;
+
+ if (ch == VK_TAB) {
+ // Don't add tabs to the input.
+ return;
+ }
+
+ HandleKeystroke(GetCurrentMessage()->message, ch, repeat_count, flags);
+}
+
+void AutocompleteEditViewWin::OnContextMenu(HWND window, const CPoint& point) {
+ if (point.x == -1 || point.y == -1) {
+ POINT p;
+ GetCaretPos(&p);
+ MapWindowPoints(HWND_DESKTOP, &p, 1);
+ context_menu_->RunMenuAt(p.x, p.y);
+ } else {
+ context_menu_->RunMenuAt(point.x, point.y);
+ }
+}
+
+void AutocompleteEditViewWin::OnCopy() {
+ const std::wstring text(GetSelectedText());
+ if (text.empty())
+ return;
+
+ ScopedClipboardWriter scw(g_browser_process->clipboard_service());
+ scw.WriteText(text);
+
+ // Check if the user is copying the whole address bar. If they are, we
+ // assume they are trying to copy a URL and write this to the clipboard as a
+ // hyperlink.
+ if (static_cast<int>(text.length()) < GetTextLength())
+ return;
+
+ // The entire control is selected. Let's see what the user typed. We can't
+ // use model_->CurrentTextIsURL() or model_->GetDataForURLExport() because
+ // right now the user is probably holding down control to cause the copy,
+ // which will screw up our calculation of the desired_tld.
+ GURL url;
+ if (model_->GetURLForText(text, &url))
+ scw.WriteHyperlink(text, url.spec());
+}
+
+void AutocompleteEditViewWin::OnCut() {
+ OnCopy();
+
+ // This replace selection will have no effect (even on the undo stack) if the
+ // current selection is empty.
+ ReplaceSel(L"", true);
+}
+
+LRESULT AutocompleteEditViewWin::OnGetObject(UINT uMsg,
+ WPARAM wparam,
+ LPARAM lparam) {
+ // Accessibility readers will send an OBJID_CLIENT message.
+ if (lparam == OBJID_CLIENT) {
+ // Re-attach for internal re-usage of accessibility pointer.
+ autocomplete_accessibility_.Attach(GetIAccessible());
+
+ if (autocomplete_accessibility_) {
+ return LresultFromObject(IID_IAccessible, wparam,
+ autocomplete_accessibility_.p);
+ }
+ }
+ return 0;
+}
+
+LRESULT AutocompleteEditViewWin::OnImeComposition(UINT message,
+ WPARAM wparam,
+ LPARAM lparam) {
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ OnBeforePossibleChange();
+ LRESULT result = DefWindowProc(message, wparam, lparam);
+ if (!OnAfterPossibleChange() && (lparam & GCS_RESULTSTR)) {
+ // The result string changed, but the text in the popup didn't actually
+ // change. This means the user finalized the composition. Rerun
+ // autocomplete so that we can now trigger inline autocomplete if
+ // applicable.
+ //
+ // Note that if we're in the midst of losing focus, UpdatePopup() won't
+ // actually rerun autocomplete, but will just set local state correctly.
+ UpdatePopup();
+ }
+ return result;
+}
+
+void AutocompleteEditViewWin::OnKeyDown(TCHAR key,
+ UINT repeat_count,
+ UINT flags) {
+ if (OnKeyDownAllModes(key, repeat_count, flags) || popup_window_mode_ ||
+ OnKeyDownOnlyWritable(key, repeat_count, flags))
+ return;
+
+ // CRichEditCtrl changes its text on WM_KEYDOWN instead of WM_CHAR for many
+ // different keys (backspace, ctrl-v, ...), so we call this in both cases.
+ HandleKeystroke(GetCurrentMessage()->message, key, repeat_count, flags);
+}
+
+void AutocompleteEditViewWin::OnKeyUp(TCHAR key,
+ UINT repeat_count,
+ UINT flags) {
+ if (key == VK_CONTROL)
+ model_->OnControlKeyChanged(false);
+
+ SetMsgHandled(false);
+}
+
+void AutocompleteEditViewWin::OnKillFocus(HWND focus_wnd) {
+ if (m_hWnd == focus_wnd) {
+ // Focus isn't actually leaving.
+ SetMsgHandled(false);
+ return;
+ }
+
+ // Close the popup.
+ ClosePopup();
+
+ // Save the user's existing selection to restore it later.
+ GetSelection(saved_selection_for_focus_change_);
+
+ // Tell the model to reset itself.
+ model_->OnKillFocus();
+
+ // Let the CRichEditCtrl do its default handling. This will complete any
+ // in-progress IME composition. We must do this after setting has_focus_ to
+ // false so that UpdatePopup() will know not to rerun autocomplete.
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ DefWindowProc(WM_KILLFOCUS, reinterpret_cast<WPARAM>(focus_wnd), 0);
+
+ // Hide the "Type to search" hint if necessary. We do this after calling
+ // DefWindowProc() because processing the resulting IME messages may notify
+ // the controller that input is in progress, which could cause the visible
+ // hints to change. (I don't know if there's a real scenario where they
+ // actually do change, but this is safest.)
+ if (model_->show_search_hint() ||
+ (model_->is_keyword_hint() && !model_->keyword().empty()))
+ controller_->OnChanged();
+
+ // Cancel any user selection and scroll the text back to the beginning of the
+ // URL. We have to do this after calling DefWindowProc() because otherwise
+ // an in-progress IME composition will be completed at the new caret position,
+ // resulting in the string jumping unexpectedly to the front of the edit.
+ PlaceCaretAt(0);
+}
+
+void AutocompleteEditViewWin::OnLButtonDblClk(UINT keys, const CPoint& point) {
+ // Save the double click info for later triple-click detection.
+ tracking_double_click_ = true;
+ double_click_point_ = point;
+ double_click_time_ = GetCurrentMessage()->time;
+ possible_drag_ = false;
+
+ // Modifying the selection counts as accepting any inline autocompletion, so
+ // track "changes" made by clicking the mouse button.
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ OnBeforePossibleChange();
+ DefWindowProc(WM_LBUTTONDBLCLK, keys,
+ MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y));
+ OnAfterPossibleChange();
+
+ gaining_focus_.reset(); // See NOTE in OnMouseActivate().
+}
+
+void AutocompleteEditViewWin::OnLButtonDown(UINT keys, const CPoint& point) {
+ if (gaining_focus_.get()) {
+ // This click is giving us focus, so we need to track how much the mouse
+ // moves to see if it's a drag or just a click. Clicks should select all
+ // the text.
+ tracking_click_ = true;
+ mouse_down_point_ = point;
+
+ // When Chrome was already the activated app, we haven't reached
+ // OnSetFocus() yet. When we get there, don't restore the saved selection,
+ // since it will just screw up the user's interaction with the edit.
+ saved_selection_for_focus_change_.cpMin = -1;
+
+ // Crazy hack: In this particular case, the CRichEditCtrl seems to have an
+ // internal flag that discards the next WM_LBUTTONDOWN without processing
+ // it, so that clicks on the edit when its owning app is not activated are
+ // eaten rather than processed (despite whatever the return value of
+ // DefWindowProc(WM_MOUSEACTIVATE, ...) may say). This behavior is
+ // confusing and we want the click to be treated normally. So, to reset the
+ // CRichEditCtrl's internal flag, we pass it an extra WM_LBUTTONDOWN here
+ // (as well as a matching WM_LBUTTONUP, just in case we'd be confusing some
+ // kind of state tracking otherwise).
+ DefWindowProc(WM_LBUTTONDOWN, keys, MAKELPARAM(point.x, point.y));
+ DefWindowProc(WM_LBUTTONUP, keys, MAKELPARAM(point.x, point.y));
+ }
+
+ // Check for triple click, then reset tracker. Should be safe to subtract
+ // double_click_time_ from the current message's time even if the timer has
+ // wrapped in between.
+ const bool is_triple_click = tracking_double_click_ &&
+ win_util::IsDoubleClick(double_click_point_, point,
+ GetCurrentMessage()->time - double_click_time_);
+ tracking_double_click_ = false;
+
+ if (!gaining_focus_.get() && !is_triple_click)
+ OnPossibleDrag(point);
+
+
+ // Modifying the selection counts as accepting any inline autocompletion, so
+ // track "changes" made by clicking the mouse button.
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ OnBeforePossibleChange();
+ DefWindowProc(WM_LBUTTONDOWN, keys,
+ MAKELPARAM(ClipXCoordToVisibleText(point.x, is_triple_click),
+ point.y));
+ OnAfterPossibleChange();
+
+ gaining_focus_.reset();
+}
+
+void AutocompleteEditViewWin::OnLButtonUp(UINT keys, const CPoint& point) {
+ // default processing should happen first so we can see the result of the
+ // selection
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ DefWindowProc(WM_LBUTTONUP, keys,
+ MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y));
+
+ // When the user has clicked and released to give us focus, select all.
+ if (tracking_click_ && !win_util::IsDrag(mouse_down_point_, point)) {
+ // Select all in the reverse direction so as not to scroll the caret
+ // into view and shift the contents jarringly.
+ SelectAll(true);
+ possible_drag_ = false;
+ }
+
+ tracking_click_ = false;
+
+ UpdateDragDone(keys);
+}
+
+LRESULT AutocompleteEditViewWin::OnMouseActivate(HWND window,
+ UINT hit_test,
+ UINT mouse_message) {
+ // First, give other handlers a chance to handle the message to see if we are
+ // actually going to activate and gain focus.
+ LRESULT result = DefWindowProc(WM_MOUSEACTIVATE,
+ reinterpret_cast<WPARAM>(window),
+ MAKELPARAM(hit_test, mouse_message));
+ // Check if we're getting focus from a left click. We have to do this here
+ // rather than in OnLButtonDown() since in many scenarios OnSetFocus() will be
+ // reached before OnLButtonDown(), preventing us from detecting this properly
+ // there. Also in those cases, we need to already know in OnSetFocus() that
+ // we should not restore the saved selection.
+ if (!model_->has_focus() && (mouse_message == WM_LBUTTONDOWN) &&
+ (result == MA_ACTIVATE)) {
+ DCHECK(!gaining_focus_.get());
+ gaining_focus_.reset(new ScopedFreeze(this, GetTextObjectModel()));
+ // NOTE: Despite |mouse_message| being WM_LBUTTONDOWN here, we're not
+ // guaranteed to call OnLButtonDown() later! Specifically, if this is the
+ // second click of a double click, we'll reach here but later call
+ // OnLButtonDblClk(). Make sure |gaining_focus_| gets reset both places, or
+ // we'll have visual glitchiness and then DCHECK failures.
+
+ // Don't restore saved selection, it will just screw up our interaction
+ // with this edit.
+ saved_selection_for_focus_change_.cpMin = -1;
+ }
+ return result;
+}
+
+void AutocompleteEditViewWin::OnMouseMove(UINT keys, const CPoint& point) {
+ if (possible_drag_) {
+ StartDragIfNecessary(point);
+ // Don't fall through to default mouse handling, otherwise a second
+ // drag session may start.
+ return;
+ }
+
+ if (tracking_click_ && !win_util::IsDrag(mouse_down_point_, point))
+ return;
+
+ tracking_click_ = false;
+
+ // Return quickly if this can't change the selection/cursor, so we don't
+ // create a ScopedFreeze (and thus do an UpdateWindow()) on every
+ // WM_MOUSEMOVE.
+ if (!(keys & MK_LBUTTON)) {
+ DefWindowProc(WM_MOUSEMOVE, keys, MAKELPARAM(point.x, point.y));
+ return;
+ }
+
+ // Clamp the selection to the visible text so the user can't drag to select
+ // the "phantom newline". In theory we could achieve this by clipping the X
+ // coordinate, but in practice the edit seems to behave nondeterministically
+ // with similar sequences of clipped input coordinates fed to it. Maybe it's
+ // reading the mouse cursor position directly?
+ //
+ // This solution has a minor visual flaw, however: if there's a visible cursor
+ // at the edge of the text (only true when there's no selection), dragging the
+ // mouse around outside that edge repaints the cursor on every WM_MOUSEMOVE
+ // instead of allowing it to blink normally. To fix this, we special-case
+ // this exact case and discard the WM_MOUSEMOVE messages instead of passing
+ // them along.
+ //
+ // But even this solution has a flaw! (Argh.) In the case where the user has
+ // a selection that starts at the edge of the edit, and proceeds to the middle
+ // of the edit, and the user is dragging back past the start edge to remove
+ // the selection, there's a redraw problem where the change between having the
+ // last few bits of text still selected and having nothing selected can be
+ // slow to repaint (which feels noticeably strange). This occurs if you only
+ // let the edit receive a single WM_MOUSEMOVE past the edge of the text. I
+ // think on each WM_MOUSEMOVE the edit is repainting its previous state, then
+ // updating its internal variables to the new state but not repainting. To
+ // fix this, we allow one more WM_MOUSEMOVE through after the selection has
+ // supposedly been shrunk to nothing; this makes the edit redraw the selection
+ // quickly so it feels smooth.
+ CHARRANGE selection;
+ GetSel(selection);
+ const bool possibly_can_discard_mousemove =
+ (selection.cpMin == selection.cpMax) &&
+ (((selection.cpMin == 0) &&
+ (ClipXCoordToVisibleText(point.x, false) > point.x)) ||
+ ((selection.cpMin == GetTextLength()) &&
+ (ClipXCoordToVisibleText(point.x, false) < point.x)));
+ if (!can_discard_mousemove_ || !possibly_can_discard_mousemove) {
+ can_discard_mousemove_ = possibly_can_discard_mousemove;
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ OnBeforePossibleChange();
+ // Force the Y coordinate to the center of the clip rect. The edit
+ // behaves strangely when the cursor is dragged vertically: if the cursor
+ // is in the middle of the text, drags inside the clip rect do nothing,
+ // and drags outside the clip rect act as if the cursor jumped to the
+ // left edge of the text. When the cursor is at the right edge, drags of
+ // just a few pixels vertically end up selecting the "phantom newline"...
+ // sometimes.
+ RECT r;
+ GetRect(&r);
+ DefWindowProc(WM_MOUSEMOVE, keys,
+ MAKELPARAM(point.x, (r.bottom - r.top) / 2));
+ OnAfterPossibleChange();
+ }
+}
+
+void AutocompleteEditViewWin::OnPaint(HDC bogus_hdc) {
+ // We need to paint over the top of the edit. If we simply let the edit do
+ // its default painting, then do ours into the window DC, the screen is
+ // updated in between and we can get flicker. To avoid this, we force the
+ // edit to paint into a memory DC, which we also paint onto, then blit the
+ // whole thing to the screen.
+
+ // Don't paint if not necessary.
+ CRect paint_clip_rect;
+ if (!GetUpdateRect(&paint_clip_rect, true))
+ return;
+
+ // Begin painting, and create a memory DC for the edit to paint into.
+ CPaintDC paint_dc(m_hWnd);
+ CDC memory_dc(CreateCompatibleDC(paint_dc));
+ CRect rect;
+ GetClientRect(&rect);
+ // NOTE: This next call uses |paint_dc| instead of |memory_dc| because
+ // |memory_dc| contains a 1x1 monochrome bitmap by default, which will cause
+ // |memory_bitmap| to be monochrome, which isn't what we want.
+ CBitmap memory_bitmap(CreateCompatibleBitmap(paint_dc, rect.Width(),
+ rect.Height()));
+ HBITMAP old_bitmap = memory_dc.SelectBitmap(memory_bitmap);
+
+ // Tell our intercept functions to supply our memory DC to the edit when it
+ // tries to call BeginPaint().
+ //
+ // The sane way to do this would be to use WM_PRINTCLIENT to ask the edit to
+ // paint into our desired DC. Unfortunately, the Rich Edit 3.0 that ships
+ // with Windows 2000/XP/Vista doesn't handle WM_PRINTCLIENT correctly; it
+ // treats it just like WM_PAINT and calls BeginPaint(), ignoring our provided
+ // DC. The Rich Edit 6.0 that ships with Office 2007 handles this better, but
+ // has other issues, and we can't redistribute that DLL anyway. So instead,
+ // we use this scary hack.
+ //
+ // NOTE: It's possible to get nested paint calls (!) (try setting the
+ // permanent URL to something longer than the edit width, then selecting the
+ // contents of the edit, typing a character, and hitting <esc>), so we can't
+ // DCHECK(!edit_hwnd_) here. Instead, just save off the old HWND, which most
+ // of the time will be NULL.
+ HWND old_edit_hwnd = edit_hwnd;
+ edit_hwnd = m_hWnd;
+ paint_struct = paint_dc.m_ps;
+ paint_struct.hdc = memory_dc;
+ DefWindowProc(WM_PAINT, reinterpret_cast<WPARAM>(bogus_hdc), 0);
+
+ // Make the selection look better.
+ EraseTopOfSelection(&memory_dc, rect, paint_clip_rect);
+
+ // Draw a slash through the scheme if this is insecure.
+ if (insecure_scheme_component_.is_nonempty())
+ DrawSlashForInsecureScheme(memory_dc, rect, paint_clip_rect);
+
+ // Draw the drop highlight.
+ if (drop_highlight_position_ != -1)
+ DrawDropHighlight(memory_dc, rect, paint_clip_rect);
+
+ // Blit the memory DC to the actual paint DC and clean up.
+ BitBlt(paint_dc, rect.left, rect.top, rect.Width(), rect.Height(), memory_dc,
+ rect.left, rect.top, SRCCOPY);
+ memory_dc.SelectBitmap(old_bitmap);
+ edit_hwnd = old_edit_hwnd;
+}
+
+void AutocompleteEditViewWin::OnNonLButtonDown(UINT keys,
+ const CPoint& point) {
+ // Interestingly, the edit doesn't seem to cancel triple clicking when the
+ // x-buttons (which usually means "thumb buttons") are pressed, so we only
+ // call this for M and R down.
+ tracking_double_click_ = false;
+
+ OnPossibleDrag(point);
+
+ SetMsgHandled(false);
+}
+
+void AutocompleteEditViewWin::OnNonLButtonUp(UINT keys, const CPoint& point) {
+ UpdateDragDone(keys);
+
+ // Let default handler have a crack at this.
+ SetMsgHandled(false);
+}
+
+void AutocompleteEditViewWin::OnPaste() {
+ // Replace the selection if we have something to paste.
+ const std::wstring text(GetClipboardText());
+ if (!text.empty()) {
+ // If this paste will be replacing all the text, record that, so we can do
+ // different behaviors in such a case.
+ if (IsSelectAll())
+ model_->on_paste_replacing_all();
+ ReplaceSel(text.c_str(), true);
+ }
+}
+
+void AutocompleteEditViewWin::OnSetFocus(HWND focus_wnd) {
+ model_->OnSetFocus(GetKeyState(VK_CONTROL) < 0);
+
+ // Notify controller if it needs to show hint UI of some kind.
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ if (model_->show_search_hint() ||
+ (model_->is_keyword_hint() && !model_->keyword().empty()))
+ controller_->OnChanged();
+
+ // Restore saved selection if available.
+ if (saved_selection_for_focus_change_.cpMin != -1) {
+ SetSelectionRange(saved_selection_for_focus_change_);
+ saved_selection_for_focus_change_.cpMin = -1;
+ }
+
+ SetMsgHandled(false);
+}
+
+void AutocompleteEditViewWin::OnSysChar(TCHAR ch,
+ UINT repeat_count,
+ UINT flags) {
+ // Nearly all alt-<xxx> combos result in beeping rather than doing something
+ // useful, so we discard most. Exceptions:
+ // * ctrl-alt-<xxx>, which is sometimes important, generates WM_CHAR instead
+ // of WM_SYSCHAR, so it doesn't need to be handled here.
+ // * alt-space gets translated by the default WM_SYSCHAR handler to a
+ // WM_SYSCOMMAND to open the application context menu, so we need to allow
+ // it through.
+ if (ch == VK_SPACE)
+ SetMsgHandled(false);
+}
+
+void AutocompleteEditViewWin::HandleKeystroke(UINT message,
+ TCHAR key,
+ UINT repeat_count,
+ UINT flags) {
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ OnBeforePossibleChange();
+ DefWindowProc(message, key, MAKELPARAM(repeat_count, flags));
+ OnAfterPossibleChange();
+}
+
+bool AutocompleteEditViewWin::OnKeyDownOnlyWritable(TCHAR key,
+ UINT repeat_count,
+ UINT flags) {
+ // NOTE: Annoyingly, ctrl-alt-<key> generates WM_KEYDOWN rather than
+ // WM_SYSKEYDOWN, so we need to check (flags & KF_ALTDOWN) in various places
+ // in this function even with a WM_SYSKEYDOWN handler.
+
+ int count = repeat_count;
+ switch (key) {
+ case VK_RETURN:
+ model_->AcceptInput((flags & KF_ALTDOWN) ?
+ NEW_FOREGROUND_TAB : CURRENT_TAB, false);
+ return true;
+
+ case VK_UP:
+ count = -count;
+ // FALL THROUGH
+ case VK_DOWN:
+ if (flags & KF_ALTDOWN)
+ return false;
+
+ model_->OnUpOrDownKeyPressed(count);
+ return true;
+
+ // Hijacking Editing Commands
+ //
+ // We hijack the keyboard short-cuts for Cut, Copy, and Paste here so that
+ // they go through our clipboard routines. This allows us to be smarter
+ // about how we interact with the clipboard and avoid bugs in the
+ // CRichEditCtrl. If we didn't hijack here, the edit control would handle
+ // these internally with sending the WM_CUT, WM_COPY, or WM_PASTE messages.
+ //
+ // Cut: Shift-Delete and Ctrl-x are treated as cut. Ctrl-Shift-Delete and
+ // Ctrl-Shift-x are not treated as cut even though the underlying
+ // CRichTextEdit would treat them as such.
+ // Copy: Ctrl-c is treated as copy. Shift-Ctrl-c is not. (This is handled
+ // in OnKeyDownAllModes().)
+ // Paste: Shift-Insert and Ctrl-v are tread as paste. Ctrl-Shift-Insert and
+ // Ctrl-Shift-v are not.
+ //
+ // This behavior matches most, but not all Windows programs, and largely
+ // conforms to what users expect.
+
+ case VK_DELETE:
+ if ((flags & KF_ALTDOWN) || GetKeyState(VK_SHIFT) >= 0)
+ return false;
+ if (GetKeyState(VK_CONTROL) >= 0) {
+ // Cut text if possible.
+ CHARRANGE selection;
+ GetSel(selection);
+ if (selection.cpMin != selection.cpMax) {
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ OnBeforePossibleChange();
+ Cut();
+ OnAfterPossibleChange();
+ } else if (popup_view_->model()->IsOpen()) {
+ // This is a bit overloaded, but we hijack Shift-Delete in this
+ // case to delete the current item from the pop-up. We prefer cutting
+ // to this when possible since that's the behavior more people expect
+ // from Shift-Delete, and it's more commonly useful.
+ popup_view_->model()->TryDeletingCurrentItem();
+ }
+ }
+ return true;
+
+ case 'X':
+ if ((flags & KF_ALTDOWN) || (GetKeyState(VK_CONTROL) >= 0))
+ return false;
+ if (GetKeyState(VK_SHIFT) >= 0) {
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ OnBeforePossibleChange();
+ Cut();
+ OnAfterPossibleChange();
+ }
+ return true;
+
+ case VK_INSERT:
+ case 'V':
+ if ((flags & KF_ALTDOWN) ||
+ (GetKeyState((key == 'V') ? VK_CONTROL : VK_SHIFT) >= 0))
+ return false;
+ if (GetKeyState((key == 'V') ? VK_SHIFT : VK_CONTROL) >= 0) {
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ OnBeforePossibleChange();
+ Paste();
+ OnAfterPossibleChange();
+ }
+ return true;
+
+ case VK_BACK: {
+ if ((flags & KF_ALTDOWN) || model_->is_keyword_hint() ||
+ model_->keyword().empty())
+ return false;
+
+ {
+ CHARRANGE selection;
+ GetSel(selection);
+ if ((selection.cpMin != selection.cpMax) || (selection.cpMin != 0))
+ return false;
+ }
+
+ // We're showing a keyword and the user pressed backspace at the beginning
+ // of the text. Delete the selected keyword.
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ model_->ClearKeyword(GetText());
+ return true;
+ }
+
+ case VK_TAB: {
+ if (model_->is_keyword_hint() && !model_->keyword().empty()) {
+ // Accept the keyword.
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ model_->AcceptKeyword();
+ }
+ return true;
+ }
+
+ case 0xbb: // Ctrl-'='. Triggers subscripting (even in plain text mode).
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool AutocompleteEditViewWin::OnKeyDownAllModes(TCHAR key,
+ UINT repeat_count,
+ UINT flags) {
+ // See KF_ALTDOWN comment atop OnKeyDownOnlyWriteable().
+
+ switch (key) {
+ case VK_CONTROL:
+ model_->OnControlKeyChanged(true);
+ return false;
+
+ case 'C':
+ // See more detailed comments in OnKeyDownOnlyWriteable().
+ if ((flags & KF_ALTDOWN) || (GetKeyState(VK_CONTROL) >= 0))
+ return false;
+ if (GetKeyState(VK_SHIFT) >= 0)
+ Copy();
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+void AutocompleteEditViewWin::GetSelection(CHARRANGE& sel) const {
+ GetSel(sel);
+
+ // See if we need to reverse the direction of the selection.
+ ITextDocument* const text_object_model = GetTextObjectModel();
+ if (!text_object_model)
+ return;
+ CComPtr<ITextSelection> selection;
+ const HRESULT hr = text_object_model->GetSelection(&selection);
+ DCHECK(hr == S_OK);
+ long flags;
+ selection->GetFlags(&flags);
+ if (flags & tomSelStartActive)
+ std::swap(sel.cpMin, sel.cpMax);
+}
+
+std::wstring AutocompleteEditViewWin::GetSelectedText() const {
+ // Figure out the length of the selection.
+ CHARRANGE sel;
+ GetSel(sel);
+
+ // Grab the selected text.
+ std::wstring str;
+ GetSelText(WriteInto(&str, sel.cpMax - sel.cpMin + 1));
+ return str;
+}
+
+void AutocompleteEditViewWin::SetSelection(LONG start, LONG end) {
+ SetSel(start, end);
+
+ if (start <= end)
+ return;
+
+ // We need to reverse the direction of the selection.
+ ITextDocument* const text_object_model = GetTextObjectModel();
+ if (!text_object_model)
+ return;
+ CComPtr<ITextSelection> selection;
+ const HRESULT hr = text_object_model->GetSelection(&selection);
+ DCHECK(hr == S_OK);
+ selection->SetFlags(tomSelStartActive);
+}
+
+void AutocompleteEditViewWin::PlaceCaretAt(std::wstring::size_type pos) {
+ SetSelection(static_cast<LONG>(pos), static_cast<LONG>(pos));
+}
+
+bool AutocompleteEditViewWin::IsSelectAllForRange(const CHARRANGE& sel) const {
+ const int text_length = GetTextLength();
+ return ((sel.cpMin == 0) && (sel.cpMax >= text_length)) ||
+ ((sel.cpMax == 0) && (sel.cpMin >= text_length));
+}
+
+LONG AutocompleteEditViewWin::ClipXCoordToVisibleText(
+ LONG x, bool is_triple_click) const {
+ // Clip the X coordinate to the left edge of the text. Careful:
+ // PosFromChar(0) may return a negative X coordinate if the beginning of the
+ // text has scrolled off the edit, so don't go past the clip rect's edge.
+ RECT r;
+ GetRect(&r);
+ const int left_bound = std::max(r.left, PosFromChar(0).x);
+ if (x < left_bound)
+ return left_bound;
+
+ // See if we need to clip to the right edge of the text.
+ const int length = GetTextLength();
+ // Asking for the coordinate of any character past the end of the text gets
+ // the pixel just to the right of the last character.
+ const int right_bound = std::min(r.right, PosFromChar(length).x);
+ if ((length == 0) || (x < right_bound))
+ return x;
+
+ // For trailing characters that are 2 pixels wide of less (like "l" in some
+ // fonts), we have a problem:
+ // * Clicks on any pixel within the character will place the cursor before
+ // the character.
+ // * Clicks on the pixel just after the character will not allow triple-
+ // click to work properly (true for any last character width).
+ // So, we move to the last pixel of the character when this is a
+ // triple-click, and moving to one past the last pixel in all other
+ // scenarios. This way, all clicks that can move the cursor will place it at
+ // the end of the text, but triple-click will still work.
+ return is_triple_click ? (right_bound - 1) : right_bound;
+}
+
+void AutocompleteEditViewWin::EmphasizeURLComponents() {
+ ITextDocument* const text_object_model = GetTextObjectModel();
+ ScopedFreeze freeze(this, text_object_model);
+ ScopedSuspendUndo suspend_undo(text_object_model);
+
+ // Save the selection.
+ CHARRANGE saved_sel;
+ GetSelection(saved_sel);
+
+ // See whether the contents are a URL with a non-empty host portion, which we
+ // should emphasize. To check for a URL, rather than using the type returned
+ // by Parse(), ask the model, which will check the desired page transition for
+ // this input. This can tell us whether an UNKNOWN input string is going to
+ // be treated as a search or a navigation, and is the same method the Paste
+ // And Go system uses.
+ url_parse::Parsed parts;
+ AutocompleteInput::Parse(GetText(), model_->GetDesiredTLD(), &parts, NULL);
+ const bool emphasize = model_->CurrentTextIsURL() && (parts.host.len > 0);
+
+ // Set the baseline emphasis.
+ CHARFORMAT cf = {0};
+ cf.dwMask = CFM_COLOR;
+ cf.dwEffects = 0;
+ cf.crTextColor = GetSysColor(emphasize ? COLOR_GRAYTEXT : COLOR_WINDOWTEXT);
+ SelectAll(false);
+ SetSelectionCharFormat(cf);
+
+ if (emphasize) {
+ // We've found a host name, give it more emphasis.
+ cf.crTextColor = GetSysColor(COLOR_WINDOWTEXT);
+ SetSelection(parts.host.begin, parts.host.end());
+ SetSelectionCharFormat(cf);
+ }
+
+ // Emphasize the scheme for security UI display purposes (if necessary).
+ insecure_scheme_component_.reset();
+ if (!model_->user_input_in_progress() && parts.scheme.is_nonempty() &&
+ (scheme_security_level_ != ToolbarModel::NORMAL)) {
+ if (scheme_security_level_ == ToolbarModel::SECURE) {
+ cf.crTextColor = kSecureSchemeColor;
+ } else {
+ insecure_scheme_component_.begin = parts.scheme.begin;
+ insecure_scheme_component_.len = parts.scheme.len;
+ cf.crTextColor = kInsecureSchemeColor;
+ }
+ SetSelection(parts.scheme.begin, parts.scheme.end());
+ SetSelectionCharFormat(cf);
+ }
+
+ // Restore the selection.
+ SetSelectionRange(saved_sel);
+}
+
+void AutocompleteEditViewWin::EraseTopOfSelection(
+ CDC* dc, const CRect& client_rect, const CRect& paint_clip_rect) {
+ // Find the area we care about painting. We could calculate the rect
+ // containing just the selected portion, but there's no harm in simply erasing
+ // the whole top of the client area, and at least once I saw us manage to
+ // select the "phantom newline" briefly, which looks very weird if not clipped
+ // off at the same height.
+ CRect erase_rect(client_rect.left, client_rect.top, client_rect.right,
+ client_rect.top + font_y_adjustment_);
+ erase_rect.IntersectRect(erase_rect, paint_clip_rect);
+
+ // Erase to the background color.
+ if (!erase_rect.IsRectNull())
+ dc->FillSolidRect(&erase_rect, background_color_);
+}
+
+void AutocompleteEditViewWin::DrawSlashForInsecureScheme(
+ HDC hdc,
+ const CRect& client_rect,
+ const CRect& paint_clip_rect) {
+ DCHECK(insecure_scheme_component_.is_nonempty());
+
+ // Calculate the rect, in window coordinates, containing the portion of the
+ // scheme where we'll be drawing the slash. Vertically, we draw across one
+ // x-height of text, plus an additional 3 stroke diameters (the stroke width
+ // plus a half-stroke width of space between the stroke and the text, both
+ // above and below the text).
+ const int font_top = client_rect.top + font_y_adjustment_;
+ const SkScalar kStrokeWidthPixels = SkIntToScalar(2);
+ const int kAdditionalSpaceOutsideFont =
+ static_cast<int>(ceil(kStrokeWidthPixels * 1.5f));
+ const CRect scheme_rect(PosFromChar(insecure_scheme_component_.begin).x,
+ font_top + font_ascent_ - font_x_height_ -
+ kAdditionalSpaceOutsideFont,
+ PosFromChar(insecure_scheme_component_.end()).x,
+ font_top + font_ascent_ +
+ kAdditionalSpaceOutsideFont);
+
+ // Clip to the portion we care about and translate to canvas coordinates
+ // (see the canvas creation below) for use later.
+ CRect canvas_clip_rect, canvas_paint_clip_rect;
+ canvas_clip_rect.IntersectRect(scheme_rect, client_rect);
+ canvas_paint_clip_rect.IntersectRect(canvas_clip_rect, paint_clip_rect);
+ if (canvas_paint_clip_rect.IsRectNull())
+ return; // We don't need to paint any of this region, so just bail early.
+ canvas_clip_rect.OffsetRect(-scheme_rect.left, -scheme_rect.top);
+ canvas_paint_clip_rect.OffsetRect(-scheme_rect.left, -scheme_rect.top);
+
+ // Create a paint context for drawing the antialiased stroke.
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStrokeWidth(kStrokeWidthPixels);
+ paint.setStrokeCap(SkPaint::kRound_Cap);
+
+ // Create a canvas as large as |scheme_rect| to do our drawing, and initialize
+ // it to fully transparent so any antialiasing will look nice when painted
+ // atop the edit.
+ ChromeCanvas canvas(scheme_rect.Width(), scheme_rect.Height(), false);
+ // TODO (jcampan): This const_cast should not be necessary once the SKIA
+ // API has been changed to return a non-const bitmap.
+ (const_cast<SkBitmap&>(canvas.getDevice()->accessBitmap(true))).
+ eraseARGB(0, 0, 0, 0);
+
+ // Calculate the start and end of the stroke, which are just the lower left
+ // and upper right corners of the canvas, inset by the radius of the endcap
+ // so we don't clip the endcap off.
+ const SkScalar kEndCapRadiusPixels = kStrokeWidthPixels / SkIntToScalar(2);
+ const SkPoint start_point = {
+ kEndCapRadiusPixels,
+ SkIntToScalar(scheme_rect.Height()) - kEndCapRadiusPixels };
+ const SkPoint end_point = {
+ SkIntToScalar(scheme_rect.Width()) - kEndCapRadiusPixels,
+ kEndCapRadiusPixels };
+
+ // Calculate the selection rectangle in canvas coordinates, which we'll use
+ // to clip the stroke so we can draw the unselected and selected portions.
+ CHARRANGE sel;
+ GetSel(sel);
+ const SkRect selection_rect = {
+ SkIntToScalar(PosFromChar(sel.cpMin).x - scheme_rect.left),
+ SkIntToScalar(0),
+ SkIntToScalar(PosFromChar(sel.cpMax).x - scheme_rect.left),
+ SkIntToScalar(scheme_rect.Height()) };
+
+ // Draw the unselected portion of the stroke.
+ canvas.save();
+ if (selection_rect.isEmpty() ||
+ canvas.clipRect(selection_rect, SkRegion::kDifference_Op)) {
+ paint.setColor(kSchemeStrikeoutColor);
+ canvas.drawLine(start_point.fX, start_point.fY,
+ end_point.fX, end_point.fY, paint);
+ }
+ canvas.restore();
+
+ // Draw the selected portion of the stroke.
+ if (!selection_rect.isEmpty() && canvas.clipRect(selection_rect)) {
+ paint.setColor(kSchemeSelectedStrikeoutColor);
+ canvas.drawLine(start_point.fX, start_point.fY,
+ end_point.fX, end_point.fY, paint);
+ }
+
+ // Now copy what we drew to the target HDC.
+ canvas.getTopPlatformDevice().drawToHDC(hdc,
+ scheme_rect.left + canvas_paint_clip_rect.left - canvas_clip_rect.left,
+ std::max(scheme_rect.top, client_rect.top) + canvas_paint_clip_rect.top -
+ canvas_clip_rect.top, &canvas_paint_clip_rect);
+}
+
+void AutocompleteEditViewWin::DrawDropHighlight(
+ HDC hdc, const CRect& client_rect, const CRect& paint_clip_rect) {
+ DCHECK(drop_highlight_position_ != -1);
+
+ const int highlight_y = client_rect.top + font_y_adjustment_;
+ const int highlight_x = PosFromChar(drop_highlight_position_).x - 1;
+ const CRect highlight_rect(highlight_x,
+ highlight_y,
+ highlight_x + 1,
+ highlight_y + font_ascent_ + font_descent_);
+
+ // Clip the highlight to the region being painted.
+ CRect clip_rect;
+ clip_rect.IntersectRect(highlight_rect, paint_clip_rect);
+ if (clip_rect.IsRectNull())
+ return;
+
+ HGDIOBJ last_pen = SelectObject(hdc, CreatePen(PS_SOLID, 1, RGB(0, 0, 0)));
+ MoveToEx(hdc, clip_rect.left, clip_rect.top, NULL);
+ LineTo(hdc, clip_rect.left, clip_rect.bottom);
+ DeleteObject(SelectObject(hdc, last_pen));
+}
+
+void AutocompleteEditViewWin::TextChanged() {
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ EmphasizeURLComponents();
+ controller_->OnChanged();
+}
+
+std::wstring AutocompleteEditViewWin::GetClipboardText() const {
+ // Try text format.
+ ClipboardService* clipboard = g_browser_process->clipboard_service();
+ if (clipboard->IsFormatAvailable(CF_UNICODETEXT)) {
+ std::wstring text;
+ clipboard->ReadText(&text);
+
+ // Note: Unlike in the find popup and textfield view, here we completely
+ // remove whitespace strings containing newlines. We assume users are
+ // most likely pasting in URLs that may have been split into multiple
+ // lines in terminals, email programs, etc., and so linebreaks indicate
+ // completely bogus whitespace that would just cause the input to be
+ // invalid.
+ return CollapseWhitespace(text, true);
+ }
+
+ // Try bookmark format.
+ //
+ // It is tempting to try bookmark format first, but the URL we get out of a
+ // bookmark has been cannonicalized via GURL. This means if a user copies
+ // and pastes from the URL bar to itself, the text will get fixed up and
+ // cannonicalized, which is not what the user expects. By pasting in this
+ // order, we are sure to paste what the user copied.
+ if (clipboard->IsFormatAvailable(Clipboard::GetUrlWFormatType())) {
+ std::string url_str;
+ clipboard->ReadBookmark(NULL, &url_str);
+ // pass resulting url string through GURL to normalize
+ GURL url(url_str);
+ if (url.is_valid())
+ return UTF8ToWide(url.spec());
+ }
+
+ return std::wstring();
+}
+
+bool AutocompleteEditViewWin::CanPasteAndGo(const std::wstring& text) const {
+ return !popup_window_mode_ && model_->CanPasteAndGo(text);
+}
+
+ITextDocument* AutocompleteEditViewWin::GetTextObjectModel() const {
+ if (!text_object_model_) {
+ // This is lazily initialized, instead of being initialized in the
+ // constructor, in order to avoid hurting startup performance.
+ CComPtr<IRichEditOle> ole_interface;
+ ole_interface.Attach(GetOleInterface());
+ text_object_model_ = ole_interface;
+ }
+ return text_object_model_;
+}
+
+void AutocompleteEditViewWin::StartDragIfNecessary(const CPoint& point) {
+ if (initiated_drag_ || !win_util::IsDrag(mouse_down_point_, point))
+ return;
+
+ scoped_refptr<OSExchangeData> data = new OSExchangeData;
+
+ DWORD supported_modes = DROPEFFECT_COPY;
+
+ CHARRANGE sel;
+ GetSelection(sel);
+
+ // We're about to start a drag session, but the edit is expecting a mouse up
+ // that it uses to reset internal state. If we don't send a mouse up now,
+ // when the mouse moves back into the edit the edit will reset the selection.
+ // So, we send the event now which resets the selection. We then restore the
+ // selection and start the drag. We always send lbuttonup as otherwise we
+ // might trigger a context menu (right up). This seems scary, but doesn't
+ // seem to cause problems.
+ {
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ DefWindowProc(WM_LBUTTONUP, 0,
+ MAKELPARAM(mouse_down_point_.x, mouse_down_point_.y));
+ SetSelectionRange(sel);
+ }
+
+ const std::wstring start_text(GetText());
+ if (IsSelectAllForRange(sel)) {
+ // All the text is selected, export as URL.
+ GURL url;
+ std::wstring title;
+ SkBitmap favicon;
+ model_->GetDataForURLExport(&url, &title, &favicon);
+ drag_utils::SetURLAndDragImage(url, title, favicon, data.get());
+ data->SetURL(url, title);
+ supported_modes |= DROPEFFECT_LINK;
+ UserMetrics::RecordAction(L"Omnibox_DragURL", model_->profile());
+ } else {
+ supported_modes |= DROPEFFECT_MOVE;
+ UserMetrics::RecordAction(L"Omnibox_DragString", model_->profile());
+ }
+
+ data->SetString(GetSelectedText());
+
+ scoped_refptr<BaseDragSource> drag_source(new BaseDragSource);
+ DWORD dropped_mode;
+ in_drag_ = true;
+ if (DoDragDrop(data, drag_source, supported_modes, &dropped_mode) ==
+ DRAGDROP_S_DROP) {
+ if ((dropped_mode == DROPEFFECT_MOVE) && (start_text == GetText())) {
+ ScopedFreeze freeze(this, GetTextObjectModel());
+ OnBeforePossibleChange();
+ SetSelectionRange(sel);
+ ReplaceSel(L"", true);
+ OnAfterPossibleChange();
+ }
+ // else case, not a move or it was a move and the drop was on us.
+ // If the drop was on us, EditDropTarget took care of the move so that
+ // we don't have to delete the text.
+ possible_drag_ = false;
+ } else {
+ // Drag was canceled or failed. The mouse may still be down and
+ // over us, in which case we need possible_drag_ to remain true so
+ // that we don't forward mouse move events to the edit which will
+ // start another drag.
+ //
+ // NOTE: we didn't use mouse capture during the mouse down as DoDragDrop
+ // does its own capture.
+ CPoint cursor_location;
+ GetCursorPos(&cursor_location);
+
+ CRect client_rect;
+ GetClientRect(&client_rect);
+
+ CPoint client_origin_on_screen(client_rect.left, client_rect.top);
+ ClientToScreen(&client_origin_on_screen);
+ client_rect.MoveToXY(client_origin_on_screen.x,
+ client_origin_on_screen.y);
+ possible_drag_ = (client_rect.PtInRect(cursor_location) &&
+ ((GetKeyState(VK_LBUTTON) != 0) ||
+ (GetKeyState(VK_MBUTTON) != 0) ||
+ (GetKeyState(VK_RBUTTON) != 0)));
+ }
+
+ in_drag_ = false;
+ initiated_drag_ = true;
+ tracking_click_ = false;
+}
+
+void AutocompleteEditViewWin::OnPossibleDrag(const CPoint& point) {
+ if (possible_drag_)
+ return;
+
+ mouse_down_point_ = point;
+ initiated_drag_ = false;
+
+ CHARRANGE selection;
+ GetSel(selection);
+ if (selection.cpMin != selection.cpMax) {
+ const POINT min_sel_location(PosFromChar(selection.cpMin));
+ const POINT max_sel_location(PosFromChar(selection.cpMax));
+ // NOTE: we don't consider the y location here as we always pass a
+ // y-coordinate in the middle to the default handler which always triggers
+ // a drag regardless of the y-coordinate.
+ possible_drag_ = (point.x >= min_sel_location.x) &&
+ (point.x < max_sel_location.x);
+ }
+}
+
+void AutocompleteEditViewWin::UpdateDragDone(UINT keys) {
+ possible_drag_ = (possible_drag_ &&
+ ((keys & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) != 0));
+}
+
+void AutocompleteEditViewWin::RepaintDropHighlight(int position) {
+ if ((position != -1) && (position <= GetTextLength())) {
+ const POINT min_loc(PosFromChar(position));
+ const RECT highlight_bounds = {min_loc.x - 1, font_y_adjustment_,
+ min_loc.x + 2, font_ascent_ + font_descent_ + font_y_adjustment_};
+ InvalidateRect(&highlight_bounds, false);
+ }
+}
diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_win.h b/chrome/browser/autocomplete/autocomplete_edit_view_win.h
new file mode 100644
index 0000000..9905f1c
--- /dev/null
+++ b/chrome/browser/autocomplete/autocomplete_edit_view_win.h
@@ -0,0 +1,458 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_EDIT_VIEW_WIN_H_
+#define CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_EDIT_VIEW_WIN_H_
+
+#include <atlbase.h>
+#include <atlapp.h>
+#include <atlcrack.h>
+#include <atlctrls.h>
+#include <atlmisc.h>
+#include <tom.h> // For ITextDocument, a COM interface to CRichEditCtrl.
+
+#include "base/scoped_ptr.h"
+#include "chrome/browser/autocomplete/autocomplete.h"
+#include "chrome/browser/autocomplete/autocomplete_edit_view.h"
+#include "chrome/browser/toolbar_model.h"
+#include "chrome/common/gfx/chrome_font.h"
+#include "chrome/common/page_transition_types.h"
+#include "chrome/views/menu.h"
+#include "webkit/glue/window_open_disposition.h"
+
+class AutocompletePopupModel;
+class CommandUpdater;
+class Profile;
+class TabContents;
+namespace views {
+class View;
+}
+
+class AutocompleteEditController;
+class AutocompleteEditModel;
+class AutocompleteEditView;
+class AutocompletePopupViewWin;
+
+// Provides the implementation of an edit control with a drop-down
+// autocomplete box. The box itself is implemented in autocomplete_popup.cc
+// This file implements the edit box and management for the popup.
+class AutocompleteEditViewWin
+ : public CWindowImpl<AutocompleteEditViewWin,
+ CRichEditCtrl,
+ CWinTraits<WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL |
+ ES_NOHIDESEL> >,
+ public CRichEditCommands<AutocompleteEditViewWin>,
+ public Menu::Delegate,
+ public AutocompleteEditView {
+ public:
+ struct State {
+ State(const CHARRANGE& selection,
+ const CHARRANGE& saved_selection_for_focus_change)
+ : selection(selection),
+ saved_selection_for_focus_change(saved_selection_for_focus_change) {
+ }
+
+ const CHARRANGE selection;
+ const CHARRANGE saved_selection_for_focus_change;
+ };
+
+ DECLARE_WND_CLASS(L"Chrome_AutocompleteEditView");
+
+ AutocompleteEditViewWin(const ChromeFont& font,
+ AutocompleteEditController* controller,
+ ToolbarModel* toolbar_model,
+ views::View* parent_view,
+ HWND hwnd,
+ Profile* profile,
+ CommandUpdater* command_updater,
+ bool popup_window_mode);
+ ~AutocompleteEditViewWin();
+
+ views::View* parent_view() const { return parent_view_; }
+
+ // Implement the AutocompleteEditView interface.
+ virtual AutocompleteEditModel* model() { return model_.get(); }
+ virtual const AutocompleteEditModel* model() const { return model_.get(); }
+
+ virtual void SaveStateToTab(TabContents* tab);
+
+ virtual void Update(const TabContents* tab_for_state_restoring);
+
+ virtual void OpenURL(const GURL& url,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition,
+ const GURL& alternate_nav_url,
+ size_t selected_line,
+ const std::wstring& keyword);
+
+ virtual std::wstring GetText() const;
+
+ virtual void SetUserText(const std::wstring& text) {
+ SetUserText(text, text, true);
+ }
+ virtual void SetUserText(const std::wstring& text,
+ const std::wstring& display_text,
+ bool update_popup);
+
+ virtual void SetWindowTextAndCaretPos(const std::wstring& text,
+ size_t caret_pos);
+
+ virtual bool IsSelectAll();
+ virtual void SelectAll(bool reversed);
+ virtual void RevertAll();
+
+ virtual void UpdatePopup();
+ virtual void ClosePopup();
+
+ virtual void OnTemporaryTextMaybeChanged(const std::wstring& display_text,
+ bool save_original_selection);
+ virtual bool OnInlineAutocompleteTextMaybeChanged(
+ const std::wstring& display_text, size_t user_text_length);
+ virtual void OnRevertTemporaryText();
+ virtual void OnBeforePossibleChange();
+ virtual bool OnAfterPossibleChange();
+
+ // Exposes custom IAccessible implementation to the overall MSAA hierarchy.
+ IAccessible* GetIAccessible();
+
+ void SetDropHighlightPosition(int position);
+ int drop_highlight_position() const { return drop_highlight_position_; }
+
+ // Returns true if a drag a drop session was initiated by this edit.
+ bool in_drag() const { return in_drag_; }
+
+ // Moves the selected text to the specified position.
+ void MoveSelectedText(int new_position);
+
+ // Inserts the text at the specified position.
+ void InsertText(int position, const std::wstring& text);
+
+ // Invokes CanPasteAndGo with the specified text, and if successful navigates
+ // to the appropriate URL. The behavior of this is the same as if the user
+ // typed in the specified text and pressed enter.
+ void PasteAndGo(const std::wstring& text);
+
+ // Called before an accelerator is processed to give us a chance to override
+ // it.
+ bool OverrideAccelerator(const views::Accelerator& accelerator);
+
+ // Handler for external events passed in to us. The View that owns us may
+ // send us events that we should treat as if they were events on us.
+ void HandleExternalMsg(UINT msg, UINT flags, const CPoint& screen_point);
+
+ // CWindowImpl
+ BEGIN_MSG_MAP(AutocompleteEdit)
+ MSG_WM_CHAR(OnChar)
+ MSG_WM_CONTEXTMENU(OnContextMenu)
+ MSG_WM_COPY(OnCopy)
+ MSG_WM_CUT(OnCut)
+ MESSAGE_HANDLER_EX(WM_GETOBJECT, OnGetObject)
+ MESSAGE_HANDLER_EX(WM_IME_COMPOSITION, OnImeComposition)
+ MSG_WM_KEYDOWN(OnKeyDown)
+ MSG_WM_KEYUP(OnKeyUp)
+ MSG_WM_KILLFOCUS(OnKillFocus)
+ MSG_WM_LBUTTONDBLCLK(OnLButtonDblClk)
+ MSG_WM_LBUTTONDOWN(OnLButtonDown)
+ MSG_WM_LBUTTONUP(OnLButtonUp)
+ MSG_WM_MBUTTONDOWN(OnNonLButtonDown)
+ MSG_WM_MBUTTONUP(OnNonLButtonUp)
+ MSG_WM_MOUSEACTIVATE(OnMouseActivate)
+ MSG_WM_MOUSEMOVE(OnMouseMove)
+ MSG_WM_PAINT(OnPaint)
+ MSG_WM_PASTE(OnPaste)
+ MSG_WM_RBUTTONDOWN(OnNonLButtonDown)
+ MSG_WM_RBUTTONUP(OnNonLButtonUp)
+ MSG_WM_SETFOCUS(OnSetFocus)
+ MSG_WM_SYSCHAR(OnSysChar) // WM_SYSxxx == WM_xxx with ALT down
+ MSG_WM_SYSKEYDOWN(OnKeyDown)
+ MSG_WM_SYSKEYUP(OnKeyUp)
+ DEFAULT_REFLECTION_HANDLER() // avoids black margin area
+ END_MSG_MAP()
+
+ // Menu::Delegate
+ virtual bool IsCommandEnabled(int id) const;
+ virtual bool GetContextualLabel(int id, std::wstring* out) const;
+ virtual void ExecuteCommand(int id);
+
+ private:
+ // This object freezes repainting of the edit until the object is destroyed.
+ // Some methods of the CRichEditCtrl draw synchronously to the screen. If we
+ // don't freeze, the user will see a rapid series of calls to these as
+ // flickers.
+ //
+ // Freezing the control while it is already frozen is permitted; the control
+ // will unfreeze once both freezes are released (the freezes stack).
+ class ScopedFreeze {
+ public:
+ ScopedFreeze(AutocompleteEditViewWin* edit,
+ ITextDocument* text_object_model);
+ ~ScopedFreeze();
+
+ private:
+ AutocompleteEditViewWin* const edit_;
+ ITextDocument* const text_object_model_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedFreeze);
+ };
+
+ // This object suspends placing any operations on the edit's undo stack until
+ // the object is destroyed. If we don't do this, some of the operations we
+ // perform behind the user's back will be undoable by the user, which feels
+ // bizarre and confusing.
+ class ScopedSuspendUndo {
+ public:
+ explicit ScopedSuspendUndo(ITextDocument* text_object_model);
+ ~ScopedSuspendUndo();
+
+ private:
+ ITextDocument* const text_object_model_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSuspendUndo);
+ };
+
+ // Replacement word-breaking proc for the rich edit control.
+ static int CALLBACK WordBreakProc(LPTSTR edit_text,
+ int current_pos,
+ int num_bytes,
+ int action);
+
+ // Returns true if |edit_text| starting at |current_pos| is "://".
+ static bool SchemeEnd(LPTSTR edit_text, int current_pos, int length);
+
+ // Message handlers
+ void OnChar(TCHAR ch, UINT repeat_count, UINT flags);
+ void OnContextMenu(HWND window, const CPoint& point);
+ void OnCopy();
+ void OnCut();
+ LRESULT OnGetObject(UINT uMsg, WPARAM wparam, LPARAM lparam);
+ LRESULT OnImeComposition(UINT message, WPARAM wparam, LPARAM lparam);
+ void OnKeyDown(TCHAR key, UINT repeat_count, UINT flags);
+ void OnKeyUp(TCHAR key, UINT repeat_count, UINT flags);
+ void OnKillFocus(HWND focus_wnd);
+ void OnLButtonDblClk(UINT keys, const CPoint& point);
+ void OnLButtonDown(UINT keys, const CPoint& point);
+ void OnLButtonUp(UINT keys, const CPoint& point);
+ LRESULT OnMouseActivate(HWND window, UINT hit_test, UINT mouse_message);
+ void OnMouseMove(UINT keys, const CPoint& point);
+ void OnNonLButtonDown(UINT keys, const CPoint& point);
+ void OnNonLButtonUp(UINT keys, const CPoint& point);
+ void OnPaint(HDC bogus_hdc);
+ void OnPaste();
+ void OnSetFocus(HWND focus_wnd);
+ void OnSysChar(TCHAR ch, UINT repeat_count, UINT flags);
+
+ // Helper function for OnChar() and OnKeyDown() that handles keystrokes that
+ // could change the text in the edit.
+ void HandleKeystroke(UINT message, TCHAR key, UINT repeat_count, UINT flags);
+
+ // Helper functions for OnKeyDown() that handle accelerators applicable when
+ // we're not read-only and all the time, respectively. These return true if
+ // they handled the key.
+ bool OnKeyDownOnlyWritable(TCHAR key, UINT repeat_count, UINT flags);
+ bool OnKeyDownAllModes(TCHAR key, UINT repeat_count, UINT flags);
+
+ // Like GetSel(), but returns a range where |cpMin| will be larger than
+ // |cpMax| if the cursor is at the start rather than the end of the selection
+ // (in other words, tracks selection direction as well as offsets).
+ // Note the non-Google-style "non-const-ref" argument, which matches GetSel().
+ void GetSelection(CHARRANGE& sel) const;
+
+ // Returns the currently selected text of the edit control.
+ std::wstring GetSelectedText() const;
+
+ // Like SetSel(), but respects the selection direction implied by |start| and
+ // |end|: if |end| < |start|, the effective cursor will be placed at the
+ // beginning of the selection.
+ void SetSelection(LONG start, LONG end);
+
+ // Like SetSelection(), but takes a CHARRANGE.
+ void SetSelectionRange(const CHARRANGE& sel) {
+ SetSelection(sel.cpMin, sel.cpMax);
+ }
+
+ // Places the caret at the given position. This clears any selection.
+ void PlaceCaretAt(size_t pos);
+
+ // Returns true if |sel| represents a forward or backward selection of all the
+ // text.
+ bool IsSelectAllForRange(const CHARRANGE& sel) const;
+
+ // Given an X coordinate in client coordinates, returns that coordinate
+ // clipped to be within the horizontal bounds of the visible text.
+ //
+ // This is used in our mouse handlers to work around quirky behaviors of the
+ // underlying CRichEditCtrl like not supporting triple-click when the user
+ // doesn't click on the text itself.
+ //
+ // |is_triple_click| should be true iff this is the third click of a triple
+ // click. Sadly, we need to clip slightly differently in this case.
+ LONG ClipXCoordToVisibleText(LONG x, bool is_triple_click) const;
+
+ // Parses the contents of the control for the scheme and the host name.
+ // Highlights the scheme in green or red depending on it security level.
+ // If a host name is found, it makes it visually stronger.
+ void EmphasizeURLComponents();
+
+ // Erases the portion of the selection in the font's y-adjustment area. For
+ // some reason the edit draws the selection rect here even though it's not
+ // part of the font.
+ void EraseTopOfSelection(CDC* dc,
+ const CRect& client_rect,
+ const CRect& paint_clip_rect);
+
+ // Draws a slash across the scheme if desired.
+ void DrawSlashForInsecureScheme(HDC hdc,
+ const CRect& client_rect,
+ const CRect& paint_clip_rect);
+
+ // Renders the drop highlight.
+ void DrawDropHighlight(HDC hdc,
+ const CRect& client_rect,
+ const CRect& paint_clip_rect);
+
+ // Internally invoked whenever the text changes in some way.
+ void TextChanged();
+
+ // Returns the current clipboard contents as a string that can be pasted in.
+ // In addition to just getting CF_UNICODETEXT out, this can also extract URLs
+ // from bookmarks on the clipboard.
+ std::wstring GetClipboardText() const;
+
+ // Determines whether the user can "paste and go", given the specified text.
+ bool CanPasteAndGo(const std::wstring& text) const;
+
+ // Getter for the text_object_model_, used by the ScopedXXX classes. Note
+ // that the pointer returned here is only valid as long as the
+ // AutocompleteEdit is still alive. Also, if the underlying call fails, this
+ // may return NULL.
+ ITextDocument* GetTextObjectModel() const;
+
+ // Invoked during a mouse move. As necessary starts a drag and drop session.
+ void StartDragIfNecessary(const CPoint& point);
+
+ // Invoked during a mouse down. If the mouse location is over the selection
+ // this sets possible_drag_ to true to indicate a drag should start if the
+ // user moves the mouse far enough to start a drag.
+ void OnPossibleDrag(const CPoint& point);
+
+ // Invoked when a mouse button is released. If none of the buttons are still
+ // down, this sets possible_drag_ to false.
+ void UpdateDragDone(UINT keys);
+
+ // Redraws the necessary region for a drop highlight at the specified
+ // position. This does nothing if position is beyond the bounds of the
+ // text.
+ void RepaintDropHighlight(int position);
+
+ scoped_ptr<AutocompleteEditModel> model_;
+
+ scoped_ptr<AutocompletePopupViewWin> popup_view_;
+
+ AutocompleteEditController* controller_;
+
+ // The parent view for the edit, used to align the popup and for
+ // accessibility.
+ views::View* parent_view_;
+
+ ToolbarModel* toolbar_model_;
+
+ // The object that handles additional command functionality exposed on the
+ // edit, such as invoking the keyword editor.
+ CommandUpdater* command_updater_;
+
+ // 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.
+ bool popup_window_mode_;
+
+ // Non-null when the edit is gaining focus from a left click. This is only
+ // needed between when WM_MOUSEACTIVATE and WM_LBUTTONDOWN get processed. It
+ // serves two purposes: first, by communicating to OnLButtonDown() that we're
+ // gaining focus from a left click, it allows us to work even with the
+ // inconsistent order in which various Windows messages get sent (see comments
+ // in OnMouseActivate()). Second, by holding the edit frozen, it ensures that
+ // when we process WM_SETFOCUS the edit won't first redraw itself with the
+ // caret at the beginning, and then have it blink to where the mouse cursor
+ // really is shortly afterward.
+ scoped_ptr<ScopedFreeze> gaining_focus_;
+
+ // When the user clicks to give us focus, we watch to see if they're clicking
+ // or dragging. When they're clicking, we select nothing until mouseup, then
+ // select all the text in the edit. During this process, tracking_click_ is
+ // true and mouse_down_point_ holds the original click location. At other
+ // times, tracking_click_ is false, and the contents of mouse_down_point_
+ // should be ignored.
+ bool tracking_click_;
+ CPoint mouse_down_point_;
+
+ // We need to know if the user triple-clicks, so track double click points
+ // and times so we can see if subsequent clicks are actually triple clicks.
+ bool tracking_double_click_;
+ CPoint double_click_point_;
+ DWORD double_click_time_;
+
+ // Used to discard unnecessary WM_MOUSEMOVE events after the first such
+ // unnecessary event. See detailed comments in OnMouseMove().
+ bool can_discard_mousemove_;
+
+ // Variables for tracking state before and after a possible change.
+ std::wstring text_before_change_;
+ CHARRANGE sel_before_change_;
+
+ // Set at the same time the model's original_* members are set, and valid in
+ // the same cases.
+ CHARRANGE original_selection_;
+
+ // Holds the user's selection across focus changes. cpMin holds -1 when
+ // there is no saved selection.
+ CHARRANGE saved_selection_for_focus_change_;
+
+ // The context menu for the edit.
+ scoped_ptr<Menu> context_menu_;
+
+ // Font we're using. We keep a reference to make sure the font supplied to
+ // the constructor doesn't go away before we do.
+ ChromeFont font_;
+
+ // Metrics about the font, which we keep so we don't need to recalculate them
+ // every time we paint. |font_y_adjustment_| is the number of pixels we need
+ // to shift the font vertically in order to make its baseline be at our
+ // desired baseline in the edit.
+ int font_ascent_;
+ int font_descent_;
+ int font_x_height_;
+ int font_y_adjustment_;
+
+ // If true, indicates the mouse is down and if the mouse is moved enough we
+ // should start a drag.
+ bool possible_drag_;
+
+ // If true, we're in a call to DoDragDrop.
+ bool in_drag_;
+
+ // If true indicates we've run a drag and drop session. This is used to
+ // avoid starting two drag and drop sessions if the drag is canceled while
+ // the mouse is still down.
+ bool initiated_drag_;
+
+ // Position of the drop highlight. If this is -1, there is no drop highlight.
+ int drop_highlight_position_;
+
+ // Security UI-related data.
+ COLORREF background_color_;
+ ToolbarModel::SecurityLevel scheme_security_level_;
+
+ // This interface is useful for accessing the CRichEditCtrl at a low level.
+ mutable CComQIPtr<ITextDocument> text_object_model_;
+
+ // This contains the scheme char start and stop indexes that should be
+ // striken-out when displaying an insecure scheme.
+ url_parse::Component insecure_scheme_component_;
+
+ // Instance of accessibility information and handling.
+ mutable CComPtr<IAccessible> autocomplete_accessibility_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutocompleteEditViewWin);
+};
+
+#endif // CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_EDIT_VIEW_WIN_H_
diff --git a/chrome/browser/autocomplete/autocomplete_popup_view_win.cc b/chrome/browser/autocomplete/autocomplete_popup_view_win.cc
index a583154..1c13a6c 100755
--- a/chrome/browser/autocomplete/autocomplete_popup_view_win.cc
+++ b/chrome/browser/autocomplete/autocomplete_popup_view_win.cc
@@ -185,7 +185,7 @@ COLORREF AutocompletePopupViewWin::DrawLineInfo::AlphaBlend(
AutocompletePopupViewWin::AutocompletePopupViewWin(
const ChromeFont& font,
- AutocompleteEditView* edit_view,
+ AutocompleteEditViewWin* edit_view,
AutocompleteEditModel* edit_model,
Profile* profile)
: model_(new AutocompletePopupModel(this, edit_model, profile)),
diff --git a/chrome/browser/autocomplete/autocomplete_popup_view_win.h b/chrome/browser/autocomplete/autocomplete_popup_view_win.h
index dcfd505..18c6449 100755
--- a/chrome/browser/autocomplete/autocomplete_popup_view_win.h
+++ b/chrome/browser/autocomplete/autocomplete_popup_view_win.h
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#ifndef CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_POPUP_VIEW_WIN_H_
+#define CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_POPUP_VIEW_WIN_H_
+
#include <atlbase.h>
#include <atlapp.h>
#include <atlcrack.h>
@@ -18,7 +21,7 @@
class AutocompletePopupModel;
class AutocompleteEditModel;
-class AutocompleteEditView;
+class AutocompleteEditViewWin;
class Profile;
class SkBitmap;
@@ -46,7 +49,7 @@ class AutocompletePopupViewWin
END_MSG_MAP()
AutocompletePopupViewWin(const ChromeFont& font,
- AutocompleteEditView* edit_view,
+ AutocompleteEditViewWin* edit_view,
AutocompleteEditModel* edit_model,
Profile* profile);
@@ -183,7 +186,7 @@ class AutocompletePopupViewWin
scoped_ptr<AutocompletePopupModel> model_;
- AutocompleteEditView* edit_view_;
+ AutocompleteEditViewWin* edit_view_;
// Cached GDI information for drawing.
DrawLineInfo line_info_;
@@ -203,3 +206,5 @@ class AutocompletePopupViewWin
DISALLOW_COPY_AND_ASSIGN(AutocompletePopupViewWin);
};
+
+#endif // CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_POPUP_VIEW_WIN_H_
diff --git a/chrome/browser/autocomplete/edit_drop_target.cc b/chrome/browser/autocomplete/edit_drop_target.cc
deleted file mode 100644
index abe1566..0000000
--- a/chrome/browser/autocomplete/edit_drop_target.cc
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/autocomplete/edit_drop_target.h"
-
-#include "base/string_util.h"
-#include "chrome/browser/autocomplete/autocomplete_edit.h"
-#include "chrome/common/os_exchange_data.h"
-#include "googleurl/src/gurl.h"
-
-namespace {
-
-// A helper method for determining a valid DROPEFFECT given the allowed
-// DROPEFFECTS. We prefer copy over link.
-DWORD CopyOrLinkDropEffect(DWORD effect) {
- if (effect & DROPEFFECT_COPY)
- return DROPEFFECT_COPY;
- if (effect & DROPEFFECT_LINK)
- return DROPEFFECT_LINK;
- return DROPEFFECT_NONE;
-}
-
-}
-
-EditDropTarget::EditDropTarget(AutocompleteEditView* edit)
- : BaseDropTarget(edit->m_hWnd),
- edit_(edit),
- drag_has_url_(false),
- drag_has_string_(false) {
-}
-
-DWORD EditDropTarget::OnDragEnter(IDataObject* data_object,
- DWORD key_state,
- POINT cursor_position,
- DWORD effect) {
- OSExchangeData os_data(data_object);
- drag_has_url_ = os_data.HasURL();
- drag_has_string_ = !drag_has_url_ && os_data.HasString();
- if (drag_has_url_) {
- if (edit_->in_drag()) {
- // The edit we're associated with originated the drag. No point in
- // allowing the user to drop back on us.
- drag_has_url_ = false;
- }
- // NOTE: it would be nice to visually show all the text is going to
- // be replaced by selecting all, but this caused painting problems. In
- // particular the flashing caret would appear outside the edit! For now
- // we stick with no visual indicator other than that shown own the mouse
- // cursor.
- }
- return OnDragOver(data_object, key_state, cursor_position, effect);
-}
-
-DWORD EditDropTarget::OnDragOver(IDataObject* data_object,
- DWORD key_state,
- POINT cursor_position,
- DWORD effect) {
- if (drag_has_url_)
- return CopyOrLinkDropEffect(effect);
-
- if (drag_has_string_) {
- UpdateDropHighlightPosition(cursor_position);
- if (edit_->drop_highlight_position() == -1 && edit_->in_drag())
- return DROPEFFECT_NONE;
- if (edit_->in_drag()) {
- // The edit we're associated with originated the drag. Do the normal drag
- // behavior.
- DCHECK((effect & DROPEFFECT_COPY) && (effect & DROPEFFECT_MOVE));
- return (key_state & MK_CONTROL) ? DROPEFFECT_COPY : DROPEFFECT_MOVE;
- }
- // Our edit didn't originate the drag, only allow link or copy.
- return CopyOrLinkDropEffect(effect);
- }
-
- return DROPEFFECT_NONE;
-}
-
-void EditDropTarget::OnDragLeave(IDataObject* data_object) {
- ResetDropHighlights();
-}
-
-DWORD EditDropTarget::OnDrop(IDataObject* data_object,
- DWORD key_state,
- POINT cursor_position,
- DWORD effect) {
- OSExchangeData os_data(data_object);
-
- if (drag_has_url_) {
- GURL url;
- std::wstring title;
- if (os_data.GetURLAndTitle(&url, &title)) {
- edit_->SetUserText(UTF8ToWide(url.spec()));
- edit_->model()->AcceptInput(CURRENT_TAB, true);
- return CopyOrLinkDropEffect(effect);
- }
- } else if (drag_has_string_) {
- int string_drop_position = edit_->drop_highlight_position();
- std::wstring text;
- if ((string_drop_position != -1 || !edit_->in_drag()) &&
- os_data.GetString(&text)) {
- DCHECK(string_drop_position == -1 ||
- ((string_drop_position >= 0) &&
- (string_drop_position <= edit_->GetTextLength())));
- const DWORD drop_operation =
- OnDragOver(data_object, key_state, cursor_position, effect);
- if (edit_->in_drag()) {
- if (drop_operation == DROPEFFECT_MOVE)
- edit_->MoveSelectedText(string_drop_position);
- else
- edit_->InsertText(string_drop_position, text);
- } else {
- edit_->PasteAndGo(CollapseWhitespace(text, true));
- }
- ResetDropHighlights();
- return drop_operation;
- }
- }
-
- ResetDropHighlights();
-
- return DROPEFFECT_NONE;
-}
-
-void EditDropTarget::UpdateDropHighlightPosition(
- const POINT& cursor_screen_position) {
- if (drag_has_string_) {
- POINT client_position = cursor_screen_position;
- ScreenToClient(edit_->m_hWnd, &client_position);
- int drop_position = edit_->CharFromPos(client_position);
- if (edit_->in_drag()) {
- // Our edit originated the drag, don't allow a drop if over the selected
- // region.
- LONG sel_start, sel_end;
- edit_->GetSel(sel_start, sel_end);
- if ((sel_start != sel_end) && (drop_position >= sel_start) &&
- (drop_position <= sel_end))
- drop_position = -1;
- } else {
- // A drop from a source other than the edit replaces all the text, so
- // we don't show the drop location. See comment in OnDragEnter as to why
- // we don't try and select all here.
- drop_position = -1;
- }
- edit_->SetDropHighlightPosition(drop_position);
- }
-}
-
-void EditDropTarget::ResetDropHighlights() {
- if (drag_has_string_)
- edit_->SetDropHighlightPosition(-1);
-}
diff --git a/chrome/browser/autocomplete/edit_drop_target.h b/chrome/browser/autocomplete/edit_drop_target.h
deleted file mode 100644
index c032544..0000000
--- a/chrome/browser/autocomplete/edit_drop_target.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_AUTOCOMPLETE_EDIT_DROP_TARGET_H_
-#define CHROME_BROWSER_AUTOCOMPLETE_EDIT_DROP_TARGET_H_
-
-#include "base/base_drop_target.h"
-
-class AutocompleteEditView;
-
-// EditDropTarget is the IDropTarget implementation installed on
-// AutocompleteEditView. EditDropTarget prefers URL over plain text. A drop of a
-// URL replaces all the text of the edit and navigates immediately to the URL. A
-// drop of plain text from the same edit either copies or moves the selected
-// text, and a drop of plain text from a source other than the edit does a paste
-// and go.
-class EditDropTarget : public BaseDropTarget {
- public:
- explicit EditDropTarget(AutocompleteEditView* edit);
-
- protected:
- virtual DWORD OnDragEnter(IDataObject* data_object,
- DWORD key_state,
- POINT cursor_position,
- DWORD effect);
- virtual DWORD OnDragOver(IDataObject* data_object,
- DWORD key_state,
- POINT cursor_position,
- DWORD effect);
- virtual void OnDragLeave(IDataObject* data_object);
- virtual DWORD OnDrop(IDataObject* data_object,
- DWORD key_state,
- POINT cursor_position,
- DWORD effect);
-
- private:
- // If dragging a string, the drop highlight position of the edit is reset
- // based on the mouse position.
- void UpdateDropHighlightPosition(const POINT& cursor_screen_position);
-
- // Resets the visual drop indicates we install on the edit.
- void ResetDropHighlights();
-
- // The edit we're the drop target for.
- AutocompleteEditView* edit_;
-
- // If true, the drag session contains a URL.
- bool drag_has_url_;
-
- // If true, the drag session contains a string. If drag_has_url_ is true,
- // this is false regardless of whether the clipboard has a string.
- bool drag_has_string_;
-
- DISALLOW_EVIL_CONSTRUCTORS(EditDropTarget);
-};
-
-#endif // CHROME_BROWSER_AUTOCOMPLETE_EDIT_DROP_TARGET_H_
diff --git a/chrome/browser/automation/automation_autocomplete_edit_tracker.h b/chrome/browser/automation/automation_autocomplete_edit_tracker.h
index e6d4cbd..717a760 100644
--- a/chrome/browser/automation/automation_autocomplete_edit_tracker.h
+++ b/chrome/browser/automation/automation_autocomplete_edit_tracker.h
@@ -6,6 +6,7 @@
#define CHROME_BROWSER_AUTOMATION_AUTOMATION_AUTOCOMPLETE_EDIT_TRACKER_H__
#include "chrome/browser/autocomplete/autocomplete_edit.h"
+#include "chrome/browser/autocomplete/autocomplete_edit_view.h"
#include "chrome/browser/automation/automation_resource_tracker.h"
class AutomationAutocompleteEditTracker:
diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj
index 735c51b..cc48667 100644
--- a/chrome/browser/browser.vcproj
+++ b/chrome/browser/browser.vcproj
@@ -158,15 +158,19 @@
>
</File>
<File
- RelativePath=".\autocomplete\autocomplete_popup_model.cc"
+ RelativePath=".\autocomplete\autocomplete_edit_view.h"
>
</File>
<File
- RelativePath=".\autocomplete\autocomplete_popup_model.h"
+ RelativePath=".\autocomplete\autocomplete_edit_view_win.cc"
>
</File>
<File
- RelativePath=".\autocomplete\autocomplete_popup_view_win.cc"
+ RelativePath=".\autocomplete\autocomplete_edit_view_win.h"
+ >
+ </File>
+ <File
+ RelativePath=".\autocomplete\autocomplete_popup_model.cc"
>
</File>
<File
@@ -178,11 +182,15 @@
>
</File>
<File
- RelativePath=".\autocomplete\edit_drop_target.cc"
+ RelativePath=".\autocomplete\autocomplete_popup_model.h"
+ >
+ </File>
+ <File
+ RelativePath=".\autocomplete\autocomplete_popup_view_win.cc"
>
</File>
<File
- RelativePath=".\autocomplete\edit_drop_target.h"
+ RelativePath=".\autocomplete\autocomplete_popup_view.h"
>
</File>
<File
diff --git a/chrome/browser/views/location_bar_view.cc b/chrome/browser/views/location_bar_view.cc
index 7cd64a7..8290b01 100644
--- a/chrome/browser/views/location_bar_view.cc
+++ b/chrome/browser/views/location_bar_view.cc
@@ -119,10 +119,10 @@ void LocationBarView::Init() {
// URL edit field.
views::Widget* widget = GetWidget();
- location_entry_.reset(new AutocompleteEditView(font_, this, model_, this,
- widget->GetHWND(),
- profile_, command_updater_,
- popup_window_mode_));
+ location_entry_.reset(new AutocompleteEditViewWin(font_, this, model_, this,
+ widget->GetHWND(),
+ profile_, command_updater_,
+ popup_window_mode_));
// View container for URL edit field.
location_entry_view_ = new views::HWNDView;
diff --git a/chrome/browser/views/location_bar_view.h b/chrome/browser/views/location_bar_view.h
index ef8c73b..9038135 100644
--- a/chrome/browser/views/location_bar_view.h
+++ b/chrome/browser/views/location_bar_view.h
@@ -9,6 +9,7 @@
#include "base/gfx/rect.h"
#include "chrome/browser/autocomplete/autocomplete_edit.h"
+#include "chrome/browser/autocomplete/autocomplete_edit_view_win.h"
#include "chrome/browser/location_bar.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/toolbar_model.h"
@@ -335,7 +336,7 @@ class LocationBarView : public LocationBar,
Profile* profile_;
// The Autocomplete Edit field.
- scoped_ptr<AutocompleteEditView> location_entry_;
+ scoped_ptr<AutocompleteEditViewWin> location_entry_;
// The CommandUpdater for the Browser object that corresponds to this View.
CommandUpdater* command_updater_;