the spell patch

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/branches/BRANCH_1_3_X@6535 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Jean-Marc Lasgouttes 2003-03-19 13:20:50 +00:00
parent 1339677a5a
commit dd6701c0a1
24 changed files with 442 additions and 499 deletions

View File

@ -21,6 +21,14 @@
* LaTeX.C (run): Fix a bug where the DVI file was not updated due * LaTeX.C (run): Fix a bug where the DVI file was not updated due
to an old format .dep file. to an old format .dep file.
2003-02-17 John Levon <levon@movementarian.org>
* SpellBase.h:
* ispell.h:
* ispell.C:
* pspell.h:
* pspell.C: reworking
2003-02-17 John Levon <levon@movementarian.org> 2003-02-17 John Levon <levon@movementarian.org>
* lyxfunc.C: fix bug 738 - revert behaves sensibly * lyxfunc.C: fix bug 738 - revert behaves sensibly

View File

@ -36,15 +36,9 @@ public:
/// return true if the spellchecker instance still exists /// return true if the spellchecker instance still exists
virtual bool alive() = 0; virtual bool alive() = 0;
/// clean up on messy exit
virtual void cleanUp() = 0;
/// check the given word of the given lang code and return the result /// check the given word of the given lang code and return the result
virtual enum Result check(WordLangTuple const &) = 0; virtual enum Result check(WordLangTuple const &) = 0;
/// finish this spellchecker instance
virtual void close() = 0;
/// insert the given word into the personal dictionary /// insert the given word into the personal dictionary
virtual void insert(WordLangTuple const &) = 0; virtual void insert(WordLangTuple const &) = 0;

View File

@ -1,3 +1,11 @@
2003-02-17 John Levon <levon@movementarian.org>
* ControlDialog.tmpl: do build before setParams for
spellchecker's sake
* ControlSpellchecker.h:
* ControlSpellchecker.C: rework
2003-02-25 Dekel Tsur <dekelts@tau.ac.il> 2003-02-25 Dekel Tsur <dekelts@tau.ac.il>
* ControlDocument.C (apply): Call to setLanguage() after updating bp_; * ControlDocument.C (apply): Call to setLanguage() after updating bp_;

View File

@ -32,17 +32,17 @@ void ControlDialog<Base>::show()
connect(); connect();
if (!dialog_built_) {
view().build();
dialog_built_ = true;
}
setParams(); setParams();
if (emergency_exit_) { if (emergency_exit_) {
hide(); hide();
return; return;
} }
if (!dialog_built_) {
view().build();
dialog_built_ = true;
}
bc().readOnly(bufferIsReadonly()); bc().readOnly(bufferIsReadonly());
view().show(); view().show();

View File

@ -22,6 +22,7 @@
#include "language.h" #include "language.h"
#include "lyxrc.h" #include "lyxrc.h"
#include "lyxtext.h" #include "lyxtext.h"
#include "debug.h"
#include "ispell.h" #include "ispell.h"
#ifdef USE_PSPELL #ifdef USE_PSPELL
@ -32,17 +33,41 @@
#include "BoostFormat.h" #include "BoostFormat.h"
using std::endl;
ControlSpellchecker::ControlSpellchecker(LyXView & lv, Dialogs & d) ControlSpellchecker::ControlSpellchecker(LyXView & lv, Dialogs & d)
: ControlDialogBD(lv, d), : ControlDialogBD(lv, d),
newval_(0.0), oldval_(0), newvalue_(0), count_(0), newval_(0.0), oldval_(0), newvalue_(0), count_(0)
stop_(false), speller_(0) {}
ControlSpellchecker::~ControlSpellchecker()
{} {}
void ControlSpellchecker::setParams() void ControlSpellchecker::setParams()
{ {
if (speller_) lyxerr[Debug::GUI] << "spell setParams" << endl;
startSession();
}
void ControlSpellchecker::clearParams()
{
lyxerr[Debug::GUI] << "spell clearParams" << endl;
endSession();
}
void ControlSpellchecker::startSession()
{
lyxerr[Debug::GUI] << "spell startSession" << endl;
if (speller_.get()) {
lyxerr[Debug::GUI] << "startSession: speller exists" << endl;
speller_.reset(0);
return; return;
}
// create spell object // create spell object
string tmp; string tmp;
@ -51,73 +76,164 @@ void ControlSpellchecker::setParams()
tmp = (lyxrc.isp_use_alt_lang) ? tmp = (lyxrc.isp_use_alt_lang) ?
lyxrc.isp_alt_lang : buffer()->params.language->code(); lyxrc.isp_alt_lang : buffer()->params.language->code();
speller_ = new PSpell(buffer()->params, tmp); speller_.reset(new PSpell(buffer()->params, tmp));
} else { } else {
#endif #endif
tmp = (lyxrc.isp_use_alt_lang) ? tmp = (lyxrc.isp_use_alt_lang) ?
lyxrc.isp_alt_lang : buffer()->params.language->lang(); lyxrc.isp_alt_lang : buffer()->params.language->lang();
speller_ = new ISpell(buffer()->params, tmp); speller_.reset(new ISpell(buffer()->params, tmp));
#ifdef USE_PSPELL #ifdef USE_PSPELL
} }
#endif #endif
if (!speller_->error().empty()) { // reset values to initial
emergency_exit_ = true; newval_ = 0.0;
Alert::alert("The spellchecker has failed", speller_->error()); oldval_ = 0;
clearParams(); newvalue_ = 0;
count_ = 0;
emergency_exit_ = false;
// start off the check
if (speller_->error().empty()) {
check();
return; return;
} }
emergency_exit_ = true;
string message = speller_->error();
if (message.empty())
message = _("The spell-checker could not be started.\n"
"Maybe it is mis-configured.");
Alert::alert(_("The spell-checker has failed"), message);
speller_.reset(0);
}
void ControlSpellchecker::endSession()
{
lyxerr[Debug::GUI] << "spell endSession" << endl;
bufferview()->endOfSpellCheck();
emergency_exit_ = true;
if (!speller_.get()) {
lyxerr[Debug::GUI] << "endSession with no speller" << endl;
return;
}
speller_.reset(0);
} }
void ControlSpellchecker::check() void ControlSpellchecker::check()
{ {
lyxerr[Debug::GUI] << "spell check a word" << endl;
SpellBase::Result res = SpellBase::OK; SpellBase::Result res = SpellBase::OK;
stop_ = false;
// clear any old selection // clear any old selection
LyXText * text = bufferview()->getLyXText(); LyXText * text = bufferview()->getLyXText();
bufferview()->toggleSelection(true); bufferview()->toggleSelection(true);
bufferview()->update(text, BufferView::SELECT); bufferview()->update(text, BufferView::SELECT);
while ((res == SpellBase::OK || res == SpellBase::IGNORE) && !stop_) { while ((res == SpellBase::OK || res == SpellBase::IGNORE)) {
word_ = bufferview()->nextWord(newval_); word_ = bufferview()->nextWord(newval_);
if (word_.word().empty()) { // end of document
clearParams(); if (word_.word().empty())
break; break;
}
++count_; ++count_;
// Update slider if and only if value has changed // Update slider if and only if value has changed
newvalue_ = int(100.0 * newval_); newvalue_ = int(100.0 * newval_);
if (newvalue_!= oldval_) { if (newvalue_!= oldval_) {
lyxerr[Debug::GUI] << "Updating spell progress." << endl;
oldval_ = newvalue_; oldval_ = newvalue_;
// set progress bar // set progress bar
view().partialUpdate(0); view().partialUpdate(SPELL_PROGRESSED);
} }
if (!speller_ || !speller_->alive()) { // speller might be dead ...
clearParams(); if (!checkAlive())
stop();
return; return;
}
res = speller_->check(word_); res = speller_->check(word_);
// ... or it might just be reporting an error
if (!checkAlive())
return;
} }
if (!stop_ && !word_.word().empty()) lyxerr[Debug::GUI] << "Found word \"" << word_.word() << "\"" << endl;
if (!word_.word().empty()) {
bufferview()->selectLastWord(); bufferview()->selectLastWord();
} else {
showSummary();
endSession();
return;
}
// set suggestions // set suggestions
if (res != SpellBase::OK && res != SpellBase::IGNORE) { if (res != SpellBase::OK && res != SpellBase::IGNORE) {
view().partialUpdate(1); lyxerr[Debug::GUI] << "Found a word needing checking." << endl;
view().partialUpdate(SPELL_FOUND_WORD);
} }
} }
bool ControlSpellchecker::checkAlive()
{
if (speller_->alive() && speller_->error().empty())
return true;
string message = speller_->error();
if (message.empty())
message = _("The spell-checker has died for some reason.\n"
"Maybe it has been killed.");
view().hide();
speller_.reset(0);
Alert::alert(_("The spell-checker has failed"), message);
return false;
}
void ControlSpellchecker::showSummary()
{
if (!checkAlive() || count_ == 0) {
view().hide();
return;
}
string message;
#if USE_BOOST_FORMAT
if (count_ != 1) {
boost::format fmter("%1$d words checked.");
fmter % count_;
message += fmter.str();
} else {
message += _("One word checked.");
}
#else
if (count_ != 1) {
message += tostr(count_) + " words checked";
} else {
message = _("One word checked.");
}
#endif
view().hide();
Alert::alert(_("Spell-checking is complete"), message);
}
void ControlSpellchecker::replace(string const & replacement) void ControlSpellchecker::replace(string const & replacement)
{ {
bufferview()->replaceWord(replacement); bufferview()->replaceWord(replacement);
@ -160,64 +276,3 @@ void ControlSpellchecker::ignoreAll()
} }
void ControlSpellchecker::stop()
{
stop_ = true;
bufferview()->endOfSpellCheck();
}
void ControlSpellchecker::clearParams()
{
if (!speller_)
return;
if (speller_->alive()) {
speller_->close();
message_ = string(_("Spellchecking completed!")) + '\n';
#if USE_BOOST_FORMAT
if (count_ != 1) {
boost::format fmter("%1$d words checked.");
fmter % count_;
message_ += fmter.str();
} else {
message_ += _("One word checked.");
}
#else
if (count_ != 1) {
message_ += tostr(count_) + " words checked";
} else {
message_ = _("One word checked.");
}
#endif
} else {
message_ = speller_->error();
speller_->cleanUp();
if (message_.empty())
message_ = _("The spell checker has died for some reason.\n"
"Maybe it has been killed.");
// make sure that the dialog is not launched
emergency_exit_ = true;
Alert::alert("The spellchecker has failed", message_);
}
delete speller_;
bufferview()->endOfSpellCheck();
// show closing message if any words were checked.
if (count_ > 0)
view().partialUpdate(2);
// reset values to initial
newval_ = 0.0;
oldval_ = 0;
newvalue_ = 0;
count_ = 0;
message_.erase();
stop_ = false;
speller_ = 0;
}

View File

@ -21,15 +21,23 @@
#include "WordLangTuple.h" #include "WordLangTuple.h"
#include <boost/scoped_ptr.hpp>
class SpellBase; class SpellBase;
/** A controller for Spellchecker dialogs. /** A controller for Spellchecker dialogs.
*/ */
class ControlSpellchecker : public ControlDialogBD { class ControlSpellchecker : public ControlDialogBD {
public: public:
/// enum State {
SPELL_PROGRESSED, //< update progress bar
SPELL_FOUND_WORD //< found a bad word
};
ControlSpellchecker(LyXView &, Dialogs &); ControlSpellchecker(LyXView &, Dialogs &);
~ControlSpellchecker();
/// replace word with replacement /// replace word with replacement
void replace(string const &); void replace(string const &);
@ -42,10 +50,8 @@ public:
/// ignore all occurances of word /// ignore all occurances of word
void ignoreAll(); void ignoreAll();
/// stop checking
void stop();
/// check text until next misspelled/unknown word /// check text until next misspelled/unknown word
/// returns true when finished
void check(); void check();
/// get suggestion /// get suggestion
@ -57,13 +63,22 @@ public:
/// returns progress value /// returns progress value
int getProgress() const { return oldval_; } int getProgress() const { return oldval_; }
/// returns exit message
string const getMessage() const { return message_; }
/// returns word count /// returns word count
int getCount() const { return count_; } int getCount() const { return count_; }
private: private:
/// give error message is spellchecker dies
bool checkAlive();
/// start a spell-checking session
void startSession();
/// end a spell-checking session
void endSession();
/// show count of checked words at normal exit
void showSummary();
/// set the params before show or update /// set the params before show or update
void setParams(); void setParams();
/// clean-up on hide. /// clean-up on hide.
@ -83,14 +98,8 @@ private:
/// word count /// word count
int count_; int count_;
/// exit message
string message_;
/// set to true to stop checking
bool stop_;
/// The actual spellchecker object /// The actual spellchecker object
SpellBase * speller_; boost::scoped_ptr<SpellBase> speller_;
}; };
#endif // CONTROLSPELLCHECKER_H #endif // CONTROLSPELLCHECKER_H

