OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "views/controls/textfield/textfield_views_model.h" | 5 #include "views/controls/textfield/textfield_views_model.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/i18n/break_iterator.h" | 9 #include "base/i18n/break_iterator.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/utf_string_conversions.h" | 11 #include "base/utf_string_conversions.h" |
12 #include "ui/base/clipboard/clipboard.h" | 12 #include "ui/base/clipboard/clipboard.h" |
13 #include "ui/base/clipboard/scoped_clipboard_writer.h" | 13 #include "ui/base/clipboard/scoped_clipboard_writer.h" |
14 #include "ui/base/range/range.h" | 14 #include "ui/base/range/range.h" |
15 #include "ui/gfx/font.h" | 15 #include "ui/gfx/font.h" |
16 #include "views/controls/textfield/textfield.h" | 16 #include "views/controls/textfield/textfield.h" |
17 #include "views/views_delegate.h" | 17 #include "views/views_delegate.h" |
18 | 18 |
19 namespace views { | 19 namespace views { |
20 | 20 |
21 TextfieldViewsModel::TextfieldViewsModel() | 21 TextfieldViewsModel::TextfieldViewsModel(Delegate* delegate) |
22 : cursor_pos_(0), | 22 : delegate_(delegate), |
23 selection_begin_(0), | 23 cursor_pos_(0), |
24 selection_start_(0), | |
25 composition_start_(0), | |
26 composition_end_(0), | |
24 is_password_(false) { | 27 is_password_(false) { |
25 } | 28 } |
26 | 29 |
27 TextfieldViewsModel::~TextfieldViewsModel() { | 30 TextfieldViewsModel::~TextfieldViewsModel() { |
28 } | 31 } |
29 | 32 |
30 void TextfieldViewsModel::GetFragments(TextFragments* fragments) const { | 33 void TextfieldViewsModel::GetFragments(TextFragments* fragments) const { |
31 DCHECK(fragments); | 34 DCHECK(fragments); |
32 fragments->clear(); | 35 fragments->clear(); |
33 if (HasSelection()) { | 36 if (HasComposition()) { |
34 int begin = std::min(selection_begin_, cursor_pos_); | 37 if (composition_start_) |
35 int end = std::max(selection_begin_, cursor_pos_); | 38 fragments->push_back(TextFragment(0, composition_start_, false, false)); |
36 if (begin != 0) { | 39 |
37 fragments->push_back(TextFragment(0, begin, false)); | 40 size_t selection_start = std::min(selection_start_, cursor_pos_); |
41 size_t selection_end = std::max(selection_start_, cursor_pos_); | |
42 size_t last_end = composition_start_; | |
43 for (ui::CompositionUnderlines::const_iterator i = | |
44 composition_underlines_.begin(); | |
45 i != composition_underlines_.end(); ++i) { | |
46 size_t fragment_start = | |
47 std::min(i->start_offset, i->end_offset) + composition_start_; | |
48 size_t fragment_end = | |
49 std::max(i->start_offset, i->end_offset) + composition_start_; | |
50 | |
51 fragment_start = std::max(last_end, fragment_start); | |
52 fragment_end = std::min(fragment_end, composition_end_); | |
53 | |
54 if (fragment_start >= fragment_end) | |
55 break; | |
56 | |
57 // If there is no selection, then just add a text fragment with underline. | |
58 if (selection_start == selection_end) { | |
59 if (last_end < fragment_start) { | |
60 fragments->push_back( | |
61 TextFragment(last_end, fragment_start, false, false)); | |
62 } | |
63 fragments->push_back( | |
64 TextFragment(fragment_start, fragment_end, false, true)); | |
65 last_end = fragment_end; | |
66 continue; | |
67 } | |
68 | |
69 size_t end = std::min(fragment_start, selection_start); | |
70 if (last_end < end) | |
71 fragments->push_back(TextFragment(last_end, end, false, false)); | |
72 | |
73 last_end = fragment_end; | |
74 | |
75 if (selection_start < fragment_start) { | |
76 end = std::min(selection_end, fragment_start); | |
77 fragments->push_back(TextFragment(selection_start, end, true, false)); | |
78 selection_start = end; | |
79 } else if (selection_start > fragment_start) { | |
80 end = std::min(selection_start, fragment_end); | |
81 fragments->push_back(TextFragment(fragment_start, end, false, true)); | |
82 fragment_start = end; | |
83 if (fragment_start == fragment_end) | |
84 continue; | |
85 } | |
86 | |
87 if (fragment_start < selection_end) { | |
88 DCHECK_EQ(selection_start, fragment_start); | |
89 end = std::min(fragment_end, selection_end); | |
90 fragments->push_back(TextFragment(fragment_start, end, true, true)); | |
91 fragment_start = end; | |
92 selection_start = end; | |
93 if (fragment_start == fragment_end) | |
94 continue; | |
95 } | |
96 | |
97 DCHECK_LT(fragment_start, fragment_end); | |
98 fragments->push_back( | |
99 TextFragment(fragment_start, fragment_end, false, true)); | |
38 } | 100 } |
39 fragments->push_back(TextFragment(begin, end, true)); | 101 |
40 int len = text_.length(); | 102 if (last_end < composition_end_) { |
41 if (end != len) { | 103 if (selection_start < selection_end) { |
42 fragments->push_back(TextFragment(end, len, false)); | 104 DCHECK_LE(last_end, selection_start); |
105 if (last_end < selection_start) { | |
106 fragments->push_back( | |
107 TextFragment(last_end, selection_start, false, false)); | |
108 } | |
109 fragments->push_back( | |
110 TextFragment(selection_start, selection_end, true, false)); | |
111 if (selection_end < composition_end_) { | |
112 fragments->push_back( | |
113 TextFragment(selection_end, composition_end_, false, false)); | |
114 } | |
115 } else { | |
116 fragments->push_back( | |
117 TextFragment(last_end, composition_end_, false, false)); | |
118 } | |
43 } | 119 } |
120 | |
121 size_t len = text_.length(); | |
122 if (composition_end_ < len) | |
123 fragments->push_back(TextFragment(composition_end_, len, false, false)); | |
124 } else if (HasSelection()) { | |
125 size_t start = std::min(selection_start_, cursor_pos_); | |
126 size_t end = std::max(selection_start_, cursor_pos_); | |
127 if (start) | |
128 fragments->push_back(TextFragment(0, start, false, false)); | |
129 fragments->push_back(TextFragment(start, end, true, false)); | |
130 size_t len = text_.length(); | |
131 if (end != len) | |
132 fragments->push_back(TextFragment(end, len, false, false)); | |
44 } else { | 133 } else { |
45 fragments->push_back(TextFragment(0, text_.length(), false)); | 134 fragments->push_back(TextFragment(0, text_.length(), false, false)); |
46 } | 135 } |
47 } | 136 } |
48 | 137 |
49 bool TextfieldViewsModel::SetText(const string16& text) { | 138 bool TextfieldViewsModel::SetText(const string16& text) { |
50 bool changed = text_ != text; | 139 bool changed = false; |
51 if (changed) { | 140 if (HasComposition()) { |
141 ConfirmComposition(); | |
142 changed = true; | |
143 } | |
144 if (text_ != text) { | |
52 text_ = text; | 145 text_ = text; |
53 if (cursor_pos_ > text.length()) { | 146 if (cursor_pos_ > text.length()) { |
54 cursor_pos_ = text.length(); | 147 cursor_pos_ = text.length(); |
55 } | 148 } |
149 changed = true; | |
56 } | 150 } |
57 ClearSelection(); | 151 ClearSelection(); |
58 return changed; | 152 return changed; |
59 } | 153 } |
60 | 154 |
61 void TextfieldViewsModel::Insert(char16 c) { | 155 void TextfieldViewsModel::InsertText(const string16& text) { |
62 if (HasSelection()) | 156 if (HasComposition()) |
157 ClearComposition(); | |
158 else if (HasSelection()) | |
63 DeleteSelection(); | 159 DeleteSelection(); |
64 text_.insert(cursor_pos_, 1, c); | 160 text_.insert(cursor_pos_, text); |
65 cursor_pos_++; | 161 cursor_pos_ += text.size(); |
66 ClearSelection(); | 162 ClearSelection(); |
67 } | 163 } |
68 | 164 |
69 void TextfieldViewsModel::Replace(char16 c) { | 165 void TextfieldViewsModel::ReplaceText(const string16& text) { |
70 if (!HasSelection()) | 166 if (HasComposition()) |
71 Delete(); | 167 ClearComposition(); |
72 Insert(c); | 168 else if (!HasSelection()) |
169 SelectRange(ui::Range(cursor_pos_, cursor_pos_ + text.length())); | |
170 InsertText(text); | |
73 } | 171 } |
74 | 172 |
75 void TextfieldViewsModel::Append(const string16& text) { | 173 void TextfieldViewsModel::Append(const string16& text) { |
174 if (HasComposition()) | |
175 ConfirmComposition(); | |
76 text_ += text; | 176 text_ += text; |
77 } | 177 } |
78 | 178 |
79 bool TextfieldViewsModel::Delete() { | 179 bool TextfieldViewsModel::Delete() { |
180 if (HasComposition()) { | |
181 ClearComposition(); | |
182 return true; | |
183 } | |
80 if (HasSelection()) { | 184 if (HasSelection()) { |
81 DeleteSelection(); | 185 DeleteSelection(); |
82 return true; | 186 return true; |
83 } else if (text_.length() > cursor_pos_) { | 187 } |
188 if (text_.length() > cursor_pos_) { | |
84 text_.erase(cursor_pos_, 1); | 189 text_.erase(cursor_pos_, 1); |
85 return true; | 190 return true; |
86 } | 191 } |
87 return false; | 192 return false; |
88 } | 193 } |
89 | 194 |
90 bool TextfieldViewsModel::Backspace() { | 195 bool TextfieldViewsModel::Backspace() { |
196 if (HasComposition()) { | |
197 ClearComposition(); | |
198 return true; | |
199 } | |
91 if (HasSelection()) { | 200 if (HasSelection()) { |
92 DeleteSelection(); | 201 DeleteSelection(); |
93 return true; | 202 return true; |
94 } else if (cursor_pos_ > 0) { | 203 } |
204 if (cursor_pos_ > 0) { | |
95 cursor_pos_--; | 205 cursor_pos_--; |
96 text_.erase(cursor_pos_, 1); | 206 text_.erase(cursor_pos_, 1); |
97 ClearSelection(); | 207 ClearSelection(); |
98 return true; | 208 return true; |
99 } | 209 } |
100 return false; | 210 return false; |
101 } | 211 } |
102 | 212 |
103 void TextfieldViewsModel::MoveCursorLeft(bool select) { | 213 void TextfieldViewsModel::MoveCursorLeft(bool select) { |
214 if (HasComposition()) | |
215 ConfirmComposition(); | |
104 // TODO(oshima): support BIDI | 216 // TODO(oshima): support BIDI |
105 if (select) { | 217 if (select) { |
106 if (cursor_pos_ > 0) | 218 if (cursor_pos_ > 0) |
107 cursor_pos_--; | 219 cursor_pos_--; |
108 } else { | 220 } else { |
109 if (HasSelection()) | 221 if (HasSelection()) |
110 cursor_pos_ = std::min(cursor_pos_, selection_begin_); | 222 cursor_pos_ = std::min(cursor_pos_, selection_start_); |
111 else if (cursor_pos_ > 0) | 223 else if (cursor_pos_ > 0) |
112 cursor_pos_--; | 224 cursor_pos_--; |
113 ClearSelection(); | 225 ClearSelection(); |
114 } | 226 } |
115 } | 227 } |
116 | 228 |
117 void TextfieldViewsModel::MoveCursorRight(bool select) { | 229 void TextfieldViewsModel::MoveCursorRight(bool select) { |
230 if (HasComposition()) | |
231 ConfirmComposition(); | |
118 // TODO(oshima): support BIDI | 232 // TODO(oshima): support BIDI |
119 if (select) { | 233 if (select) { |
120 cursor_pos_ = std::min(text_.length(), cursor_pos_ + 1); | 234 cursor_pos_ = std::min(text_.length(), cursor_pos_ + 1); |
121 } else { | 235 } else { |
122 if (HasSelection()) | 236 if (HasSelection()) |
123 cursor_pos_ = std::max(cursor_pos_, selection_begin_); | 237 cursor_pos_ = std::max(cursor_pos_, selection_start_); |
124 else | 238 else |
125 cursor_pos_ = std::min(text_.length(), cursor_pos_ + 1); | 239 cursor_pos_ = std::min(text_.length(), cursor_pos_ + 1); |
126 ClearSelection(); | 240 ClearSelection(); |
127 } | 241 } |
128 } | 242 } |
129 | 243 |
130 void TextfieldViewsModel::MoveCursorToPreviousWord(bool select) { | 244 void TextfieldViewsModel::MoveCursorToPreviousWord(bool select) { |
245 if (HasComposition()) | |
246 ConfirmComposition(); | |
131 // Notes: We always iterate words from the begining. | 247 // Notes: We always iterate words from the begining. |
132 // This is probably fast enough for our usage, but we may | 248 // This is probably fast enough for our usage, but we may |
133 // want to modify WordIterator so that it can start from the | 249 // want to modify WordIterator so that it can start from the |
134 // middle of string and advance backwards. | 250 // middle of string and advance backwards. |
135 base::BreakIterator iter(&text_, base::BreakIterator::BREAK_WORD); | 251 base::BreakIterator iter(&text_, base::BreakIterator::BREAK_WORD); |
136 bool success = iter.Init(); | 252 bool success = iter.Init(); |
137 DCHECK(success); | 253 DCHECK(success); |
138 if (!success) | 254 if (!success) |
139 return; | 255 return; |
140 int last = 0; | 256 int last = 0; |
(...skipping 14 matching lines...) Expand all Loading... | |
155 } | 271 } |
156 } | 272 } |
157 } | 273 } |
158 | 274 |
159 cursor_pos_ = last; | 275 cursor_pos_ = last; |
160 if (!select) | 276 if (!select) |
161 ClearSelection(); | 277 ClearSelection(); |
162 } | 278 } |
163 | 279 |
164 void TextfieldViewsModel::MoveCursorToNextWord(bool select) { | 280 void TextfieldViewsModel::MoveCursorToNextWord(bool select) { |
281 if (HasComposition()) | |
282 ConfirmComposition(); | |
165 base::BreakIterator iter(&text_, base::BreakIterator::BREAK_WORD); | 283 base::BreakIterator iter(&text_, base::BreakIterator::BREAK_WORD); |
166 bool success = iter.Init(); | 284 bool success = iter.Init(); |
167 DCHECK(success); | 285 DCHECK(success); |
168 if (!success) | 286 if (!success) |
169 return; | 287 return; |
170 size_t pos = 0; | 288 size_t pos = 0; |
171 while (iter.Advance()) { | 289 while (iter.Advance()) { |
172 pos = iter.pos(); | 290 pos = iter.pos(); |
173 if (iter.IsWord() && pos > cursor_pos_) { | 291 if (iter.IsWord() && pos > cursor_pos_) { |
174 break; | 292 break; |
175 } | 293 } |
176 } | 294 } |
177 cursor_pos_ = pos; | 295 cursor_pos_ = pos; |
178 if (!select) | 296 if (!select) |
179 ClearSelection(); | 297 ClearSelection(); |
180 } | 298 } |
181 | 299 |
182 void TextfieldViewsModel::MoveCursorToStart(bool select) { | 300 void TextfieldViewsModel::MoveCursorToHome(bool select) { |
301 if (HasComposition()) | |
302 ConfirmComposition(); | |
183 cursor_pos_ = 0; | 303 cursor_pos_ = 0; |
184 if (!select) | 304 if (!select) |
185 ClearSelection(); | 305 ClearSelection(); |
186 } | 306 } |
187 | 307 |
188 void TextfieldViewsModel::MoveCursorToEnd(bool select) { | 308 void TextfieldViewsModel::MoveCursorToEnd(bool select) { |
309 if (HasComposition()) | |
310 ConfirmComposition(); | |
189 cursor_pos_ = text_.length(); | 311 cursor_pos_ = text_.length(); |
190 if (!select) | 312 if (!select) |
191 ClearSelection(); | 313 ClearSelection(); |
192 } | 314 } |
193 | 315 |
194 bool TextfieldViewsModel::MoveCursorTo(size_t pos, bool select) { | 316 bool TextfieldViewsModel::MoveCursorTo(size_t pos, bool select) { |
317 if (HasComposition()) | |
318 ConfirmComposition(); | |
195 bool cursor_changed = false; | 319 bool cursor_changed = false; |
196 if (cursor_pos_ != pos) { | 320 if (cursor_pos_ != pos) { |
197 cursor_pos_ = pos; | 321 cursor_pos_ = pos; |
198 cursor_changed = true; | 322 cursor_changed = true; |
199 } | 323 } |
200 if (!select) | 324 if (!select) |
201 ClearSelection(); | 325 ClearSelection(); |
202 return cursor_changed; | 326 return cursor_changed; |
203 } | 327 } |
204 | 328 |
205 gfx::Rect TextfieldViewsModel::GetCursorBounds(const gfx::Font& font) const { | 329 gfx::Rect TextfieldViewsModel::GetCursorBounds(const gfx::Font& font) const { |
206 string16 text = GetVisibleText(); | 330 string16 text = GetVisibleText(); |
207 int x = font.GetStringWidth(text.substr(0U, cursor_pos_)); | 331 int x = font.GetStringWidth(text.substr(0U, cursor_pos_)); |
208 int h = font.GetHeight(); | 332 int h = font.GetHeight(); |
209 DCHECK(x >= 0); | 333 DCHECK(x >= 0); |
210 if (text.length() == cursor_pos_) { | 334 if (text.length() == cursor_pos_) { |
211 return gfx::Rect(x, 0, 0, h); | 335 return gfx::Rect(x, 0, 0, h); |
212 } else { | 336 } else { |
213 int x_end = font.GetStringWidth(text.substr(0U, cursor_pos_ + 1U)); | 337 int x_end = font.GetStringWidth(text.substr(0U, cursor_pos_ + 1U)); |
214 return gfx::Rect(x, 0, x_end - x, h); | 338 return gfx::Rect(x, 0, x_end - x, h); |
215 } | 339 } |
216 } | 340 } |
217 | 341 |
218 string16 TextfieldViewsModel::GetSelectedText() const { | 342 string16 TextfieldViewsModel::GetSelectedText() const { |
219 return text_.substr( | 343 return text_.substr( |
220 std::min(cursor_pos_, selection_begin_), | 344 std::min(cursor_pos_, selection_start_), |
221 std::abs(static_cast<long>(cursor_pos_ - selection_begin_))); | 345 std::abs(static_cast<long>(cursor_pos_ - selection_start_))); |
222 } | 346 } |
223 | 347 |
224 void TextfieldViewsModel::GetSelectedRange(ui::Range* range) const { | 348 void TextfieldViewsModel::GetSelectedRange(ui::Range* range) const { |
225 *range = ui::Range(selection_begin_, cursor_pos_); | 349 *range = ui::Range(selection_start_, cursor_pos_); |
226 } | 350 } |
227 | 351 |
228 void TextfieldViewsModel::SelectRange(const ui::Range& range) { | 352 void TextfieldViewsModel::SelectRange(const ui::Range& range) { |
229 selection_begin_ = GetSafePosition(range.start()); | 353 if (HasComposition()) |
354 ConfirmComposition(); | |
355 selection_start_ = GetSafePosition(range.start()); | |
230 cursor_pos_ = GetSafePosition(range.end()); | 356 cursor_pos_ = GetSafePosition(range.end()); |
231 } | 357 } |
232 | 358 |
233 void TextfieldViewsModel::SelectAll() { | 359 void TextfieldViewsModel::SelectAll() { |
360 if (HasComposition()) | |
361 ConfirmComposition(); | |
234 // SelectAll selects towards the end. | 362 // SelectAll selects towards the end. |
235 cursor_pos_ = text_.length(); | 363 cursor_pos_ = text_.length(); |
236 selection_begin_ = 0; | 364 selection_start_ = 0; |
237 } | 365 } |
238 | 366 |
239 void TextfieldViewsModel::SelectWord() { | 367 void TextfieldViewsModel::SelectWord() { |
240 // First we setup selection_begin_ and cursor_pos_. There are so many cases | 368 if (HasComposition()) |
369 ConfirmComposition(); | |
370 // First we setup selection_start_ and cursor_pos_. There are so many cases | |
241 // because we try to emulate what select-word looks like in a gtk textfield. | 371 // because we try to emulate what select-word looks like in a gtk textfield. |
242 // See associated testcase for different cases. | 372 // See associated testcase for different cases. |
243 if (cursor_pos_ > 0 && cursor_pos_ < text_.length()) { | 373 if (cursor_pos_ > 0 && cursor_pos_ < text_.length()) { |
244 if (isalnum(text_[cursor_pos_])) { | 374 if (isalnum(text_[cursor_pos_])) { |
245 selection_begin_ = cursor_pos_; | 375 selection_start_ = cursor_pos_; |
246 cursor_pos_++; | 376 cursor_pos_++; |
247 } else | 377 } else |
248 selection_begin_ = cursor_pos_ - 1; | 378 selection_start_ = cursor_pos_ - 1; |
249 } else if (cursor_pos_ == 0) { | 379 } else if (cursor_pos_ == 0) { |
250 selection_begin_ = cursor_pos_; | 380 selection_start_ = cursor_pos_; |
251 if (text_.length() > 0) | 381 if (text_.length() > 0) |
252 cursor_pos_++; | 382 cursor_pos_++; |
253 } else { | 383 } else { |
254 selection_begin_ = cursor_pos_ - 1; | 384 selection_start_ = cursor_pos_ - 1; |
255 } | 385 } |
256 | 386 |
257 // Now we move selection_begin_ to beginning of selection. Selection boundary | 387 // Now we move selection_start_ to beginning of selection. Selection boundary |
258 // is defined as the position where we have alpha-num character on one side | 388 // is defined as the position where we have alpha-num character on one side |
259 // and non-alpha-num char on the other side. | 389 // and non-alpha-num char on the other side. |
260 for (; selection_begin_ > 0; selection_begin_--) { | 390 for (; selection_start_ > 0; selection_start_--) { |
261 if (IsPositionAtWordSelectionBoundary(selection_begin_)) | 391 if (IsPositionAtWordSelectionBoundary(selection_start_)) |
262 break; | 392 break; |
263 } | 393 } |
264 | 394 |
265 // Now we move cursor_pos_ to end of selection. Selection boundary | 395 // Now we move cursor_pos_ to end of selection. Selection boundary |
266 // is defined as the position where we have alpha-num character on one side | 396 // is defined as the position where we have alpha-num character on one side |
267 // and non-alpha-num char on the other side. | 397 // and non-alpha-num char on the other side. |
268 for (; cursor_pos_ < text_.length(); cursor_pos_++) { | 398 for (; cursor_pos_ < text_.length(); cursor_pos_++) { |
269 if (IsPositionAtWordSelectionBoundary(cursor_pos_)) | 399 if (IsPositionAtWordSelectionBoundary(cursor_pos_)) |
270 break; | 400 break; |
271 } | 401 } |
272 } | 402 } |
273 | 403 |
274 void TextfieldViewsModel::ClearSelection() { | 404 void TextfieldViewsModel::ClearSelection() { |
275 selection_begin_ = cursor_pos_; | 405 if (HasComposition()) |
406 ConfirmComposition(); | |
407 selection_start_ = cursor_pos_; | |
276 } | 408 } |
277 | 409 |
278 bool TextfieldViewsModel::Cut() { | 410 bool TextfieldViewsModel::Cut() { |
279 if (HasSelection()) { | 411 if (!HasComposition() && HasSelection()) { |
280 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate | 412 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate |
281 ->GetClipboard()).WriteText(GetSelectedText()); | 413 ->GetClipboard()).WriteText(GetSelectedText()); |
282 DeleteSelection(); | 414 DeleteSelection(); |
283 return true; | 415 return true; |
284 } | 416 } |
285 return false; | 417 return false; |
286 } | 418 } |
287 | 419 |
288 void TextfieldViewsModel::Copy() { | 420 void TextfieldViewsModel::Copy() { |
289 if (HasSelection()) { | 421 if (!HasComposition() && HasSelection()) { |
290 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate | 422 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate |
291 ->GetClipboard()).WriteText(GetSelectedText()); | 423 ->GetClipboard()).WriteText(GetSelectedText()); |
292 } | 424 } |
293 } | 425 } |
294 | 426 |
295 bool TextfieldViewsModel::Paste() { | 427 bool TextfieldViewsModel::Paste() { |
296 string16 result; | 428 string16 result; |
297 views::ViewsDelegate::views_delegate->GetClipboard() | 429 views::ViewsDelegate::views_delegate->GetClipboard() |
298 ->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); | 430 ->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); |
299 if (!result.empty()) { | 431 if (!result.empty()) { |
300 if (HasSelection()) | 432 if (HasComposition()) |
433 ConfirmComposition(); | |
434 else if (HasSelection()) | |
301 DeleteSelection(); | 435 DeleteSelection(); |
302 text_.insert(cursor_pos_, result); | 436 text_.insert(cursor_pos_, result); |
303 cursor_pos_ += result.length(); | 437 cursor_pos_ += result.length(); |
304 ClearSelection(); | 438 ClearSelection(); |
305 return true; | 439 return true; |
306 } | 440 } |
307 return false; | 441 return false; |
308 } | 442 } |
309 | 443 |
310 bool TextfieldViewsModel::HasSelection() const { | 444 bool TextfieldViewsModel::HasSelection() const { |
311 return selection_begin_ != cursor_pos_; | 445 return selection_start_ != cursor_pos_; |
312 } | 446 } |
313 | 447 |
314 void TextfieldViewsModel::DeleteSelection() { | 448 void TextfieldViewsModel::DeleteSelection() { |
449 DCHECK(!HasComposition()); | |
315 DCHECK(HasSelection()); | 450 DCHECK(HasSelection()); |
316 size_t n = std::abs(static_cast<long>(cursor_pos_ - selection_begin_)); | 451 size_t n = std::abs(static_cast<long>(cursor_pos_ - selection_start_)); |
317 size_t begin = std::min(cursor_pos_, selection_begin_); | 452 size_t begin = std::min(cursor_pos_, selection_start_); |
318 text_.erase(begin, n); | 453 text_.erase(begin, n); |
319 cursor_pos_ = begin; | 454 cursor_pos_ = begin; |
320 ClearSelection(); | 455 ClearSelection(); |
321 } | 456 } |
322 | 457 |
458 string16 TextfieldViewsModel::GetTextFromRange(const ui::Range& range) const { | |
459 if (range.IsValid() && range.GetMin() < text_.length()) | |
460 return text_.substr(range.GetMin(), range.length()); | |
461 return string16(); | |
462 } | |
463 | |
464 void TextfieldViewsModel::GetTextRange(ui::Range* range) const { | |
465 *range = ui::Range(0, text_.length()); | |
466 } | |
467 | |
468 void TextfieldViewsModel::SetComposition(const ui::Composition& composition) { | |
469 if (HasComposition()) | |
470 ClearComposition(); | |
471 else if (HasSelection()) | |
472 DeleteSelection(); | |
473 | |
474 if (composition.text.empty()) | |
475 return; | |
476 | |
477 size_t length = composition.text.length(); | |
478 text_.insert(cursor_pos_, composition.text); | |
479 composition_start_ = cursor_pos_; | |
480 composition_end_ = composition_start_ + length; | |
481 composition_underlines_ = composition.underlines; | |
482 | |
483 if (composition.selection.IsValid()) { | |
484 selection_start_ = | |
485 std::min(composition_start_ + composition.selection.start(), | |
486 composition_end_); | |
487 cursor_pos_ = | |
488 std::min(composition_start_ + composition.selection.end(), | |
489 composition_end_); | |
490 } else { | |
491 cursor_pos_ = composition_end_; | |
492 ClearSelection(); | |
493 } | |
494 } | |
495 | |
496 void TextfieldViewsModel::ConfirmComposition() { | |
497 DCHECK(HasComposition()); | |
498 cursor_pos_ = composition_end_; | |
499 composition_start_ = composition_end_ = string16::npos; | |
500 composition_underlines_.clear(); | |
501 ClearSelection(); | |
502 if (delegate_) | |
503 delegate_->OnCompositionConfirmedOrCleared(); | |
504 } | |
505 | |
506 void TextfieldViewsModel::ClearComposition() { | |
507 DCHECK(HasComposition()); | |
508 text_.erase(composition_start_, composition_end_ - composition_start_); | |
509 cursor_pos_ = composition_start_; | |
510 composition_start_ = composition_end_ = string16::npos; | |
511 composition_underlines_.clear(); | |
512 ClearSelection(); | |
513 if (delegate_) | |
514 delegate_->OnCompositionConfirmedOrCleared(); | |
515 } | |
516 | |
517 void TextfieldViewsModel::GetCompositionRange(ui::Range* range) const { | |
518 if (HasComposition()) | |
519 *range = ui::Range(composition_start_, composition_end_); | |
520 else | |
521 *range = ui::Range::InvalidRange(); | |
522 } | |
523 | |
524 bool TextfieldViewsModel::HasComposition() const { | |
525 return composition_start_ != composition_end_; | |
526 } | |
527 | |
323 string16 TextfieldViewsModel::GetVisibleText(size_t begin, size_t end) const { | 528 string16 TextfieldViewsModel::GetVisibleText(size_t begin, size_t end) const { |
324 DCHECK(end >= begin); | 529 DCHECK(end >= begin); |
325 if (is_password_) { | 530 if (is_password_) { |
326 return string16(end - begin, '*'); | 531 return string16(end - begin, '*'); |
327 } | 532 } |
oshima
2011/03/22 01:50:39
can you eliminate {} while we're at it?
James Su
2011/03/22 08:41:14
Done.
| |
328 return text_.substr(begin, end - begin); | 533 return text_.substr(begin, end - begin); |
329 } | 534 } |
330 | 535 |
331 bool TextfieldViewsModel::IsPositionAtWordSelectionBoundary(size_t pos) { | 536 bool TextfieldViewsModel::IsPositionAtWordSelectionBoundary(size_t pos) { |
332 return (isalnum(text_[pos - 1]) && !isalnum(text_[pos])) || | 537 return (isalnum(text_[pos - 1]) && !isalnum(text_[pos])) || |
333 (!isalnum(text_[pos - 1]) && isalnum(text_[pos])); | 538 (!isalnum(text_[pos - 1]) && isalnum(text_[pos])); |
334 } | 539 } |
335 | 540 |
336 size_t TextfieldViewsModel::GetSafePosition(size_t position) const { | 541 size_t TextfieldViewsModel::GetSafePosition(size_t position) const { |
337 if (position > text_.length()) { | 542 if (position > text_.length()) { |
338 return text_.length(); | 543 return text_.length(); |
339 } | 544 } |
340 return position; | 545 return position; |
341 } | 546 } |
342 | 547 |
343 } // namespace views | 548 } // namespace views |
OLD | NEW |