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