aboutsummaryrefslogtreecommitdiffstats
path: root/src/virtualkeyboard/t9writeinputmethod.cpp
diff options
context:
space:
mode:
authorJarkko Koivikko <jarkko.koivikko@code-q.fi>2015-09-08 10:31:55 +0300
committerJarkko Koivikko <jarkko.koivikko@code-q.fi>2015-10-07 12:53:39 +0000
commit14be4edfcdc2fcaf23c70fbd6de8952c53702aba (patch)
tree3371cbef2dbfc20ca35839beb45d07debab95348 /src/virtualkeyboard/t9writeinputmethod.cpp
parent3c30498468b31cfc9e129e306643000e3257c4a4 (diff)
Add T9Write implementation of HandwritingInputMethod
This change adds T9Write implementation of HandwritingInputMethod. To use the T9Write, the contents of T9Write sdk must be extracted to srv/virtualkeyboard/3rdparty/t9write directory and the qmake command line must contain CONFIG+=t9write. Change-Id: Ib56d1d3dc553bb5d5677ab03e213dc8fed43ac68 Reviewed-by: Mitch Curtis <mitch.curtis@theqtcompany.com>
Diffstat (limited to 'src/virtualkeyboard/t9writeinputmethod.cpp')
-rw-r--r--src/virtualkeyboard/t9writeinputmethod.cpp1192
1 files changed, 1192 insertions, 0 deletions
diff --git a/src/virtualkeyboard/t9writeinputmethod.cpp b/src/virtualkeyboard/t9writeinputmethod.cpp
new file mode 100644
index 00000000..db71793e
--- /dev/null
+++ b/src/virtualkeyboard/t9writeinputmethod.cpp
@@ -0,0 +1,1192 @@
+/******************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://qt.io
+**
+** This file is part of the Qt Virtual Keyboard module.
+**
+** Licensees holding valid commercial license for Qt may use this file in
+** accordance with the Qt License Agreement provided with the Software
+** or, alternatively, in accordance with the terms contained in a written
+** agreement between you and The Qt Company.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.io
+**
+******************************************************************************/
+
+#include "t9writeinputmethod.h"
+#include "declarativeinputengine.h"
+#include "declarativeinputcontext.h"
+#include "declarativetrace.h"
+#include "t9writeworker.h"
+#include "virtualkeyboarddebug.h"
+#include "QDirIterator"
+#ifdef QT_VIRTUALKEYBOARD_DEBUG
+#include <QTime>
+#endif
+
+#include "decuma_hwr.h"
+#include "decumaStatus.h"
+#include "decumaSymbolCategories.h"
+#include "decumaLanguages.h"
+#include "xxt9wOem.h"
+
+class T9WriteCaseFormatter
+{
+public:
+ T9WriteCaseFormatter() :
+ preferLowercase(false)
+ {
+ }
+
+ void clear()
+ {
+ textCaseList.clear();
+ }
+
+ void ensureLength(int length, DeclarativeInputEngine::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;
+ DeclarativeInputEngine::TextCase textCase = DeclarativeInputEngine::Lower;
+ for (int i = 0; i < str.length(); ++i) {
+ if (i < textCaseList.length())
+ textCase = textCaseList.at(i);
+ result.append(textCase == DeclarativeInputEngine::Upper ? str.at(i).toUpper() : (preferLowercase ? str.at(i).toLower() : str.at(i)));
+ }
+ return result;
+ }
+
+ bool preferLowercase;
+
+private:
+ QList<DeclarativeInputEngine::TextCase> textCaseList;
+};
+
+class T9WriteInputMethodPrivate : public AbstractInputMethodPrivate
+{
+ Q_DECLARE_PUBLIC(T9WriteInputMethod)
+public:
+
+ T9WriteInputMethodPrivate(T9WriteInputMethod *q_ptr) :
+ AbstractInputMethodPrivate(),
+ q_ptr(q_ptr),
+ dictionaryLock(QMutex::Recursive),
+ convertedDictionary(0),
+ attachedDictionary(0),
+ resultId(0),
+ resultTimer(0),
+ decumaSession(0),
+ activeWordIndex(-1),
+ arcAdditionStarted(false),
+ ignoreUpdate(false),
+ textCase(DeclarativeInputEngine::Lower)
+ {
+ }
+
+ 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);
+ }
+
+ void initEngine()
+ {
+ VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::initEngine()";
+
+ if (decumaSession)
+ return;
+
+ symbolCategories.clear();
+ symbolCategories.append(DECUMA_CATEGORY_ANSI);
+ languageCategories.clear();
+ languageCategories.append(DECUMA_LANG_EN);
+
+ memset(&sessionSettings, 0, sizeof(sessionSettings));
+
+ QString latinDb = findLatinDb(":/databases/HWR_LatinCG/");
+ hwrDbFile.setFileName(latinDb);
+ if (!hwrDbFile.open(QIODevice::ReadOnly)) {
+ qWarning() << "Could not open hwr database file" << latinDb;
+ return;
+ }
+
+ sessionSettings.pStaticDB = (DECUMA_STATIC_DB_PTR)hwrDbFile.map(0, hwrDbFile.size(), QFile::NoOptions);
+ if (!sessionSettings.pStaticDB) {
+ hwrDbFile.close();
+ qWarning() << "Could not map hwr database" << latinDb;
+ return;
+ }
+
+ 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(decumaGetSessionSize(), 0);
+ decumaSession = (DECUMA_SESSION *)(!session.isEmpty() ? session.data() : 0);
+
+ DECUMA_STATUS status = decumaBeginSession(decumaSession, &sessionSettings, &memFuncs);
+ Q_ASSERT(status == decumaNoError);
+ if (status != decumaNoError) {
+ qWarning() << "Could not initialize T9Write engine" << status;
+ }
+
+ worker.reset(new T9WriteWorker(decumaSession));
+ worker->start();
+ }
+
+ void exitEngine()
+ {
+ VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::exitEngine()";
+
+ worker.reset();
+
+ if (sessionSettings.pStaticDB) {
+ hwrDbFile.unmap((uchar *)sessionSettings.pStaticDB);
+ hwrDbFile.close();
+ }
+
+ detachDictionary(&attachedDictionary);
+ destroyConvertedDictionary(&convertedDictionary);
+
+ if (decumaSession) {
+ decumaEndSession(decumaSession);
+ decumaSession = 0;
+ session.clear();
+ }
+
+ memset(&sessionSettings, 0, sizeof(sessionSettings));
+ }
+
+ QString findLatinDb(const QString &dir)
+ {
+ QString latinDb;
+ QDirIterator it(dir, QDirIterator::NoIteratorFlags);
+ while (it.hasNext()) {
+ QString fileEntry = it.next();
+
+ if (!fileEntry.endsWith(QLatin1String(".bin")))
+ continue;
+
+ latinDb = fileEntry;
+ break;
+ }
+ return latinDb;
+ }
+
+ QString findDictionary(const QString &dir, const QLocale &locale)
+ {
+ 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.endsWith(QLatin1String(".ldb")))
+ continue;
+
+ if (!fileEntry.contains("_" + languageCountry[0].toUpper()))
+ continue;
+
+ dictionary = fileEntry;
+ break;
+ }
+
+ return dictionary;
+ }
+
+ bool attachDictionary(void *dictionary)
+ {
+ VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::attachDictionary():" << dictionary;
+
+ QMutexLocker dictionaryGuard(&dictionaryLock);
+ Q_ASSERT(decumaSession != 0);
+ Q_ASSERT(dictionary != 0);
+
+ DECUMA_STATUS status = decumaAttachConvertedDictionary(decumaSession, dictionary);
+ Q_ASSERT(status == decumaNoError);
+ return status == decumaNoError;
+ }
+
+ void detachDictionary(void **dictionary)
+ {
+ QMutexLocker dictionaryGuard(&dictionaryLock);
+ Q_ASSERT(decumaSession != 0);
+ if (!dictionary || !*dictionary)
+ return;
+
+ VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::detachDictionary():" << *dictionary;
+
+ DECUMA_STATUS status = decumaDetachDictionary(decumaSession, *dictionary);
+ Q_UNUSED(status)
+ Q_ASSERT(status == decumaNoError);
+ *dictionary = 0;
+ }
+
+ void destroyConvertedDictionary(void **dictionary)
+ {
+ QMutexLocker dictionaryGuard(&dictionaryLock);
+ Q_ASSERT(decumaSession != 0);
+ if (!dictionary || !*dictionary)
+ return;
+
+ VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::destroyConvertedDictionary():" << *dictionary;
+
+ DECUMA_STATUS status = decumaDestroyConvertedDictionary(dictionary, &memFuncs);
+ Q_UNUSED(status)
+ Q_ASSERT(status == decumaNoError);
+ Q_ASSERT(*dictionary == 0);
+ }
+
+ bool setInputMode(const QLocale &locale, DeclarativeInputEngine::InputMode inputMode)
+ {
+ VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::setInputMode():" << locale << inputMode;
+
+ Q_Q(T9WriteInputMethod);
+ DECUMA_UINT32 language = mapToDecumaLanguage(locale);
+ if (language == DECUMA_LANG_GSMDEFAULT) {
+ qWarning() << "Handwriting input does not support the language" << locale.name();
+ return false;
+ }
+
+ int isLanguageSupported = 0;
+ decumaDatabaseIsLanguageSupported(sessionSettings.pStaticDB, language, &isLanguageSupported);
+ if (language == DECUMA_LANG_GSMDEFAULT) {
+ qWarning() << "Handwriting input does not support the language" << locale.name();
+ return false;
+ }
+
+ finishRecognition();
+
+ bool languageChanged = languageCategories.isEmpty() || !languageCategories.contains(language);
+ if (languageChanged) {
+ languageCategories.clear();
+ languageCategories.append(language);
+ }
+
+ // Choose the symbol categories by input mode, script and input method hints
+ bool leftToRightGestures = true;
+ Qt::InputMethodHints inputMethodHints = q->inputContext()->inputMethodHints();
+ symbolCategories.clear();
+ switch (inputMode) {
+ case DeclarativeInputEngine::Latin:
+ if (inputMethodHints.testFlag(Qt::ImhEmailCharactersOnly)) {
+ symbolCategories.append(DECUMA_CATEGORY_EMAIL);
+ } else if (inputMethodHints.testFlag(Qt::ImhUrlCharactersOnly)) {
+ symbolCategories.append(DECUMA_CATEGORY_URL);
+ } else {
+ bool includeDigits = true;
+ bool includeBasicPunctuation = true;
+ switch (locale.script()) {
+ case QLocale::LatinScript:
+ if (language == DECUMA_LANG_EN)
+ symbolCategories.append(DECUMA_CATEGORY_ANSI);
+ else
+ symbolCategories.append(DECUMA_CATEGORY_ISO8859_1);
+ break;
+
+ case QLocale::CyrillicScript:
+ symbolCategories.append(DECUMA_CATEGORY_CYRILLIC);
+ break;
+
+ default:
+ qWarning() << "Handwriting input does not support the language" << locale.name();
+ return false;
+ }
+
+ if (includeDigits)
+ symbolCategories.append(DECUMA_CATEGORY_DIGIT);
+
+ if (includeBasicPunctuation) {
+ symbolCategories.append(DECUMA_CATEGORY_BASIC_PUNCTUATIONS);
+ symbolCategories.append(DECUMA_CATEGORY_CONTRACTION_MARK);
+ }
+ }
+ break;
+
+ case DeclarativeInputEngine::Numeric:
+ symbolCategories.append(DECUMA_CATEGORY_DIGIT);
+ if (!inputMethodHints.testFlag(Qt::ImhDigitsOnly))
+ symbolCategories.append(DECUMA_CATEGORY_NUM_SUP);
+ break;
+
+ case DeclarativeInputEngine::Dialable:
+ symbolCategories.append(DECUMA_CATEGORY_PHONE_NUMBER);
+ break;
+
+ default:
+ return false;
+ }
+
+ if (leftToRightGestures) {
+ symbolCategories.append(DECUMA_CATEGORY_BACKSPACE_STROKE);
+ symbolCategories.append(DECUMA_CATEGORY_RETURN_STROKE);
+ symbolCategories.append(DECUMA_CATEGORY_WHITESPACE_STROKE);
+ }
+
+ /* 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);
+
+ // Select recognition mode
+ // Note: MCR mode is preferred in all cases, since it eliminates the need
+ // for recognition timer, thus provides better user experience.
+ sessionSettings.recognitionMode = inputMethodHints.testFlag(Qt::ImhHiddenText) ? scrMode : mcrMode;
+
+ // Detach previous dictionary if the language is being changed
+ // or the recognizer mode is single-character mode
+ if ((languageChanged || inputMethodHints.testFlag(Qt::ImhNoPredictiveText) || sessionSettings.recognitionMode == scrMode) && attachedDictionary) {
+ detachDictionary(&attachedDictionary);
+ }
+
+ // Check if a dictionary needs to be loaded
+ if (languageChanged || !convertedDictionary) {
+ destroyConvertedDictionary(&convertedDictionary);
+ dictionaryFileName = findDictionary(":/databases/XT9_LDBs/", locale);
+ if (!dictionaryFileName.isEmpty()) {
+ if (dictionaryTask.isNull() || dictionaryTask->fileUri != dictionaryFileName) {
+ dictionaryTask.reset(new T9WriteDictionaryTask(dictionaryFileName, memFuncs));
+ q->connect(dictionaryTask.data(), SIGNAL(completed(QString,void*)),
+ SLOT(dictionaryLoadCompleted(QString,void*)), Qt::DirectConnection);
+ worker->addTask(dictionaryTask);
+ }
+ }
+ }
+
+ // Attach existing dictionary, if necessary
+ if (sessionSettings.recognitionMode == mcrMode && !inputMethodHints.testFlag(Qt::ImhNoPredictiveText) &&
+ convertedDictionary && !attachedDictionary) {
+ attachDictionary(convertedDictionary);
+ attachedDictionary = convertedDictionary;
+ }
+ }
+
+ // 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 = decumaChangeSessionSettings(decumaSession, &sessionSettings);
+ Q_ASSERT(status == decumaNoError);
+
+ caseFormatter.preferLowercase = inputMethodHints.testFlag(Qt::ImhPreferLowercase);
+
+ return status == decumaNoError;
+ }
+
+ DECUMA_UINT32 mapToDecumaLanguage(const QLocale &locale)
+ {
+ 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_SR, // 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
+ };
+
+ if (locale.language() > maxLanguage)
+ return DECUMA_LANG_GSMDEFAULT;
+
+ DECUMA_UINT32 language = languageMap[locale.language()];
+
+ return language;
+ }
+
+ DeclarativeTrace *traceBegin(int traceId, DeclarativeInputEngine::PatternRecognitionMode patternRecognitionMode,
+ const QVariantMap &traceCaptureDeviceInfo, const QVariantMap &traceScreenInfo)
+ {
+ Q_UNUSED(traceId)
+ Q_UNUSED(patternRecognitionMode)
+ Q_UNUSED(traceCaptureDeviceInfo)
+ Q_UNUSED(traceScreenInfo)
+
+ 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>();
+ if (recognitionTask) {
+ recognitionTask->cancelRecognition();
+ recognitionTask->wait();
+ worker->removeTask(recognitionTask);
+ recognitionTask.reset();
+ }
+
+ 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;
+
+ DECUMA_STATUS status;
+
+ if (!arcAdditionStarted) {
+ status = decumaBeginArcAddition(decumaSession);
+ Q_ASSERT(status == decumaNoError);
+ arcAdditionStarted = true;
+ }
+
+ DECUMA_UINT32 arcID = (DECUMA_UINT32)traceId;
+ status = decumaStartNewArc(decumaSession, arcID);
+ Q_ASSERT(status == decumaNoError);
+ if (status != decumaNoError) {
+ decumaEndArcAddition(decumaSession);
+ arcAdditionStarted = false;
+ return NULL;
+ }
+
+ DeclarativeTrace *trace = new DeclarativeTrace();
+ traceList.append(trace);
+
+ return trace;
+ }
+
+ void traceEnd(DeclarativeTrace *trace)
+ {
+ if (trace->isCanceled()) {
+ decumaCancelArc(decumaSession, trace->traceId());
+ traceList.removeOne(trace);
+ delete trace;
+ } else {
+ addPointsToTraceGroup(trace);
+ }
+ if (!traceList.isEmpty()) {
+ Q_ASSERT(arcAdditionStarted);
+ if (countActiveTraces() == 0)
+ restartRecognition();
+ } else if (arcAdditionStarted) {
+ decumaEndArcAddition(decumaSession);
+ arcAdditionStarted = false;
+ }
+ }
+
+ int countActiveTraces() const
+ {
+ int count = 0;
+ foreach (DeclarativeTrace *trace, traceList) {
+ if (!trace->isFinal())
+ count++;
+ }
+ return count;
+ }
+
+ void clearTraces()
+ {
+ qDeleteAll(traceList);
+ traceList.clear();
+ }
+
+ void addPointsToTraceGroup(DeclarativeTrace *trace)
+ {
+ Q_ASSERT(decumaSession != 0);
+
+ const QVariantList points = trace->points();
+ DECUMA_UINT32 arcID = (DECUMA_UINT32)trace->traceId();
+ DECUMA_STATUS status;
+
+ foreach (const QVariant &p, points) {
+ const QPoint pt(p.toPointF().toPoint());
+ status = decumaAddPoint(decumaSession, (DECUMA_COORD)pt.x(),(DECUMA_COORD)pt.y(), arcID);
+ Q_ASSERT(status == decumaNoError);
+ }
+
+ status = decumaCommitArc(decumaSession, arcID);
+ Q_ASSERT(status == decumaNoError);
+ }
+
+ 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 = decumaNoteSelectedCandidate(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();
+ }
+
+ bool finishRecognition(bool emitSelectionListChanged = true)
+ {
+ VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::finishRecognition()";
+
+ 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) {
+ decumaEndArcAddition(decumaSession);
+ arcAdditionStarted = false;
+ }
+
+ if (!wordCandidates.isEmpty()) {
+ wordCandidates.clear();
+ wordCandidatesHwrResultIndex.clear();
+ activeWordIndex = -1;
+ if (emitSelectionListChanged) {
+ Q_Q(T9WriteInputMethod);
+ emit q->selectionListChanged(DeclarativeSelectionListModel::WordCandidateList);
+ emit q->selectionListActiveItemChanged(DeclarativeSelectionListModel::WordCandidateList, activeWordIndex);
+ }
+ result = true;
+ }
+
+ stringStart.clear();
+ scrResult.clear();
+ caseFormatter.clear();
+
+ return result;
+ }
+
+ bool select(int index = -1)
+ {
+ 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) {
+ index = index >= 0 ? index : activeWordIndex;
+ noteSelected(index);
+ QString finalWord = wordCandidates.at(index);
+ 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);
+ DeclarativeInputContext *ic = q->inputContext();
+ 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;
+
+ // Delete trace history
+ if (sessionSettings.recognitionMode == mcrMode && !symbolStrokes.isEmpty()) {
+ 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();
+ }
+
+ // Look for a gesture at the end of 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);
+ break;
+
+ default:
+ finishRecognition();
+ ic->commit(ic->preeditText());
+ 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(DeclarativeSelectionListModel::WordCandidateList);
+ emit q->selectionListActiveItemChanged(DeclarativeSelectionListModel::WordCandidateList, activeWordIndex);
+ }
+ }
+
+ T9WriteInputMethod *q_ptr;
+ static const DECUMA_MEM_FUNCTIONS memFuncs;
+ DECUMA_SESSION_SETTINGS sessionSettings;
+ DECUMA_INSTANT_GESTURE_SETTINGS instantGestureSettings;
+ QFile hwrDbFile;
+ QVector<DECUMA_UINT32> languageCategories;
+ QVector<DECUMA_UINT32> symbolCategories;
+ QScopedPointer<T9WriteWorker> worker;
+ QList<DeclarativeTrace *> traceList;
+ QMutex dictionaryLock;
+ QString dictionaryFileName;
+ void *convertedDictionary;
+ void *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;
+ DeclarativeInputEngine::TextCase textCase;
+ T9WriteCaseFormatter caseFormatter;
+};
+
+const DECUMA_MEM_FUNCTIONS T9WriteInputMethodPrivate::memFuncs = {
+ T9WriteInputMethodPrivate::decumaMalloc,
+ T9WriteInputMethodPrivate::decumaCalloc,
+ T9WriteInputMethodPrivate::decumaFree,
+ NULL
+};
+
+T9WriteInputMethod::T9WriteInputMethod(QObject *parent) :
+ AbstractInputMethod(*new T9WriteInputMethodPrivate(this), parent)
+{
+ Q_D(T9WriteInputMethod);
+ d->initEngine();
+}
+
+T9WriteInputMethod::~T9WriteInputMethod()
+{
+ Q_D(T9WriteInputMethod);
+ d->exitEngine();
+}
+
+QList<DeclarativeInputEngine::InputMode> T9WriteInputMethod::inputModes(const QString &locale)
+{
+ Q_UNUSED(locale)
+ return QList<DeclarativeInputEngine::InputMode>()
+ << DeclarativeInputEngine::Latin
+ << DeclarativeInputEngine::Numeric
+ << DeclarativeInputEngine::Dialable;
+}
+
+bool T9WriteInputMethod::setInputMode(const QString &locale, DeclarativeInputEngine::InputMode inputMode)
+{
+ Q_D(T9WriteInputMethod);
+ d->select();
+ return d->setInputMode(locale, inputMode);
+}
+
+bool T9WriteInputMethod::setTextCase(DeclarativeInputEngine::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:
+ {
+ DeclarativeInputContext *ic = inputContext();
+ QString preeditText = ic->preeditText();
+ if (preeditText.length() > 1) {
+ preeditText.chop(1);
+ 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(DeclarativeSelectionListModel::WordCandidateList);
+ emit selectionListActiveItemChanged(DeclarativeSelectionListModel::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) {
+ QChar c = text.at(0);
+ bool addToWord = !c.isPunct() && !c.isSymbol();
+
+ DeclarativeInputContext *ic = inputContext();
+ Qt::InputMethodHints inputMethodHints = ic->inputMethodHints();
+ QString preeditText = inputContext()->preeditText();
+ if (!addToWord) {
+ if (!preeditText.isEmpty()) {
+ if (inputMethodHints.testFlag(Qt::ImhUrlCharactersOnly) || inputMethodHints.testFlag(Qt::ImhEmailCharactersOnly))
+ addToWord = QString(QStringLiteral(":/?#[]@!$&'()*+,;=-_.%")).contains(c);
+ else
+ addToWord = (c == Qt::Key_Apostrophe || c == Qt::Key_Minus);
+ }
+ }
+ 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(DeclarativeSelectionListModel::WordCandidateList);
+ emit selectionListActiveItemChanged(DeclarativeSelectionListModel::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<DeclarativeSelectionListModel::Type> T9WriteInputMethod::selectionLists()
+{
+ return QList<DeclarativeSelectionListModel::Type>() << DeclarativeSelectionListModel::WordCandidateList;
+}
+
+int T9WriteInputMethod::selectionListItemCount(DeclarativeSelectionListModel::Type type)
+{
+ Q_UNUSED(type)
+ Q_D(T9WriteInputMethod);
+ return d->wordCandidates.count();
+}
+
+QVariant T9WriteInputMethod::selectionListData(DeclarativeSelectionListModel::Type type, int index, int role)
+{
+ QVariant result;
+ Q_UNUSED(type)
+ Q_D(T9WriteInputMethod);
+ switch (role) {
+ case DeclarativeSelectionListModel::DisplayRole:
+ result = QVariant(d->wordCandidates.at(index));
+ break;
+ case DeclarativeSelectionListModel::WordCompletionLengthRole:
+ result.setValue(0);
+ break;
+ default:
+ result = AbstractInputMethod::selectionListData(type, index, role);
+ break;
+ }
+ return result;
+}
+
+void T9WriteInputMethod::selectionListItemSelected(DeclarativeSelectionListModel::Type type, int index)
+{
+ Q_UNUSED(type)
+ Q_D(T9WriteInputMethod);
+ d->select(index);
+}
+
+QList<DeclarativeInputEngine::PatternRecognitionMode> T9WriteInputMethod::patternRecognitionModes() const
+{
+ return QList<DeclarativeInputEngine::PatternRecognitionMode>()
+ << DeclarativeInputEngine::HandwritingRecoginition;
+}
+
+DeclarativeTrace *T9WriteInputMethod::traceBegin(int traceId, DeclarativeInputEngine::PatternRecognitionMode patternRecognitionMode,
+ const QVariantMap &traceCaptureDeviceInfo, const QVariantMap &traceScreenInfo)
+{
+ Q_D(T9WriteInputMethod);
+ return d->traceBegin(traceId, patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo);
+}
+
+bool T9WriteInputMethod::traceEnd(DeclarativeTrace *trace)
+{
+ Q_D(T9WriteInputMethod);
+ d->traceEnd(trace);
+ 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();
+ d->clearTraces();
+ } else if (d->sessionSettings.recognitionMode == scrMode) {
+ d->select();
+ }
+ }
+}
+
+void T9WriteInputMethod::dictionaryLoadCompleted(const QString &fileUri, void *dictionary)
+{
+ Q_D(T9WriteInputMethod);
+ // Note: This method is called in worker thread context
+ QMutexLocker dictionaryGuard(&d->dictionaryLock);
+
+ VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethod::dictionaryLoadCompleted():" << fileUri << dictionary;
+
+ if (!dictionary)
+ return;
+
+ if (fileUri == d->dictionaryFileName) {
+ d->convertedDictionary = dictionary;
+ if (d->sessionSettings.recognitionMode == mcrMode &&
+ !inputContext()->inputMethodHints().testFlag(Qt::ImhNoPredictiveText) &&
+ !d->attachedDictionary) {
+ d->attachDictionary(d->convertedDictionary);
+ d->attachedDictionary = d->convertedDictionary;
+ }
+ } else {
+ d->destroyConvertedDictionary(&dictionary);
+ }
+}
+
+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);
+}