/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: impedit4.cxx,v $
 * $Revision: 1.78.54.1 $
 *
 * 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_svx.hxx"

#include <eeng_pch.hxx>

#ifndef _MSGBOX_HXX //autogen
#include <vcl/msgbox.hxx>
#endif
#include <vcl/svapp.hxx>

#include <sfx2/srchitem.hxx>
#include <svx/lspcitem.hxx>
#include <svx/adjitem.hxx>
#include <svx/tstpitem.hxx>

#include <eertfpar.hxx>
#include <svx/editeng.hxx>
#include <impedit.hxx>
#include <svx/editview.hxx>
#include <eehtml.hxx>
#include <editobj2.hxx>
#include <i18npool/lang.h>

#include <editxml.hxx>


#include <svx/akrnitem.hxx>
#include <svx/cntritem.hxx>
#include <svx/colritem.hxx>
#include <svx/crsditem.hxx>
#include <svx/escpitem.hxx>
#include <svx/fhgtitem.hxx>
#include "fontitem.hxx"
#include <svx/kernitem.hxx>
#include <svx/lrspitem.hxx>
#include <svx/postitem.hxx>
#include <svx/shdditem.hxx>
#include <svx/udlnitem.hxx>
#include <svx/ulspitem.hxx>
#include <svx/wghtitem.hxx>
#include <svx/langitem.hxx>
#include <svx/charreliefitem.hxx>
#include <svx/frmdiritem.hxx>
#include <svx/emphitem.hxx>
#include <textconv.hxx>

#include <rtl/tencinfo.h>

#include <svtools/rtfout.hxx>

#ifndef SVX_LIGHT
#include <edtspell.hxx>
#endif
#include <svx/scripttypeitem.hxx>
#include <unolingu.hxx>
#include <sfx2/sfxuno.hxx>
#include <linguistic/lngprops.hxx>
#include <com/sun/star/linguistic2/XThesaurus.hpp>
#include <com/sun/star/linguistic2/XMeaning.hpp>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <unotools/transliterationwrapper.hxx>

#ifndef _TXTCMP_HXX //autogen
#include <unotools/textsearch.hxx>
#endif
#include <comphelper/processfactory.hxx>
#include <vcl/help.hxx>

#include <svtools/rtfkeywd.hxx>

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::linguistic2;

void SwapUSHORTs( sal_uInt16& rX, sal_uInt16& rY )
{
	sal_uInt16 n = rX;
	rX = rY;
	rY = n;
}

EditPaM ImpEditEngine::Read( SvStream& rInput, const String& rBaseURL, EETextFormat eFormat, EditSelection aSel, SvKeyValueIterator* pHTTPHeaderAttrs )
{
	sal_Bool _bUpdate = GetUpdateMode();
	SetUpdateMode( sal_False );
	EditPaM aPaM;
	if ( eFormat == EE_FORMAT_TEXT )
		aPaM = ReadText( rInput, aSel );
	else if ( eFormat == EE_FORMAT_RTF )
		aPaM = ReadRTF( rInput, aSel );
	else if ( eFormat == EE_FORMAT_XML )
		aPaM = ReadXML( rInput, aSel );
	else if ( eFormat == EE_FORMAT_HTML )
        aPaM = ReadHTML( rInput, rBaseURL, aSel, pHTTPHeaderAttrs );
	else if ( eFormat == EE_FORMAT_BIN)
		aPaM = ReadBin( rInput, aSel );
	else
	{
		DBG_ERROR( "Read: Unbekanntes Format" );
	}

	FormatFullDoc();		// reicht vielleicht auch ein einfaches Format?
	SetUpdateMode( _bUpdate );

	return aPaM;
}

EditPaM ImpEditEngine::ReadText( SvStream& rInput, EditSelection aSel )
{
	if ( aSel.HasRange() )
		aSel = ImpDeleteSelection( aSel );
	EditPaM aPaM = aSel.Max();

	XubString aTmpStr, aStr;
	sal_Bool bDone = rInput.ReadByteStringLine( aTmpStr );
	while ( bDone )
	{
		aTmpStr.Erase( MAXCHARSINPARA );
		aPaM = ImpInsertText( EditSelection( aPaM, aPaM ), aTmpStr );
		aPaM = ImpInsertParaBreak( aPaM );
		bDone = rInput.ReadByteStringLine( aTmpStr );
	}
	return aPaM;
}

EditPaM ImpEditEngine::ReadXML( SvStream& rInput, EditSelection aSel )
{
#ifndef SVX_LIGHT
	if ( aSel.HasRange() )
		aSel = ImpDeleteSelection( aSel );

    ESelection aESel = CreateESel( aSel );

    ::SvxReadXML( *GetEditEnginePtr(), rInput, aESel );

    return aSel.Max();
#else
	return EditPaM();
#endif
}

EditPaM ImpEditEngine::ReadRTF( SvStream& rInput, EditSelection aSel )
{
#ifndef SVX_LIGHT

#if defined (EDITDEBUG) && !defined( UNX )
	SvFileStream aRTFOut( String( RTL_CONSTASCII_USTRINGPARAM ( "d:\\rtf_in.rtf" ) ), STREAM_WRITE );
	aRTFOut << rInput;
	aRTFOut.Close();
	rInput.Seek( 0 );
#endif
	if ( aSel.HasRange() )
		aSel = ImpDeleteSelection( aSel );

//	sal_Bool bCharsBeforeInsertPos = ( aSel.Min().GetIndex() ) ? sal_True : sal_False;
//	sal_Bool bCharsBehindInsertPos = ( aSel.Min().GetIndex() < aSel.Min().GetNode()->Len() ) ? sal_True : sal_False;

	// Der SvRTF-Parser erwartet, dass das Which-Mapping am uebergebenen Pool,
	// nicht an einem Secondary haengt.
	SfxItemPool* pPool = &aEditDoc.GetItemPool();
	while ( pPool->GetSecondaryPool() && !pPool->GetName().EqualsAscii( "EditEngineItemPool" ) )
	{
		pPool = pPool->GetSecondaryPool();

	}
	DBG_ASSERT( pPool && pPool->GetName().EqualsAscii( "EditEngineItemPool" ), "ReadRTF: Kein EditEnginePool!" );

	EditRTFParserRef xPrsr = new EditRTFParser( rInput, aSel, *pPool, this );
	SvParserState eState = xPrsr->CallParser();
	if ( ( eState != SVPAR_ACCEPTED ) && ( !rInput.GetError() ) )
	{
		rInput.SetError( EE_READWRITE_WRONGFORMAT );
		return aSel.Min();
	}
	return xPrsr->GetCurPaM();
#else
	return EditPaM();
#endif
}

EditPaM ImpEditEngine::ReadHTML( SvStream& rInput, const String& rBaseURL, EditSelection aSel, SvKeyValueIterator* pHTTPHeaderAttrs )
{
#ifndef SVX_LIGHT

	if ( aSel.HasRange() )
		aSel = ImpDeleteSelection( aSel );

//	sal_Bool bCharsBeforeInsertPos = ( aSel.Min().GetIndex() ) ? sal_True : sal_False;
//	sal_Bool bCharsBehindInsertPos = ( aSel.Min().GetIndex() < aSel.Min().GetNode()->Len() ) ? sal_True : sal_False;

    EditHTMLParserRef xPrsr = new EditHTMLParser( rInput, rBaseURL, pHTTPHeaderAttrs );
	SvParserState eState = xPrsr->CallParser( this, aSel.Max() );
	if ( ( eState != SVPAR_ACCEPTED ) && ( !rInput.GetError() ) )
	{
		rInput.SetError( EE_READWRITE_WRONGFORMAT );
		return aSel.Min();
	}
	return xPrsr->GetCurSelection().Max();
#else
	return EditPaM();
#endif
}

EditPaM ImpEditEngine::ReadBin( SvStream& rInput, EditSelection aSel )
{
	// Einfach ein temporaeres TextObject missbrauchen...
	EditTextObject* pObj = EditTextObject::Create( rInput, NULL );

	EditPaM aLastPaM = aSel.Max();
	if ( pObj )
		aLastPaM = InsertText( *pObj, aSel ).Max();

	delete pObj;
	return aLastPaM;
}

#ifndef SVX_LIGHT
void ImpEditEngine::Write( SvStream& rOutput, EETextFormat eFormat, EditSelection aSel )
{
	if ( !rOutput.IsWritable() )
		rOutput.SetError( SVSTREAM_WRITE_ERROR );

	if ( !rOutput.GetError() )
	{
		if ( eFormat == EE_FORMAT_TEXT )
			WriteText( rOutput, aSel );
		else if ( eFormat == EE_FORMAT_RTF )
			WriteRTF( rOutput, aSel );
		else if ( eFormat == EE_FORMAT_XML )
			WriteXML( rOutput, aSel );
		else if ( eFormat == EE_FORMAT_HTML )
			WriteHTML( rOutput, aSel );
		else if ( eFormat == EE_FORMAT_BIN)
			WriteBin( rOutput, aSel );
		else
		{
			DBG_ERROR( "Write: Unbekanntes Format" );
		}
	}
}
#endif

sal_uInt32 ImpEditEngine::WriteText( SvStream& rOutput, EditSelection aSel )
{
	sal_uInt16 nStartNode, nEndNode;
	sal_Bool bRange = aSel.HasRange();
	if ( bRange )
	{
		aSel.Adjust( aEditDoc );
		nStartNode = aEditDoc.GetPos( aSel.Min().GetNode() );
		nEndNode = aEditDoc.GetPos( aSel.Max().GetNode() );
	}
	else
	{
		nStartNode = 0;
		nEndNode = aEditDoc.Count()-1;
	}

	// ueber die Absaetze iterieren...
	for ( sal_uInt16 nNode = nStartNode; nNode <= nEndNode; nNode++  )
	{
		ContentNode* pNode = aEditDoc.GetObject( nNode );
		DBG_ASSERT( pNode, "Node nicht gefunden: Search&Replace" );

		sal_uInt16 nStartPos = 0;
		sal_uInt16 nEndPos = pNode->Len();
		if ( bRange )
		{
			if ( nNode == nStartNode )
				nStartPos = aSel.Min().GetIndex();
			if ( nNode == nEndNode ) // kann auch == nStart sein!
				nEndPos = aSel.Max().GetIndex();
		}
		XubString aTmpStr = aEditDoc.GetParaAsString( pNode, nStartPos, nEndPos );
		rOutput.WriteByteStringLine( aTmpStr );
	}

	return rOutput.GetError();
}

sal_Bool ImpEditEngine::WriteItemListAsRTF( ItemList& rLst, SvStream& rOutput, sal_uInt16 nPara, sal_uInt16 nPos,
						SvxFontTable& rFontTable, SvxColorList& rColorList )
{
	const SfxPoolItem* pAttrItem = rLst.First();
	while ( pAttrItem )
	{
		WriteItemAsRTF( *pAttrItem, rOutput, nPara, nPos,rFontTable, rColorList );
		pAttrItem = rLst.Next();
	}
	return ( rLst.Count() ? sal_True : sal_False );
}

void lcl_FindValidAttribs( ItemList& rLst, ContentNode* pNode, sal_uInt16 nIndex, USHORT nScriptType )
{
	sal_uInt16 nAttr = 0;
	EditCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
	while ( pAttr && ( pAttr->GetStart() <= nIndex ) )
	{
		// Start wird in While ueberprueft...
		if ( pAttr->GetEnd() > nIndex )
        {
            if ( IsScriptItemValid( pAttr->GetItem()->Which(), nScriptType ) )
			    rLst.Insert( pAttr->GetItem(), LIST_APPEND );
		}
		nAttr++;
		pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
	}
}

sal_uInt32 ImpEditEngine::WriteBin( SvStream& rOutput, EditSelection aSel, BOOL bStoreUnicodeStrings ) const
{
	BinTextObject* pObj = (BinTextObject*)CreateBinTextObject( aSel, NULL );
	pObj->StoreUnicodeStrings( bStoreUnicodeStrings );
	pObj->Store( rOutput );
	delete pObj;
	return 0;
}

#ifndef SVX_LIGHT
sal_uInt32 ImpEditEngine::WriteXML( SvStream& rOutput, EditSelection aSel )
{
    ESelection aESel = CreateESel( aSel );

    SvxWriteXML( *GetEditEnginePtr(), rOutput, aESel );

    return 0;
}
#endif

static sal_uInt16 getStylePos( const SfxStyles& rStyles, SfxStyleSheet* pSheet )
{
	sal_uInt16 nNumber = 0;
	SfxStyles::const_iterator iter( rStyles.begin() );
	while( iter != rStyles.end() )
	{
		if( (*iter++).get() == pSheet )
			return nNumber;
		++nNumber;
	}
	return 0;
}

