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