2 * \file GuiSpellchecker.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
14 #include "GuiSpellchecker.h"
16 #include "qt_helpers.h"
19 #include "BufferParams.h"
20 #include "BufferView.h"
21 #include "buffer_funcs.h"
23 #include "CutAndPaste.h"
27 #include "Paragraph.h"
29 #include "support/debug.h"
30 #include "support/docstring.h"
31 #include "support/docstring_list.h"
32 #include "support/ExceptionMessage.h"
33 #include "support/gettext.h"
34 #include "support/lstrings.h"
35 #include "support/textutils.h"
37 #include <QListWidgetItem>
39 #include "SpellChecker.h"
41 #include "frontends/alert.h"
44 using namespace lyx::support;
50 GuiSpellchecker::GuiSpellchecker(GuiView & lv)
51 : GuiDialog(lv, "spellchecker", qt_("Spellchecker")), exitEarly_(false),
52 progress_(0), count_(0)
56 connect(closePB, SIGNAL(clicked()), this, SLOT(slotClose()));
57 connect(replacePB, SIGNAL(clicked()), this, SLOT(replace()));
58 connect(ignorePB, SIGNAL(clicked()), this, SLOT(ignore()));
59 connect(replacePB_3, SIGNAL(clicked()), this, SLOT(accept()));
60 connect(addPB, SIGNAL(clicked()), this, SLOT(add()));
62 connect(replaceCO, SIGNAL(highlighted(QString)),
63 this, SLOT(replaceChanged(QString)));
64 connect(suggestionsLW, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
65 this, SLOT(replace()));
66 connect(suggestionsLW, SIGNAL(itemClicked(QListWidgetItem*)),
67 this, SLOT(suggestionChanged(QListWidgetItem*)));
69 wordED->setReadOnly(true);
71 bc().setPolicy(ButtonPolicy::NoRepeatedApplyReadOnlyPolicy);
72 bc().setCancel(closePB);
76 void GuiSpellchecker::suggestionChanged(QListWidgetItem * item)
78 if (replaceCO->count() != 0)
79 replaceCO->setItemText(0, item->text());
81 replaceCO->addItem(item->text());
83 replaceCO->setCurrentIndex(0);
87 void GuiSpellchecker::replaceChanged(const QString & str)
89 if (suggestionsLW->currentItem()
90 && suggestionsLW->currentItem()->text() == str)
93 for (int i = 0; i != suggestionsLW->count(); ++i) {
94 if (suggestionsLW->item(i)->text() == str) {
95 suggestionsLW->setCurrentRow(i);
102 void GuiSpellchecker::reject()
109 void GuiSpellchecker::updateContents()
111 // The clauses below are needed because the spellchecker
112 // has many flaws (see bugs 1950, 2218).
113 // Basically, we have to distinguish the case where a
114 // spellcheck has already been performed for the whole
115 // document (exitEarly() == true, isVisible() == false)
116 // from the rest (exitEarly() == false, isVisible() == true).
117 // FIXME: rewrite the whole beast!
118 static bool check_after_early_exit;
120 // a spellcheck has already been performed,
122 check_after_early_exit = true;
124 else if (isVisible()) {
125 // the above check triggers a second update,
126 // and isVisible() is true then. Prevent a
127 // second check that skips the first word
128 if (check_after_early_exit)
129 // don't check, but reset the bool.
130 // business as usual after this.
131 check_after_early_exit = false;
133 // perform spellcheck (default case)
139 void GuiSpellchecker::accept()
141 theSpellChecker()->accept(word_);
146 void GuiSpellchecker::add()
148 theSpellChecker()->insert(word_);
153 void GuiSpellchecker::ignore()
159 void GuiSpellchecker::replace()
161 replace(qstring_to_ucs4(replaceCO->currentText()));
165 void GuiSpellchecker::updateSuggestions(docstring_list & words)
167 wordED->setText(toqstr(word_.word()));
168 suggestionsLW->clear();
170 if (words.empty() == 0) {
171 suggestionChanged(new QListWidgetItem(wordED->text()));
174 for (size_t i = 0; i != words.size(); ++i)
175 suggestionsLW->addItem(toqstr(words[i]));
177 suggestionChanged(suggestionsLW->item(0));
178 suggestionsLW->setCurrentRow(0);
182 bool GuiSpellchecker::initialiseParams(string const &)
184 LYXERR(Debug::GUI, "Spellchecker::initialiseParams");
186 if (!theSpellChecker())
189 DocIterator const begin = doc_iterator_begin(&buffer());
190 Cursor const & cur = bufferview()->cursor();
191 progress_ = countWords(begin, cur);
192 total_ = progress_ + countWords(cur, doc_iterator_end(&buffer()));
198 void GuiSpellchecker::clearParams()
200 LYXERR(Debug::GUI, "Spellchecker::clearParams");
204 void GuiSpellchecker::check()
206 LYXERR(Debug::GUI, "Check the spelling of a word");
208 DocIterator from = bufferview()->cursor();
209 while (from && from.pos() && isLetter(from))
213 WordLangTuple word_lang;
214 docstring_list suggestions;
218 progress = buffer().spellCheck(from, to, word_lang, suggestions);
219 } catch (ExceptionMessage const & message) {
220 if (message.type_ == WarningException) {
221 Alert::warning(message.title_, message.details_);
227 LYXERR(Debug::GUI, "Found word \"" << word_lang.word() << "\"");
229 progress_ += progress;
239 int const progress_bar = total_
240 ? int(100.0 * float(progress_)/total_) : 100;
241 LYXERR(Debug::GUI, "Updating spell progress.");
243 spellcheckPR->setValue(progress_bar);
245 updateSuggestions(suggestions);
247 // FIXME: if we used a lfun like in find/replace, dispatch would do
249 int const size = to.pos() - from.pos();
250 BufferView * bv = const_cast<BufferView *>(bufferview());
251 bv->putSelectionAt(from, size, false);
255 void GuiSpellchecker::showSummary()
264 message = bformat(_("%1$d words checked."), count_);
266 message = _("One word checked.");
269 Alert::information(_("Spelling check completed"), message);
273 void GuiSpellchecker::replace(docstring const & replacement)
275 LYXERR(Debug::GUI, "GuiSpellchecker::replace("
276 << to_utf8(replacement) << ")");
277 BufferView * bv = const_cast<BufferView *>(bufferview());
278 cap::replaceSelectionWithString(bv->cursor(), replacement, true);
279 bv->buffer().markDirty();
280 // If we used an LFUN, we would not need that
281 bv->processUpdateFlags(Update::Force | Update::FitCursor);
288 void GuiSpellchecker::replaceAll(docstring const & replacement)
291 replace(replacement);
295 Dialog * createGuiSpellchecker(GuiView & lv) { return new GuiSpellchecker(lv); }
297 } // namespace frontend
300 #include "moc_GuiSpellchecker.cpp"