Add ICU message format support
[chromium-blink-merge.git] / ui / base / ime / input_method_auralinux.cc
blob73b8c306eaca028a12ce7181ba6648c95ee7d969
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 TextInputClient* client = GetTextInputClient();
95 // Processes the result text before composition for sync mode.
96 if (!result_text_.empty()) {
97 if (filtered && NeedInsertChar()) {
98 for (const auto ch : result_text_)
99 client->InsertChar(ch, event->flags());
100 } else {
101 // If |filtered| is false, that means the IME wants to commit some text
102 // but still release the key to the application. For example, Korean IME
103 // handles ENTER key to confirm its composition but still release it for
104 // the default behavior (e.g. trigger search, etc.)
105 // In such case, don't do InsertChar because a key should only trigger the
106 // keydown event once.
107 client->InsertText(result_text_);
109 should_stop_propagation = true;
112 if (composition_changed_ && !IsTextInputTypeNone()) {
113 // If composition changed, does SetComposition if composition is not empty.
114 // And ClearComposition if composition is empty.
115 if (!composition_.text.empty())
116 client->SetCompositionText(composition_);
117 else if (result_text_.empty())
118 client->ClearCompositionText();
119 should_stop_propagation = true;
122 // Makes sure the cached composition is cleared after committing any text or
123 // cleared composition.
124 if (!client->HasCompositionText())
125 composition_.Clear();
127 if (!filtered) {
128 details = DispatchKeyEventPostIME(event);
129 if (details.dispatcher_destroyed) {
130 if (should_stop_propagation)
131 event->StopPropagation();
132 return;
134 if (event->stopped_propagation() || details.target_destroyed) {
135 ResetContext();
136 } else if (event->type() == ui::ET_KEY_PRESSED) {
137 // If a key event was not filtered by |context_| or |context_simple_|,
138 // then it means the key event didn't generate any result text. For some
139 // cases, the key event may still generate a valid character, eg. a
140 // control-key event (ctrl-a, return, tab, etc.). We need to send the
141 // character to the focused text input client by calling
142 // TextInputClient::InsertChar().
143 // Note: don't use |client| and use GetTextInputClient() here because
144 // DispatchKeyEventPostIME may cause the current text input client change.
145 base::char16 ch = event->GetCharacter();
146 if (ch && GetTextInputClient())
147 GetTextInputClient()->InsertChar(ch, event->flags());
148 should_stop_propagation = true;
152 if (should_stop_propagation)
153 event->StopPropagation();
156 void InputMethodAuraLinux::UpdateContextFocusState() {
157 bool old_text_input_type = text_input_type_;
158 text_input_type_ = GetTextInputType();
160 // We only focus in |context_| when the focus is in a textfield.
161 if (old_text_input_type != TEXT_INPUT_TYPE_NONE &&
162 text_input_type_ == TEXT_INPUT_TYPE_NONE) {
163 context_->Blur();
164 } else if (old_text_input_type == TEXT_INPUT_TYPE_NONE &&
165 text_input_type_ != TEXT_INPUT_TYPE_NONE) {
166 context_->Focus();
169 // |context_simple_| can be used in any textfield, including password box, and
170 // even if the focused text input client's text input type is
171 // ui::TEXT_INPUT_TYPE_NONE.
172 if (GetTextInputClient())
173 context_simple_->Focus();
174 else
175 context_simple_->Blur();
178 void InputMethodAuraLinux::OnTextInputTypeChanged(
179 const TextInputClient* client) {
180 UpdateContextFocusState();
181 InputMethodBase::OnTextInputTypeChanged(client);
182 // TODO(yoichio): Support inputmode HTML attribute.
185 void InputMethodAuraLinux::OnCaretBoundsChanged(const TextInputClient* client) {
186 if (!IsTextInputClientFocused(client))
187 return;
188 NotifyTextInputCaretBoundsChanged(client);
189 context_->SetCursorLocation(GetTextInputClient()->GetCaretBounds());
192 void InputMethodAuraLinux::CancelComposition(const TextInputClient* client) {
193 if (!IsTextInputClientFocused(client))
194 return;
195 ResetContext();
198 void InputMethodAuraLinux::ResetContext() {
199 if (!GetTextInputClient())
200 return;
202 // To prevent any text from being committed when resetting the |context_|;
203 is_sync_mode_ = true;
204 suppress_next_result_ = true;
206 context_->Reset();
207 context_simple_->Reset();
209 // Some input methods may not honour the reset call. Focusing out/in the
210 // |context_| to make sure it gets reset correctly.
211 if (text_input_type_ != TEXT_INPUT_TYPE_NONE) {
212 context_->Blur();
213 context_->Focus();
216 composition_.Clear();
217 result_text_.clear();
218 is_sync_mode_ = false;
219 composition_changed_ = false;
222 void InputMethodAuraLinux::OnInputLocaleChanged() {
225 std::string InputMethodAuraLinux::GetInputLocale() {
226 return "";
229 bool InputMethodAuraLinux::IsCandidatePopupOpen() const {
230 // There seems no way to detect candidate windows or any popups.
231 return false;
234 // Overriden from ui::LinuxInputMethodContextDelegate
236 void InputMethodAuraLinux::OnCommit(const base::string16& text) {
237 if (suppress_next_result_ || !GetTextInputClient()) {
238 suppress_next_result_ = false;
239 return;
242 if (is_sync_mode_) {
243 // Append the text to the buffer, because commit signal might be fired
244 // multiple times when processing a key event.
245 result_text_.append(text);
246 } else if (!IsTextInputTypeNone()) {
247 // If we are not handling key event, do not bother sending text result if
248 // the focused text input client does not support text input.
249 ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, 0);
250 ui::EventDispatchDetails details = SendFakeProcessKeyEvent(&event);
251 if (details.dispatcher_destroyed)
252 return;
253 if (!event.stopped_propagation() && !details.target_destroyed)
254 GetTextInputClient()->InsertText(text);
255 composition_.Clear();
259 void InputMethodAuraLinux::OnPreeditChanged(
260 const CompositionText& composition_text) {
261 if (suppress_next_result_ || IsTextInputTypeNone())
262 return;
264 if (is_sync_mode_) {
265 if (!composition_.text.empty() || !composition_text.text.empty())
266 composition_changed_ = true;
267 } else {
268 ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, 0);
269 ui::EventDispatchDetails details = SendFakeProcessKeyEvent(&event);
270 if (details.dispatcher_destroyed)
271 return;
272 if (!event.stopped_propagation() && !details.target_destroyed)
273 GetTextInputClient()->SetCompositionText(composition_text);
276 composition_ = composition_text;
279 void InputMethodAuraLinux::OnPreeditEnd() {
280 if (suppress_next_result_ || IsTextInputTypeNone())
281 return;
283 if (is_sync_mode_) {
284 if (!composition_.text.empty()) {
285 composition_.Clear();
286 composition_changed_ = true;
288 } else {
289 TextInputClient* client = GetTextInputClient();
290 if (client && client->HasCompositionText()) {
291 ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, 0);
292 ui::EventDispatchDetails details = SendFakeProcessKeyEvent(&event);
293 if (details.dispatcher_destroyed)
294 return;
295 if (!event.stopped_propagation() && !details.target_destroyed)
296 client->ClearCompositionText();
298 composition_.Clear();
302 // Overridden from InputMethodBase.
304 void InputMethodAuraLinux::OnFocus() {
305 InputMethodBase::OnFocus();
306 UpdateContextFocusState();
309 void InputMethodAuraLinux::OnBlur() {
310 ConfirmCompositionText();
311 InputMethodBase::OnBlur();
312 UpdateContextFocusState();
315 void InputMethodAuraLinux::OnWillChangeFocusedClient(
316 TextInputClient* focused_before,
317 TextInputClient* focused) {
318 ConfirmCompositionText();
321 void InputMethodAuraLinux::OnDidChangeFocusedClient(
322 TextInputClient* focused_before,
323 TextInputClient* focused) {
324 UpdateContextFocusState();
326 // Force to update caret bounds, in case the View thinks that the caret
327 // bounds has not changed.
328 if (text_input_type_ != TEXT_INPUT_TYPE_NONE)
329 OnCaretBoundsChanged(GetTextInputClient());
331 InputMethodBase::OnDidChangeFocusedClient(focused_before, focused);
334 // private
336 bool InputMethodAuraLinux::HasInputMethodResult() {
337 return !result_text_.empty() || composition_changed_;
340 bool InputMethodAuraLinux::NeedInsertChar() const {
341 return IsTextInputTypeNone() ||
342 (!composition_changed_ && composition_.text.empty() &&
343 result_text_.length() == 1);
346 ui::EventDispatchDetails InputMethodAuraLinux::SendFakeProcessKeyEvent(
347 ui::KeyEvent* event) const {
348 KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, event->flags());
349 ui::EventDispatchDetails details = DispatchKeyEventPostIME(&key_event);
350 if (key_event.stopped_propagation())
351 event->StopPropagation();
352 return details;
355 void InputMethodAuraLinux::ConfirmCompositionText() {
356 TextInputClient* client = GetTextInputClient();
357 if (client && client->HasCompositionText())
358 client->ConfirmCompositionText();
360 ResetContext();
363 } // namespace ui