Introducing QComposeInputContext

When switching from Xlib to xcb platform plugin it was agreed that
XIM is deprecated. Users should be using QT_IM_MODULE to load input context
plugin for a more advance input method framework support.

The proposed solution is to parse the compose file directly from Qt. This
approach removes the overhead of communication protocols used in Xlib and/or IBUS.

TableGenerator class follows [1].

The compose file is searched for in the following order:

1) If the environment variable $XCOMPOSEFILE is set,
its value is used as the name of the Compose file.

2) If the user’s home directory has a file named
.XCompose, it is used as the Compose file.

3) The system provided compose file is used by mapping the
locale to a compose file from the list in
/usr/share/X11/locale/compose.dir.

Regarding step 3 - TableGenerator searches in hard-coded locations for
system-provided compose files. Here I have introcuded a new environment
variable QTCOMPOSE which can be used to prepend an extra location
to be searched.

[1] http://www.x.org/archive/X11R7.7/doc/man/man5/Compose.5.xhtml

Task-number: QTBUG-28183
Change-Id: I76dcfd454f3acc23db98192a3673c1ab2af4425f
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
This commit is contained in:
Gatis Paeglis 2013-03-01 12:55:42 +01:00 committed by The Qt Project
parent fb9cff6e88
commit 24c10b0b8d
10 changed files with 1089 additions and 2 deletions

View File

@ -82,8 +82,8 @@ QPlatformInputContext *QPlatformInputContextFactory::create()
QString icString = QString::fromLatin1(qgetenv("QT_IM_MODULE"));
if (icString == QStringLiteral("none"))
return 0;
if (icString == QLatin1String("none"))
icString = QStringLiteral("compose");
ic = create(icString);
if (ic && ic->isValid())

View File

@ -0,0 +1,3 @@
{
"Keys": [ "compose" ]
}

View File

@ -0,0 +1,20 @@
TARGET = composeplatforminputcontextplugin
PLUGIN_TYPE = platforminputcontexts
PLUGIN_CLASS_NAME = QComposePlatformInputContextPlugin
load(qt_plugin)
QT += gui-private
LIBS += $$QMAKE_LIBS_XKBCOMMON
QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XKBCOMMON
SOURCES += $$PWD/main.cpp \
$$PWD/qcomposeplatforminputcontext.cpp \
$$PWD/generator/qtablegenerator.cpp \
HEADERS += $$PWD/qcomposeplatforminputcontext.h \
$$PWD/generator/qtablegenerator.h \
$$PWD/xkbcommon_workaround.h \
OTHER_FILES += $$PWD/compose.json

View File

