/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: svx_impedit2.cxx,v $
 *
 *  $Revision: 1.10 $
 *
 *  last change: $Author: hr $ $Date: 2007/01/02 17:20:32 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/


#pragma hdrstop

#ifndef _EEITEM_HXX
#include "eeitem.hxx"
#endif
#ifndef _EEITEMID_HXX
#include "eeitemid.hxx"
#endif

#include <lspcitem.hxx>



#ifndef _SVX_ITEMDATA_HXX
#include "itemdata.hxx"
#endif

#ifndef _DATE_HXX //autogen
#include <tools/date.hxx>
#endif

#ifndef _TIME_HXX //autogen
#include <tools/time.hxx>
#endif




#ifndef _SV_WINDOW_HXX
#include <vcl/window.hxx>
#endif

#include <impedit.hxx>
#include <editeng.hxx>
#include <editview.hxx>
#include <eerdll2.hxx>
#include <eerdll.hxx>
#include <txtrange.hxx>
#include <svtools/colorcfg.hxx>
#include <svtools/ctloptions.hxx>

#include <lrspitem.hxx>
#include <ulspitem.hxx>
#include <adjitem.hxx>
#include <scripttypeitem.hxx>
#include <frmdiritem.hxx>




#ifndef _VCL_CMDEVT_H
#include <vcl/cmdevt.h>
#endif

#ifndef SVX_LIGHT
#endif



#ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HPP_
#include <com/sun/star/i18n/ScriptType.hpp>
#endif

#ifndef _COM_SUN_STAR_TEXT_CHARACTERCOMPRESSIONTYPE_HPP_
#include <com/sun/star/text/CharacterCompressionType.hpp>
#endif



#include <unicode/ubidi.h>
namespace binfilter {

#if defined(MACOSX) && ( __GNUC__ < 3 )
// moved from editundo.hxx
const EPaM& EditUndoRemoveChars::GetEPaM() { return aEPaM; }
String& EditUndoRemoveChars::GetStr() { return aText; }
#endif

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

/*N*/ USHORT lcl_CalcExtraSpace( ParaPortion* pPortion, const SvxLineSpacingItem& rLSItem )
/*N*/ {
/*N*/ 	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 */
/*N*/ 	if ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
/*N*/ 	{
/*?*/ 		nExtra = rLSItem.GetInterLineSpace();
/*N*/ 	}
/*N*/ 
/*N*/ 	return nExtra;
/*N*/ }

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

/*N*/ ImpEditEngine::ImpEditEngine( EditEngine* pEE, SfxItemPool* pItemPool ) :
/*N*/ 	aEditDoc( pItemPool ),
/*N*/ 	aPaperSize( 0x7FFFFFFF, 0x7FFFFFFF ),
/*N*/ 	aMaxAutoPaperSize( 0x7FFFFFFF, 0x7FFFFFFF ),
/*N*/ 	aMinAutoPaperSize( 0x0, 0x0 ),
/*N*/ 	aGroupChars( RTL_CONSTASCII_USTRINGPARAM( "{}()[]" ) ),
/*N*/ 	aWordDelimiters( RTL_CONSTASCII_USTRINGPARAM( "  .,;:-'`'?!_=\"{}()[]\0xFF" ) )
/*N*/ {
/*N*/ 	pEditEngine 		= pEE;
/*N*/ 	pRefDev 			= NULL;
/*N*/ 	pVirtDev 			= NULL;
/*N*/ 	pEmptyItemSet 		= NULL;
/*N*/ 	pActiveView 		= NULL;
/*N*/ 	pTextObjectPool 	= NULL;
/*N*/ 	mpIMEInfos			= NULL;
/*N*/ 	pStylePool 			= NULL;
/*N*/ 	pUndoManager 		= NULL;
/*N*/ 	pUndoMarkSelection	= NULL;
/*N*/ 	pTextRanger			= NULL;
/*N*/     pColorConfig        = NULL;
/*N*/     pCTLOptions         = NULL;
/*N*/ 
/*N*/ 	nCurTextHeight 		= 0;
/*N*/     nBlockNotifications = 0;
/*N*/ 	nBigTextObjectStart	= 20;
/*N*/ 
/*N*/ 	nStretchX			= 100;
/*N*/ 	nStretchY			= 100;
/*N*/ 
/*N*/ 	bInSelection 		= FALSE;
/*N*/ 	bOwnerOfRefDev 		= FALSE;
/*N*/ 	bDowning 			= FALSE;
/*N*/ 	bIsInUndo 			= FALSE;
/*N*/ 	bIsFormatting 		= FALSE;
/*N*/ 	bFormatted			= FALSE;
/*N*/ 	bUpdate 			= TRUE;
/*N*/     bUseAutoColor       = TRUE;
/*N*/     bForceAutoColor     = FALSE;
/*N*/ #ifndef SVX_LIGHT
/*N*/ 	bUndoEnabled 		= TRUE;
/*N*/ #else
/*N*/ 	bUndoEnabled 		= FALSE;
/*N*/ #endif
/*N*/ 	bCallParaInsertedOrDeleted = FALSE;
/*N*/ 
/*N*/ 	eDefLanguage		= LANGUAGE_DONTKNOW;
/*N*/ 	maBackgroundColor	= COL_AUTO;
/*N*/ 
/*N*/     nAsianCompressionMode = text::CharacterCompressionType::NONE;
/*N*/ 	bKernAsianPunctuation = FALSE;
/*N*/ 
/*N*/     eDefaultHorizontalTextDirection = EE_HTEXTDIR_DEFAULT;
/*N*/ 
/*N*/ 
/*N*/ 	aStatus.GetControlWord() =	EE_CNTRL_USECHARATTRIBS | EE_CNTRL_DOIDLEFORMAT |
/*N*/ 								EE_CNTRL_PASTESPECIAL | EE_CNTRL_UNDOATTRIBS |
/*N*/ 								EE_CNTRL_ALLOWBIGOBJS | EE_CNTRL_RTFSTYLESHEETS |
/*N*/ 								EE_CNTRL_FORMAT100;
/*N*/ 
/*N*/ 	aSelEngine.SetFunctionSet( &aSelFuncSet );
/*N*/ 
/*N*/ 	aStatusTimer.SetTimeout( 200 );
/*N*/ 	aStatusTimer.SetTimeoutHdl( LINK( this, ImpEditEngine, StatusTimerHdl ) );
/*N*/ 
/*N*/ 	aIdleFormatter.SetTimeout( 5 );
/*N*/ 	aIdleFormatter.SetTimeoutHdl( LINK( this, ImpEditEngine, IdleFormatHdl ) );
/*N*/ 
/*N*/ 	aOnlineSpellTimer.SetTimeout( 100 );
/*N*/ 	aOnlineSpellTimer.SetTimeoutHdl( LINK( this, ImpEditEngine, OnlineSpellHdl ) );
/*N*/ 
/*N*/ 	pRefDev 			= EE_DLL()->GetGlobalData()->GetStdRefDevice();
/*N*/ 
/*N*/ 	// Ab hier wird schon auf Daten zugegriffen!
/*N*/ 	SetRefDevice( pRefDev );
/*N*/ 	InitDoc( FALSE );
/*N*/ 
/*N*/ 	bCallParaInsertedOrDeleted = TRUE;
/*N*/ 
/*N*/     aEditDoc.SetModifyHdl( LINK( this, ImpEditEngine, DocModified ) );
/*N*/ }

/*N*/ ImpEditEngine::~ImpEditEngine()
/*N*/ {
/*N*/ 	aStatusTimer.Stop();
/*N*/ 	aOnlineSpellTimer.Stop();
/*N*/ 	aIdleFormatter.Stop();
/*N*/ 
/*N*/ 	// das Zerstoeren von Vorlagen kann sonst unnoetiges Formatieren ausloesen,
/*N*/ 	// wenn eine Parent-Vorlage zerstoert wird.
/*N*/ 	// Und das nach dem Zerstoeren der Daten!
/*N*/ 	bDowning = TRUE;
/*N*/ 	SetUpdateMode( FALSE );
/*N*/ 
/*N*/ 	delete pVirtDev;
/*N*/ 	delete pEmptyItemSet;
/*N*/ 	delete pUndoManager;
/*N*/ 	delete pTextRanger;
/*N*/ 	delete mpIMEInfos;
/*N*/     delete pColorConfig;
/*N*/     delete pCTLOptions;
/*N*/ 	if ( bOwnerOfRefDev )
/*N*/ 		delete pRefDev;
/*N*/ }

/*N*/ void ImpEditEngine::SetRefDevice( OutputDevice* pRef )
/*N*/ {
/*N*/ 	if ( bOwnerOfRefDev )
/*N*/ 		delete pRefDev;
/*N*/ 
/*N*/ 	pRefDev = pRef;
/*N*/ 	bOwnerOfRefDev = FALSE;
/*N*/ 
/*N*/ 	if ( !pRef )
/*N*/ 		pRefDev = EE_DLL()->GetGlobalData()->GetStdRefDevice();
/*N*/ 
/*N*/ 	nOnePixelInRef = (USHORT)pRefDev->PixelToLogic( Size( 1, 0 ) ).Width();
/*N*/ 
/*N*/ 	if ( IsFormatted() )
/*N*/ 	{
/*N*/ 		FormatFullDoc();
/*N*/ 		UpdateViews( (EditView*) 0);
/*N*/ 	}
/*N*/ }

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

/*N*/ void ImpEditEngine::InitDoc( BOOL bKeepParaAttribs )
/*N*/ {
/*N*/ 	USHORT nParas = aEditDoc.Count();
/*N*/ 	for ( USHORT n = bKeepParaAttribs ? 1 : 0; n < nParas; n++ )
/*N*/ 	{
/*N*/ 		if ( aEditDoc[n]->GetStyleSheet() )
/*N*/ 			EndListening( *aEditDoc[n]->GetStyleSheet(), FALSE );
/*N*/ 	}
/*N*/ 
/*N*/ 	if ( bKeepParaAttribs )
/*N*/ 		aEditDoc.RemoveText();
/*N*/ 	else
/*N*/ 		aEditDoc.Clear();
/*N*/ 
/*N*/ 	GetParaPortions().Reset();
/*N*/ 
/*N*/ 	ParaPortion* pIniPortion = new ParaPortion( aEditDoc[0] );
/*N*/ 	GetParaPortions().Insert( pIniPortion, 0 );
/*N*/ 
/*N*/ 	bFormatted = FALSE;
/*N*/ 
/*N*/ 	if ( IsCallParaInsertedOrDeleted() )
/*N*/ 	{
/*N*/ 		GetEditEnginePtr()->ParagraphDeleted( EE_PARA_ALL );
/*N*/ 		GetEditEnginePtr()->ParagraphInserted( 0 );
/*N*/ 	}
/*N*/ 
/*N*/ #ifndef SVX_LIGHT
/*N*/ 	if ( GetStatus().DoOnlineSpelling() )
/*?*/ 		aEditDoc.GetObject( 0 )->CreateWrongList();
/*N*/ #endif // !SVX_LIGHT
/*N*/ }


/*N*/ XubString ImpEditEngine::GetSelected( const EditSelection& rSel, const LineEnd eEnd  ) const
/*N*/ {
/*N*/ 	XubString aText;
/*N*/ 	if ( !rSel.HasRange() )
/*N*/ 		return aText;
/*N*/ 
/*N*/ 	String aSep = EditDoc::GetSepStr( eEnd );
/*N*/ 
/*N*/ 	EditSelection aSel( rSel );
/*N*/ 	aSel.Adjust( aEditDoc );
/*N*/ 
/*N*/ 	ContentNode* pStartNode = aSel.Min().GetNode();
/*N*/ 	ContentNode* pEndNode = aSel.Max().GetNode();
/*N*/ 	USHORT nStartNode = aEditDoc.GetPos( pStartNode );
/*N*/ 	USHORT nEndNode = aEditDoc.GetPos( pEndNode );
/*N*/ 
/*N*/ 	DBG_ASSERT( nStartNode <= nEndNode, "Selektion nicht sortiert ?" );
/*N*/ 
/*N*/ 	// ueber die Absaetze iterieren...
/*N*/ 	for ( USHORT nNode = nStartNode; nNode <= nEndNode; nNode++	)
/*N*/ 	{
/*N*/ 		DBG_ASSERT( aEditDoc.SaveGetObject( nNode ), "Node nicht gefunden: GetSelected" );
/*N*/ 		ContentNode* pNode = aEditDoc.GetObject( nNode );
/*N*/ 
/*N*/ 		xub_StrLen nStartPos = 0;
/*N*/ 		xub_StrLen nEndPos = pNode->Len();
/*N*/ 		if ( nNode == nStartNode )
/*N*/ 			nStartPos = aSel.Min().GetIndex();
/*N*/ 		if ( nNode == nEndNode ) // kann auch == nStart sein!
/*N*/ 			nEndPos = aSel.Max().GetIndex();
/*N*/ 
/*N*/ 		aText += aEditDoc.GetParaAsString( pNode, nStartPos, nEndPos );
/*N*/ 		if ( nNode < nEndNode )
/*N*/ 			aText += aSep;
/*N*/ 	}
/*N*/ 	return aText;
/*N*/ }






/*N*/ EditPaM ImpEditEngine::Clear()
/*N*/ {
/*N*/ 	InitDoc( FALSE );
/*N*/ 
/*N*/ 	EditPaM aPaM = aEditDoc.GetStartPaM();
/*N*/ 	EditSelection aSel( aPaM );
/*N*/ 
/*N*/ 	nCurTextHeight = 0;
/*N*/ 
/*N*/ 	ResetUndoManager();
/*N*/ 
/*N*/ 	for ( USHORT nView = aEditViews.Count(); nView; )
/*N*/ 	{
/*?*/ 		EditView* pView = aEditViews[--nView];
/*?*/ 		DBG_CHKOBJ( pView, EditView, 0 );
/*?*/ 		pView->pImpEditView->SetEditSelection( aSel );
/*N*/ 	}
/*N*/ 
/*N*/ 	return aPaM;
/*N*/ }

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


/*N*/ void ImpEditEngine::SetText( const XubString& rText )
/*N*/ {
/*N*/ 	// RemoveText loescht die Undo-Liste!
/*N*/ 	EditPaM aStartPaM = RemoveText();
/*N*/ 	BOOL bUndoCurrentlyEnabled = IsUndoEnabled();
/*N*/ 	// Der von Hand reingesteckte Text kann nicht vom Anwender rueckgaengig gemacht werden.
/*N*/ 	EnableUndo( FALSE );
/*N*/ 
/*N*/ 	EditSelection aEmptySel( aStartPaM, aStartPaM );
/*N*/ 	EditPaM aPaM = aStartPaM;
/*N*/ 	if ( rText.Len() )
/*N*/ 		aPaM = ImpInsertText( aEmptySel, rText );
/*N*/ 
/*N*/ 	for ( USHORT nView = 0; nView < aEditViews.Count(); nView++ )
/*N*/ 	{
/*N*/ 		EditView* pView = aEditViews[nView];
/*N*/ 		DBG_CHKOBJ( pView, EditView, 0 );
/*N*/ 		pView->pImpEditView->SetEditSelection( EditSelection( aPaM, aPaM ) );
/*N*/ 		// Wenn kein Text, dann auch Kein Format&Update
/*N*/ 		// => Der Text bleibt stehen.
/*N*/ 		if ( !rText.Len() && GetUpdateMode() )
/*N*/ 		{
/*?*/ 			Rectangle aTmpRec( pView->GetOutputArea().TopLeft(),
/*?*/ 								Size( aPaperSize.Width(), nCurTextHeight ) );
/*?*/ 			aTmpRec.Intersection( pView->GetOutputArea() );
/*?*/ 			pView->GetWindow()->Invalidate( aTmpRec );
/*N*/ 		}
/*N*/ 	}
/*N*/ 	if( !rText.Len() )	// sonst muss spaeter noch invalidiert werden, !bFormatted reicht.
/*N*/ 		nCurTextHeight = 0;
/*N*/ 	EnableUndo( bUndoCurrentlyEnabled );
/*N*/ #ifndef SVX_LIGHT
/*N*/ 	DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "Undo nach SetText?" );
/*N*/ #endif
/*N*/ }


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

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

