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