/* This file is part of the KDE project
   Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
                 2001       Sven Leiber         <s.leiber@web.de>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 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
   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.
*/

#include "KoAutoFormat.h"

#include "KoTextObject.h"
#include "KoTextParag.h"
#include "KoVariable.h"
#include "KoParagCounter.h"
#include <KoDocument.h>
#include <KoSearchDia.h>
#include <KoGlobal.h>

#include <tdeversion.h>
#include <kdebug.h>
#include <tdelocale.h>
#include <kinstance.h>
#include <tdeconfig.h>
#include <kstandarddirs.h>
#include <tdeglobal.h>
#include <kcommand.h>
//#include <KoTextFormat.h>
#include <kcompletion.h>
#include <kcalendarsystem.h>

#include <tqfile.h>
#include <tqlabel.h>
#include <tqtooltip.h>
#include <tqwhatsthis.h>
#include <tqdom.h>
#include <tqregexp.h>


KoCompletionBox::KoCompletionBox( TQWidget * parent, const char * name, WFlags f)
  : TQLabel(parent,name,f)
{
  setBackgroundColor(TQColor("#FFFFE6"));
  setFocusPolicy(TQ_NoFocus);
  setFrameShape(TQFrame::Box);
}

KoCompletionBox::~KoCompletionBox()
{
}

void KoCompletionBox::mousePressEvent( TQMouseEvent *)
{
  hide();
}

TQString& KoCompletionBox::lastWord()
{
  return m_lastWord;
}

void KoCompletionBox::setLastWord( TQString const &lastword)
{
  m_lastWord = lastword;
}

KoAutoFormatEntry::KoAutoFormatEntry(const TQString& replace)
    : m_replace( replace )
{
    m_formatOptions= 0L;
}

KoAutoFormatEntry::~KoAutoFormatEntry()
{
    delete m_formatOptions;
    m_formatOptions=0L;
}

KoSearchContext *KoAutoFormatEntry::formatEntryContext() const
{
    return m_formatOptions;
}

void KoAutoFormatEntry::createNewEntryContext()
{
    if ( !m_formatOptions )
    {
        m_formatOptions = new KoSearchContext();
    }
}

void KoAutoFormatEntry::setFormatEntryContext( KoSearchContext *_cont )
{
    delete m_formatOptions;
    m_formatOptions=_cont;
}

void KoAutoFormatEntry::clearFormatEntryContext( )
{
    delete m_formatOptions;
    m_formatOptions = 0L;
}


/******************************************************************/
/* Class: KoAutoFormat						  */
/******************************************************************/
KoAutoFormat::KoAutoFormat( KoDocument *_doc, KoVariableCollection *_varCollection, KoVariableFormatCollection *_varFormatCollection )
    : m_doc( _doc ),
      m_varCollection(_varCollection),
      m_varFormatCollection(_varFormatCollection),
      m_autoFormatLanguage( TQString()),
      m_configRead( false ),
      m_convertUpperCase( false ), m_convertUpperUpper( false ),
      m_advancedAutoCorrect( true ),
      m_autoDetectUrl( false ),
      m_ignoreDoubleSpace( false ),
      m_removeSpaceBeginEndLine( false ),
      m_useBulletStyle(false),
      m_autoChangeFormat(false),
      m_autoReplaceNumber(false),
      m_useAutoNumberStyle(false),
      m_completion(false),
      m_toolTipCompletion(false),
      m_completionAppendSpace(false),
      m_addCompletionWord(true),
      m_includeTwoUpperLetterException(false),
      m_includeAbbreviation(false),
      m_ignoreUpperCase(false),
      m_bAutoFormatActive(true),
      m_bAutoSuperScript( false ),
      m_bAutoCorrectionWithFormat( false ),
      m_bCapitalizeNameOfDays( false ),
      m_wordInserted( false ),
      m_bulletStyle(),
      m_typographicSimpleQuotes(),
      m_typographicDoubleQuotes(),
      m_typographicDefaultDoubleQuotes(),
      m_typographicDefaultSimpleQuotes(),
      m_listCompletion( new TDECompletion ),
      m_entries(17,false),
      m_allLanguages(17,false),
      m_superScriptEntries(),
      m_upperCaseExceptions(),
      m_twoUpperLetterException(),
      m_maxFindLength( 0 ),
      m_minCompletionWordLength( 5 ),
      m_nbMaxCompletionWord( 500 ),
      m_countMaxWords(0),
      m_completionBox(0),
      m_keyCompletionAction( Enter )

{
    //load once this list not each time that we "readConfig"
    loadListOfWordCompletion();
    m_listCompletion->setIgnoreCase( true );
    updateMaxWords();
    TDELocale tdelocale(m_doc->instance()->instanceName());
    for (int i = 1; i <=7; i++)
    {
        m_cacheNameOfDays.append(tdelocale.calendar()->weekDayName( i ).lower());
    }
}

KoAutoFormat::KoAutoFormat( const KoAutoFormat& format )
    : m_doc( format.m_doc ),
      m_varCollection( format.m_varCollection ),
      m_varFormatCollection( format.m_varFormatCollection ),
      m_autoFormatLanguage( format.m_autoFormatLanguage),
      m_configRead( format.m_configRead ),
      m_convertUpperCase( format.m_convertUpperCase ),
      m_convertUpperUpper( format.m_convertUpperUpper ),
      m_advancedAutoCorrect( format.m_advancedAutoCorrect ),
      m_autoDetectUrl( format.m_autoDetectUrl ),
      m_ignoreDoubleSpace( format.m_ignoreDoubleSpace ),
      m_removeSpaceBeginEndLine( format.m_removeSpaceBeginEndLine ),
      m_useBulletStyle( format.m_useBulletStyle ),
      m_autoChangeFormat( format.m_autoChangeFormat ),
      m_autoReplaceNumber( format.m_autoReplaceNumber ),
      m_useAutoNumberStyle( format.m_useAutoNumberStyle ),
      m_completion( format.m_completion ),
      m_toolTipCompletion( format.m_toolTipCompletion),
      m_completionAppendSpace( format.m_completionAppendSpace ),
      m_addCompletionWord( format.m_addCompletionWord ),
      m_includeTwoUpperLetterException( format.m_includeTwoUpperLetterException ),
      m_includeAbbreviation( format.m_includeAbbreviation ),
      m_ignoreUpperCase( format.m_ignoreUpperCase ),
      m_bAutoFormatActive( format.m_bAutoFormatActive ),
      m_bAutoSuperScript( format.m_bAutoSuperScript ),
      m_bAutoCorrectionWithFormat( format.m_bAutoCorrectionWithFormat),
      m_bCapitalizeNameOfDays( format.m_bCapitalizeNameOfDays),
      m_bulletStyle( format.m_bulletStyle ),
      m_typographicSimpleQuotes( format.m_typographicSimpleQuotes ),
      m_typographicDoubleQuotes( format.m_typographicDoubleQuotes ),
      m_typographicDefaultDoubleQuotes( format.m_typographicDefaultDoubleQuotes),
      m_typographicDefaultSimpleQuotes( format.m_typographicDefaultSimpleQuotes),
      m_listCompletion( 0L ), // don't copy it!
      m_entries(17,false ),//don't copy it.
      m_allLanguages(17,false), //don't copy it
      m_superScriptEntries ( format.m_superScriptEntries ),
      m_upperCaseExceptions( format.m_upperCaseExceptions ),
      m_twoUpperLetterException( format.m_twoUpperLetterException ),
      m_maxFindLength( format.m_maxFindLength ),
      m_minCompletionWordLength( format.m_minCompletionWordLength ),
      m_nbMaxCompletionWord( format.m_nbMaxCompletionWord ),
      m_cacheNameOfDays( format.m_cacheNameOfDays),
      m_completionBox(0),
      m_keyCompletionAction( format.m_keyCompletionAction )
{
    //m_listCompletion=new TDECompletion();
    //m_listCompletion->setItems( autoFormat.listCompletion() );
    //copyAutoFormatEntries( autoFormat );
}

KoAutoFormat::~KoAutoFormat()
{
    delete m_listCompletion;
    m_entries.setAutoDelete( true );
    m_entries.clear();
    m_allLanguages.setAutoDelete( true );
    m_allLanguages.clear();
}

void KoAutoFormat::updateMaxWords()
{
    TQStringList list = m_listCompletion->items();
    for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it )
    {
        TQString tmp = *it;
        uint maxword = 1;

        for (uint i=0; i < (uint)tmp.length(); i++)
                if ( tmp.at(i).isSpace() || tmp.at(i).isPunct() )
                        maxword++;
        if (maxword >  m_countMaxWords )
                m_countMaxWords = maxword;
    }
    kdDebug() << "m_countMaxWords: " << m_countMaxWords << endl;
}

void KoAutoFormat::loadListOfWordCompletion()
{
    TDEConfig* config = KoGlobal::kofficeConfig();
    TDEConfigGroupSaver cgs( config, "Completion Word" );
    m_listCompletion->insertItems(config->readListEntry( "list" ));
}

void KoAutoFormat::readConfig(bool force)
{
    // Read the autoformat configuration
    // This is done on demand (when typing the first char, or when opening the config dialog)
    // so that loading is faster and to avoid doing it for readonly documents.
    if ( m_configRead && !force )
        return;
    TDEConfig* config = KoGlobal::kofficeConfig();
    TDEConfigGroupSaver cgs( config, "AutoFormat" );
    //when we force don't load format language.
    if ( !force)
        m_autoFormatLanguage = config->readEntry("formatLanguage", TQString());

    m_convertUpperCase = config->readBoolEntry( "ConvertUpperCase", false );
    m_convertUpperUpper = config->readBoolEntry( "ConvertUpperUpper", false );
    m_includeTwoUpperLetterException = config->readBoolEntry( "includeTwoLetterException", false );
    m_includeAbbreviation = config->readBoolEntry( "includeAbbreviation", false );

    m_advancedAutoCorrect = config->readBoolEntry( "AdvancedAutocorrect", true );
    m_bAutoCorrectionWithFormat = config->readBoolEntry( "AutoCorrectionWithFormat",false );
    m_bCapitalizeNameOfDays = config->readBoolEntry( "CapitalizeNameOfDays",false );

    m_autoDetectUrl = config->readBoolEntry("AutoDetectUrl",false);
    m_ignoreDoubleSpace = config->readBoolEntry("IgnoreDoubleSpace", true);
    m_removeSpaceBeginEndLine = config->readBoolEntry("RemoveSpaceBeginEndLine", true);

    m_useBulletStyle = config->readBoolEntry("UseBulletStyle",false);
    TQString tmp = config->readEntry( "BulletStyle", "" );
    m_bulletStyle = tmp.isEmpty() ? TQChar() : tmp[0];

    m_autoChangeFormat = config->readBoolEntry( "AutoChangeFormat", false );

    m_autoReplaceNumber = config->readBoolEntry( "AutoReplaceNumber", true );

    m_useAutoNumberStyle = config->readBoolEntry( "AutoNumberStyle", false );


    TQString beginDoubleQuote = config->readEntry( "TypographicQuotesBegin" );
    TQString endDoubleQuote = config->readEntry( "TypographicQuotesEnd" );

    m_typographicDoubleQuotes.replace = config->readBoolEntry( "TypographicQuotesEnabled", false );

    TQString begin = config->readEntry( "TypographicSimpleQuotesBegin" );
    TQString end = config->readEntry( "TypographicSimpleQuotesEnd" );
    m_typographicSimpleQuotes.replace = config->readBoolEntry( "TypographicSimpleQuotesEnabled", false );

    m_bAutoSuperScript = config->readBoolEntry( "AutoSuperScript", true );

    config->setGroup( "completion" );
    m_completion = config->readBoolEntry( "completion", false );

    m_completionAppendSpace = config->readBoolEntry( "CompletionAppendSpace", false );
    m_minCompletionWordLength = config->readUnsignedNumEntry( "CompletionMinWordLength", 5 );
    m_nbMaxCompletionWord = config->readUnsignedNumEntry( "NbMaxCompletionWord", 100 );
    m_addCompletionWord = config->readBoolEntry( "AddCompletionWord", true );
    m_toolTipCompletion = config->readBoolEntry( "ToolTipCompletion", true );
    m_keyCompletionAction = ( KoAutoFormat::KeyCompletionAction )config->readUnsignedNumEntry( "CompletionKeyAction", 0 );

    if ( force )
    {
        m_entries.setAutoDelete(true);
        m_entries.clear();
        m_entries.setAutoDelete(false);
        m_allLanguages.setAutoDelete(true);
        m_allLanguages.clear();
        m_allLanguages.setAutoDelete(false);
        m_upperCaseExceptions.clear();
        m_superScriptEntries.clear();
        m_twoUpperLetterException.clear();

    }

    //config->setGroup( "AutoFormatEntries" );

    readAutoCorrectConfig();

    if( beginDoubleQuote.isEmpty())
    {
        if( m_typographicDefaultDoubleQuotes.begin.isNull())
            m_typographicDoubleQuotes.begin = TQChar('«');
        else
            m_typographicDoubleQuotes.begin = m_typographicDefaultDoubleQuotes.begin;
    }
    else
        m_typographicDoubleQuotes.begin = beginDoubleQuote[0];

    if( endDoubleQuote.isEmpty() )
    {
        if( m_typographicDefaultDoubleQuotes.end.isNull())
            m_typographicDoubleQuotes.end = TQChar('»');
        else
            m_typographicDoubleQuotes.end = m_typographicDefaultDoubleQuotes.end;
    }
    else
        m_typographicDoubleQuotes.end = endDoubleQuote[0];

    m_typographicDoubleQuotes.replace = m_typographicDoubleQuotes.replace
                                        && !m_typographicDoubleQuotes.begin.isNull()
                                        && !m_typographicDoubleQuotes.end.isNull();


    if( begin.isEmpty())
    {
        if( m_typographicDefaultSimpleQuotes.begin.isNull())
            m_typographicSimpleQuotes.begin = TQChar('\'');
        else
            m_typographicSimpleQuotes.begin = m_typographicDefaultSimpleQuotes.begin;
    }
    else
        m_typographicSimpleQuotes.begin = begin[0];

    if( end.isEmpty() )
    {
        if( m_typographicDefaultSimpleQuotes.end.isNull())
            m_typographicSimpleQuotes.end = TQChar('\'');
        else
            m_typographicSimpleQuotes.end = m_typographicDefaultSimpleQuotes.end;
    }
    else
        m_typographicSimpleQuotes.end = end[0];

    m_typographicSimpleQuotes.replace = m_typographicSimpleQuotes.replace
                                        && !m_typographicSimpleQuotes.end.isNull()
                                        && !m_typographicSimpleQuotes.begin.isNull();


    loadAllLanguagesAutoCorrection();
    buildMaxLen();
    autoFormatIsActive();
    m_configRead = true;
}

void KoAutoFormat::readAutoCorrectConfig()
{
    Q_ASSERT( m_entries.isEmpty() ); // readConfig is only called once...
    TDELocale tdelocale(m_doc->instance()->instanceName());
    TQString kdelang = tdelocale.languageList().front();
    kdelang.remove( TQRegExp( "@.*" ) );
    kdDebug(32500) << "KoAutoFormat: m_autoFormatLanguage=" << m_autoFormatLanguage << " kdelang=" << kdelang << endl;
    TQString fname;
    if ( !m_autoFormatLanguage.isEmpty() )
    {
        fname = locate( "data", "koffice/autocorrect/" + m_autoFormatLanguage + ".xml", m_doc->instance() );
    }
    if ( m_autoFormatLanguage != "all_languages" )
    {
        if ( fname.isEmpty() && !kdelang.isEmpty() )
            fname = locate( "data", "koffice/autocorrect/" + kdelang + ".xml", m_doc->instance() );
        if ( fname.isEmpty() && kdelang.contains("_") )
        {
            kdelang.remove( TQRegExp( "_.*" ) );
            fname = locate( "data", "koffice/autocorrect/" + kdelang + ".xml", m_doc->instance() );
        }
        if ( fname.isEmpty() )
            fname = locate( "data", "koffice/autocorrect/autocorrect.xml", m_doc->instance() );
    }
    if ( fname.isEmpty() )
        return;
    TQFile xmlFile(fname);
    if(!xmlFile.open(IO_ReadOnly))
        return;

    TQDomDocument doc;
    if(!doc.setContent(&xmlFile))
        return;

    if(doc.doctype().name() != "autocorrection") {
        //return;
    }
    TQDomElement de=doc.documentElement();

    loadAutoCorrection( de );

    TQDomElement upper = de.namedItem( "UpperCaseExceptions" ).toElement();
    if(!upper.isNull())
    {
        TQDomNodeList nl = upper.childNodes();
        for(uint i = 0; i < nl.count(); i++)
        {
            m_upperCaseExceptions+= nl.item(i).toElement().attribute("exception");
        }
    }

    TQDomElement twoUpper = de.namedItem( "TwoUpperLetterExceptions" ).toElement();
    if(!twoUpper.isNull())
    {
        TQDomNodeList nl = twoUpper.childNodes();
        for(uint i = 0; i < nl.count(); i++)
        {
            m_twoUpperLetterException+= nl.item(i).toElement().attribute("exception");
        }
    }

    TQDomElement superScript = de.namedItem( "SuperScript" ).toElement();
    if(!superScript.isNull())
    {
        TQDomNodeList nl = superScript.childNodes();
        for(uint i = 0; i < nl.count() ; i++) {
            //bug in qmap we overwrite = false doesn't work
            //so we can't add multiple "othernb"
            m_superScriptEntries.insert( nl.item(i).toElement().attribute("find"), KoAutoFormatEntry(nl.item(i).toElement().attribute("super")),FALSE );
        }
    }

    TQDomElement doubleQuote = de.namedItem( "DoubleQuote" ).toElement();
    if(!doubleQuote.isNull())
    {
        TQDomElement childItem = doubleQuote.namedItem("doublequote").toElement();
        if ( !childItem.isNull() )
        {
            TQString attr = childItem.attribute( "begin" );
            if ( !attr.isEmpty() && attr[0] != 0 )
                m_typographicDefaultDoubleQuotes.begin = attr[0];
            attr = childItem.attribute( "end" );
            if ( !attr.isEmpty() && attr[0] != 0 )
                m_typographicDefaultDoubleQuotes.end = attr[0];
        }
    }
    TQDomElement simpleQuote = de.namedItem( "SimpleQuote" ).toElement();
    if(!simpleQuote.isNull())
    {
        TQDomElement childItem = simpleQuote.namedItem("simplequote").toElement();
        if ( !childItem.isNull() )
        {
            TQString attr = childItem.attribute( "begin" );
            if ( !attr.isEmpty() && attr[0] != 0 )
                m_typographicDefaultSimpleQuotes.begin = attr[0];
            attr = childItem.attribute( "end" );
            if ( !attr.isEmpty() && attr[0] != 0 )
                m_typographicDefaultSimpleQuotes.end = attr[0];
        }
    }
}

void KoAutoFormat::loadAllLanguagesAutoCorrection()
{
    TQString fname = locate( "data", "koffice/autocorrect/all_languages.xml", m_doc->instance() );
    if ( fname.isEmpty() )
        return;
    TQFile xmlFile( fname );
    if(xmlFile.open(IO_ReadOnly))
    {
        TQDomDocument doc;
        if(!doc.setContent(&xmlFile)) {
            return;
        }
        if(doc.doctype().name() != "autocorrection") {
            //return;
        }
        TQDomElement de=doc.documentElement();

        loadAutoCorrection( de, true );
        xmlFile.close();
    }
}

void KoAutoFormat::loadAutoCorrection( const TQDomElement & _de, bool _allLanguages )
{
    TQDomElement item = _de.namedItem( "items" ).toElement();
    if(!item.isNull())
    {
        TQDomNodeList nl = item.childNodes();
        m_maxFindLength=nl.count();
        for(uint i = 0; i < m_maxFindLength; i++) {
            loadEntry( nl.item(i).toElement(), _allLanguages);
        }
    }
}

void KoAutoFormat::loadEntry( const TQDomElement &nl, bool _allLanguages)
{
    KoAutoFormatEntry *tmp =new KoAutoFormatEntry(nl.attribute("replace"));
    if ( nl.hasAttribute("FONT"))
    {
        tmp->createNewEntryContext();
        tmp->formatEntryContext()->m_family=nl.attribute("FONT");
        tmp->formatEntryContext()->m_optionsMask |= KoSearchContext::Family;
    }
    if ( nl.hasAttribute("SIZE" ))
    {
        tmp->createNewEntryContext();
        tmp->formatEntryContext()->m_size = nl.attribute("SIZE" ).toInt();
        tmp->formatEntryContext()->m_optionsMask |= KoSearchContext::Size;
    }
    if (nl.hasAttribute("BOLD" ))
    {
        tmp->createNewEntryContext();
        tmp->formatEntryContext()->m_optionsMask |= KoSearchContext::Bold;
        TQString value = nl.attribute("BOLD");
        if ( value.toInt() == 1 )
            tmp->formatEntryContext()->m_options |= KoSearchContext::Bold;
    }
    if (nl.hasAttribute("ITALIC" ))
    {
        tmp->createNewEntryContext();
        tmp->formatEntryContext()->m_optionsMask |= KoSearchContext::Italic;
        TQString value = nl.attribute("ITALIC");
        if ( value.toInt() == 1 )
            tmp->formatEntryContext()->m_options |= KoSearchContext::Italic;
    }
    if (nl.hasAttribute("UNDERLINE" ))
    {
        tmp->createNewEntryContext();
        tmp->formatEntryContext()->m_optionsMask |= KoSearchContext::Underline;
        TQString value = nl.attribute("UNDERLINE");
        if ( value =="single" )
            tmp->formatEntryContext()->m_underline = KoTextFormat::U_SIMPLE;
        else if ( value =="double" )
            tmp->formatEntryContext()->m_underline = KoTextFormat::U_DOUBLE;
        else if ( value =="single-bold" )
            tmp->formatEntryContext()->m_underline = KoTextFormat::U_SIMPLE_BOLD;
        else
            tmp->formatEntryContext()->m_underline = KoTextFormat::U_NONE;
    }
    if (nl.hasAttribute("STRIKEOUT" ))
    {
        tmp->createNewEntryContext();
        tmp->formatEntryContext()->m_optionsMask |= KoSearchContext::StrikeOut;
        TQString value = nl.attribute("STRIKEOUT");
        if ( value =="single" )
            tmp->formatEntryContext()->m_strikeOut = KoTextFormat::S_SIMPLE;
        else if ( value =="double" )
            tmp->formatEntryContext()->m_strikeOut = KoTextFormat::S_DOUBLE;
        else if ( value =="single-bold" )
            tmp->formatEntryContext()->m_strikeOut = KoTextFormat::S_SIMPLE_BOLD;
        else
            tmp->formatEntryContext()->m_strikeOut = KoTextFormat::S_NONE;
    }
    if (nl.hasAttribute("VERTALIGN" ))
    {
        tmp->createNewEntryContext();
        tmp->formatEntryContext()->m_optionsMask |= KoSearchContext::VertAlign;
        TQString value = nl.attribute("VERTALIGN");
        tmp->formatEntryContext()->m_vertAlign=static_cast<KoTextFormat::VerticalAlignment>( value.toInt() );

    }
    if ( nl.hasAttribute("TEXTCOLOR" ))
    {
        tmp->createNewEntryContext();
        tmp->formatEntryContext()->m_optionsMask |= KoSearchContext::Color;
        TQColor col( nl.attribute("TEXTCOLOR" ));
        tmp->formatEntryContext()->m_color = col;
    }
    if ( nl.hasAttribute("TEXTBGCOLOR" ))
    {
        tmp->createNewEntryContext();
        tmp->formatEntryContext()->m_optionsMask |= KoSearchContext::BgColor;
        TQColor col( nl.attribute("TEXTBGCOLOR" ));
        tmp->formatEntryContext()->m_backGroundColor = col;
    }
    if ( !_allLanguages )
        m_entries.insert( nl.attribute("find"), tmp );
    else
        m_allLanguages.insert( nl.attribute("find"), tmp );

}

