xref: /core/sw/source/core/edit/acorrect.cxx (revision 76c96ca7)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <hintids.hxx>
21 
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>
38 
39 #include <editeng/acorrcfg.hxx>
40 #include <IDocumentRedlineAccess.hxx>
41 #include <rootfrm.hxx>
42 
43 using namespace ::com::sun::star;
44 
45 namespace {
46 
47 class PaMIntoCursorShellRing
48 {
49     SwPaM &m_rDelPam, &m_rCursor;
50     SwPaM* m_pPrevDelPam;
51     SwPaM* m_pPrevCursor;
52 
53     static void RemoveFromRing( SwPaM& rPam, SwPaM const * pPrev );
54 public:
55     PaMIntoCursorShellRing( SwCursorShell& rSh, SwPaM& rCursor, SwPaM& rPam );
56     ~PaMIntoCursorShellRing();
57 };
58 
59 }
60 
PaMIntoCursorShellRing(SwCursorShell & rCSh,SwPaM & rShCursor,SwPaM & rPam)61 PaMIntoCursorShellRing::PaMIntoCursorShellRing(SwCursorShell& rCSh, SwPaM& rShCursor, SwPaM& rPam)
62     : m_rDelPam(rPam)
63     , m_rCursor(rShCursor)
64 {
65     SwPaM* pShCursor = rCSh.GetCursor_();
66 
67     m_pPrevDelPam = m_rDelPam.GetPrev();
68     m_pPrevCursor = m_rCursor.GetPrev();
69 
70     m_rDelPam.GetRingContainer().merge(pShCursor->GetRingContainer());
71     m_rCursor.GetRingContainer().merge(pShCursor->GetRingContainer());
72 }
73 
~PaMIntoCursorShellRing()74 PaMIntoCursorShellRing::~PaMIntoCursorShellRing()
75 {
76     // and take out the Pam again:
77     RemoveFromRing(m_rDelPam, m_pPrevDelPam);
78     RemoveFromRing(m_rCursor, m_pPrevCursor);
79 }
80 
RemoveFromRing(SwPaM & rPam,SwPaM const * pPrev)81 void PaMIntoCursorShellRing::RemoveFromRing( SwPaM& rPam, SwPaM const * pPrev )
82 {
83     SwPaM* p;
84     SwPaM* pNext = &rPam;
85     do {
86         p = pNext;
87         pNext = p->GetNext();
88         p->MoveTo( &rPam );
89     } while( p != pPrev );
90 }
91 
SwAutoCorrDoc(SwEditShell & rEditShell,SwPaM & rPam,sal_Unicode cIns)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 )
97 {
98 }
99 
~SwAutoCorrDoc()100 SwAutoCorrDoc::~SwAutoCorrDoc()
101 {
102     for (int i = 0; i < m_nEndUndoCounter; ++i)
103     {
104         m_rEditSh.EndUndo();
105     }
106 }
107 
DeleteSel(SwPaM & rDelPam)108 void SwAutoCorrDoc::DeleteSel( SwPaM& rDelPam )
109 {
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))
114     {
115         DeleteSelImpl(rDelPam);
116     }
117     else
118     {
119         for (auto const& pCursor : ranges)
120         {
121             DeleteSelImpl(*pCursor);
122         }
123     }
124 }
125 
DeleteSelImpl(SwPaM & rDelPam)126 void SwAutoCorrDoc::DeleteSelImpl(SwPaM & rDelPam)
127 {
128     SwDoc* pDoc = m_rEditSh.GetDoc();
129     if( pDoc->IsAutoFormatRedline() )
130     {
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 );
137     }
138     else
139     {
140         pDoc->getIDocumentContentOperations().DeleteRange( rDelPam );
141     }
142 }
143 
Delete(sal_Int32 nStt,sal_Int32 nEnd)144 bool SwAutoCorrDoc::Delete( sal_Int32 nStt, sal_Int32 nEnd )
145 {
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 );
153 
154     if( m_bUndoIdInitialized )
155         m_bUndoIdInitialized = true;
156     return true;
157 }
158 
Insert(sal_Int32 nPos,const OUString & rText)159 bool SwAutoCorrDoc::Insert( sal_Int32 nPos, const OUString& rText )
160 {
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 )
168     {
169         m_bUndoIdInitialized = true;
170         if( 1 == rText.getLength() )
171         {
172             m_rEditSh.StartUndo( SwUndoId::AUTOCORRECT );
173             ++m_nEndUndoCounter;
174         }
175     }
176     return true;
177 }
178 
Replace(sal_Int32 nPos,const OUString & rText)179 bool SwAutoCorrDoc::Replace( sal_Int32 nPos, const OUString& rText )
180 {
181     return ReplaceRange( nPos, rText.getLength(), rText );
182 }
183 
ReplaceRange(sal_Int32 nPos,sal_Int32 nSourceLength,const OUString & rText)184 bool SwAutoCorrDoc::ReplaceRange( sal_Int32 nPos, sal_Int32 nSourceLength, const OUString& rText )
185 {
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)
195 
196     SwTextNode * const pNd = m_rCursor.GetPointNode().GetTextNode();
197     if ( !pNd )
198     {
199         return false;
200     }
201 
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)));
206 
207     SwPaM* pPam = &m_rCursor;
208     if (pPam->GetPoint()->GetNode() != *pos.first
209         || pPam->GetPoint()->GetContentIndex() != pos.second)
210     {
211         pPam = new SwPaM(*pos.first, pos.second);
212     }
213 
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)
218     {
219         sal_Unicode const Char = pFrame->GetText()[n + nPos];
220         if (CH_TXTATR_BREAKWORD == Char || CH_TXTATR_INWORD == Char)
221         {
222             assert(pFrame->MapViewToModel(TextFrameIndex(n+nPos)).first->GetTextAttrForCharAt(pFrame->MapViewToModel(TextFrameIndex(n+nPos)).second));
223             bDoReplace = false;
224             break;
225         }
226     }
227 
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 ) )
233     {
234         bDoReplace = false;
235     }
236 
237     if ( bDoReplace )
238     {
239         SwDoc* pDoc = m_rEditSh.GetDoc();
240 
241         if( pDoc->IsAutoFormatRedline() )
242         {
243             if (nPos == pFrame->GetText().getLength()) // at the End do an Insert
244             {
245                 pDoc->getIDocumentContentOperations().InsertString( *pPam, rText );
246             }
247             else
248             {
249                 assert(pos.second != pos.first->Len()); // must be _before_ char
250                 PaMIntoCursorShellRing aTmp( m_rEditSh, m_rCursor, *pPam );
251 
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();
258             }
259         }
260         else
261         {
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();
268         }
269 
270         if( m_bUndoIdInitialized )
271         {
272             m_bUndoIdInitialized = true;
273             if( 1 == rText.getLength() )
274             {
275                 m_rEditSh.StartUndo( SwUndoId::AUTOCORRECT );
276                 ++m_nEndUndoCounter;
277             }
278         }
279     }
280 
281     if( pPam != &m_rCursor )
282         delete pPam;
283 
284     return true;
285 }
286 
SetAttr(sal_Int32 nStt,sal_Int32 nEnd,sal_uInt16 nSlotId,SfxPoolItem & rItem)287 void SwAutoCorrDoc::SetAttr( sal_Int32 nStt, sal_Int32 nEnd, sal_uInt16 nSlotId,
288                                         SfxPoolItem& rItem )
289 {
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)));
296 
297     SfxItemPool& rPool = m_rEditSh.GetDoc()->GetAttrPool();
298     sal_uInt16 nWhich = rPool.GetWhichIDFromSlotID( nSlotId, false );
299     if( nWhich )
300     {
301         rItem.SetWhich( nWhich );
302 
303         SfxItemSet aSet( rPool, aCharFormatSetRange );
304         SetAllScriptItem( aSet, rItem );
305 
306         m_rEditSh.GetDoc()->SetFormatItemByAutoFormat( aPam, aSet );
307 
308         if( m_bUndoIdInitialized )
309             m_bUndoIdInitialized = true;
310     }
311 }
312 
SetINetAttr(sal_Int32 nStt,sal_Int32 nEnd,const OUString & rURL)313 bool SwAutoCorrDoc::SetINetAttr( sal_Int32 nStt, sal_Int32 nEnd, const OUString& rURL )
314 {
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)));
321 
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;
329 }
330 
331 /** Return the text of a previous paragraph
332  *
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
336  */
GetPrevPara(bool const bAtNormalPos)337 OUString const* SwAutoCorrDoc::GetPrevPara(bool const bAtNormalPos)
338 {
339     OUString const* pStr(nullptr);
340 
341     if( bAtNormalPos || !m_oIndex )
342     {
343         m_oIndex.emplace(m_rCursor.GetPoint()->GetNode());
344     }
345     sw::GotoPrevLayoutTextFrame(*m_oIndex, m_rEditSh.GetLayout());
346 
347     SwTextFrame const* pFrame(nullptr);
348     for (SwTextNode * pTextNd = m_oIndex->GetNode().GetTextNode();
349              pTextNd; pTextNd = m_oIndex->GetNode().GetTextNode())
350     {
351         pFrame = static_cast<SwTextFrame const*>(
352                 pTextNd->getLayoutFrame(m_rEditSh.GetLayout()));
353         if (pFrame && !pFrame->GetText().isEmpty())
354         {
355             break;
356         }
357         sw::GotoPrevLayoutTextFrame(*m_oIndex, m_rEditSh.GetLayout());
358     }
359     if (pFrame && !pFrame->GetText().isEmpty() &&
360         0 == pFrame->GetTextNodeForParaProps()->GetAttrOutlineLevel())
361     {
362         pStr = & pFrame->GetText();
363     }
364 
365     if( m_bUndoIdInitialized )
366         m_bUndoIdInitialized = true;
367 
368     return pStr;
369 }
370 
ChgAutoCorrWord(sal_Int32 & rSttPos,sal_Int32 nEndPos,SvxAutoCorrect & rACorrect,OUString * pPara)371 bool SwAutoCorrDoc::ChgAutoCorrWord( sal_Int32& rSttPos, sal_Int32 nEndPos,
372                                          SvxAutoCorrect& rACorrect,
373                                          OUString* pPara )
374 {
375     if( m_bUndoIdInitialized )
376         m_bUndoIdInitialized = true;
377 
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?" );
382 
383     bool bRet = false;
384     if( nEndPos == rSttPos )
385         return bRet;
386 
387     LanguageType eLang = GetLanguage(nEndPos);
388     if(LANGUAGE_SYSTEM == eLang)
389         eLang = GetAppLanguage();
390     LanguageTag aLanguageTag( eLang);
391 
392     SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
393                 pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
394     assert(pFrame);
395 
396     OUString sFrameText = pFrame->GetText();
397     sal_Int32 sttPos = rSttPos;
398     auto pStatus = rACorrect.SearchWordsInList(sFrameText, sttPos, nEndPos,
399                                                *this, aLanguageTag);
400 
401     SwDoc* pDoc = m_rEditSh.GetDoc();
402     if( pStatus )
403     {
404         sal_Int32 minSttPos = sttPos;
405         do {
406             const SvxAutocorrWord* pFnd = pStatus->GetAutocorrWord();
407             // replace also last colon of keywords surrounded by colons
408             // (for example, ":name:")
409             const bool replaceLastChar = sFrameText.getLength() > nEndPos
410                                          && pFnd->GetShort()[0] == ':'
411                                          && pFnd->GetShort().endsWith(":");
412 
413             SwPosition aStartPos( pFrame->MapViewToModelPos(TextFrameIndex(sttPos)) );
414             SwPosition aEndPos( pFrame->MapViewToModelPos(TextFrameIndex(nEndPos + (replaceLastChar ? 1 : 0))) );
415             SwPaM aPam(aStartPos, aEndPos);
416 
417             // don't replace, if a redline starts or ends within the original text
418             if ( pDoc->getIDocumentRedlineAccess().HasRedline( aPam, RedlineType::Any, /*bStartOrEndInRange=*/true ) )
419             {
420                 return bRet;
421             }
422 
423             if( pFnd->IsTextOnly() )
424             {
425                 //JP 22.04.99: Bug 63883 - Special treatment for dots.
426                 const bool bLastCharIsPoint
427                     = nEndPos < sFrameText.getLength() && ('.' == sFrameText[nEndPos]);
428                 if( !bLastCharIsPoint || pFnd->GetLong().isEmpty() ||
429                     '.' != pFnd->GetLong()[ pFnd->GetLong().getLength() - 1 ] )
430                 {
431                     // replace the selection
432                     std::vector<std::shared_ptr<SwUnoCursor>> ranges;
433                     bool noRedlines = sw::GetRanges(ranges,
434                                                     *m_rEditSh.GetDoc(), aPam);
435                     OSL_ENSURE(noRedlines, "redlines should have been blocked.");
436                     OSL_ENSURE(ranges.empty(), "no redlines expected here.");
437 
438                     pDoc->getIDocumentContentOperations().ReplaceRange(
439                             aPam, pFnd->GetLong(), false);
440                     nEndPos = sttPos + pFnd->GetLong().getLength();
441                     bRet = true;
442 
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();
448                 }
449             }
450             else
451             {
452                 SwTextBlocks aTBlks( rACorrect.GetAutoCorrFileName( aLanguageTag, false, true ));
453                 sal_uInt16 nPos = aTBlks.GetIndex( pFnd->GetShort() );
454                 if( USHRT_MAX != nPos && aTBlks.BeginGetDoc( nPos ) )
455                 {
456                     DeleteSel( aPam );
457                     pDoc->DontExpandFormat( *aPam.GetPoint() );
458 
459                     if( pPara )
460                     {
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());
464                     }
465 
466                     SwDoc* pAutoDoc = aTBlks.GetDoc();
467                     SwNodeIndex aSttIdx( pAutoDoc->GetNodes().GetEndOfExtras(), 1 );
468                     SwContentNode* pContentNd = SwNodes::GoNext(&aSttIdx);
469                     SwPaM aCpyPam( aSttIdx );
470 
471                     const SwTableNode* pTableNd = pContentNd->FindTableNode();
472                     if( pTableNd )
473                     {
474                         aCpyPam.GetPoint()->Assign( *pTableNd );
475                     }
476                     aCpyPam.SetMark();
477 
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() );
483 
484                     SwDontExpandItem aExpItem;
485                     aExpItem.SaveDontExpandItems( *aPam.GetPoint() );
486 
487                     pAutoDoc->getIDocumentContentOperations().CopyRange(aCpyPam, *aPam.GetPoint(), SwCopyFlags::CheckPosInFly);
488 
489                     aExpItem.RestoreDontExpandItems( *aPam.GetPoint() );
490 
491                     if( pPara )
492                     {
493                         sw::GotoNextLayoutTextFrame(*m_oIndex, m_rEditSh.GetLayout());
494                         pTextNd = m_oIndex->GetNode().GetTextNode();
495                     }
496                     bRet = true;
497                 }
498                 aTBlks.EndGetDoc();
499             }
500             if( sttPos < minSttPos) {
501                 minSttPos = sttPos;
502             }
503             sttPos = rSttPos;
504             sFrameText = pFrame->GetText();
505         } while( SvxAutoCorrect::SearchWordsNext(sFrameText, sttPos, nEndPos,
506                                                  *pStatus) );
507         rSttPos = minSttPos;
508     }
509 
510     if( bRet && pPara && pTextNd )
511     {
512         SwTextFrame const*const pNewFrame(static_cast<SwTextFrame const*>(
513                     pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
514         *pPara = pNewFrame->GetText();
515     }
516 
517     return bRet;
518 }
519 
TransliterateRTLWord(sal_Int32 & rSttPos,sal_Int32 nEndPos,bool bApply)520 bool SwAutoCorrDoc::TransliterateRTLWord( sal_Int32& rSttPos, sal_Int32 nEndPos, bool bApply )
521 {
522     if( m_bUndoIdInitialized )
523         m_bUndoIdInitialized = true;
524 
525     SwTextNode* pTextNd = m_rCursor.GetPointNode().GetTextNode();
526     OSL_ENSURE( pTextNd, "where is the TextNode?" );
527 
528     bool bRet = false;
529     if( nEndPos == rSttPos )
530         return bRet;
531 
532     LanguageType eLang = GetLanguage(nEndPos);
533     if(LANGUAGE_SYSTEM == eLang)
534         eLang = GetAppLanguage();
535     LanguageTag aLanguageTag(eLang);
536 
537     SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
538                 pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
539     assert(pFrame);
540 
541     const OUString sFrameText = pFrame->GetText();
542     SwDoc* pDoc = m_rEditSh.GetDoc();
543     if ( pFrame->IsRightToLeft() || bApply )
544     {
545         // transliterate to Old Hungarian using Numbertext via NatNum12 number format modifier
546         OUString sWord(sFrameText.copy(rSttPos, nEndPos - rSttPos));
547         // Consonant disambiguation using hyphenation
548         uno::Reference< linguistic2::XHyphenator >  xHyph;
549         xHyph = ::GetHyphenator();
550         OUStringBuffer sDisambiguatedWord;
551 
552         const ::css::uno::Sequence< ::css::beans::PropertyValue > aProperties;
553         css::uno::Reference< css::linguistic2::XHyphenatedWord >  xHyphWord;
554         for (int i = 0; i+1 < sWord.getLength(); i++ )
555         {
556             xHyphWord = xHyph->hyphenate( sWord,
557                         aLanguageTag.getLocale(),
558                         i,
559                         aProperties );
560             // insert ZWSP at a hyphenation point, if it's not an alternative one (i.e. ssz->sz-sz)
561             if (xHyphWord.is() && xHyphWord->getHyphenationPos()+1 == i && !xHyphWord->isAlternativeSpelling())
562             {
563                 sDisambiguatedWord.append(CHAR_ZWSP);
564             }
565             sDisambiguatedWord.append(sWord[i]);
566         }
567         sDisambiguatedWord.append(sWord[sWord.getLength()-1]);
568 
569         SvNumberFormatter* pFormatter = pDoc->GetNumberFormatter();
570         OUString sConverted;
571         if (pFormatter && !sWord.isEmpty())
572         {
573             const Color* pColor = nullptr;
574 
575             // Send text as NatNum12 prefix: "word" -> "[NatNum12 word]0"
576 
577             // Closing bracket doesn't allowed in NatNum parameters, remove it from transliteration:
578             // "[word]" -> "[NatNum12 [word]0"
579             bool bHasBracket = sWord.endsWith("]");
580             if ( !bHasBracket )
581                 sDisambiguatedWord.append("]");
582             OUString sPrefix("[NatNum12 " + sDisambiguatedWord + "0");
583             if (pFormatter->GetPreviewString(sPrefix, 0, sConverted, &pColor, LANGUAGE_USER_HUNGARIAN_ROVAS))
584             {
585                 if ( bHasBracket )
586                     sConverted = sConverted + "]";
587                 bRet = true;
588             }
589         }
590 
591         SwPaM aPam(pFrame->MapViewToModelPos(TextFrameIndex(rSttPos)),
592             pFrame->MapViewToModelPos(TextFrameIndex(nEndPos)));
593         if (bRet && nEndPos <= sFrameText.getLength())
594             pDoc->getIDocumentContentOperations().ReplaceRange(aPam, sConverted, false);
595     }
596 
597     return bRet;
598 }
599 
600 // Called by the functions:
601 //  - FnCapitalStartWord
602 //  - FnCapitalStartSentence
603 // after the exchange of characters. Then the words, if necessary, can be inserted
604 // into the exception list.
SaveCpltSttWord(ACFlags nFlag,sal_Int32 nPos,const OUString & rExceptWord,sal_Unicode cChar)605 void SwAutoCorrDoc::SaveCpltSttWord( ACFlags nFlag, sal_Int32 nPos,
606                                             const OUString& rExceptWord,
607                                             sal_Unicode cChar )
608 {
609     SwNodeOffset nNode = m_oIndex ? m_oIndex->GetIndex() : m_rCursor.GetPoint()->GetNodeIndex();
610     LanguageType eLang = GetLanguage(nPos);
611     m_rEditSh.GetDoc()->SetAutoCorrExceptWord( std::make_unique<SwAutoCorrExceptWord>( nFlag,
612                                         nNode, nPos, rExceptWord, cChar, eLang ));
613 }
614 
GetLanguage(sal_Int32 nPos) const615 LanguageType SwAutoCorrDoc::GetLanguage( sal_Int32 nPos ) const
616 {
617     LanguageType eRet = LANGUAGE_SYSTEM;
618 
619     SwTextNode* pNd = m_rCursor.GetPoint()->GetNode().GetTextNode();
620 
621     if( pNd )
622     {
623         SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
624                     pNd->getLayoutFrame(m_rEditSh.GetLayout())));
625         assert(pFrame);
626         eRet = pFrame->GetLangOfChar(TextFrameIndex(nPos), 0, true);
627     }
628     if(LANGUAGE_SYSTEM == eRet)
629         eRet = GetAppLanguage();
630     return eRet;
631 }
632 
CheckChar(const SwPosition & rPos,sal_Unicode cChr)633 void SwAutoCorrExceptWord::CheckChar( const SwPosition& rPos, sal_Unicode cChr )
634 {
635     // test only if this is an improvement.
636     // If yes, then add the word to the list.
637     if (m_cChar == cChr && rPos.GetNodeIndex() == m_nNode && rPos.GetContentIndex() == m_nContent)
638     {
639         // get the current autocorrection:
640         SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect();
641 
642         // then add to the list:
643         if (ACFlags::CapitalStartWord & m_nFlags)
644             pACorr->AddWordStartException(m_sWord, m_eLanguage);
645         else if (ACFlags::CapitalStartSentence & m_nFlags)
646             pACorr->AddCplSttException(m_sWord, m_eLanguage);
647     }
648 }
649 
CheckDelChar(const SwPosition & rPos)650 bool SwAutoCorrExceptWord::CheckDelChar( const SwPosition& rPos )
651 {
652     bool bRet = false;
653     if (!m_bDeleted && rPos.GetNodeIndex() == m_nNode && rPos.GetContentIndex() == m_nContent)
654         m_bDeleted = bRet = true;
655     return bRet;
656 }
657 
~SwDontExpandItem()658 SwDontExpandItem::~SwDontExpandItem()
659 {
660 }
661 
SaveDontExpandItems(const SwPosition & rPos)662 void SwDontExpandItem::SaveDontExpandItems( const SwPosition& rPos )
663 {
664     const SwTextNode* pTextNd = rPos.GetNode().GetTextNode();
665     if( pTextNd )
666     {
667         m_pDontExpandItems.reset( new SfxItemSet( const_cast<SwDoc&>(pTextNd->GetDoc()).GetAttrPool(),
668                                             aCharFormatSetRange ) );
669         const sal_Int32 n = rPos.GetContentIndex();
670         if (!pTextNd->GetParaAttr( *m_pDontExpandItems, n, n,
671                                 n != pTextNd->GetText().getLength() ))
672         {
673             m_pDontExpandItems.reset();
674         }
675     }
676 }
677 
RestoreDontExpandItems(const SwPosition & rPos)678 void SwDontExpandItem::RestoreDontExpandItems( const SwPosition& rPos )
679 {
680     SwTextNode* pTextNd = rPos.GetNode().GetTextNode();
681     if( !pTextNd )
682         return;
683 
684     const sal_Int32 nStart = rPos.GetContentIndex();
685     if( nStart == pTextNd->GetText().getLength() )
686         pTextNd->FormatToTextAttr( pTextNd );
687 
688     if( !(pTextNd->GetpSwpHints() && pTextNd->GetpSwpHints()->Count()) )
689         return;
690 
691     const size_t nSize = pTextNd->GetpSwpHints()->Count();
692     sal_Int32 nAttrStart;
693 
694     for( size_t n = 0; n < nSize; ++n )
695     {
696         SwTextAttr* pHt = pTextNd->GetpSwpHints()->Get( n );
697         nAttrStart = pHt->GetStart();
698         if( nAttrStart > nStart )       // beyond the area
699             break;
700 
701         const sal_Int32* pAttrEnd;
702         if( nullptr != ( pAttrEnd = pHt->End() ) &&
703             ( ( nAttrStart < nStart &&
704                 ( pHt->DontExpand() ? nStart < *pAttrEnd
705                                     : nStart <= *pAttrEnd )) ||
706               ( nStart == nAttrStart &&
707                 ( nAttrStart == *pAttrEnd || !nStart ))) )
708         {
709             const SfxPoolItem* pItem;
710             if( !m_pDontExpandItems || SfxItemState::SET != m_pDontExpandItems->
711                 GetItemState( pHt->Which(), false, &pItem ) ||
712                 *pItem != pHt->GetAttr() )
713             {
714                 // The attribute was not previously set in this form in the
715                 // paragraph, so it can only be created through insert/copy
716                 // Because of that it is a candidate for DontExpand
717                 pHt->SetDontExpand( true );
718             }
719         }
720     }
721 }
722 
723 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
724