1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <fmtanchr.hxx>
21 #include <fmtcntnt.hxx>
22 #include <acorrect.hxx>
23 #include <IDocumentRedlineAccess.hxx>
24 #include <IDocumentLayoutAccess.hxx>
25 #include <IDocumentUndoRedo.hxx>
30 #include <redline.hxx>
32 #include <rootfrm.hxx>
33 #include <splargs.hxx>
36 #include <unoflatpara.hxx>
37 #include <SwGrammarMarkUp.hxx>
42 #include <frameformats.hxx>
43 #include <unotxdoc.hxx>
46 #include <com/sun/star/linguistic2/XProofreadingIterator.hpp>
47 #include <osl/diagnose.h>
49 using namespace ::com::sun::star
;
50 using namespace ::com::sun::star::linguistic2
;
53 void RestFlyInRange( SaveFlyArr
& rArr
, const SwPosition
& rStartPos
,
54 const SwNode
* pInsertPos
, bool const isForceToStartPos
)
56 SwPosition
aPos(rStartPos
);
57 for(const SaveFly
& rSave
: rArr
)
60 SwFrameFormat
* pFormat
= rSave
.pFrameFormat
;
61 SwFormatAnchor
aAnchor( pFormat
->GetAnchor() );
63 if (rSave
.isAtInsertNode
|| isForceToStartPos
)
65 if( pInsertPos
!= nullptr )
67 if (aAnchor
.GetAnchorId() == RndStdIds::FLY_AT_PARA
)
69 assert(pInsertPos
->GetContentNode());
70 aPos
.Assign( *pInsertPos
->GetContentNode(),
75 assert(aAnchor
.GetAnchorId() == RndStdIds::FLY_AT_CHAR
);
81 aPos
.Assign(rStartPos
.GetNode());
82 assert(aPos
.GetNode().GetContentNode());
87 aPos
.Assign(rStartPos
.GetNodeIndex() + rSave
.nNdDiff
);
88 assert(aPos
.GetNode().GetContentNode());
90 rSave
.nNdDiff
== SwNodeOffset(0)
91 ? rStartPos
.GetContentIndex() + rSave
.nContentIndex
92 : rSave
.nContentIndex
);
95 aAnchor
.SetAnchor( &aPos
);
96 pFormat
->GetDoc()->GetSpzFrameFormats()->push_back(static_cast<sw::SpzFrameFormat
*>(pFormat
));
97 // SetFormatAttr should call Modify() and add it to the node
98 pFormat
->SetFormatAttr( aAnchor
);
99 SwContentNode
* pCNd
= aPos
.GetNode().GetContentNode();
100 if (pCNd
&& pCNd
->getLayoutFrame(pFormat
->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr))
101 pFormat
->MakeFrames();
103 sw::CheckAnchoredFlyConsistency(rStartPos
.GetNode().GetDoc());
106 void SaveFlyInRange( const SwNodeRange
& rRg
, SaveFlyArr
& rArr
)
108 sw::SpzFrameFormats
& rSpzs
= *rRg
.aStart
.GetNode().GetDoc().GetSpzFrameFormats();
109 sw::FrameFormats
<sw::SpzFrameFormat
*>::size_type n
= 0;
110 while (n
< rSpzs
.size())
112 auto pSpz
= rSpzs
[n
];
113 SwFormatAnchor
const*const pAnchor
= &pSpz
->GetAnchor();
114 SwNode
const*const pAnchorNode
= pAnchor
->GetAnchorNode();
116 ((RndStdIds::FLY_AT_PARA
== pAnchor
->GetAnchorId()) ||
117 (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId())) &&
118 rRg
.aStart
<= *pAnchorNode
&& *pAnchorNode
< rRg
.aEnd
.GetNode() )
120 SaveFly
aSave( pAnchorNode
->GetIndex() - rRg
.aStart
.GetIndex(),
121 (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId())
122 ? pAnchor
->GetAnchorContentOffset()
125 rArr
.push_back( aSave
);
127 // set a dummy anchor position to maintain anchoring invariants
128 SwFormatAnchor
aAnchor( pSpz
->GetAnchor() );
129 aAnchor
.SetAnchor(nullptr);
130 pSpz
->SetFormatAttr(aAnchor
);
131 rSpzs
.erase( rSpzs
.begin() + n
);
136 sw::CheckAnchoredFlyConsistency(rRg
.aStart
.GetNode().GetDoc());
139 void SaveFlyInRange( const SwPaM
& rPam
, const SwPosition
& rInsPos
,
140 SaveFlyArr
& rArr
, bool bMoveAllFlys
, SwHistory
*const pHistory
)
142 sw::SpzFrameFormats
& rFormats
= *rPam
.GetPoint()->GetNode().GetDoc().GetSpzFrameFormats();
143 sw::SpzFrameFormat
* pFormat
;
144 const SwFormatAnchor
* pAnchor
;
146 const SwPosition
* pPos
= rPam
.Start();
147 const SwNode
& rSttNd
= pPos
->GetNode();
149 SwPosition
atParaEnd(*rPam
.End());
152 assert(!rPam
.End()->GetNode().IsTextNode() // can be table end-node
153 || rPam
.End()->GetContentIndex() == rPam
.End()->GetNode().GetTextNode()->Len());
154 atParaEnd
.Adjust(SwNodeOffset(1));
157 for(sw::FrameFormats
<sw::SpzFrameFormat
*>::size_type n
= 0; n
< rFormats
.size(); ++n
)
159 pFormat
= rFormats
[n
];
160 pAnchor
= &pFormat
->GetAnchor();
161 const SwPosition
* pAPos
= pAnchor
->GetContentAnchor();
162 const SwNodeIndex
* pContentIdx
;
164 ((RndStdIds::FLY_AT_PARA
== pAnchor
->GetAnchorId()) ||
165 (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId())) &&
166 // do not move if the InsPos is in the ContentArea of the Fly
167 ( nullptr == ( pContentIdx
= pFormat
->GetContent().GetContentIdx() ) ||
168 (*pContentIdx
>= rInsPos
.GetNode() ||
169 rInsPos
.GetNode() >= *pContentIdx
->GetNode().EndOfSectionNode())))
171 bool bInsPos
= false;
173 if ( (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId()
174 && IsDestroyFrameAnchoredAtChar(*pAPos
, *rPam
.Start(), *rPam
.End()))
175 || (RndStdIds::FLY_AT_PARA
== pAnchor
->GetAnchorId()
176 && IsSelectFrameAnchoredAtPara(*pAPos
, *rPam
.Start(), atParaEnd
,
178 ? DelContentType::CheckNoCntnt
|DelContentType::AllMask
179 : DelContentType::AllMask
))
180 || (RndStdIds::FLY_AT_PARA
== pAnchor
->GetAnchorId()
181 && (bInsPos
= (rInsPos
.GetNode() == pAPos
->GetNode())))
182 || (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId()
183 && (bInsPos
= (rInsPos
== *pAPos
))))
187 pHistory
->AddChangeFlyAnchor(*pFormat
);
189 SaveFly
aSave( pAPos
->GetNodeIndex() - rSttNd
.GetIndex(),
190 (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId())
191 ? (pAPos
->GetNode() == rSttNd
)
192 ? pAPos
->GetContentIndex() - rPam
.Start()->GetContentIndex()
193 : pAPos
->GetContentIndex()
196 rArr
.push_back( aSave
);
197 pFormat
->DelFrames();
198 // set a dummy anchor position to maintain anchoring invariants
199 SwFormatAnchor
aAnchor( pFormat
->GetAnchor() );
200 aAnchor
.SetAnchor(nullptr);
201 pFormat
->SetFormatAttr(aAnchor
);
202 rFormats
.erase( rFormats
.begin() + n
-- );
206 sw::CheckAnchoredFlyConsistency(rPam
.GetPoint()->GetNode().GetDoc());
209 /// Delete and move all Flys at the paragraph, that are within the selection.
210 /// If there is a Fly at the SPoint, it is moved onto the Mark.
211 void DelFlyInRange( SwNode
& rMkNd
,
213 std::optional
<sal_Int32
> oMkContentIdx
, std::optional
<sal_Int32
> oPtContentIdx
)
215 assert(oMkContentIdx
.has_value() == oPtContentIdx
.has_value());
216 SwPosition
const point(oPtContentIdx
217 ? SwPosition(rPtNd
, rPtNd
.GetContentNode(), *oPtContentIdx
)
218 : SwPosition(rPtNd
));
219 SwPosition
const mark(oPtContentIdx
220 ? SwPosition(rMkNd
, rMkNd
.GetContentNode(), *oMkContentIdx
)
221 : SwPosition(rMkNd
));
222 SwPosition
const& rStart
= mark
<= point
? mark
: point
;
223 SwPosition
const& rEnd
= mark
<= point
? point
: mark
;
225 SwDoc
& rDoc
= rMkNd
.GetDoc();
226 sw::SpzFrameFormats
& rTable
= *rDoc
.GetSpzFrameFormats();
227 for ( auto i
= rTable
.size(); i
; )
229 sw::SpzFrameFormat
* pFormat
= rTable
[--i
];
230 const SwFormatAnchor
&rAnch
= pFormat
->GetAnchor();
231 SwPosition
const*const pAPos
= rAnch
.GetContentAnchor();
233 (((rAnch
.GetAnchorId() == RndStdIds::FLY_AT_PARA
)
234 && IsSelectFrameAnchoredAtPara(*pAPos
, rStart
, rEnd
, oPtContentIdx
235 ? DelContentType::AllMask
|DelContentType::WriterfilterHack
236 : DelContentType::AllMask
|DelContentType::WriterfilterHack
|DelContentType::CheckNoCntnt
))
237 || ((rAnch
.GetAnchorId() == RndStdIds::FLY_AT_CHAR
)
238 && IsDestroyFrameAnchoredAtChar(*pAPos
, rStart
, rEnd
, oPtContentIdx
239 ? DelContentType::AllMask
|DelContentType::WriterfilterHack
240 : DelContentType::AllMask
|DelContentType::WriterfilterHack
|DelContentType::CheckNoCntnt
))))
242 // If the Fly is deleted, all Flys in its content have to be deleted too.
243 const SwFormatContent
&rContent
= pFormat
->GetContent();
244 // But only fly formats own their content, not draw formats.
245 if (rContent
.GetContentIdx() && pFormat
->Which() == RES_FLYFRMFMT
)
247 DelFlyInRange( rContent
.GetContentIdx()->GetNode(),
248 *rContent
.GetContentIdx()->
249 GetNode().EndOfSectionNode() );
250 // Position could have been moved!
251 if (i
> rTable
.size())
253 else if (i
== rTable
.size() || pFormat
!= rTable
[i
])
254 i
= std::distance(rTable
.begin(), rTable
.find(pFormat
));
257 rDoc
.getIDocumentLayoutAccess().DelLayoutFormat( pFormat
);
259 // DelLayoutFormat can also trigger the deletion of objects.
260 if (i
> rTable
.size())
266 // #i59534: Redo of insertion of multiple text nodes runs into trouble
267 // because of unnecessary expanded redlines
268 // From now on this class saves the redline positions of all redlines which ends exact at the
269 // insert position (node _and_ content index)
270 SaveRedlEndPosForRestore::SaveRedlEndPosForRestore( const SwNode
& rInsIdx
, sal_Int32 nCnt
)
271 : mnSaveContent( nCnt
)
273 const SwDoc
& rDest
= rInsIdx
.GetDoc();
274 if( rDest
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
277 SwRedlineTable::size_type nFndPos
;
278 const SwPosition
* pEnd
;
279 SwPosition
aSrcPos( rInsIdx
, rInsIdx
.GetContentNode(), nCnt
);
280 rDest
.getIDocumentRedlineAccess().GetRedline( aSrcPos
, &nFndPos
);
281 const SwRangeRedline
* pRedl
;
283 && *( pEnd
= ( pRedl
= rDest
.getIDocumentRedlineAccess().GetRedlineTable()[ nFndPos
] )->End() ) == aSrcPos
284 && *pRedl
->Start() < aSrcPos
)
288 moSaveIndex
.emplace( rInsIdx
, -1 );
290 mvSavArr
.push_back( const_cast<SwPosition
*>(pEnd
) );
294 SaveRedlEndPosForRestore::~SaveRedlEndPosForRestore()
299 void SaveRedlEndPosForRestore::Restore()
301 if (mvSavArr
.empty())
304 SwContentNode
* pNode
= moSaveIndex
->GetNode().GetContentNode();
305 // If there's no content node at the remembered position, we will not restore the old position
306 // This may happen if a table (or section?) will be inserted.
309 SwPosition
aPos( *moSaveIndex
, pNode
, mnSaveContent
);
310 for( auto n
= mvSavArr
.size(); n
; )
311 *mvSavArr
[ --n
] = aPos
;
315 /// Convert list of ranges of whichIds to a corresponding list of whichIds
316 static std::vector
<sal_uInt16
> lcl_RangesToVector(const WhichRangesContainer
& pRanges
)
318 std::vector
<sal_uInt16
> aResult
;
320 for(const WhichPair
& rPair
: pRanges
)
322 for (sal_uInt16 j
= rPair
.first
; j
<= rPair
.second
; j
++)
323 aResult
.push_back(j
);
329 void sw_GetJoinFlags( SwPaM
& rPam
, bool& rJoinText
, bool& rJoinPrev
)
333 if( rPam
.GetPoint()->GetNode() == rPam
.GetMark()->GetNode() )
336 auto [pStart
, pEnd
] = rPam
.StartEnd(); // SwPosition*
337 SwTextNode
*pSttNd
= pStart
->GetNode().GetTextNode();
341 SwTextNode
*pEndNd
= pEnd
->GetNode().GetTextNode();
342 rJoinText
= nullptr != pEndNd
;
346 bool bExchange
= pStart
== rPam
.GetPoint();
347 if( !pStart
->GetContentIndex() &&
348 pEndNd
->GetText().getLength() != pEnd
->GetContentIndex())
349 bExchange
= !bExchange
;
352 rJoinPrev
= rPam
.GetPoint() == pStart
;
353 OSL_ENSURE( !pStart
->GetContentIndex() &&
354 pEndNd
->GetText().getLength() != pEnd
->GetContentIndex()
355 ? (rPam
.GetPoint()->GetNode() < rPam
.GetMark()->GetNode())
356 : (rPam
.GetPoint()->GetNode() > rPam
.GetMark()->GetNode()),
360 bool sw_JoinText( SwPaM
& rPam
, bool bJoinPrev
)
362 SwNodeIndex
aIdx( rPam
.GetPoint()->GetNode() );
363 SwTextNode
*pTextNd
= aIdx
.GetNode().GetTextNode();
364 SwNodeIndex
aOldIdx( aIdx
);
365 SwTextNode
*pOldTextNd
= pTextNd
;
367 if( pTextNd
&& pTextNd
->CanJoinNext( &aIdx
) )
369 SwDoc
& rDoc
= rPam
.GetDoc();
372 // We do not need to handle xmlids in this case, because
373 // it is only invoked if one paragraph is/becomes completely empty
374 // (see sw_GetJoinFlags)
376 // If PageBreaks are deleted/set, it must not be added to the Undo history!
377 // Also, deleting the Node is not added to the Undo history!
378 ::sw::UndoGuard
const undoGuard(rDoc
.GetIDocumentUndoRedo());
380 /* PageBreaks, PageDesc, ColumnBreaks */
381 // If we need to change something about the logic to copy the PageBreaks,
382 // PageDesc, etc. we also have to change SwUndoDelete.
383 // There, we copy the AUTO PageBreak from the GetMarkNode!
386 pTextNd
= aIdx
.GetNode().GetTextNode();
387 if (pTextNd
->HasSwAttrSet())
389 if( SfxItemState::SET
== pTextNd
->GetpSwAttrSet()->GetItemState( RES_BREAK
, false) )
390 pTextNd
->ResetAttr( RES_BREAK
);
391 if( pTextNd
->HasSwAttrSet() &&
392 SfxItemState::SET
== pTextNd
->GetpSwAttrSet()->GetItemState( RES_PAGEDESC
, false ) )
393 pTextNd
->ResetAttr( RES_PAGEDESC
);
397 if( pOldTextNd
->HasSwAttrSet() )
399 const SfxPoolItem
* pItem
;
400 SfxItemSet
aSet( rDoc
.GetAttrPool(), aBreakSetRange
);
401 const SfxItemSet
* pSet
= pOldTextNd
->GetpSwAttrSet();
402 if( SfxItemState::SET
== pSet
->GetItemState( RES_BREAK
,
405 if( SfxItemState::SET
== pSet
->GetItemState( RES_PAGEDESC
,
409 pTextNd
->SetAttr( aSet
);
411 pOldTextNd
->FormatToTextAttr( pTextNd
);
413 const std::shared_ptr
< sw::mark::ContentIdxStore
> pContentStore(sw::mark::ContentIdxStore::Create());
414 pContentStore
->Save(rDoc
, aOldIdx
.GetIndex(), SAL_MAX_INT32
);
416 SwContentIndex
aAlphaIdx(pTextNd
);
417 pOldTextNd
->CutText( pTextNd
, aAlphaIdx
, SwContentIndex(pOldTextNd
),
419 SwPosition
aAlphaPos( aIdx
, aAlphaIdx
);
420 rDoc
.CorrRel( rPam
.GetPoint()->GetNode(), aAlphaPos
, 0, true );
422 // move all Bookmarks/TOXMarks
423 if( !pContentStore
->Empty() )
424 pContentStore
->Restore( rDoc
, aIdx
.GetIndex() );
426 // If the passed PaM is not in the Cursor ring,
427 // treat it separately (e.g. when it's being called from AutoFormat)
428 if( pOldTextNd
== rPam
.GetBound().GetContentNode() )
429 rPam
.GetBound() = aAlphaPos
;
430 if( pOldTextNd
== rPam
.GetBound( false ).GetContentNode() )
431 rPam
.GetBound( false ) = std::move(aAlphaPos
);
433 // delete the Node, at last!
434 SwNode::Merge
const eOldMergeFlag(pOldTextNd
->GetRedlineMergeFlag());
435 if (eOldMergeFlag
== SwNode::Merge::First
436 && !pTextNd
->IsCreateFrameWhenHidingRedlines())
438 sw::MoveDeletedPrevFrames(*pOldTextNd
, *pTextNd
);
440 rDoc
.GetNodes().Delete( aOldIdx
);
441 sw::CheckResetRedlineMergeFlag(*pTextNd
,
442 eOldMergeFlag
== SwNode::Merge::NonFirst
443 ? sw::Recreate::Predecessor
448 SwTextNode
* pDelNd
= aIdx
.GetNode().GetTextNode();
450 pDelNd
->FormatToTextAttr( pTextNd
);
453 /* This case was missed:
455 <something></something> <-- pTextNd
456 <other>ccc</other> <-- pDelNd
458 <something> and <other> are paragraph
459 attributes. The attribute <something> stayed if not
460 overwritten by an attribute in "ccc". Fixed by
461 first resetting all character attributes in first
464 std::vector
<sal_uInt16
> aShorts
=
465 lcl_RangesToVector(aCharFormatSetRange
);
466 pTextNd
->ResetAttr(aShorts
);
468 if( pDelNd
->HasSwAttrSet() )
470 // only copy the character attributes
471 SfxItemSet
aTmpSet( rDoc
.GetAttrPool(), aCharFormatSetRange
);
472 aTmpSet
.Put( *pDelNd
->GetpSwAttrSet() );
473 pTextNd
->SetAttr( aTmpSet
);
477 rDoc
.CorrRel( aIdx
.GetNode(), *rPam
.GetPoint(), 0, true );
478 // #i100466# adjust given <rPam>, if it does not belong to the cursors
479 if ( pDelNd
== rPam
.GetBound().GetContentNode() )
481 rPam
.GetBound().Assign( *pTextNd
);
483 if( pDelNd
== rPam
.GetBound( false ).GetContentNode() )
485 rPam
.GetBound( false ).Assign( *pTextNd
);
494 static void lcl_syncGrammarError( SwTextNode
&rTextNode
, linguistic2::ProofreadingResult
& rResult
,
495 const ModelToViewHelper
&rConversionMap
)
497 if( rTextNode
.IsGrammarCheckDirty() )
499 SwGrammarMarkUp
* pWrong
= rTextNode
.GetGrammarCheck();
500 linguistic2::SingleProofreadingError
* pArray
= rResult
.aErrors
.getArray();
504 for( sal_Int32 i
= 0; i
< rResult
.aErrors
.getLength(); ++i
)
506 const linguistic2::SingleProofreadingError
&rError
= rResult
.aErrors
[i
];
507 const sal_Int32 nStart
= rConversionMap
.ConvertToModelPosition( rError
.nErrorStart
).mnPos
;
508 const sal_Int32 nEnd
= rConversionMap
.ConvertToModelPosition( rError
.nErrorStart
+ rError
.nErrorLength
).mnPos
;
510 pArray
[j
] = pArray
[i
];
511 if( pWrong
->LookForEntry( nStart
, nEnd
) )
515 if( rResult
.aErrors
.getLength() > j
)
516 rResult
.aErrors
.realloc( j
);
519 uno::Any
SwDoc::Spell( SwPaM
& rPaM
,
520 uno::Reference
< XSpellChecker1
> const &xSpeller
,
521 sal_uInt16
* pPageCnt
, sal_uInt16
* pPageSt
,
523 SwRootFrame
const*const pLayout
,
524 SwConversionArgs
*pConvArgs
) const
526 SwPosition
* const pSttPos
= rPaM
.Start();
527 SwPosition
* const pEndPos
= rPaM
.End();
529 std::unique_ptr
<SwSpellArgs
> pSpellArgs
;
532 pConvArgs
->SetStart(*pSttPos
);
533 pConvArgs
->SetEnd(*pEndPos
);
536 pSpellArgs
.reset(new SwSpellArgs( xSpeller
, *pSttPos
, *pEndPos
, bGrammarCheck
));
538 SwNodeOffset nCurrNd
= pSttPos
->GetNodeIndex();
539 SwNodeOffset nEndNd
= pEndPos
->GetNodeIndex();
542 if( nCurrNd
<= nEndNd
)
544 SwContentFrame
* pContentFrame
;
548 SwNode
* pNd
= GetNodes()[ nCurrNd
];
549 switch( pNd
->GetNodeType() )
551 case SwNodeType::Text
:
552 if( nullptr != ( pContentFrame
= pNd
->GetTextNode()->getLayoutFrame( getIDocumentLayoutAccess().GetCurrentLayout() )) )
554 // skip protected and hidden Cells and Flys
555 if( pContentFrame
->IsProtected() )
557 nCurrNd
= pNd
->EndOfSectionIndex();
559 else if( !pContentFrame
->IsHiddenNow() )
561 if( pPageCnt
&& *pPageCnt
&& pPageSt
)
563 sal_uInt16 nPageNr
= pContentFrame
->GetPhyPageNum();
567 if( *pPageCnt
< *pPageSt
)
568 *pPageCnt
= *pPageSt
;
571 if( nPageNr
>= *pPageSt
)
572 nStat
= nPageNr
- *pPageSt
+ 1;
574 nStat
= nPageNr
+ *pPageCnt
- *pPageSt
+ 1;
575 ::SetProgressState( nStat
, GetDocShell() );
577 //Spell() changes the pSpellArgs in case an error is found
578 sal_Int32 nBeginGrammarCheck
= 0;
579 sal_Int32 nEndGrammarCheck
= 0;
580 if( pSpellArgs
&& pSpellArgs
->bIsGrammarCheck
)
582 nBeginGrammarCheck
= &pSpellArgs
->pStartPos
->GetNode() == pNd
? pSpellArgs
->pStartPos
->GetContentIndex() : 0;
583 // if grammar checking starts inside of a sentence the start position has to be adjusted
584 if( nBeginGrammarCheck
)
586 SwContentIndex
aStartIndex( pNd
->GetTextNode(), nBeginGrammarCheck
);
587 SwPosition
aStart( *pNd
, aStartIndex
);
588 SwCursor
aCursor(aStart
, nullptr);
589 SwPosition aOrigPos
= *aCursor
.GetPoint();
590 aCursor
.GoSentence( SwCursor::START_SENT
);
591 if( aOrigPos
!= *aCursor
.GetPoint() )
593 nBeginGrammarCheck
= aCursor
.GetPoint()->GetContentIndex();
596 nEndGrammarCheck
= (&pSpellArgs
->pEndPos
->GetNode() == pNd
)
597 ? pSpellArgs
->pEndPos
->GetContentIndex()
599 ->GetText().getLength();
602 sal_Int32 nSpellErrorPosition
= pNd
->GetTextNode()->GetText().getLength();
603 if( (!pConvArgs
&& pNd
->GetTextNode()->Spell( pSpellArgs
.get() )) ||
604 ( pConvArgs
&& pNd
->GetTextNode()->Convert( *pConvArgs
)))
606 // Cancel and remember position
608 nSpellErrorPosition
= pSpellArgs
->pStartPos
->GetContentIndex() > pSpellArgs
->pEndPos
->GetContentIndex() ?
609 pSpellArgs
->pEndPos
->GetContentIndex() :
610 pSpellArgs
->pStartPos
->GetContentIndex();
611 if( nCurrNd
!= nEndNd
)
613 pSttPos
->Assign(nCurrNd
, pSttPos
->GetContentIndex());
614 pEndPos
->Assign(nCurrNd
, pEndPos
->GetContentIndex());
619 if( pSpellArgs
&& pSpellArgs
->bIsGrammarCheck
)
621 uno::Reference
< linguistic2::XProofreadingIterator
> xGCIterator( GetGCIterator() );
622 if (xGCIterator
.is())
624 if (const SwDocShell
* pShell
= GetDocShell())
626 rtl::Reference
< SwXTextDocument
> xDoc
= pShell
->GetBaseModel();
627 // Expand the string:
628 const ModelToViewHelper
aConversionMap(*pNd
->GetTextNode(), pLayout
);
629 const OUString
& aExpandText
= aConversionMap
.getViewText();
631 // get XFlatParagraph to use...
632 uno::Reference
< text::XFlatParagraph
> xFlatPara
= new SwXFlatParagraph( *pNd
->GetTextNode(), aExpandText
, aConversionMap
);
634 // get error position of cursor in XFlatParagraph
635 linguistic2::ProofreadingResult aResult
;
639 aConversionMap
.ConvertToViewPosition( nBeginGrammarCheck
);
640 aResult
= xGCIterator
->checkSentenceAtPosition(
641 cppu::getXWeak(xDoc
.get()), xFlatPara
, aExpandText
, lang::Locale(), nBeginGrammarCheck
, -1, -1 );
643 lcl_syncGrammarError( *pNd
->GetTextNode(), aResult
, aConversionMap
);
645 // get suggestions to use for the specific error position
646 bGrammarErrors
= aResult
.aErrors
.hasElements();
647 // if grammar checking doesn't have any progress then quit
648 if( aResult
.nStartOfNextSentencePosition
<= nBeginGrammarCheck
)
650 // prepare next iteration
651 nBeginGrammarCheck
= aResult
.nStartOfNextSentencePosition
;
653 while( nSpellErrorPosition
> aResult
.nBehindEndOfSentencePosition
&& !bGrammarErrors
&& aResult
.nBehindEndOfSentencePosition
< nEndGrammarCheck
);
655 if( bGrammarErrors
&& nSpellErrorPosition
>= aResult
.nBehindEndOfSentencePosition
)
658 //put the cursor to the current error
659 const linguistic2::SingleProofreadingError
&rError
= aResult
.aErrors
[0];
660 pSttPos
->Assign(nCurrNd
, pSttPos
->GetContentIndex());
661 pEndPos
->Assign(nCurrNd
, pEndPos
->GetContentIndex());
662 pSpellArgs
->pStartPos
->Assign(*pNd
->GetTextNode(), aConversionMap
.ConvertToModelPosition( rError
.nErrorStart
).mnPos
);
663 pSpellArgs
->pEndPos
->Assign(*pNd
->GetTextNode(), aConversionMap
.ConvertToModelPosition( rError
.nErrorStart
+ rError
.nErrorLength
).mnPos
);
672 case SwNodeType::Section
:
673 if( static_cast<SwSectionNode
*>(pNd
)->GetSection().IsProtect() ||
674 static_cast<SwSectionNode
*>(pNd
)->GetSection().IsHidden() )
675 nCurrNd
= pNd
->EndOfSectionIndex();
677 case SwNodeType::End
:
684 bGoOn
= nCurrNd
< nEndNd
;
689 if( !aRet
.hasValue() )
692 aRet
<<= pConvArgs
->aConvText
;
694 aRet
<<= pSpellArgs
->xSpellAlt
;
702 class SwHyphArgs
: public SwInterHyphInfo
704 SwNodeIndex m_aNodeIdx
;
705 const SwNode
*m_pStart
;
706 const SwNode
*m_pEnd
;
707 sal_uInt16
*m_pPageCnt
;
708 sal_uInt16
*m_pPageSt
;
710 sal_Int32 m_nPamStart
;
714 SwHyphArgs( const SwPaM
*pPam
, const Point
&rPoint
,
715 sal_uInt16
* pPageCount
, sal_uInt16
* pPageStart
);
716 void SetPam( SwPaM
*pPam
) const;
717 void SetNode( SwNode
& rNew
) { m_aNodeIdx
.Assign(rNew
); }
718 inline void SetRange( const SwNode
*pNew
);
719 void NextNode() { ++m_aNodeIdx
; }
720 sal_uInt16
*GetPageCnt() { return m_pPageCnt
; }
721 sal_uInt16
*GetPageSt() { return m_pPageSt
; }
726 SwHyphArgs::SwHyphArgs( const SwPaM
*pPam
, const Point
&rCursorPos
,
727 sal_uInt16
* pPageCount
, sal_uInt16
* pPageStart
)
728 : SwInterHyphInfo( rCursorPos
), m_aNodeIdx(pPam
->GetPoint()->GetNode()),
729 m_pPageCnt( pPageCount
), m_pPageSt( pPageStart
)
731 // The following constraints have to be met:
732 // 1) there is at least one Selection
733 // 2) SPoint() == Start()
734 OSL_ENSURE( pPam
->HasMark(), "SwDoc::Hyphenate: blowing in the wind");
735 OSL_ENSURE( *pPam
->GetPoint() <= *pPam
->GetMark(),
736 "SwDoc::Hyphenate: New York, New York");
738 const SwPosition
*pPoint
= pPam
->GetPoint();
741 m_pStart
= pPoint
->GetNode().GetTextNode();
742 m_nPamStart
= pPoint
->GetContentIndex();
744 // Set End and Length
745 const SwPosition
*pMark
= pPam
->GetMark();
746 m_pEnd
= pMark
->GetNode().GetTextNode();
747 m_nPamLen
= pMark
->GetContentIndex();
748 if( pPoint
->GetNode() == pMark
->GetNode() )
749 m_nPamLen
= m_nPamLen
- pPoint
->GetContentIndex();
752 inline void SwHyphArgs::SetRange( const SwNode
*pNew
)
754 m_nStart
= m_pStart
== pNew
? m_nPamStart
: 0;
755 m_nEnd
= m_pEnd
== pNew
? m_nPamStart
+ m_nPamLen
: SAL_MAX_INT32
;
758 void SwHyphArgs::SetPam( SwPaM
*pPam
) const
760 pPam
->GetPoint()->Assign( m_aNodeIdx
, m_nWordStart
);
761 pPam
->GetMark()->Assign( m_aNodeIdx
, m_nWordStart
+ m_nWordLen
);
764 // Returns true if we can proceed.
765 static bool lcl_HyphenateNode( SwNode
* pNd
, void* pArgs
)
767 // Hyphenate returns true if there is a hyphenation point and sets pPam
768 SwTextNode
*pNode
= pNd
->GetTextNode();
769 SwHyphArgs
*pHyphArgs
= static_cast<SwHyphArgs
*>(pArgs
);
772 // sw_redlinehide: this will be called once per node for merged nodes;
773 // the fully deleted ones won't have frames so are skipped.
774 SwContentFrame
* pContentFrame
= pNode
->getLayoutFrame( pNode
->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
775 if( pContentFrame
&& !pContentFrame
->IsHiddenNow() )
777 sal_uInt16
*pPageSt
= pHyphArgs
->GetPageSt();
778 sal_uInt16
*pPageCnt
= pHyphArgs
->GetPageCnt();
779 if( pPageCnt
&& *pPageCnt
&& pPageSt
)
781 sal_uInt16 nPageNr
= pContentFrame
->GetPhyPageNum();
785 if( *pPageCnt
< *pPageSt
)
786 *pPageCnt
= *pPageSt
;
788 tools::Long nStat
= nPageNr
>= *pPageSt
? nPageNr
- *pPageSt
+ 1
789 : nPageNr
+ *pPageCnt
- *pPageSt
+ 1;
790 ::SetProgressState( nStat
, pNode
->GetDoc().GetDocShell() );
792 pHyphArgs
->SetRange( pNd
);
793 if( pNode
->Hyphenate( *pHyphArgs
) )
795 pHyphArgs
->SetNode( *pNd
);
800 pHyphArgs
->NextNode();
804 uno::Reference
< XHyphenatedWord
> SwDoc::Hyphenate(
805 SwPaM
*pPam
, const Point
&rCursorPos
,
806 sal_uInt16
* pPageCnt
, sal_uInt16
* pPageSt
)
808 OSL_ENSURE(this == &pPam
->GetDoc(), "SwDoc::Hyphenate: strangers in the night");
810 if( *pPam
->GetPoint() > *pPam
->GetMark() )
813 SwHyphArgs
aHyphArg( pPam
, rCursorPos
, pPageCnt
, pPageSt
);
814 SwNodeIndex
aTmpIdx( pPam
->GetMark()->GetNode(), 1 );
815 GetNodes().ForEach( pPam
->GetPoint()->GetNode(), aTmpIdx
.GetNode(),
816 lcl_HyphenateNode
, &aHyphArg
);
817 aHyphArg
.SetPam( pPam
);
818 return aHyphArg
.GetHyphWord(); // will be set by lcl_HyphenateNode
821 // Save the current values to add them as automatic entries to AutoCorrect.
822 void SwDoc::SetAutoCorrExceptWord( std::unique_ptr
<SwAutoCorrExceptWord
> pNew
)
824 mpACEWord
= std::move(pNew
);
827 void SwDoc::DeleteAutoCorrExceptWord()
832 void SwDoc::CountWords( const SwPaM
& rPaM
, SwDocStat
& rStat
)
834 // This is a modified version of SwDoc::TransliterateText
835 auto [pStart
, pEnd
] = rPaM
.StartEnd(); // SwPosition*
837 const SwNodeOffset nSttNd
= pStart
->GetNodeIndex();
838 const SwNodeOffset nEndNd
= pEnd
->GetNodeIndex();
840 const sal_Int32 nSttCnt
= pStart
->GetContentIndex();
841 const sal_Int32 nEndCnt
= pEnd
->GetContentIndex();
843 const SwTextNode
* pTNd
= pStart
->GetNode().GetTextNode();
844 if( pStart
== pEnd
&& pTNd
) // no region ?
850 if( nSttNd
!= nEndNd
)
852 SwNodeIndex
aIdx( pStart
->GetNode() );
857 pTNd
->CountWords( rStat
, nSttCnt
, pTNd
->GetText().getLength() );
860 for( ; aIdx
.GetIndex() < nEndNd
; ++aIdx
)
861 if( nullptr != ( pTNd
= aIdx
.GetNode().GetTextNode() ))
862 pTNd
->CountWords( rStat
, 0, pTNd
->GetText().getLength() );
864 if( nEndCnt
&& nullptr != ( pTNd
= pEnd
->GetNode().GetTextNode() ))
865 pTNd
->CountWords( rStat
, 0, nEndCnt
);
867 else if( pTNd
&& nSttCnt
< nEndCnt
)
868 pTNd
->CountWords( rStat
, nSttCnt
, nEndCnt
);
872 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */