/*************************************************************************
 *
 * 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: impedit2.cxx,v $
 * $Revision: 1.124.40.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>

#include <svx/lspcitem.hxx>
#include <svx/flditem.hxx>
#include <impedit.hxx>
#include <svx/editeng.hxx>
#include <svx/editview.hxx>
#include <editdbg.hxx>
#include <eerdll2.hxx>
#include <eerdll.hxx>
#include <edtspell.hxx>
#include <eeobj.hxx>
#include <txtrange.hxx>
#include <svtools/urlbmk.hxx>
#include <svtools/colorcfg.hxx>
#include <svtools/ctloptions.hxx>
#include <acorrcfg.hxx>

#include <svx/fhgtitem.hxx>
#include <svx/lrspitem.hxx>
#include <svx/ulspitem.hxx>
#include <svx/wghtitem.hxx>
#include <svx/postitem.hxx>
#include <svx/udlnitem.hxx>
#include <svx/adjitem.hxx>
#include <svx/scripttypeitem.hxx>
#include <svx/frmdiritem.hxx>
#include <fontitem.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/fcontnr.hxx>
#include <sfx2/dispatch.hxx>
#include <vcl/cmdevt.h>

#ifndef SVX_LIGHT
#ifndef _SFXFRAME_HXX //autogen
#include <sfx2/frame.hxx>
#endif
#endif
#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
#include <com/sun/star/i18n/WordType.hpp>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/lang/Locale.hpp>
#include <com/sun/star/text/CharacterCompressionType.hpp>
#include <com/sun/star/i18n/InputSequenceCheckMode.hpp>

#include <comphelper/processfactory.hxx>

#include <sot/formats.hxx>

#include <unicode/ubidi.h>

using namespace ::com::sun::star;

USHORT lcl_CalcExtraSpace( ParaPortion*, const SvxLineSpacingItem& rLSItem )
{
	USHORT nExtra = 0;
	/* if ( ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP )
			&& ( rLSItem.GetPropLineSpace() != 100 ) )
	{
		// ULONG nH = pPortion->GetNode()->GetCharAttribs().GetDefFont().GetSize().Height();
		ULONG nH = pPortion->GetLines().GetObject( 0 )->GetHeight();
		long n = nH * rLSItem.GetPropLineSpace();
		n /= 100;
		n -= nH;	// nur den Abstand
		if ( n > 0 )
			nExtra = (USHORT)n;
	}
	else */
	if ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
	{
		nExtra = rLSItem.GetInterLineSpace();
	}

	return nExtra;
}

// ----------------------------------------------------------------------
//	class ImpEditEngine
//	----------------------------------------------------------------------

ImpEditEngine::ImpEditEngine( EditEngine* pEE, SfxItemPool* pItemPool ) :
	aPaperSize( 0x7FFFFFFF, 0x7FFFFFFF ),
	aMinAutoPaperSize( 0x0, 0x0 ),
	aMaxAutoPaperSize( 0x7FFFFFFF, 0x7FFFFFFF ),
	aEditDoc( pItemPool ),
	aWordDelimiters( RTL_CONSTASCII_USTRINGPARAM( "  .,;:-'`'?!_=\"{}()[]\0xFF" ) ),
	aGroupChars( RTL_CONSTASCII_USTRINGPARAM( "{}()[]" ) )
{
	pEditEngine 		= pEE;
	pRefDev 			= NULL;
	pVirtDev 			= NULL;
	pEmptyItemSet 		= NULL;
	pActiveView 		= NULL;
	pSpellInfo 			= NULL;
    pConvInfo           = NULL;
	pTextObjectPool 	= NULL;
	mpIMEInfos			= NULL;
	pStylePool 			= NULL;
	pUndoManager 		= NULL;
	pUndoMarkSelection	= NULL;
	pTextRanger			= NULL;
    pColorConfig        = NULL;
    pCTLOptions         = NULL;

	nCurTextHeight 		= 0;
    nBlockNotifications = 0;
	nBigTextObjectStart	= 20;

	nStretchX			= 100;
	nStretchY			= 100;

	bInSelection 		= FALSE;
	bOwnerOfRefDev 		= FALSE;
	bDowning 			= FALSE;
	bIsInUndo 			= FALSE;
	bIsFormatting 		= FALSE;
	bFormatted			= FALSE;
	bUpdate 			= TRUE;
    bUseAutoColor       = TRUE;
    bForceAutoColor     = FALSE;
    bAddExtLeading      = FALSE;
	bUndoEnabled 		= TRUE;
	bCallParaInsertedOrDeleted = FALSE;
    bImpConvertFirstCall= FALSE;
    bFirstWordCapitalization    = TRUE;

	eDefLanguage		= LANGUAGE_DONTKNOW;
	maBackgroundColor	= COL_AUTO;

    nAsianCompressionMode = text::CharacterCompressionType::NONE;
	bKernAsianPunctuation = FALSE;

    eDefaultHorizontalTextDirection = EE_HTEXTDIR_DEFAULT;


	aStatus.GetControlWord() =	EE_CNTRL_USECHARATTRIBS | EE_CNTRL_DOIDLEFORMAT |
								EE_CNTRL_PASTESPECIAL | EE_CNTRL_UNDOATTRIBS |
								EE_CNTRL_ALLOWBIGOBJS | EE_CNTRL_RTFSTYLESHEETS |
								EE_CNTRL_FORMAT100;

	aSelEngine.SetFunctionSet( &aSelFuncSet );

	aStatusTimer.SetTimeout( 200 );
	aStatusTimer.SetTimeoutHdl( LINK( this, ImpEditEngine, StatusTimerHdl ) );

	aIdleFormatter.SetTimeout( 5 );
	aIdleFormatter.SetTimeoutHdl( LINK( this, ImpEditEngine, IdleFormatHdl ) );

	aOnlineSpellTimer.SetTimeout( 100 );
	aOnlineSpellTimer.SetTimeoutHdl( LINK( this, ImpEditEngine, OnlineSpellHdl ) );

	pRefDev 			= EE_DLL()->GetGlobalData()->GetStdRefDevice();

	// Ab hier wird schon auf Daten zugegriffen!
	SetRefDevice( pRefDev );
	InitDoc( FALSE );

	bCallParaInsertedOrDeleted = TRUE;

    aEditDoc.SetModifyHdl( LINK( this, ImpEditEngine, DocModified ) );

	mbLastTryMerge = FALSE;
}

ImpEditEngine::~ImpEditEngine()
{
	aStatusTimer.Stop();
	aOnlineSpellTimer.Stop();
	aIdleFormatter.Stop();

	// das Zerstoeren von Vorlagen kann sonst unnoetiges Formatieren ausloesen,
	// wenn eine Parent-Vorlage zerstoert wird.
	// Und das nach dem Zerstoeren der Daten!
	bDowning = TRUE;
	SetUpdateMode( FALSE );

	delete pVirtDev;
	delete pEmptyItemSet;
	delete pUndoManager;
	delete pTextRanger;
	delete mpIMEInfos;
    delete pColorConfig;
    delete pCTLOptions;
	if ( bOwnerOfRefDev )
		delete pRefDev;
    delete pSpellInfo;
}

void ImpEditEngine::SetRefDevice( OutputDevice* pRef )
{
	if ( bOwnerOfRefDev )
		delete pRefDev;

	pRefDev = pRef;
	bOwnerOfRefDev = FALSE;

	if ( !pRef )
		pRefDev = EE_DLL()->GetGlobalData()->GetStdRefDevice();

	nOnePixelInRef = (USHORT)pRefDev->PixelToLogic( Size( 1, 0 ) ).Width();

	if ( IsFormatted() )
	{
		FormatFullDoc();
		UpdateViews( (EditView*) 0);
	}
}

void ImpEditEngine::SetRefMapMode( const MapMode& rMapMode )
{
	if ( GetRefDevice()->GetMapMode() == rMapMode )
		return;

	// Wenn RefDev == GlobalRefDev => eigenes anlegen!
	if ( !bOwnerOfRefDev && ( pRefDev == EE_DLL()->GetGlobalData()->GetStdRefDevice() ) )
	{
		pRefDev = new VirtualDevice;
		pRefDev->SetMapMode( MAP_TWIP );
		SetRefDevice( pRefDev );
		bOwnerOfRefDev = TRUE;
	}
	pRefDev->SetMapMode( rMapMode );
	nOnePixelInRef = (USHORT)pRefDev->PixelToLogic( Size( 1, 0 ) ).Width();
	if ( IsFormatted() )
	{
		FormatFullDoc();
		UpdateViews( (EditView*) 0);
	}
}

void ImpEditEngine::InitDoc( BOOL bKeepParaAttribs )
{
	USHORT nParas = aEditDoc.Count();
	for ( USHORT n = bKeepParaAttribs ? 1 : 0; n < nParas; n++ )
	{
		if ( aEditDoc[n]->GetStyleSheet() )
			EndListening( *aEditDoc[n]->GetStyleSheet(), FALSE );
	}

	if ( bKeepParaAttribs )
		aEditDoc.RemoveText();
	else
		aEditDoc.Clear();

	GetParaPortions().Reset();

	ParaPortion* pIniPortion = new ParaPortion( aEditDoc[0] );
	GetParaPortions().Insert( pIniPortion, 0 );

	bFormatted = FALSE;

	if ( IsCallParaInsertedOrDeleted() )
	{
		GetEditEnginePtr()->ParagraphDeleted( EE_PARA_ALL );
		GetEditEnginePtr()->ParagraphInserted( 0 );
	}

#ifndef SVX_LIGHT
	if ( GetStatus().DoOnlineSpelling() )
		aEditDoc.GetObject( 0 )->CreateWrongList();
#endif // !SVX_LIGHT
}

EditPaM ImpEditEngine::DeleteSelected( EditSelection aSel )
{
	EditPaM aPaM ( ImpDeleteSelection( aSel ) );
	return aPaM;
}

XubString ImpEditEngine::GetSelected( const EditSelection& rSel, const LineEnd eEnd  ) const
{
	XubString aText;
	if ( !rSel.HasRange() )
		return aText;

	String aSep = EditDoc::GetSepStr( eEnd );

	EditSelection aSel( rSel );
	aSel.Adjust( aEditDoc );

	ContentNode* pStartNode = aSel.Min().GetNode();
	ContentNode* pEndNode = aSel.Max().GetNode();
	USHORT nStartNode = aEditDoc.GetPos( pStartNode );
	USHORT nEndNode = aEditDoc.GetPos( pEndNode );

	DBG_ASSERT( nStartNode <= nEndNode, "Selektion nicht sortiert ?" );

	// ueber die Absaetze iterieren...
	for ( USHORT nNode = nStartNode; nNode <= nEndNode; nNode++	)
	{
		DBG_ASSERT( aEditDoc.SaveGetObject( nNode ), "Node nicht gefunden: GetSelected" );
		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();

		aText += aEditDoc.GetParaAsString( pNode, nStartPos, nEndPos );
		if ( nNode < nEndNode )
			aText += aSep;
	}
	return aText;
}

BOOL ImpEditEngine::MouseButtonDown( const MouseEvent& rMEvt, EditView* pView )
{
	GetSelEngine().SetCurView( pView );
	SetActiveView( pView );

	if ( GetAutoCompleteText().Len() )
		SetAutoCompleteText( String(), TRUE );

	GetSelEngine().SelMouseButtonDown( rMEvt );
	// Sonderbehandlungen
	EditSelection aCurSel( pView->pImpEditView->GetEditSelection() );
	if ( !rMEvt.IsShift() )
	{
		if ( rMEvt.GetClicks() == 2 )
		{
			// damit die SelectionEngine weiss, dass Anker.
			aSelEngine.CursorPosChanging( TRUE, FALSE );

			EditSelection aNewSelection( SelectWord( aCurSel ) );
			pView->pImpEditView->DrawSelection();
			pView->pImpEditView->SetEditSelection( aNewSelection );
			pView->pImpEditView->DrawSelection();
			pView->ShowCursor( TRUE, TRUE );
		}
		else if ( rMEvt.GetClicks() == 3 )
		{
			// damit die SelectionEngine weiss, dass Anker.
			aSelEngine.CursorPosChanging( TRUE, FALSE );

			EditSelection aNewSelection( aCurSel );
			aNewSelection.Min().SetIndex( 0 );
			aNewSelection.Max().SetIndex( aCurSel.Min().GetNode()->Len() );
			pView->pImpEditView->DrawSelection();
			pView->pImpEditView->SetEditSelection( aNewSelection );
			pView->pImpEditView->DrawSelection();
			pView->ShowCursor( TRUE, TRUE );
		}
	}
	return TRUE;
}

void ImpEditEngine::Command( const CommandEvent& rCEvt, EditView* pView )
{
#ifndef SVX_LIGHT
	GetSelEngine().SetCurView( pView );
	SetActiveView( pView );
    if ( rCEvt.GetCommand() == COMMAND_VOICE )
	{
		const CommandVoiceData* pData = rCEvt.GetVoiceData();
		if ( pData->GetType() == VOICECOMMANDTYPE_DICTATION )
		{
			// Funktionen auf KeyEvents umbiegen, wenn keine entsprechende
			// Methode an EditView/EditEngine, damit Undo konsistent bleibt.

			SfxPoolItem* pNewAttr = NULL;

			switch ( pData->GetCommand() )
			{
				case DICTATIONCOMMAND_UNKNOWN:
				{
					pView->InsertText( pData->GetText() );
				}
				break;
				case DICTATIONCOMMAND_NEWPARAGRAPH:
				{
					pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_RETURN, 0 ) ) );
				}
				break;
				case DICTATIONCOMMAND_NEWLINE:
				{
					pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_RETURN, KEY_SHIFT ) ) );
				}
				break;
				case DICTATIONCOMMAND_TAB:
				{
					pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_TAB, 0 ) ) );
				}
				break;
				case DICTATIONCOMMAND_LEFT:
				{
					pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_LEFT, KEY_MOD1  ) ) );
				}
				break;
				case DICTATIONCOMMAND_RIGHT:
				{
					pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_RIGHT, KEY_MOD1  ) ) );
				}
				break;
				case DICTATIONCOMMAND_UP:
				{
					pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_UP, 0 ) ) );
				}
				break;
				case DICTATIONCOMMAND_DOWN:
				{
					pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_UP, 0 ) ) );
				}
				break;
				case DICTATIONCOMMAND_UNDO:
				{
					pView->Undo();
				}
				break;
				case DICTATIONCOMMAND_DEL:
				{
					pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_LEFT, KEY_MOD1|KEY_SHIFT  ) ) );
					pView->DeleteSelected();
				}
				break;
				case DICTATIONCOMMAND_BOLD_ON:
				{
					pNewAttr = new SvxWeightItem( WEIGHT_BOLD, EE_CHAR_WEIGHT );
				}
				break;
				case DICTATIONCOMMAND_BOLD_OFF:
				{
					pNewAttr = new SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT );
				}
				break;
				case DICTATIONCOMMAND_ITALIC_ON:
				{
					pNewAttr = new SvxPostureItem( ITALIC_NORMAL, EE_CHAR_ITALIC );
				}
				break;
				case DICTATIONCOMMAND_ITALIC_OFF:
				{
					pNewAttr = new SvxPostureItem( ITALIC_NORMAL, EE_CHAR_ITALIC );
				}
				break;
				case DICTATIONCOMMAND_UNDERLINE_ON:
				{
					pNewAttr = new SvxUnderlineItem( UNDERLINE_SINGLE, EE_CHAR_UNDERLINE );
				}
				break;
				case DICTATIONCOMMAND_UNDERLINE_OFF:
				{
					pNewAttr = new SvxUnderlineItem( UNDERLINE_NONE, EE_CHAR_UNDERLINE );
				}
				break;
			}

			if ( pNewAttr )
			{
				SfxItemSet aSet( GetEmptyItemSet() );
				aSet.Put( *pNewAttr );
				pView->SetAttribs( aSet );
				delete pNewAttr;
			}
		}
	}
	else if ( rCEvt.GetCommand() == COMMAND_STARTEXTTEXTINPUT )
	{
		pView->DeleteSelected();
		delete mpIMEInfos;
        EditPaM aPaM = pView->GetImpEditView()->GetEditSelection().Max();
        String aOldTextAfterStartPos = aPaM.GetNode()->Copy( aPaM.GetIndex() );
        USHORT nMax = aOldTextAfterStartPos.Search( CH_FEATURE );
        if ( nMax != STRING_NOTFOUND )  // don't overwrite features!
            aOldTextAfterStartPos.Erase( nMax );
	    mpIMEInfos = new ImplIMEInfos( aPaM, aOldTextAfterStartPos );
		mpIMEInfos->bWasCursorOverwrite = !pView->IsInsertMode();
        UndoActionStart( EDITUNDO_INSERT );
	}
	else if ( rCEvt.GetCommand() == COMMAND_ENDEXTTEXTINPUT )
	{
		DBG_ASSERT( mpIMEInfos, "COMMAND_ENDEXTTEXTINPUT => Kein Start ?" );
		if( mpIMEInfos )
		{
			// #102812# convert quotes in IME text
			// works on the last input character, this is escpecially in Korean text often done
			// quotes that are inside of the string are not replaced!
			// Borrowed from sw: edtwin.cxx
			if ( mpIMEInfos->nLen )
			{
				EditSelection aSel( mpIMEInfos->aPos );
				aSel.Min().GetIndex() += mpIMEInfos->nLen-1;
				aSel.Max().GetIndex() =
                    aSel.Max().GetIndex() + mpIMEInfos->nLen;
				// #102812# convert quotes in IME text
				// works on the last input character, this is escpecially in Korean text often done
				// quotes that are inside of the string are not replaced!
				const sal_Unicode nCharCode = aSel.Min().GetNode()->GetChar( aSel.Min().GetIndex() );
				if ( ( GetStatus().DoAutoCorrect() ) && ( ( nCharCode == '\"' ) || ( nCharCode == '\'' ) ) )
				{
					aSel = DeleteSelected( aSel );
					aSel = AutoCorrect( aSel, nCharCode, mpIMEInfos->bWasCursorOverwrite );
					pView->pImpEditView->SetEditSelection( aSel );
				}
			}

			ParaPortion* pPortion = FindParaPortion( mpIMEInfos->aPos.GetNode() );
			pPortion->MarkSelectionInvalid( mpIMEInfos->aPos.GetIndex(), 0 );

			BOOL bWasCursorOverwrite = mpIMEInfos->bWasCursorOverwrite;

			delete mpIMEInfos;
			mpIMEInfos = NULL;

			FormatAndUpdate( pView );

			pView->SetInsertMode( !bWasCursorOverwrite );
		}
        UndoActionEnd( EDITUNDO_INSERT );
	}
	else if ( rCEvt.GetCommand() == COMMAND_EXTTEXTINPUT )
	{
		DBG_ASSERT( mpIMEInfos, "COMMAND_EXTTEXTINPUT => Kein Start ?" );
		if( mpIMEInfos )
		{
			const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();

			if ( !pData->IsOnlyCursorChanged() )
			{
				EditSelection aSel( mpIMEInfos->aPos );
				aSel.Max().GetIndex() =
                    aSel.Max().GetIndex() + mpIMEInfos->nLen;
				aSel = DeleteSelected( aSel );
				aSel = ImpInsertText( aSel, pData->GetText() );

                if ( mpIMEInfos->bWasCursorOverwrite )
                {
                    USHORT nOldIMETextLen = mpIMEInfos->nLen;
                    USHORT nNewIMETextLen = pData->GetText().Len();

                    if ( ( nOldIMETextLen > nNewIMETextLen ) &&
                         ( nNewIMETextLen < mpIMEInfos->aOldTextAfterStartPos.Len() ) )
                    {
                        // restore old characters
                        USHORT nRestore = nOldIMETextLen - nNewIMETextLen;
                        EditPaM aPaM( mpIMEInfos->aPos );
                        aPaM.GetIndex() = aPaM.GetIndex() + nNewIMETextLen;
                        ImpInsertText( aPaM, mpIMEInfos->aOldTextAfterStartPos.Copy( nNewIMETextLen, nRestore ) );
                    }
                    else if ( ( nOldIMETextLen < nNewIMETextLen ) &&
                              ( nOldIMETextLen < mpIMEInfos->aOldTextAfterStartPos.Len() ) )
                    {
                        // overwrite
                        USHORT nOverwrite = nNewIMETextLen - nOldIMETextLen;
                        if ( ( nOldIMETextLen + nOverwrite ) > mpIMEInfos->aOldTextAfterStartPos.Len() )
                            nOverwrite = mpIMEInfos->aOldTextAfterStartPos.Len() - nOldIMETextLen;
                        DBG_ASSERT( nOverwrite && (nOverwrite < 0xFF00), "IME Overwrite?!" );
                        EditPaM aPaM( mpIMEInfos->aPos );
                        aPaM.GetIndex() = aPaM.GetIndex() + nNewIMETextLen;
                        EditSelection _aSel( aPaM );
                        _aSel.Max().GetIndex() =
                            _aSel.Max().GetIndex() + nOverwrite;
                        DeleteSelected( _aSel );
                    }
                }
				if ( pData->GetTextAttr() )
				{
					mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().Len() );
					mpIMEInfos->bCursor = pData->IsCursorVisible();
				}
				else
				{
					mpIMEInfos->DestroyAttribs();
					mpIMEInfos->nLen = pData->GetText().Len();
				}

				ParaPortion* pPortion = FindParaPortion( mpIMEInfos->aPos.GetNode() );
				pPortion->MarkSelectionInvalid( mpIMEInfos->aPos.GetIndex(), 0 );
				FormatAndUpdate( pView );
			}

			EditSelection aNewSel = EditPaM( mpIMEInfos->aPos.GetNode(), mpIMEInfos->aPos.GetIndex()+pData->GetCursorPos() );
			pView->SetSelection( CreateESel( aNewSel ) );
			pView->SetInsertMode( !pData->IsCursorOverwrite() );

			if ( pData->IsCursorVisible() )
				pView->ShowCursor();
			else
				pView->HideCursor();
		}
	}
	else if ( rCEvt.GetCommand() == COMMAND_INPUTCONTEXTCHANGE )
	{
	}
	else if ( rCEvt.GetCommand() == COMMAND_CURSORPOS )
	{
		if ( mpIMEInfos && mpIMEInfos->nLen )
		{
			EditPaM aPaM( pView->pImpEditView->GetEditSelection().Max() );
			Rectangle aR1 = PaMtoEditCursor( aPaM, 0 );

			USHORT nInputEnd = mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen;

			if ( !IsFormatted() )
				FormatDoc();

			ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( GetEditDoc().GetPos( aPaM.GetNode() ) );
			USHORT nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), sal_True );
			EditLine* pLine = pParaPortion->GetLines().GetObject( nLine );
			if ( pLine && ( nInputEnd > pLine->GetEnd() ) )
				nInputEnd = pLine->GetEnd();
			Rectangle aR2 = PaMtoEditCursor( EditPaM( aPaM.GetNode(), nInputEnd ), GETCRSR_ENDOFLINE );
			Rectangle aRect = pView->GetImpEditView()->GetWindowPos( aR1 );
			pView->GetWindow()->SetCursorRect( &aRect, aR2.Left()-aR1.Right() );
		}
		else
		{
			pView->GetWindow()->SetCursorRect();
		}
	}
#endif // !SVX_LIGHT

	GetSelEngine().Command( rCEvt );
}

BOOL ImpEditEngine::MouseButtonUp( const MouseEvent& rMEvt, EditView* pView )
{
	GetSelEngine().SetCurView( pView );
	GetSelEngine().SelMouseButtonUp( rMEvt );
	bInSelection = FALSE;
	// Sonderbehandlungen
	EditSelection aCurSel( pView->pImpEditView->GetEditSelection() );
	if ( !aCurSel.HasRange() )
	{
		if ( ( rMEvt.GetClicks() == 1 ) && rMEvt.IsLeft() && !rMEvt.IsMod2() )
		{
			const SvxFieldItem* pFld = pView->GetFieldUnderMousePointer();
			if ( pFld )
			{
				EditPaM aPaM( aCurSel.Max() );
				USHORT nPara = GetEditDoc().GetPos( aPaM.GetNode() );
				GetEditEnginePtr()->FieldClicked( *pFld, nPara, aPaM.GetIndex() );
			}
		}
	}
	return TRUE;
}

BOOL ImpEditEngine::MouseMove( const MouseEvent& rMEvt, EditView* pView )
{
	// MouseMove wird sofort nach ShowQuickHelp() gerufen!
//	if ( GetAutoCompleteText().Len() )
//		SetAutoCompleteText( String(), TRUE );
	GetSelEngine().SetCurView( pView );
	GetSelEngine().SelMouseMove( rMEvt );
	return TRUE;
}

EditPaM ImpEditEngine::InsertText( EditSelection aSel, const XubString& rStr )
{
	EditPaM aPaM = ImpInsertText( aSel, rStr );
	return aPaM;
}

EditPaM ImpEditEngine::Clear()
{
	InitDoc( FALSE );

	EditPaM aPaM = aEditDoc.GetStartPaM();
	EditSelection aSel( aPaM );

	nCurTextHeight = 0;

	ResetUndoManager();

	for ( USHORT nView = aEditViews.Count(); nView; )
	{
		EditView* pView = aEditViews[--nView];
		DBG_CHKOBJ( pView, EditView, 0 );
		pView->pImpEditView->SetEditSelection( aSel );
	}

	return aPaM;
}

EditPaM ImpEditEngine::RemoveText()
{
	InitDoc( TRUE );

	EditPaM aStartPaM = aEditDoc.GetStartPaM();
	EditSelection aEmptySel( aStartPaM, aStartPaM );
	for ( USHORT nView = 0; nView < aEditViews.Count(); nView++ )
	{
		EditView* pView = aEditViews.GetObject(nView);
		DBG_CHKOBJ( pView, EditView, 0 );
		pView->pImpEditView->SetEditSelection( aEmptySel );
	}
	ResetUndoManager();
	return aEditDoc.GetStartPaM();
}


void ImpEditEngine::SetText( const XubString& rText )
{
	// RemoveText loescht die Undo-Liste!
	EditPaM aStartPaM = RemoveText();
	BOOL bUndoCurrentlyEnabled = IsUndoEnabled();
	// Der von Hand reingesteckte Text kann nicht vom Anwender rueckgaengig gemacht werden.
	EnableUndo( FALSE );

	EditSelection aEmptySel( aStartPaM, aStartPaM );
	EditPaM aPaM = aStartPaM;
	if ( rText.Len() )
		aPaM = ImpInsertText( aEmptySel, rText );

	for ( USHORT nView = 0; nView < aEditViews.Count(); nView++ )
	{
		EditView* pView = aEditViews[nView];
		DBG_CHKOBJ( pView, EditView, 0 );
		pView->pImpEditView->SetEditSelection( EditSelection( aPaM, aPaM ) );
		// Wenn kein Text, dann auch Kein Format&Update
		// => Der Text bleibt stehen.
		if ( !rText.Len() && GetUpdateMode() )
		{
			Rectangle aTmpRec( pView->GetOutputArea().TopLeft(),
								Size( aPaperSize.Width(), nCurTextHeight ) );
			aTmpRec.Intersection( pView->GetOutputArea() );
			pView->GetWindow()->Invalidate( aTmpRec );
		}
	}
	if( !rText.Len() )	// sonst muss spaeter noch invalidiert werden, !bFormatted reicht.
		nCurTextHeight = 0;
	EnableUndo( bUndoCurrentlyEnabled );
#ifndef SVX_LIGHT
	DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "Undo nach SetText?" );
#endif
}


const SfxItemSet& ImpEditEngine::GetEmptyItemSet()
{
	if ( !pEmptyItemSet )
	{
		pEmptyItemSet = new SfxItemSet( aEditDoc.GetItemPool(), EE_ITEMS_START, EE_ITEMS_END );
		for ( USHORT nWhich = EE_ITEMS_START; nWhich <= EE_CHAR_END; nWhich++)
		{
			pEmptyItemSet->ClearItem( nWhich );
		}
	}
	return *pEmptyItemSet;
}

//	----------------------------------------------------------------------
//	MISC
//	----------------------------------------------------------------------
void ImpEditEngine::CursorMoved( ContentNode* pPrevNode )
{
	// Leere Attribute loeschen, aber nur, wenn Absatz nicht leer!
	if ( pPrevNode->GetCharAttribs().HasEmptyAttribs() && pPrevNode->Len() )
		pPrevNode->GetCharAttribs().DeleteEmptyAttribs( aEditDoc.GetItemPool() );
}

void ImpEditEngine::TextModified()
{
	bFormatted = FALSE;

    if ( GetNotifyHdl().IsSet() )
    {
        EENotify aNotify( EE_NOTIFY_TEXTMODIFIED );
        aNotify.pEditEngine = GetEditEnginePtr();
        CallNotify( aNotify );
    }
}


void ImpEditEngine::ParaAttribsChanged( ContentNode* pNode )
{
	DBG_ASSERT( pNode, "ParaAttribsChanged: Welcher?" );

	aEditDoc.SetModified( TRUE );
	bFormatted = FALSE;

	ParaPortion* pPortion = FindParaPortion( pNode );
	DBG_ASSERT( pPortion, "ParaAttribsChanged: Portion?" );
	pPortion->MarkSelectionInvalid( 0, pNode->Len() );

	USHORT nPara = aEditDoc.GetPos( pNode );
	pEditEngine->ParaAttribsChanged( nPara );

	ParaPortion* pNextPortion = GetParaPortions().SaveGetObject( nPara+1 );
	// => wird sowieso noch formatiert, wenn Invalid.
	if ( pNextPortion && !pNextPortion->IsInvalid() )
		CalcHeight( pNextPortion );
}

//	----------------------------------------------------------------------
//	Cursorbewegungen
//	----------------------------------------------------------------------

EditSelection ImpEditEngine::MoveCursor( const KeyEvent& rKeyEvent, EditView* pEditView )
{
	// Eigentlich nur bei Up/Down noetig, aber was solls.
	CheckIdleFormatter();

	EditPaM aPaM( pEditView->pImpEditView->GetEditSelection().Max() );

	EditPaM aOldPaM( aPaM );

    TextDirectionality eTextDirection = TextDirectionality_LeftToRight_TopToBottom;
	if ( IsVertical() )
        eTextDirection = TextDirectionality_TopToBottom_RightToLeft;
    else if ( IsRightToLeft( GetEditDoc().GetPos( aPaM.GetNode() ) ) )
        eTextDirection = TextDirectionality_RightToLeft_TopToBottom;

    KeyEvent aTranslatedKeyEvent = rKeyEvent.LogicalTextDirectionality( eTextDirection );

    BOOL bCtrl = aTranslatedKeyEvent.GetKeyCode().IsMod1() ? TRUE : FALSE;
	USHORT nCode = aTranslatedKeyEvent.GetKeyCode().GetCode();

    if ( DoVisualCursorTraveling( aPaM.GetNode() ) )
    {
        // Only for simple cursor movement...
        if ( !bCtrl && ( ( nCode == KEY_LEFT ) || ( nCode == KEY_RIGHT ) ) )
        {
            aPaM = CursorVisualLeftRight( pEditView, aPaM, rKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL, rKeyEvent.GetKeyCode().GetCode() == KEY_LEFT );
	        nCode = 0;  // skip switch statement
        }
        /*
        else if ( !bCtrl && ( ( nCode == KEY_HOME ) || ( nCode == KEY_END ) ) )
        {
            aPaM = CursorVisualStartEnd( pEditView, aPaM, nCode == KEY_HOME );
	        nCode = 0;  // skip switch statement
        }
        */
    }

    bool bKeyModifySelection = aTranslatedKeyEvent.GetKeyCode().IsShift();
	switch ( nCode )
	{
		case KEY_UP:		aPaM = CursorUp( aPaM, pEditView );
							break;
		case KEY_DOWN:		aPaM = CursorDown( aPaM, pEditView );
							break;
        case KEY_LEFT:		aPaM = bCtrl ? WordLeft( aPaM ) : CursorLeft( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL );
							break;
		case KEY_RIGHT: 	aPaM = bCtrl ? WordRight( aPaM ) : CursorRight( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL );
							break;
		case KEY_HOME:		aPaM = bCtrl ? CursorStartOfDoc() : CursorStartOfLine( aPaM );
							break;
		case KEY_END:		aPaM = bCtrl ? CursorEndOfDoc() : CursorEndOfLine( aPaM );
							break;
		case KEY_PAGEUP:	aPaM = bCtrl ? CursorStartOfDoc() : PageUp( aPaM, pEditView );
							break;
		case KEY_PAGEDOWN:	aPaM = bCtrl ? CursorEndOfDoc() : PageDown( aPaM, pEditView );
							break;
        case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_LINE:
                            aPaM = CursorStartOfLine( aPaM );
                            bKeyModifySelection = false;
                            break;
        case com::sun::star::awt::Key::MOVE_TO_END_OF_LINE:
                            aPaM = CursorEndOfLine( aPaM );
                            bKeyModifySelection = false;
                            break;
        case com::sun::star::awt::Key::MOVE_WORD_BACKWARD:
                            aPaM = WordLeft( aPaM );
                            bKeyModifySelection = false;
                            break;
        case com::sun::star::awt::Key::MOVE_WORD_FORWARD:
                            aPaM = WordRight( aPaM );
                            bKeyModifySelection = false;
                            break;
        case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
                            aPaM = CursorStartOfParagraph( aPaM );
                            if( aPaM == aOldPaM )
                            {
                                aPaM = CursorLeft( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
                                aPaM = CursorStartOfParagraph( aPaM );
                            }
                            bKeyModifySelection = false;
                            break;
        case com::sun::star::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
                            aPaM = CursorEndOfParagraph( aPaM );
                            if( aPaM == aOldPaM )
                            {
                                aPaM = CursorRight( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
                                aPaM = CursorEndOfParagraph( aPaM );
                            }
                            bKeyModifySelection = false;
                            break;
        case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
                            aPaM = CursorStartOfDoc();
                            bKeyModifySelection = false;
                            break;
        case com::sun::star::awt::Key::MOVE_TO_END_OF_DOCUMENT:
                            aPaM = CursorEndOfDoc();
                            bKeyModifySelection = false;
                            break;
        case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_LINE:
                            aPaM = CursorStartOfLine( aPaM );
                            bKeyModifySelection = true;
                            break;
        case com::sun::star::awt::Key::SELECT_TO_END_OF_LINE:
                            aPaM = CursorEndOfLine( aPaM );
                            bKeyModifySelection = true;
                            break;
        case com::sun::star::awt::Key::SELECT_BACKWARD:
                            aPaM = CursorLeft( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
                            bKeyModifySelection = true;
                            break;
        case com::sun::star::awt::Key::SELECT_FORWARD:
                            aPaM = CursorRight( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
                            bKeyModifySelection = true;
                            break;
        case com::sun::star::awt::Key::SELECT_WORD_BACKWARD:
                            aPaM = WordLeft( aPaM );
                            bKeyModifySelection = true;
                            break;
        case com::sun::star::awt::Key::SELECT_WORD_FORWARD:
                            aPaM = WordRight( aPaM );
                            bKeyModifySelection = true;
                            break;
        case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
                            aPaM = CursorStartOfParagraph( aPaM );
                            if( aPaM == aOldPaM )
                            {
                                aPaM = CursorLeft( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
                                aPaM = CursorStartOfParagraph( aPaM );
                            }
                            bKeyModifySelection = true;
                            break;
        case com::sun::star::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
                            aPaM = CursorEndOfParagraph( aPaM );
                            if( aPaM == aOldPaM )
                            {
                                aPaM = CursorRight( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
                                aPaM = CursorEndOfParagraph( aPaM );
                            }
                            bKeyModifySelection = true;
                            break;
        case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
                            aPaM = CursorStartOfDoc();
                            bKeyModifySelection = true;
                            break;
        case com::sun::star::awt::Key::SELECT_TO_END_OF_DOCUMENT:
                            aPaM = CursorEndOfDoc();
                            bKeyModifySelection = true;
                            break;
	}

	if ( aOldPaM != aPaM )
	{
		CursorMoved( aOldPaM.GetNode() );
		if ( aStatus.NotifyCursorMovements() && ( aOldPaM.GetNode() != aPaM.GetNode() ) )
		{
			aStatus.GetStatusWord() = aStatus.GetStatusWord() | EE_STAT_CRSRLEFTPARA;
			aStatus.GetPrevParagraph() = aEditDoc.GetPos( aOldPaM.GetNode() );
		}
	}
	else
		aStatus.GetStatusWord() = aStatus.GetStatusWord() | EE_STAT_CRSRMOVEFAIL;

	// Bewirkt evtl. ein CreateAnchor oder Deselection all
	aSelEngine.SetCurView( pEditView );
	aSelEngine.CursorPosChanging( bKeyModifySelection, aTranslatedKeyEvent.GetKeyCode().IsMod1() );
	EditPaM aOldEnd( pEditView->pImpEditView->GetEditSelection().Max() );
	pEditView->pImpEditView->GetEditSelection().Max() = aPaM;
	if ( bKeyModifySelection )
	{
		// Dann wird die Selektion erweitert...
		EditSelection aTmpNewSel( aOldEnd, aPaM );
		pEditView->pImpEditView->DrawSelection( aTmpNewSel );
	}
	else
		pEditView->pImpEditView->GetEditSelection().Min() = aPaM;

	return pEditView->pImpEditView->GetEditSelection();
}

EditPaM ImpEditEngine::CursorVisualStartEnd( EditView* pEditView, const EditPaM& rPaM, BOOL bStart )
{
    EditPaM aPaM( rPaM );

    USHORT nPara = GetEditDoc().GetPos( aPaM.GetNode() );
    ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );

    USHORT nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), sal_False );
    EditLine* pLine = pParaPortion->GetLines().GetObject( nLine );
    BOOL bEmptyLine = pLine->GetStart() == pLine->GetEnd();

    pEditView->pImpEditView->nExtraCursorFlags = 0;

    if ( !bEmptyLine )
    {
        String aLine( *aPaM.GetNode(), pLine->GetStart(), pLine->GetEnd() - pLine->GetStart() );
//        USHORT nPosInLine = aPaM.GetIndex() - pLine->GetStart();

        const sal_Unicode* pLineString = aLine.GetBuffer();

        UErrorCode nError = U_ZERO_ERROR;
        UBiDi* pBidi = ubidi_openSized( aLine.Len(), 0, &nError );

        const UBiDiLevel  nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/;
        ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(pLineString), aLine.Len(), nBidiLevel, NULL, &nError );	// UChar != sal_Unicode in MinGW

        USHORT nVisPos = bStart ? 0 : aLine.Len()-1;
        USHORT nLogPos = (USHORT)ubidi_getLogicalIndex( pBidi, nVisPos, &nError );

        ubidi_close( pBidi );

        aPaM.GetIndex() = nLogPos + pLine->GetStart();

        USHORT nTmp;
        USHORT nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nTmp, TRUE );
        TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
        USHORT nRTLLevel = pTextPortion->GetRightToLeft();
//        BOOL bParaRTL = IsRightToLeft( nPara );
        BOOL bPortionRTL = nRTLLevel%2 ? TRUE : FALSE;

        if ( bStart )
        {
            pEditView->pImpEditView->SetCursorBidiLevel( bPortionRTL ? 0 : 1 );
            // Maybe we must be *behind* the character
            if ( bPortionRTL && pEditView->IsInsertMode() )
                aPaM.GetIndex()++;
        }
        else
        {
            pEditView->pImpEditView->SetCursorBidiLevel( bPortionRTL ? 1 : 0 );
            if ( !bPortionRTL && pEditView->IsInsertMode() )
                aPaM.GetIndex()++;
        }
    }

    return aPaM;
}

EditPaM ImpEditEngine::CursorVisualLeftRight( EditView* pEditView, const EditPaM& rPaM, USHORT nCharacterIteratorMode, BOOL bVisualToLeft )
{
    EditPaM aPaM( rPaM );

    USHORT nPara = GetEditDoc().GetPos( aPaM.GetNode() );
    ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );

    USHORT nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), sal_False );
    EditLine* pLine = pParaPortion->GetLines().GetObject( nLine );
    BOOL bEmptyLine = pLine->GetStart() == pLine->GetEnd();

//    USHORT nCurrentCursorFlags = pEditView->pImpEditView->nExtraCursorFlags;
    pEditView->pImpEditView->nExtraCursorFlags = 0;

    BOOL bParaRTL = IsRightToLeft( nPara );

    BOOL bDone = FALSE;

    if ( bEmptyLine )
    {
        if ( bVisualToLeft )
        {
            aPaM = CursorUp( aPaM, pEditView );
            if ( aPaM != rPaM )
                aPaM = CursorVisualStartEnd( pEditView, aPaM, FALSE );
        }
        else
        {
            aPaM = CursorDown( aPaM, pEditView );
            if ( aPaM != rPaM )
                aPaM = CursorVisualStartEnd( pEditView, aPaM, TRUE );
        }

        bDone = TRUE;
    }

    BOOL bLogicalBackward = bParaRTL ? !bVisualToLeft : bVisualToLeft;

    if ( !bDone && pEditView->IsInsertMode() )
    {
        // Check if we are within a portion and don't have overwrite mode, then it's easy...
        USHORT nPortionStart;
        USHORT nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, FALSE );
        TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );

        BOOL bPortionBoundary = ( aPaM.GetIndex() == nPortionStart ) || ( aPaM.GetIndex() == (nPortionStart+pTextPortion->GetLen()) );
        USHORT nRTLLevel = pTextPortion->GetRightToLeft();

        // Portion boundary doesn't matter if both have same RTL level
        USHORT nRTLLevelNextPortion = 0xFFFF;
        if ( bPortionBoundary && aPaM.GetIndex() && ( aPaM.GetIndex() < aPaM.GetNode()->Len() ) )
        {
            USHORT nTmp;
            USHORT nNextTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex()+1, nTmp, bLogicalBackward ? FALSE : TRUE );
            TextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nNextTextPortion );
            nRTLLevelNextPortion = pNextTextPortion->GetRightToLeft();
        }

        if ( !bPortionBoundary || ( nRTLLevel == nRTLLevelNextPortion ) )
        {
            if ( ( bVisualToLeft && !(nRTLLevel%2) ) || ( !bVisualToLeft && (nRTLLevel%2) ) )
            {
                aPaM = CursorLeft( aPaM, nCharacterIteratorMode );
                pEditView->pImpEditView->SetCursorBidiLevel( 1 );
            }
            else
            {
                aPaM = CursorRight( aPaM, nCharacterIteratorMode );
                pEditView->pImpEditView->SetCursorBidiLevel( 0 );
            }
            bDone = TRUE;
        }
    }

    if ( !bDone )
    {
        BOOL bGotoStartOfNextLine = FALSE;
        BOOL bGotoEndOfPrevLine = FALSE;

        String aLine( *aPaM.GetNode(), pLine->GetStart(), pLine->GetEnd() - pLine->GetStart() );
        USHORT nPosInLine = aPaM.GetIndex() - pLine->GetStart();

        const sal_Unicode* pLineString = aLine.GetBuffer();

        UErrorCode nError = U_ZERO_ERROR;
        UBiDi* pBidi = ubidi_openSized( aLine.Len(), 0, &nError );

        const UBiDiLevel  nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/;
        ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(pLineString), aLine.Len(), nBidiLevel, NULL, &nError );	// UChar != sal_Unicode in MinGW

        if ( !pEditView->IsInsertMode() )
        {
            BOOL bEndOfLine = nPosInLine == aLine.Len();
            USHORT nVisPos = (USHORT)ubidi_getVisualIndex( pBidi, !bEndOfLine ? nPosInLine : nPosInLine-1, &nError );
            if ( bVisualToLeft )
            {
                bGotoEndOfPrevLine = nVisPos == 0;
                if ( !bEndOfLine )
                    nVisPos--;
            }
            else
            {
                bGotoStartOfNextLine = nVisPos == (aLine.Len() - 1);
                if ( !bEndOfLine )
                    nVisPos++;
            }

            if ( !bGotoEndOfPrevLine && !bGotoStartOfNextLine )
            {
                USHORT nLogPos = (USHORT)ubidi_getLogicalIndex( pBidi, nVisPos, &nError );
                aPaM.GetIndex() = pLine->GetStart() + nLogPos;
                pEditView->pImpEditView->SetCursorBidiLevel( 0 );
            }
        }
        else
        {
            BOOL bWasBehind = FALSE;
            BOOL bBeforePortion = !nPosInLine || pEditView->pImpEditView->GetCursorBidiLevel() == 1;
            if ( nPosInLine && ( !bBeforePortion ) ) // before the next portion
                bWasBehind = TRUE;  // step one back, otherwise visual will be unusable when rtl portion follows.

            USHORT nPortionStart;
            USHORT nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, bBeforePortion );
            TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
            BOOL bRTLPortion = (pTextPortion->GetRightToLeft() % 2) != 0;

            // -1: We are 'behind' the character
            long nVisPos = (long)ubidi_getVisualIndex( pBidi, bWasBehind ? nPosInLine-1 : nPosInLine, &nError );
            if ( bVisualToLeft )
            {
                if ( !bWasBehind || bRTLPortion )
                    nVisPos--;
            }
            else
            {
                if ( bWasBehind || bRTLPortion || bBeforePortion )
                    nVisPos++;
//                if ( bWasBehind && bRTLPortion )
//                    nVisPos++;
            }

            bGotoEndOfPrevLine = nVisPos < 0;
            bGotoStartOfNextLine = nVisPos >= aLine.Len();

            if ( !bGotoEndOfPrevLine && !bGotoStartOfNextLine )
            {
                USHORT nLogPos = (USHORT)ubidi_getLogicalIndex( pBidi, nVisPos, &nError );

/*
                if ( nLogPos == aPaM.GetIndex() )
                {
                    if ( bVisualToLeft )
                        bGotoEndOfPrevLine = TRUE;
                    else
                        bGotoStartOfNextLine = TRUE;
                }
                else
*/
                {
                    aPaM.GetIndex() = pLine->GetStart() + nLogPos;

                    // RTL portion, stay visually on the left side.
                    USHORT _nPortionStart;
                    // USHORT nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, !bRTLPortion );
                    USHORT _nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), _nPortionStart, TRUE );
                    TextPortion* _pTextPortion = pParaPortion->GetTextPortions().GetObject( _nTextPortion );
                    if ( bVisualToLeft && !bRTLPortion && ( _pTextPortion->GetRightToLeft() % 2 ) )
                        aPaM.GetIndex()++;
                    else if ( !bVisualToLeft && bRTLPortion && ( bWasBehind || !(_pTextPortion->GetRightToLeft() % 2 )) )
                        aPaM.GetIndex()++;

                    pEditView->pImpEditView->SetCursorBidiLevel( _nPortionStart );
                }
            }
        }

        ubidi_close( pBidi );

        if ( bGotoEndOfPrevLine )
        {
            aPaM = CursorUp( aPaM, pEditView );
            if ( aPaM != rPaM )
                aPaM = CursorVisualStartEnd( pEditView, aPaM, FALSE );
        }
        else if ( bGotoStartOfNextLine )
        {
            aPaM = CursorDown( aPaM, pEditView );
            if ( aPaM != rPaM )
                aPaM = CursorVisualStartEnd( pEditView, aPaM, TRUE );
        }
    }
    return aPaM;
}