View File

@ -41,6 +41,14 @@
* ui/QParagraphDialogBase.ui: fix dupe accelerator (bug #918) * ui/QParagraphDialogBase.ui: fix dupe accelerator (bug #918)
2003-02-17 John Levon <levon@movementarian.org>
* ui/QSpellcheckerDialogBase.ui:
* QSpellchecker.h:
* QSpellchecker.C:
* QSpellcheckerDialog.h:
* QSpellcheckerDialog.C: reworking
2003-02-17 John Levon <levon@movementarian.org> 2003-02-17 John Levon <levon@movementarian.org>
* ui/QSpellcheckerModule.ui: capitalization fix * ui/QSpellcheckerModule.ui: capitalization fix

View File

@ -46,22 +46,6 @@ void QSpellchecker::build_dialog()
} }
void QSpellchecker::update_contents()
{
dialog_->wordED->setText("");
dialog_->replaceCO->clear();
dialog_->suggestionsLB->clear();
dialog_->spellcheckPR->setProgress(0);
dialog_->spellcheckPB->setEnabled(true);
dialog_->wordED->setEnabled(false);
dialog_->replaceCO->setEnabled(false);
dialog_->replacePB->setEnabled(false);
dialog_->ignorePB->setEnabled(false);
dialog_->replacePB_3->setEnabled(false);
dialog_->addPB->setEnabled(false);
}
void QSpellchecker::accept() void QSpellchecker::accept()
{ {
controller().ignoreAll(); controller().ignoreAll();
@ -86,35 +70,18 @@ void QSpellchecker::replace()
} }
void QSpellchecker::spellcheck() void QSpellchecker::partialUpdate(int s)
{ {
dialog_->spellcheckPB->setEnabled(false); ControlSpellchecker::State const state =
dialog_->wordED->setEnabled(true); static_cast<ControlSpellchecker::State>(s);
dialog_->replaceCO->setEnabled(true);
dialog_->replacePB->setEnabled(true);
dialog_->ignorePB->setEnabled(true);
dialog_->replacePB_3->setEnabled(true);
dialog_->addPB->setEnabled(true);
controller().check();
}
switch (state) {
void QSpellchecker::stop() case ControlSpellchecker::SPELL_PROGRESSED:
{
controller().stop();
dialog_->spellcheckPB->setEnabled(true);
hide();
}
void QSpellchecker::partialUpdate(int id)
{
switch (id) {
case 0:
dialog_->spellcheckPR->setProgress(controller().getProgress()); dialog_->spellcheckPR->setProgress(controller().getProgress());
break; break;
case 1: { case ControlSpellchecker::SPELL_FOUND_WORD: {
dialog_->wordED->setText(toqstr(controller().getWord())); dialog_->wordED->setText(toqstr(controller().getWord()));
dialog_->suggestionsLB->clear(); dialog_->suggestionsLB->clear();
@ -127,12 +94,5 @@ void QSpellchecker::partialUpdate(int id)
} }
break; break;
case 2:
dialog_->spellcheckPB->setEnabled(true);
hide();
QMessageBox::information(0, qt_("Spellcheck complete"),
toqstr(controller().getMessage()),
qt_("OK"));
break;
} }
} }

View File

@ -34,17 +34,15 @@ public:
/// update from controller /// update from controller
void partialUpdate(int id); void partialUpdate(int id);
private: private:
void stop();
void accept(); void accept();
void add(); void add();
void ignore(); void ignore();
void replace(); void replace();
void spellcheck();
/// Apply changes /// Apply changes
virtual void apply() {} virtual void apply() {}
/// update /// not needed
virtual void update_contents(); virtual void update_contents() {}
/// build the dialog /// build the dialog
virtual void build_dialog(); virtual void build_dialog();
}; };

View File

