- add sources.
[platform/framework/web/crosswalk.git] / src / ui / views / controls / textfield / native_textfield_win.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/views/controls/textfield/native_textfield_win.h"
6
7 #include <algorithm>
8
9 #include "base/i18n/case_conversion.h"
10 #include "base/i18n/rtl.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/win/metro.h"
14 #include "base/win/windows_version.h"
15 #include "grit/ui_strings.h"
16 #include "skia/ext/skia_utils_win.h"
17 #include "ui/base/accessibility/accessible_view_state.h"
18 #include "ui/base/clipboard/clipboard.h"
19 #include "ui/base/clipboard/scoped_clipboard_writer.h"
20 #include "ui/base/ime/win/tsf_bridge.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/l10n/l10n_util_win.h"
23 #include "ui/base/win/mouse_wheel_util.h"
24 #include "ui/events/event.h"
25 #include "ui/events/keycodes/keyboard_codes.h"
26 #include "ui/gfx/range/range.h"
27 #include "ui/gfx/win/hwnd_util.h"
28 #include "ui/native_theme/native_theme_win.h"
29 #include "ui/views/controls/label.h"
30 #include "ui/views/controls/menu/menu_item_view.h"
31 #include "ui/views/controls/menu/menu_model_adapter.h"
32 #include "ui/views/controls/menu/menu_runner.h"
33 #include "ui/views/controls/native/native_view_host.h"
34 #include "ui/views/controls/textfield/textfield.h"
35 #include "ui/views/controls/textfield/textfield_controller.h"
36 #include "ui/views/focus/focus_manager.h"
37 #include "ui/views/metrics.h"
38 #include "ui/views/widget/widget.h"
39
40 namespace views {
41
42 ///////////////////////////////////////////////////////////////////////////////
43 // Helper classes
44
45 NativeTextfieldWin::ScopedFreeze::ScopedFreeze(NativeTextfieldWin* edit,
46                                                ITextDocument* text_object_model)
47     : edit_(edit),
48       text_object_model_(text_object_model) {
49   // Freeze the screen.
50   if (text_object_model_) {
51     long count;
52     text_object_model_->Freeze(&count);
53   }
54 }
55
56 NativeTextfieldWin::ScopedFreeze::~ScopedFreeze() {
57   // Unfreeze the screen.
58   if (text_object_model_) {
59     long count;
60     text_object_model_->Unfreeze(&count);
61     if (count == 0) {
62       // We need to UpdateWindow() here instead of InvalidateRect() because, as
63       // far as I can tell, the edit likes to synchronously erase its background
64       // when unfreezing, thus requiring us to synchronously redraw if we don't
65       // want flicker.
66       edit_->UpdateWindow();
67     }
68   }
69 }
70
71 NativeTextfieldWin::ScopedSuspendUndo::ScopedSuspendUndo(
72     ITextDocument* text_object_model)
73     : text_object_model_(text_object_model) {
74   // Suspend Undo processing.
75   if (text_object_model_)
76     text_object_model_->Undo(tomSuspend, NULL);
77 }
78
79 NativeTextfieldWin::ScopedSuspendUndo::~ScopedSuspendUndo() {
80   // Resume Undo processing.
81   if (text_object_model_)
82     text_object_model_->Undo(tomResume, NULL);
83 }
84
85 ///////////////////////////////////////////////////////////////////////////////
86 // NativeTextfieldWin
87
88 HMODULE NativeTextfieldWin::loaded_libarary_module_ = false;
89
90 NativeTextfieldWin::NativeTextfieldWin(Textfield* textfield)
91     : textfield_(textfield),
92       tracking_double_click_(false),
93       double_click_time_(0),
94       can_discard_mousemove_(false),
95       contains_mouse_(false),
96       ime_discard_composition_(false),
97       ime_composition_start_(0),
98       ime_composition_length_(0),
99       container_view_(new NativeViewHost),
100       bg_color_(0),
101       tsf_event_router_(base::win::IsTSFAwareRequired() ?
102           new ui::TSFEventRouter(this) : NULL) {
103   if (!loaded_libarary_module_) {
104     // msftedit.dll is RichEdit ver 4.1.
105     // This version is available from WinXP SP1 and has TSF support.
106     loaded_libarary_module_ = LoadLibrary(L"msftedit.dll");
107   }
108
109   DWORD style = kDefaultEditStyle | ES_AUTOHSCROLL;
110   if (textfield_->style() & Textfield::STYLE_OBSCURED)
111     style |= ES_PASSWORD;
112
113   if (textfield_->read_only())
114     style |= ES_READONLY;
115
116   // Make sure we apply RTL related extended window styles if necessary.
117   DWORD ex_style = l10n_util::GetExtendedStyles();
118
119   RECT r = {0, 0, textfield_->width(), textfield_->height()};
120   Create(textfield_->GetWidget()->GetNativeView(), r, NULL, style, ex_style);
121
122   if (textfield_->style() & Textfield::STYLE_LOWERCASE) {
123     DCHECK((textfield_->style() & Textfield::STYLE_OBSCURED) == 0);
124     SetEditStyle(SES_LOWERCASE, SES_LOWERCASE);
125   }
126
127   // Disable auto font changing. Otherwise, characters can be rendered with
128   // multiple fonts. See http://crbug.com/168480 for details.
129   const LRESULT lang_option = SendMessage(m_hWnd, EM_GETLANGOPTIONS, 0, 0);
130   SendMessage(EM_SETLANGOPTIONS, 0, lang_option & ~IMF_AUTOFONT);
131
132   // Set up the text_object_model_.
133   base::win::ScopedComPtr<IRichEditOle, &IID_IRichEditOle> ole_interface;
134   ole_interface.Attach(GetOleInterface());
135   if (ole_interface)
136     text_object_model_.QueryFrom(ole_interface);
137
138   InitializeAccessibilityInfo();
139 }
140
141 NativeTextfieldWin::~NativeTextfieldWin() {
142   if (IsWindow())
143     DestroyWindow();
144 }
145
146 // static
147 bool NativeTextfieldWin::IsDoubleClick(const POINT& origin,
148                                        const POINT& current,
149                                        DWORD elapsed_time) {
150   // The CXDOUBLECLK and CYDOUBLECLK system metrics describe the width and
151   // height of a rectangle around the origin position, inside of which clicks
152   // within the double click time are considered double clicks.
153   return (elapsed_time <= GetDoubleClickTime()) &&
154       (abs(current.x - origin.x) <= (GetSystemMetrics(SM_CXDOUBLECLK) / 2)) &&
155       (abs(current.y - origin.y) <= (GetSystemMetrics(SM_CYDOUBLECLK) / 2));
156 }
157
158 void NativeTextfieldWin::AttachHack() {
159   // See the code in textfield.cc that calls this for why this is here.
160   container_view_->set_focus_view(textfield_);
161   container_view_->Attach(m_hWnd);
162 }
163
164 ////////////////////////////////////////////////////////////////////////////////
165 // NativeTextfieldWin, NativeTextfieldWrapper implementation:
166
167 string16 NativeTextfieldWin::GetText() const {
168   int len = GetTextLength() + 1;
169   string16 str;
170   if (len > 1)
171     GetWindowText(WriteInto(&str, len), len);
172   // The text get from GetWindowText() might be wrapped with explicit bidi
173   // control characters. Refer to UpdateText() for detail. Without such
174   // wrapping, in RTL chrome, a pure LTR string ending with parenthesis will
175   // not be displayed correctly in a textfield. For example, "Yahoo!" will be
176   // displayed as "!Yahoo", and "Google (by default)" will be displayed as
177   // "(Google (by default".
178   return base::i18n::StripWrappingBidiControlCharacters(str);
179 }
180
181 void NativeTextfieldWin::UpdateText() {
182   string16 text = textfield_->text();
183   // Adjusting the string direction before setting the text in order to make
184   // sure both RTL and LTR strings are displayed properly.
185   base::i18n::AdjustStringForLocaleDirection(&text);
186   if (textfield_->style() & Textfield::STYLE_LOWERCASE)
187     text = base::i18n::ToLower(text);
188   SetWindowText(text.c_str());
189   UpdateAccessibleValue(text);
190 }
191
192 void NativeTextfieldWin::AppendText(const string16& text) {
193   int text_length = GetWindowTextLength();
194   ::SendMessage(m_hWnd, TBM_SETSEL, true, MAKELPARAM(text_length, text_length));
195   ::SendMessage(m_hWnd, EM_REPLACESEL, false,
196                 reinterpret_cast<LPARAM>(text.c_str()));
197 }
198
199 void NativeTextfieldWin::InsertOrReplaceText(const string16& text) {
200   // Currently not needed.
201   NOTIMPLEMENTED();
202 }
203
204 base::i18n::TextDirection NativeTextfieldWin::GetTextDirection() const {
205   NOTIMPLEMENTED();
206   return base::i18n::UNKNOWN_DIRECTION;
207 }
208
209 string16 NativeTextfieldWin::GetSelectedText() const {
210   CHARRANGE sel;
211   GetSel(sel);
212   string16 str;
213   if (sel.cpMin != sel.cpMax)
214     GetSelText(WriteInto(&str, sel.cpMax - sel.cpMin + 1));
215   return str;
216 }
217
218 void NativeTextfieldWin::SelectAll(bool reversed) {
219   if (reversed)
220     SetSel(GetTextLength(), 0);
221   else
222     SetSel(0, GetTextLength());
223 }
224
225 void NativeTextfieldWin::ClearSelection() {
226   SetSel(GetTextLength(), GetTextLength());
227 }
228
229 void NativeTextfieldWin::UpdateBorder() {
230   SetWindowPos(NULL, 0, 0, 0, 0,
231                SWP_NOMOVE | SWP_FRAMECHANGED | SWP_NOACTIVATE |
232                SWP_NOOWNERZORDER | SWP_NOSIZE);
233 }
234
235 void NativeTextfieldWin::UpdateTextColor() {
236   CHARFORMAT cf = {0};
237   cf.dwMask = CFM_COLOR;
238   cf.crTextColor = skia::SkColorToCOLORREF(textfield_->GetTextColor());
239   CRichEditCtrl::SetDefaultCharFormat(cf);
240 }
241
242 void NativeTextfieldWin::UpdateBackgroundColor() {
243   CRichEditCtrl::SetBackgroundColor(
244       skia::SkColorToCOLORREF(textfield_->GetBackgroundColor()));
245 }
246
247 void NativeTextfieldWin::UpdateReadOnly() {
248   SendMessage(m_hWnd, EM_SETREADONLY, textfield_->read_only(), 0);
249   UpdateAccessibleState(STATE_SYSTEM_READONLY, textfield_->read_only());
250 }
251
252 void NativeTextfieldWin::UpdateFont() {
253   SendMessage(m_hWnd, WM_SETFONT,
254               reinterpret_cast<WPARAM>(
255                   textfield_->GetPrimaryFont().GetNativeFont()),
256               TRUE);
257   // Setting the font blows away any text color we've set, so reset it.
258   UpdateTextColor();
259 }
260
261 void NativeTextfieldWin::UpdateIsObscured() {
262   // TODO: Need to implement for Windows.
263   UpdateAccessibleState(STATE_SYSTEM_PROTECTED, textfield_->IsObscured());
264 }
265
266 void NativeTextfieldWin::UpdateEnabled() {
267   SendMessage(m_hWnd, WM_ENABLE, textfield_->enabled(), 0);
268   UpdateAccessibleState(STATE_SYSTEM_UNAVAILABLE, !textfield_->enabled());
269 }
270
271 gfx::Insets NativeTextfieldWin::CalculateInsets() {
272   // NOTE: One would think GetThemeMargins would return the insets we should
273   // use, but it doesn't. The margins returned by GetThemeMargins are always
274   // 0.
275
276   // This appears to be the insets used by Windows.
277   return gfx::Insets(3, 3, 3, 3);
278 }
279
280 void NativeTextfieldWin::UpdateHorizontalMargins() {
281   int left, right;
282   if (!textfield_->GetHorizontalMargins(&left, &right))
283     return;
284
285   // SendMessage expects the two values to be packed into one using MAKELONG
286   // so we truncate to 16 bits if necessary.
287   SendMessage(m_hWnd, EM_SETMARGINS,
288               EC_LEFTMARGIN | EC_RIGHTMARGIN,
289               MAKELONG(left  & 0xFFFF, right & 0xFFFF));
290 }
291
292 void NativeTextfieldWin::UpdateVerticalMargins() {
293   int top, bottom;
294   if (!textfield_->GetVerticalMargins(&top, &bottom))
295     return;
296
297   if (top == 0 && bottom == 0) {
298     // Do nothing, default margins are 0 already.
299     return;
300   }
301   // Non-zero margins case.
302   NOTIMPLEMENTED();
303 }
304
305 bool NativeTextfieldWin::SetFocus() {
306   // Focus the associated HWND.
307   //container_view_->Focus();
308   ::SetFocus(m_hWnd);
309   return true;
310 }
311
312 View* NativeTextfieldWin::GetView() {
313   return container_view_;
314 }
315
316 gfx::NativeView NativeTextfieldWin::GetTestingHandle() const {
317   return m_hWnd;
318 }
319
320 bool NativeTextfieldWin::IsIMEComposing() const {
321   // Retrieve the length of the composition string to check if an IME is
322   // composing text. (If this length is > 0 then an IME is being used to compose
323   // text.)
324   if (base::win::IsTSFAwareRequired())
325     return tsf_event_router_->IsImeComposing();
326
327   HIMC imm_context = ImmGetContext(m_hWnd);
328   if (!imm_context)
329     return false;
330
331   const int composition_size = ImmGetCompositionString(imm_context, GCS_COMPSTR,
332                                                        NULL, 0);
333   ImmReleaseContext(m_hWnd, imm_context);
334   return composition_size > 0;
335 }
336
337 gfx::Range NativeTextfieldWin::GetSelectedRange() const {
338   // TODO(tommi): Implement.
339   NOTIMPLEMENTED();
340   return gfx::Range();
341 }
342
343 void NativeTextfieldWin::SelectRange(const gfx::Range& range) {
344   // TODO(tommi): Implement.
345   NOTIMPLEMENTED();
346 }
347
348 gfx::SelectionModel NativeTextfieldWin::GetSelectionModel() const {
349   // TODO(tommi): Implement.
350   NOTIMPLEMENTED();
351   return gfx::SelectionModel();
352 }
353
354 void NativeTextfieldWin::SelectSelectionModel(const gfx::SelectionModel& sel) {
355   // TODO(tommi): Implement.
356   NOTIMPLEMENTED();
357 }
358
359 size_t NativeTextfieldWin::GetCursorPosition() const {
360   // TODO(tommi): Implement.
361   NOTIMPLEMENTED();
362   return 0U;
363 }
364
365 bool NativeTextfieldWin::GetCursorEnabled() const {
366   // TODO(msw): Implement.
367   NOTIMPLEMENTED();
368   return true;
369 }
370
371 void NativeTextfieldWin::SetCursorEnabled(bool enabled) {
372   // TODO(msw): Implement.
373   NOTIMPLEMENTED();
374 }
375
376 bool NativeTextfieldWin::HandleKeyPressed(const ui::KeyEvent& event) {
377   return false;
378 }
379
380 bool NativeTextfieldWin::HandleKeyReleased(const ui::KeyEvent& event) {
381   return false;
382 }
383
384 void NativeTextfieldWin::HandleFocus() {
385 }
386
387 void NativeTextfieldWin::HandleBlur() {
388 }
389
390 ui::TextInputClient* NativeTextfieldWin::GetTextInputClient() {
391   return NULL;
392 }
393
394 void NativeTextfieldWin::SetColor(SkColor value) {
395   NOTREACHED();
396 }
397
398 void NativeTextfieldWin::ApplyColor(SkColor value, const gfx::Range& range) {
399   NOTREACHED();
400 }
401
402 void NativeTextfieldWin::SetStyle(gfx::TextStyle style, bool value) {
403   NOTREACHED();
404 }
405
406 void NativeTextfieldWin::ApplyStyle(gfx::TextStyle style,
407                                     bool value,
408                                     const gfx::Range& range) {
409   NOTREACHED();
410 }
411
412 void NativeTextfieldWin::ClearEditHistory() {
413   NOTREACHED();
414 }
415
416 int NativeTextfieldWin::GetFontHeight() {
417   return textfield_->font_list().GetHeight();
418 }
419
420 int NativeTextfieldWin::GetTextfieldBaseline() const {
421   return textfield_->font_list().GetBaseline();
422 }
423
424 int NativeTextfieldWin::GetWidthNeededForText() const {
425   NOTIMPLEMENTED();
426   return 0;
427 }
428
429 void NativeTextfieldWin::ExecuteTextCommand(int command_id) {
430   ExecuteCommand(command_id, 0);
431 }
432
433 bool NativeTextfieldWin::HasTextBeingDragged() {
434   NOTIMPLEMENTED();
435   return false;
436 }
437
438 gfx::Point NativeTextfieldWin::GetContextMenuLocation() {
439   NOTIMPLEMENTED();
440   return gfx::Point();
441 }
442
443 ////////////////////////////////////////////////////////////////////////////////
444 // NativeTextfieldWin, ui::SimpleMenuModel::Delegate implementation:
445
446 bool NativeTextfieldWin::IsCommandIdChecked(int command_id) const {
447   return false;
448 }
449
450 bool NativeTextfieldWin::IsCommandIdEnabled(int command_id) const {
451   switch (command_id) {
452     case IDS_APP_UNDO:       return !textfield_->read_only() && !!CanUndo();
453     case IDS_APP_CUT:        return !textfield_->read_only() &&
454                                     !textfield_->IsObscured() && !!CanCut();
455     case IDS_APP_COPY:       return !!CanCopy() && !textfield_->IsObscured();
456     case IDS_APP_PASTE:      return !textfield_->read_only() && !!CanPaste();
457     case IDS_APP_SELECT_ALL: return !!CanSelectAll();
458     default:                 NOTREACHED();
459                              return false;
460   }
461 }
462
463 bool NativeTextfieldWin::GetAcceleratorForCommandId(int command_id,
464     ui::Accelerator* accelerator) {
465   // The standard Ctrl-X, Ctrl-V and Ctrl-C are not defined as accelerators
466   // anywhere so we need to check for them explicitly here.
467   switch (command_id) {
468     case IDS_APP_CUT:
469       *accelerator = ui::Accelerator(ui::VKEY_X, ui::EF_CONTROL_DOWN);
470       return true;
471     case IDS_APP_COPY:
472       *accelerator = ui::Accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN);
473       return true;
474     case IDS_APP_PASTE:
475       *accelerator = ui::Accelerator(ui::VKEY_V, ui::EF_CONTROL_DOWN);
476       return true;
477   }
478   return container_view_->GetWidget()->GetAccelerator(command_id, accelerator);
479 }
480
481 void NativeTextfieldWin::ExecuteCommand(int command_id, int event_flags) {
482   ScopedFreeze freeze(this, GetTextObjectModel());
483   OnBeforePossibleChange();
484   switch (command_id) {
485     case IDS_APP_UNDO:       Undo();           break;
486     case IDS_APP_CUT:        Cut();            break;
487     case IDS_APP_COPY:       Copy();           break;
488     case IDS_APP_PASTE:      Paste();          break;
489     case IDS_APP_SELECT_ALL: SelectAll(false); break;
490     default:                 NOTREACHED();     break;
491   }
492   OnAfterPossibleChange(true);
493 }
494
495 void NativeTextfieldWin::OnTextUpdated(const gfx::Range& composition_range) {
496   if (ime_discard_composition_) {
497     ime_composition_start_ = composition_range.start();
498     ime_composition_length_ = composition_range.length();
499   } else {
500     ime_composition_start_ = 0;
501     ime_composition_length_ = 0;
502   }
503   OnAfterPossibleChange(false);
504   text_before_change_.clear();
505 }
506
507 void NativeTextfieldWin::OnImeStartCompositionInternal() {
508   // Users may press alt+shift or control+shift keys to change their keyboard
509   // layouts. So, we retrieve the input locale identifier everytime we start
510   // an IME composition.
511   int language_id = PRIMARYLANGID(GetKeyboardLayout(0));
512   ime_discard_composition_ =
513       language_id == LANG_JAPANESE || language_id == LANG_CHINESE;
514   ime_composition_start_ = 0;
515   ime_composition_length_ = 0;
516 }
517
518 void NativeTextfieldWin::OnImeEndCompositionInternal() {
519   // Bug 11863: Korean IMEs send a WM_IME_ENDCOMPOSITION message without
520   // sending any WM_IME_COMPOSITION messages when a user deletes all
521   // composition characters, i.e. a composition string becomes empty. To handle
522   // this case, we need to update the find results when a composition is
523   // finished or canceled.
524   textfield_->SyncText();
525 }
526
527 void NativeTextfieldWin::OnTSFStartComposition() {
528   OnImeStartCompositionInternal();
529 }
530
531 void NativeTextfieldWin::OnTSFEndComposition() {
532   OnImeEndCompositionInternal();
533 }
534
535 void NativeTextfieldWin::InitializeAccessibilityInfo() {
536   // Set the accessible state.
537   accessibility_state_ = 0;
538
539   base::win::ScopedComPtr<IAccPropServices> pAccPropServices;
540   HRESULT hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER,
541       IID_IAccPropServices, reinterpret_cast<void**>(&pAccPropServices));
542   if (!SUCCEEDED(hr))
543     return;
544
545   VARIANT var;
546
547   // Set the accessible role.
548   var.vt = VT_I4;
549   var.lVal = ROLE_SYSTEM_TEXT;
550   hr = pAccPropServices->SetHwndProp(m_hWnd, OBJID_CLIENT,
551       CHILDID_SELF, PROPID_ACC_ROLE, var);
552
553   // Set the accessible name by getting the label text.
554   View* parent = textfield_->parent();
555   int label_index = parent->GetIndexOf(textfield_) - 1;
556   if (label_index >= 0) {
557     // Try to find the name of this text field.
558     // We expect it to be a Label preceeding this view (if it exists).
559     string16 name;
560     View* label_view = parent->child_at(label_index);
561     if (!strcmp(label_view->GetClassName(), Label::kViewClassName)) {
562       ui::AccessibleViewState state;
563       label_view->GetAccessibleState(&state);
564       hr = pAccPropServices->SetHwndPropStr(m_hWnd, OBJID_CLIENT,
565           CHILDID_SELF, PROPID_ACC_NAME, state.name.c_str());
566     }
567   }
568 }
569
570 void NativeTextfieldWin::UpdateAccessibleState(uint32 state_flag,
571                                                bool set_value) {
572   base::win::ScopedComPtr<IAccPropServices> pAccPropServices;
573   HRESULT hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER,
574       IID_IAccPropServices, reinterpret_cast<void**>(&pAccPropServices));
575   if (!SUCCEEDED(hr))
576     return;
577
578   VARIANT var;
579   var.vt = VT_I4;
580   var.lVal = set_value ? accessibility_state_ | state_flag
581       : accessibility_state_ & ~state_flag;
582   hr = pAccPropServices->SetHwndProp(m_hWnd, OBJID_CLIENT,
583       CHILDID_SELF, PROPID_ACC_STATE, var);
584
585   ::NotifyWinEvent(EVENT_OBJECT_STATECHANGE, m_hWnd, OBJID_CLIENT,
586                    CHILDID_SELF);
587 }
588
589 void NativeTextfieldWin::UpdateAccessibleValue(const string16& value) {
590   base::win::ScopedComPtr<IAccPropServices> pAccPropServices;
591   HRESULT hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER,
592       IID_IAccPropServices, reinterpret_cast<void**>(&pAccPropServices));
593   if (!SUCCEEDED(hr))
594     return;
595
596   hr = pAccPropServices->SetHwndPropStr(m_hWnd, OBJID_CLIENT,
597       CHILDID_SELF, PROPID_ACC_VALUE, value.c_str());
598
599   ::NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, m_hWnd, OBJID_CLIENT,
600                    CHILDID_SELF);
601 }
602
603 ////////////////////////////////////////////////////////////////////////////////
604 // NativeTextfieldWin, private:
605
606 void NativeTextfieldWin::OnChar(TCHAR ch, UINT repeat_count, UINT flags) {
607   HandleKeystroke();
608 }
609
610 void NativeTextfieldWin::OnContextMenu(HWND window, const POINT& point) {
611   POINT p(point);
612   ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE;
613   if (point.x == -1 || point.y == -1) {
614     source_type = ui::MENU_SOURCE_KEYBOARD;
615     GetCaretPos(&p);
616     MapWindowPoints(HWND_DESKTOP, &p, 1);
617   }
618   BuildContextMenu();
619
620   MenuModelAdapter adapter(context_menu_contents_.get());
621   context_menu_runner_.reset(new MenuRunner(adapter.CreateMenu()));
622
623   ignore_result(context_menu_runner_->RunMenuAt(textfield_->GetWidget(), NULL,
624       gfx::Rect(gfx::Point(p), gfx::Size()), MenuItemView::TOPLEFT,
625       source_type, MenuRunner::HAS_MNEMONICS));
626 }
627
628 void NativeTextfieldWin::OnCopy() {
629   if (textfield_->IsObscured())
630     return;
631
632   const string16 text(GetSelectedText());
633   if (!text.empty()) {
634     ui::ScopedClipboardWriter(
635         ui::Clipboard::GetForCurrentThread(),
636         ui::CLIPBOARD_TYPE_COPY_PASTE).WriteText(text);
637     if (TextfieldController* controller = textfield_->GetController())
638       controller->OnAfterCutOrCopy();
639   }
640 }
641
642 LRESULT NativeTextfieldWin::OnCreate(const CREATESTRUCTW* /*create_struct*/) {
643   if (base::win::IsTSFAwareRequired()) {
644     // Enable TSF support of RichEdit.
645     SetEditStyle(SES_USECTF, SES_USECTF);
646
647     // When TSF is enabled, OnTextUpdated() may be called without any previous
648     // call that would have indicated the start of an editing session.  In order
649     // to guarantee we've always called OnBeforePossibleChange() before
650     // OnAfterPossibleChange(), we therefore call that here.  Note that multiple
651     // (i.e. unmatched) calls to this function in a row are safe.
652     OnBeforePossibleChange();
653   }
654   SetMsgHandled(FALSE);
655   return 0;
656 }
657
658 void NativeTextfieldWin::OnCut() {
659   if (textfield_->read_only() || textfield_->IsObscured())
660     return;
661
662   OnCopy();
663
664   // This replace selection will have no effect (even on the undo stack) if the
665   // current selection is empty.
666   ReplaceSel(L"", true);
667 }
668
669 LRESULT NativeTextfieldWin::OnGetObject(UINT message,
670                                         WPARAM wparam,
671                                         LPARAM lparam) {
672   LRESULT ret = 0;
673   if (lparam == OBJID_CLIENT) {
674     ret = LresultFromObject(IID_IAccessible, wparam,
675         textfield_->GetNativeViewAccessible());
676   }
677   return ret;
678 }
679
680 LRESULT NativeTextfieldWin::OnImeChar(UINT message,
681                                       WPARAM wparam,
682                                       LPARAM lparam) {
683   // http://crbug.com/7707: a rich-edit control may crash when it receives a
684   // WM_IME_CHAR message while it is processing a WM_IME_COMPOSITION message.
685   // Since view controls don't need WM_IME_CHAR messages, we prevent WM_IME_CHAR
686   // messages from being dispatched to view controls via the CallWindowProc()
687   // call.
688   return 0;
689 }
690
691 LRESULT NativeTextfieldWin::OnImeStartComposition(UINT message,
692                                                   WPARAM wparam,
693                                                   LPARAM lparam) {
694   OnImeStartCompositionInternal();
695   return DefWindowProc(message, wparam, lparam);
696 }
697
698 LRESULT NativeTextfieldWin::OnImeComposition(UINT message,
699                                              WPARAM wparam,
700                                              LPARAM lparam) {
701   text_before_change_.clear();
702   LRESULT result = DefWindowProc(message, wparam, lparam);
703
704   ime_composition_start_ = 0;
705   ime_composition_length_ = 0;
706   if (ime_discard_composition_) {
707     // Call IMM32 functions to retrieve the position and the length of the
708     // ongoing composition string and notify the OnAfterPossibleChange()
709     // function that it should discard the composition string from a search
710     // string. We should not call IMM32 functions in the function because it
711     // is called when an IME is not composing a string.
712     HIMC imm_context = ImmGetContext(m_hWnd);
713     if (imm_context) {
714       CHARRANGE selection;
715       GetSel(selection);
716       const int cursor_position =
717           ImmGetCompositionString(imm_context, GCS_CURSORPOS, NULL, 0);
718       if (cursor_position >= 0)
719         ime_composition_start_ = selection.cpMin - cursor_position;
720
721       const int composition_size =
722           ImmGetCompositionString(imm_context, GCS_COMPSTR, NULL, 0);
723       if (composition_size >= 0)
724         ime_composition_length_ = composition_size / sizeof(wchar_t);
725
726       ImmReleaseContext(m_hWnd, imm_context);
727     }
728   }
729
730   // If we allow OnAfterPossibleChange() to redraw the text, it will do this by
731   // setting the edit's text directly, which can cancel the current IME
732   // composition or cause other adverse affects. So we set |should_redraw_text|
733   // to false.
734   OnAfterPossibleChange(false);
735   return result;
736 }
737
738 LRESULT NativeTextfieldWin::OnImeEndComposition(UINT message,
739                                                 WPARAM wparam,
740                                                 LPARAM lparam) {
741   OnImeEndCompositionInternal();
742   return DefWindowProc(message, wparam, lparam);
743 }
744
745 LRESULT NativeTextfieldWin::OnPointerDown(UINT message, WPARAM wparam,
746                                           LPARAM lparam) {
747   SetFocus();
748   SetMsgHandled(FALSE);
749   return 0;
750 }
751
752 void NativeTextfieldWin::OnKeyDown(TCHAR key, UINT repeat_count, UINT flags) {
753   // NOTE: Annoyingly, ctrl-alt-<key> generates WM_KEYDOWN rather than
754   // WM_SYSKEYDOWN, so we need to check (flags & KF_ALTDOWN) in various places
755   // in this function even with a WM_SYSKEYDOWN handler.
756
757   switch (key) {
758
759     // Ignore Return
760     case VK_RETURN:
761       return;
762
763     // Hijacking Editing Commands
764     //
765     // We hijack the keyboard short-cuts for Cut, Copy, and Paste here so that
766     // they go through our clipboard routines.  This allows us to be smarter
767     // about how we interact with the clipboard and avoid bugs in the
768     // CRichEditCtrl.  If we didn't hijack here, the edit control would handle
769     // these internally with sending the WM_CUT, WM_COPY, or WM_PASTE messages.
770     //
771     // Cut:   Shift-Delete and Ctrl-x are treated as cut.  Ctrl-Shift-Delete and
772     //        Ctrl-Shift-x are not treated as cut even though the underlying
773     //        CRichTextEdit would treat them as such.
774     // Copy:  Ctrl-c is treated as copy.  Shift-Ctrl-c is not.
775     // Paste: Shift-Insert and Ctrl-v are tread as paste.  Ctrl-Shift-Insert and
776     //        Ctrl-Shift-v are not.
777     //
778     // This behavior matches most, but not all Windows programs, and largely
779     // conforms to what users expect.
780
781     case VK_DELETE:
782     case 'X':
783       if ((flags & KF_ALTDOWN) ||
784           (GetKeyState((key == 'X') ? VK_CONTROL : VK_SHIFT) >= 0))
785         break;
786       if (GetKeyState((key == 'X') ? VK_SHIFT : VK_CONTROL) >= 0) {
787         ScopedFreeze freeze(this, GetTextObjectModel());
788         OnBeforePossibleChange();
789         Cut();
790         OnAfterPossibleChange(true);
791       }
792       return;
793
794     case 'C':
795       if ((flags & KF_ALTDOWN) || (GetKeyState(VK_CONTROL) >= 0))
796         break;
797       if (GetKeyState(VK_SHIFT) >= 0)
798         Copy();
799       return;
800
801     case VK_INSERT:
802       // Ignore insert by itself, so we don't turn overtype mode on/off.
803       if (!(flags & KF_ALTDOWN) && (GetKeyState(VK_SHIFT) >= 0) &&
804           (GetKeyState(VK_CONTROL) >= 0))
805         return;
806       // Fall through to the next case (ie. Shift-Insert == Ctrl-V).
807     case 'V':
808       if ((flags & KF_ALTDOWN) ||
809           (GetKeyState((key == 'V') ? VK_CONTROL : VK_SHIFT) >= 0))
810         break;
811       if (GetKeyState((key == 'V') ? VK_SHIFT : VK_CONTROL) >= 0) {
812         ScopedFreeze freeze(this, GetTextObjectModel());
813         OnBeforePossibleChange();
814         Paste();
815         OnAfterPossibleChange(true);
816       }
817       return;
818
819     case 0xbb:  // Ctrl-'='.  Triggers subscripting, even in plain text mode.
820                 // We don't use VK_OEM_PLUS in case the macro isn't defined.
821                 // (e.g., we don't have this symbol in embeded environment).
822       return;
823
824     case VK_PROCESSKEY:
825       // This key event is consumed by an IME.
826       // We ignore this event because an IME sends WM_IME_COMPOSITION messages
827       // when it updates the CRichEditCtrl text.
828       return;
829   }
830
831   // CRichEditCtrl changes its text on WM_KEYDOWN instead of WM_CHAR for many
832   // different keys (backspace, ctrl-v, ...), so we call this in both cases.
833   HandleKeystroke();
834 }
835
836 void NativeTextfieldWin::OnLButtonDblClk(UINT keys, const CPoint& point) {
837   // Save the double click info for later triple-click detection.
838   tracking_double_click_ = true;
839   double_click_point_ = point;
840   double_click_time_ = GetCurrentMessage()->time;
841
842   if (!ShouldProcessMouseEvent())
843     return;
844
845   ScopedFreeze freeze(this, GetTextObjectModel());
846   OnBeforePossibleChange();
847   DefWindowProc(WM_LBUTTONDBLCLK, keys,
848                 MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y));
849   OnAfterPossibleChange(true);
850 }
851
852 void NativeTextfieldWin::OnLButtonDown(UINT keys, const CPoint& point) {
853   // Check for triple click, then reset tracker.  Should be safe to subtract
854   // double_click_time_ from the current message's time even if the timer has
855   // wrapped in between.
856   const bool is_triple_click = tracking_double_click_ &&
857       IsDoubleClick(double_click_point_, point,
858                     GetCurrentMessage()->time - double_click_time_);
859   tracking_double_click_ = false;
860
861   if (!ShouldProcessMouseEvent())
862     return;
863
864   ScopedFreeze freeze(this, GetTextObjectModel());
865   OnBeforePossibleChange();
866   DefWindowProc(WM_LBUTTONDOWN, keys,
867                 MAKELPARAM(ClipXCoordToVisibleText(point.x, is_triple_click),
868                            point.y));
869   OnAfterPossibleChange(true);
870 }
871
872 void NativeTextfieldWin::OnLButtonUp(UINT keys, const CPoint& point) {
873   ScopedFreeze freeze(this, GetTextObjectModel());
874   OnBeforePossibleChange();
875   DefWindowProc(WM_LBUTTONUP, keys,
876                 MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y));
877   OnAfterPossibleChange(true);
878 }
879
880 void NativeTextfieldWin::OnMouseLeave() {
881   SetContainsMouse(false);
882 }
883
884 LRESULT NativeTextfieldWin::OnMouseWheel(UINT message,
885                                          WPARAM w_param,
886                                          LPARAM l_param) {
887   // Reroute the mouse-wheel to the window under the mouse pointer if
888   // applicable.
889   if (ui::RerouteMouseWheel(m_hWnd, w_param, l_param))
890     return 0;
891   return DefWindowProc(message, w_param, l_param);
892 }
893
894 void NativeTextfieldWin::OnMouseMove(UINT keys, const CPoint& point) {
895   SetContainsMouse(true);
896   // Clamp the selection to the visible text so the user can't drag to select
897   // the "phantom newline".  In theory we could achieve this by clipping the X
898   // coordinate, but in practice the edit seems to behave nondeterministically
899   // with similar sequences of clipped input coordinates fed to it.  Maybe it's
900   // reading the mouse cursor position directly?
901   //
902   // This solution has a minor visual flaw, however: if there's a visible
903   // cursor at the edge of the text (only true when there's no selection),
904   // dragging the mouse around outside that edge repaints the cursor on every
905   // WM_MOUSEMOVE instead of allowing it to blink normally.  To fix this, we
906   // special-case this exact case and discard the WM_MOUSEMOVE messages instead
907   // of passing them along.
908   //
909   // But even this solution has a flaw!  (Argh.)  In the case where the user
910   // has a selection that starts at the edge of the edit, and proceeds to the
911   // middle of the edit, and the user is dragging back past the start edge to
912   // remove the selection, there's a redraw problem where the change between
913   // having the last few bits of text still selected and having nothing
914   // selected can be slow to repaint (which feels noticeably strange).  This
915   // occurs if you only let the edit receive a single WM_MOUSEMOVE past the
916   // edge of the text.  I think on each WM_MOUSEMOVE the edit is repainting its
917   // previous state, then updating its internal variables to the new state but
918   // not repainting.  To fix this, we allow one more WM_MOUSEMOVE through after
919   // the selection has supposedly been shrunk to nothing; this makes the edit
920   // redraw the selection quickly so it feels smooth.
921   CHARRANGE selection;
922   GetSel(selection);
923   const bool possibly_can_discard_mousemove =
924       (selection.cpMin == selection.cpMax) &&
925       (((selection.cpMin == 0) &&
926         (ClipXCoordToVisibleText(point.x, false) > point.x)) ||
927        ((selection.cpMin == GetTextLength()) &&
928         (ClipXCoordToVisibleText(point.x, false) < point.x)));
929   if (!can_discard_mousemove_ || !possibly_can_discard_mousemove) {
930     can_discard_mousemove_ = possibly_can_discard_mousemove;
931     ScopedFreeze freeze(this, GetTextObjectModel());
932     OnBeforePossibleChange();
933     // Force the Y coordinate to the center of the clip rect.  The edit
934     // behaves strangely when the cursor is dragged vertically: if the cursor
935     // is in the middle of the text, drags inside the clip rect do nothing,
936     // and drags outside the clip rect act as if the cursor jumped to the
937     // left edge of the text.  When the cursor is at the right edge, drags of
938     // just a few pixels vertically end up selecting the "phantom newline"...
939     // sometimes.
940     RECT r;
941     GetRect(&r);
942     DefWindowProc(WM_MOUSEMOVE, keys,
943                   MAKELPARAM(point.x, (r.bottom - r.top) / 2));
944     OnAfterPossibleChange(true);
945   }
946 }
947
948 int NativeTextfieldWin::OnNCCalcSize(BOOL w_param, LPARAM l_param) {
949   content_insets_.Set(0, 0, 0, 0);
950   if (textfield_->draw_border())
951     content_insets_ = CalculateInsets();
952   if (w_param) {
953     NCCALCSIZE_PARAMS* nc_params =
954         reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param);
955     nc_params->rgrc[0].left += content_insets_.left();
956     nc_params->rgrc[0].right -= content_insets_.right();
957     nc_params->rgrc[0].top += content_insets_.top();
958     nc_params->rgrc[0].bottom -= content_insets_.bottom();
959   } else {
960     RECT* rect = reinterpret_cast<RECT*>(l_param);
961     rect->left += content_insets_.left();
962     rect->right -= content_insets_.right();
963     rect->top += content_insets_.top();
964     rect->bottom -= content_insets_.bottom();
965   }
966   return 0;
967 }
968
969 void NativeTextfieldWin::OnNCPaint(HRGN region) {
970   if (!textfield_->draw_border())
971     return;
972
973   HDC hdc = GetWindowDC();
974
975   CRect window_rect;
976   GetWindowRect(&window_rect);
977   // Convert to be relative to 0x0.
978   window_rect.MoveToXY(0, 0);
979
980   ExcludeClipRect(hdc,
981                   window_rect.left + content_insets_.left(),
982                   window_rect.top + content_insets_.top(),
983                   window_rect.right - content_insets_.right(),
984                   window_rect.bottom - content_insets_.bottom());
985
986   HBRUSH brush = CreateSolidBrush(bg_color_);
987   FillRect(hdc, &window_rect, brush);
988   DeleteObject(brush);
989
990   int part;
991   int state;
992
993   if (base::win::GetVersion() < base::win::VERSION_VISTA) {
994     part = EP_EDITTEXT;
995
996     if (!textfield_->enabled())
997       state = ETS_DISABLED;
998     else if (textfield_->read_only())
999       state = ETS_READONLY;
1000     else if (!contains_mouse_)
1001       state = ETS_NORMAL;
1002     else
1003       state = ETS_HOT;
1004   } else {
1005     part = EP_EDITBORDER_HVSCROLL;
1006
1007     if (!textfield_->enabled())
1008       state = EPSHV_DISABLED;
1009     else if (GetFocus() == m_hWnd)
1010       state = EPSHV_FOCUSED;
1011     else if (contains_mouse_)
1012       state = EPSHV_HOT;
1013     else
1014       state = EPSHV_NORMAL;
1015     // Vista doesn't appear to have a unique state for readonly.
1016   }
1017
1018   int classic_state =
1019       (!textfield_->enabled() || textfield_->read_only()) ? DFCS_INACTIVE : 0;
1020
1021   ui::NativeThemeWin::instance()->PaintTextField(hdc, part, state,
1022                                                   classic_state, &window_rect,
1023                                                   bg_color_, false, true);
1024
1025   // NOTE: I tried checking the transparent property of the theme and invoking
1026   // drawParentBackground, but it didn't seem to make a difference.
1027
1028   ReleaseDC(hdc);
1029 }
1030
1031 void NativeTextfieldWin::OnNonLButtonDown(UINT keys, const CPoint& point) {
1032   // Interestingly, the edit doesn't seem to cancel triple clicking when the
1033   // x-buttons (which usually means "thumb buttons") are pressed, so we only
1034   // call this for M and R down.
1035   tracking_double_click_ = false;
1036
1037   if (!ShouldProcessMouseEvent())
1038     return;
1039
1040   SetMsgHandled(false);
1041 }
1042
1043 void NativeTextfieldWin::OnPaste() {
1044   if (textfield_->read_only())
1045     return;
1046
1047   ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
1048   if (!clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextWFormatType(),
1049                                     ui::CLIPBOARD_TYPE_COPY_PASTE))
1050     return;
1051
1052   string16 clipboard_str;
1053   clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &clipboard_str);
1054   if (!clipboard_str.empty()) {
1055     string16 collapsed(CollapseWhitespace(clipboard_str, false));
1056     if (textfield_->style() & Textfield::STYLE_LOWERCASE)
1057       collapsed = base::i18n::ToLower(collapsed);
1058     // Force a Paste operation to trigger ContentsChanged, even if identical
1059     // contents are pasted into the text box. See http://crbug.com/79002
1060     ReplaceSel(L"", false);
1061     textfield_->SyncText();
1062     text_before_change_.clear();
1063     ReplaceSel(collapsed.c_str(), true);
1064     if (TextfieldController* controller = textfield_->GetController())
1065       controller->OnAfterPaste();
1066   }
1067 }
1068
1069 void NativeTextfieldWin::OnSetFocus(HWND hwnd) {
1070   SetMsgHandled(FALSE);  // We still want the default processing of the message.
1071
1072   views::FocusManager* focus_manager = textfield_->GetFocusManager();
1073   if (!focus_manager) {
1074     NOTREACHED();
1075     return;
1076   }
1077   focus_manager->SetFocusedView(textfield_);
1078
1079   if (!base::win::IsTSFAwareRequired()) {
1080     return;
1081   }
1082
1083   DefWindowProc();
1084
1085   // Document manager created by RichEdit can be obtained only after
1086   // WM_SET_FOCUS event is handled.
1087   tsf_event_router_->SetManager(
1088       ui::TSFBridge::GetInstance()->GetThreadManager());
1089   SetMsgHandled(TRUE);
1090 }
1091
1092 void NativeTextfieldWin::OnKillFocus(HWND hwnd) {
1093   if(tsf_event_router_)
1094     tsf_event_router_->SetManager(NULL);
1095   SetMsgHandled(FALSE);
1096 }
1097
1098 void NativeTextfieldWin::OnSysChar(TCHAR ch, UINT repeat_count, UINT flags) {
1099   DCHECK(flags & KF_ALTDOWN);
1100   // Explicitly show the system menu at a good location on [Alt]+[Space].
1101   // Nearly all other [Alt]+<xxx> combos result in beeping rather than doing
1102   // something useful, so discard those. Note that [Ctrl]+[Alt]+<xxx> generates
1103   // WM_CHAR instead of WM_SYSCHAR, so it is not handled here.
1104   if (ch == VK_SPACE) {
1105     gfx::ShowSystemMenu(
1106         container_view_->GetWidget()->GetTopLevelWidget()->GetNativeWindow());
1107   }
1108 }
1109
1110 void NativeTextfieldWin::OnFinalMessage(HWND hwnd) {
1111   delete this;
1112 }
1113
1114 void NativeTextfieldWin::HandleKeystroke() {
1115   const MSG* msg = GetCurrentMessage();
1116   ui::KeyEvent event(*msg, msg->message == WM_CHAR);
1117   ScopedFreeze freeze(this, GetTextObjectModel());
1118
1119   TextfieldController* controller = textfield_->GetController();
1120   if (!controller || !controller->HandleKeyEvent(textfield_, event)) {
1121     OnBeforePossibleChange();
1122
1123     if (msg->wParam == ui::VKEY_HOME || msg->wParam == ui::VKEY_END) {
1124       // DefWindowProc() might reset the keyboard layout when it receives a
1125       // keydown event for VKEY_HOME or VKEY_END. When the window was created
1126       // with WS_EX_LAYOUTRTL and the current keyboard layout is not a RTL one,
1127       // if the input text is pure LTR text, the layout changes to the first RTL
1128       // keyboard layout in keyboard layout queue; if the input text is
1129       // bidirectional text, the layout changes to the keyboard layout of the
1130       // first RTL character in input text. When the window was created without
1131       // WS_EX_LAYOUTRTL and the current keyboard layout is not a LTR one, if
1132       // the input text is pure RTL text, the layout changes to English; if the
1133       // input text is bidirectional text, the layout changes to the keyboard
1134       // layout of the first LTR character in input text. Such keyboard layout
1135       // change behavior is surprising and inconsistent with keyboard behavior
1136       // elsewhere, so reset the layout in this case.
1137       HKL layout = GetKeyboardLayout(0);
1138       DefWindowProc(msg->message, msg->wParam, msg->lParam);
1139       ActivateKeyboardLayout(layout, KLF_REORDER);
1140     } else {
1141       DefWindowProc(msg->message, msg->wParam, msg->lParam);
1142     }
1143
1144     // CRichEditCtrl automatically turns on IMF_AUTOKEYBOARD when the user
1145     // inputs an RTL character, making it difficult for the user to control
1146     // what language is set as they type. Force this off to make the edit's
1147     // behavior more stable.
1148     const int lang_options = SendMessage(EM_GETLANGOPTIONS, 0, 0);
1149     if (lang_options & IMF_AUTOKEYBOARD)
1150       SendMessage(EM_SETLANGOPTIONS, 0, lang_options & ~IMF_AUTOKEYBOARD);
1151
1152     OnAfterPossibleChange(true);
1153   }
1154 }
1155
1156 void NativeTextfieldWin::OnBeforePossibleChange() {
1157   // Record our state.
1158   text_before_change_ = GetText();
1159 }
1160
1161 void NativeTextfieldWin::OnAfterPossibleChange(bool should_redraw_text) {
1162   // Prevent the user from selecting the "phantom newline" at the end of the
1163   // edit.  If they try, we just silently move the end of the selection back to
1164   // the end of the real text.
1165   CHARRANGE new_sel;
1166   GetSel(new_sel);
1167   const int length = GetTextLength();
1168   if (new_sel.cpMax > length) {
1169     new_sel.cpMax = length;
1170     if (new_sel.cpMin > length)
1171       new_sel.cpMin = length;
1172     SetSel(new_sel);
1173   }
1174
1175   string16 new_text(GetText());
1176   if (new_text != text_before_change_) {
1177     if (ime_discard_composition_ && ime_composition_start_ >= 0 &&
1178         ime_composition_length_ > 0) {
1179       // A string retrieved with a GetText() call contains a string being
1180       // composed by an IME. We remove the composition string from this search
1181       // string.
1182       new_text.erase(ime_composition_start_, ime_composition_length_);
1183       ime_composition_start_ = 0;
1184       ime_composition_length_ = 0;
1185       if (new_text.empty())
1186         return;
1187     }
1188     textfield_->SyncText();
1189     UpdateAccessibleValue(textfield_->text());
1190
1191     if (should_redraw_text) {
1192       CHARRANGE original_sel;
1193       GetSel(original_sel);
1194       string16 text(GetText());
1195       ScopedSuspendUndo suspend_undo(GetTextObjectModel());
1196
1197       SelectAll(true);
1198       ReplaceSel(reinterpret_cast<LPCTSTR>(text.c_str()), true);
1199       SetSel(original_sel);
1200     }
1201   }
1202 }
1203
1204 LONG NativeTextfieldWin::ClipXCoordToVisibleText(LONG x,
1205                                                  bool is_triple_click) const {
1206   // Clip the X coordinate to the left edge of the text.  Careful:
1207   // PosFromChar(0) may return a negative X coordinate if the beginning of the
1208   // text has scrolled off the edit, so don't go past the clip rect's edge.
1209   PARAFORMAT2 pf2;
1210   GetParaFormat(pf2);
1211   // Calculation of the clipped coordinate is more complicated if the paragraph
1212   // layout is RTL layout, or if there is RTL characters inside the LTR layout
1213   // paragraph.
1214   bool ltr_text_in_ltr_layout = true;
1215   if ((pf2.wEffects & PFE_RTLPARA) ||
1216       base::i18n::StringContainsStrongRTLChars(GetText())) {
1217     ltr_text_in_ltr_layout = false;
1218   }
1219   const int length = GetTextLength();
1220   RECT r;
1221   GetRect(&r);
1222   // The values returned by PosFromChar() seem to refer always
1223   // to the left edge of the character's bounding box.
1224   const LONG first_position_x = PosFromChar(0).x;
1225   LONG min_x = first_position_x;
1226   if (!ltr_text_in_ltr_layout) {
1227     for (int i = 1; i < length; ++i)
1228       min_x = std::min(min_x, PosFromChar(i).x);
1229   }
1230   const LONG left_bound = std::max(r.left, min_x);
1231
1232   // PosFromChar(length) is a phantom character past the end of the text. It is
1233   // not necessarily a right bound; in RTL controls it may be a left bound. So
1234   // treat it as a right bound only if it is to the right of the first
1235   // character.
1236   LONG right_bound = r.right;
1237   LONG end_position_x = PosFromChar(length).x;
1238   if (end_position_x >= first_position_x) {
1239     right_bound = std::min(right_bound, end_position_x);  // LTR case.
1240   }
1241   // For trailing characters that are 2 pixels wide of less (like "l" in some
1242   // fonts), we have a problem:
1243   //   * Clicks on any pixel within the character will place the cursor before
1244   //     the character.
1245   //   * Clicks on the pixel just after the character will not allow triple-
1246   //     click to work properly (true for any last character width).
1247   // So, we move to the last pixel of the character when this is a
1248   // triple-click, and moving to one past the last pixel in all other
1249   // scenarios.  This way, all clicks that can move the cursor will place it at
1250   // the end of the text, but triple-click will still work.
1251   if (x < left_bound) {
1252     return (is_triple_click && ltr_text_in_ltr_layout) ? left_bound - 1 :
1253                                                          left_bound;
1254   }
1255   if ((length == 0) || (x < right_bound))
1256     return x;
1257   return is_triple_click ? (right_bound - 1) : right_bound;
1258 }
1259
1260 void NativeTextfieldWin::SetContainsMouse(bool contains_mouse) {
1261   if (contains_mouse == contains_mouse_)
1262     return;
1263
1264   contains_mouse_ = contains_mouse;
1265
1266   if (!textfield_->draw_border())
1267     return;
1268
1269   if (contains_mouse_) {
1270     // Register for notification when the mouse leaves. Need to do this so
1271     // that we can reset contains mouse properly.
1272     TRACKMOUSEEVENT tme;
1273     tme.cbSize = sizeof(tme);
1274     tme.dwFlags = TME_LEAVE;
1275     tme.hwndTrack = m_hWnd;
1276     tme.dwHoverTime = 0;
1277     TrackMouseEvent(&tme);
1278   }
1279   RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_FRAME);
1280 }
1281
1282 ITextDocument* NativeTextfieldWin::GetTextObjectModel() const {
1283   if (!text_object_model_) {
1284     base::win::ScopedComPtr<IRichEditOle, &IID_IRichEditOle> ole_interface;
1285     ole_interface.Attach(GetOleInterface());
1286     if (ole_interface)
1287       text_object_model_.QueryFrom(ole_interface);
1288   }
1289   return text_object_model_;
1290 }
1291
1292 void NativeTextfieldWin::BuildContextMenu() {
1293   if (context_menu_contents_.get())
1294     return;
1295   context_menu_contents_.reset(new ui::SimpleMenuModel(this));
1296   context_menu_contents_->AddItemWithStringId(IDS_APP_UNDO, IDS_APP_UNDO);
1297   context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
1298   context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT);
1299   context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY);
1300   context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE);
1301   context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
1302   context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL,
1303                                               IDS_APP_SELECT_ALL);
1304 }
1305
1306 bool NativeTextfieldWin::ShouldProcessMouseEvent() {
1307   TextfieldController* controller = textfield_->GetController();
1308   if (!controller)
1309     return true;
1310   MSG msg(*GetCurrentMessage());
1311   // ATL doesn't set the |time| field.
1312   if (!msg.time)
1313     msg.time = GetMessageTime();
1314   ui::MouseEvent mouse_event(msg);
1315   return !controller->HandleMouseEvent(textfield_, mouse_event);
1316 }
1317
1318 }  // namespace views