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