EditPaM ImpEditEngine::CursorLeft( const EditPaM& rPaM, USHORT nCharacterIteratorMode )
{
    EditPaM aCurPaM( rPaM );
	EditPaM aNewPaM( aCurPaM );

	if ( aCurPaM.GetIndex() )
	{
		sal_Int32 nCount = 1;
		uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
 		aNewPaM.SetIndex( (USHORT)_xBI->previousCharacters( *aNewPaM.GetNode(), aNewPaM.GetIndex(), GetLocale( aNewPaM ), nCharacterIteratorMode, nCount, nCount ) );
	}
	else
	{
		ContentNode* pNode = aCurPaM.GetNode();
		pNode = GetPrevVisNode( pNode );
		if ( pNode )
		{
			aNewPaM.SetNode( pNode );
			aNewPaM.SetIndex( pNode->Len() );
		}
	}

	return aNewPaM;
}

EditPaM ImpEditEngine::CursorRight( const EditPaM& rPaM, USHORT nCharacterIteratorMode )
{
    EditPaM aCurPaM( rPaM );
	EditPaM aNewPaM( aCurPaM );

    if ( aCurPaM.GetIndex() < aCurPaM.GetNode()->Len() )
	{
		uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
		sal_Int32 nCount = 1;
		aNewPaM.SetIndex( (USHORT)_xBI->nextCharacters( *aNewPaM.GetNode(), aNewPaM.GetIndex(), GetLocale( aNewPaM ), nCharacterIteratorMode, nCount, nCount ) );
	}
	else
	{
		ContentNode* pNode = aCurPaM.GetNode();
		pNode = GetNextVisNode( pNode );
		if ( pNode )
		{
			aNewPaM.SetNode( pNode );
			aNewPaM.SetIndex( 0 );
		}
	}

	return aNewPaM;
}

EditPaM ImpEditEngine::CursorUp( const EditPaM& rPaM, EditView* pView )
{
	DBG_ASSERT( pView, "Keine View - Keine Cursorbewegung!" );

	ParaPortion* pPPortion = FindParaPortion( rPaM.GetNode() );
	DBG_ASSERT( pPPortion, "Keine passende Portion gefunden: CursorUp" );
	USHORT nLine = pPPortion->GetLineNumber( rPaM.GetIndex() );
	EditLine* pLine = pPPortion->GetLines().GetObject( nLine );

	long nX;
	if ( pView->pImpEditView->nTravelXPos == TRAVEL_X_DONTKNOW )
	{
		nX = GetXPos( pPPortion, pLine, rPaM.GetIndex() );
		pView->pImpEditView->nTravelXPos = nX+nOnePixelInRef;
	}
	else
		nX = pView->pImpEditView->nTravelXPos;

	EditPaM aNewPaM( rPaM );
	if ( nLine )	// gleicher Absatz
	{
		EditLine* pPrevLine = pPPortion->GetLines().GetObject(nLine-1);
		aNewPaM.SetIndex( GetChar( pPPortion, pPrevLine, nX ) );
		// Wenn davor eine autom.Umgebrochene Zeile, und ich muss genau an das
		// Ende dieser Zeile, landet der Cursor in der aktuellen Zeile am Anfang
		// Siehe Problem: Letztes Zeichen einer autom.umgebr. Zeile = Cursor
		if ( aNewPaM.GetIndex() && ( aNewPaM.GetIndex() == pLine->GetStart() ) )
			aNewPaM = CursorLeft( aNewPaM );
	}
	else	// vorheriger Absatz
	{
		ParaPortion* pPrevPortion = GetPrevVisPortion( pPPortion );
		if ( pPrevPortion )
		{
			pLine = pPrevPortion->GetLines().GetObject( pPrevPortion->GetLines().Count()-1 );
			DBG_ASSERT( pLine, "Zeile davor nicht gefunden: CursorUp" );
			aNewPaM.SetNode( pPrevPortion->GetNode() );
			aNewPaM.SetIndex( GetChar( pPrevPortion, pLine, nX+nOnePixelInRef ) );
		}
	}

	return aNewPaM;
}

EditPaM ImpEditEngine::CursorDown( const EditPaM& rPaM, EditView* pView )
{
	DBG_ASSERT( pView, "Keine View - Keine Cursorbewegung!" );

	ParaPortion* pPPortion = FindParaPortion( rPaM.GetNode() );
	DBG_ASSERT( pPPortion, "Keine passende Portion gefunden: CursorDown" );
	USHORT nLine = pPPortion->GetLineNumber( rPaM.GetIndex() );

	long nX;
	if ( pView->pImpEditView->nTravelXPos == TRAVEL_X_DONTKNOW )
	{
		EditLine* pLine = pPPortion->GetLines().GetObject(nLine);
		nX = GetXPos( pPPortion, pLine, rPaM.GetIndex() );
		pView->pImpEditView->nTravelXPos = nX+nOnePixelInRef;
	}
	else
		nX = pView->pImpEditView->nTravelXPos;

	EditPaM aNewPaM( rPaM );
	if ( nLine < pPPortion->GetLines().Count()-1 )
	{
		EditLine* pNextLine = pPPortion->GetLines().GetObject(nLine+1);
		aNewPaM.SetIndex( GetChar( pPPortion, pNextLine, nX ) );
		// Sonderbehandlung siehe CursorUp...
		if ( ( aNewPaM.GetIndex() == pNextLine->GetEnd() ) && ( aNewPaM.GetIndex() > pNextLine->GetStart() ) && ( aNewPaM.GetIndex() < pPPortion->GetNode()->Len() ) )
			aNewPaM = CursorLeft( aNewPaM );
	}
	else	// naechster Absatz
	{
		ParaPortion* pNextPortion = GetNextVisPortion( pPPortion );
		if ( pNextPortion )
		{
			EditLine* pLine = pNextPortion->GetLines().GetObject(0);
			DBG_ASSERT( pLine, "Zeile davor nicht gefunden: CursorUp" );
			aNewPaM.SetNode( pNextPortion->GetNode() );
			// Nie ganz ans Ende wenn mehrere Zeilen, da dann eine
			// Zeile darunter der Cursor angezeigt wird.
			aNewPaM.SetIndex( GetChar( pNextPortion, pLine, nX+nOnePixelInRef ) );
			if ( ( aNewPaM.GetIndex() == pLine->GetEnd() ) && ( aNewPaM.GetIndex() > pLine->GetStart() ) && ( pNextPortion->GetLines().Count() > 1 ) )
				aNewPaM = CursorLeft( aNewPaM );
		}
	}

	return aNewPaM;
}

EditPaM ImpEditEngine::CursorStartOfLine( const EditPaM& rPaM )
{
	ParaPortion* pCurPortion = FindParaPortion( rPaM.GetNode() );
	DBG_ASSERT( pCurPortion, "Keine Portion fuer den PaM ?" );
	USHORT nLine = pCurPortion->GetLineNumber( rPaM.GetIndex() );
	EditLine* pLine = pCurPortion->GetLines().GetObject(nLine);
	DBG_ASSERT( pLine, "Aktuelle Zeile nicht gefunden ?!" );

	EditPaM aNewPaM( rPaM );
	aNewPaM.SetIndex( pLine->GetStart() );
	return aNewPaM;
}

EditPaM ImpEditEngine::CursorEndOfLine( const EditPaM& rPaM )
{
	ParaPortion* pCurPortion = FindParaPortion( rPaM.GetNode() );
	DBG_ASSERT( pCurPortion, "Keine Portion fuer den PaM ?" );
	USHORT nLine = pCurPortion->GetLineNumber( rPaM.GetIndex() );
	EditLine* pLine = pCurPortion->GetLines().GetObject(nLine);
	DBG_ASSERT( pLine, "Aktuelle Zeile nicht gefunden ?!" );

	EditPaM aNewPaM( rPaM );
	aNewPaM.SetIndex( pLine->GetEnd() );
	if ( pLine->GetEnd() > pLine->GetStart() )
	{
//		xub_Unicode cLastChar = aNewPaM.GetNode()->GetChar( aNewPaM.GetIndex()-1 );
		if ( aNewPaM.GetNode()->IsFeature( aNewPaM.GetIndex() - 1 ) )
		{
			// Bei einem weichen Umbruch muss ich davor stehen!
			EditCharAttrib* pNextFeature = aNewPaM.GetNode()->GetCharAttribs().FindFeature( aNewPaM.GetIndex()-1 );
			if ( pNextFeature && ( pNextFeature->GetItem()->Which() == EE_FEATURE_LINEBR ) )
				aNewPaM = CursorLeft( aNewPaM );
		}
		else if ( ( aNewPaM.GetNode()->GetChar( aNewPaM.GetIndex() - 1 ) == ' ' ) && ( aNewPaM.GetIndex() != aNewPaM.GetNode()->Len() ) )
		{
			// Bei einem Blank in einer autom. umgebrochenen Zeile macht es Sinn,
			// davor zu stehen, da der Anwender hinter das Wort will.
			// Wenn diese geaendert wird, Sonderbehandlung fuer Pos1 nach End!
			aNewPaM = CursorLeft( aNewPaM );
		}
	}
	return aNewPaM;
}

EditPaM ImpEditEngine::CursorStartOfParagraph( const EditPaM& rPaM )
{
	EditPaM aPaM( rPaM.GetNode(), 0 );
	return aPaM;
}

EditPaM ImpEditEngine::CursorEndOfParagraph( const EditPaM& rPaM )
{
	EditPaM aPaM( rPaM.GetNode(), rPaM.GetNode()->Len() );
	return aPaM;
}

EditPaM ImpEditEngine::CursorStartOfDoc()
{
	EditPaM aPaM( aEditDoc.SaveGetObject( 0 ), 0 );
	return aPaM;
}

EditPaM ImpEditEngine::CursorEndOfDoc()
{
	ContentNode* pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count()-1 );
	ParaPortion* pLastPortion = GetParaPortions().SaveGetObject( aEditDoc.Count()-1 );
	DBG_ASSERT( pLastNode && pLastPortion, "CursorEndOfDoc: Node oder Portion nicht gefunden" );

	if ( !pLastPortion->IsVisible() )
	{
		pLastNode = GetPrevVisNode( pLastPortion->GetNode() );
		DBG_ASSERT( pLastNode, "Kein sichtbarer Absatz?" );
		if ( !pLastNode )
			pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count()-1 );
	}

	EditPaM aPaM( pLastNode, pLastNode->Len() );
	return aPaM;
}

EditPaM ImpEditEngine::PageUp( const EditPaM& rPaM, EditView* pView )
{
	Rectangle aRec = PaMtoEditCursor( rPaM );
	Point aTopLeft = aRec.TopLeft();
	aTopLeft.Y() -= pView->GetVisArea().GetHeight() *9/10;
	aTopLeft.X() += nOnePixelInRef;
	if ( aTopLeft.Y() < 0 )
	{
		aTopLeft.Y() = 0;
	}
	return GetPaM( aTopLeft );
}

EditPaM ImpEditEngine::PageDown( const EditPaM& rPaM, EditView* pView )
{
	Rectangle aRec = PaMtoEditCursor( rPaM );
	Point aBottomRight = aRec.BottomRight();
	aBottomRight.Y() += pView->GetVisArea().GetHeight() *9/10;
	aBottomRight.X() += nOnePixelInRef;
	long nHeight = GetTextHeight();
	if ( aBottomRight.Y() > nHeight )
	{
		aBottomRight.Y() = nHeight-2;
	}
	return GetPaM( aBottomRight );
}

EditPaM ImpEditEngine::WordLeft( const EditPaM& rPaM, sal_Int16 nWordType )
{
	USHORT nCurrentPos = rPaM.GetIndex();
	EditPaM aNewPaM( rPaM );
	if ( nCurrentPos == 0 )
	{
		// Vorheriger Absatz...
		USHORT nCurPara = aEditDoc.GetPos( aNewPaM.GetNode() );
		ContentNode* pPrevNode = aEditDoc.SaveGetObject( --nCurPara );
		if ( pPrevNode )
		{
			aNewPaM.SetNode( pPrevNode );
			aNewPaM.SetIndex( pPrevNode->Len() );
		}
	}
	else
	{
		// we need to increase the position by 1 when retrieving the locale
		// since the attribute for the char left to the cursor position is returned
		EditPaM aTmpPaM( aNewPaM );
		xub_StrLen nMax = rPaM.GetNode()->Len();
		if ( aTmpPaM.GetIndex() < nMax )
			aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
        lang::Locale aLocale( GetLocale( aTmpPaM ) );

		uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
		i18n::Boundary aBoundary = _xBI->getWordBoundary( *aNewPaM.GetNode(), nCurrentPos, aLocale, nWordType, sal_True );
		if ( aBoundary.startPos >= nCurrentPos )
            aBoundary = _xBI->previousWord( *aNewPaM.GetNode(), nCurrentPos, aLocale, nWordType );
        aNewPaM.SetIndex( ( aBoundary.startPos != (-1) ) ? (USHORT)aBoundary.startPos : 0 );
	}

	return aNewPaM;
}

EditPaM ImpEditEngine::WordRight( const EditPaM& rPaM, sal_Int16 nWordType )
{
	xub_StrLen nMax = rPaM.GetNode()->Len();
	EditPaM aNewPaM( rPaM );
	if ( aNewPaM.GetIndex() < nMax )
	{
		// we need to increase the position by 1 when retrieving the locale
		// since the attribute for the char left to the cursor position is returned
		EditPaM aTmpPaM( aNewPaM );
        aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
        lang::Locale aLocale( GetLocale( aTmpPaM ) );

		uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
        i18n::Boundary aBoundary = _xBI->nextWord( *aNewPaM.GetNode(), aNewPaM.GetIndex(), aLocale, nWordType );
		aNewPaM.SetIndex( (USHORT)aBoundary.startPos );
	}
	// not 'else', maybe the index reached nMax now...
	if ( aNewPaM.GetIndex() >= nMax )
	{
		// Naechster Absatz...
		USHORT nCurPara = aEditDoc.GetPos( aNewPaM.GetNode() );
		ContentNode* pNextNode = aEditDoc.SaveGetObject( ++nCurPara );
		if ( pNextNode )
		{
			aNewPaM.SetNode( pNextNode );
			aNewPaM.SetIndex( 0 );
		}
	}
	return aNewPaM;
}

EditPaM ImpEditEngine::StartOfWord( const EditPaM& rPaM, sal_Int16 nWordType )
{
	EditPaM aNewPaM( rPaM );

	// we need to increase the position by 1 when retrieving the locale
	// since the attribute for the char left to the cursor position is returned
	EditPaM aTmpPaM( aNewPaM );
	xub_StrLen nMax = rPaM.GetNode()->Len();
	if ( aTmpPaM.GetIndex() < nMax )
		aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
    lang::Locale aLocale( GetLocale( aTmpPaM ) );

	uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
	i18n::Boundary aBoundary = _xBI->getWordBoundary( *rPaM.GetNode(), rPaM.GetIndex(), aLocale, nWordType, sal_True );
	aNewPaM.SetIndex( (USHORT)aBoundary.startPos );
	return aNewPaM;
}

EditPaM ImpEditEngine::EndOfWord( const EditPaM& rPaM, sal_Int16 nWordType )
{
	EditPaM aNewPaM( rPaM );

	// we need to increase the position by 1 when retrieving the locale
	// since the attribute for the char left to the cursor position is returned
	EditPaM aTmpPaM( aNewPaM );
	xub_StrLen nMax = rPaM.GetNode()->Len();
	if ( aTmpPaM.GetIndex() < nMax )
		aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
    lang::Locale aLocale( GetLocale( aTmpPaM ) );

	uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
	i18n::Boundary aBoundary = _xBI->getWordBoundary( *rPaM.GetNode(), rPaM.GetIndex(), aLocale, nWordType, sal_True );
	aNewPaM.SetIndex( (USHORT)aBoundary.endPos );
	return aNewPaM;
}

EditSelection ImpEditEngine::SelectWord( const EditSelection& rCurSel, sal_Int16 nWordType, BOOL bAcceptStartOfWord )
{
    EditSelection aNewSel( rCurSel );
	EditPaM aPaM( rCurSel.Max() );

	// we need to increase the position by 1 when retrieving the locale
	// since the attribute for the char left to the cursor position is returned
	EditPaM aTmpPaM( aPaM );
	xub_StrLen nMax = aPaM.GetNode()->Len();
	if ( aTmpPaM.GetIndex() < nMax )
		aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
    lang::Locale aLocale( GetLocale( aTmpPaM ) );

	uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
    sal_Int16 nType = _xBI->getWordType( *aPaM.GetNode(), aPaM.GetIndex(), aLocale );
	if ( nType == i18n::WordType::ANY_WORD )
	{
		i18n::Boundary aBoundary = _xBI->getWordBoundary( *aPaM.GetNode(), aPaM.GetIndex(), aLocale, nWordType, sal_True );
		// don't select when curser at end of word
		if ( ( aBoundary.endPos > aPaM.GetIndex() ) &&
			 ( ( aBoundary.startPos < aPaM.GetIndex() ) || ( bAcceptStartOfWord && ( aBoundary.startPos == aPaM.GetIndex() ) ) ) )
		{
			aNewSel.Min().SetIndex( (USHORT)aBoundary.startPos );
			aNewSel.Max().SetIndex( (USHORT)aBoundary.endPos );
		}
	}

	return aNewSel;
}

EditSelection ImpEditEngine::SelectSentence( const EditSelection& rCurSel )
{
    uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
    const EditPaM& rPaM = rCurSel.Min();
    const ContentNode* pNode = rPaM.GetNode();
    // #i50710# line breaks are marked with 0x01 - the break iterator prefers 0x0a for that
    String sParagraph(*pNode);
    sParagraph.SearchAndReplaceAll(0x01,0x0a);
    //return Null if search starts at the beginning of the string
    long nStart = rPaM.GetIndex() ? _xBI->beginOfSentence( sParagraph, rPaM.GetIndex(), GetLocale( rPaM ) ) : 0;

    long nEnd = _xBI->endOfSentence( *pNode, rPaM.GetIndex(), GetLocale( rPaM ) );
    EditSelection aNewSel( rCurSel );
    DBG_ASSERT(nStart < pNode->Len() && nEnd <= pNode->Len(), "sentence indices out of range");
    aNewSel.Min().SetIndex( (USHORT)nStart );
    aNewSel.Max().SetIndex( (USHORT)nEnd );
    return aNewSel;
}

sal_Bool ImpEditEngine::IsInputSequenceCheckingRequired( sal_Unicode nChar, const EditSelection& rCurSel ) const
{
    uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
    if (!pCTLOptions)
        pCTLOptions = new SvtCTLOptions;

    // get the index that really is first
    USHORT nFirstPos = rCurSel.Min().GetIndex();
    USHORT nMaxPos   = rCurSel.Max().GetIndex();
    if (nMaxPos < nFirstPos)
        nFirstPos = nMaxPos;

    sal_Bool bIsSequenceChecking =
        pCTLOptions->IsCTLFontEnabled() &&
        pCTLOptions->IsCTLSequenceChecking() &&
        nFirstPos != 0 && /* first char needs not to be checked */
        _xBI.is() && i18n::ScriptType::COMPLEX == _xBI->getScriptType( rtl::OUString( nChar ), 0 );

    return bIsSequenceChecking;
}

