1// Copyright (c) 2012 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/views/ime/input_method_bridge.h" 6 7#include "ui/base/ime/input_method.h" 8#include "ui/base/ime/input_method_observer.h" 9#include "ui/events/event.h" 10#include "ui/gfx/rect.h" 11#include "ui/views/view.h" 12#include "ui/views/widget/widget.h" 13 14namespace views { 15 16// InputMethodBridge::HostObserver class --------------------------------------- 17 18// An observer class for observing the host input method. When the host input 19// method is destroyed, it will null out the |host_| field on the 20// InputMethodBridge object. 21class InputMethodBridge::HostObserver : public ui::InputMethodObserver { 22 public: 23 explicit HostObserver(InputMethodBridge* bridge); 24 virtual ~HostObserver(); 25 26 virtual void OnTextInputTypeChanged( 27 const ui::TextInputClient* client) OVERRIDE {} 28 virtual void OnFocus() OVERRIDE {} 29 virtual void OnBlur() OVERRIDE {} 30 virtual void OnCaretBoundsChanged( 31 const ui::TextInputClient* client) OVERRIDE {} 32 virtual void OnTextInputStateChanged( 33 const ui::TextInputClient* client) OVERRIDE {} 34 virtual void OnInputMethodDestroyed( 35 const ui::InputMethod* input_method) OVERRIDE; 36 virtual void OnShowImeIfNeeded() OVERRIDE {} 37 38 private: 39 InputMethodBridge* bridge_; 40 41 DISALLOW_COPY_AND_ASSIGN(HostObserver); 42}; 43 44InputMethodBridge::HostObserver::HostObserver(InputMethodBridge* bridge) 45 : bridge_(bridge) { 46 bridge_->host_->AddObserver(this); 47} 48 49InputMethodBridge::HostObserver::~HostObserver() { 50 if (bridge_->host_) 51 bridge_->host_->RemoveObserver(this); 52} 53 54void InputMethodBridge::HostObserver::OnInputMethodDestroyed( 55 const ui::InputMethod* input_method) { 56 DCHECK_EQ(bridge_->host_, input_method); 57 bridge_->host_->RemoveObserver(this); 58 bridge_->host_ = NULL; 59} 60 61// InputMethodBridge class ----------------------------------------------------- 62 63InputMethodBridge::InputMethodBridge(internal::InputMethodDelegate* delegate, 64 ui::InputMethod* host, 65 bool shared_input_method) 66 : host_(host), 67 shared_input_method_(shared_input_method) { 68 DCHECK(host_); 69 SetDelegate(delegate); 70 71 host_observer_.reset(new HostObserver(this)); 72} 73 74InputMethodBridge::~InputMethodBridge() { 75 // By the time we get here it's very likely |widget_|'s NativeWidget has been 76 // destroyed. This means any calls to |widget_| that go to the NativeWidget, 77 // such as IsActive(), will crash. SetFocusedTextInputClient() may callback to 78 // this and go into |widget_|. NULL out |widget_| so we don't attempt to use 79 // it. 80 DetachFromWidget(); 81 82 // Host input method might have been destroyed at this point. 83 if (host_) 84 host_->DetachTextInputClient(this); 85} 86 87void InputMethodBridge::OnFocus() { 88 DCHECK(host_); 89 90 // Direct the shared IME to send TextInputClient messages to |this| object. 91 if (shared_input_method_ || !host_->GetTextInputClient()) 92 host_->SetFocusedTextInputClient(this); 93 94 // TODO(yusukes): We don't need to call OnTextInputTypeChanged() once we move 95 // text input type tracker code to ui::InputMethodBase. 96 if (GetFocusedView()) { 97 OnTextInputTypeChanged(GetFocusedView()); 98 OnCaretBoundsChanged(GetFocusedView()); 99 } 100} 101 102void InputMethodBridge::OnBlur() { 103 DCHECK(host_); 104 105 if (HasCompositionText()) { 106 ConfirmCompositionText(); 107 host_->CancelComposition(this); 108 } 109 110 if (host_->GetTextInputClient() == this) 111 host_->SetFocusedTextInputClient(NULL); 112} 113 114bool InputMethodBridge::OnUntranslatedIMEMessage(const base::NativeEvent& event, 115 NativeEventResult* result) { 116 DCHECK(host_); 117 118 return host_->OnUntranslatedIMEMessage(event, result); 119} 120 121void InputMethodBridge::DispatchKeyEvent(const ui::KeyEvent& key) { 122 DCHECK(key.type() == ui::ET_KEY_PRESSED || key.type() == ui::ET_KEY_RELEASED); 123 124 // We can just dispatch the event here since the |key| is already processed by 125 // the system-wide IME. 126 DispatchKeyEventPostIME(key); 127} 128 129void InputMethodBridge::OnTextInputTypeChanged(View* view) { 130 DCHECK(host_); 131 132 if (IsViewFocused(view)) 133 host_->OnTextInputTypeChanged(this); 134 InputMethodBase::OnTextInputTypeChanged(view); 135} 136 137void InputMethodBridge::OnCaretBoundsChanged(View* view) { 138 DCHECK(host_); 139 140 if (IsViewFocused(view) && !IsTextInputTypeNone()) 141 host_->OnCaretBoundsChanged(this); 142} 143 144void InputMethodBridge::CancelComposition(View* view) { 145 DCHECK(host_); 146 147 if (IsViewFocused(view)) 148 host_->CancelComposition(this); 149} 150 151void InputMethodBridge::OnInputLocaleChanged() { 152 DCHECK(host_); 153 154 host_->OnInputLocaleChanged(); 155} 156 157std::string InputMethodBridge::GetInputLocale() { 158 DCHECK(host_); 159 160 return host_->GetInputLocale(); 161} 162 163bool InputMethodBridge::IsActive() { 164 DCHECK(host_); 165 166 return host_->IsActive(); 167} 168 169bool InputMethodBridge::IsCandidatePopupOpen() const { 170 DCHECK(host_); 171 172 return host_->IsCandidatePopupOpen(); 173} 174 175void InputMethodBridge::ShowImeIfNeeded() { 176 DCHECK(host_); 177 host_->ShowImeIfNeeded(); 178} 179 180// Overridden from TextInputClient. Forward an event from the system-wide IME 181// to the text input |client|, which is e.g. views::Textfield. 182void InputMethodBridge::SetCompositionText( 183 const ui::CompositionText& composition) { 184 TextInputClient* client = GetTextInputClient(); 185 if (client) 186 client->SetCompositionText(composition); 187} 188 189void InputMethodBridge::ConfirmCompositionText() { 190 TextInputClient* client = GetTextInputClient(); 191 if (client) 192 client->ConfirmCompositionText(); 193} 194 195void InputMethodBridge::ClearCompositionText() { 196 TextInputClient* client = GetTextInputClient(); 197 if (client) 198 client->ClearCompositionText(); 199} 200 201void InputMethodBridge::InsertText(const base::string16& text) { 202 TextInputClient* client = GetTextInputClient(); 203 if (client) 204 client->InsertText(text); 205} 206 207void InputMethodBridge::InsertChar(base::char16 ch, int flags) { 208 TextInputClient* client = GetTextInputClient(); 209 if (client) 210 client->InsertChar(ch, flags); 211} 212 213gfx::NativeWindow InputMethodBridge::GetAttachedWindow() const { 214 TextInputClient* client = GetTextInputClient(); 215 return client ? 216 client->GetAttachedWindow() : static_cast<gfx::NativeWindow>(NULL); 217} 218 219ui::TextInputType InputMethodBridge::GetTextInputType() const { 220 TextInputClient* client = GetTextInputClient(); 221 return client ? client->GetTextInputType() : ui::TEXT_INPUT_TYPE_NONE; 222} 223 224ui::TextInputMode InputMethodBridge::GetTextInputMode() const { 225 TextInputClient* client = GetTextInputClient(); 226 return client ? client->GetTextInputMode() : ui::TEXT_INPUT_MODE_DEFAULT; 227} 228 229bool InputMethodBridge::CanComposeInline() const { 230 TextInputClient* client = GetTextInputClient(); 231 return client ? client->CanComposeInline() : true; 232} 233 234gfx::Rect InputMethodBridge::GetCaretBounds() const { 235 TextInputClient* client = GetTextInputClient(); 236 if (!client) 237 return gfx::Rect(); 238 239 return client->GetCaretBounds(); 240} 241 242bool InputMethodBridge::GetCompositionCharacterBounds(uint32 index, 243 gfx::Rect* rect) const { 244 DCHECK(rect); 245 TextInputClient* client = GetTextInputClient(); 246 if (!client) 247 return false; 248 249 return client->GetCompositionCharacterBounds(index, rect); 250} 251 252bool InputMethodBridge::HasCompositionText() const { 253 TextInputClient* client = GetTextInputClient(); 254 return client ? client->HasCompositionText() : false; 255} 256 257bool InputMethodBridge::GetTextRange(gfx::Range* range) const { 258 TextInputClient* client = GetTextInputClient(); 259 return client ? client->GetTextRange(range) : false; 260} 261 262bool InputMethodBridge::GetCompositionTextRange(gfx::Range* range) const { 263 TextInputClient* client = GetTextInputClient(); 264 return client ? client->GetCompositionTextRange(range) : false; 265} 266 267bool InputMethodBridge::GetSelectionRange(gfx::Range* range) const { 268 TextInputClient* client = GetTextInputClient(); 269 return client ? client->GetSelectionRange(range) : false; 270} 271 272bool InputMethodBridge::SetSelectionRange(const gfx::Range& range) { 273 TextInputClient* client = GetTextInputClient(); 274 return client ? client->SetSelectionRange(range) : false; 275} 276 277bool InputMethodBridge::DeleteRange(const gfx::Range& range) { 278 TextInputClient* client = GetTextInputClient(); 279 return client ? client->DeleteRange(range) : false; 280} 281 282bool InputMethodBridge::GetTextFromRange(const gfx::Range& range, 283 base::string16* text) const { 284 TextInputClient* client = GetTextInputClient(); 285 return client ? client->GetTextFromRange(range, text) : false; 286} 287 288void InputMethodBridge::OnInputMethodChanged() { 289 TextInputClient* client = GetTextInputClient(); 290 if (client) 291 client->OnInputMethodChanged(); 292} 293 294bool InputMethodBridge::ChangeTextDirectionAndLayoutAlignment( 295 base::i18n::TextDirection direction) { 296 TextInputClient* client = GetTextInputClient(); 297 return client ? 298 client->ChangeTextDirectionAndLayoutAlignment(direction) : false; 299} 300 301void InputMethodBridge::ExtendSelectionAndDelete(size_t before, size_t after) { 302 TextInputClient* client = GetTextInputClient(); 303 if (client) 304 client->ExtendSelectionAndDelete(before, after); 305} 306 307void InputMethodBridge::EnsureCaretInRect(const gfx::Rect& rect) { 308 TextInputClient* client = GetTextInputClient(); 309 if (client) 310 client->EnsureCaretInRect(rect); 311} 312 313void InputMethodBridge::OnCandidateWindowShown() { 314} 315 316void InputMethodBridge::OnCandidateWindowUpdated() { 317} 318 319void InputMethodBridge::OnCandidateWindowHidden() { 320} 321 322bool InputMethodBridge::IsEditingCommandEnabled(int command_id) { 323 return false; 324} 325 326void InputMethodBridge::ExecuteEditingCommand(int command_id) { 327} 328 329// Overridden from FocusChangeListener. 330void InputMethodBridge::OnWillChangeFocus(View* focused_before, View* focused) { 331 if (HasCompositionText()) { 332 ConfirmCompositionText(); 333 CancelComposition(focused_before); 334 } 335} 336 337void InputMethodBridge::OnDidChangeFocus(View* focused_before, View* focused) { 338 DCHECK_EQ(GetFocusedView(), focused); 339 OnTextInputTypeChanged(focused); 340 OnCaretBoundsChanged(focused); 341} 342 343ui::InputMethod* InputMethodBridge::GetHostInputMethod() const { 344 return host_; 345} 346 347 348} // namespace views 349