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 |
+ |