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 |