1/*
2 * backgroundchecker.cpp
3 *
4 * SPDX-FileCopyrightText: 2004 Zack Rusin <zack@kde.org>
5 * SPDX-FileCopyrightText: 2009 Jakub Stachowski <qbast@go2.pl>
6 *
7 * SPDX-License-Identifier: LGPL-2.1-or-later
8 */
9#include "backgroundchecker.h"
10#include "backgroundchecker_p.h"
11
12#include "core_debug.h"
13
14using namespace Sonnet;
15
16void BackgroundCheckerPrivate::start()
17{
18 sentenceOffset = -1;
19 continueChecking();
20}
21
22void BackgroundCheckerPrivate::continueChecking()
23{
24 metaObject()->invokeMethod(obj: this, member: "checkNext", c: Qt::QueuedConnection);
25}
26
27void BackgroundCheckerPrivate::checkNext()
28{
29 do {
30 // go over current sentence
31 while (sentenceOffset != -1 && words.hasNext()) {
32 Token word = words.next();
33 if (!words.isSpellcheckable()) {
34 continue;
35 }
36
37 // ok, this is valid word, do something
38 if (currentDict.isMisspelled(word: word.toString())) {
39 lastMisspelled = word;
40 Q_EMIT misspelling(word.toString(), word.position() + sentenceOffset);
41 return;
42 }
43 }
44 // current sentence done, grab next suitable
45
46 sentenceOffset = -1;
47 const bool autodetectLanguage = currentDict.testAttribute(attr: Speller::AutoDetectLanguage);
48 const bool ignoreUpperCase = !currentDict.testAttribute(attr: Speller::CheckUppercase);
49 while (mainTokenizer.hasNext()) {
50 Token sentence = mainTokenizer.next();
51 if (autodetectLanguage && !autoDetectLanguageDisabled) {
52 if (!mainTokenizer.isSpellcheckable()) {
53 continue;
54 }
55 // FIXME: find best from family en -> en_US, en_GB, ... ?
56 currentDict.setLanguage(mainTokenizer.language());
57 }
58 sentenceOffset = sentence.position();
59 words.setBuffer(sentence.toString());
60 words.setIgnoreUppercase(ignoreUpperCase);
61 break;
62 }
63 } while (sentenceOffset != -1);
64 Q_EMIT done();
65}
66
67BackgroundChecker::BackgroundChecker(QObject *parent)
68 : QObject(parent)
69 , d(new BackgroundCheckerPrivate)
70{
71 connect(sender: d.get(), signal: &BackgroundCheckerPrivate::misspelling, context: this, slot: &BackgroundChecker::misspelling);
72 connect(sender: d.get(), signal: &BackgroundCheckerPrivate::done, context: this, slot: &BackgroundChecker::slotEngineDone);
73}
74
75BackgroundChecker::BackgroundChecker(const Speller &speller, QObject *parent)
76 : QObject(parent)
77 , d(new BackgroundCheckerPrivate)
78{
79 d->currentDict = speller;
80 connect(sender: d.get(), signal: &BackgroundCheckerPrivate::misspelling, context: this, slot: &BackgroundChecker::misspelling);
81 connect(sender: d.get(), signal: &BackgroundCheckerPrivate::done, context: this, slot: &BackgroundChecker::slotEngineDone);
82}
83
84BackgroundChecker::~BackgroundChecker() = default;
85
86void BackgroundChecker::setText(const QString &text)
87{
88 d->mainTokenizer.setBuffer(text);
89 d->start();
90}
91
92void BackgroundChecker::start()
93{
94 // ## what if d->currentText.isEmpty()?
95
96 // TODO: carry state from last buffer
97 d->mainTokenizer.setBuffer(fetchMoreText());
98 d->start();
99}
100
101void BackgroundChecker::stop()
102{
103 // d->stop();
104}
105
106QString BackgroundChecker::fetchMoreText()
107{
108 return QString();
109}
110
111void BackgroundChecker::finishedCurrentFeed()
112{
113}
114
115bool BackgroundChecker::autoDetectLanguageDisabled() const
116{
117 return d->autoDetectLanguageDisabled;
118}
119
120void BackgroundChecker::setAutoDetectLanguageDisabled(bool autoDetectDisabled)
121{
122 d->autoDetectLanguageDisabled = autoDetectDisabled;
123}
124
125void BackgroundChecker::setSpeller(const Speller &speller)
126{
127 d->currentDict = speller;
128}
129
130Speller BackgroundChecker::speller() const
131{
132 return d->currentDict;
133}
134
135bool BackgroundChecker::checkWord(const QString &word)
136{
137 return d->currentDict.isCorrect(word);
138}
139
140bool BackgroundChecker::addWordToPersonal(const QString &word)
141{
142 return d->currentDict.addToPersonal(word);
143}
144
145bool BackgroundChecker::addWordToSession(const QString &word)
146{
147 return d->currentDict.addToSession(word);
148}
149
150QStringList BackgroundChecker::suggest(const QString &word) const
151{
152 return d->currentDict.suggest(word);
153}
154
155void BackgroundChecker::changeLanguage(const QString &lang)
156{
157 // this sets language only for current sentence
158 d->currentDict.setLanguage(lang);
159}
160
161void BackgroundChecker::continueChecking()
162{
163 d->continueChecking();
164}
165
166void BackgroundChecker::slotEngineDone()
167{
168 finishedCurrentFeed();
169 const QString currentText = fetchMoreText();
170
171 if (currentText.isNull()) {
172 Q_EMIT done();
173 } else {
174 d->mainTokenizer.setBuffer(currentText);
175 d->start();
176 }
177}
178
179QString BackgroundChecker::text() const
180{
181 return d->mainTokenizer.buffer();
182}
183
184QString BackgroundChecker::currentContext() const
185{
186 int len = 60;
187 // we don't want the expression underneath casted to an unsigned int
188 // which would cause it to always evaluate to false
189 int currentPosition = d->lastMisspelled.position() + d->sentenceOffset;
190 bool begin = ((currentPosition - len / 2) <= 0) ? true : false;
191
192 QString buffer = d->mainTokenizer.buffer();
193 buffer.replace(i: currentPosition, len: d->lastMisspelled.length(), QStringLiteral("<b>%1</b>").arg(a: d->lastMisspelled.toString()));
194
195 QString context;
196 if (begin) {
197 context = QStringLiteral("%1...").arg(a: buffer.mid(position: 0, n: len));
198 } else {
199 context = QStringLiteral("...%1...").arg(a: buffer.mid(position: currentPosition - 20, n: len));
200 }
201
202 context.replace(before: QLatin1Char('\n'), after: QLatin1Char(' '));
203
204 return context;
205}
206
207void Sonnet::BackgroundChecker::replace(int start, const QString &oldText, const QString &newText)
208{
209 // FIXME: here we assume that replacement is in current fragment. So 'words' has
210 // to be adjusted and sentenceOffset does not
211 d->words.replace(position: start - (d->sentenceOffset), len: oldText.length(), newWord: newText);
212 d->mainTokenizer.replace(position: start, len: oldText.length(), newWord: newText);
213}
214
215#include "moc_backgroundchecker.cpp"
216#include "moc_backgroundchecker_p.cpp"
217

source code of sonnet/src/core/backgroundchecker.cpp