/*************************************************************************
 *                 lcl_HasStrongLTR
 *************************************************************************/
 bool lcl_HasStrongLTR ( const String& rTxt, xub_StrLen nStart, xub_StrLen nEnd )
 {
     for ( xub_StrLen nCharIdx = nStart; nCharIdx < nEnd; ++nCharIdx )
     {
         const UCharDirection nCharDir = u_charDirection ( rTxt.GetChar ( nCharIdx ));
         if ( nCharDir == U_LEFT_TO_RIGHT ||
              nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
              nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
             return true;
     }
     return false;
 }



void ImpEditEngine::InitScriptTypes( USHORT nPara )
{
	ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
	ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
	rTypes.Remove( 0, rTypes.Count() );

//	pParaPortion->aExtraCharInfos.Remove( 0, pParaPortion->aExtraCharInfos.Count() );

	ContentNode* pNode = pParaPortion->GetNode();
	if ( pNode->Len() )
	{
		uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );

		String aText( *pNode );

		// To handle fields put the character from the field in the string,
		// because endOfScript( ... ) will skip the CH_FEATURE, because this is WEAK
		EditCharAttrib* pField = pNode->GetCharAttribs().FindNextAttrib( EE_FEATURE_FIELD, 0 );
		while ( pField )
		{
			::rtl::OUString aFldText( ((EditCharAttribField*)pField)->GetFieldValue() );
			if ( aFldText.getLength() )
			{
				aText.SetChar( pField->GetStart(), aFldText.getStr()[0] );
				short nFldScriptType = _xBI->getScriptType( aFldText, 0 );

				for ( USHORT nCharInField = 1; nCharInField < aFldText.getLength(); nCharInField++ )
				{
					short nTmpType = _xBI->getScriptType( aFldText, nCharInField );

					// First char from field wins...
					if ( nFldScriptType == i18n::ScriptType::WEAK )
					{
						nFldScriptType = nTmpType;
						aText.SetChar( pField->GetStart(), aFldText.getStr()[nCharInField] );
					}

					// ...  but if the first one is LATIN, and there are CJK or CTL chars too,
					// we prefer that ScripType because we need an other font.
					if ( ( nTmpType == i18n::ScriptType::ASIAN ) || ( nTmpType == i18n::ScriptType::COMPLEX ) )
					{
						aText.SetChar( pField->GetStart(), aFldText.getStr()[nCharInField] );
						break;
					}
				}
			}
			// #112831# Last Field might go from 0xffff to 0x0000
			pField = pField->GetEnd() ? pNode->GetCharAttribs().FindNextAttrib( EE_FEATURE_FIELD, pField->GetEnd() ) : NULL;
		}

		::rtl::OUString aOUText( aText );
		USHORT nTextLen = (USHORT)aOUText.getLength();

		long nPos = 0;
		short nScriptType = _xBI->getScriptType( aOUText, nPos );
		rTypes.Insert( ScriptTypePosInfo( nScriptType, (USHORT)nPos, nTextLen ), rTypes.Count() );
		nPos = _xBI->endOfScript( aOUText, nPos, nScriptType );
		while ( ( nPos != (-1) ) && ( nPos < nTextLen ) )
		{
			rTypes[rTypes.Count()-1].nEndPos = (USHORT)nPos;

            nScriptType = _xBI->getScriptType( aOUText, nPos );
			long nEndPos = _xBI->endOfScript( aOUText, nPos, nScriptType );

            if ( ( nScriptType == i18n::ScriptType::WEAK ) || ( nScriptType == rTypes[rTypes.Count()-1].nScriptType ) )
            {
                // Expand last ScriptTypePosInfo, don't create weak or unecessary portions
                rTypes[rTypes.Count()-1].nEndPos = (USHORT)nEndPos;
            }
            else
            {
			    rTypes.Insert( ScriptTypePosInfo( nScriptType, (USHORT)nPos, nTextLen ), rTypes.Count() );
            }

			nPos = nEndPos;
		}

		if ( rTypes[0].nScriptType == i18n::ScriptType::WEAK )
			rTypes[0].nScriptType = ( rTypes.Count() > 1 ) ? rTypes[1].nScriptType : GetI18NScriptTypeOfLanguage( GetDefaultLanguage() );

		// create writing direction information:
		if ( !pParaPortion->aWritingDirectionInfos.Count() )
			InitWritingDirections( nPara );

		// i89825: Use CTL font for numbers embedded into an RTL run:
        WritingDirectionInfos& rDirInfos = pParaPortion->aWritingDirectionInfos;
        for ( USHORT n = 0; n < rDirInfos.Count(); ++n )
        {
            const xub_StrLen nStart = rDirInfos[n].nStartPos;
            const xub_StrLen nEnd   = rDirInfos[n].nEndPos;
            const BYTE nCurrDirType = rDirInfos[n].nType;

            if ( nCurrDirType % 2 == UBIDI_RTL  || // text in RTL run
                ( nCurrDirType > UBIDI_LTR && !lcl_HasStrongLTR( aText, nStart, nEnd ) ) ) // non-strong text in embedded LTR run
            {
                USHORT nIdx = 0;

                // Skip entries in ScriptArray which are not inside the RTL run:
                while ( nIdx < rTypes.Count() && rTypes[nIdx].nStartPos < nStart )
                    ++nIdx;

                // Remove any entries *inside* the current run:
                while ( nIdx < rTypes.Count() && rTypes[nIdx].nEndPos <= nEnd )
                    rTypes.Remove( nIdx );

                // special case:
                if(nIdx < rTypes.Count() && rTypes[nIdx].nStartPos < nStart && rTypes[nIdx].nEndPos > nEnd)
                {
                    rTypes.Insert( ScriptTypePosInfo( rTypes[nIdx].nScriptType, (USHORT)nEnd, rTypes[nIdx].nEndPos ), nIdx );
                    rTypes[nIdx].nEndPos = nStart;
                }

                if( nIdx )
                    rTypes[nIdx - 1].nEndPos = nStart;

                rTypes.Insert( ScriptTypePosInfo( i18n::ScriptType::COMPLEX, (USHORT)nStart, (USHORT)nEnd), nIdx );
                ++nIdx;

                if( nIdx < rTypes.Count() )
                    rTypes[nIdx].nStartPos = nEnd;
            }
        }

#if OSL_DEBUG_LEVEL > 1
        USHORT nDebugStt = 0;
        USHORT nDebugEnd = 0;
        short nDebugType = 0;
        for ( USHORT n = 0; n < rTypes.Count(); ++n )
        {
            nDebugStt  = rTypes[n].nStartPos;
            nDebugEnd  = rTypes[n].nEndPos;
            nDebugType = rTypes[n].nScriptType;
        }
#endif
    }
}

USHORT ImpEditEngine::GetScriptType( const EditPaM& rPaM, USHORT* pEndPos ) const
{
	USHORT nScriptType = 0;

	if ( pEndPos )
		*pEndPos = rPaM.GetNode()->Len();

	if ( rPaM.GetNode()->Len() )
	{
 		USHORT nPara = GetEditDoc().GetPos( rPaM.GetNode() );
		ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
		if ( !pParaPortion->aScriptInfos.Count() )
			((ImpEditEngine*)this)->InitScriptTypes( nPara );

		ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
		USHORT nPos = rPaM.GetIndex();
		for ( USHORT n = 0; n < rTypes.Count(); n++ )
		{
			if ( ( rTypes[n].nStartPos <= nPos ) && ( rTypes[n].nEndPos >= nPos ) )
	   		{
				nScriptType = rTypes[n].nScriptType;
				if( pEndPos )
					*pEndPos = rTypes[n].nEndPos;
				break;
			}
		}
	}
	return nScriptType ? nScriptType : GetI18NScriptTypeOfLanguage( GetDefaultLanguage() );
}

USHORT ImpEditEngine::GetScriptType( const EditSelection& rSel ) const
{
	EditSelection aSel( rSel );
	aSel.Adjust( aEditDoc );

	short nScriptType = 0;

 	USHORT nStartPara = GetEditDoc().GetPos( aSel.Min().GetNode() );
 	USHORT nEndPara = GetEditDoc().GetPos( aSel.Max().GetNode() );

	for ( USHORT nPara = nStartPara; nPara <= nEndPara; nPara++ )
	{
		ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
		if ( !pParaPortion->aScriptInfos.Count() )
			((ImpEditEngine*)this)->InitScriptTypes( nPara );

		ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;

		// find the first(!) script type position that holds the
		// complete selection. Thus it will work for selections as
		// well as with just moving the cursor from char to char.
		USHORT nS = ( nPara == nStartPara ) ? aSel.Min().GetIndex() : 0;
		USHORT nE = ( nPara == nEndPara ) ? aSel.Max().GetIndex() : pParaPortion->GetNode()->Len();
		for ( USHORT n = 0; n < rTypes.Count(); n++ )
		{
            if (rTypes[n].nStartPos <= nS  &&  nE <= rTypes[n].nEndPos)
		   	{
				if ( rTypes[n].nScriptType != i18n::ScriptType::WEAK )
				{
                    nScriptType |= GetItemScriptType ( rTypes[n].nScriptType );
                }
                else
				{
                    if ( !nScriptType && n )
                    {
                        // #93548# When starting with WEAK, use prev ScriptType...
                        nScriptType = rTypes[n-1].nScriptType;
                    }
				}
				break;
			}
		}
	}
	return nScriptType ? nScriptType : GetI18NScriptTypeOfLanguage( GetDefaultLanguage() );
}

BOOL ImpEditEngine::IsScriptChange( const EditPaM& rPaM ) const
{
	BOOL bScriptChange = FALSE;

	if ( rPaM.GetNode()->Len() )
	{
		USHORT nPara = GetEditDoc().GetPos( rPaM.GetNode() );
		ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
		if ( !pParaPortion->aScriptInfos.Count() )
			((ImpEditEngine*)this)->InitScriptTypes( nPara );

		ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
		USHORT nPos = rPaM.GetIndex();
		for ( USHORT n = 0; n < rTypes.Count(); n++ )
		{
			if ( rTypes[n].nStartPos == nPos )
	   		{
				bScriptChange = TRUE;
				break;
			}
		}
	}
	return bScriptChange;
}

BOOL ImpEditEngine::HasScriptType( USHORT nPara, USHORT nType ) const
{
    BOOL bTypeFound = FALSE;

	ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
	if ( !pParaPortion->aScriptInfos.Count() )
		((ImpEditEngine*)this)->InitScriptTypes( nPara );

    ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
	for ( USHORT n = rTypes.Count(); n && !bTypeFound; )
	{
	    if ( rTypes[--n].nScriptType == nType )
                bTypeFound = TRUE;
    }
    return bTypeFound;
}

void ImpEditEngine::InitWritingDirections( USHORT nPara )
{
	ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
	WritingDirectionInfos& rInfos = pParaPortion->aWritingDirectionInfos;
	rInfos.Remove( 0, rInfos.Count() );

    BOOL bCTL = FALSE;
	ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
	for ( USHORT n = 0; n < rTypes.Count(); n++ )
	{
		if ( rTypes[n].nScriptType == i18n::ScriptType::COMPLEX )
	   	{
			bCTL = TRUE;
			break;
		}
	}

    const UBiDiLevel nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/;
    if ( ( bCTL || ( nBidiLevel == 1 /*RTL*/ ) ) && pParaPortion->GetNode()->Len() )
	{

        String aText( *pParaPortion->GetNode() );

        //
        // Bidi functions from icu 2.0
        //
        UErrorCode nError = U_ZERO_ERROR;
        UBiDi* pBidi = ubidi_openSized( aText.Len(), 0, &nError );
        nError = U_ZERO_ERROR;

        ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.GetBuffer()), aText.Len(), nBidiLevel, NULL, &nError );	// UChar != sal_Unicode in MinGW
        nError = U_ZERO_ERROR;

        long nCount = ubidi_countRuns( pBidi, &nError );

        int32_t nStart = 0;
        int32_t nEnd;
        UBiDiLevel nCurrDir;

        for ( USHORT nIdx = 0; nIdx < nCount; ++nIdx )
        {
            ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir );
            rInfos.Insert( WritingDirectionInfo( nCurrDir, (USHORT)nStart, (USHORT)nEnd ), rInfos.Count() );
            nStart = nEnd;
        }

        ubidi_close( pBidi );
	}

    // No infos mean no CTL and default dir is L2R...
    if ( !rInfos.Count() )
        rInfos.Insert( WritingDirectionInfo( 0, 0, (USHORT)pParaPortion->GetNode()->Len() ), rInfos.Count() );

}

BOOL ImpEditEngine::IsRightToLeft( USHORT nPara ) const
{
    BOOL bR2L = FALSE;
    const SvxFrameDirectionItem* pFrameDirItem = NULL;

    if ( !IsVertical() )
    {
        bR2L = GetDefaultHorizontalTextDirection() == EE_HTEXTDIR_R2L;
        pFrameDirItem = &(const SvxFrameDirectionItem&)GetParaAttrib( nPara, EE_PARA_WRITINGDIR );
        if ( pFrameDirItem->GetValue() == FRMDIR_ENVIRONMENT )
        {
            // #103045# if DefaultHorizontalTextDirection is set, use that value, otherwise pool default.
            if ( GetDefaultHorizontalTextDirection() != EE_HTEXTDIR_DEFAULT )
            {
                pFrameDirItem = NULL; // bR2L allready set to default horizontal text direction
            }
            else
            {
                // Use pool default
                pFrameDirItem = &(const SvxFrameDirectionItem&)((ImpEditEngine*)this)->GetEmptyItemSet().Get( EE_PARA_WRITINGDIR );
            }
        }
    }

    if ( pFrameDirItem )
        bR2L = pFrameDirItem->GetValue() == FRMDIR_HORI_RIGHT_TOP;

    return bR2L;
}

BOOL ImpEditEngine::HasDifferentRTLLevels( const ContentNode* pNode )
{
    USHORT nPara = GetEditDoc().GetPos( (ContentNode*)pNode );
	ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );

    BOOL bHasDifferentRTLLevels = FALSE;

    USHORT nRTLLevel = IsRightToLeft( nPara ) ? 1 : 0;
	for ( USHORT n = 0; n < pParaPortion->GetTextPortions().Count(); n++ )
	{
		TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( n );
        if ( pTextPortion->GetRightToLeft() != nRTLLevel )
        {
            bHasDifferentRTLLevels = TRUE;
            break;
        }
	}
    return bHasDifferentRTLLevels;
}


BYTE ImpEditEngine::GetRightToLeft( USHORT nPara, USHORT nPos, USHORT* pStart, USHORT* pEnd )
{
//    BYTE nRightToLeft = IsRightToLeft( nPara ) ? 1 : 0;
    BYTE nRightToLeft = 0;

    ContentNode* pNode = aEditDoc.SaveGetObject( nPara );
    if ( pNode && pNode->Len() )
    {
		ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
		if ( !pParaPortion->aWritingDirectionInfos.Count() )
			InitWritingDirections( nPara );

//        BYTE nType = 0;
		WritingDirectionInfos& rDirInfos = pParaPortion->aWritingDirectionInfos;
		for ( USHORT n = 0; n < rDirInfos.Count(); n++ )
		{
			if ( ( rDirInfos[n].nStartPos <= nPos ) && ( rDirInfos[n].nEndPos >= nPos ) )
	   		{
				nRightToLeft = rDirInfos[n].nType;
                if ( pStart )
                    *pStart = rDirInfos[n].nStartPos;
                if ( pEnd )
                    *pEnd = rDirInfos[n].nEndPos;
				break;
			}
		}
    }
    return nRightToLeft;
}

SvxAdjust ImpEditEngine::GetJustification( USHORT nPara ) const
{
	SvxAdjust eJustification = SVX_ADJUST_LEFT;

    if ( !aStatus.IsOutliner() )
    {
		eJustification = ((const SvxAdjustItem&) GetParaAttrib( nPara, EE_PARA_JUST )).GetAdjust();

        if ( IsRightToLeft( nPara ) )
        {
            if ( eJustification == SVX_ADJUST_LEFT )
                eJustification = SVX_ADJUST_RIGHT;
            else if ( eJustification == SVX_ADJUST_RIGHT )
                eJustification = SVX_ADJUST_LEFT;
        }
    }
    return eJustification;
}


//	----------------------------------------------------------------------
//	Textaenderung
//	----------------------------------------------------------------------

void ImpEditEngine::ImpRemoveChars( const EditPaM& rPaM, USHORT nChars, EditUndoRemoveChars* pCurUndo )
{
	if ( IsUndoEnabled() && !IsInUndo() )
	{
		XubString aStr( rPaM.GetNode()->Copy( rPaM.GetIndex(), nChars ) );

		// Pruefen, ob Attribute geloescht oder geaendert werden:
		USHORT nStart = rPaM.GetIndex();
		USHORT nEnd = nStart + nChars;
		CharAttribArray& rAttribs = rPaM.GetNode()->GetCharAttribs().GetAttribs();
//		USHORT nAttrs = rAttribs.Count();
		for ( USHORT nAttr = 0; nAttr < rAttribs.Count(); nAttr++ )
		{
			EditCharAttrib* pAttr = rAttribs[nAttr];
			if ( ( pAttr->GetEnd() >= nStart ) && ( pAttr->GetStart() < nEnd ) )
			{
#ifndef SVX_LIGHT
				EditSelection aSel( rPaM );
				aSel.Max().GetIndex() = aSel.Max().GetIndex() + nChars;
				EditUndoSetAttribs* pAttrUndo = CreateAttribUndo( aSel, GetEmptyItemSet() );
				InsertUndo( pAttrUndo );
#endif
				break;	// for
			}
		}
		if ( pCurUndo && ( CreateEditPaM( pCurUndo->GetEPaM() ) == rPaM ) )
			pCurUndo->GetStr() += aStr;
#ifndef SVX_LIGHT
		else
			InsertUndo( new EditUndoRemoveChars( this, CreateEPaM( rPaM ), aStr ) );
#endif
	}

	aEditDoc.RemoveChars( rPaM, nChars );
	TextModified();
}

EditSelection ImpEditEngine::ImpMoveParagraphs( Range aOldPositions, USHORT nNewPos )
{
	aOldPositions.Justify();
	BOOL bValidAction = ( (long)nNewPos < aOldPositions.Min() ) || ( (long)nNewPos > aOldPositions.Max() );
	DBG_ASSERT( bValidAction, "Move in sich selbst ?" );
	DBG_ASSERT( aOldPositions.Max() <= (long)GetParaPortions().Count(), "Voll drueber weg: MoveParagraphs" );

	EditSelection aSelection;

	if ( !bValidAction )
	{
		aSelection = aEditDoc.GetStartPaM();
		return aSelection;
	}

	ULONG nParaCount = GetParaPortions().Count();

	if ( nNewPos >= nParaCount )
		nNewPos = GetParaPortions().Count();

	// Height may change when moving first or last Paragraph
	ParaPortion* pRecalc1 = NULL;
	ParaPortion* pRecalc2 = NULL;
	ParaPortion* pRecalc3 = NULL;
	ParaPortion* pRecalc4 = NULL;

	if ( nNewPos == 0 )	// Move to Start
	{
		pRecalc1 = GetParaPortions().GetObject( 0 );
		pRecalc2 = GetParaPortions().GetObject( (USHORT)aOldPositions.Min() );

	}
	else if ( nNewPos == nParaCount )
	{
		pRecalc1 = GetParaPortions().GetObject( (USHORT)(nParaCount-1) );
		pRecalc2 = GetParaPortions().GetObject( (USHORT)aOldPositions.Max() );
	}

	if ( aOldPositions.Min() == 0 )	// Move from Start
	{
		pRecalc3 = GetParaPortions().GetObject( 0 );
		pRecalc4 = GetParaPortions().GetObject(
            sal::static_int_cast< USHORT >( aOldPositions.Max()+1 ) );
	}
	else if ( (USHORT)aOldPositions.Max() == (nParaCount-1) )
	{
		pRecalc3 = GetParaPortions().GetObject( (USHORT)aOldPositions.Max() );
		pRecalc4 = GetParaPortions().GetObject( (USHORT)(aOldPositions.Min()-1) );
	}

	MoveParagraphsInfo aMoveParagraphsInfo( sal::static_int_cast< USHORT >(aOldPositions.Min()), sal::static_int_cast< USHORT >(aOldPositions.Max()), nNewPos );
	aBeginMovingParagraphsHdl.Call( &aMoveParagraphsInfo );

	if ( IsUndoEnabled() && !IsInUndo())
		InsertUndo( new EditUndoMoveParagraphs( this, aOldPositions, nNewPos ) );

	// Position nicht aus dem Auge verlieren!
	ParaPortion* pDestPortion = GetParaPortions().SaveGetObject( nNewPos );

	ParaPortionList aTmpPortionList;
	USHORT i;
	for ( i = (USHORT)aOldPositions.Min(); i <= (USHORT)aOldPositions.Max(); i++  )
	{
		// Immer aOldPositions.Min(), da Remove().
		ParaPortion* pTmpPortion = GetParaPortions().GetObject( (USHORT)aOldPositions.Min() );
		GetParaPortions().Remove( (USHORT)aOldPositions.Min() );
		aEditDoc.Remove( (USHORT)aOldPositions.Min() );
		aTmpPortionList.Insert( pTmpPortion, aTmpPortionList.Count() );
	}

	USHORT nRealNewPos = pDestPortion ? GetParaPortions().GetPos( pDestPortion ) : GetParaPortions().Count();
	DBG_ASSERT( nRealNewPos != USHRT_MAX, "ImpMoveParagraphs: Ungueltige Position!" );

	for ( i = 0; i < (USHORT)aTmpPortionList.Count(); i++  )
	{
		ParaPortion* pTmpPortion = aTmpPortionList.GetObject( i );
		if ( i == 0 )
			aSelection.Min().SetNode( pTmpPortion->GetNode() );

		aSelection.Max().SetNode( pTmpPortion->GetNode() );
		aSelection.Max().SetIndex( pTmpPortion->GetNode()->Len() );

		ContentNode* pN = pTmpPortion->GetNode();
		aEditDoc.Insert( pN, nRealNewPos+i );

		GetParaPortions().Insert( pTmpPortion, nRealNewPos+i );
	}

    aEndMovingParagraphsHdl.Call( &aMoveParagraphsInfo );

    if ( GetNotifyHdl().IsSet() )
    {
        EENotify aNotify( EE_NOTIFY_PARAGRAPHSMOVED );
        aNotify.pEditEngine = GetEditEnginePtr();
        aNotify.nParagraph = nNewPos;
        aNotify.nParam1 = sal::static_int_cast< USHORT >(aOldPositions.Min());
        aNotify.nParam2 = sal::static_int_cast< USHORT >(aOldPositions.Max());
        CallNotify( aNotify );
    }

	aEditDoc.SetModified( TRUE );

	if ( pRecalc1 )
		CalcHeight( pRecalc1 );
	if ( pRecalc2 )
		CalcHeight( pRecalc2 );
	if ( pRecalc3 )
		CalcHeight( pRecalc3 );
	if ( pRecalc4 )
		CalcHeight( pRecalc4 );

	aTmpPortionList.Remove( 0, aTmpPortionList.Count() );	// wichtig !

#ifdef EDITDEBUG
	GetParaPortions().DbgCheck(aEditDoc);
#endif
	return aSelection;
}


