File indexing completed on 2024-04-28 15:34:21
0001 /* 0002 * dialog.cpp 0003 * 0004 * SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org> 0005 * SPDX-FileCopyrightText: 2009-2010 Michel Ludwig <michel.ludwig@kdemail.net> 0006 * 0007 * SPDX-License-Identifier: LGPL-2.1-or-later 0008 */ 0009 #include "dialog.h" 0010 #include "ui_sonnetui.h" 0011 0012 #include "backgroundchecker.h" 0013 #include "settingsimpl_p.h" 0014 #include "speller.h" 0015 0016 #include <QProgressDialog> 0017 0018 #include <QDialogButtonBox> 0019 #include <QMessageBox> 0020 #include <QPushButton> 0021 #include <QStringListModel> 0022 0023 namespace Sonnet 0024 { 0025 // to initially disable sorting in the suggestions listview 0026 #define NONSORTINGCOLUMN 2 0027 0028 class ReadOnlyStringListModel : public QStringListModel 0029 { 0030 public: 0031 explicit ReadOnlyStringListModel(QObject *parent) 0032 : QStringListModel(parent) 0033 { 0034 } 0035 0036 Qt::ItemFlags flags(const QModelIndex &index) const override 0037 { 0038 Q_UNUSED(index); 0039 return Qt::ItemIsEnabled | Qt::ItemIsSelectable; 0040 } 0041 }; 0042 0043 class DialogPrivate 0044 { 0045 public: 0046 Ui_SonnetUi ui; 0047 ReadOnlyStringListModel *suggestionsModel = nullptr; 0048 QWidget *wdg = nullptr; 0049 QDialogButtonBox *buttonBox = nullptr; 0050 QProgressDialog *progressDialog = nullptr; 0051 QString originalBuffer; 0052 BackgroundChecker *checker = nullptr; 0053 0054 QString currentWord; 0055 int currentPosition; 0056 QMap<QString, QString> replaceAllMap; 0057 bool restart; // used when text is distributed across several qtextedits, eg in KAider 0058 0059 QMap<QString, QString> dictsMap; 0060 0061 int progressDialogTimeout; 0062 bool showCompletionMessageBox; 0063 bool spellCheckContinuedAfterReplacement; 0064 bool canceled; 0065 0066 void deleteProgressDialog(bool directly) 0067 { 0068 if (progressDialog) { 0069 progressDialog->hide(); 0070 if (directly) { 0071 delete progressDialog; 0072 } else { 0073 progressDialog->deleteLater(); 0074 } 0075 progressDialog = nullptr; 0076 } 0077 } 0078 }; 0079 0080 Dialog::Dialog(BackgroundChecker *checker, QWidget *parent) 0081 : QDialog(parent) 0082 , d(new DialogPrivate) 0083 { 0084 setModal(true); 0085 setWindowTitle(tr("Check Spelling", "@title:window")); 0086 0087 d->checker = checker; 0088 0089 d->canceled = false; 0090 d->showCompletionMessageBox = false; 0091 d->spellCheckContinuedAfterReplacement = true; 0092 d->progressDialogTimeout = -1; 0093 d->progressDialog = nullptr; 0094 0095 initGui(); 0096 initConnections(); 0097 } 0098 0099 Dialog::~Dialog() 0100 { 0101 delete d; 0102 } 0103 0104 void Dialog::initConnections() 0105 { 0106 connect(d->ui.m_addBtn, &QAbstractButton::clicked, this, &Dialog::slotAddWord); 0107 connect(d->ui.m_replaceBtn, &QAbstractButton::clicked, this, &Dialog::slotReplaceWord); 0108 connect(d->ui.m_replaceAllBtn, &QAbstractButton::clicked, this, &Dialog::slotReplaceAll); 0109 connect(d->ui.m_skipBtn, &QAbstractButton::clicked, this, &Dialog::slotSkip); 0110 connect(d->ui.m_skipAllBtn, &QAbstractButton::clicked, this, &Dialog::slotSkipAll); 0111 connect(d->ui.m_suggestBtn, &QAbstractButton::clicked, this, &Dialog::slotSuggest); 0112 connect(d->ui.m_language, &DictionaryComboBox::textActivated, this, &Dialog::slotChangeLanguage); 0113 connect(d->ui.m_suggestions, &QListView::clicked, this, &Dialog::slotSelectionChanged); 0114 connect(d->checker, &BackgroundChecker::misspelling, this, &Dialog::slotMisspelling); 0115 connect(d->checker, &BackgroundChecker::done, this, &Dialog::slotDone); 0116 connect(d->ui.m_suggestions, &QListView::doubleClicked, this, [this](const QModelIndex &) { 0117 slotReplaceWord(); 0118 }); 0119 connect(d->buttonBox, &QDialogButtonBox::accepted, this, &Dialog::slotFinished); 0120 connect(d->buttonBox, &QDialogButtonBox::rejected, this, &Dialog::slotCancel); 0121 connect(d->ui.m_replacement, &QLineEdit::returnPressed, this, &Dialog::slotReplaceWord); 0122 connect(d->ui.m_autoCorrect, &QPushButton::clicked, this, &Dialog::slotAutocorrect); 0123 // button use by kword/kpresenter 0124 // hide by default 0125 d->ui.m_autoCorrect->hide(); 0126 } 0127 0128 void Dialog::initGui() 0129 { 0130 QVBoxLayout *layout = new QVBoxLayout(this); 0131 0132 d->wdg = new QWidget(this); 0133 d->ui.setupUi(d->wdg); 0134 layout->addWidget(d->wdg); 0135 setGuiEnabled(false); 0136 0137 d->buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); 0138 0139 layout->addWidget(d->wdg); 0140 layout->addWidget(d->buttonBox); 0141 0142 // d->ui.m_suggestions->setSorting( NONSORTINGCOLUMN ); 0143 fillDictionaryComboBox(); 0144 d->restart = false; 0145 0146 d->suggestionsModel = new ReadOnlyStringListModel(this); 0147 d->ui.m_suggestions->setModel(d->suggestionsModel); 0148 } 0149 0150 void Dialog::activeAutoCorrect(bool _active) 0151 { 0152 if (_active) { 0153 d->ui.m_autoCorrect->show(); 0154 } else { 0155 d->ui.m_autoCorrect->hide(); 0156 } 0157 } 0158 0159 void Dialog::showProgressDialog(int timeout) 0160 { 0161 d->progressDialogTimeout = timeout; 0162 } 0163 0164 void Dialog::showSpellCheckCompletionMessage(bool b) 0165 { 0166 d->showCompletionMessageBox = b; 0167 } 0168 0169 void Dialog::setSpellCheckContinuedAfterReplacement(bool b) 0170 { 0171 d->spellCheckContinuedAfterReplacement = b; 0172 } 0173 0174 void Dialog::slotAutocorrect() 0175 { 0176 setGuiEnabled(false); 0177 setProgressDialogVisible(true); 0178 Q_EMIT autoCorrect(d->currentWord, d->ui.m_replacement->text()); 0179 slotReplaceWord(); 0180 } 0181 0182 void Dialog::setGuiEnabled(bool b) 0183 { 0184 d->wdg->setEnabled(b); 0185 } 0186 0187 void Dialog::setProgressDialogVisible(bool b) 0188 { 0189 if (!b) { 0190 d->deleteProgressDialog(true); 0191 } else if (d->progressDialogTimeout >= 0) { 0192 if (d->progressDialog) { 0193 return; 0194 } 0195 d->progressDialog = new QProgressDialog(this); 0196 d->progressDialog->setLabelText(tr("Spell checking in progress...", "progress label")); 0197 d->progressDialog->setWindowTitle(tr("Check Spelling", "@title:window")); 0198 d->progressDialog->setModal(true); 0199 d->progressDialog->setAutoClose(false); 0200 d->progressDialog->setAutoReset(false); 0201 // create an 'indefinite' progress box as we currently cannot get progress feedback from 0202 // the speller 0203 d->progressDialog->reset(); 0204 d->progressDialog->setRange(0, 0); 0205 d->progressDialog->setValue(0); 0206 connect(d->progressDialog, &QProgressDialog::canceled, this, &Dialog::slotCancel); 0207 d->progressDialog->setMinimumDuration(d->progressDialogTimeout); 0208 } 0209 } 0210 0211 void Dialog::slotFinished() 0212 { 0213 setProgressDialogVisible(false); 0214 Q_EMIT stop(); 0215 // FIXME: should we emit done here? 0216 #if SONNETUI_BUILD_DEPRECATED_SINCE(5, 65) 0217 Q_EMIT done(d->checker->text()); 0218 #endif 0219 Q_EMIT spellCheckDone(d->checker->text()); 0220 Q_EMIT spellCheckStatus(tr("Spell check stopped.")); 0221 accept(); 0222 } 0223 0224 void Dialog::slotCancel() 0225 { 0226 d->canceled = true; 0227 d->deleteProgressDialog(false); // this method can be called in response to 0228 // pressing 'Cancel' on the dialog 0229 Q_EMIT cancel(); 0230 Q_EMIT spellCheckStatus(tr("Spell check canceled.")); 0231 reject(); 0232 } 0233 0234 QString Dialog::originalBuffer() const 0235 { 0236 return d->originalBuffer; 0237 } 0238 0239 QString Dialog::buffer() const 0240 { 0241 return d->checker->text(); 0242 } 0243 0244 void Dialog::setBuffer(const QString &buf) 0245 { 0246 d->originalBuffer = buf; 0247 // it is possible to change buffer inside slot connected to done() signal 0248 d->restart = true; 0249 } 0250 0251 void Dialog::fillDictionaryComboBox() 0252 { 0253 // Since m_language is changed to DictionaryComboBox most code here is gone, 0254 // So fillDictionaryComboBox() could be removed and code moved to initGui() 0255 // because the call in show() looks obsolete 0256 Speller speller = d->checker->speller(); 0257 d->dictsMap = speller.availableDictionaries(); 0258 0259 updateDictionaryComboBox(); 0260 } 0261 0262 void Dialog::updateDictionaryComboBox() 0263 { 0264 const Speller &speller = d->checker->speller(); 0265 d->ui.m_language->setCurrentByDictionary(speller.language()); 0266 } 0267 0268 void Dialog::updateDialog(const QString &word) 0269 { 0270 d->ui.m_unknownWord->setText(word); 0271 d->ui.m_contextLabel->setText(d->checker->currentContext()); 0272 const QStringList suggs = d->checker->suggest(word); 0273 0274 if (suggs.isEmpty()) { 0275 d->ui.m_replacement->clear(); 0276 } else { 0277 d->ui.m_replacement->setText(suggs.first()); 0278 } 0279 fillSuggestions(suggs); 0280 } 0281 0282 void Dialog::show() 0283 { 0284 d->canceled = false; 0285 fillDictionaryComboBox(); 0286 if (d->originalBuffer.isEmpty()) { 0287 d->checker->start(); 0288 } else { 0289 d->checker->setText(d->originalBuffer); 0290 } 0291 setProgressDialogVisible(true); 0292 } 0293 0294 void Dialog::slotAddWord() 0295 { 0296 setGuiEnabled(false); 0297 setProgressDialogVisible(true); 0298 d->checker->addWordToPersonal(d->currentWord); 0299 d->checker->continueChecking(); 0300 } 0301 0302 void Dialog::slotReplaceWord() 0303 { 0304 setGuiEnabled(false); 0305 setProgressDialogVisible(true); 0306 QString replacementText = d->ui.m_replacement->text(); 0307 Q_EMIT replace(d->currentWord, d->currentPosition, replacementText); 0308 0309 if (d->spellCheckContinuedAfterReplacement) { 0310 d->checker->replace(d->currentPosition, d->currentWord, replacementText); 0311 d->checker->continueChecking(); 0312 } else { 0313 d->checker->stop(); 0314 } 0315 } 0316 0317 void Dialog::slotReplaceAll() 0318 { 0319 setGuiEnabled(false); 0320 setProgressDialogVisible(true); 0321 d->replaceAllMap.insert(d->currentWord, d->ui.m_replacement->text()); 0322 slotReplaceWord(); 0323 } 0324 0325 void Dialog::slotSkip() 0326 { 0327 setGuiEnabled(false); 0328 setProgressDialogVisible(true); 0329 d->checker->continueChecking(); 0330 } 0331 0332 void Dialog::slotSkipAll() 0333 { 0334 setGuiEnabled(false); 0335 setProgressDialogVisible(true); 0336 //### do we want that or should we have a d->ignoreAll list? 0337 Speller speller = d->checker->speller(); 0338 speller.addToPersonal(d->currentWord); 0339 d->checker->setSpeller(speller); 0340 d->checker->continueChecking(); 0341 } 0342 0343 void Dialog::slotSuggest() 0344 { 0345 const QStringList suggs = d->checker->suggest(d->ui.m_replacement->text()); 0346 fillSuggestions(suggs); 0347 } 0348 0349 void Dialog::slotChangeLanguage(const QString &lang) 0350 { 0351 const QString languageCode = d->dictsMap[lang]; 0352 if (!languageCode.isEmpty()) { 0353 d->checker->changeLanguage(languageCode); 0354 slotSuggest(); 0355 Q_EMIT languageChanged(languageCode); 0356 } 0357 } 0358 0359 void Dialog::slotSelectionChanged(const QModelIndex &item) 0360 { 0361 d->ui.m_replacement->setText(item.data().toString()); 0362 } 0363 0364 void Dialog::fillSuggestions(const QStringList &suggs) 0365 { 0366 d->suggestionsModel->setStringList(suggs); 0367 } 0368 0369 void Dialog::slotMisspelling(const QString &word, int start) 0370 { 0371 setGuiEnabled(true); 0372 setProgressDialogVisible(false); 0373 Q_EMIT misspelling(word, start); 0374 // NOTE this is HACK I had to introduce because BackgroundChecker lacks 'virtual' marks on methods 0375 // this dramatically reduces spellchecking time in Lokalize 0376 // as this doesn't fetch suggestions for words that are present in msgid 0377 if (!updatesEnabled()) { 0378 return; 0379 } 0380 0381 d->currentWord = word; 0382 d->currentPosition = start; 0383 if (d->replaceAllMap.contains(word)) { 0384 d->ui.m_replacement->setText(d->replaceAllMap[word]); 0385 slotReplaceWord(); 0386 } else { 0387 updateDialog(word); 0388 } 0389 QDialog::show(); 0390 } 0391 0392 void Dialog::slotDone() 0393 { 0394 d->restart = false; 0395 #if SONNETUI_BUILD_DEPRECATED_SINCE(5, 65) 0396 Q_EMIT done(d->checker->text()); 0397 #endif 0398 Q_EMIT spellCheckDone(d->checker->text()); 0399 if (d->restart) { 0400 updateDictionaryComboBox(); 0401 d->checker->setText(d->originalBuffer); 0402 d->restart = false; 0403 } else { 0404 setProgressDialogVisible(false); 0405 Q_EMIT spellCheckStatus(tr("Spell check complete.")); 0406 accept(); 0407 if (!d->canceled && d->showCompletionMessageBox) { 0408 QMessageBox::information(this, tr("Spell check complete."), tr("Check Spelling", "@title:window")); 0409 } 0410 } 0411 } 0412 } 0413 0414 #include "moc_dialog.cpp"