blob: 6a63751b31a473eb7d0a3fc80ba005650ebb334a [file] [log] [blame]
// 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_.get())
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.get());
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.get())) {
LOG(ERROR) << "Failed to initialize the monitor.";
return scoped_ptr<InputSource>();
}
// Transfer the ownership.
return scoped_ptr<InputSource>(
new InputSourceImpl(profile_manager.get(), monitor));
}
} // namespace metro_driver