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