1 // Copyright 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.
5 #include "ui/base/ime/input_method_auralinux.h"
7 #include "base/auto_reset.h"
8 #include "base/environment.h"
9 #include "ui/base/ime/linux/linux_input_method_context_factory.h"
10 #include "ui/base/ime/text_input_client.h"
11 #include "ui/events/event.h"
15 InputMethodAuraLinux::InputMethodAuraLinux(
16 internal::InputMethodDelegate
* delegate
)
17 : text_input_type_(TEXT_INPUT_TYPE_NONE
),
19 composition_changed_(false),
20 suppress_next_result_(false) {
21 SetDelegate(delegate
);
23 LinuxInputMethodContextFactory::instance()->CreateInputMethodContext(
26 LinuxInputMethodContextFactory::instance()->CreateInputMethodContext(
30 InputMethodAuraLinux::~InputMethodAuraLinux() {}
32 LinuxInputMethodContext
* InputMethodAuraLinux::GetContextForTesting(
34 return is_simple
? context_simple_
.get() : context_
.get();
37 // Overriden from InputMethod.
39 bool InputMethodAuraLinux::OnUntranslatedIMEMessage(
40 const base::NativeEvent
& event
,
41 NativeEventResult
* result
) {
45 bool InputMethodAuraLinux::DispatchKeyEvent(const ui::KeyEvent
& event
) {
46 DCHECK(event
.type() == ET_KEY_PRESSED
|| event
.type() == ET_KEY_RELEASED
);
47 DCHECK(system_toplevel_window_focused());
49 // If no text input client, do nothing.
50 if (!GetTextInputClient())
51 return DispatchKeyEventPostIME(event
);
53 suppress_next_result_
= false;
54 composition_changed_
= false;
57 bool filtered
= false;
59 base::AutoReset
<bool> flipper(&is_sync_mode_
, true);
60 if (text_input_type_
!= TEXT_INPUT_TYPE_NONE
&&
61 text_input_type_
!= TEXT_INPUT_TYPE_PASSWORD
) {
62 filtered
= context_
->DispatchKeyEvent(event
);
64 filtered
= context_simple_
->DispatchKeyEvent(event
);
68 if (event
.type() == ui::ET_KEY_PRESSED
&& filtered
) {
70 DispatchKeyEventPostIME(event
);
71 else if (HasInputMethodResult())
72 SendFakeProcessKeyEvent(event
.flags());
74 // Don't send VKEY_PROCESSKEY event if there is no result text or
75 // composition. This is to workaround the weird behavior of IBus with US
76 // keyboard, which mutes the keydown and later fake a new keydown with IME
77 // result in sync mode. In that case, user would expect only
78 // keydown/keypress/keyup event without an initial 229 keydown event.
81 TextInputClient
* client
= GetTextInputClient();
82 // Processes the result text before composition for sync mode.
83 if (!result_text_
.empty()) {
84 if (filtered
&& NeedInsertChar()) {
85 for (const auto ch
: result_text_
)
86 client
->InsertChar(ch
, event
.flags());
88 // If |filtered| is false, that means the IME wants to commit some text
89 // but still release the key to the application. For example, Korean IME
90 // handles ENTER key to confirm its composition but still release it for
91 // the default behavior (e.g. trigger search, etc.)
92 // In such case, don't do InsertChar because a key should only trigger the
93 // keydown event once.
94 client
->InsertText(result_text_
);
98 if (composition_changed_
&& !IsTextInputTypeNone()) {
99 // If composition changed, does SetComposition if composition is not empty.
100 // And ClearComposition if composition is empty.
101 if (!composition_
.text
.empty())
102 client
->SetCompositionText(composition_
);
103 else if (result_text_
.empty())
104 client
->ClearCompositionText();
107 // Makes sure the cached composition is cleared after committing any text or
108 // cleared composition.
109 if (!client
->HasCompositionText())
110 composition_
.Clear();
113 DispatchKeyEventPostIME(event
);
114 if (event
.type() == ui::ET_KEY_PRESSED
) {
115 // If a key event was not filtered by |context_| or |context_simple_|,
116 // then it means the key event didn't generate any result text. For some
117 // cases, the key event may still generate a valid character, eg. a
118 // control-key event (ctrl-a, return, tab, etc.). We need to send the
119 // character to the focused text input client by calling
120 // TextInputClient::InsertChar().
121 // Note: don't use |client| and use GetTextInputClient() here because
122 // DispatchKeyEventPostIME may cause the current text input client change.
123 base::char16 ch
= event
.GetCharacter();
124 if (ch
&& GetTextInputClient())
125 GetTextInputClient()->InsertChar(ch
, event
.flags());
132 void InputMethodAuraLinux::UpdateContextFocusState() {
133 bool old_text_input_type
= text_input_type_
;
134 text_input_type_
= GetTextInputType();
136 // We only focus in |context_| when the focus is in a textfield.
137 if (old_text_input_type
!= TEXT_INPUT_TYPE_NONE
&&
138 text_input_type_
== TEXT_INPUT_TYPE_NONE
) {
140 } else if (old_text_input_type
== TEXT_INPUT_TYPE_NONE
&&
141 text_input_type_
!= TEXT_INPUT_TYPE_NONE
) {
145 // |context_simple_| can be used in any textfield, including password box, and
146 // even if the focused text input client's text input type is
147 // ui::TEXT_INPUT_TYPE_NONE.
148 if (GetTextInputClient())
149 context_simple_
->Focus();
151 context_simple_
->Blur();
154 void InputMethodAuraLinux::OnTextInputTypeChanged(
155 const TextInputClient
* client
) {
156 UpdateContextFocusState();
157 // TODO(yoichio): Support inputmode HTML attribute.
160 void InputMethodAuraLinux::OnCaretBoundsChanged(const TextInputClient
* client
) {
161 if (!IsTextInputClientFocused(client
))
163 context_
->SetCursorLocation(GetTextInputClient()->GetCaretBounds());
166 void InputMethodAuraLinux::CancelComposition(const TextInputClient
* client
) {
167 if (!IsTextInputClientFocused(client
))
172 void InputMethodAuraLinux::ResetContext() {
173 if (!GetTextInputClient())
176 // To prevent any text from being committed when resetting the |context_|;
177 is_sync_mode_
= true;
178 suppress_next_result_
= true;
181 context_simple_
->Reset();
183 // Some input methods may not honour the reset call. Focusing out/in the
184 // |context_| to make sure it gets reset correctly.
185 if (text_input_type_
!= TEXT_INPUT_TYPE_NONE
) {
190 composition_
.Clear();
191 result_text_
.clear();
192 is_sync_mode_
= false;
193 composition_changed_
= false;
196 void InputMethodAuraLinux::OnInputLocaleChanged() {
199 std::string
InputMethodAuraLinux::GetInputLocale() {
203 bool InputMethodAuraLinux::IsActive() {
204 // InputMethodAuraLinux is always ready and up.
208 bool InputMethodAuraLinux::IsCandidatePopupOpen() const {
209 // There seems no way to detect candidate windows or any popups.
213 // Overriden from ui::LinuxInputMethodContextDelegate
215 void InputMethodAuraLinux::OnCommit(const base::string16
& text
) {
216 if (suppress_next_result_
|| !GetTextInputClient()) {
217 suppress_next_result_
= false;
222 // Append the text to the buffer, because commit signal might be fired
223 // multiple times when processing a key event.
224 result_text_
.append(text
);
225 } else if (!IsTextInputTypeNone()) {
226 // If we are not handling key event, do not bother sending text result if
227 // the focused text input client does not support text input.
228 SendFakeProcessKeyEvent(0);
229 GetTextInputClient()->InsertText(text
);
230 composition_
.Clear();
234 void InputMethodAuraLinux::OnPreeditChanged(
235 const CompositionText
& composition_text
) {
236 if (suppress_next_result_
|| IsTextInputTypeNone())
239 composition_
= composition_text
;
242 if (!composition_
.text
.empty() || !composition_text
.text
.empty())
243 composition_changed_
= true;
245 SendFakeProcessKeyEvent(0);
246 GetTextInputClient()->SetCompositionText(composition_text
);
250 void InputMethodAuraLinux::OnPreeditEnd() {
251 if (suppress_next_result_
|| IsTextInputTypeNone())
255 if (!composition_
.text
.empty()) {
256 composition_
.Clear();
257 composition_changed_
= true;
260 TextInputClient
* client
= GetTextInputClient();
261 if (client
&& client
->HasCompositionText()) {
262 SendFakeProcessKeyEvent(0);
263 client
->ClearCompositionText();
265 composition_
.Clear();
269 // Overridden from InputMethodBase.
271 void InputMethodAuraLinux::OnFocus() {
272 InputMethodBase::OnFocus();
273 UpdateContextFocusState();
276 void InputMethodAuraLinux::OnBlur() {
277 ConfirmCompositionText();
278 InputMethodBase::OnBlur();
279 UpdateContextFocusState();
282 void InputMethodAuraLinux::OnWillChangeFocusedClient(
283 TextInputClient
* focused_before
,
284 TextInputClient
* focused
) {
285 ConfirmCompositionText();
288 void InputMethodAuraLinux::OnDidChangeFocusedClient(
289 TextInputClient
* focused_before
,
290 TextInputClient
* focused
) {
291 UpdateContextFocusState();
293 // Force to update caret bounds, in case the View thinks that the caret
294 // bounds has not changed.
295 if (text_input_type_
!= TEXT_INPUT_TYPE_NONE
)
296 OnCaretBoundsChanged(GetTextInputClient());
298 InputMethodBase::OnDidChangeFocusedClient(focused_before
, focused
);
303 bool InputMethodAuraLinux::HasInputMethodResult() {
304 return !result_text_
.empty() || composition_changed_
;
307 bool InputMethodAuraLinux::NeedInsertChar() const {
308 return IsTextInputTypeNone() ||
309 (!composition_changed_
&& composition_
.text
.empty() &&
310 result_text_
.length() == 1);
313 void InputMethodAuraLinux::SendFakeProcessKeyEvent(int flags
) const {
314 DispatchKeyEventPostIME(
315 KeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_PROCESSKEY
, flags
));
318 void InputMethodAuraLinux::ConfirmCompositionText() {
319 TextInputClient
* client
= GetTextInputClient();
320 if (client
&& client
->HasCompositionText())
321 client
->ConfirmCompositionText();