| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_sw.hxx" |
| |
| // So kann man die Linguistik-Statistik ( (Tmp-Path)\swlingu.stk ) aktivieren: |
| //#define LINGU_STATISTIK |
| #ifdef LINGU_STATISTIK |
| #include <stdio.h> // in SwLinguStatistik::DTOR |
| #include <stdlib.h> // getenv() |
| #include <time.h> // clock() |
| #include <tools/stream.hxx> |
| #endif |
| |
| #include <hintids.hxx> |
| #include <vcl/svapp.hxx> |
| #include <svl/itemiter.hxx> |
| #include <editeng/splwrap.hxx> |
| #include <editeng/langitem.hxx> |
| #include <editeng/fontitem.hxx> |
| #include <editeng/scripttypeitem.hxx> |
| #include <editeng/hangulhanja.hxx> |
| #include <SwSmartTagMgr.hxx> |
| #include <linguistic/lngprops.hxx> |
| #include <unotools/transliterationwrapper.hxx> |
| #include <unotools/charclass.hxx> |
| #include <dlelstnr.hxx> |
| #include <swmodule.hxx> |
| #include <splargs.hxx> |
| #include <viewopt.hxx> |
| #include <acmplwrd.hxx> |
| #include <doc.hxx> // GetDoc() |
| #include <docsh.hxx> |
| #include <txtfld.hxx> |
| #include <fmtfld.hxx> |
| #include <txatbase.hxx> |
| #include <charatr.hxx> |
| #include <fldbas.hxx> |
| #include <pam.hxx> |
| #include <hints.hxx> |
| #include <ndtxt.hxx> |
| #include <txtfrm.hxx> |
| #include <SwGrammarMarkUp.hxx> |
| |
| #include <txttypes.hxx> |
| #include <breakit.hxx> |
| #include <crstate.hxx> |
| #include <UndoOverwrite.hxx> |
| #include <txatritr.hxx> |
| #include <redline.hxx> // SwRedline |
| #include <docary.hxx> // SwRedlineTbl |
| #include <scriptinfo.hxx> |
| #include <docstat.hxx> |
| #include <editsh.hxx> |
| #include <unotextmarkup.hxx> |
| #include <txtatr.hxx> |
| #include <fmtautofmt.hxx> |
| #include <istyleaccess.hxx> |
| |
| #include <unomid.h> |
| |
| #include <com/sun/star/beans/XPropertySet.hpp> |
| #include <com/sun/star/i18n/WordType.hdl> |
| #include <com/sun/star/i18n/ScriptType.hdl> |
| #include <com/sun/star/i18n/TransliterationModules.hpp> |
| #include <com/sun/star/i18n/TransliterationModulesExtra.hpp> |
| |
| #include <vector> |
| |
| #include <unotextrange.hxx> |
| |
| using rtl::OUString; |
| using namespace ::com::sun::star; |
| using namespace ::com::sun::star::frame; |
| using namespace ::com::sun::star::i18n; |
| using namespace ::com::sun::star::beans; |
| using namespace ::com::sun::star::uno; |
| using namespace ::com::sun::star::linguistic2; |
| using namespace ::com::sun::star::smarttags; |
| |
| // Wir ersparen uns in Hyphenate ein GetFrm() |
| // Achtung: in edlingu.cxx stehen die Variablen! |
| extern const SwTxtNode *pLinguNode; |
| extern SwTxtFrm *pLinguFrm; |
| |
| bool lcl_IsSkippableWhiteSpace( xub_Unicode cCh ) |
| { |
| return 0x3000 == cCh || |
| ' ' == cCh || |
| '\t' == cCh || |
| 0x0a == cCh; |
| } |
| |
| /* |
| * This has basically the same function as SwScriptInfo::MaskHiddenRanges, |
| * only for deleted redlines |
| */ |
| |
| sal_uInt16 lcl_MaskRedlines( const SwTxtNode& rNode, XubString& rText, |
| const xub_StrLen nStt, const xub_StrLen nEnd, |
| const xub_Unicode cChar ) |
| { |
| sal_uInt16 nNumOfMaskedRedlines = 0; |
| |
| const SwDoc& rDoc = *rNode.GetDoc(); |
| sal_uInt16 nAct = rDoc.GetRedlinePos( rNode, USHRT_MAX ); |
| |
| for ( ; nAct < rDoc.GetRedlineTbl().Count(); nAct++ ) |
| { |
| const SwRedline* pRed = rDoc.GetRedlineTbl()[ nAct ]; |
| |
| if ( pRed->Start()->nNode > rNode.GetIndex() ) |
| break; |
| |
| if( nsRedlineType_t::REDLINE_DELETE == pRed->GetType() ) |
| { |
| xub_StrLen nRedlineEnd; |
| xub_StrLen nRedlineStart; |
| |
| pRed->CalcStartEnd( rNode.GetIndex(), nRedlineStart, nRedlineEnd ); |
| |
| if ( nRedlineEnd < nStt || nRedlineStart > nEnd ) |
| continue; |
| |
| while ( nRedlineStart < nRedlineEnd && nRedlineStart < nEnd ) |
| { |
| if ( nRedlineStart >= nStt && nRedlineStart < nEnd ) |
| { |
| rText.SetChar( nRedlineStart, cChar ); |
| ++nNumOfMaskedRedlines; |
| } |
| ++nRedlineStart; |
| } |
| } |
| } |
| |
| return nNumOfMaskedRedlines; |
| } |
| |
| /* |
| * Used for spell checking. Deleted redlines and hidden characters are masked |
| */ |
| |
| sal_uInt16 lcl_MaskRedlinesAndHiddenText( const SwTxtNode& rNode, XubString& rText, |
| const xub_StrLen nStt, const xub_StrLen nEnd, |
| const xub_Unicode cChar = CH_TXTATR_INWORD, |
| bool bCheckShowHiddenChar = true ) |
| { |
| sal_uInt16 nRedlinesMasked = 0; |
| sal_uInt16 nHiddenCharsMasked = 0; |
| |
| const SwDoc& rDoc = *rNode.GetDoc(); |
| const bool bShowChg = 0 != IDocumentRedlineAccess::IsShowChanges( rDoc.GetRedlineMode() ); |
| |
| // If called from word count or from spell checking, deleted redlines |
| // should be masked: |
| if ( bShowChg ) |
| { |
| nRedlinesMasked = lcl_MaskRedlines( rNode, rText, nStt, nEnd, cChar ); |
| } |
| |
| const bool bHideHidden = !SW_MOD()->GetViewOption(rDoc.get(IDocumentSettingAccess::HTML_MODE))->IsShowHiddenChar(); |
| |
| // If called from word count, we want to mask the hidden ranges even |
| // if they are visible: |
| if ( !bCheckShowHiddenChar || bHideHidden ) |
| { |
| nHiddenCharsMasked = |
| SwScriptInfo::MaskHiddenRanges( rNode, rText, nStt, nEnd, cChar ); |
| } |
| |
| return nRedlinesMasked + nHiddenCharsMasked; |
| } |
| |
| /* |
| * Used for spell checking. Calculates a rectangle for repaint. |
| */ |
| |
| static SwRect lcl_CalculateRepaintRect( SwTxtFrm& rTxtFrm, xub_StrLen nChgStart, xub_StrLen nChgEnd ) |
| { |
| SwRect aRect; |
| |
| SwTxtNode *pNode = rTxtFrm.GetTxtNode(); |
| |
| SwNodeIndex aNdIdx( *pNode ); |
| SwPosition aPos( aNdIdx, SwIndex( pNode, nChgEnd ) ); |
| SwCrsrMoveState aTmpState( MV_NONE ); |
| aTmpState.b2Lines = sal_True; |
| rTxtFrm.GetCharRect( aRect, aPos, &aTmpState ); |
| // information about end of repaint area |
| Sw2LinesPos* pEnd2Pos = aTmpState.p2Lines; |
| |
| const SwTxtFrm *pEndFrm = &rTxtFrm; |
| |
| while( pEndFrm->HasFollow() && |
| nChgEnd >= pEndFrm->GetFollow()->GetOfst() ) |
| pEndFrm = pEndFrm->GetFollow(); |
| |
| if ( pEnd2Pos ) |
| { |
| // we are inside a special portion, take left border |
| SWRECTFN( pEndFrm ) |
| (aRect.*fnRect->fnSetTop)( (pEnd2Pos->aLine.*fnRect->fnGetTop)() ); |
| if ( pEndFrm->IsRightToLeft() ) |
| (aRect.*fnRect->fnSetLeft)( (pEnd2Pos->aPortion.*fnRect->fnGetLeft)() ); |
| else |
| (aRect.*fnRect->fnSetLeft)( (pEnd2Pos->aPortion.*fnRect->fnGetRight)() ); |
| (aRect.*fnRect->fnSetWidth)( 1 ); |
| (aRect.*fnRect->fnSetHeight)( (pEnd2Pos->aLine.*fnRect->fnGetHeight)() ); |
| delete pEnd2Pos; |
| } |
| |
| aTmpState.p2Lines = NULL; |
| SwRect aTmp; |
| aPos = SwPosition( aNdIdx, SwIndex( pNode, nChgStart ) ); |
| rTxtFrm.GetCharRect( aTmp, aPos, &aTmpState ); |
| |
| // i63141: GetCharRect(..) could cause a formatting, |
| // during the formatting SwTxtFrms could be joined, deleted, created... |
| // => we have to reinit pStartFrm and pEndFrm after the formatting |
| const SwTxtFrm* pStartFrm = &rTxtFrm; |
| while( pStartFrm->HasFollow() && |
| nChgStart >= pStartFrm->GetFollow()->GetOfst() ) |
| pStartFrm = pStartFrm->GetFollow(); |
| pEndFrm = pStartFrm; |
| while( pEndFrm->HasFollow() && |
| nChgEnd >= pEndFrm->GetFollow()->GetOfst() ) |
| pEndFrm = pEndFrm->GetFollow(); |
| |
| // information about start of repaint area |
| Sw2LinesPos* pSt2Pos = aTmpState.p2Lines; |
| if ( pSt2Pos ) |
| { |
| // we are inside a special portion, take right border |
| SWRECTFN( pStartFrm ) |
| (aTmp.*fnRect->fnSetTop)( (pSt2Pos->aLine.*fnRect->fnGetTop)() ); |
| if ( pStartFrm->IsRightToLeft() ) |
| (aTmp.*fnRect->fnSetLeft)( (pSt2Pos->aPortion.*fnRect->fnGetRight)() ); |
| else |
| (aTmp.*fnRect->fnSetLeft)( (pSt2Pos->aPortion.*fnRect->fnGetLeft)() ); |
| (aTmp.*fnRect->fnSetWidth)( 1 ); |
| (aTmp.*fnRect->fnSetHeight)( (pSt2Pos->aLine.*fnRect->fnGetHeight)() ); |
| delete pSt2Pos; |
| } |
| |
| sal_Bool bSameFrame = sal_True; |
| |
| if( rTxtFrm.HasFollow() ) |
| { |
| if( pEndFrm != pStartFrm ) |
| { |
| bSameFrame = sal_False; |
| SwRect aStFrm( pStartFrm->PaintArea() ); |
| { |
| SWRECTFN( pStartFrm ) |
| (aTmp.*fnRect->fnSetLeft)( (aStFrm.*fnRect->fnGetLeft)() ); |
| (aTmp.*fnRect->fnSetRight)( (aStFrm.*fnRect->fnGetRight)() ); |
| (aTmp.*fnRect->fnSetBottom)( (aStFrm.*fnRect->fnGetBottom)() ); |
| } |
| aStFrm = pEndFrm->PaintArea(); |
| { |
| SWRECTFN( pEndFrm ) |
| (aRect.*fnRect->fnSetTop)( (aStFrm.*fnRect->fnGetTop)() ); |
| (aRect.*fnRect->fnSetLeft)( (aStFrm.*fnRect->fnGetLeft)() ); |
| (aRect.*fnRect->fnSetRight)( (aStFrm.*fnRect->fnGetRight)() ); |
| } |
| aRect.Union( aTmp ); |
| while( sal_True ) |
| { |
| pStartFrm = pStartFrm->GetFollow(); |
| if( pStartFrm == pEndFrm ) |
| break; |
| aRect.Union( pStartFrm->PaintArea() ); |
| } |
| } |
| } |
| if( bSameFrame ) |
| { |
| SWRECTFN( pStartFrm ) |
| if( (aTmp.*fnRect->fnGetTop)() == (aRect.*fnRect->fnGetTop)() ) |
| (aRect.*fnRect->fnSetLeft)( (aTmp.*fnRect->fnGetLeft)() ); |
| else |
| { |
| SwRect aStFrm( pStartFrm->PaintArea() ); |
| (aRect.*fnRect->fnSetLeft)( (aStFrm.*fnRect->fnGetLeft)() ); |
| (aRect.*fnRect->fnSetRight)( (aStFrm.*fnRect->fnGetRight)() ); |
| (aRect.*fnRect->fnSetTop)( (aTmp.*fnRect->fnGetTop)() ); |
| } |
| |
| if( aTmp.Height() > aRect.Height() ) |
| aRect.Height( aTmp.Height() ); |
| } |
| |
| return aRect; |
| } |
| |
| /* |
| * Used for automatic styles. Used during RstAttr. |
| */ |
| |
| static bool lcl_HaveCommonAttributes( IStyleAccess& rStyleAccess, |
| const SfxItemSet* pSet1, |
| sal_uInt16 nWhichId, |
| const SfxItemSet& rSet2, |
| boost::shared_ptr<SfxItemSet>& pStyleHandle ) |
| { |
| bool bRet = false; |
| |
| SfxItemSet* pNewSet = 0; |
| |
| if ( !pSet1 ) |
| { |
| ASSERT( nWhichId, "lcl_HaveCommonAttributes not used correctly" ) |
| if ( SFX_ITEM_SET == rSet2.GetItemState( nWhichId, sal_False ) ) |
| { |
| pNewSet = rSet2.Clone( sal_True ); |
| pNewSet->ClearItem( nWhichId ); |
| } |
| } |
| else if ( pSet1->Count() ) |
| { |
| SfxItemIter aIter( *pSet1 ); |
| const SfxPoolItem* pItem = aIter.GetCurItem(); |
| while( sal_True ) |
| { |
| if ( SFX_ITEM_SET == rSet2.GetItemState( pItem->Which(), sal_False ) ) |
| { |
| if ( !pNewSet ) |
| pNewSet = rSet2.Clone( sal_True ); |
| pNewSet->ClearItem( pItem->Which() ); |
| } |
| |
| if( aIter.IsAtEnd() ) |
| break; |
| |
| pItem = aIter.NextItem(); |
| } |
| } |
| |
| if ( pNewSet ) |
| { |
| if ( pNewSet->Count() ) |
| pStyleHandle = rStyleAccess.getAutomaticStyle( *pNewSet, IStyleAccess::AUTO_STYLE_CHAR ); |
| delete pNewSet; |
| bRet = true; |
| } |
| |
| return bRet; |
| } |
| |
| inline sal_Bool InRange(xub_StrLen nIdx, xub_StrLen nStart, xub_StrLen nEnd) { |
| return ((nIdx >=nStart) && (nIdx <= nEnd)); |
| } |
| |
| /* |
| * void SwTxtNode::RstAttr(const SwIndex &rIdx, sal_uInt16 nLen) |
| * |
| * Deletes all attributes, starting at position rIdx, for length nLen. |
| */ |
| |
| /* 5 cases: |
| * 1) The attribute is completely in the deletion range: |
| * -> delete it |
| * 2) The end of the attribute is in the deletion range: |
| * -> delete it, then re-insert it with new end |
| * 3) The start of the attribute is in the deletion range: |
| * -> delete it, then re-insert it with new start |
| * 4) The attribute contains the deletion range: |
| * Split, i.e., |
| * -> Delete, re-insert from old start to start of deletion range |
| * -> insert new attribute from end of deletion range to old end |
| * 5) The attribute is outside the deletion range |
| * -> nothing to do |
| */ |
| |
| void SwTxtNode::RstTxtAttr( |
| const SwIndex &rIdx, |
| const xub_StrLen nLen, |
| const sal_uInt16 nWhich, |
| const SfxItemSet* pSet, |
| const sal_Bool bInclRefToxMark ) |
| { |
| if ( !GetpSwpHints() ) |
| return; |
| |
| xub_StrLen nStt = rIdx.GetIndex(); |
| xub_StrLen nEnd = nStt + nLen; |
| { |
| // enlarge range for the reset of text attributes in case of an overlapping input field |
| const SwTxtInputFld* pTxtInputFld = dynamic_cast<const SwTxtInputFld*>(GetTxtAttrAt( nStt, RES_TXTATR_INPUTFIELD, PARENT )); |
| if ( pTxtInputFld == NULL ) |
| { |
| pTxtInputFld = dynamic_cast<const SwTxtInputFld*>(GetTxtAttrAt(nEnd, RES_TXTATR_INPUTFIELD, PARENT )); |
| } |
| if ( pTxtInputFld != NULL ) |
| { |
| if ( nStt > *(pTxtInputFld->GetStart()) ) |
| { |
| nStt = *(pTxtInputFld->GetStart()); |
| } |
| if ( nEnd < *(pTxtInputFld->End()) ) |
| { |
| nEnd = *(pTxtInputFld->End()); |
| } |
| } |
| } |
| |
| bool bChanged = false; |
| |
| // nMin and nMax initialized to maximum / minimum (inverse) |
| xub_StrLen nMin = m_Text.Len(); |
| xub_StrLen nMax = nStt; |
| const bool bNoLen = nMin == 0; |
| |
| // We have to remember the "new" attributes, which have |
| // been introduced by splitting surrounding attributes (case 4). |
| // They may not be forgotten inside the "Forget" function |
| //std::vector< const SwTxtAttr* > aNewAttributes; |
| |
| // iterate over attribute array until start of attribute is behind deletion range |
| sal_uInt16 i = 0; |
| xub_StrLen nAttrStart; |
| SwTxtAttr *pHt = NULL; |
| while ( (i < m_pSwpHints->Count()) |
| && ( ( ( nAttrStart = *(*m_pSwpHints)[i]->GetStart()) < nEnd ) |
| || nLen==0 ) ) |
| { |
| pHt = m_pSwpHints->GetTextHint(i); |
| |
| // attributes without end stay in! |
| // but consider <bInclRefToxMark> used by Undo |
| xub_StrLen* const pAttrEnd = pHt->GetEnd(); |
| const bool bKeepAttrWithoutEnd = |
| pAttrEnd == NULL |
| && ( !bInclRefToxMark |
| || ( RES_TXTATR_REFMARK != pHt->Which() |
| && RES_TXTATR_TOXMARK != pHt->Which() |
| && RES_TXTATR_META != pHt->Which() |
| && RES_TXTATR_METAFIELD != pHt->Which() ) ); |
| if ( bKeepAttrWithoutEnd ) |
| { |
| |
| i++; |
| continue; |
| } |
| // attributes with content stay in |
| if ( pHt->HasContent() ) |
| { |
| ++i; |
| continue; |
| } |
| |
| // Default behavior is to process all attributes: |
| bool bSkipAttr = false;; |
| boost::shared_ptr<SfxItemSet> pStyleHandle; |
| |
| // 1. case: We want to reset only the attributes listed in pSet: |
| if ( pSet ) |
| { |
| bSkipAttr = SFX_ITEM_SET != pSet->GetItemState( pHt->Which(), sal_False ); |
| if ( bSkipAttr && RES_TXTATR_AUTOFMT == pHt->Which() ) |
| { |
| // if the current attribute is an autostyle, we have to check if the autostyle |
| // and pSet have any attributes in common. If so, pStyleHandle will contain |
| // a handle to AutoStyle / pSet: |
| bSkipAttr = !lcl_HaveCommonAttributes( getIDocumentStyleAccess(), pSet, 0, *static_cast<const SwFmtAutoFmt&>(pHt->GetAttr()).GetStyleHandle(), pStyleHandle ); |
| } |
| } |
| else if ( nWhich ) |
| { |
| // 2. case: We want to reset only the attributes with WhichId nWhich: |
| bSkipAttr = nWhich != pHt->Which(); |
| if ( bSkipAttr && RES_TXTATR_AUTOFMT == pHt->Which() ) |
| { |
| bSkipAttr = !lcl_HaveCommonAttributes( getIDocumentStyleAccess(), 0, nWhich, *static_cast<const SwFmtAutoFmt&>(pHt->GetAttr()).GetStyleHandle(), pStyleHandle ); |
| } |
| } |
| else if ( !bInclRefToxMark ) |
| { |
| // 3. case: Reset all attributes except from ref/toxmarks: |
| // skip hints with CH_TXTATR here |
| // (deleting those is ONLY allowed for UNDO!) |
| bSkipAttr = RES_TXTATR_REFMARK == pHt->Which() |
| || RES_TXTATR_TOXMARK == pHt->Which() |
| || RES_TXTATR_META == pHt->Which() |
| || RES_TXTATR_METAFIELD == pHt->Which(); |
| } |
| |
| if ( bSkipAttr ) |
| { |
| i++; |
| continue; |
| } |
| |
| |
| if( nStt <= nAttrStart ) // Faelle: 1,3,5 |
| { |
| const xub_StrLen nAttrEnd = pAttrEnd != NULL |
| ? *pAttrEnd |
| : nAttrStart; |
| if( nEnd > nAttrStart |
| || ( nEnd == nAttrEnd && nEnd == nAttrStart ) ) |
| { |
| // Faelle: 1,3 |
| if ( nMin > nAttrStart ) |
| nMin = nAttrStart; |
| if ( nMax < nAttrEnd ) |
| nMax = nAttrEnd; |
| // Falls wir nur ein nichtaufgespanntes Attribut entfernen, |
| // tun wir mal so, als ob sich nichts geaendert hat. |
| bChanged = bChanged || nEnd > nAttrStart || bNoLen; |
| if( nAttrEnd <= nEnd ) // Fall: 1 |
| { |
| m_pSwpHints->DeleteAtPos(i); |
| DestroyAttr( pHt ); |
| |
| if ( pStyleHandle.get() ) |
| { |
| SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(), |
| *pStyleHandle, nAttrStart, nAttrEnd ); |
| InsertHint( pNew, nsSetAttrMode::SETATTR_NOHINTADJUST ); |
| } |
| |
| // if the last attribute is a Field, the HintsArray is deleted! |
| if ( !m_pSwpHints ) |
| break; |
| |
| //JP 26.11.96: |
| // beim DeleteAtPos wird ein Resort ausgefuehrt!! |
| // darum muessen wir wieder bei 0 anfangen!!! |
| // ueber den Fall 3 koennen Attribute nach hinten |
| // verschoben worden sein; damit stimmt jetzt das i |
| // nicht mehr!!! |
| i = 0; |
| |
| continue; |
| } |
| else // Fall: 3 |
| { |
| m_pSwpHints->NoteInHistory( pHt ); |
| *pHt->GetStart() = nEnd; |
| m_pSwpHints->NoteInHistory( pHt, sal_True ); |
| |
| if ( pStyleHandle.get() && nAttrStart < nEnd ) |
| { |
| SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(), |
| *pStyleHandle, nAttrStart, nEnd ); |
| InsertHint( pNew, nsSetAttrMode::SETATTR_NOHINTADJUST ); |
| } |
| |
| bChanged = true; |
| } |
| } |
| } |
| else if ( pAttrEnd != NULL ) // Faelle: 2,4,5 |
| { |
| if( *pAttrEnd > nStt ) // Faelle: 2,4 |
| { |
| if( *pAttrEnd < nEnd ) // Fall: 2 |
| { |
| if ( nMin > nAttrStart ) |
| nMin = nAttrStart; |
| if ( nMax < *pAttrEnd ) |
| nMax = *pAttrEnd; |
| bChanged = true; |
| |
| const xub_StrLen nAttrEnd = *pAttrEnd; |
| |
| m_pSwpHints->NoteInHistory( pHt ); |
| *pAttrEnd = nStt; |
| m_pSwpHints->NoteInHistory( pHt, sal_True ); |
| |
| if ( pStyleHandle.get() ) |
| { |
| SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(), |
| *pStyleHandle, nStt, nAttrEnd ); |
| InsertHint( pNew, nsSetAttrMode::SETATTR_NOHINTADJUST ); |
| } |
| } |
| else if( nLen ) // Fall: 4 |
| { |
| // bei Lange 0 werden beide Hints vom Insert(Ht) |
| // wieder zu einem zusammengezogen !!!! |
| if ( nMin > nAttrStart ) |
| nMin = nAttrStart; |
| if ( nMax < *pAttrEnd ) |
| nMax = *pAttrEnd; |
| bChanged = true; |
| xub_StrLen nTmpEnd = *pAttrEnd; |
| m_pSwpHints->NoteInHistory( pHt ); |
| *pAttrEnd = nStt; |
| m_pSwpHints->NoteInHistory( pHt, sal_True ); |
| |
| if ( pStyleHandle.get() && nStt < nEnd ) |
| { |
| SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(), |
| *pStyleHandle, nStt, nEnd ); |
| InsertHint( pNew, nsSetAttrMode::SETATTR_NOHINTADJUST ); |
| } |
| |
| if( nEnd < nTmpEnd ) |
| { |
| SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(), |
| pHt->GetAttr(), nEnd, nTmpEnd ); |
| if ( pNew ) |
| { |
| SwTxtCharFmt* pCharFmt = dynamic_cast<SwTxtCharFmt*>(pHt); |
| if ( pCharFmt ) |
| static_cast<SwTxtCharFmt*>(pNew)->SetSortNumber( pCharFmt->GetSortNumber() ); |
| |
| InsertHint( pNew, |
| nsSetAttrMode::SETATTR_NOHINTADJUST ); |
| } |
| |
| |
| // jetzt kein i+1, weil das eingefuegte Attribut |
| // ein anderes auf die Position geschoben hat ! |
| continue; |
| } |
| } |
| } |
| } |
| |
| ++i; |
| } |
| |
| TryDeleteSwpHints(); |
| |
| if (bChanged) |
| { |
| if ( HasHints() ) |
| { |
| m_pSwpHints->Resort(); |
| } |
| |
| //TxtFrm's reagieren auf aHint, andere auf aNew |
| SwUpdateAttr aHint( |
| nMin, |
| nMax, |
| 0); |
| |
| NotifyClients( 0, &aHint ); |
| SwFmtChg aNew( GetFmtColl() ); |
| NotifyClients( 0, &aNew ); |
| } |
| } |
| |
| |
| |
| /************************************************************************* |
| * SwTxtNode::GetCurWord() |
| * |
| * Aktuelles Wort zurueckliefern: |
| * Wir suchen immer von links nach rechts, es wird also das Wort |
| * vor nPos gesucht. Es sei denn, wir befinden uns am Anfang des |
| * Absatzes, dann wird das erste Wort zurueckgeliefert. |
| * Wenn dieses erste Wort nur aus Whitespaces besteht, returnen wir |
| * einen leeren String. |
| *************************************************************************/ |
| |
| XubString SwTxtNode::GetCurWord( xub_StrLen nPos ) const |
| { |
| ASSERT( nPos <= m_Text.Len(), "SwTxtNode::GetCurWord: invalid index." ); |
| |
| if (!m_Text.Len()) |
| return m_Text; |
| |
| Boundary aBndry; |
| const uno::Reference< XBreakIterator > &rxBreak = pBreakIt->GetBreakIter(); |
| if (rxBreak.is()) |
| { |
| sal_Int16 nWordType = WordType::DICTIONARY_WORD; |
| lang::Locale aLocale( pBreakIt->GetLocale( GetLang( nPos ) ) ); |
| #ifdef DEBUG |
| sal_Bool bBegin = rxBreak->isBeginWord( m_Text, nPos, aLocale, nWordType ); |
| sal_Bool bEnd = rxBreak->isEndWord ( m_Text, nPos, aLocale, nWordType ); |
| (void)bBegin; |
| (void)bEnd; |
| #endif |
| aBndry = |
| rxBreak->getWordBoundary( m_Text, nPos, aLocale, nWordType, sal_True ); |
| |
| // if no word was found use previous word (if any) |
| if (aBndry.startPos == aBndry.endPos) |
| { |
| aBndry = rxBreak->previousWord( m_Text, nPos, aLocale, nWordType ); |
| } |
| } |
| |
| // check if word was found and if it uses a symbol font, if so |
| // enforce returning an empty string |
| if (aBndry.endPos != aBndry.startPos && IsSymbol( (xub_StrLen)aBndry.startPos )) |
| aBndry.endPos = aBndry.startPos; |
| |
| return m_Text.Copy( static_cast<xub_StrLen>(aBndry.startPos), |
| static_cast<xub_StrLen>(aBndry.endPos - aBndry.startPos) ); |
| } |
| |
| SwScanner::SwScanner( const SwTxtNode& rNd, const String& rTxt, const LanguageType* pLang, |
| const ModelToViewHelper::ConversionMap* pConvMap, |
| sal_uInt16 nType, xub_StrLen nStart, xub_StrLen nEnde, sal_Bool bClp ) |
| : rNode( rNd ), rText( rTxt), pLanguage( pLang ), pConversionMap( pConvMap ), nLen( 0 ), nWordType( nType ), bClip( bClp ) |
| { |
| ASSERT( rText.Len(), "SwScanner: EmptyString" ); |
| nStartPos = nBegin = nStart; |
| nEndPos = nEnde; |
| |
| if ( pLanguage ) |
| { |
| aCurrLang = *pLanguage; |
| } |
| else |
| { |
| ModelToViewHelper::ModelPosition aModelBeginPos = ModelToViewHelper::ConvertToModelPosition( pConversionMap, nBegin ); |
| const xub_StrLen nModelBeginPos = (xub_StrLen)aModelBeginPos.mnPos; |
| aCurrLang = rNd.GetLang( nModelBeginPos ); |
| } |
| } |
| |
| sal_Bool SwScanner::NextWord() |
| { |
| nBegin = nBegin + nLen; |
| Boundary aBound; |
| |
| CharClass& rCC = GetAppCharClass(); |
| lang::Locale aOldLocale = rCC.getLocale(); |
| |
| while ( true ) |
| { |
| // skip non-letter characters: |
| while ( nBegin < rText.Len() ) |
| { |
| if ( !lcl_IsSkippableWhiteSpace( rText.GetChar( nBegin ) ) ) |
| { |
| if ( !pLanguage ) |
| { |
| const sal_uInt16 nNextScriptType = pBreakIt->GetBreakIter()->getScriptType( rText, nBegin ); |
| ModelToViewHelper::ModelPosition aModelBeginPos = ModelToViewHelper::ConvertToModelPosition( pConversionMap, nBegin ); |
| const xub_StrLen nBeginModelPos = (xub_StrLen)aModelBeginPos.mnPos; |
| aCurrLang = rNode.GetLang( nBeginModelPos, 1, nNextScriptType ); |
| } |
| |
| if ( nWordType != i18n::WordType::WORD_COUNT ) |
| { |
| rCC.setLocale( pBreakIt->GetLocale( aCurrLang ) ); |
| if ( rCC.isLetterNumeric( rText.GetChar( nBegin ) ) ) |
| break; |
| } |
| else |
| break; |
| } |
| ++nBegin; |
| } |
| |
| if ( nBegin >= rText.Len() || nBegin >= nEndPos ) |
| return sal_False; |
| |
| // get the word boundaries |
| aBound = pBreakIt->GetBreakIter()->getWordBoundary( rText, nBegin, |
| pBreakIt->GetLocale( aCurrLang ), nWordType, sal_True ); |
| ASSERT( aBound.endPos >= aBound.startPos, "broken aBound result" ); |
| |
| //no word boundaries could be found |
| if(aBound.endPos == aBound.startPos) |
| return sal_False; |
| |
| //if a word before is found it has to be searched for the next |
| if(aBound.endPos == nBegin) |
| ++nBegin; |
| else |
| break; |
| } // end while( true ) |
| |
| rCC.setLocale( aOldLocale ); |
| |
| // #i89042, as discussed with HDU: don't evaluate script changes for word count. Use whole word. |
| if ( nWordType == i18n::WordType::WORD_COUNT ) |
| { |
| nBegin = Max( static_cast< xub_StrLen >(aBound.startPos), nBegin ); |
| nLen = 0; |
| if (static_cast< xub_StrLen >(aBound.endPos) > nBegin) |
| nLen = static_cast< xub_StrLen >(aBound.endPos) - nBegin; |
| } |
| else |
| { |
| // we have to differenciate between these cases: |
| if ( aBound.startPos <= nBegin ) |
| { |
| ASSERT( aBound.endPos >= nBegin, "Unexpected aBound result" ) |
| |
| // restrict boundaries to script boundaries and nEndPos |
| const sal_uInt16 nCurrScript = pBreakIt->GetBreakIter()->getScriptType( rText, nBegin ); |
| XubString aTmpWord = rText.Copy( nBegin, static_cast<xub_StrLen>(aBound.endPos - nBegin) ); |
| const sal_Int32 nScriptEnd = nBegin + |
| pBreakIt->GetBreakIter()->endOfScript( aTmpWord, 0, nCurrScript ); |
| const sal_Int32 nEnd = Min( aBound.endPos, nScriptEnd ); |
| |
| // restrict word start to last script change position |
| sal_Int32 nScriptBegin = 0; |
| if ( aBound.startPos < nBegin ) |
| { |
| // search from nBegin backwards until the next script change |
| aTmpWord = rText.Copy( static_cast<xub_StrLen>(aBound.startPos), |
| static_cast<xub_StrLen>(nBegin - aBound.startPos + 1) ); |
| nScriptBegin = aBound.startPos + |
| pBreakIt->GetBreakIter()->beginOfScript( aTmpWord, nBegin - aBound.startPos, |
| nCurrScript ); |
| } |
| |
| nBegin = (xub_StrLen)Max( aBound.startPos, nScriptBegin ); |
| nLen = (xub_StrLen)(nEnd - nBegin); |
| } |
| else |
| { |
| const sal_uInt16 nCurrScript = pBreakIt->GetBreakIter()->getScriptType( rText, aBound.startPos ); |
| XubString aTmpWord = rText.Copy( static_cast<xub_StrLen>(aBound.startPos), |
| static_cast<xub_StrLen>(aBound.endPos - aBound.startPos) ); |
| const sal_Int32 nScriptEnd = aBound.startPos + |
| pBreakIt->GetBreakIter()->endOfScript( aTmpWord, 0, nCurrScript ); |
| const sal_Int32 nEnd = Min( aBound.endPos, nScriptEnd ); |
| nBegin = (xub_StrLen)aBound.startPos; |
| nLen = (xub_StrLen)(nEnd - nBegin); |
| } |
| } |
| |
| // optionally clip the result of getWordBoundaries: |
| if ( bClip ) |
| { |
| aBound.startPos = Max( (xub_StrLen)aBound.startPos, nStartPos ); |
| aBound.endPos = Min( (xub_StrLen)aBound.endPos, nEndPos ); |
| nBegin = (xub_StrLen)aBound.startPos; |
| nLen = (xub_StrLen)(aBound.endPos - nBegin); |
| } |
| |
| if( ! nLen ) |
| return sal_False; |
| |
| aWord = rText.Copy( nBegin, nLen ); |
| |
| return sal_True; |
| } |
| |
| |
| sal_uInt16 SwTxtNode::Spell(SwSpellArgs* pArgs) |
| { |
| // Die Aehnlichkeiten zu SwTxtFrm::_AutoSpell sind beabsichtigt ... |
| // ACHTUNG: Ev. Bugs in beiden Routinen fixen! |
| |
| uno::Reference<beans::XPropertySet> xProp( GetLinguPropertySet() ); |
| |
| xub_StrLen nBegin, nEnd; |
| |
| // modify string according to redline information and hidden text |
| const XubString aOldTxt( m_Text ); |
| const bool bRestoreString = |
| lcl_MaskRedlinesAndHiddenText( *this, m_Text, 0, m_Text.Len() ) > 0; |
| |
| if ( pArgs->pStartNode != this ) |
| nBegin = 0; |
| else |
| nBegin = pArgs->pStartIdx->GetIndex(); |
| |
| nEnd = ( pArgs->pEndNode != this ) |
| ? m_Text.Len() |
| : pArgs->pEndIdx->GetIndex(); |
| |
| pArgs->xSpellAlt = NULL; |
| |
| // 4 cases: |
| // |
| // 1. IsWrongDirty = 0 and GetWrong = 0 |
| // Everything is checked and correct |
| // 2. IsWrongDirty = 0 and GetWrong = 1 |
| // Everything is checked and errors are identified in the wrong list |
| // 3. IsWrongDirty = 1 and GetWrong = 0 |
| // Nothing has been checked |
| // 4. IsWrongDirty = 1 and GetWrong = 1 |
| // Text has been checked but there is an invalid range in the wrong list |
| // |
| // Nothing has to be done for case 1. |
| if ( ( IsWrongDirty() || GetWrong() ) && m_Text.Len() ) |
| { |
| if ( nBegin > m_Text.Len() ) |
| { |
| nBegin = m_Text.Len(); |
| } |
| if ( nEnd > m_Text.Len() ) |
| { |
| nEnd = m_Text.Len(); |
| } |
| // |
| if(!IsWrongDirty()) |
| { |
| xub_StrLen nTemp = GetWrong()->NextWrong( nBegin ); |
| if(nTemp > nEnd) |
| { |
| // reset original text |
| if ( bRestoreString ) |
| { |
| m_Text = aOldTxt; |
| } |
| return 0; |
| } |
| if(nTemp > nBegin) |
| nBegin = nTemp; |
| |
| } |
| |
| // In case 2. we pass the wrong list to the scanned, because only |
| // the words in the wrong list have to be checked |
| SwScanner aScanner( *this, m_Text, 0, 0, |
| WordType::DICTIONARY_WORD, |
| nBegin, nEnd ); |
| while( !pArgs->xSpellAlt.is() && aScanner.NextWord() ) |
| { |
| const XubString& rWord = aScanner.GetWord(); |
| |
| // get next language for next word, consider language attributes |
| // within the word |
| LanguageType eActLang = aScanner.GetCurrentLanguage(); |
| |
| if( rWord.Len() > 0 && LANGUAGE_NONE != eActLang ) |
| { |
| if (pArgs->xSpeller.is()) |
| { |
| SvxSpellWrapper::CheckSpellLang( pArgs->xSpeller, eActLang ); |
| pArgs->xSpellAlt = pArgs->xSpeller->spell( rWord, eActLang, |
| Sequence< PropertyValue >() ); |
| } |
| if( (pArgs->xSpellAlt).is() ) |
| { |
| if( IsSymbol( aScanner.GetBegin() ) ) |
| { |
| pArgs->xSpellAlt = NULL; |
| } |
| else |
| { |
| // make sure the selection build later from the |
| // data below does not include footnotes and other |
| // "in word" character to the left and right in order |
| // to preserve those. Therefore count those "in words" |
| // in order to modify the selection accordingly. |
| const sal_Unicode* pChar = rWord.GetBuffer(); |
| xub_StrLen nLeft = 0; |
| while (pChar && *pChar++ == CH_TXTATR_INWORD) |
| ++nLeft; |
| pChar = rWord.Len() ? rWord.GetBuffer() + rWord.Len() - 1 : 0; |
| xub_StrLen nRight = 0; |
| while (pChar && *pChar-- == CH_TXTATR_INWORD) |
| ++nRight; |
| |
| pArgs->pStartNode = this; |
| pArgs->pEndNode = this; |
| pArgs->pStartIdx->Assign(this, aScanner.GetEnd() - nRight ); |
| pArgs->pEndIdx->Assign(this, aScanner.GetBegin() + nLeft ); |
| } |
| } |
| } |
| } |
| } |
| |
| // reset original text |
| if ( bRestoreString ) |
| { |
| m_Text = aOldTxt; |
| } |
| |
| return pArgs->xSpellAlt.is() ? 1 : 0; |
| } |
| |
| |
| void SwTxtNode::SetLanguageAndFont( const SwPaM &rPaM, |
| LanguageType nLang, sal_uInt16 nLangWhichId, |
| const Font *pFont, sal_uInt16 nFontWhichId ) |
| { |
| sal_uInt16 aRanges[] = { |
| nLangWhichId, nLangWhichId, |
| nFontWhichId, nFontWhichId, |
| 0, 0, 0 }; |
| if (!pFont) |
| aRanges[2] = aRanges[3] = 0; // clear entries with font WhichId |
| |
| SwEditShell *pEditShell = GetDoc()->GetEditShell(); |
| SfxItemSet aSet( pEditShell->GetAttrPool(), aRanges ); |
| aSet.Put( SvxLanguageItem( nLang, nLangWhichId ) ); |
| |
| DBG_ASSERT( pFont, "target font missing?" ); |
| if (pFont) |
| { |
| SvxFontItem aFontItem = (SvxFontItem&) aSet.Get( nFontWhichId ); |
| aFontItem.SetFamilyName( pFont->GetName()); |
| aFontItem.SetFamily( pFont->GetFamily()); |
| aFontItem.SetStyleName( pFont->GetStyleName()); |
| aFontItem.SetPitch( pFont->GetPitch()); |
| aFontItem.SetCharSet( pFont->GetCharSet() ); |
| aSet.Put( aFontItem ); |
| } |
| |
| GetDoc()->InsertItemSet( rPaM, aSet, 0 ); |
| // SetAttr( aSet ); <- Does not set language attribute of empty paragraphs correctly, |
| // <- because since there is no selection the flag to garbage |
| // <- collect all attributes is set, and therefore attributes spanned |
| // <- over empty selection are removed. |
| |
| } |
| |
| |
| sal_uInt16 SwTxtNode::Convert( SwConversionArgs &rArgs ) |
| { |
| // get range of text within node to be converted |
| // (either all the text or the the text within the selection |
| // when the conversion was started) |
| xub_StrLen nTextBegin, nTextEnd; |
| // |
| if ( rArgs.pStartNode != this ) |
| { |
| nTextBegin = 0; |
| } |
| else |
| nTextBegin = rArgs.pStartIdx->GetIndex(); |
| if (nTextBegin > m_Text.Len()) |
| { |
| nTextBegin = m_Text.Len(); |
| } |
| |
| nTextEnd = ( rArgs.pEndNode != this ) |
| ? m_Text.Len() |
| : ::std::min( rArgs.pEndIdx->GetIndex(), m_Text.Len() ); |
| |
| rArgs.aConvText = rtl::OUString(); |
| |
| // modify string according to redline information and hidden text |
| const XubString aOldTxt( m_Text ); |
| const bool bRestoreString = |
| lcl_MaskRedlinesAndHiddenText( *this, m_Text, 0, m_Text.Len() ) > 0; |
| |
| sal_Bool bFound = sal_False; |
| xub_StrLen nBegin = nTextBegin; |
| xub_StrLen nLen = 0; |
| LanguageType nLangFound = LANGUAGE_NONE; |
| if (!m_Text.Len()) |
| { |
| if (rArgs.bAllowImplicitChangesForNotConvertibleText) |
| { |
| // create SwPaM with mark & point spanning empty paragraph |
| //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different |
| SwPaM aCurPaM( *this, 0 ); |
| |
| SetLanguageAndFont( aCurPaM, |
| rArgs.nConvTargetLang, RES_CHRATR_CJK_LANGUAGE, |
| rArgs.pTargetFont, RES_CHRATR_CJK_FONT ); |
| } |
| } |
| else |
| { |
| SwLanguageIterator aIter( *this, nBegin ); |
| |
| // find non zero length text portion of appropriate language |
| do { |
| nLangFound = aIter.GetLanguage(); |
| sal_Bool bLangOk = (nLangFound == rArgs.nConvSrcLang) || |
| (editeng::HangulHanjaConversion::IsChinese( nLangFound ) && |
| editeng::HangulHanjaConversion::IsChinese( rArgs.nConvSrcLang )); |
| |
| xub_StrLen nChPos = aIter.GetChgPos(); |
| // the position at the end of the paragraph returns -1 |
| // which becomes 65535 when converted to xub_StrLen, |
| // and thus must be cut to the end of the actual string. |
| if (nChPos == (xub_StrLen) -1) |
| { |
| nChPos = m_Text.Len(); |
| } |
| |
| nLen = nChPos - nBegin; |
| bFound = bLangOk && nLen > 0; |
| if (!bFound) |
| { |
| // create SwPaM with mark & point spanning the attributed text |
| //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different |
| SwPaM aCurPaM( *this, nBegin ); |
| aCurPaM.SetMark(); |
| aCurPaM.GetPoint()->nContent = nBegin + nLen; |
| |
| // check script type of selected text |
| SwEditShell *pEditShell = GetDoc()->GetEditShell(); |
| pEditShell->Push(); // save current cursor on stack |
| pEditShell->SetSelection( aCurPaM ); |
| sal_Bool bIsAsianScript = (SCRIPTTYPE_ASIAN == pEditShell->GetScriptType()); |
| pEditShell->Pop( sal_False ); // restore cursor from stack |
| |
| if (!bIsAsianScript && rArgs.bAllowImplicitChangesForNotConvertibleText) |
| { |
| SetLanguageAndFont( aCurPaM, |
| rArgs.nConvTargetLang, RES_CHRATR_CJK_LANGUAGE, |
| rArgs.pTargetFont, RES_CHRATR_CJK_FONT ); |
| } |
| nBegin = nChPos; // start of next language portion |
| } |
| } while (!bFound && aIter.Next()); /* loop while nothing was found and still sth is left to be searched */ |
| } |
| |
| // keep resulting text within selection / range of text to be converted |
| if (nBegin < nTextBegin) |
| nBegin = nTextBegin; |
| if (nBegin + nLen > nTextEnd) |
| nLen = nTextEnd - nBegin; |
| sal_Bool bInSelection = nBegin < nTextEnd; |
| |
| if (bFound && bInSelection) // convertible text found within selection/range? |
| { |
| const XubString aTxtPortion = m_Text.Copy( nBegin, nLen ); |
| DBG_ASSERT( m_Text.Len() > 0, "convertible text portion missing!" ); |
| rArgs.aConvText = m_Text.Copy( nBegin, nLen ); |
| rArgs.nConvTextLang = nLangFound; |
| |
| // position where to start looking in next iteration (after current ends) |
| rArgs.pStartNode = this; |
| rArgs.pStartIdx->Assign(this, nBegin + nLen ); |
| // end position (when we have travelled over the whole document) |
| rArgs.pEndNode = this; |
| rArgs.pEndIdx->Assign(this, nBegin ); |
| } |
| |
| // restore original text |
| if ( bRestoreString ) |
| { |
| m_Text = aOldTxt; |
| } |
| |
| return rArgs.aConvText.getLength() ? 1 : 0; |
| } |
| |
| // Die Aehnlichkeiten zu SwTxtNode::Spell sind beabsichtigt ... |
| // ACHTUNG: Ev. Bugs in beiden Routinen fixen! |
| SwRect SwTxtFrm::_AutoSpell( const SwCntntNode* pActNode, const SwViewOption& rViewOpt, xub_StrLen nActPos ) |
| { |
| SwRect aRect; |
| #if OSL_DEBUG_LEVEL > 1 |
| static sal_Bool bStop = sal_False; |
| if ( bStop ) |
| return aRect; |
| #endif |
| // Die Aehnlichkeiten zu SwTxtNode::Spell sind beabsichtigt ... |
| // ACHTUNG: Ev. Bugs in beiden Routinen fixen! |
| SwTxtNode *pNode = GetTxtNode(); |
| if( pNode != pActNode || !nActPos ) |
| nActPos = STRING_LEN; |
| |
| SwAutoCompleteWord& rACW = SwDoc::GetAutoCompleteWords(); |
| |
| // modify string according to redline information and hidden text |
| const XubString aOldTxt( pNode->GetTxt() ); |
| const bool bRestoreString = |
| lcl_MaskRedlinesAndHiddenText( *pNode, pNode->m_Text, |
| 0, pNode->GetTxt().Len() ) > 0; |
| |
| // a change of data indicates that at least one word has been modified |
| const sal_Bool bRedlineChg = |
| ( pNode->GetTxt().GetBuffer() != aOldTxt.GetBuffer() ); |
| |
| xub_StrLen nBegin = 0; |
| xub_StrLen nEnd = pNode->GetTxt().Len(); |
| xub_StrLen nInsertPos = 0; |
| xub_StrLen nChgStart = STRING_LEN; |
| xub_StrLen nChgEnd = 0; |
| xub_StrLen nInvStart = STRING_LEN; |
| xub_StrLen nInvEnd = 0; |
| |
| const sal_Bool bAddAutoCmpl = pNode->IsAutoCompleteWordDirty() && |
| rViewOpt.IsAutoCompleteWords(); |
| |
| if( pNode->GetWrong() ) |
| { |
| nBegin = pNode->GetWrong()->GetBeginInv(); |
| if( STRING_LEN != nBegin ) |
| { |
| nEnd = pNode->GetWrong()->GetEndInv(); |
| if ( nEnd > pNode->GetTxt().Len() ) |
| { |
| nEnd = pNode->GetTxt().Len(); |
| } |
| } |
| |
| // get word around nBegin, we start at nBegin - 1 |
| if ( STRING_LEN != nBegin ) |
| { |
| if ( nBegin ) |
| --nBegin; |
| |
| LanguageType eActLang = pNode->GetLang( nBegin ); |
| Boundary aBound = |
| pBreakIt->GetBreakIter()->getWordBoundary( pNode->GetTxt(), nBegin, |
| pBreakIt->GetLocale( eActLang ), |
| WordType::DICTIONARY_WORD, sal_True ); |
| nBegin = xub_StrLen(aBound.startPos); |
| } |
| |
| // get the position in the wrong list |
| nInsertPos = pNode->GetWrong()->GetWrongPos( nBegin ); |
| |
| // sometimes we have to skip one entry |
| if( nInsertPos < pNode->GetWrong()->Count() && |
| nBegin == pNode->GetWrong()->Pos( nInsertPos ) + |
| pNode->GetWrong()->Len( nInsertPos ) ) |
| nInsertPos++; |
| } |
| |
| sal_Bool bFresh = nBegin < nEnd; |
| |
| if( nBegin < nEnd ) |
| { |
| //! register listener to LinguServiceEvents now in order to get |
| //! notified about relevant changes in the future |
| SwModule *pModule = SW_MOD(); |
| if (!pModule->GetLngSvcEvtListener().is()) |
| pModule->CreateLngSvcEvtListener(); |
| |
| uno::Reference< XSpellChecker1 > xSpell( ::GetSpellChecker() ); |
| SwDoc* pDoc = pNode->GetDoc(); |
| |
| SwScanner aScanner( *pNode, pNode->GetTxt(), 0, 0, |
| WordType::DICTIONARY_WORD, nBegin, nEnd); |
| |
| while( aScanner.NextWord() ) |
| { |
| const XubString& rWord = aScanner.GetWord(); |
| nBegin = aScanner.GetBegin(); |
| xub_StrLen nLen = aScanner.GetLen(); |
| |
| // get next language for next word, consider language attributes |
| // within the word |
| LanguageType eActLang = aScanner.GetCurrentLanguage(); |
| |
| sal_Bool bSpell = sal_True; |
| bSpell = xSpell.is() ? xSpell->hasLanguage( eActLang ) : sal_False; |
| if( bSpell && rWord.Len() > 0 ) |
| { |
| // check for: bAlter => xHyphWord.is() |
| DBG_ASSERT(!bSpell || xSpell.is(), "NULL pointer"); |
| |
| if( !xSpell->isValid( rWord, eActLang, Sequence< PropertyValue >() ) ) |
| { |
| xub_StrLen nSmartTagStt = nBegin; |
| xub_StrLen nDummy = 1; |
| if ( !pNode->GetSmartTags() || !pNode->GetSmartTags()->InWrongWord( nSmartTagStt, nDummy ) ) |
| { |
| if( !pNode->GetWrong() ) |
| { |
| pNode->SetWrong( new SwWrongList( WRONGLIST_SPELL ) ); |
| pNode->GetWrong()->SetInvalid( 0, nEnd ); |
| } |
| if( pNode->GetWrong()->Fresh( nChgStart, nChgEnd, |
| nBegin, nLen, nInsertPos, nActPos ) ) |
| pNode->GetWrong()->Insert( rtl::OUString(), 0, nBegin, nLen, nInsertPos++ ); |
| else |
| { |
| nInvStart = nBegin; |
| nInvEnd = nBegin + nLen; |
| } |
| } |
| } |
| else if( bAddAutoCmpl && rACW.GetMinWordLen() <= rWord.Len() ) |
| { |
| if ( bRedlineChg ) |
| { |
| XubString rNewWord( rWord ); |
| rACW.InsertWord( rNewWord, *pDoc ); |
| } |
| else |
| rACW.InsertWord( rWord, *pDoc ); |
| } |
| } |
| } |
| } |
| |
| // reset original text |
| // i63141 before calling GetCharRect(..) with formatting! |
| if ( bRestoreString ) |
| { |
| pNode->m_Text = aOldTxt; |
| } |
| if( pNode->GetWrong() ) |
| { |
| if( bFresh ) |
| pNode->GetWrong()->Fresh( nChgStart, nChgEnd, |
| nEnd, 0, nInsertPos, nActPos ); |
| |
| // |
| // Calculate repaint area: |
| // |
| if( nChgStart < nChgEnd ) |
| { |
| aRect = lcl_CalculateRepaintRect( *this, nChgStart, nChgEnd ); |
| } |
| |
| pNode->GetWrong()->SetInvalid( nInvStart, nInvEnd ); |
| pNode->SetWrongDirty( STRING_LEN != pNode->GetWrong()->GetBeginInv() ); |
| if( !pNode->GetWrong()->Count() && ! pNode->IsWrongDirty() ) |
| pNode->SetWrong( NULL ); |
| } |
| else |
| pNode->SetWrongDirty( false ); |
| |
| if( bAddAutoCmpl ) |
| pNode->SetAutoCompleteWordDirty( false ); |
| |
| return aRect; |
| } |
| |
| /** Function: SmartTagScan |
| |
| Function scans words in current text and checks them in the |
| smarttag libraries. If the check returns true to bounds of the |
| recognized words are stored into a list which is used later for drawing |
| the underline. |
| |
| @param SwCntntNode* pActNode |
| |
| @param xub_StrLen nActPos |
| |
| @return SwRect: Repaint area |
| */ |
| SwRect SwTxtFrm::SmartTagScan( SwCntntNode* /*pActNode*/, xub_StrLen /*nActPos*/ ) |
| { |
| SwRect aRet; |
| SwTxtNode *pNode = GetTxtNode(); |
| const rtl::OUString& rText = pNode->GetTxt(); |
| |
| // Iterate over language portions |
| SmartTagMgr& rSmartTagMgr = SwSmartTagMgr::Get(); |
| |
| SwWrongList* pSmartTagList = pNode->GetSmartTags(); |
| |
| xub_StrLen nBegin = 0; |
| xub_StrLen nEnd = static_cast< xub_StrLen >(rText.getLength()); |
| |
| if ( pSmartTagList ) |
| { |
| if ( pSmartTagList->GetBeginInv() != STRING_LEN ) |
| { |
| nBegin = pSmartTagList->GetBeginInv(); |
| nEnd = Min( pSmartTagList->GetEndInv(), (xub_StrLen)rText.getLength() ); |
| |
| if ( nBegin < nEnd ) |
| { |
| const LanguageType aCurrLang = pNode->GetLang( nBegin ); |
| const com::sun::star::lang::Locale aCurrLocale = pBreakIt->GetLocale( aCurrLang ); |
| nBegin = static_cast< xub_StrLen >(pBreakIt->GetBreakIter()->beginOfSentence( rText, nBegin, aCurrLocale )); |
| nEnd = static_cast< xub_StrLen >(Min( rText.getLength(), pBreakIt->GetBreakIter()->endOfSentence( rText, nEnd, aCurrLocale ) )); |
| } |
| } |
| } |
| |
| const sal_uInt16 nNumberOfEntries = pSmartTagList ? pSmartTagList->Count() : 0; |
| sal_uInt16 nNumberOfRemovedEntries = 0; |
| sal_uInt16 nNumberOfInsertedEntries = 0; |
| |
| // clear smart tag list between nBegin and nEnd: |
| if ( 0 != nNumberOfEntries ) |
| { |
| xub_StrLen nChgStart = STRING_LEN; |
| xub_StrLen nChgEnd = 0; |
| const sal_uInt16 nCurrentIndex = pSmartTagList->GetWrongPos( nBegin ); |
| pSmartTagList->Fresh( nChgStart, nChgEnd, nBegin, nEnd - nBegin, nCurrentIndex, STRING_LEN ); |
| nNumberOfRemovedEntries = nNumberOfEntries - pSmartTagList->Count(); |
| } |
| |
| if ( nBegin < nEnd ) |
| { |
| // Expand the string: |
| rtl::OUString aExpandText; |
| const ModelToViewHelper::ConversionMap* pConversionMap = |
| pNode->BuildConversionMap( aExpandText ); |
| |
| // Ownership ov ConversionMap is passed to SwXTextMarkup object! |
| Reference< com::sun::star::text::XTextMarkup > xTextMarkup = |
| new SwXTextMarkup( *pNode, pConversionMap ); |
| |
| Reference< ::com::sun::star::frame::XController > xController = pNode->GetDoc()->GetDocShell()->GetController(); |
| |
| SwPosition start(*pNode, nBegin); |
| SwPosition end (*pNode, nEnd); |
| Reference< ::com::sun::star::text::XTextRange > xRange = SwXTextRange::CreateXTextRange(*pNode->GetDoc(), start, &end); |
| |
| rSmartTagMgr.RecognizeTextRange(xRange, xTextMarkup, xController); |
| |
| |
| xub_StrLen nLangBegin = nBegin; |
| xub_StrLen nLangEnd = nEnd; |
| |
| // smart tag recognization has to be done for each language portion: |
| SwLanguageIterator aIter( *pNode, nLangBegin ); |
| |
| do |
| { |
| const LanguageType nLang = aIter.GetLanguage(); |
| const com::sun::star::lang::Locale aLocale = pBreakIt->GetLocale( nLang ); |
| nLangEnd = Min( nEnd, aIter.GetChgPos() ); |
| |
| const sal_uInt32 nExpandBegin = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nLangBegin ); |
| const sal_uInt32 nExpandEnd = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nLangEnd ); |
| |
| rSmartTagMgr.RecognizeString(aExpandText, xTextMarkup, xController, aLocale, nExpandBegin, nExpandEnd - nExpandBegin ); |
| |
| nLangBegin = nLangEnd; |
| } |
| while ( aIter.Next() && nLangEnd < nEnd ); |
| |
| pSmartTagList = pNode->GetSmartTags(); |
| |
| const sal_uInt16 nNumberOfEntriesAfterRecognize = pSmartTagList ? pSmartTagList->Count() : 0; |
| nNumberOfInsertedEntries = nNumberOfEntriesAfterRecognize - ( nNumberOfEntries - nNumberOfRemovedEntries ); |
| } |
| |
| if( pSmartTagList ) |
| { |
| // |
| // Update WrongList stuff |
| // |
| pSmartTagList->SetInvalid( STRING_LEN, 0 ); |
| pNode->SetSmartTagDirty( STRING_LEN != pSmartTagList->GetBeginInv() ); |
| |
| if( !pSmartTagList->Count() && !pNode->IsSmartTagDirty() ) |
| pNode->SetSmartTags( NULL ); |
| |
| // |
| // Calculate repaint area: |
| // |
| #if OSL_DEBUG_LEVEL > 1 |
| const sal_uInt16 nNumberOfEntriesAfterRecognize2 = pSmartTagList->Count(); |
| (void) nNumberOfEntriesAfterRecognize2; |
| #endif |
| if ( nBegin < nEnd && ( 0 != nNumberOfRemovedEntries || |
| 0 != nNumberOfInsertedEntries ) ) |
| { |
| aRet = lcl_CalculateRepaintRect( *this, nBegin, nEnd ); |
| } |
| } |
| else |
| pNode->SetSmartTagDirty( false ); |
| |
| return aRet; |
| } |
| |
| |
| // Wird vom CollectAutoCmplWords gerufen |
| void SwTxtFrm::CollectAutoCmplWrds( SwCntntNode* pActNode, xub_StrLen nActPos ) |
| { |
| SwTxtNode *pNode = GetTxtNode(); |
| if( pNode != pActNode || !nActPos ) |
| nActPos = STRING_LEN; |
| |
| SwDoc* pDoc = pNode->GetDoc(); |
| SwAutoCompleteWord& rACW = SwDoc::GetAutoCompleteWords(); |
| |
| xub_StrLen nBegin = 0; |
| xub_StrLen nEnd = pNode->GetTxt().Len(); |
| xub_StrLen nLen; |
| sal_Bool bACWDirty = sal_False, bAnyWrd = sal_False; |
| |
| |
| if( nBegin < nEnd ) |
| { |
| sal_uInt16 nCnt = 200; |
| SwScanner aScanner( *pNode, pNode->GetTxt(), 0, 0, |
| WordType::DICTIONARY_WORD, nBegin, nEnd ); |
| while( aScanner.NextWord() ) |
| { |
| nBegin = aScanner.GetBegin(); |
| nLen = aScanner.GetLen(); |
| if( rACW.GetMinWordLen() <= nLen ) |
| { |
| const XubString& rWord = aScanner.GetWord(); |
| |
| if( nActPos < nBegin || ( nBegin + nLen ) < nActPos ) |
| { |
| if( rACW.GetMinWordLen() <= rWord.Len() ) |
| rACW.InsertWord( rWord, *pDoc ); |
| bAnyWrd = sal_True; |
| } |
| else |
| bACWDirty = sal_True; |
| } |
| if( !--nCnt ) |
| { |
| if ( Application::AnyInput( INPUT_ANY ) ) |
| return; |
| nCnt = 100; |
| } |
| } |
| } |
| |
| if( bAnyWrd && !bACWDirty ) |
| pNode->SetAutoCompleteWordDirty( sal_False ); |
| } |
| |
| |
| /************************************************************************* |
| * SwTxtNode::Hyphenate |
| *************************************************************************/ |
| // Findet den TxtFrm und sucht dessen CalcHyph |
| |
| sal_Bool SwTxtNode::Hyphenate( SwInterHyphInfo &rHyphInf ) |
| { |
| // Abkuerzung: am Absatz ist keine Sprache eingestellt: |
| if ( LANGUAGE_NONE == sal_uInt16( GetSwAttrSet().GetLanguage().GetLanguage() ) |
| && USHRT_MAX == GetLang( 0, m_Text.Len() ) ) |
| { |
| if( !rHyphInf.IsCheck() ) |
| rHyphInf.SetNoLang( sal_True ); |
| return sal_False; |
| } |
| |
| if( pLinguNode != this ) |
| { |
| pLinguNode = this; |
| pLinguFrm = (SwTxtFrm*)getLayoutFrm( GetDoc()->GetCurrentLayout(), (Point*)(rHyphInf.GetCrsrPos()) ); |
| } |
| SwTxtFrm *pFrm = pLinguFrm; |
| if( pFrm ) |
| pFrm = &(pFrm->GetFrmAtOfst( rHyphInf.nStart )); |
| else |
| { |
| // 4935: Seit der Trennung ueber Sonderbereiche sind Faelle |
| // moeglich, in denen kein Frame zum Node vorliegt. |
| // Also kein ASSERT! |
| #if OSL_DEBUG_LEVEL > 1 |
| ASSERT( pFrm, "!SwTxtNode::Hyphenate: can't find any frame" ); |
| #endif |
| return sal_False; |
| } |
| |
| while( pFrm ) |
| { |
| if( pFrm->Hyphenate( rHyphInf ) ) |
| { |
| // Das Layout ist nicht robust gegen "Direktformatierung" |
| // (7821, 7662, 7408); vgl. layact.cxx, |
| // SwLayAction::_TurboAction(), if( !pCnt->IsValid() ... |
| pFrm->SetCompletePaint(); |
| return sal_True; |
| } |
| pFrm = (SwTxtFrm*)(pFrm->GetFollow()); |
| if( pFrm ) |
| { |
| rHyphInf.nLen = rHyphInf.nLen - (pFrm->GetOfst() - rHyphInf.nStart); |
| rHyphInf.nStart = pFrm->GetOfst(); |
| } |
| } |
| return sal_False; |
| } |
| |
| #ifdef LINGU_STATISTIK |
| |
| // globale Variable |
| SwLinguStatistik aSwLinguStat; |
| |
| |
| void SwLinguStatistik::Flush() |
| { |
| if ( !nWords ) |
| return ; |
| |
| static char *pLogName = 0; |
| const sal_Bool bFirstOpen = pLogName ? sal_False : sal_True; |
| if( bFirstOpen ) |
| { |
| char *pPath = getenv( "TEMP" ); |
| char *pName = "swlingu.stk"; |
| if( !pPath ) |
| pLogName = pName; |
| else |
| { |
| const int nLen = strlen(pPath); |
| // fuer dieses new wird es kein delete geben. |
| pLogName = new char[nLen + strlen(pName) + 3]; |
| if(nLen && (pPath[nLen-1] == '\\') || (pPath[nLen-1] == '/')) |
| snprintf( pLogName, sizeof(pLogName), "%s%s", pPath, pName ); |
| else |
| snprintf( pLogName, sizeof(pLogName), "%s/%s", pPath, pName ); |
| } |
| } |
| SvFileStream aStream( String::CreateFromAscii(pLogName), (bFirstOpen |
| ? STREAM_WRITE | STREAM_TRUNC |
| : STREAM_WRITE )); |
| |
| if( !aStream.GetError() ) |
| { |
| if ( bFirstOpen ) |
| aStream << "\nLinguistik-Statistik\n"; |
| aStream << endl << ++nFlushCnt << ". Messung\n"; |
| aStream << "Rechtschreibung\n"; |
| aStream << "gepruefte Worte: \t" << nWords << endl; |
| aStream << "als fehlerhaft erkannt:\t" << nWrong << endl; |
| aStream << "Alternativvorschlaege:\t" << nAlter << endl; |
| if ( nWrong ) |
| aStream << "Durchschnitt:\t\t" << nAlter*1.0 / nWrong << endl; |
| aStream << "Dauer (msec):\t\t" << nSpellTime << endl; |
| aStream << "\nThesaurus\n"; |
| aStream << "Synonyme gesamt:\t" << nSynonym << endl; |
| if ( nSynonym ) |
| aStream << "Synonym-Durchschnitt:\t" << |
| nSynonym*1.0 / ( nWords - nNoSynonym ) << endl; |
| aStream << "ohne Synonyme:\t\t" << nNoSynonym << endl; |
| aStream << "Bedeutungen gesamt:\t" << nSynonym << endl; |
| aStream << "keine Bedeutungen:\t"<< nNoSynonym << endl; |
| aStream << "Dauer (msec):\t\t" << nTheTime << endl; |
| aStream << "\nHyphenator\n"; |
| aStream << "Trennstellen gesamt:\t" << nHyphens << endl; |
| if ( nHyphens ) |
| aStream << "Hyphen-Durchschnitt:\t" << |
| nHyphens*1.0 / ( nWords - nNoHyph - nHyphErr ) << endl; |
| aStream << "keine Trennstellen:\t" << nNoHyph << endl; |
| aStream << "Trennung verweigert:\t" << nHyphErr << endl; |
| aStream << "Dauer (msec):\t\t" << nHyphTime << endl; |
| aStream << "---------------------------------------------\n"; |
| } |
| nWords = nWrong = nAlter = nSynonym = nNoSynonym = |
| nHyphens = nNoHyph = nHyphErr = nSpellTime = nTheTime = |
| nHyphTime = 0; |
| //pThes = NULL; |
| } |
| |
| #endif |
| |
| namespace sw // #i120045# namespace to avoid XCode template-misoptimization |
| { |
| struct TransliterationChgData |
| { |
| xub_StrLen nStart; |
| xub_StrLen nLen; |
| String sChanged; |
| Sequence< sal_Int32 > aOffsets; |
| }; |
| } |
| using sw::TransliterationChgData; |
| |
| // change text to Upper/Lower/Hiragana/Katagana/... |
| void SwTxtNode::TransliterateText( |
| utl::TransliterationWrapper& rTrans, |
| xub_StrLen nStt, xub_StrLen nEnd, |
| SwUndoTransliterate* pUndo ) |
| { |
| if (nStt < nEnd && pBreakIt->GetBreakIter().is()) |
| { |
| // since we don't use Hiragana/Katakana or half-width/full-width transliterations here |
| // it is fine to use ANYWORD_IGNOREWHITESPACES. (ANY_WORD btw is broken and will |
| // occasionaly miss words in consecutive sentences). Also with ANYWORD_IGNOREWHITESPACES |
| // text like 'just-in-time' will be converted to 'Just-In-Time' which seems to be the |
| // proper thing to do. |
| const sal_Int16 nWordType = WordType::ANYWORD_IGNOREWHITESPACES; |
| |
| //! In order to have less trouble with changing text size, e.g. because |
| //! of ligatures or � (German small sz) being resolved, we need to process |
| //! the text replacements from end to start. |
| //! This way the offsets for the yet to be changed words will be |
| //! left unchanged by the already replaced text. |
| //! For this we temporarily save the changes to be done in this vector |
| std::vector< TransliterationChgData > aChanges; |
| TransliterationChgData aChgData; |
| |
| if (rTrans.getType() == (sal_uInt32)TransliterationModulesExtra::TITLE_CASE) |
| { |
| // for 'capitalize every word' we need to iterate over each word |
| |
| Boundary aSttBndry; |
| Boundary aEndBndry; |
| aSttBndry = pBreakIt->GetBreakIter()->getWordBoundary( |
| GetTxt(), nStt, |
| pBreakIt->GetLocale( GetLang( nStt ) ), |
| nWordType, |
| sal_True /*prefer forward direction*/); |
| aEndBndry = pBreakIt->GetBreakIter()->getWordBoundary( |
| GetTxt(), nEnd, |
| pBreakIt->GetLocale( GetLang( nEnd ) ), |
| nWordType, |
| sal_False /*prefer backward direction*/); |
| |
| // prevent backtracking to the previous word if selection is at word boundary |
| if (aSttBndry.endPos <= nStt) |
| { |
| aSttBndry = pBreakIt->GetBreakIter()->nextWord( |
| GetTxt(), aSttBndry.endPos, |
| pBreakIt->GetLocale( GetLang( aSttBndry.endPos ) ), |
| nWordType); |
| } |
| // prevent advancing to the next word if selection is at word boundary |
| if (aEndBndry.startPos >= nEnd) |
| { |
| aEndBndry = pBreakIt->GetBreakIter()->previousWord( |
| GetTxt(), aEndBndry.startPos, |
| pBreakIt->GetLocale( GetLang( aEndBndry.startPos ) ), |
| nWordType); |
| } |
| |
| Boundary aCurWordBndry( aSttBndry ); |
| while (aCurWordBndry.startPos <= aEndBndry.startPos) |
| { |
| nStt = (xub_StrLen)aCurWordBndry.startPos; |
| nEnd = (xub_StrLen)aCurWordBndry.endPos; |
| sal_Int32 nLen = nEnd - nStt; |
| DBG_ASSERT( nLen > 0, "invalid word length of 0" ); |
| #if OSL_DEBUG_LEVEL > 1 |
| String aText( GetTxt().Copy( nStt, nLen ) ); |
| #endif |
| |
| Sequence <sal_Int32> aOffsets; |
| String sChgd( rTrans.transliterate( GetTxt(), GetLang( nStt ), nStt, nLen, &aOffsets )); |
| |
| if (!m_Text.Equals( sChgd, nStt, nLen )) |
| { |
| aChgData.nStart = nStt; |
| aChgData.nLen = nLen; |
| aChgData.sChanged = sChgd; |
| aChgData.aOffsets = aOffsets; |
| aChanges.push_back( aChgData ); |
| } |
| |
| aCurWordBndry = pBreakIt->GetBreakIter()->nextWord( |
| GetTxt(), nEnd, |
| pBreakIt->GetLocale( GetLang( nEnd ) ), |
| nWordType); |
| } |
| } |
| else if (rTrans.getType() == (sal_uInt32)TransliterationModulesExtra::SENTENCE_CASE) |
| { |
| // for 'sentence case' we need to iterate sentence by sentence |
| |
| sal_Int32 nLastStart = pBreakIt->GetBreakIter()->beginOfSentence( |
| GetTxt(), nEnd, |
| pBreakIt->GetLocale( GetLang( nEnd ) ) ); |
| sal_Int32 nLastEnd = pBreakIt->GetBreakIter()->endOfSentence( |
| GetTxt(), nLastStart, |
| pBreakIt->GetLocale( GetLang( nLastStart ) ) ); |
| |
| // extend nStt, nEnd to the current sentence boundaries |
| sal_Int32 nCurrentStart = pBreakIt->GetBreakIter()->beginOfSentence( |
| GetTxt(), nStt, |
| pBreakIt->GetLocale( GetLang( nStt ) ) ); |
| sal_Int32 nCurrentEnd = pBreakIt->GetBreakIter()->endOfSentence( |
| GetTxt(), nCurrentStart, |
| pBreakIt->GetLocale( GetLang( nCurrentStart ) ) ); |
| |
| // prevent backtracking to the previous sentence if selection starts at end of a sentence |
| if (nCurrentEnd <= nStt) |
| { |
| // now nCurrentStart is probably located on a non-letter word. (unless we |
| // are in Asian text with no spaces...) |
| // Thus to get the real sentence start we should locate the next real word, |
| // that is one found by DICTIONARY_WORD |
| i18n::Boundary aBndry = pBreakIt->GetBreakIter()->nextWord( |
| GetTxt(), nCurrentEnd, |
| pBreakIt->GetLocale( GetLang( nCurrentEnd ) ), |
| i18n::WordType::DICTIONARY_WORD); |
| |
| // now get new current sentence boundaries |
| nCurrentStart = pBreakIt->GetBreakIter()->beginOfSentence( |
| GetTxt(), aBndry.startPos, |
| pBreakIt->GetLocale( GetLang( aBndry.startPos) ) ); |
| nCurrentEnd = pBreakIt->GetBreakIter()->endOfSentence( |
| GetTxt(), nCurrentStart, |
| pBreakIt->GetLocale( GetLang( nCurrentStart) ) ); |
| } |
| // prevent advancing to the next sentence if selection ends at start of a sentence |
| if (nLastStart >= nEnd) |
| { |
| // now nCurrentStart is probably located on a non-letter word. (unless we |
| // are in Asian text with no spaces...) |
| // Thus to get the real sentence start we should locate the previous real word, |
| // that is one found by DICTIONARY_WORD |
| i18n::Boundary aBndry = pBreakIt->GetBreakIter()->previousWord( |
| GetTxt(), nLastStart, |
| pBreakIt->GetLocale( GetLang( nLastStart) ), |
| i18n::WordType::DICTIONARY_WORD); |
| nLastEnd = pBreakIt->GetBreakIter()->endOfSentence( |
| GetTxt(), aBndry.startPos, |
| pBreakIt->GetLocale( GetLang( aBndry.startPos) ) ); |
| if (nCurrentEnd > nLastEnd) |
| nCurrentEnd = nLastEnd; |
| } |
| |
| while (nCurrentStart < nLastEnd) |
| { |
| sal_Int32 nLen = nCurrentEnd - nCurrentStart; |
| DBG_ASSERT( nLen > 0, "invalid word length of 0" ); |
| #if OSL_DEBUG_LEVEL > 1 |
| String aText( GetTxt().Copy( nCurrentStart, nLen ) ); |
| #endif |
| |
| Sequence <sal_Int32> aOffsets; |
| String sChgd( rTrans.transliterate( GetTxt(), |
| GetLang( nCurrentStart ), nCurrentStart, nLen, &aOffsets )); |
| |
| if (!m_Text.Equals( sChgd, nStt, nLen )) |
| { |
| aChgData.nStart = nCurrentStart; |
| aChgData.nLen = nLen; |
| aChgData.sChanged = sChgd; |
| aChgData.aOffsets = aOffsets; |
| aChanges.push_back( aChgData ); |
| } |
| |
| Boundary aFirstWordBndry; |
| aFirstWordBndry = pBreakIt->GetBreakIter()->nextWord( |
| GetTxt(), nCurrentEnd, |
| pBreakIt->GetLocale( GetLang( nCurrentEnd ) ), |
| nWordType); |
| nCurrentStart = aFirstWordBndry.startPos; |
| nCurrentEnd = pBreakIt->GetBreakIter()->endOfSentence( |
| GetTxt(), nCurrentStart, |
| pBreakIt->GetLocale( GetLang( nCurrentStart ) ) ); |
| } |
| } |
| else |
| { |
| // here we may transliterate over complete language portions... |
| |
| SwLanguageIterator* pIter; |
| if( rTrans.needLanguageForTheMode() ) |
| pIter = new SwLanguageIterator( *this, nStt ); |
| else |
| pIter = 0; |
| |
| xub_StrLen nEndPos; |
| sal_uInt16 nLang; |
| do { |
| if( pIter ) |
| { |
| nLang = pIter->GetLanguage(); |
| nEndPos = pIter->GetChgPos(); |
| if( nEndPos > nEnd ) |
| nEndPos = nEnd; |
| } |
| else |
| { |
| nLang = LANGUAGE_SYSTEM; |
| nEndPos = nEnd; |
| } |
| xub_StrLen nLen = nEndPos - nStt; |
| |
| Sequence <sal_Int32> aOffsets; |
| String sChgd( rTrans.transliterate( m_Text, nLang, nStt, nLen, &aOffsets )); |
| |
| if (!m_Text.Equals( sChgd, nStt, nLen )) |
| { |
| aChgData.nStart = nStt; |
| aChgData.nLen = nLen; |
| aChgData.sChanged = sChgd; |
| aChgData.aOffsets = aOffsets; |
| aChanges.push_back( aChgData ); |
| } |
| |
| nStt = nEndPos; |
| } while( nEndPos < nEnd && pIter && pIter->Next() ); |
| delete pIter; |
| } |
| |
| if (aChanges.size() > 0) |
| { |
| // now apply the changes from end to start to leave the offsets of the |
| // yet unchanged text parts remain the same. |
| for (size_t i = 0; i < aChanges.size(); ++i) |
| { |
| TransliterationChgData &rData = aChanges[ aChanges.size() - 1 - i ]; |
| if (pUndo) |
| pUndo->AddChanges( *this, rData.nStart, rData.nLen, rData.aOffsets ); |
| ReplaceTextOnly( rData.nStart, rData.nLen, rData.sChanged, rData.aOffsets ); |
| } |
| } |
| } |
| } |
| |
| |
| void SwTxtNode::ReplaceTextOnly( xub_StrLen nPos, xub_StrLen nLen, |
| const XubString& rText, |
| const Sequence<sal_Int32>& rOffsets ) |
| { |
| m_Text.Replace( nPos, nLen, rText ); |
| |
| xub_StrLen nTLen = rText.Len(); |
| const sal_Int32* pOffsets = rOffsets.getConstArray(); |
| // now look for no 1-1 mapping -> move the indizies! |
| xub_StrLen nI, nMyOff; |
| for( nI = 0, nMyOff = nPos; nI < nTLen; ++nI, ++nMyOff ) |
| { |
| xub_StrLen nOff = (xub_StrLen)pOffsets[ nI ]; |
| if( nOff < nMyOff ) |
| { |
| // something is inserted |
| xub_StrLen nCnt = 1; |
| while( nI + nCnt < nTLen && nOff == pOffsets[ nI + nCnt ] ) |
| ++nCnt; |
| |
| Update( SwIndex( this, nMyOff ), nCnt, sal_False ); |
| nMyOff = nOff; |
| //nMyOff -= nCnt; |
| nI += nCnt - 1; |
| } |
| else if( nOff > nMyOff ) |
| { |
| // something is deleted |
| Update( SwIndex( this, nMyOff+1 ), nOff - nMyOff, sal_True ); |
| nMyOff = nOff; |
| } |
| } |
| if( nMyOff < nLen ) |
| // something is deleted at the end |
| Update( SwIndex( this, nMyOff ), nLen - nMyOff, sal_True ); |
| |
| // notify the layout! |
| SwDelTxt aDelHint( nPos, nTLen ); |
| NotifyClients( 0, &aDelHint ); |
| |
| SwInsTxt aHint( nPos, nTLen ); |
| NotifyClients( 0, &aHint ); |
| } |
| |
| void SwTxtNode::CountWords( SwDocStat& rStat, |
| xub_StrLen nStt, xub_StrLen nEnd ) const |
| { |
| ++rStat.nAllPara; // #i93174#: count _all_ paragraphs |
| if( nStt < nEnd ) |
| { |
| if ( !IsHidden() ) |
| { |
| ++rStat.nPara; |
| sal_uLong nTmpWords = 0; |
| sal_uLong nTmpChars = 0; |
| |
| // Shortcut: Whole paragraph should be considered and cached values |
| // are valid: |
| if ( 0 == nStt && GetTxt().Len() == nEnd && !IsWordCountDirty() ) |
| { |
| nTmpWords = GetParaNumberOfWords(); |
| nTmpChars = GetParaNumberOfChars(); |
| } |
| else |
| { |
| String aOldStr( m_Text ); |
| String& rCastStr = const_cast<String&>(m_Text); |
| |
| // fills the deleted redlines and hidden ranges with cChar: |
| const xub_Unicode cChar(' '); |
| const sal_uInt16 nNumOfMaskedChars = |
| lcl_MaskRedlinesAndHiddenText( *this, rCastStr, nStt, nEnd, cChar, false ); |
| |
| // expand fields |
| rtl::OUString aExpandText; |
| const ModelToViewHelper::ConversionMap* pConversionMap = |
| BuildConversionMap( aExpandText ); |
| |
| const sal_uInt32 nExpandBegin = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nStt ); |
| const sal_uInt32 nExpandEnd = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nEnd ); |
| aExpandText = aExpandText.copy( nExpandBegin, nExpandEnd - nExpandBegin ); |
| |
| const bool bCount = aExpandText.getLength() > 0; |
| |
| // count words in 'regular' text: |
| if( bCount && pBreakIt->GetBreakIter().is() ) |
| { |
| // split into different script languages |
| sal_Int32 nScriptBegin = 0; |
| while ( nScriptBegin < aExpandText.getLength() ) |
| { |
| const sal_Int16 nCurrScript = pBreakIt->GetBreakIter()->getScriptType( aExpandText, nScriptBegin ); |
| const sal_Int32 nScriptEnd = pBreakIt->GetBreakIter()->endOfScript( aExpandText, nScriptBegin, nCurrScript ); |
| rtl::OUString aScriptText = aExpandText.copy( nScriptBegin, nScriptEnd - nScriptBegin ); |
| |
| // Asian languages count words as characters |
| if ( nCurrScript == ::com::sun::star::i18n::ScriptType::ASIAN ) |
| { |
| // subtract white spaces |
| sal_Int32 nSpaceCount = 0; |
| sal_Int32 nSpacePos = 0; |
| |
| // subtract normal white spaces |
| nSpacePos = -1; |
| while ( ( nSpacePos = aScriptText.indexOf( ' ', nSpacePos + 1 ) ) != -1 ) |
| { |
| nSpaceCount++; |
| } |
| // subtract Asian full-width white spaces |
| nSpacePos = -1; |
| while ( ( nSpacePos = aScriptText.indexOf( 12288, nSpacePos + 1 ) ) != -1 ) |
| { |
| nSpaceCount++; |
| } |
| nTmpWords += nScriptEnd - nScriptBegin - nSpaceCount; |
| } |
| else |
| { |
| const String aScannerText( aScriptText ); |
| SwScanner aScanner( *this, aScannerText, 0, pConversionMap, |
| i18n::WordType::WORD_COUNT, |
| (xub_StrLen)0, (xub_StrLen)aScriptText.getLength() ); |
| |
| const rtl::OUString aBreakWord( CH_TXTATR_BREAKWORD ); |
| |
| while ( aScanner.NextWord() ) |
| { |
| if ( aScanner.GetLen() > 1 || |
| CH_TXTATR_BREAKWORD != aScriptText.match(aBreakWord, aScanner.GetBegin() ) ) |
| ++nTmpWords; |
| } |
| } |
| nScriptBegin = nScriptEnd; |
| } |
| } |
| |
| ASSERT( aExpandText.getLength() >= nNumOfMaskedChars, |
| "More characters hidden that characters in string!" ) |
| nTmpChars = nExpandEnd - nExpandBegin - nNumOfMaskedChars; |
| |
| // count words in numbering string: |
| if ( nStt == 0 && bCount ) |
| { |
| // add numbering label |
| const String aNumString = GetNumString(); |
| const xub_StrLen nNumStringLen = aNumString.Len(); |
| if ( nNumStringLen > 0 ) |
| { |
| LanguageType aLanguage = GetLang( 0 ); |
| |
| SwScanner aScanner( *this, aNumString, &aLanguage, 0, |
| i18n::WordType::WORD_COUNT, |
| 0, nNumStringLen ); |
| |
| while ( aScanner.NextWord() ) |
| ++nTmpWords; |
| |
| nTmpChars += nNumStringLen; |
| } |
| else if ( HasBullet() ) |
| { |
| ++nTmpWords; |
| ++nTmpChars; |
| } |
| } |
| |
| delete pConversionMap; |
| |
| rCastStr = aOldStr; |
| |
| // If the whole paragraph has been calculated, update cached |
| // values: |
| if ( 0 == nStt && GetTxt().Len() == nEnd ) |
| { |
| SetParaNumberOfWords( nTmpWords ); |
| SetParaNumberOfChars( nTmpChars ); |
| SetWordCountDirty( false ); |
| } |
| } |
| |
| rStat.nWord += nTmpWords; |
| rStat.nChar += nTmpChars; |
| } |
| } |
| } |
| |
| // |
| // Paragraph statistics start |
| // |
| struct SwParaIdleData_Impl |
| { |
| SwWrongList* pWrong; // for spell checking |
| SwGrammarMarkUp* pGrammarCheck; // for grammar checking / proof reading |
| SwWrongList* pSmartTags; |
| sal_uLong nNumberOfWords; |
| sal_uLong nNumberOfChars; |
| bool bWordCountDirty : 1; |
| bool bWrongDirty : 1; // Ist das Wrong-Feld auf invalid? |
| bool bGrammarCheckDirty : 1; |
| bool bSmartTagDirty : 1; |
| bool bAutoComplDirty : 1; // die ACompl-Liste muss angepasst werden |
| |
| SwParaIdleData_Impl() : |
| pWrong ( 0 ), |
| pGrammarCheck ( 0 ), |
| pSmartTags ( 0 ), |
| nNumberOfWords ( 0 ), |
| nNumberOfChars ( 0 ), |
| bWordCountDirty ( true ), |
| bWrongDirty ( true ), |
| bGrammarCheckDirty ( true ), |
| bSmartTagDirty ( true ), |
| bAutoComplDirty ( true ) {}; |
| }; |
| |
| void SwTxtNode::InitSwParaStatistics( bool bNew ) |
| { |
| if ( bNew ) |
| { |
| m_pParaIdleData_Impl = new SwParaIdleData_Impl; |
| } |
| else if ( m_pParaIdleData_Impl ) |
| { |
| delete m_pParaIdleData_Impl->pWrong; |
| delete m_pParaIdleData_Impl->pGrammarCheck; |
| delete m_pParaIdleData_Impl->pSmartTags; |
| delete m_pParaIdleData_Impl; |
| m_pParaIdleData_Impl = 0; |
| } |
| } |
| |
| void SwTxtNode::SetWrong( SwWrongList* pNew, bool bDelete ) |
| { |
| if ( m_pParaIdleData_Impl ) |
| { |
| if ( bDelete ) |
| { |
| delete m_pParaIdleData_Impl->pWrong; |
| } |
| m_pParaIdleData_Impl->pWrong = pNew; |
| } |
| } |
| |
| SwWrongList* SwTxtNode::GetWrong() |
| { |
| return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong : 0; |
| } |
| |
| // --> OD 2008-05-27 #i71360# |
| const SwWrongList* SwTxtNode::GetWrong() const |
| { |
| return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong : 0; |
| } |
| // <-- |
| |
| |
| void SwTxtNode::SetGrammarCheck( SwGrammarMarkUp* pNew, bool bDelete ) |
| { |
| if ( m_pParaIdleData_Impl ) |
| { |
| if ( bDelete ) |
| { |
| delete m_pParaIdleData_Impl->pGrammarCheck; |
| } |
| m_pParaIdleData_Impl->pGrammarCheck = pNew; |
| } |
| } |
| |
| SwGrammarMarkUp* SwTxtNode::GetGrammarCheck() |
| { |
| return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pGrammarCheck : 0; |
| } |
| |
| void SwTxtNode::SetSmartTags( SwWrongList* pNew, bool bDelete ) |
| { |
| ASSERT( !pNew || SwSmartTagMgr::Get().IsSmartTagsEnabled(), |
| "Weird - we have a smart tag list without any recognizers?" ) |
| |
| if ( m_pParaIdleData_Impl ) |
| { |
| if ( bDelete ) |
| { |
| delete m_pParaIdleData_Impl->pSmartTags; |
| } |
| m_pParaIdleData_Impl->pSmartTags = pNew; |
| } |
| } |
| |
| SwWrongList* SwTxtNode::GetSmartTags() |
| { |
| return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pSmartTags : 0; |
| } |
| |
| void SwTxtNode::SetParaNumberOfWords( sal_uLong nNew ) const |
| { |
| if ( m_pParaIdleData_Impl ) |
| { |
| m_pParaIdleData_Impl->nNumberOfWords = nNew; |
| } |
| } |
| sal_uLong SwTxtNode::GetParaNumberOfWords() const |
| { |
| return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->nNumberOfWords : 0; |
| } |
| void SwTxtNode::SetParaNumberOfChars( sal_uLong nNew ) const |
| { |
| if ( m_pParaIdleData_Impl ) |
| { |
| m_pParaIdleData_Impl->nNumberOfChars = nNew; |
| } |
| } |
| sal_uLong SwTxtNode::GetParaNumberOfChars() const |
| { |
| return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->nNumberOfChars : 0; |
| } |
| void SwTxtNode::SetWordCountDirty( bool bNew ) const |
| { |
| if ( m_pParaIdleData_Impl ) |
| { |
| m_pParaIdleData_Impl->bWordCountDirty = bNew; |
| } |
| } |
| bool SwTxtNode::IsWordCountDirty() const |
| { |
| return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bWordCountDirty : 0; |
| } |
| void SwTxtNode::SetWrongDirty( bool bNew ) const |
| { |
| if ( m_pParaIdleData_Impl ) |
| { |
| m_pParaIdleData_Impl->bWrongDirty = bNew; |
| } |
| } |
| bool SwTxtNode::IsWrongDirty() const |
| { |
| return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bWrongDirty : 0; |
| } |
| void SwTxtNode::SetGrammarCheckDirty( bool bNew ) const |
| { |
| if ( m_pParaIdleData_Impl ) |
| { |
| m_pParaIdleData_Impl->bGrammarCheckDirty = bNew; |
| } |
| } |
| bool SwTxtNode::IsGrammarCheckDirty() const |
| { |
| return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bGrammarCheckDirty : 0; |
| } |
| void SwTxtNode::SetSmartTagDirty( bool bNew ) const |
| { |
| if ( m_pParaIdleData_Impl ) |
| { |
| m_pParaIdleData_Impl->bSmartTagDirty = bNew; |
| } |
| } |
| bool SwTxtNode::IsSmartTagDirty() const |
| { |
| return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bSmartTagDirty : 0; |
| } |
| void SwTxtNode::SetAutoCompleteWordDirty( bool bNew ) const |
| { |
| if ( m_pParaIdleData_Impl ) |
| { |
| m_pParaIdleData_Impl->bAutoComplDirty = bNew; |
| } |
| } |
| bool SwTxtNode::IsAutoCompleteWordDirty() const |
| { |
| return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bAutoComplDirty : 0; |
| } |
| // |
| // Paragraph statistics end |
| // |
| |
| //Bug 120881:Modify here for Directly Page Numbering |
| sal_Bool SwTxtFrm::HasPageNumberField() |
| { |
| return GetRegisteredIn()?((SwTxtNode*)GetRegisteredIn())->HasPageNumberField():false; |
| } |
| //Bug 120881(End) |
| |