Commit e7888801 authored by Yukawa@chromium.org's avatar Yukawa@chromium.org

Support CancelCompositionText/ComfirmCompositionText in TSF.

Currently CancelCompositionText is not fully implemented in
InputMethodTSF. This CL fixes the issue.

Note:
CUAS is also implementing the protocol conversions from
  ImmNotifyIME(NI_COMPOSITIONSTR, CPS_CANCEL, 0)
and
  ImmNotifyIME(NI_COMPOSITIONSTR, CPS_COMPLETE, 0)
into TSF operations, respectively. To minimize the risk of
compatibility issues, this CL uses the the same protocol
conversions logic to CUAS.

BUG=246534
TEST=Tested with GoogleJapaneseInput

Review URL: https://chromiumcodereview.appspot.com/16901011

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@208124 0039d316-1c4b-4281-b951-d872f2087c98
parent b1ae901f
......@@ -128,12 +128,8 @@ void InputMethodTSF::OnDidChangeFocusedClient(TextInputClient* focused_before,
}
void InputMethodTSF::ConfirmCompositionText() {
if (!IsTextInputTypeNone()) {
// TSFBridge has not implemented ConfirmComposition yet. So here cancel
// the composition instead as a workaround.
// TODO(ime): Implement ConfirmComposition for TSF.
ui::TSFBridge::GetInstance()->CancelComposition();
}
if (!IsTextInputTypeNone())
ui::TSFBridge::GetInstance()->ConfirmComposition();
}
bool InputMethodTSF::IsWindowFocused(const TextInputClient* client) const {
......
......@@ -13,6 +13,7 @@ MockTSFBridge::MockTSFBridge()
: enable_ime_call_count_(0),
disalbe_ime_call_count_(0),
cancel_composition_call_count_(0),
confirm_composition_call_count_(0),
on_text_layout_changed_(0),
associate_focus_call_count_(0),
set_focused_client_call_count_(0),
......@@ -30,6 +31,11 @@ bool MockTSFBridge::CancelComposition() {
return true;
}
bool MockTSFBridge::ConfirmComposition() {
++confirm_composition_call_count_;
return true;
}
void MockTSFBridge::OnTextInputTypeChanged(const TextInputClient* client) {
latest_text_input_type_ = client->GetTextInputType();
}
......@@ -64,6 +70,7 @@ void MockTSFBridge::Reset() {
enable_ime_call_count_ = 0;
disalbe_ime_call_count_ = 0;
cancel_composition_call_count_ = 0;
confirm_composition_call_count_ = 0;
on_text_layout_changed_ = 0;
associate_focus_call_count_ = 0;
set_focused_client_call_count_ = 0;
......
......@@ -21,6 +21,7 @@ class MockTSFBridge : public TSFBridge {
// TSFBridge:
virtual bool CancelComposition() OVERRIDE;
virtual bool ConfirmComposition() OVERRIDE;
virtual void OnTextInputTypeChanged(const TextInputClient* client) OVERRIDE;
virtual void OnTextLayoutChanged() OVERRIDE;
virtual void SetFocusedClient(HWND focused_window,
......@@ -43,6 +44,11 @@ class MockTSFBridge : public TSFBridge {
return cancel_composition_call_count_;
}
// Call count of ConfirmComposition().
int confirm_composition_call_count() const {
return confirm_composition_call_count_;
}
// Call count of OnTextLayoutChanged().
int on_text_layout_changed() const {
return on_text_layout_changed_;
......@@ -76,6 +82,7 @@ class MockTSFBridge : public TSFBridge {
int enable_ime_call_count_;
int disalbe_ime_call_count_;
int cancel_composition_call_count_;
int confirm_composition_call_count_;
int on_text_layout_changed_;
int associate_focus_call_count_;
int set_focused_client_call_count_;
......
......@@ -39,6 +39,7 @@ class TSFBridgeDelegate : public TSFBridge {
virtual void OnTextInputTypeChanged(const TextInputClient* client) OVERRIDE;
virtual void OnTextLayoutChanged() OVERRIDE;
virtual bool CancelComposition() OVERRIDE;
virtual bool ConfirmComposition() OVERRIDE;
virtual void SetFocusedClient(HWND focused_window,
TextInputClient* client) OVERRIDE;
virtual void RemoveFocusedClient(TextInputClient* client) OVERRIDE;
......@@ -213,38 +214,26 @@ bool TSFBridgeDelegate::CancelComposition() {
DCHECK_EQ(base::MessageLoop::TYPE_UI, base::MessageLoop::current()->type());
DCHECK(IsInitialized());
base::win::ScopedComPtr<ITfDocumentMgr> focused_document_manager;
for (TSFDocumentMap::iterator it = tsf_document_map_.begin();
it != tsf_document_map_.end(); ++it) {
if (IsFocused(it->second.document_manager.get())) {
focused_document_manager = it->second.document_manager.get();
break;
}
}
if (focused_document_manager.get() == NULL)
TSFDocument* document = GetAssociatedDocument();
if (!document)
return false;
base::win::ScopedComPtr<ITfContext> context;
// We should use ITfDocumentMgr::GetBase instead of ITfDocumentMgr::GetTop,
// which may return a temporal context created by an IME for its modal UI
// handling, to obtain a context against which on-going composition is
// canceled. This is because ITfDocumentMgr::GetBase always returns the
// context that is created by us and owns the on-going composition.
// See http://crbug.com/169664 for details.
if (FAILED(focused_document_manager->GetBase(context.Receive()))) {
DVLOG(1) << "Failed to get top context.";
if (!document->text_store)
return false;
}
base::win::ScopedComPtr<ITfContextOwnerCompositionServices> owner;
if (FAILED(owner.QueryFrom(context))) {
DVLOG(1) << "Failed to get ITfContextOwnerCompositionService.";
return document->text_store->CancelComposition();
}
bool TSFBridgeDelegate::ConfirmComposition() {
DCHECK_EQ(base::MessageLoop::TYPE_UI, base::MessageLoop::current()->type());
DCHECK(IsInitialized());
TSFDocument* document = GetAssociatedDocument();
if (!document)
return false;
}
// Cancel all compositions.
owner->TerminateComposition(NULL);
return true;
if (!document->text_store)
return false;
return document->text_store->ConfirmComposition();
}
void TSFBridgeDelegate::SetFocusedClient(HWND focused_window,
......
......@@ -55,9 +55,17 @@ class UI_EXPORT TSFBridge {
virtual void OnTextLayoutChanged() = 0;
// Cancels the ongoing composition if exists.
// Returns true if there is no composition.
// Returns false if an edit session is on-going.
// Returns false if an error occures.
virtual bool CancelComposition() = 0;
// Confirms the ongoing composition if exists.
// Returns true if there is no composition.
// Returns false if an edit session is on-going.
// Returns false if an error occures.
virtual bool ConfirmComposition() = 0;
// Sets currently focused TextInputClient.
// Caller must free |client|.
virtual void SetFocusedClient(HWND focused_window,
......
......@@ -831,6 +831,76 @@ void TSFTextStore::RemoveFocusedTextInputClient(
}
}
bool TSFTextStore::CancelComposition() {
// There is an on-going document lock. We must not edit the text!
if (!edit_flag_)
return false;
if (string_buffer_.empty())
return true;
// Unlike ImmNotifyIME(NI_COMPOSITIONSTR, CPS_CANCEL, 0) in IMM32, TSF does
// not have a dedicated method to cancel composition. However, CUAS actually
// has a protocol conversion from CPS_CANCEL into TSF operations. According
// to the observations on Windows 7, TIPs are expected to cancel composition
// when an on-going composition text is replaced with an empty string. So
// we use the same operation to cancel composition here to minimize the risk
// of potential compatibility issues.
const size_t previous_buffer_size = string_buffer_.size();
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 = previous_buffer_size;
textChange.acpNewEnd = 0;
text_store_acp_sink_->OnTextChange(0, &textChange);
}
return true;
}
bool TSFTextStore::ConfirmComposition() {
// There is an on-going document lock. We must not edit the text!
if (!edit_flag_)
return false;
if (string_buffer_.empty())
return true;
// See the comment in TSFTextStore::CancelComposition.
// This logic is based on the observation about how to emulate
// ImmNotifyIME(NI_COMPOSITIONSTR, CPS_COMPLETE, 0) by CUAS.
const string16& composition_text = string_buffer_.substr(committed_size_);
if (!composition_text.empty())
text_input_client_->InsertText(composition_text);
const size_t previous_buffer_size = string_buffer_.size();
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 = previous_buffer_size;
textChange.acpNewEnd = 0;
text_store_acp_sink_->OnTextChange(0, &textChange);
}
return true;
}
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);
......
......@@ -202,6 +202,12 @@ class UI_EXPORT TSFTextStore : public ITextStoreACP,
// Removes currently focused TextInputClient.
void RemoveFocusedTextInputClient(TextInputClient* text_input_client);
// Cancels the ongoing composition if exists.
bool CancelComposition();
// Confirms the ongoing composition if exists.
bool ConfirmComposition();
// Sends OnLayoutChange() via |text_store_acp_sink_|.
void SendOnLayoutChange();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment