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