An error occurred while loading the file. Please try again.
-
Jarkko Koivikko authored
[ChangeLog] Added Serbian keyboard layout. Change-Id: I7baa330791c62f7bbf4ff6a253ce1eea9f9c0e32 Reviewed-by:
Mitch Curtis <mitch.curtis@qt.io>
0fc522d7
t9writeinputmethod.cpp 73.65 KiB
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "t9writeinputmethod.h"
#include "inputengine.h"
#include "inputcontext.h"
#include "trace.h"
#include "t9writeworker.h"
#include "virtualkeyboarddebug.h"
#include <QDirIterator>
#include <QCryptographicHash>
#ifdef QT_VIRTUALKEYBOARD_DEBUG
#include <QTime>
#endif
#include "handwritinggesturerecognizer.h"
#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
#include "unipentrace.h"
#include <QStandardPaths>
#endif
#include "decumaStatus.h"
#include "decumaSymbolCategories.h"
#include "decumaLanguages.h"
/* Set to 1 to enable T9 Write log.
The log is routed to qDebug() and it can be enabled for troubleshooting
and when reporting issues. The log must not to be enabled in production
build.
*/
#define QT_VIRTUALKEYBOARD_T9WRITE_LOG 0
namespace QtVirtualKeyboard {
class T9WriteCaseFormatter
{
public:
T9WriteCaseFormatter() :
preferLowercase(false)
{
}
void clear()
{
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
textCaseList.clear();
}
void ensureLength(int length, InputEngine::TextCase textCase)
{
if (length <= 0) {
textCaseList.clear();
return;
}
while (length < textCaseList.length())
textCaseList.removeLast();
while (length > textCaseList.length())
textCaseList.append(textCase);
}
QString formatString(const QString &str) const
{
QString result;
InputEngine::TextCase textCase = InputEngine::Lower;
for (int i = 0; i < str.length(); ++i) {
if (i < textCaseList.length())
textCase = textCaseList.at(i);
result.append(textCase == InputEngine::Upper ? str.at(i).toUpper() : (preferLowercase ? str.at(i).toLower() : str.at(i)));
}
return result;
}
bool preferLowercase;
private:
QList<InputEngine::TextCase> textCaseList;
};
class T9WriteInputMethodPrivate : public AbstractInputMethodPrivate
{
Q_DECLARE_PUBLIC(T9WriteInputMethod)
public:
enum EngineMode {
EngineUninitialized,
Alphabetic,
SimplifiedChinese,
TraditionalChinese,
HongKongChinese,
Japanese,
Korean
};
T9WriteInputMethodPrivate(T9WriteInputMethod *q_ptr) :
AbstractInputMethodPrivate(),
q_ptr(q_ptr),
cjk(false),
engineMode(EngineUninitialized),
defaultHwrDbPath(QLatin1String(":/QtQuick/VirtualKeyboard/T9Write/data/")),
defaultDictionaryDbPath(defaultHwrDbPath),
dictionaryLock(QMutex::Recursive),
attachedDictionary(0),
traceListHardLimit(32),
resultId(0),
resultTimer(0),
decumaSession(0),
activeWordIndex(-1),
arcAdditionStarted(false),
ignoreUpdate(false),
textCase(InputEngine::Lower)
#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
, unipenTrace()
#endif
{
}
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
static void *decumaMalloc(size_t size, void *pPrivate)
{
Q_UNUSED(pPrivate)
return malloc(size);
}
static void *decumaCalloc(size_t elements, size_t size, void *pPrivate)
{
Q_UNUSED(pPrivate)
return calloc(elements, size);
}
static void decumaFree(void *ptr, void *pPrivate)
{
Q_UNUSED(pPrivate)
free(ptr);
}
#if QT_VIRTUALKEYBOARD_T9WRITE_LOG
static void decumaLogString(void *pUserData, const char *pLogString, DECUMA_UINT32 nLogStringLength)
{
static QMutex s_logMutex;
static QByteArray s_logString;
Q_UNUSED(pUserData)
Q_UNUSED(nLogStringLength)
QMutexLocker guard(&s_logMutex);
s_logString.append(pLogString, nLogStringLength);
if (s_logString.endsWith('\n')) {
while (s_logString.endsWith('\n'))
s_logString.chop(1);
qDebug() << (const char *)s_logString.constData();
s_logString.clear();
}
}
#endif
bool initEngine(EngineMode newEngineMode)
{
if (engineMode == newEngineMode)
return engineMode != EngineUninitialized;
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::initEngine()" << newEngineMode;
if (decumaSession)
exitEngine();
if (newEngineMode == EngineUninitialized)
return false;
switch (newEngineMode) {
case Alphabetic:
cjk = false;
break;
case SimplifiedChinese:
case TraditionalChinese:
case HongKongChinese:
case Japanese:
case Korean:
cjk = true;
break;
default:
Q_ASSERT(0 && "Invalid EngineMode!");
return false;
}
engineMode = newEngineMode;
memset(&sessionSettings, 0, sizeof(sessionSettings));
QString hwrDb = findHwrDb(engineMode, defaultHwrDbPath);
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
hwrDbFile.setFileName(hwrDb);
if (!hwrDbFile.open(QIODevice::ReadOnly)) {
qCritical() << "Could not open HWR database" << hwrDb;
exitEngine();
return false;
}
sessionSettings.pStaticDB = (DECUMA_STATIC_DB_PTR)hwrDbFile.map(0, hwrDbFile.size(), QFile::NoOptions);
if (!sessionSettings.pStaticDB) {
qCritical() << "Could not read HWR database" << hwrDb;
exitEngine();
return false;
}
symbolCategories.append(DECUMA_CATEGORY_ANSI);
languageCategories.append(DECUMA_LANG_EN);
sessionSettings.recognitionMode = mcrMode;
sessionSettings.bMinimizeAddArcPreProcessing = 1;
sessionSettings.writingDirection = unknownWriting;
sessionSettings.charSet.pSymbolCategories = symbolCategories.data();
sessionSettings.charSet.nSymbolCategories = symbolCategories.size();
sessionSettings.charSet.pLanguages = languageCategories.data();
sessionSettings.charSet.nLanguages = languageCategories.size();
session = QByteArray(DECUMA_API(GetSessionSize)(), 0);
decumaSession = (DECUMA_SESSION *)(!session.isEmpty() ? session.data() : 0);
DECUMA_STATUS status = DECUMA_API(BeginSession)(decumaSession, &sessionSettings, &memFuncs);
Q_ASSERT(status == decumaNoError);
if (status != decumaNoError) {
qCritical() << "Could not initialize T9Write engine" << status;
exitEngine();
return false;
}
#if QT_VIRTUALKEYBOARD_T9WRITE_LOG
DECUMA_API(StartLogging)(decumaSession, 0, decumaLogString);
#endif
worker.reset(new T9WriteWorker(decumaSession, cjk));
worker->start();
return true;
}
void exitEngine()
{
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::exitEngine()";
worker.reset();
if (sessionSettings.pStaticDB) {
hwrDbFile.unmap((uchar *)sessionSettings.pStaticDB);
hwrDbFile.close();
}
if (attachedDictionary) {
detachDictionary(attachedDictionary);
attachedDictionary.reset();
}
loadedDictionary.reset();
if (decumaSession) {
#if QT_VIRTUALKEYBOARD_T9WRITE_LOG
DECUMA_API(StopLogging)(decumaSession);
#endif
DECUMA_API(EndSession)(decumaSession);
decumaSession = 0;
session.clear();
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
}
memset(&sessionSettings, 0, sizeof(sessionSettings));
symbolCategories.clear();
languageCategories.clear();
engineMode = EngineUninitialized;
cjk = false;
}
QString findHwrDb(EngineMode mode, const QString &dir) const
{
QString hwrDbPath(dir);
switch (mode) {
case Alphabetic:
hwrDbPath.append(QLatin1String("_databas_le.bin"));
break;
case SimplifiedChinese:
hwrDbPath.append(QLatin1String("cjk_S_gb18030_le.hdb"));
break;
case TraditionalChinese:
hwrDbPath.append(QLatin1String("cjk_T_std_le.hdb"));
break;
case HongKongChinese:
hwrDbPath.append(QLatin1String("cjk_HK_std_le.hdb"));
break;
case Japanese:
hwrDbPath.append(QLatin1String("cjk_J_std_le.hdb"));
break;
case Korean:
hwrDbPath.append(QLatin1String("cjk_K_mkt_le.hdb"));
break;
default:
return QString();
}
return QFileInfo::exists(hwrDbPath) ? hwrDbPath : QString();
}
QString findDictionary(const QString &dir, const QLocale &locale, DECUMA_SRC_DICTIONARY_TYPE &srcType)
{
srcType = numberOfSrcDictionaryTypes;
QStringList languageCountry = locale.name().split("_");
if (languageCountry.length() != 2)
return QString();
QString dictionary;
QDirIterator it(dir, QDirIterator::NoIteratorFlags);
while (it.hasNext()) {
QString fileEntry = it.next();
if (!fileEntry.contains("_" + languageCountry[0].toUpper()))
continue;
if (fileEntry.endsWith(QLatin1String(".ldb"))) {
#if T9WRITEAPIMAJORVERNUM >= 20
qCritical() << "Incompatible T9 Write dictionary" << fileEntry;
continue;
#else
srcType = decumaXT9LDB;
#endif
} else if (fileEntry.endsWith(QLatin1String(".phd"))) {
#if T9WRITEAPIMAJORVERNUM >= 20
srcType = decumaPortableHWRDictionary;
#else
qCritical() << "Incompatible T9 Write dictionary" << fileEntry;
continue;
#endif
} else {
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
qWarning() << "Incompatible T9 Write dictionary" << fileEntry;
continue;
}
dictionary = fileEntry;
break;
}
return dictionary;
}
bool attachDictionary(const QSharedPointer<T9WriteDictionary> &dictionary)
{
QMutexLocker dictionaryGuard(&dictionaryLock);
Q_ASSERT(decumaSession != 0);
Q_ASSERT(dictionary != 0);
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::attachDictionary():" << dictionary->fileName();
#if T9WRITEAPIMAJORVERNUM >= 20
DECUMA_STATUS status = DECUMA_API(AttachDictionary)(decumaSession, dictionary->data(), dictionary->size());
#else
DECUMA_STATUS status = DECUMA_API(AttachConvertedDictionary)(decumaSession, dictionary->data());
#endif
return status == decumaNoError;
}
void detachDictionary(const QSharedPointer<T9WriteDictionary> &dictionary)
{
QMutexLocker dictionaryGuard(&dictionaryLock);
if (!dictionary)
return;
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::detachDictionary():" << dictionary->fileName();
Q_ASSERT(decumaSession != 0);
DECUMA_STATUS status = DECUMA_API(DetachDictionary)(decumaSession, dictionary->data());
Q_UNUSED(status)
Q_ASSERT(status == decumaNoError);
}
bool setInputMode(const QLocale &locale, InputEngine::InputMode inputMode)
{
Q_Q(T9WriteInputMethod);
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::setInputMode():" << locale << inputMode;
finishRecognition();
if (!initEngine(mapLocaleToEngineMode(locale)))
return false;
DECUMA_UINT32 language = mapToDecumaLanguage(locale, inputMode);
if (language == DECUMA_LANG_GSMDEFAULT) {
qWarning() << "Handwriting is not supported in" << locale.name();
return false;
}
int isLanguageSupported = 0;
DECUMA_API(DatabaseIsLanguageSupported)(sessionSettings.pStaticDB, language, &isLanguageSupported);
if (!isLanguageSupported) {
qWarning() << "Handwriting is not supported in" << locale.name();
return false;
}
bool languageChanged = languageCategories.isEmpty() || languageCategories.first() != language;
languageCategories.clear();
languageCategories.append(language);
// Add English as secondary language for non-latin languages.
// T9 Write requires it for punctuation and latin symbols if
// included in the symbol categories.
if (locale.script() != QLocale::LatinScript)
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
languageCategories.append(DECUMA_LANG_EN);
if (!updateSymbolCategories(language, locale, inputMode))
return false;
updateRecognitionMode(language, locale, inputMode);
updateDictionary(language, locale, languageChanged);
static const QList<DECUMA_UINT32> rtlLanguages = QList<DECUMA_UINT32>()
<< DECUMA_LANG_AR << DECUMA_LANG_IW << DECUMA_LANG_FA << DECUMA_LANG_UR;
sessionSettings.writingDirection = rtlLanguages.contains(language) ? rightToLeft : unknownWriting;
VIRTUALKEYBOARD_DEBUG() << " -> language categories:" << languageCategories;
VIRTUALKEYBOARD_DEBUG() << " -> symbol categories:" << symbolCategories;
VIRTUALKEYBOARD_DEBUG() << " -> recognition mode:" << sessionSettings.recognitionMode;
// Change session settings
sessionSettings.charSet.pSymbolCategories = symbolCategories.data();
sessionSettings.charSet.nSymbolCategories = symbolCategories.size();
sessionSettings.charSet.pLanguages = languageCategories.data();
sessionSettings.charSet.nLanguages = languageCategories.size();
DECUMA_STATUS status = DECUMA_API(ChangeSessionSettings)(decumaSession, &sessionSettings);
Q_ASSERT(status == decumaNoError);
caseFormatter.preferLowercase = q->inputContext()->inputMethodHints().testFlag(Qt::ImhPreferLowercase);
return status == decumaNoError;
}
EngineMode mapLocaleToEngineMode(const QLocale &locale)
{
#ifdef HAVE_T9WRITE_CJK
switch (locale.language()) {
case QLocale::Chinese: {
if (locale.script() == QLocale::TraditionalChineseScript)
return locale.country() == QLocale::HongKong ? HongKongChinese : TraditionalChinese;
return SimplifiedChinese;
break;
}
case QLocale::Japanese:
return Japanese;
break;
case QLocale::Korean:
return Korean;
default:
break;
}
#else
Q_UNUSED(locale)
#endif
#ifdef HAVE_T9WRITE_ALPHABETIC
return T9WriteInputMethodPrivate::Alphabetic;
#else
return T9WriteInputMethodPrivate::EngineUninitialized;
#endif
}
DECUMA_UINT32 mapToDecumaLanguage(const QLocale &locale, InputEngine::InputMode inputMode)
{
static const QLocale::Language maxLanguage = QLocale::Vietnamese;
static const DECUMA_UINT32 languageMap[maxLanguage + 1] = {
DECUMA_LANG_GSMDEFAULT, // AnyLanguage = 0
DECUMA_LANG_GSMDEFAULT, // C = 1
DECUMA_LANG_GSMDEFAULT, // Abkhazian = 2
DECUMA_LANG_GSMDEFAULT, // Oromo = 3
DECUMA_LANG_GSMDEFAULT, // Afar = 4
DECUMA_LANG_AF, // Afrikaans = 5
DECUMA_LANG_SQ, // Albanian = 6
DECUMA_LANG_GSMDEFAULT, // Amharic = 7
DECUMA_LANG_AR, // Arabic = 8
DECUMA_LANG_GSMDEFAULT, // Armenian = 9
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
DECUMA_LANG_GSMDEFAULT, // Assamese = 10
DECUMA_LANG_GSMDEFAULT, // Aymara = 11
DECUMA_LANG_AZ, // Azerbaijani = 12
DECUMA_LANG_GSMDEFAULT, // Bashkir = 13
DECUMA_LANG_EU, // Basque = 14
DECUMA_LANG_BN, // Bengali = 15
DECUMA_LANG_GSMDEFAULT, // Dzongkha = 16
DECUMA_LANG_GSMDEFAULT, // Bihari = 17
DECUMA_LANG_GSMDEFAULT, // Bislama = 18
DECUMA_LANG_GSMDEFAULT, // Breton = 19
DECUMA_LANG_BG, // Bulgarian = 20
DECUMA_LANG_GSMDEFAULT, // Burmese = 21
DECUMA_LANG_BE, // Belarusian = 22
DECUMA_LANG_KM, // Khmer = 23
DECUMA_LANG_CA, // Catalan = 24
DECUMA_LANG_PRC, // Chinese = 25
DECUMA_LANG_GSMDEFAULT, // Corsican = 26
DECUMA_LANG_HR, // Croatian = 27
DECUMA_LANG_CS, // Czech = 28
DECUMA_LANG_DA, // Danish = 29
DECUMA_LANG_NL, // Dutch = 30
DECUMA_LANG_EN, // English = 31
DECUMA_LANG_GSMDEFAULT, // Esperanto = 32
DECUMA_LANG_ET, // Estonian = 33
DECUMA_LANG_GSMDEFAULT, // Faroese = 34
DECUMA_LANG_GSMDEFAULT, // Fijian = 35
DECUMA_LANG_FI, // Finnish = 36
DECUMA_LANG_FR, // French = 37
DECUMA_LANG_GSMDEFAULT, // WesternFrisian = 38
DECUMA_LANG_GSMDEFAULT, // Gaelic = 39
DECUMA_LANG_GL, // Galician = 40
DECUMA_LANG_GSMDEFAULT, // Georgian = 41
DECUMA_LANG_DE, // German = 42
DECUMA_LANG_EL, // Greek = 43
DECUMA_LANG_GSMDEFAULT, // Greenlandic = 44
DECUMA_LANG_GSMDEFAULT, // Guarani = 45
DECUMA_LANG_GU, // Gujarati = 46
DECUMA_LANG_HA, // Hausa = 47
DECUMA_LANG_IW, // Hebrew = 48
DECUMA_LANG_HI, // Hindi = 49
DECUMA_LANG_HU, // Hungarian = 50
DECUMA_LANG_IS, // Icelandic = 51
DECUMA_LANG_IN, // Indonesian = 52
DECUMA_LANG_GSMDEFAULT, // Interlingua = 53
DECUMA_LANG_GSMDEFAULT, // Interlingue = 54
DECUMA_LANG_GSMDEFAULT, // Inuktitut = 55
DECUMA_LANG_GSMDEFAULT, // Inupiak = 56
DECUMA_LANG_GSMDEFAULT, // Irish = 57
DECUMA_LANG_IT, // Italian = 58
DECUMA_LANG_JP, // Japanese = 59
DECUMA_LANG_GSMDEFAULT, // Javanese = 60
DECUMA_LANG_KN, // Kannada = 61
DECUMA_LANG_GSMDEFAULT, // Kashmiri = 62
DECUMA_LANG_KK, // Kazakh = 63
DECUMA_LANG_GSMDEFAULT, // Kinyarwanda = 64
DECUMA_LANG_KY, // Kirghiz = 65
DECUMA_LANG_KO, // Korean = 66
DECUMA_LANG_GSMDEFAULT, // Kurdish = 67
DECUMA_LANG_GSMDEFAULT, // Rundi = 68
DECUMA_LANG_GSMDEFAULT, // Lao = 69
DECUMA_LANG_GSMDEFAULT, // Latin = 70
DECUMA_LANG_LV, // Latvian = 71
DECUMA_LANG_GSMDEFAULT, // Lingala = 72
DECUMA_LANG_LT, // Lithuanian = 73
DECUMA_LANG_MK, // Macedonian = 74
DECUMA_LANG_GSMDEFAULT, // Malagasy = 75
DECUMA_LANG_MS, // Malay = 76
DECUMA_LANG_ML, // Malayalam = 77
DECUMA_LANG_GSMDEFAULT, // Maltese = 78
DECUMA_LANG_GSMDEFAULT, // Maori = 79
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
DECUMA_LANG_MR, // Marathi = 80
DECUMA_LANG_GSMDEFAULT, // Marshallese = 81
DECUMA_LANG_MN, // Mongolian = 82
DECUMA_LANG_GSMDEFAULT, // NauruLanguage = 83
DECUMA_LANG_GSMDEFAULT, // Nepali = 84
DECUMA_LANG_NO, // NorwegianBokmal = 85
DECUMA_LANG_GSMDEFAULT, // Occitan = 86
DECUMA_LANG_GSMDEFAULT, // Oriya = 87
DECUMA_LANG_GSMDEFAULT, // Pashto = 88
DECUMA_LANG_FA, // Persian = 89
DECUMA_LANG_PL, // Polish = 90
DECUMA_LANG_PT, // Portuguese = 91
DECUMA_LANG_PA, // Punjabi = 92
DECUMA_LANG_GSMDEFAULT, // Quechua = 93
DECUMA_LANG_GSMDEFAULT, // Romansh = 94
DECUMA_LANG_RO, // Romanian = 95
DECUMA_LANG_RU, // Russian = 96
DECUMA_LANG_GSMDEFAULT, // Samoan = 97
DECUMA_LANG_GSMDEFAULT, // Sango = 98
DECUMA_LANG_GSMDEFAULT, // Sanskrit = 99
DECUMA_LANG_SRCY, // Serbian = 100
DECUMA_LANG_GSMDEFAULT, // Ossetic = 101
DECUMA_LANG_ST, // SouthernSotho = 102
DECUMA_LANG_GSMDEFAULT, // Tswana = 103
DECUMA_LANG_GSMDEFAULT, // Shona = 104
DECUMA_LANG_GSMDEFAULT, // Sindhi = 105
DECUMA_LANG_SI, // Sinhala = 106
DECUMA_LANG_GSMDEFAULT, // Swati = 107
DECUMA_LANG_SK, // Slovak = 108
DECUMA_LANG_SL, // Slovenian = 109
DECUMA_LANG_GSMDEFAULT, // Somali = 110
DECUMA_LANG_ES, // Spanish = 111
DECUMA_LANG_GSMDEFAULT, // Sundanese = 112
DECUMA_LANG_SW, // Swahili = 113
DECUMA_LANG_SV, // Swedish = 114
DECUMA_LANG_GSMDEFAULT, // Sardinian = 115
DECUMA_LANG_TG, // Tajik = 116
DECUMA_LANG_TA, // Tamil = 117
DECUMA_LANG_GSMDEFAULT, // Tatar = 118
DECUMA_LANG_TE, // Telugu = 119
DECUMA_LANG_TH, // Thai = 120
DECUMA_LANG_GSMDEFAULT, // Tibetan = 121
DECUMA_LANG_GSMDEFAULT, // Tigrinya = 122
DECUMA_LANG_GSMDEFAULT, // Tongan = 123
DECUMA_LANG_GSMDEFAULT, // Tsonga = 124
DECUMA_LANG_TR, // Turkish = 125
DECUMA_LANG_GSMDEFAULT, // Turkmen = 126
DECUMA_LANG_GSMDEFAULT, // Tahitian = 127
DECUMA_LANG_GSMDEFAULT, // Uighur = 128
DECUMA_LANG_UK, // Ukrainian = 129
DECUMA_LANG_UR, // Urdu = 130
DECUMA_LANG_UZ, // Uzbek = 131
DECUMA_LANG_VI // Vietnamese = 132
};
int localeLanguage = locale.language();
if (locale.language() > maxLanguage)
return DECUMA_LANG_GSMDEFAULT;
DECUMA_UINT32 language = languageMap[localeLanguage];
if (language == DECUMA_LANG_PRC) {
if (inputMode != InputEngine::ChineseHandwriting)
language = DECUMA_LANG_EN;
else if (locale.script() == QLocale::TraditionalChineseScript)
language = (locale.country() == QLocale::HongKong) ? DECUMA_LANG_HK : DECUMA_LANG_TW;
} else if (language == DECUMA_LANG_JP) {
if (inputMode != InputEngine::JapaneseHandwriting)
language = DECUMA_LANG_EN;
} else if (language == DECUMA_LANG_KO) {
if (inputMode != InputEngine::KoreanHandwriting)
631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
language = DECUMA_LANG_EN;
} else if (language == DECUMA_LANG_SRCY) {
if (inputMode != InputEngine::Cyrillic)
language = DECUMA_LANG_SRLA;
}
return language;
}
void updateRecognitionMode(DECUMA_UINT32 language, const QLocale &locale,
InputEngine::InputMode inputMode)
{
Q_Q(T9WriteInputMethod);
Q_UNUSED(language)
Q_UNUSED(locale)
Q_UNUSED(inputMode)
// Select recognition mode
// Note: MCR mode is preferred, as it does not require recognition
// timer and provides better user experience.
sessionSettings.recognitionMode = mcrMode;
// Use scrMode with hidden text or with no predictive mode
if (inputMode != InputEngine::ChineseHandwriting &&
inputMode != InputEngine::JapaneseHandwriting &&
inputMode != InputEngine::KoreanHandwriting) {
const Qt::InputMethodHints inputMethodHints = q->inputContext()->inputMethodHints();
if (inputMethodHints.testFlag(Qt::ImhHiddenText) || inputMethodHints.testFlag(Qt::ImhNoPredictiveText))
sessionSettings.recognitionMode = scrMode;
}
}
bool updateSymbolCategories(DECUMA_UINT32 language, const QLocale &locale,
InputEngine::InputMode inputMode)
{
// Handle CJK in separate method
if (cjk)
return updateSymbolCategoriesCjk(language, locale, inputMode);
symbolCategories.clear();
// Choose the symbol categories by input mode, script and input method hints
bool leftToRightGestures = true;
Q_Q(T9WriteInputMethod);
const Qt::InputMethodHints inputMethodHints = q->inputContext()->inputMethodHints();
switch (inputMode) {
case InputEngine::Latin:
if (inputMethodHints.testFlag(Qt::ImhEmailCharactersOnly)) {
symbolCategories.append(DECUMA_CATEGORY_EMAIL);
} else if (inputMethodHints.testFlag(Qt::ImhUrlCharactersOnly)) {
symbolCategories.append(DECUMA_CATEGORY_URL);
} else {
if (language == DECUMA_LANG_EN || language == DECUMA_LANG_NL)
symbolCategories.append(DECUMA_CATEGORY_ANSI);
else
symbolCategories.append(DECUMA_CATEGORY_ISO8859_1);
symbolCategories.append(DECUMA_CATEGORY_DIGIT);
symbolCategories.append(DECUMA_CATEGORY_BASIC_PUNCTUATIONS);
symbolCategories.append(DECUMA_CATEGORY_CONTRACTION_MARK);
if (language == DECUMA_LANG_ES)
symbolCategories.append(DECUMA_CATEGORY_SPANISH_PUNCTUATIONS);
}
break;
case InputEngine::Numeric:
symbolCategories.append(DECUMA_CATEGORY_DIGIT);
if (!inputMethodHints.testFlag(Qt::ImhDigitsOnly))
symbolCategories.append(DECUMA_CATEGORY_NUM_SUP);
break;
701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
case InputEngine::Dialable:
symbolCategories.append(DECUMA_CATEGORY_PHONE_NUMBER);
break;
case InputEngine::Greek:
symbolCategories.append(DECUMA_CATEGORY_GREEK);
symbolCategories.append(DECUMA_CATEGORY_QUEST_EXCL_MARK_PUNCTUATIONS);
symbolCategories.append(DECUMA_CATEGORY_PERIOD_COMMA_PUNCTUATIONS);
symbolCategories.append(DECUMA_CATEGORY_COLON_PUNCTUATIONS);
symbolCategories.append(DECUMA_CATEGORY_CONTRACTION_MARK);
symbolCategories.append(DECUMA_CATEGORY_CONTRACTION_MARK);
break;
case InputEngine::Cyrillic:
symbolCategories.append(DECUMA_CATEGORY_CYRILLIC);
symbolCategories.append(DECUMA_CATEGORY_QUEST_EXCL_MARK_PUNCTUATIONS);
symbolCategories.append(DECUMA_CATEGORY_PERIOD_COMMA_PUNCTUATIONS);
// Ukrainian needs contraction mark, but not Russian or Bulgarian
if (language == DECUMA_LANG_UK)
symbolCategories.append(DECUMA_CATEGORY_CONTRACTION_MARK);
break;
default:
qWarning() << "Handwriting is not supported in" << locale.name();
return false;
}
if (leftToRightGestures) {
symbolCategories.append(DECUMA_CATEGORY_BACKSPACE_STROKE);
symbolCategories.append(DECUMA_CATEGORY_RETURN_STROKE);
symbolCategories.append(DECUMA_CATEGORY_WHITESPACE_STROKE);
}
return true;
}
bool updateSymbolCategoriesCjk(DECUMA_UINT32 language, const QLocale &locale,
InputEngine::InputMode inputMode)
{
Q_UNUSED(language)
Q_ASSERT(cjk);
symbolCategories.clear();
switch (inputMode) {
case InputEngine::Latin:
symbolCategories.append(DECUMA_CATEGORY_ANSI);
symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL);
symbolCategories.append(DECUMA_CATEGORY_PUNCTUATIONS);
break;
case InputEngine::Numeric:
symbolCategories.append(DECUMA_CATEGORY_DIGIT);
symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL);
symbolCategories.append(DECUMA_CATEGORY_PUNCTUATIONS);
break;
case InputEngine::Dialable:
symbolCategories.append(DECUMA_CATEGORY_DIGIT);
symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL);
break;
case InputEngine::ChineseHandwriting:
switch (locale.script()) {
case QLocale::SimplifiedChineseScript:
symbolCategories.append(DECUMA_CATEGORY_GB2312_A);
symbolCategories.append(DECUMA_CATEGORY_GB2312_B_CHARS_ONLY);
symbolCategories.append(DECUMA_CATEGORY_GBK_3);
symbolCategories.append(DECUMA_CATEGORY_GBK_4);
symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL);
771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
symbolCategories.append(DECUMA_CATEGORY_CJK_GENERAL_PUNCTUATIONS);
symbolCategories.append(DECUMA_CATEGORY_PUNCTUATIONS);
break;
case QLocale::TraditionalChineseScript:
symbolCategories.append(DECUMA_CATEGORY_BIGFIVE);
if (language == DECUMA_LANG_HK)
symbolCategories.append(DECUMA_CATEGORY_HKSCS_CHARS_ONLY);
symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL);
symbolCategories.append(DECUMA_CATEGORY_CJK_GENERAL_PUNCTUATIONS);
symbolCategories.append(DECUMA_CATEGORY_PUNCTUATIONS);
break;
default:
qWarning() << "Chinese handwriting is not supported in" << locale.name();
return false;
}
break;
case InputEngine::JapaneseHandwriting:
symbolCategories.append(DECUMA_CATEGORY_JIS_LEVEL_1);
symbolCategories.append(DECUMA_CATEGORY_JIS_LEVEL_2);
symbolCategories.append(DECUMA_CATEGORY_HIRAGANA);
symbolCategories.append(DECUMA_CATEGORY_KATAKANA);
symbolCategories.append(DECUMA_CATEGORY_HIRAGANASMALL);
symbolCategories.append(DECUMA_CATEGORY_KATAKANASMALL);
symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL);
symbolCategories.append(DECUMA_CATEGORY_CJK_GENERAL_PUNCTUATIONS);
symbolCategories.append(DECUMA_CATEGORY_PUNCTUATIONS);
break;
case InputEngine::KoreanHandwriting:
symbolCategories.append(DECUMA_CATEGORY_HANGUL_1001_A);
symbolCategories.append(DECUMA_CATEGORY_HANGUL_1001_B);
symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL);
symbolCategories.append(DECUMA_CATEGORY_CJK_GENERAL_PUNCTUATIONS);
symbolCategories.append(DECUMA_CATEGORY_PUNCTUATIONS);
break;
default:
return false;
}
return true;
}
void updateDictionary(DECUMA_UINT32 language, const QLocale &locale, bool languageChanged)
{
Q_Q(T9WriteInputMethod);
/* The dictionary is loaded in the background thread. Once the loading is
complete the dictionary will be attached to the current session. The
attachment happens in the worker thread context, thus the direct
connection for the signal handler and the mutex protecting the
converted dictionary for concurrent access.
The loading operation is blocking for the main thread only if the
user starts handwriting input before the operation is complete.
*/
QMutexLocker dictionaryGuard(&dictionaryLock);
// Detach previous dictionary if the language is being changed
// or the recognizer mode is single-character mode
const Qt::InputMethodHints inputMethodHints = q->inputContext()->inputMethodHints();
if ((languageChanged || inputMethodHints.testFlag(Qt::ImhNoPredictiveText) || sessionSettings.recognitionMode == scrMode) && attachedDictionary) {
detachDictionary(attachedDictionary);
attachedDictionary.reset();
}
// Check if a dictionary needs to be loaded
if (languageChanged || !loadedDictionary) {
841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
loadedDictionary.reset();
DECUMA_SRC_DICTIONARY_INFO dictionaryInfo;
memset(&dictionaryInfo, 0, sizeof(dictionaryInfo));
QList<QLocale> decumaLocales;
decumaLocales.append(locale);
// CJK: No dictionary for latin input
if (cjk && language == DECUMA_LANG_EN)
decumaLocales.clear();
dictionaryFileName.clear();
QLocale decumaLocale;
for (QLocale tryLocale : decumaLocales) {
dictionaryFileName = findDictionary(defaultDictionaryDbPath, tryLocale, dictionaryInfo.srcType);
if (!dictionaryFileName.isEmpty()) {
decumaLocale = tryLocale;
break;
}
}
if (!dictionaryFileName.isEmpty()) {
if (dictionaryTask.isNull() || dictionaryTask->dictionaryFileName != dictionaryFileName) {
VIRTUALKEYBOARD_DEBUG() << " -> load dictionary:" << dictionaryFileName;
bool convertDictionary = true;
#if defined(HAVE_T9WRITE_CJK) && T9WRITEAPIMAJORVERNUM >= 20
// Chinese dictionary cannot be converted (PHD)
if (dictionaryInfo.srcType == decumaPortableHWRDictionary && decumaLocale.language() == QLocale::Chinese)
convertDictionary = false;
#endif
QSharedPointer<T9WriteDictionary> newDictionary(new T9WriteDictionary(decumaSession, memFuncs, cjk));
dictionaryTask.reset(new T9WriteDictionaryTask(newDictionary, dictionaryFileName, convertDictionary, dictionaryInfo));
QObject::connect(dictionaryTask.data(), &T9WriteDictionaryTask::completed,
q, &T9WriteInputMethod::dictionaryLoadCompleted, Qt::DirectConnection);
worker->addTask(dictionaryTask);
}
}
}
// Attach existing dictionary, if available
if (sessionSettings.recognitionMode == mcrMode && !inputMethodHints.testFlag(Qt::ImhNoPredictiveText) &&
loadedDictionary && !attachedDictionary) {
if (attachDictionary(loadedDictionary))
attachedDictionary = loadedDictionary;
}
}
QByteArray getContext(InputEngine::PatternRecognitionMode patternRecognitionMode,
const QVariantMap &traceCaptureDeviceInfo,
const QVariantMap &traceScreenInfo) const
{
QCryptographicHash hash(QCryptographicHash::Md5);
hash.addData((const char *)&patternRecognitionMode, sizeof(patternRecognitionMode));
QByteArray mapData;
QDataStream ds(&mapData, QIODevice::WriteOnly);
ds << traceCaptureDeviceInfo;
ds << traceScreenInfo;
hash.addData(mapData);
return hash.result();
}
void setContext(InputEngine::PatternRecognitionMode patternRecognitionMode,
const QVariantMap &traceCaptureDeviceInfo,
const QVariantMap &traceScreenInfo)
911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980
{
QByteArray context = getContext(patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo);
if (context == currentContext)
return;
currentContext = context;
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::setContext():" << QString(context.toHex());
// Finish recognition, but preserve current input
Q_Q(T9WriteInputMethod);
QString preeditText = q->inputContext()->preeditText();
// WA: T9Write CJK may crash in some cases with long stringStart.
// Therefore we don't restore the current input in this mode.
bool preserveCurrentInput = !preeditText.isEmpty() && !cjk;
T9WriteCaseFormatter oldCaseFormatter(caseFormatter);
finishRecognition(!preserveCurrentInput);
if (preserveCurrentInput) {
caseFormatter = oldCaseFormatter;
stringStart = preeditText;
wordCandidates.append(preeditText);
activeWordIndex = 0;
emit q->selectionListChanged(SelectionListModel::WordCandidateList);
emit q->selectionListActiveItemChanged(SelectionListModel::WordCandidateList, activeWordIndex);
}
const int dpi = traceCaptureDeviceInfo.value("dpi", 96).toInt();
static const int INSTANT_GESTURE_WIDTH_THRESHOLD_MM = 25;
static const int INSTANT_GESTURE_HEIGHT_THRESHOLD_MM = 25;
instantGestureSettings.widthThreshold = INSTANT_GESTURE_WIDTH_THRESHOLD_MM / 25.4 * dpi;
instantGestureSettings.heightThreshold = INSTANT_GESTURE_HEIGHT_THRESHOLD_MM / 25.4 * dpi;
gestureRecognizer.setDpi(dpi);
QVariantList horizontalRulers(traceScreenInfo.value("horizontalRulers", QVariantList()).toList());
if (horizontalRulers.count() >= 2) {
sessionSettings.baseline = horizontalRulers.last().toInt();
sessionSettings.helpline = 0;
sessionSettings.topline = horizontalRulers.first().toInt();
sessionSettings.supportLineSet = baselineAndTopline;
} else {
sessionSettings.baseline = 0;
sessionSettings.helpline = 0;
sessionSettings.topline = 0;
sessionSettings.supportLineSet = baselineAndTopline;
}
DECUMA_STATUS status = DECUMA_API(ChangeSessionSettings)(decumaSession, &sessionSettings);
Q_ASSERT(status == decumaNoError);
}
Trace *traceBegin(int traceId, InputEngine::PatternRecognitionMode patternRecognitionMode,
const QVariantMap &traceCaptureDeviceInfo, const QVariantMap &traceScreenInfo)
{
Q_UNUSED(traceId)
Q_UNUSED(patternRecognitionMode)
Q_UNUSED(traceScreenInfo)
if (!worker)
return 0;
stopResultTimer();
// Dictionary must be completed before the arc addition can begin
if (dictionaryTask) {
dictionaryTask->wait();
dictionaryTask.reset();
}
// Cancel the current recognition task
981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050
worker->removeAllTasks<T9WriteRecognitionResultsTask>();
if (recognitionTask) {
recognitionTask->cancelRecognition();
recognitionTask->wait();
worker->removeTask(recognitionTask);
recognitionTask.reset();
}
#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
if (!unipenTrace)
unipenTrace.reset(new UnipenTrace(traceCaptureDeviceInfo, traceScreenInfo));
#endif
setContext(patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo);
DECUMA_STATUS status;
if (!arcAdditionStarted) {
status = DECUMA_API(BeginArcAddition)(decumaSession);
Q_ASSERT(status == decumaNoError);
arcAdditionStarted = true;
}
DECUMA_UINT32 arcID = (DECUMA_UINT32)traceId;
status = DECUMA_API(StartNewArc)(decumaSession, arcID);
Q_ASSERT(status == decumaNoError);
if (status != decumaNoError) {
DECUMA_API(EndArcAddition)(decumaSession);
arcAdditionStarted = false;
return NULL;
}
Trace *trace = new Trace();
#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
trace->setChannels(QStringList("t"));
#endif
traceList.append(trace);
return trace;
}
void traceEnd(Trace *trace)
{
if (trace->isCanceled()) {
DECUMA_API(CancelArc)(decumaSession, trace->traceId());
traceList.removeOne(trace);
delete trace;
} else {
addPointsToTraceGroup(trace);
}
if (!traceList.isEmpty()) {
Q_ASSERT(arcAdditionStarted);
if (countActiveTraces() == 0) {
restartRecognition();
if (cjk) {
// For some reason gestures don't seem to work in CJK mode
// Using our own gesture recognizer as fallback
handleGesture();
}
}
} else if (arcAdditionStarted) {
DECUMA_API(EndArcAddition)(decumaSession);
arcAdditionStarted = false;
}
}
int countActiveTraces() const
{
int count = 0;
for (Trace *trace : qAsConst(traceList)) {
1051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
if (!trace->isFinal())
count++;
}
return count;
}
void clearTraces()
{
qDeleteAll(traceList);
traceList.clear();
}
void addPointsToTraceGroup(Trace *trace)
{
Q_ASSERT(decumaSession != 0);
const QVariantList points = trace->points();
Q_ASSERT(!points.isEmpty());
DECUMA_UINT32 arcID = (DECUMA_UINT32)trace->traceId();
DECUMA_STATUS status;
for (const QVariant &p : points) {
const QPoint pt(p.toPointF().toPoint());
status = DECUMA_API(AddPoint)(decumaSession, (DECUMA_COORD)pt.x(),(DECUMA_COORD)pt.y(), arcID);
if (status != decumaNoError) {
VIRTUALKEYBOARD_DEBUG() << "decumaAddPoint failed" << status;
finishRecognition();
return;
}
}
status = DECUMA_API(CommitArc)(decumaSession, arcID);
if (status != decumaNoError) {
VIRTUALKEYBOARD_DEBUG() << "decumaCommitArc failed" << status;
finishRecognition();
return;
}
}
void noteSelected(int index)
{
if (wordCandidatesHwrResultIndex.isEmpty())
return;
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::noteSelected():" << index;
Q_ASSERT(index >= 0 && index < wordCandidatesHwrResultIndex.length());
int resultIndex = wordCandidatesHwrResultIndex[index];
DECUMA_STATUS status = DECUMA_API(NoteSelectedCandidate)(decumaSession, resultIndex);
Q_UNUSED(status)
Q_ASSERT(status == decumaNoError);
}
void restartRecognition()
{
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::restartRecognition()";
Q_Q(T9WriteInputMethod);
QSharedPointer<T9WriteRecognitionResult> recognitionResult(new T9WriteRecognitionResult(++resultId, 9, 64));
recognitionTask.reset(new T9WriteRecognitionTask(recognitionResult, instantGestureSettings,
attachedDictionary ? boostDictWords : noBoost,
stringStart));
worker->addTask(recognitionTask);
QSharedPointer<T9WriteRecognitionResultsTask> resultsTask(new T9WriteRecognitionResultsTask(recognitionResult));
q->connect(resultsTask.data(), SIGNAL(resultsAvailable(const QVariantList &)), SLOT(resultsAvailable(const QVariantList &)));
worker->addTask(resultsTask);
resetResultTimer();
}
1121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190
bool finishRecognition(bool emitSelectionListChanged = true)
{
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::finishRecognition()";
if (!worker)
return false;
bool result = !traceList.isEmpty();
Q_ASSERT(decumaSession != 0);
stopResultTimer();
clearTraces();
worker->removeAllTasks<T9WriteRecognitionResultsTask>();
if (recognitionTask) {
recognitionTask->cancelRecognition();
recognitionTask->wait();
worker->removeTask(recognitionTask);
recognitionTask.reset();
result = true;
}
if (arcAdditionStarted) {
DECUMA_API(EndArcAddition)(decumaSession);
arcAdditionStarted = false;
}
if (!wordCandidates.isEmpty()) {
wordCandidates.clear();
wordCandidatesHwrResultIndex.clear();
activeWordIndex = -1;
if (emitSelectionListChanged) {
Q_Q(T9WriteInputMethod);
emit q->selectionListChanged(SelectionListModel::WordCandidateList);
emit q->selectionListActiveItemChanged(SelectionListModel::WordCandidateList, activeWordIndex);
}
result = true;
}
stringStart.clear();
scrResult.clear();
caseFormatter.clear();
#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
unipenTrace.reset();
#endif
return result;
}
bool select(int index = -1)
{
if (!worker)
return false;
if (sessionSettings.recognitionMode == mcrMode && wordCandidates.isEmpty()) {
finishRecognition();
return false;
}
if (sessionSettings.recognitionMode == scrMode && scrResult.isEmpty()) {
finishRecognition();
return false;
}
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::select():" << index;
Q_Q(T9WriteInputMethod);
if (sessionSettings.recognitionMode == mcrMode) {
1191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260
index = index >= 0 ? index : activeWordIndex;
noteSelected(index);
QString finalWord = wordCandidates.at(index);
#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
// Record trace
if (unipenTrace) {
if (finalWord.length() == 1) {
// In recording mode, the text case must match with the current text case
QChar ch(finalWord.at(0));
if (!ch.isLetter() || (ch.isUpper() == (textCase == InputEngine::Upper))) {
QStringList homeLocations = QStandardPaths::standardLocations(QStandardPaths::HomeLocation);
if (!homeLocations.isEmpty()) {
unipenTrace->setDirectory(QStringLiteral("%1/%2").arg(homeLocations.at(0)).arg("VIRTUAL_KEYBOARD_TRACES"));
unipenTrace->record(traceList);
unipenTrace->save(ch.unicode(), 100);
}
}
}
}
#endif
finishRecognition();
q->inputContext()->commit(finalWord);
} else if (sessionSettings.recognitionMode == scrMode) {
QString finalWord = scrResult;
finishRecognition();
q->inputContext()->inputEngine()->virtualKeyClick((Qt::Key)finalWord.at(0).unicode(), finalWord, Qt::NoModifier);
}
return true;
}
void resetResultTimer()
{
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::resetResultTimer()";
Q_Q(T9WriteInputMethod);
stopResultTimer();
resultTimer = q->startTimer(500);
}
void stopResultTimer()
{
if (resultTimer) {
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::stopResultTimer()";
Q_Q(T9WriteInputMethod);
q->killTimer(resultTimer);
resultTimer = 0;
}
}
void resultsAvailable(const QVariantList &resultList)
{
if (!resultList.isEmpty()) {
if (recognitionTask && recognitionTask->resultId() == resultList.first().toMap()["resultId"].toInt())
processResult(resultList);
}
}
void processResult(const QVariantList &resultList)
{
if (resultList.isEmpty())
return;
if (resultList.first().toMap()["resultId"] != resultId)
return;
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::processResult()";
Q_Q(T9WriteInputMethod);
1261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330
InputContext *ic = q->inputContext();
if (!ic)
return;
QStringList newWordCandidates;
QList<int> newWordCandidatesHwrResultIndex;
QString resultString;
QString gesture;
QVariantList symbolStrokes;
for (int i = 0; i < resultList.size(); i++) {
QVariantMap result = resultList.at(i).toMap();
QString resultChars = result["chars"].toString();
if (i == 0)
caseFormatter.ensureLength(resultChars.length(), textCase);
if (!resultChars.isEmpty()) {
resultChars = caseFormatter.formatString(resultChars);
if (sessionSettings.recognitionMode == mcrMode) {
newWordCandidates.append(resultChars);
newWordCandidatesHwrResultIndex.append(i);
}
}
if (i == 0) {
resultString = resultChars;
if (result.contains("gesture"))
gesture = result["gesture"].toString();
if (sessionSettings.recognitionMode == mcrMode && result.contains("symbolStrokes"))
symbolStrokes = result["symbolStrokes"].toList();
if (sessionSettings.recognitionMode == scrMode)
break;
}
}
bool wordCandidatesChanged = wordCandidates != newWordCandidates;
#ifndef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
// Delete trace history
const InputEngine::InputMode inputMode = q->inputEngine()->inputMode();
if (sessionSettings.recognitionMode == mcrMode && !symbolStrokes.isEmpty() &&
inputMode != InputEngine::ChineseHandwriting &&
inputMode != InputEngine::JapaneseHandwriting &&
inputMode != InputEngine::KoreanHandwriting) {
int activeTraces = symbolStrokes.at(symbolStrokes.count() - 1).toInt();
if (symbolStrokes.count() > 1)
activeTraces += symbolStrokes.at(symbolStrokes.count() - 2).toInt();
while (activeTraces < traceList.count())
delete traceList.takeFirst();
}
// Enforce hard limit for number of traces
if (traceList.count() >= traceListHardLimit) {
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::processResult(): Clearing traces (hard limit):" << traceList.count();
clearTraces();
}
#endif
// Find a gesture at the end of the first result
if (!gesture.isEmpty()) {
DECUMA_UNICODE gestureSymbol = gesture.at(0).unicode();
switch (gestureSymbol) {
case '\b':
ic->inputEngine()->virtualKeyClick(Qt::Key_Backspace, QString(), Qt::NoModifier);
break;
case '\r':
ic->inputEngine()->virtualKeyClick(Qt::Key_Return, QLatin1String("\n"), Qt::NoModifier);
break;
case ' ':
ic->inputEngine()->virtualKeyClick(Qt::Key_Space, QLatin1String(" "), Qt::NoModifier);
1331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400
break;
default:
ic->commit(ic->preeditText());
finishRecognition();
break;
}
return;
}
if (sessionSettings.recognitionMode == mcrMode) {
ignoreUpdate = true;
ic->setPreeditText(resultString);
ignoreUpdate = false;
} else if (sessionSettings.recognitionMode == scrMode) {
if (resultTimer == 0 && !resultString.isEmpty())
ic->inputEngine()->virtualKeyClick((Qt::Key)resultString.at(0).unicode(), resultString, Qt::NoModifier);
else
scrResult = resultString;
}
if (wordCandidatesChanged) {
wordCandidates = newWordCandidates;
wordCandidatesHwrResultIndex = newWordCandidatesHwrResultIndex;
activeWordIndex = wordCandidates.isEmpty() ? -1 : 0;
emit q->selectionListChanged(SelectionListModel::WordCandidateList);
emit q->selectionListActiveItemChanged(SelectionListModel::WordCandidateList, activeWordIndex);
}
}
bool handleGesture()
{
if (countActiveTraces() > 0)
return false;
QVariantMap gesture(gestureRecognizer.recognize(traceList.mid(traceList.length() - 1, 1)));
if (gesture.isEmpty())
return false;
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::handleGesture():" << gesture;
if (gesture[QLatin1String("type")].toString() == QLatin1String("swipe")) {
static const int SWIPE_ANGLE_THRESHOLD = 15; // degrees +-
qreal swipeLength = gesture[QLatin1String("length")].toReal();
if (swipeLength >= instantGestureSettings.widthThreshold) {
Q_Q(T9WriteInputMethod);
InputContext *ic = q->inputContext();
if (!ic)
return false;
qreal swipeAngle = gesture[QLatin1String("angle_degrees")].toReal();
int swipeTouchCount = gesture[QLatin1String("touch_count")].toInt();
// Swipe left
if (swipeAngle <= 180 + SWIPE_ANGLE_THRESHOLD && swipeAngle >= 180 - SWIPE_ANGLE_THRESHOLD) {
if (swipeTouchCount == 1) {
// Single swipe: backspace
ic->inputEngine()->virtualKeyClick(Qt::Key_Backspace, QString(), Qt::NoModifier);
return true;
}
return false;
}
// Swipe right
const InputEngine::InputMode inputMode = q->inputEngine()->inputMode();
if (inputMode != InputEngine::ChineseHandwriting &&
1401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470
inputMode != InputEngine::JapaneseHandwriting &&
inputMode != InputEngine::KoreanHandwriting) {
if (swipeAngle <= SWIPE_ANGLE_THRESHOLD || swipeAngle >= 360 - SWIPE_ANGLE_THRESHOLD) {
if (swipeTouchCount == 1) {
// Single swipe: space
ic->inputEngine()->virtualKeyClick(Qt::Key_Space, QString(" "), Qt::NoModifier);
return true;
}
return false;
}
}
// Swipe up
if (swipeAngle <= 270 + SWIPE_ANGLE_THRESHOLD && swipeAngle >= 270 - SWIPE_ANGLE_THRESHOLD) {
if (swipeTouchCount == 1) {
// Single swipe: toggle input mode
select();
if (!(ic->inputMethodHints() & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly))) {
QList<int> inputModes = ic->inputEngine()->inputModes();
// Filter out duplicate numeric mode (in favor of Numeric)
int indexOfNumericInputMode = inputModes.indexOf(InputEngine::Numeric);
int indexOfDialableInputMode = inputModes.indexOf(InputEngine::Dialable);
if (indexOfNumericInputMode != -1 && indexOfDialableInputMode != -1)
inputModes.removeAt(inputMode != InputEngine::Dialable ?
indexOfDialableInputMode :
indexOfNumericInputMode);
if (inputModes.count() > 1) {
int inputModeIndex = inputModes.indexOf((int)inputMode) + 1;
if (inputModeIndex >= inputModes.count())
inputModeIndex = 0;
ic->inputEngine()->setInputMode((InputEngine::InputMode)inputModes.at(inputModeIndex));
}
}
return true;
}
}
}
}
return false;
}
bool isValidInputChar(const QChar &c) const
{
if (c.isLetterOrNumber())
return true;
if (isJoiner(c))
return true;
return false;
}
bool isJoiner(const QChar &c) const
{
if (c.isPunct() || c.isSymbol()) {
Q_Q(const T9WriteInputMethod);
InputContext *ic = q->inputContext();
if (ic) {
Qt::InputMethodHints inputMethodHints = ic->inputMethodHints();
if (inputMethodHints.testFlag(Qt::ImhUrlCharactersOnly) || inputMethodHints.testFlag(Qt::ImhEmailCharactersOnly))
return QString(QStringLiteral(":/?#[]@!$&'()*+,;=-_.%")).contains(c);
}
ushort unicode = c.unicode();
if (unicode == Qt::Key_Apostrophe || unicode == Qt::Key_Minus)
return true;
}
return false;
}
T9WriteInputMethod *q_ptr;
static const DECUMA_MEM_FUNCTIONS memFuncs;
1471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540
bool cjk;
EngineMode engineMode;
QByteArray currentContext;
DECUMA_SESSION_SETTINGS sessionSettings;
DECUMA_INSTANT_GESTURE_SETTINGS instantGestureSettings;
QString defaultHwrDbPath;
QString defaultDictionaryDbPath;
QFile hwrDbFile;
QVector<DECUMA_UINT32> languageCategories;
QVector<DECUMA_UINT32> symbolCategories;
QScopedPointer<T9WriteWorker> worker;
QList<Trace *> traceList;
int traceListHardLimit;
QMutex dictionaryLock;
QString dictionaryFileName;
QSharedPointer<T9WriteDictionary> loadedDictionary;
QSharedPointer<T9WriteDictionary> attachedDictionary;
QSharedPointer<T9WriteDictionaryTask> dictionaryTask;
QSharedPointer<T9WriteRecognitionTask> recognitionTask;
int resultId;
int resultTimer;
QByteArray session;
DECUMA_SESSION *decumaSession;
QStringList wordCandidates;
QList<int> wordCandidatesHwrResultIndex;
QString stringStart;
QString scrResult;
int activeWordIndex;
bool arcAdditionStarted;
bool ignoreUpdate;
InputEngine::TextCase textCase;
T9WriteCaseFormatter caseFormatter;
HandwritingGestureRecognizer gestureRecognizer;
#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
QScopedPointer<UnipenTrace> unipenTrace;
#endif
};
const DECUMA_MEM_FUNCTIONS T9WriteInputMethodPrivate::memFuncs = {
T9WriteInputMethodPrivate::decumaMalloc,
T9WriteInputMethodPrivate::decumaCalloc,
T9WriteInputMethodPrivate::decumaFree,
NULL
};
/*!
\class QtVirtualKeyboard::T9WriteInputMethod
\internal
*/
T9WriteInputMethod::T9WriteInputMethod(QObject *parent) :
AbstractInputMethod(*new T9WriteInputMethodPrivate(this), parent)
{
}
T9WriteInputMethod::~T9WriteInputMethod()
{
Q_D(T9WriteInputMethod);
d->exitEngine();
}
QList<InputEngine::InputMode> T9WriteInputMethod::inputModes(const QString &locale)
{
Q_D(T9WriteInputMethod);
QList<InputEngine::InputMode> availableInputModes;
const Qt::InputMethodHints inputMethodHints(inputContext()->inputMethodHints());
const QLocale loc(locale);
T9WriteInputMethodPrivate::EngineMode mode = d->mapLocaleToEngineMode(loc);
// Add primary input mode
1541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610
switch (mode) {
#ifdef HAVE_T9WRITE_ALPHABETIC
case T9WriteInputMethodPrivate::Alphabetic:
if (d->findHwrDb(T9WriteInputMethodPrivate::Alphabetic, d->defaultHwrDbPath).isEmpty())
return availableInputModes;
if (!(inputMethodHints & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly | Qt::ImhLatinOnly))) {
switch (loc.script()) {
case QLocale::GreekScript:
availableInputModes.append(InputEngine::Greek);
break;
case QLocale::CyrillicScript:
availableInputModes.append(InputEngine::Cyrillic);
break;
default:
break;
}
availableInputModes.append(InputEngine::Latin);
}
break;
#endif
#ifdef HAVE_T9WRITE_CJK
case T9WriteInputMethodPrivate::SimplifiedChinese:
case T9WriteInputMethodPrivate::TraditionalChinese:
case T9WriteInputMethodPrivate::HongKongChinese:
if (d->findHwrDb(mode, d->defaultHwrDbPath).isEmpty())
return availableInputModes;
if (!(inputMethodHints & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly | Qt::ImhLatinOnly)))
availableInputModes.append(InputEngine::ChineseHandwriting);
break;
case T9WriteInputMethodPrivate::Japanese:
if (d->findHwrDb(T9WriteInputMethodPrivate::Japanese, d->defaultHwrDbPath).isEmpty())
return availableInputModes;
if (!(inputMethodHints & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly | Qt::ImhLatinOnly)))
availableInputModes.append(InputEngine::JapaneseHandwriting);
break;
case T9WriteInputMethodPrivate::Korean:
if (d->findHwrDb(T9WriteInputMethodPrivate::Korean, d->defaultHwrDbPath).isEmpty())
return availableInputModes;
if (!(inputMethodHints & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly | Qt::ImhLatinOnly)))
availableInputModes.append(InputEngine::KoreanHandwriting);
break;
#endif
default:
return availableInputModes;
}
// Add exclusive input modes
if (inputMethodHints.testFlag(Qt::ImhDialableCharactersOnly) || inputMethodHints.testFlag(Qt::ImhDigitsOnly)) {
availableInputModes.append(InputEngine::Dialable);
} else if (inputMethodHints.testFlag(Qt::ImhFormattedNumbersOnly)) {
availableInputModes.append(InputEngine::Numeric);
} else if (inputMethodHints.testFlag(Qt::ImhLatinOnly)) {
availableInputModes.append(InputEngine::Latin);
} else {
// Add other input modes
Q_ASSERT(!availableInputModes.isEmpty());
if (!availableInputModes.contains(InputEngine::Latin))
availableInputModes.append(InputEngine::Latin);
availableInputModes.append(InputEngine::Numeric);
}
return availableInputModes;
}
bool T9WriteInputMethod::setInputMode(const QString &locale, InputEngine::InputMode inputMode)
{
Q_D(T9WriteInputMethod);
d->select();
return d->setInputMode(QLocale(locale), inputMode);
}
1611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680
bool T9WriteInputMethod::setTextCase(InputEngine::TextCase textCase)
{
Q_D(T9WriteInputMethod);
d->textCase = textCase;
return true;
}
bool T9WriteInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::KeyboardModifiers modifiers)
{
Q_UNUSED(text)
Q_UNUSED(modifiers)
Q_D(T9WriteInputMethod);
switch (key) {
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Tab:
case Qt::Key_Space:
d->select();
update();
break;
case Qt::Key_Backspace:
{
InputContext *ic = inputContext();
QString preeditText = ic->preeditText();
if (preeditText.length() > 1) {
preeditText.chop(1);
ic->setPreeditText(preeditText);
// WA: T9Write CJK may crash in some cases with long stringStart.
// Therefore we commit the current input and finish the recognition.
if (d->cjk) {
ic->commit();
d->finishRecognition();
return true;
}
d->caseFormatter.ensureLength(preeditText.length(), d->textCase);
T9WriteCaseFormatter caseFormatter(d->caseFormatter);
d->finishRecognition(false);
d->caseFormatter = caseFormatter;
d->stringStart = preeditText;
d->wordCandidates.append(preeditText);
d->activeWordIndex = 0;
emit selectionListChanged(SelectionListModel::WordCandidateList);
emit selectionListActiveItemChanged(SelectionListModel::WordCandidateList, d->activeWordIndex);
return true;
} else {
bool result = !preeditText.isEmpty();
if (result)
ic->clear();
else
result = !d->scrResult.isEmpty();
d->finishRecognition();
return result;
}
break;
}
default:
if (d->sessionSettings.recognitionMode == mcrMode && text.length() > 0) {
InputContext *ic = inputContext();
QString preeditText = ic->preeditText();
QChar c = text.at(0);
bool addToWord = d->isValidInputChar(c) && (!preeditText.isEmpty() || !d->isJoiner(c));
if (addToWord) {
preeditText.append(text);
ic->setPreeditText(preeditText);
d->caseFormatter.ensureLength(preeditText.length(), d->textCase);
T9WriteCaseFormatter caseFormatter(d->caseFormatter);
d->finishRecognition(false);
1681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750
d->caseFormatter = caseFormatter;
d->stringStart = preeditText;
d->wordCandidates.append(preeditText);
d->activeWordIndex = 0;
emit selectionListChanged(SelectionListModel::WordCandidateList);
emit selectionListActiveItemChanged(SelectionListModel::WordCandidateList, d->activeWordIndex);
return true;
} else {
ic->clear();
d->finishRecognition();
}
break;
} else if (d->sessionSettings.recognitionMode == scrMode) {
d->finishRecognition();
}
}
return false;
}
void T9WriteInputMethod::reset()
{
Q_D(T9WriteInputMethod);
d->finishRecognition();
d->setInputMode(QLocale(inputContext()->locale()), inputEngine()->inputMode());
}
void T9WriteInputMethod::update()
{
Q_D(T9WriteInputMethod);
if (d->ignoreUpdate)
return;
d->select();
}
QList<SelectionListModel::Type> T9WriteInputMethod::selectionLists()
{
return QList<SelectionListModel::Type>() << SelectionListModel::WordCandidateList;
}
int T9WriteInputMethod::selectionListItemCount(SelectionListModel::Type type)
{
Q_UNUSED(type)
Q_D(T9WriteInputMethod);
return d->wordCandidates.count();
}
QVariant T9WriteInputMethod::selectionListData(SelectionListModel::Type type, int index, int role)
{
QVariant result;
Q_UNUSED(type)
Q_D(T9WriteInputMethod);
switch (role) {
case SelectionListModel::DisplayRole:
result = QVariant(d->wordCandidates.at(index));
break;
case SelectionListModel::WordCompletionLengthRole:
result.setValue(0);
break;
default:
result = AbstractInputMethod::selectionListData(type, index, role);
break;
}
return result;
}
void T9WriteInputMethod::selectionListItemSelected(SelectionListModel::Type type, int index)
{
Q_UNUSED(type)
Q_D(T9WriteInputMethod);
d->select(index);
1751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820
}
QList<InputEngine::PatternRecognitionMode> T9WriteInputMethod::patternRecognitionModes() const
{
return QList<InputEngine::PatternRecognitionMode>()
<< InputEngine::HandwritingRecoginition;
}
Trace *T9WriteInputMethod::traceBegin(int traceId, InputEngine::PatternRecognitionMode patternRecognitionMode,
const QVariantMap &traceCaptureDeviceInfo, const QVariantMap &traceScreenInfo)
{
Q_D(T9WriteInputMethod);
return d->traceBegin(traceId, patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo);
}
bool T9WriteInputMethod::traceEnd(Trace *trace)
{
Q_D(T9WriteInputMethod);
d->traceEnd(trace);
return true;
}
bool T9WriteInputMethod::reselect(int cursorPosition, const InputEngine::ReselectFlags &reselectFlags)
{
Q_D(T9WriteInputMethod);
if (d->sessionSettings.recognitionMode == scrMode)
return false;
InputContext *ic = inputContext();
if (!ic)
return false;
const InputEngine::InputMode inputMode = inputEngine()->inputMode();
const int maxLength = (inputMode == InputEngine::ChineseHandwriting ||
inputMode == InputEngine::JapaneseHandwriting ||
inputMode == InputEngine::KoreanHandwriting) ? 0 : 32;
const QString surroundingText = ic->surroundingText();
int replaceFrom = 0;
if (reselectFlags.testFlag(InputEngine::WordBeforeCursor)) {
for (int i = cursorPosition - 1; i >= 0 && d->stringStart.length() < maxLength; --i) {
QChar c = surroundingText.at(i);
if (!d->isValidInputChar(c))
break;
d->stringStart.insert(0, c);
--replaceFrom;
}
while (replaceFrom < 0 && d->isJoiner(d->stringStart.at(0))) {
d->stringStart.remove(0, 1);
++replaceFrom;
}
}
if (reselectFlags.testFlag(InputEngine::WordAtCursor) && replaceFrom == 0) {
d->stringStart.clear();
return false;
}
if (reselectFlags.testFlag(InputEngine::WordAfterCursor)) {
for (int i = cursorPosition; i < surroundingText.length() && d->stringStart.length() < maxLength; ++i) {
QChar c = surroundingText.at(i);
if (!d->isValidInputChar(c))
break;
d->stringStart.append(c);
}
while (replaceFrom > -d->stringStart.length()) {
int lastPos = d->stringStart.length() - 1;
1821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890
if (!d->isJoiner(d->stringStart.at(lastPos)))
break;
d->stringStart.remove(lastPos, 1);
}
}
if (d->stringStart.isEmpty())
return false;
if (reselectFlags.testFlag(InputEngine::WordAtCursor) && replaceFrom == -d->stringStart.length() && d->stringStart.length() < maxLength) {
d->stringStart.clear();
return false;
}
if (d->isJoiner(d->stringStart.at(0))) {
d->stringStart.clear();
return false;
}
if (d->isJoiner(d->stringStart.at(d->stringStart.length() - 1))) {
d->stringStart.clear();
return false;
}
ic->setPreeditText(d->stringStart, QList<QInputMethodEvent::Attribute>(), replaceFrom, d->stringStart.length());
for (int i = 0; i < d->stringStart.length(); ++i)
d->caseFormatter.ensureLength(i + 1, d->stringStart.at(i).isUpper() ? InputEngine::Upper : InputEngine::Lower);
d->wordCandidates.append(d->stringStart);
d->activeWordIndex = 0;
emit selectionListChanged(SelectionListModel::WordCandidateList);
emit selectionListActiveItemChanged(SelectionListModel::WordCandidateList, d->activeWordIndex);
return true;
}
void T9WriteInputMethod::timerEvent(QTimerEvent *timerEvent)
{
Q_D(T9WriteInputMethod);
int timerId = timerEvent->timerId();
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethod::timerEvent():" << timerId;
if (timerId == d->resultTimer) {
if (d->sessionSettings.recognitionMode == mcrMode) {
d->stopResultTimer();
#ifndef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
const InputEngine::InputMode inputMode = inputEngine()->inputMode();
if (inputMode != InputEngine::ChineseHandwriting &&
inputMode != InputEngine::JapaneseHandwriting &&
inputMode != InputEngine::KoreanHandwriting) {
d->clearTraces();
}
#endif
} else if (d->sessionSettings.recognitionMode == scrMode) {
d->select();
}
}
}
void T9WriteInputMethod::dictionaryLoadCompleted(QSharedPointer<T9WriteDictionary> dictionary)
{
Q_D(T9WriteInputMethod);
// Note: This method is called in worker thread context
QMutexLocker dictionaryGuard(&d->dictionaryLock);
if (!dictionary)
return;
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethod::dictionaryLoadCompleted():"
<< dictionary->fileName() << dictionary->data() << dictionary->size();
InputContext *ic = inputContext();
18911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934
if (ic && dictionary->fileName() == d->dictionaryFileName) {
d->loadedDictionary = dictionary;
if (d->sessionSettings.recognitionMode == mcrMode &&
!ic->inputMethodHints().testFlag(Qt::ImhNoPredictiveText) &&
!d->attachedDictionary) {
if (d->attachDictionary(d->loadedDictionary))
d->attachedDictionary = d->loadedDictionary;
}
}
}
void T9WriteInputMethod::resultsAvailable(const QVariantList &resultList)
{
#ifdef QT_VIRTUALKEYBOARD_DEBUG
{
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethod::resultsAvailable():";
for (int i = 0; i < resultList.size(); i++) {
QVariantMap result = resultList.at(i).toMap();
QString resultPrint = QString("%1: ").arg(i + 1);
QString resultChars = result.value("chars").toString();
if (!resultChars.isEmpty())
resultPrint.append(resultChars);
if (result.contains("gesture")) {
if (!resultChars.isEmpty())
resultPrint.append(", ");
resultPrint.append("gesture = 0x");
resultPrint.append(result["gesture"].toString().toUtf8().toHex());
}
VIRTUALKEYBOARD_DEBUG() << resultPrint.toUtf8().constData();
}
}
#endif
Q_D(T9WriteInputMethod);
d->resultsAvailable(resultList);
}
void T9WriteInputMethod::recognitionError(int status)
{
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethod::recognitionError():" << status;
reset();
}
} // namespace QtVirtualKeyboard