void KoAutoFormat::saveConfig()
{
    TDEConfig* config = KoGlobal::kofficeConfig();
    TDELocale tdelocale(m_doc->instance()->instanceName());

    TDEConfigGroupSaver cgs( config, "AutoFormat" );
    config->writeEntry( "ConvertUpperCase", m_convertUpperCase );
    config->writeEntry( "formatLanguage", m_autoFormatLanguage=="all_languages" ? tdelocale.languageList().front() : m_autoFormatLanguage);

    config->writeEntry( "ConvertUpperUpper", m_convertUpperUpper );
    config->writeEntry( "includeTwoLetterException", m_includeTwoUpperLetterException );
    config->writeEntry( "includeAbbreviation", m_includeAbbreviation );

    config->writeEntry( "TypographicQuotesBegin", TQString( m_typographicDoubleQuotes.begin ) );
    config->writeEntry( "TypographicQuotesEnd", TQString( m_typographicDoubleQuotes.end ) );
    config->writeEntry( "TypographicQuotesEnabled", m_typographicDoubleQuotes.replace );
    config->writeEntry( "TypographicSimpleQuotesBegin", TQString( m_typographicSimpleQuotes.begin ) );
    config->writeEntry( "TypographicSimpleQuotesEnd", TQString( m_typographicSimpleQuotes.end ) );
    config->writeEntry( "TypographicSimpleQuotesEnabled", m_typographicSimpleQuotes.replace );

    config->writeEntry( "AdvancedAutocorrect", m_advancedAutoCorrect );
    config->writeEntry( "AutoCorrectionWithFormat", m_bAutoCorrectionWithFormat );
    config->writeEntry( "CapitalizeNameOfDays", m_bCapitalizeNameOfDays );

    config->writeEntry( "AutoDetectUrl",m_autoDetectUrl);

    config->writeEntry( "IgnoreDoubleSpace",m_ignoreDoubleSpace );
    config->writeEntry( "RemoveSpaceBeginEndLine",m_removeSpaceBeginEndLine );

    config->writeEntry( "UseBulletStyle", m_useBulletStyle);
    config->writeEntry( "BulletStyle", TQString(m_bulletStyle));

    config->writeEntry( "AutoChangeFormat", m_autoChangeFormat);

    config->writeEntry( "AutoReplaceNumber", m_autoReplaceNumber);

    config->writeEntry( "AutoNumberStyle", m_useAutoNumberStyle );

    config->writeEntry( "AutoSuperScript", m_bAutoSuperScript );

    config->setGroup( "completion" );
    config->writeEntry( "completion", m_completion );
    config->writeEntry( "CompletionAppendSpace", m_completionAppendSpace );
    config->writeEntry( "CompletionMinWordLength", m_minCompletionWordLength);
    config->writeEntry( "NbMaxCompletionWord", m_nbMaxCompletionWord);
    config->writeEntry( "AddCompletionWord", m_addCompletionWord );
    config->writeEntry( "ToolTipCompletion", m_toolTipCompletion );
    config->writeEntry( "CompletionKeyAction", ( int )m_keyCompletionAction );

    config->setGroup( "AutoFormatEntries" );
    TQDictIterator<KoAutoFormatEntry> it( m_entries );

    //refresh m_maxFindLength
    m_maxFindLength=0;
    TQDomDocument doc("autocorrection");

    TQDomElement begin = doc.createElement( "Word" );
    doc.appendChild( begin );
    TQDomElement items;
    items = doc.createElement("items");
    TQDomElement data;
    for ( ; it.current() ; ++it )
    {
	items.appendChild(saveEntry( it, doc));
        //m_maxFindLength=TQMAX(m_maxFindLength,it.currentKey().length());
    }
    buildMaxLen();
    begin.appendChild(items);

    TQDomElement upper;
    upper = doc.createElement("UpperCaseExceptions");
    for ( TQStringList::Iterator it = m_upperCaseExceptions.begin(); it != m_upperCaseExceptions.end();++it )
    {
	data = doc.createElement("word");
	data.setAttribute("exception",(*it) );
	upper.appendChild(data);
    }
    begin.appendChild(upper);

    TQDomElement twoUpper;
    twoUpper = doc.createElement("TwoUpperLetterExceptions");

    for ( TQStringList::Iterator it = m_twoUpperLetterException.begin(); it != m_twoUpperLetterException.end();++it )
    {
	data = doc.createElement("word");
	data.setAttribute("exception",(*it) );
	twoUpper.appendChild(data);
    }
    begin.appendChild(twoUpper);

    TQDomElement super;
    super = doc.createElement("SuperScript");
    KoAutoFormatEntryMap::Iterator it2 = m_superScriptEntries.begin();
    for ( ; it2 != m_superScriptEntries.end() ; ++it2 )
    {
	data = doc.createElement("superscript");
	data.setAttribute("find", it2.key());
	data.setAttribute("super", it2.data().replace());
	super.appendChild(data);
    }
    begin.appendChild(super);

    TQDomElement doubleQuote;
    doubleQuote = doc.createElement("DoubleQuote");
    data = doc.createElement("doublequote");
    data.setAttribute("begin", TQString(m_typographicDefaultDoubleQuotes.begin));
    data.setAttribute("end", TQString(m_typographicDefaultDoubleQuotes.end));
    doubleQuote.appendChild(data);
    begin.appendChild(doubleQuote);


    TQDomElement simpleQuote;
    simpleQuote = doc.createElement("SimpleQuote");
    data = doc.createElement("simplequote");
    data.setAttribute("begin", TQString(m_typographicDefaultSimpleQuotes.begin));
    data.setAttribute("end", TQString(m_typographicDefaultSimpleQuotes.end));
    simpleQuote.appendChild(data);
    begin.appendChild(simpleQuote);
    TQFile f;
    if ( m_autoFormatLanguage.isEmpty())
        f.setName(locateLocal("data", "koffice/autocorrect/"+tdelocale.languageList().front() + ".xml",m_doc->instance()));
    else
        f.setName(locateLocal("data", "koffice/autocorrect/"+m_autoFormatLanguage + ".xml",m_doc->instance()));
    if(!f.open(IO_WriteOnly)) {
        kdWarning()<<"Error during saving autoformat to " << f.name() << endl;
	return;
    }
    TQTextStream ts(&f);
    doc.save(ts, 2);
    f.close();
    autoFormatIsActive();
    config->sync();
}

TQDomElement KoAutoFormat::saveEntry( TQDictIterator<KoAutoFormatEntry> _entry, TQDomDocument doc)
{
    TQDomElement data;
    data = doc.createElement("item");
    data.setAttribute("find", _entry.currentKey());
    data.setAttribute("replace", _entry.current()->replace());
    if ( _entry.current()->formatEntryContext() )
    {
        KoSearchContext *tmp = _entry.current()->formatEntryContext();
        if ( tmp->m_optionsMask & KoSearchContext::Family )
        {
            data.setAttribute("FONT", tmp->m_family);
        }
        if ( tmp->m_optionsMask &  KoSearchContext::Size )
        {
            data.setAttribute("SIZE", tmp->m_size);
        }
        if ( tmp->m_optionsMask & KoSearchContext::Italic )
        {
            data.setAttribute("ITALIC", static_cast<bool>(tmp->m_options & KoSearchContext::Italic));
        }
        if ( tmp->m_optionsMask & KoSearchContext::Bold )
        {
            data.setAttribute("BOLD", static_cast<bool>(tmp->m_options & KoSearchContext::Bold));
        }
        if ( tmp->m_optionsMask & KoSearchContext::Shadow )
        {
            data.setAttribute("SHADOWTEXT", static_cast<bool>(tmp->m_options & KoSearchContext::Shadow));
        }
        if ( tmp->m_optionsMask & KoSearchContext::WordByWord )
        {
            data.setAttribute("WORDBYWORD", static_cast<bool>(tmp->m_options & KoSearchContext::WordByWord));
        }

        if ( tmp->m_optionsMask & KoSearchContext::Underline )
        {
            switch( tmp->m_underline )
            {
            case KoTextFormat::U_SIMPLE:
                data.setAttribute("UNDERLINE", "single");
                break;
            case KoTextFormat::U_DOUBLE:
                data.setAttribute("UNDERLINE", "double");
                break;
            case KoTextFormat::U_SIMPLE_BOLD:
                data.setAttribute("UNDERLINE", "single-bold");
                break;
            case KoTextFormat::U_WAVE:
                data.setAttribute("UNDERLINE", "wave");
                break;
            case KoTextFormat::U_NONE:
                data.setAttribute("UNDERLINE", "none");
                break;
            }
        }
        if ( tmp->m_optionsMask & KoSearchContext::StrikeOut )
        {
            switch( tmp->m_strikeOut )
            {
            case KoTextFormat::S_SIMPLE:
                data.setAttribute("STRIKEOUT", "single");
                break;
            case KoTextFormat::S_DOUBLE:
                data.setAttribute("STRIKEOUT", "double");
                break;
            case KoTextFormat::S_NONE:
                data.setAttribute("STRIKEOUT", "none");
                break;
            case KoTextFormat::S_SIMPLE_BOLD:
                data.setAttribute("STRIKEOUT", "single-bold");
                break;
            }
        }
        if ( tmp->m_optionsMask & KoSearchContext::Attribute )
        {
            data.setAttribute("FONTATTRIBUTE", KoTextFormat::attributeFontToString( tmp->m_attribute ) );
        }

        if ( tmp->m_optionsMask & KoSearchContext::VertAlign)
        {
            data.setAttribute( "VERTALIGN", static_cast<int>(tmp->m_vertAlign) );
        }
        if ( tmp->m_optionsMask & KoSearchContext::BgColor )
        {
            data.setAttribute( "TEXTCOLOR", tmp->m_color.name());
        }
        if ( tmp->m_optionsMask & KoSearchContext::Color )
        {
            data.setAttribute( "TEXTCOLOR", tmp->m_color.name());
        }
        if ( tmp->m_optionsMask & KoSearchContext::BgColor )
        {
            data.setAttribute( "TEXTBGCOLOR", tmp->m_backGroundColor.name());
        }
        if ( tmp->m_optionsMask & KoSearchContext::Language )
            data.setAttribute( "LANGUAGE", tmp->m_language );
    }
    return data;
}

void KoAutoFormat::addAutoFormatEntry( const TQString &key, const TQString &replace )
{
    KoAutoFormatEntry *findEntry = m_entries.find( key);
    if ( findEntry )
    {
        if ( findEntry->replace().lower() == replace.lower() )
            return;
    }

    KoAutoFormatEntry *tmp = new KoAutoFormatEntry( replace );
    m_entries.insert( key, tmp );
    saveConfig();
    buildMaxLen();
}

TQString KoAutoFormat::getLastWord(KoTextParag *parag, int const index)
{
    TQString lastWord;
    KoTextString *s = parag->string();
    for ( int i = index - 1; i >= 0; --i )
    {
        TQChar ch = s->at( i ).c;
        if ( ch.isSpace() || ch.isPunct() )
            break;
        lastWord.prepend( ch );
    }
    return lastWord;
}

TQString KoAutoFormat::getLastWord(const int max_words, KoTextParag *parag, int const index)
{
    TQString lastWord;
    KoTextString const *s = parag->string();
    int words = 0;
    for ( int i = index - 1; i >= 0; --i )
    {
        TQChar ch = s->at( i ).c;
        if ( ch.isSpace() || ch.isPunct() )
        {
                ++words;
                if (words >= max_words)
                        break;
        }
        lastWord.prepend( ch );
    }
    return lastWord;
}

TQString KoAutoFormat::getWordAfterSpace(KoTextParag *parag, int const index)
{
    TQString word;
    KoTextString *s = parag->string();
    for ( int i = index - 1; i >= 0; --i )
    {
        TQChar ch = s->at( i ).c;
        if ( ch.isSpace() )
            break;
        word.prepend( ch );
    }
    return word;

}

bool KoAutoFormat::doCompletion( KoTextCursor* textEditCursor, KoTextParag *parag, int const index, KoTextObject *txtObj )
{
    if( m_completion )
    {
        bool part=false;
        TQString lastWord, word;
        if (m_completionBox && m_completionBox->isShown() ) //word completion with the tool-tip box
        {
                word = m_completionBox->text();
                lastWord = m_completionBox->lastWord();
        }
        else
        {
                TQStringList wordlist, new_wordlist;
                for (uint i=1; i <= m_countMaxWords; i++ )
                {
                        lastWord = getLastWord(i, parag, index+1);
                        wordlist += m_listCompletion->substringCompletion( lastWord ); //find all completion words that contains lastWord
                }
                uint maxlength = 0;
                for ( TQStringList::ConstIterator it = wordlist.begin(); it != wordlist.end(); ++it ) // several completion words were found
                {
                  if ( (*it).startsWith( lastWord, false ) && new_wordlist.find(*it) == new_wordlist.end() ) //the completion words that begin with lastWord
                  {
                    if ( (*it).length() > maxlength )
                      maxlength = (*it).length();
                    new_wordlist.append(*it);
                    //kdDebug() << "adding word completion:" << *it << endl;
                  }
                }
                if ( new_wordlist.isEmpty() )
                    return false;
                if ( new_wordlist.count() == 1 ) // only one completion word was found
                  word = new_wordlist.first();
                else
                {
                  //we must extract the common part of the completions
                  for (uint i = lastWord.length(); i<maxlength && !part; i++) //iterate through all completion words
                  {
                    TQChar ch = new_wordlist.first().at(i);
                    for (TQStringList::ConstIterator it = new_wordlist.begin(); it != new_wordlist.end(); ++it )
                    {
                      if ( (*it).at(i).lower() != ch.lower() )
                      {
                        word = (*it).left(i); //the completion word is truncated here
                        //kdDebug() << "set the word completion to:" << word << endl;
                        part=true; // completion of a part of a word; a space-character after the completion should not be inserted
                        break;
                      }
                    }
                  }
                }
                if (word == lastWord)
                        return false;

                word=lastWord+word.right(word.length()-lastWord.length() );
        }
        if( !word.isEmpty() )
        {
            int const lastword_length = lastWord.length();
            int const start = index+1 - lastword_length;
            int const length = word.length();

            KMacroCommand *macro = new KMacroCommand( i18n("Completion Word"));
            KoTextCursor cursor( parag->document() );
            cursor.setParag( parag );
            cursor.setIndex( start );
            KoTextDocument * textdoc = parag->textDocument();
            if( m_completionAppendSpace && !part)
                word+=" ";
            textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
            cursor.setIndex( start + lastword_length );
            textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );

            macro->addCommand( txtObj->replaceSelectionCommand( textEditCursor, word,
                                                                i18n("Completion Word"),
                                                                KoTextDocument::HighlightSelection ));

            if ( m_completionAppendSpace && !m_ignoreUpperCase && (m_convertUpperUpper || m_convertUpperCase) && !part)
            {
                //find the first word
                for (uint i=1; i < word.length(); i++)
                        if ( word.at(i).isSpace() || word.at(i).isPunct() )
                        {
                               word.truncate(i);
                               break;
                        }
                int const newPos = start + word.length();// + index - 3;
                KCommand *cmd = doUpperCase( textEditCursor, parag, newPos, word, txtObj );
                if( cmd )
                    macro->addCommand( cmd );
                txtObj->emitHideCursor();
                textEditCursor->setIndex(start+ length+1);
            }
            else
            {
                txtObj->emitHideCursor();
                textEditCursor->setIndex(start+ length);
            }
            txtObj->emitNewCommand( macro );

            // The space/tab/CR that we inserted is still there but delete/insert moved the cursor
            // -> go right

            txtObj->emitShowCursor();
            removeToolTipCompletion();
            return true;
        }
    }
    return false;
}

bool KoAutoFormat::doToolTipCompletion( KoTextCursor* textEditCursor, KoTextParag *parag, int index, KoTextObject *txtObj, int keyPressed )
{
    if( m_completion && m_toolTipCompletion && m_completionBox && m_completionBox->isShown() )
    {
        if ( ( keyPressed == TQt::Key_Return && m_keyCompletionAction==Enter )
             || ( keyPressed == TQt::Key_Enter && m_keyCompletionAction==Enter )
             || ( keyPressed == TQt::Key_Tab && m_keyCompletionAction==Tab )
             || ( keyPressed == TQt::Key_Space && m_keyCompletionAction==Space )
             || ( keyPressed == TQt::Key_End && m_keyCompletionAction==End )
             || ( keyPressed == TQt::Key_Right && m_keyCompletionAction==Right ))
        {
            return doCompletion(textEditCursor, parag, index, txtObj);
        }
    }
    return false;
}
void KoAutoFormat::showToolTipBox(KoTextParag *parag,  int index, TQWidget *widget, const TQPoint &pos )
{

    if( m_completion && m_toolTipCompletion)
    {
        TQString lastWord, word;
        for (uint i=1; i <= m_countMaxWords; i++ )
        {
                lastWord = getLastWord(i, parag, index+1);
                word=m_listCompletion->makeCompletion( lastWord );
                if ( !word.isEmpty())
                        break;
        }
        if( !word.isEmpty() && word!=lastWord )
        {
            uint const length = lastWord.length();
            if (length<=3)
                return;
            word=lastWord+word.right(word.length()-length);
            if (!m_completionBox)
                m_completionBox = new KoCompletionBox(0,0,TQt::WType_Popup);
            TQPoint const show_pos = widget->mapToGlobal(pos);
            m_completionBox->setText(word);
            m_completionBox->setLastWord(lastWord);
            m_completionBox->adjustSize();
            int const height = m_completionBox->sizeHint().height();
            m_completionBox->move( show_pos.x(), show_pos.y() - height );

            if (!m_completionBox->isShown() )
            {
                m_completionBox->show();
                widget->setFocus();
            }
        }
        else
                removeToolTipCompletion();
    }
}
void KoAutoFormat::removeToolTipCompletion()
{
    if (m_completion && m_toolTipCompletion && m_completionBox && m_completionBox->isShown())
        m_completionBox->hide();
}

void KoAutoFormat::autoFormatIsActive()
{
    m_bAutoFormatActive = m_useBulletStyle ||
                          m_removeSpaceBeginEndLine ||
                          m_autoDetectUrl ||
                          m_convertUpperUpper ||
                          m_convertUpperCase ||
                          m_autoReplaceNumber ||
                          m_autoChangeFormat ||
                          m_completion ||
                          m_typographicDoubleQuotes.replace ||
                          m_typographicSimpleQuotes.replace ||
                          m_entries.count()!=0 ||
                          m_allLanguages.count()!=0;
}

