OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "ui/base/win/tsf_text_store.h" | 5 #include "ui/base/win/tsf_text_store.h" |
6 | 6 |
7 #include <OleCtl.h> | 7 #include <OleCtl.h> |
8 #include "base/logging.h" | 8 #include "base/win/scoped_variant.h" |
| 9 #include "ui/base/ime/text_input_client.h" |
| 10 #include "ui/gfx/rect.h" |
9 | 11 |
10 namespace ui { | 12 namespace ui { |
| 13 namespace { |
| 14 // We support only one view. |
| 15 const TsViewCookie kViewCookie = 1; |
| 16 } // namespace |
11 | 17 |
12 TsfTextStore::TsfTextStore() | 18 TsfTextStore::TsfTextStore() |
13 : ref_count_(0), | 19 : ref_count_(0), |
14 text_store_acp_sink_mask_(0) { | 20 text_store_acp_sink_mask_(0), |
| 21 window_handle_(NULL), |
| 22 text_input_client_(NULL), |
| 23 committed_size_(0), |
| 24 edit_flag_(false), |
| 25 current_lock_type_(0) { |
| 26 if (FAILED(category_manager_.CreateInstance(CLSID_TF_CategoryMgr))) { |
| 27 LOG(FATAL) << "Failed to initialize CategoryMgr."; |
| 28 return; |
| 29 } |
| 30 if (FAILED(display_attribute_manager_.CreateInstance( |
| 31 CLSID_TF_DisplayAttributeMgr))) { |
| 32 LOG(FATAL) << "Failed to initialize DisplayAttributeMgr."; |
| 33 return; |
| 34 } |
15 } | 35 } |
16 | 36 |
17 TsfTextStore::~TsfTextStore() { | 37 TsfTextStore::~TsfTextStore() { |
18 } | 38 } |
19 | 39 |
20 ULONG STDMETHODCALLTYPE TsfTextStore::AddRef() { | 40 ULONG STDMETHODCALLTYPE TsfTextStore::AddRef() { |
21 return InterlockedIncrement(&ref_count_); | 41 return InterlockedIncrement(&ref_count_); |
22 } | 42 } |
23 | 43 |
24 ULONG STDMETHODCALLTYPE TsfTextStore::Release() { | 44 ULONG STDMETHODCALLTYPE TsfTextStore::Release() { |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
66 | 86 |
67 STDMETHODIMP TsfTextStore::FindNextAttrTransition( | 87 STDMETHODIMP TsfTextStore::FindNextAttrTransition( |
68 LONG acp_start, | 88 LONG acp_start, |
69 LONG acp_halt, | 89 LONG acp_halt, |
70 ULONG num_filter_attributes, | 90 ULONG num_filter_attributes, |
71 const TS_ATTRID* filter_attributes, | 91 const TS_ATTRID* filter_attributes, |
72 DWORD flags, | 92 DWORD flags, |
73 LONG* acp_next, | 93 LONG* acp_next, |
74 BOOL* found, | 94 BOOL* found, |
75 LONG* found_offset) { | 95 LONG* found_offset) { |
76 NOTIMPLEMENTED(); | 96 if (!acp_next || !found || !found_offset) |
77 return E_NOTIMPL; | 97 return E_INVALIDARG; |
| 98 // We don't support FindNextAttrTransition. |
| 99 *acp_next = 0; |
| 100 *found = FALSE; |
| 101 *found_offset = 0; |
| 102 return S_OK; |
78 } | 103 } |
79 | 104 |
80 STDMETHODIMP TsfTextStore::GetACPFromPoint( | 105 STDMETHODIMP TsfTextStore::GetACPFromPoint( |
81 TsViewCookie view_cookie, | 106 TsViewCookie view_cookie, |
82 const POINT* point, | 107 const POINT* point, |
83 DWORD flags, | 108 DWORD flags, |
84 LONG* acp) { | 109 LONG* acp) { |
| 110 // We don't support GetACPFromPoint. |
85 NOTIMPLEMENTED(); | 111 NOTIMPLEMENTED(); |
86 return E_NOTIMPL; | 112 return E_NOTIMPL; |
87 } | 113 } |
88 | 114 |
89 STDMETHODIMP TsfTextStore::GetActiveView(TsViewCookie* view_cookie) { | 115 STDMETHODIMP TsfTextStore::GetActiveView(TsViewCookie* view_cookie) { |
90 NOTIMPLEMENTED(); | |
91 if (!view_cookie) | 116 if (!view_cookie) |
92 return E_INVALIDARG; | 117 return E_INVALIDARG; |
| 118 // We support only one view. |
| 119 *view_cookie = kViewCookie; |
93 return S_OK; | 120 return S_OK; |
94 } | 121 } |
95 | 122 |
96 STDMETHODIMP TsfTextStore::GetEmbedded(LONG acp_pos, | 123 STDMETHODIMP TsfTextStore::GetEmbedded(LONG acp_pos, |
97 REFGUID service, | 124 REFGUID service, |
98 REFIID iid, | 125 REFIID iid, |
99 IUnknown** unknown) { | 126 IUnknown** unknown) { |
| 127 // We don't support any embedded objects. |
100 NOTIMPLEMENTED(); | 128 NOTIMPLEMENTED(); |
101 if (!unknown) | 129 if (!unknown) |
102 return E_INVALIDARG; | 130 return E_INVALIDARG; |
103 *unknown = NULL; | 131 *unknown = NULL; |
104 return E_NOTIMPL; | 132 return E_NOTIMPL; |
105 } | 133 } |
106 | 134 |
107 STDMETHODIMP TsfTextStore::GetEndACP(LONG* acp) { | 135 STDMETHODIMP TsfTextStore::GetEndACP(LONG* acp) { |
108 NOTIMPLEMENTED(); | |
109 if (!acp) | 136 if (!acp) |
110 return E_INVALIDARG; | 137 return E_INVALIDARG; |
111 return E_NOTIMPL; | 138 if (!HasReadLock()) |
| 139 return TS_E_NOLOCK; |
| 140 *acp = string_buffer_.size(); |
| 141 return S_OK; |
112 } | 142 } |
113 | 143 |
114 STDMETHODIMP TsfTextStore::GetFormattedText(LONG acp_start, LONG acp_end, | 144 STDMETHODIMP TsfTextStore::GetFormattedText(LONG acp_start, LONG acp_end, |
115 IDataObject** data_object) { | 145 IDataObject** data_object) { |
| 146 // We don't support GetFormattedText. |
116 NOTIMPLEMENTED(); | 147 NOTIMPLEMENTED(); |
117 return E_NOTIMPL; | 148 return E_NOTIMPL; |
118 } | 149 } |
119 | 150 |
120 STDMETHODIMP TsfTextStore::GetScreenExt(TsViewCookie view_cookie, RECT* rect) { | 151 STDMETHODIMP TsfTextStore::GetScreenExt(TsViewCookie view_cookie, RECT* rect) { |
121 NOTIMPLEMENTED(); | 152 NOTIMPLEMENTED(); |
122 if (!rect) | 153 if (!rect) |
123 return E_INVALIDARG; | 154 return E_INVALIDARG; |
124 SetRect(rect, 0, 0, 0, 0); | 155 SetRect(rect, 0, 0, 0, 0); |
125 return S_OK; | 156 return S_OK; |
126 } | 157 } |
127 | 158 |
128 STDMETHODIMP TsfTextStore::GetSelection(ULONG selection_index, | 159 STDMETHODIMP TsfTextStore::GetSelection(ULONG selection_index, |
129 ULONG selection_buffer_size, | 160 ULONG selection_buffer_size, |
130 TS_SELECTION_ACP* selection_buffer, | 161 TS_SELECTION_ACP* selection_buffer, |
131 ULONG* fetched_count) { | 162 ULONG* fetched_count) { |
132 NOTIMPLEMENTED(); | |
133 if (!selection_buffer) | 163 if (!selection_buffer) |
134 return E_INVALIDARG; | 164 return E_INVALIDARG; |
135 if (!fetched_count) | 165 if (!fetched_count) |
136 return E_INVALIDARG; | 166 return E_INVALIDARG; |
137 return E_NOTIMPL; | 167 if (!HasReadLock()) |
| 168 return TS_E_NOLOCK; |
| 169 *fetched_count = 0; |
| 170 if ((selection_buffer_size > 0) && |
| 171 ((selection_index == 0) || (selection_index == TS_DEFAULT_SELECTION))) { |
| 172 selection_buffer[0].acpStart = selection_.start(); |
| 173 selection_buffer[0].acpEnd = selection_.end(); |
| 174 selection_buffer[0].style.ase = TS_AE_END; |
| 175 selection_buffer[0].style.fInterimChar = FALSE; |
| 176 *fetched_count = 1; |
| 177 } |
| 178 return S_OK; |
138 } | 179 } |
139 | 180 |
140 STDMETHODIMP TsfTextStore::GetStatus(TS_STATUS* status) { | 181 STDMETHODIMP TsfTextStore::GetStatus(TS_STATUS* status) { |
141 NOTIMPLEMENTED(); | |
142 if (!status) | 182 if (!status) |
143 return E_INVALIDARG; | 183 return E_INVALIDARG; |
144 return E_NOTIMPL; | 184 |
| 185 status->dwDynamicFlags = 0; |
| 186 status->dwStaticFlags = TS_SS_NOHIDDENTEXT; |
| 187 |
| 188 return S_OK; |
145 } | 189 } |
146 | 190 |
147 STDMETHODIMP TsfTextStore::GetText(LONG acp_start, | 191 STDMETHODIMP TsfTextStore::GetText(LONG acp_start, |
148 LONG acp_end, | 192 LONG acp_end, |
149 wchar_t* text_buffer, | 193 wchar_t* text_buffer, |
150 ULONG text_buffer_size, | 194 ULONG text_buffer_size, |
151 ULONG* text_buffer_copied, | 195 ULONG* text_buffer_copied, |
152 TS_RUNINFO* run_info_buffer, | 196 TS_RUNINFO* run_info_buffer, |
153 ULONG run_info_buffer_size, | 197 ULONG run_info_buffer_size, |
154 ULONG* run_info_buffer_copied, | 198 ULONG* run_info_buffer_copied, |
155 LONG* next_acp) { | 199 LONG* next_acp) { |
156 NOTIMPLEMENTED(); | |
157 if (!text_buffer_copied || !run_info_buffer_copied) | 200 if (!text_buffer_copied || !run_info_buffer_copied) |
158 return E_INVALIDARG; | 201 return E_INVALIDARG; |
159 if (!text_buffer && text_buffer_size != 0) | 202 if (!text_buffer && text_buffer_size != 0) |
160 return E_INVALIDARG; | 203 return E_INVALIDARG; |
161 if (!run_info_buffer && run_info_buffer_size != 0) | 204 if (!run_info_buffer && run_info_buffer_size != 0) |
162 return E_INVALIDARG; | 205 return E_INVALIDARG; |
163 return E_NOTIMPL; | 206 if (!HasReadLock()) |
| 207 return TF_E_NOLOCK; |
| 208 if (acp_end == -1) |
| 209 acp_end = static_cast<LONG>(string_buffer_.size()); |
| 210 if ((acp_start > acp_end) || |
| 211 (acp_start > static_cast<LONG>(string_buffer_.size())) || |
| 212 (acp_end > static_cast<LONG>(string_buffer_.size()))) { |
| 213 return TF_E_INVALIDPOS; |
| 214 } |
| 215 |
| 216 acp_end = std::min(acp_end, acp_start + static_cast<LONG>(text_buffer_size)); |
| 217 *text_buffer_copied = acp_end - acp_start; |
| 218 |
| 219 const string16& result = |
| 220 string_buffer_.substr(acp_start, *text_buffer_copied); |
| 221 for (size_t i = 0; i < result.size(); ++i) { |
| 222 text_buffer[i] = result[i]; |
| 223 } |
| 224 |
| 225 if (run_info_buffer_size) { |
| 226 run_info_buffer[0].uCount = *text_buffer_copied; |
| 227 run_info_buffer[0].type = TS_RT_PLAIN; |
| 228 *run_info_buffer_copied = 1; |
| 229 } |
| 230 |
| 231 *next_acp = acp_end; |
| 232 return S_OK; |
164 } | 233 } |
165 | 234 |
166 STDMETHODIMP TsfTextStore::GetTextExt(TsViewCookie view_cookie, | 235 STDMETHODIMP TsfTextStore::GetTextExt(TsViewCookie view_cookie, |
167 LONG acp_start, | 236 LONG acp_start, |
168 LONG acp_end, | 237 LONG acp_end, |
169 RECT* rect, | 238 RECT* rect, |
170 BOOL* clipped) { | 239 BOOL* clipped) { |
171 NOTIMPLEMENTED(); | 240 if (!rect || !clipped) |
172 return E_NOTIMPL; | 241 return E_INVALIDARG; |
| 242 if (!text_input_client_) |
| 243 return E_UNEXPECTED; |
| 244 if (view_cookie != kViewCookie) |
| 245 return E_INVALIDARG; |
| 246 if (!HasReadLock()) |
| 247 return TS_E_NOLOCK; |
| 248 if (acp_start < static_cast<LONG>(committed_size_)) |
| 249 return TS_E_INVALIDPOS; |
| 250 if (acp_end < acp_start) |
| 251 return E_INVALIDARG; |
| 252 |
| 253 gfx::Rect result; |
| 254 gfx::Rect tmp_rect; |
| 255 const uint32 start_pos = acp_start - committed_size_; |
| 256 const uint32 end_pos = acp_end - committed_size_; |
| 257 |
| 258 if (start_pos == end_pos) { |
| 259 // According to MSDN document, if |acp_start| and |acp_end| are equal it is |
| 260 // OK to just return TS_E_INVALIDARG. |
| 261 // http://msdn.microsoft.com/en-us/library/ms538435 |
| 262 // But when using Pinin IME of Windows 8, this method is called with the |
| 263 // equal values of |acp_start| and |acp_end|. So we handle this condition. |
| 264 if (start_pos == 0) { |
| 265 if (text_input_client_->GetCompositionCharacterBounds(0, &tmp_rect)) { |
| 266 result = tmp_rect; |
| 267 result.set_width(0); |
| 268 } else if (string_buffer_.size() == committed_size_) { |
| 269 result = text_input_client_->GetCaretBounds(); |
| 270 } else { |
| 271 return TS_E_NOLAYOUT; |
| 272 } |
| 273 } else if (text_input_client_->GetCompositionCharacterBounds(start_pos - 1, |
| 274 &tmp_rect)) { |
| 275 result.set_x(tmp_rect.right()); |
| 276 result.set_y(tmp_rect.y()); |
| 277 result.set_width(0); |
| 278 result.set_height(tmp_rect.height()); |
| 279 } else { |
| 280 return TS_E_NOLAYOUT; |
| 281 } |
| 282 } else { |
| 283 if (!text_input_client_->GetCompositionCharacterBounds(start_pos, &result)) |
| 284 return TS_E_NOLAYOUT; |
| 285 |
| 286 for (uint32 i = start_pos + 1; i < end_pos; ++i) { |
| 287 if (!text_input_client_->GetCompositionCharacterBounds(i, &tmp_rect)) |
| 288 return TS_E_NOLAYOUT; |
| 289 result = result.Union(tmp_rect); |
| 290 } |
| 291 } |
| 292 |
| 293 *rect = result.ToRECT(); |
| 294 *clipped = FALSE; |
| 295 return S_OK; |
173 } | 296 } |
174 | 297 |
175 STDMETHODIMP TsfTextStore::GetWnd(TsViewCookie view_cookie, | 298 STDMETHODIMP TsfTextStore::GetWnd(TsViewCookie view_cookie, |
176 HWND* window_handle) { | 299 HWND* window_handle) { |
177 NOTIMPLEMENTED(); | 300 if (!window_handle) |
178 return E_NOTIMPL; | 301 return E_INVALIDARG; |
| 302 if (view_cookie != kViewCookie) |
| 303 return E_INVALIDARG; |
| 304 *window_handle = window_handle_; |
| 305 return S_OK; |
179 } | 306 } |
180 | 307 |
181 STDMETHODIMP TsfTextStore::InsertEmbedded(DWORD flags, | 308 STDMETHODIMP TsfTextStore::InsertEmbedded(DWORD flags, |
182 LONG acp_start, | 309 LONG acp_start, |
183 LONG acp_end, | 310 LONG acp_end, |
184 IDataObject* data_object, | 311 IDataObject* data_object, |
185 TS_TEXTCHANGE* change) { | 312 TS_TEXTCHANGE* change) { |
| 313 // We don't support any embedded objects. |
186 NOTIMPLEMENTED(); | 314 NOTIMPLEMENTED(); |
187 return E_NOTIMPL; | 315 return E_NOTIMPL; |
188 } | 316 } |
189 | 317 |
190 STDMETHODIMP TsfTextStore::InsertEmbeddedAtSelection(DWORD flags, | 318 STDMETHODIMP TsfTextStore::InsertEmbeddedAtSelection(DWORD flags, |
191 IDataObject* data_object, | 319 IDataObject* data_object, |
192 LONG* acp_start, | 320 LONG* acp_start, |
193 LONG* acp_end, | 321 LONG* acp_end, |
194 TS_TEXTCHANGE* change) { | 322 TS_TEXTCHANGE* change) { |
| 323 // We don't support any embedded objects. |
195 NOTIMPLEMENTED(); | 324 NOTIMPLEMENTED(); |
196 return E_NOTIMPL; | 325 return E_NOTIMPL; |
197 } | 326 } |
198 | 327 |
199 STDMETHODIMP TsfTextStore::InsertTextAtSelection(DWORD flags, | 328 STDMETHODIMP TsfTextStore::InsertTextAtSelection(DWORD flags, |
200 const wchar_t* text_buffer, | 329 const wchar_t* text_buffer, |
201 ULONG text_buffer_size, | 330 ULONG text_buffer_size, |
202 LONG* acp_start, | 331 LONG* acp_start, |
203 LONG* acp_end, | 332 LONG* acp_end, |
204 TS_TEXTCHANGE* text_change) { | 333 TS_TEXTCHANGE* text_change) { |
205 NOTIMPLEMENTED(); | 334 const LONG start_pos = selection_.start(); |
206 return E_NOTIMPL; | 335 const LONG end_pos = selection_.end(); |
| 336 const LONG new_end_pos = start_pos + text_buffer_size; |
| 337 |
| 338 if (flags & TS_IAS_QUERYONLY) { |
| 339 if (!HasReadLock()) |
| 340 return TS_E_NOLOCK; |
| 341 if (acp_start) |
| 342 *acp_start = start_pos; |
| 343 if (acp_end) { |
| 344 *acp_end = new_end_pos; |
| 345 } |
| 346 return S_OK; |
| 347 } |
| 348 |
| 349 if (!HasReadWriteLock()) |
| 350 return TS_E_NOLOCK; |
| 351 if (!text_buffer) |
| 352 return E_INVALIDARG; |
| 353 |
| 354 DCHECK_LE(start_pos, end_pos); |
| 355 string_buffer_ = string_buffer_.substr(0, start_pos) + |
| 356 string16(text_buffer, text_buffer + text_buffer_size) + |
| 357 string_buffer_.substr(end_pos); |
| 358 if (acp_start) |
| 359 *acp_start = start_pos; |
| 360 if (acp_end) |
| 361 *acp_end = new_end_pos; |
| 362 if (text_change) { |
| 363 text_change->acpStart = start_pos; |
| 364 text_change->acpOldEnd = end_pos; |
| 365 text_change->acpNewEnd = new_end_pos; |
| 366 } |
| 367 selection_.set_start(start_pos); |
| 368 selection_.set_end(new_end_pos); |
| 369 return S_OK; |
207 } | 370 } |
208 | 371 |
209 STDMETHODIMP TsfTextStore::QueryInsert( | 372 STDMETHODIMP TsfTextStore::QueryInsert( |
210 LONG acp_test_start, | 373 LONG acp_test_start, |
211 LONG acp_test_end, | 374 LONG acp_test_end, |
212 ULONG text_size, | 375 ULONG text_size, |
213 LONG* acp_result_start, | 376 LONG* acp_result_start, |
214 LONG* acp_result_end) { | 377 LONG* acp_result_end) { |
215 NOTIMPLEMENTED(); | 378 if ((acp_test_end < acp_test_start) || |
216 return E_NOTIMPL; | 379 (acp_test_start < static_cast<LONG>(committed_size_))) { |
| 380 return E_FAIL; |
| 381 } |
| 382 if (acp_result_start) |
| 383 *acp_result_start = acp_test_start; |
| 384 if (acp_result_end) |
| 385 *acp_result_end = acp_test_start + text_size; |
| 386 return S_OK; |
217 } | 387 } |
218 | 388 |
219 STDMETHODIMP TsfTextStore::QueryInsertEmbedded(const GUID* service, | 389 STDMETHODIMP TsfTextStore::QueryInsertEmbedded(const GUID* service, |
220 const FORMATETC* format, | 390 const FORMATETC* format, |
221 BOOL* insertable) { | 391 BOOL* insertable) { |
222 NOTIMPLEMENTED(); | 392 // We don't support any embedded objects. |
223 return E_NOTIMPL; | 393 if (insertable) |
| 394 *insertable = FALSE; |
| 395 return S_OK; |
224 } | 396 } |
225 | 397 |
226 STDMETHODIMP TsfTextStore::RequestAttrsAtPosition( | 398 STDMETHODIMP TsfTextStore::RequestAttrsAtPosition( |
227 LONG acp_pos, | 399 LONG acp_pos, |
228 ULONG attribute_buffer_size, | 400 ULONG attribute_buffer_size, |
229 const TS_ATTRID* attribute_buffer, | 401 const TS_ATTRID* attribute_buffer, |
230 DWORD flags) { | 402 DWORD flags) { |
231 NOTIMPLEMENTED(); | 403 // We don't support any document attributes. |
232 return E_NOTIMPL; | 404 // This method just returns S_OK, and the subsequently called |
| 405 // RetrieveRequestedAttrs() returns 0 as the number of supported attributes. |
| 406 return S_OK; |
233 } | 407 } |
234 | 408 |
235 STDMETHODIMP TsfTextStore::RequestAttrsTransitioningAtPosition( | 409 STDMETHODIMP TsfTextStore::RequestAttrsTransitioningAtPosition( |
236 LONG acp_pos, | 410 LONG acp_pos, |
237 ULONG attribute_buffer_size, | 411 ULONG attribute_buffer_size, |
238 const TS_ATTRID* attribute_buffer, | 412 const TS_ATTRID* attribute_buffer, |
239 DWORD flags) { | 413 DWORD flags) { |
240 NOTIMPLEMENTED(); | 414 // We don't support any document attributes. |
241 return E_NOTIMPL; | 415 // This method just returns S_OK, and the subsequently called |
| 416 // RetrieveRequestedAttrs() returns 0 as the number of supported attributes. |
| 417 return S_OK; |
242 } | 418 } |
243 | 419 |
244 STDMETHODIMP TsfTextStore::RequestLock(DWORD lock_flags, HRESULT* result) { | 420 STDMETHODIMP TsfTextStore::RequestLock(DWORD lock_flags, HRESULT* result) { |
245 NOTIMPLEMENTED(); | 421 if (!text_store_acp_sink_.get()) |
246 return E_NOTIMPL; | 422 return E_FAIL; |
| 423 if (!result) |
| 424 return E_INVALIDARG; |
| 425 |
| 426 if (current_lock_type_ != 0) { |
| 427 if (lock_flags & TS_LF_SYNC) { |
| 428 // Can't lock synchronously. |
| 429 *result = TS_E_SYNCHRONOUS; |
| 430 return S_OK; |
| 431 } |
| 432 // Queue the lock request. |
| 433 lock_queue_.push_back(lock_flags & TS_LF_READWRITE); |
| 434 *result = TS_S_ASYNC; |
| 435 return S_OK; |
| 436 } |
| 437 |
| 438 // Lock |
| 439 current_lock_type_ = (lock_flags & TS_LF_READWRITE); |
| 440 |
| 441 edit_flag_ = false; |
| 442 const size_t last_committed_size = committed_size_; |
| 443 |
| 444 // Grant the lock. |
| 445 *result = text_store_acp_sink_->OnLockGranted(current_lock_type_); |
| 446 |
| 447 // Unlock |
| 448 current_lock_type_ = 0; |
| 449 |
| 450 // Handles the pending lock requests. |
| 451 while (lock_queue_.size() > 0) { |
| 452 current_lock_type_ = lock_queue_.front(); |
| 453 lock_queue_.pop_front(); |
| 454 text_store_acp_sink_->OnLockGranted(current_lock_type_); |
| 455 current_lock_type_ = 0; |
| 456 } |
| 457 |
| 458 if (!edit_flag_) { |
| 459 return S_OK; |
| 460 } |
| 461 |
| 462 // If the text store is edited in OnLockGranted(), we may need to call |
| 463 // TextInputClient::InsertText() or TextInputClient::SetCompositionText(). |
| 464 const size_t new_committed_size = committed_size_; |
| 465 const string16& new_committed_string = |
| 466 string_buffer_.substr(last_committed_size, |
| 467 new_committed_size - last_committed_size); |
| 468 const string16& composition_string = |
| 469 string_buffer_.substr(new_committed_size); |
| 470 |
| 471 // If there is new committed string, calls TextInputClient::InsertText(). |
| 472 if ((!new_committed_string.empty()) && text_input_client_) { |
| 473 text_input_client_->InsertText(new_committed_string); |
| 474 } |
| 475 |
| 476 // Calls TextInputClient::SetCompositionText(). |
| 477 CompositionText composition_text; |
| 478 composition_text.text = composition_string; |
| 479 composition_text.underlines = composition_undelines_; |
| 480 // Adjusts the offset. |
| 481 for (size_t i = 0; i < composition_text.underlines.size(); ++i) { |
| 482 composition_text.underlines[i].start_offset -= new_committed_size; |
| 483 composition_text.underlines[i].end_offset -= new_committed_size; |
| 484 } |
| 485 if (selection_.start() < new_committed_size) { |
| 486 composition_text.selection.set_start(0); |
| 487 } else { |
| 488 composition_text.selection.set_start( |
| 489 selection_.start() - new_committed_size); |
| 490 } |
| 491 if (selection_.end() < new_committed_size) { |
| 492 composition_text.selection.set_end(0); |
| 493 } else { |
| 494 composition_text.selection.set_end(selection_.end() - new_committed_size); |
| 495 } |
| 496 if (text_input_client_) |
| 497 text_input_client_->SetCompositionText(composition_text); |
| 498 |
| 499 // If there is no composition string, clear the text store status. |
| 500 // And call OnSelectionChange(), OnLayoutChange(), and OnTextChange(). |
| 501 if ((composition_string.empty()) && (new_committed_size != 0)) { |
| 502 string_buffer_.clear(); |
| 503 committed_size_ = 0; |
| 504 selection_.set_start(0); |
| 505 selection_.set_end(0); |
| 506 if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE) |
| 507 text_store_acp_sink_->OnSelectionChange(); |
| 508 if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE) |
| 509 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); |
| 510 if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) { |
| 511 TS_TEXTCHANGE textChange; |
| 512 textChange.acpStart = 0; |
| 513 textChange.acpOldEnd = new_committed_size; |
| 514 textChange.acpNewEnd = 0; |
| 515 text_store_acp_sink_->OnTextChange(0, &textChange); |
| 516 } |
| 517 } |
| 518 |
| 519 return S_OK; |
247 } | 520 } |
248 | 521 |
249 STDMETHODIMP TsfTextStore::RequestSupportedAttrs( | 522 STDMETHODIMP TsfTextStore::RequestSupportedAttrs( |
250 DWORD flags, | 523 DWORD flags, |
251 ULONG attribute_buffer_size, | 524 ULONG attribute_buffer_size, |
252 const TS_ATTRID* attribute_buffer) { | 525 const TS_ATTRID* attribute_buffer) { |
253 NOTIMPLEMENTED(); | 526 // We don't support any document attributes. |
254 return E_NOTIMPL; | 527 // This method just returns S_OK, and the subsequently called |
| 528 // RetrieveRequestedAttrs() returns 0 as the number of supported attributes. |
| 529 return S_OK; |
255 } | 530 } |
256 | 531 |
257 STDMETHODIMP TsfTextStore::RetrieveRequestedAttrs( | 532 STDMETHODIMP TsfTextStore::RetrieveRequestedAttrs( |
258 ULONG attribute_buffer_size, | 533 ULONG attribute_buffer_size, |
259 TS_ATTRVAL* attribute_buffer, | 534 TS_ATTRVAL* attribute_buffer, |
260 ULONG* attribute_buffer_copied) { | 535 ULONG* attribute_buffer_copied) { |
261 NOTIMPLEMENTED(); | 536 if (attribute_buffer_copied == NULL) |
262 return E_NOTIMPL; | 537 return E_INVALIDARG; |
| 538 // We don't support any document attributes. |
| 539 attribute_buffer_copied = 0; |
| 540 return S_OK; |
263 } | 541 } |
264 | 542 |
265 STDMETHODIMP TsfTextStore::SetSelection( | 543 STDMETHODIMP TsfTextStore::SetSelection( |
266 ULONG selection_buffer_size, | 544 ULONG selection_buffer_size, |
267 const TS_SELECTION_ACP* selection_buffer) { | 545 const TS_SELECTION_ACP* selection_buffer) { |
268 NOTIMPLEMENTED(); | 546 if (!HasReadWriteLock()) |
269 return E_NOTIMPL; | 547 return TF_E_NOLOCK; |
| 548 if (selection_buffer_size > 0) { |
| 549 const LONG start_pos = selection_buffer[0].acpStart; |
| 550 const LONG end_pos = selection_buffer[0].acpEnd; |
| 551 if ((start_pos < static_cast<LONG>(committed_size_)) || |
| 552 (static_cast<LONG>(string_buffer_.size()) < start_pos) || |
| 553 (end_pos < start_pos) || |
| 554 (static_cast<LONG>(string_buffer_.size()) < end_pos)) { |
| 555 return TF_E_INVALIDPOS; |
| 556 } |
| 557 selection_.set_start(start_pos); |
| 558 selection_.set_end(end_pos); |
| 559 } |
| 560 return S_OK; |
270 } | 561 } |
271 | 562 |
272 STDMETHODIMP TsfTextStore::SetText(DWORD flags, | 563 STDMETHODIMP TsfTextStore::SetText(DWORD flags, |
273 LONG acp_start, | 564 LONG acp_start, |
274 LONG acp_end, | 565 LONG acp_end, |
275 const wchar_t* text_buffer, | 566 const wchar_t* text_buffer, |
276 ULONG text_buffer_size, | 567 ULONG text_buffer_size, |
277 TS_TEXTCHANGE* text_change) { | 568 TS_TEXTCHANGE* text_change) { |
278 NOTIMPLEMENTED(); | 569 if (!HasReadWriteLock()) |
279 return E_NOTIMPL; | 570 return TS_E_NOLOCK; |
| 571 if (acp_start < static_cast<LONG>(committed_size_)) |
| 572 return TS_E_INVALIDPOS; |
| 573 if (static_cast<LONG>(string_buffer_.size()) < acp_start) |
| 574 return TS_E_INVALIDPOS; |
| 575 if (acp_end < acp_start) |
| 576 return E_INVALIDARG; |
| 577 if (static_cast<LONG>(string_buffer_.size()) < acp_end) |
| 578 return TS_E_INVALIDPOS; |
| 579 |
| 580 TS_SELECTION_ACP selection; |
| 581 selection.acpStart = acp_start; |
| 582 selection.acpEnd = acp_end; |
| 583 selection.style.ase = TS_AE_NONE; |
| 584 selection.style.fInterimChar = 0; |
| 585 if (SetSelection(1, &selection) != S_OK) |
| 586 return E_UNEXPECTED; |
| 587 |
| 588 TS_TEXTCHANGE change; |
| 589 if (InsertTextAtSelection(0, text_buffer, text_buffer_size, |
| 590 &acp_start, &acp_end, &change) != S_OK) { |
| 591 return E_UNEXPECTED; |
| 592 } |
| 593 if (text_change) |
| 594 *text_change = change; |
| 595 |
| 596 return S_OK; |
280 } | 597 } |
281 | 598 |
282 STDMETHODIMP TsfTextStore::UnadviseSink(IUnknown* unknown) { | 599 STDMETHODIMP TsfTextStore::UnadviseSink(IUnknown* unknown) { |
283 if (!text_store_acp_sink_.IsSameObject(unknown)) | 600 if (!text_store_acp_sink_.IsSameObject(unknown)) |
284 return CONNECT_E_NOCONNECTION; | 601 return CONNECT_E_NOCONNECTION; |
285 text_store_acp_sink_.Release(); | 602 text_store_acp_sink_.Release(); |
286 text_store_acp_sink_mask_ = 0; | 603 text_store_acp_sink_mask_ = 0; |
287 return S_OK; | 604 return S_OK; |
288 } | 605 } |
289 | 606 |
290 STDMETHODIMP TsfTextStore::OnStartComposition( | 607 STDMETHODIMP TsfTextStore::OnStartComposition( |
291 ITfCompositionView* composition_view, | 608 ITfCompositionView* composition_view, |
292 BOOL* ok) { | 609 BOOL* ok) { |
293 if (!ok) | 610 if (ok) |
294 *ok = TRUE; | 611 *ok = TRUE; |
295 return S_OK; | 612 return S_OK; |
296 } | 613 } |
297 | 614 |
298 STDMETHODIMP TsfTextStore::OnUpdateComposition( | 615 STDMETHODIMP TsfTextStore::OnUpdateComposition( |
299 ITfCompositionView* composition_view, | 616 ITfCompositionView* composition_view, |
300 ITfRange* range) { | 617 ITfRange* range) { |
301 return S_OK; | 618 return S_OK; |
302 } | 619 } |
303 | 620 |
304 STDMETHODIMP TsfTextStore::OnEndComposition( | 621 STDMETHODIMP TsfTextStore::OnEndComposition( |
305 ITfCompositionView* composition_view) { | 622 ITfCompositionView* composition_view) { |
306 return S_OK; | 623 return S_OK; |
307 } | 624 } |
308 | 625 |
309 STDMETHODIMP TsfTextStore::OnEndEdit(ITfContext* context, | 626 STDMETHODIMP TsfTextStore::OnEndEdit(ITfContext* context, |
310 TfEditCookie read_only_edit_cookie, | 627 TfEditCookie read_only_edit_cookie, |
311 ITfEditRecord* edit_record) { | 628 ITfEditRecord* edit_record) { |
| 629 if (!context || !edit_record) |
| 630 return E_INVALIDARG; |
| 631 |
| 632 size_t committed_size; |
| 633 CompositionUnderlines undelines; |
| 634 if (!GetCompositionStatus(context, read_only_edit_cookie, &committed_size, |
| 635 &undelines)) { |
| 636 return S_OK; |
| 637 } |
| 638 composition_undelines_ = undelines; |
| 639 committed_size_ = committed_size; |
| 640 edit_flag_ = true; |
312 return S_OK; | 641 return S_OK; |
313 } | 642 } |
314 | 643 |
315 } // namespace ui | 644 bool TsfTextStore::GetDisplayAttribute(TfGuidAtom guid_atom, |
| 645 TF_DISPLAYATTRIBUTE* attribute) { |
| 646 GUID guid; |
| 647 if (FAILED(category_manager_->GetGUID(guid_atom, &guid))) |
| 648 return false; |
| 649 |
| 650 base::win::ScopedComPtr<ITfDisplayAttributeInfo> display_attribute_info; |
| 651 if (FAILED(display_attribute_manager_->GetDisplayAttributeInfo( |
| 652 guid, display_attribute_info.Receive(), NULL))) { |
| 653 return false; |
| 654 } |
| 655 return SUCCEEDED(display_attribute_info->GetAttributeInfo(attribute)); |
| 656 } |
| 657 |
| 658 bool TsfTextStore::GetCompositionStatus( |
| 659 ITfContext* context, |
| 660 const TfEditCookie read_only_edit_cookie, |
| 661 size_t* committed_size, |
| 662 CompositionUnderlines* undelines) { |
| 663 DCHECK(context); |
| 664 DCHECK(committed_size); |
| 665 DCHECK(undelines); |
| 666 const GUID* rgGuids[2] = {&GUID_PROP_COMPOSING, &GUID_PROP_ATTRIBUTE}; |
| 667 base::win::ScopedComPtr<ITfReadOnlyProperty> track_property; |
| 668 if (FAILED(context->TrackProperties(rgGuids, 2, NULL, 0, |
| 669 track_property.Receive()))) { |
| 670 return false; |
| 671 } |
| 672 |
| 673 *committed_size = 0; |
| 674 undelines->clear(); |
| 675 base::win::ScopedComPtr<ITfRange> start_to_end_range; |
| 676 base::win::ScopedComPtr<ITfRange> end_range; |
| 677 if (FAILED(context->GetStart(read_only_edit_cookie, |
| 678 start_to_end_range.Receive()))) { |
| 679 return false; |
| 680 } |
| 681 if (FAILED(context->GetEnd(read_only_edit_cookie, end_range.Receive()))) |
| 682 return false; |
| 683 if (FAILED(start_to_end_range->ShiftEndToRange(read_only_edit_cookie, |
| 684 end_range, TF_ANCHOR_END))) { |
| 685 return false; |
| 686 } |
| 687 |
| 688 base::win::ScopedComPtr<IEnumTfRanges> ranges; |
| 689 if (FAILED(track_property->EnumRanges(read_only_edit_cookie, ranges.Receive(), |
| 690 start_to_end_range))) { |
| 691 return false; |
| 692 } |
| 693 |
| 694 while (true) { |
| 695 base::win::ScopedComPtr<ITfRange> range; |
| 696 if (ranges->Next(1, range.Receive(), NULL) != S_OK) |
| 697 break; |
| 698 base::win::ScopedVariant value; |
| 699 base::win::ScopedComPtr<IEnumTfPropertyValue> enum_prop_value; |
| 700 if (FAILED(track_property->GetValue(read_only_edit_cookie, range, |
| 701 value.Receive()))) { |
| 702 return false; |
| 703 } |
| 704 if (FAILED(enum_prop_value.QueryFrom(value.AsInput()->punkVal))) |
| 705 return false; |
| 706 |
| 707 TF_PROPERTYVAL property_value; |
| 708 bool is_composition = false; |
| 709 bool has_display_attribute = false; |
| 710 TF_DISPLAYATTRIBUTE display_attribute; |
| 711 while (enum_prop_value->Next(1, &property_value, NULL) == S_OK) { |
| 712 if (IsEqualGUID(property_value.guidId, GUID_PROP_COMPOSING)) { |
| 713 is_composition = (property_value.varValue.lVal == TRUE); |
| 714 } else if (IsEqualGUID(property_value.guidId, GUID_PROP_ATTRIBUTE)) { |
| 715 TfGuidAtom guid_atom = |
| 716 static_cast<TfGuidAtom>(property_value.varValue.lVal); |
| 717 if (GetDisplayAttribute(guid_atom, &display_attribute)) |
| 718 has_display_attribute = true; |
| 719 } |
| 720 VariantClear(&property_value.varValue); |
| 721 } |
| 722 |
| 723 base::win::ScopedComPtr<ITfRangeACP> range_acp; |
| 724 range_acp.QueryFrom(range); |
| 725 LONG start_pos, length; |
| 726 range_acp->GetExtent(&start_pos, &length); |
| 727 if (!is_composition) { |
| 728 if (*committed_size < static_cast<size_t>(start_pos + length)) |
| 729 *committed_size = start_pos + length; |
| 730 } else { |
| 731 CompositionUnderline underline; |
| 732 underline.start_offset = start_pos; |
| 733 underline.end_offset = start_pos + length; |
| 734 underline.color = SK_ColorBLACK; |
| 735 if (has_display_attribute) |
| 736 underline.thick = !!display_attribute.fBoldLine; |
| 737 undelines->push_back(underline); |
| 738 } |
| 739 } |
| 740 return true; |
| 741 } |
| 742 |
| 743 void TsfTextStore::SetFocusedTextInputClient( |
| 744 HWND focused_window, |
| 745 TextInputClient* text_input_client) { |
| 746 window_handle_ = focused_window; |
| 747 text_input_client_ = text_input_client; |
| 748 } |
| 749 |
| 750 void TsfTextStore::RemoveFocusedTextInputClient( |
| 751 TextInputClient* text_input_client) { |
| 752 if (text_input_client_ == text_input_client) { |
| 753 window_handle_ = NULL; |
| 754 text_input_client_ = NULL; |
| 755 } |
| 756 } |
| 757 |
| 758 void TsfTextStore::SendOnLayoutChange() { |
| 759 if (text_store_acp_sink_ && (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE)) |
| 760 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); |
| 761 } |
| 762 |
| 763 bool TsfTextStore::HasReadLock() const { |
| 764 return (current_lock_type_ & TS_LF_READ) == TS_LF_READ; |
| 765 } |
| 766 |
| 767 bool TsfTextStore::HasReadWriteLock() const { |
| 768 return (current_lock_type_ & TS_LF_READWRITE) == TS_LF_READWRITE; |
| 769 } |
| 770 |
| 771 } // namespace ui |
OLD | NEW |