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

Generated by: LCOV version 1.10