/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_editeng.hxx"


#include <com/sun/star/io/XStream.hpp>
#include <com/sun/star/lang/Locale.hpp>
#include <tools/urlobj.hxx>
#include <tools/table.hxx>
#include <i18npool/mslangid.hxx>
#include <vcl/svapp.hxx>
#include <sot/storinfo.hxx>
// fuer die Sort-String-Arrays aus dem SVMEM.HXX
#define _SVSTDARR_STRINGSISORTDTOR
#define _SVSTDARR_STRINGSDTOR
#include <svl/svstdarr.hxx>
#include <svl/fstathelper.hxx>
#include <svtools/helpopt.hxx>
#include <svl/urihelper.hxx>
#include <unotools/charclass.hxx>
#include <com/sun/star/i18n/UnicodeType.hdl>
#include <unotools/collatorwrapper.hxx>
#include <com/sun/star/i18n/CollatorOptions.hpp>
#include <unotools/localedatawrapper.hxx>
#include <unotools/transliterationwrapper.hxx>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <comphelper/processfactory.hxx>
#include <com/sun/star/io/XActiveDataSource.hpp>
#include <editeng/editids.hrc>
#include <sot/storage.hxx>
#include <comphelper/storagehelper.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/wghtitem.hxx>
#include <editeng/escpitem.hxx>
#include <editeng/svxacorr.hxx>
#include <editeng/unolingu.hxx>
#include "vcl/window.hxx"
#include <helpid.hrc>
#include <comphelper/processfactory.hxx>
#include <com/sun/star/xml/sax/InputSource.hpp>
#include <com/sun/star/xml/sax/XParser.hpp>
#include <unotools/streamwrap.hxx>
#include <SvXMLAutoCorrectImport.hxx>
#include <SvXMLAutoCorrectExport.hxx>
#include <ucbhelper/content.hxx>
#include <com/sun/star/ucb/XCommandEnvironment.hpp>
#include <com/sun/star/ucb/TransferInfo.hpp>
#include <com/sun/star/ucb/NameClash.hpp>
#include <xmloff/xmltoken.hxx>
#include <vcl/help.hxx>

#define CHAR_HARDBLANK		((sal_Unicode)0x00A0)

using namespace ::com::sun::star::ucb;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star;
using namespace ::xmloff::token;
using namespace ::rtl;
using namespace ::utl;

const int C_NONE 				= 0x00;
const int C_FULL_STOP 			= 0x01;
const int C_EXCLAMATION_MARK	= 0x02;
const int C_QUESTION_MARK		= 0x04;

static const sal_Char pImplWrdStt_ExcptLstStr[]    = "WordExceptList";
static const sal_Char pImplCplStt_ExcptLstStr[]    = "SentenceExceptList";
static const sal_Char pImplAutocorr_ListStr[]      = "DocumentList";
static const sal_Char pXMLImplWrdStt_ExcptLstStr[] = "WordExceptList.xml";
static const sal_Char pXMLImplCplStt_ExcptLstStr[] = "SentenceExceptList.xml";
static const sal_Char pXMLImplAutocorr_ListStr[]   = "DocumentList.xml";

static const sal_Char
    /* auch bei diesen Anfaengen - Klammern auf und alle Arten von Anf.Zei. */
    sImplSttSkipChars[]	= "\"\'([{\x83\x84\x89\x91\x92\x93\x94",
    /* auch bei diesen Ende - Klammern auf und alle Arten von Anf.Zei. */
    sImplEndSkipChars[]	= "\"\')]}\x83\x84\x89\x91\x92\x93\x94";

// diese Zeichen sind in Worten erlaubt: (fuer FnCptlSttSntnc)
static const sal_Char sImplWordChars[] = "-'";

void EncryptBlockName_Imp( String& rName );
void DecryptBlockName_Imp( String& rName );


// FileVersions Nummern fuer die Ersetzungs-/Ausnahmelisten getrennt
#define WORDLIST_VERSION_358	1
#define EXEPTLIST_VERSION_358	0


_SV_IMPL_SORTAR_ALG( SvxAutocorrWordList, SvxAutocorrWordPtr )
TYPEINIT0(SvxAutoCorrect)

typedef SvxAutoCorrectLanguageLists* SvxAutoCorrectLanguageListsPtr;
DECLARE_TABLE( SvxAutoCorrLanguageTable_Impl,  SvxAutoCorrectLanguageListsPtr)

DECLARE_TABLE( SvxAutoCorrLastFileAskTable_Impl, long )


inline int IsWordDelim( const sal_Unicode c )
{
    return ' ' == c || '\t' == c || 0x0a == c ||
            0xA0 == c || 0x2011 == c || 0x1 == c;
}

inline int IsLowerLetter( sal_Int32 nCharType )
{
    return CharClass::isLetterType( nCharType ) &&
            0 == ( ::com::sun::star::i18n::KCharacterType::UPPER & nCharType);
}
inline int IsUpperLetter( sal_Int32 nCharType )
{
    return CharClass::isLetterType( nCharType ) &&
            0 == ( ::com::sun::star::i18n::KCharacterType::LOWER & nCharType);
}

BOOL lcl_IsSymbolChar( CharClass& rCC, const String& rTxt,
                           xub_StrLen nStt, xub_StrLen nEnd )
{
    for( ; nStt < nEnd; ++nStt )
    {
#if OSL_DEBUG_LEVEL > 1
        sal_Int32 nCharType;
        sal_Int32 nChType;
        nCharType = rCC.getCharacterType( rTxt, nStt );
        nChType = rCC.getType( rTxt, nStt );
#endif
        if( ::com::sun::star::i18n::UnicodeType::PRIVATE_USE ==
                rCC.getType( rTxt, nStt ))
            return TRUE;
    }
    return FALSE;
}


static BOOL lcl_IsInAsciiArr( const sal_Char* pArr, const sal_Unicode c )
{
    BOOL bRet = FALSE;
    for( ; *pArr; ++pArr )
        if( *pArr == c )
        {
            bRet = TRUE;
            break;
        }
    return bRet;
}

SvxAutoCorrDoc::~SvxAutoCorrDoc()
{
}


    // wird nach dem austauschen der Zeichen von den Funktionen
    //	- FnCptlSttWrd
    // 	- FnCptlSttSntnc
    // gerufen. Dann koennen die Worte ggfs. in die Ausnahmelisten
    // aufgenommen werden.
void SvxAutoCorrDoc::SaveCpltSttWord( ULONG, xub_StrLen, const String&,
                                        sal_Unicode )
{
}

LanguageType SvxAutoCorrDoc::GetLanguage( xub_StrLen , BOOL ) const
{
    return LANGUAGE_SYSTEM;
}

static ::com::sun::star::uno::Reference<
            ::com::sun::star::lang::XMultiServiceFactory >& GetProcessFact()
{
    static ::com::sun::star::uno::Reference<
            ::com::sun::star::lang::XMultiServiceFactory > xMSF =
                                    ::comphelper::getProcessServiceFactory();
    return xMSF;
}

static USHORT GetAppLang()
{
    return Application::GetSettings().GetLanguage();
}
static LocaleDataWrapper& GetLocaleDataWrapper( USHORT nLang )
{
    static LocaleDataWrapper aLclDtWrp( GetProcessFact(),
                                        SvxCreateLocale( GetAppLang() ) );
    ::com::sun::star::lang::Locale aLcl( SvxCreateLocale( nLang ));
    const ::com::sun::star::lang::Locale& rLcl = aLclDtWrp.getLoadedLocale();
    if( aLcl.Language != rLcl.Language ||
        aLcl.Country != rLcl.Country ||
        aLcl.Variant != rLcl.Variant )
        aLclDtWrp.setLocale( aLcl );
    return aLclDtWrp;
}
static TransliterationWrapper& GetIgnoreTranslWrapper()
{
    static int bIsInit = 0;
    static TransliterationWrapper aWrp( GetProcessFact(),
                ::com::sun::star::i18n::TransliterationModules_IGNORE_KANA |
                ::com::sun::star::i18n::TransliterationModules_IGNORE_WIDTH );
    if( !bIsInit )
    {
        aWrp.loadModuleIfNeeded( GetAppLang() );
        bIsInit = 1;
    }
    return aWrp;
}
static CollatorWrapper& GetCollatorWrapper()
{
    static int bIsInit = 0;
    static CollatorWrapper aCollWrp( GetProcessFact() );
    if( !bIsInit )
    {
        aCollWrp.loadDefaultCollator( SvxCreateLocale( GetAppLang() ), 0 );
        bIsInit = 1;
    }
    return aCollWrp;
}


void SvxAutocorrWordList::DeleteAndDestroy( USHORT nP, USHORT nL )
{
    if( nL )
    {
        DBG_ASSERT( nP < nA && nP + nL <= nA, "ERR_VAR_DEL" );
        for( USHORT n=nP; n < nP + nL; n++ )
            delete *((SvxAutocorrWordPtr*)pData+n);
        SvPtrarr::Remove( nP, nL );
    }
}


BOOL SvxAutocorrWordList::Seek_Entry( const SvxAutocorrWordPtr aE, USHORT* pP ) const
{
    register USHORT nO  = SvxAutocorrWordList_SAR::Count(),
            nM,
            nU = 0;
    if( nO > 0 )
    {
        CollatorWrapper& rCmp = ::GetCollatorWrapper();
        nO--;
        while( nU <= nO )
        {
            nM = nU + ( nO - nU ) / 2;
            long nCmp = rCmp.compareString( aE->GetShort(),
                        (*((SvxAutocorrWordPtr*)pData + nM))->GetShort() );
            if( 0 == nCmp )
            {
                if( pP ) *pP = nM;
                return TRUE;
            }
            else if( 0 < nCmp )
                nU = nM + 1;
            else if( nM == 0 )
            {
                if( pP ) *pP = nU;
                return FALSE;
            }
            else
                nO = nM - 1;
        }
    }
    if( pP ) *pP = nU;
    return FALSE;
}

/* -----------------18.11.98 15:28-------------------
 *
 * --------------------------------------------------*/
void lcl_ClearTable(SvxAutoCorrLanguageTable_Impl& rLangTable)
{
    SvxAutoCorrectLanguageListsPtr pLists = rLangTable.Last();
    while(pLists)
    {
        delete pLists;
        pLists = rLangTable.Prev();
    }
    rLangTable.Clear();
}

/* -----------------03.11.06 10:15-------------------
 *
 * --------------------------------------------------*/

sal_Bool SvxAutoCorrect::IsAutoCorrectChar( sal_Unicode cChar )
{
    return  cChar == '\0' || cChar == '\t' || cChar == 0x0a ||
            cChar == ' '  || cChar == '\'' || cChar == '\"' ||
            cChar == '*'  || cChar == '_'  ||
            cChar == '.'  || cChar == ','  || cChar == ';' ||
            cChar == ':'  || cChar == '?' || cChar == '!' || cChar == '/';
}

sal_Bool SvxAutoCorrect::NeedsHardspaceAutocorr( sal_Unicode cChar )
{
    return cChar == ';' || cChar == ':'  || cChar == '?' || cChar == '!' || 
        cChar == '/' /*case for the urls exception*/;
}

/* -----------------19.11.98 10:15-------------------
 *
 * --------------------------------------------------*/
long SvxAutoCorrect::GetDefaultFlags()
{
    long nRet = Autocorrect
                    | CptlSttSntnc
                    | CptlSttWrd
                    | ChgOrdinalNumber
                    | ChgToEnEmDash
                    | AddNonBrkSpace
                    | ChgWeightUnderl
                    | SetINetAttr
                    | ChgQuotes
                    | SaveWordCplSttLst
                    | SaveWordWrdSttLst
                    | CorrectCapsLock;
    LanguageType eLang = GetAppLang();
    switch( eLang )
    {
    case LANGUAGE_ENGLISH:
    case LANGUAGE_ENGLISH_US:
    case LANGUAGE_ENGLISH_UK:
    case LANGUAGE_ENGLISH_AUS:
    case LANGUAGE_ENGLISH_CAN:
    case LANGUAGE_ENGLISH_NZ:
    case LANGUAGE_ENGLISH_EIRE:
    case LANGUAGE_ENGLISH_SAFRICA:
    case LANGUAGE_ENGLISH_JAMAICA:
    case LANGUAGE_ENGLISH_CARRIBEAN:
        nRet &= ~(ChgQuotes|ChgSglQuotes);
        break;
    }
    return nRet;
}


SvxAutoCorrect::SvxAutoCorrect( const String& rShareAutocorrFile,
                                const String& rUserAutocorrFile )
    : sShareAutoCorrFile( rShareAutocorrFile ),
    sUserAutoCorrFile( rUserAutocorrFile ),
    pLangTable( new SvxAutoCorrLanguageTable_Impl ),
    pLastFileTable( new SvxAutoCorrLastFileAskTable_Impl ),
    pCharClass( 0 ), bRunNext( false ),
    cStartDQuote( 0 ), cEndDQuote( 0 ), cStartSQuote( 0 ), cEndSQuote( 0 )
{
    nFlags = SvxAutoCorrect::GetDefaultFlags();

    cEmDash = ByteString::ConvertToUnicode( '\x97', RTL_TEXTENCODING_MS_1252 );
    cEnDash = ByteString::ConvertToUnicode( '\x96', RTL_TEXTENCODING_MS_1252 );
}

SvxAutoCorrect::SvxAutoCorrect( const SvxAutoCorrect& rCpy )
:	sShareAutoCorrFile( rCpy.sShareAutoCorrFile ),
    sUserAutoCorrFile( rCpy.sUserAutoCorrFile ),

    aSwFlags( rCpy.aSwFlags ),

    pLangTable( new SvxAutoCorrLanguageTable_Impl ),
    pLastFileTable( new SvxAutoCorrLastFileAskTable_Impl ),
    pCharClass( 0 ), bRunNext( false ),

    nFlags( rCpy.nFlags & ~(ChgWordLstLoad|CplSttLstLoad|WrdSttLstLoad)),
    cStartDQuote( rCpy.cStartDQuote ), cEndDQuote( rCpy.cEndDQuote ),
    cStartSQuote( rCpy.cStartSQuote ), cEndSQuote( rCpy.cEndSQuote ),
    cEmDash( rCpy.cEmDash ), cEnDash( rCpy.cEnDash )
{
}


