fdo#74697 Add Bluez 5 support for impress remote.
[LibreOffice.git] / vcl / source / edit / texteng.cxx
blob7e2186e47da736efae849e4c1142ab37f822a9ac
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <tools/stream.hxx>
22 #include <vcl/texteng.hxx>
23 #include <vcl/textview.hxx>
24 #include <textdoc.hxx>
25 #include <textdat2.hxx>
26 #include <textundo.hxx>
27 #include <textund2.hxx>
28 #include <svl/ctloptions.hxx>
29 #include <vcl/window.hxx>
31 #include <vcl/edit.hxx>
32 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
33 #include <com/sun/star/beans/PropertyValues.hpp>
35 #include <com/sun/star/i18n/XBreakIterator.hpp>
37 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
39 #include <com/sun/star/i18n/WordType.hpp>
41 #include <com/sun/star/i18n/InputSequenceChecker.hpp>
42 #include <com/sun/star/i18n/InputSequenceCheckMode.hpp>
43 #include <com/sun/star/i18n/ScriptType.hpp>
45 #include <comphelper/processfactory.hxx>
47 #include <unotools/localedatawrapper.hxx>
48 #include <vcl/unohelp.hxx>
50 #include <vcl/svapp.hxx>
51 #include <vcl/metric.hxx>
53 #include <unicode/ubidi.h>
55 #include <set>
56 #include <vector>
57 #include <boost/foreach.hpp>
59 using namespace ::com::sun::star;
60 using namespace ::com::sun::star::uno;
61 using namespace ::rtl;
64 TextEngine::TextEngine()
66 mpDoc = 0;
67 mpTEParaPortions = 0;
69 mpViews = new TextViews;
70 mpActiveView = NULL;
72 mbIsFormatting = sal_False;
73 mbFormatted = sal_False;
74 mbUpdate = sal_True;
75 mbModified = sal_False;
76 mbUndoEnabled = sal_False;
77 mbIsInUndo = sal_False;
78 mbDowning = sal_False;
79 mbRightToLeft = sal_False;
80 mbHasMultiLineParas = sal_False;
82 meAlign = TXTALIGN_LEFT;
84 mnMaxTextWidth = 0;
85 mnMaxTextLen = 0;
86 mnCurTextWidth = 0xFFFFFFFF;
87 mnCurTextHeight = 0;
89 mpUndoManager = NULL;
90 mpIMEInfos = NULL;
91 mpLocaleDataWrapper = NULL;
93 mpIdleFormatter = new IdleFormatter;
94 mpIdleFormatter->SetTimeoutHdl( LINK( this, TextEngine, IdleFormatHdl ) );
96 mpRefDev = new VirtualDevice;
98 ImpInitLayoutMode( mpRefDev );
100 ImpInitDoc();
102 maTextColor = COL_BLACK;
103 Font aFont;
104 aFont.SetTransparent( sal_False );
105 Color aFillColor( aFont.GetFillColor() );
106 aFillColor.SetTransparency( 0 );
107 aFont.SetFillColor( aFillColor );
108 SetFont( aFont );
111 TextEngine::~TextEngine()
113 mbDowning = sal_True;
115 delete mpIdleFormatter;
116 delete mpDoc;
117 delete mpTEParaPortions;
118 delete mpViews; // only the list, not the Views
119 delete mpRefDev;
120 delete mpUndoManager;
121 delete mpIMEInfos;
122 delete mpLocaleDataWrapper;
125 void TextEngine::InsertView( TextView* pTextView )
127 mpViews->push_back( pTextView );
128 pTextView->SetSelection( TextSelection() );
130 if ( !GetActiveView() )
131 SetActiveView( pTextView );
134 void TextEngine::RemoveView( TextView* pTextView )
136 TextViews::iterator it = std::find( mpViews->begin(), mpViews->end(), pTextView );
137 if( it != mpViews->end() )
139 pTextView->HideCursor();
140 mpViews->erase( it );
141 if ( pTextView == GetActiveView() )
142 SetActiveView( 0 );
146 sal_uInt16 TextEngine::GetViewCount() const
148 return mpViews->size();
151 TextView* TextEngine::GetView( sal_uInt16 nView ) const
153 return (*mpViews)[ nView ];
156 TextView* TextEngine::GetActiveView() const
158 return mpActiveView;
161 void TextEngine::SetActiveView( TextView* pTextView )
163 if ( pTextView != mpActiveView )
165 if ( mpActiveView )
166 mpActiveView->HideSelection();
168 mpActiveView = pTextView;
170 if ( mpActiveView )
171 mpActiveView->ShowSelection();
175 void TextEngine::SetFont( const Font& rFont )
177 if ( rFont != maFont )
179 maFont = rFont;
180 // #i40221# As the font's color now defaults to transparent (since i35764)
181 // we have to choose a useful textcolor in this case.
182 // Otherwise maTextColor and maFont.GetColor() are both transparent....
183 if( rFont.GetColor() == COL_TRANSPARENT )
184 maTextColor = COL_BLACK;
185 else
186 maTextColor = rFont.GetColor();
188 // Do not allow transparent fonts because of selection
189 // (otherwise delete the background in ImplPaint later differently)
190 maFont.SetTransparent( sal_False );
191 // Tell VCL not to use the font color, use text color from OutputDevice
192 maFont.SetColor( COL_TRANSPARENT );
193 Color aFillColor( maFont.GetFillColor() );
194 aFillColor.SetTransparency( 0 );
195 maFont.SetFillColor( aFillColor );
197 maFont.SetAlign( ALIGN_TOP );
198 mpRefDev->SetFont( maFont);
199 Size aTextSize;
200 aTextSize.Width() = mpRefDev->GetTextWidth(OUString(" "));
201 aTextSize.Height() = mpRefDev->GetTextHeight();
202 if ( !aTextSize.Width() )
203 aTextSize.Width() = mpRefDev->GetTextWidth(OUString("XXXX"));
205 mnDefTab = (sal_uInt16)aTextSize.Width();
206 if ( !mnDefTab )
207 mnDefTab = 1;
208 mnCharHeight = (sal_uInt16)aTextSize.Height();
209 mnFixCharWidth100 = 0;
211 FormatFullDoc();
212 UpdateViews();
214 for ( sal_uInt16 nView = mpViews->size(); nView; )
216 TextView* pView = (*mpViews)[ --nView ];
217 pView->GetWindow()->SetInputContext( InputContext( GetFont(), !pView->IsReadOnly() ? INPUTCONTEXT_TEXT|INPUTCONTEXT_EXTTEXTINPUT : 0 ) );
222 void TextEngine::SetMaxTextLen( sal_uLong nLen )
224 mnMaxTextLen = nLen;
227 void TextEngine::SetMaxTextWidth( sal_uLong nMaxWidth )
229 if ( nMaxWidth != mnMaxTextWidth )
231 mnMaxTextWidth = std::min( nMaxWidth, (sal_uLong)0x7FFFFFFF );
232 FormatFullDoc();
233 UpdateViews();
237 static sal_Unicode static_aLFText[] = { '\n', 0 };
238 static sal_Unicode static_aCRText[] = { '\r', 0 };
239 static sal_Unicode static_aCRLFText[] = { '\r', '\n', 0 };
241 static inline const sal_Unicode* static_getLineEndText( LineEnd aLineEnd )
243 const sal_Unicode* pRet = NULL;
245 switch( aLineEnd )
247 case LINEEND_LF: pRet = static_aLFText;break;
248 case LINEEND_CR: pRet = static_aCRText;break;
249 case LINEEND_CRLF: pRet = static_aCRLFText;break;
251 return pRet;
254 void TextEngine::ReplaceText(const TextSelection& rSel, const String& rText)
256 ImpInsertText( rSel, rText );
259 String TextEngine::GetText( LineEnd aSeparator ) const
261 return mpDoc->GetText( static_getLineEndText( aSeparator ) );
264 String TextEngine::GetTextLines( LineEnd aSeparator ) const
266 String aText;
267 sal_uLong nParas = mpTEParaPortions->Count();
268 const sal_Unicode* pSep = static_getLineEndText( aSeparator );
269 for ( sal_uLong nP = 0; nP < nParas; nP++ )
271 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nP );
273 sal_uInt16 nLines = pTEParaPortion->GetLines().size();
274 for ( sal_uInt16 nL = 0; nL < nLines; nL++ )
276 TextLine* pLine = pTEParaPortion->GetLines()[nL];
277 aText += pTEParaPortion->GetNode()->GetText().Copy( pLine->GetStart(), pLine->GetEnd() - pLine->GetStart() );
278 if ( pSep && ( ( (nP+1) < nParas ) || ( (nL+1) < nLines ) ) )
279 aText += pSep;
282 return aText;
285 String TextEngine::GetText( sal_uLong nPara ) const
287 return mpDoc->GetText( nPara );
290 sal_uLong TextEngine::GetTextLen( LineEnd aSeparator ) const
292 return mpDoc->GetTextLen( static_getLineEndText( aSeparator ) );
295 sal_uLong TextEngine::GetTextLen( const TextSelection& rSel, LineEnd aSeparator ) const
297 TextSelection aSel( rSel );
298 aSel.Justify();
299 ValidateSelection( aSel );
300 return mpDoc->GetTextLen( static_getLineEndText( aSeparator ), &aSel );
303 sal_uInt16 TextEngine::GetTextLen( sal_uLong nPara ) const
305 return mpDoc->GetNodes().GetObject( nPara )->GetText().Len();
308 void TextEngine::SetUpdateMode( sal_Bool bUpdate )
310 if ( bUpdate != mbUpdate )
312 mbUpdate = bUpdate;
313 if ( mbUpdate )
315 FormatAndUpdate( GetActiveView() );
316 if ( GetActiveView() )
317 GetActiveView()->ShowCursor();
322 sal_Bool TextEngine::DoesKeyChangeText( const KeyEvent& rKeyEvent )
324 sal_Bool bDoesChange = sal_False;
326 KeyFuncType eFunc = rKeyEvent.GetKeyCode().GetFunction();
327 if ( eFunc != KEYFUNC_DONTKNOW )
329 switch ( eFunc )
331 case KEYFUNC_UNDO:
332 case KEYFUNC_REDO:
333 case KEYFUNC_CUT:
334 case KEYFUNC_PASTE: bDoesChange = sal_True;
335 break;
336 default: // might get handled below
337 eFunc = KEYFUNC_DONTKNOW;
340 if ( eFunc == KEYFUNC_DONTKNOW )
342 switch ( rKeyEvent.GetKeyCode().GetCode() )
344 case KEY_DELETE:
345 case KEY_BACKSPACE:
347 if ( !rKeyEvent.GetKeyCode().IsMod2() )
348 bDoesChange = sal_True;
350 break;
351 case KEY_RETURN:
352 case KEY_TAB:
354 if ( !rKeyEvent.GetKeyCode().IsMod1() && !rKeyEvent.GetKeyCode().IsMod2() )
355 bDoesChange = sal_True;
357 break;
358 default:
360 bDoesChange = TextEngine::IsSimpleCharInput( rKeyEvent );
364 return bDoesChange;
367 sal_Bool TextEngine::IsSimpleCharInput( const KeyEvent& rKeyEvent )
369 if( rKeyEvent.GetCharCode() >= 32 && rKeyEvent.GetCharCode() != 127 &&
370 KEY_MOD1 != (rKeyEvent.GetKeyCode().GetModifier() & ~KEY_SHIFT) && // (ssa) #i45714#:
371 KEY_MOD2 != (rKeyEvent.GetKeyCode().GetModifier() & ~KEY_SHIFT) ) // check for Ctrl and Alt separately
373 return sal_True;
375 return sal_False;
378 void TextEngine::ImpInitDoc()
380 if ( mpDoc )
381 mpDoc->Clear();
382 else
383 mpDoc = new TextDoc;
385 delete mpTEParaPortions;
386 mpTEParaPortions = new TEParaPortions;
388 TextNode* pNode = new TextNode( String() );
389 mpDoc->GetNodes().Insert( pNode, 0 );
391 TEParaPortion* pIniPortion = new TEParaPortion( pNode );
392 mpTEParaPortions->Insert( pIniPortion, (sal_uLong)0 );
394 mbFormatted = sal_False;
396 ImpParagraphRemoved( TEXT_PARA_ALL );
397 ImpParagraphInserted( 0 );
400 String TextEngine::GetText( const TextSelection& rSel, LineEnd aSeparator ) const
402 String aText;
404 if ( !rSel.HasRange() )
405 return aText;
407 TextSelection aSel( rSel );
408 aSel.Justify();
410 sal_uLong nStartPara = aSel.GetStart().GetPara();
411 sal_uLong nEndPara = aSel.GetEnd().GetPara();
412 const sal_Unicode* pSep = static_getLineEndText( aSeparator );
413 for ( sal_uLong nNode = aSel.GetStart().GetPara(); nNode <= nEndPara; nNode++ )
415 TextNode* pNode = mpDoc->GetNodes().GetObject( nNode );
417 sal_uInt16 nStartPos = 0;
418 sal_uInt16 nEndPos = pNode->GetText().Len();
419 if ( nNode == nStartPara )
420 nStartPos = aSel.GetStart().GetIndex();
421 if ( nNode == nEndPara ) // may also be == nStart!
422 nEndPos = aSel.GetEnd().GetIndex();
424 aText += pNode->GetText().Copy( nStartPos, nEndPos-nStartPos );
425 if ( nNode < nEndPara )
426 aText += pSep;
428 return aText;
431 void TextEngine::ImpRemoveText()
433 ImpInitDoc();
435 TextPaM aStartPaM( 0, 0 );
436 TextSelection aEmptySel( aStartPaM, aStartPaM );
437 for ( sal_uInt16 nView = 0; nView < mpViews->size(); nView++ )
439 TextView* pView = (*mpViews)[ nView ];
440 pView->ImpSetSelection( aEmptySel );
442 ResetUndo();
445 void TextEngine::SetText( const OUString& rText )
447 ImpRemoveText();
449 sal_Bool bUndoCurrentlyEnabled = IsUndoEnabled();
450 // the manually inserted text cannot be reversed by the user
451 EnableUndo( sal_False );
453 TextPaM aStartPaM( 0, 0 );
454 TextSelection aEmptySel( aStartPaM, aStartPaM );
456 TextPaM aPaM = aStartPaM;
457 if ( !rText.isEmpty() )
458 aPaM = ImpInsertText( aEmptySel, rText );
460 for ( sal_uInt16 nView = 0; nView < mpViews->size(); nView++ )
462 TextView* pView = (*mpViews)[ nView ];
463 pView->ImpSetSelection( aEmptySel );
465 // if no text, then no Format&Update => the text remains
466 if ( rText.isEmpty() && GetUpdateMode() )
467 pView->Invalidate();
470 if( rText.isEmpty() ) // otherwise needs invalidation later; !bFormatted is sufficient
471 mnCurTextHeight = 0;
473 FormatAndUpdate();
475 EnableUndo( bUndoCurrentlyEnabled );
476 DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "SetText: Undo!" );
480 void TextEngine::CursorMoved( sal_uLong nNode )
482 // delete empty attribute; but only if paragraph is not empty!
483 TextNode* pNode = mpDoc->GetNodes().GetObject( nNode );
484 if ( pNode && pNode->GetCharAttribs().HasEmptyAttribs() && pNode->GetText().Len() )
485 pNode->GetCharAttribs().DeleteEmptyAttribs();
488 void TextEngine::ImpRemoveChars( const TextPaM& rPaM, sal_uInt16 nChars, SfxUndoAction* )
490 DBG_ASSERT( nChars, "ImpRemoveChars: 0 Chars?!" );
491 if ( IsUndoEnabled() && !IsInUndo() )
493 // attributes have to be saved for UNDO before RemoveChars!
494 TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
495 XubString aStr( pNode->GetText().Copy( rPaM.GetIndex(), nChars ) );
497 // check if attributes are being deleted or changed
498 sal_uInt16 nStart = rPaM.GetIndex();
499 sal_uInt16 nEnd = nStart + nChars;
500 for ( sal_uInt16 nAttr = pNode->GetCharAttribs().Count(); nAttr; )
502 TextCharAttrib* pAttr = pNode->GetCharAttribs().GetAttrib( --nAttr );
503 if ( ( pAttr->GetEnd() >= nStart ) && ( pAttr->GetStart() < nEnd ) )
505 break; // for
508 InsertUndo( new TextUndoRemoveChars( this, rPaM, aStr ) );
511 mpDoc->RemoveChars( rPaM, nChars );
512 ImpCharsRemoved( rPaM.GetPara(), rPaM.GetIndex(), nChars );
515 TextPaM TextEngine::ImpConnectParagraphs( sal_uLong nLeft, sal_uLong nRight )
517 DBG_ASSERT( nLeft != nRight, "ImpConnectParagraphs: connect the very same paragraph ?" );
519 TextNode* pLeft = mpDoc->GetNodes().GetObject( nLeft );
520 TextNode* pRight = mpDoc->GetNodes().GetObject( nRight );
522 if ( IsUndoEnabled() && !IsInUndo() )
523 InsertUndo( new TextUndoConnectParas( this, nLeft, pLeft->GetText().Len() ) );
525 // first lookup Portions, as pRight is gone after ConnectParagraphs
526 TEParaPortion* pLeftPortion = mpTEParaPortions->GetObject( nLeft );
527 TEParaPortion* pRightPortion = mpTEParaPortions->GetObject( nRight );
528 DBG_ASSERT( pLeft && pLeftPortion, "ImpConnectParagraphs(1): Hidden Portion" );
529 DBG_ASSERT( pRight && pRightPortion, "ImpConnectParagraphs(2): Hidden Portion" );
531 TextPaM aPaM = mpDoc->ConnectParagraphs( pLeft, pRight );
532 ImpParagraphRemoved( nRight );
534 pLeftPortion->MarkSelectionInvalid( aPaM.GetIndex(), pLeft->GetText().Len() );
536 mpTEParaPortions->Remove( nRight );
537 delete pRightPortion;
538 // the right Node is deleted by EditDoc::ConnectParagraphs()
540 return aPaM;
543 TextPaM TextEngine::ImpDeleteText( const TextSelection& rSel )
545 if ( !rSel.HasRange() )
546 return rSel.GetStart();
548 TextSelection aSel( rSel );
549 aSel.Justify();
550 TextPaM aStartPaM( aSel.GetStart() );
551 TextPaM aEndPaM( aSel.GetEnd() );
553 CursorMoved( aStartPaM.GetPara() ); // so that newly-adjusted attributes vanish
554 CursorMoved( aEndPaM.GetPara() ); // so that newly-adjusted attributes vanish
556 DBG_ASSERT( mpDoc->IsValidPaM( aStartPaM ), "ImpDeleteText(1): bad Index" );
557 DBG_ASSERT( mpDoc->IsValidPaM( aEndPaM ), "ImpDeleteText(2): bad Index" );
559 sal_uLong nStartNode = aStartPaM.GetPara();
560 sal_uLong nEndNode = aEndPaM.GetPara();
562 // remove all Nodes inbetween
563 for ( sal_uLong z = nStartNode+1; z < nEndNode; z++ )
565 // always nStartNode+1, because of Remove()!
566 ImpRemoveParagraph( nStartNode+1 );
569 if ( nStartNode != nEndNode )
571 // the remainder of StartNodes...
572 TextNode* pLeft = mpDoc->GetNodes().GetObject( nStartNode );
573 sal_uInt16 nChars = pLeft->GetText().Len() - aStartPaM.GetIndex();
574 if ( nChars )
576 ImpRemoveChars( aStartPaM, nChars );
577 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nStartNode );
578 DBG_ASSERT( pPortion, "ImpDeleteText(3): bad Index" );
579 pPortion->MarkSelectionInvalid( aStartPaM.GetIndex(), pLeft->GetText().Len() );
582 // the beginning of EndNodes....
583 nEndNode = nStartNode+1; // the other paragraphs were deleted
584 nChars = aEndPaM.GetIndex();
585 if ( nChars )
587 aEndPaM.GetPara() = nEndNode;
588 aEndPaM.GetIndex() = 0;
589 ImpRemoveChars( aEndPaM, nChars );
590 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nEndNode );
591 DBG_ASSERT( pPortion, "ImpDeleteText(4): bad Index" );
592 pPortion->MarkSelectionInvalid( 0, pPortion->GetNode()->GetText().Len() );
595 // connect....
596 aStartPaM = ImpConnectParagraphs( nStartNode, nEndNode );
598 else
600 sal_uInt16 nChars;
601 nChars = aEndPaM.GetIndex() - aStartPaM.GetIndex();
602 ImpRemoveChars( aStartPaM, nChars );
603 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nStartNode );
604 DBG_ASSERT( pPortion, "ImpDeleteText(5): bad Index" );
605 pPortion->MarkInvalid( aEndPaM.GetIndex(), aStartPaM.GetIndex() - aEndPaM.GetIndex() );
608 // UpdateSelections();
609 TextModified();
610 return aStartPaM;
613 void TextEngine::ImpRemoveParagraph( sal_uLong nPara )
615 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
616 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
618 // the Node is handled by Undo and is deleted if appropriate
619 mpDoc->GetNodes().Remove( nPara );
620 if ( IsUndoEnabled() && !IsInUndo() )
621 InsertUndo( new TextUndoDelPara( this, pNode, nPara ) );
622 else
623 delete pNode;
625 mpTEParaPortions->Remove( nPara );
626 delete pPortion;
628 ImpParagraphRemoved( nPara );
631 uno::Reference < i18n::XExtendedInputSequenceChecker > TextEngine::GetInputSequenceChecker() const
633 uno::Reference < i18n::XExtendedInputSequenceChecker > xISC;
634 // if ( !xISC.is() )
636 uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
637 xISC = i18n::InputSequenceChecker::create(xContext);
639 return xISC;
642 sal_Bool TextEngine::IsInputSequenceCheckingRequired( sal_Unicode c, const TextSelection& rCurSel ) const
644 uno::Reference< i18n::XBreakIterator > xBI = ((TextEngine *) this)->GetBreakIterator();
645 SvtCTLOptions aCTLOptions;
647 // get the index that really is first
648 sal_uInt16 nFirstPos = rCurSel.GetStart().GetIndex();
649 sal_uInt16 nMaxPos = rCurSel.GetEnd().GetIndex();
650 if (nMaxPos < nFirstPos)
651 nFirstPos = nMaxPos;
653 sal_Bool bIsSequenceChecking =
654 aCTLOptions.IsCTLFontEnabled() &&
655 aCTLOptions.IsCTLSequenceChecking() &&
656 nFirstPos != 0 && /* first char needs not to be checked */
657 xBI.is() && i18n::ScriptType::COMPLEX == xBI->getScriptType( OUString( c ), 0 );
659 return bIsSequenceChecking;
662 TextPaM TextEngine::ImpInsertText( const TextSelection& rCurSel, sal_Unicode c, sal_Bool bOverwrite )
664 return ImpInsertText( c, rCurSel, bOverwrite, sal_False );
667 TextPaM TextEngine::ImpInsertText( sal_Unicode c, const TextSelection& rCurSel, sal_Bool bOverwrite, sal_Bool bIsUserInput )
669 DBG_ASSERT( c != '\n', "InsertText: NewLine!" );
670 DBG_ASSERT( c != '\r', "InsertText: NewLine!" );
672 TextPaM aPaM( rCurSel.GetStart() );
673 TextNode* pNode = mpDoc->GetNodes().GetObject( aPaM.GetPara() );
675 if ( pNode->GetText().Len() < STRING_MAXLEN )
677 sal_Bool bDoOverwrite = ( bOverwrite &&
678 ( aPaM.GetIndex() < pNode->GetText().Len() ) ) ? sal_True : sal_False;
680 bool bUndoAction = ( rCurSel.HasRange() || bDoOverwrite );
682 if ( bUndoAction )
683 UndoActionStart();
685 if ( rCurSel.HasRange() )
687 aPaM = ImpDeleteText( rCurSel );
689 else if ( bDoOverwrite )
691 // if selection, then don't overwrite a character
692 TextSelection aTmpSel( aPaM );
693 aTmpSel.GetEnd().GetIndex()++;
694 ImpDeleteText( aTmpSel );
697 if (bIsUserInput && IsInputSequenceCheckingRequired( c, rCurSel ))
699 uno::Reference < i18n::XExtendedInputSequenceChecker > xISC = GetInputSequenceChecker();
700 SvtCTLOptions aCTLOptions;
702 if (xISC.is())
704 xub_StrLen nTmpPos = aPaM.GetIndex();
705 sal_Int16 nCheckMode = aCTLOptions.IsCTLSequenceCheckingRestricted() ?
706 i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC;
708 // the text that needs to be checked is only the one
709 // before the current cursor position
710 OUString aOldText( mpDoc->GetText( aPaM.GetPara() ).Copy(0, nTmpPos) );
711 OUString aNewText( aOldText );
712 if (aCTLOptions.IsCTLSequenceCheckingTypeAndReplace())
714 xISC->correctInputSequence( aNewText, nTmpPos - 1, c, nCheckMode );
716 // find position of first character that has changed
717 sal_Int32 nOldLen = aOldText.getLength();
718 sal_Int32 nNewLen = aNewText.getLength();
719 const sal_Unicode *pOldTxt = aOldText.getStr();
720 const sal_Unicode *pNewTxt = aNewText.getStr();
721 sal_Int32 nChgPos = 0;
722 while ( nChgPos < nOldLen && nChgPos < nNewLen &&
723 pOldTxt[nChgPos] == pNewTxt[nChgPos] )
724 ++nChgPos;
726 String aChgText( aNewText.copy( nChgPos ) );
728 // select text from first pos to be changed to current pos
729 TextSelection aSel( TextPaM( aPaM.GetPara(), (sal_uInt16) nChgPos ), aPaM );
731 if (aChgText.Len())
732 // ImpInsertText implicitly handles undo...
733 return ImpInsertText( aSel, aChgText );
734 else
735 return aPaM;
737 else
739 // should the character be ignored (i.e. not get inserted) ?
740 if (!xISC->checkInputSequence( aOldText, nTmpPos - 1, c, nCheckMode ))
741 return aPaM; // nothing to be done -> no need for undo
745 // at this point now we will insert the character 'normally' some lines below...
749 if ( IsUndoEnabled() && !IsInUndo() )
751 TextUndoInsertChars* pNewUndo = new TextUndoInsertChars( this, aPaM, OUString(c) );
752 sal_Bool bTryMerge = ( !bDoOverwrite && ( c != ' ' ) ) ? sal_True : sal_False;
753 InsertUndo( pNewUndo, bTryMerge );
756 TEParaPortion* pPortion = mpTEParaPortions->GetObject( aPaM.GetPara() );
757 pPortion->MarkInvalid( aPaM.GetIndex(), 1 );
758 if ( c == '\t' )
759 pPortion->SetNotSimpleInvalid();
760 aPaM = mpDoc->InsertText( aPaM, c );
761 ImpCharsInserted( aPaM.GetPara(), aPaM.GetIndex()-1, 1 );
763 TextModified();
765 if ( bUndoAction )
766 UndoActionEnd();
769 return aPaM;
773 TextPaM TextEngine::ImpInsertText( const TextSelection& rCurSel, const XubString& rStr )
775 UndoActionStart();
777 TextPaM aPaM;
779 if ( rCurSel.HasRange() )
780 aPaM = ImpDeleteText( rCurSel );
781 else
782 aPaM = rCurSel.GetEnd();
784 XubString aText(convertLineEnd(rStr, LINEEND_LF));
786 sal_uInt16 nStart = 0;
787 while ( nStart < aText.Len() )
789 sal_uInt16 nEnd = aText.Search( LINE_SEP, nStart );
790 if ( nEnd == STRING_NOTFOUND )
791 nEnd = aText.Len(); // do not dereference!
793 // Start == End => empty line
794 if ( nEnd > nStart )
796 sal_uLong nL = aPaM.GetIndex();
797 nL += ( nEnd-nStart );
798 if ( nL > STRING_MAXLEN )
800 sal_uInt16 nDiff = (sal_uInt16) (nL-STRING_MAXLEN);
801 nEnd = nEnd - nDiff;
804 XubString aLine( aText, nStart, nEnd-nStart );
805 if ( IsUndoEnabled() && !IsInUndo() )
806 InsertUndo( new TextUndoInsertChars( this, aPaM, aLine ) );
808 TEParaPortion* pPortion = mpTEParaPortions->GetObject( aPaM.GetPara() );
809 pPortion->MarkInvalid( aPaM.GetIndex(), aLine.Len() );
810 if ( aLine.Search( '\t' ) != STRING_NOTFOUND )
811 pPortion->SetNotSimpleInvalid();
813 aPaM = mpDoc->InsertText( aPaM, aLine );
814 ImpCharsInserted( aPaM.GetPara(), aPaM.GetIndex()-aLine.Len(), aLine.Len() );
817 if ( nEnd < aText.Len() )
818 aPaM = ImpInsertParaBreak( aPaM );
820 nStart = nEnd+1;
822 if ( nStart < nEnd ) // #108611# overflow
823 break;
826 UndoActionEnd();
828 TextModified();
829 return aPaM;
832 TextPaM TextEngine::ImpInsertParaBreak( const TextSelection& rCurSel, sal_Bool bKeepEndingAttribs )
834 TextPaM aPaM;
835 if ( rCurSel.HasRange() )
836 aPaM = ImpDeleteText( rCurSel );
837 else
838 aPaM = rCurSel.GetEnd();
840 return ImpInsertParaBreak( aPaM, bKeepEndingAttribs );
843 TextPaM TextEngine::ImpInsertParaBreak( const TextPaM& rPaM, sal_Bool bKeepEndingAttribs )
845 if ( IsUndoEnabled() && !IsInUndo() )
846 InsertUndo( new TextUndoSplitPara( this, rPaM.GetPara(), rPaM.GetIndex() ) );
848 TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
849 bool bFirstParaContentChanged = rPaM.GetIndex() < pNode->GetText().Len();
851 TextPaM aPaM( mpDoc->InsertParaBreak( rPaM, bKeepEndingAttribs ) );
853 TEParaPortion* pPortion = mpTEParaPortions->GetObject( rPaM.GetPara() );
854 DBG_ASSERT( pPortion, "ImpInsertParaBreak: Hidden Portion" );
855 pPortion->MarkInvalid( rPaM.GetIndex(), 0 );
857 TextNode* pNewNode = mpDoc->GetNodes().GetObject( aPaM.GetPara() );
858 TEParaPortion* pNewPortion = new TEParaPortion( pNewNode );
859 mpTEParaPortions->Insert( pNewPortion, aPaM.GetPara() );
860 ImpParagraphInserted( aPaM.GetPara() );
862 CursorMoved( rPaM.GetPara() ); // if empty attribute created
863 TextModified();
865 if ( bFirstParaContentChanged )
866 Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, rPaM.GetPara() ) );
868 return aPaM;
871 Rectangle TextEngine::PaMtoEditCursor( const TextPaM& rPaM, sal_Bool bSpecial )
873 DBG_ASSERT( GetUpdateMode(), "PaMtoEditCursor: GetUpdateMode()" );
875 Rectangle aEditCursor;
876 long nY = 0;
878 if ( !mbHasMultiLineParas )
880 nY = rPaM.GetPara() * mnCharHeight;
882 else
884 for ( sal_uLong nPortion = 0; nPortion < rPaM.GetPara(); nPortion++ )
886 TEParaPortion* pPortion = mpTEParaPortions->GetObject(nPortion);
887 nY += pPortion->GetLines().size() * mnCharHeight;
891 aEditCursor = GetEditCursor( rPaM, bSpecial );
892 aEditCursor.Top() += nY;
893 aEditCursor.Bottom() += nY;
894 return aEditCursor;
897 Rectangle TextEngine::GetEditCursor( const TextPaM& rPaM, sal_Bool bSpecial, sal_Bool bPreferPortionStart )
899 if ( !IsFormatted() && !IsFormatting() )
900 FormatAndUpdate();
902 TEParaPortion* pPortion = mpTEParaPortions->GetObject( rPaM.GetPara() );
903 //TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
906 bSpecial: If behind the last character of a made up line, stay at the
907 end of the line, not at the start of the next line.
908 Purpose: - really END = > behind the last character
909 - to selection...
913 long nY = 0;
914 sal_uInt16 nCurIndex = 0;
915 TextLine* pLine = 0;
916 for ( sal_uInt16 nLine = 0; nLine < pPortion->GetLines().size(); nLine++ )
918 TextLine* pTmpLine = pPortion->GetLines()[ nLine ];
919 if ( ( pTmpLine->GetStart() == rPaM.GetIndex() ) || ( pTmpLine->IsIn( rPaM.GetIndex(), bSpecial ) ) )
921 pLine = pTmpLine;
922 break;
925 nCurIndex = nCurIndex + pTmpLine->GetLen();
926 nY += mnCharHeight;
928 if ( !pLine )
930 // Cursor at end of paragraph
931 DBG_ASSERT( rPaM.GetIndex() == nCurIndex, "GetEditCursor: Bad Index!" );
933 pLine = pPortion->GetLines().back();
934 nY -= mnCharHeight;
935 nCurIndex = nCurIndex - pLine->GetLen();
938 Rectangle aEditCursor;
940 aEditCursor.Top() = nY;
941 nY += mnCharHeight;
942 aEditCursor.Bottom() = nY-1;
944 // search within the line
945 long nX = ImpGetXPos( rPaM.GetPara(), pLine, rPaM.GetIndex(), bPreferPortionStart );
946 aEditCursor.Left() = aEditCursor.Right() = nX;
947 return aEditCursor;
950 long TextEngine::ImpGetXPos( sal_uLong nPara, TextLine* pLine, sal_uInt16 nIndex, sal_Bool bPreferPortionStart )
952 DBG_ASSERT( ( nIndex >= pLine->GetStart() ) && ( nIndex <= pLine->GetEnd() ) , "ImpGetXPos: Bad parameters!" );
954 sal_Bool bDoPreferPortionStart = bPreferPortionStart;
955 // Assure that the portion belongs to this line
956 if ( nIndex == pLine->GetStart() )
957 bDoPreferPortionStart = sal_True;
958 else if ( nIndex == pLine->GetEnd() )
959 bDoPreferPortionStart = sal_False;
961 TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
963 sal_uInt16 nTextPortionStart = 0;
964 size_t nTextPortion = pParaPortion->GetTextPortions().FindPortion( nIndex, nTextPortionStart, bDoPreferPortionStart );
966 DBG_ASSERT( ( nTextPortion >= pLine->GetStartPortion() ) && ( nTextPortion <= pLine->GetEndPortion() ), "GetXPos: Portion not in current line!" );
968 TETextPortion* pPortion = pParaPortion->GetTextPortions()[ nTextPortion ];
970 long nX = ImpGetPortionXOffset( nPara, pLine, nTextPortion );
972 long nPortionTextWidth = pPortion->GetWidth();
974 if ( nTextPortionStart != nIndex )
976 // Search within portion...
977 if ( nIndex == ( nTextPortionStart + pPortion->GetLen() ) )
979 // End of Portion
980 if ( ( pPortion->GetKind() == PORTIONKIND_TAB ) ||
981 ( !IsRightToLeft() && !pPortion->IsRightToLeft() ) ||
982 ( IsRightToLeft() && pPortion->IsRightToLeft() ) )
984 nX += nPortionTextWidth;
985 if ( ( pPortion->GetKind() == PORTIONKIND_TAB ) && ( (nTextPortion+1) < pParaPortion->GetTextPortions().size() ) )
987 TETextPortion* pNextPortion = pParaPortion->GetTextPortions()[ nTextPortion+1 ];
988 if ( ( pNextPortion->GetKind() != PORTIONKIND_TAB ) && (
989 ( !IsRightToLeft() && pNextPortion->IsRightToLeft() ) ||
990 ( IsRightToLeft() && !pNextPortion->IsRightToLeft() ) ) )
992 // nX += pNextPortion->GetWidth();
993 // End of the tab portion, use start of next for cursor pos
994 DBG_ASSERT( !bPreferPortionStart, "ImpGetXPos: How can we get here!" );
995 nX = ImpGetXPos( nPara, pLine, nIndex, sal_True );
1001 else if ( pPortion->GetKind() == PORTIONKIND_TEXT )
1003 DBG_ASSERT( nIndex != pLine->GetStart(), "ImpGetXPos: Strange behavior" );
1005 long nPosInPortion = (long)CalcTextWidth( nPara, nTextPortionStart, nIndex-nTextPortionStart );
1007 if ( ( !IsRightToLeft() && !pPortion->IsRightToLeft() ) ||
1008 ( IsRightToLeft() && pPortion->IsRightToLeft() ) )
1010 nX += nPosInPortion;
1012 else
1014 nX += nPortionTextWidth - nPosInPortion;
1018 else // if ( nIndex == pLine->GetStart() )
1020 if ( ( pPortion->GetKind() != PORTIONKIND_TAB ) &&
1021 ( ( !IsRightToLeft() && pPortion->IsRightToLeft() ) ||
1022 ( IsRightToLeft() && !pPortion->IsRightToLeft() ) ) )
1024 nX += nPortionTextWidth;
1028 return nX;
1031 const TextAttrib* TextEngine::FindAttrib( const TextPaM& rPaM, sal_uInt16 nWhich ) const
1033 const TextAttrib* pAttr = NULL;
1034 const TextCharAttrib* pCharAttr = FindCharAttrib( rPaM, nWhich );
1035 if ( pCharAttr )
1036 pAttr = &pCharAttr->GetAttr();
1037 return pAttr;
1040 const TextCharAttrib* TextEngine::FindCharAttrib( const TextPaM& rPaM, sal_uInt16 nWhich ) const
1042 const TextCharAttrib* pAttr = NULL;
1043 TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
1044 if ( pNode && ( rPaM.GetIndex() < pNode->GetText().Len() ) )
1045 pAttr = pNode->GetCharAttribs().FindAttrib( nWhich, rPaM.GetIndex() );
1046 return pAttr;
1049 sal_Bool TextEngine::HasAttrib( sal_uInt16 nWhich ) const
1051 sal_Bool bAttr = sal_False;
1052 for ( sal_uLong n = mpDoc->GetNodes().Count(); --n && !bAttr; )
1054 TextNode* pNode = mpDoc->GetNodes().GetObject( n );
1055 bAttr = pNode->GetCharAttribs().HasAttrib( nWhich );
1057 return bAttr;
1060 TextPaM TextEngine::GetPaM( const Point& rDocPos, sal_Bool bSmart )
1062 DBG_ASSERT( GetUpdateMode(), "GetPaM: GetUpdateMode()" );
1064 long nY = 0;
1065 for ( sal_uLong nPortion = 0; nPortion < mpTEParaPortions->Count(); nPortion++ )
1067 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion );
1068 long nTmpHeight = pPortion->GetLines().size() * mnCharHeight;
1069 nY += nTmpHeight;
1070 if ( nY > rDocPos.Y() )
1072 nY -= nTmpHeight;
1073 Point aPosInPara( rDocPos );
1074 aPosInPara.Y() -= nY;
1076 TextPaM aPaM( nPortion, 0 );
1077 aPaM.GetIndex() = ImpFindIndex( nPortion, aPosInPara, bSmart );
1078 return aPaM;
1082 // not found - go to last visible
1083 sal_uLong nLastNode = mpDoc->GetNodes().Count() - 1;
1084 TextNode* pLast = mpDoc->GetNodes().GetObject( nLastNode );
1085 return TextPaM( nLastNode, pLast->GetText().Len() );
1088 sal_uInt16 TextEngine::ImpFindIndex( sal_uLong nPortion, const Point& rPosInPara, sal_Bool bSmart )
1090 DBG_ASSERT( IsFormatted(), "GetPaM: Not formatted" );
1091 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion );
1093 sal_uInt16 nCurIndex = 0;
1095 long nY = 0;
1096 TextLine* pLine = 0;
1097 sal_uInt16 nLine;
1098 for ( nLine = 0; nLine < pPortion->GetLines().size(); nLine++ )
1100 TextLine* pTmpLine = pPortion->GetLines()[ nLine ];
1101 nY += mnCharHeight;
1102 if ( nY > rPosInPara.Y() ) // that's it
1104 pLine = pTmpLine;
1105 break; // correct Y-Position not needed
1108 DBG_ASSERT( pLine, "ImpFindIndex: pLine ?" );
1110 nCurIndex = GetCharPos( nPortion, nLine, rPosInPara.X(), bSmart );
1112 if ( nCurIndex && ( nCurIndex == pLine->GetEnd() ) &&
1113 ( pLine != pPortion->GetLines().back() ) )
1115 uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator();
1116 sal_Int32 nCount = 1;
1117 nCurIndex = (sal_uInt16)xBI->previousCharacters( pPortion->GetNode()->GetText(), nCurIndex, GetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount );
1119 return nCurIndex;
1122 sal_uInt16 TextEngine::GetCharPos( sal_uLong nPortion, sal_uInt16 nLine, long nXPos, sal_Bool )
1125 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion );
1126 TextLine* pLine = pPortion->GetLines()[ nLine ];
1128 sal_uInt16 nCurIndex = pLine->GetStart();
1130 long nTmpX = pLine->GetStartX();
1131 if ( nXPos <= nTmpX )
1132 return nCurIndex;
1134 for ( sal_uInt16 i = pLine->GetStartPortion(); i <= pLine->GetEndPortion(); i++ )
1136 TETextPortion* pTextPortion = pPortion->GetTextPortions()[ i ];
1137 nTmpX += pTextPortion->GetWidth();
1139 if ( nTmpX > nXPos )
1141 if( pTextPortion->GetLen() > 1 )
1143 nTmpX -= pTextPortion->GetWidth(); // position before Portion
1144 // TODO: Optimize: no GetTextBreak if fixed-width Font
1145 Font aFont;
1146 SeekCursor( nPortion, nCurIndex+1, aFont, NULL );
1147 mpRefDev->SetFont( aFont);
1148 long nPosInPortion = nXPos-nTmpX;
1149 if ( IsRightToLeft() != pTextPortion->IsRightToLeft() )
1150 nPosInPortion = pTextPortion->GetWidth() - nPosInPortion;
1151 nCurIndex = mpRefDev->GetTextBreak( pPortion->GetNode()->GetText(), nPosInPortion, nCurIndex );
1152 // MT: GetTextBreak should assure that we are not withing a CTL cell...
1154 return nCurIndex;
1156 nCurIndex = nCurIndex + pTextPortion->GetLen();
1158 return nCurIndex;
1162 sal_uLong TextEngine::GetTextHeight() const
1164 DBG_ASSERT( GetUpdateMode(), "GetTextHeight: GetUpdateMode()" );
1166 if ( !IsFormatted() && !IsFormatting() )
1167 ((TextEngine*)this)->FormatAndUpdate();
1169 return mnCurTextHeight;
1172 sal_uLong TextEngine::GetTextHeight( sal_uLong nParagraph ) const
1174 DBG_ASSERT( GetUpdateMode(), "GetTextHeight: GetUpdateMode()" );
1176 if ( !IsFormatted() && !IsFormatting() )
1177 ((TextEngine*)this)->FormatAndUpdate();
1179 return CalcParaHeight( nParagraph );
1182 sal_uLong TextEngine::CalcTextWidth( sal_uLong nPara )
1184 sal_uLong nParaWidth = 0;
1185 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
1186 for ( sal_uInt16 nLine = pPortion->GetLines().size(); nLine; )
1188 sal_uLong nLineWidth = 0;
1189 TextLine* pLine = pPortion->GetLines()[ --nLine ];
1190 for ( sal_uInt16 nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ )
1192 TETextPortion* pTextPortion = pPortion->GetTextPortions()[ nTP ];
1193 nLineWidth += pTextPortion->GetWidth();
1195 if ( nLineWidth > nParaWidth )
1196 nParaWidth = nLineWidth;
1198 return nParaWidth;
1201 sal_uLong TextEngine::CalcTextWidth()
1203 if ( !IsFormatted() && !IsFormatting() )
1204 FormatAndUpdate();
1206 if ( mnCurTextWidth == 0xFFFFFFFF )
1208 mnCurTextWidth = 0;
1209 for ( sal_uLong nPara = mpTEParaPortions->Count(); nPara; )
1211 sal_uLong nParaWidth = CalcTextWidth( --nPara );
1212 if ( nParaWidth > mnCurTextWidth )
1213 mnCurTextWidth = nParaWidth;
1216 return mnCurTextWidth+1;// wider by 1, as CreateLines breaks at >=
1219 sal_uLong TextEngine::CalcTextHeight()
1221 DBG_ASSERT( GetUpdateMode(), "CalcTextHeight: GetUpdateMode()" );
1223 sal_uLong nY = 0;
1224 for ( sal_uLong nPortion = mpTEParaPortions->Count(); nPortion; )
1225 nY += CalcParaHeight( --nPortion );
1226 return nY;
1229 sal_uLong TextEngine::CalcTextWidth( sal_uLong nPara, sal_uInt16 nPortionStart, sal_uInt16 nLen, const Font* pFont )
1231 // within the text there must not be a Portion change (attribute/tab)!
1232 DBG_ASSERT( mpDoc->GetNodes().GetObject( nPara )->GetText().Search( '\t', nPortionStart ) >= (nPortionStart+nLen), "CalcTextWidth: Tab!" );
1234 sal_uLong nWidth;
1235 if ( mnFixCharWidth100 )
1237 nWidth = (sal_uLong)nLen*mnFixCharWidth100/100;
1239 else
1241 if ( pFont )
1243 if ( !mpRefDev->GetFont().IsSameInstance( *pFont ) )
1244 mpRefDev->SetFont( *pFont );
1246 else
1248 Font aFont;
1249 SeekCursor( nPara, nPortionStart+1, aFont, NULL );
1250 mpRefDev->SetFont( aFont );
1252 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
1253 nWidth = (sal_uLong)mpRefDev->GetTextWidth( pNode->GetText(), nPortionStart, nLen );
1256 return nWidth;
1259 sal_uInt16 TextEngine::GetLineCount( sal_uLong nParagraph ) const
1261 DBG_ASSERT( nParagraph < mpTEParaPortions->Count(), "GetLineCount: Out of range" );
1263 TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph );
1264 if ( pPPortion )
1265 return pPPortion->GetLines().size();
1267 return 0xFFFF;
1270 sal_uInt16 TextEngine::GetLineLen( sal_uLong nParagraph, sal_uInt16 nLine ) const
1272 DBG_ASSERT( nParagraph < mpTEParaPortions->Count(), "GetLineCount: Out of range" );
1274 TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph );
1275 if ( pPPortion && ( nLine < pPPortion->GetLines().size() ) )
1277 TextLine* pLine = pPPortion->GetLines()[ nLine ];
1278 return pLine->GetLen();
1281 return 0xFFFF;
1284 sal_uLong TextEngine::CalcParaHeight( sal_uLong nParagraph ) const
1286 sal_uLong nHeight = 0;
1288 TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph );
1289 DBG_ASSERT( pPPortion, "GetParaHeight: paragraph not found" );
1290 if ( pPPortion )
1291 nHeight = pPPortion->GetLines().size() * mnCharHeight;
1293 return nHeight;
1296 void TextEngine::UpdateSelections()
1300 Range TextEngine::GetInvalidYOffsets( sal_uLong nPortion )
1302 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPortion );
1303 sal_uInt16 nLines = pTEParaPortion->GetLines().size();
1304 sal_uInt16 nLastInvalid, nFirstInvalid = 0;
1305 sal_uInt16 nLine;
1306 for ( nLine = 0; nLine < nLines; nLine++ )
1308 TextLine* pL = pTEParaPortion->GetLines()[ nLine ];
1309 if ( pL->IsInvalid() )
1311 nFirstInvalid = nLine;
1312 break;
1316 for ( nLastInvalid = nFirstInvalid; nLastInvalid < nLines; nLastInvalid++ )
1318 TextLine* pL = pTEParaPortion->GetLines()[ nLine ];
1319 if ( pL->IsValid() )
1320 break;
1323 if ( nLastInvalid >= nLines )
1324 nLastInvalid = nLines-1;
1326 return Range( nFirstInvalid*mnCharHeight, ((nLastInvalid+1)*mnCharHeight)-1 );
1329 sal_uLong TextEngine::GetParagraphCount() const
1331 return mpDoc->GetNodes().Count();
1334 void TextEngine::EnableUndo( sal_Bool bEnable )
1336 // delete list when switching mode
1337 if ( bEnable != IsUndoEnabled() )
1338 ResetUndo();
1340 mbUndoEnabled = bEnable;
1343 ::svl::IUndoManager& TextEngine::GetUndoManager()
1345 if ( !mpUndoManager )
1346 mpUndoManager = new TextUndoManager( this );
1347 return *mpUndoManager;
1350 void TextEngine::UndoActionStart( sal_uInt16 nId )
1352 if ( IsUndoEnabled() && !IsInUndo() )
1354 String aComment;
1355 GetUndoManager().EnterListAction( aComment, XubString(), nId );
1359 void TextEngine::UndoActionEnd()
1361 if ( IsUndoEnabled() && !IsInUndo() )
1362 GetUndoManager().LeaveListAction();
1365 void TextEngine::InsertUndo( TextUndo* pUndo, sal_Bool bTryMerge )
1367 DBG_ASSERT( !IsInUndo(), "InsertUndo: in Undo mode!" );
1368 GetUndoManager().AddUndoAction( pUndo, bTryMerge );
1371 void TextEngine::ResetUndo()
1373 if ( mpUndoManager )
1374 mpUndoManager->Clear();
1377 void TextEngine::InsertContent( TextNode* pNode, sal_uLong nPara )
1379 DBG_ASSERT( pNode, "InsertContent: NULL-Pointer!" );
1380 DBG_ASSERT( IsInUndo(), "InsertContent: only in Undo()!" );
1381 TEParaPortion* pNew = new TEParaPortion( pNode );
1382 mpTEParaPortions->Insert( pNew, nPara );
1383 mpDoc->GetNodes().Insert( pNode, nPara );
1384 ImpParagraphInserted( nPara );
1387 TextPaM TextEngine::SplitContent( sal_uLong nNode, sal_uInt16 nSepPos )
1389 #ifdef DBG_UTIL
1390 TextNode* pNode = mpDoc->GetNodes().GetObject( nNode );
1391 DBG_ASSERT( pNode, "SplitContent: Invalid Node!" );
1392 DBG_ASSERT( IsInUndo(), "SplitContent: only in Undo()!" );
1393 DBG_ASSERT( nSepPos <= pNode->GetText().Len(), "SplitContent: Bad index" );
1394 #endif
1395 TextPaM aPaM( nNode, nSepPos );
1396 return ImpInsertParaBreak( aPaM );
1399 TextPaM TextEngine::ConnectContents( sal_uLong nLeftNode )
1401 DBG_ASSERT( IsInUndo(), "ConnectContent: only in Undo()!" );
1402 return ImpConnectParagraphs( nLeftNode, nLeftNode+1 );
1405 void TextEngine::SeekCursor( sal_uLong nPara, sal_uInt16 nPos, Font& rFont, OutputDevice* pOutDev )
1407 rFont = maFont;
1408 if ( pOutDev )
1409 pOutDev->SetTextColor( maTextColor );
1411 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
1412 sal_uInt16 nAttribs = pNode->GetCharAttribs().Count();
1413 for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
1415 TextCharAttrib* pAttrib = pNode->GetCharAttribs().GetAttrib( nAttr );
1416 if ( pAttrib->GetStart() > nPos )
1417 break;
1419 // When seeking don't use Attr that start there!
1420 // Do not use empty attributes:
1421 // - If just being setup and empty => no effect on Font
1422 // - Characters that are setup in an empty paragraph become visible right away.
1423 if ( ( ( pAttrib->GetStart() < nPos ) && ( pAttrib->GetEnd() >= nPos ) )
1424 || !pNode->GetText().Len() )
1426 if ( pAttrib->Which() != TEXTATTR_FONTCOLOR )
1428 pAttrib->GetAttr().SetFont(rFont);
1430 else
1432 if ( pOutDev )
1433 pOutDev->SetTextColor( ((TextAttribFontColor&)pAttrib->GetAttr()).GetColor() );
1438 if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetPara() == nPara ) &&
1439 ( nPos > mpIMEInfos->aPos.GetIndex() ) && ( nPos <= ( mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen ) ) )
1441 sal_uInt16 nAttr = mpIMEInfos->pAttribs[ nPos - mpIMEInfos->aPos.GetIndex() - 1 ];
1442 if ( nAttr & EXTTEXTINPUT_ATTR_UNDERLINE )
1443 rFont.SetUnderline( UNDERLINE_SINGLE );
1444 else if ( nAttr & EXTTEXTINPUT_ATTR_BOLDUNDERLINE )
1445 rFont.SetUnderline( UNDERLINE_BOLD );
1446 else if ( nAttr & EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE )
1447 rFont.SetUnderline( UNDERLINE_DOTTED );
1448 else if ( nAttr & EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE )
1449 rFont.SetUnderline( UNDERLINE_DOTTED );
1450 if ( nAttr & EXTTEXTINPUT_ATTR_REDTEXT )
1451 rFont.SetColor( Color( COL_RED ) );
1452 else if ( nAttr & EXTTEXTINPUT_ATTR_HALFTONETEXT )
1453 rFont.SetColor( Color( COL_LIGHTGRAY ) );
1454 if ( nAttr & EXTTEXTINPUT_ATTR_HIGHLIGHT )
1456 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1457 rFont.SetColor( rStyleSettings.GetHighlightTextColor() );
1458 rFont.SetFillColor( rStyleSettings.GetHighlightColor() );
1459 rFont.SetTransparent( sal_False );
1461 else if ( nAttr & EXTTEXTINPUT_ATTR_GRAYWAVELINE )
1463 rFont.SetUnderline( UNDERLINE_WAVE );
1464 // if( pOut )
1465 // pOut->SetTextLineColor( Color( COL_LIGHTGRAY ) );
1470 void TextEngine::FormatAndUpdate( TextView* pCurView )
1472 if ( mbDowning )
1473 return ;
1475 if ( IsInUndo() )
1476 IdleFormatAndUpdate( pCurView );
1477 else
1479 FormatDoc();
1480 UpdateViews( pCurView );
1485 void TextEngine::IdleFormatAndUpdate( TextView* pCurView, sal_uInt16 nMaxTimerRestarts )
1487 mpIdleFormatter->DoIdleFormat( pCurView, nMaxTimerRestarts );
1490 void TextEngine::TextModified()
1492 mbFormatted = sal_False;
1493 mbModified = sal_True;
1496 void TextEngine::UpdateViews( TextView* pCurView )
1498 if ( !GetUpdateMode() || IsFormatting() || maInvalidRect.IsEmpty() )
1499 return;
1501 DBG_ASSERT( IsFormatted(), "UpdateViews: Doc not formatted!" );
1503 for ( sal_uInt16 nView = 0; nView < mpViews->size(); nView++ )
1505 TextView* pView = (*mpViews)[ nView ];
1506 pView->HideCursor();
1508 Rectangle aClipRect( maInvalidRect );
1509 Size aOutSz = pView->GetWindow()->GetOutputSizePixel();
1510 Rectangle aVisArea( pView->GetStartDocPos(), aOutSz );
1511 aClipRect.Intersection( aVisArea );
1512 if ( !aClipRect.IsEmpty() )
1514 // translate into window coordinates
1515 Point aNewPos = pView->GetWindowPos( aClipRect.TopLeft() );
1516 if ( IsRightToLeft() )
1517 aNewPos.X() -= aOutSz.Width() - 1;
1518 aClipRect.SetPos( aNewPos );
1520 if ( pView == pCurView )
1521 pView->ImpPaint( aClipRect, !pView->GetWindow()->IsPaintTransparent() );
1522 else
1523 pView->GetWindow()->Invalidate( aClipRect );
1527 if ( pCurView )
1529 pCurView->ShowCursor( pCurView->IsAutoScroll() );
1532 maInvalidRect = Rectangle();
1535 IMPL_LINK_NOARG(TextEngine, IdleFormatHdl)
1537 FormatAndUpdate( mpIdleFormatter->GetView() );
1538 return 0;
1541 void TextEngine::CheckIdleFormatter()
1543 mpIdleFormatter->ForceTimeout();
1546 void TextEngine::FormatFullDoc()
1548 for ( sal_uLong nPortion = 0; nPortion < mpTEParaPortions->Count(); nPortion++ )
1550 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPortion ); sal_uInt16 nLen = pTEParaPortion->GetNode()->GetText().Len();
1551 pTEParaPortion->MarkSelectionInvalid( 0, nLen );
1553 mbFormatted = sal_False;
1554 FormatDoc();
1557 void TextEngine::FormatDoc()
1559 if ( IsFormatted() || !GetUpdateMode() || IsFormatting() )
1560 return;
1562 mbIsFormatting = sal_True;
1563 mbHasMultiLineParas = sal_False;
1565 long nY = 0;
1566 bool bGrow = false;
1568 maInvalidRect = Rectangle(); // clear
1569 for ( sal_uLong nPara = 0; nPara < mpTEParaPortions->Count(); nPara++ )
1571 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1572 if ( pTEParaPortion->IsInvalid() )
1574 sal_uLong nOldParaWidth = 0xFFFFFFFF;
1575 if ( mnCurTextWidth != 0xFFFFFFFF )
1576 nOldParaWidth = CalcTextWidth( nPara );
1578 ImpFormattingParagraph( nPara );
1580 if ( CreateLines( nPara ) )
1581 bGrow = true;
1583 // set InvalidRect only once
1584 if ( maInvalidRect.IsEmpty() )
1586 // otherwise remains Empty() for Paperwidth 0 (AutoPageSize)
1587 long nWidth = (long)mnMaxTextWidth;
1588 if ( !nWidth )
1589 nWidth = 0x7FFFFFFF;
1590 Range aInvRange( GetInvalidYOffsets( nPara ) );
1591 maInvalidRect = Rectangle( Point( 0, nY+aInvRange.Min() ),
1592 Size( nWidth, aInvRange.Len() ) );
1594 else
1596 maInvalidRect.Bottom() = nY + CalcParaHeight( nPara );
1599 if ( mnCurTextWidth != 0xFFFFFFFF )
1601 sal_uLong nNewParaWidth = CalcTextWidth( nPara );
1602 if ( nNewParaWidth >= mnCurTextWidth )
1603 mnCurTextWidth = nNewParaWidth;
1604 else if ( ( nOldParaWidth != 0xFFFFFFFF ) && ( nOldParaWidth >= mnCurTextWidth ) )
1605 mnCurTextWidth = 0xFFFFFFFF;
1608 else if ( bGrow )
1610 maInvalidRect.Bottom() = nY + CalcParaHeight( nPara );
1612 nY += CalcParaHeight( nPara );
1613 if ( !mbHasMultiLineParas && pTEParaPortion->GetLines().size() > 1 )
1614 mbHasMultiLineParas = sal_True;
1617 if ( !maInvalidRect.IsEmpty() )
1619 sal_uLong nNewHeight = CalcTextHeight();
1620 long nDiff = nNewHeight - mnCurTextHeight;
1621 if ( nNewHeight < mnCurTextHeight )
1623 maInvalidRect.Bottom() = (long)std::max( nNewHeight, mnCurTextHeight );
1624 if ( maInvalidRect.IsEmpty() )
1626 maInvalidRect.Top() = 0;
1627 // Left and Right are not evaluated, but set because of IsEmpty
1628 maInvalidRect.Left() = 0;
1629 maInvalidRect.Right() = mnMaxTextWidth;
1633 mnCurTextHeight = nNewHeight;
1634 if ( nDiff )
1636 mbFormatted = sal_True;
1637 ImpTextHeightChanged();
1641 mbIsFormatting = sal_False;
1642 mbFormatted = sal_True;
1644 ImpTextFormatted();
1647 void TextEngine::CreateAndInsertEmptyLine( sal_uLong nPara )
1649 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
1650 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1652 TextLine* pTmpLine = new TextLine;
1653 pTmpLine->SetStart( pNode->GetText().Len() );
1654 pTmpLine->SetEnd( pTmpLine->GetStart() );
1655 pTEParaPortion->GetLines().push_back( pTmpLine );
1657 if ( ImpGetAlign() == TXTALIGN_CENTER )
1658 pTmpLine->SetStartX( (short)(mnMaxTextWidth / 2) );
1659 else if ( ImpGetAlign() == TXTALIGN_RIGHT )
1660 pTmpLine->SetStartX( (short)mnMaxTextWidth );
1661 else
1662 pTmpLine->SetStartX( mpDoc->GetLeftMargin() );
1664 bool bLineBreak = pNode->GetText().Len() ? true : false;
1666 TETextPortion* pDummyPortion = new TETextPortion( 0 );
1667 pDummyPortion->GetWidth() = 0;
1668 pTEParaPortion->GetTextPortions().push_back( pDummyPortion );
1670 if ( bLineBreak )
1672 // -2: The new one is already inserted.
1673 OSL_ENSURE(
1674 pTEParaPortion->GetLines()[pTEParaPortion->GetLines().size()-2],
1675 "CreateAndInsertEmptyLine: Soft Break, no Line?!");
1676 sal_uInt16 nPos = (sal_uInt16) pTEParaPortion->GetTextPortions().size() - 1 ;
1677 pTmpLine->SetStartPortion( nPos );
1678 pTmpLine->SetEndPortion( nPos );
1682 void TextEngine::ImpBreakLine( sal_uLong nPara, TextLine* pLine, TETextPortion*, sal_uInt16 nPortionStart, long nRemainingWidth )
1684 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
1686 // Font still should be adjusted
1687 sal_uInt16 nMaxBreakPos = mpRefDev->GetTextBreak( pNode->GetText(), nRemainingWidth, nPortionStart );
1689 DBG_ASSERT( nMaxBreakPos < pNode->GetText().Len(), "ImpBreakLine: Break?!" );
1691 if ( nMaxBreakPos == STRING_LEN ) // GetTextBreak() != GetTextSize()
1692 nMaxBreakPos = pNode->GetText().Len() - 1;
1694 uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator();
1695 i18n::LineBreakHyphenationOptions aHyphOptions( NULL, uno::Sequence< beans::PropertyValue >(), 1 );
1697 i18n::LineBreakUserOptions aUserOptions;
1698 aUserOptions.forbiddenBeginCharacters = ImpGetLocaleDataWrapper()->getForbiddenCharacters().beginLine;
1699 aUserOptions.forbiddenEndCharacters = ImpGetLocaleDataWrapper()->getForbiddenCharacters().endLine;
1700 aUserOptions.applyForbiddenRules = sal_True;
1701 aUserOptions.allowPunctuationOutsideMargin = sal_False;
1702 aUserOptions.allowHyphenateEnglish = sal_False;
1704 static const com::sun::star::lang::Locale aDefLocale;
1705 i18n::LineBreakResults aLBR = xBI->getLineBreak( pNode->GetText(), nMaxBreakPos, aDefLocale, pLine->GetStart(), aHyphOptions, aUserOptions );
1706 sal_uInt16 nBreakPos = (sal_uInt16)aLBR.breakIndex;
1707 if ( nBreakPos <= pLine->GetStart() )
1709 nBreakPos = nMaxBreakPos;
1710 if ( nBreakPos <= pLine->GetStart() )
1711 nBreakPos = pLine->GetStart() + 1; // infinite loop otherwise!
1714 // the damaged Portion is the End Portion
1715 pLine->SetEnd( nBreakPos );
1716 sal_uInt16 nEndPortion = SplitTextPortion( nPara, nBreakPos );
1718 bool bBlankSeparator = ( ( nBreakPos >= pLine->GetStart() ) &&
1719 ( pNode->GetText().GetChar( nBreakPos ) == ' ' ) );
1720 if ( bBlankSeparator )
1722 // generally suppress blanks at the end of line
1723 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1724 TETextPortion* pTP = pTEParaPortion->GetTextPortions()[ nEndPortion ];
1725 DBG_ASSERT( nBreakPos > pLine->GetStart(), "ImpBreakLine: SplitTextPortion at beginning of line?" );
1726 pTP->GetWidth() = (long)CalcTextWidth( nPara, nBreakPos-pTP->GetLen(), pTP->GetLen()-1 );
1728 pLine->SetEndPortion( nEndPortion );
1731 sal_uInt16 TextEngine::SplitTextPortion( sal_uLong nPara, sal_uInt16 nPos )
1734 // the Portion at nPos is being split, unless there is already a switch at nPos
1735 if ( nPos == 0 )
1736 return 0;
1738 sal_uInt16 nSplitPortion;
1739 sal_uInt16 nTmpPos = 0;
1740 TETextPortion* pTextPortion = 0;
1741 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1742 sal_uInt16 nPortions = pTEParaPortion->GetTextPortions().size();
1743 for ( nSplitPortion = 0; nSplitPortion < nPortions; nSplitPortion++ )
1745 TETextPortion* pTP = pTEParaPortion->GetTextPortions()[nSplitPortion];
1746 nTmpPos = nTmpPos + pTP->GetLen();
1747 if ( nTmpPos >= nPos )
1749 if ( nTmpPos == nPos ) // nothing needs splitting
1750 return nSplitPortion;
1751 pTextPortion = pTP;
1752 break;
1756 DBG_ASSERT( pTextPortion, "SplitTextPortion: position outside of region!" );
1758 sal_uInt16 nOverlapp = nTmpPos - nPos;
1759 pTextPortion->GetLen() = pTextPortion->GetLen() - nOverlapp;
1760 TETextPortion* pNewPortion = new TETextPortion( nOverlapp );
1761 pTEParaPortion->GetTextPortions().insert( pTEParaPortion->GetTextPortions().begin() + nSplitPortion + 1, pNewPortion );
1762 pTextPortion->GetWidth() = (long)CalcTextWidth( nPara, nPos-pTextPortion->GetLen(), pTextPortion->GetLen() );
1764 return nSplitPortion;
1767 void TextEngine::CreateTextPortions( sal_uLong nPara, sal_uInt16 nStartPos )
1769 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1770 TextNode* pNode = pTEParaPortion->GetNode();
1771 DBG_ASSERT( pNode->GetText().Len(), "CreateTextPortions: should not be used for empty paragraphs!" );
1773 std::set<sal_uInt16> aPositions;
1774 std::set<sal_uInt16>::iterator aPositionsIt;
1775 aPositions.insert(0);
1777 sal_uInt16 nAttribs = pNode->GetCharAttribs().Count();
1778 for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
1780 TextCharAttrib* pAttrib = pNode->GetCharAttribs().GetAttrib( nAttr );
1782 aPositions.insert( pAttrib->GetStart() );
1783 aPositions.insert( pAttrib->GetEnd() );
1785 aPositions.insert( pNode->GetText().Len() );
1787 const std::vector<TEWritingDirectionInfo>& rWritingDirections = pTEParaPortion->GetWritingDirectionInfos();
1788 for ( std::vector<TEWritingDirectionInfo>::const_iterator it = rWritingDirections.begin(); it != rWritingDirections.end(); ++it )
1789 aPositions.insert( (*it).nStartPos );
1791 if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetPara() == nPara ) )
1793 sal_uInt16 nLastAttr = 0xFFFF;
1794 for( sal_uInt16 n = 0; n < mpIMEInfos->nLen; n++ )
1796 if ( mpIMEInfos->pAttribs[n] != nLastAttr )
1798 aPositions.insert( mpIMEInfos->aPos.GetIndex() + n );
1799 nLastAttr = mpIMEInfos->pAttribs[n];
1804 sal_uInt16 nTabPos = pNode->GetText().Search( '\t', 0 );
1805 while ( nTabPos != STRING_NOTFOUND )
1807 aPositions.insert( nTabPos );
1808 aPositions.insert( nTabPos + 1 );
1809 nTabPos = pNode->GetText().Search( '\t', nTabPos+1 );
1812 // Delete starting with...
1813 // Unfortunately, the number of TextPortions does not have to be
1814 // equal to aPositions.Count(), because of linebreaks
1815 sal_uInt16 nPortionStart = 0;
1816 sal_uInt16 nInvPortion = 0;
1817 sal_uInt16 nP;
1818 for ( nP = 0; nP < pTEParaPortion->GetTextPortions().size(); nP++ )
1820 TETextPortion* pTmpPortion = pTEParaPortion->GetTextPortions()[nP];
1821 nPortionStart = nPortionStart + pTmpPortion->GetLen();
1822 if ( nPortionStart >= nStartPos )
1824 nPortionStart = nPortionStart - pTmpPortion->GetLen();
1825 nInvPortion = nP;
1826 break;
1829 OSL_ENSURE(nP < pTEParaPortion->GetTextPortions().size()
1830 || pTEParaPortion->GetTextPortions().empty(),
1831 "CreateTextPortions: Nothing to delete!");
1832 if ( nInvPortion && ( nPortionStart+pTEParaPortion->GetTextPortions()[nInvPortion]->GetLen() > nStartPos ) )
1834 // better one before...
1835 // But only if it was within the Portion; otherwise it might be
1836 // the only one in the previous line!
1837 nInvPortion--;
1838 nPortionStart = nPortionStart - pTEParaPortion->GetTextPortions()[nInvPortion]->GetLen();
1840 pTEParaPortion->GetTextPortions().DeleteFromPortion( nInvPortion );
1842 // a Portion might have been created by a line break
1843 aPositions.insert( nPortionStart );
1845 aPositionsIt = aPositions.find( nPortionStart );
1846 DBG_ASSERT( aPositionsIt != aPositions.end(), "CreateTextPortions: nPortionStart not found" );
1848 if ( aPositionsIt != aPositions.end() )
1850 std::set<sal_uInt16>::iterator nextIt = aPositionsIt;
1851 for ( ++nextIt; nextIt != aPositions.end(); ++aPositionsIt, ++nextIt )
1853 TETextPortion* pNew = new TETextPortion( *nextIt - *aPositionsIt );
1854 pTEParaPortion->GetTextPortions().push_back( pNew );
1857 OSL_ENSURE(pTEParaPortion->GetTextPortions().size(), "CreateTextPortions: No Portions?!");
1860 void TextEngine::RecalcTextPortion( sal_uLong nPara, sal_uInt16 nStartPos, short nNewChars )
1862 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1863 OSL_ENSURE(pTEParaPortion->GetTextPortions().size(), "RecalcTextPortion: no Portions!");
1864 OSL_ENSURE(nNewChars, "RecalcTextPortion: Diff == 0");
1866 TextNode* const pNode = pTEParaPortion->GetNode();
1867 if ( nNewChars > 0 )
1869 // If an Attribute is starting/ending at nStartPos, or there is a tab
1870 // before nStartPos => a new Portion starts.
1871 // Otherwise the Portion is extended at nStartPos.
1872 // Or if at the very beginning ( StartPos 0 ) followed by a tab...
1873 if ( ( pNode->GetCharAttribs().HasBoundingAttrib( nStartPos ) ) ||
1874 ( nStartPos && ( pNode->GetText().GetChar( nStartPos - 1 ) == '\t' ) ) ||
1875 ( ( !nStartPos && ( nNewChars < pNode->GetText().Len() ) && pNode->GetText().GetChar( nNewChars ) == '\t' ) ) )
1877 sal_uInt16 nNewPortionPos = 0;
1878 if ( nStartPos )
1879 nNewPortionPos = SplitTextPortion( nPara, nStartPos ) + 1;
1881 // Here could be an empty Portion if the paragraph was empty,
1882 // or a new line was created by a hard line-break.
1883 if ( ( nNewPortionPos < pTEParaPortion->GetTextPortions().size() ) &&
1884 !pTEParaPortion->GetTextPortions()[nNewPortionPos]->GetLen() )
1886 // use the empty Portion
1887 sal_uInt16 & r =
1888 pTEParaPortion->GetTextPortions()[nNewPortionPos]->GetLen();
1889 r = r + nNewChars;
1891 else
1893 TETextPortion* pNewPortion = new TETextPortion( nNewChars );
1894 pTEParaPortion->GetTextPortions().insert( pTEParaPortion->GetTextPortions().begin() + nNewPortionPos, pNewPortion );
1897 else
1899 sal_uInt16 nPortionStart;
1900 const sal_uInt16 nTP = pTEParaPortion->GetTextPortions().
1901 FindPortion( nStartPos, nPortionStart );
1902 TETextPortion* const pTP = pTEParaPortion->GetTextPortions()[ nTP ];
1903 DBG_ASSERT( pTP, "RecalcTextPortion: Portion not found!" );
1904 pTP->GetLen() = pTP->GetLen() + nNewChars;
1905 pTP->GetWidth() = (-1);
1908 else
1910 // Shrink or remove Portion
1911 // Before calling this function, ensure that no Portions were in the deleted range!
1913 // There must be no Portion reaching into or starting within,
1914 // thus: nStartPos <= nPos <= nStartPos - nNewChars(neg.)
1915 sal_uInt16 nPortion = 0;
1916 sal_uInt16 nPos = 0;
1917 sal_uInt16 nEnd = nStartPos-nNewChars;
1918 sal_uInt16 nPortions = pTEParaPortion->GetTextPortions().size();
1919 TETextPortion* pTP = 0;
1920 for ( nPortion = 0; nPortion < nPortions; nPortion++ )
1922 pTP = pTEParaPortion->GetTextPortions()[ nPortion ];
1923 if ( ( nPos+pTP->GetLen() ) > nStartPos )
1925 DBG_ASSERT( nPos <= nStartPos, "RecalcTextPortion: Bad Start!" );
1926 DBG_ASSERT( nPos+pTP->GetLen() >= nEnd, "RecalcTextPortion: Bad End!" );
1927 break;
1929 nPos = nPos + pTP->GetLen();
1931 DBG_ASSERT( pTP, "RecalcTextPortion: Portion not found!" );
1932 if ( ( nPos == nStartPos ) && ( (nPos+pTP->GetLen()) == nEnd ) )
1934 // remove Portion
1935 pTEParaPortion->GetTextPortions().erase( pTEParaPortion->GetTextPortions().begin() + nPortion );
1936 delete pTP;
1938 else
1940 DBG_ASSERT( pTP->GetLen() > (-nNewChars), "RecalcTextPortion: Portion too small to shrink!" );
1941 pTP->GetLen() = pTP->GetLen() + nNewChars;
1943 OSL_ENSURE( pTEParaPortion->GetTextPortions().size(),
1944 "RecalcTextPortion: none are left!" );
1948 void TextEngine::ImpPaint( OutputDevice* pOutDev, const Point& rStartPos, Rectangle const* pPaintArea, TextSelection const* pPaintRange, TextSelection const* pSelection )
1950 if ( !GetUpdateMode() )
1951 return;
1953 if ( !IsFormatted() )
1954 FormatDoc();
1956 Window* pOutWin = dynamic_cast<Window*>(pOutDev);
1957 bool bTransparent = (pOutWin && pOutWin->IsPaintTransparent());
1959 long nY = rStartPos.Y();
1961 TextPaM const* pSelStart = 0;
1962 TextPaM const* pSelEnd = 0;
1963 if ( pSelection && pSelection->HasRange() )
1965 bool bInvers = pSelection->GetEnd() < pSelection->GetStart();
1966 pSelStart = !bInvers ? &pSelection->GetStart() : &pSelection->GetEnd();
1967 pSelEnd = bInvers ? &pSelection->GetStart() : &pSelection->GetEnd();
1969 DBG_ASSERT( !pPaintRange || ( pPaintRange->GetStart() < pPaintRange->GetEnd() ), "ImpPaint: Paint-Range?!" );
1971 const StyleSettings& rStyleSettings = pOutDev->GetSettings().GetStyleSettings();
1973 // for all paragraphs
1974 for ( sal_uLong nPara = 0; nPara < mpTEParaPortions->Count(); nPara++ )
1976 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
1977 // in case while typing Idle-Formatting, asynchronous Paint
1978 if ( pPortion->IsInvalid() )
1979 return;
1981 sal_uLong nParaHeight = CalcParaHeight( nPara );
1982 if ( ( !pPaintArea || ( ( nY + (long)nParaHeight ) > pPaintArea->Top() ) )
1983 && ( !pPaintRange || ( ( nPara >= pPaintRange->GetStart().GetPara() ) && ( nPara <= pPaintRange->GetEnd().GetPara() ) ) ) )
1985 // for all lines of the paragraph
1986 sal_uInt16 nLines = pPortion->GetLines().size();
1987 sal_uInt16 nIndex = 0;
1988 for ( sal_uInt16 nLine = 0; nLine < nLines; nLine++ )
1990 TextLine* pLine = pPortion->GetLines()[nLine];
1991 Point aTmpPos( rStartPos.X() + pLine->GetStartX(), nY );
1993 if ( ( !pPaintArea || ( ( nY + mnCharHeight ) > pPaintArea->Top() ) )
1994 && ( !pPaintRange || (
1995 ( TextPaM( nPara, pLine->GetStart() ) < pPaintRange->GetEnd() ) &&
1996 ( TextPaM( nPara, pLine->GetEnd() ) > pPaintRange->GetStart() ) ) ) )
1998 // for all Portions of the line
1999 nIndex = pLine->GetStart();
2000 for ( sal_uInt16 y = pLine->GetStartPortion(); y <= pLine->GetEndPortion(); y++ )
2002 OSL_ENSURE(pPortion->GetTextPortions().size(),
2003 "ImpPaint: Line without Textportion!");
2004 TETextPortion* pTextPortion = pPortion->GetTextPortions()[ y ];
2005 DBG_ASSERT( pTextPortion, "ImpPaint: Bad pTextPortion!" );
2007 ImpInitLayoutMode( pOutDev /*, pTextPortion->IsRightToLeft() */);
2009 long nTxtWidth = pTextPortion->GetWidth();
2010 aTmpPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nIndex, nIndex );
2012 // only print if starting in the visible region
2013 if ( ( ( aTmpPos.X() + nTxtWidth ) >= 0 )
2014 && ( !pPaintRange || (
2015 ( TextPaM( nPara, nIndex ) < pPaintRange->GetEnd() ) &&
2016 ( TextPaM( nPara, nIndex + pTextPortion->GetLen() ) > pPaintRange->GetStart() ) ) ) )
2018 switch ( pTextPortion->GetKind() )
2020 case PORTIONKIND_TEXT:
2023 Font aFont;
2024 SeekCursor( nPara, nIndex+1, aFont, pOutDev );
2025 if( bTransparent )
2026 aFont.SetTransparent( sal_True );
2027 else if ( pSelection )
2028 aFont.SetTransparent( sal_False );
2029 pOutDev->SetFont( aFont );
2031 sal_uInt16 nTmpIndex = nIndex;
2032 sal_uInt16 nEnd = nTmpIndex + pTextPortion->GetLen();
2033 Point aPos = aTmpPos;
2034 if ( pPaintRange )
2036 // maybe not print all of it
2037 if ( ( pPaintRange->GetStart().GetPara() == nPara )
2038 && ( nTmpIndex < pPaintRange->GetStart().GetIndex() ) )
2040 nTmpIndex = pPaintRange->GetStart().GetIndex();
2042 if ( ( pPaintRange->GetEnd().GetPara() == nPara )
2043 && ( nEnd > pPaintRange->GetEnd().GetIndex() ) )
2045 nEnd = pPaintRange->GetEnd().GetIndex();
2049 bool bDone = false;
2050 if ( pSelStart )
2052 // is a part of it in the selection?
2053 TextPaM aTextStart( nPara, nTmpIndex );
2054 TextPaM aTextEnd( nPara, nEnd );
2055 if ( ( aTextStart < *pSelEnd ) && ( aTextEnd > *pSelStart ) )
2057 sal_uInt16 nL;
2059 // 1) Region before Selection
2060 if ( aTextStart < *pSelStart )
2062 nL = pSelStart->GetIndex() - nTmpIndex;
2063 pOutDev->SetFont( aFont);
2064 aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL );
2065 pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nL );
2066 nTmpIndex = nTmpIndex + nL;
2069 // 2) Region with Selection
2070 nL = nEnd-nTmpIndex;
2071 if ( aTextEnd > *pSelEnd )
2072 nL = pSelEnd->GetIndex() - nTmpIndex;
2073 if ( nL )
2075 Color aOldTextColor = pOutDev->GetTextColor();
2076 pOutDev->SetTextColor( rStyleSettings.GetHighlightTextColor() );
2077 pOutDev->SetTextFillColor( rStyleSettings.GetHighlightColor() );
2078 aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL );
2079 pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nL );
2080 pOutDev->SetTextColor( aOldTextColor );
2081 pOutDev->SetTextFillColor();
2082 nTmpIndex = nTmpIndex + nL;
2085 // 3) Region after Selection
2086 if ( nTmpIndex < nEnd )
2088 nL = nEnd-nTmpIndex;
2089 aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL );
2090 pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nEnd-nTmpIndex );
2092 bDone = true;
2095 if ( !bDone )
2097 aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nEnd );
2098 pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nEnd-nTmpIndex );
2103 break;
2104 case PORTIONKIND_TAB:
2106 // for HideSelection() only Range, pSelection = 0.
2107 if ( pSelStart || pPaintRange )
2109 Rectangle aTabArea( aTmpPos, Point( aTmpPos.X()+nTxtWidth, aTmpPos.Y()+mnCharHeight-1 ) );
2110 bool bDone = false;
2111 if ( pSelStart )
2113 // is the Tab in the Selection???
2114 TextPaM aTextStart( nPara, nIndex );
2115 TextPaM aTextEnd( nPara, nIndex+1 );
2116 if ( ( aTextStart < *pSelEnd ) && ( aTextEnd > *pSelStart ) )
2118 Color aOldColor = pOutDev->GetFillColor();
2119 pOutDev->SetFillColor( rStyleSettings.GetHighlightColor() );
2120 pOutDev->DrawRect( aTabArea );
2121 pOutDev->SetFillColor( aOldColor );
2122 bDone = true;
2125 if ( !bDone )
2127 pOutDev->Erase( aTabArea );
2131 break;
2132 default: OSL_FAIL( "ImpPaint: Unknown Portion-Type !" );
2136 nIndex = nIndex + pTextPortion->GetLen();
2140 nY += mnCharHeight;
2142 if ( pPaintArea && ( nY >= pPaintArea->Bottom() ) )
2143 break; // no more visible actions
2146 else
2148 nY += nParaHeight;
2151 if ( pPaintArea && ( nY > pPaintArea->Bottom() ) )
2152 break; // no more visible actions
2156 sal_Bool TextEngine::CreateLines( sal_uLong nPara )
2158 // sal_Bool: changing Height of Paragraph Yes/No - sal_True/sal_False
2160 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2161 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2162 DBG_ASSERT( pTEParaPortion->IsInvalid(), "CreateLines: Portion not invalid!" );
2164 sal_uInt16 nOldLineCount = pTEParaPortion->GetLines().size();
2166 // fast special case for empty paragraphs
2167 if ( pTEParaPortion->GetNode()->GetText().Len() == 0 )
2169 if ( !pTEParaPortion->GetTextPortions().empty() )
2170 pTEParaPortion->GetTextPortions().Reset();
2171 if ( !pTEParaPortion->GetLines().empty() )
2173 BOOST_FOREACH(TextLine* pLine, pTEParaPortion->GetLines())
2174 delete pLine;
2175 pTEParaPortion->GetLines().clear();
2177 CreateAndInsertEmptyLine( nPara );
2178 pTEParaPortion->SetValid();
2179 return nOldLineCount != pTEParaPortion->GetLines().size();
2182 // initialization
2183 if ( pTEParaPortion->GetLines().empty() )
2185 TextLine* pL = new TextLine;
2186 pTEParaPortion->GetLines().push_back( pL );
2189 const short nInvalidDiff = pTEParaPortion->GetInvalidDiff();
2190 const sal_uInt16 nInvalidStart = pTEParaPortion->GetInvalidPosStart();
2191 const sal_uInt16 nInvalidEnd = nInvalidStart + std::abs( nInvalidDiff );
2192 bool bQuickFormat = false;
2194 if ( pTEParaPortion->GetWritingDirectionInfos().empty() )
2195 ImpInitWritingDirections( nPara );
2197 if ( pTEParaPortion->GetWritingDirectionInfos().size() == 1 )
2199 if ( pTEParaPortion->IsSimpleInvalid() && ( nInvalidDiff > 0 ) )
2201 bQuickFormat = true;
2203 else if ( ( pTEParaPortion->IsSimpleInvalid() ) && ( nInvalidDiff < 0 ) )
2205 // check if deleting across Portion border
2206 sal_uInt16 nStart = nInvalidStart; // duplicate!!!
2207 sal_uInt16 nEnd = nStart - nInvalidDiff; // neg.
2208 bQuickFormat = true;
2209 sal_uInt16 nPos = 0;
2210 sal_uInt16 nPortions = pTEParaPortion->GetTextPortions().size();
2211 for ( sal_uInt16 nTP = 0; nTP < nPortions; nTP++ )
2213 // there must be no Start/End in the deleted region
2214 TETextPortion* const pTP = pTEParaPortion->GetTextPortions()[ nTP ];
2215 nPos = nPos + pTP->GetLen();
2216 if ( ( nPos > nStart ) && ( nPos < nEnd ) )
2218 bQuickFormat = false;
2219 break;
2225 if ( bQuickFormat )
2226 RecalcTextPortion( nPara, nInvalidStart, nInvalidDiff );
2227 else
2228 CreateTextPortions( nPara, nInvalidStart );
2230 // search for line with InvalidPos; start a line prior
2231 // flag lines => do not remove!
2233 sal_uInt16 nLine = pTEParaPortion->GetLines().size()-1;
2234 for ( sal_uInt16 nL = 0; nL <= nLine; nL++ )
2236 TextLine* pLine = pTEParaPortion->GetLines()[ nL ];
2237 if ( pLine->GetEnd() > nInvalidStart )
2239 nLine = nL;
2240 break;
2242 pLine->SetValid();
2244 // start a line before...
2245 // if typing at the end, the line before cannot change
2246 if ( nLine && ( !pTEParaPortion->IsSimpleInvalid() || ( nInvalidEnd < pNode->GetText().Len() ) || ( nInvalidDiff <= 0 ) ) )
2247 nLine--;
2249 TextLine* pLine = pTEParaPortion->GetLines()[ nLine ];
2251 // format all lines starting here
2252 size_t nDelFromLine = std::numeric_limits<size_t>::max();
2253 bool bLineBreak = false;
2255 sal_uInt16 nIndex = pLine->GetStart();
2256 TextLine aSaveLine( *pLine );
2258 Font aFont;
2260 bool bCalcPortion = true;
2262 while ( nIndex < pNode->GetText().Len() )
2264 bool bEOL = false;
2265 sal_uInt16 nPortionStart = 0;
2266 sal_uInt16 nPortionEnd = 0;
2268 sal_uInt16 nTmpPos = nIndex;
2269 sal_uInt16 nTmpPortion = pLine->GetStartPortion();
2270 long nTmpWidth = mpDoc->GetLeftMargin();
2271 // do not subtract margin; it is included in TmpWidth
2272 long nXWidth = mnMaxTextWidth ? mnMaxTextWidth : 0x7FFFFFFF;
2273 if ( nXWidth < nTmpWidth )
2274 nXWidth = nTmpWidth;
2276 // search for Portion that does not fit anymore into line
2277 TETextPortion* pPortion = 0;
2278 bool bBrokenLine = false;
2279 bLineBreak = false;
2281 while ( ( nTmpWidth <= nXWidth ) && !bEOL && ( nTmpPortion < pTEParaPortion->GetTextPortions().size() ) )
2283 nPortionStart = nTmpPos;
2284 pPortion = pTEParaPortion->GetTextPortions()[ nTmpPortion ];
2285 DBG_ASSERT( pPortion->GetLen(), "CreateLines: Empty Portion!" );
2286 if ( pNode->GetText().GetChar( nTmpPos ) == '\t' )
2288 long nCurPos = nTmpWidth-mpDoc->GetLeftMargin();
2289 nTmpWidth = ((nCurPos/mnDefTab)+1)*mnDefTab+mpDoc->GetLeftMargin();
2290 pPortion->GetWidth() = nTmpWidth - nCurPos - mpDoc->GetLeftMargin();
2291 // infinite loop, if this is the first token of the line and nTmpWidth > aPaperSize.Width !!!
2292 if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) )
2294 // adjust Tab
2295 pPortion->GetWidth() = nXWidth-1;
2296 nTmpWidth = pPortion->GetWidth();
2297 bEOL = true;
2298 bBrokenLine = true;
2300 pPortion->GetKind() = PORTIONKIND_TAB;
2302 else
2305 if ( bCalcPortion || !pPortion->HasValidSize() )
2306 pPortion->GetWidth() = (long)CalcTextWidth( nPara, nTmpPos, pPortion->GetLen() );
2307 nTmpWidth += pPortion->GetWidth();
2309 pPortion->GetRightToLeft() = ImpGetRightToLeft( nPara, nTmpPos+1 );
2310 pPortion->GetKind() = PORTIONKIND_TEXT;
2313 nTmpPos = nTmpPos + pPortion->GetLen();
2314 nPortionEnd = nTmpPos;
2315 nTmpPortion++;
2318 // this was perhaps one Portion too far
2319 bool bFixedEnd = false;
2320 if ( nTmpWidth > nXWidth )
2322 nPortionEnd = nTmpPos;
2323 nTmpPos = nTmpPos - pPortion->GetLen();
2324 nPortionStart = nTmpPos;
2325 nTmpPortion--;
2326 bEOL = false;
2328 nTmpWidth -= pPortion->GetWidth();
2329 if ( pPortion->GetKind() == PORTIONKIND_TAB )
2331 bEOL = true;
2332 bFixedEnd = true;
2335 else
2337 bEOL = true;
2338 pLine->SetEnd( nPortionEnd );
2339 OSL_ENSURE(pTEParaPortion->GetTextPortions().size(),
2340 "CreateLines: No TextPortions?");
2341 pLine->SetEndPortion( (sal_uInt16)pTEParaPortion->GetTextPortions().size() - 1 );
2344 if ( bFixedEnd )
2346 pLine->SetEnd( nPortionStart );
2347 pLine->SetEndPortion( nTmpPortion-1 );
2349 else if ( bLineBreak || bBrokenLine )
2351 pLine->SetEnd( nPortionStart+1 );
2352 pLine->SetEndPortion( nTmpPortion-1 );
2354 else if ( !bEOL )
2356 DBG_ASSERT( (nPortionEnd-nPortionStart) == pPortion->GetLen(), "CreateLines: There is a Portion after all?!" );
2357 long nRemainingWidth = mnMaxTextWidth - nTmpWidth;
2358 ImpBreakLine( nPara, pLine, pPortion, nPortionStart, nRemainingWidth );
2361 if ( ( ImpGetAlign() == TXTALIGN_CENTER ) || ( ImpGetAlign() == TXTALIGN_RIGHT ) )
2363 // adjust
2364 long nTextWidth = 0;
2365 for ( sal_uInt16 nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ )
2367 TETextPortion* pTextPortion = pTEParaPortion->GetTextPortions()[ nTP ];
2368 nTextWidth += pTextPortion->GetWidth();
2370 long nSpace = mnMaxTextWidth - nTextWidth;
2371 if ( nSpace > 0 )
2373 if ( ImpGetAlign() == TXTALIGN_CENTER )
2374 pLine->SetStartX( (sal_uInt16)(nSpace / 2) );
2375 else // TXTALIGN_RIGHT
2376 pLine->SetStartX( (sal_uInt16)nSpace );
2379 else
2381 pLine->SetStartX( mpDoc->GetLeftMargin() );
2384 // check if the line has to be printed again
2385 pLine->SetInvalid();
2387 if ( pTEParaPortion->IsSimpleInvalid() )
2389 // Change due to simple TextChange...
2390 // Do not abort formatting, as Portions might have to be split!
2391 // Once it is ok to abort, then validate the following lines!
2392 // But mark as valid, thus reduce printing...
2393 if ( pLine->GetEnd() < nInvalidStart )
2395 if ( *pLine == aSaveLine )
2397 pLine->SetValid();
2400 else
2402 sal_uInt16 nStart = pLine->GetStart();
2403 sal_uInt16 nEnd = pLine->GetEnd();
2405 if ( nStart > nInvalidEnd )
2407 if ( ( ( nStart-nInvalidDiff ) == aSaveLine.GetStart() ) &&
2408 ( ( nEnd-nInvalidDiff ) == aSaveLine.GetEnd() ) )
2410 pLine->SetValid();
2411 if ( bCalcPortion && bQuickFormat )
2413 bCalcPortion = false;
2414 pTEParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
2415 break;
2419 else if ( bQuickFormat && ( nEnd > nInvalidEnd) )
2421 // If the invalid line ends such that the next line starts
2422 // at the 'same' position as before (no change in line breaks),
2423 // the text width does not have to be recalculated.
2424 if ( nEnd == ( aSaveLine.GetEnd() + nInvalidDiff ) )
2426 bCalcPortion = false;
2427 pTEParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
2428 break;
2434 nIndex = pLine->GetEnd(); // next line Start = previous line End
2435 // because nEnd is past the last char!
2437 sal_uInt16 nEndPortion = pLine->GetEndPortion();
2439 // next line or new line
2440 pLine = 0;
2441 if ( nLine < pTEParaPortion->GetLines().size()-1 )
2442 pLine = pTEParaPortion->GetLines()[ ++nLine ];
2443 if ( pLine && ( nIndex >= pNode->GetText().Len() ) )
2445 nDelFromLine = nLine;
2446 break;
2448 if ( !pLine && ( nIndex < pNode->GetText().Len() ) )
2450 pLine = new TextLine;
2451 pTEParaPortion->GetLines().insert( pTEParaPortion->GetLines().begin() + ++nLine, pLine );
2453 if ( pLine )
2455 aSaveLine = *pLine;
2456 pLine->SetStart( nIndex );
2457 pLine->SetEnd( nIndex );
2458 pLine->SetStartPortion( nEndPortion+1 );
2459 pLine->SetEndPortion( nEndPortion+1 );
2461 } // while ( Index < Len )
2463 if (nDelFromLine != std::numeric_limits<size_t>::max())
2465 for( TextLines::iterator it = pTEParaPortion->GetLines().begin() + nDelFromLine;
2466 it != pTEParaPortion->GetLines().end(); ++it )
2468 delete *it;
2470 pTEParaPortion->GetLines().erase( pTEParaPortion->GetLines().begin() + nDelFromLine,
2471 pTEParaPortion->GetLines().end() );
2474 DBG_ASSERT( pTEParaPortion->GetLines().size(), "CreateLines: No Line!" );
2476 if ( bLineBreak )
2477 CreateAndInsertEmptyLine( nPara );
2479 pTEParaPortion->SetValid();
2481 return nOldLineCount != pTEParaPortion->GetLines().size();
2484 String TextEngine::GetWord( const TextPaM& rCursorPos, TextPaM* pStartOfWord )
2486 String aWord;
2487 if ( rCursorPos.GetPara() < mpDoc->GetNodes().Count() )
2489 TextSelection aSel( rCursorPos );
2490 TextNode* pNode = mpDoc->GetNodes().GetObject( rCursorPos.GetPara() );
2491 uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator();
2492 i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), rCursorPos.GetIndex(), GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True );
2493 aSel.GetStart().GetIndex() = (sal_uInt16)aBoundary.startPos;
2494 aSel.GetEnd().GetIndex() = (sal_uInt16)aBoundary.endPos;
2495 aWord = pNode->GetText().Copy( aSel.GetStart().GetIndex(), aSel.GetEnd().GetIndex() - aSel.GetStart().GetIndex() );
2496 if ( pStartOfWord )
2497 *pStartOfWord = aSel.GetStart();
2499 return aWord;
2502 sal_Bool TextEngine::Read( SvStream& rInput, const TextSelection* pSel )
2504 sal_Bool bUpdate = GetUpdateMode();
2505 SetUpdateMode( sal_False );
2507 UndoActionStart();
2508 TextSelection aSel;
2509 if ( pSel )
2510 aSel = *pSel;
2511 else
2513 sal_uLong nParas = mpDoc->GetNodes().Count();
2514 TextNode* pNode = mpDoc->GetNodes().GetObject( nParas - 1 );
2515 aSel = TextPaM( nParas-1 , pNode->GetText().Len() );
2518 if ( aSel.HasRange() )
2519 aSel = ImpDeleteText( aSel );
2521 OString aLine;
2522 sal_Bool bDone = rInput.ReadLine( aLine );
2523 OUString aTmpStr(OStringToOUString(aLine, rInput.GetStreamCharSet()));
2524 while ( bDone )
2526 aSel = ImpInsertText( aSel, aTmpStr );
2527 bDone = rInput.ReadLine( aLine );
2528 aTmpStr = OStringToOUString(aLine, rInput.GetStreamCharSet());
2529 if ( bDone )
2530 aSel = ImpInsertParaBreak( aSel.GetEnd() );
2533 UndoActionEnd();
2535 TextSelection aNewSel( aSel.GetEnd(), aSel.GetEnd() );
2537 // so that FormatAndUpdate does not access the invalid selection
2538 if ( GetActiveView() )
2539 GetActiveView()->ImpSetSelection( aNewSel );
2541 SetUpdateMode( bUpdate );
2542 FormatAndUpdate( GetActiveView() );
2544 return rInput.GetError() ? sal_False : sal_True;
2547 sal_Bool TextEngine::Write( SvStream& rOutput, const TextSelection* pSel, sal_Bool bHTML )
2549 TextSelection aSel;
2550 if ( pSel )
2551 aSel = *pSel;
2552 else
2554 sal_uLong nParas = mpDoc->GetNodes().Count();
2555 TextNode* pNode = mpDoc->GetNodes().GetObject( nParas - 1 );
2556 aSel.GetStart() = TextPaM( 0, 0 );
2557 aSel.GetEnd() = TextPaM( nParas-1, pNode->GetText().Len() );
2560 if ( bHTML )
2562 rOutput.WriteLine( "<HTML>" );
2563 rOutput.WriteLine( "<BODY>" );
2566 for ( sal_uLong nPara = aSel.GetStart().GetPara(); nPara <= aSel.GetEnd().GetPara(); nPara++ )
2568 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2570 sal_uInt16 nStartPos = 0;
2571 sal_uInt16 nEndPos = pNode->GetText().Len();
2572 if ( nPara == aSel.GetStart().GetPara() )
2573 nStartPos = aSel.GetStart().GetIndex();
2574 if ( nPara == aSel.GetEnd().GetPara() )
2575 nEndPos = aSel.GetEnd().GetIndex();
2577 String aText;
2578 if ( !bHTML )
2580 aText = pNode->GetText().Copy( nStartPos, nEndPos-nStartPos );
2582 else
2584 aText.AssignAscii( "<P STYLE=\"margin-bottom: 0cm\">" );
2586 if ( nStartPos == nEndPos )
2588 // Empty lines will be removed by Writer
2589 aText.AppendAscii( "<BR>" );
2591 else
2593 sal_uInt16 nTmpStart = nStartPos;
2594 sal_uInt16 nTmpEnd = nEndPos;
2597 TextCharAttrib* pAttr = pNode->GetCharAttribs().FindNextAttrib( TEXTATTR_HYPERLINK, nTmpStart, nEndPos );
2598 nTmpEnd = pAttr ? pAttr->GetStart() : nEndPos;
2600 // Text before Attribute
2601 aText += pNode->GetText().Copy( nTmpStart, nTmpEnd-nTmpStart );
2603 if ( pAttr )
2605 nTmpEnd = std::min( pAttr->GetEnd(), nEndPos );
2607 // e.g. <A HREF="http://www.mopo.de/">Morgenpost</A>
2608 aText.AppendAscii( "<A HREF=\"" );
2609 aText += ((const TextAttribHyperLink&) pAttr->GetAttr() ).GetURL();
2610 aText.AppendAscii( "\">" );
2611 nTmpStart = pAttr->GetStart();
2612 aText += pNode->GetText().Copy( nTmpStart, nTmpEnd-nTmpStart );
2613 aText.AppendAscii( "</A>" );
2615 nTmpStart = pAttr->GetEnd();
2617 } while ( nTmpEnd < nEndPos );
2620 aText.AppendAscii( "</P>" );
2622 rOutput.WriteLine(OUStringToOString(aText,
2623 rOutput.GetStreamCharSet()));
2626 if ( bHTML )
2628 rOutput.WriteLine( "</BODY>" );
2629 rOutput.WriteLine( "</HTML>" );
2632 return rOutput.GetError() ? sal_False : sal_True;
2635 void TextEngine::RemoveAttribs( sal_uLong nPara, sal_Bool bIdleFormatAndUpdate )
2637 if ( nPara < mpDoc->GetNodes().Count() )
2639 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2640 if ( pNode->GetCharAttribs().Count() )
2642 pNode->GetCharAttribs().Clear( sal_True );
2644 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2645 pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() );
2647 mbFormatted = sal_False;
2649 if ( bIdleFormatAndUpdate )
2650 IdleFormatAndUpdate( NULL, 0xFFFF );
2651 else
2652 FormatAndUpdate( NULL );
2656 void TextEngine::RemoveAttribs( sal_uLong nPara, sal_uInt16 nWhich, sal_Bool bIdleFormatAndUpdate )
2658 if ( nPara < mpDoc->GetNodes().Count() )
2660 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2661 if ( pNode->GetCharAttribs().Count() )
2663 TextCharAttribList& rAttribs = pNode->GetCharAttribs();
2664 sal_uInt16 nAttrCount = rAttribs.Count();
2665 for(sal_uInt16 nAttr = nAttrCount; nAttr; --nAttr)
2667 if(rAttribs.GetAttrib( nAttr - 1 )->Which() == nWhich)
2668 rAttribs.RemoveAttrib( nAttr -1 );
2670 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2671 pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() );
2672 mbFormatted = sal_False;
2673 if(bIdleFormatAndUpdate)
2674 IdleFormatAndUpdate( NULL, 0xFFFF );
2675 else
2676 FormatAndUpdate( NULL );
2680 void TextEngine::RemoveAttrib( sal_uLong nPara, const TextCharAttrib& rAttrib )
2682 if ( nPara < mpDoc->GetNodes().Count() )
2684 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2685 if ( pNode->GetCharAttribs().Count() )
2687 TextCharAttribList& rAttribs = pNode->GetCharAttribs();
2688 sal_uInt16 nAttrCount = rAttribs.Count();
2689 for(sal_uInt16 nAttr = nAttrCount; nAttr; --nAttr)
2691 if(rAttribs.GetAttrib( nAttr - 1 ) == &rAttrib)
2693 rAttribs.RemoveAttrib( nAttr -1 );
2694 break;
2697 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2698 pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() );
2699 mbFormatted = sal_False;
2700 FormatAndUpdate( NULL );
2705 void TextEngine::SetAttrib( const TextAttrib& rAttr, sal_uLong nPara, sal_uInt16 nStart, sal_uInt16 nEnd, sal_Bool bIdleFormatAndUpdate )
2708 // For now do not check if Attributes overlap!
2709 // This function is for TextEditors that want to _quickly_ generate the Syntax-Highlight
2711 // As TextEngine is currently intended only for TextEditors, there is no Undo for Attributes!
2713 if ( nPara < mpDoc->GetNodes().Count() )
2715 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2716 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2718 sal_uInt16 nMax = pNode->GetText().Len();
2719 if ( nStart > nMax )
2720 nStart = nMax;
2721 if ( nEnd > nMax )
2722 nEnd = nMax;
2724 pNode->GetCharAttribs().InsertAttrib( new TextCharAttrib( rAttr, nStart, nEnd ) );
2725 pTEParaPortion->MarkSelectionInvalid( nStart, nEnd );
2727 mbFormatted = sal_False;
2728 if ( bIdleFormatAndUpdate )
2729 IdleFormatAndUpdate( NULL, 0xFFFF );
2730 else
2731 FormatAndUpdate( NULL );
2735 void TextEngine::SetTextAlign( TxtAlign eAlign )
2737 if ( eAlign != meAlign )
2739 meAlign = eAlign;
2740 FormatFullDoc();
2741 UpdateViews();
2746 void TextEngine::ValidateSelection( TextSelection& rSel ) const
2748 ValidatePaM( rSel.GetStart() );
2749 ValidatePaM( rSel.GetEnd() );
2752 void TextEngine::ValidatePaM( TextPaM& rPaM ) const
2754 sal_uLong nMaxPara = mpDoc->GetNodes().Count() - 1;
2755 if ( rPaM.GetPara() > nMaxPara )
2757 rPaM.GetPara() = nMaxPara;
2758 rPaM.GetIndex() = 0xFFFF;
2761 sal_uInt16 nMaxIndex = GetTextLen( rPaM.GetPara() );
2762 if ( rPaM.GetIndex() > nMaxIndex )
2763 rPaM.GetIndex() = nMaxIndex;
2767 // adjust State & Selection
2769 void TextEngine::ImpParagraphInserted( sal_uLong nPara )
2771 // No adjustment needed for the active View;
2772 // but for all passive Views the Selection needs adjusting.
2773 if ( mpViews->size() > 1 )
2775 for ( sal_uInt16 nView = mpViews->size(); nView; )
2777 TextView* pView = (*mpViews)[ --nView ];
2778 if ( pView != GetActiveView() )
2780 for ( int n = 0; n <= 1; n++ )
2782 TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd();
2783 if ( rPaM.GetPara() >= nPara )
2784 rPaM.GetPara()++;
2789 Broadcast( TextHint( TEXT_HINT_PARAINSERTED, nPara ) );
2792 void TextEngine::ImpParagraphRemoved( sal_uLong nPara )
2794 if ( mpViews->size() > 1 )
2796 for ( sal_uInt16 nView = mpViews->size(); nView; )
2798 TextView* pView = (*mpViews)[ --nView ];
2799 if ( pView != GetActiveView() )
2801 sal_uLong nParas = mpDoc->GetNodes().Count();
2802 for ( int n = 0; n <= 1; n++ )
2804 TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd();
2805 if ( rPaM.GetPara() > nPara )
2806 rPaM.GetPara()--;
2807 else if ( rPaM.GetPara() == nPara )
2809 rPaM.GetIndex() = 0;
2810 if ( rPaM.GetPara() >= nParas )
2811 rPaM.GetPara()--;
2817 Broadcast( TextHint( TEXT_HINT_PARAREMOVED, nPara ) );
2820 void TextEngine::ImpCharsRemoved( sal_uLong nPara, sal_uInt16 nPos, sal_uInt16 nChars )
2822 if ( mpViews->size() > 1 )
2824 for ( sal_uInt16 nView = mpViews->size(); nView; )
2826 TextView* pView = (*mpViews)[ --nView ];
2827 if ( pView != GetActiveView() )
2829 sal_uInt16 nEnd = nPos+nChars;
2830 for ( int n = 0; n <= 1; n++ )
2832 TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd();
2833 if ( rPaM.GetPara() == nPara )
2835 if ( rPaM.GetIndex() > nEnd )
2836 rPaM.GetIndex() = rPaM.GetIndex() - nChars;
2837 else if ( rPaM.GetIndex() > nPos )
2838 rPaM.GetIndex() = nPos;
2844 Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, nPara ) );
2847 void TextEngine::ImpCharsInserted( sal_uLong nPara, sal_uInt16 nPos, sal_uInt16 nChars )
2849 if ( mpViews->size() > 1 )
2851 for ( sal_uInt16 nView = mpViews->size(); nView; )
2853 TextView* pView = (*mpViews)[ --nView ];
2854 if ( pView != GetActiveView() )
2856 for ( int n = 0; n <= 1; n++ )
2858 TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd();
2859 if ( rPaM.GetPara() == nPara )
2861 if ( rPaM.GetIndex() >= nPos )
2862 rPaM.GetIndex() = rPaM.GetIndex() + nChars;
2868 Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, nPara ) );
2871 void TextEngine::ImpFormattingParagraph( sal_uLong nPara )
2873 Broadcast( TextHint( TEXT_HINT_FORMATPARA, nPara ) );
2876 void TextEngine::ImpTextHeightChanged()
2878 Broadcast( TextHint( TEXT_HINT_TEXTHEIGHTCHANGED ) );
2881 void TextEngine::ImpTextFormatted()
2883 Broadcast( TextHint( TEXT_HINT_TEXTFORMATTED ) );
2886 void TextEngine::Draw( OutputDevice* pDev, const Point& rPos )
2888 ImpPaint( pDev, rPos, NULL );
2891 void TextEngine::SetLeftMargin( sal_uInt16 n )
2893 mpDoc->SetLeftMargin( n );
2896 sal_uInt16 TextEngine::GetLeftMargin() const
2898 return mpDoc->GetLeftMargin();
2901 uno::Reference< i18n::XBreakIterator > TextEngine::GetBreakIterator()
2903 if ( !mxBreakIterator.is() )
2904 mxBreakIterator = vcl::unohelper::CreateBreakIterator();
2905 DBG_ASSERT( mxBreakIterator.is(), "BreakIterator: Failed to create!" );
2906 return mxBreakIterator;
2909 void TextEngine::SetLocale( const ::com::sun::star::lang::Locale& rLocale )
2911 maLocale = rLocale;
2912 delete mpLocaleDataWrapper;
2913 mpLocaleDataWrapper = NULL;
2916 ::com::sun::star::lang::Locale TextEngine::GetLocale()
2918 if ( maLocale.Language.isEmpty() )
2920 maLocale = Application::GetSettings().GetUILanguageTag().getLocale(); // TODO: why UI locale?
2922 return maLocale;
2925 LocaleDataWrapper* TextEngine::ImpGetLocaleDataWrapper()
2927 if ( !mpLocaleDataWrapper )
2928 mpLocaleDataWrapper = new LocaleDataWrapper( LanguageTag( GetLocale()) );
2930 return mpLocaleDataWrapper;
2933 void TextEngine::SetRightToLeft( sal_Bool bR2L )
2935 if ( mbRightToLeft != bR2L )
2937 mbRightToLeft = bR2L;
2938 meAlign = bR2L ? TXTALIGN_RIGHT : TXTALIGN_LEFT;
2939 FormatFullDoc();
2940 UpdateViews();
2944 void TextEngine::ImpInitWritingDirections( sal_uLong nPara )
2946 TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
2947 std::vector<TEWritingDirectionInfo>& rInfos = pParaPortion->GetWritingDirectionInfos();
2948 rInfos.clear();
2950 if ( pParaPortion->GetNode()->GetText().Len() )
2952 const UBiDiLevel nBidiLevel = IsRightToLeft() ? 1 /*RTL*/ : 0 /*LTR*/;
2953 String aText( pParaPortion->GetNode()->GetText() );
2956 // Bidi functions from icu 2.0
2958 UErrorCode nError = U_ZERO_ERROR;
2959 UBiDi* pBidi = ubidi_openSized( aText.Len(), 0, &nError );
2960 nError = U_ZERO_ERROR;
2962 ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.GetBuffer()), aText.Len(), nBidiLevel, NULL, &nError ); // UChar != sal_Unicode in MinGW
2963 nError = U_ZERO_ERROR;
2965 long nCount = ubidi_countRuns( pBidi, &nError );
2967 int32_t nStart = 0;
2968 int32_t nEnd;
2969 UBiDiLevel nCurrDir;
2971 for ( sal_uInt16 nIdx = 0; nIdx < nCount; ++nIdx )
2973 ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir );
2974 rInfos.push_back( TEWritingDirectionInfo( nCurrDir, (sal_uInt16)nStart, (sal_uInt16)nEnd ) );
2975 nStart = nEnd;
2978 ubidi_close( pBidi );
2981 // No infos mean no CTL and default dir is L2R...
2982 if ( rInfos.empty() )
2983 rInfos.push_back( TEWritingDirectionInfo( 0, 0, (sal_uInt16)pParaPortion->GetNode()->GetText().Len() ) );
2987 sal_uInt8 TextEngine::ImpGetRightToLeft( sal_uLong nPara, sal_uInt16 nPos, sal_uInt16* pStart, sal_uInt16* pEnd )
2989 sal_uInt8 nRightToLeft = 0;
2991 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2992 if ( pNode && pNode->GetText().Len() )
2994 TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
2995 if ( pParaPortion->GetWritingDirectionInfos().empty() )
2996 ImpInitWritingDirections( nPara );
2998 std::vector<TEWritingDirectionInfo>& rDirInfos = pParaPortion->GetWritingDirectionInfos();
2999 for ( std::vector<TEWritingDirectionInfo>::const_iterator rDirInfosIt = rDirInfos.begin(); rDirInfosIt != rDirInfos.end(); ++rDirInfosIt )
3001 if ( ( (*rDirInfosIt).nStartPos <= nPos ) && ( (*rDirInfosIt).nEndPos >= nPos ) )
3003 nRightToLeft = (*rDirInfosIt).nType;
3004 if ( pStart )
3005 *pStart = (*rDirInfosIt).nStartPos;
3006 if ( pEnd )
3007 *pEnd = (*rDirInfosIt).nEndPos;
3008 break;
3012 return nRightToLeft;
3015 long TextEngine::ImpGetPortionXOffset( sal_uLong nPara, TextLine* pLine, sal_uInt16 nTextPortion )
3017 long nX = pLine->GetStartX();
3019 TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
3021 for ( sal_uInt16 i = pLine->GetStartPortion(); i < nTextPortion; i++ )
3023 TETextPortion* pPortion = pParaPortion->GetTextPortions()[ i ];
3024 nX += pPortion->GetWidth();
3027 TETextPortion* pDestPortion = pParaPortion->GetTextPortions()[ nTextPortion ];
3028 if ( pDestPortion->GetKind() != PORTIONKIND_TAB )
3030 if ( !IsRightToLeft() && pDestPortion->GetRightToLeft() )
3032 // Portions behind must be added, visual before this portion
3033 sal_uInt16 nTmpPortion = nTextPortion+1;
3034 while ( nTmpPortion <= pLine->GetEndPortion() )
3036 TETextPortion* pNextTextPortion = pParaPortion->GetTextPortions()[ nTmpPortion ];
3037 if ( pNextTextPortion->GetRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
3038 nX += pNextTextPortion->GetWidth();
3039 else
3040 break;
3041 nTmpPortion++;
3043 // Portions before must be removed, visual behind this portion
3044 nTmpPortion = nTextPortion;
3045 while ( nTmpPortion > pLine->GetStartPortion() )
3047 --nTmpPortion;
3048 TETextPortion* pPrevTextPortion = pParaPortion->GetTextPortions()[ nTmpPortion ];
3049 if ( pPrevTextPortion->GetRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
3050 nX -= pPrevTextPortion->GetWidth();
3051 else
3052 break;
3055 else if ( IsRightToLeft() && !pDestPortion->IsRightToLeft() )
3057 // Portions behind must be removed, visual behind this portion
3058 sal_uInt16 nTmpPortion = nTextPortion+1;
3059 while ( nTmpPortion <= pLine->GetEndPortion() )
3061 TETextPortion* pNextTextPortion = pParaPortion->GetTextPortions()[ nTmpPortion ];
3062 if ( !pNextTextPortion->IsRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
3063 nX += pNextTextPortion->GetWidth();
3064 else
3065 break;
3066 nTmpPortion++;
3068 // Portions before must be added, visual before this portion
3069 nTmpPortion = nTextPortion;
3070 while ( nTmpPortion > pLine->GetStartPortion() )
3072 --nTmpPortion;
3073 TETextPortion* pPrevTextPortion = pParaPortion->GetTextPortions()[ nTmpPortion ];
3074 if ( !pPrevTextPortion->IsRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
3075 nX -= pPrevTextPortion->GetWidth();
3076 else
3077 break;
3082 return nX;
3085 void TextEngine::ImpInitLayoutMode( OutputDevice* pOutDev, sal_Bool bDrawingR2LPortion )
3087 sal_uLong nLayoutMode = pOutDev->GetLayoutMode();
3089 nLayoutMode &= ~(TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_COMPLEX_DISABLED | TEXT_LAYOUT_BIDI_STRONG );
3090 if ( bDrawingR2LPortion )
3091 nLayoutMode |= TEXT_LAYOUT_BIDI_RTL;
3093 pOutDev->SetLayoutMode( nLayoutMode );
3096 TxtAlign TextEngine::ImpGetAlign() const
3098 TxtAlign eAlign = meAlign;
3099 if ( IsRightToLeft() )
3101 if ( eAlign == TXTALIGN_LEFT )
3102 eAlign = TXTALIGN_RIGHT;
3103 else if ( eAlign == TXTALIGN_RIGHT )
3104 eAlign = TXTALIGN_LEFT;
3106 return eAlign;
3109 long TextEngine::ImpGetOutputOffset( sal_uLong nPara, TextLine* pLine, sal_uInt16 nIndex, sal_uInt16 nIndex2 )
3111 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
3113 sal_uInt16 nPortionStart;
3114 sal_uInt16 nPortion = pPortion->GetTextPortions().FindPortion( nIndex, nPortionStart, sal_True );
3116 TETextPortion* pTextPortion = pPortion->GetTextPortions()[ nPortion ];
3118 long nX;
3120 if ( ( nIndex == nPortionStart ) && ( nIndex == nIndex2 ) )
3122 // Output of full portion, so we need portion x offset.
3123 // Use ImpGetPortionXOffset, because GetXPos may deliver left or right position from portioon, depending on R2L, L2R
3124 nX = ImpGetPortionXOffset( nPara, pLine, nPortion );
3125 if ( IsRightToLeft() )
3127 nX = -nX -pTextPortion->GetWidth();
3130 else
3132 nX = ImpGetXPos( nPara, pLine, nIndex, nIndex == nPortionStart );
3133 if ( nIndex2 != nIndex )
3135 long nX2 = ImpGetXPos( nPara, pLine, nIndex2, sal_False );
3136 if ( ( !IsRightToLeft() && ( nX2 < nX ) ) ||
3137 ( IsRightToLeft() && ( nX2 > nX ) ) )
3139 nX = nX2;
3142 if ( IsRightToLeft() )
3144 nX = -nX;
3148 return nX;
3151 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */