summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorhashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-21 07:58:14 +0000
committerhashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-21 07:58:14 +0000
commitd737d61b8c63eecdca2097c5fcfccce159863070 (patch)
tree24183ab83a2cfb18c661def6f60c6da318a3f69f /ui
parent1ea33a3eb282e4ae9eb1e8bb7dece0336460b6c2 (diff)
downloadchromium_src-d737d61b8c63eecdca2097c5fcfccce159863070.zip
chromium_src-d737d61b8c63eecdca2097c5fcfccce159863070.tar.gz
chromium_src-d737d61b8c63eecdca2097c5fcfccce159863070.tar.bz2
Implement hexadecimal unicode sequence character composition
Still not supported: - Preedit - Compositing with Shift+Control pressed BUG=chromium-os:15925 TEST=ui_unittests --gtest_filter="CharacterComposerTest.*" Review URL: http://codereview.chromium.org/9741004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@127924 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r--ui/base/ime/character_composer.cc112
-rw-r--r--ui/base/ime/character_composer.h22
-rw-r--r--ui/base/ime/character_composer_unittest.cc275
-rw-r--r--ui/base/ime/input_method_ibus.cc9
4 files changed, 359 insertions, 59 deletions
diff --git a/ui/base/ime/character_composer.cc b/ui/base/ime/character_composer.cc
index 3194967..7dcfa35 100644
--- a/ui/base/ime/character_composer.cc
+++ b/ui/base/ime/character_composer.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -7,9 +7,11 @@
#include <algorithm>
#include <iterator>
+#include "base/third_party/icu/icu_utf.h"
// Note for Gtk removal: gdkkeysyms.h only contains a set of
// '#define GDK_KeyName 0xNNNN' macros and does not #include any Gtk headers.
#include "third_party/gtk+/gdk/gdkkeysyms.h"
+#include "ui/base/events.h"
#include "ui/base/glib/glib_integers.h"
namespace {
@@ -62,7 +64,7 @@ inline bool operator!=(const SequenceIterator& l, const SequenceIterator& r) {
return !(l == r);
}
-// A function to compare keycode value.
+// A function to compare key value.
inline int CompareSequenceValue(unsigned int l, unsigned int r) {
return (l > r) ? 1 : ((l < r) ? -1 : 0);
}
@@ -293,8 +295,8 @@ const uint16 cedilla_compose_seqs[] = {
GDK_KEY_Multi_key, GDK_KEY_c, GDK_KEY_apostrophe, 0, 0, 0x00E7,
};
-bool KeypressShouldBeIgnored(unsigned int keycode) {
- switch(keycode) {
+bool KeypressShouldBeIgnored(unsigned int keyval) {
+ switch(keyval) {
case GDK_KEY_Shift_L:
case GDK_KEY_Shift_R:
case GDK_KEY_Control_L:
@@ -334,36 +336,106 @@ bool CheckCharacterComposeTable(const ComposeBufferType& sequence,
return false;
}
+// Converts |character| to UTF16 string.
+// Returns false when |character| is not a valid character.
+bool UTF32CharacterToUTF16(uint32 character, string16* output) {
+ output->clear();
+ // Reject invalid character. (e.g. codepoint greater than 0x10ffff)
+ if (!CBU_IS_UNICODE_CHAR(character))
+ return false;
+ if (character) {
+ output->resize(CBU16_LENGTH(character));
+ size_t i = 0;
+ CBU16_APPEND_UNSAFE(&(*output)[0], i, character);
+ }
+ return true;
+}
+
+// Returns an hexadecimal digit integer (0 to 15) corresponding to |keyval|.
+// -1 is returned when |keyval| cannot be a hexadecimal digit.
+int KeyvalToHexDigit(unsigned int keyval) {
+ if (GDK_KEY_0 <= keyval && keyval <= GDK_KEY_9)
+ return keyval - GDK_KEY_0;
+ if (GDK_KEY_a <= keyval && keyval <= GDK_KEY_f)
+ return keyval - GDK_KEY_a + 10;
+ if (GDK_KEY_A <= keyval && keyval <= GDK_KEY_F)
+ return keyval - GDK_KEY_A + 10;
+ return -1; // |keyval| cannot be a hexadecimal digit.
+}
+
} // namespace
namespace ui {
-CharacterComposer::CharacterComposer() {}
+CharacterComposer::CharacterComposer() : composition_mode_(KEY_SEQUENCE_MODE) {}
CharacterComposer::~CharacterComposer() {}
void CharacterComposer::Reset() {
compose_buffer_.clear();
composed_character_.clear();
+ composition_mode_ = KEY_SEQUENCE_MODE;
}
-bool CharacterComposer::FilterKeyPress(unsigned int keycode) {
- if(KeypressShouldBeIgnored(keycode))
+bool CharacterComposer::FilterKeyPress(unsigned int keyval,
+ unsigned int flags) {
+ composed_character_.clear();
+
+ // We don't care about modifier key presses.
+ if(KeypressShouldBeIgnored(keyval))
return false;
- compose_buffer_.push_back(keycode);
+ // When the user presses Ctrl+Shift+U, maybe switch to HEX_MODE.
+ // We don't care about other modifiers like Alt. When CapsLock is down, we
+ // do nothing because what we receive is Ctrl+Shift+u (not U).
+ if (keyval == GDK_KEY_U && (flags & EF_SHIFT_DOWN) &&
+ (flags & EF_CONTROL_DOWN)) {
+ if (composition_mode_ == KEY_SEQUENCE_MODE && compose_buffer_.empty()) {
+ // There is no ongoing composition. Let's switch to HEX_MODE.
+ composition_mode_ = HEX_MODE;
+ return true;
+ }
+ }
+
+ if (composition_mode_ == HEX_MODE) {
+ const size_t kMaxHexSequenceLength = 8;
+ const int hex_digit = KeyvalToHexDigit(keyval);
+
+ if (keyval == GDK_KEY_Escape) {
+ // Cancel composition when ESC is pressed.
+ Reset();
+ } else if (keyval == GDK_KEY_Return || keyval == GDK_KEY_KP_Enter ||
+ keyval == GDK_KEY_ISO_Enter ||
+ keyval == GDK_KEY_space || keyval == GDK_KEY_KP_Space) {
+ // Commit the composed character when Enter or space is pressed.
+ CommitHex();
+ } else if (keyval == GDK_KEY_BackSpace) {
+ // Pop back the buffer when Backspace is pressed.
+ if (!compose_buffer_.empty()) {
+ compose_buffer_.pop_back();
+ } else {
+ // If there is no character in |compose_buffer_|, cancel composition.
+ Reset();
+ }
+ } else if (hex_digit >= 0 &&
+ compose_buffer_.size() < kMaxHexSequenceLength) {
+ // Add the key to the buffer if it is a hex digit.
+ compose_buffer_.push_back(hex_digit);
+ }
+ return true;
+ }
+
+ DCHECK(composition_mode_ == KEY_SEQUENCE_MODE);
+ compose_buffer_.push_back(keyval);
// Check compose table.
- composed_character_.clear();
uint32 composed_character_utf32 = 0;
if (CheckCharacterComposeTable(compose_buffer_, &composed_character_utf32)) {
// Key press is recognized as a part of composition.
- if (composed_character_utf32 !=0) {
+ if (composed_character_utf32 != 0) {
// We get a composed character.
compose_buffer_.clear();
- // We assume that composed character is in BMP.
- if (composed_character_utf32 <= 0xffff)
- composed_character_ += static_cast<char16>(composed_character_utf32);
+ UTF32CharacterToUTF16(composed_character_utf32, &composed_character_);
}
return true;
}
@@ -376,4 +448,18 @@ bool CharacterComposer::FilterKeyPress(unsigned int keycode) {
return false;
}
+void CharacterComposer::CommitHex() {
+ DCHECK(composition_mode_ == HEX_MODE);
+ uint32 composed_character_utf32 = 0;
+ for (size_t i = 0; i != compose_buffer_.size(); ++i) {
+ const uint32 digit = compose_buffer_[i];
+ DCHECK(0 <= digit && digit < 16);
+ composed_character_utf32 <<= 4;
+ composed_character_utf32 |= digit;
+ }
+ UTF32CharacterToUTF16(composed_character_utf32, &composed_character_);
+ compose_buffer_.clear();
+ composition_mode_ = KEY_SEQUENCE_MODE;
+}
+
} // namespace ui
diff --git a/ui/base/ime/character_composer.h b/ui/base/ime/character_composer.h
index 7ba232d..3ebf53d 100644
--- a/ui/base/ime/character_composer.h
+++ b/ui/base/ime/character_composer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -8,7 +8,6 @@
#include <vector>
-#include "base/basictypes.h"
#include "base/string_util.h"
#include "ui/base/ui_export.h"
@@ -29,7 +28,9 @@ class UI_EXPORT CharacterComposer {
// Filters keypress.
// Returns true if the keypress is recognized as a part of composition
// sequence.
- bool FilterKeyPress(unsigned int keycode);
+ // |keyval| must be a GDK_KEY_* constants.
+ // |flags| must be a combination of ui::EF_* flags.
+ bool FilterKeyPress(unsigned int keyval, unsigned int flags);
// Returns a string consisting of composed character.
// Empty string is returned when there is no composition result.
@@ -38,12 +39,27 @@ class UI_EXPORT CharacterComposer {
}
private:
+ // An enum to describe composition mode.
+ enum CompositionMode {
+ // This is the initial state.
+ // Composite a character with dead-keys and compose-key.
+ KEY_SEQUENCE_MODE,
+ // Composite a character with a hexadecimal unicode sequence.
+ HEX_MODE,
+ };
+
+ // Commit a character composed from hexadecimal uncode sequence
+ void CommitHex();
+
// Remembers keypresses previously filtered.
std::vector<unsigned int> compose_buffer_;
// A string representing the composed character.
string16 composed_character_;
+ // Composition mode which this instance is in.
+ CompositionMode composition_mode_;
+
DISALLOW_COPY_AND_ASSIGN(CharacterComposer);
};
diff --git a/ui/base/ime/character_composer_unittest.cc b/ui/base/ime/character_composer_unittest.cc
index 14eea8f..da480ae 100644
--- a/ui/base/ime/character_composer_unittest.cc
+++ b/ui/base/ime/character_composer_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -6,6 +6,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/gtk+/gdk/gdkkeysyms.h"
+#include "ui/base/events.h"
#include "ui/base/glib/glib_integers.h"
namespace ui {
@@ -13,14 +14,18 @@ namespace ui {
namespace {
// Expects key is not filtered and no character is composed.
-void ExpectKeyNotFiltered(CharacterComposer* character_composer, uint key) {
- EXPECT_FALSE(character_composer->FilterKeyPress(key));
+void ExpectKeyNotFiltered(CharacterComposer* character_composer,
+ uint key,
+ uint flags) {
+ EXPECT_FALSE(character_composer->FilterKeyPress(key, flags));
EXPECT_TRUE(character_composer->composed_character().empty());
}
// Expects key is filtered and no character is composed.
-void ExpectKeyFiltered(CharacterComposer* character_composer, uint key) {
- EXPECT_TRUE(character_composer->FilterKeyPress(key));
+void ExpectKeyFiltered(CharacterComposer* character_composer,
+ uint key,
+ uint flags) {
+ EXPECT_TRUE(character_composer->FilterKeyPress(key, flags));
EXPECT_TRUE(character_composer->composed_character().empty());
}
@@ -28,10 +33,11 @@ void ExpectKeyFiltered(CharacterComposer* character_composer, uint key) {
void ExpectCharacterComposed(CharacterComposer* character_composer,
uint key1,
uint key2,
+ uint flags,
const string16& expected_character) {
- ExpectKeyFiltered(character_composer, key1);
- EXPECT_TRUE(character_composer->FilterKeyPress(key2));
- EXPECT_EQ(character_composer->composed_character(), expected_character);
+ ExpectKeyFiltered(character_composer, key1, flags);
+ EXPECT_TRUE(character_composer->FilterKeyPress(key2, flags));
+ EXPECT_EQ(expected_character, character_composer->composed_character());
}
// Expects |expected_character| is composed after sequence [key1, key2, key3].
@@ -39,9 +45,11 @@ void ExpectCharacterComposed(CharacterComposer* character_composer,
uint key1,
uint key2,
uint key3,
+ uint flags,
const string16& expected_character) {
- ExpectKeyFiltered(character_composer, key1);
- ExpectCharacterComposed(character_composer, key2, key3, expected_character);
+ ExpectKeyFiltered(character_composer, key1, flags);
+ ExpectCharacterComposed(character_composer, key2, key3, flags,
+ expected_character);
}
// Expects |expected_character| is composed after sequence [key1, key2, key3,
@@ -51,12 +59,44 @@ void ExpectCharacterComposed(CharacterComposer* character_composer,
uint key2,
uint key3,
uint key4,
+ uint flags,
+ const string16& expected_character) {
+ ExpectKeyFiltered(character_composer, key1, flags);
+ ExpectCharacterComposed(character_composer, key2, key3, key4, flags,
+ expected_character);
+}
+
+// Expects |expected_character| is composed after sequence [key1, key2, key3,
+// key 4, key5].
+void ExpectCharacterComposed(CharacterComposer* character_composer,
+ uint key1,
+ uint key2,
+ uint key3,
+ uint key4,
+ uint key5,
+ uint flags,
const string16& expected_character) {
- ExpectKeyFiltered(character_composer, key1);
- ExpectCharacterComposed(character_composer, key2, key3, key4,
+ ExpectKeyFiltered(character_composer, key1, flags);
+ ExpectCharacterComposed(character_composer, key2, key3, key4, key5, flags,
expected_character);
}
+// Expects |expected_character| is composed after sequence [key1, key2, key3,
+// key 4, key5, key6].
+void ExpectCharacterComposed(CharacterComposer* character_composer,
+ uint key1,
+ uint key2,
+ uint key3,
+ uint key4,
+ uint key5,
+ uint key6,
+ uint flags,
+ const string16& expected_character) {
+ ExpectKeyFiltered(character_composer, key1, flags);
+ ExpectCharacterComposed(character_composer, key2, key3, key4, key5, key6,
+ flags, expected_character);
+}
+
} // namespace
TEST(CharacterComposerTest, InitialState) {
@@ -66,72 +106,72 @@ TEST(CharacterComposerTest, InitialState) {
TEST(CharacterComposerTest, NormalKeyIsNotFiltered) {
CharacterComposer character_composer;
- ExpectKeyNotFiltered(&character_composer, GDK_KEY_B);
- ExpectKeyNotFiltered(&character_composer, GDK_KEY_Z);
- ExpectKeyNotFiltered(&character_composer, GDK_KEY_c);
- ExpectKeyNotFiltered(&character_composer, GDK_KEY_m);
- ExpectKeyNotFiltered(&character_composer, GDK_KEY_0);
- ExpectKeyNotFiltered(&character_composer, GDK_KEY_1);
- ExpectKeyNotFiltered(&character_composer, GDK_KEY_8);
+ ExpectKeyNotFiltered(&character_composer, GDK_KEY_B, 0);
+ ExpectKeyNotFiltered(&character_composer, GDK_KEY_Z, 0);
+ ExpectKeyNotFiltered(&character_composer, GDK_KEY_c, 0);
+ ExpectKeyNotFiltered(&character_composer, GDK_KEY_m, 0);
+ ExpectKeyNotFiltered(&character_composer, GDK_KEY_0, 0);
+ ExpectKeyNotFiltered(&character_composer, GDK_KEY_1, 0);
+ ExpectKeyNotFiltered(&character_composer, GDK_KEY_8, 0);
}
TEST(CharacterComposerTest, PartiallyMatchingSequence) {
CharacterComposer character_composer;
// Composition with sequence ['dead acute', '1'] will fail.
- ExpectKeyFiltered(&character_composer, GDK_KEY_dead_acute);
- EXPECT_TRUE(character_composer.FilterKeyPress(GDK_KEY_1));
+ ExpectKeyFiltered(&character_composer, GDK_KEY_dead_acute, 0);
+ EXPECT_TRUE(character_composer.FilterKeyPress(GDK_KEY_1, 0));
EXPECT_TRUE(character_composer.composed_character().empty());
// Composition with sequence ['dead acute', 'dead circumflex', '1'] will fail.
- ExpectKeyFiltered(&character_composer, GDK_KEY_dead_acute);
- ExpectKeyFiltered(&character_composer, GDK_KEY_dead_circumflex);
- EXPECT_TRUE(character_composer.FilterKeyPress(GDK_KEY_1));
+ ExpectKeyFiltered(&character_composer, GDK_KEY_dead_acute, 0);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_dead_circumflex, 0);
+ EXPECT_TRUE(character_composer.FilterKeyPress(GDK_KEY_1, 0));
EXPECT_TRUE(character_composer.composed_character().empty());
}
TEST(CharacterComposerTest, FullyMatchingSequences) {
CharacterComposer character_composer;
// LATIN SMALL LETTER A WITH ACUTE
- ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, GDK_KEY_a,
+ ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, GDK_KEY_a, 0,
string16(1, 0x00E1));
// LATIN CAPITAL LETTER A WITH ACUTE
- ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, GDK_KEY_A,
+ ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, GDK_KEY_A, 0,
string16(1, 0x00C1));
// GRAVE ACCENT
ExpectCharacterComposed(&character_composer, GDK_KEY_dead_grave,
- GDK_KEY_dead_grave, string16(1, 0x0060));
+ GDK_KEY_dead_grave, 0, string16(1, 0x0060));
// LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE
ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute,
- GDK_KEY_dead_circumflex, GDK_KEY_a,
+ GDK_KEY_dead_circumflex, GDK_KEY_a, 0,
string16(1, 0x1EA5));
// LATIN CAPITAL LETTER U WITH HORN AND GRAVE
ExpectCharacterComposed(&character_composer, GDK_KEY_dead_grave,
- GDK_KEY_dead_horn, GDK_KEY_U, string16(1, 0x1EEA));
+ GDK_KEY_dead_horn, GDK_KEY_U, 0, string16(1, 0x1EEA));
// LATIN CAPITAL LETTER C WITH CEDILLA
- ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, GDK_KEY_C,
+ ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, GDK_KEY_C, 0,
string16(1, 0x00C7));
// LATIN SMALL LETTER C WITH CEDILLA
- ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, GDK_KEY_c,
+ ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, GDK_KEY_c, 0,
string16(1, 0x00E7));
}
TEST(CharacterComposerTest, FullyMatchingSequencesAfterMatchingFailure) {
CharacterComposer character_composer;
// Composition with sequence ['dead acute', 'dead circumflex', '1'] will fail.
- ExpectKeyFiltered(&character_composer, GDK_KEY_dead_acute);
- ExpectKeyFiltered(&character_composer, GDK_KEY_dead_circumflex);
- EXPECT_TRUE(character_composer.FilterKeyPress(GDK_KEY_1));
+ ExpectKeyFiltered(&character_composer, GDK_KEY_dead_acute, 0);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_dead_circumflex, 0);
+ EXPECT_TRUE(character_composer.FilterKeyPress(GDK_KEY_1, 0));
EXPECT_TRUE(character_composer.composed_character().empty());
// LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE
ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute,
- GDK_KEY_dead_circumflex, GDK_KEY_a,
+ GDK_KEY_dead_circumflex, GDK_KEY_a, 0,
string16(1, 0x1EA5));
}
TEST(CharacterComposerTest, ComposedCharacterIsClearedAfterReset) {
CharacterComposer character_composer;
- ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, GDK_KEY_a,
+ ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, GDK_KEY_a, 0,
string16(1, 0x00E1));
character_composer.Reset();
EXPECT_TRUE(character_composer.composed_character().empty());
@@ -141,9 +181,9 @@ TEST(CharacterComposerTest, CompositionStateIsClearedAfterReset) {
CharacterComposer character_composer;
// Even though sequence ['dead acute', 'a'] will compose 'a with acute',
// no character is composed here because of reset.
- ExpectKeyFiltered(&character_composer, GDK_KEY_dead_acute);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_dead_acute, 0);
character_composer.Reset();
- EXPECT_FALSE(character_composer.FilterKeyPress(GDK_KEY_a));
+ EXPECT_FALSE(character_composer.FilterKeyPress(GDK_KEY_a, 0));
EXPECT_TRUE(character_composer.composed_character().empty());
}
@@ -187,10 +227,167 @@ TEST(CharacterComposerTest, MainTableIsCorrectlyOrdered) {
index += stride) {
const uint16* sequence = &gtk_compose_seqs_compact[index];
const uint16* sequence_prev = sequence - stride;
- EXPECT_EQ(compare_sequence(sequence, sequence_prev, length), 1);
+ EXPECT_EQ(1, compare_sequence(sequence, sequence_prev, length));
}
}
}
}
+TEST(CharacterComposerTest, HexadecimalComposition) {
+ CharacterComposer character_composer;
+ // HIRAGANA LETTER A (U+3042)
+ ExpectKeyFiltered(&character_composer, GDK_KEY_U,
+ EF_SHIFT_DOWN | EF_CONTROL_DOWN);
+ ExpectCharacterComposed(&character_composer, GDK_KEY_3, GDK_KEY_0, GDK_KEY_4,
+ GDK_KEY_2, GDK_KEY_space, 0, string16(1, 0x3042));
+ // MUSICAL KEYBOARD (U+1F3B9)
+ const char16 kMusicalKeyboard[] = {0xd83c, 0xdfb9};
+ ExpectKeyFiltered(&character_composer, GDK_KEY_U,
+ EF_SHIFT_DOWN | EF_CONTROL_DOWN);
+ ExpectCharacterComposed(&character_composer, GDK_KEY_1, GDK_KEY_f, GDK_KEY_3,
+ GDK_KEY_b, GDK_KEY_9, GDK_KEY_Return, 0,
+ string16(kMusicalKeyboard,
+ kMusicalKeyboard +
+ arraysize(kMusicalKeyboard)));
+}
+
+TEST(CharacterComposerTest, HexadecimalCompositionWithNonHexKey) {
+ CharacterComposer character_composer;
+
+ // Sequence [Ctrl+Shift+U, x, space] does not compose a character.
+ ExpectKeyFiltered(&character_composer, GDK_KEY_U,
+ EF_SHIFT_DOWN | EF_CONTROL_DOWN);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_x, 0);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_space, 0);
+ EXPECT_TRUE(character_composer.composed_character().empty());
+
+ // HIRAGANA LETTER A (U+3042) with a sequence [3, 0, x, 4, 2].
+ ExpectKeyFiltered(&character_composer, GDK_KEY_U,
+ EF_SHIFT_DOWN | EF_CONTROL_DOWN);
+ ExpectCharacterComposed(&character_composer, GDK_KEY_3, GDK_KEY_0, GDK_KEY_x,
+ GDK_KEY_4, GDK_KEY_2, GDK_KEY_space, 0,
+ string16(1, 0x3042));
+}
+
+TEST(CharacterComposerTest, HexadecimalCompositionWithAdditionalModifiers) {
+ CharacterComposer character_composer;
+
+ // Ctrl+Shift+Alt+U
+ // HIRAGANA LETTER A (U+3042)
+ ExpectKeyFiltered(&character_composer, GDK_KEY_U,
+ EF_SHIFT_DOWN | EF_CONTROL_DOWN | EF_ALT_DOWN);
+ ExpectCharacterComposed(&character_composer, GDK_KEY_3, GDK_KEY_0, GDK_KEY_4,
+ GDK_KEY_2, GDK_KEY_space, 0, string16(1, 0x3042));
+
+ // Ctrl+Shift+u (CapsLock enabled)
+ ExpectKeyNotFiltered(&character_composer, GDK_KEY_u,
+ EF_SHIFT_DOWN | EF_CONTROL_DOWN | EF_CAPS_LOCK_DOWN);
+}
+
+TEST(CharacterComposerTest, CancelHexadecimalComposition) {
+ CharacterComposer character_composer;
+ // Cancel composition with ESC.
+ ExpectKeyFiltered(&character_composer, GDK_KEY_U,
+ EF_SHIFT_DOWN | EF_CONTROL_DOWN);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_1, 0);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_Escape, 0);
+
+ // Now we can start composition again since the last composition was
+ // cancelled.
+ // HIRAGANA LETTER A (U+3042)
+ ExpectKeyFiltered(&character_composer, GDK_KEY_U,
+ EF_SHIFT_DOWN | EF_CONTROL_DOWN);
+ ExpectCharacterComposed(&character_composer, GDK_KEY_3, GDK_KEY_0, GDK_KEY_4,
+ GDK_KEY_2, GDK_KEY_space, 0, string16(1, 0x3042));
+}
+
+TEST(CharacterComposerTest, HexadecimalCompositionWithBackspace) {
+ CharacterComposer character_composer;
+ // HIRAGANA LETTER A (U+3042)
+ ExpectKeyFiltered(&character_composer, GDK_KEY_U,
+ EF_SHIFT_DOWN | EF_CONTROL_DOWN);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_3, 0);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_0, 0);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_f, 0);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_BackSpace, 0);
+ ExpectCharacterComposed(&character_composer, GDK_KEY_4, GDK_KEY_2,
+ GDK_KEY_space, 0, string16(1, 0x3042));
+}
+
+TEST(CharacterComposerTest, CancelHexadecimalCompositionWithBackspace) {
+ CharacterComposer character_composer;
+
+ // Backspace just after Ctrl+Shift+U.
+ ExpectKeyFiltered(&character_composer, GDK_KEY_U,
+ EF_SHIFT_DOWN | EF_CONTROL_DOWN);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_BackSpace, 0);
+ ExpectKeyNotFiltered(&character_composer, GDK_KEY_3, 0);
+
+ // Backspace twice after Ctrl+Shift+U and 3.
+ ExpectKeyFiltered(&character_composer, GDK_KEY_U,
+ EF_SHIFT_DOWN | EF_CONTROL_DOWN);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_3, 0);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_BackSpace, 0);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_BackSpace, 0);
+ ExpectKeyNotFiltered(&character_composer, GDK_KEY_3, 0);
+}
+
+TEST(CharacterComposerTest, InvalidHexadecimalSequence) {
+ CharacterComposer character_composer;
+ // U+FFFFFFFF
+ ExpectKeyFiltered(&character_composer, GDK_KEY_U,
+ EF_SHIFT_DOWN | EF_CONTROL_DOWN);
+ for (int i = 0; i < 8; ++i)
+ ExpectKeyFiltered(&character_composer, GDK_KEY_f, 0);
+ EXPECT_TRUE(character_composer.FilterKeyPress(GDK_KEY_space, 0));
+ EXPECT_TRUE(character_composer.composed_character().empty());
+
+ // U+0000 (Actually, this is a valid unicode character, but we don't
+ // compose a string with a character '\0')
+ ExpectKeyFiltered(&character_composer, GDK_KEY_U,
+ EF_SHIFT_DOWN | EF_CONTROL_DOWN);
+ for (int i = 0; i < 4; ++i)
+ ExpectKeyFiltered(&character_composer, GDK_KEY_0, 0);
+ EXPECT_TRUE(character_composer.FilterKeyPress(GDK_KEY_space, 0));
+ EXPECT_TRUE(character_composer.composed_character().empty());
+
+ // U+10FFFF
+ ExpectKeyFiltered(&character_composer, GDK_KEY_U,
+ EF_SHIFT_DOWN | EF_CONTROL_DOWN);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_1, 0);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_0, 0);
+ for (int i = 0; i < 4; ++i)
+ ExpectKeyFiltered(&character_composer, GDK_KEY_f, 0);
+ EXPECT_TRUE(character_composer.FilterKeyPress(GDK_KEY_space, 0));
+ EXPECT_TRUE(character_composer.composed_character().empty());
+
+ // U+110000
+ ExpectKeyFiltered(&character_composer, GDK_KEY_U,
+ EF_SHIFT_DOWN | EF_CONTROL_DOWN);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_1, 0);
+ ExpectKeyFiltered(&character_composer, GDK_KEY_1, 0);
+ for (int i = 0; i < 4; ++i)
+ ExpectKeyFiltered(&character_composer, GDK_KEY_0, 0);
+ EXPECT_TRUE(character_composer.FilterKeyPress(GDK_KEY_space, 0));
+ EXPECT_TRUE(character_composer.composed_character().empty());
+}
+
+TEST(CharacterComposerTest, HexadecimalSequenceAndDeadKey) {
+ CharacterComposer character_composer;
+ // LATIN SMALL LETTER A WITH ACUTE
+ ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, GDK_KEY_a, 0,
+ string16(1, 0x00E1));
+ // HIRAGANA LETTER A (U+3042) with dead_acute ignored.
+ ExpectKeyFiltered(&character_composer, GDK_KEY_U,
+ EF_SHIFT_DOWN | EF_CONTROL_DOWN);
+ ExpectCharacterComposed(&character_composer, GDK_KEY_3, GDK_KEY_0,
+ GDK_KEY_dead_acute, GDK_KEY_4, GDK_KEY_2,
+ GDK_KEY_space, 0, string16(1, 0x3042));
+ // LATIN CAPITAL LETTER U WITH ACUTE while 'U' is pressed with Ctrl+Shift.
+ ExpectKeyFiltered(&character_composer, GDK_KEY_dead_acute, 0);
+ EXPECT_TRUE(character_composer.FilterKeyPress(
+ GDK_KEY_U, EF_SHIFT_DOWN | EF_CONTROL_DOWN));
+ EXPECT_EQ(string16(1, 0x00DA), character_composer.composed_character());
+}
+
} // namespace ui
diff --git a/ui/base/ime/input_method_ibus.cc b/ui/base/ime/input_method_ibus.cc
index b847215..d9a35cb 100644
--- a/ui/base/ime/input_method_ibus.cc
+++ b/ui/base/ime/input_method_ibus.cc
@@ -574,8 +574,11 @@ void InputMethodIBus::ProcessUnfilteredKeyPressEvent(
if (client != GetTextInputClient())
return;
+ const uint32 state =
+ EventFlagsFromXFlags(GetKeyEvent(native_event)->state);
+
// Process compose and dead keys
- if (character_composer_.FilterKeyPress(ibus_keyval)) {
+ if (character_composer_.FilterKeyPress(ibus_keyval, state)) {
string16 composed = character_composer_.composed_character();
if (!composed.empty()) {
client = GetTextInputClient();
@@ -590,8 +593,6 @@ void InputMethodIBus::ProcessUnfilteredKeyPressEvent(
// to send corresponding character to the focused text input client.
client = GetTextInputClient();
- const uint32 state =
- EventFlagsFromXFlags(GetKeyEvent(native_event)->state);
uint16 ch = 0;
if (!(state & ui::EF_CONTROL_DOWN))
ch = ui::GetCharacterFromXEvent(native_event);
@@ -615,7 +616,7 @@ void InputMethodIBus::ProcessUnfilteredFabricatedKeyPressEvent(
if (client != GetTextInputClient())
return;
- if (character_composer_.FilterKeyPress(ibus_keyval)) {
+ if (character_composer_.FilterKeyPress(ibus_keyval, flags)) {
string16 composed = character_composer_.composed_character();
if (!composed.empty()) {
client = GetTextInputClient();