summaryrefslogtreecommitdiff
path: root/sw/source/core/txtnode/txtedt.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/txtnode/txtedt.cxx')
-rw-r--r--sw/source/core/txtnode/txtedt.cxx2170
1 files changed, 2170 insertions, 0 deletions
diff --git a/sw/source/core/txtnode/txtedt.cxx b/sw/source/core/txtnode/txtedt.cxx
new file mode 100644
index 000000000000..0630dbd722b6
--- /dev/null
+++ b/sw/source/core/txtnode/txtedt.cxx
@@ -0,0 +1,2170 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 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 <undobj.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>
+
+
+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
+ */
+
+USHORT lcl_MaskRedlines( const SwTxtNode& rNode, XubString& rText,
+ const xub_StrLen nStt, const xub_StrLen nEnd,
+ const xub_Unicode cChar )
+{
+ USHORT nNumOfMaskedRedlines = 0;
+
+ const SwDoc& rDoc = *rNode.GetDoc();
+ USHORT 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
+ */
+
+USHORT 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 )
+{
+ USHORT nRedlinesMasked = 0;
+ USHORT 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;
+ }
+
+ BOOL bSameFrame = TRUE;
+
+ if( rTxtFrm.HasFollow() )
+ {
+ if( pEndFrm != pStartFrm )
+ {
+ bSameFrame = 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( 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,
+ USHORT 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, FALSE ) )
+ {
+ pNewSet = rSet2.Clone( TRUE );
+ pNewSet->ClearItem( nWhichId );
+ }
+ }
+ else if ( pSet1->Count() )
+ {
+ SfxItemIter aIter( *pSet1 );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ while( TRUE )
+ {
+ if ( SFX_ITEM_SET == rSet2.GetItemState( pItem->Which(), FALSE ) )
+ {
+ if ( !pNewSet )
+ pNewSet = rSet2.Clone( 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 BOOL InRange(xub_StrLen nIdx, xub_StrLen nStart, xub_StrLen nEnd) {
+ return ((nIdx >=nStart) && (nIdx <= nEnd));
+}
+
+/*
+ * void SwTxtNode::RstAttr(const SwIndex &rIdx, USHORT 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::RstAttr(const SwIndex &rIdx, xub_StrLen nLen, USHORT nWhich,
+ const SfxItemSet* pSet, BOOL bInclRefToxMark )
+{
+ // Attribute?
+ if ( !GetpSwpHints() )
+ return;
+
+ USHORT i = 0;
+ xub_StrLen nStt = rIdx.GetIndex();
+ xub_StrLen nEnd = nStt + nLen;
+ xub_StrLen nAttrStart;
+ SwTxtAttr *pHt;
+
+ 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;
+
+ // 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
+ while ((i < m_pSwpHints->Count()) &&
+ ((( nAttrStart = *(*m_pSwpHints)[i]->GetStart()) < nEnd ) || nLen==0) )
+ {
+ pHt = m_pSwpHints->GetTextHint(i);
+
+ // attributes without end stay in!
+ xub_StrLen * const pAttrEnd = pHt->GetEnd();
+ if ( !pAttrEnd /*|| pHt->HasDummyChar()*/ ) // see bInclRefToxMark
+ {
+ 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(), 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
+ {
+ if( nEnd > nAttrStart
+ || ( nEnd == *pAttrEnd && nEnd==nAttrStart ) )
+ {
+ // Faelle: 1,3
+ if ( nMin > nAttrStart )
+ nMin = nAttrStart;
+ if ( nMax < *pAttrEnd )
+ nMax = *pAttrEnd;
+ // Falls wir nur ein nichtaufgespanntes Attribut entfernen,
+ // tun wir mal so, als ob sich nichts geaendert hat.
+ bChanged = bChanged || nEnd > nAttrStart || bNoLen;
+ if( *pAttrEnd <= nEnd ) // Fall: 1
+ {
+ const xub_StrLen nAttrEnd = *pAttrEnd;
+
+ 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, TRUE );
+
+ if ( pStyleHandle.get() && nAttrStart < nEnd )
+ {
+ SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(),
+ *pStyleHandle, nAttrStart, nEnd );
+ InsertHint( pNew, nsSetAttrMode::SETATTR_NOHINTADJUST );
+ }
+
+ bChanged = TRUE;
+ }
+ }
+ }
+ else // 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, 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, 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 );
+ SwModify::Modify( 0, &aHint );
+ SwFmtChg aNew( GetFmtColl() );
+ SwModify::Modify( 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
+ BOOL bBegin = rxBreak->isBeginWord( m_Text, nPos, aLocale, nWordType );
+ BOOL bEnd = rxBreak->isEndWord ( m_Text, nPos, aLocale, nWordType );
+ (void)bBegin;
+ (void)bEnd;
+#endif
+ aBndry =
+ rxBreak->getWordBoundary( m_Text, nPos, aLocale, nWordType, 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,
+ USHORT nType, xub_StrLen nStart, xub_StrLen nEnde, 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 );
+ }
+}
+
+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 USHORT 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 FALSE;
+
+ // get the word boundaries
+ aBound = pBreakIt->GetBreakIter()->getWordBoundary( rText, nBegin,
+ pBreakIt->GetLocale( aCurrLang ), nWordType, sal_True );
+
+ //no word boundaries could be found
+ if(aBound.endPos == aBound.startPos)
+ return 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 );
+
+ // 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 USHORT 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 USHORT 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 FALSE;
+
+ aWord = rText.Copy( nBegin, nLen );
+
+ return TRUE;
+}
+
+
+USHORT 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, USHORT nLangWhichId,
+ const Font *pFont, USHORT 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.GetFamilyName() = pFont->GetName();
+ aFontItem.GetFamily() = pFont->GetFamily();
+ aFontItem.GetStyleName() = pFont->GetStyleName();
+ aFontItem.GetPitch() = pFont->GetPitch();
+ aFontItem.GetCharSet() = 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.
+
+}
+
+
+USHORT 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 BOOL bStop = 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, 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++;
+ }
+
+ 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();
+
+ BOOL bSpell = TRUE;
+ bSpell = xSpell.is() ? xSpell->hasLanguage( eActLang ) : 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 USHORT nNumberOfEntries = pSmartTagList ? pSmartTagList->Count() : 0;
+ USHORT nNumberOfRemovedEntries = 0;
+ USHORT nNumberOfInsertedEntries = 0;
+
+ // clear smart tag list between nBegin and nEnd:
+ if ( 0 != nNumberOfEntries )
+ {
+ xub_StrLen nChgStart = STRING_LEN;
+ xub_StrLen nChgEnd = 0;
+ const USHORT 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();
+
+ 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.Recognize( aExpandText, xTextMarkup, xController, aLocale, nExpandBegin, nExpandEnd - nExpandBegin );
+
+ nLangBegin = nLangEnd;
+ }
+ while ( aIter.Next() && nLangEnd < nEnd );
+
+ pSmartTagList = pNode->GetSmartTags();
+
+ const USHORT 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 USHORT 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;
+ BOOL bACWDirty = FALSE, bAnyWrd = FALSE;
+
+
+ if( nBegin < nEnd )
+ {
+ USHORT 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 = TRUE;
+ }
+ else
+ bACWDirty = TRUE;
+ }
+ if( !--nCnt )
+ {
+ if ( Application::AnyInput( INPUT_ANY ) )
+ return;
+ nCnt = 100;
+ }
+ }
+ }
+
+ if( bAnyWrd && !bACWDirty )
+ pNode->SetAutoCompleteWordDirty( FALSE );
+}
+
+
+/*************************************************************************
+ * SwTxtNode::Hyphenate
+ *************************************************************************/
+// Findet den TxtFrm und sucht dessen CalcHyph
+
+BOOL SwTxtNode::Hyphenate( SwInterHyphInfo &rHyphInf )
+{
+ // Abkuerzung: am Absatz ist keine Sprache eingestellt:
+ if ( LANGUAGE_NONE == USHORT( GetSwAttrSet().GetLanguage().GetLanguage() )
+ && USHRT_MAX == GetLang( 0, m_Text.Len() ) )
+ {
+ if( !rHyphInf.IsCheck() )
+ rHyphInf.SetNoLang( TRUE );
+ return FALSE;
+ }
+
+ if( pLinguNode != this )
+ {
+ pLinguNode = this;
+ pLinguFrm = (SwTxtFrm*)GetFrm( (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 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 TRUE;
+ }
+ pFrm = (SwTxtFrm*)(pFrm->GetFollow());
+ if( pFrm )
+ {
+ rHyphInf.nLen = rHyphInf.nLen - (pFrm->GetOfst() - rHyphInf.nStart);
+ rHyphInf.nStart = pFrm->GetOfst();
+ }
+ }
+ return FALSE;
+}
+
+#ifdef LINGU_STATISTIK
+
+// globale Variable
+SwLinguStatistik aSwLinguStat;
+
+
+void SwLinguStatistik::Flush()
+{
+ if ( !nWords )
+ return ;
+
+ static char *pLogName = 0;
+ const BOOL bFirstOpen = pLogName ? FALSE : 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
+
+
+struct TransliterationChgData
+{
+ xub_StrLen nStart;
+ xub_StrLen nLen;
+ String sChanged;
+ Sequence< sal_Int32 > aOffsets;
+};
+
+// 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,
+ TRUE /*prefer forward direction*/);
+ aEndBndry = pBreakIt->GetBreakIter()->getWordBoundary(
+ GetTxt(), nEnd,
+ pBreakIt->GetLocale( GetLang( nEnd ) ),
+ nWordType,
+ 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, FALSE );
+ nMyOff = nOff;
+ //nMyOff -= nCnt;
+ nI += nCnt - 1;
+ }
+ else if( nOff > nMyOff )
+ {
+ // something is deleted
+ Update( SwIndex( this, nMyOff+1 ), nOff - nMyOff, TRUE );
+ nMyOff = nOff;
+ }
+ }
+ if( nMyOff < nLen )
+ // something is deleted at the end
+ Update( SwIndex( this, nMyOff ), nLen - nMyOff, TRUE );
+
+ // notify the layout!
+ SwDelTxt aDelHint( nPos, nTLen );
+ SwModify::Modify( 0, &aDelHint );
+
+ SwInsTxt aHint( nPos, nTLen );
+ SwModify::Modify( 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;
+ ULONG nTmpWords = 0;
+ 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 USHORT 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 );
+
+ const bool bCount = aExpandText.getLength() > 0;
+
+ // count words in 'regular' text:
+ if( bCount && pBreakIt->GetBreakIter().is() )
+ {
+ const String aScannerText( aExpandText );
+ SwScanner aScanner( *this, aScannerText, 0, pConversionMap,
+ i18n::WordType::WORD_COUNT,
+ (xub_StrLen)nExpandBegin, (xub_StrLen)nExpandEnd );
+
+ const rtl::OUString aBreakWord( CH_TXTATR_BREAKWORD );
+
+ while ( aScanner.NextWord() )
+ {
+ if ( aScanner.GetLen() > 1 ||
+ CH_TXTATR_BREAKWORD != aExpandText.match(aBreakWord, aScanner.GetBegin() ) )
+ ++nTmpWords;
+ }
+ }
+
+ 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;
+ ULONG nNumberOfWords;
+ 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( ULONG nNew ) const
+{
+ if ( m_pParaIdleData_Impl )
+ {
+ m_pParaIdleData_Impl->nNumberOfWords = nNew;
+ }
+}
+ULONG SwTxtNode::GetParaNumberOfWords() const
+{
+ return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->nNumberOfWords : 0;
+}
+void SwTxtNode::SetParaNumberOfChars( ULONG nNew ) const
+{
+ if ( m_pParaIdleData_Impl )
+ {
+ m_pParaIdleData_Impl->nNumberOfChars = nNew;
+ }
+}
+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
+//