EditPaM ImpEditEngine::ImpConnectParagraphs( ContentNode* pLeft, ContentNode* pRight, BOOL bBackward )
{
	DBG_ASSERT( pLeft != pRight, "Den gleichen Absatz zusammenfuegen ?" );
	DBG_ASSERT( aEditDoc.GetPos( pLeft ) != USHRT_MAX, "Einzufuegenden Node nicht gefunden(1)" );
	DBG_ASSERT( aEditDoc.GetPos( pRight ) != USHRT_MAX, "Einzufuegenden Node nicht gefunden(2)" );

	USHORT nParagraphTobeDeleted = aEditDoc.GetPos( pRight );
	DeletedNodeInfo* pInf = new DeletedNodeInfo( (ULONG)pRight, nParagraphTobeDeleted );
	aDeletedNodes.Insert( pInf, aDeletedNodes.Count() );

    GetEditEnginePtr()->ParagraphConnected( aEditDoc.GetPos( pLeft ), aEditDoc.GetPos( pRight ) );

#ifndef SVX_LIGHT
	if ( IsUndoEnabled() && !IsInUndo() )
	{
		InsertUndo( new EditUndoConnectParas( this,
			aEditDoc.GetPos( pLeft ), pLeft->Len(),
			pLeft->GetContentAttribs().GetItems(), pRight->GetContentAttribs().GetItems(),
			pLeft->GetStyleSheet(), pRight->GetStyleSheet(), bBackward ) );
	}
#endif

	if ( bBackward )
	{
		pLeft->SetStyleSheet( pRight->GetStyleSheet(), TRUE );
		pLeft->GetContentAttribs().GetItems().Set( pRight->GetContentAttribs().GetItems() );
		pLeft->GetCharAttribs().GetDefFont() = pRight->GetCharAttribs().GetDefFont();
	}

	ParaAttribsChanged( pLeft );

	// Erstmal Portions suchen, da pRight nach ConnectParagraphs weg.
	ParaPortion* pLeftPortion = FindParaPortion( pLeft );
	ParaPortion* pRightPortion = FindParaPortion( pRight );
	DBG_ASSERT( pLeftPortion, "Blinde Portion in ImpConnectParagraphs(1)" );
	DBG_ASSERT( pRightPortion, "Blinde Portion in ImpConnectParagraphs(2)" );
	DBG_ASSERT( nParagraphTobeDeleted == GetParaPortions().GetPos( pRightPortion ), "NodePos != PortionPos?" );

#ifndef SVX_LIGHT
	if ( GetStatus().DoOnlineSpelling() )
	{
		xub_StrLen nEnd = pLeft->Len();
		xub_StrLen nInv = nEnd ? nEnd-1 : nEnd;
		pLeft->GetWrongList()->ClearWrongs( nInv, 0xFFFF, pLeft );	// Evtl. einen wegnehmen
		pLeft->GetWrongList()->MarkInvalid( nInv, nEnd+1 );
		// Falschgeschriebene Woerter ruebernehmen:
		USHORT nRWrongs = pRight->GetWrongList()->Count();
		for ( USHORT nW = 0; nW < nRWrongs; nW++ )
		{
			WrongRange aWrong = pRight->GetWrongList()->GetObject( nW );
			if ( aWrong.nStart != 0 ) 	// Nicht ein anschliessender
			{
				aWrong.nStart = aWrong.nStart + nEnd;
				aWrong.nEnd = aWrong.nEnd + nEnd;
				pLeft->GetWrongList()->InsertWrong( aWrong, pLeft->GetWrongList()->Count() );
			}
		}
	}
#endif

	if ( IsCallParaInsertedOrDeleted() )
		GetEditEnginePtr()->ParagraphDeleted( nParagraphTobeDeleted );

	EditPaM aPaM = aEditDoc.ConnectParagraphs( pLeft, pRight );
	GetParaPortions().Remove( nParagraphTobeDeleted );
	delete pRightPortion;

	pLeftPortion->MarkSelectionInvalid( aPaM.GetIndex(), pLeft->Len() );

	// der rechte Node wird von EditDoc::ConnectParagraphs() geloescht.

	if ( GetTextRanger() )
	{
		// Durch das zusammenfuegen wird der linke zwar neu formatiert, aber
		// wenn sich dessen Hoehe nicht aendert bekommt die Formatierung die
		// Aenderung der Gesaamthoehe des Textes zu spaet mit...
		for ( USHORT n = nParagraphTobeDeleted; n < GetParaPortions().Count(); n++ )
		{
			ParaPortion* pPP = GetParaPortions().GetObject( n );
			pPP->MarkSelectionInvalid( 0, pPP->GetNode()->Len() );
			pPP->GetLines().Reset();
		}
	}

	TextModified();

	return aPaM;
}

EditPaM ImpEditEngine::DeleteLeftOrRight( const EditSelection& rSel, BYTE nMode, BYTE nDelMode )
{
	DBG_ASSERT( !EditSelection( rSel ).DbgIsBuggy( aEditDoc ), "Index im Wald in DeleteLeftOrRight" );

	if ( rSel.HasRange() )	// dann nur Sel. loeschen
		return ImpDeleteSelection( rSel );

	const EditPaM aCurPos( rSel.Max() );
	EditPaM aDelStart( aCurPos );
	EditPaM aDelEnd( aCurPos );
	if ( nMode == DEL_LEFT )
	{
		if ( nDelMode == DELMODE_SIMPLE )
		{
			aDelStart = CursorLeft( aCurPos, i18n::CharacterIteratorMode::SKIPCHARACTER );
		}
		else if ( nDelMode == DELMODE_RESTOFWORD )
		{
			aDelStart = StartOfWord( aCurPos );
			if ( aDelStart.GetIndex() == aCurPos.GetIndex() )
				aDelStart = WordLeft( aCurPos );
		}
		else	// DELMODE_RESTOFCONTENT
		{
			aDelStart.SetIndex( 0 );
			if ( aDelStart == aCurPos )
			{
				// kompletter Absatz davor
				ContentNode* pPrev = GetPrevVisNode( aCurPos.GetNode() );
				if ( pPrev )
					aDelStart = EditPaM( pPrev, 0 );
			}
		}
	}
	else
	{
		if ( nDelMode == DELMODE_SIMPLE )
		{
			aDelEnd = CursorRight( aCurPos );
		}
		else if ( nDelMode == DELMODE_RESTOFWORD )
		{
			aDelEnd = EndOfWord( aCurPos );
			if (aDelEnd.GetIndex() == aCurPos.GetIndex())
			{
				xub_StrLen nLen = aCurPos.GetNode()->Len();
				// end of para?
				if (aDelEnd.GetIndex() == nLen)
					aDelEnd = WordLeft( aCurPos );
				else // there's still sth to delete on the right
				{
					aDelEnd = EndOfWord( WordRight( aCurPos ) );
					// if there'n no next word...
					if (aDelEnd.GetIndex() == nLen )
						aDelEnd.SetIndex( nLen );
				}
			}
		}
		else	// DELMODE_RESTOFCONTENT
		{
			aDelEnd.SetIndex( aCurPos.GetNode()->Len() );
			if ( aDelEnd == aCurPos )
			{
				// kompletter Absatz dahinter
				ContentNode* pNext = GetNextVisNode( aCurPos.GetNode() );
				if ( pNext )
					aDelEnd = EditPaM( pNext, pNext->Len() );
			}
		}
	}

	// Bei DELMODE_RESTOFCONTENT reicht bei verschiedenen Nodes
	// kein ConnectParagraphs.
	if ( ( nDelMode == DELMODE_RESTOFCONTENT ) || ( aDelStart.GetNode() == aDelEnd.GetNode() ) )
		return ImpDeleteSelection( EditSelection( aDelStart, aDelEnd ) );

	// Jetzt entscheiden, ob noch Selektion loeschen (RESTOFCONTENTS)
	BOOL bSpecialBackward = ( ( nMode == DEL_LEFT ) && ( nDelMode == DELMODE_SIMPLE ) )
								? TRUE : FALSE;
	if ( aStatus.IsAnyOutliner() )
		bSpecialBackward = FALSE;

	return ImpConnectParagraphs( aDelStart.GetNode(), aDelEnd.GetNode(), bSpecialBackward );
}

EditPaM ImpEditEngine::ImpDeleteSelection( EditSelection aSel )
{
	if ( !aSel.HasRange() )
		return aSel.Min();

	aSel.Adjust( aEditDoc );
	EditPaM aStartPaM( aSel.Min() );
	EditPaM aEndPaM( aSel.Max() );

	CursorMoved( aStartPaM.GetNode() ); // nur damit neu eingestellte Attribute verschwinden...
	CursorMoved( aEndPaM.GetNode() );	// nur damit neu eingestellte Attribute verschwinden...

	DBG_ASSERT( aStartPaM.GetIndex() <= aStartPaM.GetNode()->Len(), "Index im Wald in ImpDeleteSelection" );
	DBG_ASSERT( aEndPaM.GetIndex() <= aEndPaM.GetNode()->Len(), "Index im Wald in ImpDeleteSelection" );

	USHORT nStartNode = aEditDoc.GetPos( aStartPaM.GetNode() );
	USHORT nEndNode = aEditDoc.GetPos( aEndPaM.GetNode() );

	DBG_ASSERT( nEndNode != USHRT_MAX, "Start > End ?!" );
	DBG_ASSERT( nStartNode <= nEndNode, "Start > End ?!" );

	// Alle Nodes dazwischen entfernen....
	for ( ULONG z = nStartNode+1; z < nEndNode; z++ )
	{
		// Immer nStartNode+1, wegen Remove()!
		ImpRemoveParagraph( nStartNode+1 );
	}

	if ( aStartPaM.GetNode() != aEndPaM.GetNode() )
	{
		// Den Rest des StartNodes...
		USHORT nChars;
		nChars = aStartPaM.GetNode()->Len() - aStartPaM.GetIndex();
		ImpRemoveChars( aStartPaM, nChars );
		ParaPortion* pPortion = FindParaPortion( aStartPaM.GetNode() );
		DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteSelection(3)" );
		pPortion->MarkSelectionInvalid( aStartPaM.GetIndex(), aStartPaM.GetNode()->Len() );

		// Den Anfang des EndNodes....
		nChars = aEndPaM.GetIndex();
		aEndPaM.SetIndex( 0 );
		ImpRemoveChars( aEndPaM, nChars );
		pPortion = FindParaPortion( aEndPaM.GetNode() );
		DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteSelection(4)" );
		pPortion->MarkSelectionInvalid( 0, aEndPaM.GetNode()->Len() );
		// Zusammenfuegen....
		aStartPaM = ImpConnectParagraphs( aStartPaM.GetNode(), aEndPaM.GetNode() );
	}
	else
	{
		USHORT nChars;
		nChars = aEndPaM.GetIndex() - aStartPaM.GetIndex();
		ImpRemoveChars( aStartPaM, nChars );
		ParaPortion* pPortion = FindParaPortion( aStartPaM.GetNode() );
		DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteSelection(5)" );
		pPortion->MarkInvalid( aEndPaM.GetIndex(), aStartPaM.GetIndex() - aEndPaM.GetIndex() );
	}

	UpdateSelections();
	TextModified();
	return aStartPaM;
}

void ImpEditEngine::ImpRemoveParagraph( USHORT nPara )
{
	ContentNode* pNode = aEditDoc.SaveGetObject( nPara );
	ContentNode* pNextNode = aEditDoc.SaveGetObject( nPara+1 );
	ParaPortion* pPortion = GetParaPortions().SaveGetObject( nPara );

	DBG_ASSERT( pNode, "Blinder Node in ImpRemoveParagraph" );
	DBG_ASSERT( pPortion, "Blinde Portion in ImpRemoveParagraph(2)" );

	DeletedNodeInfo* pInf = new DeletedNodeInfo( (ULONG)pNode, nPara );
	aDeletedNodes.Insert( pInf, aDeletedNodes.Count() );

	// Der Node wird vom Undo verwaltet und ggf. zerstoert!
	/* delete */ aEditDoc.Remove( nPara );
	GetParaPortions().Remove( nPara );
	delete pPortion;

	if ( IsCallParaInsertedOrDeleted() )
	{
		GetEditEnginePtr()->ParagraphDeleted( nPara );
	}

	// Im folgenden muss ggf. Extra-Space neu ermittelt werden.
	// Bei ParaAttribsChanged wird leider der Absatz neu formatiert,
	// aber diese Methode sollte nicht Zeitkritsch sein!
	if ( pNextNode )
		ParaAttribsChanged( pNextNode );

#ifndef SVX_LIGHT
	if ( IsUndoEnabled() && !IsInUndo() )
		InsertUndo( new EditUndoDelContent( this, pNode, nPara ) );
	else
#endif
	{
		aEditDoc.RemoveItemsFromPool( pNode );
		if ( pNode->GetStyleSheet() )
			EndListening( *pNode->GetStyleSheet(), FALSE );
		delete pNode;
	}
}

EditPaM ImpEditEngine::AutoCorrect( const EditSelection& rCurSel, xub_Unicode c, BOOL bOverwrite )
{
	EditSelection aSel( rCurSel );
#ifndef SVX_LIGHT
	SvxAutoCorrect*	pAutoCorrect = SvxAutoCorrCfg::Get()->GetAutoCorrect();
	if ( pAutoCorrect )
	{
		if ( aSel.HasRange() )
			aSel = ImpDeleteSelection( rCurSel );

		// #i78661 allow application to turn off capitalization of
		// start sentence explicitly.
		// (This is done by setting IsFirstWordCapitalization to FALSE.)
		BOOL bOldCptlSttSntnc = pAutoCorrect->IsAutoCorrFlag( CptlSttSntnc );
		if (!IsFirstWordCapitalization())
		{
			ESelection aESel( CreateESel(aSel) );
			EditSelection aFirstWordSel;
			EditSelection aSecondWordSel;
			if (aESel.nEndPara == 0)	// is this the first para?
			{
				// select first word...
				// start by checking if para starts with word.
				aFirstWordSel = SelectWord( CreateSel(ESelection()) );
				if (aFirstWordSel.Min().GetIndex() == 0 && aFirstWordSel.Max().GetIndex() == 0)
				{
					// para does not start with word -> select next/first word
					EditPaM aRightWord( WordRight( aFirstWordSel.Max(), 1 ) );
					aFirstWordSel = SelectWord( EditSelection( aRightWord ) );
				}

				// select second word
				// (sometimes aSel mightnot point to the end of the first word
				// but to some following char like '.'. ':', ...
				// In those cases we need aSecondWordSel to see if aSel
				// will actually effect the first word.)
				EditPaM aRight2Word( WordRight( aFirstWordSel.Max(), 1 ) );
				aSecondWordSel = SelectWord( EditSelection( aRight2Word ) );
			}
			BOOL bIsFirstWordInFirstPara = aESel.nEndPara == 0 &&
					aFirstWordSel.Max().GetIndex() <= aSel.Max().GetIndex() &&
					aSel.Max().GetIndex() <= aSecondWordSel.Min().GetIndex();

			if (bIsFirstWordInFirstPara)
				pAutoCorrect->SetAutoCorrFlag( CptlSttSntnc, IsFirstWordCapitalization() );
		}

		ContentNode* pNode = aSel.Max().GetNode();
		USHORT nIndex = aSel.Max().GetIndex();
		EdtAutoCorrDoc aAuto( this, pNode, nIndex, c );
		pAutoCorrect->AutoCorrect( aAuto, *pNode, nIndex, c, !bOverwrite );
		aSel.Max().SetIndex( aAuto.GetCursor() );

		// #i78661 since the SvxAutoCorrect object used here is
		// shared we need to reset the value to it's original state.
		pAutoCorrect->SetAutoCorrFlag( CptlSttSntnc, bOldCptlSttSntnc );
	}
#endif // !SVX_LIGHT
	return aSel.Max();
}


EditPaM ImpEditEngine::InsertText( const EditSelection& rCurSel,
        xub_Unicode c, BOOL bOverwrite, sal_Bool bIsUserInput )
{
	DBG_ASSERT( c != '\t', "Tab bei InsertText ?" );
	DBG_ASSERT( c != '\n', "Zeilenumbruch bei InsertText ?" );

	EditPaM aPaM( rCurSel.Min() );

	BOOL bDoOverwrite = ( bOverwrite &&
			( aPaM.GetIndex() < aPaM.GetNode()->Len() ) ) ? TRUE : FALSE;

	BOOL bUndoAction = ( rCurSel.HasRange() || bDoOverwrite );

	if ( bUndoAction )
		UndoActionStart( EDITUNDO_INSERT );

	if ( rCurSel.HasRange() )
	{
		aPaM = ImpDeleteSelection( rCurSel );
	}
	else if ( bDoOverwrite )
	{
		// Wenn Selektion, dann nicht auch noch ein Zeichen ueberschreiben!
		EditSelection aTmpSel( aPaM );
		aTmpSel.Max().GetIndex()++;
		DBG_ASSERT( !aTmpSel.DbgIsBuggy( aEditDoc ), "Overwrite: Fehlerhafte Selektion!" );
		ImpDeleteSelection( aTmpSel );
	}

	if ( aPaM.GetNode()->Len() < MAXCHARSINPARA )
	{
        if (bIsUserInput && IsInputSequenceCheckingRequired( c, rCurSel ))
        {
            uno::Reference < i18n::XExtendedInputSequenceChecker > _xISC( ImplGetInputSequenceChecker() );
            if (!pCTLOptions)
                pCTLOptions = new SvtCTLOptions;

            if (_xISC.is() || pCTLOptions)
            {
                xub_StrLen nTmpPos = aPaM.GetIndex();
                sal_Int16 nCheckMode = pCTLOptions->IsCTLSequenceCheckingRestricted() ?
                        i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC;

				// the text that needs to be checked is only the one
				// before the current cursor position
                rtl::OUString aOldText( aPaM.GetNode()->Copy(0, nTmpPos) );
                rtl::OUString aNewText( aOldText );
                if (pCTLOptions->IsCTLSequenceCheckingTypeAndReplace())
                {
                    /*const xub_StrLen nPrevPos = static_cast< xub_StrLen >*/( _xISC->correctInputSequence( aNewText, nTmpPos - 1, c, nCheckMode ) );

                    // find position of first character that has changed
                    sal_Int32 nOldLen = aOldText.getLength();
                    sal_Int32 nNewLen = aNewText.getLength();
                    const sal_Unicode *pOldTxt = aOldText.getStr();
                    const sal_Unicode *pNewTxt = aNewText.getStr();
                    sal_Int32 nChgPos = 0;
                    while ( nChgPos < nOldLen && nChgPos < nNewLen &&
                            pOldTxt[nChgPos] == pNewTxt[nChgPos] )
                        ++nChgPos;

                    xub_StrLen nChgLen = static_cast< xub_StrLen >( nNewLen - nChgPos );
					String aChgText( aNewText.copy( nChgPos ), nChgLen );

                    // select text from first pos to be changed to current pos
                    EditSelection aSel( EditPaM( aPaM.GetNode(), (USHORT) nChgPos ), aPaM );

					if (aChgText.Len())
						return InsertText( aSel, aChgText ); // implicitly handles undo
					else
						return aPaM;
                }
                else
                {
                    // should the character be ignored (i.e. not get inserted) ?
                    if (!_xISC->checkInputSequence( aOldText, nTmpPos - 1, c, nCheckMode ))
                        return aPaM;    // nothing to be done -> no need for undo
                }
            }

            // at this point now we will insert the character 'normally' some lines below...
        }

		if ( IsUndoEnabled() && !IsInUndo() )
		{
			EditUndoInsertChars* pNewUndo = new EditUndoInsertChars( this, CreateEPaM( aPaM ), c );
			BOOL bTryMerge = ( !bDoOverwrite && ( c != ' ' ) ) ? TRUE : FALSE;
			InsertUndo( pNewUndo, bTryMerge );
		}

        aEditDoc.InsertText( (const EditPaM&)aPaM, c );
		ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
		DBG_ASSERT( pPortion, "Blinde Portion in InsertText" );
        pPortion->MarkInvalid( aPaM.GetIndex(), 1 );
        aPaM.GetIndex()++;   // macht EditDoc-Methode nicht mehr
	}

	TextModified();

	if ( bUndoAction )
		UndoActionEnd( EDITUNDO_INSERT );

	return aPaM;
}

EditPaM ImpEditEngine::ImpInsertText( EditSelection aCurSel, const XubString& rStr )
{
	UndoActionStart( EDITUNDO_INSERT );

	EditPaM aPaM;
	if ( aCurSel.HasRange() )
		aPaM = ImpDeleteSelection( aCurSel );
	else
		aPaM = aCurSel.Max();

	EditPaM aCurPaM( aPaM );	// fuers Invalidieren

	XubString aText( rStr );
	aText.ConvertLineEnd( LINEEND_LF );
	SfxVoidItem aTabItem( EE_FEATURE_TAB );

	// Konvertiert nach LineSep = \n
	// Token mit LINE_SEP abfragen,
	// da der MAC-Compiler aus \n etwas anderes macht!

	USHORT nStart = 0;
	while ( nStart < aText.Len() )
	{
		USHORT nEnd = aText.Search( LINE_SEP, nStart );
		if ( nEnd == STRING_NOTFOUND )
			nEnd = aText.Len();	// nicht dereferenzieren!

		// Start == End => Leerzeile
		if ( nEnd > nStart )
		{
			XubString aLine( aText, nStart, nEnd-nStart );
			xub_StrLen nChars = aPaM.GetNode()->Len() + aLine.Len();
			if ( nChars > MAXCHARSINPARA )
			{
                USHORT nMaxNewChars = MAXCHARSINPARA-aPaM.GetNode()->Len();
				nEnd -= ( aLine.Len() - nMaxNewChars );	// Dann landen die Zeichen im naechsten Absatz.
				aLine.Erase( nMaxNewChars );            // Del Rest...
			}
#ifndef SVX_LIGHT
			if ( IsUndoEnabled() && !IsInUndo() )
				InsertUndo( new EditUndoInsertChars( this, CreateEPaM( aPaM ), aLine ) );
#endif
			// Tabs ?
			if ( aLine.Search( '\t' ) == STRING_NOTFOUND )
				aPaM = aEditDoc.InsertText( aPaM, aLine );
			else
			{
				USHORT nStart2 = 0;
				while ( nStart2 < aLine.Len() )
				{
					USHORT nEnd2 = aLine.Search( '\t', nStart2 );
					if ( nEnd2 == STRING_NOTFOUND )
						nEnd2 = aLine.Len();	// nicht dereferenzieren!

					if ( nEnd2 > nStart2 )
						aPaM = aEditDoc.InsertText( aPaM, XubString( aLine, nStart2, nEnd2-nStart2 ) );
					if ( nEnd2 < aLine.Len() )
					{
						// aPaM = ImpInsertFeature( EditSelection( aPaM, aPaM ),  );
						aPaM = aEditDoc.InsertFeature( aPaM, aTabItem );
					}
					nStart2 = nEnd2+1;
				}
			}
			ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
			DBG_ASSERT( pPortion, "Blinde Portion in InsertText" );
			pPortion->MarkInvalid( aCurPaM.GetIndex(), aLine.Len() );
		}
		if ( nEnd < aText.Len() )
			aPaM = ImpInsertParaBreak( aPaM );

		nStart = nEnd+1;
	}

	UndoActionEnd( EDITUNDO_INSERT );

	TextModified();
	return aPaM;
}

