OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/base/ime/character_composer.h" | 5 #include "ui/base/ime/character_composer.h" |
6 | 6 |
| 7 #include <X11/Xlib.h> |
| 8 |
7 #include <algorithm> | 9 #include <algorithm> |
8 #include <iterator> | 10 #include <iterator> |
9 | 11 |
10 #include "base/third_party/icu/icu_utf.h" | 12 #include "base/third_party/icu/icu_utf.h" |
11 #include "base/utf_string_conversions.h" | 13 #include "base/utf_string_conversions.h" |
12 // Note for Gtk removal: gdkkeysyms.h only contains a set of | 14 // Note for Gtk removal: gdkkeysyms.h only contains a set of |
13 // '#define GDK_KeyName 0xNNNN' macros and does not #include any Gtk headers. | 15 // '#define GDK_KeyName 0xNNNN' macros and does not #include any Gtk headers. |
14 #include "third_party/gtk+/gdk/gdkkeysyms.h" | 16 #include "third_party/gtk+/gdk/gdkkeysyms.h" |
15 #include "ui/base/events/event_constants.h" | 17 #include "ui/base/events/event_constants.h" |
16 #include "ui/base/glib/glib_integers.h" | 18 #include "ui/base/glib/glib_integers.h" |
| 19 #include "ui/base/x/x11_util.h" |
17 | 20 |
18 // Note for Gtk removal: gtkimcontextsimpleseqs.h does not #include any Gtk | 21 // Note for Gtk removal: gtkimcontextsimpleseqs.h does not #include any Gtk |
19 // headers and only contains one big guint16 array |gtk_compose_seqs_compact| | 22 // headers and only contains one big guint16 array |gtk_compose_seqs_compact| |
20 // which defines the main compose table. The table has internal linkage. | 23 // which defines the main compose table. The table has internal linkage. |
21 // The order of header inclusion is out of order because | 24 // The order of header inclusion is out of order because |
22 // gtkimcontextsimpleseqs.h depends on guint16, which is defined in | 25 // gtkimcontextsimpleseqs.h depends on guint16, which is defined in |
23 // "ui/base/glib/glib_integers.h". | 26 // "ui/base/glib/glib_integers.h". |
24 #include "third_party/gtk+/gtk/gtkimcontextsimpleseqs.h" | 27 #include "third_party/gtk+/gtk/gtkimcontextsimpleseqs.h" |
25 | 28 |
26 namespace { | 29 namespace { |
(...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
342 if (!CBU_IS_UNICODE_CHAR(character)) | 345 if (!CBU_IS_UNICODE_CHAR(character)) |
343 return false; | 346 return false; |
344 if (character) { | 347 if (character) { |
345 output->resize(CBU16_LENGTH(character)); | 348 output->resize(CBU16_LENGTH(character)); |
346 size_t i = 0; | 349 size_t i = 0; |
347 CBU16_APPEND_UNSAFE(&(*output)[0], i, character); | 350 CBU16_APPEND_UNSAFE(&(*output)[0], i, character); |
348 } | 351 } |
349 return true; | 352 return true; |
350 } | 353 } |
351 | 354 |
| 355 // Converts a X keycode to a X keysym with no modifiers. |
| 356 KeySym XKeyCodeToXKeySym(unsigned int keycode) { |
| 357 Display* display = ui::GetXDisplay(); |
| 358 if (!display) |
| 359 return NoSymbol; |
| 360 |
| 361 XKeyEvent x_key_event = {0}; |
| 362 x_key_event.type = KeyPress; |
| 363 x_key_event.display = display; |
| 364 x_key_event.keycode = keycode; |
| 365 return ::XLookupKeysym(&x_key_event, 0); |
| 366 } |
| 367 |
352 // Returns an hexadecimal digit integer (0 to 15) corresponding to |keyval|. | 368 // Returns an hexadecimal digit integer (0 to 15) corresponding to |keyval|. |
353 // -1 is returned when |keyval| cannot be a hexadecimal digit. | 369 // -1 is returned when |keyval| cannot be a hexadecimal digit. |
354 int KeyvalToHexDigit(unsigned int keyval) { | 370 int KeyvalToHexDigit(unsigned int keyval) { |
355 if (GDK_KEY_0 <= keyval && keyval <= GDK_KEY_9) | 371 if (GDK_KEY_0 <= keyval && keyval <= GDK_KEY_9) |
356 return keyval - GDK_KEY_0; | 372 return keyval - GDK_KEY_0; |
357 if (GDK_KEY_a <= keyval && keyval <= GDK_KEY_f) | 373 if (GDK_KEY_a <= keyval && keyval <= GDK_KEY_f) |
358 return keyval - GDK_KEY_a + 10; | 374 return keyval - GDK_KEY_a + 10; |
359 if (GDK_KEY_A <= keyval && keyval <= GDK_KEY_F) | 375 if (GDK_KEY_A <= keyval && keyval <= GDK_KEY_F) |
360 return keyval - GDK_KEY_A + 10; | 376 return keyval - GDK_KEY_A + 10; |
361 return -1; // |keyval| cannot be a hexadecimal digit. | 377 return -1; // |keyval| cannot be a hexadecimal digit. |
362 } | 378 } |
363 | 379 |
364 } // namespace | 380 } // namespace |
365 | 381 |
366 namespace ui { | 382 namespace ui { |
367 | 383 |
368 CharacterComposer::CharacterComposer() : composition_mode_(KEY_SEQUENCE_MODE) {} | 384 CharacterComposer::CharacterComposer() : composition_mode_(KEY_SEQUENCE_MODE) {} |
369 | 385 |
370 CharacterComposer::~CharacterComposer() {} | 386 CharacterComposer::~CharacterComposer() {} |
371 | 387 |
372 void CharacterComposer::Reset() { | 388 void CharacterComposer::Reset() { |
373 compose_buffer_.clear(); | 389 compose_buffer_.clear(); |
374 composed_character_.clear(); | 390 composed_character_.clear(); |
375 preedit_string_.clear(); | 391 preedit_string_.clear(); |
376 composition_mode_ = KEY_SEQUENCE_MODE; | 392 composition_mode_ = KEY_SEQUENCE_MODE; |
377 } | 393 } |
378 | 394 |
379 bool CharacterComposer::FilterKeyPress(unsigned int keyval, | 395 bool CharacterComposer::FilterKeyPress(unsigned int keyval, |
380 unsigned int flags) { | 396 unsigned int keycode, |
| 397 int flags) { |
381 composed_character_.clear(); | 398 composed_character_.clear(); |
382 preedit_string_.clear(); | 399 preedit_string_.clear(); |
383 | 400 |
384 // We don't care about modifier key presses. | 401 // We don't care about modifier key presses. |
385 if(KeypressShouldBeIgnored(keyval)) | 402 if(KeypressShouldBeIgnored(keyval)) |
386 return false; | 403 return false; |
387 | 404 |
388 // When the user presses Ctrl+Shift+U, maybe switch to HEX_MODE. | 405 // When the user presses Ctrl+Shift+U, maybe switch to HEX_MODE. |
389 // We don't care about other modifiers like Alt. When CapsLock is down, we | 406 // We don't care about other modifiers like Alt. When CapsLock is down, we |
390 // do nothing because what we receive is Ctrl+Shift+u (not U). | 407 // do nothing because what we receive is Ctrl+Shift+u (not U). |
391 if (keyval == GDK_KEY_U && (flags & EF_SHIFT_DOWN) && | 408 if (keyval == GDK_KEY_U && (flags & EF_SHIFT_DOWN) && |
392 (flags & EF_CONTROL_DOWN)) { | 409 (flags & EF_CONTROL_DOWN)) { |
393 if (composition_mode_ == KEY_SEQUENCE_MODE && compose_buffer_.empty()) { | 410 if (composition_mode_ == KEY_SEQUENCE_MODE && compose_buffer_.empty()) { |
394 // There is no ongoing composition. Let's switch to HEX_MODE. | 411 // There is no ongoing composition. Let's switch to HEX_MODE. |
395 composition_mode_ = HEX_MODE; | 412 composition_mode_ = HEX_MODE; |
396 UpdatePreeditStringHexMode(); | 413 UpdatePreeditStringHexMode(); |
397 return true; | 414 return true; |
398 } | 415 } |
399 } | 416 } |
400 | 417 |
401 // Filter key press in an appropriate manner. | 418 // Filter key press in an appropriate manner. |
402 switch (composition_mode_) { | 419 switch (composition_mode_) { |
403 case KEY_SEQUENCE_MODE: | 420 case KEY_SEQUENCE_MODE: |
404 return FilterKeyPressSequenceMode(keyval, flags); | 421 return FilterKeyPressSequenceMode(keyval, keycode, flags); |
405 case HEX_MODE: | 422 case HEX_MODE: |
406 return FilterKeyPressHexMode(keyval, flags); | 423 return FilterKeyPressHexMode(keyval, keycode, flags); |
407 default: | 424 default: |
408 NOTREACHED(); | 425 NOTREACHED(); |
409 return false; | 426 return false; |
410 } | 427 } |
411 } | 428 } |
412 | 429 |
413 bool CharacterComposer::FilterKeyPressSequenceMode(unsigned int keyval, | 430 bool CharacterComposer::FilterKeyPressSequenceMode(unsigned int keyval, |
414 unsigned int flags) { | 431 unsigned int keycode, |
| 432 int flags) { |
415 DCHECK(composition_mode_ == KEY_SEQUENCE_MODE); | 433 DCHECK(composition_mode_ == KEY_SEQUENCE_MODE); |
416 compose_buffer_.push_back(keyval); | 434 compose_buffer_.push_back(keyval); |
417 | 435 |
418 // Check compose table. | 436 // Check compose table. |
419 uint32 composed_character_utf32 = 0; | 437 uint32 composed_character_utf32 = 0; |
420 if (CheckCharacterComposeTable(compose_buffer_, &composed_character_utf32)) { | 438 if (CheckCharacterComposeTable(compose_buffer_, &composed_character_utf32)) { |
421 // Key press is recognized as a part of composition. | 439 // Key press is recognized as a part of composition. |
422 if (composed_character_utf32 != 0) { | 440 if (composed_character_utf32 != 0) { |
423 // We get a composed character. | 441 // We get a composed character. |
424 compose_buffer_.clear(); | 442 compose_buffer_.clear(); |
425 UTF32CharacterToUTF16(composed_character_utf32, &composed_character_); | 443 UTF32CharacterToUTF16(composed_character_utf32, &composed_character_); |
426 } | 444 } |
427 return true; | 445 return true; |
428 } | 446 } |
429 // Key press is not a part of composition. | 447 // Key press is not a part of composition. |
430 compose_buffer_.pop_back(); // Remove the keypress added this time. | 448 compose_buffer_.pop_back(); // Remove the keypress added this time. |
431 if (!compose_buffer_.empty()) { | 449 if (!compose_buffer_.empty()) { |
432 compose_buffer_.clear(); | 450 compose_buffer_.clear(); |
433 return true; | 451 return true; |
434 } | 452 } |
435 return false; | 453 return false; |
436 } | 454 } |
437 | 455 |
438 bool CharacterComposer::FilterKeyPressHexMode(unsigned int keyval, | 456 bool CharacterComposer::FilterKeyPressHexMode(unsigned int keyval, |
439 unsigned int flags) { | 457 unsigned int keycode, |
| 458 int flags) { |
440 DCHECK(composition_mode_ == HEX_MODE); | 459 DCHECK(composition_mode_ == HEX_MODE); |
441 const size_t kMaxHexSequenceLength = 8; | 460 const size_t kMaxHexSequenceLength = 8; |
442 const int hex_digit = KeyvalToHexDigit(keyval); | 461 int hex_digit = KeyvalToHexDigit(keyval); |
| 462 if (hex_digit < 0) { |
| 463 // With 101 keyboard, control + shift + 3 produces '#', but a user may |
| 464 // have intended to type '3'. So, if a hexadecimal character was not found, |
| 465 // suppose a user is holding shift key (and possibly control key, too) and |
| 466 // try a character with modifier keys removed. |
| 467 hex_digit = KeyvalToHexDigit(XKeyCodeToXKeySym(keycode)); |
| 468 } |
443 | 469 |
444 if (keyval == GDK_KEY_Escape) { | 470 if (keyval == GDK_KEY_Escape) { |
445 // Cancel composition when ESC is pressed. | 471 // Cancel composition when ESC is pressed. |
446 Reset(); | 472 Reset(); |
447 } else if (keyval == GDK_KEY_Return || keyval == GDK_KEY_KP_Enter || | 473 } else if (keyval == GDK_KEY_Return || keyval == GDK_KEY_KP_Enter || |
448 keyval == GDK_KEY_ISO_Enter || | 474 keyval == GDK_KEY_ISO_Enter || |
449 keyval == GDK_KEY_space || keyval == GDK_KEY_KP_Space) { | 475 keyval == GDK_KEY_space || keyval == GDK_KEY_KP_Space) { |
450 // Commit the composed character when Enter or space is pressed. | 476 // Commit the composed character when Enter or space is pressed. |
451 CommitHex(); | 477 CommitHex(); |
452 } else if (keyval == GDK_KEY_BackSpace) { | 478 } else if (keyval == GDK_KEY_BackSpace) { |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
489 std::string preedit_string_ascii("u"); | 515 std::string preedit_string_ascii("u"); |
490 for (size_t i = 0; i != compose_buffer_.size(); ++i) { | 516 for (size_t i = 0; i != compose_buffer_.size(); ++i) { |
491 const int digit = compose_buffer_[i]; | 517 const int digit = compose_buffer_[i]; |
492 DCHECK(0 <= digit && digit < 16); | 518 DCHECK(0 <= digit && digit < 16); |
493 preedit_string_ascii += digit <= 9 ? ('0' + digit) : ('a' + (digit - 10)); | 519 preedit_string_ascii += digit <= 9 ? ('0' + digit) : ('a' + (digit - 10)); |
494 } | 520 } |
495 preedit_string_ = ASCIIToUTF16(preedit_string_ascii); | 521 preedit_string_ = ASCIIToUTF16(preedit_string_ascii); |
496 } | 522 } |
497 | 523 |
498 } // namespace ui | 524 } // namespace ui |
OLD | NEW |