SvxAutoCorrect::~SvxAutoCorrect()
{
    lcl_ClearTable(*pLangTable);
    delete pLangTable;
    delete pLastFileTable;
    delete pCharClass;
}

void SvxAutoCorrect::_GetCharClass( LanguageType eLang )
{
    delete pCharClass;
    pCharClass = new CharClass( SvxCreateLocale( eLang ));
    eCharClassLang = eLang;
}

void SvxAutoCorrect::SetAutoCorrFlag( long nFlag, BOOL bOn )
{
    long nOld = nFlags;
    nFlags = bOn ? nFlags | nFlag
                 : nFlags & ~nFlag;

    if( !bOn )
    {
        if( (nOld & CptlSttSntnc) != (nFlags & CptlSttSntnc) )
            nFlags &= ~CplSttLstLoad;
        if( (nOld & CptlSttWrd) != (nFlags & CptlSttWrd) )
            nFlags &= ~WrdSttLstLoad;
        if( (nOld & Autocorrect) != (nFlags & Autocorrect) )
            nFlags &= ~ChgWordLstLoad;
    }
}


    // Zwei Grossbuchstaben am Wort-Anfang ??
BOOL SvxAutoCorrect::FnCptlSttWrd( SvxAutoCorrDoc& rDoc, const String& rTxt,
                                    xub_StrLen nSttPos, xub_StrLen nEndPos,
                                    LanguageType eLang )
{
    BOOL bRet = FALSE;
    CharClass& rCC = GetCharClass( eLang );

    // loesche alle nicht alpanum. Zeichen am Wortanfang/-ende und
    // teste dann ( erkennt: "(min.", "/min.", usw.)
    for( ; nSttPos < nEndPos; ++nSttPos )
        if( rCC.isLetterNumeric( rTxt, nSttPos ))
            break;
    for( ; nSttPos < nEndPos; --nEndPos )
        if( rCC.isLetterNumeric( rTxt, nEndPos - 1 ))
            break;

    // Zwei Grossbuchstaben am Wort-Anfang ??
    if( nSttPos+2 < nEndPos &&
        IsUpperLetter( rCC.getCharacterType( rTxt, nSttPos )) &&
        IsUpperLetter( rCC.getCharacterType( rTxt, ++nSttPos )) &&
        // ist das 3. Zeichen ein klein geschiebenes Alpha-Zeichen
        IsLowerLetter( rCC.getCharacterType( rTxt, nSttPos +1 )) &&
        // keine Sonder-Attribute ersetzen
        0x1 != rTxt.GetChar( nSttPos ) && 0x2 != rTxt.GetChar( nSttPos ))
    {
        // teste ob das Wort in einer Ausnahmeliste steht
        String sWord( rTxt.Copy( nSttPos - 1, nEndPos - nSttPos + 1 ));
        if( !FindInWrdSttExceptList(eLang, sWord) )
        {
            sal_Unicode cSave = rTxt.GetChar( nSttPos );
            String sChar( cSave );
            rCC.toLower( sChar );
            if( sChar.GetChar(0) != cSave && rDoc.Replace( nSttPos, sChar ))
            {
                if( SaveWordWrdSttLst & nFlags )
                    rDoc.SaveCpltSttWord( CptlSttWrd, nSttPos, sWord, cSave );
                bRet = TRUE;
            }
        }
    }
    return bRet;
}


BOOL SvxAutoCorrect::FnChgOrdinalNumber(
                                SvxAutoCorrDoc& rDoc, const String& rTxt,
                                xub_StrLen nSttPos, xub_StrLen nEndPos,
                                LanguageType eLang )
{
// 1st, 2nd, 3rd, 4 - 0th
// 201th oder 201st
// 12th oder 12nd
    CharClass& rCC = GetCharClass( eLang );
    BOOL bChg = FALSE;

    for( ; nSttPos < nEndPos; ++nSttPos )
        if( !lcl_IsInAsciiArr( sImplSttSkipChars, rTxt.GetChar( nSttPos ) ))
            break;
    for( ; nSttPos < nEndPos; --nEndPos )
        if( !lcl_IsInAsciiArr( sImplEndSkipChars, rTxt.GetChar( nEndPos - 1 ) ))
            break;

    if( 2 < nEndPos - nSttPos &&
        rCC.isDigit( rTxt, nEndPos - 3 ) )
    {
        static sal_Char __READONLY_DATA
            sAll[]		= "th",			/* rest */
            sFirst[]	= "st",      	/* 1 */
            sSecond[]	= "nd",       	/* 2 */
            sThird[]	= "rd";       	/* 3 */
        static const sal_Char* __READONLY_DATA aNumberTab[ 4 ] =
        {
            sAll, sFirst, sSecond, sThird
        };

        sal_Unicode c = rTxt.GetChar( nEndPos - 3 );
        if( ( c -= '0' ) > 3 )
            c = 0;

        bChg = ( ((sal_Unicode)*((aNumberTab[ c ])+0)) ==
                                        rTxt.GetChar( nEndPos - 2 ) &&
                 ((sal_Unicode)*((aNumberTab[ c ])+1)) ==
                                         rTxt.GetChar( nEndPos - 1 )) ||
               ( 3 < nEndPos - nSttPos &&
                ( ((sal_Unicode)*(sAll+0)) == rTxt.GetChar( nEndPos - 2 ) &&
                  ((sal_Unicode)*(sAll+1)) == rTxt.GetChar( nEndPos - 1 )));

        if( bChg )
        {
            // dann pruefe mal, ob alle bis zum Start alle Zahlen sind
            for( xub_StrLen n = nEndPos - 3; nSttPos < n; )
                if( !rCC.isDigit( rTxt, --n ) )
                {
                    bChg = !rCC.isLetter( rTxt, n );
                    break;
                }

            if( bChg )		// dann setze mal das Escapement Attribut
            {
                SvxEscapementItem aSvxEscapementItem( DFLT_ESC_AUTO_SUPER,
                                                    DFLT_ESC_PROP, SID_ATTR_CHAR_ESCAPEMENT );
                rDoc.SetAttr( nEndPos - 2, nEndPos,
                                SID_ATTR_CHAR_ESCAPEMENT,
                                aSvxEscapementItem);
            }
        }

    }
    return bChg;
}


BOOL SvxAutoCorrect::FnChgToEnEmDash(
                                SvxAutoCorrDoc& rDoc, const String& rTxt,
                                xub_StrLen nSttPos, xub_StrLen nEndPos,
                                LanguageType eLang )
{
    BOOL bRet = FALSE;
    CharClass& rCC = GetCharClass( eLang );
    if (eLang == LANGUAGE_SYSTEM)
        eLang = GetAppLang();
    bool bAlwaysUseEmDash = (cEmDash && (eLang == LANGUAGE_RUSSIAN || eLang == LANGUAGE_UKRAINIAN));

    // ersetze " - " oder " --" durch "enDash"
    if( cEnDash && 1 < nSttPos && 1 <= nEndPos - nSttPos )
    {
        sal_Unicode cCh = rTxt.GetChar( nSttPos );
        if( '-' == cCh )
        {
            if( ' ' == rTxt.GetChar( nSttPos-1 ) &&
                '-' == rTxt.GetChar( nSttPos+1 ))
            {
                xub_StrLen n;
                for( n = nSttPos+2; n < nEndPos && lcl_IsInAsciiArr(
                            sImplSttSkipChars,(cCh = rTxt.GetChar( n )));
                        ++n )
                    ;

                // found: " --[<AnySttChars>][A-z0-9]
                if( rCC.isLetterNumeric( cCh ) )
                {
                    for( n = nSttPos-1; n && lcl_IsInAsciiArr(
                            sImplEndSkipChars,(cCh = rTxt.GetChar( --n ))); )
                        ;

                    // found: "[A-z0-9][<AnyEndChars>] --[<AnySttChars>][A-z0-9]
                    if( rCC.isLetterNumeric( cCh ))
                    {
                        rDoc.Delete( nSttPos, nSttPos + 2 );
                        rDoc.Insert( nSttPos, bAlwaysUseEmDash ? cEmDash : cEnDash );
                        bRet = TRUE;
                    }
                }
            }
        }
        else if( 3 < nSttPos &&
                 ' ' == rTxt.GetChar( nSttPos-1 ) &&
                 '-' == rTxt.GetChar( nSttPos-2 ))
        {
            xub_StrLen n, nLen = 1, nTmpPos = nSttPos - 2;
            if( '-' == ( cCh = rTxt.GetChar( nTmpPos-1 )) )
            {
                --nTmpPos;
                ++nLen;
                cCh = rTxt.GetChar( nTmpPos-1 );
            }
            if( ' ' == cCh )
            {
                for( n = nSttPos; n < nEndPos && lcl_IsInAsciiArr(
                            sImplSttSkipChars,(cCh = rTxt.GetChar( n )));
                        ++n )
                    ;

                // found: " - [<AnySttChars>][A-z0-9]
                if( rCC.isLetterNumeric( cCh ) )
                {
                    cCh = ' ';
                    for( n = nTmpPos-1; n && lcl_IsInAsciiArr(
                            sImplEndSkipChars,(cCh = rTxt.GetChar( --n ))); )
                            ;
                    // found: "[A-z0-9][<AnyEndChars>] - [<AnySttChars>][A-z0-9]
                    if( rCC.isLetterNumeric( cCh ))
                    {
                        rDoc.Delete( nTmpPos, nTmpPos + nLen );
                        rDoc.Insert( nTmpPos, bAlwaysUseEmDash ? cEmDash : cEnDash );
                        bRet = TRUE;
                    }
                }
            }
        }
    }

    // Replace [A-z0-9]--[A-z0-9] double dash with "emDash" or "enDash".
    // Finnish and Hungarian use enDash instead of emDash.
    bool bEnDash = (eLang == LANGUAGE_HUNGARIAN || eLang == LANGUAGE_FINNISH);    
    if( ((cEmDash && !bEnDash) || (cEnDash && bEnDash)) && 4 <= nEndPos - nSttPos )
    {
        String sTmp( rTxt.Copy( nSttPos, nEndPos - nSttPos ) );
        xub_StrLen nFndPos = sTmp.SearchAscii( "--" );
        if( STRING_NOTFOUND != nFndPos && nFndPos &&
            nFndPos + 2 < sTmp.Len() &&
            ( rCC.isLetterNumeric( sTmp, nFndPos - 1 ) ||
              lcl_IsInAsciiArr( sImplEndSkipChars, rTxt.GetChar( nFndPos - 1 ) )) &&
            ( rCC.isLetterNumeric( sTmp, nFndPos + 2 ) ||
            lcl_IsInAsciiArr( sImplSttSkipChars, rTxt.GetChar( nFndPos + 2 ) )))
        {
            nSttPos = nSttPos + nFndPos;
            rDoc.Delete( nSttPos, nSttPos + 2 );
            rDoc.Insert( nSttPos, (bEnDash ? cEnDash : cEmDash) );
            bRet = TRUE;
        }
    }
    return bRet;
}

BOOL SvxAutoCorrect::FnAddNonBrkSpace(
                                SvxAutoCorrDoc& rDoc, const String& rTxt,
                                xub_StrLen, xub_StrLen nEndPos,
                                LanguageType eLang )
{
    bool bRet = false;
    
    CharClass& rCC = GetCharClass( eLang );
    const lang::Locale rLocale = rCC.getLocale( );

    if ( rLocale.Language == OUString::createFromAscii( "fr" ) )
    {
        bool bFrCA = rLocale.Country == OUString::createFromAscii( "CA" );
        OUString allChars = OUString::createFromAscii( ":;!?" );
        OUString chars( allChars );
        if ( bFrCA )
            chars = OUString::createFromAscii( ":" );

        sal_Unicode cChar = rTxt.GetChar( nEndPos );
        bool bHasSpace = chars.indexOf( cChar ) != -1;
        bool bIsSpecial = allChars.indexOf( cChar ) != -1;
        if ( bIsSpecial )
        {
            // Get the last word delimiter position
            xub_StrLen nSttWdPos = nEndPos;
            while( nSttWdPos && !IsWordDelim( rTxt.GetChar( --nSttWdPos )))
                ;
            
            // Check the presence of "://" in the word
            xub_StrLen nStrPos = rTxt.Search( String::CreateFromAscii( "://" ), nSttWdPos + 1 );
            if ( STRING_NOTFOUND == nStrPos && nEndPos > 0 )
            {
                // Check the previous char
                sal_Unicode cPrevChar = rTxt.GetChar( nEndPos - 1 );
                if ( ( chars.indexOf( cPrevChar ) == -1 ) && cPrevChar != '\t' )
                { 
                    // Remove any previous normal space
                    xub_StrLen nPos = nEndPos - 1;
                    while ( cPrevChar == ' ' || cPrevChar == CHAR_HARDBLANK )
                    {
                        if ( nPos == 0 ) break;
                        nPos--;
                        cPrevChar = rTxt.GetChar( nPos );
                    }
    
                    nPos++;
                    if ( nEndPos - nPos > 0 )
                        rDoc.Delete( nPos, nEndPos );

                    // Add the non-breaking space at the end pos
                    if ( bHasSpace )
                        rDoc.Insert( nPos, CHAR_HARDBLANK );
                    bRunNext = true;
                    bRet = true;
                }
                else if ( chars.indexOf( cPrevChar ) != -1 )
                    bRunNext = true;
            }
        }
        else if ( cChar == '/' && nEndPos > 1 && rTxt.Len() > (nEndPos - 1) )
        {
            // Remove the hardspace right before to avoid formatting URLs
            sal_Unicode cPrevChar = rTxt.GetChar( nEndPos - 1 );
            sal_Unicode cMaybeSpaceChar = rTxt.GetChar( nEndPos - 2 );
            if ( cPrevChar == ':' && cMaybeSpaceChar == CHAR_HARDBLANK )
            {
                rDoc.Delete( nEndPos - 2, nEndPos - 1 );
                bRet = true;
            }
        }
    }

    return bRet;
}

BOOL SvxAutoCorrect::FnSetINetAttr( SvxAutoCorrDoc& rDoc, const String& rTxt,
                                    xub_StrLen nSttPos, xub_StrLen nEndPos,
                                    LanguageType eLang )
{
    String sURL( URIHelper::FindFirstURLInText( rTxt, nSttPos, nEndPos,
                                                GetCharClass( eLang ) ));
    BOOL bRet = 0 != sURL.Len();
    if( bRet )			// also Attribut setzen:
        rDoc.SetINetAttr( nSttPos, nEndPos, sURL );
    return bRet;
}


BOOL SvxAutoCorrect::FnChgWeightUnderl( SvxAutoCorrDoc& rDoc, const String& rTxt,
                                        xub_StrLen, xub_StrLen nEndPos,
                                        LanguageType eLang )
{
    // Bedingung:
    //	Am Anfang:	_ oder * hinter Space mit nachfolgenden !Space
    //	Am Ende:	_ oder * vor Space (Worttrenner?)

    sal_Unicode c, cInsChar = rTxt.GetChar( nEndPos );	// unterstreichen oder fett
    if( ++nEndPos != rTxt.Len() &&
        !IsWordDelim( rTxt.GetChar( nEndPos ) ) )
        return FALSE;

    --nEndPos;

    BOOL bAlphaNum = FALSE;
    xub_StrLen nPos = nEndPos, nFndPos = STRING_NOTFOUND;
    CharClass& rCC = GetCharClass( eLang );

    while( nPos )
    {
        switch( c = rTxt.GetChar( --nPos ) )
        {
        case '_':
        case '*':
            if( c == cInsChar )
            {
                if( bAlphaNum && nPos+1 < nEndPos && ( !nPos ||
                    IsWordDelim( rTxt.GetChar( nPos-1 ))) &&
                    !IsWordDelim( rTxt.GetChar( nPos+1 )))
                        nFndPos = nPos;
                else
                    // Bedingung ist nicht erfuellt, also abbrechen
                    nFndPos = STRING_NOTFOUND;
                nPos = 0;
            }
            break;
        default:
            if( !bAlphaNum )
                bAlphaNum = rCC.isLetterNumeric( rTxt, nPos );
        }
    }

    if( STRING_NOTFOUND != nFndPos )
    {
        // ueber den gefundenen Bereich das Attribut aufspannen und
        // das gefunde und am Ende stehende Zeichen loeschen
        if( '*' == cInsChar )			// Fett
        {
            SvxWeightItem aSvxWeightItem( WEIGHT_BOLD, SID_ATTR_CHAR_WEIGHT );
            rDoc.SetAttr( nFndPos + 1, nEndPos,
                            SID_ATTR_CHAR_WEIGHT,
                            aSvxWeightItem);
        }
        else							// unterstrichen
        {
            SvxUnderlineItem aSvxUnderlineItem( UNDERLINE_SINGLE, SID_ATTR_CHAR_UNDERLINE );
            rDoc.SetAttr( nFndPos + 1, nEndPos,
                            SID_ATTR_CHAR_UNDERLINE,
                            aSvxUnderlineItem);
        }
        rDoc.Delete( nEndPos, nEndPos + 1 );
        rDoc.Delete( nFndPos, nFndPos + 1 );
    }

    return STRING_NOTFOUND != nFndPos;
}


BOOL SvxAutoCorrect::FnCptlSttSntnc( SvxAutoCorrDoc& rDoc,
                                    const String& rTxt, BOOL bNormalPos,
                                    xub_StrLen nSttPos, xub_StrLen nEndPos,
                                    LanguageType eLang )
{
    // Grossbuchstabe am Satz-Anfang ??
    if( !rTxt.Len() || nEndPos <= nSttPos )
        return FALSE;

     CharClass& rCC = GetCharClass( eLang );
    String aText( rTxt );
    const sal_Unicode *pStart = aText.GetBuffer(),
                      *pStr = pStart + nEndPos,
                      *pWordStt = 0,
                      *pDelim = 0;

    BOOL bAtStart = FALSE, bPrevPara = FALSE;
    do {
        --pStr;
        if( rCC.isLetter(
                aText, sal::static_int_cast< xub_StrLen >( pStr - pStart ) ) )
        {
            if( !pWordStt )
                pDelim = pStr+1;
            pWordStt = pStr;
        }
        else if( pWordStt &&
                 !rCC.isDigit(
                     aText,
                     sal::static_int_cast< xub_StrLen >( pStr - pStart ) ) )
        {
            if( lcl_IsInAsciiArr( sImplWordChars, *pStr ) &&
                pWordStt - 1 == pStr &&
                // --> FME 2005-02-14 #i38971#
                // l'intallazione at beginning of paragraph. Replaced < by <=
                (long)(pStart + 1) <= (long)pStr &&
                // <--
                rCC.isLetter(
                    aText,
                    sal::static_int_cast< xub_StrLen >( pStr-1 - pStart ) ) )
                pWordStt = --pStr;
            else
                break;
        }
    } while( 0 == ( bAtStart = (pStart == pStr)) );


    if(	!pWordStt ||
        rCC.isDigit(
            aText, sal::static_int_cast< xub_StrLen >( pStr - pStart ) ) ||
        IsUpperLetter(
            rCC.getCharacterType(
                aText,
                sal::static_int_cast< xub_StrLen >( pWordStt - pStart ) ) ) ||
        0x1 == *pWordStt || 0x2 == *pWordStt )
        return FALSE;		// kein zu ersetzendes Zeichen, oder schon ok

    // JP 27.10.97: wenn das Wort weniger als 3 Zeichen hat und der Trenner
    //				ein "Num"-Trenner ist, dann nicht ersetzen!
    //				Damit wird ein "a.",  "a)", "a-a" nicht ersetzt!
    if( *pDelim && 2 >= pDelim - pWordStt &&
        lcl_IsInAsciiArr( ".-)>", *pDelim ) )
        return FALSE;

    if( !bAtStart )	// noch kein Absatz Anfang ?
    {
        if ( IsWordDelim( *pStr ) )
        {
            while( 0 == ( bAtStart = (pStart == pStr--) ) && IsWordDelim( *pStr ))
                ;
        }
        // Asian full stop, full width full stop, full width exclamation mark
        // and full width question marks are treated as word delimiters
        else if ( 0x3002 != *pStr && 0xFF0E != *pStr && 0xFF01 != *pStr &&
                  0xFF1F != *pStr )
            return FALSE; // kein gueltiger Trenner -> keine Ersetzung
    }

    if( bAtStart )	// am Absatz Anfang ?
    {
        // Ueberpruefe den vorherigen Absatz, wenn es diesen gibt.
        // Wenn ja, dann pruefe auf SatzTrenner am Ende.
        const String* pPrevPara = rDoc.GetPrevPara( bNormalPos );
        if( !pPrevPara )
        {
            // gueltiger Trenner -> Ersetze
            String sChar( *pWordStt );
            rCC.toUpper( sChar );
            return  sChar != *pWordStt &&
                    rDoc.Replace( xub_StrLen( pWordStt - pStart ), sChar );
        }

        aText = *pPrevPara;
        bPrevPara = TRUE;
        bAtStart = FALSE;
        pStart = aText.GetBuffer();
        pStr = pStart + aText.Len();

        do {			// alle Blanks ueberlesen
            --pStr;
            if( !IsWordDelim( *pStr ))
                break;
        } while( 0 == ( bAtStart = (pStart == pStr)) );

        if( bAtStart )
            return FALSE;		// kein gueltiger Trenner -> keine Ersetzung
    }

    // bis hierhier wurde [ \t]+[A-Z0-9]+ gefunden. Test jetzt auf den
    // Satztrenner. Es koennen alle 3 vorkommen, aber nicht mehrfach !!
    const sal_Unicode* pExceptStt = 0;
    if( !bAtStart )
    {
        BOOL bWeiter = TRUE;
        int nFlag = C_NONE;
        do {
            switch( *pStr )
            {
            // Western and Asian full stop
            case '.':
            case 0x3002 :
            case 0xFF0E :
                {
                    if( nFlag & C_FULL_STOP )
                        return FALSE;		// kein gueltiger Trenner -> keine Ersetzung
                    nFlag |= C_FULL_STOP;
                    pExceptStt = pStr;
                }
                break;
            case '!':
            case 0xFF01 :
                {
                    if( nFlag & C_EXCLAMATION_MARK )
                        return FALSE; 	// kein gueltiger Trenner -> keine Ersetzung
                    nFlag |= C_EXCLAMATION_MARK;
                }
                break;
            case '?':
            case 0xFF1F :
                {
                    if( nFlag & C_QUESTION_MARK)
                        return FALSE;		// kein gueltiger Trenner -> keine Ersetzung
                    nFlag |= C_QUESTION_MARK;
                }
                break;
            default:
                if( !nFlag )
                    return FALSE;		// kein gueltiger Trenner -> keine Ersetzung
                else
                    bWeiter = FALSE;
                break;
            }

            if( bWeiter && pStr-- == pStart )
            {
// !!! wenn am Anfang, dann nie ersetzen.
//				if( !nFlag )
                    return FALSE;		// kein gueltiger Trenner -> keine Ersetzung
//				++pStr;
//				break;		// Schleife beenden
            }
        } while( bWeiter );
        if( C_FULL_STOP != nFlag )
            pExceptStt = 0;
    }

    if( 2 > ( pStr - pStart ) )
        return FALSE;

    if( !rCC.isLetterNumeric(
            aText, sal::static_int_cast< xub_StrLen >( pStr-- - pStart ) ) )
    {
        BOOL bValid = FALSE, bAlphaFnd = FALSE;
        const sal_Unicode* pTmpStr = pStr;
        while( !bValid )
        {
            if( rCC.isDigit(
                    aText,
                    sal::static_int_cast< xub_StrLen >( pTmpStr - pStart ) ) )
            {
                bValid = TRUE;
                pStr = pTmpStr - 1;
            }
            else if( rCC.isLetter(
                         aText,
                         sal::static_int_cast< xub_StrLen >(
                             pTmpStr - pStart ) ) )
            {
                if( bAlphaFnd )
                {
                    bValid = TRUE;
                    pStr = pTmpStr;
                }
                else
                    bAlphaFnd = TRUE;
            }
            else if( bAlphaFnd || IsWordDelim( *pTmpStr ) )
                break;

            if( pTmpStr == pStart )
                break;

            --pTmpStr;
        }

        if( !bValid )
            return FALSE;		// kein gueltiger Trenner -> keine Ersetzung
    }

    BOOL bNumericOnly = '0' <= *(pStr+1) && *(pStr+1) <= '9';

    // suche den Anfang vom Wort
    while( !IsWordDelim( *pStr ))
    {
        if( bNumericOnly &&
            rCC.isLetter(
                aText, sal::static_int_cast< xub_StrLen >( pStr - pStart ) ) )
            bNumericOnly = FALSE;

        if( pStart == pStr )
            break;

        --pStr;
    }

    if( bNumericOnly )		// besteht nur aus Zahlen, dann nicht
        return FALSE;

    if( IsWordDelim( *pStr ))
        ++pStr;

    String sWord;

    // ueberpruefe anhand der Exceptionliste
    if( pExceptStt )
    {
        sWord = String(
            pStr, sal::static_int_cast< xub_StrLen >( pExceptStt - pStr + 1 ) );
        if( FindInCplSttExceptList(eLang, sWord) )
            return FALSE;

        // loesche alle nicht alpanum. Zeichen am Wortanfang/-ende und
        // teste dann noch mal ( erkennt: "(min.", "/min.", usw.)
        String sTmp( sWord );
        while( sTmp.Len() &&
                !rCC.isLetterNumeric( sTmp, 0 ) )
            sTmp.Erase( 0, 1 );

        // alle hinteren nicht alphanumerische Zeichen bis auf das
        // Letzte entfernen
        xub_StrLen nLen = sTmp.Len();
        while( nLen && !rCC.isLetterNumeric( sTmp, nLen-1 ) )
            --nLen;
        if( nLen + 1 < sTmp.Len() )
            sTmp.Erase( nLen + 1 );

        if( sTmp.Len() && sTmp.Len() != sWord.Len() &&
            FindInCplSttExceptList(eLang, sTmp))
            return FALSE;

        if(FindInCplSttExceptList(eLang, sWord, TRUE))
            return FALSE;
    }

    // Ok, dann ersetze mal
    sal_Unicode cSave = *pWordStt;
    nSttPos = sal::static_int_cast< xub_StrLen >( pWordStt - rTxt.GetBuffer() );
    String sChar( cSave );
    rCC.toUpper( sChar );
    BOOL bRet = sChar.GetChar(0) != cSave && rDoc.Replace( nSttPos, sChar );

    // das Wort will vielleicht jemand haben
    if( bRet && SaveWordCplSttLst & nFlags )
        rDoc.SaveCpltSttWord( CptlSttSntnc, nSttPos, sWord, cSave );

    return bRet;
}

