2 * \file GuiSpellchecker.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * \author Abdelrazak Younes
10 * Full author contact details are available in file CREDITS.
15 #include "GuiSpellchecker.h"
16 #include "GuiApplication.h"
18 #include "qt_helpers.h"
20 #include "ui_SpellcheckerUi.h"
23 #include "BufferParams.h"
24 #include "BufferView.h"
25 #include "buffer_funcs.h"
27 #include "CutAndPaste.h"
28 #include "FuncRequest.h"
33 #include "Paragraph.h"
34 #include "WordLangTuple.h"
36 #include "support/debug.h"
37 #include "support/docstring.h"
38 #include "support/docstring_list.h"
39 #include "support/ExceptionMessage.h"
40 #include "support/gettext.h"
41 #include "support/lstrings.h"
42 #include "support/textutils.h"
44 #include <QListWidgetItem>
47 #include "SpellChecker.h"
49 #include "frontends/alert.h"
52 using namespace lyx::support;
58 struct GuiSpellchecker::Private
60 Private() : progress_(0), count_(0), stuck_(false) {}
61 Ui::SpellcheckerUi ui;
62 /// current word being checked and lang code
64 /// values for progress
69 /// flag for last move forward success
74 GuiSpellchecker::GuiSpellchecker(GuiView & lv)
75 : DockView(lv, "spellchecker", qt_("Spellchecker"),
76 Qt::RightDockWidgetArea), d(new GuiSpellchecker::Private)
80 connect(d->ui.suggestionsLW, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
81 this, SLOT(on_replacePB_clicked()));
84 QAbstractItemModel * language_model = guiApp->languageModel();
85 // FIXME: it would be nice if sorting was enabled/disabled via a checkbox.
86 language_model->sort(0);
87 d->ui.languageCO->setModel(language_model);
88 d->ui.languageCO->setModelColumn(1);
90 d->ui.wordED->setReadOnly(true);
92 d->ui.suggestionsLW->installEventFilter(this);
96 GuiSpellchecker::~GuiSpellchecker()
102 void GuiSpellchecker::on_closePB_clicked()
108 bool GuiSpellchecker::eventFilter(QObject *obj, QEvent *event)
110 if (obj == d->ui.suggestionsLW && event->type() == QEvent::KeyPress) {
111 QKeyEvent *e = static_cast<QKeyEvent *> (event);
112 if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
113 if (d->ui.suggestionsLW->currentItem()) {
114 on_suggestionsLW_itemClicked(d->ui.suggestionsLW->currentItem());
115 on_replacePB_clicked();
118 } else if (e->key() == Qt::Key_Right) {
119 if (d->ui.suggestionsLW->currentItem())
120 on_suggestionsLW_itemClicked(d->ui.suggestionsLW->currentItem());
124 // standard event processing
125 return QWidget::eventFilter(obj, event);
129 void GuiSpellchecker::on_suggestionsLW_itemClicked(QListWidgetItem * item)
131 if (d->ui.replaceCO->count() != 0)
132 d->ui.replaceCO->setItemText(0, item->text());
134 d->ui.replaceCO->addItem(item->text());
136 d->ui.replaceCO->setCurrentIndex(0);
140 void GuiSpellchecker::on_replaceCO_highlighted(const QString & str)
142 QListWidget * lw = d->ui.suggestionsLW;
143 if (lw->currentItem() && lw->currentItem()->text() == str)
146 for (int i = 0; i != lw->count(); ++i) {
147 if (lw->item(i)->text() == str) {
148 lw->setCurrentRow(i);
155 void GuiSpellchecker::updateView()
157 if (hasFocus() && d->count_ == 0)
162 void GuiSpellchecker::forward()
164 DocIterator from = bufferview()->cursor();
166 dispatch(FuncRequest(LFUN_ESCAPE));
167 dispatch(FuncRequest(LFUN_CHAR_FORWARD));
168 d->stuck_ = from == bufferview()->cursor();
172 void GuiSpellchecker::on_languageCO_activated(int index)
175 fromqstr(d->ui.languageCO->itemData(index).toString());
176 if (!d->word_.lang() || d->word_.lang()->lang() == lang)
179 dispatch(FuncRequest(LFUN_LANGUAGE, lang));
184 void GuiSpellchecker::on_ignoreAllPB_clicked()
186 /// replace all occurrences of word
187 if (d->word_.lang() && !d->word_.word().empty())
188 theSpellChecker()->accept(d->word_);
194 void GuiSpellchecker::on_addPB_clicked()
196 /// insert word in personal dictionary
197 theSpellChecker()->insert(d->word_);
203 void GuiSpellchecker::on_ignorePB_clicked()
210 void GuiSpellchecker::on_findNextPB_clicked()
212 docstring const data = find2string(
213 qstring_to_ucs4(d->ui.wordED->text()),
215 dispatch(FuncRequest(LFUN_WORD_FIND, data));
219 void GuiSpellchecker::on_replacePB_clicked()
221 docstring const replacement = qstring_to_ucs4(d->ui.replaceCO->currentText());
222 docstring const data = replace2string(
223 replacement, qstring_to_ucs4(d->ui.wordED->text()),
224 true, true, false, false);
226 LYXERR(Debug::GUI, "Replace (" << replacement << ")");
227 dispatch(FuncRequest(LFUN_WORD_REPLACE, data));
233 void GuiSpellchecker::on_replaceAllPB_clicked()
235 docstring const data = replace2string(
236 qstring_to_ucs4(d->ui.replaceCO->currentText()),
237 qstring_to_ucs4(d->ui.wordED->text()),
238 true, true, true, true);
239 dispatch(FuncRequest(LFUN_WORD_REPLACE, data));
241 check(); // continue spellchecking
245 void GuiSpellchecker::updateSuggestions(docstring_list & words)
247 QString const suggestion = toqstr(d->word_.word());
248 d->ui.wordED->setText(suggestion);
249 QListWidget * lw = d->ui.suggestionsLW;
253 on_suggestionsLW_itemClicked(new QListWidgetItem(suggestion));
256 for (size_t i = 0; i != words.size(); ++i)
257 lw->addItem(toqstr(words[i]));
259 on_suggestionsLW_itemClicked(lw->item(0));
260 lw->setCurrentRow(0);
264 bool GuiSpellchecker::initialiseParams(string const &)
266 LYXERR(Debug::GUI, "Spellchecker::initialiseParams");
268 if (!theSpellChecker())
271 DocIterator const begin = doc_iterator_begin(&buffer());
272 Cursor const & cur = bufferview()->cursor();
273 d->progress_ = countWords(begin, cur);
274 d->total_ = d->progress_ + countWords(cur, doc_iterator_end(&buffer()));
280 void GuiSpellchecker::check()
282 LYXERR(Debug::GUI, "Check the spelling of the words starting at " << d->progress_);
284 // last move forward failed
291 DocIterator from = bufferview()->cursor();
293 WordLangTuple word_lang;
294 docstring_list suggestions;
298 progress = buffer().spellCheck(from, to, word_lang, suggestions);
299 } catch (ExceptionMessage const & message) {
300 if (message.type_ == WarningException) {
301 Alert::warning(message.title_, message.details_);
307 d->count_ += progress;
308 d->progress_ += progress;
309 LYXERR(Debug::GUI, "Found word \"" << word_lang.word() << "\"" <<
310 " at position " << d->progress_);
313 if (from == doc_iterator_end(&buffer())) {
318 // current misspelled word has to be counted too.
325 d->word_ = word_lang;
327 int const progress_bar = d->total_
328 ? int(100.0 * float(d->progress_)/d->total_) : 100;
329 LYXERR(Debug::GUI, "Updating spell progress." <<
330 " Now we have " << progress_bar << " percent.");
332 d->ui.spellcheckPR->setValue(progress_bar);
334 updateSuggestions(suggestions);
336 int const pos = d->ui.languageCO->findData(toqstr(word_lang.lang()->lang()));
338 d->ui.languageCO->setCurrentIndex(pos);
341 // If we used a LFUN, dispatch would do all of this for us
342 int const size = to.pos() - from.pos();
343 BufferView * bv = const_cast<BufferView *>(bufferview());
344 bv->putSelectionAt(from, size, false);
345 bv->processUpdateFlags(Update::Force | Update::FitCursor);
350 void GuiSpellchecker::showSummary()
352 if (d->count_ == 0) {
359 message = bformat(_("%1$d words checked."), d->count_);
361 message = _("One word checked.");
364 Alert::information(_("Spelling check completed"), message);
368 Dialog * createGuiSpellchecker(GuiView & lv)
370 GuiSpellchecker * gui = new GuiSpellchecker(lv);
372 gui->setFloating(true);
378 } // namespace frontend
381 #include "moc_GuiSpellchecker.cpp"