LibreOffice Module sw (master)  1
txtedt.cxx
Go to the documentation of this file.
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 <svl/languageoptions.hxx>
24 #include <editeng/splwrap.hxx>
25 #include <editeng/langitem.hxx>
26 #include <editeng/fontitem.hxx>
27 #include <editeng/hangulhanja.hxx>
29 #include <SwSmartTagMgr.hxx>
30 #include <o3tl/safeint.hxx>
31 #include <osl/diagnose.h>
32 #include <officecfg/Office/Writer.hxx>
34 #include <unotools/charclass.hxx>
35 #include <sal/log.hxx>
36 #include <swmodule.hxx>
37 #include <splargs.hxx>
38 #include <viewopt.hxx>
39 #include <acmplwrd.hxx>
40 #include <doc.hxx>
43 #include <docsh.hxx>
44 #include <txtfld.hxx>
45 #include <txatbase.hxx>
46 #include <charatr.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 #include <swscanner.hxx>
54 
55 #include <breakit.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>
69 
70 #include <com/sun/star/i18n/WordType.hpp>
71 #include <com/sun/star/i18n/ScriptType.hpp>
72 #include <com/sun/star/i18n/XBreakIterator.hpp>
73 
74 #include <vector>
75 #include <utility>
76 
77 #include <unotextrange.hxx>
78 
79 using namespace ::com::sun::star;
80 using namespace ::com::sun::star::frame;
81 using namespace ::com::sun::star::i18n;
82 using namespace ::com::sun::star::beans;
83 using namespace ::com::sun::star::uno;
84 using namespace ::com::sun::star::linguistic2;
85 using namespace ::com::sun::star::smarttags;
86 
87 namespace
88 {
89  void DetectAndMarkMissingDictionaries( SwDoc& rDoc,
90  const uno::Reference< XSpellChecker1 >& xSpell,
91  const LanguageType eActLang )
92  {
93  if( xSpell.is() && !xSpell->hasLanguage( eActLang.get() ) )
94  rDoc.SetMissingDictionaries( true );
95  else
96  rDoc.SetMissingDictionaries( false );
97  }
98 }
99 
101 {
102  std::unique_ptr<SwWrongList> pWrong; // for spell checking
103  std::unique_ptr<SwGrammarMarkUp> pGrammarCheck; // for grammar checking / proof reading
104  std::unique_ptr<SwWrongList> pSmartTags;
114 
116  nNumberOfWords ( 0 ),
117  nNumberOfAsianWords ( 0 ),
118  nNumberOfChars ( 0 ),
119  nNumberOfCharsExcludingSpaces ( 0 ),
120  bWordCountDirty ( true ),
121  eWrongDirty ( SwTextNode::WrongState::TODO ),
122  bGrammarCheckDirty ( true ),
123  bSmartTagDirty ( true ),
124  bAutoComplDirty ( true ) {};
125 };
126 
127 static bool lcl_HasComments(const SwTextNode& rNode)
128 {
129  sal_Int32 nPosition = rNode.GetText().indexOf(CH_TXTATR_INWORD);
130  while (nPosition != -1)
131  {
132  const SwTextAttr* pAttr = rNode.GetTextAttrForCharAt(nPosition);
133  if (pAttr && pAttr->Which() == RES_TXTATR_ANNOTATION)
134  return true;
135  nPosition = rNode.GetText().indexOf(CH_TXTATR_INWORD, nPosition + 1);
136  }
137  return false;
138 }
139 
140 /*
141  * This has basically the same function as SwScriptInfo::MaskHiddenRanges,
142  * only for deleted redlines
143  */
144 
145 static sal_Int32
146 lcl_MaskRedlines( const SwTextNode& rNode, OUStringBuffer& rText,
147  sal_Int32 nStt, sal_Int32 nEnd,
148  const sal_Unicode cChar )
149 {
150  sal_Int32 nNumOfMaskedRedlines = 0;
151 
152  const SwDoc& rDoc = rNode.GetDoc();
153 
154  for ( SwRedlineTable::size_type nAct = rDoc.getIDocumentRedlineAccess().GetRedlinePos( rNode, RedlineType::Any ); nAct < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size(); ++nAct )
155  {
156  const SwRangeRedline* pRed = rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nAct ];
157 
158  if ( pRed->Start()->nNode > rNode.GetIndex() )
159  break;
160 
161  if( RedlineType::Delete == pRed->GetType() )
162  {
163  sal_Int32 nRedlineEnd;
164  sal_Int32 nRedlineStart;
165 
166  pRed->CalcStartEnd( rNode.GetIndex(), nRedlineStart, nRedlineEnd );
167 
168  if ( nRedlineEnd < nStt || nRedlineStart > nEnd )
169  continue;
170 
171  while ( nRedlineStart < nRedlineEnd && nRedlineStart < nEnd )
172  {
173  if (nRedlineStart >= nStt)
174  {
175  rText[nRedlineStart] = cChar;
176  ++nNumOfMaskedRedlines;
177  }
178  ++nRedlineStart;
179  }
180  }
181  }
182 
183  return nNumOfMaskedRedlines;
184 }
185 
189 static bool
190 lcl_MaskRedlinesAndHiddenText( const SwTextNode& rNode, OUStringBuffer& rText,
191  sal_Int32 nStt, sal_Int32 nEnd,
192  const sal_Unicode cChar = CH_TXTATR_INWORD )
193 {
194  sal_Int32 nRedlinesMasked = 0;
195  sal_Int32 nHiddenCharsMasked = 0;
196 
197  const SwDoc& rDoc = rNode.GetDoc();
199 
200  // If called from word count or from spell checking, deleted redlines
201  // should be masked:
202  if ( bShowChg )
203  {
204  nRedlinesMasked = lcl_MaskRedlines( rNode, rText, nStt, nEnd, cChar );
205  }
206 
207  const bool bHideHidden = !SW_MOD()->GetViewOption(rDoc.GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE))->IsShowHiddenChar();
208 
209  // If called from word count, we want to mask the hidden ranges even
210  // if they are visible:
211  if ( bHideHidden )
212  {
213  nHiddenCharsMasked =
214  SwScriptInfo::MaskHiddenRanges( rNode, rText, nStt, nEnd, cChar );
215  }
216 
217  return (nRedlinesMasked > 0) || (nHiddenCharsMasked > 0);
218 }
219 
224  const SwTextFrame & rTextFrame, const SwTextNode & rNode,
225  sal_Int32 const nChgStart, sal_Int32 const nChgEnd)
226 {
227  TextFrameIndex const iChgStart(rTextFrame.MapModelToView(&rNode, nChgStart));
228  TextFrameIndex const iChgEnd(rTextFrame.MapModelToView(&rNode, nChgEnd));
229 
230  SwRect aRect = rTextFrame.GetPaintArea();
231  SwRect aTmp = rTextFrame.GetPaintArea();
232 
233  const SwTextFrame* pStartFrame = &rTextFrame;
234  while( pStartFrame->HasFollow() &&
235  iChgStart >= pStartFrame->GetFollow()->GetOffset())
236  pStartFrame = pStartFrame->GetFollow();
237  const SwTextFrame* pEndFrame = pStartFrame;
238  while( pEndFrame->HasFollow() &&
239  iChgEnd >= pEndFrame->GetFollow()->GetOffset())
240  pEndFrame = pEndFrame->GetFollow();
241 
242  bool bSameFrame = true;
243 
244  if( rTextFrame.HasFollow() )
245  {
246  if( pEndFrame != pStartFrame )
247  {
248  bSameFrame = false;
249  SwRect aStFrame( pStartFrame->GetPaintArea() );
250  {
251  SwRectFnSet aRectFnSet(pStartFrame);
252  aRectFnSet.SetLeft( aTmp, aRectFnSet.GetLeft(aStFrame) );
253  aRectFnSet.SetRight( aTmp, aRectFnSet.GetRight(aStFrame) );
254  aRectFnSet.SetBottom( aTmp, aRectFnSet.GetBottom(aStFrame) );
255  }
256  aStFrame = pEndFrame->GetPaintArea();
257  {
258  SwRectFnSet aRectFnSet(pEndFrame);
259  aRectFnSet.SetTop( aRect, aRectFnSet.GetTop(aStFrame) );
260  aRectFnSet.SetLeft( aRect, aRectFnSet.GetLeft(aStFrame) );
261  aRectFnSet.SetRight( aRect, aRectFnSet.GetRight(aStFrame) );
262  }
263  aRect.Union( aTmp );
264  while( true )
265  {
266  pStartFrame = pStartFrame->GetFollow();
267  if( pStartFrame == pEndFrame )
268  break;
269  aRect.Union( pStartFrame->GetPaintArea() );
270  }
271  }
272  }
273  if( bSameFrame )
274  {
275  SwRectFnSet aRectFnSet(pStartFrame);
276  if( aRectFnSet.GetTop(aTmp) == aRectFnSet.GetTop(aRect) )
277  aRectFnSet.SetLeft( aRect, aRectFnSet.GetLeft(aTmp) );
278  else
279  {
280  SwRect aStFrame( pStartFrame->GetPaintArea() );
281  aRectFnSet.SetLeft( aRect, aRectFnSet.GetLeft(aStFrame) );
282  aRectFnSet.SetRight( aRect, aRectFnSet.GetRight(aStFrame) );
283  aRectFnSet.SetTop( aRect, aRectFnSet.GetTop(aTmp) );
284  }
285 
286  if( aTmp.Height() > aRect.Height() )
287  aRect.Height( aTmp.Height() );
288  }
289 
290  return aRect;
291 }
292 
296 static bool lcl_HaveCommonAttributes( IStyleAccess& rStyleAccess,
297  const SfxItemSet* pSet1,
298  sal_uInt16 nWhichId,
299  const SfxItemSet& rSet2,
300  std::shared_ptr<SfxItemSet>& pStyleHandle )
301 {
302  bool bRet = false;
303 
304  std::unique_ptr<SfxItemSet> pNewSet;
305 
306  if ( !pSet1 )
307  {
308  OSL_ENSURE( nWhichId, "lcl_HaveCommonAttributes not used correctly" );
309  if ( SfxItemState::SET == rSet2.GetItemState( nWhichId, false ) )
310  {
311  pNewSet = rSet2.Clone();
312  pNewSet->ClearItem( nWhichId );
313  }
314  }
315  else if ( pSet1->Count() )
316  {
317  SfxItemIter aIter( *pSet1 );
318  const SfxPoolItem* pItem = aIter.GetCurItem();
319  do
320  {
321  if ( SfxItemState::SET == rSet2.GetItemState( pItem->Which(), false ) )
322  {
323  if ( !pNewSet )
324  pNewSet = rSet2.Clone();
325  pNewSet->ClearItem( pItem->Which() );
326  }
327 
328  pItem = aIter.NextItem();
329  } while (pItem);
330  }
331 
332  if ( pNewSet )
333  {
334  if ( pNewSet->Count() )
335  pStyleHandle = rStyleAccess.getAutomaticStyle( *pNewSet, IStyleAccess::AUTO_STYLE_CHAR );
336  bRet = true;
337  }
338 
339  return bRet;
340 }
341 
366  const SwIndex &rIdx,
367  const sal_Int32 nLen,
368  const sal_uInt16 nWhich,
369  const SfxItemSet* pSet,
370  const bool bInclRefToxMark,
371  const bool bExactRange )
372 {
373  if ( !GetpSwpHints() )
374  return;
375 
376  sal_Int32 nStt = rIdx.GetIndex();
377  sal_Int32 nEnd = nStt + nLen;
378  {
379  // enlarge range for the reset of text attributes in case of an overlapping input field
380  const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt( nStt, RES_TXTATR_INPUTFIELD, PARENT ));
381  if ( pTextInputField == nullptr )
382  {
383  pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt(nEnd, RES_TXTATR_INPUTFIELD, PARENT ));
384  }
385  if ( pTextInputField != nullptr )
386  {
387  if ( nStt > pTextInputField->GetStart() )
388  {
389  nStt = pTextInputField->GetStart();
390  }
391  if ( nEnd < *(pTextInputField->End()) )
392  {
393  nEnd = *(pTextInputField->End());
394  }
395  }
396  }
397 
398  bool bChanged = false;
399 
400  // nMin and nMax initialized to maximum / minimum (inverse)
401  sal_Int32 nMin = m_Text.getLength();
402  sal_Int32 nMax = nStt;
403  const bool bNoLen = nMin == 0;
404 
405  // We have to remember the "new" attributes that have
406  // been introduced by splitting surrounding attributes (case 2,3,4).
407  std::vector<SwTextAttr *> newAttributes;
408  std::vector<SwTextAttr *> delAttributes;
409 
410  // iterate over attribute array until start of attribute is behind deletion range
411  m_pSwpHints->SortIfNeedBe(); // trigger sorting now, we don't want it during iteration
412  size_t i = 0;
413  sal_Int32 nAttrStart = sal_Int32();
414  SwTextAttr *pHt = nullptr;
415  while ( (i < m_pSwpHints->Count())
416  && ( ( ( nAttrStart = m_pSwpHints->GetWithoutResorting(i)->GetStart()) < nEnd )
417  || nLen==0 ) && !bExactRange)
418  {
419  pHt = m_pSwpHints->GetWithoutResorting(i);
420 
421  // attributes without end stay in!
422  // but consider <bInclRefToxMark> used by Undo
423  const sal_Int32* const pAttrEnd = pHt->GetEnd();
424  const bool bKeepAttrWithoutEnd =
425  pAttrEnd == nullptr
426  && ( !bInclRefToxMark
427  || ( RES_TXTATR_REFMARK != pHt->Which()
428  && RES_TXTATR_TOXMARK != pHt->Which()
429  && RES_TXTATR_META != pHt->Which()
430  && RES_TXTATR_METAFIELD != pHt->Which() ) );
431  if ( bKeepAttrWithoutEnd )
432  {
433 
434  i++;
435  continue;
436  }
437  // attributes with content stay in
438  if ( pHt->HasContent() )
439  {
440  ++i;
441  continue;
442  }
443 
444  // Default behavior is to process all attributes:
445  bool bSkipAttr = false;
446  std::shared_ptr<SfxItemSet> pStyleHandle;
447 
448  // 1. case: We want to reset only the attributes listed in pSet:
449  if ( pSet )
450  {
451  bSkipAttr = SfxItemState::SET != pSet->GetItemState( pHt->Which(), false );
452  if ( bSkipAttr && RES_TXTATR_AUTOFMT == pHt->Which() )
453  {
454  // if the current attribute is an autostyle, we have to check if the autostyle
455  // and pSet have any attributes in common. If so, pStyleHandle will contain
456  // a handle to AutoStyle / pSet:
457  bSkipAttr = !lcl_HaveCommonAttributes( getIDocumentStyleAccess(), pSet, 0, *static_cast<const SwFormatAutoFormat&>(pHt->GetAttr()).GetStyleHandle(), pStyleHandle );
458  }
459  }
460  else if ( nWhich )
461  {
462  // 2. case: We want to reset only the attributes with WhichId nWhich:
463  bSkipAttr = nWhich != pHt->Which();
464  if ( bSkipAttr && RES_TXTATR_AUTOFMT == pHt->Which() )
465  {
466  bSkipAttr = !lcl_HaveCommonAttributes( getIDocumentStyleAccess(), nullptr, nWhich, *static_cast<const SwFormatAutoFormat&>(pHt->GetAttr()).GetStyleHandle(), pStyleHandle );
467  }
468  }
469  else if ( !bInclRefToxMark )
470  {
471  // 3. case: Reset all attributes except from ref/toxmarks:
472  // skip hints with CH_TXTATR here
473  // (deleting those is ONLY allowed for UNDO!)
474  bSkipAttr = RES_TXTATR_REFMARK == pHt->Which()
475  || RES_TXTATR_TOXMARK == pHt->Which()
476  || RES_TXTATR_META == pHt->Which()
477  || RES_TXTATR_METAFIELD == pHt->Which();
478  }
479 
480  if ( bSkipAttr )
481  {
482  i++;
483  continue;
484  }
485 
486  if (nStt <= nAttrStart) // Case: 1,3,5
487  {
488  const sal_Int32 nAttrEnd = pAttrEnd != nullptr
489  ? *pAttrEnd
490  : nAttrStart;
491  if (nEnd > nAttrStart
492  || (nEnd == nAttrEnd && nEnd == nAttrStart)) // Case: 1,3
493  {
494  if ( nMin > nAttrStart )
495  nMin = nAttrStart;
496  if ( nMax < nAttrEnd )
497  nMax = nAttrEnd;
498  // If only a no-extent hint is deleted, no resorting is needed
499  bChanged = bChanged || nEnd > nAttrStart || bNoLen;
500  if (nAttrEnd <= nEnd) // Case: 1
501  {
502  delAttributes.push_back(pHt);
503 
504  if ( pStyleHandle )
505  {
506  SwTextAttr* pNew = MakeTextAttr( GetDoc(),
507  *pStyleHandle, nAttrStart, nAttrEnd );
508  newAttributes.push_back(pNew);
509  }
510  }
511  else // Case: 3
512  {
513  bChanged = true;
514  m_pSwpHints->NoteInHistory( pHt );
515  // UGLY: this may temporarily destroy the sorting!
516  pHt->SetStart(nEnd);
517  m_pSwpHints->NoteInHistory( pHt, true );
518 
519  if ( pStyleHandle && nAttrStart < nEnd )
520  {
521  SwTextAttr* pNew = MakeTextAttr( GetDoc(),
522  *pStyleHandle, nAttrStart, nEnd );
523  newAttributes.push_back(pNew);
524  }
525  }
526  }
527  }
528  else if (pAttrEnd != nullptr) // Case: 2,4,5
529  {
530  if (*pAttrEnd > nStt) // Case: 2,4
531  {
532  if (*pAttrEnd < nEnd) // Case: 2
533  {
534  if ( nMin > nAttrStart )
535  nMin = nAttrStart;
536  if ( nMax < *pAttrEnd )
537  nMax = *pAttrEnd;
538  bChanged = true;
539 
540  const sal_Int32 nAttrEnd = *pAttrEnd;
541 
542  m_pSwpHints->NoteInHistory( pHt );
543  // UGLY: this may temporarily destroy the sorting!
544  pHt->SetEnd(nStt);
545  m_pSwpHints->NoteInHistory( pHt, true );
546 
547  if ( pStyleHandle )
548  {
549  SwTextAttr* pNew = MakeTextAttr( GetDoc(),
550  *pStyleHandle, nStt, nAttrEnd );
551  newAttributes.push_back(pNew);
552  }
553  }
554  else if (nLen) // Case: 4
555  {
556  // for Length 0 both hints would be merged again by
557  // InsertHint, so leave them alone!
558  if ( nMin > nAttrStart )
559  nMin = nAttrStart;
560  if ( nMax < *pAttrEnd )
561  nMax = *pAttrEnd;
562  bChanged = true;
563  const sal_Int32 nTmpEnd = *pAttrEnd;
564  m_pSwpHints->NoteInHistory( pHt );
565  // UGLY: this may temporarily destroy the sorting!
566  pHt->SetEnd(nStt);
567  m_pSwpHints->NoteInHistory( pHt, true );
568 
569  if ( pStyleHandle && nStt < nEnd )
570  {
571  SwTextAttr* pNew = MakeTextAttr( GetDoc(),
572  *pStyleHandle, nStt, nEnd );
573  newAttributes.push_back(pNew);
574  }
575 
576  if( nEnd < nTmpEnd )
577  {
578  SwTextAttr* pNew = MakeTextAttr( GetDoc(),
579  pHt->GetAttr(), nEnd, nTmpEnd );
580  if ( pNew )
581  {
582  SwTextCharFormat* pCharFormat = dynamic_cast<SwTextCharFormat*>(pHt);
583  if ( pCharFormat )
584  static_txtattr_cast<SwTextCharFormat*>(pNew)->SetSortNumber(pCharFormat->GetSortNumber());
585 
586  newAttributes.push_back(pNew);
587  }
588  }
589  }
590  }
591  }
592  ++i;
593  }
594 
595  if (bExactRange)
596  {
597  // Only delete the hints which start at nStt and end at nEnd.
598  for (i = 0; i < m_pSwpHints->Count(); ++i)
599  {
600  SwTextAttr* pHint = m_pSwpHints->Get(i);
601  if ( (isTXTATR_WITHEND(pHint->Which()) && RES_TXTATR_AUTOFMT != pHint->Which())
602  || pHint->GetStart() != nStt)
603  continue;
604 
605  const sal_Int32* pHintEnd = pHint->GetEnd();
606  if (!pHintEnd || *pHintEnd != nEnd)
607  continue;
608 
609  delAttributes.push_back(pHint);
610  }
611  }
612 
613  if (bChanged && !delAttributes.empty())
614  { // Delete() calls GetStartOf() - requires sorted hints!
615  m_pSwpHints->Resort();
616  }
617 
618  // delay deleting the hints because it re-sorts the hints array
619  for (SwTextAttr *const pDel : delAttributes)
620  {
621  m_pSwpHints->Delete(pDel);
622  DestroyAttr(pDel);
623  }
624 
625  // delay inserting the hints because it re-sorts the hints array
626  for (SwTextAttr *const pNew : newAttributes)
627  {
629  }
630 
632 
633  if (!bChanged)
634  return;
635 
636  if ( HasHints() )
637  { // possibly sometimes Resort would be sufficient, but...
638  m_pSwpHints->MergePortions(*this);
639  }
640 
641  // TextFrame's respond to aHint, others to aNew
642  SwUpdateAttr aHint(
643  nMin,
644  nMax,
645  0);
646 
647  CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
648  SwFormatChg aNew( GetFormatColl() );
649  CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aNew));
650 }
651 
652 static sal_Int32 clipIndexBounds(const OUString &rStr, sal_Int32 nPos)
653 {
654  if (nPos < 0)
655  return 0;
656  if (nPos > rStr.getLength())
657  return rStr.getLength();
658  return nPos;
659 }
660 
661 // Return current word:
662 // Search from left to right, so find the word before nPos.
663 // Except if at the start of the paragraph, then return the first word.
664 // If the first word consists only of whitespace, return an empty string.
665 OUString SwTextFrame::GetCurWord(SwPosition const& rPos) const
666 {
668  SwTextNode *const pTextNode(rPos.nNode.GetNode().GetTextNode());
669  assert(pTextNode);
670  OUString const& rText(GetText());
671  assert(sal_Int32(nPos) <= rText.getLength()); // invalid index
672 
673  if (rText.isEmpty() || IsHiddenNow())
674  return OUString();
675 
676  assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
677  const uno::Reference< XBreakIterator > &rxBreak = g_pBreakIt->GetBreakIter();
678  sal_Int16 nWordType = WordType::DICTIONARY_WORD;
679  lang::Locale aLocale( g_pBreakIt->GetLocale(pTextNode->GetLang(rPos.nContent.GetIndex())) );
680  Boundary aBndry =
681  rxBreak->getWordBoundary(rText, sal_Int32(nPos), aLocale, nWordType, true);
682 
683  // if no word was found use previous word (if any)
684  if (aBndry.startPos == aBndry.endPos)
685  {
686  aBndry = rxBreak->previousWord(rText, sal_Int32(nPos), aLocale, nWordType);
687  }
688 
689  // check if word was found and if it uses a symbol font, if so
690  // enforce returning an empty string
691  if (aBndry.endPos != aBndry.startPos
692  && IsSymbolAt(TextFrameIndex(aBndry.startPos)))
693  {
694  aBndry.endPos = aBndry.startPos;
695  }
696 
697  // can have -1 as start/end of bounds not found
698  aBndry.startPos = clipIndexBounds(rText, aBndry.startPos);
699  aBndry.endPos = clipIndexBounds(rText, aBndry.endPos);
700 
701  return rText.copy(aBndry.startPos,
702  aBndry.endPos - aBndry.startPos);
703 }
704 
705 SwScanner::SwScanner( const SwTextNode& rNd, const OUString& rText,
706  const LanguageType* pLang, const ModelToViewHelper& rConvMap,
707  sal_uInt16 nType, sal_Int32 nStart, sal_Int32 nEnd, bool bClp )
708  : SwScanner(
709  [&rNd](sal_Int32 const nBegin, sal_uInt16 const nScript, bool const bNoChar)
710  { return rNd.GetLang(nBegin, bNoChar ? 0 : 1, nScript); }
711  , rText, pLang, rConvMap, nType, nStart, nEnd, bClp)
712 {
713 }
714 
715 SwScanner::SwScanner(std::function<LanguageType(sal_Int32, sal_Int32, bool)> const& pGetLangOfChar,
716  const OUString& rText, const LanguageType* pLang,
717  const ModelToViewHelper& rConvMap, sal_uInt16 nType, sal_Int32 nStart,
718  sal_Int32 nEnd, bool bClp)
719  : m_pGetLangOfChar(pGetLangOfChar)
720  , m_aPreDashReplacementText(rText)
721  , m_pLanguage(pLang)
722  , m_ModelToView(rConvMap)
723  , m_nLength(0)
724  , m_nOverriddenDashCount(0)
725  , m_nWordType(nType)
726  , m_bClip(bClp)
727 {
728  m_nStartPos = m_nBegin = nStart;
729  m_nEndPos = nEnd;
730 
731  //MSWord f.e has special emdash and endash behaviour in that they break
732  //words for the purposes of word counting, while a hyphen etc. doesn't.
733 
734  //The default configuration treats emdash/endash as a word break, but
735  //additional ones can be added in under tools->options
736  if (m_nWordType == i18n::WordType::WORD_COUNT)
737  {
739  OUStringBuffer aBuf(m_aPreDashReplacementText);
740  for (sal_Int32 i = m_nStartPos; i < m_nEndPos; ++i)
741  {
742  if (i < 0)
743  continue;
744  sal_Unicode cChar = aBuf[i];
745  if (sDashes.indexOf(cChar) != -1)
746  {
747  aBuf[i] = ' ';
749  }
750  }
751  m_aText = aBuf.makeStringAndClear();
752  }
753  else
755 
756  assert(m_aPreDashReplacementText.getLength() == m_aText.getLength());
757 
758  if ( m_pLanguage )
759  {
761  }
762  else
763  {
764  ModelToViewHelper::ModelPosition aModelBeginPos =
766  m_aCurrentLang = m_pGetLangOfChar(aModelBeginPos.mnPos, 0, true);
767  }
768 }
769 
770 namespace
771 {
772  //fdo#45271 for Asian words count characters instead of words
773  sal_Int32 forceEachAsianCodePointToWord(const OUString &rText, sal_Int32 nBegin, sal_Int32 nLen)
774  {
775  if (nLen > 1)
776  {
777  const uno::Reference< XBreakIterator > &rxBreak = g_pBreakIt->GetBreakIter();
778 
779  sal_uInt16 nCurrScript = rxBreak->getScriptType( rText, nBegin );
780 
781  sal_Int32 indexUtf16 = nBegin;
782  rText.iterateCodePoints(&indexUtf16);
783 
784  //First character is Asian, consider it a word :-(
785  if (nCurrScript == i18n::ScriptType::ASIAN)
786  {
787  nLen = indexUtf16 - nBegin;
788  return nLen;
789  }
790 
791  //First character was not Asian, consider appearance of any Asian character
792  //to be the end of the word
793  while (indexUtf16 < nBegin + nLen)
794  {
795  nCurrScript = rxBreak->getScriptType( rText, indexUtf16 );
796  if (nCurrScript == i18n::ScriptType::ASIAN)
797  {
798  nLen = indexUtf16 - nBegin;
799  return nLen;
800  }
801  rText.iterateCodePoints(&indexUtf16);
802  }
803  }
804  return nLen;
805  }
806 }
807 
809 {
811  Boundary aBound;
812 
813  const CharClass* pCC = &GetAppCharClass();
814  std::optional<CharClass> xLocalCharClass;
815 
816  while ( true )
817  {
818  // skip non-letter characters:
819  while (m_nBegin < m_aText.getLength())
820  {
821  if (m_nBegin >= 0 && !u_isspace(m_aText[m_nBegin]))
822  {
823  if ( !m_pLanguage )
824  {
825  const sal_uInt16 nNextScriptType = g_pBreakIt->GetBreakIter()->getScriptType( m_aText, m_nBegin );
826  ModelToViewHelper::ModelPosition aModelBeginPos =
828  m_aCurrentLang = m_pGetLangOfChar(aModelBeginPos.mnPos, nNextScriptType, false);
829  }
830 
831  if ( m_nWordType != i18n::WordType::WORD_COUNT )
832  {
833  xLocalCharClass.emplace(LanguageTag( g_pBreakIt->GetLocale( m_aCurrentLang ) ));
834  pCC = &*xLocalCharClass;
835  if ( pCC->isLetterNumeric(OUString(m_aText[m_nBegin])) )
836  break;
837  }
838  else
839  break;
840  }
841  ++m_nBegin;
842  }
843 
844  if ( m_nBegin >= m_aText.getLength() || m_nBegin >= m_nEndPos )
845  return false;
846 
847  // get the word boundaries
848  aBound = g_pBreakIt->GetBreakIter()->getWordBoundary( m_aText, m_nBegin,
850  OSL_ENSURE( aBound.endPos >= aBound.startPos, "broken aBound result" );
851 
852  // we don't want to include preceding text
853  // to count words in text with mixed script punctuation correctly,
854  // but we want to include preceding symbols (eg. percent sign, section sign,
855  // degree sign defined by dict_word_hu to spell check their affixed forms).
856  if (m_nWordType == i18n::WordType::WORD_COUNT && aBound.startPos < m_nBegin)
857  aBound.startPos = m_nBegin;
858 
859  //no word boundaries could be found
860  if(aBound.endPos == aBound.startPos)
861  return false;
862 
863  //if a word before is found it has to be searched for the next
864  if(aBound.endPos == m_nBegin)
865  ++m_nBegin;
866  else
867  break;
868  } // end while( true )
869 
870  // #i89042, as discussed with HDU: don't evaluate script changes for word count. Use whole word.
871  if ( m_nWordType == i18n::WordType::WORD_COUNT )
872  {
873  m_nBegin = std::max(aBound.startPos, m_nBegin);
874  m_nLength = 0;
875  if (aBound.endPos > m_nBegin)
876  m_nLength = aBound.endPos - m_nBegin;
877  }
878  else
879  {
880  // we have to differentiate between these cases:
881  if ( aBound.startPos <= m_nBegin )
882  {
883  OSL_ENSURE( aBound.endPos >= m_nBegin, "Unexpected aBound result" );
884 
885  // restrict boundaries to script boundaries and nEndPos
886  const sal_uInt16 nCurrScript = g_pBreakIt->GetBreakIter()->getScriptType( m_aText, m_nBegin );
887  OUString aTmpWord = m_aText.copy( m_nBegin, aBound.endPos - m_nBegin );
888  const sal_Int32 nScriptEnd = m_nBegin +
889  g_pBreakIt->GetBreakIter()->endOfScript( aTmpWord, 0, nCurrScript );
890  const sal_Int32 nEnd = std::min( aBound.endPos, nScriptEnd );
891 
892  // restrict word start to last script change position
893  sal_Int32 nScriptBegin = 0;
894  if ( aBound.startPos < m_nBegin )
895  {
896  // search from nBegin backwards until the next script change
897  aTmpWord = m_aText.copy( aBound.startPos,
898  m_nBegin - aBound.startPos + 1 );
899  nScriptBegin = aBound.startPos +
900  g_pBreakIt->GetBreakIter()->beginOfScript( aTmpWord, m_nBegin - aBound.startPos,
901  nCurrScript );
902  }
903 
904  m_nBegin = std::max( aBound.startPos, nScriptBegin );
905  m_nLength = nEnd - m_nBegin;
906  }
907  else
908  {
909  const sal_uInt16 nCurrScript = g_pBreakIt->GetBreakIter()->getScriptType( m_aText, aBound.startPos );
910  OUString aTmpWord = m_aText.copy( aBound.startPos,
911  aBound.endPos - aBound.startPos );
912  const sal_Int32 nScriptEnd = aBound.startPos +
913  g_pBreakIt->GetBreakIter()->endOfScript( aTmpWord, 0, nCurrScript );
914  const sal_Int32 nEnd = std::min( aBound.endPos, nScriptEnd );
915  m_nBegin = aBound.startPos;
916  m_nLength = nEnd - m_nBegin;
917  }
918  }
919 
920  // optionally clip the result of getWordBoundaries:
921  if ( m_bClip )
922  {
923  aBound.startPos = std::max( aBound.startPos, m_nStartPos );
924  aBound.endPos = std::min( aBound.endPos, m_nEndPos );
925  if (aBound.endPos < aBound.startPos)
926  {
928  m_nLength = 0; // found word is outside of search interval
929  }
930  else
931  {
932  m_nBegin = aBound.startPos;
933  m_nLength = aBound.endPos - m_nBegin;
934  }
935  }
936 
937  if( ! m_nLength )
938  return false;
939 
940  if ( m_nWordType == i18n::WordType::WORD_COUNT )
941  m_nLength = forceEachAsianCodePointToWord(m_aText, m_nBegin, m_nLength);
942 
943  m_aWord = m_aPreDashReplacementText.copy( m_nBegin, m_nLength );
944 
945  return true;
946 }
947 
948 // Note: this is a clone of SwTextFrame::AutoSpell_, so keep them in sync when fixing things!
950 {
951  // modify string according to redline information and hidden text
952  const OUString aOldText( m_Text );
953  OUStringBuffer buf(m_Text);
954  const bool bContainsComments = lcl_HasComments(*this);
955  const bool bRestoreString =
956  lcl_MaskRedlinesAndHiddenText(*this, buf, 0, m_Text.getLength());
957  if (bRestoreString)
958  { // ??? UGLY: is it really necessary to modify m_Text here?
959  m_Text = buf.makeStringAndClear();
960  }
961 
962  sal_Int32 nBegin = ( pArgs->pStartNode != this )
963  ? 0
964  : pArgs->pStartIdx->GetIndex();
965 
966  sal_Int32 nEnd = ( pArgs->pEndNode != this )
967  ? m_Text.getLength()
968  : pArgs->pEndIdx->GetIndex();
969 
970  pArgs->xSpellAlt = nullptr;
971 
972  // 4 cases:
973 
974  // 1. IsWrongDirty = 0 and GetWrong = 0
975  // Everything is checked and correct
976  // 2. IsWrongDirty = 0 and GetWrong = 1
977  // Everything is checked and errors are identified in the wrong list
978  // 3. IsWrongDirty = 1 and GetWrong = 0
979  // Nothing has been checked
980  // 4. IsWrongDirty = 1 and GetWrong = 1
981  // Text has been checked but there is an invalid range in the wrong list
982 
983  // Nothing has to be done for case 1.
984  if ( ( IsWrongDirty() || GetWrong() ) && m_Text.getLength() )
985  {
986  if (nBegin > m_Text.getLength())
987  {
988  nBegin = m_Text.getLength();
989  }
990  if (nEnd > m_Text.getLength())
991  {
992  nEnd = m_Text.getLength();
993  }
994 
995  if(!IsWrongDirty())
996  {
997  const sal_Int32 nTemp = GetWrong()->NextWrong( nBegin );
998  if(nTemp > nEnd)
999  {
1000  // reset original text
1001  if ( bRestoreString )
1002  {
1003  m_Text = aOldText;
1004  }
1005  return false;
1006  }
1007  if(nTemp > nBegin)
1008  nBegin = nTemp;
1009 
1010  }
1011 
1012  // In case 2. we pass the wrong list to the scanned, because only
1013  // the words in the wrong list have to be checked
1014  SwScanner aScanner( *this, m_Text, nullptr, ModelToViewHelper(),
1015  WordType::DICTIONARY_WORD,
1016  nBegin, nEnd );
1017  while( !pArgs->xSpellAlt.is() && aScanner.NextWord() )
1018  {
1019  const OUString& rWord = aScanner.GetWord();
1020 
1021  // get next language for next word, consider language attributes
1022  // within the word
1023  LanguageType eActLang = aScanner.GetCurrentLanguage();
1024  DetectAndMarkMissingDictionaries( GetTextNode()->GetDoc(), pArgs->xSpeller, eActLang );
1025 
1026  if( rWord.getLength() > 0 && LANGUAGE_NONE != eActLang )
1027  {
1028  if (pArgs->xSpeller.is())
1029  {
1030  SvxSpellWrapper::CheckSpellLang( pArgs->xSpeller, eActLang );
1031  pArgs->xSpellAlt = pArgs->xSpeller->spell( rWord, static_cast<sal_uInt16>(eActLang),
1032  Sequence< PropertyValue >() );
1033  }
1034  if( pArgs->xSpellAlt.is() )
1035  {
1036  if ( IsSymbolAt(aScanner.GetBegin()) ||
1037  // redlines can leave "in word" character within word,
1038  // we must remove them before spell checking
1039  // to avoid false alarm
1040  ( (bRestoreString || bContainsComments) && pArgs->xSpeller->isValid( rWord.replaceAll(OUStringChar(CH_TXTATR_INWORD), ""),
1041  static_cast<sal_uInt16>(eActLang), Sequence< PropertyValue >() ) ) )
1042  {
1043  pArgs->xSpellAlt = nullptr;
1044  }
1045  else
1046  {
1047  // make sure the selection build later from the data
1048  // below does not include "in word" character to the
1049  // left and right in order to preserve those. Therefore
1050  // count those "in words" in order to modify the
1051  // selection accordingly.
1052  const sal_Unicode* pChar = rWord.getStr();
1053  sal_Int32 nLeft = 0;
1054  while (*pChar++ == CH_TXTATR_INWORD)
1055  ++nLeft;
1056  pChar = rWord.getLength() ? rWord.getStr() + rWord.getLength() - 1 : nullptr;
1057  sal_Int32 nRight = 0;
1058  while (pChar && *pChar-- == CH_TXTATR_INWORD)
1059  ++nRight;
1060 
1061  pArgs->pStartNode = this;
1062  pArgs->pEndNode = this;
1063  pArgs->pStartIdx->Assign(this, aScanner.GetEnd() - nRight );
1064  pArgs->pEndIdx->Assign(this, aScanner.GetBegin() + nLeft );
1065  }
1066  }
1067  }
1068  }
1069  }
1070 
1071  // reset original text
1072  if ( bRestoreString )
1073  {
1074  m_Text = aOldText;
1075  }
1076 
1077  return pArgs->xSpellAlt.is();
1078 }
1079 
1081  LanguageType nLang, sal_uInt16 nLangWhichId,
1082  const vcl::Font *pFont, sal_uInt16 nFontWhichId )
1083 {
1084  SwEditShell *pEditShell = GetDoc().GetEditShell();
1085  SfxItemSet aSet(pEditShell->GetAttrPool(), nLangWhichId, nLangWhichId );
1086  if (pFont)
1087  aSet.MergeRange(nFontWhichId, nFontWhichId); // Keep it sorted
1088  aSet.Put( SvxLanguageItem( nLang, nLangWhichId ) );
1089 
1090  OSL_ENSURE( pFont, "target font missing?" );
1091  if (pFont)
1092  {
1093  SvxFontItem aFontItem = static_cast<const SvxFontItem&>( aSet.Get( nFontWhichId ) );
1094  aFontItem.SetFamilyName( pFont->GetFamilyName());
1095  aFontItem.SetFamily( pFont->GetFamilyType());
1096  aFontItem.SetStyleName( pFont->GetStyleName());
1097  aFontItem.SetPitch( pFont->GetPitch());
1098  aFontItem.SetCharSet( pFont->GetCharSet() );
1099  aSet.Put( aFontItem );
1100  }
1101 
1103  // SetAttr( aSet ); <- Does not set language attribute of empty paragraphs correctly,
1104  // <- because since there is no selection the flag to garbage
1105  // <- collect all attributes is set, and therefore attributes spanned
1106  // <- over empty selection are removed.
1107 
1108 }
1109 
1111 {
1112  // get range of text within node to be converted
1113  // (either all the text or the text within the selection
1114  // when the conversion was started)
1115  const sal_Int32 nTextBegin = ( rArgs.pStartNode == this )
1116  ? std::min(rArgs.pStartIdx->GetIndex(), m_Text.getLength())
1117  : 0;
1118 
1119  const sal_Int32 nTextEnd = ( rArgs.pEndNode == this )
1120  ? std::min(rArgs.pEndIdx->GetIndex(), m_Text.getLength())
1121  : m_Text.getLength();
1122 
1123  rArgs.aConvText.clear();
1124 
1125  // modify string according to redline information and hidden text
1126  const OUString aOldText( m_Text );
1127  OUStringBuffer buf(m_Text);
1128  const bool bRestoreString =
1129  lcl_MaskRedlinesAndHiddenText(*this, buf, 0, m_Text.getLength());
1130  if (bRestoreString)
1131  { // ??? UGLY: is it really necessary to modify m_Text here?
1132  m_Text = buf.makeStringAndClear();
1133  }
1134 
1135  bool bFound = false;
1136  sal_Int32 nBegin = nTextBegin;
1137  sal_Int32 nLen = 0;
1138  LanguageType nLangFound = LANGUAGE_NONE;
1139  if (m_Text.isEmpty())
1140  {
1142  {
1143  // create SwPaM with mark & point spanning empty paragraph
1144  //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different
1145  SwPaM aCurPaM( *this, 0 );
1146 
1147  SetLanguageAndFont( aCurPaM,
1150  }
1151  }
1152  else
1153  {
1154  SwLanguageIterator aIter( *this, nBegin );
1155 
1156  // Implicit changes require setting new attributes, which in turn destroys
1157  // the attribute sequence on that aIter iterates. We store the necessary
1158  // coordinates and apply those changes after iterating through the text.
1159  typedef std::pair<sal_Int32, sal_Int32> ImplicitChangesRange;
1160  std::vector<ImplicitChangesRange> aImplicitChanges;
1161 
1162  // find non zero length text portion of appropriate language
1163  do {
1164  nLangFound = aIter.GetLanguage();
1165  bool bLangOk = (nLangFound == rArgs.nConvSrcLang) ||
1168 
1169  sal_Int32 nChPos = aIter.GetChgPos();
1170  // the position at the end of the paragraph is COMPLETE_STRING and
1171  // thus must be cut to the end of the actual string.
1172  assert(nChPos != -1);
1173  if (nChPos == -1 || nChPos == COMPLETE_STRING)
1174  {
1175  nChPos = m_Text.getLength();
1176  }
1177 
1178  nLen = nChPos - nBegin;
1179  bFound = bLangOk && nLen > 0;
1180  if (!bFound)
1181  {
1182  // create SwPaM with mark & point spanning the attributed text
1183  //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different
1184  SwPaM aCurPaM( *this, nBegin );
1185  aCurPaM.SetMark();
1186  aCurPaM.GetPoint()->nContent = nBegin + nLen;
1187 
1188  // check script type of selected text
1189  SwEditShell *pEditShell = GetDoc().GetEditShell();
1190  pEditShell->Push(); // save current cursor on stack
1191  pEditShell->SetSelection( aCurPaM );
1192  bool bIsAsianScript = (SvtScriptType::ASIAN == pEditShell->GetScriptType());
1193  pEditShell->Pop(SwCursorShell::PopMode::DeleteCurrent); // restore cursor from stack
1194 
1195  if (!bIsAsianScript && rArgs.bAllowImplicitChangesForNotConvertibleText)
1196  {
1197  // Store for later use
1198  aImplicitChanges.emplace_back(nBegin, nBegin+nLen);
1199  }
1200  nBegin = nChPos; // start of next language portion
1201  }
1202  } while (!bFound && aIter.Next()); /* loop while nothing was found and still sth is left to be searched */
1203 
1204  // Apply implicit changes, if any, now that aIter is no longer used
1205  for (const auto& rImplicitChange : aImplicitChanges)
1206  {
1207  SwPaM aPaM( *this, rImplicitChange.first );
1208  aPaM.SetMark();
1209  aPaM.GetPoint()->nContent = rImplicitChange.second;
1211  }
1212 
1213  }
1214 
1215  // keep resulting text within selection / range of text to be converted
1216  if (nBegin < nTextBegin)
1217  nBegin = nTextBegin;
1218  if (nBegin + nLen > nTextEnd)
1219  nLen = nTextEnd - nBegin;
1220  bool bInSelection = nBegin < nTextEnd;
1221 
1222  if (bFound && bInSelection) // convertible text found within selection/range?
1223  {
1224  OSL_ENSURE( !m_Text.isEmpty(), "convertible text portion missing!" );
1225  rArgs.aConvText = m_Text.copy(nBegin, nLen);
1226  rArgs.nConvTextLang = nLangFound;
1227 
1228  // position where to start looking in next iteration (after current ends)
1229  rArgs.pStartNode = this;
1230  rArgs.pStartIdx->Assign(this, nBegin + nLen );
1231  // end position (when we have travelled over the whole document)
1232  rArgs.pEndNode = this;
1233  rArgs.pEndIdx->Assign(this, nBegin );
1234  }
1235 
1236  // restore original text
1237  if ( bRestoreString )
1238  {
1239  m_Text = aOldText;
1240  }
1241 
1242  return !rArgs.aConvText.isEmpty();
1243 }
1244 
1245 // Note: this is a clone of SwTextNode::Spell, so keep them in sync when fixing things!
1246 SwRect SwTextFrame::AutoSpell_(SwTextNode & rNode, sal_Int32 nActPos)
1247 {
1248  SwRect aRect;
1249  assert(sw::FrameContainsNode(*this, rNode.GetIndex()));
1250  SwTextNode *const pNode(&rNode);
1251  if (!nActPos)
1252  nActPos = COMPLETE_STRING;
1253 
1255 
1256  // modify string according to redline information and hidden text
1257  const OUString aOldText( pNode->GetText() );
1258  OUStringBuffer buf(pNode->m_Text);
1259  const bool bContainsComments = lcl_HasComments(rNode);
1260  const bool bRestoreString =
1261  lcl_MaskRedlinesAndHiddenText(*pNode, buf, 0, pNode->GetText().getLength());
1262  if (bRestoreString)
1263  { // ??? UGLY: is it really necessary to modify m_Text here? just for GetLang()?
1264  pNode->m_Text = buf.makeStringAndClear();
1265  }
1266 
1267  // a change of data indicates that at least one word has been modified
1268 
1269  sal_Int32 nBegin = 0;
1270  sal_Int32 nEnd = pNode->GetText().getLength();
1271  sal_Int32 nInsertPos = 0;
1272  sal_Int32 nChgStart = COMPLETE_STRING;
1273  sal_Int32 nChgEnd = 0;
1274  sal_Int32 nInvStart = COMPLETE_STRING;
1275  sal_Int32 nInvEnd = 0;
1276 
1277  const bool bAddAutoCmpl = pNode->IsAutoCompleteWordDirty() &&
1279 
1280  if( pNode->GetWrong() )
1281  {
1282  nBegin = pNode->GetWrong()->GetBeginInv();
1283  if( COMPLETE_STRING != nBegin )
1284  {
1285  nEnd = std::max(pNode->GetWrong()->GetEndInv(), pNode->GetText().getLength());
1286  }
1287 
1288  // get word around nBegin, we start at nBegin - 1
1289  if ( COMPLETE_STRING != nBegin )
1290  {
1291  if ( nBegin )
1292  --nBegin;
1293 
1294  LanguageType eActLang = pNode->GetLang( nBegin );
1295  Boundary aBound =
1296  g_pBreakIt->GetBreakIter()->getWordBoundary( pNode->GetText(), nBegin,
1297  g_pBreakIt->GetLocale( eActLang ),
1298  WordType::DICTIONARY_WORD, true );
1299  nBegin = aBound.startPos;
1300  }
1301 
1302  // get the position in the wrong list
1303  nInsertPos = pNode->GetWrong()->GetWrongPos( nBegin );
1304 
1305  // sometimes we have to skip one entry
1306  if( nInsertPos < pNode->GetWrong()->Count() &&
1307  nBegin == pNode->GetWrong()->Pos( nInsertPos ) +
1308  pNode->GetWrong()->Len( nInsertPos ) )
1309  nInsertPos++;
1310  }
1311 
1312  bool bFresh = nBegin < nEnd;
1313  bool bPending(false);
1314 
1315  if( bFresh )
1316  {
1317  uno::Reference< XSpellChecker1 > xSpell( ::GetSpellChecker() );
1318  SwDoc& rDoc = pNode->GetDoc();
1319 
1320  SwScanner aScanner( *pNode, pNode->GetText(), nullptr, ModelToViewHelper(),
1321  WordType::DICTIONARY_WORD, nBegin, nEnd);
1322 
1323  while( aScanner.NextWord() )
1324  {
1325  const OUString& rWord = aScanner.GetWord();
1326  nBegin = aScanner.GetBegin();
1327  sal_Int32 nLen = aScanner.GetLen();
1328 
1329  // get next language for next word, consider language attributes
1330  // within the word
1331  LanguageType eActLang = aScanner.GetCurrentLanguage();
1332  DetectAndMarkMissingDictionaries( rDoc, xSpell, eActLang );
1333 
1334  bool bSpell = xSpell.is() && xSpell->hasLanguage( static_cast<sal_uInt16>(eActLang) );
1335  if( bSpell && !rWord.isEmpty() )
1336  {
1337  // check for: bAlter => xHyphWord.is()
1338  OSL_ENSURE(!bSpell || xSpell.is(), "NULL pointer");
1339  if( !xSpell->isValid( rWord, static_cast<sal_uInt16>(eActLang), Sequence< PropertyValue >() ) &&
1340  // redlines can leave "in word" character within word,
1341  // we must remove them before spell checking
1342  // to avoid false alarm
1343  ((!bRestoreString && !bContainsComments) || !xSpell->isValid( rWord.replaceAll(OUStringChar(CH_TXTATR_INWORD), ""),
1344  static_cast<sal_uInt16>(eActLang), Sequence< PropertyValue >() ) ) )
1345  {
1346  sal_Int32 nSmartTagStt = nBegin;
1347  sal_Int32 nDummy = 1;
1348  if ( !pNode->GetSmartTags() || !pNode->GetSmartTags()->InWrongWord( nSmartTagStt, nDummy ) )
1349  {
1350  if( !pNode->GetWrong() )
1351  {
1352  pNode->SetWrong( std::make_unique<SwWrongList>( WRONGLIST_SPELL ) );
1353  pNode->GetWrong()->SetInvalid( 0, nEnd );
1354  }
1355  SwWrongList::FreshState const eState(pNode->GetWrong()->Fresh(
1356  nChgStart, nChgEnd, nBegin, nLen, nInsertPos, nActPos));
1357  switch (eState)
1358  {
1360  pNode->GetWrong()->Insert(OUString(), nullptr, nBegin, nLen, nInsertPos++);
1361  break;
1363  bPending = true;
1364  [[fallthrough]]; // to mark as invalid
1366  nInvStart = nBegin;
1367  nInvEnd = nBegin + nLen;
1368  break;
1369  }
1370  }
1371  }
1372  else if( bAddAutoCmpl && rACW.GetMinWordLen() <= rWord.getLength() )
1373  {
1374  // tdf#119695 only add the word if the cursor position is outside the word
1375  // so that the incomplete words are not added as autocomplete candidates
1376  bool bCursorOutsideWord = nActPos > nBegin + nLen || nActPos < nBegin;
1377  if (bCursorOutsideWord)
1378  rACW.InsertWord(rWord, rDoc);
1379  }
1380  }
1381  }
1382  }
1383 
1384  // reset original text
1385  // i63141 before calling GetCharRect(..) with formatting!
1386  if ( bRestoreString )
1387  {
1388  pNode->m_Text = aOldText;
1389  }
1390  if( pNode->GetWrong() )
1391  {
1392  if( bFresh )
1393  pNode->GetWrong()->Fresh( nChgStart, nChgEnd,
1394  nEnd, 0, nInsertPos, nActPos );
1395 
1396  // Calculate repaint area:
1397 
1398  if( nChgStart < nChgEnd )
1399  {
1400  aRect = lcl_CalculateRepaintRect(*this, rNode, nChgStart, nChgEnd);
1401 
1402  // fdo#71558 notify misspelled word to accessibility
1403  SwViewShell* pViewSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
1404  if( pViewSh )
1405  pViewSh->InvalidateAccessibleParaAttrs( *this );
1406  }
1407 
1408  pNode->GetWrong()->SetInvalid( nInvStart, nInvEnd );
1409  pNode->SetWrongDirty(
1410  (COMPLETE_STRING != pNode->GetWrong()->GetBeginInv())
1411  ? (bPending
1415  if( !pNode->GetWrong()->Count() && ! pNode->IsWrongDirty() )
1416  pNode->ClearWrong();
1417  }
1418  else
1420 
1421  if( bAddAutoCmpl )
1422  pNode->SetAutoCompleteWordDirty( false );
1423 
1424  return aRect;
1425 }
1426 
1437 {
1438  SwRect aRet;
1439 
1440  assert(sw::FrameContainsNode(*this, rNode.GetIndex()));
1441  SwTextNode *const pNode = &rNode;
1442  const OUString& rText = pNode->GetText();
1443 
1444  // Iterate over language portions
1445  SmartTagMgr& rSmartTagMgr = SwSmartTagMgr::Get();
1446 
1447  SwWrongList* pSmartTagList = pNode->GetSmartTags();
1448 
1449  sal_Int32 nBegin = 0;
1450  sal_Int32 nEnd = rText.getLength();
1451 
1452  if ( pSmartTagList )
1453  {
1454  if ( pSmartTagList->GetBeginInv() != COMPLETE_STRING )
1455  {
1456  nBegin = pSmartTagList->GetBeginInv();
1457  nEnd = std::min( pSmartTagList->GetEndInv(), rText.getLength() );
1458 
1459  if ( nBegin < nEnd )
1460  {
1461  const LanguageType aCurrLang = pNode->GetLang( nBegin );
1462  const css::lang::Locale aCurrLocale = g_pBreakIt->GetLocale( aCurrLang );
1463  nBegin = g_pBreakIt->GetBreakIter()->beginOfSentence( rText, nBegin, aCurrLocale );
1464  nEnd = g_pBreakIt->GetBreakIter()->endOfSentence(rText, nEnd, aCurrLocale);
1465  if (nEnd > rText.getLength() || nEnd < 0)
1466  nEnd = rText.getLength();
1467  }
1468  }
1469  }
1470 
1471  const sal_uInt16 nNumberOfEntries = pSmartTagList ? pSmartTagList->Count() : 0;
1472  sal_uInt16 nNumberOfRemovedEntries = 0;
1473  sal_uInt16 nNumberOfInsertedEntries = 0;
1474 
1475  // clear smart tag list between nBegin and nEnd:
1476  if ( 0 != nNumberOfEntries )
1477  {
1478  sal_Int32 nChgStart = COMPLETE_STRING;
1479  sal_Int32 nChgEnd = 0;
1480  const sal_uInt16 nCurrentIndex = pSmartTagList->GetWrongPos( nBegin );
1481  pSmartTagList->Fresh( nChgStart, nChgEnd, nBegin, nEnd - nBegin, nCurrentIndex, COMPLETE_STRING );
1482  nNumberOfRemovedEntries = nNumberOfEntries - pSmartTagList->Count();
1483  }
1484 
1485  if ( nBegin < nEnd )
1486  {
1487  // Expand the string:
1488  const ModelToViewHelper aConversionMap(*pNode, getRootFrame() /*TODO - replace or expand fields for smart tags?*/);
1489  const OUString& aExpandText = aConversionMap.getViewText();
1490 
1491  // Ownership ov ConversionMap is passed to SwXTextMarkup object!
1492  uno::Reference<text::XTextMarkup> const xTextMarkup =
1493  new SwXTextMarkup(pNode, aConversionMap);
1494 
1495  css::uno::Reference< css::frame::XController > xController = pNode->GetDoc().GetDocShell()->GetController();
1496 
1497  SwPosition start(*pNode, nBegin);
1498  SwPosition end (*pNode, nEnd);
1499  Reference< css::text::XTextRange > xRange = SwXTextRange::CreateXTextRange(pNode->GetDoc(), start, &end);
1500 
1501  rSmartTagMgr.RecognizeTextRange(xRange, xTextMarkup, xController);
1502 
1503  sal_Int32 nLangBegin = nBegin;
1504  sal_Int32 nLangEnd;
1505 
1506  // smart tag recognition has to be done for each language portion:
1507  SwLanguageIterator aIter( *pNode, nLangBegin );
1508 
1509  do
1510  {
1511  const LanguageType nLang = aIter.GetLanguage();
1512  const css::lang::Locale aLocale = g_pBreakIt->GetLocale( nLang );
1513  nLangEnd = std::min<sal_Int32>( nEnd, aIter.GetChgPos() );
1514 
1515  const sal_Int32 nExpandBegin = aConversionMap.ConvertToViewPosition( nLangBegin );
1516  const sal_Int32 nExpandEnd = aConversionMap.ConvertToViewPosition( nLangEnd );
1517 
1518  rSmartTagMgr.RecognizeString(aExpandText, xTextMarkup, xController, aLocale, nExpandBegin, nExpandEnd - nExpandBegin );
1519 
1520  nLangBegin = nLangEnd;
1521  }
1522  while ( aIter.Next() && nLangEnd < nEnd );
1523 
1524  pSmartTagList = pNode->GetSmartTags();
1525 
1526  const sal_uInt16 nNumberOfEntriesAfterRecognize = pSmartTagList ? pSmartTagList->Count() : 0;
1527  nNumberOfInsertedEntries = nNumberOfEntriesAfterRecognize - ( nNumberOfEntries - nNumberOfRemovedEntries );
1528  }
1529 
1530  if( pSmartTagList )
1531  {
1532  // Update WrongList stuff
1533  pSmartTagList->SetInvalid( COMPLETE_STRING, 0 );
1534  pNode->SetSmartTagDirty( COMPLETE_STRING != pSmartTagList->GetBeginInv() );
1535 
1536  if( !pSmartTagList->Count() && !pNode->IsSmartTagDirty() )
1537  pNode->ClearSmartTags();
1538 
1539  // Calculate repaint area:
1540  if ( nBegin < nEnd && ( 0 != nNumberOfRemovedEntries ||
1541  0 != nNumberOfInsertedEntries ) )
1542  {
1543  aRet = lcl_CalculateRepaintRect(*this, rNode, nBegin, nEnd);
1544  }
1545  }
1546  else
1547  pNode->SetSmartTagDirty( false );
1548 
1549  return aRet;
1550 }
1551 
1552 void SwTextFrame::CollectAutoCmplWrds(SwTextNode & rNode, sal_Int32 nActPos)
1553 {
1554  assert(sw::FrameContainsNode(*this, rNode.GetIndex())); (void) this;
1555  SwTextNode *const pNode(&rNode);
1556  if (!nActPos)
1557  nActPos = COMPLETE_STRING;
1558 
1559  SwDoc& rDoc = pNode->GetDoc();
1561 
1562  sal_Int32 nBegin = 0;
1563  sal_Int32 nEnd = pNode->GetText().getLength();
1564  sal_Int32 nLen;
1565  bool bACWDirty = false;
1566 
1567  if( nBegin < nEnd )
1568  {
1569  int nCnt = 200;
1570  SwScanner aScanner( *pNode, pNode->GetText(), nullptr, ModelToViewHelper(),
1571  WordType::DICTIONARY_WORD, nBegin, nEnd );
1572  while( aScanner.NextWord() )
1573  {
1574  nBegin = aScanner.GetBegin();
1575  nLen = aScanner.GetLen();
1576  if( rACW.GetMinWordLen() <= nLen )
1577  {
1578  const OUString& rWord = aScanner.GetWord();
1579 
1580  if( nActPos < nBegin || ( nBegin + nLen ) < nActPos )
1581  {
1582  if( rACW.GetMinWordLen() <= rWord.getLength() )
1583  rACW.InsertWord( rWord, rDoc );
1584  }
1585  else
1586  bACWDirty = true;
1587  }
1588  if( !--nCnt )
1589  {
1590  // don't wait for TIMER here, so we can finish big paragraphs
1591  if (Application::AnyInput(VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER)))
1592  return;
1593  nCnt = 100;
1594  }
1595  }
1596  }
1597 
1598  if (!bACWDirty)
1599  pNode->SetAutoCompleteWordDirty( false );
1600 }
1601 
1603  SwTextFrame const& rFrame, SwTextNode const& rNode,
1604  SwInterHyphInfo const& rHyphInfo)
1605  : m_nStart(rFrame.MapModelToView(&rNode, rHyphInfo.m_nStart))
1606  , m_nEnd(rFrame.MapModelToView(&rNode, rHyphInfo.m_nEnd))
1607  , m_nWordStart(0)
1608  , m_nWordLen(0)
1609 {
1610 }
1611 
1613  SwTextNode const& rNode, SwInterHyphInfo & o_rHyphInfo)
1614 {
1615  std::pair<SwTextNode const*, sal_Int32> const wordStart(rFrame.MapViewToModel(m_nWordStart));
1616  std::pair<SwTextNode const*, sal_Int32> const wordEnd(rFrame.MapViewToModel(m_nWordStart+m_nWordLen));
1617  if (wordStart.first != &rNode || wordEnd.first != &rNode)
1618  { // not sure if this can happen since nStart/nEnd are in rNode
1619  SAL_WARN("sw.core", "UpdateTextNodeHyphInfo: outside of node");
1620  return;
1621  }
1622  o_rHyphInfo.m_nWordStart = wordStart.second;
1623  o_rHyphInfo.m_nWordLen = wordEnd.second - wordStart.second;
1624  o_rHyphInfo.SetHyphWord(m_xHyphWord);
1625 }
1626 
1629 {
1630  // shortcut: paragraph doesn't have a language set:
1632  && LanguageType(USHRT_MAX) == GetLang(0, m_Text.getLength()))
1633  {
1634  return false;
1635  }
1636 
1638  [&rHyphInf, this]() {
1639  std::pair<Point, bool> tmp;
1640  Point const*const pPoint = rHyphInf.GetCursorPos();
1641  if (pPoint)
1642  {
1643  tmp.first = *pPoint;
1644  tmp.second = true;
1645  }
1646  return static_cast<SwTextFrame*>(this->getLayoutFrame(
1647  this->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
1648  nullptr, pPoint ? &tmp : nullptr));
1649  });
1650  if (!pFrame)
1651  {
1652  // There was a comment here that claimed that the following assertion
1653  // shouldn't exist as it's triggered by "Trennung ueber Sonderbereiche",
1654  // (hyphenation across special sections?), whatever that means.
1655  OSL_ENSURE( pFrame, "!SwTextNode::Hyphenate: can't find any frame" );
1656  return false;
1657  }
1658  SwInterHyphInfoTextFrame aHyphInfo(*pFrame, *this, rHyphInf);
1659 
1660  pFrame = &(pFrame->GetFrameAtOfst( aHyphInfo.m_nStart ));
1661 
1662  while( pFrame )
1663  {
1664  if (pFrame->Hyphenate(aHyphInfo))
1665  {
1666  // The layout is not robust wrt. "direct formatting"
1667  // cf. layact.cxx, SwLayAction::TurboAction_(), if( !pCnt->IsValid() ...
1668  pFrame->SetCompletePaint();
1669  aHyphInfo.UpdateTextNodeHyphInfo(*pFrame, *this, rHyphInf);
1670  return true;
1671  }
1672  pFrame = pFrame->GetFollow();
1673  if( pFrame )
1674  {
1675  aHyphInfo.m_nEnd = aHyphInfo.m_nEnd - (pFrame->GetOffset() - aHyphInfo.m_nStart);
1676  aHyphInfo.m_nStart = pFrame->GetOffset();
1677  }
1678  }
1679  return false;
1680 }
1681 
1682 namespace
1683 {
1684  struct swTransliterationChgData
1685  {
1686  sal_Int32 nStart;
1687  sal_Int32 nLen;
1688  OUString sChanged;
1689  Sequence< sal_Int32 > aOffsets;
1690  };
1691 }
1692 
1693 // change text to Upper/Lower/Hiragana/Katakana/...
1696  sal_Int32 nStt, sal_Int32 nEnd,
1697  SwUndoTransliterate* pUndo, bool bUseRedlining )
1698 {
1699  if (nStt >= nEnd)
1700  return;
1701 
1702  // since we don't use Hiragana/Katakana or half-width/full-width transliterations here
1703  // it is fine to use ANYWORD_IGNOREWHITESPACES. (ANY_WORD btw is broken and will
1704  // occasionally miss words in consecutive sentences). Also with ANYWORD_IGNOREWHITESPACES
1705  // text like 'just-in-time' will be converted to 'Just-In-Time' which seems to be the
1706  // proper thing to do.
1707  const sal_Int16 nWordType = WordType::ANYWORD_IGNOREWHITESPACES;
1708 
1709  // In order to have less trouble with changing text size, e.g. because
1710  // of ligatures or German small sz being resolved, we need to process
1711  // the text replacements from end to start.
1712  // This way the offsets for the yet to be changed words will be
1713  // left unchanged by the already replaced text.
1714  // For this we temporarily save the changes to be done in this vector
1715  std::vector< swTransliterationChgData > aChanges;
1716  swTransliterationChgData aChgData;
1717 
1718  if (rTrans.getType() == TransliterationFlags::TITLE_CASE)
1719  {
1720  // for 'capitalize every word' we need to iterate over each word
1721 
1722  Boundary aSttBndry;
1723  Boundary aEndBndry;
1724  aSttBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
1725  GetText(), nStt,
1726  g_pBreakIt->GetLocale( GetLang( nStt ) ),
1727  nWordType,
1728  true /*prefer forward direction*/);
1729  aEndBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
1730  GetText(), nEnd,
1731  g_pBreakIt->GetLocale( GetLang( nEnd ) ),
1732  nWordType,
1733  false /*prefer backward direction*/);
1734 
1735  // prevent backtracking to the previous word if selection is at word boundary
1736  if (aSttBndry.endPos <= nStt)
1737  {
1738  aSttBndry = g_pBreakIt->GetBreakIter()->nextWord(
1739  GetText(), aSttBndry.endPos,
1740  g_pBreakIt->GetLocale( GetLang( aSttBndry.endPos ) ),
1741  nWordType);
1742  }
1743  // prevent advancing to the next word if selection is at word boundary
1744  if (aEndBndry.startPos >= nEnd)
1745  {
1746  aEndBndry = g_pBreakIt->GetBreakIter()->previousWord(
1747  GetText(), aEndBndry.startPos,
1748  g_pBreakIt->GetLocale( GetLang( aEndBndry.startPos ) ),
1749  nWordType);
1750  }
1751 
1752  Boundary aCurWordBndry( aSttBndry );
1753  while (aCurWordBndry.startPos <= aEndBndry.startPos)
1754  {
1755  nStt = aCurWordBndry.startPos;
1756  nEnd = aCurWordBndry.endPos;
1757  const sal_Int32 nLen = nEnd - nStt;
1758  OSL_ENSURE( nLen > 0, "invalid word length of 0" );
1759 
1760  Sequence <sal_Int32> aOffsets;
1761  OUString const sChgd( rTrans.transliterate(
1762  GetText(), GetLang(nStt), nStt, nLen, &aOffsets) );
1763 
1764  assert(nStt < m_Text.getLength());
1765  if (0 != rtl_ustr_shortenedCompare_WithLength(
1766  m_Text.getStr() + nStt, m_Text.getLength() - nStt,
1767  sChgd.getStr(), sChgd.getLength(), nLen))
1768  {
1769  aChgData.nStart = nStt;
1770  aChgData.nLen = nLen;
1771  aChgData.sChanged = sChgd;
1772  aChgData.aOffsets = aOffsets;
1773  aChanges.push_back( aChgData );
1774  }
1775 
1776  aCurWordBndry = g_pBreakIt->GetBreakIter()->nextWord(
1777  GetText(), nStt,
1778  g_pBreakIt->GetLocale(GetLang(nStt, 1)),
1779  nWordType);
1780  }
1781  }
1782  else if (rTrans.getType() == TransliterationFlags::SENTENCE_CASE)
1783  {
1784  // for 'sentence case' we need to iterate sentence by sentence
1785 
1786  sal_Int32 nLastStart = g_pBreakIt->GetBreakIter()->beginOfSentence(
1787  GetText(), nEnd,
1788  g_pBreakIt->GetLocale( GetLang( nEnd ) ) );
1789  sal_Int32 nLastEnd = g_pBreakIt->GetBreakIter()->endOfSentence(
1790  GetText(), nLastStart,
1791  g_pBreakIt->GetLocale( GetLang( nLastStart ) ) );
1792 
1793  // extend nStt, nEnd to the current sentence boundaries
1794  sal_Int32 nCurrentStart = g_pBreakIt->GetBreakIter()->beginOfSentence(
1795  GetText(), nStt,
1796  g_pBreakIt->GetLocale( GetLang( nStt ) ) );
1797  sal_Int32 nCurrentEnd = g_pBreakIt->GetBreakIter()->endOfSentence(
1798  GetText(), nCurrentStart,
1799  g_pBreakIt->GetLocale( GetLang( nCurrentStart ) ) );
1800 
1801  // prevent backtracking to the previous sentence if selection starts at end of a sentence
1802  if (nCurrentEnd <= nStt)
1803  {
1804  // now nCurrentStart is probably located on a non-letter word. (unless we
1805  // are in Asian text with no spaces...)
1806  // Thus to get the real sentence start we should locate the next real word,
1807  // that is one found by DICTIONARY_WORD
1808  i18n::Boundary aBndry = g_pBreakIt->GetBreakIter()->nextWord(
1809  GetText(), nCurrentEnd,
1810  g_pBreakIt->GetLocale( GetLang( nCurrentEnd ) ),
1811  i18n::WordType::DICTIONARY_WORD);
1812 
1813  // now get new current sentence boundaries
1814  nCurrentStart = g_pBreakIt->GetBreakIter()->beginOfSentence(
1815  GetText(), aBndry.startPos,
1816  g_pBreakIt->GetLocale( GetLang( aBndry.startPos) ) );
1817  nCurrentEnd = g_pBreakIt->GetBreakIter()->endOfSentence(
1818  GetText(), nCurrentStart,
1819  g_pBreakIt->GetLocale( GetLang( nCurrentStart) ) );
1820  }
1821  // prevent advancing to the next sentence if selection ends at start of a sentence
1822  if (nLastStart >= nEnd)
1823  {
1824  // now nCurrentStart is probably located on a non-letter word. (unless we
1825  // are in Asian text with no spaces...)
1826  // Thus to get the real sentence start we should locate the previous real word,
1827  // that is one found by DICTIONARY_WORD
1828  i18n::Boundary aBndry = g_pBreakIt->GetBreakIter()->previousWord(
1829  GetText(), nLastStart,
1830  g_pBreakIt->GetLocale( GetLang( nLastStart) ),
1831  i18n::WordType::DICTIONARY_WORD);
1832  nLastEnd = g_pBreakIt->GetBreakIter()->endOfSentence(
1833  GetText(), aBndry.startPos,
1834  g_pBreakIt->GetLocale( GetLang( aBndry.startPos) ) );
1835  if (nCurrentEnd > nLastEnd)
1836  nCurrentEnd = nLastEnd;
1837  }
1838 
1839  while (nCurrentStart < nLastEnd)
1840  {
1841  sal_Int32 nLen = nCurrentEnd - nCurrentStart;
1842  OSL_ENSURE( nLen > 0, "invalid word length of 0" );
1843 
1844  Sequence <sal_Int32> aOffsets;
1845  OUString const sChgd( rTrans.transliterate(GetText(),
1846  GetLang(nCurrentStart), nCurrentStart, nLen, &aOffsets) );
1847 
1848  assert(nStt < m_Text.getLength());
1849  if (0 != rtl_ustr_shortenedCompare_WithLength(
1850  m_Text.getStr() + nStt, m_Text.getLength() - nStt,
1851  sChgd.getStr(), sChgd.getLength(), nLen))
1852  {
1853  aChgData.nStart = nCurrentStart;
1854  aChgData.nLen = nLen;
1855  aChgData.sChanged = sChgd;
1856  aChgData.aOffsets = aOffsets;
1857  aChanges.push_back( aChgData );
1858  }
1859 
1860  Boundary aFirstWordBndry = g_pBreakIt->GetBreakIter()->nextWord(
1861  GetText(), nCurrentEnd,
1862  g_pBreakIt->GetLocale( GetLang( nCurrentEnd ) ),
1863  nWordType);
1864  nCurrentStart = aFirstWordBndry.startPos;
1865  nCurrentEnd = g_pBreakIt->GetBreakIter()->endOfSentence(
1866  GetText(), nCurrentStart,
1867  g_pBreakIt->GetLocale( GetLang( nCurrentStart ) ) );
1868  }
1869  }
1870  else
1871  {
1872  // here we may transliterate over complete language portions...
1873 
1874  std::unique_ptr<SwLanguageIterator> pIter;
1875  if( rTrans.needLanguageForTheMode() )
1876  pIter.reset(new SwLanguageIterator( *this, nStt ));
1877 
1878  sal_Int32 nEndPos = 0;
1880  do {
1881  if( pIter )
1882  {
1883  nLang = pIter->GetLanguage();
1884  nEndPos = pIter->GetChgPos();
1885  if( nEndPos > nEnd )
1886  nEndPos = nEnd;
1887  }
1888  else
1889  {
1890  nLang = LANGUAGE_SYSTEM;
1891  nEndPos = nEnd;
1892  }
1893  const sal_Int32 nLen = nEndPos - nStt;
1894 
1895  Sequence <sal_Int32> aOffsets;
1896  OUString const sChgd( rTrans.transliterate(
1897  m_Text, nLang, nStt, nLen, &aOffsets) );
1898 
1899  assert(nStt < m_Text.getLength());
1900  if (0 != rtl_ustr_shortenedCompare_WithLength(
1901  m_Text.getStr() + nStt, m_Text.getLength() - nStt,
1902  sChgd.getStr(), sChgd.getLength(), nLen))
1903  {
1904  aChgData.nStart = nStt;
1905  aChgData.nLen = nLen;
1906  aChgData.sChanged = sChgd;
1907  aChgData.aOffsets = aOffsets;
1908  aChanges.push_back( aChgData );
1909  }
1910 
1911  nStt = nEndPos;
1912  } while( nEndPos < nEnd && pIter && pIter->Next() );
1913  }
1914 
1915  if (aChanges.empty())
1916  return;
1917 
1918  // now apply the changes from end to start to leave the offsets of the
1919  // yet unchanged text parts remain the same.
1920  size_t nSum(0);
1921 
1922  for (size_t i = 0; i < aChanges.size(); ++i)
1923  { // check this here since AddChanges cannot be moved below
1924  // call to ReplaceTextOnly
1925  swTransliterationChgData & rData =
1926  aChanges[ aChanges.size() - 1 - i ];
1927  nSum += rData.sChanged.getLength() - rData.nLen;
1928  if (nSum > o3tl::make_unsigned(GetSpaceLeft()))
1929  {
1930  SAL_WARN("sw.core", "SwTextNode::ReplaceTextOnly: "
1931  "node text with insertion > node capacity.");
1932  return;
1933  }
1934 
1935  if ( bUseRedlining )
1936  {
1937  // create SwPaM with mark & point spanning the attributed text
1938  //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different
1939  SwPaM aCurPaM( *this, rData.nStart );
1940  aCurPaM.SetMark();
1941  aCurPaM.GetPoint()->nContent = rData.nStart + rData.nLen;
1942  // replace the changed words
1943  if ( aCurPaM.GetText() != rData.sChanged )
1944  GetDoc().getIDocumentContentOperations().ReplaceRange( aCurPaM, rData.sChanged, false );
1945  }
1946  else
1947  {
1948  if (pUndo)
1949  pUndo->AddChanges( *this, rData.nStart, rData.nLen, rData.aOffsets );
1950  ReplaceTextOnly( rData.nStart, rData.nLen, rData.sChanged, rData.aOffsets );
1951  }
1952  }
1953 }
1954 
1955 void SwTextNode::ReplaceTextOnly( sal_Int32 nPos, sal_Int32 nLen,
1956  const OUString & rText,
1957  const Sequence<sal_Int32>& rOffsets )
1958 {
1959  assert(rText.getLength() - nLen <= GetSpaceLeft());
1960 
1961  m_Text = m_Text.replaceAt(nPos, nLen, rText);
1962 
1963  sal_Int32 nTLen = rText.getLength();
1964  const sal_Int32* pOffsets = rOffsets.getConstArray();
1965  // now look for no 1-1 mapping -> move the indices!
1966  sal_Int32 nMyOff = nPos;
1967  for( sal_Int32 nI = 0; nI < nTLen; ++nI )
1968  {
1969  const sal_Int32 nOff = pOffsets[ nI ];
1970  if( nOff < nMyOff )
1971  {
1972  // something is inserted
1973  sal_Int32 nCnt = 1;
1974  while( nI + nCnt < nTLen && nOff == pOffsets[ nI + nCnt ] )
1975  ++nCnt;
1976 
1977  Update( SwIndex( this, nMyOff ), nCnt );
1978  nMyOff = nOff;
1979  //nMyOff -= nCnt;
1980  nI += nCnt - 1;
1981  }
1982  else if( nOff > nMyOff )
1983  {
1984  // something is deleted
1985  Update( SwIndex( this, nMyOff+1 ), nOff - nMyOff, true );
1986  nMyOff = nOff;
1987  }
1988  ++nMyOff;
1989  }
1990  if( nMyOff < nLen )
1991  // something is deleted at the end
1992  Update( SwIndex( this, nMyOff ), nLen - nMyOff, true );
1993 
1994  // notify the layout!
1995  SwDelText aDelHint( nPos, nTLen );
1996  CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aDelHint));
1997 
1998  SwInsText aHint( nPos, nTLen );
1999  CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
2000 }
2001 
2002 // the return values allows us to see if we did the heavy-
2003 // lifting required to actually break and count the words.
2005  sal_Int32 nStt, sal_Int32 nEnd ) const
2006 {
2007  if( nStt > nEnd )
2008  { // bad call
2009  return false;
2010  }
2011  if (IsInRedlines())
2012  { //not counting txtnodes used to hold deleted redline content
2013  return false;
2014  }
2015  bool bCountAll = ( (0 == nStt) && (GetText().getLength() == nEnd) );
2016  ++rStat.nAllPara; // #i93174#: count _all_ paragraphs
2017  if ( IsHidden() )
2018  { // not counting hidden paras
2019  return false;
2020  }
2021  // count words in numbering string if started at beginning of para:
2022  bool bCountNumbering = nStt == 0;
2023  bool bHasBullet = false, bHasNumbering = false;
2024  OUString sNumString;
2025  if (bCountNumbering)
2026  {
2027  sNumString = GetNumString();
2028  bHasNumbering = !sNumString.isEmpty();
2029  if (!bHasNumbering)
2030  bHasBullet = HasBullet();
2031  bCountNumbering = bHasNumbering || bHasBullet;
2032  }
2033 
2034  if( nStt == nEnd && !bCountNumbering)
2035  { // unnumbered empty node or empty selection
2036  if (bCountAll)
2037  {
2038  SetWordCountDirty( false ); // reset flag to speed up DoIdleJob
2039  }
2040  return false;
2041  }
2042 
2043  // count of non-empty paras
2044  ++rStat.nPara;
2045 
2046  // Shortcut when counting whole paragraph and current count is clean
2047  if ( bCountAll && !IsWordCountDirty() )
2048  {
2049  // accumulate into DocStat record to return the values
2051  {
2056  }
2057  return false;
2058  }
2059 
2060  // ConversionMap to expand fields, remove invisible and redline deleted text for scanner
2061  const ModelToViewHelper aConversionMap(*this,
2062  getIDocumentLayoutAccess().GetCurrentLayout(),
2064  const OUString& aExpandText = aConversionMap.getViewText();
2065 
2066  if (aExpandText.isEmpty() && !bCountNumbering)
2067  {
2068  if (bCountAll)
2069  {
2070  SetWordCountDirty( false ); // reset flag to speed up DoIdleJob
2071  }
2072  return false;
2073  }
2074 
2075  // map start and end points onto the ConversionMap
2076  const sal_Int32 nExpandBegin = aConversionMap.ConvertToViewPosition( nStt );
2077  const sal_Int32 nExpandEnd = aConversionMap.ConvertToViewPosition( nEnd );
2078 
2079  //do the count
2080  // all counts exclude hidden paras and hidden+redlined within para
2081  // definition of space/white chars in SwScanner (and BreakIter!)
2082  // uses both u_isspace and BreakIter getWordBoundary in SwScanner
2083  sal_uInt32 nTmpWords = 0; // count of all words
2084  sal_uInt32 nTmpAsianWords = 0; //count of all Asian codepoints
2085  sal_uInt32 nTmpChars = 0; // count of all chars
2086  sal_uInt32 nTmpCharsExcludingSpaces = 0; // all non-white chars
2087 
2088  // count words in masked and expanded text:
2089  if (!aExpandText.isEmpty())
2090  {
2091  assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
2092 
2093  // zero is NULL for pLanguage -----------v last param = true for clipping
2094  SwScanner aScanner( *this, aExpandText, nullptr, aConversionMap, i18n::WordType::WORD_COUNT,
2095  nExpandBegin, nExpandEnd, true );
2096 
2097  // used to filter out scanner returning almost empty strings (len=1; unichar=0x0001)
2098  const OUString aBreakWord( CH_TXTATR_BREAKWORD );
2099 
2100  while ( aScanner.NextWord() )
2101  {
2102  if( !aExpandText.match(aBreakWord, aScanner.GetBegin() ))
2103  {
2104  ++nTmpWords;
2105  const OUString &rWord = aScanner.GetWord();
2106  if (g_pBreakIt->GetBreakIter()->getScriptType(rWord, 0) == i18n::ScriptType::ASIAN)
2107  ++nTmpAsianWords;
2108  nTmpCharsExcludingSpaces += g_pBreakIt->getGraphemeCount(rWord);
2109  }
2110  }
2111 
2112  nTmpCharsExcludingSpaces += aScanner.getOverriddenDashCount();
2113 
2114  nTmpChars = g_pBreakIt->getGraphemeCount(aExpandText, nExpandBegin, nExpandEnd);
2115  }
2116 
2117  // no nTmpCharsExcludingSpaces adjust needed neither for blanked out MaskedChars
2118  // nor for mid-word selection - set scanner bClip = true at creation
2119 
2120  // count outline number label - ? no expansion into map
2121  // always counts all of number-ish label
2122  if (bHasNumbering) // count words in numbering string
2123  {
2124  LanguageType aLanguage = GetLang( 0 );
2125 
2126  SwScanner aScanner( *this, sNumString, &aLanguage, ModelToViewHelper(),
2127  i18n::WordType::WORD_COUNT, 0, sNumString.getLength(), true );
2128 
2129  while ( aScanner.NextWord() )
2130  {
2131  ++nTmpWords;
2132  const OUString &rWord = aScanner.GetWord();
2133  if (g_pBreakIt->GetBreakIter()->getScriptType(rWord, 0) == i18n::ScriptType::ASIAN)
2134  ++nTmpAsianWords;
2135  nTmpCharsExcludingSpaces += g_pBreakIt->getGraphemeCount(rWord);
2136  }
2137 
2138  nTmpCharsExcludingSpaces += aScanner.getOverriddenDashCount();
2139  nTmpChars += g_pBreakIt->getGraphemeCount(sNumString);
2140  }
2141  else if ( bHasBullet )
2142  {
2143  ++nTmpWords;
2144  ++nTmpChars;
2145  ++nTmpCharsExcludingSpaces;
2146  }
2147 
2148  // If counting the whole para then update cached values and mark clean
2149  if ( bCountAll )
2150  {
2151  if ( m_pParaIdleData_Impl )
2152  {
2153  m_pParaIdleData_Impl->nNumberOfWords = nTmpWords;
2154  m_pParaIdleData_Impl->nNumberOfAsianWords = nTmpAsianWords;
2155  m_pParaIdleData_Impl->nNumberOfChars = nTmpChars;
2156  m_pParaIdleData_Impl->nNumberOfCharsExcludingSpaces = nTmpCharsExcludingSpaces;
2157  }
2158  SetWordCountDirty( false );
2159  }
2160  // accumulate into DocStat record to return the values
2161  rStat.nWord += nTmpWords;
2162  rStat.nAsianWord += nTmpAsianWords;
2163  rStat.nChar += nTmpChars;
2164  rStat.nCharExcludingSpaces += nTmpCharsExcludingSpaces;
2165 
2166  return true;
2167 }
2168 
2169 // Paragraph statistics start -->
2170 
2172 {
2173  if ( bNew )
2174  {
2176  }
2177  else if ( m_pParaIdleData_Impl )
2178  {
2179  m_pParaIdleData_Impl->pWrong.reset();
2182  delete m_pParaIdleData_Impl;
2183  m_pParaIdleData_Impl = nullptr;
2184  }
2185 }
2186 
2187 void SwTextNode::SetWrong( std::unique_ptr<SwWrongList> pNew )
2188 {
2189  if ( m_pParaIdleData_Impl )
2190  m_pParaIdleData_Impl->pWrong = std::move(pNew);
2191 }
2192 
2194 {
2195  if ( m_pParaIdleData_Impl )
2196  m_pParaIdleData_Impl->pWrong.reset();
2197 }
2198 
2199 std::unique_ptr<SwWrongList> SwTextNode::ReleaseWrong()
2200 {
2201  return m_pParaIdleData_Impl ? std::move(m_pParaIdleData_Impl->pWrong) : nullptr;
2202 }
2203 
2205 {
2206  return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong.get() : nullptr;
2207 }
2208 
2209 // #i71360#
2211 {
2212  return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong.get() : nullptr;
2213 }
2214 
2215 void SwTextNode::SetGrammarCheck( std::unique_ptr<SwGrammarMarkUp> pNew )
2216 {
2217  if ( m_pParaIdleData_Impl )
2218  m_pParaIdleData_Impl->pGrammarCheck = std::move(pNew);
2219 }
2220 
2222 {
2223  if ( m_pParaIdleData_Impl )
2225 }
2226 
2227 std::unique_ptr<SwGrammarMarkUp> SwTextNode::ReleaseGrammarCheck()
2228 {
2229  return m_pParaIdleData_Impl ? std::move(m_pParaIdleData_Impl->pGrammarCheck) : nullptr;
2230 }
2231 
2233 {
2234  return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pGrammarCheck.get() : nullptr;
2235 }
2236 
2238 {
2239  return static_cast<SwWrongList const*>(const_cast<SwTextNode*>(this)->GetGrammarCheck());
2240 }
2241 
2242 void SwTextNode::SetSmartTags( std::unique_ptr<SwWrongList> pNew )
2243 {
2244  OSL_ENSURE( !pNew || SwSmartTagMgr::Get().IsSmartTagsEnabled(),
2245  "Weird - we have a smart tag list without any recognizers?" );
2246 
2247  if ( m_pParaIdleData_Impl )
2248  m_pParaIdleData_Impl->pSmartTags = std::move(pNew);
2249 }
2250 
2252 {
2253  if ( m_pParaIdleData_Impl )
2255 }
2256 
2257 std::unique_ptr<SwWrongList> SwTextNode::ReleaseSmartTags()
2258 {
2259  return m_pParaIdleData_Impl ? std::move(m_pParaIdleData_Impl->pSmartTags) : nullptr;
2260 }
2261 
2263 {
2264  return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pSmartTags.get() : nullptr;
2265 }
2266 
2268 {
2269  return const_cast<SwWrongList const*>(const_cast<SwTextNode*>(this)->GetSmartTags());
2270 }
2271 
2272 void SwTextNode::SetWordCountDirty( bool bNew ) const
2273 {
2274  if ( m_pParaIdleData_Impl )
2275  {
2277  }
2278 }
2279 
2281 {
2283 }
2284 
2286 {
2287  if ( m_pParaIdleData_Impl )
2288  {
2290  }
2291 }
2292 
2294 {
2296 }
2297 
2299 {
2301 }
2302 
2303 void SwTextNode::SetGrammarCheckDirty( bool bNew ) const
2304 {
2305  if ( m_pParaIdleData_Impl )
2306  {
2308  }
2309 }
2310 
2312 {
2314 }
2315 
2316 void SwTextNode::SetSmartTagDirty( bool bNew ) const
2317 {
2318  if ( m_pParaIdleData_Impl )
2319  {
2321  }
2322 }
2323 
2325 {
2327 }
2328 
2330 {
2331  if ( m_pParaIdleData_Impl )
2332  {
2334  }
2335 }
2336 
2338 {
2340 }
2341 
2342 // <-- Paragraph statistics end
2343 
2344 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
void SetWordCountDirty(bool bNew) const
Definition: txtedt.cxx:2272
sal_Int32 Pos(sal_uInt16 nIdx) const
Definition: wrong.hxx:319
void SetTop(SwRect &rRect, tools::Long nNew) const
Definition: frame.hxx:1387
void SetCharSet(rtl_TextEncoding _eEncoding)
const sal_Int32 m_nLength
OUString m_aWord
Definition: swscanner.hxx:35
Represents the visualization of a paragraph.
Definition: txtfrm.hxx:158
virtual std::shared_ptr< SfxItemSet > getAutomaticStyle(const SfxItemSet &rSet, SwAutoStyleFamily eFamily, const OUString *pParentName=nullptr)=0
#define LANGUAGE_NONE
bool bAllowImplicitChangesForNotConvertibleText
Definition: splargs.hxx:72
tools::Long GetRight(const SwRect &rRect) const
Definition: frame.hxx:1381
sal_uLong GetIndex() const
Definition: node.hxx:291
bool InsertWord(const OUString &rWord, SwDoc &rDoc)
Definition: acmplwrd.cxx:219
SwInterHyphInfoTextFrame(SwTextFrame const &rFrame, SwTextNode const &rNode, SwInterHyphInfo const &rHyphInfo)
Definition: txtedt.cxx:1602
bool HasBullet() const
Returns if this text node has a bullet.
Definition: ndtxt.cxx:3065
void SetStyleName(const OUString &rStyleName)
void SetWrong(std::unique_ptr< SwWrongList > pNew)
Definition: txtedt.cxx:2187
SwScanner(const std::function< LanguageType(sal_Int32, sal_Int32, bool)> &pGetLangOfChar, const OUString &rText, const LanguageType *pLang, const ModelToViewHelper &rConvMap, sal_uInt16 nWordType, sal_Int32 nStart, sal_Int32 nEnd, bool bClip=false)
Marks a position in the document model.
Definition: pam.hxx:35
const OUString & GetFamilyName() const
constexpr TypedWhichId< SwFormatMeta > RES_TXTATR_METAFIELD(49)
sal_uLong nPara
paragraphs for document statistic: non-empty and non-hidden ones
Definition: docstat.hxx:32
const OUString & GetText() const
Definition: ndtxt.hxx:218
virtual bool get(DocumentSettingId id) const override
Return the specified document setting.
static SwAutoCompleteWord & GetAutoCompleteWords()
Definition: doc.hxx:1536
static SwRect lcl_CalculateRepaintRect(const SwTextFrame &rTextFrame, const SwTextNode &rNode, sal_Int32 const nChgStart, sal_Int32 const nChgEnd)
Used for spell checking.
Definition: txtedt.cxx:223
std::unique_ptr< SwGrammarMarkUp > pGrammarCheck
Definition: txtedt.cxx:103
bool IsWrongDirty() const
Definition: txtedt.cxx:2298
SwDocShell * GetDocShell()
Definition: doc.hxx:1352
SwpHints * GetpSwpHints()
Definition: ndtxt.hxx:226
const OUString & GetStyleName() const
bool isTXTATR_WITHEND(const sal_uInt16 nWhich)
Definition: hintids.hxx:480
FreshState Fresh(sal_Int32 &rStart, sal_Int32 &rEnd, sal_Int32 nPos, sal_Int32 nLen, sal_uInt16 nIndex, sal_Int32 nCursorPos)
Remove given range of entries.
Definition: wrong.cxx:355
void InvalidateAccessibleParaAttrs(const SwTextFrame &rTextFrame)
invalidate attributes for paragraphs and paragraph's characters
Definition: viewsh.cxx:2557
constexpr TypedWhichId< SwFormatMeta > RES_TXTATR_META(48)
SwNodeIndex nNode
Definition: pam.hxx:37
void SetPitch(FontPitch _ePitch)
void SetCompletePaint() const
Definition: frame.hxx:995
virtual void InsertItemSet(const SwPaM &rRg, const SfxItemSet &, const SetAttrMode nFlags=SetAttrMode::DEFAULT, SwRootFrame const *pLayout=nullptr)=0
sal_Int32 GetSpaceLeft() const
Definition: ndtxt.hxx:873
virtual const sal_Int32 * GetEnd() const
end position
Definition: txatbase.cxx:48
sal_uIntPtr sal_uLong
void UpdateTextNodeHyphInfo(SwTextFrame const &rFrame, SwTextNode const &rNode, SwInterHyphInfo &o_rHyphInfo)
Definition: txtedt.cxx:1612
bool Spell(SwSpellArgs *)
Definition: txtedt.cxx:949
css::uno::Reference< css::linguistic2::XSpellChecker1 > xSpeller
Definition: splargs.hxx:88
sal_uInt16 m_nWordType
Definition: swscanner.hxx:46
Implementation of the css::text::XTextMarkup interface.
void SetStart(sal_Int32 n)
start position
Definition: txatbase.hxx:85
LanguageType GetLanguage(SfxItemSet const &aSet, sal_uInt16 nLangWhichId)
Definition: langhelper.cxx:390
static bool lcl_MaskRedlinesAndHiddenText(const SwTextNode &rNode, OUStringBuffer &rText, sal_Int32 nStt, sal_Int32 nEnd, const sal_Unicode cChar=CH_TXTATR_INWORD)
Used for spell checking.
Definition: txtedt.cxx:190
void ClearGrammarCheck()
Definition: txtedt.cxx:2221
static bool IsAutoCompleteWords()
Definition: viewopt.cxx:333
SwContentFrame * getLayoutFrame(const SwRootFrame *, const SwPosition *pPos=nullptr, std::pair< Point, bool > const *pViewPosAndCalcFrame=nullptr) const
Definition: node.cxx:1213
Definition: doc.hxx:188
TransliterationFlags getType() const
bool HasFollow() const
Definition: flowfrm.hxx:165
SAL_DLLPRIVATE void SetLanguageAndFont(const SwPaM &rPaM, LanguageType nLang, sal_uInt16 nLangWhichId, const vcl::Font *pFont, sal_uInt16 nFontWhichId)
Definition: txtedt.cxx:1080
aBuf
void SetFamily(FontFamily _eFamily)
sal_uLong nChar
Definition: docstat.hxx:37
LanguageType GetCurrentLanguage() const
Definition: swscanner.hxx:70
std::unique_ptr< SwGrammarMarkUp > ReleaseGrammarCheck()
Definition: txtedt.cxx:2227
const ContentProperties & rData
SwNode & GetNode() const
Definition: ndindex.hxx:119
void TryDeleteSwpHints()
Definition: ndtxt.hxx:842
bool IsSmartTagDirty() const
Definition: txtedt.cxx:2324
sal_uLong nNumberOfWords
Definition: txtedt.cxx:105
This struct defines a position in the model string.
Of course Writer needs its own rectangles.
Definition: swrect.hxx:34
sal_uInt16 Which() const
Definition: txatbase.hxx:114
sal_Int32 m_nStartPos
Definition: swscanner.hxx:40
const OUString & GetText() const
Returns the text portion we want to edit (for inline see underneath)
Definition: txtfrm.cxx:1294
SwTextAttr * GetTextAttrAt(sal_Int32 const nIndex, sal_uInt16 const nWhich, enum GetTextAttrMode const eMode=DEFAULT) const
get the innermost text attribute covering position nIndex.
Definition: ndtxt.cxx:1691
const vcl::Font * pTargetFont
Definition: splargs.hxx:70
IDocumentContentOperations const & getIDocumentContentOperations() const
Definition: doc.cxx:315
void SetGrammarCheckDirty(bool bNew) const
Definition: txtedt.cxx:2303
void CalcStartEnd(sal_uLong nNdIdx, sal_Int32 &rStart, sal_Int32 &rEnd) const
Calculates the intersection with text node number nNdIdx.
Definition: docredln.cxx:1333
constexpr TypedWhichId< SwFormatField > RES_TXTATR_ANNOTATION(59)
sal_uLong nCharExcludingSpaces
Definition: docstat.hxx:38
const Point * GetCursorPos() const
Definition: splargs.hxx:131
std::unique_ptr< SwWrongList > ReleaseSmartTags()
Definition: txtedt.cxx:2257
sal_Int32 m_nWordStart
output: found word
Definition: splargs.hxx:121
FontFamily GetFamilyType()
VclInputFlags
static bool IsShowChanges(const RedlineFlags eM)
bool IsSymbolAt(sal_Int32 nBegin) const
in ndcopy.cxx
Definition: itratr.cxx:853
static LanguageType nLang
Definition: srtdlg.cxx:51
void SetFamilyName(const OUString &rFamilyName)
bool Hyphenate(SwInterHyphInfoTextFrame &rInf)
We format a Line for interactive hyphenation.
Definition: txthyph.cxx:58
size_type size() const
Definition: docary.hxx:265
SwTextAttr * GetTextAttrForCharAt(const sal_Int32 nIndex, const sal_uInt16 nWhich=RES_TXTATR_END) const
get the text attribute at position nIndex which owns the dummy character CH_TXTATR_* at that position...
Definition: ndtxt.cxx:3012
bool IsHiddenNow() const
Hidden.
Definition: txtfrm.cxx:1361
tools::Long GetTop(const SwRect &rRect) const
Definition: frame.hxx:1378
OUString GetCurWord(SwPosition const &) const
Definition: txtedt.cxx:665
#define CH_TXTATR_BREAKWORD
Definition: hintids.hxx:170
SwRect GetPaintArea() const
|* The paintarea is the area, in which the content of a frame is allowed |* to be displayed...
Definition: ssfrm.cxx:589
sal_Int32 getGraphemeCount(const OUString &rStr, sal_Int32 nStart, sal_Int32 nEnd) const
Definition: breakit.cxx:157
bool isLetterNumeric(const OUString &rStr, sal_Int32 nPos) const
sal_uInt16 sal_Unicode
static sal_Int32 MaskHiddenRanges(const SwTextNode &rNode, OUStringBuffer &rText, const sal_Int32 nStt, const sal_Int32 nEnd, const sal_Unicode cChar)
Hidden text attribute handling.
Definition: porlay.cxx:1823
const SfxPoolItem * NextItem()
void SetBottom(SwRect &rRect, tools::Long nNew) const
Definition: frame.hxx:1388
void MergeRange(sal_uInt16 nFrom, sal_uInt16 nTo)
bool IsInRedlines() const
Checks if this node is in redlines.
Definition: node.cxx:2110
#define CH_TXTATR_INWORD
Definition: hintids.hxx:171
Reference< XController > xController
SwIndex nContent
Definition: pam.hxx:38
TextFrameIndex m_nWordStart
output: found word
Definition: txtfrm.hxx:64
sal_Int32 ConvertToViewPosition(sal_Int32 nModelPos) const
Converts a model position into a view position.
A wrapper around SfxPoolItem to store the start position of (usually) a text portion, with an optional end.
Definition: txatbase.hxx:41
void DestroyAttr(SwTextAttr *pAttr)
Definition: thints.cxx:1162
bool bAutoComplDirty
auto complete list dirty
Definition: txtedt.cxx:113
SwBreakIt * g_pBreakIt
Definition: breakit.cxx:33
static bool lcl_HasComments(const SwTextNode &rNode)
Definition: txtedt.cxx:127
sal_Int32 GetStart() const
Definition: txatbase.hxx:86
sal_Int32 m_nEndPos
Definition: swscanner.hxx:41
sal_Int32 m_nOverriddenDashCount
Definition: swscanner.hxx:44
bool IsWordCountDirty() const
Definition: txtedt.cxx:2280
sal_Int32 m_nBegin
Definition: swscanner.hxx:42
constexpr TypedWhichId< SwFormatAutoFormat > RES_TXTATR_AUTOFMT(50)
sal_uLong nNumberOfChars
Definition: txtedt.cxx:107
bool needLanguageForTheMode() const
tools::Long GetLeft(const SwRect &rRect) const
Definition: frame.hxx:1380
void SetWrongDirty(WrongState eNew) const
Definition: txtedt.cxx:2285
static sal_Int16 CheckSpellLang(css::uno::Reference< css::linguistic2::XSpellChecker1 > const &xSpell, LanguageType nLang)
TextFrameIndex m_nEnd
Definition: txtfrm.hxx:62
std::function< LanguageType(sal_Int32, sal_Int32, bool)> m_pGetLangOfChar
Definition: swscanner.hxx:34
SwTextFrame * GetFollow()
Definition: txtfrm.hxx:855
TextFrameIndex GetOffset() const
Definition: txtfrm.hxx:438
SAL_DLLPRIVATE void InitSwParaStatistics(bool bNew)
Start: Data collected during idle time.
Definition: txtedt.cxx:2171
void SetGrammarCheck(std::unique_ptr< SwGrammarMarkUp > pNew)
Definition: txtedt.cxx:2215
OUString GetText() const
Definition: pam.cxx:1076
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:136
void TransliterateText(utl::TransliterationWrapper &rTrans, sal_Int32 nStart, sal_Int32 nEnd, SwUndoTransliterate *pUndo, bool bUseRedlining=false)
change text to Upper/Lower/Hiragana/Katakana/...
Definition: txtedt.cxx:1694
sal_Int32 NextWrong(sal_Int32 nChk) const
Find next incorrectly selected position.
Definition: wrong.cxx:163
bool FrameContainsNode(SwContentFrame const &rFrame, sal_uLong nNodeIndex)
Definition: txtfrm.cxx:288
const SfxItemPool & GetAttrPool() const
Definition: viewsh.hxx:613
virtual SwRedlineTable::size_type GetRedlinePos(const SwNode &rNode, RedlineType nType) const =0
SwIndex * pStartIdx
Definition: splargs.hxx:39
LanguageType nConvTextLang
Definition: splargs.hxx:66
UNDERLYING_TYPE get() const
bool m_bClip
Definition: swscanner.hxx:47
void RstTextAttr(const SwIndex &rIdx, const sal_Int32 nLen, const sal_uInt16 nWhich=0, const SfxItemSet *pSet=nullptr, const bool bInclRefToxMark=false, const bool bExactRange=false)
delete all attributes.
Definition: txtedt.cxx:365
const OUString & getViewText() const
sal_uInt16 Count() const
Definition: wrong.hxx:324
SvtScriptType GetScriptType() const
returns the script type of the selection
Definition: edattr.cxx:676
TextFrameIndex MapModelToViewPos(SwPosition const &rPos) const
Definition: txtfrm.cxx:1268
sal_uLong nAsianWord
Definition: docstat.hxx:36
#define SW_MOD()
Definition: swmodule.hxx:256
FontPitch GetPitch()
SfxItemState GetItemState(sal_uInt16 nWhich, bool bSrchInParent=true, const SfxPoolItem **ppItem=nullptr) const
int i
void SetSmartTagDirty(bool bNew) const
Definition: txtedt.cxx:2316
const ModelToViewHelper m_ModelToView
Definition: swscanner.hxx:39
SwDoc & GetDoc()
Definition: node.hxx:212
const SwPosition * GetPoint() const
Definition: pam.hxx:207
void RecognizeTextRange(const css::uno::Reference< css::text::XTextRange > &rRange, const css::uno::Reference< css::text::XTextMarkup > &rMarkup, const css::uno::Reference< css::frame::XController > &rController) const
static bool AnyInput(VclInputFlags nType=VCL_INPUT_ANY)
SwIndex & Assign(SwIndexReg *, sal_Int32)
Definition: index.cxx:206
static SwSmartTagMgr & Get()
SwEditShell const * GetEditShell() const
Definition: doccorr.cxx:328
SwTextAttr * MakeTextAttr(SwDoc &rDoc, SfxPoolItem &rAttr, sal_Int32 const nStt, sal_Int32 const nEnd, CopyOrNewType const bIsCopy, SwTextNode *const pTextNode)
if COPY then pTextNode must be given!
Definition: thints.cxx:1016
#define LANGUAGE_SYSTEM
void Push()
store a copy of the current cursor on the cursor stack
Definition: crsrsh.cxx:2240
Count
bool HasContent() const
Definition: txatbase.hxx:110
TextFrameIndex MapModelToView(MergedPara const &, SwTextNode const *pNode, sal_Int32 nIndex)
Definition: txtfrm.cxx:1184
static bool IsChinese(LanguageType nLang)
vector_type::size_type size_type
Definition: docary.hxx:223
sal_uInt16 Count() const
sal_Int32 m_nWordLen
Definition: splargs.hxx:122
static bool lcl_HaveCommonAttributes(IStyleAccess &rStyleAccess, const SfxItemSet *pSet1, sal_uInt16 nWhichId, const SfxItemSet &rSet2, std::shared_ptr< SfxItemSet > &pStyleHandle)
Used for automatic styles.
Definition: txtedt.cxx:296
void SetAutoCompleteWordDirty(bool bNew) const
Definition: txtedt.cxx:2329
void SetRight(SwRect &rRect, tools::Long nNew) const
Definition: frame.hxx:1390
LanguageType nConvSrcLang
Definition: splargs.hxx:65
SwTextFrame & GetFrameAtOfst(TextFrameIndex nOfst)
Definition: frmcrsr.cxx:143
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
sal_Int32 Len(sal_uInt16 nIdx) const
Definition: wrong.hxx:314
Marks a character position inside a document model node.
Definition: index.hxx:33
void SetSelection(const SwPaM &rCursor)
Definition: crsrsh.cxx:3538
SwGrammarMarkUp * GetGrammarCheck()
Definition: txtedt.cxx:2232
const OUString m_aPreDashReplacementText
Definition: swscanner.hxx:36
bool Hyphenate(SwInterHyphInfo &rHyphInf)
Interactive hyphenation: we find TextFrame and call its CalcHyph.
Definition: txtedt.cxx:1628
void SetMissingDictionaries(bool bIsMissing)
Use to notify if the dictionary can be found for a single content portion (has to be called for all p...
Definition: doc.cxx:1818
css::uno::Reference< css::linguistic2::XHyphenatedWord > m_xHyphWord
output: hyphenated word
Definition: txtfrm.hxx:58
void SetSmartTags(std::unique_ptr< SwWrongList > pNew)
Definition: txtedt.cxx:2242
sal_uInt16 GetSortNumber() const
Definition: txtatr.hxx:49
SwWrongList * GetSmartTags()
Definition: txtedt.cxx:2262
virtual bool ReplaceRange(SwPaM &rPam, const OUString &rNewStr, const bool bRegExReplace)=0
Replace selected range in a TextNode with string.
LanguageType nConvTargetLang
Definition: splargs.hxx:69
T static_txtattr_cast(S *s)
Definition: txatbase.hxx:241
attention: NOHINTADJUST prevents MergePortions! when using this need to pay attention to ignore start...
sal_Int32 GetChgPos() const
Definition: txatritr.hxx:67
std::pair< SwTextNode *, sal_Int32 > MapViewToModel(TextFrameIndex nIndex) const
map position in potentially merged text frame to SwPosition
Definition: txtfrm.cxx:1232
sal_uLong nNumberOfCharsExcludingSpaces
Definition: txtedt.cxx:108
sal_Int32 GetEndInv() const
Definition: wrong.hxx:290
enumrange< T >::Iterator end(enumrange< T >)
const SwPosition * Start() const
Definition: pam.hxx:212
EXPAND : (Start < nIndex <= End)
Definition: ndtxt.hxx:371
std::unique_ptr< SwWrongList > pWrong
Definition: txtedt.cxx:102
#define VCL_INPUT_ANY
const IDocumentLayoutAccess & getIDocumentLayoutAccess() const
Provides access to the document layout interface.
Definition: node.cxx:2098
css::uno::Reference< css::i18n::XBreakIterator > const & GetBreakIter() const
Definition: breakit.hxx:62
void ReplaceTextOnly(sal_Int32 nPos, sal_Int32 nLen, const OUString &rText, const css::uno::Sequence< sal_Int32 > &rOffsets)
Definition: txtedt.cxx:1955
uno::Reference< linguistic2::XSpellChecker1 > GetSpellChecker()
Definition: swtypes.cxx:52
virtual void SetEnd(sal_Int32)
Definition: txatbase.cxx:53
const OUString & GetWord() const
Definition: swscanner.hxx:64
OUString aConvText
Definition: splargs.hxx:64
const css::lang::Locale & GetLocale(const LanguageType aLang)
Definition: breakit.hxx:67
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:79
SwTextNode::WrongState eWrongDirty
online spell checking needed/done?
Definition: txtedt.cxx:110
static sal_Int32 lcl_MaskRedlines(const SwTextNode &rNode, OUStringBuffer &rText, sal_Int32 nStt, sal_Int32 nEnd, const sal_Unicode cChar)
Definition: txtedt.cxx:146
css::uno::Reference< css::linguistic2::XSpellAlternatives > xSpellAlt
Definition: splargs.hxx:90
friend class SwIndex
Definition: index.hxx:108
IDocumentRedlineAccess const & getIDocumentRedlineAccess() const
Definition: doc.cxx:335
sal_Int32 GetBeginInv() const
Definition: wrong.hxx:289
void SetHyphWord(const css::uno::Reference< css::linguistic2::XHyphenatedWord > &rxHW)
Definition: splargs.hxx:135
SwRect AutoSpell_(SwTextNode &, sal_Int32)
Is called by DoIdleJob_() and ExecSpellPopup()
Definition: txtedt.cxx:1246
sal_Int32 m_nLength
Definition: swscanner.hxx:43
sal_uInt16 GetMinWordLen() const
Definition: acmplwrd.hxx:64
WrongState GetWrongDirty() const
Definition: txtedt.cxx:2293
css::uno::Reference< css::frame::XController > GetController()
Definition: docsh.cxx:1281
std::unique_ptr< SwWrongList > pSmartTags
Definition: txtedt.cxx:104
virtual std::unique_ptr< SfxItemSet > Clone(bool bItems=true, SfxItemPool *pToPool=nullptr) const
sal_uInt16 GetWrongPos(sal_Int32 nValue) const
Find the first position that is greater or equal to the given value.
Definition: wrong.cxx:190
SwParaIdleData_Impl * m_pParaIdleData_Impl
Definition: ndtxt.hxx:100
void ClearSmartTags()
Definition: txtedt.cxx:2251
TextFrameIndex MapModelToView(SwTextNode const *pNode, sal_Int32 nIndex) const
Definition: txtfrm.cxx:1253
OUString transliterate(const OUString &rStr, sal_Int32 nStart, sal_Int32 nLen) const
sal_Int32 GetIndex() const
Definition: index.hxx:91
bool InWrongWord(sal_Int32 &rChk, sal_Int32 &rLn) const
If a word is incorrectly selected, this method returns begin and length of it.
Definition: wrong.cxx:102
void CollectAutoCmplWrds(SwTextNode &, sal_Int32)
Is called by DoIdleJob_()
Definition: txtedt.cxx:1552
constexpr TypedWhichId< SvxFontItem > RES_CHRATR_CJK_FONT(22)
::sw::DocumentSettingManager & GetDocumentSettingManager()
Definition: doc.cxx:186
LanguageType GetLang(const sal_Int32 nBegin, const sal_Int32 nLen=0, sal_uInt16 nScript=0) const
Definition: thints.cxx:3364
TextFrameIndex m_nStart
input: requested range to hyphenate
Definition: txtfrm.hxx:61
RedlineType GetType(sal_uInt16 nPos=0) const
Definition: docredln.cxx:1840
const sal_Int32 * End() const
Definition: txatbase.hxx:152
ModelPosition ConvertToModelPosition(sal_Int32 nViewPos) const
Converts a view position into a model position.
QPRO_FUNC_TYPE nType
SwRect SmartTagScan(SwTextNode &)
Is called by DoIdleJob_()
Definition: txtedt.cxx:1436
bool IsHidden() const
Definition: ndtxt.cxx:4494
SwWrongList * GetWrong()
Definition: txtedt.cxx:2204
constexpr TypedWhichId< SwTOXMark > RES_TXTATR_TOXMARK(47)
const char * pChar
sal_Int32 GetBegin() const
Definition: swscanner.hxx:66
bool IsAutoCompleteWordDirty() const
Definition: txtedt.cxx:2337
bool CountWords(SwDocStat &rStat, sal_Int32 nStart, sal_Int32 nEnd) const
count words in given range - returns true if we refreshed out count
Definition: txtedt.cxx:2004
const LanguageType * m_pLanguage
Definition: swscanner.hxx:38
constexpr TypedWhichId< SwFormatField > RES_TXTATR_INPUTFIELD(55)
a clone of SwInterHyphInfo, but with TextFrameIndex instead of node index
Definition: txtfrm.hxx:54
const SwAttrSet & GetSwAttrSet() const
Does node has already its own auto-attributes? Access to SwAttrSet.
Definition: node.hxx:715
virtual void CallSwClientNotify(const SfxHint &rHint) const override
Definition: calbck.cxx:326
void RecognizeString(const OUString &rText, const css::uno::Reference< css::text::XTextMarkup > &rMarkup, const css::uno::Reference< css::frame::XController > &rController, const css::lang::Locale &rLocale, sal_uInt32 nStart, sal_uInt32 nLen) const
bool InsertHint(SwTextAttr *const pAttr, const SetAttrMode nMode=SetAttrMode::DEFAULT)
Insert pAttr into hints array.
Definition: thints.cxx:1305
constexpr sal_Int32 COMPLETE_STRING
Definition: swtypes.hxx:58
sal_uLong nNumberOfAsianWords
Definition: txtedt.cxx:106
OUString GetNumString(const bool _bInclPrefixAndSuffixStrings=true, const unsigned int _nRestrictToThisLevel=MAXLEVEL, SwRootFrame const *pLayout=nullptr) const
Returns outline of numbering string.
Definition: ndtxt.cxx:3083
const SfxPoolItem & GetAttr() const
Definition: txatbase.hxx:163
SwTextNode * pStartNode
Definition: splargs.hxx:38
void ClearWrong()
Definition: txtedt.cxx:2193
LanguageType m_aCurrentLang
Definition: swscanner.hxx:45
tools::Long GetBottom(const SwRect &rRect) const
Definition: frame.hxx:1379
virtual RedlineFlags GetRedlineFlags() const =0
Query the currently set redline mode.
#define SAL_WARN(area, stream)
SwIndex * pEndIdx
Definition: splargs.hxx:41
void SetLeft(SwRect &rRect, tools::Long nNew) const
Definition: frame.hxx:1389
SwFormatColl * GetFormatColl() const
Definition: node.hxx:454
std::unique_ptr< SwpHints > m_pSwpHints
May be 0.
Definition: ndtxt.hxx:93
virtual void SetMark()
Unless this is called, the getter method of Mark will return Point.
Definition: pam.cxx:478
static sal_Int32 clipIndexBounds(const OUString &rStr, sal_Int32 nPos)
Definition: txtedt.cxx:652
SwTextFrame * SwHyphIterCacheLastTextFrame(SwTextNode const *pNode, const sw::Creator &create)
Definition: edlingu.cxx:585
constexpr TypedWhichId< SwFormatRefMark > RES_TXTATR_REFMARK(RES_TXTATR_WITHEND_BEGIN)
bool Pop(PopMode)
delete cursor
Definition: crsrsh.cxx:2262
sal_uLong nWord
Definition: docstat.hxx:35
static css::uno::Reference< css::text::XTextRange > CreateXTextRange(SwDoc &rDoc, const SwPosition &rPos, const SwPosition *const pMark)
Definition: unoobj2.cxx:1214
virtual const SwRedlineTable & GetRedlineTable() const =0
bool NextWord()
Definition: txtedt.cxx:808
std::unique_ptr< SwWrongList > ReleaseWrong()
Definition: txtedt.cxx:2199
sal_uLong nAllPara
all paragraphs, including empty/hidden ones
Definition: docstat.hxx:34
void Insert(sal_uInt16 nWhere, std::vector< SwWrongArea >::iterator startPos, std::vector< SwWrongArea >::iterator const &endPos)
Definition: wrong.cxx:537
SwViewShell * GetCurrShell() const
Definition: rootfrm.hxx:207
o3tl::strong_int< sal_Int32, struct Tag_TextFrameIndex > TextFrameIndex
Denotes a character index in a text frame at a layout level, after extent mapping from a text node at...
bool Convert(SwConversionArgs &)
Definition: txtedt.cxx:1110
TextFrameIndex m_nWordLen
Definition: txtfrm.hxx:65
CharClass & GetAppCharClass()
Definition: init.cxx:703
void Height(tools::Long nNew)
Definition: swrect.hxx:193
LanguageType GetLanguage() const
Definition: txatritr.hxx:68
SwTextNode * pEndNode
Definition: splargs.hxx:40
OUString m_Text
Definition: ndtxt.hxx:98
IStyleAccess & getIDocumentStyleAccess()
Provides access to the document automatic styles interface.
Definition: node.cxx:2108
sal_uInt16 Which() const
SwRootFrame * getRootFrame()
Definition: frame.hxx:680
constexpr TypedWhichId< SvxLanguageItem > RES_CHRATR_CJK_LANGUAGE(24)
virtual void Update(SwIndex const &rPos, const sal_Int32 nChangeLen, const bool bNegative=false, const bool bDelete=false) override
override SwIndexReg
Definition: ndtxt.cxx:1163
OUString m_aText
Definition: swscanner.hxx:37
void SetInvalid(sal_Int32 nBegin, sal_Int32 nEnd)
Definition: wrong.cxx:253
rtl_TextEncoding GetCharSet() const
sal_uInt16 nPos
const SfxPoolItem * GetCurItem() const
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:856
bool IsGrammarCheckDirty() const
Definition: txtedt.cxx:2311
bool HasHints() const
Definition: ndtxt.hxx:228
bool IsSymbolAt(TextFrameIndex) const
Definition: itratr.cxx:186
sal_Int32 GetEnd() const
Definition: swscanner.hxx:67
void AddChanges(SwTextNode &rTNd, sal_Int32 nStart, sal_Int32 nLen, css::uno::Sequence< sal_Int32 > const &rOffsets)
Definition: unovwr.cxx:383
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo