| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/views/controls/textfield/textfield_views_model.h" | 5 #include "ui/views/controls/textfield/textfield_views_model.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/i18n/break_iterator.h" | 9 #include "base/i18n/break_iterator.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/stl_util.h" | 11 #include "base/stl_util.h" |
| 12 #include "base/utf_offset_string_conversions.h" |
| 12 #include "base/utf_string_conversions.h" | 13 #include "base/utf_string_conversions.h" |
| 13 #include "ui/base/clipboard/clipboard.h" | 14 #include "ui/base/clipboard/clipboard.h" |
| 14 #include "ui/base/clipboard/scoped_clipboard_writer.h" | 15 #include "ui/base/clipboard/scoped_clipboard_writer.h" |
| 15 #include "ui/base/range/range.h" | 16 #include "ui/base/range/range.h" |
| 16 #include "ui/gfx/canvas.h" | 17 #include "ui/gfx/canvas.h" |
| 17 #include "ui/gfx/font.h" | 18 #include "ui/gfx/font.h" |
| 18 #include "ui/gfx/render_text.h" | 19 #include "ui/gfx/render_text.h" |
| 19 #include "ui/views/controls/textfield/textfield.h" | 20 #include "ui/views/controls/textfield/textfield.h" |
| 20 #include "ui/views/views_delegate.h" | 21 #include "ui/views/views_delegate.h" |
| 21 | 22 |
| 22 namespace views { | 23 namespace views { |
| 23 | 24 |
| 24 namespace internal { | 25 namespace internal { |
| 25 | 26 |
| 27 // All chars are replaced by this char when the password style is set. |
| 28 // TODO(benrg): GTK uses the first of U+25CF, U+2022, U+2731, U+273A, '*' |
| 29 // that's available in the font (find_invisible_char() in gtkentry.c). |
| 30 const char16 kPasswordReplacementChar = '*'; |
| 31 |
| 26 // An edit object holds enough information/state to undo/redo the | 32 // An edit object holds enough information/state to undo/redo the |
| 27 // change. Two edits are merged when possible, for example, when | 33 // change. Two edits are merged when possible, for example, when |
| 28 // you type new characters in sequence. |Commit()| can be used to | 34 // you type new characters in sequence. |Commit()| can be used to |
| 29 // mark an edit as an independent edit and it shouldn't be merged. | 35 // mark an edit as an independent edit and it shouldn't be merged. |
| 30 // (For example, when you did undo/redo, or a text is appended via | 36 // (For example, when you did undo/redo, or a text is appended via |
| 31 // API) | 37 // API) |
| 32 class Edit { | 38 class Edit { |
| 33 public: | 39 public: |
| 34 enum Type { | 40 enum Type { |
| 35 INSERT_EDIT, | 41 INSERT_EDIT, |
| (...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 282 : delegate_(delegate), | 288 : delegate_(delegate), |
| 283 render_text_(gfx::RenderText::CreateRenderText()), | 289 render_text_(gfx::RenderText::CreateRenderText()), |
| 284 current_edit_(edit_history_.end()) { | 290 current_edit_(edit_history_.end()) { |
| 285 } | 291 } |
| 286 | 292 |
| 287 TextfieldViewsModel::~TextfieldViewsModel() { | 293 TextfieldViewsModel::~TextfieldViewsModel() { |
| 288 ClearEditHistory(); | 294 ClearEditHistory(); |
| 289 ClearComposition(); | 295 ClearComposition(); |
| 290 } | 296 } |
| 291 | 297 |
| 292 const string16& TextfieldViewsModel::GetText() const { | |
| 293 return render_text_->text(); | |
| 294 } | |
| 295 | |
| 296 bool TextfieldViewsModel::SetText(const string16& text) { | 298 bool TextfieldViewsModel::SetText(const string16& text) { |
| 297 bool changed = false; | 299 bool changed = false; |
| 298 if (HasCompositionText()) { | 300 if (HasCompositionText()) { |
| 299 ConfirmCompositionText(); | 301 ConfirmCompositionText(); |
| 300 changed = true; | 302 changed = true; |
| 301 } | 303 } |
| 302 if (GetText() != text) { | 304 if (GetText() != text) { |
| 303 if (changed) // No need to remember composition. | 305 if (changed) // No need to remember composition. |
| 304 Undo(); | 306 Undo(); |
| 305 size_t old_cursor = GetCursorPosition(); | 307 size_t old_cursor = GetCursorPosition(); |
| 306 size_t new_cursor = old_cursor > text.length() ? text.length() : old_cursor; | 308 size_t new_cursor = old_cursor > text.length() ? text.length() : old_cursor; |
| 307 SelectAll(); | 309 SelectAll(); |
| 308 // If there is a composition text, don't merge with previous edit. | 310 // If there is a composition text, don't merge with previous edit. |
| 309 // Otherwise, force merge the edits. | 311 // Otherwise, force merge the edits. |
| 310 ExecuteAndRecordReplace( | 312 ExecuteAndRecordReplace( |
| 311 changed ? DO_NOT_MERGE : MERGE_WITH_PREVIOUS, | 313 changed ? DO_NOT_MERGE : MERGE_WITH_PREVIOUS, |
| 312 old_cursor, | 314 old_cursor, |
| 313 new_cursor, | 315 new_cursor, |
| 314 text, | 316 text, |
| 315 0U); | 317 0U); |
| 316 render_text_->SetCursorPosition(new_cursor); | 318 render_text_->SetCursorPosition(ToDisplayIndex(new_cursor)); |
| 317 } | 319 } |
| 318 ClearSelection(); | 320 ClearSelection(); |
| 319 return changed; | 321 return changed; |
| 320 } | 322 } |
| 321 | 323 |
| 324 void TextfieldViewsModel::SetObscured(bool obscured) { |
| 325 if (obscured != obscured_) { |
| 326 obscured_ = obscured; |
| 327 render_text_->SetText(GetDisplayText()); |
| 328 } |
| 329 } |
| 330 |
| 322 void TextfieldViewsModel::Append(const string16& text) { | 331 void TextfieldViewsModel::Append(const string16& text) { |
| 323 if (HasCompositionText()) | 332 if (HasCompositionText()) |
| 324 ConfirmCompositionText(); | 333 ConfirmCompositionText(); |
| 325 size_t save = GetCursorPosition(); | 334 size_t save = render_text_->GetCursorPosition(); |
| 326 MoveCursor(gfx::LINE_BREAK, | 335 MoveCursor(gfx::LINE_BREAK, |
| 327 render_text_->GetVisualDirectionOfLogicalEnd(), | 336 render_text_->GetVisualDirectionOfLogicalEnd(), |
| 328 false); | 337 false); |
| 329 InsertText(text); | 338 InsertText(text); |
| 330 render_text_->SetCursorPosition(save); | 339 render_text_->SetCursorPosition(save); |
| 331 ClearSelection(); | 340 ClearSelection(); |
| 332 } | 341 } |
| 333 | 342 |
| 334 bool TextfieldViewsModel::Delete() { | 343 bool TextfieldViewsModel::Delete() { |
| 335 if (HasCompositionText()) { | 344 if (HasCompositionText()) { |
| 336 // No undo/redo for composition text. | 345 // No undo/redo for composition text. |
| 337 CancelCompositionText(); | 346 CancelCompositionText(); |
| 338 return true; | 347 return true; |
| 339 } | 348 } |
| 340 if (HasSelection()) { | 349 if (HasSelection()) { |
| 341 DeleteSelection(); | 350 DeleteSelection(); |
| 342 return true; | 351 return true; |
| 343 } | 352 } |
| 344 if (GetText().length() > GetCursorPosition()) { | 353 if (GetText().length() > GetCursorPosition()) { |
| 345 size_t cursor_position = GetCursorPosition(); | 354 size_t cursor_position = GetCursorPosition(); |
| 346 size_t next_grapheme_index = render_text_->IndexOfAdjacentGrapheme( | 355 size_t next_grapheme_index = |
| 347 cursor_position, gfx::CURSOR_FORWARD); | 356 FromDisplayIndex(render_text_->IndexOfAdjacentGrapheme( |
| 357 ToDisplayIndex(cursor_position), gfx::CURSOR_FORWARD)); |
| 348 ExecuteAndRecordDelete(cursor_position, next_grapheme_index, true); | 358 ExecuteAndRecordDelete(cursor_position, next_grapheme_index, true); |
| 349 return true; | 359 return true; |
| 350 } | 360 } |
| 351 return false; | 361 return false; |
| 352 } | 362 } |
| 353 | 363 |
| 354 bool TextfieldViewsModel::Backspace() { | 364 bool TextfieldViewsModel::Backspace() { |
| 355 if (HasCompositionText()) { | 365 if (HasCompositionText()) { |
| 356 // No undo/redo for composition text. | 366 // No undo/redo for composition text. |
| 357 CancelCompositionText(); | 367 CancelCompositionText(); |
| 358 return true; | 368 return true; |
| 359 } | 369 } |
| 360 if (HasSelection()) { | 370 if (HasSelection()) { |
| 361 DeleteSelection(); | 371 DeleteSelection(); |
| 362 return true; | 372 return true; |
| 363 } | 373 } |
| 364 if (GetCursorPosition() > 0) { | 374 size_t cursor_position = GetCursorPosition(); |
| 365 size_t cursor_position = GetCursorPosition(); | 375 if (cursor_position > 0) { |
| 366 ExecuteAndRecordDelete(cursor_position, cursor_position - 1, true); | 376 size_t previous_char = Utf16OffsetToIndex(GetText(), cursor_position, -1); |
| 377 ExecuteAndRecordDelete(cursor_position, previous_char, true); |
| 367 return true; | 378 return true; |
| 368 } | 379 } |
| 369 return false; | 380 return false; |
| 370 } | 381 } |
| 371 | 382 |
| 372 size_t TextfieldViewsModel::GetCursorPosition() const { | 383 size_t TextfieldViewsModel::GetCursorPosition() const { |
| 373 return render_text_->GetCursorPosition(); | 384 return FromDisplayIndex(render_text_->GetCursorPosition()); |
| 374 } | 385 } |
| 375 | 386 |
| 376 void TextfieldViewsModel::MoveCursor(gfx::BreakType break_type, | 387 void TextfieldViewsModel::MoveCursor(gfx::BreakType break_type, |
| 377 gfx::VisualCursorDirection direction, | 388 gfx::VisualCursorDirection direction, |
| 378 bool select) { | 389 bool select) { |
| 379 if (HasCompositionText()) | 390 if (HasCompositionText()) |
| 380 ConfirmCompositionText(); | 391 ConfirmCompositionText(); |
| 381 render_text_->MoveCursor(break_type, direction, select); | 392 render_text_->MoveCursor(break_type, direction, select); |
| 382 } | 393 } |
| 383 | 394 |
| 384 bool TextfieldViewsModel::MoveCursorTo(const gfx::SelectionModel& selection) { | 395 bool TextfieldViewsModel::MoveCursorTo(const gfx::SelectionModel& sel) { |
| 396 gfx::SelectionModel selection = ToDisplaySelection(sel); |
| 385 if (HasCompositionText()) { | 397 if (HasCompositionText()) { |
| 386 ConfirmCompositionText(); | 398 ConfirmCompositionText(); |
| 387 // ConfirmCompositionText() updates cursor position. Need to reflect it in | 399 // ConfirmCompositionText() updates cursor position. Need to reflect it in |
| 388 // the SelectionModel parameter of MoveCursorTo(). | 400 // the SelectionModel parameter of MoveCursorTo(). |
| 389 if (render_text_->GetSelectionStart() != selection.selection_end()) | 401 if (render_text_->GetSelectionStart() != selection.selection_end()) |
| 390 return render_text_->SelectRange(ui::Range( | 402 return render_text_->SelectRange(ui::Range( |
| 391 render_text_->GetSelectionStart(), selection.selection_end())); | 403 render_text_->GetSelectionStart(), selection.selection_end())); |
| 392 gfx::SelectionModel sel(selection.selection_end(), | 404 gfx::SelectionModel sel(selection.selection_end(), |
| 393 selection.caret_pos(), | 405 selection.caret_pos(), |
| 394 selection.caret_placement()); | 406 selection.caret_placement()); |
| 395 return render_text_->MoveCursorTo(sel); | 407 return render_text_->MoveCursorTo(sel); |
| 396 } | 408 } |
| 397 return render_text_->MoveCursorTo(selection); | 409 return render_text_->MoveCursorTo(selection); |
| 398 } | 410 } |
| 399 | 411 |
| 400 bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) { | 412 bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) { |
| 401 if (HasCompositionText()) | 413 if (HasCompositionText()) |
| 402 ConfirmCompositionText(); | 414 ConfirmCompositionText(); |
| 403 return render_text_->MoveCursorTo(point, select); | 415 return render_text_->MoveCursorTo(point, select); |
| 404 } | 416 } |
| 405 | 417 |
| 406 string16 TextfieldViewsModel::GetSelectedText() const { | 418 string16 TextfieldViewsModel::GetSelectedText() const { |
| 407 return GetText().substr(render_text_->MinOfSelection(), | 419 size_t lo = FromDisplayIndex(render_text_->MinOfSelection()); |
| 408 (render_text_->MaxOfSelection() - render_text_->MinOfSelection())); | 420 size_t hi = FromDisplayIndex(render_text_->MaxOfSelection()); |
| 421 return GetText().substr(lo, hi - lo); |
| 409 } | 422 } |
| 410 | 423 |
| 411 void TextfieldViewsModel::GetSelectedRange(ui::Range* range) const { | 424 void TextfieldViewsModel::GetSelectedRange(ui::Range* range) const { |
| 412 range->set_start(render_text_->GetSelectionStart()); | 425 *range = FromDisplayRange(ui::Range( |
| 413 range->set_end(render_text_->GetCursorPosition()); | 426 render_text_->GetSelectionStart(), render_text_->GetCursorPosition())); |
| 414 } | 427 } |
| 415 | 428 |
| 416 void TextfieldViewsModel::SelectRange(const ui::Range& range) { | 429 void TextfieldViewsModel::SelectRange(const ui::Range& range) { |
| 417 if (HasCompositionText()) | 430 if (HasCompositionText()) |
| 418 ConfirmCompositionText(); | 431 ConfirmCompositionText(); |
| 419 render_text_->SelectRange(range); | 432 render_text_->SelectRange(ToDisplayRange(range)); |
| 420 } | 433 } |
| 421 | 434 |
| 422 void TextfieldViewsModel::GetSelectionModel(gfx::SelectionModel* sel) const { | 435 void TextfieldViewsModel::GetSelectionModel(gfx::SelectionModel* sel) const { |
| 423 *sel = render_text_->selection_model(); | 436 *sel = FromDisplaySelection(render_text_->selection_model()); |
| 424 } | 437 } |
| 425 | 438 |
| 426 void TextfieldViewsModel::SelectSelectionModel(const gfx::SelectionModel& sel) { | 439 void TextfieldViewsModel::SelectSelectionModel(const gfx::SelectionModel& sel) { |
| 427 if (HasCompositionText()) | 440 if (HasCompositionText()) |
| 428 ConfirmCompositionText(); | 441 ConfirmCompositionText(); |
| 429 render_text_->MoveCursorTo(sel); | 442 render_text_->MoveCursorTo(ToDisplaySelection(sel)); |
| 430 } | 443 } |
| 431 | 444 |
| 432 void TextfieldViewsModel::SelectAll() { | 445 void TextfieldViewsModel::SelectAll() { |
| 433 if (HasCompositionText()) | 446 if (HasCompositionText()) |
| 434 ConfirmCompositionText(); | 447 ConfirmCompositionText(); |
| 435 render_text_->SelectAll(); | 448 render_text_->SelectAll(); |
| 436 } | 449 } |
| 437 | 450 |
| 438 void TextfieldViewsModel::SelectWord() { | 451 void TextfieldViewsModel::SelectWord() { |
| 439 if (HasCompositionText()) | 452 if (HasCompositionText()) |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 491 current_edit_ = edit_history_.begin(); | 504 current_edit_ = edit_history_.begin(); |
| 492 else | 505 else |
| 493 current_edit_ ++; | 506 current_edit_ ++; |
| 494 string16 old = GetText(); | 507 string16 old = GetText(); |
| 495 size_t old_cursor = GetCursorPosition(); | 508 size_t old_cursor = GetCursorPosition(); |
| 496 (*current_edit_)->Redo(this); | 509 (*current_edit_)->Redo(this); |
| 497 return old != GetText() || old_cursor != GetCursorPosition(); | 510 return old != GetText() || old_cursor != GetCursorPosition(); |
| 498 } | 511 } |
| 499 | 512 |
| 500 bool TextfieldViewsModel::Cut() { | 513 bool TextfieldViewsModel::Cut() { |
| 501 if (!HasCompositionText() && HasSelection()) { | 514 if (!HasCompositionText() && HasSelection() && !is_obscured()) { |
| 502 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate | 515 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate |
| 503 ->GetClipboard()).WriteText(GetSelectedText()); | 516 ->GetClipboard()).WriteText(GetSelectedText()); |
| 504 // A trick to let undo/redo handle cursor correctly. | 517 // A trick to let undo/redo handle cursor correctly. |
| 505 // Undoing CUT moves the cursor to the end of the change rather | 518 // Undoing CUT moves the cursor to the end of the change rather |
| 506 // than beginning, unlike Delete/Backspace. | 519 // than beginning, unlike Delete/Backspace. |
| 507 // TODO(oshima): Change Delete/Backspace to use DeleteSelection, | 520 // TODO(oshima): Change Delete/Backspace to use DeleteSelection, |
| 508 // update DeleteEdit and remove this trick. | 521 // update DeleteEdit and remove this trick. |
| 509 render_text_->SelectRange(ui::Range(render_text_->GetCursorPosition(), | 522 render_text_->SelectRange(ui::Range(render_text_->GetCursorPosition(), |
| 510 render_text_->GetSelectionStart())); | 523 render_text_->GetSelectionStart())); |
| 511 DeleteSelection(); | 524 DeleteSelection(); |
| 512 return true; | 525 return true; |
| 513 } | 526 } |
| 514 return false; | 527 return false; |
| 515 } | 528 } |
| 516 | 529 |
| 517 bool TextfieldViewsModel::Copy() { | 530 bool TextfieldViewsModel::Copy() { |
| 518 if (!HasCompositionText() && HasSelection()) { | 531 if (!HasCompositionText() && HasSelection() && !is_obscured()) { |
| 519 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate | 532 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate |
| 520 ->GetClipboard()).WriteText(GetSelectedText()); | 533 ->GetClipboard()).WriteText(GetSelectedText()); |
| 521 return true; | 534 return true; |
| 522 } | 535 } |
| 523 return false; | 536 return false; |
| 524 } | 537 } |
| 525 | 538 |
| 526 bool TextfieldViewsModel::Paste() { | 539 bool TextfieldViewsModel::Paste() { |
| 527 string16 result; | 540 string16 result; |
| 528 views::ViewsDelegate::views_delegate->GetClipboard() | 541 views::ViewsDelegate::views_delegate->GetClipboard() |
| 529 ->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); | 542 ->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); |
| 530 if (!result.empty()) { | 543 if (!result.empty()) { |
| 531 InsertTextInternal(result, false); | 544 InsertTextInternal(result, false); |
| 532 return true; | 545 return true; |
| 533 } | 546 } |
| 534 return false; | 547 return false; |
| 535 } | 548 } |
| 536 | 549 |
| 537 bool TextfieldViewsModel::HasSelection() const { | 550 bool TextfieldViewsModel::HasSelection() const { |
| 538 return !render_text_->EmptySelection(); | 551 return !render_text_->EmptySelection(); |
| 539 } | 552 } |
| 540 | 553 |
| 541 void TextfieldViewsModel::DeleteSelection() { | 554 void TextfieldViewsModel::DeleteSelection() { |
| 542 DCHECK(!HasCompositionText()); | 555 DCHECK(!HasCompositionText()); |
| 543 DCHECK(HasSelection()); | 556 DCHECK(HasSelection()); |
| 544 ExecuteAndRecordDelete(render_text_->GetSelectionStart(), | 557 ExecuteAndRecordDelete(FromDisplayIndex(render_text_->GetSelectionStart()), |
| 545 render_text_->GetCursorPosition(), false); | 558 FromDisplayIndex(render_text_->GetCursorPosition()), |
| 559 false); |
| 546 } | 560 } |
| 547 | 561 |
| 548 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( | 562 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( |
| 549 const string16& text, size_t position) { | 563 const string16& text, size_t position) { |
| 550 if (HasCompositionText()) | 564 if (HasCompositionText()) |
| 551 CancelCompositionText(); | 565 CancelCompositionText(); |
| 552 ExecuteAndRecordReplace(DO_NOT_MERGE, | 566 ExecuteAndRecordReplace(DO_NOT_MERGE, |
| 553 GetCursorPosition(), | 567 GetCursorPosition(), |
| 554 position + text.length(), | 568 position + text.length(), |
| 555 text, | 569 text, |
| (...skipping 15 matching lines...) Expand all Loading... |
| 571 if (HasCompositionText()) | 585 if (HasCompositionText()) |
| 572 CancelCompositionText(); | 586 CancelCompositionText(); |
| 573 else if (HasSelection()) | 587 else if (HasSelection()) |
| 574 DeleteSelection(); | 588 DeleteSelection(); |
| 575 | 589 |
| 576 if (composition.text.empty()) | 590 if (composition.text.empty()) |
| 577 return; | 591 return; |
| 578 | 592 |
| 579 size_t cursor = GetCursorPosition(); | 593 size_t cursor = GetCursorPosition(); |
| 580 string16 new_text = GetText(); | 594 string16 new_text = GetText(); |
| 581 render_text_->SetText(new_text.insert(cursor, composition.text)); | 595 SetTextInternal(new_text.insert(cursor, composition.text)); |
| 582 ui::Range range(cursor, cursor + composition.text.length()); | 596 ui::Range range(cursor, cursor + composition.text.length()); |
| 583 render_text_->SetCompositionRange(range); | 597 render_text_->SetCompositionRange(ToDisplayRange(range)); |
| 584 // TODO(msw): Support multiple composition underline ranges. | 598 // TODO(msw): Support multiple composition underline ranges. |
| 585 | 599 |
| 586 if (composition.selection.IsValid()) { | 600 if (composition.selection.IsValid()) { |
| 587 size_t start = | 601 size_t start = |
| 588 std::min(range.start() + composition.selection.start(), range.end()); | 602 std::min(range.start() + composition.selection.start(), range.end()); |
| 589 size_t end = | 603 size_t end = |
| 590 std::min(range.start() + composition.selection.end(), range.end()); | 604 std::min(range.start() + composition.selection.end(), range.end()); |
| 591 render_text_->SelectRange(ui::Range(start, end)); | 605 render_text_->SelectRange(ToDisplayRange(ui::Range(start, end))); |
| 592 } else { | 606 } else { |
| 593 render_text_->SetCursorPosition(range.end()); | 607 render_text_->SetCursorPosition(ToDisplayIndex(range.end())); |
| 594 } | 608 } |
| 595 } | 609 } |
| 596 | 610 |
| 597 void TextfieldViewsModel::ConfirmCompositionText() { | 611 void TextfieldViewsModel::ConfirmCompositionText() { |
| 598 DCHECK(HasCompositionText()); | 612 DCHECK(HasCompositionText()); |
| 599 ui::Range range = render_text_->GetCompositionRange(); | 613 ui::Range range = FromDisplayRange(render_text_->GetCompositionRange()); |
| 600 string16 text = GetText().substr(range.start(), range.length()); | 614 string16 text = GetText().substr(range.start(), range.length()); |
| 601 // TODO(oshima): current behavior on ChromeOS is a bit weird and not | 615 // TODO(oshima): current behavior on ChromeOS is a bit weird and not |
| 602 // sure exactly how this should work. Find out and fix if necessary. | 616 // sure exactly how this should work. Find out and fix if necessary. |
| 603 AddOrMergeEditHistory(new InsertEdit(false, text, range.start())); | 617 AddOrMergeEditHistory(new InsertEdit(false, text, range.start())); |
| 604 render_text_->SetCursorPosition(range.end()); | 618 render_text_->SetCursorPosition(ToDisplayIndex(range.end())); |
| 605 ClearComposition(); | 619 ClearComposition(); |
| 606 if (delegate_) | 620 if (delegate_) |
| 607 delegate_->OnCompositionTextConfirmedOrCleared(); | 621 delegate_->OnCompositionTextConfirmedOrCleared(); |
| 608 } | 622 } |
| 609 | 623 |
| 610 void TextfieldViewsModel::CancelCompositionText() { | 624 void TextfieldViewsModel::CancelCompositionText() { |
| 611 DCHECK(HasCompositionText()); | 625 DCHECK(HasCompositionText()); |
| 612 ui::Range range = render_text_->GetCompositionRange(); | 626 ui::Range range = FromDisplayRange(render_text_->GetCompositionRange()); |
| 613 ClearComposition(); | 627 ClearComposition(); |
| 614 string16 new_text = GetText(); | 628 string16 new_text = GetText(); |
| 615 render_text_->SetText(new_text.erase(range.start(), range.length())); | 629 SetTextInternal(new_text.erase(range.start(), range.length())); |
| 616 render_text_->SetCursorPosition(range.start()); | 630 render_text_->SetCursorPosition(ToDisplayIndex(range.start())); |
| 617 if (delegate_) | 631 if (delegate_) |
| 618 delegate_->OnCompositionTextConfirmedOrCleared(); | 632 delegate_->OnCompositionTextConfirmedOrCleared(); |
| 619 } | 633 } |
| 620 | 634 |
| 621 void TextfieldViewsModel::ClearComposition() { | 635 void TextfieldViewsModel::ClearComposition() { |
| 622 render_text_->SetCompositionRange(ui::Range::InvalidRange()); | 636 render_text_->SetCompositionRange(ui::Range::InvalidRange()); |
| 623 } | 637 } |
| 624 | 638 |
| 625 void TextfieldViewsModel::GetCompositionTextRange(ui::Range* range) const { | 639 void TextfieldViewsModel::GetCompositionTextRange(ui::Range* range) const { |
| 626 *range = ui::Range(render_text_->GetCompositionRange()); | 640 *range = ui::Range(FromDisplayRange(render_text_->GetCompositionRange())); |
| 627 } | 641 } |
| 628 | 642 |
| 629 bool TextfieldViewsModel::HasCompositionText() const { | 643 bool TextfieldViewsModel::HasCompositionText() const { |
| 630 return !render_text_->GetCompositionRange().is_empty(); | 644 return !render_text_->GetCompositionRange().is_empty(); |
| 631 } | 645 } |
| 632 | 646 |
| 633 ///////////////////////////////////////////////////////////////// | 647 ///////////////////////////////////////////////////////////////// |
| 634 // TextfieldViewsModel: private | 648 // TextfieldViewsModel: private |
| 635 | 649 |
| 636 void TextfieldViewsModel::InsertTextInternal(const string16& text, | 650 void TextfieldViewsModel::InsertTextInternal(const string16& text, |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 695 bool backward = from > to; | 709 bool backward = from > to; |
| 696 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); | 710 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); |
| 697 bool delete_edit = AddOrMergeEditHistory(edit); | 711 bool delete_edit = AddOrMergeEditHistory(edit); |
| 698 edit->Redo(this); | 712 edit->Redo(this); |
| 699 if (delete_edit) | 713 if (delete_edit) |
| 700 delete edit; | 714 delete edit; |
| 701 } | 715 } |
| 702 | 716 |
| 703 void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( | 717 void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( |
| 704 MergeType merge_type, const string16& new_text) { | 718 MergeType merge_type, const string16& new_text) { |
| 705 size_t new_text_start = render_text_->MinOfSelection(); | 719 size_t new_text_start = FromDisplayIndex(render_text_->MinOfSelection()); |
| 706 size_t new_cursor_pos = new_text_start + new_text.length(); | 720 size_t new_cursor_pos = new_text_start + new_text.length(); |
| 707 ExecuteAndRecordReplace(merge_type, | 721 ExecuteAndRecordReplace(merge_type, |
| 708 GetCursorPosition(), | 722 GetCursorPosition(), |
| 709 new_cursor_pos, | 723 new_cursor_pos, |
| 710 new_text, | 724 new_text, |
| 711 new_text_start); | 725 new_text_start); |
| 712 } | 726 } |
| 713 | 727 |
| 714 void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type, | 728 void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type, |
| 715 size_t old_cursor_pos, | 729 size_t old_cursor_pos, |
| 716 size_t new_cursor_pos, | 730 size_t new_cursor_pos, |
| 717 const string16& new_text, | 731 const string16& new_text, |
| 718 size_t new_text_start) { | 732 size_t new_text_start) { |
| 719 size_t old_text_start = render_text_->MinOfSelection(); | 733 size_t old_text_start = FromDisplayIndex(render_text_->MinOfSelection()); |
| 720 bool backward = | 734 bool backward = |
| 721 render_text_->GetSelectionStart() > render_text_->GetCursorPosition(); | 735 render_text_->GetSelectionStart() > render_text_->GetCursorPosition(); |
| 722 Edit* edit = new ReplaceEdit(merge_type, | 736 Edit* edit = new ReplaceEdit(merge_type, |
| 723 GetSelectedText(), | 737 GetSelectedText(), |
| 724 old_cursor_pos, | 738 old_cursor_pos, |
| 725 old_text_start, | 739 old_text_start, |
| 726 backward, | 740 backward, |
| 727 new_cursor_pos, | 741 new_cursor_pos, |
| 728 new_text, | 742 new_text, |
| 729 new_text_start); | 743 new_text_start); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 765 | 779 |
| 766 void TextfieldViewsModel::ModifyText(size_t delete_from, | 780 void TextfieldViewsModel::ModifyText(size_t delete_from, |
| 767 size_t delete_to, | 781 size_t delete_to, |
| 768 const string16& new_text, | 782 const string16& new_text, |
| 769 size_t new_text_insert_at, | 783 size_t new_text_insert_at, |
| 770 size_t new_cursor_pos) { | 784 size_t new_cursor_pos) { |
| 771 DCHECK_LE(delete_from, delete_to); | 785 DCHECK_LE(delete_from, delete_to); |
| 772 string16 text = GetText(); | 786 string16 text = GetText(); |
| 773 ClearComposition(); | 787 ClearComposition(); |
| 774 if (delete_from != delete_to) | 788 if (delete_from != delete_to) |
| 775 render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); | 789 SetTextInternal(text.erase(delete_from, delete_to - delete_from)); |
| 776 if (!new_text.empty()) | 790 if (!new_text.empty()) |
| 777 render_text_->SetText(text.insert(new_text_insert_at, new_text)); | 791 SetTextInternal(text.insert(new_text_insert_at, new_text)); |
| 778 render_text_->SetCursorPosition(new_cursor_pos); | 792 render_text_->SetCursorPosition(ToDisplayIndex(new_cursor_pos)); |
| 779 // TODO(oshima): mac selects the text that is just undone (but gtk doesn't). | 793 // TODO(oshima): mac selects the text that is just undone (but gtk doesn't). |
| 780 // This looks fine feature and we may want to do the same. | 794 // This looks fine feature and we may want to do the same. |
| 781 } | 795 } |
| 782 | 796 |
| 797 void TextfieldViewsModel::SetTextInternal(const string16& new_text) { |
| 798 text_ = new_text; |
| 799 render_text_->SetText(GetDisplayText()); |
| 800 } |
| 801 |
| 802 string16 TextfieldViewsModel::GetDisplayText() const { |
| 803 if (!obscured_) |
| 804 return text_; |
| 805 size_t obscured_text_length = |
| 806 static_cast<size_t>(Utf16IndexToOffset(text_, 0, text_.length())); |
| 807 return string16(obscured_text_length, internal::kPasswordReplacementChar); |
| 808 } |
| 809 |
| 810 size_t TextfieldViewsModel::FromDisplayIndex(size_t index) const { |
| 811 if (!is_obscured()) |
| 812 return index; |
| 813 return Utf16OffsetToIndex(GetText(), 0, index); |
| 814 } |
| 815 size_t TextfieldViewsModel::ToDisplayIndex(size_t index) const { |
| 816 if (!is_obscured()) |
| 817 return index; |
| 818 return Utf16IndexToOffset(GetText(), 0, index); |
| 819 } |
| 820 ui::Range TextfieldViewsModel::FromDisplayRange(ui::Range range) const { |
| 821 if (!is_obscured() || !range.IsValid()) |
| 822 return range; |
| 823 return ui::Range(FromDisplayIndex(range.start()), |
| 824 FromDisplayIndex(range.end())); |
| 825 } |
| 826 ui::Range TextfieldViewsModel::ToDisplayRange(ui::Range range) const { |
| 827 if (!is_obscured() || !range.IsValid()) |
| 828 return range; |
| 829 return ui::Range(ToDisplayIndex(range.start()), |
| 830 ToDisplayIndex(range.end())); |
| 831 } |
| 832 gfx::SelectionModel TextfieldViewsModel::FromDisplaySelection( |
| 833 const gfx::SelectionModel& model) const { |
| 834 return gfx::SelectionModel(FromDisplayIndex(model.selection_start()), |
| 835 FromDisplayIndex(model.selection_end()), |
| 836 FromDisplayIndex(model.caret_pos()), |
| 837 model.caret_placement()); |
| 838 } |
| 839 gfx::SelectionModel TextfieldViewsModel::ToDisplaySelection( |
| 840 const gfx::SelectionModel& model) const { |
| 841 return gfx::SelectionModel(ToDisplayIndex(model.selection_start()), |
| 842 ToDisplayIndex(model.selection_end()), |
| 843 ToDisplayIndex(model.caret_pos()), |
| 844 model.caret_placement()); |
| 845 } |
| 846 |
| 783 } // namespace views | 847 } // namespace views |
| OLD | NEW |