|
|
|
/* This file is part of the KDE libraries
|
|
|
|
Copyright (C) 1997 David Sweet <dsweet@kde.org>
|
|
|
|
Copyright (C) 2000-2001 Wolfram Diestel <wolfram@steloj.de>
|
|
|
|
Copyright (C) 2003 Zack Rusin <zack@kde.org>
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License version 2 as published by the Free Software Foundation.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdlib.h> // atoi
|
|
|
|
|
|
|
|
#ifdef HAVE_STRINGS_H
|
|
|
|
#include <strings.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <tqregexp.h>
|
|
|
|
#include <tqtextcodec.h>
|
|
|
|
#include <tqtimer.h>
|
|
|
|
|
|
|
|
#include <kapplication.h>
|
|
|
|
#include <kmessagebox.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include "kspell.h"
|
|
|
|
#include "kspelldlg.h"
|
|
|
|
#include <kwin.h>
|
|
|
|
#include <kprocio.h>
|
|
|
|
|
|
|
|
#define MAXLINELENGTH 10000
|
|
|
|
#undef IGNORE //fix possible conflict
|
|
|
|
|
|
|
|
enum {
|
|
|
|
GOOD= 0,
|
|
|
|
IGNORE= 1,
|
|
|
|
REPLACE= 2,
|
|
|
|
MISTAKE= 3
|
|
|
|
};
|
|
|
|
|
|
|
|
enum checkMethod { Method1 = 0, Method2 };
|
|
|
|
|
|
|
|
struct BufferedWord
|
|
|
|
{
|
|
|
|
checkMethod method;
|
|
|
|
TQString word;
|
|
|
|
bool useDialog;
|
|
|
|
bool suggest;
|
|
|
|
};
|
|
|
|
|
|
|
|
class KSpell::KSpellPrivate
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
bool endOfResponse;
|
|
|
|
bool m_bIgnoreUpperWords;
|
|
|
|
bool m_bIgnoreTitleCase;
|
|
|
|
bool m_bNoMisspellingsEncountered;
|
|
|
|
SpellerType type;
|
|
|
|
KSpell* suggestSpell;
|
|
|
|
bool checking;
|
|
|
|
TQValueList<BufferedWord> unchecked;
|
|
|
|
TQTimer *checkNextTimer;
|
|
|
|
bool aspellV6;
|
|
|
|
};
|
|
|
|
|
|
|
|
//TODO
|
|
|
|
//Parse stderr output
|
|
|
|
//e.g. -- invalid dictionary name
|
|
|
|
|
|
|
|
/*
|
|
|
|
Things to put in KSpellConfigDlg:
|
|
|
|
make root/affix combinations that aren't in the dictionary (-m)
|
|
|
|
don't generate any affix/root combinations (-P)
|
|
|
|
Report run-together words with missing blanks as spelling errors. (-B)
|
|
|
|
default dictionary (-d [dictionary])
|
|
|
|
personal dictionary (-p [dictionary])
|
|
|
|
path to ispell -- NO: ispell should be in $PATH
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
// Connects a slot to KProcIO's output signal
|
|
|
|
#define OUTPUT(x) (connect (proc, TQT_SIGNAL (readReady(KProcIO *)), this, TQT_SLOT (x(KProcIO *))))
|
|
|
|
|
|
|
|
// Disconnect a slot from...
|
|
|
|
#define NOOUTPUT(x) (disconnect (proc, TQT_SIGNAL (readReady(KProcIO *)), this, TQT_SLOT (x(KProcIO *))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
KSpell::KSpell( TQWidget *_parent, const TQString &_caption,
|
|
|
|
TQObject *obj, const char *slot, KSpellConfig *_ksc,
|
|
|
|
bool _progressbar, bool _modal )
|
|
|
|
{
|
|
|
|
initialize( _parent, _caption, obj, slot, _ksc,
|
|
|
|
_progressbar, _modal, Text );
|
|
|
|
}
|
|
|
|
|
|
|
|
KSpell::KSpell( TQWidget *_parent, const TQString &_caption,
|
|
|
|
TQObject *obj, const char *slot, KSpellConfig *_ksc,
|
|
|
|
bool _progressbar, bool _modal, SpellerType type )
|
|
|
|
{
|
|
|
|
initialize( _parent, _caption, obj, slot, _ksc,
|
|
|
|
_progressbar, _modal, type );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::hide() { ksdlg->hide(); }
|
|
|
|
|
|
|
|
int KSpell::heightDlg() const { return ksdlg->height(); }
|
|
|
|
int KSpell::widthDlg() const { return ksdlg->width(); }
|
|
|
|
|
|
|
|
// Check if aspell is at least version 0.6
|
|
|
|
static bool determineASpellV6()
|
|
|
|
{
|
|
|
|
TQString result;
|
|
|
|
FILE *fs = popen("aspell -v", "r");
|
|
|
|
if (fs)
|
|
|
|
{
|
|
|
|
// Close textstream before we close fs
|
|
|
|
{
|
|
|
|
TQTextStream ts(fs, IO_ReadOnly);
|
|
|
|
result = ts.read().stripWhiteSpace();
|
|
|
|
}
|
|
|
|
pclose(fs);
|
|
|
|
}
|
|
|
|
|
|
|
|
TQRegExp rx("Aspell (\\d.\\d)");
|
|
|
|
if (rx.search(result) != -1)
|
|
|
|
{
|
|
|
|
float version = rx.cap(1).toFloat();
|
|
|
|
return (version >= 0.6);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
KSpell::startIspell()
|
|
|
|
//trystart = {0,1,2}
|
|
|
|
{
|
|
|
|
if ((trystart == 0) && (ksconfig->client() == KS_CLIENT_ASPELL))
|
|
|
|
d->aspellV6 = determineASpellV6();
|
|
|
|
|
|
|
|
kdDebug(750) << "Try #" << trystart << endl;
|
|
|
|
|
|
|
|
if ( trystart > 0 ) {
|
|
|
|
proc->resetAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ( ksconfig->client() )
|
|
|
|
{
|
|
|
|
case KS_CLIENT_ISPELL:
|
|
|
|
*proc << "ispell";
|
|
|
|
kdDebug(750) << "Using ispell" << endl;
|
|
|
|
break;
|
|
|
|
case KS_CLIENT_ASPELL:
|
|
|
|
*proc << "aspell";
|
|
|
|
kdDebug(750) << "Using aspell" << endl;
|
|
|
|
break;
|
|
|
|
case KS_CLIENT_HSPELL:
|
|
|
|
*proc << "hspell";
|
|
|
|
kdDebug(750) << "Using hspell" << endl;
|
|
|
|
break;
|
|
|
|
case KS_CLIENT_ZEMBEREK:
|
|
|
|
*proc << "zpspell";
|
|
|
|
kdDebug(750) << "Using zemberek(zpspell)" << endl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ksconfig->client() == KS_CLIENT_ISPELL || ksconfig->client() == KS_CLIENT_ASPELL )
|
|
|
|
{
|
|
|
|
*proc << "-a" << "-S";
|
|
|
|
|
|
|
|
switch ( d->type )
|
|
|
|
{
|
|
|
|
case HTML:
|
|
|
|
//Debian uses an ispell version that has the -h option instead.
|
|
|
|
//Not sure what they did, but the preferred spell checker
|
|
|
|
//on that platform is aspell anyway, so use -H untill I'll come
|
|
|
|
//up with something better.
|
|
|
|
*proc << "-H";
|
|
|
|
break;
|
|
|
|
case TeX:
|
|
|
|
//same for aspell and ispell
|
|
|
|
*proc << "-t";
|
|
|
|
break;
|
|
|
|
case Nroff:
|
|
|
|
//only ispell supports
|
|
|
|
if ( ksconfig->client() == KS_CLIENT_ISPELL )
|
|
|
|
*proc << "-n";
|
|
|
|
break;
|
|
|
|
case Text:
|
|
|
|
default:
|
|
|
|
//nothing
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ksconfig->noRootAffix())
|
|
|
|
{
|
|
|
|
*proc<<"-m";
|
|
|
|
}
|
|
|
|
if (ksconfig->runTogether())
|
|
|
|
{
|
|
|
|
*proc << "-B";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*proc << "-C";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (trystart<2)
|
|
|
|
{
|
|
|
|
if (! ksconfig->dictionary().isEmpty())
|
|
|
|
{
|
|
|
|
kdDebug(750) << "using dictionary [" << ksconfig->dictionary() << "]" << endl;
|
|
|
|
*proc << "-d";
|
|
|
|
*proc << ksconfig->dictionary();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Note to potential debuggers: -Tlatin2 _is_ being added on the
|
|
|
|
// _first_ try. But, some versions of ispell will fail with this
|
|
|
|
// option, so kspell tries again without it. That's why as 'ps -ax'
|
|
|
|
// shows "ispell -a -S ..." withou the "-Tlatin2" option.
|
|
|
|
|
|
|
|
if ( trystart<1 ) {
|
|
|
|
switch ( ksconfig->encoding() )
|
|
|
|
{
|
|
|
|
case KS_E_LATIN1:
|
|
|
|
*proc << "-Tlatin1";
|
|
|
|
break;
|
|
|
|
case KS_E_LATIN2:
|
|
|
|
*proc << "-Tlatin2";
|
|
|
|
break;
|
|
|
|
case KS_E_LATIN3:
|
|
|
|
*proc << "-Tlatin3";
|
|
|
|
break;
|
|
|
|
|
|
|
|
// add the other charsets here
|
|
|
|
case KS_E_LATIN4:
|
|
|
|
case KS_E_LATIN5:
|
|
|
|
case KS_E_LATIN7:
|
|
|
|
case KS_E_LATIN8:
|
|
|
|
case KS_E_LATIN9:
|
|
|
|
case KS_E_LATIN13:
|
|
|
|
// will work, if this is the default charset in the dictionary
|
|
|
|
kdError(750) << "charsets ISO-8859-4, -5, -7, -8, -9 and -13 not supported yet" << endl;
|
|
|
|
break;
|
|
|
|
case KS_E_LATIN15: // ISO-8859-15 (Latin 9)
|
|
|
|
if (ksconfig->client() == KS_CLIENT_ISPELL)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* As far as I know, there are no ispell dictionary using ISO-8859-15
|
|
|
|
* but users have the tendency to select this encoding instead of ISO-8859-1
|
|
|
|
* So put ispell in ISO-8859-1 (Latin 1) mode.
|
|
|
|
*/
|
|
|
|
*proc << "-Tlatin1";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
kdError(750) << "ISO-8859-15 not supported for aspell yet." << endl;
|
|
|
|
break;
|
|
|
|
case KS_E_UTF8:
|
|
|
|
*proc << "-Tutf8";
|
|
|
|
if (ksconfig->client() == KS_CLIENT_ASPELL)
|
|
|
|
*proc << "--encoding=utf-8";
|
|
|
|
break;
|
|
|
|
case KS_E_KOI8U:
|
|
|
|
*proc << "-w'"; // add ' as a word char
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// -a : pipe mode
|
|
|
|
// -S : sort suggestions by probable correctness
|
|
|
|
}
|
|
|
|
else // hspell and Zemberek(zpspell) doesn't need all the rest of the options
|
|
|
|
*proc << "-a";
|
|
|
|
|
|
|
|
if (trystart == 0) //don't connect these multiple times
|
|
|
|
{
|
|
|
|
connect( proc, TQT_SIGNAL(receivedStderr(KProcess *, char *, int)),
|
|
|
|
this, TQT_SLOT(ispellErrors(KProcess *, char *, int)) );
|
|
|
|
|
|
|
|
connect( proc, TQT_SIGNAL(processExited(KProcess *)),
|
|
|
|
this, TQT_SLOT(ispellExit (KProcess *)) );
|
|
|
|
|
|
|
|
OUTPUT(KSpell2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !proc->start() )
|
|
|
|
{
|
|
|
|
m_status = Error;
|
|
|
|
TQTimer::singleShot( 0, this, TQT_SLOT(emitDeath()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
KSpell::ispellErrors( KProcess *, char *buffer, int buflen )
|
|
|
|
{
|
|
|
|
buffer[buflen-1] = '\0';
|
|
|
|
// kdDebug(750) << "ispellErrors [" << buffer << "]\n" << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::KSpell2( KProcIO * )
|
|
|
|
|
|
|
|
{
|
|
|
|
TQString line;
|
|
|
|
|
|
|
|
kdDebug(750) << "KSpell::KSpell2" << endl;
|
|
|
|
|
|
|
|
trystart = maxtrystart; //We've officially started ispell and don't want
|
|
|
|
//to try again if it dies.
|
|
|
|
|
|
|
|
if ( proc->readln( line, true ) == -1 )
|
|
|
|
{
|
|
|
|
TQTimer::singleShot( 0, this, TQT_SLOT(emitDeath()) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( line[0] != '@' ) //@ indicates that ispell is working fine
|
|
|
|
{
|
|
|
|
TQTimer::singleShot( 0, this, TQT_SLOT(emitDeath()) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//We want to recognize KDE in any text!
|
|
|
|
if ( !ignore("kde") )
|
|
|
|
{
|
|
|
|
kdDebug(750) << "@KDE was false" << endl;
|
|
|
|
TQTimer::singleShot( 0, this, TQT_SLOT(emitDeath()) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//We want to recognize linux in any text!
|
|
|
|
if ( !ignore("linux") )
|
|
|
|
{
|
|
|
|
kdDebug(750) << "@Linux was false" << endl;
|
|
|
|
TQTimer::singleShot( 0, this, TQT_SLOT(emitDeath()) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
NOOUTPUT( KSpell2 );
|
|
|
|
|
|
|
|
m_status = Running;
|
|
|
|
emit ready( this );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
KSpell::setUpDialog( bool reallyuseprogressbar )
|
|
|
|
{
|
|
|
|
if ( dialogsetup )
|
|
|
|
return;
|
|
|
|
|
|
|
|
//Set up the dialog box
|
|
|
|
ksdlg = new KSpellDlg( parent, "dialog",
|
|
|
|
progressbar && reallyuseprogressbar, modaldlg );
|
|
|
|
ksdlg->setCaption( caption );
|
|
|
|
|
|
|
|
connect( ksdlg, TQT_SIGNAL(command(int)),
|
|
|
|
this, TQT_SLOT(slotStopCancel(int)) );
|
|
|
|
connect( this, TQT_SIGNAL(progress(unsigned int)),
|
|
|
|
ksdlg, TQT_SLOT(slotProgress(unsigned int)) );
|
|
|
|
|
|
|
|
#ifdef Q_WS_X11 // FIXME(E): Implement for Qt/Embedded
|
|
|
|
KWin::setIcons( ksdlg->winId(), kapp->icon(), kapp->miniIcon() );
|
|
|
|
#endif
|
|
|
|
if ( modaldlg )
|
|
|
|
ksdlg->setFocus();
|
|
|
|
dialogsetup = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KSpell::addPersonal( const TQString & word )
|
|
|
|
{
|
|
|
|
TQString qs = word.simplifyWhiteSpace();
|
|
|
|
|
|
|
|
//we'll let ispell do the work here b/c we can
|
|
|
|
if ( qs.tqfind(' ') != -1 || qs.isEmpty() ) // make sure it's a _word_
|
|
|
|
return false;
|
|
|
|
|
|
|
|
qs.prepend( "*" );
|
|
|
|
personaldict = true;
|
|
|
|
|
|
|
|
return proc->writeStdin( qs );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KSpell::writePersonalDictionary()
|
|
|
|
{
|
|
|
|
return proc->writeStdin(TQString("#"));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KSpell::ignore( const TQString & word )
|
|
|
|
{
|
|
|
|
TQString qs = word.simplifyWhiteSpace();
|
|
|
|
|
|
|
|
//we'll let ispell do the work here b/c we can
|
|
|
|
if ( qs.tqfind (' ') != -1 || qs.isEmpty() ) // make sure it's a _word_
|
|
|
|
return false;
|
|
|
|
|
|
|
|
qs.prepend( "@" );
|
|
|
|
|
|
|
|
return proc->writeStdin( qs );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
KSpell::cleanFputsWord( const TQString & s, bool appendCR )
|
|
|
|
{
|
|
|
|
TQString qs(s);
|
|
|
|
bool empty = true;
|
|
|
|
|
|
|
|
for( unsigned int i = 0; i < qs.length(); i++ )
|
|
|
|
{
|
|
|
|
//we need some punctuation for ornaments
|
|
|
|
if ( qs[i] != '\'' && qs[i] != '\"' && qs[i] != '-'
|
|
|
|
&& qs[i].isPunct() || qs[i].isSpace() )
|
|
|
|
{
|
|
|
|
qs.remove(i,1);
|
|
|
|
i--;
|
|
|
|
} else {
|
|
|
|
if ( qs[i].isLetter() )
|
|
|
|
empty=false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't check empty words, otherwise synchronization will lost
|
|
|
|
if (empty)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return proc->writeStdin( "^"+qs, appendCR );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
KSpell::cleanFputs( const TQString & s, bool appendCR )
|
|
|
|
{
|
|
|
|
TQString qs(s);
|
|
|
|
unsigned l = qs.length();
|
|
|
|
|
|
|
|
// some uses of '$' (e.g. "$0") cause ispell to skip all following text
|
|
|
|
for( unsigned int i = 0; i < l; ++i )
|
|
|
|
{
|
|
|
|
if( qs[i] == '$' )
|
|
|
|
qs[i] = ' ';
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( l<MAXLINELENGTH )
|
|
|
|
{
|
|
|
|
if ( qs.isEmpty() )
|
|
|
|
qs="";
|
|
|
|
return proc->writeStdin( "^"+qs, appendCR );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return proc->writeStdin( TQString::fromAscii( "^\n" ),appendCR );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KSpell::checkWord( const TQString & buffer, bool _usedialog )
|
|
|
|
{
|
|
|
|
if (d->checking) { // don't check multiple words simultaneously
|
|
|
|
BufferedWord bufferedWord;
|
|
|
|
bufferedWord.method = Method1;
|
|
|
|
bufferedWord.word = buffer;
|
|
|
|
bufferedWord.useDialog = _usedialog;
|
|
|
|
d->unchecked.append( bufferedWord );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
d->checking = true;
|
|
|
|
TQString qs = buffer.simplifyWhiteSpace();
|
|
|
|
|
|
|
|
if ( qs.tqfind (' ') != -1 || qs.isEmpty() ) { // make sure it's a _word_
|
|
|
|
d->checkNextTimer->start( 0, true );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
///set the dialog signal handler
|
|
|
|
dialog3slot = TQT_SLOT(checkWord3());
|
|
|
|
|
|
|
|
usedialog = _usedialog;
|
|
|
|
setUpDialog( false );
|
|
|
|
if ( _usedialog )
|
|
|
|
{
|
|
|
|
emitProgress();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ksdlg->hide();
|
|
|
|
|
|
|
|
TQString blank_line;
|
|
|
|
while (proc->readln( blank_line, true ) != -1); // eat spurious blanks
|
|
|
|
|
|
|
|
OUTPUT(checkWord2);
|
|
|
|
// connect (this, TQT_SIGNAL (dialog3()), this, TQT_SLOT (checkWord3()));
|
|
|
|
|
|
|
|
proc->writeStdin(TQString("%")); // turn off terse mode
|
|
|
|
proc->writeStdin( buffer ); // send the word to ispell
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KSpell::checkWord( const TQString & buffer, bool _usedialog, bool suggest )
|
|
|
|
{
|
|
|
|
if (d->checking) { // don't check multiple words simultaneously
|
|
|
|
BufferedWord bufferedWord;
|
|
|
|
bufferedWord.method = Method2;
|
|
|
|
bufferedWord.word = buffer;
|
|
|
|
bufferedWord.useDialog = _usedialog;
|
|
|
|
bufferedWord.suggest = suggest;
|
|
|
|
d->unchecked.append( bufferedWord );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
d->checking = true;
|
|
|
|
TQString qs = buffer.simplifyWhiteSpace();
|
|
|
|
|
|
|
|
if ( qs.tqfind (' ') != -1 || qs.isEmpty() ) { // make sure it's a _word_
|
|
|
|
d->checkNextTimer->start( 0, true );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
///set the dialog signal handler
|
|
|
|
if ( !suggest ) {
|
|
|
|
dialog3slot = TQT_SLOT(checkWord3());
|
|
|
|
usedialog = _usedialog;
|
|
|
|
setUpDialog( false );
|
|
|
|
if ( _usedialog )
|
|
|
|
{
|
|
|
|
emitProgress();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ksdlg->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString blank_line;
|
|
|
|
while (proc->readln( blank_line, true ) != -1); // eat spurious blanks
|
|
|
|
|
|
|
|
OUTPUT(checkWord2);
|
|
|
|
// connect (this, TQT_SIGNAL (dialog3()), this, TQT_SLOT (checkWord3()));
|
|
|
|
|
|
|
|
proc->writeStdin(TQString("%")); // turn off terse mode
|
|
|
|
proc->writeStdin( buffer ); // send the word to ispell
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::checkWord2( KProcIO* )
|
|
|
|
{
|
|
|
|
TQString word;
|
|
|
|
TQString line;
|
|
|
|
proc->readln( line, true ); //get ispell's response
|
|
|
|
|
|
|
|
/* ispell man page: "Each sentence of text input is terminated with an
|
|
|
|
additional blank line, indicating that ispell has completed processing
|
|
|
|
the input line."
|
|
|
|
<sanders>
|
|
|
|
But there can be multiple lines returned in the case of an error,
|
|
|
|
in this case we should consume all the output given otherwise spell checking
|
|
|
|
can get out of sync.
|
|
|
|
</sanders>
|
|
|
|
*/
|
|
|
|
TQString blank_line;
|
|
|
|
while (proc->readln( blank_line, true ) != -1); // eat the blank line
|
|
|
|
NOOUTPUT(checkWord2);
|
|
|
|
|
|
|
|
bool mistake = ( parseOneResponse(line, word, sugg) == MISTAKE );
|
|
|
|
if ( mistake && usedialog )
|
|
|
|
{
|
|
|
|
cwword = word;
|
|
|
|
dialog( word, sugg, TQT_SLOT(checkWord3()) );
|
|
|
|
d->checkNextTimer->start( 0, true );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if( mistake )
|
|
|
|
{
|
|
|
|
emit misspelling( word, sugg, lastpos );
|
|
|
|
}
|
|
|
|
|
|
|
|
//emits a "corrected" signal _even_ if no change was made
|
|
|
|
//so that the calling program knows when the check is complete
|
|
|
|
emit corrected( word, word, 0L );
|
|
|
|
d->checkNextTimer->start( 0, true );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::checkNext()
|
|
|
|
{
|
|
|
|
// Queue words to prevent kspell from turning into a fork bomb
|
|
|
|
d->checking = false;
|
|
|
|
if (!d->unchecked.empty()) {
|
|
|
|
BufferedWord buf = d->unchecked.front();
|
|
|
|
d->unchecked.pop_front();
|
|
|
|
|
|
|
|
if (buf.method == Method1)
|
|
|
|
checkWord( buf.word, buf.useDialog );
|
|
|
|
else
|
|
|
|
checkWord( buf.word, buf.useDialog, buf.suggest );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::suggestWord( KProcIO * )
|
|
|
|
{
|
|
|
|
TQString word;
|
|
|
|
TQString line;
|
|
|
|
proc->readln( line, true ); //get ispell's response
|
|
|
|
|
|
|
|
/* ispell man page: "Each sentence of text input is terminated with an
|
|
|
|
additional blank line, indicating that ispell has completed processing
|
|
|
|
the input line." */
|
|
|
|
TQString blank_line;
|
|
|
|
proc->readln( blank_line, true ); // eat the blank line
|
|
|
|
|
|
|
|
NOOUTPUT(checkWord2);
|
|
|
|
|
|
|
|
bool mistake = ( parseOneResponse(line, word, sugg) == MISTAKE );
|
|
|
|
if ( mistake && usedialog )
|
|
|
|
{
|
|
|
|
cwword=word;
|
|
|
|
dialog( word, sugg, TQT_SLOT(checkWord3()) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::checkWord3()
|
|
|
|
{
|
|
|
|
disconnect( this, TQT_SIGNAL(dialog3()), this, TQT_SLOT(checkWord3()) );
|
|
|
|
|
|
|
|
emit corrected( cwword, replacement(), 0L );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KSpell::funnyWord( const TQString & word )
|
|
|
|
// composes a guess from ispell to a readable word
|
|
|
|
// e.g. "re+fry-y+ies" -> "refries"
|
|
|
|
{
|
|
|
|
TQString qs;
|
|
|
|
unsigned int i=0;
|
|
|
|
|
|
|
|
for( i=0; word [i]!='\0';i++ )
|
|
|
|
{
|
|
|
|
if (word [i]=='+')
|
|
|
|
continue;
|
|
|
|
if (word [i]=='-')
|
|
|
|
{
|
|
|
|
TQString shorty;
|
|
|
|
unsigned int j;
|
|
|
|
int k;
|
|
|
|
|
|
|
|
for( j = i+1; word[j] != '\0' && word[j] != '+' && word[j] != '-'; j++ )
|
|
|
|
shorty += word[j];
|
|
|
|
|
|
|
|
i = j-1;
|
|
|
|
|
|
|
|
if ( !( k = qs.tqfindRev(shorty) ) || k != -1 )
|
|
|
|
qs.remove( k, shorty.length() );
|
|
|
|
else
|
|
|
|
{
|
|
|
|
qs += '-';
|
|
|
|
qs += shorty; //it was a hyphen, not a '-' from ispell
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
qs += word[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return qs;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int KSpell::parseOneResponse( const TQString &buffer, TQString &word, TQStringList & sugg )
|
|
|
|
// buffer is checked, word and sugg are filled in
|
|
|
|
// returns
|
|
|
|
// GOOD if word is fine
|
|
|
|
// IGNORE if word is in ignorelist
|
|
|
|
// REPLACE if word is in replacelist
|
|
|
|
// MISTAKE if word is misspelled
|
|
|
|
{
|
|
|
|
word = "";
|
|
|
|
posinline=0;
|
|
|
|
|
|
|
|
sugg.clear();
|
|
|
|
|
|
|
|
if ( buffer[0] == '*' || buffer[0] == '+' || buffer[0] == '-' )
|
|
|
|
{
|
|
|
|
return GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( buffer[0] == '&' || buffer[0] == '?' || buffer[0] == '#' )
|
|
|
|
{
|
|
|
|
int i,j;
|
|
|
|
|
|
|
|
|
|
|
|
word = buffer.mid( 2, buffer.tqfind( ' ', 3 ) -2 );
|
|
|
|
//check() needs this
|
|
|
|
orig=word;
|
|
|
|
|
|
|
|
if( d->m_bIgnoreTitleCase && word == word.upper() )
|
|
|
|
return IGNORE;
|
|
|
|
|
|
|
|
if( d->m_bIgnoreUpperWords && word[0] == word[0].upper() )
|
|
|
|
{
|
|
|
|
TQString text = word[0] + word.right( word.length()-1 ).lower();
|
|
|
|
if( text == word )
|
|
|
|
return IGNORE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////// Ignore-list stuff //////////
|
|
|
|
//We don't take advantage of ispell's ignore function because
|
|
|
|
//we can't interrupt ispell's output (when checking a large
|
|
|
|
//buffer) to add a word to _it's_ ignore-list.
|
|
|
|
if ( ignorelist.tqfindIndex( word.lower() ) != -1 )
|
|
|
|
return IGNORE;
|
|
|
|
|
|
|
|
//// Position in line ///
|
|
|
|
TQString qs2;
|
|
|
|
|
|
|
|
if ( buffer.tqfind( ':' ) != -1 )
|
|
|
|
qs2 = buffer.left( buffer.tqfind(':') );
|
|
|
|
else
|
|
|
|
qs2 = buffer;
|
|
|
|
|
|
|
|
posinline = qs2.right( qs2.length()-qs2.tqfindRev(' ') ).toInt()-1;
|
|
|
|
|
|
|
|
///// Replace-list stuff ////
|
|
|
|
TQStringList::Iterator it = replacelist.begin();
|
|
|
|
for( ;it != replacelist.end(); ++it, ++it ) // Skip two entries at a time.
|
|
|
|
{
|
|
|
|
if ( word == *it ) // Word matches
|
|
|
|
{
|
|
|
|
++it;
|
|
|
|
word = *it; // Replace it with the next entry
|
|
|
|
return REPLACE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/////// Suggestions //////
|
|
|
|
if ( buffer[0] != '#' )
|
|
|
|
{
|
|
|
|
TQString qs = buffer.mid( buffer.tqfind(':')+2, buffer.length() );
|
|
|
|
qs += ',';
|
|
|
|
sugg.clear();
|
|
|
|
i = j = 0;
|
|
|
|
|
|
|
|
while( (unsigned int)i < qs.length() )
|
|
|
|
{
|
|
|
|
TQString temp = qs.mid( i, (j=qs.tqfind (',',i)) - i );
|
|
|
|
sugg.append( funnyWord(temp) );
|
|
|
|
|
|
|
|
i=j+2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( (sugg.count()==1) && (sugg.first() == word) )
|
|
|
|
return GOOD;
|
|
|
|
|
|
|
|
return MISTAKE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( buffer.isEmpty() ) {
|
|
|
|
kdDebug(750) << "Got an empty response: ignoring"<<endl;
|
|
|
|
return GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
kdError(750) << "HERE?: [" << buffer << "]" << endl;
|
|
|
|
kdError(750) << "Please report this to zack@kde.org" << endl;
|
|
|
|
kdError(750) << "Thank you!" << endl;
|
|
|
|
|
|
|
|
emit done( false );
|
|
|
|
emit done( KSpell::origbuffer );
|
|
|
|
return MISTAKE;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KSpell::checkList (TQStringList *_wordlist, bool _usedialog)
|
|
|
|
// prepare check of string list
|
|
|
|
{
|
|
|
|
wordlist=_wordlist;
|
|
|
|
if ((totalpos=wordlist->count())==0)
|
|
|
|
return false;
|
|
|
|
wlIt = wordlist->begin();
|
|
|
|
usedialog=_usedialog;
|
|
|
|
|
|
|
|
// prepare the dialog
|
|
|
|
setUpDialog();
|
|
|
|
|
|
|
|
//set the dialog signal handler
|
|
|
|
dialog3slot = TQT_SLOT (checkList4 ());
|
|
|
|
|
|
|
|
proc->writeStdin (TQString("%")); // turn off terse mode & check one word at a time
|
|
|
|
|
|
|
|
//lastpos now counts which *word number* we are at in checkListReplaceCurrent()
|
|
|
|
lastpos = -1;
|
|
|
|
checkList2();
|
|
|
|
|
|
|
|
// when checked, KProcIO calls checkList3a
|
|
|
|
OUTPUT(checkList3a);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::checkList2 ()
|
|
|
|
// send one word from the list to KProcIO
|
|
|
|
// invoked first time by checkList, later by checkListReplaceCurrent and checkList4
|
|
|
|
{
|
|
|
|
// send next word
|
|
|
|
if (wlIt != wordlist->end())
|
|
|
|
{
|
|
|
|
kdDebug(750) << "KS::cklist2 " << lastpos << ": " << *wlIt << endl;
|
|
|
|
|
|
|
|
d->endOfResponse = false;
|
|
|
|
bool put;
|
|
|
|
lastpos++; offset=0;
|
|
|
|
put = cleanFputsWord (*wlIt);
|
|
|
|
++wlIt;
|
|
|
|
|
|
|
|
// when cleanFPutsWord failed (e.g. on empty word)
|
|
|
|
// try next word; may be this is not good for other
|
|
|
|
// problems, because this will make read the list up to the end
|
|
|
|
if (!put) {
|
|
|
|
checkList2();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
// end of word list
|
|
|
|
{
|
|
|
|
NOOUTPUT(checkList3a);
|
|
|
|
ksdlg->hide();
|
|
|
|
emit done(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::checkList3a (KProcIO *)
|
|
|
|
// invoked by KProcIO, when data from ispell are read
|
|
|
|
{
|
|
|
|
//kdDebug(750) << "start of checkList3a" << endl;
|
|
|
|
|
|
|
|
// don't read more data, when dialog is waiting
|
|
|
|
// for user interaction
|
|
|
|
if ( dlgon ) {
|
|
|
|
//kdDebug(750) << "dlgon: don't read more data" << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int e, tempe;
|
|
|
|
|
|
|
|
TQString word;
|
|
|
|
TQString line;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
tempe=proc->readln( line, true ); //get ispell's response
|
|
|
|
|
|
|
|
//kdDebug(750) << "checkList3a: read bytes [" << tempe << "]" << endl;
|
|
|
|
|
|
|
|
|
|
|
|
if ( tempe == 0 ) {
|
|
|
|
d->endOfResponse = true;
|
|
|
|
//kdDebug(750) << "checkList3a: end of resp" << endl;
|
|
|
|
} else if ( tempe>0 ) {
|
|
|
|
if ( (e=parseOneResponse( line, word, sugg ) ) == MISTAKE ||
|
|
|
|
e==REPLACE )
|
|
|
|
{
|
|
|
|
dlgresult=-1;
|
|
|
|
|
|
|
|
if ( e == REPLACE )
|
|
|
|
{
|
|
|
|
TQString old = *(--wlIt); ++wlIt;
|
|
|
|
dlgreplacement = word;
|
|
|
|
checkListReplaceCurrent();
|
|
|
|
// inform application
|
|
|
|
emit corrected( old, *(--wlIt), lastpos ); ++wlIt;
|
|
|
|
}
|
|
|
|
else if( usedialog )
|
|
|
|
{
|
|
|
|
cwword = word;
|
|
|
|
dlgon = true;
|
|
|
|
// show the dialog
|
|
|
|
dialog( word, sugg, TQT_SLOT(checkList4()) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
d->m_bNoMisspellingsEncountered = false;
|
|
|
|
emit misspelling( word, sugg, lastpos );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
emitProgress (); //maybe
|
|
|
|
|
|
|
|
// stop when empty line or no more data
|
|
|
|
} while (tempe > 0);
|
|
|
|
|
|
|
|
//kdDebug(750) << "checkList3a: exit loop with [" << tempe << "]" << endl;
|
|
|
|
|
|
|
|
// if we got an empty line, t.e. end of ispell/aspell response
|
|
|
|
// and the dialog isn't waiting for user interaction, send next word
|
|
|
|
if (d->endOfResponse && !dlgon) {
|
|
|
|
//kdDebug(750) << "checkList3a: send next word" << endl;
|
|
|
|
checkList2();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::checkListReplaceCurrent()
|
|
|
|
{
|
|
|
|
|
|
|
|
// go back to misspelled word
|
|
|
|
wlIt--;
|
|
|
|
|
|
|
|
TQString s = *wlIt;
|
|
|
|
s.replace(posinline+offset,orig.length(),replacement());
|
|
|
|
offset += replacement().length()-orig.length();
|
|
|
|
wordlist->insert (wlIt, s);
|
|
|
|
wlIt = wordlist->remove (wlIt);
|
|
|
|
// wlIt now points to the word after the repalced one
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::checkList4 ()
|
|
|
|
// evaluate dialog return, when a button was pressed there
|
|
|
|
{
|
|
|
|
dlgon=false;
|
|
|
|
TQString old;
|
|
|
|
|
|
|
|
disconnect (this, TQT_SIGNAL (dialog3()), this, TQT_SLOT (checkList4()));
|
|
|
|
|
|
|
|
//others should have been processed by dialog() already
|
|
|
|
switch (dlgresult)
|
|
|
|
{
|
|
|
|
case KS_REPLACE:
|
|
|
|
case KS_REPLACEALL:
|
|
|
|
kdDebug(750) << "KS: cklist4: lastpos: " << lastpos << endl;
|
|
|
|
old = *(--wlIt);
|
|
|
|
++wlIt;
|
|
|
|
// replace word
|
|
|
|
checkListReplaceCurrent();
|
|
|
|
emit corrected( old, *(--wlIt), lastpos );
|
|
|
|
++wlIt;
|
|
|
|
break;
|
|
|
|
case KS_CANCEL:
|
|
|
|
ksdlg->hide();
|
|
|
|
emit done( false );
|
|
|
|
return;
|
|
|
|
case KS_STOP:
|
|
|
|
ksdlg->hide();
|
|
|
|
emit done( true );
|
|
|
|
return;
|
|
|
|
case KS_CONFIG:
|
|
|
|
ksdlg->hide();
|
|
|
|
emit done( false );
|
|
|
|
//check( origbuffer.mid( lastpos ), true );
|
|
|
|
//trystart = 0;
|
|
|
|
//proc->disconnect();
|
|
|
|
//proc->kill();
|
|
|
|
//delete proc;
|
|
|
|
//proc = new KProcIO( codec );
|
|
|
|
//startIspell();
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
|
|
|
// read more if there is more, otherwise send next word
|
|
|
|
if (!d->endOfResponse) {
|
|
|
|
//kdDebug(750) << "checkList4: read more from response" << endl;
|
|
|
|
checkList3a(NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KSpell::check( const TQString &_buffer, bool _usedialog )
|
|
|
|
{
|
|
|
|
TQString qs;
|
|
|
|
|
|
|
|
usedialog = _usedialog;
|
|
|
|
setUpDialog();
|
|
|
|
//set the dialog signal handler
|
|
|
|
dialog3slot = TQT_SLOT(check3());
|
|
|
|
|
|
|
|
kdDebug(750) << "KS: check" << endl;
|
|
|
|
origbuffer = _buffer;
|
|
|
|
if ( ( totalpos = origbuffer.length() ) == 0 )
|
|
|
|
{
|
|
|
|
emit done( origbuffer );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Torben: I corrected the \n\n problem directly in the
|
|
|
|
// origbuffer since I got errors otherwise
|
|
|
|
if ( !origbuffer.endsWith("\n\n" ) )
|
|
|
|
{
|
|
|
|
if (origbuffer.tqat(origbuffer.length()-1)!='\n')
|
|
|
|
{
|
|
|
|
origbuffer+='\n';
|
|
|
|
origbuffer+='\n'; //shouldn't these be removed at some point?
|
|
|
|
}
|
|
|
|
else
|
|
|
|
origbuffer+='\n';
|
|
|
|
}
|
|
|
|
|
|
|
|
newbuffer = origbuffer;
|
|
|
|
|
|
|
|
// KProcIO calls check2 when read from ispell
|
|
|
|
OUTPUT( check2 );
|
|
|
|
proc->writeStdin(TQString("!"));
|
|
|
|
|
|
|
|
//lastpos is a position in newbuffer (it has offset in it)
|
|
|
|
offset = lastlastline = lastpos = lastline = 0;
|
|
|
|
|
|
|
|
emitProgress();
|
|
|
|
|
|
|
|
// send first buffer line
|
|
|
|
int i = origbuffer.tqfind( '\n', 0 ) + 1;
|
|
|
|
qs = origbuffer.mid( 0, i );
|
|
|
|
cleanFputs( qs, false );
|
|
|
|
|
|
|
|
lastline=i; //the character position, not a line number
|
|
|
|
|
|
|
|
if ( usedialog )
|
|
|
|
{
|
|
|
|
emitProgress();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ksdlg->hide();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KSpell::check2( KProcIO * )
|
|
|
|
// invoked by KProcIO when read from ispell
|
|
|
|
{
|
|
|
|
int e, tempe;
|
|
|
|
TQString word;
|
|
|
|
TQString line;
|
|
|
|
static bool recursive = false;
|
|
|
|
if (recursive &&
|
|
|
|
!ksdlg )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
recursive = true;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
tempe = proc->readln( line, false ); //get ispell's response
|
|
|
|
//kdDebug(750) << "KSpell::check2 (" << tempe << "b)" << endl;
|
|
|
|
|
|
|
|
if ( tempe>0 )
|
|
|
|
{
|
|
|
|
if ( ( e=parseOneResponse (line, word, sugg) )==MISTAKE ||
|
|
|
|
e==REPLACE)
|
|
|
|
{
|
|
|
|
dlgresult=-1;
|
|
|
|
|
|
|
|
// for multibyte encoding posinline needs correction
|
|
|
|
if ((ksconfig->encoding() == KS_E_UTF8) && !d->aspellV6) {
|
|
|
|
// kdDebug(750) << "line: " << origbuffer.mid(lastlastline,
|
|
|
|
// lastline-lastlastline) << endl;
|
|
|
|
// kdDebug(750) << "posinline uncorr: " << posinline << endl;
|
|
|
|
|
|
|
|
// convert line to UTF-8, cut at pos, convert back to UCS-2
|
|
|
|
// and get string length
|
|
|
|
posinline = (TQString::fromUtf8(
|
|
|
|
origbuffer.mid(lastlastline,lastline-lastlastline).utf8(),
|
|
|
|
posinline)).length();
|
|
|
|
// kdDebug(750) << "posinline corr: " << posinline << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
lastpos = posinline+lastlastline+offset;
|
|
|
|
|
|
|
|
//orig is set by parseOneResponse()
|
|
|
|
|
|
|
|
if (e==REPLACE)
|
|
|
|
{
|
|
|
|
dlgreplacement=word;
|
|
|
|
emit corrected( orig, replacement(), lastpos );
|
|
|
|
offset += replacement().length()-orig.length();
|
|
|
|
newbuffer.replace( lastpos, orig.length(), word );
|
|
|
|
}
|
|
|
|
else //MISTAKE
|
|
|
|
{
|
|
|
|
cwword = word;
|
|
|
|
//kdDebug(750) << "(Before dialog) word=[" << word << "] cwword =[" << cwword << "]\n" << endl;
|
|
|
|
if ( usedialog ) {
|
|
|
|
// show the word in the dialog
|
|
|
|
dialog( word, sugg, TQT_SLOT(check3()) );
|
|
|
|
} else {
|
|
|
|
// No dialog, just emit misspelling and continue
|
|
|
|
d->m_bNoMisspellingsEncountered = false;
|
|
|
|
emit misspelling( word, sugg, lastpos );
|
|
|
|
dlgresult = KS_IGNORE;
|
|
|
|
check3();
|
|
|
|
}
|
|
|
|
recursive = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
emitProgress(); //maybe
|
|
|
|
|
|
|
|
} while( tempe>0 );
|
|
|
|
|
|
|
|
if ( tempe == -1 ) { //we were called, but no data seems to be ready...
|
|
|
|
// Make sure we don't get called directly again and make sure we do get
|
|
|
|
// called when new data arrives.
|
|
|
|
NOOUTPUT( check2 );
|
|
|
|
proc->enableReadSignals(true);
|
|
|
|
OUTPUT( check2 );
|
|
|
|
recursive = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
proc->ackRead();
|
|
|
|
|
|
|
|
//If there is more to check, then send another line to ISpell.
|
|
|
|
if ( (unsigned int)lastline < origbuffer.length() )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
TQString qs;
|
|
|
|
|
|
|
|
//kdDebug(750) << "[EOL](" << tempe << ")[" << temp << "]" << endl;
|
|
|
|
|
|
|
|
lastpos = (lastlastline=lastline) + offset; //do we really want this?
|
|
|
|
i = origbuffer.tqfind('\n', lastline) + 1;
|
|
|
|
qs = origbuffer.mid( lastline, i-lastline );
|
|
|
|
cleanFputs( qs, false );
|
|
|
|
lastline = i;
|
|
|
|
recursive = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
//This is the end of it all
|
|
|
|
{
|
|
|
|
ksdlg->hide();
|
|
|
|
// kdDebug(750) << "check2() done" << endl;
|
|
|
|
newbuffer.truncate( newbuffer.length()-2 );
|
|
|
|
emitProgress();
|
|
|
|
emit done( newbuffer );
|
|
|
|
}
|
|
|
|
recursive = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::check3 ()
|
|
|
|
// evaluates the return value of the dialog
|
|
|
|
{
|
|
|
|
disconnect (this, TQT_SIGNAL (dialog3()), this, TQT_SLOT (check3()));
|
|
|
|
kdDebug(750) << "check3 [" << cwword << "] [" << replacement() << "] " << dlgresult << endl;
|
|
|
|
|
|
|
|
//others should have been processed by dialog() already
|
|
|
|
switch (dlgresult)
|
|
|
|
{
|
|
|
|
case KS_REPLACE:
|
|
|
|
case KS_REPLACEALL:
|
|
|
|
offset+=replacement().length()-cwword.length();
|
|
|
|
newbuffer.replace (lastpos, cwword.length(),
|
|
|
|
replacement());
|
|
|
|
emit corrected (dlgorigword, replacement(), lastpos);
|
|
|
|
break;
|
|
|
|
case KS_CANCEL:
|
|
|
|
// kdDebug(750) << "canceled\n" << endl;
|
|
|
|
ksdlg->hide();
|
|
|
|
emit done( origbuffer );
|
|
|
|
return;
|
|
|
|
case KS_CONFIG:
|
|
|
|
ksdlg->hide();
|
|
|
|
emit done( origbuffer );
|
|
|
|
KMessageBox::information( 0, i18n("You have to restart the dialog for changes to take effect") );
|
|
|
|
//check( origbuffer.mid( lastpos ), true );
|
|
|
|
return;
|
|
|
|
case KS_STOP:
|
|
|
|
ksdlg->hide();
|
|
|
|
//buffer=newbuffer);
|
|
|
|
emitProgress();
|
|
|
|
emit done (newbuffer);
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
|
|
|
proc->ackRead();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
KSpell::slotStopCancel (int result)
|
|
|
|
{
|
|
|
|
if (dialogwillprocess)
|
|
|
|
return;
|
|
|
|
|
|
|
|
kdDebug(750) << "KSpell::slotStopCancel [" << result << "]" << endl;
|
|
|
|
|
|
|
|
if (result==KS_STOP || result==KS_CANCEL)
|
|
|
|
if (!dialog3slot.isEmpty())
|
|
|
|
{
|
|
|
|
dlgresult=result;
|
|
|
|
connect (this, TQT_SIGNAL (dialog3()), this, dialog3slot.ascii());
|
|
|
|
emit dialog3();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KSpell::dialog( const TQString & word, TQStringList & sugg, const char *_slot )
|
|
|
|
{
|
|
|
|
dlgorigword = word;
|
|
|
|
|
|
|
|
dialog3slot = _slot;
|
|
|
|
dialogwillprocess = true;
|
|
|
|
connect( ksdlg, TQT_SIGNAL(command(int)), this, TQT_SLOT(dialog2(int)) );
|
|
|
|
TQString tmpBuf = newbuffer;
|
|
|
|
kdDebug(750)<<" position = "<<lastpos<<endl;
|
|
|
|
|
|
|
|
// extract a context string, replace all characters which might confuse
|
|
|
|
// the RichText display and highlight the possibly wrong word
|
|
|
|
TQString marker( "_MARKER_" );
|
|
|
|
tmpBuf.replace( lastpos, word.length(), marker );
|
|
|
|
TQString context = tmpBuf.mid(QMAX(lastpos-18,0), 2*18+marker.length());
|
|
|
|
context.replace( '\n',TQString::tqfromLatin1(" "));
|
|
|
|
context.replace( '<', TQString::tqfromLatin1("<") );
|
|
|
|
context.replace( '>', TQString::tqfromLatin1(">") );
|
|
|
|
context.replace( marker, TQString::tqfromLatin1("<b>%1</b>").arg( word ) );
|
|
|
|
context = "<qt>" + context + "</qt>";
|
|
|
|
|
|
|
|
ksdlg->init( word, &sugg, context );
|
|
|
|
d->m_bNoMisspellingsEncountered = false;
|
|
|
|
emit misspelling( word, sugg, lastpos );
|
|
|
|
|
|
|
|
emitProgress();
|
|
|
|
ksdlg->show();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::dialog2( int result )
|
|
|
|
{
|
|
|
|
TQString qs;
|
|
|
|
|
|
|
|
disconnect( ksdlg, TQT_SIGNAL(command(int)), this, TQT_SLOT(dialog2(int)) );
|
|
|
|
dialogwillprocess = false;
|
|
|
|
dlgresult = result;
|
|
|
|
ksdlg->standby();
|
|
|
|
|
|
|
|
dlgreplacement = ksdlg->replacement();
|
|
|
|
|
|
|
|
//process result here
|
|
|
|
switch ( dlgresult )
|
|
|
|
{
|
|
|
|
case KS_IGNORE:
|
|
|
|
emit ignoreword( dlgorigword );
|
|
|
|
break;
|
|
|
|
case KS_IGNOREALL:
|
|
|
|
// would be better to lower case only words with beginning cap
|
|
|
|
ignorelist.prepend( dlgorigword.lower() );
|
|
|
|
emit ignoreall( dlgorigword );
|
|
|
|
break;
|
|
|
|
case KS_ADD:
|
|
|
|
addPersonal( dlgorigword );
|
|
|
|
personaldict = true;
|
|
|
|
emit addword( dlgorigword );
|
|
|
|
// adding to pesonal dict takes effect at the next line, not the current
|
|
|
|
ignorelist.prepend( dlgorigword.lower() );
|
|
|
|
break;
|
|
|
|
case KS_REPLACEALL:
|
|
|
|
{
|
|
|
|
replacelist.append( dlgorigword );
|
|
|
|
TQString _replacement = replacement();
|
|
|
|
replacelist.append( _replacement );
|
|
|
|
emit replaceall( dlgorigword , _replacement );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case KS_SUGGEST:
|
|
|
|
checkWord( ksdlg->replacement(), false, true );
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
connect( this, TQT_SIGNAL(dialog3()), this, dialog3slot.ascii() );
|
|
|
|
emit dialog3();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
KSpell::~KSpell()
|
|
|
|
{
|
|
|
|
delete proc;
|
|
|
|
delete ksconfig;
|
|
|
|
delete ksdlg;
|
|
|
|
delete d->checkNextTimer;
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
KSpellConfig KSpell::ksConfig() const
|
|
|
|
{
|
|
|
|
ksconfig->setIgnoreList(ignorelist);
|
|
|
|
ksconfig->setReplaceAllList(replacelist);
|
|
|
|
return *ksconfig;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::cleanUp()
|
|
|
|
{
|
|
|
|
if ( m_status == Cleaning )
|
|
|
|
return; // Ignore
|
|
|
|
|
|
|
|
if ( m_status == Running )
|
|
|
|
{
|
|
|
|
if ( personaldict )
|
|
|
|
writePersonalDictionary();
|
|
|
|
m_status = Cleaning;
|
|
|
|
}
|
|
|
|
proc->closeStdin();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::ispellExit( KProcess* )
|
|
|
|
{
|
|
|
|
kdDebug() << "KSpell::ispellExit() " << m_status << endl;
|
|
|
|
|
|
|
|
if ( (m_status == Starting) && (trystart < maxtrystart) )
|
|
|
|
{
|
|
|
|
trystart++;
|
|
|
|
startIspell();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( m_status == Starting )
|
|
|
|
m_status = Error;
|
|
|
|
else if (m_status == Cleaning)
|
|
|
|
m_status = d->m_bNoMisspellingsEncountered ? FinishedNoMisspellingsEncountered : Finished;
|
|
|
|
else if ( m_status == Running )
|
|
|
|
m_status = Crashed;
|
|
|
|
else // Error, Finished, Crashed
|
|
|
|
return; // Dead already
|
|
|
|
|
|
|
|
kdDebug(750) << "Death" << endl;
|
|
|
|
TQTimer::singleShot( 0, this, TQT_SLOT(emitDeath()) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is always called from the event loop to make
|
|
|
|
// sure that the receiver can safely delete the
|
|
|
|
// KSpell object.
|
|
|
|
void KSpell::emitDeath()
|
|
|
|
{
|
|
|
|
bool deleteMe = autoDelete; // Can't access object after next call!
|
|
|
|
emit death();
|
|
|
|
if ( deleteMe )
|
|
|
|
deleteLater();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::setProgressResolution (unsigned int res)
|
|
|
|
{
|
|
|
|
progres=res;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::emitProgress ()
|
|
|
|
{
|
|
|
|
uint nextprog = (uint) (100.*lastpos/(double)totalpos);
|
|
|
|
|
|
|
|
if ( nextprog >= curprog )
|
|
|
|
{
|
|
|
|
curprog = nextprog;
|
|
|
|
emit progress( curprog );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::moveDlg( int x, int y )
|
|
|
|
{
|
|
|
|
TQPoint pt( x,y ), pt2;
|
|
|
|
pt2 = parent->mapToGlobal( pt );
|
|
|
|
ksdlg->move( pt2.x(),pt2.y() );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::setIgnoreUpperWords(bool _ignore)
|
|
|
|
{
|
|
|
|
d->m_bIgnoreUpperWords=_ignore;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::setIgnoreTitleCase(bool _ignore)
|
|
|
|
{
|
|
|
|
d->m_bIgnoreTitleCase=_ignore;
|
|
|
|
}
|
|
|
|
// --------------------------------------------------
|
|
|
|
// Stuff for modal (blocking) spell checking
|
|
|
|
//
|
|
|
|
// Written by Torben Weis <weis@kde.org>. So please
|
|
|
|
// send bug reports regarding the modal stuff to me.
|
|
|
|
// --------------------------------------------------
|
|
|
|
|
|
|
|
int
|
|
|
|
KSpell::modalCheck( TQString& text )
|
|
|
|
{
|
|
|
|
return modalCheck( text,0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
KSpell::modalCheck( TQString& text, KSpellConfig* _kcs )
|
|
|
|
{
|
|
|
|
modalreturn = 0;
|
|
|
|
modaltext = text;
|
|
|
|
|
|
|
|
KSpell* spell = new KSpell( 0L, i18n("Spell Checker"), 0 ,
|
|
|
|
0, _kcs, true, true );
|
|
|
|
|
|
|
|
while (spell->status()!=Finished)
|
|
|
|
kapp->processEvents();
|
|
|
|
|
|
|
|
text = modaltext;
|
|
|
|
|
|
|
|
delete spell;
|
|
|
|
return modalreturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::slotSpellCheckerCorrected( const TQString & oldText, const TQString & newText, unsigned int pos )
|
|
|
|
{
|
|
|
|
modaltext=modaltext.replace(pos,oldText.length(),newText);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KSpell::slotModalReady()
|
|
|
|
{
|
|
|
|
//kdDebug() << tqApp->loopLevel() << endl;
|
|
|
|
//kdDebug(750) << "MODAL READY------------------" << endl;
|
|
|
|
|
|
|
|
Q_ASSERT( m_status == Running );
|
|
|
|
connect( this, TQT_SIGNAL( done( const TQString & ) ),
|
|
|
|
this, TQT_SLOT( slotModalDone( const TQString & ) ) );
|
|
|
|
TQObject::connect( this, TQT_SIGNAL( corrected( const TQString&, const TQString&, unsigned int ) ),
|
|
|
|
this, TQT_SLOT( slotSpellCheckerCorrected( const TQString&, const TQString &, unsigned int ) ) );
|
|
|
|
TQObject::connect( this, TQT_SIGNAL( death() ),
|
|
|
|
this, TQT_SLOT( slotModalSpellCheckerFinished( ) ) );
|
|
|
|
check( modaltext );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::slotModalDone( const TQString &/*_buffer*/ )
|
|
|
|
{
|
|
|
|
//kdDebug(750) << "MODAL DONE " << _buffer << endl;
|
|
|
|
//modaltext = _buffer;
|
|
|
|
cleanUp();
|
|
|
|
|
|
|
|
//kdDebug() << "ABOUT TO EXIT LOOP" << endl;
|
|
|
|
//tqApp->exit_loop();
|
|
|
|
|
|
|
|
//modalWidgetHack->close(true);
|
|
|
|
slotModalSpellCheckerFinished();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::slotModalSpellCheckerFinished( )
|
|
|
|
{
|
|
|
|
modalreturn=(int)this->status();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KSpell::initialize( TQWidget *_parent, const TQString &_caption,
|
|
|
|
TQObject *obj, const char *slot, KSpellConfig *_ksc,
|
|
|
|
bool _progressbar, bool _modal, SpellerType type )
|
|
|
|
{
|
|
|
|
d = new KSpellPrivate;
|
|
|
|
|
|
|
|
d->m_bIgnoreUpperWords =false;
|
|
|
|
d->m_bIgnoreTitleCase =false;
|
|
|
|
d->m_bNoMisspellingsEncountered = true;
|
|
|
|
d->type = type;
|
|
|
|
d->checking = false;
|
|
|
|
d->aspellV6 = false;
|
|
|
|
d->checkNextTimer = new TQTimer( this );
|
|
|
|
connect( d->checkNextTimer, TQT_SIGNAL( timeout() ),
|
|
|
|
this, TQT_SLOT( checkNext() ));
|
|
|
|
autoDelete = false;
|
|
|
|
modaldlg = _modal;
|
|
|
|
progressbar = _progressbar;
|
|
|
|
|
|
|
|
proc = 0;
|
|
|
|
ksconfig = 0;
|
|
|
|
ksdlg = 0;
|
|
|
|
lastpos = 0;
|
|
|
|
|
|
|
|
//won't be using the dialog in ksconfig, just the option values
|
|
|
|
if ( _ksc )
|
|
|
|
ksconfig = new KSpellConfig( *_ksc );
|
|
|
|
else
|
|
|
|
ksconfig = new KSpellConfig;
|
|
|
|
|
|
|
|
codec = 0;
|
|
|
|
switch ( ksconfig->encoding() )
|
|
|
|
{
|
|
|
|
case KS_E_LATIN1:
|
|
|
|
codec = TQTextCodec::codecForName("ISO 8859-1");
|
|
|
|
break;
|
|
|
|
case KS_E_LATIN2:
|
|
|
|
codec = TQTextCodec::codecForName("ISO 8859-2");
|
|
|
|
break;
|
|
|
|
case KS_E_LATIN3:
|
|
|
|
codec = TQTextCodec::codecForName("ISO 8859-3");
|
|
|
|
break;
|
|
|
|
case KS_E_LATIN4:
|
|
|
|
codec = TQTextCodec::codecForName("ISO 8859-4");
|
|
|
|
break;
|
|
|
|
case KS_E_LATIN5:
|
|
|
|
codec = TQTextCodec::codecForName("ISO 8859-5");
|
|
|
|
break;
|
|
|
|
case KS_E_LATIN7:
|
|
|
|
codec = TQTextCodec::codecForName("ISO 8859-7");
|
|
|
|
break;
|
|
|
|
case KS_E_LATIN8:
|
|
|
|
codec = TQTextCodec::codecForName("ISO 8859-8-i");
|
|
|
|
break;
|
|
|
|
case KS_E_LATIN9:
|
|
|
|
codec = TQTextCodec::codecForName("ISO 8859-9");
|
|
|
|
break;
|
|
|
|
case KS_E_LATIN13:
|
|
|
|
codec = TQTextCodec::codecForName("ISO 8859-13");
|
|
|
|
break;
|
|
|
|
case KS_E_LATIN15:
|
|
|
|
codec = TQTextCodec::codecForName("ISO 8859-15");
|
|
|
|
break;
|
|
|
|
case KS_E_UTF8:
|
|
|
|
codec = TQTextCodec::codecForName("UTF-8");
|
|
|
|
break;
|
|
|
|
case KS_E_KOI8R:
|
|
|
|
codec = TQTextCodec::codecForName("KOI8-R");
|
|
|
|
break;
|
|
|
|
case KS_E_KOI8U:
|
|
|
|
codec = TQTextCodec::codecForName("KOI8-U");
|
|
|
|
break;
|
|
|
|
case KS_E_CP1251:
|
|
|
|
codec = TQTextCodec::codecForName("CP1251");
|
|
|
|
break;
|
|
|
|
case KS_E_CP1255:
|
|
|
|
codec = TQTextCodec::codecForName("CP1255");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
kdDebug(750) << __FILE__ << ":" << __LINE__ << " Codec = " << (codec ? codec->name() : "<default>") << endl;
|
|
|
|
|
|
|
|
// copy ignore list from ksconfig
|
|
|
|
ignorelist += ksconfig->ignoreList();
|
|
|
|
|
|
|
|
replacelist += ksconfig->replaceAllList();
|
|
|
|
texmode=dlgon=false;
|
|
|
|
m_status = Starting;
|
|
|
|
dialogsetup = false;
|
|
|
|
progres=10;
|
|
|
|
curprog=0;
|
|
|
|
|
|
|
|
dialogwillprocess = false;
|
|
|
|
dialog3slot = TQString::null;
|
|
|
|
|
|
|
|
personaldict = false;
|
|
|
|
dlgresult = -1;
|
|
|
|
|
|
|
|
caption = _caption;
|
|
|
|
|
|
|
|
parent = _parent;
|
|
|
|
|
|
|
|
trystart = 0;
|
|
|
|
maxtrystart = 2;
|
|
|
|
|
|
|
|
if ( obj && slot )
|
|
|
|
// caller wants to know when kspell is ready
|
|
|
|
connect( this, TQT_SIGNAL(ready(KSpell *)), obj, slot);
|
|
|
|
else
|
|
|
|
// Hack for modal spell checking
|
|
|
|
connect( this, TQT_SIGNAL(ready(KSpell *)), this, TQT_SLOT(slotModalReady()) );
|
|
|
|
|
|
|
|
proc = new KProcIO( codec );
|
|
|
|
|
|
|
|
startIspell();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KSpell::modaltext;
|
|
|
|
int KSpell::modalreturn = 0;
|
|
|
|
TQWidget* KSpell::modalWidgetHack = 0;
|
|
|
|
|
|
|
|
#include "kspell.moc"
|
|
|
|
|