bool SvxAutoCorrect::FnCorrectCapsLock( SvxAutoCorrDoc& rDoc, const String& rTxt,
                                        xub_StrLen nSttPos, xub_StrLen nEndPos,
                                        LanguageType eLang )
{
    if (nEndPos - nSttPos < 2)
        // string must be at least 2-character long.
        return false;

    CharClass& rCC = GetCharClass( eLang );

    // Check the first 2 letters.
    if ( !IsLowerLetter(rCC.getCharacterType(rTxt, nSttPos)) )
        return false;

    if ( !IsUpperLetter(rCC.getCharacterType(rTxt, nSttPos+1)) )
        return false;

    String aConverted;
    aConverted.Append( rCC.upper(rTxt.GetChar(nSttPos)) );
    aConverted.Append( rCC.lower(rTxt.GetChar(nSttPos+1)) );

    for (xub_StrLen i = nSttPos+2; i < nEndPos; ++i)
    {
        if ( IsLowerLetter(rCC.getCharacterType(rTxt, i)) )
            // A lowercase letter disqualifies the whole text.
            return false;

        if ( IsUpperLetter(rCC.getCharacterType(rTxt, i)) )
            // Another uppercase letter.  Convert it.
            aConverted.Append( rCC.lower(rTxt.GetChar(i)) );
        else
            // This is not an alphabetic letter.  Leave it as-is.
            aConverted.Append(rTxt.GetChar(i));
    }

    // Replace the word.
    rDoc.Delete(nSttPos, nEndPos);
    rDoc.Insert(nSttPos, aConverted);

    return true;
}


sal_Unicode SvxAutoCorrect::GetQuote( sal_Unicode cInsChar, BOOL bSttQuote,
                                        LanguageType eLang ) const
{
    sal_Unicode cRet = bSttQuote ? ( '\"' == cInsChar
                                    ? GetStartDoubleQuote()
                                    : GetStartSingleQuote() )
                                   : ( '\"' == cInsChar
                                    ? GetEndDoubleQuote()
                                    : GetEndSingleQuote() );
    if( !cRet )
    {
        // dann ueber die Language das richtige Zeichen heraussuchen
        if( LANGUAGE_NONE == eLang )
            cRet = cInsChar;
        else
        {
            LocaleDataWrapper& rLcl = GetLocaleDataWrapper( eLang );
            String sRet( bSttQuote
                            ? ( '\"' == cInsChar
                                ? rLcl.getDoubleQuotationMarkStart()
                                : rLcl.getQuotationMarkStart() )
                            : ( '\"' == cInsChar
                                ? rLcl.getDoubleQuotationMarkEnd()
                                : rLcl.getQuotationMarkEnd() ));
            cRet = sRet.Len() ? sRet.GetChar( 0 ) : cInsChar;
        }
    }
    return cRet;
}

void SvxAutoCorrect::InsertQuote( SvxAutoCorrDoc& rDoc, xub_StrLen nInsPos,
                                    sal_Unicode cInsChar, BOOL bSttQuote,
                                    BOOL bIns )
{
    LanguageType eLang = rDoc.GetLanguage( nInsPos, FALSE );
    sal_Unicode cRet = GetQuote( cInsChar, bSttQuote, eLang );

    //JP 13.02.99: damit beim Undo das "einfuegte" Zeichen wieder erscheint,
    //				wird es erstmal eingefuegt und dann ueberschrieben
    String sChg( cInsChar );
    if( bIns )
        rDoc.Insert( nInsPos, sChg );
    else
        rDoc.Replace( nInsPos, sChg );

    //JP 13.08.97: Bug 42477 - bei doppelten Anfuehrungszeichen muss bei
    //				franzoesischer Sprache an Anfang ein Leerzeichen dahinter
    //				und am Ende ein Leerzeichen dahinter eingefuegt werden.
    sChg = cRet;

    if( '\"' == cInsChar )
    {
        if( LANGUAGE_SYSTEM == eLang )
            eLang = GetAppLang();
        switch( eLang )
        {
        case LANGUAGE_FRENCH:
        case LANGUAGE_FRENCH_BELGIAN:
        case LANGUAGE_FRENCH_CANADIAN:
        case LANGUAGE_FRENCH_SWISS:
        case LANGUAGE_FRENCH_LUXEMBOURG:
            // JP 09.02.99: das zusaetzliche Zeichen immer per Insert einfuegen.
            //				Es ueberschreibt nichts!
            {
                String s( static_cast< sal_Unicode >(0xA0) );
                    // UNICODE code for no break space
                if( rDoc.Insert( bSttQuote ? nInsPos+1 : nInsPos, s ))
                {
                    if( !bSttQuote )
                        ++nInsPos;
                }
            }
            break;
        }
    }

    rDoc.Replace( nInsPos, sChg );
}

String SvxAutoCorrect::GetQuote( SvxAutoCorrDoc& rDoc, xub_StrLen nInsPos,
                                sal_Unicode cInsChar, BOOL bSttQuote )
{
    LanguageType eLang = rDoc.GetLanguage( nInsPos, FALSE );
    sal_Unicode cRet = GetQuote( cInsChar, bSttQuote, eLang );

    String sRet( cRet );
    //JP 13.08.97: Bug 42477 - bei doppelten Anfuehrungszeichen muss bei
    //				franzoesischer Sprache an Anfang ein Leerzeichen dahinter
    //				und am Ende ein Leerzeichen dahinter eingefuegt werden.
    if( '\"' == cInsChar )
    {
        if( LANGUAGE_SYSTEM == eLang )
            eLang = GetAppLang();
        switch( eLang )
        {
        case LANGUAGE_FRENCH:
        case LANGUAGE_FRENCH_BELGIAN:
        case LANGUAGE_FRENCH_CANADIAN:
        case LANGUAGE_FRENCH_SWISS:
        case LANGUAGE_FRENCH_LUXEMBOURG:
            if( bSttQuote )
                sRet += ' ';
            else
                sRet.Insert( ' ', 0 );
            break;
        }
    }
    return sRet;
}

ULONG SvxAutoCorrect::AutoCorrect( SvxAutoCorrDoc& rDoc, const String& rTxt,
                                    xub_StrLen nInsPos, sal_Unicode cChar,
                                    BOOL bInsert, Window* pFrameWin )
{
    ULONG nRet = 0;
    bool bIsNextRun = bRunNext;
    bRunNext = false;  // if it was set, then it has to be turned off
    
    do{		                            // only for middle check loop !!
        if( cChar )
        {
            //JP 10.02.97: doppelte Spaces verhindern
            if( nInsPos && ' ' == cChar &&
                IsAutoCorrFlag( IgnoreDoubleSpace ) &&
                ' ' == rTxt.GetChar( nInsPos - 1 ) )
            {
                nRet = IgnoreDoubleSpace;
                break;
            }

            BOOL bSingle = '\'' == cChar;
            BOOL bIsReplaceQuote =
                        (IsAutoCorrFlag( ChgQuotes ) && ('\"' == cChar )) ||
                        (IsAutoCorrFlag( ChgSglQuotes ) && bSingle );
            if( bIsReplaceQuote )
            {
                sal_Unicode cPrev;
                BOOL bSttQuote = !nInsPos ||
                        IsWordDelim( ( cPrev = rTxt.GetChar( nInsPos-1 ))) ||
// os: #56034# - Warum kein schliessendes Anfuehrungszeichen nach dem Bindestrich?
//						strchr( "-([{", cPrev ) ||
                        lcl_IsInAsciiArr( "([{", cPrev ) ||
                        ( cEmDash && cEmDash == cPrev ) ||
                        ( cEnDash && cEnDash == cPrev );

                InsertQuote( rDoc, nInsPos, cChar, bSttQuote, bInsert );
                nRet = bSingle ? ChgSglQuotes : ChgQuotes;
                break;
            }

            if( bInsert )
                rDoc.Insert( nInsPos, cChar );
            else
                rDoc.Replace( nInsPos, cChar );
    
            // Hardspaces autocorrection    
            if ( IsAutoCorrFlag( AddNonBrkSpace ) )
            {
                if ( NeedsHardspaceAutocorr( cChar ) &&
                    FnAddNonBrkSpace( rDoc, rTxt, 0, nInsPos, rDoc.GetLanguage( nInsPos, FALSE ) ) )
                {
                    nRet = AddNonBrkSpace;
                }
                else if ( bIsNextRun && !IsAutoCorrectChar( cChar ) )
                {
                    // Remove the NBSP if it wasn't an autocorrection
                    if ( nInsPos != 0 && NeedsHardspaceAutocorr( rTxt.GetChar( nInsPos - 1 ) ) &&
                            cChar != ' ' && cChar != '\t' && cChar != CHAR_HARDBLANK )
                    {
                        // Look for the last HARD_SPACE
                        xub_StrLen nPos = nInsPos - 1;
                        bool bContinue = true;
                        while ( bContinue )
                        {
                            const sal_Unicode cTmpChar = rTxt.GetChar( nPos );
                            if ( cTmpChar == CHAR_HARDBLANK )
                            {
                                rDoc.Delete( nPos, nPos + 1 );
                                nRet = AddNonBrkSpace;
                                bContinue = false;
                            }
                            else if ( !NeedsHardspaceAutocorr( cTmpChar ) || nPos == 0 )
                                bContinue = false;
                            nPos--;
                        }
                    }
                }
            }
        }

        if( !nInsPos )
            break;

        xub_StrLen nPos = nInsPos - 1;

        // Bug 19286: nur direkt hinter dem "Wort" aufsetzen
        if( IsWordDelim( rTxt.GetChar( nPos )))
            break;

        // automatisches Fett oder Unterstreichen setzen?
        if( '*' == cChar || '_' == cChar )
        {
            if( IsAutoCorrFlag( ChgWeightUnderl ) &&
                FnChgWeightUnderl( rDoc, rTxt, 0, nPos+1 ) )
                nRet = ChgWeightUnderl;
            break;
        }

        while( nPos && !IsWordDelim( rTxt.GetChar( --nPos )))
            ;

        // Absatz-Anfang oder ein Blank gefunden, suche nach dem Wort
        // Kuerzel im Auto
        xub_StrLen nCapLttrPos = nPos+1;		// auf das 1. Zeichen
        if( !nPos && !IsWordDelim( rTxt.GetChar( 0 )))
            --nCapLttrPos;			// Absatz Anfang und kein Blank !

        LanguageType eLang = rDoc.GetLanguage( nCapLttrPos, FALSE );
        if( LANGUAGE_SYSTEM == eLang )
            eLang = MsLangId::getSystemLanguage();
        CharClass& rCC = GetCharClass( eLang );

        // Bug 19285: Symbolzeichen nicht anfassen
        if( lcl_IsSymbolChar( rCC, rTxt, nCapLttrPos, nInsPos ))
            break;
            
        if( IsAutoCorrFlag( Autocorrect ) )
        {
            const String* pPara = 0;
            const String** ppPara = IsAutoCorrFlag(CptlSttSntnc) ? &pPara : 0;

            BOOL bChgWord = rDoc.ChgAutoCorrWord( nCapLttrPos, nInsPos,
                                                    *this, ppPara );
            if( !bChgWord )
            {
                // JP 16.06.98: dann versuche mal alle !AlphaNum. Zeichen los zu
                //				werden und teste dann nochmals
                //JP 22.04.99: Bug 63883 - entferne nur die "Klammern Start/-Anfaenge",
                //				alle anderen Zeichen muessen drin bleiben.
                xub_StrLen nCapLttrPos1 = nCapLttrPos, nInsPos1 = nInsPos;
                while( nCapLttrPos1 < nInsPos &&
                        lcl_IsInAsciiArr( sImplSttSkipChars, rTxt.GetChar( nCapLttrPos1 ) )
                        )
                        ++nCapLttrPos1;
                while( nCapLttrPos1 < nInsPos1 && nInsPos1 &&
                        lcl_IsInAsciiArr( sImplEndSkipChars, rTxt.GetChar( nInsPos1-1 ) )
                        )
                        --nInsPos1;

                if( (nCapLttrPos1 != nCapLttrPos || nInsPos1 != nInsPos ) &&
                    nCapLttrPos1 < nInsPos1 &&
                    rDoc.ChgAutoCorrWord( nCapLttrPos1, nInsPos1, *this, ppPara ))
                {
                    bChgWord = TRUE;
                    nCapLttrPos = nCapLttrPos1;
                }
            }

            if( bChgWord )
            {
                nRet = Autocorrect;
                if( pPara )
                {
                    xub_StrLen nEnd = nCapLttrPos;
                    while( nEnd < pPara->Len() &&
                            !IsWordDelim( pPara->GetChar( nEnd )))
                        ++nEnd;

                    // Grossbuchstabe am Satz-Anfang ??
                    if( IsAutoCorrFlag( CptlSttSntnc ) &&
                        FnCptlSttSntnc( rDoc, *pPara, FALSE,
                                                nCapLttrPos, nEnd, eLang ) )
                        nRet |= CptlSttSntnc;

                    if( IsAutoCorrFlag( ChgToEnEmDash ) &&
                        FnChgToEnEmDash( rDoc, rTxt, nCapLttrPos, nEnd, eLang ) )
                        nRet |= ChgToEnEmDash;
                }
                break;
            }
        }

        if( ( IsAutoCorrFlag( nRet = ChgOrdinalNumber ) &&
                FnChgOrdinalNumber( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) ) ||
            ( IsAutoCorrFlag( nRet = SetINetAttr ) &&
                ( ' ' == cChar || '\t' == cChar || 0x0a == cChar || !cChar ) &&
                FnSetINetAttr( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) ) )
            ;
        else
        {
            bool bLockKeyOn = pFrameWin && (pFrameWin->GetIndicatorState() & INDICATOR_CAPSLOCK);

            nRet = 0;
            if ( bLockKeyOn && IsAutoCorrFlag( CorrectCapsLock ) &&
                 FnCorrectCapsLock( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) )
            {
                // Correct accidental use of cAPS LOCK key (do this only when
                // the caps or shift lock key is pressed).  Turn off the caps
                // lock afterwords.
                nRet |= CorrectCapsLock;
                pFrameWin->SimulateKeyPress( KEY_CAPSLOCK );
            }

            // Grossbuchstabe am Satz-Anfang ??
            if( IsAutoCorrFlag( CptlSttSntnc ) &&
                FnCptlSttSntnc( rDoc, rTxt, TRUE, nCapLttrPos, nInsPos, eLang ) )
                nRet |= CptlSttSntnc;

            // Zwei Grossbuchstaben am Wort-Anfang ??
            if( IsAutoCorrFlag( CptlSttWrd ) &&
                FnCptlSttWrd( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) )
                nRet |= CptlSttWrd;

            if( IsAutoCorrFlag( ChgToEnEmDash ) &&
                FnChgToEnEmDash( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) )
                nRet |= ChgToEnEmDash;
        }

    } while( FALSE );

    if( nRet )
    {
        ULONG nHelpId = 0;
        if( nRet & ( Autocorrect|CptlSttSntnc|CptlSttWrd|ChgToEnEmDash ) )
        {
            // von 0 - 15
            if( nRet & ChgToEnEmDash )
                nHelpId += 8;
            if( nRet & Autocorrect )
                nHelpId += 4;
            if( nRet & CptlSttSntnc )
                nHelpId += 2;
            if( nRet & CptlSttWrd )
                nHelpId += 1;
        }
        else
        {
                 if( nRet & ChgQuotes) 			nHelpId = 16;
            else if( nRet & ChgSglQuotes) 		nHelpId = 17;
            else if( nRet & SetINetAttr) 		nHelpId = 18;
            else if( nRet & IgnoreDoubleSpace)  nHelpId = 19;
            else if( nRet & ChgWeightUnderl) 	nHelpId = 20;
            else if( nRet & AddNonBrkSpace)     nHelpId = 21;
            else if( nRet & ChgOrdinalNumber)	nHelpId = 22;
        }

        if( nHelpId )
        {
            nHelpId += HID_AUTOCORR_HELP_START - 1;
            Application::GetHelp()->OpenHelpAgent( nHelpId );
        }
    }


    return nRet;
}