/*N*/ void ImpEditEngine::TextModified()
/*N*/ {
/*N*/ 	bFormatted = FALSE;
/*N*/ 
/*N*/     if ( GetNotifyHdl().IsSet() )
/*N*/     {
/*?*/         DBG_BF_ASSERT(0, "STRIP"); //STRIP001 EENotify aNotify( EE_NOTIFY_TEXTMODIFIED );
/*N*/     }
/*N*/ }


/*N*/ void ImpEditEngine::ParaAttribsChanged( ContentNode* pNode )
/*N*/ {
/*N*/ 	DBG_ASSERT( pNode, "ParaAttribsChanged: Welcher?" );
/*N*/ 
/*N*/ 	aEditDoc.SetModified( TRUE );
/*N*/ 	bFormatted = FALSE;
/*N*/ 
/*N*/ 	ParaPortion* pPortion = FindParaPortion( pNode );
/*N*/ 	DBG_ASSERT( pPortion, "ParaAttribsChanged: Portion?" );
/*N*/ 	pPortion->MarkSelectionInvalid( 0, pNode->Len() );
/*N*/ 
/*N*/ 	USHORT nPara = aEditDoc.GetPos( pNode );
/*N*/ 	pEditEngine->ParaAttribsChanged( nPara );
/*N*/ 
/*N*/ 	ParaPortion* pNextPortion = GetParaPortions().SaveGetObject( nPara+1 );
/*N*/ 	// => wird sowieso noch formatiert, wenn Invalid.
/*N*/ 	if ( pNextPortion && !pNextPortion->IsInvalid() )
/*?*/ 		CalcHeight( pNextPortion );
/*N*/ }

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


















/*N*/ EditPaM ImpEditEngine::WordRight( const EditPaM& rPaM, sal_Int16 nWordType )
/*N*/ {
/*N*/ 	xub_StrLen nMax = rPaM.GetNode()->Len();
/*N*/ 	EditPaM aNewPaM( rPaM );
/*N*/ 	if ( aNewPaM.GetIndex() < nMax )
/*N*/ 	{
/*N*/ 		uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
/*N*/ 		i18n::Boundary aBoundary = xBI->nextWord( *aNewPaM.GetNode(), aNewPaM.GetIndex(), GetLocale( aNewPaM ), nWordType );
/*N*/ 		aNewPaM.SetIndex( (USHORT)aBoundary.startPos );
/*N*/ 	}
/*N*/ 	// not 'else', maybe the index reached nMax now...
/*N*/ 	if ( aNewPaM.GetIndex() >= nMax )
/*N*/ 	{
/*?*/ 		// Naechster Absatz...
/*?*/ 		USHORT nCurPara = aEditDoc.GetPos( aNewPaM.GetNode() );
/*?*/ 		ContentNode* pNextNode = aEditDoc.SaveGetObject( ++nCurPara );
/*?*/ 		if ( pNextNode )
/*?*/ 		{
/*?*/ 			aNewPaM.SetNode( pNextNode );
/*?*/ 			aNewPaM.SetIndex( 0 );
/*?*/ 		}
/*N*/ 	}
/*N*/ 	return aNewPaM;
/*N*/ }



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

/*N*/ void ImpEditEngine::InitScriptTypes( USHORT nPara )
/*N*/ {
/*N*/ 	ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
/*N*/ 	ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
/*N*/ 	rTypes.Remove( 0, rTypes.Count() );
/*N*/ 
/*N*/ 
/*N*/ //	pParaPortion->aExtraCharInfos.Remove( 0, pParaPortion->aExtraCharInfos.Count() );
/*N*/ 
/*N*/ 	ContentNode* pNode = pParaPortion->GetNode();
/*N*/ 	if ( pNode->Len() )
/*N*/ 	{
/*N*/ 		uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
/*N*/ 
/*N*/ 		String aText( *pNode );
/*N*/ 
/*N*/ 		// To handle fields put the character from the field in the string,
/*N*/ 		// because endOfScript( ... ) will skip the CH_FEATURE, because this is WEAK
/*N*/ 		EditCharAttrib* pField = pNode->GetCharAttribs().FindNextAttrib( EE_FEATURE_FIELD, 0 );
/*N*/ 		while ( pField )
/*N*/ 		{
/*N*/ 			::rtl::OUString aFldText( ((EditCharAttribField*)pField)->GetFieldValue() );
/*N*/ 			if ( aFldText.getLength() )
/*N*/ 			{
/*N*/ 				aText.SetChar( pField->GetStart(), aFldText.getStr()[0] );
/*N*/ 				short nFldScriptType = xBI->getScriptType( aFldText, 0 );
/*N*/ 
/*N*/ 				for ( USHORT nCharInField = 1; nCharInField < aFldText.getLength(); nCharInField++ )
/*N*/ 				{
/*N*/ 					short nTmpType = xBI->getScriptType( aFldText, nCharInField );
/*N*/ 
/*N*/ 					// First char from field wins...
/*N*/ 					if ( nFldScriptType == i18n::ScriptType::WEAK )
/*N*/ 					{
/*?*/ 						nFldScriptType = nTmpType;
/*?*/ 						aText.SetChar( pField->GetStart(), aFldText.getStr()[nCharInField] );
/*N*/ 					}
/*N*/ 
/*N*/ 					// ...  but if the first one is LATIN, and there are CJK or CTL chars too,
/*N*/ 					// we prefer that ScripType because we need an other font.
/*N*/ 					if ( ( nTmpType == i18n::ScriptType::ASIAN ) || ( nTmpType == i18n::ScriptType::COMPLEX ) )
/*N*/ 					{
/*?*/ 						aText.SetChar( pField->GetStart(), aFldText.getStr()[nCharInField] );
/*?*/ 						break;
/*N*/ 					}
/*N*/ 				}
/*N*/ 			}
/*N*/ 			pField = pNode->GetCharAttribs().FindNextAttrib( EE_FEATURE_FIELD, pField->GetEnd() );
/*N*/ 		}
/*N*/ 
/*N*/ 		::rtl::OUString aOUText( aText );
/*N*/ 		USHORT nTextLen = (USHORT)aOUText.getLength();
/*N*/ 
/*N*/ 		long nPos = 0;
/*N*/ 		short nScriptType = xBI->getScriptType( aOUText, nPos );
/*N*/ 		rTypes.Insert( ScriptTypePosInfo( nScriptType, (USHORT)nPos, nTextLen ), rTypes.Count() );
/*N*/ 		nPos = xBI->endOfScript( aOUText, nPos, nScriptType );
/*N*/ 		while ( ( nPos != (-1) ) && ( nPos < nTextLen ) )
/*N*/ 		{
/*N*/ 			rTypes[rTypes.Count()-1].nEndPos = (USHORT)nPos;
/*N*/ 
/*N*/             nScriptType = xBI->getScriptType( aOUText, nPos );
/*N*/ 			long nEndPos = xBI->endOfScript( aOUText, nPos, nScriptType );
/*N*/ 
/*N*/             // #96850# Handle blanks as weak, remove if BreakIterator returns WEAK for spaces.
/*N*/ 			if ( ( nScriptType == i18n::ScriptType::LATIN ) && ( aOUText.getStr()[ nPos ] == 0x20 ) )
/*N*/             {
/*?*/                 BOOL bOnlySpaces = TRUE;
/*?*/                 for ( USHORT n = nPos+1; ( n < nEndPos ) && bOnlySpaces; n++ )
/*?*/                 {
/*?*/                     if ( aOUText.getStr()[ n ] != 0x20 )
/*?*/                         bOnlySpaces = FALSE;
/*?*/                 }
/*?*/                 if ( bOnlySpaces )
/*?*/                     nScriptType = i18n::ScriptType::WEAK;
/*N*/             }
/*N*/ 
/*N*/             if ( ( nScriptType == i18n::ScriptType::WEAK ) || ( nScriptType == rTypes[rTypes.Count()-1].nScriptType ) )
/*N*/             {
/*?*/                 // Expand last ScriptTypePosInfo, don't create weak or unecessary portions
/*?*/                 rTypes[rTypes.Count()-1].nEndPos = (USHORT)nEndPos;
/*N*/             }
/*N*/             else
/*N*/             {
/*N*/ 			    rTypes.Insert( ScriptTypePosInfo( nScriptType, (USHORT)nPos, nTextLen ), rTypes.Count() );
/*N*/             }
/*N*/ 
/*N*/ 			nPos = nEndPos;
/*N*/ 		}
/*N*/ 
/*N*/ 		if ( rTypes[0].nScriptType == i18n::ScriptType::WEAK )
/*N*/ 			rTypes[0].nScriptType = ( rTypes.Count() > 1 ) ? rTypes[1].nScriptType : GetI18NScriptTypeOfLanguage( GetDefaultLanguage() );
/*N*/ 	}
/*N*/ }

