xref: /aoo42x/main/sw/source/core/txtnode/txtedt.cxx (revision 86e1cf34)
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             ++i;
644         }
645     }
646 
647     TryDeleteSwpHints();
648     if (bChanged)
649     {
650         if ( HasHints() )
651         {
652             m_pSwpHints->Resort();
653         }
654         //TxtFrm's reagieren auf aHint, andere auf aNew
655         SwUpdateAttr aHint( nMin, nMax, 0 );
656         NotifyClients( 0, &aHint );
657         SwFmtChg aNew( GetFmtColl() );
658         NotifyClients( 0, &aNew );
659     }
660 }
661 
662 
663 
664 /*************************************************************************
665  *				  SwTxtNode::GetCurWord()
666  *
667  * Aktuelles Wort zurueckliefern:
668  * Wir suchen immer von links nach rechts, es wird also das Wort
669  * vor nPos gesucht. Es sei denn, wir befinden uns am Anfang des
670  * Absatzes, dann wird das erste Wort zurueckgeliefert.
671  * Wenn dieses erste Wort nur aus Whitespaces besteht, returnen wir
672  * einen leeren String.
673  *************************************************************************/
674 
675 XubString SwTxtNode::GetCurWord( xub_StrLen nPos ) const
676 {
677     ASSERT( nPos <= m_Text.Len(), "SwTxtNode::GetCurWord: invalid index." );
678 
679     if (!m_Text.Len())
680         return m_Text;
681 
682 	Boundary aBndry;
683 	const uno::Reference< XBreakIterator > &rxBreak = pBreakIt->GetBreakIter();
684     if (rxBreak.is())
685     {
686         sal_Int16 nWordType = WordType::DICTIONARY_WORD;
687         lang::Locale aLocale( pBreakIt->GetLocale( GetLang( nPos ) ) );
688 #ifdef DEBUG
689         sal_Bool bBegin = rxBreak->isBeginWord( m_Text, nPos, aLocale, nWordType );
690         sal_Bool bEnd   = rxBreak->isEndWord  ( m_Text, nPos, aLocale, nWordType );
691         (void)bBegin;
692         (void)bEnd;
693 #endif
694         aBndry =
695             rxBreak->getWordBoundary( m_Text, nPos, aLocale, nWordType, sal_True );
696 
697         // if no word was found use previous word (if any)
698         if (aBndry.startPos == aBndry.endPos)
699         {
700             aBndry = rxBreak->previousWord( m_Text, nPos, aLocale, nWordType );
701         }
702     }
703 
704     // check if word was found and if it uses a symbol font, if so
705     // enforce returning an empty string
706     if (aBndry.endPos != aBndry.startPos && IsSymbol( (xub_StrLen)aBndry.startPos ))
707 		aBndry.endPos = aBndry.startPos;
708 
709     return m_Text.Copy( static_cast<xub_StrLen>(aBndry.startPos),
710                        static_cast<xub_StrLen>(aBndry.endPos - aBndry.startPos) );
711 }
712 
713 SwScanner::SwScanner( const SwTxtNode& rNd, const String& rTxt, const LanguageType* pLang,
714                       const ModelToViewHelper::ConversionMap* pConvMap,
715                       sal_uInt16 nType, xub_StrLen nStart, xub_StrLen nEnde, sal_Bool bClp )
716     : rNode( rNd ), rText( rTxt), pLanguage( pLang ), pConversionMap( pConvMap ), nLen( 0 ), nWordType( nType ), bClip( bClp )
717 {
718     ASSERT( rText.Len(), "SwScanner: EmptyString" );
719     nStartPos = nBegin = nStart;
720     nEndPos = nEnde;
721 
722     if ( pLanguage )
723     {
724         aCurrLang = *pLanguage;
725     }
726     else
727     {
728         ModelToViewHelper::ModelPosition aModelBeginPos = ModelToViewHelper::ConvertToModelPosition( pConversionMap, nBegin );
729         const xub_StrLen nModelBeginPos = (xub_StrLen)aModelBeginPos.mnPos;
730         aCurrLang = rNd.GetLang( nModelBeginPos );
731     }
732 }
733 
734 sal_Bool SwScanner::NextWord()
735 {
736     nBegin = nBegin + nLen;
737     Boundary aBound;
738 
739     CharClass& rCC = GetAppCharClass();
740     lang::Locale aOldLocale = rCC.getLocale();
741 
742 	while ( true )
743 	{
744         // skip non-letter characters:
745         while ( nBegin < rText.Len() )
746         {
747             if ( !lcl_IsSkippableWhiteSpace( rText.GetChar( nBegin ) ) )
748             {
749                 if ( !pLanguage )
750                 {
751                     const sal_uInt16 nNextScriptType = pBreakIt->GetBreakIter()->getScriptType( rText, nBegin );
752                     ModelToViewHelper::ModelPosition aModelBeginPos = ModelToViewHelper::ConvertToModelPosition( pConversionMap, nBegin );
753                     const xub_StrLen nBeginModelPos = (xub_StrLen)aModelBeginPos.mnPos;
754                     aCurrLang = rNode.GetLang( nBeginModelPos, 1, nNextScriptType );
755                 }
756 
757                 if ( nWordType != i18n::WordType::WORD_COUNT )
758                 {
759                     rCC.setLocale( pBreakIt->GetLocale( aCurrLang ) );
760                     if ( rCC.isLetterNumeric( rText.GetChar( nBegin ) ) )
761                         break;
762                 }
763                 else
764                     break;
765             }
766             ++nBegin;
767         }
768 
769 		if ( nBegin >= rText.Len() || nBegin >= nEndPos )
770 			return sal_False;
771 
772         // get the word boundaries
773         aBound = pBreakIt->GetBreakIter()->getWordBoundary( rText, nBegin,
774 				pBreakIt->GetLocale( aCurrLang ), nWordType, sal_True );
775         ASSERT( aBound.endPos >= aBound.startPos, "broken aBound result" );
776 
777         //no word boundaries could be found
778         if(aBound.endPos == aBound.startPos)
779             return sal_False;
780 
781         //if a word before is found it has to be searched for the next
782 		if(aBound.endPos == nBegin)
783             ++nBegin;
784         else
785 			break;
786     } // end while( true )
787 
788     rCC.setLocale( aOldLocale );
789 
790     // #i89042, as discussed with HDU: don't evaluate script changes for word count. Use whole word.
791     if ( nWordType == i18n::WordType::WORD_COUNT )
792     {
793         nBegin = Max( static_cast< xub_StrLen >(aBound.startPos), nBegin );
794         nLen   = 0;
795         if (static_cast< xub_StrLen >(aBound.endPos) > nBegin)
796             nLen = static_cast< xub_StrLen >(aBound.endPos) - nBegin;
797     }
798     else
799     {
800         // we have to differenciate between these cases:
801         if ( aBound.startPos <= nBegin )
802         {
803             ASSERT( aBound.endPos >= nBegin, "Unexpected aBound result" )
804 
805             // restrict boundaries to script boundaries and nEndPos
806             const sal_uInt16 nCurrScript = pBreakIt->GetBreakIter()->getScriptType( rText, nBegin );
807             XubString aTmpWord = rText.Copy( nBegin, static_cast<xub_StrLen>(aBound.endPos - nBegin) );
808             const sal_Int32 nScriptEnd = nBegin +
809                 pBreakIt->GetBreakIter()->endOfScript( aTmpWord, 0, nCurrScript );
810             const sal_Int32 nEnd = Min( aBound.endPos, nScriptEnd );
811 
812             // restrict word start to last script change position
813             sal_Int32 nScriptBegin = 0;
814             if ( aBound.startPos < nBegin )
815             {
816                 // search from nBegin backwards until the next script change
817                 aTmpWord = rText.Copy( static_cast<xub_StrLen>(aBound.startPos),
818                                        static_cast<xub_StrLen>(nBegin - aBound.startPos + 1) );
819                 nScriptBegin = aBound.startPos +
820                     pBreakIt->GetBreakIter()->beginOfScript( aTmpWord, nBegin - aBound.startPos,
821                                                     nCurrScript );
822             }
823 
824             nBegin = (xub_StrLen)Max( aBound.startPos, nScriptBegin );
825 	        nLen = (xub_StrLen)(nEnd - nBegin);
826         }
827         else
828         {
829             const sal_uInt16 nCurrScript = pBreakIt->GetBreakIter()->getScriptType( rText, aBound.startPos );
830             XubString aTmpWord = rText.Copy( static_cast<xub_StrLen>(aBound.startPos),
831                                              static_cast<xub_StrLen>(aBound.endPos - aBound.startPos) );
832             const sal_Int32 nScriptEnd = aBound.startPos +
833                 pBreakIt->GetBreakIter()->endOfScript( aTmpWord, 0, nCurrScript );
834             const sal_Int32 nEnd = Min( aBound.endPos, nScriptEnd );
835             nBegin = (xub_StrLen)aBound.startPos;
836 	        nLen = (xub_StrLen)(nEnd - nBegin);
837         }
838     }
839 
840 	// optionally clip the result of getWordBoundaries:
841 	if ( bClip )
842 	{
843 		aBound.startPos = Max( (xub_StrLen)aBound.startPos, nStartPos );
844 		aBound.endPos = Min( (xub_StrLen)aBound.endPos, nEndPos );
845         nBegin = (xub_StrLen)aBound.startPos;
846 	    nLen = (xub_StrLen)(aBound.endPos - nBegin);
847 	}
848 
849     if( ! nLen )
850         return sal_False;
851 
852     aWord = rText.Copy( nBegin, nLen );
853 
854     return sal_True;
855 }
856 
857 
858 sal_uInt16 SwTxtNode::Spell(SwSpellArgs* pArgs)
859 {
860 	// Die Aehnlichkeiten zu SwTxtFrm::_AutoSpell sind beabsichtigt ...
861 	// ACHTUNG: Ev. Bugs in beiden Routinen fixen!
862 
863 	uno::Reference<beans::XPropertySet> xProp( GetLinguPropertySet() );
864 
865 	xub_StrLen nBegin, nEnd;
866 
867     // modify string according to redline information and hidden text
868     const XubString aOldTxt( m_Text );
869     const bool bRestoreString =
870         lcl_MaskRedlinesAndHiddenText( *this, m_Text, 0, m_Text.Len() ) > 0;
871 
872     if ( pArgs->pStartNode != this )
873 		nBegin = 0;
874 	else
875         nBegin = pArgs->pStartIdx->GetIndex();
876 
877     nEnd = ( pArgs->pEndNode != this )
878             ? m_Text.Len()
879             : pArgs->pEndIdx->GetIndex();
880 
881 	pArgs->xSpellAlt = NULL;
882 
883     // 4 cases:
884     //
885     // 1. IsWrongDirty = 0 and GetWrong = 0
886     //      Everything is checked and correct
887     // 2. IsWrongDirty = 0 and GetWrong = 1
888     //      Everything is checked and errors are identified in the wrong list
889     // 3. IsWrongDirty = 1 and GetWrong = 0
890     //      Nothing has been checked
891     // 4. IsWrongDirty = 1 and GetWrong = 1
892     //      Text has been checked but there is an invalid range in the wrong list
893     //
894     // Nothing has to be done for case 1.
895     if ( ( IsWrongDirty() || GetWrong() ) && m_Text.Len() )
896     {
897         if ( nBegin > m_Text.Len() )
898         {
899             nBegin = m_Text.Len();
900         }
901         if ( nEnd > m_Text.Len() )
902         {
903             nEnd = m_Text.Len();
904         }
905         //
906         if(!IsWrongDirty())
907         {
908             xub_StrLen nTemp = GetWrong()->NextWrong( nBegin );
909             if(nTemp > nEnd)
910             {
911                 // reset original text
912                 if ( bRestoreString )
913                 {
914                     m_Text = aOldTxt;
915                 }
916                 return 0;
917             }
918             if(nTemp > nBegin)
919                 nBegin = nTemp;
920 
921         }
922 
923         // In case 2. we pass the wrong list to the scanned, because only
924         // the words in the wrong list have to be checked
925         SwScanner aScanner( *this, m_Text, 0, 0,
926                             WordType::DICTIONARY_WORD,
927                             nBegin, nEnd );
928         while( !pArgs->xSpellAlt.is() && aScanner.NextWord() )
929 		{
930 			const XubString& rWord = aScanner.GetWord();
931 
932             // get next language for next word, consider language attributes
933             // within the word
934             LanguageType eActLang = aScanner.GetCurrentLanguage();
935 
936             if( rWord.Len() > 0 && LANGUAGE_NONE != eActLang )
937 			{
938 				if (pArgs->xSpeller.is())
939 				{
940 					SvxSpellWrapper::CheckSpellLang( pArgs->xSpeller, eActLang );
941 					pArgs->xSpellAlt = pArgs->xSpeller->spell( rWord, eActLang,
942 											Sequence< PropertyValue >() );
943 				}
944 				if( (pArgs->xSpellAlt).is() )
945 				{
946 					if( IsSymbol( aScanner.GetBegin() ) )
947 					{
948 						pArgs->xSpellAlt = NULL;
949 					}
950 					else
951 					{
952 						// make sure the selection build later from the
953 						// data below does not include footnotes and other
954 						// "in word" character to the left and right in order
955 						// to preserve those. Therefore count those "in words"
956 						// in order to modify the selection accordingly.
957 						const sal_Unicode* pChar = rWord.GetBuffer();
958 						xub_StrLen nLeft = 0;
959 						while (pChar && *pChar++ == CH_TXTATR_INWORD)
960 							++nLeft;
961 						pChar = rWord.Len() ? rWord.GetBuffer() + rWord.Len() - 1 : 0;
962 						xub_StrLen nRight = 0;
963 						while (pChar && *pChar-- == CH_TXTATR_INWORD)
964 							++nRight;
965 
966 						pArgs->pStartNode = this;
967 						pArgs->pEndNode = this;
968                         pArgs->pStartIdx->Assign(this, aScanner.GetEnd() - nRight );
969                         pArgs->pEndIdx->Assign(this, aScanner.GetBegin() + nLeft );
970 					}
971 				}
972 			}
973         }
974 	}
975 
976     // reset original text
977     if ( bRestoreString )
978     {
979         m_Text = aOldTxt;
980     }
981 
982     return pArgs->xSpellAlt.is() ? 1 : 0;
983 }
984 
985 
986 void SwTxtNode::SetLanguageAndFont( const SwPaM &rPaM,
987     LanguageType nLang, sal_uInt16 nLangWhichId,
988     const Font *pFont,  sal_uInt16 nFontWhichId )
989 {
990     sal_uInt16 aRanges[] = {
991             nLangWhichId, nLangWhichId,
992             nFontWhichId, nFontWhichId,
993             0, 0, 0 };
994     if (!pFont)
995         aRanges[2] = aRanges[3] = 0;    // clear entries with font WhichId
996 
997     SwEditShell *pEditShell = GetDoc()->GetEditShell();
998     SfxItemSet aSet( pEditShell->GetAttrPool(), aRanges );
999     aSet.Put( SvxLanguageItem( nLang, nLangWhichId ) );
1000 
1001     DBG_ASSERT( pFont, "target font missing?" );
1002     if (pFont)
1003     {
1004         SvxFontItem aFontItem = (SvxFontItem&) aSet.Get( nFontWhichId );
1005         aFontItem.SetFamilyName(   pFont->GetName());
1006         aFontItem.SetFamily(       pFont->GetFamily());
1007         aFontItem.SetStyleName(    pFont->GetStyleName());
1008         aFontItem.SetPitch(        pFont->GetPitch());
1009         aFontItem.SetCharSet( pFont->GetCharSet() );
1010         aSet.Put( aFontItem );
1011     }
1012 
1013     GetDoc()->InsertItemSet( rPaM, aSet, 0 );
1014     // SetAttr( aSet );    <- Does not set language attribute of empty paragraphs correctly,
1015     //                     <- because since there is no selection the flag to garbage
1016     //                     <- collect all attributes is set, and therefore attributes spanned
1017     //                     <- over empty selection are removed.
1018 
1019 }
1020 
1021 
1022 sal_uInt16 SwTxtNode::Convert( SwConversionArgs &rArgs )
1023 {
1024     // get range of text within node to be converted
1025     // (either all the text or the the text within the selection
1026     // when the conversion was started)
1027     xub_StrLen nTextBegin, nTextEnd;
1028     //
1029     if ( rArgs.pStartNode != this )
1030 	{
1031         nTextBegin = 0;
1032 	}
1033     else
1034         nTextBegin = rArgs.pStartIdx->GetIndex();
1035     if (nTextBegin > m_Text.Len())
1036     {
1037         nTextBegin = m_Text.Len();
1038     }
1039 
1040     nTextEnd = ( rArgs.pEndNode != this )
1041         ?  m_Text.Len()
1042         :  ::std::min( rArgs.pEndIdx->GetIndex(), m_Text.Len() );
1043 
1044     rArgs.aConvText = rtl::OUString();
1045 
1046     // modify string according to redline information and hidden text
1047     const XubString aOldTxt( m_Text );
1048     const bool bRestoreString =
1049         lcl_MaskRedlinesAndHiddenText( *this, m_Text, 0, m_Text.Len() ) > 0;
1050 
1051     sal_Bool    bFound  = sal_False;
1052     xub_StrLen  nBegin  = nTextBegin;
1053     xub_StrLen  nLen = 0;
1054 	LanguageType nLangFound = LANGUAGE_NONE;
1055     if (!m_Text.Len())
1056     {
1057         if (rArgs.bAllowImplicitChangesForNotConvertibleText)
1058         {
1059             // create SwPaM with mark & point spanning empty paragraph
1060             //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different
1061             SwPaM aCurPaM( *this, 0 );
1062 
1063             SetLanguageAndFont( aCurPaM,
1064                     rArgs.nConvTargetLang, RES_CHRATR_CJK_LANGUAGE,
1065                     rArgs.pTargetFont, RES_CHRATR_CJK_FONT );
1066         }
1067     }
1068     else
1069     {
1070         SwLanguageIterator aIter( *this, nBegin );
1071 
1072         // find non zero length text portion of appropriate language
1073         do {
1074 			nLangFound = aIter.GetLanguage();
1075             sal_Bool bLangOk =  (nLangFound == rArgs.nConvSrcLang) ||
1076                                 (editeng::HangulHanjaConversion::IsChinese( nLangFound ) &&
1077                                  editeng::HangulHanjaConversion::IsChinese( rArgs.nConvSrcLang ));
1078 
1079 			xub_StrLen nChPos = aIter.GetChgPos();
1080 			// the position at the end of the paragraph returns -1
1081 			// which becomes 65535 when converted to xub_StrLen,
1082 			// and thus must be cut to the end of the actual string.
1083 			if (nChPos == (xub_StrLen) -1)
1084             {
1085                 nChPos = m_Text.Len();
1086             }
1087 
1088 			nLen = nChPos - nBegin;
1089 			bFound = bLangOk && nLen > 0;
1090 			if (!bFound)
1091             {
1092                 // create SwPaM with mark & point spanning the attributed text
1093                 //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different
1094                 SwPaM aCurPaM( *this, nBegin );
1095                 aCurPaM.SetMark();
1096                 aCurPaM.GetPoint()->nContent = nBegin + nLen;
1097 
1098                 // check script type of selected text
1099                 SwEditShell *pEditShell = GetDoc()->GetEditShell();
1100                 pEditShell->Push();             // save current cursor on stack
1101                 pEditShell->SetSelection( aCurPaM );
1102                 sal_Bool bIsAsianScript = (SCRIPTTYPE_ASIAN == pEditShell->GetScriptType());
1103                 pEditShell->Pop( sal_False );   // restore cursor from stack
1104 
1105                 if (!bIsAsianScript && rArgs.bAllowImplicitChangesForNotConvertibleText)
1106                 {
1107                     SetLanguageAndFont( aCurPaM,
1108                             rArgs.nConvTargetLang, RES_CHRATR_CJK_LANGUAGE,
1109                             rArgs.pTargetFont, RES_CHRATR_CJK_FONT );
1110                 }
1111 				nBegin = nChPos;    // start of next language portion
1112             }
1113         } while (!bFound && aIter.Next());	/* loop while nothing was found and still sth is left to be searched */
1114     }
1115 
1116     // keep resulting text within selection / range of text to be converted
1117     if (nBegin < nTextBegin)
1118         nBegin = nTextBegin;
1119     if (nBegin + nLen > nTextEnd)
1120         nLen = nTextEnd - nBegin;
1121     sal_Bool bInSelection = nBegin < nTextEnd;
1122 
1123     if (bFound && bInSelection)     // convertible text found within selection/range?
1124     {
1125         const XubString aTxtPortion = m_Text.Copy( nBegin, nLen );
1126         DBG_ASSERT( m_Text.Len() > 0, "convertible text portion missing!" );
1127         rArgs.aConvText     = m_Text.Copy( nBegin, nLen );
1128 		rArgs.nConvTextLang = nLangFound;
1129 
1130         // position where to start looking in next iteration (after current ends)
1131 		rArgs.pStartNode = this;
1132         rArgs.pStartIdx->Assign(this, nBegin + nLen );
1133         // end position (when we have travelled over the whole document)
1134         rArgs.pEndNode = this;
1135         rArgs.pEndIdx->Assign(this, nBegin );
1136 	}
1137 
1138     // restore original text
1139     if ( bRestoreString )
1140     {
1141         m_Text = aOldTxt;
1142     }
1143 
1144     return rArgs.aConvText.getLength() ? 1 : 0;
1145 }
1146 
1147 // Die Aehnlichkeiten zu SwTxtNode::Spell sind beabsichtigt ...
1148 // ACHTUNG: Ev. Bugs in beiden Routinen fixen!
1149 SwRect SwTxtFrm::_AutoSpell( const SwCntntNode* pActNode, const SwViewOption& rViewOpt, xub_StrLen nActPos )
1150 {
1151     SwRect aRect;
1152 #if OSL_DEBUG_LEVEL > 1
1153     static sal_Bool bStop = sal_False;
1154     if ( bStop )
1155         return aRect;
1156 #endif
1157     // Die Aehnlichkeiten zu SwTxtNode::Spell sind beabsichtigt ...
1158     // ACHTUNG: Ev. Bugs in beiden Routinen fixen!
1159     SwTxtNode *pNode = GetTxtNode();
1160     if( pNode != pActNode || !nActPos )
1161         nActPos = STRING_LEN;
1162 
1163     SwAutoCompleteWord& rACW = SwDoc::GetAutoCompleteWords();
1164 
1165     // modify string according to redline information and hidden text
1166     const XubString aOldTxt( pNode->GetTxt() );
1167     const bool bRestoreString =
1168             lcl_MaskRedlinesAndHiddenText( *pNode, pNode->m_Text,
1169                 0, pNode->GetTxt().Len() )     > 0;
1170 
1171     // a change of data indicates that at least one word has been modified
1172     const sal_Bool bRedlineChg =
1173         ( pNode->GetTxt().GetBuffer() != aOldTxt.GetBuffer() );
1174 
1175     xub_StrLen nBegin = 0;
1176     xub_StrLen nEnd = pNode->GetTxt().Len();
1177     xub_StrLen nInsertPos = 0;
1178     xub_StrLen nChgStart = STRING_LEN;
1179     xub_StrLen nChgEnd = 0;
1180     xub_StrLen nInvStart = STRING_LEN;
1181     xub_StrLen nInvEnd = 0;
1182 
1183     const sal_Bool bAddAutoCmpl = pNode->IsAutoCompleteWordDirty() &&
1184                                   rViewOpt.IsAutoCompleteWords();
1185 
1186     if( pNode->GetWrong() )
1187     {
1188         nBegin = pNode->GetWrong()->GetBeginInv();
1189         if( STRING_LEN != nBegin )
1190         {
1191             nEnd = pNode->GetWrong()->GetEndInv();
1192             if ( nEnd > pNode->GetTxt().Len() )
1193             {
1194                 nEnd = pNode->GetTxt().Len();
1195             }
1196         }
1197 
1198         // get word around nBegin, we start at nBegin - 1
1199         if ( STRING_LEN != nBegin )
1200         {
1201             if ( nBegin )
1202                 --nBegin;
1203 
1204             LanguageType eActLang = pNode->GetLang( nBegin );
1205             Boundary aBound =
1206                 pBreakIt->GetBreakIter()->getWordBoundary( pNode->GetTxt(), nBegin,
1207                     pBreakIt->GetLocale( eActLang ),
1208                     WordType::DICTIONARY_WORD, sal_True );
1209             nBegin = xub_StrLen(aBound.startPos);
1210         }
1211 
1212         // get the position in the wrong list
1213         nInsertPos = pNode->GetWrong()->GetWrongPos( nBegin );
1214 
1215         // sometimes we have to skip one entry
1216         if( nInsertPos < pNode->GetWrong()->Count() &&
1217             nBegin == pNode->GetWrong()->Pos( nInsertPos ) +
1218                       pNode->GetWrong()->Len( nInsertPos ) )
1219                 nInsertPos++;
1220     }
1221 
1222     sal_Bool bFresh = nBegin < nEnd;
1223 
1224     if( nBegin < nEnd )
1225     {
1226         //! register listener to LinguServiceEvents now in order to get
1227         //! notified about relevant changes in the future
1228         SwModule *pModule = SW_MOD();
1229         if (!pModule->GetLngSvcEvtListener().is())
1230             pModule->CreateLngSvcEvtListener();
1231 
1232 		uno::Reference< XSpellChecker1 > xSpell( ::GetSpellChecker() );
1233         SwDoc* pDoc = pNode->GetDoc();
1234 
1235         SwScanner aScanner( *pNode, pNode->GetTxt(), 0, 0,
1236                             WordType::DICTIONARY_WORD, nBegin, nEnd);
1237 
1238         while( aScanner.NextWord() )
1239         {
1240             const XubString& rWord = aScanner.GetWord();
1241             nBegin = aScanner.GetBegin();
1242             xub_StrLen nLen = aScanner.GetLen();
1243 
1244             // get next language for next word, consider language attributes
1245             // within the word
1246             LanguageType eActLang = aScanner.GetCurrentLanguage();
1247 
1248             sal_Bool bSpell = sal_True;
1249             bSpell = xSpell.is() ? xSpell->hasLanguage( eActLang ) : sal_False;
1250             if( bSpell && rWord.Len() > 0 )
1251             {
1252                 // check for: bAlter => xHyphWord.is()
1253                 DBG_ASSERT(!bSpell || xSpell.is(), "NULL pointer");
1254 
1255                 if( !xSpell->isValid( rWord, eActLang, Sequence< PropertyValue >() ) )
1256                 {
1257                     xub_StrLen nSmartTagStt = nBegin;
1258                     xub_StrLen nDummy = 1;
1259                     if ( !pNode->GetSmartTags() || !pNode->GetSmartTags()->InWrongWord( nSmartTagStt, nDummy ) )
1260                     {
1261                         if( !pNode->GetWrong() )
1262                         {
1263                             pNode->SetWrong( new SwWrongList( WRONGLIST_SPELL ) );
1264                             pNode->GetWrong()->SetInvalid( 0, nEnd );
1265                         }
1266                         if( pNode->GetWrong()->Fresh( nChgStart, nChgEnd,
1267                             nBegin, nLen, nInsertPos, nActPos ) )
1268                             pNode->GetWrong()->Insert( rtl::OUString(), 0, nBegin, nLen, nInsertPos++ );
1269                         else
1270                         {
1271                             nInvStart = nBegin;
1272                             nInvEnd = nBegin + nLen;
1273                         }
1274                     }
1275                 }
1276                 else if( bAddAutoCmpl && rACW.GetMinWordLen() <= rWord.Len() )
1277                 {
1278                     if ( bRedlineChg )
1279                     {
1280                         XubString rNewWord( rWord );
1281                         rACW.InsertWord( rNewWord, *pDoc );
1282                     }
1283                     else
1284                         rACW.InsertWord( rWord, *pDoc );
1285                 }
1286             }
1287         }
1288     }
1289 
1290     // reset original text
1291     // i63141 before calling GetCharRect(..) with formatting!
1292     if ( bRestoreString )
1293     {
1294         pNode->m_Text = aOldTxt;
1295     }
1296     if( pNode->GetWrong() )
1297     {
1298         if( bFresh )
1299             pNode->GetWrong()->Fresh( nChgStart, nChgEnd,
1300                                       nEnd, 0, nInsertPos, nActPos );
1301 
1302         //
1303         // Calculate repaint area:
1304         //
1305         if( nChgStart < nChgEnd )
1306         {
1307             aRect = lcl_CalculateRepaintRect( *this, nChgStart, nChgEnd );
1308         }
1309 
1310         pNode->GetWrong()->SetInvalid( nInvStart, nInvEnd );
1311         pNode->SetWrongDirty( STRING_LEN != pNode->GetWrong()->GetBeginInv() );
1312         if( !pNode->GetWrong()->Count() && ! pNode->IsWrongDirty() )
1313             pNode->SetWrong( NULL );
1314     }
1315     else
1316         pNode->SetWrongDirty( false );
1317 
1318     if( bAddAutoCmpl )
1319         pNode->SetAutoCompleteWordDirty( false );
1320 
1321     return aRect;
1322 }
1323 
1324 /** Function: SmartTagScan
1325 
1326     Function scans words in current text and checks them in the
1327     smarttag libraries. If the check returns true to bounds of the
1328     recognized words are stored into a list which is used later for drawing
1329     the underline.
1330 
1331     @param SwCntntNode* pActNode
1332 
1333     @param xub_StrLen nActPos
1334 
1335     @return SwRect: Repaint area
1336 */
1337 SwRect SwTxtFrm::SmartTagScan( SwCntntNode* /*pActNode*/, xub_StrLen /*nActPos*/ )
1338 {
1339     SwRect aRet;
1340     SwTxtNode *pNode = GetTxtNode();
1341     const rtl::OUString& rText = pNode->GetTxt();
1342 
1343     // Iterate over language portions
1344     SmartTagMgr& rSmartTagMgr = SwSmartTagMgr::Get();
1345 
1346     SwWrongList* pSmartTagList = pNode->GetSmartTags();
1347 
1348     xub_StrLen nBegin = 0;
1349     xub_StrLen nEnd = static_cast< xub_StrLen >(rText.getLength());
1350 
1351     if ( pSmartTagList )
1352     {
1353         if ( pSmartTagList->GetBeginInv() != STRING_LEN )
1354         {
1355             nBegin = pSmartTagList->GetBeginInv();
1356             nEnd = Min( pSmartTagList->GetEndInv(), (xub_StrLen)rText.getLength() );
1357 
1358             if ( nBegin < nEnd )
1359             {
1360                 const LanguageType aCurrLang = pNode->GetLang( nBegin );
1361                 const com::sun::star::lang::Locale aCurrLocale = pBreakIt->GetLocale( aCurrLang );
1362                 nBegin = static_cast< xub_StrLen >(pBreakIt->GetBreakIter()->beginOfSentence( rText, nBegin, aCurrLocale ));
1363                 nEnd = static_cast< xub_StrLen >(Min( rText.getLength(), pBreakIt->GetBreakIter()->endOfSentence( rText, nEnd, aCurrLocale ) ));
1364             }
1365         }
1366     }
1367 
1368     const sal_uInt16 nNumberOfEntries = pSmartTagList ? pSmartTagList->Count() : 0;
1369     sal_uInt16 nNumberOfRemovedEntries = 0;
1370     sal_uInt16 nNumberOfInsertedEntries = 0;
1371 
1372     // clear smart tag list between nBegin and nEnd:
1373     if ( 0 != nNumberOfEntries )
1374     {
1375         xub_StrLen nChgStart = STRING_LEN;
1376         xub_StrLen nChgEnd = 0;
1377         const sal_uInt16 nCurrentIndex = pSmartTagList->GetWrongPos( nBegin );
1378         pSmartTagList->Fresh( nChgStart, nChgEnd, nBegin, nEnd - nBegin, nCurrentIndex, STRING_LEN );
1379         nNumberOfRemovedEntries = nNumberOfEntries - pSmartTagList->Count();
1380     }
1381 
1382     if ( nBegin < nEnd )
1383     {
1384         // Expand the string:
1385         rtl::OUString aExpandText;
1386         const ModelToViewHelper::ConversionMap* pConversionMap =
1387                 pNode->BuildConversionMap( aExpandText );
1388 
1389         // Ownership ov ConversionMap is passed to SwXTextMarkup object!
1390         Reference< com::sun::star::text::XTextMarkup > xTextMarkup =
1391              new SwXTextMarkup( *pNode, pConversionMap );
1392 
1393         Reference< ::com::sun::star::frame::XController > xController = pNode->GetDoc()->GetDocShell()->GetController();
1394 
1395 	SwPosition start(*pNode, nBegin);
1396 	SwPosition end  (*pNode, nEnd);
1397         Reference< ::com::sun::star::text::XTextRange > xRange = SwXTextRange::CreateXTextRange(*pNode->GetDoc(), start, &end);
1398 
1399 	rSmartTagMgr.RecognizeTextRange(xRange, xTextMarkup, xController);
1400 
1401 
1402 	xub_StrLen nLangBegin = nBegin;
1403         xub_StrLen nLangEnd = nEnd;
1404 
1405         // smart tag recognization has to be done for each language portion:
1406         SwLanguageIterator aIter( *pNode, nLangBegin );
1407 
1408         do
1409         {
1410             const LanguageType nLang = aIter.GetLanguage();
1411             const com::sun::star::lang::Locale aLocale = pBreakIt->GetLocale( nLang );
1412             nLangEnd = Min( nEnd, aIter.GetChgPos() );
1413 
1414             const sal_uInt32 nExpandBegin = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nLangBegin );
1415             const sal_uInt32 nExpandEnd   = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nLangEnd );
1416 
1417             rSmartTagMgr.RecognizeString(aExpandText, xTextMarkup, xController, aLocale, nExpandBegin, nExpandEnd - nExpandBegin );
1418 
1419             nLangBegin = nLangEnd;
1420         }
1421         while ( aIter.Next() && nLangEnd < nEnd );
1422 
1423         pSmartTagList = pNode->GetSmartTags();
1424 
1425         const sal_uInt16 nNumberOfEntriesAfterRecognize = pSmartTagList ? pSmartTagList->Count() : 0;
1426         nNumberOfInsertedEntries = nNumberOfEntriesAfterRecognize - ( nNumberOfEntries - nNumberOfRemovedEntries );
1427     }
1428 
1429     if( pSmartTagList )
1430 	{
1431         //
1432         // Update WrongList stuff
1433         //
1434         pSmartTagList->SetInvalid( STRING_LEN, 0 );
1435         pNode->SetSmartTagDirty( STRING_LEN != pSmartTagList->GetBeginInv() );
1436 
1437         if( !pSmartTagList->Count() && !pNode->IsSmartTagDirty() )
1438             pNode->SetSmartTags( NULL );
1439 
1440         //
1441         // Calculate repaint area:
1442         //
1443 #if OSL_DEBUG_LEVEL > 1
1444         const sal_uInt16 nNumberOfEntriesAfterRecognize2 = pSmartTagList->Count();
1445 		(void) nNumberOfEntriesAfterRecognize2;
1446 #endif
1447         if ( nBegin < nEnd && ( 0 != nNumberOfRemovedEntries ||
1448                                 0 != nNumberOfInsertedEntries ) )
1449 		{
1450             aRet = lcl_CalculateRepaintRect( *this, nBegin, nEnd );
1451         }
1452     }
1453 	else
1454         pNode->SetSmartTagDirty( false );
1455 
1456     return aRet;
1457 }
1458 
1459 
1460 // Wird vom CollectAutoCmplWords gerufen
1461 void SwTxtFrm::CollectAutoCmplWrds( SwCntntNode* pActNode, xub_StrLen nActPos )
1462 {
1463 	SwTxtNode *pNode = GetTxtNode();
1464 	if( pNode != pActNode || !nActPos )
1465 		nActPos = STRING_LEN;
1466 
1467     SwDoc* pDoc = pNode->GetDoc();
1468     SwAutoCompleteWord& rACW = SwDoc::GetAutoCompleteWords();
1469 
1470 	xub_StrLen nBegin = 0;
1471     xub_StrLen nEnd = pNode->GetTxt().Len();
1472 	xub_StrLen nLen;
1473 	sal_Bool bACWDirty = sal_False, bAnyWrd = sal_False;
1474 
1475 
1476 	if( nBegin < nEnd )
1477 	{
1478         sal_uInt16 nCnt = 200;
1479         SwScanner aScanner( *pNode, pNode->GetTxt(), 0, 0,
1480                             WordType::DICTIONARY_WORD, nBegin, nEnd );
1481         while( aScanner.NextWord() )
1482 		{
1483 			nBegin = aScanner.GetBegin();
1484 			nLen = aScanner.GetLen();
1485 			if( rACW.GetMinWordLen() <= nLen )
1486 			{
1487 				const XubString& rWord = aScanner.GetWord();
1488 
1489 				if( nActPos < nBegin || ( nBegin + nLen ) < nActPos )
1490 				{
1491                     if( rACW.GetMinWordLen() <= rWord.Len() )
1492                         rACW.InsertWord( rWord, *pDoc );
1493                     bAnyWrd = sal_True;
1494 				}
1495 				else
1496 					bACWDirty = sal_True;
1497 			}
1498             if( !--nCnt )
1499             {
1500                 if ( Application::AnyInput( INPUT_ANY ) )
1501                     return;
1502                 nCnt = 100;
1503             }
1504 		}
1505 	}
1506 
1507 	if( bAnyWrd && !bACWDirty )
1508 		pNode->SetAutoCompleteWordDirty( sal_False );
1509 }
1510 
1511 
1512 /*************************************************************************
1513  *						SwTxtNode::Hyphenate
1514  *************************************************************************/
1515 // Findet den TxtFrm und sucht dessen CalcHyph
1516 
1517 sal_Bool SwTxtNode::Hyphenate( SwInterHyphInfo &rHyphInf )
1518 {
1519 	// Abkuerzung: am Absatz ist keine Sprache eingestellt:
1520     if ( LANGUAGE_NONE == sal_uInt16( GetSwAttrSet().GetLanguage().GetLanguage() )
1521          && USHRT_MAX == GetLang( 0, m_Text.Len() ) )
1522     {
1523 		if( !rHyphInf.IsCheck() )
1524 			rHyphInf.SetNoLang( sal_True );
1525 		return sal_False;
1526 	}
1527 
1528 	if( pLinguNode != this )
1529 	{
1530 		pLinguNode = this;
1531 		pLinguFrm = (SwTxtFrm*)getLayoutFrm( GetDoc()->GetCurrentLayout(), (Point*)(rHyphInf.GetCrsrPos()) );
1532 	}
1533 	SwTxtFrm *pFrm = pLinguFrm;
1534 	if( pFrm )
1535         pFrm = &(pFrm->GetFrmAtOfst( rHyphInf.nStart ));
1536 	else
1537 	{
1538 		// 4935: Seit der Trennung ueber Sonderbereiche sind Faelle
1539 		// moeglich, in denen kein Frame zum Node vorliegt.
1540 		// Also kein ASSERT!
1541 #if OSL_DEBUG_LEVEL > 1
1542 		ASSERT( pFrm, "!SwTxtNode::Hyphenate: can't find any frame" );
1543 #endif
1544 		return sal_False;
1545 	}
1546 
1547 	while( pFrm )
1548 	{
1549 		if( pFrm->Hyphenate( rHyphInf ) )
1550 		{
1551 			// Das Layout ist nicht robust gegen "Direktformatierung"
1552 			// (7821, 7662, 7408); vgl. layact.cxx,
1553 			// SwLayAction::_TurboAction(), if( !pCnt->IsValid() ...
1554 			pFrm->SetCompletePaint();
1555 			return sal_True;
1556 		}
1557 		pFrm = (SwTxtFrm*)(pFrm->GetFollow());
1558 		if( pFrm )
1559 		{
1560 			rHyphInf.nLen = rHyphInf.nLen - (pFrm->GetOfst() - rHyphInf.nStart);
1561 			rHyphInf.nStart = pFrm->GetOfst();
1562 		}
1563 	}
1564 	return sal_False;
1565 }
1566 
1567 #ifdef LINGU_STATISTIK
1568 
1569 // globale Variable
1570 SwLinguStatistik aSwLinguStat;
1571 
1572 
1573 void SwLinguStatistik::Flush()
1574 {
1575 	if ( !nWords )
1576 		return ;
1577 
1578 	static char *pLogName = 0;
1579 	const sal_Bool bFirstOpen = pLogName ? sal_False : sal_True;
1580 	if( bFirstOpen )
1581 	{
1582 		char *pPath = getenv( "TEMP" );
1583 		char *pName = "swlingu.stk";
1584 		if( !pPath )
1585 			pLogName = pName;
1586 		else
1587 		{
1588 			const int nLen = strlen(pPath);
1589 			// fuer dieses new wird es kein delete geben.
1590 			pLogName = new char[nLen + strlen(pName) + 3];
1591 			if(nLen && (pPath[nLen-1] == '\\') || (pPath[nLen-1] == '/'))
1592 				snprintf( pLogName, sizeof(pLogName), "%s%s", pPath, pName );
1593 			else
1594 				snprintf( pLogName, sizeof(pLogName), "%s/%s", pPath, pName );
1595 		}
1596 	}
1597 	SvFileStream aStream( String::CreateFromAscii(pLogName), (bFirstOpen
1598 										? STREAM_WRITE | STREAM_TRUNC
1599 										: STREAM_WRITE ));
1600 
1601 	if( !aStream.GetError() )
1602 	{
1603 		if ( bFirstOpen )
1604 			aStream << "\nLinguistik-Statistik\n";
1605 		aStream << endl << ++nFlushCnt << ". Messung\n";
1606 		aStream << "Rechtschreibung\n";
1607 		aStream << "gepruefte Worte: \t" << nWords << endl;
1608 		aStream << "als fehlerhaft erkannt:\t" << nWrong << endl;
1609 		aStream << "Alternativvorschlaege:\t" << nAlter << endl;
1610 		if ( nWrong )
1611 			aStream << "Durchschnitt:\t\t" << nAlter*1.0 / nWrong << endl;
1612 		aStream << "Dauer (msec):\t\t" << nSpellTime << endl;
1613 		aStream << "\nThesaurus\n";
1614 		aStream << "Synonyme gesamt:\t" << nSynonym << endl;
1615 		if ( nSynonym )
1616 			aStream << "Synonym-Durchschnitt:\t" <<
1617 							nSynonym*1.0 / ( nWords - nNoSynonym ) << endl;
1618 		aStream << "ohne Synonyme:\t\t" << nNoSynonym << endl;
1619 		aStream << "Bedeutungen gesamt:\t" << nSynonym << endl;
1620 		aStream << "keine Bedeutungen:\t"<< nNoSynonym << endl;
1621 		aStream << "Dauer (msec):\t\t" << nTheTime << endl;
1622 		aStream << "\nHyphenator\n";
1623 		aStream << "Trennstellen gesamt:\t" << nHyphens << endl;
1624 		if ( nHyphens )
1625 			aStream << "Hyphen-Durchschnitt:\t" <<
1626 					nHyphens*1.0 / ( nWords - nNoHyph - nHyphErr ) << endl;
1627 		aStream << "keine Trennstellen:\t" << nNoHyph << endl;
1628 		aStream << "Trennung verweigert:\t" << nHyphErr << endl;
1629 		aStream << "Dauer (msec):\t\t" << nHyphTime << endl;
1630 		aStream << "---------------------------------------------\n";
1631 	}
1632 	nWords = nWrong = nAlter = nSynonym = nNoSynonym =
1633 	nHyphens = nNoHyph = nHyphErr = nSpellTime = nTheTime =
1634 	nHyphTime = 0;
1635 	//pThes = NULL;
1636 }
1637 
1638 #endif
1639 
1640 namespace sw // #i120045# namespace to avoid XCode template-misoptimization
1641 {
1642 struct TransliterationChgData
1643 {
1644     xub_StrLen              nStart;
1645     xub_StrLen              nLen;
1646     String                  sChanged;
1647     Sequence< sal_Int32 >   aOffsets;
1648 };
1649 }
1650 using sw::TransliterationChgData;
1651 
1652 // change text to Upper/Lower/Hiragana/Katagana/...
1653 void SwTxtNode::TransliterateText(
1654     utl::TransliterationWrapper& rTrans,
1655     xub_StrLen nStt, xub_StrLen nEnd,
1656     SwUndoTransliterate* pUndo )
1657 {
1658     if (nStt < nEnd && pBreakIt->GetBreakIter().is())
1659 	{
1660         // since we don't use Hiragana/Katakana or half-width/full-width transliterations here
1661         // it is fine to use ANYWORD_IGNOREWHITESPACES. (ANY_WORD btw is broken and will
1662         // occasionaly miss words in consecutive sentences). Also with ANYWORD_IGNOREWHITESPACES
1663         // text like 'just-in-time' will be converted to 'Just-In-Time' which seems to be the
1664         // proper thing to do.
1665         const sal_Int16 nWordType = WordType::ANYWORD_IGNOREWHITESPACES;
1666 
1667         //! In order to have less trouble with changing text size, e.g. because
1668         //! of ligatures or � (German small sz) being resolved, we need to process
1669         //! the text replacements from end to start.
1670         //! This way the offsets for the yet to be changed words will be
1671         //! left unchanged by the already replaced text.
1672         //! For this we temporarily save the changes to be done in this vector
1673         std::vector< TransliterationChgData >   aChanges;
1674         TransliterationChgData                  aChgData;
1675 
1676         if (rTrans.getType() == (sal_uInt32)TransliterationModulesExtra::TITLE_CASE)
1677         {
1678             // for 'capitalize every word' we need to iterate over each word
1679 
1680             Boundary aSttBndry;
1681             Boundary aEndBndry;
1682             aSttBndry = pBreakIt->GetBreakIter()->getWordBoundary(
1683                         GetTxt(), nStt,
1684                         pBreakIt->GetLocale( GetLang( nStt ) ),
1685                         nWordType,
1686                         sal_True /*prefer forward direction*/);
1687             aEndBndry = pBreakIt->GetBreakIter()->getWordBoundary(
1688                         GetTxt(), nEnd,
1689                         pBreakIt->GetLocale( GetLang( nEnd ) ),
1690                         nWordType,
1691                         sal_False /*prefer backward direction*/);
1692 
1693             // prevent backtracking to the previous word if selection is at word boundary
1694             if (aSttBndry.endPos <= nStt)
1695             {
1696                 aSttBndry = pBreakIt->GetBreakIter()->nextWord(
1697                         GetTxt(), aSttBndry.endPos,
1698                         pBreakIt->GetLocale( GetLang( aSttBndry.endPos ) ),
1699                         nWordType);
1700             }
1701             // prevent advancing to the next word if selection is at word boundary
1702             if (aEndBndry.startPos >= nEnd)
1703             {
1704                 aEndBndry = pBreakIt->GetBreakIter()->previousWord(
1705                         GetTxt(), aEndBndry.startPos,
1706                         pBreakIt->GetLocale( GetLang( aEndBndry.startPos ) ),
1707                         nWordType);
1708             }
1709 
1710             Boundary aCurWordBndry( aSttBndry );
1711             while (aCurWordBndry.startPos <= aEndBndry.startPos)
1712             {
1713                 nStt = (xub_StrLen)aCurWordBndry.startPos;
1714                 nEnd = (xub_StrLen)aCurWordBndry.endPos;
1715                 sal_Int32 nLen = nEnd - nStt;
1716                 DBG_ASSERT( nLen > 0, "invalid word length of 0" );
1717 #if OSL_DEBUG_LEVEL > 1
1718                 String aText( GetTxt().Copy( nStt, nLen ) );
1719 #endif
1720 
1721 		        Sequence <sal_Int32> aOffsets;
1722                 String sChgd( rTrans.transliterate( GetTxt(), GetLang( nStt ), nStt, nLen, &aOffsets ));
1723 
1724                 if (!m_Text.Equals( sChgd, nStt, nLen ))
1725                 {
1726                     aChgData.nStart     = nStt;
1727                     aChgData.nLen       = nLen;
1728                     aChgData.sChanged   = sChgd;
1729                     aChgData.aOffsets   = aOffsets;
1730                     aChanges.push_back( aChgData );
1731                 }
1732 
1733                 aCurWordBndry = pBreakIt->GetBreakIter()->nextWord(
1734                         GetTxt(), nEnd,
1735                         pBreakIt->GetLocale( GetLang( nEnd ) ),
1736                         nWordType);
1737             }
1738         }
1739         else if (rTrans.getType() == (sal_uInt32)TransliterationModulesExtra::SENTENCE_CASE)
1740         {
1741             // for 'sentence case' we need to iterate sentence by sentence
1742 
1743             sal_Int32 nLastStart = pBreakIt->GetBreakIter()->beginOfSentence(
1744                     GetTxt(), nEnd,
1745                     pBreakIt->GetLocale( GetLang( nEnd ) ) );
1746             sal_Int32 nLastEnd = pBreakIt->GetBreakIter()->endOfSentence(
1747                     GetTxt(), nLastStart,
1748                     pBreakIt->GetLocale( GetLang( nLastStart ) ) );
1749 
1750             // extend nStt, nEnd to the current sentence boundaries
1751             sal_Int32 nCurrentStart = pBreakIt->GetBreakIter()->beginOfSentence(
1752                     GetTxt(), nStt,
1753                     pBreakIt->GetLocale( GetLang( nStt ) ) );
1754             sal_Int32 nCurrentEnd = pBreakIt->GetBreakIter()->endOfSentence(
1755                     GetTxt(), nCurrentStart,
1756                     pBreakIt->GetLocale( GetLang( nCurrentStart ) ) );
1757 
1758             // prevent backtracking to the previous sentence if selection starts at end of a sentence
1759             if (nCurrentEnd <= nStt)
1760             {
1761                 // now nCurrentStart is probably located on a non-letter word. (unless we
1762                 // are in Asian text with no spaces...)
1763                 // Thus to get the real sentence start we should locate the next real word,
1764                 // that is one found by DICTIONARY_WORD
1765                 i18n::Boundary aBndry = pBreakIt->GetBreakIter()->nextWord(
1766                         GetTxt(), nCurrentEnd,
1767                         pBreakIt->GetLocale( GetLang( nCurrentEnd ) ),
1768                         i18n::WordType::DICTIONARY_WORD);
1769 
1770                 // now get new current sentence boundaries
1771                 nCurrentStart = pBreakIt->GetBreakIter()->beginOfSentence(
1772                         GetTxt(), aBndry.startPos,
1773                         pBreakIt->GetLocale( GetLang( aBndry.startPos) ) );
1774                 nCurrentEnd = pBreakIt->GetBreakIter()->endOfSentence(
1775                         GetTxt(), nCurrentStart,
1776                         pBreakIt->GetLocale( GetLang( nCurrentStart) ) );
1777             }
1778             // prevent advancing to the next sentence if selection ends at start of a sentence
1779             if (nLastStart >= nEnd)
1780             {
1781                 // now nCurrentStart is probably located on a non-letter word. (unless we
1782                 // are in Asian text with no spaces...)
1783                 // Thus to get the real sentence start we should locate the previous real word,
1784                 // that is one found by DICTIONARY_WORD
1785                 i18n::Boundary aBndry = pBreakIt->GetBreakIter()->previousWord(
1786                         GetTxt(), nLastStart,
1787                         pBreakIt->GetLocale( GetLang( nLastStart) ),
1788                         i18n::WordType::DICTIONARY_WORD);
1789                 nLastEnd = pBreakIt->GetBreakIter()->endOfSentence(
1790                         GetTxt(), aBndry.startPos,
1791                         pBreakIt->GetLocale( GetLang( aBndry.startPos) ) );
1792                 if (nCurrentEnd > nLastEnd)
1793                     nCurrentEnd = nLastEnd;
1794             }
1795 
1796             while (nCurrentStart < nLastEnd)
1797             {
1798                 sal_Int32 nLen = nCurrentEnd - nCurrentStart;
1799                 DBG_ASSERT( nLen > 0, "invalid word length of 0" );
1800 #if OSL_DEBUG_LEVEL > 1
1801                 String aText( GetTxt().Copy( nCurrentStart, nLen ) );
1802 #endif
1803 
1804 		        Sequence <sal_Int32> aOffsets;
1805                 String sChgd( rTrans.transliterate( GetTxt(),
1806                         GetLang( nCurrentStart ), nCurrentStart, nLen, &aOffsets ));
1807 
1808                 if (!m_Text.Equals( sChgd, nStt, nLen ))
1809                 {
1810                     aChgData.nStart     = nCurrentStart;
1811                     aChgData.nLen       = nLen;
1812                     aChgData.sChanged   = sChgd;
1813                     aChgData.aOffsets   = aOffsets;
1814                     aChanges.push_back( aChgData );
1815                 }
1816 
1817                 Boundary aFirstWordBndry;
1818                 aFirstWordBndry = pBreakIt->GetBreakIter()->nextWord(
1819                         GetTxt(), nCurrentEnd,
1820                         pBreakIt->GetLocale( GetLang( nCurrentEnd ) ),
1821                         nWordType);
1822                 nCurrentStart = aFirstWordBndry.startPos;
1823                 nCurrentEnd = pBreakIt->GetBreakIter()->endOfSentence(
1824                         GetTxt(), nCurrentStart,
1825                         pBreakIt->GetLocale( GetLang( nCurrentStart ) ) );
1826             }
1827         }
1828         else
1829         {
1830             // here we may transliterate over complete language portions...
1831 
1832 		    SwLanguageIterator* pIter;
1833 		    if( rTrans.needLanguageForTheMode() )
1834                 pIter = new SwLanguageIterator( *this, nStt );
1835 		    else
1836 			    pIter = 0;
1837 
1838 		    xub_StrLen nEndPos;
1839 		    sal_uInt16 nLang;
1840 		    do {
1841 			    if( pIter )
1842 			    {
1843 				    nLang = pIter->GetLanguage();
1844 				    nEndPos = pIter->GetChgPos();
1845 				    if( nEndPos > nEnd )
1846 					    nEndPos = nEnd;
1847 			    }
1848 			    else
1849 			    {
1850 				    nLang = LANGUAGE_SYSTEM;
1851 				    nEndPos = nEnd;
1852 			    }
1853                 xub_StrLen nLen = nEndPos - nStt;
1854 
1855 			    Sequence <sal_Int32> aOffsets;
1856                 String sChgd( rTrans.transliterate( m_Text, nLang, nStt, nLen, &aOffsets ));
1857 
1858                 if (!m_Text.Equals( sChgd, nStt, nLen ))
1859                 {
1860                     aChgData.nStart     = nStt;
1861                     aChgData.nLen       = nLen;
1862                     aChgData.sChanged   = sChgd;
1863                     aChgData.aOffsets   = aOffsets;
1864                     aChanges.push_back( aChgData );
1865                 }
1866 
1867                 nStt = nEndPos;
1868 		    } while( nEndPos < nEnd && pIter && pIter->Next() );
1869 		    delete pIter;
1870         }
1871 
1872         if (aChanges.size() > 0)
1873         {
1874             // now apply the changes from end to start to leave the offsets of the
1875             // yet unchanged text parts remain the same.
1876             for (size_t i = 0; i < aChanges.size(); ++i)
1877             {
1878                 TransliterationChgData &rData = aChanges[ aChanges.size() - 1 - i ];
1879                 if (pUndo)
1880                     pUndo->AddChanges( *this, rData.nStart, rData.nLen, rData.aOffsets );
1881                 ReplaceTextOnly( rData.nStart, rData.nLen, rData.sChanged, rData.aOffsets );
1882             }
1883         }
1884     }
1885 }
1886 
1887 
1888 void SwTxtNode::ReplaceTextOnly( xub_StrLen nPos, xub_StrLen nLen,
1889 								const XubString& rText,
1890 								const Sequence<sal_Int32>& rOffsets )
1891 {
1892     m_Text.Replace( nPos, nLen, rText );
1893 
1894 	xub_StrLen nTLen = rText.Len();
1895 	const sal_Int32* pOffsets = rOffsets.getConstArray();
1896 	// now look for no 1-1 mapping -> move the indizies!
1897 	xub_StrLen nI, nMyOff;
1898 	for( nI = 0, nMyOff = nPos; nI < nTLen; ++nI, ++nMyOff )
1899 	{
1900 		xub_StrLen nOff = (xub_StrLen)pOffsets[ nI ];
1901 		if( nOff < nMyOff )
1902 		{
1903 			// something is inserted
1904 			xub_StrLen nCnt = 1;
1905 			while( nI + nCnt < nTLen && nOff == pOffsets[ nI + nCnt ] )
1906 				++nCnt;
1907 
1908 			Update( SwIndex( this, nMyOff ), nCnt, sal_False );
1909 			nMyOff = nOff;
1910 			//nMyOff -= nCnt;
1911 			nI += nCnt - 1;
1912 		}
1913 		else if( nOff > nMyOff )
1914 		{
1915 			// something is deleted
1916 			Update( SwIndex( this, nMyOff+1 ), nOff - nMyOff, sal_True );
1917 			nMyOff = nOff;
1918 		}
1919 	}
1920 	if( nMyOff < nLen )
1921 		// something is deleted at the end
1922 		Update( SwIndex( this, nMyOff ), nLen - nMyOff, sal_True );
1923 
1924 	// notify the layout!
1925 	SwDelTxt aDelHint( nPos, nTLen );
1926 	NotifyClients( 0, &aDelHint );
1927 
1928 	SwInsTxt aHint( nPos, nTLen );
1929 	NotifyClients( 0, &aHint );
1930 }
1931 
1932 void SwTxtNode::CountWords( SwDocStat& rStat,
1933                             xub_StrLen nStt, xub_StrLen nEnd ) const
1934 {
1935     ++rStat.nAllPara; // #i93174#: count _all_ paragraphs
1936     if( nStt < nEnd )
1937     {
1938         if ( !IsHidden() )
1939         {
1940             ++rStat.nPara;
1941             sal_uLong nTmpWords = 0;
1942             sal_uLong nTmpChars = 0;
1943 
1944             // Shortcut: Whole paragraph should be considered and cached values
1945             // are valid:
1946             if ( 0 == nStt && GetTxt().Len() == nEnd && !IsWordCountDirty() )
1947             {
1948                 nTmpWords = GetParaNumberOfWords();
1949                 nTmpChars = GetParaNumberOfChars();
1950             }
1951             else
1952             {
1953                 String aOldStr( m_Text );
1954                 String& rCastStr = const_cast<String&>(m_Text);
1955 
1956                 // fills the deleted redlines and hidden ranges with cChar:
1957                 const xub_Unicode cChar(' ');
1958                 const sal_uInt16 nNumOfMaskedChars =
1959                         lcl_MaskRedlinesAndHiddenText( *this, rCastStr, nStt, nEnd, cChar, false );
1960 
1961                 // expand fields
1962                 rtl::OUString aExpandText;
1963                 const ModelToViewHelper::ConversionMap* pConversionMap =
1964                         BuildConversionMap( aExpandText );
1965 
1966                 const sal_uInt32 nExpandBegin = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nStt );
1967                 const sal_uInt32 nExpandEnd   = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nEnd );
1968                 aExpandText = aExpandText.copy( nExpandBegin, nExpandEnd - nExpandBegin );
1969 
1970                 const bool bCount = aExpandText.getLength() > 0;
1971 
1972                 // count words in 'regular' text:
1973                 if( bCount && pBreakIt->GetBreakIter().is() )
1974                 {
1975                     // split into different script languages
1976                     sal_Int32 nScriptBegin = 0;
1977                     while ( nScriptBegin < aExpandText.getLength() )
1978                     {
1979                         const sal_Int16 nCurrScript = pBreakIt->GetBreakIter()->getScriptType( aExpandText, nScriptBegin );
1980                         const sal_Int32 nScriptEnd = pBreakIt->GetBreakIter()->endOfScript( aExpandText, nScriptBegin, nCurrScript );
1981                         rtl::OUString aScriptText = aExpandText.copy( nScriptBegin, nScriptEnd - nScriptBegin );
1982 
1983                         // Asian languages count words as characters
1984                         if ( nCurrScript == ::com::sun::star::i18n::ScriptType::ASIAN )
1985                         {
1986                             // subtract white spaces
1987                             sal_Int32 nSpaceCount = 0;
1988                             sal_Int32 nSpacePos = 0;
1989 
1990                             // subtract normal white spaces
1991                             nSpacePos = -1;
1992                             while ( ( nSpacePos = aScriptText.indexOf( ' ', nSpacePos + 1 ) ) != -1 )
1993                             {
1994                                 nSpaceCount++;
1995                             }
1996                             // subtract Asian full-width white spaces
1997                             nSpacePos = -1;
1998                             while ( ( nSpacePos = aScriptText.indexOf( 12288, nSpacePos + 1 ) ) != -1 )
1999                             {
2000                                 nSpaceCount++;
2001                             }
2002                             nTmpWords += nScriptEnd - nScriptBegin - nSpaceCount;
2003                         }
2004                         else
2005                         {
2006                             const String aScannerText( aScriptText );
2007                             SwScanner aScanner( *this, aScannerText, 0, pConversionMap,
2008                                                 i18n::WordType::WORD_COUNT,
2009                                                 (xub_StrLen)0, (xub_StrLen)aScriptText.getLength() );
2010 
2011                             const rtl::OUString aBreakWord( CH_TXTATR_BREAKWORD );
2012 
2013                             while ( aScanner.NextWord() )
2014                             {
2015                                 if ( aScanner.GetLen() > 1 ||
2016                                      CH_TXTATR_BREAKWORD != aScriptText.match(aBreakWord, aScanner.GetBegin() ) )
2017                                     ++nTmpWords;
2018                             }
2019                         }
2020                         nScriptBegin = nScriptEnd;
2021                     }
2022                 }
2023 
2024                 ASSERT( aExpandText.getLength() >= nNumOfMaskedChars,
2025                         "More characters hidden that characters in string!" )
2026                 nTmpChars = nExpandEnd - nExpandBegin - nNumOfMaskedChars;
2027 
2028                 // count words in numbering string:
2029                 if ( nStt == 0 && bCount )
2030                 {
2031                     // add numbering label
2032                     const String aNumString = GetNumString();
2033                     const xub_StrLen nNumStringLen = aNumString.Len();
2034                     if ( nNumStringLen > 0 )
2035                     {
2036                         LanguageType aLanguage = GetLang( 0 );
2037 
2038                         SwScanner aScanner( *this, aNumString, &aLanguage, 0,
2039                                             i18n::WordType::WORD_COUNT,
2040                                             0, nNumStringLen );
2041 
2042                         while ( aScanner.NextWord() )
2043                             ++nTmpWords;
2044 
2045                         nTmpChars += nNumStringLen;
2046                     }
2047                     else if ( HasBullet() )
2048                     {
2049                         ++nTmpWords;
2050                         ++nTmpChars;
2051                     }
2052                 }
2053 
2054                 delete pConversionMap;
2055 
2056                 rCastStr = aOldStr;
2057 
2058                 // If the whole paragraph has been calculated, update cached
2059                 // values:
2060                 if ( 0 == nStt && GetTxt().Len() == nEnd )
2061                 {
2062                     SetParaNumberOfWords( nTmpWords );
2063                     SetParaNumberOfChars( nTmpChars );
2064                     SetWordCountDirty( false );
2065                 }
2066             }
2067 
2068             rStat.nWord += nTmpWords;
2069             rStat.nChar += nTmpChars;
2070         }
2071     }
2072 }
2073 
2074 //
2075 // Paragraph statistics start
2076 //
2077 struct SwParaIdleData_Impl
2078 {
2079     SwWrongList* pWrong;            // for spell checking
2080     SwGrammarMarkUp* pGrammarCheck;     // for grammar checking /  proof reading
2081     SwWrongList* pSmartTags;
2082     sal_uLong nNumberOfWords;
2083     sal_uLong nNumberOfChars;
2084     bool bWordCountDirty        : 1;
2085     bool bWrongDirty            : 1;    // Ist das Wrong-Feld auf invalid?
2086     bool bGrammarCheckDirty     : 1;
2087     bool bSmartTagDirty         : 1;
2088     bool bAutoComplDirty        : 1;    // die ACompl-Liste muss angepasst werden
2089 
2090     SwParaIdleData_Impl() :
2091         pWrong              ( 0 ),
2092         pGrammarCheck       ( 0 ),
2093         pSmartTags          ( 0 ),
2094         nNumberOfWords      ( 0 ),
2095         nNumberOfChars      ( 0 ),
2096         bWordCountDirty     ( true ),
2097         bWrongDirty         ( true ),
2098         bGrammarCheckDirty  ( true ),
2099         bSmartTagDirty      ( true ),
2100         bAutoComplDirty     ( true ) {};
2101 };
2102 
2103 void SwTxtNode::InitSwParaStatistics( bool bNew )
2104 {
2105     if ( bNew )
2106     {
2107         m_pParaIdleData_Impl = new SwParaIdleData_Impl;
2108     }
2109     else if ( m_pParaIdleData_Impl )
2110     {
2111         delete m_pParaIdleData_Impl->pWrong;
2112         delete m_pParaIdleData_Impl->pGrammarCheck;
2113         delete m_pParaIdleData_Impl->pSmartTags;
2114         delete m_pParaIdleData_Impl;
2115         m_pParaIdleData_Impl = 0;
2116     }
2117 }
2118 
2119 void SwTxtNode::SetWrong( SwWrongList* pNew, bool bDelete )
2120 {
2121     if ( m_pParaIdleData_Impl )
2122     {
2123         if ( bDelete )
2124         {
2125             delete m_pParaIdleData_Impl->pWrong;
2126         }
2127         m_pParaIdleData_Impl->pWrong = pNew;
2128     }
2129 }
2130 
2131 SwWrongList* SwTxtNode::GetWrong()
2132 {
2133     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong : 0;
2134 }
2135 
2136 // --> OD 2008-05-27 #i71360#
2137 const SwWrongList* SwTxtNode::GetWrong() const
2138 {
2139     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong : 0;
2140 }
2141 // <--
2142 
2143 
2144 void SwTxtNode::SetGrammarCheck( SwGrammarMarkUp* pNew, bool bDelete )
2145 {
2146     if ( m_pParaIdleData_Impl )
2147     {
2148         if ( bDelete )
2149         {
2150             delete m_pParaIdleData_Impl->pGrammarCheck;
2151         }
2152         m_pParaIdleData_Impl->pGrammarCheck = pNew;
2153     }
2154 }
2155 
2156 SwGrammarMarkUp* SwTxtNode::GetGrammarCheck()
2157 {
2158     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pGrammarCheck : 0;
2159 }
2160 
2161 void SwTxtNode::SetSmartTags( SwWrongList* pNew, bool bDelete )
2162 {
2163     ASSERT( !pNew || SwSmartTagMgr::Get().IsSmartTagsEnabled(),
2164             "Weird - we have a smart tag list without any recognizers?" )
2165 
2166     if ( m_pParaIdleData_Impl )
2167     {
2168         if ( bDelete )
2169         {
2170             delete m_pParaIdleData_Impl->pSmartTags;
2171         }
2172         m_pParaIdleData_Impl->pSmartTags = pNew;
2173     }
2174 }
2175 
2176 SwWrongList* SwTxtNode::GetSmartTags()
2177 {
2178     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pSmartTags : 0;
2179 }
2180 
2181 void SwTxtNode::SetParaNumberOfWords( sal_uLong nNew ) const
2182 {
2183     if ( m_pParaIdleData_Impl )
2184     {
2185         m_pParaIdleData_Impl->nNumberOfWords = nNew;
2186     }
2187 }
2188 sal_uLong SwTxtNode::GetParaNumberOfWords() const
2189 {
2190     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->nNumberOfWords : 0;
2191 }
2192 void SwTxtNode::SetParaNumberOfChars( sal_uLong nNew ) const
2193 {
2194     if ( m_pParaIdleData_Impl )
2195     {
2196         m_pParaIdleData_Impl->nNumberOfChars = nNew;
2197     }
2198 }
2199 sal_uLong SwTxtNode::GetParaNumberOfChars() const
2200 {
2201     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->nNumberOfChars : 0;
2202 }
2203 void SwTxtNode::SetWordCountDirty( bool bNew ) const
2204 {
2205     if ( m_pParaIdleData_Impl )
2206     {
2207         m_pParaIdleData_Impl->bWordCountDirty = bNew;
2208     }
2209 }
2210 bool SwTxtNode::IsWordCountDirty() const
2211 {
2212     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bWordCountDirty : 0;
2213 }
2214 void SwTxtNode::SetWrongDirty( bool bNew ) const
2215 {
2216     if ( m_pParaIdleData_Impl )
2217     {
2218         m_pParaIdleData_Impl->bWrongDirty = bNew;
2219     }
2220 }
2221 bool SwTxtNode::IsWrongDirty() const
2222 {
2223     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bWrongDirty : 0;
2224 }
2225 void SwTxtNode::SetGrammarCheckDirty( bool bNew ) const
2226 {
2227     if ( m_pParaIdleData_Impl )
2228     {
2229         m_pParaIdleData_Impl->bGrammarCheckDirty = bNew;
2230     }
2231 }
2232 bool SwTxtNode::IsGrammarCheckDirty() const
2233 {
2234     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bGrammarCheckDirty : 0;
2235 }
2236 void SwTxtNode::SetSmartTagDirty( bool bNew ) const
2237 {
2238     if ( m_pParaIdleData_Impl )
2239     {
2240         m_pParaIdleData_Impl->bSmartTagDirty = bNew;
2241     }
2242 }
2243 bool SwTxtNode::IsSmartTagDirty() const
2244 {
2245     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bSmartTagDirty : 0;
2246 }
2247 void SwTxtNode::SetAutoCompleteWordDirty( bool bNew ) const
2248 {
2249     if ( m_pParaIdleData_Impl )
2250     {
2251         m_pParaIdleData_Impl->bAutoComplDirty = bNew;
2252     }
2253 }
2254 bool SwTxtNode::IsAutoCompleteWordDirty() const
2255 {
2256     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bAutoComplDirty : 0;
2257 }
2258 //
2259 // Paragraph statistics end
2260 //
2261 
2262 //Bug 120881:Modify here for Directly Page Numbering
2263 sal_Bool SwTxtFrm::HasPageNumberField()
2264 {
2265 	return GetRegisteredIn()?((SwTxtNode*)GetRegisteredIn())->HasPageNumberField():false;
2266 }
2267 //Bug 120881(End)
2268 
2269