spell rework. revert fix

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@6191 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
John Levon 2003-02-17 18:40:04 +00:00
parent a4e4ebc7eb
commit 47c34c3afa
25 changed files with 438 additions and 498 deletions

View File

@ -1,3 +1,13 @@
2003-02-17 John Levon <levon@movementarian.org>
* 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 <poenitz@gmx.net>

View File

@ -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;

View File

@ -1,3 +1,12 @@
2003-02-17 John Levon <levon@movementarian.org>
* 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 <j.spitzmueller@gmx.de>
* ControlBibtex.[Ch]: new function getBibFiles() (provides list

View File

@ -32,17 +32,17 @@ void ControlDialog<Base>::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();

View File

@ -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,73 +73,164 @@ 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);
}
void ControlSpellchecker::replace(string const & replacement)
{
bufferview()->replaceWord(replacement);
@ -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;
}

View File

@ -12,6 +12,7 @@
#ifndef CONTROLSPELLCHECKER_H
#define CONTROLSPELLCHECKER_H
#include <boost/scoped_ptr.hpp>
#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<SpellBase> speller_;
};
#endif // CONTROLSPELLCHECKER_H

View File

@ -1,3 +1,13 @@
2003-02-17 John Levon <levon@movementarian.org>
* 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 <levon@movementarian.org>
* ui/QPrefSpellcheckerModule.ui: capitalization fix

View File

@ -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()
void QSpellchecker::partialUpdate(int s)
{
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();
}
ControlSpellchecker::State const state =
static_cast<ControlSpellchecker::State>(s);
switch (state) {
void QSpellchecker::stop()
{
controller().stop();
dialog_->spellcheckPB->setEnabled(true);
hide();
}
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;
}
}

View File

@ -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();
};

View File

@ -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();
}

View File

@ -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();

View File

@ -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.

View File

@ -13,8 +13,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>294</width>
<height>379</height>
<width>323</width>
<height>389</height>
</rect>
</property>
<property stdset="1">
@ -218,7 +218,7 @@
</property>
<property stdset="1">
<name>text</name>
<string>Unknown:</string>
<string>Unknown word:</string>
</property>
<property>
<name>buddy</name>
@ -263,21 +263,6 @@
<string>Replace with selected word</string>
</property>
</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>
</widget>
<connections>
@ -311,12 +296,6 @@
<receiver>QSpellcheckerDialogBase</receiver>
<slot>addClicked()</slot>
</connection>
<connection>
<sender>spellcheckPB</sender>
<signal>clicked()</signal>
<receiver>QSpellcheckerDialogBase</receiver>
<slot>spellcheckClicked()</slot>
</connection>
<connection>
<sender>suggestionsLB</sender>
<signal>doubleClicked(QListBoxItem*)</signal>
@ -335,11 +314,9 @@
<slot access="public">optionsClicked()</slot>
<slot access="public">replaceChanged(const QString &amp;)</slot>
<slot access="public">replaceClicked()</slot>
<slot access="public">spellcheckClicked()</slot>
<slot access="public">suggestionChanged(const QString &amp;)</slot>
</connections>
<tabstops>
<tabstop>spellcheckPB</tabstop>
<tabstop>wordED</tabstop>
<tabstop>replaceCO</tabstop>
<tabstop>suggestionsLB</tabstop>

View File

@ -1,3 +1,9 @@
2003-02-17 John Levon <levon@movementarian.org>
* FormSpellchecker.h:
* FormSpellchecker.C:
* ui/form_spellchecker.fd: rework, remove start, etc.
2003-02-16 Jean-Marc Lasgouttes <lasgouttes@freesurf.fr>
* FormPreferences.C (apply): make sure that rc.font_norm_type is

View File

@ -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
using std::endl;
typedef FormCB<ControlSpellchecker, FormDB<FD_spellchecker> > 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)
{
switch (state) {
case READY_TO_START:
fl_set_slider_value(dialog_->slider_progress, 0.0);
fl_set_object_label(dialog_->slider_progress, "0 %");
break;
ControlSpellchecker::State const state =
static_cast<ControlSpellchecker::State>(s);
case CHECKING:
{
switch (state) {
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;
}
}

View File

@ -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

View File

@ -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

View File

@ -9,35 +9,6 @@
#include <config.h>
#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 "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 <sys/select.h>
#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;
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) {
lyxerr << "LyX: Can't create pipe for spellchecker!" << endl;
setError();
// This is what happens when goto gets banned.
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;
}
if ((inerr = fdopen(pipeerr[0], "r")) == 0) {
error_ = _("Can't open pipe for spellchecker.");
return;
}
setvbuf(out, o_buf, _IOLBF, BUFSIZ);
isp_fd = pipeout[0];
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);
if (!error) {
if (!err_read) {
// Set terse mode (silently accept correct words)
fputs("!\n", out);
return;
}
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;
/* 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_;
}

View File

@ -10,11 +10,14 @@
#ifndef SP_ISPELL_H
#define SP_ISPELL_H
#include <cstdio>
#include "SpellBase.h"
#include <boost/scoped_ptr.hpp>
#include <cstdio>
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<ForkedProcess> child_;
// vileness below ... please FIXME
/// str ???

View File

@ -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));

View File

@ -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());

View File

@ -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 &);

View File

@ -1,3 +1,8 @@
2003-02-17 John Levon <levon@movementarian.org>
* forkedcall.h:
* forkedcall.C: add running()
2003-02-08 John Levon <levon@movementarian.org>
* Makefile.am:

View File

@ -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;

View File

@ -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"