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