SvxAutoCorrectLanguageLists& SvxAutoCorrect::_GetLanguageList(
                                                        LanguageType eLang )
{
    if( !pLangTable->IsKeyValid( ULONG( eLang )))
        CreateLanguageFile( eLang, TRUE);
    return *pLangTable->Seek( ULONG( eLang ) );
}

void SvxAutoCorrect::SaveCplSttExceptList( LanguageType eLang )
{
    if( pLangTable->IsKeyValid( ULONG( eLang )))
    {
        SvxAutoCorrectLanguageListsPtr pLists = pLangTable->Seek(ULONG(eLang));
        if( pLists )
            pLists->SaveCplSttExceptList();
    }
#ifdef DBG_UTIL
    else
    {
        DBG_ERROR("speichern einer leeren Liste?");
    }
#endif
}

void SvxAutoCorrect::SaveWrdSttExceptList(LanguageType eLang)
{
    if(pLangTable->IsKeyValid(ULONG(eLang)))
    {
        SvxAutoCorrectLanguageListsPtr pLists = pLangTable->Seek(ULONG(eLang));
        if(pLists)
            pLists->SaveWrdSttExceptList();
    }
#ifdef DBG_UTIL
    else
    {
        DBG_ERROR("speichern einer leeren Liste?");
    }
#endif
}


    // fuegt ein einzelnes Wort hinzu. Die Liste wird sofort
    // in die Datei geschrieben!
BOOL SvxAutoCorrect::AddCplSttException( const String& rNew,
                                        LanguageType eLang )
{
    SvxAutoCorrectLanguageListsPtr pLists = 0;
    //entweder die richtige Sprache ist vorhanden oder es kommt in die allg. Liste
    if( pLangTable->IsKeyValid(ULONG(eLang)))
        pLists = pLangTable->Seek(ULONG(eLang));
    else if(pLangTable->IsKeyValid(ULONG(LANGUAGE_DONTKNOW))||
            CreateLanguageFile(LANGUAGE_DONTKNOW, TRUE))
    {
        pLists = pLangTable->Seek(ULONG(LANGUAGE_DONTKNOW));
    }
    DBG_ASSERT(pLists, "keine Autokorrekturdatei");
    return pLists->AddToCplSttExceptList(rNew);
}


    // fuegt ein einzelnes Wort hinzu. Die Liste wird sofort
    // in die Datei geschrieben!
BOOL SvxAutoCorrect::AddWrtSttException( const String& rNew,
                                         LanguageType eLang )
{
    SvxAutoCorrectLanguageListsPtr pLists = 0;
    //entweder die richtige Sprache ist vorhanden oder es kommt in die allg. Liste
    if(pLangTable->IsKeyValid(ULONG(eLang)))
        pLists = pLangTable->Seek(ULONG(eLang));
    else if(pLangTable->IsKeyValid(ULONG(LANGUAGE_DONTKNOW))||
            CreateLanguageFile(LANGUAGE_DONTKNOW, TRUE))
        pLists = pLangTable->Seek(ULONG(LANGUAGE_DONTKNOW));
    DBG_ASSERT(pLists, "keine Autokorrekturdatei");
    return pLists->AddToWrdSttExceptList(rNew);
}




void SvxAutoCorrect::SetUserAutoCorrFileName( const String& rNew )
{
    if( sUserAutoCorrFile != rNew )
    {
        sUserAutoCorrFile = rNew;

        // sind die Listen gesetzt sind, so muessen sie jetzt geloescht
        // werden
        lcl_ClearTable(*pLangTable);
        nFlags &= ~(CplSttLstLoad | WrdSttLstLoad | ChgWordLstLoad );
    }
}

void SvxAutoCorrect::SetShareAutoCorrFileName( const String& rNew )
{
    if( sShareAutoCorrFile != rNew )
    {
        sShareAutoCorrFile = rNew;

        // sind die Listen gesetzt sind, so muessen sie jetzt geloescht
        // werden
        lcl_ClearTable(*pLangTable);
        nFlags &= ~(CplSttLstLoad | WrdSttLstLoad | ChgWordLstLoad );
    }
}


BOOL SvxAutoCorrect::GetPrevAutoCorrWord( SvxAutoCorrDoc& rDoc,
                                        const String& rTxt, xub_StrLen nPos,
                                        String& rWord ) const
{
    if( !nPos )
        return FALSE;

    xub_StrLen nEnde = nPos;

    // dahinter muss ein Blank oder Tab folgen!
    if( ( nPos < rTxt.Len() &&
        !IsWordDelim( rTxt.GetChar( nPos ))) ||
        IsWordDelim( rTxt.GetChar( --nPos )))
        return FALSE;

    while( nPos && !IsWordDelim( rTxt.GetChar( --nPos )))
        ;

    // Absatz-Anfang oder ein Blank gefunden, suche nach dem Wort
    // Kuerzel im Auto
    xub_StrLen nCapLttrPos = nPos+1;		// auf das 1. Zeichen
    if( !nPos && !IsWordDelim( rTxt.GetChar( 0 )))
        --nCapLttrPos;			// Absatz Anfang und kein Blank !

    while( lcl_IsInAsciiArr( sImplSttSkipChars, rTxt.GetChar( nCapLttrPos )) )
        if( ++nCapLttrPos >= nEnde )
            return FALSE;

    // Bug 19285: Symbolzeichen nicht anfassen
    // Interresant erst ab 3 Zeichen
    if( 3 > nEnde - nCapLttrPos )
        return FALSE;

    LanguageType eLang = rDoc.GetLanguage( nCapLttrPos, FALSE );
    if( LANGUAGE_SYSTEM == eLang )
        eLang = MsLangId::getSystemLanguage();

    SvxAutoCorrect* pThis = (SvxAutoCorrect*)this;
    CharClass& rCC = pThis->GetCharClass( eLang );

    if( lcl_IsSymbolChar( rCC, rTxt, nCapLttrPos, nEnde ))
        return FALSE;

    rWord = rTxt.Copy( nCapLttrPos, nEnde - nCapLttrPos );
    return TRUE;
}

BOOL SvxAutoCorrect::CreateLanguageFile( LanguageType eLang, BOOL bNewFile )
{
    DBG_ASSERT(!pLangTable->IsKeyValid(ULONG(eLang)), "Sprache ist bereits vorhanden");

    String sUserDirFile( GetAutoCorrFileName( eLang, TRUE, FALSE )),
           sShareDirFile( sUserDirFile );
    SvxAutoCorrectLanguageListsPtr pLists = 0;

    Time nMinTime( 0, 2 ), nAktTime, nLastCheckTime;
    ULONG nFndPos;
    if( TABLE_ENTRY_NOTFOUND !=
                    pLastFileTable->SearchKey( ULONG( eLang ), &nFndPos ) &&
        ( nLastCheckTime.SetTime( pLastFileTable->GetObject( nFndPos )),
            nLastCheckTime < nAktTime ) &&
        ( nAktTime - nLastCheckTime ) < nMinTime )
    {
        // no need to test the file, because the last check is not older then
        // 2 minutes.
        if( bNewFile )
        {
            sShareDirFile = sUserDirFile;
            pLists = new SvxAutoCorrectLanguageLists( *this, sShareDirFile,
                                                        sUserDirFile, eLang );
            pLangTable->Insert( ULONG(eLang), pLists );
            pLastFileTable->Remove( ULONG( eLang ) );
        }
    }
    else if( ( FStatHelper::IsDocument( sUserDirFile ) ||
                FStatHelper::IsDocument( sShareDirFile =
                              GetAutoCorrFileName( eLang, FALSE, FALSE ) ) ) ||
        ( sShareDirFile = sUserDirFile, bNewFile ))
    {
        pLists = new SvxAutoCorrectLanguageLists( *this, sShareDirFile,
                                                    sUserDirFile, eLang );
        pLangTable->Insert( ULONG(eLang), pLists );
        pLastFileTable->Remove( ULONG( eLang ) );
    }
    else if( !bNewFile )
    {
        if( !pLastFileTable->Insert( ULONG( eLang ), nAktTime.GetTime() ))
            pLastFileTable->Replace( ULONG( eLang ), nAktTime.GetTime() );
    }
    return pLists != 0;
}

BOOL SvxAutoCorrect::PutText( const String& rShort, const String& rLong,
                                LanguageType eLang )
{
    BOOL bRet = FALSE;
    if( pLangTable->IsKeyValid( ULONG(eLang)) || CreateLanguageFile(eLang) )
        bRet = pLangTable->Seek( ULONG(eLang) )->PutText(rShort, rLong);
    return bRet;
}


    //	- loesche einen Eintrag
BOOL SvxAutoCorrect::DeleteText( const String& rShort, LanguageType eLang )
{
    BOOL bRet = FALSE;
    if( pLangTable->IsKeyValid( ULONG( eLang )) )
        bRet = pLangTable->Seek( ULONG( eLang ))->DeleteText( rShort );
    return bRet;
}


    //	- return den Ersetzungstext (nur fuer SWG-Format, alle anderen
    //		koennen aus der Wortliste herausgeholt werden!)
BOOL SvxAutoCorrect::GetLongText( const com::sun::star::uno::Reference < com::sun::star::embed::XStorage >&, const String&, const String& , String& )
{
    return FALSE;
}

    //	- Text mit Attributierung (kann nur der SWG - SWG-Format!)
BOOL SvxAutoCorrect::PutText( const com::sun::star::uno::Reference < com::sun::star::embed::XStorage >&, const String&, const String&, SfxObjectShell&,
                                String& )
{
    return FALSE;
}

void EncryptBlockName_Imp( String& rName )
{
    xub_StrLen nLen, nPos = 1;
    rName.Insert( '#', 0 );
    sal_Unicode* pName = rName.GetBufferAccess();
    for ( nLen = rName.Len(), ++pName; nPos < nLen; ++nPos, ++pName )
    {
        if( lcl_IsInAsciiArr( "!/:.\\", *pName ))
            *pName &= 0x0f;
    }
}

/* This code is copied from SwXMLTextBlocks::GeneratePackageName */
void GeneratePackageName ( const String& rShort, String& rPackageName )
{
    rPackageName = rShort;
    xub_StrLen nPos = 0;
    sal_Unicode pDelims[] = { '!', '/', ':', '.', '\\', 0 };
    ByteString sByte ( rPackageName, RTL_TEXTENCODING_UTF7);
    rPackageName = String (sByte, RTL_TEXTENCODING_ASCII_US);
    while( STRING_NOTFOUND != ( nPos = rPackageName.SearchChar( pDelims, nPos )))
    {
        rPackageName.SetChar( nPos, '_' );
        ++nPos;
    }
}

void DecryptBlockName_Imp( String& rName )
{
    if( '#' == rName.GetChar( 0 ) )
    {
        rName.Erase( 0, 1 );
        sal_Unicode* pName = rName.GetBufferAccess();
        xub_StrLen nLen, nPos;
        for ( nLen = rName.Len(), nPos = 0; nPos < nLen; ++nPos, ++pName )
            switch( *pName )
            {
            case 0x01:	*pName = '!';	break;
            case 0x0A:	*pName = ':';	break;
            case 0x0C:	*pName = '\\';	break;
            case 0x0E:	*pName = '.';	break;
            case 0x0F:	*pName = '/';	break;
            }
    }
}


/* -----------------18.11.98 16:00-------------------
 *
 * --------------------------------------------------*/
