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