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/events/event.h"
8#include "ui/base/ime/input_method.h"
9#include "ui/gfx/rect.h"
10#include "ui/views/view.h"
11#include "ui/views/widget/widget.h"
12
13namespace views {
14
15InputMethodBridge::InputMethodBridge(internal::InputMethodDelegate* delegate,
16                                     ui::InputMethod* host,
17                                     bool shared_input_method)
18    : host_(host),
19      shared_input_method_(shared_input_method),
20      context_focused_(false) {
21  DCHECK(host_);
22  SetDelegate(delegate);
23}
24
25InputMethodBridge::~InputMethodBridge() {
26  // By the time we get here it's very likely |widget_|'s NativeWidget has been
27  // destroyed. This means any calls to |widget_| that go to the NativeWidget,
28  // such as IsActive(), will crash. SetFocusedTextInputClient() may callback to
29  // this and go into |widget_|. NULL out |widget_| so we don't attempt to use
30  // it.
31  DetachFromWidget();
32  if (host_->GetTextInputClient() == this)
33    host_->SetFocusedTextInputClient(NULL);
34}
35
36void InputMethodBridge::OnFocus() {
37  // Direct the shared IME to send TextInputClient messages to |this| object.
38  if (shared_input_method_ || !host_->GetTextInputClient())
39    host_->SetFocusedTextInputClient(this);
40
41  // TODO(yusukes): We don't need to call OnTextInputTypeChanged() once we move
42  // text input type tracker code to ui::InputMethodBase.
43  if (GetFocusedView())
44    OnTextInputTypeChanged(GetFocusedView());
45}
46
47void InputMethodBridge::OnBlur() {
48  if (HasCompositionText()) {
49    ConfirmCompositionText();
50    host_->CancelComposition(this);
51  }
52
53  if (host_->GetTextInputClient() == this)
54    host_->SetFocusedTextInputClient(NULL);
55}
56
57bool InputMethodBridge::OnUntranslatedIMEMessage(const base::NativeEvent& event,
58                                                 NativeEventResult* result) {
59  return host_->OnUntranslatedIMEMessage(event, result);
60}
61
62void InputMethodBridge::DispatchKeyEvent(const ui::KeyEvent& key) {
63  DCHECK(key.type() == ui::ET_KEY_PRESSED || key.type() == ui::ET_KEY_RELEASED);
64
65  // We can just dispatch the event here since the |key| is already processed by
66  // the system-wide IME.
67  DispatchKeyEventPostIME(key);
68}
69
70void InputMethodBridge::OnTextInputTypeChanged(View* view) {
71  if (IsViewFocused(view))
72    host_->OnTextInputTypeChanged(this);
73  InputMethodBase::OnTextInputTypeChanged(view);
74}
75
76void InputMethodBridge::OnCaretBoundsChanged(View* view) {
77  if (IsViewFocused(view) && !IsTextInputTypeNone())
78    host_->OnCaretBoundsChanged(this);
79}
80
81void InputMethodBridge::CancelComposition(View* view) {
82  if (IsViewFocused(view))
83    host_->CancelComposition(this);
84}
85
86void InputMethodBridge::OnInputLocaleChanged() {
87  return host_->OnInputLocaleChanged();
88}
89
90std::string InputMethodBridge::GetInputLocale() {
91  return host_->GetInputLocale();
92}
93
94base::i18n::TextDirection InputMethodBridge::GetInputTextDirection() {
95  return host_->GetInputTextDirection();
96}
97
98bool InputMethodBridge::IsActive() {
99  return host_->IsActive();
100}
101
102bool InputMethodBridge::IsCandidatePopupOpen() const {
103  return host_->IsCandidatePopupOpen();
104}
105
106// Overridden from TextInputClient. Forward an event from the system-wide IME
107// to the text input |client|, which is e.g. views::NativeTextfieldViews.
108void InputMethodBridge::SetCompositionText(
109    const ui::CompositionText& composition) {
110  TextInputClient* client = GetTextInputClient();
111  if (client)
112    client->SetCompositionText(composition);
113}
114
115void InputMethodBridge::ConfirmCompositionText() {
116  TextInputClient* client = GetTextInputClient();
117  if (client)
118    client->ConfirmCompositionText();
119}
120
121void InputMethodBridge::ClearCompositionText() {
122  TextInputClient* client = GetTextInputClient();
123  if (client)
124    client->ClearCompositionText();
125}
126
127void InputMethodBridge::InsertText(const string16& text) {
128  TextInputClient* client = GetTextInputClient();
129  if (client)
130    client->InsertText(text);
131}
132
133void InputMethodBridge::InsertChar(char16 ch, int flags) {
134  TextInputClient* client = GetTextInputClient();
135  if (client)
136    client->InsertChar(ch, flags);
137}
138
139gfx::NativeWindow InputMethodBridge::GetAttachedWindow() const {
140  TextInputClient* client = GetTextInputClient();
141  return client ?
142      client->GetAttachedWindow() : static_cast<gfx::NativeWindow>(NULL);
143}
144
145ui::TextInputType InputMethodBridge::GetTextInputType() const {
146  TextInputClient* client = GetTextInputClient();
147  return client ? client->GetTextInputType() : ui::TEXT_INPUT_TYPE_NONE;
148}
149
150ui::TextInputMode InputMethodBridge::GetTextInputMode() const {
151  TextInputClient* client = GetTextInputClient();
152  return client ? client->GetTextInputMode() : ui::TEXT_INPUT_MODE_DEFAULT;
153}
154
155bool InputMethodBridge::CanComposeInline() const {
156  TextInputClient* client = GetTextInputClient();
157  return client ? client->CanComposeInline() : true;
158}
159
160gfx::Rect InputMethodBridge::GetCaretBounds() {
161  TextInputClient* client = GetTextInputClient();
162  if (!client)
163    return gfx::Rect();
164
165  return client->GetCaretBounds();
166}
167
168bool InputMethodBridge::GetCompositionCharacterBounds(uint32 index,
169                                                      gfx::Rect* rect) {
170  DCHECK(rect);
171  TextInputClient* client = GetTextInputClient();
172  if (!client)
173    return false;
174
175  return client->GetCompositionCharacterBounds(index, rect);
176}
177
178bool InputMethodBridge::HasCompositionText() {
179  TextInputClient* client = GetTextInputClient();
180  return client ? client->HasCompositionText() : false;
181}
182
183bool InputMethodBridge::GetTextRange(ui::Range* range) {
184  TextInputClient* client = GetTextInputClient();
185  return client ?  client->GetTextRange(range) : false;
186}
187
188bool InputMethodBridge::GetCompositionTextRange(ui::Range* range) {
189  TextInputClient* client = GetTextInputClient();
190  return client ? client->GetCompositionTextRange(range) : false;
191}
192
193bool InputMethodBridge::GetSelectionRange(ui::Range* range) {
194  TextInputClient* client = GetTextInputClient();
195  return client ? client->GetSelectionRange(range) : false;
196}
197
198bool InputMethodBridge::SetSelectionRange(const ui::Range& range) {
199  TextInputClient* client = GetTextInputClient();
200  return client ? client->SetSelectionRange(range) : false;
201}
202
203bool InputMethodBridge::DeleteRange(const ui::Range& range) {
204  TextInputClient* client = GetTextInputClient();
205  return client ? client->DeleteRange(range) : false;
206}
207
208bool InputMethodBridge::GetTextFromRange(
209    const ui::Range& range, string16* text) {
210  TextInputClient* client = GetTextInputClient();
211  return client ? client->GetTextFromRange(range, text) : false;
212}
213
214void InputMethodBridge::OnInputMethodChanged() {
215  TextInputClient* client = GetTextInputClient();
216  if (client)
217    client->OnInputMethodChanged();
218}
219
220bool InputMethodBridge::ChangeTextDirectionAndLayoutAlignment(
221    base::i18n::TextDirection direction) {
222  TextInputClient* client = GetTextInputClient();
223  return client ?
224      client->ChangeTextDirectionAndLayoutAlignment(direction) : false;
225}
226
227void InputMethodBridge::ExtendSelectionAndDelete(size_t before, size_t after) {
228  TextInputClient* client = GetTextInputClient();
229  if (client)
230    client->ExtendSelectionAndDelete(before, after);
231}
232
233void InputMethodBridge::EnsureCaretInRect(const gfx::Rect& rect) {
234  TextInputClient* client = GetTextInputClient();
235  if (client)
236    client->EnsureCaretInRect(rect);
237}
238
239// Overridden from FocusChangeListener.
240void InputMethodBridge::OnWillChangeFocus(View* focused_before, View* focused) {
241  if (HasCompositionText()) {
242    ConfirmCompositionText();
243    CancelComposition(focused_before);
244  }
245}
246
247void InputMethodBridge::OnDidChangeFocus(View* focused_before, View* focused) {
248  DCHECK_EQ(GetFocusedView(), focused);
249  OnTextInputTypeChanged(focused);
250  OnCaretBoundsChanged(focused);
251}
252
253ui::InputMethod* InputMethodBridge::GetHostInputMethod() const {
254  return host_;
255}
256
257
258}  // namespace views
259