aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJarkko Koivikko <jarkko.koivikko@code-q.fi>2015-03-25 10:08:02 +0200
committerJarkko Koivikko <jarkko.koivikko@code-q.fi>2015-06-17 12:35:27 +0300
commit1d5eae310178006383a298156bdb134beffca36b (patch)
treec49abdb16c05cf01c22335b5a3cf69ba4037f0fc
parenta6333e2fedb2798f62c7cee16a3634707b6b52d5 (diff)
Move Hunspell dictionary loading to worker thread
This change moves the Hunspell dictionary loading to worker thread. The dictionary loading is relatively heavy task for large dictionary files, such as Arabic, which can take more than 2 seconds to load on the Nexus 7 hardware. The Hunspell worker thread is no longer destroyed and created every time the dictionary changes, instead the worker thread is re-used. The dictionary loading happens by adding a new kind of task for the worker thread. Also, added a special function for clearing all but dictionary loading tasks from the worker task queue. This is needed for a special case where the dictionary is still loading while the user starts typing. Updated the test cases to take into account changes in delays in loading the dictionary. Change-Id: If9be14c7703b39af79f6e3b6708e297a28c49f2f Reviewed-by: Gatis Paeglis <gatis.paeglis@theqtcompany.com>
-rw-r--r--src/virtualkeyboard/hunspellinputmethod_p.cpp57
-rw-r--r--src/virtualkeyboard/hunspellworker.cpp93
-rw-r--r--src/virtualkeyboard/hunspellworker.h31
-rw-r--r--tests/auto/inputpanel/data/inputpanel/inputpanel.qml1
-rw-r--r--tests/auto/inputpanel/data/tst_inputpanel.qml3
5 files changed, 136 insertions, 49 deletions
diff --git a/src/virtualkeyboard/hunspellinputmethod_p.cpp b/src/virtualkeyboard/hunspellinputmethod_p.cpp
index 90fe6efb..7fd98191 100644
--- a/src/virtualkeyboard/hunspellinputmethod_p.cpp
+++ b/src/virtualkeyboard/hunspellinputmethod_p.cpp
@@ -20,7 +20,6 @@
#include "declarativeinputcontext.h"
#include <hunspell/hunspell.h>
#include <QStringList>
-#include <QFileInfo>
#include <QDir>
#include "virtualkeyboarddebug.h"
#include <QTextCodec>
@@ -29,7 +28,7 @@
HunspellInputMethodPrivate::HunspellInputMethodPrivate(HunspellInputMethod *q_ptr) :
AbstractInputMethodPrivate(),
q_ptr(q_ptr),
- hunspellWorker(0),
+ hunspellWorker(new HunspellWorker()),
locale(),
word(),
wordCandidates(),
@@ -38,6 +37,8 @@ HunspellInputMethodPrivate::HunspellInputMethodPrivate(HunspellInputMethod *q_pt
ignoreUpdate(false),
autoSpaceAllowed(false)
{
+ if (hunspellWorker)
+ hunspellWorker->start();
}
HunspellInputMethodPrivate::~HunspellInputMethodPrivate()
@@ -46,9 +47,10 @@ HunspellInputMethodPrivate::~HunspellInputMethodPrivate()
bool HunspellInputMethodPrivate::createHunspell(const QString &locale)
{
+ if (!hunspellWorker)
+ return false;
if (this->locale != locale) {
- hunspellWorker.reset(0);
- Hunhandle *hunspell = 0;
+ hunspellWorker->removeAllTasks();
QString hunspellDataPath(QString::fromLatin1(qgetenv("QT_VIRTUALKEYBOARD_HUNSPELL_DATA_PATH").constData()));
const QString pathListSep(
#if defined(Q_OS_WIN32)
@@ -58,43 +60,20 @@ bool HunspellInputMethodPrivate::createHunspell(const QString &locale)
#endif
);
QStringList searchPaths(hunspellDataPath.split(pathListSep, QString::SkipEmptyParts));
- searchPaths.append(QDir(QLibraryInfo::location(QLibraryInfo::DataPath) + "/qtvirtualkeyboard/hunspell").absolutePath());
+ const QStringList defaultPaths = QStringList()
+ << QDir(QLibraryInfo::location(QLibraryInfo::DataPath) + QStringLiteral("/qtvirtualkeyboard/hunspell")).absolutePath()
#if !defined(Q_OS_WIN32)
- searchPaths.append(QStringLiteral("/usr/share/hunspell"));
- searchPaths.append(QStringLiteral("/usr/share/myspell/dicts"));
+ << QStringLiteral("/usr/share/hunspell")
+ << QStringLiteral("/usr/share/myspell/dicts")
#endif
- foreach (const QString &searchPath, searchPaths) {
- QByteArray affpath(QString("%1/%2.aff").arg(searchPath).arg(locale).toUtf8());
- QByteArray dpath(QString("%1/%2.dic").arg(searchPath).arg(locale).toUtf8());
- if (QFileInfo(dpath).exists()) {
- hunspell = Hunspell_create(affpath.constData(), dpath.constData());
- if (hunspell) {
- /* Make sure the encoding used by the dictionary is supported
- by the QTextCodec.
- */
- if (QTextCodec::codecForName(Hunspell_get_dic_encoding(hunspell))) {
- break;
- } else {
- qWarning() << "The Hunspell dictionary" << QString("%1/%2.dic").arg(searchPath).arg(locale) << "cannot be used because it uses an unknown text codec" << QString(Hunspell_get_dic_encoding(hunspell));
- Hunspell_destroy(hunspell);
- hunspell = 0;
- }
- }
- }
- }
- if (!hunspell) {
- VIRTUALKEYBOARD_DEBUG() << "Missing Hunspell dictionary for locale" << locale << "in" << searchPaths;
- this->locale.clear();
- return false;
+ ;
+ foreach (const QString &defaultPath, defaultPaths) {
+ if (!searchPaths.contains(defaultPath))
+ searchPaths.append(defaultPath);
}
+ QSharedPointer<HunspellLoadDictionaryTask> loadDictionaryTask(new HunspellLoadDictionaryTask(locale, searchPaths));
+ hunspellWorker->addTask(loadDictionaryTask);
this->locale = locale;
- hunspellWorker.reset(new HunspellWorker(hunspell));
- if (!hunspellWorker) {
- Hunspell_destroy(hunspell);
- this->locale.clear();
- return false;
- }
- hunspellWorker->start();
}
return true;
}
@@ -115,7 +94,7 @@ bool HunspellInputMethodPrivate::updateSuggestions()
bool wordCandidateListChanged = false;
if (!word.isEmpty()) {
if (hunspellWorker)
- hunspellWorker->removeAllTasks();
+ hunspellWorker->removeAllTasksExcept<HunspellLoadDictionaryTask>();
if (wordCandidates.isEmpty()) {
wordCandidates.append(word);
activeWordIndex = 0;
@@ -154,7 +133,7 @@ bool HunspellInputMethodPrivate::updateSuggestions()
bool HunspellInputMethodPrivate::clearSuggestions()
{
if (hunspellWorker)
- hunspellWorker->removeAllTasks();
+ hunspellWorker->removeAllTasksExcept<HunspellLoadDictionaryTask>();
if (wordCandidates.isEmpty())
return false;
wordCandidates.clear();
diff --git a/src/virtualkeyboard/hunspellworker.cpp b/src/virtualkeyboard/hunspellworker.cpp
index 8084b1ac..420a69ec 100644
--- a/src/virtualkeyboard/hunspellworker.cpp
+++ b/src/virtualkeyboard/hunspellworker.cpp
@@ -17,11 +17,78 @@
****************************************************************************/
#include "hunspellworker.h"
+#include "virtualkeyboarddebug.h"
#include <QVector>
#include <QTextCodec>
+#include <QFileInfo>
+#include <QTime>
+
+HunspellLoadDictionaryTask::HunspellLoadDictionaryTask(const QString &locale, const QStringList &searchPaths) :
+ HunspellTask(),
+ hunspellPtr(0),
+ locale(locale),
+ searchPaths(searchPaths)
+{
+}
+
+void HunspellLoadDictionaryTask::run()
+{
+ Q_ASSERT(hunspellPtr != 0);
+
+ VIRTUALKEYBOARD_DEBUG() << "HunspellLoadDictionaryTask::run(): locale:" << locale;
+
+#ifdef QT_VIRTUALKEYBOARD_DEBUG
+ QTime perf;
+ perf.start();
+#endif
+
+ if (*hunspellPtr) {
+ Hunspell_destroy(*hunspellPtr);
+ *hunspellPtr = 0;
+ }
+
+ QString affPath;
+ QString dicPath;
+ foreach (const QString &searchPath, searchPaths) {
+ affPath = QStringLiteral("%1/%2.aff").arg(searchPath).arg(locale);
+ if (QFileInfo(affPath).exists()) {
+ dicPath = QStringLiteral("%1/%2.dic").arg(searchPath).arg(locale);
+ if (QFileInfo(dicPath).exists())
+ break;
+ dicPath.clear();
+ }
+ affPath.clear();
+ }
+
+ if (affPath.isEmpty() || dicPath.isEmpty()) {
+ VIRTUALKEYBOARD_DEBUG() << "Hunspell dictionary is missing for the" << locale << "language. Search paths" << searchPaths;
+ return;
+ }
+
+ *hunspellPtr = Hunspell_create(affPath.toUtf8().constData(), dicPath.toUtf8().constData());
+ if (*hunspellPtr) {
+ /* Make sure the encoding used by the dictionary is supported
+ by the QTextCodec.
+ */
+ if (!QTextCodec::codecForName(Hunspell_get_dic_encoding(*hunspellPtr))) {
+ qWarning() << "The Hunspell dictionary" << dicPath << "cannot be used because it uses an unknown text codec" << QString(Hunspell_get_dic_encoding(*hunspellPtr));
+ Hunspell_destroy(*hunspellPtr);
+ *hunspellPtr = 0;
+ }
+ }
+
+#ifdef QT_VIRTUALKEYBOARD_DEBUG
+ VIRTUALKEYBOARD_DEBUG() << "HunspellLoadDictionaryTask::run(): time:" << perf.elapsed() << "ms";
+#endif
+}
void HunspellBuildSuggestionsTask::run()
{
+#ifdef QT_VIRTUALKEYBOARD_DEBUG
+ QTime perf;
+ perf.start();
+#endif
+
wordList->list.append(word);
wordList->index = 0;
@@ -85,6 +152,10 @@ void HunspellBuildSuggestionsTask::run()
}
}
Hunspell_free_list(hunspell, &slst, n);
+
+#ifdef QT_VIRTUALKEYBOARD_DEBUG
+ VIRTUALKEYBOARD_DEBUG() << "HunspellBuildSuggestionsTask::run(): time:" << perf.elapsed() << "ms";
+#endif
}
bool HunspellBuildSuggestionsTask::spellCheck(const QString &word)
@@ -140,11 +211,11 @@ void HunspellUpdateSuggestionsTask::run()
emit updateSuggestions(wordList->list, wordList->index);
}
-HunspellWorker::HunspellWorker(Hunhandle *hunspell, QObject *parent) :
+HunspellWorker::HunspellWorker(QObject *parent) :
QThread(parent),
taskSema(),
taskLock(),
- hunspell(hunspell),
+ hunspell(0),
abort(false)
{
}
@@ -154,10 +225,6 @@ HunspellWorker::~HunspellWorker()
abort = true;
taskSema.release(1);
wait();
- if (hunspell) {
- Hunspell_destroy(hunspell);
- hunspell = 0;
- }
}
void HunspellWorker::addTask(QSharedPointer<HunspellTask> task)
@@ -173,8 +240,6 @@ void HunspellWorker::removeAllTasks()
{
QMutexLocker guard(&taskLock);
taskList.clear();
- if (taskSema.available())
- taskSema.acquire(taskSema.available());
}
void HunspellWorker::run()
@@ -192,8 +257,18 @@ void HunspellWorker::run()
}
}
if (currentTask) {
- currentTask->hunspell = hunspell;
+ QSharedPointer<HunspellLoadDictionaryTask> loadDictionaryTask(currentTask.objectCast<HunspellLoadDictionaryTask>());
+ if (loadDictionaryTask)
+ loadDictionaryTask->hunspellPtr = &hunspell;
+ else if (hunspell)
+ currentTask->hunspell = hunspell;
+ else
+ continue;
currentTask->run();
}
}
+ if (hunspell) {
+ Hunspell_destroy(hunspell);
+ hunspell = 0;
+ }
}
diff --git a/src/virtualkeyboard/hunspellworker.h b/src/virtualkeyboard/hunspellworker.h
index 789cd6e3..38cc59d8 100644
--- a/src/virtualkeyboard/hunspellworker.h
+++ b/src/virtualkeyboard/hunspellworker.h
@@ -40,6 +40,19 @@ public:
Hunhandle *hunspell;
};
+class HunspellLoadDictionaryTask : public HunspellTask
+{
+ Q_OBJECT
+public:
+ explicit HunspellLoadDictionaryTask(const QString &locale, const QStringList &searchPaths);
+
+ void run();
+
+ Hunhandle **hunspellPtr;
+ const QString locale;
+ const QStringList searchPaths;
+};
+
class HunspellWordList
{
public:
@@ -85,16 +98,32 @@ class HunspellWorker : public QThread
{
Q_OBJECT
public:
- explicit HunspellWorker(Hunhandle *hunspell, QObject *parent = 0);
+ explicit HunspellWorker(QObject *parent = 0);
~HunspellWorker();
void addTask(QSharedPointer<HunspellTask> task);
void removeAllTasks();
+ template <class X>
+ void removeAllTasksExcept() {
+ QMutexLocker guard(&taskLock);
+ for (int i = 0; i < taskList.size();) {
+ QSharedPointer<X> task(taskList[i].objectCast<X>());
+ if (!task)
+ taskList.removeAt(i);
+ else
+ i++;
+ }
+ }
+
protected:
void run();
private:
+ void createHunspell();
+
+private:
+ friend class HunspellLoadDictionaryTask;
QList<QSharedPointer<HunspellTask> > taskList;
QSemaphore taskSema;
QMutex taskLock;
diff --git a/tests/auto/inputpanel/data/inputpanel/inputpanel.qml b/tests/auto/inputpanel/data/inputpanel/inputpanel.qml
index e1bc3d04..4f332a72 100644
--- a/tests/auto/inputpanel/data/inputpanel/inputpanel.qml
+++ b/tests/auto/inputpanel/data/inputpanel/inputpanel.qml
@@ -33,6 +33,7 @@ InputPanel {
property var virtualKeyPressPoint: null
readonly property bool autoCapitalizationEnabled: InputContext.shiftHandler.autoCapitalizationEnabled
readonly property bool toggleShiftEnabled: InputContext.shiftHandler.toggleShiftEnabled
+ readonly property string locale: InputContext.locale
readonly property int inputMode: InputContext.inputEngine.inputMode
readonly property var keyboard: findChildByProperty(inputPanel, "objectName", "keyboard", null)
readonly property var keyboardLayoutLoader: findChildByProperty(keyboard, "objectName", "keyboardLayoutLoader", null)
diff --git a/tests/auto/inputpanel/data/tst_inputpanel.qml b/tests/auto/inputpanel/data/tst_inputpanel.qml
index 0385e394..cc925a26 100644
--- a/tests/auto/inputpanel/data/tst_inputpanel.qml
+++ b/tests/auto/inputpanel/data/tst_inputpanel.qml
@@ -73,7 +73,10 @@ Rectangle {
var locale = data !== undefined && data.hasOwnProperty("initLocale") ? data.initLocale : "en_GB"
if (!inputPanel.isLocaleSupported(locale))
expectFail("", "Input locale not enabled")
+ var localeChanged = inputPanel.locale !== locale
verify(inputPanel.setLocale(locale))
+ if (localeChanged && !(textInput.inputMethodHints & Qt.ImhNoPredictiveText))
+ wait(300)
if (data !== undefined && data.hasOwnProperty("initInputMode"))
verify(inputPanel.setInputMode(inputPanel.mapInputMode(data.initInputMode)))
Qt.inputMethod.show()