LCOV - code coverage report
Current view: top level - vcl/source/edit - texteng.cxx (source / functions) Hit Total Coverage
Test: commit 10e77ab3ff6f4314137acd6e2702a6e5c1ce1fae Lines: 711 1670 42.6 %
Date: 2014-11-03 Functions: 58 117 49.6 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10