File indexing completed on 2024-03-24 04:03:40
0001 /* 0002 * backgroundchecker.cpp 0003 * 0004 * SPDX-FileCopyrightText: 2004 Zack Rusin <zack@kde.org> 0005 * SPDX-FileCopyrightText: 2009 Jakub Stachowski <qbast@go2.pl> 0006 * 0007 * SPDX-License-Identifier: LGPL-2.1-or-later 0008 */ 0009 #include "backgroundchecker.h" 0010 #include "backgroundchecker_p.h" 0011 0012 #include "core_debug.h" 0013 0014 using namespace Sonnet; 0015 0016 void BackgroundCheckerPrivate::start() 0017 { 0018 sentenceOffset = -1; 0019 continueChecking(); 0020 } 0021 0022 void BackgroundCheckerPrivate::continueChecking() 0023 { 0024 metaObject()->invokeMethod(this, "checkNext", Qt::QueuedConnection); 0025 } 0026 0027 void BackgroundCheckerPrivate::checkNext() 0028 { 0029 do { 0030 // go over current sentence 0031 while (sentenceOffset != -1 && words.hasNext()) { 0032 Token word = words.next(); 0033 if (!words.isSpellcheckable()) { 0034 continue; 0035 } 0036 0037 // ok, this is valid word, do something 0038 if (currentDict.isMisspelled(word.toString())) { 0039 lastMisspelled = word; 0040 Q_EMIT misspelling(word.toString(), word.position() + sentenceOffset); 0041 return; 0042 } 0043 } 0044 // current sentence done, grab next suitable 0045 0046 sentenceOffset = -1; 0047 const bool autodetectLanguage = currentDict.testAttribute(Speller::AutoDetectLanguage); 0048 const bool ignoreUpperCase = !currentDict.testAttribute(Speller::CheckUppercase); 0049 while (mainTokenizer.hasNext()) { 0050 Token sentence = mainTokenizer.next(); 0051 if (autodetectLanguage && !autoDetectLanguageDisabled) { 0052 if (!mainTokenizer.isSpellcheckable()) { 0053 continue; 0054 } 0055 // FIXME: find best from family en -> en_US, en_GB, ... ? 0056 currentDict.setLanguage(mainTokenizer.language()); 0057 } 0058 sentenceOffset = sentence.position(); 0059 words.setBuffer(sentence.toString()); 0060 words.setIgnoreUppercase(ignoreUpperCase); 0061 break; 0062 } 0063 } while (sentenceOffset != -1); 0064 Q_EMIT done(); 0065 } 0066 0067 BackgroundChecker::BackgroundChecker(QObject *parent) 0068 : QObject(parent) 0069 , d(new BackgroundCheckerPrivate) 0070 { 0071 connect(d.get(), &BackgroundCheckerPrivate::misspelling, this, &BackgroundChecker::misspelling); 0072 connect(d.get(), &BackgroundCheckerPrivate::done, this, &BackgroundChecker::slotEngineDone); 0073 } 0074 0075 BackgroundChecker::BackgroundChecker(const Speller &speller, QObject *parent) 0076 : QObject(parent) 0077 , d(new BackgroundCheckerPrivate) 0078 { 0079 d->currentDict = speller; 0080 connect(d.get(), &BackgroundCheckerPrivate::misspelling, this, &BackgroundChecker::misspelling); 0081 connect(d.get(), &BackgroundCheckerPrivate::done, this, &BackgroundChecker::slotEngineDone); 0082 } 0083 0084 BackgroundChecker::~BackgroundChecker() = default; 0085 0086 void BackgroundChecker::setText(const QString &text) 0087 { 0088 d->mainTokenizer.setBuffer(text); 0089 d->start(); 0090 } 0091 0092 void BackgroundChecker::start() 0093 { 0094 // ## what if d->currentText.isEmpty()? 0095 0096 // TODO: carry state from last buffer 0097 d->mainTokenizer.setBuffer(fetchMoreText()); 0098 d->start(); 0099 } 0100 0101 void BackgroundChecker::stop() 0102 { 0103 // d->stop(); 0104 } 0105 0106 QString BackgroundChecker::fetchMoreText() 0107 { 0108 return QString(); 0109 } 0110 0111 void BackgroundChecker::finishedCurrentFeed() 0112 { 0113 } 0114 0115 bool BackgroundChecker::autoDetectLanguageDisabled() const 0116 { 0117 return d->autoDetectLanguageDisabled; 0118 } 0119 0120 void BackgroundChecker::setAutoDetectLanguageDisabled(bool autoDetectDisabled) 0121 { 0122 d->autoDetectLanguageDisabled = autoDetectDisabled; 0123 } 0124 0125 void BackgroundChecker::setSpeller(const Speller &speller) 0126 { 0127 d->currentDict = speller; 0128 } 0129 0130 Speller BackgroundChecker::speller() const 0131 { 0132 return d->currentDict; 0133 } 0134 0135 bool BackgroundChecker::checkWord(const QString &word) 0136 { 0137 return d->currentDict.isCorrect(word); 0138 } 0139 0140 bool BackgroundChecker::addWordToPersonal(const QString &word) 0141 { 0142 return d->currentDict.addToPersonal(word); 0143 } 0144 0145 bool BackgroundChecker::addWordToSession(const QString &word) 0146 { 0147 return d->currentDict.addToSession(word); 0148 } 0149 0150 QStringList BackgroundChecker::suggest(const QString &word) const 0151 { 0152 return d->currentDict.suggest(word); 0153 } 0154 0155 void BackgroundChecker::changeLanguage(const QString &lang) 0156 { 0157 // this sets language only for current sentence 0158 d->currentDict.setLanguage(lang); 0159 } 0160 0161 void BackgroundChecker::continueChecking() 0162 { 0163 d->continueChecking(); 0164 } 0165 0166 void BackgroundChecker::slotEngineDone() 0167 { 0168 finishedCurrentFeed(); 0169 const QString currentText = fetchMoreText(); 0170 0171 if (currentText.isNull()) { 0172 Q_EMIT done(); 0173 } else { 0174 d->mainTokenizer.setBuffer(currentText); 0175 d->start(); 0176 } 0177 } 0178 0179 QString BackgroundChecker::text() const 0180 { 0181 return d->mainTokenizer.buffer(); 0182 } 0183 0184 QString BackgroundChecker::currentContext() const 0185 { 0186 int len = 60; 0187 // we don't want the expression underneath casted to an unsigned int 0188 // which would cause it to always evaluate to false 0189 int currentPosition = d->lastMisspelled.position() + d->sentenceOffset; 0190 bool begin = ((currentPosition - len / 2) <= 0) ? true : false; 0191 0192 QString buffer = d->mainTokenizer.buffer(); 0193 buffer.replace(currentPosition, d->lastMisspelled.length(), QStringLiteral("<b>%1</b>").arg(d->lastMisspelled.toString())); 0194 0195 QString context; 0196 if (begin) { 0197 context = QStringLiteral("%1...").arg(buffer.mid(0, len)); 0198 } else { 0199 context = QStringLiteral("...%1...").arg(buffer.mid(currentPosition - 20, len)); 0200 } 0201 0202 context.replace(QLatin1Char('\n'), QLatin1Char(' ')); 0203 0204 return context; 0205 } 0206 0207 void Sonnet::BackgroundChecker::replace(int start, const QString &oldText, const QString &newText) 0208 { 0209 // FIXME: here we assume that replacement is in current fragment. So 'words' has 0210 // to be adjusted and sentenceOffset does not 0211 d->words.replace(start - (d->sentenceOffset), oldText.length(), newText); 0212 d->mainTokenizer.replace(start, oldText.length(), newText); 0213 } 0214 0215 #include "moc_backgroundchecker.cpp" 0216 #include "moc_backgroundchecker_p.cpp"