| Index: ui/base/win/tsf_text_store.cc
|
| diff --git a/ui/base/win/tsf_text_store.cc b/ui/base/win/tsf_text_store.cc
|
| index 2c49d6b0949154ffaec3bf3ba47e5e1efff59d1c..efe77bee5f5abc256dd32b246686d9dbde99530f 100644
|
| --- a/ui/base/win/tsf_text_store.cc
|
| +++ b/ui/base/win/tsf_text_store.cc
|
| @@ -5,13 +5,33 @@
|
| #include "ui/base/win/tsf_text_store.h"
|
|
|
| #include <OleCtl.h>
|
| -#include "base/logging.h"
|
| +#include "base/win/scoped_variant.h"
|
| +#include "ui/base/ime/text_input_client.h"
|
| +#include "ui/gfx/rect.h"
|
|
|
| namespace ui {
|
| +namespace {
|
| +// We support only one view.
|
| +const TsViewCookie kViewCookie = 1;
|
| +} // namespace
|
|
|
| TsfTextStore::TsfTextStore()
|
| : ref_count_(0),
|
| - text_store_acp_sink_mask_(0) {
|
| + text_store_acp_sink_mask_(0),
|
| + window_handle_(NULL),
|
| + text_input_client_(NULL),
|
| + committed_size_(0),
|
| + edit_flag_(false),
|
| + current_lock_type_(0) {
|
| + if (FAILED(category_manager_.CreateInstance(CLSID_TF_CategoryMgr))) {
|
| + LOG(FATAL) << "Failed to initialize CategoryMgr.";
|
| + return;
|
| + }
|
| + if (FAILED(display_attribute_manager_.CreateInstance(
|
| + CLSID_TF_DisplayAttributeMgr))) {
|
| + LOG(FATAL) << "Failed to initialize DisplayAttributeMgr.";
|
| + return;
|
| + }
|
| }
|
|
|
| TsfTextStore::~TsfTextStore() {
|
| @@ -73,8 +93,13 @@ STDMETHODIMP TsfTextStore::FindNextAttrTransition(
|
| LONG* acp_next,
|
| BOOL* found,
|
| LONG* found_offset) {
|
| - NOTIMPLEMENTED();
|
| - return E_NOTIMPL;
|
| + if (!acp_next || !found || !found_offset)
|
| + return E_INVALIDARG;
|
| + // We don't support FindNextAttrTransition.
|
| + *acp_next = 0;
|
| + *found = FALSE;
|
| + *found_offset = 0;
|
| + return S_OK;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::GetACPFromPoint(
|
| @@ -82,14 +107,16 @@ STDMETHODIMP TsfTextStore::GetACPFromPoint(
|
| const POINT* point,
|
| DWORD flags,
|
| LONG* acp) {
|
| + // We don't support GetACPFromPoint.
|
| NOTIMPLEMENTED();
|
| return E_NOTIMPL;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::GetActiveView(TsViewCookie* view_cookie) {
|
| - NOTIMPLEMENTED();
|
| if (!view_cookie)
|
| return E_INVALIDARG;
|
| + // We support only one view.
|
| + *view_cookie = kViewCookie;
|
| return S_OK;
|
| }
|
|
|
| @@ -97,6 +124,7 @@ STDMETHODIMP TsfTextStore::GetEmbedded(LONG acp_pos,
|
| REFGUID service,
|
| REFIID iid,
|
| IUnknown** unknown) {
|
| + // We don't support any embedded objects.
|
| NOTIMPLEMENTED();
|
| if (!unknown)
|
| return E_INVALIDARG;
|
| @@ -105,14 +133,17 @@ STDMETHODIMP TsfTextStore::GetEmbedded(LONG acp_pos,
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::GetEndACP(LONG* acp) {
|
| - NOTIMPLEMENTED();
|
| if (!acp)
|
| return E_INVALIDARG;
|
| - return E_NOTIMPL;
|
| + if (!HasReadLock())
|
| + return TS_E_NOLOCK;
|
| + *acp = string_buffer_.size();
|
| + return S_OK;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::GetFormattedText(LONG acp_start, LONG acp_end,
|
| IDataObject** data_object) {
|
| + // We don't support GetFormattedText.
|
| NOTIMPLEMENTED();
|
| return E_NOTIMPL;
|
| }
|
| @@ -129,19 +160,32 @@ STDMETHODIMP TsfTextStore::GetSelection(ULONG selection_index,
|
| ULONG selection_buffer_size,
|
| TS_SELECTION_ACP* selection_buffer,
|
| ULONG* fetched_count) {
|
| - NOTIMPLEMENTED();
|
| if (!selection_buffer)
|
| return E_INVALIDARG;
|
| if (!fetched_count)
|
| return E_INVALIDARG;
|
| - return E_NOTIMPL;
|
| + if (!HasReadLock())
|
| + return TS_E_NOLOCK;
|
| + *fetched_count = 0;
|
| + if ((selection_buffer_size > 0) &&
|
| + ((selection_index == 0) || (selection_index == TS_DEFAULT_SELECTION))) {
|
| + selection_buffer[0].acpStart = selection_.start();
|
| + selection_buffer[0].acpEnd = selection_.end();
|
| + selection_buffer[0].style.ase = TS_AE_END;
|
| + selection_buffer[0].style.fInterimChar = FALSE;
|
| + *fetched_count = 1;
|
| + }
|
| + return S_OK;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::GetStatus(TS_STATUS* status) {
|
| - NOTIMPLEMENTED();
|
| if (!status)
|
| return E_INVALIDARG;
|
| - return E_NOTIMPL;
|
| +
|
| + status->dwDynamicFlags = 0;
|
| + status->dwStaticFlags = TS_SS_NOHIDDENTEXT;
|
| +
|
| + return S_OK;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::GetText(LONG acp_start,
|
| @@ -153,14 +197,39 @@ STDMETHODIMP TsfTextStore::GetText(LONG acp_start,
|
| ULONG run_info_buffer_size,
|
| ULONG* run_info_buffer_copied,
|
| LONG* next_acp) {
|
| - NOTIMPLEMENTED();
|
| if (!text_buffer_copied || !run_info_buffer_copied)
|
| return E_INVALIDARG;
|
| if (!text_buffer && text_buffer_size != 0)
|
| return E_INVALIDARG;
|
| if (!run_info_buffer && run_info_buffer_size != 0)
|
| return E_INVALIDARG;
|
| - return E_NOTIMPL;
|
| + if (!HasReadLock())
|
| + return TF_E_NOLOCK;
|
| + if (acp_end == -1)
|
| + acp_end = static_cast<LONG>(string_buffer_.size());
|
| + if ((acp_start > acp_end) ||
|
| + (acp_start > static_cast<LONG>(string_buffer_.size())) ||
|
| + (acp_end > static_cast<LONG>(string_buffer_.size()))) {
|
| + return TF_E_INVALIDPOS;
|
| + }
|
| +
|
| + acp_end = std::min(acp_end, acp_start + static_cast<LONG>(text_buffer_size));
|
| + *text_buffer_copied = acp_end - acp_start;
|
| +
|
| + const string16& result =
|
| + string_buffer_.substr(acp_start, *text_buffer_copied);
|
| + for (size_t i = 0; i < result.size(); ++i) {
|
| + text_buffer[i] = result[i];
|
| + }
|
| +
|
| + if (run_info_buffer_size) {
|
| + run_info_buffer[0].uCount = *text_buffer_copied;
|
| + run_info_buffer[0].type = TS_RT_PLAIN;
|
| + *run_info_buffer_copied = 1;
|
| + }
|
| +
|
| + *next_acp = acp_end;
|
| + return S_OK;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::GetTextExt(TsViewCookie view_cookie,
|
| @@ -168,14 +237,72 @@ STDMETHODIMP TsfTextStore::GetTextExt(TsViewCookie view_cookie,
|
| LONG acp_end,
|
| RECT* rect,
|
| BOOL* clipped) {
|
| - NOTIMPLEMENTED();
|
| - return E_NOTIMPL;
|
| + if (!rect || !clipped)
|
| + return E_INVALIDARG;
|
| + if (!text_input_client_)
|
| + return E_UNEXPECTED;
|
| + if (view_cookie != kViewCookie)
|
| + return E_INVALIDARG;
|
| + if (!HasReadLock())
|
| + return TS_E_NOLOCK;
|
| + if (acp_start < static_cast<LONG>(committed_size_))
|
| + return TS_E_INVALIDPOS;
|
| + if (acp_end < acp_start)
|
| + return E_INVALIDARG;
|
| +
|
| + gfx::Rect result;
|
| + gfx::Rect tmp_rect;
|
| + const uint32 start_pos = acp_start - committed_size_;
|
| + const uint32 end_pos = acp_end - committed_size_;
|
| +
|
| + if (start_pos == end_pos) {
|
| + // According to MSDN document, if |acp_start| and |acp_end| are equal it is
|
| + // OK to just return TS_E_INVALIDARG.
|
| + // http://msdn.microsoft.com/en-us/library/ms538435
|
| + // But when using Pinin IME of Windows 8, this method is called with the
|
| + // equal values of |acp_start| and |acp_end|. So we handle this condition.
|
| + if (start_pos == 0) {
|
| + if (text_input_client_->GetCompositionCharacterBounds(0, &tmp_rect)) {
|
| + result = tmp_rect;
|
| + result.set_width(0);
|
| + } else if (string_buffer_.size() == committed_size_) {
|
| + result = text_input_client_->GetCaretBounds();
|
| + } else {
|
| + return TS_E_NOLAYOUT;
|
| + }
|
| + } else if (text_input_client_->GetCompositionCharacterBounds(start_pos - 1,
|
| + &tmp_rect)) {
|
| + result.set_x(tmp_rect.right());
|
| + result.set_y(tmp_rect.y());
|
| + result.set_width(0);
|
| + result.set_height(tmp_rect.height());
|
| + } else {
|
| + return TS_E_NOLAYOUT;
|
| + }
|
| + } else {
|
| + if (!text_input_client_->GetCompositionCharacterBounds(start_pos, &result))
|
| + return TS_E_NOLAYOUT;
|
| +
|
| + for (uint32 i = start_pos + 1; i < end_pos; ++i) {
|
| + if (!text_input_client_->GetCompositionCharacterBounds(i, &tmp_rect))
|
| + return TS_E_NOLAYOUT;
|
| + result = result.Union(tmp_rect);
|
| + }
|
| + }
|
| +
|
| + *rect = result.ToRECT();
|
| + *clipped = FALSE;
|
| + return S_OK;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::GetWnd(TsViewCookie view_cookie,
|
| HWND* window_handle) {
|
| - NOTIMPLEMENTED();
|
| - return E_NOTIMPL;
|
| + if (!window_handle)
|
| + return E_INVALIDARG;
|
| + if (view_cookie != kViewCookie)
|
| + return E_INVALIDARG;
|
| + *window_handle = window_handle_;
|
| + return S_OK;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::InsertEmbedded(DWORD flags,
|
| @@ -183,6 +310,7 @@ STDMETHODIMP TsfTextStore::InsertEmbedded(DWORD flags,
|
| LONG acp_end,
|
| IDataObject* data_object,
|
| TS_TEXTCHANGE* change) {
|
| + // We don't support any embedded objects.
|
| NOTIMPLEMENTED();
|
| return E_NOTIMPL;
|
| }
|
| @@ -192,6 +320,7 @@ STDMETHODIMP TsfTextStore::InsertEmbeddedAtSelection(DWORD flags,
|
| LONG* acp_start,
|
| LONG* acp_end,
|
| TS_TEXTCHANGE* change) {
|
| + // We don't support any embedded objects.
|
| NOTIMPLEMENTED();
|
| return E_NOTIMPL;
|
| }
|
| @@ -202,8 +331,42 @@ STDMETHODIMP TsfTextStore::InsertTextAtSelection(DWORD flags,
|
| LONG* acp_start,
|
| LONG* acp_end,
|
| TS_TEXTCHANGE* text_change) {
|
| - NOTIMPLEMENTED();
|
| - return E_NOTIMPL;
|
| + const LONG start_pos = selection_.start();
|
| + const LONG end_pos = selection_.end();
|
| + const LONG new_end_pos = start_pos + text_buffer_size;
|
| +
|
| + if (flags & TS_IAS_QUERYONLY) {
|
| + if (!HasReadLock())
|
| + return TS_E_NOLOCK;
|
| + if (acp_start)
|
| + *acp_start = start_pos;
|
| + if (acp_end) {
|
| + *acp_end = new_end_pos;
|
| + }
|
| + return S_OK;
|
| + }
|
| +
|
| + if (!HasReadWriteLock())
|
| + return TS_E_NOLOCK;
|
| + if (!text_buffer)
|
| + return E_INVALIDARG;
|
| +
|
| + DCHECK_LE(start_pos, end_pos);
|
| + string_buffer_ = string_buffer_.substr(0, start_pos) +
|
| + string16(text_buffer, text_buffer + text_buffer_size) +
|
| + string_buffer_.substr(end_pos);
|
| + if (acp_start)
|
| + *acp_start = start_pos;
|
| + if (acp_end)
|
| + *acp_end = new_end_pos;
|
| + if (text_change) {
|
| + text_change->acpStart = start_pos;
|
| + text_change->acpOldEnd = end_pos;
|
| + text_change->acpNewEnd = new_end_pos;
|
| + }
|
| + selection_.set_start(start_pos);
|
| + selection_.set_end(new_end_pos);
|
| + return S_OK;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::QueryInsert(
|
| @@ -212,15 +375,24 @@ STDMETHODIMP TsfTextStore::QueryInsert(
|
| ULONG text_size,
|
| LONG* acp_result_start,
|
| LONG* acp_result_end) {
|
| - NOTIMPLEMENTED();
|
| - return E_NOTIMPL;
|
| + if ((acp_test_end < acp_test_start) ||
|
| + (acp_test_start < static_cast<LONG>(committed_size_))) {
|
| + return E_FAIL;
|
| + }
|
| + if (acp_result_start)
|
| + *acp_result_start = acp_test_start;
|
| + if (acp_result_end)
|
| + *acp_result_end = acp_test_start + text_size;
|
| + return S_OK;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::QueryInsertEmbedded(const GUID* service,
|
| const FORMATETC* format,
|
| BOOL* insertable) {
|
| - NOTIMPLEMENTED();
|
| - return E_NOTIMPL;
|
| + // We don't support any embedded objects.
|
| + if (insertable)
|
| + *insertable = FALSE;
|
| + return S_OK;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::RequestAttrsAtPosition(
|
| @@ -228,8 +400,10 @@ STDMETHODIMP TsfTextStore::RequestAttrsAtPosition(
|
| ULONG attribute_buffer_size,
|
| const TS_ATTRID* attribute_buffer,
|
| DWORD flags) {
|
| - NOTIMPLEMENTED();
|
| - return E_NOTIMPL;
|
| + // We don't support any document attributes.
|
| + // This method just returns S_OK, and the subsequently called
|
| + // RetrieveRequestedAttrs() returns 0 as the number of supported attributes.
|
| + return S_OK;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::RequestAttrsTransitioningAtPosition(
|
| @@ -237,36 +411,153 @@ STDMETHODIMP TsfTextStore::RequestAttrsTransitioningAtPosition(
|
| ULONG attribute_buffer_size,
|
| const TS_ATTRID* attribute_buffer,
|
| DWORD flags) {
|
| - NOTIMPLEMENTED();
|
| - return E_NOTIMPL;
|
| + // We don't support any document attributes.
|
| + // This method just returns S_OK, and the subsequently called
|
| + // RetrieveRequestedAttrs() returns 0 as the number of supported attributes.
|
| + return S_OK;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::RequestLock(DWORD lock_flags, HRESULT* result) {
|
| - NOTIMPLEMENTED();
|
| - return E_NOTIMPL;
|
| + if (!text_store_acp_sink_.get())
|
| + return E_FAIL;
|
| + if (!result)
|
| + return E_INVALIDARG;
|
| +
|
| + if (current_lock_type_ != 0) {
|
| + if (lock_flags & TS_LF_SYNC) {
|
| + // Can't lock synchronously.
|
| + *result = TS_E_SYNCHRONOUS;
|
| + return S_OK;
|
| + }
|
| + // Queue the lock request.
|
| + lock_queue_.push_back(lock_flags & TS_LF_READWRITE);
|
| + *result = TS_S_ASYNC;
|
| + return S_OK;
|
| + }
|
| +
|
| + // Lock
|
| + current_lock_type_ = (lock_flags & TS_LF_READWRITE);
|
| +
|
| + edit_flag_ = false;
|
| + const size_t last_committed_size = committed_size_;
|
| +
|
| + // Grant the lock.
|
| + *result = text_store_acp_sink_->OnLockGranted(current_lock_type_);
|
| +
|
| + // Unlock
|
| + current_lock_type_ = 0;
|
| +
|
| + // Handles the pending lock requests.
|
| + while (lock_queue_.size() > 0) {
|
| + current_lock_type_ = lock_queue_.front();
|
| + lock_queue_.pop_front();
|
| + text_store_acp_sink_->OnLockGranted(current_lock_type_);
|
| + current_lock_type_ = 0;
|
| + }
|
| +
|
| + if (!edit_flag_) {
|
| + return S_OK;
|
| + }
|
| +
|
| + // If the text store is edited in OnLockGranted(), we may need to call
|
| + // TextInputClient::InsertText() or TextInputClient::SetCompositionText().
|
| + const size_t new_committed_size = committed_size_;
|
| + const string16& new_committed_string =
|
| + string_buffer_.substr(last_committed_size,
|
| + new_committed_size - last_committed_size);
|
| + const string16& composition_string =
|
| + string_buffer_.substr(new_committed_size);
|
| +
|
| + // If there is new committed string, calls TextInputClient::InsertText().
|
| + if ((!new_committed_string.empty()) && text_input_client_) {
|
| + text_input_client_->InsertText(new_committed_string);
|
| + }
|
| +
|
| + // Calls TextInputClient::SetCompositionText().
|
| + CompositionText composition_text;
|
| + composition_text.text = composition_string;
|
| + composition_text.underlines = composition_undelines_;
|
| + // Adjusts the offset.
|
| + for (size_t i = 0; i < composition_text.underlines.size(); ++i) {
|
| + composition_text.underlines[i].start_offset -= new_committed_size;
|
| + composition_text.underlines[i].end_offset -= new_committed_size;
|
| + }
|
| + if (selection_.start() < new_committed_size) {
|
| + composition_text.selection.set_start(0);
|
| + } else {
|
| + composition_text.selection.set_start(
|
| + selection_.start() - new_committed_size);
|
| + }
|
| + if (selection_.end() < new_committed_size) {
|
| + composition_text.selection.set_end(0);
|
| + } else {
|
| + composition_text.selection.set_end(selection_.end() - new_committed_size);
|
| + }
|
| + if (text_input_client_)
|
| + text_input_client_->SetCompositionText(composition_text);
|
| +
|
| + // If there is no composition string, clear the text store status.
|
| + // And call OnSelectionChange(), OnLayoutChange(), and OnTextChange().
|
| + if ((composition_string.empty()) && (new_committed_size != 0)) {
|
| + string_buffer_.clear();
|
| + committed_size_ = 0;
|
| + selection_.set_start(0);
|
| + selection_.set_end(0);
|
| + if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE)
|
| + text_store_acp_sink_->OnSelectionChange();
|
| + if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE)
|
| + text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0);
|
| + if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) {
|
| + TS_TEXTCHANGE textChange;
|
| + textChange.acpStart = 0;
|
| + textChange.acpOldEnd = new_committed_size;
|
| + textChange.acpNewEnd = 0;
|
| + text_store_acp_sink_->OnTextChange(0, &textChange);
|
| + }
|
| + }
|
| +
|
| + return S_OK;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::RequestSupportedAttrs(
|
| DWORD flags,
|
| ULONG attribute_buffer_size,
|
| const TS_ATTRID* attribute_buffer) {
|
| - NOTIMPLEMENTED();
|
| - return E_NOTIMPL;
|
| + // We don't support any document attributes.
|
| + // This method just returns S_OK, and the subsequently called
|
| + // RetrieveRequestedAttrs() returns 0 as the number of supported attributes.
|
| + return S_OK;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::RetrieveRequestedAttrs(
|
| ULONG attribute_buffer_size,
|
| TS_ATTRVAL* attribute_buffer,
|
| ULONG* attribute_buffer_copied) {
|
| - NOTIMPLEMENTED();
|
| - return E_NOTIMPL;
|
| + if (attribute_buffer_copied == NULL)
|
| + return E_INVALIDARG;
|
| + // We don't support any document attributes.
|
| + attribute_buffer_copied = 0;
|
| + return S_OK;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::SetSelection(
|
| ULONG selection_buffer_size,
|
| const TS_SELECTION_ACP* selection_buffer) {
|
| - NOTIMPLEMENTED();
|
| - return E_NOTIMPL;
|
| + if (!HasReadWriteLock())
|
| + return TF_E_NOLOCK;
|
| + if (selection_buffer_size > 0) {
|
| + const LONG start_pos = selection_buffer[0].acpStart;
|
| + const LONG end_pos = selection_buffer[0].acpEnd;
|
| + if ((start_pos < static_cast<LONG>(committed_size_)) ||
|
| + (static_cast<LONG>(string_buffer_.size()) < start_pos) ||
|
| + (end_pos < start_pos) ||
|
| + (static_cast<LONG>(string_buffer_.size()) < end_pos)) {
|
| + return TF_E_INVALIDPOS;
|
| + }
|
| + selection_.set_start(start_pos);
|
| + selection_.set_end(end_pos);
|
| + }
|
| + return S_OK;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::SetText(DWORD flags,
|
| @@ -275,8 +566,34 @@ STDMETHODIMP TsfTextStore::SetText(DWORD flags,
|
| const wchar_t* text_buffer,
|
| ULONG text_buffer_size,
|
| TS_TEXTCHANGE* text_change) {
|
| - NOTIMPLEMENTED();
|
| - return E_NOTIMPL;
|
| + if (!HasReadWriteLock())
|
| + return TS_E_NOLOCK;
|
| + if (acp_start < static_cast<LONG>(committed_size_))
|
| + return TS_E_INVALIDPOS;
|
| + if (static_cast<LONG>(string_buffer_.size()) < acp_start)
|
| + return TS_E_INVALIDPOS;
|
| + if (acp_end < acp_start)
|
| + return E_INVALIDARG;
|
| + if (static_cast<LONG>(string_buffer_.size()) < acp_end)
|
| + return TS_E_INVALIDPOS;
|
| +
|
| + TS_SELECTION_ACP selection;
|
| + selection.acpStart = acp_start;
|
| + selection.acpEnd = acp_end;
|
| + selection.style.ase = TS_AE_NONE;
|
| + selection.style.fInterimChar = 0;
|
| + if (SetSelection(1, &selection) != S_OK)
|
| + return E_UNEXPECTED;
|
| +
|
| + TS_TEXTCHANGE change;
|
| + if (InsertTextAtSelection(0, text_buffer, text_buffer_size,
|
| + &acp_start, &acp_end, &change) != S_OK) {
|
| + return E_UNEXPECTED;
|
| + }
|
| + if (text_change)
|
| + *text_change = change;
|
| +
|
| + return S_OK;
|
| }
|
|
|
| STDMETHODIMP TsfTextStore::UnadviseSink(IUnknown* unknown) {
|
| @@ -290,7 +607,7 @@ STDMETHODIMP TsfTextStore::UnadviseSink(IUnknown* unknown) {
|
| STDMETHODIMP TsfTextStore::OnStartComposition(
|
| ITfCompositionView* composition_view,
|
| BOOL* ok) {
|
| - if (!ok)
|
| + if (ok)
|
| *ok = TRUE;
|
| return S_OK;
|
| }
|
| @@ -309,7 +626,146 @@ STDMETHODIMP TsfTextStore::OnEndComposition(
|
| STDMETHODIMP TsfTextStore::OnEndEdit(ITfContext* context,
|
| TfEditCookie read_only_edit_cookie,
|
| ITfEditRecord* edit_record) {
|
| + if (!context || !edit_record)
|
| + return E_INVALIDARG;
|
| +
|
| + size_t committed_size;
|
| + CompositionUnderlines undelines;
|
| + if (!GetCompositionStatus(context, read_only_edit_cookie, &committed_size,
|
| + &undelines)) {
|
| + return S_OK;
|
| + }
|
| + composition_undelines_ = undelines;
|
| + committed_size_ = committed_size;
|
| + edit_flag_ = true;
|
| return S_OK;
|
| }
|
|
|
| -} // namespace ui
|
| +bool TsfTextStore::GetDisplayAttribute(TfGuidAtom guid_atom,
|
| + TF_DISPLAYATTRIBUTE* attribute) {
|
| + GUID guid;
|
| + if (FAILED(category_manager_->GetGUID(guid_atom, &guid)))
|
| + return false;
|
| +
|
| + base::win::ScopedComPtr<ITfDisplayAttributeInfo> display_attribute_info;
|
| + if (FAILED(display_attribute_manager_->GetDisplayAttributeInfo(
|
| + guid, display_attribute_info.Receive(), NULL))) {
|
| + return false;
|
| + }
|
| + return SUCCEEDED(display_attribute_info->GetAttributeInfo(attribute));
|
| +}
|
| +
|
| +bool TsfTextStore::GetCompositionStatus(
|
| + ITfContext* context,
|
| + const TfEditCookie read_only_edit_cookie,
|
| + size_t* committed_size,
|
| + CompositionUnderlines* undelines) {
|
| + DCHECK(context);
|
| + DCHECK(committed_size);
|
| + DCHECK(undelines);
|
| + const GUID* rgGuids[2] = {&GUID_PROP_COMPOSING, &GUID_PROP_ATTRIBUTE};
|
| + base::win::ScopedComPtr<ITfReadOnlyProperty> track_property;
|
| + if (FAILED(context->TrackProperties(rgGuids, 2, NULL, 0,
|
| + track_property.Receive()))) {
|
| + return false;
|
| + }
|
| +
|
| + *committed_size = 0;
|
| + undelines->clear();
|
| + base::win::ScopedComPtr<ITfRange> start_to_end_range;
|
| + base::win::ScopedComPtr<ITfRange> end_range;
|
| + if (FAILED(context->GetStart(read_only_edit_cookie,
|
| + start_to_end_range.Receive()))) {
|
| + return false;
|
| + }
|
| + if (FAILED(context->GetEnd(read_only_edit_cookie, end_range.Receive())))
|
| + return false;
|
| + if (FAILED(start_to_end_range->ShiftEndToRange(read_only_edit_cookie,
|
| + end_range, TF_ANCHOR_END))) {
|
| + return false;
|
| + }
|
| +
|
| + base::win::ScopedComPtr<IEnumTfRanges> ranges;
|
| + if (FAILED(track_property->EnumRanges(read_only_edit_cookie, ranges.Receive(),
|
| + start_to_end_range))) {
|
| + return false;
|
| + }
|
| +
|
| + while (true) {
|
| + base::win::ScopedComPtr<ITfRange> range;
|
| + if (ranges->Next(1, range.Receive(), NULL) != S_OK)
|
| + break;
|
| + base::win::ScopedVariant value;
|
| + base::win::ScopedComPtr<IEnumTfPropertyValue> enum_prop_value;
|
| + if (FAILED(track_property->GetValue(read_only_edit_cookie, range,
|
| + value.Receive()))) {
|
| + return false;
|
| + }
|
| + if (FAILED(enum_prop_value.QueryFrom(value.AsInput()->punkVal)))
|
| + return false;
|
| +
|
| + TF_PROPERTYVAL property_value;
|
| + bool is_composition = false;
|
| + bool has_display_attribute = false;
|
| + TF_DISPLAYATTRIBUTE display_attribute;
|
| + while (enum_prop_value->Next(1, &property_value, NULL) == S_OK) {
|
| + if (IsEqualGUID(property_value.guidId, GUID_PROP_COMPOSING)) {
|
| + is_composition = (property_value.varValue.lVal == TRUE);
|
| + } else if (IsEqualGUID(property_value.guidId, GUID_PROP_ATTRIBUTE)) {
|
| + TfGuidAtom guid_atom =
|
| + static_cast<TfGuidAtom>(property_value.varValue.lVal);
|
| + if (GetDisplayAttribute(guid_atom, &display_attribute))
|
| + has_display_attribute = true;
|
| + }
|
| + VariantClear(&property_value.varValue);
|
| + }
|
| +
|
| + base::win::ScopedComPtr<ITfRangeACP> range_acp;
|
| + range_acp.QueryFrom(range);
|
| + LONG start_pos, length;
|
| + range_acp->GetExtent(&start_pos, &length);
|
| + if (!is_composition) {
|
| + if (*committed_size < static_cast<size_t>(start_pos + length))
|
| + *committed_size = start_pos + length;
|
| + } else {
|
| + CompositionUnderline underline;
|
| + underline.start_offset = start_pos;
|
| + underline.end_offset = start_pos + length;
|
| + underline.color = SK_ColorBLACK;
|
| + if (has_display_attribute)
|
| + underline.thick = !!display_attribute.fBoldLine;
|
| + undelines->push_back(underline);
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +void TsfTextStore::SetFocusedTextInputClient(
|
| + HWND focused_window,
|
| + TextInputClient* text_input_client) {
|
| + window_handle_ = focused_window;
|
| + text_input_client_ = text_input_client;
|
| +}
|
| +
|
| +void TsfTextStore::RemoveFocusedTextInputClient(
|
| + TextInputClient* text_input_client) {
|
| + if (text_input_client_ == text_input_client) {
|
| + window_handle_ = NULL;
|
| + text_input_client_ = NULL;
|
| + }
|
| +}
|
| +
|
| +void TsfTextStore::SendOnLayoutChange() {
|
| + if (text_store_acp_sink_ && (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE))
|
| + text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0);
|
| +}
|
| +
|
| +bool TsfTextStore::HasReadLock() const {
|
| + return (current_lock_type_ & TS_LF_READ) == TS_LF_READ;
|
| +}
|
| +
|
| +bool TsfTextStore::HasReadWriteLock() const {
|
| + return (current_lock_type_ & TS_LF_READWRITE) == TS_LF_READWRITE;
|
| +}
|
| +
|
| +} // namespace ui
|
|
|