LCOV - code coverage report
Current view: top level - usr/local/src/libreoffice/vcl/source/edit - texteng.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 714 1676 42.6 %
Date: 2013-07-09 Functions: 59 119 49.6 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10