sal_uInt32 ImpEditEngine::WriteRTF( SvStream& rOutput, EditSelection aSel )
{
#ifndef SVX_LIGHT
	DBG_ASSERT( GetUpdateMode(), "WriteRTF bei UpdateMode = sal_False!" );
	CheckIdleFormatter();
	if ( !IsFormatted() )
		FormatDoc();

	sal_uInt16 nStartNode, nEndNode;
	aSel.Adjust( aEditDoc );

	nStartNode = aEditDoc.GetPos( aSel.Min().GetNode() );
	nEndNode = aEditDoc.GetPos( aSel.Max().GetNode() );

	// RTF-Vorspann...
	rOutput << '{' ;

	rOutput << sRTF_RTF;

	rOutput << sRTF_ANSI;
	rtl_TextEncoding eDestEnc = RTL_TEXTENCODING_MS_1252;

	// Fonttabelle erzeugen und rausschreiben...
	SvxFontTable aFontTable;
	// DefaultFont muss ganz vorne stehen, damit DEF-Font im RTF
	aFontTable.Insert( 0, new SvxFontItem( (const SvxFontItem&)aEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_FONTINFO ) ) );
	aFontTable.Insert( 1, new SvxFontItem( (const SvxFontItem&)aEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_FONTINFO_CJK ) ) );
	aFontTable.Insert( 2, new SvxFontItem( (const SvxFontItem&)aEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_FONTINFO_CTL ) ) );
    for ( USHORT nScriptType = 0; nScriptType < 3; nScriptType++ )
    {
        USHORT nWhich = EE_CHAR_FONTINFO;
        if ( nScriptType == 1 )
            nWhich = EE_CHAR_FONTINFO_CJK;
        else if ( nScriptType == 2 )
            nWhich = EE_CHAR_FONTINFO_CTL;

        sal_uInt16 i = 0;
	    SvxFontItem* pFontItem = (SvxFontItem*)aEditDoc.GetItemPool().GetItem( nWhich, i );
	    while ( pFontItem )
	    {
            bool bAlreadyExist = false;
            ULONG nTestMax = nScriptType ? aFontTable.Count() : 1;
            for ( ULONG nTest = 0; !bAlreadyExist && ( nTest < nTestMax ); nTest++ )
            {
                bAlreadyExist = *aFontTable.Get( nTest ) == *pFontItem;
            }

            if ( !bAlreadyExist )
		        aFontTable.Insert( aFontTable.Count(), new SvxFontItem( *pFontItem ) );

            pFontItem = (SvxFontItem*)aEditDoc.GetItemPool().GetItem( nWhich, ++i );
	    }
    }

	rOutput << endl << '{' << sRTF_FONTTBL;
	sal_uInt16 j;
	for ( j = 0; j < aFontTable.Count(); j++ )
	{
		SvxFontItem* pFontItem = aFontTable.Get( j );
		rOutput << '{';
		rOutput << sRTF_F;
		rOutput.WriteNumber( j );
		switch ( pFontItem->GetFamily()  )
		{
			case FAMILY_DONTKNOW:		rOutput << sRTF_FNIL;
										break;
			case FAMILY_DECORATIVE:		rOutput << sRTF_FDECOR;
										break;
			case FAMILY_MODERN:			rOutput << sRTF_FMODERN;
										break;
			case FAMILY_ROMAN:			rOutput << sRTF_FROMAN;
										break;
			case FAMILY_SCRIPT:			rOutput << sRTF_FSCRIPT;
										break;
			case FAMILY_SWISS:			rOutput << sRTF_FSWISS;
										break;
			default:
				break;
		}
		rOutput << sRTF_FPRQ;
		sal_uInt16 nVal = 0;
		switch( pFontItem->GetPitch() )
		{
			case PITCH_FIXED:		nVal = 1;		break;
			case PITCH_VARIABLE:	nVal = 2;		break;
			default:
				break;
		}
		rOutput.WriteNumber( nVal );

		CharSet eChrSet = pFontItem->GetCharSet();
		DBG_ASSERT( eChrSet != 9, "SystemCharSet?!" );
		if( RTL_TEXTENCODING_DONTKNOW == eChrSet )
			eChrSet = gsl_getSystemTextEncoding();
		rOutput << sRTF_FCHARSET;
		rOutput.WriteNumber( rtl_getBestWindowsCharsetFromTextEncoding( eChrSet ) );

		rOutput << ' ';
        RTFOutFuncs::Out_String( rOutput, pFontItem->GetFamilyName(), eDestEnc );
		rOutput << ";}";
	}
	rOutput << '}';
	rOutput << endl;

	// ColorList rausschreiben...
	SvxColorList aColorList;
	sal_uInt16 i = 0;
	SvxColorItem* pColorItem = (SvxColorItem*)aEditDoc.GetItemPool().GetItem( EE_CHAR_COLOR, i );
	while ( pColorItem )
	{
		USHORT nPos = i;
		if ( pColorItem->GetValue() == COL_AUTO )
			nPos = 0;
		aColorList.Insert( new SvxColorItem( *pColorItem ), nPos );
		pColorItem = (SvxColorItem*)aEditDoc.GetItemPool().GetItem( EE_CHAR_COLOR, ++i );
	}
	aColorList.Insert( new SvxColorItem( (const SvxColorItem&)aEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_COLOR) ), (sal_uInt32)i );

	rOutput << '{' << sRTF_COLORTBL;
	for ( j = 0; j < aColorList.Count(); j++ )
	{
		pColorItem = aColorList.GetObject( j );
		if ( !j || ( pColorItem->GetValue() != COL_AUTO ) )
		{
			rOutput << sRTF_RED;
			rOutput.WriteNumber( pColorItem->GetValue().GetRed() );
			rOutput << sRTF_GREEN;
			rOutput.WriteNumber( pColorItem->GetValue().GetGreen() );
			rOutput << sRTF_BLUE;
			rOutput.WriteNumber( pColorItem->GetValue().GetBlue() );
		}
		rOutput << ';';
	}
	rOutput << '}';
	rOutput << endl;

	// StyleSheets...
	if ( GetStyleSheetPool() )
	{
		sal_uInt16 nStyles = (sal_uInt16)GetStyleSheetPool()->GetStyles().size();
		if ( nStyles )
		{
			rOutput << '{' << sRTF_STYLESHEET;

			for ( sal_uInt16 nStyle = 0; nStyle < nStyles; nStyle++ )
			{

				SfxStyleSheet* pStyle = (SfxStyleSheet*)GetStyleSheetPool()->GetStyles()[ nStyle ].get();

				rOutput << endl << '{' << sRTF_S;
				sal_uInt16 nNumber = (sal_uInt16) (nStyle + 1);
				rOutput.WriteNumber( nNumber );

				// Attribute, auch aus Parent!
				for ( sal_uInt16 nParAttr = EE_PARA_START; nParAttr <= EE_CHAR_END; nParAttr++ )
				{
					if ( pStyle->GetItemSet().GetItemState( nParAttr ) == SFX_ITEM_ON )
					{
						const SfxPoolItem& rItem = pStyle->GetItemSet().Get( nParAttr );
						WriteItemAsRTF( rItem, rOutput, 0, 0, aFontTable, aColorList );
					}
				}

				// Parent...(nur wenn noetig)
				if ( pStyle->GetParent().Len() && ( pStyle->GetParent() != pStyle->GetName() ) )
				{
					SfxStyleSheet* pParent = (SfxStyleSheet*)GetStyleSheetPool()->Find( pStyle->GetParent(), pStyle->GetFamily() );
					DBG_ASSERT( pParent, "Parent nicht gefunden!" );
					rOutput << sRTF_SBASEDON;
					nNumber = (sal_uInt16) getStylePos( GetStyleSheetPool()->GetStyles(), pParent ) + 1;
					rOutput.WriteNumber( nNumber );
				}

				// Folgevorlage...(immer)
				SfxStyleSheet* pNext = pStyle;
				if ( pStyle->GetFollow().Len() && ( pStyle->GetFollow() != pStyle->GetName() ) )
					pNext = (SfxStyleSheet*)GetStyleSheetPool()->Find( pStyle->GetFollow(), pStyle->GetFamily() );

				DBG_ASSERT( pNext, "Naechsten nicht gefunden!" );
				rOutput << sRTF_SNEXT;
				nNumber = (sal_uInt16) getStylePos( GetStyleSheetPool()->GetStyles(), pNext ) + 1;
				rOutput.WriteNumber( nNumber );

				// Namen der Vorlage...
				rOutput << " " << ByteString( pStyle->GetName(), eDestEnc ).GetBuffer();
				rOutput << ";}";
			}
			rOutput << '}';
			rOutput << endl;
		}
	}

	// Die Pool-Defaults vorweg schreiben...
	rOutput << '{' << sRTF_IGNORE << "\\EditEnginePoolDefaults";
	for ( sal_uInt16 nPoolDefItem = EE_PARA_START; nPoolDefItem <= EE_CHAR_END; nPoolDefItem++)
	{
		const SfxPoolItem& rItem = aEditDoc.GetItemPool().GetDefaultItem( nPoolDefItem );
		WriteItemAsRTF( rItem, rOutput, 0, 0, aFontTable, aColorList );
	}
	rOutput << '}' << endl;

	// Def-Hoehe vorweg, da sonst 12Pt
	// Doch nicht, onst in jedem Absatz hart!
	// SfxItemSet aTmpSet( GetEmptyItemSet() );
	// const SvxFontHeightItem& rDefFontHeight = (const SvxFontHeightItem&)aTmpSet.Get( EE_CHAR_FONTHEIGHT );
	// WriteItemAsRTF( rDefFontHeight, rOutput, aFontTable, aColorList );
	// rOutput << '{' << sRTF_IGNORE << "\\EditEnginePoolDefaultHeight}" << endl;

	// DefTab:
	MapMode aTwpMode( MAP_TWIP );
	sal_uInt16 nDefTabTwps = (sal_uInt16) GetRefDevice()->LogicToLogic(
										Point( aEditDoc.GetDefTab(), 0 ),
										&GetRefMapMode(), &aTwpMode ).X();
	rOutput << sRTF_DEFTAB;
	rOutput.WriteNumber( nDefTabTwps );
	rOutput << endl;

	// ueber die Absaetze iterieren...
	rOutput << '{' << endl;
	for ( sal_uInt16 nNode = nStartNode; nNode <= nEndNode; nNode++  )
	{
		ContentNode* pNode = aEditDoc.SaveGetObject( nNode );
		DBG_ASSERT( pNode, "Node nicht gefunden: Search&Replace" );

		// Die Absatzattribute vorweg...
		sal_Bool bAttr = sal_False;

		// Vorlage ?
		if ( pNode->GetStyleSheet() )
		{
			// Nummer der Vorlage
			rOutput << sRTF_S;
			sal_uInt16 nNumber = (sal_uInt16) getStylePos( GetStyleSheetPool()->GetStyles(), pNode->GetStyleSheet() ) + 1;
			rOutput.WriteNumber( nNumber );

			// Alle Attribute
			// Attribute, auch aus Parent!
			for ( sal_uInt16 nParAttr = EE_PARA_START; nParAttr <= EE_CHAR_END; nParAttr++ )
			{
				if ( pNode->GetStyleSheet()->GetItemSet().GetItemState( nParAttr ) == SFX_ITEM_ON )
				{
					const SfxPoolItem& rItem = pNode->GetStyleSheet()->GetItemSet().Get( nParAttr );
					WriteItemAsRTF( rItem, rOutput, nNode, 0, aFontTable, aColorList );
					bAttr = sal_True;
				}
			}
		}

		for ( sal_uInt16 nParAttr = EE_PARA_START; nParAttr <= EE_CHAR_END; nParAttr++ )
		{
//			const SfxPoolItem& rItem = pNode->GetContentAttribs().GetItem( nParAttr );
			// Jetzt, wo StyleSheet-Verarbeitung, nur noch harte Absatzattribute!
			if ( pNode->GetContentAttribs().GetItems().GetItemState( nParAttr ) == SFX_ITEM_ON )
			{
				const SfxPoolItem& rItem = pNode->GetContentAttribs().GetItems().Get( nParAttr );
				WriteItemAsRTF( rItem, rOutput, nNode, 0, aFontTable, aColorList );
				bAttr = sal_True;
			}
		}
		if ( bAttr )
			rOutput << ' ';	// Separator

		ItemList aAttribItems;
		ParaPortion* pParaPortion = FindParaPortion( pNode );
		DBG_ASSERT( pParaPortion, "Portion nicht gefunden: WriteRTF" );

		sal_uInt16 nIndex = 0;
		sal_uInt16 nStartPos = 0;
		sal_uInt16 nEndPos = pNode->Len();
		sal_uInt16 nStartPortion = 0;
		sal_uInt16 nEndPortion = (sal_uInt16)pParaPortion->GetTextPortions().Count() - 1;
		sal_Bool bFinishPortion = sal_False;
		sal_uInt16 nPortionStart;

		if ( nNode == nStartNode )
		{
			nStartPos = aSel.Min().GetIndex();
			nStartPortion = pParaPortion->GetTextPortions().FindPortion( nStartPos, nPortionStart );
			if ( nStartPos != 0 )
			{
				aAttribItems.Clear();
                lcl_FindValidAttribs( aAttribItems, pNode, nStartPos, GetScriptType( EditPaM( pNode, 0 ) ) );
				if ( aAttribItems.Count() )
				{
					// Diese Attribute duerfen nicht fuer den gesamten
					// Absatz gelten:
					rOutput << '{';
					WriteItemListAsRTF( aAttribItems, rOutput, nNode, nStartPos, aFontTable, aColorList );
					bFinishPortion = sal_True;
				}
				aAttribItems.Clear();
			}
		}
		if ( nNode == nEndNode ) // kann auch == nStart sein!
		{
			nEndPos = aSel.Max().GetIndex();
			nEndPortion = pParaPortion->GetTextPortions().FindPortion( nEndPos, nPortionStart );
		}

		EditCharAttrib* pNextFeature = pNode->GetCharAttribs().FindFeature( nIndex );
		// Bei 0 anfangen, damit der Index richtig ist...

		for ( sal_uInt16 n = 0; n <= nEndPortion; n++ )
		{
			TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject(n);
			if ( n < nStartPortion )
			{
				nIndex = nIndex + pTextPortion->GetLen();
				continue;
			}

			if ( pNextFeature && ( pNextFeature->GetStart() == nIndex ) && ( pNextFeature->GetItem()->Which() != EE_FEATURE_FIELD ) )
			{
				WriteItemAsRTF( *pNextFeature->GetItem(), rOutput, nNode, nIndex, aFontTable, aColorList );
				pNextFeature = pNode->GetCharAttribs().FindFeature( pNextFeature->GetStart() + 1 );
			}
			else
			{
				aAttribItems.Clear();
                USHORT nScriptType = GetScriptType( EditPaM( pNode, nIndex+1 ) );
                if ( !n || IsScriptChange( EditPaM( pNode, nIndex ) ) )
                {
                    SfxItemSet aAttribs = GetAttribs( nNode, nIndex+1, nIndex+1 );
                    aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_FONTINFO, nScriptType ) ), LIST_APPEND );
                    aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_FONTHEIGHT, nScriptType ) ), LIST_APPEND );
                    aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_WEIGHT, nScriptType ) ), LIST_APPEND );
                    aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_ITALIC, nScriptType ) ), LIST_APPEND );
                    aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType ) ), LIST_APPEND );
                }
                // #96298# Insert hard attribs AFTER CJK attribs...
				lcl_FindValidAttribs( aAttribItems, pNode, nIndex, nScriptType );

				rOutput << '{';
				if ( WriteItemListAsRTF( aAttribItems, rOutput, nNode, nIndex, aFontTable, aColorList ) )
					rOutput << ' ';

				USHORT nS = nIndex;
				USHORT nE = nIndex + pTextPortion->GetLen();
				if ( n == nStartPortion )
					nS = nStartPos;
				if ( n == nEndPortion )
					nE = nEndPos;

				XubString aRTFStr = aEditDoc.GetParaAsString( pNode, nS, nE);
				RTFOutFuncs::Out_String( rOutput, aRTFStr, eDestEnc );
				rOutput << '}';
			}
			if ( bFinishPortion )
			{
				rOutput << '}';
				bFinishPortion = sal_False;
			}

			nIndex = nIndex + pTextPortion->GetLen();
		}

		rOutput << sRTF_PAR << sRTF_PARD << sRTF_PLAIN;;
		rOutput << endl;
	}
	// RTF-Nachspann...
	rOutput << "}}";	// 1xKlammerung Absaetze, 1x Klammerung RTF-Dokument
	rOutput.Flush();

#if defined (EDITDEBUG) && !defined( UNX )
    {
        SvFileStream aStream( String( RTL_CONSTASCII_USTRINGPARAM ( "d:\\rtf_out.rtf" ) ), STREAM_WRITE|STREAM_TRUNC );
        ULONG nP = rOutput.Tell();
        rOutput.Seek( 0 );
        aStream << rOutput;
        rOutput.Seek( nP );
    }
#endif

	return rOutput.GetError();
#else
	return 0;
#endif
}