void KoAutoFormat::doAutoFormat( KoTextCursor* textEditCursor, KoTextParag *parag, int index, TQChar ch,KoTextObject *txtObj )
{
    m_ignoreUpperCase = false;

    if ( !m_configRead )
        readConfig();

    if ( !m_bAutoFormatActive )
        return;

    if( ch.isSpace())
    {
        //a link doesn't have a space
        //=>m_ignoreUpperCase = false
        //m_ignoreUpperCase=false;

        TQString word=getWordAfterSpace(parag,index);

        if ( m_autoChangeFormat && index > 3)
        {
            KCommand *cmd =doAutoChangeFormat( textEditCursor, parag, index, word, txtObj );
            if ( cmd )
                txtObj->emitNewCommand( cmd );

        }
        if ( m_autoReplaceNumber )
        {
            KCommand *cmd = doAutoReplaceNumber( textEditCursor, parag, index, word, txtObj );
            if ( cmd )
                txtObj->emitNewCommand( cmd );
        }
    }

    if( ch =='\n' )
    {

        if( m_removeSpaceBeginEndLine && index > 1)
        {
            KCommand *cmd = doRemoveSpaceBeginEndLine( textEditCursor, parag, txtObj, index );
            if ( cmd )
                txtObj->emitNewCommand( cmd );
        }
        if( m_useBulletStyle  && index > 3)
        {
            KCommand *cmd =doUseBulletStyle( textEditCursor, parag, txtObj, index );
            if ( cmd )
                txtObj->emitNewCommand( cmd );
        }
        if( m_useAutoNumberStyle && index > 3 )
        {
            KCommand *cmd =doUseNumberStyle( textEditCursor, parag, txtObj, index );
            if ( cmd )
                txtObj->emitNewCommand( cmd );
        }
        if( m_convertUpperUpper && m_includeTwoUpperLetterException )
            doAutoIncludeUpperUpper(textEditCursor, parag, txtObj );
        if( m_convertUpperCase && m_includeAbbreviation )
            doAutoIncludeAbbreviation(textEditCursor, parag, txtObj );
    }

    //kdDebug(32500) << "KoAutoFormat::doAutoFormat ch=" << TQString(ch) << endl;
    //if ( !m_enabled )
    //    return;
    // Auto-correction happens when pressing space, tab, CR, punct etc.
    if ( (ch.isSpace() || ch==':' || ch=='?' || ch=='!' || ch==',' || (m_advancedAutoCorrect && ch=='.') ) && index > 0 )
    {
        KCommand *cmd = 0L;
        KMacroCommand *macro = 0L;
        TQString lastWord = getWordAfterSpace(parag, index);
        //kdDebug(32500) << "KoAutoFormat::doAutoFormat lastWord=" << lastWord << endl;

        if ( ch == '.')
                detectStartOfLink( parag, index, true );
        else
                detectStartOfLink( parag, index, false );

        if ( !m_wordInserted && m_advancedAutoCorrect && !m_ignoreUpperCase)
        {
                int const completionBeginPos = index -lastWord.length();
                int newPos = index;
                cmd = doAutoCorrect( textEditCursor, parag, newPos, txtObj );

                if( cmd )
                {
                if (!macro)
                        macro = new KMacroCommand(i18n("Autocorrection"));
                macro->addCommand( cmd );
                }

                int const endPos=textEditCursor->index();
                bool was_a_replacement;
                if (index == newPos)
                        was_a_replacement = false;
                else
                        was_a_replacement = true;

                if( was_a_replacement) // a replacement took place
                {
                        txtObj->emitHideCursor();
                        if(endPos==0) //new line, the user pressed enter
                        {
                                textEditCursor->gotoUp();
                                textEditCursor->gotoLineEnd();
                                newPos=textEditCursor->index();
                        }
                        else
                                newPos= endPos-1;

                        m_wordInserted = true; //don't allow other replacements in this replacement
                        for(int i=completionBeginPos; i<newPos;i++)
                        {
                                textEditCursor->setIndex(i);
                                doAutoFormat( textEditCursor, parag, i, parag->toString().at(i),txtObj );

                        }
                        textEditCursor->setIndex(newPos);
                        doAutoFormat( textEditCursor, parag, newPos, ch,txtObj );
                        m_wordInserted = false;
                        if (endPos==0)
                        {
                                textEditCursor->gotoLineStart();
                                textEditCursor->gotoDown();
                        }
                        else
                                textEditCursor->setIndex(newPos+1);
                        txtObj->emitShowCursor();
                        return;
                }

        }

        if (!m_ignoreUpperCase && m_bCapitalizeNameOfDays)
        {
            KCommand *cmd = doCapitalizeNameOfDays( textEditCursor, parag, index, lastWord, txtObj  );

            if( cmd )
            {
                if (!macro)
                macro = new KMacroCommand(i18n("Autocorrection"));
                macro->addCommand( cmd );
                m_ignoreUpperCase = true;
            }
        }

        if (ch=='.')
                return;

        //kdDebug(32500)<<" m_listCompletion->items() :"<<m_listCompletion->items()<<endl;
        if( !m_ignoreUpperCase && m_completion && m_addCompletionWord && m_listCompletion->items().count() < m_nbMaxCompletionWord )
        {
                TQString completionWord("");
                TQChar ch;
                for (uint i=0;i<lastWord.length();i++)
                {
                        ch = lastWord.at(i);
                        if (ch.isPunct() && ch!='-' && ch!='=' )
                        {
                                if (completionWord.at(0) == '-')
                                        completionWord.remove(0,1);

                                if (completionWord.length()>= m_minCompletionWordLength  && !completionWord.isEmpty() && m_listCompletion->makeCompletion(completionWord).isEmpty())
                                {
                                        kdDebug() << "Adding:" << completionWord << endl;
                                        m_listCompletion->addItem( completionWord );
                                        if ( completionWord.length() > m_countMaxWords )
                                            m_countMaxWords = completionWord.length();

                                }
                                completionWord = "";
                        }
                        else
                        {
                                completionWord.append(ch);
                                if (i==lastWord.length()-1)
                                {
                                        if (completionWord.at(0) == '-')
                                                completionWord.remove(0,1);
                                        if (completionWord.at(completionWord.length()-1) == '-')
                                                completionWord.truncate(completionWord.length()-1);
                                        completionWord.remove('=');
                                        if (completionWord.length()>= m_minCompletionWordLength && !completionWord.isEmpty() && m_listCompletion->makeCompletion(completionWord).isEmpty())
                                        {
                                                kdDebug() << "Adding:" << completionWord << endl;
                                                m_listCompletion->addItem( completionWord );
                                                if ( completionWord.length() > m_countMaxWords )
                                                    m_countMaxWords = completionWord.length();
                                        }
                                }
                        }
                }
        }

        if( m_autoDetectUrl && m_ignoreUpperCase && (ch!='?' || lastWord.at(lastWord.length()-1)=='?') )
        {
                doAutoDetectUrl( textEditCursor, parag, index, lastWord, txtObj );
                //textEditCursor->gotoRight();
        }

        if (!m_ignoreUpperCase && (m_convertUpperUpper || m_convertUpperCase) )
        {
            cmd = doUpperCase( textEditCursor, parag, index, lastWord, txtObj );

            if( cmd )
            {
                if (!macro)
                    macro = new KMacroCommand(i18n("Autocorrection"));
                macro->addCommand( cmd );
            }
        }

        if ( macro )
            txtObj->emitNewCommand( macro );

        if(!m_ignoreUpperCase &&  m_bAutoSuperScript && m_superScriptEntries.count()>0)
        {
            if( lastWord.at(0).isPunct() )
                lastWord.remove(0,1);
            KCommand * cmd = doAutoSuperScript( textEditCursor, parag, index, lastWord, txtObj  );
            if ( cmd )
                txtObj->emitNewCommand( cmd );
        }

    }
    else
    {
        if ( ch == '"' && m_typographicDoubleQuotes.replace )
        {
                KCommand *cmd = doTypographicQuotes( textEditCursor, parag, index, txtObj, true /*double quote*/ );
                if ( cmd )
                txtObj->emitNewCommand( cmd );
        }
        else if ( ch == '\'' && m_typographicDoubleQuotes.replace )
        {
                KCommand *cmd = doTypographicQuotes( textEditCursor, parag, index, txtObj, false /* simple quote*/ );
                if ( cmd )
                txtObj->emitNewCommand( cmd );
        }
    }
}

KCommand *KoAutoFormat::doAutoCorrect( KoTextCursor* textEditCursor, KoTextParag *parag, int &index, KoTextObject *txtObj )
{
    //if(!m_advancedAutoCorrect)
      //  return 0L;
    // Prepare an array with words of different lengths, all terminating at "index".
    // Obviously only full words are put into the array
    // But this allows 'find strings' with spaces and punctuation in them.
    TQString * wordArray = new TQString[m_maxFindLength+1];
    {
        TQString word;
        KoTextString *s = parag->string();
        for ( int i = index - 1; i >= 0; --i )
        {
            TQChar ch = s->at( i ).c;
	    // It's necessary to stop at spaces - #99063
            if ( ch.isSpace() /*|| ch.isPunct()*/ || i==0)
            {
                if(i==0 && word.length()<m_maxFindLength)
                    word.prepend( ch );
                wordArray[word.length()]=word;
            }
            word.prepend( ch );
            if (((index - 1)-i) == (int)m_maxFindLength)
                break;
        }

    }
    KCommand *cmd = autoFormatWord( textEditCursor, parag, index, txtObj, wordArray, false );
    if ( !cmd )
        cmd = autoFormatWord( textEditCursor, parag, index, txtObj, wordArray, true );
    delete [] wordArray;
    return cmd;
}


KCommand *KoAutoFormat::autoFormatWord( KoTextCursor* textEditCursor, KoTextParag *parag, int &index, KoTextObject *txtObj, TQString * _wordArray, bool _allLanguages )
{
    KoTextDocument * textdoc = parag->textDocument();

    // Now for each entry in the autocorrect list, look if
    // the word of the same size in wordArray matches.
    // This allows an o(n) behaviour instead of an o(n^2).
    for(int i=m_maxFindLength;i>0;--i)
    {
        if ( !_wordArray[i].isEmpty())
        {
            KoAutoFormatEntry* it = 0L;
            if ( _allLanguages )
                it = m_allLanguages[ _wordArray[i] ];
            else
                it = m_entries[ _wordArray[i] ];
            if ( _wordArray[i]!=0  && it )
            {
                unsigned int length = _wordArray[i].length();
                int const start = index - length;
                KoTextCursor cursor( parag->document() );
                cursor.setParag( parag );
                cursor.setIndex( start );
                textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
                cursor.setIndex( start + length );
                textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );
                KCommand *cmd = 0L;
                kdDebug()<<"it->replace() :"<<it->replace()<<endl;
                if (!it->formatEntryContext() || !m_bAutoCorrectionWithFormat)
                {
                    cmd = txtObj->replaceSelectionCommand( textEditCursor, it->replace(),
                                                           i18n("Autocorrect Word"),
                                                           KoTextDocument::HighlightSelection );
                }
                else
                {
                    int flags = 0;
                    KoTextFormat * lastFormat = parag->at( start )->format();
                    KoTextFormat * newFormat = new KoTextFormat(*lastFormat);
                    changeTextFormat(it->formatEntryContext(), newFormat, flags );
                    KMacroCommand *macro = new KMacroCommand( i18n("Autocorrect Word with Format"));
                    KCommand *cmd2=txtObj->replaceSelectionCommand( textEditCursor, it->replace(),
                                                                    i18n("Autocorrect Word"),
                                                                    KoTextDocument::HighlightSelection );
                    if ( cmd2 )
                        macro->addCommand(cmd2);
                    KoTextCursor cursor( parag->document() );
                    cursor.setParag( parag );
                    cursor.setIndex( start );
                    textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
                    cursor.setIndex( start + it->replace().length()/*+ length + 1*/ );
                    textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );

                    cmd2 =txtObj->setFormatCommand( textEditCursor, &lastFormat, newFormat, flags, false, KoTextDocument::HighlightSelection );
                    macro->addCommand( cmd2);

                    index = index - length + it->replace().length();
                    textEditCursor->setIndex(index+1);
                    cmd2 =txtObj->setFormatCommand( textEditCursor, &newFormat, lastFormat, 0 );
                    macro->addCommand( cmd2);
                    parag->at( index+1 )->setFormat(lastFormat);

                    cmd = macro;
                    txtObj->emitHideCursor();
                    textEditCursor->gotoRight();
                    txtObj->emitShowCursor();

                    return cmd;
                }
                // The space/tab/CR that we inserted is still there but delete/insert moved the cursor
                // -> go right

                txtObj->emitHideCursor();
                textEditCursor->gotoRight();
                txtObj->emitShowCursor();
                index = index - length + it->replace().length();
                return cmd;
            }
        }
    }
    return 0L;
}

KCommand *KoAutoFormat::doTypographicQuotes( KoTextCursor* textEditCursor, KoTextParag *parag, int index, KoTextObject *txtObj, bool doubleQuotes )
{
    //kdDebug(32500) << "KoAutoFormat::doTypographicQuotes" << endl;
    KoTextDocument * textdoc = parag->textDocument();
    KoTextCursor cursor( parag->document() );
    cursor.setParag( parag );
    cursor.setIndex( index );
    textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
    cursor.setIndex( index + 1 );
    textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );

    // Need to determine if we want a starting or ending quote.
    // we use a starting quote in three cases:
    //   1. if the previous character is a space
    //   2. if the previous character is some kind of opening punctuation (e.g., "(", "[", or "{")
    //      a. and the character before that is not an opening quote (so that we get quotations of single characters
    //         right)
    //  3. if the previous character is an opening quote (so that we get nested quotations right)
    //      a. and the character before that is not an opening quote (so that we get quotations of single characters
    //         right)
    //      b. and the previous quote of a different kind (so that we get empty quotations right)
    TQString replacement;
    bool ending = true;

    if( index > 0 )
    {
	TQChar::Category c1 = parag->at( index - 1 )->c.category();

	// case 1 and 2
	if ( c1 == TQChar::Separator_Space || c1 == TQChar::Separator_Line || c1 == TQChar::Separator_Paragraph ||
	    c1 == TQChar::Punctuation_Open || c1 == TQChar::Other_Control )
	    ending = false;

	// case 3
	if ( c1 == TQChar::Punctuation_InitialQuote )
	{
	    TQChar openingQuote;

	    if( doubleQuotes )
		openingQuote = m_typographicDoubleQuotes.begin;
            else
		openingQuote = m_typographicSimpleQuotes.begin;

	    // case 3b
	    if( parag->at( index - 1 )->c != openingQuote )
		ending = false;
	}
    }

    // cases 2a and 3a
    if( index > 1 && !ending )
    {
	TQChar::Category c2 = parag->at( index - 2 )->c.category();
	ending = (c2 == TQChar::Punctuation_InitialQuote);
    }

    if( ending )
    {
        if( doubleQuotes )
            replacement = m_typographicDoubleQuotes.end;
        else
            replacement = m_typographicSimpleQuotes.end;
    }
    else
    {
        if( doubleQuotes )
            replacement = m_typographicDoubleQuotes.begin;
        else
            replacement = m_typographicSimpleQuotes.begin;
    }
    return txtObj->replaceSelectionCommand( textEditCursor, replacement,
                                            i18n("Typographic Quote"),
                                            KoTextDocument::HighlightSelection );
}

