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