void ImpEditEngine::WriteItemAsRTF( const SfxPoolItem& rItem, SvStream& rOutput, sal_uInt16 nPara, sal_uInt16 nPos,
							SvxFontTable& rFontTable, SvxColorList& rColorList )
{
	sal_uInt16 nWhich = rItem.Which();
	switch ( nWhich )
	{
        case EE_PARA_WRITINGDIR:
        {
            const SvxFrameDirectionItem& rWritingMode = (const SvxFrameDirectionItem&)rItem;
            if ( rWritingMode.GetValue() == FRMDIR_HORI_RIGHT_TOP )
                rOutput << "\\rtlpar";
            else
                rOutput << "\\ltrpar";
        }
        break;
		case EE_PARA_OUTLLEVEL:
		{
			sal_Int16 nLevel = ((const SfxInt16Item&)rItem).GetValue();
			if( nLevel >= 0 )
			{
				rOutput << "\\level";
				rOutput.WriteNumber( nLevel );
			}
		}
		break;
		case EE_PARA_OUTLLRSPACE:
		case EE_PARA_LRSPACE:
		{
//            const ContentNode *pNode = aEditDoc.GetObject( nPara );

			rOutput << sRTF_FI;
			short nTxtFirst = ((const SvxLRSpaceItem&)rItem).GetTxtFirstLineOfst();
			nTxtFirst = (short)LogicToTwips( nTxtFirst );
			rOutput.WriteNumber( nTxtFirst );
			rOutput << sRTF_LI;
            sal_uInt16 nTxtLeft = static_cast< sal_uInt16 >(((const SvxLRSpaceItem&)rItem).GetTxtLeft());
			nTxtLeft = (sal_uInt16)LogicToTwips( nTxtLeft );
			rOutput.WriteNumber( nTxtLeft );
			rOutput << sRTF_RI;
			sal_uInt32 nTxtRight = ((const SvxLRSpaceItem&)rItem).GetRight();
			nTxtRight = LogicToTwips( nTxtRight);
			rOutput.WriteNumber( nTxtRight );
		}
		break;
		case EE_PARA_ULSPACE:
		{
			rOutput << sRTF_SB;
			sal_uInt16 nUpper = ((const SvxULSpaceItem&)rItem).GetUpper();
			nUpper = (sal_uInt16)LogicToTwips( nUpper );
			rOutput.WriteNumber( nUpper );
			rOutput << sRTF_SA;
			sal_uInt16 nLower = ((const SvxULSpaceItem&)rItem).GetLower();
			nLower = (sal_uInt16)LogicToTwips( nLower );
			rOutput.WriteNumber( nLower );
		}
		break;
		case EE_PARA_SBL:
		{
			rOutput << sRTF_SL;
			long nVal = ((const SvxLineSpacingItem&)rItem).GetLineHeight();
            char cMult = '0';
			if ( ((const SvxLineSpacingItem&)rItem).GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP )
			{
				// Woher kriege ich jetzt den Wert?
				// Der SwRTF-Parser geht von einem 240er Font aus!
				nVal = ((const SvxLineSpacingItem&)rItem).GetPropLineSpace();
				nVal *= 240;
				nVal /= 100;
                cMult = '1';
			}
			rOutput.WriteNumber( nVal );
            rOutput << sRTF_SLMULT << cMult;
		}
		break;
		case EE_PARA_JUST:
		{
			SvxAdjust eJustification = ((const SvxAdjustItem&)rItem).GetAdjust();
			switch ( eJustification )
			{
				case SVX_ADJUST_CENTER:	rOutput << sRTF_QC;
										break;
				case SVX_ADJUST_RIGHT:	rOutput << sRTF_QR;
										break;
				default:				rOutput << sRTF_QL;
										break;
			}
		}
		break;
		case EE_PARA_TABS:
		{
			const SvxTabStopItem& rTabs = (const SvxTabStopItem&) rItem;
			for ( sal_uInt16 i = 0; i < rTabs.Count(); i++ )
			{
				const SvxTabStop& rTab = rTabs[i];
				rOutput << sRTF_TX;
				rOutput.WriteNumber( LogicToTwips( rTab.GetTabPos() ) );
			}
		}
		break;
		case EE_CHAR_COLOR:
		{
			sal_uInt32 n = rColorList.GetId( (const SvxColorItem&)rItem );
			rOutput << sRTF_CF;
			rOutput.WriteNumber( n );
		}
		break;
		case EE_CHAR_FONTINFO:
		case EE_CHAR_FONTINFO_CJK:
		case EE_CHAR_FONTINFO_CTL:
		{
			sal_uInt32 n = rFontTable.GetId( (const SvxFontItem&)rItem );
			rOutput << sRTF_F;
			rOutput.WriteNumber( n );
		}
		break;
		case EE_CHAR_FONTHEIGHT:
		case EE_CHAR_FONTHEIGHT_CJK:
		case EE_CHAR_FONTHEIGHT_CTL:
		{
			rOutput << sRTF_FS;
			long nHeight = ((const SvxFontHeightItem&)rItem).GetHeight();
			nHeight = LogicToTwips( nHeight );
			// Twips => HalfPoints
			nHeight /= 10;
			rOutput.WriteNumber( nHeight );
		}
		break;
		case EE_CHAR_WEIGHT:
		case EE_CHAR_WEIGHT_CJK:
		case EE_CHAR_WEIGHT_CTL:
		{
			FontWeight e = ((const SvxWeightItem&)rItem).GetWeight();
			switch ( e )
			{
				case WEIGHT_BOLD:	rOutput << sRTF_B;				break;
				default:			rOutput << sRTF_B << '0';		break;
			}
		}
		break;
		case EE_CHAR_UNDERLINE:
		{
			// muesste bei WordLineMode ggf. ulw werden,
			// aber die Information fehlt hier
			FontUnderline e = ((const SvxUnderlineItem&)rItem).GetLineStyle();
			switch ( e )
			{
				case UNDERLINE_NONE:	rOutput << sRTF_ULNONE;		break;
				case UNDERLINE_SINGLE:	rOutput << sRTF_UL; 		break;
				case UNDERLINE_DOUBLE:	rOutput << sRTF_ULDB;		break;
				case UNDERLINE_DOTTED:	rOutput << sRTF_ULD;		break;
				default:
					break;
			}
		}
		break;
		case EE_CHAR_OVERLINE:
		{
			FontUnderline e = ((const SvxOverlineItem&)rItem).GetLineStyle();
			switch ( e )
			{
				case UNDERLINE_NONE:	rOutput << sRTF_OLNONE;		break;
				case UNDERLINE_SINGLE:	rOutput << sRTF_OL; 		break;
				case UNDERLINE_DOUBLE:	rOutput << sRTF_OLDB;		break;
				case UNDERLINE_DOTTED:	rOutput << sRTF_OLD;		break;
				default:
					break;
			}
		}
		break;
		case EE_CHAR_STRIKEOUT:
		{
			FontStrikeout e = ((const SvxCrossedOutItem&)rItem).GetStrikeout();
			switch ( e )
			{
				case STRIKEOUT_SINGLE:
				case STRIKEOUT_DOUBLE:	rOutput << sRTF_STRIKE; 		break;
				case STRIKEOUT_NONE:	rOutput << sRTF_STRIKE << '0';	break;
				default:
					break;
			}
		}
		break;
		case EE_CHAR_ITALIC:
		case EE_CHAR_ITALIC_CJK:
		case EE_CHAR_ITALIC_CTL:
		{
			FontItalic e = ((const SvxPostureItem&)rItem).GetPosture();
			switch ( e )
			{
				case ITALIC_OBLIQUE:
				case ITALIC_NORMAL:	rOutput << sRTF_I; 		break;
				case ITALIC_NONE:	rOutput << sRTF_I << '0';	break;
				default:
					break;
			}
		}
		break;
		case EE_CHAR_OUTLINE:
		{
			rOutput << sRTF_OUTL;
			if ( ((const SvxContourItem&)rItem).GetValue() == 0 )
				rOutput << '0';
		}
		break;
		case EE_CHAR_RELIEF:
		{
			USHORT nRelief = ((const SvxCharReliefItem&)rItem).GetValue();
			if ( nRelief == RELIEF_EMBOSSED )
				rOutput << sRTF_EMBO;
			if ( nRelief == RELIEF_ENGRAVED )
				rOutput << sRTF_IMPR;
		}
		break;
		case EE_CHAR_EMPHASISMARK:
		{
			USHORT nMark = ((const SvxEmphasisMarkItem&)rItem).GetValue();
			if ( nMark == EMPHASISMARK_NONE )
				rOutput << sRTF_ACCNONE;
			else if ( nMark == EMPHASISMARK_SIDE_DOTS )
				rOutput << sRTF_ACCCOMMA;
			else
				rOutput << sRTF_ACCDOT;
		}
		break;
		case EE_CHAR_SHADOW:
		{
			rOutput << sRTF_SHAD;
			if ( ((const SvxShadowedItem&)rItem).GetValue() == 0 )
				rOutput << '0';
		}
		break;
		case EE_FEATURE_TAB:
		{
			rOutput << sRTF_TAB;
		}
		break;
		case EE_FEATURE_LINEBR:
		{
			rOutput << sRTF_SL;
		}
		break;
		case EE_CHAR_KERNING:
		{
			rOutput << sRTF_EXPNDTW;
			rOutput.WriteNumber( LogicToTwips(
				((const SvxKerningItem&)rItem).GetValue() ) );
		}
		break;
		case EE_CHAR_PAIRKERNING:
		{
			rOutput << sRTF_KERNING;
			rOutput.WriteNumber( ((const SvxAutoKernItem&)rItem).GetValue() ? 1 : 0 );
		}
		break;
		case EE_CHAR_ESCAPEMENT:
		{
			SvxFont aFont;
			ContentNode* pNode = aEditDoc.GetObject( nPara );
			SeekCursor( pNode, nPos, aFont );
			MapMode aPntMode( MAP_POINT );
			long nFontHeight = GetRefDevice()->LogicToLogic(
					aFont.GetSize(), &GetRefMapMode(), &aPntMode ).Height();
			nFontHeight *=2;	// HalfPoints
			sal_uInt16 nProp = ((const SvxEscapementItem&)rItem).GetProp();
			sal_uInt16 nProp100 = nProp*100;	// Fuer SWG-Token Prop in 100tel Prozent.
			short nEsc = ((const SvxEscapementItem&)rItem).GetEsc();
			if ( nEsc == DFLT_ESC_AUTO_SUPER )
			{
				nEsc = 100 - nProp;
				nProp100++;	// Eine 1 hinten bedeutet 'automatisch'.
			}
			else if ( nEsc == DFLT_ESC_AUTO_SUB )
			{
				nEsc = sal::static_int_cast< short >( -( 100 - nProp ) );
				nProp100++;
			}
			// SWG:
			if ( nEsc )
				rOutput << "{\\*\\updnprop" << ByteString::CreateFromInt32( nProp100 ).GetBuffer() << '}';
			long nUpDown = nFontHeight * Abs( nEsc ) / 100;
			ByteString aUpDown = ByteString::CreateFromInt32( nUpDown );
			if ( nEsc < 0 )
				rOutput << sRTF_DN << aUpDown.GetBuffer();
			else if ( nEsc > 0 )
				rOutput << sRTF_UP << aUpDown.GetBuffer();
		}
		break;
	}
}

sal_uInt32 ImpEditEngine::WriteHTML( SvStream&, EditSelection )
{
	return 0;
}


EditTextObject*	ImpEditEngine::CreateTextObject()
{
	EditSelection aCompleteSelection;
	aCompleteSelection.Min() = aEditDoc.GetStartPaM();
	aCompleteSelection.Max() = aEditDoc.GetEndPaM();

	return CreateTextObject( aCompleteSelection );
}

EditTextObject*	ImpEditEngine::CreateTextObject( EditSelection aSel )
{
	return CreateBinTextObject( aSel, GetEditTextObjectPool(), aStatus.AllowBigObjects(), nBigTextObjectStart );
}

EditTextObject*	ImpEditEngine::CreateBinTextObject( EditSelection aSel, SfxItemPool* pPool, sal_Bool bAllowBigObjects, sal_uInt16 nBigObjectStart ) const
{
	BinTextObject* pTxtObj = new BinTextObject( pPool );
	pTxtObj->SetVertical( IsVertical() );
	MapUnit eMapUnit = (MapUnit)aEditDoc.GetItemPool().GetMetric( DEF_METRIC );
	pTxtObj->SetMetric( (sal_uInt16) eMapUnit );
	if ( pTxtObj->IsOwnerOfPool() )
		pTxtObj->GetPool()->SetDefaultMetric( (SfxMapUnit) eMapUnit );

	sal_uInt16 nStartNode, nEndNode;
	sal_uInt32 nTextPortions = 0;

	aSel.Adjust( aEditDoc );
	nStartNode = aEditDoc.GetPos( aSel.Min().GetNode() );
	nEndNode = aEditDoc.GetPos( aSel.Max().GetNode() );

	sal_Bool bOnlyFullParagraphs = ( aSel.Min().GetIndex() ||
		( aSel.Max().GetIndex() < aSel.Max().GetNode()->Len() ) ) ?
			sal_False : sal_True;

	// Vorlagen werden nicht gespeichert!
	// ( Nur Name und Familie, Vorlage selbst muss in App stehen! )

	pTxtObj->SetScriptType( GetScriptType( aSel ) );

	// ueber die Absaetze iterieren...
	sal_uInt16 nNode;
	for ( nNode = nStartNode; nNode <= nEndNode; nNode++  )
	{
		ContentNode* pNode = aEditDoc.SaveGetObject( nNode );
		DBG_ASSERT( pNode, "Node nicht gefunden: Search&Replace" );

		if ( bOnlyFullParagraphs )
		{
			ParaPortion* pParaPortion = GetParaPortions()[nNode];
			nTextPortions += pParaPortion->GetTextPortions().Count();
		}

		sal_uInt16 nStartPos = 0;
		sal_uInt16 nEndPos = pNode->Len();

		sal_Bool bEmptyPara = nEndPos ? sal_False : sal_True;

		if ( ( nNode == nStartNode ) && !bOnlyFullParagraphs )
			nStartPos = aSel.Min().GetIndex();
		if ( ( nNode == nEndNode ) && !bOnlyFullParagraphs )
			nEndPos = aSel.Max().GetIndex();


		ContentInfo* pC = pTxtObj->CreateAndInsertContent();

		// Die Absatzattribute...
		pC->GetParaAttribs().Set( pNode->GetContentAttribs().GetItems() );

		// Das StyleSheet...
		if ( pNode->GetStyleSheet() )
		{
			pC->GetStyle() = pNode->GetStyleSheet()->GetName();
			pC->GetFamily() = pNode->GetStyleSheet()->GetFamily();
		}

		// Der Text...
		pC->GetText() = pNode->Copy( nStartPos, nEndPos-nStartPos );

		// und die Attribute...
		sal_uInt16 nAttr = 0;
		EditCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
		while ( pAttr )
		{
			// In einem leeren Absatz die Attribute behalten!
			if ( bEmptyPara ||
				 ( ( pAttr->GetEnd() > nStartPos ) && ( pAttr->GetStart() < nEndPos ) ) )
			{
				XEditAttribute* pX = pTxtObj->CreateAttrib( *pAttr->GetItem(), pAttr->GetStart(), pAttr->GetEnd() );
				// Evtl. korrigieren...
				if ( ( nNode == nStartNode ) && ( nStartPos != 0 ) )
				{
					pX->GetStart() = ( pX->GetStart() > nStartPos ) ? pX->GetStart()-nStartPos : 0;
					pX->GetEnd() = pX->GetEnd() - nStartPos;

				}
				if ( nNode == nEndNode )
				{
					if ( pX->GetEnd() > (nEndPos-nStartPos) )
						pX->GetEnd() = nEndPos-nStartPos;
				}
				DBG_ASSERT( pX->GetEnd() <= (nEndPos-nStartPos), "CreateBinTextObject: Attribut zu lang!" );
				if ( !pX->GetLen() && !bEmptyPara )
					pTxtObj->DestroyAttrib( pX );
				else
					pC->GetAttribs().Insert( pX, pC->GetAttribs().Count() );
			}
			nAttr++;
			pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
		}

#ifndef SVX_LIGHT
		// ggf. Online-Spelling
		if ( bAllowBigObjects && bOnlyFullParagraphs && pNode->GetWrongList() )
			pC->SetWrongList( pNode->GetWrongList()->Clone() );
#endif // !SVX_LIGHT

	}

	// Bei grossen Textobjekten die PortionInfos merken:
	// Schwelle rauf setzen, wenn Olli die Absaetze nicht mehr zerhackt!
	if ( bAllowBigObjects && bOnlyFullParagraphs && IsFormatted() && GetUpdateMode() && ( nTextPortions >= nBigObjectStart ) )
	{
		XParaPortionList* pXList = new XParaPortionList( GetRefDevice(), aPaperSize.Width() );
		pTxtObj->SetPortionInfo( pXList );
		for ( nNode = nStartNode; nNode <= nEndNode; nNode++  )
		{
			ParaPortion* pParaPortion = GetParaPortions()[nNode];
			XParaPortion* pX = new XParaPortion;
			pXList->Insert( pX, pXList->Count() );

			pX->nHeight = pParaPortion->GetHeight();
			pX->nFirstLineOffset = pParaPortion->GetFirstLineOffset();

			// Die TextPortions
			sal_uInt16 nCount = pParaPortion->GetTextPortions().Count();
			sal_uInt16 n;
			for ( n = 0; n < nCount; n++ )
			{
				TextPortion* pTextPortion = pParaPortion->GetTextPortions()[n];
				TextPortion* pNew = new TextPortion( *pTextPortion );
				pX->aTextPortions.Insert( pNew, pX->aTextPortions.Count() );
			}

			// Die Zeilen
			nCount = pParaPortion->GetLines().Count();
			for ( n = 0; n < nCount; n++ )
			{
				EditLine* pLine = pParaPortion->GetLines()[n];
				EditLine* pNew = pLine->Clone();
				pX->aLines.Insert( pNew, pX->aLines.Count() );
			}
#ifdef DBG_UTIL
			USHORT nTest;
            int nTPLen = 0, nTxtLen = 0;
			for ( nTest = pParaPortion->GetTextPortions().Count(); nTest; )
				nTPLen += pParaPortion->GetTextPortions().GetObject( --nTest )->GetLen();
			for ( nTest = pParaPortion->GetLines().Count(); nTest; )
				nTxtLen += pParaPortion->GetLines().GetObject( --nTest )->GetLen();
			DBG_ASSERT( ( nTPLen == pParaPortion->GetNode()->Len() ) && ( nTxtLen == pParaPortion->GetNode()->Len() ), "CreateBinTextObject: ParaPortion not completely formatted!" );
#endif
		}
	}
	return pTxtObj;
}

