You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdelibs/tdespell2/filter.cpp

310 lines
7.5 KiB

// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*-
/*
* filter.cpp
*
* Copyright (C) 2004 Zack Rusin <zack@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include "filter.h"
#include "settings.h"
#include <kstaticdeleter.h>
#include <kdebug.h>
#include <tqstring.h>
namespace KSpell2
{
static Word endWord;
static KStaticDeleter<Filter> sd;
static Filter* defFilter = 0;
class Filter::Private
{
public:
// The reason it's not in the class directly is that
// i'm not 100% sure that having the settings() here is
// the way i want to be doing this.
Settings *settings;
};
Filter* Filter::defaultFilter()
{
if ( !defFilter )
sd.setObject( defFilter, new Filter() );
return defFilter;
}
Word Filter::end()
{
return endWord;
}
Filter::Filter()
: m_currentPosition( 0 )
{
d = new Private;
d->settings = 0;
}
Filter::~Filter()
{
delete d; d = 0;
}
void Filter::setSettings( Settings *conf )
{
d->settings = conf;
}
Settings *Filter::settings() const
{
return d->settings;
}
void Filter::restart()
{
m_currentPosition = 0;
}
void Filter::setBuffer( const TQString& buffer )
{
m_buffer = buffer;
m_currentPosition = 0;
}
TQString Filter::buffer() const
{
return m_buffer;
}
bool Filter::atEnd() const
{
if ( m_currentPosition >= m_buffer.length() ) {
return true;
} else
return false;
}
Word Filter::nextWord() const
{
TQChar currentChar = skipToLetter( m_currentPosition );
if ( m_currentPosition >= m_buffer.length() ) {
return Filter::end();
}
bool allUppercase = currentChar.category() & TQChar::Letter_Uppercase;
bool runTogether = false;
TQString foundWord;
int start = m_currentPosition;
while ( currentChar.isLetter() ) {
if ( currentChar.category() & TQChar::Letter_Lowercase )
allUppercase = false;
/* FIXME: this does not work for Hebrew for example
//we consider run-together words as mixed-case words
if ( !allUppercase &&
currentChar.category() & TQChar::Letter_Uppercase )
runTogether = true;
*/
foundWord += currentChar;
++m_currentPosition;
currentChar = m_buffer[ m_currentPosition ];
}
if ( shouldBeSkipped( allUppercase, runTogether, foundWord ) )
return nextWord();
return Word( foundWord, start );
}
Word Filter::previousWord() const
{
while ( !m_buffer[ m_currentPosition ].isLetter() &&
m_currentPosition != 0) {
--m_currentPosition;
}
if ( m_currentPosition == 0 ) {
return Filter::end();
}
TQString foundWord;
int start = m_currentPosition;
while ( m_buffer[ start ].isLetter() ) {
foundWord.prepend( m_buffer[ m_currentPosition ] );
--start;
}
return Word( foundWord, start );
}
Word Filter::wordAtPosition( unsigned int pos ) const
{
if ( pos > m_buffer.length() )
return Filter::end();
int currentPosition = pos - 1;
TQString foundWord;
while ( currentPosition >= 0 &&
m_buffer[ currentPosition ].isLetter() ) {
foundWord.prepend( m_buffer[ currentPosition ] );
--currentPosition;
}
// currentPosition == 0 means the first char is not letter
// currentPosition == -1 means we reached the beginning
int start = (currentPosition < 0) ? 0 : ++currentPosition;
currentPosition = pos ;
if ( m_buffer[ currentPosition ].isLetter() ) {
while ( m_buffer[ currentPosition ].isLetter() ) {
foundWord.append( m_buffer[ currentPosition ] );
++currentPosition;
}
}
return Word( foundWord, start );
}
void Filter::setCurrentPosition( int i )
{
m_currentPosition = i;
//go back to the last word so that next word returns something
//useful
while ( m_buffer[m_currentPosition].isLetter() && m_currentPosition > 0 )
--m_currentPosition;
}
int Filter::currentPosition() const
{
return m_currentPosition;
}
void Filter::replace( const Word& w, const TQString& newWord)
{
int oldLen = w.word.length();
int newLen = newWord.length();
if ( oldLen != newLen && m_currentPosition > w.start ) {
if ( m_currentPosition > w.start ) {
int len = newLen - oldLen;
m_currentPosition += len;
}
}
m_buffer = m_buffer.replace( w.start, oldLen, newWord );
}
TQString Filter::context() const
{
int len = 60;
//we don't want the expression underneath casted to an unsigned int
//which would cause it to always evaluate to false
int signedPosition = m_currentPosition;
bool begin = ( (signedPosition - len/2)<=0 ) ? true : false;
TQString buffer = m_buffer;
Word word = wordAtPosition( m_currentPosition );
buffer = buffer.replace( word.start, word.word.length(),
TQString( "<b>%1</b>" ).arg( word.word ) );
TQString context;
if ( begin )
context = TQString( "%1...")
.arg( buffer.mid( 0, len ) );
else
context = TQString( "...%1..." )
.arg( buffer.mid( m_currentPosition - 20, len ) );
context = context.replace( '\n', ' ' );
return context;
}
bool Filter::trySkipLinks() const
{
TQChar currentChar = m_buffer[ m_currentPosition ];
uint length = m_buffer.length();
//URL - if so skip
if ( currentChar == ':' &&
( m_buffer[ ++m_currentPosition] == '/' || ( m_currentPosition + 1 ) >= length ) ) {
//in both cases url is considered finished at the first whitespace occurence
while ( !m_buffer[ m_currentPosition++ ].isSpace() && m_currentPosition < length )
;
return true;
}
//Email - if so skip
if ( currentChar == '@' ) {
while ( !m_buffer[ ++m_currentPosition ].isSpace() && m_currentPosition < length )
;
return true;
}
return false;
}
bool Filter::ignore( const TQString& word ) const
{
if ( d->settings ) {
return d->settings->ignore( word );
}
return false;
}
TQChar Filter::skipToLetter( uint &fromPosition ) const
{
TQChar currentChar = m_buffer[ fromPosition ];
while ( !currentChar.isLetter() &&
++fromPosition < m_buffer.length() ) {
currentChar = m_buffer[ fromPosition ];
}
return currentChar;
}
bool Filter::shouldBeSkipped( bool wordWasUppercase, bool wordWasRunTogether,
const TQString& foundWord ) const
{
bool checkUpper = ( d->settings ) ?
d->settings->checkUppercase () : true;
bool skipRunTogether = ( d->settings ) ?
d->settings->skipRunTogether() : true;
if ( trySkipLinks() )
return true;
if ( wordWasUppercase && !checkUpper )
return true;
if ( wordWasRunTogether && skipRunTogether )
return true;
return ignore( foundWord );
}
}