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