void ImpEditEngine::SetText( const EditTextObject& rTextObject )
{
	// Da Setzen eines TextObject ist nicht Undo-faehig!
	ResetUndoManager();
	sal_Bool _bUpdate = GetUpdateMode();
	sal_Bool _bUndo = IsUndoEnabled();

	SetText( XubString() );
	EditPaM aPaM = aEditDoc.GetStartPaM();

	SetUpdateMode( sal_False );
	EnableUndo( sal_False );

	InsertText( rTextObject, EditSelection( aPaM, aPaM ) );
	SetVertical( rTextObject.IsVertical() );

#ifndef SVX_LIGHT
	DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "Woher kommt das Undo in SetText ?!" );
#endif
	SetUpdateMode( _bUpdate );
	EnableUndo( _bUndo );
}

EditSelection ImpEditEngine::InsertText( const EditTextObject& rTextObject, EditSelection aSel )
{
    EnterBlockNotifications();
	aSel.Adjust( aEditDoc );
	if ( aSel.HasRange() )
		aSel = ImpDeleteSelection( aSel );
	EditSelection aNewSel = InsertBinTextObject( (BinTextObject&)rTextObject, aSel.Max() );
    LeaveBlockNotifications();
    return aNewSel;

	// MT 05/00: InsertBinTextObject direkt hier machen...
}