@ -0,0 +1,402 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qtablegenerator.h"
#include <QtCore/QRegularExpression>
#include <QtCore/QByteArray>
#include <QtCore/QTextCodec>
#include <QtCore/QDebug>
#include <QtCore/QStringList>
#include <QtCore/QString>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon_workaround.h>
#include <X11/keysym.h>
//#define DEBUG_GENERATOR
TableGenerator::TableGenerator() : m_state(NoErrors),
m_systemComposeDir(QString())
{
initPossibleLocations();
findComposeFile();
orderComposeTable();
#ifdef DEBUG_GENERATOR
printComposeTable();
#endif
}
void TableGenerator::initPossibleLocations()
{
// AFAICT there is no way to know the exact location
// of the compose files. It depends on how Xlib was configured
// on a specific platform. During the "./configure" process
// xlib generates a config.h file which contains a bunch of defines,
// including XLOCALEDIR which points to the location of the compose file dir.
// To add an extra system path use the QTCOMPOSE environment variable
if (qEnvironmentVariableIsSet("QTCOMPOSE")) {
m_possibleLocations.append(QString(qgetenv("QTCOMPOSE")));
}
m_possibleLocations.append(QStringLiteral("/usr/share/X11/locale"));
m_possibleLocations.append(QStringLiteral("/usr/lib/X11/locale"));
}
void TableGenerator::findComposeFile()
{
bool found = false;
// check if XCOMPOSEFILE points to a Compose file
if (qEnvironmentVariableIsSet("XCOMPOSEFILE")) {
QString composeFile(qgetenv("XCOMPOSEFILE"));
if (composeFile.endsWith(QLatin1String("Compose")))
found = processFile(composeFile);
else
qWarning("Qt Warning: XCOMPOSEFILE doesn't point to a valid Compose file");
#ifdef DEBUG_GENERATOR
if (found)
qDebug() << "Using Compose file from: " << composeFile;
#endif
}
// check if users home directory has a file named .XCompose
if (!found && cleanState()) {
QString composeFile = qgetenv("HOME") + QStringLiteral("/.XCompose");
if (QFile(composeFile).exists())
found = processFile(composeFile);
#ifdef DEBUG_GENERATOR
if (found)
qDebug() << "Using Compose file from: " << composeFile;
#endif
}
// check for the system provided compose files
if (!found && cleanState()) {
readLocaleMappings();
if (cleanState()) {
QString table = m_localeToTable.value(locale().toUpper());
if (table.isEmpty())
// no table mappings for the system's locale in the compose.dir
m_state = UnsupportedLocale;
else
found = processFile(systemComposeDir() + QLatin1String("/") + table);
#ifdef DEBUG_GENERATOR
if (found)
qDebug() << "Using Compose file from: " <<
systemComposeDir() + QLatin1String("/") + table;
#endif
}
}
if (found && m_composeTable.isEmpty())
m_state = EmptyTable;
if (!found)
m_state = MissingComposeFile;
}
bool TableGenerator::findSystemComposeDir()
{
bool found = false;
for (int i = 0; i < m_possibleLocations.size(); ++i) {
QString path = m_possibleLocations.at(i);
if (QFile(path + QLatin1String("/compose.dir")).exists()) {
m_systemComposeDir = path;
found = true;
break;
}
}
if (!found) {
// should we ask to report this in the qt bug tracker?
m_state = UnknownSystemComposeDir;
qWarning("Qt Warning: Could not find a location of the system's Compose files. "
"Consider setting the QTCOMPOSE environment variable.");
}
return found;
}
QString TableGenerator::systemComposeDir()
{
if (m_systemComposeDir.isNull()
&& !findSystemComposeDir()) {
return QLatin1String("$QTCOMPOSE");
}
return m_systemComposeDir;
}
QString TableGenerator::locale() const
{
char *name = setlocale(LC_CTYPE, (char *)0);
return QLatin1String(name);
}
void TableGenerator::readLocaleMappings()
{
QFile mappings(systemComposeDir() + QLatin1String("/compose.dir"));
if (mappings.exists()) {
mappings.open(QIODevice::ReadOnly);
QTextStream in(&mappings);
// formating of compose.dir has some inconsistencies
while (!in.atEnd()) {
QString line = in.readLine();
if (!line.startsWith("#") && line.size() != 0 &&
line.at(0).isLower()) {
QStringList pair = line.split(QRegExp(QLatin1String("\\s+")));
QString table = pair.at(0);
if (table.endsWith(QLatin1String(":")))
table.remove(table.size() - 1, 1);
m_localeToTable.insert(pair.at(1).toUpper(), table);
}
}
mappings.close();
}
}
bool TableGenerator::processFile(QString composeFileName)
{
QFile composeFile(composeFileName);
if (composeFile.exists()) {
composeFile.open(QIODevice::ReadOnly);
parseComposeFile(&composeFile);
return true;
}
qWarning() << QString(QLatin1String("Qt Warning: Compose file: \"%1\" can't be found"))
.arg(composeFile.fileName());
return false;
}
TableGenerator::~TableGenerator()
{
}
QList<QComposeTableElement> TableGenerator::composeTable() const
{
return m_composeTable;
}
void TableGenerator::parseComposeFile(QFile *composeFile)
{
#ifdef DEBUG_GENERATOR
qDebug() << "TableGenerator::parseComposeFile: " << composeFile->fileName();
#endif
QTextStream in(composeFile);
while (!in.atEnd()) {
QString line = in.readLine();
if (line.startsWith(QLatin1String("<"))) {
parseKeySequence(line);
} else if (line.startsWith(QLatin1String("include"))) {
parseIncludeInstruction(line);
}
}
composeFile->close();
}
void TableGenerator::parseIncludeInstruction(QString line)
{
// Parse something that looks like:
// include "/usr/share/X11/locale/en_US.UTF-8/Compose"
QString quote = QStringLiteral("\"");
line.remove(0, line.indexOf(quote) + 1);
line.chop(line.length() - line.indexOf(quote));
// expand substitutions if present
line.replace(QLatin1String("%H"), QString(qgetenv("HOME")));
line.replace(QLatin1String("%L"), locale());
line.replace(QLatin1String("%S"), systemComposeDir());
processFile(line);
}
ushort TableGenerator::keysymToUtf8(uint32_t sym)
{
QByteArray chars;
int bytes;
chars.resize(8);
if (needWorkaround(sym)) {
uint32_t codepoint;
if (sym == XKB_KEY_KP_Space)
codepoint = XKB_KEY_space & 0x7f;
else
codepoint = sym & 0x7f;
bytes = utf32_to_utf8(codepoint, chars.data());
} else {
bytes = xkb_keysym_to_utf8(sym, chars.data(), chars.size());
}
if (bytes == -1)
qWarning("TableGenerator::keysymToUtf8 - buffer too small");
chars.resize(bytes-1);
#ifdef DEBUG_GENERATOR
QTextCodec *codec = QTextCodec::codecForLocale();
qDebug() << QString("keysym - 0x%1 : utf8 - %2").arg(QString::number(sym, 16))
.arg(codec->toUnicode(chars));
#endif
const QChar *ch = QString(chars.data()).unicode();
return ch->unicode();
}
uint32_t TableGenerator::stringToKeysym(QString keysymName)
{
uint32_t keysym;
const char *name = keysymName.toLatin1().constData();
if ((keysym = xkb_keysym_from_name(name, (xkb_keysym_flags)0)) == XKB_KEY_NoSymbol)
qWarning() << QString("Qt Warrning - invalid keysym: %1").arg(keysymName);
return keysym;
}
void TableGenerator::parseKeySequence(QString line)
{
// we are interested in the lines with the following format:
// <Multi_key> <numbersign> <S> : "♬" U266c # BEAMED SIXTEENTH NOTE
int keysEnd = line.indexOf(QLatin1String(":"));
QString keys = line.left(keysEnd).trimmed();
// find the key sequence
QString regexp = QStringLiteral("<[^>]+>");
QRegularExpression reg(regexp);
QRegularExpressionMatchIterator i = reg.globalMatch(keys);
QStringList keyList;
while (i.hasNext()) {
QRegularExpressionMatch match = i.next();
QString word = match.captured(0);
keyList << word;
}
QComposeTableElement elem;
QString quote = QStringLiteral("\"");
// find the composed value - strings may be direct text encoded in the locale
// for which the compose file is to be used, or an escaped octal or hexadecimal
// character code. Octal codes are specified as "\123" and hexadecimal codes as "\0x123a".
int composeValueIndex = line.indexOf(quote, keysEnd) + 1;
const QChar valueType(line.at(composeValueIndex));
if (valueType == '\\' && line.at(composeValueIndex + 1).isDigit()) {
// handle octal and hex code values
QChar detectBase(line.at(composeValueIndex + 2));
QString codeValue = line.mid(composeValueIndex + 1, line.lastIndexOf(quote) - composeValueIndex - 1);
if (detectBase == 'x') {
// hexadecimal character code
elem.value = keysymToUtf8(codeValue.toUInt(0, 16));
} else {
// octal character code
QString hexStr = QString::number(codeValue.toUInt(0, 8), 16);
elem.value = keysymToUtf8(hexStr.toUInt(0, 16));
}
} else {
// handle direct text encoded in the locale
elem.value = valueType.unicode();
}
// find the comment
int commnetIndex = line.lastIndexOf(quote) + 1;
elem.comment = line.mid(commnetIndex).trimmed();
// Convert to X11 keysym
int count = keyList.length();
for (int i = 0; i < QT_KEYSEQUENCE_MAX_LEN; i++) {
if (i < count) {
QString keysym = keyList.at(i);
keysym.remove(keysym.length() - 1, 1);
keysym.remove(0, 1);
if (keysym == QLatin1String("dead_inverted_breve"))
keysym = QStringLiteral("dead_invertedbreve");
else if (keysym == QLatin1String("dead_double_grave"))
keysym = QStringLiteral("dead_doublegrave");
elem.keys[i] = stringToKeysym(keysym);
} else {
elem.keys[i] = 0;
}
}
m_composeTable.append(elem);
}
void TableGenerator::printComposeTable() const
{
if (composeTable().isEmpty())
return;
QString output;
QComposeTableElement elem;
QString comma = QStringLiteral(",");
int tableSize = m_composeTable.size();
for (int i = 0; i < tableSize; ++i) {
elem = m_composeTable.at(i);
output.append(QLatin1String("{ {"));
for (int j = 0; j < QT_KEYSEQUENCE_MAX_LEN; j++) {
output.append(QString(QLatin1String("0x%1, ")).arg(QString::number(elem.keys[j],16)));
}
// take care of the trailing comma
if (i == tableSize - 1)
comma = QStringLiteral("");
output.append(QString(QLatin1String("}, 0x%1, \"\" }%2 // %3 \n"))
.arg(QString::number(elem.value,16))
.arg(comma)
.arg(elem.comment));
}
qDebug() << "output: \n" << output;
}
void TableGenerator::orderComposeTable()
{
// Stable-sorting to ensure that the item that appeared before the other in the
// original container will still appear first after the sort. This property is
// needed to handle the cases when user re-defines already defined key sequence
qStableSort(m_composeTable.begin(), m_composeTable.end(), Compare());
}