/*N*/ USHORT ImpEditEngine::GetScriptType( const EditPaM& rPaM, USHORT* pEndPos ) const
/*N*/ {
/*N*/ 	USHORT nScriptType = 0;
/*N*/ 
/*N*/ 	if ( pEndPos )
/*?*/ 		*pEndPos = rPaM.GetNode()->Len();
/*N*/ 
/*N*/ 	if ( rPaM.GetNode()->Len() )
/*N*/ 	{
/*N*/  		USHORT nPara = GetEditDoc().GetPos( rPaM.GetNode() );
/*N*/ 		ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
/*N*/ 		if ( !pParaPortion->aScriptInfos.Count() )
/*?*/ 			((ImpEditEngine*)this)->InitScriptTypes( nPara );
/*N*/ 
/*N*/ 		ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
/*N*/ 		USHORT nPos = rPaM.GetIndex();
/*N*/ 		for ( USHORT n = 0; n < rTypes.Count(); n++ )
/*N*/ 		{
/*N*/ 			if ( ( rTypes[n].nStartPos <= nPos ) && ( rTypes[n].nEndPos >= nPos ) )
/*N*/ 	   		{
/*N*/ 				nScriptType = rTypes[n].nScriptType;
/*N*/ 				if( pEndPos )
/*?*/ 					*pEndPos = rTypes[n].nEndPos;
/*N*/ 				break;
/*N*/ 			}
/*N*/ 		}
/*N*/ 	}
/*N*/ 	return nScriptType ? nScriptType : GetI18NScriptTypeOfLanguage( GetDefaultLanguage() );
/*N*/ }

/*N*/ USHORT ImpEditEngine::GetScriptType( const EditSelection& rSel ) const
/*N*/ {
/*N*/ 	EditSelection aSel( rSel );
/*N*/ 	aSel.Adjust( aEditDoc );
/*N*/ 
/*N*/ 	short nScriptType = 0;
/*N*/ 
/*N*/  	USHORT nStartPara = GetEditDoc().GetPos( aSel.Min().GetNode() );
/*N*/  	USHORT nEndPara = GetEditDoc().GetPos( aSel.Max().GetNode() );
/*N*/ 
/*N*/ 	for ( USHORT nPara = nStartPara; nPara <= nEndPara; nPara++ )
/*N*/ 	{
/*N*/ 		ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
/*N*/ 		if ( !pParaPortion->aScriptInfos.Count() )
/*N*/ 			((ImpEditEngine*)this)->InitScriptTypes( nPara );
/*N*/ 
/*N*/ 		ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
/*N*/ 
/*N*/ 		USHORT nS = ( nPara == nStartPara ) ? aSel.Min().GetIndex() : 0;
/*N*/ 		USHORT nE = ( nPara == nEndPara ) ? aSel.Max().GetIndex() : pParaPortion->GetNode()->Len();
/*N*/ 		for ( USHORT n = 0; n < rTypes.Count(); n++ )
/*N*/ 		{
/*N*/ 			if ( ( rTypes[n].nStartPos <= nE ) && ( rTypes[n].nEndPos >= nS ) )
/*N*/ 		   	{
/*N*/ 				if ( rTypes[n].nScriptType != i18n::ScriptType::WEAK )
/*N*/ 				{
/*N*/                     nScriptType |= GetItemScriptType ( rTypes[n].nScriptType );
/*N*/                 }
/*N*/                 else
/*N*/ 				{
/*?*/                     if ( !nScriptType && n )
/*?*/                     {
/*?*/                         // #93548# When starting with WEAK, use prev ScriptType...
/*?*/                         nScriptType = rTypes[n-1].nScriptType;
/*N*/                     }
/*N*/ 				}
/*N*/ 			}
/*N*/ 		}
/*N*/ 	}
/*N*/ 	return nScriptType ? nScriptType : GetI18NScriptTypeOfLanguage( GetDefaultLanguage() );
/*N*/ }

/*N*/ BOOL ImpEditEngine::IsScriptChange( const EditPaM& rPaM ) const
/*N*/ {
/*N*/ 	BOOL bScriptChange = FALSE;
/*N*/ 
/*N*/ 	if ( rPaM.GetNode()->Len() )
/*N*/ 	{
/*N*/ 		USHORT nPara = GetEditDoc().GetPos( rPaM.GetNode() );
/*N*/ 		ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
/*N*/ 		if ( !pParaPortion->aScriptInfos.Count() )
/*?*/ 			((ImpEditEngine*)this)->InitScriptTypes( nPara );
/*N*/ 
/*N*/ 		ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
/*N*/ 		USHORT nPos = rPaM.GetIndex();
/*N*/ 		for ( USHORT n = 0; n < rTypes.Count(); n++ )
/*N*/ 		{
/*N*/ 			if ( rTypes[n].nStartPos == nPos )
/*N*/ 	   		{
/*N*/ 				bScriptChange = TRUE;
/*N*/ 				break;
/*N*/ 			}
/*N*/ 		}
/*N*/ 	}
/*N*/ 	return bScriptChange;
/*N*/ }

/*N*/ BOOL ImpEditEngine::HasScriptType( USHORT nPara, USHORT nType ) const
/*N*/ {
/*N*/     BOOL bTypeFound = FALSE;
/*N*/ 
/*N*/ 	ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
/*N*/ 	if ( !pParaPortion->aScriptInfos.Count() )
/*N*/ 		((ImpEditEngine*)this)->InitScriptTypes( nPara );
/*N*/ 
/*N*/     ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
/*N*/ 	for ( USHORT n = rTypes.Count(); n && !bTypeFound; )
/*N*/ 	{
/*N*/ 	    if ( rTypes[--n].nScriptType == nType )
/*N*/                 bTypeFound = TRUE;
/*N*/     }
/*N*/     return bTypeFound;
/*N*/ }

/*N*/ void ImpEditEngine::InitWritingDirections( USHORT nPara )
/*N*/ {
/*N*/ 	ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
/*N*/ 	WritingDirectionInfos& rInfos = pParaPortion->aWritingDirectionInfos;
/*N*/ 	rInfos.Remove( 0, rInfos.Count() );
/*N*/ 
/*N*/     BOOL bCTL = FALSE;
/*N*/ 	ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
/*N*/ 	for ( USHORT n = 0; n < rTypes.Count(); n++ )
/*N*/ 	{
/*N*/ 		if ( rTypes[n].nScriptType == i18n::ScriptType::COMPLEX )
/*N*/ 	   	{
/*N*/ 			bCTL = TRUE;
/*N*/ 			break;
/*N*/ 		}
/*N*/ 	}
/*N*/ 
/*N*/     const BYTE nDefaultDir = IsRightToLeft( nPara ) ? UBIDI_RTL : UBIDI_LTR;
/*N*/ 	if ( ( bCTL || ( nDefaultDir == UBIDI_RTL ) ) && pParaPortion->GetNode()->Len() )
/*N*/ 	{
/*?*/ 
/*?*/         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, aText.GetBuffer(), aText.Len(), nDefaultDir, NULL, &nError );
/*?*/         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 );
/*N*/ 	}
/*N*/ 
/*N*/     // No infos mean no CTL and default dir is L2R...
/*N*/     if ( !rInfos.Count() )
/*N*/         rInfos.Insert( WritingDirectionInfo( 0, 0, (USHORT)pParaPortion->GetNode()->Len() ), rInfos.Count() );
/*N*/ 
/*N*/ }

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



/*N*/ BYTE ImpEditEngine::GetRightToLeft( USHORT nPara, USHORT nPos, USHORT* pStart, USHORT* pEnd )
/*N*/ {
/*N*/ //    BYTE nRightToLeft = IsRightToLeft( nPara ) ? 1 : 0;
/*N*/     BYTE nRightToLeft = 0;
/*N*/ 
/*N*/     ContentNode* pNode = aEditDoc.SaveGetObject( nPara );
/*N*/     if ( pNode && pNode->Len() )
/*N*/     {
/*N*/ 		ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
/*N*/ 		if ( !pParaPortion->aWritingDirectionInfos.Count() )
/*N*/ 			InitWritingDirections( nPara );
/*N*/ 
/*N*/         BYTE nType = 0;
/*N*/ 		WritingDirectionInfos& rDirInfos = pParaPortion->aWritingDirectionInfos;
/*N*/ 		for ( USHORT n = 0; n < rDirInfos.Count(); n++ )
/*N*/ 		{
/*N*/ 			if ( ( rDirInfos[n].nStartPos <= nPos ) && ( rDirInfos[n].nEndPos >= nPos ) )
/*N*/ 	   		{
/*N*/ 				nRightToLeft = rDirInfos[n].nType;
/*N*/                 if ( pStart )
/*?*/                     *pStart = rDirInfos[n].nStartPos;
/*N*/                 if ( pEnd )
/*?*/                     *pEnd = rDirInfos[n].nEndPos;
/*N*/ 				break;
/*N*/ 			}
/*N*/ 		}
/*N*/     }
/*N*/     return nRightToLeft;
/*N*/ }

/*N*/ SvxAdjust ImpEditEngine::GetJustification( USHORT nPara ) const
/*N*/ {
/*N*/ 	SvxAdjust eJustification = SVX_ADJUST_LEFT;
/*N*/ 
/*N*/     if ( !aStatus.IsOutliner() )
/*N*/     {
/*N*/ 		eJustification = ((const SvxAdjustItem&) GetParaAttrib( nPara, EE_PARA_JUST )).GetAdjust();
/*N*/ 
/*N*/         if ( IsRightToLeft( nPara ) )
/*N*/         {
/*?*/             if ( eJustification == SVX_ADJUST_LEFT )
/*?*/                 eJustification = SVX_ADJUST_RIGHT;
/*?*/             else if ( eJustification == SVX_ADJUST_RIGHT )
/*?*/                 eJustification = SVX_ADJUST_LEFT;
/*N*/         }
/*N*/     }
/*N*/     return eJustification;
/*N*/ }


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

/*N*/ void ImpEditEngine::ImpRemoveChars( const EditPaM& rPaM, USHORT nChars, EditUndoRemoveChars* pCurUndo )
/*N*/ {
/*N*/ 	aEditDoc.RemoveChars( rPaM, nChars );
/*N*/ 	TextModified();
/*N*/ }



