LCOV - code coverage report
Current view: top level - vcl/source/edit - texteng.cxx (source / functions) Hit Total Coverage
Test: commit e02a6cb2c3e2b23b203b422e4e0680877f232636 Lines: 0 1670 0.0 %
Date: 2014-04-14 Functions: 0 116 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10