Chromium Code Reviews| Index: views/controls/textfield/textfield_view_model.cc |
| diff --git a/views/controls/textfield/textfield_view_model.cc b/views/controls/textfield/textfield_view_model.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..fbea8fcd0a73bee729f0cdc0640e0e959759eecb |
| --- /dev/null |
| +++ b/views/controls/textfield/textfield_view_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_view_model.h" |
| + |
| +#include <algorithm> |
| + |
| +#include "base/i18n/word_iterator.h" |
| +#include "base/logging.h" |
| +#include "base/utf_string_conversions.h" |
| +#include "gfx/font.h" |
| + |
| +namespace views { |
| + |
| +TextfieldViewModel::TextfieldViewModel() |
| + : cursor_pos_(0), |
| + begin_(0), |
| + end_(0), |
| + is_password_(false) { |
| +} |
| + |
| +TextfieldViewModel::~TextfieldViewModel() { |
| +} |
| + |
| +void TextfieldViewModel::PopulateFragments(TextFragments* fragments) const { |
| + DCHECK(fragments); |
| + fragments->clear(); |
| + if (HasSelection()) { |
| + int begin = std::min(begin_, end_); |
| + int end = std::max(begin_, 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 TextfieldViewModel::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 TextfieldViewModel::Insert(char16 c) { |
| + if (HasSelection()) |
| + DeleteSelection(); |
| + text_.insert(cursor_pos_, 1, c); |
|
sky
2010/12/15 20:31:27
Meta question. Isn't it possible for a visual char
oshima
2010/12/16 01:15:19
Yes, you're correct. This only works in UTF16/UCS-
|
| + cursor_pos_++; |
| +} |
| + |
| +void TextfieldViewModel::Replace(char16 c) { |
| + if (!HasSelection()) |
| + Delete(); |
| + Insert(c); |
| +} |
| + |
| +void TextfieldViewModel::Append(const string16& text) { |
| + text_ += text; |
| +} |
| + |
| +bool TextfieldViewModel::Delete() { |
| + if (HasSelection()) { |
| + DeleteSelection(); |
| + return true; |
| + } else if (text_.length() > cursor_pos_) { |
| + text_.erase(cursor_pos_, 1); |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +bool TextfieldViewModel::Backspace() { |
| + if (HasSelection()) { |
| + DeleteSelection(); |
| + return true; |
| + } else if(cursor_pos_ > 0) { |
| + cursor_pos_--; |
| + text_.erase(cursor_pos_, 1); |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +void TextfieldViewModel::MoveCursorLeft(bool select) { |
| + // support bidi |
| + int old = cursor_pos_; |
| + if (cursor_pos_ > 0) |
| + cursor_pos_--; |
| + if (select) |
| + Select(old, cursor_pos_); |
| + else |
| + ClearSelection(); |
| +} |
| + |
| +void TextfieldViewModel::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 TextfieldViewModel::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 adnvace backwards. |
|
sky
2010/12/15 20:31:27
adnvace -> advance
oshima
2010/12/16 01:15:19
Done.
|
| + WordIterator iter(&text_, WordIterator::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.GetWord().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.GetWord().length(); |
| + continue; |
| + } |
| + if (select) |
| + Select(old, cursor_pos_); |
| + else |
| + ClearSelection(); |
| + break; |
| + } |
| + } |
| +} |
| + |
| +void TextfieldViewModel::MoveCursorToNextWord(bool select) { |
| + WordIterator iter(&text_, WordIterator::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 TextfieldViewModel::MoveCursorToStart(bool select) { |
| + int old = cursor_pos_; |
| + cursor_pos_ = 0; |
| + if (select) |
| + Select(old, cursor_pos_); |
| + else |
| + ClearSelection(); |
| +} |
| + |
| +void TextfieldViewModel::MoveCursorToEnd(bool select) { |
| + int old = cursor_pos_; |
| + cursor_pos_ = text_.length(); |
| + if (select) |
| + Select(old, cursor_pos_); |
| + else |
| + ClearSelection(); |
| +} |
| + |
| +bool TextfieldViewModel::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 TextfieldViewModel::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 TextfieldViewModel::GetSelectedText() const { |
| + return text_.substr(std::min(begin_, end_), |
| + std::abs(static_cast<long>(end_ - begin_))); |
|
sky
2010/12/15 20:31:27
static_cast<size_t>
oshima
2010/12/16 01:15:19
size_t is unsigned. using long to make it signed f
|
| +} |
| + |
| +void TextfieldViewModel::SelectAll() { |
| + begin_ = 0; |
| + end_ = text_.length(); |
|
sky
2010/12/15 20:31:27
Select(0, text_.length())
oshima
2010/12/16 01:15:19
Done.
|
| +} |
| + |
| +void TextfieldViewModel::ClearSelection() { |
| + begin_ = end_ = cursor_pos_; |
| +} |
| + |
| +void TextfieldViewModel::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 |
| + begin_ = old; |
| + end_ = end; |
| + } else { |
| + end_ = end; |
| + } |
| +} |
| + |
| +bool TextfieldViewModel::HasSelection() const { |
| + return begin_ != end_; |
| +} |
| + |
| +void TextfieldViewModel::DeleteSelection() { |
| + DCHECK(HasSelection()); |
| + size_t n = std::abs(static_cast<long>(end_ - begin_)); |
|
sky
2010/12/15 20:31:27
static_cast<size_t>
oshima
2010/12/16 01:15:19
same here.
|
| + size_t begin = std::min(begin_, end_); |
| + text_.erase(begin, n); |
| + cursor_pos_ = begin; |
| + ClearSelection(); |
| +} |
| + |
| +string16 TextfieldViewModel::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 |