/*N*/ EditPaM ImpEditEngine::ImpConnectParagraphs( ContentNode* pLeft, ContentNode* pRight, BOOL bBackward )
/*N*/ {
/*N*/ 	DBG_ASSERT( pLeft != pRight, "Den gleichen Absatz zusammenfuegen ?" );
/*N*/ 	DBG_ASSERT( aEditDoc.GetPos( pLeft ) != USHRT_MAX, "Einzufuegenden Node nicht gefunden(1)" );
/*N*/ 	DBG_ASSERT( aEditDoc.GetPos( pRight ) != USHRT_MAX, "Einzufuegenden Node nicht gefunden(2)" );
/*N*/ 
/*N*/ 	USHORT nParagraphTobeDeleted = aEditDoc.GetPos( pRight );
/*N*/ 	DeletedNodeInfo* pInf = new DeletedNodeInfo( (ULONG)pRight, nParagraphTobeDeleted );
/*N*/ 	aDeletedNodes.Insert( pInf, aDeletedNodes.Count() );
/*N*/ 
/*N*/ #ifndef SVX_LIGHT
/*N*/ 	if ( IsUndoEnabled() && !IsInUndo() )
/*N*/ 	{
/*?*/ 		DBG_BF_ASSERT(0, "STRIP"); //STRIP001 InsertUndo( new EditUndoConnectParas( this,
/*N*/ 	}
/*N*/ #endif
/*N*/ 
/*N*/ 	if ( bBackward )
/*N*/ 	{
/*?*/ 		pLeft->SetStyleSheet( pRight->GetStyleSheet(), TRUE );
/*?*/ 		pLeft->GetContentAttribs().GetItems().Set( pRight->GetContentAttribs().GetItems() );
/*?*/ 		pLeft->GetCharAttribs().GetDefFont() = pRight->GetCharAttribs().GetDefFont();
/*N*/ 	}
/*N*/ 
/*N*/ 	ParaAttribsChanged( pLeft );
/*N*/ 
/*N*/ 	// Erstmal Portions suchen, da pRight nach ConnectParagraphs weg.
/*N*/ 	ParaPortion* pLeftPortion = FindParaPortion( pLeft );
/*N*/ 	ParaPortion* pRightPortion = FindParaPortion( pRight );
/*N*/ 	DBG_ASSERT( pLeftPortion, "Blinde Portion in ImpConnectParagraphs(1)" );
/*N*/ 	DBG_ASSERT( pRightPortion, "Blinde Portion in ImpConnectParagraphs(2)" );
/*N*/ 	DBG_ASSERT( nParagraphTobeDeleted == GetParaPortions().GetPos( pRightPortion ), "NodePos != PortionPos?" );
/*N*/ 
/*N*/ #ifndef SVX_LIGHT
/*N*/ 	if ( GetStatus().DoOnlineSpelling() )
/*N*/ 	{
/*?*/ 		DBG_BF_ASSERT(0, "STRIP"); //STRIP001 xub_StrLen nEnd = pLeft->Len();
/*N*/ 	}
/*N*/ #endif
/*N*/ 
/*N*/ 	if ( IsCallParaInsertedOrDeleted() )
/*N*/ 		GetEditEnginePtr()->ParagraphDeleted( nParagraphTobeDeleted );
/*N*/ 
/*N*/ 	EditPaM aPaM = aEditDoc.ConnectParagraphs( pLeft, pRight );
/*N*/ 	GetParaPortions().Remove( nParagraphTobeDeleted );
/*N*/ 	delete pRightPortion;
/*N*/ 
/*N*/ 	pLeftPortion->MarkSelectionInvalid( aPaM.GetIndex(), pLeft->Len() );
/*N*/ 
/*N*/ 	// der rechte Node wird von EditDoc::ConnectParagraphs() geloescht.
/*N*/ 
/*N*/ 	if ( GetTextRanger() )
/*N*/ 	{
/*?*/ 		// 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();
/*?*/ 		}
/*N*/ 	}
/*N*/ 
/*N*/ 	TextModified();
/*N*/ 
/*N*/ 	return aPaM;
/*N*/ }


/*N*/ EditPaM ImpEditEngine::ImpDeleteSelection( EditSelection aSel )
/*N*/ {
/*N*/ 	if ( !aSel.HasRange() )
/*?*/ 		return aSel.Min();
/*N*/ 
/*N*/ 	aSel.Adjust( aEditDoc );
/*N*/ 	EditPaM aStartPaM( aSel.Min() );
/*N*/ 	EditPaM aEndPaM( aSel.Max() );
/*N*/ 
/*N*/ 	CursorMoved( aStartPaM.GetNode() ); // nur damit neu eingestellte Attribute verschwinden...
/*N*/ 	CursorMoved( aEndPaM.GetNode() );	// nur damit neu eingestellte Attribute verschwinden...
/*N*/ 
/*N*/ 	DBG_ASSERT( aStartPaM.GetIndex() <= aStartPaM.GetNode()->Len(), "Index im Wald in ImpDeleteSelection" )
/*N*/ 	DBG_ASSERT( aEndPaM.GetIndex() <= aEndPaM.GetNode()->Len(), "Index im Wald in ImpDeleteSelection" )
/*N*/ 
/*N*/ 	USHORT nStartNode = aEditDoc.GetPos( aStartPaM.GetNode() );
/*N*/ 	USHORT nEndNode = aEditDoc.GetPos( aEndPaM.GetNode() );
/*N*/ 
/*N*/ 	DBG_ASSERT( nEndNode != USHRT_MAX, "Start > End ?!" );
/*N*/ 	DBG_ASSERT( nStartNode <= nEndNode, "Start > End ?!" );
/*N*/ 
/*N*/ 	// Alle Nodes dazwischen entfernen....
/*N*/ 	for ( ULONG z = nStartNode+1; z < nEndNode; z++ )
/*N*/ 	{
/*?*/ 		// Immer nStartNode+1, wegen Remove()!
/*?*/ 		DBG_BF_ASSERT(0, "STRIP"); //STRIP001 ImpRemoveParagraph( nStartNode+1 );
/*N*/ 	}
/*N*/ 
/*N*/ 	if ( aStartPaM.GetNode() != aEndPaM.GetNode() )
/*N*/ 	{
/*N*/ 		// Den Rest des StartNodes...
/*N*/ 		USHORT nChars;
/*N*/ 		nChars = aStartPaM.GetNode()->Len() - aStartPaM.GetIndex();
/*N*/ 		ImpRemoveChars( aStartPaM, nChars );
/*N*/ 		ParaPortion* pPortion = FindParaPortion( aStartPaM.GetNode() );
/*N*/ 		DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteSelection(3)" );
/*N*/ 		pPortion->MarkSelectionInvalid( aStartPaM.GetIndex(), aStartPaM.GetNode()->Len() );
/*N*/ 
/*N*/ 		// Den Anfang des EndNodes....
/*N*/ 		nChars = aEndPaM.GetIndex();
/*N*/ 		aEndPaM.SetIndex( 0 );
/*N*/ 		ImpRemoveChars( aEndPaM, nChars );
/*N*/ 		pPortion = FindParaPortion( aEndPaM.GetNode() );
/*N*/ 		DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteSelection(4)" );
/*N*/ 		pPortion->MarkSelectionInvalid( 0, aEndPaM.GetNode()->Len() );
/*N*/ 		// Zusammenfuegen....
/*N*/ 		aStartPaM = ImpConnectParagraphs( aStartPaM.GetNode(), aEndPaM.GetNode() );
/*N*/ 	}
/*N*/ 	else
/*N*/ 	{
/*N*/ 		USHORT nChars;
/*N*/ 		nChars = aEndPaM.GetIndex() - aStartPaM.GetIndex();
/*N*/ 		ImpRemoveChars( aStartPaM, nChars );
/*N*/ 		ParaPortion* pPortion = FindParaPortion( aStartPaM.GetNode() );
/*N*/ 		DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteSelection(5)" );
/*N*/ 		pPortion->MarkInvalid( aEndPaM.GetIndex(), aStartPaM.GetIndex() - aEndPaM.GetIndex() );
/*N*/ 	}
/*N*/ 
/*N*/ 	UpdateSelections();
/*N*/ 	TextModified();
/*N*/ 	return aStartPaM;
/*N*/ }


/*N*/ EditPaM ImpEditEngine::ImpInsertText( EditSelection aCurSel, const XubString& rStr )
/*N*/ {
/*N*/ 	EditPaM aPaM;
/*N*/ 	if ( aCurSel.HasRange() )
/*N*/ 		aPaM = ImpDeleteSelection( aCurSel );
/*N*/ 	else
/*N*/ 		aPaM = aCurSel.Max();
/*N*/ 
/*N*/ 	EditPaM aCurPaM( aPaM );	// fuers Invalidieren
/*N*/ 
/*N*/ 	XubString aText( rStr );
/*N*/ 	aText.ConvertLineEnd( LINEEND_LF );
/*N*/ 	SfxVoidItem aTabItem( EE_FEATURE_TAB );
/*N*/ 
/*N*/ 	// Konvertiert nach LineSep = \n
/*N*/ 	// Token mit LINE_SEP abfragen,
/*N*/ 	// da der MAC-Compiler aus \n etwas anderes macht!
/*N*/ 
/*N*/ 	USHORT nStart = 0;
/*N*/ 	while ( nStart < aText.Len() )
/*N*/ 	{
/*N*/ 		USHORT nEnd = aText.Search( LINE_SEP, nStart );
/*N*/ 		if ( nEnd == STRING_NOTFOUND )
/*N*/ 			nEnd = aText.Len();	// nicht dereferenzieren!
/*N*/ 
/*N*/ 		// Start == End => Leerzeile
/*N*/ 		if ( nEnd > nStart )
/*N*/ 		{
/*N*/ 			XubString aLine( aText, nStart, nEnd-nStart );
/*N*/ 			xub_StrLen nChars = aPaM.GetNode()->Len() + aLine.Len();
/*N*/ 			if ( nChars > MAXCHARSINPARA )
/*N*/ 			{
/*?*/                 USHORT nMaxNewChars = MAXCHARSINPARA-aPaM.GetNode()->Len();
/*?*/ 				nEnd -= ( aLine.Len() - nMaxNewChars );	// Dann landen die Zeichen im naechsten Absatz.
/*?*/ 				aLine.Erase( nMaxNewChars );            // Del Rest...
/*N*/ 			}
/*N*/ #ifndef SVX_LIGHT
/*N*/ 			if ( IsUndoEnabled() && !IsInUndo() )
/*N*/ 				InsertUndo( new EditUndoInsertChars( this, CreateEPaM( aPaM ), aLine ) );
/*N*/ #endif
/*N*/ 			// Tabs ?
/*N*/ 			if ( aLine.Search( '\t' ) == STRING_NOTFOUND )
/*N*/ 				aPaM = aEditDoc.InsertText( aPaM, aLine );
/*N*/ 			else
/*N*/ 			{
/*N*/ 				USHORT nStart2 = 0;
/*N*/ 				while ( nStart2 < aLine.Len() )
/*N*/ 				{
/*N*/ 					USHORT nEnd2 = aLine.Search( '\t', nStart2 );
/*N*/ 					if ( nEnd2 == STRING_NOTFOUND )
/*?*/ 						nEnd2 = aLine.Len();	// nicht dereferenzieren!
/*N*/ 
/*N*/ 					if ( nEnd2 > nStart2 )
/*?*/ 						aPaM = aEditDoc.InsertText( aPaM, XubString( aLine, nStart2, nEnd2-nStart2 ) );
/*N*/ 					if ( nEnd2 < aLine.Len() )
/*N*/ 					{
/*N*/ 						// aPaM = ImpInsertFeature( EditSelection( aPaM, aPaM ),  );
/*N*/ 						aPaM = aEditDoc.InsertFeature( aPaM, aTabItem );
/*N*/ 					}
/*N*/ 					nStart2 = nEnd2+1;
/*N*/ 				}
/*N*/ 			}
/*N*/ 			ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
/*N*/ 			DBG_ASSERT( pPortion, "Blinde Portion in InsertText" );
/*N*/ 			pPortion->MarkInvalid( aCurPaM.GetIndex(), aLine.Len() );
/*N*/ 		}
/*N*/ 		if ( nEnd < aText.Len() )
/*N*/ 			aPaM = ImpInsertParaBreak( aPaM );
/*N*/ 
/*N*/ 		nStart = nEnd+1;
/*N*/ 	}
/*N*/ 
/*N*/ 	TextModified();
/*N*/ 	return aPaM;
/*N*/ }

