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

Generated by: LCOV version 1.11