View File

@ -0,0 +1,128 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QTABLEGENERATOR_H
#define QTABLEGENERATOR_H
#include <QtCore/QList>
#include <QtCore/QFile>
#include <QtCore/QMap>
#include <QtCore/QString>
#define QT_KEYSEQUENCE_MAX_LEN 6
struct QComposeTableElement {
uint keys[QT_KEYSEQUENCE_MAX_LEN];
uint value;
QString comment;
};
class Compare
{
public:
bool operator () (const QComposeTableElement &lhs, const uint rhs[QT_KEYSEQUENCE_MAX_LEN])
{
for (size_t i = 0; i < QT_KEYSEQUENCE_MAX_LEN; i++) {
if (lhs.keys[i] != rhs[i])
return (lhs.keys[i] < rhs[i]);
}
return false;
}
bool operator () (const QComposeTableElement &lhs, const QComposeTableElement &rhs)
{
for (size_t i = 0; i < QT_KEYSEQUENCE_MAX_LEN; i++) {
if (lhs.keys[i] != rhs.keys[i])
return (lhs.keys[i] < rhs.keys[i]);
}
return false;
}
};
class TableGenerator
{
public:
enum TableState
{
UnsupportedLocale,
EmptyTable,
UnknownSystemComposeDir,
MissingComposeFile,
NoErrors
};
TableGenerator();
~TableGenerator();
void parseComposeFile(QFile *composeFile);
void printComposeTable() const;
void orderComposeTable();
QList<QComposeTableElement> composeTable() const;
TableState tableState() const { return m_state; }
protected:
bool processFile(QString composeFileName);
void parseKeySequence(QString line);
void parseIncludeInstruction(QString line);
void findComposeFile();
bool findSystemComposeDir();
QString systemComposeDir();
ushort keysymToUtf8(uint32_t sym);
uint32_t stringToKeysym(QString keysymName);
void readLocaleMappings();
void initPossibleLocations();
bool cleanState() const { return ((m_state & NoErrors) == NoErrors); }
QString locale() const;
private:
QList<QComposeTableElement> m_composeTable;
QMap<QString, QString> m_localeToTable;
TableState m_state;
QString m_systemComposeDir;
QList<QString> m_possibleLocations;
};
#endif // QTABLEGENERATOR_H