const SvxAutocorrWord* lcl_SearchWordsInList(
                SvxAutoCorrectLanguageListsPtr pList, const String& rTxt,
                xub_StrLen& rStt, xub_StrLen nEndPos, SvxAutoCorrDoc& )
{
    const SvxAutocorrWordList* pAutoCorrWordList = pList->GetAutocorrWordList();
    TransliterationWrapper& rCmp = GetIgnoreTranslWrapper();
    for( xub_StrLen nPos = 0; nPos < pAutoCorrWordList->Count(); ++nPos )
    {
        const SvxAutocorrWord* pFnd = (*pAutoCorrWordList)[ nPos ];
        const String& rChk = pFnd->GetShort();
        if( nEndPos >= rChk.Len() )
        {
            xub_StrLen nCalcStt = nEndPos - rChk.Len();
            if( ( !nCalcStt || nCalcStt == rStt ||
                ( nCalcStt < rStt &&
                    IsWordDelim( rTxt.GetChar(nCalcStt - 1 ) ))) )
            {
                String sWord( rTxt.GetBuffer() + nCalcStt, rChk.Len() );
                if( rCmp.isEqual( rChk, sWord ))
                {
                    rStt = nCalcStt;
                    return pFnd;
                }
            }
        }
    }
    return 0;
}


// suche das oder die Worte in der ErsetzungsTabelle
const SvxAutocorrWord* SvxAutoCorrect::SearchWordsInList(
                const String& rTxt, xub_StrLen& rStt, xub_StrLen nEndPos,
                SvxAutoCorrDoc& rDoc, LanguageType& rLang )
{
    LanguageType eLang = rLang;
    const SvxAutocorrWord* pRet = 0;
    if( LANGUAGE_SYSTEM == eLang )
        eLang = MsLangId::getSystemLanguage();

    // zuerst nach eLang suchen, dann nach der Obersprache
    // US-Englisch -> Englisch und zuletzt in LANGUAGE_DONTKNOW

    if( pLangTable->IsKeyValid( ULONG( eLang ) ) ||
        CreateLanguageFile( eLang, FALSE ))
    {
        //die Sprache ist vorhanden - also her damit
        SvxAutoCorrectLanguageListsPtr pList = pLangTable->Seek(ULONG(eLang));
        pRet = lcl_SearchWordsInList(  pList, rTxt, rStt, nEndPos, rDoc );
        if( pRet )
        {
            rLang = eLang;
            return pRet;
        }
    }

    // wenn es hier noch nicht gefunden werden konnte, dann weitersuchen
    ULONG nTmpKey1 = eLang & 0x7ff, // die Hauptsprache in vielen Faellen u.B. DE
          nTmpKey2 = eLang & 0x3ff, // sonst z.B. EN
          nTmp;

    if( ((nTmp = nTmpKey1) != (ULONG)eLang &&
         ( pLangTable->IsKeyValid( nTmpKey1 ) ||
           CreateLanguageFile( LanguageType( nTmpKey1 ), FALSE ) )) ||
        (( nTmp = nTmpKey2) != (ULONG)eLang &&
         ( pLangTable->IsKeyValid( nTmpKey2 ) ||
           CreateLanguageFile( LanguageType( nTmpKey2 ), FALSE ) )) )
    {
        //die Sprache ist vorhanden - also her damit
        SvxAutoCorrectLanguageListsPtr pList = pLangTable->Seek( nTmp );
        pRet = lcl_SearchWordsInList( pList, rTxt, rStt, nEndPos, rDoc);
        if( pRet )
        {
            rLang = LanguageType( nTmp );
            return pRet;
        }
    }
    if( pLangTable->IsKeyValid( ULONG( LANGUAGE_DONTKNOW ) ) ||
        CreateLanguageFile( LANGUAGE_DONTKNOW, FALSE ) )
    {
        //die Sprache ist vorhanden - also her damit
        SvxAutoCorrectLanguageListsPtr pList = pLangTable->Seek(ULONG(LANGUAGE_DONTKNOW));
        pRet = lcl_SearchWordsInList( pList, rTxt, rStt, nEndPos, rDoc);
        if( pRet )
        {
            rLang = LANGUAGE_DONTKNOW;
            return pRet;
        }
    }
    return 0;
}
/* -----------------18.11.98 13:46-------------------
 *
 * --------------------------------------------------*/
BOOL SvxAutoCorrect::FindInWrdSttExceptList( LanguageType eLang,
                                             const String& sWord )
{
    //zuerst nach eLang suchen, dann nach der Obersprace US-Englisch -> Englisch
    //und zuletzt in LANGUAGE_DONTKNOW
    ULONG nTmpKey1 = eLang & 0x7ff; // die Hauptsprache in vielen Faellen u.B. DE
    ULONG nTmpKey2 = eLang & 0x3ff; // sonst z.B. EN
    String sTemp(sWord);
    if( pLangTable->IsKeyValid( ULONG( eLang )) ||
        CreateLanguageFile( eLang, FALSE ) )
    {
        //die Sprache ist vorhanden - also her damit
        SvxAutoCorrectLanguageListsPtr pList = pLangTable->Seek(ULONG(eLang));
        String _sTemp(sWord);
        if(pList->GetWrdSttExceptList()->Seek_Entry(&_sTemp))
            return TRUE;

    }
    // wenn es hier noch nicht gefunden werden konnte, dann weitersuchen
    ULONG nTmp;
    if( ((nTmp = nTmpKey1) != (ULONG)eLang &&
         ( pLangTable->IsKeyValid( nTmpKey1 ) ||
           CreateLanguageFile( LanguageType( nTmpKey1 ), FALSE ) )) ||
        (( nTmp = nTmpKey2) != (ULONG)eLang &&
         ( pLangTable->IsKeyValid( nTmpKey2 ) ||
           CreateLanguageFile( LanguageType( nTmpKey2 ), FALSE ) )) )
    {
        //die Sprache ist vorhanden - also her damit
        SvxAutoCorrectLanguageListsPtr pList = pLangTable->Seek(nTmp);
        if(pList->GetWrdSttExceptList()->Seek_Entry(&sTemp))
            return TRUE;
    }
    if(pLangTable->IsKeyValid(ULONG(LANGUAGE_DONTKNOW))|| CreateLanguageFile(LANGUAGE_DONTKNOW, FALSE))
    {
        //die Sprache ist vorhanden - also her damit
        SvxAutoCorrectLanguageListsPtr pList = pLangTable->Seek(ULONG(LANGUAGE_DONTKNOW));
        if(pList->GetWrdSttExceptList()->Seek_Entry(&sTemp))
            return TRUE;
    }
    return FALSE;
}
/* -----------------18.11.98 14:28-------------------
 *
 * --------------------------------------------------*/
BOOL lcl_FindAbbreviation( const SvStringsISortDtor* pList, const String& sWord)
{
    String sAbk( '~' );
    USHORT nPos;
    pList->Seek_Entry( &sAbk, &nPos );
    if( nPos < pList->Count() )
    {
        String sLowerWord( sWord ); sLowerWord.ToLowerAscii();
        const String* pAbk;
        for( USHORT n = nPos;
                n < pList->Count() &&
                '~' == ( pAbk = (*pList)[ n ])->GetChar( 0 );
            ++n )
        {
            // ~ und ~. sind nicht erlaubt!
            if( 2 < pAbk->Len() && pAbk->Len() - 1 <= sWord.Len() )
            {
                String sLowerAbk( *pAbk ); sLowerAbk.ToLowerAscii();
                for( xub_StrLen i = sLowerAbk.Len(), ii = sLowerWord.Len(); i; )
                {
                    if( !--i )		// stimmt ueberein
                        return TRUE;

                    if( sLowerAbk.GetChar( i ) != sLowerWord.GetChar( --ii ))
                        break;
                }
            }
        }
    }
    DBG_ASSERT( !(nPos && '~' == (*pList)[ --nPos ]->GetChar( 0 ) ),
            "falsch sortierte ExeptionListe?" );
    return FALSE;
}
/* -----------------18.11.98 14:49-------------------
 *
 * --------------------------------------------------*/
BOOL SvxAutoCorrect::FindInCplSttExceptList(LanguageType eLang,
                                const String& sWord, BOOL bAbbreviation)
{
    //zuerst nach eLang suchen, dann nach der Obersprace US-Englisch -> Englisch
    //und zuletzt in LANGUAGE_DONTKNOW
    ULONG nTmpKey1 = eLang & 0x7ff; // die Hauptsprache in vielen Faellen u.B. DE
    ULONG nTmpKey2 = eLang & 0x3ff; // sonst z.B. EN
    String sTemp( sWord );
    if( pLangTable->IsKeyValid( ULONG( eLang )) ||
        CreateLanguageFile( eLang, FALSE ))
    {
        //die Sprache ist vorhanden - also her damit
        SvxAutoCorrectLanguageListsPtr pLists = pLangTable->Seek(ULONG(eLang));
        const SvStringsISortDtor* pList = pLists->GetCplSttExceptList();
        if(bAbbreviation ? lcl_FindAbbreviation( pList, sWord)
                         : pList->Seek_Entry( &sTemp ) )
            return TRUE;
    }
    // wenn es hier noch nicht gefunden werden konnte, dann weitersuchen
    ULONG nTmp;

    if( ((nTmp = nTmpKey1) != (ULONG)eLang &&
         ( pLangTable->IsKeyValid( nTmpKey1 ) ||
           CreateLanguageFile( LanguageType( nTmpKey1 ), FALSE ) )) ||
        (( nTmp = nTmpKey2) != (ULONG)eLang &&
         ( pLangTable->IsKeyValid( nTmpKey2 ) ||
           CreateLanguageFile( LanguageType( nTmpKey2 ), FALSE ) )) )
    {
        //die Sprache ist vorhanden - also her damit
        SvxAutoCorrectLanguageListsPtr pLists = pLangTable->Seek(nTmp);
        const SvStringsISortDtor* pList = pLists->GetCplSttExceptList();
        if(bAbbreviation ? lcl_FindAbbreviation( pList, sWord)
                         : pList->Seek_Entry( &sTemp ) )
            return TRUE;
    }
    if(pLangTable->IsKeyValid(ULONG(LANGUAGE_DONTKNOW))|| CreateLanguageFile(LANGUAGE_DONTKNOW, FALSE))
    {
        //die Sprache ist vorhanden - also her damit
        SvxAutoCorrectLanguageListsPtr pLists = pLangTable->Seek(LANGUAGE_DONTKNOW);
        const SvStringsISortDtor* pList = pLists->GetCplSttExceptList();
        if(bAbbreviation ? lcl_FindAbbreviation( pList, sWord)
                         : pList->Seek_Entry( &sTemp ) )
            return TRUE;
    }
    return FALSE;

}

/* -----------------20.11.98 11:53-------------------
 *
 * --------------------------------------------------*/
String SvxAutoCorrect::GetAutoCorrFileName( LanguageType eLang,
                                            BOOL bNewFile, BOOL bTst ) const
{
    String sRet, sExt( MsLangId::convertLanguageToIsoString( eLang ) );
    sExt.Insert('_', 0);
    sExt.AppendAscii( ".dat" );
    if( bNewFile )
        ( sRet = sUserAutoCorrFile )  += sExt;
    else if( !bTst )
        ( sRet = sShareAutoCorrFile )  += sExt;
    else
    {
        // test first in the user directory - if not exist, then
        ( sRet = sUserAutoCorrFile ) += sExt;
        if( !FStatHelper::IsDocument( sRet ))
            ( sRet = sShareAutoCorrFile ) += sExt;
    }
    return sRet;
}

/* -----------------18.11.98 11:16-------------------
 *
 * --------------------------------------------------*/
SvxAutoCorrectLanguageLists::SvxAutoCorrectLanguageLists(
                SvxAutoCorrect& rParent,
                const String& rShareAutoCorrectFile,
                const String& rUserAutoCorrectFile,
                LanguageType eLang)
:	sShareAutoCorrFile( rShareAutoCorrectFile ),
    sUserAutoCorrFile( rUserAutoCorrectFile ),
    eLanguage(eLang),
    pCplStt_ExcptLst( 0 ),
    pWrdStt_ExcptLst( 0 ),
    pAutocorr_List( 0 ),
    rAutoCorrect(rParent),
    nFlags(0)
{
}

/* -----------------18.11.98 11:16-------------------
 *
 * --------------------------------------------------*/
SvxAutoCorrectLanguageLists::~SvxAutoCorrectLanguageLists()
{
    delete pCplStt_ExcptLst;
    delete pWrdStt_ExcptLst;
    delete pAutocorr_List;
}

/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
BOOL SvxAutoCorrectLanguageLists::IsFileChanged_Imp()
{
    // nur alle 2 Minuten aufs FileSystem zugreifen um den
    // Dateistempel zu ueberpruefen
    BOOL bRet = FALSE;

    Time nMinTime( 0, 2 );
    Time nAktTime;
    if( aLastCheckTime > nAktTime ||	   				// ueberlauf ?
        ( nAktTime -= aLastCheckTime ) > nMinTime )		// min Zeit vergangen
    {
        Date aTstDate; Time aTstTime;
        if( FStatHelper::GetModifiedDateTimeOfFile( sShareAutoCorrFile,
                                            &aTstDate, &aTstTime ) &&
            ( aModifiedDate != aTstDate || aModifiedTime != aTstTime ))
        {
            bRet = TRUE;
            // dann mal schnell alle Listen entfernen!
            if( CplSttLstLoad & nFlags && pCplStt_ExcptLst )
                delete pCplStt_ExcptLst, pCplStt_ExcptLst = 0;
            if( WrdSttLstLoad & nFlags && pWrdStt_ExcptLst )
                delete pWrdStt_ExcptLst, pWrdStt_ExcptLst = 0;
            if( ChgWordLstLoad & nFlags && pAutocorr_List )
                delete pAutocorr_List, pAutocorr_List = 0;
            nFlags &= ~(CplSttLstLoad | WrdSttLstLoad | ChgWordLstLoad );
        }
        aLastCheckTime = Time();
    }
    return bRet;
}

