lib Library API Documentation

koaspell.cpp

00001 /* This file is part of the KDE libraries
00002    Copyright (C) 1997 David Sweet <dsweet@kde.org>
00003    Copyright (C) 2000-2001 Wolfram Diestel <wolfram@steloj.de>
00004    Copyright (C) 2002-2003 Laurent Montel <lmontel@mandrakesoft.com>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License version 2 as published by the Free Software Foundation.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018    Boston, MA 02111-1307, USA.
00019 */
00020 #ifdef HAVE_CONFIG_H
00021 #include <config.h>
00022 #endif
00023 
00024 #ifdef HAVE_LIBASPELL
00025 
00026 #include <stdio.h>
00027 #include <sys/time.h>
00028 #include <sys/types.h>
00029 #include <unistd.h>
00030 #include <ctype.h>
00031 #include <stdlib.h> // atoi
00032 
00033 #ifdef HAVE_STRINGS_H
00034 #include <strings.h>
00035 #endif
00036 
00037 #include <qtextcodec.h>
00038 #include <qtimer.h>
00039 #include <kapplication.h>
00040 #include <kdebug.h>
00041 #include <klocale.h>
00042 #include "koaspell.h"
00043 #include "koaspell.moc"
00044 #include "koSpell.h"
00045 #include "koSpelldlg.h"
00046 #include <kwin.h>
00047 #include <kprocio.h>
00048 
00049 #include <qtimer.h>
00050 
00051 #include <aspell.h>
00052 
00053 
00054 KOASpell::KOASpell( KOSpellConfig *_ksc )
00055     :KOSpell(_ksc)
00056 {
00057     initSpell(_ksc);
00058     initConfig();
00059     QTimer::singleShot( 0, this, SLOT( slotEmitCheckerReady() ) );
00060 }
00061 
00062 //TODO FIXME !!!! KOSpellConfig not used.
00063 void KOASpell::initSpell(KOSpellConfig *_ksc)
00064 {
00065     Q_UNUSED( _ksc );
00066     m_bIgnoreUpperWords=false;
00067     m_bIgnoreTitleCase=false;
00068     autocorrect = false;
00069     autoDelete = false;
00070     modaldlg = false;
00071     speller = 0L;
00072     config = 0L;
00073     offset = 0;
00074     ksdlg=0;
00075     lastpos = -1;
00076 
00077     personaldict=FALSE;
00078     dlgresult=-1;
00079 
00080     caption=QString::null;
00081 
00082     parent=0L;
00083 }
00084 
00085 KOASpell::KOASpell (QWidget *_parent, const QString &_caption,
00086         KOSpellConfig *_ksc,
00087         bool _modal,  bool _autocorrect, KOSpellerType _type)
00088     :KOSpell(_parent,_caption,_ksc,_modal,_autocorrect, _type )
00089 {
00090     initSpell(_ksc);
00091     autocorrect = _autocorrect;
00092     modaldlg = _modal;
00093     caption=_caption;
00094     parent=_parent;
00095 
00096     setUpDialog();
00097     QTimer::singleShot( 0, this, SLOT( slotEmitCheckerReady() ) );
00098 }
00099 
00100 void KOASpell::slotEmitCheckerReady()
00101 {
00102     emit ready( this );
00103     emit spellCheckerReady();
00104 }
00105 
00106 bool KOASpell::initConfig(const QString & language)
00107 {
00108     config = new_aspell_config();
00109     kdDebug(30006)<<" ksconfig->dictionary() :"<<ksconfig->dictionary()<<endl;
00110     Q_ASSERT( ksconfig->client() == KOS_CLIENT_ASPELL );
00111     aspell_config_replace(config, "lang", language.isEmpty() ? (ksconfig->dictionary().isEmpty() ? "en": ksconfig->dictionary().latin1()) : language.latin1() );
00112 
00113     kdDebug(30006)<<" ksconfig->dictionary() :"<<ksconfig->dictionary()<<endl;
00114 
00115     AspellCanHaveError * ret;
00116     ret = new_aspell_speller(config);
00117     if (aspell_error(ret) != 0) {
00118         kdDebug(30006)<<"Error :"<<aspell_error_message(ret)<<endl;
00119         delete_aspell_can_have_error(ret);
00120         return false;
00121     }
00154     aspell_config_replace(config, "ignore-case", ksconfig->ignoreCase()?"true" : "false" );
00155     aspell_config_replace(config, "ignore-accents", ksconfig->ignoreAccent()?"true" : "false" );
00156 
00157     ret = new_aspell_speller(config);
00158 
00159     delete_aspell_config(config);
00160 
00161     if (aspell_error(ret) != 0) {
00162         printf("Error: %s\n",aspell_error_message(ret));
00163         delete_aspell_can_have_error(ret);
00164         return false;
00165     }
00166     speller = to_aspell_speller(ret);
00167     config = aspell_speller_config(speller);
00168     return true;
00169 }
00170 
00171 void
00172 KOASpell::setUpDialog ()
00173 {
00174     if (ksdlg)
00175         return;
00176     bool ret = initConfig();
00177     if ( !ret )
00178         return;
00179 
00180     //Set up the dialog box
00181     ksdlg=new KOSpellDlg (parent, ksconfig,"dialog", KOSpellConfig::indexFromLanguageFileName( ksconfig->dictionary()),  modaldlg, autocorrect );
00182     ksdlg->setCaption (caption);
00183 #ifdef Q_WS_X11 // FIXME(E): Implement for Qt/Embedded
00184     KWin::setIcons (ksdlg->winId(), kapp->icon(), kapp->miniIcon());
00185 #endif
00186     if ( modaldlg )
00187         ksdlg->setFocus();
00188 }
00189 
00190 bool KOASpell::addPersonal (const QString & word)
00191 {
00192     if( !speller)
00193         return false;
00194     //add to aspell internal.
00195     aspell_speller_add_to_personal(speller, word.latin1(), word.length());
00196     //save directly into personnal dictionary.
00197     writePersonalDictionary();
00198     return true;
00199 }
00200 
00201 bool KOASpell::writePersonalDictionary ()
00202 {
00203     if( !speller)
00204         return false;
00205     aspell_speller_save_all_word_lists(speller);
00206     kdDebug(30006)<<"aspell_speller_error_message(speller) :"<<aspell_speller_error_message(speller)<<endl;
00207     return true;
00208 }
00209 
00210 bool KOASpell::ignore (const QString & /*word*/)
00211 {
00212     //fixme !!!!!!!!!!!!!!!!
00213     return true;
00214 }
00215 
00216 
00217 QStringList KOASpell::resultCheckWord( const QString &_word )
00218 {
00219     if (_word.isEmpty() || !speller)
00220         return QStringList();
00221     kdDebug(30006)<<" aspell_config_retrieve(config, lang) :"<<aspell_config_retrieve(config, "lang")<<endl;
00222     QStringList result;
00223     const AspellWordList *wl = aspell_speller_suggest(speller, _word.latin1(), -1);
00224     if (wl == 0) {
00225         kdDebug(30006)<<"Error: "<< aspell_speller_error_message(speller)<<endl;
00226     } else {
00227         AspellStringEnumeration * els = aspell_word_list_elements(wl);
00228         const char * word2;
00229         while ( (word2 = aspell_string_enumeration_next(els)) != 0) {
00230             result.append( word2 );
00231             kdDebug(30006)<<" word2 :"<<word2<<endl;
00232         }
00233     }
00234     return result;
00235 }
00236 
00237 bool KOASpell::spellWord( const QString &_word )
00238 {
00239     QStringList lst =resultCheckWord( _word );
00240     if ( lst.isEmpty() && ((lastpos >= (int)origbuffer.length()-1)|| lastpos<0) )
00241     {
00242         //change m_status before to emit signal otherwise
00243         //kword + multiframe doesn't work
00244         m_status = Finished;
00245         emit done( origbuffer );
00246         return false;
00247     }
00248     if ( lst.contains( _word ))
00249       return false;
00250 
00251     dialog( _word, lst);
00252     return true;
00253 }
00254 
00255 void KOASpell::nextWord()
00256 {
00257     QString word;
00258     lastpos++;
00259     bool haveAnNumber = false;
00260     do
00261     {
00262         int i =0;
00263         for ( i = lastpos; i<(int)origbuffer.length();i++)
00264         {
00265             QChar ch = origbuffer[i];
00266             if ( ch.isSpace() || ch.isPunct() )
00267                 break;
00268             if ( ch.isNumber() )
00269                 haveAnNumber = true;
00270             word.append(ch);
00271         }
00272         lastpos = i;
00273         if ( !word.isEmpty() )
00274             testIgnoreWord( word, haveAnNumber );
00275         else
00276             lastpos++;
00277     }
00278     while ( word.isEmpty() && (lastpos < (int)origbuffer.length()-1));
00279     if ( m_status != Finished && !spellWord( word ))
00280     {
00281         checkNextWord();
00282     }
00283 }
00284 
00285 void KOASpell::testIgnoreWord( QString & word, bool haveAnNumber )
00286 {
00287     if ( !ksconfig->spellWordWithNumber() && haveAnNumber )
00288     {
00289         word ="";
00290         return;
00291     }
00292 
00293     if(m_bIgnoreTitleCase && word==word.upper())
00294     {
00295         word ="";
00296         return;
00297     }
00298 
00299     if(m_bIgnoreUpperWords && word[0]==word[0].upper())
00300     {
00301         QString text=word[0]+word.right(word.length()-1).lower();
00302         if(text==word)
00303         {
00304             word ="";
00305             return;
00306         }
00307     }
00308 
00309     //We don't take advantage of ispell's ignore function because
00310     //we can't interrupt ispell's output (when checking a large
00311     //buffer) to add a word to _it's_ ignore-list.
00312     if (!word.isEmpty() &&ignorelist.findIndex(word.lower())!=-1)
00313     {
00314         word ="";
00315         return;
00316     }
00317     //
00318     QStringList::Iterator it = replacelist.begin();
00319     for(;it != replacelist.end(); ++it, ++it) // Skip two entries at a time.
00320     {
00321         if (word == *it) // Word matches
00322         {
00323             QString origWord = *it;
00324             ++it;
00325             word = *it;   // Replace it with the next entry
00326             correctWord( origWord ,  word);
00327             word ="";
00328         }
00329     }
00330 }
00331 
00332 void KOASpell::correctWord( const QString & originalword, const QString & newword )
00333 {
00334     emit corrected (originalword ,  newword, lastpos+offset-originalword.length());
00335     offset+=newword.length()-originalword.length();
00336     newbuffer.replace (lastpos+offset, newword.length(), newword );
00337 }
00338 
00339 void KOASpell::previousWord()
00340 {
00341     QString word;
00342     lastpos--;
00343     bool haveAnNumber = false;
00344     do
00345     {
00346         int i =0;
00347         for ( i = lastpos; i>=0;--i)
00348         {
00349             QChar ch = origbuffer[i];
00350             if ( ch.isSpace() || ch.isPunct() )
00351             {
00352                 lastpos--;
00353                 break;
00354             }
00355             if ( ch.isNumber() )
00356                 haveAnNumber = true;
00357             word.prepend(ch);
00358         }
00359         lastpos = i;
00360         if ( !word.isEmpty() )
00361             testIgnoreWord( word, haveAnNumber );
00362         else
00363             lastpos--;
00364     }
00365     while ( word.isEmpty() && (lastpos >= 0));
00366 
00367     if ( m_status != Finished && !spellWord( word ))
00368     {
00369         checkNextWord();
00370     }
00371 
00372 }
00373 
00374 bool KOASpell::check( const QString &_buffer, bool _usedialog )
00375 {
00376     if( !ksdlg )
00377         return false;
00378     lastpos = -1;
00379     usedialog = _usedialog;
00380     origbuffer = _buffer;
00381     m_status = Starting;
00382     if ( ( totalpos = origbuffer.length() ) == 0 )
00383     {
00384         emit done(origbuffer);
00385         return FALSE;
00386     }
00387 
00388     // Torben: I corrected the \n\n problem directly in the
00389     //         origbuffer since I got errors otherwise
00390     if ( origbuffer.right(2) != "\n\n" )
00391     {
00392         if (origbuffer.at(origbuffer.length()-1)!='\n')
00393     {
00394             origbuffer+='\n';
00395             origbuffer+='\n'; //shouldn't these be removed at some point?
00396     }
00397         else
00398             origbuffer+='\n';
00399     }
00400 
00401     newbuffer=origbuffer;
00402     //lastpos is a position in newbuffer (it has offset in it)
00403     offset=lastlastline=lastline=0;
00404     lastpos = -1;
00405 
00406 
00407     // send first buffer line
00408     int i = origbuffer.find('\n', 0)+1;
00409     QString qs;
00410     qs=origbuffer.mid (0,i);
00411     lastline=i; //the character position, not a line number
00412     if (_usedialog)
00413         ksdlg->show();
00414     else
00415         ksdlg->hide();
00416 
00417     //check new word.
00418     checkNextWord();
00419     return TRUE;
00420 }
00421 
00422 void KOASpell::checkNextWord()
00423 {
00424     if ( !ksdlg)
00425         return;
00426 
00427     if ( !ksdlg->previousWord() )
00428         nextWord();
00429     else
00430         previousWord();
00431 }
00432 
00433 void KOASpell::dialog(const QString & word, QStringList & sugg )
00434 {
00435     if ( !ksdlg )
00436         return;
00437     dlgorigword=word;
00438 
00439     connect (ksdlg, SIGNAL (command (int)), this, SLOT (dialog2(int)));
00440     ksdlg->init (word, &sugg);
00441     if (!ksdlg->previousWord())
00442         misspellingWord (word, sugg, lastpos+offset-word.length());
00443     else
00444         misspellingWord (word, sugg, lastpos+offset+1);
00445 
00446     ksdlg->show();
00447 }
00448 
00449 void KOASpell::dialog2 (int result)
00450 {
00451     if ( !ksdlg )
00452         return;
00453     QString qs;
00454     disconnect (ksdlg, SIGNAL (command (int)), this, SLOT (dialog2(int)));
00455     dlgresult=result;
00456     ksdlg->standby();
00457 
00458     dlgreplacement=ksdlg->replacement();
00459     bool testNextWord = true;
00460     QString _replacement;
00461     switch (dlgresult)
00462     {
00463     case KOS_IGNORE:
00464         emit ignoreword(dlgorigword);
00465         break;
00466     case KOS_IGNOREALL:
00467         // would be better to lower case only words with beginning cap
00468         ignorelist.prepend(dlgorigword.lower());
00469         emit ignoreall (dlgorigword);
00470         break;
00471     case KOS_ADD:
00472         addPersonal (dlgorigword);
00473         personaldict=TRUE;
00474         emit addword (dlgorigword);
00475         // adding to personal dict takes effect at the next line, not the current
00476         ignorelist.prepend(dlgorigword.lower());
00477         break;
00478     case KOS_REPLACEALL:
00479         replacelist.append (dlgorigword);
00480         _replacement = replacement();
00481         replacelist.append (_replacement);
00482 
00483         emit replaceall( dlgorigword ,  _replacement );
00484         correctWord( dlgorigword ,  _replacement );
00485         break;
00486     case KOS_ADDAUTOCORRECT:
00487         //todo add new word ????
00488         emit addAutoCorrect (dlgorigword , replacement());
00489     case KOS_REPLACE:
00490         correctWord( dlgorigword ,  replacement() );
00491         break;
00492     case KOS_CHECKAGAINWITHNEWLANGUAGE:
00493         changeSpellLanguage( ksdlg->languageIndex());
00494         spellCheckReplaceWord( dlgreplacement);
00495         testNextWord = false;
00496         break;
00497     case KOS_CHECKAGAIN:
00498         spellCheckReplaceWord( dlgreplacement);
00499         testNextWord = false;
00500         break;
00501     case KOS_STOP:
00502         testNextWord = false;
00503         ksdlg->hide();
00504         //buffer=newbuffer);
00505         emit done (newbuffer);
00506         emit death();
00507         break;
00508     case KOS_CANCEL:
00509         testNextWord = false;
00510         //      kdDebug(30006) << "cancelled\n" << endl;
00511         ksdlg->hide();
00512         emit done (origbuffer);
00513         emit death();
00514         break;
00515     }
00516     if ( testNextWord)
00517         checkNextWord();
00518 }
00519 
00520 void KOASpell::spellCheckReplaceWord( const QString & _word)
00521 {
00522     if ( !ksdlg )
00523         return;
00524     connect (ksdlg, SIGNAL (command (int)), this, SLOT (dialog2(int)));
00525     QStringList lst;
00526     lst=resultCheckWord( _word );
00527     ksdlg->changeSuggList( &lst);
00528     ksdlg->show();
00529 }
00530 
00531 void KOASpell::deleteSpellChecker()
00532 {
00533     if( speller )
00534     {
00535         delete_aspell_speller(speller);
00536         speller = 0;
00537     }
00538 }
00539 
00540 KOASpell::~KOASpell ()
00541 {
00542     deleteSpellChecker();
00543 }
00544 
00545 
00546 void KOASpell::changeSpellLanguage( int index )
00547 {
00548     deleteSpellChecker();
00549     initConfig( KOSpellConfig::listOfLanguageFileName()[index].latin1());
00550 #if 0
00551     kdDebug(30006)<<"Before KOSpellConfig::listOfLanguageFileName()[index].latin1() :"<<KOSpellConfig::listOfLanguageFileName()[index].latin1()<<endl;
00552     aspell_config_replace(config, "lang",KOSpellConfig::listOfLanguageFileName()[index].latin1());
00553     kdDebug(30006)<<" After aspell_config_retrieve(config, lang) :"<<aspell_config_retrieve(config, "lang")<<endl;
00554 #endif
00555 }
00556 
00557 
00558 int KOASpell::modalCheck( QString& text, KOSpellConfig* _kcs )
00559 {
00560     modalreturn = 0;
00561     modaltext = text;
00562 
00563     KOASpell* m_spell = new KOASpell(0L, i18n("Spell Checker"), 0 ,_kcs,true );
00564     QObject::connect( m_spell, SIGNAL( death() ),
00565                       m_spell, SLOT( slotModalSpellCheckerFinished() ) );
00566     QObject::connect( m_spell, SIGNAL( corrected( const QString &, const QString &, unsigned int ) ),
00567                       m_spell, SLOT( slotSpellCheckerCorrected( const QString &, const QString &, unsigned int ) ) );
00568     QObject::connect( m_spell, SIGNAL( done( const QString & ) ),
00569                       m_spell, SLOT( slotModalDone( const QString & ) ) );
00570 
00571     bool result = m_spell->check( text );
00572     if ( !result)
00573     {
00574         delete m_spell;
00575         m_spell=0L;
00576         return modalreturn;
00577     }
00578 
00579     while (m_spell->status()!=Finished)
00580         kapp->processEvents();
00581 
00582     text = modaltext;
00583     delete m_spell;
00584     return modalreturn;
00585 }
00586 
00587 void KOASpell::slotSpellCheckerCorrected( const QString & oldText, const QString & newText, unsigned int pos )
00588 {
00589     modaltext=modaltext.replace(pos,oldText.length(),newText);
00590 }
00591 
00592 
00593 void KOASpell::slotModalDone( const QString &/*_buffer*/ )
00594 {
00595     slotModalSpellCheckerFinished();
00596 }
00597 
00598 void KOASpell::slotModalSpellCheckerFinished()
00599 {
00600     modalreturn=(int)this->status();
00601 }
00602 
00603 
00604 #endif
00605 
00606 
KDE Logo
This file is part of the documentation for lib Library Version 1.3.5.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Mar 11 11:47:40 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003