From 47c34c3afa59b030a4578c82fea46d926e0c0c66 Mon Sep 17 00:00:00 2001 From: John Levon Date: Mon, 17 Feb 2003 18:40:04 +0000 Subject: [PATCH] spell rework. revert fix git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@6191 a592a061-630c-0410-9148-cb99ea01b6c8 --- src/ChangeLog | 10 + src/SpellBase.h | 6 - src/frontends/controllers/ChangeLog | 9 + src/frontends/controllers/ControlDialog.tmpl | 10 +- .../controllers/ControlSpellchecker.C | 219 ++++++++----- .../controllers/ControlSpellchecker.h | 36 ++- src/frontends/qt2/ChangeLog | 10 + src/frontends/qt2/QSpellchecker.C | 52 +--- src/frontends/qt2/QSpellchecker.h | 6 +- src/frontends/qt2/QSpellcheckerDialog.C | 15 +- src/frontends/qt2/QSpellcheckerDialog.h | 2 - src/frontends/qt2/README | 2 + .../qt2/ui/QSpellcheckerDialogBase.ui | 29 +- src/frontends/xforms/ChangeLog | 6 + src/frontends/xforms/FormSpellchecker.C | 94 +----- src/frontends/xforms/FormSpellchecker.h | 16 +- .../xforms/forms/form_spellchecker.fd | 22 +- src/ispell.C | 287 +++++++++--------- src/ispell.h | 35 ++- src/lyxfunc.C | 3 + src/pspell.C | 35 +-- src/pspell.h | 6 - src/support/ChangeLog | 5 + src/support/forkedcall.C | 18 ++ src/support/forkedcall.h | 3 + 25 files changed, 438 insertions(+), 498 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index acf1324d92..0799db9ba7 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,13 @@ +2003-02-17 John Levon + + * SpellBase.h: + * ispell.h: + * ispell.C: + * pspell.h: + * pspell.C: reworking. Especially in ispell, a large + number of clean ups and bug fixes. + + * lyxfunc.C: fix revert to behave sensibly 2003-02-17 André Pönitz diff --git a/src/SpellBase.h b/src/SpellBase.h index 1ed6a56f7a..83410d3521 100644 --- a/src/SpellBase.h +++ b/src/SpellBase.h @@ -36,15 +36,9 @@ public: /// return true if the spellchecker instance still exists 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 virtual enum Result check(WordLangTuple const &) = 0; - /// finish this spellchecker instance - virtual void close() = 0; - /// insert the given word into the personal dictionary virtual void insert(WordLangTuple const &) = 0; diff --git a/src/frontends/controllers/ChangeLog b/src/frontends/controllers/ChangeLog index 4a6e9ca0e3..ab544de3b2 100644 --- a/src/frontends/controllers/ChangeLog +++ b/src/frontends/controllers/ChangeLog @@ -1,3 +1,12 @@ +2003-02-17 John Levon + + * ControlDialog.tmpl: build dialog before calling setParams + for the benefit of ControlSpellchecker + + * ControlSpellchecker.h: + * ControlSpellchecker.C: rework. Lots of bugs fixed and + cleanups. + 2003-02-14 Juergen Spitzmueller * ControlBibtex.[Ch]: new function getBibFiles() (provides list diff --git a/src/frontends/controllers/ControlDialog.tmpl b/src/frontends/controllers/ControlDialog.tmpl index 0ba97195d3..abcd863bbc 100644 --- a/src/frontends/controllers/ControlDialog.tmpl +++ b/src/frontends/controllers/ControlDialog.tmpl @@ -32,17 +32,17 @@ void ControlDialog::show() connect(); + if (!dialog_built_) { + view().build(); + dialog_built_ = true; + } + setParams(); if (emergency_exit_) { hide(); return; } - if (!dialog_built_) { - view().build(); - dialog_built_ = true; - } - bc().readOnly(bufferIsReadonly()); view().show(); diff --git a/src/frontends/controllers/ControlSpellchecker.C b/src/frontends/controllers/ControlSpellchecker.C index a9b9b4d6df..d485acbbdd 100644 --- a/src/frontends/controllers/ControlSpellchecker.C +++ b/src/frontends/controllers/ControlSpellchecker.C @@ -19,6 +19,7 @@ #include "language.h" #include "lyxrc.h" #include "lyxtext.h" +#include "debug.h" #include "ispell.h" #ifdef USE_PSPELL @@ -29,17 +30,41 @@ #include "BoostFormat.h" +using std::endl; + ControlSpellchecker::ControlSpellchecker(LyXView & lv, Dialogs & d) : ControlDialogBD(lv, d), - newval_(0.0), oldval_(0), newvalue_(0), count_(0), - stop_(false), speller_(0) + newval_(0.0), oldval_(0), newvalue_(0), count_(0) +{} + + +ControlSpellchecker::~ControlSpellchecker() {} 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; + } // create spell object string tmp; @@ -48,70 +73,161 @@ void ControlSpellchecker::setParams() tmp = (lyxrc.isp_use_alt_lang) ? lyxrc.isp_alt_lang : buffer()->params.language->code(); - speller_ = new PSpell(buffer()->params, tmp); + speller_.reset(new PSpell(buffer()->params, tmp)); } else { #endif tmp = (lyxrc.isp_use_alt_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 } #endif - if (!speller_->error().empty()) { - emergency_exit_ = true; - Alert::alert("The spellchecker has failed", speller_->error()); - clearParams(); + // reset values to initial + newval_ = 0.0; + oldval_ = 0; + newvalue_ = 0; + count_ = 0; + emergency_exit_ = false; + + // start off the check + if (speller_->error().empty()) { + check(); 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() { + lyxerr[Debug::GUI] << "spell check a word" << endl; + SpellBase::Result res = SpellBase::OK; - stop_ = false; // clear any old selection LyXText * text = bufferview()->getLyXText(); bufferview()->toggleSelection(true); bufferview()->update(text, BufferView::SELECT); - while ((res == SpellBase::OK || res == SpellBase::IGNORE) && !stop_) { + while ((res == SpellBase::OK || res == SpellBase::IGNORE)) { word_ = bufferview()->nextWord(newval_); - if (word_.word().empty()) { - clearParams(); + // end of document + if (word_.word().empty()) break; - } ++count_; // Update slider if and only if value has changed newvalue_ = int(100.0 * newval_); if (newvalue_!= oldval_) { + lyxerr[Debug::GUI] << "Updating spell progress." << endl; oldval_ = newvalue_; // set progress bar - view().partialUpdate(0); + view().partialUpdate(SPELL_PROGRESSED); } - if (!speller_ || !speller_->alive()) { - clearParams(); - stop(); + // speller might be dead ... + if (!checkAlive()) return; - } 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(); + } else { + showSummary(); + endSession(); + return; + } // set suggestions 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); } @@ -157,64 +273,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; -} diff --git a/src/frontends/controllers/ControlSpellchecker.h b/src/frontends/controllers/ControlSpellchecker.h index 1800ceb3d4..5e22acfeb9 100644 --- a/src/frontends/controllers/ControlSpellchecker.h +++ b/src/frontends/controllers/ControlSpellchecker.h @@ -12,6 +12,7 @@ #ifndef CONTROLSPELLCHECKER_H #define CONTROLSPELLCHECKER_H +#include #include "ControlDialog_impl.h" #include "LString.h" @@ -23,9 +24,15 @@ class SpellBase; */ class ControlSpellchecker : public ControlDialogBD { public: - /// + enum State { + SPELL_PROGRESSED, //< update progress bar + SPELL_FOUND_WORD //< found a bad word + }; + ControlSpellchecker(LyXView &, Dialogs &); + ~ControlSpellchecker(); + /// replace word with replacement void replace(string const &); @@ -38,10 +45,8 @@ public: /// ignore all occurances of word void ignoreAll(); - /// stop checking - void stop(); - /// check text until next misspelled/unknown word + /// returns true when finished void check(); /// get suggestion @@ -53,13 +58,22 @@ public: /// returns progress value int getProgress() const { return oldval_; } - /// returns exit message - string const getMessage() const { return message_; } - /// returns word count int getCount() const { return count_; } 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 void setParams(); /// clean-up on hide. @@ -79,14 +93,8 @@ private: /// word count int count_; - /// exit message - string message_; - - /// set to true to stop checking - bool stop_; - /// The actual spellchecker object - SpellBase * speller_; + boost::scoped_ptr speller_; }; #endif // CONTROLSPELLCHECKER_H diff --git a/src/frontends/qt2/ChangeLog b/src/frontends/qt2/ChangeLog index 89708945f0..09336bbd69 100644 --- a/src/frontends/qt2/ChangeLog +++ b/src/frontends/qt2/ChangeLog @@ -1,3 +1,13 @@ +2003-02-17 John Levon + + * QSpellchecker.h: + * QSpellchecker.C: + * ui/QSpellcheckerDialogBase.ui: + * QSpellcheckerDialog.h: + * QSpellcheckerDialog.C: re-working, remove start etc. + + * README: add comment about checking accelerators + 2003-02-17 John Levon * ui/QPrefSpellcheckerModule.ui: capitalization fix diff --git a/src/frontends/qt2/QSpellchecker.C b/src/frontends/qt2/QSpellchecker.C index 7da1a09b2a..8b9d6ceeaf 100644 --- a/src/frontends/qt2/QSpellchecker.C +++ b/src/frontends/qt2/QSpellchecker.C @@ -43,22 +43,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() { controller().ignoreAll(); @@ -83,35 +67,18 @@ void QSpellchecker::replace() } -void QSpellchecker::spellcheck() -{ - dialog_->spellcheckPB->setEnabled(false); - dialog_->wordED->setEnabled(true); - dialog_->replaceCO->setEnabled(true); - dialog_->replacePB->setEnabled(true); - dialog_->ignorePB->setEnabled(true); - dialog_->replacePB_3->setEnabled(true); - dialog_->addPB->setEnabled(true); - controller().check(); -} - - -void QSpellchecker::stop() +void QSpellchecker::partialUpdate(int s) { - controller().stop(); - dialog_->spellcheckPB->setEnabled(true); - hide(); -} + ControlSpellchecker::State const state = + static_cast(s); + switch (state) { -void QSpellchecker::partialUpdate(int id) -{ - switch (id) { - case 0: + case ControlSpellchecker::SPELL_PROGRESSED: dialog_->spellcheckPR->setProgress(controller().getProgress()); break; - case 1: { + case ControlSpellchecker::SPELL_FOUND_WORD: { dialog_->wordED->setText(toqstr(controller().getWord())); dialog_->suggestionsLB->clear(); @@ -128,12 +95,5 @@ void QSpellchecker::partialUpdate(int id) } break; - case 2: - dialog_->spellcheckPB->setEnabled(true); - hide(); - QMessageBox::information(0, qt_("Spellcheck complete"), - toqstr(controller().getMessage()), - qt_("OK")); - break; } } diff --git a/src/frontends/qt2/QSpellchecker.h b/src/frontends/qt2/QSpellchecker.h index 10477b69eb..d088a181a8 100644 --- a/src/frontends/qt2/QSpellchecker.h +++ b/src/frontends/qt2/QSpellchecker.h @@ -31,17 +31,15 @@ public: /// update from controller void partialUpdate(int id); private: - void stop(); void accept(); void add(); void ignore(); void replace(); - void spellcheck(); /// Apply changes virtual void apply() {} - /// update - virtual void update_contents(); + /// not needed + virtual void update_contents() {} /// build the dialog virtual void build_dialog(); }; diff --git a/src/frontends/qt2/QSpellcheckerDialog.C b/src/frontends/qt2/QSpellcheckerDialog.C index ccbd4cacbb..01e8f7a174 100644 --- a/src/frontends/qt2/QSpellcheckerDialog.C +++ b/src/frontends/qt2/QSpellcheckerDialog.C @@ -26,13 +26,7 @@ QSpellcheckerDialog::QSpellcheckerDialog(QSpellchecker * form) form_(form) { connect(closePB, SIGNAL(clicked()), - this, SLOT(stop())); -} - - -void QSpellcheckerDialog::stop() -{ - form_->stop(); + form, SLOT(slotClose())); } @@ -42,12 +36,6 @@ void QSpellcheckerDialog::acceptClicked() } -void QSpellcheckerDialog::spellcheckClicked() -{ - form_->spellcheck(); -} - - void QSpellcheckerDialog::addClicked() { form_->add(); @@ -95,7 +83,6 @@ void QSpellcheckerDialog::replaceChanged(QString const & str) void QSpellcheckerDialog::closeEvent(QCloseEvent * e) { - form_->stop(); form_->slotWMHide(); e->accept(); } diff --git a/src/frontends/qt2/QSpellcheckerDialog.h b/src/frontends/qt2/QSpellcheckerDialog.h index da19ee0d0d..029551a3a4 100644 --- a/src/frontends/qt2/QSpellcheckerDialog.h +++ b/src/frontends/qt2/QSpellcheckerDialog.h @@ -25,9 +25,7 @@ public slots: virtual void suggestionChanged(const QString &); protected slots: - virtual void stop(); virtual void acceptClicked(); - virtual void spellcheckClicked(); virtual void addClicked(); virtual void replaceClicked(); virtual void ignoreClicked(); diff --git a/src/frontends/qt2/README b/src/frontends/qt2/README index 51fcbec63b..516cd5f996 100644 --- a/src/frontends/qt2/README +++ b/src/frontends/qt2/README @@ -22,6 +22,8 @@ Remember to check tab order on a dialog (third icon, with blue bars in designer) Remember to check sensible resizing behaviour on a dialog. +Remember to use Edit->Check Accelerators + If necessary, you should override Qt2Base::isValid() for determining the validity of the current dialog's contents. diff --git a/src/frontends/qt2/ui/QSpellcheckerDialogBase.ui b/src/frontends/qt2/ui/QSpellcheckerDialogBase.ui index e21fd9051f..37d6f8b2f7 100644 --- a/src/frontends/qt2/ui/QSpellcheckerDialogBase.ui +++ b/src/frontends/qt2/ui/QSpellcheckerDialogBase.ui @@ -13,8 +13,8 @@ 0 0 - 294 - 379 + 323 + 389 @@ -218,7 +218,7 @@ text - Unknown: + Unknown word: buddy @@ -263,21 +263,6 @@ Replace with selected word - - QPushButton - - name - spellcheckPB - - - text - &Start... - - - toolTip - Start spellcheck - - @@ -311,12 +296,6 @@ QSpellcheckerDialogBase addClicked() - - spellcheckPB - clicked() - QSpellcheckerDialogBase - spellcheckClicked() - suggestionsLB doubleClicked(QListBoxItem*) @@ -335,11 +314,9 @@ optionsClicked() replaceChanged(const QString &) replaceClicked() - spellcheckClicked() suggestionChanged(const QString &) - spellcheckPB wordED replaceCO suggestionsLB diff --git a/src/frontends/xforms/ChangeLog b/src/frontends/xforms/ChangeLog index a4c248ee48..c4796e85c9 100644 --- a/src/frontends/xforms/ChangeLog +++ b/src/frontends/xforms/ChangeLog @@ -1,3 +1,9 @@ +2003-02-17 John Levon + + * FormSpellchecker.h: + * FormSpellchecker.C: + * ui/form_spellchecker.fd: rework, remove start, etc. + 2003-02-16 Jean-Marc Lasgouttes * FormPreferences.C (apply): make sure that rc.font_norm_type is diff --git a/src/frontends/xforms/FormSpellchecker.C b/src/frontends/xforms/FormSpellchecker.C index 7507731241..a9dcff8b4d 100644 --- a/src/frontends/xforms/FormSpellchecker.C +++ b/src/frontends/xforms/FormSpellchecker.C @@ -12,8 +12,8 @@ #include "xformsBC.h" -#include "ControlSpellchecker.h" #include "FormSpellchecker.h" +#include "ControlSpellchecker.h" #include "forms/form_spellchecker.h" #include "forms_gettext.h" @@ -23,11 +23,12 @@ #include FORMS_H_LOCATION -typedef FormCB > base_class; +using std::endl; +typedef FormCB > base_class; FormSpellchecker::FormSpellchecker() - : base_class(_("Spellchecker")), state_(STOPPED) + : base_class(_("Spellchecker")) {} @@ -61,8 +62,6 @@ void FormSpellchecker::build() tooltips().init(dialog_->browser_suggestions, str); // Work-around xforms' bug; enable tooltips for browser widgets. setPrehandler(dialog_->browser_suggestions); - str = _("Start the spellingchecker."); - tooltips().init(dialog_->button_start, str); str = _("Replace unknown word."); tooltips().init(dialog_->button_replace, str); str = _("Ignore unknown word."); @@ -76,16 +75,14 @@ void FormSpellchecker::build() } -void FormSpellchecker::updateState(State state) +void FormSpellchecker::partialUpdate(int s) { + ControlSpellchecker::State const state = + static_cast(s); + switch (state) { - case READY_TO_START: - fl_set_slider_value(dialog_->slider_progress, 0.0); - fl_set_object_label(dialog_->slider_progress, "0 %"); - break; - case CHECKING: - { + case ControlSpellchecker::SPELL_FOUND_WORD: { // Set suggestions. string w = controller().getWord(); fl_set_input(dialog_->input_replacement, w.c_str()); @@ -98,8 +95,7 @@ void FormSpellchecker::updateState(State state) // Fall through... } - case STARTED: - { + case ControlSpellchecker::SPELL_PROGRESSED: { int const progress = controller().getProgress(); if (progress == 0) break; @@ -111,68 +107,17 @@ void FormSpellchecker::updateState(State state) fl_set_slider_bounds(dialog_->slider_progress, 0.0, total); fl_set_slider_value(dialog_->slider_progress, wordcount); fl_set_object_label(dialog_->slider_progress, label.c_str()); + fl_redraw_object(dialog_->slider_progress); 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) { - if (ob == dialog_->button_start) { - updateState(STARTED); - controller().check(); - - } else if (ob == dialog_->button_replace) { + if (ob == dialog_->button_replace) { string const tmp = getString(dialog_->input_replacement); controller().replace(tmp); @@ -206,18 +151,3 @@ ButtonPolicy::SMInput FormSpellchecker::input(FL_OBJECT * ob, long ob_value) 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; - } -} diff --git a/src/frontends/xforms/FormSpellchecker.h b/src/frontends/xforms/FormSpellchecker.h index 7d09c66d84..88a56d5b9b 100644 --- a/src/frontends/xforms/FormSpellchecker.h +++ b/src/frontends/xforms/FormSpellchecker.h @@ -30,26 +30,14 @@ private: virtual void apply() {} /// Build the dialog virtual void build(); - /// - virtual void update(); + /// not needed. + virtual void update() {} /// set suggestions and exit message virtual void partialUpdate(int); /// Filter the inputs virtual ButtonPolicy::SMInput input(FL_OBJECT *, long); - - /// - enum State { - READY_TO_START, - STARTED, - CHECKING, - STOPPED - }; - /// - void updateState(State state); - /// - State state_; }; #endif // FORMSPELLCHECKER_H diff --git a/src/frontends/xforms/forms/form_spellchecker.fd b/src/frontends/xforms/forms/form_spellchecker.fd index ae348565b7..bd499f3fd2 100644 --- a/src/frontends/xforms/forms/form_spellchecker.fd +++ b/src/frontends/xforms/forms/form_spellchecker.fd @@ -11,7 +11,7 @@ SnapGrid: 5 Name: form_spellchecker Width: 385 Height: 375 -Number of Objects: 13 +Number of Objects: 12 -------------------- class: FL_BOX @@ -121,24 +121,6 @@ name: browser_suggestions callback: C_FormBaseInputCB 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 type: NORMAL_BUTTON @@ -185,7 +167,7 @@ alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_NORMAL_SIZE lcol: FL_BLACK -label: Accept|#A +label: Ignore All|#g shortcut: resize: FL_RESIZE_X gravity: FL_NorthEast FL_NorthEast diff --git a/src/ispell.C b/src/ispell.C index 34183d7ad7..07db2583e7 100644 --- a/src/ispell.C +++ b/src/ispell.C @@ -9,35 +9,6 @@ #include -#include -#include -#include -#include -#include - -// FIXME: do we need any of this horrible gook ? -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif - -#ifdef HAVE_SYS_SELECT_H -# ifdef HAVE_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 -# endif -# endif -#include -#endif - #include "LString.h" #include "lyxrc.h" #include "language.h" @@ -45,10 +16,13 @@ #include "encoding.h" #include "ispell.h" #include "WordLangTuple.h" +#include "gettext.h" #include "support/forkedcall.h" #include "support/lstrings.h" +#include + #ifndef CXX_GLOBAL_CSTD using std::strcpy; using std::strlen; @@ -57,18 +31,16 @@ using std::strstr; #endif using std::endl; +using std::max; namespace { -/// pid for the `ispell' process. -pid_t isp_pid = -1; - class LaunchIspell : public ForkedProcess { public: /// LaunchIspell(BufferParams const & p, string const & l, - int * in, int * out) - : params(p), lang(l), pipein(in), pipeout(out) {} + int * in, int * out, int * err) + : params(p), lang(l), pipein(in), pipeout(out), pipeerr(err) {} /// virtual ForkedProcess * clone() const { return new LaunchIspell(*this); @@ -84,6 +56,7 @@ private: string const & lang; int * const pipein; int * const pipeout; + int * const pipeerr; }; @@ -96,7 +69,7 @@ int LaunchIspell::start() int LaunchIspell::generateChild() { - isp_pid = fork(); + pid_t isp_pid = fork(); if (isp_pid != 0) { // failed (-1) or parent process (>0) @@ -106,10 +79,13 @@ int LaunchIspell::generateChild() // child process dup2(pipein[0], STDIN_FILENO); dup2(pipeout[1], STDOUT_FILENO); - ::close(pipein[0]); - ::close(pipein[1]); - ::close(pipeout[0]); - ::close(pipeout[1]); + dup2(pipeerr[1], STDERR_FILENO); + close(pipein[0]); + close(pipein[1]); + close(pipeout[0]); + close(pipeout[1]); + close(pipeerr[0]); + close(pipeerr[1]); char * argv[14]; int argc = 0; @@ -200,122 +176,161 @@ int LaunchIspell::generateChild() 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 - int pipein[2]; - int pipeout[2]; + lyxerr[Debug::GUI] << "Created ispell" << endl; + + // 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; - isp_pid = -1; + // This is what happens when goto gets banned. - if (pipe(pipein) == -1 || pipe(pipeout) == -1) { - 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; } if ((out = fdopen(pipein[1], "w")) == 0) { - lyxerr << "LyX: Can't create stream for pipe for spellchecker!" - << endl; - setError(); + error_ = _("Can't open pipe for spellchecker."); return; } if ((in = fdopen(pipeout[0], "r")) == 0) { - lyxerr <<"LyX: Can't create stream for pipe for spellchecker!" - << endl; - setError(); + error_ = _("Can't open pipe for spellchecker."); return; } - setvbuf(out, o_buf, _IOLBF, BUFSIZ); + if ((inerr = fdopen(pipeerr[0], "r")) == 0) { + error_ = _("Can't open pipe for spellchecker."); + return; + } - isp_fd = pipeout[0]; + setvbuf(out, o_buf, _IOLBF, BUFSIZ); - LaunchIspell childprocess(params, lang, pipein, pipeout); - isp_pid = childprocess.start(); - if (isp_pid == -1) { - lyxerr << "LyX: Can't create child process for spellchecker!" - << endl; - setError(); + LaunchIspell * li = new LaunchIspell(params, lang, pipein, pipeout, pipeerr); + child_.reset(li); + if (li->start() == -1) { + error_ = _("Could not create an ispell process.\nYou may not have " + " the right languages installed."); + child_.reset(0); return; } - setError(); /* 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 - // the right typecast. - retval = select(SELECT_TYPE_ARG1 (pipeout[0]+1), - SELECT_TYPE_ARG234 (&infds), - 0, - 0, - SELECT_TYPE_ARG5 (&tv)); + bool err_read; + bool error = select(err_read); - if (retval > 0) { - // Ok, do the reading. We don't have to FD_ISSET since - // there is only one fd in infds. - fgets(buf, 2048, in); - - fputs("!\n", out); // Set terse mode (silently accept correct words) - - } 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; + if (!error) { + if (!err_read) { + // Set terse mode (silently accept correct words) + fputs("!\n", out); + return; + } + + /* must have read something from stderr */ + error_ = buf; } else { - // Select returned error - lyxerr << "Select on ispell returned error, what now?" << endl; + // select returned error + 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() { - 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) { - error_ = - "\n\n" - "The spellcheck-process has died for some reason.\n" - "*One* possible reason could be that you do not have\n" - "a dictionary file for the language of this document\n" - "installed.\n" - "Check your spellchecker or set another dictionary\n" - "in the Spellchecker Options menu.\n\n"; - } else { - error_ = 0; + fd_set infds; + struct timeval tv; + int retval = 0; + FD_ZERO(&infds); + FD_SET(pipeout[0], &infds); + FD_SET(pipeerr[0], &infds); + tv.tv_sec = 2; + tv.tv_usec = 0; + + retval = ::select(SELECT_TYPE_ARG1 (max(pipeout[0], pipeerr[0]) + 1), + 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() { + // Well, somebody is a sick fuck. + if (str == 0 || *(e+1) == '\0') return ""; char * b = e + 2; @@ -329,13 +344,7 @@ string const ISpell::nextMiss() bool ISpell::alive() { - return isp_pid != -1; -} - - -void ISpell::cleanUp() -{ - ::fclose(out); + return child_.get() && child_->running(); } @@ -348,8 +357,18 @@ enum ISpell::Result ISpell::check(WordLangTuple const & word) ::fputs(word.word().c_str(), out); ::fputc('\n', out); - char buf[1024]; - ::fgets(buf, 1024, in); + bool err_read; + 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 // the signal-handler could have disabled blocking on the fd @@ -396,20 +415,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) { ::fputc('*', out); // Insert word in personal dictionary @@ -428,7 +433,5 @@ void ISpell::accept(WordLangTuple const & word) string const ISpell::error() { - if (error_) - return error_; - return ""; + return error_; } diff --git a/src/ispell.h b/src/ispell.h index cdb193fc6c..23a75eb6ce 100644 --- a/src/ispell.h +++ b/src/ispell.h @@ -10,11 +10,14 @@ #ifndef SP_ISPELL_H #define SP_ISPELL_H -#include - #include "SpellBase.h" +#include + +#include + class BufferParams; +class ForkedProcess; /// i/a spell process-based spellchecker class ISpell : public SpellBase { @@ -26,15 +29,9 @@ public: /// return true if the spellchecker instance still exists virtual bool alive(); - /// clean up on messy exit - virtual void cleanUp(); - /// check the given word and return the result virtual enum Result check(WordLangTuple const & word); - /// finish this spellchecker instance - virtual void close(); - /// insert the given word into the personal dictionary virtual void insert(WordLangTuple const & word); @@ -48,17 +45,29 @@ public: virtual string const error(); private: - /// - void setError(); + /// read some data. Returns true on an error. Sets err_read + /// to true if the data was from stderr. + bool select(bool & err_read); /// instream to communicate with ispell FILE * in; /// outstream to communicate with ispell 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 - char const * error_; - /// the fd of the outgoing pipe - int isp_fd; + string error_; + + boost::scoped_ptr child_; // vileness below ... please FIXME /// str ??? diff --git a/src/lyxfunc.C b/src/lyxfunc.C index b74ad0e369..99a61ed8e2 100644 --- a/src/lyxfunc.C +++ b/src/lyxfunc.C @@ -454,6 +454,9 @@ FuncStatus LyXFunc::getStatus(FuncRequest const & ev) const case LFUN_VC_HISTORY: disable = !buf->lyxvc.inUse(); break; + case LFUN_MENURELOAD: + disable = buf->isUnnamed() || buf->isClean(); + break; case LFUN_BOOKMARK_GOTO: disable = !view()-> isSavedPosition(strToUnsignedInt(ev.argument)); diff --git a/src/pspell.C b/src/pspell.C index 1c3692775c..832b3c1b51 100644 --- a/src/pspell.C +++ b/src/pspell.C @@ -12,6 +12,7 @@ #ifdef USE_PSPELL #include "support/LAssert.h" +#include "debug.h" #define USE_ORIGINAL_MANAGER_FUNCS 1 // new aspell pspell missing extern "C" @@ -22,38 +23,39 @@ extern "C" { #include "pspell.h" #include "WordLangTuple.h" +using std::endl; + PSpell::PSpell(BufferParams const &, string const & lang) : els(0), spell_error_object(0) { addManager(lang); + lyxerr[Debug::GUI] << "created pspell" << endl; } PSpell::~PSpell() { - cleanUp(); - close(); + lyxerr[Debug::GUI] << "killed pspell" << endl; + + if (spell_error_object) { + delete_pspell_can_have_error(spell_error_object); + spell_error_object = 0; + } + if (els) delete_pspell_string_emulation(els); + Managers::iterator it = managers_.begin(); Managers::iterator end = managers_.end(); for (; it != end; ++it) { + pspell_manager_save_all_word_lists(it->second.manager); delete_pspell_manager(it->second.manager); 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) { PspellConfig * config = new_pspell_config(); @@ -108,17 +110,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) { Managers::iterator it = managers_.find(word.lang_code()); diff --git a/src/pspell.h b/src/pspell.h index 108fb0eb16..bf3c1f3e99 100644 --- a/src/pspell.h +++ b/src/pspell.h @@ -37,15 +37,9 @@ public: */ virtual bool alive() { return true; } - /// clean up on messy exit - virtual void cleanUp(); - /// check the given word and return the result virtual enum Result check(WordLangTuple const &); - /// finish this spellchecker instance - virtual void close(); - /// insert the given word into the personal dictionary virtual void insert(WordLangTuple const &); diff --git a/src/support/ChangeLog b/src/support/ChangeLog index c6c17a5782..532b344007 100644 --- a/src/support/ChangeLog +++ b/src/support/ChangeLog @@ -1,3 +1,8 @@ +2003-02-17 John Levon + + * forkedcall.h: + * forkedcall.C: add running() + 2003-02-08 John Levon * Makefile.am: diff --git a/src/support/forkedcall.C b/src/support/forkedcall.C index c1d64a0839..c3e6938098 100644 --- a/src/support/forkedcall.C +++ b/src/support/forkedcall.C @@ -143,6 +143,24 @@ int ForkedProcess::runNonBlocking() 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) { lyxerr << "ForkedProcess::kill(" << tol << ')' << endl; diff --git a/src/support/forkedcall.h b/src/support/forkedcall.h index ec8d0d972e..b1bfa2865b 100644 --- a/src/support/forkedcall.h +++ b/src/support/forkedcall.h @@ -94,6 +94,9 @@ public: /// Returns the identifying command (for display in the GUI perhaps). string const & command() const { return command_; } + /// is the process running ? + bool running() const; + /** Kill child prematurely. * First, a SIGHUP is sent to the child. * If that does not end the child process within "tolerance" -- 2.39.2