KCommand * KoAutoFormat::doUpperCase( KoTextCursor *textEditCursor, KoTextParag *parag,
                                int index, const TQString & word, KoTextObject *txtObj )
{
    KoTextDocument * textdoc = parag->textDocument();
    unsigned int length = word.length();
    if (word.at(length-1) == '.' )
    {
        --index;
        --length;
    }
    int const start = index - length;
    KoTextCursor backCursor( parag->document() );
    backCursor.setParag( parag );
    backCursor.setIndex( start );

    // backCursor now points at the first char of the word
    TQChar const firstChar = backCursor.parag()->at( backCursor.index() )->c;

    bool bNeedMove = false;
    KCommand *cmd = 0L;
    if ( m_convertUpperCase && isLower( firstChar ) )
    {
        bool beginningOfSentence = true; // true if beginning of text
        // Go back over any space/tab/CR
        while ( backCursor.index() > 0 || backCursor.parag()->prev() )
        {
            beginningOfSentence = false; // we could go back -> false unless we'll find '.'
            backCursor.gotoLeft();
            if ( !backCursor.parag()->at( backCursor.index() )->c.isSpace() )
                break;
        }
        // We are now at the first non-space char before the word
        if ( !beginningOfSentence )
                beginningOfSentence = isMark( backCursor.parag()->at( backCursor.index() )->c);
            //beginningOfSentence = isMark( backCursor.parag()->at( backCursor.index() )->c ) && (backCursor.parag()->at( backCursor.index()+1 )->c.isSpace());
        if ( !beginningOfSentence && start==0 )
            if ( parag->counter() || backCursor.parag()->at( backCursor.index() )->c.isPunct() )
                beginningOfSentence = true;

        // Now look for exceptions
        if ( beginningOfSentence )
        {
            TQChar const punct = backCursor.parag()->at( backCursor.index() )->c;
            TQString const text = getLastWord( backCursor.parag(), backCursor.index() )
                           + punct;
                           kdDebug() << "text: " << text << endl;
            // text has the word at the end of the 'sentence', including the termination. Example: "Mr."
            beginningOfSentence = (m_upperCaseExceptions.findIndex(text)==-1); // Ok if we can't find it
        }

        if ( beginningOfSentence )
        {
            KoTextCursor cursor( parag->document() );
            cursor.setParag( parag );
            cursor.setIndex( start );
            textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
            cursor.setIndex( start + 1 );
            textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );
            cmd = txtObj->replaceSelectionCommand( textEditCursor, TQString( firstChar.upper() ),
                                                   i18n("Autocorrect (capitalize first letter)"),
                                                   KoTextDocument::HighlightSelection );
            bNeedMove = true;
        }
    }
    else if ( m_convertUpperUpper && isUpper( firstChar ) && length > 2 )
    {
        backCursor.setIndex( backCursor.index() + 1 );
        TQChar secondChar = backCursor.parag()->at( backCursor.index() )->c;
        //kdDebug(32500)<<" secondChar :"<<secondChar<<endl;
        if ( isUpper( secondChar ) )
        {
            // Check next letter - we still want to be able to write fully uppercase words...
            backCursor.setIndex( backCursor.index() + 1 );
            TQChar thirdChar = backCursor.parag()->at( backCursor.index() )->c;
            if ( isLower( thirdChar ) && (m_twoUpperLetterException.findIndex(word)==-1))
            {
                // Ok, convert
                KoTextCursor cursor( parag->document() );
                cursor.setParag( parag );
                cursor.setIndex( start + 1 ); // After all the first letter's fine, so only change the second letter
                textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
                cursor.setIndex( start + 2 );
                textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );

                TQString replacement = word[1].lower();
                cmd = txtObj->replaceSelectionCommand( textEditCursor, replacement,
                                                       i18n("Autocorrect"),
                                                       KoTextDocument::HighlightSelection );

                bNeedMove = true;
            }
        }
    }
    if ( bNeedMove )
    {
        if (word.at(word.length()-1) == '.' )
                ++index;
        txtObj->emitHideCursor();
        textEditCursor->setParag( parag );
        textEditCursor->setIndex( index );
        textEditCursor->gotoRight(); // not the same thing as index+1, in case of CR
        txtObj->emitShowCursor();
    }
    return cmd;
}

KCommand * KoAutoFormat::doAutoReplaceNumber( KoTextCursor* textEditCursor, KoTextParag *parag, int& index, const TQString & word , KoTextObject *txtObj )
{
    unsigned int length = word.length();
    if ( length != 3 )
        return 0L;
    KoTextDocument * textdoc = parag->textDocument();
    int start = index - length;
    if( word == TQString("1/2") || word == TQString("1/4") || word == TQString("3/4") )
    {
        KoTextCursor cursor( parag->document() );
        cursor.setParag( parag );
        cursor.setIndex( start );
        textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
        cursor.setIndex( start + length );
        textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );
        TQString replacement;
        if( word == TQString("1/2") )
            replacement=TQString("½");
        else if (word == TQString("1/4") )
            replacement=TQString("¼");
        else if (word == TQString("3/4") )
            replacement=TQString("¾");
        TQString cmdName = i18n("Autocorrect for Fraction");
        KCommand *cmd =txtObj->replaceSelectionCommand( textEditCursor, replacement,
                                                        cmdName,
                                                        KoTextDocument::HighlightSelection );
        txtObj->emitHideCursor();
        textEditCursor->gotoRight();
        txtObj->emitShowCursor();
        index = index - length + replacement.length();
        return cmd;
    }
    return 0L;
}

void KoAutoFormat::detectStartOfLink(KoTextParag * parag, int const index, bool const insertedDot)
{
    TQString word;
    KoTextString *s = parag->string();
    for ( int i = 0; i < index; ++i )
    {
        word.append( s->at( i ).c );
    }

    if (word.find("http")!=-1 || word.find("https")!=-1 || word.find("mailto")!=-1 || word.find("ftp")!=-1 || word.find("file")!=-1
        || word.find("news")!=-1 || word.find('@')!=-1)
                m_ignoreUpperCase=true;
    else
    {
        int const tmp_pos=word.find("www.");
        if (tmp_pos!=-1 && (word.find('.',tmp_pos+4)!=-1 || insertedDot) )
               m_ignoreUpperCase=true;
    }
}

void KoAutoFormat::doAutoDetectUrl( KoTextCursor *textEditCursor, KoTextParag *parag, int &index, TQString & word, KoTextObject *txtObj )
{
    kdDebug() << "link:" << word << endl;
    char link_type = 0;
    int pos = word.find("http://");
    int tmp_pos = word.find("https://");
    if(tmp_pos<pos && tmp_pos!=-1)
          pos = tmp_pos;
    tmp_pos = word.find("mailto:/");
    if((tmp_pos<pos || pos==-1 ) && tmp_pos!=-1)
          pos = tmp_pos;
    tmp_pos = word.find("ftp://");
    if((tmp_pos<pos || pos==-1 ) && tmp_pos!=-1)
          pos = tmp_pos;
    tmp_pos = word.find("ftp.");
    if((tmp_pos<pos || pos==-1 ) && tmp_pos!=-1)
    {
          pos = tmp_pos;
          link_type = 3;
    }
    tmp_pos = word.find("file:/");
    if((tmp_pos<pos || pos==-1 ) && tmp_pos!=-1)
          pos = tmp_pos;
    tmp_pos = word.find("news:");
    if((tmp_pos<pos || pos==-1 ) && tmp_pos!=-1)
          pos = tmp_pos;
    tmp_pos = word.find("www.");
    if((tmp_pos<pos || pos==-1 ) && tmp_pos!=-1 && word.find('.',tmp_pos+4)!=-1 )
    {
          pos = tmp_pos;
          link_type = 2;
    }
    tmp_pos = word.find('@');
    if ( pos == -1 && tmp_pos != -1 )
    {
          pos = tmp_pos-1;
          TQChar c;
          while( pos>=0 )
          {
                c = word.at(pos);
                if ( c.isPunct() && c!='.'&& c!='_')    break;
                else    --pos;
          }
          if ( pos == tmp_pos-1 ) //it not a valid address
          {
                m_ignoreUpperCase = false;
                pos = -1;
          }
          else
                ++pos;
          link_type = 1;
    }
    if(pos!=-1)
    {
        // A URL inside e.g. quotes (like "http://www.koffice.org" with the quotes) shouldn't include the quote in the URL.
	while ( !word.at(word.length()-1).isLetter() &&  !word.at(word.length()-1).isDigit() && word.at(word.length()-1)!='/')
        {
                word.truncate(word.length()-1);
                --index;
        }
        word.remove(0,pos);
        unsigned int const length = word.length();
        int const start = index - length;
        KoTextCursor cursor( parag->document() );
        KoTextDocument * textdoc = parag->textDocument();
        cursor.setParag( parag );
        cursor.setIndex( start );
        textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
        cursor.setIndex( start + length );
        textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );
        TQString newWord = word;
        if(link_type==1)
            newWord = TQString("mailto:") + word;
        else if(link_type==2)
            newWord = TQString("http://") + word;
        else if(link_type==3)
            newWord = TQString("ftp://") + word;

        KoVariable* var = new KoLinkVariable( textdoc, word, newWord, m_varFormatCollection->format( "STRING" ), m_varCollection );
        CustomItemsMap customItemsMap;
        customItemsMap.insert( 0, var );
        KoTextFormat * lastFormat = parag->at( start )->format();
        int origCursorIndex = textEditCursor->index();
        txtObj->insert( textEditCursor, lastFormat, KoTextObject::customItemChar(), i18n("Insert Variable"),
                        KoTextDocument::HighlightSelection, KoTextObject::DefaultInsertFlags, customItemsMap );
        var->recalc();
        parag->invalidate(0);
        parag->setChanged( true );

        // adjust index
        index -= length-1; // we removed length chars and inserted one instead

        txtObj->emitHideCursor();
        textEditCursor->setIndex( origCursorIndex - (length-1) );
        txtObj->emitShowCursor();

        // ###### TODO: Move to a common method, this code is duplicated...
        if ( m_completion && m_addCompletionWord && m_listCompletion->items().count() < m_nbMaxCompletionWord )
        {
            if (word.length()>= m_minCompletionWordLength  && !word.isEmpty() && m_listCompletion->makeCompletion(word).isEmpty())
            {
                kdDebug() << "Adding:" << word << endl;
                m_listCompletion->addItem( word );
                if ( word.length() > m_countMaxWords )
                    m_countMaxWords = word.length();
            }
        }
    }
}