@ -29,13 +29,7 @@ QSpellcheckerDialog::QSpellcheckerDialog(QSpellchecker * form)
form_(form) form_(form)
{ {
connect(closePB, SIGNAL(clicked()), connect(closePB, SIGNAL(clicked()),
this, SLOT(stop())); form, SLOT(slotClose()));
}
void QSpellcheckerDialog::stop()
{
form_->stop();
} }
@ -45,12 +39,6 @@ void QSpellcheckerDialog::acceptClicked()
} }
void QSpellcheckerDialog::spellcheckClicked()
{
form_->spellcheck();
}
void QSpellcheckerDialog::addClicked() void QSpellcheckerDialog::addClicked()
{ {
form_->add(); form_->add();
@ -98,7 +86,6 @@ void QSpellcheckerDialog::replaceChanged(QString const & str)
void QSpellcheckerDialog::closeEvent(QCloseEvent * e) void QSpellcheckerDialog::closeEvent(QCloseEvent * e)
{ {
form_->stop();
form_->slotWMHide(); form_->slotWMHide();
e->accept(); e->accept();
} }

View File

@ -28,9 +28,7 @@ public slots:
virtual void suggestionChanged(const QString &); virtual void suggestionChanged(const QString &);
protected slots: protected slots:
virtual void stop();
virtual void acceptClicked(); virtual void acceptClicked();
virtual void spellcheckClicked();
virtual void addClicked(); virtual void addClicked();
virtual void replaceClicked(); virtual void replaceClicked();
virtual void ignoreClicked(); virtual void ignoreClicked();

View File

@ -13,7 +13,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>309</width> <width>305</width>
<height>395</height> <height>395</height>
</rect> </rect>
</property> </property>
@ -72,7 +72,7 @@
</property> </property>
<property stdset="1"> <property stdset="1">
<name>text</name> <name>text</name>
<string>A&amp;dd</string> <string>&amp;Add</string>
</property> </property>
<property> <property>
<name>toolTip</name> <name>toolTip</name>
@ -102,7 +102,7 @@
</property> </property>
<property stdset="1"> <property stdset="1">
<name>text</name> <name>text</name>
<string>&amp;Accept</string> <string>I&amp;gnore All</string>
</property> </property>
<property> <property>
<name>toolTip</name> <name>toolTip</name>
@ -218,7 +218,7 @@
</property> </property>
<property stdset="1"> <property stdset="1">
<name>text</name> <name>text</name>
<string>Unknown:</string> <string>Unknown word:</string>
</property> </property>
<property> <property>
<name>buddy</name> <name>buddy</name>
@ -263,21 +263,6 @@
<string>Replace with selected word</string> <string>Replace with selected word</string>
</property> </property>
</widget> </widget>
<widget row="1" column="2" >
<class>QPushButton</class>
<property stdset="1">
<name>name</name>
<cstring>spellcheckPB</cstring>
</property>
<property stdset="1">
<name>text</name>
<string>&amp;Start...</string>
</property>
<property>
<name>toolTip</name>
<string>Start spellcheck</string>
</property>
</widget>
</grid> </grid>
</widget> </widget>
<connections> <connections>
@ -311,12 +296,6 @@
<receiver>QSpellcheckerDialogBase</receiver> <receiver>QSpellcheckerDialogBase</receiver>
<slot>addClicked()</slot> <slot>addClicked()</slot>
</connection> </connection>
<connection>
<sender>spellcheckPB</sender>
<signal>clicked()</signal>
<receiver>QSpellcheckerDialogBase</receiver>
<slot>spellcheckClicked()</slot>
</connection>
<connection> <connection>
<sender>suggestionsLB</sender> <sender>suggestionsLB</sender>
<signal>doubleClicked(QListBoxItem*)</signal> <signal>doubleClicked(QListBoxItem*)</signal>
@ -335,11 +314,9 @@
<slot access="public">optionsClicked()</slot> <slot access="public">optionsClicked()</slot>
<slot access="public">replaceChanged(const QString &amp;)</slot> <slot access="public">replaceChanged(const QString &amp;)</slot>
<slot access="public">replaceClicked()</slot> <slot access="public">replaceClicked()</slot>
<slot access="public">spellcheckClicked()</slot>
<slot access="public">suggestionChanged(const QString &amp;)</slot> <slot access="public">suggestionChanged(const QString &amp;)</slot>
</connections> </connections>
<tabstops> <tabstops>
<tabstop>spellcheckPB</tabstop>
<tabstop>wordED</tabstop> <tabstop>wordED</tabstop>
<tabstop>replaceCO</tabstop> <tabstop>replaceCO</tabstop>
<tabstop>suggestionsLB</tabstop> <tabstop>suggestionsLB</tabstop>

View File

@ -1,3 +1,9 @@
2003-02-17 John Levon <levon@movementarian.org>
* FormSpellchecker.h:
* FormSpellchecker.C:
* forms/form_spellchecker.fd: reworking
2003-02-17 Rob Lahaye <lahaye@snu.ac.kr> 2003-02-17 Rob Lahaye <lahaye@snu.ac.kr>
* FormTexinfo.C: fix full filename lookup when showing * FormTexinfo.C: fix full filename lookup when showing

View File

@ -15,8 +15,8 @@
#endif #endif
#include "xformsBC.h" #include "xformsBC.h"
#include "ControlSpellchecker.h"
#include "FormSpellchecker.h" #include "FormSpellchecker.h"
#include "ControlSpellchecker.h"
#include "forms/form_spellchecker.h" #include "forms/form_spellchecker.h"
#include "forms_gettext.h" #include "forms_gettext.h"
@ -26,11 +26,12 @@
#include FORMS_H_LOCATION #include FORMS_H_LOCATION
using std::endl;
typedef FormCB<ControlSpellchecker, FormDB<FD_spellchecker> > base_class; typedef FormCB<ControlSpellchecker, FormDB<FD_spellchecker> > base_class;
FormSpellchecker::FormSpellchecker() FormSpellchecker::FormSpellchecker()
: base_class(_("Spellchecker")), state_(STOPPED) : base_class(_("Spellchecker"))
{} {}
@ -64,8 +65,6 @@ void FormSpellchecker::build()
tooltips().init(dialog_->browser_suggestions, str); tooltips().init(dialog_->browser_suggestions, str);
// Work-around xforms' bug; enable tooltips for browser widgets. // Work-around xforms' bug; enable tooltips for browser widgets.
setPrehandler(dialog_->browser_suggestions); setPrehandler(dialog_->browser_suggestions);
str = _("Start the spellingchecker.");
tooltips().init(dialog_->button_start, str);
str = _("Replace unknown word."); str = _("Replace unknown word.");
tooltips().init(dialog_->button_replace, str); tooltips().init(dialog_->button_replace, str);
str = _("Ignore unknown word."); str = _("Ignore unknown word.");
@ -79,16 +78,14 @@ void FormSpellchecker::build()
} }
void FormSpellchecker::updateState(State state) void FormSpellchecker::partialUpdate(int s)
{ {
switch (state) { ControlSpellchecker::State const state =
case READY_TO_START: static_cast<ControlSpellchecker::State>(s);
fl_set_slider_value(dialog_->slider_progress, 0.0);
fl_set_object_label(dialog_->slider_progress, "0 %");
break;
case CHECKING: switch (state) {
{
case ControlSpellchecker::SPELL_FOUND_WORD: {
// Set suggestions. // Set suggestions.
string w = controller().getWord(); string w = controller().getWord();
fl_set_input(dialog_->input_replacement, w.c_str()); fl_set_input(dialog_->input_replacement, w.c_str());
@ -101,8 +98,7 @@ void FormSpellchecker::updateState(State state)
// Fall through... // Fall through...
} }
case STARTED: case ControlSpellchecker::SPELL_PROGRESSED: {
{
int const progress = controller().getProgress(); int const progress = controller().getProgress();
if (progress == 0) if (progress == 0)
break; break;
@ -114,68 +110,17 @@ void FormSpellchecker::updateState(State state)
fl_set_slider_bounds(dialog_->slider_progress, 0.0, total); fl_set_slider_bounds(dialog_->slider_progress, 0.0, total);
fl_set_slider_value(dialog_->slider_progress, wordcount); fl_set_slider_value(dialog_->slider_progress, wordcount);
fl_set_object_label(dialog_->slider_progress, label.c_str()); fl_set_object_label(dialog_->slider_progress, label.c_str());
fl_redraw_object(dialog_->slider_progress);
break; break;
} }
case STOPPED:
{
controller().stop();
double const wordcount = controller().getCount();
fl_set_slider_bounds(dialog_->slider_progress, 0.0, wordcount);
fl_set_slider_value(dialog_->slider_progress, wordcount);
fl_set_object_label(dialog_->slider_progress, "100 %");
break;
} }
}
bool const state_change = state_ != state;
state_ = state;
if (!state_change)
return;
bool const running = (state == STARTED || state == CHECKING);
string const label = running ? _("Stop|#S") : _("Start|#S");
fl_set_object_label(dialog_->button_start, idex(label).c_str());
fl_set_button_shortcut(dialog_->button_start, scex(label).c_str(), 1);
fl_redraw_object(dialog_->button_start);
string const tip = running ?
_("Stop the spellingchecker.") :
_("Start the spellingchecker.");
tooltips().init(dialog_->button_start, tip);
setEnabled(dialog_->button_replace, running);
setEnabled(dialog_->button_ignore, running);
setEnabled(dialog_->button_accept, running);
setEnabled(dialog_->button_add, running);
setEnabled(dialog_->browser_suggestions, running);
setEnabled(dialog_->input_replacement, running);
}
void FormSpellchecker::update()
{
// clear input fields
fl_set_input(dialog_->input_replacement, "");
fl_set_object_label(dialog_->text_unknown, "");
fl_clear_browser(dialog_->browser_suggestions);
// reset dialog and buttons into start condition
updateState(READY_TO_START);
} }
ButtonPolicy::SMInput FormSpellchecker::input(FL_OBJECT * ob, long ob_value) ButtonPolicy::SMInput FormSpellchecker::input(FL_OBJECT * ob, long ob_value)
{ {
if (ob == dialog_->button_start) { if (ob == dialog_->button_replace) {
updateState(STARTED);
controller().check();
} else if (ob == dialog_->button_replace) {
string const tmp = getString(dialog_->input_replacement); string const tmp = getString(dialog_->input_replacement);
controller().replace(tmp); controller().replace(tmp);
@ -209,18 +154,3 @@ ButtonPolicy::SMInput FormSpellchecker::input(FL_OBJECT * ob, long ob_value)
return ButtonPolicy::SMI_VALID; return ButtonPolicy::SMI_VALID;
} }
void FormSpellchecker::partialUpdate(int id)
{
switch (id) {
case 1:
// Set suggestions.
updateState(CHECKING);
break;
case 2:
// End of spell checking.
updateState(STOPPED);
break;
}
}

View File

@ -33,26 +33,14 @@ private:
virtual void apply() {} virtual void apply() {}
/// Build the dialog /// Build the dialog
virtual void build(); virtual void build();
/// /// not needed.
virtual void update(); virtual void update() {}
/// set suggestions and exit message /// set suggestions and exit message
virtual void partialUpdate(int); virtual void partialUpdate(int);
/// Filter the inputs /// Filter the inputs
virtual ButtonPolicy::SMInput input(FL_OBJECT *, long); virtual ButtonPolicy::SMInput input(FL_OBJECT *, long);
///
enum State {
READY_TO_START,
STARTED,
CHECKING,
STOPPED
};
///
void updateState(State state);
///
State state_;
}; };
#endif // FORMSPELLCHECKER_H #endif // FORMSPELLCHECKER_H

View File

@ -11,7 +11,7 @@ SnapGrid: 5
Name: form_spellchecker Name: form_spellchecker
Width: 385 Width: 385
Height: 375 Height: 375
Number of Objects: 13 Number of Objects: 12
-------------------- --------------------
class: FL_BOX class: FL_BOX
@ -121,24 +121,6 @@ name: browser_suggestions
callback: C_FormBaseInputCB callback: C_FormBaseInputCB
argument: 0 argument: 0
--------------------
class: FL_BUTTON
type: NORMAL_BUTTON
box: 280 25 100 25
boxtype: FL_UP_BOX
colors: FL_COL1 FL_COL1
alignment: FL_ALIGN_CENTER
style: FL_NORMAL_STYLE
size: FL_NORMAL_SIZE
lcol: FL_BLACK
label: Start|#S
shortcut:
resize: FL_RESIZE_NONE
gravity: FL_NorthEast FL_NorthEast
name: button_start
callback: C_FormBaseInputCB
argument: 0
-------------------- --------------------
class: FL_BUTTON class: FL_BUTTON
type: NORMAL_BUTTON type: NORMAL_BUTTON
@ -185,7 +167,7 @@ alignment: FL_ALIGN_CENTER
style: FL_NORMAL_STYLE style: FL_NORMAL_STYLE
size: FL_NORMAL_SIZE size: FL_NORMAL_SIZE
lcol: FL_BLACK lcol: FL_BLACK
label: Accept|#A label: Ignore All|#g
shortcut: shortcut:
resize: FL_RESIZE_X resize: FL_RESIZE_X
gravity: FL_NorthEast FL_NorthEast gravity: FL_NorthEast FL_NorthEast

View File

@ -13,35 +13,6 @@
#pragma implementation #pragma implementation
#endif #endif
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstdio>
// FIXME: do we need any of this horrible gook ?
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <ctime>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <ctime>
# endif
#endif
#ifdef HAVE_SYS_SELECT_H
# ifdef HAVE_STRINGS_H
// <strings.h> is needed at least on AIX because FD_ZERO uses bzero().
// BUT we cannot include both string.h and strings.h on Irix 6.5 :(
# ifdef _AIX
# include <strings.h>
# endif
# endif
#include <sys/select.h>
#endif
#include "LString.h" #include "LString.h"
#include "lyxrc.h" #include "lyxrc.h"
#include "language.h" #include "language.h"
@ -49,10 +20,13 @@
#include "encoding.h" #include "encoding.h"
#include "ispell.h" #include "ispell.h"
#include "WordLangTuple.h" #include "WordLangTuple.h"
#include "gettext.h"
#include "support/forkedcall.h" #include "support/forkedcall.h"
#include "support/lstrings.h" #include "support/lstrings.h"
#include <sys/select.h>
#ifndef CXX_GLOBAL_CSTD #ifndef CXX_GLOBAL_CSTD
using std::strcpy; using std::strcpy;
using std::strlen; using std::strlen;
@ -61,18 +35,16 @@ using std::strstr;
#endif #endif
using std::endl; using std::endl;
using std::max;
namespace { namespace {
/// pid for the `ispell' process.
pid_t isp_pid = -1;
class LaunchIspell : public ForkedProcess { class LaunchIspell : public ForkedProcess {
public: public:
/// ///
LaunchIspell(BufferParams const & p, string const & l, LaunchIspell(BufferParams const & p, string const & l,
int * in, int * out) int * in, int * out, int * err)
: params(p), lang(l), pipein(in), pipeout(out) {} : params(p), lang(l), pipein(in), pipeout(out), pipeerr(err) {}
/// ///
virtual ForkedProcess * clone() const { virtual ForkedProcess * clone() const {
return new LaunchIspell(*this); return new LaunchIspell(*this);
@ -88,6 +60,7 @@ private:
string const & lang; string const & lang;
int * const pipein; int * const pipein;
int * const pipeout; int * const pipeout;
int * const pipeerr;
}; };
@ -100,7 +73,7 @@ int LaunchIspell::start()
int LaunchIspell::generateChild() int LaunchIspell::generateChild()
{ {
isp_pid = fork(); pid_t isp_pid = fork();
if (isp_pid != 0) { if (isp_pid != 0) {
// failed (-1) or parent process (>0) // failed (-1) or parent process (>0)
@ -110,10 +83,13 @@ int LaunchIspell::generateChild()
// child process // child process
dup2(pipein[0], STDIN_FILENO); dup2(pipein[0], STDIN_FILENO);
dup2(pipeout[1], STDOUT_FILENO); dup2(pipeout[1], STDOUT_FILENO);
::close(pipein[0]); dup2(pipeerr[1], STDERR_FILENO);
::close(pipein[1]); close(pipein[0]);
::close(pipeout[0]); close(pipein[1]);
::close(pipeout[1]); close(pipeout[0]);
close(pipeout[1]);
close(pipeerr[0]);
close(pipeerr[1]);
char * argv[14]; char * argv[14];
int argc = 0; int argc = 0;
@ -204,122 +180,161 @@ int LaunchIspell::generateChild()
ISpell::ISpell(BufferParams const & params, string const & lang) ISpell::ISpell(BufferParams const & params, string const & lang)
: str(0) : in(0), out(0), inerr(0), str(0)
{ {
static char o_buf[BUFSIZ]; // jc: it could be smaller lyxerr[Debug::GUI] << "Created ispell" << endl;
int pipein[2];
int pipeout[2];
isp_pid = -1; // static due to the setvbuf. Ugly.
static char o_buf[BUFSIZ];
// We need to throw an exception not do this
pipein[0] = pipein[1] = pipeout[0] = pipeout[1]
= pipeerr[0] = pipeerr[1] = -1;
if (pipe(pipein) == -1 || pipe(pipeout) == -1) { // This is what happens when goto gets banned.
lyxerr << "LyX: Can't create pipe for spellchecker!" << endl;
setError(); if (pipe(pipein) == -1) {
error_ = _("Can't create pipe for spellchecker.");
return;
}
if (pipe(pipeout) == -1) {
close(pipein[0]);
close(pipein[1]);
error_ = _("Can't create pipe for spellchecker.");
return;
}
if (pipe(pipeerr) == -1) {
close(pipein[0]);
close(pipein[1]);
close(pipeout[0]);
close(pipeout[1]);
error_ = _("Can't create pipe for spellchecker.");
return; return;
} }
if ((out = fdopen(pipein[1], "w")) == 0) { if ((out = fdopen(pipein[1], "w")) == 0) {
lyxerr << "LyX: Can't create stream for pipe for spellchecker!" error_ = _("Can't open pipe for spellchecker.");
<< endl;
setError();
return; return;
} }
if ((in = fdopen(pipeout[0], "r")) == 0) { if ((in = fdopen(pipeout[0], "r")) == 0) {
lyxerr <<"LyX: Can't create stream for pipe for spellchecker!" error_ = _("Can't open pipe for spellchecker.");
<< endl; return;
setError(); }
if ((inerr = fdopen(pipeerr[0], "r")) == 0) {
error_ = _("Can't open pipe for spellchecker.");
return; return;
} }
setvbuf(out, o_buf, _IOLBF, BUFSIZ); setvbuf(out, o_buf, _IOLBF, BUFSIZ);
isp_fd = pipeout[0]; LaunchIspell * li = new LaunchIspell(params, lang, pipein, pipeout, pipeerr);
child_.reset(li);
LaunchIspell childprocess(params, lang, pipein, pipeout); if (li->start() == -1) {
isp_pid = childprocess.start(); error_ = _("Could not create an ispell process.\nYou may not have "
if (isp_pid == -1) { " the right languages installed.");
lyxerr << "LyX: Can't create child process for spellchecker!" child_.reset(0);
<< endl;
setError();
return; return;
} }
setError();
/* Parent process: Read ispells identification message */ /* Parent process: Read ispells identification message */
// Hmm...what are we using this id msg for? Nothing? (Lgb)
// Actually I used it to tell if it's truly Ispell or if it's
// aspell -- (kevinatk@home.com)
// But no code actually used the results for anything useful
// so I removed it again. Perhaps we can remove this code too.
// - jbl
char buf[2048];
fd_set infds;
struct timeval tv;
int retval = 0;
FD_ZERO(&infds);
FD_SET(pipeout[0], &infds);
tv.tv_sec = 15; // fifteen second timeout. Probably too much,
// but it can't really hurt.
tv.tv_usec = 0;
// Configure provides us with macros which are supposed to do bool err_read;
// the right typecast. bool error = select(err_read);
retval = select(SELECT_TYPE_ARG1 (pipeout[0]+1),
SELECT_TYPE_ARG234 (&infds),
0,
0,
SELECT_TYPE_ARG5 (&tv));
if (retval > 0) { if (!error) {
// Ok, do the reading. We don't have to FD_ISSET since if (!err_read) {
// there is only one fd in infds. // Set terse mode (silently accept correct words)
fgets(buf, 2048, in); fputs("!\n", out);
return;
}
fputs("!\n", out); // Set terse mode (silently accept correct words) /* must have read something from stderr */
error_ = buf;
} else if (retval == 0) {
// timeout. Give nice message to user.
lyxerr << "Ispell read timed out, what now?" << endl;
// This probably works but could need some thought
isp_pid = -1;
::close(pipeout[0]);
::close(pipeout[1]);
::close(pipein[0]);
::close(pipein[1]);
isp_fd = -1;
} else { } else {
// Select returned error // select returned error
lyxerr << "Select on ispell returned error, what now?" << endl; error_ = _("The spell process returned an error.\nPerhaps "
"it has been configured wrongly ?");
} }
close(pipein[0]);
close(pipein[1]);
close(pipeout[0]);
close(pipeout[1]);
close(pipeerr[0]);
close(pipeerr[1]);
child_->kill();
child_.reset(0);
} }
ISpell::~ISpell() ISpell::~ISpell()
{ {
delete[] str; lyxerr[Debug::GUI] << "Killing ispell" << endl;
if (in)
fclose(in);
if (inerr)
fclose(inerr);
if (out) {
fputs("#\n", out); // Save personal dictionary
fflush(out);
fclose(out);
}
close(pipein[0]);
close(pipein[1]);
close(pipeout[0]);
close(pipeout[1]);
close(pipeerr[0]);
close(pipeerr[1]);
delete [] str;
} }
void ISpell::setError() bool ISpell::select(bool & err_read)
{ {
if (isp_pid == -1) { fd_set infds;
error_ = struct timeval tv;
"\n\n" int retval = 0;
"The spellcheck-process has died for some reason.\n" FD_ZERO(&infds);
"*One* possible reason could be that you do not have\n" FD_SET(pipeout[0], &infds);
"a dictionary file for the language of this document\n" FD_SET(pipeerr[0], &infds);
"installed.\n" tv.tv_sec = 2;
"Check your spellchecker or set another dictionary\n" tv.tv_usec = 0;
"in the Spellchecker Options menu.\n\n";
} else { retval = ::select(SELECT_TYPE_ARG1 (max(pipeout[0], pipeerr[0]) + 1),
error_ = 0; SELECT_TYPE_ARG234 (&infds),
0,
0,
SELECT_TYPE_ARG5 (&tv));
// error
if (retval <= 0)
return true;
if (FD_ISSET(pipeerr[0], &infds)) {
fgets(buf, BUFSIZ, inerr);
err_read = true;
return false;
} }
fgets(buf, BUFSIZ, in);
err_read = false;
return false;
} }
string const ISpell::nextMiss() string const ISpell::nextMiss()
{ {
// Well, somebody is a sick fuck.
if (str == 0 || *(e+1) == '\0') if (str == 0 || *(e+1) == '\0')
return ""; return "";
char * b = e + 2; char * b = e + 2;
@ -333,13 +348,7 @@ string const ISpell::nextMiss()
bool ISpell::alive() bool ISpell::alive()
{ {
return isp_pid != -1; return child_.get() && child_->running();
}
void ISpell::cleanUp()
{
::fclose(out);
} }
@ -352,8 +361,18 @@ enum ISpell::Result ISpell::check(WordLangTuple const & word)
::fputs(word.word().c_str(), out); ::fputs(word.word().c_str(), out);
::fputc('\n', out); ::fputc('\n', out);
char buf[1024]; bool err_read;
::fgets(buf, 1024, in); bool error = select(err_read);
if (error) {
error_ = _("Could not communicate with the spell-checker program");
return UNKNOWN;
}
if (err_read) {
error_ = buf;
return UNKNOWN;
}
// I think we have to check if ispell is still alive here because // I think we have to check if ispell is still alive here because
// the signal-handler could have disabled blocking on the fd // the signal-handler could have disabled blocking on the fd
@ -400,20 +419,6 @@ enum ISpell::Result ISpell::check(WordLangTuple const & word)
} }
void ISpell::close()
{
// Note: If you decide to optimize this out when it is not
// needed please note that when Aspell is used this command
// is also needed to save the replacement dictionary.
// -- Kevin Atkinson (kevinatk@home.com)
fputs("#\n", out); // Save personal dictionary
fflush(out);
fclose(out);
}
void ISpell::insert(WordLangTuple const & word) void ISpell::insert(WordLangTuple const & word)
{ {
::fputc('*', out); // Insert word in personal dictionary ::fputc('*', out); // Insert word in personal dictionary
@ -432,7 +437,5 @@ void ISpell::accept(WordLangTuple const & word)
string const ISpell::error() string const ISpell::error()
{ {
if (error_) return error_;
return error_;
return "";
} }

View File

@ -10,11 +10,14 @@
#ifndef SP_ISPELL_H #ifndef SP_ISPELL_H
#define SP_ISPELL_H #define SP_ISPELL_H
#include <cstdio>
#include "SpellBase.h" #include "SpellBase.h"
#include <boost/scoped_ptr.hpp>
#include <cstdio>
class BufferParams; class BufferParams;
class ForkedProcess;
/// i/a spell process-based spellchecker /// i/a spell process-based spellchecker
class ISpell : public SpellBase { class ISpell : public SpellBase {
@ -26,15 +29,9 @@ public:
/// return true if the spellchecker instance still exists /// return true if the spellchecker instance still exists
virtual bool alive(); virtual bool alive();
/// clean up on messy exit
virtual void cleanUp();
/// check the given word and return the result /// check the given word and return the result
virtual enum Result check(WordLangTuple const & word); virtual enum Result check(WordLangTuple const & word);
/// finish this spellchecker instance
virtual void close();
/// insert the given word into the personal dictionary /// insert the given word into the personal dictionary
virtual void insert(WordLangTuple const & word); virtual void insert(WordLangTuple const & word);
@ -48,17 +45,29 @@ public:
virtual string const error(); virtual string const error();
private: private:
/// /// read some data. Returns true on an error. Sets err_read
void setError(); /// to true if the data was from stderr.
bool select(bool & err_read);
/// instream to communicate with ispell /// instream to communicate with ispell
FILE * in; FILE * in;
/// outstream to communicate with ispell /// outstream to communicate with ispell
FILE * out; FILE * out;
/// errstream for ispell
FILE * inerr;
/// pipe fds
int pipein[2];
int pipeout[2];
int pipeerr[2];
/// buffer for reading
char buf[BUFSIZ];
/// spell error /// spell error
char const * error_; string error_;
/// the fd of the outgoing pipe
int isp_fd; boost::scoped_ptr<ForkedProcess> child_;
// vileness below ... please FIXME // vileness below ... please FIXME
/// str ??? /// str ???

View File

@ -16,6 +16,7 @@
#ifdef USE_PSPELL #ifdef USE_PSPELL
#include "support/LAssert.h" #include "support/LAssert.h"
#include "debug.h"
#define USE_ORIGINAL_MANAGER_FUNCS 1 #define USE_ORIGINAL_MANAGER_FUNCS 1
// new aspell pspell missing extern "C" // new aspell pspell missing extern "C"
@ -26,38 +27,39 @@ extern "C" {
#include "pspell.h" #include "pspell.h"
#include "WordLangTuple.h" #include "WordLangTuple.h"
using std::endl;
PSpell::PSpell(BufferParams const &, string const & lang) PSpell::PSpell(BufferParams const &, string const & lang)
: els(0), spell_error_object(0) : els(0), spell_error_object(0)
{ {
addManager(lang); addManager(lang);
lyxerr[Debug::GUI] << "created pspell" << endl;
} }
PSpell::~PSpell() PSpell::~PSpell()
{ {
cleanUp(); lyxerr[Debug::GUI] << "killed pspell" << endl;
close();
if (spell_error_object) {
delete_pspell_can_have_error(spell_error_object);
spell_error_object = 0;
}
if (els) if (els)
delete_pspell_string_emulation(els); delete_pspell_string_emulation(els);
Managers::iterator it = managers_.begin(); Managers::iterator it = managers_.begin();
Managers::iterator end = managers_.end(); Managers::iterator end = managers_.end();
for (; it != end; ++it) { for (; it != end; ++it) {
pspell_manager_save_all_word_lists(it->second.manager);
delete_pspell_manager(it->second.manager); delete_pspell_manager(it->second.manager);
delete_pspell_config(it->second.config); delete_pspell_config(it->second.config);
} }
} }
void PSpell::cleanUp()
{
if (spell_error_object) {
delete_pspell_can_have_error(spell_error_object);
spell_error_object = 0;
}
}
void PSpell::addManager(string const & lang) void PSpell::addManager(string const & lang)
{ {
PspellConfig * config = new_pspell_config(); PspellConfig * config = new_pspell_config();
@ -112,17 +114,6 @@ enum PSpell::Result PSpell::check(WordLangTuple const & word)
} }
void PSpell::close()
{
Managers::iterator it = managers_.begin();
Managers::iterator end = managers_.end();
for (; it != end; ++it) {
pspell_manager_save_all_word_lists(it->second.manager);
}
}
void PSpell::insert(WordLangTuple const & word) void PSpell::insert(WordLangTuple const & word)
{ {
Managers::iterator it = managers_.find(word.lang_code()); Managers::iterator it = managers_.find(word.lang_code());

View File

@ -37,15 +37,9 @@ public:
*/ */
virtual bool alive() { return true; } virtual bool alive() { return true; }
/// clean up on messy exit
virtual void cleanUp();
/// check the given word and return the result /// check the given word and return the result
virtual enum Result check(WordLangTuple const &); virtual enum Result check(WordLangTuple const &);
/// finish this spellchecker instance
virtual void close();
/// insert the given word into the personal dictionary /// insert the given word into the personal dictionary
virtual void insert(WordLangTuple const &); virtual void insert(WordLangTuple const &);

View File

@ -1,3 +1,8 @@
2003-02-17 John Levon <levon@movementarian.org>
* forkedcall.h:
* forkedcall.C: add running()
2003-03-06 Alfredo Braunstein <abraunst@libero.it> 2003-03-06 Alfredo Braunstein <abraunst@libero.it>
* forkedcontr.C (timer): reworked the loop to allow running changes * forkedcontr.C (timer): reworked the loop to allow running changes

View File

@ -147,6 +147,24 @@ int ForkedProcess::runNonBlocking()
return retval_; return retval_;
} }
bool ForkedProcess::running() const
{
if (!pid())
return false;
// Un-UNIX like, but we don't have much use for
// knowing if a zombie exists, so just reap it first.
int waitstatus;
waitpid(pid(), &waitstatus, WNOHANG);
// Racy of course, but it will do.
if (::kill(pid(), 0) && errno == ESRCH)
return false;
return true;
}
void ForkedProcess::kill(int tol) void ForkedProcess::kill(int tol)
{ {
lyxerr << "ForkedProcess::kill(" << tol << ')' << endl; lyxerr << "ForkedProcess::kill(" << tol << ')' << endl;

View File

@ -98,6 +98,9 @@ public:
/// Returns the identifying command (for display in the GUI perhaps). /// Returns the identifying command (for display in the GUI perhaps).
string const & command() const { return command_; } string const & command() const { return command_; }
/// is the process running ?
bool running() const;
/** Kill child prematurely. /** Kill child prematurely.
* First, a SIGHUP is sent to the child. * First, a SIGHUP is sent to the child.
* If that does not end the child process within "tolerance" * If that does not end the child process within "tolerance"

View File

@ -22,6 +22,18 @@ What's new
** Updates ** Updates
- The spell-checking system has been over-hauled, including the following
changes :
o start spell-checking immediately on pressing F7
o xform's broken start/stop button removed
o better spell progress feedback
o long hang on ispell error changed
o infinite hang on ispell error fixed
o reports ispell errors back to the user
o the personal dictionaries are always correctly written out
o "Accept" button became "Ignore All" for clarity
- updated russian interface localisation - updated russian interface localisation
** Bug fixes ** Bug fixes