Chromium Code Reviews| 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 |