2022-01-06 19:29:19 +00:00
|
|
|
#include "SpellChecker.h"
|
|
|
|
|
|
|
|
#ifdef Q_OS_MACOS
|
2022-02-21 18:03:38 +00:00
|
|
|
# include "hunspell/hunspell.hxx"
|
2022-01-06 19:29:19 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <QDebug>
|
2022-02-21 18:03:38 +00:00
|
|
|
#include <QFile>
|
2022-01-06 19:29:19 +00:00
|
|
|
#include <QLocale>
|
2022-02-21 18:03:38 +00:00
|
|
|
#include <QTextCodec>
|
2022-01-06 19:29:19 +00:00
|
|
|
|
|
|
|
#include <QDir>
|
2022-02-21 18:03:38 +00:00
|
|
|
#include <QGuiApplication>
|
|
|
|
#include <QRegularExpression>
|
2022-01-06 19:29:19 +00:00
|
|
|
|
|
|
|
#include <QInputMethod>
|
|
|
|
|
2022-02-21 18:03:38 +00:00
|
|
|
SpellChecker::SpellChecker(QObject* parent)
|
2022-01-06 19:29:19 +00:00
|
|
|
: QObject(parent)
|
|
|
|
#ifdef Q_OS_MACOS
|
|
|
|
, m_hunspell(nullptr)
|
|
|
|
#endif
|
|
|
|
, m_userDict("userDict_")
|
2022-02-21 18:03:38 +00:00
|
|
|
{ }
|
2022-01-06 19:29:19 +00:00
|
|
|
|
|
|
|
SpellChecker::~SpellChecker()
|
|
|
|
{
|
|
|
|
#ifdef Q_OS_MACOS
|
|
|
|
delete m_hunspell;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-02-21 18:03:38 +00:00
|
|
|
bool SpellChecker::spell(const QString& word)
|
2022-01-06 19:29:19 +00:00
|
|
|
{
|
|
|
|
#ifdef Q_OS_MACOS
|
|
|
|
return m_hunspell->spell(m_codec->fromUnicode(word).toStdString());
|
|
|
|
#else
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SpellChecker::isInit() const
|
|
|
|
{
|
|
|
|
#ifdef Q_OS_MACOS
|
|
|
|
return !m_hunspell;
|
|
|
|
#else
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpellChecker::initHunspell()
|
|
|
|
{
|
|
|
|
#ifdef Q_OS_MACOS
|
2022-02-21 18:03:38 +00:00
|
|
|
if(m_hunspell)
|
|
|
|
{
|
2022-01-06 19:29:19 +00:00
|
|
|
delete m_hunspell;
|
|
|
|
}
|
|
|
|
|
2022-02-21 18:03:38 +00:00
|
|
|
QString dictFile = QGuiApplication::applicationDirPath() + "/dictionaries/" + m_lang + "/index.dic";
|
|
|
|
QString affixFile = QGuiApplication::applicationDirPath() + "/dictionaries/" + m_lang + "/index.aff";
|
2022-01-06 19:29:19 +00:00
|
|
|
QByteArray dictFilePathBA = dictFile.toLocal8Bit();
|
|
|
|
QByteArray affixFilePathBA = affixFile.toLocal8Bit();
|
2022-02-21 18:03:38 +00:00
|
|
|
m_hunspell = new Hunspell(affixFilePathBA.constData(), dictFilePathBA.constData());
|
2022-01-06 19:29:19 +00:00
|
|
|
|
|
|
|
// detect encoding analyzing the SET option in the affix file
|
|
|
|
auto encoding = QStringLiteral("ISO8859-15");
|
|
|
|
QFile _affixFile(affixFile);
|
2022-02-21 18:03:38 +00:00
|
|
|
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;
|
|
|
|
}
|
2022-01-06 19:29:19 +00:00
|
|
|
}
|
2022-02-21 18:03:38 +00:00
|
|
|
_affixFile.close();
|
2022-01-06 19:29:19 +00:00
|
|
|
}
|
|
|
|
m_codec = QTextCodec::codecForName(encoding.toLatin1().constData());
|
|
|
|
|
|
|
|
QString userDict = m_userDict + m_lang + ".txt";
|
|
|
|
|
2022-02-21 18:03:38 +00:00
|
|
|
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.";
|
2022-01-06 19:29:19 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-02-21 18:03:38 +00:00
|
|
|
QVariantList SpellChecker::suggest(const QString& word)
|
2022-01-06 19:29:19 +00:00
|
|
|
{
|
|
|
|
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());
|
2022-02-21 18:03:38 +00:00
|
|
|
if(numSuggestions > 0)
|
|
|
|
{
|
2022-01-06 19:29:19 +00:00
|
|
|
suggestions.reserve(numSuggestions);
|
2022-02-21 18:03:38 +00:00
|
|
|
for(int i = 0; i < numSuggestions; i++)
|
|
|
|
{
|
|
|
|
suggestions << m_codec->toUnicode(QByteArray::fromStdString(wordlist[i]));
|
2022-01-06 19:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return suggestions;
|
|
|
|
}
|
|
|
|
|
2022-02-21 18:03:38 +00:00
|
|
|
void SpellChecker::ignoreWord(const QString& word)
|
2022-01-06 19:29:19 +00:00
|
|
|
{
|
|
|
|
#ifdef Q_OS_MACOS
|
|
|
|
m_hunspell->add(m_codec->fromUnicode(word).constData());
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-02-21 18:03:38 +00:00
|
|
|
void SpellChecker::addToUserWordlist(const QString& word)
|
2022-01-06 19:29:19 +00:00
|
|
|
{
|
|
|
|
#ifdef Q_OS_MACOS
|
|
|
|
QString userDict = m_userDict + m_lang + ".txt";
|
2022-02-21 18:03:38 +00:00
|
|
|
if(!userDict.isEmpty())
|
|
|
|
{
|
2022-01-06 19:29:19 +00:00
|
|
|
QFile userDictonaryFile(userDict);
|
2022-02-21 18:03:38 +00:00
|
|
|
if(userDictonaryFile.open(QIODevice::Append))
|
|
|
|
{
|
2022-01-06 19:29:19 +00:00
|
|
|
QTextStream stream(&userDictonaryFile);
|
|
|
|
stream << word << "\n";
|
|
|
|
userDictonaryFile.close();
|
|
|
|
}
|
2022-02-21 18:03:38 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
qWarning() << "User dictionary in " << userDict << "could not be opened for appending a new word";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-01-06 19:29:19 +00:00
|
|
|
qDebug() << "User dictionary not set.";
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
const QString& SpellChecker::lang() const
|
|
|
|
{
|
|
|
|
return m_lang;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpellChecker::setLang(const QString& lang)
|
|
|
|
{
|
2022-02-21 18:03:38 +00:00
|
|
|
if(m_lang != lang)
|
|
|
|
{
|
2022-01-06 19:29:19 +00:00
|
|
|
m_lang = lang;
|
|
|
|
initHunspell();
|
|
|
|
emit langChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const QString& SpellChecker::userDict() const
|
|
|
|
{
|
|
|
|
return m_userDict;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpellChecker::setUserDict(const QString& userDict)
|
|
|
|
{
|
2022-02-21 18:03:38 +00:00
|
|
|
if(m_userDict != userDict)
|
|
|
|
{
|
2022-01-06 19:29:19 +00:00
|
|
|
m_userDict = userDict;
|
|
|
|
emit userDictChanged();
|
|
|
|
}
|
|
|
|
}
|