diff options
author | suzhe@google.com <suzhe@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-21 23:14:41 +0000 |
---|---|---|
committer | suzhe@google.com <suzhe@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-21 23:14:41 +0000 |
commit | 1acaaa82022e7e7003bdcff770e96c074da349c0 (patch) | |
tree | f7ff03075fd6f1fc5037c9a0d929acabe4d64e9f /views | |
parent | 2f2401dba48e119acfbb98909d060f8840bca5e6 (diff) | |
download | chromium_src-1acaaa82022e7e7003bdcff770e96c074da349c0.zip chromium_src-1acaaa82022e7e7003bdcff770e96c074da349c0.tar.gz chromium_src-1acaaa82022e7e7003bdcff770e96c074da349c0.tar.bz2 |
Add GetCharacter() and GetUnmodifiedCharacter() methods to views::Event.
BUG=75003
TEST=views_unittests --gtest_filter=EventTest.*
Review URL: http://codereview.chromium.org/6713027
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@78943 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views')
-rw-r--r-- | views/events/event.cc | 101 | ||||
-rw-r--r-- | views/events/event.h | 34 | ||||
-rw-r--r-- | views/events/event_gtk.cc | 46 | ||||
-rw-r--r-- | views/events/event_unittest.cc | 106 | ||||
-rw-r--r-- | views/events/event_win.cc | 11 | ||||
-rw-r--r-- | views/events/event_x.cc | 44 | ||||
-rw-r--r-- | views/views.gyp | 1 |
7 files changed, 342 insertions, 1 deletions
diff --git a/views/events/event.cc b/views/events/event.cc index de8a1ba..dfab221 100644 --- a/views/events/event.cc +++ b/views/events/event.cc @@ -68,6 +68,107 @@ KeyEvent::KeyEvent(ui::EventType type, ui::KeyboardCode key_code, key_code_(key_code) { } +// KeyEvent, private: --------------------------------------------------------- + +// static +uint16 KeyEvent::GetCharacterFromKeyCode(ui::KeyboardCode key_code, int flags) { + const bool ctrl = (flags & ui::EF_CONTROL_DOWN) != 0; + const bool shift = (flags & ui::EF_SHIFT_DOWN) != 0; + const bool upper = shift ^ ((flags & ui::EF_CAPS_LOCK_DOWN) != 0); + + // Following Windows behavior to map ctrl-a ~ ctrl-z to \x01 ~ \x1A. + if (key_code >= ui::VKEY_A && key_code <= ui::VKEY_Z) + return key_code - ui::VKEY_A + (ctrl ? 1 : (upper ? 'A' : 'a')); + + // Other ctrl characters + if (ctrl) { + if (shift) { + // following graphics chars require shift key to input. + switch (key_code) { + // ctrl-@ maps to \x00 (Null byte) + case ui::VKEY_2: + return 0; + // ctrl-^ maps to \x1E (Record separator, Information separator two) + case ui::VKEY_6: + return 0x1E; + // ctrl-_ maps to \x1F (Unit separator, Information separator one) + case ui::VKEY_OEM_MINUS: + return 0x1F; + // Returns 0 for all other keys to avoid inputting unexpected chars. + default: + return 0; + } + } else { + switch (key_code) { + // ctrl-[ maps to \x1B (Escape) + case ui::VKEY_OEM_4: + return 0x1B; + // ctrl-\ maps to \x1C (File separator, Information separator four) + case ui::VKEY_OEM_5: + return 0x1C; + // ctrl-] maps to \x1D (Group separator, Information separator three) + case ui::VKEY_OEM_6: + return 0x1D; + // ctrl-Enter maps to \x0A (Line feed) + case ui::VKEY_RETURN: + return 0x0A; + // Returns 0 for all other keys to avoid inputting unexpected chars. + default: + return 0; + } + } + } + + // Normal characters + if (key_code >= ui::VKEY_0 && key_code <= ui::VKEY_9) + return shift ? ")!@#$%^&*("[key_code - ui::VKEY_0] : key_code; + else if (key_code >= ui::VKEY_NUMPAD0 && key_code <= ui::VKEY_NUMPAD9) + return key_code - ui::VKEY_NUMPAD0 + '0'; + + switch (key_code) { + case ui::VKEY_TAB: + return '\t'; + case ui::VKEY_RETURN: + return '\r'; + case ui::VKEY_MULTIPLY: + return '*'; + case ui::VKEY_ADD: + return '+'; + case ui::VKEY_SUBTRACT: + return '-'; + case ui::VKEY_DECIMAL: + return '.'; + case ui::VKEY_DIVIDE: + return '/'; + case ui::VKEY_SPACE: + return ' '; + case ui::VKEY_OEM_1: + return shift ? ':' : ';'; + case ui::VKEY_OEM_PLUS: + return shift ? '+' : '='; + case ui::VKEY_OEM_COMMA: + return shift ? '<' : ','; + case ui::VKEY_OEM_MINUS: + return shift ? '_' : '-'; + case ui::VKEY_OEM_PERIOD: + return shift ? '>' : '.'; + case ui::VKEY_OEM_2: + return shift ? '?' : '/'; + case ui::VKEY_OEM_3: + return shift ? '~' : '`'; + case ui::VKEY_OEM_4: + return shift ? '{' : '['; + case ui::VKEY_OEM_5: + return shift ? '|' : '\\'; + case ui::VKEY_OEM_6: + return shift ? '}' : ']'; + case ui::VKEY_OEM_7: + return shift ? '"' : '\''; + default: + return 0; + } +} + //////////////////////////////////////////////////////////////////////////////// // MouseEvent, public: diff --git a/views/events/event.h b/views/events/event.h index 4173d53..15e320d 100644 --- a/views/events/event.h +++ b/views/events/event.h @@ -289,14 +289,46 @@ class KeyEvent : public Event { // Creates a new KeyEvent synthetically (i.e. not in response to an input // event from the host environment). This is typically only used in testing as // some metadata obtainable from the underlying native event is not present. - // TODO(beng): see if we can kill this. + // It's also used by input methods to fabricate keyboard events. KeyEvent(ui::EventType type, ui::KeyboardCode key_code, int event_flags); ui::KeyboardCode key_code() const { return key_code_; } + // Gets the character generated by this key event. It only supports Unicode + // BMP characters. + uint16 GetCharacter() const; + + // Gets the character generated by this key event ignoring concurrently-held + // modifiers (except shift). + uint16 GetUnmodifiedCharacter() const; + private: + // A helper function to get the character generated by a key event in a + // platform independent way. It supports control characters as well. + // It assumes a US keyboard layout is used, so it may only be used when there + // is no native event or no better way to get the character. + // For example, if a virtual keyboard implementation can only generate key + // events with key_code and flags information, then there is no way for us to + // determine the actual character that should be generate by the key. Because + // a key_code only represents a physical key on the keyboard, it has nothing + // to do with the actual character printed on that key. In such case, the only + // thing we can do is to assume that we are using a US keyboard and get the + // character according to US keyboard layout definition. + // If a virtual keyboard implementation wants to support other keyboard + // layouts, that may generate different text for a certain key than on a US + // keyboard, a special native event object should be introduced to carry extra + // information to help determine the correct character. + // Take XKeyEvent as an example, it contains not only keycode and modifier + // flags but also group and other extra XKB information to help determine the + // correct character. That's why we can use XLookupString() function to get + // the correct text generated by a X key event (See how is GetCharacter() + // implemented in event_x.cc). + // TODO(suzhe): define a native event object for virtual keyboard. We may need + // to take the actual feature requirement into account. + static uint16 GetCharacterFromKeyCode(ui::KeyboardCode key_code, int flags); + ui::KeyboardCode key_code_; DISALLOW_COPY_AND_ASSIGN(KeyEvent); diff --git a/views/events/event_gtk.cc b/views/events/event_gtk.cc index 8dfa62d..8c62a76 100644 --- a/views/events/event_gtk.cc +++ b/views/events/event_gtk.cc @@ -77,6 +77,15 @@ unsigned int GetGdkStateFromNative(NativeEvent native_event) { return 0; } +#if !defined(TOUCH_UI) +uint16 GetCharacterFromGdkKeyval(guint keyval) { + guint32 ch = gdk_keyval_to_unicode(keyval); + + // We only support BMP characters. + return ch < 0xFFFE ? static_cast<uint16>(ch) : 0; +} +#endif + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -179,6 +188,43 @@ KeyEvent::KeyEvent(NativeEvent2 native_event_2, FromNativeEvent2 from_native) // TODO(beng): remove once we rid views of Gtk/Gdk. NOTREACHED(); } + +uint16 KeyEvent::GetCharacter() const { + // Gtk doesn't support control characters. + if (IsControlDown() || !native_event()) + return GetCharacterFromKeyCode(key_code_, flags()); + + uint16 ch = GetCharacterFromGdkKeyval( + GetGdkEventKeyFromNative(native_event())->keyval); + return ch ? ch : GetCharacterFromKeyCode(key_code_, flags()); +} + +uint16 KeyEvent::GetUnmodifiedCharacter() const { + if (!native_event()) + return GetCharacterFromKeyCode(key_code_, flags() & ui::EF_SHIFT_DOWN); + + GdkEventKey* key = GetGdkEventKeyFromNative(native_event()); + + static const guint kIgnoredModifiers = + GDK_CONTROL_MASK | GDK_LOCK_MASK | GDK_MOD1_MASK | GDK_MOD2_MASK | + GDK_MOD3_MASK | GDK_MOD4_MASK | GDK_MOD5_MASK | GDK_SUPER_MASK | + GDK_HYPER_MASK | GDK_META_MASK; + + // We can't use things like (key->state & GDK_SHIFT_MASK), as it may mask out + // bits used by X11 or Gtk internally. + GdkModifierType modifiers = + static_cast<GdkModifierType>(key->state & ~kIgnoredModifiers); + guint keyval = 0; + uint16 ch = 0; + if (gdk_keymap_translate_keyboard_state(NULL, key->hardware_keycode, + modifiers, key->group, &keyval, + NULL, NULL, NULL)) { + ch = GetCharacterFromGdkKeyval(keyval); + } + + return ch ? ch : + GetCharacterFromKeyCode(key_code_, flags() & ui::EF_SHIFT_DOWN); +} #endif //////////////////////////////////////////////////////////////////////////////// diff --git a/views/events/event_unittest.cc b/views/events/event_unittest.cc new file mode 100644 index 0000000..8afb48e --- /dev/null +++ b/views/events/event_unittest.cc @@ -0,0 +1,106 @@ +// Copyright (c) 2011 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. + +#include "testing/gtest/include/gtest/gtest.h" + +#include "base/basictypes.h" +#include "views/events/event.h" + +namespace views { + +class EventTest : public testing::Test { + public: + EventTest() { + } + + private: + DISALLOW_COPY_AND_ASSIGN(EventTest); +}; + +TEST_F(EventTest, KeyEvent) { + static const struct { + ui::KeyboardCode key_code; + int flags; + uint16 character; + uint16 unmodified_character; + } kTestData[] = { + { ui::VKEY_A, 0, 'a', 'a' }, + { ui::VKEY_A, ui::EF_SHIFT_DOWN, 'A', 'A' }, + { ui::VKEY_A, ui::EF_CAPS_LOCK_DOWN, 'A', 'a' }, + { ui::VKEY_A, ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN, 'a', 'A' }, + { ui::VKEY_A, ui::EF_CONTROL_DOWN, 0x01, 'a' }, + { ui::VKEY_A, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, '\x01', 'A' }, + { ui::VKEY_Z, 0, 'z', 'z' }, + { ui::VKEY_Z, ui::EF_SHIFT_DOWN, 'Z', 'Z' }, + { ui::VKEY_Z, ui::EF_CAPS_LOCK_DOWN, 'Z', 'z' }, + { ui::VKEY_Z, ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN, 'z', 'Z' }, + { ui::VKEY_Z, ui::EF_CONTROL_DOWN, '\x1A', 'z' }, + { ui::VKEY_Z, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, '\x1A', 'Z' }, + + { ui::VKEY_2, ui::EF_CONTROL_DOWN, '\0', '2' }, + { ui::VKEY_2, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, '\0', '@' }, + { ui::VKEY_6, ui::EF_CONTROL_DOWN, '\0', '6' }, + { ui::VKEY_6, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, '\x1E', '^' }, + { ui::VKEY_OEM_MINUS, ui::EF_CONTROL_DOWN, '\0', '-' }, + { ui::VKEY_OEM_MINUS, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, '\x1F', '_'}, + { ui::VKEY_OEM_4, ui::EF_CONTROL_DOWN, '\x1B', '[' }, + { ui::VKEY_OEM_4, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, '\0', '{' }, + { ui::VKEY_OEM_5, ui::EF_CONTROL_DOWN, '\x1C', '\\' }, + { ui::VKEY_OEM_5, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, '\0', '|' }, + { ui::VKEY_OEM_6, ui::EF_CONTROL_DOWN, '\x1D', ']' }, + { ui::VKEY_OEM_6, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, '\0', '}' }, + { ui::VKEY_RETURN, ui::EF_CONTROL_DOWN, '\x0A', '\r' }, + + { ui::VKEY_0, 0, '0', '0' }, + { ui::VKEY_0, ui::EF_SHIFT_DOWN, ')', ')' }, + { ui::VKEY_0, ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN, ')', ')' }, + { ui::VKEY_0, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, '\0', ')' }, + + { ui::VKEY_9, 0, '9', '9' }, + { ui::VKEY_9, ui::EF_SHIFT_DOWN, '(', '(' }, + { ui::VKEY_9, ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN, '(', '(' }, + { ui::VKEY_9, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, '\0', '(' }, + + { ui::VKEY_NUMPAD0, ui::EF_CONTROL_DOWN, '\0', '0' }, + { ui::VKEY_NUMPAD0, ui::EF_SHIFT_DOWN, '0', '0' }, + + { ui::VKEY_NUMPAD9, ui::EF_CONTROL_DOWN, '\0', '9' }, + { ui::VKEY_NUMPAD9, ui::EF_SHIFT_DOWN, '9', '9' }, + + { ui::VKEY_TAB, ui::EF_CONTROL_DOWN, '\0', '\t' }, + { ui::VKEY_TAB, ui::EF_SHIFT_DOWN, '\t', '\t' }, + + { ui::VKEY_MULTIPLY, ui::EF_CONTROL_DOWN, '\0', '*' }, + { ui::VKEY_MULTIPLY, ui::EF_SHIFT_DOWN, '*', '*' }, + { ui::VKEY_ADD, ui::EF_CONTROL_DOWN, '\0', '+' }, + { ui::VKEY_ADD, ui::EF_SHIFT_DOWN, '+', '+' }, + { ui::VKEY_SUBTRACT, ui::EF_CONTROL_DOWN, '\0', '-' }, + { ui::VKEY_SUBTRACT, ui::EF_SHIFT_DOWN, '-', '-' }, + { ui::VKEY_DECIMAL, ui::EF_CONTROL_DOWN, '\0', '.' }, + { ui::VKEY_DECIMAL, ui::EF_SHIFT_DOWN, '.', '.' }, + { ui::VKEY_DIVIDE, ui::EF_CONTROL_DOWN, '\0', '/' }, + { ui::VKEY_DIVIDE, ui::EF_SHIFT_DOWN, '/', '/' }, + + { ui::VKEY_OEM_1, ui::EF_CONTROL_DOWN, '\0', ';' }, + { ui::VKEY_OEM_1, ui::EF_SHIFT_DOWN, ':', ':' }, + { ui::VKEY_OEM_PLUS, ui::EF_CONTROL_DOWN, '\0', '=' }, + { ui::VKEY_OEM_PLUS, ui::EF_SHIFT_DOWN, '+', '+' }, + { ui::VKEY_OEM_COMMA, ui::EF_CONTROL_DOWN, '\0', ',' }, + { ui::VKEY_OEM_COMMA, ui::EF_SHIFT_DOWN, '<', '<' }, + { ui::VKEY_OEM_PERIOD, ui::EF_CONTROL_DOWN, '\0', '.' }, + { ui::VKEY_OEM_PERIOD, ui::EF_SHIFT_DOWN, '>', '>' }, + { ui::VKEY_OEM_3, ui::EF_CONTROL_DOWN, '\0', '`' }, + { ui::VKEY_OEM_3, ui::EF_SHIFT_DOWN, '~', '~' }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestData); ++i) { + KeyEvent key(ui::ET_KEY_PRESSED, kTestData[i].key_code, kTestData[i].flags); + EXPECT_EQ(kTestData[i].character, key.GetCharacter()) + << " Index:" << i << " key_code:" << kTestData[i].key_code; + EXPECT_EQ(kTestData[i].unmodified_character, key.GetUnmodifiedCharacter()) + << " Index:" << i << " key_code:" << kTestData[i].key_code; + } +} + +} // namespace views diff --git a/views/events/event_win.cc b/views/events/event_win.cc index 11f63ec..ad19185 100644 --- a/views/events/event_win.cc +++ b/views/events/event_win.cc @@ -217,6 +217,17 @@ KeyEvent::KeyEvent(NativeEvent2 native_event_2, FromNativeEvent2 from_native) NOTREACHED(); } +uint16 KeyEvent::GetCharacter() const { + return (native_event().message == WM_CHAR) ? key_code_ : + GetCharacterFromKeyCode(key_code_, flags()); +} + +uint16 KeyEvent::GetUnmodifiedCharacter() const { + // Looks like there is no way to get unmodified character on Windows. + return (native_event().message == WM_CHAR) ? key_code_ : + GetCharacterFromKeyCode(key_code_, flags() & ui::EF_SHIFT_DOWN); +} + //////////////////////////////////////////////////////////////////////////////// // MouseEvent, public: diff --git a/views/events/event_x.cc b/views/events/event_x.cc index 4526fd9..ebbcfc1 100644 --- a/views/events/event_x.cc +++ b/views/events/event_x.cc @@ -4,11 +4,14 @@ #include "views/events/event.h" +#include <gdk/gdk.h> #include <gdk/gdkx.h> #if defined(HAVE_XINPUT2) #include <X11/extensions/XInput2.h> #endif +#include <X11/Xlib.h> +#include "base/utf_string_conversions.h" #include "ui/base/keycodes/keyboard_code_conversion_x.h" #include "views/widget/root_view.h" #include "views/widget/widget_gtk.h" @@ -213,6 +216,16 @@ int GetLocatedEventFlags(XEvent* xev, bool touch) { return 0; } +uint16 GetCharacterFromXKeyEvent(XKeyEvent* key) { + char buf[6]; + int bytes_written = XLookupString(key, buf, 6, NULL, NULL); + DCHECK_LE(bytes_written, 6); + + string16 result; + return (bytes_written > 0 && UTF8ToUTF16(buf, bytes_written, &result) && + result.length() == 1) ? result[0] : 0; +} + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -248,6 +261,37 @@ KeyEvent::KeyEvent(NativeEvent2 native_event_2, FromNativeEvent2 from_native) key_code_(ui::KeyboardCodeFromXKeyEvent(native_event_2)) { } +uint16 KeyEvent::GetCharacter() const { + if (!native_event_2()) + return GetCharacterFromKeyCode(key_code_, flags()); + + DCHECK(native_event_2()->type == KeyPress || + native_event_2()->type == KeyRelease); + + uint16 ch = GetCharacterFromXKeyEvent(&native_event_2()->xkey); + return ch ? ch : GetCharacterFromKeyCode(key_code_, flags()); +} + +uint16 KeyEvent::GetUnmodifiedCharacter() const { + if (!native_event_2()) + return GetCharacterFromKeyCode(key_code_, flags() & ui::EF_SHIFT_DOWN); + + DCHECK(native_event_2()->type == KeyPress || + native_event_2()->type == KeyRelease); + + XKeyEvent key = native_event_2()->xkey; + + static const unsigned int kIgnoredModifiers = ControlMask | LockMask | + Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask; + + // We can't use things like (key.state & ShiftMask), as it may mask out bits + // used by X11 internally. + key.state &= ~kIgnoredModifiers; + uint16 ch = GetCharacterFromXKeyEvent(&key); + return ch ? ch : + GetCharacterFromKeyCode(key_code_, flags() & ui::EF_SHIFT_DOWN); +} + //////////////////////////////////////////////////////////////////////////////// // MouseEvent, public: diff --git a/views/views.gyp b/views/views.gyp index 4321af2..2ca2d25 100644 --- a/views/views.gyp +++ b/views/views.gyp @@ -481,6 +481,7 @@ 'controls/table/table_view_unittest.cc', 'controls/textfield/native_textfield_views_unittest.cc', 'controls/textfield/textfield_views_model_unittest.cc', + 'events/event_unittest.cc', 'focus/accelerator_handler_gtk_unittest.cc', 'focus/focus_manager_unittest.cc', 'layout/grid_layout_unittest.cc', |