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
lcl_IsSkippableWhiteSpace(xub_Unicode cCh)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
lcl_MaskRedlines(const SwTxtNode & rNode,XubString & rText,const xub_StrLen nStt,const xub_StrLen nEnd,const xub_Unicode cChar)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
lcl_MaskRedlinesAndHiddenText(const SwTxtNode & rNode,XubString & rText,const xub_StrLen nStt,const xub_StrLen nEnd,const xub_Unicode cChar=CH_TXTATR_INWORD,bool bCheckShowHiddenChar=true)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
lcl_CalculateRepaintRect(SwTxtFrm & rTxtFrm,xub_StrLen nChgStart,xub_StrLen nChgEnd)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
lcl_HaveCommonAttributes(IStyleAccess & rStyleAccess,const SfxItemSet * pSet1,sal_uInt16 nWhichId,const SfxItemSet & rSet2,boost::shared_ptr<SfxItemSet> & pStyleHandle)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
InRange(xub_StrLen nIdx,xub_StrLen nStart,xub_StrLen nEnd)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
RstTxtAttr(const SwIndex & rIdx,const xub_StrLen nLen,const sal_uInt16 nWhich,const SfxItemSet * pSet,const sal_Bool bInclRefToxMark)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
GetCurWord(xub_StrLen nPos) const682 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
SwScanner(const SwTxtNode & rNd,const String & rTxt,const LanguageType * pLang,const ModelToViewHelper::ConversionMap * pConvMap,sal_uInt16 nType,xub_StrLen nStart,xub_StrLen nEnde,sal_Bool bClp)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
NextWord()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
Spell(SwSpellArgs * pArgs)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
SetLanguageAndFont(const SwPaM & rPaM,LanguageType nLang,sal_uInt16 nLangWhichId,const Font * pFont,sal_uInt16 nFontWhichId)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
Convert(SwConversionArgs & rArgs)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 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!
_AutoSpell(const SwCntntNode * pActNode,const SwViewOption & rViewOpt,xub_StrLen nActPos)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 */
SmartTagScan(SwCntntNode *,xub_StrLen)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
CollectAutoCmplWrds(SwCntntNode * pActNode,xub_StrLen nActPos)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
Hyphenate(SwInterHyphInfo & rHyphInf)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
Flush()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/...
TransliterateText(utl::TransliterationWrapper & rTrans,xub_StrLen nStt,xub_StrLen nEnd,SwUndoTransliterate * pUndo)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
ReplaceTextOnly(xub_StrLen nPos,xub_StrLen nLen,const XubString & rText,const Sequence<sal_Int32> & rOffsets)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
CountWords(SwDocStat & rStat,xub_StrLen nStt,xub_StrLen nEnd) const1939 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
SwParaIdleData_ImplSwParaIdleData_Impl2097 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
InitSwParaStatistics(bool bNew)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
SetWrong(SwWrongList * pNew,bool bDelete)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
GetWrong()2138 SwWrongList* SwTxtNode::GetWrong()
2139 {
2140 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong : 0;
2141 }
2142
2143 // --> OD 2008-05-27 #i71360#
GetWrong() const2144 const SwWrongList* SwTxtNode::GetWrong() const
2145 {
2146 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong : 0;
2147 }
2148 // <--
2149
2150
SetGrammarCheck(SwGrammarMarkUp * pNew,bool bDelete)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
GetGrammarCheck()2163 SwGrammarMarkUp* SwTxtNode::GetGrammarCheck()
2164 {
2165 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pGrammarCheck : 0;
2166 }
2167
SetSmartTags(SwWrongList * pNew,bool bDelete)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
GetSmartTags()2183 SwWrongList* SwTxtNode::GetSmartTags()
2184 {
2185 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pSmartTags : 0;
2186 }
2187
SetParaNumberOfWords(sal_uLong nNew) const2188 void SwTxtNode::SetParaNumberOfWords( sal_uLong nNew ) const
2189 {
2190 if ( m_pParaIdleData_Impl )
2191 {
2192 m_pParaIdleData_Impl->nNumberOfWords = nNew;
2193 }
2194 }
GetParaNumberOfWords() const2195 sal_uLong SwTxtNode::GetParaNumberOfWords() const
2196 {
2197 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->nNumberOfWords : 0;
2198 }
SetParaNumberOfChars(sal_uLong nNew) const2199 void SwTxtNode::SetParaNumberOfChars( sal_uLong nNew ) const
2200 {
2201 if ( m_pParaIdleData_Impl )
2202 {
2203 m_pParaIdleData_Impl->nNumberOfChars = nNew;
2204 }
2205 }
GetParaNumberOfChars() const2206 sal_uLong SwTxtNode::GetParaNumberOfChars() const
2207 {
2208 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->nNumberOfChars : 0;
2209 }
SetWordCountDirty(bool bNew) const2210 void SwTxtNode::SetWordCountDirty( bool bNew ) const
2211 {
2212 if ( m_pParaIdleData_Impl )
2213 {
2214 m_pParaIdleData_Impl->bWordCountDirty = bNew;
2215 }
2216 }
IsWordCountDirty() const2217 bool SwTxtNode::IsWordCountDirty() const
2218 {
2219 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bWordCountDirty : 0;
2220 }
SetWrongDirty(bool bNew) const2221 void SwTxtNode::SetWrongDirty( bool bNew ) const
2222 {
2223 if ( m_pParaIdleData_Impl )
2224 {
2225 m_pParaIdleData_Impl->bWrongDirty = bNew;
2226 }
2227 }
IsWrongDirty() const2228 bool SwTxtNode::IsWrongDirty() const
2229 {
2230 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bWrongDirty : 0;
2231 }
SetGrammarCheckDirty(bool bNew) const2232 void SwTxtNode::SetGrammarCheckDirty( bool bNew ) const
2233 {
2234 if ( m_pParaIdleData_Impl )
2235 {
2236 m_pParaIdleData_Impl->bGrammarCheckDirty = bNew;
2237 }
2238 }
IsGrammarCheckDirty() const2239 bool SwTxtNode::IsGrammarCheckDirty() const
2240 {
2241 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bGrammarCheckDirty : 0;
2242 }
SetSmartTagDirty(bool bNew) const2243 void SwTxtNode::SetSmartTagDirty( bool bNew ) const
2244 {
2245 if ( m_pParaIdleData_Impl )
2246 {
2247 m_pParaIdleData_Impl->bSmartTagDirty = bNew;
2248 }
2249 }
IsSmartTagDirty() const2250 bool SwTxtNode::IsSmartTagDirty() const
2251 {
2252 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bSmartTagDirty : 0;
2253 }
SetAutoCompleteWordDirty(bool bNew) const2254 void SwTxtNode::SetAutoCompleteWordDirty( bool bNew ) const
2255 {
2256 if ( m_pParaIdleData_Impl )
2257 {
2258 m_pParaIdleData_Impl->bAutoComplDirty = bNew;
2259 }
2260 }
IsAutoCompleteWordDirty() const2261 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
HasPageNumberField()2270 sal_Bool SwTxtFrm::HasPageNumberField()
2271 {
2272 return GetRegisteredIn()?((SwTxtNode*)GetRegisteredIn())->HasPageNumberField():false;
2273 }
2274 //Bug 120881(End)
2275
2276