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