EditSelection ImpEditEngine::InsertBinTextObject( BinTextObject& rTextObject, EditPaM aPaM )
{
	// Optimieren:
	// Kein GetPos undFindParaportion, sondern Index berechnen!
	EditSelection aSel( aPaM, aPaM );
	DBG_ASSERT( !aSel.DbgIsBuggy( aEditDoc ), "InsertBibTextObject: Selektion kaput!(1)" );

	sal_Bool bUsePortionInfo = sal_False;
//	sal_Bool bFields = sal_False;
	XParaPortionList* pPortionInfo = rTextObject.GetPortionInfo();

	if ( pPortionInfo && ( (long)pPortionInfo->GetPaperWidth() == aPaperSize.Width() )
			&& ( pPortionInfo->GetRefMapMode() == GetRefDevice()->GetMapMode() ) )
	{
		if ( ( pPortionInfo->GetRefDevPtr() == (sal_uIntPtr)GetRefDevice() ) ||
			 ( ( pPortionInfo->GetRefDevType() == OUTDEV_VIRDEV ) &&
			   ( GetRefDevice()->GetOutDevType() == OUTDEV_VIRDEV ) ) )
		bUsePortionInfo = sal_True;
	}

	sal_Bool bConvertItems = sal_False;
	MapUnit eSourceUnit = MapUnit(), eDestUnit = MapUnit();
	if ( rTextObject.HasMetric() )
	{
		eSourceUnit = (MapUnit)rTextObject.GetMetric();
		eDestUnit = (MapUnit)aEditDoc.GetItemPool().GetMetric( DEF_METRIC );
		if ( eSourceUnit != eDestUnit )
			bConvertItems = sal_True;
	}

	sal_uInt16 nContents = rTextObject.GetContents().Count();
	sal_uInt16 nPara = aEditDoc.GetPos( aPaM.GetNode() );

	for ( sal_uInt16 n = 0; n < nContents; n++, nPara++ )
	{
		ContentInfo* pC = rTextObject.GetContents().GetObject( n );
		sal_Bool bNewContent = aPaM.GetNode()->Len() ? sal_False: sal_True;
		sal_uInt16 nStartPos = aPaM.GetIndex();

		aPaM = ImpFastInsertText( aPaM, pC->GetText() );

		ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
		DBG_ASSERT( pPortion, "Blinde Portion in FastInsertText" );
		pPortion->MarkInvalid( nStartPos, pC->GetText().Len() );

		// Zeicheattribute...
		sal_Bool bAllreadyHasAttribs = aPaM.GetNode()->GetCharAttribs().Count() ? sal_True : sal_False;
		sal_uInt16 nNewAttribs = pC->GetAttribs().Count();
		if ( nNewAttribs )
		{
            BOOL bUpdateFields = FALSE;
			for ( sal_uInt16 nAttr = 0; nAttr < nNewAttribs; nAttr++ )
			{
				XEditAttribute* pX = pC->GetAttribs().GetObject( nAttr );
				// Kann passieren wenn Absaetze >16K entstehen, dann wird einfach umgebrochen.
				if ( pX->GetEnd() <= aPaM.GetNode()->Len() )
				{
					if ( !bAllreadyHasAttribs || pX->IsFeature() )
					{
						// Normale Attribute gehen dann schneller...
						// Features duerfen nicht ueber EditDoc::InsertAttrib
						// eingefuegt werden, sie sind bei FastInsertText schon im TextFluss
						DBG_ASSERT( pX->GetEnd() <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribut zu gross!" );
						EditCharAttrib* pAttr;
						if ( !bConvertItems )
							pAttr = MakeCharAttrib( aEditDoc.GetItemPool(), *(pX->GetItem()), pX->GetStart()+nStartPos, pX->GetEnd()+nStartPos );
						else
						{
							SfxPoolItem* pNew = pX->GetItem()->Clone();
							ConvertItem( *pNew, eSourceUnit, eDestUnit );
							pAttr = MakeCharAttrib( aEditDoc.GetItemPool(), *pNew, pX->GetStart()+nStartPos, pX->GetEnd()+nStartPos );
							delete pNew;
						}
						DBG_ASSERT( pAttr->GetEnd() <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribut passt nicht! (1)" );
						aPaM.GetNode()->GetCharAttribs().InsertAttrib( pAttr );
						if ( pAttr->Which() == EE_FEATURE_FIELD )
                            bUpdateFields = TRUE;
					}
					else
					{
						DBG_ASSERT( pX->GetEnd()+nStartPos <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribut passt nicht! (2)" );
						// Tabs und andere Features koennen nicht ueber InsertAttrib eingefuegt werden:
						aEditDoc.InsertAttrib( aPaM.GetNode(), pX->GetStart()+nStartPos, pX->GetEnd()+nStartPos, *pX->GetItem() );
					}
				}
			}
            if ( bUpdateFields )
                UpdateFields();

            // Sonst QuickFormat => Keine Attribute!
			pPortion->MarkSelectionInvalid( nStartPos, pC->GetText().Len() );
		}

		DBG_ASSERT( CheckOrderedList( aPaM.GetNode()->GetCharAttribs().GetAttribs(), sal_True ), "InsertBinTextObject: Start-Liste verdreht" );

		sal_Bool bParaAttribs = sal_False;
		if ( bNewContent || ( ( n > 0 ) && ( n < (nContents-1) ) ) )
		{
            bParaAttribs = sal_False;
            // #101512# Don't overwrite level/style from existing paragraph in OutlineView
            // MT 10/2002: Removed because of #103874#, handled in Outliner::EndPasteOrDropHdl now.
//            if ( !aStatus.IsOutliner() || n )
            {
			    // nur dann Style und ParaAttribs, wenn neuer Absatz, oder
			    // komplett inneliegender...
			    bParaAttribs = pC->GetParaAttribs().Count() ? sal_True : sal_False;
			    if ( GetStyleSheetPool() && pC->GetStyle().Len() )
			    {
				    SfxStyleSheet* pStyle = (SfxStyleSheet*)GetStyleSheetPool()->Find( pC->GetStyle(), pC->GetFamily() );
				    DBG_ASSERT( pStyle, "InsertBinTextObject - Style not found!" );
				    SetStyleSheet( nPara, pStyle );
			    }
			    if ( !bConvertItems )
				    SetParaAttribs( aEditDoc.GetPos( aPaM.GetNode() ), pC->GetParaAttribs() );
			    else
			    {
				    SfxItemSet aAttribs( GetEmptyItemSet() );
				    ConvertAndPutItems( aAttribs, pC->GetParaAttribs(), &eSourceUnit, &eDestUnit );
				    SetParaAttribs( aEditDoc.GetPos( aPaM.GetNode() ), aAttribs );
			    }
            }
			if ( bNewContent && bUsePortionInfo )
			{
				XParaPortion* pXP = pPortionInfo->GetObject( n );
				DBG_ASSERT( pXP, "InsertBinTextObject: PortionInfo?" );
				ParaPortion* pParaPortion = GetParaPortions()[ nPara ];
				DBG_ASSERT( pParaPortion, "InsertBinTextObject: ParaPortion?" );
				pParaPortion->nHeight = pXP->nHeight;
				pParaPortion->nFirstLineOffset = pXP->nFirstLineOffset;
				pParaPortion->bForceRepaint = sal_True;
				pParaPortion->SetValid();	// Nicht formatieren

				// Die TextPortions
				pParaPortion->GetTextPortions().Reset();
				sal_uInt16 nCount = pXP->aTextPortions.Count();
				for ( sal_uInt16 _n = 0; _n < nCount; _n++ )
				{
					TextPortion* pTextPortion = pXP->aTextPortions[_n];
					TextPortion* pNew = new TextPortion( *pTextPortion );
					pParaPortion->GetTextPortions().Insert( pNew, _n );
				}

				// Die Zeilen
				pParaPortion->GetLines().Reset();
				nCount = pXP->aLines.Count();
				for ( sal_uInt16 m = 0; m < nCount; m++ )
				{
					EditLine* pLine = pXP->aLines[m];
					EditLine* pNew = pLine->Clone();
					pNew->SetInvalid();	// neu Painten!
					pParaPortion->GetLines().Insert( pNew, m );
				}
#ifdef DBG_UTIL
				USHORT nTest;
                int nTPLen = 0, nTxtLen = 0;
				for ( nTest = pParaPortion->GetTextPortions().Count(); nTest; )
					nTPLen += pParaPortion->GetTextPortions().GetObject( --nTest )->GetLen();
				for ( nTest = pParaPortion->GetLines().Count(); nTest; )
					nTxtLen += pParaPortion->GetLines().GetObject( --nTest )->GetLen();
				DBG_ASSERT( ( nTPLen == pParaPortion->GetNode()->Len() ) && ( nTxtLen == pParaPortion->GetNode()->Len() ), "InsertBinTextObject: ParaPortion not completely formatted!" );
#endif
			}
		}
		if ( !bParaAttribs ) // DefFont wird bei FastInsertParagraph nicht berechnet
		{
			aPaM.GetNode()->GetCharAttribs().GetDefFont() = aEditDoc.GetDefFont();
			if ( aStatus.UseCharAttribs() )
				aPaM.GetNode()->CreateDefFont();
		}

#ifndef SVX_LIGHT
		if ( bNewContent && GetStatus().DoOnlineSpelling() && pC->GetWrongList() )
		{
			aPaM.GetNode()->DestroyWrongList();	// otherwise MLK, if list exists...
			aPaM.GetNode()->SetWrongList( pC->GetWrongList()->Clone() );
		}
#endif // !SVX_LIGHT

		// Zeilenumbruch, wenn weitere folgen...
		if ( n < ( nContents-1) )
		{
			if ( bNewContent )
				aPaM = ImpFastInsertParagraph( nPara+1 );
			else
				aPaM = ImpInsertParaBreak( aPaM, sal_False );
		}
	}

	aSel.Max() = aPaM;
	DBG_ASSERT( !aSel.DbgIsBuggy( aEditDoc ), "InsertBibTextObject: Selektion kaput!(1)" );
	return aSel;
}

LanguageType ImpEditEngine::GetLanguage( const EditPaM& rPaM, USHORT* pEndPos ) const
{
	short nScriptType = GetScriptType( rPaM, pEndPos );	// pEndPos will be valid now, pointing to ScriptChange or NodeLen
	USHORT nLangId = GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType );
	const SvxLanguageItem* pLangItem = &(const SvxLanguageItem&)rPaM.GetNode()->GetContentAttribs().GetItem( nLangId );
	EditCharAttrib*	pAttr = rPaM.GetNode()->GetCharAttribs().FindAttrib( nLangId, rPaM.GetIndex() );
	if ( pAttr )
		pLangItem = (const SvxLanguageItem*)pAttr->GetItem();

	if ( pEndPos && pAttr && ( pAttr->GetEnd() < *pEndPos ) )
		*pEndPos = pAttr->GetEnd();

	return pLangItem->GetLanguage();
}

::com::sun::star::lang::Locale ImpEditEngine::GetLocale( const EditPaM& rPaM ) const
{
	return SvxCreateLocale( GetLanguage( rPaM ) );
}

Reference< XSpellChecker1 > ImpEditEngine::GetSpeller()
{
#ifndef SVX_LIGHT
	if ( !xSpeller.is() )
		xSpeller = SvxGetSpellChecker();
#endif
	return xSpeller;
}

EESpellState ImpEditEngine::Spell( EditView* pEditView, sal_Bool bMultipleDoc )
{
#ifdef SVX_LIGHT
	return EE_SPELL_NOSPELLER;
#else

	DBG_ASSERTWARNING( xSpeller.is(), "Kein Speller gesetzt!" );

	if ( !xSpeller.is() )
		return EE_SPELL_NOSPELLER;

	aOnlineSpellTimer.Stop();

	// Bei MultipleDoc immer von vorne/hinten...
	if ( bMultipleDoc )
	{
        pEditView->pImpEditView->SetEditSelection( aEditDoc.GetStartPaM() );
	}

	EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
	pSpellInfo = new SpellInfo;
	pSpellInfo->bMultipleDoc = bMultipleDoc;
    pSpellInfo->aSpellStart = CreateEPaM( SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD ).Min() );

	sal_Bool bIsStart = sal_False;
	if ( bMultipleDoc )
		bIsStart = sal_True;	// Immer von Vorne bzw. von hinten...
    else if ( ( CreateEPaM( aEditDoc.GetStartPaM() ) == pSpellInfo->aSpellStart ) )
		bIsStart = sal_True;

	EditSpellWrapper* pWrp = new EditSpellWrapper( Application::GetDefDialogParent(),
			xSpeller, bIsStart, sal_False, pEditView );
	pWrp->SpellDocument();
	delete pWrp;

	if ( !bMultipleDoc )
	{
		pEditView->pImpEditView->DrawSelection();
		if ( aCurSel.Max().GetIndex() > aCurSel.Max().GetNode()->Len() )
			aCurSel.Max().GetIndex() = aCurSel.Max().GetNode()->Len();
		aCurSel.Min() = aCurSel.Max();
		pEditView->pImpEditView->SetEditSelection( aCurSel );
		pEditView->pImpEditView->DrawSelection();
		pEditView->ShowCursor( sal_True, sal_False );
	}
	EESpellState eState = pSpellInfo->eState;
	delete pSpellInfo;
	pSpellInfo = 0;
	return eState;
#endif
}


sal_Bool ImpEditEngine::HasConvertibleTextPortion( LanguageType nSrcLang )
{
#ifdef SVX_LIGHT
    return sal_False;
#else
    sal_Bool    bHasConvTxt = sal_False;

    USHORT nParas = pEditEngine->GetParagraphCount();
    for (USHORT k = 0;  k < nParas;  ++k)
    {
        SvUShorts aPortions;
        pEditEngine->GetPortions( k, aPortions );
        for ( USHORT nPos = 0; nPos < aPortions.Count(); ++nPos )
        {
            USHORT nEnd   = aPortions.GetObject( nPos );
            USHORT nStart = nPos > 0 ? aPortions.GetObject( nPos - 1 ) : 0;

			// if the paragraph is not empty we need to increase the index
			// by one since the attribute of the character left to the
			// specified position is evaluated.
			if (nEnd > nStart)	// empty para?
				++nStart;
            LanguageType nLangFound = pEditEngine->GetLanguage( k, nStart );
#ifdef DEBUG
            lang::Locale aLocale( SvxCreateLocale( nLangFound ) );
#endif
            bHasConvTxt =   (nSrcLang == nLangFound) ||
                            (svx::HangulHanjaConversion::IsChinese( nLangFound ) &&
                             svx::HangulHanjaConversion::IsChinese( nSrcLang ));
            if (bHasConvTxt)
                return bHasConvTxt;
       }
    }

#endif
    return bHasConvTxt;
}


void ImpEditEngine::Convert( EditView* pEditView,
        LanguageType nSrcLang, LanguageType nDestLang, const Font *pDestFont,
        INT32 nOptions, sal_Bool bIsInteractive, sal_Bool bMultipleDoc )
{
    // modified version of ImpEditEngine::Spell

#ifdef SVX_LIGHT
#else

    // Bei MultipleDoc immer von vorne/hinten...
    if ( bMultipleDoc )
        pEditView->pImpEditView->SetEditSelection( aEditDoc.GetStartPaM() );

	//
	// initialize pConvInfo
	//
    EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
    aCurSel.Adjust( aEditDoc );
    pConvInfo = new ConvInfo;
    pConvInfo->bMultipleDoc = bMultipleDoc;
    pConvInfo->aConvStart = CreateEPaM( aCurSel.Min() );
	//
	// if it is not just a selection and we are about to begin
	// with the current conversion for the very first time
	// we need to find the start of the current (initial)
	// convertible unit in order for the text conversion to give
	// the correct result for that. Since it is easier to obtain
	// the start of the word we use that though.
	if (!aCurSel.HasRange() && ImplGetBreakIterator().is())
	{
		EditPaM aWordStartPaM(	SelectWord( aCurSel, i18n::WordType::DICTIONARY_WORD ).Min() );

		// since #118246 / #117803 still occurs if the cursor is placed
		// between the two chinese characters to be converted (because both
		// of them are words on their own!) using the word boundary here does
		// not work. Thus since chinese conversion is not interactive we start
		// at the begin of the paragraph to solve the problem, i.e. have the
        // TextConversion service get those characters together in the same call.
		USHORT nStartIdx = ( svx::HangulHanjaConversion::IsChinese( nSrcLang ) ) ?
								0 : aWordStartPaM.GetIndex();
		pConvInfo->aConvStart.nIndex = nStartIdx;
	}
	//
    pConvInfo->aConvContinue = pConvInfo->aConvStart;

    sal_Bool bIsStart = sal_False;
    if ( bMultipleDoc )
        bIsStart = sal_True;    // Immer von Vorne bzw. von hinten...
    else if ( CreateEPaM( aEditDoc.GetStartPaM() ) == pConvInfo->aConvStart )
        bIsStart = sal_True;

    bImpConvertFirstCall = sal_True;    // next ImpConvert call is the very first in this conversion turn

    Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
    TextConvWrapper aWrp( Application::GetDefDialogParent(), xMSF,
                          SvxCreateLocale( nSrcLang ), SvxCreateLocale( nDestLang ),
                          pDestFont,
                          nOptions, bIsInteractive,
                          bIsStart, pEditView );

	//
	//!! optimization does not work since when update mode is false
    //!! the object is 'lying' about it portions, paragraphs,
	//!! EndPaM... later on.
    //!! Should not be a great problem since text boxes or cells in
	//!! Calc usually have only a rather short text.
	//
	// disallow formatting, updating the view, ... while
	// non-interactively converting the document. (saves time)
	//if (!bIsInteractive)
	//	SetUpdateMode( FALSE );

	aWrp.Convert();

	//if (!bIsInteractive)
	//SetUpdateMode( TRUE, 0, TRUE );

    if ( !bMultipleDoc )
    {
        pEditView->pImpEditView->DrawSelection();
        if ( aCurSel.Max().GetIndex() > aCurSel.Max().GetNode()->Len() )
            aCurSel.Max().GetIndex() = aCurSel.Max().GetNode()->Len();
        aCurSel.Min() = aCurSel.Max();
        pEditView->pImpEditView->SetEditSelection( aCurSel );
        pEditView->pImpEditView->DrawSelection();
        pEditView->ShowCursor( sal_True, sal_False );
    }
    delete pConvInfo;
    pConvInfo = 0;
#endif
}


void ImpEditEngine::SetLanguageAndFont(
    const ESelection &rESel,
    LanguageType nLang, USHORT nLangWhichId,
    const Font *pFont,  USHORT nFontWhichId )
{
    ESelection aOldSel = pActiveView->GetSelection();
    pActiveView->SetSelection( rESel );

    // set new language attribute
    SfxItemSet aNewSet( pActiveView->GetEmptyItemSet() );
    aNewSet.Put( SvxLanguageItem( nLang, nLangWhichId ) );

    // new font to be set?
    DBG_ASSERT( pFont, "target font missing?" );
    if (pFont)
    {
        // set new font attribute
        SvxFontItem aFontItem = (SvxFontItem&) aNewSet.Get( nFontWhichId );
        aFontItem.GetFamilyName()   = pFont->GetName();
        aFontItem.GetFamily()       = pFont->GetFamily();
        aFontItem.GetStyleName()    = pFont->GetStyleName();
        aFontItem.GetPitch()        = pFont->GetPitch();
        aFontItem.GetCharSet()      = pFont->GetCharSet();
        aNewSet.Put( aFontItem );
    }

    // apply new attributes
    pActiveView->SetAttribs( aNewSet );

    pActiveView->SetSelection( aOldSel );
}


void ImpEditEngine::ImpConvert( rtl::OUString &rConvTxt, LanguageType &rConvTxtLang,
        EditView* pEditView, LanguageType nSrcLang, const ESelection &rConvRange,
        sal_Bool bAllowImplicitChangesForNotConvertibleText,
        LanguageType nTargetLang, const Font *pTargetFont  )
{
    // modified version of ImpEditEngine::ImpSpell

    // looks for next convertible text portion to be passed on to the wrapper

    String aRes;
    LanguageType nResLang = LANGUAGE_NONE;

#ifdef SVX_LIGHT
    rConvTxt = rtl::OUString();
    rConvTxtLang = LANGUAGE_NONE;
#else

    /* ContentNode* pLastNode = */ aEditDoc.SaveGetObject( aEditDoc.Count()-1 );

    EditPaM aPos( CreateEditPaM( pConvInfo->aConvContinue ) );
	EditSelection aCurSel = EditSelection( aPos, aPos );

    String aWord;

    while (!aRes.Len())
    {
        // empty paragraph found that needs to have language and font set?
		if (bAllowImplicitChangesForNotConvertibleText &&
            !pEditEngine->GetText( pConvInfo->aConvContinue.nPara ).Len())
		{
			USHORT nPara = pConvInfo->aConvContinue.nPara;
            ESelection aESel( nPara, 0, nPara, 0 );
            // see comment for below same function call
            SetLanguageAndFont( aESel,
                    nTargetLang, EE_CHAR_LANGUAGE_CJK,
                    pTargetFont, EE_CHAR_FONTINFO_CJK );
		}


		if (pConvInfo->aConvContinue.nPara  == pConvInfo->aConvTo.nPara &&
			pConvInfo->aConvContinue.nIndex >= pConvInfo->aConvTo.nIndex)
			break;

/*
        // Bekannter (wahrscheinlicher) Bug: Wenn SpellToCurrent, muss
        // Current bei jeder Ersetzung korrigiert werden, sonst passt
        // das Ende evtl. nicht mehr genau...
        if ( pConvInfo->bConvToEnd || pConvInfo->bMultipleDoc )
        {
            if ( aCurSel.Max().GetNode() == pLastNode &&
                 aCurSel.Max().GetIndex() >= pLastNode->Len() )
                break;
        }
*/

		USHORT nAttribStart	= USHRT_MAX;
		USHORT nAttribEnd	= USHRT_MAX;
		USHORT nCurPos		= USHRT_MAX;
		EPaM aCurStart = CreateEPaM( aCurSel.Min() );
		SvUShorts aPortions;
		pEditEngine->GetPortions( (USHORT)aCurStart.nPara, aPortions );
		for ( USHORT nPos = 0; nPos < aPortions.Count(); ++nPos )
		{
			USHORT nEnd	  = aPortions.GetObject( nPos );
			USHORT nStart = nPos > 0 ? aPortions.GetObject( nPos - 1 ) : 0;

            // the language attribute is obtained from the left character
            // (like usually all other attributes)
            // thus we usually have to add 1 in order to get the language
            // of the text right to the cursor position
            USHORT nLangIdx = nEnd > nStart ? nStart + 1 : nStart;
            LanguageType nLangFound = pEditEngine->GetLanguage( aCurStart.nPara, nLangIdx );
#ifdef DEBUG
            lang::Locale aLocale( SvxCreateLocale( nLangFound ) );
#endif
            sal_Bool bLangOk =  (nLangFound == nSrcLang) ||
                                (svx::HangulHanjaConversion::IsChinese( nLangFound ) &&
                                 svx::HangulHanjaConversion::IsChinese( nSrcLang ));

            if (nAttribEnd != USHRT_MAX) // start already found?
			{
				DBG_ASSERT(nEnd >= aCurStart.nIndex, "error while scanning attributes (a)" );
				DBG_ASSERT(nEnd >= nAttribEnd, "error while scanning attributes (b)" );
				if (/*nEnd >= aCurStart.nIndex &&*/ nLangFound == nResLang)
					nAttribEnd = nEnd;
				else  // language attrib has changed
					break;
			}
			if (nAttribStart == USHRT_MAX && // start not yet found?
				nEnd > aCurStart.nIndex && bLangOk)
			{
				nAttribStart = nStart;
				nAttribEnd   = nEnd;
				nResLang = nLangFound;
			}
			//! the list of portions may have changed compared to the previous
			//! call to this function (because of possibly changed language
			//! attribute!)
			//! But since we don't want to start in the already processed part
			//! we clip the start accordingly.
			if (nAttribStart < aCurStart.nIndex)
			{
				nAttribStart = aCurStart.nIndex;
			}

            // check script type to the right of the start of the current portion
            EditPaM aPaM( CreateEditPaM( EPaM(aCurStart.nPara, nLangIdx) ) );
            sal_Bool bIsAsianScript = (i18n::ScriptType::ASIAN == GetScriptType( aPaM ));
            // not yet processed text part with for conversion
            // not suitable language found that needs to be changed?
            if (bAllowImplicitChangesForNotConvertibleText &&
                !bLangOk && !bIsAsianScript && nEnd > aCurStart.nIndex)
			{
                ESelection aESel( aCurStart.nPara, nStart, aCurStart.nPara, nEnd );
				// set language and font to target language and font of conversion
				//! Now this especially includes all non convertible text e.g.
				//! spaces, empty paragraphs and western text.
				// This is in order for every *new* text entered at *any* position to
				// have the correct language and font attributes set.
                SetLanguageAndFont( aESel,
                        nTargetLang, EE_CHAR_LANGUAGE_CJK,
                        pTargetFont, EE_CHAR_FONTINFO_CJK );
			}

			nCurPos = nEnd;
		}

		if (nAttribStart != USHRT_MAX  &&  nAttribEnd != USHRT_MAX)
		{
			aCurSel.Min().SetIndex( nAttribStart );
			aCurSel.Max().SetIndex( nAttribEnd );
		}
		else if (nCurPos != USHRT_MAX)
		{
			// set selection to end of scanned text
			// (used to set the position where to continue from later on)
			aCurSel.Min().SetIndex( nCurPos );
			aCurSel.Max().SetIndex( nCurPos );
		}

		if ( !pConvInfo->bConvToEnd )
        {
            EPaM aEPaM( CreateEPaM( aCurSel.Min() ) );
            if ( !( aEPaM < pConvInfo->aConvTo ) )
                break;
        }

		// clip selected word to the converted area
		// (main use when conversion starts/ends **within** a word)
		EditPaM aPaM( CreateEditPaM( pConvInfo->aConvStart ) );
		if (pConvInfo->bConvToEnd &&
			aCurSel.Min().GetNode() == aPaM.GetNode() &&
			aCurSel.Min().GetIndex() < aPaM.GetIndex())
				aCurSel.Min().SetIndex( aPaM.GetIndex() );
		aPaM = CreateEditPaM( pConvInfo->aConvContinue );
		if (aCurSel.Min().GetNode() == aPaM.GetNode() &&
			aCurSel.Min().GetIndex() < aPaM.GetIndex())
				aCurSel.Min().SetIndex( aPaM.GetIndex() );
		aPaM = CreateEditPaM( pConvInfo->aConvTo );
		if ((!pConvInfo->bConvToEnd || rConvRange.HasRange())&&
			aCurSel.Max().GetNode() == aPaM.GetNode() &&
			aCurSel.Max().GetIndex() > aPaM.GetIndex())
				aCurSel.Max().SetIndex( aPaM.GetIndex() );

		aWord = GetSelected( aCurSel );

        if ( aWord.Len() > 0 /* && bLangOk */)
            aRes = aWord;

		// move to next word/paragraph if necessary
        if ( !aRes.Len() )
            aCurSel = WordRight( aCurSel.Min(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );

        pConvInfo->aConvContinue = CreateEPaM( aCurSel.Max() );
    }

    pEditView->pImpEditView->DrawSelection();
    pEditView->pImpEditView->SetEditSelection( aCurSel );
    pEditView->pImpEditView->DrawSelection();
    pEditView->ShowCursor( sal_True, sal_False );

    rConvTxt = aRes;
    if (rConvTxt.getLength())
        rConvTxtLang = nResLang;
#endif
}


Reference< XSpellAlternatives > ImpEditEngine::ImpSpell( EditView* pEditView )
{
#ifdef SVX_LIGHT
	return Reference< XSpellAlternatives >();
#else

	DBG_ASSERT( xSpeller.is(), "Kein Speller gesetzt!" );

    ContentNode* pLastNode = aEditDoc.SaveGetObject( (aEditDoc.Count()-1) );
	EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
    aCurSel.Min() = aCurSel.Max();

	String aWord;
	Reference< XSpellAlternatives > xSpellAlt;
	Sequence< PropertyValue > aEmptySeq;
	while (!xSpellAlt.is())
	{

		// Bekannter (wahrscheinlicher) Bug: Wenn SpellToCurrent, muss
		// Current bei jeder Ersetzung korrigiert werden, sonst passt
		// das Ende evtl. nicht mehr genau...
		if ( pSpellInfo->bSpellToEnd || pSpellInfo->bMultipleDoc )
		{
			if ( aCurSel.Max().GetNode() == pLastNode )
			{
                if ( ( aCurSel.Max().GetIndex() >= pLastNode->Len() ) )
					break;
			}
		}
		else if ( !pSpellInfo->bSpellToEnd )
		{
			EPaM aEPaM( CreateEPaM( aCurSel.Max() ) );
            if ( !( aEPaM < pSpellInfo->aSpellTo ) )
				break;
		}

		aCurSel = SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
		aWord = GetSelected( aCurSel );

		// Wenn Punkt dahinter, muss dieser mit uebergeben werden !
		// Falls Abkuerzung...
		if ( aWord.Len() && ( aCurSel.Max().GetIndex() < aCurSel.Max().GetNode()->Len() ) )
		{
			sal_Unicode cNext = aCurSel.Max().GetNode()->GetChar( aCurSel.Max().GetIndex() );
			if ( cNext == '.' )
			{
				aCurSel.Max().GetIndex()++;
				aWord += cNext;
			}
		}

		if ( aWord.Len() > 0 )
		{
			LanguageType eLang = GetLanguage( aCurSel.Max() );
			SvxSpellWrapper::CheckSpellLang( xSpeller, eLang );
			xSpellAlt = xSpeller->spell( aWord, eLang, aEmptySeq );
		}

		if ( !xSpellAlt.is() )
			aCurSel = WordRight( aCurSel.Min(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
		else
			pSpellInfo->eState = EE_SPELL_ERRORFOUND;
	}

	pEditView->pImpEditView->DrawSelection();
	pEditView->pImpEditView->SetEditSelection( aCurSel );
	pEditView->pImpEditView->DrawSelection();
	pEditView->ShowCursor( sal_True, sal_False );
	return xSpellAlt;
#endif
}
/*-- 13.10.2003 16:43:27---------------------------------------------------

  -----------------------------------------------------------------------*/
void ImpEditEngine::EndSpelling()
{
    DELETEZ(pSpellInfo);
}
/*-- 13.10.2003 16:43:27---------------------------------------------------

  -----------------------------------------------------------------------*/
void ImpEditEngine::StartSpelling(EditView& rEditView, sal_Bool bMultipleDoc)
{
    DBG_ASSERT(!pSpellInfo, "pSpellInfo already set?");
    pSpellInfo = new SpellInfo;
    pSpellInfo->bMultipleDoc = bMultipleDoc;
    rEditView.pImpEditView->SetEditSelection( aEditDoc.GetStartPaM() );
    EditSelection aCurSel( rEditView.pImpEditView->GetEditSelection() );
    pSpellInfo->aSpellStart = CreateEPaM( SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD ).Min() );
}
/*-- 13.10.2003 16:43:27---------------------------------------------------
    Search for the next wrong word within the given selection
  -----------------------------------------------------------------------*/
Reference< XSpellAlternatives > ImpEditEngine::ImpFindNextError(EditSelection& rSelection)
{
    /* ContentNode* pLastNode = */ aEditDoc.SaveGetObject( (aEditDoc.Count()-1) );
    EditSelection aCurSel( rSelection.Min() );

    String aWord;
    Reference< XSpellAlternatives > xSpellAlt;
    Sequence< PropertyValue > aEmptySeq;
    while (!xSpellAlt.is())
    {
        //check if the end of the selection has been reached
        {
            EPaM aEPaM( CreateEPaM( aCurSel.Max() ) );
            if ( !( aEPaM < CreateEPaM( rSelection.Max()) ) )
                break;
        }

        aCurSel = SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
        aWord = GetSelected( aCurSel );

        // Wenn Punkt dahinter, muss dieser mit uebergeben werden !
        // Falls Abkuerzung...
        if ( aWord.Len() && ( aCurSel.Max().GetIndex() < aCurSel.Max().GetNode()->Len() ) )
        {
            sal_Unicode cNext = aCurSel.Max().GetNode()->GetChar( aCurSel.Max().GetIndex() );
            if ( cNext == '.' )
            {
                aCurSel.Max().GetIndex()++;
                aWord += cNext;
            }
        }

        if ( aWord.Len() > 0 )
            xSpellAlt = xSpeller->spell( aWord, GetLanguage( aCurSel.Max() ), aEmptySeq );

        if ( !xSpellAlt.is() )
            aCurSel = WordRight( aCurSel.Min(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
        else
		{
            pSpellInfo->eState = EE_SPELL_ERRORFOUND;
			rSelection = aCurSel;
		}
    }
    return xSpellAlt;
}
/*-- 13.10.2003 16:43:27---------------------------------------------------

  -----------------------------------------------------------------------*/
bool ImpEditEngine::SpellSentence(EditView& rEditView, ::svx::SpellPortions& rToFill, bool /*bIsGrammarChecking*/ )
{
#ifdef SVX_LIGHT
#else
    bool bRet = false;
    //the pSpellInfo has to be created on demand
	if(!pSpellInfo)
	{
		pSpellInfo = new SpellInfo;
		pSpellInfo->bMultipleDoc = sal_True;
		rEditView.pImpEditView->SetEditSelection( aEditDoc.GetStartPaM() );
		EditSelection aCurSel( rEditView.pImpEditView->GetEditSelection() );
		pSpellInfo->aSpellStart = CreateEPaM( SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD ).Min() );
	}
    DBG_ASSERT( xSpeller.is(), "Kein Speller gesetzt!" );
    pSpellInfo->aLastSpellPortions.clear();
    pSpellInfo->aLastSpellContentSelections.clear();
    rToFill.clear();
    EditSelection aCurSel( rEditView.pImpEditView->GetEditSelection() );
    //if no selection previously exists the range is extended to the end of the object
    if(aCurSel.Min() == aCurSel.Max())
    {
        ContentNode* pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count()-1);
        aCurSel.Max() = EditPaM(pLastNode, pLastNode->Len());
    }
    Reference< XSpellAlternatives > xAlt = ImpFindNextError(aCurSel);
    if(xAlt.is())
    {
        bRet = true;
		//find the sentence boundaries
        EditSelection aSentencePaM = SelectSentence(aCurSel);
        //make sure that the sentence is never smaller than the error range!
        if(aSentencePaM.Max().GetIndex() < aCurSel.Max().GetIndex())
            aSentencePaM.Max() = aCurSel.Max();
        //add the portion preceeding the error
        EditSelection aStartSelection(aSentencePaM.Min(), aCurSel.Min());
        if(aStartSelection.HasRange())
			AddPortionIterated(rEditView, aStartSelection, 0, rToFill);
        //add the error portion
        AddPortionIterated(rEditView, aCurSel, xAlt, rToFill);
        //find the end of the sentence
        //search for all errors in the rest of the sentence and add all the portions
        do
        {
            EditSelection aNextSel = EditSelection(aCurSel.Max(), aSentencePaM.Max());
            xAlt = ImpFindNextError(aNextSel);
            if(xAlt.is())
            {
                //add the part between the previous and the current error
				AddPortionIterated(rEditView, EditSelection(aCurSel.Max(), aNextSel.Min()), 0, rToFill);
                //add the current error
				AddPortionIterated(rEditView, aNextSel, xAlt, rToFill);
            }
            else
                AddPortionIterated(rEditView, EditSelection(aCurSel.Max(), aSentencePaM.Max()), xAlt, rToFill);
			aCurSel = aNextSel;
        }
        while( xAlt.is() );
		//set the selection to the end of the current sentence
		rEditView.pImpEditView->SetEditSelection(aSentencePaM.Max());
    }
#endif
    return bRet;
}

/*-- 15.10.2003 16:09:12---------------------------------------------------
    adds one portion to the SpellPortions
  -----------------------------------------------------------------------*/
void ImpEditEngine::AddPortion(
                            const EditSelection rSel,
                            uno::Reference< XSpellAlternatives > xAlt,
                                ::svx::SpellPortions& rToFill,
                                bool bIsField)
{
#ifdef SVX_LIGHT
#else
    if(rSel.HasRange())
	{
		svx::SpellPortion aPortion;
		aPortion.sText = GetSelected( rSel );
		aPortion.eLanguage = GetLanguage( rSel.Min() );
		aPortion.xAlternatives = xAlt;
        aPortion.bIsField = bIsField;
        rToFill.push_back(aPortion);

		//save the spelled portions for later use
		pSpellInfo->aLastSpellPortions.push_back(aPortion);
		pSpellInfo->aLastSpellContentSelections.push_back(rSel);

	}
#endif
}

/*-- 15.10.2003 16:07:47---------------------------------------------------
    adds one or more portions of text to the SpellPortions depending on language changes
  -----------------------------------------------------------------------*/
void ImpEditEngine::AddPortionIterated(
                            EditView& rEditView,
                            const EditSelection rSel,
                            Reference< XSpellAlternatives > xAlt,
                                ::svx::SpellPortions& rToFill)
{
#ifdef SVX_LIGHT
#else
    if(rSel.Min() != rSel.Max())
    {
        if(xAlt.is())
        {
            AddPortion(rSel, xAlt, rToFill, false);
        }
        else
        {
            //iterate and search for language attribute changes
            //save the start and end positions
            bool bTest = rSel.Min().GetIndex() <= rSel.Max().GetIndex();
            EditPaM aStart(bTest ? rSel.Min() : rSel.Max());
            EditPaM aEnd(bTest ? rSel.Max() : rSel.Min());
            //iterate over the text to find changes in language
            //set the mark equal to the point
            EditPaM aCursor(aStart);
            rEditView.pImpEditView->SetEditSelection( aCursor );
            LanguageType eStartLanguage = GetLanguage( aCursor );
            //search for a field attribute at the beginning - only the end position
            //of this field is kept to end a portion at that position
            const EditCharAttrib* pFieldAttr = aCursor.GetNode()->GetCharAttribs().
                                                    FindFeature( aCursor.GetIndex() );
            bool bIsField = pFieldAttr &&
                    pFieldAttr->GetStart() == aCursor.GetIndex() &&
                    pFieldAttr->GetStart() != pFieldAttr->GetEnd() &&
                    pFieldAttr->Which() == EE_FEATURE_FIELD;
            USHORT nEndField = bIsField ? pFieldAttr->GetEnd() : USHRT_MAX;
			bool bIsEndField = false;
            do
            {
                aCursor = CursorRight( aCursor);
                //determine whether a field and has been reached
				bIsEndField = nEndField == aCursor.GetIndex();
                //search for a new field attribute
                EditCharAttrib* _pFieldAttr = aCursor.GetNode()->GetCharAttribs().
                                                        FindFeature( aCursor.GetIndex() );
                bIsField = _pFieldAttr &&
                        _pFieldAttr->GetStart() == aCursor.GetIndex() &&
                        _pFieldAttr->GetStart() != _pFieldAttr->GetEnd() &&
                        _pFieldAttr->Which() == EE_FEATURE_FIELD;
                //on every new field move the end position
                if(bIsField)
                    nEndField = bIsField ? _pFieldAttr->GetEnd() : USHRT_MAX;

                LanguageType eCurLanguage = GetLanguage( aCursor );
                if(eCurLanguage != eStartLanguage || bIsField || bIsEndField)
                {
                    eStartLanguage = eCurLanguage;
                    //go one step back - the cursor currently selects the first character
                    //with a different language
                    //create a selection from start to the current Cursor
                    EditSelection aSelection(aStart, aCursor);
                    AddPortion(aSelection, xAlt, rToFill, bIsEndField);
                    aStart = aCursor;
                }
            }
            while(aCursor.GetIndex() < aEnd.GetIndex());
            EditSelection aSelection(aStart, aCursor);
            AddPortion(aSelection, xAlt, rToFill, bIsField);
        }
    }
#endif
}

/*-- 13.10.2003 16:43:33---------------------------------------------------

  -----------------------------------------------------------------------*/
void ImpEditEngine::ApplyChangedSentence(EditView& rEditView, const ::svx::SpellPortions& rNewPortions, bool /*bIsGrammarChecking*/ )
{
#ifdef SVX_LIGHT
#else
    DBG_ASSERT(pSpellInfo, "pSpellInfo not initialized");
    if(pSpellInfo)
    {
        UndoActionStart( EDITUNDO_INSERT );
        if(pSpellInfo->aLastSpellPortions.size() == rNewPortions.size())
        {
            //the simple case: the same number of elements on both sides
            //each changed element has to be applied to the corresponding source element
            svx::SpellPortions::const_iterator aCurrentNewPortion = rNewPortions.end();
            svx::SpellPortions::const_iterator aCurrentOldPortion = pSpellInfo->aLastSpellPortions.end();
            SpellContentSelections::const_iterator aCurrentOldPosition = pSpellInfo->aLastSpellContentSelections.end();
            bool bSetToEnd = false;
			do
            {
                --aCurrentNewPortion;
                --aCurrentOldPortion;
                --aCurrentOldPosition;
				//set the cursor to the end of the sentence - necessary to
				//resume there at the next step
				if(!bSetToEnd)
				{
					bSetToEnd = true;
					rEditView.pImpEditView->SetEditSelection( aCurrentOldPosition->Max() );
				}

                USHORT nScriptType = GetI18NScriptTypeOfLanguage( aCurrentNewPortion->eLanguage );
//                LanguageType eTextLanguage = GetLanguage( aCurrentOldPosition->Min() );

                USHORT nLangWhichId = EE_CHAR_LANGUAGE;
                switch(nScriptType)
                {
                    case SCRIPTTYPE_ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break;
                    case SCRIPTTYPE_COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break;
                }
                if(aCurrentNewPortion->sText != aCurrentOldPortion->sText)
                {
                    //change text and apply language
                    SfxItemSet aSet( aEditDoc.GetItemPool(), nLangWhichId, nLangWhichId);
                    aSet.Put(SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId));
                    SetAttribs( *aCurrentOldPosition, aSet );
                    ImpInsertText( *aCurrentOldPosition, aCurrentNewPortion->sText );
                }
                else if(aCurrentNewPortion->eLanguage != aCurrentOldPortion->eLanguage)
                {
                    //apply language
                    SfxItemSet aSet( aEditDoc.GetItemPool(), nLangWhichId, nLangWhichId);
                    aSet.Put(SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId));
                    SetAttribs( *aCurrentOldPosition, aSet );
                }
                if(aCurrentNewPortion == rNewPortions.begin())
                    break;
            }
            while(aCurrentNewPortion != rNewPortions.begin());
        }
        else
        {
            //select the complete sentence
            SpellContentSelections::const_iterator aCurrentEndPosition = pSpellInfo->aLastSpellContentSelections.end();
            --aCurrentEndPosition;
            SpellContentSelections::const_iterator aCurrentStartPosition = pSpellInfo->aLastSpellContentSelections.begin();
            EditSelection aAllSentence(aCurrentStartPosition->Min(), aCurrentEndPosition->Max());

            //delete the sentence completely
            ImpDeleteSelection( aAllSentence );
            svx::SpellPortions::const_iterator aCurrentNewPortion = rNewPortions.begin();
            EditPaM aCurrentPaM = aAllSentence.Min();
            while(aCurrentNewPortion != rNewPortions.end())
            {
                //set the language attribute
                LanguageType eCurLanguage = GetLanguage( aCurrentPaM );
                if(eCurLanguage != aCurrentNewPortion->eLanguage)
                {
                    USHORT nScriptType = GetI18NScriptTypeOfLanguage( aCurrentNewPortion->eLanguage );
                    USHORT nLangWhichId = EE_CHAR_LANGUAGE;
                    switch(nScriptType)
                    {
                        case SCRIPTTYPE_ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break;
                        case SCRIPTTYPE_COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break;
                    }
                    SfxItemSet aSet( aEditDoc.GetItemPool(), nLangWhichId, nLangWhichId);
                    aSet.Put(SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId));
                    SetAttribs( aCurrentPaM, aSet );
                }
                //insert the new string and set the cursor to the end of the inserted string
                aCurrentPaM = ImpInsertText( aCurrentPaM , aCurrentNewPortion->sText );
                ++aCurrentNewPortion;
            }
        }
        UndoActionEnd( EDITUNDO_INSERT );
    }
    FormatAndUpdate();
    aEditDoc.SetModified(TRUE);
#endif
}
/*-- 08.09.2008 11:33:02---------------------------------------------------

  -----------------------------------------------------------------------*/
