nss: upgrade to release 3.73
[LibreOffice.git] / sw / source / core / edit / acorrect.cxx
blob04efdc213c472f67b4451ab89c12d7a00ecfded8
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/i18n/BreakType.hpp>
35 #include <com/sun/star/i18n/WordType.hpp>
36 #include <com/sun/star/i18n/XBreakIterator.hpp>
37 #include <com/sun/star/linguistic2/XHyphenator.hpp>
38 #include <com/sun/star/linguistic2/XHyphenatedWord.hpp>
39 #include <svl/zforlist.hxx>
40 #include <svl/zformat.hxx>
42 #include <editeng/acorrcfg.hxx>
43 #include <redline.hxx>
44 #include <IDocumentRedlineAccess.hxx>
45 #include <rootfrm.hxx>
47 using namespace ::com::sun::star;
49 namespace {
51 class PaMIntoCursorShellRing
53 SwPaM &m_rDelPam, &m_rCursor;
54 SwPaM* m_pPrevDelPam;
55 SwPaM* m_pPrevCursor;
57 static void RemoveFromRing( SwPaM& rPam, SwPaM const * pPrev );
58 public:
59 PaMIntoCursorShellRing( SwCursorShell& rSh, SwPaM& rCursor, SwPaM& rPam );
60 ~PaMIntoCursorShellRing();
65 PaMIntoCursorShellRing::PaMIntoCursorShellRing(SwCursorShell& rCSh, SwPaM& rShCursor, SwPaM& rPam)
66 : m_rDelPam(rPam)
67 , m_rCursor(rShCursor)
69 SwPaM* pShCursor = rCSh.GetCursor_();
71 m_pPrevDelPam = m_rDelPam.GetPrev();
72 m_pPrevCursor = m_rCursor.GetPrev();
74 m_rDelPam.GetRingContainer().merge(pShCursor->GetRingContainer());
75 m_rCursor.GetRingContainer().merge(pShCursor->GetRingContainer());
78 PaMIntoCursorShellRing::~PaMIntoCursorShellRing()
80 // and take out the Pam again:
81 RemoveFromRing(m_rDelPam, m_pPrevDelPam);
82 RemoveFromRing(m_rCursor, m_pPrevCursor);
85 void PaMIntoCursorShellRing::RemoveFromRing( SwPaM& rPam, SwPaM const * pPrev )
87 SwPaM* p;
88 SwPaM* pNext = &rPam;
89 do {
90 p = pNext;
91 pNext = p->GetNext();
92 p->MoveTo( &rPam );
93 } while( p != pPrev );
96 SwAutoCorrDoc::SwAutoCorrDoc( SwEditShell& rEditShell, SwPaM& rPam,
97 sal_Unicode cIns )
98 : m_rEditSh( rEditShell ), m_rCursor( rPam )
99 , m_nEndUndoCounter(0)
100 , m_bUndoIdInitialized( cIns == 0 )
104 SwAutoCorrDoc::~SwAutoCorrDoc()
106 for (int i = 0; i < m_nEndUndoCounter; ++i)
108 m_rEditSh.EndUndo();
112 void SwAutoCorrDoc::DeleteSel( SwPaM& rDelPam )
114 // this should work with plain SwPaM as well because start and end
115 // are always in same node, but since there is GetRanges already...
116 std::vector<std::shared_ptr<SwUnoCursor>> ranges;
117 if (sw::GetRanges(ranges, *m_rEditSh.GetDoc(), rDelPam))
119 DeleteSelImpl(rDelPam);
121 else
123 for (auto const& pCursor : ranges)
125 DeleteSelImpl(*pCursor);
130 void SwAutoCorrDoc::DeleteSelImpl(SwPaM & rDelPam)
132 SwDoc* pDoc = m_rEditSh.GetDoc();
133 if( pDoc->IsAutoFormatRedline() )
135 // so that also the DelPam be moved, include it in the
136 // Shell-Cursr-Ring !!
137 // ??? is that really necessary - this should never join nodes, so Update should be enough?
138 // PaMIntoCursorShellRing aTmp( rEditSh, rCursor, rDelPam );
139 assert(rDelPam.GetPoint()->nNode == rDelPam.GetMark()->nNode);
140 pDoc->getIDocumentContentOperations().DeleteAndJoin( rDelPam );
142 else
144 pDoc->getIDocumentContentOperations().DeleteRange( rDelPam );
148 bool SwAutoCorrDoc::Delete( sal_Int32 nStt, sal_Int32 nEnd )
150 SwTextNode const*const pTextNd = m_rCursor.GetNode().GetTextNode();
151 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
152 pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
153 assert(pFrame);
154 SwPaM aSel(pFrame->MapViewToModelPos(TextFrameIndex(nStt)),
155 pFrame->MapViewToModelPos(TextFrameIndex(nEnd)));
156 DeleteSel( aSel );
158 if( m_bUndoIdInitialized )
159 m_bUndoIdInitialized = true;
160 return true;
163 bool SwAutoCorrDoc::Insert( sal_Int32 nPos, const OUString& rText )
165 SwTextNode const*const pTextNd = m_rCursor.GetNode().GetTextNode();
166 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
167 pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
168 assert(pFrame);
169 SwPaM aPam(pFrame->MapViewToModelPos(TextFrameIndex(nPos)));
170 m_rEditSh.GetDoc()->getIDocumentContentOperations().InsertString( aPam, rText );
171 if( !m_bUndoIdInitialized )
173 m_bUndoIdInitialized = true;
174 if( 1 == rText.getLength() )
176 m_rEditSh.StartUndo( SwUndoId::AUTOCORRECT );
177 ++m_nEndUndoCounter;
180 return true;
183 bool SwAutoCorrDoc::Replace( sal_Int32 nPos, const OUString& rText )
185 return ReplaceRange( nPos, rText.getLength(), rText );
188 bool SwAutoCorrDoc::ReplaceRange( sal_Int32 nPos, sal_Int32 nSourceLength, const OUString& rText )
190 assert(nSourceLength == 1); // sw_redlinehide: this is currently the case,
191 // and ensures that the replace range cannot *contain* delete redlines,
192 // so we don't need something along the lines of:
193 // if (sw::GetRanges(ranges, *rEditSh.GetDoc(), aPam))
194 // ReplaceImpl(...)
195 // else
196 // ReplaceImpl(ranges.begin())
197 // for (ranges.begin() + 1; ranges.end(); )
198 // DeleteImpl(*it)
200 SwTextNode * const pNd = m_rCursor.GetNode().GetTextNode();
201 if ( !pNd )
203 return false;
206 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
207 pNd->getLayoutFrame(m_rEditSh.GetLayout())));
208 assert(pFrame);
209 std::pair<SwTextNode *, sal_Int32> const pos(pFrame->MapViewToModel(TextFrameIndex(nPos)));
211 SwPaM* pPam = &m_rCursor;
212 if (pPam->GetPoint()->nNode != *pos.first
213 || pPam->GetPoint()->nContent != pos.second)
215 pPam = new SwPaM(*pos.first, pos.second);
218 // text attributes with dummy characters must not be replaced!
219 bool bDoReplace = true;
220 sal_Int32 const nLen = rText.getLength();
221 for (sal_Int32 n = 0; n < nLen && n + nPos < pFrame->GetText().getLength(); ++n)
223 sal_Unicode const Char = pFrame->GetText()[n + nPos];
224 if (CH_TXTATR_BREAKWORD == Char || CH_TXTATR_INWORD == Char)
226 assert(pFrame->MapViewToModel(TextFrameIndex(n+nPos)).first->GetTextAttrForCharAt(pFrame->MapViewToModel(TextFrameIndex(n+nPos)).second));
227 bDoReplace = false;
228 break;
232 // tdf#83419 avoid bad autocorrect with visible redlines
233 // e.g. replacing the first letter of the tracked deletion
234 // with its capitalized (and not deleted) version.
235 if ( bDoReplace && !pFrame->getRootFrame()->IsHideRedlines() &&
236 m_rEditSh.GetDoc()->getIDocumentRedlineAccess().HasRedline( *pPam, RedlineType::Delete, /*bStartOrEndInRange=*/false ) )
238 bDoReplace = false;
241 if ( bDoReplace )
243 SwDoc* pDoc = m_rEditSh.GetDoc();
245 if( pDoc->IsAutoFormatRedline() )
247 if (nPos == pFrame->GetText().getLength()) // at the End do an Insert
249 pDoc->getIDocumentContentOperations().InsertString( *pPam, rText );
251 else
253 assert(pos.second != pos.first->Len()); // must be _before_ char
254 PaMIntoCursorShellRing aTmp( m_rEditSh, m_rCursor, *pPam );
256 pPam->SetMark();
257 pPam->GetPoint()->nContent = std::min<sal_Int32>(
258 pos.first->GetText().getLength(), pos.second + nSourceLength);
259 pDoc->getIDocumentContentOperations().ReplaceRange( *pPam, rText, false );
260 pPam->Exchange();
261 pPam->DeleteMark();
264 else
266 pPam->SetMark();
267 pPam->GetPoint()->nContent = std::min<sal_Int32>(
268 pos.first->GetText().getLength(), pos.second + nSourceLength);
269 pDoc->getIDocumentContentOperations().ReplaceRange( *pPam, rText, false );
270 pPam->Exchange();
271 pPam->DeleteMark();
274 if( m_bUndoIdInitialized )
276 m_bUndoIdInitialized = true;
277 if( 1 == rText.getLength() )
279 m_rEditSh.StartUndo( SwUndoId::AUTOCORRECT );
280 ++m_nEndUndoCounter;
285 if( pPam != &m_rCursor )
286 delete pPam;
288 return true;
291 void SwAutoCorrDoc::SetAttr( sal_Int32 nStt, sal_Int32 nEnd, sal_uInt16 nSlotId,
292 SfxPoolItem& rItem )
294 SwTextNode const*const pTextNd = m_rCursor.GetNode().GetTextNode();
295 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
296 pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
297 assert(pFrame);
298 SwPaM aPam(pFrame->MapViewToModelPos(TextFrameIndex(nStt)),
299 pFrame->MapViewToModelPos(TextFrameIndex(nEnd)));
301 SfxItemPool& rPool = m_rEditSh.GetDoc()->GetAttrPool();
302 sal_uInt16 nWhich = rPool.GetWhich( nSlotId, false );
303 if( nWhich )
305 rItem.SetWhich( nWhich );
307 SfxItemSet aSet( rPool, aCharFormatSetRange );
308 SetAllScriptItem( aSet, rItem );
310 m_rEditSh.GetDoc()->SetFormatItemByAutoFormat( aPam, aSet );
312 if( m_bUndoIdInitialized )
313 m_bUndoIdInitialized = true;
317 bool SwAutoCorrDoc::SetINetAttr( sal_Int32 nStt, sal_Int32 nEnd, const OUString& rURL )
319 SwTextNode const*const pTextNd = m_rCursor.GetNode().GetTextNode();
320 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
321 pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
322 assert(pFrame);
323 SwPaM aPam(pFrame->MapViewToModelPos(TextFrameIndex(nStt)),
324 pFrame->MapViewToModelPos(TextFrameIndex(nEnd)));
326 SfxItemSet aSet( m_rEditSh.GetDoc()->GetAttrPool(),
327 svl::Items<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT>{} );
328 aSet.Put( SwFormatINetFormat( rURL, OUString() ));
329 m_rEditSh.GetDoc()->SetFormatItemByAutoFormat( aPam, aSet );
330 if( m_bUndoIdInitialized )
331 m_bUndoIdInitialized = true;
332 return true;
335 /** Return the text of a previous paragraph
337 * @param bAtNormalPos If <true> before the normal insert position; if <false> in which the
338 * corrected word was inserted. (Doesn't need to be the same paragraph!)
339 * @return text or 0, if previous paragraph does not exists or there are only blankness
341 OUString const* SwAutoCorrDoc::GetPrevPara(bool const bAtNormalPos)
343 OUString const* pStr(nullptr);
345 if( bAtNormalPos || !m_pIndex )
347 m_pIndex.reset(new SwNodeIndex(m_rCursor.GetPoint()->nNode));
349 sw::GotoPrevLayoutTextFrame(*m_pIndex, m_rEditSh.GetLayout());
351 SwTextFrame const* pFrame(nullptr);
352 for (SwTextNode * pTextNd = m_pIndex->GetNode().GetTextNode();
353 pTextNd; pTextNd = m_pIndex->GetNode().GetTextNode())
355 pFrame = static_cast<SwTextFrame const*>(
356 pTextNd->getLayoutFrame(m_rEditSh.GetLayout()));
357 if (pFrame && !pFrame->GetText().isEmpty())
359 break;
361 sw::GotoPrevLayoutTextFrame(*m_pIndex, m_rEditSh.GetLayout());
363 if (pFrame && 0 == pFrame->GetTextNodeForParaProps()->GetAttrOutlineLevel())
364 pStr = & pFrame->GetText();
366 if( m_bUndoIdInitialized )
367 m_bUndoIdInitialized = true;
369 return pStr;
372 bool SwAutoCorrDoc::ChgAutoCorrWord( sal_Int32& rSttPos, sal_Int32 nEndPos,
373 SvxAutoCorrect& rACorrect,
374 OUString* pPara )
376 if( m_bUndoIdInitialized )
377 m_bUndoIdInitialized = true;
379 // Found a beginning of a paragraph or a Blank,
380 // search for the word Kuerzel (Shortcut) in the Auto
381 SwTextNode* pTextNd = m_rCursor.GetNode().GetTextNode();
382 OSL_ENSURE( pTextNd, "where is the TextNode?" );
384 bool bRet = false;
385 if( nEndPos == rSttPos )
386 return bRet;
388 LanguageType eLang = GetLanguage(nEndPos);
389 if(LANGUAGE_SYSTEM == eLang)
390 eLang = GetAppLanguage();
391 LanguageTag aLanguageTag( eLang);
393 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
394 pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
395 assert(pFrame);
397 const OUString sFrameText = pFrame->GetText();
398 const SvxAutocorrWord* pFnd = rACorrect.SearchWordsInList(
399 sFrameText, rSttPos, nEndPos, *this, aLanguageTag);
400 SwDoc* pDoc = m_rEditSh.GetDoc();
401 if( pFnd )
403 // replace also last colon of keywords surrounded by colons (for example, ":name:")
404 const bool replaceLastChar = sFrameText.getLength() > nEndPos && pFnd->GetShort()[0] == ':'
405 && pFnd->GetShort().endsWith(":");
407 SwPosition aStartPos( pFrame->MapViewToModelPos(TextFrameIndex(rSttPos) ));
408 SwPosition aEndPos( pFrame->MapViewToModelPos(TextFrameIndex(nEndPos + (replaceLastChar ? 1 : 0))) );
409 SwPaM aPam(aStartPos, aEndPos);
411 // don't replace, if a redline starts or ends within the original text
412 if ( pDoc->getIDocumentRedlineAccess().HasRedline( aPam, RedlineType::Any, /*bStartOrEndInRange=*/true ) )
414 return bRet;
417 if( pFnd->IsTextOnly() )
419 //JP 22.04.99: Bug 63883 - Special treatment for dots.
420 const bool bLastCharIsPoint
421 = nEndPos < sFrameText.getLength() && ('.' == sFrameText[nEndPos]);
422 if( !bLastCharIsPoint || pFnd->GetLong().isEmpty() ||
423 '.' != pFnd->GetLong()[ pFnd->GetLong().getLength() - 1 ] )
425 // replace the selection
426 std::vector<std::shared_ptr<SwUnoCursor>> ranges;
427 if (sw::GetRanges(ranges, *m_rEditSh.GetDoc(), aPam))
429 pDoc->getIDocumentContentOperations().ReplaceRange(aPam, pFnd->GetLong(), false);
430 bRet = true;
432 else if (!ranges.empty())
434 assert(ranges.front()->GetPoint()->nNode == ranges.front()->GetMark()->nNode);
435 pDoc->getIDocumentContentOperations().ReplaceRange(
436 *ranges.front(), pFnd->GetLong(), false);
437 for (auto it = ranges.begin() + 1; it != ranges.end(); ++it)
439 DeleteSelImpl(**it);
441 bRet = true;
444 // tdf#83260 After calling sw::DocumentContentOperationsManager::ReplaceRange
445 // pTextNd may become invalid when change tracking is on and Edit -> Track Changes -> Show == OFF.
446 // ReplaceRange shows changes, this moves deleted nodes from special section to document.
447 // Then Show mode is disabled again. As a result pTextNd may be invalidated.
448 pTextNd = m_rCursor.GetNode().GetTextNode();
451 else
453 SwTextBlocks aTBlks( rACorrect.GetAutoCorrFileName( aLanguageTag, false, true ));
454 sal_uInt16 nPos = aTBlks.GetIndex( pFnd->GetShort() );
455 if( USHRT_MAX != nPos && aTBlks.BeginGetDoc( nPos ) )
457 DeleteSel( aPam );
458 pDoc->DontExpandFormat( *aPam.GetPoint() );
460 if( pPara )
462 OSL_ENSURE( !m_pIndex, "who has not deleted his Index?" );
463 m_pIndex.reset(new SwNodeIndex( m_rCursor.GetPoint()->nNode ));
464 sw::GotoPrevLayoutTextFrame(*m_pIndex, m_rEditSh.GetLayout());
467 SwDoc* pAutoDoc = aTBlks.GetDoc();
468 SwNodeIndex aSttIdx( pAutoDoc->GetNodes().GetEndOfExtras(), 1 );
469 SwContentNode* pContentNd = pAutoDoc->GetNodes().GoNext( &aSttIdx );
470 SwPaM aCpyPam( aSttIdx );
472 const SwTableNode* pTableNd = pContentNd->FindTableNode();
473 if( pTableNd )
475 aCpyPam.GetPoint()->nContent.Assign( nullptr, 0 );
476 aCpyPam.GetPoint()->nNode = *pTableNd;
478 aCpyPam.SetMark();
480 // then until the end of the Nodes Array
481 aCpyPam.GetPoint()->nNode.Assign( pAutoDoc->GetNodes().GetEndOfContent(), -1 );
482 pContentNd = aCpyPam.GetContentNode();
483 aCpyPam.GetPoint()->nContent.Assign(
484 pContentNd, pContentNd ? pContentNd->Len() : 0);
486 SwDontExpandItem aExpItem;
487 aExpItem.SaveDontExpandItems( *aPam.GetPoint() );
489 pAutoDoc->getIDocumentContentOperations().CopyRange(aCpyPam, *aPam.GetPoint(), SwCopyFlags::CheckPosInFly);
491 aExpItem.RestoreDontExpandItems( *aPam.GetPoint() );
493 if( pPara )
495 sw::GotoNextLayoutTextFrame(*m_pIndex, m_rEditSh.GetLayout());
496 pTextNd = m_pIndex->GetNode().GetTextNode();
498 bRet = true;
500 aTBlks.EndGetDoc();
504 if( bRet && pPara && pTextNd )
506 SwTextFrame const*const pNewFrame(static_cast<SwTextFrame const*>(
507 pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
508 *pPara = pNewFrame->GetText();
511 return bRet;
514 bool SwAutoCorrDoc::TransliterateRTLWord( sal_Int32& rSttPos, sal_Int32 nEndPos )
516 if( m_bUndoIdInitialized )
517 m_bUndoIdInitialized = true;
519 SwTextNode* pTextNd = m_rCursor.GetNode().GetTextNode();
520 OSL_ENSURE( pTextNd, "where is the TextNode?" );
522 bool bRet = false;
523 if( nEndPos == rSttPos )
524 return bRet;
526 LanguageType eLang = GetLanguage(nEndPos);
527 if(LANGUAGE_SYSTEM == eLang)
528 eLang = GetAppLanguage();
529 LanguageTag aLanguageTag(eLang);
531 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
532 pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
533 assert(pFrame);
535 const OUString sFrameText = pFrame->GetText();
536 SwDoc* pDoc = m_rEditSh.GetDoc();
537 if ( pFrame->IsRightToLeft() )
539 // transliterate to Old Hungarian using Numbertext via NatNum12 number format modifier
540 OUString sWord(sFrameText.copy(rSttPos, nEndPos - rSttPos));
541 // Consonant disambiguation using hyphenation
542 uno::Reference< linguistic2::XHyphenator > xHyph;
543 xHyph = ::GetHyphenator();
544 OUStringBuffer sDisambiguatedWord;
546 const ::css::uno::Sequence< ::css::beans::PropertyValue > aProperties;
547 css::uno::Reference< css::linguistic2::XHyphenatedWord > xHyphWord;
548 for (int i = 0; i+1 < sWord.getLength(); i++ )
550 xHyphWord = xHyph->hyphenate( sWord,
551 aLanguageTag.getLocale(),
553 aProperties );
554 // insert ZWSP at a hyphenation point, if it's not an alternative one (i.e. ssz->sz-sz)
555 if (xHyphWord.is() && xHyphWord->getHyphenationPos()+1 == i && !xHyphWord->isAlternativeSpelling())
557 sDisambiguatedWord.append(CHAR_ZWSP);
559 sDisambiguatedWord.append(sWord[i]);
561 sDisambiguatedWord.append(sWord[sWord.getLength()-1]);
563 SvNumberFormatter* pFormatter = pDoc->GetNumberFormatter();
564 OUString sConverted;
565 if (pFormatter && !sWord.isEmpty())
567 const Color* pColor = nullptr;
568 // Send text as NatNum12 prefix
569 OUString sPrefix("[NatNum12 " + sDisambiguatedWord.makeStringAndClear() + "]0");
570 if (pFormatter->GetPreviewString(sPrefix, 0, sConverted, &pColor, LANGUAGE_USER_HUNGARIAN_ROVAS))
571 bRet = true;
574 SwPaM aPam(pFrame->MapViewToModelPos(TextFrameIndex(rSttPos)),
575 pFrame->MapViewToModelPos(TextFrameIndex(nEndPos)));
576 if (bRet && nEndPos <= sFrameText.getLength())
577 pDoc->getIDocumentContentOperations().ReplaceRange(aPam, sConverted, false);
580 return bRet;
583 // Called by the functions:
584 // - FnCapitalStartWord
585 // - FnCapitalStartSentence
586 // after the exchange of characters. Then the words, if necessary, can be inserted
587 // into the exception list.
588 void SwAutoCorrDoc::SaveCpltSttWord( ACFlags nFlag, sal_Int32 nPos,
589 const OUString& rExceptWord,
590 sal_Unicode cChar )
592 sal_uLong nNode = m_pIndex ? m_pIndex->GetIndex() : m_rCursor.GetPoint()->nNode.GetIndex();
593 LanguageType eLang = GetLanguage(nPos);
594 m_rEditSh.GetDoc()->SetAutoCorrExceptWord( std::make_unique<SwAutoCorrExceptWord>( nFlag,
595 nNode, nPos, rExceptWord, cChar, eLang ));
598 LanguageType SwAutoCorrDoc::GetLanguage( sal_Int32 nPos ) const
600 LanguageType eRet = LANGUAGE_SYSTEM;
602 SwTextNode* pNd = m_rCursor.GetPoint()->nNode.GetNode().GetTextNode();
604 if( pNd )
606 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
607 pNd->getLayoutFrame(m_rEditSh.GetLayout())));
608 assert(pFrame);
609 eRet = pFrame->GetLangOfChar(TextFrameIndex(nPos), 0, true);
611 if(LANGUAGE_SYSTEM == eRet)
612 eRet = GetAppLanguage();
613 return eRet;
616 void SwAutoCorrExceptWord::CheckChar( const SwPosition& rPos, sal_Unicode cChr )
618 // test only if this is an improvement.
619 // If yes, then add the word to the list.
620 if (m_cChar == cChr && rPos.nNode.GetIndex() == m_nNode && rPos.nContent.GetIndex() == m_nContent)
622 // get the current autocorrection:
623 SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect();
625 // then add to the list:
626 if (ACFlags::CapitalStartWord & m_nFlags)
627 pACorr->AddWrtSttException(m_sWord, m_eLanguage);
628 else if (ACFlags::CapitalStartSentence & m_nFlags)
629 pACorr->AddCplSttException(m_sWord, m_eLanguage);
633 bool SwAutoCorrExceptWord::CheckDelChar( const SwPosition& rPos )
635 bool bRet = false;
636 if (!m_bDeleted && rPos.nNode.GetIndex() == m_nNode && rPos.nContent.GetIndex() == m_nContent)
637 m_bDeleted = bRet = true;
638 return bRet;
641 SwDontExpandItem::~SwDontExpandItem()
645 void SwDontExpandItem::SaveDontExpandItems( const SwPosition& rPos )
647 const SwTextNode* pTextNd = rPos.nNode.GetNode().GetTextNode();
648 if( pTextNd )
650 m_pDontExpandItems.reset( new SfxItemSet( const_cast<SwDoc&>(pTextNd->GetDoc()).GetAttrPool(),
651 aCharFormatSetRange ) );
652 const sal_Int32 n = rPos.nContent.GetIndex();
653 if (!pTextNd->GetParaAttr( *m_pDontExpandItems, n, n,
654 n != pTextNd->GetText().getLength() ))
656 m_pDontExpandItems.reset();
661 void SwDontExpandItem::RestoreDontExpandItems( const SwPosition& rPos )
663 SwTextNode* pTextNd = rPos.nNode.GetNode().GetTextNode();
664 if( !pTextNd )
665 return;
667 const sal_Int32 nStart = rPos.nContent.GetIndex();
668 if( nStart == pTextNd->GetText().getLength() )
669 pTextNd->FormatToTextAttr( pTextNd );
671 if( !(pTextNd->GetpSwpHints() && pTextNd->GetpSwpHints()->Count()) )
672 return;
674 const size_t nSize = pTextNd->GetpSwpHints()->Count();
675 sal_Int32 nAttrStart;
677 for( size_t n = 0; n < nSize; ++n )
679 SwTextAttr* pHt = pTextNd->GetpSwpHints()->Get( n );
680 nAttrStart = pHt->GetStart();
681 if( nAttrStart > nStart ) // beyond the area
682 break;
684 const sal_Int32* pAttrEnd;
685 if( nullptr != ( pAttrEnd = pHt->End() ) &&
686 ( ( nAttrStart < nStart &&
687 ( pHt->DontExpand() ? nStart < *pAttrEnd
688 : nStart <= *pAttrEnd )) ||
689 ( nStart == nAttrStart &&
690 ( nAttrStart == *pAttrEnd || !nStart ))) )
692 const SfxPoolItem* pItem;
693 if( !m_pDontExpandItems || SfxItemState::SET != m_pDontExpandItems->
694 GetItemState( pHt->Which(), false, &pItem ) ||
695 *pItem != pHt->GetAttr() )
697 // The attribute was not previously set in this form in the
698 // paragraph, so it can only be created through insert/copy
699 // Because of that it is a candidate for DontExpand
700 pHt->SetDontExpand( true );
706 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */