git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@20797 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
André Pönitz 2007-10-06 19:51:03 +00:00
parent 86af8a2811
commit e093f9212b
7 changed files with 509 additions and 622 deletions

View File

@ -194,7 +194,7 @@ void Dialogs::checkStatus()
for(; it != end; ++it) { for(; it != end; ++it) {
Dialog * const dialog = it->second.get(); Dialog * const dialog = it->second.get();
if (dialog->isVisibleView()) if (dialog && dialog->isVisibleView())
dialog->checkStatus(); dialog->checkStatus();
} }
} }

View File

@ -1,348 +0,0 @@
/**
* \file ControlSpellchecker.cpp
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Edwin Leuven
*
* Full author contact details are available in file CREDITS.
*/
#include <config.h>
#include "ControlSpellchecker.h"
#include "Buffer.h"
#include "BufferParams.h"
#include "BufferView.h"
#include "Cursor.h"
#include "CutAndPaste.h"
#include "debug.h"
#include "gettext.h"
#include "Language.h"
#include "LyXRC.h"
#include "Paragraph.h"
#if defined(USE_ASPELL)
# include "ASpell_local.h"
#elif defined(USE_PSPELL)
# include "PSpell.h"
#endif
#if defined(USE_ISPELL)
# include "ISpell.h"
#else
# include "SpellBase.h"
#endif
#include "support/textutils.h"
#include "support/convert.h"
#include "support/docstring.h"
#include "frontends/alert.h"
// FIXME: those two headers are needed because of the
// WorkArea::redraw() call below.
#include "frontends/LyXView.h"
#include "frontends/WorkArea.h"
using std::advance;
using std::distance;
using std::endl;
using std::string;
namespace lyx {
using support::bformat;
using support::contains;
namespace frontend {
ControlSpellchecker::ControlSpellchecker(Dialog & parent)
: Controller(parent), exitEarly_(false),
oldval_(0), newvalue_(0), count_(0), speller_(0)
{
}
ControlSpellchecker::~ControlSpellchecker()
{
delete speller_;
}
static SpellBase * getSpeller(BufferParams const & bp)
{
string lang = (lyxrc.isp_use_alt_lang)
? lyxrc.isp_alt_lang
: bp.language->code();
#if defined(USE_ASPELL)
if (lyxrc.use_spell_lib)
return new ASpell(bp, lang);
#elif defined(USE_PSPELL)
if (lyxrc.use_spell_lib)
return new PSpell(bp, lang);
#endif
#if defined(USE_ISPELL)
lang = lyxrc.isp_use_alt_lang ?
lyxrc.isp_alt_lang : bp.language->lang();
return new ISpell(bp, lang);
#else
return new SpellBase;
#endif
}
bool ControlSpellchecker::initialiseParams(std::string const &)
{
LYXERR(Debug::GUI) << "Spellchecker::initialiseParams" << endl;
speller_ = getSpeller(buffer().params());
if (!speller_)
return false;
// reset values to initial
oldval_ = 0;
newvalue_ = 0;
count_ = 0;
bool const success = speller_->error().empty();
if (!success) {
Alert::error(_("Spellchecker error"),
_("The spellchecker could not be started\n")
+ speller_->error());
delete speller_;
speller_ = 0;
}
return success;
}
void ControlSpellchecker::clearParams()
{
LYXERR(Debug::GUI) << "Spellchecker::clearParams" << endl;
delete speller_;
speller_ = 0;
}
static bool isLetter(DocIterator const & dit)
{
return dit.inTexted()
&& dit.inset().allowSpellCheck()
&& dit.pos() != dit.lastpos()
&& (dit.paragraph().isLetter(dit.pos())
// We want to pass the ' and escape chars to ispell
|| contains(from_utf8(lyxrc.isp_esc_chars + '\''),
dit.paragraph().getChar(dit.pos())))
&& !dit.paragraph().isDeleted(dit.pos());
}
static WordLangTuple nextWord(Cursor & cur, ptrdiff_t & progress)
{
BufferParams const & bp = cur.bv().buffer().params();
bool inword = false;
bool ignoreword = false;
cur.resetAnchor();
docstring word;
string lang_code;
while (cur.depth()) {
if (isLetter(cur)) {
if (!inword) {
inword = true;
ignoreword = false;
cur.resetAnchor();
word.clear();
lang_code = cur.paragraph().getFontSettings(bp, cur.pos()).language()->code();
}
// Insets like optional hyphens and ligature
// break are part of a word.
if (!cur.paragraph().isInset(cur.pos())) {
Paragraph::value_type const c =
cur.paragraph().getChar(cur.pos());
word += c;
if (isDigit(c))
ignoreword = true;
}
} else { // !isLetter(cur)
if (inword)
if (!word.empty() && !ignoreword) {
cur.setSelection();
return WordLangTuple(word, lang_code);
}
inword = false;
}
cur.forwardPos();
++progress;
}
return WordLangTuple(docstring(), string());
}
void ControlSpellchecker::check()
{
LYXERR(Debug::GUI) << "Check the spelling of a word" << endl;
SpellBase::Result res = SpellBase::OK;
Cursor cur = bufferview()->cursor();
while (cur && cur.pos() && isLetter(cur))
cur.backwardPos();
ptrdiff_t start = 0;
ptrdiff_t total = 0;
DocIterator it = DocIterator(buffer().inset());
for (start = 0; it != cur; it.forwardPos())
++start;
for (total = start; it; it.forwardPos())
++total;
exitEarly_ = false;
while (res == SpellBase::OK || res == SpellBase::IGNORED_WORD) {
word_ = nextWord(cur, start);
// end of document
if (getWord().empty()) {
showSummary();
exitEarly_ = true;
return;
}
++count_;
// Update slider if and only if value has changed
float progress = total ? float(start)/total : 1;
newvalue_ = int(100.0 * progress);
if (newvalue_!= oldval_) {
LYXERR(Debug::GUI) << "Updating spell progress." << endl;
oldval_ = newvalue_;
// set progress bar
dialog().partialUpdateView(SPELL_PROGRESSED);
}
// speller might be dead ...
if (!checkAlive())
return;
res = speller_->check(word_);
// ... or it might just be reporting an error
if (!checkAlive())
return;
}
LYXERR(Debug::GUI) << "Found word \"" << to_utf8(getWord()) << "\"" << endl;
int const size = cur.selEnd().pos() - cur.selBegin().pos();
cur.pos() -= size;
bufferview()->putSelectionAt(cur, size, false);
// FIXME: if we used a lfun like in find/replace, dispatch would do
// that for us
bufferview()->update();
// FIXME: this Controller is very badly designed...
lyxview().currentWorkArea()->redraw();
// set suggestions
if (res != SpellBase::OK && res != SpellBase::IGNORED_WORD) {
LYXERR(Debug::GUI) << "Found a word needing checking." << endl;
dialog().partialUpdateView(SPELL_FOUND_WORD);
}
}
bool ControlSpellchecker::checkAlive()
{
if (speller_->alive() && speller_->error().empty())
return true;
docstring message;
if (speller_->error().empty())
message = _("The spellchecker has died for some reason.\n"
"Maybe it has been killed.");
else
message = _("The spellchecker has failed.\n") + speller_->error();
dialog().slotClose();
Alert::error(_("The spellchecker has failed"), message);
return false;
}
void ControlSpellchecker::showSummary()
{
if (!checkAlive() || count_ == 0) {
dialog().slotClose();
return;
}
docstring message;
if (count_ != 1)
message = bformat(_("%1$d words checked."), count_);
else
message = _("One word checked.");
dialog().slotClose();
Alert::information(_("Spelling check completed"), message);
}
void ControlSpellchecker::replace(docstring const & replacement)
{
LYXERR(Debug::GUI) << "ControlSpellchecker::replace("
<< to_utf8(replacement) << ")" << std::endl;
cap::replaceSelectionWithString(bufferview()->cursor(), replacement, true);
buffer().markDirty();
// If we used an LFUN, we would not need that
bufferview()->update();
// fix up the count
--count_;
check();
}
void ControlSpellchecker::replaceAll(docstring const & replacement)
{
// TODO: add to list
replace(replacement);
}
void ControlSpellchecker::insert()
{
speller_->insert(word_);
check();
}
docstring const ControlSpellchecker::getSuggestion() const
{
return speller_->nextMiss();
}
docstring const ControlSpellchecker::getWord() const
{
return word_.word();
}
void ControlSpellchecker::ignoreAll()
{
speller_->accept(word_);
check();
}
} // namespace frontend
} // namespace lyx

View File

@ -1,104 +0,0 @@
// -*- C++ -*-
/**
* \file ControlSpellchecker.h
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Edwin Leuven
*
* Full author contact details are available in file CREDITS.
*/
#ifndef CONTROLSPELLCHECKER_H
#define CONTROLSPELLCHECKER_H
#include "Dialog.h"
#include "WordLangTuple.h"
namespace lyx {
class SpellBase;
namespace frontend {
/** A controller for Spellchecker dialogs.
*/
class ControlSpellchecker : public Controller
{
public:
enum State {
SPELL_PROGRESSED, //< update progress bar
SPELL_FOUND_WORD //< found a bad word
};
ControlSpellchecker(Dialog &);
~ControlSpellchecker();
///
virtual bool initialiseParams(std::string const & data);
///
virtual void clearParams();
/// Not needed here
virtual void dispatchParams() {}
///
virtual bool isBufferDependent() const { return true; }
///
virtual bool exitEarly() const { return exitEarly_; }
/// replace word with replacement
void replace(docstring const &);
/// replace all occurances of word
void replaceAll(docstring const &);
/// insert word in personal dictionary
void insert();
/// ignore all occurances of word
void ignoreAll();
/// check text until next misspelled/unknown word
/// returns true when finished
void check();
/// get suggestion
docstring const getSuggestion() const;
/// get word
docstring const getWord() const;
/// returns progress value
int getProgress() const { return oldval_; }
/// returns word count
int getCount() const { return count_; }
private:
/// give error message is spellchecker dies
bool checkAlive();
/// show count of checked words at normal exit
void showSummary();
private:
/// set to true when spellchecking is finished
bool exitEarly_;
/// current word being checked and lang code
WordLangTuple word_;
/// values for progress
int oldval_;
int newvalue_;
/// word count
int count_;
/// The actual spellchecker object
SpellBase * speller_;
};
} // namespace frontend
} // namespace lyx
#endif // CONTROLSPELLCHECKER_H

View File

@ -20,7 +20,6 @@ SOURCEFILES = \
ControlPrint.cpp \ ControlPrint.cpp \
ControlSearch.cpp \ ControlSearch.cpp \
ControlSendto.cpp \ ControlSendto.cpp \
ControlSpellchecker.cpp \
ControlThesaurus.cpp \ ControlThesaurus.cpp \
ControlToc.cpp \ ControlToc.cpp \
frontend_helpers.cpp frontend_helpers.cpp
@ -38,7 +37,6 @@ HEADERFILES = \
ControlPrint.h \ ControlPrint.h \
ControlSearch.h \ ControlSearch.h \
ControlSendto.h \ ControlSendto.h \
ControlSpellchecker.h \
ControlThesaurus.h \ ControlThesaurus.h \
ControlToc.h \ ControlToc.h \
frontend_helpers.h frontend_helpers.h

View File

@ -30,7 +30,6 @@
#include "GuiSearch.h" #include "GuiSearch.h"
#include "GuiSendto.h" #include "GuiSendto.h"
#include "GuiShowFile.h" #include "GuiShowFile.h"
#include "GuiSpellchecker.h"
#include "GuiToc.h" #include "GuiToc.h"
#include "GuiView.h" #include "GuiView.h"
#include "TocWidget.h" #include "TocWidget.h"
@ -141,110 +140,107 @@ Dialog * Dialogs::build(string const & name)
{ {
BOOST_ASSERT(isValidName(name)); BOOST_ASSERT(isValidName(name));
Dialog * dialog = 0;
GuiViewBase & guiview = static_cast<GuiViewBase &>(lyxview_); GuiViewBase & guiview = static_cast<GuiViewBase &>(lyxview_);
if (name == "aboutlyx") { if (name == "aboutlyx")
dialog = createGuiAbout(lyxview_); return createGuiAbout(lyxview_);
} else if (name == "bibitem") { if (name == "bibitem")
dialog = new GuiBibitemDialog(lyxview_); return new GuiBibitemDialog(lyxview_);
} else if (name == "bibtex") { if (name == "bibtex")
dialog = createGuiBibtex(lyxview_); return createGuiBibtex(lyxview_);
} else if (name == "box") { if (name == "box")
dialog = createGuiBox(lyxview_); return createGuiBox(lyxview_);
} else if (name == "branch") { if (name == "branch")
dialog = createGuiBranch(lyxview_); return createGuiBranch(lyxview_);
} else if (name == "changes") { if (name == "changes")
dialog = createGuiChanges(lyxview_); return createGuiChanges(lyxview_);
} else if (name == "character") { if (name == "character")
dialog = createGuiCharacter(lyxview_); return createGuiCharacter(lyxview_);
} else if (name == "citation") { if (name == "citation")
dialog = createGuiCitation(lyxview_); return createGuiCitation(lyxview_);
} else if (name == "document") { if (name == "document")
dialog = new GuiDocumentDialog(lyxview_); return new GuiDocumentDialog(lyxview_);
} else if (name == "embedding") { if (name == "embedding")
dialog = createGuiEmbeddedFiles(lyxview_); return createGuiEmbeddedFiles(lyxview_);
} else if (name == "errorlist") { if (name == "errorlist")
dialog = createGuiErrorList(lyxview_); return createGuiErrorList(lyxview_);
} else if (name == "ert") { if (name == "ert")
dialog = createGuiERT(lyxview_); return createGuiERT(lyxview_);
} else if (name == "external") { if (name == "external")
dialog = new GuiExternalDialog(lyxview_); return new GuiExternalDialog(lyxview_);
} else if (name == "file") { if (name == "file")
dialog = createGuiShowFile(lyxview_); return createGuiShowFile(lyxview_);
} else if (name == "findreplace") { if (name == "findreplace")
dialog = new GuiSearchDialog(lyxview_); return new GuiSearchDialog(lyxview_);
} else if (name == "float") { if (name == "float")
dialog = createGuiFloat(lyxview_); return createGuiFloat(lyxview_);
} else if (name == "graphics") { if (name == "graphics")
dialog = new GuiGraphicsDialog(lyxview_); return new GuiGraphicsDialog(lyxview_);
} else if (name == "include") { if (name == "include")
dialog = createGuiInclude(lyxview_); return createGuiInclude(lyxview_);
} else if (name == "index") { if (name == "index")
dialog = new GuiIndexDialog(lyxview_); return new GuiIndexDialog(lyxview_);
} else if (name == "nomenclature") { if (name == "nomenclature")
dialog = new GuiNomenclDialog(lyxview_); return new GuiNomenclDialog(lyxview_);
} else if (name == "label") { if (name == "label")
dialog = new GuiLabelDialog(lyxview_); return new GuiLabelDialog(lyxview_);
} else if (name == "log") { if (name == "log")
dialog = createGuiLog(lyxview_); return createGuiLog(lyxview_);
} else if (name == "view-source") { if (name == "view-source")
dialog = createGuiViewSource(lyxview_); return createGuiViewSource(lyxview_);
} else if (name == "mathdelimiter") { if (name == "mathdelimiter")
dialog = new GuiDelimiterDialog(lyxview_); return new GuiDelimiterDialog(lyxview_);
} else if (name == "mathmatrix") { if (name == "mathmatrix")
dialog = new GuiMathMatrixDialog(lyxview_); return new GuiMathMatrixDialog(lyxview_);
} else if (name == "note") { if (name == "note")
dialog = createGuiNote(lyxview_); return createGuiNote(lyxview_);
} else if (name == "paragraph") { if (name == "paragraph") {
#ifdef USE_DOCK_WIDGET #ifdef USE_DOCK_WIDGET
DockView<ControlParagraph, GuiParagraph> * dv = return new DockView<ControlParagraph, GuiParagraph>(guiview, name,
new DockView<ControlParagraph, GuiParagraph>(guiview, name, Qt::TopDockWidgetArea);
Qt::TopDockWidgetArea);
#else #else
DialogView<ControlParagraph, GuiParagraph> * dv = return new DialogView<ControlParagraph, GuiParagraph>(guiview, name);
new DialogView<ControlParagraph, GuiParagraph>(guiview, name);
#endif #endif
dialog = dv; }
} else if (name == "prefs") { if (name == "prefs")
dialog = new GuiPrefsDialog(lyxview_); return new GuiPrefsDialog(lyxview_);
} else if (name == "print") { if (name == "print")
dialog = new GuiPrintDialog(lyxview_); return new GuiPrintDialog(lyxview_);
} else if (name == "ref") { if (name == "ref")
dialog = createGuiRef(lyxview_); return createGuiRef(lyxview_);
} else if (name == "sendto") { if (name == "sendto")
dialog = new GuiSendtoDialog(lyxview_); return new GuiSendtoDialog(lyxview_);
} else if (name == "spellchecker") { if (name == "spellchecker")
dialog = new GuiSpellcheckerDialog(lyxview_); return createGuiSpellchecker(lyxview_);
} else if (name == "tabular") { if (name == "tabular")
dialog = createGuiTabular(lyxview_); return createGuiTabular(lyxview_);
} else if (name == "tabularcreate") { if (name == "tabularcreate")
dialog = createGuiTabularCreate(lyxview_); return createGuiTabularCreate(lyxview_);
} else if (name == "texinfo") { if (name == "texinfo")
dialog = createGuiTexInfo(lyxview_); return createGuiTexInfo(lyxview_);
#ifdef HAVE_LIBAIKSAURUS #ifdef HAVE_LIBAIKSAURUS
} else if (name == "thesaurus") { if (name == "thesaurus")
dialog = new GuiThesaurusDialog(lyxview_); return new GuiThesaurusDialog(lyxview_);
#endif #endif
} else if (name == "toc") { if (name == "toc") {
#ifdef Q_WS_MACX #ifdef Q_WS_MACX
// On Mac show as a drawer at the right // On Mac show as a drawer at the right
dialog = new DockView<GuiToc, TocWidget>(guiview, name, return new DockView<GuiToc, TocWidget>(guiview, name,
Qt::RightDockWidgetArea, Qt::Drawer); Qt::RightDockWidgetArea, Qt::Drawer);
#else #else
dialog = new DockView<GuiToc, TocWidget>(guiview, name); return new DockView<GuiToc, TocWidget>(guiview, name);
#endif #endif
} else if (name == "url") {
dialog = new GuiURLDialog(lyxview_);
} else if (name == "vspace") {
dialog = createGuiVSpace(lyxview_);
} else if (name == "wrap") {
dialog = createGuiWrap(lyxview_);
} else if (name == "listings") {
dialog = createGuiListings(lyxview_);
} }
if (name == "url")
return new GuiURLDialog(lyxview_);
if (name == "vspace")
return createGuiVSpace(lyxview_);
if (name == "wrap")
return createGuiWrap(lyxview_);
if (name == "listings")
return createGuiListings(lyxview_);
return dialog; return 0;
} }

View File

@ -4,6 +4,7 @@
* Licence details can be found in the file COPYING. * Licence details can be found in the file COPYING.
* *
* \author John Levon * \author John Levon
* \author Edwin Leuven
* *
* Full author contact details are available in file CREDITS. * Full author contact details are available in file CREDITS.
*/ */
@ -12,9 +13,23 @@
#include "GuiSpellchecker.h" #include "GuiSpellchecker.h"
#include "ControlSpellchecker.h"
#include "qt_helpers.h" #include "qt_helpers.h"
#include "Buffer.h"
#include "BufferParams.h"
#include "BufferView.h"
#include "Cursor.h"
#include "CutAndPaste.h"
#include "debug.h"
#include "gettext.h"
#include "Language.h"
#include "LyXRC.h"
#include "Paragraph.h"
#include "support/textutils.h"
#include "support/convert.h"
#include "support/docstring.h"
#include <QProgressBar> #include <QProgressBar>
#include <QLineEdit> #include <QLineEdit>
#include <QPushButton> #include <QPushButton>
@ -25,33 +40,54 @@
#include <QTextCharFormat> #include <QTextCharFormat>
#include <QTextDocument> #include <QTextDocument>
#if defined(USE_ASPELL)
# include "ASpell_local.h"
#elif defined(USE_PSPELL)
# include "PSpell.h"
#endif
#if defined(USE_ISPELL)
# include "ISpell.h"
#else
# include "SpellBase.h"
#endif
#include "frontends/alert.h"
// FIXME: those two headers are needed because of the
// WorkArea::redraw() call below.
#include "frontends/LyXView.h"
#include "frontends/WorkArea.h"
using std::advance;
using std::distance;
using std::endl;
using std::string; using std::string;
namespace lyx { namespace lyx {
namespace frontend { namespace frontend {
GuiSpellcheckerDialog::GuiSpellcheckerDialog(LyXView & lv) using support::bformat;
: GuiDialog(lv, "spellchecker") using support::contains;
GuiSpellchecker::GuiSpellchecker(LyXView & lv)
: GuiDialog(lv, "spellchecker"), Controller(this), exitEarly_(false),
oldval_(0), newvalue_(0), count_(0), speller_(0)
{ {
setupUi(this); setupUi(this);
setViewTitle(_("Spellchecker")); setViewTitle(_("Spellchecker"));
setController(new ControlSpellchecker(*this)); setController(this, false);
connect(closePB, SIGNAL(clicked()), this, SLOT(slotClose())); connect(closePB, SIGNAL(clicked()), this, SLOT(slotClose()));
connect(replacePB, SIGNAL(clicked()), this, SLOT(replaceClicked()));
connect(ignorePB, SIGNAL(clicked()), this, SLOT(ignoreClicked()));
connect(replacePB_3, SIGNAL(clicked()), this, SLOT(acceptClicked()));
connect(addPB, SIGNAL(clicked()), this, SLOT(addClicked()));
connect(replaceCO, SIGNAL(highlighted(const QString &)), connect(replaceCO, SIGNAL(highlighted(QString)),
this, SLOT(replaceChanged(const QString &))); this, SLOT(replaceChanged(QString)));
connect(replacePB, SIGNAL(clicked()),
this, SLOT(replaceClicked()));
connect(ignorePB, SIGNAL(clicked()),
this, SLOT(ignoreClicked()));
connect(replacePB_3, SIGNAL(clicked()),
this, SLOT(acceptClicked()));
connect(addPB, SIGNAL(clicked()),
this, SLOT(addClicked()));
connect(suggestionsLW, SIGNAL(itemDoubleClicked(QListWidgetItem*)), connect(suggestionsLW, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
this, SLOT(replaceClicked() ) ); this, SLOT(replaceClicked()));
connect(suggestionsLW, SIGNAL(itemClicked(QListWidgetItem*)), connect(suggestionsLW, SIGNAL(itemClicked(QListWidgetItem*)),
this, SLOT(suggestionChanged(QListWidgetItem*))); this, SLOT(suggestionChanged(QListWidgetItem*)));
@ -62,37 +98,13 @@ GuiSpellcheckerDialog::GuiSpellcheckerDialog(LyXView & lv)
} }
ControlSpellchecker & GuiSpellcheckerDialog::controller() GuiSpellchecker::~GuiSpellchecker()
{ {
return static_cast<ControlSpellchecker &>(GuiDialog::controller()); delete speller_;
} }
void GuiSpellcheckerDialog::acceptClicked() void GuiSpellchecker::suggestionChanged(QListWidgetItem * item)
{
accept();
}
void GuiSpellcheckerDialog::addClicked()
{
add();
}
void GuiSpellcheckerDialog::replaceClicked()
{
replace();
}
void GuiSpellcheckerDialog::ignoreClicked()
{
ignore();
}
void GuiSpellcheckerDialog::suggestionChanged(QListWidgetItem * item)
{ {
if (replaceCO->count() != 0) if (replaceCO->count() != 0)
replaceCO->setItemText(0, item->text()); replaceCO->setItemText(0, item->text());
@ -103,12 +115,12 @@ void GuiSpellcheckerDialog::suggestionChanged(QListWidgetItem * item)
} }
void GuiSpellcheckerDialog::replaceChanged(const QString & str) void GuiSpellchecker::replaceChanged(const QString & str)
{ {
if (suggestionsLW->currentItem()->text() == str) if (suggestionsLW->currentItem()->text() == str)
return; return;
for (int i = 0; i < suggestionsLW->count(); ++i) { for (int i = 0; i != suggestionsLW->count(); ++i) {
if (suggestionsLW->item(i)->text() == str) { if (suggestionsLW->item(i)->text() == str) {
suggestionsLW->setCurrentRow(i); suggestionsLW->setCurrentRow(i);
break; break;
@ -117,64 +129,64 @@ void GuiSpellcheckerDialog::replaceChanged(const QString & str)
} }
void GuiSpellcheckerDialog::closeEvent(QCloseEvent * e) void GuiSpellchecker::closeEvent(QCloseEvent * e)
{ {
slotClose(); slotClose();
GuiDialog::closeEvent(e); GuiDialog::closeEvent(e);
} }
void GuiSpellcheckerDialog::reject() void GuiSpellchecker::reject()
{ {
slotClose(); slotClose();
QDialog::reject(); QDialog::reject();
} }
void GuiSpellcheckerDialog::updateContents() void GuiSpellchecker::updateContents()
{ {
if (isVisibleView() || controller().exitEarly()) if (isVisibleView() || exitEarly())
controller().check(); check();
} }
void GuiSpellcheckerDialog::accept() void GuiSpellchecker::accept()
{ {
controller().ignoreAll(); ignoreAll();
} }
void GuiSpellcheckerDialog::add() void GuiSpellchecker::add()
{ {
controller().insert(); insert();
} }
void GuiSpellcheckerDialog::ignore() void GuiSpellchecker::ignore()
{ {
controller().check(); check();
} }
void GuiSpellcheckerDialog::replace() void GuiSpellchecker::replace()
{ {
controller().replace(qstring_to_ucs4(replaceCO->currentText())); replace(qstring_to_ucs4(replaceCO->currentText()));
} }
void GuiSpellcheckerDialog::partialUpdate(int state) void GuiSpellchecker::partialUpdate(int state)
{ {
switch (state) { switch (state) {
case ControlSpellchecker::SPELL_PROGRESSED: case SPELL_PROGRESSED:
spellcheckPR->setValue(controller().getProgress()); spellcheckPR->setValue(getProgress());
break; break;
case ControlSpellchecker::SPELL_FOUND_WORD: { case SPELL_FOUND_WORD: {
wordED->setText(toqstr(controller().getWord())); wordED->setText(toqstr(getWord()));
suggestionsLW->clear(); suggestionsLW->clear();
docstring w; docstring w;
while (!(w = controller().getSuggestion()).empty()) while (!(w = getSuggestion()).empty())
suggestionsLW->addItem(toqstr(w)); suggestionsLW->addItem(toqstr(w));
if (suggestionsLW->count() == 0) if (suggestionsLW->count() == 0)
@ -188,6 +200,283 @@ void GuiSpellcheckerDialog::partialUpdate(int state)
} }
} }
static SpellBase * getSpeller(BufferParams const & bp)
{
string lang = (lyxrc.isp_use_alt_lang)
? lyxrc.isp_alt_lang
: bp.language->code();
#if defined(USE_ASPELL)
if (lyxrc.use_spell_lib)
return new ASpell(bp, lang);
#elif defined(USE_PSPELL)
if (lyxrc.use_spell_lib)
return new PSpell(bp, lang);
#endif
#if defined(USE_ISPELL)
lang = lyxrc.isp_use_alt_lang ?
lyxrc.isp_alt_lang : bp.language->lang();
return new ISpell(bp, lang);
#else
return new SpellBase;
#endif
}
bool GuiSpellchecker::initialiseParams(std::string const &)
{
LYXERR(Debug::GUI) << "Spellchecker::initialiseParams" << endl;
speller_ = getSpeller(buffer().params());
if (!speller_)
return false;
// reset values to initial
oldval_ = 0;
newvalue_ = 0;
count_ = 0;
bool const success = speller_->error().empty();
if (!success) {
Alert::error(_("Spellchecker error"),
_("The spellchecker could not be started\n")
+ speller_->error());
delete speller_;
speller_ = 0;
}
return success;
}
void GuiSpellchecker::clearParams()
{
LYXERR(Debug::GUI) << "Spellchecker::clearParams" << endl;
delete speller_;
speller_ = 0;
}
static bool isLetter(DocIterator const & dit)
{
return dit.inTexted()
&& dit.inset().allowSpellCheck()
&& dit.pos() != dit.lastpos()
&& (dit.paragraph().isLetter(dit.pos())
// We want to pass the ' and escape chars to ispell
|| contains(from_utf8(lyxrc.isp_esc_chars + '\''),
dit.paragraph().getChar(dit.pos())))
&& !dit.paragraph().isDeleted(dit.pos());
}
static WordLangTuple nextWord(Cursor & cur, ptrdiff_t & progress)
{
BufferParams const & bp = cur.bv().buffer().params();
bool inword = false;
bool ignoreword = false;
cur.resetAnchor();
docstring word;
string lang_code;
while (cur.depth()) {
if (isLetter(cur)) {
if (!inword) {
inword = true;
ignoreword = false;
cur.resetAnchor();
word.clear();
lang_code = cur.paragraph().getFontSettings(bp, cur.pos()).language()->code();
}
// Insets like optional hyphens and ligature
// break are part of a word.
if (!cur.paragraph().isInset(cur.pos())) {
Paragraph::value_type const c =
cur.paragraph().getChar(cur.pos());
word += c;
if (isDigit(c))
ignoreword = true;
}
} else { // !isLetter(cur)
if (inword)
if (!word.empty() && !ignoreword) {
cur.setSelection();
return WordLangTuple(word, lang_code);
}
inword = false;
}
cur.forwardPos();
++progress;
}
return WordLangTuple(docstring(), string());
}
void GuiSpellchecker::check()
{
LYXERR(Debug::GUI) << "Check the spelling of a word" << endl;
SpellBase::Result res = SpellBase::OK;
Cursor cur = bufferview()->cursor();
while (cur && cur.pos() && isLetter(cur))
cur.backwardPos();
ptrdiff_t start = 0;
ptrdiff_t total = 0;
DocIterator it = DocIterator(buffer().inset());
for (start = 0; it != cur; it.forwardPos())
++start;
for (total = start; it; it.forwardPos())
++total;
exitEarly_ = false;
while (res == SpellBase::OK || res == SpellBase::IGNORED_WORD) {
word_ = nextWord(cur, start);
// end of document
if (getWord().empty()) {
showSummary();
exitEarly_ = true;
return;
}
++count_;
// Update slider if and only if value has changed
float progress = total ? float(start)/total : 1;
newvalue_ = int(100.0 * progress);
if (newvalue_!= oldval_) {
LYXERR(Debug::GUI) << "Updating spell progress." << endl;
oldval_ = newvalue_;
// set progress bar
dialog().partialUpdateView(SPELL_PROGRESSED);
}
// speller might be dead ...
if (!checkAlive())
return;
res = speller_->check(word_);
// ... or it might just be reporting an error
if (!checkAlive())
return;
}
LYXERR(Debug::GUI) << "Found word \"" << to_utf8(getWord()) << "\"" << endl;
int const size = cur.selEnd().pos() - cur.selBegin().pos();
cur.pos() -= size;
bufferview()->putSelectionAt(cur, size, false);
// FIXME: if we used a lfun like in find/replace, dispatch would do
// that for us
bufferview()->update();
// FIXME: this Controller is very badly designed...
lyxview().currentWorkArea()->redraw();
// set suggestions
if (res != SpellBase::OK && res != SpellBase::IGNORED_WORD) {
LYXERR(Debug::GUI) << "Found a word needing checking." << endl;
dialog().partialUpdateView(SPELL_FOUND_WORD);
}
}
bool GuiSpellchecker::checkAlive()
{
if (speller_->alive() && speller_->error().empty())
return true;
docstring message;
if (speller_->error().empty())
message = _("The spellchecker has died for some reason.\n"
"Maybe it has been killed.");
else
message = _("The spellchecker has failed.\n") + speller_->error();
dialog().slotClose();
Alert::error(_("The spellchecker has failed"), message);
return false;
}
void GuiSpellchecker::showSummary()
{
if (!checkAlive() || count_ == 0) {
dialog().slotClose();
return;
}
docstring message;
if (count_ != 1)
message = bformat(_("%1$d words checked."), count_);
else
message = _("One word checked.");
dialog().slotClose();
Alert::information(_("Spelling check completed"), message);
}
void GuiSpellchecker::replace(docstring const & replacement)
{
LYXERR(Debug::GUI) << "GuiSpellchecker::replace("
<< to_utf8(replacement) << ")" << std::endl;
cap::replaceSelectionWithString(bufferview()->cursor(), replacement, true);
buffer().markDirty();
// If we used an LFUN, we would not need that
bufferview()->update();
// fix up the count
--count_;
check();
}
void GuiSpellchecker::replaceAll(docstring const & replacement)
{
// TODO: add to list
replace(replacement);
}
void GuiSpellchecker::insert()
{
speller_->insert(word_);
check();
}
docstring GuiSpellchecker::getSuggestion() const
{
return speller_->nextMiss();
}
docstring GuiSpellchecker::getWord() const
{
return word_.word();
}
void GuiSpellchecker::ignoreAll()
{
speller_->accept(word_);
check();
}
Dialog * createGuiSpellchecker(LyXView & lv) { return new GuiSpellchecker(lv); }
} // namespace frontend } // namespace frontend
} // namespace lyx } // namespace lyx

View File

@ -6,6 +6,7 @@
* *
* \author John Levon * \author John Levon
* \author Kalle Dalheimer * \author Kalle Dalheimer
* \author Edwin Leuven
* *
* Full author contact details are available in file CREDITS. * Full author contact details are available in file CREDITS.
*/ */
@ -14,29 +15,35 @@
#define GUISPELLCHECKER_H #define GUISPELLCHECKER_H
#include "GuiDialog.h" #include "GuiDialog.h"
#include "ControlSpellchecker.h"
#include "ui_SpellcheckerUi.h" #include "ui_SpellcheckerUi.h"
#include "Dialog.h"
#include "WordLangTuple.h"
class QListWidgetItem; class QListWidgetItem;
namespace lyx { namespace lyx {
class SpellBase;
namespace frontend { namespace frontend {
class GuiSpellcheckerDialog : public GuiDialog, public Ui::SpellcheckerUi class GuiSpellchecker
: public GuiDialog, public Ui::SpellcheckerUi, public Controller
{ {
Q_OBJECT Q_OBJECT
public: public:
GuiSpellcheckerDialog(LyXView & lv); GuiSpellchecker(LyXView & lv);
~GuiSpellchecker();
public Q_SLOTS: public Q_SLOTS:
void suggestionChanged(QListWidgetItem *); void suggestionChanged(QListWidgetItem *);
private Q_SLOTS: private Q_SLOTS:
void acceptClicked(); void accept();
void addClicked(); void add();
void replaceClicked(); void ignore();
void ignoreClicked(); void replace();
void replaceChanged(const QString &); void replaceChanged(const QString &);
void reject(); void reject();
@ -46,14 +53,63 @@ private:
/// update from controller /// update from controller
void partialUpdate(int id); void partialUpdate(int id);
/// parent controller /// parent controller
ControlSpellchecker & controller(); Controller & controller() { return *this; }
////
void accept();
void add();
void ignore();
void replace();
/// ///
void updateContents(); void updateContents();
///
enum State {
SPELL_PROGRESSED, //< update progress bar
SPELL_FOUND_WORD //< found a bad word
};
///
bool initialiseParams(std::string const & data);
///
void clearParams();
/// Not needed here
void dispatchParams() {}
///
bool isBufferDependent() const { return true; }
///
bool exitEarly() const { return exitEarly_; }
/// replace word with replacement
void replace(docstring const &);
/// replace all occurances of word
void replaceAll(docstring const &);
/// insert word in personal dictionary
void insert();
/// ignore all occurances of word
void ignoreAll();
/// check text until next misspelled/unknown word
/// returns true when finished
void check();
/// get suggestion
docstring getSuggestion() const;
/// get word
docstring getWord() const;
/// returns progress value
int getProgress() const { return oldval_; }
/// returns word count
int getCount() const { return count_; }
/// give error message is spellchecker dies
bool checkAlive();
/// show count of checked words at normal exit
void showSummary();
/// set to true when spellchecking is finished
bool exitEarly_;
/// current word being checked and lang code
WordLangTuple word_;
/// values for progress
int oldval_;
int newvalue_;
/// word count
int count_;
/// The actual spellchecker object
SpellBase * speller_;
}; };
} // namespace frontend } // namespace frontend