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 <config_wasm_strip.h>
22 #include <hintids.hxx>
24 #include <svl/ctloptions.hxx>
25 #include <editeng/lspcitem.hxx>
26 #include <editeng/lrspitem.hxx>
27 #include <editeng/brushitem.hxx>
28 #include <editeng/pgrditem.hxx>
29 #include <unotools/configmgr.hxx>
30 #include <swmodule.hxx>
31 #include <SwSmartTagMgr.hxx>
33 #include <IDocumentSettingAccess.hxx>
34 #include <IDocumentDeviceAccess.hxx>
35 #include <IDocumentFieldsAccess.hxx>
36 #include <rootfrm.hxx>
37 #include <pagefrm.hxx>
42 #include <viewopt.hxx>
46 #include <frmtool.hxx>
47 #include <tgrditem.hxx>
48 #include <dbg_lay.hxx>
53 #include <ftninfo.hxx>
54 #include <fmtline.hxx>
56 #include <notxtfrm.hxx>
57 #include <sectfrm.hxx>
58 #include "itrform2.hxx"
60 #include "txtcache.hxx"
61 #include <fntcache.hxx>
62 #include <SwGrammarMarkUp.hxx>
63 #include <lineinfo.hxx>
64 #include <SwPortionHandler.hxx>
65 #include <dcontact.hxx>
66 #include <sortedobjs.hxx>
67 #include <txtflcnt.hxx>
68 #include <fmtflcnt.hxx>
69 #include <fmtcntnt.hxx>
70 #include <numrule.hxx>
71 #include <GrammarContact.hxx>
79 #include <FrameControlsManager.hxx>
83 MergedAttrIterBase::MergedAttrIterBase(SwTextFrame
const& rFrame
)
84 : m_pMerged(rFrame
.GetMergedPara())
85 , m_pNode(m_pMerged
? nullptr : rFrame
.GetTextNodeFirst())
91 SwTextAttr
const* MergedAttrIter::NextAttr(SwTextNode
const** ppNode
)
95 while (m_CurrentExtent
< m_pMerged
->extents
.size())
97 sw::Extent
const& rExtent(m_pMerged
->extents
[m_CurrentExtent
]);
98 if (SwpHints
const*const pHints
= rExtent
.pNode
->GetpSwpHints())
100 while (m_CurrentHint
< pHints
->Count())
102 SwTextAttr
*const pHint(pHints
->Get(m_CurrentHint
));
103 if (rExtent
.nEnd
< pHint
->GetStart()
104 // <= if it has no end or isn't empty
105 || (rExtent
.nEnd
== pHint
->GetStart()
107 || *pHint
->GetEnd() != pHint
->GetStart())))
112 if (rExtent
.nStart
<= pHint
->GetStart())
116 *ppNode
= rExtent
.pNode
;
123 if (m_CurrentExtent
< m_pMerged
->extents
.size() &&
124 rExtent
.pNode
!= m_pMerged
->extents
[m_CurrentExtent
].pNode
)
126 m_CurrentHint
= 0; // reset
133 SwpHints
const*const pHints(m_pNode
->GetpSwpHints());
136 if (m_CurrentHint
< pHints
->Count())
138 SwTextAttr
const*const pHint(pHints
->Get(m_CurrentHint
));
151 MergedAttrIterByEnd::MergedAttrIterByEnd(SwTextFrame
const& rFrame
)
152 : m_pNode(rFrame
.GetMergedPara() ? nullptr : rFrame
.GetTextNodeFirst())
157 MergedAttrIterReverse
iter(rFrame
);
158 SwTextNode
const* pNode(nullptr);
159 while (SwTextAttr
const* pHint
= iter
.PrevAttr(&pNode
))
161 m_Hints
.emplace_back(pNode
, pHint
);
166 SwTextAttr
const* MergedAttrIterByEnd::NextAttr(SwTextNode
const*& rpNode
)
170 SwpHints
const*const pHints(m_pNode
->GetpSwpHints());
173 if (m_CurrentHint
< pHints
->Count())
175 SwTextAttr
const*const pHint(
176 pHints
->GetSortedByEnd(m_CurrentHint
));
186 if (m_CurrentHint
< m_Hints
.size())
188 auto const ret
= m_Hints
[m_Hints
.size() - m_CurrentHint
- 1];
197 void MergedAttrIterByEnd::PrevAttr()
199 assert(0 < m_CurrentHint
); // should only rewind as far as 0
203 MergedAttrIterReverse::MergedAttrIterReverse(SwTextFrame
const& rFrame
)
204 : MergedAttrIterBase(rFrame
)
208 m_CurrentExtent
= m_pMerged
->extents
.size();
209 SwpHints
const*const pHints(0 < m_CurrentExtent
210 ? m_pMerged
->extents
[m_CurrentExtent
-1].pNode
->GetpSwpHints()
214 pHints
->SortIfNeedBe();
215 m_CurrentHint
= pHints
->Count();
220 if (SwpHints
const*const pHints
= m_pNode
->GetpSwpHints())
222 pHints
->SortIfNeedBe();
223 m_CurrentHint
= pHints
->Count();
228 SwTextAttr
const* MergedAttrIterReverse::PrevAttr(SwTextNode
const** ppNode
)
232 while (0 < m_CurrentExtent
)
234 sw::Extent
const& rExtent(m_pMerged
->extents
[m_CurrentExtent
-1]);
235 if (SwpHints
const*const pHints
= rExtent
.pNode
->GetpSwpHints())
237 while (0 < m_CurrentHint
)
239 SwTextAttr
*const pHint(
240 pHints
->GetSortedByEnd(m_CurrentHint
- 1));
241 if (pHint
->GetAnyEnd() < rExtent
.nStart
242 // <= if it has end and isn't empty
244 && *pHint
->GetEnd() != pHint
->GetStart()
245 && *pHint
->GetEnd() == rExtent
.nStart
))
250 if (pHint
->GetAnyEnd() <= rExtent
.nEnd
)
254 *ppNode
= rExtent
.pNode
;
261 if (0 < m_CurrentExtent
&&
262 rExtent
.pNode
!= m_pMerged
->extents
[m_CurrentExtent
-1].pNode
)
264 SwpHints
const*const pHints(
265 m_pMerged
->extents
[m_CurrentExtent
-1].pNode
->GetpSwpHints());
266 m_CurrentHint
= pHints
? pHints
->Count() : 0; // reset
268 pHints
->SortIfNeedBe();
275 SwpHints
const*const pHints(m_pNode
->GetpSwpHints());
276 if (pHints
&& 0 < m_CurrentHint
)
278 SwTextAttr
const*const pHint(pHints
->GetSortedByEnd(m_CurrentHint
- 1));
290 bool FrameContainsNode(SwContentFrame
const& rFrame
, SwNodeOffset
const nNodeIndex
)
292 if (rFrame
.IsTextFrame())
294 SwTextFrame
const& rTextFrame(static_cast<SwTextFrame
const&>(rFrame
));
295 if (sw::MergedPara
const*const pMerged
= rTextFrame
.GetMergedPara())
297 SwNodeOffset
const nFirst(pMerged
->pFirstNode
->GetIndex());
298 SwNodeOffset
const nLast(pMerged
->pLastNode
->GetIndex());
299 return (nFirst
<= nNodeIndex
&& nNodeIndex
<= nLast
);
303 return rTextFrame
.GetTextNodeFirst()->GetIndex() == nNodeIndex
;
308 assert(rFrame
.IsNoTextFrame());
309 return static_cast<SwNoTextFrame
const&>(rFrame
).GetNode()->GetIndex() == nNodeIndex
;
313 bool IsParaPropsNode(SwRootFrame
const& rLayout
, SwTextNode
const& rNode
)
315 if (rLayout
.HasMergedParas())
317 if (SwTextFrame
const*const pFrame
= static_cast<SwTextFrame
*>(rNode
.getLayoutFrame(&rLayout
)))
319 sw::MergedPara
const*const pMerged(pFrame
->GetMergedPara());
320 if (pMerged
&& pMerged
->pParaPropsNode
!= &rNode
)
330 GetParaPropsNode(SwRootFrame
const& rLayout
, SwNode
const& rPos
)
332 const SwTextNode
*const pTextNode(rPos
.GetTextNode());
333 if (pTextNode
&& !sw::IsParaPropsNode(rLayout
, *pTextNode
))
335 return static_cast<SwTextFrame
*>(pTextNode
->getLayoutFrame(&rLayout
))->GetMergedPara()->pParaPropsNode
;
339 return const_cast<SwTextNode
*>(pTextNode
);
344 GetParaPropsPos(SwRootFrame
const& rLayout
, SwPosition
const& rPos
)
346 SwPosition
pos(rPos
);
347 SwTextNode
const*const pNode(pos
.GetNode().GetTextNode());
349 pos
.Assign( *sw::GetParaPropsNode(rLayout
, *pNode
) );
353 std::pair
<SwTextNode
*, SwTextNode
*>
354 GetFirstAndLastNode(SwRootFrame
const& rLayout
, SwNode
const& rPos
)
356 SwTextNode
*const pTextNode(const_cast<SwTextNode
*>(rPos
.GetTextNode()));
357 if (pTextNode
&& rLayout
.HasMergedParas())
359 if (SwTextFrame
const*const pFrame
= static_cast<SwTextFrame
*>(pTextNode
->getLayoutFrame(&rLayout
)))
361 if (sw::MergedPara
const*const pMerged
= pFrame
->GetMergedPara())
363 return std::make_pair(pMerged
->pFirstNode
, const_cast<SwTextNode
*>(pMerged
->pLastNode
));
367 return std::make_pair(pTextNode
, pTextNode
);
370 SwTextNode
const& GetAttrMerged(SfxItemSet
& rFormatSet
,
371 SwTextNode
const& rNode
, SwRootFrame
const*const pLayout
)
373 rNode
.SwContentNode::GetAttr(rFormatSet
);
374 if (pLayout
&& pLayout
->HasMergedParas())
376 auto pFrame
= static_cast<SwTextFrame
*>(rNode
.getLayoutFrame(pLayout
));
377 if (sw::MergedPara
const*const pMerged
= pFrame
? pFrame
->GetMergedPara() : nullptr)
379 if (pMerged
->pFirstNode
!= &rNode
)
381 rFormatSet
.ClearItem(RES_PAGEDESC
);
382 rFormatSet
.ClearItem(RES_BREAK
);
383 static_assert(RES_PAGEDESC
+ 1 == sal_uInt16(RES_BREAK
),
384 "first-node items must be adjacent");
385 SfxItemSetFixed
<RES_PAGEDESC
, RES_BREAK
> firstSet(*rFormatSet
.GetPool());
386 pMerged
->pFirstNode
->SwContentNode::GetAttr(firstSet
);
387 rFormatSet
.Put(firstSet
);
390 if (pMerged
->pParaPropsNode
!= &rNode
)
392 for (sal_uInt16 i
= RES_PARATR_BEGIN
; i
!= RES_FRMATR_END
; ++i
)
394 if (i
!= RES_PAGEDESC
&& i
!= RES_BREAK
)
396 rFormatSet
.ClearItem(i
);
399 for (sal_uInt16 i
= XATTR_FILL_FIRST
; i
<= XATTR_FILL_LAST
; ++i
)
401 rFormatSet
.ClearItem(i
);
403 SfxItemSetFixed
<RES_PARATR_BEGIN
, RES_PAGEDESC
,
404 RES_BREAK
+1, RES_FRMATR_END
,
405 XATTR_FILL_FIRST
, XATTR_FILL_LAST
+1>
406 propsSet(*rFormatSet
.GetPool());
407 pMerged
->pParaPropsNode
->SwContentNode::GetAttr(propsSet
);
408 rFormatSet
.Put(propsSet
);
409 return *pMerged
->pParaPropsNode
;
411 // keep all the CHRATR/UNKNOWNATR anyway...
419 /// Switches width and height of the text frame
420 void SwTextFrame::SwapWidthAndHeight()
423 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*this);
427 const tools::Long nPrtOfstX
= aPrt
.Pos().X();
428 aPrt
.Pos().setX( aPrt
.Pos().Y() );
432 aPrt
.Pos().setY( nPrtOfstX
);
436 aPrt
.Pos().setY( getFrameArea().Width() - ( nPrtOfstX
+ aPrt
.Width() ) );
441 const tools::Long nPrtOfstY
= aPrt
.Pos().Y();
442 aPrt
.Pos().setY( aPrt
.Pos().X() );
446 aPrt
.Pos().setX( nPrtOfstY
);
450 aPrt
.Pos().setX( getFrameArea().Height() - ( nPrtOfstY
+ aPrt
.Height() ) );
454 const tools::Long nPrtWidth
= aPrt
.Width();
455 aPrt
.Width( aPrt
.Height() );
456 aPrt
.Height( nPrtWidth
);
460 const tools::Long nFrameWidth
= getFrameArea().Width();
461 SwFrameAreaDefinition::FrameAreaWriteAccess
aFrm(*this);
462 aFrm
.Width( aFrm
.Height() );
463 aFrm
.Height( nFrameWidth
);
466 mbIsSwapped
= ! mbIsSwapped
;
470 * Calculates the coordinates of a rectangle when switching from
471 * horizontal to vertical layout.
473 void SwTextFrame::SwitchHorizontalToVertical( SwRect
& rRect
) const
475 // calc offset inside frame
476 tools::Long nOfstX
, nOfstY
;
481 // X and Y offsets here mean the position of the point that will be the top left corner
483 nOfstX
= rRect
.Left() + rRect
.Width() - getFrameArea().Left();
484 nOfstY
= rRect
.Top() - getFrameArea().Top();
488 nOfstX
= rRect
.Left() - getFrameArea().Left();
489 nOfstY
= rRect
.Top() - getFrameArea().Top();
494 nOfstX
= rRect
.Left() - getFrameArea().Left();
495 nOfstY
= rRect
.Top() + rRect
.Height() - getFrameArea().Top();
498 const tools::Long nWidth
= rRect
.Width();
499 const tools::Long nHeight
= rRect
.Height();
503 rRect
.Left(getFrameArea().Left() + nOfstY
);
508 rRect
.Left( getFrameArea().Left() + getFrameArea().Height() - nOfstY
);
511 rRect
.Left( getFrameArea().Left() + getFrameArea().Width() - nOfstY
);
517 rRect
.Top(getFrameArea().Top() + getFrameArea().Width() - nOfstX
);
519 rRect
.Top(getFrameArea().Top() + getFrameArea().Height() - nOfstX
);
522 rRect
.Top(getFrameArea().Top() + nOfstX
);
523 rRect
.Width( nHeight
);
524 rRect
.Height( nWidth
);
528 * Calculates the coordinates of a point when switching from
529 * horizontal to vertical layout.
531 void SwTextFrame::SwitchHorizontalToVertical( Point
& rPoint
) const
535 // The horizontal origo is the top left corner, the LRBT origo is the
536 // bottom left corner. Finally x and y has to be swapped.
537 SAL_WARN_IF(!mbIsSwapped
, "sw.core",
538 "SwTextFrame::SwitchHorizontalToVertical, IsVertLRBT, not swapped");
539 Point
aPoint(rPoint
);
540 rPoint
.setX(getFrameArea().Left() + (aPoint
.Y() - getFrameArea().Top()));
541 // This would be bottom - x delta, but bottom is top + height, finally
542 // width (and not height), as it's swapped.
543 rPoint
.setY(getFrameArea().Top() + getFrameArea().Width()
544 - (aPoint
.X() - getFrameArea().Left()));
548 // calc offset inside frame
549 const tools::Long nOfstX
= rPoint
.X() - getFrameArea().Left();
550 const tools::Long nOfstY
= rPoint
.Y() - getFrameArea().Top();
552 rPoint
.setX( getFrameArea().Left() + nOfstY
);
556 rPoint
.setX( getFrameArea().Left() + getFrameArea().Height() - nOfstY
);
558 // calc rotated coords
559 rPoint
.setX( getFrameArea().Left() + getFrameArea().Width() - nOfstY
);
562 rPoint
.setY( getFrameArea().Top() + nOfstX
);
566 * Calculates the a limit value when switching from
567 * horizontal to vertical layout.
569 tools::Long
SwTextFrame::SwitchHorizontalToVertical( tools::Long nLimit
) const
571 Point
aTmp( 0, nLimit
);
572 SwitchHorizontalToVertical( aTmp
);
577 * Calculates the coordinates of a rectangle when switching from
578 * vertical to horizontal layout.
580 void SwTextFrame::SwitchVerticalToHorizontal( SwRect
& rRect
) const
584 // calc offset inside frame
586 nOfstX
= rRect
.Left() - getFrameArea().Left();
590 nOfstX
= getFrameArea().Left() + getFrameArea().Height() - ( rRect
.Left() + rRect
.Width() );
592 nOfstX
= getFrameArea().Left() + getFrameArea().Width() - ( rRect
.Left() + rRect
.Width() );
598 // Note that mbIsSwapped only affects the frame area, not rRect, so rRect.Height() is used
599 // here unconditionally.
601 nOfstY
= getFrameArea().Top() + getFrameArea().Width() - (rRect
.Top() + rRect
.Height());
603 nOfstY
= getFrameArea().Top() + getFrameArea().Height() - (rRect
.Top() + rRect
.Height());
606 nOfstY
= rRect
.Top() - getFrameArea().Top();
607 const tools::Long nWidth
= rRect
.Height();
608 const tools::Long nHeight
= rRect
.Width();
610 // calc rotated coords
611 rRect
.Left( getFrameArea().Left() + nOfstY
);
612 rRect
.Top( getFrameArea().Top() + nOfstX
);
613 rRect
.Width( nWidth
);
614 rRect
.Height( nHeight
);
618 * Calculates the coordinates of a point when switching from
619 * vertical to horizontal layout.
621 void SwTextFrame::SwitchVerticalToHorizontal( Point
& rPoint
) const
625 // calc offset inside frame
627 // X offset is Y - left.
628 nOfstX
= rPoint
.X() - getFrameArea().Left();
631 // X offset is right - X.
633 nOfstX
= getFrameArea().Left() + getFrameArea().Height() - rPoint
.X();
635 nOfstX
= getFrameArea().Left() + getFrameArea().Width() - rPoint
.X();
641 // Y offset is bottom - Y.
643 nOfstY
= getFrameArea().Top() + getFrameArea().Width() - rPoint
.Y();
645 nOfstY
= getFrameArea().Top() + getFrameArea().Height() - rPoint
.Y();
648 // Y offset is Y - top.
649 nOfstY
= rPoint
.Y() - getFrameArea().Top();
651 // calc rotated coords
652 rPoint
.setX( getFrameArea().Left() + nOfstY
);
653 rPoint
.setY( getFrameArea().Top() + nOfstX
);
657 * Calculates the a limit value when switching from
658 * vertical to horizontal layout.
660 tools::Long
SwTextFrame::SwitchVerticalToHorizontal( tools::Long nLimit
) const
662 Point
aTmp( nLimit
, 0 );
663 SwitchVerticalToHorizontal( aTmp
);
667 SwFrameSwapper::SwFrameSwapper( const SwTextFrame
* pTextFrame
, bool bSwapIfNotSwapped
)
668 : pFrame( pTextFrame
), bUndo( false )
670 if (pFrame
->IsVertical() && bSwapIfNotSwapped
!= pFrame
->IsSwapped())
673 const_cast<SwTextFrame
*>(pFrame
)->SwapWidthAndHeight();
677 SwFrameSwapper::~SwFrameSwapper()
680 const_cast<SwTextFrame
*>(pFrame
)->SwapWidthAndHeight();
683 void SwTextFrame::SwitchLTRtoRTL( SwRect
& rRect
) const
685 SwSwapIfNotSwapped
swap(const_cast<SwTextFrame
*>(this));
687 tools::Long nWidth
= rRect
.Width();
688 rRect
.Left( 2 * ( getFrameArea().Left() + getFramePrintArea().Left() ) +
689 getFramePrintArea().Width() - rRect
.Right() - 1 );
691 rRect
.Width( nWidth
);
694 void SwTextFrame::SwitchLTRtoRTL( Point
& rPoint
) const
696 SwSwapIfNotSwapped
swap(const_cast<SwTextFrame
*>(this));
698 rPoint
.setX( 2 * ( getFrameArea().Left() + getFramePrintArea().Left() ) + getFramePrintArea().Width() - rPoint
.X() - 1 );
701 SwLayoutModeModifier::SwLayoutModeModifier( const OutputDevice
& rOutp
) :
702 m_rOut( rOutp
), m_nOldLayoutMode( rOutp
.GetLayoutMode() )
706 SwLayoutModeModifier::~SwLayoutModeModifier()
708 const_cast<OutputDevice
&>(m_rOut
).SetLayoutMode( m_nOldLayoutMode
);
711 void SwLayoutModeModifier::Modify( bool bChgToRTL
)
713 const_cast<OutputDevice
&>(m_rOut
).SetLayoutMode( bChgToRTL
?
714 vcl::text::ComplexTextLayoutFlags::BiDiStrong
| vcl::text::ComplexTextLayoutFlags::BiDiRtl
:
715 vcl::text::ComplexTextLayoutFlags::BiDiStrong
);
718 void SwLayoutModeModifier::SetAuto()
720 const vcl::text::ComplexTextLayoutFlags nNewLayoutMode
= m_nOldLayoutMode
& ~vcl::text::ComplexTextLayoutFlags::BiDiStrong
;
721 const_cast<OutputDevice
&>(m_rOut
).SetLayoutMode( nNewLayoutMode
);
724 SwDigitModeModifier::SwDigitModeModifier( const OutputDevice
& rOutp
, LanguageType eCurLang
) :
725 rOut( rOutp
), nOldLanguageType( rOutp
.GetDigitLanguage() )
727 LanguageType eLang
= eCurLang
;
728 if (utl::ConfigManager::IsFuzzing())
729 eLang
= LANGUAGE_ENGLISH_US
;
732 const SvtCTLOptions::TextNumerals nTextNumerals
= SvtCTLOptions::GetCTLTextNumerals();
734 if ( SvtCTLOptions::NUMERALS_HINDI
== nTextNumerals
)
735 eLang
= LANGUAGE_ARABIC_SAUDI_ARABIA
;
736 else if ( SvtCTLOptions::NUMERALS_ARABIC
== nTextNumerals
)
737 eLang
= LANGUAGE_ENGLISH
;
738 else if ( SvtCTLOptions::NUMERALS_SYSTEM
== nTextNumerals
)
739 eLang
= ::GetAppLanguage();
742 const_cast<OutputDevice
&>(rOut
).SetDigitLanguage( eLang
);
745 SwDigitModeModifier::~SwDigitModeModifier()
747 const_cast<OutputDevice
&>(rOut
).SetDigitLanguage( nOldLanguageType
);
750 void SwTextFrame::Init()
752 OSL_ENSURE( !IsLocked(), "+SwTextFrame::Init: this is locked." );
756 SetHasRotatedPortions(false);
757 // set flags directly to save a ResetPreps call,
758 // and thereby an unnecessary GetPara call
759 // don't set bOrphan, bLocked or bWait to false!
760 // bOrphan = bFlag7 = bFlag8 = false;
764 SwTextFrame::SwTextFrame(SwTextNode
* const pNode
, SwFrame
* pSib
,
765 sw::FrameMode
const eMode
)
766 : SwContentFrame( pNode
, pSib
)
769 , mnFlyAnchorOfst( 0 )
770 , mnFlyAnchorOfstNoWrap( 0 )
771 , mnFlyAnchorVertOfstNoWrap( 0 )
772 , mnFootnoteLine( 0 )
773 , mnHeightOfLastLine( 0 )
774 , mnAdditionalFirstLineOffset( 0 )
776 , mnCacheIndex( USHRT_MAX
)
779 , mbJustWidow( false )
781 , mbInFootnoteConnect( false )
782 , mbFootnote( false )
784 , mbHasRotatedPortions( false )
785 , mbFieldFollow( false )
786 , mbHasAnimation( false )
787 , mbIsSwapped( false )
788 , mbFollowFormatAllowed( true )
790 mnFrameType
= SwFrameType::Txt
;
791 // note: this may call SwClientNotify if it's in a list so do it last
792 // note: this may change this->pRegisteredIn to m_pMergedPara->listeners
793 m_pMergedPara
= CheckParaRedlineMerge(*this, *pNode
, eMode
);
798 SwTextFrame
* MakeTextFrame(SwTextNode
& rNode
, SwFrame
*const pSibling
,
799 sw::FrameMode
const eMode
)
801 return new SwTextFrame(&rNode
, pSibling
, eMode
);
804 void RemoveFootnotesForNode(
805 SwRootFrame
const& rLayout
, SwTextNode
const& rTextNode
,
806 std::vector
<std::pair
<sal_Int32
, sal_Int32
>> const*const pExtents
)
808 if (pExtents
&& pExtents
->empty())
810 return; // nothing to do
812 const SwFootnoteIdxs
&rFootnoteIdxs
= rTextNode
.GetDoc().GetFootnoteIdxs();
814 SwNodeOffset
const nIndex
= rTextNode
.GetIndex();
815 rFootnoteIdxs
.SeekEntry( rTextNode
, &nPos
);
816 if (nPos
< rFootnoteIdxs
.size())
818 while (nPos
&& rTextNode
== (rFootnoteIdxs
[ nPos
]->GetTextNode()))
820 if (nPos
|| rTextNode
!= (rFootnoteIdxs
[ nPos
]->GetTextNode()))
824 for ( ; nPos
< rFootnoteIdxs
.size(); ++nPos
)
826 SwTextFootnote
* pTextFootnote
= rFootnoteIdxs
[ nPos
];
827 if (pTextFootnote
->GetTextNode().GetIndex() > nIndex
)
831 while ((*pExtents
)[iter
].second
<= pTextFootnote
->GetStart())
834 if (iter
== pExtents
->size())
839 if (pTextFootnote
->GetStart() < (*pExtents
)[iter
].first
)
844 pTextFootnote
->DelFrames(&rLayout
);
850 void SwTextFrame::DestroyImpl()
852 // Remove associated SwParaPortion from s_pTextCache
855 assert(!GetDoc().IsInDtor()); // this shouldn't be happening with ViewShell owning layout
856 if (!GetDoc().IsInDtor() && HasFootnote())
860 SwTextNode
const* pNode(nullptr);
861 for (auto const& e
: m_pMergedPara
->extents
)
863 if (e
.pNode
!= pNode
)
866 // sw_redlinehide: not sure if it's necessary to check
867 // if the nodes are still alive here, which would require
868 // accessing WriterMultiListener::m_vDepends
869 sw::RemoveFootnotesForNode(*getRootFrame(), *pNode
, nullptr);
875 SwTextNode
*const pNode(static_cast<SwTextNode
*>(GetDep()));
878 sw::RemoveFootnotesForNode(*getRootFrame(), *pNode
, nullptr);
883 if (!GetDoc().IsInDtor())
885 if (SwView
* pView
= GetActiveView())
886 pView
->GetEditWin().GetFrameControlsManager().RemoveControls(this);
889 SwContentFrame::DestroyImpl();
892 SwTextFrame::~SwTextFrame()
899 // 1. if real insert => correct nStart/nEnd for full nLen
900 // 2. if rl un-delete => do not correct nStart/nEnd but just include un-deleted
901 static TextFrameIndex
UpdateMergedParaForInsert(MergedPara
& rMerged
,
902 bool const isRealInsert
,
903 SwTextNode
const& rNode
, sal_Int32
const nIndex
, sal_Int32
const nLen
)
905 assert(!isRealInsert
|| nLen
); // can 0 happen? yes, for redline in empty node
906 assert(nIndex
<= rNode
.Len());
907 assert(nIndex
+ nLen
<= rNode
.Len());
908 assert(rMerged
.pFirstNode
->GetIndex() <= rNode
.GetIndex() && rNode
.GetIndex() <= rMerged
.pLastNode
->GetIndex());
911 return TextFrameIndex(0);
913 OUStringBuffer
text(rMerged
.mergedText
);
914 sal_Int32
nTFIndex(0); // index used for insertion at the end
915 sal_Int32
nInserted(0);
916 bool bInserted(false);
917 bool bFoundNode(false);
918 auto itInsert(rMerged
.extents
.end());
919 for (auto it
= rMerged
.extents
.begin(); it
!= rMerged
.extents
.end(); ++it
)
921 if (it
->pNode
== &rNode
)
926 if (it
->nStart
<= nIndex
&& nIndex
<= it
->nEnd
)
927 { // note: this can happen only once
928 text
.insert(nTFIndex
+ (nIndex
- it
->nStart
),
929 rNode
.GetText().subView(nIndex
, nLen
));
935 else if (nIndex
< it
->nStart
)
937 if (itInsert
== rMerged
.extents
.end())
947 assert(it
== rMerged
.extents
.begin() || (it
-1)->pNode
!= &rNode
|| (it
-1)->nEnd
< nIndex
);
948 if (nIndex
+ nLen
< it
->nStart
)
953 if (nIndex
< it
->nStart
)
955 text
.insert(nTFIndex
,
956 rNode
.GetText().subView(nIndex
, it
->nStart
- nIndex
));
957 nInserted
+= it
->nStart
- nIndex
;
961 assert(it
->nStart
<= nIndex
);
962 if (nIndex
<= it
->nEnd
)
964 nTFIndex
+= it
->nEnd
- it
->nStart
;
965 while (it
->nEnd
< nIndex
+ nLen
)
968 (it
+1) != rMerged
.extents
.end() && (it
+1)->pNode
== it
->pNode
971 if (pNext
&& pNext
->nStart
<= nIndex
+ nLen
)
973 text
.insert(nTFIndex
,
974 rNode
.GetText().subView(it
->nEnd
, pNext
->nStart
- it
->nEnd
));
975 nTFIndex
+= pNext
->nStart
- it
->nEnd
;
976 nInserted
+= pNext
->nStart
- it
->nEnd
;
977 pNext
->nStart
= it
->nStart
;
978 it
= rMerged
.extents
.erase(it
);
982 text
.insert(nTFIndex
,
983 rNode
.GetText().subView(it
->nEnd
, nIndex
+ nLen
- it
->nEnd
));
984 nTFIndex
+= nIndex
+ nLen
- it
->nEnd
;
985 nInserted
+= nIndex
+ nLen
- it
->nEnd
;
986 it
->nEnd
= nIndex
+ nLen
;
994 else if (rNode
.GetIndex() < it
->pNode
->GetIndex() || bFoundNode
)
996 if (itInsert
== rMerged
.extents
.end())
1002 if (itInsert
== rMerged
.extents
.end())
1004 nTFIndex
+= it
->nEnd
- it
->nStart
;
1007 // assert((bFoundNode || rMerged.extents.empty()) && "text node not found - why is it sending hints to us");
1009 { // must be in a gap
1010 rMerged
.extents
.emplace(itInsert
, const_cast<SwTextNode
*>(&rNode
), nIndex
, nIndex
+ nLen
);
1011 text
.insert(nTFIndex
, rNode
.GetText().subView(nIndex
, nLen
));
1013 if (rMerged
.extents
.size() == 1 // also if it was empty!
1014 || rMerged
.pParaPropsNode
->GetIndex() < rNode
.GetIndex())
1015 { // text inserted after current para-props node
1016 rMerged
.pParaPropsNode
->RemoveFromListRLHidden();
1017 rMerged
.pParaPropsNode
= &const_cast<SwTextNode
&>(rNode
);
1018 rMerged
.pParaPropsNode
->AddToListRLHidden();
1020 // called from SwRangeRedline::InvalidateRange()
1021 if (rNode
.GetRedlineMergeFlag() == SwNode::Merge::Hidden
)
1023 const_cast<SwTextNode
&>(rNode
).SetRedlineMergeFlag(SwNode::Merge::NonFirst
);
1026 rMerged
.mergedText
= text
.makeStringAndClear();
1027 return TextFrameIndex(nInserted
);
1030 // 1. if real delete => correct nStart/nEnd for full nLen
1031 // 2. if rl delete => do not correct nStart/nEnd but just exclude deleted
1032 TextFrameIndex
UpdateMergedParaForDelete(MergedPara
& rMerged
,
1033 bool const isRealDelete
,
1034 SwTextNode
const& rNode
, sal_Int32 nIndex
, sal_Int32
const nLen
)
1036 assert(nIndex
<= rNode
.Len());
1037 assert(rMerged
.pFirstNode
->GetIndex() <= rNode
.GetIndex() && rNode
.GetIndex() <= rMerged
.pLastNode
->GetIndex());
1038 OUStringBuffer
text(rMerged
.mergedText
);
1039 sal_Int32
nTFIndex(0);
1040 sal_Int32
nToDelete(nLen
);
1041 sal_Int32
nDeleted(0);
1042 size_t nFoundNode(0);
1044 auto it
= rMerged
.extents
.begin();
1045 for (; it
!= rMerged
.extents
.end(); )
1048 if (it
->pNode
== &rNode
)
1051 if (nIndex
+ nToDelete
< it
->nStart
)
1063 if (nIndex
< it
->nStart
)
1065 // do not adjust nIndex into the text frame index space!
1066 nToDelete
-= it
->nStart
- nIndex
;
1067 nIndex
= it
->nStart
;
1068 // note: continue with the if check below, no else!
1070 if (it
->nStart
<= nIndex
&& nIndex
< it
->nEnd
)
1072 sal_Int32
const nDeleteHere(nIndex
+ nToDelete
<= it
->nEnd
1074 : it
->nEnd
- nIndex
);
1075 text
.remove(nTFIndex
+ (nIndex
- it
->nStart
), nDeleteHere
);
1076 bErase
= nDeleteHere
== it
->nEnd
- it
->nStart
;
1080 assert(it
->nStart
== nIndex
);
1081 it
= rMerged
.extents
.erase(it
);
1083 else if (isRealDelete
)
1084 { // adjust for deleted text
1085 it
->nStart
-= (nLen
- nToDelete
);
1086 it
->nEnd
-= (nLen
- nToDelete
+ nDeleteHere
);
1087 if (it
!= rMerged
.extents
.begin()
1088 && (it
-1)->pNode
== &rNode
1089 && (it
-1)->nEnd
== it
->nStart
)
1090 { // merge adjacent extents
1091 nTFIndex
+= it
->nEnd
- it
->nStart
;
1092 (it
-1)->nEnd
= it
->nEnd
;
1093 it
= rMerged
.extents
.erase(it
);
1094 bErase
= true; // skip increment
1098 { // exclude text marked as deleted
1099 if (nIndex
+ nDeleteHere
== it
->nEnd
)
1101 it
->nEnd
-= nDeleteHere
;
1105 if (nIndex
== it
->nStart
)
1107 it
->nStart
+= nDeleteHere
;
1111 sal_Int32
const nOldEnd(it
->nEnd
);
1113 it
= rMerged
.extents
.emplace(it
+1,
1114 it
->pNode
, nIndex
+ nDeleteHere
, nOldEnd
);
1116 assert(nDeleteHere
== nToDelete
);
1119 nDeleted
+= nDeleteHere
;
1120 nToDelete
-= nDeleteHere
;
1121 nIndex
+= nDeleteHere
;
1122 if (!isRealDelete
&& nToDelete
== 0)
1129 else if (nFoundNode
!= 0)
1135 nTFIndex
+= it
->nEnd
- it
->nStart
;
1139 // assert(nFoundNode != 0 && "text node not found - why is it sending hints to us");
1140 assert(nIndex
<= rNode
.Len() + nLen
);
1141 // if there's a remaining deletion, it must be in gap at the end of the node
1142 // can't do: might be last one in node was erased assert(nLen == 0 || rMerged.empty() || (it-1)->nEnd <= nIndex);
1143 // note: if first node gets deleted then that must call DelFrames as
1144 // pFirstNode is never updated
1145 if (nErased
&& nErased
== nFoundNode
)
1146 { // all visible text from node was erased
1148 if (rMerged
.pParaPropsNode
== &rNode
)
1150 rMerged
.pParaPropsNode
->RemoveFromListRLHidden();
1151 rMerged
.pParaPropsNode
= rMerged
.extents
.empty()
1152 ? const_cast<SwTextNode
*>(rMerged
.pLastNode
)
1153 : rMerged
.extents
.front().pNode
;
1154 rMerged
.pParaPropsNode
->AddToListRLHidden();
1157 // NOPE must listen on all non-hidden nodes; particularly on pLastNode rMerged.listener.EndListening(&const_cast<SwTextNode&>(rNode));
1159 rMerged
.mergedText
= text
.makeStringAndClear();
1160 return TextFrameIndex(nDeleted
);
1163 std::pair
<SwTextNode
*, sal_Int32
>
1164 MapViewToModel(MergedPara
const& rMerged
, TextFrameIndex
const i_nIndex
)
1166 sal_Int32
nIndex(i_nIndex
);
1167 sw::Extent
const* pExtent(nullptr);
1168 for (const auto& rExt
: rMerged
.extents
)
1171 if (nIndex
< (pExtent
->nEnd
- pExtent
->nStart
))
1173 return std::make_pair(pExtent
->pNode
, pExtent
->nStart
+ nIndex
);
1175 nIndex
= nIndex
- (pExtent
->nEnd
- pExtent
->nStart
);
1177 assert(nIndex
== 0 && "view index out of bounds");
1179 ? std::make_pair(pExtent
->pNode
, pExtent
->nEnd
) //1-past-the-end index
1180 : std::make_pair(const_cast<SwTextNode
*>(rMerged
.pLastNode
), rMerged
.pLastNode
->Len());
1183 TextFrameIndex
MapModelToView(MergedPara
const& rMerged
, SwTextNode
const*const pNode
, sal_Int32
const nIndex
)
1185 assert(rMerged
.pFirstNode
->GetIndex() <= pNode
->GetIndex()
1186 && pNode
->GetIndex() <= rMerged
.pLastNode
->GetIndex());
1188 bool bFoundNode(false);
1189 for (auto const& e
: rMerged
.extents
)
1191 if (pNode
->GetIndex() < e
.pNode
->GetIndex())
1193 return TextFrameIndex(nRet
);
1195 if (e
.pNode
== pNode
)
1197 if (e
.nStart
<= nIndex
&& nIndex
<= e
.nEnd
)
1199 return TextFrameIndex(nRet
+ (nIndex
- e
.nStart
));
1201 else if (nIndex
< e
.nStart
)
1203 // in gap before this extent => map to 0 here TODO???
1204 return TextFrameIndex(nRet
);
1208 else if (bFoundNode
)
1212 nRet
+= e
.nEnd
- e
.nStart
;
1216 // must be in a gap at the end of the node
1217 assert(nIndex
<= pNode
->Len());
1218 return TextFrameIndex(nRet
);
1220 else if (rMerged
.extents
.empty())
1222 assert(nIndex
<= pNode
->Len());
1223 return TextFrameIndex(0);
1225 return TextFrameIndex(rMerged
.mergedText
.getLength());
1230 std::pair
<SwTextNode
*, sal_Int32
>
1231 SwTextFrame::MapViewToModel(TextFrameIndex
const nIndex
) const
1233 //nope assert(GetPara());
1234 sw::MergedPara
const*const pMerged(GetMergedPara());
1237 return sw::MapViewToModel(*pMerged
, nIndex
);
1241 return std::make_pair(static_cast<SwTextNode
*>(const_cast<sw::BroadcastingModify
*>(
1242 SwFrame::GetDep())), sal_Int32(nIndex
));
1246 SwPosition
SwTextFrame::MapViewToModelPos(TextFrameIndex
const nIndex
) const
1248 std::pair
<SwTextNode
*, sal_Int32
> const ret(MapViewToModel(nIndex
));
1249 return SwPosition(*ret
.first
, ret
.second
);
1252 TextFrameIndex
SwTextFrame::MapModelToView(SwTextNode
const*const pNode
, sal_Int32
const nIndex
) const
1254 //nope assert(GetPara());
1255 sw::MergedPara
const*const pMerged(GetMergedPara());
1258 return sw::MapModelToView(*pMerged
, pNode
, nIndex
);
1262 assert(static_cast<const SwTextNode
*>(SwFrame::GetDep()) == pNode
);
1263 return TextFrameIndex(nIndex
);
1267 TextFrameIndex
SwTextFrame::MapModelToViewPos(SwPosition
const& rPos
) const
1269 SwTextNode
const*const pNode(rPos
.GetNode().GetTextNode());
1270 sal_Int32
const nIndex(rPos
.GetContentIndex());
1271 return MapModelToView(pNode
, nIndex
);
1274 void SwTextFrame::SetMergedPara(std::unique_ptr
<sw::MergedPara
> p
)
1276 SwTextNode
*const pFirst(m_pMergedPara
? m_pMergedPara
->pFirstNode
: nullptr);
1277 m_pMergedPara
= std::move(p
);
1282 assert(pFirst
== m_pMergedPara
->pFirstNode
);
1286 pFirst
->Add(this); // must register at node again
1289 // postcondition: frame must be listening somewhere
1290 assert(m_pMergedPara
|| GetDep());
1293 const OUString
& SwTextFrame::GetText() const
1295 //nope assert(GetPara());
1296 sw::MergedPara
const*const pMerged(GetMergedPara());
1298 return pMerged
->mergedText
;
1300 return static_cast<SwTextNode
const*>(SwFrame::GetDep())->GetText();
1303 SwTextNode
const* SwTextFrame::GetTextNodeForParaProps() const
1305 // FIXME can GetPara be 0 ? yes... this is needed in SwContentNotify::SwContentNotify() which is called before any formatting is started
1306 //nope assert(GetPara());
1307 sw::MergedPara
const*const pMerged(GetMergedPara());
1310 // assert(pMerged->pFirstNode == pMerged->pParaPropsNode); // surprising news!
1311 return pMerged
->pParaPropsNode
;
1314 return static_cast<SwTextNode
const*>(SwFrame::GetDep());
1317 SwTextNode
const* SwTextFrame::GetTextNodeForFirstText() const
1319 sw::MergedPara
const*const pMerged(GetMergedPara());
1321 return pMerged
->extents
.empty()
1322 ? pMerged
->pFirstNode
1323 : pMerged
->extents
.front().pNode
;
1325 return static_cast<SwTextNode
const*>(SwFrame::GetDep());
1328 SwTextNode
const* SwTextFrame::GetTextNodeFirst() const
1330 //nope assert(GetPara());
1331 sw::MergedPara
const*const pMerged(GetMergedPara());
1333 return pMerged
->pFirstNode
;
1335 return static_cast<SwTextNode
const*>(SwFrame::GetDep());
1338 SwDoc
const& SwTextFrame::GetDoc() const
1340 return GetTextNodeFirst()->GetDoc();
1343 LanguageType
SwTextFrame::GetLangOfChar(TextFrameIndex
const nIndex
,
1344 sal_uInt16
const nScript
, bool const bNoChar
) const
1346 // a single character can be mapped uniquely!
1347 std::pair
<SwTextNode
const*, sal_Int32
> const pos(MapViewToModel(nIndex
));
1348 return pos
.first
->GetLang(pos
.second
, bNoChar
? 0 : 1, nScript
);
1351 void SwTextFrame::ResetPreps()
1353 if ( GetCacheIdx() != USHRT_MAX
)
1355 if (SwParaPortion
*pPara
= GetPara())
1356 pPara
->ResetPreps();
1360 bool SwTextFrame::IsHiddenNow() const
1362 SwFrameSwapper
aSwapper( this, true );
1364 if( !getFrameArea().Width() && isFrameAreaDefinitionValid() && GetUpper()->isFrameAreaDefinitionValid() ) // invalid when stack overflows (StackHack)!
1366 // OSL_FAIL( "SwTextFrame::IsHiddenNow: thin frame" );
1370 bool bHiddenCharsHidePara(false);
1371 bool bHiddenParaField(false);
1374 TextFrameIndex
nHiddenStart(COMPLETE_STRING
);
1375 TextFrameIndex
nHiddenEnd(0);
1376 if (auto const pScriptInfo
= GetScriptInfo())
1378 pScriptInfo
->GetBoundsOfHiddenRange(TextFrameIndex(0),
1379 nHiddenStart
, nHiddenEnd
);
1381 else // ParaPortion is created in Format, but this is called earlier
1384 aInfo
.InitScriptInfo(*m_pMergedPara
->pFirstNode
, m_pMergedPara
.get(), IsRightToLeft());
1385 aInfo
.GetBoundsOfHiddenRange(TextFrameIndex(0),
1386 nHiddenStart
, nHiddenEnd
);
1388 if (TextFrameIndex(0) == nHiddenStart
&&
1389 TextFrameIndex(GetText().getLength()) <= nHiddenEnd
)
1391 bHiddenCharsHidePara
= true;
1393 sw::MergedAttrIter
iter(*this);
1394 SwTextNode
const* pNode(nullptr);
1395 int nNewResultWeight
= 0;
1396 for (SwTextAttr
const* pHint
= iter
.NextAttr(&pNode
); pHint
; pHint
= iter
.NextAttr(&pNode
))
1398 if (pHint
->Which() == RES_TXTATR_FIELD
)
1400 // see also SwpHints::CalcHiddenParaField()
1401 const SwFormatField
& rField
= pHint
->GetFormatField();
1402 int nCurWeight
= pNode
->GetDoc().FieldCanHideParaWeight(rField
.GetField()->GetTyp()->Which());
1403 if (nCurWeight
> nNewResultWeight
)
1405 nNewResultWeight
= nCurWeight
;
1406 bHiddenParaField
= pNode
->GetDoc().FieldHidesPara(*rField
.GetField());
1408 else if (nCurWeight
== nNewResultWeight
&& bHiddenParaField
)
1410 // Currently, for both supported hiding types (HiddenPara, Database), "Don't hide"
1411 // takes precedence - i.e., if there's a "Don't hide" field of that weight, we only
1412 // care about fields of higher weight.
1413 bHiddenParaField
= pNode
->GetDoc().FieldHidesPara(*rField
.GetField());
1420 bHiddenCharsHidePara
= static_cast<SwTextNode
const*>(SwFrame::GetDep())->HasHiddenCharAttribute( true );
1421 bHiddenParaField
= static_cast<SwTextNode
const*>(SwFrame::GetDep())->IsHiddenByParaField();
1423 const SwViewShell
* pVsh
= getRootFrame()->GetCurrShell();
1425 if ( pVsh
&& ( bHiddenCharsHidePara
|| bHiddenParaField
) )
1429 ( bHiddenParaField
&&
1430 ( !pVsh
->GetViewOptions()->IsShowHiddenPara() &&
1431 !pVsh
->GetViewOptions()->IsFieldName() ) ) ||
1432 ( bHiddenCharsHidePara
&&
1433 !pVsh
->GetViewOptions()->IsShowHiddenChar() ) )
1435 // in order to put the cursor in the body text, one paragraph must
1436 // be visible - check this for the 1st body paragraph
1437 if (IsInDocBody() && FindPrevCnt() == nullptr)
1439 bool isAllHidden(true);
1440 for (SwContentFrame
const* pNext
= FindNextCnt(true);
1441 pNext
!= nullptr; pNext
= pNext
->FindNextCnt(true))
1443 if (!pNext
->IsTextFrame()
1444 || !static_cast<SwTextFrame
const*>(pNext
)->IsHiddenNow())
1446 isAllHidden
= false;
1452 SAL_INFO("sw.core", "unhiding one body paragraph");
1463 /// Removes Textfrm's attachments, when it's hidden
1464 void SwTextFrame::HideHidden()
1466 OSL_ENSURE( !GetFollow() && IsHiddenNow(),
1467 "HideHidden on visible frame of hidden frame has follow" );
1469 HideFootnotes(GetOffset(), TextFrameIndex(COMPLETE_STRING
));
1470 HideAndShowObjects();
1472 // format information is obsolete
1476 void SwTextFrame::HideFootnotes(TextFrameIndex
const nStart
, TextFrameIndex
const nEnd
)
1478 SwPageFrame
*pPage
= nullptr;
1479 sw::MergedAttrIter
iter(*this);
1480 SwTextNode
const* pNode(nullptr);
1481 for (SwTextAttr
const* pHt
= iter
.NextAttr(&pNode
); pHt
; pHt
= iter
.NextAttr(&pNode
))
1483 if (pHt
->Which() == RES_TXTATR_FTN
)
1485 TextFrameIndex
const nIdx(MapModelToView(pNode
, pHt
->GetStart()));
1491 pPage
= FindPageFrame();
1492 pPage
->RemoveFootnote( this, static_cast<const SwTextFootnote
*>(pHt
) );
1499 * as-character anchored graphics, which are used for a graphic bullet list.
1500 * As long as these graphic bullet list aren't imported, do not hide a
1501 * at-character anchored object, if
1502 * (a) the document is an imported WW8 document -
1503 * checked by checking certain compatibility options -
1504 * (b) the paragraph is the last content in the document and
1505 * (c) the anchor character is an as-character anchored graphic.
1507 bool sw_HideObj( const SwTextFrame
& _rFrame
,
1508 const RndStdIds _eAnchorType
,
1509 SwFormatAnchor
const& rFormatAnchor
,
1510 SwAnchoredObject
* _pAnchoredObj
)
1514 if (_eAnchorType
== RndStdIds::FLY_AT_CHAR
)
1516 const IDocumentSettingAccess
*const pIDSA
= &_rFrame
.GetDoc().getIDocumentSettingAccess();
1517 if ( !pIDSA
->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING
) &&
1518 !pIDSA
->get(DocumentSettingId::OLD_LINE_SPACING
) &&
1519 !pIDSA
->get(DocumentSettingId::USE_FORMER_OBJECT_POS
) &&
1520 pIDSA
->get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION
) &&
1521 _rFrame
.IsInDocBody() && !_rFrame
.FindNextCnt() )
1523 SwTextNode
const& rNode(*rFormatAnchor
.GetAnchorNode()->GetTextNode());
1524 assert(FrameContainsNode(_rFrame
, rNode
.GetIndex()));
1525 sal_Int32
const nObjAnchorPos(rFormatAnchor
.GetAnchorContentOffset());
1526 const sal_Unicode cAnchorChar
= nObjAnchorPos
< rNode
.Len()
1527 ? rNode
.GetText()[nObjAnchorPos
]
1529 if (cAnchorChar
== CH_TXTATR_BREAKWORD
)
1531 const SwTextAttr
* const pHint(
1532 rNode
.GetTextAttrForCharAt(nObjAnchorPos
, RES_TXTATR_FLYCNT
));
1535 const SwFrameFormat
* pFrameFormat
=
1536 static_cast<const SwTextFlyCnt
*>(pHint
)->GetFlyCnt().GetFrameFormat();
1537 if ( pFrameFormat
->Which() == RES_FLYFRMFMT
)
1539 SwNodeIndex nContentIndex
= *(pFrameFormat
->GetContent().GetContentIdx());
1541 if ( nContentIndex
.GetNode().IsNoTextNode() )
1544 // set needed data structure values for object positioning
1545 SwRectFnSet
aRectFnSet(&_rFrame
);
1546 SwRect
aLastCharRect( _rFrame
.getFrameArea() );
1547 aRectFnSet
.SetWidth( aLastCharRect
, 1 );
1548 _pAnchoredObj
->maLastCharRect
= aLastCharRect
;
1549 _pAnchoredObj
->mnLastTopOfLine
= aRectFnSet
.GetTop(aLastCharRect
);
1563 * Method hides respectively shows objects, which are anchored at paragraph,
1564 * at/as a character of the paragraph, corresponding to the paragraph and
1565 * paragraph portion visibility.
1567 * - is called from HideHidden() - should hide objects in hidden paragraphs and
1568 * - from Format_() - should hide/show objects in partly visible paragraphs
1570 void SwTextFrame::HideAndShowObjects()
1572 if ( GetDrawObjs() )
1574 if ( IsHiddenNow() )
1576 // complete paragraph is hidden. Thus, hide all objects
1577 for (SwAnchoredObject
* i
: *GetDrawObjs())
1579 SdrObject
* pObj
= i
->DrawObj();
1580 SwContact
* pContact
= static_cast<SwContact
*>(pObj
->GetUserCall());
1581 // under certain conditions
1582 const RndStdIds
eAnchorType( pContact
->GetAnchorId() );
1583 if ((eAnchorType
!= RndStdIds::FLY_AT_CHAR
) ||
1584 sw_HideObj(*this, eAnchorType
, pContact
->GetAnchorFormat(),
1587 pContact
->MoveObjToInvisibleLayer( pObj
);
1593 // paragraph is visible, but can contain hidden text portion.
1594 // first we check if objects are allowed to be hidden:
1595 const SwViewShell
* pVsh
= getRootFrame()->GetCurrShell();
1596 const bool bShouldBeHidden
= !pVsh
|| !pVsh
->GetWin() ||
1597 !pVsh
->GetViewOptions()->IsShowHiddenChar();
1599 // Thus, show all objects, which are anchored at paragraph and
1600 // hide/show objects, which are anchored at/as character, according
1601 // to the visibility of the anchor character.
1602 for (SwAnchoredObject
* i
: *GetDrawObjs())
1604 SdrObject
* pObj
= i
->DrawObj();
1605 SwContact
* pContact
= static_cast<SwContact
*>(pObj
->GetUserCall());
1606 // Determine anchor type only once
1607 const RndStdIds
eAnchorType( pContact
->GetAnchorId() );
1609 if (eAnchorType
== RndStdIds::FLY_AT_PARA
)
1611 pContact
->MoveObjToVisibleLayer( pObj
);
1613 else if ((eAnchorType
== RndStdIds::FLY_AT_CHAR
) ||
1614 (eAnchorType
== RndStdIds::FLY_AS_CHAR
))
1616 sal_Int32 nHiddenStart
;
1617 sal_Int32 nHiddenEnd
;
1618 const SwFormatAnchor
& rAnchorFormat
= pContact
->GetAnchorFormat();
1619 SwScriptInfo::GetBoundsOfHiddenRange(
1620 *rAnchorFormat
.GetAnchorNode()->GetTextNode(),
1621 rAnchorFormat
.GetAnchorContentOffset(), nHiddenStart
, nHiddenEnd
);
1622 // Under certain conditions
1623 if ( nHiddenStart
!= COMPLETE_STRING
&& bShouldBeHidden
&&
1624 sw_HideObj(*this, eAnchorType
, rAnchorFormat
, i
))
1626 pContact
->MoveObjToInvisibleLayer( pObj
);
1629 pContact
->MoveObjToVisibleLayer( pObj
);
1633 OSL_FAIL( "<SwTextFrame::HideAndShowObjects()> - object not anchored at/inside paragraph!?" );
1641 SwTextFrame
*pMaster
= FindMaster();
1642 OSL_ENSURE(pMaster
, "SwTextFrame without master");
1644 pMaster
->HideAndShowObjects();
1649 * Returns the first possible break point in the current line.
1650 * This method is used in SwTextFrame::Format() to decide whether the previous
1651 * line has to be formatted as well.
1652 * nFound is <= nEndLine.
1654 TextFrameIndex
SwTextFrame::FindBrk(std::u16string_view aText
,
1655 const TextFrameIndex nStart
,
1656 const TextFrameIndex nEnd
)
1658 sal_Int32 nFound
= sal_Int32(nStart
);
1659 const sal_Int32 nEndLine
= std::min(sal_Int32(nEnd
), sal_Int32(aText
.size()) - 1);
1661 // Skip all leading blanks.
1662 while( nFound
<= nEndLine
&& ' ' == aText
[nFound
] )
1667 // A tricky situation with the TextAttr-Dummy-character (in this case "$"):
1668 // "Dr.$Meyer" at the beginning of the second line. Typing a blank after that
1669 // doesn't result in the word moving into first line, even though that would work.
1670 // For this reason we don't skip the dummy char.
1671 while( nFound
<= nEndLine
&& ' ' != aText
[nFound
] )
1676 return TextFrameIndex(nFound
);
1679 bool SwTextFrame::IsIdxInside(TextFrameIndex
const nPos
, TextFrameIndex
const nLen
) const
1681 if (nPos
== TextFrameIndex(COMPLETE_STRING
)) // the "not found" range
1683 // Silence over-eager warning emitted at least by GCC trunk towards 6:
1684 #if defined __GNUC__ && !defined __clang__
1685 #pragma GCC diagnostic push
1686 #pragma GCC diagnostic ignored "-Wstrict-overflow"
1688 if (nLen
!= TextFrameIndex(COMPLETE_STRING
) && GetOffset() > nPos
+ nLen
) // the range preceded us
1689 #if defined __GNUC__ && !defined __clang__
1690 #pragma GCC diagnostic pop
1694 if( !GetFollow() ) // the range doesn't precede us,
1695 return true; // nobody follows us.
1697 TextFrameIndex
const nMax
= GetFollow()->GetOffset();
1699 // either the range overlap or our text has been deleted
1700 // sw_redlinehide: GetText() should be okay here because it has already
1701 // been updated in the INS/DEL hint case
1702 if (nMax
> nPos
|| nMax
> TextFrameIndex(GetText().getLength()))
1705 // changes made in the first line of a follow can modify the master
1706 const SwParaPortion
* pPara
= GetFollow()->GetPara();
1707 return pPara
&& ( nPos
<= nMax
+ pPara
->GetLen() );
1710 inline void SwTextFrame::InvalidateRange(const SwCharRange
&aRange
, const tools::Long nD
)
1712 if ( IsIdxInside( aRange
.Start(), aRange
.Len() ) )
1713 InvalidateRange_( aRange
, nD
);
1716 void SwTextFrame::InvalidateRange_( const SwCharRange
&aRange
, const tools::Long nD
)
1724 SwParaPortion
*pPara
= GetPara();
1729 // In nDelta the differences between old and new
1730 // linelengths are being added, that's why it's negative
1731 // if chars have been added and positive, if chars have
1733 pPara
->SetDelta(pPara
->GetDelta() + nD
);
1736 SwCharRange
&rReformat
= pPara
->GetReformat();
1737 if(aRange
!= rReformat
) {
1738 if (TextFrameIndex(COMPLETE_STRING
) == rReformat
.Len())
1741 rReformat
+= aRange
;
1750 void SwTextFrame::CalcLineSpace()
1752 OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
1753 "SwTextFrame::CalcLineSpace with swapped frame!" );
1755 if( IsLocked() || !HasPara() )
1758 if( GetDrawObjs() ||
1759 GetTextNodeForParaProps()->GetSwAttrSet().GetFirstLineIndent().IsAutoFirst())
1765 SwParaPortion
*const pPara(GetPara());
1767 if (pPara
->IsFixLineHeight())
1773 Size
aNewSize( getFramePrintArea().SSize() );
1775 SwTextFormatInfo
aInf( getRootFrame()->GetCurrShell()->GetOut(), this );
1776 SwTextFormatter
aLine( this, &aInf
);
1777 if( aLine
.GetDropLines() )
1784 aLine
.RecalcRealHeight();
1786 aNewSize
.setHeight( (aLine
.Y() - getFrameArea().Top()) + aLine
.GetLineHeight() );
1788 SwTwips nDelta
= aNewSize
.Height() - getFramePrintArea().Height();
1789 // Underflow with free-flying frames
1790 if( aInf
.GetTextFly().IsOn() )
1792 SwRect
aTmpFrame( getFrameArea() );
1794 aTmpFrame
.Height( getFramePrintArea().Height() );
1796 aTmpFrame
.Height( aNewSize
.Height() );
1797 if( aInf
.GetTextFly().Relax( aTmpFrame
) )
1807 SwTextFrameBreak
aBreak( this );
1808 if( GetFollow() || aBreak
.IsBreakNow( aLine
) )
1810 // if there is a Follow() or if we need to break here, reformat
1815 // everything is business as usual...
1816 pPara
->SetPrepAdjust();
1821 static void lcl_SetWrong( SwTextFrame
& rFrame
, SwTextNode
const& rNode
,
1822 sal_Int32
const nPos
, sal_Int32
const nCnt
, bool const bMove
)
1824 if ( !rFrame
.IsFollow() )
1826 SwTextNode
& rTextNode
= const_cast<SwTextNode
&>(rNode
);
1827 sw::GrammarContact
* pGrammarContact
= sw::getGrammarContactFor(rTextNode
);
1828 SwGrammarMarkUp
* pWrongGrammar
= pGrammarContact
?
1829 pGrammarContact
->getGrammarCheck( rTextNode
, false ) :
1830 rTextNode
.GetGrammarCheck();
1831 bool bGrammarProxy
= pWrongGrammar
!= rTextNode
.GetGrammarCheck();
1834 if( rTextNode
.GetWrong() )
1835 rTextNode
.GetWrong()->Move( nPos
, nCnt
);
1837 pWrongGrammar
->MoveGrammar( nPos
, nCnt
);
1838 if( bGrammarProxy
&& rTextNode
.GetGrammarCheck() )
1839 rTextNode
.GetGrammarCheck()->MoveGrammar( nPos
, nCnt
);
1840 if( rTextNode
.GetSmartTags() )
1841 rTextNode
.GetSmartTags()->Move( nPos
, nCnt
);
1845 if( rTextNode
.GetWrong() )
1846 rTextNode
.GetWrong()->Invalidate( nPos
, nCnt
);
1848 pWrongGrammar
->Invalidate( nPos
, nCnt
);
1849 if( rTextNode
.GetSmartTags() )
1850 rTextNode
.GetSmartTags()->Invalidate( nPos
, nCnt
);
1852 const sal_Int32 nEnd
= nPos
+ (nCnt
> 0 ? nCnt
: 1 );
1853 if ( !rTextNode
.GetWrong() && !rTextNode
.IsWrongDirty() )
1855 rTextNode
.SetWrong( std::make_unique
<SwWrongList
>( WRONGLIST_SPELL
) );
1856 rTextNode
.GetWrong()->SetInvalid( nPos
, nEnd
);
1858 if ( !rTextNode
.GetSmartTags() && !rTextNode
.IsSmartTagDirty() )
1860 rTextNode
.SetSmartTags( std::make_unique
<SwWrongList
>( WRONGLIST_SMARTTAG
) );
1861 rTextNode
.GetSmartTags()->SetInvalid( nPos
, nEnd
);
1863 rTextNode
.SetWrongDirty(sw::WrongState::TODO
);
1864 rTextNode
.SetGrammarCheckDirty( true );
1865 rTextNode
.SetWordCountDirty( true );
1866 rTextNode
.SetAutoCompleteWordDirty( true );
1867 rTextNode
.SetSmartTagDirty( true );
1870 SwRootFrame
*pRootFrame
= rFrame
.getRootFrame();
1873 pRootFrame
->SetNeedGrammarCheck( true );
1876 SwPageFrame
*pPage
= rFrame
.FindPageFrame();
1879 pPage
->InvalidateSpelling();
1880 pPage
->InvalidateAutoCompleteWords();
1881 pPage
->InvalidateWordCount();
1882 pPage
->InvalidateSmartTags();
1886 static void lcl_SetScriptInval(SwTextFrame
& rFrame
, TextFrameIndex
const nPos
)
1888 if( rFrame
.GetPara() )
1889 rFrame
.GetPara()->GetScriptInfo().SetInvalidityA( nPos
);
1892 // note: SwClientNotify will be called once for every frame => just fix own Ofst
1893 static void lcl_ModifyOfst(SwTextFrame
& rFrame
,
1894 TextFrameIndex
const nPos
, TextFrameIndex
const nLen
,
1895 TextFrameIndex (* op
)(TextFrameIndex
const&, TextFrameIndex
const&))
1897 assert(nLen
!= TextFrameIndex(COMPLETE_STRING
));
1898 if (rFrame
.IsFollow() && nPos
< rFrame
.GetOffset())
1900 rFrame
.ManipOfst( std::max(nPos
, op(rFrame
.GetOffset(), nLen
)) );
1901 assert(sal_Int32(rFrame
.GetOffset()) <= rFrame
.GetText().getLength());
1907 void UpdateMergedParaForMove(sw::MergedPara
& rMerged
,
1908 SwTextFrame
& rTextFrame
,
1909 bool & o_rbRecalcFootnoteFlag
,
1910 SwTextNode
const& rDestNode
,
1911 SwTextNode
const& rNode
,
1912 sal_Int32
const nDestStart
,
1913 sal_Int32
const nSourceStart
,
1914 sal_Int32
const nLen
)
1916 std::vector
<std::pair
<sal_Int32
, sal_Int32
>> deleted
;
1917 sal_Int32
const nSourceEnd(nSourceStart
+ nLen
);
1918 sal_Int32
nLastEnd(0);
1919 for (const auto& rExt
: rMerged
.extents
)
1921 if (rExt
.pNode
== &rNode
)
1923 sal_Int32
const nStart(std::max(nLastEnd
, nSourceStart
));
1924 sal_Int32
const nEnd(std::min(rExt
.nStart
, nSourceEnd
));
1927 deleted
.emplace_back(nStart
, nEnd
);
1929 nLastEnd
= rExt
.nEnd
;
1930 if (nSourceEnd
<= rExt
.nEnd
)
1935 else if (rNode
.GetIndex() < rExt
.pNode
->GetIndex())
1940 if (nLastEnd
!= rNode
.Len()) // without nLen, string yet to be removed
1942 if (nLastEnd
< nSourceEnd
)
1944 deleted
.emplace_back(std::max(nLastEnd
, nSourceStart
), nSourceEnd
);
1947 if (deleted
.empty())
1950 o_rbRecalcFootnoteFlag
= true;
1951 for (auto const& it
: deleted
)
1953 sal_Int32
const nStart(it
.first
- nSourceStart
+ nDestStart
);
1954 TextFrameIndex
const nDeleted
= UpdateMergedParaForDelete(rMerged
, false,
1955 rDestNode
, nStart
, it
.second
- it
.first
);
1956 //FIXME asserts valid for join - but if called from split, the new node isn't there yet and it will be added later... assert(nDeleted);
1957 // assert(nDeleted == it.second - it.first);
1960 // InvalidateRange/lcl_SetScriptInval was called sufficiently for InsertText
1961 lcl_SetWrong(rTextFrame
, rDestNode
, nStart
, it
.first
- it
.second
, false);
1962 TextFrameIndex
const nIndex(sw::MapModelToView(rMerged
, &rDestNode
, nStart
));
1963 lcl_ModifyOfst(rTextFrame
, nIndex
, nDeleted
, &o3tl::operator-<sal_Int32
, Tag_TextFrameIndex
>);
1971 * Related: fdo#56031 filter out attribute changes that don't matter for
1972 * humans/a11y to stop flooding the destination mortal with useless noise
1974 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
1975 static bool isA11yRelevantAttribute(sal_uInt16 nWhich
)
1977 return nWhich
!= RES_CHRATR_RSID
;
1980 static bool hasA11yRelevantAttribute( const std::vector
<sal_uInt16
>& rWhichFmtAttr
)
1982 for( sal_uInt16 nWhich
: rWhichFmtAttr
)
1983 if ( isA11yRelevantAttribute( nWhich
) )
1988 #endif // ENABLE_WASM_STRIP_ACCESSIBILITY
1990 // Note: for now this overrides SwClient::SwClientNotify; the intermediary
1991 // classes still override SwClient::Modify, which should continue to work
1992 // as their implementation of SwClientNotify is SwClient's which calls Modify.
1993 // Therefore we also don't need to call SwClient::SwClientNotify(rModify, rHint)
1994 // because that's all it does, and this implementation calls
1995 // SwContentFrame::SwClientNotify() when appropriate.
1996 void SwTextFrame::SwClientNotify(SwModify
const& rModify
, SfxHint
const& rHint
)
1998 SfxPoolItem
const* pOld(nullptr);
1999 SfxPoolItem
const* pNew(nullptr);
2000 sw::MoveText
const* pMoveText(nullptr);
2001 sw::InsertText
const* pInsertText(nullptr);
2002 sw::DeleteText
const* pDeleteText(nullptr);
2003 sw::DeleteChar
const* pDeleteChar(nullptr);
2004 sw::RedlineDelText
const* pRedlineDelText(nullptr);
2005 sw::RedlineUnDelText
const* pRedlineUnDelText(nullptr);
2007 sal_uInt16 nWhich
= 0;
2008 if (rHint
.GetId() == SfxHintId::SwLegacyModify
)
2010 auto pHint
= static_cast<const sw::LegacyModifyHint
*>(&rHint
);
2011 pOld
= pHint
->m_pOld
;
2012 pNew
= pHint
->m_pNew
;
2013 nWhich
= pHint
->GetWhich();
2015 else if (rHint
.GetId() == SfxHintId::SwInsertText
)
2017 pInsertText
= static_cast<const sw::InsertText
*>(&rHint
);
2019 else if (rHint
.GetId() == SfxHintId::SwDeleteText
)
2021 pDeleteText
= static_cast<const sw::DeleteText
*>(&rHint
);
2023 else if (rHint
.GetId() == SfxHintId::SwDeleteChar
)
2025 pDeleteChar
= static_cast<const sw::DeleteChar
*>(&rHint
);
2027 else if (rHint
.GetId() == SfxHintId::SwDocPosUpdateAtIndex
)
2029 auto pDocPosAt
= static_cast<const sw::DocPosUpdateAtIndex
*>(&rHint
);
2030 Broadcast(SfxHint()); // notify SwAccessibleParagraph
2033 if(pDocPosAt
->m_nDocPos
> getFrameArea().Top())
2035 TextFrameIndex
const nIndex(MapModelToView(
2036 &pDocPosAt
->m_rNode
,
2037 pDocPosAt
->m_nIndex
));
2038 InvalidateRange(SwCharRange(nIndex
, TextFrameIndex(1)));
2041 else if (auto const pHt
= dynamic_cast<sw::MoveText
const*>(&rHint
))
2045 else if (auto const pHynt
= dynamic_cast<sw::RedlineDelText
const*>(&rHint
))
2047 pRedlineDelText
= pHynt
;
2049 else if (auto const pHnt
= dynamic_cast<sw::RedlineUnDelText
const*>(&rHint
))
2051 pRedlineUnDelText
= pHnt
;
2055 assert(!"unexpected hint");
2060 assert(m_pMergedPara
->listener
.IsListeningTo(&rModify
));
2063 SwTextNode
const& rNode(static_cast<SwTextNode
const&>(rModify
));
2065 // modifications concerning frame attributes are processed by the base class
2066 if( IsInRange( aFrameFormatSetRange
, nWhich
) || RES_FMT_CHG
== nWhich
)
2069 { // ignore item set changes that don't apply
2070 SwTextNode
const*const pAttrNode(
2071 (nWhich
== RES_PAGEDESC
|| nWhich
== RES_BREAK
)
2072 ? m_pMergedPara
->pFirstNode
2073 : m_pMergedPara
->pParaPropsNode
);
2074 if (pAttrNode
!= &rModify
)
2079 SwContentFrame::SwClientNotify(rModify
, sw::LegacyModifyHint(pOld
, pNew
));
2080 if( nWhich
== RES_FMT_CHG
&& getRootFrame()->GetCurrShell() )
2082 // collection has changed
2085 lcl_SetWrong( *this, rNode
, 0, COMPLETE_STRING
, false );
2086 SetDerivedR2L( false );
2088 // Force complete paint due to existing indents.
2090 InvalidateLineNum();
2095 if (m_pMergedPara
&& m_pMergedPara
->pParaPropsNode
!= &rModify
)
2097 if (isPARATR(nWhich
) || isPARATR_LIST(nWhich
)) // FRMATR handled above
2099 return; // ignore it
2103 Broadcast(SfxHint()); // notify SwAccessibleParagraph
2105 // while locked ignore all modifications
2110 // warning: one has to ensure that all variables are set
2111 TextFrameIndex nPos
;
2112 TextFrameIndex nLen
;
2113 bool bSetFieldsDirty
= false;
2114 bool bRecalcFootnoteFlag
= false;
2116 if (pRedlineDelText
)
2120 sal_Int32
const nNPos
= pRedlineDelText
->nStart
;
2121 sal_Int32
const nNLen
= pRedlineDelText
->nLen
;
2122 nPos
= MapModelToView(&rNode
, nNPos
);
2123 // update merged before doing anything else
2124 nLen
= UpdateMergedParaForDelete(*m_pMergedPara
, false, rNode
, nNPos
, nNLen
);
2125 const sal_Int32 m
= -nNLen
;
2126 if (nLen
&& IsIdxInside(nPos
, nLen
))
2128 InvalidateRange( SwCharRange(nPos
, TextFrameIndex(1)), m
);
2130 lcl_SetWrong( *this, rNode
, nNPos
, m
, false );
2133 lcl_SetScriptInval( *this, nPos
);
2134 bSetFieldsDirty
= bRecalcFootnoteFlag
= true;
2135 lcl_ModifyOfst(*this, nPos
, nLen
, &o3tl::operator-<sal_Int32
, Tag_TextFrameIndex
>);
2139 else if (pRedlineUnDelText
)
2143 sal_Int32
const nNPos
= pRedlineUnDelText
->nStart
;
2144 sal_Int32
const nNLen
= pRedlineUnDelText
->nLen
;
2145 nPos
= MapModelToView(&rNode
, nNPos
);
2146 nLen
= UpdateMergedParaForInsert(*m_pMergedPara
, false, rNode
, nNPos
, nNLen
);
2147 if (IsIdxInside(nPos
, nLen
))
2151 // Refresh NumPortions even when line is empty!
2158 InvalidateRange_( SwCharRange( nPos
, nLen
), nNLen
);
2160 lcl_SetWrong( *this, rNode
, nNPos
, nNLen
, false );
2161 lcl_SetScriptInval( *this, nPos
);
2162 bSetFieldsDirty
= true;
2163 lcl_ModifyOfst(*this, nPos
, nLen
, &o3tl::operator+<sal_Int32
, Tag_TextFrameIndex
>);
2169 && m_pMergedPara
->pFirstNode
->GetIndex() <= pMoveText
->pDestNode
->GetIndex()
2170 && pMoveText
->pDestNode
->GetIndex() <= m_pMergedPara
->pLastNode
->GetIndex())
2171 { // if it's not 2 nodes in merged frame, assume the target node doesn't have frames at all
2172 assert(abs(rNode
.GetIndex() - pMoveText
->pDestNode
->GetIndex()) == SwNodeOffset(1));
2173 UpdateMergedParaForMove(*m_pMergedPara
,
2175 bRecalcFootnoteFlag
,
2176 *pMoveText
->pDestNode
, rNode
,
2177 pMoveText
->nDestStart
,
2178 pMoveText
->nSourceStart
,
2183 // there is a situation where this is okay: from JoinNext, which will then call CheckResetRedlineMergeFlag, which will then create merged from scratch for this frame
2184 // assert(!m_pMergedPara || !getRootFrame()->IsHideRedlines() || !pMoveText->pDestNode->getLayoutFrame(getRootFrame()));
2187 else if (pInsertText
)
2189 nPos
= MapModelToView(&rNode
, pInsertText
->nPos
);
2190 // unlike redlines, inserting into fieldmark must be explicitly handled
2191 bool isHidden(false);
2192 switch (getRootFrame()->GetFieldmarkMode())
2194 case sw::FieldmarkMode::ShowCommand
:
2195 isHidden
= pInsertText
->isInsideFieldmarkResult
;
2197 case sw::FieldmarkMode::ShowResult
:
2198 isHidden
= pInsertText
->isInsideFieldmarkCommand
;
2200 case sw::FieldmarkMode::ShowBoth
: // just to avoid the warning
2205 nLen
= TextFrameIndex(pInsertText
->nLen
);
2208 UpdateMergedParaForInsert(*m_pMergedPara
, true, rNode
, pInsertText
->nPos
, pInsertText
->nLen
);
2210 if( IsIdxInside( nPos
, nLen
) )
2214 // Refresh NumPortions even when line is empty!
2221 InvalidateRange_( SwCharRange( nPos
, nLen
), pInsertText
->nLen
);
2223 lcl_SetScriptInval( *this, nPos
);
2224 bSetFieldsDirty
= true;
2225 lcl_ModifyOfst(*this, nPos
, nLen
, &o3tl::operator+<sal_Int32
, Tag_TextFrameIndex
>);
2227 lcl_SetWrong( *this, rNode
, pInsertText
->nPos
, pInsertText
->nLen
, true );
2229 else if (pDeleteText
)
2231 nPos
= MapModelToView(&rNode
, pDeleteText
->nStart
);
2233 { // update merged before doing anything else
2234 nLen
= UpdateMergedParaForDelete(*m_pMergedPara
, true, rNode
, pDeleteText
->nStart
, pDeleteText
->nLen
);
2238 nLen
= TextFrameIndex(pDeleteText
->nLen
);
2240 const sal_Int32 m
= -pDeleteText
->nLen
;
2241 if ((!m_pMergedPara
|| nLen
) && IsIdxInside(nPos
, nLen
))
2246 InvalidateRange( SwCharRange(nPos
, TextFrameIndex(1)), m
);
2248 lcl_SetWrong( *this, rNode
, pDeleteText
->nStart
, m
, true );
2251 lcl_SetScriptInval( *this, nPos
);
2252 bSetFieldsDirty
= bRecalcFootnoteFlag
= true;
2253 lcl_ModifyOfst(*this, nPos
, nLen
, &o3tl::operator-<sal_Int32
, Tag_TextFrameIndex
>);
2256 else if (pDeleteChar
)
2258 nPos
= MapModelToView(&rNode
, pDeleteChar
->m_nPos
);
2261 nLen
= UpdateMergedParaForDelete(*m_pMergedPara
, true, rNode
, pDeleteChar
->m_nPos
, 1);
2265 nLen
= TextFrameIndex(1);
2267 lcl_SetWrong( *this, rNode
, pDeleteChar
->m_nPos
, -1, true );
2270 InvalidateRange( SwCharRange(nPos
, nLen
), -1 );
2271 lcl_SetScriptInval( *this, nPos
);
2272 bSetFieldsDirty
= bRecalcFootnoteFlag
= true;
2273 lcl_ModifyOfst(*this, nPos
, nLen
, &o3tl::operator-<sal_Int32
, Tag_TextFrameIndex
>);
2276 else switch (nWhich
)
2278 case RES_LINENUMBER
:
2280 assert(false); // should have been forwarded to SwContentFrame
2281 InvalidateLineNum();
2284 case RES_UPDATE_ATTR
:
2286 const SwUpdateAttr
* pNewUpdate
= static_cast<const SwUpdateAttr
*>(pNew
);
2288 sal_Int32
const nNPos
= pNewUpdate
->getStart();
2289 sal_Int32
const nNLen
= pNewUpdate
->getEnd() - nNPos
;
2290 nPos
= MapModelToView(&rNode
, nNPos
);
2291 nLen
= MapModelToView(&rNode
, nNPos
+ nNLen
) - nPos
;
2292 if( IsIdxInside( nPos
, nLen
) )
2294 // We need to reformat anyways, even if the invalidated
2296 // E.g.: empty line, set 14 pt!
2298 // FootnoteNumbers need to be formatted
2300 nLen
= TextFrameIndex(1);
2302 InvalidateRange_( SwCharRange( nPos
, nLen
) );
2303 const sal_uInt16 nTmp
= pNewUpdate
->getWhichAttr();
2305 if( ! nTmp
|| RES_TXTATR_CHARFMT
== nTmp
|| RES_TXTATR_INETFMT
== nTmp
|| RES_TXTATR_AUTOFMT
== nTmp
||
2306 RES_FMT_CHG
== nTmp
|| RES_ATTRSET_CHG
== nTmp
)
2308 lcl_SetWrong( *this, rNode
, nNPos
, nNPos
+ nNLen
, false );
2309 lcl_SetScriptInval( *this, nPos
);
2313 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2314 if( isA11yRelevantAttribute( pNewUpdate
->getWhichAttr() ) &&
2315 hasA11yRelevantAttribute( pNewUpdate
->getFmtAttrs() ) )
2317 SwViewShell
* pViewSh
= getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
2320 pViewSh
->InvalidateAccessibleParaAttrs( *this );
2326 case RES_OBJECTDYING
:
2329 case RES_PARATR_LINESPACING
:
2334 if( IsInSct() && !GetPrev() )
2336 SwSectionFrame
*pSect
= FindSctFrame();
2337 if( pSect
->ContainsAny() == this )
2338 pSect
->InvalidatePrt();
2342 // (1) Also invalidate next frame on next page/column.
2343 // (2) Skip empty sections and hidden paragraphs
2344 // Thus, use method <InvalidateNextPrtArea()>
2345 InvalidateNextPrtArea();
2351 case RES_TXTATR_FIELD
:
2352 case RES_TXTATR_ANNOTATION
:
2354 sal_Int32
const nNPos
= static_cast<const SwFormatField
*>(pNew
)->GetTextField()->GetStart();
2355 nPos
= MapModelToView(&rNode
, nNPos
);
2356 if (IsIdxInside(nPos
, TextFrameIndex(1)))
2361 // opt: invalidate window?
2366 InvalidateRange_(SwCharRange(nPos
, TextFrameIndex(1)));
2368 bSetFieldsDirty
= true;
2370 if ( SwSmartTagMgr::Get().IsSmartTagsEnabled() )
2371 lcl_SetWrong( *this, rNode
, nNPos
, nNPos
+ 1, false );
2375 case RES_TXTATR_FTN
:
2377 if (!IsInFootnote())
2378 { // the hint may be sent from the anchor node, or from a
2379 // node in the footnote; the anchor index is only valid in the
2381 assert(rNode
== static_cast<const SwFormatFootnote
*>(pNew
)->GetTextFootnote()->GetTextNode());
2382 nPos
= MapModelToView(&rNode
,
2383 static_cast<const SwFormatFootnote
*>(pNew
)->GetTextFootnote()->GetStart());
2386 else nPos
= TextFrameIndex(42); // shut up MSVC 2017 spurious warning C4701
2388 if (IsInFootnote() || IsIdxInside(nPos
, TextFrameIndex(1)))
2389 Prepare( PrepareHint::FootnoteInvalidation
, static_cast<const SwFormatFootnote
*>(pNew
)->GetTextFootnote() );
2393 case RES_ATTRSET_CHG
:
2395 InvalidateLineNum();
2397 const SwAttrSet
& rNewSet
= *static_cast<const SwAttrSetChg
*>(pNew
)->GetChgSet();
2399 sal_uInt16 nCount
= rNewSet
.Count();
2401 if( const SwFormatFootnote
* pItem
= rNewSet
.GetItemIfSet( RES_TXTATR_FTN
, false ) )
2403 nPos
= MapModelToView(&rNode
, pItem
->GetTextFootnote()->GetStart());
2404 if (IsIdxInside(nPos
, TextFrameIndex(1)))
2405 Prepare( PrepareHint::FootnoteInvalidation
, pNew
);
2410 if( const SwFormatField
* pItem
= rNewSet
.GetItemIfSet( RES_TXTATR_FIELD
, false ) )
2412 nPos
= MapModelToView(&rNode
, pItem
->GetTextField()->GetStart());
2413 if (IsIdxInside(nPos
, TextFrameIndex(1)))
2415 const SfxPoolItem
* pOldItem
= pOld
?
2416 &(static_cast<const SwAttrSetChg
*>(pOld
)->GetChgSet()->Get(RES_TXTATR_FIELD
)) : nullptr;
2417 if( pItem
== pOldItem
)
2423 InvalidateRange_(SwCharRange(nPos
, TextFrameIndex(1)));
2428 bool bLineSpace
= SfxItemState::SET
== rNewSet
.GetItemState(
2429 RES_PARATR_LINESPACING
, false ),
2430 bRegister
= SfxItemState::SET
== rNewSet
.GetItemState(
2431 RES_PARATR_REGISTER
, false );
2432 if ( bLineSpace
|| bRegister
)
2434 if (!m_pMergedPara
|| m_pMergedPara
->pParaPropsNode
== &rModify
)
2436 Prepare( bRegister
? PrepareHint::Register
: PrepareHint::AdjustSizeWithoutFormatting
);
2442 // (1) Also invalidate next frame on next page/column.
2443 // (2) Skip empty sections and hidden paragraphs
2444 // Thus, use method <InvalidateNextPrtArea()>
2445 InvalidateNextPrtArea();
2453 if ((!m_pMergedPara
|| m_pMergedPara
->pParaPropsNode
== &rModify
)
2454 && IsInSct() && !GetPrev())
2456 SwSectionFrame
*pSect
= FindSctFrame();
2457 if( pSect
->ContainsAny() == this )
2458 pSect
->InvalidatePrt();
2464 if ( SfxItemState::SET
== rNewSet
.GetItemState( RES_PARATR_SPLIT
,
2467 if (!m_pMergedPara
|| m_pMergedPara
->pParaPropsNode
== &rModify
)
2478 if( SfxItemState::SET
== rNewSet
.GetItemState( RES_BACKGROUND
, false)
2479 && (!m_pMergedPara
|| m_pMergedPara
->pParaPropsNode
== &rModify
)
2480 && !IsFollow() && GetDrawObjs() )
2482 SwSortedObjs
*pObjs
= GetDrawObjs();
2483 for ( size_t i
= 0; GetDrawObjs() && i
< pObjs
->size(); ++i
)
2485 SwAnchoredObject
* pAnchoredObj
= (*pObjs
)[i
];
2486 if ( auto pFly
= pAnchoredObj
->DynCastFlyFrame() )
2488 if( !pFly
->IsFlyInContentFrame() )
2490 const SvxBrushItem
&rBack
=
2491 pFly
->GetAttrSet()->GetBackground();
2493 // following condition determines, if the fly frame
2494 // "inherites" the background color of text frame.
2495 // This is the case, if fly frame background
2496 // color is "no fill"/"auto fill" and if the fly frame
2497 // has no background graphic.
2498 // Thus, check complete fly frame background
2499 // color and *not* only its transparency value
2500 if ( (rBack
.GetColor() == COL_TRANSPARENT
) &&
2501 rBack
.GetGraphicPos() == GPOS_NONE
)
2503 pFly
->SetCompletePaint();
2504 pFly
->InvalidatePage();
2511 if ( SfxItemState::SET
==
2512 rNewSet
.GetItemState( RES_TXTATR_CHARFMT
, false ) )
2514 lcl_SetWrong( *this, rNode
, 0, COMPLETE_STRING
, false );
2515 lcl_SetScriptInval( *this, TextFrameIndex(0) );
2517 else if ( SfxItemState::SET
==
2518 rNewSet
.GetItemState( RES_CHRATR_LANGUAGE
, false ) ||
2519 SfxItemState::SET
==
2520 rNewSet
.GetItemState( RES_CHRATR_CJK_LANGUAGE
, false ) ||
2521 SfxItemState::SET
==
2522 rNewSet
.GetItemState( RES_CHRATR_CTL_LANGUAGE
, false ) )
2523 lcl_SetWrong( *this, rNode
, 0, COMPLETE_STRING
, false );
2524 else if ( SfxItemState::SET
==
2525 rNewSet
.GetItemState( RES_CHRATR_FONT
, false ) ||
2526 SfxItemState::SET
==
2527 rNewSet
.GetItemState( RES_CHRATR_CJK_FONT
, false ) ||
2528 SfxItemState::SET
==
2529 rNewSet
.GetItemState( RES_CHRATR_CTL_FONT
, false ) )
2530 lcl_SetScriptInval( *this, TextFrameIndex(0) );
2531 else if ( SfxItemState::SET
==
2532 rNewSet
.GetItemState( RES_FRAMEDIR
, false )
2533 && (!m_pMergedPara
|| m_pMergedPara
->pParaPropsNode
== &rModify
))
2535 SetDerivedR2L( false );
2537 // Force complete paint due to existing indents.
2543 if( getRootFrame()->GetCurrShell() )
2549 if (nClear
|| (m_pMergedPara
&&
2550 (m_pMergedPara
->pParaPropsNode
!= &rModify
||
2551 m_pMergedPara
->pFirstNode
!= &rModify
)))
2554 SwAttrSetChg
aOldSet( *static_cast<const SwAttrSetChg
*>(pOld
) );
2555 SwAttrSetChg
aNewSet( *static_cast<const SwAttrSetChg
*>(pNew
) );
2557 if (m_pMergedPara
&& m_pMergedPara
->pParaPropsNode
!= &rModify
)
2559 for (sal_uInt16 i
= RES_PARATR_BEGIN
; i
!= RES_FRMATR_END
; ++i
)
2561 if (i
!= RES_BREAK
&& i
!= RES_PAGEDESC
)
2563 aOldSet
.ClearItem(i
);
2564 aNewSet
.ClearItem(i
);
2567 for (sal_uInt16 i
= XATTR_FILL_FIRST
; i
<= XATTR_FILL_LAST
; ++i
)
2569 aOldSet
.ClearItem(i
);
2570 aNewSet
.ClearItem(i
);
2573 if (m_pMergedPara
&& m_pMergedPara
->pFirstNode
!= &rModify
)
2575 aOldSet
.ClearItem(RES_BREAK
);
2576 aNewSet
.ClearItem(RES_BREAK
);
2577 aOldSet
.ClearItem(RES_PAGEDESC
);
2578 aNewSet
.ClearItem(RES_PAGEDESC
);
2583 aOldSet
.ClearItem( RES_TXTATR_FTN
);
2584 aNewSet
.ClearItem( RES_TXTATR_FTN
);
2588 aOldSet
.ClearItem( RES_TXTATR_FIELD
);
2589 aNewSet
.ClearItem( RES_TXTATR_FIELD
);
2591 if ( 0x04 & nClear
)
2595 aOldSet
.ClearItem( RES_PARATR_LINESPACING
);
2596 aNewSet
.ClearItem( RES_PARATR_LINESPACING
);
2600 aOldSet
.ClearItem( RES_PARATR_REGISTER
);
2601 aNewSet
.ClearItem( RES_PARATR_REGISTER
);
2604 if ( 0x08 & nClear
)
2606 aOldSet
.ClearItem( RES_PARATR_SPLIT
);
2607 aNewSet
.ClearItem( RES_PARATR_SPLIT
);
2609 if (aOldSet
.Count() || aNewSet
.Count())
2611 SwContentFrame::SwClientNotify(rModify
, sw::LegacyModifyHint(&aOldSet
, &aNewSet
));
2615 SwContentFrame::SwClientNotify(rModify
, sw::LegacyModifyHint(pOld
, pNew
));
2618 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2619 if (isA11yRelevantAttribute(nWhich
))
2621 SwViewShell
* pViewSh
= getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
2624 pViewSh
->InvalidateAccessibleParaAttrs( *this );
2630 case RES_PARATR_SPLIT
:
2634 bSetFieldsDirty
= true;
2637 assert(false); // should have been forwarded to SwContentFrame
2638 SetDerivedR2L( false );
2647 // is called by e. g. HiddenPara with 0
2648 SwFrame
*pNxt
= FindNext();
2649 if ( nullptr != pNxt
)
2650 pNxt
->InvalidatePrt();
2655 if( bSetFieldsDirty
)
2656 GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, &rNode
, SwNodeOffset(1) );
2658 if ( bRecalcFootnoteFlag
)
2662 bool SwTextFrame::GetInfo( SfxPoolItem
&rHint
) const
2664 if ( RES_VIRTPAGENUM_INFO
== rHint
.Which() && IsInDocBody() && ! IsFollow() )
2666 SwVirtPageNumInfo
&rInfo
= static_cast<SwVirtPageNumInfo
&>(rHint
);
2667 const SwPageFrame
*pPage
= FindPageFrame();
2670 if ( pPage
== rInfo
.GetOrigPage() && !GetPrev() )
2672 // this should be the one
2673 // (could only differ temporarily; is that disturbing?)
2674 rInfo
.SetInfo( pPage
, this );
2677 if ( pPage
->GetPhyPageNum() < rInfo
.GetOrigPage()->GetPhyPageNum() &&
2678 (!rInfo
.GetPage() || pPage
->GetPhyPageNum() > rInfo
.GetPage()->GetPhyPageNum()))
2680 // this could be the one
2681 rInfo
.SetInfo( pPage
, this );
2688 void SwTextFrame::PrepWidows( const sal_uInt16 nNeed
, bool bNotify
)
2690 OSL_ENSURE(GetFollow() && nNeed
, "+SwTextFrame::Prepare: lost all friends");
2692 SwParaPortion
*pPara
= GetPara();
2695 pPara
->SetPrepWidows();
2697 sal_uInt16 nHave
= nNeed
;
2699 // We yield a few lines and shrink in CalcPreps()
2700 SwSwapIfNotSwapped
swap( this );
2702 SwTextSizeInfo
aInf( this );
2703 SwTextMargin
aLine( this, &aInf
);
2705 TextFrameIndex nTmpLen
= aLine
.GetCurr()->GetLen();
2706 while( nHave
&& aLine
.PrevLine() )
2710 nTmpLen
= aLine
.GetCurr()->GetLen();
2713 // If it's certain that we can yield lines, the Master needs
2714 // to check the widow rule
2718 if( !IsFollow() ) // only a master decides about orphans
2720 const WidowsAndOrphans
aWidOrp( this );
2721 bSplit
= ( aLine
.GetLineNr() >= aWidOrp
.GetOrphansLines() &&
2722 aLine
.GetLineNr() >= aLine
.GetDropLines() );
2727 GetFollow()->SetOffset( aLine
.GetEnd() );
2728 aLine
.TruncLines( true );
2729 if( pPara
->IsFollowField() )
2730 GetFollow()->SetFieldFollow( true );
2740 static bool lcl_ErgoVadis(SwTextFrame
* pFrame
, TextFrameIndex
& rPos
, const PrepareHint ePrep
)
2742 const SwFootnoteInfo
&rFootnoteInfo
= pFrame
->GetDoc().GetFootnoteInfo();
2743 if( ePrep
== PrepareHint::ErgoSum
)
2745 if( rFootnoteInfo
.m_aErgoSum
.isEmpty() )
2747 rPos
= pFrame
->GetOffset();
2751 if( rFootnoteInfo
.m_aQuoVadis
.isEmpty() )
2753 if( pFrame
->HasFollow() )
2754 rPos
= pFrame
->GetFollow()->GetOffset();
2756 rPos
= TextFrameIndex(pFrame
->GetText().getLength());
2758 --rPos
; // our last character
2763 // Silence over-eager warning emitted at least by GCC 5.3.1
2764 #if defined __GNUC__ && !defined __clang__
2765 # pragma GCC diagnostic push
2766 # pragma GCC diagnostic ignored "-Wstrict-overflow"
2768 bool SwTextFrame::Prepare( const PrepareHint ePrep
, const void* pVoid
,
2771 bool bParaPossiblyInvalid
= false;
2773 SwFrameSwapper
aSwapper( this, false );
2779 case PrepareHint::BossChanged
:
2780 SetInvalidVert( true ); // Test
2782 case PrepareHint::WidowsOrphans
:
2783 case PrepareHint::Widows
:
2784 case PrepareHint::FootnoteInvalidationGone
: return bParaPossiblyInvalid
;
2786 case PrepareHint::FramePositionChanged
:
2788 // We also need an InvalidateSize for Areas (with and without columns),
2789 // so that we format and bUndersized is set (if needed)
2790 if( IsInFly() || IsInSct() )
2792 SwTwips nTmpBottom
= GetUpper()->getFrameArea().Top() +
2793 GetUpper()->getFramePrintArea().Bottom();
2794 if( nTmpBottom
< getFrameArea().Bottom() )
2797 // Are there any free-flying frames on this page?
2798 SwTextFly
aTextFly( this );
2799 if( aTextFly
.IsOn() )
2801 // Does any free-flying frame overlap?
2802 if ( aTextFly
.Relax() || IsUndersized() )
2805 if (GetTextNodeForParaProps()->GetSwAttrSet().GetRegister().GetValue())
2808 SwTextGridItem
const*const pGrid(GetGridItem(FindPageFrame()));
2809 if (pGrid
&& GetTextNodeForParaProps()->GetSwAttrSet().GetParaGrid().GetValue())
2812 // i#28701 - consider anchored objects
2813 if ( GetDrawObjs() )
2816 return bParaPossiblyInvalid
;
2823 // Split fly anchors are technically empty (have no SwParaPortion), but otherwise behave like
2824 // other split text frames, which are non-empty.
2825 bool bSplitFlyAnchor
= GetOffset() == TextFrameIndex(0) && HasFollow()
2826 && GetFollow()->GetOffset() == TextFrameIndex(0);
2828 if( !HasPara() && !bSplitFlyAnchor
&& PrepareHint::MustFit
!= ePrep
)
2830 SetInvalidVert( true ); // Test
2831 OSL_ENSURE( !IsLocked(), "SwTextFrame::Prepare: three of a perfect pair" );
2836 return bParaPossiblyInvalid
;
2839 // Get object from cache while locking
2840 SwTextLineAccess
aAccess( this );
2841 SwParaPortion
*pPara
= aAccess
.GetPara();
2845 case PrepareHint::FootnoteMove
:
2847 SwFrameAreaDefinition::FrameAreaWriteAccess
aFrm(*this);
2852 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*this);
2859 case PrepareHint::AdjustSizeWithoutFormatting
:
2860 pPara
->SetPrepAdjust();
2861 if( IsFootnoteNumFrame() != pPara
->IsFootnoteNum() ||
2864 InvalidateRange(SwCharRange(TextFrameIndex(0), TextFrameIndex(1)), 1);
2865 if( GetOffset() && !IsFollow() )
2866 SetOffset_(TextFrameIndex(0));
2869 case PrepareHint::MustFit
:
2870 pPara
->SetPrepMustFit(true);
2872 case PrepareHint::WidowsOrphans
:
2873 pPara
->SetPrepAdjust();
2875 case PrepareHint::Widows
:
2876 // MustFit is stronger than anything else
2877 if( pPara
->IsPrepMustFit() )
2878 return bParaPossiblyInvalid
;
2879 // see comment in WidowsAndOrphans::FindOrphans and CalcPreps()
2880 PrepWidows( *static_cast<const sal_uInt16
*>(pVoid
), bNotify
);
2883 case PrepareHint::FootnoteInvalidation
:
2885 SwTextFootnote
const *pFootnote
= static_cast<SwTextFootnote
const *>(pVoid
);
2886 if( IsInFootnote() )
2888 // Am I the first TextFrame of a footnote?
2890 // So we're a TextFrame of the footnote, which has
2891 // to display the footnote number or the ErgoSum text
2892 InvalidateRange(SwCharRange(TextFrameIndex(0), TextFrameIndex(1)), 1);
2896 // We're the last Footnote; we need to update the
2897 // QuoVadis texts now
2898 const SwFootnoteInfo
&rFootnoteInfo
= GetDoc().GetFootnoteInfo();
2899 if( !pPara
->UpdateQuoVadis( rFootnoteInfo
.m_aQuoVadis
) )
2901 TextFrameIndex nPos
= pPara
->GetParLen();
2904 InvalidateRange( SwCharRange(nPos
, TextFrameIndex(1)), 1);
2910 // We are the TextFrame _with_ the footnote
2911 TextFrameIndex
const nPos
= MapModelToView(
2912 &pFootnote
->GetTextNode(), pFootnote
->GetStart());
2913 InvalidateRange(SwCharRange(nPos
, TextFrameIndex(1)), 1);
2917 case PrepareHint::BossChanged
:
2921 SetInvalidVert( false );
2922 bool bOld
= IsVertical();
2923 SetInvalidVert( true );
2924 if( bOld
!= IsVertical() )
2925 InvalidateRange(SwCharRange(GetOffset(), TextFrameIndex(COMPLETE_STRING
)));
2930 TextFrameIndex nNxtOfst
= GetFollow()->GetOffset();
2933 InvalidateRange(SwCharRange( nNxtOfst
, TextFrameIndex(1)), 1);
2935 if( IsInFootnote() )
2937 TextFrameIndex nPos
;
2938 if( lcl_ErgoVadis( this, nPos
, PrepareHint::QuoVadis
) )
2939 InvalidateRange( SwCharRange( nPos
, TextFrameIndex(1)) );
2940 if( lcl_ErgoVadis( this, nPos
, PrepareHint::ErgoSum
) )
2941 InvalidateRange( SwCharRange( nPos
, TextFrameIndex(1)) );
2943 // If we have a page number field, we must invalidate those spots
2944 SwTextNode
const* pNode(nullptr);
2945 sw::MergedAttrIter
iter(*this);
2946 TextFrameIndex
const nEnd
= GetFollow()
2947 ? GetFollow()->GetOffset() : TextFrameIndex(COMPLETE_STRING
);
2948 for (SwTextAttr
const* pHt
= iter
.NextAttr(&pNode
); pHt
; pHt
= iter
.NextAttr(&pNode
))
2950 TextFrameIndex
const nStart(MapModelToView(pNode
, pHt
->GetStart()));
2951 if (nStart
>= GetOffset())
2956 // If we're flowing back and own a Footnote, the Footnote also flows
2957 // with us. So that it doesn't obstruct us, we send ourselves
2959 // pVoid != 0 means MoveBwd()
2960 const sal_uInt16 nWhich
= pHt
->Which();
2961 if (RES_TXTATR_FIELD
== nWhich
||
2962 (HasFootnote() && pVoid
&& RES_TXTATR_FTN
== nWhich
))
2963 InvalidateRange(SwCharRange(nStart
, TextFrameIndex(1)), 1);
2966 // A new boss, a new chance for growing
2967 if( IsUndersized() )
2970 InvalidateRange(SwCharRange(GetOffset(), TextFrameIndex(1)), 1);
2975 case PrepareHint::FramePositionChanged
:
2977 if ( isFramePrintAreaValid() )
2979 SwTextGridItem
const*const pGrid(GetGridItem(FindPageFrame()));
2980 if (pGrid
&& GetTextNodeForParaProps()->GetSwAttrSet().GetParaGrid().GetValue())
2984 // If we don't overlap with anybody:
2985 // did any free-flying frame overlapped _before_ the position change?
2986 bool bFormat
= pPara
->HasFly();
2991 SwTwips nTmpBottom
= GetUpper()->getFrameArea().Top() +
2992 GetUpper()->getFramePrintArea().Bottom();
2993 if( nTmpBottom
< getFrameArea().Bottom() )
2998 if ( GetDrawObjs() )
3000 const size_t nCnt
= GetDrawObjs()->size();
3001 for ( size_t i
= 0; i
< nCnt
; ++i
)
3003 SwAnchoredObject
* pAnchoredObj
= (*GetDrawObjs())[i
];
3004 // i#28701 - consider all
3005 // to-character anchored objects
3006 if ( pAnchoredObj
->GetFrameFormat().GetAnchor().GetAnchorId()
3007 == RndStdIds::FLY_AT_CHAR
)
3016 // Are there any free-flying frames on this page?
3017 SwTextFly
aTextFly( this );
3018 if( aTextFly
.IsOn() )
3020 // Does any free-flying frame overlap?
3021 const bool bRelaxed
= aTextFly
.Relax();
3022 bFormat
= bRelaxed
|| IsUndersized();
3025 // It's possible that pPara was deleted above; retrieve it again
3026 pPara
= aAccess
.GetPara();
3037 if( pPara
->GetRepaint().HasArea() )
3046 if (GetTextNodeForParaProps()->GetSwAttrSet().GetRegister().GetValue())
3047 bParaPossiblyInvalid
= Prepare( PrepareHint::Register
, nullptr, bNotify
);
3048 // The Frames need to be readjusted, which caused by changes
3050 else if( HasFootnote() )
3052 bParaPossiblyInvalid
= Prepare( PrepareHint::AdjustSizeWithoutFormatting
, nullptr, bNotify
);
3056 return bParaPossiblyInvalid
; // So that there's no SetPrep()
3058 if (bParaPossiblyInvalid
)
3060 // It's possible that pPara was deleted above; retrieve it again
3061 pPara
= aAccess
.GetPara();
3067 case PrepareHint::Register
:
3068 if (GetTextNodeForParaProps()->GetSwAttrSet().GetRegister().GetValue())
3070 pPara
->SetPrepAdjust();
3073 // It's possible that pPara was deleted above; retrieve it again
3074 bParaPossiblyInvalid
= true;
3075 pPara
= aAccess
.GetPara();
3079 SwFrame
* pNxt
= GetIndNext();
3080 if ( nullptr != pNxt
)
3082 pNxt
->InvalidatePrt_();
3083 if ( pNxt
->IsLayoutFrame() )
3084 pNxt
->InvalidatePage();
3089 case PrepareHint::FootnoteInvalidationGone
:
3091 // If a Follow is calling us, because a footnote is being deleted, our last
3092 // line has to be formatted, so that the first line of the Follow can flow up.
3093 // Which had flowed to the next page to be together with the footnote (this is
3094 // especially true for areas with columns)
3095 OSL_ENSURE( GetFollow(), "PrepareHint::FootnoteInvalidationGone may only be called by Follow" );
3096 TextFrameIndex nPos
= GetFollow()->GetOffset();
3097 if( IsFollow() && GetOffset() == nPos
) // If we don't have a mass of text, we call our
3098 FindMaster()->Prepare( PrepareHint::FootnoteInvalidationGone
); // Master's Prepare
3100 --nPos
; // The char preceding our Follow
3101 InvalidateRange(SwCharRange(nPos
, TextFrameIndex(1)));
3102 return bParaPossiblyInvalid
;
3104 case PrepareHint::ErgoSum
:
3105 case PrepareHint::QuoVadis
:
3107 TextFrameIndex nPos
;
3108 if( lcl_ErgoVadis( this, nPos
, ePrep
) )
3109 InvalidateRange(SwCharRange(nPos
, TextFrameIndex(1)));
3112 case PrepareHint::FlyFrameAttributesChanged
:
3116 TextFrameIndex
const nWhere
= CalcFlyPos( static_cast<SwFrameFormat
const *>(pVoid
) );
3117 OSL_ENSURE( TextFrameIndex(COMPLETE_STRING
) != nWhere
, "Prepare: Why me?" );
3118 InvalidateRange(SwCharRange(nWhere
, TextFrameIndex(1)));
3119 return bParaPossiblyInvalid
;
3121 [[fallthrough
]]; // else: continue with default case block
3123 case PrepareHint::Clear
:
3128 if( PrepareHint::FlyFrameArrive
== ePrep
|| PrepareHint::FlyFrameLeave
== ePrep
)
3130 TextFrameIndex
const nLen
= (GetFollow()
3131 ? GetFollow()->GetOffset()
3132 : TextFrameIndex(COMPLETE_STRING
))
3134 InvalidateRange( SwCharRange( GetOffset(), nLen
) );
3139 if( pPara
->GetRepaint().HasArea() )
3143 if( GetOffset() && !IsFollow() )
3144 SetOffset_( TextFrameIndex(0) );
3150 return bParaPossiblyInvalid
; // no SetPrep() happened
3158 return bParaPossiblyInvalid
;
3160 #if defined __GNUC__ && !defined __clang__
3161 # pragma GCC diagnostic pop
3165 * Small Helper class:
3166 * Prepares a test format.
3167 * The frame is changed in size and position, its SwParaPortion is moved aside
3168 * and a new one is created.
3169 * To achieve this, run formatting with bTestFormat flag set.
3170 * In the destructor the TextFrame is reset to its original state.
3174 SwTextFrame
*pFrame
;
3175 SwParaPortion
*pOldPara
;
3176 SwRect aOldFrame
, aOldPrt
;
3178 SwTestFormat( SwTextFrame
* pTextFrame
, const SwFrame
* pPrv
, SwTwips nMaxHeight
);
3182 SwTestFormat::SwTestFormat( SwTextFrame
* pTextFrame
, const SwFrame
* pPre
, SwTwips nMaxHeight
)
3183 : pFrame( pTextFrame
)
3185 aOldFrame
= pFrame
->getFrameArea();
3186 aOldPrt
= pFrame
->getFramePrintArea();
3188 SwRectFnSet
aRectFnSet(pFrame
);
3189 SwTwips nLower
= aRectFnSet
.GetBottomMargin(*pFrame
);
3192 // indeed, here the GetUpper()->getFramePrintArea() gets copied and manipulated
3193 SwFrameAreaDefinition::FrameAreaWriteAccess
aFrm(*pFrame
);
3194 aFrm
.setSwRect(pFrame
->GetUpper()->getFramePrintArea());
3195 aFrm
+= pFrame
->GetUpper()->getFrameArea().Pos();
3196 aRectFnSet
.SetHeight( aFrm
, nMaxHeight
);
3198 if( pFrame
->GetPrev() )
3202 aRectFnSet
.GetBottom(pFrame
->GetPrev()->getFrameArea()) - ( aRectFnSet
.IsVert() ? nMaxHeight
+ 1 : 0 ) );
3206 SwBorderAttrAccess
aAccess( SwFrame::GetCache(), pFrame
);
3207 const SwBorderAttrs
&rAttrs
= *aAccess
.Get();
3210 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*pFrame
);
3211 aRectFnSet
.SetPosX(aPrt
, rAttrs
.CalcLeft( pFrame
) );
3216 SwTwips nUpper
= pFrame
->CalcUpperSpace( &rAttrs
, pPre
);
3217 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*pFrame
);
3218 aRectFnSet
.SetPosY(aPrt
, nUpper
);
3222 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*pFrame
);
3223 aRectFnSet
.SetHeight( aPrt
, std::max( tools::Long(0) , aRectFnSet
.GetHeight(pFrame
->getFrameArea()) - aRectFnSet
.GetTop(aPrt
) - nLower
) );
3224 aRectFnSet
.SetWidth( aPrt
, aRectFnSet
.GetWidth(pFrame
->getFrameArea()) - ( rAttrs
.CalcLeft( pFrame
) + rAttrs
.CalcRight( pFrame
) ) );
3227 pOldPara
= pFrame
->HasPara() ? pFrame
->GetPara() : nullptr;
3228 pFrame
->SetPara( new SwParaPortion(), false );
3229 OSL_ENSURE( ! pFrame
->IsSwapped(), "A frame is swapped before Format_" );
3231 if ( pFrame
->IsVertical() )
3232 pFrame
->SwapWidthAndHeight();
3234 SwTextFormatInfo
aInf( pFrame
->getRootFrame()->GetCurrShell()->GetOut(), pFrame
, false, true, true );
3235 SwTextFormatter
aLine( pFrame
, &aInf
);
3237 pFrame
->Format_( aLine
, aInf
);
3239 if ( pFrame
->IsVertical() )
3240 pFrame
->SwapWidthAndHeight();
3242 OSL_ENSURE( ! pFrame
->IsSwapped(), "A frame is swapped after Format_" );
3245 SwTestFormat::~SwTestFormat()
3248 SwFrameAreaDefinition::FrameAreaWriteAccess
aFrm(*pFrame
);
3249 aFrm
.setSwRect(aOldFrame
);
3253 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*pFrame
);
3254 aPrt
.setSwRect(aOldPrt
);
3257 pFrame
->SetPara( pOldPara
);
3260 bool SwTextFrame::TestFormat( const SwFrame
* pPrv
, SwTwips
&rMaxHeight
, bool &bSplit
)
3262 PROTOCOL_ENTER( this, PROT::TestFormat
, DbgAction::NONE
, nullptr )
3264 if( IsLocked() && GetUpper()->getFramePrintArea().Width() <= 0 )
3267 SwTestFormat
aSave( this, pPrv
, rMaxHeight
);
3269 return SwTextFrame::WouldFit(rMaxHeight
, bSplit
, true, false);
3273 * We should not and don't need to reformat.
3274 * We assume that we already formatted and that the formatting
3275 * data is still current.
3277 * We also assume that the frame width of the Master and Follow
3278 * are the same. That's why we're not calling FindBreak() for
3280 * The required height is coming from nMaxHeight.
3282 * @returns true if I can split
3284 bool SwTextFrame::WouldFit(SwTwips
&rMaxHeight
, bool &bSplit
, bool bTst
, bool bMoveBwd
)
3286 OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
3287 "SwTextFrame::WouldFit with swapped frame" );
3288 SwRectFnSet
aRectFnSet(this);
3293 // it can happen that the IdleCollector removed the cached information
3297 // i#27801 - correction: 'short cut' for empty paragraph
3298 // can *not* be applied, if test format is in progress. The test format doesn't
3299 // adjust the frame and the printing area - see method <SwTextFrame::Format_(..)>,
3300 // which is called in <SwTextFrame::TestFormat(..)>
3301 if ( IsEmpty() && !bTst
)
3304 SwTwips nHeight
= aRectFnSet
.IsVert() ? getFramePrintArea().SSize().Width() : getFramePrintArea().SSize().Height();
3305 if( rMaxHeight
< nHeight
)
3309 rMaxHeight
-= nHeight
;
3314 // GetPara can still be 0 in edge cases
3315 // We return true in order to be reformatted on the new Page
3316 OSL_ENSURE( HasPara() || IsHiddenNow(), "WouldFit: GetFormatted() and then !HasPara()" );
3317 if( !HasPara() || ( !aRectFnSet
.GetHeight(getFrameArea()) && IsHiddenNow() ) )
3320 // Because the Orphan flag only exists for a short moment, we also check
3321 // whether the Framesize is set to very huge by CalcPreps, in order to
3323 if (IsWidow() || (aRectFnSet
.IsVert()
3324 ? (0 == getFrameArea().Left())
3325 : (sw::WIDOW_MAGIC
- 20000 < getFrameArea().Bottom())))
3330 // If we've ended up here due to a Widow request by our Follow, we check
3331 // whether there's a Follow with a real height at all.
3332 // Else (e.g. for newly created SctFrames) we ignore the IsWidow() and
3333 // still check if we can find enough room
3334 if (((!aRectFnSet
.IsVert() && getFrameArea().Bottom() <= sw::WIDOW_MAGIC
- 20000) ||
3335 ( aRectFnSet
.IsVert() && 0 < getFrameArea().Left() ) ) &&
3336 ( GetFollow()->IsVertical() ?
3337 !GetFollow()->getFrameArea().Width() :
3338 !GetFollow()->getFrameArea().Height() ) )
3340 SwTextFrame
* pFoll
= GetFollow()->GetFollow();
3342 ( pFoll
->IsVertical() ?
3343 !pFoll
->getFrameArea().Width() :
3344 !pFoll
->getFrameArea().Height() ) )
3345 pFoll
= pFoll
->GetFollow();
3354 SwSwapIfNotSwapped
swap( this );
3356 SwTextSizeInfo
aInf( this );
3357 SwTextMargin
aLine( this, &aInf
);
3359 WidowsAndOrphans
aFrameBreak( this, rMaxHeight
, bSplit
);
3364 // is breaking necessary?
3365 bSplit
= !aFrameBreak
.IsInside( aLine
);
3367 bRet
= !aFrameBreak
.IsKeepAlways() && aFrameBreak
.WouldFit(aLine
, rMaxHeight
, bTst
, bMoveBwd
);
3370 // we need the total height including the current line
3374 rMaxHeight
-= aLine
.GetLineHeight();
3375 } while ( aLine
.Next() );
3381 SwTwips
SwTextFrame::GetParHeight() const
3383 OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
3384 "SwTextFrame::GetParHeight with swapped frame" );
3387 { // For non-empty paragraphs this is a special case
3388 // For UnderSized we can simply just ask 1 Twip more
3389 sal_uInt16 nRet
= o3tl::narrowing
<sal_uInt16
>(getFramePrintArea().SSize().Height());
3390 if( IsUndersized() )
3392 if( IsEmpty() || GetText().isEmpty() )
3393 nRet
= o3tl::narrowing
<sal_uInt16
>(EmptyHeight());
3400 // TODO: Refactor and improve code
3401 const SwLineLayout
* pLineLayout
= GetPara();
3402 SwTwips nHeight
= pLineLayout
? pLineLayout
->GetRealHeight() : 0;
3404 // Is this paragraph scrolled? Our height until now is at least
3405 // one line height too low then
3406 if( GetOffset() && !IsFollow() )
3409 while ( pLineLayout
&& pLineLayout
->GetNext() )
3411 pLineLayout
= pLineLayout
->GetNext();
3412 nHeight
= nHeight
+ pLineLayout
->GetRealHeight();
3419 * @returns this _always_ in the formatted state!
3421 SwTextFrame
* SwTextFrame::GetFormatted( bool bForceQuickFormat
)
3423 vcl::RenderContext
* pRenderContext
= getRootFrame()->GetCurrShell()->GetOut();
3424 SwSwapIfSwapped
swap( this );
3426 // In case the SwLineLayout was cleared out of the s_pTextCache, recreate it
3427 // Not for empty paragraphs
3428 if( !HasPara() && !(isFrameAreaDefinitionValid() && IsEmpty()) )
3430 // Calc() must be called, because frame position can be wrong
3431 const bool bFormat
= isFrameAreaSizeValid();
3432 Calc(pRenderContext
); // calls Format() if invalid
3434 // If the flags were valid (hence bFormat=true), Calc did nothing,
3435 // so Format() must be called manually in order to recreate
3436 // the SwLineLayout that has been deleted from the
3437 // SwTextFrame::s_pTextCache (hence !HasPara() above).
3438 // Optimization with FormatQuick()
3439 if( bFormat
&& !FormatQuick( bForceQuickFormat
) )
3440 Format(getRootFrame()->GetCurrShell()->GetOut());
3446 SwTwips
SwTextFrame::CalcFitToContent()
3449 // If we are currently locked, we better return with a
3450 // fairly reasonable value:
3452 return getFramePrintArea().Width();
3454 SwParaPortion
* pOldPara
= GetPara();
3455 SwParaPortion
*pDummy
= new SwParaPortion();
3456 SetPara( pDummy
, false );
3457 const SwPageFrame
* pPage
= FindPageFrame();
3459 const Point aOldFramePos
= getFrameArea().Pos();
3460 const SwTwips nOldFrameWidth
= getFrameArea().Width();
3461 const SwTwips nOldPrtWidth
= getFramePrintArea().Width();
3462 const SwTwips nPageWidth
= GetUpper()->IsVertical() ?
3463 pPage
->getFramePrintArea().Height() :
3464 pPage
->getFramePrintArea().Width();
3467 SwFrameAreaDefinition::FrameAreaWriteAccess
aFrm(*this);
3468 aFrm
.Width( nPageWidth
);
3472 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*this);
3473 aPrt
.Width( nPageWidth
);
3476 // i#25422 objects anchored as character in RTL
3477 if ( IsRightToLeft() )
3479 SwFrameAreaDefinition::FrameAreaWriteAccess
aFrm(*this);
3480 aFrm
.Pos().AdjustX(nOldFrameWidth
- nPageWidth
);
3483 TextFrameLockGuard
aLock( this );
3485 SwTextFormatInfo
aInf( getRootFrame()->GetCurrShell()->GetOut(), this, false, true, true );
3486 aInf
.SetIgnoreFly( true );
3487 SwTextFormatter
aLine( this, &aInf
);
3488 SwHookOut
aHook( aInf
);
3490 // i#54031 - assure minimum of MINLAY twips.
3491 const SwTwips nMax
= std::max( SwTwips(MINLAY
), aLine
.CalcFitToContent_() + 1 );
3494 SwFrameAreaDefinition::FrameAreaWriteAccess
aFrm(*this);
3495 aFrm
.Width( nOldFrameWidth
);
3497 // i#25422 objects anchored as character in RTL
3498 if ( IsRightToLeft() )
3500 aFrm
.Pos() = aOldFramePos
;
3505 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*this);
3506 aPrt
.Width( nOldPrtWidth
);
3509 SetPara( pOldPara
);
3515 * Simulate format for a list item paragraph, whose list level attributes
3516 * are in LABEL_ALIGNMENT mode, in order to determine additional first
3517 * line offset for the real text formatting due to the value of label
3518 * adjustment attribute of the list level.
3520 void SwTextFrame::CalcAdditionalFirstLineOffset()
3525 // reset additional first line offset
3526 mnAdditionalFirstLineOffset
= 0;
3528 const SwTextNode
* pTextNode( GetTextNodeForParaProps() );
3529 // sw_redlinehide: check that pParaPropsNode is the correct one
3530 assert(pTextNode
->IsNumbered(getRootFrame()) == pTextNode
->IsNumbered(nullptr));
3531 if (!(pTextNode
->IsNumbered(getRootFrame()) &&
3532 pTextNode
->IsCountedInList() && pTextNode
->GetNumRule()))
3535 int nListLevel
= pTextNode
->GetActualListLevel();
3540 if (nListLevel
>= MAXLEVEL
)
3541 nListLevel
= MAXLEVEL
- 1;
3543 const SwNumFormat
& rNumFormat
=
3544 pTextNode
->GetNumRule()->Get( o3tl::narrowing
<sal_uInt16
>(nListLevel
) );
3545 if ( rNumFormat
.GetPositionAndSpaceMode() != SvxNumberFormat::LABEL_ALIGNMENT
)
3548 // keep current paragraph portion and apply dummy paragraph portion
3549 SwParaPortion
* pOldPara
= GetPara();
3550 SwParaPortion
*pDummy
= new SwParaPortion();
3551 SetPara( pDummy
, false );
3554 TextFrameLockGuard
aLock( this );
3556 // simulate text formatting
3557 SwTextFormatInfo
aInf( getRootFrame()->GetCurrShell()->GetOut(), this, false, true, true );
3558 aInf
.SetIgnoreFly( true );
3559 SwTextFormatter
aLine( this, &aInf
);
3560 SwHookOut
aHook( aInf
);
3561 aLine
.CalcFitToContent_();
3563 // determine additional first line offset
3564 const SwLinePortion
* pFirstPortion
= aLine
.GetCurr()->GetFirstPortion();
3565 if ( pFirstPortion
->InNumberGrp() && !pFirstPortion
->IsFootnoteNumPortion() )
3567 SwTwips
nNumberPortionWidth( pFirstPortion
->Width() );
3569 const SwLinePortion
* pPortion
= pFirstPortion
->GetNextPortion();
3571 pPortion
->InNumberGrp() && !pPortion
->IsFootnoteNumPortion())
3573 nNumberPortionWidth
+= pPortion
->Width();
3574 pPortion
= pPortion
->GetNextPortion();
3577 if ( ( IsRightToLeft() &&
3578 rNumFormat
.GetNumAdjust() == SvxAdjust::Left
) ||
3579 ( !IsRightToLeft() &&
3580 rNumFormat
.GetNumAdjust() == SvxAdjust::Right
) )
3582 mnAdditionalFirstLineOffset
= -nNumberPortionWidth
;
3584 else if ( rNumFormat
.GetNumAdjust() == SvxAdjust::Center
)
3586 mnAdditionalFirstLineOffset
= -(nNumberPortionWidth
/2);
3590 // restore paragraph portion
3591 SetPara( pOldPara
);
3595 * Determine the height of the last line for the calculation of
3596 * the proportional line spacing
3598 * Height of last line will be stored in new member
3599 * mnHeightOfLastLine and can be accessed via method
3600 * GetHeightOfLastLine()
3602 * @param _bUseFont force the usage of the former algorithm to
3603 * determine the height of the last line, which
3606 void SwTextFrame::CalcHeightOfLastLine( const bool _bUseFont
)
3609 // Invalidate printing area, if height of last line changes
3610 const SwTwips
nOldHeightOfLastLine( mnHeightOfLastLine
);
3612 // determine output device
3613 SwViewShell
* pVsh
= getRootFrame()->GetCurrShell();
3614 OSL_ENSURE( pVsh
, "<SwTextFrame::_GetHeightOfLastLineForPropLineSpacing()> - no SwViewShell" );
3617 // There could be no <SwViewShell> instance in the case of loading a binary
3618 // StarOffice file format containing an embedded Writer document.
3623 OutputDevice
* pOut
= pVsh
->GetOut();
3624 const IDocumentSettingAccess
*const pIDSA
= &GetDoc().getIDocumentSettingAccess();
3625 if ( !pVsh
->GetViewOptions()->getBrowseMode() ||
3626 pVsh
->GetViewOptions()->IsPrtFormat() )
3628 pOut
= GetDoc().getIDocumentDeviceAccess().getReferenceDevice( true );
3630 OSL_ENSURE( pOut
, "<SwTextFrame::_GetHeightOfLastLineForPropLineSpacing()> - no OutputDevice" );
3637 // determine height of last line
3638 if ( _bUseFont
|| pIDSA
->get(DocumentSettingId::OLD_LINE_SPACING
) )
3640 // former determination of last line height for proportional line
3641 // spacing - take height of font set at the paragraph
3642 // FIXME actually... must the font match across all nodes?
3643 SwFont
aFont( &GetTextNodeForParaProps()->GetSwAttrSet(), pIDSA
);
3645 // we must ensure that the font is restored correctly on the OutputDevice
3646 // otherwise Last!=Owner could occur
3649 SwFntObj
*pOldFont
= pLastFont
;
3650 pLastFont
= nullptr;
3651 aFont
.SetFntChg( true );
3652 aFont
.ChgPhysFnt( pVsh
, *pOut
);
3653 mnHeightOfLastLine
= aFont
.GetHeight( pVsh
, *pOut
);
3654 assert(pLastFont
&& "coverity[var_deref_model] - pLastFont should be set in SwSubFont::ChgFnt");
3655 pLastFont
->Unlock();
3656 pLastFont
= pOldFont
;
3657 pLastFont
->SetDevFont( pVsh
, *pOut
);
3661 vcl::Font aOldFont
= pOut
->GetFont();
3662 aFont
.SetFntChg( true );
3663 aFont
.ChgPhysFnt( pVsh
, *pOut
);
3664 mnHeightOfLastLine
= aFont
.GetHeight( pVsh
, *pOut
);
3665 assert(pLastFont
&& "coverity[var_deref_model] - pLastFont should be set in SwSubFont::ChgFnt");
3666 pLastFont
->Unlock();
3667 pLastFont
= nullptr;
3668 pOut
->SetFont( aOldFont
);
3673 // new determination of last line height - take actually height of last line
3675 // assure same results, if paragraph is undersized
3676 if ( IsUndersized() )
3678 mnHeightOfLastLine
= 0;
3682 bool bCalcHeightOfLastLine
= true;
3683 if ( ( !HasPara() && IsEmpty( ) ) || GetText().isEmpty() )
3685 mnHeightOfLastLine
= EmptyHeight();
3686 bCalcHeightOfLastLine
= false;
3689 if ( bCalcHeightOfLastLine
)
3691 OSL_ENSURE( HasPara(),
3692 "<SwTextFrame::CalcHeightOfLastLine()> - missing paragraph portions." );
3693 const SwLineLayout
* pLineLayout
= GetPara();
3694 while ( pLineLayout
&& pLineLayout
->GetNext() )
3696 // iteration to last line
3697 pLineLayout
= pLineLayout
->GetNext();
3701 SwTwips nAscent
, nDescent
, nDummy1
, nDummy2
;
3702 // i#47162 - suppress consideration of
3703 // fly content portions and the line portion.
3704 pLineLayout
->MaxAscentDescent( nAscent
, nDescent
,
3708 // Suppress wrong invalidation of printing area, if method is
3709 // called recursive.
3710 // Thus, member <mnHeightOfLastLine> is only set directly, if
3711 // no recursive call is needed.
3712 const SwTwips nNewHeightOfLastLine
= nAscent
+ nDescent
;
3713 // i#47162 - if last line only contains
3714 // fly content portions, <mnHeightOfLastLine> is zero.
3715 // In this case determine height of last line by the font
3716 if ( nNewHeightOfLastLine
== 0 )
3718 CalcHeightOfLastLine( true );
3722 mnHeightOfLastLine
= nNewHeightOfLastLine
;
3729 // invalidate printing area, if height of last line changes
3730 if ( mnHeightOfLastLine
!= nOldHeightOfLastLine
)
3737 * Method returns the value of the inter line spacing for a text frame.
3738 * Such a value exists for proportional line spacings ("1,5 Lines",
3739 * "Double", "Proportional" and for leading line spacing ("Leading").
3741 * @param _bNoPropLineSpacing (default = false) control whether the
3742 * value of a proportional line spacing is
3745 tools::Long
SwTextFrame::GetLineSpace( const bool _bNoPropLineSpace
) const
3747 tools::Long nRet
= 0;
3749 const SvxLineSpacingItem
&rSpace
= GetTextNodeForParaProps()->GetSwAttrSet().GetLineSpacing();
3751 switch( rSpace
.GetInterLineSpaceRule() )
3753 case SvxInterLineSpaceRule::Prop
:
3755 if ( _bNoPropLineSpace
)
3760 // i#11860 - adjust spacing implementation for object positioning
3761 // - compatibility to MS Word
3762 nRet
= GetHeightOfLastLine();
3764 tools::Long nTmp
= nRet
;
3765 nTmp
*= rSpace
.GetPropLineSpace();
3774 case SvxInterLineSpaceRule::Fix
:
3776 if ( rSpace
.GetInterLineSpace() > 0 )
3777 nRet
= rSpace
.GetInterLineSpace();
3786 sal_uInt16
SwTextFrame::FirstLineHeight() const
3790 if( IsEmpty() && isFrameAreaDefinitionValid() )
3791 return IsVertical() ? o3tl::narrowing
<sal_uInt16
>(getFramePrintArea().Width()) : o3tl::narrowing
<sal_uInt16
>(getFramePrintArea().Height());
3794 const SwParaPortion
*pPara
= GetPara();
3798 // tdf#146500 Lines with only fly overlap cannot be "moved", so the idea
3799 // here is to continue until there's some text.
3800 // FIXME ideally we want to count a fly to the line in which it is anchored
3801 // - it may even be anchored in some other paragraph! SwFlyPortion doesn't
3802 // have a pointer sadly so no way to find out.
3803 sal_uInt16
nHeight(0);
3804 for (SwLineLayout
const* pLine
= pPara
; pLine
; pLine
= pLine
->GetNext())
3806 nHeight
+= pLine
->Height();
3807 if (::sw::FindNonFlyPortion(*pLine
))
3815 sal_Int32
SwTextFrame::GetLineCount(TextFrameIndex
const nPos
)
3818 SwTextFrame
*pFrame
= this;
3821 pFrame
->GetFormatted();
3822 if( !pFrame
->HasPara() )
3824 SwTextSizeInfo
aInf( pFrame
);
3825 SwTextMargin
aLine( pFrame
, &aInf
);
3826 if (TextFrameIndex(COMPLETE_STRING
) == nPos
)
3829 aLine
.CharToLine( nPos
);
3830 nRet
= nRet
+ aLine
.GetLineNr();
3831 pFrame
= pFrame
->GetFollow();
3832 } while ( pFrame
&& pFrame
->GetOffset() <= nPos
);
3836 void SwTextFrame::ChgThisLines()
3838 // not necessary to format here (GetFormatted etc.), because we have to come from there!
3840 const SwLineNumberInfo
&rInf
= GetDoc().GetLineNumberInfo();
3841 if ( !GetText().isEmpty() && HasPara() )
3843 SwTextSizeInfo
aInf( this );
3844 SwTextMargin
aLine( this, &aInf
);
3845 if ( rInf
.IsCountBlankLines() )
3848 nNew
= aLine
.GetLineNr();
3854 if( aLine
.GetCurr()->HasContent() )
3856 } while ( aLine
.NextLine() );
3859 else if ( rInf
.IsCountBlankLines() )
3862 if ( nNew
== mnThisLines
)
3865 if (!IsInTab() && GetTextNodeForParaProps()->GetSwAttrSet().GetLineNumber().IsCount())
3867 mnAllLines
-= mnThisLines
;
3869 mnAllLines
+= mnThisLines
;
3870 SwFrame
*pNxt
= GetNextContentFrame();
3871 while( pNxt
&& pNxt
->IsInTab() )
3873 pNxt
= pNxt
->FindTabFrame();
3874 if( nullptr != pNxt
)
3875 pNxt
= pNxt
->FindNextCnt();
3878 pNxt
->InvalidateLineNum();
3880 // Extend repaint to the bottom.
3883 SwRepaint
& rRepaint
= GetPara()->GetRepaint();
3884 rRepaint
.Bottom( std::max( rRepaint
.Bottom(),
3885 getFrameArea().Top()+getFramePrintArea().Bottom()));
3888 else // Paragraphs which are not counted should not manipulate the AllLines.
3892 void SwTextFrame::RecalcAllLines()
3899 const sal_Int32 nOld
= GetAllLines();
3900 const SwFormatLineNumber
&rLineNum
= GetTextNodeForParaProps()->GetSwAttrSet().GetLineNumber();
3902 const bool bRestart
= GetDoc().GetLineNumberInfo().IsRestartEachPage();
3904 if ( !IsFollow() && rLineNum
.GetStartValue() && rLineNum
.IsCount() )
3905 nNewNum
= rLineNum
.GetStartValue() - 1;
3906 // If it is a follow or not has not be considered if it is a restart at each page; the
3907 // restart should also take effect at follows.
3908 else if ( bRestart
&& FindPageFrame()->FindFirstBodyContent() == this )
3914 SwContentFrame
*pPrv
= GetPrevContentFrame();
3916 (pPrv
->IsInTab() || pPrv
->IsInDocBody() != IsInDocBody()) )
3917 pPrv
= pPrv
->GetPrevContentFrame();
3919 // i#78254 Restart line numbering at page change
3920 // First body content may be in table!
3921 if ( bRestart
&& pPrv
&& pPrv
->FindPageFrame() != FindPageFrame() )
3924 nNewNum
= pPrv
? static_cast<SwTextFrame
*>(pPrv
)->GetAllLines() : 0;
3926 if ( rLineNum
.IsCount() )
3927 nNewNum
+= GetThisLines();
3929 if ( nOld
== nNewNum
)
3932 mnAllLines
= nNewNum
;
3933 SwContentFrame
*pNxt
= GetNextContentFrame();
3935 (pNxt
->IsInTab() || pNxt
->IsInDocBody() != IsInDocBody()) )
3936 pNxt
= pNxt
->GetNextContentFrame();
3939 if ( pNxt
->GetUpper() != GetUpper() )
3940 pNxt
->InvalidateLineNum();
3942 pNxt
->InvalidateLineNum_();
3946 void SwTextFrame::VisitPortions( SwPortionHandler
& rPH
) const
3948 const SwParaPortion
* pPara
= isFrameAreaDefinitionValid() ? GetPara() : nullptr;
3953 rPH
.Skip( GetOffset() );
3955 const SwLineLayout
* pLine
= pPara
;
3958 const SwLinePortion
* pPor
= pLine
->GetFirstPortion();
3961 pPor
->HandlePortion( rPH
);
3962 pPor
= pPor
->GetNextPortion();
3966 pLine
= pLine
->GetNext();
3973 const SwScriptInfo
* SwTextFrame::GetScriptInfo() const
3975 const SwParaPortion
* pPara
= GetPara();
3976 return pPara
? &pPara
->GetScriptInfo() : nullptr;
3980 * Helper function for SwTextFrame::CalcBasePosForFly()
3982 static SwTwips
lcl_CalcFlyBasePos( const SwTextFrame
& rFrame
, SwRect aFlyRect
,
3983 SwTextFly
const & rTextFly
)
3985 SwRectFnSet
aRectFnSet(&rFrame
);
3986 SwTwips nRet
= rFrame
.IsRightToLeft() ?
3987 aRectFnSet
.GetRight(rFrame
.getFrameArea()) :
3988 aRectFnSet
.GetLeft(rFrame
.getFrameArea());
3992 SwRect aRect
= rTextFly
.GetFrame( aFlyRect
);
3993 if ( 0 != aRectFnSet
.GetWidth(aRect
) )
3995 if ( rFrame
.IsRightToLeft() )
3997 if ( aRectFnSet
.GetRight(aRect
) -
3998 aRectFnSet
.GetRight(aFlyRect
) >= 0 )
4000 aRectFnSet
.SetRight(
4001 aFlyRect
, aRectFnSet
.GetLeft(aRect
) );
4002 nRet
= aRectFnSet
.GetLeft(aRect
);
4009 if ( aRectFnSet
.GetLeft(aFlyRect
) -
4010 aRectFnSet
.GetLeft(aRect
) >= 0 )
4013 aFlyRect
, aRectFnSet
.GetRight(aRect
) + 1 );
4014 nRet
= aRectFnSet
.GetRight(aRect
);
4023 while ( aRectFnSet
.GetWidth(aFlyRect
) > 0 );
4028 void SwTextFrame::CalcBaseOfstForFly()
4030 OSL_ENSURE( !IsVertical() || !IsSwapped(),
4031 "SwTextFrame::CalcBasePosForFly with swapped frame!" );
4033 if (!GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::ADD_FLY_OFFSETS
))
4036 SwRectFnSet
aRectFnSet(this);
4038 SwRect
aFlyRect( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() );
4040 // Get first 'real' line and adjust position and height of line rectangle.
4041 // Correct behaviour if no 'real' line exists
4042 // (empty paragraph with and without a dummy portion)
4043 SwTwips nFlyAnchorVertOfstNoWrap
= 0;
4045 SwTwips nTop
= aRectFnSet
.GetTop(aFlyRect
);
4046 const SwLineLayout
* pLay
= GetPara();
4047 SwTwips nLineHeight
= 200;
4048 while( pLay
&& pLay
->IsDummy() && pLay
->GetNext() )
4050 nTop
+= pLay
->Height();
4051 nFlyAnchorVertOfstNoWrap
+= pLay
->Height();
4052 pLay
= pLay
->GetNext();
4056 nLineHeight
= pLay
->Height();
4058 aRectFnSet
.SetTopAndHeight( aFlyRect
, nTop
, nLineHeight
);
4061 SwTextFly
aTextFly( this );
4062 aTextFly
.SetIgnoreCurrentFrame( true );
4063 aTextFly
.SetIgnoreContour( true );
4064 // ignore objects in page header|footer for
4065 // text frames not in page header|footer
4066 aTextFly
.SetIgnoreObjsInHeaderFooter( true );
4067 SwTwips nRet1
= lcl_CalcFlyBasePos( *this, aFlyRect
, aTextFly
);
4068 aTextFly
.SetIgnoreCurrentFrame( false );
4069 SwTwips nRet2
= lcl_CalcFlyBasePos( *this, aFlyRect
, aTextFly
);
4071 // make values relative to frame start position
4072 SwTwips nLeft
= IsRightToLeft() ?
4073 aRectFnSet
.GetRight(getFrameArea()) :
4074 aRectFnSet
.GetLeft(getFrameArea());
4076 mnFlyAnchorOfst
= nRet1
- nLeft
;
4077 mnFlyAnchorOfstNoWrap
= nRet2
- nLeft
;
4079 if (!GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS
))
4082 if (mnFlyAnchorOfstNoWrap
> 0)
4083 mnFlyAnchorVertOfstNoWrap
= nFlyAnchorVertOfstNoWrap
;
4086 SwTwips
SwTextFrame::GetBaseVertOffsetForFly(bool bIgnoreFlysAnchoredAtThisFrame
) const
4088 return bIgnoreFlysAnchoredAtThisFrame
? 0 : mnFlyAnchorVertOfstNoWrap
;
4092 * Repaint all text frames of the given text node
4094 void SwTextFrame::repaintTextFrames( const SwTextNode
& rNode
)
4096 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(rNode
);
4097 for( const SwTextFrame
*pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next() )
4099 SwRect
aRec( pFrame
->GetPaintArea() );
4100 const SwRootFrame
*pRootFrame
= pFrame
->getRootFrame();
4101 SwViewShell
*pCurShell
= pRootFrame
? pRootFrame
->GetCurrShell() : nullptr;
4103 pCurShell
->InvalidateWindows( aRec
);
4107 void SwTextFrame::UpdateOutlineContentVisibilityButton(SwWrtShell
* pWrtSh
) const
4109 if (pWrtSh
&& pWrtSh
->GetViewOptions()->IsShowOutlineContentVisibilityButton() &&
4110 GetTextNodeFirst()->IsOutline())
4112 SwEditWin
& rEditWin
= pWrtSh
->GetView().GetEditWin();
4113 SwFrameControlsManager
& rMngr
= rEditWin
.GetFrameControlsManager();
4114 rMngr
.SetOutlineContentVisibilityButton(this);
4118 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */