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 |