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/textfield.h" 6 7#include <string> 8 9#include "base/debug/trace_event.h" 10#include "ui/accessibility/ax_view_state.h" 11#include "ui/base/clipboard/scoped_clipboard_writer.h" 12#include "ui/base/cursor/cursor.h" 13#include "ui/base/dragdrop/drag_drop_types.h" 14#include "ui/base/dragdrop/drag_utils.h" 15#include "ui/base/ui_base_switches_util.h" 16#include "ui/compositor/scoped_animation_duration_scale_mode.h" 17#include "ui/events/event.h" 18#include "ui/events/keycodes/keyboard_codes.h" 19#include "ui/gfx/canvas.h" 20#include "ui/gfx/display.h" 21#include "ui/gfx/insets.h" 22#include "ui/gfx/screen.h" 23#include "ui/native_theme/native_theme.h" 24#include "ui/strings/grit/ui_strings.h" 25#include "ui/views/background.h" 26#include "ui/views/controls/focusable_border.h" 27#include "ui/views/controls/label.h" 28#include "ui/views/controls/menu/menu_runner.h" 29#include "ui/views/controls/native/native_view_host.h" 30#include "ui/views/controls/textfield/textfield_controller.h" 31#include "ui/views/drag_utils.h" 32#include "ui/views/ime/input_method.h" 33#include "ui/views/metrics.h" 34#include "ui/views/native_cursor.h" 35#include "ui/views/painter.h" 36#include "ui/views/views_delegate.h" 37#include "ui/views/widget/widget.h" 38 39#if defined(OS_WIN) 40#include "base/win/win_util.h" 41#endif 42 43#if defined(OS_LINUX) && !defined(OS_CHROMEOS) 44#include "base/strings/utf_string_conversions.h" 45#include "ui/events/linux/text_edit_command_auralinux.h" 46#include "ui/events/linux/text_edit_key_bindings_delegate_auralinux.h" 47#endif 48 49namespace views { 50 51namespace { 52 53// Default placeholder text color. 54const SkColor kDefaultPlaceholderTextColor = SK_ColorLTGRAY; 55 56const int kNoCommand = 0; 57 58void ConvertRectToScreen(const View* src, gfx::Rect* r) { 59 DCHECK(src); 60 61 gfx::Point new_origin = r->origin(); 62 View::ConvertPointToScreen(src, &new_origin); 63 r->set_origin(new_origin); 64} 65 66// Get the drag selection timer delay, respecting animation scaling for testing. 67int GetDragSelectionDelay() { 68 switch (ui::ScopedAnimationDurationScaleMode::duration_scale_mode()) { 69 case ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION: return 100; 70 case ui::ScopedAnimationDurationScaleMode::FAST_DURATION: return 25; 71 case ui::ScopedAnimationDurationScaleMode::SLOW_DURATION: return 400; 72 case ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION: return 1; 73 case ui::ScopedAnimationDurationScaleMode::ZERO_DURATION: return 0; 74 } 75 return 100; 76} 77 78// Get the default command for a given key |event| and selection state. 79int GetCommandForKeyEvent(const ui::KeyEvent& event, bool has_selection) { 80 if (event.type() != ui::ET_KEY_PRESSED || event.IsUnicodeKeyCode()) 81 return kNoCommand; 82 83 const bool shift = event.IsShiftDown(); 84 const bool control = event.IsControlDown(); 85 const bool alt = event.IsAltDown() || event.IsAltGrDown(); 86 switch (event.key_code()) { 87 case ui::VKEY_Z: 88 if (control && !shift && !alt) 89 return IDS_APP_UNDO; 90 return (control && shift && !alt) ? IDS_APP_REDO : kNoCommand; 91 case ui::VKEY_Y: 92 return (control && !alt) ? IDS_APP_REDO : kNoCommand; 93 case ui::VKEY_A: 94 return (control && !alt) ? IDS_APP_SELECT_ALL : kNoCommand; 95 case ui::VKEY_X: 96 return (control && !alt) ? IDS_APP_CUT : kNoCommand; 97 case ui::VKEY_C: 98 return (control && !alt) ? IDS_APP_COPY : kNoCommand; 99 case ui::VKEY_V: 100 return (control && !alt) ? IDS_APP_PASTE : kNoCommand; 101 case ui::VKEY_RIGHT: 102 // Ignore alt+right, which may be a browser navigation shortcut. 103 if (alt) 104 return kNoCommand; 105 if (!shift) 106 return control ? IDS_MOVE_WORD_RIGHT : IDS_MOVE_RIGHT; 107 return control ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION : 108 IDS_MOVE_RIGHT_AND_MODIFY_SELECTION; 109 case ui::VKEY_LEFT: 110 // Ignore alt+left, which may be a browser navigation shortcut. 111 if (alt) 112 return kNoCommand; 113 if (!shift) 114 return control ? IDS_MOVE_WORD_LEFT : IDS_MOVE_LEFT; 115 return control ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION : 116 IDS_MOVE_LEFT_AND_MODIFY_SELECTION; 117 case ui::VKEY_HOME: 118 return shift ? IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION : 119 IDS_MOVE_TO_BEGINNING_OF_LINE; 120 case ui::VKEY_END: 121 return shift ? IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION : 122 IDS_MOVE_TO_END_OF_LINE; 123 case ui::VKEY_BACK: 124 if (!control || has_selection) 125 return IDS_DELETE_BACKWARD; 126#if defined(OS_LINUX) 127 // Only erase by line break on Linux and ChromeOS. 128 if (shift) 129 return IDS_DELETE_TO_BEGINNING_OF_LINE; 130#endif 131 return IDS_DELETE_WORD_BACKWARD; 132 case ui::VKEY_DELETE: 133 if (!control || has_selection) 134 return (shift && has_selection) ? IDS_APP_CUT : IDS_DELETE_FORWARD; 135#if defined(OS_LINUX) 136 // Only erase by line break on Linux and ChromeOS. 137 if (shift) 138 return IDS_DELETE_TO_END_OF_LINE; 139#endif 140 return IDS_DELETE_WORD_FORWARD; 141 case ui::VKEY_INSERT: 142 if (control && !shift) 143 return IDS_APP_COPY; 144 return (shift && !control) ? IDS_APP_PASTE : kNoCommand; 145 default: 146 return kNoCommand; 147 } 148} 149 150#if defined(OS_LINUX) && !defined(OS_CHROMEOS) 151// Convert a custom text edit |command| to the equivalent views command ID. 152int GetViewsCommand(const ui::TextEditCommandAuraLinux& command, bool rtl) { 153 const bool select = command.extend_selection(); 154 switch (command.command_id()) { 155 case ui::TextEditCommandAuraLinux::COPY: 156 return IDS_APP_COPY; 157 case ui::TextEditCommandAuraLinux::CUT: 158 return IDS_APP_CUT; 159 case ui::TextEditCommandAuraLinux::DELETE_BACKWARD: 160 return IDS_DELETE_BACKWARD; 161 case ui::TextEditCommandAuraLinux::DELETE_FORWARD: 162 return IDS_DELETE_FORWARD; 163 case ui::TextEditCommandAuraLinux::DELETE_TO_BEGINING_OF_LINE: 164 case ui::TextEditCommandAuraLinux::DELETE_TO_BEGINING_OF_PARAGRAPH: 165 return IDS_DELETE_TO_BEGINNING_OF_LINE; 166 case ui::TextEditCommandAuraLinux::DELETE_TO_END_OF_LINE: 167 case ui::TextEditCommandAuraLinux::DELETE_TO_END_OF_PARAGRAPH: 168 return IDS_DELETE_TO_END_OF_LINE; 169 case ui::TextEditCommandAuraLinux::DELETE_WORD_BACKWARD: 170 return IDS_DELETE_WORD_BACKWARD; 171 case ui::TextEditCommandAuraLinux::DELETE_WORD_FORWARD: 172 return IDS_DELETE_WORD_FORWARD; 173 case ui::TextEditCommandAuraLinux::INSERT_TEXT: 174 return kNoCommand; 175 case ui::TextEditCommandAuraLinux::MOVE_BACKWARD: 176 if (rtl) 177 return select ? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION : IDS_MOVE_RIGHT; 178 return select ? IDS_MOVE_LEFT_AND_MODIFY_SELECTION : IDS_MOVE_LEFT; 179 case ui::TextEditCommandAuraLinux::MOVE_DOWN: 180 return IDS_MOVE_DOWN; 181 case ui::TextEditCommandAuraLinux::MOVE_FORWARD: 182 if (rtl) 183 return select ? IDS_MOVE_LEFT_AND_MODIFY_SELECTION : IDS_MOVE_LEFT; 184 return select ? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION : IDS_MOVE_RIGHT; 185 case ui::TextEditCommandAuraLinux::MOVE_LEFT: 186 return select ? IDS_MOVE_LEFT_AND_MODIFY_SELECTION : IDS_MOVE_LEFT; 187 case ui::TextEditCommandAuraLinux::MOVE_PAGE_DOWN: 188 case ui::TextEditCommandAuraLinux::MOVE_PAGE_UP: 189 return kNoCommand; 190 case ui::TextEditCommandAuraLinux::MOVE_RIGHT: 191 return select ? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION : IDS_MOVE_RIGHT; 192 case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_DOCUMENT: 193 case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_LINE: 194 case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_PARAGRAPH: 195 return select ? IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION : 196 IDS_MOVE_TO_BEGINNING_OF_LINE; 197 case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_DOCUMENT: 198 case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_LINE: 199 case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_PARAGRAPH: 200 return select ? IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION : 201 IDS_MOVE_TO_END_OF_LINE; 202 case ui::TextEditCommandAuraLinux::MOVE_UP: 203 return IDS_MOVE_UP; 204 case ui::TextEditCommandAuraLinux::MOVE_WORD_BACKWARD: 205 if (rtl) { 206 return select ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION : 207 IDS_MOVE_WORD_RIGHT; 208 } 209 return select ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION : 210 IDS_MOVE_WORD_LEFT; 211 case ui::TextEditCommandAuraLinux::MOVE_WORD_FORWARD: 212 if (rtl) { 213 return select ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION : 214 IDS_MOVE_WORD_LEFT; 215 } 216 return select ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION : 217 IDS_MOVE_WORD_RIGHT; 218 case ui::TextEditCommandAuraLinux::MOVE_WORD_LEFT: 219 return select ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION : 220 IDS_MOVE_WORD_LEFT; 221 case ui::TextEditCommandAuraLinux::MOVE_WORD_RIGHT: 222 return select ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION : 223 IDS_MOVE_WORD_RIGHT; 224 case ui::TextEditCommandAuraLinux::PASTE: 225 return IDS_APP_PASTE; 226 case ui::TextEditCommandAuraLinux::SELECT_ALL: 227 return IDS_APP_SELECT_ALL; 228 case ui::TextEditCommandAuraLinux::SET_MARK: 229 case ui::TextEditCommandAuraLinux::UNSELECT: 230 case ui::TextEditCommandAuraLinux::INVALID_COMMAND: 231 return kNoCommand; 232 } 233 return kNoCommand; 234} 235#endif 236 237} // namespace 238 239// static 240const char Textfield::kViewClassName[] = "Textfield"; 241const int Textfield::kTextPadding = 3; 242 243// static 244size_t Textfield::GetCaretBlinkMs() { 245 static const size_t default_value = 500; 246#if defined(OS_WIN) 247 static const size_t system_value = ::GetCaretBlinkTime(); 248 if (system_value != 0) 249 return (system_value == INFINITE) ? 0 : system_value; 250#endif 251 return default_value; 252} 253 254Textfield::Textfield() 255 : model_(new TextfieldModel(this)), 256 controller_(NULL), 257 read_only_(false), 258 default_width_in_chars_(0), 259 use_default_text_color_(true), 260 use_default_background_color_(true), 261 use_default_selection_text_color_(true), 262 use_default_selection_background_color_(true), 263 text_color_(SK_ColorBLACK), 264 background_color_(SK_ColorWHITE), 265 selection_text_color_(SK_ColorWHITE), 266 selection_background_color_(SK_ColorBLUE), 267 placeholder_text_color_(kDefaultPlaceholderTextColor), 268 text_input_type_(ui::TEXT_INPUT_TYPE_TEXT), 269 performing_user_action_(false), 270 skip_input_method_cancel_composition_(false), 271 cursor_visible_(false), 272 drop_cursor_visible_(false), 273 initiating_drag_(false), 274 aggregated_clicks_(0), 275 drag_start_display_offset_(0), 276 touch_handles_hidden_due_to_scroll_(false), 277 weak_ptr_factory_(this) { 278 set_context_menu_controller(this); 279 set_drag_controller(this); 280 SetBorder(scoped_ptr<Border>(new FocusableBorder())); 281 SetFocusable(true); 282 283 if (ViewsDelegate::views_delegate) { 284 password_reveal_duration_ = ViewsDelegate::views_delegate-> 285 GetDefaultTextfieldObscuredRevealDuration(); 286 } 287} 288 289Textfield::~Textfield() {} 290 291void Textfield::SetReadOnly(bool read_only) { 292 // Update read-only without changing the focusable state (or active, etc.). 293 read_only_ = read_only; 294 if (GetInputMethod()) 295 GetInputMethod()->OnTextInputTypeChanged(this); 296 SetColor(GetTextColor()); 297 UpdateBackgroundColor(); 298} 299 300void Textfield::SetTextInputType(ui::TextInputType type) { 301 GetRenderText()->SetObscured(type == ui::TEXT_INPUT_TYPE_PASSWORD); 302 text_input_type_ = type; 303 OnCaretBoundsChanged(); 304 if (GetInputMethod()) 305 GetInputMethod()->OnTextInputTypeChanged(this); 306 SchedulePaint(); 307} 308 309void Textfield::SetText(const base::string16& new_text) { 310 model_->SetText(new_text); 311 OnCaretBoundsChanged(); 312 SchedulePaint(); 313 NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED, true); 314} 315 316void Textfield::AppendText(const base::string16& new_text) { 317 if (new_text.empty()) 318 return; 319 model_->Append(new_text); 320 OnCaretBoundsChanged(); 321 SchedulePaint(); 322} 323 324void Textfield::InsertOrReplaceText(const base::string16& new_text) { 325 if (new_text.empty()) 326 return; 327 model_->InsertText(new_text); 328 OnCaretBoundsChanged(); 329 SchedulePaint(); 330} 331 332base::i18n::TextDirection Textfield::GetTextDirection() const { 333 return GetRenderText()->GetTextDirection(); 334} 335 336base::string16 Textfield::GetSelectedText() const { 337 return model_->GetSelectedText(); 338} 339 340void Textfield::SelectAll(bool reversed) { 341 model_->SelectAll(reversed); 342 UpdateSelectionClipboard(); 343 UpdateAfterChange(false, true); 344} 345 346void Textfield::SelectWordAt(const gfx::Point& point) { 347 model_->MoveCursorTo(point, false); 348 model_->SelectWord(); 349 UpdateAfterChange(false, true); 350} 351 352void Textfield::ClearSelection() { 353 model_->ClearSelection(); 354 UpdateAfterChange(false, true); 355} 356 357bool Textfield::HasSelection() const { 358 return !GetSelectedRange().is_empty(); 359} 360 361SkColor Textfield::GetTextColor() const { 362 if (!use_default_text_color_) 363 return text_color_; 364 365 return GetNativeTheme()->GetSystemColor(read_only() ? 366 ui::NativeTheme::kColorId_TextfieldReadOnlyColor : 367 ui::NativeTheme::kColorId_TextfieldDefaultColor); 368} 369 370void Textfield::SetTextColor(SkColor color) { 371 text_color_ = color; 372 use_default_text_color_ = false; 373 SetColor(color); 374} 375 376void Textfield::UseDefaultTextColor() { 377 use_default_text_color_ = true; 378 SetColor(GetTextColor()); 379} 380 381SkColor Textfield::GetBackgroundColor() const { 382 if (!use_default_background_color_) 383 return background_color_; 384 385 return GetNativeTheme()->GetSystemColor(read_only() ? 386 ui::NativeTheme::kColorId_TextfieldReadOnlyBackground : 387 ui::NativeTheme::kColorId_TextfieldDefaultBackground); 388} 389 390void Textfield::SetBackgroundColor(SkColor color) { 391 background_color_ = color; 392 use_default_background_color_ = false; 393 UpdateBackgroundColor(); 394} 395 396void Textfield::UseDefaultBackgroundColor() { 397 use_default_background_color_ = true; 398 UpdateBackgroundColor(); 399} 400 401SkColor Textfield::GetSelectionTextColor() const { 402 return use_default_selection_text_color_ ? 403 GetNativeTheme()->GetSystemColor( 404 ui::NativeTheme::kColorId_TextfieldSelectionColor) : 405 selection_text_color_; 406} 407 408void Textfield::SetSelectionTextColor(SkColor color) { 409 selection_text_color_ = color; 410 use_default_selection_text_color_ = false; 411 GetRenderText()->set_selection_color(GetSelectionTextColor()); 412 SchedulePaint(); 413} 414 415void Textfield::UseDefaultSelectionTextColor() { 416 use_default_selection_text_color_ = true; 417 GetRenderText()->set_selection_color(GetSelectionTextColor()); 418 SchedulePaint(); 419} 420 421void Textfield::SetShadows(const gfx::ShadowValues& shadows) { 422 GetRenderText()->set_shadows(shadows); 423 SchedulePaint(); 424} 425 426SkColor Textfield::GetSelectionBackgroundColor() const { 427 return use_default_selection_background_color_ ? 428 GetNativeTheme()->GetSystemColor( 429 ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused) : 430 selection_background_color_; 431} 432 433void Textfield::SetSelectionBackgroundColor(SkColor color) { 434 selection_background_color_ = color; 435 use_default_selection_background_color_ = false; 436 GetRenderText()->set_selection_background_focused_color( 437 GetSelectionBackgroundColor()); 438 SchedulePaint(); 439} 440 441void Textfield::UseDefaultSelectionBackgroundColor() { 442 use_default_selection_background_color_ = true; 443 GetRenderText()->set_selection_background_focused_color( 444 GetSelectionBackgroundColor()); 445 SchedulePaint(); 446} 447 448bool Textfield::GetCursorEnabled() const { 449 return GetRenderText()->cursor_enabled(); 450} 451 452void Textfield::SetCursorEnabled(bool enabled) { 453 GetRenderText()->SetCursorEnabled(enabled); 454} 455 456const gfx::FontList& Textfield::GetFontList() const { 457 return GetRenderText()->font_list(); 458} 459 460void Textfield::SetFontList(const gfx::FontList& font_list) { 461 GetRenderText()->SetFontList(font_list); 462 OnCaretBoundsChanged(); 463 PreferredSizeChanged(); 464} 465 466base::string16 Textfield::GetPlaceholderText() const { 467 return placeholder_text_; 468} 469 470gfx::HorizontalAlignment Textfield::GetHorizontalAlignment() const { 471 return GetRenderText()->horizontal_alignment(); 472} 473 474void Textfield::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) { 475 GetRenderText()->SetHorizontalAlignment(alignment); 476} 477 478void Textfield::ShowImeIfNeeded() { 479 if (enabled() && !read_only()) 480 GetInputMethod()->ShowImeIfNeeded(); 481} 482 483bool Textfield::IsIMEComposing() const { 484 return model_->HasCompositionText(); 485} 486 487const gfx::Range& Textfield::GetSelectedRange() const { 488 return GetRenderText()->selection(); 489} 490 491void Textfield::SelectRange(const gfx::Range& range) { 492 model_->SelectRange(range); 493 UpdateAfterChange(false, true); 494} 495 496const gfx::SelectionModel& Textfield::GetSelectionModel() const { 497 return GetRenderText()->selection_model(); 498} 499 500void Textfield::SelectSelectionModel(const gfx::SelectionModel& sel) { 501 model_->SelectSelectionModel(sel); 502 UpdateAfterChange(false, true); 503} 504 505size_t Textfield::GetCursorPosition() const { 506 return model_->GetCursorPosition(); 507} 508 509void Textfield::SetColor(SkColor value) { 510 GetRenderText()->SetColor(value); 511 SchedulePaint(); 512} 513 514void Textfield::ApplyColor(SkColor value, const gfx::Range& range) { 515 GetRenderText()->ApplyColor(value, range); 516 SchedulePaint(); 517} 518 519void Textfield::SetStyle(gfx::TextStyle style, bool value) { 520 GetRenderText()->SetStyle(style, value); 521 SchedulePaint(); 522} 523 524void Textfield::ApplyStyle(gfx::TextStyle style, 525 bool value, 526 const gfx::Range& range) { 527 GetRenderText()->ApplyStyle(style, value, range); 528 SchedulePaint(); 529} 530 531void Textfield::ClearEditHistory() { 532 model_->ClearEditHistory(); 533} 534 535void Textfield::SetAccessibleName(const base::string16& name) { 536 accessible_name_ = name; 537} 538 539void Textfield::ExecuteCommand(int command_id) { 540 ExecuteCommand(command_id, ui::EF_NONE); 541} 542 543void Textfield::SetFocusPainter(scoped_ptr<Painter> focus_painter) { 544 focus_painter_ = focus_painter.Pass(); 545} 546 547bool Textfield::HasTextBeingDragged() { 548 return initiating_drag_; 549} 550 551//////////////////////////////////////////////////////////////////////////////// 552// Textfield, View overrides: 553 554gfx::Insets Textfield::GetInsets() const { 555 gfx::Insets insets = View::GetInsets(); 556 insets += gfx::Insets(kTextPadding, kTextPadding, kTextPadding, kTextPadding); 557 return insets; 558} 559 560int Textfield::GetBaseline() const { 561 return GetInsets().top() + GetRenderText()->GetBaseline(); 562} 563 564gfx::Size Textfield::GetPreferredSize() const { 565 const gfx::Insets& insets = GetInsets(); 566 return gfx::Size(GetFontList().GetExpectedTextWidth(default_width_in_chars_) + 567 insets.width(), GetFontList().GetHeight() + insets.height()); 568} 569 570const char* Textfield::GetClassName() const { 571 return kViewClassName; 572} 573 574gfx::NativeCursor Textfield::GetCursor(const ui::MouseEvent& event) { 575 bool in_selection = GetRenderText()->IsPointInSelection(event.location()); 576 bool drag_event = event.type() == ui::ET_MOUSE_DRAGGED; 577 bool text_cursor = !initiating_drag_ && (drag_event || !in_selection); 578 return text_cursor ? GetNativeIBeamCursor() : gfx::kNullCursor; 579} 580 581bool Textfield::OnMousePressed(const ui::MouseEvent& event) { 582 TrackMouseClicks(event); 583 584 if (!controller_ || !controller_->HandleMouseEvent(this, event)) { 585 if (event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton()) { 586 RequestFocus(); 587 ShowImeIfNeeded(); 588 } 589 590 if (event.IsOnlyLeftMouseButton()) { 591 OnBeforeUserAction(); 592 initiating_drag_ = false; 593 switch (aggregated_clicks_) { 594 case 0: 595 if (GetRenderText()->IsPointInSelection(event.location())) 596 initiating_drag_ = true; 597 else 598 MoveCursorTo(event.location(), event.IsShiftDown()); 599 break; 600 case 1: 601 SelectWordAt(event.location()); 602 double_click_word_ = GetRenderText()->selection(); 603 break; 604 case 2: 605 SelectAll(false); 606 break; 607 default: 608 NOTREACHED(); 609 } 610 OnAfterUserAction(); 611 } 612 613#if defined(OS_LINUX) && !defined(OS_CHROMEOS) 614 if (event.IsOnlyMiddleMouseButton()) { 615 if (GetRenderText()->IsPointInSelection(event.location())) { 616 OnBeforeUserAction(); 617 ClearSelection(); 618 ui::ScopedClipboardWriter( 619 ui::CLIPBOARD_TYPE_SELECTION).WriteText(base::string16()); 620 OnAfterUserAction(); 621 } else if (!read_only()) { 622 PasteSelectionClipboard(event); 623 } 624 } 625#endif 626 } 627 628 return true; 629} 630 631bool Textfield::OnMouseDragged(const ui::MouseEvent& event) { 632 last_drag_location_ = event.location(); 633 634 // Don't adjust the cursor on a potential drag and drop, or if the mouse 635 // movement from the last mouse click does not exceed the drag threshold. 636 if (initiating_drag_ || !event.IsOnlyLeftMouseButton() || 637 !ExceededDragThreshold(last_drag_location_ - last_click_location_)) { 638 return true; 639 } 640 641 // A timer is used to continuously scroll while selecting beyond side edges. 642 if ((event.location().x() > 0 && event.location().x() < size().width()) || 643 GetDragSelectionDelay() == 0) { 644 drag_selection_timer_.Stop(); 645 SelectThroughLastDragLocation(); 646 } else if (!drag_selection_timer_.IsRunning()) { 647 drag_selection_timer_.Start( 648 FROM_HERE, base::TimeDelta::FromMilliseconds(GetDragSelectionDelay()), 649 this, &Textfield::SelectThroughLastDragLocation); 650 } 651 652 return true; 653} 654 655void Textfield::OnMouseReleased(const ui::MouseEvent& event) { 656 OnBeforeUserAction(); 657 drag_selection_timer_.Stop(); 658 // Cancel suspected drag initiations, the user was clicking in the selection. 659 if (initiating_drag_) 660 MoveCursorTo(event.location(), false); 661 initiating_drag_ = false; 662 UpdateSelectionClipboard(); 663 OnAfterUserAction(); 664} 665 666bool Textfield::OnKeyPressed(const ui::KeyEvent& event) { 667 // Since HandleKeyEvent() might destroy |this|, get a weak pointer and verify 668 // it isn't null before proceeding. 669 base::WeakPtr<Textfield> textfield(weak_ptr_factory_.GetWeakPtr()); 670 671 bool handled = controller_ && controller_->HandleKeyEvent(this, event); 672 673 if (!textfield) 674 return handled; 675 676#if defined(OS_LINUX) && !defined(OS_CHROMEOS) 677 ui::TextEditKeyBindingsDelegateAuraLinux* delegate = 678 ui::GetTextEditKeyBindingsDelegate(); 679 std::vector<ui::TextEditCommandAuraLinux> commands; 680 if (!handled && delegate && delegate->MatchEvent(event, &commands)) { 681 const bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT; 682 for (size_t i = 0; i < commands.size(); ++i) { 683 const int command = GetViewsCommand(commands[i], rtl); 684 if (IsCommandIdEnabled(command)) { 685 ExecuteCommand(command); 686 handled = true; 687 } 688 } 689 return handled; 690 } 691#endif 692 693 const int command = GetCommandForKeyEvent(event, HasSelection()); 694 if (!handled && IsCommandIdEnabled(command)) { 695 ExecuteCommand(command); 696 handled = true; 697 } 698 return handled; 699} 700 701ui::TextInputClient* Textfield::GetTextInputClient() { 702 return read_only_ ? NULL : this; 703} 704 705void Textfield::OnGestureEvent(ui::GestureEvent* event) { 706 switch (event->type()) { 707 case ui::ET_GESTURE_TAP_DOWN: 708 RequestFocus(); 709 ShowImeIfNeeded(); 710 event->SetHandled(); 711 break; 712 case ui::ET_GESTURE_TAP: 713 if (event->details().tap_count() == 1) { 714 if (!GetRenderText()->IsPointInSelection(event->location())) { 715 OnBeforeUserAction(); 716 MoveCursorTo(event->location(), false); 717 OnAfterUserAction(); 718 } 719 } else if (event->details().tap_count() == 2) { 720 OnBeforeUserAction(); 721 SelectWordAt(event->location()); 722 OnAfterUserAction(); 723 } else { 724 OnBeforeUserAction(); 725 SelectAll(false); 726 OnAfterUserAction(); 727 } 728 CreateTouchSelectionControllerAndNotifyIt(); 729#if defined(OS_WIN) 730 if (!read_only()) 731 base::win::DisplayVirtualKeyboard(); 732#endif 733 event->SetHandled(); 734 break; 735 case ui::ET_GESTURE_LONG_PRESS: 736 if (!GetRenderText()->IsPointInSelection(event->location())) { 737 // If long-press happens outside selection, select word and try to 738 // activate touch selection. 739 OnBeforeUserAction(); 740 SelectWordAt(event->location()); 741 OnAfterUserAction(); 742 CreateTouchSelectionControllerAndNotifyIt(); 743 // If touch selection activated successfully, mark event as handled so 744 // that the regular context menu is not shown. 745 if (touch_selection_controller_) 746 event->SetHandled(); 747 } else { 748 // If long-press happens on the selection, deactivate touch selection 749 // and try to initiate drag-drop. If drag-drop is not enabled, context 750 // menu will be shown. Event is not marked as handled to let Views 751 // handle drag-drop or context menu. 752 DestroyTouchSelection(); 753 initiating_drag_ = switches::IsTouchDragDropEnabled(); 754 } 755 break; 756 case ui::ET_GESTURE_LONG_TAP: 757 // If touch selection is enabled, the context menu on long tap will be 758 // shown by the |touch_selection_controller_|, hence we mark the event 759 // handled so Views does not try to show context menu on it. 760 if (touch_selection_controller_) 761 event->SetHandled(); 762 break; 763 case ui::ET_GESTURE_SCROLL_BEGIN: 764 touch_handles_hidden_due_to_scroll_ = touch_selection_controller_ != NULL; 765 DestroyTouchSelection(); 766 drag_start_location_ = event->location(); 767 drag_start_display_offset_ = 768 GetRenderText()->GetUpdatedDisplayOffset().x(); 769 event->SetHandled(); 770 break; 771 case ui::ET_GESTURE_SCROLL_UPDATE: { 772 int new_offset = drag_start_display_offset_ + event->location().x() - 773 drag_start_location_.x(); 774 GetRenderText()->SetDisplayOffset(new_offset); 775 SchedulePaint(); 776 event->SetHandled(); 777 break; 778 } 779 case ui::ET_GESTURE_SCROLL_END: 780 case ui::ET_SCROLL_FLING_START: 781 if (touch_handles_hidden_due_to_scroll_) { 782 CreateTouchSelectionControllerAndNotifyIt(); 783 touch_handles_hidden_due_to_scroll_ = false; 784 } 785 event->SetHandled(); 786 break; 787 default: 788 return; 789 } 790} 791 792void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) { 793 SelectAll(false); 794} 795 796bool Textfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) { 797#if defined(OS_LINUX) && !defined(OS_CHROMEOS) 798 // Skip any accelerator handling that conflicts with custom keybindings. 799 ui::TextEditKeyBindingsDelegateAuraLinux* delegate = 800 ui::GetTextEditKeyBindingsDelegate(); 801 std::vector<ui::TextEditCommandAuraLinux> commands; 802 if (delegate && delegate->MatchEvent(event, &commands)) { 803 const bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT; 804 for (size_t i = 0; i < commands.size(); ++i) 805 if (IsCommandIdEnabled(GetViewsCommand(commands[i], rtl))) 806 return true; 807 } 808#endif 809 810 // Skip backspace accelerator handling; editable textfields handle this key. 811 // Also skip processing Windows [Alt]+<num-pad digit> Unicode alt-codes. 812 const bool is_backspace = event.key_code() == ui::VKEY_BACK; 813 return (is_backspace && !read_only()) || event.IsUnicodeKeyCode(); 814} 815 816bool Textfield::GetDropFormats( 817 int* formats, 818 std::set<OSExchangeData::CustomFormat>* custom_formats) { 819 if (!enabled() || read_only()) 820 return false; 821 // TODO(msw): Can we support URL, FILENAME, etc.? 822 *formats = ui::OSExchangeData::STRING; 823 if (controller_) 824 controller_->AppendDropFormats(formats, custom_formats); 825 return true; 826} 827 828bool Textfield::CanDrop(const OSExchangeData& data) { 829 int formats; 830 std::set<OSExchangeData::CustomFormat> custom_formats; 831 GetDropFormats(&formats, &custom_formats); 832 return enabled() && !read_only() && 833 data.HasAnyFormat(formats, custom_formats); 834} 835 836int Textfield::OnDragUpdated(const ui::DropTargetEvent& event) { 837 DCHECK(CanDrop(event.data())); 838 gfx::RenderText* render_text = GetRenderText(); 839 const gfx::Range& selection = render_text->selection(); 840 drop_cursor_position_ = render_text->FindCursorPosition(event.location()); 841 bool in_selection = !selection.is_empty() && 842 selection.Contains(gfx::Range(drop_cursor_position_.caret_pos())); 843 drop_cursor_visible_ = !in_selection; 844 // TODO(msw): Pan over text when the user drags to the visible text edge. 845 OnCaretBoundsChanged(); 846 SchedulePaint(); 847 848 if (initiating_drag_) { 849 if (in_selection) 850 return ui::DragDropTypes::DRAG_NONE; 851 return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY : 852 ui::DragDropTypes::DRAG_MOVE; 853 } 854 return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE; 855} 856 857void Textfield::OnDragExited() { 858 drop_cursor_visible_ = false; 859 SchedulePaint(); 860} 861 862int Textfield::OnPerformDrop(const ui::DropTargetEvent& event) { 863 DCHECK(CanDrop(event.data())); 864 drop_cursor_visible_ = false; 865 866 if (controller_) { 867 int drag_operation = controller_->OnDrop(event.data()); 868 if (drag_operation != ui::DragDropTypes::DRAG_NONE) 869 return drag_operation; 870 } 871 872 gfx::RenderText* render_text = GetRenderText(); 873 DCHECK(!initiating_drag_ || 874 !render_text->IsPointInSelection(event.location())); 875 OnBeforeUserAction(); 876 skip_input_method_cancel_composition_ = true; 877 878 gfx::SelectionModel drop_destination_model = 879 render_text->FindCursorPosition(event.location()); 880 base::string16 new_text; 881 event.data().GetString(&new_text); 882 883 // Delete the current selection for a drag and drop within this view. 884 const bool move = initiating_drag_ && !event.IsControlDown() && 885 event.source_operations() & ui::DragDropTypes::DRAG_MOVE; 886 if (move) { 887 // Adjust the drop destination if it is on or after the current selection. 888 size_t pos = drop_destination_model.caret_pos(); 889 pos -= render_text->selection().Intersect(gfx::Range(0, pos)).length(); 890 model_->DeleteSelectionAndInsertTextAt(new_text, pos); 891 } else { 892 model_->MoveCursorTo(drop_destination_model); 893 // Drop always inserts text even if the textfield is not in insert mode. 894 model_->InsertText(new_text); 895 } 896 skip_input_method_cancel_composition_ = false; 897 UpdateAfterChange(true, true); 898 OnAfterUserAction(); 899 return move ? ui::DragDropTypes::DRAG_MOVE : ui::DragDropTypes::DRAG_COPY; 900} 901 902void Textfield::OnDragDone() { 903 initiating_drag_ = false; 904 drop_cursor_visible_ = false; 905} 906 907void Textfield::GetAccessibleState(ui::AXViewState* state) { 908 state->role = ui::AX_ROLE_TEXT_FIELD; 909 state->name = accessible_name_; 910 if (read_only()) 911 state->AddStateFlag(ui::AX_STATE_READ_ONLY); 912 if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD) 913 state->AddStateFlag(ui::AX_STATE_PROTECTED); 914 state->value = text(); 915 916 const gfx::Range range = GetSelectedRange(); 917 state->selection_start = range.start(); 918 state->selection_end = range.end(); 919 920 if (!read_only()) { 921 state->set_value_callback = 922 base::Bind(&Textfield::AccessibilitySetValue, 923 weak_ptr_factory_.GetWeakPtr()); 924 } 925} 926 927void Textfield::OnBoundsChanged(const gfx::Rect& previous_bounds) { 928 // Textfield insets include a reasonable amount of whitespace on all sides of 929 // the default font list. Fallback fonts with larger heights may paint over 930 // the vertical whitespace as needed. Alternate solutions involve undesirable 931 // behavior like changing the default font size, shrinking some fallback fonts 932 // beyond their legibility, or enlarging controls dynamically with content. 933 gfx::Rect bounds = GetContentsBounds(); 934 // GetContentsBounds() does not actually use the local GetInsets() override. 935 bounds.Inset(gfx::Insets(0, kTextPadding, 0, kTextPadding)); 936 GetRenderText()->SetDisplayRect(bounds); 937 OnCaretBoundsChanged(); 938} 939 940bool Textfield::GetNeedsNotificationWhenVisibleBoundsChange() const { 941 return true; 942} 943 944void Textfield::OnVisibleBoundsChanged() { 945 if (touch_selection_controller_) 946 touch_selection_controller_->SelectionChanged(); 947} 948 949void Textfield::OnEnabledChanged() { 950 View::OnEnabledChanged(); 951 if (GetInputMethod()) 952 GetInputMethod()->OnTextInputTypeChanged(this); 953 SchedulePaint(); 954} 955 956void Textfield::OnPaint(gfx::Canvas* canvas) { 957 OnPaintBackground(canvas); 958 PaintTextAndCursor(canvas); 959 OnPaintBorder(canvas); 960} 961 962void Textfield::OnFocus() { 963 GetRenderText()->set_focused(true); 964 cursor_visible_ = true; 965 SchedulePaint(); 966 GetInputMethod()->OnFocus(); 967 OnCaretBoundsChanged(); 968 969 const size_t caret_blink_ms = Textfield::GetCaretBlinkMs(); 970 if (caret_blink_ms != 0) { 971 cursor_repaint_timer_.Start(FROM_HERE, 972 base::TimeDelta::FromMilliseconds(caret_blink_ms), this, 973 &Textfield::UpdateCursor); 974 } 975 976 View::OnFocus(); 977 SchedulePaint(); 978} 979 980void Textfield::OnBlur() { 981 GetRenderText()->set_focused(false); 982 GetInputMethod()->OnBlur(); 983 cursor_repaint_timer_.Stop(); 984 if (cursor_visible_) { 985 cursor_visible_ = false; 986 RepaintCursor(); 987 } 988 989 DestroyTouchSelection(); 990 991 // Border typically draws focus indicator. 992 SchedulePaint(); 993} 994 995gfx::Point Textfield::GetKeyboardContextMenuLocation() { 996 return GetCaretBounds().bottom_right(); 997} 998 999void Textfield::OnNativeThemeChanged(const ui::NativeTheme* theme) { 1000 gfx::RenderText* render_text = GetRenderText(); 1001 render_text->SetColor(GetTextColor()); 1002 UpdateBackgroundColor(); 1003 render_text->set_cursor_color(GetTextColor()); 1004 render_text->set_selection_color(GetSelectionTextColor()); 1005 render_text->set_selection_background_focused_color( 1006 GetSelectionBackgroundColor()); 1007} 1008 1009//////////////////////////////////////////////////////////////////////////////// 1010// Textfield, TextfieldModel::Delegate overrides: 1011 1012void Textfield::OnCompositionTextConfirmedOrCleared() { 1013 if (!skip_input_method_cancel_composition_) 1014 GetInputMethod()->CancelComposition(this); 1015} 1016 1017//////////////////////////////////////////////////////////////////////////////// 1018// Textfield, ContextMenuController overrides: 1019 1020void Textfield::ShowContextMenuForView(View* source, 1021 const gfx::Point& point, 1022 ui::MenuSourceType source_type) { 1023 UpdateContextMenu(); 1024 ignore_result(context_menu_runner_->RunMenuAt(GetWidget(), 1025 NULL, 1026 gfx::Rect(point, gfx::Size()), 1027 MENU_ANCHOR_TOPLEFT, 1028 source_type)); 1029} 1030 1031//////////////////////////////////////////////////////////////////////////////// 1032// Textfield, DragController overrides: 1033 1034void Textfield::WriteDragDataForView(View* sender, 1035 const gfx::Point& press_pt, 1036 OSExchangeData* data) { 1037 const base::string16& selected_text(GetSelectedText()); 1038 data->SetString(selected_text); 1039 Label label(selected_text, GetFontList()); 1040 label.SetBackgroundColor(GetBackgroundColor()); 1041 label.SetSubpixelRenderingEnabled(false); 1042 gfx::Size size(label.GetPreferredSize()); 1043 gfx::NativeView native_view = GetWidget()->GetNativeView(); 1044 gfx::Display display = gfx::Screen::GetScreenFor(native_view)-> 1045 GetDisplayNearestWindow(native_view); 1046 size.SetToMin(gfx::Size(display.size().width(), height())); 1047 label.SetBoundsRect(gfx::Rect(size)); 1048 scoped_ptr<gfx::Canvas> canvas( 1049 GetCanvasForDragImage(GetWidget(), label.size())); 1050 label.SetEnabledColor(GetTextColor()); 1051#if defined(OS_LINUX) && !defined(OS_CHROMEOS) 1052 // Desktop Linux Aura does not yet support transparency in drag images. 1053 canvas->DrawColor(GetBackgroundColor()); 1054#endif 1055 label.Paint(canvas.get(), views::CullSet()); 1056 const gfx::Vector2d kOffset(-15, 0); 1057 drag_utils::SetDragImageOnDataObject(*canvas, kOffset, data); 1058 if (controller_) 1059 controller_->OnWriteDragData(data); 1060} 1061 1062int Textfield::GetDragOperationsForView(View* sender, const gfx::Point& p) { 1063 int drag_operations = ui::DragDropTypes::DRAG_COPY; 1064 if (!enabled() || text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD || 1065 !GetRenderText()->IsPointInSelection(p)) { 1066 drag_operations = ui::DragDropTypes::DRAG_NONE; 1067 } else if (sender == this && !read_only()) { 1068 drag_operations = 1069 ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY; 1070 } 1071 if (controller_) 1072 controller_->OnGetDragOperationsForTextfield(&drag_operations); 1073 return drag_operations; 1074} 1075 1076bool Textfield::CanStartDragForView(View* sender, 1077 const gfx::Point& press_pt, 1078 const gfx::Point& p) { 1079 return initiating_drag_ && GetRenderText()->IsPointInSelection(press_pt); 1080} 1081 1082//////////////////////////////////////////////////////////////////////////////// 1083// Textfield, ui::TouchEditable overrides: 1084 1085void Textfield::SelectRect(const gfx::Point& start, const gfx::Point& end) { 1086 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) 1087 return; 1088 1089 gfx::SelectionModel start_caret = GetRenderText()->FindCursorPosition(start); 1090 gfx::SelectionModel end_caret = GetRenderText()->FindCursorPosition(end); 1091 gfx::SelectionModel selection( 1092 gfx::Range(start_caret.caret_pos(), end_caret.caret_pos()), 1093 end_caret.caret_affinity()); 1094 1095 OnBeforeUserAction(); 1096 SelectSelectionModel(selection); 1097 OnAfterUserAction(); 1098} 1099 1100void Textfield::MoveCaretTo(const gfx::Point& point) { 1101 SelectRect(point, point); 1102} 1103 1104void Textfield::GetSelectionEndPoints(gfx::Rect* p1, gfx::Rect* p2) { 1105 gfx::RenderText* render_text = GetRenderText(); 1106 const gfx::SelectionModel& sel = render_text->selection_model(); 1107 gfx::SelectionModel start_sel = 1108 render_text->GetSelectionModelForSelectionStart(); 1109 *p1 = render_text->GetCursorBounds(start_sel, true); 1110 *p2 = render_text->GetCursorBounds(sel, true); 1111} 1112 1113gfx::Rect Textfield::GetBounds() { 1114 return GetLocalBounds(); 1115} 1116 1117gfx::NativeView Textfield::GetNativeView() const { 1118 return GetWidget()->GetNativeView(); 1119} 1120 1121void Textfield::ConvertPointToScreen(gfx::Point* point) { 1122 View::ConvertPointToScreen(this, point); 1123} 1124 1125void Textfield::ConvertPointFromScreen(gfx::Point* point) { 1126 View::ConvertPointFromScreen(this, point); 1127} 1128 1129bool Textfield::DrawsHandles() { 1130 return false; 1131} 1132 1133void Textfield::OpenContextMenu(const gfx::Point& anchor) { 1134 DestroyTouchSelection(); 1135 ShowContextMenu(anchor, ui::MENU_SOURCE_TOUCH_EDIT_MENU); 1136} 1137 1138void Textfield::DestroyTouchSelection() { 1139 touch_selection_controller_.reset(); 1140} 1141 1142//////////////////////////////////////////////////////////////////////////////// 1143// Textfield, ui::SimpleMenuModel::Delegate overrides: 1144 1145bool Textfield::IsCommandIdChecked(int command_id) const { 1146 return true; 1147} 1148 1149bool Textfield::IsCommandIdEnabled(int command_id) const { 1150 base::string16 result; 1151 bool editable = !read_only(); 1152 bool readable = text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD; 1153 switch (command_id) { 1154 case IDS_APP_UNDO: 1155 return editable && model_->CanUndo(); 1156 case IDS_APP_REDO: 1157 return editable && model_->CanRedo(); 1158 case IDS_APP_CUT: 1159 return editable && readable && model_->HasSelection(); 1160 case IDS_APP_COPY: 1161 return readable && model_->HasSelection(); 1162 case IDS_APP_PASTE: 1163 ui::Clipboard::GetForCurrentThread()->ReadText( 1164 ui::CLIPBOARD_TYPE_COPY_PASTE, &result); 1165 return editable && !result.empty(); 1166 case IDS_APP_DELETE: 1167 return editable && model_->HasSelection(); 1168 case IDS_APP_SELECT_ALL: 1169 return !text().empty(); 1170 case IDS_DELETE_FORWARD: 1171 case IDS_DELETE_BACKWARD: 1172 case IDS_DELETE_TO_BEGINNING_OF_LINE: 1173 case IDS_DELETE_TO_END_OF_LINE: 1174 case IDS_DELETE_WORD_BACKWARD: 1175 case IDS_DELETE_WORD_FORWARD: 1176 return editable; 1177 case IDS_MOVE_LEFT: 1178 case IDS_MOVE_LEFT_AND_MODIFY_SELECTION: 1179 case IDS_MOVE_RIGHT: 1180 case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION: 1181 case IDS_MOVE_WORD_LEFT: 1182 case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION: 1183 case IDS_MOVE_WORD_RIGHT: 1184 case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION: 1185 case IDS_MOVE_TO_BEGINNING_OF_LINE: 1186 case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION: 1187 case IDS_MOVE_TO_END_OF_LINE: 1188 case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION: 1189 return true; 1190 default: 1191 return false; 1192 } 1193} 1194 1195bool Textfield::GetAcceleratorForCommandId(int command_id, 1196 ui::Accelerator* accelerator) { 1197 return false; 1198} 1199 1200void Textfield::ExecuteCommand(int command_id, int event_flags) { 1201 DestroyTouchSelection(); 1202 if (!IsCommandIdEnabled(command_id)) 1203 return; 1204 1205 bool text_changed = false; 1206 bool cursor_changed = false; 1207 bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT; 1208 gfx::VisualCursorDirection begin = rtl ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT; 1209 gfx::VisualCursorDirection end = rtl ? gfx::CURSOR_LEFT : gfx::CURSOR_RIGHT; 1210 gfx::SelectionModel selection_model = GetSelectionModel(); 1211 1212 OnBeforeUserAction(); 1213 switch (command_id) { 1214 case IDS_APP_UNDO: 1215 text_changed = cursor_changed = model_->Undo(); 1216 break; 1217 case IDS_APP_REDO: 1218 text_changed = cursor_changed = model_->Redo(); 1219 break; 1220 case IDS_APP_CUT: 1221 text_changed = cursor_changed = Cut(); 1222 break; 1223 case IDS_APP_COPY: 1224 Copy(); 1225 break; 1226 case IDS_APP_PASTE: 1227 text_changed = cursor_changed = Paste(); 1228 break; 1229 case IDS_APP_DELETE: 1230 text_changed = cursor_changed = model_->Delete(); 1231 break; 1232 case IDS_APP_SELECT_ALL: 1233 SelectAll(false); 1234 break; 1235 case IDS_DELETE_BACKWARD: 1236 text_changed = cursor_changed = model_->Backspace(); 1237 break; 1238 case IDS_DELETE_FORWARD: 1239 text_changed = cursor_changed = model_->Delete(); 1240 break; 1241 case IDS_DELETE_TO_END_OF_LINE: 1242 model_->MoveCursor(gfx::LINE_BREAK, end, true); 1243 text_changed = cursor_changed = model_->Delete(); 1244 break; 1245 case IDS_DELETE_TO_BEGINNING_OF_LINE: 1246 model_->MoveCursor(gfx::LINE_BREAK, begin, true); 1247 text_changed = cursor_changed = model_->Backspace(); 1248 break; 1249 case IDS_DELETE_WORD_BACKWARD: 1250 model_->MoveCursor(gfx::WORD_BREAK, begin, true); 1251 text_changed = cursor_changed = model_->Backspace(); 1252 break; 1253 case IDS_DELETE_WORD_FORWARD: 1254 model_->MoveCursor(gfx::WORD_BREAK, end, true); 1255 text_changed = cursor_changed = model_->Delete(); 1256 break; 1257 case IDS_MOVE_LEFT: 1258 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false); 1259 break; 1260 case IDS_MOVE_LEFT_AND_MODIFY_SELECTION: 1261 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); 1262 break; 1263 case IDS_MOVE_RIGHT: 1264 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); 1265 break; 1266 case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION: 1267 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); 1268 break; 1269 case IDS_MOVE_WORD_LEFT: 1270 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, false); 1271 break; 1272 case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION: 1273 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true); 1274 break; 1275 case IDS_MOVE_WORD_RIGHT: 1276 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false); 1277 break; 1278 case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION: 1279 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true); 1280 break; 1281 case IDS_MOVE_TO_BEGINNING_OF_LINE: 1282 model_->MoveCursor(gfx::LINE_BREAK, begin, false); 1283 break; 1284 case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION: 1285 model_->MoveCursor(gfx::LINE_BREAK, begin, true); 1286 break; 1287 case IDS_MOVE_TO_END_OF_LINE: 1288 model_->MoveCursor(gfx::LINE_BREAK, end, false); 1289 break; 1290 case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION: 1291 model_->MoveCursor(gfx::LINE_BREAK, end, true); 1292 break; 1293 default: 1294 NOTREACHED(); 1295 break; 1296 } 1297 1298 cursor_changed |= GetSelectionModel() != selection_model; 1299 if (cursor_changed) 1300 UpdateSelectionClipboard(); 1301 UpdateAfterChange(text_changed, cursor_changed); 1302 OnAfterUserAction(); 1303} 1304 1305//////////////////////////////////////////////////////////////////////////////// 1306// Textfield, ui::TextInputClient overrides: 1307 1308void Textfield::SetCompositionText(const ui::CompositionText& composition) { 1309 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) 1310 return; 1311 1312 OnBeforeUserAction(); 1313 skip_input_method_cancel_composition_ = true; 1314 model_->SetCompositionText(composition); 1315 skip_input_method_cancel_composition_ = false; 1316 UpdateAfterChange(true, true); 1317 OnAfterUserAction(); 1318} 1319 1320void Textfield::ConfirmCompositionText() { 1321 if (!model_->HasCompositionText()) 1322 return; 1323 1324 OnBeforeUserAction(); 1325 skip_input_method_cancel_composition_ = true; 1326 model_->ConfirmCompositionText(); 1327 skip_input_method_cancel_composition_ = false; 1328 UpdateAfterChange(true, true); 1329 OnAfterUserAction(); 1330} 1331 1332void Textfield::ClearCompositionText() { 1333 if (!model_->HasCompositionText()) 1334 return; 1335 1336 OnBeforeUserAction(); 1337 skip_input_method_cancel_composition_ = true; 1338 model_->CancelCompositionText(); 1339 skip_input_method_cancel_composition_ = false; 1340 UpdateAfterChange(true, true); 1341 OnAfterUserAction(); 1342} 1343 1344void Textfield::InsertText(const base::string16& new_text) { 1345 // TODO(suzhe): Filter invalid characters. 1346 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || new_text.empty()) 1347 return; 1348 1349 OnBeforeUserAction(); 1350 skip_input_method_cancel_composition_ = true; 1351 if (GetRenderText()->insert_mode()) 1352 model_->InsertText(new_text); 1353 else 1354 model_->ReplaceText(new_text); 1355 skip_input_method_cancel_composition_ = false; 1356 UpdateAfterChange(true, true); 1357 OnAfterUserAction(); 1358} 1359 1360void Textfield::InsertChar(base::char16 ch, int flags) { 1361 const int kControlModifierMask = ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | 1362 ui::EF_COMMAND_DOWN | ui::EF_ALTGR_DOWN | 1363 ui::EF_MOD3_DOWN; 1364 1365 // Filter out all control characters, including tab and new line characters, 1366 // and all characters with Alt modifier. But allow characters with the AltGr 1367 // modifier. On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a 1368 // different flag that we don't care about. 1369 const bool should_insert_char = 1370 ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) && 1371 (flags & kControlModifierMask) != ui::EF_ALT_DOWN; 1372 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || !should_insert_char) 1373 return; 1374 1375 OnBeforeUserAction(); 1376 skip_input_method_cancel_composition_ = true; 1377 if (GetRenderText()->insert_mode()) 1378 model_->InsertChar(ch); 1379 else 1380 model_->ReplaceChar(ch); 1381 skip_input_method_cancel_composition_ = false; 1382 1383 UpdateAfterChange(true, true); 1384 OnAfterUserAction(); 1385 1386 if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD && 1387 password_reveal_duration_ != base::TimeDelta()) { 1388 const size_t change_offset = model_->GetCursorPosition(); 1389 DCHECK_GT(change_offset, 0u); 1390 RevealPasswordChar(change_offset - 1); 1391 } 1392} 1393 1394gfx::NativeWindow Textfield::GetAttachedWindow() const { 1395 // Imagine the following hierarchy. 1396 // [NativeWidget A] - FocusManager 1397 // [View] 1398 // [NativeWidget B] 1399 // [View] 1400 // [View X] 1401 // An important thing is that [NativeWidget A] owns Win32 input focus even 1402 // when [View X] is logically focused by FocusManager. As a result, an Win32 1403 // IME may want to interact with the native view of [NativeWidget A] rather 1404 // than that of [NativeWidget B]. This is why we need to call 1405 // GetTopLevelWidget() here. 1406 return GetWidget()->GetTopLevelWidget()->GetNativeWindow(); 1407} 1408 1409ui::TextInputType Textfield::GetTextInputType() const { 1410 if (read_only() || !enabled()) 1411 return ui::TEXT_INPUT_TYPE_NONE; 1412 return text_input_type_; 1413} 1414 1415ui::TextInputMode Textfield::GetTextInputMode() const { 1416 return ui::TEXT_INPUT_MODE_DEFAULT; 1417} 1418 1419bool Textfield::CanComposeInline() const { 1420 return true; 1421} 1422 1423gfx::Rect Textfield::GetCaretBounds() const { 1424 gfx::Rect rect = GetRenderText()->GetUpdatedCursorBounds(); 1425 ConvertRectToScreen(this, &rect); 1426 return rect; 1427} 1428 1429bool Textfield::GetCompositionCharacterBounds(uint32 index, 1430 gfx::Rect* rect) const { 1431 DCHECK(rect); 1432 if (!HasCompositionText()) 1433 return false; 1434 gfx::RenderText* render_text = GetRenderText(); 1435 const gfx::Range& composition_range = render_text->GetCompositionRange(); 1436 DCHECK(!composition_range.is_empty()); 1437 1438 size_t text_index = composition_range.start() + index; 1439 if (composition_range.end() <= text_index) 1440 return false; 1441 if (!render_text->IsValidCursorIndex(text_index)) { 1442 text_index = render_text->IndexOfAdjacentGrapheme( 1443 text_index, gfx::CURSOR_BACKWARD); 1444 } 1445 if (text_index < composition_range.start()) 1446 return false; 1447 const gfx::SelectionModel caret(text_index, gfx::CURSOR_BACKWARD); 1448 *rect = render_text->GetCursorBounds(caret, false); 1449 ConvertRectToScreen(this, rect); 1450 return true; 1451} 1452 1453bool Textfield::HasCompositionText() const { 1454 return model_->HasCompositionText(); 1455} 1456 1457bool Textfield::GetTextRange(gfx::Range* range) const { 1458 if (!ImeEditingAllowed()) 1459 return false; 1460 1461 model_->GetTextRange(range); 1462 return true; 1463} 1464 1465bool Textfield::GetCompositionTextRange(gfx::Range* range) const { 1466 if (!ImeEditingAllowed()) 1467 return false; 1468 1469 model_->GetCompositionTextRange(range); 1470 return true; 1471} 1472 1473bool Textfield::GetSelectionRange(gfx::Range* range) const { 1474 if (!ImeEditingAllowed()) 1475 return false; 1476 *range = GetRenderText()->selection(); 1477 return true; 1478} 1479 1480bool Textfield::SetSelectionRange(const gfx::Range& range) { 1481 if (!ImeEditingAllowed() || !range.IsValid()) 1482 return false; 1483 OnBeforeUserAction(); 1484 SelectRange(range); 1485 OnAfterUserAction(); 1486 return true; 1487} 1488 1489bool Textfield::DeleteRange(const gfx::Range& range) { 1490 if (!ImeEditingAllowed() || range.is_empty()) 1491 return false; 1492 1493 OnBeforeUserAction(); 1494 model_->SelectRange(range); 1495 if (model_->HasSelection()) { 1496 model_->DeleteSelection(); 1497 UpdateAfterChange(true, true); 1498 } 1499 OnAfterUserAction(); 1500 return true; 1501} 1502 1503bool Textfield::GetTextFromRange(const gfx::Range& range, 1504 base::string16* range_text) const { 1505 if (!ImeEditingAllowed() || !range.IsValid()) 1506 return false; 1507 1508 gfx::Range text_range; 1509 if (!GetTextRange(&text_range) || !text_range.Contains(range)) 1510 return false; 1511 1512 *range_text = model_->GetTextFromRange(range); 1513 return true; 1514} 1515 1516void Textfield::OnInputMethodChanged() {} 1517 1518bool Textfield::ChangeTextDirectionAndLayoutAlignment( 1519 base::i18n::TextDirection direction) { 1520 // Restore text directionality mode when the indicated direction matches the 1521 // current forced mode; otherwise, force the mode indicated. This helps users 1522 // manage BiDi text layout without getting stuck in forced LTR or RTL modes. 1523 const gfx::DirectionalityMode mode = direction == base::i18n::RIGHT_TO_LEFT ? 1524 gfx::DIRECTIONALITY_FORCE_RTL : gfx::DIRECTIONALITY_FORCE_LTR; 1525 if (mode == GetRenderText()->directionality_mode()) 1526 GetRenderText()->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT); 1527 else 1528 GetRenderText()->SetDirectionalityMode(mode); 1529 SchedulePaint(); 1530 return true; 1531} 1532 1533void Textfield::ExtendSelectionAndDelete(size_t before, size_t after) { 1534 gfx::Range range = GetRenderText()->selection(); 1535 DCHECK_GE(range.start(), before); 1536 1537 range.set_start(range.start() - before); 1538 range.set_end(range.end() + after); 1539 gfx::Range text_range; 1540 if (GetTextRange(&text_range) && text_range.Contains(range)) 1541 DeleteRange(range); 1542} 1543 1544void Textfield::EnsureCaretInRect(const gfx::Rect& rect) {} 1545 1546void Textfield::OnCandidateWindowShown() {} 1547 1548void Textfield::OnCandidateWindowUpdated() {} 1549 1550void Textfield::OnCandidateWindowHidden() {} 1551 1552bool Textfield::IsEditingCommandEnabled(int command_id) { 1553 return IsCommandIdEnabled(command_id); 1554} 1555 1556void Textfield::ExecuteEditingCommand(int command_id) { 1557 ExecuteCommand(command_id); 1558} 1559 1560//////////////////////////////////////////////////////////////////////////////// 1561// Textfield, protected: 1562 1563gfx::RenderText* Textfield::GetRenderText() const { 1564 return model_->render_text(); 1565} 1566 1567base::string16 Textfield::GetSelectionClipboardText() const { 1568 base::string16 selection_clipboard_text; 1569 ui::Clipboard::GetForCurrentThread()->ReadText( 1570 ui::CLIPBOARD_TYPE_SELECTION, &selection_clipboard_text); 1571 return selection_clipboard_text; 1572} 1573 1574//////////////////////////////////////////////////////////////////////////////// 1575// Textfield, private: 1576 1577void Textfield::AccessibilitySetValue(const base::string16& new_value) { 1578 if (!read_only()) { 1579 SetText(new_value); 1580 ClearSelection(); 1581 } 1582} 1583 1584void Textfield::UpdateBackgroundColor() { 1585 const SkColor color = GetBackgroundColor(); 1586 set_background(Background::CreateSolidBackground(color)); 1587 GetRenderText()->set_background_is_transparent(SkColorGetA(color) != 0xFF); 1588 SchedulePaint(); 1589} 1590 1591void Textfield::UpdateAfterChange(bool text_changed, bool cursor_changed) { 1592 if (text_changed) { 1593 if (controller_) 1594 controller_->ContentsChanged(this, text()); 1595 NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED, true); 1596 } 1597 if (cursor_changed) { 1598 cursor_visible_ = true; 1599 RepaintCursor(); 1600 if (cursor_repaint_timer_.IsRunning()) 1601 cursor_repaint_timer_.Reset(); 1602 if (!text_changed) { 1603 // TEXT_CHANGED implies TEXT_SELECTION_CHANGED, so we only need to fire 1604 // this if only the selection changed. 1605 NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_SELECTION_CHANGED, true); 1606 } 1607 } 1608 if (text_changed || cursor_changed) { 1609 OnCaretBoundsChanged(); 1610 SchedulePaint(); 1611 } 1612} 1613 1614void Textfield::UpdateCursor() { 1615 const size_t caret_blink_ms = Textfield::GetCaretBlinkMs(); 1616 cursor_visible_ = !cursor_visible_ || (caret_blink_ms == 0); 1617 RepaintCursor(); 1618} 1619 1620void Textfield::RepaintCursor() { 1621 gfx::Rect r(GetRenderText()->GetUpdatedCursorBounds()); 1622 r.Inset(-1, -1, -1, -1); 1623 SchedulePaintInRect(r); 1624} 1625 1626void Textfield::PaintTextAndCursor(gfx::Canvas* canvas) { 1627 TRACE_EVENT0("views", "Textfield::PaintTextAndCursor"); 1628 canvas->Save(); 1629 1630 // Draw placeholder text if needed. 1631 gfx::RenderText* render_text = GetRenderText(); 1632 if (text().empty() && !GetPlaceholderText().empty()) { 1633 canvas->DrawStringRect(GetPlaceholderText(), GetFontList(), 1634 placeholder_text_color(), render_text->display_rect()); 1635 } 1636 1637 // Draw the text, cursor, and selection. 1638 render_text->set_cursor_visible(cursor_visible_ && !drop_cursor_visible_ && 1639 !HasSelection()); 1640 render_text->Draw(canvas); 1641 1642 // Draw the detached drop cursor that marks where the text will be dropped. 1643 if (drop_cursor_visible_) 1644 render_text->DrawCursor(canvas, drop_cursor_position_); 1645 1646 canvas->Restore(); 1647} 1648 1649void Textfield::MoveCursorTo(const gfx::Point& point, bool select) { 1650 if (model_->MoveCursorTo(point, select)) 1651 UpdateAfterChange(false, true); 1652} 1653 1654void Textfield::SelectThroughLastDragLocation() { 1655 OnBeforeUserAction(); 1656 model_->MoveCursorTo(last_drag_location_, true); 1657 if (aggregated_clicks_ == 1) { 1658 model_->SelectWord(); 1659 // Expand the selection so the initially selected word remains selected. 1660 gfx::Range selection = GetRenderText()->selection(); 1661 const size_t min = std::min(selection.GetMin(), 1662 double_click_word_.GetMin()); 1663 const size_t max = std::max(selection.GetMax(), 1664 double_click_word_.GetMax()); 1665 const bool reversed = selection.is_reversed(); 1666 selection.set_start(reversed ? max : min); 1667 selection.set_end(reversed ? min : max); 1668 model_->SelectRange(selection); 1669 } 1670 UpdateAfterChange(false, true); 1671 OnAfterUserAction(); 1672} 1673 1674void Textfield::OnCaretBoundsChanged() { 1675 if (GetInputMethod()) 1676 GetInputMethod()->OnCaretBoundsChanged(this); 1677 if (touch_selection_controller_) 1678 touch_selection_controller_->SelectionChanged(); 1679} 1680 1681void Textfield::OnBeforeUserAction() { 1682 DCHECK(!performing_user_action_); 1683 performing_user_action_ = true; 1684 if (controller_) 1685 controller_->OnBeforeUserAction(this); 1686} 1687 1688void Textfield::OnAfterUserAction() { 1689 if (controller_) 1690 controller_->OnAfterUserAction(this); 1691 DCHECK(performing_user_action_); 1692 performing_user_action_ = false; 1693} 1694 1695bool Textfield::Cut() { 1696 if (!read_only() && text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD && 1697 model_->Cut()) { 1698 if (controller_) 1699 controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_COPY_PASTE); 1700 return true; 1701 } 1702 return false; 1703} 1704 1705bool Textfield::Copy() { 1706 if (text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD && model_->Copy()) { 1707 if (controller_) 1708 controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_COPY_PASTE); 1709 return true; 1710 } 1711 return false; 1712} 1713 1714bool Textfield::Paste() { 1715 if (!read_only() && model_->Paste()) { 1716 if (controller_) 1717 controller_->OnAfterPaste(); 1718 return true; 1719 } 1720 return false; 1721} 1722 1723void Textfield::UpdateContextMenu() { 1724 if (!context_menu_contents_.get()) { 1725 context_menu_contents_.reset(new ui::SimpleMenuModel(this)); 1726 context_menu_contents_->AddItemWithStringId(IDS_APP_UNDO, IDS_APP_UNDO); 1727 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR); 1728 context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT); 1729 context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); 1730 context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE); 1731 context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE); 1732 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR); 1733 context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL, 1734 IDS_APP_SELECT_ALL); 1735 if (controller_) 1736 controller_->UpdateContextMenu(context_menu_contents_.get()); 1737 } 1738 context_menu_runner_.reset( 1739 new MenuRunner(context_menu_contents_.get(), 1740 MenuRunner::HAS_MNEMONICS | MenuRunner::CONTEXT_MENU)); 1741} 1742 1743void Textfield::TrackMouseClicks(const ui::MouseEvent& event) { 1744 if (event.IsOnlyLeftMouseButton()) { 1745 base::TimeDelta time_delta = event.time_stamp() - last_click_time_; 1746 if (time_delta.InMilliseconds() <= GetDoubleClickInterval() && 1747 !ExceededDragThreshold(event.location() - last_click_location_)) { 1748 // Upon clicking after a triple click, the count should go back to double 1749 // click and alternate between double and triple. This assignment maps 1750 // 0 to 1, 1 to 2, 2 to 1. 1751 aggregated_clicks_ = (aggregated_clicks_ % 2) + 1; 1752 } else { 1753 aggregated_clicks_ = 0; 1754 } 1755 last_click_time_ = event.time_stamp(); 1756 last_click_location_ = event.location(); 1757 } 1758} 1759 1760bool Textfield::ImeEditingAllowed() const { 1761 // Disallow input method editing of password fields. 1762 ui::TextInputType t = GetTextInputType(); 1763 return (t != ui::TEXT_INPUT_TYPE_NONE && t != ui::TEXT_INPUT_TYPE_PASSWORD); 1764} 1765 1766void Textfield::RevealPasswordChar(int index) { 1767 GetRenderText()->SetObscuredRevealIndex(index); 1768 SchedulePaint(); 1769 1770 if (index != -1) { 1771 password_reveal_timer_.Start(FROM_HERE, password_reveal_duration_, 1772 base::Bind(&Textfield::RevealPasswordChar, 1773 weak_ptr_factory_.GetWeakPtr(), -1)); 1774 } 1775} 1776 1777void Textfield::CreateTouchSelectionControllerAndNotifyIt() { 1778 if (!HasFocus()) 1779 return; 1780 1781 if (!touch_selection_controller_) { 1782 touch_selection_controller_.reset( 1783 ui::TouchSelectionController::create(this)); 1784 } 1785 if (touch_selection_controller_) 1786 touch_selection_controller_->SelectionChanged(); 1787} 1788 1789void Textfield::UpdateSelectionClipboard() const { 1790#if defined(OS_LINUX) && !defined(OS_CHROMEOS) 1791 if (performing_user_action_ && HasSelection()) { 1792 ui::ScopedClipboardWriter( 1793 ui::CLIPBOARD_TYPE_SELECTION).WriteText(GetSelectedText()); 1794 if (controller_) 1795 controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_SELECTION); 1796 } 1797#endif 1798} 1799 1800void Textfield::PasteSelectionClipboard(const ui::MouseEvent& event) { 1801 DCHECK(event.IsOnlyMiddleMouseButton()); 1802 DCHECK(!read_only()); 1803 base::string16 selection_clipboard_text = GetSelectionClipboardText(); 1804 if (!selection_clipboard_text.empty()) { 1805 OnBeforeUserAction(); 1806 gfx::Range range = GetSelectionModel().selection(); 1807 gfx::LogicalCursorDirection affinity = GetSelectionModel().caret_affinity(); 1808 const gfx::SelectionModel mouse = 1809 GetRenderText()->FindCursorPosition(event.location()); 1810 model_->MoveCursorTo(mouse); 1811 model_->InsertText(selection_clipboard_text); 1812 // Update the new selection range as needed. 1813 if (range.GetMin() >= mouse.caret_pos()) { 1814 const size_t length = selection_clipboard_text.length(); 1815 range = gfx::Range(range.start() + length, range.end() + length); 1816 } 1817 model_->MoveCursorTo(gfx::SelectionModel(range, affinity)); 1818 UpdateAfterChange(true, true); 1819 OnAfterUserAction(); 1820 } 1821} 1822 1823} // namespace views 1824