/*N*/ EditPaM ImpEditEngine::ImpFastInsertText( EditPaM aPaM, const XubString& rStr )
/*N*/ {
/*N*/ 	DBG_ASSERT( rStr.Search( 0x0A ) == STRING_NOTFOUND, "FastInsertText: Zeilentrenner nicht erlaubt!" );
/*N*/ 	DBG_ASSERT( rStr.Search( 0x0D ) == STRING_NOTFOUND, "FastInsertText: Zeilentrenner nicht erlaubt!" );
/*N*/ 	DBG_ASSERT( rStr.Search( '\t' ) == STRING_NOTFOUND, "FastInsertText: Features nicht erlaubt!" );
/*N*/ 
/*N*/ 	if ( ( aPaM.GetNode()->Len() + rStr.Len() ) < MAXCHARSINPARA )
/*N*/ 	{
/*N*/ #ifndef SVX_LIGHT
/*N*/ 		if ( IsUndoEnabled() && !IsInUndo() )
/*?*/ 			InsertUndo( new EditUndoInsertChars( this, CreateEPaM( aPaM ), rStr ) );
/*N*/ #endif
/*N*/ 
/*N*/ 		aPaM = aEditDoc.InsertText( aPaM, rStr );
/*N*/ 		TextModified();
/*N*/ 	}
/*N*/ 	else
/*N*/ 	{
/*?*/ 		aPaM = ImpInsertText( aPaM, rStr );
/*N*/ 	}
/*N*/ 
/*N*/ 	return aPaM;
/*N*/ }

/*N*/ EditPaM ImpEditEngine::ImpInsertFeature( EditSelection aCurSel, const SfxPoolItem& rItem )
/*N*/ {
/*N*/ 	EditPaM aPaM;
/*N*/ 	if ( aCurSel.HasRange() )
/*?*/ 		aPaM = ImpDeleteSelection( aCurSel );
/*N*/ 	else
/*N*/ 		aPaM = aCurSel.Max();
/*N*/ 
/*N*/ #ifndef SVX_LIGHT
/*N*/ 	if ( IsUndoEnabled() && !IsInUndo() )
/*N*/ 		InsertUndo( new EditUndoInsertFeature( this, CreateEPaM( aPaM ), rItem ) );
/*N*/ #endif
/*N*/ 	aPaM = aEditDoc.InsertFeature( aPaM, rItem );
/*N*/ 
/*N*/ 	ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
/*N*/ 	DBG_ASSERT( pPortion, "Blinde Portion in InsertFeature" );
/*N*/ 	pPortion->MarkInvalid( aPaM.GetIndex()-1, 1 );
/*N*/ 
/*N*/ 	TextModified();
/*N*/ 
/*N*/ 	return aPaM;
/*N*/ }

/*N*/ EditPaM ImpEditEngine::ImpInsertParaBreak( const EditPaM& rPaM, BOOL bKeepEndingAttribs )
/*N*/ {
/*N*/ #ifndef SVX_LIGHT
/*N*/ 	if ( IsUndoEnabled() && !IsInUndo() )
/*N*/ 		InsertUndo( new EditUndoSplitPara( this, aEditDoc.GetPos( rPaM.GetNode() ), rPaM.GetIndex() ) );
/*N*/ #endif
/*N*/ 
/*N*/ 	EditPaM aPaM( aEditDoc.InsertParaBreak( rPaM, bKeepEndingAttribs ) );
/*N*/ 
/*N*/ #ifndef SVX_LIGHT
/*N*/ 	if ( GetStatus().DoOnlineSpelling() )
/*N*/ 	{
/*?*/ 		DBG_BF_ASSERT(0, "STRIP"); //STRIP001 xub_StrLen nEnd = rPaM.GetNode()->Len();
/*N*/ 	}
/*N*/ #endif // !SVX_LIGHT
/*N*/ 
/*N*/ 
/*N*/ 	ParaPortion* pPortion = FindParaPortion( rPaM.GetNode() );
/*N*/ 	DBG_ASSERT( pPortion, "Blinde Portion in ImpInsertParaBreak" );
/*N*/ 	pPortion->MarkInvalid( rPaM.GetIndex(), 0 );
/*N*/ 
/*N*/ 	// Optimieren: Nicht unnoetig viele GetPos auf die Listen ansetzen!
/*N*/ 	// Hier z.B. bei Undo, aber auch in allen anderen Methoden.
/*N*/ 	USHORT nPos = GetParaPortions().GetPos( pPortion );
/*N*/ 	ParaPortion* pNewPortion = new ParaPortion( aPaM.GetNode() );
/*N*/ 	GetParaPortions().Insert( pNewPortion, nPos + 1 );
/*N*/ 	ParaAttribsChanged( pNewPortion->GetNode() );
/*N*/ 	if ( IsCallParaInsertedOrDeleted() )
/*N*/ 		GetEditEnginePtr()->ParagraphInserted( nPos+1 );
/*N*/ 
/*N*/ 	CursorMoved( rPaM.GetNode() );	// falls leeres Attribut entstanden.
/*N*/ 	TextModified();
/*N*/ 	return aPaM;
/*N*/ }

/*N*/ EditPaM ImpEditEngine::ImpFastInsertParagraph( USHORT nPara )
/*N*/ {
/*N*/ #ifndef SVX_LIGHT
/*N*/ 	if ( IsUndoEnabled() && !IsInUndo() )
/*N*/ 	{
/*?*/ 		DBG_BF_ASSERT(0, "STRIP"); //STRIP001 if ( nPara )
/*N*/ 	}
/*N*/ #endif
/*N*/ 
/*N*/ 	ContentNode* pNode = new ContentNode( aEditDoc.GetItemPool() );
/*N*/ 	// Falls FlatMode, wird spaeter kein Font eingestellt:
/*N*/ 	pNode->GetCharAttribs().GetDefFont() = aEditDoc.GetDefFont();
/*N*/ 
/*N*/ #ifndef SVX_LIGHT
/*N*/ 	if ( GetStatus().DoOnlineSpelling() )
/*?*/ 		pNode->CreateWrongList();
/*N*/ #endif // !SVX_LIGHT
/*N*/ 
/*N*/ 	aEditDoc.Insert( pNode, nPara );
/*N*/ 
/*N*/ 	ParaPortion* pNewPortion = new ParaPortion( pNode );
/*N*/ 	GetParaPortions().Insert( pNewPortion, nPara );
/*N*/ 	if ( IsCallParaInsertedOrDeleted() )
/*N*/ 		GetEditEnginePtr()->ParagraphInserted( nPara );
/*N*/ 
/*N*/ 	return EditPaM( pNode, 0 );
/*N*/ }

/*N*/ BOOL ImpEditEngine::UpdateFields()
/*N*/ {
/*N*/ 	BOOL bChanges = FALSE;
/*N*/ 	USHORT nParas = GetEditDoc().Count();
/*N*/ 	for ( USHORT nPara = 0; nPara < nParas; nPara++ )
/*N*/ 	{
/*N*/ 		BOOL bChangesInPara = FALSE;
/*N*/ 		ContentNode* pNode = GetEditDoc().GetObject( nPara );
/*N*/ 		DBG_ASSERT( pNode, "NULL-Pointer im Doc" );
/*N*/ 		CharAttribArray& rAttribs = pNode->GetCharAttribs().GetAttribs();
/*N*/ 		USHORT nAttrs = rAttribs.Count();
/*N*/ 		for ( USHORT nAttr = 0; nAttr < rAttribs.Count(); nAttr++ )
/*N*/ 		{
/*N*/ 			EditCharAttrib* pAttr = rAttribs[nAttr];
/*N*/ 			if ( pAttr->Which() == EE_FEATURE_FIELD )
/*N*/ 			{
/*N*/ 				EditCharAttribField* pField = (EditCharAttribField*)pAttr;
/*N*/ 				EditCharAttribField* pCurrent = new EditCharAttribField( *pField );
/*N*/ 				pField->Reset();
/*N*/ 
/*N*/                 if ( aStatus.MarkFields() )
/*N*/                     pField->GetFldColor() = new Color( GetColorConfig().GetColorValue( svtools::WRITERFIELDSHADINGS ).nColor );
/*N*/ 
/*N*/ 				XubString aFldValue = GetEditEnginePtr()->CalcFieldValue(
/*N*/ 										(const SvxFieldItem&)*pField->GetItem(),
/*N*/ 										nPara, pField->GetStart(),
/*N*/ 										pField->GetTxtColor(), pField->GetFldColor() );
/*N*/ 				pField->GetFieldValue() = aFldValue;
/*N*/ 				if ( *pField != *pCurrent )
/*N*/ 				{
/*N*/ 					bChanges = TRUE;
/*N*/ 					bChangesInPara = TRUE;
/*N*/ 				}
/*N*/ 				delete pCurrent;
/*N*/ 			}
/*N*/ 		}
/*N*/ 		if ( bChangesInPara )
/*N*/ 		{
/*N*/ 			// ggf. etwas genauer invalidieren.
/*N*/ 			ParaPortion* pPortion = GetParaPortions().GetObject( nPara );
/*N*/ 			DBG_ASSERT( pPortion, "NULL-Pointer im Doc" );
/*N*/ 			pPortion->MarkSelectionInvalid( 0, pNode->Len() );
/*N*/ 		}
/*N*/ 	}
/*N*/ 	return bChanges;
/*N*/ }

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

//	----------------------------------------------------------------------
//	Hilfsfunktionen
//	----------------------------------------------------------------------

