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