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