LCOV - code coverage report
Current view: top level - sw/source/core/txtnode - txtedt.cxx (source / functions) Hit Total Coverage
Test: commit 0e63ca4fde4e446f346e35849c756a30ca294aab Lines: 620 1055 58.8 %
Date: 2014-04-11 Functions: 42 50 84.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 <hintids.hxx>
      21             : #include <vcl/svapp.hxx>
      22             : #include <svl/itemiter.hxx>
      23             : #include <editeng/splwrap.hxx>
      24             : #include <editeng/langitem.hxx>
      25             : #include <editeng/fontitem.hxx>
      26             : #include <editeng/scripttypeitem.hxx>
      27             : #include <editeng/hangulhanja.hxx>
      28             : #include <SwSmartTagMgr.hxx>
      29             : #include <linguistic/lngprops.hxx>
      30             : #include <officecfg/Office/Writer.hxx>
      31             : #include <unotools/transliterationwrapper.hxx>
      32             : #include <unotools/charclass.hxx>
      33             : #include <dlelstnr.hxx>
      34             : #include <swmodule.hxx>
      35             : #include <splargs.hxx>
      36             : #include <viewopt.hxx>
      37             : #include <acmplwrd.hxx>
      38             : #include <doc.hxx>
      39             : #include <docsh.hxx>
      40             : #include <txtfld.hxx>
      41             : #include <fmtfld.hxx>
      42             : #include <txatbase.hxx>
      43             : #include <charatr.hxx>
      44             : #include <fldbas.hxx>
      45             : #include <pam.hxx>
      46             : #include <hints.hxx>
      47             : #include <ndtxt.hxx>
      48             : #include <txtfrm.hxx>
      49             : #include <SwGrammarMarkUp.hxx>
      50             : 
      51             : #include <txttypes.hxx>
      52             : #include <breakit.hxx>
      53             : #include <crstate.hxx>
      54             : #include <UndoOverwrite.hxx>
      55             : #include <txatritr.hxx>
      56             : #include <redline.hxx>
      57             : #include <docary.hxx>
      58             : #include <scriptinfo.hxx>
      59             : #include <docstat.hxx>
      60             : #include <editsh.hxx>
      61             : #include <unotextmarkup.hxx>
      62             : #include <txtatr.hxx>
      63             : #include <fmtautofmt.hxx>
      64             : #include <istyleaccess.hxx>
      65             : #include <unicode/uchar.h>
      66             : 
      67             : #include <unomid.h>
      68             : 
      69             : #include <com/sun/star/beans/XPropertySet.hpp>
      70             : #include <com/sun/star/i18n/WordType.hpp>
      71             : #include <com/sun/star/i18n/ScriptType.hpp>
      72             : #include <com/sun/star/i18n/TransliterationModules.hpp>
      73             : #include <com/sun/star/i18n/TransliterationModulesExtra.hpp>
      74             : 
      75             : #include <vector>
      76             : #include <utility>
      77             : 
      78             : #include <unotextrange.hxx>
      79             : 
      80             : using namespace ::com::sun::star;
      81             : using namespace ::com::sun::star::frame;
      82             : using namespace ::com::sun::star::i18n;
      83             : using namespace ::com::sun::star::beans;
      84             : using namespace ::com::sun::star::uno;
      85             : using namespace ::com::sun::star::linguistic2;
      86             : using namespace ::com::sun::star::smarttags;
      87             : 
      88             : // Wir ersparen uns in Hyphenate ein GetFrm()
      89             : // Achtung: in edlingu.cxx stehen die Variablen!
      90             : extern const SwTxtNode *pLinguNode;
      91             : extern       SwTxtFrm  *pLinguFrm;
      92             : 
      93             : /*
      94             :  * This has basically the same function as SwScriptInfo::MaskHiddenRanges,
      95             :  * only for deleted redlines
      96             :  */
      97             : 
      98             : static sal_uInt16
      99        1808 : lcl_MaskRedlines( const SwTxtNode& rNode, OUStringBuffer& rText,
     100             :                          sal_Int32 nStt, sal_Int32 nEnd,
     101             :                          const sal_Unicode cChar )
     102             : {
     103        1808 :     sal_uInt16 nNumOfMaskedRedlines = 0;
     104             : 
     105        1808 :     const SwDoc& rDoc = *rNode.GetDoc();
     106        1808 :     sal_uInt16 nAct = rDoc.GetRedlinePos( rNode, USHRT_MAX );
     107             : 
     108        1808 :     for ( ; nAct < rDoc.GetRedlineTbl().size(); nAct++ )
     109             :     {
     110           0 :         const SwRangeRedline* pRed = rDoc.GetRedlineTbl()[ nAct ];
     111             : 
     112           0 :         if ( pRed->Start()->nNode > rNode.GetIndex() )
     113           0 :             break;
     114             : 
     115           0 :         if( nsRedlineType_t::REDLINE_DELETE == pRed->GetType() )
     116             :         {
     117             :             sal_Int32 nRedlineEnd;
     118             :             sal_Int32 nRedlineStart;
     119             : 
     120           0 :             pRed->CalcStartEnd( rNode.GetIndex(), nRedlineStart, nRedlineEnd );
     121             : 
     122           0 :             if ( nRedlineEnd < nStt || nRedlineStart > nEnd )
     123           0 :                 continue;
     124             : 
     125           0 :             while ( nRedlineStart < nRedlineEnd && nRedlineStart < nEnd )
     126             :             {
     127           0 :                 if ( nRedlineStart >= nStt && nRedlineStart < nEnd )
     128             :                 {
     129           0 :                     rText[nRedlineStart] = cChar;
     130           0 :                     ++nNumOfMaskedRedlines;
     131             :                 }
     132           0 :                 ++nRedlineStart;
     133             :             }
     134             :         }
     135             :     }
     136             : 
     137        1808 :     return nNumOfMaskedRedlines;
     138             : }
     139             : 
     140             : /**
     141             :  * Used for spell checking. Deleted redlines and hidden characters are masked
     142             :  */
     143             : static sal_uInt16
     144        1808 : lcl_MaskRedlinesAndHiddenText( const SwTxtNode& rNode, OUStringBuffer& rText,
     145             :                                       sal_Int32 nStt, sal_Int32 nEnd,
     146             :                                       const sal_Unicode cChar = CH_TXTATR_INWORD,
     147             :                                       bool bCheckShowHiddenChar = true )
     148             : {
     149        1808 :     sal_uInt16 nRedlinesMasked = 0;
     150        1808 :     sal_uInt16 nHiddenCharsMasked = 0;
     151             : 
     152        1808 :     const SwDoc& rDoc = *rNode.GetDoc();
     153        1808 :     const bool bShowChg = IDocumentRedlineAccess::IsShowChanges( rDoc.GetRedlineMode() );
     154             : 
     155             :     // If called from word count or from spell checking, deleted redlines
     156             :     // should be masked:
     157        1808 :     if ( bShowChg )
     158             :     {
     159        1808 :         nRedlinesMasked = lcl_MaskRedlines( rNode, rText, nStt, nEnd, cChar );
     160             :     }
     161             : 
     162        1808 :     const bool bHideHidden = !SW_MOD()->GetViewOption(rDoc.get(IDocumentSettingAccess::HTML_MODE))->IsShowHiddenChar();
     163             : 
     164             :     // If called from word count, we want to mask the hidden ranges even
     165             :     // if they are visible:
     166        1808 :     if ( !bCheckShowHiddenChar || bHideHidden )
     167             :     {
     168             :         nHiddenCharsMasked =
     169        1808 :             SwScriptInfo::MaskHiddenRanges( rNode, rText, nStt, nEnd, cChar );
     170             :     }
     171             : 
     172        1808 :     return nRedlinesMasked + nHiddenCharsMasked;
     173             : }
     174             : 
     175             : /**
     176             :  * Used for spell checking. Calculates a rectangle for repaint.
     177             :  */
     178          53 : static SwRect lcl_CalculateRepaintRect( SwTxtFrm& rTxtFrm, sal_Int32 nChgStart, sal_Int32 nChgEnd )
     179             : {
     180          53 :     SwRect aRect;
     181             : 
     182          53 :     SwTxtNode *pNode = rTxtFrm.GetTxtNode();
     183             : 
     184          53 :     SwNodeIndex aNdIdx( *pNode );
     185         106 :     SwPosition aPos( aNdIdx, SwIndex( pNode, nChgEnd ) );
     186          53 :     SwCrsrMoveState aTmpState( MV_NONE );
     187          53 :     aTmpState.b2Lines = sal_True;
     188          53 :     rTxtFrm.GetCharRect( aRect, aPos, &aTmpState );
     189             :     // information about end of repaint area
     190          53 :     Sw2LinesPos* pEnd2Pos = aTmpState.p2Lines;
     191             : 
     192          53 :     const SwTxtFrm *pEndFrm = &rTxtFrm;
     193             : 
     194         106 :     while( pEndFrm->HasFollow() &&
     195           0 :            nChgEnd >= pEndFrm->GetFollow()->GetOfst() )
     196           0 :         pEndFrm = pEndFrm->GetFollow();
     197             : 
     198          53 :     if ( pEnd2Pos )
     199             :     {
     200             :         // we are inside a special portion, take left border
     201           0 :         SWRECTFN( pEndFrm )
     202           0 :         (aRect.*fnRect->fnSetTop)( (pEnd2Pos->aLine.*fnRect->fnGetTop)() );
     203           0 :         if ( pEndFrm->IsRightToLeft() )
     204           0 :             (aRect.*fnRect->fnSetLeft)( (pEnd2Pos->aPortion.*fnRect->fnGetLeft)() );
     205             :         else
     206           0 :             (aRect.*fnRect->fnSetLeft)( (pEnd2Pos->aPortion.*fnRect->fnGetRight)() );
     207           0 :         (aRect.*fnRect->fnSetWidth)( 1 );
     208           0 :         (aRect.*fnRect->fnSetHeight)( (pEnd2Pos->aLine.*fnRect->fnGetHeight)() );
     209           0 :         delete pEnd2Pos;
     210             :     }
     211             : 
     212          53 :     aTmpState.p2Lines = NULL;
     213          53 :     SwRect aTmp;
     214          53 :     aPos = SwPosition( aNdIdx, SwIndex( pNode, nChgStart ) );
     215          53 :     rTxtFrm.GetCharRect( aTmp, aPos, &aTmpState );
     216             : 
     217             :     // i63141: GetCharRect(..) could cause a formatting,
     218             :     // during the formatting SwTxtFrms could be joined, deleted, created...
     219             :     // => we have to reinit pStartFrm and pEndFrm after the formatting
     220          53 :     const SwTxtFrm* pStartFrm = &rTxtFrm;
     221         106 :     while( pStartFrm->HasFollow() &&
     222           0 :            nChgStart >= pStartFrm->GetFollow()->GetOfst() )
     223           0 :         pStartFrm = pStartFrm->GetFollow();
     224          53 :     pEndFrm = pStartFrm;
     225         106 :     while( pEndFrm->HasFollow() &&
     226           0 :            nChgEnd >= pEndFrm->GetFollow()->GetOfst() )
     227           0 :         pEndFrm = pEndFrm->GetFollow();
     228             : 
     229             :     // information about start of repaint area
     230          53 :     Sw2LinesPos* pSt2Pos = aTmpState.p2Lines;
     231          53 :     if ( pSt2Pos )
     232             :     {
     233             :         // we are inside a special portion, take right border
     234           0 :         SWRECTFN( pStartFrm )
     235           0 :         (aTmp.*fnRect->fnSetTop)( (pSt2Pos->aLine.*fnRect->fnGetTop)() );
     236           0 :         if ( pStartFrm->IsRightToLeft() )
     237           0 :             (aTmp.*fnRect->fnSetLeft)( (pSt2Pos->aPortion.*fnRect->fnGetRight)() );
     238             :         else
     239           0 :             (aTmp.*fnRect->fnSetLeft)( (pSt2Pos->aPortion.*fnRect->fnGetLeft)() );
     240           0 :         (aTmp.*fnRect->fnSetWidth)( 1 );
     241           0 :         (aTmp.*fnRect->fnSetHeight)( (pSt2Pos->aLine.*fnRect->fnGetHeight)() );
     242           0 :         delete pSt2Pos;
     243             :     }
     244             : 
     245          53 :     bool bSameFrame = true;
     246             : 
     247          53 :     if( rTxtFrm.HasFollow() )
     248             :     {
     249           0 :         if( pEndFrm != pStartFrm )
     250             :         {
     251           0 :             bSameFrame = false;
     252           0 :             SwRect aStFrm( pStartFrm->PaintArea() );
     253             :             {
     254           0 :                 SWRECTFN( pStartFrm )
     255           0 :                 (aTmp.*fnRect->fnSetLeft)( (aStFrm.*fnRect->fnGetLeft)() );
     256           0 :                 (aTmp.*fnRect->fnSetRight)( (aStFrm.*fnRect->fnGetRight)() );
     257           0 :                 (aTmp.*fnRect->fnSetBottom)( (aStFrm.*fnRect->fnGetBottom)() );
     258             :             }
     259           0 :             aStFrm = pEndFrm->PaintArea();
     260             :             {
     261           0 :                 SWRECTFN( pEndFrm )
     262           0 :                 (aRect.*fnRect->fnSetTop)( (aStFrm.*fnRect->fnGetTop)() );
     263           0 :                 (aRect.*fnRect->fnSetLeft)( (aStFrm.*fnRect->fnGetLeft)() );
     264           0 :                 (aRect.*fnRect->fnSetRight)( (aStFrm.*fnRect->fnGetRight)() );
     265             :             }
     266           0 :             aRect.Union( aTmp );
     267             :             while( true )
     268             :             {
     269           0 :                 pStartFrm = pStartFrm->GetFollow();
     270           0 :                 if( pStartFrm == pEndFrm )
     271           0 :                     break;
     272           0 :                 aRect.Union( pStartFrm->PaintArea() );
     273           0 :             }
     274             :         }
     275             :     }
     276          53 :     if( bSameFrame )
     277             :     {
     278          53 :         SWRECTFN( pStartFrm )
     279          53 :         if( (aTmp.*fnRect->fnGetTop)() == (aRect.*fnRect->fnGetTop)() )
     280          40 :             (aRect.*fnRect->fnSetLeft)( (aTmp.*fnRect->fnGetLeft)() );
     281             :         else
     282             :         {
     283          13 :             SwRect aStFrm( pStartFrm->PaintArea() );
     284          13 :             (aRect.*fnRect->fnSetLeft)( (aStFrm.*fnRect->fnGetLeft)() );
     285          13 :             (aRect.*fnRect->fnSetRight)( (aStFrm.*fnRect->fnGetRight)() );
     286          13 :             (aRect.*fnRect->fnSetTop)( (aTmp.*fnRect->fnGetTop)() );
     287             :         }
     288             : 
     289          53 :         if( aTmp.Height() > aRect.Height() )
     290           0 :             aRect.Height( aTmp.Height() );
     291             :     }
     292             : 
     293         106 :     return aRect;
     294             : }
     295             : 
     296             : /**
     297             :  * Used for automatic styles. Used during RstAttr.
     298             :  */
     299        3677 : static bool lcl_HaveCommonAttributes( IStyleAccess& rStyleAccess,
     300             :                                       const SfxItemSet* pSet1,
     301             :                                       sal_uInt16 nWhichId,
     302             :                                       const SfxItemSet& rSet2,
     303             :                                       boost::shared_ptr<SfxItemSet>& pStyleHandle )
     304             : {
     305        3677 :     bool bRet = false;
     306             : 
     307        3677 :     SfxItemSet* pNewSet = 0;
     308             : 
     309        3677 :     if ( !pSet1 )
     310             :     {
     311             :         OSL_ENSURE( nWhichId, "lcl_HaveCommonAttributes not used correctly" );
     312           0 :         if ( SFX_ITEM_SET == rSet2.GetItemState( nWhichId, false ) )
     313             :         {
     314           0 :             pNewSet = rSet2.Clone( true );
     315           0 :             pNewSet->ClearItem( nWhichId );
     316             :         }
     317             :     }
     318        3677 :     else if ( pSet1->Count() )
     319             :     {
     320        3677 :         SfxItemIter aIter( *pSet1 );
     321        3677 :         const SfxPoolItem* pItem = aIter.GetCurItem();
     322             :         while( true )
     323             :         {
     324        3677 :             if ( SFX_ITEM_SET == rSet2.GetItemState( pItem->Which(), false ) )
     325             :             {
     326          42 :                 if ( !pNewSet )
     327          42 :                     pNewSet = rSet2.Clone( true );
     328          42 :                 pNewSet->ClearItem( pItem->Which() );
     329             :             }
     330             : 
     331        3677 :             if( aIter.IsAtEnd() )
     332        3677 :                 break;
     333             : 
     334           0 :             pItem = aIter.NextItem();
     335        3677 :         }
     336             :     }
     337             : 
     338        3677 :     if ( pNewSet )
     339             :     {
     340          42 :         if ( pNewSet->Count() )
     341          41 :             pStyleHandle = rStyleAccess.getAutomaticStyle( *pNewSet, IStyleAccess::AUTO_STYLE_CHAR );
     342          42 :         delete pNewSet;
     343          42 :         bRet = true;
     344             :     }
     345             : 
     346        3677 :     return bRet;
     347             : }
     348             : 
     349             : /** Delete all attributes
     350             :  *
     351             :  * 5 cases:
     352             :  * 1) The attribute is completely in the deletion range:
     353             :  *    -> delete it
     354             :  * 2) The end of the attribute is in the deletion range:
     355             :  *    -> delete it, then re-insert it with new end
     356             :  * 3) The start of the attribute is in the deletion range:
     357             :  *    -> delete it, then re-insert it with new start
     358             :  * 4) The attribute contains the deletion range:
     359             :  *       Split, i.e.,
     360             :  *    -> Delete, re-insert from old start to start of deletion range
     361             :  *    -> insert new attribute from end of deletion range to old end
     362             :  * 5) The attribute is outside the deletion range
     363             :  *    -> nothing to do
     364             :  *
     365             :  * @param rIdx starting position
     366             :  * @param nLen length of the deletion
     367             :  * @param nthat ???
     368             :  * @param pSet ???
     369             :  * @param bInclRefToxMark ???
     370             :  */
     371             : 
     372        6286 : void SwTxtNode::RstTxtAttr(
     373             :     const SwIndex &rIdx,
     374             :     const sal_Int32 nLen,
     375             :     const sal_uInt16 nWhich,
     376             :     const SfxItemSet* pSet,
     377             :     const bool bInclRefToxMark )
     378             : {
     379        6286 :     if ( !GetpSwpHints() )
     380        6286 :         return;
     381             : 
     382        6286 :     sal_Int32 nStt = rIdx.GetIndex();
     383        6286 :     sal_Int32 nEnd = nStt + nLen;
     384             :     {
     385             :         // enlarge range for the reset of text attributes in case of an overlapping input field
     386        6286 :         const SwTxtInputFld* pTxtInputFld = dynamic_cast<const SwTxtInputFld*>(GetTxtAttrAt( nStt, RES_TXTATR_INPUTFIELD, PARENT ));
     387        6286 :         if ( pTxtInputFld == NULL )
     388             :         {
     389        6286 :             pTxtInputFld = dynamic_cast<const SwTxtInputFld*>(GetTxtAttrAt(nEnd, RES_TXTATR_INPUTFIELD, PARENT ));
     390             :         }
     391        6286 :         if ( pTxtInputFld != NULL )
     392             :         {
     393           0 :             if ( nStt > *(pTxtInputFld->GetStart()) )
     394             :             {
     395           0 :                 nStt = *(pTxtInputFld->GetStart());
     396             :             }
     397           0 :             if ( nEnd < *(pTxtInputFld->End()) )
     398             :             {
     399           0 :                 nEnd = *(pTxtInputFld->End());
     400             :             }
     401             :         }
     402             :     }
     403             : 
     404        6286 :     bool bChanged = false;
     405             : 
     406             :     // nMin and nMax initialized to maximum / minimum (inverse)
     407        6286 :     sal_Int32 nMin = m_Text.getLength();
     408        6286 :     sal_Int32 nMax = nStt;
     409        6286 :     const bool bNoLen = nMin == 0;
     410             : 
     411             :     // We have to remember the "new" attributes, that have
     412             :     // been introduced by splitting surrounding attributes (case 4).
     413             :     // They may not be forgotten inside the "Forget" function
     414             :     //std::vector< const SwTxtAttr* > aNewAttributes;
     415             : 
     416             :     // iterate over attribute array until start of attribute is behind deletion range
     417        6286 :     sal_uInt16 i = 0;
     418             :     sal_Int32 nAttrStart;
     419        6286 :     SwTxtAttr *pHt = NULL;
     420       36264 :     while ( (i < m_pSwpHints->Count())
     421       23692 :             && ( ( ( nAttrStart = *(*m_pSwpHints)[i]->GetStart()) < nEnd )
     422        2636 :                  || nLen==0 ) )
     423             :     {
     424        8703 :         pHt = m_pSwpHints->GetTextHint(i);
     425             : 
     426             :         // attributes without end stay in!
     427             :         // but consider <bInclRefToxMark> used by Undo
     428        8703 :         sal_Int32* const pAttrEnd = pHt->GetEnd();
     429             :         const bool bKeepAttrWithoutEnd =
     430             :             pAttrEnd == NULL
     431        8975 :             && ( !bInclRefToxMark
     432           0 :                  || ( RES_TXTATR_REFMARK != pHt->Which()
     433           0 :                       && RES_TXTATR_TOXMARK != pHt->Which()
     434           0 :                       && RES_TXTATR_META != pHt->Which()
     435        8703 :                       && RES_TXTATR_METAFIELD != pHt->Which() ) );
     436        8703 :         if ( bKeepAttrWithoutEnd )
     437             :         {
     438             : 
     439         272 :             i++;
     440        5976 :             continue;
     441             :         }
     442             :         // attributes with content stay in
     443        8431 :         if ( pHt->HasContent() )
     444             :         {
     445           0 :             ++i;
     446           0 :             continue;
     447             :         }
     448             : 
     449             :         // Default behavior is to process all attributes:
     450        8431 :         bool bSkipAttr = false;
     451        8431 :         boost::shared_ptr<SfxItemSet> pStyleHandle;
     452             : 
     453             :         // 1. case: We want to reset only the attributes listed in pSet:
     454        8431 :         if ( pSet )
     455             :         {
     456        5432 :             bSkipAttr = SFX_ITEM_SET != pSet->GetItemState( pHt->Which(), false );
     457        5432 :             if ( bSkipAttr && RES_TXTATR_AUTOFMT == pHt->Which() )
     458             :             {
     459             :                 // if the current attribute is an autostyle, we have to check if the autostyle
     460             :                 // and pSet have any attributes in common. If so, pStyleHandle will contain
     461             :                 // a handle to AutoStyle / pSet:
     462        3677 :                 bSkipAttr = !lcl_HaveCommonAttributes( getIDocumentStyleAccess(), pSet, 0, *static_cast<const SwFmtAutoFmt&>(pHt->GetAttr()).GetStyleHandle(), pStyleHandle );
     463             :             }
     464             :         }
     465        2999 :         else if ( nWhich )
     466             :         {
     467             :             // 2. case: We want to reset only the attributes with WhichId nWhich:
     468           0 :             bSkipAttr = nWhich != pHt->Which();
     469           0 :             if ( bSkipAttr && RES_TXTATR_AUTOFMT == pHt->Which() )
     470             :             {
     471           0 :                 bSkipAttr = !lcl_HaveCommonAttributes( getIDocumentStyleAccess(), 0, nWhich, *static_cast<const SwFmtAutoFmt&>(pHt->GetAttr()).GetStyleHandle(), pStyleHandle );
     472             :             }
     473             :         }
     474        2999 :         else if ( !bInclRefToxMark )
     475             :         {
     476             :             // 3. case: Reset all attributes except from ref/toxmarks:
     477             :             // skip hints with CH_TXTATR here
     478             :             // (deleting those is ONLY allowed for UNDO!)
     479        2999 :             bSkipAttr = RES_TXTATR_REFMARK   == pHt->Which()
     480        2999 :                      || RES_TXTATR_TOXMARK   == pHt->Which()
     481        2999 :                      || RES_TXTATR_META      == pHt->Which()
     482        5998 :                      || RES_TXTATR_METAFIELD == pHt->Which();
     483             :         }
     484             : 
     485        8431 :         if ( bSkipAttr )
     486             :         {
     487        4779 :             i++;
     488        4779 :             continue;
     489             :         }
     490             : 
     491        3652 :         if (nStt <= nAttrStart)     // Case: 1,3,5
     492             :         {
     493             :             const sal_Int32 nAttrEnd = pAttrEnd != NULL
     494             :                                         ? *pAttrEnd
     495         653 :                                         : nAttrStart;
     496         653 :             if (nEnd > nAttrStart
     497           0 :                 || (nEnd == nAttrEnd && nEnd == nAttrStart)) // Case: 1,3
     498             :             {
     499         653 :                 if ( nMin > nAttrStart )
     500         653 :                     nMin = nAttrStart;
     501         653 :                 if ( nMax < nAttrEnd )
     502         633 :                     nMax = nAttrEnd;
     503             :                 // If only a no-extent hint is deleted, no resorting is needed
     504         653 :                 bChanged = bChanged || nEnd > nAttrStart || bNoLen;
     505         653 :                 if (nAttrEnd <= nEnd)   // Case: 1
     506             :                 {
     507         653 :                     m_pSwpHints->DeleteAtPos(i);
     508         653 :                     DestroyAttr( pHt );
     509             : 
     510         653 :                     if ( pStyleHandle.get() )
     511             :                     {
     512          41 :                         SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(),
     513          82 :                                 *pStyleHandle, nAttrStart, nAttrEnd );
     514          41 :                         InsertHint( pNew, nsSetAttrMode::SETATTR_NOHINTADJUST );
     515             :                     }
     516             : 
     517             :                     // if the last attribute is a Field, the HintsArray is deleted!
     518         653 :                     if ( !m_pSwpHints )
     519           0 :                         break;
     520             : 
     521             :                     //JP 26.11.96:
     522             :                     // DeleteAtPos does a Resort!  Via Case 3 or 4 hints could
     523             :                     // have been moved around so i is wrong now.
     524             :                     // So we have to start over at 0 again.
     525         653 :                     i = 0;
     526         653 :                     continue;
     527             :                 }
     528             :                 else    // Case: 3
     529             :                 {
     530           0 :                     m_pSwpHints->NoteInHistory( pHt );
     531             :                     // UGLY: this may temporarily destroy the sorting!
     532           0 :                     *pHt->GetStart() = nEnd;
     533           0 :                     m_pSwpHints->NoteInHistory( pHt, true );
     534             : 
     535           0 :                     if ( pStyleHandle.get() && nAttrStart < nEnd )
     536             :                     {
     537           0 :                         SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(),
     538           0 :                                 *pStyleHandle, nAttrStart, nEnd );
     539           0 :                         InsertHint( pNew, nsSetAttrMode::SETATTR_NOHINTADJUST );
     540             :                     }
     541             : 
     542             :                     // this case appears to rely on InsertHint not re-sorting
     543             :                     // and pNew being inserted behind pHt
     544             :                     assert(pHt == m_pSwpHints->GetTextHint(i));
     545             : 
     546           0 :                     bChanged = true;
     547             :                 }
     548             :             }
     549             :         }
     550        2999 :         else if (pAttrEnd != 0)         // Case: 2,4,5
     551             :         {
     552        2999 :             if (*pAttrEnd > nStt)       // Case: 2,4
     553             :             {
     554           0 :                 if (*pAttrEnd < nEnd)   // Case: 2
     555             :                 {
     556           0 :                     if ( nMin > nAttrStart )
     557           0 :                         nMin = nAttrStart;
     558           0 :                     if ( nMax < *pAttrEnd )
     559           0 :                         nMax = *pAttrEnd;
     560           0 :                     bChanged = true;
     561             : 
     562           0 :                     const sal_Int32 nAttrEnd = *pAttrEnd;
     563             : 
     564           0 :                     m_pSwpHints->NoteInHistory( pHt );
     565             :                     // UGLY: this may temporarily destroy the sorting!
     566           0 :                     *pAttrEnd = nStt;
     567           0 :                     m_pSwpHints->NoteInHistory( pHt, true );
     568             : 
     569           0 :                     if ( pStyleHandle.get() )
     570             :                     {
     571           0 :                         SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(),
     572           0 :                             *pStyleHandle, nStt, nAttrEnd );
     573           0 :                         InsertHint( pNew, nsSetAttrMode::SETATTR_NOHINTADJUST );
     574             :                     }
     575             : 
     576             :                     // this case appears to rely on InsertHint not re-sorting
     577             :                     // and pNew being inserted behind pHt
     578             :                     assert(pHt == m_pSwpHints->GetTextHint(i));
     579             :                 }
     580           0 :                 else if (nLen)  // Case: 4
     581             :                 {
     582             :                     // for Length 0 both hints would be merged again by
     583             :                     // InsertHint, so leave them alone!
     584           0 :                     if ( nMin > nAttrStart )
     585           0 :                         nMin = nAttrStart;
     586           0 :                     if ( nMax < *pAttrEnd )
     587           0 :                         nMax = *pAttrEnd;
     588           0 :                     bChanged = true;
     589           0 :                     const sal_Int32 nTmpEnd = *pAttrEnd;
     590           0 :                     m_pSwpHints->NoteInHistory( pHt );
     591             :                     // UGLY: this may temporarily destroy the sorting!
     592           0 :                     *pAttrEnd = nStt;
     593           0 :                     m_pSwpHints->NoteInHistory( pHt, true );
     594             : 
     595           0 :                     if ( pStyleHandle.get() && nStt < nEnd )
     596             :                     {
     597           0 :                         SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(),
     598           0 :                             *pStyleHandle, nStt, nEnd );
     599           0 :                         InsertHint( pNew, nsSetAttrMode::SETATTR_NOHINTADJUST );
     600             :                     }
     601             : 
     602           0 :                     if( nEnd < nTmpEnd )
     603             :                     {
     604           0 :                         SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(),
     605           0 :                             pHt->GetAttr(), nEnd, nTmpEnd );
     606           0 :                         if ( pNew )
     607             :                         {
     608           0 :                             SwTxtCharFmt* pCharFmt = dynamic_cast<SwTxtCharFmt*>(pHt);
     609           0 :                             if ( pCharFmt )
     610           0 :                                 static_cast<SwTxtCharFmt*>(pNew)->SetSortNumber( pCharFmt->GetSortNumber() );
     611             : 
     612             :                             InsertHint( pNew,
     613           0 :                                 nsSetAttrMode::SETATTR_NOHINTADJUST );
     614             :                         }
     615             :                     }
     616             : 
     617             :                     // this case appears to rely on InsertHint not re-sorting
     618             :                     // and pNew being inserted behind pHt
     619             :                     assert(pHt == m_pSwpHints->GetTextHint(i));
     620             :                 }
     621             :             }
     622             :         }
     623        2999 :         ++i;
     624        2999 :     }
     625             : 
     626        6286 :     TryDeleteSwpHints();
     627        6286 :     if (bChanged)
     628             :     {
     629         653 :         if ( HasHints() )
     630             :         {   // possibly sometimes Resort would be sufficient, but...
     631         626 :             m_pSwpHints->MergePortions(*this);
     632             :         }
     633             :         // TxtFrm's respond to aHint, others to aNew
     634         653 :         SwUpdateAttr aHint( nMin, nMax, 0 );
     635         653 :         NotifyClients( 0, &aHint );
     636        1306 :         SwFmtChg aNew( GetFmtColl() );
     637        1306 :         NotifyClients( 0, &aNew );
     638             :     }
     639             : }
     640             : 
     641           0 : sal_Int32 clipIndexBounds(const OUString &rStr, sal_Int32 nPos)
     642             : {
     643           0 :     if (nPos < 0)
     644           0 :         return 0;
     645           0 :     if (nPos > rStr.getLength())
     646           0 :         return rStr.getLength();
     647           0 :     return nPos;
     648             : }
     649             : 
     650             : /*************************************************************************
     651             :  *                SwTxtNode::GetCurWord()
     652             :  *
     653             :  * Aktuelles Wort zurueckliefern:
     654             :  * Wir suchen immer von links nach rechts, es wird also das Wort
     655             :  * vor nPos gesucht. Es sei denn, wir befinden uns am Anfang des
     656             :  * Absatzes, dann wird das erste Wort zurueckgeliefert.
     657             :  * Wenn dieses erste Wort nur aus Whitespaces besteht, returnen wir
     658             :  * einen leeren String.
     659             :  *************************************************************************/
     660           0 : OUString SwTxtNode::GetCurWord( sal_Int32 nPos ) const
     661             : {
     662             :     assert(nPos <= m_Text.getLength()); // invalid index
     663             : 
     664           0 :     if (m_Text.isEmpty())
     665           0 :         return m_Text;
     666             : 
     667           0 :     Boundary aBndry;
     668           0 :     const uno::Reference< XBreakIterator > &rxBreak = g_pBreakIt->GetBreakIter();
     669           0 :     if (rxBreak.is())
     670             :     {
     671           0 :         sal_Int16 nWordType = WordType::DICTIONARY_WORD;
     672           0 :         lang::Locale aLocale( g_pBreakIt->GetLocale( GetLang( nPos ) ) );
     673             : #if OSL_DEBUG_LEVEL > 1
     674             :         sal_Bool bBegin = rxBreak->isBeginWord( m_Text, nPos, aLocale, nWordType );
     675             :         sal_Bool bEnd   = rxBreak->isEndWord  ( m_Text, nPos, aLocale, nWordType );
     676             :         (void)bBegin;
     677             :         (void)bEnd;
     678             : #endif
     679             :         aBndry =
     680           0 :             rxBreak->getWordBoundary( m_Text, nPos, aLocale, nWordType, sal_True );
     681             : 
     682             :         // if no word was found use previous word (if any)
     683           0 :         if (aBndry.startPos == aBndry.endPos)
     684             :         {
     685           0 :             aBndry = rxBreak->previousWord( m_Text, nPos, aLocale, nWordType );
     686           0 :         }
     687             :     }
     688             : 
     689             :     // check if word was found and if it uses a symbol font, if so
     690             :     // enforce returning an empty string
     691           0 :     if (aBndry.endPos != aBndry.startPos && IsSymbol( aBndry.startPos ))
     692           0 :         aBndry.endPos = aBndry.startPos;
     693             : 
     694             :     // can have -1 as start/end of bounds not found
     695           0 :     aBndry.startPos = clipIndexBounds(m_Text, aBndry.startPos);
     696           0 :     aBndry.endPos = clipIndexBounds(m_Text, aBndry.endPos);
     697             : 
     698             :     return m_Text.copy(aBndry.startPos,
     699           0 :                        aBndry.endPos - aBndry.startPos);
     700             : }
     701             : 
     702        1852 : SwScanner::SwScanner( const SwTxtNode& rNd, const OUString& rTxt,
     703             :     const LanguageType* pLang, const ModelToViewHelper& rConvMap,
     704             :     sal_uInt16 nType, sal_Int32 nStart, sal_Int32 nEnde, sal_Bool bClp )
     705             :     : rNode( rNd )
     706             :     , aPreDashReplacementText(rTxt)
     707             :     , pLanguage( pLang )
     708             :     , m_ModelToView( rConvMap )
     709             :     , nLen( 0 )
     710             :     , nOverriddenDashCount( 0 )
     711             :     , nWordType( nType )
     712        1852 :     , bClip( bClp )
     713             : {
     714             :     OSL_ENSURE( !aPreDashReplacementText.isEmpty(), "SwScanner: EmptyString" );
     715        1852 :     nStartPos = nBegin = nStart;
     716        1852 :     nEndPos = nEnde;
     717             : 
     718             :     //MSWord f.e has special emdash and endash behaviour in that they break
     719             :     //words for the purposes of word counting, while a hyphen etc. doesn't.
     720             : 
     721             :     //The default configuration treats emdash/endash as a word break, but
     722             :     //additional ones can be added in under tools->options
     723        1852 :     if (nWordType == i18n::WordType::WORD_COUNT)
     724             :     {
     725         588 :         OUString sDashes = officecfg::Office::Writer::WordCount::AdditionalSeparators::get();
     726        1176 :         OUStringBuffer aBuf(aPreDashReplacementText);
     727      103842 :         for (sal_Int32 i = nStartPos; i < nEndPos; ++i)
     728             :         {
     729      103254 :             sal_Unicode cChar = aBuf[i];
     730      103254 :             if (sDashes.indexOf(cChar) != -1)
     731             :             {
     732           7 :                 aBuf[i] = ' ';
     733           7 :                 ++nOverriddenDashCount;
     734             :             }
     735             :         }
     736        1176 :         aText = aBuf.makeStringAndClear();
     737             :     }
     738             :     else
     739        1264 :         aText = aPreDashReplacementText;
     740             : 
     741             :     assert(aPreDashReplacementText.getLength() == aText.getLength());
     742             : 
     743        1852 :     if ( pLanguage )
     744             :     {
     745          24 :         aCurrLang = *pLanguage;
     746             :     }
     747             :     else
     748             :     {
     749             :         ModelToViewHelper::ModelPosition aModelBeginPos =
     750        1828 :             m_ModelToView.ConvertToModelPosition( nBegin );
     751        1828 :         aCurrLang = rNd.GetLang( aModelBeginPos.mnPos );
     752             :     }
     753        1852 : }
     754             : 
     755             : namespace
     756             : {
     757             :     //fdo#45271 for Asian words count characters instead of words
     758        8366 :     sal_Int32 forceEachAsianCodePointToWord(const OUString &rText, sal_Int32 nBegin, sal_Int32 nLen)
     759             :     {
     760        8366 :         if (nLen > 1)
     761             :         {
     762        8167 :             const uno::Reference< XBreakIterator > &rxBreak = g_pBreakIt->GetBreakIter();
     763             : 
     764        8167 :             sal_uInt16 nCurrScript = rxBreak->getScriptType( rText, nBegin );
     765             : 
     766        8167 :             sal_Int32 indexUtf16 = nBegin;
     767        8167 :             rText.iterateCodePoints(&indexUtf16, 1);
     768             : 
     769             :             //First character is Asian, consider it a word :-(
     770        8167 :             if (nCurrScript == i18n::ScriptType::ASIAN)
     771             :             {
     772           6 :                 nLen = indexUtf16 - nBegin;
     773           6 :                 return nLen;
     774             :             }
     775             : 
     776             :             //First character was not Asian, consider appearance of any Asian character
     777             :             //to be the end of the word
     778      103296 :             while (indexUtf16 < nBegin + nLen)
     779             :             {
     780       86974 :                 nCurrScript = rxBreak->getScriptType( rText, indexUtf16 );
     781       86974 :                 if (nCurrScript == i18n::ScriptType::ASIAN)
     782             :                 {
     783           0 :                     nLen = indexUtf16 - nBegin;
     784           0 :                     return nLen;
     785             :                 }
     786       86974 :                 rText.iterateCodePoints(&indexUtf16, 1);
     787        8161 :             }
     788             :         }
     789        8360 :         return nLen;
     790             :     }
     791             : }
     792             : 
     793       19603 : sal_Bool SwScanner::NextWord()
     794             : {
     795       19603 :     nBegin = nBegin + nLen;
     796       19603 :     Boundary aBound;
     797             : 
     798       19603 :     CharClass& rCC = GetAppCharClass();
     799       19603 :     LanguageTag aOldLanguageTag = rCC.getLanguageTag();
     800             : 
     801             :     while ( true )
     802             :     {
     803             :         // skip non-letter characters:
     804       55949 :         while ( nBegin < aText.getLength() )
     805             :         {
     806       34495 :             if ( !u_isspace( aText[nBegin] ) )
     807             :             {
     808       19121 :                 if ( !pLanguage )
     809             :                 {
     810       19097 :                     const sal_uInt16 nNextScriptType = g_pBreakIt->GetBreakIter()->getScriptType( aText, nBegin );
     811             :                     ModelToViewHelper::ModelPosition aModelBeginPos =
     812       19097 :                         m_ModelToView.ConvertToModelPosition( nBegin );
     813       19097 :                     aCurrLang = rNode.GetLang( aModelBeginPos.mnPos, 1, nNextScriptType );
     814             :                 }
     815             : 
     816       19121 :                 if ( nWordType != i18n::WordType::WORD_COUNT )
     817             :                 {
     818       10755 :                     rCC.setLanguageTag( LanguageTag( g_pBreakIt->GetLocale( aCurrLang )) );
     819       10755 :                     if ( rCC.isLetterNumeric(OUString(aText[nBegin])) )
     820        9386 :                         break;
     821             :                 }
     822             :                 else
     823        8366 :                     break;
     824             :             }
     825       16743 :             ++nBegin;
     826             :         }
     827             : 
     828       19603 :         if ( nBegin >= aText.getLength() || nBegin >= nEndPos )
     829        1851 :             return sal_False;
     830             : 
     831             :         // get the word boundaries
     832       35504 :         aBound = g_pBreakIt->GetBreakIter()->getWordBoundary( aText, nBegin,
     833       17752 :                 g_pBreakIt->GetLocale( aCurrLang ), nWordType, sal_True );
     834             :         OSL_ENSURE( aBound.endPos >= aBound.startPos, "broken aBound result" );
     835             : 
     836             :         // we don't want to include preceeding text
     837             :         // to count words in text with mixed script punctuation correctly,
     838             :         // but we want to include preceeding symbols (eg. percent sign, section sign,
     839             :         // degree sign defined by dict_word_hu to spell check their affixed forms).
     840       17752 :         if (nWordType == i18n::WordType::WORD_COUNT && aBound.startPos < nBegin)
     841           5 :             aBound.startPos = nBegin;
     842             : 
     843             :         //no word boundaries could be found
     844       17752 :         if(aBound.endPos == aBound.startPos)
     845           0 :             return sal_False;
     846             : 
     847             :         //if a word before is found it has to be searched for the next
     848       17752 :         if(aBound.endPos == nBegin)
     849           0 :             ++nBegin;
     850             :         else
     851       17752 :             break;
     852             :     } // end while( true )
     853             : 
     854       17752 :     rCC.setLanguageTag( aOldLanguageTag );
     855             : 
     856             :     // #i89042, as discussed with HDU: don't evaluate script changes for word count. Use whole word.
     857       17752 :     if ( nWordType == i18n::WordType::WORD_COUNT )
     858             :     {
     859        8366 :         nBegin = std::max(aBound.startPos, nBegin);
     860        8366 :         nLen   = 0;
     861        8366 :         if (aBound.endPos > nBegin)
     862        8366 :             nLen = aBound.endPos - nBegin;
     863             :     }
     864             :     else
     865             :     {
     866             :         // we have to differenciate between these cases:
     867        9386 :         if ( aBound.startPos <= nBegin )
     868             :         {
     869             :             OSL_ENSURE( aBound.endPos >= nBegin, "Unexpected aBound result" );
     870             : 
     871             :             // restrict boundaries to script boundaries and nEndPos
     872        9386 :             const sal_uInt16 nCurrScript = g_pBreakIt->GetBreakIter()->getScriptType( aText, nBegin );
     873        9386 :             OUString aTmpWord = aText.copy( nBegin, aBound.endPos - nBegin );
     874       18772 :             const sal_Int32 nScriptEnd = nBegin +
     875       18772 :                 g_pBreakIt->GetBreakIter()->endOfScript( aTmpWord, 0, nCurrScript );
     876        9386 :             const sal_Int32 nEnd = std::min( aBound.endPos, nScriptEnd );
     877             : 
     878             :             // restrict word start to last script change position
     879        9386 :             sal_Int32 nScriptBegin = 0;
     880        9386 :             if ( aBound.startPos < nBegin )
     881             :             {
     882             :                 // search from nBegin backwards until the next script change
     883           0 :                 aTmpWord = aText.copy( aBound.startPos,
     884           0 :                                        nBegin - aBound.startPos + 1 );
     885           0 :                 nScriptBegin = aBound.startPos +
     886           0 :                     g_pBreakIt->GetBreakIter()->beginOfScript( aTmpWord, nBegin - aBound.startPos,
     887           0 :                                                     nCurrScript );
     888             :             }
     889             : 
     890        9386 :             nBegin = std::max( aBound.startPos, nScriptBegin );
     891        9386 :             nLen = nEnd - nBegin;
     892             :         }
     893             :         else
     894             :         {
     895           0 :             const sal_uInt16 nCurrScript = g_pBreakIt->GetBreakIter()->getScriptType( aText, aBound.startPos );
     896             :             OUString aTmpWord = aText.copy( aBound.startPos,
     897           0 :                                              aBound.endPos - aBound.startPos );
     898           0 :             const sal_Int32 nScriptEnd = aBound.startPos +
     899           0 :                 g_pBreakIt->GetBreakIter()->endOfScript( aTmpWord, 0, nCurrScript );
     900           0 :             const sal_Int32 nEnd = std::min( aBound.endPos, nScriptEnd );
     901           0 :             nBegin = aBound.startPos;
     902           0 :             nLen = nEnd - nBegin;
     903             :         }
     904             :     }
     905             : 
     906             :     // optionally clip the result of getWordBoundaries:
     907       17752 :     if ( bClip )
     908             :     {
     909        8366 :         aBound.startPos = std::max( aBound.startPos, nStartPos );
     910        8366 :         aBound.endPos = std::min( aBound.endPos, nEndPos );
     911        8366 :         nBegin = aBound.startPos;
     912        8366 :         nLen = aBound.endPos - nBegin;
     913             :     }
     914             : 
     915       17752 :     if( ! nLen )
     916           0 :         return sal_False;
     917             : 
     918       17752 :     if ( nWordType == i18n::WordType::WORD_COUNT )
     919        8366 :         nLen = forceEachAsianCodePointToWord(aText, nBegin, nLen);
     920             : 
     921       17752 :     aWord = aPreDashReplacementText.copy( nBegin, nLen );
     922             : 
     923       17752 :     return sal_True;
     924             : }
     925             : 
     926           0 : sal_uInt16 SwTxtNode::Spell(SwSpellArgs* pArgs)
     927             : {
     928             :     // Die Aehnlichkeiten zu SwTxtFrm::_AutoSpell sind beabsichtigt ...
     929             :     // ACHTUNG: Ev. Bugs in beiden Routinen fixen!
     930             : 
     931             :     // modify string according to redline information and hidden text
     932           0 :     const OUString aOldTxt( m_Text );
     933           0 :     OUStringBuffer buf(m_Text);
     934             :     const bool bRestoreString =
     935           0 :         lcl_MaskRedlinesAndHiddenText(*this, buf, 0, m_Text.getLength()) > 0;
     936           0 :     if (bRestoreString)
     937             :     {   // ??? UGLY: is it really necessary to modify m_Text here?
     938           0 :         m_Text = buf.makeStringAndClear();
     939             :     }
     940             : 
     941           0 :     sal_Int32 nBegin = ( pArgs->pStartNode != this )
     942             :         ? 0
     943           0 :         : pArgs->pStartIdx->GetIndex();
     944             : 
     945           0 :     sal_Int32 nEnd = ( pArgs->pEndNode != this )
     946           0 :             ? m_Text.getLength()
     947           0 :             : pArgs->pEndIdx->GetIndex();
     948             : 
     949           0 :     pArgs->xSpellAlt = NULL;
     950             : 
     951             :     // 4 cases:
     952             : 
     953             :     // 1. IsWrongDirty = 0 and GetWrong = 0
     954             :     //      Everything is checked and correct
     955             :     // 2. IsWrongDirty = 0 and GetWrong = 1
     956             :     //      Everything is checked and errors are identified in the wrong list
     957             :     // 3. IsWrongDirty = 1 and GetWrong = 0
     958             :     //      Nothing has been checked
     959             :     // 4. IsWrongDirty = 1 and GetWrong = 1
     960             :     //      Text has been checked but there is an invalid range in the wrong list
     961             : 
     962             :     // Nothing has to be done for case 1.
     963           0 :     if ( ( IsWrongDirty() || GetWrong() ) && m_Text.getLength() )
     964             :     {
     965           0 :         if (nBegin > m_Text.getLength())
     966             :         {
     967           0 :             nBegin = m_Text.getLength();
     968             :         }
     969           0 :         if (nEnd > m_Text.getLength())
     970             :         {
     971           0 :             nEnd = m_Text.getLength();
     972             :         }
     973             : 
     974           0 :         if(!IsWrongDirty())
     975             :         {
     976           0 :             const sal_Int32 nTemp = GetWrong()->NextWrong( nBegin );
     977           0 :             if(nTemp > nEnd)
     978             :             {
     979             :                 // reset original text
     980           0 :                 if ( bRestoreString )
     981             :                 {
     982           0 :                     m_Text = aOldTxt;
     983             :                 }
     984           0 :                 return 0;
     985             :             }
     986           0 :             if(nTemp > nBegin)
     987           0 :                 nBegin = nTemp;
     988             : 
     989             :         }
     990             : 
     991             :         // In case 2. we pass the wrong list to the scanned, because only
     992             :         // the words in the wrong list have to be checked
     993             :         SwScanner aScanner( *this, m_Text, 0, ModelToViewHelper(),
     994             :                             WordType::DICTIONARY_WORD,
     995           0 :                             nBegin, nEnd );
     996           0 :         while( !pArgs->xSpellAlt.is() && aScanner.NextWord() )
     997             :         {
     998           0 :             const OUString& rWord = aScanner.GetWord();
     999             : 
    1000             :             // get next language for next word, consider language attributes
    1001             :             // within the word
    1002           0 :             LanguageType eActLang = aScanner.GetCurrentLanguage();
    1003             : 
    1004           0 :             if( rWord.getLength() > 0 && LANGUAGE_NONE != eActLang )
    1005             :             {
    1006           0 :                 if (pArgs->xSpeller.is())
    1007             :                 {
    1008           0 :                     SvxSpellWrapper::CheckSpellLang( pArgs->xSpeller, eActLang );
    1009           0 :                     pArgs->xSpellAlt = pArgs->xSpeller->spell( rWord, eActLang,
    1010           0 :                                             Sequence< PropertyValue >() );
    1011             :                 }
    1012           0 :                 if( (pArgs->xSpellAlt).is() )
    1013             :                 {
    1014           0 :                     if( IsSymbol( aScanner.GetBegin() ) )
    1015             :                     {
    1016           0 :                         pArgs->xSpellAlt = NULL;
    1017             :                     }
    1018             :                     else
    1019             :                     {
    1020             :                         // make sure the selection build later from the data
    1021             :                         // below does not include "in word" character to the
    1022             :                         // left and right in order to preserve those. Therefore
    1023             :                         // count those "in words" in order to modify the
    1024             :                         // selection accordingly.
    1025           0 :                         const sal_Unicode* pChar = rWord.getStr();
    1026           0 :                         sal_Int32 nLeft = 0;
    1027           0 :                         while (pChar && *pChar++ == CH_TXTATR_INWORD)
    1028           0 :                             ++nLeft;
    1029           0 :                         pChar = rWord.getLength() ? rWord.getStr() + rWord.getLength() - 1 : 0;
    1030           0 :                         sal_Int32 nRight = 0;
    1031           0 :                         while (pChar && *pChar-- == CH_TXTATR_INWORD)
    1032           0 :                             ++nRight;
    1033             : 
    1034           0 :                         pArgs->pStartNode = this;
    1035           0 :                         pArgs->pEndNode = this;
    1036           0 :                         pArgs->pStartIdx->Assign(this, aScanner.GetEnd() - nRight );
    1037           0 :                         pArgs->pEndIdx->Assign(this, aScanner.GetBegin() + nLeft );
    1038             :                     }
    1039             :                 }
    1040             :             }
    1041           0 :         }
    1042             :     }
    1043             : 
    1044             :     // reset original text
    1045           0 :     if ( bRestoreString )
    1046             :     {
    1047           0 :         m_Text = aOldTxt;
    1048             :     }
    1049             : 
    1050           0 :     return pArgs->xSpellAlt.is() ? 1 : 0;
    1051             : }
    1052             : 
    1053           0 : void SwTxtNode::SetLanguageAndFont( const SwPaM &rPaM,
    1054             :     LanguageType nLang, sal_uInt16 nLangWhichId,
    1055             :     const Font *pFont,  sal_uInt16 nFontWhichId )
    1056             : {
    1057             :     sal_uInt16 aRanges[] = {
    1058             :             nLangWhichId, nLangWhichId,
    1059             :             nFontWhichId, nFontWhichId,
    1060           0 :             0, 0, 0 };
    1061           0 :     if (!pFont)
    1062           0 :         aRanges[2] = aRanges[3] = 0;    // clear entries with font WhichId
    1063             : 
    1064           0 :     SwEditShell *pEditShell = GetDoc()->GetEditShell();
    1065           0 :     SfxItemSet aSet( pEditShell->GetAttrPool(), aRanges );
    1066           0 :     aSet.Put( SvxLanguageItem( nLang, nLangWhichId ) );
    1067             : 
    1068             :     OSL_ENSURE( pFont, "target font missing?" );
    1069           0 :     if (pFont)
    1070             :     {
    1071           0 :         SvxFontItem aFontItem = (SvxFontItem&) aSet.Get( nFontWhichId );
    1072           0 :         aFontItem.SetFamilyName(   pFont->GetName());
    1073           0 :         aFontItem.SetFamily(       pFont->GetFamily());
    1074           0 :         aFontItem.SetStyleName(    pFont->GetStyleName());
    1075           0 :         aFontItem.SetPitch(        pFont->GetPitch());
    1076           0 :         aFontItem.SetCharSet( pFont->GetCharSet() );
    1077           0 :         aSet.Put( aFontItem );
    1078             :     }
    1079             : 
    1080           0 :     GetDoc()->InsertItemSet( rPaM, aSet, 0 );
    1081             :     // SetAttr( aSet );    <- Does not set language attribute of empty paragraphs correctly,
    1082             :     //                     <- because since there is no selection the flag to garbage
    1083             :     //                     <- collect all attributes is set, and therefore attributes spanned
    1084             :     //                     <- over empty selection are removed.
    1085             : 
    1086           0 : }
    1087             : 
    1088           0 : sal_uInt16 SwTxtNode::Convert( SwConversionArgs &rArgs )
    1089             : {
    1090             :     // get range of text within node to be converted
    1091             :     // (either all the text or the text within the selection
    1092             :     // when the conversion was started)
    1093           0 :     const sal_Int32 nTextBegin = ( rArgs.pStartNode == this )
    1094           0 :         ? ::std::min(rArgs.pStartIdx->GetIndex(), m_Text.getLength())
    1095           0 :         : 0;
    1096             : 
    1097           0 :     const sal_Int32 nTextEnd = ( rArgs.pEndNode == this )
    1098           0 :         ?  ::std::min(rArgs.pEndIdx->GetIndex(), m_Text.getLength())
    1099           0 :         :  m_Text.getLength();
    1100             : 
    1101           0 :     rArgs.aConvText = OUString();
    1102             : 
    1103             :     // modify string according to redline information and hidden text
    1104           0 :     const OUString aOldTxt( m_Text );
    1105           0 :     OUStringBuffer buf(m_Text);
    1106             :     const bool bRestoreString =
    1107           0 :         lcl_MaskRedlinesAndHiddenText(*this, buf, 0, m_Text.getLength()) > 0;
    1108           0 :     if (bRestoreString)
    1109             :     {   // ??? UGLY: is it really necessary to modify m_Text here?
    1110           0 :         m_Text = buf.makeStringAndClear();
    1111             :     }
    1112             : 
    1113           0 :     bool    bFound  = false;
    1114           0 :     sal_Int32  nBegin  = nTextBegin;
    1115           0 :     sal_Int32  nLen = 0;
    1116           0 :     LanguageType nLangFound = LANGUAGE_NONE;
    1117           0 :     if (m_Text.isEmpty())
    1118             :     {
    1119           0 :         if (rArgs.bAllowImplicitChangesForNotConvertibleText)
    1120             :         {
    1121             :             // create SwPaM with mark & point spanning empty paragraph
    1122             :             //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different
    1123           0 :             SwPaM aCurPaM( *this, 0 );
    1124             : 
    1125             :             SetLanguageAndFont( aCurPaM,
    1126             :                     rArgs.nConvTargetLang, RES_CHRATR_CJK_LANGUAGE,
    1127           0 :                     rArgs.pTargetFont, RES_CHRATR_CJK_FONT );
    1128             :         }
    1129             :     }
    1130             :     else
    1131             :     {
    1132           0 :         SwLanguageIterator aIter( *this, nBegin );
    1133             : 
    1134             :         // Implicit changes require setting new attributes, which in turn destroys
    1135             :         // the attribute sequence on that aIter iterates. We store the necessary
    1136             :         // coordinates and apply those changes after iterating through the text.
    1137             :         typedef std::pair<sal_Int32, sal_Int32> ImplicitChangesRange;
    1138           0 :         std::vector<ImplicitChangesRange> aImplicitChanges;
    1139             : 
    1140             :         // find non zero length text portion of appropriate language
    1141           0 :         do {
    1142           0 :             nLangFound = aIter.GetLanguage();
    1143           0 :             bool bLangOk =  (nLangFound == rArgs.nConvSrcLang) ||
    1144           0 :                                 (editeng::HangulHanjaConversion::IsChinese( nLangFound ) &&
    1145           0 :                                  editeng::HangulHanjaConversion::IsChinese( rArgs.nConvSrcLang ));
    1146             : 
    1147           0 :             sal_Int32 nChPos = aIter.GetChgPos();
    1148             :             // the position at the end of the paragraph is COMPLETE_STRING and
    1149             :             // thus must be cut to the end of the actual string.
    1150             :             assert(nChPos != -1);
    1151           0 :             if (nChPos == -1 || nChPos == COMPLETE_STRING)
    1152             :             {
    1153           0 :                 nChPos = m_Text.getLength();
    1154             :             }
    1155             : 
    1156           0 :             nLen = nChPos - nBegin;
    1157           0 :             bFound = bLangOk && nLen > 0;
    1158           0 :             if (!bFound)
    1159             :             {
    1160             :                 // create SwPaM with mark & point spanning the attributed text
    1161             :                 //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different
    1162           0 :                 SwPaM aCurPaM( *this, nBegin );
    1163           0 :                 aCurPaM.SetMark();
    1164           0 :                 aCurPaM.GetPoint()->nContent = nBegin + nLen;
    1165             : 
    1166             :                 // check script type of selected text
    1167           0 :                 SwEditShell *pEditShell = GetDoc()->GetEditShell();
    1168           0 :                 pEditShell->Push();             // save current cursor on stack
    1169           0 :                 pEditShell->SetSelection( aCurPaM );
    1170           0 :                 bool bIsAsianScript = (SCRIPTTYPE_ASIAN == pEditShell->GetScriptType());
    1171           0 :                 pEditShell->Pop( sal_False );   // restore cursor from stack
    1172             : 
    1173           0 :                 if (!bIsAsianScript && rArgs.bAllowImplicitChangesForNotConvertibleText)
    1174             :                 {
    1175             :                     // Store for later use
    1176           0 :                     aImplicitChanges.push_back(ImplicitChangesRange(nBegin, nBegin+nLen));
    1177             :                 }
    1178           0 :                 nBegin = nChPos;    // start of next language portion
    1179             :             }
    1180           0 :         } while (!bFound && aIter.Next());  /* loop while nothing was found and still sth is left to be searched */
    1181             : 
    1182             :         // Apply implicit changes, if any, now that aIter is no longer used
    1183           0 :         for (size_t i = 0; i < aImplicitChanges.size(); ++i)
    1184             :         {
    1185           0 :             SwPaM aPaM( *this, aImplicitChanges[i].first );
    1186           0 :             aPaM.SetMark();
    1187           0 :             aPaM.GetPoint()->nContent = aImplicitChanges[i].second;
    1188           0 :             SetLanguageAndFont( aPaM, rArgs.nConvTargetLang, RES_CHRATR_CJK_LANGUAGE, rArgs.pTargetFont, RES_CHRATR_CJK_FONT );
    1189           0 :         }
    1190             : 
    1191             :     }
    1192             : 
    1193             :     // keep resulting text within selection / range of text to be converted
    1194           0 :     if (nBegin < nTextBegin)
    1195           0 :         nBegin = nTextBegin;
    1196           0 :     if (nBegin + nLen > nTextEnd)
    1197           0 :         nLen = nTextEnd - nBegin;
    1198           0 :     bool bInSelection = nBegin < nTextEnd;
    1199             : 
    1200           0 :     if (bFound && bInSelection)     // convertible text found within selection/range?
    1201             :     {
    1202             :         OSL_ENSURE( !m_Text.isEmpty(), "convertible text portion missing!" );
    1203           0 :         rArgs.aConvText     = m_Text.copy(nBegin, nLen);
    1204           0 :         rArgs.nConvTextLang = nLangFound;
    1205             : 
    1206             :         // position where to start looking in next iteration (after current ends)
    1207           0 :         rArgs.pStartNode = this;
    1208           0 :         rArgs.pStartIdx->Assign(this, nBegin + nLen );
    1209             :         // end position (when we have travelled over the whole document)
    1210           0 :         rArgs.pEndNode = this;
    1211           0 :         rArgs.pEndIdx->Assign(this, nBegin );
    1212             :     }
    1213             : 
    1214             :     // restore original text
    1215           0 :     if ( bRestoreString )
    1216             :     {
    1217           0 :         m_Text = aOldTxt;
    1218             :     }
    1219             : 
    1220           0 :     return rArgs.aConvText.isEmpty() ? 0 : 1;
    1221             : }
    1222             : 
    1223             : // Die Aehnlichkeiten zu SwTxtNode::Spell sind beabsichtigt ...
    1224             : // ACHTUNG: Ev. Bugs in beiden Routinen fixen!
    1225        1808 : SwRect SwTxtFrm::_AutoSpell( const SwCntntNode* pActNode, const SwViewOption& rViewOpt, sal_Int32 nActPos )
    1226             : {
    1227        1808 :     SwRect aRect;
    1228             : #if OSL_DEBUG_LEVEL > 1
    1229             :     static bool bStop = false;
    1230             :     if ( bStop )
    1231             :         return aRect;
    1232             : #endif
    1233             :     // Die Aehnlichkeiten zu SwTxtNode::Spell sind beabsichtigt ...
    1234             :     // ACHTUNG: Ev. Bugs in beiden Routinen fixen!
    1235        1808 :     SwTxtNode *pNode = GetTxtNode();
    1236        1808 :     if( pNode != pActNode || !nActPos )
    1237         822 :         nActPos = COMPLETE_STRING;
    1238             : 
    1239        1808 :     SwAutoCompleteWord& rACW = SwDoc::GetAutoCompleteWords();
    1240             : 
    1241             :     // modify string according to redline information and hidden text
    1242        1808 :     const OUString aOldTxt( pNode->GetTxt() );
    1243        3616 :     OUStringBuffer buf(pNode->m_Text);
    1244             :     const bool bRestoreString = lcl_MaskRedlinesAndHiddenText( *pNode, buf,
    1245        1808 :                 0, pNode->GetTxt().getLength()) > 0;
    1246        1808 :     if (bRestoreString)
    1247             :     {   // ??? UGLY: is it really necessary to modify m_Text here?
    1248           0 :         pNode->m_Text = buf.makeStringAndClear();
    1249             :     }
    1250             : 
    1251             :     // a change of data indicates that at least one word has been modified
    1252        1808 :     const bool bRedlineChg = (pNode->GetTxt().getStr() != aOldTxt.getStr());
    1253             : 
    1254        1808 :     sal_Int32 nBegin = 0;
    1255        1808 :     sal_Int32 nEnd = pNode->GetTxt().getLength();
    1256        1808 :     sal_Int32 nInsertPos = 0;
    1257        1808 :     sal_Int32 nChgStart = COMPLETE_STRING;
    1258        1808 :     sal_Int32 nChgEnd = 0;
    1259        1808 :     sal_Int32 nInvStart = COMPLETE_STRING;
    1260        1808 :     sal_Int32 nInvEnd = 0;
    1261             : 
    1262        2816 :     const bool bAddAutoCmpl = pNode->IsAutoCompleteWordDirty() &&
    1263        2816 :                                   rViewOpt.IsAutoCompleteWords();
    1264             : 
    1265        1808 :     if( pNode->GetWrong() )
    1266             :     {
    1267         998 :         nBegin = pNode->GetWrong()->GetBeginInv();
    1268         998 :         if( COMPLETE_STRING != nBegin )
    1269             :         {
    1270         998 :             nEnd = std::max(pNode->GetWrong()->GetEndInv(), pNode->GetTxt().getLength());
    1271             :         }
    1272             : 
    1273             :         // get word around nBegin, we start at nBegin - 1
    1274         998 :         if ( COMPLETE_STRING != nBegin )
    1275             :         {
    1276         998 :             if ( nBegin )
    1277          57 :                 --nBegin;
    1278             : 
    1279         998 :             LanguageType eActLang = pNode->GetLang( nBegin );
    1280             :             Boundary aBound =
    1281        2994 :                 g_pBreakIt->GetBreakIter()->getWordBoundary( pNode->GetTxt(), nBegin,
    1282         998 :                     g_pBreakIt->GetLocale( eActLang ),
    1283        1996 :                     WordType::DICTIONARY_WORD, sal_True );
    1284         998 :             nBegin = aBound.startPos;
    1285             :         }
    1286             : 
    1287             :         // get the position in the wrong list
    1288         998 :         nInsertPos = pNode->GetWrong()->GetWrongPos( nBegin );
    1289             : 
    1290             :         // sometimes we have to skip one entry
    1291        1014 :         if( nInsertPos < pNode->GetWrong()->Count() &&
    1292          32 :             nBegin == pNode->GetWrong()->Pos( nInsertPos ) +
    1293          16 :                       pNode->GetWrong()->Len( nInsertPos ) )
    1294           0 :                 nInsertPos++;
    1295             :     }
    1296             : 
    1297        1808 :     bool bFresh = nBegin < nEnd;
    1298             : 
    1299        1808 :     if( nBegin < nEnd )
    1300             :     {
    1301             :         //! register listener to LinguServiceEvents now in order to get
    1302             :         //! notified about relevant changes in the future
    1303        1260 :         SwModule *pModule = SW_MOD();
    1304        1260 :         if (!pModule->GetLngSvcEvtListener().is())
    1305           9 :             pModule->CreateLngSvcEvtListener();
    1306             : 
    1307        1260 :         uno::Reference< XSpellChecker1 > xSpell( ::GetSpellChecker() );
    1308        1260 :         SwDoc* pDoc = pNode->GetDoc();
    1309             : 
    1310        1260 :         SwScanner aScanner( *pNode, pNode->GetTxt(), 0, ModelToViewHelper(),
    1311        3780 :                             WordType::DICTIONARY_WORD, nBegin, nEnd);
    1312             : 
    1313       11904 :         while( aScanner.NextWord() )
    1314             :         {
    1315        9384 :             const OUString& rWord = aScanner.GetWord();
    1316        9384 :             nBegin = aScanner.GetBegin();
    1317        9384 :             sal_Int32 nLen = aScanner.GetLen();
    1318             : 
    1319             :             // get next language for next word, consider language attributes
    1320             :             // within the word
    1321        9384 :             LanguageType eActLang = aScanner.GetCurrentLanguage();
    1322             : 
    1323        9384 :             sal_Bool bSpell = xSpell.is() ? xSpell->hasLanguage( eActLang ) : sal_False;
    1324        9384 :             if( bSpell && !rWord.isEmpty() )
    1325             :             {
    1326             :                 // check for: bAlter => xHyphWord.is()
    1327             :                 OSL_ENSURE(!bSpell || xSpell.is(), "NULL pointer");
    1328             : 
    1329        9361 :                 if( !xSpell->isValid( rWord, eActLang, Sequence< PropertyValue >() ) )
    1330             :                 {
    1331        1119 :                     sal_Int32 nSmartTagStt = nBegin;
    1332        1119 :                     sal_Int32 nDummy = 1;
    1333        1119 :                     if ( !pNode->GetSmartTags() || !pNode->GetSmartTags()->InWrongWord( nSmartTagStt, nDummy ) )
    1334             :                     {
    1335        1119 :                         if( !pNode->GetWrong() )
    1336             :                         {
    1337          57 :                             pNode->SetWrong( new SwWrongList( WRONGLIST_SPELL ) );
    1338          57 :                             pNode->GetWrong()->SetInvalid( 0, nEnd );
    1339             :                         }
    1340        1119 :                         if( pNode->GetWrong()->Fresh( nChgStart, nChgEnd,
    1341        1119 :                             nBegin, nLen, nInsertPos, nActPos ) )
    1342         305 :                             pNode->GetWrong()->Insert( OUString(), 0, nBegin, nLen, nInsertPos++ );
    1343             :                         else
    1344             :                         {
    1345         814 :                             nInvStart = nBegin;
    1346         814 :                             nInvEnd = nBegin + nLen;
    1347             :                         }
    1348             :                     }
    1349             :                 }
    1350        8242 :                 else if( bAddAutoCmpl && rACW.GetMinWordLen() <= rWord.getLength() )
    1351             :                 {
    1352        4490 :                     if ( bRedlineChg )
    1353             :                     {
    1354           0 :                         OUString rNewWord( rWord );
    1355           0 :                         rACW.InsertWord( rNewWord, *pDoc );
    1356             :                     }
    1357             :                     else
    1358        4490 :                         rACW.InsertWord( rWord, *pDoc );
    1359             :                 }
    1360             :             }
    1361        1260 :         }
    1362             :     }
    1363             : 
    1364             :     // reset original text
    1365             :     // i63141 before calling GetCharRect(..) with formatting!
    1366        1808 :     if ( bRestoreString )
    1367             :     {
    1368           0 :         pNode->m_Text = aOldTxt;
    1369             :     }
    1370        1808 :     if( pNode->GetWrong() )
    1371             :     {
    1372        1055 :         if( bFresh )
    1373             :             pNode->GetWrong()->Fresh( nChgStart, nChgEnd,
    1374        1055 :                                       nEnd, 0, nInsertPos, nActPos );
    1375             : 
    1376             :         // Calculate repaint area:
    1377             : 
    1378        1055 :         if( nChgStart < nChgEnd )
    1379             :         {
    1380          53 :             aRect = lcl_CalculateRepaintRect( *this, nChgStart, nChgEnd );
    1381             :         }
    1382             : 
    1383        1055 :         pNode->GetWrong()->SetInvalid( nInvStart, nInvEnd );
    1384        1055 :         pNode->SetWrongDirty( COMPLETE_STRING != pNode->GetWrong()->GetBeginInv() );
    1385        1055 :         if( !pNode->GetWrong()->Count() && ! pNode->IsWrongDirty() )
    1386         180 :             pNode->SetWrong( NULL );
    1387             :     }
    1388             :     else
    1389         753 :         pNode->SetWrongDirty( false );
    1390             : 
    1391        1808 :     if( bAddAutoCmpl )
    1392        1008 :         pNode->SetAutoCompleteWordDirty( false );
    1393             : 
    1394        3616 :     return aRect;
    1395             : }
    1396             : 
    1397             : /** Function: SmartTagScan
    1398             : 
    1399             :     Function scans words in current text and checks them in the
    1400             :     smarttag libraries. If the check returns true to bounds of the
    1401             :     recognized words are stored into a list that is used later for drawing
    1402             :     the underline.
    1403             : 
    1404             :     @param pActNode ???
    1405             :     @param nActPos ???
    1406             :     @return SwRect Repaint area
    1407             : */
    1408           0 : SwRect SwTxtFrm::SmartTagScan( SwCntntNode* /*pActNode*/, sal_Int32 /*nActPos*/ )
    1409             : {
    1410           0 :     SwRect aRet;
    1411           0 :     SwTxtNode *pNode = GetTxtNode();
    1412           0 :     const OUString& rText = pNode->GetTxt();
    1413             : 
    1414             :     // Iterate over language portions
    1415           0 :     SmartTagMgr& rSmartTagMgr = SwSmartTagMgr::Get();
    1416             : 
    1417           0 :     SwWrongList* pSmartTagList = pNode->GetSmartTags();
    1418             : 
    1419           0 :     sal_Int32 nBegin = 0;
    1420           0 :     sal_Int32 nEnd = rText.getLength();
    1421             : 
    1422           0 :     if ( pSmartTagList )
    1423             :     {
    1424           0 :         if ( pSmartTagList->GetBeginInv() != COMPLETE_STRING )
    1425             :         {
    1426           0 :             nBegin = pSmartTagList->GetBeginInv();
    1427           0 :             nEnd = std::min( pSmartTagList->GetEndInv(), rText.getLength() );
    1428             : 
    1429           0 :             if ( nBegin < nEnd )
    1430             :             {
    1431           0 :                 const LanguageType aCurrLang = pNode->GetLang( nBegin );
    1432           0 :                 const com::sun::star::lang::Locale aCurrLocale = g_pBreakIt->GetLocale( aCurrLang );
    1433           0 :                 nBegin = g_pBreakIt->GetBreakIter()->beginOfSentence( rText, nBegin, aCurrLocale );
    1434           0 :                 nEnd = g_pBreakIt->GetBreakIter()->endOfSentence(rText, nEnd, aCurrLocale);
    1435           0 :                 if (nEnd > rText.getLength() || nEnd < 0)
    1436           0 :                     nEnd = rText.getLength();
    1437             :             }
    1438             :         }
    1439             :     }
    1440             : 
    1441           0 :     const sal_uInt16 nNumberOfEntries = pSmartTagList ? pSmartTagList->Count() : 0;
    1442           0 :     sal_uInt16 nNumberOfRemovedEntries = 0;
    1443           0 :     sal_uInt16 nNumberOfInsertedEntries = 0;
    1444             : 
    1445             :     // clear smart tag list between nBegin and nEnd:
    1446           0 :     if ( 0 != nNumberOfEntries )
    1447             :     {
    1448           0 :         sal_Int32 nChgStart = COMPLETE_STRING;
    1449           0 :         sal_Int32 nChgEnd = 0;
    1450           0 :         const sal_uInt16 nCurrentIndex = pSmartTagList->GetWrongPos( nBegin );
    1451           0 :         pSmartTagList->Fresh( nChgStart, nChgEnd, nBegin, nEnd - nBegin, nCurrentIndex, COMPLETE_STRING );
    1452           0 :         nNumberOfRemovedEntries = nNumberOfEntries - pSmartTagList->Count();
    1453             :     }
    1454             : 
    1455           0 :     if ( nBegin < nEnd )
    1456             :     {
    1457             :         // Expand the string:
    1458           0 :         const ModelToViewHelper aConversionMap(*pNode /*TODO - replace or expand fields for smart tags?*/);
    1459           0 :         OUString aExpandText = aConversionMap.getViewText();
    1460             : 
    1461             :         // Ownership ov ConversionMap is passed to SwXTextMarkup object!
    1462             :         uno::Reference<text::XTextMarkup> const xTextMarkup =
    1463           0 :              new SwXTextMarkup(pNode, aConversionMap);
    1464             : 
    1465           0 :         com::sun::star::uno::Reference< ::com::sun::star::frame::XController > xController = pNode->GetDoc()->GetDocShell()->GetController();
    1466             : 
    1467           0 :         SwPosition start(*pNode, nBegin);
    1468           0 :         SwPosition end  (*pNode, nEnd);
    1469           0 :         Reference< ::com::sun::star::text::XTextRange > xRange = SwXTextRange::CreateXTextRange(*pNode->GetDoc(), start, &end);
    1470             : 
    1471           0 :         rSmartTagMgr.RecognizeTextRange(xRange, xTextMarkup, xController);
    1472             : 
    1473           0 :         sal_Int32 nLangBegin = nBegin;
    1474           0 :         sal_Int32 nLangEnd = nEnd;
    1475             : 
    1476             :         // smart tag recognition has to be done for each language portion:
    1477           0 :         SwLanguageIterator aIter( *pNode, nLangBegin );
    1478             : 
    1479           0 :         do
    1480             :         {
    1481           0 :             const LanguageType nLang = aIter.GetLanguage();
    1482           0 :             const com::sun::star::lang::Locale aLocale = g_pBreakIt->GetLocale( nLang );
    1483           0 :             nLangEnd = std::min<sal_Int32>( nEnd, aIter.GetChgPos() );
    1484             : 
    1485           0 :             const sal_Int32 nExpandBegin = aConversionMap.ConvertToViewPosition( nLangBegin );
    1486           0 :             const sal_Int32 nExpandEnd   = aConversionMap.ConvertToViewPosition( nLangEnd );
    1487             : 
    1488           0 :             rSmartTagMgr.RecognizeString(aExpandText, xTextMarkup, xController, aLocale, nExpandBegin, nExpandEnd - nExpandBegin );
    1489             : 
    1490           0 :             nLangBegin = nLangEnd;
    1491             :         }
    1492           0 :         while ( aIter.Next() && nLangEnd < nEnd );
    1493             : 
    1494           0 :         pSmartTagList = pNode->GetSmartTags();
    1495             : 
    1496           0 :         const sal_uInt16 nNumberOfEntriesAfterRecognize = pSmartTagList ? pSmartTagList->Count() : 0;
    1497           0 :         nNumberOfInsertedEntries = nNumberOfEntriesAfterRecognize - ( nNumberOfEntries - nNumberOfRemovedEntries );
    1498             :     }
    1499             : 
    1500           0 :     if( pSmartTagList )
    1501             :     {
    1502             :         // Update WrongList stuff
    1503           0 :         pSmartTagList->SetInvalid( COMPLETE_STRING, 0 );
    1504           0 :         pNode->SetSmartTagDirty( COMPLETE_STRING != pSmartTagList->GetBeginInv() );
    1505             : 
    1506           0 :         if( !pSmartTagList->Count() && !pNode->IsSmartTagDirty() )
    1507           0 :             pNode->SetSmartTags( NULL );
    1508             : 
    1509             :         // Calculate repaint area:
    1510             : #if OSL_DEBUG_LEVEL > 1
    1511             :         const sal_uInt16 nNumberOfEntriesAfterRecognize2 = pSmartTagList->Count();
    1512             :         (void) nNumberOfEntriesAfterRecognize2;
    1513             : #endif
    1514           0 :         if ( nBegin < nEnd && ( 0 != nNumberOfRemovedEntries ||
    1515             :                                 0 != nNumberOfInsertedEntries ) )
    1516             :         {
    1517           0 :             aRet = lcl_CalculateRepaintRect( *this, nBegin, nEnd );
    1518             :         }
    1519             :     }
    1520             :     else
    1521           0 :         pNode->SetSmartTagDirty( false );
    1522             : 
    1523           0 :     return aRet;
    1524             : }
    1525             : 
    1526             : // Wird vom CollectAutoCmplWords gerufen
    1527           3 : void SwTxtFrm::CollectAutoCmplWrds( SwCntntNode* pActNode, sal_Int32 nActPos )
    1528             : {
    1529           3 :     SwTxtNode *pNode = GetTxtNode();
    1530           3 :     if( pNode != pActNode || !nActPos )
    1531           3 :         nActPos = COMPLETE_STRING;
    1532             : 
    1533           3 :     SwDoc* pDoc = pNode->GetDoc();
    1534           3 :     SwAutoCompleteWord& rACW = SwDoc::GetAutoCompleteWords();
    1535             : 
    1536           3 :     sal_Int32  nBegin = 0;
    1537           3 :     sal_Int32  nEnd = pNode->GetTxt().getLength();
    1538             :     sal_Int32  nLen;
    1539           3 :     bool bACWDirty = false, bAnyWrd = false;
    1540             : 
    1541           3 :     if( nBegin < nEnd )
    1542             :     {
    1543           3 :         sal_uInt16 nCnt = 200;
    1544           3 :         SwScanner aScanner( *pNode, pNode->GetTxt(), 0, ModelToViewHelper(),
    1545           6 :                             WordType::DICTIONARY_WORD, nBegin, nEnd );
    1546           6 :         while( aScanner.NextWord() )
    1547             :         {
    1548           0 :             nBegin = aScanner.GetBegin();
    1549           0 :             nLen = aScanner.GetLen();
    1550           0 :             if( rACW.GetMinWordLen() <= nLen )
    1551             :             {
    1552           0 :                 const OUString& rWord = aScanner.GetWord();
    1553             : 
    1554           0 :                 if( nActPos < nBegin || ( nBegin + nLen ) < nActPos )
    1555             :                 {
    1556           0 :                     if( rACW.GetMinWordLen() <= rWord.getLength() )
    1557           0 :                         rACW.InsertWord( rWord, *pDoc );
    1558           0 :                     bAnyWrd = true;
    1559             :                 }
    1560             :                 else
    1561           0 :                     bACWDirty = true;
    1562             :             }
    1563           0 :             if( !--nCnt )
    1564             :             {
    1565           0 :                 if ( Application::AnyInput( VCL_INPUT_ANY ) )
    1566           3 :                     return;
    1567           0 :                 nCnt = 100;
    1568             :             }
    1569           3 :         }
    1570             :     }
    1571             : 
    1572           3 :     if( bAnyWrd && !bACWDirty )
    1573           0 :         pNode->SetAutoCompleteWordDirty( false );
    1574             : }
    1575             : 
    1576             : /** Findet den TxtFrm und sucht dessen CalcHyph */
    1577           0 : bool SwTxtNode::Hyphenate( SwInterHyphInfo &rHyphInf )
    1578             : {
    1579             :     // Abkuerzung: am Absatz ist keine Sprache eingestellt:
    1580           0 :     if ( LANGUAGE_NONE == sal_uInt16( GetSwAttrSet().GetLanguage().GetLanguage() )
    1581           0 :          && USHRT_MAX == GetLang(0, m_Text.getLength()))
    1582             :     {
    1583           0 :         if( !rHyphInf.IsCheck() )
    1584           0 :             rHyphInf.SetNoLang( true );
    1585           0 :         return false;
    1586             :     }
    1587             : 
    1588           0 :     if( pLinguNode != this )
    1589             :     {
    1590           0 :         pLinguNode = this;
    1591           0 :         pLinguFrm = (SwTxtFrm*)getLayoutFrm( GetDoc()->GetCurrentLayout(), (Point*)(rHyphInf.GetCrsrPos()) );
    1592             :     }
    1593           0 :     SwTxtFrm *pFrm = pLinguFrm;
    1594           0 :     if( pFrm )
    1595           0 :         pFrm = &(pFrm->GetFrmAtOfst( rHyphInf.nStart ));
    1596             :     else
    1597             :     {
    1598             :         // 4935: Seit der Trennung ueber Sonderbereiche sind Faelle
    1599             :         // moeglich, in denen kein Frame zum Node vorliegt.
    1600             :         // Also keinOSL_ENSURE
    1601             :         OSL_ENSURE( pFrm, "!SwTxtNode::Hyphenate: can't find any frame" );
    1602           0 :         return false;
    1603             :     }
    1604             : 
    1605           0 :     while( pFrm )
    1606             :     {
    1607           0 :         if( pFrm->Hyphenate( rHyphInf ) )
    1608             :         {
    1609             :             // Das Layout ist nicht robust gegen "Direktformatierung"
    1610             :             // (7821, 7662, 7408); vgl. layact.cxx,
    1611             :             // SwLayAction::_TurboAction(), if( !pCnt->IsValid() ...
    1612           0 :             pFrm->SetCompletePaint();
    1613           0 :             return true;
    1614             :         }
    1615           0 :         pFrm = (SwTxtFrm*)(pFrm->GetFollow());
    1616           0 :         if( pFrm )
    1617             :         {
    1618           0 :             rHyphInf.nEnd = rHyphInf.nEnd - (pFrm->GetOfst() - rHyphInf.nStart);
    1619           0 :             rHyphInf.nStart = pFrm->GetOfst();
    1620             :         }
    1621             :     }
    1622           0 :     return false;
    1623             : }
    1624             : 
    1625             : namespace
    1626             : {
    1627          22 :     struct swTransliterationChgData
    1628             :     {
    1629             :         sal_Int32               nStart;
    1630             :         sal_Int32               nLen;
    1631             :         OUString                sChanged;
    1632             :         Sequence< sal_Int32 >   aOffsets;
    1633             :     };
    1634             : }
    1635             : 
    1636             : // change text to Upper/Lower/Hiragana/Katagana/...
    1637           6 : void SwTxtNode::TransliterateText(
    1638             :     utl::TransliterationWrapper& rTrans,
    1639             :     sal_Int32 nStt, sal_Int32 nEnd,
    1640             :     SwUndoTransliterate* pUndo )
    1641             : {
    1642           6 :     if (nStt < nEnd && g_pBreakIt->GetBreakIter().is())
    1643             :     {
    1644             :         // since we don't use Hiragana/Katakana or half-width/full-width transliterations here
    1645             :         // it is fine to use ANYWORD_IGNOREWHITESPACES. (ANY_WORD btw is broken and will
    1646             :         // occasionaly miss words in consecutive sentences). Also with ANYWORD_IGNOREWHITESPACES
    1647             :         // text like 'just-in-time' will be converted to 'Just-In-Time' which seems to be the
    1648             :         // proper thing to do.
    1649           6 :         const sal_Int16 nWordType = WordType::ANYWORD_IGNOREWHITESPACES;
    1650             : 
    1651             :         // In order to have less trouble with changing text size, e.g. because
    1652             :         // of ligatures or German small sz being resolved, we need to process
    1653             :         // the text replacements from end to start.
    1654             :         // This way the offsets for the yet to be changed words will be
    1655             :         // left unchanged by the already replaced text.
    1656             :         // For this we temporarily save the changes to be done in this vector
    1657           6 :         std::vector< swTransliterationChgData >   aChanges;
    1658          12 :         swTransliterationChgData                  aChgData;
    1659             : 
    1660           6 :         if (rTrans.getType() == (sal_uInt32)TransliterationModulesExtra::TITLE_CASE)
    1661             :         {
    1662             :             // for 'capitalize every word' we need to iterate over each word
    1663             : 
    1664           1 :             Boundary aSttBndry;
    1665           1 :             Boundary aEndBndry;
    1666           2 :             aSttBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
    1667           1 :                         GetTxt(), nStt,
    1668           1 :                         g_pBreakIt->GetLocale( GetLang( nStt ) ),
    1669             :                         nWordType,
    1670           2 :                         sal_True /*prefer forward direction*/);
    1671           2 :             aEndBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
    1672           1 :                         GetTxt(), nEnd,
    1673           1 :                         g_pBreakIt->GetLocale( GetLang( nEnd ) ),
    1674             :                         nWordType,
    1675           2 :                         sal_False /*prefer backward direction*/);
    1676             : 
    1677             :             // prevent backtracking to the previous word if selection is at word boundary
    1678           1 :             if (aSttBndry.endPos <= nStt)
    1679             :             {
    1680           0 :                 aSttBndry = g_pBreakIt->GetBreakIter()->nextWord(
    1681           0 :                         GetTxt(), aSttBndry.endPos,
    1682           0 :                         g_pBreakIt->GetLocale( GetLang( aSttBndry.endPos ) ),
    1683           0 :                         nWordType);
    1684             :             }
    1685             :             // prevent advancing to the next word if selection is at word boundary
    1686           1 :             if (aEndBndry.startPos >= nEnd)
    1687             :             {
    1688           0 :                 aEndBndry = g_pBreakIt->GetBreakIter()->previousWord(
    1689           0 :                         GetTxt(), aEndBndry.startPos,
    1690           0 :                         g_pBreakIt->GetLocale( GetLang( aEndBndry.startPos ) ),
    1691           0 :                         nWordType);
    1692             :             }
    1693             : 
    1694           1 :             Boundary aCurWordBndry( aSttBndry );
    1695           3 :             while (aCurWordBndry.startPos <= aEndBndry.startPos)
    1696             :             {
    1697           1 :                 nStt = aCurWordBndry.startPos;
    1698           1 :                 nEnd = aCurWordBndry.endPos;
    1699           1 :                 const sal_Int32 nLen = nEnd - nStt;
    1700             :                 OSL_ENSURE( nLen > 0, "invalid word length of 0" );
    1701             : 
    1702           1 :                 Sequence <sal_Int32> aOffsets;
    1703             :                 OUString const sChgd( rTrans.transliterate(
    1704           2 :                             GetTxt(), GetLang(nStt), nStt, nLen, &aOffsets) );
    1705             : 
    1706             :                 assert(nStt < m_Text.getLength());
    1707           1 :                 if (0 != rtl_ustr_shortenedCompare_WithLength(
    1708           2 :                             m_Text.getStr() + nStt, m_Text.getLength() - nStt,
    1709           3 :                             sChgd.getStr(), sChgd.getLength(), nLen))
    1710             :                 {
    1711           1 :                     aChgData.nStart     = nStt;
    1712           1 :                     aChgData.nLen       = nLen;
    1713           1 :                     aChgData.sChanged   = sChgd;
    1714           1 :                     aChgData.aOffsets   = aOffsets;
    1715           1 :                     aChanges.push_back( aChgData );
    1716             :                 }
    1717             : 
    1718           2 :                 aCurWordBndry = g_pBreakIt->GetBreakIter()->nextWord(
    1719           1 :                         GetTxt(), nEnd,
    1720           1 :                         g_pBreakIt->GetLocale( GetLang( nEnd ) ),
    1721           2 :                         nWordType);
    1722           1 :             }
    1723             :         }
    1724           5 :         else if (rTrans.getType() == (sal_uInt32)TransliterationModulesExtra::SENTENCE_CASE)
    1725             :         {
    1726             :             // for 'sentence case' we need to iterate sentence by sentence
    1727             : 
    1728           2 :             sal_Int32 nLastStart = g_pBreakIt->GetBreakIter()->beginOfSentence(
    1729           1 :                     GetTxt(), nEnd,
    1730           2 :                     g_pBreakIt->GetLocale( GetLang( nEnd ) ) );
    1731           2 :             sal_Int32 nLastEnd = g_pBreakIt->GetBreakIter()->endOfSentence(
    1732           1 :                     GetTxt(), nLastStart,
    1733           2 :                     g_pBreakIt->GetLocale( GetLang( nLastStart ) ) );
    1734             : 
    1735             :             // extend nStt, nEnd to the current sentence boundaries
    1736           2 :             sal_Int32 nCurrentStart = g_pBreakIt->GetBreakIter()->beginOfSentence(
    1737           1 :                     GetTxt(), nStt,
    1738           2 :                     g_pBreakIt->GetLocale( GetLang( nStt ) ) );
    1739           2 :             sal_Int32 nCurrentEnd = g_pBreakIt->GetBreakIter()->endOfSentence(
    1740           1 :                     GetTxt(), nCurrentStart,
    1741           2 :                     g_pBreakIt->GetLocale( GetLang( nCurrentStart ) ) );
    1742             : 
    1743             :             // prevent backtracking to the previous sentence if selection starts at end of a sentence
    1744           1 :             if (nCurrentEnd <= nStt)
    1745             :             {
    1746             :                 // now nCurrentStart is probably located on a non-letter word. (unless we
    1747             :                 // are in Asian text with no spaces...)
    1748             :                 // Thus to get the real sentence start we should locate the next real word,
    1749             :                 // that is one found by DICTIONARY_WORD
    1750           0 :                 i18n::Boundary aBndry = g_pBreakIt->GetBreakIter()->nextWord(
    1751           0 :                         GetTxt(), nCurrentEnd,
    1752           0 :                         g_pBreakIt->GetLocale( GetLang( nCurrentEnd ) ),
    1753           0 :                         i18n::WordType::DICTIONARY_WORD);
    1754             : 
    1755             :                 // now get new current sentence boundaries
    1756           0 :                 nCurrentStart = g_pBreakIt->GetBreakIter()->beginOfSentence(
    1757           0 :                         GetTxt(), aBndry.startPos,
    1758           0 :                         g_pBreakIt->GetLocale( GetLang( aBndry.startPos) ) );
    1759           0 :                 nCurrentEnd = g_pBreakIt->GetBreakIter()->endOfSentence(
    1760           0 :                         GetTxt(), nCurrentStart,
    1761           0 :                         g_pBreakIt->GetLocale( GetLang( nCurrentStart) ) );
    1762             :             }
    1763             :             // prevent advancing to the next sentence if selection ends at start of a sentence
    1764           1 :             if (nLastStart >= nEnd)
    1765             :             {
    1766             :                 // now nCurrentStart is probably located on a non-letter word. (unless we
    1767             :                 // are in Asian text with no spaces...)
    1768             :                 // Thus to get the real sentence start we should locate the previous real word,
    1769             :                 // that is one found by DICTIONARY_WORD
    1770           0 :                 i18n::Boundary aBndry = g_pBreakIt->GetBreakIter()->previousWord(
    1771           0 :                         GetTxt(), nLastStart,
    1772           0 :                         g_pBreakIt->GetLocale( GetLang( nLastStart) ),
    1773           0 :                         i18n::WordType::DICTIONARY_WORD);
    1774           0 :                 nLastEnd = g_pBreakIt->GetBreakIter()->endOfSentence(
    1775           0 :                         GetTxt(), aBndry.startPos,
    1776           0 :                         g_pBreakIt->GetLocale( GetLang( aBndry.startPos) ) );
    1777           0 :                 if (nCurrentEnd > nLastEnd)
    1778           0 :                     nCurrentEnd = nLastEnd;
    1779             :             }
    1780             : 
    1781           3 :             while (nCurrentStart < nLastEnd)
    1782             :             {
    1783           1 :                 sal_Int32 nLen = nCurrentEnd - nCurrentStart;
    1784             :                 OSL_ENSURE( nLen > 0, "invalid word length of 0" );
    1785             : 
    1786           1 :                 Sequence <sal_Int32> aOffsets;
    1787           1 :                 OUString const sChgd( rTrans.transliterate(GetTxt(),
    1788           3 :                     GetLang(nCurrentStart), nCurrentStart, nLen, &aOffsets) );
    1789             : 
    1790             :                 assert(nStt < m_Text.getLength());
    1791           1 :                 if (0 != rtl_ustr_shortenedCompare_WithLength(
    1792           2 :                             m_Text.getStr() + nStt, m_Text.getLength() - nStt,
    1793           3 :                             sChgd.getStr(), sChgd.getLength(), nLen))
    1794             :                 {
    1795           1 :                     aChgData.nStart     = nCurrentStart;
    1796           1 :                     aChgData.nLen       = nLen;
    1797           1 :                     aChgData.sChanged   = sChgd;
    1798           1 :                     aChgData.aOffsets   = aOffsets;
    1799           1 :                     aChanges.push_back( aChgData );
    1800             :                 }
    1801             : 
    1802           1 :                 Boundary aFirstWordBndry;
    1803           2 :                 aFirstWordBndry = g_pBreakIt->GetBreakIter()->nextWord(
    1804           1 :                         GetTxt(), nCurrentEnd,
    1805           1 :                         g_pBreakIt->GetLocale( GetLang( nCurrentEnd ) ),
    1806           2 :                         nWordType);
    1807           1 :                 nCurrentStart = aFirstWordBndry.startPos;
    1808           2 :                 nCurrentEnd = g_pBreakIt->GetBreakIter()->endOfSentence(
    1809           1 :                         GetTxt(), nCurrentStart,
    1810           2 :                         g_pBreakIt->GetLocale( GetLang( nCurrentStart ) ) );
    1811           1 :             }
    1812             :         }
    1813             :         else
    1814             :         {
    1815             :             // here we may transliterate over complete language portions...
    1816             : 
    1817             :             SwLanguageIterator* pIter;
    1818           4 :             if( rTrans.needLanguageForTheMode() )
    1819           3 :                 pIter = new SwLanguageIterator( *this, nStt );
    1820             :             else
    1821           1 :                 pIter = 0;
    1822             : 
    1823           4 :             sal_Int32 nEndPos = 0;
    1824           4 :             sal_uInt16 nLang = LANGUAGE_NONE;
    1825           4 :             do {
    1826           4 :                 if( pIter )
    1827             :                 {
    1828           3 :                     nLang = pIter->GetLanguage();
    1829           3 :                     nEndPos = pIter->GetChgPos();
    1830           3 :                     if( nEndPos > nEnd )
    1831           0 :                         nEndPos = nEnd;
    1832             :                 }
    1833             :                 else
    1834             :                 {
    1835           1 :                     nLang = LANGUAGE_SYSTEM;
    1836           1 :                     nEndPos = nEnd;
    1837             :                 }
    1838           4 :                 const sal_Int32 nLen = nEndPos - nStt;
    1839             : 
    1840           4 :                 Sequence <sal_Int32> aOffsets;
    1841             :                 OUString const sChgd( rTrans.transliterate(
    1842           8 :                             m_Text, nLang, nStt, nLen, &aOffsets) );
    1843             : 
    1844             :                 assert(nStt < m_Text.getLength());
    1845           4 :                 if (0 != rtl_ustr_shortenedCompare_WithLength(
    1846           8 :                             m_Text.getStr() + nStt, m_Text.getLength() - nStt,
    1847          12 :                             sChgd.getStr(), sChgd.getLength(), nLen))
    1848             :                 {
    1849           3 :                     aChgData.nStart     = nStt;
    1850           3 :                     aChgData.nLen       = nLen;
    1851           3 :                     aChgData.sChanged   = sChgd;
    1852           3 :                     aChgData.aOffsets   = aOffsets;
    1853           3 :                     aChanges.push_back( aChgData );
    1854             :                 }
    1855             : 
    1856           8 :                 nStt = nEndPos;
    1857           4 :             } while( nEndPos < nEnd && pIter && pIter->Next() );
    1858           4 :             delete pIter;
    1859             :         }
    1860             : 
    1861           6 :         if (!aChanges.empty())
    1862             :         {
    1863             :             // now apply the changes from end to start to leave the offsets of the
    1864             :             // yet unchanged text parts remain the same.
    1865           5 :             size_t nSum(0);
    1866          10 :             for (size_t i = 0; i < aChanges.size(); ++i)
    1867             :             {   // check this here since AddChanges cannot be moved below
    1868             :                 // call to ReplaceTextOnly
    1869             :                 swTransliterationChgData & rData =
    1870           5 :                     aChanges[ aChanges.size() - 1 - i ];
    1871           5 :                 nSum += rData.sChanged.getLength() - rData.nLen;
    1872           5 :                 if (nSum > static_cast<size_t>(GetSpaceLeft()))
    1873             :                 {
    1874             :                     SAL_WARN("sw.core", "SwTxtNode::ReplaceTextOnly: "
    1875             :                             "node text with insertion > node capacity.");
    1876           6 :                     return;
    1877             :                 }
    1878           5 :                 if (pUndo)
    1879           0 :                     pUndo->AddChanges( *this, rData.nStart, rData.nLen, rData.aOffsets );
    1880           5 :                 ReplaceTextOnly( rData.nStart, rData.nLen, rData.sChanged, rData.aOffsets );
    1881             :             }
    1882           6 :         }
    1883             :     }
    1884             : }
    1885             : 
    1886           5 : void SwTxtNode::ReplaceTextOnly( sal_Int32 nPos, sal_Int32 nLen,
    1887             :                                 const OUString & rText,
    1888             :                                 const Sequence<sal_Int32>& rOffsets )
    1889             : {
    1890             :     assert(rText.getLength() - nLen <= GetSpaceLeft());
    1891             : 
    1892           5 :     m_Text = m_Text.replaceAt(nPos, nLen, rText);
    1893             : 
    1894           5 :     sal_Int32 nTLen = rText.getLength();
    1895           5 :     const sal_Int32* pOffsets = rOffsets.getConstArray();
    1896             :     // now look for no 1-1 mapping -> move the indizies!
    1897           5 :     sal_Int32 nMyOff = nPos;
    1898          35 :     for( sal_Int32 nI = 0; nI < nTLen; ++nI )
    1899             :     {
    1900          30 :         const sal_Int32 nOff = pOffsets[ nI ];
    1901          30 :         if( nOff < nMyOff )
    1902             :         {
    1903             :             // something is inserted
    1904           0 :             sal_Int32 nCnt = 1;
    1905           0 :             while( nI + nCnt < nTLen && nOff == pOffsets[ nI + nCnt ] )
    1906           0 :                 ++nCnt;
    1907             : 
    1908           0 :             Update( SwIndex( this, nMyOff ), nCnt, false );
    1909           0 :             nMyOff = nOff;
    1910             :             //nMyOff -= nCnt;
    1911           0 :             nI += nCnt - 1;
    1912             :         }
    1913          30 :         else if( nOff > nMyOff )
    1914             :         {
    1915             :             // something is deleted
    1916           0 :             Update( SwIndex( this, nMyOff+1 ), nOff - nMyOff, true );
    1917           0 :             nMyOff = nOff;
    1918             :         }
    1919          30 :         ++nMyOff;
    1920             :     }
    1921           5 :     if( nMyOff < nLen )
    1922             :         // something is deleted at the end
    1923           0 :         Update( SwIndex( this, nMyOff ), nLen - nMyOff, true );
    1924             : 
    1925             :     // notify the layout!
    1926           5 :     SwDelTxt aDelHint( nPos, nTLen );
    1927           5 :     NotifyClients( 0, &aDelHint );
    1928             : 
    1929          10 :     SwInsTxt aHint( nPos, nTLen );
    1930          10 :     NotifyClients( 0, &aHint );
    1931           5 : }
    1932             : 
    1933             : // the return values allows us to see if we did the heavy-
    1934             : // lifting required to actually break and count the words.
    1935        4674 : bool SwTxtNode::CountWords( SwDocStat& rStat,
    1936             :                             sal_Int32 nStt, sal_Int32 nEnd ) const
    1937             : {
    1938        4674 :     if( nStt > nEnd )
    1939             :     {   // bad call
    1940           0 :         return false;
    1941             :     }
    1942        4674 :     if (IsInRedlines())
    1943             :     {   //not counting txtnodes used to hold deleted redline content
    1944           1 :         return false;
    1945             :     }
    1946        4673 :     bool bCountAll = ( (0 == nStt) && (GetTxt().getLength() == nEnd) );
    1947        4673 :     ++rStat.nAllPara; // #i93174#: count _all_ paragraphs
    1948        4673 :     if ( IsHidden() )
    1949             :     {   // not counting hidden paras
    1950           2 :         return false;
    1951             :     }
    1952             :     // count words in numbering string if started at beginning of para:
    1953        4671 :     bool bCountNumbering = nStt == 0;
    1954        4671 :     bool bHasBullet = false, bHasNumbering = false;
    1955        4671 :     OUString sNumString;
    1956        4671 :     if (bCountNumbering)
    1957             :     {
    1958        4671 :         sNumString = GetNumString();
    1959        4671 :         bHasNumbering = !sNumString.isEmpty();
    1960        4671 :         if (!bHasNumbering)
    1961        3728 :             bHasBullet = HasBullet();
    1962        4671 :         bCountNumbering = bHasNumbering || bHasBullet;
    1963             :     }
    1964             : 
    1965        4671 :     if( nStt == nEnd && !bCountNumbering)
    1966             :     {   // unnumbered empty node or empty selection
    1967        2110 :         return false;
    1968             :     }
    1969             : 
    1970             :     // count of non-empty paras
    1971        2561 :     ++rStat.nPara;
    1972             : 
    1973             :     // Shortcut when counting whole paragraph and current count is clean
    1974        2561 :     if ( bCountAll && !IsWordCountDirty() )
    1975             :     {
    1976             :         // accumulate into DocStat record to return the values
    1977        1688 :         rStat.nWord += GetParaNumberOfWords();
    1978        1688 :         rStat.nAsianWord += GetParaNumberOfAsianWords();
    1979        1688 :         rStat.nChar += GetParaNumberOfChars();
    1980        1688 :         rStat.nCharExcludingSpaces += GetParaNumberOfCharsExcludingSpaces();
    1981        1688 :         return false;
    1982             :     }
    1983             : 
    1984             :     // ConversionMap to expand fields, remove invisible and redline deleted text for scanner
    1985        1746 :     const ModelToViewHelper aConversionMap(*this, EXPANDFIELDS | EXPANDFOOTNOTE | HIDEINVISIBLE | HIDEREDLINED);
    1986        1746 :     OUString aExpandText = aConversionMap.getViewText();
    1987             : 
    1988         873 :     if (aExpandText.isEmpty() && !bCountNumbering)
    1989             :     {
    1990         308 :         return false;
    1991             :     }
    1992             : 
    1993             :     // map start and end points onto the ConversionMap
    1994         565 :     const sal_Int32 nExpandBegin = aConversionMap.ConvertToViewPosition( nStt );
    1995         565 :     const sal_Int32 nExpandEnd   = aConversionMap.ConvertToViewPosition( nEnd );
    1996             : 
    1997             :     //do the count
    1998             :     // all counts exclude hidden paras and hidden+redlined within para
    1999             :     // definition of space/white chars in SwScanner (and BreakIter!)
    2000             :     // uses both u_isspace and BreakIter getWordBoundary in SwScanner
    2001         565 :     sal_uInt32 nTmpWords = 0;        // count of all words
    2002         565 :     sal_uInt32 nTmpAsianWords = 0;   //count of all Asian codepoints
    2003         565 :     sal_uInt32 nTmpChars = 0;        // count of all chars
    2004         565 :     sal_uInt32 nTmpCharsExcludingSpaces = 0;  // all non-white chars
    2005             : 
    2006             :     // count words in masked and expanded text:
    2007         565 :     if (!aExpandText.isEmpty())
    2008             :     {
    2009         564 :         if (g_pBreakIt->GetBreakIter().is())
    2010             :         {
    2011             :             // zero is NULL for pLanguage -----------v               last param = true for clipping
    2012             :             SwScanner aScanner( *this, aExpandText, 0, aConversionMap, i18n::WordType::WORD_COUNT,
    2013         564 :                                 nExpandBegin, nExpandEnd, true );
    2014             : 
    2015             :             // used to filter out scanner returning almost empty strings (len=1; unichar=0x0001)
    2016        1128 :             const OUString aBreakWord( CH_TXTATR_BREAKWORD );
    2017             : 
    2018        9470 :             while ( aScanner.NextWord() )
    2019             :             {
    2020        8342 :                 if( !aExpandText.match(aBreakWord, aScanner.GetBegin() ))
    2021             :                 {
    2022        8342 :                     ++nTmpWords;
    2023        8342 :                     const OUString &rWord = aScanner.GetWord();
    2024        8342 :                     if (g_pBreakIt->GetBreakIter()->getScriptType(rWord, 0) == i18n::ScriptType::ASIAN)
    2025          44 :                         ++nTmpAsianWords;
    2026        8342 :                     nTmpCharsExcludingSpaces += g_pBreakIt->getGraphemeCount(rWord);
    2027             :                 }
    2028             :             }
    2029             : 
    2030        1128 :             nTmpCharsExcludingSpaces += aScanner.getOverriddenDashCount();
    2031             :         }
    2032             : 
    2033         564 :         nTmpChars = g_pBreakIt->getGraphemeCount(aExpandText, nExpandBegin, nExpandEnd);
    2034             :     }
    2035             : 
    2036             :     // no nTmpCharsExcludingSpaces adjust needed neither for blanked out MaskedChars
    2037             :     // nor for mid-word selection - set scanner bClip = true at creation
    2038             : 
    2039             :     // count outline number label - ? no expansion into map
    2040             :     // always counts all of number-ish label
    2041         565 :     if (bHasNumbering) // count words in numbering string
    2042             :     {
    2043          24 :         LanguageType aLanguage = GetLang( 0 );
    2044             : 
    2045             :         SwScanner aScanner( *this, sNumString, &aLanguage, ModelToViewHelper(),
    2046          24 :                             i18n::WordType::WORD_COUNT, 0, sNumString.getLength(), true );
    2047             : 
    2048          72 :         while ( aScanner.NextWord() )
    2049             :         {
    2050          24 :             ++nTmpWords;
    2051          24 :             const OUString &rWord = aScanner.GetWord();
    2052          24 :             if (g_pBreakIt->GetBreakIter()->getScriptType(rWord, 0) == i18n::ScriptType::ASIAN)
    2053           0 :                 ++nTmpAsianWords;
    2054          24 :             nTmpCharsExcludingSpaces += g_pBreakIt->getGraphemeCount(rWord);
    2055             :         }
    2056             : 
    2057          24 :         nTmpCharsExcludingSpaces += aScanner.getOverriddenDashCount();
    2058          24 :         nTmpChars += g_pBreakIt->getGraphemeCount(sNumString);
    2059             :     }
    2060         541 :     else if ( bHasBullet )
    2061             :     {
    2062          15 :         ++nTmpWords;
    2063          15 :         ++nTmpChars;
    2064          15 :         ++nTmpCharsExcludingSpaces;
    2065             :     }
    2066             : 
    2067             :     // If counting the whole para then update cached values and mark clean
    2068         565 :     if ( bCountAll )
    2069             :     {
    2070         565 :         SetParaNumberOfWords( nTmpWords );
    2071         565 :         SetParaNumberOfAsianWords( nTmpAsianWords );
    2072         565 :         SetParaNumberOfChars( nTmpChars );
    2073         565 :         SetParaNumberOfCharsExcludingSpaces( nTmpCharsExcludingSpaces );
    2074         565 :         SetWordCountDirty( false );
    2075             :     }
    2076             :     // accumulate into DocStat record to return the values
    2077         565 :     rStat.nWord += nTmpWords;
    2078         565 :     rStat.nAsianWord += nTmpAsianWords;
    2079         565 :     rStat.nChar += nTmpChars;
    2080         565 :     rStat.nCharExcludingSpaces += nTmpCharsExcludingSpaces;
    2081             : 
    2082        5236 :     return true;
    2083             : }
    2084             : 
    2085             : // Paragraph statistics start -->
    2086             : 
    2087             : struct SwParaIdleData_Impl
    2088             : {
    2089             :     SwWrongList* pWrong;                // for spell checking
    2090             :     SwGrammarMarkUp* pGrammarCheck;     // for grammar checking /  proof reading
    2091             :     SwWrongList* pSmartTags;
    2092             :     sal_uLong nNumberOfWords;
    2093             :     sal_uLong nNumberOfAsianWords;
    2094             :     sal_uLong nNumberOfChars;
    2095             :     sal_uLong nNumberOfCharsExcludingSpaces;
    2096             :     bool bWordCountDirty;
    2097             :     bool bWrongDirty;                   // Ist das Wrong-Feld auf invalid?
    2098             :     bool bGrammarCheckDirty;
    2099             :     bool bSmartTagDirty;
    2100             :     bool bAutoComplDirty;               // die ACompl-Liste muss angepasst werden
    2101             : 
    2102       37106 :     SwParaIdleData_Impl() :
    2103             :         pWrong              ( 0 ),
    2104             :         pGrammarCheck       ( 0 ),
    2105             :         pSmartTags          ( 0 ),
    2106             :         nNumberOfWords      ( 0 ),
    2107             :         nNumberOfAsianWords ( 0 ),
    2108             :         nNumberOfChars      ( 0 ),
    2109             :         nNumberOfCharsExcludingSpaces ( 0 ),
    2110             :         bWordCountDirty     ( true ),
    2111             :         bWrongDirty         ( true ),
    2112             :         bGrammarCheckDirty  ( true ),
    2113             :         bSmartTagDirty      ( true ),
    2114       37106 :         bAutoComplDirty     ( true ) {};
    2115             : };
    2116             : 
    2117       74164 : void SwTxtNode::InitSwParaStatistics( bool bNew )
    2118             : {
    2119       74164 :     if ( bNew )
    2120             :     {
    2121       37106 :         m_pParaIdleData_Impl = new SwParaIdleData_Impl;
    2122             :     }
    2123       37058 :     else if ( m_pParaIdleData_Impl )
    2124             :     {
    2125       37058 :         delete m_pParaIdleData_Impl->pWrong;
    2126       37058 :         delete m_pParaIdleData_Impl->pGrammarCheck;
    2127       37058 :         delete m_pParaIdleData_Impl->pSmartTags;
    2128       37058 :         delete m_pParaIdleData_Impl;
    2129       37058 :         m_pParaIdleData_Impl = 0;
    2130             :     }
    2131       74164 : }
    2132             : 
    2133       12034 : void SwTxtNode::SetWrong( SwWrongList* pNew, bool bDelete )
    2134             : {
    2135       12034 :     if ( m_pParaIdleData_Impl )
    2136             :     {
    2137       10322 :         if ( bDelete )
    2138             :         {
    2139        7872 :             delete m_pParaIdleData_Impl->pWrong;
    2140             :         }
    2141       10322 :         m_pParaIdleData_Impl->pWrong = pNew;
    2142             :     }
    2143       12034 : }
    2144             : 
    2145       54411 : SwWrongList* SwTxtNode::GetWrong()
    2146             : {
    2147       54411 :     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong : 0;
    2148             : }
    2149             : 
    2150             : // #i71360#
    2151           0 : const SwWrongList* SwTxtNode::GetWrong() const
    2152             : {
    2153           0 :     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong : 0;
    2154             : }
    2155             : 
    2156       11386 : void SwTxtNode::SetGrammarCheck( SwGrammarMarkUp* pNew, bool bDelete )
    2157             : {
    2158       11386 :     if ( m_pParaIdleData_Impl )
    2159             :     {
    2160        9674 :         if ( bDelete )
    2161             :         {
    2162        7239 :             delete m_pParaIdleData_Impl->pGrammarCheck;
    2163             :         }
    2164        9674 :         m_pParaIdleData_Impl->pGrammarCheck = pNew;
    2165             :     }
    2166       11386 : }
    2167             : 
    2168       34183 : SwGrammarMarkUp* SwTxtNode::GetGrammarCheck()
    2169             : {
    2170       34183 :     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pGrammarCheck : 0;
    2171             : }
    2172             : 
    2173       11386 : void SwTxtNode::SetSmartTags( SwWrongList* pNew, bool bDelete )
    2174             : {
    2175             :     OSL_ENSURE( !pNew || SwSmartTagMgr::Get().IsSmartTagsEnabled(),
    2176             :             "Weird - we have a smart tag list without any recognizers?" );
    2177             : 
    2178       11386 :     if ( m_pParaIdleData_Impl )
    2179             :     {
    2180        9674 :         if ( bDelete )
    2181             :         {
    2182        7239 :             delete m_pParaIdleData_Impl->pSmartTags;
    2183             :         }
    2184        9674 :         m_pParaIdleData_Impl->pSmartTags = pNew;
    2185             :     }
    2186       11386 : }
    2187             : 
    2188       37966 : SwWrongList* SwTxtNode::GetSmartTags()
    2189             : {
    2190       37966 :     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pSmartTags : 0;
    2191             : }
    2192             : 
    2193         565 : void SwTxtNode::SetParaNumberOfWords( sal_uLong nNew ) const
    2194             : {
    2195         565 :     if ( m_pParaIdleData_Impl )
    2196             :     {
    2197         565 :         m_pParaIdleData_Impl->nNumberOfWords = nNew;
    2198             :     }
    2199         565 : }
    2200             : 
    2201        1688 : sal_uLong SwTxtNode::GetParaNumberOfWords() const
    2202             : {
    2203        1688 :     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->nNumberOfWords : 0;
    2204             : }
    2205             : 
    2206         565 : void SwTxtNode::SetParaNumberOfAsianWords( sal_uLong nNew ) const
    2207             : {
    2208         565 :     if ( m_pParaIdleData_Impl )
    2209             :     {
    2210         565 :         m_pParaIdleData_Impl->nNumberOfAsianWords = nNew;
    2211             :     }
    2212         565 : }
    2213             : 
    2214        1688 : sal_uLong SwTxtNode::GetParaNumberOfAsianWords() const
    2215             : {
    2216        1688 :     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->nNumberOfAsianWords : 0;
    2217             : }
    2218             : 
    2219         565 : void SwTxtNode::SetParaNumberOfChars( sal_uLong nNew ) const
    2220             : {
    2221         565 :     if ( m_pParaIdleData_Impl )
    2222             :     {
    2223         565 :         m_pParaIdleData_Impl->nNumberOfChars = nNew;
    2224             :     }
    2225         565 : }
    2226             : 
    2227        1688 : sal_uLong SwTxtNode::GetParaNumberOfChars() const
    2228             : {
    2229        1688 :     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->nNumberOfChars : 0;
    2230             : }
    2231             : 
    2232       27309 : void SwTxtNode::SetWordCountDirty( bool bNew ) const
    2233             : {
    2234       27309 :     if ( m_pParaIdleData_Impl )
    2235             :     {
    2236       25597 :         m_pParaIdleData_Impl->bWordCountDirty = bNew;
    2237             :     }
    2238       27309 : }
    2239             : 
    2240        1688 : sal_uLong SwTxtNode::GetParaNumberOfCharsExcludingSpaces() const
    2241             : {
    2242        1688 :     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->nNumberOfCharsExcludingSpaces : 0;
    2243             : }
    2244             : 
    2245         565 : void SwTxtNode::SetParaNumberOfCharsExcludingSpaces( sal_uLong nNew ) const
    2246             : {
    2247         565 :     if ( m_pParaIdleData_Impl )
    2248             :     {
    2249         565 :         m_pParaIdleData_Impl->nNumberOfCharsExcludingSpaces = nNew;
    2250             :     }
    2251         565 : }
    2252             : 
    2253        3970 : bool SwTxtNode::IsWordCountDirty() const
    2254             : {
    2255        3970 :     return m_pParaIdleData_Impl && m_pParaIdleData_Impl->bWordCountDirty;
    2256             : }
    2257             : 
    2258       23218 : void SwTxtNode::SetWrongDirty( bool bNew ) const
    2259             : {
    2260       23218 :     if ( m_pParaIdleData_Impl )
    2261             :     {
    2262       21506 :         m_pParaIdleData_Impl->bWrongDirty = bNew;
    2263             :     }
    2264       23218 : }
    2265             : 
    2266       14691 : bool SwTxtNode::IsWrongDirty() const
    2267             : {
    2268       14691 :     return m_pParaIdleData_Impl && m_pParaIdleData_Impl->bWrongDirty;
    2269             : }
    2270             : 
    2271       21399 : void SwTxtNode::SetGrammarCheckDirty( bool bNew ) const
    2272             : {
    2273       21399 :     if ( m_pParaIdleData_Impl )
    2274             :     {
    2275       19687 :         m_pParaIdleData_Impl->bGrammarCheckDirty = bNew;
    2276             :     }
    2277       21399 : }
    2278             : 
    2279      105475 : bool SwTxtNode::IsGrammarCheckDirty() const
    2280             : {
    2281      105475 :     return m_pParaIdleData_Impl && m_pParaIdleData_Impl->bGrammarCheckDirty;
    2282             : }
    2283             : 
    2284       21399 : void SwTxtNode::SetSmartTagDirty( bool bNew ) const
    2285             : {
    2286       21399 :     if ( m_pParaIdleData_Impl )
    2287             :     {
    2288       19687 :         m_pParaIdleData_Impl->bSmartTagDirty = bNew;
    2289             :     }
    2290       21399 : }
    2291             : 
    2292       11862 : bool SwTxtNode::IsSmartTagDirty() const
    2293             : {
    2294       11862 :     return m_pParaIdleData_Impl && m_pParaIdleData_Impl->bSmartTagDirty;
    2295             : }
    2296             : 
    2297      127435 : void SwTxtNode::SetAutoCompleteWordDirty( bool bNew ) const
    2298             : {
    2299      127435 :     if ( m_pParaIdleData_Impl )
    2300             :     {
    2301      125723 :         m_pParaIdleData_Impl->bAutoComplDirty = bNew;
    2302             :     }
    2303      127435 : }
    2304             : 
    2305        4566 : bool SwTxtNode::IsAutoCompleteWordDirty() const
    2306             : {
    2307        4566 :     return m_pParaIdleData_Impl && m_pParaIdleData_Impl->bAutoComplDirty;
    2308             : }
    2309             : 
    2310             : // <-- Paragraph statistics end
    2311             : 
    2312             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10