EditPaM ImpEditEngine::ImpFastInsertText( EditPaM aPaM, const XubString& rStr )
{
	DBG_ASSERT( rStr.Search( 0x0A ) == STRING_NOTFOUND, "FastInsertText: Zeilentrenner nicht erlaubt!" );
	DBG_ASSERT( rStr.Search( 0x0D ) == STRING_NOTFOUND, "FastInsertText: Zeilentrenner nicht erlaubt!" );
	DBG_ASSERT( rStr.Search( '\t' ) == STRING_NOTFOUND, "FastInsertText: Features nicht erlaubt!" );

	if ( ( aPaM.GetNode()->Len() + rStr.Len() ) < MAXCHARSINPARA )
	{
#ifndef SVX_LIGHT
		if ( IsUndoEnabled() && !IsInUndo() )
			InsertUndo( new EditUndoInsertChars( this, CreateEPaM( aPaM ), rStr ) );
#endif

		aPaM = aEditDoc.InsertText( aPaM, rStr );
		TextModified();
	}
	else
	{
		aPaM = ImpInsertText( aPaM, rStr );
	}

	return aPaM;
}

EditPaM ImpEditEngine::ImpInsertFeature( EditSelection aCurSel, const SfxPoolItem& rItem )
{
	EditPaM aPaM;
	if ( aCurSel.HasRange() )
		aPaM = ImpDeleteSelection( aCurSel );
	else
		aPaM = aCurSel.Max();

	if ( aPaM.GetIndex() >= 0xfffe )
		return aPaM;

#ifndef SVX_LIGHT
	if ( IsUndoEnabled() && !IsInUndo() )
		InsertUndo( new EditUndoInsertFeature( this, CreateEPaM( aPaM ), rItem ) );
#endif
	aPaM = aEditDoc.InsertFeature( aPaM, rItem );

	ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
	DBG_ASSERT( pPortion, "Blinde Portion in InsertFeature" );
	pPortion->MarkInvalid( aPaM.GetIndex()-1, 1 );

	TextModified();

	return aPaM;
}

EditPaM ImpEditEngine::ImpInsertParaBreak( const EditSelection& rCurSel, BOOL bKeepEndingAttribs )
{
	EditPaM aPaM;
	if ( rCurSel.HasRange() )
		aPaM = ImpDeleteSelection( rCurSel );
	else
		aPaM = rCurSel.Max();

	return ImpInsertParaBreak( aPaM, bKeepEndingAttribs );
}

EditPaM ImpEditEngine::ImpInsertParaBreak( const EditPaM& rPaM, BOOL bKeepEndingAttribs )
{
	if ( aEditDoc.Count() >= 0xFFFE )
	{
		DBG_ERROR( "Can't process more than 64K paragraphs!" );
		return rPaM;
	}

#ifndef SVX_LIGHT
	if ( IsUndoEnabled() && !IsInUndo() )
		InsertUndo( new EditUndoSplitPara( this, aEditDoc.GetPos( rPaM.GetNode() ), rPaM.GetIndex() ) );
#endif

	EditPaM aPaM( aEditDoc.InsertParaBreak( rPaM, bKeepEndingAttribs ) );

#ifndef SVX_LIGHT
	if ( GetStatus().DoOnlineSpelling() )
	{
		xub_StrLen nEnd = rPaM.GetNode()->Len();
		aPaM.GetNode()->CreateWrongList();
		WrongList* pLWrongs = rPaM.GetNode()->GetWrongList();
		WrongList* pRWrongs = aPaM.GetNode()->GetWrongList();
		// Falschgeschriebene Woerter ruebernehmen:
		USHORT nLWrongs = pLWrongs->Count();
		for ( USHORT nW = 0; nW < nLWrongs; nW++ )
		{
			WrongRange& rWrong = pLWrongs->GetObject( nW );
			// Nur wenn wirklich dahinter, ein ueberlappendes wird beim Spell korrigiert
			if ( rWrong.nStart > nEnd )
			{
				pRWrongs->InsertWrong( rWrong, pRWrongs->Count() );
				WrongRange& rRWrong = pRWrongs->GetObject( pRWrongs->Count() - 1 );
				rRWrong.nStart = rRWrong.nStart - nEnd;
				rRWrong.nEnd = rRWrong.nEnd - nEnd;
			}
			else if ( ( rWrong.nStart < nEnd ) && ( rWrong.nEnd > nEnd ) )
				rWrong.nEnd = nEnd;
		}
		USHORT nInv = nEnd ? nEnd-1 : nEnd;
		if ( nEnd )
			pLWrongs->MarkInvalid( nInv, nEnd );
		else
			pLWrongs->SetValid();
		pRWrongs->SetValid(); // sonst 0 - 0xFFFF
		pRWrongs->MarkInvalid( 0, 1 );	// Nur das erste Wort testen
	}
#endif // !SVX_LIGHT


	ParaPortion* pPortion = FindParaPortion( rPaM.GetNode() );
	DBG_ASSERT( pPortion, "Blinde Portion in ImpInsertParaBreak" );
	pPortion->MarkInvalid( rPaM.GetIndex(), 0 );

	// Optimieren: Nicht unnoetig viele GetPos auf die Listen ansetzen!
	// Hier z.B. bei Undo, aber auch in allen anderen Methoden.
	USHORT nPos = GetParaPortions().GetPos( pPortion );
	ParaPortion* pNewPortion = new ParaPortion( aPaM.GetNode() );
	GetParaPortions().Insert( pNewPortion, nPos + 1 );
	ParaAttribsChanged( pNewPortion->GetNode() );
	if ( IsCallParaInsertedOrDeleted() )
		GetEditEnginePtr()->ParagraphInserted( nPos+1 );

	CursorMoved( rPaM.GetNode() );	// falls leeres Attribut entstanden.
	TextModified();
	return aPaM;
}

EditPaM ImpEditEngine::ImpFastInsertParagraph( USHORT nPara )
{
#ifndef SVX_LIGHT
	if ( IsUndoEnabled() && !IsInUndo() )
	{
		if ( nPara )
		{
			DBG_ASSERT( aEditDoc.SaveGetObject( nPara-1 ), "FastInsertParagraph: Prev existiert nicht" );
			InsertUndo( new EditUndoSplitPara( this, nPara-1, aEditDoc.GetObject( nPara-1 )->Len() ) );
		}
		else
			InsertUndo( new EditUndoSplitPara( this, 0, 0 ) );
	}
#endif

	ContentNode* pNode = new ContentNode( aEditDoc.GetItemPool() );
	// Falls FlatMode, wird spaeter kein Font eingestellt:
	pNode->GetCharAttribs().GetDefFont() = aEditDoc.GetDefFont();

#ifndef SVX_LIGHT
	if ( GetStatus().DoOnlineSpelling() )
		pNode->CreateWrongList();
#endif // !SVX_LIGHT

	aEditDoc.Insert( pNode, nPara );

	ParaPortion* pNewPortion = new ParaPortion( pNode );
	GetParaPortions().Insert( pNewPortion, nPara );
	if ( IsCallParaInsertedOrDeleted() )
		GetEditEnginePtr()->ParagraphInserted( nPara );

	return EditPaM( pNode, 0 );
}

EditPaM ImpEditEngine::InsertParaBreak( EditSelection aCurSel )
{
	EditPaM aPaM( ImpInsertParaBreak( aCurSel ) );
	if ( aStatus.DoAutoIndenting() )
	{
		USHORT nPara = aEditDoc.GetPos( aPaM.GetNode() );
		DBG_ASSERT( nPara > 0, "AutoIndenting: Fehler!" );
		XubString aPrevParaText( GetEditDoc().GetParaAsString( nPara-1 ) );
		USHORT n = 0;
		while ( ( n < aPrevParaText.Len() ) &&
				( ( aPrevParaText.GetChar(n) == ' ' ) || ( aPrevParaText.GetChar(n) == '\t' ) ) )
		{
			if ( aPrevParaText.GetChar(n) == '\t' )
				aPaM = ImpInsertFeature( aPaM, SfxVoidItem( EE_FEATURE_TAB ) );
			else
				aPaM = ImpInsertText( aPaM, aPrevParaText.GetChar(n) );
			n++;
		}

	}
	return aPaM;
}

EditPaM ImpEditEngine::InsertTab( EditSelection aCurSel )
{
	EditPaM aPaM( ImpInsertFeature( aCurSel, SfxVoidItem( EE_FEATURE_TAB ) ) );
	return aPaM;
}

EditPaM ImpEditEngine::InsertField( EditSelection aCurSel, const SvxFieldItem& rFld )
{
	EditPaM aPaM( ImpInsertFeature( aCurSel, rFld ) );
	return aPaM;
}

BOOL ImpEditEngine::UpdateFields()
{
	BOOL bChanges = FALSE;
	USHORT nParas = GetEditDoc().Count();
	for ( USHORT nPara = 0; nPara < nParas; nPara++ )
	{
		BOOL bChangesInPara = FALSE;
		ContentNode* pNode = GetEditDoc().GetObject( nPara );
		DBG_ASSERT( pNode, "NULL-Pointer im Doc" );
		CharAttribArray& rAttribs = pNode->GetCharAttribs().GetAttribs();
//		USHORT nAttrs = rAttribs.Count();
		for ( USHORT nAttr = 0; nAttr < rAttribs.Count(); nAttr++ )
		{
			EditCharAttrib* pAttr = rAttribs[nAttr];
			if ( pAttr->Which() == EE_FEATURE_FIELD )
			{
				EditCharAttribField* pField = (EditCharAttribField*)pAttr;
				EditCharAttribField* pCurrent = new EditCharAttribField( *pField );
				pField->Reset();

                if ( aStatus.MarkFields() )
                    pField->GetFldColor() = new Color( GetColorConfig().GetColorValue( svtools::WRITERFIELDSHADINGS ).nColor );

				XubString aFldValue = GetEditEnginePtr()->CalcFieldValue(
										(const SvxFieldItem&)*pField->GetItem(),
										nPara, pField->GetStart(),
										pField->GetTxtColor(), pField->GetFldColor() );
				pField->GetFieldValue() = aFldValue;
				if ( *pField != *pCurrent )
				{
					bChanges = TRUE;
					bChangesInPara = TRUE;
				}
				delete pCurrent;
			}
		}
		if ( bChangesInPara )
		{
			// ggf. etwas genauer invalidieren.
			ParaPortion* pPortion = GetParaPortions().GetObject( nPara );
			DBG_ASSERT( pPortion, "NULL-Pointer im Doc" );
			pPortion->MarkSelectionInvalid( 0, pNode->Len() );
		}
	}
	return bChanges;
}

EditPaM ImpEditEngine::InsertLineBreak( EditSelection aCurSel )
{
	EditPaM aPaM( ImpInsertFeature( aCurSel, SfxVoidItem( EE_FEATURE_LINEBR ) ) );
	return aPaM;
}

//	----------------------------------------------------------------------
//	Hilfsfunktionen
//	----------------------------------------------------------------------
Rectangle ImpEditEngine::PaMtoEditCursor( EditPaM aPaM, USHORT nFlags )
{
	DBG_ASSERT( GetUpdateMode(), "Darf bei Update=FALSE nicht erreicht werden: PaMtoEditCursor" );

	Rectangle aEditCursor;
	long nY = 0;
	for ( USHORT nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
	{
		ParaPortion* pPortion = GetParaPortions().GetObject(nPortion);
		ContentNode* pNode = pPortion->GetNode();
		DBG_ASSERT( pNode, "Ungueltiger Node in Portion!" );
		if ( pNode != aPaM.GetNode() )
		{
			nY += pPortion->GetHeight();
		}
		else
		{
			aEditCursor = GetEditCursor( pPortion, aPaM.GetIndex(), nFlags );
			aEditCursor.Top() += nY;
			aEditCursor.Bottom() += nY;
			return aEditCursor;
		}
	}
	DBG_ERROR( "Portion nicht gefunden!" );
	return aEditCursor;
}

EditPaM ImpEditEngine::GetPaM( Point aDocPos, BOOL bSmart )
{
	DBG_ASSERT( GetUpdateMode(), "Darf bei Update=FALSE nicht erreicht werden: GetPaM" );

	long nY = 0;
	long nTmpHeight;
	EditPaM aPaM;
	USHORT nPortion;
	for ( nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
	{
		ParaPortion* pPortion = GetParaPortions().GetObject(nPortion);
		nTmpHeight = pPortion->GetHeight(); 	// sollte auch bei !bVisible richtig sein!
		nY += nTmpHeight;
		if ( nY > aDocPos.Y() )
		{
			nY -= nTmpHeight;
			aDocPos.Y() -= nY;
			// unsichtbare Portions ueberspringen:
			while ( pPortion && !pPortion->IsVisible() )
			{
				nPortion++;
				pPortion = GetParaPortions().SaveGetObject( nPortion );
			}
			DBG_ASSERT( pPortion, "Keinen sichtbaren Absatz gefunden: GetPaM" );
			aPaM = GetPaM( pPortion, aDocPos, bSmart );
			return aPaM;

		}
	}
	// Dann den letzten sichtbaren Suchen:
	nPortion = GetParaPortions().Count()-1;
	while ( nPortion && !GetParaPortions()[nPortion]->IsVisible() )
		nPortion--;

	DBG_ASSERT( GetParaPortions()[nPortion]->IsVisible(), "Keinen sichtbaren Absatz gefunden: GetPaM" );
	aPaM.SetNode( GetParaPortions()[nPortion]->GetNode() );
	aPaM.SetIndex( GetParaPortions()[nPortion]->GetNode()->Len() );
	return aPaM;
}

sal_uInt32 ImpEditEngine::GetTextHeight() const
{
	DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=FALSE nicht verwendet werden: GetTextHeight" );
	DBG_ASSERT( IsFormatted() || IsFormatting(), "GetTextHeight: Nicht formatiert" );
	return nCurTextHeight;
}

sal_uInt32 ImpEditEngine::CalcTextWidth( BOOL bIgnoreExtraSpace )
{
	// Wenn noch nicht formatiert und nicht gerade dabei.
	// Wird in der Formatierung bei AutoPageSize gerufen.
	if ( !IsFormatted() && !IsFormatting() )
		FormatDoc();

	EditLine* pLine;

	long nMaxWidth = 0;
	long nCurWidth = 0;

	// --------------------------------------------------
	// Ueber alle Absaetze...
	// --------------------------------------------------
	USHORT nParas = GetParaPortions().Count();
//	USHORT nBiggestPara = 0;
//	USHORT nBiggestLine = 0;
	for ( USHORT nPara = 0; nPara < nParas; nPara++ )
	{
		ParaPortion* pPortion = GetParaPortions().GetObject( nPara );
		if ( pPortion->IsVisible() )
		{
            const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pPortion->GetNode() );
            sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pPortion->GetNode() );

			// --------------------------------------------------
			// Ueber die Zeilen des Absatzes...
			// --------------------------------------------------
			ULONG nLines = pPortion->GetLines().Count();
			for ( USHORT nLine = 0; nLine < nLines; nLine++ )
			{
				pLine = pPortion->GetLines().GetObject( nLine );
				DBG_ASSERT( pLine, "NULL-Pointer im Zeileniterator in CalcWidth" );
				// nCurWidth = pLine->GetStartPosX();
				// Bei Center oder Right haengt die breite von der
				// Papierbreite ab, hier nicht erwuenscht.
				// Am besten generell nicht auf StartPosX verlassen,
				// es muss auch die rechte Einrueckung beruecksichtigt werden!
                nCurWidth = GetXValue( rLRItem.GetTxtLeft() + nSpaceBeforeAndMinLabelWidth );
				if ( nLine == 0 )
				{
					long nFI = GetXValue( rLRItem.GetTxtFirstLineOfst() );
					nCurWidth -= nFI;
					if ( pPortion->GetBulletX() > nCurWidth )
					{
						nCurWidth += nFI;	// LI?
						if ( pPortion->GetBulletX() > nCurWidth )
							nCurWidth = pPortion->GetBulletX();
					}
				}
				nCurWidth += GetXValue( rLRItem.GetRight() );
				nCurWidth += CalcLineWidth( pPortion, pLine, bIgnoreExtraSpace );
				if ( nCurWidth > nMaxWidth )
				{
					nMaxWidth = nCurWidth;
				}
			}
		}
	}
	if ( nMaxWidth < 0 )
		nMaxWidth = 0;

	nMaxWidth++; // Ein breiter, da in CreateLines bei >= umgebrochen wird.
	return (sal_uInt32)nMaxWidth;
}

sal_uInt32 ImpEditEngine::CalcLineWidth( ParaPortion* pPortion, EditLine* pLine, BOOL bIgnoreExtraSpace )
{
	USHORT nPara = GetEditDoc().GetPos( pPortion->GetNode() );

    // #114278# Saving both layout mode and language (since I'm
    // potentially changing both)
    GetRefDevice()->Push( PUSH_TEXTLAYOUTMODE|PUSH_TEXTLANGUAGE );

    ImplInitLayoutMode( GetRefDevice(), nPara, 0xFFFF );

    SvxAdjust eJustification = GetJustification( nPara );

    // Berechnung der Breite ohne die Indents...
	sal_uInt32 nWidth = 0;
    USHORT nPos = pLine->GetStart();
	for ( USHORT nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ )
	{
		TextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( nTP );
		switch ( pTextPortion->GetKind() )
		{
			case PORTIONKIND_FIELD:
			case PORTIONKIND_HYPHENATOR:
			case PORTIONKIND_TAB:
			{
				nWidth += pTextPortion->GetSize().Width();
			}
			break;
			case PORTIONKIND_TEXT:
			{
                if ( ( eJustification != SVX_ADJUST_BLOCK ) || ( !bIgnoreExtraSpace ) )
                {
				    nWidth += pTextPortion->GetSize().Width();
                }
                else
                {
	                SvxFont aTmpFont( pPortion->GetNode()->GetCharAttribs().GetDefFont() );
				    SeekCursor( pPortion->GetNode(), nPos+1, aTmpFont );
				    aTmpFont.SetPhysFont( GetRefDevice() );
                    ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
                    nWidth += aTmpFont.QuickGetTextSize( GetRefDevice(), *pPortion->GetNode(), nPos, pTextPortion->GetLen(), NULL ).Width();
                }
			}
			break;
		}
        nPos = nPos + pTextPortion->GetLen();
	}

    GetRefDevice()->Pop();

    return nWidth;
}

sal_uInt32 ImpEditEngine::CalcTextHeight()
{
	DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=FALSE nicht verwendet werden: CalcTextHeight" );
	sal_uInt32 nY = 0;
	for ( USHORT nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
		nY += GetParaPortions()[nPortion]->GetHeight();
	return nY;
}

USHORT ImpEditEngine::GetLineCount( USHORT nParagraph ) const
{
	DBG_ASSERT( nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" );
	ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
	DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetLineCount" );
	if ( pPPortion )
		return pPPortion->GetLines().Count();

	return 0xFFFF;
}

xub_StrLen ImpEditEngine::GetLineLen( USHORT nParagraph, USHORT nLine ) const
{
	DBG_ASSERT( nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" );
	ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
	DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetLineHeight" );
	if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) )
	{
		EditLine* pLine = pPPortion->GetLines().GetObject( nLine );
		DBG_ASSERT( pLine, "Zeile nicht gefunden: GetLineHeight" );
		return pLine->GetLen();
	}

	return 0xFFFF;
}

USHORT ImpEditEngine::GetLineHeight( USHORT nParagraph, USHORT nLine )
{
	DBG_ASSERT( nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" );
	ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
	DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetLineHeight" );
	if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) )
	{
		EditLine* pLine = pPPortion->GetLines().GetObject( nLine );
		DBG_ASSERT( pLine, "Zeile nicht gefunden: GetLineHeight" );
		return pLine->GetHeight();
	}

	return 0xFFFF;
}

sal_uInt32 ImpEditEngine::GetParaHeight( USHORT nParagraph )
{
	sal_uInt32 nHeight = 0;

	ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
	DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetParaHeight" );

	if ( pPPortion )
		nHeight = pPPortion->GetHeight();

	return nHeight;
}

void ImpEditEngine::UpdateSelections()
{
	USHORT nInvNodes = aDeletedNodes.Count();

	// Pruefen, ob eine der Selektionen auf einem geloeschten Node steht...
	// Wenn der Node gueltig ist, muss noch der Index geprueft werden!
	for ( USHORT nView = 0; nView < aEditViews.Count(); nView++ )
	{
		EditView* pView = aEditViews.GetObject(nView);
		DBG_CHKOBJ( pView, EditView, 0 );
		EditSelection aCurSel( pView->pImpEditView->GetEditSelection() );
		BOOL bChanged = FALSE;
		for ( USHORT n = 0; n < nInvNodes; n++ )
		{
			DeletedNodeInfo* pInf = aDeletedNodes.GetObject( n );
			if ( ( ( ULONG )(aCurSel.Min().GetNode()) == pInf->GetInvalidAdress() ) ||
				 ( ( ULONG )(aCurSel.Max().GetNode()) == pInf->GetInvalidAdress() ) )
			{
				// ParaPortions verwenden, da jetzt auch versteckte
				// Absaetze beruecksichtigt werden muessen!
				USHORT nPara = pInf->GetPosition();
				ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nPara );
				if ( !pPPortion ) // letzter Absatz
				{
					nPara = GetParaPortions().Count()-1;
					pPPortion = GetParaPortions().GetObject( nPara );
				}
				DBG_ASSERT( pPPortion, "Leeres Document in UpdateSelections ?" );
				// Nicht aus einem verstecktem Absatz landen:
				USHORT nCurPara = nPara;
				USHORT nLastPara = GetParaPortions().Count()-1;
				while ( nPara <= nLastPara && !GetParaPortions()[nPara]->IsVisible() )
					nPara++;
				if ( nPara > nLastPara ) // dann eben rueckwaerts...
				{
					nPara = nCurPara;
					while ( nPara && !GetParaPortions()[nPara]->IsVisible() )
						nPara--;
				}
				DBG_ASSERT( GetParaPortions()[nPara]->IsVisible(), "Keinen sichtbaren Absatz gefunden: UpdateSelections" );

				ParaPortion* pParaPortion = GetParaPortions()[nPara];
				EditSelection aTmpSelection( EditPaM( pParaPortion->GetNode(), 0 ) );
				pView->pImpEditView->SetEditSelection( aTmpSelection );
				bChanged=TRUE;
				break;	// for-Schleife
			}
		}
		if ( !bChanged )
		{
			// Index prueffen, falls Node geschrumpft.
			if ( aCurSel.Min().GetIndex() > aCurSel.Min().GetNode()->Len() )
			{
				aCurSel.Min().GetIndex() = aCurSel.Min().GetNode()->Len();
				pView->pImpEditView->SetEditSelection( aCurSel );
			}
			if ( aCurSel.Max().GetIndex() > aCurSel.Max().GetNode()->Len() )
			{
				aCurSel.Max().GetIndex() = aCurSel.Max().GetNode()->Len();
				pView->pImpEditView->SetEditSelection( aCurSel );
			}
		}
	}

	// Loeschen...
	for ( USHORT n = 0; n < nInvNodes; n++ )
	{
		DeletedNodeInfo* pInf = aDeletedNodes.GetObject( n );
		delete pInf;
	}
	aDeletedNodes.Remove( 0, aDeletedNodes.Count() );
}

