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