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