• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "win8/metro_driver/ime/input_source.h"
6 
7 #include <atlbase.h>
8 #include <atlcom.h>
9 #include <msctf.h>
10 
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/logging.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/observer_list.h"
16 #include "base/win/scoped_comptr.h"
17 #include "ui/base/win/atl_module.h"
18 #include "win8/metro_driver/ime/input_source_observer.h"
19 
20 namespace metro_driver {
21 namespace {
22 
23 // An implementation of ITfLanguageProfileNotifySink interface, which will be
24 // used to receive notifications when the text input source is changed.
25 class ATL_NO_VTABLE InputSourceMonitor
26     : public CComObjectRootEx<CComMultiThreadModel>,
27       public ITfLanguageProfileNotifySink {
28  public:
InputSourceMonitor()29   InputSourceMonitor()
30       : cookie_(TF_INVALID_COOKIE) {
31   }
32 
33   BEGIN_COM_MAP(InputSourceMonitor)
COM_INTERFACE_ENTRY(ITfLanguageProfileNotifySink)34     COM_INTERFACE_ENTRY(ITfLanguageProfileNotifySink)
35   END_COM_MAP()
36 
37   bool Initialize(ITfSource* source) {
38     DWORD cookie = TF_INVALID_COOKIE;
39     HRESULT hr = source->AdviseSink(IID_ITfLanguageProfileNotifySink,
40                                     this,
41                                     &cookie);
42     if (FAILED(hr)) {
43       LOG(ERROR) << "ITfSource::AdviseSink failed. hr = " << hr;
44       return false;
45     }
46     cookie_ = cookie;
47     source_ = source;
48     return true;
49   }
50 
SetCallback(base::Closure on_language_chanaged)51   void SetCallback(base::Closure on_language_chanaged) {
52     on_language_chanaged_ = on_language_chanaged;
53   }
54 
Unadvise()55   void Unadvise() {
56     if (cookie_ == TF_INVALID_COOKIE || !source_)
57       return;
58     if (FAILED(source_->UnadviseSink(cookie_)))
59       return;
60     cookie_ = TF_INVALID_COOKIE;
61     source_.Release();
62   }
63 
64  private:
65   // ITfLanguageProfileNotifySink overrides:
STDMETHOD(OnLanguageChange)66   STDMETHOD(OnLanguageChange)(LANGID langid, BOOL *accept) OVERRIDE {
67     if (!accept)
68       return E_INVALIDARG;
69     *accept = TRUE;
70     return S_OK;
71   }
72 
STDMETHOD(OnLanguageChanged)73   STDMETHOD(OnLanguageChanged)() OVERRIDE {
74     if (!on_language_chanaged_.is_null())
75       on_language_chanaged_.Run();
76     return S_OK;
77   }
78 
79   base::Closure on_language_chanaged_;
80   base::win::ScopedComPtr<ITfSource> source_;
81   DWORD cookie_;
82 
83   DISALLOW_COPY_AND_ASSIGN(InputSourceMonitor);
84 };
85 
86 class InputSourceImpl : public InputSource {
87  public:
InputSourceImpl(ITfInputProcessorProfileMgr * profile_manager,InputSourceMonitor * monitor)88   InputSourceImpl(ITfInputProcessorProfileMgr* profile_manager,
89                   InputSourceMonitor* monitor)
90       : profile_manager_(profile_manager),
91         monitor_(monitor) {
92     monitor_->SetCallback(base::Bind(&InputSourceImpl::OnLanguageChanged,
93                                      base::Unretained(this)));
94   }
~InputSourceImpl()95   virtual ~InputSourceImpl() {
96     monitor_->SetCallback(base::Closure());
97     monitor_->Unadvise();
98   }
99 
100  private:
101   // InputSource overrides.
GetActiveSource(LANGID * langid,bool * is_ime)102   virtual bool GetActiveSource(LANGID* langid, bool* is_ime) OVERRIDE {
103     TF_INPUTPROCESSORPROFILE profile = {};
104     HRESULT hr = profile_manager_->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD,
105                                                     &profile);
106     if (FAILED(hr)) {
107       LOG(ERROR) << "ITfInputProcessorProfileMgr::GetActiveProfile failed."
108                  << " hr = " << hr;
109       return false;
110     }
111     *langid = profile.langid;
112     *is_ime = profile.dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR;
113     return true;
114   }
AddObserver(InputSourceObserver * observer)115   virtual void AddObserver(InputSourceObserver* observer) OVERRIDE {
116     observer_list_.AddObserver(observer);
117   }
RemoveObserver(InputSourceObserver * observer)118   virtual void RemoveObserver(InputSourceObserver* observer) OVERRIDE {
119     observer_list_.RemoveObserver(observer);
120   }
OnLanguageChanged()121   void OnLanguageChanged() {
122     FOR_EACH_OBSERVER(InputSourceObserver,
123                       observer_list_,
124                       OnInputSourceChanged());
125   }
126 
127   base::win::ScopedComPtr<ITfInputProcessorProfileMgr> profile_manager_;
128   scoped_refptr<InputSourceMonitor> monitor_;
129   ObserverList<InputSourceObserver> observer_list_;
130 
131   DISALLOW_COPY_AND_ASSIGN(InputSourceImpl);
132 };
133 
134 }  // namespace
135 
136 // static
Create()137 scoped_ptr<InputSource> InputSource::Create() {
138   ui::win::CreateATLModuleIfNeeded();
139 
140   base::win::ScopedComPtr<ITfInputProcessorProfileMgr> profile_manager;
141   HRESULT hr = profile_manager.CreateInstance(CLSID_TF_InputProcessorProfiles);
142   if (FAILED(hr)) {
143     LOG(ERROR) << "Failed to instantiate CLSID_TF_InputProcessorProfiles."
144                << " hr = " << hr;
145     return scoped_ptr<InputSource>();
146   }
147   base::win::ScopedComPtr<ITfSource> profiles_source;
148   hr = profiles_source.QueryFrom(profile_manager);
149   if (FAILED(hr)) {
150     LOG(ERROR) << "QueryFrom to ITfSource failed. hr = " << hr;
151     return scoped_ptr<InputSource>();
152   }
153 
154   CComObject<InputSourceMonitor>* monitor = NULL;
155   hr = CComObject<InputSourceMonitor>::CreateInstance(&monitor);
156   if (FAILED(hr)) {
157     LOG(ERROR) << "CComObject<InputSourceMonitor>::CreateInstance failed."
158                << " hr = " << hr;
159     return scoped_ptr<InputSource>();
160   }
161   if (!monitor->Initialize(profiles_source)) {
162     LOG(ERROR) << "Failed to initialize the monitor.";
163     return scoped_ptr<InputSource>();
164   }
165 
166   // Transfer the ownership.
167   return scoped_ptr<InputSource>(new InputSourceImpl(profile_manager, monitor));
168 }
169 
170 }  // namespace metro_driver
171