summaryrefslogtreecommitdiffstats
path: root/win8/metro_driver/ime/input_source.cc
diff options
context:
space:
mode:
Diffstat (limited to 'win8/metro_driver/ime/input_source.cc')
-rw-r--r--win8/metro_driver/ime/input_source.cc170
1 files changed, 170 insertions, 0 deletions
diff --git a/win8/metro_driver/ime/input_source.cc b/win8/metro_driver/ime/input_source.cc
new file mode 100644
index 0000000..dd27380
--- /dev/null
+++ b/win8/metro_driver/ime/input_source.cc
@@ -0,0 +1,170 @@
+// Copyright 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/input_source.h"
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include <msctf.h>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "base/win/scoped_comptr.h"
+#include "ui/base/win/atl_module.h"
+#include "win8/metro_driver/ime/input_source_observer.h"
+
+namespace metro_driver {
+namespace {
+
+// An implementation of ITfLanguageProfileNotifySink interface, which will be
+// used to receive notifications when the text input source is changed.
+class ATL_NO_VTABLE InputSourceMonitor
+ : public CComObjectRootEx<CComMultiThreadModel>,
+ public ITfLanguageProfileNotifySink {
+ public:
+ InputSourceMonitor()
+ : cookie_(TF_INVALID_COOKIE) {
+ }
+
+ BEGIN_COM_MAP(InputSourceMonitor)
+ COM_INTERFACE_ENTRY(ITfLanguageProfileNotifySink)
+ END_COM_MAP()
+
+ bool Initialize(ITfSource* source) {
+ DWORD cookie = TF_INVALID_COOKIE;
+ HRESULT hr = source->AdviseSink(IID_ITfLanguageProfileNotifySink,
+ this,
+ &cookie);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "ITfSource::AdviseSink failed. hr = " << hr;
+ return false;
+ }
+ cookie_ = cookie;
+ source_ = source;
+ return true;
+ }
+
+ void SetCallback(base::Closure on_language_chanaged) {
+ on_language_chanaged_ = on_language_chanaged;
+ }
+
+ void Unadvise() {
+ if (cookie_ == TF_INVALID_COOKIE || !source_)
+ return;
+ if (FAILED(source_->UnadviseSink(cookie_)))
+ return;
+ cookie_ = TF_INVALID_COOKIE;
+ source_.Release();
+ }
+
+ private:
+ // ITfLanguageProfileNotifySink overrides:
+ STDMETHOD(OnLanguageChange)(LANGID langid, BOOL *accept) OVERRIDE {
+ if (!accept)
+ return E_INVALIDARG;
+ *accept = TRUE;
+ return S_OK;
+ }
+
+ STDMETHOD(OnLanguageChanged)() OVERRIDE {
+ if (!on_language_chanaged_.is_null())
+ on_language_chanaged_.Run();
+ return S_OK;
+ }
+
+ base::Closure on_language_chanaged_;
+ base::win::ScopedComPtr<ITfSource> source_;
+ DWORD cookie_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputSourceMonitor);
+};
+
+class InputSourceImpl : public InputSource {
+ public:
+ InputSourceImpl(ITfInputProcessorProfileMgr* profile_manager,
+ InputSourceMonitor* monitor)
+ : profile_manager_(profile_manager),
+ monitor_(monitor) {
+ monitor_->SetCallback(base::Bind(&InputSourceImpl::OnLanguageChanged,
+ base::Unretained(this)));
+ }
+ virtual ~InputSourceImpl() {
+ monitor_->SetCallback(base::Closure());
+ monitor_->Unadvise();
+ }
+
+ private:
+ // InputSource overrides.
+ virtual bool GetActiveSource(LANGID* langid, bool* is_ime) OVERRIDE {
+ TF_INPUTPROCESSORPROFILE profile = {};
+ HRESULT hr = profile_manager_->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD,
+ &profile);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "ITfInputProcessorProfileMgr::GetActiveProfile failed."
+ << " hr = " << hr;
+ return false;
+ }
+ *langid = profile.langid;
+ *is_ime = profile.dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR;
+ return true;
+ }
+ virtual void AddObserver(InputSourceObserver* observer) OVERRIDE {
+ observer_list_.AddObserver(observer);
+ }
+ virtual void RemoveObserver(InputSourceObserver* observer) OVERRIDE {
+ observer_list_.RemoveObserver(observer);
+ }
+ void OnLanguageChanged() {
+ FOR_EACH_OBSERVER(InputSourceObserver,
+ observer_list_,
+ OnInputSourceChanged());
+ }
+
+ base::win::ScopedComPtr<ITfInputProcessorProfileMgr> profile_manager_;
+ scoped_refptr<InputSourceMonitor> monitor_;
+ ObserverList<InputSourceObserver> observer_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputSourceImpl);
+};
+
+} // namespace
+
+// static
+scoped_ptr<InputSource> InputSource::Create() {
+ ui::win::CreateATLModuleIfNeeded();
+
+ base::win::ScopedComPtr<ITfInputProcessorProfileMgr> profile_manager;
+ HRESULT hr = profile_manager.CreateInstance(CLSID_TF_InputProcessorProfiles);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to instantiate CLSID_TF_InputProcessorProfiles."
+ << " hr = " << hr;
+ return scoped_ptr<InputSource>();
+ }
+ base::win::ScopedComPtr<ITfSource> profiles_source;
+ hr = profiles_source.QueryFrom(profile_manager);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "QueryFrom to ITfSource failed. hr = " << hr;
+ return scoped_ptr<InputSource>();
+ }
+
+ CComObject<InputSourceMonitor>* monitor = NULL;
+ hr = CComObject<InputSourceMonitor>::CreateInstance(&monitor);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "CComObject<InputSourceMonitor>::CreateInstance failed."
+ << " hr = " << hr;
+ return scoped_ptr<InputSource>();
+ }
+ if (!monitor->Initialize(profiles_source)) {
+ LOG(ERROR) << "Failed to initialize the monitor.";
+ return scoped_ptr<InputSource>();
+ }
+
+ // Transfer the ownership.
+ return scoped_ptr<InputSource>(new InputSourceImpl(profile_manager, monitor));
+}
+
+} // namespace metro_driver