xref: /aoo41x/main/svtools/source/edit/texteng.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_svtools.hxx"
30 
31 
32 #include <tools/stream.hxx>
33 
34 #include <svtools/texteng.hxx>
35 #include <svtools/textview.hxx>
36 #include <textdoc.hxx>
37 #include <textdat2.hxx>
38 #include <textundo.hxx>
39 #include <textund2.hxx>
40 #include <svl/ctloptions.hxx>
41 #include <vcl/window.hxx>
42 
43 #include <vcl/edit.hxx>
44 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
45 #include <com/sun/star/beans/PropertyValues.hpp>
46 
47 #ifndef _COM_SUN_STAR_TEXT_XBREAKITERATOR_HPP_
48 #include <com/sun/star/i18n/XBreakIterator.hpp>
49 #endif
50 
51 #ifndef _COM_SUN_STAR_TEXT_CHARACTERITERATORMODE_HPP_
52 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
53 #endif
54 
55 #ifndef _COM_SUN_STAR_TEXT_WORDTYPE_HPP_
56 #include <com/sun/star/i18n/WordType.hpp>
57 #endif
58 
59 #ifndef _COM_SUN_STAR_I18N_XEXTENDEDINPUTSEQUENCECHECKER_HDL_
60 #include <com/sun/star/i18n/XExtendedInputSequenceChecker.hpp>
61 #endif
62 #include <com/sun/star/i18n/InputSequenceCheckMode.hpp>
63 #include <com/sun/star/i18n/ScriptType.hpp>
64 
65 #include <comphelper/processfactory.hxx>
66 
67 #include <unotools/localedatawrapper.hxx>
68 #include <vcl/unohelp.hxx>
69 
70 #include <vcl/svapp.hxx>
71 #include <vcl/unohelp.hxx>
72 #include <vcl/metric.hxx>
73 
74 #include <unicode/ubidi.h>
75 
76 using namespace ::com::sun::star;
77 using namespace ::com::sun::star::uno;
78 using namespace ::rtl;
79 
80 typedef TextView* TextViewPtr;
81 SV_DECL_PTRARR( TextViews, TextViewPtr, 0, 1 )
82 // SV_IMPL_PTRARR( TextViews, TextViewPtr );
83 
84 SV_DECL_VARARR_SORT( TESortedPositions, sal_uLong, 16, 8 )
85 SV_IMPL_VARARR_SORT( TESortedPositions, sal_uLong )
86 
87 #define RESDIFF		10
88 #define SCRLRANGE	20		// 1/20 der Breite/Hoehe scrollen, wenn im QueryDrop
89 
90 
91 // -------------------------------------------------------------------------
92 // (-) class TextEngine
93 // -------------------------------------------------------------------------
94 TextEngine::TextEngine()
95 {
96 	mpDoc = 0;
97 	mpTEParaPortions = 0;
98 
99 	mpViews = new TextViews;
100 	mpActiveView = NULL;
101 
102 	mbIsFormatting		= sal_False;
103 	mbFormatted			= sal_False;
104 	mbUpdate 			= sal_True;
105 	mbModified			= sal_False;
106 	mbUndoEnabled 		= sal_False;
107 	mbIsInUndo			= sal_False;
108 	mbDowning			= sal_False;
109     mbRightToLeft		= sal_False;
110 	mbHasMultiLineParas = sal_False;
111 
112 	meAlign			= TXTALIGN_LEFT;
113 
114 	mnMaxTextWidth	= 0;
115 	mnMaxTextLen 	= 0;
116 	mnCurTextWidth	= 0xFFFFFFFF;
117 	mnCurTextHeight	= 0;
118 
119 	mpUndoManager 	= NULL;
120    	mpIMEInfos		= NULL;
121     mpLocaleDataWrapper = NULL;
122 
123     mpIdleFormatter = new IdleFormatter;
124 	mpIdleFormatter->SetTimeoutHdl( LINK( this, TextEngine, IdleFormatHdl ) );
125 
126 	mpRefDev = new VirtualDevice;
127 
128     ImpInitLayoutMode( mpRefDev );
129 
130 	ImpInitDoc();
131 
132     maTextColor = COL_BLACK;
133 	Font aFont;
134 	aFont.SetTransparent( sal_False );
135 	Color aFillColor( aFont.GetFillColor() );
136 	aFillColor.SetTransparency( 0 );
137 	aFont.SetFillColor( aFillColor );
138 	SetFont( aFont );
139 }
140 
141 TextEngine::~TextEngine()
142 {
143 	mbDowning = sal_True;
144 
145 	delete mpIdleFormatter;
146 	delete mpDoc;
147 	delete mpTEParaPortions;
148 	delete mpViews;	// nur die Liste, nicht die Vies
149 	delete mpRefDev;
150 	delete mpUndoManager;
151 	delete mpIMEInfos;
152     delete mpLocaleDataWrapper;
153 }
154 
155 void TextEngine::InsertView( TextView* pTextView )
156 {
157 	mpViews->Insert( pTextView, mpViews->Count() );
158 	pTextView->SetSelection( TextSelection() );
159 
160 	if ( !GetActiveView() )
161 		SetActiveView( pTextView );
162 }
163 
164 void TextEngine::RemoveView( TextView* pTextView )
165 {
166 	sal_uInt16 nPos = mpViews->GetPos( pTextView );
167 	if( nPos != USHRT_MAX )
168 	{
169 		pTextView->HideCursor();
170 		mpViews->Remove( nPos, 1 );
171 		if ( pTextView == GetActiveView() )
172 			SetActiveView( 0 );
173 	}
174 }
175 
176 sal_uInt16 TextEngine::GetViewCount() const
177 {
178 	return mpViews->Count();
179 }
180 
181 TextView* TextEngine::GetView( sal_uInt16 nView ) const
182 {
183 	return mpViews->GetObject( nView );
184 }
185 
186 TextView* TextEngine::GetActiveView() const
187 {
188 	return mpActiveView;
189 }
190 
191 void TextEngine::SetActiveView( TextView* pTextView )
192 {
193 	if ( pTextView != mpActiveView )
194 	{
195 		if ( mpActiveView )
196 			mpActiveView->HideSelection();
197 
198 		mpActiveView = pTextView;
199 
200 		if ( mpActiveView )
201 			mpActiveView->ShowSelection();
202 	}
203 }
204 
205 void TextEngine::SetFont( const Font& rFont )
206 {
207 	if ( rFont != maFont )
208 	{
209 		maFont = rFont;
210         // #i40221# As the font's color now defaults to transparent (since i35764)
211         //  we have to choose a useful textcolor in this case.
212         // Otherwise maTextColor and maFont.GetColor() are both transparent....
213 		if( rFont.GetColor() == COL_TRANSPARENT )
214 		    maTextColor = COL_BLACK;
215         else
216             maTextColor = rFont.GetColor();
217 
218 		// Wegen Selektion keinen Transparenten Font zulassen...
219 		// (Sonst spaeter in ImplPaint den Hintergrund anders loeschen...)
220 		maFont.SetTransparent( sal_False );
221 		// Tell VCL not to use the font color, use text color from OutputDevice
222 		maFont.SetColor( COL_TRANSPARENT );
223 		Color aFillColor( maFont.GetFillColor() );
224 		aFillColor.SetTransparency( 0 );
225 		maFont.SetFillColor( aFillColor );
226 
227 		maFont.SetAlign( ALIGN_TOP );
228 		mpRefDev->SetFont( maFont);
229 		Size aTextSize;
230 		aTextSize.Width() = mpRefDev->GetTextWidth( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "    " ) ) );
231 		aTextSize.Height() = mpRefDev->GetTextHeight();
232 		if ( !aTextSize.Width() )
233 			aTextSize.Width() = mpRefDev->GetTextWidth( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "XXXX" ) ) );
234 
235 		mnDefTab = (sal_uInt16)aTextSize.Width();
236 		if ( !mnDefTab )
237 			mnDefTab = 1;
238 		mnCharHeight = (sal_uInt16)aTextSize.Height();
239 /*
240         // #93746# Doesn't work with CJK HalfWidth/FullWidth
241         FontMetric aRealFont( mpRefDev->GetFontMetric() );
242 		if ( aRealFont.GetPitch() == PITCH_FIXED )
243 		{
244 			String aX100;
245 			aX100.Fill( 100, 'X' );
246 			mnFixCharWidth100 = (sal_uInt16)mpRefDev->GetTextWidth( aX100 );
247 		}
248 		else
249 */
250 		mnFixCharWidth100 = 0;
251 
252 		FormatFullDoc();
253 		UpdateViews();
254 
255 		for ( sal_uInt16 nView = mpViews->Count(); nView; )
256 		{
257 			TextView* pView = mpViews->GetObject( --nView );
258             pView->GetWindow()->SetInputContext( InputContext( GetFont(), !pView->IsReadOnly() ? INPUTCONTEXT_TEXT|INPUTCONTEXT_EXTTEXTINPUT : 0 ) );
259         }
260 	}
261 }
262 
263 void TextEngine::SetDefTab( sal_uInt16 nDefTab )
264 {
265 	mnDefTab = nDefTab;
266 	// evtl neu setzen?
267 }
268 
269 void TextEngine::SetMaxTextLen( sal_uLong nLen )
270 {
271 	mnMaxTextLen = nLen;
272 }
273 
274 void TextEngine::SetMaxTextWidth( sal_uLong nMaxWidth )
275 {
276 	if ( nMaxWidth != mnMaxTextWidth )
277 	{
278 		mnMaxTextWidth = Min( nMaxWidth, (sal_uLong)0x7FFFFFFF );
279 		FormatFullDoc();
280 		UpdateViews();
281 	}
282 }
283 
284 static sal_Unicode static_aLFText[] = { '\n', 0 };
285 static sal_Unicode static_aCRText[] = { '\r', 0 };
286 static sal_Unicode static_aCRLFText[] = { '\r', '\n', 0 };
287 
288 static inline const sal_Unicode* static_getLineEndText( LineEnd aLineEnd )
289 {
290 	const sal_Unicode* pRet = NULL;
291 
292 	switch( aLineEnd )
293 	{
294 	case LINEEND_LF: pRet = static_aLFText;break;
295 	case LINEEND_CR: pRet = static_aCRText;break;
296 	case LINEEND_CRLF: pRet = static_aCRLFText;break;
297 	}
298 	return pRet;
299 }
300 
301 void  TextEngine::ReplaceText(const TextSelection& rSel, const String& rText)
302 {
303     ImpInsertText( rSel, rText );
304 }
305 
306 String TextEngine::GetText( LineEnd aSeparator ) const
307 {
308 	return mpDoc->GetText( static_getLineEndText( aSeparator ) );
309 }
310 
311 String TextEngine::GetTextLines( LineEnd aSeparator ) const
312 {
313 	String aText;
314 	sal_uLong nParas = mpTEParaPortions->Count();
315 	const sal_Unicode* pSep = static_getLineEndText( aSeparator );
316 	for ( sal_uLong nP = 0; nP < nParas; nP++ )
317 	{
318 		TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nP );
319 
320 		sal_uInt16 nLines = pTEParaPortion->GetLines().Count();
321 		for ( sal_uInt16 nL = 0; nL < nLines; nL++ )
322 		{
323 			TextLine* pLine = pTEParaPortion->GetLines()[nL];
324 			aText += pTEParaPortion->GetNode()->GetText().Copy( pLine->GetStart(), pLine->GetEnd() - pLine->GetStart() );
325 			if ( pSep && ( ( (nP+1) < nParas ) || ( (nL+1) < nLines ) ) )
326 				aText += pSep;
327 		}
328 	}
329 	return aText;
330 }
331 
332 String TextEngine::GetText( sal_uLong nPara ) const
333 {
334 	return mpDoc->GetText( nPara );
335 }
336 
337 sal_uLong TextEngine::GetTextLen( LineEnd aSeparator ) const
338 {
339 	return mpDoc->GetTextLen( static_getLineEndText( aSeparator ) );
340 }
341 
342 sal_uLong TextEngine::GetTextLen( const TextSelection& rSel, LineEnd aSeparator ) const
343 {
344 	TextSelection aSel( rSel );
345 	aSel.Justify();
346 	ValidateSelection( aSel );
347 	return mpDoc->GetTextLen( static_getLineEndText( aSeparator ), &aSel );
348 }
349 
350 sal_uInt16 TextEngine::GetTextLen( sal_uLong nPara ) const
351 {
352 	return mpDoc->GetNodes().GetObject( nPara )->GetText().Len();
353 }
354 
355 void TextEngine::SetUpdateMode( sal_Bool bUpdate )
356 {
357 	if ( bUpdate != mbUpdate )
358 	{
359 		mbUpdate = bUpdate;
360 		if ( mbUpdate )
361 		{
362 			FormatAndUpdate( GetActiveView() );
363 			if ( GetActiveView() )
364 				GetActiveView()->ShowCursor();
365 		}
366 	}
367 }
368 
369 sal_Bool TextEngine::DoesKeyMoveCursor( const KeyEvent& rKeyEvent )
370 {
371 	sal_Bool bDoesMove = sal_False;
372 
373 	switch ( rKeyEvent.GetKeyCode().GetCode() )
374 	{
375 		case KEY_UP:
376 		case KEY_DOWN:
377 		case KEY_LEFT:
378 		case KEY_RIGHT:
379 		case KEY_HOME:
380 		case KEY_END:
381 		case KEY_PAGEUP:
382 		case KEY_PAGEDOWN:
383 		{
384 			if ( !rKeyEvent.GetKeyCode().IsMod2() )
385 				bDoesMove = sal_True;
386 		}
387 		break;
388 	}
389 	return bDoesMove;
390 }
391 
392 sal_Bool TextEngine::DoesKeyChangeText( const KeyEvent& rKeyEvent )
393 {
394 	sal_Bool bDoesChange = sal_False;
395 
396 	KeyFuncType eFunc = rKeyEvent.GetKeyCode().GetFunction();
397 	if ( eFunc != KEYFUNC_DONTKNOW )
398 	{
399 		switch ( eFunc )
400 		{
401 			case KEYFUNC_UNDO:
402 			case KEYFUNC_REDO:
403 			case KEYFUNC_CUT:
404 			case KEYFUNC_PASTE:	bDoesChange = sal_True;
405 			break;
406 			default:	// wird dann evtl. unten bearbeitet.
407 						eFunc = KEYFUNC_DONTKNOW;
408 		}
409 	}
410 	if ( eFunc == KEYFUNC_DONTKNOW )
411 	{
412 		switch ( rKeyEvent.GetKeyCode().GetCode() )
413 		{
414 			case KEY_DELETE:
415 			case KEY_BACKSPACE:
416 			{
417 				if ( !rKeyEvent.GetKeyCode().IsMod2() )
418 					bDoesChange = sal_True;
419 			}
420 			break;
421 			case KEY_RETURN:
422 			case KEY_TAB:
423 			{
424 				if ( !rKeyEvent.GetKeyCode().IsMod1() && !rKeyEvent.GetKeyCode().IsMod2() )
425 					bDoesChange = sal_True;
426 			}
427 			break;
428 			default:
429 			{
430 				bDoesChange = TextEngine::IsSimpleCharInput( rKeyEvent );
431 			}
432 		}
433 	}
434 	return bDoesChange;
435 }
436 
437 sal_Bool TextEngine::IsSimpleCharInput( const KeyEvent& rKeyEvent )
438 {
439 	if( rKeyEvent.GetCharCode() >= 32 && rKeyEvent.GetCharCode() != 127 &&
440         KEY_MOD1 != (rKeyEvent.GetKeyCode().GetModifier() & ~KEY_SHIFT) && // (ssa) #i45714#:
441         KEY_MOD2 != (rKeyEvent.GetKeyCode().GetModifier() & ~KEY_SHIFT) )  // check for Ctrl and Alt separately
442 	{
443 		return sal_True;
444 	}
445 	return sal_False;
446 }
447 
448 void TextEngine::ImpInitDoc()
449 {
450 	if ( mpDoc )
451 		mpDoc->Clear();
452 	else
453 		mpDoc = new TextDoc;
454 
455 	delete mpTEParaPortions;
456 	mpTEParaPortions = new TEParaPortions;
457 
458 	TextNode* pNode = new TextNode( String() );
459 	mpDoc->GetNodes().Insert( pNode, 0 );
460 
461 	TEParaPortion* pIniPortion = new TEParaPortion( pNode );
462 	mpTEParaPortions->Insert( pIniPortion, (sal_uLong)0 );
463 
464 	mbFormatted = sal_False;
465 
466 	ImpParagraphRemoved( TEXT_PARA_ALL );
467 	ImpParagraphInserted( 0 );
468 }
469 
470 String TextEngine::GetText( const TextSelection& rSel, LineEnd aSeparator ) const
471 {
472 	String aText;
473 
474 	if ( !rSel.HasRange() )
475 		return aText;
476 
477 	TextSelection aSel( rSel );
478 	aSel.Justify();
479 
480 	sal_uLong nStartPara = aSel.GetStart().GetPara();
481 	sal_uLong nEndPara = aSel.GetEnd().GetPara();
482 	const sal_Unicode* pSep = static_getLineEndText( aSeparator );
483 	for ( sal_uLong nNode = aSel.GetStart().GetPara(); nNode <= nEndPara; nNode++ )
484 	{
485 		TextNode* pNode = mpDoc->GetNodes().GetObject( nNode );
486 
487 		sal_uInt16 nStartPos = 0;
488 		sal_uInt16 nEndPos = pNode->GetText().Len();
489 		if ( nNode == nStartPara )
490 			nStartPos = aSel.GetStart().GetIndex();
491 		if ( nNode == nEndPara ) // kann auch == nStart sein!
492 			nEndPos = aSel.GetEnd().GetIndex();
493 
494 		aText += pNode->GetText().Copy( nStartPos, nEndPos-nStartPos );
495 		if ( nNode < nEndPara )
496 			aText += pSep;
497 	}
498 	return aText;
499 }
500 
501 void TextEngine::ImpRemoveText()
502 {
503 	ImpInitDoc();
504 
505 	TextPaM aStartPaM( 0, 0 );
506 	TextSelection aEmptySel( aStartPaM, aStartPaM );
507 	for ( sal_uInt16 nView = 0; nView < mpViews->Count(); nView++ )
508 	{
509 		TextView* pView = mpViews->GetObject( nView );
510 		pView->ImpSetSelection( aEmptySel );
511 	}
512 	ResetUndo();
513 }
514 
515 void TextEngine::SetText( const XubString& rText )
516 {
517 	ImpRemoveText();
518 
519 	sal_Bool bUndoCurrentlyEnabled = IsUndoEnabled();
520 	// Der von Hand reingesteckte Text kann nicht vom Anwender rueckgaengig gemacht werden.
521 	EnableUndo( sal_False );
522 
523 	TextPaM aStartPaM( 0, 0 );
524 	TextSelection aEmptySel( aStartPaM, aStartPaM );
525 
526 	TextPaM aPaM = aStartPaM;
527 	if ( rText.Len() )
528 		aPaM = ImpInsertText( aEmptySel, rText );
529 
530 	for ( sal_uInt16 nView = 0; nView < mpViews->Count(); nView++ )
531 	{
532 		TextView* pView = mpViews->GetObject( nView );
533 		pView->ImpSetSelection( aEmptySel );
534 
535 		// Wenn kein Text, dann auch Kein Format&Update
536 		// => Der Text bleibt stehen.
537 		if ( !rText.Len() && GetUpdateMode() )
538 			pView->Invalidate();
539 	}
540 
541 	if( !rText.Len() )	// sonst muss spaeter noch invalidiert werden, !bFormatted reicht.
542 		mnCurTextHeight = 0;
543 
544 	FormatAndUpdate();
545 
546 	EnableUndo( bUndoCurrentlyEnabled );
547 	DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "Undo nach SetText?" );
548 }
549 
550 
551 void TextEngine::CursorMoved( sal_uLong nNode )
552 {
553 	// Leere Attribute loeschen, aber nur, wenn Absatz nicht leer!
554 	TextNode* pNode = mpDoc->GetNodes().GetObject( nNode );
555 	if ( pNode && pNode->GetCharAttribs().HasEmptyAttribs() && pNode->GetText().Len() )
556 		pNode->GetCharAttribs().DeleteEmptyAttribs();
557 }
558 
559 void TextEngine::ImpRemoveChars( const TextPaM& rPaM, sal_uInt16 nChars, SfxUndoAction* )
560 {
561 	DBG_ASSERT( nChars, "ImpRemoveChars - 0 Chars?!" );
562 	if ( IsUndoEnabled() && !IsInUndo() )
563 	{
564 		// Attribute muessen hier vorm RemoveChars fuer UNDO gesichert werden!
565 		TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
566 		XubString aStr( pNode->GetText().Copy( rPaM.GetIndex(), nChars ) );
567 
568 		// Pruefen, ob Attribute geloescht oder geaendert werden:
569 		sal_uInt16 nStart = rPaM.GetIndex();
570 		sal_uInt16 nEnd = nStart + nChars;
571 		for ( sal_uInt16 nAttr = pNode->GetCharAttribs().Count(); nAttr; )
572 		{
573 			TextCharAttrib* pAttr = pNode->GetCharAttribs().GetAttrib( --nAttr );
574 			if ( ( pAttr->GetEnd() >= nStart ) && ( pAttr->GetStart() < nEnd ) )
575 			{
576 //				TextSelection aSel( rPaM );
577 //				aSel.GetEnd().GetIndex() += nChars;
578 //				TextUndoSetAttribs* pAttrUndo = CreateAttribUndo( aSel );
579 //				InsertUndo( pAttrUndo );
580 				break;	// for
581 			}
582 		}
583 //		if ( pCurUndo && ( CreateTextPaM( pCurUndo->GetEPaM() ) == rPaM ) )
584 //			pCurUndo->GetStr() += aStr;
585 //		else
586 			InsertUndo( new TextUndoRemoveChars( this, rPaM, aStr ) );
587 	}
588 
589 	mpDoc->RemoveChars( rPaM, nChars );
590 	ImpCharsRemoved( rPaM.GetPara(), rPaM.GetIndex(), nChars );
591 }
592 
593 TextPaM TextEngine::ImpConnectParagraphs( sal_uLong nLeft, sal_uLong nRight )
594 {
595 	DBG_ASSERT( nLeft != nRight, "Den gleichen Absatz zusammenfuegen ?" );
596 
597 	TextNode* pLeft = mpDoc->GetNodes().GetObject( nLeft );
598 	TextNode* pRight = mpDoc->GetNodes().GetObject( nRight );
599 
600 	if ( IsUndoEnabled() && !IsInUndo() )
601 		InsertUndo( new TextUndoConnectParas( this, nLeft, pLeft->GetText().Len() ) );
602 
603 	// Erstmal Portions suchen, da pRight nach ConnectParagraphs weg.
604 	TEParaPortion* pLeftPortion = mpTEParaPortions->GetObject( nLeft );
605 	TEParaPortion* pRightPortion = mpTEParaPortions->GetObject( nRight );
606 	DBG_ASSERT( pLeft && pLeftPortion, "Blinde Portion in ImpConnectParagraphs(1)" );
607 	DBG_ASSERT( pRight && pRightPortion, "Blinde Portion in ImpConnectParagraphs(2)" );
608 
609 	TextPaM aPaM = mpDoc->ConnectParagraphs( pLeft, pRight );
610 	ImpParagraphRemoved( nRight );
611 
612 	pLeftPortion->MarkSelectionInvalid( aPaM.GetIndex(), pLeft->GetText().Len() );
613 
614 	mpTEParaPortions->Remove( nRight );
615 	delete pRightPortion;
616 	// der rechte Node wird von EditDoc::ConnectParagraphs() geloescht.
617 
618 	return aPaM;
619 }
620 
621 TextPaM TextEngine::ImpDeleteText( const TextSelection& rSel )
622 {
623 	if ( !rSel.HasRange() )
624 		return rSel.GetStart();
625 
626 	TextSelection aSel( rSel );
627 	aSel.Justify();
628 	TextPaM aStartPaM( aSel.GetStart() );
629 	TextPaM aEndPaM( aSel.GetEnd() );
630 
631 	CursorMoved( aStartPaM.GetPara() ); // nur damit neu eingestellte Attribute verschwinden...
632 	CursorMoved( aEndPaM.GetPara() );	// nur damit neu eingestellte Attribute verschwinden...
633 
634 	DBG_ASSERT( mpDoc->IsValidPaM( aStartPaM ), "Index im Wald in ImpDeleteText" );
635 	DBG_ASSERT( mpDoc->IsValidPaM( aEndPaM ), "Index im Wald in ImpDeleteText" );
636 
637 	sal_uLong nStartNode = aStartPaM.GetPara();
638 	sal_uLong nEndNode = aEndPaM.GetPara();
639 
640 	// Alle Nodes dazwischen entfernen....
641 	for ( sal_uLong z = nStartNode+1; z < nEndNode; z++ )
642 	{
643 		// Immer nStartNode+1, wegen Remove()!
644 		ImpRemoveParagraph( nStartNode+1 );
645 	}
646 
647 	if ( nStartNode != nEndNode )
648 	{
649 		// Den Rest des StartNodes...
650 		TextNode* pLeft = mpDoc->GetNodes().GetObject( nStartNode );
651 		sal_uInt16 nChars = pLeft->GetText().Len() - aStartPaM.GetIndex();
652 		if ( nChars )
653 		{
654 			ImpRemoveChars( aStartPaM, nChars );
655 			TEParaPortion* pPortion = mpTEParaPortions->GetObject( nStartNode );
656 			DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(3)" );
657 			pPortion->MarkSelectionInvalid( aStartPaM.GetIndex(), pLeft->GetText().Len() );
658 		}
659 
660 		// Den Anfang des EndNodes....
661 		nEndNode = nStartNode+1;	// Die anderen Absaetze wurden geloescht
662 		nChars = aEndPaM.GetIndex();
663 		if ( nChars )
664 		{
665 			aEndPaM.GetPara() = nEndNode;
666 			aEndPaM.GetIndex() = 0;
667 			ImpRemoveChars( aEndPaM, nChars );
668 			TEParaPortion* pPortion = mpTEParaPortions->GetObject( nEndNode );
669 			DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(4)" );
670 			pPortion->MarkSelectionInvalid( 0, pPortion->GetNode()->GetText().Len() );
671 		}
672 
673 		// Zusammenfuegen....
674 		aStartPaM = ImpConnectParagraphs( nStartNode, nEndNode );
675 	}
676 	else
677 	{
678 		sal_uInt16 nChars;
679 		nChars = aEndPaM.GetIndex() - aStartPaM.GetIndex();
680 		ImpRemoveChars( aStartPaM, nChars );
681 		TEParaPortion* pPortion = mpTEParaPortions->GetObject( nStartNode );
682 		DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(5)" );
683 		pPortion->MarkInvalid( aEndPaM.GetIndex(), aStartPaM.GetIndex() - aEndPaM.GetIndex() );
684 	}
685 
686 //	UpdateSelections();
687 	TextModified();
688 	return aStartPaM;
689 }
690 
691 void TextEngine::ImpRemoveParagraph( sal_uLong nPara )
692 {
693 	TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
694 	TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
695 
696 	// Der Node wird vom Undo verwaltet und ggf. zerstoert!
697 	/* delete */ mpDoc->GetNodes().Remove( nPara );
698 	if ( IsUndoEnabled() && !IsInUndo() )
699 		InsertUndo( new TextUndoDelPara( this, pNode, nPara ) );
700 	else
701 		delete pNode;
702 
703 	mpTEParaPortions->Remove( nPara );
704 	delete pPortion;
705 
706 	ImpParagraphRemoved( nPara );
707 }
708 
709 uno::Reference < i18n::XExtendedInputSequenceChecker > TextEngine::GetInputSequenceChecker() const
710 {
711     uno::Reference < i18n::XExtendedInputSequenceChecker > xISC;
712 //    if ( !xISC.is() )
713     {
714         uno::Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
715         uno::Reference< uno::XInterface > xI = xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.InputSequenceChecker" ) );
716         if ( xI.is() )
717         {
718             Any x = xI->queryInterface( ::getCppuType((const uno::Reference< i18n::XExtendedInputSequenceChecker >*)0) );
719             x >>= xISC;
720         }
721     }
722     return xISC;
723 }
724 
725 sal_Bool TextEngine::IsInputSequenceCheckingRequired( sal_Unicode c, const TextSelection& rCurSel ) const
726 {
727     uno::Reference< i18n::XBreakIterator > xBI = ((TextEngine *) this)->GetBreakIterator();
728     SvtCTLOptions aCTLOptions;
729 
730     // get the index that really is first
731     sal_uInt16 nFirstPos = rCurSel.GetStart().GetIndex();
732     sal_uInt16 nMaxPos   = rCurSel.GetEnd().GetIndex();
733     if (nMaxPos < nFirstPos)
734         nFirstPos = nMaxPos;
735 
736     sal_Bool bIsSequenceChecking =
737         aCTLOptions.IsCTLFontEnabled() &&
738         aCTLOptions.IsCTLSequenceChecking() &&
739         nFirstPos != 0 && /* first char needs not to be checked */
740         xBI.is() && i18n::ScriptType::COMPLEX == xBI->getScriptType( rtl::OUString( c ), 0 );
741 
742     return bIsSequenceChecking;
743 }
744 
745 TextPaM TextEngine::ImpInsertText( const TextSelection& rCurSel, sal_Unicode c, sal_Bool bOverwrite )
746 {
747     return ImpInsertText( c, rCurSel, bOverwrite, sal_False );
748 }
749 
750 TextPaM TextEngine::ImpInsertText( sal_Unicode c, const TextSelection& rCurSel, sal_Bool bOverwrite, sal_Bool bIsUserInput )
751 {
752     DBG_ASSERT( c != '\n', "Zeilenumbruch bei InsertText ?" );
753     DBG_ASSERT( c != '\r', "Zeilenumbruch bei InsertText ?" );
754 
755     TextPaM aPaM( rCurSel.GetStart() );
756     TextNode* pNode = mpDoc->GetNodes().GetObject( aPaM.GetPara() );
757 
758     if ( pNode->GetText().Len() < STRING_MAXLEN )
759     {
760         sal_Bool bDoOverwrite = ( bOverwrite &&
761                 ( aPaM.GetIndex() < pNode->GetText().Len() ) ) ? sal_True : sal_False;
762 
763         sal_Bool bUndoAction = ( rCurSel.HasRange() || bDoOverwrite );
764 
765         if ( bUndoAction )
766             UndoActionStart();
767 
768         if ( rCurSel.HasRange() )
769         {
770             aPaM = ImpDeleteText( rCurSel );
771         }
772         else if ( bDoOverwrite )
773         {
774             // Wenn Selektion, dann kein Zeichen ueberschreiben
775             TextSelection aTmpSel( aPaM );
776             aTmpSel.GetEnd().GetIndex()++;
777             ImpDeleteText( aTmpSel );
778         }
779 
780         if (bIsUserInput && IsInputSequenceCheckingRequired( c, rCurSel ))
781         {
782             uno::Reference < i18n::XExtendedInputSequenceChecker > xISC = GetInputSequenceChecker();
783             SvtCTLOptions aCTLOptions;
784 
785             if (xISC.is())
786             {
787                 xub_StrLen nTmpPos = aPaM.GetIndex();
788                 sal_Int16 nCheckMode = aCTLOptions.IsCTLSequenceCheckingRestricted() ?
789                         i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC;
790 
791                 // the text that needs to be checked is only the one
792                 // before the current cursor position
793                 rtl::OUString aOldText( mpDoc->GetText( aPaM.GetPara() ).Copy(0, nTmpPos) );
794                 rtl::OUString aNewText( aOldText );
795                 if (aCTLOptions.IsCTLSequenceCheckingTypeAndReplace())
796                 {
797                     xISC->correctInputSequence( aNewText, nTmpPos - 1, c, nCheckMode );
798 
799                     // find position of first character that has changed
800                     sal_Int32 nOldLen = aOldText.getLength();
801                     sal_Int32 nNewLen = aNewText.getLength();
802                     const sal_Unicode *pOldTxt = aOldText.getStr();
803                     const sal_Unicode *pNewTxt = aNewText.getStr();
804                     sal_Int32 nChgPos = 0;
805                     while ( nChgPos < nOldLen && nChgPos < nNewLen &&
806                             pOldTxt[nChgPos] == pNewTxt[nChgPos] )
807                         ++nChgPos;
808 
809                     xub_StrLen nChgLen = static_cast< xub_StrLen >(nNewLen - nChgPos);
810                     String aChgText( aNewText.copy( nChgPos ), nChgLen );
811 
812                     // select text from first pos to be changed to current pos
813                     TextSelection aSel( TextPaM( aPaM.GetPara(), (sal_uInt16) nChgPos ), aPaM );
814 
815                     if (aChgText.Len())
816                         // ImpInsertText implicitly handles undo...
817                         return ImpInsertText( aSel, aChgText );
818                     else
819                         return aPaM;
820                 }
821                 else
822                 {
823                     // should the character be ignored (i.e. not get inserted) ?
824                     if (!xISC->checkInputSequence( aOldText, nTmpPos - 1, c, nCheckMode ))
825                         return aPaM;    // nothing to be done -> no need for undo
826                 }
827             }
828 
829             // at this point now we will insert the character 'normally' some lines below...
830         }
831 
832 
833         if ( IsUndoEnabled() && !IsInUndo() )
834         {
835             TextUndoInsertChars* pNewUndo = new TextUndoInsertChars( this, aPaM, c );
836             sal_Bool bTryMerge = ( !bDoOverwrite && ( c != ' ' ) ) ? sal_True : sal_False;
837             InsertUndo( pNewUndo, bTryMerge );
838         }
839 
840         TEParaPortion* pPortion = mpTEParaPortions->GetObject( aPaM.GetPara() );
841         pPortion->MarkInvalid( aPaM.GetIndex(), 1 );
842         if ( c == '\t' )
843             pPortion->SetNotSimpleInvalid();
844         aPaM = mpDoc->InsertText( aPaM, c );
845         ImpCharsInserted( aPaM.GetPara(), aPaM.GetIndex()-1, 1 );
846 
847         TextModified();
848 
849         if ( bUndoAction )
850             UndoActionEnd();
851     }
852 
853     return aPaM;
854 }
855 
856 
857 TextPaM TextEngine::ImpInsertText( const TextSelection& rCurSel, const XubString& rStr )
858 {
859     UndoActionStart();
860 
861 	TextPaM aPaM;
862 
863 	if ( rCurSel.HasRange() )
864 		aPaM = ImpDeleteText( rCurSel );
865 	else
866 		aPaM = rCurSel.GetEnd();
867 
868 	XubString aText( rStr );
869 	aText.ConvertLineEnd( LINEEND_LF );
870 
871 	sal_uInt16 nStart = 0;
872 	while ( nStart < aText.Len() )
873 	{
874 		sal_uInt16 nEnd = aText.Search( LINE_SEP, nStart );
875 		if ( nEnd == STRING_NOTFOUND )
876 			nEnd = aText.Len();	// nicht dereferenzieren!
877 
878 		// Start == End => Leerzeile
879 		if ( nEnd > nStart )
880 		{
881 			sal_uLong nL = aPaM.GetIndex();
882 			nL += ( nEnd-nStart );
883 			if ( nL > STRING_MAXLEN )
884 			{
885 				sal_uInt16 nDiff = (sal_uInt16) (nL-STRING_MAXLEN);
886 				nEnd = nEnd - nDiff;
887 			}
888 
889 			XubString aLine( aText, nStart, nEnd-nStart );
890 			if ( IsUndoEnabled() && !IsInUndo() )
891 				InsertUndo( new TextUndoInsertChars( this, aPaM, aLine ) );
892 
893 			TEParaPortion* pPortion = mpTEParaPortions->GetObject( aPaM.GetPara() );
894 			pPortion->MarkInvalid( aPaM.GetIndex(), aLine.Len() );
895 			if ( aLine.Search( '\t' ) != STRING_NOTFOUND )
896 				pPortion->SetNotSimpleInvalid();
897 
898 			aPaM = mpDoc->InsertText( aPaM, aLine );
899 			ImpCharsInserted( aPaM.GetPara(), aPaM.GetIndex()-aLine.Len(), aLine.Len() );
900 
901 		}
902 		if ( nEnd < aText.Len() )
903 			aPaM = ImpInsertParaBreak( aPaM );
904 
905 		nStart = nEnd+1;
906 
907         if ( nStart < nEnd )    // #108611# overflow
908             break;
909 	}
910 
911     UndoActionEnd();
912 
913     TextModified();
914 	return aPaM;
915 }
916 
917 TextPaM TextEngine::ImpInsertParaBreak( const TextSelection& rCurSel, sal_Bool bKeepEndingAttribs )
918 {
919 	TextPaM aPaM;
920 	if ( rCurSel.HasRange() )
921 		aPaM = ImpDeleteText( rCurSel );
922 	else
923 		aPaM = rCurSel.GetEnd();
924 
925 	return ImpInsertParaBreak( aPaM, bKeepEndingAttribs );
926 }
927 
928 TextPaM TextEngine::ImpInsertParaBreak( const TextPaM& rPaM, sal_Bool bKeepEndingAttribs )
929 {
930 	if ( IsUndoEnabled() && !IsInUndo() )
931 		InsertUndo( new TextUndoSplitPara( this, rPaM.GetPara(), rPaM.GetIndex() ) );
932 
933 	TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
934 	sal_Bool bFirstParaContentChanged = rPaM.GetIndex() < pNode->GetText().Len();
935 
936 	TextPaM aPaM( mpDoc->InsertParaBreak( rPaM, bKeepEndingAttribs ) );
937 
938 	TEParaPortion* pPortion = mpTEParaPortions->GetObject( rPaM.GetPara() );
939 	DBG_ASSERT( pPortion, "Blinde Portion in ImpInsertParaBreak" );
940 	pPortion->MarkInvalid( rPaM.GetIndex(), 0 );
941 
942 	TextNode* pNewNode = mpDoc->GetNodes().GetObject( aPaM.GetPara() );
943 	TEParaPortion* pNewPortion = new TEParaPortion( pNewNode );
944 	mpTEParaPortions->Insert( pNewPortion, aPaM.GetPara() );
945 	ImpParagraphInserted( aPaM.GetPara() );
946 
947 	CursorMoved( rPaM.GetPara() );	// falls leeres Attribut entstanden.
948 	TextModified();
949 
950 	if ( bFirstParaContentChanged )
951 		Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, rPaM.GetPara() ) );
952 
953 	return aPaM;
954 }
955 
956 Rectangle TextEngine::PaMtoEditCursor( const TextPaM& rPaM, sal_Bool bSpecial )
957 {
958 	DBG_ASSERT( GetUpdateMode(), "Darf bei Update=sal_False nicht erreicht werden: PaMtoEditCursor" );
959 
960 	Rectangle aEditCursor;
961 	long nY = 0;
962 
963 	if ( !mbHasMultiLineParas )
964 	{
965 		nY = rPaM.GetPara() * mnCharHeight;
966 	}
967 	else
968 	{
969 		for ( sal_uLong nPortion = 0; nPortion < rPaM.GetPara(); nPortion++ )
970 		{
971 			TEParaPortion* pPortion = mpTEParaPortions->GetObject(nPortion);
972 			nY += pPortion->GetLines().Count() * mnCharHeight;
973 		}
974 	}
975 
976 	aEditCursor = GetEditCursor( rPaM, bSpecial );
977 	aEditCursor.Top() += nY;
978 	aEditCursor.Bottom() += nY;
979 	return aEditCursor;
980 }
981 
982 Rectangle TextEngine::GetEditCursor( const TextPaM& rPaM, sal_Bool bSpecial, sal_Bool bPreferPortionStart )
983 {
984     if ( !IsFormatted() && !IsFormatting() )
985 		FormatAndUpdate();
986 
987 	TEParaPortion* pPortion = mpTEParaPortions->GetObject( rPaM.GetPara() );
988 	//TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
989 
990 	/*
991 	 bSpecial:	Wenn hinter dem letzten Zeichen einer umgebrochenen Zeile,
992 	 am Ende der Zeile bleiben, nicht am Anfang der naechsten.
993 	 Zweck: 	- END => wirklich hinter das letzte Zeichen
994 				- Selektion....
995 	  bSpecial: If behind the last character of a made up line, stay at the
996 	  			end of the line, not at the start of the next line.
997 	  Purpose:  - really END = > behind the last character
998 	  			- to selection...
999 
1000 	*/
1001 
1002 	long nY = 0;
1003 	sal_uInt16 nCurIndex = 0;
1004 	TextLine* pLine = 0;
1005 	for ( sal_uInt16 nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
1006 	{
1007 		TextLine* pTmpLine = pPortion->GetLines().GetObject( nLine );
1008 		if ( ( pTmpLine->GetStart() == rPaM.GetIndex() ) || ( pTmpLine->IsIn( rPaM.GetIndex(), bSpecial ) ) )
1009 		{
1010 			pLine = pTmpLine;
1011 			break;
1012 		}
1013 
1014 		nCurIndex = nCurIndex + pTmpLine->GetLen();
1015 		nY += mnCharHeight;
1016 	}
1017 	if ( !pLine )
1018 	{
1019 		// Cursor am Ende des Absatzes.
1020 		DBG_ASSERT( rPaM.GetIndex() == nCurIndex, "Index voll daneben in GetEditCursor!" );
1021 
1022 		pLine = pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1 );
1023 		nY -= mnCharHeight;
1024 		nCurIndex = nCurIndex - pLine->GetLen();
1025 	}
1026 
1027 	Rectangle aEditCursor;
1028 
1029 	aEditCursor.Top() = nY;
1030 	nY += mnCharHeight;
1031 	aEditCursor.Bottom() = nY-1;
1032 
1033 	// innerhalb der Zeile suchen....
1034 	long nX = ImpGetXPos( rPaM.GetPara(), pLine, rPaM.GetIndex(), bPreferPortionStart );
1035 	aEditCursor.Left() = aEditCursor.Right() = nX;
1036 	return aEditCursor;
1037 }
1038 
1039 long TextEngine::ImpGetXPos( sal_uLong nPara, TextLine* pLine, sal_uInt16 nIndex, sal_Bool bPreferPortionStart )
1040 {
1041 	DBG_ASSERT( ( nIndex >= pLine->GetStart() ) && ( nIndex <= pLine->GetEnd() ) , "ImpGetXPos muss richtig gerufen werden!" );
1042 
1043     sal_Bool bDoPreferPortionStart = bPreferPortionStart;
1044     // Assure that the portion belongs to this line:
1045     if ( nIndex == pLine->GetStart() )
1046         bDoPreferPortionStart = sal_True;
1047     else if ( nIndex == pLine->GetEnd() )
1048         bDoPreferPortionStart = sal_False;
1049 
1050 	TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
1051 
1052     sal_uInt16 nTextPortionStart = 0;
1053     sal_uInt16 nTextPortion = pParaPortion->GetTextPortions().FindPortion( nIndex, nTextPortionStart, bDoPreferPortionStart );
1054 
1055     DBG_ASSERT( ( nTextPortion >= pLine->GetStartPortion() ) && ( nTextPortion <= pLine->GetEndPortion() ), "GetXPos: Portion not in current line! " );
1056 
1057     TETextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
1058 
1059     long nX = ImpGetPortionXOffset( nPara, pLine, nTextPortion );
1060 
1061     long nPortionTextWidth = pPortion->GetWidth();
1062 
1063     if ( nTextPortionStart != nIndex )
1064     {
1065         // Search within portion...
1066         if ( nIndex == ( nTextPortionStart + pPortion->GetLen() ) )
1067         {
1068             // End of Portion
1069             if ( ( pPortion->GetKind() == PORTIONKIND_TAB ) ||
1070                  ( !IsRightToLeft() && !pPortion->IsRightToLeft() ) ||
1071                  ( IsRightToLeft() && pPortion->IsRightToLeft() ) )
1072             {
1073 				nX += nPortionTextWidth;
1074                 if ( ( pPortion->GetKind() == PORTIONKIND_TAB ) && ( (nTextPortion+1) < pParaPortion->GetTextPortions().Count() ) )
1075                 {
1076                     TETextPortion* pNextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion+1 );
1077                     if ( ( pNextPortion->GetKind() != PORTIONKIND_TAB ) && (
1078                               ( !IsRightToLeft() && pNextPortion->IsRightToLeft() ) ||
1079                               ( IsRightToLeft() && !pNextPortion->IsRightToLeft() ) ) )
1080                     {
1081 //                        nX += pNextPortion->GetWidth();
1082                         // End of the tab portion, use start of next for cursor pos
1083                         DBG_ASSERT( !bPreferPortionStart, "ImpGetXPos - How can we this tab portion here???" );
1084                         nX = ImpGetXPos( nPara, pLine, nIndex, sal_True );
1085                     }
1086 
1087                 }
1088             }
1089         }
1090         else if ( pPortion->GetKind() == PORTIONKIND_TEXT )
1091         {
1092 			DBG_ASSERT( nIndex != pLine->GetStart(), "Strange behavior in new ImpGetXPos()" );
1093 
1094             long nPosInPortion = (long)CalcTextWidth( nPara, nTextPortionStart, nIndex-nTextPortionStart );
1095 
1096             if ( ( !IsRightToLeft() && !pPortion->IsRightToLeft() ) ||
1097                  ( IsRightToLeft() && pPortion->IsRightToLeft() ) )
1098             {
1099 				nX += nPosInPortion;
1100             }
1101             else
1102             {
1103 				nX += nPortionTextWidth - nPosInPortion;
1104             }
1105 		}
1106     }
1107     else // if ( nIndex == pLine->GetStart() )
1108     {
1109         if ( ( pPortion->GetKind() != PORTIONKIND_TAB ) &&
1110                 ( ( !IsRightToLeft() && pPortion->IsRightToLeft() ) ||
1111                 ( IsRightToLeft() && !pPortion->IsRightToLeft() ) ) )
1112         {
1113 		    nX += nPortionTextWidth;
1114         }
1115     }
1116 
1117 	return nX;
1118 }
1119 
1120 const TextAttrib* TextEngine::FindAttrib( const TextPaM& rPaM, sal_uInt16 nWhich ) const
1121 {
1122 	const TextAttrib* pAttr = NULL;
1123 	const TextCharAttrib* pCharAttr = FindCharAttrib( rPaM, nWhich );
1124 	if ( pCharAttr )
1125 		pAttr = &pCharAttr->GetAttr();
1126 	return pAttr;
1127 }
1128 
1129 const TextCharAttrib* TextEngine::FindCharAttrib( const TextPaM& rPaM, sal_uInt16 nWhich ) const
1130 {
1131 	const TextCharAttrib* pAttr = NULL;
1132 	TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
1133 	if ( pNode && ( rPaM.GetIndex() < pNode->GetText().Len() ) )
1134 		pAttr = pNode->GetCharAttribs().FindAttrib( nWhich, rPaM.GetIndex() );
1135 	return pAttr;
1136 }
1137 
1138 sal_Bool TextEngine::HasAttrib( sal_uInt16 nWhich ) const
1139 {
1140 	sal_Bool bAttr = sal_False;
1141 	for ( sal_uLong n = mpDoc->GetNodes().Count(); --n && !bAttr; )
1142 	{
1143 		TextNode* pNode = mpDoc->GetNodes().GetObject( n );
1144 		bAttr = pNode->GetCharAttribs().HasAttrib( nWhich );
1145 	}
1146 	return bAttr;
1147 }
1148 
1149 TextPaM TextEngine::GetPaM( const Point& rDocPos, sal_Bool bSmart )
1150 {
1151 	DBG_ASSERT( GetUpdateMode(), "Darf bei Update=sal_False nicht erreicht werden: GetPaM" );
1152 
1153 	long nY = 0;
1154 	for ( sal_uLong nPortion = 0; nPortion < mpTEParaPortions->Count(); nPortion++ )
1155 	{
1156 		TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion );
1157 		long nTmpHeight = pPortion->GetLines().Count() * mnCharHeight;
1158 		nY += nTmpHeight;
1159 		if ( nY > rDocPos.Y() )
1160 		{
1161 			nY -= nTmpHeight;
1162 			Point aPosInPara( rDocPos );
1163 			aPosInPara.Y() -= nY;
1164 
1165 			TextPaM aPaM( nPortion, 0 );
1166 			aPaM.GetIndex() = ImpFindIndex( nPortion, aPosInPara, bSmart );
1167 			return aPaM;
1168 		}
1169 	}
1170 
1171 	// Nicht gefunden - Dann den letzten sichtbare...
1172 	sal_uLong nLastNode = mpDoc->GetNodes().Count() - 1;
1173 	TextNode* pLast = mpDoc->GetNodes().GetObject( nLastNode );
1174 	return TextPaM( nLastNode, pLast->GetText().Len() );
1175 }
1176 
1177 sal_uInt16 TextEngine::ImpFindIndex( sal_uLong nPortion, const Point& rPosInPara, sal_Bool bSmart )
1178 {
1179 	DBG_ASSERT( IsFormatted(), "GetPaM: Nicht formatiert" );
1180 	TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion );
1181 
1182 	sal_uInt16 nCurIndex = 0;
1183 
1184 	long nY = 0;
1185 	TextLine* pLine = 0;
1186 	sal_uInt16 nLine;
1187 	for ( nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
1188 	{
1189 		TextLine* pTmpLine = pPortion->GetLines().GetObject( nLine );
1190 		nY += mnCharHeight;
1191 		if ( nY > rPosInPara.Y() ) 	// das war 'se
1192 		{
1193 			pLine = pTmpLine;
1194 			break;					// richtige Y-Position intressiert nicht
1195 		}
1196 	}
1197 	DBG_ASSERT( pLine, "ImpFindIndex: pLine ?" );
1198 
1199 	nCurIndex = GetCharPos( nPortion, nLine, rPosInPara.X(), bSmart );
1200 
1201 	if ( nCurIndex && ( nCurIndex == pLine->GetEnd() ) &&
1202 		 ( pLine != pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1) ) )
1203 	{
1204 		uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator();
1205 		sal_Int32 nCount = 1;
1206 		nCurIndex = (sal_uInt16)xBI->previousCharacters( pPortion->GetNode()->GetText(), nCurIndex, GetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount );
1207 	}
1208 	return nCurIndex;
1209 }
1210 
1211 sal_uInt16 TextEngine::GetCharPos( sal_uLong nPortion, sal_uInt16 nLine, long nXPos, sal_Bool )
1212 {
1213 
1214 	TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion );
1215 	TextLine* pLine = pPortion->GetLines().GetObject( nLine );
1216 
1217 	sal_uInt16 nCurIndex = pLine->GetStart();
1218 
1219 	long nTmpX = pLine->GetStartX();
1220 	if ( nXPos <= nTmpX )
1221 		return nCurIndex;
1222 
1223 	for ( sal_uInt16 i = pLine->GetStartPortion(); i <= pLine->GetEndPortion(); i++ )
1224 	{
1225 		TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( i );
1226 		nTmpX += pTextPortion->GetWidth();
1227 
1228 		if ( nTmpX > nXPos )
1229 		{
1230 			if( pTextPortion->GetLen() > 1 )
1231 			{
1232 				nTmpX -= pTextPortion->GetWidth();	// vor die Portion stellen
1233 				// Optimieren: Kein GetTextBreak, wenn feste Fontbreite...
1234 				Font aFont;
1235 				SeekCursor( nPortion, nCurIndex+1, aFont, NULL );
1236 				mpRefDev->SetFont( aFont);
1237                 long nPosInPortion = nXPos-nTmpX;
1238                 if ( IsRightToLeft() != pTextPortion->IsRightToLeft() )
1239                     nPosInPortion = pTextPortion->GetWidth() - nPosInPortion;
1240 				nCurIndex = mpRefDev->GetTextBreak( pPortion->GetNode()->GetText(), nPosInPortion, nCurIndex );
1241                 // MT: GetTextBreak should assure that we are not withing a CTL cell...
1242 			}
1243 			return nCurIndex;
1244 		}
1245 		nCurIndex = nCurIndex + pTextPortion->GetLen();
1246 	}
1247 	return nCurIndex;
1248 }
1249 
1250 
1251 sal_uLong TextEngine::GetTextHeight() const
1252 {
1253 	DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=sal_False nicht verwendet werden: GetTextHeight" );
1254 
1255     if ( !IsFormatted() && !IsFormatting() )
1256 		((TextEngine*)this)->FormatAndUpdate();
1257 
1258 	return mnCurTextHeight;
1259 }
1260 
1261 sal_uLong TextEngine::GetTextHeight( sal_uLong nParagraph ) const
1262 {
1263 	DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=sal_False nicht verwendet werden: GetTextHeight" );
1264 
1265   	if ( !IsFormatted() && !IsFormatting() )
1266 		((TextEngine*)this)->FormatAndUpdate();
1267 
1268 	return CalcParaHeight( nParagraph );
1269 }
1270 
1271 sal_uLong TextEngine::CalcTextWidth( sal_uLong nPara )
1272 {
1273 	sal_uLong nParaWidth = 0;
1274 	TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
1275 	for ( sal_uInt16 nLine = pPortion->GetLines().Count(); nLine; )
1276 	{
1277 		sal_uLong nLineWidth = 0;
1278 		TextLine* pLine = pPortion->GetLines().GetObject( --nLine );
1279 		for ( sal_uInt16 nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ )
1280 		{
1281 			TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( nTP );
1282 			nLineWidth += pTextPortion->GetWidth();
1283 		}
1284 		if ( nLineWidth > nParaWidth )
1285 			nParaWidth = nLineWidth;
1286 	}
1287 	return nParaWidth;
1288 }
1289 
1290 sal_uLong TextEngine::CalcTextWidth()
1291 {
1292 	if ( !IsFormatted() && !IsFormatting() )
1293 		FormatAndUpdate();
1294 
1295 	if ( mnCurTextWidth == 0xFFFFFFFF )
1296 	{
1297 		mnCurTextWidth = 0;
1298 		for ( sal_uLong nPara = mpTEParaPortions->Count(); nPara; )
1299 		{
1300 			sal_uLong nParaWidth = CalcTextWidth( --nPara );
1301 			if ( nParaWidth > mnCurTextWidth )
1302 				mnCurTextWidth = nParaWidth;
1303 		}
1304 	}
1305 	return mnCurTextWidth+1;// Ein breiter, da in CreateLines bei >= umgebrochen wird.
1306 }
1307 
1308 sal_uLong TextEngine::CalcTextHeight()
1309 {
1310 	DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=sal_False nicht verwendet werden: CalcTextHeight" );
1311 
1312 	sal_uLong nY = 0;
1313 	for ( sal_uLong nPortion = mpTEParaPortions->Count(); nPortion; )
1314 		nY += CalcParaHeight( --nPortion );
1315 	return nY;
1316 }
1317 
1318 sal_uLong TextEngine::CalcTextWidth( sal_uLong nPara, sal_uInt16 nPortionStart, sal_uInt16 nLen, const Font* pFont )
1319 {
1320 	// Innerhalb des Textes darf es keinen Portionwechsel (Attribut/Tab) geben!
1321 	DBG_ASSERT( mpDoc->GetNodes().GetObject( nPara )->GetText().Search( '\t', nPortionStart ) >= (nPortionStart+nLen), "CalcTextWidth: Tab!" );
1322 
1323 	sal_uLong nWidth;
1324 	if ( mnFixCharWidth100 )
1325     {
1326 		nWidth = (sal_uLong)nLen*mnFixCharWidth100/100;
1327     }
1328 	else
1329 	{
1330 		if ( pFont )
1331 		{
1332 			if ( !mpRefDev->GetFont().IsSameInstance( *pFont ) )
1333 				mpRefDev->SetFont( *pFont );
1334 		}
1335 		else
1336 		{
1337 			Font aFont;
1338 			SeekCursor( nPara, nPortionStart+1, aFont, NULL );
1339 			mpRefDev->SetFont( aFont );
1340 		}
1341 		TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
1342 		nWidth = (sal_uLong)mpRefDev->GetTextWidth( pNode->GetText(), nPortionStart, nLen );
1343 
1344 	}
1345 	return nWidth;
1346 }
1347 
1348 
1349 sal_uInt16 TextEngine::GetLineCount( sal_uLong nParagraph ) const
1350 {
1351 	DBG_ASSERT( nParagraph < mpTEParaPortions->Count(), "GetLineCount: Out of range" );
1352 
1353 	TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph );
1354 	if ( pPPortion )
1355 		return pPPortion->GetLines().Count();
1356 
1357 	return 0xFFFF;
1358 }
1359 
1360 sal_uInt16 TextEngine::GetLineLen( sal_uLong nParagraph, sal_uInt16 nLine ) const
1361 {
1362 	DBG_ASSERT( nParagraph < mpTEParaPortions->Count(), "GetLineCount: Out of range" );
1363 
1364 	TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph );
1365 	if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) )
1366 	{
1367 		TextLine* pLine = pPPortion->GetLines().GetObject( nLine );
1368 		return pLine->GetLen();
1369 	}
1370 
1371 	return 0xFFFF;
1372 }
1373 
1374 sal_uLong TextEngine::CalcParaHeight( sal_uLong nParagraph ) const
1375 {
1376 	sal_uLong nHeight = 0;
1377 
1378 	TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph );
1379 	DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetParaHeight" );
1380 	if ( pPPortion )
1381 		nHeight = pPPortion->GetLines().Count() * mnCharHeight;
1382 
1383 	return nHeight;
1384 }
1385 
1386 void TextEngine::UpdateSelections()
1387 {
1388 }
1389 
1390 Range TextEngine::GetInvalidYOffsets( sal_uLong nPortion )
1391 {
1392 	TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPortion );
1393 	sal_uInt16 nLines = pTEParaPortion->GetLines().Count();
1394 	sal_uInt16 nLastInvalid, nFirstInvalid = 0;
1395 	sal_uInt16 nLine;
1396 	for ( nLine = 0; nLine < nLines; nLine++ )
1397 	{
1398 		TextLine* pL = pTEParaPortion->GetLines().GetObject( nLine );
1399 		if ( pL->IsInvalid() )
1400 		{
1401 			nFirstInvalid = nLine;
1402 			break;
1403 		}
1404 	}
1405 
1406 	for ( nLastInvalid = nFirstInvalid; nLastInvalid < nLines; nLastInvalid++ )
1407 	{
1408 		TextLine* pL = pTEParaPortion->GetLines().GetObject( nLine );
1409 		if ( pL->IsValid() )
1410 			break;
1411 	}
1412 
1413 	if ( nLastInvalid >= nLines )
1414 		nLastInvalid = nLines-1;
1415 
1416 	return Range( nFirstInvalid*mnCharHeight, ((nLastInvalid+1)*mnCharHeight)-1 );
1417 }
1418 
1419 sal_uLong TextEngine::GetParagraphCount() const
1420 {
1421 	return mpDoc->GetNodes().Count();
1422 }
1423 
1424 void TextEngine::EnableUndo( sal_Bool bEnable )
1425 {
1426 	// Beim Umschalten des Modus Liste loeschen:
1427 	if ( bEnable != IsUndoEnabled() )
1428 		ResetUndo();
1429 
1430 	mbUndoEnabled = bEnable;
1431 }
1432 
1433 ::svl::IUndoManager& TextEngine::GetUndoManager()
1434 {
1435 	if ( !mpUndoManager )
1436 		mpUndoManager = new TextUndoManager( this );
1437 	return *mpUndoManager;
1438 }
1439 
1440 void TextEngine::UndoActionStart( sal_uInt16 nId )
1441 {
1442 	if ( IsUndoEnabled() && !IsInUndo() )
1443 	{
1444 		String aComment;
1445 		// ...
1446 		GetUndoManager().EnterListAction( aComment, XubString(), nId );
1447 	}
1448 }
1449 
1450 void TextEngine::UndoActionEnd()
1451 {
1452 	if ( IsUndoEnabled() && !IsInUndo() )
1453 		GetUndoManager().LeaveListAction();
1454 }
1455 
1456 void TextEngine::InsertUndo( TextUndo* pUndo, sal_Bool bTryMerge )
1457 {
1458 	DBG_ASSERT( !IsInUndo(), "InsertUndo im Undomodus!" );
1459 	GetUndoManager().AddUndoAction( pUndo, bTryMerge );
1460 }
1461 
1462 void TextEngine::ResetUndo()
1463 {
1464 	if ( mpUndoManager )
1465 		mpUndoManager->Clear();
1466 }
1467 
1468 void TextEngine::InsertContent( TextNode* pNode, sal_uLong nPara )
1469 {
1470 	DBG_ASSERT( pNode, "NULL-Pointer in InsertContent! " );
1471 	DBG_ASSERT( IsInUndo(), "InsertContent nur fuer Undo()!" );
1472 	TEParaPortion* pNew = new TEParaPortion( pNode );
1473 	mpTEParaPortions->Insert( pNew, nPara );
1474 	mpDoc->GetNodes().Insert( pNode, nPara );
1475 	ImpParagraphInserted( nPara );
1476 }
1477 
1478 TextPaM TextEngine::SplitContent( sal_uLong nNode, sal_uInt16 nSepPos )
1479 {
1480     #ifdef DBG_UTIL
1481 	TextNode* pNode = mpDoc->GetNodes().GetObject( nNode );
1482 	DBG_ASSERT( pNode, "Ungueltiger Node in SplitContent" );
1483 	DBG_ASSERT( IsInUndo(), "SplitContent nur fuer Undo()!" );
1484 	DBG_ASSERT( nSepPos <= pNode->GetText().Len(), "Index im Wald: SplitContent" );
1485     #endif
1486 	TextPaM aPaM( nNode, nSepPos );
1487 	return ImpInsertParaBreak( aPaM );
1488 }
1489 
1490 TextPaM TextEngine::ConnectContents( sal_uLong nLeftNode )
1491 {
1492 	DBG_ASSERT( IsInUndo(), "ConnectContent nur fuer Undo()!" );
1493 	return ImpConnectParagraphs( nLeftNode, nLeftNode+1 );
1494 }
1495 
1496 void TextEngine::SeekCursor( sal_uLong nPara, sal_uInt16 nPos, Font& rFont, OutputDevice* pOutDev )
1497 {
1498 	rFont = maFont;
1499 	if ( pOutDev )
1500 		pOutDev->SetTextColor( maTextColor );
1501 
1502 	TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
1503 	sal_uInt16 nAttribs = pNode->GetCharAttribs().Count();
1504 	for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
1505 	{
1506 		TextCharAttrib* pAttrib = pNode->GetCharAttribs().GetAttrib( nAttr );
1507 		if ( pAttrib->GetStart() > nPos )
1508 			break;
1509 
1510 		// Beim Seeken nicht die Attr beruecksichtigen, die dort beginnen!
1511 		// Leere Attribute werden beruecksichtigt( verwendet), da diese
1512 		// gerade eingestellt wurden.
1513 		// 12.4.95: Doch keine Leeren Attribute verwenden:
1514 		// - Wenn gerade eingestellt und leer => keine Auswirkung auf Font
1515 		// In einem leeren Absatz eingestellte Zeichen werden sofort wirksam.
1516 		if ( ( ( pAttrib->GetStart() < nPos ) && ( pAttrib->GetEnd() >= nPos ) )
1517 					|| !pNode->GetText().Len() )
1518 		{
1519 			if ( pAttrib->Which() != TEXTATTR_FONTCOLOR )
1520 			{
1521 				pAttrib->GetAttr().SetFont( rFont );
1522 			}
1523 			else
1524 			{
1525 				if ( pOutDev )
1526 					pOutDev->SetTextColor( ((TextAttribFontColor&)pAttrib->GetAttr()).GetColor() );
1527 			}
1528 		}
1529 	}
1530 
1531 	if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetPara() == nPara ) &&
1532 		( nPos > mpIMEInfos->aPos.GetIndex() ) && ( nPos <= ( mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen ) ) )
1533 	{
1534 		sal_uInt16 nAttr = mpIMEInfos->pAttribs[ nPos - mpIMEInfos->aPos.GetIndex() - 1 ];
1535 		if ( nAttr & EXTTEXTINPUT_ATTR_UNDERLINE )
1536 			rFont.SetUnderline( UNDERLINE_SINGLE );
1537 		else if ( nAttr & EXTTEXTINPUT_ATTR_BOLDUNDERLINE )
1538 			rFont.SetUnderline( UNDERLINE_BOLD );
1539 		else if ( nAttr & EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE )
1540 			rFont.SetUnderline( UNDERLINE_DOTTED );
1541 		else if ( nAttr & EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE )
1542 			rFont.SetUnderline( UNDERLINE_DOTTED );
1543 		if ( nAttr & EXTTEXTINPUT_ATTR_REDTEXT )
1544 			rFont.SetColor( Color( COL_RED ) );
1545 		else if ( nAttr & EXTTEXTINPUT_ATTR_HALFTONETEXT )
1546 			rFont.SetColor( Color( COL_LIGHTGRAY ) );
1547 		if ( nAttr & EXTTEXTINPUT_ATTR_HIGHLIGHT )
1548 		{
1549 			const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1550 			rFont.SetColor( rStyleSettings.GetHighlightTextColor() );
1551 			rFont.SetFillColor( rStyleSettings.GetHighlightColor() );
1552 			rFont.SetTransparent( sal_False );
1553 		}
1554 		else if ( nAttr & EXTTEXTINPUT_ATTR_GRAYWAVELINE )
1555 		{
1556 			rFont.SetUnderline( UNDERLINE_WAVE );
1557 //			if( pOut )
1558 //				pOut->SetTextLineColor( Color( COL_LIGHTGRAY ) );
1559 		}
1560 	}
1561 }
1562 
1563 void TextEngine::SetUpdateMode( sal_Bool bUp, TextView* pCurView, sal_Bool bForceUpdate )
1564 {
1565 	sal_Bool bChanged = ( GetUpdateMode() != bUp );
1566 
1567 	mbUpdate = bUp;
1568 	if ( mbUpdate && ( bChanged || bForceUpdate ) )
1569 		FormatAndUpdate( pCurView );
1570 }
1571 
1572 void TextEngine::FormatAndUpdate( TextView* pCurView )
1573 {
1574 	if ( mbDowning )
1575 		return ;
1576 
1577 	if ( IsInUndo() )
1578 		IdleFormatAndUpdate( pCurView );
1579 	else
1580 	{
1581 		FormatDoc();
1582 		UpdateViews( pCurView );
1583 	}
1584 }
1585 
1586 
1587 void TextEngine::IdleFormatAndUpdate( TextView* pCurView, sal_uInt16 nMaxTimerRestarts )
1588 {
1589 	mpIdleFormatter->DoIdleFormat( pCurView, nMaxTimerRestarts );
1590 }
1591 
1592 void TextEngine::TextModified()
1593 {
1594 	mbFormatted = sal_False;
1595 	mbModified = sal_True;
1596 }
1597 
1598 void TextEngine::UpdateViews( TextView* pCurView )
1599 {
1600 	if ( !GetUpdateMode() || IsFormatting() || maInvalidRec.IsEmpty() )
1601 		return;
1602 
1603 	DBG_ASSERT( IsFormatted(), "UpdateViews: Doc nicht formatiert!" );
1604 
1605 	for ( sal_uInt16 nView = 0; nView < mpViews->Count(); nView++ )
1606 	{
1607 		TextView* pView = mpViews->GetObject( nView );
1608 		pView->HideCursor();
1609 
1610 		Rectangle aClipRec( maInvalidRec );
1611         Size aOutSz = pView->GetWindow()->GetOutputSizePixel();
1612 		Rectangle aVisArea( pView->GetStartDocPos(), aOutSz );
1613 		aClipRec.Intersection( aVisArea );
1614 		if ( !aClipRec.IsEmpty() )
1615 		{
1616 			// in Fensterkoordinaten umwandeln....
1617             Point aNewPos = pView->GetWindowPos( aClipRec.TopLeft() );
1618             if ( IsRightToLeft() )
1619                 aNewPos.X() -= aOutSz.Width() - 1;
1620 			aClipRec.SetPos( aNewPos );
1621 
1622 			if ( pView == pCurView )
1623 				pView->ImpPaint( aClipRec, !pView->GetWindow()->IsPaintTransparent() );
1624 			else
1625 				pView->GetWindow()->Invalidate( aClipRec );
1626 		}
1627 	}
1628 
1629 	if ( pCurView )
1630 	{
1631         pCurView->ShowCursor( pCurView->IsAutoScroll() );
1632 	}
1633 
1634 	maInvalidRec = Rectangle();
1635 }
1636 
1637 IMPL_LINK( TextEngine, IdleFormatHdl, Timer *, EMPTYARG )
1638 {
1639 	FormatAndUpdate( mpIdleFormatter->GetView() );
1640 	return 0;
1641 }
1642 
1643 void TextEngine::CheckIdleFormatter()
1644 {
1645 	mpIdleFormatter->ForceTimeout();
1646 }
1647 
1648 void TextEngine::FormatFullDoc()
1649 {
1650 	for ( sal_uLong nPortion = 0; nPortion < mpTEParaPortions->Count(); nPortion++ )
1651 	{
1652 		TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPortion );		sal_uInt16 nLen = pTEParaPortion->GetNode()->GetText().Len();
1653 		pTEParaPortion->MarkSelectionInvalid( 0, nLen );
1654 	}
1655 	mbFormatted = sal_False;
1656 	FormatDoc();
1657 }
1658 
1659 void TextEngine::FormatDoc()
1660 {
1661 	if ( IsFormatted() || !GetUpdateMode() || IsFormatting() )
1662 		return;
1663 
1664 	mbIsFormatting = sal_True;
1665 	mbHasMultiLineParas = sal_False;
1666 
1667 	long nY = 0;
1668 	sal_Bool bGrow = sal_False;
1669 
1670 	maInvalidRec = Rectangle();	// leermachen
1671 	for ( sal_uLong nPara = 0; nPara < mpTEParaPortions->Count(); nPara++ )
1672 	{
1673 		TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1674 		if ( pTEParaPortion->IsInvalid() )
1675 		{
1676 			sal_uLong nOldParaWidth = 0xFFFFFFFF;
1677 			if ( mnCurTextWidth != 0xFFFFFFFF )
1678 				nOldParaWidth = CalcTextWidth( nPara );
1679 
1680 			ImpFormattingParagraph( nPara );
1681 
1682 			if ( CreateLines( nPara ) )
1683 				bGrow = sal_True;
1684 
1685 			// InvalidRec nur einmal setzen...
1686 			if ( maInvalidRec.IsEmpty() )
1687 			{
1688 				// Bei Paperwidth 0 (AutoPageSize) bleibt es sonst Empty()...
1689 				long nWidth = (long)mnMaxTextWidth;
1690 				if ( !nWidth )
1691 					nWidth = 0x7FFFFFFF;
1692 				Range aInvRange( GetInvalidYOffsets( nPara ) );
1693 				maInvalidRec = Rectangle( Point( 0, nY+aInvRange.Min() ),
1694 					Size( nWidth, aInvRange.Len() ) );
1695 			}
1696 			else
1697 			{
1698 				maInvalidRec.Bottom() = nY + CalcParaHeight( nPara );
1699 			}
1700 
1701 			if ( mnCurTextWidth != 0xFFFFFFFF )
1702 			{
1703 				sal_uLong nNewParaWidth = CalcTextWidth( nPara );
1704 				if ( nNewParaWidth >= mnCurTextWidth )
1705 					mnCurTextWidth = nNewParaWidth;
1706 				else if ( ( nOldParaWidth != 0xFFFFFFFF ) && ( nOldParaWidth >= mnCurTextWidth ) )
1707 					mnCurTextWidth = 0xFFFFFFFF;
1708 			}
1709 		}
1710 		else if ( bGrow )
1711 		{
1712 			maInvalidRec.Bottom() = nY + CalcParaHeight( nPara );
1713 		}
1714 		nY += CalcParaHeight( nPara );
1715 		if ( !mbHasMultiLineParas && pTEParaPortion->GetLines().Count() > 1 )
1716 			mbHasMultiLineParas = sal_True;
1717 	}
1718 
1719 	if ( !maInvalidRec.IsEmpty() )
1720 	{
1721 		sal_uLong nNewHeight = CalcTextHeight();
1722 		long nDiff = nNewHeight - mnCurTextHeight;
1723 		if ( nNewHeight < mnCurTextHeight )
1724 		{
1725 			maInvalidRec.Bottom() = (long)Max( nNewHeight, mnCurTextHeight );
1726 			if ( maInvalidRec.IsEmpty() )
1727 			{
1728 				maInvalidRec.Top() = 0;
1729 				// Left und Right werden nicht ausgewertet, aber wegen IsEmpty gesetzt.
1730 				maInvalidRec.Left() = 0;
1731 				maInvalidRec.Right() = mnMaxTextWidth;
1732 			}
1733 		}
1734 
1735 		mnCurTextHeight = nNewHeight;
1736 		if ( nDiff )
1737 		{
1738 			mbFormatted = sal_True;
1739 			ImpTextHeightChanged();
1740 		}
1741 	}
1742 
1743 	mbIsFormatting = sal_False;
1744 	mbFormatted = sal_True;
1745 
1746 	ImpTextFormatted();
1747 }
1748 
1749 void TextEngine::CreateAndInsertEmptyLine( sal_uLong nPara )
1750 {
1751 	TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
1752 	TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1753 
1754 	TextLine* pTmpLine = new TextLine;
1755 	pTmpLine->SetStart( pNode->GetText().Len() );
1756 	pTmpLine->SetEnd( pTmpLine->GetStart() );
1757 	pTEParaPortion->GetLines().Insert( pTmpLine, pTEParaPortion->GetLines().Count() );
1758 
1759 	if ( ImpGetAlign() == TXTALIGN_CENTER )
1760 		pTmpLine->SetStartX( (short)(mnMaxTextWidth / 2) );
1761 	else if ( ImpGetAlign() == TXTALIGN_RIGHT )
1762 		pTmpLine->SetStartX( (short)mnMaxTextWidth );
1763 	else
1764 		pTmpLine->SetStartX( mpDoc->GetLeftMargin() );
1765 
1766 	sal_Bool bLineBreak = pNode->GetText().Len() ? sal_True : sal_False;
1767 
1768 	TETextPortion* pDummyPortion = new TETextPortion( 0 );
1769 	pDummyPortion->GetWidth() = 0;
1770 	pTEParaPortion->GetTextPortions().Insert( pDummyPortion, pTEParaPortion->GetTextPortions().Count() );
1771 
1772 	if ( bLineBreak == sal_True )
1773 	{
1774 		// -2: Die neue ist bereits eingefuegt.
1775         #ifdef DBG_UTIL
1776 		TextLine* pLastLine = pTEParaPortion->GetLines().GetObject( pTEParaPortion->GetLines().Count()-2 );
1777 		DBG_ASSERT( pLastLine, "Weicher Umbruch, keine Zeile ?!" );
1778         #endif
1779 		sal_uInt16 nPos = (sal_uInt16) pTEParaPortion->GetTextPortions().Count() - 1 ;
1780 		pTmpLine->SetStartPortion( nPos );
1781 		pTmpLine->SetEndPortion( nPos );
1782 	}
1783 }
1784 
1785 void TextEngine::ImpBreakLine( sal_uLong nPara, TextLine* pLine, TETextPortion*, sal_uInt16 nPortionStart, long nRemainingWidth )
1786 {
1787 	TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
1788 
1789 	// Font sollte noch eingestellt sein.
1790 	sal_uInt16 nMaxBreakPos = mpRefDev->GetTextBreak( pNode->GetText(), nRemainingWidth, nPortionStart );
1791 
1792 	DBG_ASSERT( nMaxBreakPos < pNode->GetText().Len(), "Break?!" );
1793 
1794 	if ( nMaxBreakPos == STRING_LEN )	// GetTextBreak() ist anderer Auffassung als GetTextSize()
1795 		nMaxBreakPos = pNode->GetText().Len() - 1;
1796 
1797 	uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator();
1798 	i18n::LineBreakHyphenationOptions aHyphOptions( NULL, uno::Sequence< beans::PropertyValue >(), 1 );
1799 
1800     i18n::LineBreakUserOptions aUserOptions;
1801 	aUserOptions.forbiddenBeginCharacters = ImpGetLocaleDataWrapper()->getForbiddenCharacters().beginLine;
1802 	aUserOptions.forbiddenEndCharacters = ImpGetLocaleDataWrapper()->getForbiddenCharacters().endLine;
1803 	aUserOptions.applyForbiddenRules = sal_True;
1804 	aUserOptions.allowPunctuationOutsideMargin = sal_False;
1805 	aUserOptions.allowHyphenateEnglish = sal_False;
1806 
1807     static const com::sun::star::lang::Locale aDefLocale;
1808 	i18n::LineBreakResults aLBR = xBI->getLineBreak( pNode->GetText(), nMaxBreakPos, aDefLocale, pLine->GetStart(), aHyphOptions, aUserOptions );
1809 	sal_uInt16 nBreakPos = (sal_uInt16)aLBR.breakIndex;
1810 	if ( nBreakPos <= pLine->GetStart() )
1811     {
1812 		nBreakPos = nMaxBreakPos;
1813 		if ( nBreakPos <= pLine->GetStart() )
1814 			nBreakPos = pLine->GetStart() + 1; 	// Sonst Endlosschleife!
1815     }
1816 
1817 
1818 	// die angeknackste Portion ist die End-Portion
1819 	pLine->SetEnd( nBreakPos );
1820 	sal_uInt16 nEndPortion = SplitTextPortion( nPara, nBreakPos );
1821 
1822 	sal_Bool bBlankSeparator = ( ( nBreakPos >= pLine->GetStart() ) &&
1823 								 ( pNode->GetText().GetChar( nBreakPos ) == ' ' ) ) ? sal_True : sal_False;
1824 	if ( bBlankSeparator )
1825 	{
1826 		// Blanks am Zeilenende generell unterdruecken...
1827 		TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1828 		TETextPortion* pTP = pTEParaPortion->GetTextPortions().GetObject( nEndPortion );
1829 		DBG_ASSERT( nBreakPos > pLine->GetStart(), "SplitTextPortion am Anfang der Zeile?" );
1830 		pTP->GetWidth() = (long)CalcTextWidth( nPara, nBreakPos-pTP->GetLen(), pTP->GetLen()-1 );
1831 	}
1832 	pLine->SetEndPortion( nEndPortion );
1833 }
1834 
1835 sal_uInt16 TextEngine::SplitTextPortion( sal_uLong nPara, sal_uInt16 nPos )
1836 {
1837 
1838 	// Die Portion bei nPos wird geplittet, wenn bei nPos nicht
1839 	// sowieso ein Wechsel ist
1840 	if ( nPos == 0 )
1841 		return 0;
1842 
1843 	sal_uInt16 nSplitPortion;
1844 	sal_uInt16 nTmpPos = 0;
1845 	TETextPortion* pTextPortion = 0;
1846 	TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1847 	sal_uInt16 nPortions = pTEParaPortion->GetTextPortions().Count();
1848 	for ( nSplitPortion = 0; nSplitPortion < nPortions; nSplitPortion++ )
1849 	{
1850 		TETextPortion* pTP = pTEParaPortion->GetTextPortions().GetObject(nSplitPortion);
1851 		nTmpPos = nTmpPos + pTP->GetLen();
1852 		if ( nTmpPos >= nPos )
1853 		{
1854 			if ( nTmpPos == nPos )	// dann braucht nichts geteilt werden
1855 				return nSplitPortion;
1856 			pTextPortion = pTP;
1857 			break;
1858 		}
1859 	}
1860 
1861 	DBG_ASSERT( pTextPortion, "Position ausserhalb des Bereichs!" );
1862 
1863 	sal_uInt16 nOverlapp = nTmpPos - nPos;
1864 	pTextPortion->GetLen() = pTextPortion->GetLen() - nOverlapp;
1865 	TETextPortion* pNewPortion = new TETextPortion( nOverlapp );
1866 	pTEParaPortion->GetTextPortions().Insert( pNewPortion, nSplitPortion+1 );
1867 	pTextPortion->GetWidth() = (long)CalcTextWidth( nPara, nPos-pTextPortion->GetLen(), pTextPortion->GetLen() );
1868 
1869 	return nSplitPortion;
1870 }
1871 
1872 void TextEngine::CreateTextPortions( sal_uLong nPara, sal_uInt16 nStartPos )
1873 {
1874 	TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1875 	TextNode* pNode = pTEParaPortion->GetNode();
1876 	DBG_ASSERT( pNode->GetText().Len(), "CreateTextPortions sollte nicht fuer leere Absaetze verwendet werden!" );
1877 
1878 	TESortedPositions aPositions;
1879 	sal_uLong nZero = 0;
1880 	aPositions.Insert( nZero );
1881 
1882 	sal_uInt16 nAttribs = pNode->GetCharAttribs().Count();
1883 	for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
1884 	{
1885 		TextCharAttrib* pAttrib = pNode->GetCharAttribs().GetAttrib( nAttr );
1886 
1887 		// Start und Ende in das Array eintragen...
1888 		// Die InsertMethode laesst keine doppelten Werte zu....
1889 		aPositions.Insert( pAttrib->GetStart() );
1890 		aPositions.Insert( pAttrib->GetEnd() );
1891 	}
1892 	aPositions.Insert( pNode->GetText().Len() );
1893 
1894     const TEWritingDirectionInfos& rWritingDirections = pTEParaPortion->GetWritingDirectionInfos();
1895 	for ( sal_uInt16 nD = 0; nD < rWritingDirections.Count(); nD++ )
1896 		aPositions.Insert( rWritingDirections[nD].nStartPos );
1897 
1898 	if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetPara() == nPara ) )
1899 	{
1900 		sal_uInt16 nLastAttr = 0xFFFF;
1901 		for( sal_uInt16 n = 0; n < mpIMEInfos->nLen; n++ )
1902 		{
1903 			if ( mpIMEInfos->pAttribs[n] != nLastAttr )
1904 			{
1905 				aPositions.Insert( mpIMEInfos->aPos.GetIndex() + n );
1906 				nLastAttr = mpIMEInfos->pAttribs[n];
1907 			}
1908 		}
1909 	}
1910 
1911 	sal_uInt16 nTabPos = pNode->GetText().Search( '\t', 0 );
1912 	while ( nTabPos != STRING_NOTFOUND )
1913 	{
1914 		aPositions.Insert( nTabPos );
1915 		aPositions.Insert( nTabPos + 1 );
1916 		nTabPos = pNode->GetText().Search( '\t', nTabPos+1 );
1917 	}
1918 
1919 	// Ab ... loeschen:
1920 	// Leider muss die Anzahl der TextPortions mit aPositions.Count()
1921 	// nicht uebereinstimmen, da evtl. Zeilenumbrueche...
1922 	sal_uInt16 nPortionStart = 0;
1923 	sal_uInt16 nInvPortion = 0;
1924     sal_uInt16 nP;
1925 	for ( nP = 0; nP < pTEParaPortion->GetTextPortions().Count(); nP++ )
1926 	{
1927 		TETextPortion* pTmpPortion = pTEParaPortion->GetTextPortions().GetObject(nP);
1928 		nPortionStart = nPortionStart + pTmpPortion->GetLen();
1929 		if ( nPortionStart >= nStartPos )
1930 		{
1931 			nPortionStart = nPortionStart - pTmpPortion->GetLen();
1932 			nInvPortion = nP;
1933 			break;
1934 		}
1935 	}
1936 	DBG_ASSERT( nP < pTEParaPortion->GetTextPortions().Count() || !pTEParaPortion->GetTextPortions().Count(), "Nichts zum loeschen: CreateTextPortions" );
1937 	if ( nInvPortion && ( nPortionStart+pTEParaPortion->GetTextPortions().GetObject(nInvPortion)->GetLen() > nStartPos ) )
1938 	{
1939 		// lieber eine davor...
1940 		// Aber nur wenn es mitten in der Portion war, sonst ist es evtl.
1941 		// die einzige in der Zeile davor !
1942 		nInvPortion--;
1943 		nPortionStart = nPortionStart - pTEParaPortion->GetTextPortions().GetObject(nInvPortion)->GetLen();
1944 	}
1945 	pTEParaPortion->GetTextPortions().DeleteFromPortion( nInvPortion );
1946 
1947 	// Eine Portion kann auch durch einen Zeilenumbruch entstanden sein:
1948 	aPositions.Insert( nPortionStart );
1949 
1950 	sal_uInt16 nInvPos;
1951     #ifdef DBG_UTIL
1952 	sal_Bool bFound =
1953     #endif
1954         aPositions.Seek_Entry( nPortionStart, &nInvPos );
1955 	DBG_ASSERT( bFound && ( nInvPos < (aPositions.Count()-1) ), "InvPos ?!" );
1956 	for ( sal_uInt16 i = nInvPos+1; i < aPositions.Count(); i++ )
1957 	{
1958 		TETextPortion* pNew = new TETextPortion( (sal_uInt16)aPositions[i] - (sal_uInt16)aPositions[i-1] );
1959 		pTEParaPortion->GetTextPortions().Insert( pNew, pTEParaPortion->GetTextPortions().Count());
1960 	}
1961 
1962 	DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "Keine Portions?!" );
1963 #ifdef EDITDEBUG
1964 	DBG_ASSERT( pTEParaPortion->DbgCheckTextPortions(), "Portions kaputt?" );
1965 #endif
1966 }
1967 
1968 void TextEngine::RecalcTextPortion( sal_uLong nPara, sal_uInt16 nStartPos, short nNewChars )
1969 {
1970 	TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1971 	DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "Keine Portions!" );
1972 	DBG_ASSERT( nNewChars, "RecalcTextPortion mit Diff == 0" );
1973 
1974 	TextNode* const pNode = pTEParaPortion->GetNode();
1975 	if ( nNewChars > 0 )
1976 	{
1977 		// Wenn an nStartPos ein Attribut beginnt/endet, oder vor nStartPos
1978 		// ein Tab steht, faengt eine neue Portion an,
1979 		// ansonsten wird die Portion an nStartPos erweitert.
1980 		// Oder wenn ganz vorne ( StartPos 0 ) und dann ein Tab
1981 
1982 		if ( ( pNode->GetCharAttribs().HasBoundingAttrib( nStartPos ) ) ||
1983 			 ( nStartPos && ( pNode->GetText().GetChar( nStartPos - 1 ) == '\t' ) ) ||
1984 			 ( ( !nStartPos && ( nNewChars < pNode->GetText().Len() ) && pNode->GetText().GetChar( nNewChars ) == '\t' ) ) )
1985 		{
1986 			sal_uInt16 nNewPortionPos = 0;
1987 			if ( nStartPos )
1988 				nNewPortionPos = SplitTextPortion( nPara, nStartPos ) + 1;
1989 //			else if ( ( pTEParaPortion->GetTextPortions().Count() == 1 ) &&
1990 //						!pTEParaPortion->GetTextPortions()[0]->GetLen() )
1991 //				pTEParaPortion->GetTextPortions().Reset();	// DummyPortion
1992 
1993 			// Eine leere Portion kann hier stehen, wenn der Absatz leer war,
1994 			// oder eine Zeile durch einen harten Zeilenumbruch entstanden ist.
1995 			if ( ( nNewPortionPos < pTEParaPortion->GetTextPortions().Count() ) &&
1996 					!pTEParaPortion->GetTextPortions()[nNewPortionPos]->GetLen() )
1997 			{
1998 				// Dann die leere Portion verwenden.
1999                 sal_uInt16 & r =
2000                     pTEParaPortion->GetTextPortions()[nNewPortionPos]->GetLen();
2001                 r = r + nNewChars;
2002 			}
2003 			else
2004 			{
2005 				TETextPortion* pNewPortion = new TETextPortion( nNewChars );
2006 				pTEParaPortion->GetTextPortions().Insert( pNewPortion, nNewPortionPos );
2007 			}
2008 		}
2009 		else
2010 		{
2011 			sal_uInt16 nPortionStart;
2012 			const sal_uInt16 nTP = pTEParaPortion->GetTextPortions().
2013 				FindPortion( nStartPos, nPortionStart );
2014 			TETextPortion* const pTP = pTEParaPortion->GetTextPortions()[ nTP ];
2015 			DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden"  );
2016 			pTP->GetLen() = pTP->GetLen() + nNewChars;
2017 			pTP->GetWidth() = (-1);
2018 		}
2019 	}
2020 	else
2021 	{
2022 		// Portion schrumpfen oder ggf. entfernen.
2023 		// Vor Aufruf dieser Methode muss sichergestellt sein, dass
2024 		// keine Portions in dem geloeschten Bereich lagen!
2025 
2026 		// Es darf keine reinragende oder im Bereich startende Portion geben,
2027 		// also muss nStartPos <= nPos <= nStartPos - nNewChars(neg.) sein
2028 		sal_uInt16 nPortion = 0;
2029 		sal_uInt16 nPos = 0;
2030 		sal_uInt16 nEnd = nStartPos-nNewChars;
2031 		sal_uInt16 nPortions = pTEParaPortion->GetTextPortions().Count();
2032 		TETextPortion* pTP = 0;
2033 		for ( nPortion = 0; nPortion < nPortions; nPortion++ )
2034 		{
2035 			pTP = pTEParaPortion->GetTextPortions()[ nPortion ];
2036 			if ( ( nPos+pTP->GetLen() ) > nStartPos )
2037 			{
2038 				DBG_ASSERT( nPos <= nStartPos, "Start falsch!" );
2039 				DBG_ASSERT( nPos+pTP->GetLen() >= nEnd, "End falsch!" );
2040 				break;
2041 			}
2042 			nPos = nPos + pTP->GetLen();
2043 		}
2044 		DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden" );
2045 		if ( ( nPos == nStartPos ) && ( (nPos+pTP->GetLen()) == nEnd ) )
2046 		{
2047 			// Portion entfernen;
2048 			pTEParaPortion->GetTextPortions().Remove( nPortion );
2049 			delete pTP;
2050 		}
2051 		else
2052 		{
2053 			DBG_ASSERT( pTP->GetLen() > (-nNewChars), "Portion zu klein zum schrumpfen!" );
2054 			pTP->GetLen() = pTP->GetLen() + nNewChars;
2055 		}
2056 		DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "RecalcTextPortions: Keine mehr da!" );
2057 	}
2058 
2059 #ifdef EDITDEBUG
2060 	DBG_ASSERT( pTEParaPortion->DbgCheckTextPortions(), "Portions kaputt?" );
2061 #endif
2062 }
2063 
2064 void TextEngine::ImpPaint( OutputDevice* pOutDev, const Point& rStartPos, Rectangle const* pPaintArea, TextSelection const* pPaintRange, TextSelection const* pSelection )
2065 {
2066 	if ( !GetUpdateMode() )
2067 		return;
2068 
2069 	if ( !IsFormatted() )
2070 		FormatDoc();
2071 
2072     bool bTransparent = false;
2073     Window* pOutWin = dynamic_cast<Window*>(pOutDev);
2074     bTransparent = (pOutWin && pOutWin->IsPaintTransparent());
2075 
2076 	long nY = rStartPos.Y();
2077 
2078 	TextPaM const* pSelStart = 0;
2079 	TextPaM const* pSelEnd = 0;
2080 	if ( pSelection && pSelection->HasRange() )
2081 	{
2082 		sal_Bool bInvers = pSelection->GetEnd() < pSelection->GetStart();
2083 		pSelStart = !bInvers ? &pSelection->GetStart() : &pSelection->GetEnd();
2084 		pSelEnd = bInvers ? &pSelection->GetStart() : &pSelection->GetEnd();
2085 	}
2086 	DBG_ASSERT( !pPaintRange || ( pPaintRange->GetStart() < pPaintRange->GetEnd() ), "ImpPaint: Paint-Range?!" );
2087 
2088 	const StyleSettings& rStyleSettings = pOutDev->GetSettings().GetStyleSettings();
2089 
2090 	// --------------------------------------------------
2091 	// Ueber alle Absaetze...
2092 	// --------------------------------------------------
2093 	for ( sal_uLong nPara = 0; nPara < mpTEParaPortions->Count(); nPara++ )
2094 	{
2095 		TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
2096 		// falls beim Tippen Idle-Formatierung, asynchrones Paint.
2097 		if ( pPortion->IsInvalid() )
2098 			return;
2099 
2100 		sal_uLong nParaHeight = CalcParaHeight( nPara );
2101 		sal_uInt16 nIndex = 0;
2102 		if ( ( !pPaintArea || ( ( nY + (long)nParaHeight ) > pPaintArea->Top() ) )
2103 				&& ( !pPaintRange || ( ( nPara >= pPaintRange->GetStart().GetPara() ) && ( nPara <= pPaintRange->GetEnd().GetPara() ) ) ) )
2104 		{
2105 			// --------------------------------------------------
2106 			// Ueber die Zeilen des Absatzes...
2107 			// --------------------------------------------------
2108 			sal_uInt16 nLines = pPortion->GetLines().Count();
2109 			for ( sal_uInt16 nLine = 0; nLine < nLines; nLine++ )
2110 			{
2111 				TextLine* pLine = pPortion->GetLines().GetObject(nLine);
2112 				Point aTmpPos( rStartPos.X() + pLine->GetStartX(), nY );
2113 
2114 				if ( ( !pPaintArea || ( ( nY + mnCharHeight ) > pPaintArea->Top() ) )
2115 					&& ( !pPaintRange || (
2116 						( TextPaM( nPara, pLine->GetStart() ) < pPaintRange->GetEnd() ) &&
2117 						( TextPaM( nPara, pLine->GetEnd() ) > pPaintRange->GetStart() ) ) ) )
2118 				{
2119 					// --------------------------------------------------
2120 					// Ueber die Portions der Zeile...
2121 					// --------------------------------------------------
2122 					nIndex = pLine->GetStart();
2123 					for ( sal_uInt16 y = pLine->GetStartPortion(); y <= pLine->GetEndPortion(); y++ )
2124 					{
2125 						DBG_ASSERT( pPortion->GetTextPortions().Count(), "Zeile ohne Textportion im Paint!" );
2126 						TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( y );
2127 						DBG_ASSERT( pTextPortion, "NULL-Pointer im Portioniterator in UpdateViews" );
2128 
2129                         ImpInitLayoutMode( pOutDev /*, pTextPortion->IsRightToLeft() */);
2130 
2131 						long nTxtWidth = pTextPortion->GetWidth();
2132                         aTmpPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nIndex, nIndex );
2133 
2134 						// nur ausgeben, was im sichtbaren Bereich beginnt:
2135 						if ( ( ( aTmpPos.X() + nTxtWidth ) >= 0 )
2136 							&& ( !pPaintRange || (
2137 								( TextPaM( nPara, nIndex ) < pPaintRange->GetEnd() ) &&
2138 									( TextPaM( nPara, nIndex + pTextPortion->GetLen() ) > pPaintRange->GetStart() ) ) ) )
2139 						{
2140 							switch ( pTextPortion->GetKind() )
2141 							{
2142 								case PORTIONKIND_TEXT:
2143 								{
2144 									{
2145 										Font aFont;
2146 										SeekCursor( nPara, nIndex+1, aFont, pOutDev );
2147                                         if( bTransparent )
2148                                             aFont.SetTransparent( sal_True );
2149 										else if ( pSelection )
2150 											aFont.SetTransparent( sal_False );
2151 										pOutDev->SetFont( aFont );
2152 
2153 										sal_uInt16 nTmpIndex = nIndex;
2154 										sal_uInt16 nEnd = nTmpIndex + pTextPortion->GetLen();
2155 										Point aPos = aTmpPos;
2156 										if ( pPaintRange )
2157 										{
2158 											// evtl soll nicht alles ausgegeben werden...
2159 											if ( ( pPaintRange->GetStart().GetPara() == nPara )
2160 													&& ( nTmpIndex < pPaintRange->GetStart().GetIndex() ) )
2161 											{
2162 												nTmpIndex = pPaintRange->GetStart().GetIndex();
2163 											}
2164 											if ( ( pPaintRange->GetEnd().GetPara() == nPara )
2165 													&& ( nEnd > pPaintRange->GetEnd().GetIndex() ) )
2166 											{
2167 												nEnd = pPaintRange->GetEnd().GetIndex();
2168 											}
2169 										}
2170 
2171 										sal_Bool bDone = sal_False;
2172 										if ( pSelStart )
2173 										{
2174 											// liegt ein Teil in der Selektion???
2175 											TextPaM aTextStart( nPara, nTmpIndex );
2176 											TextPaM aTextEnd( nPara, nEnd );
2177 											if ( ( aTextStart < *pSelEnd ) && ( aTextEnd > *pSelStart ) )
2178 											{
2179 												sal_uInt16 nL;
2180 
2181 												// 1) Bereich vor Selektion
2182 												if ( aTextStart < *pSelStart )
2183 												{
2184 													nL = pSelStart->GetIndex() - nTmpIndex;
2185 													pOutDev->SetFont( aFont);
2186                                                     aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL );
2187 													pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nL );
2188 													nTmpIndex = nTmpIndex + nL;
2189 
2190 												}
2191 												// 2) Bereich mit Selektion
2192 												nL = nEnd-nTmpIndex;
2193 												if ( aTextEnd > *pSelEnd )
2194 													nL = pSelEnd->GetIndex() - nTmpIndex;
2195 												if ( nL )
2196 												{
2197 													Color aOldTextColor = pOutDev->GetTextColor();
2198 													pOutDev->SetTextColor( rStyleSettings.GetHighlightTextColor() );
2199 													pOutDev->SetTextFillColor( rStyleSettings.GetHighlightColor() );
2200 													aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL );
2201 													pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nL );
2202 													pOutDev->SetTextColor( aOldTextColor );
2203 													pOutDev->SetTextFillColor();
2204 													nTmpIndex = nTmpIndex + nL;
2205 												}
2206 
2207 												// 3) Bereich nach Selektion
2208 												if ( nTmpIndex < nEnd )
2209 												{
2210 												    nL = nEnd-nTmpIndex;
2211                                                     aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL );
2212 													pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nEnd-nTmpIndex );
2213 												}
2214 												bDone = sal_True;
2215 											}
2216 										}
2217 										if ( !bDone )
2218                                         {
2219                                             aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nEnd );
2220 											pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nEnd-nTmpIndex );
2221                                         }
2222 									}
2223 
2224 								}
2225 								break;
2226 								case PORTIONKIND_TAB:
2227 								{
2228 									// Bei HideSelection() nur Range, pSelection = 0.
2229 									if ( pSelStart || pPaintRange )
2230 									{
2231 										Rectangle aTabArea( aTmpPos, Point( aTmpPos.X()+nTxtWidth, aTmpPos.Y()+mnCharHeight-1 ) );
2232 										sal_Bool bDone = sal_False;
2233 										if ( pSelStart )
2234 										{
2235 											// liegt der Tab in der Selektion???
2236 											TextPaM aTextStart( nPara, nIndex );
2237 											TextPaM aTextEnd( nPara, nIndex+1 );
2238 											if ( ( aTextStart < *pSelEnd ) && ( aTextEnd > *pSelStart ) )
2239 											{
2240 												Color aOldColor = pOutDev->GetFillColor();
2241 												pOutDev->SetFillColor( rStyleSettings.GetHighlightColor() );
2242 												pOutDev->DrawRect( aTabArea );
2243 												pOutDev->SetFillColor( aOldColor );
2244 												bDone = sal_True;
2245 											}
2246 										}
2247 										if ( !bDone )
2248 										{
2249 											pOutDev->Erase( aTabArea );
2250 										}
2251 									}
2252 #ifdef EDITDEBUG
2253 									Rectangle aTabArea( aTmpPos, Point( aTmpPos.X()+nTxtWidth, aTmpPos.Y()+mnCharHeight-1 ) );
2254 									Color aOldColor = pOutDev->GetFillColor();
2255                                     pOutDev->SetFillColor( (y%2) ? COL_RED : COL_GREEN );
2256 									pOutDev->DrawRect( aTabArea );
2257 									pOutDev->SetFillColor( aOldColor );
2258 #endif
2259 								}
2260 								break;
2261 								default:	DBG_ERROR( "ImpPaint: Unknown Portion-Type !" );
2262 							}
2263 						}
2264 
2265 						nIndex = nIndex + pTextPortion->GetLen();
2266 					}
2267 				}
2268 
2269 				nY += mnCharHeight;
2270 
2271 				if ( pPaintArea && ( nY >= pPaintArea->Bottom() ) )
2272 					break;	// keine sichtbaren Aktionen mehr...
2273 			}
2274 		}
2275 		else
2276 		{
2277 			nY += nParaHeight;
2278 		}
2279 
2280 		if ( pPaintArea && ( nY > pPaintArea->Bottom() ) )
2281 			break;	// keine sichtbaren Aktionen mehr...
2282 	}
2283 }
2284 
2285 sal_Bool TextEngine::CreateLines( sal_uLong nPara )
2286 {
2287 	// sal_Bool: Aenderung der Hoehe des Absatzes Ja/Nein - sal_True/sal_False
2288 
2289 	TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2290 	TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2291 	DBG_ASSERT( pTEParaPortion->IsInvalid(), "CreateLines: Portion nicht invalid!" );
2292 
2293 	sal_uInt16 nOldLineCount = pTEParaPortion->GetLines().Count();
2294 
2295 	// ---------------------------------------------------------------
2296 	// Schnelle Sonderbehandlung fuer leere Absaetze...
2297 	// ---------------------------------------------------------------
2298 	if ( pTEParaPortion->GetNode()->GetText().Len() == 0 )
2299 	{
2300 		// schnelle Sonderbehandlung...
2301 		if ( pTEParaPortion->GetTextPortions().Count() )
2302 			pTEParaPortion->GetTextPortions().Reset();
2303 		if ( pTEParaPortion->GetLines().Count() )
2304 			pTEParaPortion->GetLines().DeleteAndDestroy( 0, pTEParaPortion->GetLines().Count() );
2305 		CreateAndInsertEmptyLine( nPara );
2306 		pTEParaPortion->SetValid();
2307 		return nOldLineCount != pTEParaPortion->GetLines().Count();
2308 	}
2309 
2310 	// ---------------------------------------------------------------
2311 	// Initialisierung......
2312 	// ---------------------------------------------------------------
2313 
2314 	if ( pTEParaPortion->GetLines().Count() == 0 )
2315 	{
2316 		TextLine* pL = new TextLine;
2317 		pTEParaPortion->GetLines().Insert( pL, 0 );
2318 	}
2319 
2320 	const short nInvalidDiff = pTEParaPortion->GetInvalidDiff();
2321 	const sal_uInt16 nInvalidStart = pTEParaPortion->GetInvalidPosStart();
2322 	const sal_uInt16 nInvalidEnd =  nInvalidStart + Abs( nInvalidDiff );
2323 	sal_Bool bQuickFormat = sal_False;
2324 
2325 	if ( !pTEParaPortion->GetWritingDirectionInfos().Count() )
2326 		ImpInitWritingDirections( nPara );
2327 
2328     if ( pTEParaPortion->GetWritingDirectionInfos().Count() == 1 )
2329     {
2330 	    if ( pTEParaPortion->IsSimpleInvalid() && ( nInvalidDiff > 0 ) )
2331 	    {
2332 		    bQuickFormat = sal_True;
2333 	    }
2334 	    else if ( ( pTEParaPortion->IsSimpleInvalid() ) && ( nInvalidDiff < 0 ) )
2335 	    {
2336 		    // pruefen, ob loeschen ueber Portiongrenzen erfolgte...
2337 		    sal_uInt16 nStart = nInvalidStart;	// DOPPELT !!!!!!!!!!!!!!!
2338 		    sal_uInt16 nEnd = nStart - nInvalidDiff;  // neg.
2339 		    bQuickFormat = sal_True;
2340 		    sal_uInt16 nPos = 0;
2341 		    sal_uInt16 nPortions = pTEParaPortion->GetTextPortions().Count();
2342 		    for ( sal_uInt16 nTP = 0; nTP < nPortions; nTP++ )
2343 		    {
2344 			    // Es darf kein Start/Ende im geloeschten Bereich liegen.
2345 			    TETextPortion* const pTP = pTEParaPortion->GetTextPortions().GetObject( nTP );
2346 			    nPos = nPos + pTP->GetLen();
2347 			    if ( ( nPos > nStart ) && ( nPos < nEnd ) )
2348 			    {
2349 				    bQuickFormat = sal_False;
2350 				    break;
2351 			    }
2352 		    }
2353 	    }
2354     }
2355 
2356 	if ( bQuickFormat )
2357 		RecalcTextPortion( nPara, nInvalidStart, nInvalidDiff );
2358 	else
2359 		CreateTextPortions( nPara, nInvalidStart );
2360 
2361 	// ---------------------------------------------------------------
2362 	// Zeile mit InvalidPos suchen, eine Zeile davor beginnen...
2363 	// Zeilen flaggen => nicht removen !
2364 	// ---------------------------------------------------------------
2365 
2366 	sal_uInt16 nLine = pTEParaPortion->GetLines().Count()-1;
2367 	for ( sal_uInt16 nL = 0; nL <= nLine; nL++ )
2368 	{
2369 		TextLine* pLine = pTEParaPortion->GetLines().GetObject( nL );
2370 		if ( pLine->GetEnd() > nInvalidStart )
2371 		{
2372 			nLine = nL;
2373 			break;
2374 		}
2375 		pLine->SetValid();
2376 	}
2377 	// Eine Zeile davor beginnen...
2378 	// Wenn ganz hinten getippt wird, kann sich die Zeile davor nicht aendern.
2379 	if ( nLine && ( !pTEParaPortion->IsSimpleInvalid() || ( nInvalidEnd < pNode->GetText().Len() ) || ( nInvalidDiff <= 0 ) ) )
2380 		nLine--;
2381 
2382 	TextLine* pLine = pTEParaPortion->GetLines().GetObject( nLine );
2383 
2384 	// ---------------------------------------------------------------
2385 	// Ab hier alle Zeilen durchformatieren...
2386 	// ---------------------------------------------------------------
2387 	sal_uInt16 nDelFromLine = 0xFFFF;
2388 	sal_Bool bLineBreak = sal_False;
2389 
2390 	sal_uInt16 nIndex = pLine->GetStart();
2391 	TextLine aSaveLine( *pLine );
2392 
2393 	Font aFont;
2394 
2395 	sal_Bool bCalcPortion = sal_True;
2396 
2397 	while ( nIndex < pNode->GetText().Len() )
2398 	{
2399 		sal_Bool bEOL = sal_False;
2400 		sal_Bool bEOC = sal_False;
2401 		sal_uInt16 nPortionStart = 0;
2402 		sal_uInt16 nPortionEnd = 0;
2403 
2404 		sal_uInt16 nTmpPos = nIndex;
2405 		sal_uInt16 nTmpPortion = pLine->GetStartPortion();
2406 		long nTmpWidth = mpDoc->GetLeftMargin();
2407 //		long nXWidth = mnMaxTextWidth ? ( mnMaxTextWidth - mpDoc->GetLeftMargin() ) : 0x7FFFFFFF;
2408 		// Margin nicht abziehen, ist schon in TmpWidth enthalten.
2409 		long nXWidth = mnMaxTextWidth ? mnMaxTextWidth : 0x7FFFFFFF;
2410 		if ( nXWidth < nTmpWidth )
2411 			nXWidth = nTmpWidth;
2412 
2413 		// Portion suchen, die nicht mehr in Zeile passt....
2414 		TETextPortion* pPortion = 0;
2415 		sal_Bool bBrokenLine = sal_False;
2416 		bLineBreak = sal_False;
2417 
2418 		while ( ( nTmpWidth <= nXWidth ) && !bEOL && ( nTmpPortion < pTEParaPortion->GetTextPortions().Count() ) )
2419 		{
2420 			nPortionStart = nTmpPos;
2421 			pPortion = pTEParaPortion->GetTextPortions().GetObject( nTmpPortion );
2422 			DBG_ASSERT( pPortion->GetLen(), "Leere Portion in CreateLines ?!" );
2423 			if ( pNode->GetText().GetChar( nTmpPos ) == '\t' )
2424 			{
2425 				long nCurPos = nTmpWidth-mpDoc->GetLeftMargin();
2426 				nTmpWidth = ((nCurPos/mnDefTab)+1)*mnDefTab+mpDoc->GetLeftMargin();
2427 				pPortion->GetWidth() = nTmpWidth - nCurPos - mpDoc->GetLeftMargin();
2428 				// Wenn dies das erste Token in der Zeile ist, und
2429 				// nTmpWidth > aPaperSize.Width, habe ich eine Endlos-Schleife!
2430 				if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) )
2431 				{
2432 					// Aber was jetzt ? Tab passend machen!
2433 					pPortion->GetWidth() = nXWidth-1;
2434 					nTmpWidth = pPortion->GetWidth();
2435 					bEOL = sal_True;
2436 					bBrokenLine = sal_True;
2437 				}
2438 				pPortion->GetKind() = PORTIONKIND_TAB;
2439 			}
2440 			else
2441 			{
2442 
2443 				if ( bCalcPortion || !pPortion->HasValidSize() )
2444 					pPortion->GetWidth() = (long)CalcTextWidth( nPara, nTmpPos, pPortion->GetLen() );
2445 				nTmpWidth += pPortion->GetWidth();
2446 
2447                 pPortion->GetRightToLeft() = ImpGetRightToLeft( nPara, nTmpPos+1 );
2448 				pPortion->GetKind() = PORTIONKIND_TEXT;
2449 			}
2450 
2451 			nTmpPos = nTmpPos + pPortion->GetLen();
2452 			nPortionEnd = nTmpPos;
2453 			nTmpPortion++;
2454 		}
2455 
2456 		// das war evtl. eine Portion zu weit:
2457 		sal_Bool bFixedEnd = sal_False;
2458 		if ( nTmpWidth > nXWidth )
2459 		{
2460 			nPortionEnd = nTmpPos;
2461 			nTmpPos = nTmpPos - pPortion->GetLen();
2462 			nPortionStart = nTmpPos;
2463 			nTmpPortion--;
2464 			bEOL = sal_False;
2465 			bEOC = sal_False;
2466 
2467 			nTmpWidth -= pPortion->GetWidth();
2468 			if ( pPortion->GetKind() == PORTIONKIND_TAB )
2469 			{
2470 				bEOL = sal_True;
2471 				bFixedEnd = sal_True;
2472 			}
2473 		}
2474 		else
2475 		{
2476 			bEOL = sal_True;
2477 			bEOC = sal_True;
2478 			pLine->SetEnd( nPortionEnd );
2479 			DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "Keine TextPortions?" );
2480 			pLine->SetEndPortion( (sal_uInt16)pTEParaPortion->GetTextPortions().Count() - 1 );
2481 		}
2482 
2483 		if ( bFixedEnd )
2484 		{
2485 			pLine->SetEnd( nPortionStart );
2486 			pLine->SetEndPortion( nTmpPortion-1 );
2487 		}
2488 		else if ( bLineBreak || bBrokenLine )
2489 		{
2490 			pLine->SetEnd( nPortionStart+1 );
2491 			pLine->SetEndPortion( nTmpPortion-1 );
2492 			bEOC = sal_False; // wurde oben gesetzt, vielleich mal die if's umstellen?
2493 		}
2494 		else if ( !bEOL )
2495 		{
2496 			DBG_ASSERT( (nPortionEnd-nPortionStart) == pPortion->GetLen(), "Doch eine andere Portion?!" );
2497 			long nRemainingWidth = mnMaxTextWidth - nTmpWidth;
2498 			ImpBreakLine( nPara, pLine, pPortion, nPortionStart, nRemainingWidth );
2499 		}
2500 
2501 		if ( ( ImpGetAlign() == TXTALIGN_CENTER ) || ( ImpGetAlign() == TXTALIGN_RIGHT ) )
2502 		{
2503 			// Ausrichten...
2504 			long nTextWidth = 0;
2505 			for ( sal_uInt16 nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ )
2506 			{
2507 				TETextPortion* pTextPortion = pTEParaPortion->GetTextPortions().GetObject( nTP );
2508 				nTextWidth += pTextPortion->GetWidth();
2509 			}
2510 			long nSpace = mnMaxTextWidth - nTextWidth;
2511 			if ( nSpace > 0 )
2512 			{
2513 				if ( ImpGetAlign() == TXTALIGN_CENTER )
2514 					pLine->SetStartX( (sal_uInt16)(nSpace / 2) );
2515 				else	// TXTALIGN_RIGHT
2516 					pLine->SetStartX( (sal_uInt16)nSpace );
2517 			}
2518 		}
2519 		else
2520 		{
2521 			pLine->SetStartX( mpDoc->GetLeftMargin() );
2522 		}
2523 
2524 		// -----------------------------------------------------------------
2525 		// pruefen, ob die Zeile neu ausgegeben werden muss...
2526 		// -----------------------------------------------------------------
2527 		pLine->SetInvalid();
2528 
2529 		if ( pTEParaPortion->IsSimpleInvalid() )
2530 		{
2531 			// Aenderung durch einfache Textaenderung...
2532 			// Formatierung nicht abbrechen, da Portions evtl. wieder
2533 			// gesplittet werden muessen!
2534 			// Wenn irgendwann mal abbrechbar, dann fogende Zeilen Validieren!
2535 			// Aber ggf. als Valid markieren, damit weniger Ausgabe...
2536 			if ( pLine->GetEnd() < nInvalidStart )
2537 			{
2538 				if ( *pLine == aSaveLine )
2539 				{
2540 					pLine->SetValid();
2541 				}
2542 			}
2543 			else
2544 			{
2545 				sal_uInt16 nStart = pLine->GetStart();
2546 				sal_uInt16 nEnd = pLine->GetEnd();
2547 
2548 				if ( nStart > nInvalidEnd )
2549 				{
2550 					if ( ( ( nStart-nInvalidDiff ) == aSaveLine.GetStart() ) &&
2551 							( ( nEnd-nInvalidDiff ) == aSaveLine.GetEnd() ) )
2552 					{
2553 						pLine->SetValid();
2554 						if ( bCalcPortion && bQuickFormat )
2555 						{
2556 							bCalcPortion = sal_False;
2557 							pTEParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
2558 							break;
2559 						}
2560 					}
2561 				}
2562 				else if ( bQuickFormat && ( nEnd > nInvalidEnd) )
2563 				{
2564 					// Wenn die ungueltige Zeile so endet, dass die naechste an
2565 					// der 'gleichen' Textstelle wie vorher beginnt, also nicht
2566 					// anders umgebrochen wird, brauche ich dort auch nicht die
2567 					// textbreiten neu bestimmen:
2568 					if ( nEnd == ( aSaveLine.GetEnd() + nInvalidDiff ) )
2569 					{
2570 						bCalcPortion = sal_False;
2571 						pTEParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
2572 						break;
2573 					}
2574 				}
2575 			}
2576 		}
2577 
2578 		nIndex = pLine->GetEnd();	// naechste Zeile Start = letzte Zeile Ende
2579 									// weil nEnd hinter das letzte Zeichen zeigt!
2580 
2581 		sal_uInt16 nEndPortion = pLine->GetEndPortion();
2582 
2583 		// Naechste Zeile oder ggf. neue Zeile....
2584 		pLine = 0;
2585 		if ( nLine < pTEParaPortion->GetLines().Count()-1 )
2586 			pLine = pTEParaPortion->GetLines().GetObject( ++nLine );
2587 		if ( pLine && ( nIndex >= pNode->GetText().Len() ) )
2588 		{
2589 			nDelFromLine = nLine;
2590 			break;
2591 		}
2592 		if ( !pLine && ( nIndex < pNode->GetText().Len() )  )
2593 		{
2594 			pLine = new TextLine;
2595 			pTEParaPortion->GetLines().Insert( pLine, ++nLine );
2596 		}
2597 		if ( pLine )
2598 		{
2599 			aSaveLine = *pLine;
2600 			pLine->SetStart( nIndex );
2601 			pLine->SetEnd( nIndex );
2602 			pLine->SetStartPortion( nEndPortion+1 );
2603 			pLine->SetEndPortion( nEndPortion+1 );
2604 		}
2605 	}	// while ( Index < Len )
2606 
2607 	if ( nDelFromLine != 0xFFFF )
2608 		pTEParaPortion->GetLines().DeleteAndDestroy( nDelFromLine, pTEParaPortion->GetLines().Count() - nDelFromLine );
2609 
2610 	DBG_ASSERT( pTEParaPortion->GetLines().Count(), "Keine Zeile nach CreateLines!" );
2611 
2612 	if ( bLineBreak == sal_True )
2613 		CreateAndInsertEmptyLine( nPara );
2614 
2615 	pTEParaPortion->SetValid();
2616 
2617 	return nOldLineCount != pTEParaPortion->GetLines().Count();
2618 }
2619 
2620 String TextEngine::GetWord( const TextPaM& rCursorPos, TextPaM* pStartOfWord )
2621 {
2622 	String aWord;
2623 	if ( rCursorPos.GetPara() < mpDoc->GetNodes().Count() )
2624 	{
2625 		TextSelection aSel( rCursorPos );
2626 		TextNode* pNode = mpDoc->GetNodes().GetObject(  rCursorPos.GetPara() );
2627 		uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator();
2628 		i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), rCursorPos.GetIndex(), GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True );
2629 		aSel.GetStart().GetIndex() = (sal_uInt16)aBoundary.startPos;
2630 		aSel.GetEnd().GetIndex() = (sal_uInt16)aBoundary.endPos;
2631 		aWord = pNode->GetText().Copy( aSel.GetStart().GetIndex(), aSel.GetEnd().GetIndex() - aSel.GetStart().GetIndex() );
2632 		if ( pStartOfWord )
2633 			*pStartOfWord = aSel.GetStart();
2634 	}
2635 	return aWord;
2636 }
2637 
2638 sal_Bool TextEngine::Read( SvStream& rInput, const TextSelection* pSel )
2639 {
2640 	sal_Bool bUpdate = GetUpdateMode();
2641 	SetUpdateMode( sal_False );
2642 
2643 	UndoActionStart();
2644 	TextSelection aSel;
2645 	if ( pSel )
2646 		aSel = *pSel;
2647 	else
2648 	{
2649 		sal_uLong nParas = mpDoc->GetNodes().Count();
2650 		TextNode* pNode = mpDoc->GetNodes().GetObject( nParas - 1 );
2651 		aSel = TextPaM( nParas-1 , pNode->GetText().Len() );
2652 	}
2653 
2654 	if ( aSel.HasRange() )
2655 		aSel = ImpDeleteText( aSel );
2656 
2657 	ByteString aLine;
2658 	sal_Bool bDone = rInput.ReadLine( aLine );
2659 	String aTmpStr( aLine, rInput.GetStreamCharSet() ), aStr;
2660 	while ( bDone )
2661 	{
2662 		aSel = ImpInsertText( aSel, aTmpStr );
2663 		bDone = rInput.ReadLine( aLine );
2664 		aTmpStr = String( aLine, rInput.GetStreamCharSet() );
2665 		if ( bDone )
2666 			aSel = ImpInsertParaBreak( aSel.GetEnd() );
2667 	}
2668 
2669 	UndoActionEnd();
2670 
2671 	TextSelection aNewSel( aSel.GetEnd(), aSel.GetEnd() );
2672 
2673 	// Damit bei FormatAndUpdate nicht auf die ungueltige Selektion zugegriffen wird.
2674 	if ( GetActiveView() )
2675 		GetActiveView()->ImpSetSelection( aNewSel );
2676 
2677 	SetUpdateMode( bUpdate );
2678 	FormatAndUpdate( GetActiveView() );
2679 
2680 	return rInput.GetError() ? sal_False : sal_True;
2681 }
2682 
2683 sal_Bool TextEngine::Write( SvStream& rOutput, const TextSelection* pSel, sal_Bool bHTML )
2684 {
2685 	TextSelection aSel;
2686 	if ( pSel )
2687 		aSel = *pSel;
2688 	else
2689 	{
2690 		sal_uLong nParas = mpDoc->GetNodes().Count();
2691 		TextNode* pNode = mpDoc->GetNodes().GetObject( nParas - 1 );
2692 		aSel.GetStart() = TextPaM( 0, 0 );
2693 		aSel.GetEnd() = TextPaM( nParas-1, pNode->GetText().Len() );
2694 	}
2695 
2696 	if ( bHTML )
2697 	{
2698 		rOutput.WriteLine( "<HTML>" );
2699 		rOutput.WriteLine( "<BODY>" );
2700 	}
2701 
2702 	for ( sal_uLong nPara = aSel.GetStart().GetPara(); nPara <= aSel.GetEnd().GetPara(); nPara++  )
2703 	{
2704 		TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2705 
2706 		sal_uInt16 nStartPos = 0;
2707 		sal_uInt16 nEndPos = pNode->GetText().Len();
2708 		if ( nPara == aSel.GetStart().GetPara() )
2709 			nStartPos = aSel.GetStart().GetIndex();
2710 		if ( nPara == aSel.GetEnd().GetPara() )
2711 			nEndPos = aSel.GetEnd().GetIndex();
2712 
2713 		String aText;
2714 		if ( !bHTML )
2715 		{
2716 			aText = pNode->GetText().Copy( nStartPos, nEndPos-nStartPos );
2717 		}
2718 		else
2719 		{
2720 			aText.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "<P STYLE=\"margin-bottom: 0cm\">" ) );
2721 
2722 			if ( nStartPos == nEndPos )
2723 			{
2724 				// Leerzeilen werden von Writer wegoptimiert
2725 				aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "<BR>" ) );
2726 			}
2727 			else
2728 			{
2729 				sal_uInt16 nTmpStart = nStartPos;
2730 				sal_uInt16 nTmpEnd = nEndPos;
2731 				do
2732 				{
2733 					TextCharAttrib* pAttr = pNode->GetCharAttribs().FindNextAttrib( TEXTATTR_HYPERLINK, nTmpStart, nEndPos );
2734 					nTmpEnd = pAttr ? pAttr->GetStart() : nEndPos;
2735 
2736 					// Text vor dem Attribut
2737 					aText += pNode->GetText().Copy( nTmpStart, nTmpEnd-nTmpStart );
2738 
2739 					if ( pAttr )
2740 					{
2741 						nTmpEnd = Min( pAttr->GetEnd(), nEndPos );
2742 
2743 						// z.B. <A HREF="http://www.mopo.de/">Morgenpost</A>
2744 						aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "<A HREF=\"" ) );
2745 						aText += ((const TextAttribHyperLink&) pAttr->GetAttr() ).GetURL();
2746 						aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "\">" ) );
2747 						nTmpStart = pAttr->GetStart();
2748 						aText += pNode->GetText().Copy( nTmpStart, nTmpEnd-nTmpStart );
2749 						aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "</A>" ) );
2750 
2751 						nTmpStart = pAttr->GetEnd();
2752 					}
2753 				} while ( nTmpEnd < nEndPos );
2754 			}
2755 
2756 			aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "</P>" ) );
2757 		}
2758 		rOutput.WriteLine( ByteString( aText, rOutput.GetStreamCharSet() ) );
2759 	}
2760 
2761 	if ( bHTML )
2762 	{
2763 		rOutput.WriteLine( "</BODY>" );
2764 		rOutput.WriteLine( "</HTML>" );
2765 	}
2766 
2767 	return rOutput.GetError() ? sal_False : sal_True;
2768 }
2769 
2770 void TextEngine::RemoveAttribs( sal_uLong nPara, sal_Bool bIdleFormatAndUpdate )
2771 {
2772 	if ( nPara < mpDoc->GetNodes().Count() )
2773 	{
2774 		TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2775 		if ( pNode->GetCharAttribs().Count() )
2776 		{
2777 			pNode->GetCharAttribs().Clear( sal_True );
2778 
2779 			TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2780 			pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() );
2781 
2782 			mbFormatted = sal_False;
2783 
2784     		if ( bIdleFormatAndUpdate )
2785     			IdleFormatAndUpdate( NULL, 0xFFFF );
2786     		else
2787     			FormatAndUpdate( NULL );
2788 		}
2789 	}
2790 }
2791 void TextEngine::RemoveAttribs( sal_uLong nPara, sal_uInt16 nWhich, sal_Bool bIdleFormatAndUpdate )
2792 {
2793     if ( nPara < mpDoc->GetNodes().Count() )
2794     {
2795         TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2796         if ( pNode->GetCharAttribs().Count() )
2797         {
2798             TextCharAttribList& rAttribs = pNode->GetCharAttribs();
2799             sal_uInt16 nAttrCount = rAttribs.Count();
2800             for(sal_uInt16 nAttr = nAttrCount; nAttr; --nAttr)
2801             {
2802                 if(rAttribs.GetAttrib( nAttr - 1 )->Which() == nWhich)
2803                     rAttribs.RemoveAttrib( nAttr -1 );
2804             }
2805             TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2806             pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() );
2807             mbFormatted = sal_False;
2808             if(bIdleFormatAndUpdate)
2809                 IdleFormatAndUpdate( NULL, 0xFFFF );
2810             else
2811                 FormatAndUpdate( NULL );
2812         }
2813     }
2814 }
2815 void TextEngine::RemoveAttrib( sal_uLong nPara, const TextCharAttrib& rAttrib )
2816 {
2817     if ( nPara < mpDoc->GetNodes().Count() )
2818     {
2819         TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2820         if ( pNode->GetCharAttribs().Count() )
2821         {
2822             TextCharAttribList& rAttribs = pNode->GetCharAttribs();
2823             sal_uInt16 nAttrCount = rAttribs.Count();
2824             for(sal_uInt16 nAttr = nAttrCount; nAttr; --nAttr)
2825             {
2826                 if(rAttribs.GetAttrib( nAttr - 1 ) == &rAttrib)
2827                 {
2828                     rAttribs.RemoveAttrib( nAttr -1 );
2829                     break;
2830                 }
2831             }
2832             TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2833             pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() );
2834             mbFormatted = sal_False;
2835             FormatAndUpdate( NULL );
2836         }
2837     }
2838 }
2839 
2840 void TextEngine::SetAttrib( const TextAttrib& rAttr, sal_uLong nPara, sal_uInt16 nStart, sal_uInt16 nEnd, sal_Bool bIdleFormatAndUpdate )
2841 {
2842 	// Es wird hier erstmal nicht geprueft, ob sich Attribute ueberlappen!
2843 	// Diese Methode ist erstmal nur fuer einen Editor, der fuer eine Zeile
2844 	// _schnell_ das Syntax-Highlight einstellen will.
2845 
2846 	// Da die TextEngine z.Zt fuer Editoren gedacht ist gibt es auch kein
2847 	// Undo fuer Attribute!
2848 
2849 	if ( nPara < mpDoc->GetNodes().Count() )
2850 	{
2851 		TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2852 		TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2853 
2854 		sal_uInt16 nMax = pNode->GetText().Len();
2855 		if ( nStart > nMax )
2856 			nStart = nMax;
2857 		if ( nEnd > nMax )
2858 			nEnd = nMax;
2859 
2860 		pNode->GetCharAttribs().InsertAttrib( new TextCharAttrib( rAttr, nStart, nEnd ) );
2861 		pTEParaPortion->MarkSelectionInvalid( nStart, nEnd );
2862 
2863 		mbFormatted = sal_False;
2864 		if ( bIdleFormatAndUpdate )
2865 			IdleFormatAndUpdate( NULL, 0xFFFF );
2866 		else
2867 			FormatAndUpdate( NULL );
2868 	}
2869 }
2870 
2871 void TextEngine::SetTextAlign( TxtAlign eAlign )
2872 {
2873 	if ( eAlign != meAlign )
2874 	{
2875 		meAlign = eAlign;
2876 		FormatFullDoc();
2877 		UpdateViews();
2878 	}
2879 }
2880 
2881 
2882 void TextEngine::ValidateSelection( TextSelection& rSel ) const
2883 {
2884 	ValidatePaM( rSel.GetStart() );
2885 	ValidatePaM( rSel.GetEnd() );
2886 }
2887 
2888 void TextEngine::ValidatePaM( TextPaM& rPaM ) const
2889 {
2890 	sal_uLong nMaxPara = mpDoc->GetNodes().Count() - 1;
2891 	if ( rPaM.GetPara() > nMaxPara )
2892 	{
2893 		rPaM.GetPara() = nMaxPara;
2894 		rPaM.GetIndex() = 0xFFFF;
2895 	}
2896 
2897 	sal_uInt16 nMaxIndex = GetTextLen( rPaM.GetPara() );
2898 	if ( rPaM.GetIndex() > nMaxIndex )
2899 		rPaM.GetIndex() = nMaxIndex;
2900 }
2901 
2902 
2903 // Status & Selektionsanpassung
2904 
2905 void TextEngine::ImpParagraphInserted( sal_uLong nPara )
2906 {
2907 	// Die aktive View braucht nicht angepasst werden, aber bei allen
2908 	// passiven muss die Selektion angepasst werden:
2909 	if ( mpViews->Count() > 1 )
2910 	{
2911 		for ( sal_uInt16 nView = mpViews->Count(); nView; )
2912 		{
2913 			TextView* pView = mpViews->GetObject( --nView );
2914 			if ( pView != GetActiveView() )
2915 			{
2916 //				sal_Bool bInvers = pView->maSelection.GetEnd() < pView->maSelection.GetStart();
2917 //				TextPaM& rMin = !bInvers ? pView->maSelection.GetStart(): pView->maSelection.GetEnd();
2918 //				TextPaM& rMax = bInvers ? pView->maSelection.GetStart() : pView->maSelection.GetEnd();
2919 //
2920 //				if ( rMin.GetPara() >= nPara )
2921 //					rMin.GetPara()++;
2922 //				if ( rMax.GetPara() >= nPara )
2923 //					rMax.GetPara()++;
2924 				for ( int n = 0; n <= 1; n++ )
2925 				{
2926                     TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd();
2927 					if ( rPaM.GetPara() >= nPara )
2928 						rPaM.GetPara()++;
2929 				}
2930 			}
2931 		}
2932 	}
2933 	Broadcast( TextHint( TEXT_HINT_PARAINSERTED, nPara ) );
2934 }
2935 
2936 void TextEngine::ImpParagraphRemoved( sal_uLong nPara )
2937 {
2938 	if ( mpViews->Count() > 1 )
2939 	{
2940 		for ( sal_uInt16 nView = mpViews->Count(); nView; )
2941 		{
2942 			TextView* pView = mpViews->GetObject( --nView );
2943 			if ( pView != GetActiveView() )
2944 			{
2945 				sal_uLong nParas = mpDoc->GetNodes().Count();
2946 				for ( int n = 0; n <= 1; n++ )
2947 				{
2948                     TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd();
2949 					if ( rPaM.GetPara() > nPara )
2950 						rPaM.GetPara()--;
2951 					else if ( rPaM.GetPara() == nPara )
2952 					{
2953 						rPaM.GetIndex() = 0;
2954 						if ( rPaM.GetPara() >= nParas )
2955 							rPaM.GetPara()--;
2956 					}
2957 				}
2958 			}
2959 		}
2960 	}
2961 	Broadcast( TextHint( TEXT_HINT_PARAREMOVED, nPara ) );
2962 }
2963 
2964 void TextEngine::ImpCharsRemoved( sal_uLong nPara, sal_uInt16 nPos, sal_uInt16 nChars )
2965 {
2966 	if ( mpViews->Count() > 1 )
2967 	{
2968 		for ( sal_uInt16 nView = mpViews->Count(); nView; )
2969 		{
2970 			TextView* pView = mpViews->GetObject( --nView );
2971 			if ( pView != GetActiveView() )
2972 			{
2973 				sal_uInt16 nEnd = nPos+nChars;
2974 				for ( int n = 0; n <= 1; n++ )
2975 				{
2976                     TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd();
2977 					if ( rPaM.GetPara() == nPara )
2978 					{
2979 						if ( rPaM.GetIndex() > nEnd )
2980 							rPaM.GetIndex() = rPaM.GetIndex() - nChars;
2981 						else if ( rPaM.GetIndex() > nPos )
2982 							rPaM.GetIndex() = nPos;
2983 					}
2984 				}
2985 			}
2986 		}
2987 	}
2988 	Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, nPara ) );
2989 }
2990 
2991 void TextEngine::ImpCharsInserted( sal_uLong nPara, sal_uInt16 nPos, sal_uInt16 nChars )
2992 {
2993 	if ( mpViews->Count() > 1 )
2994 	{
2995 		for ( sal_uInt16 nView = mpViews->Count(); nView; )
2996 		{
2997 			TextView* pView = mpViews->GetObject( --nView );
2998 			if ( pView != GetActiveView() )
2999 			{
3000 				for ( int n = 0; n <= 1; n++ )
3001 				{
3002                     TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd();
3003 					if ( rPaM.GetPara() == nPara )
3004 					{
3005 						if ( rPaM.GetIndex() >= nPos )
3006 							rPaM.GetIndex() = rPaM.GetIndex() + nChars;
3007 					}
3008 				}
3009 			}
3010 		}
3011 	}
3012 	Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, nPara ) );
3013 }
3014 
3015 void TextEngine::ImpFormattingParagraph( sal_uLong nPara )
3016 {
3017 	Broadcast( TextHint( TEXT_HINT_FORMATPARA, nPara ) );
3018 }
3019 
3020 void TextEngine::ImpTextHeightChanged()
3021 {
3022 	Broadcast( TextHint( TEXT_HINT_TEXTHEIGHTCHANGED ) );
3023 }
3024 
3025 void TextEngine::ImpTextFormatted()
3026 {
3027 	Broadcast( TextHint( TEXT_HINT_TEXTFORMATTED ) );
3028 }
3029 
3030 void TextEngine::Draw( OutputDevice* pDev, const Point& rPos )
3031 {
3032 	ImpPaint( pDev, rPos, NULL );
3033 }
3034 
3035 void TextEngine::SetLeftMargin( sal_uInt16 n )
3036 {
3037 	mpDoc->SetLeftMargin( n );
3038 }
3039 
3040 sal_uInt16 TextEngine::GetLeftMargin() const
3041 {
3042 	return mpDoc->GetLeftMargin();
3043 }
3044 
3045 uno::Reference< i18n::XBreakIterator > TextEngine::GetBreakIterator()
3046 {
3047 	if ( !mxBreakIterator.is() )
3048 		mxBreakIterator = vcl::unohelper::CreateBreakIterator();
3049     DBG_ASSERT( mxBreakIterator.is(), "Could not create BreakIterator" );
3050 	return mxBreakIterator;
3051 }
3052 
3053 void TextEngine::SetLocale( const ::com::sun::star::lang::Locale& rLocale )
3054 {
3055     maLocale = rLocale;
3056     delete mpLocaleDataWrapper;
3057     mpLocaleDataWrapper = NULL;
3058 }
3059 
3060 ::com::sun::star::lang::Locale TextEngine::GetLocale()
3061 {
3062 	if ( !maLocale.Language.getLength() )
3063 	{
3064         maLocale = Application::GetSettings().GetUILocale();
3065 	}
3066 	return maLocale;
3067 }
3068 
3069 LocaleDataWrapper* TextEngine::ImpGetLocaleDataWrapper()
3070 {
3071     if ( !mpLocaleDataWrapper )
3072         mpLocaleDataWrapper = new LocaleDataWrapper( vcl::unohelper::GetMultiServiceFactory(), GetLocale() );
3073 
3074     return mpLocaleDataWrapper;
3075 }
3076 
3077 void TextEngine::SetRightToLeft( sal_Bool bR2L )
3078 {
3079     if ( mbRightToLeft != bR2L )
3080     {
3081         mbRightToLeft = bR2L;
3082         meAlign = bR2L ? TXTALIGN_RIGHT : TXTALIGN_LEFT;
3083 		FormatFullDoc();
3084 		UpdateViews();
3085     }
3086 }
3087 
3088 void TextEngine::ImpInitWritingDirections( sal_uLong nPara )
3089 {
3090 	TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
3091 	TEWritingDirectionInfos& rInfos = pParaPortion->GetWritingDirectionInfos();
3092 	rInfos.Remove( 0, rInfos.Count() );
3093 
3094 	if ( pParaPortion->GetNode()->GetText().Len() )
3095 	{
3096         const UBiDiLevel nBidiLevel = IsRightToLeft() ? 1 /*RTL*/ : 0 /*LTR*/;
3097         String aText( pParaPortion->GetNode()->GetText() );
3098 
3099         //
3100         // Bidi functions from icu 2.0
3101         //
3102         UErrorCode nError = U_ZERO_ERROR;
3103         UBiDi* pBidi = ubidi_openSized( aText.Len(), 0, &nError );
3104         nError = U_ZERO_ERROR;
3105 
3106         ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.GetBuffer()), aText.Len(), nBidiLevel, NULL, &nError );	// UChar != sal_Unicode in MinGW
3107         nError = U_ZERO_ERROR;
3108 
3109         long nCount = ubidi_countRuns( pBidi, &nError );
3110 
3111         int32_t nStart = 0;
3112         int32_t nEnd;
3113         UBiDiLevel nCurrDir;
3114 
3115         for ( sal_uInt16 nIdx = 0; nIdx < nCount; ++nIdx )
3116         {
3117             ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir );
3118             rInfos.Insert( TEWritingDirectionInfo( nCurrDir, (sal_uInt16)nStart, (sal_uInt16)nEnd ), rInfos.Count() );
3119             nStart = nEnd;
3120         }
3121 
3122         ubidi_close( pBidi );
3123 	}
3124 
3125     // No infos mean no CTL and default dir is L2R...
3126     if ( !rInfos.Count() )
3127         rInfos.Insert( TEWritingDirectionInfo( 0, 0, (sal_uInt16)pParaPortion->GetNode()->GetText().Len() ), rInfos.Count() );
3128 
3129 }
3130 
3131 sal_uInt8 TextEngine::ImpGetRightToLeft( sal_uLong nPara, sal_uInt16 nPos, sal_uInt16* pStart, sal_uInt16* pEnd )
3132 {
3133     sal_uInt8 nRightToLeft = 0;
3134 
3135     TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
3136     if ( pNode && pNode->GetText().Len() )
3137     {
3138 		TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
3139 		if ( !pParaPortion->GetWritingDirectionInfos().Count() )
3140 			ImpInitWritingDirections( nPara );
3141 
3142 		TEWritingDirectionInfos& rDirInfos = pParaPortion->GetWritingDirectionInfos();
3143 		for ( sal_uInt16 n = 0; n < rDirInfos.Count(); n++ )
3144 		{
3145 			if ( ( rDirInfos[n].nStartPos <= nPos ) && ( rDirInfos[n].nEndPos >= nPos ) )
3146 	   		{
3147 				nRightToLeft = rDirInfos[n].nType;
3148                 if ( pStart )
3149                     *pStart = rDirInfos[n].nStartPos;
3150                 if ( pEnd )
3151                     *pEnd = rDirInfos[n].nEndPos;
3152 				break;
3153 			}
3154 		}
3155     }
3156     return nRightToLeft;
3157 }
3158 
3159 long TextEngine::ImpGetPortionXOffset( sal_uLong nPara, TextLine* pLine, sal_uInt16 nTextPortion )
3160 {
3161 	long nX = pLine->GetStartX();
3162 
3163 	TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
3164 
3165     for ( sal_uInt16 i = pLine->GetStartPortion(); i < nTextPortion; i++ )
3166 	{
3167 		TETextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( i );
3168 		nX += pPortion->GetWidth();
3169     }
3170 
3171     TETextPortion* pDestPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
3172     if ( pDestPortion->GetKind() != PORTIONKIND_TAB )
3173     {
3174         if ( !IsRightToLeft() && pDestPortion->GetRightToLeft() )
3175         {
3176             // Portions behind must be added, visual before this portion
3177             sal_uInt16 nTmpPortion = nTextPortion+1;
3178             while ( nTmpPortion <= pLine->GetEndPortion() )
3179             {
3180 		        TETextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
3181                 if ( pNextTextPortion->GetRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
3182                     nX += pNextTextPortion->GetWidth();
3183                 else
3184                     break;
3185                 nTmpPortion++;
3186             }
3187             // Portions before must be removed, visual behind this portion
3188             nTmpPortion = nTextPortion;
3189             while ( nTmpPortion > pLine->GetStartPortion() )
3190             {
3191                 --nTmpPortion;
3192 		        TETextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
3193                 if ( pPrevTextPortion->GetRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
3194                     nX -= pPrevTextPortion->GetWidth();
3195                 else
3196                     break;
3197             }
3198         }
3199         else if ( IsRightToLeft() && !pDestPortion->IsRightToLeft() )
3200         {
3201             // Portions behind must be removed, visual behind this portion
3202             sal_uInt16 nTmpPortion = nTextPortion+1;
3203             while ( nTmpPortion <= pLine->GetEndPortion() )
3204             {
3205 		        TETextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
3206                 if ( !pNextTextPortion->IsRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
3207                     nX += pNextTextPortion->GetWidth();
3208                 else
3209                     break;
3210                 nTmpPortion++;
3211             }
3212             // Portions before must be added, visual before this portion
3213             nTmpPortion = nTextPortion;
3214             while ( nTmpPortion > pLine->GetStartPortion() )
3215             {
3216                 --nTmpPortion;
3217 		        TETextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
3218                 if ( !pPrevTextPortion->IsRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
3219                     nX -= pPrevTextPortion->GetWidth();
3220                 else
3221                     break;
3222             }
3223         }
3224     }
3225 /*
3226     if ( IsRightToLeft() )
3227     {
3228         // Switch X postions...
3229         DBG_ASSERT( GetMaxTextWidth(), "GetPortionXOffset - max text width?!" );
3230         DBG_ASSERT( nX <= (long)GetMaxTextWidth(), "GetPortionXOffset - position out of paper size!" );
3231         nX = GetMaxTextWidth() - nX;
3232         nX -= pDestPortion->GetWidth();
3233     }
3234 */
3235 
3236     return nX;
3237 }
3238 
3239 void TextEngine::ImpInitLayoutMode( OutputDevice* pOutDev, sal_Bool bDrawingR2LPortion )
3240 {
3241     sal_uLong nLayoutMode = pOutDev->GetLayoutMode();
3242 
3243     nLayoutMode &= ~(TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_COMPLEX_DISABLED | TEXT_LAYOUT_BIDI_STRONG );
3244     if ( bDrawingR2LPortion )
3245         nLayoutMode |= TEXT_LAYOUT_BIDI_RTL;
3246 
3247     pOutDev->SetLayoutMode( nLayoutMode );
3248 }
3249 
3250 TxtAlign TextEngine::ImpGetAlign() const
3251 {
3252     TxtAlign eAlign = meAlign;
3253     if ( IsRightToLeft() )
3254     {
3255         if ( eAlign == TXTALIGN_LEFT )
3256             eAlign = TXTALIGN_RIGHT;
3257         else if ( eAlign == TXTALIGN_RIGHT )
3258             eAlign = TXTALIGN_LEFT;
3259     }
3260     return eAlign;
3261 }
3262 
3263 long TextEngine::ImpGetOutputOffset( sal_uLong nPara, TextLine* pLine, sal_uInt16 nIndex, sal_uInt16 nIndex2 )
3264 {
3265     TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
3266 
3267     sal_uInt16 nPortionStart;
3268     sal_uInt16 nPortion = pPortion->GetTextPortions().FindPortion( nIndex, nPortionStart, sal_True );
3269 
3270     TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( nPortion );
3271 
3272     long nX;
3273 
3274     if ( ( nIndex == nPortionStart ) && ( nIndex == nIndex2 )  )
3275     {
3276         // Output of full portion, so we need portion x offset.
3277         // Use ImpGetPortionXOffset, because GetXPos may deliver left or right position from portioon, depending on R2L, L2R
3278         nX = ImpGetPortionXOffset( nPara, pLine, nPortion );
3279         if ( IsRightToLeft() )
3280         {
3281 	        nX = -nX -pTextPortion->GetWidth();
3282         }
3283     }
3284     else
3285     {
3286         nX = ImpGetXPos( nPara, pLine, nIndex, nIndex == nPortionStart );
3287         if ( nIndex2 != nIndex )
3288         {
3289             long nX2 = ImpGetXPos( nPara, pLine, nIndex2, sal_False );
3290             if ( ( !IsRightToLeft() && ( nX2 < nX ) ) ||
3291                  ( IsRightToLeft() && ( nX2 > nX ) ) )
3292             {
3293                 nX = nX2;
3294             }
3295         }
3296         if ( IsRightToLeft() )
3297         {
3298 	        nX = -nX;
3299         }
3300     }
3301 
3302     return nX;
3303 }
3304