Files
libreoffice/svtools/source/edit/texteng.cxx

3114 lines
107 KiB
C++
Raw Normal View History

2000-09-18 16:07:07 +00:00
/*************************************************************************
*
* $RCSfile: texteng.cxx,v $
*
* $Revision: 1.24 $
2000-09-18 16:07:07 +00:00
*
* last change: $Author: mt $ $Date: 2002-11-20 15:43:58 $
2000-09-18 16:07:07 +00:00
*
* The Contents of this file are made available subject to the terms of
* either of the following licenses
*
* - GNU Lesser General Public License Version 2.1
* - Sun Industry Standards Source License Version 1.1
*
* Sun Microsystems Inc., October, 2000
*
* GNU Lesser General Public License Version 2.1
* =============================================
* Copyright 2000 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
*
*
* Sun Industry Standards Source License Version 1.1
* =================================================
* The contents of this file are subject to the Sun Industry Standards
* Source License Version 1.1 (the "License"); You may not use this file
* except in compliance with the License. You may obtain a copy of the
* License at http://www.openoffice.org/license.html.
*
* Software provided under this License is provided on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
* WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
* MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
* See the License for the specific provisions governing your rights and
* obligations concerning the Software.
*
* The Initial Developer of the Original Code is: Sun Microsystems, Inc.
*
* Copyright: 2000 by Sun Microsystems, Inc.
*
* All Rights Reserved.
*
* Contributor(s): _______________________________________
*
*
************************************************************************/
#ifndef _STREAM_HXX //autogen
#include <tools/stream.hxx>
#endif
#include <texteng.hxx>
#include <textview.hxx>
#include <textdoc.hxx>
#include <textdat2.hxx>
#include <textundo.hxx>
#include <textund2.hxx>
#include <tools/isolang.hxx>
#ifndef _SV_WINDOW_HXX //autogen
#include <vcl/window.hxx>
#endif
#include <vcl/edit.hxx>
#ifndef _COM_SUN_STAR_LANG_XMULTISERVICEFACTORY_HPP_
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#endif
#ifndef _COM_SUN_STAR_BEANS_PROPERTYVALUES_HPP_
#include <com/sun/star/beans/PropertyValues.hpp>
#endif
2000-09-18 16:07:07 +00:00
#ifndef _COM_SUN_STAR_TEXT_XBREAKITERATOR_HPP_
#include <com/sun/star/i18n/XBreakIterator.hpp>
2000-09-18 16:07:07 +00:00
#endif
#ifndef _COM_SUN_STAR_TEXT_CHARACTERITERATORMODE_HPP_
#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
2000-09-18 16:07:07 +00:00
#endif
#ifndef _COM_SUN_STAR_TEXT_WORDTYPE_HPP_
#include <com/sun/star/i18n/WordType.hpp>
2000-09-18 16:07:07 +00:00
#endif
2002-10-17 16:16:28 +00:00
#include <unotools/localedatawrapper.hxx>
#include <vcl/unohelp.hxx>
2000-10-13 08:55:28 +00:00
#include <vcl/svapp.hxx>
2000-09-18 16:07:07 +00:00
#include <vcl/unohelp.hxx>
#include <vcl/metric.hxx>
2002-08-12 14:36:21 +00:00
#include <unicode/ubidi.h>
2000-09-18 16:07:07 +00:00
using namespace ::com::sun::star;
typedef TextView* TextViewPtr;
SV_DECL_PTRARR( TextViews, TextViewPtr, 0, 1 );
// SV_IMPL_PTRARR( TextViews, TextViewPtr );
SV_DECL_VARARR_SORT( TESortedPositions, ULONG, 16, 8 );
SV_IMPL_VARARR_SORT( TESortedPositions, ULONG );
#define RESDIFF 10
#define SCRLRANGE 20 // 1/20 der Breite/Hoehe scrollen, wenn im QueryDrop
// -------------------------------------------------------------------------
// (-) class TextEngine
// -------------------------------------------------------------------------
2002-10-17 16:16:28 +00:00
TextEngine::TextEngine()
2000-09-18 16:07:07 +00:00
{
mpDoc = 0;
mpTEParaPortions = 0;
mpViews = new TextViews;
mpActiveView = NULL;
mbIsFormatting = FALSE;
mbFormatted = FALSE;
mbUpdate = TRUE;
mbModified = FALSE;
mbUndoEnabled = FALSE;
mbIsInUndo = FALSE;
mbDowning = FALSE;
2002-08-12 14:36:21 +00:00
mbRightToLeft = FALSE;
2000-09-18 16:07:07 +00:00
meAlign = TXTALIGN_LEFT;
mnMaxTextWidth = 0;
mnMaxTextLen = 0;
mnCurTextHeight = 0;
2000-10-13 08:55:28 +00:00
mpUndoManager = NULL;
2002-08-12 14:36:21 +00:00
mpIMEInfos = NULL;
2002-10-17 16:16:28 +00:00
mpLocaleDataWrapper = NULL;
2000-09-18 16:07:07 +00:00
mpIdleFormatter = new IdleFormatter;
mpRefDev = new VirtualDevice;
2002-08-12 14:36:21 +00:00
ImpInitLayoutMode( mpRefDev );
2000-09-18 16:07:07 +00:00
ImpInitDoc();
Font aFont;
aFont.SetTransparent( FALSE );
Color aFillColor( aFont.GetFillColor() );
aFillColor.SetTransparency( 0 );
aFont.SetFillColor( aFillColor );
SetFont( aFont );
}
TextEngine::~TextEngine()
{
mbDowning = TRUE;
delete mpIdleFormatter;
delete mpDoc;
delete mpTEParaPortions;
delete mpViews; // nur die Liste, nicht die Vies
delete mpRefDev;
delete mpUndoManager;
2000-10-13 08:55:28 +00:00
delete mpIMEInfos;
2002-10-17 16:16:28 +00:00
delete mpLocaleDataWrapper;
2000-09-18 16:07:07 +00:00
}
void TextEngine::InsertView( TextView* pTextView )
{
mpViews->Insert( pTextView, mpViews->Count() );
pTextView->SetSelection( TextSelection() );
if ( !GetActiveView() )
SetActiveView( pTextView );
}
void TextEngine::RemoveView( TextView* pTextView )
{
USHORT nPos = mpViews->GetPos( pTextView );
if( nPos != USHRT_MAX )
{
pTextView->HideCursor();
mpViews->Remove( nPos, 1 );
if ( pTextView == GetActiveView() )
SetActiveView( 0 );
}
}
USHORT TextEngine::GetViewCount() const
{
return mpViews->Count();
}
TextView* TextEngine::GetView( USHORT nView ) const
{
return mpViews->GetObject( nView );
}
TextView* TextEngine::GetActiveView() const
{
return mpActiveView;
}
void TextEngine::SetActiveView( TextView* pTextView )
{
if ( pTextView != mpActiveView )
{
if ( mpActiveView )
mpActiveView->HideSelection();
mpActiveView = pTextView;
if ( mpActiveView )
mpActiveView->ShowSelection();
}
}
void TextEngine::SetFont( const Font& rFont )
{
if ( rFont != maFont )
{
maFont = rFont;
// Wegen Selektion keinen Transparenten Font zulassen...
// (Sonst spaeter in ImplPaint den Hintergrund anders loeschen...)
maFont.SetTransparent( FALSE );
Color aFillColor( maFont.GetFillColor() );
aFillColor.SetTransparency( 0 );
maFont.SetFillColor( aFillColor );
maFont.SetAlign( ALIGN_TOP );
mpRefDev->SetFont( maFont );
Size aTextSize;
aTextSize.Width() = mpRefDev->GetTextWidth( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( " " ) ) );
aTextSize.Height() = mpRefDev->GetTextHeight();
if ( !aTextSize.Width() )
aTextSize.Width() = mpRefDev->GetTextWidth( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "XXXX" ) ) );
mnDefTab = (USHORT)aTextSize.Width();
if ( !mnDefTab )
mnDefTab = 1;
mnCharHeight = (USHORT)aTextSize.Height();
/*
// #93746# Doesn't work with CJK HalfWidth/FullWidth
FontMetric aRealFont( mpRefDev->GetFontMetric() );
if ( aRealFont.GetPitch() == PITCH_FIXED )
2000-09-18 16:07:07 +00:00
{
String aX100;
aX100.Fill( 100, 'X' );
mnFixCharWidth100 = (USHORT)mpRefDev->GetTextWidth( aX100 );
}
else
*/
mnFixCharWidth100 = 0;
2000-09-18 16:07:07 +00:00
FormatFullDoc();
2002-09-09 14:17:49 +00:00
UpdateViews();
for ( USHORT nView = mpViews->Count(); nView; )
{
TextView* pView = mpViews->GetObject( --nView );
pView->GetWindow()->SetInputContext( InputContext( GetFont(), !pView->IsReadOnly() ? INPUTCONTEXT_TEXT|INPUTCONTEXT_EXTTEXTINPUT : 0 ) );
}
2000-09-18 16:07:07 +00:00
}
}
void TextEngine::SetDefTab( USHORT nDefTab )
{
mnDefTab = nDefTab;
// evtl neu setzen?
}
void TextEngine::SetMaxTextLen( ULONG nLen )
{
mnMaxTextLen = nLen;
}
void TextEngine::SetMaxTextWidth( ULONG nMaxWidth )
{
if ( nMaxWidth != mnMaxTextWidth )
{
mnMaxTextWidth = Min( nMaxWidth, (ULONG)0x7FFFFFFF );
FormatFullDoc();
UpdateViews();
}
}
static sal_Unicode static_aLFText[] = { '\n', 0 };
static sal_Unicode static_aCRText[] = { '\r', 0 };
static sal_Unicode static_aCRLFText[] = { '\r', '\n', 0 };
static inline const sal_Unicode* static_getLineEndText( LineEnd aLineEnd )
{
const sal_Unicode* pRet = NULL;
switch( aLineEnd )
{
case LINEEND_LF: pRet = static_aLFText;break;
case LINEEND_CR: pRet = static_aCRText;break;
case LINEEND_CRLF: pRet = static_aCRLFText;break;
}
return pRet;
}
String TextEngine::GetText( LineEnd aSeparator ) const
{
return mpDoc->GetText( static_getLineEndText( aSeparator ) );
}
String TextEngine::GetTextLines( LineEnd aSeparator ) const
{
String aText;
ULONG nParas = mpTEParaPortions->Count();
const sal_Unicode* pSep = static_getLineEndText( aSeparator );
for ( ULONG nP = 0; nP < nParas; nP++ )
{
TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nP );
USHORT nLines = pTEParaPortion->GetLines().Count();
for ( USHORT nL = 0; nL < nLines; nL++ )
{
TextLine* pLine = pTEParaPortion->GetLines()[nL];
aText += pTEParaPortion->GetNode()->GetText().Copy( pLine->GetStart(), pLine->GetEnd() - pLine->GetStart() );
if ( pSep && ( ( (nP+1) < nParas ) || ( (nL+1) < nLines ) ) )
aText += pSep;
}
}
return aText;
}
String TextEngine::GetText( ULONG nPara ) const
{
return mpDoc->GetText( nPara );
}
ULONG TextEngine::GetTextLen( LineEnd aSeparator ) const
{
return mpDoc->GetTextLen( static_getLineEndText( aSeparator ) );
}
ULONG TextEngine::GetTextLen( const TextSelection& rSel, LineEnd aSeparator ) const
{
TextSelection aSel( rSel );
aSel.Justify();
ValidateSelection( aSel );
return mpDoc->GetTextLen( static_getLineEndText( aSeparator ), &aSel );
}
USHORT TextEngine::GetTextLen( ULONG nPara ) const
{
return mpDoc->GetNodes().GetObject( nPara )->GetText().Len();
}
void TextEngine::SetUpdateMode( BOOL bUpdate )
{
if ( bUpdate != mbUpdate )
{
mbUpdate = bUpdate;
if ( mbUpdate )
{
FormatAndUpdate( GetActiveView() );
if ( GetActiveView() )
GetActiveView()->ShowCursor();
}
}
}
void TextEngine::SetWordDelimiters( const String& rDelimiters )
{
2002-10-17 16:16:28 +00:00
// Only used in basic project, should be removed (I18N)
DBG_ERROR( "TextEngine::SetWordDelimiters is not longer supported, since we have I18N" );
2000-09-18 16:07:07 +00:00
}
BOOL TextEngine::DoesKeyMoveCursor( const KeyEvent& rKeyEvent )
{
BOOL bDoesMove = FALSE;
switch ( rKeyEvent.GetKeyCode().GetCode() )
{
case KEY_UP:
case KEY_DOWN:
case KEY_LEFT:
case KEY_RIGHT:
case KEY_HOME:
case KEY_END:
case KEY_PAGEUP:
case KEY_PAGEDOWN:
{
if ( !rKeyEvent.GetKeyCode().IsMod2() )
bDoesMove = TRUE;
}
break;
}
return bDoesMove;
}
BOOL TextEngine::DoesKeyChangeText( const KeyEvent& rKeyEvent )
{
BOOL bDoesChange = FALSE;
KeyFuncType eFunc = rKeyEvent.GetKeyCode().GetFunction();
if ( eFunc != KEYFUNC_DONTKNOW )
{
switch ( eFunc )
{
case KEYFUNC_UNDO:
case KEYFUNC_REDO:
case KEYFUNC_CUT:
case KEYFUNC_PASTE: bDoesChange = TRUE;
break;
default: // wird dann evtl. unten bearbeitet.
eFunc = KEYFUNC_DONTKNOW;
}
}
if ( eFunc == KEYFUNC_DONTKNOW )
{
switch ( rKeyEvent.GetKeyCode().GetCode() )
{
case KEY_DELETE:
case KEY_BACKSPACE:
{
if ( !rKeyEvent.GetKeyCode().IsMod2() )
bDoesChange = TRUE;
}
break;
case KEY_RETURN:
case KEY_TAB:
{
if ( !rKeyEvent.GetKeyCode().IsMod1() && !rKeyEvent.GetKeyCode().IsMod2() )
bDoesChange = TRUE;
}
break;
default:
{
bDoesChange = TextEngine::IsSimpleCharInput( rKeyEvent );
}
}
}
return bDoesChange;
}
BOOL TextEngine::IsSimpleCharInput( const KeyEvent& rKeyEvent )
{
if( rKeyEvent.GetCharCode() >= 32 && rKeyEvent.GetCharCode() != 127 &&
#ifndef MAC
KEY_MOD2 != (rKeyEvent.GetKeyCode().GetModifier() & ~KEY_SHIFT ) )
#else
KEY_MOD1 != rKeyEvent.GetKeyCode().GetModifier() )
#endif
{
return TRUE;
}
return FALSE;
}
void TextEngine::ImpInitDoc()
{
if ( mpDoc )
mpDoc->Clear();
else
mpDoc = new TextDoc;
delete mpTEParaPortions;
mpTEParaPortions = new TEParaPortions;
TextNode* pNode = new TextNode( String() );
mpDoc->GetNodes().Insert( pNode );
TEParaPortion* pIniPortion = new TEParaPortion( pNode );
mpTEParaPortions->Insert( pIniPortion, (ULONG)0 );
mbFormatted = FALSE;
ImpParagraphRemoved( TEXT_PARA_ALL );
ImpParagraphInserted( 0 );
}
String TextEngine::GetText( const TextSelection& rSel, LineEnd aSeparator ) const
{
String aText;
if ( !rSel.HasRange() )
return aText;
TextSelection aSel( rSel );
aSel.Justify();
ULONG nStartPara = aSel.GetStart().GetPara();
ULONG nEndPara = aSel.GetEnd().GetPara();
const sal_Unicode* pSep = static_getLineEndText( aSeparator );
for ( ULONG nNode = aSel.GetStart().GetPara(); nNode <= nEndPara; nNode++ )
{
TextNode* pNode = mpDoc->GetNodes().GetObject( nNode );
USHORT nStartPos = 0;
USHORT nEndPos = pNode->GetText().Len();
if ( nNode == nStartPara )
nStartPos = aSel.GetStart().GetIndex();
if ( nNode == nEndPara ) // kann auch == nStart sein!
nEndPos = aSel.GetEnd().GetIndex();
aText += pNode->GetText().Copy( nStartPos, nEndPos-nStartPos );
if ( nNode < nEndPara )
aText += pSep;
}
return aText;
}
void TextEngine::ImpRemoveText()
{
ImpInitDoc();
TextPaM aStartPaM( 0, 0 );
TextSelection aEmptySel( aStartPaM, aStartPaM );
for ( USHORT nView = 0; nView < mpViews->Count(); nView++ )
{
TextView* pView = mpViews->GetObject( nView );
2002-08-23 11:34:04 +00:00
pView->ImpSetSelection( aEmptySel );
2000-09-18 16:07:07 +00:00
}
ResetUndo();
}
void TextEngine::SetText( const XubString& rText )
{
ImpRemoveText();
BOOL bUndoCurrentlyEnabled = IsUndoEnabled();
// Der von Hand reingesteckte Text kann nicht vom Anwender rueckgaengig gemacht werden.
EnableUndo( FALSE );
TextPaM aStartPaM( 0, 0 );
TextSelection aEmptySel( aStartPaM, aStartPaM );
TextPaM aPaM = aStartPaM;
if ( rText.Len() )
aPaM = ImpInsertText( aEmptySel, rText );
for ( USHORT nView = 0; nView < mpViews->Count(); nView++ )
{
TextView* pView = mpViews->GetObject( nView );
2002-08-23 11:34:04 +00:00
pView->ImpSetSelection( aEmptySel );
2000-09-18 16:07:07 +00:00
// Wenn kein Text, dann auch Kein Format&Update
// => Der Text bleibt stehen.
if ( !rText.Len() && GetUpdateMode() )
pView->Invalidate();
}
if( !rText.Len() ) // sonst muss spaeter noch invalidiert werden, !bFormatted reicht.
mnCurTextHeight = 0;
FormatAndUpdate();
EnableUndo( bUndoCurrentlyEnabled );
DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "Undo nach SetText?" );
}
void TextEngine::CursorMoved( ULONG nNode )
{
// Leere Attribute loeschen, aber nur, wenn Absatz nicht leer!
TextNode* pNode = mpDoc->GetNodes().GetObject( nNode );
if ( pNode && pNode->GetCharAttribs().HasEmptyAttribs() && pNode->GetText().Len() )
pNode->GetCharAttribs().DeleteEmptyAttribs();
}
void TextEngine::ImpRemoveChars( const TextPaM& rPaM, USHORT nChars, SfxUndoAction* pCurUndo )
{
DBG_ASSERT( nChars, "ImpRemoveChars - 0 Chars?!" );
if ( IsUndoEnabled() && !IsInUndo() )
{
// Attribute muessen hier vorm RemoveChars fuer UNDO gesichert werden!
TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
XubString aStr( pNode->GetText().Copy( rPaM.GetIndex(), nChars ) );
// Pruefen, ob Attribute geloescht oder geaendert werden:
USHORT nStart = rPaM.GetIndex();
USHORT nEnd = nStart + nChars;
for ( USHORT nAttr = pNode->GetCharAttribs().Count(); nAttr; )
{
TextCharAttrib* pAttr = pNode->GetCharAttribs().GetAttrib( --nAttr );
if ( ( pAttr->GetEnd() >= nStart ) && ( pAttr->GetStart() < nEnd ) )
{
// TextSelection aSel( rPaM );
// aSel.GetEnd().GetIndex() += nChars;
// TextUndoSetAttribs* pAttrUndo = CreateAttribUndo( aSel );
// InsertUndo( pAttrUndo );
break; // for
}
}
// if ( pCurUndo && ( CreateTextPaM( pCurUndo->GetEPaM() ) == rPaM ) )
// pCurUndo->GetStr() += aStr;
// else
InsertUndo( new TextUndoRemoveChars( this, rPaM, aStr ) );
}
mpDoc->RemoveChars( rPaM, nChars );
ImpCharsRemoved( rPaM.GetPara(), rPaM.GetIndex(), nChars );
}
TextPaM TextEngine::ImpConnectParagraphs( ULONG nLeft, ULONG nRight )
{
DBG_ASSERT( nLeft != nRight, "Den gleichen Absatz zusammenfuegen ?" );
TextNode* pLeft = mpDoc->GetNodes().GetObject( nLeft );
TextNode* pRight = mpDoc->GetNodes().GetObject( nRight );
if ( IsUndoEnabled() && !IsInUndo() )
InsertUndo( new TextUndoConnectParas( this, nLeft, pLeft->GetText().Len() ) );
// Erstmal Portions suchen, da pRight nach ConnectParagraphs weg.
TEParaPortion* pLeftPortion = mpTEParaPortions->GetObject( nLeft );
TEParaPortion* pRightPortion = mpTEParaPortions->GetObject( nRight );
DBG_ASSERT( pLeft && pLeftPortion, "Blinde Portion in ImpConnectParagraphs(1)" );
DBG_ASSERT( pRight && pRightPortion, "Blinde Portion in ImpConnectParagraphs(2)" );
TextPaM aPaM = mpDoc->ConnectParagraphs( pLeft, pRight );
ImpParagraphRemoved( nRight );
pLeftPortion->MarkSelectionInvalid( aPaM.GetIndex(), pLeft->GetText().Len() );
mpTEParaPortions->Remove( nRight );
delete pRightPortion;
// der rechte Node wird von EditDoc::ConnectParagraphs() geloescht.
return aPaM;
}
TextPaM TextEngine::ImpDeleteText( const TextSelection& rSel )
{
if ( !rSel.HasRange() )
return rSel.GetStart();
TextSelection aSel( rSel );
aSel.Justify();
TextPaM aStartPaM( aSel.GetStart() );
TextPaM aEndPaM( aSel.GetEnd() );
CursorMoved( aStartPaM.GetPara() ); // nur damit neu eingestellte Attribute verschwinden...
CursorMoved( aEndPaM.GetPara() ); // nur damit neu eingestellte Attribute verschwinden...
DBG_ASSERT( mpDoc->IsValidPaM( aStartPaM ), "Index im Wald in ImpDeleteText" )
DBG_ASSERT( mpDoc->IsValidPaM( aEndPaM ), "Index im Wald in ImpDeleteText" )
ULONG nStartNode = aStartPaM.GetPara();
ULONG nEndNode = aEndPaM.GetPara();
// Alle Nodes dazwischen entfernen....
for ( ULONG z = nStartNode+1; z < nEndNode; z++ )
{
// Immer nStartNode+1, wegen Remove()!
ImpRemoveParagraph( nStartNode+1 );
}
if ( nStartNode != nEndNode )
{
// Den Rest des StartNodes...
TextNode* pLeft = mpDoc->GetNodes().GetObject( nStartNode );
USHORT nChars = pLeft->GetText().Len() - aStartPaM.GetIndex();
if ( nChars )
{
ImpRemoveChars( aStartPaM, nChars );
TEParaPortion* pPortion = mpTEParaPortions->GetObject( nStartNode );
DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(3)" );
pPortion->MarkSelectionInvalid( aStartPaM.GetIndex(), pLeft->GetText().Len() );
}
// Den Anfang des EndNodes....
nEndNode = nStartNode+1; // Die anderen Absaetze wurden geloescht
nChars = aEndPaM.GetIndex();
if ( nChars )
{
aEndPaM.GetPara() = nEndNode;
aEndPaM.GetIndex() = 0;
ImpRemoveChars( aEndPaM, nChars );
TEParaPortion* pPortion = mpTEParaPortions->GetObject( nEndNode );
DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(4)" );
pPortion->MarkSelectionInvalid( 0, pPortion->GetNode()->GetText().Len() );
}
// Zusammenfuegen....
aStartPaM = ImpConnectParagraphs( nStartNode, nEndNode );
}
else
{
USHORT nChars;
nChars = aEndPaM.GetIndex() - aStartPaM.GetIndex();
ImpRemoveChars( aStartPaM, nChars );
TEParaPortion* pPortion = mpTEParaPortions->GetObject( nStartNode );
DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(5)" );
pPortion->MarkInvalid( aEndPaM.GetIndex(), aStartPaM.GetIndex() - aEndPaM.GetIndex() );
}
// UpdateSelections();
TextModified();
return aStartPaM;
}
void TextEngine::ImpRemoveParagraph( ULONG nPara )
{
TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
// Der Node wird vom Undo verwaltet und ggf. zerstoert!
/* delete */ mpDoc->GetNodes().Remove( nPara );
if ( IsUndoEnabled() && !IsInUndo() )
InsertUndo( new TextUndoDelPara( this, pNode, nPara ) );
else
delete pNode;
mpTEParaPortions->Remove( nPara );
delete pPortion;
ImpParagraphRemoved( nPara );
}
TextPaM TextEngine::ImpInsertText( const TextSelection& rCurSel, sal_Unicode c, BOOL bOverwrite )
{
DBG_ASSERT( c != '\n', "Zeilenumbruch bei InsertText ?" );
DBG_ASSERT( c != '\r', "Zeilenumbruch bei InsertText ?" );
TextPaM aPaM( rCurSel.GetStart() );
TextNode* pNode = mpDoc->GetNodes().GetObject( aPaM.GetPara() );
if ( pNode->GetText().Len() < STRING_MAXLEN )
{
BOOL bDoOverwrite = ( bOverwrite &&
( aPaM.GetIndex() < pNode->GetText().Len() ) ) ? TRUE : FALSE;
BOOL bUndoAction = ( rCurSel.HasRange() || bDoOverwrite );
if ( bUndoAction )
UndoActionStart( TEXTUNDO_INSERT );
if ( rCurSel.HasRange() )
{
aPaM = ImpDeleteText( rCurSel );
}
else if ( bDoOverwrite )
{
// Wenn Selektion, dann kein Zeichen ueberschreiben
TextSelection aTmpSel( aPaM );
aTmpSel.GetEnd().GetIndex()++;
ImpDeleteText( aTmpSel );
}
if ( IsUndoEnabled() && !IsInUndo() )
{
TextUndoInsertChars* pNewUndo = new TextUndoInsertChars( this, aPaM, c );
BOOL bTryMerge = ( !bDoOverwrite && ( c != ' ' ) ) ? TRUE : FALSE;
InsertUndo( pNewUndo, bTryMerge );
}
TEParaPortion* pPortion = mpTEParaPortions->GetObject( aPaM.GetPara() );
pPortion->MarkInvalid( aPaM.GetIndex(), 1 );
if ( c == '\t' )
pPortion->SetNotSimpleInvalid();
aPaM = mpDoc->InsertText( aPaM, c );
ImpCharsInserted( aPaM.GetPara(), aPaM.GetIndex()-1, 1 );
TextModified();
if ( bUndoAction )
UndoActionEnd( TEXTUNDO_INSERT );
}
return aPaM;
}
TextPaM TextEngine::ImpInsertText( const TextSelection& rCurSel, const XubString& rStr )
{
TextPaM aPaM;
if ( rCurSel.HasRange() )
aPaM = ImpDeleteText( rCurSel );
else
aPaM = rCurSel.GetEnd();
TextPaM aCurPaM( aPaM ); // fuers Invalidieren
XubString aText( rStr );
aText.ConvertLineEnd( LINEEND_LF );
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 )
{
ULONG nL = aPaM.GetIndex();
nL += ( nEnd-nStart );
if ( nL > STRING_MAXLEN )
{
USHORT nDiff = (USHORT) (nL-STRING_MAXLEN);
nEnd -= nDiff;
}
XubString aLine( aText, nStart, nEnd-nStart );
if ( IsUndoEnabled() && !IsInUndo() )
InsertUndo( new TextUndoInsertChars( this, aPaM, aLine ) );
TEParaPortion* pPortion = mpTEParaPortions->GetObject( aPaM.GetPara() );
pPortion->MarkInvalid( aPaM.GetIndex(), aLine.Len() );
if ( aLine.Search( '\t' ) != STRING_NOTFOUND )
pPortion->SetNotSimpleInvalid();
aPaM = mpDoc->InsertText( aPaM, aLine );
ImpCharsInserted( aPaM.GetPara(), aPaM.GetIndex()-aLine.Len(), aLine.Len() );
}
if ( nEnd < aText.Len() )
aPaM = ImpInsertParaBreak( aPaM );
nStart = nEnd+1;
}
TextModified();
return aPaM;
}
TextPaM TextEngine::ImpInsertParaBreak( const TextSelection& rCurSel, BOOL bKeepEndingAttribs )
{
TextPaM aPaM;
if ( rCurSel.HasRange() )
aPaM = ImpDeleteText( rCurSel );
else
aPaM = rCurSel.GetEnd();
return ImpInsertParaBreak( aPaM, bKeepEndingAttribs );
}
TextPaM TextEngine::ImpInsertParaBreak( const TextPaM& rPaM, BOOL bKeepEndingAttribs )
{
if ( IsUndoEnabled() && !IsInUndo() )
InsertUndo( new TextUndoSplitPara( this, rPaM.GetPara(), rPaM.GetIndex() ) );
TextPaM aPaM( mpDoc->InsertParaBreak( rPaM, bKeepEndingAttribs ) );
TEParaPortion* pPortion = mpTEParaPortions->GetObject( rPaM.GetPara() );
DBG_ASSERT( pPortion, "Blinde Portion in ImpInsertParaBreak" );
pPortion->MarkInvalid( rPaM.GetIndex(), 0 );
TextNode* pNewNode = mpDoc->GetNodes().GetObject( aPaM.GetPara() );
TEParaPortion* pNewPortion = new TEParaPortion( pNewNode );
mpTEParaPortions->Insert( pNewPortion, aPaM.GetPara() );
ImpParagraphInserted( aPaM.GetPara() );
CursorMoved( rPaM.GetPara() ); // falls leeres Attribut entstanden.
TextModified();
return aPaM;
}
Rectangle TextEngine::PaMtoEditCursor( const TextPaM& rPaM, BOOL bSpecial )
{
DBG_ASSERT( GetUpdateMode(), "Darf bei Update=FALSE nicht erreicht werden: PaMtoEditCursor" );
Rectangle aEditCursor;
long nY = 0;
for ( ULONG nPortion = 0; nPortion < rPaM.GetPara(); nPortion++ )
{
TEParaPortion* pPortion = mpTEParaPortions->GetObject(nPortion);
nY += pPortion->GetLines().Count() * mnCharHeight;
}
aEditCursor = GetEditCursor( rPaM, bSpecial );
aEditCursor.Top() += nY;
aEditCursor.Bottom() += nY;
return aEditCursor;
}
2002-08-15 13:26:09 +00:00
Rectangle TextEngine::GetEditCursor( const TextPaM& rPaM, BOOL bSpecial, BOOL bPreferPortionStart )
2000-09-18 16:07:07 +00:00
{
2002-08-23 11:34:04 +00:00
if ( !IsFormatted() && !IsFormatting() )
FormatDoc();
2000-09-18 16:07:07 +00:00
TEParaPortion* pPortion = mpTEParaPortions->GetObject( rPaM.GetPara() );
TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
/*
bSpecial: 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 = 0;
USHORT nCurIndex = 0;
TextLine* pLine = 0;
for ( USHORT nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
{
TextLine* pTmpLine = pPortion->GetLines().GetObject( nLine );
if ( ( pTmpLine->GetStart() == rPaM.GetIndex() ) || ( pTmpLine->IsIn( rPaM.GetIndex(), bSpecial ) ) )
{
pLine = pTmpLine;
break;
}
nCurIndex += pTmpLine->GetLen();
nY += mnCharHeight;
}
if ( !pLine )
{
// Cursor am Ende des Absatzes.
DBG_ASSERT( rPaM.GetIndex() == nCurIndex, "Index voll daneben in GetEditCursor!" );
pLine = pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1 );
nY -= mnCharHeight;
nCurIndex -= pLine->GetLen();
}
Rectangle aEditCursor;
aEditCursor.Top() = nY;
nY += mnCharHeight;
aEditCursor.Bottom() = nY-1;
// innerhalb der Zeile suchen....
2002-08-15 13:26:09 +00:00
long nX = ImpGetXPos( rPaM.GetPara(), pLine, rPaM.GetIndex(), bPreferPortionStart );
2002-08-12 14:36:21 +00:00
aEditCursor.Left() = aEditCursor.Right() = nX;
return aEditCursor;
}
long TextEngine::ImpGetXPos( ULONG nPara, TextLine* pLine, USHORT nIndex, BOOL bPreferPortionStart )
{
DBG_ASSERT( ( nIndex >= pLine->GetStart() ) && ( nIndex <= pLine->GetEnd() ) , "ImpGetXPos 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;
TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
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! " );
TETextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
long nX = ImpGetPortionXOffset( nPara, pLine, nTextPortion );
long nPortionTextWidth = pPortion->GetWidth();
if ( nTextPortionStart != nIndex )
2000-09-18 16:07:07 +00:00
{
2002-08-12 14:36:21 +00:00
// Search within portion...
if ( nIndex == ( nTextPortionStart + pPortion->GetLen() ) )
{
// End of Portion
2002-08-13 13:07:14 +00:00
if ( ( pPortion->GetKind() == PORTIONKIND_TAB ) ||
( !IsRightToLeft() && !pPortion->IsRightToLeft() ) ||
2002-08-12 14:36:21 +00:00
( IsRightToLeft() && pPortion->IsRightToLeft() ) )
2002-08-13 13:07:14 +00:00
{
2002-08-12 14:36:21 +00:00
nX += nPortionTextWidth;
2002-08-13 13:07:14 +00:00
if ( ( pPortion->GetKind() == PORTIONKIND_TAB ) && ( (nTextPortion+1) < pParaPortion->GetTextPortions().Count() ) )
{
TETextPortion* pNextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion+1 );
2002-08-14 12:09:49 +00:00
if ( ( pNextPortion->GetKind() != PORTIONKIND_TAB ) && (
( !IsRightToLeft() && pNextPortion->IsRightToLeft() ) ||
( IsRightToLeft() && !pNextPortion->IsRightToLeft() ) ) )
2002-08-13 13:07:14 +00:00
{
2002-08-15 13:26:09 +00:00
// nX += pNextPortion->GetWidth();
// End of the tab portion, use start of next for cursor pos
DBG_ASSERT( !bPreferPortionStart, "ImpGetXPos - How can we this tab portion here???" );
nX = ImpGetXPos( nPara, pLine, nIndex, TRUE );
2002-08-13 13:07:14 +00:00
}
}
}
2002-08-12 14:36:21 +00:00
}
else if ( pPortion->GetKind() == PORTIONKIND_TEXT )
2000-09-18 16:07:07 +00:00
{
2002-08-12 14:36:21 +00:00
DBG_ASSERT( nIndex != pLine->GetStart(), "Strange behavior in new ImpGetXPos()" );
2000-09-18 16:07:07 +00:00
2002-08-12 14:36:21 +00:00
long nPosInPortion = (long)CalcTextWidth( nPara, nTextPortionStart, nIndex-nTextPortionStart );
if ( ( !IsRightToLeft() && !pPortion->IsRightToLeft() ) ||
( IsRightToLeft() && pPortion->IsRightToLeft() ) )
{
nX += nPosInPortion;
}
else
{
nX += nPortionTextWidth - nPosInPortion;
}
2000-09-18 16:07:07 +00:00
}
2002-08-12 14:36:21 +00:00
}
else // if ( nIndex == pLine->GetStart() )
{
2002-08-15 13:26:09 +00:00
if ( ( pPortion->GetKind() != PORTIONKIND_TAB ) &&
( ( !IsRightToLeft() && pPortion->IsRightToLeft() ) ||
( IsRightToLeft() && !pPortion->IsRightToLeft() ) ) )
2000-09-18 16:07:07 +00:00
{
2002-08-12 14:36:21 +00:00
nX += nPortionTextWidth;
2000-09-18 16:07:07 +00:00
}
}
2002-08-12 14:36:21 +00:00
return nX;
2000-09-18 16:07:07 +00:00
}
const TextAttrib* TextEngine::FindAttrib( const TextPaM& rPaM, USHORT nWhich ) const
{
const TextAttrib* pAttr = NULL;
const TextCharAttrib* pCharAttr = FindCharAttrib( rPaM, nWhich );
if ( pCharAttr )
pAttr = &pCharAttr->GetAttr();
return pAttr;
}
const TextCharAttrib* TextEngine::FindCharAttrib( const TextPaM& rPaM, USHORT nWhich ) const
{
const TextCharAttrib* pAttr = NULL;
TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
if ( pNode && ( rPaM.GetIndex() < pNode->GetText().Len() ) )
pAttr = pNode->GetCharAttribs().FindAttrib( nWhich, rPaM.GetIndex() );
return pAttr;
}
BOOL TextEngine::HasAttrib( USHORT nWhich ) const
{
BOOL bAttr = FALSE;
for ( ULONG n = mpDoc->GetNodes().Count(); --n && !bAttr; )
{
TextNode* pNode = mpDoc->GetNodes().GetObject( n );
bAttr = pNode->GetCharAttribs().HasAttrib( nWhich );
}
return bAttr;
}
TextPaM TextEngine::GetPaM( const Point& rDocPos, BOOL bSmart )
{
DBG_ASSERT( GetUpdateMode(), "Darf bei Update=FALSE nicht erreicht werden: GetPaM" );
long nY = 0;
for ( ULONG nPortion = 0; nPortion < mpTEParaPortions->Count(); nPortion++ )
{
TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion );
long nTmpHeight = pPortion->GetLines().Count() * mnCharHeight;
nY += nTmpHeight;
if ( nY > rDocPos.Y() )
{
nY -= nTmpHeight;
Point aPosInPara( rDocPos );
aPosInPara.Y() -= nY;
TextPaM aPaM( nPortion, 0 );
aPaM.GetIndex() = ImpFindIndex( nPortion, aPosInPara, bSmart );
return aPaM;
}
}
// Nicht gefunden - Dann den letzten sichtbare...
ULONG nLastNode = mpDoc->GetNodes().Count() - 1;
TextNode* pLast = mpDoc->GetNodes().GetObject( nLastNode );
return TextPaM( nLastNode, pLast->GetText().Len() );
}
USHORT TextEngine::ImpFindIndex( ULONG nPortion, const Point& rPosInPara, BOOL bSmart )
{
DBG_ASSERT( IsFormatted(), "GetPaM: Nicht formatiert" );
TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion );
USHORT nCurIndex = 0;
long nY = 0;
TextLine* pLine = 0;
USHORT nLine;
for ( nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
{
TextLine* pTmpLine = pPortion->GetLines().GetObject( nLine );
nY += mnCharHeight;
if ( nY > rPosInPara.Y() ) // das war 'se
{
pLine = pTmpLine;
break; // richtige Y-Position intressiert nicht
}
}
DBG_ASSERT( pLine, "ImpFindIndex: pLine ?" );
nCurIndex = GetCharPos( nPortion, nLine, rPosInPara.X(), bSmart );
if ( nCurIndex && ( nCurIndex == pLine->GetEnd() ) &&
( pLine != pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1) ) )
{
2002-05-17 10:48:47 +00:00
uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator();
sal_Int32 nCount = 1;
nCurIndex = (USHORT)xBI->previousCharacters( pPortion->GetNode()->GetText(), nCurIndex, GetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount );
2000-09-18 16:07:07 +00:00
}
return nCurIndex;
}
USHORT TextEngine::GetCharPos( ULONG nPortion, USHORT nLine, long nXPos, BOOL bSmart )
{
TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion );
TextLine* pLine = pPortion->GetLines().GetObject( nLine );
USHORT nCurIndex = pLine->GetStart();
2002-10-17 08:50:51 +00:00
long nTmpX = pLine->GetStartX();
if ( nXPos <= nTmpX )
2000-09-18 16:07:07 +00:00
return nCurIndex;
for ( USHORT i = pLine->GetStartPortion(); i <= pLine->GetEndPortion(); i++ )
{
2002-08-12 14:36:21 +00:00
TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( i );
2000-09-18 16:07:07 +00:00
nTmpX += pTextPortion->GetWidth();
if ( nTmpX > nXPos )
{
if( pTextPortion->GetLen() > 1 )
{
2002-10-17 08:50:51 +00:00
nTmpX -= pTextPortion->GetWidth(); // vor die Portion stellen
// Optimieren: Kein GetTextBreak, wenn feste Fontbreite...
2000-09-18 16:07:07 +00:00
Font aFont;
SeekCursor( nPortion, nCurIndex+1, aFont );
mpRefDev->SetFont( aFont );
2002-10-17 08:50:51 +00:00
long nPosInPortion = nXPos-nTmpX;
if ( IsRightToLeft() != pTextPortion->IsRightToLeft() )
nPosInPortion = pTextPortion->GetWidth() - nPosInPortion;
nCurIndex = mpRefDev->GetTextBreak( pPortion->GetNode()->GetText(), nPosInPortion, nCurIndex );
// MT: GetTextBreak should assure that we are not withing a CTL cell...
2000-09-18 16:07:07 +00:00
}
return nCurIndex;
}
nCurIndex += pTextPortion->GetLen();
}
return nCurIndex;
}
ULONG TextEngine::GetTextHeight() const
{
DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=FALSE nicht verwendet werden: GetTextHeight" );
if ( !IsFormatted() && !IsFormatting() )
((TextEngine*)this)->FormatDoc();
2000-09-18 16:07:07 +00:00
return mnCurTextHeight;
}
ULONG TextEngine::GetTextHeight( ULONG nParagraph ) const
{
DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=FALSE nicht verwendet werden: GetTextHeight" );
if ( !IsFormatted() && !IsFormatting() )
((TextEngine*)this)->FormatDoc();
return CalcParaHeight( nParagraph );
}
2000-09-18 16:07:07 +00:00
ULONG TextEngine::CalcTextWidth()
{
if ( !IsFormatted() && !IsFormatting() )
FormatDoc();
long nMaxWidth = 0;
long nCurWidth = 0;
// --------------------------------------------------
// Ueber alle Absaetze...
// --------------------------------------------------
ULONG nParas = mpTEParaPortions->Count();
for ( ULONG nPara = mpTEParaPortions->Count(); nPara; )
{
TEParaPortion* pPortion = mpTEParaPortions->GetObject( --nPara );
// --------------------------------------------------
// Ueber die Zeilen des Absatzes...
// --------------------------------------------------
for ( USHORT nLine = pPortion->GetLines().Count(); nLine; )
{
TextLine* pLine = pPortion->GetLines().GetObject( --nLine );
// --------------------------------------------------
// Ueber die Portions der Zeile
// --------------------------------------------------
nCurWidth = 0;
for ( USHORT nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ )
{
2002-08-12 14:36:21 +00:00
TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( nTP );
2000-09-18 16:07:07 +00:00
nCurWidth += pTextPortion->GetWidth();
}
if ( nCurWidth > nMaxWidth )
nMaxWidth = nCurWidth;
}
}
nMaxWidth++; // Ein breiter, da in CreateLines bei >= umgebrochen wird.
return (ULONG)nMaxWidth;
}
ULONG TextEngine::CalcTextHeight()
{
DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=FALSE nicht verwendet werden: CalcTextHeight" );
ULONG nY = 0;
for ( ULONG nPortion = mpTEParaPortions->Count(); nPortion; )
nY += CalcParaHeight( --nPortion );
return nY;
}
ULONG TextEngine::CalcTextWidth( ULONG nPara, USHORT nPortionStart, USHORT nLen, const Font* pFont )
{
// Innerhalb des Textes darf es keinen Portionwechsel (Attribut/Tab) geben!
DBG_ASSERT( mpDoc->GetNodes().GetObject( nPara )->GetText().Search( '\t', nPortionStart ) >= (nPortionStart+nLen), "CalcTextWidth: Tab!" );
ULONG nWidth;
if ( mnFixCharWidth100 )
2002-08-12 14:36:21 +00:00
{
2000-09-18 16:07:07 +00:00
nWidth = (ULONG)nLen*mnFixCharWidth100/100;
2002-08-12 14:36:21 +00:00
}
2000-09-18 16:07:07 +00:00
else
{
if ( pFont )
{
if ( !mpRefDev->GetFont().IsSameInstance( *pFont ) )
mpRefDev->SetFont( *pFont );
}
else
{
Font aFont;
SeekCursor( nPara, nPortionStart+1, aFont );
mpRefDev->SetFont( aFont );
}
TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
nWidth = (ULONG)mpRefDev->GetTextWidth( pNode->GetText(), nPortionStart, nLen );
}
return nWidth;
}
USHORT TextEngine::GetLineCount( ULONG nParagraph ) const
{
DBG_ASSERT( nParagraph < mpTEParaPortions->Count(), "GetLineCount: Out of range" );
TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph );
if ( pPPortion )
return pPPortion->GetLines().Count();
return 0xFFFF;
}
USHORT TextEngine::GetLineLen( ULONG nParagraph, USHORT nLine ) const
{
DBG_ASSERT( nParagraph < mpTEParaPortions->Count(), "GetLineCount: Out of range" );
TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph );
if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) )
{
TextLine* pLine = pPPortion->GetLines().GetObject( nLine );
return pLine->GetLen();
}
return 0xFFFF;
}
ULONG TextEngine::CalcParaHeight( ULONG nParagraph ) const
2000-09-18 16:07:07 +00:00
{
ULONG nHeight = 0;
TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph );
DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetParaHeight" );
if ( pPPortion )
nHeight = pPPortion->GetLines().Count() * mnCharHeight;
return nHeight;
}
void TextEngine::UpdateSelections()
{
}
Range TextEngine::GetInvalidYOffsets( ULONG nPortion )
{
TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPortion );
USHORT nLines = pTEParaPortion->GetLines().Count();
USHORT nLastInvalid, nFirstInvalid = 0;
USHORT nLine;
for ( nLine = 0; nLine < nLines; nLine++ )
{
TextLine* pL = pTEParaPortion->GetLines().GetObject( nLine );
if ( pL->IsInvalid() )
{
nFirstInvalid = nLine;
break;
}
}
for ( nLastInvalid = nFirstInvalid; nLastInvalid < nLines; nLastInvalid++ )
{
TextLine* pL = pTEParaPortion->GetLines().GetObject( nLine );
if ( pL->IsValid() )
break;
}
if ( nLastInvalid >= nLines )
nLastInvalid = nLines-1;
return Range( nFirstInvalid*mnCharHeight, ((nLastInvalid+1)*mnCharHeight)-1 );
}
ULONG TextEngine::GetParagraphCount() const
{
return mpDoc->GetNodes().Count();
}
void TextEngine::EnableUndo( BOOL bEnable )
{
// Beim Umschalten des Modus Liste loeschen:
if ( bEnable != IsUndoEnabled() )
ResetUndo();
mbUndoEnabled = bEnable;
}
SfxUndoManager& TextEngine::GetUndoManager()
{
if ( !mpUndoManager )
mpUndoManager = new TextUndoManager( this );
return *mpUndoManager;
}
void TextEngine::UndoActionStart( USHORT nId )
{
if ( IsUndoEnabled() && !IsInUndo() )
{
String aComment;
// ...
GetUndoManager().EnterListAction( aComment, XubString(), nId );
}
}
void TextEngine::UndoActionEnd( USHORT nId )
{
if ( IsUndoEnabled() && !IsInUndo() )
GetUndoManager().LeaveListAction();
}
void TextEngine::InsertUndo( TextUndo* pUndo, BOOL bTryMerge )
{
DBG_ASSERT( !IsInUndo(), "InsertUndo im Undomodus!" );
GetUndoManager().AddUndoAction( pUndo, bTryMerge );
}
void TextEngine::ResetUndo()
{
if ( mpUndoManager )
mpUndoManager->Clear();
}
void TextEngine::InsertContent( TextNode* pNode, ULONG nPara )
{
DBG_ASSERT( pNode, "NULL-Pointer in InsertContent! " );
DBG_ASSERT( IsInUndo(), "InsertContent nur fuer Undo()!" );
TEParaPortion* pNew = new TEParaPortion( pNode );
mpTEParaPortions->Insert( pNew, nPara );
mpDoc->GetNodes().Insert( pNode, nPara );
ImpParagraphInserted( nPara );
}
TextPaM TextEngine::SplitContent( ULONG nNode, USHORT nSepPos )
{
TextNode* pNode = mpDoc->GetNodes().GetObject( nNode );
DBG_ASSERT( pNode, "Ungueltiger Node in SplitContent" );
DBG_ASSERT( IsInUndo(), "SplitContent nur fuer Undo()!" );
DBG_ASSERT( nSepPos <= pNode->GetText().Len(), "Index im Wald: SplitContent" );
TextPaM aPaM( nNode, nSepPos );
return ImpInsertParaBreak( aPaM );
}
TextPaM TextEngine::ConnectContents( ULONG nLeftNode )
{
DBG_ASSERT( IsInUndo(), "ConnectContent nur fuer Undo()!" );
return ImpConnectParagraphs( nLeftNode, nLeftNode+1 );
}
void TextEngine::SeekCursor( ULONG nPara, USHORT nPos, Font& rFont )
{
rFont = maFont;
TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
USHORT nAttribs = pNode->GetCharAttribs().Count();
for ( USHORT nAttr = 0; nAttr < nAttribs; nAttr++ )
{
TextCharAttrib* pAttrib = pNode->GetCharAttribs().GetAttrib( nAttr );
if ( pAttrib->GetStart() > nPos )
break;
// Beim Seeken nicht die Attr beruecksichtigen, die dort beginnen!
// Leere Attribute werden beruecksichtigt( verwendet), da diese
// gerade eingestellt wurden.
// 12.4.95: Doch keine Leeren Attribute verwenden:
// - Wenn gerade eingestellt und leer => keine Auswirkung auf Font
// In einem leeren Absatz eingestellte Zeichen werden sofort wirksam.
if ( ( ( pAttrib->GetStart() < nPos ) && ( pAttrib->GetEnd() >= nPos ) )
|| !pNode->GetText().Len() )
{
pAttrib->GetAttr().SetFont( rFont );
}
}
2000-10-13 08:55:28 +00:00
if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetPara() == nPara ) &&
( nPos > mpIMEInfos->aPos.GetIndex() ) && ( nPos <= ( mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen ) ) )
{
sal_uInt16 nAttr = mpIMEInfos->pAttribs[ nPos - mpIMEInfos->aPos.GetIndex() - 1 ];
if ( nAttr & EXTTEXTINPUT_ATTR_UNDERLINE )
rFont.SetUnderline( UNDERLINE_SINGLE );
else if ( nAttr & EXTTEXTINPUT_ATTR_BOLDUNDERLINE )
rFont.SetUnderline( UNDERLINE_BOLD );
else if ( nAttr & EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE )
rFont.SetUnderline( UNDERLINE_DOTTED );
else if ( nAttr & EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE )
rFont.SetUnderline( UNDERLINE_DOTTED );
2000-11-08 09:42:11 +00:00
if ( nAttr & EXTTEXTINPUT_ATTR_REDTEXT )
2000-10-13 08:55:28 +00:00
rFont.SetColor( Color( COL_RED ) );
2000-11-08 09:42:11 +00:00
else if ( nAttr & EXTTEXTINPUT_ATTR_HALFTONETEXT )
rFont.SetColor( Color( COL_LIGHTGRAY ) );
2000-10-13 08:55:28 +00:00
if ( nAttr & EXTTEXTINPUT_ATTR_HIGHLIGHT )
{
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
rFont.SetColor( rStyleSettings.GetHighlightTextColor() );
rFont.SetFillColor( rStyleSettings.GetHighlightColor() );
rFont.SetTransparent( FALSE );
}
else if ( nAttr & EXTTEXTINPUT_ATTR_GRAYWAVELINE )
{
rFont.SetUnderline( UNDERLINE_WAVE );
// if( pOut )
// pOut->SetTextLineColor( Color( COL_LIGHTGRAY ) );
}
}
2000-09-18 16:07:07 +00:00
}
void TextEngine::SetUpdateMode( BOOL bUp, TextView* pCurView, BOOL bForceUpdate )
{
BOOL bChanged = ( GetUpdateMode() != bUp );
mbUpdate = bUp;
if ( mbUpdate && ( bChanged || bForceUpdate ) )
FormatAndUpdate( pCurView );
}
void TextEngine::FormatAndUpdate( TextView* pCurView )
{
if ( mbDowning )
return ;
if ( IsInUndo() )
IdleFormatAndUpdate( pCurView );
else
{
FormatDoc();
UpdateViews( pCurView );
}
}
void TextEngine::IdleFormatAndUpdate( TextView* pCurView )
{
mpIdleFormatter->DoIdleFormat( pCurView );
}
void TextEngine::TextModified()
{
mbFormatted = FALSE;
}
void TextEngine::UpdateViews( TextView* pCurView )
{
if ( !GetUpdateMode() || IsFormatting() || maInvalidRec.IsEmpty() )
return;
DBG_ASSERT( IsFormatted(), "UpdateViews: Doc nicht formatiert!" );
for ( USHORT nView = 0; nView < mpViews->Count(); nView++ )
{
TextView* pView = mpViews->GetObject( nView );
pView->HideCursor();
Rectangle aClipRec( maInvalidRec );
2002-08-12 14:36:21 +00:00
Size aOutSz = pView->GetWindow()->GetOutputSizePixel();
Rectangle aVisArea( pView->GetStartDocPos(), aOutSz );
2000-09-18 16:07:07 +00:00
aClipRec.Intersection( aVisArea );
if ( !aClipRec.IsEmpty() )
{
// in Fensterkoordinaten umwandeln....
2002-08-12 14:36:21 +00:00
Point aNewPos = pView->GetWindowPos( aClipRec.TopLeft() );
if ( IsRightToLeft() )
aNewPos.X() -= aOutSz.Width() - 1;
aClipRec.SetPos( aNewPos );
2000-09-18 16:07:07 +00:00
if ( pView == pCurView )
pView->ImpPaint( aClipRec, TRUE );
else
pView->GetWindow()->Invalidate( aClipRec );
}
}
if ( pCurView )
{
pCurView->ShowCursor( pCurView->mbAutoScroll );
}
maInvalidRec = Rectangle();
}
IMPL_LINK_INLINE_START( TextEngine, IdleFormatHdl, Timer *, EMPTYARG )
{
mpIdleFormatter->ResetRestarts();
FormatAndUpdate( mpIdleFormatter->GetView() );
return 0;
}
IMPL_LINK_INLINE_END( TextEngine, IdleFormatHdl, Timer *, EMPTYARG )
void TextEngine::CheckIdleFormatter()
{
mpIdleFormatter->ForceTimeout();
}
void TextEngine::FormatFullDoc()
{
for ( ULONG nPortion = 0; nPortion < mpTEParaPortions->Count(); nPortion++ )
{
TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPortion );
USHORT nLen = pTEParaPortion->GetNode()->GetText().Len();
pTEParaPortion->MarkSelectionInvalid( 0, nLen );
}
FormatDoc();
}
void TextEngine::FormatDoc()
{
if ( !GetUpdateMode() || IsFormatting() )
return;
mbIsFormatting = TRUE;
mbModified = TRUE;
long nY = 0;
BOOL bGrow = FALSE;
maInvalidRec = Rectangle(); // leermachen
for ( ULONG nPara = 0; nPara < mpTEParaPortions->Count(); nPara++ )
{
TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
if ( pTEParaPortion->IsInvalid() )
{
ImpFormattingParagraph( nPara );
if ( CreateLines( nPara ) )
bGrow = TRUE;
// InvalidRec nur einmal setzen...
if ( maInvalidRec.IsEmpty() )
{
// Bei Paperwidth 0 (AutoPageSize) bleibt es sonst Empty()...
long nWidth = (long)mnMaxTextWidth;
if ( !nWidth )
nWidth = 0x7FFFFFFF;
Range aInvRange( GetInvalidYOffsets( nPara ) );
maInvalidRec = Rectangle( Point( 0, nY+aInvRange.Min() ),
Size( nWidth, aInvRange.Len() ) );
}
else
{
maInvalidRec.Bottom() = nY + CalcParaHeight( nPara );
}
}
else if ( bGrow )
{
maInvalidRec.Bottom() = nY + CalcParaHeight( nPara );
}
nY += CalcParaHeight( nPara );
}
if ( !maInvalidRec.IsEmpty() )
{
ULONG nNewHeight = CalcTextHeight();
long nDiff = nNewHeight - mnCurTextHeight;
if ( nNewHeight < mnCurTextHeight )
{
maInvalidRec.Bottom() = (long)Max( nNewHeight, mnCurTextHeight );
if ( maInvalidRec.IsEmpty() )
{
maInvalidRec.Top() = 0;
// Left und Right werden nicht ausgewertet, aber wegen IsEmpty gesetzt.
maInvalidRec.Left() = 0;
maInvalidRec.Right() = mnMaxTextWidth;
}
}
mnCurTextHeight = nNewHeight;
if ( nDiff )
{
mbFormatted = TRUE;
ImpTextHeightChanged();
}
}
mbIsFormatting = FALSE;
mbFormatted = TRUE;
ImpTextFormatted();
}
void TextEngine::CreateAndInsertEmptyLine( ULONG nPara )
{
TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
TextLine* pTmpLine = new TextLine;
pTmpLine->SetStart( pNode->GetText().Len() );
pTmpLine->SetEnd( pTmpLine->GetStart() );
pTEParaPortion->GetLines().Insert( pTmpLine, pTEParaPortion->GetLines().Count() );
2002-08-12 14:36:21 +00:00
if ( ImpGetAlign() == TXTALIGN_CENTER )
2000-09-18 16:07:07 +00:00
pTmpLine->SetStartX( (short)(mnMaxTextWidth / 2) );
2002-08-12 14:36:21 +00:00
else if ( ImpGetAlign() == TXTALIGN_RIGHT )
2000-09-18 16:07:07 +00:00
pTmpLine->SetStartX( (short)mnMaxTextWidth );
else
pTmpLine->SetStartX( mpDoc->GetLeftMargin() );
BOOL bLineBreak = pNode->GetText().Len() ? TRUE : FALSE;
2002-08-12 14:36:21 +00:00
TETextPortion* pDummyPortion = new TETextPortion( 0 );
2000-09-18 16:07:07 +00:00
pDummyPortion->GetWidth() = 0;
pTEParaPortion->GetTextPortions().Insert( pDummyPortion, pTEParaPortion->GetTextPortions().Count() );
if ( bLineBreak == TRUE )
{
// -2: Die neue ist bereits eingefuegt.
TextLine* pLastLine = pTEParaPortion->GetLines().GetObject( pTEParaPortion->GetLines().Count()-2 );
DBG_ASSERT( pLastLine, "Weicher Umbruch, keine Zeile ?!" );
USHORT nPos = (USHORT) pTEParaPortion->GetTextPortions().Count() - 1 ;
pTmpLine->SetStartPortion( nPos );
pTmpLine->SetEndPortion( nPos );
}
}
2002-08-12 14:36:21 +00:00
void TextEngine::ImpBreakLine( ULONG nPara, TextLine* pLine, TETextPortion* pPortion, USHORT nPortionStart, long nRemainingWidth )
2000-09-18 16:07:07 +00:00
{
TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
// Font sollte noch eingestellt sein.
USHORT nMaxBreakPos = mpRefDev->GetTextBreak( pNode->GetText(), nRemainingWidth, nPortionStart );
DBG_ASSERT( nMaxBreakPos < pNode->GetText().Len(), "Break?!" );
if ( nMaxBreakPos == STRING_LEN ) // GetTextBreak() ist anderer Auffassung als GetTextSize()
nMaxBreakPos = pNode->GetText().Len() - 1;
uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator();
i18n::LineBreakHyphenationOptions aHyphOptions( NULL, uno::Sequence< beans::PropertyValue >(), 1 );
2002-10-17 16:16:28 +00:00
i18n::LineBreakUserOptions aUserOptions;
2002-10-17 16:16:28 +00:00
aUserOptions.forbiddenBeginCharacters = ImpGetLocaleDataWrapper()->getForbiddenCharacters().beginLine;
aUserOptions.forbiddenEndCharacters = ImpGetLocaleDataWrapper()->getForbiddenCharacters().endLine;
aUserOptions.applyForbiddenRules = sal_True;
aUserOptions.allowPunctuationOutsideMargin = sal_False;
aUserOptions.allowHyphenateEnglish = sal_False;
i18n::LineBreakResults aLBR = xBI->getLineBreak( pNode->GetText(), nMaxBreakPos, GetLocale(), pLine->GetStart(), aHyphOptions, aUserOptions );
2001-05-11 07:02:12 +00:00
USHORT nBreakPos = (USHORT)aLBR.breakIndex;
2000-09-18 16:07:07 +00:00
if ( nBreakPos <= pLine->GetStart() )
{
2000-09-18 16:07:07 +00:00
nBreakPos = nMaxBreakPos;
if ( nBreakPos <= pLine->GetStart() )
nBreakPos = pLine->GetStart() + 1; // Sonst Endlosschleife!
}
2000-09-18 16:07:07 +00:00
// die angeknackste Portion ist die End-Portion
pLine->SetEnd( nBreakPos );
USHORT nEndPortion = SplitTextPortion( nPara, nBreakPos );
sal_Bool bBlankSeparator = ( ( nBreakPos >= pLine->GetStart() ) &&
( pNode->GetText().GetChar( nBreakPos ) == ' ' ) ) ? sal_True : sal_False;
if ( bBlankSeparator )
{
// Blanks am Zeilenende generell unterdruecken...
TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2002-08-12 14:36:21 +00:00
TETextPortion* pTP = pTEParaPortion->GetTextPortions().GetObject( nEndPortion );
2000-09-18 16:07:07 +00:00
DBG_ASSERT( nBreakPos > pLine->GetStart(), "SplitTextPortion am Anfang der Zeile?" );
pTP->GetWidth() = (long)CalcTextWidth( nPara, nBreakPos-pTP->GetLen(), pTP->GetLen()-1 );
}
pLine->SetEndPortion( nEndPortion );
}
USHORT TextEngine::SplitTextPortion( ULONG nPara, USHORT nPos )
{
// Die Portion bei nPos wird geplittet, wenn bei nPos nicht
// sowieso ein Wechsel ist
if ( nPos == 0 )
return 0;
USHORT nSplitPortion;
USHORT nTmpPos = 0;
2002-08-12 14:36:21 +00:00
TETextPortion* pTextPortion = 0;
2000-09-18 16:07:07 +00:00
TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
USHORT nPortions = pTEParaPortion->GetTextPortions().Count();
for ( nSplitPortion = 0; nSplitPortion < nPortions; nSplitPortion++ )
{
2002-08-12 14:36:21 +00:00
TETextPortion* pTP = pTEParaPortion->GetTextPortions().GetObject(nSplitPortion);
2000-09-18 16:07:07 +00:00
nTmpPos += pTP->GetLen();
if ( nTmpPos >= nPos )
{
if ( nTmpPos == nPos ) // dann braucht nichts geteilt werden
return nSplitPortion;
pTextPortion = pTP;
break;
}
}
DBG_ASSERT( pTextPortion, "Position ausserhalb des Bereichs!" );
USHORT nOverlapp = nTmpPos - nPos;
pTextPortion->GetLen() -= nOverlapp;
2002-08-12 14:36:21 +00:00
TETextPortion* pNewPortion = new TETextPortion( nOverlapp );
2000-09-18 16:07:07 +00:00
pTEParaPortion->GetTextPortions().Insert( pNewPortion, nSplitPortion+1 );
pTextPortion->GetWidth() = (long)CalcTextWidth( nPara, nPos-pTextPortion->GetLen(), pTextPortion->GetLen() );
return nSplitPortion;
}
void TextEngine::CreateTextPortions( ULONG nPara, USHORT nStartPos )
{
TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
TextNode* pNode = pTEParaPortion->GetNode();
DBG_ASSERT( pNode->GetText().Len(), "CreateTextPortions sollte nicht fuer leere Absaetze verwendet werden!" );
TESortedPositions aPositions;
ULONG nZero = 0;
aPositions.Insert( nZero );
USHORT nAttribs = pNode->GetCharAttribs().Count();
for ( USHORT nAttr = 0; nAttr < nAttribs; nAttr++ )
{
TextCharAttrib* pAttrib = pNode->GetCharAttribs().GetAttrib( nAttr );
// Start und Ende in das Array eintragen...
// Die InsertMethode laesst keine doppelten Werte zu....
aPositions.Insert( pAttrib->GetStart() );
aPositions.Insert( pAttrib->GetEnd() );
}
aPositions.Insert( pNode->GetText().Len() );
2002-08-12 14:36:21 +00:00
const TEWritingDirectionInfos& rWritingDirections = pTEParaPortion->GetWritingDirectionInfos();
for ( USHORT nD = 0; nD < rWritingDirections.Count(); nD++ )
aPositions.Insert( rWritingDirections[nD].nStartPos );
2000-10-13 08:55:28 +00:00
if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetPara() == nPara ) )
{
sal_uInt16 nLastAttr = 0xFFFF;
for( sal_uInt16 n = 0; n < mpIMEInfos->nLen; n++ )
{
if ( mpIMEInfos->pAttribs[n] != nLastAttr )
{
aPositions.Insert( mpIMEInfos->aPos.GetIndex() + n );
nLastAttr = mpIMEInfos->pAttribs[n];
}
}
}
2000-09-18 16:07:07 +00:00
USHORT nTabPos = pNode->GetText().Search( '\t', 0 );
while ( nTabPos != STRING_NOTFOUND )
{
aPositions.Insert( nTabPos );
aPositions.Insert( nTabPos + 1 );
nTabPos = pNode->GetText().Search( '\t', nTabPos+1 );
}
// Ab ... loeschen:
// Leider muss die Anzahl der TextPortions mit aPositions.Count()
// nicht uebereinstimmen, da evtl. Zeilenumbrueche...
USHORT nPortionStart = 0;
USHORT nInvPortion = 0;
for ( USHORT nP = 0; nP < pTEParaPortion->GetTextPortions().Count(); nP++ )
{
2002-08-12 14:36:21 +00:00
TETextPortion* pTmpPortion = pTEParaPortion->GetTextPortions().GetObject(nP);
2000-09-18 16:07:07 +00:00
nPortionStart += pTmpPortion->GetLen();
if ( nPortionStart >= nStartPos )
{
nPortionStart -= pTmpPortion->GetLen();
nInvPortion = nP;
break;
}
}
DBG_ASSERT( nP < pTEParaPortion->GetTextPortions().Count() || !pTEParaPortion->GetTextPortions().Count(), "Nichts zum loeschen: CreateTextPortions" );
if ( nInvPortion && ( nPortionStart+pTEParaPortion->GetTextPortions().GetObject(nInvPortion)->GetLen() > nStartPos ) )
{
// lieber eine davor...
// Aber nur wenn es mitten in der Portion war, sonst ist es evtl.
// die einzige in der Zeile davor !
nInvPortion--;
nPortionStart -= pTEParaPortion->GetTextPortions().GetObject(nInvPortion)->GetLen();
}
pTEParaPortion->GetTextPortions().DeleteFromPortion( nInvPortion );
// Eine Portion kann auch durch einen Zeilenumbruch entstanden sein:
aPositions.Insert( nPortionStart );
USHORT nInvPos;
BOOL bFound = aPositions.Seek_Entry( nPortionStart, &nInvPos );
DBG_ASSERT( bFound && ( nInvPos < (aPositions.Count()-1) ), "InvPos ?!" );
for ( USHORT i = nInvPos+1; i < aPositions.Count(); i++ )
{
2002-08-12 14:36:21 +00:00
TETextPortion* pNew = new TETextPortion( (USHORT)aPositions[i] - (USHORT)aPositions[i-1] );
2000-09-18 16:07:07 +00:00
pTEParaPortion->GetTextPortions().Insert( pNew, pTEParaPortion->GetTextPortions().Count());
}
DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "Keine Portions?!" );
#ifdef EDITDEBUG
DBG_ASSERT( pTEParaPortion->DbgCheckTextPortions(), "Portions kaputt?" );
#endif
}
void TextEngine::RecalcTextPortion( ULONG nPara, USHORT nStartPos, short nNewChars )
{
TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "Keine Portions!" );
DBG_ASSERT( nNewChars, "RecalcTextPortion mit Diff == 0" );
TextNode* const pNode = pTEParaPortion->GetNode();
if ( nNewChars > 0 )
{
// Wenn an nStartPos ein Attribut beginnt/endet, oder vor nStartPos
// ein Tab steht, faengt eine neue Portion an,
// ansonsten wird die Portion an nStartPos erweitert.
// Oder wenn ganz vorne ( StartPos 0 ) und dann ein Tab
if ( ( pNode->GetCharAttribs().HasBoundingAttrib( nStartPos ) ) ||
( nStartPos && ( pNode->GetText().GetChar( nStartPos - 1 ) == '\t' ) ) ||
( ( !nStartPos && ( nNewChars < pNode->GetText().Len() ) && pNode->GetText().GetChar( nNewChars ) == '\t' ) ) )
{
USHORT nNewPortionPos = 0;
if ( nStartPos )
nNewPortionPos = SplitTextPortion( nPara, nStartPos ) + 1;
// else if ( ( pTEParaPortion->GetTextPortions().Count() == 1 ) &&
// !pTEParaPortion->GetTextPortions()[0]->GetLen() )
// pTEParaPortion->GetTextPortions().Reset(); // DummyPortion
// Eine leere Portion kann hier stehen, wenn der Absatz leer war,
// oder eine Zeile durch einen harten Zeilenumbruch entstanden ist.
if ( ( nNewPortionPos < pTEParaPortion->GetTextPortions().Count() ) &&
!pTEParaPortion->GetTextPortions()[nNewPortionPos]->GetLen() )
{
// Dann die leere Portion verwenden.
pTEParaPortion->GetTextPortions()[nNewPortionPos]->GetLen() += nNewChars;
}
else
{
2002-08-12 14:36:21 +00:00
TETextPortion* pNewPortion = new TETextPortion( nNewChars );
2000-09-18 16:07:07 +00:00
pTEParaPortion->GetTextPortions().Insert( pNewPortion, nNewPortionPos );
}
}
else
{
USHORT nPortionStart;
const USHORT nTP = pTEParaPortion->GetTextPortions().
FindPortion( nStartPos, nPortionStart );
2002-08-12 14:36:21 +00:00
TETextPortion* const pTP = pTEParaPortion->GetTextPortions()[ nTP ];
2000-09-18 16:07:07 +00:00
DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden" );
pTP->GetLen() += nNewChars;
pTP->GetWidth() = (-1);
}
}
else
{
// Portion schrumpfen oder ggf. entfernen.
// Vor Aufruf dieser Methode muss sichergestellt sein, dass
// keine Portions in dem geloeschten Bereich lagen!
// Es darf keine reinragende oder im Bereich startende Portion geben,
// also muss nStartPos <= nPos <= nStartPos - nNewChars(neg.) sein
USHORT nPortion = 0;
USHORT nPos = 0;
USHORT nEnd = nStartPos-nNewChars;
USHORT nPortions = pTEParaPortion->GetTextPortions().Count();
2002-08-12 14:36:21 +00:00
TETextPortion* pTP = 0;
2000-09-18 16:07:07 +00:00
for ( nPortion = 0; nPortion < nPortions; nPortion++ )
{
pTP = pTEParaPortion->GetTextPortions()[ nPortion ];
if ( ( nPos+pTP->GetLen() ) > nStartPos )
{
DBG_ASSERT( nPos <= nStartPos, "Start falsch!" );
DBG_ASSERT( nPos+pTP->GetLen() >= nEnd, "End falsch!" );
break;
}
nPos += pTP->GetLen();
}
DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden" );
if ( ( nPos == nStartPos ) && ( (nPos+pTP->GetLen()) == nEnd ) )
{
// Portion entfernen;
pTEParaPortion->GetTextPortions().Remove( nPortion );
delete pTP;
}
else
{
DBG_ASSERT( pTP->GetLen() > (-nNewChars), "Portion zu klein zum schrumpfen!" );
pTP->GetLen() += nNewChars;
}
DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "RecalcTextPortions: Keine mehr da!" );
}
#ifdef EDITDEBUG
DBG_ASSERT( pTEParaPortion->DbgCheckTextPortions(), "Portions kaputt?" );
#endif
}
void TextEngine::ImpPaint( OutputDevice* pOutDev, const Point& rStartPos, Rectangle const* pPaintArea, TextSelection const* pPaintRange, TextSelection const* pSelection )
{
if ( !GetUpdateMode() )
return;
if ( !IsFormatted() )
FormatDoc();
long nY = rStartPos.Y();
TextPaM const* pSelStart = 0;
TextPaM const* pSelEnd = 0;
if ( pSelection && pSelection->HasRange() )
{
BOOL bInvers = pSelection->GetEnd() < pSelection->GetStart();
pSelStart = !bInvers ? &pSelection->GetStart() : &pSelection->GetEnd();
pSelEnd = bInvers ? &pSelection->GetStart() : &pSelection->GetEnd();
}
DBG_ASSERT( !pPaintRange || ( pPaintRange->GetStart() < pPaintRange->GetEnd() ), "ImpPaint: Paint-Range?!" );
const StyleSettings& rStyleSettings = pOutDev->GetSettings().GetStyleSettings();
// --------------------------------------------------
// Ueber alle Absaetze...
// --------------------------------------------------
for ( ULONG nPara = 0; nPara < mpTEParaPortions->Count(); nPara++ )
{
TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
// falls beim Tippen Idle-Formatierung, asynchrones Paint.
if ( pPortion->IsInvalid() )
return;
ULONG nParaHeight = CalcParaHeight( nPara );
USHORT nIndex = 0;
if ( ( !pPaintArea || ( ( nY + (long)nParaHeight ) > pPaintArea->Top() ) )
&& ( !pPaintRange || ( ( nPara >= pPaintRange->GetStart().GetPara() ) && ( nPara <= pPaintRange->GetEnd().GetPara() ) ) ) )
{
// --------------------------------------------------
// Ueber die Zeilen des Absatzes...
// --------------------------------------------------
USHORT nLines = pPortion->GetLines().Count();
for ( USHORT nLine = 0; nLine < nLines; nLine++ )
{
TextLine* pLine = pPortion->GetLines().GetObject(nLine);
Point aTmpPos( rStartPos.X() + pLine->GetStartX(), nY );
if ( ( !pPaintArea || ( ( nY + mnCharHeight ) > pPaintArea->Top() ) )
&& ( !pPaintRange || (
( TextPaM( nPara, pLine->GetStart() ) < pPaintRange->GetEnd() ) &&
( TextPaM( nPara, pLine->GetEnd() ) > pPaintRange->GetStart() ) ) ) )
{
// --------------------------------------------------
// Ueber die Portions der Zeile...
// --------------------------------------------------
nIndex = pLine->GetStart();
for ( USHORT y = pLine->GetStartPortion(); y <= pLine->GetEndPortion(); y++ )
{
DBG_ASSERT( pPortion->GetTextPortions().Count(), "Zeile ohne Textportion im Paint!" );
2002-08-12 14:36:21 +00:00
TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( y );
2000-09-18 16:07:07 +00:00
DBG_ASSERT( pTextPortion, "NULL-Pointer im Portioniterator in UpdateViews" );
2002-08-12 14:36:21 +00:00
ImpInitLayoutMode( pOutDev /*, pTextPortion->IsRightToLeft() */);
2000-09-18 16:07:07 +00:00
long nTxtWidth = pTextPortion->GetWidth();
2002-08-12 14:36:21 +00:00
aTmpPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nIndex, nIndex );
2000-09-18 16:07:07 +00:00
// nur ausgeben, was im sichtbaren Bereich beginnt:
if ( ( ( aTmpPos.X() + nTxtWidth ) >= 0 )
&& ( !pPaintRange || (
( TextPaM( nPara, nIndex ) < pPaintRange->GetEnd() ) &&
( TextPaM( nPara, nIndex + pTextPortion->GetLen() ) > pPaintRange->GetStart() ) ) ) )
{
switch ( pTextPortion->GetKind() )
{
case PORTIONKIND_TEXT:
{
{
Font aFont;
SeekCursor( nPara, nIndex+1, aFont );
if ( pSelection )
aFont.SetTransparent( FALSE );
pOutDev->SetFont( aFont );
USHORT nTmpIndex = nIndex;
USHORT nEnd = nTmpIndex + pTextPortion->GetLen();
Point aPos = aTmpPos;
if ( pPaintRange )
{
// evtl soll nicht alles ausgegeben werden...
if ( ( pPaintRange->GetStart().GetPara() == nPara )
&& ( nTmpIndex < pPaintRange->GetStart().GetIndex() ) )
{
2002-08-12 14:36:21 +00:00
nTmpIndex = pPaintRange->GetStart().GetIndex();
2000-09-18 16:07:07 +00:00
}
if ( ( pPaintRange->GetEnd().GetPara() == nPara )
&& ( nEnd > pPaintRange->GetEnd().GetIndex() ) )
{
nEnd = pPaintRange->GetEnd().GetIndex();
}
}
BOOL bDone = FALSE;
if ( pSelStart )
{
// liegt ein Teil in der Selektion???
TextPaM aTextStart( nPara, nTmpIndex );
TextPaM aTextEnd( nPara, nEnd );
if ( ( aTextStart < *pSelEnd ) && ( aTextEnd > *pSelStart ) )
{
USHORT nL;
// 1) Bereich vor Selektion
if ( aTextStart < *pSelStart )
{
nL = pSelStart->GetIndex() - nTmpIndex;
pOutDev->SetFont( aFont );
2002-08-12 14:36:21 +00:00
aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL );
2000-09-18 16:07:07 +00:00
pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nL );
nTmpIndex += nL;
}
// 2) Bereich mit Selektion
nL = nEnd-nTmpIndex;
if ( aTextEnd > *pSelEnd )
nL = pSelEnd->GetIndex() - nTmpIndex;
pOutDev->SetTextColor( rStyleSettings.GetHighlightTextColor() );
pOutDev->SetTextFillColor( rStyleSettings.GetHighlightColor() );
2002-08-12 14:36:21 +00:00
aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL );
2000-09-18 16:07:07 +00:00
pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nL );
nTmpIndex += nL;
// 3) Bereich nach Selektion
if ( nTmpIndex < nEnd )
{
pOutDev->SetFont( aFont );
2002-08-12 14:36:21 +00:00
nL = nEnd-nTmpIndex;
aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL );
2000-09-18 16:07:07 +00:00
pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nEnd-nTmpIndex );
}
bDone = TRUE;
}
}
if ( !bDone )
2002-08-12 14:36:21 +00:00
{
aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nEnd );
2000-09-18 16:07:07 +00:00
pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nEnd-nTmpIndex );
2002-08-12 14:36:21 +00:00
}
2000-09-18 16:07:07 +00:00
}
}
break;
case PORTIONKIND_TAB:
{
// Bei HideSelection() nur Range, pSelection = 0.
if ( pSelStart || pPaintRange )
{
Rectangle aTabArea( aTmpPos, Point( aTmpPos.X()+nTxtWidth, aTmpPos.Y()+mnCharHeight-1 ) );
BOOL bDone = FALSE;
if ( pSelStart )
{
// liegt der Tab in der Selektion???
TextPaM aTextStart( nPara, nIndex );
TextPaM aTextEnd( nPara, nIndex+1 );
if ( ( aTextStart < *pSelEnd ) && ( aTextEnd > *pSelStart ) )
{
Color aOldColor = pOutDev->GetFillColor();
pOutDev->SetFillColor( rStyleSettings.GetHighlightColor() );
pOutDev->DrawRect( aTabArea );
pOutDev->SetFillColor( aOldColor );
bDone = TRUE;
}
}
if ( !bDone )
{
pOutDev->Erase( aTabArea );
}
}
2002-08-14 12:09:49 +00:00
#ifdef EDITDEBUG
Rectangle aTabArea( aTmpPos, Point( aTmpPos.X()+nTxtWidth, aTmpPos.Y()+mnCharHeight-1 ) );
Color aOldColor = pOutDev->GetFillColor();
pOutDev->SetFillColor( (y%2) ? COL_RED : COL_GREEN );
pOutDev->DrawRect( aTabArea );
pOutDev->SetFillColor( aOldColor );
#endif
2000-09-18 16:07:07 +00:00
}
break;
default: DBG_ERROR( "ImpPaint: Unknown Portion-Type !" );
}
}
nIndex += pTextPortion->GetLen();
}
}
nY += mnCharHeight;
if ( pPaintArea && ( nY >= pPaintArea->Bottom() ) )
break; // keine sichtbaren Aktionen mehr...
}
}
else
{
nY += nParaHeight;
}
if ( pPaintArea && ( nY > pPaintArea->Bottom() ) )
break; // keine sichtbaren Aktionen mehr...
}
}
BOOL TextEngine::CreateLines( ULONG nPara )
{
// BOOL: Aenderung der Hoehe des Absatzes Ja/Nein - TRUE/FALSE
TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
DBG_ASSERT( pTEParaPortion->IsInvalid(), "CreateLines: Portion nicht invalid!" );
USHORT nOldLineCount = pTEParaPortion->GetLines().Count();
// ---------------------------------------------------------------
// Schnelle Sonderbehandlung fuer leere Absaetze...
// ---------------------------------------------------------------
if ( pTEParaPortion->GetNode()->GetText().Len() == 0 )
{
// schnelle Sonderbehandlung...
if ( pTEParaPortion->GetTextPortions().Count() )
pTEParaPortion->GetTextPortions().Reset();
if ( pTEParaPortion->GetLines().Count() )
pTEParaPortion->GetLines().DeleteAndDestroy( 0, pTEParaPortion->GetLines().Count() );
CreateAndInsertEmptyLine( nPara );
pTEParaPortion->SetValid();
return nOldLineCount != pTEParaPortion->GetLines().Count();
}
// ---------------------------------------------------------------
// Initialisierung......
// ---------------------------------------------------------------
if ( pTEParaPortion->GetLines().Count() == 0 )
{
TextLine* pL = new TextLine;
pTEParaPortion->GetLines().Insert( pL, 0 );
}
const short nInvalidDiff = pTEParaPortion->GetInvalidDiff();
const USHORT nInvalidStart = pTEParaPortion->GetInvalidPosStart();
const USHORT nInvalidEnd = nInvalidStart + Abs( nInvalidDiff );
BOOL bQuickFormat = FALSE;
if ( !pTEParaPortion->GetWritingDirectionInfos().Count() )
ImpInitWritingDirections( nPara );
if ( pTEParaPortion->GetWritingDirectionInfos().Count() == 1 )
2000-09-18 16:07:07 +00:00
{
if ( pTEParaPortion->IsSimpleInvalid() && ( nInvalidDiff > 0 ) )
2000-09-18 16:07:07 +00:00
{
bQuickFormat = TRUE;
}
else if ( ( pTEParaPortion->IsSimpleInvalid() ) && ( nInvalidDiff < 0 ) )
{
// pruefen, ob loeschen ueber Portiongrenzen erfolgte...
USHORT nStart = nInvalidStart; // DOPPELT !!!!!!!!!!!!!!!
USHORT nEnd = nStart - nInvalidDiff; // neg.
bQuickFormat = TRUE;
USHORT nPos = 0;
USHORT nPortions = pTEParaPortion->GetTextPortions().Count();
for ( USHORT nTP = 0; nTP < nPortions; nTP++ )
2000-09-18 16:07:07 +00:00
{
// Es darf kein Start/Ende im geloeschten Bereich liegen.
TETextPortion* const pTP = pTEParaPortion->GetTextPortions().GetObject( nTP );
nPos += pTP->GetLen();
if ( ( nPos > nStart ) && ( nPos < nEnd ) )
{
bQuickFormat = FALSE;
break;
}
2000-09-18 16:07:07 +00:00
}
}
}
if ( bQuickFormat )
2000-09-18 16:07:07 +00:00
RecalcTextPortion( nPara, nInvalidStart, nInvalidDiff );
else
CreateTextPortions( nPara, nInvalidStart );
// ---------------------------------------------------------------
// Zeile mit InvalidPos suchen, eine Zeile davor beginnen...
// Zeilen flaggen => nicht removen !
// ---------------------------------------------------------------
USHORT nLine = pTEParaPortion->GetLines().Count()-1;
for ( USHORT nL = 0; nL <= nLine; nL++ )
{
TextLine* pLine = pTEParaPortion->GetLines().GetObject( nL );
if ( pLine->GetEnd() > nInvalidStart )
{
nLine = nL;
break;
}
pLine->SetValid();
}
// Eine Zeile davor beginnen...
// Wenn ganz hinten getippt wird, kann sich die Zeile davor nicht aendern.
if ( nLine && ( !pTEParaPortion->IsSimpleInvalid() || ( nInvalidEnd < pNode->GetText().Len() ) || ( nInvalidDiff <= 0 ) ) )
nLine--;
TextLine* pLine = pTEParaPortion->GetLines().GetObject( nLine );
// ---------------------------------------------------------------
// Ab hier alle Zeilen durchformatieren...
// ---------------------------------------------------------------
USHORT nDelFromLine = 0xFFFF;
BOOL bLineBreak = FALSE;
USHORT nIndex = pLine->GetStart();
TextLine aSaveLine( *pLine );
Font aFont;
BOOL bCalcPortion = TRUE;
while ( nIndex < pNode->GetText().Len() )
{
BOOL bEOL = FALSE;
BOOL bEOC = FALSE;
USHORT nPortionStart = 0;
USHORT nPortionEnd = 0;
USHORT nTmpPos = nIndex;
USHORT nTmpPortion = pLine->GetStartPortion();
long nTmpWidth = mpDoc->GetLeftMargin();
// long nXWidth = mnMaxTextWidth ? ( mnMaxTextWidth - mpDoc->GetLeftMargin() ) : 0x7FFFFFFF;
// Margin nicht abziehen, ist schon in TmpWidth enthalten.
long nXWidth = mnMaxTextWidth ? mnMaxTextWidth : 0x7FFFFFFF;
if ( nXWidth < nTmpWidth )
nXWidth = nTmpWidth;
// Portion suchen, die nicht mehr in Zeile passt....
2002-08-12 14:36:21 +00:00
TETextPortion* pPortion;
2000-09-18 16:07:07 +00:00
BOOL bBrokenLine = FALSE;
bLineBreak = FALSE;
while ( ( nTmpWidth <= nXWidth ) && !bEOL && ( nTmpPortion < pTEParaPortion->GetTextPortions().Count() ) )
{
nPortionStart = nTmpPos;
pPortion = pTEParaPortion->GetTextPortions().GetObject( nTmpPortion );
DBG_ASSERT( pPortion->GetLen(), "Leere Portion in CreateLines ?!" );
if ( pNode->GetText().GetChar( nTmpPos ) == '\t' )
{
long nCurPos = nTmpWidth-mpDoc->GetLeftMargin();
nTmpWidth = ((nCurPos/mnDefTab)+1)*mnDefTab+mpDoc->GetLeftMargin();
pPortion->GetWidth() = nTmpWidth - nCurPos - mpDoc->GetLeftMargin();
// Wenn dies das erste Token in der Zeile ist, und
// nTmpWidth > aPaperSize.Width, habe ich eine Endlos-Schleife!
if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) )
{
// Aber was jetzt ? Tab passend machen!
pPortion->GetWidth() = nXWidth-1;
nTmpWidth = pPortion->GetWidth();
bEOL = TRUE;
bBrokenLine = TRUE;
}
pPortion->GetKind() = PORTIONKIND_TAB;
}
else
{
2002-08-12 14:36:21 +00:00
2000-09-18 16:07:07 +00:00
if ( bCalcPortion || !pPortion->HasValidSize() )
pPortion->GetWidth() = (long)CalcTextWidth( nPara, nTmpPos, pPortion->GetLen() );
nTmpWidth += pPortion->GetWidth();
2002-08-12 14:36:21 +00:00
pPortion->GetRightToLeft() = ImpGetRightToLeft( nPara, nTmpPos+1 );
pPortion->GetKind() = PORTIONKIND_TEXT;
2000-09-18 16:07:07 +00:00
}
nTmpPos += pPortion->GetLen();
nPortionEnd = nTmpPos;
nTmpPortion++;
}
// das war evtl. eine Portion zu weit:
BOOL bFixedEnd = FALSE;
if ( nTmpWidth > nXWidth )
{
nPortionEnd = nTmpPos;
nTmpPos -= pPortion->GetLen();
nPortionStart = nTmpPos;
nTmpPortion--;
bEOL = FALSE;
bEOC = FALSE;
nTmpWidth -= pPortion->GetWidth();
if ( pPortion->GetKind() == PORTIONKIND_TAB )
{
bEOL = TRUE;
bFixedEnd = TRUE;
}
}
else
{
bEOL = TRUE;
bEOC = TRUE;
pLine->SetEnd( nPortionEnd );
DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "Keine TextPortions?" );
pLine->SetEndPortion( (USHORT)pTEParaPortion->GetTextPortions().Count() - 1 );
}
if ( bFixedEnd )
{
pLine->SetEnd( nPortionStart );
pLine->SetEndPortion( nTmpPortion-1 );
}
else if ( bLineBreak || bBrokenLine )
{
pLine->SetEnd( nPortionStart+1 );
pLine->SetEndPortion( nTmpPortion-1 );
bEOC = FALSE; // wurde oben gesetzt, vielleich mal die if's umstellen?
}
else if ( !bEOL )
{
DBG_ASSERT( (nPortionEnd-nPortionStart) == pPortion->GetLen(), "Doch eine andere Portion?!" );
long nRemainingWidth = mnMaxTextWidth - nTmpWidth;
ImpBreakLine( nPara, pLine, pPortion, nPortionStart, nRemainingWidth );
}
2002-08-12 14:36:21 +00:00
if ( ( ImpGetAlign() == TXTALIGN_CENTER ) || ( ImpGetAlign() == TXTALIGN_RIGHT ) )
2000-09-18 16:07:07 +00:00
{
// Ausrichten...
long nTextWidth = 0;
for ( USHORT nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ )
{
2002-08-12 14:36:21 +00:00
TETextPortion* pTextPortion = pTEParaPortion->GetTextPortions().GetObject( nTP );
2000-09-18 16:07:07 +00:00
nTextWidth += pTextPortion->GetWidth();
}
long nSpace = mnMaxTextWidth - nTextWidth;
if ( nSpace > 0 )
{
2002-08-12 14:36:21 +00:00
if ( ImpGetAlign() == TXTALIGN_CENTER )
2000-09-18 16:07:07 +00:00
pLine->SetStartX( (USHORT)(nSpace / 2) );
else // TXTALIGN_RIGHT
pLine->SetStartX( (USHORT)nSpace );
}
}
else
{
pLine->SetStartX( mpDoc->GetLeftMargin() );
}
// -----------------------------------------------------------------
// pruefen, ob die Zeile neu ausgegeben werden muss...
// -----------------------------------------------------------------
pLine->SetInvalid();
if ( pTEParaPortion->IsSimpleInvalid() )
{
// Aenderung durch einfache Textaenderung...
// Formatierung nicht abbrechen, da Portions evtl. wieder
// gesplittet werden muessen!
// Wenn irgendwann mal abbrechbar, dann fogende Zeilen Validieren!
// Aber ggf. als Valid markieren, damit weniger Ausgabe...
if ( pLine->GetEnd() < nInvalidStart )
{
if ( *pLine == aSaveLine )
{
pLine->SetValid();
}
}
else
{
USHORT nStart = pLine->GetStart();
USHORT nEnd = pLine->GetEnd();
if ( nStart > nInvalidEnd )
{
if ( ( ( nStart-nInvalidDiff ) == aSaveLine.GetStart() ) &&
( ( nEnd-nInvalidDiff ) == aSaveLine.GetEnd() ) )
{
pLine->SetValid();
if ( bCalcPortion && bQuickFormat )
{
bCalcPortion = FALSE;
pTEParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
break;
}
}
}
else if ( bQuickFormat && ( nEnd > nInvalidEnd) )
{
// Wenn die ungueltige Zeile so endet, dass die naechste an
// der 'gleichen' Textstelle wie vorher beginnt, also nicht
// anders umgebrochen wird, brauche ich dort auch nicht die
// textbreiten neu bestimmen:
if ( nEnd == ( aSaveLine.GetEnd() + nInvalidDiff ) )
{
bCalcPortion = FALSE;
pTEParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
break;
}
}
}
}
nIndex = pLine->GetEnd(); // naechste Zeile Start = letzte Zeile Ende
// weil nEnd hinter das letzte Zeichen zeigt!
USHORT nEndPortion = pLine->GetEndPortion();
// Naechste Zeile oder ggf. neue Zeile....
pLine = 0;
if ( nLine < pTEParaPortion->GetLines().Count()-1 )
pLine = pTEParaPortion->GetLines().GetObject( ++nLine );
if ( pLine && ( nIndex >= pNode->GetText().Len() ) )
{
nDelFromLine = nLine;
break;
}
if ( !pLine && ( nIndex < pNode->GetText().Len() ) )
{
pLine = new TextLine;
pTEParaPortion->GetLines().Insert( pLine, ++nLine );
}
if ( pLine )
{
aSaveLine = *pLine;
pLine->SetStart( nIndex );
pLine->SetEnd( nIndex );
pLine->SetStartPortion( nEndPortion+1 );
pLine->SetEndPortion( nEndPortion+1 );
}
} // while ( Index < Len )
if ( nDelFromLine != 0xFFFF )
pTEParaPortion->GetLines().DeleteAndDestroy( nDelFromLine, pTEParaPortion->GetLines().Count() - nDelFromLine );
DBG_ASSERT( pTEParaPortion->GetLines().Count(), "Keine Zeile nach CreateLines!" );
if ( bLineBreak == TRUE )
CreateAndInsertEmptyLine( nPara );
pTEParaPortion->SetValid();
return nOldLineCount != pTEParaPortion->GetLines().Count();
}
String TextEngine::GetWord( const TextPaM& rCursorPos, TextPaM* pStartOfWord )
{
String aWord;
if ( rCursorPos.GetPara() < mpDoc->GetNodes().Count() )
{
TextSelection aSel( rCursorPos );
TextNode* pNode = mpDoc->GetNodes().GetObject( rCursorPos.GetPara() );
uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator();
i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), rCursorPos.GetIndex(), GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True );
2001-05-11 07:02:12 +00:00
aSel.GetStart().GetIndex() = (USHORT)aBoundary.startPos;
aSel.GetEnd().GetIndex() = (USHORT)aBoundary.endPos;
2000-09-18 16:07:07 +00:00
aWord = pNode->GetText().Copy( aSel.GetStart().GetIndex(), aSel.GetEnd().GetIndex() - aSel.GetStart().GetIndex() );
if ( pStartOfWord )
*pStartOfWord = aSel.GetStart();
}
return aWord;
}
BOOL TextEngine::Read( SvStream& rInput, const TextSelection* pSel )
{
BOOL bUpdate = GetUpdateMode();
SetUpdateMode( FALSE );
UndoActionStart( TEXTUNDO_READ );
TextSelection aSel;
if ( pSel )
aSel = *pSel;
else
{
ULONG nParas = mpDoc->GetNodes().Count();
TextNode* pNode = mpDoc->GetNodes().GetObject( nParas - 1 );
aSel = TextPaM( nParas-1 , pNode->GetText().Len() );
}
if ( aSel.HasRange() )
aSel = ImpDeleteText( aSel );
ByteString aLine;
BOOL bDone = rInput.ReadLine( aLine );
String aTmpStr( aLine, rInput.GetStreamCharSet() ), aStr;
while ( bDone )
{
aSel = ImpInsertText( aSel, aTmpStr );
bDone = rInput.ReadLine( aLine );
aTmpStr = String( aLine, rInput.GetStreamCharSet() );
if ( bDone )
aSel = ImpInsertParaBreak( aSel.GetEnd() );
}
UndoActionEnd( TEXTUNDO_READ );
TextSelection aNewSel( aSel.GetEnd(), aSel.GetEnd() );
// Damit bei FormatAndUpdate nicht auf die ungueltige Selektion zugegriffen wird.
if ( GetActiveView() )
2002-08-23 11:34:04 +00:00
GetActiveView()->ImpSetSelection( aNewSel );
2000-09-18 16:07:07 +00:00
SetUpdateMode( bUpdate );
FormatAndUpdate( GetActiveView() );
// if ( GetActiveView() )
// GetActiveView()->SetSelection( aNewSel );
return rInput.GetError() ? FALSE : TRUE;
}
#if SUPD < 540
BOOL TextEngine::Write( SvStream& rOutput, const TextSelection* pSel )
{
return Write( rOutput, pSel, FALSE );
}
#endif
BOOL TextEngine::Write( SvStream& rOutput, const TextSelection* pSel, BOOL bHTML )
{
TextSelection aSel;
if ( pSel )
aSel = *pSel;
else
{
ULONG nParas = mpDoc->GetNodes().Count();
TextNode* pNode = mpDoc->GetNodes().GetObject( nParas - 1 );
aSel.GetStart() = TextPaM( 0, 0 );
aSel.GetEnd() = TextPaM( nParas-1, pNode->GetText().Len() );
}
if ( bHTML )
{
rOutput.WriteLine( "<HTML>" );
rOutput.WriteLine( "<BODY>" );
}
for ( ULONG nPara = aSel.GetStart().GetPara(); nPara <= aSel.GetEnd().GetPara(); nPara++ )
{
TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
USHORT nStartPos = 0;
USHORT nEndPos = pNode->GetText().Len();
if ( nPara == aSel.GetStart().GetPara() )
nStartPos = aSel.GetStart().GetIndex();
if ( nPara == aSel.GetEnd().GetPara() )
nEndPos = aSel.GetEnd().GetIndex();
String aText;
if ( !bHTML )
{
aText = pNode->GetText().Copy( nStartPos, nEndPos-nStartPos );
}
else
{
aText.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "<P STYLE=\"margin-bottom: 0cm\">" ) );
if ( nStartPos == nEndPos )
{
// Leerzeilen werden von Writer wegoptimiert
aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "<BR>" ) );
}
else
{
USHORT nTmpStart = nStartPos;
USHORT nTmpEnd = nEndPos;
do
{
TextCharAttrib* pAttr = pNode->GetCharAttribs().FindNextAttrib( TEXTATTR_HYPERLINK, nTmpStart, nEndPos );
nTmpEnd = pAttr ? pAttr->GetStart() : nEndPos;
// Text vor dem Attribut
aText += pNode->GetText().Copy( nTmpStart, nTmpEnd-nTmpStart );
if ( pAttr )
{
nTmpEnd = Min( pAttr->GetEnd(), nEndPos );
// z.B. <A HREF="http://www.mopo.de/">Morgenpost</A>
aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "<A HREF=\"" ) );
aText += ((const TextAttribHyperLink&) pAttr->GetAttr() ).GetURL();
aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "\">" ) );
nTmpStart = pAttr->GetStart();
aText += pNode->GetText().Copy( nTmpStart, nTmpEnd-nTmpStart );
aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "</A>" ) );
nTmpStart = pAttr->GetEnd();
}
} while ( nTmpEnd < nEndPos );
}
aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "</P>" ) );
}
rOutput.WriteLine( ByteString( aText, rOutput.GetStreamCharSet() ) );
}
if ( bHTML )
{
rOutput.WriteLine( "</BODY>" );
rOutput.WriteLine( "</HTML>" );
}
return rOutput.GetError() ? FALSE : TRUE;
}
void TextEngine::RemoveAttribs( ULONG nPara )
{
if ( nPara < mpDoc->GetNodes().Count() )
{
TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
if ( pNode->GetCharAttribs().Count() )
{
pNode->GetCharAttribs().Clear( TRUE );
TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() );
mbFormatted = FALSE;
FormatAndUpdate( NULL );
}
}
}
void TextEngine::SetAttrib( const TextAttrib& rAttr, ULONG nPara, USHORT nStart, USHORT nEnd )
{
// Es wird hier erstmal nicht geprueft, ob sich Attribute ueberlappen!
// Diese Methode ist erstmal nur fuer einen Editor, der fuer eine Zeile
// _schnell_ das Syntax-Highlight einstellen will.
// Da die TextEngine z.Zt fuer Editoren gedacht ist gibt es auch kein
// Undo fuer Attribute!
if ( nPara < mpDoc->GetNodes().Count() )
{
TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
USHORT nMax = pNode->GetText().Len();
if ( nStart > nMax )
nStart = nMax;
if ( nEnd > nMax )
nEnd = nMax;
pNode->GetCharAttribs().InsertAttrib( new TextCharAttrib( rAttr, nStart, nEnd ) );
pTEParaPortion->MarkSelectionInvalid( nStart, nEnd );
mbFormatted = FALSE;
FormatAndUpdate( NULL );
}
}
void TextEngine::SetTextAlign( TxtAlign eAlign )
{
if ( eAlign != meAlign )
{
meAlign = eAlign;
FormatFullDoc();
2002-08-12 14:36:21 +00:00
UpdateViews();
2000-09-18 16:07:07 +00:00
}
}
void TextEngine::ValidateSelection( TextSelection& rSel ) const
{
ValidatePaM( rSel.GetStart() );
ValidatePaM( rSel.GetEnd() );
}
void TextEngine::ValidatePaM( TextPaM& rPaM ) const
{
ULONG nMaxPara = mpDoc->GetNodes().Count() - 1;
if ( rPaM.GetPara() > nMaxPara )
{
rPaM.GetPara() = nMaxPara;
rPaM.GetIndex() = 0xFFFF;
}
USHORT nMaxIndex = GetTextLen( rPaM.GetPara() );
if ( rPaM.GetIndex() > nMaxIndex )
rPaM.GetIndex() = nMaxIndex;
}
// Status & Selektionsanpassung
void TextEngine::ImpParagraphInserted( ULONG nPara )
{
// Die aktive View braucht nicht angepasst werden, aber bei allen
// passiven muss die Selektion angepasst werden:
if ( mpViews->Count() > 1 )
{
for ( USHORT nView = mpViews->Count(); nView; )
{
TextView* pView = mpViews->GetObject( --nView );
if ( pView != GetActiveView() )
{
// BOOL bInvers = pView->maSelection.GetEnd() < pView->maSelection.GetStart();
// TextPaM& rMin = !bInvers ? pView->maSelection.GetStart(): pView->maSelection.GetEnd();
// TextPaM& rMax = bInvers ? pView->maSelection.GetStart() : pView->maSelection.GetEnd();
//
// if ( rMin.GetPara() >= nPara )
// rMin.GetPara()++;
// if ( rMax.GetPara() >= nPara )
// rMax.GetPara()++;
for ( int n = 0; n <= 1; n++ )
{
TextPaM& rPaM = n ? pView->maSelection.GetStart(): pView->maSelection.GetEnd();
if ( rPaM.GetPara() >= nPara )
rPaM.GetPara()++;
}
}
}
}
Broadcast( TextHint( TEXT_HINT_PARAINSERTED, nPara ) );
}
void TextEngine::ImpParagraphRemoved( ULONG nPara )
{
if ( mpViews->Count() > 1 )
{
for ( USHORT nView = mpViews->Count(); nView; )
{
TextView* pView = mpViews->GetObject( --nView );
if ( pView != GetActiveView() )
{
ULONG nParas = mpDoc->GetNodes().Count();
for ( int n = 0; n <= 1; n++ )
{
TextPaM& rPaM = n ? pView->maSelection.GetStart(): pView->maSelection.GetEnd();
if ( rPaM.GetPara() > nPara )
rPaM.GetPara()--;
else if ( rPaM.GetPara() == nPara )
{
rPaM.GetIndex() = 0;
if ( rPaM.GetPara() >= nParas )
rPaM.GetPara()--;
}
}
}
}
}
Broadcast( TextHint( TEXT_HINT_PARAREMOVED, nPara ) );
}
void TextEngine::ImpCharsRemoved( ULONG nPara, USHORT nPos, USHORT nChars )
{
if ( mpViews->Count() > 1 )
{
for ( USHORT nView = mpViews->Count(); nView; )
{
TextView* pView = mpViews->GetObject( --nView );
if ( pView != GetActiveView() )
{
USHORT nEnd = nPos+nChars;
for ( int n = 0; n <= 1; n++ )
{
TextPaM& rPaM = n ? pView->maSelection.GetStart(): pView->maSelection.GetEnd();
if ( rPaM.GetPara() == nPara )
{
if ( rPaM.GetIndex() > nEnd )
rPaM.GetIndex() -= nChars;
else if ( rPaM.GetIndex() > nPos )
rPaM.GetIndex() = nPos;
}
}
}
}
}
}
void TextEngine::ImpCharsInserted( ULONG nPara, USHORT nPos, USHORT nChars )
{
if ( mpViews->Count() > 1 )
{
for ( USHORT nView = mpViews->Count(); nView; )
{
TextView* pView = mpViews->GetObject( --nView );
if ( pView != GetActiveView() )
{
for ( int n = 0; n <= 1; n++ )
{
TextPaM& rPaM = n ? pView->maSelection.GetStart(): pView->maSelection.GetEnd();
if ( rPaM.GetPara() == nPara )
{
if ( rPaM.GetIndex() >= nPos )
rPaM.GetIndex() += nChars;
}
}
}
}
}
}
void TextEngine::ImpFormattingParagraph( ULONG nPara )
{
Broadcast( TextHint( TEXT_HINT_FORMATPARA, nPara ) );
}
void TextEngine::ImpTextHeightChanged()
{
Broadcast( TextHint( TEXT_HINT_TEXTHEIGHTCHANGED ) );
}
void TextEngine::ImpTextFormatted()
{
Broadcast( TextHint( TEXT_HINT_TEXTFORMATTED ) );
}
void TextEngine::Draw( OutputDevice* pDev, const Point& rPos )
{
ImpPaint( pDev, rPos, NULL );
}
void TextEngine::SetLeftMargin( USHORT n )
{
mpDoc->SetLeftMargin( n );
}
USHORT TextEngine::GetLeftMargin() const
{
return mpDoc->GetLeftMargin();
}
uno::Reference< i18n::XBreakIterator > TextEngine::GetBreakIterator()
2000-09-18 16:07:07 +00:00
{
if ( !mxBreakIterator.is() )
mxBreakIterator = vcl::unohelper::CreateBreakIterator();
2002-05-29 09:38:46 +00:00
DBG_ASSERT( mxBreakIterator.is(), "Could not create BreakIterator" );
2000-09-18 16:07:07 +00:00
return mxBreakIterator;
}
2002-05-17 10:48:47 +00:00
void TextEngine::SetLocale( const ::com::sun::star::lang::Locale& rLocale )
{
maLocale = rLocale;
2002-10-17 16:16:28 +00:00
delete mpLocaleDataWrapper;
mpLocaleDataWrapper = NULL;
2002-05-17 10:48:47 +00:00
}
2000-09-18 16:07:07 +00:00
::com::sun::star::lang::Locale TextEngine::GetLocale()
{
if ( !maLocale.Language.getLength() )
{
2002-10-17 16:16:28 +00:00
maLocale = Application::GetSettings().GetUILocale();
2000-09-18 16:07:07 +00:00
}
return maLocale;
}
2002-08-12 14:36:21 +00:00
2002-10-17 16:16:28 +00:00
LocaleDataWrapper* TextEngine::ImpGetLocaleDataWrapper()
{
if ( !mpLocaleDataWrapper )
mpLocaleDataWrapper = new LocaleDataWrapper( vcl::unohelper::GetMultiServiceFactory(), GetLocale() );
return mpLocaleDataWrapper;
}
2002-08-12 14:36:21 +00:00
void TextEngine::SetRightToLeft( BOOL bR2L )
{
if ( mbRightToLeft != bR2L )
{
mbRightToLeft = bR2L;
2002-08-14 12:09:49 +00:00
meAlign = bR2L ? TXTALIGN_RIGHT : TXTALIGN_LEFT;
2002-08-12 14:36:21 +00:00
FormatFullDoc();
UpdateViews();
}
}
void TextEngine::ImpInitWritingDirections( ULONG nPara )
{
TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
TEWritingDirectionInfos& rInfos = pParaPortion->GetWritingDirectionInfos();
rInfos.Remove( 0, rInfos.Count() );
if ( pParaPortion->GetNode()->GetText().Len() )
{
const BYTE nDefaultDir = IsRightToLeft() ? UBIDI_RTL : UBIDI_LTR;
String aText( pParaPortion->GetNode()->GetText() );
//
// 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 );
UTextOffset nStart = 0;
UTextOffset nEnd;
UBiDiLevel nCurrDir;
for ( USHORT nIdx = 0; nIdx < nCount; ++nIdx )
{
ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir );
rInfos.Insert( TEWritingDirectionInfo( 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( TEWritingDirectionInfo( 0, 0, (USHORT)pParaPortion->GetNode()->GetText().Len() ), rInfos.Count() );
}
BYTE TextEngine::ImpGetRightToLeft( ULONG nPara, USHORT nPos, USHORT* pStart, USHORT* pEnd )
{
BYTE nRightToLeft = 0;
TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
if ( pNode && pNode->GetText().Len() )
{
TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
if ( !pParaPortion->GetWritingDirectionInfos().Count() )
ImpInitWritingDirections( nPara );
BYTE nType = 0;
TEWritingDirectionInfos& rDirInfos = pParaPortion->GetWritingDirectionInfos();
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;
}
long TextEngine::ImpGetPortionXOffset( ULONG nPara, TextLine* pLine, USHORT nTextPortion )
{
long nX = pLine->GetStartX();
TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
for ( USHORT i = pLine->GetStartPortion(); i < nTextPortion; i++ )
{
TETextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( i );
nX += pPortion->GetWidth();
}
TETextPortion* pDestPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
2002-08-13 13:07:14 +00:00
if ( pDestPortion->GetKind() != PORTIONKIND_TAB )
2002-08-12 14:36:21 +00:00
{
2002-08-13 13:07:14 +00:00
if ( !IsRightToLeft() && pDestPortion->GetRightToLeft() )
2002-08-12 14:36:21 +00:00
{
2002-08-13 13:07:14 +00:00
// Portions behind must be added, visual before this portion
sal_uInt16 nTmpPortion = nTextPortion+1;
while ( nTmpPortion <= pLine->GetEndPortion() )
{
TETextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
if ( pNextTextPortion->GetRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
nX += pNextTextPortion->GetWidth();
else
break;
nTmpPortion++;
}
// Portions before must be removed, visual behind this portion
nTmpPortion = nTextPortion;
while ( nTmpPortion > pLine->GetStartPortion() )
{
--nTmpPortion;
TETextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
if ( pPrevTextPortion->GetRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
nX -= pPrevTextPortion->GetWidth();
else
break;
}
2002-08-12 14:36:21 +00:00
}
2002-08-13 13:07:14 +00:00
else if ( IsRightToLeft() && !pDestPortion->IsRightToLeft() )
2002-08-12 14:36:21 +00:00
{
2002-08-13 13:07:14 +00:00
// Portions behind must be removed, visual behind this portion
sal_uInt16 nTmpPortion = nTextPortion+1;
while ( nTmpPortion <= pLine->GetEndPortion() )
{
TETextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
if ( !pNextTextPortion->IsRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
nX += pNextTextPortion->GetWidth();
else
break;
nTmpPortion++;
}
// Portions before must be added, visual before this portion
nTmpPortion = nTextPortion;
while ( nTmpPortion > pLine->GetStartPortion() )
{
--nTmpPortion;
TETextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
if ( !pPrevTextPortion->IsRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
nX -= pPrevTextPortion->GetWidth();
else
break;
}
2002-08-12 14:36:21 +00:00
}
}
/*
if ( IsRightToLeft() )
{
// Switch X postions...
DBG_ASSERT( GetMaxTextWidth(), "GetPortionXOffset - max text width?!" );
DBG_ASSERT( nX <= (long)GetMaxTextWidth(), "GetPortionXOffset - position out of paper size!" );
nX = GetMaxTextWidth() - nX;
nX -= pDestPortion->GetWidth();
}
*/
return nX;
}
void TextEngine::ImpInitLayoutMode( OutputDevice* pOutDev, BOOL bDrawingR2LPortion )
{
ULONG nLayoutMode = pOutDev->GetLayoutMode();
nLayoutMode &= ~(TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_COMPLEX_DISABLED | TEXT_LAYOUT_BIDI_STRONG );
if ( bDrawingR2LPortion )
nLayoutMode |= TEXT_LAYOUT_BIDI_RTL;
pOutDev->SetLayoutMode( nLayoutMode );
}
TxtAlign TextEngine::ImpGetAlign() const
{
TxtAlign eAlign = meAlign;
if ( IsRightToLeft() )
{
if ( eAlign == TXTALIGN_LEFT )
eAlign = TXTALIGN_RIGHT;
else if ( eAlign == TXTALIGN_RIGHT )
eAlign = TXTALIGN_LEFT;
}
return eAlign;
}
long TextEngine::ImpGetOutputOffset( ULONG nPara, TextLine* pLine, USHORT nIndex, USHORT nIndex2 )
{
TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
USHORT nPortionStart;
USHORT nPortion = pPortion->GetTextPortions().FindPortion( nIndex, nPortionStart, TRUE );
TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( nPortion );
long nX;
if ( ( nIndex == nPortionStart ) && ( nIndex == nIndex2 ) )
{
// Output of full portion, so we need portion x offset.
// Use ImpGetPortionXOffset, because GetXPos may deliver left or right position from portioon, depending on R2L, L2R
nX = ImpGetPortionXOffset( nPara, pLine, nPortion );
if ( IsRightToLeft() )
{
nX = -nX -pTextPortion->GetWidth();
}
}
else
{
nX = ImpGetXPos( nPara, pLine, nIndex, nIndex == nPortionStart );
if ( nIndex2 != nIndex )
{
long nX2 = ImpGetXPos( nPara, pLine, nIndex2, FALSE );
if ( ( !IsRightToLeft() && ( nX2 < nX ) ) ||
( IsRightToLeft() && ( nX2 > nX ) ) )
{
nX = nX2;
}
}
if ( IsRightToLeft() )
{
nX = -nX;
}
}
return nX;
2002-10-17 08:50:51 +00:00
}