15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/ime/character_composer.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include <X11/Xlib.h>
890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm>
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <iterator>
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/third_party/icu/icu_utf.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Note for Gtk removal: gdkkeysyms.h only contains a set of
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// '#define GDK_KeyName 0xNNNN' macros and does not #include any Gtk headers.
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/gtk+/gdk/gdkkeysyms.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/events/event_constants.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/glib/glib_integers.h"
1990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "ui/base/x/x11_util.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Note for Gtk removal: gtkimcontextsimpleseqs.h does not #include any Gtk
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// headers and only contains one big guint16 array |gtk_compose_seqs_compact|
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// which defines the main compose table. The table has internal linkage.
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The order of header inclusion is out of order because
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// gtkimcontextsimpleseqs.h depends on guint16, which is defined in
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// "ui/base/glib/glib_integers.h".
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "third_party/gtk+/gtk/gtkimcontextsimpleseqs.h"
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
317dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch// A black list for not composing dead keys. Once the key combination is listed
327dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch// below, the dead key won't work even when this is listed in
337dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch// gtkimcontextsimpleseqs.h. This only supports two keyevent sequenses.
347dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch// TODO(nona): Remove this hack.
357dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochconst struct BlackListedDeadKey {
367dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  uint32 first_key;  // target first key event.
377dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  uint32 second_key;  // target second key event.
387dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  uint32 output_char;  // the character to be inserted if the filter is matched.
397dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  bool consume;  // true if the original key event will be consumed.
407dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch} kBlackListedDeadKeys[] = {
41bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  { GDK_KEY_dead_acute, GDK_KEY_m, GDK_KEY_apostrophe, false },
427dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  { GDK_KEY_dead_acute, GDK_KEY_s, GDK_KEY_apostrophe, false },
437dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  { GDK_KEY_dead_acute, GDK_KEY_t, GDK_KEY_apostrophe, false },
44bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  { GDK_KEY_dead_acute, GDK_KEY_v, GDK_KEY_apostrophe, false },
457dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  { GDK_KEY_dead_acute, GDK_KEY_dead_acute, GDK_KEY_apostrophe, true },
467dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch};
477dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)typedef std::vector<unsigned int> ComposeBufferType;
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// An iterator class to apply std::lower_bound for composition table.
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class SequenceIterator
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : public std::iterator<std::random_access_iterator_tag, const uint16*> {
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SequenceIterator() : ptr_(NULL), stride_(0) {}
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SequenceIterator(const uint16* ptr, int stride)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : ptr_(ptr), stride_(stride) {}
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const uint16* ptr() const {return ptr_;}
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int stride() const {return stride_;}
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SequenceIterator& operator++() {
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ptr_ += stride_;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return *this;
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SequenceIterator& operator+=(int n) {
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ptr_ += stride_*n;
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return *this;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const uint16* operator*() const {return ptr_;}
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const uint16* ptr_;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int stride_;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)inline SequenceIterator operator+(const SequenceIterator& l, int r) {
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SequenceIterator(l) += r;
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)inline int operator-(const SequenceIterator& l, const SequenceIterator& r) {
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int d = l.ptr() - r.ptr();
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(l.stride() == r.stride() && l.stride() > 0 && d%l.stride() == 0);
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return d/l.stride();
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)inline bool operator==(const SequenceIterator& l, const SequenceIterator& r) {
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(l.stride() == r.stride());
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return l.ptr() == r.ptr();
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)inline bool operator!=(const SequenceIterator& l, const SequenceIterator& r) {
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return !(l == r);
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// A function to compare key value.
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)inline int CompareSequenceValue(unsigned int l, unsigned int r) {
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (l > r) ? 1 : ((l < r) ? -1 : 0);
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// A template to make |CompareFunc| work like operator<.
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// |CompareFunc| is required to implement a member function,
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// int operator()(const ComposeBufferType& l, const uint16* r) const.
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)template<typename CompareFunc>
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct ComparatorAdoptor {
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool operator()(const ComposeBufferType& l, const uint16* r) const {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return CompareFunc()(l, r) == -1;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool operator()(const uint16* l, const ComposeBufferType& r) const {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return CompareFunc()(r, l) == 1;
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ComposeChecker {
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This class does not take the ownership of |data|, |data| should be alive
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for the lifetime of the object.
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |data| is a pointer to the head of an array of
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // length (|max_sequence_length| + 2)*|n_sequences|.
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Every (|max_sequence_length| + 2) elements of |data| represent an entry.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // First |max_sequence_length| elements of an entry is the sequecne which
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // composes the character represented by the last two elements of the entry.
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ComposeChecker(const uint16* data, int max_sequence_length, int n_sequences);
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool CheckSequence(const ComposeBufferType& sequence,
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     uint32* composed_character) const;
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  struct CompareSequence {
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int operator()(const ComposeBufferType& l, const uint16* r) const;
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This class does not take the ownership of |data_|,
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the dtor does not delete |data_|.
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const uint16* data_;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int max_sequence_length_;
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int n_sequences_;
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int row_stride_;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(ComposeChecker);
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ComposeChecker::ComposeChecker(const uint16* data,
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               int max_sequence_length,
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               int n_sequences)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : data_(data),
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      max_sequence_length_(max_sequence_length),
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      n_sequences_(n_sequences),
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      row_stride_(max_sequence_length + 2) {
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ComposeChecker::CheckSequence(const ComposeBufferType& sequence,
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   uint32* composed_character) const {
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int sequence_length = sequence.size();
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (sequence_length > max_sequence_length_)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Find sequence in the table.
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const SequenceIterator begin(data_, row_stride_);
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const SequenceIterator end = begin + n_sequences_;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const SequenceIterator found = std::lower_bound(
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      begin, end, sequence, ComparatorAdoptor<CompareSequence>());
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (found == end || CompareSequence()(sequence, *found) != 0)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (sequence_length == max_sequence_length_ ||
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (*found)[sequence_length] == 0) {
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // |found| is not partially matching. It's fully matching.
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (found + 1 == end ||
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        CompareSequence()(sequence, *(found + 1)) != 0) {
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // There is no composition longer than |found| which matches to
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // |sequence|.
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const uint32 value = ((*found)[max_sequence_length_] << 16) |
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (*found)[max_sequence_length_ + 1];
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *composed_character = value;
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int ComposeChecker::CompareSequence::operator()(const ComposeBufferType& l,
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                const uint16* r) const {
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for(size_t i = 0; i < l.size(); ++i) {
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int compare_result = CompareSequenceValue(l[i], r[i]);
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if(compare_result)
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return compare_result;
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ComposeCheckerWithCompactTable {
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This class does not take the ownership of |data|, |data| should be alive
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for the lifetime of the object.
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // First |index_size|*|index_stride| elements of |data| are an index table.
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Every |index_stride| elements of an index table are an index entry.
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If you are checking with a sequence of length N beginning with character C,
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // you have to find an index entry whose first element is C, then get the N-th
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // element of the index entry as the index.
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The index is pointing the element of |data| where the composition table for
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // sequences of length N beginning with C is placed.
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ComposeCheckerWithCompactTable(const uint16* data,
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 int max_sequence_length,
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 int index_size,
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 int index_stride);
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool CheckSequence(const ComposeBufferType& sequence,
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     uint32* composed_character) const;
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  struct CompareSequenceFront {
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int operator()(const ComposeBufferType& l, const uint16* r) const;
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  struct CompareSequenceSkipFront {
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int operator()(const ComposeBufferType& l, const uint16* r) const;
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This class does not take the ownership of |data_|,
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the dtor does not delete |data_|.
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const uint16* data_;
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int max_sequence_length_;
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int index_size_;
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int index_stride_;
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ComposeCheckerWithCompactTable::ComposeCheckerWithCompactTable(
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const uint16* data,
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int max_sequence_length,
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int index_size,
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int index_stride)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : data_(data),
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      max_sequence_length_(max_sequence_length),
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      index_size_(index_size),
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      index_stride_(index_stride) {
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ComposeCheckerWithCompactTable::CheckSequence(
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const ComposeBufferType& sequence,
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uint32* composed_character) const {
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int compose_length = sequence.size();
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (compose_length > max_sequence_length_)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Find corresponding index for the first keypress.
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const SequenceIterator index_begin(data_, index_stride_);
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const SequenceIterator index_end = index_begin + index_size_;
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const SequenceIterator index =
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::lower_bound(index_begin, index_end, sequence,
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       ComparatorAdoptor<CompareSequenceFront>());
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (index == index_end || CompareSequenceFront()(sequence, *index) != 0)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (compose_length == 1)
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check for composition sequences.
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int length = compose_length - 1; length < max_sequence_length_;
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++length) {
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const uint16* table = data_ + (*index)[length];
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const uint16* table_next = data_ + (*index)[length + 1];
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (table_next > table) {
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // There are composition sequences for this |length|.
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const int row_stride = length + 1;
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const int n_sequences = (table_next - table)/row_stride;
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const SequenceIterator table_begin(table, row_stride);
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const SequenceIterator table_end = table_begin + n_sequences;
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const SequenceIterator found =
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          std::lower_bound(table_begin, table_end, sequence,
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           ComparatorAdoptor<CompareSequenceSkipFront>());
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (found != table_end &&
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          CompareSequenceSkipFront()(sequence, *found) == 0) {
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (length == compose_length - 1)  // Exact match.
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          *composed_character = (*found)[length];
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return true;
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int ComposeCheckerWithCompactTable::CompareSequenceFront::operator()(
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const ComposeBufferType& l, const uint16* r) const {
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return CompareSequenceValue(l[0], r[0]);
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int ComposeCheckerWithCompactTable::CompareSequenceSkipFront::operator()(
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const ComposeBufferType& l, const uint16* r) const {
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for(size_t i = 1; i < l.size(); ++i) {
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int compare_result = CompareSequenceValue(l[i], r[i - 1]);
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if(compare_result)
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return compare_result;
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0;
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Additional table.
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The difference between this and the default input method is the handling
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// of C+acute - this method produces C WITH CEDILLA rather than C WITH ACUTE.
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// For languages that use CCedilla and not acute, this is the preferred mapping,
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// and is particularly important for pt_BR, where the us-intl keyboard is
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// used extensively.
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const uint16 cedilla_compose_seqs[] = {
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // LATIN_CAPITAL_LETTER_C_WITH_CEDILLA
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GDK_KEY_dead_acute, GDK_KEY_C, 0, 0, 0, 0x00C7,
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // LATIN_SMALL_LETTER_C_WITH_CEDILLA
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GDK_KEY_dead_acute, GDK_KEY_c, 0, 0, 0, 0x00E7,
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // LATIN_CAPITAL_LETTER_C_WITH_CEDILLA
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GDK_KEY_Multi_key, GDK_KEY_apostrophe, GDK_KEY_C, 0, 0, 0x00C7,
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // LATIN_SMALL_LETTER_C_WITH_CEDILLA
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GDK_KEY_Multi_key, GDK_KEY_apostrophe, GDK_KEY_c, 0, 0, 0x00E7,
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // LATIN_CAPITAL_LETTER_C_WITH_CEDILLA
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GDK_KEY_Multi_key, GDK_KEY_C, GDK_KEY_apostrophe, 0, 0, 0x00C7,
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // LATIN_SMALL_LETTER_C_WITH_CEDILLA
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GDK_KEY_Multi_key, GDK_KEY_c, GDK_KEY_apostrophe, 0, 0, 0x00E7,
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool KeypressShouldBeIgnored(unsigned int keyval) {
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch(keyval) {
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GDK_KEY_Shift_L:
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GDK_KEY_Shift_R:
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GDK_KEY_Control_L:
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GDK_KEY_Control_R:
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GDK_KEY_Caps_Lock:
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GDK_KEY_Shift_Lock:
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GDK_KEY_Meta_L:
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GDK_KEY_Meta_R:
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GDK_KEY_Alt_L:
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GDK_KEY_Alt_R:
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GDK_KEY_Super_L:
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GDK_KEY_Super_R:
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GDK_KEY_Hyper_L:
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GDK_KEY_Hyper_R:
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GDK_KEY_Mode_switch:
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case GDK_KEY_ISO_Level3_Shift:
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool CheckCharacterComposeTable(const ComposeBufferType& sequence,
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                uint32* composed_character) {
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check cedilla compose table.
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const ComposeChecker kCedillaComposeChecker(
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cedilla_compose_seqs, 4, arraysize(cedilla_compose_seqs)/(4 + 2));
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (kCedillaComposeChecker.CheckSequence(sequence, composed_character))
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check main compose table.
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const ComposeCheckerWithCompactTable kMainComposeChecker(
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gtk_compose_seqs_compact, 5, 24, 6);
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (kMainComposeChecker.CheckSequence(sequence, composed_character))
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Converts |character| to UTF16 string.
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns false when |character| is not a valid character.
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool UTF32CharacterToUTF16(uint32 character, string16* output) {
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  output->clear();
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Reject invalid character. (e.g. codepoint greater than 0x10ffff)
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!CBU_IS_UNICODE_CHAR(character))
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (character) {
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    output->resize(CBU16_LENGTH(character));
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t i = 0;
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CBU16_APPEND_UNSAFE(&(*output)[0], i, character);
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
37290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Converts a X keycode to a X keysym with no modifiers.
37390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)KeySym XKeyCodeToXKeySym(unsigned int keycode) {
37490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  Display* display = ui::GetXDisplay();
37590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (!display)
37690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return NoSymbol;
37790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
37890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  XKeyEvent x_key_event = {0};
37990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  x_key_event.type = KeyPress;
38090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  x_key_event.display = display;
38190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  x_key_event.keycode = keycode;
38290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  return ::XLookupKeysym(&x_key_event, 0);
38390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
38490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns an hexadecimal digit integer (0 to 15) corresponding to |keyval|.
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// -1 is returned when |keyval| cannot be a hexadecimal digit.
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int KeyvalToHexDigit(unsigned int keyval) {
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (GDK_KEY_0 <= keyval && keyval <= GDK_KEY_9)
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return keyval - GDK_KEY_0;
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (GDK_KEY_a <= keyval && keyval <= GDK_KEY_f)
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return keyval - GDK_KEY_a + 10;
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (GDK_KEY_A <= keyval && keyval <= GDK_KEY_F)
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return keyval - GDK_KEY_A + 10;
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return -1;  // |keyval| cannot be a hexadecimal digit.
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace ui {
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CharacterComposer::CharacterComposer() : composition_mode_(KEY_SEQUENCE_MODE) {}
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CharacterComposer::~CharacterComposer() {}
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CharacterComposer::Reset() {
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  compose_buffer_.clear();
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  composed_character_.clear();
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  preedit_string_.clear();
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  composition_mode_ = KEY_SEQUENCE_MODE;
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool CharacterComposer::FilterKeyPress(unsigned int keyval,
41390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                       unsigned int keycode,
41490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                       int flags) {
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  composed_character_.clear();
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  preedit_string_.clear();
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We don't care about modifier key presses.
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if(KeypressShouldBeIgnored(keyval))
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // When the user presses Ctrl+Shift+U, maybe switch to HEX_MODE.
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We don't care about other modifiers like Alt.  When CapsLock is down, we
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // do nothing because what we receive is Ctrl+Shift+u (not U).
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (keyval == GDK_KEY_U && (flags & EF_SHIFT_DOWN) &&
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (flags & EF_CONTROL_DOWN)) {
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (composition_mode_ == KEY_SEQUENCE_MODE && compose_buffer_.empty()) {
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // There is no ongoing composition.  Let's switch to HEX_MODE.
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      composition_mode_ = HEX_MODE;
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      UpdatePreeditStringHexMode();
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Filter key press in an appropriate manner.
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (composition_mode_) {
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case KEY_SEQUENCE_MODE:
43890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      return FilterKeyPressSequenceMode(keyval, keycode, flags);
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case HEX_MODE:
44090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      return FilterKeyPressHexMode(keyval, keycode, flags);
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool CharacterComposer::FilterKeyPressSequenceMode(unsigned int keyval,
44890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                                   unsigned int keycode,
44990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                                   int flags) {
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(composition_mode_ == KEY_SEQUENCE_MODE);
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  compose_buffer_.push_back(keyval);
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4537dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (compose_buffer_.size() == 2U) {
4547dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kBlackListedDeadKeys); ++i) {
4557dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      if (compose_buffer_[0] == kBlackListedDeadKeys[i].first_key &&
4567dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          compose_buffer_[1] == kBlackListedDeadKeys[i].second_key ) {
4577dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        Reset();
4587dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        composed_character_.push_back(kBlackListedDeadKeys[i].output_char);
4597dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        return kBlackListedDeadKeys[i].consume;
4607dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      }
4617dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    }
4627dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  }
4637dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check compose table.
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uint32 composed_character_utf32 = 0;
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (CheckCharacterComposeTable(compose_buffer_, &composed_character_utf32)) {
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Key press is recognized as a part of composition.
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (composed_character_utf32 != 0) {
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We get a composed character.
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      compose_buffer_.clear();
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      UTF32CharacterToUTF16(composed_character_utf32, &composed_character_);
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Key press is not a part of composition.
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  compose_buffer_.pop_back();  // Remove the keypress added this time.
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!compose_buffer_.empty()) {
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    compose_buffer_.clear();
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool CharacterComposer::FilterKeyPressHexMode(unsigned int keyval,
48590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                              unsigned int keycode,
48690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                              int flags) {
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(composition_mode_ == HEX_MODE);
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t kMaxHexSequenceLength = 8;
48990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  int hex_digit = KeyvalToHexDigit(keyval);
49090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (hex_digit < 0) {
49190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    // With 101 keyboard, control + shift + 3 produces '#', but a user may
49290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    // have intended to type '3'.  So, if a hexadecimal character was not found,
49390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    // suppose a user is holding shift key (and possibly control key, too) and
49490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    // try a character with modifier keys removed.
49590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    hex_digit = KeyvalToHexDigit(XKeyCodeToXKeySym(keycode));
49690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (keyval == GDK_KEY_Escape) {
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Cancel composition when ESC is pressed.
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Reset();
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (keyval == GDK_KEY_Return || keyval == GDK_KEY_KP_Enter ||
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             keyval == GDK_KEY_ISO_Enter ||
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             keyval == GDK_KEY_space || keyval == GDK_KEY_KP_Space) {
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Commit the composed character when Enter or space is pressed.
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CommitHex();
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (keyval == GDK_KEY_BackSpace) {
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Pop back the buffer when Backspace is pressed.
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!compose_buffer_.empty()) {
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      compose_buffer_.pop_back();
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If there is no character in |compose_buffer_|, cancel composition.
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Reset();
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (hex_digit >= 0 &&
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             compose_buffer_.size() < kMaxHexSequenceLength) {
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Add the key to the buffer if it is a hex digit.
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    compose_buffer_.push_back(hex_digit);
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdatePreeditStringHexMode();
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CharacterComposer::CommitHex() {
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(composition_mode_ == HEX_MODE);
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uint32 composed_character_utf32 = 0;
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i != compose_buffer_.size(); ++i) {
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const uint32 digit = compose_buffer_[i];
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(0 <= digit && digit < 16);
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    composed_character_utf32 <<= 4;
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    composed_character_utf32 |= digit;
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Reset();
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UTF32CharacterToUTF16(composed_character_utf32, &composed_character_);
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CharacterComposer::UpdatePreeditStringHexMode() {
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (composition_mode_ != HEX_MODE) {
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    preedit_string_.clear();
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string preedit_string_ascii("u");
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i != compose_buffer_.size(); ++i) {
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int digit = compose_buffer_[i];
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(0 <= digit && digit < 16);
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    preedit_string_ascii += digit <= 9 ? ('0' + digit) : ('a' + (digit - 10));
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  preedit_string_ = ASCIIToUTF16(preedit_string_ascii);
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace ui
553