This source file includes following definitions.
- InitializeSentenceMode
- InitializeDisabledContext
- IsPasswordField
- source_
- CreateTextEditSink
- Create
- document_manager
- text_store
- text_edit_sink_
- thread_manager_
- CancelComposition
- OnDocumentChanged
- OnWindowActivated
- OnCompositionChanged
- OnTextCommitted
- GetCaretBounds
- GetCompositionCharacterBounds
- OnDocumentTypeChanged
- CreateTextService
#include "win8/metro_driver/ime/text_service.h"
#include <msctf.h>
#include "base/logging.h"
#include "base/win/scoped_variant.h"
#include "ui/metro_viewer/ime_types.h"
#include "win8/metro_driver/ime/text_service_delegate.h"
#include "win8/metro_driver/ime/text_store.h"
#include "win8/metro_driver/ime/text_store_delegate.h"
namespace metro_driver {
namespace {
bool InitializeSentenceMode(ITfThreadMgr2* thread_manager,
TfClientId client_id) {
base::win::ScopedComPtr<ITfCompartmentMgr> thread_compartment_manager;
HRESULT hr = thread_compartment_manager.QueryFrom(thread_manager);
if (FAILED(hr)) {
LOG(ERROR) << "QueryFrom failed. hr = " << hr;
return false;
}
base::win::ScopedComPtr<ITfCompartment> sentence_compartment;
hr = thread_compartment_manager->GetCompartment(
GUID_COMPARTMENT_KEYBOARD_INPUTMODE_SENTENCE,
sentence_compartment.Receive());
if (FAILED(hr)) {
LOG(ERROR) << "ITfCompartment::GetCompartment failed. hr = " << hr;
return false;
}
base::win::ScopedVariant sentence_variant;
sentence_variant.Set(TF_SENTENCEMODE_PHRASEPREDICT);
hr = sentence_compartment->SetValue(client_id, &sentence_variant);
if (FAILED(hr)) {
LOG(ERROR) << "ITfCompartment::SetValue failed. hr = " << hr;
return false;
}
return true;
}
bool InitializeDisabledContext(ITfContext* context, TfClientId client_id) {
base::win::ScopedComPtr<ITfCompartmentMgr> compartment_mgr;
HRESULT hr = compartment_mgr.QueryFrom(context);
if (FAILED(hr)) {
LOG(ERROR) << "QueryFrom failed. hr = " << hr;
return false;
}
base::win::ScopedComPtr<ITfCompartment> disabled_compartment;
hr = compartment_mgr->GetCompartment(GUID_COMPARTMENT_KEYBOARD_DISABLED,
disabled_compartment.Receive());
if (FAILED(hr)) {
LOG(ERROR) << "ITfCompartment::GetCompartment failed. hr = " << hr;
return false;
}
base::win::ScopedVariant variant;
variant.Set(1);
hr = disabled_compartment->SetValue(client_id, &variant);
if (FAILED(hr)) {
LOG(ERROR) << "ITfCompartment::SetValue failed. hr = " << hr;
return false;
}
base::win::ScopedComPtr<ITfCompartment> empty_context;
hr = compartment_mgr->GetCompartment(GUID_COMPARTMENT_EMPTYCONTEXT,
empty_context.Receive());
if (FAILED(hr)) {
LOG(ERROR) << "ITfCompartment::GetCompartment failed. hr = " << hr;
return false;
}
base::win::ScopedVariant empty_context_variant;
empty_context_variant.Set(static_cast<int32>(1));
hr = empty_context->SetValue(client_id, &empty_context_variant);
if (FAILED(hr)) {
LOG(ERROR) << "ITfCompartment::SetValue failed. hr = " << hr;
return false;
}
return true;
}
bool IsPasswordField(const std::vector<InputScope>& input_scopes) {
return std::find(input_scopes.begin(), input_scopes.end(), IS_PASSWORD) !=
input_scopes.end();
}
class EventSink {
public:
EventSink(DWORD cookie, base::win::ScopedComPtr<ITfSource> source)
: cookie_(cookie),
source_(source) {}
~EventSink() {
if (!source_ || cookie_ != TF_INVALID_COOKIE)
return;
source_->UnadviseSink(cookie_);
cookie_ = TF_INVALID_COOKIE;
source_.Release();
}
private:
DWORD cookie_;
base::win::ScopedComPtr<ITfSource> source_;
DISALLOW_COPY_AND_ASSIGN(EventSink);
};
scoped_ptr<EventSink> CreateTextEditSink(ITfContext* context,
ITfTextEditSink* text_store) {
DCHECK(text_store);
base::win::ScopedComPtr<ITfSource> source;
DWORD cookie = TF_INVALID_EDIT_COOKIE;
HRESULT hr = source.QueryFrom(context);
if (FAILED(hr)) {
LOG(ERROR) << "QueryFrom failed, hr = " << hr;
return scoped_ptr<EventSink>();
}
hr = source->AdviseSink(IID_ITfTextEditSink, text_store, &cookie);
if (FAILED(hr)) {
LOG(ERROR) << "AdviseSink failed, hr = " << hr;
return scoped_ptr<EventSink>();
}
return scoped_ptr<EventSink>(new EventSink(cookie, source));
}
class DocumentBinding {
public:
~DocumentBinding() {
if (!document_manager_)
return;
document_manager_->Pop(TF_POPF_ALL);
}
static scoped_ptr<DocumentBinding> Create(
ITfThreadMgr2* thread_manager,
TfClientId client_id,
const std::vector<InputScope>& input_scopes,
HWND window_handle,
TextStoreDelegate* delegate) {
base::win::ScopedComPtr<ITfDocumentMgr> document_manager;
HRESULT hr = thread_manager->CreateDocumentMgr(document_manager.Receive());
if (FAILED(hr)) {
LOG(ERROR) << "ITfThreadMgr2::CreateDocumentMgr failed. hr = " << hr;
return scoped_ptr<DocumentBinding>();
}
const bool use_null_text_store = input_scopes.empty();
scoped_refptr<TextStore> text_store;
if (!use_null_text_store) {
text_store = TextStore::Create(window_handle, input_scopes, delegate);
if (!text_store) {
LOG(ERROR) << "Failed to create TextStore.";
return scoped_ptr<DocumentBinding>();
}
}
base::win::ScopedComPtr<ITfContext> context;
DWORD edit_cookie = TF_INVALID_EDIT_COOKIE;
hr = document_manager->CreateContext(
client_id,
0,
static_cast<ITextStoreACP*>(text_store.get()),
context.Receive(),
&edit_cookie);
if (FAILED(hr)) {
LOG(ERROR) << "ITfDocumentMgr::CreateContext failed. hr = " << hr;
return scoped_ptr<DocumentBinding>();
}
if ((use_null_text_store || IsPasswordField(input_scopes)) &&
!InitializeDisabledContext(context, client_id)) {
LOG(ERROR) << "InitializeDisabledContext failed.";
return scoped_ptr<DocumentBinding>();
}
scoped_ptr<EventSink> text_edit_sink;
if (!use_null_text_store) {
text_edit_sink = CreateTextEditSink(context, text_store);
if (!text_edit_sink) {
LOG(ERROR) << "CreateTextEditSink failed.";
return scoped_ptr<DocumentBinding>();
}
}
hr = document_manager->Push(context);
if (FAILED(hr)) {
LOG(ERROR) << "ITfDocumentMgr::Push failed. hr = " << hr;
return scoped_ptr<DocumentBinding>();
}
return scoped_ptr<DocumentBinding>(
new DocumentBinding(text_store,
document_manager,
text_edit_sink.Pass()));
}
ITfDocumentMgr* document_manager() const {
return document_manager_;
}
scoped_refptr<TextStore> text_store() const {
return text_store_;
}
private:
DocumentBinding(scoped_refptr<TextStore> text_store,
base::win::ScopedComPtr<ITfDocumentMgr> document_manager,
scoped_ptr<EventSink> text_edit_sink)
: text_store_(text_store),
document_manager_(document_manager),
text_edit_sink_(text_edit_sink.Pass()) {}
scoped_refptr<TextStore> text_store_;
base::win::ScopedComPtr<ITfDocumentMgr> document_manager_;
scoped_ptr<EventSink> text_edit_sink_;
DISALLOW_COPY_AND_ASSIGN(DocumentBinding);
};
class TextServiceImpl : public TextService,
public TextStoreDelegate {
public:
TextServiceImpl(ITfThreadMgr2* thread_manager,
TfClientId client_id,
HWND window_handle,
TextServiceDelegate* delegate)
: client_id_(client_id),
window_handle_(window_handle),
delegate_(delegate),
thread_manager_(thread_manager) {
DCHECK_NE(TF_CLIENTID_NULL, client_id);
DCHECK(window_handle != NULL);
DCHECK(thread_manager_);
}
virtual ~TextServiceImpl() {
thread_manager_->Deactivate();
}
private:
virtual void TextService::CancelComposition() OVERRIDE {
if (!current_document_) {
VLOG(0) << "|current_document_| is NULL due to the previous error.";
return;
}
TextStore* text_store = current_document_->text_store();
if (!text_store)
return;
text_store->CancelComposition();
}
virtual void OnDocumentChanged(
const std::vector<int32>& input_scopes,
const std::vector<metro_viewer::CharacterBounds>& character_bounds)
OVERRIDE {
bool document_type_changed = input_scopes_ != input_scopes;
input_scopes_ = input_scopes;
composition_character_bounds_ = character_bounds;
if (document_type_changed)
OnDocumentTypeChanged(input_scopes);
}
virtual void OnWindowActivated() OVERRIDE {
if (!current_document_) {
VLOG(0) << "|current_document_| is NULL due to the previous error.";
return;
}
ITfDocumentMgr* document_manager = current_document_->document_manager();
if (!document_manager) {
VLOG(0) << "|document_manager| is NULL due to the previous error.";
return;
}
HRESULT hr = thread_manager_->SetFocus(document_manager);
if (FAILED(hr)) {
LOG(ERROR) << "ITfThreadMgr2::SetFocus failed. hr = " << hr;
return;
}
}
virtual void OnCompositionChanged(
const base::string16& text,
int32 selection_start,
int32 selection_end,
const std::vector<metro_viewer::UnderlineInfo>& underlines) OVERRIDE {
if (!delegate_)
return;
delegate_->OnCompositionChanged(text,
selection_start,
selection_end,
underlines);
}
virtual void OnTextCommitted(const base::string16& text) OVERRIDE {
if (!delegate_)
return;
delegate_->OnTextCommitted(text);
}
virtual RECT GetCaretBounds() {
if (composition_character_bounds_.empty()) {
const RECT rect = {};
return rect;
}
const metro_viewer::CharacterBounds& bounds =
composition_character_bounds_[0];
POINT left_top = { bounds.left, bounds.top };
POINT right_bottom = { bounds.right, bounds.bottom };
ClientToScreen(window_handle_, &left_top);
ClientToScreen(window_handle_, &right_bottom);
const RECT rect = {
left_top.x,
left_top.y,
right_bottom.x,
right_bottom.y,
};
return rect;
}
virtual bool GetCompositionCharacterBounds(uint32 index,
RECT* rect) OVERRIDE {
if (index >= composition_character_bounds_.size()) {
return false;
}
const metro_viewer::CharacterBounds& bounds =
composition_character_bounds_[index];
POINT left_top = { bounds.left, bounds.top };
POINT right_bottom = { bounds.right, bounds.bottom };
ClientToScreen(window_handle_, &left_top);
ClientToScreen(window_handle_, &right_bottom);
SetRect(rect, left_top.x, left_top.y, right_bottom.x, right_bottom.y);
return true;
}
void OnDocumentTypeChanged(const std::vector<int32>& input_scopes) {
std::vector<InputScope> native_input_scopes(input_scopes.size());
for (size_t i = 0; i < input_scopes.size(); ++i)
native_input_scopes[i] = static_cast<InputScope>(input_scopes[i]);
scoped_ptr<DocumentBinding> new_document =
DocumentBinding::Create(thread_manager_.get(),
client_id_,
native_input_scopes,
window_handle_,
this);
LOG_IF(ERROR, !new_document) << "Failed to create a new document.";
current_document_.swap(new_document);
OnWindowActivated();
}
TfClientId client_id_;
HWND window_handle_;
TextServiceDelegate* delegate_;
scoped_ptr<DocumentBinding> current_document_;
base::win::ScopedComPtr<ITfThreadMgr2> thread_manager_;
std::vector<int32> input_scopes_;
std::vector<metro_viewer::CharacterBounds> composition_character_bounds_;
DISALLOW_COPY_AND_ASSIGN(TextServiceImpl);
};
}
scoped_ptr<TextService>
CreateTextService(TextServiceDelegate* delegate, HWND window_handle) {
if (!delegate)
return scoped_ptr<TextService>();
base::win::ScopedComPtr<ITfThreadMgr2> thread_manager;
HRESULT hr = thread_manager.CreateInstance(CLSID_TF_ThreadMgr);
if (FAILED(hr)) {
LOG(ERROR) << "Failed to create instance of CLSID_TF_ThreadMgr. hr = "
<< hr;
return scoped_ptr<TextService>();
}
TfClientId client_id = TF_CLIENTID_NULL;
hr = thread_manager->ActivateEx(&client_id, 0);
if (FAILED(hr)) {
LOG(ERROR) << "ITfThreadMgr2::ActivateEx failed. hr = " << hr;
return scoped_ptr<TextService>();
}
if (!InitializeSentenceMode(thread_manager, client_id)) {
LOG(ERROR) << "InitializeSentenceMode failed.";
thread_manager->Deactivate();
return scoped_ptr<TextService>();
}
return scoped_ptr<TextService>(new TextServiceImpl(thread_manager,
client_id,
window_handle,
delegate));
}
}