| Index: views/controls/textfield/textfield_views_model.cc
|
| diff --git a/views/controls/textfield/textfield_views_model.cc b/views/controls/textfield/textfield_views_model.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c22daefda979eeb8d1acaac577f7540f90fffdad
|
| --- /dev/null
|
| +++ b/views/controls/textfield/textfield_views_model.cc
|
| @@ -0,0 +1,266 @@
|
| +// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "views/controls/textfield/textfield_views_model.h"
|
| +
|
| +#include <algorithm>
|
| +
|
| +#include "base/i18n/break_iterator.h"
|
| +#include "base/logging.h"
|
| +#include "base/utf_string_conversions.h"
|
| +#include "gfx/font.h"
|
| +
|
| +namespace views {
|
| +
|
| +TextfieldViewsModel::TextfieldViewsModel()
|
| + : cursor_pos_(0),
|
| + selection_begin_(0),
|
| + selection_end_(0),
|
| + is_password_(false) {
|
| +}
|
| +
|
| +TextfieldViewsModel::~TextfieldViewsModel() {
|
| +}
|
| +
|
| +void TextfieldViewsModel::GetFragments(TextFragments* fragments) const {
|
| + DCHECK(fragments);
|
| + fragments->clear();
|
| + if (HasSelection()) {
|
| + int begin = std::min(selection_begin_, selection_end_);
|
| + int end = std::max(selection_begin_, selection_end_);
|
| + if (begin != 0) {
|
| + fragments->push_back(TextFragment(0, begin, false));
|
| + }
|
| + fragments->push_back(TextFragment(begin, end, true));
|
| + int len = text_.length();
|
| + if (end != len) {
|
| + fragments->push_back(TextFragment(end, len, false));
|
| + }
|
| + } else {
|
| + fragments->push_back(TextFragment(0, text_.length(), false));
|
| + }
|
| +}
|
| +
|
| +bool TextfieldViewsModel::SetText(const string16& text) {
|
| + bool changed = text_ != text;
|
| + if (changed) {
|
| + text_ = text;
|
| + if (cursor_pos_ > text.length()) {
|
| + cursor_pos_ = text.length();
|
| + }
|
| + }
|
| + ClearSelection();
|
| + return changed;
|
| +}
|
| +
|
| +void TextfieldViewsModel::Insert(char16 c) {
|
| + if (HasSelection())
|
| + DeleteSelection();
|
| + text_.insert(cursor_pos_, 1, c);
|
| + cursor_pos_++;
|
| +}
|
| +
|
| +void TextfieldViewsModel::Replace(char16 c) {
|
| + if (!HasSelection())
|
| + Delete();
|
| + Insert(c);
|
| +}
|
| +
|
| +void TextfieldViewsModel::Append(const string16& text) {
|
| + text_ += text;
|
| +}
|
| +
|
| +bool TextfieldViewsModel::Delete() {
|
| + if (HasSelection()) {
|
| + DeleteSelection();
|
| + return true;
|
| + } else if (text_.length() > cursor_pos_) {
|
| + text_.erase(cursor_pos_, 1);
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +bool TextfieldViewsModel::Backspace() {
|
| + if (HasSelection()) {
|
| + DeleteSelection();
|
| + return true;
|
| + } else if(cursor_pos_ > 0) {
|
| + cursor_pos_--;
|
| + text_.erase(cursor_pos_, 1);
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +void TextfieldViewsModel::MoveCursorLeft(bool select) {
|
| + // support bidi
|
| + int old = cursor_pos_;
|
| + if (cursor_pos_ > 0)
|
| + cursor_pos_--;
|
| + if (select)
|
| + Select(old, cursor_pos_);
|
| + else
|
| + ClearSelection();
|
| +}
|
| +
|
| +void TextfieldViewsModel::MoveCursorRight(bool select) {
|
| + int old = cursor_pos_;
|
| + // support bidi
|
| + cursor_pos_ = std::min(text_.length(), cursor_pos_ + 1);
|
| + if (select)
|
| + Select(old, cursor_pos_);
|
| + else
|
| + ClearSelection();
|
| +}
|
| +
|
| +void TextfieldViewsModel::MoveCursorToPreviousWord(bool select) {
|
| + // Notes: We always iterate words from the begining.
|
| + // This is probably fast enough for our usage, but we may
|
| + // want to modify WordIterator so that it can start from the
|
| + // middle of string and advance backwards.
|
| + base::BreakIterator iter(&text_, base::BreakIterator::BREAK_WORD);
|
| + bool success = iter.Init();
|
| + DCHECK(success);
|
| + if (!success)
|
| + return;
|
| + int prev = 0;
|
| + while (iter.Advance()) {
|
| + if (iter.IsWord()) {
|
| + int old = cursor_pos_;
|
| + size_t begin = iter.pos() - iter.GetString().length();
|
| + if (begin == cursor_pos_) {
|
| + // The cursor is at the beginning of a word.
|
| + // Move to previous word.
|
| + cursor_pos_ = prev;
|
| + } else if(iter.pos() >= cursor_pos_) {
|
| + // The cursor is in the middle or at the end of a word.
|
| + // Move to the top of current word.
|
| + cursor_pos_ = begin;
|
| + } else {
|
| + prev = iter.pos() - iter.GetString().length();
|
| + continue;
|
| + }
|
| + if (select)
|
| + Select(old, cursor_pos_);
|
| + else
|
| + ClearSelection();
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +
|
| +void TextfieldViewsModel::MoveCursorToNextWord(bool select) {
|
| + base::BreakIterator iter(&text_, base::BreakIterator::BREAK_WORD);
|
| + bool success = iter.Init();
|
| + DCHECK(success);
|
| + if (!success)
|
| + return;
|
| + while (iter.Advance()) {
|
| + if (iter.IsWord() && iter.pos() > cursor_pos_) {
|
| + int old = cursor_pos_;
|
| + cursor_pos_ = iter.pos();
|
| + if (select)
|
| + Select(old, cursor_pos_);
|
| + else
|
| + ClearSelection();
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +
|
| +void TextfieldViewsModel::MoveCursorToStart(bool select) {
|
| + int old = cursor_pos_;
|
| + cursor_pos_ = 0;
|
| + if (select)
|
| + Select(old, cursor_pos_);
|
| + else
|
| + ClearSelection();
|
| +}
|
| +
|
| +void TextfieldViewsModel::MoveCursorToEnd(bool select) {
|
| + int old = cursor_pos_;
|
| + cursor_pos_ = text_.length();
|
| + if (select)
|
| + Select(old, cursor_pos_);
|
| + else
|
| + ClearSelection();
|
| +}
|
| +
|
| +bool TextfieldViewsModel::MoveCursorTo(size_t pos, bool select) {
|
| + if (cursor_pos_ != pos) {
|
| + if (select)
|
| + Select(cursor_pos_, pos);
|
| + else
|
| + ClearSelection();
|
| + cursor_pos_ = pos;
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +gfx::Rect TextfieldViewsModel::GetCursorBounds(const gfx::Font& font) const {
|
| + string16 text = GetVisibleText();
|
| + int x = font.GetStringWidth(UTF16ToWide(text.substr(0U, cursor_pos_)));
|
| + int h = font.GetHeight();
|
| + DCHECK(x >= 0);
|
| + if (text.length() == cursor_pos_) {
|
| + return gfx::Rect(x, 0, 0, h);
|
| + } else {
|
| + int x_end =
|
| + font.GetStringWidth(UTF16ToWide(text.substr(0U, cursor_pos_ + 1U)));
|
| + return gfx::Rect(x, 0, x_end - x, h);
|
| + }
|
| +}
|
| +
|
| +string16 TextfieldViewsModel::GetSelectedText() const {
|
| + return text_.substr(
|
| + std::min(selection_begin_, selection_end_),
|
| + std::abs(static_cast<long>(selection_end_ - selection_begin_)));
|
| +}
|
| +
|
| +void TextfieldViewsModel::SelectAll() {
|
| + Select(0, text_.length());
|
| +}
|
| +
|
| +void TextfieldViewsModel::ClearSelection() {
|
| + selection_begin_ = selection_end_ = cursor_pos_;
|
| +}
|
| +
|
| +void TextfieldViewsModel::Select(size_t old, size_t end) {
|
| + if (old == end) return; // no action
|
| +
|
| + DCHECK(0 <= old && old <= text_.length());
|
| + DCHECK(0 <= end && old <= text_.length());
|
| + if (!HasSelection()) {
|
| + // first time
|
| + selection_begin_ = old;
|
| + selection_end_ = end;
|
| + } else {
|
| + selection_end_ = end;
|
| + }
|
| +}
|
| +
|
| +bool TextfieldViewsModel::HasSelection() const {
|
| + return selection_begin_ != selection_end_;
|
| +}
|
| +
|
| +void TextfieldViewsModel::DeleteSelection() {
|
| + DCHECK(HasSelection());
|
| + size_t n = std::abs(static_cast<long>(selection_end_ - selection_begin_));
|
| + size_t begin = std::min(selection_begin_, selection_end_);
|
| + text_.erase(begin, n);
|
| + cursor_pos_ = begin;
|
| + ClearSelection();
|
| +}
|
| +
|
| +string16 TextfieldViewsModel::GetVisibleText(size_t begin, size_t end) const {
|
| + DCHECK(end >= begin);
|
| + if (is_password_) {
|
| + return string16(end - begin, '*');
|
| + }
|
| + return text_.substr(begin, end - begin);
|
| +}
|
| +
|
| +} // namespace views
|
|
|