View File

@ -0,0 +1,70 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qpa/qplatforminputcontextplugin_p.h>
#include <QtCore/QStringList>
#include "qcomposeplatforminputcontext.h"
QT_BEGIN_NAMESPACE
class QComposePlatformInputContextPlugin : public QPlatformInputContextPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPlatformInputContextFactoryInterface" FILE "compose.json")
public:
QComposeInputContext *create(const QString &, const QStringList &);
};
QComposeInputContext *QComposePlatformInputContextPlugin::create(const QString &system, const QStringList &paramList)
{
Q_UNUSED(paramList);
if (system.compare(system, QStringLiteral("compose"), Qt::CaseInsensitive) == 0)
return new QComposeInputContext;
return 0;
}
QT_END_NAMESPACE
#include "main.moc"

View File

@ -0,0 +1,268 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qcomposeplatforminputcontext.h"
#include <QtCore/QCoreApplication>
#include <QtGui/QKeyEvent>
#include <QtCore/QDebug>
QT_BEGIN_NAMESPACE
//#define DEBUG_COMPOSING
static const int ignoreKeys[] = {
Qt::Key_Shift,
Qt::Key_Control,
Qt::Key_Meta,
Qt::Key_Alt,
Qt::Key_CapsLock,
Qt::Key_Super_L,
Qt::Key_Super_R,
Qt::Key_Hyper_L,
Qt::Key_Hyper_R,
Qt::Key_Mode_switch
};
static const int composingKeys[] = {
Qt::Key_Multi_key,
Qt::Key_Dead_Grave,
Qt::Key_Dead_Acute,
Qt::Key_Dead_Circumflex,
Qt::Key_Dead_Tilde,
Qt::Key_Dead_Macron,
Qt::Key_Dead_Breve,
Qt::Key_Dead_Abovedot,
Qt::Key_Dead_Diaeresis,
Qt::Key_Dead_Abovering,
Qt::Key_Dead_Doubleacute,
Qt::Key_Dead_Caron,
Qt::Key_Dead_Cedilla,
Qt::Key_Dead_Ogonek,
Qt::Key_Dead_Iota,
Qt::Key_Dead_Voiced_Sound,
Qt::Key_Dead_Semivoiced_Sound,
Qt::Key_Dead_Belowdot,
Qt::Key_Dead_Hook,
Qt::Key_Dead_Horn
};
QComposeInputContext::QComposeInputContext()
{
TableGenerator reader;
m_tableState = reader.tableState();
if ((m_tableState & TableGenerator::NoErrors) == TableGenerator::NoErrors) {
m_composeTable = reader.composeTable();
clearComposeBuffer();
}
}
bool QComposeInputContext::filterEvent(const QEvent *event)
{
// if there were errors when generating the compose table input
// context should not try to filter anything, simply return false
if ((m_tableState & TableGenerator::NoErrors) != TableGenerator::NoErrors)
return false;
QKeyEvent *keyEvent = (QKeyEvent *)event;
// should pass only the key presses
if (keyEvent->type() != QEvent::KeyPress) {
return false;
}
int keyval = keyEvent->key();
int keysym = 0;
if (ignoreKey(keyval))
return false;
QString text = keyEvent->text();
if (!composeKey(keyval) && text.isEmpty())
return false;
keysym = keyEvent->nativeVirtualKey();
int nCompose = 0;
while (m_composeBuffer[nCompose] != 0 && nCompose < QT_KEYSEQUENCE_MAX_LEN)
nCompose++;
if (nCompose == QT_KEYSEQUENCE_MAX_LEN) {
reset();
nCompose = 0;
}
m_composeBuffer[nCompose] = keysym;
// check sequence
if (checkComposeTable())
return true;
return false;
}
bool QComposeInputContext::isValid() const
{
return true;
}
void QComposeInputContext::setFocusObject(QObject *object)
{
m_focusObject = object;
}
void QComposeInputContext::reset()
{
clearComposeBuffer();
}
void QComposeInputContext::update(Qt::InputMethodQueries q)
{
QPlatformInputContext::update(q);
}
static bool isDuplicate(const QComposeTableElement &lhs, const QComposeTableElement &rhs)
{
for (size_t i = 0; i < QT_KEYSEQUENCE_MAX_LEN; i++) {
if (lhs.keys[i] != rhs.keys[i])
return false;
}
return true;
}
bool QComposeInputContext::checkComposeTable()
{
QList<QComposeTableElement>::iterator it =
qLowerBound(m_composeTable.begin(), m_composeTable.end(), m_composeBuffer, Compare());
// prevent dereferencing an 'end' iterator, which would result in a crash
if (it == m_composeTable.end())
it -= 1;
QComposeTableElement elem = *it;
// would be nicer if qLowerBound had API that tells if the item was actually found
if (m_composeBuffer[0] != elem.keys[0]) {
#ifdef DEBUG_COMPOSING
qDebug( "### no match ###" );
#endif
reset();
return false;
}
// check if compose buffer is matched
for (int i=0; i < QT_KEYSEQUENCE_MAX_LEN; i++) {
// check if partial match
if (m_composeBuffer[i] == 0 && elem.keys[i]) {
#ifdef DEBUG_COMPOSING
qDebug("### partial match ###");
#endif
return true;
}
if (m_composeBuffer[i] != elem.keys[i]) {
#ifdef DEBUG_COMPOSING
qDebug("### different entry ###");
#endif
reset();
return i != 0;
}
}
#ifdef DEBUG_COMPOSING
qDebug("### match exactly ###");
#endif
// check if the key sequence is overwriten - see the comment in
// TableGenerator::orderComposeTable()
int next = 1;
do {
// if we are at the end of the table, then we have nothing to do here
if (it + next != m_composeTable.end()) {
QComposeTableElement nextElem = *(it + next);
if (isDuplicate(elem, nextElem)) {
elem = nextElem;
next++;
continue;
} else {
break;
}
}
break;
} while (true);
commitText(elem.value);
reset();
return true;
}
void QComposeInputContext::commitText(uint character) const
{
QInputMethodEvent event;
event.setCommitString(QChar(character));
QCoreApplication::sendEvent(m_focusObject, &event);
}
bool QComposeInputContext::ignoreKey(int keyval) const
{
for (uint i = 0; i < (sizeof(ignoreKeys) / sizeof(ignoreKeys[0])); i++)
if (keyval == ignoreKeys[i])
return true;
return false;
}
bool QComposeInputContext::composeKey(int keyval) const
{
for (uint i = 0; i < (sizeof(composingKeys) / sizeof(composingKeys[0])); i++)
if (keyval == composingKeys[i])
return true;
return false;
}
void QComposeInputContext::clearComposeBuffer()
{
for (uint i=0; i < (sizeof(m_composeBuffer) / sizeof(int)); i++)
m_composeBuffer[i] = 0;
}
QComposeInputContext::~QComposeInputContext() {}
QT_END_NAMESPACE

