10#include "voikkodebug.h"
14#include <QStandardPaths>
20#include <QJsonDocument>
26inline const QString replacement_bad_str() Q_DECL_NOEXCEPT
28 return QStringLiteral(
"bad");
31inline const QString replacement_good_str() Q_DECL_NOEXCEPT
33 return QStringLiteral(
"good");
36inline const QString personal_words_str() Q_DECL_NOEXCEPT
38 return QStringLiteral(
"PersonalWords");
41inline const QString replacements_str() Q_DECL_NOEXCEPT
43 return QStringLiteral(
"Replacements");
47QString getUserDictionaryPath() Q_DECL_NOEXCEPT
53 if (QSysInfo::windowsVersion() == QSysInfo::WV_XP || QSysInfo::windowsVersion() == QSysInfo::WV_2003) {
56 directory += QStringLiteral(
"/../../Application Data");
58 directory += QStringLiteral(
"/../Roaming");
62 directory += QStringLiteral(
"/Sonnet");
66 return path.absoluteFilePath(QStringLiteral(
"Voikko-user-dictionary.json"));
72 pair[replacement_bad_str()] = good;
73 pair[replacement_good_str()] = bad;
75 auto replaceList = languageNode[replacements_str()].toArray();
76 replaceList.append(pair);
77 languageNode[replacements_str()] = replaceList;
80void addPersonalWordToNode(
QJsonObject &languageNode,
const QString &word) Q_DECL_NOEXCEPT
82 auto arr = languageNode[personal_words_str()].toArray();
84 languageNode[personal_words_str()] = arr;
94 QFile userDictFile(fileName);
96 if (!userDictFile.exists()) {
101 qCWarning(SONNET_VOIKKO) <<
"Could not open personal dictionary. Failed to open file" << fileName;
102 qCWarning(SONNET_VOIKKO) <<
"Reason:" << userDictFile.errorString();
107 userDictFile.close();
113class VoikkoDictPrivate
116 VoikkoHandle *m_handle;
123 QString m_userDictionaryFilepath;
128 VoikkoDictPrivate(
const QString &language,
const VoikkoDict *publicPart) Q_DECL_NOEXCEPT : q(publicPart),
129 m_userDictionaryFilepath(getUserDictionaryPath()),
130 m_conversionBuffer(256)
133 m_handle = voikkoInit(&error, language.toUtf8().data(),
nullptr);
135 if (error !=
nullptr) {
136 qCWarning(SONNET_VOIKKO) <<
"Failed to initialize Voikko spelling backend. Reason:" <<
error;
138 loadUserDictionary();
150 QFile userDictFile(m_userDictionaryFilepath);
153 qCWarning(SONNET_VOIKKO) <<
"Could not save personal dictionary. Failed to open file:" << m_userDictionaryFilepath;
154 qCWarning(SONNET_VOIKKO) <<
"Reason:" << userDictFile.errorString();
159 auto root = readJsonRootObject(m_userDictionaryFilepath);
160 auto languageNode = root[q->language()].toObject();
164 addReplacementToNode(languageNode, bad, good);
166 addPersonalWordToNode(languageNode, personalWord);
169 root[q->language()] = languageNode;
172 userDictFile.reset();
173 userDictFile.write(dictDoc.
toJson());
174 userDictFile.close();
175 qCDebug(SONNET_VOIKKO) <<
"Changes to user dictionary saved to file: " << m_userDictionaryFilepath;
184 void loadUserDictionary() Q_DECL_NOEXCEPT
188 auto root = readJsonRootObject(m_userDictionaryFilepath);
189 auto languageNode = root[q->language()].toObject();
191 if (languageNode.isEmpty()) {
195 loadUserWords(languageNode);
196 loadUserReplacements(languageNode);
203 inline const wchar_t *QStringToWchar(
const QString &str) Q_DECL_NOEXCEPT
205 m_conversionBuffer.
resize(str.length() + 1);
206 int size = str.toWCharArray(m_conversionBuffer.
data());
207 m_conversionBuffer[size] =
'\0';
216 inline void loadUserWords(
const QJsonObject &languageNode) Q_DECL_NOEXCEPT
218 const auto words = languageNode[personal_words_str()].toArray();
219 for (
auto word : words) {
220 m_personalWords.
insert(word.toString());
222 qCDebug(SONNET_VOIKKO) << QStringLiteral(
"Loaded %1 words from the user dictionary.").arg(words.size());
228 inline void loadUserReplacements(
const QJsonObject &languageNode) Q_DECL_NOEXCEPT
230 const auto words = languageNode[replacements_str()].toArray();
231 for (
auto pair : words) {
232 m_replacements[pair.toObject()[replacement_bad_str()].toString()] = pair.toObject()[replacement_good_str()].toString();
234 qCDebug(SONNET_VOIKKO) << QStringLiteral(
"Loaded %1 replacements from the user dictionary.").arg(words.size());
238VoikkoDict::VoikkoDict(
const QString &language) Q_DECL_NOEXCEPT : SpellerPlugin(language), d(
new VoikkoDictPrivate(language,
this))
240 qCDebug(SONNET_VOIKKO) <<
"Loading dictionary for language:" << language;
243VoikkoDict::~VoikkoDict()
247bool VoikkoDict::isCorrect(
const QString &word)
const
250 if (d->m_sessionWords.contains(word) || d->m_personalWords.contains(word)) {
254 return voikkoSpellUcs4(d->m_handle, d->QStringToWchar(word)) == VOIKKO_SPELL_OK;
261 auto userDictPos = d->m_replacements.constFind(word);
262 if (userDictPos != d->m_replacements.constEnd()) {
263 suggestions.
append(*userDictPos);
266 auto voikkoSuggestions = voikkoSuggestUcs4(d->m_handle, d->QStringToWchar(word));
268 if (!voikkoSuggestions) {
272 for (
int i = 0; voikkoSuggestions[i] !=
nullptr; ++i) {
274 suggestions.
append(suggestion);
276 qCDebug(SONNET_VOIKKO) <<
"Misspelled:" << word <<
"|Suggestons:" << suggestions.
join(
QLatin1String(
", "));
278 voikko_free_suggest_ucs4(voikkoSuggestions);
283bool VoikkoDict::storeReplacement(
const QString &bad,
const QString &good)
285 qCDebug(SONNET_VOIKKO) <<
"Adding new replacement pair to user dictionary:" << bad <<
"->" << good;
286 d->m_replacements[bad] = good;
287 return d->storePersonal(
QString(), bad, good);
290bool VoikkoDict::addToPersonal(
const QString &word)
292 qCDebug(SONNET_VOIKKO()) <<
"Adding new word to user dictionary" << word;
293 d->m_personalWords.insert(word);
294 return d->storePersonal(word);
297bool VoikkoDict::addToSession(
const QString &word)
299 qCDebug(SONNET_VOIKKO()) <<
"Adding new word to session dictionary" << word;
300 d->m_sessionWords.insert(word);
304bool VoikkoDict::initFailed() const Q_DECL_NOEXCEPT
QString path(const QString &relativePath)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
void setObject(const QJsonObject &object)
QByteArray toJson(JsonFormat format) const const
void append(QList< T > &&value)
const_pointer constData() const const
void resize(qsizetype size)
iterator insert(const T &value)
QString writableLocation(StandardLocation type)
QString fromWCharArray(const wchar_t *string, qsizetype size)
bool isEmpty() const const
QString join(QChar separator) const const