File indexing completed on 2024-04-28 15:34:18
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, &BackgroundCheckerPrivate::misspelling, this, &BackgroundChecker::misspelling); 0072 connect(d, &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, &BackgroundCheckerPrivate::misspelling, this, &BackgroundChecker::misspelling); 0081 connect(d, &BackgroundCheckerPrivate::done, this, &BackgroundChecker::slotEngineDone); 0082 } 0083 0084 BackgroundChecker::~BackgroundChecker() 0085 { 0086 delete d; 0087 } 0088 0089 void BackgroundChecker::setText(const QString &text) 0090 { 0091 d->mainTokenizer.setBuffer(text); 0092 d->start(); 0093 } 0094 0095 void BackgroundChecker::start() 0096 { 0097 // ## what if d->currentText.isEmpty()? 0098 0099 // TODO: carry state from last buffer 0100 d->mainTokenizer.setBuffer(fetchMoreText()); 0101 d->start(); 0102 } 0103 0104 void BackgroundChecker::stop() 0105 { 0106 // d->stop(); 0107 } 0108 0109 QString BackgroundChecker::fetchMoreText() 0110 { 0111 return QString(); 0112 } 0113 0114 void BackgroundChecker::finishedCurrentFeed() 0115 { 0116 } 0117 0118 bool BackgroundChecker::autoDetectLanguageDisabled() const 0119 { 0120 return d->autoDetectLanguageDisabled; 0121 } 0122 0123 void BackgroundChecker::setAutoDetectLanguageDisabled(bool autoDetectDisabled) 0124 { 0125 d->autoDetectLanguageDisabled = autoDetectDisabled; 0126 } 0127 0128 void BackgroundChecker::setSpeller(const Speller &speller) 0129 { 0130 d->currentDict = speller; 0131 } 0132 0133 Speller BackgroundChecker::speller() const 0134 { 0135 return d->currentDict; 0136 } 0137 0138 bool BackgroundChecker::checkWord(const QString &word) 0139 { 0140 return d->currentDict.isCorrect(word); 0141 } 0142 0143 bool BackgroundChecker::addWordToPersonal(const QString &word) 0144 { 0145 return d->currentDict.addToPersonal(word); 0146 } 0147 0148 bool BackgroundChecker::addWordToSession(const QString &word) 0149 { 0150 return d->currentDict.addToSession(word); 0151 } 0152 0153 QStringList BackgroundChecker::suggest(const QString &word) const 0154 { 0155 return d->currentDict.suggest(word); 0156 } 0157 0158 void BackgroundChecker::changeLanguage(const QString &lang) 0159 { 0160 // this sets language only for current sentence 0161 d->currentDict.setLanguage(lang); 0162 } 0163 0164 void BackgroundChecker::continueChecking() 0165 { 0166 d->continueChecking(); 0167 } 0168 0169 void BackgroundChecker::slotEngineDone() 0170 { 0171 finishedCurrentFeed(); 0172 const QString currentText = fetchMoreText(); 0173 0174 if (currentText.isNull()) { 0175 Q_EMIT done(); 0176 } else { 0177 d->mainTokenizer.setBuffer(currentText); 0178 d->start(); 0179 } 0180 } 0181 0182 QString BackgroundChecker::text() const 0183 { 0184 return d->mainTokenizer.buffer(); 0185 } 0186 0187 QString BackgroundChecker::currentContext() const 0188 { 0189 int len = 60; 0190 // we don't want the expression underneath casted to an unsigned int 0191 // which would cause it to always evaluate to false 0192 int currentPosition = d->lastMisspelled.position() + d->sentenceOffset; 0193 bool begin = ((currentPosition - len / 2) <= 0) ? true : false; 0194 0195 QString buffer = d->mainTokenizer.buffer(); 0196 buffer.replace(currentPosition, d->lastMisspelled.length(), QStringLiteral("<b>%1</b>").arg(d->lastMisspelled.toString())); 0197 0198 QString context; 0199 if (begin) { 0200 context = QStringLiteral("%1...").arg(buffer.mid(0, len)); 0201 } else { 0202 context = QStringLiteral("...%1...").arg(buffer.mid(currentPosition - 20, len)); 0203 } 0204 0205 context.replace(QLatin1Char('\n'), QLatin1Char(' ')); 0206 0207 return context; 0208 } 0209 0210 void Sonnet::BackgroundChecker::replace(int start, const QString &oldText, const QString &newText) 0211 { 0212 // FIXME: here we assume that replacement is in current fragment. So 'words' has 0213 // to be adjusted and sentenceOffset does not 0214 d->words.replace(start - (d->sentenceOffset), oldText.length(), newText); 0215 d->mainTokenizer.replace(start, oldText.length(), newText); 0216 } 0217 0218 #include "moc_backgroundchecker.cpp" 0219 #include "moc_backgroundchecker_p.cpp"