1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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()
69 mpViews
= new TextViews
;
72 mbIsFormatting
= sal_False
;
73 mbFormatted
= sal_False
;
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
;
86 mnCurTextWidth
= 0xFFFFFFFF;
91 mpLocaleDataWrapper
= NULL
;
93 mpIdleFormatter
= new IdleFormatter
;
94 mpIdleFormatter
->SetTimeoutHdl( LINK( this, TextEngine
, IdleFormatHdl
) );
96 mpRefDev
= new VirtualDevice
;
98 ImpInitLayoutMode( mpRefDev
);
102 maTextColor
= COL_BLACK
;
104 aFont
.SetTransparent( sal_False
);
105 Color
aFillColor( aFont
.GetFillColor() );
106 aFillColor
.SetTransparency( 0 );
107 aFont
.SetFillColor( aFillColor
);
111 TextEngine::~TextEngine()
113 mbDowning
= sal_True
;
115 delete mpIdleFormatter
;
117 delete mpTEParaPortions
;
118 delete mpViews
; // only the list, not the Views
120 delete mpUndoManager
;
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() )
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
161 void TextEngine::SetActiveView( TextView
* pTextView
)
163 if ( pTextView
!= mpActiveView
)
166 mpActiveView
->HideSelection();
168 mpActiveView
= pTextView
;
171 mpActiveView
->ShowSelection();
175 void TextEngine::SetFont( const Font
& rFont
)
177 if ( rFont
!= maFont
)
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
;
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
);
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();
208 mnCharHeight
= (sal_uInt16
)aTextSize
.Height();
209 mnFixCharWidth100
= 0;
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
)
227 void TextEngine::SetMaxTextWidth( sal_uLong nMaxWidth
)
229 if ( nMaxWidth
!= mnMaxTextWidth
)
231 mnMaxTextWidth
= std::min( nMaxWidth
, (sal_uLong
)0x7FFFFFFF );
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
;
247 case LINEEND_LF
: pRet
= static_aLFText
;break;
248 case LINEEND_CR
: pRet
= static_aCRText
;break;
249 case LINEEND_CRLF
: pRet
= static_aCRLFText
;break;
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
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
) ) )
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
);
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
)
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
)
334 case KEYFUNC_PASTE
: bDoesChange
= sal_True
;
336 default: // might get handled below
337 eFunc
= KEYFUNC_DONTKNOW
;
340 if ( eFunc
== KEYFUNC_DONTKNOW
)
342 switch ( rKeyEvent
.GetKeyCode().GetCode() )
347 if ( !rKeyEvent
.GetKeyCode().IsMod2() )
348 bDoesChange
= sal_True
;
354 if ( !rKeyEvent
.GetKeyCode().IsMod1() && !rKeyEvent
.GetKeyCode().IsMod2() )
355 bDoesChange
= sal_True
;
360 bDoesChange
= TextEngine::IsSimpleCharInput( rKeyEvent
);
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
378 void TextEngine::ImpInitDoc()
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
404 if ( !rSel
.HasRange() )
407 TextSelection
aSel( rSel
);
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
)
431 void TextEngine::ImpRemoveText()
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
);
445 void TextEngine::SetText( const OUString
& rText
)
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() )
470 if( rText
.isEmpty() ) // otherwise needs invalidation later; !bFormatted is sufficient
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
) )
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()
543 TextPaM
TextEngine::ImpDeleteText( const TextSelection
& rSel
)
545 if ( !rSel
.HasRange() )
546 return rSel
.GetStart();
548 TextSelection
aSel( rSel
);
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();
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();
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() );
596 aStartPaM
= ImpConnectParagraphs( nStartNode
, nEndNode
);
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();
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
) );
625 mpTEParaPortions
->Remove( nPara
);
628 ImpParagraphRemoved( nPara
);
631 uno::Reference
< i18n::XExtendedInputSequenceChecker
> TextEngine::GetInputSequenceChecker() const
633 uno::Reference
< i18n::XExtendedInputSequenceChecker
> xISC
;
636 uno::Reference
< uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
637 xISC
= i18n::InputSequenceChecker::create(xContext
);
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
)
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
);
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
;
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
] )
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
);
732 // ImpInsertText implicitly handles undo...
733 return ImpInsertText( aSel
, aChgText
);
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 );
759 pPortion
->SetNotSimpleInvalid();
760 aPaM
= mpDoc
->InsertText( aPaM
, c
);
761 ImpCharsInserted( aPaM
.GetPara(), aPaM
.GetIndex()-1, 1 );
773 TextPaM
TextEngine::ImpInsertText( const TextSelection
& rCurSel
, const XubString
& rStr
)
779 if ( rCurSel
.HasRange() )
780 aPaM
= ImpDeleteText( rCurSel
);
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
796 sal_uLong nL
= aPaM
.GetIndex();
797 nL
+= ( nEnd
-nStart
);
798 if ( nL
> STRING_MAXLEN
)
800 sal_uInt16 nDiff
= (sal_uInt16
) (nL
-STRING_MAXLEN
);
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
);
822 if ( nStart
< nEnd
) // #108611# overflow
832 TextPaM
TextEngine::ImpInsertParaBreak( const TextSelection
& rCurSel
, sal_Bool bKeepEndingAttribs
)
835 if ( rCurSel
.HasRange() )
836 aPaM
= ImpDeleteText( rCurSel
);
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
865 if ( bFirstParaContentChanged
)
866 Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED
, rPaM
.GetPara() ) );
871 Rectangle
TextEngine::PaMtoEditCursor( const TextPaM
& rPaM
, sal_Bool bSpecial
)
873 DBG_ASSERT( GetUpdateMode(), "PaMtoEditCursor: GetUpdateMode()" );
875 Rectangle aEditCursor
;
878 if ( !mbHasMultiLineParas
)
880 nY
= rPaM
.GetPara() * mnCharHeight
;
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
;
897 Rectangle
TextEngine::GetEditCursor( const TextPaM
& rPaM
, sal_Bool bSpecial
, sal_Bool bPreferPortionStart
)
899 if ( !IsFormatted() && !IsFormatting() )
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
914 sal_uInt16 nCurIndex
= 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
) ) )
925 nCurIndex
= nCurIndex
+ pTmpLine
->GetLen();
930 // Cursor at end of paragraph
931 DBG_ASSERT( rPaM
.GetIndex() == nCurIndex
, "GetEditCursor: Bad Index!" );
933 pLine
= pPortion
->GetLines().back();
935 nCurIndex
= nCurIndex
- pLine
->GetLen();
938 Rectangle aEditCursor
;
940 aEditCursor
.Top() = nY
;
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
;
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() ) )
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
;
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
;
1031 const TextAttrib
* TextEngine::FindAttrib( const TextPaM
& rPaM
, sal_uInt16 nWhich
) const
1033 const TextAttrib
* pAttr
= NULL
;
1034 const TextCharAttrib
* pCharAttr
= FindCharAttrib( rPaM
, nWhich
);
1036 pAttr
= &pCharAttr
->GetAttr();
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() );
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
);
1060 TextPaM
TextEngine::GetPaM( const Point
& rDocPos
, sal_Bool bSmart
)
1062 DBG_ASSERT( GetUpdateMode(), "GetPaM: GetUpdateMode()" );
1065 for ( sal_uLong nPortion
= 0; nPortion
< mpTEParaPortions
->Count(); nPortion
++ )
1067 TEParaPortion
* pPortion
= mpTEParaPortions
->GetObject( nPortion
);
1068 long nTmpHeight
= pPortion
->GetLines().size() * mnCharHeight
;
1070 if ( nY
> rDocPos
.Y() )
1073 Point
aPosInPara( rDocPos
);
1074 aPosInPara
.Y() -= nY
;
1076 TextPaM
aPaM( nPortion
, 0 );
1077 aPaM
.GetIndex() = ImpFindIndex( nPortion
, aPosInPara
, bSmart
);
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;
1096 TextLine
* pLine
= 0;
1098 for ( nLine
= 0; nLine
< pPortion
->GetLines().size(); nLine
++ )
1100 TextLine
* pTmpLine
= pPortion
->GetLines()[ nLine
];
1102 if ( nY
> rPosInPara
.Y() ) // that's it
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
);
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
)
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
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...
1156 nCurIndex
= nCurIndex
+ pTextPortion
->GetLen();
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
;
1201 sal_uLong
TextEngine::CalcTextWidth()
1203 if ( !IsFormatted() && !IsFormatting() )
1206 if ( mnCurTextWidth
== 0xFFFFFFFF )
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()" );
1224 for ( sal_uLong nPortion
= mpTEParaPortions
->Count(); nPortion
; )
1225 nY
+= CalcParaHeight( --nPortion
);
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!" );
1235 if ( mnFixCharWidth100
)
1237 nWidth
= (sal_uLong
)nLen
*mnFixCharWidth100
/100;
1243 if ( !mpRefDev
->GetFont().IsSameInstance( *pFont
) )
1244 mpRefDev
->SetFont( *pFont
);
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
);
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
);
1265 return pPPortion
->GetLines().size();
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();
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" );
1291 nHeight
= pPPortion
->GetLines().size() * mnCharHeight
;
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;
1306 for ( nLine
= 0; nLine
< nLines
; nLine
++ )
1308 TextLine
* pL
= pTEParaPortion
->GetLines()[ nLine
];
1309 if ( pL
->IsInvalid() )
1311 nFirstInvalid
= nLine
;
1316 for ( nLastInvalid
= nFirstInvalid
; nLastInvalid
< nLines
; nLastInvalid
++ )
1318 TextLine
* pL
= pTEParaPortion
->GetLines()[ nLine
];
1319 if ( pL
->IsValid() )
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() )
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() )
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
)
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" );
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
)
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
)
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
);
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
);
1465 // pOut->SetTextLineColor( Color( COL_LIGHTGRAY ) );
1470 void TextEngine::FormatAndUpdate( TextView
* pCurView
)
1476 IdleFormatAndUpdate( pCurView
);
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() )
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() );
1523 pView
->GetWindow()->Invalidate( aClipRect
);
1529 pCurView
->ShowCursor( pCurView
->IsAutoScroll() );
1532 maInvalidRect
= Rectangle();
1535 IMPL_LINK_NOARG(TextEngine
, IdleFormatHdl
)
1537 FormatAndUpdate( mpIdleFormatter
->GetView() );
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
;
1557 void TextEngine::FormatDoc()
1559 if ( IsFormatted() || !GetUpdateMode() || IsFormatting() )
1562 mbIsFormatting
= sal_True
;
1563 mbHasMultiLineParas
= sal_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
) )
1583 // set InvalidRect only once
1584 if ( maInvalidRect
.IsEmpty() )
1586 // otherwise remains Empty() for Paperwidth 0 (AutoPageSize)
1587 long nWidth
= (long)mnMaxTextWidth
;
1589 nWidth
= 0x7FFFFFFF;
1590 Range
aInvRange( GetInvalidYOffsets( nPara
) );
1591 maInvalidRect
= Rectangle( Point( 0, nY
+aInvRange
.Min() ),
1592 Size( nWidth
, aInvRange
.Len() ) );
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;
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
;
1636 mbFormatted
= sal_True
;
1637 ImpTextHeightChanged();
1641 mbIsFormatting
= sal_False
;
1642 mbFormatted
= sal_True
;
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
);
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
);
1672 // -2: The new one is already inserted.
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
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
;
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;
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();
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!
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;
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
1888 pTEParaPortion
->GetTextPortions()[nNewPortionPos
]->GetLen();
1893 TETextPortion
* pNewPortion
= new TETextPortion( nNewChars
);
1894 pTEParaPortion
->GetTextPortions().insert( pTEParaPortion
->GetTextPortions().begin() + nNewPortionPos
, pNewPortion
);
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);
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!" );
1929 nPos
= nPos
+ pTP
->GetLen();
1931 DBG_ASSERT( pTP
, "RecalcTextPortion: Portion not found!" );
1932 if ( ( nPos
== nStartPos
) && ( (nPos
+pTP
->GetLen()) == nEnd
) )
1935 pTEParaPortion
->GetTextPortions().erase( pTEParaPortion
->GetTextPortions().begin() + nPortion
);
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() )
1953 if ( !IsFormatted() )
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() )
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
:
2024 SeekCursor( nPara
, nIndex
+1, aFont
, pOutDev
);
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
;
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();
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
) )
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
;
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
);
2097 aPos
.X() = rStartPos
.X() + ImpGetOutputOffset( nPara
, pLine
, nTmpIndex
, nEnd
);
2098 pOutDev
->DrawText( aPos
, pPortion
->GetNode()->GetText(), nTmpIndex
, nEnd
-nTmpIndex
);
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 ) );
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
);
2127 pOutDev
->Erase( aTabArea
);
2132 default: OSL_FAIL( "ImpPaint: Unknown Portion-Type !" );
2136 nIndex
= nIndex
+ pTextPortion
->GetLen();
2142 if ( pPaintArea
&& ( nY
>= pPaintArea
->Bottom() ) )
2143 break; // no more visible actions
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())
2175 pTEParaPortion
->GetLines().clear();
2177 CreateAndInsertEmptyLine( nPara
);
2178 pTEParaPortion
->SetValid();
2179 return nOldLineCount
!= pTEParaPortion
->GetLines().size();
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;
2226 RecalcTextPortion( nPara
, nInvalidStart
, nInvalidDiff
);
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
)
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 ) ) )
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
);
2260 bool bCalcPortion
= true;
2262 while ( nIndex
< pNode
->GetText().Len() )
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;
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() ) )
2295 pPortion
->GetWidth() = nXWidth
-1;
2296 nTmpWidth
= pPortion
->GetWidth();
2300 pPortion
->GetKind() = PORTIONKIND_TAB
;
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
;
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
;
2328 nTmpWidth
-= pPortion
->GetWidth();
2329 if ( pPortion
->GetKind() == PORTIONKIND_TAB
)
2338 pLine
->SetEnd( nPortionEnd
);
2339 OSL_ENSURE(pTEParaPortion
->GetTextPortions().size(),
2340 "CreateLines: No TextPortions?");
2341 pLine
->SetEndPortion( (sal_uInt16
)pTEParaPortion
->GetTextPortions().size() - 1 );
2346 pLine
->SetEnd( nPortionStart
);
2347 pLine
->SetEndPortion( nTmpPortion
-1 );
2349 else if ( bLineBreak
|| bBrokenLine
)
2351 pLine
->SetEnd( nPortionStart
+1 );
2352 pLine
->SetEndPortion( nTmpPortion
-1 );
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
) )
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
;
2373 if ( ImpGetAlign() == TXTALIGN_CENTER
)
2374 pLine
->SetStartX( (sal_uInt16
)(nSpace
/ 2) );
2375 else // TXTALIGN_RIGHT
2376 pLine
->SetStartX( (sal_uInt16
)nSpace
);
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
)
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() ) )
2411 if ( bCalcPortion
&& bQuickFormat
)
2413 bCalcPortion
= false;
2414 pTEParaPortion
->CorrectValuesBehindLastFormattedLine( nLine
);
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
);
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
2441 if ( nLine
< pTEParaPortion
->GetLines().size()-1 )
2442 pLine
= pTEParaPortion
->GetLines()[ ++nLine
];
2443 if ( pLine
&& ( nIndex
>= pNode
->GetText().Len() ) )
2445 nDelFromLine
= nLine
;
2448 if ( !pLine
&& ( nIndex
< pNode
->GetText().Len() ) )
2450 pLine
= new TextLine
;
2451 pTEParaPortion
->GetLines().insert( pTEParaPortion
->GetLines().begin() + ++nLine
, 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
)
2470 pTEParaPortion
->GetLines().erase( pTEParaPortion
->GetLines().begin() + nDelFromLine
,
2471 pTEParaPortion
->GetLines().end() );
2474 DBG_ASSERT( pTEParaPortion
->GetLines().size(), "CreateLines: No Line!" );
2477 CreateAndInsertEmptyLine( nPara
);
2479 pTEParaPortion
->SetValid();
2481 return nOldLineCount
!= pTEParaPortion
->GetLines().size();
2484 String
TextEngine::GetWord( const TextPaM
& rCursorPos
, TextPaM
* pStartOfWord
)
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() );
2497 *pStartOfWord
= aSel
.GetStart();
2502 sal_Bool
TextEngine::Read( SvStream
& rInput
, const TextSelection
* pSel
)
2504 sal_Bool bUpdate
= GetUpdateMode();
2505 SetUpdateMode( sal_False
);
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
);
2522 sal_Bool bDone
= rInput
.ReadLine( aLine
);
2523 OUString
aTmpStr(OStringToOUString(aLine
, rInput
.GetStreamCharSet()));
2526 aSel
= ImpInsertText( aSel
, aTmpStr
);
2527 bDone
= rInput
.ReadLine( aLine
);
2528 aTmpStr
= OStringToOUString(aLine
, rInput
.GetStreamCharSet());
2530 aSel
= ImpInsertParaBreak( aSel
.GetEnd() );
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
)
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() );
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();
2580 aText
= pNode
->GetText().Copy( nStartPos
, nEndPos
-nStartPos
);
2584 aText
.AssignAscii( "<P STYLE=\"margin-bottom: 0cm\">" );
2586 if ( nStartPos
== nEndPos
)
2588 // Empty lines will be removed by Writer
2589 aText
.AppendAscii( "<BR>" );
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
);
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()));
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 );
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 );
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 );
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
)
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 );
2731 FormatAndUpdate( NULL
);
2735 void TextEngine::SetTextAlign( TxtAlign eAlign
)
2737 if ( eAlign
!= meAlign
)
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
)
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
)
2807 else if ( rPaM
.GetPara() == nPara
)
2809 rPaM
.GetIndex() = 0;
2810 if ( rPaM
.GetPara() >= nParas
)
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
)
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?
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
;
2944 void TextEngine::ImpInitWritingDirections( sal_uLong nPara
)
2946 TEParaPortion
* pParaPortion
= mpTEParaPortions
->GetObject( nPara
);
2947 std::vector
<TEWritingDirectionInfo
>& rInfos
= pParaPortion
->GetWritingDirectionInfos();
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
);
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
) );
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
;
3005 *pStart
= (*rDirInfosIt
).nStartPos
;
3007 *pEnd
= (*rDirInfosIt
).nEndPos
;
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();
3043 // Portions before must be removed, visual behind this portion
3044 nTmpPortion
= nTextPortion
;
3045 while ( nTmpPortion
> pLine
->GetStartPortion() )
3048 TETextPortion
* pPrevTextPortion
= pParaPortion
->GetTextPortions()[ nTmpPortion
];
3049 if ( pPrevTextPortion
->GetRightToLeft() && ( pPrevTextPortion
->GetKind() != PORTIONKIND_TAB
) )
3050 nX
-= pPrevTextPortion
->GetWidth();
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();
3068 // Portions before must be added, visual before this portion
3069 nTmpPortion
= nTextPortion
;
3070 while ( nTmpPortion
> pLine
->GetStartPortion() )
3073 TETextPortion
* pPrevTextPortion
= pParaPortion
->GetTextPortions()[ nTmpPortion
];
3074 if ( !pPrevTextPortion
->IsRightToLeft() && ( pPrevTextPortion
->GetKind() != PORTIONKIND_TAB
) )
3075 nX
-= pPrevTextPortion
->GetWidth();
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
;
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
];
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();
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
) ) )
3142 if ( IsRightToLeft() )
3151 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */