00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
#ifdef HAVE_CONFIG_H
00021
#include <config.h>
00022
#endif
00023
00024
00025
#include <qtextcodec.h>
00026
#include <qtimer.h>
00027
#include <kdebug.h>
00028
#include <klocale.h>
00029
#include <kprocio.h>
00030
#include "kospell.h"
00031
#include <qfileinfo.h>
00032
#include <qdir.h>
00033
#include <kglobal.h>
00034
#define MAXLINELENGTH 10000
00035
00036
class KoSpell::KoSpellPrivate
00037 {
00038
public:
00039
bool endOfResponse;
00040
bool m_bIgnoreUpperWords;
00041
bool m_bIgnoreTitleCase;
00042 };
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
#define OUTPUT(x) (connect (proc, SIGNAL (readReady(KProcIO *)), this, SLOT (x(KProcIO *))))
00062
00063
00064
#define NOOUTPUT(x) (disconnect (proc, SIGNAL (readReady(KProcIO *)), this, SLOT (x(KProcIO *))))
00065
00066
00067
00068 KoSpell::KoSpell(
QWidget *,
QObject *obj,
const char *slot, KSpellConfig *_ksc)
00069 {
00070 d=
new KoSpellPrivate;
00071
00072 d->m_bIgnoreUpperWords=
false;
00073 d->m_bIgnoreTitleCase=
false;
00074
00075 proc=0;
00076 ksconfig=0;
00077
00078
if (_ksc!=0)
00079 ksconfig =
new KSpellConfig(*_ksc);
00080
else
00081 ksconfig =
new KSpellConfig;
00082 codec = 0;
00083
switch (ksconfig->encoding())
00084 {
00085
case KS_E_LATIN1:
00086 codec = QTextCodec::codecForName(
"ISO 8859-1");
00087
break;
00088
case KS_E_LATIN2:
00089 codec = QTextCodec::codecForName(
"ISO 8859-2");
00090
break;
00091
case KS_E_LATIN3:
00092 codec = QTextCodec::codecForName(
"ISO 8859-3");
00093
break;
00094
case KS_E_LATIN4:
00095 codec = QTextCodec::codecForName(
"ISO 8859-4");
00096
break;
00097
case KS_E_LATIN5:
00098 codec = QTextCodec::codecForName(
"ISO 8859-5");
00099
break;
00100
case KS_E_LATIN7:
00101 codec = QTextCodec::codecForName(
"ISO 8859-7");
00102
break;
00103
case KS_E_LATIN8:
00104 codec = QTextCodec::codecForName(
"ISO 8859-8");
00105
break;
00106
case KS_E_LATIN9:
00107 codec = QTextCodec::codecForName(
"ISO 8859-9");
00108
break;
00109
case KS_E_LATIN13:
00110 codec = QTextCodec::codecForName(
"ISO 8859-13");
00111
break;
00112
case KS_E_LATIN15:
00113 codec = QTextCodec::codecForName(
"ISO 8859-15");
00114
break;
00115
case KS_E_UTF8:
00116 codec = QTextCodec::codecForName(
"UTF-8");
00117
break;
00118
case KS_E_KOI8R:
00119 codec = QTextCodec::codecForName(
"KOI8-R");
00120
break;
00121
case KS_E_KOI8U:
00122 codec = QTextCodec::codecForName(
"KOI8-U");
00123
break;
00124
default:
00125
break;
00126 }
00127
00128 kdDebug(32500) << __FILE__ <<
":" << __LINE__ <<
" Codec = " << (codec ? codec->name() :
"<default>") << endl;
00129
00130 m_status = Starting;
00131
00132 ignorelist += ksconfig->ignoreList();
00133
00134
00135
if ( obj && slot )
00136 connect (
this, SIGNAL(
ready(
KoSpell *)), obj, slot);
00137
00138 proc=
new KProcIO(codec);
00139
00140 trystart=0;
00141 maxtrystart=2;
00142 startIspell();
00143 }
00144
00145
00146
void KoSpell::startIspell()
00147 {
00148 kdDebug(32500) <<
"Try #" << trystart << endl;
00149
if (trystart>0)
00150 proc->resetAll();
00151
00152
switch (ksconfig->client())
00153 {
00154
case KS_CLIENT_ISPELL:
00155 *proc <<
"ispell";
00156 kdDebug(32500) <<
"Using ispell" << endl;
00157
break;
00158
case KS_CLIENT_ASPELL:
00159 *proc <<
"aspell";
00160 kdDebug(32500) <<
"Using aspell" << endl;
00161
break;
00162 }
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172 *proc <<
"-a" <<
"-S";
00173
if (ksconfig->noRootAffix())
00174 *proc<<
"-m";
00175
00176
if (ksconfig->runTogether())
00177 *proc <<
"-B";
00178
else
00179 *proc <<
"-C";
00180
00181
if (trystart<2)
00182 {
00183
if (! ksconfig->dictionary().isEmpty())
00184 {
00185 kdDebug(32500) <<
"using dictionary [" << ksconfig->dictionary() <<
"]" << endl;
00186 *proc <<
"-d";
00187 *proc << ksconfig->dictionary();
00188 }
00189 }
00190
00191
00192
00193
00194
00195
00196
if (trystart<1)
00197
switch (ksconfig->encoding())
00198 {
00199
case KS_E_LATIN1:
00200 *proc <<
"-Tlatin1";
00201
break;
00202
case KS_E_LATIN2:
00203 *proc <<
"-Tlatin2";
00204
break;
00205
case KS_E_LATIN3:
00206 *proc <<
"-Tlatin3";
00207
break;
00208
00209
00210
case KS_E_LATIN4:
00211
case KS_E_LATIN5:
00212
case KS_E_LATIN7:
00213
case KS_E_LATIN8:
00214
case KS_E_LATIN9:
00215
case KS_E_LATIN13:
00216
case KS_E_LATIN15:
00217
00218 kdError() <<
"charsets iso-8859-4 .. iso-8859-15 not supported yet" << endl;
00219
break;
00220
00221
case KS_E_UTF8:
00222 *proc <<
"-Tutf8";
00223
break;
00224
00225
case KS_E_KOI8U:
00226 *proc <<
"-w'";
00227
break;
00228 }
00229
00230
if(trystart==0)
00231 {
00232 connect(proc, SIGNAL (receivedStderr (KProcess *,
char *,
int)),
00233
this, SLOT (ispellErrors (KProcess *,
char *,
int)));
00234
00235 connect(proc, SIGNAL(processExited(KProcess *)),
00236
this, SLOT (ispellExit (KProcess *)));
00237
00238 OUTPUT(KoSpell2);
00239 }
00240
00241
if(!proc->start())
00242 {
00243 m_status = Error;
00244 QTimer::singleShot(0,
this, SLOT(emitDeath()));
00245 }
00246 }
00247
00248
void KoSpell::ispellErrors(KProcess *,
char *buffer,
int buflen)
00249 {
00250 buffer [buflen-1] =
'\0';
00251 kdDebug(32500) <<
"ispellErrors [" << buffer <<
"]\n" << endl;
00252 }
00253
00254
void KoSpell::KoSpell2 (KProcIO *)
00255 {
00256 kdDebug(32500) <<
"KoSpell::KoSpell2" << endl;
00257
00258
QString line;
00259
00260
if(proc->fgets(line,
true)==-1)
00261 {
00262 QTimer::singleShot(0,
this, SLOT(emitDeath()));
00263
return;
00264 }
00265
00266
if(line[0]!=
'@')
00267 {
00268 QTimer::singleShot(0,
this, SLOT(emitDeath()));
00269
return;
00270 }
00271
00272
00273 proc->fputs(
"!");
00274
00275 NOOUTPUT (KoSpell2);
00276 OUTPUT(check2);
00277
00278 m_status = Running;
00279 emit
ready(
this);
00280 }
00281
00282 bool KoSpell::addPersonal(
const QString & word)
00283 {
00284
QString w = word;
00285
00286
00287
if(w.find (
' ')!=-1 || w.isEmpty())
00288
return false;
00289
00290 w.prepend (
"*");
00291 w.append(
"\n#" );
00292
00293
return proc->fputs(w);
00294 }
00295
00296
bool KoSpell::writePersonalDictionary()
00297 {
00298
return proc->fputs(
"#");
00299 }
00300
00301 bool KoSpell::ignore(
const QString & word)
00302 {
00303
QString qs = word.simplifyWhiteSpace();
00304
00305
00306
if (qs.find (
' ')!=-1 || qs.isEmpty())
00307
return FALSE;
00308
00309 qs.prepend (
"@");
00310
00311
return proc->fputs(qs);
00312 }
00313
00314
00315
00316
00317
QString KoSpell::funnyWord(
const QString & word)
00318 {
00319
QString qs;
00320
unsigned int i=0;
00321
00322
for (i=0; i<word.length(); i++)
00323 {
00324
if (word[i]==
'+')
00325
continue;
00326
00327
if (word [i]==
'-')
00328 {
00329
QString shorty;
00330
unsigned int j;
00331
int k;
00332
00333
for(j=i+1; j<word.length() && word [j]!=
'+' && word [j]!=
'-'; j++)
00334 shorty+=word [j];
00335 i=j-1;
00336
00337
if ((k=qs.findRev (shorty))==0 || k!=-1)
00338 qs.remove (k,shorty.length());
00339
else
00340 {
00341 qs+=
'-';
00342 qs+=shorty;
00343 }
00344 }
00345
else
00346 qs+=word [i];
00347 }
00348
return qs;
00349 }
00350
00351 KoSpell::Spelling KoSpell::parseLine(
const QString &line,
QString &word,
int &pos)
00352 {
00353
bool skip =
false;
00354
00355
00356
if(line.isEmpty())
00357
return SpellingDone;
00358
00359
QChar ch = line[0];
00360
switch(ch)
00361 {
00362
case '*':
00363
case '+':
00364
case '-':
00365
return SpellingOk;
00366
case '&':
00367
case '?':
00368 skip =
true;
00369
case '#':
00370 {
00371
int p = line.find(
QChar(
' '), 2);
00372 word = line.mid(2, p-2);
00373 p++;
00374
if(skip)
00375 {
00376
while(line[p].isDigit())
00377 p++;
00378 p++;
00379 }
00380
int l=0;
00381
while(line[p+l].isDigit())
00382 l++;
00383
bool ok=
true;
00384 pos = line.mid(p,l).toInt(&ok);
00385
00386
00387
00388
return Misspelled;
00389 }
00390
default:
00391
return SpellingError;
00392 }
00393
return SpellingError;
00394 }
00395
00396 bool KoSpell::check(
const QString &buffer)
00397 {
00398
00399
if(buffer.isEmpty())
00400 {
00401
00402 emit done();
00403
return true;
00404 }
00405
00406
00407
QString buf( buffer );
00408 buf.replace(
'\n',
' ' );
00409
00410
00411 m_buffer << buf;
00412
00413 proc->fputs(
"^",
false);
00414 proc->fputs(buf);
00415
00416
return true;
00417 }
00418
00419
00420
void KoSpell::check2(KProcIO *)
00421 {
00422
00423
QString line;
00424
int bytes;
00425
while((bytes=proc->fgets(line,
true)) >= 0)
00426 {
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
int pos=0;
00439
QString word;
00440 Spelling spelling = parseLine(line, word, pos);
00441
if(word.length()>1 && d->m_bIgnoreTitleCase && word==word.upper())
00442 {
00443 spelling=SpellingIgnore;
00444 }
00445
00446
if(word.length()>1 && d->m_bIgnoreUpperWords && word[0]==word[0].upper())
00447 {
00448
QString text=word[0]+word.right(word.length()-1).lower();
00449
if(text==word)
00450 spelling=SpellingIgnore;
00451 }
00452
if (ignorelist.findIndex(word.lower())!=-1)
00453 spelling=SpellingIgnore;
00454
00455
switch(spelling)
00456 {
00457
case Misspelled:
00458 {
00459
QString buffer = m_buffer.front();
00460 pos--;
00461
00462
00463
if ( ksconfig->encoding() == KS_E_UTF8 )
00464 {
00465
for(
int i=0; i < pos; i++)
00466 {
00467 ushort u = buffer[i].unicode();
00468
if(u > 0x7f)
00469 pos--;
00470
else if(u > 0x7ff)
00471 pos-=2;
00472
00473
00474
00475
00476 }
00477 }
00478
00479 emit
misspelling(word, pos);
00480
break;
00481 }
00482
00483
case SpellingDone:
00484
00485 Q_ASSERT(!m_buffer.isEmpty());
00486
if (!m_buffer.isEmpty())
00487 m_buffer.pop_front();
00488 emit done();
00489
break;
00490
case SpellingIgnore:
00491
break;
00492
default:
00493 kdDebug(32500) <<
"KoSpell::check2() ERROR" << endl;
00494
break;
00495 }
00496 }
00497
00498
00499 }
00500
00501 KoSpell:: ~KoSpell ()
00502 {
00503
delete d;
00504
delete proc;
00505
delete ksconfig;
00506 }
00507
00508 KSpellConfig
KoSpell::ksConfig ()
const
00509
{
00510 ksconfig->setIgnoreList(ignorelist);
00511
return *ksconfig;
00512 }
00513
00514 void KoSpell::cleanUp ()
00515 {
00516
if (m_status == Cleaning)
return;
00517
if (m_status == Running)
00518 {
00519 m_status = Cleaning;
00520 }
00521 proc->closeStdin();
00522 }
00523
00524
void KoSpell::ispellExit(KProcess *)
00525 {
00526 kdDebug(32500) <<
"KoSpell::ispellExit() " << m_status << endl;
00527
00528
if ((m_status == Starting) && (trystart<maxtrystart))
00529 {
00530 trystart++;
00531 startIspell();
00532
return;
00533 }
00534
00535
if (m_status == Starting)
00536 m_status = Error;
00537
else if (m_status == Cleaning)
00538 m_status = Finished;
00539
else if (m_status == Running)
00540 m_status = Crashed;
00541
else
00542
return;
00543
00544 kdDebug(32500) <<
"Death" << endl;
00545 QTimer::singleShot( 0,
this, SLOT(emitDeath()));
00546 }
00547
00548
00549
00550
00551
void KoSpell::emitDeath()
00552 {
00553
00554 emit
death();
00555
00556
00557 }
00558
00559 void KoSpell::setIgnoreUpperWords(
bool _ignore)
00560 {
00561 d->m_bIgnoreUpperWords=_ignore;
00562 }
00563
00564 void KoSpell::setIgnoreTitleCase(
bool _ignore)
00565 {
00566 d->m_bIgnoreTitleCase=_ignore;
00567 }
00568
00569
00570
00571
QStringList KoSpell::getAvailDictsIspell ()
00572 {
00573
QStringList listIspell;
00574
00575
QFileInfo dir (
"/usr/lib/ispell");
00576
if (!dir.exists() || !dir.isDir())
00577 dir.setFile (
"/usr/local/lib/ispell");
00578
if (!dir.exists() || !dir.isDir())
00579 dir.setFile (
"/usr/local/share/ispell");
00580
if (!dir.exists() || !dir.isDir())
00581 dir.setFile (
"/usr/share/ispell");
00582
00583
00584
00585
00586
00587
if (!dir.exists() || !dir.isDir())
return QStringList();
00588
00589 kdDebug(32500) <<
"KoSpell::getAvailDictsIspell "
00590 << dir.filePath() <<
" " << dir.dirPath() << endl;
00591
00592
QDir thedir (dir.filePath(),
"*.hash");
00593
00594 kdDebug(32500) <<
"KoSpell" << thedir.path() <<
"\n" << endl;
00595 kdDebug(32500) <<
"entryList().count()="
00596 << thedir.entryList().count() << endl;
00597
00598
for (
unsigned int i=0;i<thedir.entryList().count();i++)
00599 {
00600
QString fname, lname, hname;
00601 fname = thedir [i];
00602
00603
00604
if (fname.right(5) ==
".hash") fname.remove (fname.length()-5,5);
00605
00606
if (interpret (fname, lname, hname))
00607 {
00608 hname=i18n(
"default spelling dictionary"
00609 ,
"Default - %1 [%2]").arg(hname).arg(fname);
00610 listIspell.append(hname);
00611 }
00612
else
00613 {
00614 hname=hname+
" ["+fname+
"]";
00615 listIspell.append(hname);
00616 }
00617 }
00618
return listIspell;
00619 }
00620
00621
QStringList KoSpell::getAvailDictsAspell () {
00622
00623
QStringList listAspell;
00624
00625
00626
00627
QFileInfo dir (
"/usr/lib/aspell");
00628
if (!dir.exists() || !dir.isDir())
00629 dir.setFile (
"/usr/local/lib/aspell");
00630
if (!dir.exists() || !dir.isDir())
00631 dir.setFile (
"/usr/share/aspell");
00632
if (!dir.exists() || !dir.isDir())
00633 dir.setFile (
"/usr/local/share/aspell");
00634
if (!dir.exists() || !dir.isDir())
return QStringList();
00635
00636 kdDebug(32500) <<
"KoSpell::getAvailDictsAspell "
00637 << dir.filePath() <<
" " << dir.dirPath() << endl;
00638
00639
QDir thedir (dir.filePath(),
"*");
00640
00641 kdDebug(32500) <<
"KSpellConfig" << thedir.path() <<
"\n" << endl;
00642 kdDebug(32500) <<
"entryList().count()="
00643 << thedir.entryList().count() << endl;
00644
00645
for (
unsigned int i=0; i<thedir.entryList().count(); i++)
00646 {
00647
QString fname, lname, hname;
00648 fname = thedir [i];
00649
00650
00651
00652
00653
if (fname[0] !=
'.' && fname.find(
'-') < 0)
00654 {
00655
00656
00657
if (fname.right(6) ==
".multi") fname.remove (fname.length()-6,6);
00658
00659
if (interpret (fname, lname, hname))
00660 {
00661 hname=i18n(
"default spelling dictionary"
00662 ,
"Default - %1 [%2]").arg(hname).arg(fname);
00663 listAspell.append(hname);
00664 }
00665
else
00666 {
00667 hname=hname+
" ["+fname+
"]";
00668 listAspell.append(hname);
00669 }
00670 }
00671 }
00672
return listAspell;
00673 }
00674
00675
00676
bool
00677 KoSpell::interpret (
QString &fname,
QString &lname,
00678
QString &hname)
00679
00680 {
00681
00682 kdDebug(750) <<
"KSpellConfig::interpret [" << fname <<
"]" << endl;
00683
00684
QString dname(fname);
00685
00686
if(dname.right(1)==
"+")
00687 dname.remove(dname.length()-1, 1);
00688
00689
if(dname.right(3)==
"sml" || dname.right(3)==
"med" || dname.right(3)==
"lrg" || dname.right(3)==
"xlg")
00690 dname.remove(dname.length()-3,3);
00691
00692
00693
if (dname==
"english" || dname==
"american" ||
00694 dname==
"british" || dname==
"canadian") {
00695 lname=
"en"; hname=i18n(
"English");
00696 }
00697
else if (dname==
"espa~nol" || dname==
"espanol") {
00698 lname=
"es"; hname=i18n(
"Spanish");
00699 }
00700
else if (dname==
"dansk") {
00701 lname=
"da"; hname=i18n(
"Danish");
00702 }
00703
else if (dname==
"deutsch") {
00704 lname=
"de"; hname=i18n(
"German");
00705 }
00706
else if (dname==
"german") {
00707 lname=
"de"; hname=i18n(
"German (new orth.)");
00708 }
00709
else if (dname==
"portuguesb" || dname==
"br") {
00710 lname=
"br"; hname=i18n(
"Brazilian Portuguese");
00711 }
00712
else if (dname==
"portugues") {
00713 lname=
"pt"; hname=i18n(
"Portuguese");
00714 }
00715
else if (dname==
"esperanto") {
00716 lname=
"eo"; hname=i18n(
"Esperanto");
00717 }
00718
else if (dname==
"norsk") {
00719 lname=
"no"; hname=i18n(
"Norwegian");
00720 }
00721
else if (dname==
"polish") {
00722 lname=
"pl"; hname=i18n(
"Polish");
00723 }
00724
else if (dname==
"russian") {
00725 lname=
"ru"; hname=i18n(
"Russian");
00726 }
00727
else if (dname==
"slovensko") {
00728 lname=
"si"; hname=i18n(
"Slovenian");
00729 }
00730
else if (dname==
"slovak"){
00731 lname=
"sk"; hname=i18n(
"Slovak");
00732 }
00733
else if (dname==
"czech") {
00734 lname=
"cs"; hname=i18n(
"Czech");
00735 }
00736
else if (dname==
"svenska") {
00737 lname=
"sv"; hname=i18n(
"Swedish");
00738 }
00739
else if (dname==
"swiss") {
00740 lname=
"de"; hname=i18n(
"Swiss German");
00741 }
00742
else if (dname==
"ukrainian") {
00743 lname=
"uk"; hname=i18n(
"Ukrainian");
00744 }
00745
else if (dname==
"lietuviu" || dname==
"lithuanian") {
00746 lname=
"lt"; hname=i18n(
"Lithuanian");
00747 }
00748
else if (dname==
"francais" || dname==
"french") {
00749 lname=
"fr"; hname=i18n(
"French");
00750 }
00751
else if (dname==
"belarusian") {
00752 lname=
"be"; hname=i18n(
"Belarusian");
00753 }
00754
else if( dname ==
"magyar" ) {
00755 lname=
"hu"; hname=i18n(
"Hungarian");
00756 }
00757
else {
00758 lname=
""; hname=i18n(
"Unknown ispell dictionary",
"Unknown");
00759 }
00760
00761
00762
if ( (KGlobal::locale()->language()==QString::fromLatin1(
"C") &&
00763 lname==QString::fromLatin1(
"en")) ||
00764 KGlobal::locale()->language()==lname)
00765
return TRUE;
00766
00767
return FALSE;
00768 }
00769
00770
00771
#include "kospell.moc"
00772