/*N*/ EditPaM ImpEditEngine::GetPaM( Point aDocPos, BOOL bSmart )
/*N*/ {
/*N*/ 	DBG_ASSERT( GetUpdateMode(), "Darf bei Update=FALSE nicht erreicht werden: GetPaM" );
/*N*/ 
/*N*/ 	long nY = 0;
/*N*/ 	long nTmpHeight;
/*N*/ 	EditPaM aPaM;
/*N*/ 	USHORT nPortion;
/*N*/ 	for ( nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
/*N*/ 	{
/*N*/ 		ParaPortion* pPortion = GetParaPortions().GetObject(nPortion);
/*N*/ 		nTmpHeight = pPortion->GetHeight(); 	// sollte auch bei !bVisible richtig sein!
/*N*/ 		nY += nTmpHeight;
/*N*/ 		if ( nY > aDocPos.Y() )
/*N*/ 		{
/*N*/ 			nY -= nTmpHeight;
/*N*/ 			aDocPos.Y() -= nY;
/*N*/ 			// unsichtbare Portions ueberspringen:
/*N*/ 			while ( pPortion && !pPortion->IsVisible() )
/*N*/ 			{
/*?*/ 				nPortion++;
/*?*/ 				pPortion = GetParaPortions().SaveGetObject( nPortion );
/*N*/ 			}
/*N*/ 			DBG_ASSERT( pPortion, "Keinen sichtbaren Absatz gefunden: GetPaM" );
/*N*/ 			aPaM = GetPaM( pPortion, aDocPos, bSmart );
/*N*/ 			return aPaM;
/*N*/ 
/*N*/ 		}
/*N*/ 	}
/*?*/ 	// 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;
/*N*/ }

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

/*N*/ sal_uInt32 ImpEditEngine::CalcTextWidth( BOOL bIgnoreExtraSpace )
/*N*/ {
/*N*/ 	// Wenn noch nicht formatiert und nicht gerade dabei.
/*N*/ 	// Wird in der Formatierung bei AutoPageSize gerufen.
/*N*/ 	if ( !IsFormatted() && !IsFormatting() )
/*?*/ 		FormatDoc();
/*N*/ 
/*N*/ 	EditLine* pLine;
/*N*/ 
/*N*/ 	long nMaxWidth = 0;
/*N*/ 	long nCurWidth = 0;
/*N*/ 
/*N*/ 	// --------------------------------------------------
/*N*/ 	// Ueber alle Absaetze...
/*N*/ 	// --------------------------------------------------
/*N*/ 	USHORT nParas = GetParaPortions().Count();
/*N*/ 	USHORT nBiggestPara = 0;
/*N*/ 	USHORT nBiggestLine = 0;
/*N*/ 	for ( USHORT nPara = 0; nPara < nParas; nPara++ )
/*N*/ 	{
/*N*/ 		ParaPortion* pPortion = GetParaPortions().GetObject( nPara );
/*N*/ 		const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pPortion->GetNode() );
/*N*/ 
/*N*/ 		if ( pPortion->IsVisible() )
/*N*/ 		{
/*N*/ 			// --------------------------------------------------
/*N*/ 			// Ueber die Zeilen des Absatzes...
/*N*/ 			// --------------------------------------------------
/*N*/ 			ULONG nLines = pPortion->GetLines().Count();
/*N*/ 			for ( USHORT nLine = 0; nLine < nLines; nLine++ )
/*N*/ 			{
/*N*/ 				pLine = pPortion->GetLines().GetObject( nLine );
/*N*/ 				DBG_ASSERT( pLine, "NULL-Pointer im Zeileniterator in CalcWidth" );
/*N*/ 				// nCurWidth = pLine->GetStartPosX();
/*N*/ 				// Bei Center oder Right haengt die breite von der
/*N*/ 				// Papierbreite ab, hier nicht erwuenscht.
/*N*/ 				// Am besten generell nicht auf StartPosX verlassen,
/*N*/ 				// es muss auch die rechte Einrueckung beruecksichtigt werden!
/*N*/ 				nCurWidth = GetXValue( rLRItem.GetTxtLeft() );
/*N*/ 				if ( nLine == 0 )
/*N*/ 				{
/*N*/ 					long nFI = GetXValue( rLRItem.GetTxtFirstLineOfst() );
/*N*/ 					nCurWidth += nFI;
/*N*/ 					if ( pPortion->GetBulletX() > nCurWidth )
/*N*/ 					{
/*?*/ 						nCurWidth -= nFI;	// LI?
/*?*/ 						if ( pPortion->GetBulletX() > nCurWidth )
/*?*/ 							nCurWidth = pPortion->GetBulletX();
/*N*/ 					}
/*N*/ 				}
/*N*/ 				nCurWidth += GetXValue( rLRItem.GetRight() );
/*N*/ 				nCurWidth += CalcLineWidth( pPortion, pLine, bIgnoreExtraSpace );
/*N*/ 				if ( nCurWidth > nMaxWidth )
/*N*/ 				{
/*N*/ 					nMaxWidth = nCurWidth;
/*N*/ 				}
/*N*/ 			}
/*N*/ 		}
/*N*/ 	}
/*N*/ 	if ( nMaxWidth < 0 )
/*?*/ 		nMaxWidth = 0;
/*N*/ 
/*N*/ 	nMaxWidth++; // Ein breiter, da in CreateLines bei >= umgebrochen wird.
/*N*/ 	return (sal_uInt32)nMaxWidth;
/*N*/ }

/*N*/ sal_uInt32 ImpEditEngine::CalcLineWidth( ParaPortion* pPortion, EditLine* pLine, BOOL bIgnoreExtraSpace )
/*N*/ {
/*N*/ 	USHORT nPara = GetEditDoc().GetPos( pPortion->GetNode() );
/*N*/     ULONG nOldLayoutMode = GetRefDevice()->GetLayoutMode();
/*N*/ 
/*N*/     ImplInitLayoutMode( GetRefDevice(), nPara, 0xFFFF );
/*N*/ 
/*N*/     SvxAdjust eJustification = GetJustification( nPara );
/*N*/ 
/*N*/     // Berechnung der Breite ohne die Indents...
/*N*/ 	sal_uInt32 nWidth = 0;
/*N*/     USHORT nPos = pLine->GetStart();
/*N*/ 	for ( USHORT nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ )
/*N*/ 	{
/*N*/ 		TextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( nTP );
/*N*/ 		switch ( pTextPortion->GetKind() )
/*N*/ 		{
/*N*/ 			case PORTIONKIND_FIELD:
/*N*/ 			case PORTIONKIND_HYPHENATOR:
/*N*/ 			case PORTIONKIND_TAB:
/*N*/ 			{
/*N*/ 				nWidth += pTextPortion->GetSize().Width();
/*N*/ 			}
/*N*/ 			break;
/*N*/ 			case PORTIONKIND_TEXT:
/*N*/ 			{
/*N*/                 if ( ( eJustification != SVX_ADJUST_BLOCK ) || ( !bIgnoreExtraSpace ) )
/*N*/                 {
/*N*/ 				    nWidth += pTextPortion->GetSize().Width();
/*N*/                 }
/*N*/                 else
/*N*/                 {
/*?*/ 	                SvxFont aTmpFont( pPortion->GetNode()->GetCharAttribs().GetDefFont() );
/*?*/ 				    SeekCursor( pPortion->GetNode(), nPos+1, aTmpFont );
/*?*/ 				    aTmpFont.SetPhysFont( GetRefDevice() );
/*?*/ 					nWidth += aTmpFont.QuickGetTextSize( GetRefDevice(), *pPortion->GetNode(), nPos, pTextPortion->GetLen(), NULL ).Width();
/*N*/                 }
/*N*/ 			}
/*N*/ 			break;
/*N*/ 		}
/*N*/         nPos += pTextPortion->GetLen();
/*N*/ 	}
/*N*/ 
/*N*/     GetRefDevice()->SetLayoutMode( nOldLayoutMode );
/*N*/ 
/*N*/     return nWidth;
/*N*/ }

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

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




/*N*/ void ImpEditEngine::UpdateSelections()
/*N*/ {
/*N*/ 	USHORT nInvNodes = aDeletedNodes.Count();
/*N*/ 
/*N*/ 	// Pruefen, ob eine der Selektionen auf einem geloeschten Node steht...
/*N*/ 	// Wenn der Node gueltig ist, muss noch der Index geprueft werden!
/*N*/ 	for ( USHORT nView = 0; nView < aEditViews.Count(); nView++ )
/*N*/ 	{
/*N*/ 		EditView* pView = aEditViews.GetObject(nView);
/*N*/ 		DBG_CHKOBJ( pView, EditView, 0 );
/*N*/ 		EditSelection aCurSel( pView->pImpEditView->GetEditSelection() );
/*N*/ 		BOOL bChanged = FALSE;
/*N*/ 		for ( USHORT n = 0; n < nInvNodes; n++ )
/*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
/*?*/ 			}
/*N*/ 		}
/*N*/ 		if ( !bChanged )
/*N*/ 		{
/*N*/ 			// Index prueffen, falls Node geschrumpft.
/*N*/ 			if ( aCurSel.Min().GetIndex() > aCurSel.Min().GetNode()->Len() )
/*N*/ 			{
/*?*/ 				aCurSel.Min().GetIndex() = aCurSel.Min().GetNode()->Len();
/*?*/ 				pView->pImpEditView->SetEditSelection( aCurSel );
/*N*/ 			}
/*N*/ 			if ( aCurSel.Max().GetIndex() > aCurSel.Max().GetNode()->Len() )
/*N*/ 			{
/*?*/ 				aCurSel.Max().GetIndex() = aCurSel.Max().GetNode()->Len();
/*?*/ 				pView->pImpEditView->SetEditSelection( aCurSel );
/*N*/ 			}
/*N*/ 		}
/*N*/ 	}
/*N*/ 
/*N*/ 	// Loeschen...
/*N*/ 	for ( USHORT n = 0; n < nInvNodes; n++ )
/*N*/ 	{
/*N*/ 		DeletedNodeInfo* pInf = aDeletedNodes.GetObject( n );
/*N*/ 		delete pInf;
/*N*/ 	}
/*N*/ 	aDeletedNodes.Remove( 0, aDeletedNodes.Count() );
/*N*/ }

/*N*/ EditSelection ImpEditEngine::ConvertSelection( USHORT nStartPara, USHORT nStartPos,
/*N*/ 							 USHORT nEndPara, USHORT nEndPos ) const
/*N*/ {
/*N*/ 	EditSelection aNewSelection;
/*N*/ 
/*N*/ 	// Start...
/*N*/ 	ContentNode* pNode = aEditDoc.SaveGetObject( nStartPara );
/*N*/ 	USHORT nIndex = nStartPos;
/*N*/ 	if ( !pNode )
/*N*/ 	{
/*?*/ 		pNode = aEditDoc[ aEditDoc.Count()-1 ];
/*?*/ 		nIndex = pNode->Len();
/*N*/ 	}
/*N*/ 	else if ( nIndex > pNode->Len() )
/*?*/ 		nIndex = pNode->Len();
/*N*/ 
/*N*/ 	aNewSelection.Min().SetNode( pNode );
/*N*/ 	aNewSelection.Min().SetIndex( nIndex );
/*N*/ 
/*N*/ 	// End...
/*N*/ 	pNode = aEditDoc.SaveGetObject( nEndPara );
/*N*/ 	nIndex = nEndPos;
/*N*/ 	if ( !pNode )
/*N*/ 	{
/*?*/ 		pNode = aEditDoc[ aEditDoc.Count()-1 ];
/*?*/ 		nIndex = pNode->Len();
/*N*/ 	}
/*N*/ 	else if ( nIndex > pNode->Len() )
/*?*/ 		nIndex = pNode->Len();
/*N*/ 
/*N*/ 	aNewSelection.Max().SetNode( pNode );
/*N*/ 	aNewSelection.Max().SetIndex( nIndex );
/*N*/ 
/*N*/ 	return aNewSelection;
/*N*/ }



/*N*/ void ImpEditEngine::SetActiveView( EditView* pView )
/*N*/ {
/*N*/ 	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
/*N*/ 	// Eigentlich waere jetzt ein bHasVisSel und HideSelection notwendig !!!
/*N*/ 
/*N*/ 	if ( pView == pActiveView )
/*N*/ 		return;
/*N*/ 
/*N*/ 	if ( pActiveView && pActiveView->HasSelection() )
/*?*/ 		pActiveView->pImpEditView->DrawSelection();	// Wegzeichnen...
/*N*/ 
/*N*/ 	pActiveView = pView;
/*N*/ 
/*N*/ 	if ( pActiveView && pActiveView->HasSelection() )
/*?*/ 		pActiveView->pImpEditView->DrawSelection();	// Wegzeichnen...
/*N*/ 
/*N*/ 	//	NN: Quick fix for #78668#:
/*N*/ 	//	When editing of a cell in Calc is ended, the edit engine is not deleted,
/*N*/ 	//	only the edit views are removed. If mpIMEInfos is still set in that case,
/*N*/ 	//	mpIMEInfos->aPos points to an invalid selection.
/*N*/ 	//	-> reset mpIMEInfos now
/*N*/ 	//	(probably something like this is necessary whenever the content is modified
/*N*/ 	//	from the outside)
/*N*/ 
/*N*/ 	if ( !pView && mpIMEInfos )
/*N*/ 	{
/*?*/ 		delete mpIMEInfos;
/*?*/ 		mpIMEInfos = NULL;
/*N*/ 	}
/*N*/ }



