Commit abc53e66 authored by nona@chromium.org's avatar nona@chromium.org

Introduce TsfEventRouter.

TsfEventRouter is used for catching tsf related events and forwarding to observers.
This class is not used at this moment. This will be used for implementing Omnibox suggestion on Metro UI.
This patch only affects on Metro UI and never affects on classic UI on Win8 or prior.

BUG=151901, 141820
TEST=Manually done on Win8 classic and metro mode.

Review URL: https://chromiumcodereview.appspot.com/11148012

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@162356 0039d316-1c4b-4281-b951-d872f2087c98
parent f3b93f6c
...@@ -52,6 +52,10 @@ void MockTsfBridge::RemoveFocusedClient(TextInputClient* client) { ...@@ -52,6 +52,10 @@ void MockTsfBridge::RemoveFocusedClient(TextInputClient* client) {
focused_window_ = NULL; focused_window_ = NULL;
} }
base::win::ScopedComPtr<ITfThreadMgr> MockTsfBridge::GetThreadManager() {
return thread_manager_;
}
void MockTsfBridge::Reset() { void MockTsfBridge::Reset() {
shutdown_call_count_ = 0; shutdown_call_count_ = 0;
enable_ime_call_count_ = 0; enable_ime_call_count_ = 0;
......
...@@ -5,7 +5,10 @@ ...@@ -5,7 +5,10 @@
#ifndef UI_BASE_WIN_MOCK_TSF_BRIDGE_H_ #ifndef UI_BASE_WIN_MOCK_TSF_BRIDGE_H_
#define UI_BASE_WIN_MOCK_TSF_BRIDGE_H_ #define UI_BASE_WIN_MOCK_TSF_BRIDGE_H_
#include <msctf.h>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/win/scoped_comptr.h"
#include "ui/base/ime/text_input_type.h" #include "ui/base/ime/text_input_type.h"
#include "ui/base/win/tsf_bridge.h" #include "ui/base/win/tsf_bridge.h"
...@@ -32,6 +35,9 @@ class MockTsfBridge : public TsfBridge { ...@@ -32,6 +35,9 @@ class MockTsfBridge : public TsfBridge {
// TsfBridge override. // TsfBridge override.
virtual void RemoveFocusedClient(TextInputClient* client) OVERRIDE; virtual void RemoveFocusedClient(TextInputClient* client) OVERRIDE;
// TsfBridge override.
virtual base::win::ScopedComPtr<ITfThreadMgr> GetThreadManager() OVERRIDE;
// Resets MockTsfBridge state including function call counter. // Resets MockTsfBridge state including function call counter.
void Reset(); void Reset();
...@@ -84,6 +90,7 @@ class MockTsfBridge : public TsfBridge { ...@@ -84,6 +90,7 @@ class MockTsfBridge : public TsfBridge {
TextInputClient* text_input_client_; TextInputClient* text_input_client_;
HWND focused_window_; HWND focused_window_;
TextInputType latest_text_input_type_; TextInputType latest_text_input_type_;
base::win::ScopedComPtr<ITfThreadMgr> thread_manager_;
DISALLOW_COPY_AND_ASSIGN(MockTsfBridge); DISALLOW_COPY_AND_ASSIGN(MockTsfBridge);
}; };
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
#include <msctf.h> #include <msctf.h>
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
...@@ -183,6 +184,12 @@ class TsfBridgeDelegate : public TsfBridge { ...@@ -183,6 +184,12 @@ class TsfBridgeDelegate : public TsfBridge {
} }
} }
virtual base::win::ScopedComPtr<ITfThreadMgr> GetThreadManager() OVERRIDE {
DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type());
DCHECK(IsInitialized());
return thread_manager_;
}
private: private:
friend struct DefaultSingletonTraits<TsfBridgeDelegate>; friend struct DefaultSingletonTraits<TsfBridgeDelegate>;
......
...@@ -6,7 +6,10 @@ ...@@ -6,7 +6,10 @@
#define UI_BASE_WIN_TSF_BRIDGE_H_ #define UI_BASE_WIN_TSF_BRIDGE_H_
#include <Windows.h> #include <Windows.h>
#include <msctf.h>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/win/scoped_comptr.h"
#include "ui/base/ui_export.h" #include "ui/base/ui_export.h"
namespace ui { namespace ui {
...@@ -61,6 +64,9 @@ class UI_EXPORT TsfBridge { ...@@ -61,6 +64,9 @@ class UI_EXPORT TsfBridge {
// Caller must free |client|. // Caller must free |client|.
virtual void RemoveFocusedClient(TextInputClient* client) = 0; virtual void RemoveFocusedClient(TextInputClient* client) = 0;
// Obtains current thread manager.
virtual base::win::ScopedComPtr<ITfThreadMgr> GetThreadManager() = 0;
protected: protected:
// Uses GetInstance() instead. // Uses GetInstance() instead.
TsfBridge(); TsfBridge();
......
// Copyright (c) 2012 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 "ui/base/win/tsf_event_router.h"
#include <msctf.h>
#include <set>
#include <utility>
#include "base/bind.h"
#include "base/win/scoped_comptr.h"
#include "base/win/metro.h"
namespace ui {
class TsfEventRouterImpl : public TsfEventRouter,
public ITfUIElementSink,
public ITfTextEditSink {
public:
TsfEventRouterImpl()
: context_source_cookie_(TF_INVALID_COOKIE),
ui_source_cookie_(TF_INVALID_COOKIE),
ref_count_(0) {}
virtual ~TsfEventRouterImpl() {}
// ITfTextEditSink override.
virtual ULONG STDMETHODCALLTYPE AddRef() OVERRIDE {
return InterlockedIncrement(&ref_count_);
}
// ITfTextEditSink override.
virtual ULONG STDMETHODCALLTYPE Release() OVERRIDE {
const LONG count = InterlockedDecrement(&ref_count_);
if (!count) {
delete this;
return 0;
}
return static_cast<ULONG>(count);
}
// ITfTextEditSink override.
virtual STDMETHODIMP QueryInterface(REFIID iid, void** result) OVERRIDE {
if (!result)
return E_INVALIDARG;
if (iid == IID_IUnknown || iid == IID_ITfTextEditSink) {
*result = static_cast<ITfTextEditSink*>(this);
} else if (iid == IID_ITfUIElementSink) {
*result = static_cast<ITfUIElementSink*>(this);
} else {
*result = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
// ITfTextEditSink override.
virtual STDMETHODIMP OnEndEdit(ITfContext* context,
TfEditCookie read_only_cookie,
ITfEditRecord* edit_record) OVERRIDE {
if (!edit_record || !context)
return E_INVALIDARG;
if (text_updated_callback_.is_null())
return S_OK;
// |edit_record| can be used to obtain updated ranges in terms of text
// contents and/or text attributes. Here we are interested only in text
// update so we use TF_GTP_INCL_TEXT and check if there is any range which
// contains updated text.
base::win::ScopedComPtr<IEnumTfRanges> ranges;
if (FAILED(edit_record->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT,
NULL,
0,
ranges.Receive()))) {
return S_OK; // don't care about failures.
}
ULONG fetched_count = 0;
base::win::ScopedComPtr<ITfRange> range;
if (FAILED(ranges->Next(1, range.Receive(), &fetched_count)))
return S_OK; // don't care about failures.
// |fetched_count| != 0 means there is at least one range that contains
// updated texts.
if (fetched_count != 0)
text_updated_callback_.Run();
return S_OK;
}
// ITfUiElementSink override.
virtual STDMETHODIMP BeginUIElement(DWORD element_id, BOOL* is_show) {
if (is_show)
*is_show = TRUE; // without this the UI element will not be shown.
if (!IsCandidateWindowInternal(element_id))
return S_OK;
std::pair<std::set<DWORD>::iterator, bool> insert_result =
open_candidate_window_ids_.insert(element_id);
if (candidat_window_count_changed_callback_.is_null())
return S_OK;
// Don't call if |element_id| is already handled.
if (!insert_result.second)
return S_OK;
candidat_window_count_changed_callback_.Run(
open_candidate_window_ids_.size());
return S_OK;
}
// ITfUiElementSink override.
virtual STDMETHODIMP UpdateUIElement(DWORD element_id) {
return S_OK;
}
// ITfUiElementSink override.
virtual STDMETHODIMP EndUIElement(DWORD element_id) {
if (open_candidate_window_ids_.erase(element_id) == 0)
return S_OK;
if (candidat_window_count_changed_callback_.is_null())
return S_OK;
candidat_window_count_changed_callback_.Run(
open_candidate_window_ids_.size());
return S_OK;
}
// TsfEventRouter override.
virtual void SetManager(ITfThreadMgr* manager) OVERRIDE {
EnsureDeassociated();
if (manager)
Associate(manager);
}
// TsfEventRouter override.
virtual bool IsImeComposing() OVERRIDE {
DCHECK(base::win::IsTsfAwareRequired())
<< "Do not call without TSF environment.";
if (!context_)
return false;
return IsImeComposingInternal(context_);
}
// TsfEventRouter override.
virtual void SetTextUpdatedCallback(
const TextUpdatedCallback& callback) OVERRIDE {
text_updated_callback_ = callback;
}
// TsfEventRouter override.
virtual void SetCandidateWindowStatusChangedCallback(
const CandidateWindowCountChangedCallback& callback) OVERRIDE {
candidat_window_count_changed_callback_ = callback;
}
private:
// Returns true if the given |context| is in composing.
static bool IsImeComposingInternal(ITfContext* context) {
DCHECK(base::win::IsTsfAwareRequired())
<< "Do not call without TSF environment.";
DCHECK(context);
base::win::ScopedComPtr<ITfContextComposition> context_composition;
if (FAILED(context_composition.QueryFrom(context)))
return false;
base::win::ScopedComPtr<IEnumITfCompositionView> enum_composition_view;
if (FAILED(context_composition->EnumCompositions(
enum_composition_view.Receive())))
return false;
base::win::ScopedComPtr<ITfCompositionView> composition_view;
return enum_composition_view->Next(1, composition_view.Receive(),
NULL) == S_OK;
}
// Returns true if the given |element_id| represents candidate window.
bool IsCandidateWindowInternal(DWORD element_id) {
DCHECK(ui_element_manager_.get());
base::win::ScopedComPtr<ITfUIElement> ui_element;
if (FAILED(ui_element_manager_->GetUIElement(element_id,
ui_element.Receive()))) {
return false;
}
base::win::ScopedComPtr<ITfCandidateListUIElement>
candidate_list_ui_element;
return SUCCEEDED(candidate_list_ui_element.QueryFrom(ui_element));
}
// Associates this class with specified |manager|.
void Associate(ITfThreadMgr* thread_manager) {
DCHECK(base::win::IsTsfAwareRequired())
<< "Do not call without TSF environment.";
DCHECK(thread_manager);
base::win::ScopedComPtr<ITfDocumentMgr> document_manager;
if (FAILED(thread_manager->GetFocus(document_manager.Receive())))
return;
if (FAILED(document_manager->GetBase(context_.Receive())))
return;
if (FAILED(context_source_.QueryFrom(context_)))
return;
context_source_->AdviseSink(IID_ITfTextEditSink,
static_cast<ITfTextEditSink*>(this),
&context_source_cookie_);
if (FAILED(ui_element_manager_.QueryFrom(thread_manager)))
return;
if (FAILED(ui_source_.QueryFrom(ui_element_manager_)))
return;
ui_source_->AdviseSink(IID_ITfUIElementSink,
static_cast<ITfUIElementSink*>(this),
&ui_source_cookie_);
}
// Resets the association, this function is safe to call if there is no
// associated thread manager.
void EnsureDeassociated() {
DCHECK(base::win::IsTsfAwareRequired())
<< "Do not call without TSF environment.";
context_.Release();
if (context_source_) {
context_source_->UnadviseSink(context_source_cookie_);
context_source_.Release();
}
context_source_cookie_ = TF_INVALID_COOKIE;
ui_element_manager_.Release();
if (ui_source_) {
ui_source_->UnadviseSink(ui_source_cookie_);
ui_source_.Release();
}
ui_source_cookie_ = TF_INVALID_COOKIE;
}
// Callback function fired when the text contents are updated.
TextUpdatedCallback text_updated_callback_;
CandidateWindowCountChangedCallback candidat_window_count_changed_callback_;
// A context associated with this class.
base::win::ScopedComPtr<ITfContext> context_;
// The ITfSource associated with |context_|.
base::win::ScopedComPtr<ITfSource> context_source_;
// The cookie for |context_source_|.
DWORD context_source_cookie_;
// A UiElementMgr associated with this class.
base::win::ScopedComPtr<ITfUIElementMgr> ui_element_manager_;
// The ITfSouce associated with |ui_element_manager_|.
base::win::ScopedComPtr<ITfSource> ui_source_;
// The cookie for |ui_source_|.
DWORD ui_source_cookie_;
// The set of currently opend candidate window ids.
std::set<DWORD> open_candidate_window_ids_;
// The reference count of this instance.
volatile LONG ref_count_;
DISALLOW_COPY_AND_ASSIGN(TsfEventRouterImpl);
};
///////////////////////////////////////////////////////////////////////////////
// TsfEventRouter
TsfEventRouter::TsfEventRouter() {
}
TsfEventRouter::~TsfEventRouter() {
}
TsfEventRouter* TsfEventRouter::Create() {
return new TsfEventRouterImpl();
}
} // namespace ui
// Copyright (c) 2012 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.
#ifndef UI_BASE_WIN_TSF_EVENT_ROUTER_H_
#define UI_BASE_WIN_TSF_EVENT_ROUTER_H_
#include <msctf.h>
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "ui/base/ime/text_input_type.h"
#include "ui/base/ui_export.h"
struct ITfDocumentMgr;
namespace ui {
// This is an abstract interface that monitors events associated with TSF and
// forwards them to the observer. In order to manage the life cycle of this
// object by scoped_refptr and the implementation class of this interface is COM
// class anyway, this interface is derived from IUnknown.
class TsfEventRouter : public IUnknown {
public:
typedef base::Callback<void ()> TextUpdatedCallback;
typedef base::Callback<void (size_t window_count)>
CandidateWindowCountChangedCallback;
virtual ~TsfEventRouter();
// Sets |manager| to be monitored. |manager| can be NULL.
virtual void SetManager(ITfThreadMgr* manager) = 0;
// Returns true if the IME is composing texts.
virtual bool IsImeComposing() = 0;
// Sets the callback function which is invoked when the text contents is
// updated.
virtual void SetTextUpdatedCallback(
const TextUpdatedCallback& callback) = 0;
// Sets the callback function which is invoked when the number of currently
// candidate window opened is changed.
virtual void SetCandidateWindowStatusChangedCallback(
const CandidateWindowCountChangedCallback& callback) = 0;
// Factory function, creates a new instance and retunrns ownership.
static UI_EXPORT TsfEventRouter* Create();
protected:
// Create should be used instead.
TsfEventRouter();
DISALLOW_COPY_AND_ASSIGN(TsfEventRouter);
};
} // namespace ui
#endif // UI_BASE_WIN_TSF_EVENT_ROUTER_H_
...@@ -333,6 +333,8 @@ ...@@ -333,6 +333,8 @@
'base/win/singleton_hwnd.h', 'base/win/singleton_hwnd.h',
'base/win/tsf_bridge.cc', 'base/win/tsf_bridge.cc',
'base/win/tsf_bridge.h', 'base/win/tsf_bridge.h',
'base/win/tsf_event_router.cc',
'base/win/tsf_event_router.h',
'base/win/tsf_input_scope.cc', 'base/win/tsf_input_scope.cc',
'base/win/tsf_input_scope.h', 'base/win/tsf_input_scope.h',
'base/win/tsf_text_store.cc', 'base/win/tsf_text_store.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment