diff options
Diffstat (limited to 'src/plugins/t9write/plugin/t9writeinputmethod.cpp')
-rw-r--r-- | src/plugins/t9write/plugin/t9writeinputmethod.cpp | 2177 |
1 files changed, 0 insertions, 2177 deletions
diff --git a/src/plugins/t9write/plugin/t9writeinputmethod.cpp b/src/plugins/t9write/plugin/t9writeinputmethod.cpp deleted file mode 100644 index 007a1989..00000000 --- a/src/plugins/t9write/plugin/t9writeinputmethod.cpp +++ /dev/null @@ -1,2177 +0,0 @@ -/**************************************************************************** -** -** 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_p.h" -#include <QtVirtualKeyboard/qvirtualkeyboardinputengine.h> -#include <QtVirtualKeyboard/qvirtualkeyboardinputcontext.h> -#include <QtVirtualKeyboard/qvirtualkeyboardtrace.h> -#include "t9writeworker_p.h" -#include <QLoggingCategory> -#include <QDirIterator> -#include <QCryptographicHash> -#include <QTime> -#include <QMetaEnum> -#include <QtVirtualKeyboard/private/handwritinggesturerecognizer_p.h> -#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT -#include <QtVirtualKeyboard/private/unipentrace_p.h> -#include <QStandardPaths> -#endif - -#include "decumaStatus.h" -#include "decumaSymbolCategories.h" -#include "decumaLanguages.h" -#include "xxt9wOem.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 - -QT_BEGIN_NAMESPACE -namespace QtVirtualKeyboard { - -Q_LOGGING_CATEGORY(lcT9Write, "qt.virtualkeyboard.t9write") - -class T9WriteCaseFormatter -{ -public: - T9WriteCaseFormatter() : - preferLowercase(false) - { - } - - void clear() - { - textCaseList.clear(); - } - - void ensureLength(int length, QVirtualKeyboardInputEngine::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; - QVirtualKeyboardInputEngine::TextCase textCase = QVirtualKeyboardInputEngine::TextCase::Lower; - for (int i = 0; i < str.length(); ++i) { - if (i < textCaseList.length()) - textCase = textCaseList.at(i); - result.append(textCase == QVirtualKeyboardInputEngine::TextCase::Upper ? str.at(i).toUpper() : (preferLowercase ? str.at(i).toLower() : str.at(i))); - } - return result; - } - - bool preferLowercase; - -private: - QList<QVirtualKeyboardInputEngine::TextCase> textCaseList; -}; - -class T9WriteInputMethodPrivate -{ - Q_DECLARE_PUBLIC(T9WriteInputMethod) -public: - T9WriteInputMethodPrivate(T9WriteInputMethod *q_ptr) : - q_ptr(q_ptr), - cjk(false), - engineMode(T9WriteInputMethod::EngineMode::Uninitialized), - defaultHwrDbPath(QLatin1String(":/QtQuick/VirtualKeyboard/T9Write/data/")), - defaultDictionaryDbPath(defaultHwrDbPath), - traceListHardLimit(32), - attachedDictionary(nullptr), - resultId(0), - lastResultId(0), - resultTimer(0), - decumaSession(nullptr), - activeWordIndex(-1), - arcAdditionStarted(false), - ignoreUpdate(false), - textCase(QVirtualKeyboardInputEngine::TextCase::Lower) -#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT - , unipenTrace() -#endif - { - } - - 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); - 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 - - static const char *engineModeToString(T9WriteInputMethod::EngineMode mode) - { - return QMetaEnum::fromType<T9WriteInputMethod::EngineMode>().key(static_cast<int>(mode)); - } - - bool initEngine(T9WriteInputMethod::EngineMode newEngineMode) - { - if (engineMode == newEngineMode) - return engineMode != T9WriteInputMethod::EngineMode::Uninitialized; - - qCDebug(lcT9Write) << "T9WriteInputMethodPrivate::initEngine()" << engineModeToString(newEngineMode); - - if (decumaSession) - exitEngine(); - - if (newEngineMode == T9WriteInputMethod::EngineMode::Uninitialized) - return false; - - switch (newEngineMode) { - case T9WriteInputMethod::EngineMode::Alphabetic: - case T9WriteInputMethod::EngineMode::Arabic: - case T9WriteInputMethod::EngineMode::Hebrew: - case T9WriteInputMethod::EngineMode::Thai: - cjk = false; - break; - case T9WriteInputMethod::EngineMode::SimplifiedChinese: - case T9WriteInputMethod::EngineMode::TraditionalChinese: - case T9WriteInputMethod::EngineMode::HongKongChinese: - case T9WriteInputMethod::EngineMode::Japanese: - case T9WriteInputMethod::EngineMode::Korean: - cjk = true; - break; - default: - Q_ASSERT(0 && "Invalid T9WriteInputMethod::EngineMode!"); - return false; - } - engineMode = newEngineMode; - - memset(&sessionSettings, 0, sizeof(sessionSettings)); - - QString hwrDb = findHwrDb(engineMode, defaultHwrDbPath); - hwrDbFile.setFileName(hwrDb); - if (!hwrDbFile.open(QIODevice::ReadOnly)) { - qCCritical(lcT9Write) << "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) { - qCCritical(lcT9Write) << "Could not read HWR database" << hwrDb; - exitEngine(); - return false; - } - - symbolCategories.append(DECUMA_CATEGORY_ANSI); - languageCategories.append(DECUMA_LANG_EN); - - sessionSettings.recognitionMode = mcrMode; - 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() : nullptr); - - DECUMA_STATUS status = DECUMA_API(BeginSession)(decumaSession, &sessionSettings, &memFuncs); - Q_ASSERT(status == decumaNoError); - if (status != decumaNoError) { - qCCritical(lcT9Write) << "Could not initialize 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(); - - Q_Q(T9WriteInputMethod); - processResultConnection = QObject::connect(q, &T9WriteInputMethod::resultListChanged, q, &T9WriteInputMethod::processResult, Qt::QueuedConnection); - - return true; - } - - void exitEngine() - { - qCDebug(lcT9Write) << "T9WriteInputMethodPrivate::exitEngine()"; - - if (processResultConnection) - QObject::disconnect(processResultConnection); - - 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 = nullptr; - session.clear(); - } - - memset(&sessionSettings, 0, sizeof(sessionSettings)); - - symbolCategories.clear(); - languageCategories.clear(); - - engineMode = T9WriteInputMethod::EngineMode::Uninitialized; - cjk = false; - } - - QString findHwrDb(T9WriteInputMethod::EngineMode mode, const QString &dir) const - { - QString hwrDbPath(dir); - switch (mode) { - case T9WriteInputMethod::EngineMode::Alphabetic: -#if T9WRITEAPIMAJORVERNUM >= 21 - hwrDbPath.append(QLatin1String("hwrDB_le.bin")); -#else - hwrDbPath.append(QLatin1String("_databas_le.bin")); -#endif - break; - case T9WriteInputMethod::EngineMode::Arabic: -#if T9WRITEAPIMAJORVERNUM >= 21 - hwrDbPath.append(QLatin1String("arabic/hwrDB_le.bin")); -#else - hwrDbPath.append(QLatin1String("arabic/_databas_le.bin")); -#endif - break; - case T9WriteInputMethod::EngineMode::Hebrew: -#if T9WRITEAPIMAJORVERNUM >= 21 - hwrDbPath.append(QLatin1String("hebrew/hwrDB_le.bin")); -#else - hwrDbPath.append(QLatin1String("hebrew/_databas_le.bin")); -#endif - break; - case T9WriteInputMethod::EngineMode::Thai: -#if T9WRITEAPIMAJORVERNUM >= 21 - hwrDbPath.append(QLatin1String("thai/hwrDB_le.bin")); -#else - hwrDbPath.append(QLatin1String("thai/_databas_le.bin")); -#endif - break; - case T9WriteInputMethod::EngineMode::SimplifiedChinese: - hwrDbPath.append(QLatin1String("cjk_S_gb18030_le.hdb")); - break; - case T9WriteInputMethod::EngineMode::TraditionalChinese: - hwrDbPath.append(QLatin1String("cjk_T_std_le.hdb")); - break; - case T9WriteInputMethod::EngineMode::HongKongChinese: - hwrDbPath.append(QLatin1String("cjk_HK_std_le.hdb")); - break; - case T9WriteInputMethod::EngineMode::Japanese: - hwrDbPath.append(QLatin1String("cjk_J_std_le.hdb")); - break; - case T9WriteInputMethod::EngineMode::Korean: - hwrDbPath.append(QLatin1String("cjk_K_mkt_le.hdb")); - break; - default: - return QString(); - } - if (!QFileInfo::exists(hwrDbPath)) { - qCCritical(lcT9Write) << "Could not find HWR database for" << engineModeToString(mode); - return QString(); - } - return hwrDbPath; - } - - QString findDictionary(const QString &dir, const QLocale &locale, DECUMA_SRC_DICTIONARY_TYPE &srcType) - { - srcType = numberOfSrcDictionaryTypes; - - QStringList languageCountry = locale.name().split(QLatin1String("_")); - if (languageCountry.length() != 2) - return QString(); - const QString language = languageCountry[0].toUpper(); - - QString dictionary; - QDirIterator it(dir, QDirIterator::NoIteratorFlags); - while (it.hasNext()) { - QString fileEntry = it.next(); - const QFileInfo fileInfo(fileEntry); - - if (fileInfo.isDir()) - continue; - - const QString fileName(fileInfo.fileName()); - if (!fileName.startsWith(language) && - !fileName.startsWith(QLatin1String("zzEval_") + language)) - continue; - - if (fileEntry.endsWith(QLatin1String(".ldb"))) { -#if T9WRITEAPIMAJORVERNUM >= 20 - qCCritical(lcT9Write) << "Incompatible dictionary" << fileEntry; - continue; -#else - srcType = decumaXT9LDB; -#endif - } else if (fileEntry.endsWith(QLatin1String(".phd"))) { -#if T9WRITEAPIMAJORVERNUM >= 20 - srcType = decumaPortableHWRDictionary; -#else - qCCritical(lcT9Write) << "Incompatible dictionary" << fileEntry; - continue; -#endif - } else { - qCCritical(lcT9Write) << "Incompatible dictionary" << fileEntry; - continue; - } - - dictionary = fileEntry; - break; - } - - return dictionary; - } - - bool attachDictionary(const QSharedPointer<T9WriteDictionary> &dictionary) - { - const std::lock_guard<QRecursiveMutex> dictionaryGuard(dictionaryLock); - Q_ASSERT(decumaSession != nullptr); - Q_ASSERT(dictionary != nullptr); - qCDebug(lcT9Write) << "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) - { - const std::lock_guard<QRecursiveMutex> dictionaryGuard(dictionaryLock); - if (!dictionary) - return; - - qCDebug(lcT9Write) << "T9WriteInputMethodPrivate::detachDictionary():" << dictionary->fileName(); - - Q_ASSERT(decumaSession != nullptr); - DECUMA_STATUS status = DECUMA_API(DetachDictionary)(decumaSession, dictionary->data()); - Q_UNUSED(status); - Q_ASSERT(status == decumaNoError); - } - - bool setInputMode(const QLocale &locale, QVirtualKeyboardInputEngine::InputMode inputMode) - { - Q_Q(T9WriteInputMethod); - qCDebug(lcT9Write) << "T9WriteInputMethodPrivate::setInputMode():" << locale << inputMode; - - finishRecognition(); - - DECUMA_UINT32 language = mapToDecumaLanguage(locale, inputMode); - if (language == DECUMA_LANG_GSMDEFAULT) { - qCCritical(lcT9Write) << "Language is not supported" << locale.name(); - return false; - } - - if (!initEngine(mapLocaleToEngineMode(locale, language))) - return false; - - int isLanguageSupported = 0; - DECUMA_API(DatabaseIsLanguageSupported)(sessionSettings.pStaticDB, language, &isLanguageSupported); - if (!isLanguageSupported) { - qCCritical(lcT9Write) << "Language is not supported" << 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) - 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 : leftToRight; - - // Enable multi-threaded recognition if available. -#ifdef DECUMA_USE_MULTI_THREAD - // Note: This feature requires T9 Write v8.0.0 or later, - // and feature enabled in the SDK. - sessionSettings.nMaxThreads = qMax(QThread::idealThreadCount(), 0); -#endif - - qCDebug(lcT9Write) << " -> language categories:" << languageCategories; - qCDebug(lcT9Write) << " -> symbol categories:" << symbolCategories; - qCDebug(lcT9Write) << " -> 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; - } - - T9WriteInputMethod::EngineMode mapLocaleToEngineMode(const QLocale &locale, DECUMA_UINT32 language = 0) - { -#ifdef HAVE_T9WRITE_CJK - switch (locale.language()) { - case QLocale::Chinese: { - if (locale.script() == QLocale::TraditionalChineseScript) - return locale.country() == QLocale::HongKong ? T9WriteInputMethod::EngineMode::HongKongChinese : T9WriteInputMethod::EngineMode::TraditionalChinese; - return T9WriteInputMethod::EngineMode::SimplifiedChinese; - break; - } - case QLocale::Japanese: - return T9WriteInputMethod::EngineMode::Japanese; - break; - case QLocale::Korean: - return T9WriteInputMethod::EngineMode::Korean; - default: - break; - } -#else - Q_UNUSED(locale); - Q_UNUSED(language); -#endif - -#ifdef HAVE_T9WRITE_ALPHABETIC - switch (locale.script()) { - case QLocale::ArabicScript: - return T9WriteInputMethod::EngineMode::Arabic; - case QLocale::HebrewScript: - return T9WriteInputMethod::EngineMode::Hebrew; - case QLocale::ThaiScript: - return language == DECUMA_LANG_EN ? T9WriteInputMethod::EngineMode::Alphabetic - : T9WriteInputMethod::EngineMode::Thai; - default: - return T9WriteInputMethod::EngineMode::Alphabetic; - } -#else - return T9WriteInputMethod::EngineMode::Uninitialized; -#endif - } - - DECUMA_UINT32 mapToDecumaLanguage(const QLocale &locale, QVirtualKeyboardInputEngine::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 - 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 - 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 != QVirtualKeyboardInputEngine::InputMode::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 != QVirtualKeyboardInputEngine::InputMode::JapaneseHandwriting) - language = DECUMA_LANG_EN; - } else if (language == DECUMA_LANG_KO) { - if (inputMode != QVirtualKeyboardInputEngine::InputMode::KoreanHandwriting) - language = DECUMA_LANG_EN; - } else if (language == DECUMA_LANG_SRCY) { - if (inputMode != QVirtualKeyboardInputEngine::InputMode::Cyrillic) - language = DECUMA_LANG_SRLA; - } else if (language == DECUMA_LANG_AR || language == DECUMA_LANG_FA) { - if (inputMode != QVirtualKeyboardInputEngine::InputMode::Arabic && inputMode != QVirtualKeyboardInputEngine::InputMode::Numeric) - language = DECUMA_LANG_EN; - } else if (language == DECUMA_LANG_IW) { - if (inputMode != QVirtualKeyboardInputEngine::InputMode::Hebrew) - language = DECUMA_LANG_EN; - } else if (language == DECUMA_LANG_TH) { - if (inputMode != QVirtualKeyboardInputEngine::InputMode::Thai) - language = DECUMA_LANG_EN; - } - - return language; - } - - void updateRecognitionMode(DECUMA_UINT32 language, const QLocale &locale, - QVirtualKeyboardInputEngine::InputMode inputMode) - { - Q_Q(T9WriteInputMethod); - Q_UNUSED(language); - Q_UNUSED(locale); - - // Select recognition mode - // Note: MCR mode is preferred, as it does not require recognition - // timer and provides better user experience. - sessionSettings.recognitionMode = mcrMode; - - // T9 Write Alphabetic v8.0.0 supports UCR mode for specific languages -#if T9WRITEAPIMAJORVERNUM >= 21 - if (!cjk) { - switch (inputMode) { - case QVirtualKeyboardInputEngine::InputMode::Latin: - switch (language) { - case DECUMA_LANG_EN: - case DECUMA_LANG_FR: - case DECUMA_LANG_IT: - case DECUMA_LANG_DE: - case DECUMA_LANG_ES: - sessionSettings.recognitionMode = ucrMode; - break; - default: - break; - } - break; - case QVirtualKeyboardInputEngine::InputMode::Arabic: - sessionSettings.recognitionMode = ucrMode; - break; - default: - break; - } - } -#endif - - // Use scrMode with hidden text or with no predictive mode - if (inputMode != QVirtualKeyboardInputEngine::InputMode::ChineseHandwriting && - inputMode != QVirtualKeyboardInputEngine::InputMode::JapaneseHandwriting && - inputMode != QVirtualKeyboardInputEngine::InputMode::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, - QVirtualKeyboardInputEngine::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 QVirtualKeyboardInputEngine::InputMode::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 || - language == DECUMA_LANG_MS || language == DECUMA_LANG_IN) - 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); - else if (language == DECUMA_LANG_VI) - symbolCategories.append(DECUMA_CATEGORY_VIETNAMESE_SUPPLEMENTS); - } - break; - - case QVirtualKeyboardInputEngine::InputMode::Numeric: - if (language == DECUMA_LANG_AR || language == DECUMA_LANG_FA) { - symbolCategories.append(DECUMA_CATEGORY_ARABIC_NUM_MODE); - symbolCategories.append(DECUMA_CATEGORY_ARABIC_GESTURES); - leftToRightGestures = false; - break; - } - symbolCategories.append(DECUMA_CATEGORY_DIGIT); - if (!inputMethodHints.testFlag(Qt::ImhDigitsOnly)) - symbolCategories.append(DECUMA_CATEGORY_NUM_SUP); - break; - - case QVirtualKeyboardInputEngine::InputMode::Dialable: - symbolCategories.append(DECUMA_CATEGORY_PHONE_NUMBER); - break; - - case QVirtualKeyboardInputEngine::InputMode::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 QVirtualKeyboardInputEngine::InputMode::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; - - case QVirtualKeyboardInputEngine::InputMode::Arabic: - symbolCategories.append(DECUMA_CATEGORY_ARABIC_ISOLATED_LETTER_MODE); - symbolCategories.append(DECUMA_CATEGORY_ARABIC_GESTURES); - leftToRightGestures = false; - break; - - case QVirtualKeyboardInputEngine::InputMode::Hebrew: - symbolCategories.append(DECUMA_CATEGORY_HEBREW_GL_HEBREW_CURSIVE_MODE); - symbolCategories.append(DECUMA_CATEGORY_HEBREW_GL_HEBREW_LETTERSYMBOLS); - symbolCategories.append(DECUMA_CATEGORY_HEBREW_SHEQEL); - symbolCategories.append(DECUMA_CATEGORY_ARABIC_GESTURES); - leftToRightGestures = false; - break; - - case QVirtualKeyboardInputEngine::InputMode::Thai: - symbolCategories.append(DECUMA_CATEGORY_THAI_BASE); - symbolCategories.append(DECUMA_CATEGORY_THAI_NON_BASE); - break; - - default: - qCCritical(lcT9Write) << "Invalid input mode" << inputMode; - 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, - QVirtualKeyboardInputEngine::InputMode inputMode) - { - Q_ASSERT(cjk); - - symbolCategories.clear(); - - switch (inputMode) { - case QVirtualKeyboardInputEngine::InputMode::Latin: - symbolCategories.append(DECUMA_CATEGORY_ANSI); - symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL); - symbolCategories.append(DECUMA_CATEGORY_PUNCTUATIONS); - break; - - case QVirtualKeyboardInputEngine::InputMode::Numeric: - symbolCategories.append(DECUMA_CATEGORY_DIGIT); - symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL); - symbolCategories.append(DECUMA_CATEGORY_PUNCTUATIONS); - break; - - case QVirtualKeyboardInputEngine::InputMode::Dialable: - symbolCategories.append(DECUMA_CATEGORY_DIGIT); - symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL); - break; - - case QVirtualKeyboardInputEngine::InputMode::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); - 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: - qCCritical(lcT9Write) << "Invalid locale" << locale << "for" << engineModeToString(engineMode); - return false; - } - break; - - case QVirtualKeyboardInputEngine::InputMode::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 QVirtualKeyboardInputEngine::InputMode::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. - */ - const std::lock_guard<QRecursiveMutex> 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) { - 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) { - qCDebug(lcT9Write) << " -> 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 != scrMode && !inputMethodHints.testFlag(Qt::ImhNoPredictiveText) && - loadedDictionary && !attachedDictionary) { - if (attachDictionary(loadedDictionary)) - attachedDictionary = loadedDictionary; - } - } - - QByteArray getContext(QVirtualKeyboardInputEngine::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(QVirtualKeyboardInputEngine::PatternRecognitionMode patternRecognitionMode, - const QVariantMap &traceCaptureDeviceInfo, - const QVariantMap &traceScreenInfo, - const QByteArray &context) - { - Q_UNUSED(patternRecognitionMode); - if (context == currentContext) - return; - currentContext = context; - - qCDebug(lcT9Write) << "T9WriteInputMethodPrivate::setContext():" << QLatin1String((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(QVirtualKeyboardSelectionListModel::Type::WordCandidateList); - emit q->selectionListActiveItemChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList, activeWordIndex); - } - - const int dpi = traceCaptureDeviceInfo.value(QLatin1String("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(QLatin1String("horizontalRulers"), QVariantList()).toList()); - if (horizontalRulers.count() > 2) { - sessionSettings.baseline = horizontalRulers.last().toInt(); - sessionSettings.helpline = 0; - sessionSettings.topline = horizontalRulers.first().toInt(); - sessionSettings.supportLineSet = baselineAndTopline; - sessionSettings.UIInputGuide = supportlines; - } else if (horizontalRulers.count() == 2) { - sessionSettings.baseline = horizontalRulers.last().toInt(); - sessionSettings.helpline = horizontalRulers.first().toInt(); - sessionSettings.topline = 0; - sessionSettings.supportLineSet = baselineAndHelpline; - sessionSettings.UIInputGuide = supportlines; - } else { - sessionSettings.baseline = 0; - sessionSettings.helpline = 0; - sessionSettings.topline = 0; - sessionSettings.supportLineSet = baselineAndHelpline; - sessionSettings.UIInputGuide = none; - } - - DECUMA_STATUS status = DECUMA_API(ChangeSessionSettings)(decumaSession, &sessionSettings); - Q_ASSERT(status == decumaNoError); - } - - QVirtualKeyboardTrace *traceBegin( - int traceId, QVirtualKeyboardInputEngine::PatternRecognitionMode patternRecognitionMode, - const QVariantMap &traceCaptureDeviceInfo, const QVariantMap &traceScreenInfo) - { - if (!worker) - return nullptr; - - // The result id follows the trace id so that the (previous) - // results completed during the handwriting can be rejected. - resultId = traceId; - - stopResultTimer(); - - // Dictionary must be completed before the arc addition can begin - if (dictionaryTask) { - dictionaryTask->wait(); - dictionaryTask.reset(); - } - - // Cancel the current recognition task - worker->removeAllTasks<T9WriteRecognitionResultsTask>(); - worker->removeAllTasks<T9WriteRecognitionTask>(); - if (recognitionTask) { - recognitionTask->cancelRecognition(); - recognitionTask.reset(); - } - -#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT - if (!unipenTrace) - unipenTrace.reset(new UnipenTrace(traceCaptureDeviceInfo, traceScreenInfo)); -#endif - - QByteArray context = getContext(patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo); - if (context != currentContext) { - worker->waitForAllTasks(); - setContext(patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo, context); - } - - DECUMA_STATUS status; - - if (!arcAdditionStarted) { - worker->waitForAllTasks(); - status = DECUMA_API(BeginArcAddition)(decumaSession); - Q_ASSERT(status == decumaNoError); - arcAdditionStarted = true; - } - - QVirtualKeyboardTrace *trace = new QVirtualKeyboardTrace(); -#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT - trace->setChannels(QStringList(QLatin1String("t"))); -#endif - traceList.append(trace); - - return trace; - } - - void traceEnd(QVirtualKeyboardTrace *trace) - { - if (trace->isCanceled()) { - traceList.removeOne(trace); - delete trace; - } else { - if (cjk && countActiveTraces() == 0) { - // For some reason gestures don't seem to work in CJK mode - // Using our own gesture recognizer as fallback - if (handleGesture()) - return; - } - worker->addTask(QSharedPointer<T9WriteAddArcTask>(new T9WriteAddArcTask(trace))); - } - if (!traceList.isEmpty()) { - Q_ASSERT(arcAdditionStarted); - if (countActiveTraces() == 0) - restartRecognition(); - } - } - - int countActiveTraces() const - { - int count = 0; - for (QVirtualKeyboardTrace *trace : qAsConst(traceList)) { - if (!trace->isFinal()) - count++; - } - return count; - } - - void clearTraces() - { - worker->waitForAllTasks(); - qDeleteAll(traceList); - traceList.clear(); - } - - void noteSelected(int index) - { - if (wordCandidatesHwrResultIndex.isEmpty()) - return; - - qCDebug(lcT9Write) << "T9WriteInputMethodPrivate::noteSelected():" << index; - Q_ASSERT(index >= 0 && index < wordCandidatesHwrResultIndex.length()); - int resultIndex = wordCandidatesHwrResultIndex[index]; - DECUMA_API(NoteSelectedCandidate)(decumaSession, resultIndex); - } - - void restartRecognition() - { - qCDebug(lcT9Write) << "T9WriteInputMethodPrivate::restartRecognition()"; - - Q_Q(T9WriteInputMethod); - - worker->removeAllTasks<T9WriteRecognitionResultsTask>(); - if (recognitionTask) { - recognitionTask->cancelRecognition(); - recognitionTask.reset(); - } - - // Boost dictionary words by default - BOOST_LEVEL boostLevel = attachedDictionary ? boostDictWords : noBoost; - - // Disable dictionary boost in UCR mode for URL and E-mail input - // Otherwise it will completely mess input - const Qt::InputMethodHints inputMethodHints = q->inputContext()->inputMethodHints(); - if (sessionSettings.recognitionMode == ucrMode && (inputMethodHints & (Qt::ImhUrlCharactersOnly | Qt::ImhEmailCharactersOnly))) - boostLevel = noBoost; - - QSharedPointer<T9WriteRecognitionResult> recognitionResult(new T9WriteRecognitionResult(resultId, 9, 64)); - recognitionTask.reset(new T9WriteRecognitionTask(recognitionResult, instantGestureSettings, - boostLevel, 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(); - } - - void waitForRecognitionResults() - { - if (!worker) - return; - - qCDebug(lcT9Write) << "T9WriteInputMethodPrivate::waitForRecognitionResults()"; - worker->waitForAllTasks(); - processResult(); - } - - bool finishRecognition(bool emitSelectionListChanged = true) - { - qCDebug(lcT9Write) << "T9WriteInputMethodPrivate::finishRecognition()"; - if (!worker) - return false; - - bool result = !traceList.isEmpty(); - - Q_ASSERT(decumaSession != nullptr); - - stopResultTimer(); - - worker->removeAllTasks<T9WriteAddArcTask>(); - worker->removeAllTasks<T9WriteRecognitionResultsTask>(); - if (recognitionTask) { - recognitionTask->cancelRecognition(); - recognitionTask.reset(); - result = true; - } - - clearTraces(); - - 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(QVirtualKeyboardSelectionListModel::Type::WordCandidateList); - emit q->selectionListActiveItemChanged(QVirtualKeyboardSelectionListModel::Type::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 != scrMode && wordCandidates.isEmpty()) { - finishRecognition(); - return false; - } - if (sessionSettings.recognitionMode == scrMode && scrResult.isEmpty()) { - finishRecognition(); - return false; - } - - qCDebug(lcT9Write) << "T9WriteInputMethodPrivate::select():" << index; - - Q_Q(T9WriteInputMethod); - if (sessionSettings.recognitionMode != scrMode) { - 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 == QVirtualKeyboardInputEngine::TextCase::Upper))) { - QStringList homeLocations = QStandardPaths::standardLocations(QStandardPaths::HomeLocation); - if (!homeLocations.isEmpty()) { - unipenTrace->setDirectory(QStringLiteral("%1/%2").arg(homeLocations.at(0)).arg(QLatin1String("VIRTUAL_KEYBOARD_TRACES"))); - unipenTrace->record(traceList); - unipenTrace->save(ch.unicode(), 100); - } - } - } - } -#endif - - finishRecognition(); - QChar gesture = T9WriteInputMethodPrivate::mapSymbolToGesture(finalWord.right(1).at(0)); - if (!gesture.isNull()) - finalWord.chop(1); - q->inputContext()->commit(finalWord); - applyGesture(gesture); - } 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(int interval = 500) - { - qCDebug(lcT9Write) << "T9WriteInputMethodPrivate::resetResultTimer():" << interval; - Q_Q(T9WriteInputMethod); - stopResultTimer(); - resultTimer = q->startTimer(interval); - } - - void stopResultTimer() - { - if (resultTimer) { - qCDebug(lcT9Write) << "T9WriteInputMethodPrivate::stopResultTimer()"; - Q_Q(T9WriteInputMethod); - q->killTimer(resultTimer); - resultTimer = 0; - } - } - - void processResult() - { - qCDebug(lcT9Write) << "T9WriteInputMethodPrivate::processResult()"; - Q_Q(T9WriteInputMethod); - QVirtualKeyboardInputContext *ic = q->inputContext(); - if (!ic) - return; - - QStringList newWordCandidates; - QList<int> newWordCandidatesHwrResultIndex; - QString resultString; - QString gesture; - QVariantList symbolStrokes; - { - QMutexLocker resultListGuard(&resultListLock); - if (resultList.isEmpty()) - return; - - if (resultList.first().toMap()[QLatin1String("resultId")] != resultId) { - qCDebug(lcT9Write) << "T9WriteInputMethodPrivate::processResult(): resultId mismatch" << resultList.first().toMap()[QLatin1String("resultId")] << "(" << resultId << ")"; - resultList.clear(); - return; - } - lastResultId = resultId; - - for (int i = 0; i < resultList.size(); i++) { - QVariantMap result = resultList.at(i).toMap(); - QString resultChars = result[QLatin1String("chars")].toString(); - if (i == 0) { - if (ic->isShiftActive()) { - caseFormatter.ensureLength(1, textCase); - caseFormatter.ensureLength(resultChars.length(), QVirtualKeyboardInputEngine::TextCase::Lower); - } else { - caseFormatter.ensureLength(resultChars.length(), textCase); - } - } - if (!resultChars.isEmpty()) { - resultChars = caseFormatter.formatString(resultChars); - if (sessionSettings.recognitionMode != scrMode) { - newWordCandidates.append(resultChars); - newWordCandidatesHwrResultIndex.append(i); - } - } - if (i == 0) { - resultString = resultChars; - if (result.contains(QLatin1String("gesture"))) - gesture = result[QLatin1String("gesture")].toString(); - if (sessionSettings.recognitionMode != scrMode && result.contains(QLatin1String("symbolStrokes"))) - symbolStrokes = result[QLatin1String("symbolStrokes")].toList(); - if (sessionSettings.recognitionMode == scrMode) - break; - } else { - // Add a gesture symbol to the secondary candidate - if (sessionSettings.recognitionMode != scrMode && result.contains(QLatin1String("gesture"))) { - QString gesture2 = result[QLatin1String("gesture")].toString(); - if (gesture2.length() == 1) { - QChar symbol = T9WriteInputMethodPrivate::mapGestureToSymbol(gesture2.at(0).unicode()); - if (!symbol.isNull()) { - // Check for duplicates - bool duplicateFound = false; - for (const QString &wordCandidate : newWordCandidates) { - duplicateFound = wordCandidate.size() == 1 && wordCandidate.at(0) == symbol; - if (duplicateFound) - break; - } - if (!duplicateFound) { - if (!resultChars.isEmpty()) { - newWordCandidates.last().append(symbol); - } else { - newWordCandidates.append(symbol); - newWordCandidatesHwrResultIndex.append(i); - } - } - } - } - } - } - } - - resultList.clear(); - } - - bool wordCandidatesChanged = wordCandidates != newWordCandidates; - -#ifndef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT - // Delete trace history - // Note: We have to be sure there are no background tasks - // running since the QVirtualKeyboardTrace objects consumed there. - if (worker->numberOfPendingTasks() == 0) { - - const QVirtualKeyboardInputEngine::InputMode inputMode = q->inputEngine()->inputMode(); - if (sessionSettings.recognitionMode == mcrMode && !symbolStrokes.isEmpty() && - inputMode != QVirtualKeyboardInputEngine::InputMode::ChineseHandwriting && - inputMode != QVirtualKeyboardInputEngine::InputMode::JapaneseHandwriting && - inputMode != QVirtualKeyboardInputEngine::InputMode::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) { - qCDebug(lcT9Write) << "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(); - if (!applyGesture(gestureSymbol)) { - ic->commit(ic->preeditText()); - finishRecognition(); - } - - return; - } - - if (sessionSettings.recognitionMode != scrMode) { - ignoreUpdate = true; - ic->setPreeditText(resultString); - ignoreUpdate = false; - } else { - scrResult = resultString; - } - - if (wordCandidatesChanged) { - wordCandidates = newWordCandidates; - wordCandidatesHwrResultIndex = newWordCandidatesHwrResultIndex; - activeWordIndex = wordCandidates.isEmpty() ? -1 : 0; - emit q->selectionListChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList); - emit q->selectionListActiveItemChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList, activeWordIndex); - } - - if (arcAdditionStarted && traceList.isEmpty() && worker->numberOfPendingTasks() == 0) { - DECUMA_API(EndArcAddition)(decumaSession); - arcAdditionStarted = false; - } - } - - static QChar mapGestureToSymbol(const QChar &gesture) - { - switch (gesture.unicode()) { - case '\r': - return QChar(0x23CE); - case ' ': - return QChar(0x2423); - default: - return QChar(); - } - } - - static QChar mapSymbolToGesture(const QChar &symbol) - { - switch (symbol.unicode()) { - case 0x23CE: - return QLatin1Char('\r'); - case 0x2423: - return QLatin1Char(' '); - default: - return QChar(); - } - } - - bool applyGesture(const QChar &gesture) - { - Q_Q(T9WriteInputMethod); - QVirtualKeyboardInputContext *ic = q->inputContext(); - switch (gesture.unicode()) { - case '\b': - return ic->inputEngine()->virtualKeyClick(Qt::Key_Backspace, QString(), Qt::NoModifier); - case '\r': - return ic->inputEngine()->virtualKeyClick(Qt::Key_Return, QLatin1String("\n"), Qt::NoModifier); - case ' ': - return ic->inputEngine()->virtualKeyClick(Qt::Key_Space, QLatin1String(" "), Qt::NoModifier); - default: - return false; - } - } - - bool handleGesture() - { - if (countActiveTraces() > 0) - return false; - - QVariantMap gesture(gestureRecognizer.recognize(traceList.mid(traceList.length() - 1, 1))); - if (gesture.isEmpty()) - return false; - - qCDebug(lcT9Write) << "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); - QVirtualKeyboardInputContext *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 QVirtualKeyboardInputEngine::InputMode inputMode = q->inputEngine()->inputMode(); - if (inputMode != QVirtualKeyboardInputEngine::InputMode::ChineseHandwriting && - inputMode != QVirtualKeyboardInputEngine::InputMode::JapaneseHandwriting && - inputMode != QVirtualKeyboardInputEngine::InputMode::KoreanHandwriting) { - if (swipeAngle <= SWIPE_ANGLE_THRESHOLD || swipeAngle >= 360 - SWIPE_ANGLE_THRESHOLD) { - if (swipeTouchCount == 1) { - // Single swipe: space - ic->inputEngine()->virtualKeyClick(Qt::Key_Space, QLatin1String(" "), 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(static_cast<const int>(QVirtualKeyboardInputEngine::InputMode::Numeric)); - int indexOfDialableInputMode = inputModes.indexOf(static_cast<const int>(QVirtualKeyboardInputEngine::InputMode::Dialable)); - if (indexOfNumericInputMode != -1 && indexOfDialableInputMode != -1) - inputModes.removeAt(inputMode != QVirtualKeyboardInputEngine::InputMode::Dialable ? - indexOfDialableInputMode : - indexOfNumericInputMode); - if (inputModes.count() > 1) { - int inputModeIndex = inputModes.indexOf(static_cast<const int>(inputMode)) + 1; - if (inputModeIndex >= inputModes.count()) - inputModeIndex = 0; - ic->inputEngine()->setInputMode(static_cast<QVirtualKeyboardInputEngine::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); - QVirtualKeyboardInputContext *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; - bool cjk; - T9WriteInputMethod::EngineMode engineMode; - QByteArray currentContext; - DECUMA_SESSION_SETTINGS sessionSettings; - DECUMA_INSTANT_GESTURE_SETTINGS instantGestureSettings; - QString defaultHwrDbPath; - QString defaultDictionaryDbPath; - QFile hwrDbFile; - QList<DECUMA_UINT32> languageCategories; - QList<DECUMA_UINT32> symbolCategories; - QScopedPointer<T9WriteWorker> worker; - QList<QVirtualKeyboardTrace *> traceList; - int traceListHardLimit; - QRecursiveMutex dictionaryLock; - QString dictionaryFileName; - QSharedPointer<T9WriteDictionary> loadedDictionary; - QSharedPointer<T9WriteDictionary> attachedDictionary; - QSharedPointer<T9WriteDictionaryTask> dictionaryTask; - QSharedPointer<T9WriteRecognitionTask> recognitionTask; - QMutex resultListLock; - QVariantList resultList; - int resultId; - int lastResultId; - int resultTimer; - QMetaObject::Connection processResultConnection; - QByteArray session; - DECUMA_SESSION *decumaSession; - QStringList wordCandidates; - QList<int> wordCandidatesHwrResultIndex; - QString stringStart; - QString scrResult; - int activeWordIndex; - bool arcAdditionStarted; - bool ignoreUpdate; - QVirtualKeyboardInputEngine::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, - nullptr -}; - -/*! - \class QtVirtualKeyboard::T9WriteInputMethod - \internal -*/ - -T9WriteInputMethod::T9WriteInputMethod(QObject *parent) : - QVirtualKeyboardAbstractInputMethod(parent), - d_ptr(new T9WriteInputMethodPrivate(this)) -{ -} - -T9WriteInputMethod::~T9WriteInputMethod() -{ - Q_D(T9WriteInputMethod); - d->exitEngine(); -} - -QList<QVirtualKeyboardInputEngine::InputMode> T9WriteInputMethod::inputModes(const QString &locale) -{ - Q_D(T9WriteInputMethod); - QList<QVirtualKeyboardInputEngine::InputMode> availableInputModes; - const Qt::InputMethodHints inputMethodHints(inputContext()->inputMethodHints()); - const QLocale loc(locale); - T9WriteInputMethod::EngineMode mode = d->mapLocaleToEngineMode(loc); - - // Add primary input mode - switch (mode) { -#ifdef HAVE_T9WRITE_ALPHABETIC - case T9WriteInputMethod::EngineMode::Alphabetic: - if (d->findHwrDb(T9WriteInputMethod::EngineMode::Alphabetic, d->defaultHwrDbPath).isEmpty()) - return availableInputModes; - if (!(inputMethodHints & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly | Qt::ImhLatinOnly))) { - switch (loc.script()) { - case QLocale::GreekScript: - availableInputModes.append(QVirtualKeyboardInputEngine::InputMode::Greek); - break; - case QLocale::CyrillicScript: - availableInputModes.append(QVirtualKeyboardInputEngine::InputMode::Cyrillic); - break; - case QLocale::ThaiScript: - availableInputModes.append(QVirtualKeyboardInputEngine::InputMode::Thai); - break; - default: - break; - } - availableInputModes.append(QVirtualKeyboardInputEngine::InputMode::Latin); - } - break; - case T9WriteInputMethod::EngineMode::Arabic: - if (d->findHwrDb(T9WriteInputMethod::EngineMode::Arabic, d->defaultHwrDbPath).isEmpty()) - return availableInputModes; - if (!(inputMethodHints & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly | Qt::ImhLatinOnly))) - availableInputModes.append(QVirtualKeyboardInputEngine::InputMode::Arabic); - break; - case T9WriteInputMethod::EngineMode::Hebrew: - if (d->findHwrDb(T9WriteInputMethod::EngineMode::Hebrew, d->defaultHwrDbPath).isEmpty()) - return availableInputModes; - if (!(inputMethodHints & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly | Qt::ImhLatinOnly))) - availableInputModes.append(QVirtualKeyboardInputEngine::InputMode::Hebrew); - break; - case T9WriteInputMethod::EngineMode::Thai: - if (d->findHwrDb(T9WriteInputMethod::EngineMode::Thai, d->defaultHwrDbPath).isEmpty()) - return availableInputModes; - if (!(inputMethodHints & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly | Qt::ImhLatinOnly))) - availableInputModes.append(QVirtualKeyboardInputEngine::InputMode::Thai); - break; -#endif -#ifdef HAVE_T9WRITE_CJK - case T9WriteInputMethod::EngineMode::SimplifiedChinese: - case T9WriteInputMethod::EngineMode::TraditionalChinese: - case T9WriteInputMethod::EngineMode::HongKongChinese: - if (d->findHwrDb(mode, d->defaultHwrDbPath).isEmpty()) - return availableInputModes; - if (!(inputMethodHints & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly | Qt::ImhLatinOnly))) - availableInputModes.append(QVirtualKeyboardInputEngine::InputMode::ChineseHandwriting); - break; - case T9WriteInputMethod::EngineMode::Japanese: - if (d->findHwrDb(T9WriteInputMethod::EngineMode::Japanese, d->defaultHwrDbPath).isEmpty()) - return availableInputModes; - if (!(inputMethodHints & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly | Qt::ImhLatinOnly))) - availableInputModes.append(QVirtualKeyboardInputEngine::InputMode::JapaneseHandwriting); - break; - case T9WriteInputMethod::EngineMode::Korean: - if (d->findHwrDb(T9WriteInputMethod::EngineMode::Korean, d->defaultHwrDbPath).isEmpty()) - return availableInputModes; - if (!(inputMethodHints & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly | Qt::ImhLatinOnly))) - availableInputModes.append(QVirtualKeyboardInputEngine::InputMode::KoreanHandwriting); - break; -#endif - default: - return availableInputModes; - } - - // Add exclusive input modes - if (inputMethodHints.testFlag(Qt::ImhDialableCharactersOnly) || inputMethodHints.testFlag(Qt::ImhDigitsOnly)) { - availableInputModes.append(QVirtualKeyboardInputEngine::InputMode::Dialable); - } else if (inputMethodHints.testFlag(Qt::ImhFormattedNumbersOnly)) { - availableInputModes.append(QVirtualKeyboardInputEngine::InputMode::Numeric); - } else if (inputMethodHints.testFlag(Qt::ImhLatinOnly)) { - availableInputModes.append(QVirtualKeyboardInputEngine::InputMode::Latin); - } else { - // Add other input modes - Q_ASSERT(!availableInputModes.isEmpty()); - if (!availableInputModes.contains(QVirtualKeyboardInputEngine::InputMode::Latin)) - availableInputModes.append(QVirtualKeyboardInputEngine::InputMode::Latin); - availableInputModes.append(QVirtualKeyboardInputEngine::InputMode::Numeric); - } - - return availableInputModes; -} - -bool T9WriteInputMethod::setInputMode(const QString &locale, QVirtualKeyboardInputEngine::InputMode inputMode) -{ - Q_D(T9WriteInputMethod); - d->select(); - return d->setInputMode(QLocale(locale), inputMode); -} - -bool T9WriteInputMethod::setTextCase(QVirtualKeyboardInputEngine::TextCase textCase) -{ - Q_D(T9WriteInputMethod); - d->textCase = textCase; - return true; -} - -bool T9WriteInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::KeyboardModifiers modifiers) -{ - 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: - { - QVirtualKeyboardInputContext *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) { - d->waitForRecognitionResults(); - 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(QVirtualKeyboardSelectionListModel::Type::WordCandidateList); - emit selectionListActiveItemChanged(QVirtualKeyboardSelectionListModel::Type::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 != scrMode && text.length() > 0) { - d->waitForRecognitionResults(); - QVirtualKeyboardInputContext *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); - d->caseFormatter = caseFormatter; - d->stringStart = preeditText; - d->wordCandidates.append(preeditText); - d->activeWordIndex = 0; - emit selectionListChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList); - emit selectionListActiveItemChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList, d->activeWordIndex); - return true; - } else { - ic->commit(); - 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<QVirtualKeyboardSelectionListModel::Type> T9WriteInputMethod::selectionLists() -{ - return QList<QVirtualKeyboardSelectionListModel::Type>() << QVirtualKeyboardSelectionListModel::Type::WordCandidateList; -} - -int T9WriteInputMethod::selectionListItemCount(QVirtualKeyboardSelectionListModel::Type type) -{ - Q_UNUSED(type); - Q_D(T9WriteInputMethod); - return d->wordCandidates.count(); -} - -QVariant T9WriteInputMethod::selectionListData(QVirtualKeyboardSelectionListModel::Type type, int index, QVirtualKeyboardSelectionListModel::Role role) -{ - QVariant result; - Q_D(T9WriteInputMethod); - switch (role) { - case QVirtualKeyboardSelectionListModel::Role::Display: - result = QVariant(d->wordCandidates.at(index)); - break; - case QVirtualKeyboardSelectionListModel::Role::WordCompletionLength: - result.setValue(0); - break; - default: - result = QVirtualKeyboardAbstractInputMethod::selectionListData(type, index, role); - break; - } - return result; -} - -void T9WriteInputMethod::selectionListItemSelected(QVirtualKeyboardSelectionListModel::Type type, int index) -{ - Q_UNUSED(type); - Q_D(T9WriteInputMethod); - d->select(index); -} - -QList<QVirtualKeyboardInputEngine::PatternRecognitionMode> T9WriteInputMethod::patternRecognitionModes() const -{ - return QList<QVirtualKeyboardInputEngine::PatternRecognitionMode>() - << QVirtualKeyboardInputEngine::PatternRecognitionMode::Handwriting; -} - -QVirtualKeyboardTrace *T9WriteInputMethod::traceBegin( - int traceId, QVirtualKeyboardInputEngine::PatternRecognitionMode patternRecognitionMode, - const QVariantMap &traceCaptureDeviceInfo, const QVariantMap &traceScreenInfo) -{ - Q_D(T9WriteInputMethod); - return d->traceBegin(traceId, patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo); -} - -bool T9WriteInputMethod::traceEnd(QVirtualKeyboardTrace *trace) -{ - Q_D(T9WriteInputMethod); - d->traceEnd(trace); - return true; -} - -bool T9WriteInputMethod::reselect(int cursorPosition, const QVirtualKeyboardInputEngine::ReselectFlags &reselectFlags) -{ - Q_D(T9WriteInputMethod); - - if (d->sessionSettings.recognitionMode == scrMode) - return false; - - QVirtualKeyboardInputContext *ic = inputContext(); - if (!ic) - return false; - - const QVirtualKeyboardInputEngine::InputMode inputMode = inputEngine()->inputMode(); - const int maxLength = (inputMode == QVirtualKeyboardInputEngine::InputMode::ChineseHandwriting || - inputMode == QVirtualKeyboardInputEngine::InputMode::JapaneseHandwriting || - inputMode == QVirtualKeyboardInputEngine::InputMode::KoreanHandwriting) ? 0 : 32; - const QString surroundingText = ic->surroundingText(); - int replaceFrom = 0; - - if (reselectFlags.testFlag(QVirtualKeyboardInputEngine::ReselectFlag::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(QVirtualKeyboardInputEngine::ReselectFlag::WordAtCursor) && replaceFrom == 0) { - d->stringStart.clear(); - return false; - } - - if (reselectFlags.testFlag(QVirtualKeyboardInputEngine::ReselectFlag::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; - if (!d->isJoiner(d->stringStart.at(lastPos))) - break; - d->stringStart.remove(lastPos, 1); - } - } - - if (d->stringStart.isEmpty()) - return false; - - if (reselectFlags.testFlag(QVirtualKeyboardInputEngine::ReselectFlag::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() ? QVirtualKeyboardInputEngine::TextCase::Upper : QVirtualKeyboardInputEngine::TextCase::Lower); - d->wordCandidates.append(d->stringStart); - d->activeWordIndex = 0; - emit selectionListChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList); - emit selectionListActiveItemChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList, d->activeWordIndex); - - return true; -} - -void T9WriteInputMethod::timerEvent(QTimerEvent *timerEvent) -{ - Q_D(T9WriteInputMethod); - int timerId = timerEvent->timerId(); - qCDebug(lcT9Write) << "T9WriteInputMethod::timerEvent():" << timerId; - if (timerId == d->resultTimer) { - d->stopResultTimer(); - - // Ignore if the result is not yet available - if (d->resultId != d->lastResultId) { - qCDebug(lcT9Write) << "T9WriteInputMethod::timerEvent(): Result not yet available"; - return; - } - - if (d->sessionSettings.recognitionMode != scrMode) { -#ifndef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT - // Don't clear traces in UCR mode if dictionary is loaded. - // In UCR mode the whole purpose is to write the word with - // one or few strokes. - if (d->sessionSettings.recognitionMode == ucrMode) { - const std::lock_guard<QRecursiveMutex> dictionaryGuard(d->dictionaryLock); - if (d->attachedDictionary) - return; - } - - const QVirtualKeyboardInputEngine::InputMode inputMode = inputEngine()->inputMode(); - if (inputMode != QVirtualKeyboardInputEngine::InputMode::ChineseHandwriting && - inputMode != QVirtualKeyboardInputEngine::InputMode::JapaneseHandwriting && - inputMode != QVirtualKeyboardInputEngine::InputMode::KoreanHandwriting) { - d->clearTraces(); - } -#endif - } else { - d->select(); - } - } -} - -void T9WriteInputMethod::dictionaryLoadCompleted(QSharedPointer<T9WriteDictionary> dictionary) -{ - Q_D(T9WriteInputMethod); - // Note: This method is called in worker thread context - const std::lock_guard<QRecursiveMutex> dictionaryGuard(d->dictionaryLock); - - if (!dictionary) - return; - - qCDebug(lcT9Write) << "T9WriteInputMethod::dictionaryLoadCompleted():" - << dictionary->fileName() << dictionary->data() << dictionary->size(); - - QVirtualKeyboardInputContext *ic = inputContext(); - if (ic && dictionary->fileName() == d->dictionaryFileName) { - d->loadedDictionary = dictionary; - if (d->sessionSettings.recognitionMode != scrMode && - !ic->inputMethodHints().testFlag(Qt::ImhNoPredictiveText) && - !d->attachedDictionary) { - if (d->attachDictionary(d->loadedDictionary)) - d->attachedDictionary = d->loadedDictionary; - } - } -} - -void T9WriteInputMethod::resultsAvailable(const QVariantList &resultList) -{ -#ifdef SENSITIVE_DEBUG - if (lcT9Write().isDebugEnabled()) { - qCDebug(lcT9Write) << "T9WriteInputMethod::resultsAvailable():"; - for (int i = 0; i < resultList.size(); i++) { - QVariantMap result = resultList.at(i).toMap(); - QString resultPrint = QStringLiteral("%1: ").arg(i + 1); - QString resultChars = result.value(QLatin1String("chars")).toString(); - if (!resultChars.isEmpty()) - resultPrint.append(resultChars); - if (result.contains(QLatin1String("gesture"))) { - if (!resultChars.isEmpty()) - resultPrint.append(QLatin1String(", ")); - QString gesture = result[QLatin1String("gesture")].toString(); - resultPrint.append(QLatin1String("gesture =")); - for (const QChar &chr : gesture) { - resultPrint.append(QString::fromLatin1(" 0x%1").arg(chr.unicode(), 0, 16)); - } - } - qCDebug(lcT9Write) << resultPrint.toUtf8().constData(); - } - } -#endif - Q_D(T9WriteInputMethod); - QMutexLocker resultListGuard(&d->resultListLock); - d->resultList = resultList; - emit resultListChanged(); -} - -void T9WriteInputMethod::processResult() -{ - Q_D(T9WriteInputMethod); - bool resultTimerWasRunning = d->resultTimer != 0; - - d->processResult(); - - // Restart the result timer now if it stopped before the results were completed - if (!resultTimerWasRunning && (!d->scrResult.isEmpty() || !d->wordCandidates.isEmpty())) - d->resetResultTimer(0); - -} - -void T9WriteInputMethod::recognitionError(int status) -{ - qCDebug(lcT9Write) << "T9WriteInputMethod::recognitionError():" << status; - reset(); -} - -} // namespace QtVirtualKeyboard -QT_END_NAMESPACE |