/*N*/ Range ImpEditEngine::GetInvalidYOffsets( ParaPortion* pPortion )
/*N*/ {
/*N*/ 	Range aRange( 0, 0 );
/*N*/ 
/*N*/ 	if ( pPortion->IsVisible() )
/*N*/ 	{
/*N*/ 		const SvxULSpaceItem& rULSpace = (const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
/*N*/ 		const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
/*N*/ 		USHORT nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
/*N*/ 							? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
/*N*/ 
/*N*/ 		// erst von vorne...
/*N*/ 		USHORT nFirstInvalid = 0xFFFF;
/*N*/ 		USHORT nLine;
/*N*/ 		for ( nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
/*N*/ 		{
/*N*/ 			EditLine* pL = pPortion->GetLines().GetObject( nLine );
/*N*/ 			if ( pL->IsInvalid() )
/*N*/ 			{
/*N*/ 				nFirstInvalid = nLine;
/*N*/ 				break;
/*N*/ 			}
/*?*/ 			if ( nLine && !aStatus.IsOutliner() ) 	// nicht die erste Zeile
/*?*/ 				aRange.Min() += nSBL;
/*?*/ 			aRange.Min() += pL->GetHeight();
/*N*/ 		}
/*N*/ 		DBG_ASSERT( nFirstInvalid != 0xFFFF, "Keine ungueltige Zeile gefunden in GetInvalidYOffset(1)" );
/*N*/ 
/*N*/ 
/*N*/ 		// Abgleichen und weiter...
/*N*/ 		aRange.Max() = aRange.Min();
/*N*/ 		aRange.Max() += pPortion->GetFirstLineOffset();
/*N*/ 		if ( nFirstInvalid != 0 )	// Nur wenn nicht die erste Zeile ungueltig
/*?*/ 			aRange.Min() = aRange.Max();
/*N*/ 
/*N*/ 		USHORT nLastInvalid = pPortion->GetLines().Count()-1;
/*N*/ 		for ( nLine = nFirstInvalid; nLine < pPortion->GetLines().Count(); nLine++ )
/*N*/ 		{
/*N*/ 			EditLine* pL = pPortion->GetLines().GetObject( nLine );
/*N*/ 			if ( pL->IsValid() )
/*N*/ 			{
/*?*/ 				nLastInvalid = nLine;
/*?*/ 				break;
/*N*/ 			}
/*N*/ 
/*N*/ 			if ( nLine && !aStatus.IsOutliner() )
/*N*/ 				aRange.Max() += nSBL;
/*N*/ 			aRange.Max() += pL->GetHeight();
/*N*/ 		}
/*N*/ 
/*N*/ 		// MT 07/00 SBL kann jetzt kleiner 100% sein => ggf. die Zeile davor neu ausgeben.
/*N*/ 		if( ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP ) && rLSItem.GetPropLineSpace() &&
/*N*/ 			( rLSItem.GetPropLineSpace() < 100 ) )
/*N*/ 		{
/*?*/ 			EditLine* pL = pPortion->GetLines().GetObject( nFirstInvalid );
/*?*/ 			long n = pL->GetTxtHeight() * ( 100 - rLSItem.GetPropLineSpace() );
/*?*/ 			n /= 100;
/*?*/ 			aRange.Min() -= n;
/*?*/ 			aRange.Max() += n;
/*N*/ 		}
/*N*/ 
/*N*/ 		if ( ( nLastInvalid == pPortion->GetLines().Count()-1 ) && ( !aStatus.IsOutliner() ) )
/*N*/ 			aRange.Max() += GetYValue( rULSpace.GetLower() );
/*N*/ 	}
/*N*/ 	return aRange;
/*N*/ }

/*N*/ EditPaM ImpEditEngine::GetPaM( ParaPortion* pPortion, Point aDocPos, BOOL bSmart )
/*N*/ {
/*N*/ 	DBG_ASSERT( pPortion->IsVisible(), "Wozu GetPaM() bei einem unsichtbaren Absatz?" );
/*N*/ 	DBG_ASSERT( IsFormatted(), "GetPaM: Nicht formatiert" );
/*N*/ 
/*N*/ 	USHORT nCurIndex = 0;
/*N*/ 	EditPaM aPaM;
/*N*/ 	aPaM.SetNode( pPortion->GetNode() );
/*N*/ 
/*N*/ 	const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
/*N*/ 	USHORT nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
/*N*/ 						? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
/*N*/ 
/*N*/ 	long nY = pPortion->GetFirstLineOffset();
/*N*/ 
/*N*/ 	DBG_ASSERT( pPortion->GetLines().Count(), "Leere ParaPortion in GetPaM!" );
/*N*/ 
/*N*/ 	EditLine* pLine = 0;
/*N*/ 	for ( USHORT nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
/*N*/ 	{
/*N*/ 		EditLine* pTmpLine = pPortion->GetLines().GetObject( nLine );
/*N*/ 		nY += pTmpLine->GetHeight();
/*N*/ 		if ( !aStatus.IsOutliner() )
/*N*/ 			nY += nSBL;
/*N*/ 		if ( nY > aDocPos.Y() ) 	// das war 'se
/*N*/ 		{
/*N*/ 			pLine = pTmpLine;
/*N*/ 			break;					// richtige Y-Position intressiert nicht
/*N*/ 		}
/*N*/ 
/*?*/ 		nCurIndex += pTmpLine->GetLen();
/*N*/ 	}
/*N*/ 
/*N*/ 	if ( !pLine ) // darf nur im Bereich von SA passieren!
/*N*/ 	{
/*?*/ 		#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;
/*N*/ 	}
/*N*/ 
/*N*/ 	// Wenn Zeile gefunden, nur noch X-Position => Index
/*N*/ 	nCurIndex = GetChar( pPortion, pLine, aDocPos.X(), bSmart );
/*N*/ 	aPaM.SetIndex( nCurIndex );
/*N*/ 
/*N*/ 	if ( nCurIndex && ( nCurIndex == pLine->GetEnd() ) &&
/*N*/ 		 ( pLine != pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1) ) )
/*N*/     {
/*?*/         DBG_BF_ASSERT(0, "STRIP"); //STRIP001 aPaM = CursorLeft( aPaM, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL );
/*N*/     }
/*N*/ 
/*N*/ 	return aPaM;
/*N*/ }

/*N*/ USHORT ImpEditEngine::GetChar( ParaPortion* pParaPortion, EditLine* pLine, long nXPos, BOOL bSmart )
/*N*/ {
/*N*/ 	DBG_ASSERT( pLine, "Keine Zeile erhalten: GetChar" );
/*N*/ 
/*N*/     USHORT nChar = 0xFFFF;
/*N*/     USHORT nCurIndex = pLine->GetStart();
/*N*/ 
/*N*/ 
/*N*/     // Search best matching portion with GetPortionXOffset()
/*N*/     for ( USHORT i = pLine->GetStartPortion(); i <= pLine->GetEndPortion(); i++ )
/*N*/ 	{
/*N*/ 		TextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( i );
/*N*/         long nXLeft = GetPortionXOffset( pParaPortion, pLine, i );
/*N*/         long nXRight = nXLeft + pPortion->GetSize().Width();
/*N*/         if ( ( nXLeft <= nXPos ) && ( nXRight >= nXPos ) )
/*N*/         {
/*N*/              nChar = nCurIndex;
/*N*/ 
/*N*/             // Search within Portion...
/*N*/ 
/*N*/             // Don't search within special portions...
/*N*/ 			if ( pPortion->GetKind() != PORTIONKIND_TEXT )
/*N*/ 			{
/*?*/                 // ...but check on which side
/*?*/                 if ( bSmart )
/*?*/                 {
/*?*/ 				    long nLeftDiff = nXPos-nXLeft;
/*?*/ 				    long nRightDiff = nXRight-nXPos;
/*?*/ 				    if ( nRightDiff < nLeftDiff )
/*?*/ 					    nChar++;
/*?*/                 }
/*N*/ 			}
/*N*/             else
/*N*/             {
/*N*/ 			    USHORT nMax = pPortion->GetLen();
/*N*/ 			    USHORT nOffset = 0xFFFF;
/*N*/ 			    USHORT nTmpCurIndex = nChar - pLine->GetStart();
/*N*/ 
/*N*/                 long nXInPortion = nXPos - nXLeft;
/*N*/                 if ( pPortion->IsRightToLeft() )
/*?*/                     nXInPortion = nXRight - nXPos;
/*N*/ 
/*N*/                 // Search in Array...
/*N*/ 			    for ( USHORT x = 0; x < nMax; x++ )
/*N*/ 			    {
/*N*/ 				    long nTmpPosMax = pLine->GetCharPosArray().GetObject( nTmpCurIndex+x );
/*N*/ 				    if ( nTmpPosMax > nXInPortion )
/*N*/ 				    {
/*N*/ 					    // pruefen, ob dieser oder der davor...
/*N*/                         long nTmpPosMin = x ? pLine->GetCharPosArray().GetObject( nTmpCurIndex+x-1 ) : 0;
/*N*/ 					    long nDiffLeft = nXInPortion - nTmpPosMin;
/*N*/ 					    long nDiffRight = nTmpPosMax - nXInPortion;
/*N*/ 					    DBG_ASSERT( nDiffLeft >= 0, "DiffLeft negativ" );
/*N*/ 					    DBG_ASSERT( nDiffRight >= 0, "DiffRight negativ" );
/*N*/ 					    nOffset = ( bSmart && ( nDiffRight < nDiffLeft ) ) ? x+1 : x;
/*N*/ 					    // I18N: If there are character position with the length of 0,
/*N*/                         // they belong to the same character, we can not use this position as an index.
/*N*/ 					    // Skip all 0-positions, cheaper than using XBreakIterator:
/*N*/ 					    if ( nOffset < nMax )
/*N*/ 					    {
/*N*/ 						    const long nX = pLine->GetCharPosArray().GetObject(nOffset);
/*N*/ 						    while ( ( (nOffset+1) < nMax ) && ( pLine->GetCharPosArray().GetObject(nOffset+1) == nX ) )
/*N*/ 							    nOffset++;
/*N*/ 					    }
/*N*/ 					    break;
/*N*/ 				    }
/*N*/ 			    }
/*N*/ 
/*N*/ 			    // Bei Verwendung des CharPosArray duerfte es keine Ungenauigkeiten geben!
/*N*/ 			    // Vielleicht bei Kerning ?
/*N*/ 			    // 0xFFF passiert z.B. bei Outline-Font, wenn ganz hinten.
/*N*/ 			    if ( nOffset == 0xFFFF )
/*N*/ 				    nOffset = nMax;
/*N*/ 
/*N*/ 			    DBG_ASSERT( nOffset <= nMax, "nOffset > nMax" );
/*N*/ 
/*N*/                 nChar += nOffset;
/*N*/ 
/*N*/                 // Check if index is within a cell:
/*N*/                 if ( nChar && ( nChar < pParaPortion->GetNode()->Len() ) )
/*N*/                 {
/*N*/                     EditPaM aPaM( pParaPortion->GetNode(), nChar+1 );
/*N*/                     USHORT nScriptType = GetScriptType( aPaM );
/*N*/                     if ( nScriptType == i18n::ScriptType::COMPLEX )
/*N*/                     {
/*?*/ 		                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;
/*?*/                         }
/*N*/                     }
/*N*/                 }
/*N*/             }
/*N*/         }
/*N*/ 
/*N*/         nCurIndex += pPortion->GetLen();
/*N*/     }
/*N*/ 
/*N*/     if ( nChar == 0xFFFF )
/*N*/     {
/*?*/         nChar = ( nXPos <= pLine->GetStartPosX() ) ? pLine->GetStart() : pLine->GetEnd();
/*N*/     }
/*N*/ 
/*N*/     return nChar;
/*N*/ }