EditSelection ImpEditEngine::ConvertSelection( USHORT nStartPara, USHORT nStartPos,
							 USHORT nEndPara, USHORT nEndPos ) const
{
	EditSelection aNewSelection;

	// Start...
	ContentNode* pNode = aEditDoc.SaveGetObject( nStartPara );
	USHORT nIndex = nStartPos;
	if ( !pNode )
	{
		pNode = aEditDoc[ aEditDoc.Count()-1 ];
		nIndex = pNode->Len();
	}
	else if ( nIndex > pNode->Len() )
		nIndex = pNode->Len();

	aNewSelection.Min().SetNode( pNode );
	aNewSelection.Min().SetIndex( nIndex );

	// End...
	pNode = aEditDoc.SaveGetObject( nEndPara );
	nIndex = nEndPos;
	if ( !pNode )
	{
		pNode = aEditDoc[ aEditDoc.Count()-1 ];
		nIndex = pNode->Len();
	}
	else if ( nIndex > pNode->Len() )
		nIndex = pNode->Len();

	aNewSelection.Max().SetNode( pNode );
	aNewSelection.Max().SetIndex( nIndex );

	return aNewSelection;
}

EditSelection ImpEditEngine::MatchGroup( const EditSelection& rSel )
{
	EditSelection aMatchSel;
	EditSelection aTmpSel( rSel );
	aTmpSel.Adjust( GetEditDoc() );
	if ( (	aTmpSel.Min().GetNode() != aTmpSel.Max().GetNode() ) ||
		 ( ( aTmpSel.Max().GetIndex() - aTmpSel.Min().GetIndex() ) > 1 ) )
	{
		return aMatchSel;
	}

	USHORT nPos = aTmpSel.Min().GetIndex();
	ContentNode* pNode = aTmpSel.Min().GetNode();
	if ( nPos >= pNode->Len() )
		return aMatchSel;

	USHORT nMatchChar = aGroupChars.Search( pNode->GetChar( nPos ) );
	if ( nMatchChar != STRING_NOTFOUND )
	{
		USHORT nNode = aEditDoc.GetPos( pNode );
		if ( ( nMatchChar % 2 ) == 0 )
		{
			// Vorwaerts suchen...
			xub_Unicode nSC = aGroupChars.GetChar( nMatchChar );
			DBG_ASSERT( aGroupChars.Len() > (nMatchChar+1), "Ungueltige Gruppe von MatchChars!" );
			xub_Unicode nEC = aGroupChars.GetChar( nMatchChar+1 );

			USHORT nCur = aTmpSel.Min().GetIndex()+1;
			USHORT nLevel = 1;
			while ( pNode && nLevel )
			{
				XubString& rStr = *pNode;
				while ( nCur < rStr.Len() )
				{
					if ( rStr.GetChar( nCur ) == nSC )
						nLevel++;
					else if ( rStr.GetChar( nCur ) == nEC )
					{
						nLevel--;
						if ( !nLevel )
							break;	// while nCur...
					}
					nCur++;
				}

				if ( nLevel )
				{
					nNode++;
					pNode = nNode < aEditDoc.Count() ? aEditDoc.GetObject( nNode ) : 0;
					nCur = 0;
				}
			}
			if ( nLevel == 0 )	// gefunden
			{
				aMatchSel.Min() = aTmpSel.Min();
				aMatchSel.Max() = EditPaM( pNode, nCur+1 );
			}
		}
		else
		{
			// Rueckwaerts suchen...
			xub_Unicode nEC = aGroupChars.GetChar( nMatchChar );
			xub_Unicode nSC = aGroupChars.GetChar( nMatchChar-1 );

			USHORT nCur = aTmpSel.Min().GetIndex()-1;
			USHORT nLevel = 1;
			while ( pNode && nLevel )
			{
				if ( pNode->Len() )
				{
					XubString& rStr = *pNode;
					while ( nCur )
					{
						if ( rStr.GetChar( nCur ) == nSC )
						{
							nLevel--;
							if ( !nLevel )
								break;	// while nCur...
						}
						else if ( rStr.GetChar( nCur ) == nEC )
							nLevel++;

						nCur--;
					}
				}

				if ( nLevel )
				{
					pNode = nNode ? aEditDoc.GetObject( --nNode ) : 0;
					if ( pNode )
						nCur = pNode->Len()-1;	// egal ob negativ, weil if Len()
				}
			}

			if ( nLevel == 0 )	// gefunden
			{
				aMatchSel.Min() = aTmpSel.Min();
				aMatchSel.Min().GetIndex()++;	// hinter das Zeichen
				aMatchSel.Max() = EditPaM( pNode, nCur );
			}
		}
	}
	return aMatchSel;
}

void ImpEditEngine::StopSelectionMode()
{
	if ( ( IsInSelectionMode() || aSelEngine.IsInSelection() ) && pActiveView )
	{
		pActiveView->pImpEditView->DrawSelection();	// Wegzeichnen...
		EditSelection aSel( pActiveView->pImpEditView->GetEditSelection() );
		aSel.Min() = aSel.Max();
		pActiveView->pImpEditView->SetEditSelection( aSel );
		pActiveView->ShowCursor();
		aSelEngine.Reset();
		bInSelection = FALSE;
	}
}

void ImpEditEngine::SetActiveView( EditView* pView )
{
	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	// Eigentlich waere jetzt ein bHasVisSel und HideSelection notwendig !!!

	if ( pView == pActiveView )
		return;

	if ( pActiveView && pActiveView->HasSelection() )
		pActiveView->pImpEditView->DrawSelection();	// Wegzeichnen...

	pActiveView = pView;

	if ( pActiveView && pActiveView->HasSelection() )
		pActiveView->pImpEditView->DrawSelection();	// Wegzeichnen...

	//	NN: Quick fix for #78668#:
	//	When editing of a cell in Calc is ended, the edit engine is not deleted,
	//	only the edit views are removed. If mpIMEInfos is still set in that case,
	//	mpIMEInfos->aPos points to an invalid selection.
	//	-> reset mpIMEInfos now
	//	(probably something like this is necessary whenever the content is modified
	//	from the outside)

	if ( !pView && mpIMEInfos )
	{
		delete mpIMEInfos;
		mpIMEInfos = NULL;
	}
}

uno::Reference< datatransfer::XTransferable > ImpEditEngine::CreateTransferable( const EditSelection& rSelection ) const
{
#ifndef SVX_LIGHT
    EditSelection aSelection( rSelection );
	aSelection.Adjust( GetEditDoc() );

	EditDataObject* pDataObj = new EditDataObject;
	uno::Reference< datatransfer::XTransferable > xDataObj;
	xDataObj = pDataObj;

	XubString aText( GetSelected( aSelection ) );
	aText.ConvertLineEnd();	// Systemspezifisch
	pDataObj->GetString() = aText;

    SvxFontItem::EnableStoreUnicodeNames( TRUE );
	WriteBin( pDataObj->GetStream(), aSelection, TRUE );
	pDataObj->GetStream().Seek( 0 );
    SvxFontItem::EnableStoreUnicodeNames( FALSE );

    ((ImpEditEngine*)this)->WriteRTF( pDataObj->GetRTFStream(), aSelection );
	pDataObj->GetRTFStream().Seek( 0 );

	if ( ( aSelection.Min().GetNode() == aSelection.Max().GetNode() )
			&& ( aSelection.Max().GetIndex() == (aSelection.Min().GetIndex()+1) ) )
	{
		const EditCharAttrib* pAttr = aSelection.Min().GetNode()->GetCharAttribs().
			FindFeature( aSelection.Min().GetIndex() );
		if ( pAttr &&
			( pAttr->GetStart() == aSelection.Min().GetIndex() ) &&
			( pAttr->Which() == EE_FEATURE_FIELD ) )
		{
			const SvxFieldItem*	pField = (const SvxFieldItem*)pAttr->GetItem();
			const SvxFieldData* pFld = pField->GetField();
			if ( pFld && pFld->ISA( SvxURLField ) )
			{
				// Office-Bookmark
				String aURL( ((const SvxURLField*)pFld)->GetURL() );
				String aTxt( ((const SvxURLField*)pFld)->GetRepresentation() );
				pDataObj->GetURL() = aURL;
			}
		}
	}

    return xDataObj;
#else
	return uno::Reference< datatransfer::XTransferable >();
#endif
}

EditSelection ImpEditEngine::InsertText( uno::Reference< datatransfer::XTransferable >& rxDataObj, const String& rBaseURL, const EditPaM& rPaM, BOOL bUseSpecial )
{
	EditSelection aNewSelection( rPaM );

	if ( rxDataObj.is() )
	{
		datatransfer::DataFlavor aFlavor;
		BOOL bDone = FALSE;

		if ( bUseSpecial )
		{
			// BIN
			SotExchange::GetFormatDataFlavor( SOT_FORMATSTR_ID_EDITENGINE, aFlavor );
			if ( rxDataObj->isDataFlavorSupported( aFlavor ) )
			{
				try
				{
					uno::Any aData = rxDataObj->getTransferData( aFlavor );
					uno::Sequence< sal_Int8 > aSeq;
					aData >>= aSeq;
					{
						SvMemoryStream aBinStream( aSeq.getArray(), aSeq.getLength(), STREAM_READ );
						aNewSelection = Read( aBinStream, rBaseURL, EE_FORMAT_BIN, rPaM );
					}
					bDone = TRUE;
				}
				catch( const ::com::sun::star::uno::Exception& )
				{
				}
			}

			if ( !bDone )
			{
				// Bookmark
				/*
				String aURL = ...;
				String aTxt = ...;
				// Feld nur einfuegen, wenn Factory vorhanden.
				if ( ITEMDATA() && ITEMDATA()->GetClassManager().Get( SVX_URLFIELD ) )
				{
					SvxFieldItem aField( SvxURLField( aURL, aTxt, SVXURLFORMAT_URL ), EE_FEATURE_FIELD  );
					aNewSelection = InsertField( aPaM, aField );
					UpdateFields();
				}
				else
					aNewSelection = ImpInsertText( aPaM, aURL );
				}
				*/
			}
			if ( !bDone )
			{
				// RTF
				SotExchange::GetFormatDataFlavor( SOT_FORMAT_RTF, aFlavor );
				if ( rxDataObj->isDataFlavorSupported( aFlavor ) )
				{
					try
					{
						uno::Any aData = rxDataObj->getTransferData( aFlavor );
						uno::Sequence< sal_Int8 > aSeq;
						aData >>= aSeq;
						{
							SvMemoryStream aRTFStream( aSeq.getArray(), aSeq.getLength(), STREAM_READ );
							aNewSelection = Read( aRTFStream, rBaseURL, EE_FORMAT_RTF, rPaM );
						}
						bDone = TRUE;
					}
					catch( const ::com::sun::star::uno::Exception& )
					{
					}
				}
			}
			if ( !bDone )
			{
				// XML ?
                // Currently, there is nothing like "The" XML format, StarOffice doesn't offer plain XML in Clipboard...
			}
		}
		if ( !bDone )
		{
			SotExchange::GetFormatDataFlavor( SOT_FORMAT_STRING, aFlavor );
			if ( rxDataObj->isDataFlavorSupported( aFlavor ) )
			{
			    try
        	    {
    				uno::Any aData = rxDataObj->getTransferData( aFlavor );
    				::rtl::OUString aText;
    				aData >>= aText;
    				aNewSelection = ImpInsertText( rPaM, aText );
   					bDone = TRUE;
				}
				catch( ... )
				{
                    ; // #i9286# can happen, even if isDataFlavorSupported returns true...
				}
			}
		}
	}

	return aNewSelection;
}

