Revision control
++ // Copyright (C) MyScript. Contact: https://www.myscript.com/about/contact-us/sales-inquiry/
++ // Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). Contact: https://www.qt.io/licensing/
++ // Copyright (C) 2017 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include "myscriptinputmethod_p.h"
++ #include "myscriptinputmethod_p_p.h"
++
++ #include <QtVirtualKeyboard/qvirtualkeyboardinputengine.h>
++ #include <QtVirtualKeyboard/qvirtualkeyboardinputcontext.h>
++ #include <QtVirtualKeyboard/qvirtualkeyboardtrace.h>
++
++ #include <QLoggingCategory>
++
++ #include MYSCRIPT_CERTIFICATE
++ #include <common/Properties.h>
++ #include <voim.h>
++
++ #include <thread>
++ #include <mutex>
++ #include <chrono>
++
++ #ifndef PATH_MAX
++ #define PATH_MAX 512
++ #endif
++
++ #include <QCryptographicHash>
++ #include <QFile>
++ #include <QThread>
++
++ #include <QtCore/qmath.h>
++ #include <QtCore/QLibraryInfo>
++ #include <QTextFormat>
++
++ #define VERIFY(arg) if (!(arg)) abort();
++
++ #define VERIFY2(arg, msg, engine) if (!(arg)) { \
++ qCCritical(qlcVKMyScript) << msg << strMyScriptError(voGetError(engine)); \
++ abort(); \
++ }
++
++ #define GESTURE_STRING_RIGHT_TO_LEFT "\xF3\xB0\x80\x82" // equivalent Unicode is "\U000F0002"
++ #define GESTURE_STRING_LEFT_TO_RIGHT "\xF3\xB0\x80\x83" // equivalent Unicode is "\U000F0003"
++ #define GESTURE_STRING_DOWN_THEN_LEFT "\xF3\xB0\x80\x84" // equivalent Unicode is "\U000F0004"
++ #define GESTURE_STRING_DOWN_THEN_RIGHT "\xF3\xB0\x80\x88" // equivalent Unicode is "\U000F0008"
++
++ QT_BEGIN_NAMESPACE
++ namespace QtVirtualKeyboard {
++
++ Q_LOGGING_CATEGORY(qlcVKMyScript, "qt.virtualkeyboard.myscript")
++
++ typedef enum GESTURE_TYPE {
++ GESTURE_TYPE_NONE = 0,
++ GESTURE_TYPE_RIGHT_TO_LEFT = 0x000F0002,
++ GESTURE_TYPE_LEFT_TO_RIGHT = 0x000F0003,
++ GESTURE_TYPE_DOWN_THEN_LEFT = 0x000F0004,
++ GESTURE_TYPE_DOWN_THEN_RIGHT = 0x000F0008
++ } GestureType;
++
++ typedef struct CANDIDATE_ITEM {
++ int candidateIndex;
++ QStringList candidates;
++ } CandidateItem;
++
++ static inline QString getVoimErrorMessage(const voimErrorCode code)
++ {
++ QString message;
++
++ switch (code) {
++ case VOIM_EC_NO_ERROR:
++ message = QString::fromLatin1("no error occurred");
++ break;
++ case VOIM_EC_INVALID_VALUE:
++ message = QString::fromLatin1("a value passed to an API function was not valid");
++ break;
++ case VOIM_EC_INVALID_INDEX:
++ message = QString::fromLatin1("an index passed to an API function was out of range");
++ break;
++ case VOIM_EC_INVALID_OPERATION:
++ message = QString::fromLatin1("the requested operation is not valid regarding the current state of the target object");
++ break;
++ case VOIM_EC_OUT_OF_MEMORY:
++ message = QString::fromLatin1("a memory allocation failure");
++ break;
++ case VOIM_EC_IO_FAILURE:
++ message = QString::fromLatin1("an I/O operation failure");
++ break;
++ case VOIM_EC_INTERNAL_ERROR:
++ message = QString::fromLatin1("an internal error");
++ break;
++ default:
++ message = QString::fromLatin1("unknown error code - ") + QString::number(code);
++ break;
++ }
++
++ return message;
++ }
++
++ class MyScriptInputMethodPrivate
++ {
++ Q_DECLARE_PUBLIC(MyScriptInputMethod)
++ public:
++ MyScriptInputMethodPrivate(MyScriptInputMethod *q_ptr) :
++ q_ptr(q_ptr),
++ m_engine(nullptr),
++ m_languageManager(nullptr),
++ m_recognizer(nullptr),
++ m_onManagingResult(false),
++ m_isProcessing(false),
++ m_commitTimer(0),
++ textCase(QVirtualKeyboardInputEngine::TextCase::Lower),
++ wordIndex(-1),
++ m_itemIndex(-1),
++ m_preeditCursorPosition(0)
++ {
++ initHwrEngine();
++ }
++
++ ~MyScriptInputMethodPrivate()
++ {
++ destroyHwrEngine();
++ }
++
++ void setContext(QVirtualKeyboardInputEngine::PatternRecognitionMode patternRecognitionMode,
++ const QVariantMap &traceCaptureDeviceInfo,
++ const QVariantMap &traceScreenInfo)
++ {
++ Q_UNUSED(patternRecognitionMode);
++ Q_UNUSED(traceCaptureDeviceInfo);
++ Q_UNUSED(traceScreenInfo);
++
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO;
++ }
++
++ QVirtualKeyboardTrace *traceBegin(
++ int traceId, QVirtualKeyboardInputEngine::PatternRecognitionMode patternRecognitionMode,
++ const QVariantMap &traceCaptureDeviceInfo, const QVariantMap &traceScreenInfo)
++ {
++ Q_UNUSED(patternRecognitionMode);
++ Q_UNUSED(traceCaptureDeviceInfo);
++ Q_UNUSED(traceScreenInfo);
++
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO << traceId;
++
++ if (!m_isProcessing) {
++ Q_Q(MyScriptInputMethod);
++ if (!q->inputContext()->preeditText().isEmpty())
++ q->inputContext()->commit();
++ }
++
++ stopCommitTimer();
++
++ for (int i = 0; i < traceList.size(); i++) {
++ traceList[i]->setOpacity(qMax(0.0, 1 - 0.25 * (traceList.size() - i)));
++ }
++
++ Q_Q(MyScriptInputMethod);
++ QVirtualKeyboardTrace *trace = new QVirtualKeyboardTrace(q);
++ traceList.append(trace);
++
++ return trace;
++ }
++
++ void traceEnd(QVirtualKeyboardTrace *trace)
++ {
++ if (trace->isCanceled()) {
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO << "discarded" << trace;
++ traceList.removeOne(trace);
++ delete trace;
++ } else {
++ addPointsToTraceGroup(trace);
++ }
++
++ if (!traceList.isEmpty() && countActiveTraces() == 0) {
++ resetCommitTimer();
++ }
++ }
++
++ int countActiveTraces() const
++ {
++ int count = 0;
++ for (QVirtualKeyboardTrace *trace : std::as_const(traceList)) {
++ if (!trace->isFinal())
++ count++;
++ }
++ return count;
++ }
++
++ void clearTraces()
++ {
++ qDeleteAll(traceList);
++ traceList.clear();
++ }
++
++ void handleBackspace()
++ {
++ Q_Q(MyScriptInputMethod);
++
++ cancelRecognition();
++
++ clearCandidates();
++ clearItems();
++ q->inputContext()->commit();
++ }
++
++ void addPointsToTraceGroup(QVirtualKeyboardTrace *trace)
++ {
++ const QVariantList sourcePoints = trace->points();
++
++ struct voPoint {
++ float x;
++ float y;
++ };
++
++ std::vector<voPoint> points;
++ points.reserve(sourcePoints.size());
++ for (const QVariant &p : sourcePoints) {
++ const QPointF pt(p.toPointF());
++ points.push_back({ (float)pt.x(), (float)pt.y() });
++ }
++
++ if (!voim_addStroke(m_engine, m_recognizer, &points.data()->x, sizeof(voPoint), &points.data()->y, sizeof(voPoint), (int)(points.size())) &&
++ voim_getError(m_engine) != VOIM_EC_NO_ERROR) {
++ qCCritical(qlcVKMyScript) << "voim_addStroke() failed -" << getVoimErrorMessage(voim_getError(m_engine));
++ return;
++ }
++ }
++
++ void clearCandidates(void)
++ {
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO;
++
++ wordCandidates.clear();
++ word = QString();
++ wordIndex = -1;
++
++ updateCandidateView();
++ }
++
++ void clearItems(void)
++ {
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO;
++
++ m_itemIndex = -1;
++
++ for (int i = 0; i < m_items.size(); i++) {
++ CandidateItem *candidateItem = m_items.at(i).second;
++ delete candidateItem;
++ }
++
++ m_items.clear();
++ }
++
++ bool cancelRecognition()
++ {
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO;
++ Q_ASSERT(m_engine != nullptr && m_recognizer != nullptr);
++
++ if (m_isProcessing)
++ stopCommitTimer(); //commit();
++ clearTraces();
++
++ if (voim_cancel(m_engine, m_recognizer) &&
++ voim_getError(m_engine) != VOIM_EC_NO_ERROR) {
++ qCCritical(qlcVKMyScript) << "voim_cancel() failed -" << getVoimErrorMessage(voim_getError(m_engine));
++ return false;
++ }
++
++ m_isProcessing = false;
++ return !traceList.isEmpty();
++ }
++
++ void resetCommitTimer(void)
++ {
++ Q_Q(MyScriptInputMethod);
++
++ stopCommitTimer();
++ m_commitTimer = q->startTimer(1500);
++ }
++
++ void stopCommitTimer(void)
++ {
++ if (m_commitTimer) {
++ Q_Q(MyScriptInputMethod);
++ q->killTimer(m_commitTimer);
++ m_commitTimer = 0;
++ }
++ }
++
++ QString getLibraryPath(QString libName)
++ {
++ QString libPath = QLibraryInfo::path(QLibraryInfo::LibrariesPath) + "/" + libName;
++ if (QFile::exists(libPath)) {
++ return libPath;
++ } else {
++ return QLibraryInfo::path(QLibraryInfo::BinariesPath) + "/" + libName;
++ }
++ }
++
++ void initHwrEngine(void)
++ {
++ if (!createEngine())
++ return;
++
++ if (!createLanguageManager()) {
++ voim_destroyEngine(m_engine);
++ m_engine = nullptr;
++
++ return;
++ }
++
++ if (!createRecognizer()) {
++ voim_destroyLanguageManager(m_engine, m_languageManager);
++ voim_destroyEngine(m_engine);
++
++ m_languageManager = nullptr;
++ m_engine = nullptr;
++
++ return;
++ }
++
++ if (!voim_setNotificationCallback(m_engine, m_recognizer, notificationCallback, this)) {
++ qCCritical(qlcVKMyScript) << "voim_setNotificationCallback() failed -" << getVoimErrorMessage(voim_getError(m_engine));
++ return;
++ }
++
++ m_threadController.reset(new MyScriptRecognizeController(this, m_engine, m_recognizer));
++ }
++
++ bool createEngine(void)
++ {
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO;
++
++ /*
++ * NOTE: you must use the certificate provided by MyScript to use a MyScript product.
++ * It is described in MyCertificate.c and MyCertificate.h
++ */
++ const voCertificate *certificate = &myCertificate;
++ voimProperty *properties = nullptr;
++
++ QString imLibrary = getLibraryPath(MYSCRIPT_VOIM_NAME);
++ properties = Properties_put(properties, "com.myscript.im.library", imLibrary.toStdString().c_str());
++ if (!properties) {
++ qCCritical(qlcVKMyScript) << "failed to define property " << "com.myscript.im.library" << " with value " << imLibrary;
++ return false;
++ }
++
++ QString engineLibrary = getLibraryPath(MYSCRIPT_ENGINE_NAME);
++ properties = Properties_put(properties, "com.myscript.engine.library", engineLibrary.toStdString().c_str());
++ if (!properties) {
++ qCCritical(qlcVKMyScript) << "failed to define property " << "com.myscript.engine.library" << " with value " << engineLibrary;
++ return false;
++ }
++
++ QString propertyFile = QLatin1String("/Engine.properties");
++ propertyFile = MYSCRIPT_VOIM_PROPERTY_PATH + propertyFile;
++
++ if (!checkFile(propertyFile)) {
++ qCCritical(qlcVKMyScript) << "failed to open Engine Property file " << propertyFile;
++ return false;
++ }
++
++ m_engine = voim_createEngine(certificate, propertyFile.toStdString().c_str(), properties);
++ if (!m_engine) {
++ qCCritical(qlcVKMyScript) << "voim_createEngine() failed";
++ return false;
++ }
++
++ return true;
++ }
++
++ bool createLanguageManager(void)
++ {
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO;
++
++ voimProperty *properties = nullptr;
++
++ QString languageConf = MYSCRIPT_LANGUAGE_CONF_PATH;
++ properties = Properties_put(properties, "com.myscript.im.languageSearchPath", languageConf.toStdString().c_str());
++ if (!properties) {
++ qCCritical(qlcVKMyScript) << "failed to define property " << "com.myscript.im.languageSearchPath" << " with value " << languageConf;
++ return false;
++ }
++
++ properties = Properties_put(properties, "com.myscript.im.languageManifestSuffix", ".conf");
++ if (!properties) {
++ qCCritical(qlcVKMyScript) << "failed to define property " << "com.myscript.im.languageManifestSuffix" << " with value" << " \".conf\"";
++ return false;
++ }
++
++ QString propertyFile = QLatin1String("/LanguageManager.properties");
++ propertyFile = MYSCRIPT_VOIM_PROPERTY_PATH + propertyFile;
++
++ if (!checkFile(propertyFile)) {
++ qCCritical(qlcVKMyScript) << "failed to open LanguageManager Property file " << propertyFile;
++ return false;
++ }
++
++ m_languageManager = voim_createLanguageManager(m_engine, propertyFile.toStdString().c_str(), properties);
++ if (!m_languageManager) {
++ qCCritical(qlcVKMyScript) << "voim_createLanguageManager() failed -" << getVoimErrorMessage(voim_getError(m_engine));
++ return false;
++ }
++
++ voim_refreshLanguageList(m_engine, m_languageManager);
++ if (voim_getError(m_engine) != VOIM_EC_NO_ERROR) {
++ qCCritical(qlcVKMyScript) << "voim_refreshLanguageList failed -" << getVoimErrorMessage(voim_getError(m_engine));
++ }
++ Properties_destroy(properties);
++
++ return true;
++ }
++
++ bool createRecognizer(void)
++ {
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO;
++
++ QString propertyFile = QLatin1String("/Recognizer.properties");
++ propertyFile = MYSCRIPT_VOIM_PROPERTY_PATH + propertyFile;
++
++ if (!checkFile(propertyFile)) {
++ qCCritical(qlcVKMyScript) << "failed to open Recognizer Property file " << propertyFile;
++ return false;
++ }
++
++ m_recognizer = voim_createRecognizer(m_engine, m_languageManager, propertyFile.toStdString().c_str(), NULL);
++ if (!m_recognizer) {
++ qCCritical(qlcVKMyScript) << "voim_createRecognizer() failed -" << getVoimErrorMessage(voim_getError(m_engine));
++ return false;
++ }
++
++ return true;
++ }
++
++ QString getLanguageName(const QString &locale)
++ {
++ if (locale.startsWith(QLatin1String("ar"))) // (language == "ar_EG" || language == "ar_AR")
++ return QLatin1String("ar");
++ else if (locale.startsWith(QLatin1String("fa"))) // (language == "fa_FA")
++ return QLatin1String("fa_IR");
++ else if (locale.startsWith(QLatin1String("nb")))
++ return QLatin1String("no_NO");
++ else if (locale.startsWith(QLatin1String("sr")))
++ return QLatin1String("sr_Cyrl_RS");
++ else
++ return locale;
++ }
++
++ QString getModeName(Qt::InputMethodHints inputMethodHints)
++ {
++ /*
++ * Qt::InputMethodHints flags description
++ *
++ * - Qt::ImhNone :0x00000000
++ * - Qt::ImhHiddenText :0x00000001
++ * - Qt::ImhSensitiveData :0x00000002
++ * - Qt::ImhNoAutoUppercase :0x00000004
++ * - Qt::ImhPreferNumbers :0x00000008
++ * - Qt::ImhPreferUppercase :0x00000010
++ * - Qt::ImhPreferLowercase :0x00000020
++ * - Qt::ImhNoPredictiveText :0x00000040
++ * - Qt::ImhDate :0x00000080
++ * - Qt::ImhTime :0x00000100
++ * - Qt::ImhPreferLatin :0x00000200
++ * - Qt::ImhMultiLine :0x00000400
++ * - Qt::ImhExclusiveInputMask :0xffff0000
++ * - Qt::ImhDigitsOnly :0x00010000
++ * - Qt::ImhFormattedNumbersOnly :0x00020000
++ * - Qt::ImhUppercaseOnly :0x00040000
++ * - Qt::ImhLowercaseOnly :0x00080000
++ * - Qt::ImhDialableCharactersOnly :0x00100000
++ * - Qt::ImhEmailCharactersOnly :0x00200000
++ * - Qt::ImhUrlCharactersOnly :0x00400000
++ * - Qt::ImhLatinOnly :0x00800000
++ */
++
++ if (inputMethodHints & Qt::ImhDigitsOnly)
++ return QLatin1String("number-superimposed");
++
++ if (inputMethodHints & Qt::ImhFormattedNumbersOnly)
++ return QLatin1String("number-superimposed"); // "number-superimposed" is not correctly matched with Qt::ImhFormattedNumbersOnly
++ // temporary linked to "number-superimposed", need to improve it later on
++
++ if (inputMethodHints & Qt::ImhDialableCharactersOnly)
++ return QLatin1String("phone_number-superimposed");
++
++ if (inputMethodHints & Qt::ImhEmailCharactersOnly)
++ return QLatin1String("email-superimposed");
++
++ if (inputMethodHints & Qt::ImhUrlCharactersOnly)
++ return QLatin1String("uri-superimposed");
++
++ return QLatin1String("text-superimposed");
++ }
++
++ bool setMode(const QString &locale, Qt::InputMethodHints inputMethodHints)
++ {
++ if (locale == m_locale && inputMethodHints == m_inputMethodHints)
++ return false;
++
++ m_locale = locale;
++ m_inputMethodHints = inputMethodHints;
++
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO << locale;
++ Q_ASSERT(m_engine != nullptr && m_recognizer != nullptr);
++
++ QString language = getLanguageName(m_locale);
++ QString mode = getModeName(m_inputMethodHints);
++
++ if (!voim_setMode(m_engine, m_recognizer, language.toStdString().c_str(), mode.toStdString().c_str()) &&
++ voim_getError(m_engine) != VOIM_EC_NO_ERROR) {
++ qCCritical(qlcVKMyScript) << "voim_setMode() failed -" << getVoimErrorMessage(voim_getError(m_engine));
++ return false;
++ }
++
++ return true;
++ }
++
++ bool commit(void)
++ {
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO;
++ Q_ASSERT(m_engine != nullptr && m_recognizer != nullptr);
++
++ if (!voim_commit(m_engine, m_recognizer) &&
++ voim_getError(m_engine) != VOIM_EC_NO_ERROR) {
++ qCCritical(qlcVKMyScript) << "voim_commit() failed -" << getVoimErrorMessage(voim_getError(m_engine));
++ return false;
++ }
++
++ stopCommitTimer();
++
++ return true;
++ }
++
++ void destroyHwrEngine(void)
++ {
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO;
++
++ cancelRecognition();
++
++ if (m_engine) {
++ if (m_recognizer)
++ voim_destroyRecognizer(m_engine, m_recognizer);
++
++ if (m_languageManager)
++ voim_destroyLanguageManager(m_engine, m_languageManager);
++
++ voim_destroyEngine(m_engine);
++ }
++
++ m_threadController.reset();
++ }
++
++ bool checkFile(QString filename)
++ {
++ if (QFile::exists(filename)) {
++ return true;
++ } else {
++ return false;
++ }
++ }
++
++ /******************************************************************************
++ * The notificationCallback handles events raised by
++ * the worker thread of a recognizer
++ *
++ * Parameters:
++ * - engine : the VOIM engine
++ * - recognizer : the VOIM recognizer that sends the event.
++ * - eventType : the type of the event.
++ * - eventParameters : the parameters associated with the event, if any.
++ * - userParam : the userParameter that passed to
++ * voim_setNotificationCallback
++ *****************************************************************************/
++ static void notificationCallback(voimEngine *engine,
++ voimRecognizer *recognizer,
++ voimEventType eventType,
++ const void *eventParameters,
++ void *userParam)
++ {
++ if (userParam) {
++ MyScriptInputMethodPrivate *pParent = static_cast<MyScriptInputMethodPrivate *>(userParam);
++ pParent->onNotify(engine, recognizer, eventType, eventParameters);
++ }
++ }
++
++ /******************************************************************************
++ * Event types of VOIM handwriting notification callback
++ *
++ * - VOIM_EVENT_ON_SET_MODE
++ * :Occurs when a mode change request has been taken into account.
++ * - VOIM_EVENT_ON_SET_POSITION_AND_SCALE_INDICATOR
++ * :Occurs when a guide line change request has been taken into account.
++ * - VOIM_EVENT_ON_SET_USER_DICTIONARY
++ * :Occurs when a user dictionary has been set to the recognizer.
++ * - VOIM_EVENT_ON_ADD_STROKE
++ * :Occurs when a new digital ink stroke has been added to
++ * the recognizer and received by the worker thread.
++ * - VOIM_EVENT_ON_RECOGNITION_START
++ * :Occurs when a recognition process starts.
++ * - VOIM_EVENT_ON_RECOGNITION_PROGRESS
++ * :Occurs during a recognition process.
++ * - VOIM_EVENT_ON_RECOGNITION_END
++ * :Occurs when a recognition process ends.
++ * - VOIM_EVENT_ON_COMMIT
++ * :Occurs when a commit request has been taken into account.
++ * - VOIM_EVENT_ON_CANCEL
++ * :Occurs when a cancel request has been taken into account.
++ * - VOIM_EVENT_ON_FLOW_SYNC
++ * :Occurs when a flow sync command is received by
++ * the recognizer worker thread.
++ * - VOIM_EVENT_ON_NEW_INPUT_ITEM
++ * :Occurs when a new input item command is received by
++ * the recognizer worker thread.
++ * - VOIM_EVENT_ON_ADD_STRING
++ * :Occurs when a new digital text string has been added
++ * to the recognizer and received by the worker thread.
++ *****************************************************************************/
++ void onNotify(voimEngine *engine, voimRecognizer *recognizer,
++ voimEventType eventType, const void *eventParameters)
++ {
++ Q_UNUSED(recognizer);
++
++ switch (eventType) {
++
++ case VOIM_EVENT_ON_SET_MODE:
++ {
++ /*
++ * voimSetModeParameters
++ *
++ * voimLanguage *language - The language that contains the handwriting mode
++ * int modeIndex - The index of the mode in the language
++ * bool successful - True if the operation was successful
++ */
++ const voimSetModeParameters *p = (const voimSetModeParameters *)eventParameters;
++
++ if (p->successful) {
++ QString language = QString::fromUtf8(voim_getLanguageName(engine, p->language));
++ QString mode = QString::fromUtf8(voim_getLanguageModeNameAt(engine, p->language, p->modeIndex));
++
++ qCDebug(qlcVKMyScript) << "a mode" << language << "/" << mode <<
++ "has been set, ready to receive strokes";
++ } else {
++ qCCritical(qlcVKMyScript) << "failed to set mode";
++ }
++ }
++ break;
++
++ case VOIM_EVENT_ON_SET_POSITION_AND_SCALE_INDICATOR:
++ {
++ /*
++ * voimSetPositionAndScaleIndicatorParameters
++ *
++ * float baselinePosition - The baseline position
++ * float xHeight - The height of small letter x
++ * float lineSpacing - The line spacing
++ * bool successful - True if the operation was successful
++ */
++ const voimSetPositionAndScaleIndicatorParameters *p = (const voimSetPositionAndScaleIndicatorParameters *)eventParameters;
++
++ if (p->successful) {
++ qCDebug(qlcVKMyScript) << "baselinePosition \"" << p->baselinePosition << "\", " <<
++ "xHeight \"" << p->xHeight << "\", " <<
++ "lineSpacing \"" << p->lineSpacing << "\" have been set";
++ } else {
++ qCCritical(qlcVKMyScript) << "failed to set position and scale indicator";
++ }
++ }
++ break;
++
++ case VOIM_EVENT_ON_SET_USER_DICTIONARY:
++ {
++ /*
++ * voimSetUserDictionaryParameters
++ *
++ * voimDictionary *dictionary - The dictionary
++ * bool successful - True if the operation was successful
++ */
++ const voimSetUserDictionaryParameters *p = (const voimSetUserDictionaryParameters *)eventParameters;
++
++ if (p->successful)
++ qCDebug(qlcVKMyScript) << "user dictionary has been set";
++ else
++ qCCritical(qlcVKMyScript) << "failed to set user dictionary";
++ }
++ break;
++
++ case VOIM_EVENT_ON_ADD_STROKE:
++ {
++ /*
++ * voimAddStrokeParameters
++ *
++ * int sessionIndex - The index of the recognition session
++ * int strokeIndex - The index of the stroke in the recognition session
++ * bool successful - True if the operation was successful
++ */
++ const voimAddStrokeParameters *p = (const voimAddStrokeParameters *)eventParameters;
++
++ if (p->successful) {
++ qCDebug(qlcVKMyScript) << "a stroke with sessionIndex \"" << p->sessionIndex << "\", " <<
++ "strokeIndex \"" << p->strokeIndex << "\" has been added";
++ } else {
++ qCCritical(qlcVKMyScript) << "failed to add stroke";
++ }
++ }
++ break;
++
++ case VOIM_EVENT_ON_RECOGNITION_START:
++ {
++ /*
++ * voimRecognitionStartParameters
++ *
++ * int firstStrokeIndex - The index of the first new stroke to be recognized
++ * int sessionIndex - The index of the recognition session
++ * int strokeCount - The number of new strokes to be recognized
++ */
++ const voimRecognitionStartParameters *p = (const voimRecognitionStartParameters *)eventParameters;
++
++ qCDebug(qlcVKMyScript) << "recognition started at sessionIndex \"" << p->sessionIndex << "\", " <<
++ "firstStrokeIndex \"" << p->firstStrokeIndex << "\", " <<
++ "strokeCount \"" << p->strokeCount << "\"";
++
++ m_isProcessing = true;
++ }
++ break;
++
++ case VOIM_EVENT_ON_RECOGNITION_PROGRESS:
++ {
++ /*
++ * voimRecognitionProgressParameters
++ *
++ * int amountDone - The current amount of work done
++ * int amountToDo - The current amount of work to do
++ */
++ const voimRecognitionProgressParameters *p = (const voimRecognitionProgressParameters *)eventParameters;
++
++ if (p->amountDone == p->amountToDo)
++ qCDebug(qlcVKMyScript) << "progress recognition, " << p->amountDone << "/" << p->amountToDo;
++ }
++ break;
++
++ case VOIM_EVENT_ON_RECOGNITION_END:
++ {
++ /*
++ * voimRecognitionEndParameters
++ *
++ * bool successful - True if the operation was successful
++ */
++ const voimRecognitionEndParameters *p = (const voimRecognitionEndParameters *)eventParameters;
++
++ if (p->successful) {
++ qCDebug(qlcVKMyScript) << "recognition has been ended";
++
++ if (!m_onManagingResult) {
++ m_onManagingResult = true;
++ if (m_threadController) {
++ m_threadController->emitRecognitionEnded();
++ }
++ }
++ } else {
++ qCCritical(qlcVKMyScript) << "failed to finish recognition";
++ }
++ }
++ break;
++
++ case VOIM_EVENT_ON_COMMIT:
++ {
++ /*
++ * voimCommitParameters
++ *
++ * bool successful - True if the operation was successful
++ */
++ const voimCommitParameters *p = (const voimCommitParameters *)eventParameters;
++
++ if (p->successful) {
++ qCDebug(qlcVKMyScript) << "recognition has been committed";
++
++ if (m_threadController) {
++ m_threadController->emitRecognitionCommitted();
++ }
++ } else {
++ qCCritical(qlcVKMyScript) << "failed to commit recognition";
++ }
++ }
++ break;
++
++ case VOIM_EVENT_ON_CANCEL:
++ {
++ /*
++ * voimCancelParameters
++ *
++ * bool successful - True if the operation was successful
++ */
++ const voimCancelParameters *p = (const voimCancelParameters *)eventParameters;
++
++ if (p->successful)
++ qCDebug(qlcVKMyScript) << "recognition has been canceled";
++ else
++ qCCritical(qlcVKMyScript) << "failed to cancel recognition";
++ }
++ break;
++
++ case VOIM_EVENT_ON_FLOW_SYNC:
++ {
++ /*
++ * voimFlowSyncParameters
++ *
++ * int intValue - The integer value that was passed to the voim_flowSync() function
++ */
++ const voimFlowSyncParameters *p = (const voimFlowSyncParameters *)eventParameters;
++
++ qCDebug(qlcVKMyScript) << "voim_flowSync() has been called with value \"" << p->intValue << "\"";
++ }
++ break;
++
++ case VOIM_EVENT_ON_NEW_INPUT_ITEM:
++ {
++ /*
++ * voimNewInputItemParameters
++ *
++ * bool successful - True if the operation was successful
++ */
++ const voimNewInputItemParameters *p = (const voimNewInputItemParameters *)eventParameters;
++
++ if (p->successful)
++ qCDebug(qlcVKMyScript) << "new recognition session being created while remaining in the current session";
++ else
++ qCCritical(qlcVKMyScript) << "failed to create new recogniiton session";
++ }
++ break;
++
++ case VOIM_EVENT_ON_ADD_STRING:
++ {
++ /*
++ * [Note that the parameters are same as that of the VOIM_EVENT_ON_ADD_STROKE event
++ * because references to it in the result work the same (using stroke index
++ * for string and point index for its characters)]
++ *
++ * voimAddStrokeParameters
++ *
++ * int sessionIndex - The index of the recognition session
++ * int strokeIndex - The index of the stroke in the recognition session
++ * bool successful - True if the operation was successful
++ */
++ const voimAddStrokeParameters *p = (const voimAddStrokeParameters *)eventParameters;
++
++ if (p->successful) {
++ qCDebug(qlcVKMyScript) << "a stroke with sessionIndex \"" << p->sessionIndex << "\", " <<
++ "strokeIndex \"" << p->strokeIndex << "\" has been added";
++ } else {
++ qCCritical(qlcVKMyScript) << "failed to add string";
++ }
++ }
++ break;
++
++ default:
++ break;
++ }
++ }
++
++ void updateCandidateView(void)
++ {
++ Q_Q(MyScriptInputMethod);
++
++ emit q->selectionListChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList);
++ emit q->selectionListActiveItemChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList, wordIndex);
++ }
++
++ void updatePreeditTextCursor(int cursorPosition)
++ {
++ Q_Q(MyScriptInputMethod);
++
++ QVirtualKeyboardInputContext *ic = q->inputContext();
++ if (!ic)
++ return;
++
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO;
++ #ifdef SENSITIVE_DEBUG
++ qCDebug(qlcVKMyScript) << "preeditText:" << ic->preeditText();
++ #endif
++
++ bool isItemChanged = false;
++ int lastPosition = 0;
++ QList<std::pair<int, CandidateItem *>>::const_iterator iter;
++
++ for (iter = m_items.cbegin(); iter != m_items.cend(); iter++) {
++ int itemIndex = iter->first;
++ CandidateItem *candidateItem = iter->second;
++ int candidateIndex = candidateItem->candidateIndex;
++ QString candidate = candidateItem->candidates.at(candidateIndex);
++
++ lastPosition += candidate.length();
++ if (candidate != " " && cursorPosition <= lastPosition) {
++ m_itemStartPosition = lastPosition - candidate.length();
++ m_itemLength = candidate.length();
++
++ if (m_itemIndex != itemIndex) {
++ clearCandidates();
++ word = candidate;
++ wordIndex = candidateIndex;
++ wordCandidates = candidateItem->candidates;
++
++ m_itemIndex = itemIndex;
++ isItemChanged = true;
++ }
++
++ break;
++ }
++ }
++
++ if (isItemChanged)
++ updateCandidateView();
++ }
++
++ MyScriptInputMethod *q_ptr;
++
++ QScopedPointer<MyScriptRecognizeController> m_threadController;
++
++ voimEngine *m_engine;
++ voimLanguageManager *m_languageManager;
++ voimRecognizer *m_recognizer;
++ bool m_onManagingResult;
++ bool m_isProcessing;
++ int m_commitTimer;
++
++ QList<QVirtualKeyboardTrace *> traceList;
++
++ QVirtualKeyboardInputEngine::TextCase textCase;
++ QStringList wordCandidates;
++ QString word;
++ int wordIndex;
++
++ int m_itemIndex;
++ int m_itemStartPosition;
++ int m_itemLength;
++ QList<std::pair<int, CandidateItem *>> m_items;
++
++ QString m_locale;
++ Qt::InputMethodHints m_inputMethodHints;
++
++ int m_preeditCursorPosition;
++ };
++
++ /*!
++ \class QtVirtualKeyboard::MyScriptInputMethod
++ \internal
++ */
++
++ MyScriptInputMethod::MyScriptInputMethod(QObject *parent) :
++ QVirtualKeyboardAbstractInputMethod(parent),
++ d_ptr(new MyScriptInputMethodPrivate(this))
++ {
++ connect(this, SIGNAL(preeditTextChanged(QString,bool,int,int,int)),
++ this, SLOT(setPreeditText(QString,bool,int,int,int)));
++ connect(this, SIGNAL(gestureDetected(int,int)),
++ this, SLOT(doGestureAction(int,int)));
++ }
++
++ MyScriptInputMethod::~MyScriptInputMethod()
++ {
++
++ }
++
++ QList<QVirtualKeyboardInputEngine::InputMode> MyScriptInputMethod::inputModes(const QString &locale)
++ {
++ Q_UNUSED(locale);
++ return QList<QVirtualKeyboardInputEngine::InputMode>()
++ << QVirtualKeyboardInputEngine::InputMode::Latin;
++ }
++
++ bool MyScriptInputMethod::setInputMode(const QString &locale, QVirtualKeyboardInputEngine::InputMode inputMode)
++ {
++ Q_UNUSED(inputMode);
++ Q_D(MyScriptInputMethod);
++ QVirtualKeyboardInputContext *ic = inputContext();
++ if (d->setMode(locale, ic->inputMethodHints())) {
++ d->m_locale = locale;
++ return true;
++ }
++ return false;
++ }
++
++ bool MyScriptInputMethod::setTextCase(QVirtualKeyboardInputEngine::TextCase textCase)
++ {
++ Q_D(MyScriptInputMethod);
++ d->textCase = textCase;
++ return true;
++ }
++
++ bool MyScriptInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::KeyboardModifiers modifiers)
++ {
++ Q_UNUSED(text);
++ Q_UNUSED(modifiers);
++
++ Q_D(MyScriptInputMethod);
++ switch (key) {
++ case Qt::Key_Backspace:
++ d->handleBackspace();
++ break;
++ default:
++ d->cancelRecognition();
++ if (inputContext())
++ inputContext()->commit();
++ break;
++ }
++ return false;
++ }
++
++ void MyScriptInputMethod::reset()
++ {
++ QVirtualKeyboardInputContext *ic = inputContext();
++ if (ic) {
++ Q_D(MyScriptInputMethod);
++ d->clearCandidates();
++ ic->commit();
++ }
++ }
++
++ void MyScriptInputMethod::update()
++ {
++ Q_D(MyScriptInputMethod);
++ if (d->m_isProcessing)
++ d->cancelRecognition();
++
++ reset();
++ }
++
++ QList<QVirtualKeyboardSelectionListModel::Type> MyScriptInputMethod::selectionLists()
++ {
++ return QList<QVirtualKeyboardSelectionListModel::Type>() << QVirtualKeyboardSelectionListModel::Type::WordCandidateList;
++ }
++
++ int MyScriptInputMethod::selectionListItemCount(QVirtualKeyboardSelectionListModel::Type type)
++ {
++ Q_D(MyScriptInputMethod);
++
++ if (type != QVirtualKeyboardSelectionListModel::Type::WordCandidateList)
++ return 0;
++
++ return d->wordCandidates.size();
++ }
++
++ QVariant MyScriptInputMethod::selectionListData(QVirtualKeyboardSelectionListModel::Type type, int index, QVirtualKeyboardSelectionListModel::Role role)
++ {
++ Q_D(MyScriptInputMethod);
++
++ if (type != QVirtualKeyboardSelectionListModel::Type::WordCandidateList)
++ return QVariant();
++
++ switch (role) {
++ case QVirtualKeyboardSelectionListModel::Role::Display:
++ return QVariant(d->wordCandidates.at(index));
++ case QVirtualKeyboardSelectionListModel::Role::WordCompletionLength:
++ {
++ const QString wordCandidate(d->wordCandidates.at(index));
++ int wordCompletionLength = wordCandidate.length() - d->word.length();
++ return QVariant((wordCompletionLength > 0 && wordCandidate.startsWith(d->word)) ? wordCompletionLength : 0);
++ }
++ default:
++ return QVirtualKeyboardAbstractInputMethod::selectionListData(type, index, role);
++ }
++ }
++
++ void MyScriptInputMethod::selectionListItemSelected(QVirtualKeyboardSelectionListModel::Type type, int index)
++ {
++ Q_D(MyScriptInputMethod);
++
++ if (d->m_isProcessing)
++ return;
++
++ QVirtualKeyboardInputContext *ic = inputContext();
++ if (!ic)
++ return;
++
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO;
++
++ QVirtualKeyboardAbstractInputMethod::selectionListItemSelected(type, index);
++
++ int itemIndex = d->m_itemIndex;
++ CandidateItem *candidateItem = d->m_items.at(itemIndex).second;
++
++ QString candidate = candidateItem->candidates.at(index);
++ candidateItem->candidateIndex = index;
++
++ QString label = ic->preeditText();
++ label.replace(d->m_itemStartPosition, d->m_itemLength, candidate);
++ setPreeditText(label, true, d->m_preeditCursorPosition, d->m_itemStartPosition, candidate.length());
++
++ d->updatePreeditTextCursor(d->m_preeditCursorPosition);
++ }
++
++ QList<QVirtualKeyboardInputEngine::PatternRecognitionMode> MyScriptInputMethod::patternRecognitionModes() const
++ {
++ return QList<QVirtualKeyboardInputEngine::PatternRecognitionMode>()
++ << QVirtualKeyboardInputEngine::PatternRecognitionMode::Handwriting;
++ }
++
++ QVirtualKeyboardTrace *MyScriptInputMethod::traceBegin(
++ int traceId, QVirtualKeyboardInputEngine::PatternRecognitionMode patternRecognitionMode,
++ const QVariantMap &traceCaptureDeviceInfo, const QVariantMap &traceScreenInfo)
++ {
++ Q_D(MyScriptInputMethod);
++
++ return d->traceBegin(traceId, patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo);
++ }
++
++ bool MyScriptInputMethod::traceEnd(QVirtualKeyboardTrace *trace)
++ {
++ Q_D(MyScriptInputMethod);
++
++ d->traceEnd(trace);
++ return true;
++ }
++
++ bool MyScriptInputMethod::clickPreeditText(int cursorPosition)
++ {
++ Q_D(MyScriptInputMethod);
++
++ if (d->m_isProcessing)
++ return true;
++
++ QVirtualKeyboardInputContext *ic = inputContext();
++ if (ic) {
++ setPreeditText(ic->preeditText(), true, cursorPosition);
++ }
++
++ return true;
++ }
++
++ void MyScriptInputMethod::timerEvent(QTimerEvent *timerEvent)
++ {
++ Q_D(MyScriptInputMethod);
++
++ if (timerEvent->timerId() == d->m_commitTimer) {
++ d->commit();
++ }
++ }
++
++ void MyScriptInputMethod::setPreeditText(QString label, bool isCommitted, int cursorPosition, int highlightStart, int highlightLength)
++ {
++ Q_D(MyScriptInputMethod);
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO;
++
++ QList<QInputMethodEvent::Attribute> attributes;
++ QBrush foreground = isCommitted ? QBrush(Qt::black) : QBrush(Qt::blue);
++ QBrush backgroundNormal = QBrush(Qt::white);
++ QBrush backgroundHighlight = QBrush(QColor(0x66, 0xCD, 0xAA));
++
++ QTextCharFormat textFormat;
++ textFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
++ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, label.length(), textFormat));
++
++ int highlightEnd = highlightStart + highlightLength;
++ if (highlightLength > 0 && highlightStart <= label.length() && highlightEnd <= label.length()) {
++
++ if (highlightStart > 0) {
++ QTextCharFormat normal;
++ normal.setBackground(backgroundNormal);
++ normal.setForeground(foreground);
++ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, highlightStart, normal));
++ }
++
++ QTextCharFormat highlight;
++ highlight.setBackground(backgroundHighlight);
++ highlight.setForeground(foreground);
++ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, highlightStart, highlightLength, highlight));
++
++ if (highlightEnd < label.length()) {
++ QTextCharFormat normal;
++ int highlightLength = label.length() - highlightEnd;
++ normal.setBackground(backgroundNormal);
++ normal.setForeground(foreground);
++ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, highlightEnd, highlightLength, normal));
++ }
++ } else {
++
++ QTextCharFormat normal;
++ normal.setBackground(backgroundNormal);
++ normal.setForeground(foreground);
++ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, label.length(), normal));
++ }
++
++ d->m_preeditCursorPosition = (cursorPosition != -1) ? cursorPosition : label.length();
++ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, d->m_preeditCursorPosition, 1, QVariant()));
++
++ inputContext()->setPreeditText(label, attributes);
++ if (isCommitted) {
++ Q_D(MyScriptInputMethod);
++ d->updatePreeditTextCursor(d->m_preeditCursorPosition);
++ }
++ }
++
++ void MyScriptInputMethod::doGestureAction(const int gestureType, const int gestureCount)
++ {
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO;
++
++ Q_D(MyScriptInputMethod);
++ d->cancelRecognition();
++
++ QVirtualKeyboardInputContext *ic = inputContext();
++ if (ic) {
++ switch (gestureType) {
++ case GESTURE_TYPE_LEFT_TO_RIGHT:
++ if (d->m_locale.contains("ar")) {
++ ic->inputEngine()->virtualKeyClick(Qt::Key_Backspace, QString(), Qt::NoModifier);
++ } else {
++ for (int i = 0; i < gestureCount; i++)
++ ic->inputEngine()->virtualKeyClick(Qt::Key_Space, QLatin1String(" "), Qt::NoModifier);
++ }
++ break;
++
++ case GESTURE_TYPE_RIGHT_TO_LEFT:
++ if (d->m_locale.contains("ar")) {
++ for (int i = 0; i < gestureCount; i++)
++ ic->inputEngine()->virtualKeyClick(Qt::Key_Space, QLatin1String(" "), Qt::NoModifier);
++ } else {
++ ic->inputEngine()->virtualKeyClick(Qt::Key_Backspace, QString(), Qt::NoModifier);
++ }
++ break;
++
++ case GESTURE_TYPE_DOWN_THEN_LEFT:
++ if (!d->m_locale.contains("ar"))
++ ic->inputEngine()->virtualKeyClick(Qt::Key_Return, QString(), Qt::NoModifier);
++ break;
++
++ case GESTURE_TYPE_DOWN_THEN_RIGHT:
++ if (d->m_locale.contains("ar"))
++ ic->inputEngine()->virtualKeyClick(Qt::Key_Return, QString(), Qt::NoModifier);
++ break;
++ }
++ }
++ }
++
++ MyScriptRecognizeWorker::MyScriptRecognizeWorker(voimEngine *engine, voimRecognizer *recognizer)
++ : QObject()
++ , m_engine(engine)
++ , m_recognizer(recognizer)
++ , m_resultLabel(QString())
++ {
++ }
++
++ void MyScriptRecognizeWorker::manageRecognitionEnded() {
++ qCDebug(qlcVKMyScript) << "recognition has been ended (thread)";
++ manageRecognitionResult(m_engine, m_recognizer, false);
++ }
++
++ void MyScriptRecognizeWorker::manageRecognitionCommitted() {
++ qCDebug(qlcVKMyScript) << "recognition has been committed (thread)";
++ manageRecognitionResult(m_engine, m_recognizer, true);
++ }
++
++ void MyScriptRecognizeWorker::manageRecognitionResult(voimEngine *engine, voimRecognizer *recognizer, const bool isCommitted)
++ {
++ Q_ASSERT(engine != nullptr && recognizer != nullptr);
++
++ voimResult *result = voim_getResult(engine, recognizer, false, true);
++ if (result != NULL) {
++
++ QString resultLabel = "";
++ qCDebug(qlcVKMyScript) << ">>> recognition result";
++
++ QStringList wordCandidates;
++ QString word;
++ int wordIndex = -1;
++
++ int gestureType = GESTURE_TYPE_NONE;
++ int gestureCount = 1;
++
++ emit clearItem();
++
++ int itemCount = voim_getItemCount(engine, result);
++ for (int itemIndex = 0; itemIndex < itemCount; itemIndex++) {
++
++ wordCandidates.clear();
++ word = QString();
++ wordIndex = -1;
++
++ qCDebug(qlcVKMyScript) << " * item #" << itemIndex << " of " << itemCount;
++
++ int candidateCount = voim_getItemCandidateCount(engine, result, itemIndex);
++ for (int candidateIndex = 0; candidateIndex < candidateCount; candidateIndex++) {
++
++ int length = voim_getItemCandidateLabel(engine, result, itemIndex, candidateIndex, nullptr, 0, "UTF-8");
++
++ if (length > 0) {
++
++ std::vector<char> bytes(length + 1);
++ char *temp = bytes.data();
++
++ voim_getItemCandidateLabel(engine, result, itemIndex, candidateIndex, temp, length, "UTF-8");
++ #ifdef SENSITIVE_DEBUG
++ float score = voim_getItemCandidateScore(engine, result, itemIndex, candidateIndex);
++ qCDebug(qlcVKMyScript) << " - candidate #" << candidateIndex << " of " << candidateCount << " :" << temp << "(" << score << ")";
++ #endif
++
++ QString label = QString::fromUtf8(temp);
++
++ if (candidateIndex == 0) {
++ gestureType = isGesture(label);
++
++ if (gestureType == GESTURE_TYPE_NONE) {
++ resultLabel += label;
++ } else {
++ gestureCount = label.length() / QString::fromStdString(GESTURE_STRING_LEFT_TO_RIGHT).length();
++ break;
++ }
++ }
++
++ if (isGesture(label) == GESTURE_TYPE_NONE) {
++ if (wordIndex == -1) {
++ word = label;
++ wordIndex = candidateIndex;
++ }
++
++ wordCandidates << label;
++ }
++ }
++ }
++
++ if (gestureType != GESTURE_TYPE_NONE)
++ break;
++
++ emit newItem(itemIndex, wordIndex, wordCandidates);
++ }
++
++ voim_destroyResult(m_engine, result);
++
++ if (gestureType != GESTURE_TYPE_NONE) {
++ emit clearTraces();
++ emit gestureDetected(gestureType, gestureCount);
++ } else {
++ m_resultLabel = resultLabel;
++ emit preeditChanged(m_resultLabel, false);
++ emit newCandidates(wordCandidates, word, wordIndex);
++ }
++
++ } else if (result == NULL && voim_getError(m_engine) != VOIM_EC_NO_ERROR) {
++ qCCritical(qlcVKMyScript) << "voim_getResult() failed -" << getVoimErrorMessage(voim_getError(m_engine));
++ }
++
++ emit recognitionEnded();
++
++ if (isCommitted) {
++ emit clearTraces();
++ emit preeditChanged(m_resultLabel, true);
++ emit recognitionCommitted();
++ }
++ }
++
++ int MyScriptRecognizeWorker::isGesture(const QString label)
++ {
++ if (label.length() < 2)
++ return GESTURE_TYPE_NONE;
++
++ if (label.contains(QString::fromUtf8(GESTURE_STRING_RIGHT_TO_LEFT)))
++ return GESTURE_TYPE_RIGHT_TO_LEFT;
++
++ if (label.contains(QString::fromUtf8(GESTURE_STRING_LEFT_TO_RIGHT)))
++ return GESTURE_TYPE_LEFT_TO_RIGHT;
++
++ if (label.contains(QString::fromUtf8(GESTURE_STRING_DOWN_THEN_LEFT)))
++ return GESTURE_TYPE_DOWN_THEN_LEFT;
++
++ if (label.contains(QString::fromUtf8(GESTURE_STRING_DOWN_THEN_RIGHT)))
++ return GESTURE_TYPE_DOWN_THEN_RIGHT;
++
++ return GESTURE_TYPE_NONE;
++ }
++
++ MyScriptRecognizeController::MyScriptRecognizeController(MyScriptInputMethodPrivate *d_, voimEngine *engine, voimRecognizer *recognizer)
++ : QObject()
++ , d(d_)
++ {
++ MyScriptRecognizeWorker *worker = new MyScriptRecognizeWorker(engine, recognizer);
++ worker->moveToThread(&workerThread);
++ connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
++ connect(this, &MyScriptRecognizeController::recognitionEnded, worker, &MyScriptRecognizeWorker::manageRecognitionEnded);
++ connect(this, &MyScriptRecognizeController::recognitionCommitted, worker, &MyScriptRecognizeWorker::manageRecognitionCommitted);
++ connect(worker, &MyScriptRecognizeWorker::recognitionEnded, this, &MyScriptRecognizeController::handleRecognitionEnded);
++ connect(worker, &MyScriptRecognizeWorker::recognitionCommitted, this, &MyScriptRecognizeController::handleRecognitionCommitted);
++ connect(worker, &MyScriptRecognizeWorker::gestureDetected, this, &MyScriptRecognizeController::handleGestureDetected);
++ connect(worker, &MyScriptRecognizeWorker::preeditChanged, this, &MyScriptRecognizeController::handlePreeditChanged);
++ connect(worker, &MyScriptRecognizeWorker::clearItem, this, &MyScriptRecognizeController::handleClearItem);
++ connect(worker, &MyScriptRecognizeWorker::newItem, this, &MyScriptRecognizeController::handleNewItem);
++ connect(worker, &MyScriptRecognizeWorker::newCandidates, this, &MyScriptRecognizeController::handleNewCandidates);
++ connect(worker, &MyScriptRecognizeWorker::clearTraces, this, &MyScriptRecognizeController::handleClearTraces);
++ workerThread.start();
++ }
++
++ MyScriptRecognizeController::~MyScriptRecognizeController()
++ {
++ workerThread.quit();
++ workerThread.wait();
++ }
++
++ void MyScriptRecognizeController::emitRecognitionEnded()
++ {
++ emit recognitionEnded();
++ }
++
++ void MyScriptRecognizeController::emitRecognitionCommitted()
++ {
++ emit recognitionCommitted();
++ }
++
++ void MyScriptRecognizeController::handleRecognitionEnded()
++ {
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO;
++ d->m_onManagingResult = false;
++ }
++
++ void MyScriptRecognizeController::handleRecognitionCommitted()
++ {
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO;
++ d->m_isProcessing = false;
++ }
++
++ void MyScriptRecognizeController::handleGestureDetected(const int gestureType, const int gestureCount)
++ {
++ qCDebug(qlcVKMyScript) << Q_FUNC_INFO;
++ emit d->q_ptr->gestureDetected(gestureType, gestureCount);
++ }
++
++ void MyScriptRecognizeController::handlePreeditChanged(const QString &preedit, const bool isCommitted)
++ {
++ emit d->q_ptr->preeditTextChanged(preedit, isCommitted, -1, 0, 0);
++ }
++
++ void MyScriptRecognizeController::handleClearItem()
++ {
++ d->clearItems();
++ }
++
++ void MyScriptRecognizeController::handleNewItem(const int itemIndex, const int candidateIndex, const QStringList &candidates)
++ {
++ CandidateItem *candidateItem = new CandidateItem;
++
++ candidateItem->candidateIndex = candidateIndex;
++ candidateItem->candidates = candidates;
++
++ d->m_items.push_back(std::make_pair(itemIndex, candidateItem));
++
++ d->m_itemIndex = itemIndex;
++ }
++
++ void MyScriptRecognizeController::handleNewCandidates(const QStringList &candidates, const QString &word, int wordIndex)
++ {
++ d->wordCandidates = candidates;
++ d->word = word;
++ d->wordIndex = wordIndex;
++
++ if (!word.isEmpty() && word != " " && word != "\u00A0") {
++ d->updateCandidateView();
++ }
++ }
++
++ void MyScriptRecognizeController::handleClearTraces()
++ {
++ d->clearTraces();
++ }
++
++ } // namespace QtVirtualKeyboard
++ QT_END_NAMESPACE