void KoAutoFormat::doAutoIncludeUpperUpper(KoTextCursor* /*textEditCursor*/, KoTextParag *parag, KoTextObject* /*txtObj*/ )
{
    KoTextString *s = parag->string();

    if( s->length() < 2 )
        return;

    for (int i=0; i<=(s->length() - 1);i++)
    {
        TQString word;
        for ( int j = i ; j < s->length() - 1; j++ )
        {
            TQChar ch = s->at( j ).c;
            if ( ch.isSpace() )
                break;
            word.append( ch );
        }
        if( word.length() > 2 && word.left(2)==word.left(2).upper() && word.at(3)!=word.at(3).upper() )
        {
            if ( m_twoUpperLetterException.findIndex(word )==-1)
                m_twoUpperLetterException.append( word);
        }
        i+=word.length();
    }

}


void KoAutoFormat::doAutoIncludeAbbreviation(KoTextCursor* /*textEditCursor*/, KoTextParag *parag, KoTextObject* /*txtObj*/ )
{
    KoTextString *s = parag->string();
    if( s->length() < 2 )
        return;
    for (int i=0; i<=(s->length() - 1);i++)
    {
        TQString wordAfter;
        TQString word;

        for ( int j = i ; j < s->length() - 1; j++ )
        {
            TQChar ch = s->at( j ).c;
            if ( ch.isSpace() )
                break;
            word.append( ch );
        }
        if ( isMark( word.at(word.length()-1)) )
        {
            for ( int j = i+word.length()+1 ; j < s->length() - 1; j++ )
            {
                TQChar ch = s->at( j ).c;
                if ( ch.isSpace() )
                    break;
                wordAfter.append( ch );
            }
            if( word.length()>1 && !wordAfter.isEmpty() && wordAfter.at(0)==wordAfter.at(0).lower())
            {
                if ( m_upperCaseExceptions.findIndex(word )==-1)
                    m_upperCaseExceptions.append( word );
            }
        }
        i+=word.length();
        if( !wordAfter.isEmpty())
        {
            i+=wordAfter.length()+1;
        }
    }

}


KCommand * KoAutoFormat::doAutoChangeFormat( KoTextCursor *textEditCursor, KoTextParag *parag,int index, const TQString & word, KoTextObject *txtObj )
{
    bool underline = (word.at(0)=='_' && word.at(word.length()-1)=='_');
    bool bold = (word.at(0)=='*' && word.at(word.length()-1)=='*');
    if( bold || underline)
    {
        TQString replacement=word.mid(1,word.length()-2);
        int start = index - word.length();
        KoTextDocument * textdoc = parag->textDocument();
        KMacroCommand *macro=new KMacroCommand(i18n("Autocorrection: Change Format"));
        KoTextCursor cursor( parag->document() );

        cursor.setParag( parag );
        cursor.setIndex( start );
        textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
        cursor.setIndex( start + word.length() );
        textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );
        macro->addCommand(txtObj->replaceSelectionCommand( textEditCursor, replacement,
                                                           i18n("Autocorrect Word"),
                                                           KoTextDocument::HighlightSelection));

        KoTextFormat * lastFormat = parag->at( start )->format();
        KoTextFormat * newFormat = new KoTextFormat(*lastFormat);
        cursor.setIndex( start );
        textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
        cursor.setIndex( start + word.length()-2 );
        textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );

        if( bold)
        {
            newFormat->setBold(true);
            macro->addCommand(txtObj->setFormatCommand( textEditCursor, 0L, newFormat, KoTextFormat::Bold , false,KoTextDocument::HighlightSelection  ));
        }
        else if( underline )
        {
            newFormat->setUnderline(true);
            macro->addCommand(txtObj->setFormatCommand( textEditCursor, 0L, newFormat, KoTextFormat::Underline , false,KoTextDocument::HighlightSelection  ));
        }
        txtObj->emitHideCursor();
        textEditCursor->gotoRight();
        txtObj->emitShowCursor();
        return macro;
    }
    return 0L;
}

KCommand *KoAutoFormat::doUseBulletStyle(KoTextCursor * /*textEditCursor*/, KoTextParag *parag, KoTextObject *txtObj, int& index )
{
    KoTextDocument * textdoc = parag->textDocument();
    KoTextCursor cursor( parag->document() );
    KoTextString *s = parag->string();
    TQChar ch = s->at( 0 ).c;

    if( m_useBulletStyle && (ch =='*' || ch == '-' || ch =='+') && (s->at(1).c).isSpace())
    {
        if ( parag->counter() && parag->counter()->numbering() == KoParagCounter::NUM_FOOTNOTE )
            return 0L;
        KMacroCommand *macroCmd = new KMacroCommand( i18n("Autocorrect (use bullet style)"));
        cursor.setParag( parag );
        cursor.setIndex( 0 );
        textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
        cursor.setParag( parag );
        cursor.setIndex( 2 );
        textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );
        KCommand *cmd=txtObj->removeSelectedTextCommand( &cursor, KoTextDocument::HighlightSelection  );
        // Adjust index
        index -= 2;
        if(cmd)
            macroCmd->addCommand(cmd);

        cursor.setParag( parag );
        cursor.setIndex( 0 );
        textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );

        cursor.setIndex( 2 );
        textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );


        KoParagCounter c;
        if( m_bulletStyle.isNull() && (ch == '*' || ch == '+' || ch == '-'))
        {
            if ( ch =='*')
            {
                c.setNumbering( KoParagCounter::NUM_LIST );
                c.setStyle( KoParagCounter::STYLE_DISCBULLET );
            }
            else if ( ch =='+' || ch=='-')
            {
                c.setNumbering( KoParagCounter::NUM_LIST );
                c.setStyle( KoParagCounter::STYLE_CUSTOMBULLET );
                if ( ch =='-' )
                    c.setCustomBulletCharacter( '-' );
                else if ( ch=='+')
                    c.setCustomBulletCharacter( '+' );
            }
        }
        else
        {
            c.setNumbering( KoParagCounter::NUM_LIST );
            c.setStyle( KoParagCounter::STYLE_CUSTOMBULLET );
            c.setCustomBulletCharacter( m_bulletStyle );
        }
        c.setSuffix(TQString());
        cmd=txtObj->setCounterCommand( &cursor, c ,KoTextDocument::HighlightSelection );
        if( cmd)
            macroCmd->addCommand(cmd);
        if (parag->next() )
                cursor.setParag( parag->next() );
        else
              return 0L;

        cursor.setIndex( 0 );
        textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
        cursor.setIndex( 0 );
        textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );
        cmd=txtObj->setCounterCommand( &cursor, c ,KoTextDocument::HighlightSelection );
        if(cmd)
            macroCmd->addCommand(cmd);
        return macroCmd;
    }
    return 0L;

}

KCommand *KoAutoFormat::doUseNumberStyle(KoTextCursor * /*textEditCursor*/, KoTextParag *parag, KoTextObject *txtObj, int& index )
{
    if ( parag->counter() && parag->counter()->numbering() == KoParagCounter::NUM_FOOTNOTE )
        return 0L;
    KoTextDocument * textdoc = parag->textDocument();
    KoTextCursor cursor( parag->document() );
    KoTextString *s = parag->string();
    TQString word;
    for ( int i = 0 ; i < s->length() - 1; i++ )
    {
        TQChar ch = s->at( i ).c;
        if ( ch.isSpace() )
            break;
        word.append( ch );
    }
    TQChar punct=word[word.length()-1];
    if( punct.isPunct() )
    {
        TQString number=word.mid(0,word.length()-1);
        bool ok;
        uint val=number.toUInt(&ok);
        if( ok )
        {
            KMacroCommand *macroCmd = new KMacroCommand( i18n("Autocorrect (use number style)"));
            cursor.setParag( parag );
            cursor.setIndex( 0 );
            textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
            cursor.setParag( parag );
            cursor.setIndex( word.length()+1 );
            textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );
            KCommand *cmd=txtObj->removeSelectedTextCommand( &cursor, KoTextDocument::HighlightSelection  );
            // Adjust index
            index -= word.length()+1;
            if(cmd)
                macroCmd->addCommand(cmd);

            // Apply counter to this paragraph
            cursor.setParag( parag );
            cursor.setIndex( 0 );
            textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );

            cursor.setIndex( 2 );
            textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );

            KoParagCounter c;
            c.setNumbering( KoParagCounter::NUM_LIST );
            c.setStyle( KoParagCounter::STYLE_NUM );
            c.setSuffix(TQString( punct ));
            c.setStartNumber( (int)val);

            // Look at which number this parag will have without a restart counter flag,
            // to see if we need it. Thanks to Shaheed for number() taking a parag as param,
            // so that it works even if the parag doesn't have this counter yet!
            if ( c.number( parag ) != (int)val )
                c.setRestartCounter( true );

            cmd=txtObj->setCounterCommand( &cursor, c, KoTextDocument::HighlightSelection );
            if( cmd)
                macroCmd->addCommand(cmd);
            // Apply counter to next paragraph too
            // but without restart
            c.setRestartCounter( false );
            cursor.setParag( parag->next() );
            cursor.setIndex( 0 );
            textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
            cursor.setIndex( 0 );
            textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );
            cmd=txtObj->setCounterCommand( &cursor, c, KoTextDocument::HighlightSelection );
            if(cmd)
                macroCmd->addCommand(cmd);
            return macroCmd;
        }
    }
    return 0L;
}


KCommand * KoAutoFormat::doRemoveSpaceBeginEndLine( KoTextCursor *textEditCursor, KoTextParag *parag, KoTextObject *txtObj, int &index )
{
    KoTextString *s = parag->string();
    KoTextDocument * textdoc = parag->textDocument();
    KoTextCursor cursor( parag->document() );

    KMacroCommand *macroCmd = 0L;
    // Cut away spaces at end of paragraph
    for ( int i = parag->lastCharPos(); i >= 0; --i )
    {
        TQChar ch = s->at( i ).c;
        if ( ch != ' ' )   // was: !ch.isSpace(), but this includes tabs, and this option is only about spaces
        {
            if( i == parag->lastCharPos() )
                break;
            cursor.setParag( parag );
            cursor.setIndex( i+1 );
            textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
            cursor.setParag( parag );
            cursor.setIndex( parag->lastCharPos()+1 );
            textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );
            KCommand *cmd=txtObj->replaceSelectionCommand( &cursor, "", TQString(), KoTextDocument::HighlightSelection );

            if(cmd)
            {
                if ( index > i )
                    index = i;
                if ( !macroCmd )
                    macroCmd = new KMacroCommand( i18n("Autocorrect (remove start and end line space)"));
                macroCmd->addCommand(cmd);
            }
            break;
        }
    }

    // Cut away spaces at start of parag.

    for ( int i = 0 ; i <= parag->lastCharPos() ; i++ )
    {
        TQChar ch = s->at( i ).c;
        if ( ch != ' ' )   // was: !ch.isSpace(), but this includes tabs, and this option is only about spaces
        {
            if( i == 0 )
                break;

            cursor.setParag( parag );
            cursor.setIndex( 0 );
            textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
            cursor.setParag( parag );
            cursor.setIndex( i );
            textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );
            KCommand *cmd=txtObj->replaceSelectionCommand( &cursor, "", TQString(), KoTextDocument::HighlightSelection );

            if(cmd)
            {
                index -= i; // adjust index
                if ( !macroCmd )
                    macroCmd = new KMacroCommand( i18n("Autocorrect (remove start and end line space)"));
                macroCmd->addCommand(cmd);
            }
            break;
        }
    }

    if( macroCmd )
    {
        txtObj->emitHideCursor();
        textEditCursor->setParag( parag->next() );
        //textEditCursor->cursorgotoRight();
        txtObj->emitShowCursor();
    }
    return macroCmd;
}