Range ImpEditEngine::GetInvalidYOffsets( ParaPortion* pPortion )
{
	Range aRange( 0, 0 );

	if ( pPortion->IsVisible() )
	{
		const SvxULSpaceItem& rULSpace = (const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
		const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
		USHORT nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
							? GetYValue( rLSItem.GetInterLineSpace() ) : 0;

		// erst von vorne...
		USHORT nFirstInvalid = 0xFFFF;
		USHORT nLine;
		for ( nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
		{
			EditLine* pL = pPortion->GetLines().GetObject( nLine );
			if ( pL->IsInvalid() )
			{
				nFirstInvalid = nLine;
				break;
			}
			if ( nLine && !aStatus.IsOutliner() ) 	// nicht die erste Zeile
				aRange.Min() += nSBL;
			aRange.Min() += pL->GetHeight();
		}
		DBG_ASSERT( nFirstInvalid != 0xFFFF, "Keine ungueltige Zeile gefunden in GetInvalidYOffset(1)" );


		// Abgleichen und weiter...
		aRange.Max() = aRange.Min();
		aRange.Max() += pPortion->GetFirstLineOffset();
		if ( nFirstInvalid != 0 )	// Nur wenn nicht die erste Zeile ungueltig
			aRange.Min() = aRange.Max();

		USHORT nLastInvalid = pPortion->GetLines().Count()-1;
		for ( nLine = nFirstInvalid; nLine < pPortion->GetLines().Count(); nLine++ )
		{
			EditLine* pL = pPortion->GetLines().GetObject( nLine );
			if ( pL->IsValid() )
			{
				nLastInvalid = nLine;
				break;
			}

			if ( nLine && !aStatus.IsOutliner() )
				aRange.Max() += nSBL;
			aRange.Max() += pL->GetHeight();
		}

		// MT 07/00 SBL kann jetzt kleiner 100% sein => ggf. die Zeile davor neu ausgeben.
		if( ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP ) && rLSItem.GetPropLineSpace() &&
			( rLSItem.GetPropLineSpace() < 100 ) )
		{
			EditLine* pL = pPortion->GetLines().GetObject( nFirstInvalid );
			long n = pL->GetTxtHeight() * ( 100 - rLSItem.GetPropLineSpace() );
			n /= 100;
			aRange.Min() -= n;
			aRange.Max() += n;
		}

		if ( ( nLastInvalid == pPortion->GetLines().Count()-1 ) && ( !aStatus.IsOutliner() ) )
			aRange.Max() += GetYValue( rULSpace.GetLower() );
	}
	return aRange;
}

EditPaM ImpEditEngine::GetPaM( ParaPortion* pPortion, Point aDocPos, BOOL bSmart )
{
	DBG_ASSERT( pPortion->IsVisible(), "Wozu GetPaM() bei einem unsichtbaren Absatz?" );
	DBG_ASSERT( IsFormatted(), "GetPaM: Nicht formatiert" );

	USHORT nCurIndex = 0;
	EditPaM aPaM;
	aPaM.SetNode( pPortion->GetNode() );

	const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
	USHORT nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
						? GetYValue( rLSItem.GetInterLineSpace() ) : 0;

	long nY = pPortion->GetFirstLineOffset();

	DBG_ASSERT( pPortion->GetLines().Count(), "Leere ParaPortion in GetPaM!" );

	EditLine* pLine = 0;
	for ( USHORT nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
	{
		EditLine* pTmpLine = pPortion->GetLines().GetObject( nLine );
		nY += pTmpLine->GetHeight();
		if ( !aStatus.IsOutliner() )
			nY += nSBL;
		if ( nY > aDocPos.Y() ) 	// das war 'se
		{
			pLine = pTmpLine;
			break;					// richtige Y-Position intressiert nicht
		}

		nCurIndex = nCurIndex + pTmpLine->GetLen();
	}

	if ( !pLine ) // darf nur im Bereich von SA passieren!
	{
		#ifdef DBG_UTIL
		 const SvxULSpaceItem& rULSpace =(const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
		 DBG_ASSERT( nY+GetYValue( rULSpace.GetLower() ) >= aDocPos.Y() , "Index in keiner Zeile, GetPaM ?" );
		#endif
		aPaM.SetIndex( pPortion->GetNode()->Len() );
		return aPaM;
	}

	// Wenn Zeile gefunden, nur noch X-Position => Index
	nCurIndex = GetChar( pPortion, pLine, aDocPos.X(), bSmart );
	aPaM.SetIndex( nCurIndex );

	if ( nCurIndex && ( nCurIndex == pLine->GetEnd() ) &&
		 ( pLine != pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1) ) )
    {
        aPaM = CursorLeft( aPaM, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL );
    }

	return aPaM;
}

USHORT ImpEditEngine::GetChar( ParaPortion* pParaPortion, EditLine* pLine, long nXPos, BOOL bSmart )
{
	DBG_ASSERT( pLine, "Keine Zeile erhalten: GetChar" );

    USHORT nChar = 0xFFFF;
    USHORT nCurIndex = pLine->GetStart();


    // Search best matching portion with GetPortionXOffset()
    for ( USHORT i = pLine->GetStartPortion(); i <= pLine->GetEndPortion(); i++ )
	{
		TextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( i );
        long nXLeft = GetPortionXOffset( pParaPortion, pLine, i );
        long nXRight = nXLeft + pPortion->GetSize().Width();
        if ( ( nXLeft <= nXPos ) && ( nXRight >= nXPos ) )
        {
             nChar = nCurIndex;

            // Search within Portion...

            // Don't search within special portions...
			if ( pPortion->GetKind() != PORTIONKIND_TEXT )
			{
                // ...but check on which side
                if ( bSmart )
                {
				    long nLeftDiff = nXPos-nXLeft;
				    long nRightDiff = nXRight-nXPos;
				    if ( nRightDiff < nLeftDiff )
					    nChar++;
                }
			}
            else
            {
			    USHORT nMax = pPortion->GetLen();
			    USHORT nOffset = 0xFFFF;
			    USHORT nTmpCurIndex = nChar - pLine->GetStart();

                long nXInPortion = nXPos - nXLeft;
                if ( pPortion->IsRightToLeft() )
                    nXInPortion = nXRight - nXPos;

                // Search in Array...
			    for ( USHORT x = 0; x < nMax; x++ )
			    {
				    long nTmpPosMax = pLine->GetCharPosArray().GetObject( nTmpCurIndex+x );
				    if ( nTmpPosMax > nXInPortion )
				    {
					    // pruefen, ob dieser oder der davor...
                        long nTmpPosMin = x ? pLine->GetCharPosArray().GetObject( nTmpCurIndex+x-1 ) : 0;
					    long nDiffLeft = nXInPortion - nTmpPosMin;
					    long nDiffRight = nTmpPosMax - nXInPortion;
					    DBG_ASSERT( nDiffLeft >= 0, "DiffLeft negativ" );
					    DBG_ASSERT( nDiffRight >= 0, "DiffRight negativ" );
					    nOffset = ( bSmart && ( nDiffRight < nDiffLeft ) ) ? x+1 : x;
					    // I18N: If there are character position with the length of 0,
                        // they belong to the same character, we can not use this position as an index.
					    // Skip all 0-positions, cheaper than using XBreakIterator:
					    if ( nOffset < nMax )
					    {
						    const long nX = pLine->GetCharPosArray().GetObject(nOffset);
						    while ( ( (nOffset+1) < nMax ) && ( pLine->GetCharPosArray().GetObject(nOffset+1) == nX ) )
							    nOffset++;
					    }
					    break;
				    }
			    }

			    // Bei Verwendung des CharPosArray duerfte es keine Ungenauigkeiten geben!
			    // Vielleicht bei Kerning ?
			    // 0xFFF passiert z.B. bei Outline-Font, wenn ganz hinten.
			    if ( nOffset == 0xFFFF )
				    nOffset = nMax;

			    DBG_ASSERT( nOffset <= nMax, "nOffset > nMax" );

                nChar = nChar + nOffset;

                // Check if index is within a cell:
                if ( nChar && ( nChar < pParaPortion->GetNode()->Len() ) )
                {
                    EditPaM aPaM( pParaPortion->GetNode(), nChar+1 );
                    USHORT nScriptType = GetScriptType( aPaM );
                    if ( nScriptType == i18n::ScriptType::COMPLEX )
                    {
		                uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
		                sal_Int32 nCount = 1;
                        lang::Locale aLocale = GetLocale( aPaM );
                        USHORT nRight = (USHORT)_xBI->nextCharacters( *pParaPortion->GetNode(), nChar, aLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount );
						USHORT nLeft = (USHORT)_xBI->previousCharacters( *pParaPortion->GetNode(), nRight, aLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount );
                        if ( ( nLeft != nChar ) && ( nRight != nChar ) )
                        {
                            nChar = ( Abs( nRight - nChar ) < Abs( nLeft - nChar ) ) ? nRight : nLeft;
                        }
                    }
                }
            }
        }

        nCurIndex = nCurIndex + pPortion->GetLen();
    }

    if ( nChar == 0xFFFF )
    {
        nChar = ( nXPos <= pLine->GetStartPosX() ) ? pLine->GetStart() : pLine->GetEnd();
    }

    return nChar;
}

Range ImpEditEngine::GetLineXPosStartEnd( ParaPortion* pParaPortion, EditLine* pLine )
{
    Range aLineXPosStartEnd;

    USHORT nPara = GetEditDoc().GetPos( pParaPortion->GetNode() );
    if ( !IsRightToLeft( nPara ) )
    {
        aLineXPosStartEnd.Min() = pLine->GetStartPosX();
        aLineXPosStartEnd.Max() = pLine->GetStartPosX() + pLine->GetTextWidth();
    }
    else
    {
        aLineXPosStartEnd.Min() = GetPaperSize().Width() - ( pLine->GetStartPosX() + pLine->GetTextWidth() );
        aLineXPosStartEnd.Max() = GetPaperSize().Width() - pLine->GetStartPosX();
    }


    return aLineXPosStartEnd;
}

long ImpEditEngine::GetPortionXOffset( ParaPortion* pParaPortion, EditLine* pLine, USHORT nTextPortion )
{
	long nX = pLine->GetStartPosX();

    for ( USHORT i = pLine->GetStartPortion(); i < nTextPortion; i++ )
	{
		TextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( i );
		switch ( pPortion->GetKind() )
		{
			case PORTIONKIND_FIELD:
			case PORTIONKIND_TEXT:
			case PORTIONKIND_HYPHENATOR:
			case PORTIONKIND_TAB:
//	        case PORTIONKIND_EXTRASPACE:
			{
				nX += pPortion->GetSize().Width();
			}
			break;
		}
    }

    USHORT nPara = GetEditDoc().GetPos( pParaPortion->GetNode() );
    BOOL bR2LPara = IsRightToLeft( nPara );

    TextPortion* pDestPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
    if ( pDestPortion->GetKind() != PORTIONKIND_TAB )
    {
        if ( !bR2LPara && pDestPortion->GetRightToLeft() )
        {
            // Portions behind must be added, visual before this portion
            sal_uInt16 nTmpPortion = nTextPortion+1;
            while ( nTmpPortion <= pLine->GetEndPortion() )
            {
		        TextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
                if ( pNextTextPortion->GetRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
                    nX += pNextTextPortion->GetSize().Width();
                else
                    break;
                nTmpPortion++;
            }
            // Portions before must be removed, visual behind this portion
            nTmpPortion = nTextPortion;
            while ( nTmpPortion > pLine->GetStartPortion() )
            {
                --nTmpPortion;
		        TextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
                if ( pPrevTextPortion->GetRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
                    nX -= pPrevTextPortion->GetSize().Width();
                else
                    break;
            }
        }
        else if ( bR2LPara && !pDestPortion->IsRightToLeft() )
        {
            // Portions behind must be ermoved, visual behind this portion
            sal_uInt16 nTmpPortion = nTextPortion+1;
            while ( nTmpPortion <= pLine->GetEndPortion() )
            {
		        TextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
                if ( !pNextTextPortion->IsRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
                    nX += pNextTextPortion->GetSize().Width();
                else
                    break;
                nTmpPortion++;
            }
            // Portions before must be added, visual before this portion
            nTmpPortion = nTextPortion;
            while ( nTmpPortion > pLine->GetStartPortion() )
            {
                --nTmpPortion;
		        TextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
                if ( !pPrevTextPortion->IsRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
                    nX -= pPrevTextPortion->GetSize().Width();
                else
                    break;
            }
        }
    }
    if ( bR2LPara )
    {
        // Switch X postions...
        DBG_ASSERT( GetTextRanger() || GetPaperSize().Width(), "GetPortionXOffset - paper size?!" );
        DBG_ASSERT( GetTextRanger() || (nX <= GetPaperSize().Width()), "GetPortionXOffset - position out of paper size!" );
        nX = GetPaperSize().Width() - nX;
        nX -= pDestPortion->GetSize().Width();
    }

    return nX;
}

long ImpEditEngine::GetXPos( ParaPortion* pParaPortion, EditLine* pLine, USHORT nIndex, BOOL bPreferPortionStart )
{
	DBG_ASSERT( pLine, "Keine Zeile erhalten: GetXPos" );
	DBG_ASSERT( ( nIndex >= pLine->GetStart() ) && ( nIndex <= pLine->GetEnd() ) , "GetXPos muss richtig gerufen werden!" );

    BOOL bDoPreferPortionStart = bPreferPortionStart;
    // Assure that the portion belongs to this line:
    if ( nIndex == pLine->GetStart() )
        bDoPreferPortionStart = TRUE;
    else if ( nIndex == pLine->GetEnd() )
        bDoPreferPortionStart = FALSE;

    USHORT nTextPortionStart = 0;
    USHORT nTextPortion = pParaPortion->GetTextPortions().FindPortion( nIndex, nTextPortionStart, bDoPreferPortionStart );

    DBG_ASSERT( ( nTextPortion >= pLine->GetStartPortion() ) && ( nTextPortion <= pLine->GetEndPortion() ), "GetXPos: Portion not in current line! " );

    TextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );

    long nX = GetPortionXOffset( pParaPortion, pLine, nTextPortion );

    // calc text width, portion size may include CJK/CTL spacing...
    // But the array migh not be init yet, if using text ranger this method is called within CreateLines()...
    long nPortionTextWidth = pPortion->GetSize().Width();
    if ( ( pPortion->GetKind() == PORTIONKIND_TEXT ) && pPortion->GetLen() && !GetTextRanger() )
        nPortionTextWidth = pLine->GetCharPosArray().GetObject( nTextPortionStart + pPortion->GetLen() - 1 - pLine->GetStart() );

    if ( nTextPortionStart != nIndex )
    {
        // Search within portion...
        if ( nIndex == ( nTextPortionStart + pPortion->GetLen() ) )
        {
            // End of Portion
            if ( pPortion->GetKind() == PORTIONKIND_TAB )
            {
                if ( (nTextPortion+1) < pParaPortion->GetTextPortions().Count() )
                {
                    TextPortion* pNextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion+1 );
                    if ( pNextPortion->GetKind() != PORTIONKIND_TAB )
                    {
                        // DBG_ASSERT( !bPreferPortionStart, "GetXPos - How can we this tab portion here???" );
						// #109879# We loop if nIndex == pLine->GetEnd, because bPreferPortionStart will be reset
						if ( !bPreferPortionStart )
							nX = GetXPos( pParaPortion, pLine, nIndex, TRUE );
						else if ( !IsRightToLeft( GetEditDoc().GetPos( pParaPortion->GetNode() ) ) )
							nX += nPortionTextWidth;
                    }
                }
                else if ( !IsRightToLeft( GetEditDoc().GetPos( pParaPortion->GetNode() ) ) )
                {
				    nX += nPortionTextWidth;
                }
            }
            else if ( !pPortion->IsRightToLeft() )
            {
				nX += nPortionTextWidth;
            }
        }
        else if ( pPortion->GetKind() == PORTIONKIND_TEXT )
        {
			DBG_ASSERT( nIndex != pLine->GetStart(), "Strange behavior in new GetXPos()" );
			DBG_ASSERT( pLine && pLine->GetCharPosArray().Count(), "svx::ImpEditEngine::GetXPos(), portion in an empty line?" );

			if( pLine->GetCharPosArray().Count() )
			{
				USHORT nPos = nIndex - 1 - pLine->GetStart();
				if( nPos >= pLine->GetCharPosArray().Count() )
				{
					nPos = pLine->GetCharPosArray().Count()-1;
					DBG_ERROR("svx::ImpEditEngine::GetXPos(), index out of range!");
				}

				long nPosInPortion = pLine->GetCharPosArray().GetObject( nPos );

				if ( !pPortion->IsRightToLeft() )
				{
					nX += nPosInPortion;
				}
				else
				{
					nX += nPortionTextWidth - nPosInPortion;
				}

				if ( pPortion->GetExtraInfos() && pPortion->GetExtraInfos()->bCompressed )
				{
					nX += pPortion->GetExtraInfos()->nPortionOffsetX;
					if ( pPortion->GetExtraInfos()->nAsianCompressionTypes & CHAR_PUNCTUATIONRIGHT )
					{
						BYTE nType = GetCharTypeForCompression( pParaPortion->GetNode()->GetChar( nIndex ) );
						if ( nType == CHAR_PUNCTUATIONRIGHT )
						{
							USHORT n = nIndex - nTextPortionStart;
							const sal_Int32* pDXArray = pLine->GetCharPosArray().GetData()+( nTextPortionStart-pLine->GetStart() );
							sal_Int32 nCharWidth = ( ( (n+1) < pPortion->GetLen() ) ? pDXArray[n] : pPortion->GetSize().Width() )
															- ( n ? pDXArray[n-1] : 0 );
							if ( (n+1) < pPortion->GetLen() )
							{
								// smaller, when char behind is CHAR_PUNCTUATIONRIGHT also
								nType = GetCharTypeForCompression( pParaPortion->GetNode()->GetChar( nIndex+1 ) );
								if ( nType == CHAR_PUNCTUATIONRIGHT )
								{
									sal_Int32 nNextCharWidth = ( ( (n+2) < pPortion->GetLen() ) ? pDXArray[n+1] : pPortion->GetSize().Width() )
																	- pDXArray[n];
									sal_Int32 nCompressed = nNextCharWidth/2;
									nCompressed *= pPortion->GetExtraInfos()->nMaxCompression100thPercent;
									nCompressed /= 10000;
									nCharWidth += nCompressed;
								}
							}
							else
							{
								nCharWidth *= 2;    // last char pos to portion end is only compressed size
							}
							nX += nCharWidth/2; // 50% compression
						}
					}
				}
			}
		}
    }
    else // if ( nIndex == pLine->GetStart() )
    {
        if ( pPortion->IsRightToLeft() )
        {
		    nX += nPortionTextWidth;
        }
    }

	return nX;
}

void ImpEditEngine::CalcHeight( ParaPortion* pPortion )
{
	pPortion->nHeight = 0;
	pPortion->nFirstLineOffset = 0;

	if ( pPortion->IsVisible() )
	{
		DBG_ASSERT( pPortion->GetLines().Count(), "Absatz ohne Zeilen in ParaPortion::CalcHeight" );
		for ( USHORT nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
			pPortion->nHeight += pPortion->GetLines().GetObject( nLine )->GetHeight();

		if ( !aStatus.IsOutliner() )
		{
			const SvxULSpaceItem& rULItem = (const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
			const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
			USHORT nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX ) ? GetYValue( rLSItem.GetInterLineSpace() ) : 0;

			if ( nSBL )
			{
				if ( pPortion->GetLines().Count() > 1 )
					pPortion->nHeight += ( pPortion->GetLines().Count() - 1 ) * nSBL;
				if ( aStatus.ULSpaceSummation() )
					pPortion->nHeight += nSBL;
			}

			USHORT nPortion = GetParaPortions().GetPos( pPortion );
			if ( nPortion || aStatus.ULSpaceFirstParagraph() )
			{
				USHORT nUpper = GetYValue( rULItem.GetUpper() );
				pPortion->nHeight += nUpper;
				pPortion->nFirstLineOffset = nUpper;
			}

			if ( ( nPortion != (GetParaPortions().Count()-1) ) )
			{
				pPortion->nHeight += GetYValue( rULItem.GetLower() );	// nicht in letzter
			}


			if ( nPortion && !aStatus.ULSpaceSummation() )
			{
				ParaPortion* pPrev = GetParaPortions().SaveGetObject( nPortion-1 );
				const SvxULSpaceItem& rPrevULItem = (const SvxULSpaceItem&)pPrev->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
				const SvxLineSpacingItem& rPrevLSItem = (const SvxLineSpacingItem&)pPrev->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );

				// Verhalten WinWord6/Writer3:
				// Bei einem proportionalen Zeilenabstand wird auch der Absatzabstand
				// manipuliert.
				// Nur Writer3: Nicht aufaddieren, sondern Mindestabstand.

				// Pruefen, ob Abstand durch LineSpacing > Upper:
				USHORT nExtraSpace = GetYValue( lcl_CalcExtraSpace( pPortion, rLSItem ) );
				if ( nExtraSpace > pPortion->nFirstLineOffset )
				{
					// Absatz wird 'groesser':
					pPortion->nHeight += ( nExtraSpace - pPortion->nFirstLineOffset );
					pPortion->nFirstLineOffset = nExtraSpace;
				}

				// nFirstLineOffset jetzt f(pNode) => jetzt f(pNode, pPrev) ermitteln:
				USHORT nPrevLower = GetYValue( rPrevULItem.GetLower() );

				// Dieser PrevLower steckt noch in der Hoehe der PrevPortion...
				if ( nPrevLower > pPortion->nFirstLineOffset )
				{
					// Absatz wird 'kleiner':
					pPortion->nHeight -= pPortion->nFirstLineOffset;
					pPortion->nFirstLineOffset = 0;
				}
				else if ( nPrevLower )
				{
					// Absatz wird 'etwas kleiner':
					pPortion->nHeight -= nPrevLower;
					pPortion->nFirstLineOffset =
                        pPortion->nFirstLineOffset - nPrevLower;
				}

				// Finde ich zwar nicht so gut, aber Writer3-Feature:
				// Pruefen, ob Abstand durch LineSpacing > Lower:
				// Dieser Wert steckt nicht in der Hoehe der PrevPortion.
				if ( !pPrev->IsInvalid() )
				{
					nExtraSpace = GetYValue( lcl_CalcExtraSpace( pPrev, rPrevLSItem ) );
					if ( nExtraSpace > nPrevLower )
					{
						USHORT nMoreLower = nExtraSpace - nPrevLower;
						// Absatz wird 'groesser', 'waechst' nach unten:
						if ( nMoreLower > pPortion->nFirstLineOffset )
						{
							pPortion->nHeight += ( nMoreLower - pPortion->nFirstLineOffset );
							pPortion->nFirstLineOffset = nMoreLower;
						}
					}
				}
			}
		}
	}
}

Rectangle ImpEditEngine::GetEditCursor( ParaPortion* pPortion, USHORT nIndex, USHORT nFlags )
{
	DBG_ASSERT( pPortion->IsVisible(), "Wozu GetEditCursor() bei einem unsichtbaren Absatz?" );
	DBG_ASSERT( IsFormatted() || GetTextRanger(), "GetEditCursor: Nicht formatiert" );

	/*
	 GETCRSR_ENDOFLINE: Wenn hinter dem letzten Zeichen einer umgebrochenen Zeile,
	 am Ende der Zeile bleiben, nicht am Anfang der naechsten.
	 Zweck: 	- END => wirklich hinter das letzte Zeichen
				- Selektion....
	*/

	long nY = pPortion->GetFirstLineOffset();

	const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
	USHORT nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
						? GetYValue( rLSItem.GetInterLineSpace() ) : 0;

	USHORT nCurIndex = 0;
	DBG_ASSERT( pPortion->GetLines().Count(), "Leere ParaPortion in GetEditCursor!" );
	EditLine* pLine = 0;
	BOOL bEOL = ( nFlags & GETCRSR_ENDOFLINE ) ? TRUE : FALSE;
	for ( USHORT nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
	{
		EditLine* pTmpLine = pPortion->GetLines().GetObject( nLine );
		if ( ( pTmpLine->GetStart() == nIndex ) || ( pTmpLine->IsIn( nIndex, bEOL ) ) )
		{
			pLine = pTmpLine;
			break;
		}

		nCurIndex = nCurIndex + pTmpLine->GetLen();
		nY += pTmpLine->GetHeight();
		if ( !aStatus.IsOutliner() )
			nY += nSBL;
	}
	if ( !pLine )
	{
		// Cursor am Ende des Absatzes.
		DBG_ASSERT( nIndex == nCurIndex, "Index voll daneben in GetEditCursor!" );

		pLine = pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1 );
		nY -= pLine->GetHeight();
		if ( !aStatus.IsOutliner() )
			nY -= nSBL;
		nCurIndex = nCurIndex -  pLine->GetLen();
	}

	Rectangle aEditCursor;

	aEditCursor.Top() = nY;
	nY += pLine->GetHeight();
	aEditCursor.Bottom() = nY-1;

	// innerhalb der Zeile suchen...
    long nX;

    if ( ( nIndex == pLine->GetStart() ) && ( nFlags & GETCRSR_STARTOFLINE ) )
    {
        Range aXRange = GetLineXPosStartEnd( pPortion, pLine );
        nX = !IsRightToLeft( GetEditDoc().GetPos( pPortion->GetNode() ) ) ? aXRange.Min() : aXRange.Max();
    }
    else if ( ( nIndex == pLine->GetEnd() ) && ( nFlags & GETCRSR_ENDOFLINE ) )
    {
        Range aXRange = GetLineXPosStartEnd( pPortion, pLine );
        nX = !IsRightToLeft( GetEditDoc().GetPos( pPortion->GetNode() ) ) ? aXRange.Max() : aXRange.Min();
    }
    else
    {
        nX = GetXPos( pPortion, pLine, nIndex, ( nFlags & GETCRSR_PREFERPORTIONSTART ) ? TRUE : FALSE );
    }

    aEditCursor.Left() = aEditCursor.Right() = nX;

	if ( nFlags & GETCRSR_TXTONLY )
		aEditCursor.Top() = aEditCursor.Bottom() - pLine->GetTxtHeight() + 1;
	else
		aEditCursor.Top() = aEditCursor.Bottom() - Min( pLine->GetTxtHeight(), pLine->GetHeight() ) + 1;

	return aEditCursor;
}

void ImpEditEngine::SetValidPaperSize( const Size& rNewSz )
{
	aPaperSize = rNewSz;

	long nMinWidth = aStatus.AutoPageWidth() ? aMinAutoPaperSize.Width() : 0;
	long nMaxWidth = aStatus.AutoPageWidth() ? aMaxAutoPaperSize.Width() : 0x7FFFFFFF;
	long nMinHeight = aStatus.AutoPageHeight() ? aMinAutoPaperSize.Height() : 0;
	long nMaxHeight = aStatus.AutoPageHeight() ? aMaxAutoPaperSize.Height() : 0x7FFFFFFF;

	// Minimale/Maximale Breite:
	if ( aPaperSize.Width() < nMinWidth )
		aPaperSize.Width() = nMinWidth;
	else if ( aPaperSize.Width() > nMaxWidth )
		aPaperSize.Width() = nMaxWidth;

	// Minimale/Maximale Hoehe:
	if ( aPaperSize.Height() < nMinHeight )
		aPaperSize.Height() = nMinHeight;
	else if ( aPaperSize.Height() > nMaxHeight )
		aPaperSize.Height() = nMaxHeight;
}

void ImpEditEngine::IndentBlock( EditView* pEditView, BOOL bRight )
{
	ESelection aESel( CreateESel( pEditView->pImpEditView->GetEditSelection() ) );
	aESel.Adjust();

	// Nur wenn mehrere selektierte Absaetze...
	if ( aESel.nEndPara > aESel.nStartPara )
	{
		ESelection aNewSel = aESel;
		aNewSel.nStartPos = 0;
		aNewSel.nEndPos = 0xFFFF;

		if ( aESel.nEndPos == 0 )
		{
			aESel.nEndPara--;		// dann diesen Absatz nicht...
			aNewSel.nEndPos = 0;
		}

		pEditView->pImpEditView->DrawSelection();
		pEditView->pImpEditView->SetEditSelection(
						pEditView->pImpEditView->GetEditSelection().Max() );
		UndoActionStart( bRight ? EDITUNDO_INDENTBLOCK : EDITUNDO_UNINDENTBLOCK );

		for ( USHORT nPara = aESel.nStartPara; nPara <= aESel.nEndPara; nPara++ )
		{
			ContentNode* pNode = GetEditDoc().GetObject( nPara );
			if ( bRight )
			{
				// Tabs hinzufuegen
				EditPaM aPaM( pNode, 0 );
				InsertTab( aPaM );
			}
			else
			{
				// Tabs entfernen
				EditCharAttrib* pFeature = pNode->GetCharAttribs().FindFeature( 0 );
				if ( pFeature && ( pFeature->GetStart() == 0 ) &&
				   ( pFeature->GetItem()->Which() == EE_FEATURE_TAB ) )
				{
					EditPaM aStartPaM( pNode, 0 );
					EditPaM aEndPaM( pNode, 1 );
					ImpDeleteSelection( EditSelection( aStartPaM, aEndPaM ) );
				}
			}
		}

		UndoActionEnd( bRight ? EDITUNDO_INDENTBLOCK : EDITUNDO_UNINDENTBLOCK );
		UpdateSelections();
		FormatAndUpdate( pEditView );

		ContentNode* pLastNode = GetEditDoc().GetObject( aNewSel.nEndPara );
		if ( pLastNode->Len() < aNewSel.nEndPos )
			aNewSel.nEndPos = pLastNode->Len();
		pEditView->pImpEditView->SetEditSelection( CreateSel( aNewSel ) );
		pEditView->pImpEditView->DrawSelection();
		pEditView->pImpEditView->ShowCursor( FALSE, TRUE );
	}
}

vos::ORef<SvxForbiddenCharactersTable> ImpEditEngine::GetForbiddenCharsTable( BOOL bGetInternal ) const
{
	vos::ORef<SvxForbiddenCharactersTable> xF = xForbiddenCharsTable;
	if ( !xF.isValid() && bGetInternal )
		xF = EE_DLL()->GetGlobalData()->GetForbiddenCharsTable();
	return xF;
}

void ImpEditEngine::SetForbiddenCharsTable( vos::ORef<SvxForbiddenCharactersTable> xForbiddenChars )
{
	EE_DLL()->GetGlobalData()->SetForbiddenCharsTable( xForbiddenChars );
}

svtools::ColorConfig& ImpEditEngine::GetColorConfig()
{
    if ( !pColorConfig )
        pColorConfig = new svtools::ColorConfig;

    return *pColorConfig;
}

BOOL ImpEditEngine::IsVisualCursorTravelingEnabled()
{
    BOOL bVisualCursorTravaling = FALSE;

    if( !pCTLOptions )
        pCTLOptions = new SvtCTLOptions;

    if ( pCTLOptions->IsCTLFontEnabled() && ( pCTLOptions->GetCTLCursorMovement() == SvtCTLOptions::MOVEMENT_VISUAL ) )
    {
        bVisualCursorTravaling = TRUE;
    }

    return bVisualCursorTravaling;

}

BOOL ImpEditEngine::DoVisualCursorTraveling( const ContentNode* )
{
    // Don't check if it's necessary, because we also need it when leaving the paragraph
    return IsVisualCursorTravelingEnabled();
/*
    BOOL bDoVisualCursorTraveling = FALSE;

    if ( IsVisualCursorTravelingEnabled() && pNode->Len() )
    {
        // Only necessary when RTL text in LTR para or LTR text in RTL para
        bDoVisualCursorTraveling = HasDifferentRTLLevels( pNode );
    }

    return bDoVisualCursorTraveling;
*/
}


void ImpEditEngine::CallNotify( EENotify& rNotify )
{
    if ( !nBlockNotifications )
    {
        GetNotifyHdl().Call( &rNotify );
    }
    else
    {
        EENotify* pNewNotify = new EENotify( rNotify );
        aNotifyCache.Insert( pNewNotify, aNotifyCache.Count() );
    }
}

void ImpEditEngine::EnterBlockNotifications()
{
    if( !nBlockNotifications )
    {
        // #109864# Send out START notification immediately, to allow
        // external, non-queued events to be captured as well from
        // client side
        EENotify aNotify( EE_NOTIFY_BLOCKNOTIFICATION_START );
        aNotify.pEditEngine = GetEditEnginePtr();
        GetNotifyHdl().Call( &aNotify );
    }

    nBlockNotifications++;
}

void ImpEditEngine::LeaveBlockNotifications()
{
    DBG_ASSERT( nBlockNotifications, "LeaveBlockNotifications - Why?" );

    nBlockNotifications--;
    if ( !nBlockNotifications )
    {
        // Call blocked notify events...
        while ( aNotifyCache.Count() )
        {
            EENotify* pNotify = aNotifyCache[0];
            // Remove from list before calling, maybe we enter LeaveBlockNotifications while calling the handler...
            aNotifyCache.Remove( 0 );
            GetNotifyHdl().Call( pNotify );
            delete pNotify;
        }

        EENotify aNotify( EE_NOTIFY_BLOCKNOTIFICATION_END );
        aNotify.pEditEngine = GetEditEnginePtr();
        GetNotifyHdl().Call( &aNotify );
    }
}

IMPL_LINK( ImpEditEngine, DocModified, void*, EMPTYARG )
{
    aModifyHdl.Call( NULL /*GetEditEnginePtr()*/ ); // NULL, because also used for Outliner
    return 0;
}
