aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/lipi-toolkit/plugin
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/lipi-toolkit/plugin')
-rw-r--r--src/plugins/lipi-toolkit/plugin/lipi.json6
-rw-r--r--src/plugins/lipi-toolkit/plugin/lipiinputmethod.cpp639
-rw-r--r--src/plugins/lipi-toolkit/plugin/lipiinputmethod_p.h97
-rw-r--r--src/plugins/lipi-toolkit/plugin/lipiplugin.cpp43
-rw-r--r--src/plugins/lipi-toolkit/plugin/lipiplugin.h49
-rw-r--r--src/plugins/lipi-toolkit/plugin/lipisharedrecognizer.cpp408
-rw-r--r--src/plugins/lipi-toolkit/plugin/lipisharedrecognizer_p.h105
-rw-r--r--src/plugins/lipi-toolkit/plugin/lipiworker.cpp250
-rw-r--r--src/plugins/lipi-toolkit/plugin/lipiworker_p.h161
-rw-r--r--src/plugins/lipi-toolkit/plugin/plugin.pro68
10 files changed, 1826 insertions, 0 deletions
diff --git a/src/plugins/lipi-toolkit/plugin/lipi.json b/src/plugins/lipi-toolkit/plugin/lipi.json
new file mode 100644
index 00000000..8e22b4e9
--- /dev/null
+++ b/src/plugins/lipi-toolkit/plugin/lipi.json
@@ -0,0 +1,6 @@
+{
+ "Name": "handwriting",
+ "Provider": "Qt Lipi-Toolkit Extension",
+ "InputMethod": "HandwritingInputMethod",
+ "Version": 100
+}
diff --git a/src/plugins/lipi-toolkit/plugin/lipiinputmethod.cpp b/src/plugins/lipi-toolkit/plugin/lipiinputmethod.cpp
new file mode 100644
index 00000000..ca23c491
--- /dev/null
+++ b/src/plugins/lipi-toolkit/plugin/lipiinputmethod.cpp
@@ -0,0 +1,639 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 "lipiinputmethod_p.h"
+#include "lipisharedrecognizer_p.h"
+#include <QtVirtualKeyboard/qvirtualkeyboardinputengine.h>
+#include <QtVirtualKeyboard/qvirtualkeyboardinputcontext.h>
+#include <QtVirtualKeyboard/private/qvirtualkeyboardinputcontext_p.h>
+#include <QtVirtualKeyboard/private/shifthandler_p.h>
+#include <QLoggingCategory>
+#include <QtVirtualKeyboard/qvirtualkeyboardtrace.h>
+#include <QtVirtualKeyboard/private/handwritinggesturerecognizer_p.h>
+
+#ifdef QT_HUNSPELLINPUTMETHOD_LIB
+#include <QtHunspellInputMethod/private/hunspellinputmethod_p_p.h>
+#endif
+
+#include "LTKCaptureDevice.h"
+#include "LTKScreenContext.h"
+#include "LTKTraceGroup.h"
+#include "LTKChannel.h"
+#include "LTKTraceFormat.h"
+#include "LTKTrace.h"
+#include "LTKShapeRecoResult.h"
+
+#include <QCryptographicHash>
+
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+#include <QtVirtualKeyboard/private/unipentrace_p.h>
+#include <QStandardPaths>
+#endif
+
+QT_BEGIN_NAMESPACE
+namespace QtVirtualKeyboard {
+
+#ifdef QT_HUNSPELLINPUTMETHOD_LIB
+#define LipiInputMethodPrivateBase HunspellInputMethodPrivate
+#else
+#define LipiInputMethodPrivateBase DummyPrivate
+class DummyPrivate {};
+#endif
+
+Q_LOGGING_CATEGORY(lcLipi, "qt.virtualkeyboard.lipi")
+
+class LipiInputMethodPrivate : public LipiInputMethodPrivateBase
+{
+ Q_DECLARE_PUBLIC(LipiInputMethod)
+public:
+ LipiInputMethodPrivate(LipiInputMethod *q_ptr) :
+#ifdef QT_HUNSPELLINPUTMETHOD_LIB
+ LipiInputMethodPrivateBase(static_cast<HunspellInputMethod *>(q_ptr)),
+#else
+ LipiInputMethodPrivateBase(),
+#endif
+ q_ptr(q_ptr),
+ recognizeTimer(0),
+ textCase(QVirtualKeyboardInputEngine::TextCase::Lower)
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+ , unipenTrace(0)
+#endif
+ {
+ }
+
+ ~LipiInputMethodPrivate()
+ {
+ cancelRecognition();
+ }
+
+ 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)
+ {
+ QByteArray context = getContext(patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo);
+ if (context == currentContext)
+ return;
+
+ qCDebug(lcLipi) << "LipiInputMethodPrivate::setContext():" << QLatin1String(context.toHex());
+
+ clearTraces();
+
+ deviceInfo.reset(new LTKCaptureDevice());
+ deviceInfo->setSamplingRate(traceCaptureDeviceInfo.value(QLatin1String("sampleRate"), 60).toInt());
+ deviceInfo->setXDPI(traceCaptureDeviceInfo.value(QLatin1String("dpi"), 96).toInt());
+ deviceInfo->setYDPI(deviceInfo->getXDPI());
+ deviceInfo->setLatency(traceCaptureDeviceInfo.value(QLatin1String("latency"), 0.0).toFloat());
+ deviceInfo->setUniformSampling(traceCaptureDeviceInfo.value(QLatin1String("uniform"), false).toBool());
+
+ screenContext.reset(new LTKScreenContext());
+ QRectF boundingBox(traceScreenInfo.value(QLatin1String("boundingBox")).toRectF());
+ if (!boundingBox.isEmpty()) {
+ screenContext->setBboxLeft(boundingBox.left());
+ screenContext->setBboxTop(boundingBox.top());
+ screenContext->setBboxRight(boundingBox.right());
+ screenContext->setBboxBottom(boundingBox.bottom());
+ }
+
+ QVariantList horizontalRulers(traceScreenInfo.value(QLatin1String("horizontalRulers"), QVariantList()).toList());
+ if (!horizontalRulers.isEmpty()) {
+ for (QVariantList::ConstIterator i = horizontalRulers.constBegin();
+ i != horizontalRulers.constEnd(); i++) {
+ screenContext->addHLine(i->toFloat());
+ }
+ }
+
+ QVariantList verticalRulers(traceScreenInfo.value(QLatin1String("verticalRulers"), QVariantList()).toList());
+ if (!horizontalRulers.isEmpty()) {
+ for (QVariantList::ConstIterator i = verticalRulers.constBegin();
+ i != verticalRulers.constEnd(); i++) {
+ screenContext->addVLine(i->toFloat());
+ }
+ }
+
+ gestureRecognizer.setDpi(deviceInfo->getXDPI());
+
+ currentContext = context;
+ }
+
+ QVirtualKeyboardTrace *traceBegin(
+ int traceId, QVirtualKeyboardInputEngine::PatternRecognitionMode patternRecognitionMode,
+ const QVariantMap &traceCaptureDeviceInfo, const QVariantMap &traceScreenInfo)
+ {
+ Q_UNUSED(traceId)
+
+ stopRecognizeTimer();
+
+ setContext(patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo);
+
+ if (recognitionTask) {
+ recognizer.cancelRecognitionTask(recognitionTask);
+ recognitionTask.reset();
+ delayedResult.clear();
+ }
+
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+ if (!unipenTrace) {
+ Q_Q(LipiInputMethod);
+ unipenTrace = new UnipenTrace(traceCaptureDeviceInfo, traceScreenInfo, q);
+ }
+#endif
+
+ QVirtualKeyboardTrace *trace = new QVirtualKeyboardTrace();
+ trace->setChannels(QStringList(QLatin1String("t")));
+ traceList.append(trace);
+
+ return trace;
+ }
+
+ void traceEnd(QVirtualKeyboardTrace *trace)
+ {
+ if (trace->isCanceled()) {
+ qCDebug(lcLipi) << "LipiInputMethodPrivate::traceEnd(): discarded" << trace;
+ traceList.removeOne(trace);
+ delete trace;
+ } else {
+ addPointsToTraceGroup(trace);
+ }
+ handleGesture();
+ if (!traceList.isEmpty() && countActiveTraces() == 0)
+ restartRecognition();
+ }
+
+ int countActiveTraces() const
+ {
+ int count = 0;
+ for (QVirtualKeyboardTrace *trace : qAsConst(traceList)) {
+ if (!trace->isFinal())
+ count++;
+ }
+ return count;
+ }
+
+ void handleGesture()
+ {
+ if (countActiveTraces() > 0)
+ return;
+
+ QVariantMap gesture = gestureRecognizer.recognize(traceList);
+ if (gesture.isEmpty())
+ return;
+
+ qCDebug(lcLipi) << "LipiInputMethodPrivate::handleGesture():" << gesture;
+
+ if (gesture[QLatin1String("type")].toString() == QLatin1String("swipe")) {
+
+ static const int SWIPE_MIN_LENGTH = 25; // mm
+ static const int SWIPE_ANGLE_THRESHOLD = 15; // degrees +-
+
+ qreal swipeLength = gesture[QLatin1String("length_mm")].toReal();
+ if (swipeLength >= SWIPE_MIN_LENGTH) {
+
+ Q_Q(LipiInputMethod);
+ QVirtualKeyboardInputContext *ic = q->inputContext();
+ if (!ic)
+ return;
+
+ 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
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+ dumpTraces();
+ saveTraces(Qt::Key_Backspace, 100);
+#endif
+ cancelRecognition();
+ ic->inputEngine()->virtualKeyClick(Qt::Key_Backspace, QString(), Qt::NoModifier);
+ } else if (swipeTouchCount == 2) {
+ // Double swipe: reset word, or backspace
+ cancelRecognition();
+ if (!ic->preeditText().isEmpty()) {
+ q->reset();
+ ic->setPreeditText(QString());
+ } else {
+ ic->inputEngine()->virtualKeyClick(Qt::Key_Backspace, QString(), Qt::NoModifier);
+ }
+ }
+ return;
+ }
+
+ // Swipe right
+ if (swipeAngle <= SWIPE_ANGLE_THRESHOLD || swipeAngle >= 360 - SWIPE_ANGLE_THRESHOLD) {
+ if (swipeTouchCount == 1) {
+ // Single swipe: space
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+ dumpTraces();
+ saveTraces(Qt::Key_Space, 100);
+#endif
+ cancelRecognition();
+ ic->inputEngine()->virtualKeyClick(Qt::Key_Space, QLatin1String(" "), Qt::NoModifier);
+ } else if (swipeTouchCount == 2) {
+ // Double swipe: commit word, or insert space
+ cancelRecognition();
+#ifdef QT_HUNSPELLINPUTMETHOD_LIB
+ int activeWordIndex = wordCandidates.index();
+ if (activeWordIndex != -1) {
+ q->selectionListItemSelected(QVirtualKeyboardSelectionListModel::Type::WordCandidateList, activeWordIndex);
+ return;
+ }
+#endif
+ ic->inputEngine()->virtualKeyClick(Qt::Key_Space, QLatin1String(" "), Qt::NoModifier);
+ }
+ return;
+ }
+
+ // Swipe up
+ if (swipeAngle <= 270 + SWIPE_ANGLE_THRESHOLD && swipeAngle >= 270 - SWIPE_ANGLE_THRESHOLD) {
+ if (swipeTouchCount == 1) {
+ // Single swipe: toggle input mode
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+ dumpTraces();
+ saveTraces(Qt::Key_Mode_switch, 100);
+#endif
+ cancelRecognition();
+ if (!(ic->inputMethodHints() & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly))) {
+ QVirtualKeyboardInputEngine::InputMode inputMode = ic->inputEngine()->inputMode();
+ inputMode = inputMode == QVirtualKeyboardInputEngine::InputMode::Latin ?
+ QVirtualKeyboardInputEngine::InputMode::Numeric : QVirtualKeyboardInputEngine::InputMode::Latin;
+ ic->inputEngine()->setInputMode(inputMode);
+ }
+ } else if (swipeTouchCount == 2) {
+ // Double swipe: toggle text case
+ cancelRecognition();
+ ic->priv()->shiftHandler()->toggleShift();
+ }
+ return;
+ }
+ }
+ }
+ }
+
+ void clearTraces()
+ {
+ qDeleteAll(traceList);
+ traceList.clear();
+ traceGroup.emptyAllTraces();
+ }
+
+ void addPointsToTraceGroup(QVirtualKeyboardTrace *trace)
+ {
+ vector<LTKChannel> channels;
+ channels.push_back(LTKChannel("X", DT_INT, true));
+ channels.push_back(LTKChannel("Y", DT_INT, true));
+ bool hasTime = trace->channels().contains(QLatin1String("t"));
+ if (hasTime)
+ channels.push_back(LTKChannel("T", DT_FLOAT, true));
+ LTKTraceFormat traceFormat(channels);
+ LTKTrace ltktrace(traceFormat);
+
+ const QVariantList points = trace->points();
+ const QVariantList timeData = hasTime ? trace->channelData(QLatin1String("t")) : QVariantList();
+ QVariantList::ConstIterator t = timeData.constBegin();
+ for (const QVariant &p : points) {
+ const QPointF pt(p.toPointF());
+ vector<float> point;
+ point.push_back(pt.x());
+ point.push_back(pt.y());
+ if (hasTime) {
+ point.push_back(t->toFloat());
+ t++;
+ }
+ ltktrace.addPoint(point);
+ }
+ traceGroup.addTrace(ltktrace);
+ }
+
+ void finishRecognition()
+ {
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+ dumpTraces();
+#endif
+ stopRecognizeTimer();
+ clearTraces();
+ if (recognitionTask && !delayedResult.isEmpty() && recognitionTask->resultId() == delayedResult[QLatin1String("resultId")].toInt())
+ processResult(delayedResult);
+ delayedResult.clear();
+ recognitionTask.reset();
+ }
+
+ void restartRecognition()
+ {
+ recognitionTask = recognizer.newRecognition(*deviceInfo, *screenContext, subsetOfClasses, 0.0f, 4);
+ if (recognitionTask) {
+ Q_Q(LipiInputMethod);
+
+ recognitionTask->traceGroup = traceGroup;
+
+ QSharedPointer<LipiRecognitionResultsTask> resultsTask = recognizer.startRecognition(recognitionTask);
+ q->connect(resultsTask.data(), SIGNAL(resultsAvailable(const QVariantList &)), SLOT(resultsAvailable(const QVariantList &)));
+
+ resetRecognizeTimer();
+ } else {
+ stopRecognizeTimer();
+ }
+ }
+
+ bool cancelRecognition()
+ {
+ stopRecognizeTimer();
+ clearTraces();
+ delayedResult.clear();
+ bool result = !recognitionTask.isNull();
+ recognitionTask.reset();
+ return recognizer.cancelRecognition() || result;
+ }
+
+ void resetRecognizeTimer()
+ {
+ Q_Q(LipiInputMethod);
+ stopRecognizeTimer();
+ recognizeTimer = q->startTimer(300);
+ }
+
+ void stopRecognizeTimer()
+ {
+ if (recognizeTimer) {
+ Q_Q(LipiInputMethod);
+ q->killTimer(recognizeTimer);
+ recognizeTimer = 0;
+ }
+ }
+
+ void resultsAvailable(const QVariantList &resultList)
+ {
+ if (!resultList.isEmpty()) {
+ const QVariantMap result = resultList.at(0).toMap();
+ if (recognitionTask && recognitionTask->resultId() == result[QLatin1String("resultId")].toInt())
+ delayedResult = result;
+ else
+ processResult(result);
+ }
+ }
+
+ void processResult(const QVariantMap &result)
+ {
+ const QChar ch = result[QLatin1String("unicode")].toChar();
+ const QChar chUpper = ch.toUpper();
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+ // In recording mode, the text case must match with the current text case
+ if (unipenTrace) {
+ if (!ch.isLetter() || (ch.isUpper() == (textCase == QVirtualKeyboardInputEngine::TextCase::Upper)))
+ saveTraces(ch.unicode(), qRound(result[QLatin1String("confidence")].toDouble() * 100));
+ delete unipenTrace;
+ unipenTrace = 0;
+ }
+#endif
+ Q_Q(LipiInputMethod);
+ q->inputContext()->inputEngine()->virtualKeyClick((Qt::Key)chUpper.unicode(),
+ textCase == QVirtualKeyboardInputEngine::TextCase::Lower ? QString(ch.toLower()) : QString(chUpper),
+ Qt::NoModifier);
+ }
+
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+ void dumpTraces()
+ {
+ if (unipenTrace)
+ unipenTrace->record(traceList);
+ }
+
+ void saveTraces(uint unicode, uint confidence)
+ {
+ if (!unipenTrace)
+ return;
+
+ QStringList homeLocations = QStandardPaths::standardLocations(QStandardPaths::HomeLocation);
+ if (!homeLocations.isEmpty()) {
+ QString filePath = QStringLiteral("%1/%2").arg(homeLocations.at(0)).arg(QLatin1String("VIRTUAL_KEYBOARD_TRACES"));
+ unipenTrace->setDirectory(filePath);
+ unipenTrace->save(unicode, confidence);
+ }
+ }
+#endif
+
+ LipiInputMethod *q_ptr;
+ LipiSharedRecognizer recognizer;
+ QByteArray currentContext;
+ QScopedPointer<LTKCaptureDevice> deviceInfo;
+ QScopedPointer<LTKScreenContext> screenContext;
+ QSharedPointer<LipiRecognitionTask> recognitionTask;
+ LTKTraceGroup traceGroup;
+ QList<QVirtualKeyboardTrace *> traceList;
+ int recognizeTimer;
+ QVirtualKeyboardInputEngine::TextCase textCase;
+ vector<int> subsetOfClasses;
+ QVariantMap delayedResult;
+ HandwritingGestureRecognizer gestureRecognizer;
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+ UnipenTrace *unipenTrace;
+#endif
+};
+
+/*!
+ \class QtVirtualKeyboard::LipiInputMethod
+ \internal
+*/
+
+LipiInputMethod::LipiInputMethod(QObject *parent) :
+#ifdef QT_HUNSPELLINPUTMETHOD_LIB
+ LipiInputMethodBase(new LipiInputMethodPrivate(this), parent)
+#else
+ LipiInputMethodBase(parent),
+ d_ptr(new LipiInputMethodPrivate(this))
+#endif
+{
+}
+
+LipiInputMethod::~LipiInputMethod()
+{
+}
+
+QList<QVirtualKeyboardInputEngine::InputMode> LipiInputMethod::inputModes(const QString &locale)
+{
+ Q_UNUSED(locale)
+ QList<QVirtualKeyboardInputEngine::InputMode> availableInputModes;
+ const Qt::InputMethodHints inputMethodHints(inputContext()->inputMethodHints());
+
+ 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 {
+ availableInputModes.append(QVirtualKeyboardInputEngine::InputMode::Latin);
+ availableInputModes.append(QVirtualKeyboardInputEngine::InputMode::Numeric);
+ }
+
+ return availableInputModes;
+}
+
+bool LipiInputMethod::setInputMode(const QString &locale, QVirtualKeyboardInputEngine::InputMode inputMode)
+{
+ Q_D(LipiInputMethod);
+#ifdef QT_HUNSPELLINPUTMETHOD_LIB
+ HunspellInputMethod::setInputMode(locale, inputMode);
+#else
+ Q_UNUSED(locale)
+#endif
+ bool result = d->recognizer.setModel(QStringLiteral("SHAPEREC_ALPHANUM"));
+ if (!result)
+ return false;
+ d->subsetOfClasses.clear();
+ switch (inputMode) {
+ case QVirtualKeyboardInputEngine::InputMode::Latin:
+ d->recognizer.subsetOfClasses(QStringLiteral("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz?,.@"), d->subsetOfClasses);
+ break;
+ case QVirtualKeyboardInputEngine::InputMode::Numeric:
+ case QVirtualKeyboardInputEngine::InputMode::Dialable:
+ d->recognizer.subsetOfClasses(QStringLiteral("1234567890,.+"), d->subsetOfClasses);
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+bool LipiInputMethod::setTextCase(QVirtualKeyboardInputEngine::TextCase textCase)
+{
+ Q_D(LipiInputMethod);
+ d->textCase = textCase;
+#ifdef QT_HUNSPELLINPUTMETHOD_LIB
+ HunspellInputMethod::setTextCase(textCase);
+#endif
+ return true;
+}
+
+bool LipiInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::KeyboardModifiers modifiers)
+{
+#ifdef QT_HUNSPELLINPUTMETHOD_LIB
+ Q_D(LipiInputMethod);
+ switch (key) {
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ d->cancelRecognition();
+ break;
+ case Qt::Key_Backspace:
+ if (d->cancelRecognition())
+ return true;
+ break;
+ default:
+ break;
+ }
+ return HunspellInputMethod::keyEvent(key, text, modifiers);
+#else
+ Q_UNUSED(key)
+ Q_UNUSED(text)
+ Q_UNUSED(modifiers)
+ return false;
+#endif
+}
+
+void LipiInputMethod::reset()
+{
+ LipiInputMethodBase::reset();
+ Q_D(LipiInputMethod);
+ d->cancelRecognition();
+}
+
+void LipiInputMethod::update()
+{
+ LipiInputMethodBase::update();
+}
+
+void LipiInputMethod::selectionListItemSelected(QVirtualKeyboardSelectionListModel::Type type, int index)
+{
+ LipiInputMethodBase::selectionListItemSelected(type, index);
+ Q_D(LipiInputMethod);
+ d->cancelRecognition();
+}
+
+QList<QVirtualKeyboardInputEngine::PatternRecognitionMode> LipiInputMethod::patternRecognitionModes() const
+{
+ return QList<QVirtualKeyboardInputEngine::PatternRecognitionMode>()
+ << QVirtualKeyboardInputEngine::PatternRecognitionMode::Handwriting;
+}
+
+QVirtualKeyboardTrace *LipiInputMethod::traceBegin(
+ int traceId, QVirtualKeyboardInputEngine::PatternRecognitionMode patternRecognitionMode,
+ const QVariantMap &traceCaptureDeviceInfo, const QVariantMap &traceScreenInfo)
+{
+ Q_D(LipiInputMethod);
+ return d->traceBegin(traceId, patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo);
+}
+
+bool LipiInputMethod::traceEnd(QVirtualKeyboardTrace *trace)
+{
+ Q_D(LipiInputMethod);
+ d->traceEnd(trace);
+ return true;
+}
+
+void LipiInputMethod::timerEvent(QTimerEvent *timerEvent)
+{
+ Q_D(LipiInputMethod);
+ if (timerEvent->timerId() == d->recognizeTimer) {
+ d->finishRecognition();
+ }
+}
+
+void LipiInputMethod::resultsAvailable(const QVariantList &resultList)
+{
+ if (lcLipi().isDebugEnabled()) {
+ qCDebug(lcLipi) << "LipiInputMethod::resultsAvailable():";
+ for (int i = 0; i < resultList.size(); i++) {
+ QVariantMap result = resultList.at(i).toMap();
+ const QChar unicode = result[QLatin1String("unicode")].toChar();
+ const double confidence = result[QLatin1String("confidence")].toDouble();
+ qCDebug(lcLipi) << QStringLiteral("%1: %2 (%3)").arg(i + 1)
+ .arg(unicode).arg(confidence).toUtf8().constData();
+ }
+ }
+ Q_D(LipiInputMethod);
+ d->resultsAvailable(resultList);
+}
+
+} // namespace QtVirtualKeyboard
+QT_END_NAMESPACE
diff --git a/src/plugins/lipi-toolkit/plugin/lipiinputmethod_p.h b/src/plugins/lipi-toolkit/plugin/lipiinputmethod_p.h
new file mode 100644
index 00000000..abbccf90
--- /dev/null
+++ b/src/plugins/lipi-toolkit/plugin/lipiinputmethod_p.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+#ifndef LIPIINPUTMETHOD_P_H
+#define LIPIINPUTMETHOD_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifdef QT_HUNSPELLINPUTMETHOD_LIB
+#include <QtHunspellInputMethod/private/hunspellinputmethod_p.h>
+#define LipiInputMethodBase HunspellInputMethod
+#else
+#include <QtVirtualKeyboard/qvirtualkeyboardabstractinputmethod.h>
+#define LipiInputMethodBase QVirtualKeyboardAbstractInputMethod
+#endif
+
+QT_BEGIN_NAMESPACE
+namespace QtVirtualKeyboard {
+
+class LipiInputMethodPrivate;
+
+class LipiInputMethod : public LipiInputMethodBase
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(LipiInputMethod)
+public:
+ explicit LipiInputMethod(QObject *parent = nullptr);
+ ~LipiInputMethod();
+
+ QList<QVirtualKeyboardInputEngine::InputMode> inputModes(const QString &locale);
+ bool setInputMode(const QString &locale, QVirtualKeyboardInputEngine::InputMode inputMode);
+ bool setTextCase(QVirtualKeyboardInputEngine::TextCase textCase);
+
+ bool keyEvent(Qt::Key key, const QString &text, Qt::KeyboardModifiers modifiers);
+
+ void reset();
+ void update();
+
+ void selectionListItemSelected(QVirtualKeyboardSelectionListModel::Type type, int index);
+
+ QList<QVirtualKeyboardInputEngine::PatternRecognitionMode> patternRecognitionModes() const;
+ QVirtualKeyboardTrace *traceBegin(
+ int traceId, QVirtualKeyboardInputEngine::PatternRecognitionMode patternRecognitionMode,
+ const QVariantMap &traceCaptureDeviceInfo, const QVariantMap &traceScreenInfo);
+ bool traceEnd(QVirtualKeyboardTrace *trace);
+
+protected:
+ void timerEvent(QTimerEvent *timerEvent);
+
+protected slots:
+ void resultsAvailable(const QVariantList &resultList);
+
+#ifndef QT_HUNSPELLINPUTMETHOD_LIB
+private:
+ QScopedPointer<LipiInputMethodPrivate> d_ptr;
+#endif
+};
+
+} // namespace QtVirtualKeyboard
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/lipi-toolkit/plugin/lipiplugin.cpp b/src/plugins/lipi-toolkit/plugin/lipiplugin.cpp
new file mode 100644
index 00000000..8878f45a
--- /dev/null
+++ b/src/plugins/lipi-toolkit/plugin/lipiplugin.cpp
@@ -0,0 +1,43 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 "lipiplugin.h"
+#include "lipiinputmethod_p.h"
+#include <QtQml>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QtVirtualKeyboard;
+
+void QtVirtualKeyboardLipiPlugin::registerTypes(const char *uri) const
+{
+ qmlRegisterType<LipiInputMethod>(uri, 2, 0, "HandwritingInputMethod");
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/lipi-toolkit/plugin/lipiplugin.h b/src/plugins/lipi-toolkit/plugin/lipiplugin.h
new file mode 100644
index 00000000..79e5b20d
--- /dev/null
+++ b/src/plugins/lipi-toolkit/plugin/lipiplugin.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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$
+**
+****************************************************************************/
+
+#ifndef LIPIPLUGIN_H
+#define LIPIPLUGIN_H
+
+#include <QVirtualKeyboardExtensionPlugin>
+
+QT_BEGIN_NAMESPACE
+
+class QtVirtualKeyboardLipiPlugin : public QVirtualKeyboardExtensionPlugin
+{
+ Q_OBJECT
+ Q_INTERFACES(QVirtualKeyboardExtensionPlugin)
+ Q_PLUGIN_METADATA(IID QVirtualKeyboardExtensionPluginFactoryInterface_iid
+ FILE "lipi.json")
+public:
+ void registerTypes(const char *uri) const;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/lipi-toolkit/plugin/lipisharedrecognizer.cpp b/src/plugins/lipi-toolkit/plugin/lipisharedrecognizer.cpp
new file mode 100644
index 00000000..090f129c
--- /dev/null
+++ b/src/plugins/lipi-toolkit/plugin/lipisharedrecognizer.cpp
@@ -0,0 +1,408 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 "lipisharedrecognizer_p.h"
+#include <QLoggingCategory>
+#include "lipiworker_p.h"
+
+#include "LTKMacros.h"
+#include "LTKInc.h"
+#include "LTKTypes.h"
+#include "LTKOSUtil.h"
+#include "LTKOSUtilFactory.h"
+#include "LTKErrorsList.h"
+#include "LTKErrors.h"
+#include "LTKLogger.h"
+#include "LTKConfigFileReader.h"
+#include "LTKException.h"
+#include "LTKLipiEngineInterface.h"
+
+#include <QDir>
+#include <QtCore/QLibraryInfo>
+
+QT_BEGIN_NAMESPACE
+namespace QtVirtualKeyboard {
+
+Q_DECLARE_LOGGING_CATEGORY(lcLipi)
+
+int LipiSharedRecognizer::s_lipiEngineRefCount = 0;
+QString LipiSharedRecognizer::s_lipiRoot;
+QString LipiSharedRecognizer::s_lipiLib;
+void *LipiSharedRecognizer::s_lipiEngineHandle = nullptr;
+LipiSharedRecognizer::FN_PTR_CREATELTKLIPIENGINE LipiSharedRecognizer::s_createLTKLipiEngine = nullptr;
+LipiSharedRecognizer::FN_PTR_DELETELTKLIPIENGINE LipiSharedRecognizer::s_deleteLTKLipiEngine = nullptr;
+LTKLipiEngineInterface *LipiSharedRecognizer::s_lipiEngine = nullptr;
+LTKShapeRecognizer *LipiSharedRecognizer::s_shapeRecognizer = nullptr;
+LipiWorker *LipiSharedRecognizer::s_lipiWorker = nullptr;
+QMap<int, QChar> LipiSharedRecognizer::s_unicodeMap;
+QString LipiSharedRecognizer::s_activeModel;
+stringStringMap LipiSharedRecognizer::s_lipiEngineConfigEntries;
+int LipiSharedRecognizer::s_recognitionCount = 0;
+
+/*!
+ \class QtVirtualKeyboard::LipiSharedRecognizer
+ \internal
+*/
+
+LipiSharedRecognizer::LipiSharedRecognizer()
+{
+ loadLipiInterface();
+}
+
+LipiSharedRecognizer::~LipiSharedRecognizer()
+{
+ unloadLipiInterface();
+}
+
+QString LipiSharedRecognizer::model() const
+{
+ return s_activeModel;
+}
+
+bool LipiSharedRecognizer::setModel(const QString &modelName)
+{
+ qCDebug(lcLipi) << "LipiSharedRecognizer::setModel():" << modelName;
+
+ if (!s_lipiEngine) {
+ qCWarning(lcLipi) << "Engine not initialized";
+ return false;
+ }
+
+ if (modelName.isEmpty())
+ return false;
+
+ if (modelName == s_activeModel)
+ return true;
+
+ unloadModelData();
+
+ return loadModelData(modelName) == SUCCESS;
+}
+
+void LipiSharedRecognizer::subsetOfClasses(const QString &charset, vector<int> &outSubsetOfClasses) const
+{
+ outSubsetOfClasses.clear();
+ outSubsetOfClasses.reserve(charset.length());
+ QString notFound;
+ for (int i = 0; i < charset.length(); i++) {
+ int classId = s_unicodeMap.key(charset.at(i), -1);
+ if (classId != -1)
+ outSubsetOfClasses.push_back(classId);
+ else if (lcLipi().isDebugEnabled())
+ notFound.append(charset.at(i));
+ }
+ if (!notFound.isEmpty())
+ qCDebug(lcLipi) << "LipiSharedRecognizer::subsetOfClasses(): unrecognized characters" << notFound;
+}
+
+QSharedPointer<LipiRecognitionTask> LipiSharedRecognizer::newRecognition(const LTKCaptureDevice& deviceInfo,
+ const LTKScreenContext& screenContext,
+ const vector<int>& inSubsetOfClasses,
+ float confThreshold,
+ int numChoices)
+{
+ if (!s_lipiEngine || !s_shapeRecognizer || !s_lipiWorker)
+ return QSharedPointer<LipiRecognitionTask>();
+
+ QSharedPointer<LipiRecognitionTask> task(new LipiRecognitionTask(deviceInfo,
+ screenContext,
+ inSubsetOfClasses,
+ confThreshold,
+ numChoices,
+ s_recognitionCount));
+
+ ++s_recognitionCount;
+
+ return task;
+}
+
+QSharedPointer<LipiRecognitionResultsTask> LipiSharedRecognizer::startRecognition(QSharedPointer<LipiRecognitionTask> &recognitionTask)
+{
+ if (!s_lipiEngine || !s_shapeRecognizer || !s_lipiWorker)
+ return QSharedPointer<LipiRecognitionResultsTask>();
+
+ QSharedPointer<LipiRecognitionResultsTask> resultsTask(new LipiRecognitionResultsTask(recognitionTask->resultVector,
+ s_unicodeMap,
+ recognitionTask->resultId()));
+
+ s_lipiWorker->addTask(recognitionTask);
+ s_lipiWorker->addTask(resultsTask);
+
+ return resultsTask;
+}
+
+bool LipiSharedRecognizer::cancelRecognition()
+{
+ if (!s_lipiEngine || !s_shapeRecognizer || !s_lipiWorker)
+ return false;
+
+ return s_lipiWorker->removeAllTasks() > 0;
+}
+
+bool LipiSharedRecognizer::cancelRecognitionTask(QSharedPointer<LipiRecognitionTask> &recognitionTask)
+{
+ if (!s_lipiEngine || !s_shapeRecognizer || !s_lipiWorker || !recognitionTask)
+ return false;
+
+ return recognitionTask->cancelRecognition() || s_lipiWorker->removeTask(recognitionTask) > 0;
+}
+
+int LipiSharedRecognizer::loadLipiInterface()
+{
+ qCDebug(lcLipi) << "LipiSharedRecognizer::loadLipiInterface():" << s_lipiEngineRefCount;
+
+ if (++s_lipiEngineRefCount == 1) {
+ if (s_lipiRoot.isEmpty()) {
+ /* LIPI_ROOT defines the root directory for lipi-toolkit project.
+ LIPI_LIB is an extension implemented for QtVirtualKeyboard and
+ allows using different location for lipi-toolkit plugins.
+
+ LIPI_LIB defaults to LIPI_ROOT + "/lib".
+ */
+ bool lipiRootVarIsEmpty = qEnvironmentVariableIsEmpty("LIPI_ROOT");
+ s_lipiRoot = lipiRootVarIsEmpty ?
+ QDir(QLibraryInfo::location(QLibraryInfo::DataPath) + QLatin1String("/qtvirtualkeyboard/lipi_toolkit")).absolutePath() :
+ qEnvironmentVariable("LIPI_ROOT");
+
+ bool lipiLibVarIsEmpty = qEnvironmentVariableIsEmpty("LIPI_LIB");
+ if (!lipiLibVarIsEmpty)
+ s_lipiLib = qEnvironmentVariable("LIPI_LIB");
+ else if (!lipiRootVarIsEmpty)
+ s_lipiLib = s_lipiRoot + QLatin1String("/lib");
+ else
+ s_lipiLib = QDir(QLibraryInfo::location(QLibraryInfo::PluginsPath) + QLatin1String("/lipi_toolkit")).absolutePath();
+ }
+
+ QScopedPointer<LTKOSUtil> osUtil(LTKOSUtilFactory::getInstance());
+ const string lipiRootPath(QDir::toNativeSeparators(s_lipiRoot).toStdString());
+ const string lipiLibPath(QDir::toNativeSeparators(s_lipiLib).toStdString());
+
+ int result = osUtil->loadSharedLib(lipiLibPath, LIPIENGINE_MODULE_STR, &s_lipiEngineHandle);
+ if (result != SUCCESS) {
+ qCWarning(lcLipi) << QStringLiteral("Error %1: Could not open shared library for module '%2'").arg(result).arg(QLatin1String(LIPIENGINE_MODULE_STR));
+ return result;
+ }
+
+ result = loadLipiEngineConfig();
+ if (result != SUCCESS)
+ return result;
+
+ result = osUtil->getFunctionAddress(s_lipiEngineHandle, "createLTKLipiEngine", (void **)&s_createLTKLipiEngine);
+ if (result != SUCCESS) {
+ qCWarning(lcLipi) << QStringLiteral("Error %1: %2").arg(result).arg(QLatin1String(getErrorMessage(result).c_str()));
+ return result;
+ }
+
+ result = osUtil->getFunctionAddress(s_lipiEngineHandle, "deleteLTKLipiEngine", (void **)&s_deleteLTKLipiEngine);
+ if (result != SUCCESS) {
+ qCWarning(lcLipi) << QStringLiteral("Error %1: %2").arg(result).arg(QLatin1String(getErrorMessage(result).c_str()));
+ return result;
+ }
+
+ s_lipiEngine = s_createLTKLipiEngine();
+ s_lipiEngine->setLipiRootPath(lipiRootPath);
+ s_lipiEngine->setLipiLibPath(lipiLibPath);
+#if 0
+ s_lipiEngine->setLipiLogFileName(QDir::toNativeSeparators(QString("%1/lipi.log").arg(s_lipiRoot)).toStdString());
+ s_lipiEngine->setLipiLogLevel("DEBUG");
+#endif
+
+ result = s_lipiEngine->initializeLipiEngine();
+ if (result != SUCCESS) {
+ qCWarning(lcLipi) << QStringLiteral("Error %1: %2").arg(result).arg(QLatin1String(getErrorMessage(result).c_str()));
+ return result;
+ }
+ }
+
+ return SUCCESS;
+}
+
+void LipiSharedRecognizer::unloadLipiInterface()
+{
+ qCDebug(lcLipi) << "LipiSharedRecognizer::unloadLipiInterface():" << s_lipiEngineRefCount;
+
+ Q_ASSERT(s_lipiEngineRefCount > 0);
+ if (--s_lipiEngineRefCount == 0) {
+ unloadModelData();
+ if (s_lipiEngine) {
+ s_deleteLTKLipiEngine();
+ s_lipiEngine = nullptr;
+ }
+ s_createLTKLipiEngine = nullptr;
+ s_deleteLTKLipiEngine = nullptr;
+ QScopedPointer<LTKOSUtil> osUtil(LTKOSUtilFactory::getInstance());
+ osUtil->unloadSharedLib(s_lipiEngineHandle);
+ s_lipiEngineHandle = nullptr;
+ }
+}
+
+int LipiSharedRecognizer::loadLipiEngineConfig()
+{
+ s_lipiEngineConfigEntries.clear();
+
+ const QString &lipiEngineConfigFile(QDir::toNativeSeparators(QStringLiteral("%1/projects/lipiengine.cfg").arg(s_lipiRoot)));
+ if (!QFileInfo::exists(lipiEngineConfigFile)) {
+ qCWarning(lcLipi) << "File not found" << lipiEngineConfigFile;
+ return FAILURE;
+ }
+
+ try {
+ LTKConfigFileReader configReader(lipiEngineConfigFile.toStdString());
+ s_lipiEngineConfigEntries = configReader.getCfgFileMap();
+ } catch (LTKException e) {
+ return e.getErrorCode();
+ }
+
+ return SUCCESS;
+}
+
+int LipiSharedRecognizer::resolveLogicalNameToProjectProfile(const QString &logicalName, QString &outProjectName, QString &outProfileName)
+{
+ outProjectName.clear();
+ outProfileName.clear();
+
+ stringStringMap::const_iterator configEntry = s_lipiEngineConfigEntries.find(logicalName.toStdString());
+ if (configEntry == s_lipiEngineConfigEntries.end())
+ return FAILURE;
+
+ QStringList parts = QString::fromLatin1(configEntry->second.c_str()).split(QLatin1Char('('), QString::SkipEmptyParts);
+ if (parts.length() != 2)
+ return FAILURE;
+
+ parts[1].replace(QLatin1Char(')'), QString());
+
+ outProjectName = parts[0].trimmed();
+ outProfileName = parts[1].trimmed();
+
+ return SUCCESS;
+}
+
+int LipiSharedRecognizer::loadModelData(const QString &logicalName)
+{
+ qCDebug(lcLipi) << "LipiSharedRecognizer::loadModelData():" << logicalName;
+
+ Q_ASSERT(s_shapeRecognizer == nullptr);
+ Q_ASSERT(s_lipiWorker == nullptr);
+
+ QTime perf;
+ perf.start();
+
+ s_activeModel = logicalName;
+
+ QString project;
+ QString profile;
+ int result = resolveLogicalNameToProjectProfile(logicalName, project, profile);
+ if (result == SUCCESS) {
+ string strProject = project.toStdString();
+ string strProfile = profile.toStdString();
+ int result = s_lipiEngine->createShapeRecognizer(strProject, strProfile, &s_shapeRecognizer);
+ if (result == SUCCESS) {
+ result = loadMapping(QDir::toNativeSeparators(QStringLiteral("%1/projects/%2/config/unicodeMapfile_%2.ini").arg(s_lipiRoot).arg(project)));
+ if (result == SUCCESS) {
+ s_lipiWorker = new LipiWorker(s_shapeRecognizer);
+ QSharedPointer<LipiLoadModelDataTask> loadModelDataTask(new LipiLoadModelDataTask());
+ s_lipiWorker->addTask(loadModelDataTask);
+ s_lipiWorker->start();
+ }
+ }
+ }
+
+ if (result == SUCCESS)
+ qCDebug(lcLipi) << "LipiSharedRecognizer::loadModelData(): time:" << perf.elapsed() << "ms";
+
+ if (result != SUCCESS) {
+ qCWarning(lcLipi) << QStringLiteral("Error %1: %2").arg(result).arg(QLatin1String(getErrorMessage(result).c_str()));
+ unloadModelData();
+ }
+
+ return result;
+}
+
+void LipiSharedRecognizer::unloadModelData()
+{
+ if (!s_shapeRecognizer)
+ return;
+
+ qCDebug(lcLipi) << "LipiSharedRecognizer::unloadModelData():" << s_activeModel;
+
+ QTime perf;
+ perf.start();
+
+ if (s_lipiWorker) {
+ delete s_lipiWorker;
+ s_lipiWorker = nullptr;
+ }
+
+ s_lipiEngine->deleteShapeRecognizer(s_shapeRecognizer);
+ s_shapeRecognizer = nullptr;
+ s_unicodeMap.clear();
+ s_activeModel.clear();
+
+ qCDebug(lcLipi) << "LipiSharedRecognizer::unloadModelData(): time:" << perf.elapsed() << "ms";
+}
+
+int LipiSharedRecognizer::loadMapping(const QString &mapFile)
+{
+ if (!QFileInfo(mapFile).exists()) {
+ qCWarning(lcLipi) << "File not found" << mapFile;
+ return FAILURE;
+ }
+
+ try {
+ LTKConfigFileReader configfilereader(mapFile.toStdString());
+ const stringStringMap &cfgFileMap = configfilereader.getCfgFileMap();
+
+ for (stringStringMap::const_iterator i = cfgFileMap.begin(); i != cfgFileMap.end(); i++) {
+ if (i->first.empty())
+ continue;
+ if (!QChar::fromLatin1(i->first.at(0)).isDigit())
+ continue;
+
+ bool ok;
+ int id = QString::fromLatin1(i->first.c_str()).toInt(&ok, 10);
+ if (!ok)
+ continue;
+
+ QChar ch = QChar(QString::fromLatin1(i->second.c_str()).toInt(&ok, 16));
+ if (!ok)
+ continue;
+
+ s_unicodeMap[id] = ch;
+ }
+ } catch (LTKException) {
+ return FAILURE;
+ }
+
+ qCDebug(lcLipi) << s_unicodeMap;
+
+ return SUCCESS;
+}
+
+} // namespace QtVirtualKeyboard
+QT_END_NAMESPACE
diff --git a/src/plugins/lipi-toolkit/plugin/lipisharedrecognizer_p.h b/src/plugins/lipi-toolkit/plugin/lipisharedrecognizer_p.h
new file mode 100644
index 00000000..9d7cfbea
--- /dev/null
+++ b/src/plugins/lipi-toolkit/plugin/lipisharedrecognizer_p.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+#ifndef LIPISHAREDRECOGNIZER_P_H
+#define LIPISHAREDRECOGNIZER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QString>
+#include <QMap>
+
+#include "lipiworker_p.h"
+
+class LTKLipiEngineInterface;
+
+QT_BEGIN_NAMESPACE
+namespace QtVirtualKeyboard {
+
+class LipiSharedRecognizer
+{
+ Q_DISABLE_COPY(LipiSharedRecognizer)
+public:
+ LipiSharedRecognizer();
+ ~LipiSharedRecognizer();
+
+ QString model() const;
+ bool setModel(const QString &modelName);
+
+ void subsetOfClasses(const QString &charset, vector<int> &outSubsetOfClasses) const;
+
+ QSharedPointer<LipiRecognitionTask> newRecognition(const LTKCaptureDevice& deviceInfo,
+ const LTKScreenContext& screenContext,
+ const vector<int>& inSubsetOfClasses,
+ float confThreshold,
+ int numChoices);
+ QSharedPointer<LipiRecognitionResultsTask> startRecognition(QSharedPointer<LipiRecognitionTask> &recognitionTask);
+ bool cancelRecognition();
+ bool cancelRecognitionTask(QSharedPointer<LipiRecognitionTask> &recognitionTask);
+
+private:
+ static int loadLipiInterface();
+ static void unloadLipiInterface();
+ static int loadLipiEngineConfig();
+ static int resolveLogicalNameToProjectProfile(const QString &logicalName, QString &outProjectName, QString &outProfileName);
+ static int loadModelData(const QString &logicalName);
+ static void unloadModelData();
+ static int loadMapping(const QString &mapFile);
+
+ typedef LTKLipiEngineInterface* (*FN_PTR_CREATELTKLIPIENGINE)(void);
+ typedef void (*FN_PTR_DELETELTKLIPIENGINE)(void);
+
+ static int s_lipiEngineRefCount;
+ static QString s_lipiRoot;
+ static QString s_lipiLib;
+ static void *s_lipiEngineHandle;
+ static FN_PTR_CREATELTKLIPIENGINE s_createLTKLipiEngine;
+ static FN_PTR_DELETELTKLIPIENGINE s_deleteLTKLipiEngine;
+ static LTKLipiEngineInterface *s_lipiEngine;
+ static LTKShapeRecognizer *s_shapeRecognizer;
+ static LipiWorker *s_lipiWorker;
+ static QMap<int, QChar> s_unicodeMap;
+ static QString s_activeModel;
+ static stringStringMap s_lipiEngineConfigEntries;
+ static int s_recognitionCount;
+};
+
+} // namespace QtVirtualKeyboard
+QT_END_NAMESPACE
+
+#endif // LIPISHAREDRECOGNIZER_P_H
diff --git a/src/plugins/lipi-toolkit/plugin/lipiworker.cpp b/src/plugins/lipi-toolkit/plugin/lipiworker.cpp
new file mode 100644
index 00000000..ffe5c3b7
--- /dev/null
+++ b/src/plugins/lipi-toolkit/plugin/lipiworker.cpp
@@ -0,0 +1,250 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 "lipiworker_p.h"
+#include <QLoggingCategory>
+
+#include <QTime>
+
+#include "LTKShapeRecognizer.h"
+#include "LTKErrors.h"
+
+QT_BEGIN_NAMESPACE
+namespace QtVirtualKeyboard {
+
+Q_DECLARE_LOGGING_CATEGORY(lcLipi)
+
+/*!
+ \class QtVirtualKeyboard::LipiTask
+ \internal
+*/
+
+/*!
+ \class QtVirtualKeyboard::LipiLoadModelDataTask
+ \internal
+*/
+
+void LipiLoadModelDataTask::run()
+{
+ qCDebug(lcLipi) << "LipiLoadModelDataTask::run()";
+ QTime perf;
+ perf.start();
+ int result = shapeRecognizer->loadModelData();
+ qCDebug(lcLipi) << "LipiLoadModelDataTask::run(): time:" << perf.elapsed() << "ms";
+ if (result != SUCCESS)
+ qCWarning(lcLipi) << QStringLiteral("Error %1: %2").arg(result).arg(QLatin1String(getErrorMessage(result).c_str()));
+}
+
+/*!
+ \class QtVirtualKeyboard::LipiRecognitionTask
+ \internal
+*/
+
+LipiRecognitionTask::LipiRecognitionTask(const LTKCaptureDevice& deviceInfo,
+ const LTKScreenContext& screenContext,
+ const vector<int>& inSubsetOfClasses,
+ float confThreshold,
+ int numChoices,
+ int resultId) :
+ LipiTask(),
+ deviceInfo(deviceInfo),
+ screenContext(screenContext),
+ inSubsetOfClasses(inSubsetOfClasses),
+ confThreshold(confThreshold),
+ numChoices(numChoices),
+ resultVector(new vector<LTKShapeRecoResult>()),
+ _resultId(resultId),
+ stateRunning(false),
+ stateCancelled(false)
+{
+}
+
+void LipiRecognitionTask::run()
+{
+ qCDebug(lcLipi) << "LipiRecognitionTask::run()";
+
+ if (!shapeRecognizer || !resultVector)
+ return;
+
+ {
+ QMutexLocker stateGuard(&stateLock);
+ stateRunning = true;
+ }
+
+ resultVector->clear();
+ resultVector->reserve(numChoices);
+
+ shapeRecognizer->setDeviceContext(deviceInfo);
+
+ QTime perf;
+ perf.start();
+ shapeRecognizer->recognize(traceGroup, screenContext,
+ inSubsetOfClasses, confThreshold,
+ numChoices, *resultVector);
+
+ int perfElapsed = perf.elapsed();
+
+ {
+ QMutexLocker stateGuard(&stateLock);
+ stateRunning = false;
+ if (stateCancelled)
+ resultVector->clear();
+ qCDebug(lcLipi) << "LipiRecognitionTask::run(): time:" << perfElapsed << "ms" << (stateCancelled ? "(cancelled)" : "");
+ }
+}
+
+bool LipiRecognitionTask::cancelRecognition()
+{
+ QMutexLocker stateGuard(&stateLock);
+ stateCancelled = true;
+ bool result = (stateRunning && shapeRecognizer);
+ if (result)
+ shapeRecognizer->requestCancelRecognition();
+ return result;
+}
+
+int LipiRecognitionTask::resultId() const
+{
+ return _resultId;
+}
+
+/*!
+ \class QtVirtualKeyboard::LipiRecognitionResultsTask
+ \internal
+*/
+
+LipiRecognitionResultsTask::LipiRecognitionResultsTask(QSharedPointer<vector<LTKShapeRecoResult> > resultVector,
+ const QMap<int, QChar> &unicodeMap,
+ int resultId) :
+ LipiTask(),
+ resultVector(resultVector),
+ unicodeMap(unicodeMap),
+ _resultId(resultId)
+{
+}
+
+void LipiRecognitionResultsTask::run()
+{
+ if (!resultVector || unicodeMap.isEmpty())
+ return;
+
+ QVariantList resultList;
+ for (vector<LTKShapeRecoResult>::const_iterator i = resultVector->begin();
+ i != resultVector->end(); i++) {
+ QVariantMap result;
+ int shapeId = i->getShapeId();
+ result[QLatin1String("resultId")] = _resultId;
+ result[QLatin1String("shapeId")] = shapeId;
+ result[QLatin1String("unicode")] = unicodeMap.value(shapeId);
+ result[QLatin1String("confidence")] = i->getConfidence();
+ resultList.append(result);
+ }
+
+ if (resultList.isEmpty())
+ return;
+
+ emit resultsAvailable(resultList);
+}
+
+/*!
+ \class QtVirtualKeyboard::LipiWorker
+ \internal
+*/
+
+LipiWorker::LipiWorker(LTKShapeRecognizer *shapeRecognizer, QObject *parent) :
+ QThread(parent),
+ taskSema(),
+ taskLock(),
+ shapeRecognizer(shapeRecognizer)
+{
+ abort = false;
+}
+
+LipiWorker::~LipiWorker()
+{
+ abort = true;
+ taskSema.release();
+ wait();
+ if (shapeRecognizer)
+ shapeRecognizer->unloadModelData();
+}
+
+void LipiWorker::addTask(QSharedPointer<LipiTask> task)
+{
+ if (task) {
+ QMutexLocker guard(&taskLock);
+ taskList.append(task);
+ taskSema.release();
+ }
+}
+
+int LipiWorker::removeTask(QSharedPointer<LipiTask> task)
+{
+ int count = 0;
+ if (task) {
+ QMutexLocker guard(&taskLock);
+ count = taskList.removeAll(task);
+ taskSema.acquire(qMin(count, taskSema.available()));
+ }
+ return count;
+}
+
+int LipiWorker::removeAllTasks()
+{
+ QMutexLocker guard(&taskLock);
+ int count = taskList.count();
+ taskList.clear();
+ if (taskSema.available())
+ taskSema.acquire(taskSema.available());
+ return count;
+}
+
+void LipiWorker::run()
+{
+ while (!abort) {
+ taskSema.acquire();
+ if (abort)
+ break;
+ QSharedPointer<LipiTask> currentTask;
+ {
+ QMutexLocker guard(&taskLock);
+ if (!taskList.isEmpty()) {
+ currentTask = taskList.front();
+ taskList.pop_front();
+ }
+ }
+ if (currentTask) {
+ currentTask->shapeRecognizer = shapeRecognizer;
+ currentTask->run();
+ }
+ }
+}
+
+} // namespace QtVirtualKeyboard
+QT_END_NAMESPACE
diff --git a/src/plugins/lipi-toolkit/plugin/lipiworker_p.h b/src/plugins/lipi-toolkit/plugin/lipiworker_p.h
new file mode 100644
index 00000000..098e198b
--- /dev/null
+++ b/src/plugins/lipi-toolkit/plugin/lipiworker_p.h
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+#ifndef LIPIWORKER_P_H
+#define LIPIWORKER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QThread>
+#include <QSemaphore>
+#include <QMutex>
+#include <QStringList>
+#include <QSharedPointer>
+#include <QMap>
+
+#include "LTKTypes.h"
+#include "LTKCaptureDevice.h"
+#include "LTKScreenContext.h"
+#include "LTKTraceGroup.h"
+#include "LTKChannel.h"
+#include "LTKTraceFormat.h"
+#include "LTKTrace.h"
+#include "LTKShapeRecognizer.h"
+#include "LTKShapeRecoResult.h"
+
+QT_BEGIN_NAMESPACE
+namespace QtVirtualKeyboard {
+
+class LipiTask : public QObject
+{
+ Q_OBJECT
+public:
+ explicit LipiTask(QObject *parent = nullptr) :
+ QObject(parent),
+ shapeRecognizer(nullptr)
+ {}
+
+ virtual void run() = 0;
+
+ LTKShapeRecognizer *shapeRecognizer;
+};
+
+class LipiLoadModelDataTask : public LipiTask
+{
+ Q_OBJECT
+public:
+ void run();
+};
+
+class LipiRecognitionTask : public LipiTask
+{
+ Q_OBJECT
+public:
+ explicit LipiRecognitionTask(const LTKCaptureDevice& deviceInfo,
+ const LTKScreenContext& screenContext,
+ const vector<int>& inSubsetOfClasses,
+ float confThreshold,
+ int numChoices,
+ int resultId);
+
+ void run();
+ bool cancelRecognition();
+ int resultId() const;
+
+ LTKTraceGroup traceGroup;
+
+private:
+ friend class LipiSharedRecognizer;
+ const QMap<int, QChar> unicodeMap;
+ const LTKCaptureDevice deviceInfo;
+ const LTKScreenContext screenContext;
+ const vector<int> inSubsetOfClasses;
+ const float confThreshold;
+ const int numChoices;
+ QSharedPointer<vector<LTKShapeRecoResult> > resultVector;
+ const int _resultId;
+ QMutex stateLock;
+ bool stateRunning;
+ bool stateCancelled;
+};
+
+class LipiRecognitionResultsTask : public LipiTask
+{
+ Q_OBJECT
+public:
+ explicit LipiRecognitionResultsTask(QSharedPointer<vector<LTKShapeRecoResult> > resultVector,
+ const QMap<int, QChar> &unicodeMap,
+ int resultId);
+
+ void run();
+
+signals:
+ void resultsAvailable(const QVariantList &resultList);
+
+private:
+ QSharedPointer<vector<LTKShapeRecoResult> > resultVector;
+ const QMap<int, QChar> &unicodeMap;
+ const int _resultId;
+};
+
+class LipiWorker : public QThread
+{
+ Q_OBJECT
+public:
+ explicit LipiWorker(LTKShapeRecognizer *shapeRecognizer, QObject *parent = nullptr);
+ ~LipiWorker();
+
+ void addTask(QSharedPointer<LipiTask> task);
+ int removeTask(QSharedPointer<LipiTask> task);
+ int removeAllTasks();
+
+protected:
+ void run();
+
+private:
+ QList<QSharedPointer<LipiTask> > taskList;
+ QSemaphore taskSema;
+ QMutex taskLock;
+ LTKShapeRecognizer *shapeRecognizer;
+ QBasicAtomicInt abort;
+};
+
+} // namespace QtVirtualKeyboard
+QT_END_NAMESPACE
+
+#endif // LIPIWORKER_P_H
diff --git a/src/plugins/lipi-toolkit/plugin/plugin.pro b/src/plugins/lipi-toolkit/plugin/plugin.pro
new file mode 100644
index 00000000..c3a12440
--- /dev/null
+++ b/src/plugins/lipi-toolkit/plugin/plugin.pro
@@ -0,0 +1,68 @@
+TARGET = qtvirtualkeyboard_lipi
+CONFIG += exceptions
+QT += qml virtualkeyboard-private
+
+HEADERS += \
+ lipiinputmethod_p.h \
+ lipiplugin.h \
+ lipisharedrecognizer_p.h \
+ lipiworker_p.h
+SOURCES += \
+ lipiinputmethod.cpp \
+ lipiplugin.cpp \
+ lipisharedrecognizer.cpp \
+ lipiworker.cpp
+OTHER_FILES += \
+ lipi.json
+
+DEFINES += \
+ QT_NO_CAST_TO_ASCII \
+ QT_ASCII_CAST_WARNINGS \
+ QT_NO_CAST_FROM_ASCII \
+ QT_NO_CAST_FROM_BYTEARRAY
+
+include(../../../config.pri)
+
+INCLUDEPATH += \
+ ../3rdparty/lipi-toolkit/src/include \
+ ../3rdparty/lipi-toolkit/src/util/lib
+LIBS += -L$$OUT_PWD/../../lib \
+ -lshaperecommon$$qtPlatformTargetSuffix() \
+ -lltkcommon$$qtPlatformTargetSuffix() \
+ -lltkutil$$qtPlatformTargetSuffix()
+win32: LIBS += Advapi32.lib
+else: QMAKE_USE += libdl
+ltk_projects.files = $$PWD/../3rdparty/lipi-toolkit/projects
+ltk_projects.path = $$VIRTUALKEYBOARD_INSTALL_DATA/lipi_toolkit
+INSTALLS += ltk_projects
+!prefix_build: COPIES += ltk_projects
+
+!disable-hunspell {
+ QT += hunspellinputmethod-private
+}
+
+LAYOUT_FILES += $$LAYOUTS_BASE/content/layouts/fallback/handwriting.qml
+contains(CONFIG, lang-en(_GB)?): LAYOUT_FILES += $$LAYOUTS_BASE/content/layouts/en_GB/handwriting.fallback
+contains(CONFIG, lang-en(_US)?): LAYOUT_FILES += $$LAYOUTS_BASE/content/layouts/en_US/handwriting.fallback
+contains(CONFIG, lang-id.*): LAYOUT_FILES += $$LAYOUTS_BASE/content/layouts/id_ID/handwriting.fallback
+contains(CONFIG, lang-ms.*): LAYOUT_FILES += $$LAYOUTS_BASE/content/layouts/ms_MY/handwriting.fallback
+contains(CONFIG, lang-nl.*): LAYOUT_FILES += $$LAYOUTS_BASE/content/layouts/nl_NL/handwriting.fallback
+
+OTHER_FILES += \
+ $$LAYOUT_FILES
+
+!isEmpty(LAYOUT_FILES) {
+ virtualkeyboard_ltk_layouts.files = $$LAYOUT_FILES
+ virtualkeyboard_ltk_layouts.base = $$LAYOUTS_BASE
+ virtualkeyboard_ltk_layouts.prefix = $$LAYOUTS_PREFIX
+ RESOURCES += virtualkeyboard_ltk_layouts
+}
+
+win32 {
+ QMAKE_TARGET_PRODUCT = "Qt Virtual Keyboard Lipi-Toolkit (Qt $$QT_VERSION)"
+ QMAKE_TARGET_DESCRIPTION = "Virtual Keyboard Extension for Qt."
+}
+
+PLUGIN_TYPE = virtualkeyboard
+PLUGIN_CLASS_NAME = QtVirtualKeyboardLipiPlugin
+load(qt_plugin)