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