~ubuntu-branches/ubuntu/breezy/koffice/breezy

« back to all changes in this revision

Viewing changes to lib/kospell/koaspell.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Ben Burton
  • Date: 2004-05-09 11:33:00 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20040509113300-vfrdadqsvjfuhn3b
Tags: 1:1.3.1-1
* New upstream bugfix release.
* Built against newer imagemagick (closes: #246623).
* Made koffice-libs/kformula recommend/depend on latex-xft-fonts, which
  provides mathematical fonts that the formula editor can use.  Also
  patched the kformula part to make these fonts the default.
* Changed kword menu hint from "WordProcessors" to "Word processors"
  (closes: #246209).
* Spellchecker configuration is now fixed (closes: #221256, #227568).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* This file is part of the KDE libraries
 
2
   Copyright (C) 1997 David Sweet <dsweet@kde.org>
 
3
   Copyright (C) 2000-2001 Wolfram Diestel <wolfram@steloj.de>
 
4
   Copyright (C) 2002-2003 Laurent Montel <lmontel@mandrakesoft.com>
 
5
 
 
6
   This library is free software; you can redistribute it and/or
 
7
   modify it under the terms of the GNU Library General Public
 
8
   License version 2 as published by the Free Software Foundation.
 
9
 
 
10
   This library is distributed in the hope that it will be useful,
 
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
13
   Library General Public License for more details.
 
14
 
 
15
   You should have received a copy of the GNU Library General Public License
 
16
   along with this library; see the file COPYING.LIB.  If not, write to
 
17
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
18
   Boston, MA 02111-1307, USA.
 
19
*/
 
20
#ifdef HAVE_CONFIG_H
 
21
#include <config.h>
 
22
#endif
 
23
 
 
24
#ifdef HAVE_LIBASPELL
 
25
 
 
26
#include <stdio.h>
 
27
#include <sys/time.h>
 
28
#include <sys/types.h>
 
29
#include <unistd.h>
 
30
#include <ctype.h>
 
31
#include <stdlib.h> // atoi
 
32
 
 
33
#ifdef HAVE_STRINGS_H
 
34
#include <strings.h>
 
35
#endif
 
36
 
 
37
#include <qtextcodec.h>
 
38
#include <qtimer.h>
 
39
#include <kapplication.h>
 
40
#include <kdebug.h>
 
41
#include <klocale.h>
 
42
#include "koaspell.h"
 
43
#include "koaspell.moc"
 
44
#include "koSpell.h"
 
45
#include "koSpelldlg.h"
 
46
#include <kwin.h>
 
47
#include <kprocio.h>
 
48
 
 
49
#include <qtimer.h>
 
50
 
 
51
#include <aspell.h>
 
52
 
 
53
 
 
54
KOASpell::KOASpell( KOSpellConfig *_ksc )
 
55
    :KOSpell(_ksc)
 
56
{
 
57
    initSpell(_ksc);
 
58
    initConfig();
 
59
    QTimer::singleShot( 0, this, SLOT( slotEmitCheckerReady() ) );
 
60
}
 
61
 
 
62
//TODO FIXME !!!! KOSpellConfig not used.
 
63
void KOASpell::initSpell(KOSpellConfig *_ksc)
 
64
{
 
65
    Q_UNUSED( _ksc );
 
66
    m_bIgnoreUpperWords=false;
 
67
    m_bIgnoreTitleCase=false;
 
68
    autocorrect = false;
 
69
    autoDelete = false;
 
70
    modaldlg = false;
 
71
    speller = 0L;
 
72
    config = 0L;
 
73
    offset = 0;
 
74
    ksdlg=0;
 
75
    lastpos = -1;
 
76
 
 
77
    personaldict=FALSE;
 
78
    dlgresult=-1;
 
79
 
 
80
    caption=QString::null;
 
81
 
 
82
    parent=0L;
 
83
}
 
84
 
 
85
KOASpell::KOASpell (QWidget *_parent, const QString &_caption,
 
86
                KOSpellConfig *_ksc,
 
87
                bool _modal,  bool _autocorrect, KOSpellerType _type)
 
88
    :KOSpell(_parent,_caption,_ksc,_modal,_autocorrect, _type )
 
89
{
 
90
    initSpell(_ksc);
 
91
    autocorrect = _autocorrect;
 
92
    modaldlg = _modal;
 
93
    caption=_caption;
 
94
    parent=_parent;
 
95
 
 
96
    setUpDialog();
 
97
    QTimer::singleShot( 0, this, SLOT( slotEmitCheckerReady() ) );
 
98
}
 
99
 
 
100
void KOASpell::slotEmitCheckerReady()
 
101
{
 
102
    emit ready( this );
 
103
    emit spellCheckerReady();
 
104
}
 
105
 
 
106
bool KOASpell::initConfig(const QString & language)
 
107
{
 
108
    config = new_aspell_config();
 
109
    kdDebug(30006)<<" ksconfig->dictionary() :"<<ksconfig->dictionary()<<endl;
 
110
    Q_ASSERT( ksconfig->client() == KOS_CLIENT_ASPELL );
 
111
    aspell_config_replace(config, "lang", language.isEmpty() ? (ksconfig->dictionary().isEmpty() ? "en": ksconfig->dictionary().latin1()) : language.latin1() );
 
112
 
 
113
    kdDebug(30006)<<" ksconfig->dictionary() :"<<ksconfig->dictionary()<<endl;
 
114
 
 
115
    AspellCanHaveError * ret;
 
116
    ret = new_aspell_speller(config);
 
117
    if (aspell_error(ret) != 0) {
 
118
        kdDebug(30006)<<"Error :"<<aspell_error_message(ret)<<endl;
 
119
        delete_aspell_can_have_error(ret);
 
120
        return false;
 
121
    }
 
122
    /**
 
123
       My version of aspell uses the iso-8859-x syntax for those...
 
124
    switch (ksconfig->encoding())
 
125
    {
 
126
    case KOS_E_LATIN1:
 
127
        aspell_config_replace(config, "encoding", "latin1");
 
128
        break;
 
129
    case KOS_E_LATIN2:
 
130
        aspell_config_replace(config, "encoding", "latin2");
 
131
        break;
 
132
    case KOS_E_LATIN3:
 
133
        aspell_config_replace(config, "encoding", "latin3");
 
134
        break;
 
135
        // add the other charsets here
 
136
    case KOS_E_LATIN4:
 
137
    case KOS_E_LATIN5:
 
138
    case KOS_E_LATIN7:
 
139
    case KOS_E_LATIN8:
 
140
    case KOS_E_LATIN9:
 
141
    case KOS_E_LATIN13:
 
142
    case KOS_E_LATIN15:
 
143
        // will work, if this is the default charset in the dictionary
 
144
        kdError(750) << "charsets iso-8859-4 .. iso-8859-15 not supported yet" << endl;
 
145
        break;
 
146
    case KOS_E_UTF8:
 
147
        aspell_config_replace(config, "encoding", "utf8");
 
148
        break;
 
149
    case KOS_E_KOI8U:
 
150
        //todo
 
151
        break;
 
152
    }
 
153
    */
 
154
    aspell_config_replace(config, "ignore-case", ksconfig->ignoreCase()?"true" : "false" );
 
155
    aspell_config_replace(config, "ignore-accents", ksconfig->ignoreAccent()?"true" : "false" );
 
156
 
 
157
    ret = new_aspell_speller(config);
 
158
 
 
159
    delete_aspell_config(config);
 
160
 
 
161
    if (aspell_error(ret) != 0) {
 
162
        printf("Error: %s\n",aspell_error_message(ret));
 
163
        delete_aspell_can_have_error(ret);
 
164
        return false;
 
165
    }
 
166
    speller = to_aspell_speller(ret);
 
167
    config = aspell_speller_config(speller);
 
168
    return true;
 
169
}
 
170
 
 
171
void
 
172
KOASpell::setUpDialog ()
 
173
{
 
174
    if (ksdlg)
 
175
        return;
 
176
    bool ret = initConfig();
 
177
    if ( !ret )
 
178
        return;
 
179
 
 
180
    //Set up the dialog box
 
181
    ksdlg=new KOSpellDlg (parent, ksconfig,"dialog", KOSpellConfig::indexFromLanguageFileName( ksconfig->dictionary()),  modaldlg, autocorrect );
 
182
    ksdlg->setCaption (caption);
 
183
#ifdef Q_WS_X11 // FIXME(E): Implement for Qt/Embedded
 
184
    KWin::setIcons (ksdlg->winId(), kapp->icon(), kapp->miniIcon());
 
185
#endif
 
186
    if ( modaldlg )
 
187
        ksdlg->setFocus();
 
188
}
 
189
 
 
190
bool KOASpell::addPersonal (const QString & word)
 
191
{
 
192
    if( !speller)
 
193
        return false;
 
194
    //add to aspell internal.
 
195
    aspell_speller_add_to_personal(speller, word.latin1(), word.length());
 
196
    //save directly into personnal dictionary.
 
197
    writePersonalDictionary();
 
198
    return true;
 
199
}
 
200
 
 
201
bool KOASpell::writePersonalDictionary ()
 
202
{
 
203
    if( !speller)
 
204
        return false;
 
205
    aspell_speller_save_all_word_lists(speller);
 
206
    kdDebug(30006)<<"aspell_speller_error_message(speller) :"<<aspell_speller_error_message(speller)<<endl;
 
207
    return true;
 
208
}
 
209
 
 
210
bool KOASpell::ignore (const QString & /*word*/)
 
211
{
 
212
    //fixme !!!!!!!!!!!!!!!!
 
213
    return true;
 
214
}
 
215
 
 
216
 
 
217
QStringList KOASpell::resultCheckWord( const QString &_word )
 
218
{
 
219
    if (_word.isEmpty() || !speller)
 
220
        return QStringList();
 
221
    kdDebug(30006)<<" aspell_config_retrieve(config, lang) :"<<aspell_config_retrieve(config, "lang")<<endl;
 
222
    QStringList result;
 
223
    const AspellWordList *wl = aspell_speller_suggest(speller, _word.latin1(), -1);
 
224
    if (wl == 0) {
 
225
        kdDebug(30006)<<"Error: "<< aspell_speller_error_message(speller)<<endl;
 
226
    } else {
 
227
        AspellStringEnumeration * els = aspell_word_list_elements(wl);
 
228
        const char * word2;
 
229
        while ( (word2 = aspell_string_enumeration_next(els)) != 0) {
 
230
            result.append( word2 );
 
231
            kdDebug(30006)<<" word2 :"<<word2<<endl;
 
232
        }
 
233
    }
 
234
    return result;
 
235
}
 
236
 
 
237
bool KOASpell::spellWord( const QString &_word )
 
238
{
 
239
    QStringList lst =resultCheckWord( _word );
 
240
    if ( lst.isEmpty() && ((lastpos >= (int)origbuffer.length()-1)|| lastpos<0) )
 
241
    {
 
242
        //change m_status before to emit signal otherwise
 
243
        //kword + multiframe doesn't work
 
244
        m_status = Finished;
 
245
        emit done( origbuffer );
 
246
        return false;
 
247
    }
 
248
    if ( lst.contains( _word ))
 
249
      return false;
 
250
 
 
251
    dialog( _word, lst);
 
252
    return true;
 
253
}
 
254
 
 
255
void KOASpell::nextWord()
 
256
{
 
257
    QString word;
 
258
    lastpos++;
 
259
    bool haveAnNumber = false;
 
260
    do
 
261
    {
 
262
        int i =0;
 
263
        for ( i = lastpos; i<(int)origbuffer.length();i++)
 
264
        {
 
265
            QChar ch = origbuffer[i];
 
266
            if ( ch.isSpace() || ch.isPunct() )
 
267
                break;
 
268
            if ( ch.isNumber() )
 
269
                haveAnNumber = true;
 
270
            word.append(ch);
 
271
        }
 
272
        lastpos = i;
 
273
        if ( !word.isEmpty() )
 
274
            testIgnoreWord( word, haveAnNumber );
 
275
        else
 
276
            lastpos++;
 
277
    }
 
278
    while ( word.isEmpty() && (lastpos < (int)origbuffer.length()-1));
 
279
    if ( m_status != Finished && !spellWord( word ))
 
280
    {
 
281
        checkNextWord();
 
282
    }
 
283
}
 
284
 
 
285
void KOASpell::testIgnoreWord( QString & word, bool haveAnNumber )
 
286
{
 
287
    if ( !ksconfig->spellWordWithNumber() && haveAnNumber )
 
288
    {
 
289
        word ="";
 
290
        return;
 
291
    }
 
292
 
 
293
    if(m_bIgnoreTitleCase && word==word.upper())
 
294
    {
 
295
        word ="";
 
296
        return;
 
297
    }
 
298
 
 
299
    if(m_bIgnoreUpperWords && word[0]==word[0].upper())
 
300
    {
 
301
        QString text=word[0]+word.right(word.length()-1).lower();
 
302
        if(text==word)
 
303
        {
 
304
            word ="";
 
305
            return;
 
306
        }
 
307
    }
 
308
 
 
309
    //We don't take advantage of ispell's ignore function because
 
310
    //we can't interrupt ispell's output (when checking a large
 
311
    //buffer) to add a word to _it's_ ignore-list.
 
312
    if (!word.isEmpty() &&ignorelist.findIndex(word.lower())!=-1)
 
313
    {
 
314
        word ="";
 
315
        return;
 
316
    }
 
317
    //
 
318
    QStringList::Iterator it = replacelist.begin();
 
319
    for(;it != replacelist.end(); ++it, ++it) // Skip two entries at a time.
 
320
    {
 
321
        if (word == *it) // Word matches
 
322
        {
 
323
            QString origWord = *it;
 
324
            ++it;
 
325
            word = *it;   // Replace it with the next entry
 
326
            correctWord( origWord ,  word);
 
327
            word ="";
 
328
        }
 
329
    }
 
330
}
 
331
 
 
332
void KOASpell::correctWord( const QString & originalword, const QString & newword )
 
333
{
 
334
    emit corrected (originalword ,  newword, lastpos+offset-originalword.length());
 
335
    offset+=newword.length()-originalword.length();
 
336
    newbuffer.replace (lastpos+offset, newword.length(), newword );
 
337
}
 
338
 
 
339
void KOASpell::previousWord()
 
340
{
 
341
    QString word;
 
342
    lastpos--;
 
343
    bool haveAnNumber = false;
 
344
    do
 
345
    {
 
346
        int i =0;
 
347
        for ( i = lastpos; i>=0;--i)
 
348
        {
 
349
            QChar ch = origbuffer[i];
 
350
            if ( ch.isSpace() || ch.isPunct() )
 
351
            {
 
352
                lastpos--;
 
353
                break;
 
354
            }
 
355
            if ( ch.isNumber() )
 
356
                haveAnNumber = true;
 
357
            word.prepend(ch);
 
358
        }
 
359
        lastpos = i;
 
360
        if ( !word.isEmpty() )
 
361
            testIgnoreWord( word, haveAnNumber );
 
362
        else
 
363
            lastpos--;
 
364
    }
 
365
    while ( word.isEmpty() && (lastpos >= 0));
 
366
 
 
367
    if ( m_status != Finished && !spellWord( word ))
 
368
    {
 
369
        checkNextWord();
 
370
    }
 
371
 
 
372
}
 
373
 
 
374
bool KOASpell::check( const QString &_buffer, bool _usedialog )
 
375
{
 
376
    if( !ksdlg )
 
377
        return false;
 
378
    lastpos = -1;
 
379
    usedialog = _usedialog;
 
380
    origbuffer = _buffer;
 
381
    m_status = Starting;
 
382
    if ( ( totalpos = origbuffer.length() ) == 0 )
 
383
    {
 
384
        emit done(origbuffer);
 
385
        return FALSE;
 
386
    }
 
387
 
 
388
    // Torben: I corrected the \n\n problem directly in the
 
389
    //         origbuffer since I got errors otherwise
 
390
    if ( origbuffer.right(2) != "\n\n" )
 
391
    {
 
392
        if (origbuffer.at(origbuffer.length()-1)!='\n')
 
393
        {
 
394
            origbuffer+='\n';
 
395
            origbuffer+='\n'; //shouldn't these be removed at some point?
 
396
        }
 
397
        else
 
398
            origbuffer+='\n';
 
399
    }
 
400
 
 
401
    newbuffer=origbuffer;
 
402
    //lastpos is a position in newbuffer (it has offset in it)
 
403
    offset=lastlastline=lastline=0;
 
404
    lastpos = -1;
 
405
 
 
406
 
 
407
    // send first buffer line
 
408
    int i = origbuffer.find('\n', 0)+1;
 
409
    QString qs;
 
410
    qs=origbuffer.mid (0,i);
 
411
    lastline=i; //the character position, not a line number
 
412
    if (_usedialog)
 
413
        ksdlg->show();
 
414
    else
 
415
        ksdlg->hide();
 
416
 
 
417
    //check new word.
 
418
    checkNextWord();
 
419
    return TRUE;
 
420
}
 
421
 
 
422
void KOASpell::checkNextWord()
 
423
{
 
424
    if ( !ksdlg)
 
425
        return;
 
426
 
 
427
    if ( !ksdlg->previousWord() )
 
428
        nextWord();
 
429
    else
 
430
        previousWord();
 
431
}
 
432
 
 
433
void KOASpell::dialog(const QString & word, QStringList & sugg )
 
434
{
 
435
    if ( !ksdlg )
 
436
        return;
 
437
    dlgorigword=word;
 
438
 
 
439
    connect (ksdlg, SIGNAL (command (int)), this, SLOT (dialog2(int)));
 
440
    ksdlg->init (word, &sugg);
 
441
    if (!ksdlg->previousWord())
 
442
        misspellingWord (word, sugg, lastpos+offset-word.length());
 
443
    else
 
444
        misspellingWord (word, sugg, lastpos+offset+1);
 
445
 
 
446
    ksdlg->show();
 
447
}
 
448
 
 
449
void KOASpell::dialog2 (int result)
 
450
{
 
451
    if ( !ksdlg )
 
452
        return;
 
453
    QString qs;
 
454
    disconnect (ksdlg, SIGNAL (command (int)), this, SLOT (dialog2(int)));
 
455
    dlgresult=result;
 
456
    ksdlg->standby();
 
457
 
 
458
    dlgreplacement=ksdlg->replacement();
 
459
    bool testNextWord = true;
 
460
    QString _replacement;
 
461
    switch (dlgresult)
 
462
    {
 
463
    case KOS_IGNORE:
 
464
        emit ignoreword(dlgorigword);
 
465
        break;
 
466
    case KOS_IGNOREALL:
 
467
        // would be better to lower case only words with beginning cap
 
468
        ignorelist.prepend(dlgorigword.lower());
 
469
        emit ignoreall (dlgorigword);
 
470
        break;
 
471
    case KOS_ADD:
 
472
        addPersonal (dlgorigword);
 
473
        personaldict=TRUE;
 
474
        emit addword (dlgorigword);
 
475
        // adding to personal dict takes effect at the next line, not the current
 
476
        ignorelist.prepend(dlgorigword.lower());
 
477
        break;
 
478
    case KOS_REPLACEALL:
 
479
        replacelist.append (dlgorigword);
 
480
        _replacement = replacement();
 
481
        replacelist.append (_replacement);
 
482
 
 
483
        emit replaceall( dlgorigword ,  _replacement );
 
484
        correctWord( dlgorigword ,  _replacement );
 
485
        break;
 
486
    case KOS_ADDAUTOCORRECT:
 
487
        //todo add new word ????
 
488
        emit addAutoCorrect (dlgorigword , replacement());
 
489
    case KOS_REPLACE:
 
490
        correctWord( dlgorigword ,  replacement() );
 
491
        break;
 
492
    case KOS_CHECKAGAINWITHNEWLANGUAGE:
 
493
        changeSpellLanguage( ksdlg->languageIndex());
 
494
        spellCheckReplaceWord( dlgreplacement);
 
495
        testNextWord = false;
 
496
        break;
 
497
    case KOS_CHECKAGAIN:
 
498
        spellCheckReplaceWord( dlgreplacement);
 
499
        testNextWord = false;
 
500
        break;
 
501
    case KOS_STOP:
 
502
        testNextWord = false;
 
503
        ksdlg->hide();
 
504
        //buffer=newbuffer);
 
505
        emit done (newbuffer);
 
506
        emit death();
 
507
        break;
 
508
    case KOS_CANCEL:
 
509
        testNextWord = false;
 
510
        //      kdDebug(30006) << "cancelled\n" << endl;
 
511
        ksdlg->hide();
 
512
        emit done (origbuffer);
 
513
        emit death();
 
514
        break;
 
515
    }
 
516
    if ( testNextWord)
 
517
        checkNextWord();
 
518
}
 
519
 
 
520
void KOASpell::spellCheckReplaceWord( const QString & _word)
 
521
{
 
522
    if ( !ksdlg )
 
523
        return;
 
524
    connect (ksdlg, SIGNAL (command (int)), this, SLOT (dialog2(int)));
 
525
    QStringList lst;
 
526
    lst=resultCheckWord( _word );
 
527
    ksdlg->changeSuggList( &lst);
 
528
    ksdlg->show();
 
529
}
 
530
 
 
531
void KOASpell::deleteSpellChecker()
 
532
{
 
533
    if( speller )
 
534
    {
 
535
        delete_aspell_speller(speller);
 
536
        speller = 0;
 
537
    }
 
538
}
 
539
 
 
540
KOASpell::~KOASpell ()
 
541
{
 
542
    deleteSpellChecker();
 
543
}
 
544
 
 
545
 
 
546
void KOASpell::changeSpellLanguage( int index )
 
547
{
 
548
    deleteSpellChecker();
 
549
    initConfig( KOSpellConfig::listOfLanguageFileName()[index].latin1());
 
550
#if 0
 
551
    kdDebug(30006)<<"Before KOSpellConfig::listOfLanguageFileName()[index].latin1() :"<<KOSpellConfig::listOfLanguageFileName()[index].latin1()<<endl;
 
552
    aspell_config_replace(config, "lang",KOSpellConfig::listOfLanguageFileName()[index].latin1());
 
553
    kdDebug(30006)<<" After aspell_config_retrieve(config, lang) :"<<aspell_config_retrieve(config, "lang")<<endl;
 
554
#endif
 
555
}
 
556
 
 
557
 
 
558
int KOASpell::modalCheck( QString& text, KOSpellConfig* _kcs )
 
559
{
 
560
    modalreturn = 0;
 
561
    modaltext = text;
 
562
 
 
563
    KOASpell* m_spell = new KOASpell(0L, i18n("Spell Checker"), 0 ,_kcs,true );
 
564
    QObject::connect( m_spell, SIGNAL( death() ),
 
565
                      m_spell, SLOT( slotModalSpellCheckerFinished() ) );
 
566
    QObject::connect( m_spell, SIGNAL( corrected( const QString &, const QString &, unsigned int ) ),
 
567
                      m_spell, SLOT( slotSpellCheckerCorrected( const QString &, const QString &, unsigned int ) ) );
 
568
    QObject::connect( m_spell, SIGNAL( done( const QString & ) ),
 
569
                      m_spell, SLOT( slotModalDone( const QString & ) ) );
 
570
 
 
571
    bool result = m_spell->check( text );
 
572
    if ( !result)
 
573
    {
 
574
        delete m_spell;
 
575
        m_spell=0L;
 
576
        return modalreturn;
 
577
    }
 
578
 
 
579
    while (m_spell->status()!=Finished)
 
580
        kapp->processEvents();
 
581
 
 
582
    text = modaltext;
 
583
    delete m_spell;
 
584
    return modalreturn;
 
585
}
 
586
 
 
587
void KOASpell::slotSpellCheckerCorrected( const QString & oldText, const QString & newText, unsigned int pos )
 
588
{
 
589
    modaltext=modaltext.replace(pos,oldText.length(),newText);
 
590
}
 
591
 
 
592
 
 
593
void KOASpell::slotModalDone( const QString &/*_buffer*/ )
 
594
{
 
595
    slotModalSpellCheckerFinished();
 
596
}
 
597
 
 
598
void KOASpell::slotModalSpellCheckerFinished()
 
599
{
 
600
    modalreturn=(int)this->status();
 
601
}
 
602
 
 
603
 
 
604
#endif
 
605
 
 
606