1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: txtedt.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sw.hxx"
34 // So kann man die Linguistik-Statistik ( (Tmp-Path)\swlingu.stk ) aktivieren:
35 //#define LINGU_STATISTIK
36 #ifdef LINGU_STATISTIK
37 #include <stdio.h> // in SwLinguStatistik::DTOR
38 #include <stdlib.h> // getenv()
39 #include <time.h> // clock()
40 #include <tools/stream.hxx>
42 #include <hintids.hxx>
43 #include <vcl/svapp.hxx>
44 #include <svtools/itemiter.hxx>
45 #include <svx/splwrap.hxx>
46 #include <svx/langitem.hxx>
47 #include <svx/fontitem.hxx>
48 #include <svx/scripttypeitem.hxx>
49 #include <svx/hangulhanja.hxx>
50 #include <SwSmartTagMgr.hxx>
51 #include <linguistic/lngprops.hxx>
52 #include <com/sun/star/beans/XPropertySet.hpp>
53 #include <com/sun/star/i18n/WordType.hdl>
54 #include <com/sun/star/i18n/ScriptType.hdl>
55 #include <unotools/transliterationwrapper.hxx>
56 #include <unotools/charclass.hxx>
57 #include <dlelstnr.hxx>
58 #include <swmodule.hxx>
59 #include <splargs.hxx>
60 #include <viewopt.hxx>
61 #include <acmplwrd.hxx>
62 #include <doc.hxx> // GetDoc()
66 #include <txatbase.hxx>
67 #include <charatr.hxx>
73 #include <SwGrammarMarkUp.hxx>
75 #include <txttypes.hxx>
76 #include <breakit.hxx>
77 #include <crstate.hxx>
79 #include <txatritr.hxx>
80 #include <redline.hxx> // SwRedline
81 #include <docary.hxx> // SwRedlineTbl
82 #include <scriptinfo.hxx>
83 #include <docstat.hxx>
85 #include <unotextmarkup.hxx>
87 #include <fmtautofmt.hxx>
88 #include <istyleaccess.hxx>
93 using namespace ::com::sun::star
;
94 using namespace ::com::sun::star::frame
;
95 using namespace ::com::sun::star::i18n
;
96 using namespace ::com::sun::star::beans
;
97 using namespace ::com::sun::star::uno
;
98 using namespace ::com::sun::star::linguistic2
;
99 using namespace ::com::sun::star::smarttags
;
101 // Wir ersparen uns in Hyphenate ein GetFrm()
102 // Achtung: in edlingu.cxx stehen die Variablen!
103 extern const SwTxtNode
*pLinguNode
;
104 extern SwTxtFrm
*pLinguFrm
;
106 bool lcl_IsSkippableWhiteSpace( xub_Unicode cCh
)
108 return 0x3000 == cCh
||
115 * This has basically the same function as SwScriptInfo::MaskHiddenRanges,
116 * only for deleted redlines
119 USHORT
lcl_MaskRedlines( const SwTxtNode
& rNode
, XubString
& rText
,
120 const xub_StrLen nStt
, const xub_StrLen nEnd
,
121 const xub_Unicode cChar
)
123 USHORT nNumOfMaskedRedlines
= 0;
125 const SwDoc
& rDoc
= *rNode
.GetDoc();
126 USHORT nAct
= rDoc
.GetRedlinePos( rNode
, USHRT_MAX
);
128 for ( ; nAct
< rDoc
.GetRedlineTbl().Count(); nAct
++ )
130 const SwRedline
* pRed
= rDoc
.GetRedlineTbl()[ nAct
];
132 if ( pRed
->Start()->nNode
> rNode
.GetIndex() )
135 if( nsRedlineType_t::REDLINE_DELETE
== pRed
->GetType() )
137 xub_StrLen nRedlineEnd
;
138 xub_StrLen nRedlineStart
;
140 pRed
->CalcStartEnd( rNode
.GetIndex(), nRedlineStart
, nRedlineEnd
);
142 if ( nRedlineEnd
< nStt
|| nRedlineStart
> nEnd
)
145 while ( nRedlineStart
< nRedlineEnd
&& nRedlineStart
< nEnd
)
147 if ( nRedlineStart
>= nStt
&& nRedlineStart
< nEnd
)
149 rText
.SetChar( nRedlineStart
, cChar
);
150 ++nNumOfMaskedRedlines
;
157 return nNumOfMaskedRedlines
;
161 * Used for spell checking. Deleted redlines and hidden characters are masked
164 USHORT
lcl_MaskRedlinesAndHiddenText( const SwTxtNode
& rNode
, XubString
& rText
,
165 const xub_StrLen nStt
, const xub_StrLen nEnd
,
166 const xub_Unicode cChar
= CH_TXTATR_INWORD
,
167 bool bCheckShowHiddenChar
= true )
169 USHORT nRedlinesMasked
= 0;
170 USHORT nHiddenCharsMasked
= 0;
172 const SwDoc
& rDoc
= *rNode
.GetDoc();
173 const bool bShowChg
= 0 != IDocumentRedlineAccess::IsShowChanges( rDoc
.GetRedlineMode() );
175 // If called from word count or from spell checking, deleted redlines
179 nRedlinesMasked
= lcl_MaskRedlines( rNode
, rText
, nStt
, nEnd
, cChar
);
182 const bool bHideHidden
= !SW_MOD()->GetViewOption(rDoc
.get(IDocumentSettingAccess::HTML_MODE
))->IsShowHiddenChar();
184 // If called from word count, we want to mask the hidden ranges even
185 // if they are visible:
186 if ( !bCheckShowHiddenChar
|| bHideHidden
)
189 SwScriptInfo::MaskHiddenRanges( rNode
, rText
, nStt
, nEnd
, cChar
);
192 return nRedlinesMasked
+ nHiddenCharsMasked
;
196 * Used for spell checking. Calculates a rectangle for repaint.
199 static SwRect
lcl_CalculateRepaintRect( SwTxtFrm
& rTxtFrm
, xub_StrLen nChgStart
, xub_StrLen nChgEnd
)
203 SwTxtNode
*pNode
= rTxtFrm
.GetTxtNode();
205 SwNodeIndex
aNdIdx( *pNode
);
206 SwPosition
aPos( aNdIdx
, SwIndex( pNode
, nChgEnd
) );
207 SwCrsrMoveState
aTmpState( MV_NONE
);
208 aTmpState
.b2Lines
= sal_True
;
209 rTxtFrm
.GetCharRect( aRect
, aPos
, &aTmpState
);
210 // information about end of repaint area
211 Sw2LinesPos
* pEnd2Pos
= aTmpState
.p2Lines
;
213 const SwTxtFrm
*pEndFrm
= &rTxtFrm
;
215 while( pEndFrm
->HasFollow() &&
216 nChgEnd
>= pEndFrm
->GetFollow()->GetOfst() )
217 pEndFrm
= pEndFrm
->GetFollow();
221 // we are inside a special portion, take left border
223 (aRect
.*fnRect
->fnSetTop
)( (pEnd2Pos
->aLine
.*fnRect
->fnGetTop
)() );
224 if ( pEndFrm
->IsRightToLeft() )
225 (aRect
.*fnRect
->fnSetLeft
)( (pEnd2Pos
->aPortion
.*fnRect
->fnGetLeft
)() );
227 (aRect
.*fnRect
->fnSetLeft
)( (pEnd2Pos
->aPortion
.*fnRect
->fnGetRight
)() );
228 (aRect
.*fnRect
->fnSetWidth
)( 1 );
229 (aRect
.*fnRect
->fnSetHeight
)( (pEnd2Pos
->aLine
.*fnRect
->fnGetHeight
)() );
233 aTmpState
.p2Lines
= NULL
;
235 aPos
= SwPosition( aNdIdx
, SwIndex( pNode
, nChgStart
) );
236 rTxtFrm
.GetCharRect( aTmp
, aPos
, &aTmpState
);
238 // i63141: GetCharRect(..) could cause a formatting,
239 // during the formatting SwTxtFrms could be joined, deleted, created...
240 // => we have to reinit pStartFrm and pEndFrm after the formatting
241 const SwTxtFrm
* pStartFrm
= &rTxtFrm
;
242 while( pStartFrm
->HasFollow() &&
243 nChgStart
>= pStartFrm
->GetFollow()->GetOfst() )
244 pStartFrm
= pStartFrm
->GetFollow();
246 while( pEndFrm
->HasFollow() &&
247 nChgEnd
>= pEndFrm
->GetFollow()->GetOfst() )
248 pEndFrm
= pEndFrm
->GetFollow();
250 // information about start of repaint area
251 Sw2LinesPos
* pSt2Pos
= aTmpState
.p2Lines
;
254 // we are inside a special portion, take right border
255 SWRECTFN( pStartFrm
)
256 (aTmp
.*fnRect
->fnSetTop
)( (pSt2Pos
->aLine
.*fnRect
->fnGetTop
)() );
257 if ( pStartFrm
->IsRightToLeft() )
258 (aTmp
.*fnRect
->fnSetLeft
)( (pSt2Pos
->aPortion
.*fnRect
->fnGetRight
)() );
260 (aTmp
.*fnRect
->fnSetLeft
)( (pSt2Pos
->aPortion
.*fnRect
->fnGetLeft
)() );
261 (aTmp
.*fnRect
->fnSetWidth
)( 1 );
262 (aTmp
.*fnRect
->fnSetHeight
)( (pSt2Pos
->aLine
.*fnRect
->fnGetHeight
)() );
266 BOOL bSameFrame
= TRUE
;
268 if( rTxtFrm
.HasFollow() )
270 if( pEndFrm
!= pStartFrm
)
273 SwRect
aStFrm( pStartFrm
->PaintArea() );
275 SWRECTFN( pStartFrm
)
276 (aTmp
.*fnRect
->fnSetLeft
)( (aStFrm
.*fnRect
->fnGetLeft
)() );
277 (aTmp
.*fnRect
->fnSetRight
)( (aStFrm
.*fnRect
->fnGetRight
)() );
278 (aTmp
.*fnRect
->fnSetBottom
)( (aStFrm
.*fnRect
->fnGetBottom
)() );
280 aStFrm
= pEndFrm
->PaintArea();
283 (aRect
.*fnRect
->fnSetTop
)( (aStFrm
.*fnRect
->fnGetTop
)() );
284 (aRect
.*fnRect
->fnSetLeft
)( (aStFrm
.*fnRect
->fnGetLeft
)() );
285 (aRect
.*fnRect
->fnSetRight
)( (aStFrm
.*fnRect
->fnGetRight
)() );
290 pStartFrm
= pStartFrm
->GetFollow();
291 if( pStartFrm
== pEndFrm
)
293 aRect
.Union( pStartFrm
->PaintArea() );
299 SWRECTFN( pStartFrm
)
300 if( (aTmp
.*fnRect
->fnGetTop
)() == (aRect
.*fnRect
->fnGetTop
)() )
301 (aRect
.*fnRect
->fnSetLeft
)( (aTmp
.*fnRect
->fnGetLeft
)() );
304 SwRect
aStFrm( pStartFrm
->PaintArea() );
305 (aRect
.*fnRect
->fnSetLeft
)( (aStFrm
.*fnRect
->fnGetLeft
)() );
306 (aRect
.*fnRect
->fnSetRight
)( (aStFrm
.*fnRect
->fnGetRight
)() );
307 (aRect
.*fnRect
->fnSetTop
)( (aTmp
.*fnRect
->fnGetTop
)() );
310 if( aTmp
.Height() > aRect
.Height() )
311 aRect
.Height( aTmp
.Height() );
318 * Used for automatic styles. Used during RstAttr.
321 static bool lcl_HaveCommonAttributes( IStyleAccess
& rStyleAccess
,
322 const SfxItemSet
* pSet1
,
324 const SfxItemSet
& rSet2
,
325 boost::shared_ptr
<SfxItemSet
>& pStyleHandle
)
329 SfxItemSet
* pNewSet
= 0;
333 ASSERT( nWhichId
, "lcl_HaveCommonAttributes not used correctly" )
334 if ( SFX_ITEM_SET
== rSet2
.GetItemState( nWhichId
, FALSE
) )
336 pNewSet
= rSet2
.Clone( TRUE
);
337 pNewSet
->ClearItem( nWhichId
);
340 else if ( pSet1
->Count() )
342 SfxItemIter
aIter( *pSet1
);
343 const SfxPoolItem
* pItem
= aIter
.GetCurItem();
346 if ( SFX_ITEM_SET
== rSet2
.GetItemState( pItem
->Which(), FALSE
) )
349 pNewSet
= rSet2
.Clone( TRUE
);
350 pNewSet
->ClearItem( pItem
->Which() );
353 if( aIter
.IsAtEnd() )
356 pItem
= aIter
.NextItem();
362 if ( pNewSet
->Count() )
363 pStyleHandle
= rStyleAccess
.getAutomaticStyle( *pNewSet
, IStyleAccess::AUTO_STYLE_CHAR
);
372 * Ein Zeichen wurde eingefuegt.
375 SwTxtNode
& SwTxtNode::Insert( xub_Unicode c
, const SwIndex
&rIdx
)
377 xub_StrLen nOrigLen
= m_Text
.Len();
379 ASSERT( rIdx
<= nOrigLen
, "SwTxtNode::Insert: invalid index." );
380 ASSERT( nOrigLen
< STRING_LEN
,
381 "SwTxtNode::Insert: node text with insertion > STRING_LEN." );
383 if ( nOrigLen
== m_Text
.Insert( c
, rIdx
.GetIndex() ).Len() )
388 // leere Hints und Feldattribute an rIdx.GetIndex suchen
392 for ( USHORT i
=0; i
< m_pSwpHints
->Count() &&
393 rIdx
>= *(*m_pSwpHints
)[i
]->GetStart(); ++i
)
395 SwTxtAttr
*pHt
= m_pSwpHints
->GetTextHint(i
);
396 pEndIdx
= pHt
->GetEnd();
399 // leere Hints an rIdx.GetIndex ?
400 BOOL bEmpty
= *pEndIdx
== *pHt
->GetStart()
401 && rIdx
== *pHt
->GetStart();
405 m_pSwpHints
->DeleteAtPos(i
);
407 *pHt
->GetStart() -= 1;
416 // den Frames Bescheid sagen
417 SwInsChr
aHint( rIdx
.GetIndex()-1 );
418 SwModify::Modify( 0, &aHint
);
422 inline BOOL
InRange(xub_StrLen nIdx
, xub_StrLen nStart
, xub_StrLen nEnd
) {
423 return ((nIdx
>=nStart
) && (nIdx
<= nEnd
));
427 * void SwTxtNode::RstAttr(const SwIndex &rIdx, USHORT nLen)
429 * Deletes all attributes, starting at position rIdx, for length nLen.
433 * 1) The attribute is completely in the deletion range:
435 * 2) The end of the attribute is in the deletion range:
436 * -> delete it, then re-insert it with new end
437 * 3) The start of the attribute is in the deletion range:
438 * -> delete it, then re-insert it with new start
439 * 4) The attribute contains the deletion range:
441 * -> Delete, re-insert from old start to start of deletion range
442 * -> insert new attribute from end of deletion range to old end
443 * 5) The attribute is outside the deletion range
447 void SwTxtNode::RstAttr(const SwIndex
&rIdx
, xub_StrLen nLen
, USHORT nWhich
,
448 const SfxItemSet
* pSet
, BOOL bInclRefToxMark
)
451 if ( !GetpSwpHints() )
455 xub_StrLen nStt
= rIdx
.GetIndex();
456 xub_StrLen nEnd
= nStt
+ nLen
;
457 xub_StrLen
*pAttrEnd
;
458 xub_StrLen nAttrStart
;
461 BOOL bChanged
= FALSE
;
463 // nMin and nMax initialized to maximum / minimum (inverse)
464 xub_StrLen nMin
= m_Text
.Len();
465 xub_StrLen nMax
= nStt
;
467 const BOOL bNoLen
= !nMin
;
469 // We have to remember the "new" attributes, which have
470 // been introduced by splitting surrounding attributes (case 4).
471 // They may not be forgotten inside the "Forget" function
472 //std::vector< const SwTxtAttr* > aNewAttributes;
474 // iterate over attribute array until start of attribute is behind
476 while ((i
< m_pSwpHints
->Count()) &&
477 ((( nAttrStart
= *(*m_pSwpHints
)[i
]->GetStart()) < nEnd
) || nLen
==0) )
479 pHt
= m_pSwpHints
->GetTextHint(i
);
481 // attributes without end stay in!
482 pAttrEnd
= pHt
->GetEnd();
489 // Default behavior is to process all attributes:
490 bool bSkipAttr
= false;;
491 boost::shared_ptr
<SfxItemSet
> pStyleHandle
;
493 // 1. case: We want to reset only the attributes listed in pSet:
496 bSkipAttr
= SFX_ITEM_SET
!= pSet
->GetItemState( pHt
->Which(), FALSE
);
497 if ( bSkipAttr
&& RES_TXTATR_AUTOFMT
== pHt
->Which() )
499 // if the current attribute is an autostyle, we have to check if the autostyle
500 // and pSet have any attributes in common. If so, pStyleHandle will contain
501 // a handle to AutoStyle / pSet:
502 bSkipAttr
= !lcl_HaveCommonAttributes( getIDocumentStyleAccess(), pSet
, 0, *static_cast<const SwFmtAutoFmt
&>(pHt
->GetAttr()).GetStyleHandle(), pStyleHandle
);
507 // 2. case: We want to reset only the attributes with WhichId nWhich:
508 bSkipAttr
= nWhich
!= pHt
->Which();
509 if ( bSkipAttr
&& RES_TXTATR_AUTOFMT
== pHt
->Which() )
511 bSkipAttr
= !lcl_HaveCommonAttributes( getIDocumentStyleAccess(), 0, nWhich
, *static_cast<const SwFmtAutoFmt
&>(pHt
->GetAttr()).GetStyleHandle(), pStyleHandle
);
514 else if ( !bInclRefToxMark
)
516 // 3. case: Reset all attributes except from ref/toxmarks:
517 bSkipAttr
= RES_TXTATR_REFMARK
== pHt
->Which() ||
518 RES_TXTATR_TOXMARK
== pHt
->Which();
528 if( nStt
<= nAttrStart
) // Faelle: 1,3,5
530 if( nEnd
> nAttrStart
531 || ( nEnd
== *pAttrEnd
&& nEnd
==nAttrStart
) )
534 if ( nMin
> nAttrStart
)
536 if ( nMax
< *pAttrEnd
)
538 // Falls wir nur ein nichtaufgespanntes Attribut entfernen,
539 // tun wir mal so, als ob sich nichts geaendert hat.
540 bChanged
= bChanged
|| nEnd
> nAttrStart
|| bNoLen
;
541 if( *pAttrEnd
<= nEnd
) // Fall: 1
543 const xub_StrLen nAttrEnd
= *pAttrEnd
;
545 m_pSwpHints
->DeleteAtPos(i
);
548 if ( pStyleHandle
.get() )
550 SwTxtAttr
* pNew
= MakeTxtAttr( *pStyleHandle
, nAttrStart
, nAttrEnd
);
551 Insert( pNew
, nsSetAttrMode::SETATTR_NOHINTADJUST
);
554 // if the last attribute is a Field, the HintsArray is
560 // beim DeleteAtPos wird ein Resort ausgefuehrt!!
561 // darum muessen wir wieder bei 0 anfangen!!!
562 // ueber den Fall 3 koennen Attribute nach hinten
563 // verschoben worden sein; damit stimmt jetzt das i
571 m_pSwpHints
->NoteInHistory( pHt
);
572 *pHt
->GetStart() = nEnd
;
573 m_pSwpHints
->NoteInHistory( pHt
, TRUE
);
575 if ( pStyleHandle
.get() && nAttrStart
< nEnd
)
577 SwTxtAttr
* pNew
= MakeTxtAttr( *pStyleHandle
, nAttrStart
, nEnd
);
578 Insert( pNew
, nsSetAttrMode::SETATTR_NOHINTADJUST
);
585 else // Faelle: 2,4,5
586 if( *pAttrEnd
> nStt
) // Faelle: 2,4
588 if( *pAttrEnd
< nEnd
) // Fall: 2
590 if ( nMin
> nAttrStart
)
592 if ( nMax
< *pAttrEnd
)
596 const xub_StrLen nAttrEnd
= *pAttrEnd
;
598 m_pSwpHints
->NoteInHistory( pHt
);
600 m_pSwpHints
->NoteInHistory( pHt
, TRUE
);
602 if ( pStyleHandle
.get() )
604 SwTxtAttr
* pNew
= MakeTxtAttr( *pStyleHandle
, nStt
, nAttrEnd
);
605 Insert( pNew
, nsSetAttrMode::SETATTR_NOHINTADJUST
);
608 else if( nLen
) // Fall: 4
609 { // bei Lange 0 werden beide Hints vom Insert(Ht)
610 // wieder zu einem zusammengezogen !!!!
611 if ( nMin
> nAttrStart
)
613 if ( nMax
< *pAttrEnd
)
616 xub_StrLen nTmpEnd
= *pAttrEnd
;
617 m_pSwpHints
->NoteInHistory( pHt
);
619 m_pSwpHints
->NoteInHistory( pHt
, TRUE
);
621 if ( pStyleHandle
.get() && nStt
< nEnd
)
623 SwTxtAttr
* pNew
= MakeTxtAttr( *pStyleHandle
, nStt
, nEnd
);
624 Insert( pNew
, nsSetAttrMode::SETATTR_NOHINTADJUST
);
629 SwTxtAttr
* pNew
= MakeTxtAttr( pHt
->GetAttr(), nEnd
, nTmpEnd
);
632 SwTxtCharFmt
* pCharFmt
= dynamic_cast<SwTxtCharFmt
*>(pHt
);
634 static_cast<SwTxtCharFmt
*>(pNew
)->SetSortNumber( pCharFmt
->GetSortNumber() );
636 Insert( pNew
, nsSetAttrMode::SETATTR_NOHINTADJUST
);
640 // jetzt kein i+1, weil das eingefuegte Attribut
641 // ein anderes auf die Position geschoben hat !
654 m_pSwpHints
->Resort();
656 //TxtFrm's reagieren auf aHint, andere auf aNew
657 SwUpdateAttr
aHint( nMin
, nMax
, 0 );
658 SwModify::Modify( 0, &aHint
);
659 SwFmtChg
aNew( GetFmtColl() );
660 SwModify::Modify( 0, &aNew
);
666 /*************************************************************************
667 * SwTxtNode::GetCurWord()
669 * Aktuelles Wort zurueckliefern:
670 * Wir suchen immer von links nach rechts, es wird also das Wort
671 * vor nPos gesucht. Es sei denn, wir befinden uns am Anfang des
672 * Absatzes, dann wird das erste Wort zurueckgeliefert.
673 * Wenn dieses erste Wort nur aus Whitespaces besteht, returnen wir
674 * einen leeren String.
675 *************************************************************************/
677 XubString
SwTxtNode::GetCurWord( xub_StrLen nPos
) const
679 ASSERT( nPos
<= m_Text
.Len(), "SwTxtNode::GetCurWord: invalid index." );
685 const uno::Reference
< XBreakIterator
> &rxBreak
= pBreakIt
->GetBreakIter();
688 sal_Int16 nWordType
= WordType::DICTIONARY_WORD
;
689 lang::Locale
aLocale( pBreakIt
->GetLocale( GetLang( nPos
) ) );
691 BOOL bBegin
= rxBreak
->isBeginWord( m_Text
, nPos
, aLocale
, nWordType
);
692 BOOL bEnd
= rxBreak
->isEndWord ( m_Text
, nPos
, aLocale
, nWordType
);
697 rxBreak
->getWordBoundary( m_Text
, nPos
, aLocale
, nWordType
, TRUE
);
699 // if no word was found use previous word (if any)
700 if (aBndry
.startPos
== aBndry
.endPos
)
702 aBndry
= rxBreak
->previousWord( m_Text
, nPos
, aLocale
, nWordType
);
706 // check if word was found and if it uses a symbol font, if so
707 // enforce returning an empty string
708 if (aBndry
.endPos
!= aBndry
.startPos
&& IsSymbol( (xub_StrLen
)aBndry
.startPos
))
709 aBndry
.endPos
= aBndry
.startPos
;
711 return m_Text
.Copy( static_cast<xub_StrLen
>(aBndry
.startPos
),
712 static_cast<xub_StrLen
>(aBndry
.endPos
- aBndry
.startPos
) );
715 SwScanner::SwScanner( const SwTxtNode
& rNd
, const String
& rTxt
, const LanguageType
* pLang
,
716 const ModelToViewHelper::ConversionMap
* pConvMap
,
717 USHORT nType
, xub_StrLen nStart
, xub_StrLen nEnde
, BOOL bClp
)
718 : rNode( rNd
), rText( rTxt
), pLanguage( pLang
), pConversionMap( pConvMap
), nLen( 0 ), nWordType( nType
), bClip( bClp
)
720 ASSERT( rText
.Len(), "SwScanner: EmptyString" );
721 nStartPos
= nBegin
= nStart
;
726 aCurrLang
= *pLanguage
;
730 ModelToViewHelper::ModelPosition aModelBeginPos
= ModelToViewHelper::ConvertToModelPosition( pConversionMap
, nBegin
);
731 const xub_StrLen nModelBeginPos
= (xub_StrLen
)aModelBeginPos
.mnPos
;
732 aCurrLang
= rNd
.GetLang( nModelBeginPos
);
736 BOOL
SwScanner::NextWord()
738 nBegin
= nBegin
+ nLen
;
741 CharClass
& rCC
= GetAppCharClass();
742 lang::Locale aOldLocale
= rCC
.getLocale();
746 // skip non-letter characters:
747 while ( nBegin
< rText
.Len() )
749 if ( !lcl_IsSkippableWhiteSpace( rText
.GetChar( nBegin
) ) )
753 const USHORT nNextScriptType
= pBreakIt
->GetBreakIter()->getScriptType( rText
, nBegin
);
754 ModelToViewHelper::ModelPosition aModelBeginPos
= ModelToViewHelper::ConvertToModelPosition( pConversionMap
, nBegin
);
755 const xub_StrLen nBeginModelPos
= (xub_StrLen
)aModelBeginPos
.mnPos
;
756 aCurrLang
= rNode
.GetLang( nBeginModelPos
, 1, nNextScriptType
);
759 if ( nWordType
!= i18n::WordType::WORD_COUNT
)
761 rCC
.setLocale( pBreakIt
->GetLocale( aCurrLang
) );
762 if ( rCC
.isLetterNumeric( rText
.GetChar( nBegin
) ) )
771 if ( nBegin
>= rText
.Len() || nBegin
>= nEndPos
)
774 // get the word boundaries
775 aBound
= pBreakIt
->GetBreakIter()->getWordBoundary( rText
, nBegin
,
776 pBreakIt
->GetLocale( aCurrLang
), nWordType
, sal_True
);
778 //no word boundaries could be found
779 if(aBound
.endPos
== aBound
.startPos
)
782 //if a word before is found it has to be searched for the next
783 if(aBound
.endPos
== nBegin
)
787 } // end while( true )
789 rCC
.setLocale( aOldLocale
);
791 // we have to differenciate between these cases:
792 if ( aBound
.startPos
<= nBegin
)
794 ASSERT( aBound
.endPos
>= nBegin
, "Unexpected aBound result" )
796 // restrict boundaries to script boundaries and nEndPos
797 const USHORT nCurrScript
=
798 pBreakIt
->GetBreakIter()->getScriptType( rText
, nBegin
);
800 XubString aTmpWord
= rText
.Copy( nBegin
, static_cast<xub_StrLen
>(aBound
.endPos
- nBegin
) );
801 const sal_Int32 nScriptEnd
= nBegin
+
802 pBreakIt
->GetBreakIter()->endOfScript( aTmpWord
, 0, nCurrScript
);
803 const sal_Int32 nEnd
= Min( aBound
.endPos
, nScriptEnd
);
805 // restrict word start to last script change position
806 sal_Int32 nScriptBegin
= 0;
807 if ( aBound
.startPos
< nBegin
)
809 // search from nBegin backwards until the next script change
810 aTmpWord
= rText
.Copy( static_cast<xub_StrLen
>(aBound
.startPos
),
811 static_cast<xub_StrLen
>(nBegin
- aBound
.startPos
+ 1) );
812 nScriptBegin
= aBound
.startPos
+
813 pBreakIt
->GetBreakIter()->beginOfScript( aTmpWord
, nBegin
- aBound
.startPos
,
817 nBegin
= (xub_StrLen
)Max( aBound
.startPos
, nScriptBegin
);
818 nLen
= (xub_StrLen
)(nEnd
- nBegin
);
822 const USHORT nCurrScript
=
823 pBreakIt
->GetBreakIter()->getScriptType( rText
, aBound
.startPos
);
824 XubString aTmpWord
= rText
.Copy( static_cast<xub_StrLen
>(aBound
.startPos
),
825 static_cast<xub_StrLen
>(aBound
.endPos
- aBound
.startPos
) );
826 const sal_Int32 nScriptEnd
= aBound
.startPos
+
827 pBreakIt
->GetBreakIter()->endOfScript( aTmpWord
, 0, nCurrScript
);
828 const sal_Int32 nEnd
= Min( aBound
.endPos
, nScriptEnd
);
829 nBegin
= (xub_StrLen
)aBound
.startPos
;
830 nLen
= (xub_StrLen
)(nEnd
- nBegin
);
833 // optionally clip the result of getWordBoundaries:
836 aBound
.startPos
= Max( (xub_StrLen
)aBound
.startPos
, nStartPos
);
837 aBound
.endPos
= Min( (xub_StrLen
)aBound
.endPos
, nEndPos
);
838 nBegin
= (xub_StrLen
)aBound
.startPos
;
839 nLen
= (xub_StrLen
)(aBound
.endPos
- nBegin
);
845 aWord
= rText
.Copy( nBegin
, nLen
);
851 USHORT
SwTxtNode::Spell(SwSpellArgs
* pArgs
)
853 // Die Aehnlichkeiten zu SwTxtFrm::_AutoSpell sind beabsichtigt ...
854 // ACHTUNG: Ev. Bugs in beiden Routinen fixen!
856 uno::Reference
<beans::XPropertySet
> xProp( GetLinguPropertySet() );
858 xub_StrLen nBegin
, nEnd
;
860 // modify string according to redline information and hidden text
861 const XubString
aOldTxt( m_Text
);
862 const bool bRestoreString
=
863 lcl_MaskRedlinesAndHiddenText( *this, m_Text
, 0, m_Text
.Len() ) > 0;
865 if ( pArgs
->pStartNode
!= this )
868 nBegin
= pArgs
->pStartIdx
->GetIndex();
870 nEnd
= ( pArgs
->pEndNode
!= this )
872 : pArgs
->pEndIdx
->GetIndex();
874 pArgs
->xSpellAlt
= NULL
;
878 // 1. IsWrongDirty = 0 and GetWrong = 0
879 // Everything is checked and correct
880 // 2. IsWrongDirty = 0 and GetWrong = 1
881 // Everything is checked and errors are identified in the wrong list
882 // 3. IsWrongDirty = 1 and GetWrong = 0
883 // Nothing has been checked
884 // 4. IsWrongDirty = 1 and GetWrong = 1
885 // Text has been checked but there is an invalid range in the wrong list
887 // Nothing has to be done for case 1.
888 if ( ( IsWrongDirty() || GetWrong() ) && m_Text
.Len() )
890 if ( nBegin
> m_Text
.Len() )
892 nBegin
= m_Text
.Len();
894 if ( nEnd
> m_Text
.Len() )
901 xub_StrLen nTemp
= GetWrong()->NextWrong( nBegin
);
904 // reset original text
905 if ( bRestoreString
)
916 // In case 2. we pass the wrong list to the scanned, because only
917 // the words in the wrong list have to be checked
918 SwScanner
aScanner( *this, m_Text
, 0, 0,
919 WordType::DICTIONARY_WORD
,
921 while( !pArgs
->xSpellAlt
.is() && aScanner
.NextWord() )
923 const XubString
& rWord
= aScanner
.GetWord();
925 // get next language for next word, consider language attributes
927 LanguageType eActLang
= aScanner
.GetCurrentLanguage();
929 if( rWord
.Len() > 0 && LANGUAGE_NONE
!= eActLang
)
931 if (pArgs
->xSpeller
.is())
933 SvxSpellWrapper::CheckSpellLang( pArgs
->xSpeller
, eActLang
);
934 pArgs
->xSpellAlt
= pArgs
->xSpeller
->spell( rWord
, eActLang
,
935 Sequence
< PropertyValue
>() );
937 if( (pArgs
->xSpellAlt
).is() )
939 if( IsSymbol( aScanner
.GetBegin() ) )
941 pArgs
->xSpellAlt
= NULL
;
945 // make sure the selection build later from the
946 // data below does not include footnotes and other
947 // "in word" character to the left and right in order
948 // to preserve those. Therefore count those "in words"
949 // in order to modify the selection accordingly.
950 const sal_Unicode
* pChar
= rWord
.GetBuffer();
951 xub_StrLen nLeft
= 0;
952 while (pChar
&& *pChar
++ == CH_TXTATR_INWORD
)
954 pChar
= rWord
.Len() ? rWord
.GetBuffer() + rWord
.Len() - 1 : 0;
955 xub_StrLen nRight
= 0;
956 while (pChar
&& *pChar
-- == CH_TXTATR_INWORD
)
959 pArgs
->pStartNode
= this;
960 pArgs
->pEndNode
= this;
961 pArgs
->pStartIdx
->Assign(this, aScanner
.GetEnd() - nRight
);
962 pArgs
->pEndIdx
->Assign(this, aScanner
.GetBegin() + nLeft
);
969 // reset original text
970 if ( bRestoreString
)
975 return pArgs
->xSpellAlt
.is() ? 1 : 0;
979 void SwTxtNode::SetLanguageAndFont( const SwPaM
&rPaM
,
980 LanguageType nLang
, USHORT nLangWhichId
,
981 const Font
*pFont
, USHORT nFontWhichId
)
983 sal_uInt16 aRanges
[] = {
984 nLangWhichId
, nLangWhichId
,
985 nFontWhichId
, nFontWhichId
,
988 aRanges
[2] = aRanges
[3] = 0; // clear entries with font WhichId
990 SwEditShell
*pEditShell
= GetDoc()->GetEditShell();
991 SfxItemSet
aSet( pEditShell
->GetAttrPool(), aRanges
);
992 aSet
.Put( SvxLanguageItem( nLang
, nLangWhichId
) );
994 DBG_ASSERT( pFont
, "target font missing?" );
997 SvxFontItem aFontItem
= (SvxFontItem
&) aSet
.Get( nFontWhichId
);
998 aFontItem
.GetFamilyName() = pFont
->GetName();
999 aFontItem
.GetFamily() = pFont
->GetFamily();
1000 aFontItem
.GetStyleName() = pFont
->GetStyleName();
1001 aFontItem
.GetPitch() = pFont
->GetPitch();
1002 aFontItem
.GetCharSet() = pFont
->GetCharSet();
1003 aSet
.Put( aFontItem
);
1006 GetDoc()->Insert( rPaM
, aSet
, 0 );
1007 // SetAttr( aSet ); <- Does not set language attribute of empty paragraphs correctly,
1008 // <- because since there is no selection the flag to garbage
1009 // <- collect all attributes is set, and therefore attributes spanned
1010 // <- over empty selection are removed.
1015 USHORT
SwTxtNode::Convert( SwConversionArgs
&rArgs
)
1017 // get range of text within node to be converted
1018 // (either all the text or the the text within the selection
1019 // when the conversion was started)
1020 xub_StrLen nTextBegin
, nTextEnd
;
1022 if ( rArgs
.pStartNode
!= this )
1027 nTextBegin
= rArgs
.pStartIdx
->GetIndex();
1028 if (nTextBegin
> m_Text
.Len())
1030 nTextBegin
= m_Text
.Len();
1033 nTextEnd
= ( rArgs
.pEndNode
!= this )
1035 : ::std::min( rArgs
.pEndIdx
->GetIndex(), m_Text
.Len() );
1037 rArgs
.aConvText
= rtl::OUString();
1039 // modify string according to redline information and hidden text
1040 const XubString
aOldTxt( m_Text
);
1041 const bool bRestoreString
=
1042 lcl_MaskRedlinesAndHiddenText( *this, m_Text
, 0, m_Text
.Len() ) > 0;
1044 sal_Bool bFound
= sal_False
;
1045 xub_StrLen nBegin
= nTextBegin
;
1046 xub_StrLen nLen
= 0;
1047 LanguageType nLangFound
= LANGUAGE_NONE
;
1050 if (rArgs
.bAllowImplicitChangesForNotConvertibleText
)
1052 // create SwPaM with mark & point spanning empty paragraph
1053 //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different
1054 SwPaM
aCurPaM( *this, 0 );
1056 SetLanguageAndFont( aCurPaM
,
1057 rArgs
.nConvTargetLang
, RES_CHRATR_CJK_LANGUAGE
,
1058 rArgs
.pTargetFont
, RES_CHRATR_CJK_FONT
);
1063 SwLanguageIterator
aIter( *this, nBegin
);
1065 // find non zero length text portion of appropriate language
1067 nLangFound
= aIter
.GetLanguage();
1068 sal_Bool bLangOk
= (nLangFound
== rArgs
.nConvSrcLang
) ||
1069 (svx::HangulHanjaConversion::IsChinese( nLangFound
) &&
1070 svx::HangulHanjaConversion::IsChinese( rArgs
.nConvSrcLang
));
1072 xub_StrLen nChPos
= aIter
.GetChgPos();
1073 // the position at the end of the paragraph returns -1
1074 // which becomes 65535 when converted to xub_StrLen,
1075 // and thus must be cut to the end of the actual string.
1076 if (nChPos
== (xub_StrLen
) -1)
1078 nChPos
= m_Text
.Len();
1081 nLen
= nChPos
- nBegin
;
1082 bFound
= bLangOk
&& nLen
> 0;
1085 // create SwPaM with mark & point spanning the attributed text
1086 //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different
1087 SwPaM
aCurPaM( *this, nBegin
);
1089 aCurPaM
.GetPoint()->nContent
= nBegin
+ nLen
;
1091 // check script type of selected text
1092 SwEditShell
*pEditShell
= GetDoc()->GetEditShell();
1093 pEditShell
->Push(); // save current cursor on stack
1094 pEditShell
->SetSelection( aCurPaM
);
1095 sal_Bool bIsAsianScript
= (SCRIPTTYPE_ASIAN
== pEditShell
->GetScriptType());
1096 pEditShell
->Pop( sal_False
); // restore cursor from stack
1098 if (!bIsAsianScript
&& rArgs
.bAllowImplicitChangesForNotConvertibleText
)
1100 SetLanguageAndFont( aCurPaM
,
1101 rArgs
.nConvTargetLang
, RES_CHRATR_CJK_LANGUAGE
,
1102 rArgs
.pTargetFont
, RES_CHRATR_CJK_FONT
);
1104 nBegin
= nChPos
; // start of next language portion
1106 } while (!bFound
&& aIter
.Next()); /* loop while nothing was found and still sth is left to be searched */
1109 // keep resulting text within selection / range of text to be converted
1110 if (nBegin
< nTextBegin
)
1111 nBegin
= nTextBegin
;
1112 if (nBegin
+ nLen
> nTextEnd
)
1113 nLen
= nTextEnd
- nBegin
;
1114 sal_Bool bInSelection
= nBegin
< nTextEnd
;
1116 if (bFound
&& bInSelection
) // convertible text found within selection/range?
1118 const XubString aTxtPortion
= m_Text
.Copy( nBegin
, nLen
);
1119 DBG_ASSERT( m_Text
.Len() > 0, "convertible text portion missing!" );
1120 rArgs
.aConvText
= m_Text
.Copy( nBegin
, nLen
);
1121 rArgs
.nConvTextLang
= nLangFound
;
1123 // position where to start looking in next iteration (after current ends)
1124 rArgs
.pStartNode
= this;
1125 rArgs
.pStartIdx
->Assign(this, nBegin
+ nLen
);
1126 // end position (when we have travelled over the whole document)
1127 rArgs
.pEndNode
= this;
1128 rArgs
.pEndIdx
->Assign(this, nBegin
);
1131 // restore original text
1132 if ( bRestoreString
)
1137 return rArgs
.aConvText
.getLength() ? 1 : 0;
1140 // Die Aehnlichkeiten zu SwTxtNode::Spell sind beabsichtigt ...
1141 // ACHTUNG: Ev. Bugs in beiden Routinen fixen!
1142 SwRect
SwTxtFrm::_AutoSpell( const SwCntntNode
* pActNode
, const SwViewOption
& rViewOpt
, xub_StrLen nActPos
)
1145 #if OSL_DEBUG_LEVEL > 1
1146 static BOOL bStop
= FALSE
;
1150 // Die Aehnlichkeiten zu SwTxtNode::Spell sind beabsichtigt ...
1151 // ACHTUNG: Ev. Bugs in beiden Routinen fixen!
1152 SwTxtNode
*pNode
= GetTxtNode();
1153 if( pNode
!= pActNode
|| !nActPos
)
1154 nActPos
= STRING_LEN
;
1156 SwAutoCompleteWord
& rACW
= SwDoc::GetAutoCompleteWords();
1158 // modify string according to redline information and hidden text
1159 const XubString
aOldTxt( pNode
->GetTxt() );
1160 const bool bRestoreString
=
1161 lcl_MaskRedlinesAndHiddenText( *pNode
, pNode
->m_Text
,
1162 0, pNode
->GetTxt().Len() ) > 0;
1164 // a change of data indicates that at least one word has been modified
1165 const sal_Bool bRedlineChg
=
1166 ( pNode
->GetTxt().GetBuffer() != aOldTxt
.GetBuffer() );
1168 xub_StrLen nBegin
= 0;
1169 xub_StrLen nEnd
= pNode
->GetTxt().Len();
1170 xub_StrLen nInsertPos
= 0;
1171 xub_StrLen nChgStart
= STRING_LEN
;
1172 xub_StrLen nChgEnd
= 0;
1173 xub_StrLen nInvStart
= STRING_LEN
;
1174 xub_StrLen nInvEnd
= 0;
1176 const sal_Bool bAddAutoCmpl
= pNode
->IsAutoCompleteWordDirty() &&
1177 rViewOpt
.IsAutoCompleteWords();
1179 if( pNode
->GetWrong() )
1181 nBegin
= pNode
->GetWrong()->GetBeginInv();
1182 if( STRING_LEN
!= nBegin
)
1184 nEnd
= pNode
->GetWrong()->GetEndInv();
1185 if ( nEnd
> pNode
->GetTxt().Len() )
1187 nEnd
= pNode
->GetTxt().Len();
1191 // get word around nBegin, we start at nBegin - 1
1192 if ( STRING_LEN
!= nBegin
)
1197 LanguageType eActLang
= pNode
->GetLang( nBegin
);
1199 pBreakIt
->GetBreakIter()->getWordBoundary( pNode
->GetTxt(), nBegin
,
1200 pBreakIt
->GetLocale( eActLang
),
1201 WordType::DICTIONARY_WORD
, TRUE
);
1202 nBegin
= xub_StrLen(aBound
.startPos
);
1205 // get the position in the wrong list
1206 nInsertPos
= pNode
->GetWrong()->GetWrongPos( nBegin
);
1208 // sometimes we have to skip one entry
1209 if( nInsertPos
< pNode
->GetWrong()->Count() &&
1210 nBegin
== pNode
->GetWrong()->Pos( nInsertPos
) +
1211 pNode
->GetWrong()->Len( nInsertPos
) )
1215 BOOL bFresh
= nBegin
< nEnd
;
1219 //! register listener to LinguServiceEvents now in order to get
1220 //! notified about relevant changes in the future
1221 SwModule
*pModule
= SW_MOD();
1222 if (!pModule
->GetLngSvcEvtListener().is())
1223 pModule
->CreateLngSvcEvtListener();
1225 uno::Reference
< XSpellChecker1
> xSpell( ::GetSpellChecker() );
1226 SwDoc
* pDoc
= pNode
->GetDoc();
1228 SwScanner
aScanner( *pNode
, pNode
->GetTxt(), 0, 0,
1229 WordType::DICTIONARY_WORD
, nBegin
, nEnd
);
1231 while( aScanner
.NextWord() )
1233 const XubString
& rWord
= aScanner
.GetWord();
1234 nBegin
= aScanner
.GetBegin();
1235 xub_StrLen nLen
= aScanner
.GetLen();
1237 // get next language for next word, consider language attributes
1239 LanguageType eActLang
= aScanner
.GetCurrentLanguage();
1242 bSpell
= xSpell
.is() ? xSpell
->hasLanguage( eActLang
) : FALSE
;
1243 if( bSpell
&& rWord
.Len() > 0 )
1245 // check for: bAlter => xHyphWord.is()
1246 DBG_ASSERT(!bSpell
|| xSpell
.is(), "NULL pointer");
1248 if( !xSpell
->isValid( rWord
, eActLang
, Sequence
< PropertyValue
>() ) )
1250 xub_StrLen nSmartTagStt
= nBegin
;
1251 xub_StrLen nDummy
= 1;
1252 if ( !pNode
->GetSmartTags() || !pNode
->GetSmartTags()->InWrongWord( nSmartTagStt
, nDummy
) )
1254 if( !pNode
->GetWrong() )
1256 pNode
->SetWrong( new SwWrongList( WRONGLIST_SPELL
) );
1257 pNode
->GetWrong()->SetInvalid( 0, nEnd
);
1259 if( pNode
->GetWrong()->Fresh( nChgStart
, nChgEnd
,
1260 nBegin
, nLen
, nInsertPos
, nActPos
) )
1261 pNode
->GetWrong()->Insert( rtl::OUString(), 0, nBegin
, nLen
, nInsertPos
++ );
1265 nInvEnd
= nBegin
+ nLen
;
1269 else if( bAddAutoCmpl
&& rACW
.GetMinWordLen() <= rWord
.Len() )
1273 XubString
rNewWord( rWord
);
1274 rACW
.InsertWord( rNewWord
, *pDoc
);
1277 rACW
.InsertWord( rWord
, *pDoc
);
1283 // reset original text
1284 // i63141 before calling GetCharRect(..) with formatting!
1285 if ( bRestoreString
)
1287 pNode
->m_Text
= aOldTxt
;
1289 if( pNode
->GetWrong() )
1292 pNode
->GetWrong()->Fresh( nChgStart
, nChgEnd
,
1293 nEnd
, 0, nInsertPos
, nActPos
);
1296 // Calculate repaint area:
1298 if( nChgStart
< nChgEnd
)
1300 aRect
= lcl_CalculateRepaintRect( *this, nChgStart
, nChgEnd
);
1303 pNode
->GetWrong()->SetInvalid( nInvStart
, nInvEnd
);
1304 pNode
->SetWrongDirty( STRING_LEN
!= pNode
->GetWrong()->GetBeginInv() );
1305 if( !pNode
->GetWrong()->Count() && ! pNode
->IsWrongDirty() )
1306 pNode
->SetWrong( NULL
);
1309 pNode
->SetWrongDirty( false );
1312 pNode
->SetAutoCompleteWordDirty( false );
1317 /** Function: SmartTagScan
1319 Function scans words in current text and checks them in the
1320 smarttag libraries. If the check returns true to bounds of the
1321 recognized words are stored into a list which is used later for drawing
1324 @param SwCntntNode* pActNode
1326 @param xub_StrLen nActPos
1328 @return SwRect: Repaint area
1330 SwRect
SwTxtFrm::SmartTagScan( SwCntntNode
* /*pActNode*/, xub_StrLen
/*nActPos*/ )
1333 SwTxtNode
*pNode
= GetTxtNode();
1334 const rtl::OUString
& rText
= pNode
->GetTxt();
1336 // Iterate over language portions
1337 SmartTagMgr
& rSmartTagMgr
= SwSmartTagMgr::Get();
1339 SwWrongList
* pSmartTagList
= pNode
->GetSmartTags();
1341 xub_StrLen nBegin
= 0;
1342 xub_StrLen nEnd
= static_cast< xub_StrLen
>(rText
.getLength());
1344 if ( pSmartTagList
)
1346 if ( pSmartTagList
->GetBeginInv() != STRING_LEN
)
1348 nBegin
= pSmartTagList
->GetBeginInv();
1349 nEnd
= Min( pSmartTagList
->GetEndInv(), (xub_StrLen
)rText
.getLength() );
1351 if ( nBegin
< nEnd
)
1353 const LanguageType aCurrLang
= pNode
->GetLang( nBegin
);
1354 const com::sun::star::lang::Locale aCurrLocale
= pBreakIt
->GetLocale( aCurrLang
);
1355 nBegin
= static_cast< xub_StrLen
>(pBreakIt
->GetBreakIter()->beginOfSentence( rText
, nBegin
, aCurrLocale
));
1356 nEnd
= static_cast< xub_StrLen
>(Min( rText
.getLength(), pBreakIt
->GetBreakIter()->endOfSentence( rText
, nEnd
, aCurrLocale
) ));
1361 const USHORT nNumberOfEntries
= pSmartTagList
? pSmartTagList
->Count() : 0;
1362 USHORT nNumberOfRemovedEntries
= 0;
1363 USHORT nNumberOfInsertedEntries
= 0;
1365 // clear smart tag list between nBegin and nEnd:
1366 if ( 0 != nNumberOfEntries
)
1368 xub_StrLen nChgStart
= STRING_LEN
;
1369 xub_StrLen nChgEnd
= 0;
1370 const USHORT nCurrentIndex
= pSmartTagList
->GetWrongPos( nBegin
);
1371 pSmartTagList
->Fresh( nChgStart
, nChgEnd
, nBegin
, nEnd
- nBegin
, nCurrentIndex
, STRING_LEN
);
1372 nNumberOfRemovedEntries
= nNumberOfEntries
- pSmartTagList
->Count();
1375 if ( nBegin
< nEnd
)
1377 // Expand the string:
1378 rtl::OUString aExpandText
;
1379 const ModelToViewHelper::ConversionMap
* pConversionMap
=
1380 pNode
->BuildConversionMap( aExpandText
);
1382 // Ownership ov ConversionMap is passed to SwXTextMarkup object!
1383 Reference
< com::sun::star::text::XTextMarkup
> xTextMarkup
=
1384 new SwXTextMarkup( *pNode
, pConversionMap
);
1386 Reference
< ::com::sun::star::frame::XController
> xController
= pNode
->GetDoc()->GetDocShell()->GetController();
1388 xub_StrLen nLangBegin
= nBegin
;
1389 xub_StrLen nLangEnd
= nEnd
;
1391 // smart tag recognization has to be done for each language portion:
1392 SwLanguageIterator
aIter( *pNode
, nLangBegin
);
1396 const LanguageType nLang
= aIter
.GetLanguage();
1397 const com::sun::star::lang::Locale aLocale
= pBreakIt
->GetLocale( nLang
);
1398 nLangEnd
= Min( nEnd
, aIter
.GetChgPos() );
1400 const sal_uInt32 nExpandBegin
= ModelToViewHelper::ConvertToViewPosition( pConversionMap
, nLangBegin
);
1401 const sal_uInt32 nExpandEnd
= ModelToViewHelper::ConvertToViewPosition( pConversionMap
, nLangEnd
);
1403 rSmartTagMgr
.Recognize( aExpandText
, xTextMarkup
, xController
, aLocale
, nExpandBegin
, nExpandEnd
- nExpandBegin
);
1405 nLangBegin
= nLangEnd
;
1407 while ( aIter
.Next() && nLangEnd
< nEnd
);
1409 pSmartTagList
= pNode
->GetSmartTags();
1411 const USHORT nNumberOfEntriesAfterRecognize
= pSmartTagList
? pSmartTagList
->Count() : 0;
1412 nNumberOfInsertedEntries
= nNumberOfEntriesAfterRecognize
- ( nNumberOfEntries
- nNumberOfRemovedEntries
);
1418 // Update WrongList stuff
1420 pSmartTagList
->SetInvalid( STRING_LEN
, 0 );
1421 pNode
->SetSmartTagDirty( STRING_LEN
!= pSmartTagList
->GetBeginInv() );
1423 if( !pSmartTagList
->Count() && !pNode
->IsSmartTagDirty() )
1424 pNode
->SetSmartTags( NULL
);
1427 // Calculate repaint area:
1429 #if OSL_DEBUG_LEVEL > 1
1430 const USHORT nNumberOfEntriesAfterRecognize2
= pSmartTagList
->Count();
1431 (void) nNumberOfEntriesAfterRecognize2
;
1433 if ( nBegin
< nEnd
&& ( 0 != nNumberOfRemovedEntries
||
1434 0 != nNumberOfInsertedEntries
) )
1436 aRet
= lcl_CalculateRepaintRect( *this, nBegin
, nEnd
);
1440 pNode
->SetSmartTagDirty( false );
1446 // Wird vom CollectAutoCmplWords gerufen
1447 void SwTxtFrm::CollectAutoCmplWrds( SwCntntNode
* pActNode
, xub_StrLen nActPos
)
1449 SwTxtNode
*pNode
= GetTxtNode();
1450 if( pNode
!= pActNode
|| !nActPos
)
1451 nActPos
= STRING_LEN
;
1453 SwDoc
* pDoc
= pNode
->GetDoc();
1454 SwAutoCompleteWord
& rACW
= SwDoc::GetAutoCompleteWords();
1456 xub_StrLen nBegin
= 0;
1457 xub_StrLen nEnd
= pNode
->GetTxt().Len();
1459 BOOL bACWDirty
= FALSE
, bAnyWrd
= FALSE
;
1465 SwScanner
aScanner( *pNode
, pNode
->GetTxt(), 0, 0,
1466 WordType::DICTIONARY_WORD
, nBegin
, nEnd
);
1467 while( aScanner
.NextWord() )
1469 nBegin
= aScanner
.GetBegin();
1470 nLen
= aScanner
.GetLen();
1471 if( rACW
.GetMinWordLen() <= nLen
)
1473 const XubString
& rWord
= aScanner
.GetWord();
1475 if( nActPos
< nBegin
|| ( nBegin
+ nLen
) < nActPos
)
1477 if( rACW
.GetMinWordLen() <= rWord
.Len() )
1478 rACW
.InsertWord( rWord
, *pDoc
);
1486 if ( Application::AnyInput( INPUT_ANY
) )
1493 if( bAnyWrd
&& !bACWDirty
)
1494 pNode
->SetAutoCompleteWordDirty( FALSE
);
1498 /*************************************************************************
1499 * SwTxtNode::Hyphenate
1500 *************************************************************************/
1501 // Findet den TxtFrm und sucht dessen CalcHyph
1503 BOOL
SwTxtNode::Hyphenate( SwInterHyphInfo
&rHyphInf
)
1505 // Abkuerzung: am Absatz ist keine Sprache eingestellt:
1506 if ( LANGUAGE_NONE
== USHORT( GetSwAttrSet().GetLanguage().GetLanguage() )
1507 && USHRT_MAX
== GetLang( 0, m_Text
.Len() ) )
1509 if( !rHyphInf
.IsCheck() )
1510 rHyphInf
.SetNoLang( TRUE
);
1514 if( pLinguNode
!= this )
1517 pLinguFrm
= (SwTxtFrm
*)GetFrm( (Point
*)(rHyphInf
.GetCrsrPos()) );
1519 SwTxtFrm
*pFrm
= pLinguFrm
;
1521 pFrm
= &(pFrm
->GetFrmAtOfst( rHyphInf
.nStart
));
1524 // 4935: Seit der Trennung ueber Sonderbereiche sind Faelle
1525 // moeglich, in denen kein Frame zum Node vorliegt.
1526 // Also kein ASSERT!
1527 #if OSL_DEBUG_LEVEL > 1
1528 ASSERT( pFrm
, "!SwTxtNode::Hyphenate: can't find any frame" );
1535 if( pFrm
->Hyphenate( rHyphInf
) )
1537 // Das Layout ist nicht robust gegen "Direktformatierung"
1538 // (7821, 7662, 7408); vgl. layact.cxx,
1539 // SwLayAction::_TurboAction(), if( !pCnt->IsValid() ...
1540 pFrm
->SetCompletePaint();
1543 pFrm
= (SwTxtFrm
*)(pFrm
->GetFollow());
1546 rHyphInf
.nLen
= rHyphInf
.nLen
- (pFrm
->GetOfst() - rHyphInf
.nStart
);
1547 rHyphInf
.nStart
= pFrm
->GetOfst();
1553 #ifdef LINGU_STATISTIK
1556 SwLinguStatistik aSwLinguStat
;
1559 void SwLinguStatistik::Flush()
1564 static char *pLogName
= 0;
1565 const BOOL bFirstOpen
= pLogName
? FALSE
: TRUE
;
1568 char *pPath
= getenv( "TEMP" );
1569 char *pName
= "swlingu.stk";
1574 const int nLen
= strlen(pPath
);
1575 // fuer dieses new wird es kein delete geben.
1576 pLogName
= new char[nLen
+ strlen(pName
) + 3];
1577 if(nLen
&& (pPath
[nLen
-1] == '\\') || (pPath
[nLen
-1] == '/'))
1578 snprintf( pLogName
, sizeof(pLogName
), "%s%s", pPath
, pName
);
1580 snprintf( pLogName
, sizeof(pLogName
), "%s/%s", pPath
, pName
);
1583 SvFileStream
aStream( String::CreateFromAscii(pLogName
), (bFirstOpen
1584 ? STREAM_WRITE
| STREAM_TRUNC
1587 if( !aStream
.GetError() )
1590 aStream
<< "\nLinguistik-Statistik\n";
1591 aStream
<< endl
<< ++nFlushCnt
<< ". Messung\n";
1592 aStream
<< "Rechtschreibung\n";
1593 aStream
<< "gepruefte Worte: \t" << nWords
<< endl
;
1594 aStream
<< "als fehlerhaft erkannt:\t" << nWrong
<< endl
;
1595 aStream
<< "Alternativvorschlaege:\t" << nAlter
<< endl
;
1597 aStream
<< "Durchschnitt:\t\t" << nAlter
*1.0 / nWrong
<< endl
;
1598 aStream
<< "Dauer (msec):\t\t" << nSpellTime
<< endl
;
1599 aStream
<< "\nThesaurus\n";
1600 aStream
<< "Synonyme gesamt:\t" << nSynonym
<< endl
;
1602 aStream
<< "Synonym-Durchschnitt:\t" <<
1603 nSynonym
*1.0 / ( nWords
- nNoSynonym
) << endl
;
1604 aStream
<< "ohne Synonyme:\t\t" << nNoSynonym
<< endl
;
1605 aStream
<< "Bedeutungen gesamt:\t" << nSynonym
<< endl
;
1606 aStream
<< "keine Bedeutungen:\t"<< nNoSynonym
<< endl
;
1607 aStream
<< "Dauer (msec):\t\t" << nTheTime
<< endl
;
1608 aStream
<< "\nHyphenator\n";
1609 aStream
<< "Trennstellen gesamt:\t" << nHyphens
<< endl
;
1611 aStream
<< "Hyphen-Durchschnitt:\t" <<
1612 nHyphens
*1.0 / ( nWords
- nNoHyph
- nHyphErr
) << endl
;
1613 aStream
<< "keine Trennstellen:\t" << nNoHyph
<< endl
;
1614 aStream
<< "Trennung verweigert:\t" << nHyphErr
<< endl
;
1615 aStream
<< "Dauer (msec):\t\t" << nHyphTime
<< endl
;
1616 aStream
<< "---------------------------------------------\n";
1618 nWords
= nWrong
= nAlter
= nSynonym
= nNoSynonym
=
1619 nHyphens
= nNoHyph
= nHyphErr
= nSpellTime
= nTheTime
=
1626 // change text to Upper/Lower/Hiragana/Katagana/...
1627 void SwTxtNode::TransliterateText( utl::TransliterationWrapper
& rTrans
,
1628 xub_StrLen nStt
, xub_StrLen nEnd
, SwUndoTransliterate
* pUndo
)
1632 SwLanguageIterator
* pIter
;
1633 if( rTrans
.needLanguageForTheMode() )
1634 pIter
= new SwLanguageIterator( *this, nStt
);
1643 nLang
= pIter
->GetLanguage();
1644 nEndPos
= pIter
->GetChgPos();
1645 if( nEndPos
> nEnd
)
1650 nLang
= LANGUAGE_SYSTEM
;
1653 xub_StrLen nLen
= nEndPos
- nStt
;
1655 Sequence
<sal_Int32
> aOffsets
;
1656 String
sChgd( rTrans
.transliterate( m_Text
, nLang
, nStt
, nLen
,
1658 if( !m_Text
.Equals( sChgd
, nStt
, nLen
) )
1662 pUndo
->AddChanges( *this, nStt
, nLen
, aOffsets
);
1664 ReplaceTextOnly( nStt
, nLen
, sChgd
, aOffsets
);
1667 } while( nEndPos
< nEnd
&& pIter
&& pIter
->Next() );
1673 void SwTxtNode::ReplaceTextOnly( xub_StrLen nPos
, xub_StrLen nLen
,
1674 const XubString
& rText
,
1675 const Sequence
<sal_Int32
>& rOffsets
)
1677 m_Text
.Replace( nPos
, nLen
, rText
);
1679 xub_StrLen nTLen
= rText
.Len();
1680 const sal_Int32
* pOffsets
= rOffsets
.getConstArray();
1681 // now look for no 1-1 mapping -> move the indizies!
1682 xub_StrLen nI
, nMyOff
;
1683 for( nI
= 0, nMyOff
= nPos
; nI
< nTLen
; ++nI
, ++nMyOff
)
1685 xub_StrLen nOff
= (xub_StrLen
)pOffsets
[ nI
];
1688 // something is inserted
1689 xub_StrLen nCnt
= 1;
1690 while( nI
+ nCnt
< nTLen
&& nOff
== pOffsets
[ nI
+ nCnt
] )
1693 Update( SwIndex( this, nMyOff
), nCnt
, FALSE
);
1698 else if( nOff
> nMyOff
)
1700 // something is deleted
1701 Update( SwIndex( this, nMyOff
+1 ), nOff
- nMyOff
, TRUE
);
1706 // something is deleted at the end
1707 Update( SwIndex( this, nMyOff
), nLen
- nMyOff
, TRUE
);
1709 // notify the layout!
1710 SwDelTxt
aDelHint( nPos
, nTLen
);
1711 SwModify::Modify( 0, &aDelHint
);
1713 SwInsTxt
aHint( nPos
, nTLen
);
1714 SwModify::Modify( 0, &aHint
);
1717 void SwTxtNode::CountWords( SwDocStat
& rStat
,
1718 xub_StrLen nStt
, xub_StrLen nEnd
) const
1725 ULONG nTmpWords
= 0;
1726 ULONG nTmpChars
= 0;
1728 // Shortcut: Whole paragraph should be considered and cached values
1730 if ( 0 == nStt
&& GetTxt().Len() == nEnd
&& !IsWordCountDirty() )
1732 nTmpWords
= GetParaNumberOfWords();
1733 nTmpChars
= GetParaNumberOfChars();
1737 String
aOldStr( m_Text
);
1738 String
& rCastStr
= const_cast<String
&>(m_Text
);
1740 // fills the deleted redlines and hidden ranges with cChar:
1741 const xub_Unicode
cChar(' ');
1742 const USHORT nNumOfMaskedChars
=
1743 lcl_MaskRedlinesAndHiddenText( *this, rCastStr
, nStt
, nEnd
, cChar
, false );
1746 rtl::OUString aExpandText
;
1747 const ModelToViewHelper::ConversionMap
* pConversionMap
=
1748 BuildConversionMap( aExpandText
);
1750 const sal_uInt32 nExpandBegin
= ModelToViewHelper::ConvertToViewPosition( pConversionMap
, nStt
);
1751 const sal_uInt32 nExpandEnd
= ModelToViewHelper::ConvertToViewPosition( pConversionMap
, nEnd
);
1753 const bool bCount
= aExpandText
.getLength() > 0;
1755 // count words in 'regular' text:
1756 if( bCount
&& pBreakIt
->GetBreakIter().is() )
1758 const String
aScannerText( aExpandText
);
1759 SwScanner
aScanner( *this, aScannerText
, 0, pConversionMap
,
1760 i18n::WordType::WORD_COUNT
,
1761 (xub_StrLen
)nExpandBegin
, (xub_StrLen
)nExpandEnd
);
1763 const rtl::OUString
aBreakWord( CH_TXTATR_BREAKWORD
);
1765 while ( aScanner
.NextWord() )
1767 if ( aScanner
.GetLen() > 1 ||
1768 CH_TXTATR_BREAKWORD
!= aExpandText
.match(aBreakWord
, aScanner
.GetBegin() ) )
1773 ASSERT( aExpandText
.getLength() >= nNumOfMaskedChars
,
1774 "More characters hidden that characters in string!" )
1775 nTmpChars
= nExpandEnd
- nExpandBegin
- nNumOfMaskedChars
;
1777 // count words in numbering string:
1778 if ( nStt
== 0 && bCount
)
1780 // add numbering label
1781 const String aNumString
= GetNumString();
1782 const xub_StrLen nNumStringLen
= aNumString
.Len();
1783 if ( nNumStringLen
> 0 )
1785 LanguageType aLanguage
= GetLang( 0 );
1787 SwScanner
aScanner( *this, aNumString
, &aLanguage
, 0,
1788 i18n::WordType::WORD_COUNT
,
1791 while ( aScanner
.NextWord() )
1794 nTmpChars
+= nNumStringLen
;
1796 else if ( HasBullet() )
1803 delete pConversionMap
;
1807 // If the whole paragraph has been calculated, update cached
1809 if ( 0 == nStt
&& GetTxt().Len() == nEnd
)
1811 SetParaNumberOfWords( nTmpWords
);
1812 SetParaNumberOfChars( nTmpChars
);
1813 SetWordCountDirty( false );
1817 rStat
.nWord
+= nTmpWords
;
1818 rStat
.nChar
+= nTmpChars
;
1824 // Paragraph statistics start
1826 struct SwParaIdleData_Impl
1828 SwWrongList
* pWrong
; // for spell checking
1829 SwGrammarMarkUp
* pGrammarCheck
; // for grammar checking / proof reading
1830 SwWrongList
* pSmartTags
;
1831 ULONG nNumberOfWords
;
1832 ULONG nNumberOfChars
;
1833 bool bWordCountDirty
: 1;
1834 bool bWrongDirty
: 1; // Ist das Wrong-Feld auf invalid?
1835 bool bGrammarCheckDirty
: 1;
1836 bool bSmartTagDirty
: 1;
1837 bool bAutoComplDirty
: 1; // die ACompl-Liste muss angepasst werden
1839 SwParaIdleData_Impl() :
1841 pGrammarCheck ( 0 ),
1843 nNumberOfWords ( 0 ),
1844 nNumberOfChars ( 0 ),
1845 bWordCountDirty ( true ),
1846 bWrongDirty ( true ),
1847 bGrammarCheckDirty ( true ),
1848 bSmartTagDirty ( true ),
1849 bAutoComplDirty ( true ) {};
1852 void SwTxtNode::InitSwParaStatistics( bool bNew
)
1856 m_pParaIdleData_Impl
= new SwParaIdleData_Impl
;
1858 else if ( m_pParaIdleData_Impl
)
1860 delete m_pParaIdleData_Impl
->pWrong
;
1861 delete m_pParaIdleData_Impl
->pGrammarCheck
;
1862 delete m_pParaIdleData_Impl
->pSmartTags
;
1863 delete m_pParaIdleData_Impl
;
1864 m_pParaIdleData_Impl
= 0;
1868 void SwTxtNode::SetWrong( SwWrongList
* pNew
, bool bDelete
)
1870 if ( m_pParaIdleData_Impl
)
1874 delete m_pParaIdleData_Impl
->pWrong
;
1876 m_pParaIdleData_Impl
->pWrong
= pNew
;
1880 SwWrongList
* SwTxtNode::GetWrong()
1882 return m_pParaIdleData_Impl
? m_pParaIdleData_Impl
->pWrong
: 0;
1885 // --> OD 2008-05-27 #i71360#
1886 const SwWrongList
* SwTxtNode::GetWrong() const
1888 return m_pParaIdleData_Impl
? m_pParaIdleData_Impl
->pWrong
: 0;
1893 void SwTxtNode::SetGrammarCheck( SwGrammarMarkUp
* pNew
, bool bDelete
)
1895 if ( m_pParaIdleData_Impl
)
1899 delete m_pParaIdleData_Impl
->pGrammarCheck
;
1901 m_pParaIdleData_Impl
->pGrammarCheck
= pNew
;
1905 SwGrammarMarkUp
* SwTxtNode::GetGrammarCheck()
1907 return m_pParaIdleData_Impl
? m_pParaIdleData_Impl
->pGrammarCheck
: 0;
1910 void SwTxtNode::SetSmartTags( SwWrongList
* pNew
, bool bDelete
)
1912 ASSERT( !pNew
|| SwSmartTagMgr::Get().IsSmartTagsEnabled(),
1913 "Weird - we have a smart tag list without any recognizers?" )
1915 if ( m_pParaIdleData_Impl
)
1919 delete m_pParaIdleData_Impl
->pSmartTags
;
1921 m_pParaIdleData_Impl
->pSmartTags
= pNew
;
1925 SwWrongList
* SwTxtNode::GetSmartTags()
1927 return m_pParaIdleData_Impl
? m_pParaIdleData_Impl
->pSmartTags
: 0;
1930 void SwTxtNode::SetParaNumberOfWords( ULONG nNew
) const
1932 if ( m_pParaIdleData_Impl
)
1934 m_pParaIdleData_Impl
->nNumberOfWords
= nNew
;
1937 ULONG
SwTxtNode::GetParaNumberOfWords() const
1939 return m_pParaIdleData_Impl
? m_pParaIdleData_Impl
->nNumberOfWords
: 0;
1941 void SwTxtNode::SetParaNumberOfChars( ULONG nNew
) const
1943 if ( m_pParaIdleData_Impl
)
1945 m_pParaIdleData_Impl
->nNumberOfChars
= nNew
;
1948 ULONG
SwTxtNode::GetParaNumberOfChars() const
1950 return m_pParaIdleData_Impl
? m_pParaIdleData_Impl
->nNumberOfChars
: 0;
1952 void SwTxtNode::SetWordCountDirty( bool bNew
) const
1954 if ( m_pParaIdleData_Impl
)
1956 m_pParaIdleData_Impl
->bWordCountDirty
= bNew
;
1959 bool SwTxtNode::IsWordCountDirty() const
1961 return m_pParaIdleData_Impl
? m_pParaIdleData_Impl
->bWordCountDirty
: 0;
1963 void SwTxtNode::SetWrongDirty( bool bNew
) const
1965 if ( m_pParaIdleData_Impl
)
1967 m_pParaIdleData_Impl
->bWrongDirty
= bNew
;
1970 bool SwTxtNode::IsWrongDirty() const
1972 return m_pParaIdleData_Impl
? m_pParaIdleData_Impl
->bWrongDirty
: 0;
1974 void SwTxtNode::SetGrammarCheckDirty( bool bNew
) const
1976 if ( m_pParaIdleData_Impl
)
1978 m_pParaIdleData_Impl
->bGrammarCheckDirty
= bNew
;
1981 bool SwTxtNode::IsGrammarCheckDirty() const
1983 return m_pParaIdleData_Impl
? m_pParaIdleData_Impl
->bGrammarCheckDirty
: 0;
1985 void SwTxtNode::SetSmartTagDirty( bool bNew
) const
1987 if ( m_pParaIdleData_Impl
)
1989 m_pParaIdleData_Impl
->bSmartTagDirty
= bNew
;
1992 bool SwTxtNode::IsSmartTagDirty() const
1994 return m_pParaIdleData_Impl
? m_pParaIdleData_Impl
->bSmartTagDirty
: 0;
1996 void SwTxtNode::SetAutoCompleteWordDirty( bool bNew
) const
1998 if ( m_pParaIdleData_Impl
)
2000 m_pParaIdleData_Impl
->bAutoComplDirty
= bNew
;
2003 bool SwTxtNode::IsAutoCompleteWordDirty() const
2005 return m_pParaIdleData_Impl
? m_pParaIdleData_Impl
->bAutoComplDirty
: 0;
2008 // Paragraph statistics end