Chromium Code Reviews| Index: ui/base/ime/remote_input_method_win.cc |
| diff --git a/ui/base/ime/remote_input_method_win.cc b/ui/base/ime/remote_input_method_win.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..456e589f575c5c9a8d921d9ff78fa91e62ed59d7 |
| --- /dev/null |
| +++ b/ui/base/ime/remote_input_method_win.cc |
| @@ -0,0 +1,327 @@ |
| +// 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 "ui/base/ime/remote_input_method_win.h" |
| + |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "ui/base/ime/input_method.h" |
| +#include "ui/base/ime/input_method_delegate.h" |
| +#include "ui/base/ime/remote_input_method_delegate_win.h" |
| +#include "ui/base/ime/text_input_client.h" |
| +#include "ui/base/ime/win/tsf_input_scope.h" |
| +#include "ui/events/event.h" |
| +#include "ui/events/event_utils.h" |
| +#include "ui/gfx/rect.h" |
| + |
| +namespace ui { |
| +namespace { |
| + |
| +const LANGID kFallbackLangID = |
| + MAKELANGID(LANG_NEUTRAL, SUBLANG_UI_CUSTOM_DEFAULT); |
| + |
| +// Caveats: Currently we support only one instance at the same time. |
|
Seigo Nonaka
2013/11/18 20:00:27
Could you describe why should we support multiple
yukawa
2013/11/19 07:17:51
I don't think we need to support multiple instance
|
| +class InstanceMapper { |
|
Seigo Nonaka
2013/11/18 20:00:27
Does this class is necessary?
Seems there is only
yukawa
2013/11/19 07:17:51
Done.
|
| + public: |
| + static void RegisterInstance(InputMethod* public_interface, |
| + RemoteInputMethodPrivateWin* private_interface) { |
| + CHECK(public_interface_ == NULL) |
| + << "Currently only one instance is supported at the same time"; |
| + CHECK(private_interface_ == NULL) |
| + << "Currently only one instance is supported at the same time"; |
| + public_interface_ = public_interface; |
| + private_interface_ = private_interface; |
| + } |
| + |
| + static void UnregisterInstance(InputMethod* public_interface) { |
| + RemoteInputMethodPrivateWin* private_interface = |
| + GetPrivate(public_interface); |
| + if (public_interface_ == public_interface && |
| + private_interface_ == private_interface) { |
| + public_interface_ = NULL; |
| + private_interface_ = NULL; |
| + } |
| + } |
| + |
| + static RemoteInputMethodPrivateWin* GetPrivate( |
| + InputMethod* public_interface) { |
| + if (public_interface_ != public_interface) |
| + return NULL; |
| + return private_interface_; |
| + } |
| + |
| + private: |
| + static InputMethod* public_interface_; |
| + static RemoteInputMethodPrivateWin* private_interface_; |
| + |
| + DISALLOW_IMPLICIT_CONSTRUCTORS(InstanceMapper); |
| +}; |
| +InputMethod* InstanceMapper::public_interface_ = NULL; |
| +RemoteInputMethodPrivateWin* InstanceMapper::private_interface_ = NULL; |
| + |
| +std::string GetLocaleString(LCID Locale_id, LCTYPE locale_type) { |
| + wchar_t buffer[16] = {}; |
| + |
| + //|chars_written| includes NUL terminator. |
| + int chars_written = |
| + GetLocaleInfo(Locale_id, locale_type, buffer, arraysize(buffer)); |
| + if (chars_written <= 1 || arraysize(buffer) < chars_written) |
| + return std::string(); |
| + std::string result; |
| + WideToUTF8(buffer, chars_written - 1, &result); |
| + return result; |
| +} |
| + |
| +std::vector<int32> GetInputScopesAsInt(TextInputType text_input_type, |
| + TextInputMode text_input_mode) { |
| + std::vector<int32> result; |
| + // An empty vector represents |text_input_type| is TEXT_INPUT_TYPE_NONE. |
| + if (text_input_type == TEXT_INPUT_TYPE_NONE) |
| + return result; |
| + |
| + const std::vector<InputScope>& input_scopes = |
| + tsf_inputscope::GetInputScopes(text_input_type, text_input_mode); |
| + result.reserve(input_scopes.size()); |
| + for (size_t i = 0; i < input_scopes.size(); ++i) |
| + result.push_back(static_cast<int32>(input_scopes[i])); |
| + return result; |
| +} |
| + |
| +std::vector<gfx::Rect> GetCompositionCharacterBounds( |
| + const TextInputClient* client) { |
| + if (!client) |
| + return std::vector<gfx::Rect>(); |
| + |
| + if (!client->HasCompositionText()) { |
| + std::vector<gfx::Rect> caret; |
| + caret.push_back(client->GetCaretBounds()); |
| + return caret; |
| + } |
| + |
| + std::vector<gfx::Rect> bounds; |
| + for (uint32 i = 0;; ++i) { |
| + gfx::Rect rect; |
| + if (!client->GetCompositionCharacterBounds(i, &rect)) |
| + break; |
| + bounds.push_back(rect); |
| + } |
| + return bounds; |
| +} |
| + |
| +class RemoteInputMethodWin : public InputMethod, |
| + public RemoteInputMethodPrivateWin { |
| + public: |
| + explicit RemoteInputMethodWin(internal::InputMethodDelegate* delegate) |
| + : delegate_(delegate), |
| + remote_delegate_(NULL), |
| + text_input_client_(NULL), |
| + is_candidate_popup_open_(false), |
| + is_ime_(false), |
| + langid_(kFallbackLangID) { |
| + InstanceMapper::RegisterInstance(this, this); |
| + } |
|
Seigo Nonaka
2013/11/18 20:00:27
plz add one line break between functions.
yukawa
2013/11/19 07:17:51
Done.
|
| + virtual ~RemoteInputMethodWin() { |
| + InstanceMapper::UnregisterInstance(this); |
| + } |
| + |
| + private: |
| + // Overridden from InputMethod: |
| + virtual void SetDelegate(internal::InputMethodDelegate* delegate) OVERRIDE { |
| + delegate_ = delegate; |
| + } |
| + virtual void Init(bool focused) OVERRIDE {} |
| + virtual void OnFocus() OVERRIDE {} |
| + virtual void OnBlur() OVERRIDE {} |
| + virtual bool OnUntranslatedIMEMessage(const base::NativeEvent& event, |
| + NativeEventResult* result) OVERRIDE { |
| + return false; |
| + } |
| + virtual void SetFocusedTextInputClient(TextInputClient* client) OVERRIDE { |
| + |
| + std::vector<int32> prev_input_scopes; |
| + std::swap(input_scopes_, prev_input_scopes); |
| + std::vector<gfx::Rect> prev_bounds; |
| + std::swap(composition_character_bounds_, prev_bounds); |
| + if (client) { |
| + input_scopes_ = GetInputScopesAsInt(client->GetTextInputType(), |
| + client->GetTextInputMode()); |
| + composition_character_bounds_ = GetCompositionCharacterBounds(client); |
| + } |
| + |
| + text_input_client_ = client; |
| + |
| + if (!remote_delegate_ || (prev_input_scopes == input_scopes_ && |
| + prev_bounds == composition_character_bounds_)) |
| + return; |
| + remote_delegate_->OnTextInputClientUpdated( |
| + input_scopes_, composition_character_bounds_); |
| + } |
| + virtual void DetachTextInputClient(TextInputClient* client) OVERRIDE { |
| + if (text_input_client_ == client) { |
|
Seigo Nonaka
2013/11/18 20:00:27
nit: I prefer early exit style but up to you.
yukawa
2013/11/19 07:17:51
Done. And simplified.
|
| + if (!input_scopes_.empty() || !composition_character_bounds_.empty()) { |
| + input_scopes_.empty(); |
| + composition_character_bounds_.empty(); |
| + if (remote_delegate_) { |
| + remote_delegate_->OnTextInputClientUpdated( |
| + input_scopes_, composition_character_bounds_); |
| + } |
| + } |
| + text_input_client_ = NULL; |
| + } |
| + } |
| + virtual TextInputClient* GetTextInputClient() const OVERRIDE { |
| + return text_input_client_; |
| + } |
| + virtual bool DispatchKeyEvent(const ui::KeyEvent& event) OVERRIDE { |
| + if (event.HasNativeEvent()) |
|
Seigo Nonaka
2013/11/18 20:00:27
DCHECK?
yukawa
2013/11/19 07:17:51
Um, I changed my mind and added WM_CHAR handler be
Seigo Nonaka
2013/11/20 04:14:29
Let me confirm again, will RemoteInputMethodWin di
yukawa
2013/11/20 05:06:07
Do you mean we should do as follows?
if (event.Ha
Seigo Nonaka
2013/11/20 05:40:41
Okay, as we talked offline, it seems good to imple
|
| + return false; |
| + |
| + if (event.is_char() && text_input_client_) { |
| + GetTextInputClient()->InsertChar(event.key_code(), |
| + ui::GetModifiersFromKeyState()); |
| + return true; |
| + } |
| + if (!delegate_) |
| + return false; |
| + return delegate_->DispatchFabricatedKeyEventPostIME(event.type(), |
| + event.key_code(), |
| + event.flags()); |
| + } |
| + virtual void OnTextInputTypeChanged(const TextInputClient* client) OVERRIDE { |
| + if (!text_input_client_ || text_input_client_ != client) |
| + return; |
| + std::vector<int32> prev_input_scopes; |
| + std::swap(input_scopes_, prev_input_scopes); |
| + input_scopes_ = GetInputScopesAsInt(client->GetTextInputType(), |
| + client->GetTextInputMode()); |
| + if (input_scopes_ != prev_input_scopes && remote_delegate_) { |
| + remote_delegate_->OnTextInputClientUpdated( |
| + input_scopes_, composition_character_bounds_); |
| + } |
| + } |
| + virtual void OnCaretBoundsChanged(const TextInputClient* client) OVERRIDE { |
| + if (!text_input_client_ || text_input_client_ != client) |
| + return; |
| + std::vector<gfx::Rect> prev_rects; |
| + std::swap(composition_character_bounds_, prev_rects); |
| + composition_character_bounds_ = GetCompositionCharacterBounds(client); |
| + if (composition_character_bounds_ != prev_rects && remote_delegate_) { |
| + remote_delegate_->OnTextInputClientUpdated( |
| + input_scopes_, composition_character_bounds_); |
| + } |
| + } |
| + virtual void CancelComposition(const TextInputClient* client) OVERRIDE { |
| + if (CanSendRemoteNotification(client)) |
| + remote_delegate_->CancelComposition(); |
| + } |
| + virtual void OnInputLocaleChanged() OVERRIDE { |
| + // not supported. |
| + } |
| + virtual std::string GetInputLocale() OVERRIDE { |
| + const LCID locale_id = MAKELCID(langid_, SORT_DEFAULT); |
| + std::string language = |
| + GetLocaleString(locale_id, LOCALE_SISO639LANGNAME); |
| + if (SUBLANGID(langid_) == SUBLANG_NEUTRAL || language.empty()) |
| + return language; |
| + const std::string& region = |
| + GetLocaleString(locale_id, LOCALE_SISO3166CTRYNAME); |
| + if (region.empty()) |
| + return language; |
| + return language.append(1, '-').append(region); |
| + } |
| + virtual base::i18n::TextDirection GetInputTextDirection() OVERRIDE { |
| + switch (PRIMARYLANGID(langid_)) { |
| + case LANG_ARABIC: |
| + case LANG_HEBREW: |
| + case LANG_PERSIAN: |
| + case LANG_SYRIAC: |
| + case LANG_UIGHUR: |
| + case LANG_URDU: |
| + return base::i18n::RIGHT_TO_LEFT; |
| + default: |
| + return base::i18n::LEFT_TO_RIGHT; |
| + } |
| + } |
| + virtual bool IsActive() OVERRIDE { |
| + return true; // always turned on |
| + } |
| + virtual TextInputType GetTextInputType() const OVERRIDE { |
| + return text_input_client_ ? text_input_client_->GetTextInputType() |
| + : TEXT_INPUT_TYPE_NONE; |
| + } |
| + virtual TextInputMode GetTextInputMode() const OVERRIDE { |
| + return text_input_client_ ? text_input_client_->GetTextInputMode() |
| + : TEXT_INPUT_MODE_DEFAULT; |
| + } |
| + virtual bool CanComposeInline() const OVERRIDE { |
| + return text_input_client_ ? text_input_client_->CanComposeInline() : true; |
| + } |
| + virtual bool IsCandidatePopupOpen() const OVERRIDE { |
| + return is_candidate_popup_open_; |
| + } |
| + virtual void AddObserver(InputMethodObserver* observer) OVERRIDE { |
| + // not supported. |
|
Seigo Nonaka
2013/11/18 20:00:27
If you will support this, please file a bug and le
yukawa
2013/11/19 07:17:51
I don't want to support this. IMHO, I'd like to d
|
| + } |
| + virtual void RemoveObserver(InputMethodObserver* observer) OVERRIDE { |
| + // not supported. |
| + } |
| + |
| + // Overridden from RemoteInputMethodPrivateWin: |
| + virtual void SetRemoteDelegate( |
| + internal::RemoteInputMethodDelegateWin* delegate) OVERRIDE{ |
| + remote_delegate_ = delegate; |
| + |
| + // Sync initial state. |
| + if (remote_delegate_) { |
| + remote_delegate_->OnTextInputClientUpdated( |
| + input_scopes_, composition_character_bounds_); |
| + } |
| + } |
| + virtual void OnCandidatePopupChanged(bool visible) OVERRIDE { |
| + is_candidate_popup_open_ = visible; |
| + } |
| + virtual void OnLanguageChanged(LANGID langid, bool /*is_ime*/) OVERRIDE { |
| + // Note: Currently |is_ime| is not utilized yet. |
| + const bool changed = (langid_ != langid); |
| + langid_ = langid; |
| + if (changed && GetTextInputClient()) |
| + GetTextInputClient()->OnInputMethodChanged(); |
| + } |
| + |
| + bool CanSendRemoteNotification( |
| + const TextInputClient* text_input_client) const { |
| + return text_input_client_ && |
| + text_input_client_ == text_input_client && |
| + remote_delegate_; |
| + } |
| + |
| + internal::InputMethodDelegate* delegate_; |
| + internal::RemoteInputMethodDelegateWin* remote_delegate_; |
| + |
| + TextInputClient* text_input_client_; |
| + std::vector<int32> input_scopes_; |
| + std::vector<gfx::Rect> composition_character_bounds_; |
| + bool is_candidate_popup_open_; |
| + bool is_ime_; |
| + LANGID langid_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(RemoteInputMethodWin); |
| +}; |
| + |
| +} // namespace |
| + |
| +RemoteInputMethodPrivateWin::RemoteInputMethodPrivateWin() {} |
| + |
| +// static |
| +scoped_ptr<InputMethod> CreateRemoteInputMethodWin( |
| + internal::InputMethodDelegate* delegate) { |
| + return scoped_ptr<InputMethod>(new RemoteInputMethodWin(delegate)); |
| +} |
| + |
| +// static |
| +RemoteInputMethodPrivateWin* RemoteInputMethodPrivateWin::Get( |
| + InputMethod* input_method) { |
| + return InstanceMapper::GetPrivate(input_method); |
| +} |
| + |
| +} // namespace ui |