void ImpEditEngine::PutSpellingToSentenceStart( EditView& rEditView )
{
#ifdef SVX_LIGHT
#else
    if( pSpellInfo && pSpellInfo->aLastSpellContentSelections.size() )
    {
        rEditView.pImpEditView->SetEditSelection( pSpellInfo->aLastSpellContentSelections.begin()->Min() );
    }
  
#endif
}


void ImpEditEngine::DoOnlineSpelling( ContentNode* pThisNodeOnly, sal_Bool bSpellAtCursorPos, sal_Bool bInteruptable )
{
#ifndef SVX_LIGHT
	/*
	 Er wird ueber alle Absaetze iteriert, nur Absaetze mit invalidierter
	 WrongList werden geprueft...

	 Es werden alle Woerter im invalidierten Bereich geprueft.
	 Ist ein Wort falsch, aber noch nicht in der WrongList, oder umgekehrt,
	 wird der Bereich des Wortes invalidiert
	  (	kein Invalidate, sondern wenn nur Uebergaenge von richtig=>falsch,
		einfaches Paint, bei Uebergaengen von falsch=>richtig mit VDev
		ueberplaetten )
	*/

 	if ( !xSpeller.is() )
		return;

	EditPaM aCursorPos;
	if( pActiveView && !bSpellAtCursorPos )
	{
		DBG_CHKOBJ( pActiveView, EditView, 0 );
		aCursorPos = pActiveView->pImpEditView->GetEditSelection().Max();
	}
	sal_Bool bRestartTimer = sal_False;

	ContentNode* pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count() - 1 );
	sal_uInt16 nNodes = GetEditDoc().Count();
	sal_uInt16 nInvalids = 0;
	Sequence< PropertyValue > aEmptySeq;
	for ( sal_uInt16 n = 0; n < nNodes; n++ )
	{
		ContentNode* pNode = GetEditDoc().GetObject( n );
		if ( pThisNodeOnly )
			pNode = pThisNodeOnly;

		if ( pNode->GetWrongList()->IsInvalid() )
		{
			WrongList* pWrongList = pNode->GetWrongList();
			sal_uInt16 nInvStart = pWrongList->GetInvalidStart();
			sal_uInt16 nInvEnd = pWrongList->GetInvalidEnd();

			sal_uInt16 nWrongs = 0;	// Auch im Absatz mal die Kontrolle abgeben...
//			sal_Bool bStop = sal_False;

			sal_uInt16 nPaintFrom = 0xFFFF, nPaintTo = 0;
			sal_Bool bSimpleRepaint = sal_True;

			pWrongList->SetValid();

			EditPaM aPaM( pNode, nInvStart );
			EditSelection aSel( aPaM, aPaM );
			while ( ( aSel.Max().GetNode() == pNode ) /* && !bStop */ )
			{
				if ( ( aSel.Min().GetIndex() > nInvEnd )
						|| ( ( aSel.Max().GetNode() == pLastNode ) && ( aSel.Max().GetIndex() >= pLastNode->Len() ) ) )
					break;	// Dokument- oder Ungueltigkeitsbereich-Ende

				aSel = SelectWord( aSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
				String aWord( GetSelected( aSel ) );
				// Wenn Punkt dahinter, muss dieser mit uebergeben werden !
				// Falls Abkuerzung...
				sal_Bool bDottAdded = sal_False;
				if ( aSel.Max().GetIndex() < aSel.Max().GetNode()->Len() )
				{
					sal_Unicode cNext = aSel.Max().GetNode()->GetChar( aSel.Max().GetIndex() );
					if ( cNext == '.' )
					{
						aSel.Max().GetIndex()++;
						aWord += cNext;
						bDottAdded = sal_True;
					}
				}


				sal_Bool bChanged = sal_False;
				if ( aWord.Len() > 0 )
				{
					sal_uInt16 nWStart = aSel.Min().GetIndex();
					sal_uInt16 nWEnd= aSel.Max().GetIndex();
					if ( !xSpeller->isValid( aWord, GetLanguage( EditPaM( aSel.Min().GetNode(), nWStart+1 ) ), aEmptySeq ) )
					{
						// Pruefen, ob schon richtig markiert...
						nWrongs++;
						// Nur bei SimpleRepaint stoppen, sonst zu oft VDev
	//						if ( ( nWrongs > 8 ) && bSimpleRepaint )
	//						{
	//							bStop = sal_True;
	// 							pWrongList->MarkInvalid( aSel.Max().GetIndex(), nInvEnd );
	//						}
						sal_uInt16 nXEnd = bDottAdded ? nWEnd -1 : nWEnd;
						if ( !pWrongList->HasWrong( nWStart, nXEnd ) )
						{
							// Wort als falsch markieren...
							// Aber nur, wenn nicht an Cursor-Position...
							sal_Bool bCursorPos = sal_False;
							if ( aCursorPos.GetNode() == pNode )
							{
								if ( ( nWStart <= aCursorPos.GetIndex() ) && nWEnd >= aCursorPos.GetIndex() )
									bCursorPos = sal_True;
							}
							if ( bCursorPos )
							{
								// Dann weiter als ungueltig markieren...
								pWrongList->GetInvalidStart() = nWStart;
								pWrongList->GetInvalidEnd() = nWEnd;
								bRestartTimer = sal_True;
							}
							else
							{
								// Es kann sein, dass die Wrongs in der Liste nicht
								// genau ueber Woerter aufgespannt sind, weil die
								// WordDelimiters beim Expandieren nicht ausgewrtet werden.
								pWrongList->InsertWrong( nWStart, nXEnd, sal_True );
								bChanged = sal_True;
							}
						}
					}
					else
					{
						// Pruefen, ob nicht als als falsch markiert....
						if ( pWrongList->HasAnyWrong( nWStart, nWEnd ) )
						{
							pWrongList->ClearWrongs( nWStart, nWEnd, pNode );
							bSimpleRepaint = sal_False;
							bChanged = sal_True;
						}
					}
					if ( bChanged  )
					{
						if ( nPaintFrom == 0xFFFF )
							nPaintFrom = nWStart;
						nPaintTo = nWEnd;
					}
				}

				EditPaM aLastEnd( aSel.Max() );
				aSel = WordRight( aSel.Max(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
				if ( bChanged && ( aSel.Min().GetNode() == pNode ) &&
						( ( aSel.Min().GetIndex()-aLastEnd.GetIndex() > 1 ) ) )
				{
					// Wenn zwei Worte durch mehr Zeichen als ein Blank getrennt
					// sind, kann es passieren, dass beim Aufsplitten eines Wrongs
					// der Start den zweiten Wortes vor dem tatsaechlich Wort liegt
					pWrongList->ClearWrongs( aLastEnd.GetIndex(), aSel.Min().GetIndex(), pNode );
				}
			}

			// Invalidieren?
            if ( ( nPaintFrom != 0xFFFF ) )
			{
				aStatus.GetStatusWord() |= EE_STAT_WRONGWORDCHANGED;
				CallStatusHdl();

				if ( aEditViews.Count() )
				{
					// Bei SimpleRepaint wuerde ein uebermalen ohne VDev reichen,
					// aber dann muesste ich ueber alle Views, Intersecten,
					// Clippen, ...
					// Lohnt wahrscheinlich nicht.
					EditPaM aStartPaM( pNode, nPaintFrom );
					EditPaM aEndPaM( pNode, nPaintTo );
					Rectangle aStartCursor( PaMtoEditCursor( aStartPaM ) );
					Rectangle aEndCursor( PaMtoEditCursor( aEndPaM ) );
					DBG_ASSERT( aInvalidRec.IsEmpty(), "InvalidRect gesetzt!" );
					aInvalidRec.Left() = 0;
					aInvalidRec.Right() = GetPaperSize().Width();
					aInvalidRec.Top() = aStartCursor.Top();
					aInvalidRec.Bottom() = aEndCursor.Bottom();
					if ( pActiveView && pActiveView->HasSelection() )
					{
						// Dann darf nicht ueber VDev ausgegeben werden
						UpdateViews( NULL );
					}
					else if ( bSimpleRepaint )
					{
						for ( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
						{
							EditView* pView = aEditViews[nView];
							Rectangle aClipRec( aInvalidRec );
							aClipRec.Intersection( pView->GetVisArea() );
							if ( !aClipRec.IsEmpty() )
							{
								// in Fensterkoordinaten umwandeln....
								aClipRec.SetPos( pView->pImpEditView->GetWindowPos( aClipRec.TopLeft() ) );
								// Wenn Selektion, dann VDev...
								Paint( pView->pImpEditView, aClipRec, pView->HasSelection() );
							}
						}
					}
					else
					{
						UpdateViews( pActiveView );
					}
					aInvalidRec = Rectangle();
				}
			}
			// Nach zwei korrigierten Nodes die Kontrolle abgeben...
			nInvalids++;
			if ( bInteruptable && ( nInvalids >= 2 ) )
			{
				bRestartTimer = sal_True;
				break;
			}
		}

		if ( pThisNodeOnly )
			break;
	}
	if ( bRestartTimer )
		aOnlineSpellTimer.Start();
#endif // !SVX_LIGHT
}


EESpellState ImpEditEngine::HasSpellErrors()
{
	DBG_ASSERT( xSpeller.is(), "Kein Speller gesetzt!" );

#ifndef SVX_LIGHT
	ContentNode* pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count() - 1 );
	EditSelection aCurSel( aEditDoc.GetStartPaM() );

	String aWord;
	Reference< XSpellAlternatives > xSpellAlt;
	Sequence< PropertyValue > aEmptySeq;
	while ( !xSpellAlt.is() )
	{
		if ( ( aCurSel.Max().GetNode() == pLastNode ) &&
			 ( aCurSel.Max().GetIndex() >= pLastNode->Len() ) )
		{
			return EE_SPELL_OK;
		}

		aCurSel = SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
		aWord = GetSelected( aCurSel );
		if ( aWord.Len() > 0 )
		{
			LanguageType eLang = GetLanguage( aCurSel.Max() );
			SvxSpellWrapper::CheckSpellLang( xSpeller, eLang );
			xSpellAlt = xSpeller->spell( aWord, eLang, aEmptySeq );
		}
		aCurSel = WordRight( aCurSel.Max(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
	}
#endif

	return EE_SPELL_ERRORFOUND;
}

EESpellState ImpEditEngine::StartThesaurus( EditView* pEditView )
{
#ifndef SVX_LIGHT
	EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
	if ( !aCurSel.HasRange() )
		aCurSel = SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
	String aWord( GetSelected( aCurSel ) );

	Reference< XThesaurus > xThes( SvxGetThesaurus() );
	if (!xThes.is())
		return EE_SPELL_ERRORFOUND;

	SvxThesaurusDialog aDialog( pEditView->GetWindow(), xThes, aWord, GetLanguage( aCurSel.Max() ) );

	if ( aDialog.Execute() == RET_OK )
	{
		// Wort ersetzen...
		pEditView->pImpEditView->DrawSelection();
		pEditView->pImpEditView->SetEditSelection( aCurSel );
		pEditView->pImpEditView->DrawSelection();
		pEditView->InsertText( aDialog.GetWord() );
		pEditView->ShowCursor( sal_True, sal_False );
	}

	return EE_SPELL_OK;
#else
	return EE_SPELL_NOSPELLER;
#endif
}

sal_uInt16 ImpEditEngine::StartSearchAndReplace( EditView* pEditView, const SvxSearchItem& rSearchItem )
{
	sal_uInt16 nFound = 0;

#ifndef SVX_LIGHT
	EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );

	// FIND_ALL ohne Mehrfachselektion nicht moeglich.
	if ( ( rSearchItem.GetCommand() == SVX_SEARCHCMD_FIND ) ||
		 ( rSearchItem.GetCommand() == SVX_SEARCHCMD_FIND_ALL ) )
	{
		if ( Search( rSearchItem, pEditView ) )
			nFound++;
	}
	else if ( rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE )
	{
		// Das Wort ist selektiert, wenn der Anwender die Selektion
		// nicht zwischendurch manipuliert:
		if ( aCurSel.HasRange() )
		{
			pEditView->InsertText( rSearchItem.GetReplaceString() );
			nFound = 1;
		}
		else
			if( Search( rSearchItem, pEditView ) )
				nFound = 1;
	}
	else if ( rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE_ALL )
	{
		// Der Writer ersetzt alle, vorn Anfang bis Ende...
		SvxSearchItem aTmpItem( rSearchItem );
		aTmpItem.SetBackward( sal_False );

		pEditView->pImpEditView->DrawSelection();

        aCurSel.Adjust( aEditDoc );
		EditPaM aStartPaM = aTmpItem.GetSelection() ? aCurSel.Min() : aEditDoc.GetStartPaM();
		EditSelection aFoundSel( aCurSel.Max() );
		sal_Bool bFound = ImpSearch( aTmpItem, aCurSel, aStartPaM, aFoundSel );
		if ( bFound )
			UndoActionStart( EDITUNDO_REPLACEALL );
		while ( bFound )
		{
			nFound++;
			aStartPaM = ImpInsertText( aFoundSel, rSearchItem.GetReplaceString() );
			bFound = ImpSearch( aTmpItem, aCurSel, aStartPaM, aFoundSel );
		}
		if ( nFound )
		{
			EditPaM aNewPaM( aFoundSel.Max() );
			if ( aNewPaM.GetIndex() > aNewPaM.GetNode()->Len() )
				aNewPaM.GetIndex() =  aNewPaM.GetNode()->Len();
			pEditView->pImpEditView->SetEditSelection( aNewPaM );
			FormatAndUpdate( pEditView );
			UndoActionEnd( EDITUNDO_REPLACEALL );
		}
		else
		{
			pEditView->pImpEditView->DrawSelection();
			pEditView->ShowCursor( sal_True, sal_False );
		}
	}
#endif // !SVX_LIGHT
	return nFound;
}

BOOL ImpEditEngine::Search( const SvxSearchItem& rSearchItem, EditView* pEditView )
{
	EditSelection aSel( pEditView->pImpEditView->GetEditSelection() );
	aSel.Adjust( aEditDoc );
	EditPaM aStartPaM( aSel.Max() );
	if ( rSearchItem.GetSelection() && !rSearchItem.GetBackward() )
		aStartPaM = aSel.Min();

	EditSelection aFoundSel;
	BOOL bFound = ImpSearch( rSearchItem, aSel, aStartPaM, aFoundSel );
	if ( bFound && ( aFoundSel == aSel ) )	// Bei Rueckwaetssuche
	{
		aStartPaM = aSel.Min();
		bFound = ImpSearch( rSearchItem, aSel, aStartPaM, aFoundSel );
	}

	pEditView->pImpEditView->DrawSelection();
	if ( bFound )
	{
		// Erstmal das Min einstellen, damit das ganze Wort in den sichtbaren Bereich kommt.
		pEditView->pImpEditView->SetEditSelection( aFoundSel.Min() );
		pEditView->ShowCursor( TRUE, FALSE );
		pEditView->pImpEditView->SetEditSelection( aFoundSel );
	}
	else
		pEditView->pImpEditView->SetEditSelection( aSel.Max() );

	pEditView->pImpEditView->DrawSelection();
	pEditView->ShowCursor( TRUE, FALSE );
	return bFound;
}

sal_Bool ImpEditEngine::ImpSearch( const SvxSearchItem& rSearchItem,
	const EditSelection& rSearchSelection, const EditPaM& rStartPos, EditSelection& rFoundSel )
{
#ifndef SVX_LIGHT
	util::SearchOptions aSearchOptions( rSearchItem.GetSearchOptions() );
	aSearchOptions.Locale = GetLocale( rStartPos );

	sal_Bool bBack = rSearchItem.GetBackward();
	sal_Bool bSearchInSelection = rSearchItem.GetSelection();
	sal_uInt16 nStartNode = aEditDoc.GetPos( rStartPos.GetNode() );
	sal_uInt16 nEndNode;
	if ( bSearchInSelection )
	{
		nEndNode = aEditDoc.GetPos( bBack ? rSearchSelection.Min().GetNode() : rSearchSelection.Max().GetNode() );
	}
	else
	{
		nEndNode = bBack ? 0 : aEditDoc.Count()-1;
	}

	utl::TextSearch aSearcher( aSearchOptions );

	// ueber die Absaetze iterieren...
	for ( sal_uInt16 nNode = nStartNode;
			bBack ? ( nNode >= nEndNode ) : ( nNode <= nEndNode) ;
			bBack ? nNode-- : nNode++ )
	{
		// Bei rueckwaertsuche, wenn nEndNode = 0:
		if ( nNode >= 0xFFFF )
			return sal_False;

		ContentNode* pNode = aEditDoc.GetObject( nNode );

		sal_uInt16 nStartPos = 0;
		sal_uInt16 nEndPos = pNode->Len();
		if ( nNode == nStartNode )
		{
			if ( bBack )
				nEndPos = rStartPos.GetIndex();
			else
				nStartPos = rStartPos.GetIndex();
		}
		if ( ( nNode == nEndNode ) && bSearchInSelection )
		{
			if ( bBack )
				nStartPos = rSearchSelection.Min().GetIndex();
			else
				nEndPos = rSearchSelection.Max().GetIndex();
		}

		// Suchen...
		XubString aParaStr( GetEditDoc().GetParaAsString( pNode ) );
		bool bFound = false;
		if ( bBack )
		{
			SwapUSHORTs( nStartPos, nEndPos );
			bFound = aSearcher.SearchBkwrd( aParaStr, &nStartPos, &nEndPos);
		}
		else
			bFound = aSearcher.SearchFrwrd( aParaStr, &nStartPos, &nEndPos);

		if ( bFound )
		{
			rFoundSel.Min().SetNode( pNode );
			rFoundSel.Min().SetIndex( nStartPos );
			rFoundSel.Max().SetNode( pNode );
			rFoundSel.Max().SetIndex( nEndPos );
			return sal_True;
		}
	}
#endif // !SVX_LIGHT
	return sal_False;
}

sal_Bool ImpEditEngine::HasText( const SvxSearchItem& rSearchItem )
{
#ifndef SVX_LIGHT
	SvxSearchItem aTmpItem( rSearchItem );
	aTmpItem.SetBackward( sal_False );
	aTmpItem.SetSelection( sal_False );

	EditPaM aStartPaM( aEditDoc.GetStartPaM() );
	EditSelection aDummySel( aStartPaM );
	EditSelection aFoundSel;
	return ImpSearch( aTmpItem, aDummySel, aStartPaM, aFoundSel );
#else
	return sal_False;
#endif
}

void ImpEditEngine::SetAutoCompleteText( const String& rStr, sal_Bool bClearTipWindow )
{
#ifndef SVX_LIGHT
	aAutoCompleteText = rStr;
	if ( bClearTipWindow && pActiveView )
		Help::ShowQuickHelp( pActiveView->GetWindow(), Rectangle(), String(), 0 );
#endif // !SVX_LIGHT
}

EditSelection ImpEditEngine::TransliterateText( const EditSelection& rSelection, sal_Int32 nTransliterationMode )
{
	EditSelection aSel( rSelection );
	aSel.Adjust( aEditDoc );

	if ( !aSel.HasRange() )
		aSel = SelectWord( aSel );

    EditSelection aNewSel( aSel );

	USHORT nStartNode = aEditDoc.GetPos( aSel.Min().GetNode() );
	USHORT nEndNode = aEditDoc.GetPos( aSel.Max().GetNode() );

	BOOL bChanges = FALSE;
    BOOL bLenChanged = FALSE;
	EditUndoTransliteration* pUndo = NULL;

	utl::TransliterationWrapper aTranslitarationWrapper( ::comphelper::getProcessServiceFactory(), nTransliterationMode );
	BOOL bConsiderLanguage = aTranslitarationWrapper.needLanguageForTheMode();

	for ( USHORT nNode = nStartNode; nNode <= nEndNode; nNode++	)
	{
		ContentNode* pNode = aEditDoc.GetObject( nNode );
		xub_StrLen nStartPos = 0;
		xub_StrLen nEndPos = pNode->Len();
		if ( nNode == nStartNode )
			nStartPos = aSel.Min().GetIndex();
		if ( nNode == nEndNode ) // kann auch == nStart sein!
			nEndPos = aSel.Max().GetIndex();

		USHORT nCurrentStart = nStartPos;
		USHORT nCurrentEnd = nEndPos;
		sal_uInt16 nLanguage = LANGUAGE_SYSTEM;

		do
		{
			if ( bConsiderLanguage )
			{
				nLanguage = GetLanguage( EditPaM( pNode, nCurrentStart+1 ), &nCurrentEnd );
				if ( nCurrentEnd > nEndPos )
					nCurrentEnd = nEndPos;
			}

			xub_StrLen nLen = nCurrentEnd - nCurrentStart;

			Sequence <sal_Int32> aOffsets;
			String aNewText( aTranslitarationWrapper.transliterate( *pNode, nLanguage, nCurrentStart, nLen, &aOffsets ) );

			if( ( nLen != aNewText.Len() ) || !pNode->Equals( aNewText, nCurrentStart, nLen ) )
			{
				bChanges = TRUE;
                if ( nLen != aNewText.Len() )
                    bLenChanged = TRUE;

#ifndef SVX_LIGHT
                // Create UndoAction on Demand....
	            if ( !pUndo && IsUndoEnabled() && !IsInUndo() )
	            {
		            ESelection aESel( CreateESel( aSel ) );
		            pUndo = new EditUndoTransliteration( this, aESel, nTransliterationMode );

		            if ( ( nStartNode == nEndNode ) && !aSel.Min().GetNode()->GetCharAttribs().HasAttrib( aSel.Min().GetIndex(), aSel.Max().GetIndex() ) )
			            pUndo->SetText( aSel.Min().GetNode()->Copy( aSel.Min().GetIndex(), aSel.Max().GetIndex()-aSel.Min().GetIndex() ) );
		            else
			            pUndo->SetText( CreateBinTextObject( aSel, NULL ) );
	            }
#endif

                // Change text without loosing the attributes
                USHORT nCharsAfterTransliteration =
                    sal::static_int_cast< USHORT >(aOffsets.getLength());
                const sal_Int32* pOffsets = aOffsets.getConstArray();
                short nDiffs = 0;
                for ( USHORT n = 0; n < nCharsAfterTransliteration; n++ )
                {
                    USHORT nCurrentPos = nCurrentStart+n;
                    sal_Int32 nDiff = (nCurrentPos-nDiffs) - pOffsets[n];

                    if ( !nDiff )
                    {
                        DBG_ASSERT( nCurrentPos < pNode->Len(), "TransliterateText - String smaller than expected!" );
                        pNode->SetChar( nCurrentPos, aNewText.GetChar(n) );
                    }
                    else if ( nDiff < 0 )
                    {
                        // Replace first char, delete the rest...
                        DBG_ASSERT( nCurrentPos < pNode->Len(), "TransliterateText - String smaller than expected!" );
                        pNode->SetChar( nCurrentPos, aNewText.GetChar(n) );

                        DBG_ASSERT( (nCurrentPos+1) < pNode->Len(), "TransliterateText - String smaller than expected!" );
                        GetEditDoc().RemoveChars( EditPaM( pNode, nCurrentPos+1 ), sal::static_int_cast< USHORT >(-nDiff) );
                    }
                    else
                    {
                        DBG_ASSERT( nDiff == 1, "TransliterateText - Diff other than expected! But should work..." );
                        GetEditDoc().InsertText( EditPaM( pNode, nCurrentPos ), aNewText.GetChar(n) );

                    }
                    nDiffs = sal::static_int_cast< short >(nDiffs + nDiff);
                }

                if ( nNode == nEndNode )
                    aNewSel.Max().GetIndex() =
                        aNewSel.Max().GetIndex() + nDiffs;

                ParaPortion* pParaPortion = GetParaPortions()[nNode];
				pParaPortion->MarkSelectionInvalid( nCurrentStart, std::max< USHORT >( nCurrentStart+nLen, nCurrentStart+aNewText.Len() ) );

			}
			nCurrentStart = nCurrentEnd;
		} while( nCurrentEnd < nEndPos );
	}

#ifndef SVX_LIGHT
	if ( pUndo )
	{
		ESelection aESel( CreateESel( aNewSel ) );
		pUndo->SetNewSelection( aESel );
		InsertUndo( pUndo );
	}
#endif

	if ( bChanges )
    {
        TextModified();
        SetModifyFlag( sal_True );
        if ( bLenChanged )
            UpdateSelections();
		FormatAndUpdate();
    }

    return aNewSel;
}

void ImpEditEngine::SetAsianCompressionMode( USHORT n )
{
    if ( n != nAsianCompressionMode )
    {
        nAsianCompressionMode = n;
        if ( ImplHasText() )
        {
            FormatFullDoc();
            UpdateViews();
        }
    }
}

void ImpEditEngine::SetKernAsianPunctuation( BOOL b )
{
    if ( b != bKernAsianPunctuation )
    {
        bKernAsianPunctuation = b;
        if ( ImplHasText() )
        {
            FormatFullDoc();
            UpdateViews();
        }
    }
}

void ImpEditEngine::SetAddExtLeading( BOOL bExtLeading )
{
    if ( IsAddExtLeading() != bExtLeading )
    {
        bAddExtLeading = bExtLeading;
        if ( ImplHasText() )
        {
            FormatFullDoc();
            UpdateViews();
        }
    }
};



BOOL ImpEditEngine::ImplHasText() const
{
    return ( ( GetEditDoc().Count() > 1 ) || GetEditDoc().GetObject(0)->Len() );
}

long ImpEditEngine::LogicToTwips( long n )
{
	Size aSz( n, 0 );
	MapMode aTwipsMode( MAP_TWIP );
	aSz = pRefDev->LogicToLogic( aSz, NULL, &aTwipsMode );
	return aSz.Width();
}


