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>
45 #include <com/sun/star/linguistic2/XProofreadingIterator.hpp>
46 #include <osl/diagnose.h>
48 using namespace ::com::sun::star
;
49 using namespace ::com::sun::star::linguistic2
;
50 using namespace ::com::sun::star::i18n
;
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 for(sw::FrameFormats
<sw::SpzFrameFormat
*>::size_type n
= 0; n
< rSpzs
.size(); ++n
)
111 auto pSpz
= rSpzs
[n
];
112 SwFormatAnchor
const*const pAnchor
= &pSpz
->GetAnchor();
113 SwNode
const*const pAnchorNode
= pAnchor
->GetAnchorNode();
115 ((RndStdIds::FLY_AT_PARA
== pAnchor
->GetAnchorId()) ||
116 (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId())) &&
117 rRg
.aStart
<= *pAnchorNode
&& *pAnchorNode
< rRg
.aEnd
.GetNode() )
119 SaveFly
aSave( pAnchorNode
->GetIndex() - rRg
.aStart
.GetIndex(),
120 (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId())
121 ? pAnchor
->GetAnchorContentOffset()
124 rArr
.push_back( aSave
);
126 // set a dummy anchor position to maintain anchoring invariants
127 SwFormatAnchor
aAnchor( pSpz
->GetAnchor() );
128 aAnchor
.SetAnchor(nullptr);
129 pSpz
->SetFormatAttr(aAnchor
);
130 rSpzs
.erase( rSpzs
.begin() + n
-- );
133 sw::CheckAnchoredFlyConsistency(rRg
.aStart
.GetNode().GetDoc());
136 void SaveFlyInRange( const SwPaM
& rPam
, const SwPosition
& rInsPos
,
137 SaveFlyArr
& rArr
, bool bMoveAllFlys
, SwHistory
*const pHistory
)
139 sw::SpzFrameFormats
& rFormats
= *rPam
.GetPoint()->GetNode().GetDoc().GetSpzFrameFormats();
140 sw::SpzFrameFormat
* pFormat
;
141 const SwFormatAnchor
* pAnchor
;
143 const SwPosition
* pPos
= rPam
.Start();
144 const SwNode
& rSttNd
= pPos
->GetNode();
146 SwPosition
atParaEnd(*rPam
.End());
149 assert(!rPam
.End()->GetNode().IsTextNode() // can be table end-node
150 || rPam
.End()->GetContentIndex() == rPam
.End()->GetNode().GetTextNode()->Len());
151 atParaEnd
.Adjust(SwNodeOffset(1));
154 for(sw::FrameFormats
<sw::SpzFrameFormat
*>::size_type n
= 0; n
< rFormats
.size(); ++n
)
156 pFormat
= rFormats
[n
];
157 pAnchor
= &pFormat
->GetAnchor();
158 const SwPosition
* pAPos
= pAnchor
->GetContentAnchor();
159 const SwNodeIndex
* pContentIdx
;
161 ((RndStdIds::FLY_AT_PARA
== pAnchor
->GetAnchorId()) ||
162 (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId())) &&
163 // do not move if the InsPos is in the ContentArea of the Fly
164 ( nullptr == ( pContentIdx
= pFormat
->GetContent().GetContentIdx() ) ||
165 (*pContentIdx
>= rInsPos
.GetNode() ||
166 rInsPos
.GetNode() >= *pContentIdx
->GetNode().EndOfSectionNode())))
168 bool bInsPos
= false;
170 if ( (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId()
171 && IsDestroyFrameAnchoredAtChar(*pAPos
, *rPam
.Start(), *rPam
.End()))
172 || (RndStdIds::FLY_AT_PARA
== pAnchor
->GetAnchorId()
173 && IsSelectFrameAnchoredAtPara(*pAPos
, *rPam
.Start(), atParaEnd
,
175 ? DelContentType::CheckNoCntnt
|DelContentType::AllMask
176 : DelContentType::AllMask
))
177 || (RndStdIds::FLY_AT_PARA
== pAnchor
->GetAnchorId()
178 && (bInsPos
= (rInsPos
.GetNode() == pAPos
->GetNode())))
179 || (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId()
180 && (bInsPos
= (rInsPos
== *pAPos
))))
184 pHistory
->AddChangeFlyAnchor(*pFormat
);
186 SaveFly
aSave( pAPos
->GetNodeIndex() - rSttNd
.GetIndex(),
187 (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId())
188 ? (pAPos
->GetNode() == rSttNd
)
189 ? pAPos
->GetContentIndex() - rPam
.Start()->GetContentIndex()
190 : pAPos
->GetContentIndex()
193 rArr
.push_back( aSave
);
194 pFormat
->DelFrames();
195 // set a dummy anchor position to maintain anchoring invariants
196 SwFormatAnchor
aAnchor( pFormat
->GetAnchor() );
197 aAnchor
.SetAnchor(nullptr);
198 pFormat
->SetFormatAttr(aAnchor
);
199 rFormats
.erase( rFormats
.begin() + n
-- );
203 sw::CheckAnchoredFlyConsistency(rPam
.GetPoint()->GetNode().GetDoc());
206 /// Delete and move all Flys at the paragraph, that are within the selection.
207 /// If there is a Fly at the SPoint, it is moved onto the Mark.
208 void DelFlyInRange( SwNode
& rMkNd
,
210 std::optional
<sal_Int32
> oMkContentIdx
, std::optional
<sal_Int32
> oPtContentIdx
)
212 assert(oMkContentIdx
.has_value() == oPtContentIdx
.has_value());
213 SwPosition
const point(oPtContentIdx
214 ? SwPosition(rPtNd
, rPtNd
.GetContentNode(), *oPtContentIdx
)
215 : SwPosition(rPtNd
));
216 SwPosition
const mark(oPtContentIdx
217 ? SwPosition(rMkNd
, rMkNd
.GetContentNode(), *oMkContentIdx
)
218 : SwPosition(rMkNd
));
219 SwPosition
const& rStart
= mark
<= point
? mark
: point
;
220 SwPosition
const& rEnd
= mark
<= point
? point
: mark
;
222 SwDoc
& rDoc
= rMkNd
.GetDoc();
223 sw::SpzFrameFormats
& rTable
= *rDoc
.GetSpzFrameFormats();
224 for ( auto i
= rTable
.size(); i
; )
226 sw::SpzFrameFormat
* pFormat
= rTable
[--i
];
227 const SwFormatAnchor
&rAnch
= pFormat
->GetAnchor();
228 SwPosition
const*const pAPos
= rAnch
.GetContentAnchor();
230 (((rAnch
.GetAnchorId() == RndStdIds::FLY_AT_PARA
)
231 && IsSelectFrameAnchoredAtPara(*pAPos
, rStart
, rEnd
, oPtContentIdx
232 ? DelContentType::AllMask
|DelContentType::WriterfilterHack
233 : DelContentType::AllMask
|DelContentType::WriterfilterHack
|DelContentType::CheckNoCntnt
))
234 || ((rAnch
.GetAnchorId() == RndStdIds::FLY_AT_CHAR
)
235 && IsDestroyFrameAnchoredAtChar(*pAPos
, rStart
, rEnd
, oPtContentIdx
236 ? DelContentType::AllMask
|DelContentType::WriterfilterHack
237 : DelContentType::AllMask
|DelContentType::WriterfilterHack
|DelContentType::CheckNoCntnt
))))
239 // If the Fly is deleted, all Flys in its content have to be deleted too.
240 const SwFormatContent
&rContent
= pFormat
->GetContent();
241 // But only fly formats own their content, not draw formats.
242 if (rContent
.GetContentIdx() && pFormat
->Which() == RES_FLYFRMFMT
)
244 DelFlyInRange( rContent
.GetContentIdx()->GetNode(),
245 *rContent
.GetContentIdx()->
246 GetNode().EndOfSectionNode() );
247 // Position could have been moved!
248 if (i
> rTable
.size())
250 else if (i
== rTable
.size() || pFormat
!= rTable
[i
])
251 i
= std::distance(rTable
.begin(), rTable
.find(pFormat
));
254 rDoc
.getIDocumentLayoutAccess().DelLayoutFormat( pFormat
);
256 // DelLayoutFormat can also trigger the deletion of objects.
257 if (i
> rTable
.size())
263 // #i59534: Redo of insertion of multiple text nodes runs into trouble
264 // because of unnecessary expanded redlines
265 // From now on this class saves the redline positions of all redlines which ends exact at the
266 // insert position (node _and_ content index)
267 SaveRedlEndPosForRestore::SaveRedlEndPosForRestore( const SwNode
& rInsIdx
, sal_Int32 nCnt
)
268 : mnSaveContent( nCnt
)
270 const SwDoc
& rDest
= rInsIdx
.GetDoc();
271 if( rDest
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
274 SwRedlineTable::size_type nFndPos
;
275 const SwPosition
* pEnd
;
276 SwPosition
aSrcPos( rInsIdx
, rInsIdx
.GetContentNode(), nCnt
);
277 rDest
.getIDocumentRedlineAccess().GetRedline( aSrcPos
, &nFndPos
);
278 const SwRangeRedline
* pRedl
;
280 && *( pEnd
= ( pRedl
= rDest
.getIDocumentRedlineAccess().GetRedlineTable()[ nFndPos
] )->End() ) == aSrcPos
281 && *pRedl
->Start() < aSrcPos
)
285 moSaveIndex
.emplace( rInsIdx
, -1 );
287 mvSavArr
.push_back( const_cast<SwPosition
*>(pEnd
) );
291 SaveRedlEndPosForRestore::~SaveRedlEndPosForRestore()
296 void SaveRedlEndPosForRestore::Restore()
298 if (mvSavArr
.empty())
301 SwContentNode
* pNode
= moSaveIndex
->GetNode().GetContentNode();
302 // If there's no content node at the remembered position, we will not restore the old position
303 // This may happen if a table (or section?) will be inserted.
306 SwPosition
aPos( *moSaveIndex
, pNode
, mnSaveContent
);
307 for( auto n
= mvSavArr
.size(); n
; )
308 *mvSavArr
[ --n
] = aPos
;
312 /// Convert list of ranges of whichIds to a corresponding list of whichIds
313 static std::vector
<sal_uInt16
> lcl_RangesToVector(const WhichRangesContainer
& pRanges
)
315 std::vector
<sal_uInt16
> aResult
;
317 for(const WhichPair
& rPair
: pRanges
)
319 for (sal_uInt16 j
= rPair
.first
; j
<= rPair
.second
; j
++)
320 aResult
.push_back(j
);
326 void sw_GetJoinFlags( SwPaM
& rPam
, bool& rJoinText
, bool& rJoinPrev
)
330 if( rPam
.GetPoint()->GetNode() == rPam
.GetMark()->GetNode() )
333 auto [pStt
, pEnd
] = rPam
.StartEnd(); // SwPosition*
334 SwTextNode
*pSttNd
= pStt
->GetNode().GetTextNode();
338 SwTextNode
*pEndNd
= pEnd
->GetNode().GetTextNode();
339 rJoinText
= nullptr != pEndNd
;
343 bool bExchange
= pStt
== rPam
.GetPoint();
344 if( !pStt
->GetContentIndex() &&
345 pEndNd
->GetText().getLength() != pEnd
->GetContentIndex())
346 bExchange
= !bExchange
;
349 rJoinPrev
= rPam
.GetPoint() == pStt
;
350 OSL_ENSURE( !pStt
->GetContentIndex() &&
351 pEndNd
->GetText().getLength() != pEnd
->GetContentIndex()
352 ? (rPam
.GetPoint()->GetNode() < rPam
.GetMark()->GetNode())
353 : (rPam
.GetPoint()->GetNode() > rPam
.GetMark()->GetNode()),
357 bool sw_JoinText( SwPaM
& rPam
, bool bJoinPrev
)
359 SwNodeIndex
aIdx( rPam
.GetPoint()->GetNode() );
360 SwTextNode
*pTextNd
= aIdx
.GetNode().GetTextNode();
361 SwNodeIndex
aOldIdx( aIdx
);
362 SwTextNode
*pOldTextNd
= pTextNd
;
364 if( pTextNd
&& pTextNd
->CanJoinNext( &aIdx
) )
366 SwDoc
& rDoc
= rPam
.GetDoc();
369 // We do not need to handle xmlids in this case, because
370 // it is only invoked if one paragraph is/becomes completely empty
371 // (see sw_GetJoinFlags)
373 // If PageBreaks are deleted/set, it must not be added to the Undo history!
374 // Also, deleting the Node is not added to the Undo history!
375 ::sw::UndoGuard
const undoGuard(rDoc
.GetIDocumentUndoRedo());
377 /* PageBreaks, PageDesc, ColumnBreaks */
378 // If we need to change something about the logic to copy the PageBreaks,
379 // PageDesc, etc. we also have to change SwUndoDelete.
380 // There, we copy the AUTO PageBreak from the GetMarkNode!
383 pTextNd
= aIdx
.GetNode().GetTextNode();
384 if (pTextNd
->HasSwAttrSet())
386 if( SfxItemState::SET
== pTextNd
->GetpSwAttrSet()->GetItemState( RES_BREAK
, false) )
387 pTextNd
->ResetAttr( RES_BREAK
);
388 if( pTextNd
->HasSwAttrSet() &&
389 SfxItemState::SET
== pTextNd
->GetpSwAttrSet()->GetItemState( RES_PAGEDESC
, false ) )
390 pTextNd
->ResetAttr( RES_PAGEDESC
);
394 if( pOldTextNd
->HasSwAttrSet() )
396 const SfxPoolItem
* pItem
;
397 SfxItemSet
aSet( rDoc
.GetAttrPool(), aBreakSetRange
);
398 const SfxItemSet
* pSet
= pOldTextNd
->GetpSwAttrSet();
399 if( SfxItemState::SET
== pSet
->GetItemState( RES_BREAK
,
402 if( SfxItemState::SET
== pSet
->GetItemState( RES_PAGEDESC
,
406 pTextNd
->SetAttr( aSet
);
408 pOldTextNd
->FormatToTextAttr( pTextNd
);
410 const std::shared_ptr
< sw::mark::ContentIdxStore
> pContentStore(sw::mark::ContentIdxStore::Create());
411 pContentStore
->Save(rDoc
, aOldIdx
.GetIndex(), SAL_MAX_INT32
);
413 SwContentIndex
aAlphaIdx(pTextNd
);
414 pOldTextNd
->CutText( pTextNd
, aAlphaIdx
, SwContentIndex(pOldTextNd
),
416 SwPosition
aAlphaPos( aIdx
, aAlphaIdx
);
417 rDoc
.CorrRel( rPam
.GetPoint()->GetNode(), aAlphaPos
, 0, true );
419 // move all Bookmarks/TOXMarks
420 if( !pContentStore
->Empty() )
421 pContentStore
->Restore( rDoc
, aIdx
.GetIndex() );
423 // If the passed PaM is not in the Cursor ring,
424 // treat it separately (e.g. when it's being called from AutoFormat)
425 if( pOldTextNd
== rPam
.GetBound().GetContentNode() )
426 rPam
.GetBound() = aAlphaPos
;
427 if( pOldTextNd
== rPam
.GetBound( false ).GetContentNode() )
428 rPam
.GetBound( false ) = aAlphaPos
;
430 // delete the Node, at last!
431 SwNode::Merge
const eOldMergeFlag(pOldTextNd
->GetRedlineMergeFlag());
432 if (eOldMergeFlag
== SwNode::Merge::First
433 && !pTextNd
->IsCreateFrameWhenHidingRedlines())
435 sw::MoveDeletedPrevFrames(*pOldTextNd
, *pTextNd
);
437 rDoc
.GetNodes().Delete( aOldIdx
);
438 sw::CheckResetRedlineMergeFlag(*pTextNd
,
439 eOldMergeFlag
== SwNode::Merge::NonFirst
440 ? sw::Recreate::Predecessor
445 SwTextNode
* pDelNd
= aIdx
.GetNode().GetTextNode();
447 pDelNd
->FormatToTextAttr( pTextNd
);
450 /* This case was missed:
452 <something></something> <-- pTextNd
453 <other>ccc</other> <-- pDelNd
455 <something> and <other> are paragraph
456 attributes. The attribute <something> stayed if not
457 overwritten by an attribute in "ccc". Fixed by
458 first resetting all character attributes in first
461 std::vector
<sal_uInt16
> aShorts
=
462 lcl_RangesToVector(aCharFormatSetRange
);
463 pTextNd
->ResetAttr(aShorts
);
465 if( pDelNd
->HasSwAttrSet() )
467 // only copy the character attributes
468 SfxItemSet
aTmpSet( rDoc
.GetAttrPool(), aCharFormatSetRange
);
469 aTmpSet
.Put( *pDelNd
->GetpSwAttrSet() );
470 pTextNd
->SetAttr( aTmpSet
);
474 rDoc
.CorrRel( aIdx
.GetNode(), *rPam
.GetPoint(), 0, true );
475 // #i100466# adjust given <rPam>, if it does not belong to the cursors
476 if ( pDelNd
== rPam
.GetBound().GetContentNode() )
478 rPam
.GetBound().Assign( *pTextNd
);
480 if( pDelNd
== rPam
.GetBound( false ).GetContentNode() )
482 rPam
.GetBound( false ).Assign( *pTextNd
);
491 static void lcl_syncGrammarError( SwTextNode
&rTextNode
, linguistic2::ProofreadingResult
& rResult
,
492 const ModelToViewHelper
&rConversionMap
)
494 if( rTextNode
.IsGrammarCheckDirty() )
496 SwGrammarMarkUp
* pWrong
= rTextNode
.GetGrammarCheck();
497 linguistic2::SingleProofreadingError
* pArray
= rResult
.aErrors
.getArray();
501 for( sal_Int32 i
= 0; i
< rResult
.aErrors
.getLength(); ++i
)
503 const linguistic2::SingleProofreadingError
&rError
= rResult
.aErrors
[i
];
504 const sal_Int32 nStart
= rConversionMap
.ConvertToModelPosition( rError
.nErrorStart
).mnPos
;
505 const sal_Int32 nEnd
= rConversionMap
.ConvertToModelPosition( rError
.nErrorStart
+ rError
.nErrorLength
).mnPos
;
507 pArray
[j
] = pArray
[i
];
508 if( pWrong
->LookForEntry( nStart
, nEnd
) )
512 if( rResult
.aErrors
.getLength() > j
)
513 rResult
.aErrors
.realloc( j
);
516 uno::Any
SwDoc::Spell( SwPaM
& rPaM
,
517 uno::Reference
< XSpellChecker1
> const &xSpeller
,
518 sal_uInt16
* pPageCnt
, sal_uInt16
* pPageSt
,
520 SwRootFrame
const*const pLayout
,
521 SwConversionArgs
*pConvArgs
) const
523 SwPosition
* const pSttPos
= rPaM
.Start();
524 SwPosition
* const pEndPos
= rPaM
.End();
526 std::unique_ptr
<SwSpellArgs
> pSpellArgs
;
529 pConvArgs
->SetStart(*pSttPos
);
530 pConvArgs
->SetEnd(*pEndPos
);
533 pSpellArgs
.reset(new SwSpellArgs( xSpeller
, *pSttPos
, *pEndPos
, bGrammarCheck
));
535 SwNodeOffset nCurrNd
= pSttPos
->GetNodeIndex();
536 SwNodeOffset nEndNd
= pEndPos
->GetNodeIndex();
539 if( nCurrNd
<= nEndNd
)
541 SwContentFrame
* pContentFrame
;
545 SwNode
* pNd
= GetNodes()[ nCurrNd
];
546 switch( pNd
->GetNodeType() )
548 case SwNodeType::Text
:
549 if( nullptr != ( pContentFrame
= pNd
->GetTextNode()->getLayoutFrame( getIDocumentLayoutAccess().GetCurrentLayout() )) )
551 // skip protected and hidden Cells and Flys
552 if( pContentFrame
->IsProtected() )
554 nCurrNd
= pNd
->EndOfSectionIndex();
556 else if( !static_cast<SwTextFrame
*>(pContentFrame
)->IsHiddenNow() )
558 if( pPageCnt
&& *pPageCnt
&& pPageSt
)
560 sal_uInt16 nPageNr
= pContentFrame
->GetPhyPageNum();
564 if( *pPageCnt
< *pPageSt
)
565 *pPageCnt
= *pPageSt
;
568 if( nPageNr
>= *pPageSt
)
569 nStat
= nPageNr
- *pPageSt
+ 1;
571 nStat
= nPageNr
+ *pPageCnt
- *pPageSt
+ 1;
572 ::SetProgressState( nStat
, GetDocShell() );
574 //Spell() changes the pSpellArgs in case an error is found
575 sal_Int32 nBeginGrammarCheck
= 0;
576 sal_Int32 nEndGrammarCheck
= 0;
577 if( pSpellArgs
&& pSpellArgs
->bIsGrammarCheck
)
579 nBeginGrammarCheck
= &pSpellArgs
->pStartPos
->GetNode() == pNd
? pSpellArgs
->pStartPos
->GetContentIndex() : 0;
580 // if grammar checking starts inside of a sentence the start position has to be adjusted
581 if( nBeginGrammarCheck
)
583 SwContentIndex
aStartIndex( pNd
->GetTextNode(), nBeginGrammarCheck
);
584 SwPosition
aStart( *pNd
, aStartIndex
);
585 SwCursor
aCursor(aStart
, nullptr);
586 SwPosition aOrigPos
= *aCursor
.GetPoint();
587 aCursor
.GoSentence( SwCursor::START_SENT
);
588 if( aOrigPos
!= *aCursor
.GetPoint() )
590 nBeginGrammarCheck
= aCursor
.GetPoint()->GetContentIndex();
593 nEndGrammarCheck
= (&pSpellArgs
->pEndPos
->GetNode() == pNd
)
594 ? pSpellArgs
->pEndPos
->GetContentIndex()
596 ->GetText().getLength();
599 sal_Int32 nSpellErrorPosition
= pNd
->GetTextNode()->GetText().getLength();
600 if( (!pConvArgs
&& pNd
->GetTextNode()->Spell( pSpellArgs
.get() )) ||
601 ( pConvArgs
&& pNd
->GetTextNode()->Convert( *pConvArgs
)))
603 // Cancel and remember position
605 nSpellErrorPosition
= pSpellArgs
->pStartPos
->GetContentIndex() > pSpellArgs
->pEndPos
->GetContentIndex() ?
606 pSpellArgs
->pEndPos
->GetContentIndex() :
607 pSpellArgs
->pStartPos
->GetContentIndex();
608 if( nCurrNd
!= nEndNd
)
610 pSttPos
->Assign(nCurrNd
, pSttPos
->GetContentIndex());
611 pEndPos
->Assign(nCurrNd
, pEndPos
->GetContentIndex());
616 if( pSpellArgs
&& pSpellArgs
->bIsGrammarCheck
)
618 uno::Reference
< linguistic2::XProofreadingIterator
> xGCIterator( GetGCIterator() );
619 if (xGCIterator
.is())
621 uno::Reference
< lang::XComponent
> xDoc
= GetDocShell()->GetBaseModel();
622 // Expand the string:
623 const ModelToViewHelper
aConversionMap(*pNd
->GetTextNode(), pLayout
);
624 const OUString
& aExpandText
= aConversionMap
.getViewText();
626 // get XFlatParagraph to use...
627 uno::Reference
< text::XFlatParagraph
> xFlatPara
= new SwXFlatParagraph( *pNd
->GetTextNode(), aExpandText
, aConversionMap
);
629 // get error position of cursor in XFlatParagraph
630 linguistic2::ProofreadingResult aResult
;
634 aConversionMap
.ConvertToViewPosition( nBeginGrammarCheck
);
635 aResult
= xGCIterator
->checkSentenceAtPosition(
636 xDoc
, xFlatPara
, aExpandText
, lang::Locale(), nBeginGrammarCheck
, -1, -1 );
638 lcl_syncGrammarError( *pNd
->GetTextNode(), aResult
, aConversionMap
);
640 // get suggestions to use for the specific error position
641 bGrammarErrors
= aResult
.aErrors
.hasElements();
642 // if grammar checking doesn't have any progress then quit
643 if( aResult
.nStartOfNextSentencePosition
<= nBeginGrammarCheck
)
645 // prepare next iteration
646 nBeginGrammarCheck
= aResult
.nStartOfNextSentencePosition
;
648 while( nSpellErrorPosition
> aResult
.nBehindEndOfSentencePosition
&& !bGrammarErrors
&& aResult
.nBehindEndOfSentencePosition
< nEndGrammarCheck
);
650 if( bGrammarErrors
&& nSpellErrorPosition
>= aResult
.nBehindEndOfSentencePosition
)
653 //put the cursor to the current error
654 const linguistic2::SingleProofreadingError
&rError
= aResult
.aErrors
[0];
655 pSttPos
->Assign(nCurrNd
, pSttPos
->GetContentIndex());
656 pEndPos
->Assign(nCurrNd
, pEndPos
->GetContentIndex());
657 pSpellArgs
->pStartPos
->Assign(*pNd
->GetTextNode(), aConversionMap
.ConvertToModelPosition( rError
.nErrorStart
).mnPos
);
658 pSpellArgs
->pEndPos
->Assign(*pNd
->GetTextNode(), aConversionMap
.ConvertToModelPosition( rError
.nErrorStart
+ rError
.nErrorLength
).mnPos
);
666 case SwNodeType::Section
:
667 if( static_cast<SwSectionNode
*>(pNd
)->GetSection().IsProtect() ||
668 static_cast<SwSectionNode
*>(pNd
)->GetSection().IsHidden() )
669 nCurrNd
= pNd
->EndOfSectionIndex();
671 case SwNodeType::End
:
678 bGoOn
= nCurrNd
< nEndNd
;
683 if( !aRet
.hasValue() )
686 aRet
<<= pConvArgs
->aConvText
;
688 aRet
<<= pSpellArgs
->xSpellAlt
;
696 class SwHyphArgs
: public SwInterHyphInfo
698 SwNodeIndex m_aNodeIdx
;
699 const SwNode
*m_pStart
;
700 const SwNode
*m_pEnd
;
701 sal_uInt16
*m_pPageCnt
;
702 sal_uInt16
*m_pPageSt
;
704 sal_Int32 m_nPamStart
;
708 SwHyphArgs( const SwPaM
*pPam
, const Point
&rPoint
,
709 sal_uInt16
* pPageCount
, sal_uInt16
* pPageStart
);
710 void SetPam( SwPaM
*pPam
) const;
711 void SetNode( SwNode
& rNew
) { m_aNodeIdx
.Assign(rNew
); }
712 inline void SetRange( const SwNode
*pNew
);
713 void NextNode() { ++m_aNodeIdx
; }
714 sal_uInt16
*GetPageCnt() { return m_pPageCnt
; }
715 sal_uInt16
*GetPageSt() { return m_pPageSt
; }
720 SwHyphArgs::SwHyphArgs( const SwPaM
*pPam
, const Point
&rCursorPos
,
721 sal_uInt16
* pPageCount
, sal_uInt16
* pPageStart
)
722 : SwInterHyphInfo( rCursorPos
), m_aNodeIdx(pPam
->GetPoint()->GetNode()),
723 m_pPageCnt( pPageCount
), m_pPageSt( pPageStart
)
725 // The following constraints have to be met:
726 // 1) there is at least one Selection
727 // 2) SPoint() == Start()
728 OSL_ENSURE( pPam
->HasMark(), "SwDoc::Hyphenate: blowing in the wind");
729 OSL_ENSURE( *pPam
->GetPoint() <= *pPam
->GetMark(),
730 "SwDoc::Hyphenate: New York, New York");
732 const SwPosition
*pPoint
= pPam
->GetPoint();
735 m_pStart
= pPoint
->GetNode().GetTextNode();
736 m_nPamStart
= pPoint
->GetContentIndex();
738 // Set End and Length
739 const SwPosition
*pMark
= pPam
->GetMark();
740 m_pEnd
= pMark
->GetNode().GetTextNode();
741 m_nPamLen
= pMark
->GetContentIndex();
742 if( pPoint
->GetNode() == pMark
->GetNode() )
743 m_nPamLen
= m_nPamLen
- pPoint
->GetContentIndex();
746 inline void SwHyphArgs::SetRange( const SwNode
*pNew
)
748 m_nStart
= m_pStart
== pNew
? m_nPamStart
: 0;
749 m_nEnd
= m_pEnd
== pNew
? m_nPamStart
+ m_nPamLen
: SAL_MAX_INT32
;
752 void SwHyphArgs::SetPam( SwPaM
*pPam
) const
754 pPam
->GetPoint()->Assign( m_aNodeIdx
, m_nWordStart
);
755 pPam
->GetMark()->Assign( m_aNodeIdx
, m_nWordStart
+ m_nWordLen
);
758 // Returns true if we can proceed.
759 static bool lcl_HyphenateNode( SwNode
* pNd
, void* pArgs
)
761 // Hyphenate returns true if there is a hyphenation point and sets pPam
762 SwTextNode
*pNode
= pNd
->GetTextNode();
763 SwHyphArgs
*pHyphArgs
= static_cast<SwHyphArgs
*>(pArgs
);
766 // sw_redlinehide: this will be called once per node for merged nodes;
767 // the fully deleted ones won't have frames so are skipped.
768 SwContentFrame
* pContentFrame
= pNode
->getLayoutFrame( pNode
->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
769 if( pContentFrame
&& !static_cast<SwTextFrame
*>(pContentFrame
)->IsHiddenNow() )
771 sal_uInt16
*pPageSt
= pHyphArgs
->GetPageSt();
772 sal_uInt16
*pPageCnt
= pHyphArgs
->GetPageCnt();
773 if( pPageCnt
&& *pPageCnt
&& pPageSt
)
775 sal_uInt16 nPageNr
= pContentFrame
->GetPhyPageNum();
779 if( *pPageCnt
< *pPageSt
)
780 *pPageCnt
= *pPageSt
;
782 tools::Long nStat
= nPageNr
>= *pPageSt
? nPageNr
- *pPageSt
+ 1
783 : nPageNr
+ *pPageCnt
- *pPageSt
+ 1;
784 ::SetProgressState( nStat
, pNode
->GetDoc().GetDocShell() );
786 pHyphArgs
->SetRange( pNd
);
787 if( pNode
->Hyphenate( *pHyphArgs
) )
789 pHyphArgs
->SetNode( *pNd
);
794 pHyphArgs
->NextNode();
798 uno::Reference
< XHyphenatedWord
> SwDoc::Hyphenate(
799 SwPaM
*pPam
, const Point
&rCursorPos
,
800 sal_uInt16
* pPageCnt
, sal_uInt16
* pPageSt
)
802 OSL_ENSURE(this == &pPam
->GetDoc(), "SwDoc::Hyphenate: strangers in the night");
804 if( *pPam
->GetPoint() > *pPam
->GetMark() )
807 SwHyphArgs
aHyphArg( pPam
, rCursorPos
, pPageCnt
, pPageSt
);
808 SwNodeIndex
aTmpIdx( pPam
->GetMark()->GetNode(), 1 );
809 GetNodes().ForEach( pPam
->GetPoint()->GetNode(), aTmpIdx
.GetNode(),
810 lcl_HyphenateNode
, &aHyphArg
);
811 aHyphArg
.SetPam( pPam
);
812 return aHyphArg
.GetHyphWord(); // will be set by lcl_HyphenateNode
815 // Save the current values to add them as automatic entries to AutoCorrect.
816 void SwDoc::SetAutoCorrExceptWord( std::unique_ptr
<SwAutoCorrExceptWord
> pNew
)
818 mpACEWord
= std::move(pNew
);
821 void SwDoc::DeleteAutoCorrExceptWord()
826 void SwDoc::CountWords( const SwPaM
& rPaM
, SwDocStat
& rStat
)
828 // This is a modified version of SwDoc::TransliterateText
829 auto [pStt
, pEnd
] = rPaM
.StartEnd(); // SwPosition*
831 const SwNodeOffset nSttNd
= pStt
->GetNodeIndex();
832 const SwNodeOffset nEndNd
= pEnd
->GetNodeIndex();
834 const sal_Int32 nSttCnt
= pStt
->GetContentIndex();
835 const sal_Int32 nEndCnt
= pEnd
->GetContentIndex();
837 const SwTextNode
* pTNd
= pStt
->GetNode().GetTextNode();
838 if( pStt
== pEnd
&& pTNd
) // no region ?
844 if( nSttNd
!= nEndNd
)
846 SwNodeIndex
aIdx( pStt
->GetNode() );
851 pTNd
->CountWords( rStat
, nSttCnt
, pTNd
->GetText().getLength() );
854 for( ; aIdx
.GetIndex() < nEndNd
; ++aIdx
)
855 if( nullptr != ( pTNd
= aIdx
.GetNode().GetTextNode() ))
856 pTNd
->CountWords( rStat
, 0, pTNd
->GetText().getLength() );
858 if( nEndCnt
&& nullptr != ( pTNd
= pEnd
->GetNode().GetTextNode() ))
859 pTNd
->CountWords( rStat
, 0, nEndCnt
);
861 else if( pTNd
&& nSttCnt
< nEndCnt
)
862 pTNd
->CountWords( rStat
, nSttCnt
, nEndCnt
);
866 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */