Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / base / ime / input_method_auralinux.cc
blobade657766e8f3ff26b76660c220fbbbbbd23a0a7
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"
13 namespace ui {
15 InputMethodAuraLinux::InputMethodAuraLinux(
16 internal::InputMethodDelegate* delegate)
17 : text_input_type_(TEXT_INPUT_TYPE_NONE),
18 is_sync_mode_(false),
19 composition_changed_(false),
20 suppress_next_result_(false) {
21 SetDelegate(delegate);
22 context_ =
23 LinuxInputMethodContextFactory::instance()->CreateInputMethodContext(
24 this, false);
25 context_simple_ =
26 LinuxInputMethodContextFactory::instance()->CreateInputMethodContext(
27 this, true);
30 InputMethodAuraLinux::~InputMethodAuraLinux() {
33 LinuxInputMethodContext* InputMethodAuraLinux::GetContextForTesting(
34 bool is_simple) {
35 return is_simple ? context_simple_.get() : context_.get();
38 // Overriden from InputMethod.
40 bool InputMethodAuraLinux::OnUntranslatedIMEMessage(
41 const base::NativeEvent& event,
42 NativeEventResult* result) {
43 return false;
46 void InputMethodAuraLinux::DispatchKeyEvent(ui::KeyEvent* event) {
47 DCHECK(event->type() == ET_KEY_PRESSED || event->type() == ET_KEY_RELEASED);
48 DCHECK(system_toplevel_window_focused());
50 // If no text input client, do nothing.
51 if (!GetTextInputClient()) {
52 ignore_result(DispatchKeyEventPostIME(event));
53 return;
56 suppress_next_result_ = false;
57 composition_changed_ = false;
58 result_text_.clear();
60 bool filtered = false;
62 base::AutoReset<bool> flipper(&is_sync_mode_, true);
63 if (text_input_type_ != TEXT_INPUT_TYPE_NONE &&
64 text_input_type_ != TEXT_INPUT_TYPE_PASSWORD) {
65 filtered = context_->DispatchKeyEvent(*event);
66 } else {
67 filtered = context_simple_->DispatchKeyEvent(*event);
71 ui::EventDispatchDetails details;
72 if (event->type() == ui::ET_KEY_PRESSED && filtered) {
73 if (NeedInsertChar())
74 details = DispatchKeyEventPostIME(event);
75 else if (HasInputMethodResult())
76 details = SendFakeProcessKeyEvent(event);
77 if (details.dispatcher_destroyed)
78 return;
79 // If the KEYDOWN is stopped propagation (e.g. triggered an accelerator),
80 // don't InsertChar/InsertText to the input field.
81 if (event->stopped_propagation() || details.target_destroyed) {
82 ResetContext();
83 return;
86 // Don't send VKEY_PROCESSKEY event if there is no result text or
87 // composition. This is to workaround the weird behavior of IBus with US
88 // keyboard, which mutes the keydown and later fake a new keydown with IME
89 // result in sync mode. In that case, user would expect only
90 // keydown/keypress/keyup event without an initial 229 keydown event.
93 bool should_stop_propagation = false;
94 // Note: |client| could be NULL because DispatchKeyEventPostIME could have
95 // changed the text input client.
96 TextInputClient* client = GetTextInputClient();
97 // Processes the result text before composition for sync mode.
98 if (client && !result_text_.empty()) {
99 if (filtered && NeedInsertChar()) {
100 for (const auto ch : result_text_)
101 client->InsertChar(ch, event->flags());
102 } else {
103 // If |filtered| is false, that means the IME wants to commit some text
104 // but still release the key to the application. For example, Korean IME
105 // handles ENTER key to confirm its composition but still release it for
106 // the default behavior (e.g. trigger search, etc.)
107 // In such case, don't do InsertChar because a key should only trigger the
108 // keydown event once.
109 client->InsertText(result_text_);
111 should_stop_propagation = true;
114 if (client && composition_changed_ && !IsTextInputTypeNone()) {
115 // If composition changed, does SetComposition if composition is not empty.
116 // And ClearComposition if composition is empty.
117 if (!composition_.text.empty())
118 client->SetCompositionText(composition_);
119 else if (result_text_.empty())
120 client->ClearCompositionText();
121 should_stop_propagation = true;
124 // Makes sure the cached composition is cleared after committing any text or
125 // cleared composition.
126 if (client && !client->HasCompositionText())
127 composition_.Clear();
129 if (!filtered) {
130 details = DispatchKeyEventPostIME(event);
131 if (details.dispatcher_destroyed) {
132 if (should_stop_propagation)
133 event->StopPropagation();
134 return;
136 if (event->stopped_propagation() || details.target_destroyed) {
137 ResetContext();
138 } else if (event->type() == ui::ET_KEY_PRESSED) {
139 // If a key event was not filtered by |context_| or |context_simple_|,
140 // then it means the key event didn't generate any result text. For some
141 // cases, the key event may still generate a valid character, eg. a
142 // control-key event (ctrl-a, return, tab, etc.). We need to send the
143 // character to the focused text input client by calling
144 // TextInputClient::InsertChar().
145 // Note: don't use |client| and use GetTextInputClient() here because
146 // DispatchKeyEventPostIME may cause the current text input client change.
147 base::char16 ch = event->GetCharacter();
148 if (ch && GetTextInputClient())
149 GetTextInputClient()->InsertChar(ch, event->flags());
150 should_stop_propagation = true;
154 if (should_stop_propagation)
155 event->StopPropagation();
158 void InputMethodAuraLinux::UpdateContextFocusState() {
159 bool old_text_input_type = text_input_type_;
160 text_input_type_ = GetTextInputType();
162 // We only focus in |context_| when the focus is in a textfield.
163 if (old_text_input_type != TEXT_INPUT_TYPE_NONE &&
164 text_input_type_ == TEXT_INPUT_TYPE_NONE) {
165 context_->Blur();
166 } else if (old_text_input_type == TEXT_INPUT_TYPE_NONE &&
167 text_input_type_ != TEXT_INPUT_TYPE_NONE) {
168 context_->Focus();
171 // |context_simple_| can be used in any textfield, including password box, and
172 // even if the focused text input client's text input type is
173 // ui::TEXT_INPUT_TYPE_NONE.
174 if (GetTextInputClient())
175 context_simple_->Focus();
176 else
177 context_simple_->Blur();
180 void InputMethodAuraLinux::OnTextInputTypeChanged(
181 const TextInputClient* client) {
182 UpdateContextFocusState();
183 InputMethodBase::OnTextInputTypeChanged(client);
184 // TODO(yoichio): Support inputmode HTML attribute.
187 void InputMethodAuraLinux::OnCaretBoundsChanged(const TextInputClient* client) {
188 if (!IsTextInputClientFocused(client))
189 return;
190 NotifyTextInputCaretBoundsChanged(client);
191 context_->SetCursorLocation(GetTextInputClient()->GetCaretBounds());
194 void InputMethodAuraLinux::CancelComposition(const TextInputClient* client) {
195 if (!IsTextInputClientFocused(client))
196 return;
197 ResetContext();
200 void InputMethodAuraLinux::ResetContext() {
201 if (!GetTextInputClient())
202 return;
204 // To prevent any text from being committed when resetting the |context_|;
205 is_sync_mode_ = true;
206 suppress_next_result_ = true;
208 context_->Reset();
209 context_simple_->Reset();
211 // Some input methods may not honour the reset call. Focusing out/in the
212 // |context_| to make sure it gets reset correctly.
213 if (text_input_type_ != TEXT_INPUT_TYPE_NONE) {
214 context_->Blur();
215 context_->Focus();
218 composition_.Clear();
219 result_text_.clear();
220 is_sync_mode_ = false;
221 composition_changed_ = false;
224 void InputMethodAuraLinux::OnInputLocaleChanged() {
227 std::string InputMethodAuraLinux::GetInputLocale() {
228 return "";
231 bool InputMethodAuraLinux::IsCandidatePopupOpen() const {
232 // There seems no way to detect candidate windows or any popups.
233 return false;
236 // Overriden from ui::LinuxInputMethodContextDelegate
238 void InputMethodAuraLinux::OnCommit(const base::string16& text) {
239 if (suppress_next_result_ || !GetTextInputClient()) {
240 suppress_next_result_ = false;
241 return;
244 if (is_sync_mode_) {
245 // Append the text to the buffer, because commit signal might be fired
246 // multiple times when processing a key event.
247 result_text_.append(text);
248 } else if (!IsTextInputTypeNone()) {
249 // If we are not handling key event, do not bother sending text result if
250 // the focused text input client does not support text input.
251 ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, 0);
252 ui::EventDispatchDetails details = SendFakeProcessKeyEvent(&event);
253 if (details.dispatcher_destroyed)
254 return;
255 if (!event.stopped_propagation() && !details.target_destroyed)
256 GetTextInputClient()->InsertText(text);
257 composition_.Clear();
261 void InputMethodAuraLinux::OnPreeditChanged(
262 const CompositionText& composition_text) {
263 if (suppress_next_result_ || IsTextInputTypeNone())
264 return;
266 if (is_sync_mode_) {
267 if (!composition_.text.empty() || !composition_text.text.empty())
268 composition_changed_ = true;
269 } else {
270 ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, 0);
271 ui::EventDispatchDetails details = SendFakeProcessKeyEvent(&event);
272 if (details.dispatcher_destroyed)
273 return;
274 if (!event.stopped_propagation() && !details.target_destroyed)
275 GetTextInputClient()->SetCompositionText(composition_text);
278 composition_ = composition_text;
281 void InputMethodAuraLinux::OnPreeditEnd() {
282 if (suppress_next_result_ || IsTextInputTypeNone())
283 return;
285 if (is_sync_mode_) {
286 if (!composition_.text.empty()) {
287 composition_.Clear();
288 composition_changed_ = true;
290 } else {
291 TextInputClient* client = GetTextInputClient();
292 if (client && client->HasCompositionText()) {
293 ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, 0);
294 ui::EventDispatchDetails details = SendFakeProcessKeyEvent(&event);
295 if (details.dispatcher_destroyed)
296 return;
297 if (!event.stopped_propagation() && !details.target_destroyed)
298 client->ClearCompositionText();
300 composition_.Clear();
304 // Overridden from InputMethodBase.
306 void InputMethodAuraLinux::OnFocus() {
307 InputMethodBase::OnFocus();
308 UpdateContextFocusState();
311 void InputMethodAuraLinux::OnBlur() {
312 ConfirmCompositionText();
313 InputMethodBase::OnBlur();
314 UpdateContextFocusState();
317 void InputMethodAuraLinux::OnWillChangeFocusedClient(
318 TextInputClient* focused_before,
319 TextInputClient* focused) {
320 ConfirmCompositionText();
323 void InputMethodAuraLinux::OnDidChangeFocusedClient(
324 TextInputClient* focused_before,
325 TextInputClient* focused) {
326 UpdateContextFocusState();
328 // Force to update caret bounds, in case the View thinks that the caret
329 // bounds has not changed.
330 if (text_input_type_ != TEXT_INPUT_TYPE_NONE)
331 OnCaretBoundsChanged(GetTextInputClient());
333 InputMethodBase::OnDidChangeFocusedClient(focused_before, focused);
336 // private
338 bool InputMethodAuraLinux::HasInputMethodResult() {
339 return !result_text_.empty() || composition_changed_;
342 bool InputMethodAuraLinux::NeedInsertChar() const {
343 return IsTextInputTypeNone() ||
344 (!composition_changed_ && composition_.text.empty() &&
345 result_text_.length() == 1);
348 ui::EventDispatchDetails InputMethodAuraLinux::SendFakeProcessKeyEvent(
349 ui::KeyEvent* event) const {
350 KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, event->flags());
351 ui::EventDispatchDetails details = DispatchKeyEventPostIME(&key_event);
352 if (key_event.stopped_propagation())
353 event->StopPropagation();
354 return details;
357 void InputMethodAuraLinux::ConfirmCompositionText() {
358 TextInputClient* client = GetTextInputClient();
359 if (client && client->HasCompositionText())
360 client->ConfirmCompositionText();
362 ResetContext();
365 } // namespace ui