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