diff options
author | hashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-21 07:58:14 +0000 |
---|---|---|
committer | hashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-21 07:58:14 +0000 |
commit | d737d61b8c63eecdca2097c5fcfccce159863070 (patch) | |
tree | 24183ab83a2cfb18c661def6f60c6da318a3f69f /ui | |
parent | 1ea33a3eb282e4ae9eb1e8bb7dece0336460b6c2 (diff) | |
download | chromium_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.cc | 112 | ||||
-rw-r--r-- | ui/base/ime/character_composer.h | 22 | ||||
-rw-r--r-- | ui/base/ime/character_composer_unittest.cc | 275 | ||||
-rw-r--r-- | ui/base/ime/input_method_ibus.cc | 9 |
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 = >k_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(); |