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