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