| Index: win8/metro_driver/ime/text_service.cc
|
| diff --git a/win8/metro_driver/ime/text_service.cc b/win8/metro_driver/ime/text_service.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4d1a4188f8476d675e443a65e9e4411512aa65ed
|
| --- /dev/null
|
| +++ b/win8/metro_driver/ime/text_service.cc
|
| @@ -0,0 +1,252 @@
|
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "win8/metro_driver/ime/text_service.h"
|
| +
|
| +#include <inputscope.h>
|
| +#include <msctf.h>
|
| +
|
| +#include "base/win/scoped_variant.h"
|
| +#include "win8/metro_driver/ime/text_store.h"
|
| +
|
| +namespace metro_driver {
|
| +namespace {
|
| +
|
| +class DocumentImpl {
|
| + public:
|
| + DocumentImpl()
|
| + : edit_cookie_(TF_INVALID_COOKIE),
|
| + text_edit_sink_cookie_(TF_INVALID_COOKIE) {}
|
| +
|
| + ~DocumentImpl() {
|
| + Uninitialize();
|
| + }
|
| +
|
| + bool Initialize(ITfThreadMgr2* thread_manager,
|
| + TfClientId client_id,
|
| + TextStore* text_store) {
|
| + Uninitialize();
|
| +
|
| + base::win::ScopedComPtr<ITfDocumentMgr> document_manager;
|
| + base::win::ScopedComPtr<ITfContext> context;
|
| +
|
| + if (FAILED(thread_manager->CreateDocumentMgr(document_manager.Receive()))) {
|
| + return false;
|
| + }
|
| +
|
| + DWORD edit_cookie = TF_INVALID_EDIT_COOKIE;
|
| + if (FAILED(document_manager->CreateContext(
|
| + client_id,
|
| + 0,
|
| + static_cast<ITextStoreACP*>(text_store),
|
| + context.Receive(),
|
| + &edit_cookie))) {
|
| + return false;
|
| + }
|
| +
|
| + if (FAILED(document_manager->Push(context))) {
|
| + return false;
|
| + }
|
| +
|
| + base::win::ScopedComPtr<ITfSource> source;
|
| + DWORD text_edit_sink_cookie = TF_INVALID_EDIT_COOKIE;
|
| + if (text_store) {
|
| + if (FAILED(source.QueryFrom(context))) {
|
| + return false;
|
| + }
|
| + if (FAILED(source->AdviseSink(IID_ITfTextEditSink,
|
| + static_cast<ITfTextEditSink*>(text_store),
|
| + &text_edit_sink_cookie))) {
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + document_manager_ = document_manager;
|
| + context_ = context;
|
| + text_store_ = text_store;
|
| + text_edit_sink_source_ = source;
|
| + edit_cookie_ = edit_cookie;
|
| + text_edit_sink_cookie_ = text_edit_sink_cookie;
|
| + return true;
|
| + }
|
| +
|
| + void Uninitialize() {
|
| + if (text_edit_sink_source_ &&
|
| + (text_edit_sink_cookie_ != TF_INVALID_COOKIE)) {
|
| + text_edit_sink_source_->UnadviseSink(text_edit_sink_cookie_);
|
| + text_edit_sink_cookie_ = TF_INVALID_COOKIE;
|
| + text_edit_sink_source_.Release();
|
| + }
|
| + document_manager_.Release();
|
| +
|
| + scoped_refptr<TextStore> text_store;
|
| + text_store_.swap(text_store);
|
| + }
|
| +
|
| + ITfDocumentMgr* document_manager() const {
|
| + return document_manager_;
|
| + }
|
| +
|
| + ITfContext* context() const {
|
| + return context_;
|
| + }
|
| +
|
| + scoped_refptr<TextStore> text_store() {
|
| + return text_store_;
|
| + }
|
| +
|
| + private:
|
| + base::win::ScopedComPtr<ITfDocumentMgr> document_manager_;
|
| + base::win::ScopedComPtr<ITfSource> text_edit_sink_source_;
|
| + base::win::ScopedComPtr<ITfContext> context_;
|
| + scoped_refptr<TextStore> text_store_;
|
| + DWORD edit_cookie_;
|
| + DWORD text_edit_sink_cookie_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(DocumentImpl);
|
| +};
|
| +
|
| +// Japanese IME expects the default value of this compartment is
|
| +// TF_SENTENCEMODE_PHRASEPREDICT like IMM32 implementation. This value is
|
| +// managed per thread, thus setting this value at once is enough. This
|
| +// value never affects other IMEs except for Japanese.
|
| +bool InitializeInputModeSentene(ITfThreadMgr2* thread_manager,
|
| + TfClientId client_id) {
|
| + base::win::ScopedComPtr<ITfCompartmentMgr> thread_compartment_manager;
|
| + if (FAILED(thread_compartment_manager.QueryFrom(thread_manager)))
|
| + return false;
|
| +
|
| + base::win::ScopedComPtr<ITfCompartment> sentence_compartment;
|
| + if (FAILED(thread_compartment_manager->GetCompartment(
|
| + GUID_COMPARTMENT_KEYBOARD_INPUTMODE_SENTENCE,
|
| + sentence_compartment.Receive())))
|
| + return false;
|
| +
|
| + base::win::ScopedVariant sentence_variant;
|
| + sentence_variant.Set(TF_SENTENCEMODE_PHRASEPREDICT);
|
| + if (FAILED(sentence_compartment->SetValue(client_id, &sentence_variant)))
|
| + return false;
|
| + return true;
|
| +}
|
| +
|
| +// Initializes |context| where an IME will be disabled.
|
| +bool InitializeDisabledContext(ITfContext* context, TfClientId client_id) {
|
| + base::win::ScopedComPtr<ITfCompartmentMgr> compartment_mgr;
|
| + if (FAILED(compartment_mgr.QueryFrom(context)))
|
| + return false;
|
| +
|
| + base::win::ScopedComPtr<ITfCompartment> disabled_compartment;
|
| + if (FAILED(compartment_mgr->GetCompartment(
|
| + GUID_COMPARTMENT_KEYBOARD_DISABLED,
|
| + disabled_compartment.Receive()))) {
|
| + return false;
|
| + }
|
| +
|
| + base::win::ScopedVariant variant;
|
| + variant.Set(1);
|
| + if (FAILED(disabled_compartment->SetValue(client_id, &variant)))
|
| + return false;
|
| +
|
| + base::win::ScopedComPtr<ITfCompartment> empty_context;
|
| + if (FAILED(compartment_mgr->GetCompartment(GUID_COMPARTMENT_EMPTYCONTEXT,
|
| + empty_context.Receive()))) {
|
| + return false;
|
| + }
|
| +
|
| + base::win::ScopedVariant empty_context_variant;
|
| + empty_context_variant.Set(static_cast<int32>(1));
|
| + if (FAILED(empty_context->SetValue(client_id, &empty_context_variant)))
|
| + return false;
|
| +
|
| + return true;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +struct TextService::InternalState {
|
| + InternalState()
|
| + : client_id(TF_CLIENTID_NULL),
|
| + win_event_hook_initialized(false),
|
| + window_handle(NULL),
|
| + text_store_delegate(NULL) {}
|
| +
|
| + TfClientId client_id;
|
| + bool win_event_hook_initialized;
|
| + scoped_ptr<DocumentImpl> current_document;
|
| + base::win::ScopedComPtr<ITfThreadMgr2> thread_manager;
|
| + HWND window_handle;
|
| + TextStoreDelegate* text_store_delegate;
|
| +};
|
| +
|
| +TextService::TextService(HWND window_handle,
|
| + TextStoreDelegate* text_store_delegate)
|
| + : state_(new InternalState) {
|
| + if (FAILED(state_->thread_manager.CreateInstance(CLSID_TF_ThreadMgr)))
|
| + return;
|
| + if (FAILED(state_->thread_manager->ActivateEx(&state_->client_id, 0)))
|
| + return;
|
| + state_->window_handle = window_handle;
|
| + state_->text_store_delegate = text_store_delegate;
|
| + InitializeInputModeSentene(state_->thread_manager, state_->client_id);
|
| +}
|
| +
|
| +TextService::~TextService() {
|
| + state_->current_document.reset(NULL);
|
| + state_->thread_manager->Deactivate();
|
| + state_.reset(NULL);
|
| +}
|
| +
|
| +void TextService::CancelComposition() {
|
| + DocumentImpl* document = state_->current_document.get();
|
| + if (!document)
|
| + return;
|
| + TextStore* text_store = document->text_store();
|
| + if (!text_store)
|
| + return;
|
| + text_store->CancelComposition();
|
| +}
|
| +
|
| +void TextService::OnDocumentTypeChanged(
|
| + const std::vector<int32>& input_scopes) {
|
| + state_->current_document.reset(new DocumentImpl);
|
| + TextStore* text_store = NULL;
|
| + if (!input_scopes.empty()) {
|
| + std::vector<InputScope> buffer(input_scopes.size());
|
| + for (size_t i = 0; i < input_scopes.size(); ++i)
|
| + buffer[i] = static_cast<InputScope>(input_scopes[i]);
|
| + text_store = new TextStore(
|
| + state_->window_handle, buffer, state_->text_store_delegate);
|
| + }
|
| + scoped_ptr<DocumentImpl> new_document(new DocumentImpl);
|
| + if (!new_document->Initialize(state_->thread_manager.get(),
|
| + state_->client_id,
|
| + text_store)) {
|
| + return;
|
| + }
|
| + const bool has_password_field =
|
| + std::find(input_scopes.begin(), input_scopes.end(), IS_PASSWORD) !=
|
| + input_scopes.end();
|
| + const bool use_disabled_context = input_scopes.empty() || has_password_field;
|
| + if (use_disabled_context &&
|
| + !InitializeDisabledContext(new_document->context(), state_->client_id)) {
|
| + return;
|
| + }
|
| + state_->current_document.swap(new_document);
|
| + if (FAILED(state_->thread_manager->SetFocus(
|
| + state_->current_document->document_manager()))) {
|
| + return;
|
| + }
|
| + return;
|
| +}
|
| +
|
| +void TextService::OnWindowActivated() {
|
| + DocumentImpl* document = state_->current_document.get();
|
| + if (!document)
|
| + return;
|
| + if (FAILED(state_->thread_manager->SetFocus(document->document_manager())))
|
| + return;
|
| +}
|
| +
|
| +} // namespace metro_driver
|
| +
|
|
|