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