Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 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 "ui/base/ime/remote_input_method_win.h" | |
| 6 | |
| 7 #include "base/strings/utf_string_conversions.h" | |
| 8 #include "ui/base/ime/input_method.h" | |
| 9 #include "ui/base/ime/input_method_delegate.h" | |
| 10 #include "ui/base/ime/remote_input_method_delegate_win.h" | |
| 11 #include "ui/base/ime/text_input_client.h" | |
| 12 #include "ui/base/ime/win/tsf_input_scope.h" | |
| 13 #include "ui/events/event.h" | |
| 14 #include "ui/events/event_utils.h" | |
| 15 #include "ui/gfx/rect.h" | |
| 16 | |
| 17 namespace ui { | |
| 18 namespace { | |
| 19 | |
| 20 const LANGID kFallbackLangID = | |
| 21 MAKELANGID(LANG_NEUTRAL, SUBLANG_UI_CUSTOM_DEFAULT); | |
| 22 | |
| 23 // 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
| |
| 24 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.
| |
| 25 public: | |
| 26 static void RegisterInstance(InputMethod* public_interface, | |
| 27 RemoteInputMethodPrivateWin* private_interface) { | |
| 28 CHECK(public_interface_ == NULL) | |
| 29 << "Currently only one instance is supported at the same time"; | |
| 30 CHECK(private_interface_ == NULL) | |
| 31 << "Currently only one instance is supported at the same time"; | |
| 32 public_interface_ = public_interface; | |
| 33 private_interface_ = private_interface; | |
| 34 } | |
| 35 | |
| 36 static void UnregisterInstance(InputMethod* public_interface) { | |
| 37 RemoteInputMethodPrivateWin* private_interface = | |
| 38 GetPrivate(public_interface); | |
| 39 if (public_interface_ == public_interface && | |
| 40 private_interface_ == private_interface) { | |
| 41 public_interface_ = NULL; | |
| 42 private_interface_ = NULL; | |
| 43 } | |
| 44 } | |
| 45 | |
| 46 static RemoteInputMethodPrivateWin* GetPrivate( | |
| 47 InputMethod* public_interface) { | |
| 48 if (public_interface_ != public_interface) | |
| 49 return NULL; | |
| 50 return private_interface_; | |
| 51 } | |
| 52 | |
| 53 private: | |
| 54 static InputMethod* public_interface_; | |
| 55 static RemoteInputMethodPrivateWin* private_interface_; | |
| 56 | |
| 57 DISALLOW_IMPLICIT_CONSTRUCTORS(InstanceMapper); | |
| 58 }; | |
| 59 InputMethod* InstanceMapper::public_interface_ = NULL; | |
| 60 RemoteInputMethodPrivateWin* InstanceMapper::private_interface_ = NULL; | |
| 61 | |
| 62 std::string GetLocaleString(LCID Locale_id, LCTYPE locale_type) { | |
| 63 wchar_t buffer[16] = {}; | |
| 64 | |
| 65 //|chars_written| includes NUL terminator. | |
| 66 int chars_written = | |
| 67 GetLocaleInfo(Locale_id, locale_type, buffer, arraysize(buffer)); | |
| 68 if (chars_written <= 1 || arraysize(buffer) < chars_written) | |
| 69 return std::string(); | |
| 70 std::string result; | |
| 71 WideToUTF8(buffer, chars_written - 1, &result); | |
| 72 return result; | |
| 73 } | |
| 74 | |
| 75 std::vector<int32> GetInputScopesAsInt(TextInputType text_input_type, | |
| 76 TextInputMode text_input_mode) { | |
| 77 std::vector<int32> result; | |
| 78 // An empty vector represents |text_input_type| is TEXT_INPUT_TYPE_NONE. | |
| 79 if (text_input_type == TEXT_INPUT_TYPE_NONE) | |
| 80 return result; | |
| 81 | |
| 82 const std::vector<InputScope>& input_scopes = | |
| 83 tsf_inputscope::GetInputScopes(text_input_type, text_input_mode); | |
| 84 result.reserve(input_scopes.size()); | |
| 85 for (size_t i = 0; i < input_scopes.size(); ++i) | |
| 86 result.push_back(static_cast<int32>(input_scopes[i])); | |
| 87 return result; | |
| 88 } | |
| 89 | |
| 90 std::vector<gfx::Rect> GetCompositionCharacterBounds( | |
| 91 const TextInputClient* client) { | |
| 92 if (!client) | |
| 93 return std::vector<gfx::Rect>(); | |
| 94 | |
| 95 if (!client->HasCompositionText()) { | |
| 96 std::vector<gfx::Rect> caret; | |
| 97 caret.push_back(client->GetCaretBounds()); | |
| 98 return caret; | |
| 99 } | |
| 100 | |
| 101 std::vector<gfx::Rect> bounds; | |
| 102 for (uint32 i = 0;; ++i) { | |
| 103 gfx::Rect rect; | |
| 104 if (!client->GetCompositionCharacterBounds(i, &rect)) | |
| 105 break; | |
| 106 bounds.push_back(rect); | |
| 107 } | |
| 108 return bounds; | |
| 109 } | |
| 110 | |
| 111 class RemoteInputMethodWin : public InputMethod, | |
| 112 public RemoteInputMethodPrivateWin { | |
| 113 public: | |
| 114 explicit RemoteInputMethodWin(internal::InputMethodDelegate* delegate) | |
| 115 : delegate_(delegate), | |
| 116 remote_delegate_(NULL), | |
| 117 text_input_client_(NULL), | |
| 118 is_candidate_popup_open_(false), | |
| 119 is_ime_(false), | |
| 120 langid_(kFallbackLangID) { | |
| 121 InstanceMapper::RegisterInstance(this, this); | |
| 122 } | |
|
Seigo Nonaka
2013/11/18 20:00:27
plz add one line break between functions.
yukawa
2013/11/19 07:17:51
Done.
| |
| 123 virtual ~RemoteInputMethodWin() { | |
| 124 InstanceMapper::UnregisterInstance(this); | |
| 125 } | |
| 126 | |
| 127 private: | |
| 128 // Overridden from InputMethod: | |
| 129 virtual void SetDelegate(internal::InputMethodDelegate* delegate) OVERRIDE { | |
| 130 delegate_ = delegate; | |
| 131 } | |
| 132 virtual void Init(bool focused) OVERRIDE {} | |
| 133 virtual void OnFocus() OVERRIDE {} | |
| 134 virtual void OnBlur() OVERRIDE {} | |
| 135 virtual bool OnUntranslatedIMEMessage(const base::NativeEvent& event, | |
| 136 NativeEventResult* result) OVERRIDE { | |
| 137 return false; | |
| 138 } | |
| 139 virtual void SetFocusedTextInputClient(TextInputClient* client) OVERRIDE { | |
| 140 | |
| 141 std::vector<int32> prev_input_scopes; | |
| 142 std::swap(input_scopes_, prev_input_scopes); | |
| 143 std::vector<gfx::Rect> prev_bounds; | |
| 144 std::swap(composition_character_bounds_, prev_bounds); | |
| 145 if (client) { | |
| 146 input_scopes_ = GetInputScopesAsInt(client->GetTextInputType(), | |
| 147 client->GetTextInputMode()); | |
| 148 composition_character_bounds_ = GetCompositionCharacterBounds(client); | |
| 149 } | |
| 150 | |
| 151 text_input_client_ = client; | |
| 152 | |
| 153 if (!remote_delegate_ || (prev_input_scopes == input_scopes_ && | |
| 154 prev_bounds == composition_character_bounds_)) | |
| 155 return; | |
| 156 remote_delegate_->OnTextInputClientUpdated( | |
| 157 input_scopes_, composition_character_bounds_); | |
| 158 } | |
| 159 virtual void DetachTextInputClient(TextInputClient* client) OVERRIDE { | |
| 160 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.
| |
| 161 if (!input_scopes_.empty() || !composition_character_bounds_.empty()) { | |
| 162 input_scopes_.empty(); | |
| 163 composition_character_bounds_.empty(); | |
| 164 if (remote_delegate_) { | |
| 165 remote_delegate_->OnTextInputClientUpdated( | |
| 166 input_scopes_, composition_character_bounds_); | |
| 167 } | |
| 168 } | |
| 169 text_input_client_ = NULL; | |
| 170 } | |
| 171 } | |
| 172 virtual TextInputClient* GetTextInputClient() const OVERRIDE { | |
| 173 return text_input_client_; | |
| 174 } | |
| 175 virtual bool DispatchKeyEvent(const ui::KeyEvent& event) OVERRIDE { | |
| 176 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
| |
| 177 return false; | |
| 178 | |
| 179 if (event.is_char() && text_input_client_) { | |
| 180 GetTextInputClient()->InsertChar(event.key_code(), | |
| 181 ui::GetModifiersFromKeyState()); | |
| 182 return true; | |
| 183 } | |
| 184 if (!delegate_) | |
| 185 return false; | |
| 186 return delegate_->DispatchFabricatedKeyEventPostIME(event.type(), | |
| 187 event.key_code(), | |
| 188 event.flags()); | |
| 189 } | |
| 190 virtual void OnTextInputTypeChanged(const TextInputClient* client) OVERRIDE { | |
| 191 if (!text_input_client_ || text_input_client_ != client) | |
| 192 return; | |
| 193 std::vector<int32> prev_input_scopes; | |
| 194 std::swap(input_scopes_, prev_input_scopes); | |
| 195 input_scopes_ = GetInputScopesAsInt(client->GetTextInputType(), | |
| 196 client->GetTextInputMode()); | |
| 197 if (input_scopes_ != prev_input_scopes && remote_delegate_) { | |
| 198 remote_delegate_->OnTextInputClientUpdated( | |
| 199 input_scopes_, composition_character_bounds_); | |
| 200 } | |
| 201 } | |
| 202 virtual void OnCaretBoundsChanged(const TextInputClient* client) OVERRIDE { | |
| 203 if (!text_input_client_ || text_input_client_ != client) | |
| 204 return; | |
| 205 std::vector<gfx::Rect> prev_rects; | |
| 206 std::swap(composition_character_bounds_, prev_rects); | |
| 207 composition_character_bounds_ = GetCompositionCharacterBounds(client); | |
| 208 if (composition_character_bounds_ != prev_rects && remote_delegate_) { | |
| 209 remote_delegate_->OnTextInputClientUpdated( | |
| 210 input_scopes_, composition_character_bounds_); | |
| 211 } | |
| 212 } | |
| 213 virtual void CancelComposition(const TextInputClient* client) OVERRIDE { | |
| 214 if (CanSendRemoteNotification(client)) | |
| 215 remote_delegate_->CancelComposition(); | |
| 216 } | |
| 217 virtual void OnInputLocaleChanged() OVERRIDE { | |
| 218 // not supported. | |
| 219 } | |
| 220 virtual std::string GetInputLocale() OVERRIDE { | |
| 221 const LCID locale_id = MAKELCID(langid_, SORT_DEFAULT); | |
| 222 std::string language = | |
| 223 GetLocaleString(locale_id, LOCALE_SISO639LANGNAME); | |
| 224 if (SUBLANGID(langid_) == SUBLANG_NEUTRAL || language.empty()) | |
| 225 return language; | |
| 226 const std::string& region = | |
| 227 GetLocaleString(locale_id, LOCALE_SISO3166CTRYNAME); | |
| 228 if (region.empty()) | |
| 229 return language; | |
| 230 return language.append(1, '-').append(region); | |
| 231 } | |
| 232 virtual base::i18n::TextDirection GetInputTextDirection() OVERRIDE { | |
| 233 switch (PRIMARYLANGID(langid_)) { | |
| 234 case LANG_ARABIC: | |
| 235 case LANG_HEBREW: | |
| 236 case LANG_PERSIAN: | |
| 237 case LANG_SYRIAC: | |
| 238 case LANG_UIGHUR: | |
| 239 case LANG_URDU: | |
| 240 return base::i18n::RIGHT_TO_LEFT; | |
| 241 default: | |
| 242 return base::i18n::LEFT_TO_RIGHT; | |
| 243 } | |
| 244 } | |
| 245 virtual bool IsActive() OVERRIDE { | |
| 246 return true; // always turned on | |
| 247 } | |
| 248 virtual TextInputType GetTextInputType() const OVERRIDE { | |
| 249 return text_input_client_ ? text_input_client_->GetTextInputType() | |
| 250 : TEXT_INPUT_TYPE_NONE; | |
| 251 } | |
| 252 virtual TextInputMode GetTextInputMode() const OVERRIDE { | |
| 253 return text_input_client_ ? text_input_client_->GetTextInputMode() | |
| 254 : TEXT_INPUT_MODE_DEFAULT; | |
| 255 } | |
| 256 virtual bool CanComposeInline() const OVERRIDE { | |
| 257 return text_input_client_ ? text_input_client_->CanComposeInline() : true; | |
| 258 } | |
| 259 virtual bool IsCandidatePopupOpen() const OVERRIDE { | |
| 260 return is_candidate_popup_open_; | |
| 261 } | |
| 262 virtual void AddObserver(InputMethodObserver* observer) OVERRIDE { | |
| 263 // 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
| |
| 264 } | |
| 265 virtual void RemoveObserver(InputMethodObserver* observer) OVERRIDE { | |
| 266 // not supported. | |
| 267 } | |
| 268 | |
| 269 // Overridden from RemoteInputMethodPrivateWin: | |
| 270 virtual void SetRemoteDelegate( | |
| 271 internal::RemoteInputMethodDelegateWin* delegate) OVERRIDE{ | |
| 272 remote_delegate_ = delegate; | |
| 273 | |
| 274 // Sync initial state. | |
| 275 if (remote_delegate_) { | |
| 276 remote_delegate_->OnTextInputClientUpdated( | |
| 277 input_scopes_, composition_character_bounds_); | |
| 278 } | |
| 279 } | |
| 280 virtual void OnCandidatePopupChanged(bool visible) OVERRIDE { | |
| 281 is_candidate_popup_open_ = visible; | |
| 282 } | |
| 283 virtual void OnLanguageChanged(LANGID langid, bool /*is_ime*/) OVERRIDE { | |
| 284 // Note: Currently |is_ime| is not utilized yet. | |
| 285 const bool changed = (langid_ != langid); | |
| 286 langid_ = langid; | |
| 287 if (changed && GetTextInputClient()) | |
| 288 GetTextInputClient()->OnInputMethodChanged(); | |
| 289 } | |
| 290 | |
| 291 bool CanSendRemoteNotification( | |
| 292 const TextInputClient* text_input_client) const { | |
| 293 return text_input_client_ && | |
| 294 text_input_client_ == text_input_client && | |
| 295 remote_delegate_; | |
| 296 } | |
| 297 | |
| 298 internal::InputMethodDelegate* delegate_; | |
| 299 internal::RemoteInputMethodDelegateWin* remote_delegate_; | |
| 300 | |
| 301 TextInputClient* text_input_client_; | |
| 302 std::vector<int32> input_scopes_; | |
| 303 std::vector<gfx::Rect> composition_character_bounds_; | |
| 304 bool is_candidate_popup_open_; | |
| 305 bool is_ime_; | |
| 306 LANGID langid_; | |
| 307 | |
| 308 DISALLOW_COPY_AND_ASSIGN(RemoteInputMethodWin); | |
| 309 }; | |
| 310 | |
| 311 } // namespace | |
| 312 | |
| 313 RemoteInputMethodPrivateWin::RemoteInputMethodPrivateWin() {} | |
| 314 | |
| 315 // static | |
| 316 scoped_ptr<InputMethod> CreateRemoteInputMethodWin( | |
| 317 internal::InputMethodDelegate* delegate) { | |
| 318 return scoped_ptr<InputMethod>(new RemoteInputMethodWin(delegate)); | |
| 319 } | |
| 320 | |
| 321 // static | |
| 322 RemoteInputMethodPrivateWin* RemoteInputMethodPrivateWin::Get( | |
| 323 InputMethod* input_method) { | |
| 324 return InstanceMapper::GetPrivate(input_method); | |
| 325 } | |
| 326 | |
| 327 } // namespace ui | |
| OLD | NEW |