parent
556de60a8d
commit
b9b4ecae21
|
@ -12,7 +12,8 @@ DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
handler.cpp \
|
handler.cpp \
|
||||||
main.cpp \
|
main.cpp \
|
||||||
sandboxapp.cpp
|
sandboxapp.cpp \
|
||||||
|
spellchecker.cpp
|
||||||
|
|
||||||
!macx {
|
!macx {
|
||||||
SOURCES += statuswindow.cpp
|
SOURCES += statuswindow.cpp
|
||||||
|
@ -59,7 +60,8 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
handler.h \
|
handler.h \
|
||||||
sandboxapp.h \
|
sandboxapp.h \
|
||||||
statuswindow.h
|
statuswindow.h \
|
||||||
|
spellchecker.h
|
||||||
|
|
||||||
OTHER_FILES += $$files($$PWD/../*.qml, true)
|
OTHER_FILES += $$files($$PWD/../*.qml, true)
|
||||||
OTHER_FILES += $$files($$PWD/*.qml, true)
|
OTHER_FILES += $$files($$PWD/*.qml, true)
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include "statuswindow.h"
|
#include "statuswindow.h"
|
||||||
|
#include "spellchecker.h"
|
||||||
|
|
||||||
SandboxApp::SandboxApp(int &argc, char **argv)
|
SandboxApp::SandboxApp(int &argc, char **argv)
|
||||||
: QGuiApplication(argc, argv),
|
: QGuiApplication(argc, argv),
|
||||||
|
@ -16,6 +17,7 @@ SandboxApp::SandboxApp(int &argc, char **argv)
|
||||||
void SandboxApp::startEngine()
|
void SandboxApp::startEngine()
|
||||||
{
|
{
|
||||||
qmlRegisterType<StatusWindow>("Sandbox", 0, 1, "StatusWindow");
|
qmlRegisterType<StatusWindow>("Sandbox", 0, 1, "StatusWindow");
|
||||||
|
qmlRegisterType<SpellChecker>("Sandbox", 0, 1, "Spellchecker");
|
||||||
|
|
||||||
#ifdef QT_DEBUG
|
#ifdef QT_DEBUG
|
||||||
const QUrl url(applicationDirPath() + "/../main.qml");
|
const QUrl url(applicationDirPath() + "/../main.qml");
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
#include "spellchecker.h"
|
||||||
|
|
||||||
|
#include "hunspell.hxx"
|
||||||
|
#include <QTextCodec>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QLocale>
|
||||||
|
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
SpellChecker::SpellChecker(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_hunspell(nullptr)
|
||||||
|
, m_userDict("userDict_")
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SpellChecker::~SpellChecker()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
delete m_hunspell;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpellChecker::spell(const QString &word)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
return m_hunspell->spell(m_codec->fromUnicode(word).toStdString());
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpellChecker::isInit() const
|
||||||
|
{
|
||||||
|
return !m_hunspell;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpellChecker::initHunspell()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
if (m_hunspell) {
|
||||||
|
delete m_hunspell;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString dictFile = QApplication::applicationDirPath() + "/dictionaries/" + m_lang + "/index.dic";
|
||||||
|
QString affixFile = QApplication::applicationDirPath() + "/dictionaries/" + m_lang + "/index.aff";
|
||||||
|
QByteArray dictFilePathBA = dictFile.toLocal8Bit();
|
||||||
|
QByteArray affixFilePathBA = affixFile.toLocal8Bit();
|
||||||
|
m_hunspell = new Hunspell(affixFilePathBA.constData(),
|
||||||
|
dictFilePathBA.constData());
|
||||||
|
|
||||||
|
// detect encoding analyzing the SET option in the affix file
|
||||||
|
auto encoding = QStringLiteral("ISO8859-15");
|
||||||
|
QFile _affixFile(affixFile);
|
||||||
|
if (_affixFile.open(QIODevice::ReadOnly)) {
|
||||||
|
QTextStream stream(&_affixFile);
|
||||||
|
QRegularExpression enc_detector(
|
||||||
|
QStringLiteral("^\\s*SET\\s+([A-Z0-9\\-]+)\\s*"),
|
||||||
|
QRegularExpression::CaseInsensitiveOption);
|
||||||
|
QString sLine;
|
||||||
|
QRegularExpressionMatch match;
|
||||||
|
while (!stream.atEnd()) {
|
||||||
|
sLine = stream.readLine();
|
||||||
|
if (sLine.isEmpty()) { continue; }
|
||||||
|
match = enc_detector.match(sLine);
|
||||||
|
if (match.hasMatch()) {
|
||||||
|
encoding = match.captured(1);
|
||||||
|
qDebug() << "Encoding set to " + encoding;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_affixFile.close();
|
||||||
|
}
|
||||||
|
m_codec = QTextCodec::codecForName(encoding.toLatin1().constData());
|
||||||
|
|
||||||
|
QString userDict = m_userDict + m_lang + ".txt";
|
||||||
|
|
||||||
|
if (!userDict.isEmpty()) {
|
||||||
|
QFile userDictonaryFile(userDict);
|
||||||
|
if (userDictonaryFile.open(QIODevice::ReadOnly)) {
|
||||||
|
QTextStream stream(&userDictonaryFile);
|
||||||
|
for (QString word = stream.readLine();
|
||||||
|
!word.isEmpty();
|
||||||
|
word = stream.readLine())
|
||||||
|
ignoreWord(word);
|
||||||
|
userDictonaryFile.close();
|
||||||
|
} else {
|
||||||
|
qWarning() << "User dictionary in " << userDict
|
||||||
|
<< "could not be opened";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "User dictionary not set.";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantList SpellChecker::suggest(const QString &word)
|
||||||
|
{
|
||||||
|
int numSuggestions = 0;
|
||||||
|
QVariantList suggestions;
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
std::vector<std::string> wordlist;
|
||||||
|
wordlist = m_hunspell->suggest(m_codec->fromUnicode(word).toStdString());
|
||||||
|
|
||||||
|
numSuggestions = static_cast<int>(wordlist.size());
|
||||||
|
if (numSuggestions > 0) {
|
||||||
|
suggestions.reserve(numSuggestions);
|
||||||
|
for (int i = 0; i < numSuggestions; i++) {
|
||||||
|
suggestions << m_codec->toUnicode(
|
||||||
|
QByteArray::fromStdString(wordlist[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return suggestions;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpellChecker::ignoreWord(const QString &word)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
m_hunspell->add(m_codec->fromUnicode(word).constData());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpellChecker::addToUserWordlist(const QString &word)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
QString userDict = m_userDict + m_lang + ".txt";
|
||||||
|
if (!userDict.isEmpty()) {
|
||||||
|
QFile userDictonaryFile(userDict);
|
||||||
|
if (userDictonaryFile.open(QIODevice::Append)) {
|
||||||
|
QTextStream stream(&userDictonaryFile);
|
||||||
|
stream << word << "\n";
|
||||||
|
userDictonaryFile.close();
|
||||||
|
} else {
|
||||||
|
qWarning() << "User dictionary in " << userDict
|
||||||
|
<< "could not be opened for appending a new word";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "User dictionary not set.";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString& SpellChecker::lang() const
|
||||||
|
{
|
||||||
|
return m_lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpellChecker::setLang(const QString& lang)
|
||||||
|
{
|
||||||
|
if (m_lang != lang) {
|
||||||
|
m_lang = lang;
|
||||||
|
initHunspell();
|
||||||
|
emit langChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString& SpellChecker::userDict() const
|
||||||
|
{
|
||||||
|
return m_userDict;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpellChecker::setUserDict(const QString& userDict)
|
||||||
|
{
|
||||||
|
if (m_userDict != userDict) {
|
||||||
|
m_userDict = userDict;
|
||||||
|
emit userDictChanged();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
#ifndef SPELLCHECKER_H
|
||||||
|
#define SPELLCHECKER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QQuickTextDocument>
|
||||||
|
#include <QSyntaxHighlighter>
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
class Hunspell;
|
||||||
|
#endif
|
||||||
|
class QTextCodec;
|
||||||
|
|
||||||
|
class SpellChecker : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QString lang READ lang WRITE setLang NOTIFY langChanged)
|
||||||
|
Q_PROPERTY(QString userDict READ userDict WRITE setUserDict NOTIFY userDictChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SpellChecker(QObject *parent = nullptr);
|
||||||
|
~SpellChecker();
|
||||||
|
|
||||||
|
Q_INVOKABLE bool spell(const QString& word);
|
||||||
|
Q_INVOKABLE QVariantList suggest(const QString &word);
|
||||||
|
Q_INVOKABLE void ignoreWord(const QString &word);
|
||||||
|
Q_INVOKABLE void addToUserWordlist(const QString &word);
|
||||||
|
Q_INVOKABLE bool isInit() const;
|
||||||
|
|
||||||
|
const QString& lang() const;
|
||||||
|
void setLang(const QString& lang);
|
||||||
|
|
||||||
|
const QString& userDict() const;
|
||||||
|
void setUserDict(const QString& userDict);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void langChanged();
|
||||||
|
void userDictChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initHunspell();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_lang;
|
||||||
|
QString m_userDict;
|
||||||
|
|
||||||
|
QQuickTextDocument *m_document;
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
Hunspell *m_hunspell;
|
||||||
|
#endif
|
||||||
|
QTextCodec *m_codec;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SPELLCHECKER_H
|
Loading…
Reference in New Issue