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

Generated by: LCOV version 1.11