void SvxAutoCorrectLanguageLists::LoadXMLExceptList_Imp(
                                        SvStringsISortDtor*& rpLst,
                                        const sal_Char* pStrmName,
                                        SotStorageRef& rStg)
{
    if( rpLst )
        rpLst->DeleteAndDestroy( 0, rpLst->Count() );
    else
        rpLst = new SvStringsISortDtor( 16, 16 );

    {
        String sStrmName( pStrmName, RTL_TEXTENCODING_MS_1252 );
        String sTmp( sStrmName );

        if( rStg.Is() && rStg->IsStream( sStrmName ) )
        {
            SvStorageStreamRef xStrm = rStg->OpenSotStream( sTmp,
                ( STREAM_READ | STREAM_SHARE_DENYWRITE | STREAM_NOCREATE ) );
            if( SVSTREAM_OK != xStrm->GetError())
            {
                xStrm.Clear();
                rStg.Clear();
                RemoveStream_Imp( sStrmName );
            }
            else
            {
                uno::Reference< lang::XMultiServiceFactory > xServiceFactory =
                    comphelper::getProcessServiceFactory();
                DBG_ASSERT( xServiceFactory.is(),
                    "XMLReader::Read: got no service manager" );
                if( !xServiceFactory.is() )
                {
                    // Throw an exception ?
                }

                xml::sax::InputSource aParserInput;
                aParserInput.sSystemId = sStrmName;

                xStrm->Seek( 0L );
                xStrm->SetBufferSize( 8 * 1024 );
                aParserInput.aInputStream = new utl::OInputStreamWrapper( *xStrm );

                // get parser
                uno::Reference< XInterface > xXMLParser = xServiceFactory->createInstance(
                    OUString::createFromAscii("com.sun.star.xml.sax.Parser") );
                DBG_ASSERT( xXMLParser.is(),
                    "XMLReader::Read: com.sun.star.xml.sax.Parser service missing" );
                if( !xXMLParser.is() )
                {
                    // Maybe throw an exception?
                }

                // get filter
                // #110680#
                // uno::Reference< xml::sax::XDocumentHandler > xFilter = new SvXMLExceptionListImport ( *rpLst );
                uno::Reference< xml::sax::XDocumentHandler > xFilter = new SvXMLExceptionListImport ( xServiceFactory, *rpLst );

                // connect parser and filter
                uno::Reference< xml::sax::XParser > xParser( xXMLParser, UNO_QUERY );
                xParser->setDocumentHandler( xFilter );

                // parse
                try
                {
                    xParser->parseStream( aParserInput );
                }
                catch( xml::sax::SAXParseException&  )
                {
                    // re throw ?
                }
                catch( xml::sax::SAXException&  )
                {
                    // re throw ?
                }
                catch( io::IOException& )
                {
                    // re throw ?
                }
            }
        }

        // Zeitstempel noch setzen
        FStatHelper::GetModifiedDateTimeOfFile( sShareAutoCorrFile,
                                        &aModifiedDate, &aModifiedTime );
        aLastCheckTime = Time();
    }

}
/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
void SvxAutoCorrectLanguageLists::SaveExceptList_Imp(
                            const SvStringsISortDtor& rLst,
                            const sal_Char* pStrmName,
                            SotStorageRef &rStg,
                            BOOL bConvert )
{
    if( rStg.Is() )
    {
        String sStrmName( pStrmName, RTL_TEXTENCODING_MS_1252 );
        if( !rLst.Count() )
        {
            rStg->Remove( sStrmName );
            rStg->Commit();
        }
        else
        {
            SotStorageStreamRef xStrm = rStg->OpenSotStream( sStrmName,
                    ( STREAM_READ | STREAM_WRITE | STREAM_SHARE_DENYWRITE ) );
            if( xStrm.Is() )
            {
                xStrm->SetSize( 0 );
                xStrm->SetBufferSize( 8192 );
                String aPropName( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM("MediaType") ) );
                OUString aMime( RTL_CONSTASCII_USTRINGPARAM("text/xml") );
                uno::Any aAny;
                aAny <<= aMime;
                xStrm->SetProperty( aPropName, aAny );


                uno::Reference< lang::XMultiServiceFactory > xServiceFactory =
                    comphelper::getProcessServiceFactory();
                DBG_ASSERT( xServiceFactory.is(),
                            "XMLReader::Read: got no service manager" );
                if( !xServiceFactory.is() )
                {
                    // Throw an exception ?
                }

                    uno::Reference < XInterface > xWriter (xServiceFactory->createInstance(
                        OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.xml.sax.Writer"))));
                    DBG_ASSERT(xWriter.is(),"com.sun.star.xml.sax.Writer service missing");
                uno::Reference < io::XOutputStream> xOut = new utl::OOutputStreamWrapper( *xStrm );
                    uno::Reference<io::XActiveDataSource> xSrc(xWriter, uno::UNO_QUERY);
                    xSrc->setOutputStream(xOut);

                    uno::Reference<xml::sax::XDocumentHandler> xHandler(xWriter, uno::UNO_QUERY);

                // #110680#
                    // SvXMLExceptionListExport aExp(rLst, sStrmName, xHandler);
                    SvXMLExceptionListExport aExp( xServiceFactory, rLst, sStrmName, xHandler );

                aExp.exportDoc( XML_BLOCK_LIST );

                xStrm->Commit();
                if( xStrm->GetError() == SVSTREAM_OK )
                {
                    xStrm.Clear();
                    if (!bConvert)
                    {
                        rStg->Commit();
                        if( SVSTREAM_OK != rStg->GetError() )
                        {
                            rStg->Remove( sStrmName );
                            rStg->Commit();
                        }
                    }
                }
            }
        }
    }
}
/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
SvxAutocorrWordList* SvxAutoCorrectLanguageLists::LoadAutocorrWordList()
{
    if( pAutocorr_List )
        pAutocorr_List->DeleteAndDestroy( 0, pAutocorr_List->Count() );
    else
        pAutocorr_List = new SvxAutocorrWordList( 16, 16 );

    SvStringsDtor aRemoveArr;
    try
    {
        uno::Reference < embed::XStorage > xStg = comphelper::OStorageHelper::GetStorageFromURL( sShareAutoCorrFile, embed::ElementModes::READ );
        String aXMLWordListName( pXMLImplAutocorr_ListStr, RTL_TEXTENCODING_MS_1252 );
        uno::Reference < io::XStream > xStrm = xStg->openStreamElement( aXMLWordListName, embed::ElementModes::READ );
        uno::Reference< lang::XMultiServiceFactory > xServiceFactory = comphelper::getProcessServiceFactory();

        xml::sax::InputSource aParserInput;
        aParserInput.sSystemId = aXMLWordListName;
        aParserInput.aInputStream = xStrm->getInputStream();

        // get parser
        uno::Reference< XInterface > xXMLParser = xServiceFactory->createInstance( OUString::createFromAscii("com.sun.star.xml.sax.Parser") );
        DBG_ASSERT( xXMLParser.is(), "XMLReader::Read: com.sun.star.xml.sax.Parser service missing" );
        if( xXMLParser.is() )
        {
            uno::Reference< xml::sax::XDocumentHandler > xFilter = new SvXMLAutoCorrectImport( xServiceFactory, pAutocorr_List, rAutoCorrect, xStg );

            // connect parser and filter
            uno::Reference< xml::sax::XParser > xParser( xXMLParser, UNO_QUERY );
            xParser->setDocumentHandler( xFilter );

            // parse
            xParser->parseStream( aParserInput );
        }
    }
    catch ( uno::Exception& )
    {
    }

    // Zeitstempel noch setzen
    FStatHelper::GetModifiedDateTimeOfFile( sShareAutoCorrFile,
                                    &aModifiedDate, &aModifiedTime );
    aLastCheckTime = Time();

    return pAutocorr_List;
}

/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/

void SvxAutoCorrectLanguageLists::SetAutocorrWordList( SvxAutocorrWordList* pList )
{
    if( pAutocorr_List && pList != pAutocorr_List )
        delete pAutocorr_List;
    pAutocorr_List = pList;
    if( !pAutocorr_List )
    {
        DBG_ASSERT( !this, "keine gueltige Liste" );
        pAutocorr_List = new SvxAutocorrWordList( 16, 16 );
    }
    nFlags |= ChgWordLstLoad;
}

/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
const SvxAutocorrWordList* SvxAutoCorrectLanguageLists::GetAutocorrWordList()
{
    if( !( ChgWordLstLoad & nFlags ) || IsFileChanged_Imp() )
        SetAutocorrWordList( LoadAutocorrWordList() );
    return pAutocorr_List;
}
/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
SvStringsISortDtor* SvxAutoCorrectLanguageLists::GetCplSttExceptList()
{
    if( !( CplSttLstLoad & nFlags ) || IsFileChanged_Imp() )
        SetCplSttExceptList( LoadCplSttExceptList() );
    return pCplStt_ExcptLst;
}
/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
BOOL SvxAutoCorrectLanguageLists::AddToCplSttExceptList(const String& rNew)
{
    String* pNew = new String( rNew );
    if( rNew.Len() && GetCplSttExceptList()->Insert( pNew ) )
    {
        MakeUserStorage_Impl();
        SotStorageRef xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE, TRUE );

        SaveExceptList_Imp( *pCplStt_ExcptLst, pXMLImplCplStt_ExcptLstStr, xStg );

        xStg = 0;
        // Zeitstempel noch setzen
        FStatHelper::GetModifiedDateTimeOfFile( sUserAutoCorrFile,
                                            &aModifiedDate, &aModifiedTime );
        aLastCheckTime = Time();
    }
    else
        delete pNew, pNew = 0;
    return 0 != pNew;
}
/* -----------------18.11.98 15:20-------------------
 *
 * --------------------------------------------------*/
BOOL SvxAutoCorrectLanguageLists::AddToWrdSttExceptList(const String& rNew)
{
    String* pNew = new String( rNew );
    SvStringsISortDtor* pExceptList = LoadWrdSttExceptList();
    if( rNew.Len() && pExceptList && pExceptList->Insert( pNew ) )
    {
        MakeUserStorage_Impl();
        SotStorageRef xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE, TRUE );

        SaveExceptList_Imp( *pWrdStt_ExcptLst, pXMLImplWrdStt_ExcptLstStr, xStg );

        xStg = 0;
        // Zeitstempel noch setzen
        FStatHelper::GetModifiedDateTimeOfFile( sUserAutoCorrFile,
                                            &aModifiedDate, &aModifiedTime );
        aLastCheckTime = Time();
    }
    else
        delete pNew, pNew = 0;
    return 0 != pNew;
}

/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
SvStringsISortDtor* SvxAutoCorrectLanguageLists::LoadCplSttExceptList()
{
    SotStorageRef xStg = new SotStorage( sShareAutoCorrFile, STREAM_READ | STREAM_SHARE_DENYNONE, TRUE );
    String sTemp ( RTL_CONSTASCII_USTRINGPARAM ( pXMLImplCplStt_ExcptLstStr ) );
    if( xStg.Is() && xStg->IsContained( sTemp ) )
        LoadXMLExceptList_Imp( pCplStt_ExcptLst, pXMLImplCplStt_ExcptLstStr, xStg );

    return pCplStt_ExcptLst;
}

/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
void SvxAutoCorrectLanguageLists::SaveCplSttExceptList()
{
    MakeUserStorage_Impl();
    SotStorageRef xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE, TRUE );

    SaveExceptList_Imp( *pCplStt_ExcptLst, pXMLImplCplStt_ExcptLstStr, xStg );

    xStg = 0;

    // Zeitstempel noch setzen
    FStatHelper::GetModifiedDateTimeOfFile( sUserAutoCorrFile,
                                            &aModifiedDate, &aModifiedTime );
    aLastCheckTime = Time();
}

/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
void SvxAutoCorrectLanguageLists::SetCplSttExceptList( SvStringsISortDtor* pList )
{
    if( pCplStt_ExcptLst && pList != pCplStt_ExcptLst )
        delete pCplStt_ExcptLst;

    pCplStt_ExcptLst = pList;
    if( !pCplStt_ExcptLst )
    {
        DBG_ASSERT( !this, "keine gueltige Liste" );
        pCplStt_ExcptLst = new SvStringsISortDtor( 16, 16 );
    }
    nFlags |= CplSttLstLoad;
}
/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
SvStringsISortDtor* SvxAutoCorrectLanguageLists::LoadWrdSttExceptList()
{
    SotStorageRef xStg = new SotStorage( sShareAutoCorrFile, STREAM_READ | STREAM_SHARE_DENYNONE, TRUE );
    String sTemp ( RTL_CONSTASCII_USTRINGPARAM ( pXMLImplWrdStt_ExcptLstStr ) );
    if( xStg.Is() && xStg->IsContained( sTemp ) )
        LoadXMLExceptList_Imp( pWrdStt_ExcptLst, pXMLImplWrdStt_ExcptLstStr, xStg );
    return pWrdStt_ExcptLst;
}
/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
void SvxAutoCorrectLanguageLists::SaveWrdSttExceptList()
{
    MakeUserStorage_Impl();
    SotStorageRef xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE, TRUE );

    SaveExceptList_Imp( *pWrdStt_ExcptLst, pXMLImplWrdStt_ExcptLstStr, xStg );

    xStg = 0;
    // Zeitstempel noch setzen
    FStatHelper::GetModifiedDateTimeOfFile( sUserAutoCorrFile,
                                            &aModifiedDate, &aModifiedTime );
    aLastCheckTime = Time();
}
/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
void SvxAutoCorrectLanguageLists::SetWrdSttExceptList( SvStringsISortDtor* pList )
{
    if( pWrdStt_ExcptLst && pList != pWrdStt_ExcptLst )
        delete pWrdStt_ExcptLst;
    pWrdStt_ExcptLst = pList;
    if( !pWrdStt_ExcptLst )
    {
        DBG_ASSERT( !this, "keine gueltige Liste" );
        pWrdStt_ExcptLst = new SvStringsISortDtor( 16, 16 );
    }
    nFlags |= WrdSttLstLoad;
}
/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
SvStringsISortDtor* SvxAutoCorrectLanguageLists::GetWrdSttExceptList()
{
    if( !( WrdSttLstLoad & nFlags ) || IsFileChanged_Imp() )
        SetWrdSttExceptList( LoadWrdSttExceptList() );
    return pWrdStt_ExcptLst;
}
/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
void SvxAutoCorrectLanguageLists::RemoveStream_Imp( const String& rName )
{
    if( sShareAutoCorrFile != sUserAutoCorrFile )
    {
        SotStorageRef xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE, TRUE );
        if( xStg.Is() && SVSTREAM_OK == xStg->GetError() &&
            xStg->IsStream( rName ) )
        {
            xStg->Remove( rName );
            xStg->Commit();

            xStg = 0;
        }
    }
}

void SvxAutoCorrectLanguageLists::MakeUserStorage_Impl()
{
    // The conversion needs to happen if the file is already in the user
    // directory and is in the old format. Additionally it needs to
    // happen when the file is being copied from share to user.

    sal_Bool bError = sal_False, bConvert = sal_False, bCopy = sal_False;
    INetURLObject aDest;
    INetURLObject aSource;

//	String sDestPath = sUserAutoCorrFile.Copy ( 0, sUserAutoCorrFile.Len()-3);
//	sDestPath.AppendAscii ("bak");


    if (sUserAutoCorrFile != sShareAutoCorrFile )
    {
        aSource = INetURLObject ( sShareAutoCorrFile ); //aSource.setFSysPath ( sShareAutoCorrFile, INetURLObject::FSYS_DETECT );
        aDest = INetURLObject ( sUserAutoCorrFile );
        if ( SotStorage::IsOLEStorage ( sShareAutoCorrFile ) )
        {
            aDest.SetExtension ( String::CreateFromAscii ( "bak" ) );
            bConvert = sal_True;
        }
        bCopy = sal_True;
    }
    else if ( SotStorage::IsOLEStorage ( sUserAutoCorrFile ) )
    {
        aSource = INetURLObject ( sUserAutoCorrFile );
        aDest = INetURLObject ( sUserAutoCorrFile );
        aDest.SetExtension ( String::CreateFromAscii ( "bak" ) );
        bCopy = bConvert = sal_True;
    }
    if (bCopy)
    {
        try
        {
            String sMain(aDest.GetMainURL( INetURLObject::DECODE_TO_IURI ));
            sal_Unicode cSlash = '/';
            xub_StrLen nSlashPos = sMain.SearchBackward(cSlash);
            sMain.Erase(nSlashPos);
            ::ucbhelper::Content aNewContent(	sMain, uno::Reference< XCommandEnvironment > ());
            Any aAny;
            TransferInfo aInfo;
            aInfo.NameClash = NameClash::OVERWRITE;
            aInfo.NewTitle  = aDest.GetName();
            aInfo.SourceURL = aSource.GetMainURL( INetURLObject::DECODE_TO_IURI );
            aInfo.MoveData  = FALSE;
            aAny <<= aInfo;
            aNewContent.executeCommand( OUString ( RTL_CONSTASCII_USTRINGPARAM( "transfer" ) ), aAny);
        }
        catch (...)
        {
            bError = sal_True;
        }
    }
    if (bConvert && !bError)
    {
        SotStorageRef xSrcStg = new SotStorage( aDest.GetMainURL( INetURLObject::DECODE_TO_IURI ), STREAM_READ, TRUE );
        SotStorageRef xDstStg = new SotStorage( sUserAutoCorrFile, STREAM_WRITE, TRUE );

        if( xSrcStg.Is() && xDstStg.Is() )
        {
            String sWord 	    ( RTL_CONSTASCII_USTRINGPARAM ( pImplWrdStt_ExcptLstStr ) );
            String sSentence    ( RTL_CONSTASCII_USTRINGPARAM ( pImplCplStt_ExcptLstStr ) );
            String sXMLWord     ( RTL_CONSTASCII_USTRINGPARAM ( pXMLImplWrdStt_ExcptLstStr ) );
            String sXMLSentence ( RTL_CONSTASCII_USTRINGPARAM ( pXMLImplCplStt_ExcptLstStr ) );
            SvStringsISortDtor 	*pTmpWordList = NULL;

            if (xSrcStg->IsContained( sXMLWord ) )
                LoadXMLExceptList_Imp( pTmpWordList, pXMLImplWrdStt_ExcptLstStr, xSrcStg );

            if (pTmpWordList)
            {
                SaveExceptList_Imp( *pTmpWordList, pXMLImplWrdStt_ExcptLstStr, xDstStg, TRUE );
                pTmpWordList->DeleteAndDestroy( 0, pTmpWordList->Count() );
                pTmpWordList = NULL;
            }


            if (xSrcStg->IsContained( sXMLSentence ) )
                LoadXMLExceptList_Imp( pTmpWordList, pXMLImplCplStt_ExcptLstStr, xSrcStg );

            if (pTmpWordList)
            {
                SaveExceptList_Imp( *pTmpWordList, pXMLImplCplStt_ExcptLstStr, xDstStg, TRUE );
                pTmpWordList->DeleteAndDestroy( 0, pTmpWordList->Count() );
            }

            GetAutocorrWordList();
            MakeBlocklist_Imp( *xDstStg );
            // xDstStg is committed in MakeBlocklist_Imp
            /*xSrcStg->CopyTo( &xDstStg );*/
            sShareAutoCorrFile = sUserAutoCorrFile;
            xDstStg = 0;
            try
            {
                ::ucbhelper::Content aContent ( aDest.GetMainURL( INetURLObject::DECODE_TO_IURI ), uno::Reference < XCommandEnvironment > ());
                aContent.executeCommand ( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "delete" ) ), makeAny ( sal_Bool (sal_True ) ) );
            }
            catch (...)
            {
            }
        }
    }
    else if( bCopy && !bError )
        sShareAutoCorrFile = sUserAutoCorrFile;
}

/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
BOOL SvxAutoCorrectLanguageLists::MakeBlocklist_Imp( SvStorage& rStg )
{
    String sStrmName( pXMLImplAutocorr_ListStr, RTL_TEXTENCODING_MS_1252 );
    BOOL bRet = TRUE, bRemove = !pAutocorr_List || !pAutocorr_List->Count();
    if( !bRemove )
    {
        /*
        if ( rStg.IsContained( sStrmName) )
        {
            rStg.Remove ( sStrmName );
            rStg.Commit();
        }
        */
        SvStorageStreamRef refList = rStg.OpenSotStream( sStrmName,
                    ( STREAM_READ | STREAM_WRITE | STREAM_SHARE_DENYWRITE ) );
        if( refList.Is() )
        {
            refList->SetSize( 0 );
            refList->SetBufferSize( 8192 );
            String aPropName( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM("MediaType") ) );
            OUString aMime( RTL_CONSTASCII_USTRINGPARAM("text/xml") );
            uno::Any aAny;
            aAny <<= aMime;
            refList->SetProperty( aPropName, aAny );

            uno::Reference< lang::XMultiServiceFactory > xServiceFactory =
                comphelper::getProcessServiceFactory();
            DBG_ASSERT( xServiceFactory.is(),
                        "XMLReader::Read: got no service manager" );
            if( !xServiceFactory.is() )
            {
                // Throw an exception ?
            }

                uno::Reference < XInterface > xWriter (xServiceFactory->createInstance(
                    OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.xml.sax.Writer"))));
                DBG_ASSERT(xWriter.is(),"com.sun.star.xml.sax.Writer service missing");
            uno::Reference < io::XOutputStream> xOut = new utl::OOutputStreamWrapper( *refList );
                uno::Reference<io::XActiveDataSource> xSrc(xWriter, uno::UNO_QUERY);
                xSrc->setOutputStream(xOut);

                uno::Reference<xml::sax::XDocumentHandler> xHandler(xWriter, uno::UNO_QUERY);

            // #110680#
                // SvXMLAutoCorrectExport aExp(pAutocorr_List, sStrmName, xHandler);
                SvXMLAutoCorrectExport aExp( xServiceFactory, pAutocorr_List, sStrmName, xHandler );

            aExp.exportDoc( XML_BLOCK_LIST );

            refList->Commit();
            bRet = SVSTREAM_OK == refList->GetError();
            if( bRet )
            {
                refList.Clear();
                rStg.Commit();
                if( SVSTREAM_OK != rStg.GetError() )
                {
                    bRemove = TRUE;
                    bRet = FALSE;
                }
            }

            /*
            refList->SetSize( 0 );
            refList->SetBufferSize( 8192 );
            rtl_TextEncoding eEncoding = gsl_getSystemTextEncoding();

            String aDummy;				// Erkennungszeichen fuer neue Streams
            refList->WriteByteString( aDummy, RTL_TEXTENCODING_MS_1252 )
                     << (BYTE)	4		// Laenge des Headers (ohne den Leerstring)
                     << (USHORT)WORDLIST_VERSION_358	// Version des Streams
                     << (BYTE)eEncoding;				// der Zeichensatz

            for( USHORT i = 0; i < pAutocorr_List->Count() &&
                                SVSTREAM_OK == refList->GetError(); ++i )
            {
                SvxAutocorrWord* p = pAutocorr_List->GetObject( i );
                refList->WriteByteString( p->GetShort(), eEncoding ).
                        WriteByteString(  p->IsTextOnly()
                                            ? p->GetLong()
                                            : p->GetShort(), eEncoding );
            }
            refList->Commit();
            bRet = SVSTREAM_OK == refList->GetError();
            if( bRet )
            {
                refList.Clear();
                rStg.Commit();
                if( SVSTREAM_OK != rStg.GetError() )
                {
                    bRemove = TRUE;
                    bRet = FALSE;
                }
            }
            */
        }
        else
            bRet = FALSE;
    }

    if( bRemove )
    {
        rStg.Remove( sStrmName );
        rStg.Commit();
    }

    return bRet;
}

/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
BOOL SvxAutoCorrectLanguageLists::PutText( const String& rShort,
                                           const String& rLong )
{
    // erstmal akt. Liste besorgen!
    GetAutocorrWordList();

    MakeUserStorage_Impl();
    SotStorageRef xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE, TRUE );

    BOOL bRet = xStg.Is() && SVSTREAM_OK == xStg->GetError();

/*	if( bRet )
    {
        // PutText( *xStg, rShort );
    }
*/
    // die Wortliste aktualisieren
    if( bRet )
    {
        USHORT nPos;
        SvxAutocorrWord* pNew = new SvxAutocorrWord( rShort, rLong, TRUE );
        if( pAutocorr_List->Seek_Entry( pNew, &nPos ) )
        {
            if( !(*pAutocorr_List)[ nPos ]->IsTextOnly() )
            {
                // dann ist der Storage noch zu entfernen
                String sStgNm( rShort );
                if (xStg->IsOLEStorage())
                    EncryptBlockName_Imp( sStgNm );
                else
                    GeneratePackageName ( rShort, sStgNm);

                if( xStg->IsContained( sStgNm ) )
                    xStg->Remove( sStgNm );
            }
            pAutocorr_List->DeleteAndDestroy( nPos );
        }

        if( pAutocorr_List->Insert( pNew ) )
        {
            bRet = MakeBlocklist_Imp( *xStg );
            xStg = 0;
        }
        else
        {
            delete pNew;
            bRet = FALSE;
        }
    }
    return bRet;
}
/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
    //	- Text mit Attributierung (kann nur der SWG - SWG-Format!)
BOOL SvxAutoCorrectLanguageLists::PutText( const String& rShort,
                                        SfxObjectShell& rShell )
{
    // erstmal akt. Liste besorgen!
    GetAutocorrWordList();

    MakeUserStorage_Impl();

    BOOL bRet = FALSE;
    String sLong;
    try
    {
        uno::Reference < embed::XStorage > xStg = comphelper::OStorageHelper::GetStorageFromURL( sUserAutoCorrFile, embed::ElementModes::READWRITE );
//		String aName( rShort );
//		EncryptBlockName_Imp( aName );
//		bRet = PutText( *xStg, aName, rShell, sLong );
        bRet = rAutoCorrect.PutText( xStg, sUserAutoCorrFile, rShort, rShell, sLong );
        xStg = 0;

        // die Wortliste aktualisieren
        if( bRet )
        {
            SvxAutocorrWord* pNew = new SvxAutocorrWord( rShort, sLong, FALSE );
            if( pAutocorr_List->Insert( pNew ) )
            {
                SotStorageRef xStor = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE, TRUE );
                MakeBlocklist_Imp( *xStor );
            }
            else
                delete pNew;
        }
    }
    catch ( uno::Exception& )
    {
    }

    return bRet;
}

/* -----------------18.11.98 11:26-------------------
 *
 * --------------------------------------------------*/
    //	- loesche einen Eintrag
BOOL SvxAutoCorrectLanguageLists::DeleteText( const String& rShort )
{
    // erstmal akt. Liste besorgen!
    GetAutocorrWordList();

    MakeUserStorage_Impl();

    SotStorageRef xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE, TRUE );
    BOOL bRet = xStg.Is() && SVSTREAM_OK == xStg->GetError();
    if( bRet )
    {
        USHORT nPos;
        SvxAutocorrWord aTmp( rShort, rShort );
        if( pAutocorr_List->Seek_Entry( &aTmp, &nPos ) )
        {
            SvxAutocorrWord* pFnd = (*pAutocorr_List)[ nPos ];
            if( !pFnd->IsTextOnly() )
            {
                String aName( rShort );
                if (xStg->IsOLEStorage())
                    EncryptBlockName_Imp( aName );
                else
                    GeneratePackageName ( rShort, aName );
                if( xStg->IsContained( aName ) )
                {
                    xStg->Remove( aName );
                    bRet = xStg->Commit();
                }

            }
            // die Wortliste aktualisieren
            pAutocorr_List->DeleteAndDestroy( nPos );
            MakeBlocklist_Imp( *xStg );
            xStg = 0;
        }
        else
            bRet = FALSE;
    }
    return bRet;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
