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