KCommand *KoAutoFormat::doCapitalizeNameOfDays( KoTextCursor* textEditCursor, KoTextParag *parag, int index, const TQString & word , KoTextObject *txtObj )
{
    //m_cacheNameOfDays
    //todo
    int pos = m_cacheNameOfDays.findIndex( word.lower() );
    if ( pos == -1 )
        return 0L;
    KoTextDocument * textdoc = parag->textDocument();
    TQString replaceStr= m_cacheNameOfDays[pos];
    int start = index - replaceStr.length();
    int length = replaceStr.length();
    if( word.at(0).isLetter() && word.at(0)==word.at(0).lower() )
    {
        KoTextCursor cursor( parag->document() );
        cursor.setParag( parag );
        cursor.setIndex( start );
        textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
        cursor.setIndex( start + length );
        TQString replacement = replaceStr.at(0).upper() + replaceStr.right( length-1 );
        textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );
        TQString cmdName=i18n("Capitalize Name of Days");
        KCommand *cmd =txtObj->replaceSelectionCommand( textEditCursor, replacement,
                                                        cmdName,
                                                        KoTextDocument::HighlightSelection );
        txtObj->emitHideCursor();
        textEditCursor->gotoRight();
        txtObj->emitShowCursor();
        return cmd;
    }
    return 0L;
}

KCommand *KoAutoFormat::doAutoSuperScript( KoTextCursor* textEditCursor, KoTextParag *parag, int index, const TQString & word , KoTextObject *txtObj )
{
    KoAutoFormatEntryMap::Iterator it = m_superScriptEntries.begin();
    bool found = false;
    TQString replace;
    for ( ; it != m_superScriptEntries.end() ; ++it )
    {
        if( it.key()==word)
        {
            replace = it.data().replace();
            found = true;
            break;
        }
        else if ( it.key()=="othernb")
        {
            TQString tmp = it.data().replace();
            int pos = word.find( tmp );
            if( pos != -1)
            {
                if( pos + tmp.length() == word.length())
                {
                    bool ok;
                    word.left( pos ).toInt( &ok);
                    if( ok )
                    {
                        replace = tmp;
                        found = true;
                        break;
                    }
                }
            }
        }
    }
    if (found )
    {
        KoTextDocument * textdoc = parag->textDocument();

        int start = index - replace.length();
        KoTextFormat * lastFormat = parag->at( start )->format();
        KoTextFormat * newFormat = new KoTextFormat(*lastFormat);
        KoTextCursor cursor( parag->document() );

        cursor.setParag( parag );
        cursor.setIndex( start );
        textdoc->setSelectionStart( KoTextDocument::HighlightSelection, &cursor );
        cursor.setIndex( start + word.length() -1 );
        textdoc->setSelectionEnd( KoTextDocument::HighlightSelection, &cursor );
        newFormat->setVAlign(KoTextFormat::AlignSuperScript);
        KCommand *cmd =txtObj->setFormatCommand( textEditCursor, 0L, newFormat, KoTextFormat::VAlign , false,KoTextDocument::HighlightSelection  );
        textdoc->removeSelection( KoTextDocument::HighlightSelection );

        return cmd;
    }
    return 0L;
}

bool KoAutoFormat::doIgnoreDoubleSpace( KoTextParag *parag, int index, TQChar ch )
{
    if( m_ignoreDoubleSpace && ch==' ' && index >=  0 && !parag->hasAnySelection() )
    {
        KoTextString *s = parag->string();
        TQChar ch = s->at( index ).c;
        if ( ch==' ' )
          return true;
    }
    return false;
}

void KoAutoFormat::configTypographicSimpleQuotes( TypographicQuotes _tq )
{
    m_typographicSimpleQuotes = _tq;
}

void KoAutoFormat::configTypographicDoubleQuotes( TypographicQuotes _tq )
{
    m_typographicDoubleQuotes = _tq;
}

void KoAutoFormat::configUpperCase( bool _uc )
{
    m_convertUpperCase = _uc;
}

void KoAutoFormat::configUpperUpper( bool _uu )
{
    m_convertUpperUpper = _uu;
}

void KoAutoFormat::configAdvancedAutocorrect( bool _aa )
{
    m_advancedAutoCorrect = _aa;
}

void KoAutoFormat::configAutoDetectUrl(bool _au)
{
    m_autoDetectUrl=_au;
}

void KoAutoFormat::configIgnoreDoubleSpace( bool _ids)
{
    m_ignoreDoubleSpace=_ids;
}

void KoAutoFormat::configRemoveSpaceBeginEndLine( bool _space)
{
    m_removeSpaceBeginEndLine=_space;
}

void KoAutoFormat::configUseBulletStyle( bool _ubs)
{
    m_useBulletStyle=_ubs;
}

void KoAutoFormat::configBulletStyle( TQChar b )
{
    m_bulletStyle = b;
}

void KoAutoFormat::configAutoChangeFormat( bool b)
{
    m_autoChangeFormat = b;
}


void KoAutoFormat::configAutoReplaceNumber( bool b )
{
    m_autoReplaceNumber = b;
}

void KoAutoFormat::configAutoNumberStyle( bool b )
{
    m_useAutoNumberStyle = b;
}

void KoAutoFormat::configCompletion( bool b )
{
    m_completion = b;
}

void KoAutoFormat::configToolTipCompletion( bool b )
{
    m_toolTipCompletion = b;
    if (!b && m_completionBox)
    {
        delete m_completionBox;
        m_completionBox = 0;
    }
}

void KoAutoFormat::configKeyCompletionAction( KeyCompletionAction action )
{
    m_keyCompletionAction = action;
}

void KoAutoFormat::configAppendSpace( bool b)
{
    m_completionAppendSpace= b;
}

void KoAutoFormat::configMinWordLength( uint val )
{
   m_minCompletionWordLength = val;
}

void KoAutoFormat::configNbMaxCompletionWord( uint val )
{
    m_nbMaxCompletionWord = val;
}


void KoAutoFormat::configAddCompletionWord( bool b )
{
    m_addCompletionWord= b;
}

bool KoAutoFormat::isUpper( const TQChar &c )
{
    return c.lower() != c;
}

bool KoAutoFormat::isLower( const TQChar &c )
{
    // Note that this is not the same as !isUpper !
    // For instance '1' is not lower nor upper,
    return c.upper() != c;
}

bool KoAutoFormat::isMark( const TQChar &c )
{
    return ( c == TQChar( '.' ) ||
	     c == TQChar( '?' ) ||
	     c == TQChar( '!' ) );
}

bool KoAutoFormat::isSeparator( const TQChar &c )
{
    return ( !c.isLetter() && !c.isNumber() && !c.isDigit() );
}

void KoAutoFormat::buildMaxLen()
{
    m_maxFindLength = 0;
    TQDictIterator<KoAutoFormatEntry> it( m_entries );
    for( ; it.current(); ++it )
    {
	m_maxFindLength = TQMAX( m_maxFindLength, it.currentKey().length() );
    }
    TQDictIterator<KoAutoFormatEntry> it2( m_allLanguages );
    for( ; it2.current(); ++it2 )
    {
	m_maxFindLength = TQMAX( m_maxFindLength, it2.currentKey().length() );
    }
}

TQStringList KoAutoFormat::listCompletion() const
{
   return m_listCompletion->items();
}


void KoAutoFormat::configIncludeTwoUpperUpperLetterException( bool b)
{
    m_includeTwoUpperLetterException = b;
}

void KoAutoFormat::configIncludeAbbreviation( bool b )
{
    m_includeAbbreviation = b;
}

void KoAutoFormat::configAutoSuperScript( bool b )
{
    m_bAutoSuperScript = b;
}

void KoAutoFormat::configCorrectionWithFormat( bool b)
{
    m_bAutoCorrectionWithFormat = b;
}

void KoAutoFormat::configCapitalizeNameOfDays( bool b)
{
    m_bCapitalizeNameOfDays = b;
}

void KoAutoFormat::configAutoFormatLanguage( const TQString &_lang)
{
    m_autoFormatLanguage=_lang;
}

KCommand *KoAutoFormat::applyAutoFormat( KoTextObject * obj )
{
  KoTextParag * parag = obj->textDocument()->firstParag();
  KoTextCursor *cursor = new KoTextCursor( obj->textDocument() );
  KMacroCommand *macro = 0L;
  while ( parag )
  {
    cursor->setIndex(0);
    for (int i=0;i<parag->length();i++)
    {
      cursor->gotoRight();
      //kdDebug() << "ch:" << parag->string()->at(i).c << endl;
      if (i == parag->length()-1)
	doAutoFormat(cursor,parag,i,'\n',obj);
      else
	doAutoFormat(cursor,parag,i, parag->string()->at(i).c,obj);
    }
    parag = parag->next();

  }
  delete cursor;
  return macro;
}

void KoAutoFormat::changeTextFormat(KoSearchContext *formatOptions, KoTextFormat * format, int & flags )
{
    if (formatOptions )
    {
        if (formatOptions->m_optionsMask & KoSearchContext::Bold)
        {
            format->setBold( formatOptions->m_options & KoSearchContext::Bold);
            flags |=KoTextFormat::Bold;
        }
        if ( formatOptions->m_optionsMask & KoSearchContext::Size)
        {
            format->setPointSize( formatOptions->m_size );
            flags |=KoTextFormat::Size;
        }
        if ( formatOptions->m_optionsMask & KoSearchContext::Family)
        {
            format->setFamily( formatOptions->m_family );
            flags |=KoTextFormat::Family;
        }
        if ( formatOptions->m_optionsMask & KoSearchContext::Color)
        {
            format->setColor(formatOptions->m_color);
            flags |=KoTextFormat::Color;
        }
        if ( formatOptions->m_optionsMask & KoSearchContext::BgColor)
        {
            format->setTextBackgroundColor(formatOptions->m_backGroundColor);
            flags |=KoTextFormat::TextBackgroundColor;
        }

        if ( formatOptions->m_optionsMask & KoSearchContext::Italic)
        {
            format->setItalic( formatOptions->m_options & KoSearchContext::Italic);
            flags |=KoTextFormat::Italic;
        }
        if ( formatOptions->m_optionsMask & KoSearchContext::WordByWord)
        {
            format->setWordByWord( formatOptions->m_options & KoSearchContext::WordByWord );
            flags |=KoTextFormat::WordByWord;
        }
        if ( formatOptions->m_optionsMask & KoSearchContext::Shadow)
        {
            if ( formatOptions->m_options & KoSearchContext::Shadow )
                format->setShadow( 1, 1, TQt::gray );
            else
                format->setShadow( 0, 0, TQColor() );
            flags |=KoTextFormat::ShadowText;
        }

        if ( formatOptions->m_optionsMask & KoSearchContext::Underline)
        {
            format->setUnderlineType(formatOptions->m_underline);
            flags |=KoTextFormat::ExtendUnderLine;
        }
        if ( formatOptions->m_optionsMask & KoSearchContext::StrikeOut)
        {
            format->setStrikeOutType(formatOptions->m_strikeOut);
            flags |= KoTextFormat::StrikeOut;
        }
        if ( formatOptions->m_optionsMask & KoSearchContext::VertAlign)
        {
            format->setVAlign(formatOptions->m_vertAlign);
            flags |=KoTextFormat::VAlign;
        }
        if ( formatOptions->m_optionsMask & KoSearchContext::Attribute)
        {
            format->setAttributeFont(formatOptions->m_attribute);
            flags |= KoTextFormat::Attribute;
        }
        if (formatOptions->m_optionsMask & KoSearchContext::Language)
        {
            flags |= KoTextFormat::Language;
            format->setLanguage( formatOptions->m_language );
        }
    }
}

#include "KoAutoFormat.moc"
