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