View File

@ -0,0 +1,85 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QCOMPOSEPLATFORMINPUTCONTEXT_H
#define QCOMPOSEPLATFORMINPUTCONTEXT_H
#include <qpa/qplatforminputcontext.h>
#include <QtCore/QList>
#include "generator/qtablegenerator.h"
QT_BEGIN_NAMESPACE
class QEvent;
class QComposeInputContext : public QPlatformInputContext
{
Q_OBJECT
public:
QComposeInputContext();
~QComposeInputContext();
bool isValid() const;
void setFocusObject(QObject *object);
void reset();
void update(Qt::InputMethodQueries);
bool filterEvent(const QEvent *event);
protected:
void clearComposeBuffer();
bool ignoreKey(int keyval) const;
bool composeKey(int keyval) const;
bool checkComposeTable();
void commitText(uint character) const;
private:
QObject *m_focusObject;
QList<QComposeTableElement> m_composeTable;
uint m_composeBuffer[QT_KEYSEQUENCE_MAX_LEN + 1];
TableGenerator::TableState m_tableState;
};
QT_END_NAMESPACE
#endif // QCOMPOSEPLATFORMINPUTCONTEXT_H

View File

@ -0,0 +1,105 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef XKBCOMMON_WORKAROUND_H
#define XKBCOMMON_WORKAROUND_H
// Function utf32_to_utf8() is borrowed from the libxkbcommon library,
// file keysym-utf.c. The workaround should be removed once the fix from
// https://bugs.freedesktop.org/show_bug.cgi?id=56780 gets released.
static int utf32_to_utf8(uint32_t unichar, char *buffer)
{
int count, shift, length;
uint8_t head;
if (unichar <= 0x007f) {
buffer[0] = unichar;
buffer[1] = '\0';
return 2;
}
else if (unichar <= 0x07FF) {
length = 2;
head = 0xc0;
}
else if (unichar <= 0xffff) {
length = 3;
head = 0xe0;
}
else if (unichar <= 0x1fffff) {
length = 4;
head = 0xf0;
}
else if (unichar <= 0x3ffffff) {
length = 5;
head = 0xf8;
}
else {
length = 6;
head = 0xfc;
}
for (count = length - 1, shift = 0; count > 0; count--, shift += 6)
buffer[count] = 0x80 | ((unichar >> shift) & 0x3f);
buffer[0] = head | ((unichar >> shift) & 0x3f);
buffer[length] = '\0';
return length + 1;
}
static bool needWorkaround(uint32_t sym)
{
/* patch encoding botch */
if (sym == XKB_KEY_KP_Space)
return true;
/* special keysyms */
if ((sym >= XKB_KEY_BackSpace && sym <= XKB_KEY_Clear) ||
(sym >= XKB_KEY_KP_Multiply && sym <= XKB_KEY_KP_9) ||
sym == XKB_KEY_Return || sym == XKB_KEY_Escape ||
sym == XKB_KEY_Delete || sym == XKB_KEY_KP_Tab ||
sym == XKB_KEY_KP_Enter || sym == XKB_KEY_KP_Equal)
return true;
return false;
}
#endif // XKBCOMMON_WORKAROUND_H

View File

@ -1,4 +1,10 @@
TEMPLATE = subdirs
qtHaveModule(dbus) {
!mac:!win32:SUBDIRS += ibus maliit
}
unix:!macx:contains(QT_CONFIG, xkbcommon): {
SUBDIRS += compose
}