Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2010 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 "views/controls/textfield/textfield_view_model.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/i18n/word_iterator.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/utf_string_conversions.h" | |
| 12 #include "gfx/font.h" | |
| 13 | |
| 14 namespace views { | |
| 15 | |
| 16 TextfieldViewModel::TextfieldViewModel() | |
| 17 : cursor_pos_(0), | |
| 18 begin_(0), | |
| 19 end_(0), | |
| 20 is_password_(false) { | |
| 21 } | |
| 22 | |
| 23 TextfieldViewModel::~TextfieldViewModel() { | |
| 24 } | |
| 25 | |
| 26 void TextfieldViewModel::PopulateFragments(TextFragments* fragments) const { | |
| 27 DCHECK(fragments); | |
| 28 fragments->clear(); | |
| 29 if (HasSelection()) { | |
| 30 int begin = std::min(begin_, end_); | |
| 31 int end = std::max(begin_, end_); | |
| 32 if (begin != 0) { | |
| 33 fragments->push_back(TextFragment(0, begin, false)); | |
| 34 } | |
| 35 fragments->push_back(TextFragment(begin, end, true)); | |
| 36 int len = text_.length(); | |
| 37 if (end != len) { | |
| 38 fragments->push_back(TextFragment(end, len, false)); | |
| 39 } | |
| 40 } else { | |
| 41 fragments->push_back(TextFragment(0, text_.length(), false)); | |
| 42 } | |
| 43 } | |
| 44 | |
| 45 bool TextfieldViewModel::SetText(const string16& text) { | |
| 46 bool changed = text_ != text; | |
| 47 if (changed) { | |
| 48 text_ = text; | |
| 49 if (cursor_pos_ > text.length()) { | |
| 50 cursor_pos_ = text.length(); | |
| 51 } | |
| 52 } | |
| 53 ClearSelection(); | |
| 54 return changed; | |
| 55 } | |
| 56 | |
| 57 void TextfieldViewModel::Insert(char16 c) { | |
| 58 if (HasSelection()) | |
| 59 DeleteSelection(); | |
| 60 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-
| |
| 61 cursor_pos_++; | |
| 62 } | |
| 63 | |
| 64 void TextfieldViewModel::Replace(char16 c) { | |
| 65 if (!HasSelection()) | |
| 66 Delete(); | |
| 67 Insert(c); | |
| 68 } | |
| 69 | |
| 70 void TextfieldViewModel::Append(const string16& text) { | |
| 71 text_ += text; | |
| 72 } | |
| 73 | |
| 74 bool TextfieldViewModel::Delete() { | |
| 75 if (HasSelection()) { | |
| 76 DeleteSelection(); | |
| 77 return true; | |
| 78 } else if (text_.length() > cursor_pos_) { | |
| 79 text_.erase(cursor_pos_, 1); | |
| 80 return true; | |
| 81 } | |
| 82 return false; | |
| 83 } | |
| 84 | |
| 85 bool TextfieldViewModel::Backspace() { | |
| 86 if (HasSelection()) { | |
| 87 DeleteSelection(); | |
| 88 return true; | |
| 89 } else if(cursor_pos_ > 0) { | |
| 90 cursor_pos_--; | |
| 91 text_.erase(cursor_pos_, 1); | |
| 92 return true; | |
| 93 } | |
| 94 return false; | |
| 95 } | |
| 96 | |
| 97 void TextfieldViewModel::MoveCursorLeft(bool select) { | |
| 98 // support bidi | |
| 99 int old = cursor_pos_; | |
| 100 if (cursor_pos_ > 0) | |
| 101 cursor_pos_--; | |
| 102 if (select) | |
| 103 Select(old, cursor_pos_); | |
| 104 else | |
| 105 ClearSelection(); | |
| 106 } | |
| 107 | |
| 108 void TextfieldViewModel::MoveCursorRight(bool select) { | |
| 109 int old = cursor_pos_; | |
| 110 // support bidi | |
| 111 cursor_pos_ = std::min(text_.length(), cursor_pos_ + 1); | |
| 112 if (select) | |
| 113 Select(old, cursor_pos_); | |
| 114 else | |
| 115 ClearSelection(); | |
| 116 } | |
| 117 | |
| 118 void TextfieldViewModel::MoveCursorToPreviousWord(bool select) { | |
| 119 // Notes: We always iterate words from the begining. | |
| 120 // This is probably fast enough for our usage, but we may | |
| 121 // want to modify WordIterator so that it can start from the | |
| 122 // middle of string and adnvace backwards. | |
|
sky
2010/12/15 20:31:27
adnvace -> advance
oshima
2010/12/16 01:15:19
Done.
| |
| 123 WordIterator iter(&text_, WordIterator::BREAK_WORD); | |
| 124 bool success = iter.Init(); | |
| 125 DCHECK(success); | |
| 126 if (!success) | |
| 127 return; | |
| 128 int prev = 0; | |
| 129 while (iter.Advance()) { | |
| 130 if (iter.IsWord()) { | |
| 131 int old = cursor_pos_; | |
| 132 size_t begin = iter.pos() - iter.GetWord().length(); | |
| 133 if (begin == cursor_pos_) { | |
| 134 // The cursor is at the beginning of a word. | |
| 135 // Move to previous word. | |
| 136 cursor_pos_ = prev; | |
| 137 } else if(iter.pos() >= cursor_pos_) { | |
| 138 // The cursor is in the middle or at the end of a word. | |
| 139 // Move to the top of current word. | |
| 140 cursor_pos_ = begin; | |
| 141 } else { | |
| 142 prev = iter.pos() - iter.GetWord().length(); | |
| 143 continue; | |
| 144 } | |
| 145 if (select) | |
| 146 Select(old, cursor_pos_); | |
| 147 else | |
| 148 ClearSelection(); | |
| 149 break; | |
| 150 } | |
| 151 } | |
| 152 } | |
| 153 | |
| 154 void TextfieldViewModel::MoveCursorToNextWord(bool select) { | |
| 155 WordIterator iter(&text_, WordIterator::BREAK_WORD); | |
| 156 bool success = iter.Init(); | |
| 157 DCHECK(success); | |
| 158 if (!success) | |
| 159 return; | |
| 160 while (iter.Advance()) { | |
| 161 if (iter.IsWord() && iter.pos() > cursor_pos_) { | |
| 162 int old = cursor_pos_; | |
| 163 cursor_pos_ = iter.pos(); | |
| 164 if (select) | |
| 165 Select(old, cursor_pos_); | |
| 166 else | |
| 167 ClearSelection(); | |
| 168 break; | |
| 169 } | |
| 170 } | |
| 171 } | |
| 172 | |
| 173 void TextfieldViewModel::MoveCursorToStart(bool select) { | |
| 174 int old = cursor_pos_; | |
| 175 cursor_pos_ = 0; | |
| 176 if (select) | |
| 177 Select(old, cursor_pos_); | |
| 178 else | |
| 179 ClearSelection(); | |
| 180 } | |
| 181 | |
| 182 void TextfieldViewModel::MoveCursorToEnd(bool select) { | |
| 183 int old = cursor_pos_; | |
| 184 cursor_pos_ = text_.length(); | |
| 185 if (select) | |
| 186 Select(old, cursor_pos_); | |
| 187 else | |
| 188 ClearSelection(); | |
| 189 } | |
| 190 | |
| 191 bool TextfieldViewModel::MoveCursorTo(size_t pos, bool select) { | |
| 192 if (cursor_pos_ != pos) { | |
| 193 if (select) | |
| 194 Select(cursor_pos_, pos); | |
| 195 else | |
| 196 ClearSelection(); | |
| 197 cursor_pos_ = pos; | |
| 198 return true; | |
| 199 } | |
| 200 return false; | |
| 201 } | |
| 202 | |
| 203 gfx::Rect TextfieldViewModel::GetCursorBounds(const gfx::Font& font) const { | |
| 204 string16 text = GetVisibleText(); | |
| 205 int x = font.GetStringWidth(UTF16ToWide(text.substr(0U, cursor_pos_))); | |
| 206 int h = font.GetHeight(); | |
| 207 DCHECK(x >= 0); | |
| 208 if (text.length() == cursor_pos_) { | |
| 209 return gfx::Rect(x, 0, 0, h); | |
| 210 } else { | |
| 211 int x_end = | |
| 212 font.GetStringWidth(UTF16ToWide(text.substr(0U, cursor_pos_ + 1U))); | |
| 213 return gfx::Rect(x, 0, x_end - x, h); | |
| 214 } | |
| 215 } | |
| 216 | |
| 217 string16 TextfieldViewModel::GetSelectedText() const { | |
| 218 return text_.substr(std::min(begin_, end_), | |
| 219 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
| |
| 220 } | |
| 221 | |
| 222 void TextfieldViewModel::SelectAll() { | |
| 223 begin_ = 0; | |
| 224 end_ = text_.length(); | |
|
sky
2010/12/15 20:31:27
Select(0, text_.length())
oshima
2010/12/16 01:15:19
Done.
| |
| 225 } | |
| 226 | |
| 227 void TextfieldViewModel::ClearSelection() { | |
| 228 begin_ = end_ = cursor_pos_; | |
| 229 } | |
| 230 | |
| 231 void TextfieldViewModel::Select(size_t old, size_t end) { | |
| 232 if (old == end) return; // no action | |
| 233 | |
| 234 DCHECK(0 <= old && old <= text_.length()); | |
| 235 DCHECK(0 <= end && old <= text_.length()); | |
| 236 if (!HasSelection()) { | |
| 237 // first time | |
| 238 begin_ = old; | |
| 239 end_ = end; | |
| 240 } else { | |
| 241 end_ = end; | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 bool TextfieldViewModel::HasSelection() const { | |
| 246 return begin_ != end_; | |
| 247 } | |
| 248 | |
| 249 void TextfieldViewModel::DeleteSelection() { | |
| 250 DCHECK(HasSelection()); | |
| 251 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.
| |
| 252 size_t begin = std::min(begin_, end_); | |
| 253 text_.erase(begin, n); | |
| 254 cursor_pos_ = begin; | |
| 255 ClearSelection(); | |
| 256 } | |
| 257 | |
| 258 string16 TextfieldViewModel::GetVisibleText(size_t begin, size_t end) const { | |
| 259 DCHECK(end >= begin); | |
| 260 if (is_password_) { | |
| 261 return string16(end - begin, '*'); | |
| 262 } | |
| 263 return text_.substr(begin, end - begin); | |
| 264 } | |
| 265 | |
| 266 } // namespace views | |
| OLD | NEW |