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