/*N*/ Range ImpEditEngine::GetLineXPosStartEnd( ParaPortion* pParaPortion, EditLine* pLine )
/*N*/ {
/*N*/     Range aLineXPosStartEnd;
/*N*/ 
/*N*/     USHORT nPara = GetEditDoc().GetPos( pParaPortion->GetNode() );
/*N*/     if ( !IsRightToLeft( nPara ) )
/*N*/     {
/*N*/         aLineXPosStartEnd.Min() = pLine->GetStartPosX();
/*N*/         aLineXPosStartEnd.Max() = pLine->GetStartPosX() + pLine->GetTextWidth();
/*N*/     }
/*N*/     else
/*N*/     {
/*?*/         aLineXPosStartEnd.Min() = GetPaperSize().Width() - ( pLine->GetStartPosX() + pLine->GetTextWidth() );
/*?*/         aLineXPosStartEnd.Max() = GetPaperSize().Width() - pLine->GetStartPosX();
/*N*/     }
/*N*/ 
/*N*/ 
/*N*/     return aLineXPosStartEnd;
/*N*/ }

/*N*/ long ImpEditEngine::GetPortionXOffset( ParaPortion* pParaPortion, EditLine* pLine, USHORT nTextPortion )
/*N*/ {
/*N*/ 	long nX = pLine->GetStartPosX();
/*N*/ 
/*N*/     for ( USHORT i = pLine->GetStartPortion(); i < nTextPortion; i++ )
/*N*/ 	{
/*N*/ 		TextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( i );
/*N*/ 		switch ( pPortion->GetKind() )
/*N*/ 		{
/*N*/ 			case PORTIONKIND_FIELD:
/*N*/ 			case PORTIONKIND_TEXT:
/*N*/ 			case PORTIONKIND_HYPHENATOR:
/*N*/ 			case PORTIONKIND_TAB:
/*N*/ //	        case PORTIONKIND_EXTRASPACE:
/*N*/ 			{
/*N*/ 				nX += pPortion->GetSize().Width();
/*N*/ 			}
/*N*/ 			break;
/*N*/ 		}
/*N*/     }
/*N*/ 
/*N*/     USHORT nPara = GetEditDoc().GetPos( pParaPortion->GetNode() );
/*N*/     BOOL bR2LPara = IsRightToLeft( nPara );
/*N*/ 
/*N*/     TextPortion* pDestPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
/*N*/     if ( pDestPortion->GetKind() != PORTIONKIND_TAB )
/*N*/     {
/*N*/         if ( !bR2LPara && pDestPortion->GetRightToLeft() )
/*N*/         {
/*?*/             // 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;
/*?*/             }
/*N*/         }
/*N*/         else if ( bR2LPara && !pDestPortion->IsRightToLeft() )
/*N*/         {
/*?*/             // 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;
/*?*/             }
/*N*/         }
/*N*/     }
/*N*/     if ( bR2LPara )
/*N*/     {
/*?*/         // 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();
/*N*/     }
/*N*/ 
/*N*/     return nX;
/*N*/ }


/*N*/ void ImpEditEngine::CalcHeight( ParaPortion* pPortion )
/*N*/ {
/*N*/ 	pPortion->nHeight = 0;
/*N*/ 	pPortion->nFirstLineOffset = 0;
/*N*/ 
/*N*/ 	if ( pPortion->IsVisible() )
/*N*/ 	{
/*N*/ 		DBG_ASSERT( pPortion->GetLines().Count(), "Absatz ohne Zeilen in ParaPortion::CalcHeight" );
/*N*/ 		for ( USHORT nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
/*N*/ 			pPortion->nHeight += pPortion->GetLines().GetObject( nLine )->GetHeight();
/*N*/ 
/*N*/ 		if ( !aStatus.IsOutliner() )
/*N*/ 		{
/*N*/ 			const SvxULSpaceItem& rULItem = (const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
/*N*/ 			const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
/*N*/ 			USHORT nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX ) ? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
/*N*/ 
/*N*/ 			if ( nSBL )
/*N*/ 			{
/*?*/ 				if ( pPortion->GetLines().Count() > 1 )
/*?*/ 					pPortion->nHeight += ( pPortion->GetLines().Count() - 1 ) * nSBL;
/*?*/ 				if ( aStatus.ULSpaceSummation() )
/*?*/ 					pPortion->nHeight += nSBL;
/*N*/ 			}
/*N*/ 
/*N*/ 			USHORT nPortion = GetParaPortions().GetPos( pPortion );
/*N*/ 			if ( nPortion || aStatus.ULSpaceFirstParagraph() )
/*N*/ 			{
/*N*/ 				USHORT nUpper = GetYValue( rULItem.GetUpper() );
/*N*/ 				pPortion->nHeight += nUpper;
/*N*/ 				pPortion->nFirstLineOffset = nUpper;
/*N*/ 			}
/*N*/ 
/*N*/ 			if ( ( nPortion != (GetParaPortions().Count()-1) ) )
/*N*/ 			{
/*N*/ 				pPortion->nHeight += GetYValue( rULItem.GetLower() );	// nicht in letzter
/*N*/ 			}
/*N*/ 
/*N*/ 
/*N*/ 			if ( nPortion && !aStatus.ULSpaceSummation() )
/*N*/ 			{
/*N*/ 				ParaPortion* pPrev = GetParaPortions().SaveGetObject( nPortion-1 );
/*N*/ 				const SvxULSpaceItem& rPrevULItem = (const SvxULSpaceItem&)pPrev->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
/*N*/ 				const SvxLineSpacingItem& rPrevLSItem = (const SvxLineSpacingItem&)pPrev->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
/*N*/ 
/*N*/ 				// Verhalten WinWord6/Writer3:
/*N*/ 				// Bei einem proportionalen Zeilenabstand wird auch der Absatzabstand
/*N*/ 				// manipuliert.
/*N*/ 				// Nur Writer3: Nicht aufaddieren, sondern Mindestabstand.
/*N*/ 
/*N*/ 				// Pruefen, ob Abstand durch LineSpacing > Upper:
/*N*/ 				USHORT nExtraSpace = GetYValue( lcl_CalcExtraSpace( pPortion, rLSItem ) );
/*N*/ 				if ( nExtraSpace > pPortion->nFirstLineOffset )
/*N*/ 				{
/*?*/ 					// Absatz wird 'groesser':
/*?*/ 					pPortion->nHeight += ( nExtraSpace - pPortion->nFirstLineOffset );
/*?*/ 					pPortion->nFirstLineOffset = nExtraSpace;
/*N*/ 				}
/*N*/ 
/*N*/ 				// nFirstLineOffset jetzt f(pNode) => jetzt f(pNode, pPrev) ermitteln:
/*N*/ 				USHORT nPrevLower = GetYValue( rPrevULItem.GetLower() );
/*N*/ 
/*N*/ 				// Dieser PrevLower steckt noch in der Hoehe der PrevPortion...
/*N*/ 				if ( nPrevLower > pPortion->nFirstLineOffset )
/*N*/ 				{
/*N*/ 					// Absatz wird 'kleiner':
/*N*/ 					pPortion->nHeight -= pPortion->nFirstLineOffset;
/*N*/ 					pPortion->nFirstLineOffset = 0;
/*N*/ 				}
/*N*/ 				else if ( nPrevLower )
/*N*/ 				{
/*?*/ 					// Absatz wird 'etwas kleiner':
/*?*/ 					pPortion->nHeight -= nPrevLower;
/*?*/ 					pPortion->nFirstLineOffset -= nPrevLower;
/*N*/ 				}
/*N*/ 
/*N*/ 				// Finde ich zwar nicht so gut, aber Writer3-Feature:
/*N*/ 				// Pruefen, ob Abstand durch LineSpacing > Lower:
/*N*/ 				// Dieser Wert steckt nicht in der Hoehe der PrevPortion.
/*N*/ 				if ( !pPrev->IsInvalid() )
/*N*/ 				{
/*N*/ 					nExtraSpace = GetYValue( lcl_CalcExtraSpace( pPrev, rPrevLSItem ) );
/*N*/ 					if ( nExtraSpace > nPrevLower )
/*N*/ 					{
/*?*/ 						USHORT nMoreLower = nExtraSpace - nPrevLower;
/*?*/ 						// Absatz wird 'groesser', 'waechst' nach unten:
/*?*/ 						if ( nMoreLower > pPortion->nFirstLineOffset )
/*?*/ 						{
/*?*/ 							pPortion->nHeight += ( nMoreLower - pPortion->nFirstLineOffset );
/*?*/ 							pPortion->nFirstLineOffset = nMoreLower;
/*?*/ 						}
/*N*/ 					}
/*N*/ 				}
/*N*/ 			}
/*N*/ 		}
/*N*/ 	}
/*N*/ }


/*N*/ void ImpEditEngine::SetValidPaperSize( const Size& rNewSz )
/*N*/ {
/*N*/ 	aPaperSize = rNewSz;
/*N*/ 
/*N*/ 	long nMinWidth = aStatus.AutoPageWidth() ? aMinAutoPaperSize.Width() : 0;
/*N*/ 	long nMaxWidth = aStatus.AutoPageWidth() ? aMaxAutoPaperSize.Width() : 0x7FFFFFFF;
/*N*/ 	long nMinHeight = aStatus.AutoPageHeight() ? aMinAutoPaperSize.Height() : 0;
/*N*/ 	long nMaxHeight = aStatus.AutoPageHeight() ? aMaxAutoPaperSize.Height() : 0x7FFFFFFF;
/*N*/ 
/*N*/ 	// Minimale/Maximale Breite:
/*N*/ 	if ( aPaperSize.Width() < nMinWidth )
/*N*/ 		aPaperSize.Width() = nMinWidth;
/*N*/ 	else if ( aPaperSize.Width() > nMaxWidth )
/*?*/ 		aPaperSize.Width() = nMaxWidth;
/*N*/ 
/*N*/ 	// Minimale/Maximale Hoehe:
/*N*/ 	if ( aPaperSize.Height() < nMinHeight )
/*?*/ 		aPaperSize.Height() = nMinHeight;
/*N*/ 	else if ( aPaperSize.Height() > nMaxHeight )
/*?*/ 		aPaperSize.Height() = nMaxHeight;
/*N*/ }


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

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

/*N*/ svtools::ColorConfig& ImpEditEngine::GetColorConfig()
/*N*/ {
/*N*/     if ( !pColorConfig )
/*N*/         pColorConfig = new svtools::ColorConfig;
/*N*/ 
/*N*/     return *pColorConfig;
/*N*/ }





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

/*N*/ void ImpEditEngine::LeaveBlockNotifications()
/*N*/ {
/*N*/     DBG_ASSERT( nBlockNotifications, "LeaveBlockNotifications - Why?" );
/*N*/ 
/*N*/     nBlockNotifications--;
/*N*/     if ( !nBlockNotifications )
/*N*/     {
/*N*/         // Call blocked notify events...
/*N*/         while ( aNotifyCache.Count() )
/*N*/         {
/*N*/             EENotify* pNotify = aNotifyCache[0];
/*N*/             // Remove from list before calling, maybe we enter LeaveBlockNotifications while calling the handler...
/*N*/             aNotifyCache.Remove( 0 );
/*N*/             GetNotifyHdl().Call( pNotify );
/*N*/             delete pNotify;
/*N*/         }
/*N*/ 
/*N*/         EENotify aNotify( EE_NOTIFY_BLOCKNOTIFICATION_END );
/*N*/         aNotify.pEditEngine = GetEditEnginePtr();
/*N*/         GetNotifyHdl().Call( &aNotify );
/*N*/     }
/*N*/ }

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