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

Generated by: LCOV version 1.10