Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / edit / acorrect.cxx
blob06ab1231ff4073b21a33d023432fd3860e573ee4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <hintids.hxx>
22 #include <fmtinfmt.hxx>
23 #include <editsh.hxx>
24 #include <doc.hxx>
25 #include <pam.hxx>
26 #include <unocrsr.hxx>
27 #include <txatbase.hxx>
28 #include <txtfrm.hxx>
29 #include <ndtxt.hxx>
30 #include <acorrect.hxx>
31 #include <shellio.hxx>
32 #include <swundo.hxx>
33 #include <viscrs.hxx>
34 #include <com/sun/star/linguistic2/XHyphenator.hpp>
35 #include <com/sun/star/linguistic2/XHyphenatedWord.hpp>
36 #include <osl/diagnose.h>
37 #include <svl/numformat.hxx>
39 #include <editeng/acorrcfg.hxx>
40 #include <IDocumentRedlineAccess.hxx>
41 #include <rootfrm.hxx>
43 using namespace ::com::sun::star;
45 namespace {
47 class PaMIntoCursorShellRing
49 SwPaM &m_rDelPam, &m_rCursor;
50 SwPaM* m_pPrevDelPam;
51 SwPaM* m_pPrevCursor;
53 static void RemoveFromRing( SwPaM& rPam, SwPaM const * pPrev );
54 public:
55 PaMIntoCursorShellRing( SwCursorShell& rSh, SwPaM& rCursor, SwPaM& rPam );
56 ~PaMIntoCursorShellRing();
61 PaMIntoCursorShellRing::PaMIntoCursorShellRing(SwCursorShell& rCSh, SwPaM& rShCursor, SwPaM& rPam)
62 : m_rDelPam(rPam)
63 , m_rCursor(rShCursor)
65 SwPaM* pShCursor = rCSh.GetCursor_();
67 m_pPrevDelPam = m_rDelPam.GetPrev();
68 m_pPrevCursor = m_rCursor.GetPrev();
70 m_rDelPam.GetRingContainer().merge(pShCursor->GetRingContainer());
71 m_rCursor.GetRingContainer().merge(pShCursor->GetRingContainer());
74 PaMIntoCursorShellRing::~PaMIntoCursorShellRing()
76 // and take out the Pam again:
77 RemoveFromRing(m_rDelPam, m_pPrevDelPam);
78 RemoveFromRing(m_rCursor, m_pPrevCursor);
81 void PaMIntoCursorShellRing::RemoveFromRing( SwPaM& rPam, SwPaM const * pPrev )
83 SwPaM* p;
84 SwPaM* pNext = &rPam;
85 do {
86 p = pNext;
87 pNext = p->GetNext();
88 p->MoveTo( &rPam );
89 } while( p != pPrev );
92 SwAutoCorrDoc::SwAutoCorrDoc( SwEditShell& rEditShell, SwPaM& rPam,
93 sal_Unicode cIns )
94 : m_rEditSh( rEditShell ), m_rCursor( rPam )
95 , m_nEndUndoCounter(0)
96 , m_bUndoIdInitialized( cIns == 0 )
100 SwAutoCorrDoc::~SwAutoCorrDoc()
102 for (int i = 0; i < m_nEndUndoCounter; ++i)
104 m_rEditSh.EndUndo();
108 void SwAutoCorrDoc::DeleteSel( SwPaM& rDelPam )
110 // this should work with plain SwPaM as well because start and end
111 // are always in same node, but since there is GetRanges already...
112 std::vector<std::shared_ptr<SwUnoCursor>> ranges;
113 if (sw::GetRanges(ranges, *m_rEditSh.GetDoc(), rDelPam))
115 DeleteSelImpl(rDelPam);
117 else
119 for (auto const& pCursor : ranges)
121 DeleteSelImpl(*pCursor);
126 void SwAutoCorrDoc::DeleteSelImpl(SwPaM & rDelPam)
128 SwDoc* pDoc = m_rEditSh.GetDoc();
129 if( pDoc->IsAutoFormatRedline() )
131 // so that also the DelPam be moved, include it in the
132 // Shell-Cursr-Ring !!
133 // ??? is that really necessary - this should never join nodes, so Update should be enough?
134 // PaMIntoCursorShellRing aTmp( rEditSh, rCursor, rDelPam );
135 assert(rDelPam.GetPoint()->GetNode() == rDelPam.GetMark()->GetNode());
136 pDoc->getIDocumentContentOperations().DeleteAndJoin( rDelPam );
138 else
140 pDoc->getIDocumentContentOperations().DeleteRange( rDelPam );
144 bool SwAutoCorrDoc::Delete( sal_Int32 nStt, sal_Int32 nEnd )
146 SwTextNode const*const pTextNd = m_rCursor.GetPointNode().GetTextNode();
147 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
148 pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
149 assert(pFrame);
150 SwPaM aSel(pFrame->MapViewToModelPos(TextFrameIndex(nStt)),
151 pFrame->MapViewToModelPos(TextFrameIndex(nEnd)));
152 DeleteSel( aSel );
154 if( m_bUndoIdInitialized )
155 m_bUndoIdInitialized = true;
156 return true;
159 bool SwAutoCorrDoc::Insert( sal_Int32 nPos, const OUString& rText )
161 SwTextNode const*const pTextNd = m_rCursor.GetPointNode().GetTextNode();
162 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
163 pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
164 assert(pFrame);
165 SwPaM aPam(pFrame->MapViewToModelPos(TextFrameIndex(nPos)));
166 m_rEditSh.GetDoc()->getIDocumentContentOperations().InsertString( aPam, rText );
167 if( !m_bUndoIdInitialized )
169 m_bUndoIdInitialized = true;
170 if( 1 == rText.getLength() )
172 m_rEditSh.StartUndo( SwUndoId::AUTOCORRECT );
173 ++m_nEndUndoCounter;
176 return true;
179 bool SwAutoCorrDoc::Replace( sal_Int32 nPos, const OUString& rText )
181 return ReplaceRange( nPos, rText.getLength(), rText );
184 bool SwAutoCorrDoc::ReplaceRange( sal_Int32 nPos, sal_Int32 nSourceLength, const OUString& rText )
186 assert(nSourceLength == 1); // sw_redlinehide: this is currently the case,
187 // and ensures that the replace range cannot *contain* delete redlines,
188 // so we don't need something along the lines of:
189 // if (sw::GetRanges(ranges, *rEditSh.GetDoc(), aPam))
190 // ReplaceImpl(...)
191 // else
192 // ReplaceImpl(ranges.begin())
193 // for (ranges.begin() + 1; ranges.end(); )
194 // DeleteImpl(*it)
196 SwTextNode * const pNd = m_rCursor.GetPointNode().GetTextNode();
197 if ( !pNd )
199 return false;
202 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
203 pNd->getLayoutFrame(m_rEditSh.GetLayout())));
204 assert(pFrame);
205 std::pair<SwTextNode *, sal_Int32> const pos(pFrame->MapViewToModel(TextFrameIndex(nPos)));
207 SwPaM* pPam = &m_rCursor;
208 if (pPam->GetPoint()->GetNode() != *pos.first
209 || pPam->GetPoint()->GetContentIndex() != pos.second)
211 pPam = new SwPaM(*pos.first, pos.second);
214 // text attributes with dummy characters must not be replaced!
215 bool bDoReplace = true;
216 sal_Int32 const nLen = rText.getLength();
217 for (sal_Int32 n = 0; n < nLen && n + nPos < pFrame->GetText().getLength(); ++n)
219 sal_Unicode const Char = pFrame->GetText()[n + nPos];
220 if (CH_TXTATR_BREAKWORD == Char || CH_TXTATR_INWORD == Char)
222 assert(pFrame->MapViewToModel(TextFrameIndex(n+nPos)).first->GetTextAttrForCharAt(pFrame->MapViewToModel(TextFrameIndex(n+nPos)).second));
223 bDoReplace = false;
224 break;
228 // tdf#83419 avoid bad autocorrect with visible redlines
229 // e.g. replacing the first letter of the tracked deletion
230 // with its capitalized (and not deleted) version.
231 if ( bDoReplace && !pFrame->getRootFrame()->IsHideRedlines() &&
232 m_rEditSh.GetDoc()->getIDocumentRedlineAccess().HasRedline( *pPam, RedlineType::Delete, /*bStartOrEndInRange=*/false ) )
234 bDoReplace = false;
237 if ( bDoReplace )
239 SwDoc* pDoc = m_rEditSh.GetDoc();
241 if( pDoc->IsAutoFormatRedline() )
243 if (nPos == pFrame->GetText().getLength()) // at the End do an Insert
245 pDoc->getIDocumentContentOperations().InsertString( *pPam, rText );
247 else
249 assert(pos.second != pos.first->Len()); // must be _before_ char
250 PaMIntoCursorShellRing aTmp( m_rEditSh, m_rCursor, *pPam );
252 pPam->SetMark();
253 pPam->GetPoint()->SetContent( std::min<sal_Int32>(
254 pos.first->GetText().getLength(), pos.second + nSourceLength) );
255 pDoc->getIDocumentContentOperations().ReplaceRange( *pPam, rText, false );
256 pPam->Exchange();
257 pPam->DeleteMark();
260 else
262 pPam->SetMark();
263 pPam->GetPoint()->SetContent( std::min<sal_Int32>(
264 pos.first->GetText().getLength(), pos.second + nSourceLength) );
265 pDoc->getIDocumentContentOperations().ReplaceRange( *pPam, rText, false );
266 pPam->Exchange();
267 pPam->DeleteMark();
270 if( m_bUndoIdInitialized )
272 m_bUndoIdInitialized = true;
273 if( 1 == rText.getLength() )
275 m_rEditSh.StartUndo( SwUndoId::AUTOCORRECT );
276 ++m_nEndUndoCounter;
281 if( pPam != &m_rCursor )
282 delete pPam;
284 return true;
287 void SwAutoCorrDoc::SetAttr( sal_Int32 nStt, sal_Int32 nEnd, sal_uInt16 nSlotId,
288 SfxPoolItem& rItem )
290 SwTextNode const*const pTextNd = m_rCursor.GetPointNode().GetTextNode();
291 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
292 pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
293 assert(pFrame);
294 SwPaM aPam(pFrame->MapViewToModelPos(TextFrameIndex(nStt)),
295 pFrame->MapViewToModelPos(TextFrameIndex(nEnd)));
297 SfxItemPool& rPool = m_rEditSh.GetDoc()->GetAttrPool();
298 sal_uInt16 nWhich = rPool.GetWhich( nSlotId, false );
299 if( nWhich )
301 rItem.SetWhich( nWhich );
303 SfxItemSet aSet( rPool, aCharFormatSetRange );
304 SetAllScriptItem( aSet, rItem );
306 m_rEditSh.GetDoc()->SetFormatItemByAutoFormat( aPam, aSet );
308 if( m_bUndoIdInitialized )
309 m_bUndoIdInitialized = true;
313 bool SwAutoCorrDoc::SetINetAttr( sal_Int32 nStt, sal_Int32 nEnd, const OUString& rURL )
315 SwTextNode const*const pTextNd = m_rCursor.GetPointNode().GetTextNode();
316 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
317 pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
318 assert(pFrame);
319 SwPaM aPam(pFrame->MapViewToModelPos(TextFrameIndex(nStt)),
320 pFrame->MapViewToModelPos(TextFrameIndex(nEnd)));
322 SfxItemSetFixed<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT>
323 aSet( m_rEditSh.GetDoc()->GetAttrPool() );
324 aSet.Put( SwFormatINetFormat( rURL, OUString() ));
325 m_rEditSh.GetDoc()->SetFormatItemByAutoFormat( aPam, aSet );
326 if( m_bUndoIdInitialized )
327 m_bUndoIdInitialized = true;
328 return true;
331 /** Return the text of a previous paragraph
333 * @param bAtNormalPos If <true> before the normal insert position; if <false> in which the
334 * corrected word was inserted. (Doesn't need to be the same paragraph!)
335 * @return text or 0, if previous paragraph does not exists or there are only blankness
337 OUString const* SwAutoCorrDoc::GetPrevPara(bool const bAtNormalPos)
339 OUString const* pStr(nullptr);
341 if( bAtNormalPos || !m_oIndex )
343 m_oIndex.emplace(m_rCursor.GetPoint()->GetNode());
345 sw::GotoPrevLayoutTextFrame(*m_oIndex, m_rEditSh.GetLayout());
347 SwTextFrame const* pFrame(nullptr);
348 for (SwTextNode * pTextNd = m_oIndex->GetNode().GetTextNode();
349 pTextNd; pTextNd = m_oIndex->GetNode().GetTextNode())
351 pFrame = static_cast<SwTextFrame const*>(
352 pTextNd->getLayoutFrame(m_rEditSh.GetLayout()));
353 if (pFrame && !pFrame->GetText().isEmpty())
355 break;
357 sw::GotoPrevLayoutTextFrame(*m_oIndex, m_rEditSh.GetLayout());
359 if (pFrame && !pFrame->GetText().isEmpty() &&
360 0 == pFrame->GetTextNodeForParaProps()->GetAttrOutlineLevel())
362 pStr = & pFrame->GetText();
365 if( m_bUndoIdInitialized )
366 m_bUndoIdInitialized = true;
368 return pStr;
371 bool SwAutoCorrDoc::ChgAutoCorrWord( sal_Int32& rSttPos, sal_Int32 nEndPos,
372 SvxAutoCorrect& rACorrect,
373 OUString* pPara )
375 if( m_bUndoIdInitialized )
376 m_bUndoIdInitialized = true;
378 // Found a beginning of a paragraph or a Blank,
379 // search for the word Kuerzel (Shortcut) in the Auto
380 SwTextNode* pTextNd = m_rCursor.GetPointNode().GetTextNode();
381 OSL_ENSURE( pTextNd, "where is the TextNode?" );
383 bool bRet = false;
384 if( nEndPos == rSttPos )
385 return bRet;
387 LanguageType eLang = GetLanguage(nEndPos);
388 if(LANGUAGE_SYSTEM == eLang)
389 eLang = GetAppLanguage();
390 LanguageTag aLanguageTag( eLang);
392 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
393 pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
394 assert(pFrame);
396 const OUString sFrameText = pFrame->GetText();
397 const SvxAutocorrWord* pFnd = rACorrect.SearchWordsInList(
398 sFrameText, rSttPos, nEndPos, *this, aLanguageTag);
399 SwDoc* pDoc = m_rEditSh.GetDoc();
400 if( pFnd )
402 // replace also last colon of keywords surrounded by colons (for example, ":name:")
403 const bool replaceLastChar = sFrameText.getLength() > nEndPos && pFnd->GetShort()[0] == ':'
404 && pFnd->GetShort().endsWith(":");
406 SwPosition aStartPos( pFrame->MapViewToModelPos(TextFrameIndex(rSttPos) ));
407 SwPosition aEndPos( pFrame->MapViewToModelPos(TextFrameIndex(nEndPos + (replaceLastChar ? 1 : 0))) );
408 SwPaM aPam(aStartPos, aEndPos);
410 // don't replace, if a redline starts or ends within the original text
411 if ( pDoc->getIDocumentRedlineAccess().HasRedline( aPam, RedlineType::Any, /*bStartOrEndInRange=*/true ) )
413 return bRet;
416 if( pFnd->IsTextOnly() )
418 //JP 22.04.99: Bug 63883 - Special treatment for dots.
419 const bool bLastCharIsPoint
420 = nEndPos < sFrameText.getLength() && ('.' == sFrameText[nEndPos]);
421 if( !bLastCharIsPoint || pFnd->GetLong().isEmpty() ||
422 '.' != pFnd->GetLong()[ pFnd->GetLong().getLength() - 1 ] )
424 // replace the selection
425 std::vector<std::shared_ptr<SwUnoCursor>> ranges;
426 if (sw::GetRanges(ranges, *m_rEditSh.GetDoc(), aPam))
428 pDoc->getIDocumentContentOperations().ReplaceRange(aPam, pFnd->GetLong(), false);
429 bRet = true;
431 else if (!ranges.empty())
433 assert(ranges.front()->GetPoint()->GetNode() == ranges.front()->GetMark()->GetNode());
434 pDoc->getIDocumentContentOperations().ReplaceRange(
435 *ranges.front(), pFnd->GetLong(), false);
436 for (auto it = ranges.begin() + 1; it != ranges.end(); ++it)
438 DeleteSelImpl(**it);
440 bRet = true;
443 // tdf#83260 After calling sw::DocumentContentOperationsManager::ReplaceRange
444 // pTextNd may become invalid when change tracking is on and Edit -> Track Changes -> Show == OFF.
445 // ReplaceRange shows changes, this moves deleted nodes from special section to document.
446 // Then Show mode is disabled again. As a result pTextNd may be invalidated.
447 pTextNd = m_rCursor.GetPointNode().GetTextNode();
450 else
452 SwTextBlocks aTBlks( rACorrect.GetAutoCorrFileName( aLanguageTag, false, true ));
453 sal_uInt16 nPos = aTBlks.GetIndex( pFnd->GetShort() );
454 if( USHRT_MAX != nPos && aTBlks.BeginGetDoc( nPos ) )
456 DeleteSel( aPam );
457 pDoc->DontExpandFormat( *aPam.GetPoint() );
459 if( pPara )
461 OSL_ENSURE( !m_oIndex, "who has not deleted his Index?" );
462 m_oIndex.emplace(m_rCursor.GetPoint()->GetNode());
463 sw::GotoPrevLayoutTextFrame(*m_oIndex, m_rEditSh.GetLayout());
466 SwDoc* pAutoDoc = aTBlks.GetDoc();
467 SwNodeIndex aSttIdx( pAutoDoc->GetNodes().GetEndOfExtras(), 1 );
468 SwContentNode* pContentNd = pAutoDoc->GetNodes().GoNext( &aSttIdx );
469 SwPaM aCpyPam( aSttIdx );
471 const SwTableNode* pTableNd = pContentNd->FindTableNode();
472 if( pTableNd )
474 aCpyPam.GetPoint()->Assign( *pTableNd );
476 aCpyPam.SetMark();
478 // then until the end of the Nodes Array
479 aCpyPam.GetPoint()->Assign( pAutoDoc->GetNodes().GetEndOfContent(), SwNodeOffset(-1) );
480 pContentNd = aCpyPam.GetPointContentNode();
481 if (pContentNd)
482 aCpyPam.GetPoint()->SetContent( pContentNd->Len() );
484 SwDontExpandItem aExpItem;
485 aExpItem.SaveDontExpandItems( *aPam.GetPoint() );
487 pAutoDoc->getIDocumentContentOperations().CopyRange(aCpyPam, *aPam.GetPoint(), SwCopyFlags::CheckPosInFly);
489 aExpItem.RestoreDontExpandItems( *aPam.GetPoint() );
491 if( pPara )
493 sw::GotoNextLayoutTextFrame(*m_oIndex, m_rEditSh.GetLayout());
494 pTextNd = m_oIndex->GetNode().GetTextNode();
496 bRet = true;
498 aTBlks.EndGetDoc();
502 if( bRet && pPara && pTextNd )
504 SwTextFrame const*const pNewFrame(static_cast<SwTextFrame const*>(
505 pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
506 *pPara = pNewFrame->GetText();
509 return bRet;
512 bool SwAutoCorrDoc::TransliterateRTLWord( sal_Int32& rSttPos, sal_Int32 nEndPos, bool bApply )
514 if( m_bUndoIdInitialized )
515 m_bUndoIdInitialized = true;
517 SwTextNode* pTextNd = m_rCursor.GetPointNode().GetTextNode();
518 OSL_ENSURE( pTextNd, "where is the TextNode?" );
520 bool bRet = false;
521 if( nEndPos == rSttPos )
522 return bRet;
524 LanguageType eLang = GetLanguage(nEndPos);
525 if(LANGUAGE_SYSTEM == eLang)
526 eLang = GetAppLanguage();
527 LanguageTag aLanguageTag(eLang);
529 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
530 pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
531 assert(pFrame);
533 const OUString sFrameText = pFrame->GetText();
534 SwDoc* pDoc = m_rEditSh.GetDoc();
535 if ( pFrame->IsRightToLeft() || bApply )
537 // transliterate to Old Hungarian using Numbertext via NatNum12 number format modifier
538 OUString sWord(sFrameText.copy(rSttPos, nEndPos - rSttPos));
539 // Consonant disambiguation using hyphenation
540 uno::Reference< linguistic2::XHyphenator > xHyph;
541 xHyph = ::GetHyphenator();
542 OUStringBuffer sDisambiguatedWord;
544 const ::css::uno::Sequence< ::css::beans::PropertyValue > aProperties;
545 css::uno::Reference< css::linguistic2::XHyphenatedWord > xHyphWord;
546 for (int i = 0; i+1 < sWord.getLength(); i++ )
548 xHyphWord = xHyph->hyphenate( sWord,
549 aLanguageTag.getLocale(),
551 aProperties );
552 // insert ZWSP at a hyphenation point, if it's not an alternative one (i.e. ssz->sz-sz)
553 if (xHyphWord.is() && xHyphWord->getHyphenationPos()+1 == i && !xHyphWord->isAlternativeSpelling())
555 sDisambiguatedWord.append(CHAR_ZWSP);
557 sDisambiguatedWord.append(sWord[i]);
559 sDisambiguatedWord.append(sWord[sWord.getLength()-1]);
561 SvNumberFormatter* pFormatter = pDoc->GetNumberFormatter();
562 OUString sConverted;
563 if (pFormatter && !sWord.isEmpty())
565 const Color* pColor = nullptr;
567 // Send text as NatNum12 prefix: "word" -> "[NatNum12 word]0"
569 // Closing bracket doesn't allowed in NatNum parameters, remove it from transliteration:
570 // "[word]" -> "[NatNum12 [word]0"
571 bool bHasBracket = sWord.endsWith("]");
572 if ( !bHasBracket )
573 sDisambiguatedWord.append("]");
574 OUString sPrefix("[NatNum12 " + sDisambiguatedWord + "0");
575 if (pFormatter->GetPreviewString(sPrefix, 0, sConverted, &pColor, LANGUAGE_USER_HUNGARIAN_ROVAS))
577 if ( bHasBracket )
578 sConverted = sConverted + "]";
579 bRet = true;
583 SwPaM aPam(pFrame->MapViewToModelPos(TextFrameIndex(rSttPos)),
584 pFrame->MapViewToModelPos(TextFrameIndex(nEndPos)));
585 if (bRet && nEndPos <= sFrameText.getLength())
586 pDoc->getIDocumentContentOperations().ReplaceRange(aPam, sConverted, false);
589 return bRet;
592 // Called by the functions:
593 // - FnCapitalStartWord
594 // - FnCapitalStartSentence
595 // after the exchange of characters. Then the words, if necessary, can be inserted
596 // into the exception list.
597 void SwAutoCorrDoc::SaveCpltSttWord( ACFlags nFlag, sal_Int32 nPos,
598 const OUString& rExceptWord,
599 sal_Unicode cChar )
601 SwNodeOffset nNode = m_oIndex ? m_oIndex->GetIndex() : m_rCursor.GetPoint()->GetNodeIndex();
602 LanguageType eLang = GetLanguage(nPos);
603 m_rEditSh.GetDoc()->SetAutoCorrExceptWord( std::make_unique<SwAutoCorrExceptWord>( nFlag,
604 nNode, nPos, rExceptWord, cChar, eLang ));
607 LanguageType SwAutoCorrDoc::GetLanguage( sal_Int32 nPos ) const
609 LanguageType eRet = LANGUAGE_SYSTEM;
611 SwTextNode* pNd = m_rCursor.GetPoint()->GetNode().GetTextNode();
613 if( pNd )
615 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
616 pNd->getLayoutFrame(m_rEditSh.GetLayout())));
617 assert(pFrame);
618 eRet = pFrame->GetLangOfChar(TextFrameIndex(nPos), 0, true);
620 if(LANGUAGE_SYSTEM == eRet)
621 eRet = GetAppLanguage();
622 return eRet;
625 void SwAutoCorrExceptWord::CheckChar( const SwPosition& rPos, sal_Unicode cChr )
627 // test only if this is an improvement.
628 // If yes, then add the word to the list.
629 if (m_cChar == cChr && rPos.GetNodeIndex() == m_nNode && rPos.GetContentIndex() == m_nContent)
631 // get the current autocorrection:
632 SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect();
634 // then add to the list:
635 if (ACFlags::CapitalStartWord & m_nFlags)
636 pACorr->AddWordStartException(m_sWord, m_eLanguage);
637 else if (ACFlags::CapitalStartSentence & m_nFlags)
638 pACorr->AddCplSttException(m_sWord, m_eLanguage);
642 bool SwAutoCorrExceptWord::CheckDelChar( const SwPosition& rPos )
644 bool bRet = false;
645 if (!m_bDeleted && rPos.GetNodeIndex() == m_nNode && rPos.GetContentIndex() == m_nContent)
646 m_bDeleted = bRet = true;
647 return bRet;
650 SwDontExpandItem::~SwDontExpandItem()
654 void SwDontExpandItem::SaveDontExpandItems( const SwPosition& rPos )
656 const SwTextNode* pTextNd = rPos.GetNode().GetTextNode();
657 if( pTextNd )
659 m_pDontExpandItems.reset( new SfxItemSet( const_cast<SwDoc&>(pTextNd->GetDoc()).GetAttrPool(),
660 aCharFormatSetRange ) );
661 const sal_Int32 n = rPos.GetContentIndex();
662 if (!pTextNd->GetParaAttr( *m_pDontExpandItems, n, n,
663 n != pTextNd->GetText().getLength() ))
665 m_pDontExpandItems.reset();
670 void SwDontExpandItem::RestoreDontExpandItems( const SwPosition& rPos )
672 SwTextNode* pTextNd = rPos.GetNode().GetTextNode();
673 if( !pTextNd )
674 return;
676 const sal_Int32 nStart = rPos.GetContentIndex();
677 if( nStart == pTextNd->GetText().getLength() )
678 pTextNd->FormatToTextAttr( pTextNd );
680 if( !(pTextNd->GetpSwpHints() && pTextNd->GetpSwpHints()->Count()) )
681 return;
683 const size_t nSize = pTextNd->GetpSwpHints()->Count();
684 sal_Int32 nAttrStart;
686 for( size_t n = 0; n < nSize; ++n )
688 SwTextAttr* pHt = pTextNd->GetpSwpHints()->Get( n );
689 nAttrStart = pHt->GetStart();
690 if( nAttrStart > nStart ) // beyond the area
691 break;
693 const sal_Int32* pAttrEnd;
694 if( nullptr != ( pAttrEnd = pHt->End() ) &&
695 ( ( nAttrStart < nStart &&
696 ( pHt->DontExpand() ? nStart < *pAttrEnd
697 : nStart <= *pAttrEnd )) ||
698 ( nStart == nAttrStart &&
699 ( nAttrStart == *pAttrEnd || !nStart ))) )
701 const SfxPoolItem* pItem;
702 if( !m_pDontExpandItems || SfxItemState::SET != m_pDontExpandItems->
703 GetItemState( pHt->Which(), false, &pItem ) ||
704 *pItem != pHt->GetAttr() )
706 // The attribute was not previously set in this form in the
707 // paragraph, so it can only be created through insert/copy
708 // Because of that it is a candidate for DontExpand
709 pHt->SetDontExpand( true );
715 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */