| 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 |