| Index: views/controls/text_field.cc
|
| ===================================================================
|
| --- views/controls/text_field.cc (revision 17006)
|
| +++ views/controls/text_field.cc (working copy)
|
| @@ -1,1206 +0,0 @@
|
| -// Copyright (c) 2009 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 "views/controls/text_field.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 <vsstyle.h>
|
| -
|
| -#include "app/gfx/insets.h"
|
| -#include "app/l10n_util.h"
|
| -#include "app/l10n_util_win.h"
|
| -#include "app/win_util.h"
|
| -#include "base/clipboard.h"
|
| -#include "base/gfx/native_theme.h"
|
| -#include "base/scoped_clipboard_writer.h"
|
| -#include "base/string_util.h"
|
| -#include "base/win_util.h"
|
| -#include "grit/app_strings.h"
|
| -#include "skia/ext/skia_utils_win.h"
|
| -#include "views/controls/hwnd_view.h"
|
| -#include "views/controls/menu/menu_win.h"
|
| -#include "views/focus/focus_util_win.h"
|
| -#include "views/views_delegate.h"
|
| -#include "views/widget/widget.h"
|
| -
|
| -using gfx::NativeTheme;
|
| -
|
| -namespace views {
|
| -
|
| -static const int kDefaultEditStyle = WS_CHILD | WS_VISIBLE;
|
| -
|
| -class TextField::Edit
|
| - : public CWindowImpl<TextField::Edit, CRichEditCtrl,
|
| - CWinTraits<kDefaultEditStyle> >,
|
| - public CRichEditCommands<TextField::Edit>,
|
| - public Menu::Delegate {
|
| - public:
|
| - DECLARE_WND_CLASS(L"ChromeViewsTextFieldEdit");
|
| -
|
| - Edit(TextField* parent, bool draw_border);
|
| - ~Edit();
|
| -
|
| - std::wstring GetText() const;
|
| - void SetText(const std::wstring& text);
|
| - void AppendText(const std::wstring& text);
|
| -
|
| - std::wstring GetSelectedText() const;
|
| -
|
| - // 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();
|
| -
|
| - // Clears the selection within the edit field and sets the caret to the end.
|
| - void ClearSelection();
|
| -
|
| - // Removes the border.
|
| - void RemoveBorder();
|
| -
|
| - void SetEnabled(bool enabled);
|
| -
|
| - void SetBackgroundColor(COLORREF bg_color);
|
| -
|
| - // CWindowImpl
|
| - BEGIN_MSG_MAP(Edit)
|
| - MSG_WM_CHAR(OnChar)
|
| - MSG_WM_CONTEXTMENU(OnContextMenu)
|
| - MSG_WM_COPY(OnCopy)
|
| - MSG_WM_CUT(OnCut)
|
| - MESSAGE_HANDLER_EX(WM_IME_CHAR, OnImeChar)
|
| - MESSAGE_HANDLER_EX(WM_IME_STARTCOMPOSITION, OnImeStartComposition)
|
| - MESSAGE_HANDLER_EX(WM_IME_COMPOSITION, OnImeComposition)
|
| - MESSAGE_HANDLER_EX(WM_IME_ENDCOMPOSITION, OnImeEndComposition)
|
| - MSG_WM_KEYDOWN(OnKeyDown)
|
| - MSG_WM_LBUTTONDBLCLK(OnLButtonDblClk)
|
| - MSG_WM_LBUTTONDOWN(OnLButtonDown)
|
| - MSG_WM_LBUTTONUP(OnLButtonUp)
|
| - MSG_WM_MBUTTONDOWN(OnNonLButtonDown)
|
| - MSG_WM_MOUSEMOVE(OnMouseMove)
|
| - MSG_WM_MOUSELEAVE(OnMouseLeave)
|
| - MESSAGE_HANDLER_EX(WM_MOUSEWHEEL, OnMouseWheel)
|
| - MSG_WM_NCCALCSIZE(OnNCCalcSize)
|
| - MSG_WM_NCPAINT(OnNCPaint)
|
| - MSG_WM_RBUTTONDOWN(OnNonLButtonDown)
|
| - MSG_WM_PASTE(OnPaste)
|
| - MSG_WM_SYSCHAR(OnSysChar) // WM_SYSxxx == WM_xxx with ALT down
|
| - MSG_WM_SYSKEYDOWN(OnKeyDown)
|
| - END_MSG_MAP()
|
| -
|
| - // Menu::Delegate
|
| - virtual bool IsCommandEnabled(int id) 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(Edit* edit, ITextDocument* text_object_model);
|
| - ~ScopedFreeze();
|
| -
|
| - private:
|
| - Edit* const edit_;
|
| - ITextDocument* const text_object_model_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(ScopedFreeze);
|
| - };
|
| -
|
| - // message handlers
|
| - void OnChar(TCHAR key, UINT repeat_count, UINT flags);
|
| - void OnContextMenu(HWND window, const CPoint& point);
|
| - void OnCopy();
|
| - void OnCut();
|
| - LRESULT OnImeChar(UINT message, WPARAM wparam, LPARAM lparam);
|
| - LRESULT OnImeStartComposition(UINT message, WPARAM wparam, LPARAM lparam);
|
| - LRESULT OnImeComposition(UINT message, WPARAM wparam, LPARAM lparam);
|
| - LRESULT OnImeEndComposition(UINT message, WPARAM wparam, LPARAM lparam);
|
| - void OnKeyDown(TCHAR key, UINT repeat_count, UINT flags);
|
| - void OnLButtonDblClk(UINT keys, const CPoint& point);
|
| - void OnLButtonDown(UINT keys, const CPoint& point);
|
| - void OnLButtonUp(UINT keys, const CPoint& point);
|
| - void OnMouseLeave();
|
| - LRESULT OnMouseWheel(UINT message, WPARAM w_param, LPARAM l_param);
|
| - void OnMouseMove(UINT keys, const CPoint& point);
|
| - int OnNCCalcSize(BOOL w_param, LPARAM l_param);
|
| - void OnNCPaint(HRGN region);
|
| - void OnNonLButtonDown(UINT keys, const CPoint& point);
|
| - void OnPaste();
|
| - 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);
|
| -
|
| - // 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();
|
| - void OnAfterPossibleChange();
|
| -
|
| - // 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;
|
| -
|
| - // Sets whether the mouse is in the edit. As necessary this redraws the
|
| - // edit.
|
| - void SetContainsMouse(bool contains_mouse);
|
| -
|
| - // Getter for the text_object_model_, used by the ScopedFreeze class. Note
|
| - // that the pointer returned here is only valid as long as the Edit is still
|
| - // alive.
|
| - ITextDocument* GetTextObjectModel() const;
|
| -
|
| - // 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_;
|
| -
|
| - // The text of this control before a possible change.
|
| - std::wstring text_before_change_;
|
| -
|
| - // If true, the mouse is over the edit.
|
| - bool contains_mouse_;
|
| -
|
| - static bool did_load_library_;
|
| -
|
| - TextField* parent_;
|
| -
|
| - // The context menu for the edit.
|
| - scoped_ptr<Menu> context_menu_;
|
| -
|
| - // Border insets.
|
| - gfx::Insets content_insets_;
|
| -
|
| - // Whether the border is drawn.
|
| - bool draw_border_;
|
| -
|
| - // This interface is useful for accessing the CRichEditCtrl at a low level.
|
| - mutable CComQIPtr<ITextDocument> text_object_model_;
|
| -
|
| - // The position and the length of the ongoing composition string.
|
| - // These values are used for removing a composition string from a search
|
| - // text to emulate Firefox.
|
| - bool ime_discard_composition_;
|
| - int ime_composition_start_;
|
| - int ime_composition_length_;
|
| -
|
| - COLORREF bg_color_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(Edit);
|
| -};
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -// Helper classes
|
| -
|
| -TextField::Edit::ScopedFreeze::ScopedFreeze(TextField::Edit* 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);
|
| - }
|
| -}
|
| -
|
| -TextField::Edit::ScopedFreeze::~ScopedFreeze() {
|
| - // Unfreeze the screen.
|
| - if (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();
|
| - }
|
| - }
|
| -}
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -// TextField::Edit
|
| -
|
| -bool TextField::Edit::did_load_library_ = false;
|
| -
|
| -TextField::Edit::Edit(TextField* parent, bool draw_border)
|
| - : parent_(parent),
|
| - tracking_double_click_(false),
|
| - double_click_time_(0),
|
| - can_discard_mousemove_(false),
|
| - contains_mouse_(false),
|
| - draw_border_(draw_border),
|
| - ime_discard_composition_(false),
|
| - ime_composition_start_(0),
|
| - ime_composition_length_(0),
|
| - bg_color_(0) {
|
| - if (!did_load_library_)
|
| - did_load_library_ = !!LoadLibrary(L"riched20.dll");
|
| -
|
| - DWORD style = kDefaultEditStyle;
|
| - if (parent->GetStyle() & TextField::STYLE_PASSWORD)
|
| - style |= ES_PASSWORD;
|
| -
|
| - if (parent->read_only_)
|
| - style |= ES_READONLY;
|
| -
|
| - if (parent->GetStyle() & TextField::STYLE_MULTILINE)
|
| - style |= ES_MULTILINE | ES_WANTRETURN | ES_AUTOVSCROLL;
|
| - else
|
| - style |= ES_AUTOHSCROLL;
|
| - // Make sure we apply RTL related extended window styles if necessary.
|
| - DWORD ex_style = l10n_util::GetExtendedStyles();
|
| -
|
| - RECT r = {0, 0, parent_->width(), parent_->height()};
|
| - Create(parent_->GetWidget()->GetNativeView(), r, NULL, style, ex_style);
|
| -
|
| - if (parent->GetStyle() & TextField::STYLE_LOWERCASE) {
|
| - DCHECK((parent->GetStyle() & TextField::STYLE_PASSWORD) == 0);
|
| - SetEditStyle(SES_LOWERCASE, SES_LOWERCASE);
|
| - }
|
| -
|
| - // Set up the text_object_model_.
|
| - CComPtr<IRichEditOle> ole_interface;
|
| - ole_interface.Attach(GetOleInterface());
|
| - text_object_model_ = ole_interface;
|
| -
|
| - context_menu_.reset(new MenuWin(this, Menu::TOPLEFT, m_hWnd));
|
| - context_menu_->AppendMenuItemWithLabel(IDS_APP_UNDO,
|
| - l10n_util::GetString(IDS_APP_UNDO));
|
| - context_menu_->AppendSeparator();
|
| - context_menu_->AppendMenuItemWithLabel(IDS_APP_CUT,
|
| - l10n_util::GetString(IDS_APP_CUT));
|
| - context_menu_->AppendMenuItemWithLabel(IDS_APP_COPY,
|
| - l10n_util::GetString(IDS_APP_COPY));
|
| - context_menu_->AppendMenuItemWithLabel(IDS_APP_PASTE,
|
| - l10n_util::GetString(IDS_APP_PASTE));
|
| - context_menu_->AppendSeparator();
|
| - context_menu_->AppendMenuItemWithLabel(IDS_APP_SELECT_ALL,
|
| - l10n_util::GetString(IDS_APP_SELECT_ALL));
|
| -}
|
| -
|
| -TextField::Edit::~Edit() {
|
| -}
|
| -
|
| -std::wstring TextField::Edit::GetText() const {
|
| - int len = GetTextLength() + 1;
|
| - std::wstring str;
|
| - GetWindowText(WriteInto(&str, len), len);
|
| - return str;
|
| -}
|
| -
|
| -void TextField::Edit::SetText(const std::wstring& text) {
|
| - // Adjusting the string direction before setting the text in order to make
|
| - // sure both RTL and LTR strings are displayed properly.
|
| - std::wstring text_to_set;
|
| - if (!l10n_util::AdjustStringForLocaleDirection(text, &text_to_set))
|
| - text_to_set = text;
|
| - if (parent_->GetStyle() & STYLE_LOWERCASE)
|
| - text_to_set = l10n_util::ToLower(text_to_set);
|
| - SetWindowText(text_to_set.c_str());
|
| -}
|
| -
|
| -void TextField::Edit::AppendText(const std::wstring& text) {
|
| - int text_length = GetWindowTextLength();
|
| - ::SendMessage(m_hWnd, TBM_SETSEL, true, MAKELPARAM(text_length, text_length));
|
| - ::SendMessage(m_hWnd, EM_REPLACESEL, false,
|
| - reinterpret_cast<LPARAM>(text.c_str()));
|
| -}
|
| -
|
| -std::wstring TextField::Edit::GetSelectedText() const {
|
| - // Figure out the length of the selection.
|
| - long start;
|
| - long end;
|
| - GetSel(start, end);
|
| -
|
| - // Grab the selected text.
|
| - std::wstring str;
|
| - GetSelText(WriteInto(&str, end - start + 1));
|
| -
|
| - return str;
|
| -}
|
| -
|
| -void TextField::Edit::SelectAll() {
|
| - // Select from the end to the front so that the first part of the text is
|
| - // always visible.
|
| - SetSel(GetTextLength(), 0);
|
| -}
|
| -
|
| -void TextField::Edit::ClearSelection() {
|
| - SetSel(GetTextLength(), GetTextLength());
|
| -}
|
| -
|
| -void TextField::Edit::RemoveBorder() {
|
| - if (!draw_border_)
|
| - return;
|
| -
|
| - draw_border_ = false;
|
| - SetWindowPos(NULL, 0, 0, 0, 0,
|
| - SWP_NOMOVE | SWP_FRAMECHANGED | SWP_NOACTIVATE |
|
| - SWP_NOOWNERZORDER | SWP_NOSIZE);
|
| -}
|
| -
|
| -void TextField::Edit::SetEnabled(bool enabled) {
|
| - SendMessage(parent_->GetNativeComponent(), WM_ENABLE,
|
| - static_cast<WPARAM>(enabled), 0);
|
| -}
|
| -
|
| -// static
|
| -bool TextField::IsKeystrokeEnter(const Keystroke& key) {
|
| - return key.key == VK_RETURN;
|
| -}
|
| -
|
| -// static
|
| -bool TextField::IsKeystrokeEscape(const Keystroke& key) {
|
| - return key.key == VK_ESCAPE;
|
| -}
|
| -
|
| -void TextField::Edit::SetBackgroundColor(COLORREF bg_color) {
|
| - CRichEditCtrl::SetBackgroundColor(bg_color);
|
| - bg_color_ = bg_color;
|
| -}
|
| -
|
| -bool TextField::Edit::IsCommandEnabled(int id) const {
|
| - switch (id) {
|
| - case IDS_APP_UNDO: return !parent_->IsReadOnly() && !!CanUndo();
|
| - case IDS_APP_CUT: return !parent_->IsReadOnly() &&
|
| - !parent_->IsPassword() && !!CanCut();
|
| - case IDS_APP_COPY: return !!CanCopy() && !parent_->IsPassword();
|
| - case IDS_APP_PASTE: return !parent_->IsReadOnly() && !!CanPaste();
|
| - case IDS_APP_SELECT_ALL: return !!CanSelectAll();
|
| - default: NOTREACHED();
|
| - return false;
|
| - }
|
| -}
|
| -
|
| -void TextField::Edit::ExecuteCommand(int id) {
|
| - ScopedFreeze freeze(this, GetTextObjectModel());
|
| - OnBeforePossibleChange();
|
| - switch (id) {
|
| - case IDS_APP_UNDO: Undo(); break;
|
| - case IDS_APP_CUT: Cut(); break;
|
| - case IDS_APP_COPY: Copy(); break;
|
| - case IDS_APP_PASTE: Paste(); break;
|
| - case IDS_APP_SELECT_ALL: SelectAll(); break;
|
| - default: NOTREACHED(); break;
|
| - }
|
| - OnAfterPossibleChange();
|
| -}
|
| -
|
| -void TextField::Edit::OnChar(TCHAR ch, UINT repeat_count, UINT flags) {
|
| - HandleKeystroke(GetCurrentMessage()->message, ch, repeat_count, flags);
|
| -}
|
| -
|
| -void TextField::Edit::OnContextMenu(HWND window, const CPoint& point) {
|
| - CPoint p(point);
|
| - if (point.x == -1 || point.y == -1) {
|
| - GetCaretPos(&p);
|
| - MapWindowPoints(HWND_DESKTOP, &p, 1);
|
| - }
|
| - context_menu_->RunMenuAt(p.x, p.y);
|
| -}
|
| -
|
| -void TextField::Edit::OnCopy() {
|
| - if (parent_->IsPassword())
|
| - return;
|
| -
|
| - const std::wstring text(GetSelectedText());
|
| -
|
| - if (!text.empty() && ViewsDelegate::views_delegate) {
|
| - ScopedClipboardWriter scw(ViewsDelegate::views_delegate->GetClipboard());
|
| - scw.WriteText(text);
|
| - }
|
| -}
|
| -
|
| -void TextField::Edit::OnCut() {
|
| - if (parent_->IsReadOnly() || parent_->IsPassword())
|
| - return;
|
| -
|
| - OnCopy();
|
| -
|
| - // This replace selection will have no effect (even on the undo stack) if the
|
| - // current selection is empty.
|
| - ReplaceSel(L"", true);
|
| -}
|
| -
|
| -LRESULT TextField::Edit::OnImeChar(UINT message, WPARAM wparam, LPARAM lparam) {
|
| - // http://crbug.com/7707: a rich-edit control may crash when it receives a
|
| - // WM_IME_CHAR message while it is processing a WM_IME_COMPOSITION message.
|
| - // Since view controls don't need WM_IME_CHAR messages, we prevent WM_IME_CHAR
|
| - // messages from being dispatched to view controls via the CallWindowProc()
|
| - // call.
|
| - return 0;
|
| -}
|
| -
|
| -LRESULT TextField::Edit::OnImeStartComposition(UINT message,
|
| - WPARAM wparam,
|
| - LPARAM lparam) {
|
| - // Users may press alt+shift or control+shift keys to change their keyboard
|
| - // layouts. So, we retrieve the input locale identifier everytime we start
|
| - // an IME composition.
|
| - int language_id = PRIMARYLANGID(GetKeyboardLayout(0));
|
| - ime_discard_composition_ =
|
| - language_id == LANG_JAPANESE || language_id == LANG_CHINESE;
|
| - ime_composition_start_ = 0;
|
| - ime_composition_length_ = 0;
|
| -
|
| - return DefWindowProc(message, wparam, lparam);
|
| -}
|
| -
|
| -LRESULT TextField::Edit::OnImeComposition(UINT message,
|
| - WPARAM wparam,
|
| - LPARAM lparam) {
|
| - text_before_change_.clear();
|
| - LRESULT result = DefWindowProc(message, wparam, lparam);
|
| -
|
| - ime_composition_start_ = 0;
|
| - ime_composition_length_ = 0;
|
| - if (ime_discard_composition_) {
|
| - // Call IMM32 functions to retrieve the position and the length of the
|
| - // ongoing composition string and notify the OnAfterPossibleChange()
|
| - // function that it should discard the composition string from a search
|
| - // string. We should not call IMM32 functions in the function because it
|
| - // is called when an IME is not composing a string.
|
| - HIMC imm_context = ImmGetContext(m_hWnd);
|
| - if (imm_context) {
|
| - CHARRANGE selection;
|
| - GetSel(selection);
|
| - const int cursor_position =
|
| - ImmGetCompositionString(imm_context, GCS_CURSORPOS, NULL, 0);
|
| - if (cursor_position >= 0)
|
| - ime_composition_start_ = selection.cpMin - cursor_position;
|
| -
|
| - const int composition_size =
|
| - ImmGetCompositionString(imm_context, GCS_COMPSTR, NULL, 0);
|
| - if (composition_size >= 0)
|
| - ime_composition_length_ = composition_size / sizeof(wchar_t);
|
| -
|
| - ImmReleaseContext(m_hWnd, imm_context);
|
| - }
|
| - }
|
| -
|
| - OnAfterPossibleChange();
|
| - return result;
|
| -}
|
| -
|
| -LRESULT TextField::Edit::OnImeEndComposition(UINT message,
|
| - WPARAM wparam,
|
| - LPARAM lparam) {
|
| - // Bug 11863: Korean IMEs send a WM_IME_ENDCOMPOSITION message without
|
| - // sending any WM_IME_COMPOSITION messages when a user deletes all
|
| - // composition characters, i.e. a composition string becomes empty. To handle
|
| - // this case, we need to update the find results when a composition is
|
| - // finished or canceled.
|
| - parent_->SyncText();
|
| - if (parent_->GetController())
|
| - parent_->GetController()->ContentsChanged(parent_, GetText());
|
| - return DefWindowProc(message, wparam, lparam);
|
| -}
|
| -
|
| -void TextField::Edit::OnKeyDown(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.
|
| -
|
| - switch (key) {
|
| - case VK_RETURN:
|
| - // If we are multi-line, we want to let returns through so they start a
|
| - // new line.
|
| - if (parent_->IsMultiLine())
|
| - break;
|
| - else
|
| - return;
|
| - // 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.
|
| - // 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:
|
| - case 'X':
|
| - if ((flags & KF_ALTDOWN) ||
|
| - (GetKeyState((key == 'X') ? VK_CONTROL : VK_SHIFT) >= 0))
|
| - break;
|
| - if (GetKeyState((key == 'X') ? VK_SHIFT : VK_CONTROL) >= 0) {
|
| - ScopedFreeze freeze(this, GetTextObjectModel());
|
| - OnBeforePossibleChange();
|
| - Cut();
|
| - OnAfterPossibleChange();
|
| - }
|
| - return;
|
| -
|
| - case 'C':
|
| - if ((flags & KF_ALTDOWN) || (GetKeyState(VK_CONTROL) >= 0))
|
| - break;
|
| - if (GetKeyState(VK_SHIFT) >= 0)
|
| - Copy();
|
| - return;
|
| -
|
| - case VK_INSERT:
|
| - case 'V':
|
| - if ((flags & KF_ALTDOWN) ||
|
| - (GetKeyState((key == 'V') ? VK_CONTROL : VK_SHIFT) >= 0))
|
| - break;
|
| - if (GetKeyState((key == 'V') ? VK_SHIFT : VK_CONTROL) >= 0) {
|
| - ScopedFreeze freeze(this, GetTextObjectModel());
|
| - OnBeforePossibleChange();
|
| - Paste();
|
| - OnAfterPossibleChange();
|
| - }
|
| - return;
|
| -
|
| - case 0xbb: // Ctrl-'='. Triggers subscripting, even in plain text mode.
|
| - return;
|
| -
|
| - case VK_PROCESSKEY:
|
| - // This key event is consumed by an IME.
|
| - // We ignore this event because an IME sends WM_IME_COMPOSITION messages
|
| - // when it updates the CRichEditCtrl text.
|
| - 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 TextField::Edit::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;
|
| -
|
| - ScopedFreeze freeze(this, GetTextObjectModel());
|
| - OnBeforePossibleChange();
|
| - DefWindowProc(WM_LBUTTONDBLCLK, keys,
|
| - MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y));
|
| - OnAfterPossibleChange();
|
| -}
|
| -
|
| -void TextField::Edit::OnLButtonDown(UINT keys, const CPoint& point) {
|
| - // 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;
|
| -
|
| - ScopedFreeze freeze(this, GetTextObjectModel());
|
| - OnBeforePossibleChange();
|
| - DefWindowProc(WM_LBUTTONDOWN, keys,
|
| - MAKELPARAM(ClipXCoordToVisibleText(point.x, is_triple_click),
|
| - point.y));
|
| - OnAfterPossibleChange();
|
| -}
|
| -
|
| -void TextField::Edit::OnLButtonUp(UINT keys, const CPoint& point) {
|
| - ScopedFreeze freeze(this, GetTextObjectModel());
|
| - OnBeforePossibleChange();
|
| - DefWindowProc(WM_LBUTTONUP, keys,
|
| - MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y));
|
| - OnAfterPossibleChange();
|
| -}
|
| -
|
| -void TextField::Edit::OnMouseLeave() {
|
| - SetContainsMouse(false);
|
| -}
|
| -
|
| -LRESULT TextField::Edit::OnMouseWheel(UINT message,
|
| - WPARAM w_param, LPARAM l_param) {
|
| - // Reroute the mouse-wheel to the window under the mouse pointer if
|
| - // applicable.
|
| - if (views::RerouteMouseWheel(m_hWnd, w_param, l_param))
|
| - return 0;
|
| - return DefWindowProc(message, w_param, l_param);;
|
| -}
|
| -
|
| -void TextField::Edit::OnMouseMove(UINT keys, const CPoint& point) {
|
| - SetContainsMouse(true);
|
| - // 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();
|
| - }
|
| -}
|
| -
|
| -int TextField::Edit::OnNCCalcSize(BOOL w_param, LPARAM l_param) {
|
| - content_insets_.Set(0, 0, 0, 0);
|
| - parent_->CalculateInsets(&content_insets_);
|
| - if (w_param) {
|
| - NCCALCSIZE_PARAMS* nc_params =
|
| - reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param);
|
| - nc_params->rgrc[0].left += content_insets_.left();
|
| - nc_params->rgrc[0].right -= content_insets_.right();
|
| - nc_params->rgrc[0].top += content_insets_.top();
|
| - nc_params->rgrc[0].bottom -= content_insets_.bottom();
|
| - } else {
|
| - RECT* rect = reinterpret_cast<RECT*>(l_param);
|
| - rect->left += content_insets_.left();
|
| - rect->right -= content_insets_.right();
|
| - rect->top += content_insets_.top();
|
| - rect->bottom -= content_insets_.bottom();
|
| - }
|
| - return 0;
|
| -}
|
| -
|
| -void TextField::Edit::OnNCPaint(HRGN region) {
|
| - if (!draw_border_)
|
| - return;
|
| -
|
| - HDC hdc = GetWindowDC();
|
| -
|
| - CRect window_rect;
|
| - GetWindowRect(&window_rect);
|
| - // Convert to be relative to 0x0.
|
| - window_rect.MoveToXY(0, 0);
|
| -
|
| - ExcludeClipRect(hdc,
|
| - window_rect.left + content_insets_.left(),
|
| - window_rect.top + content_insets_.top(),
|
| - window_rect.right - content_insets_.right(),
|
| - window_rect.bottom - content_insets_.bottom());
|
| -
|
| - HBRUSH brush = CreateSolidBrush(bg_color_);
|
| - FillRect(hdc, &window_rect, brush);
|
| - DeleteObject(brush);
|
| -
|
| - int part;
|
| - int state;
|
| -
|
| - if (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) {
|
| - part = EP_EDITTEXT;
|
| -
|
| - if (!parent_->IsEnabled())
|
| - state = ETS_DISABLED;
|
| - else if (parent_->IsReadOnly())
|
| - state = ETS_READONLY;
|
| - else if (!contains_mouse_)
|
| - state = ETS_NORMAL;
|
| - else
|
| - state = ETS_HOT;
|
| - } else {
|
| - part = EP_EDITBORDER_HVSCROLL;
|
| -
|
| - if (!parent_->IsEnabled())
|
| - state = EPSHV_DISABLED;
|
| - else if (GetFocus() == m_hWnd)
|
| - state = EPSHV_FOCUSED;
|
| - else if (contains_mouse_)
|
| - state = EPSHV_HOT;
|
| - else
|
| - state = EPSHV_NORMAL;
|
| - // Vista doesn't appear to have a unique state for readonly.
|
| - }
|
| -
|
| - int classic_state =
|
| - (!parent_->IsEnabled() || parent_->IsReadOnly()) ? DFCS_INACTIVE : 0;
|
| -
|
| - NativeTheme::instance()->PaintTextField(hdc, part, state, classic_state,
|
| - &window_rect, bg_color_, false,
|
| - true);
|
| -
|
| - // NOTE: I tried checking the transparent property of the theme and invoking
|
| - // drawParentBackground, but it didn't seem to make a difference.
|
| -
|
| - ReleaseDC(hdc);
|
| -}
|
| -
|
| -void TextField::Edit::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;
|
| - SetMsgHandled(false);
|
| -}
|
| -
|
| -void TextField::Edit::OnPaste() {
|
| - if (parent_->IsReadOnly() || !ViewsDelegate::views_delegate)
|
| - return;
|
| -
|
| - Clipboard* clipboard = ViewsDelegate::views_delegate->GetClipboard();
|
| -
|
| - if (!clipboard->IsFormatAvailable(Clipboard::GetPlainTextWFormatType()))
|
| - return;
|
| -
|
| - std::wstring clipboard_str;
|
| - clipboard->ReadText(&clipboard_str);
|
| - if (!clipboard_str.empty()) {
|
| - std::wstring collapsed(CollapseWhitespace(clipboard_str, false));
|
| - if (parent_->GetStyle() & STYLE_LOWERCASE)
|
| - collapsed = l10n_util::ToLower(collapsed);
|
| - // Force a Paste operation to trigger OnContentsChanged, even if identical
|
| - // contents are pasted into the text box.
|
| - text_before_change_.clear();
|
| - ReplaceSel(collapsed.c_str(), true);
|
| - }
|
| -}
|
| -
|
| -void TextField::Edit::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 TextField::Edit::HandleKeystroke(UINT message,
|
| - TCHAR key,
|
| - UINT repeat_count,
|
| - UINT flags) {
|
| - ScopedFreeze freeze(this, GetTextObjectModel());
|
| -
|
| - TextField::Controller* controller = parent_->GetController();
|
| - bool handled = false;
|
| - if (controller) {
|
| - handled = controller->HandleKeystroke(parent_,
|
| - TextField::Keystroke(message, key, repeat_count, flags));
|
| - }
|
| -
|
| - if (!handled) {
|
| - OnBeforePossibleChange();
|
| - DefWindowProc(message, key, MAKELPARAM(repeat_count, flags));
|
| - OnAfterPossibleChange();
|
| - }
|
| -}
|
| -
|
| -void TextField::Edit::OnBeforePossibleChange() {
|
| - // Record our state.
|
| - text_before_change_ = GetText();
|
| -}
|
| -
|
| -void TextField::Edit::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;
|
| - GetSel(new_sel);
|
| - const int length = GetTextLength();
|
| - if (new_sel.cpMax > length) {
|
| - new_sel.cpMax = length;
|
| - if (new_sel.cpMin > length)
|
| - new_sel.cpMin = length;
|
| - SetSel(new_sel);
|
| - }
|
| -
|
| - std::wstring new_text(GetText());
|
| - if (new_text != text_before_change_) {
|
| - if (ime_discard_composition_ && ime_composition_start_ >= 0 &&
|
| - ime_composition_length_ > 0) {
|
| - // A string retrieved with a GetText() call contains a string being
|
| - // composed by an IME. We remove the composition string from this search
|
| - // string.
|
| - new_text.erase(ime_composition_start_, ime_composition_length_);
|
| - ime_composition_start_ = 0;
|
| - ime_composition_length_ = 0;
|
| - if (new_text.empty())
|
| - return;
|
| - }
|
| - parent_->SyncText();
|
| - if (parent_->GetController())
|
| - parent_->GetController()->ContentsChanged(parent_, new_text);
|
| - }
|
| -}
|
| -
|
| -LONG TextField::Edit::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.
|
| - PARAFORMAT2 pf2;
|
| - GetParaFormat(pf2);
|
| - // Calculation of the clipped coordinate is more complicated if the paragraph
|
| - // layout is RTL layout, or if there is RTL characters inside the LTR layout
|
| - // paragraph.
|
| - bool ltr_text_in_ltr_layout = true;
|
| - if ((pf2.wEffects & PFE_RTLPARA) ||
|
| - l10n_util::StringContainsStrongRTLChars(GetText())) {
|
| - ltr_text_in_ltr_layout = false;
|
| - }
|
| - const int length = GetTextLength();
|
| - RECT r;
|
| - GetRect(&r);
|
| - // The values returned by PosFromChar() seem to refer always
|
| - // to the left edge of the character's bounding box.
|
| - const LONG first_position_x = PosFromChar(0).x;
|
| - LONG min_x = first_position_x;
|
| - if (!ltr_text_in_ltr_layout) {
|
| - for (int i = 1; i < length; ++i)
|
| - min_x = std::min(min_x, PosFromChar(i).x);
|
| - }
|
| - const LONG left_bound = std::max(r.left, min_x);
|
| -
|
| - // PosFromChar(length) is a phantom character past the end of the text. It is
|
| - // not necessarily a right bound; in RTL controls it may be a left bound. So
|
| - // treat it as a right bound only if it is to the right of the first
|
| - // character.
|
| - LONG right_bound = r.right;
|
| - LONG end_position_x = PosFromChar(length).x;
|
| - if (end_position_x >= first_position_x) {
|
| - right_bound = std::min(right_bound, end_position_x); // LTR case.
|
| - }
|
| - // 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.
|
| - if (x < left_bound) {
|
| - return (is_triple_click && ltr_text_in_ltr_layout) ? left_bound - 1 :
|
| - left_bound;
|
| - }
|
| - if ((length == 0) || (x < right_bound))
|
| - return x;
|
| - return is_triple_click ? (right_bound - 1) : right_bound;
|
| -}
|
| -
|
| -void TextField::Edit::SetContainsMouse(bool contains_mouse) {
|
| - if (contains_mouse == contains_mouse_)
|
| - return;
|
| -
|
| - contains_mouse_ = contains_mouse;
|
| -
|
| - if (!draw_border_)
|
| - return;
|
| -
|
| - if (contains_mouse_) {
|
| - // Register for notification when the mouse leaves. Need to do this so
|
| - // that we can reset contains mouse properly.
|
| - TRACKMOUSEEVENT tme;
|
| - tme.cbSize = sizeof(tme);
|
| - tme.dwFlags = TME_LEAVE;
|
| - tme.hwndTrack = m_hWnd;
|
| - tme.dwHoverTime = 0;
|
| - TrackMouseEvent(&tme);
|
| - }
|
| - RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_FRAME);
|
| -}
|
| -
|
| -ITextDocument* TextField::Edit::GetTextObjectModel() const {
|
| - if (!text_object_model_) {
|
| - CComPtr<IRichEditOle> ole_interface;
|
| - ole_interface.Attach(GetOleInterface());
|
| - text_object_model_ = ole_interface;
|
| - }
|
| - return text_object_model_;
|
| -}
|
| -
|
| -/////////////////////////////////////////////////////////////////////////////
|
| -// TextField
|
| -
|
| -TextField::~TextField() {
|
| - if (edit_) {
|
| - // If the edit hwnd still exists, we need to destroy it explicitly.
|
| - if (*edit_)
|
| - edit_->DestroyWindow();
|
| - delete edit_;
|
| - }
|
| -}
|
| -
|
| -void TextField::ViewHierarchyChanged(bool is_add, View* parent, View* child) {
|
| - Widget* widget;
|
| -
|
| - if (is_add && (widget = GetWidget())) {
|
| - // This notification is called from the AddChildView call below. Ignore it.
|
| - if (native_view_ && !edit_)
|
| - return;
|
| -
|
| - if (!native_view_) {
|
| - native_view_ = new HWNDView(); // Deleted from our superclass destructor
|
| - AddChildView(native_view_);
|
| -
|
| - // Maps the focus of the native control to the focus of this view.
|
| - native_view_->SetAssociatedFocusView(this);
|
| - }
|
| -
|
| - // If edit_ is invalid from a previous use. Reset it.
|
| - if (edit_ && !IsWindow(edit_->m_hWnd)) {
|
| - native_view_->Detach();
|
| - delete edit_;
|
| - edit_ = NULL;
|
| - }
|
| -
|
| - if (!edit_) {
|
| - edit_ = new Edit(this, draw_border_);
|
| - edit_->SetFont(font_.hfont());
|
| - native_view_->Attach(*edit_);
|
| - if (!text_.empty())
|
| - edit_->SetText(text_);
|
| - UpdateEditBackgroundColor();
|
| - Layout();
|
| - }
|
| - } else if (!is_add && edit_ && IsWindow(edit_->m_hWnd)) {
|
| - edit_->SetParent(NULL);
|
| - }
|
| -}
|
| -
|
| -void TextField::Layout() {
|
| - if (native_view_) {
|
| - native_view_->SetBounds(GetLocalBounds(true));
|
| - native_view_->Layout();
|
| - }
|
| -}
|
| -
|
| -gfx::Size TextField::GetPreferredSize() {
|
| - gfx::Insets insets;
|
| - CalculateInsets(&insets);
|
| - return gfx::Size(font_.GetExpectedTextWidth(default_width_in_chars_) +
|
| - insets.width(),
|
| - num_lines_ * font_.height() + insets.height());
|
| -}
|
| -
|
| -std::wstring TextField::GetText() const {
|
| - return text_;
|
| -}
|
| -
|
| -void TextField::SetText(const std::wstring& text) {
|
| - text_ = text;
|
| - if (edit_)
|
| - edit_->SetText(text);
|
| -}
|
| -
|
| -void TextField::AppendText(const std::wstring& text) {
|
| - text_ += text;
|
| - if (edit_)
|
| - edit_->AppendText(text);
|
| -}
|
| -
|
| -void TextField::CalculateInsets(gfx::Insets* insets) {
|
| - DCHECK(insets);
|
| -
|
| - if (!draw_border_)
|
| - return;
|
| -
|
| - // NOTE: One would think GetThemeMargins would return the insets we should
|
| - // use, but it doesn't. The margins returned by GetThemeMargins are always
|
| - // 0.
|
| -
|
| - // This appears to be the insets used by Windows.
|
| - insets->Set(3, 3, 3, 3);
|
| -}
|
| -
|
| -void TextField::SyncText() {
|
| - if (edit_)
|
| - text_ = edit_->GetText();
|
| -}
|
| -
|
| -void TextField::SetController(Controller* controller) {
|
| - controller_ = controller;
|
| -}
|
| -
|
| -TextField::Controller* TextField::GetController() const {
|
| - return controller_;
|
| -}
|
| -
|
| -bool TextField::IsReadOnly() const {
|
| - return edit_ ? ((edit_->GetStyle() & ES_READONLY) != 0) : read_only_;
|
| -}
|
| -
|
| -bool TextField::IsPassword() const {
|
| - return GetStyle() & TextField::STYLE_PASSWORD;
|
| -}
|
| -
|
| -bool TextField::IsMultiLine() const {
|
| - return (style_ & STYLE_MULTILINE) != 0;
|
| -}
|
| -
|
| -void TextField::SetReadOnly(bool read_only) {
|
| - read_only_ = read_only;
|
| - if (edit_) {
|
| - edit_->SetReadOnly(read_only);
|
| - UpdateEditBackgroundColor();
|
| - }
|
| -}
|
| -
|
| -void TextField::Focus() {
|
| - ::SetFocus(native_view_->GetHWND());
|
| -}
|
| -
|
| -void TextField::SelectAll() {
|
| - if (edit_)
|
| - edit_->SelectAll();
|
| -}
|
| -
|
| -void TextField::ClearSelection() const {
|
| - if (edit_)
|
| - edit_->ClearSelection();
|
| -}
|
| -
|
| -HWND TextField::GetNativeComponent() {
|
| - return native_view_->GetHWND();
|
| -}
|
| -
|
| -void TextField::SetBackgroundColor(SkColor color) {
|
| - background_color_ = color;
|
| - use_default_background_color_ = false;
|
| - UpdateEditBackgroundColor();
|
| -}
|
| -
|
| -void TextField::SetDefaultBackgroundColor() {
|
| - use_default_background_color_ = true;
|
| - UpdateEditBackgroundColor();
|
| -}
|
| -
|
| -void TextField::SetFont(const gfx::Font& font) {
|
| - font_ = font;
|
| - if (edit_)
|
| - edit_->SetFont(font.hfont());
|
| -}
|
| -
|
| -gfx::Font TextField::GetFont() const {
|
| - return font_;
|
| -}
|
| -
|
| -bool TextField::SetHorizontalMargins(int left, int right) {
|
| - // SendMessage expects the two values to be packed into one using MAKELONG
|
| - // so we truncate to 16 bits if necessary.
|
| - return ERROR_SUCCESS == SendMessage(GetNativeComponent(),
|
| - (UINT) EM_SETMARGINS,
|
| - (WPARAM) EC_LEFTMARGIN | EC_RIGHTMARGIN,
|
| - (LPARAM) MAKELONG(left & 0xFFFF,
|
| - right & 0xFFFF));
|
| -}
|
| -
|
| -void TextField::SetHeightInLines(int num_lines) {
|
| - DCHECK(IsMultiLine());
|
| - num_lines_ = num_lines;
|
| -}
|
| -
|
| -void TextField::RemoveBorder() {
|
| - if (!draw_border_)
|
| - return;
|
| -
|
| - draw_border_ = false;
|
| - if (edit_)
|
| - edit_->RemoveBorder();
|
| -}
|
| -
|
| -void TextField::SetEnabled(bool enabled) {
|
| - View::SetEnabled(enabled);
|
| - edit_->SetEnabled(enabled);
|
| -}
|
| -
|
| -bool TextField::IsFocusable() const {
|
| - return IsEnabled() && !IsReadOnly();
|
| -}
|
| -
|
| -void TextField::AboutToRequestFocusFromTabTraversal(bool reverse) {
|
| - SelectAll();
|
| -}
|
| -
|
| -bool TextField::SkipDefaultKeyEventProcessing(const KeyEvent& e) {
|
| - // TODO(hamaji): Figure out which keyboard combinations we need to add here,
|
| - // similar to LocationBarView::SkipDefaultKeyEventProcessing.
|
| - if (e.GetCharacter() == VK_BACK)
|
| - return true; // We'll handle BackSpace ourselves.
|
| -
|
| - // We don't translate accelerators for ALT + NumPad digit, they are used for
|
| - // entering special characters.
|
| - if (e.IsAltDown() &&
|
| - win_util::IsNumPadDigit(e.GetCharacter(), e.IsExtendedKey()))
|
| - return true;
|
| -
|
| - return false;
|
| -}
|
| -
|
| -void TextField::UpdateEditBackgroundColor() {
|
| - if (!edit_)
|
| - return;
|
| -
|
| - COLORREF bg_color;
|
| - if (!use_default_background_color_)
|
| - bg_color = skia::SkColorToCOLORREF(background_color_);
|
| - else
|
| - bg_color = GetSysColor(read_only_ ? COLOR_3DFACE : COLOR_WINDOW);
|
| - edit_->SetBackgroundColor(bg_color);
|
| -}
|
| -
|
| -} // namespace views
|
|
|