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/charhiddenitem.hxx>
29 #include <editeng/pgrditem.hxx>
30 #include <comphelper/configuration.hxx>
31 #include <swmodule.hxx>
32 #include <SwSmartTagMgr.hxx>
34 #include <IDocumentSettingAccess.hxx>
35 #include <IDocumentDeviceAccess.hxx>
36 #include <IDocumentFieldsAccess.hxx>
37 #include <rootfrm.hxx>
38 #include <pagefrm.hxx>
43 #include <viewopt.hxx>
47 #include <frmtool.hxx>
48 #include <tgrditem.hxx>
49 #include <dbg_lay.hxx>
54 #include <ftninfo.hxx>
55 #include <fmtline.hxx>
57 #include <notxtfrm.hxx>
58 #include <sectfrm.hxx>
59 #include "itrform2.hxx"
61 #include "txtcache.hxx"
62 #include <fntcache.hxx>
63 #include <SwGrammarMarkUp.hxx>
64 #include <lineinfo.hxx>
65 #include <SwPortionHandler.hxx>
66 #include <dcontact.hxx>
67 #include <sortedobjs.hxx>
68 #include <txtflcnt.hxx>
69 #include <fmtflcnt.hxx>
70 #include <fmtcntnt.hxx>
71 #include <numrule.hxx>
72 #include <GrammarContact.hxx>
80 #include <FrameControlsManager.hxx>
84 MergedAttrIterBase::MergedAttrIterBase(SwTextFrame
const& rFrame
)
85 : m_pMerged(rFrame
.GetMergedPara())
86 , m_pNode(m_pMerged
? nullptr : rFrame
.GetTextNodeFirst())
92 SwTextAttr
const* MergedAttrIter::NextAttr(SwTextNode
const** ppNode
)
96 while (m_CurrentExtent
< m_pMerged
->extents
.size())
98 sw::Extent
const& rExtent(m_pMerged
->extents
[m_CurrentExtent
]);
99 if (SwpHints
const*const pHints
= rExtent
.pNode
->GetpSwpHints())
101 while (m_CurrentHint
< pHints
->Count())
103 SwTextAttr
*const pHint(pHints
->Get(m_CurrentHint
));
104 if (rExtent
.nEnd
< pHint
->GetStart()
105 // <= if it has no end or isn't empty
106 || (rExtent
.nEnd
== pHint
->GetStart()
108 || *pHint
->GetEnd() != pHint
->GetStart())))
113 if (rExtent
.nStart
<= pHint
->GetStart())
117 *ppNode
= rExtent
.pNode
;
124 if (m_CurrentExtent
< m_pMerged
->extents
.size() &&
125 rExtent
.pNode
!= m_pMerged
->extents
[m_CurrentExtent
].pNode
)
127 m_CurrentHint
= 0; // reset
134 SwpHints
const*const pHints(m_pNode
->GetpSwpHints());
137 if (m_CurrentHint
< pHints
->Count())
139 SwTextAttr
const*const pHint(pHints
->Get(m_CurrentHint
));
152 MergedAttrIterByEnd::MergedAttrIterByEnd(SwTextFrame
const& rFrame
)
153 : m_pNode(rFrame
.GetMergedPara() ? nullptr : rFrame
.GetTextNodeFirst())
158 MergedAttrIterReverse
iter(rFrame
);
159 SwTextNode
const* pNode(nullptr);
160 while (SwTextAttr
const* pHint
= iter
.PrevAttr(&pNode
))
162 m_Hints
.emplace_back(pNode
, pHint
);
167 SwTextAttr
const* MergedAttrIterByEnd::NextAttr(SwTextNode
const*& rpNode
)
171 SwpHints
const*const pHints(m_pNode
->GetpSwpHints());
174 if (m_CurrentHint
< pHints
->Count())
176 SwTextAttr
const*const pHint(
177 pHints
->GetSortedByEnd(m_CurrentHint
));
187 if (m_CurrentHint
< m_Hints
.size())
189 auto const ret
= m_Hints
[m_Hints
.size() - m_CurrentHint
- 1];
198 void MergedAttrIterByEnd::PrevAttr()
200 assert(0 < m_CurrentHint
); // should only rewind as far as 0
204 MergedAttrIterReverse::MergedAttrIterReverse(SwTextFrame
const& rFrame
)
205 : MergedAttrIterBase(rFrame
)
209 m_CurrentExtent
= m_pMerged
->extents
.size();
210 SwpHints
const*const pHints(0 < m_CurrentExtent
211 ? m_pMerged
->extents
[m_CurrentExtent
-1].pNode
->GetpSwpHints()
215 pHints
->SortIfNeedBe();
216 m_CurrentHint
= pHints
->Count();
221 if (SwpHints
const*const pHints
= m_pNode
->GetpSwpHints())
223 pHints
->SortIfNeedBe();
224 m_CurrentHint
= pHints
->Count();
229 SwTextAttr
const* MergedAttrIterReverse::PrevAttr(SwTextNode
const** ppNode
)
233 while (0 < m_CurrentExtent
)
235 sw::Extent
const& rExtent(m_pMerged
->extents
[m_CurrentExtent
-1]);
236 if (SwpHints
const*const pHints
= rExtent
.pNode
->GetpSwpHints())
238 while (0 < m_CurrentHint
)
240 SwTextAttr
*const pHint(
241 pHints
->GetSortedByEnd(m_CurrentHint
- 1));
242 if (pHint
->GetAnyEnd() < rExtent
.nStart
243 // <= if it has end and isn't empty
245 && *pHint
->GetEnd() != pHint
->GetStart()
246 && *pHint
->GetEnd() == rExtent
.nStart
))
251 if (pHint
->GetAnyEnd() <= rExtent
.nEnd
)
255 *ppNode
= rExtent
.pNode
;
262 if (0 < m_CurrentExtent
&&
263 rExtent
.pNode
!= m_pMerged
->extents
[m_CurrentExtent
-1].pNode
)
265 SwpHints
const*const pHints(
266 m_pMerged
->extents
[m_CurrentExtent
-1].pNode
->GetpSwpHints());
267 m_CurrentHint
= pHints
? pHints
->Count() : 0; // reset
269 pHints
->SortIfNeedBe();
276 SwpHints
const*const pHints(m_pNode
->GetpSwpHints());
277 if (pHints
&& 0 < m_CurrentHint
)
279 SwTextAttr
const*const pHint(pHints
->GetSortedByEnd(m_CurrentHint
- 1));
291 bool FrameContainsNode(SwContentFrame
const& rFrame
, SwNodeOffset
const nNodeIndex
)
293 if (rFrame
.IsTextFrame())
295 SwTextFrame
const& rTextFrame(static_cast<SwTextFrame
const&>(rFrame
));
296 if (sw::MergedPara
const*const pMerged
= rTextFrame
.GetMergedPara())
298 SwNodeOffset
const nFirst(pMerged
->pFirstNode
->GetIndex());
299 SwNodeOffset
const nLast(pMerged
->pLastNode
->GetIndex());
300 return (nFirst
<= nNodeIndex
&& nNodeIndex
<= nLast
);
304 return rTextFrame
.GetTextNodeFirst()->GetIndex() == nNodeIndex
;
309 assert(rFrame
.IsNoTextFrame());
310 return static_cast<SwNoTextFrame
const&>(rFrame
).GetNode()->GetIndex() == nNodeIndex
;
314 bool IsParaPropsNode(SwRootFrame
const& rLayout
, SwTextNode
const& rNode
)
316 if (rLayout
.HasMergedParas())
318 if (SwTextFrame
const*const pFrame
= static_cast<SwTextFrame
*>(rNode
.getLayoutFrame(&rLayout
)))
320 sw::MergedPara
const*const pMerged(pFrame
->GetMergedPara());
321 if (pMerged
&& pMerged
->pParaPropsNode
!= &rNode
)
331 GetParaPropsNode(SwRootFrame
const& rLayout
, SwNode
const& rPos
)
333 const SwTextNode
*const pTextNode(rPos
.GetTextNode());
334 if (pTextNode
&& !sw::IsParaPropsNode(rLayout
, *pTextNode
))
336 return static_cast<SwTextFrame
*>(pTextNode
->getLayoutFrame(&rLayout
))->GetMergedPara()->pParaPropsNode
;
340 return const_cast<SwTextNode
*>(pTextNode
);
345 GetParaPropsPos(SwRootFrame
const& rLayout
, SwPosition
const& rPos
)
347 SwPosition
pos(rPos
);
348 SwTextNode
const*const pNode(pos
.GetNode().GetTextNode());
350 pos
.Assign( *sw::GetParaPropsNode(rLayout
, *pNode
) );
354 std::pair
<SwTextNode
*, SwTextNode
*>
355 GetFirstAndLastNode(SwRootFrame
const& rLayout
, SwNode
const& rPos
)
357 SwTextNode
*const pTextNode(const_cast<SwTextNode
*>(rPos
.GetTextNode()));
358 if (pTextNode
&& rLayout
.HasMergedParas())
360 if (SwTextFrame
const*const pFrame
= static_cast<SwTextFrame
*>(pTextNode
->getLayoutFrame(&rLayout
)))
362 if (sw::MergedPara
const*const pMerged
= pFrame
->GetMergedPara())
364 return std::make_pair(pMerged
->pFirstNode
, const_cast<SwTextNode
*>(pMerged
->pLastNode
));
368 return std::make_pair(pTextNode
, pTextNode
);
371 SwTextNode
const& GetAttrMerged(SfxItemSet
& rFormatSet
,
372 SwTextNode
const& rNode
, SwRootFrame
const*const pLayout
)
374 rNode
.SwContentNode::GetAttr(rFormatSet
);
375 if (pLayout
&& pLayout
->HasMergedParas())
377 auto pFrame
= static_cast<SwTextFrame
*>(rNode
.getLayoutFrame(pLayout
));
378 if (sw::MergedPara
const*const pMerged
= pFrame
? pFrame
->GetMergedPara() : nullptr)
380 if (pMerged
->pFirstNode
!= &rNode
)
382 rFormatSet
.ClearItem(RES_PAGEDESC
);
383 rFormatSet
.ClearItem(RES_BREAK
);
384 static_assert(RES_PAGEDESC
+ 1 == sal_uInt16(RES_BREAK
),
385 "first-node items must be adjacent");
386 SfxItemSetFixed
<RES_PAGEDESC
, RES_BREAK
> firstSet(*rFormatSet
.GetPool());
387 pMerged
->pFirstNode
->SwContentNode::GetAttr(firstSet
);
388 rFormatSet
.Put(firstSet
);
391 if (pMerged
->pParaPropsNode
!= &rNode
)
393 for (sal_uInt16 i
= RES_PARATR_BEGIN
; i
!= RES_FRMATR_END
; ++i
)
395 if (i
!= RES_PAGEDESC
&& i
!= RES_BREAK
)
397 rFormatSet
.ClearItem(i
);
400 for (sal_uInt16 i
= XATTR_FILL_FIRST
; i
<= XATTR_FILL_LAST
; ++i
)
402 rFormatSet
.ClearItem(i
);
404 SfxItemSetFixed
<RES_PARATR_BEGIN
, RES_PAGEDESC
,
405 RES_BREAK
+1, RES_FRMATR_END
,
406 XATTR_FILL_FIRST
, XATTR_FILL_LAST
+1>
407 propsSet(*rFormatSet
.GetPool());
408 pMerged
->pParaPropsNode
->SwContentNode::GetAttr(propsSet
);
409 rFormatSet
.Put(propsSet
);
410 return *pMerged
->pParaPropsNode
;
412 // keep all the CHRATR/UNKNOWNATR anyway...
420 /// Switches width and height of the text frame
421 void SwTextFrame::SwapWidthAndHeight()
424 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*this);
428 const tools::Long nPrtOfstX
= aPrt
.Pos().X();
429 aPrt
.Pos().setX( aPrt
.Pos().Y() );
433 aPrt
.Pos().setY( nPrtOfstX
);
437 aPrt
.Pos().setY( getFrameArea().Width() - ( nPrtOfstX
+ aPrt
.Width() ) );
442 const tools::Long nPrtOfstY
= aPrt
.Pos().Y();
443 aPrt
.Pos().setY( aPrt
.Pos().X() );
447 aPrt
.Pos().setX( nPrtOfstY
);
451 aPrt
.Pos().setX( getFrameArea().Height() - ( nPrtOfstY
+ aPrt
.Height() ) );
455 const tools::Long nPrtWidth
= aPrt
.Width();
456 aPrt
.Width( aPrt
.Height() );
457 aPrt
.Height( nPrtWidth
);
461 const tools::Long nFrameWidth
= getFrameArea().Width();
462 SwFrameAreaDefinition::FrameAreaWriteAccess
aFrm(*this);
463 aFrm
.Width( aFrm
.Height() );
464 aFrm
.Height( nFrameWidth
);
467 mbIsSwapped
= ! mbIsSwapped
;
471 * Calculates the coordinates of a rectangle when switching from
472 * horizontal to vertical layout.
474 void SwTextFrame::SwitchHorizontalToVertical( SwRect
& rRect
) const
476 // calc offset inside frame
477 tools::Long nOfstX
, nOfstY
;
482 // X and Y offsets here mean the position of the point that will be the top left corner
484 nOfstX
= rRect
.Left() + rRect
.Width() - getFrameArea().Left();
485 nOfstY
= rRect
.Top() - getFrameArea().Top();
489 nOfstX
= rRect
.Left() - getFrameArea().Left();
490 nOfstY
= rRect
.Top() - getFrameArea().Top();
495 nOfstX
= rRect
.Left() - getFrameArea().Left();
496 nOfstY
= rRect
.Top() + rRect
.Height() - getFrameArea().Top();
499 const tools::Long nWidth
= rRect
.Width();
500 const tools::Long nHeight
= rRect
.Height();
504 rRect
.Left(getFrameArea().Left() + nOfstY
);
509 rRect
.Left( getFrameArea().Left() + getFrameArea().Height() - nOfstY
);
512 rRect
.Left( getFrameArea().Left() + getFrameArea().Width() - nOfstY
);
518 rRect
.Top(getFrameArea().Top() + getFrameArea().Width() - nOfstX
);
520 rRect
.Top(getFrameArea().Top() + getFrameArea().Height() - nOfstX
);
523 rRect
.Top(getFrameArea().Top() + nOfstX
);
524 rRect
.Width( nHeight
);
525 rRect
.Height( nWidth
);
529 * Calculates the coordinates of a point when switching from
530 * horizontal to vertical layout.
532 void SwTextFrame::SwitchHorizontalToVertical( Point
& rPoint
) const
536 // The horizontal origo is the top left corner, the LRBT origo is the
537 // bottom left corner. Finally x and y has to be swapped.
538 SAL_WARN_IF(!mbIsSwapped
, "sw.core",
539 "SwTextFrame::SwitchHorizontalToVertical, IsVertLRBT, not swapped");
540 Point
aPoint(rPoint
);
541 rPoint
.setX(getFrameArea().Left() + (aPoint
.Y() - getFrameArea().Top()));
542 // This would be bottom - x delta, but bottom is top + height, finally
543 // width (and not height), as it's swapped.
544 rPoint
.setY(getFrameArea().Top() + getFrameArea().Width()
545 - (aPoint
.X() - getFrameArea().Left()));
549 // calc offset inside frame
550 const tools::Long nOfstX
= rPoint
.X() - getFrameArea().Left();
551 const tools::Long nOfstY
= rPoint
.Y() - getFrameArea().Top();
553 rPoint
.setX( getFrameArea().Left() + nOfstY
);
557 rPoint
.setX( getFrameArea().Left() + getFrameArea().Height() - nOfstY
);
559 // calc rotated coords
560 rPoint
.setX( getFrameArea().Left() + getFrameArea().Width() - nOfstY
);
563 rPoint
.setY( getFrameArea().Top() + nOfstX
);
567 * Calculates the a limit value when switching from
568 * horizontal to vertical layout.
570 tools::Long
SwTextFrame::SwitchHorizontalToVertical( tools::Long nLimit
) const
572 Point
aTmp( 0, nLimit
);
573 SwitchHorizontalToVertical( aTmp
);
578 * Calculates the coordinates of a rectangle when switching from
579 * vertical to horizontal layout.
581 void SwTextFrame::SwitchVerticalToHorizontal( SwRect
& rRect
) const
585 // calc offset inside frame
587 nOfstX
= rRect
.Left() - getFrameArea().Left();
591 nOfstX
= getFrameArea().Left() + getFrameArea().Height() - ( rRect
.Left() + rRect
.Width() );
593 nOfstX
= getFrameArea().Left() + getFrameArea().Width() - ( rRect
.Left() + rRect
.Width() );
599 // Note that mbIsSwapped only affects the frame area, not rRect, so rRect.Height() is used
600 // here unconditionally.
602 nOfstY
= getFrameArea().Top() + getFrameArea().Width() - (rRect
.Top() + rRect
.Height());
604 nOfstY
= getFrameArea().Top() + getFrameArea().Height() - (rRect
.Top() + rRect
.Height());
607 nOfstY
= rRect
.Top() - getFrameArea().Top();
608 const tools::Long nWidth
= rRect
.Height();
609 const tools::Long nHeight
= rRect
.Width();
611 // calc rotated coords
612 rRect
.Left( getFrameArea().Left() + nOfstY
);
613 rRect
.Top( getFrameArea().Top() + nOfstX
);
614 rRect
.Width( nWidth
);
615 rRect
.Height( nHeight
);
619 * Calculates the coordinates of a point when switching from
620 * vertical to horizontal layout.
622 void SwTextFrame::SwitchVerticalToHorizontal( Point
& rPoint
) const
626 // calc offset inside frame
628 // X offset is Y - left.
629 nOfstX
= rPoint
.X() - getFrameArea().Left();
632 // X offset is right - X.
634 nOfstX
= getFrameArea().Left() + getFrameArea().Height() - rPoint
.X();
636 nOfstX
= getFrameArea().Left() + getFrameArea().Width() - rPoint
.X();
642 // Y offset is bottom - Y.
644 nOfstY
= getFrameArea().Top() + getFrameArea().Width() - rPoint
.Y();
646 nOfstY
= getFrameArea().Top() + getFrameArea().Height() - rPoint
.Y();
649 // Y offset is Y - top.
650 nOfstY
= rPoint
.Y() - getFrameArea().Top();
652 // calc rotated coords
653 rPoint
.setX( getFrameArea().Left() + nOfstY
);
654 rPoint
.setY( getFrameArea().Top() + nOfstX
);
658 * Calculates the a limit value when switching from
659 * vertical to horizontal layout.
661 tools::Long
SwTextFrame::SwitchVerticalToHorizontal( tools::Long nLimit
) const
663 Point
aTmp( nLimit
, 0 );
664 SwitchVerticalToHorizontal( aTmp
);
668 SwFrameSwapper::SwFrameSwapper( const SwTextFrame
* pTextFrame
, bool bSwapIfNotSwapped
)
669 : pFrame( pTextFrame
), bUndo( false )
671 if (pFrame
->IsVertical() && bSwapIfNotSwapped
!= pFrame
->IsSwapped())
674 const_cast<SwTextFrame
*>(pFrame
)->SwapWidthAndHeight();
678 SwFrameSwapper::~SwFrameSwapper()
681 const_cast<SwTextFrame
*>(pFrame
)->SwapWidthAndHeight();
684 void SwTextFrame::SwitchLTRtoRTL( SwRect
& rRect
) const
686 SwSwapIfNotSwapped
swap(const_cast<SwTextFrame
*>(this));
688 tools::Long nWidth
= rRect
.Width();
689 rRect
.Left( 2 * ( getFrameArea().Left() + getFramePrintArea().Left() ) +
690 getFramePrintArea().Width() - rRect
.Right() - 1 );
692 rRect
.Width( nWidth
);
695 void SwTextFrame::SwitchLTRtoRTL( Point
& rPoint
) const
697 SwSwapIfNotSwapped
swap(const_cast<SwTextFrame
*>(this));
699 rPoint
.setX( 2 * ( getFrameArea().Left() + getFramePrintArea().Left() ) + getFramePrintArea().Width() - rPoint
.X() - 1 );
702 SwLayoutModeModifier::SwLayoutModeModifier( const OutputDevice
& rOutp
) :
703 m_rOut( rOutp
), m_nOldLayoutMode( rOutp
.GetLayoutMode() )
707 SwLayoutModeModifier::~SwLayoutModeModifier()
709 const_cast<OutputDevice
&>(m_rOut
).SetLayoutMode( m_nOldLayoutMode
);
712 void SwLayoutModeModifier::Modify( bool bChgToRTL
)
714 const_cast<OutputDevice
&>(m_rOut
).SetLayoutMode( bChgToRTL
?
715 vcl::text::ComplexTextLayoutFlags::BiDiStrong
| vcl::text::ComplexTextLayoutFlags::BiDiRtl
:
716 vcl::text::ComplexTextLayoutFlags::BiDiStrong
);
719 void SwLayoutModeModifier::SetAuto()
721 const vcl::text::ComplexTextLayoutFlags nNewLayoutMode
= m_nOldLayoutMode
& ~vcl::text::ComplexTextLayoutFlags::BiDiStrong
;
722 const_cast<OutputDevice
&>(m_rOut
).SetLayoutMode( nNewLayoutMode
);
725 SwDigitModeModifier::SwDigitModeModifier( const OutputDevice
& rOutp
, LanguageType eCurLang
,
726 SvtCTLOptions::TextNumerals eCTLTextNumerals
) :
727 rOut( rOutp
), nOldLanguageType( rOutp
.GetDigitLanguage() )
729 LanguageType eLang
= eCurLang
;
730 if (comphelper::IsFuzzing())
731 eLang
= LANGUAGE_ENGLISH_US
;
734 if ( SvtCTLOptions::NUMERALS_HINDI
== eCTLTextNumerals
)
735 eLang
= LANGUAGE_ARABIC_SAUDI_ARABIA
;
736 else if ( SvtCTLOptions::NUMERALS_ARABIC
== eCTLTextNumerals
)
737 eLang
= LANGUAGE_ENGLISH
;
738 else if ( SvtCTLOptions::NUMERALS_SYSTEM
== eCTLTextNumerals
)
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
);
796 void SwTextFrame::dumpAsXmlAttributes(xmlTextWriterPtr writer
) const
798 SwContentFrame::dumpAsXmlAttributes(writer
);
800 const SwTextNode
*pTextNode
= GetTextNodeFirst();
801 (void)xmlTextWriterWriteFormatAttribute( writer
, BAD_CAST( "txtNodeIndex" ), "%" SAL_PRIdINT32
, sal_Int32(pTextNode
->GetIndex()) );
803 OString aMode
= "Horizontal"_ostr
;
806 aMode
= "VertBTLR"_ostr
;
810 aMode
= "VertLR"_ostr
;
812 else if (IsVertical())
814 aMode
= "Vertical"_ostr
;
816 (void)xmlTextWriterWriteAttribute(writer
, BAD_CAST("WritingMode"), BAD_CAST(aMode
.getStr()));
819 void SwTextFrame::dumpAsXml(xmlTextWriterPtr writer
) const
821 (void)xmlTextWriterStartElement(writer
, reinterpret_cast<const xmlChar
*>("txt"));
822 dumpAsXmlAttributes( writer
);
824 (void)xmlTextWriterWriteFormatAttribute( writer
, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32
, GetFollow()->GetFrameId() );
826 if (m_pPrecede
!= nullptr)
827 (void)xmlTextWriterWriteFormatAttribute( writer
, BAD_CAST( "precede" ), "%" SAL_PRIuUINT32
, static_cast<SwTextFrame
*>(m_pPrecede
)->GetFrameId() );
829 (void)xmlTextWriterWriteAttribute(writer
, BAD_CAST("offset"), BAD_CAST(OString::number(static_cast<sal_Int32
>(mnOffset
)).getStr()));
831 sw::MergedPara
const*const pMerged(GetMergedPara());
834 (void)xmlTextWriterStartElement( writer
, BAD_CAST( "merged" ) );
835 (void)xmlTextWriterWriteFormatAttribute( writer
, BAD_CAST( "paraPropsNodeIndex" ), "%" SAL_PRIdINT32
, sal_Int32(pMerged
->pParaPropsNode
->GetIndex()) );
836 for (auto const& e
: pMerged
->extents
)
838 (void)xmlTextWriterStartElement( writer
, BAD_CAST( "extent" ) );
839 (void)xmlTextWriterWriteFormatAttribute( writer
, BAD_CAST( "txtNodeIndex" ), "%" SAL_PRIdINT32
, sal_Int32(e
.pNode
->GetIndex()) );
840 (void)xmlTextWriterWriteFormatAttribute( writer
, BAD_CAST( "start" ), "%" SAL_PRIdINT32
, e
.nStart
);
841 (void)xmlTextWriterWriteFormatAttribute( writer
, BAD_CAST( "end" ), "%" SAL_PRIdINT32
, e
.nEnd
);
842 (void)xmlTextWriterEndElement( writer
);
844 (void)xmlTextWriterEndElement( writer
);
847 (void)xmlTextWriterStartElement(writer
, BAD_CAST("infos"));
848 dumpInfosAsXml(writer
);
849 (void)xmlTextWriterEndElement(writer
);
851 // Dump Anchored objects if any
852 const SwSortedObjs
* pAnchored
= GetDrawObjs();
853 if ( pAnchored
&& pAnchored
->size() > 0 )
855 (void)xmlTextWriterStartElement( writer
, BAD_CAST( "anchored" ) );
857 for (SwAnchoredObject
* pObject
: *pAnchored
)
859 pObject
->dumpAsXml( writer
);
862 (void)xmlTextWriterEndElement( writer
);
866 OUString aText
= GetText( );
867 for ( int i
= 0; i
< 32; i
++ )
869 aText
= aText
.replace( i
, '*' );
871 auto nTextOffset
= static_cast<sal_Int32
>(GetOffset());
872 sal_Int32 nTextLength
= aText
.getLength() - nTextOffset
;
873 if (const SwTextFrame
* pTextFrameFollow
= GetFollow())
875 nTextLength
= static_cast<sal_Int32
>(pTextFrameFollow
->GetOffset() - GetOffset());
880 = OUStringToOString(aText
.subView(nTextOffset
, nTextLength
), RTL_TEXTENCODING_UTF8
);
881 (void)xmlTextWriterWriteString( writer
,
882 reinterpret_cast<const xmlChar
*>(aText8
.getStr( )) );
884 if (const SwParaPortion
* pPara
= GetPara())
886 (void)xmlTextWriterStartElement(writer
, BAD_CAST("SwParaPortion"));
887 TextFrameIndex
nOffset(0);
888 const OUString
& rText
= GetText();
889 (void)xmlTextWriterWriteFormatAttribute(writer
, BAD_CAST("ptr"), "%p", pPara
);
890 const SwLineLayout
* pLine
= pPara
;
893 nOffset
+= GetOffset();
897 (void)xmlTextWriterStartElement(writer
, BAD_CAST("SwLineLayout"));
898 pLine
->dumpAsXmlAttributes(writer
, rText
, nOffset
);
899 const SwLinePortion
* pPor
= pLine
->GetFirstPortion();
902 pPor
->dumpAsXml(writer
, rText
, nOffset
);
903 pPor
= pPor
->GetNextPortion();
905 (void)xmlTextWriterEndElement(writer
);
906 pLine
= pLine
->GetNext();
908 (void)xmlTextWriterEndElement(writer
);
911 (void)xmlTextWriterEndElement(writer
);
916 SwTextFrame
* MakeTextFrame(SwTextNode
& rNode
, SwFrame
*const pSibling
,
917 sw::FrameMode
const eMode
)
919 return new SwTextFrame(&rNode
, pSibling
, eMode
);
922 void RemoveFootnotesForNode(
923 SwRootFrame
const& rLayout
, SwTextNode
const& rTextNode
,
924 std::vector
<std::pair
<sal_Int32
, sal_Int32
>> const*const pExtents
)
926 if (pExtents
&& pExtents
->empty())
928 return; // nothing to do
930 const SwFootnoteIdxs
&rFootnoteIdxs
= rTextNode
.GetDoc().GetFootnoteIdxs();
932 SwNodeOffset
const nIndex
= rTextNode
.GetIndex();
933 rFootnoteIdxs
.SeekEntry( rTextNode
, &nPos
);
934 if (nPos
< rFootnoteIdxs
.size())
936 while (nPos
> 0 && rTextNode
== (rFootnoteIdxs
[ nPos
]->GetTextNode()))
938 if (nPos
|| rTextNode
!= (rFootnoteIdxs
[ nPos
]->GetTextNode()))
942 for ( ; nPos
< rFootnoteIdxs
.size(); ++nPos
)
944 SwTextFootnote
* pTextFootnote
= rFootnoteIdxs
[ nPos
];
945 if (pTextFootnote
->GetTextNode().GetIndex() > nIndex
)
949 while ((*pExtents
)[iter
].second
<= pTextFootnote
->GetStart())
952 if (iter
== pExtents
->size())
957 if (pTextFootnote
->GetStart() < (*pExtents
)[iter
].first
)
962 pTextFootnote
->DelFrames(&rLayout
);
968 void SwTextFrame::DestroyImpl()
970 // Remove associated SwParaPortion from s_pTextCache
973 assert(!GetDoc().IsInDtor()); // this shouldn't be happening with ViewShell owning layout
974 if (!GetDoc().IsInDtor() && HasFootnote())
978 SwTextNode
const* pNode(nullptr);
979 for (auto const& e
: m_pMergedPara
->extents
)
981 if (e
.pNode
!= pNode
)
984 // sw_redlinehide: not sure if it's necessary to check
985 // if the nodes are still alive here, which would require
986 // accessing WriterMultiListener::m_vDepends
987 sw::RemoveFootnotesForNode(*getRootFrame(), *pNode
, nullptr);
993 SwTextNode
*const pNode(static_cast<SwTextNode
*>(GetDep()));
996 sw::RemoveFootnotesForNode(*getRootFrame(), *pNode
, nullptr);
1001 if (!GetDoc().IsInDtor())
1003 if (SwView
* pView
= GetActiveView())
1004 pView
->GetEditWin().GetFrameControlsManager().RemoveControls(this);
1007 SwContentFrame::DestroyImpl();
1010 SwTextFrame::~SwTextFrame()
1017 // 1. if real insert => correct nStart/nEnd for full nLen
1018 // 2. if rl un-delete => do not correct nStart/nEnd but just include un-deleted
1019 static TextFrameIndex
UpdateMergedParaForInsert(MergedPara
& rMerged
,
1020 bool const isRealInsert
,
1021 SwTextNode
const& rNode
, sal_Int32
const nIndex
, sal_Int32
const nLen
)
1023 assert(!isRealInsert
|| nLen
); // can 0 happen? yes, for redline in empty node
1024 assert(nIndex
<= rNode
.Len());
1025 assert(nIndex
+ nLen
<= rNode
.Len());
1026 assert(rMerged
.pFirstNode
->GetIndex() <= rNode
.GetIndex() && rNode
.GetIndex() <= rMerged
.pLastNode
->GetIndex());
1029 return TextFrameIndex(0);
1031 OUStringBuffer
text(rMerged
.mergedText
);
1032 sal_Int32
nTFIndex(0); // index used for insertion at the end
1033 sal_Int32
nInserted(0);
1034 bool bInserted(false);
1035 bool bFoundNode(false);
1036 auto itInsert(rMerged
.extents
.end());
1037 for (auto it
= rMerged
.extents
.begin(); it
!= rMerged
.extents
.end(); ++it
)
1039 if (it
->pNode
== &rNode
)
1044 if (it
->nStart
<= nIndex
&& nIndex
<= it
->nEnd
)
1045 { // note: this can happen only once
1046 text
.insert(nTFIndex
+ (nIndex
- it
->nStart
),
1047 rNode
.GetText().subView(nIndex
, nLen
));
1053 else if (nIndex
< it
->nStart
)
1055 if (itInsert
== rMerged
.extents
.end())
1065 assert(it
== rMerged
.extents
.begin() || (it
-1)->pNode
!= &rNode
|| (it
-1)->nEnd
< nIndex
);
1066 if (nIndex
+ nLen
< it
->nStart
)
1071 if (nIndex
< it
->nStart
)
1073 text
.insert(nTFIndex
,
1074 rNode
.GetText().subView(nIndex
, it
->nStart
- nIndex
));
1075 nInserted
+= it
->nStart
- nIndex
;
1076 it
->nStart
= nIndex
;
1079 assert(it
->nStart
<= nIndex
);
1080 if (nIndex
<= it
->nEnd
)
1082 nTFIndex
+= it
->nEnd
- it
->nStart
;
1083 while (it
->nEnd
< nIndex
+ nLen
)
1086 (it
+1) != rMerged
.extents
.end() && (it
+1)->pNode
== it
->pNode
1089 if (pNext
&& pNext
->nStart
<= nIndex
+ nLen
)
1091 text
.insert(nTFIndex
,
1092 rNode
.GetText().subView(it
->nEnd
, pNext
->nStart
- it
->nEnd
));
1093 nTFIndex
+= pNext
->nStart
- it
->nEnd
;
1094 nInserted
+= pNext
->nStart
- it
->nEnd
;
1095 pNext
->nStart
= it
->nStart
;
1096 it
= rMerged
.extents
.erase(it
);
1100 text
.insert(nTFIndex
,
1101 rNode
.GetText().subView(it
->nEnd
, nIndex
+ nLen
- it
->nEnd
));
1102 nTFIndex
+= nIndex
+ nLen
- it
->nEnd
;
1103 nInserted
+= nIndex
+ nLen
- it
->nEnd
;
1104 it
->nEnd
= nIndex
+ nLen
;
1112 else if (rNode
.GetIndex() < it
->pNode
->GetIndex() || bFoundNode
)
1114 if (itInsert
== rMerged
.extents
.end())
1120 if (itInsert
== rMerged
.extents
.end())
1122 nTFIndex
+= it
->nEnd
- it
->nStart
;
1125 // assert((bFoundNode || rMerged.extents.empty()) && "text node not found - why is it sending hints to us");
1127 { // must be in a gap
1128 rMerged
.extents
.emplace(itInsert
, const_cast<SwTextNode
*>(&rNode
), nIndex
, nIndex
+ nLen
);
1129 text
.insert(nTFIndex
, rNode
.GetText().subView(nIndex
, nLen
));
1131 if (rMerged
.extents
.size() == 1 // also if it was empty!
1132 || rMerged
.pParaPropsNode
->GetIndex() < rNode
.GetIndex())
1133 { // text inserted after current para-props node
1134 rMerged
.pParaPropsNode
->RemoveFromListRLHidden();
1135 rMerged
.pParaPropsNode
= &const_cast<SwTextNode
&>(rNode
);
1136 rMerged
.pParaPropsNode
->AddToListRLHidden();
1138 // called from SwRangeRedline::InvalidateRange()
1139 if (rNode
.GetRedlineMergeFlag() == SwNode::Merge::Hidden
)
1141 const_cast<SwTextNode
&>(rNode
).SetRedlineMergeFlag(SwNode::Merge::NonFirst
);
1144 rMerged
.mergedText
= text
.makeStringAndClear();
1145 return TextFrameIndex(nInserted
);
1148 // 1. if real delete => correct nStart/nEnd for full nLen
1149 // 2. if rl delete => do not correct nStart/nEnd but just exclude deleted
1150 TextFrameIndex
UpdateMergedParaForDelete(MergedPara
& rMerged
,
1151 bool const isRealDelete
,
1152 SwTextNode
const& rNode
, sal_Int32 nIndex
, sal_Int32
const nLen
)
1154 assert(nIndex
<= rNode
.Len());
1155 assert(rMerged
.pFirstNode
->GetIndex() <= rNode
.GetIndex() && rNode
.GetIndex() <= rMerged
.pLastNode
->GetIndex());
1156 OUStringBuffer
text(rMerged
.mergedText
);
1157 sal_Int32
nTFIndex(0);
1158 sal_Int32
nToDelete(nLen
);
1159 sal_Int32
nDeleted(0);
1160 size_t nFoundNode(0);
1162 auto it
= rMerged
.extents
.begin();
1163 for (; it
!= rMerged
.extents
.end(); )
1166 if (it
->pNode
== &rNode
)
1169 if (nIndex
+ nToDelete
< it
->nStart
)
1181 if (nIndex
< it
->nStart
)
1183 // do not adjust nIndex into the text frame index space!
1184 nToDelete
-= it
->nStart
- nIndex
;
1185 nIndex
= it
->nStart
;
1186 // note: continue with the if check below, no else!
1188 if (it
->nStart
<= nIndex
&& nIndex
< it
->nEnd
)
1190 sal_Int32
const nDeleteHere(nIndex
+ nToDelete
<= it
->nEnd
1192 : it
->nEnd
- nIndex
);
1193 text
.remove(nTFIndex
+ (nIndex
- it
->nStart
), nDeleteHere
);
1194 bErase
= nDeleteHere
== it
->nEnd
- it
->nStart
;
1198 assert(it
->nStart
== nIndex
);
1199 it
= rMerged
.extents
.erase(it
);
1201 else if (isRealDelete
)
1202 { // adjust for deleted text
1203 it
->nStart
-= (nLen
- nToDelete
);
1204 it
->nEnd
-= (nLen
- nToDelete
+ nDeleteHere
);
1205 if (it
!= rMerged
.extents
.begin()
1206 && (it
-1)->pNode
== &rNode
1207 && (it
-1)->nEnd
== it
->nStart
)
1208 { // merge adjacent extents
1209 nTFIndex
+= it
->nEnd
- it
->nStart
;
1210 (it
-1)->nEnd
= it
->nEnd
;
1211 it
= rMerged
.extents
.erase(it
);
1212 bErase
= true; // skip increment
1216 { // exclude text marked as deleted
1217 if (nIndex
+ nDeleteHere
== it
->nEnd
)
1219 it
->nEnd
-= nDeleteHere
;
1223 if (nIndex
== it
->nStart
)
1225 it
->nStart
+= nDeleteHere
;
1229 sal_Int32
const nOldEnd(it
->nEnd
);
1231 it
= rMerged
.extents
.emplace(it
+1,
1232 it
->pNode
, nIndex
+ nDeleteHere
, nOldEnd
);
1234 assert(nDeleteHere
== nToDelete
);
1237 nDeleted
+= nDeleteHere
;
1238 nToDelete
-= nDeleteHere
;
1239 nIndex
+= nDeleteHere
;
1240 if (!isRealDelete
&& nToDelete
== 0)
1247 else if (nFoundNode
!= 0)
1253 nTFIndex
+= it
->nEnd
- it
->nStart
;
1257 // assert(nFoundNode != 0 && "text node not found - why is it sending hints to us");
1258 assert(nIndex
<= rNode
.Len() + nLen
);
1259 // if there's a remaining deletion, it must be in gap at the end of the node
1260 // can't do: might be last one in node was erased assert(nLen == 0 || rMerged.empty() || (it-1)->nEnd <= nIndex);
1261 // note: if first node gets deleted then that must call DelFrames as
1262 // pFirstNode is never updated
1263 if (nErased
&& nErased
== nFoundNode
)
1264 { // all visible text from node was erased
1266 if (rMerged
.pParaPropsNode
== &rNode
)
1268 rMerged
.pParaPropsNode
->RemoveFromListRLHidden();
1269 rMerged
.pParaPropsNode
= rMerged
.extents
.empty()
1270 ? const_cast<SwTextNode
*>(rMerged
.pLastNode
)
1271 : rMerged
.extents
.front().pNode
;
1272 rMerged
.pParaPropsNode
->AddToListRLHidden();
1275 // NOPE must listen on all non-hidden nodes; particularly on pLastNode rMerged.listener.EndListening(&const_cast<SwTextNode&>(rNode));
1277 rMerged
.mergedText
= text
.makeStringAndClear();
1278 return TextFrameIndex(nDeleted
);
1281 std::pair
<SwTextNode
*, sal_Int32
>
1282 MapViewToModel(MergedPara
const& rMerged
, TextFrameIndex
const i_nIndex
)
1284 sal_Int32
nIndex(i_nIndex
);
1285 sw::Extent
const* pExtent(nullptr);
1286 for (const auto& rExt
: rMerged
.extents
)
1289 if (nIndex
< (pExtent
->nEnd
- pExtent
->nStart
))
1291 return std::make_pair(pExtent
->pNode
, pExtent
->nStart
+ nIndex
);
1293 nIndex
= nIndex
- (pExtent
->nEnd
- pExtent
->nStart
);
1295 assert(nIndex
== 0 && "view index out of bounds");
1297 ? std::make_pair(pExtent
->pNode
, pExtent
->nEnd
) //1-past-the-end index
1298 : std::make_pair(const_cast<SwTextNode
*>(rMerged
.pLastNode
), rMerged
.pLastNode
->Len());
1301 TextFrameIndex
MapModelToView(MergedPara
const& rMerged
, SwTextNode
const*const pNode
, sal_Int32
const nIndex
)
1303 assert(rMerged
.pFirstNode
->GetIndex() <= pNode
->GetIndex()
1304 && pNode
->GetIndex() <= rMerged
.pLastNode
->GetIndex());
1306 bool bFoundNode(false);
1307 for (auto const& e
: rMerged
.extents
)
1309 if (pNode
->GetIndex() < e
.pNode
->GetIndex())
1311 return TextFrameIndex(nRet
);
1313 if (e
.pNode
== pNode
)
1315 if (e
.nStart
<= nIndex
&& nIndex
<= e
.nEnd
)
1317 return TextFrameIndex(nRet
+ (nIndex
- e
.nStart
));
1319 else if (nIndex
< e
.nStart
)
1321 // in gap before this extent => map to 0 here TODO???
1322 return TextFrameIndex(nRet
);
1326 else if (bFoundNode
)
1330 nRet
+= e
.nEnd
- e
.nStart
;
1334 // must be in a gap at the end of the node
1335 assert(nIndex
<= pNode
->Len());
1336 return TextFrameIndex(nRet
);
1338 else if (rMerged
.extents
.empty())
1340 assert(nIndex
<= pNode
->Len());
1341 return TextFrameIndex(0);
1343 return TextFrameIndex(rMerged
.mergedText
.getLength());
1348 std::pair
<SwTextNode
*, sal_Int32
>
1349 SwTextFrame::MapViewToModel(TextFrameIndex
const nIndex
) const
1351 //nope assert(GetPara());
1352 sw::MergedPara
const*const pMerged(GetMergedPara());
1355 return sw::MapViewToModel(*pMerged
, nIndex
);
1359 return std::make_pair(static_cast<SwTextNode
*>(const_cast<sw::BroadcastingModify
*>(
1360 SwFrame::GetDep())), sal_Int32(nIndex
));
1364 SwPosition
SwTextFrame::MapViewToModelPos(TextFrameIndex
const nIndex
) const
1366 std::pair
<SwTextNode
*, sal_Int32
> const ret(MapViewToModel(nIndex
));
1367 return SwPosition(*ret
.first
, ret
.second
);
1370 TextFrameIndex
SwTextFrame::MapModelToView(SwTextNode
const*const pNode
, sal_Int32
const nIndex
) const
1372 //nope assert(GetPara());
1373 sw::MergedPara
const*const pMerged(GetMergedPara());
1376 return sw::MapModelToView(*pMerged
, pNode
, nIndex
);
1380 return TextFrameIndex(nIndex
);
1384 TextFrameIndex
SwTextFrame::MapModelToViewPos(SwPosition
const& rPos
) const
1386 SwTextNode
const*const pNode(rPos
.GetNode().GetTextNode());
1387 sal_Int32
const nIndex(rPos
.GetContentIndex());
1388 return MapModelToView(pNode
, nIndex
);
1391 void SwTextFrame::SetMergedPara(std::unique_ptr
<sw::MergedPara
> p
)
1393 SwTextNode
*const pFirst(m_pMergedPara
? m_pMergedPara
->pFirstNode
: nullptr);
1394 m_pMergedPara
= std::move(p
);
1399 assert(pFirst
== m_pMergedPara
->pFirstNode
);
1403 pFirst
->Add(*this); // must register at node again
1406 // postcondition: frame must be listening somewhere
1407 assert(m_pMergedPara
|| GetDep());
1410 const OUString
& SwTextFrame::GetText() const
1412 //nope assert(GetPara());
1413 sw::MergedPara
const*const pMerged(GetMergedPara());
1415 return pMerged
->mergedText
;
1417 return static_cast<SwTextNode
const*>(SwFrame::GetDep())->GetText();
1420 SwTextNode
const* SwTextFrame::GetTextNodeForParaProps() const
1422 // FIXME can GetPara be 0 ? yes... this is needed in SwContentNotify::SwContentNotify() which is called before any formatting is started
1423 //nope assert(GetPara());
1424 sw::MergedPara
const*const pMerged(GetMergedPara());
1427 // assert(pMerged->pFirstNode == pMerged->pParaPropsNode); // surprising news!
1428 return pMerged
->pParaPropsNode
;
1431 return static_cast<SwTextNode
const*>(SwFrame::GetDep());
1434 SwTextNode
const* SwTextFrame::GetTextNodeForFirstText() const
1436 sw::MergedPara
const*const pMerged(GetMergedPara());
1438 return pMerged
->extents
.empty()
1439 ? pMerged
->pFirstNode
1440 : pMerged
->extents
.front().pNode
;
1442 return static_cast<SwTextNode
const*>(SwFrame::GetDep());
1445 SwTextNode
const* SwTextFrame::GetTextNodeFirst() const
1447 //nope assert(GetPara());
1448 sw::MergedPara
const*const pMerged(GetMergedPara());
1450 return pMerged
->pFirstNode
;
1452 return static_cast<SwTextNode
const*>(SwFrame::GetDep());
1455 SwDoc
const& SwTextFrame::GetDoc() const
1457 return GetTextNodeFirst()->GetDoc();
1460 LanguageType
SwTextFrame::GetLangOfChar(TextFrameIndex
const nIndex
,
1461 sal_uInt16
const nScript
, bool const bNoChar
, bool const bNoneIfNoHyphenation
) const
1463 // a single character can be mapped uniquely!
1464 std::pair
<SwTextNode
const*, sal_Int32
> const pos(MapViewToModel(nIndex
));
1465 return pos
.first
->GetLang(pos
.second
, bNoChar
? 0 : 1, nScript
, bNoneIfNoHyphenation
);
1468 void SwTextFrame::ResetPreps()
1470 if ( GetCacheIdx() != USHRT_MAX
)
1472 if (SwParaPortion
*pPara
= GetPara())
1473 pPara
->ResetPreps();
1477 static auto FindCellFrame(SwFrame
const* pLower
) -> SwLayoutFrame
const*
1481 if (pLower
->IsCellFrame())
1483 return static_cast<SwLayoutFrame
const*>(pLower
);
1485 pLower
= pLower
->GetUpper();
1490 bool SwTextFrame::IsHiddenNow() const
1492 SwFrameSwapper
aSwapper( this, true );
1494 if( !getFrameArea().Width() && isFrameAreaDefinitionValid() && GetUpper()->isFrameAreaDefinitionValid() ) // invalid when stack overflows (StackHack)!
1496 // OSL_FAIL( "SwTextFrame::IsHiddenNow: thin frame" );
1500 // TODO: what is the above check good for and can it be removed?
1501 return IsHiddenNowImpl();
1504 bool SwTextFrame::IsHiddenNowImpl() const
1506 if (SwContentFrame::IsHiddenNow())
1509 bool bHiddenCharsHidePara(false);
1510 bool bHiddenParaField(false);
1513 TextFrameIndex
nHiddenStart(COMPLETE_STRING
);
1514 TextFrameIndex
nHiddenEnd(0);
1515 if (auto const pScriptInfo
= GetScriptInfo())
1517 pScriptInfo
->GetBoundsOfHiddenRange(TextFrameIndex(0),
1518 nHiddenStart
, nHiddenEnd
);
1520 else // ParaPortion is created in Format, but this is called earlier
1523 aInfo
.InitScriptInfo(*m_pMergedPara
->pFirstNode
, m_pMergedPara
.get(), IsRightToLeft());
1524 aInfo
.GetBoundsOfHiddenRange(TextFrameIndex(0),
1525 nHiddenStart
, nHiddenEnd
);
1527 if (TextFrameIndex(0) == nHiddenStart
&&
1528 TextFrameIndex(GetText().getLength()) <= nHiddenEnd
)
1530 bHiddenCharsHidePara
= true;
1532 sw::MergedAttrIter
iter(*this);
1533 SwTextNode
const* pNode(nullptr);
1534 int nNewResultWeight
= 0;
1535 for (SwTextAttr
const* pHint
= iter
.NextAttr(&pNode
); pHint
; pHint
= iter
.NextAttr(&pNode
))
1537 if (pHint
->Which() == RES_TXTATR_FIELD
)
1539 // see also SwpHints::CalcHiddenParaField()
1540 const SwFormatField
& rField
= pHint
->GetFormatField();
1541 int nCurWeight
= pNode
->GetDoc().FieldCanHideParaWeight(rField
.GetField()->GetTyp()->Which());
1542 if (nCurWeight
> nNewResultWeight
)
1544 nNewResultWeight
= nCurWeight
;
1545 bHiddenParaField
= pNode
->GetDoc().FieldHidesPara(*rField
.GetField());
1547 else if (nCurWeight
== nNewResultWeight
&& bHiddenParaField
)
1549 // Currently, for both supported hiding types (HiddenPara, Database), "Don't hide"
1550 // takes precedence - i.e., if there's a "Don't hide" field of that weight, we only
1551 // care about fields of higher weight.
1552 bHiddenParaField
= pNode
->GetDoc().FieldHidesPara(*rField
.GetField());
1559 bHiddenCharsHidePara
= static_cast<SwTextNode
const*>(SwFrame::GetDep())->HasHiddenCharAttribute( true );
1560 bHiddenParaField
= static_cast<SwTextNode
const*>(SwFrame::GetDep())->IsHiddenByParaField();
1562 if (bHiddenCharsHidePara
&& GetDoc().getIDocumentSettingAccess().get(
1563 DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH
))
1565 // apparently in Word it's always the last para marker that determines hidden?
1566 // even in case when they are merged by delete redline (it's obvious when they are merged by hidden-attribute
1567 SwTextNode
const*const pNode
{ m_pMergedPara
1568 ? m_pMergedPara
->pLastNode
1569 : static_cast<SwTextNode
const*>(SwFrame::GetDep()) };
1570 // Word ignores hidden formatting on the cell end marker
1571 bool isLastInCell
{false};
1572 if (SwLayoutFrame
const*const pCellFrame
{FindCellFrame(this)})
1574 SwContentFrame
const* pNext
{GetNextContentFrame()};
1575 // skip frame in hidden section ("this" is *not* in hidden section!)
1576 while (pNext
&& pNext
->SwContentFrame::IsHiddenNow())
1578 pNext
= pNext
->GetNextContentFrame();
1580 isLastInCell
= pNext
== nullptr || !pCellFrame
->IsAnLower(pNext
);
1584 SwFormatAutoFormat
const& rListAutoFormat
{pNode
->GetAttr(RES_PARATR_LIST_AUTOFMT
)};
1585 std::shared_ptr
<SfxItemSet
> const pSet
{rListAutoFormat
.GetStyleHandle()};
1586 SvxCharHiddenItem
const* pItem
{pSet
? pSet
->GetItemIfSet(RES_CHRATR_HIDDEN
) : nullptr};
1589 // don't use node's mpAttrSet, it doesn't apply to para marker
1590 SwFormatColl
const*const pStyle
{pNode
->GetFormatColl()};
1593 pItem
= &pStyle
->GetFormatAttr(RES_CHRATR_HIDDEN
);
1596 if (!pItem
|| !pItem
->GetValue())
1598 bHiddenCharsHidePara
= false;
1602 const SwViewShell
* pVsh
= getRootFrame()->GetCurrShell();
1604 if ( pVsh
&& ( bHiddenCharsHidePara
|| bHiddenParaField
) )
1608 ( bHiddenParaField
&&
1609 ( !pVsh
->GetViewOptions()->IsShowHiddenPara() &&
1610 !pVsh
->GetViewOptions()->IsFieldName() ) ) ||
1611 ( bHiddenCharsHidePara
&&
1612 !pVsh
->GetViewOptions()->IsShowHiddenChar() ) )
1614 // in order to put the cursor in the body text, one paragraph must
1615 // be visible - check this for the 1st body paragraph
1616 if (IsInDocBody() && FindPrevCnt() == nullptr)
1618 for (SwContentFrame
const* pNext
= FindNextCnt(true);
1619 pNext
!= nullptr; pNext
= pNext
->FindNextCnt(true))
1621 if (!pNext
->IsHiddenNow())
1624 SAL_INFO("sw.core", "unhiding one body paragraph");
1634 /// Removes Textfrm's attachments, when it's hidden
1635 void SwTextFrame::HideHidden()
1637 OSL_ENSURE( !GetFollow() && IsHiddenNow(),
1638 "HideHidden on visible frame of hidden frame has follow" );
1640 HideFootnotes(GetOffset(), TextFrameIndex(COMPLETE_STRING
));
1641 HideAndShowObjects();
1643 // format information is obsolete
1647 void SwTextFrame::HideFootnotes(TextFrameIndex
const nStart
, TextFrameIndex
const nEnd
)
1649 SwPageFrame
*pPage
= nullptr;
1650 sw::MergedAttrIter
iter(*this);
1651 SwTextNode
const* pNode(nullptr);
1652 for (SwTextAttr
const* pHt
= iter
.NextAttr(&pNode
); pHt
; pHt
= iter
.NextAttr(&pNode
))
1654 if (pHt
->Which() == RES_TXTATR_FTN
)
1656 TextFrameIndex
const nIdx(MapModelToView(pNode
, pHt
->GetStart()));
1662 pPage
= FindPageFrame();
1663 pPage
->RemoveFootnote( this, static_cast<const SwTextFootnote
*>(pHt
) );
1670 * as-character anchored graphics, which are used for a graphic bullet list.
1671 * As long as these graphic bullet list aren't imported, do not hide a
1672 * at-character anchored object, if
1673 * (a) the document is an imported WW8 document -
1674 * checked by checking certain compatibility options -
1675 * (b) the paragraph is the last content in the document and
1676 * (c) the anchor character is an as-character anchored graphic.
1678 bool sw_HideObj( const SwTextFrame
& _rFrame
,
1679 const RndStdIds _eAnchorType
,
1680 SwFormatAnchor
const& rFormatAnchor
,
1681 SwAnchoredObject
* _pAnchoredObj
)
1685 if (_eAnchorType
== RndStdIds::FLY_AT_CHAR
)
1687 const IDocumentSettingAccess
*const pIDSA
= &_rFrame
.GetDoc().getIDocumentSettingAccess();
1688 if ( !pIDSA
->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING
) &&
1689 !pIDSA
->get(DocumentSettingId::OLD_LINE_SPACING
) &&
1690 !pIDSA
->get(DocumentSettingId::USE_FORMER_OBJECT_POS
) &&
1691 pIDSA
->get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION
) &&
1692 _rFrame
.IsInDocBody() && !_rFrame
.FindNextCnt() )
1694 SwTextNode
const& rNode(*rFormatAnchor
.GetAnchorNode()->GetTextNode());
1695 assert(FrameContainsNode(_rFrame
, rNode
.GetIndex()));
1696 sal_Int32
const nObjAnchorPos(rFormatAnchor
.GetAnchorContentOffset());
1697 const sal_Unicode cAnchorChar
= nObjAnchorPos
< rNode
.Len()
1698 ? rNode
.GetText()[nObjAnchorPos
]
1700 if (cAnchorChar
== CH_TXTATR_BREAKWORD
)
1702 const SwTextAttr
* const pHint(
1703 rNode
.GetTextAttrForCharAt(nObjAnchorPos
, RES_TXTATR_FLYCNT
));
1706 const SwFrameFormat
* pFrameFormat
=
1707 static_cast<const SwTextFlyCnt
*>(pHint
)->GetFlyCnt().GetFrameFormat();
1708 if ( pFrameFormat
->Which() == RES_FLYFRMFMT
)
1710 SwNodeIndex nContentIndex
= *(pFrameFormat
->GetContent().GetContentIdx());
1712 if ( nContentIndex
.GetNode().IsNoTextNode() )
1715 // set needed data structure values for object positioning
1716 SwRectFnSet
aRectFnSet(&_rFrame
);
1717 SwRect
aLastCharRect( _rFrame
.getFrameArea() );
1718 aRectFnSet
.SetWidth( aLastCharRect
, 1 );
1719 _pAnchoredObj
->maLastCharRect
= aLastCharRect
;
1720 _pAnchoredObj
->mnLastTopOfLine
= aRectFnSet
.GetTop(aLastCharRect
);
1734 * Method hides respectively shows objects, which are anchored at paragraph,
1735 * at/as a character of the paragraph, corresponding to the paragraph and
1736 * paragraph portion visibility.
1738 * - is called from HideHidden() - should hide objects in hidden paragraphs and
1739 * - from Format_() - should hide/show objects in partly visible paragraphs
1741 void SwTextFrame::HideAndShowObjects()
1743 if ( GetDrawObjs() )
1745 if ( IsHiddenNow() )
1747 // complete paragraph is hidden. Thus, hide all objects
1748 for (SwAnchoredObject
* i
: *GetDrawObjs())
1750 SdrObject
* pObj
= i
->DrawObj();
1751 SwContact
* pContact
= static_cast<SwContact
*>(pObj
->GetUserCall());
1752 // under certain conditions
1753 const RndStdIds
eAnchorType( pContact
->GetAnchorId() );
1754 if ((eAnchorType
!= RndStdIds::FLY_AT_CHAR
) ||
1755 sw_HideObj(*this, eAnchorType
, pContact
->GetAnchorFormat(),
1758 pContact
->MoveObjToInvisibleLayer( pObj
);
1764 // paragraph is visible, but can contain hidden text portion.
1765 // first we check if objects are allowed to be hidden:
1766 const SwViewShell
* pVsh
= getRootFrame()->GetCurrShell();
1767 const bool bShouldBeHidden
= !pVsh
|| !pVsh
->GetWin() ||
1768 !pVsh
->GetViewOptions()->IsShowHiddenChar();
1770 // Thus, show all objects, which are anchored at paragraph and
1771 // hide/show objects, which are anchored at/as character, according
1772 // to the visibility of the anchor character.
1773 for (SwAnchoredObject
* i
: *GetDrawObjs())
1775 SdrObject
* pObj
= i
->DrawObj();
1776 SwContact
* pContact
= static_cast<SwContact
*>(pObj
->GetUserCall());
1777 // Determine anchor type only once
1778 const RndStdIds
eAnchorType( pContact
->GetAnchorId() );
1780 if (eAnchorType
== RndStdIds::FLY_AT_PARA
)
1782 pContact
->MoveObjToVisibleLayer( pObj
);
1784 else if ((eAnchorType
== RndStdIds::FLY_AT_CHAR
) ||
1785 (eAnchorType
== RndStdIds::FLY_AS_CHAR
))
1787 sal_Int32 nHiddenStart
;
1788 sal_Int32 nHiddenEnd
;
1789 const SwFormatAnchor
& rAnchorFormat
= pContact
->GetAnchorFormat();
1790 SwScriptInfo::GetBoundsOfHiddenRange(
1791 *rAnchorFormat
.GetAnchorNode()->GetTextNode(),
1792 rAnchorFormat
.GetAnchorContentOffset(), nHiddenStart
, nHiddenEnd
);
1793 // Under certain conditions
1794 if ( nHiddenStart
!= COMPLETE_STRING
&& bShouldBeHidden
&&
1795 sw_HideObj(*this, eAnchorType
, rAnchorFormat
, i
))
1797 pContact
->MoveObjToInvisibleLayer( pObj
);
1800 pContact
->MoveObjToVisibleLayer( pObj
);
1804 OSL_FAIL( "<SwTextFrame::HideAndShowObjects()> - object not anchored at/inside paragraph!?" );
1812 SwTextFrame
*pMaster
= FindMaster();
1813 OSL_ENSURE(pMaster
, "SwTextFrame without master");
1815 pMaster
->HideAndShowObjects();
1819 void SwLayoutFrame::HideAndShowObjects()
1821 for (SwFrame
* pLower
= Lower(); pLower
; pLower
= pLower
->GetNext())
1823 pLower
->HideAndShowObjects();
1827 void SwFrame::HideAndShowObjects()
1832 * Returns the first possible break point in the current line.
1833 * This method is used in SwTextFrame::Format() to decide whether the previous
1834 * line has to be formatted as well.
1835 * nFound is <= nEndLine.
1837 TextFrameIndex
SwTextFrame::FindBrk(std::u16string_view aText
,
1838 const TextFrameIndex nStart
,
1839 const TextFrameIndex nEnd
)
1841 sal_Int32 nFound
= sal_Int32(nStart
);
1842 const sal_Int32 nEndLine
= std::min(sal_Int32(nEnd
), sal_Int32(aText
.size()) - 1);
1844 // Skip all leading blanks.
1845 while( nFound
<= nEndLine
&& ' ' == aText
[nFound
] )
1850 // A tricky situation with the TextAttr-Dummy-character (in this case "$"):
1851 // "Dr.$Meyer" at the beginning of the second line. Typing a blank after that
1852 // doesn't result in the word moving into first line, even though that would work.
1853 // For this reason we don't skip the dummy char.
1854 while( nFound
<= nEndLine
&& ' ' != aText
[nFound
] )
1859 return TextFrameIndex(nFound
);
1862 bool SwTextFrame::IsIdxInside(TextFrameIndex
const nPos
, TextFrameIndex
const nLen
) const
1864 if (nPos
== TextFrameIndex(COMPLETE_STRING
)) // the "not found" range
1866 // Silence over-eager warning emitted at least by GCC trunk towards 6:
1867 #if defined __GNUC__ && !defined __clang__
1868 #pragma GCC diagnostic push
1869 #pragma GCC diagnostic ignored "-Wstrict-overflow"
1871 if (nLen
!= TextFrameIndex(COMPLETE_STRING
) && GetOffset() > nPos
+ nLen
) // the range preceded us
1872 #if defined __GNUC__ && !defined __clang__
1873 #pragma GCC diagnostic pop
1877 if( !GetFollow() ) // the range doesn't precede us,
1878 return true; // nobody follows us.
1880 TextFrameIndex
const nMax
= GetFollow()->GetOffset();
1882 // either the range overlap or our text has been deleted
1883 // sw_redlinehide: GetText() should be okay here because it has already
1884 // been updated in the INS/DEL hint case
1885 if (nMax
> nPos
|| nMax
> TextFrameIndex(GetText().getLength()))
1888 // changes made in the first line of a follow can modify the master
1889 const SwParaPortion
* pPara
= GetFollow()->GetPara();
1890 return pPara
&& ( nPos
<= nMax
+ pPara
->GetLen() );
1893 inline void SwTextFrame::InvalidateRange(const SwCharRange
&aRange
, const tools::Long nD
)
1895 if ( IsIdxInside( aRange
.Start(), aRange
.Len() ) )
1896 InvalidateRange_( aRange
, nD
);
1899 void SwTextFrame::InvalidateRange_( const SwCharRange
&aRange
, const tools::Long nD
)
1907 SwParaPortion
*pPara
= GetPara();
1912 // In nDelta the differences between old and new
1913 // linelengths are being added, that's why it's negative
1914 // if chars have been added and positive, if chars have
1916 pPara
->SetDelta(pPara
->GetDelta() + nD
);
1919 SwCharRange
&rReformat
= pPara
->GetReformat();
1920 if(aRange
!= rReformat
) {
1921 if (TextFrameIndex(COMPLETE_STRING
) == rReformat
.Len())
1924 rReformat
+= aRange
;
1933 void SwTextFrame::CalcLineSpace()
1935 OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
1936 "SwTextFrame::CalcLineSpace with swapped frame!" );
1938 if( IsLocked() || !HasPara() )
1941 if( GetDrawObjs() ||
1942 GetTextNodeForParaProps()->GetSwAttrSet().GetFirstLineIndent().IsAutoFirst())
1948 SwParaPortion
*const pPara(GetPara());
1950 if (pPara
->IsFixLineHeight())
1956 Size
aNewSize( getFramePrintArea().SSize() );
1958 SwTextFormatInfo
aInf( getRootFrame()->GetCurrShell()->GetOut(), this );
1959 SwTextFormatter
aLine( this, &aInf
);
1960 if( aLine
.GetDropLines() )
1967 aLine
.RecalcRealHeight();
1969 aNewSize
.setHeight( (aLine
.Y() - getFrameArea().Top()) + aLine
.GetLineHeight() );
1971 SwTwips nDelta
= aNewSize
.Height() - getFramePrintArea().Height();
1972 // Underflow with free-flying frames
1973 if( aInf
.GetTextFly().IsOn() )
1975 SwRect
aTmpFrame( getFrameArea() );
1977 aTmpFrame
.Height( getFramePrintArea().Height() );
1979 aTmpFrame
.Height( aNewSize
.Height() );
1980 if( aInf
.GetTextFly().Relax( aTmpFrame
) )
1990 SwTextFrameBreak
aBreak( this );
1991 if( GetFollow() || aBreak
.IsBreakNow( aLine
) )
1993 // if there is a Follow() or if we need to break here, reformat
1998 // everything is business as usual...
1999 pPara
->SetPrepAdjust();
2004 static void lcl_SetWrong( SwTextFrame
& rFrame
, SwTextNode
const& rNode
,
2005 sal_Int32
const nPos
, sal_Int32
const nCnt
, bool const bMove
)
2007 if ( !rFrame
.IsFollow() )
2009 SwTextNode
& rTextNode
= const_cast<SwTextNode
&>(rNode
);
2010 sw::GrammarContact
* pGrammarContact
= sw::getGrammarContactFor(rTextNode
);
2011 SwGrammarMarkUp
* pWrongGrammar
= pGrammarContact
?
2012 pGrammarContact
->getGrammarCheck( rTextNode
, false ) :
2013 rTextNode
.GetGrammarCheck();
2014 bool bGrammarProxy
= pWrongGrammar
!= rTextNode
.GetGrammarCheck();
2017 if( rTextNode
.GetWrong() )
2018 rTextNode
.GetWrong()->Move( nPos
, nCnt
);
2020 pWrongGrammar
->MoveGrammar( nPos
, nCnt
);
2021 if( bGrammarProxy
&& rTextNode
.GetGrammarCheck() )
2022 rTextNode
.GetGrammarCheck()->MoveGrammar( nPos
, nCnt
);
2023 if( rTextNode
.GetSmartTags() )
2024 rTextNode
.GetSmartTags()->Move( nPos
, nCnt
);
2028 if( rTextNode
.GetWrong() )
2029 rTextNode
.GetWrong()->Invalidate( nPos
, nCnt
);
2031 pWrongGrammar
->Invalidate( nPos
, nCnt
);
2032 if( rTextNode
.GetSmartTags() )
2033 rTextNode
.GetSmartTags()->Invalidate( nPos
, nCnt
);
2035 const sal_Int32 nEnd
= nPos
+ (nCnt
> 0 ? nCnt
: 1 );
2036 if ( !rTextNode
.GetWrong() && !rTextNode
.IsWrongDirty() )
2038 rTextNode
.SetWrong( std::make_unique
<SwWrongList
>( WRONGLIST_SPELL
) );
2039 rTextNode
.GetWrong()->SetInvalid( nPos
, nEnd
);
2041 if ( !rTextNode
.GetSmartTags() && !rTextNode
.IsSmartTagDirty() )
2043 rTextNode
.SetSmartTags( std::make_unique
<SwWrongList
>( WRONGLIST_SMARTTAG
) );
2044 rTextNode
.GetSmartTags()->SetInvalid( nPos
, nEnd
);
2046 rTextNode
.SetWrongDirty(sw::WrongState::TODO
);
2047 rTextNode
.SetGrammarCheckDirty( true );
2048 rTextNode
.SetWordCountDirty( true );
2049 rTextNode
.SetAutoCompleteWordDirty( true );
2050 rTextNode
.SetSmartTagDirty( true );
2053 SwRootFrame
*pRootFrame
= rFrame
.getRootFrame();
2056 pRootFrame
->SetNeedGrammarCheck( true );
2059 SwPageFrame
*pPage
= rFrame
.FindPageFrame();
2062 pPage
->InvalidateSpelling();
2063 pPage
->InvalidateAutoCompleteWords();
2064 pPage
->InvalidateWordCount();
2065 pPage
->InvalidateSmartTags();
2069 static void lcl_SetScriptInval(SwTextFrame
& rFrame
, TextFrameIndex
const nPos
)
2071 if( rFrame
.GetPara() )
2072 rFrame
.GetPara()->GetScriptInfo().SetInvalidityA( nPos
);
2075 // note: SwClientNotify will be called once for every frame => just fix own Ofst
2076 static void lcl_ModifyOfst(SwTextFrame
& rFrame
,
2077 TextFrameIndex
const nPos
, TextFrameIndex
const nLen
,
2078 TextFrameIndex (* op
)(TextFrameIndex
const&, TextFrameIndex
const&))
2080 assert(nLen
!= TextFrameIndex(COMPLETE_STRING
));
2081 if (rFrame
.IsFollow() && nPos
< rFrame
.GetOffset())
2083 rFrame
.ManipOfst( std::max(nPos
, op(rFrame
.GetOffset(), nLen
)) );
2084 assert(sal_Int32(rFrame
.GetOffset()) <= rFrame
.GetText().getLength());
2090 void UpdateMergedParaForMove(sw::MergedPara
& rMerged
,
2091 SwTextFrame
& rTextFrame
,
2092 bool & o_rbRecalcFootnoteFlag
,
2093 SwTextNode
const& rDestNode
,
2094 SwTextNode
const& rNode
,
2095 sal_Int32
const nDestStart
,
2096 sal_Int32
const nSourceStart
,
2097 sal_Int32
const nLen
)
2099 std::vector
<std::pair
<sal_Int32
, sal_Int32
>> deleted
;
2100 sal_Int32
const nSourceEnd(nSourceStart
+ nLen
);
2101 sal_Int32
nLastEnd(0);
2102 for (const auto& rExt
: rMerged
.extents
)
2104 if (rExt
.pNode
== &rNode
)
2106 sal_Int32
const nStart(std::max(nLastEnd
, nSourceStart
));
2107 sal_Int32
const nEnd(std::min(rExt
.nStart
, nSourceEnd
));
2110 deleted
.emplace_back(nStart
, nEnd
);
2112 nLastEnd
= rExt
.nEnd
;
2113 if (nSourceEnd
<= rExt
.nEnd
)
2118 else if (rNode
.GetIndex() < rExt
.pNode
->GetIndex())
2123 if (nLastEnd
!= rNode
.Len()) // without nLen, string yet to be removed
2125 if (nLastEnd
< nSourceEnd
)
2127 deleted
.emplace_back(std::max(nLastEnd
, nSourceStart
), nSourceEnd
);
2130 if (deleted
.empty())
2133 o_rbRecalcFootnoteFlag
= true;
2134 for (auto const& it
: deleted
)
2136 sal_Int32
const nStart(it
.first
- nSourceStart
+ nDestStart
);
2137 TextFrameIndex
const nDeleted
= UpdateMergedParaForDelete(rMerged
, false,
2138 rDestNode
, nStart
, it
.second
- it
.first
);
2139 //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);
2140 // assert(nDeleted == it.second - it.first);
2143 // InvalidateRange/lcl_SetScriptInval was called sufficiently for InsertText
2144 lcl_SetWrong(rTextFrame
, rDestNode
, nStart
, it
.first
- it
.second
, false);
2145 TextFrameIndex
const nIndex(sw::MapModelToView(rMerged
, &rDestNode
, nStart
));
2146 lcl_ModifyOfst(rTextFrame
, nIndex
, nDeleted
, &o3tl::operator-<sal_Int32
, Tag_TextFrameIndex
>);
2154 * Related: fdo#56031 filter out attribute changes that don't matter for
2155 * humans/a11y to stop flooding the destination mortal with useless noise
2157 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2158 static bool isA11yRelevantAttribute(sal_uInt16 nWhich
)
2160 return nWhich
!= RES_CHRATR_RSID
;
2163 static bool hasA11yRelevantAttribute( const std::vector
<sal_uInt16
>& rWhichFmtAttr
)
2165 for( sal_uInt16 nWhich
: rWhichFmtAttr
)
2166 if ( isA11yRelevantAttribute( nWhich
) )
2171 #endif // ENABLE_WASM_STRIP_ACCESSIBILITY
2173 // Note: for now this overrides SwClient::SwClientNotify; the intermediary
2174 // classes still override SwClient::Modify, which should continue to work
2175 // as their implementation of SwClientNotify is SwClient's which calls Modify.
2176 // Therefore we also don't need to call SwClient::SwClientNotify(rModify, rHint)
2177 // because that's all it does, and this implementation calls
2178 // SwContentFrame::SwClientNotify() when appropriate.
2179 void SwTextFrame::SwClientNotify(SwModify
const& rModify
, SfxHint
const& rHint
)
2181 SfxPoolItem
const* pOld(nullptr);
2182 SfxPoolItem
const* pNew(nullptr);
2183 sw::MoveText
const* pMoveText(nullptr);
2184 sw::InsertText
const* pInsertText(nullptr);
2185 sw::DeleteText
const* pDeleteText(nullptr);
2186 sw::DeleteChar
const* pDeleteChar(nullptr);
2187 sw::RedlineDelText
const* pRedlineDelText(nullptr);
2188 sw::RedlineUnDelText
const* pRedlineUnDelText(nullptr);
2189 SwFormatChangeHint
const * pFormatChangedHint(nullptr);
2190 sw::AttrSetChangeHint
const* pAttrSetChangeHint(nullptr);
2191 sw::UpdateAttrHint
const* pUpdateAttrHint(nullptr);
2193 sal_uInt16 nWhich
= 0;
2194 if (rHint
.GetId() == SfxHintId::SwLegacyModify
)
2196 auto pHint
= static_cast<const sw::LegacyModifyHint
*>(&rHint
);
2197 pOld
= pHint
->m_pOld
;
2198 pNew
= pHint
->m_pNew
;
2199 nWhich
= pHint
->GetWhich();
2201 else if (rHint
.GetId() == SfxHintId::SwUpdateAttr
)
2203 pUpdateAttrHint
= static_cast<const sw::UpdateAttrHint
*>(&rHint
);
2205 else if (rHint
.GetId() == SfxHintId::SwInsertText
)
2207 pInsertText
= static_cast<const sw::InsertText
*>(&rHint
);
2209 else if (rHint
.GetId() == SfxHintId::SwDeleteText
)
2211 pDeleteText
= static_cast<const sw::DeleteText
*>(&rHint
);
2213 else if (rHint
.GetId() == SfxHintId::SwDeleteChar
)
2215 pDeleteChar
= static_cast<const sw::DeleteChar
*>(&rHint
);
2217 else if (rHint
.GetId() == SfxHintId::SwDocPosUpdateAtIndex
)
2219 auto pDocPosAt
= static_cast<const sw::DocPosUpdateAtIndex
*>(&rHint
);
2220 Broadcast(SfxHint()); // notify SwAccessibleParagraph
2223 if(pDocPosAt
->m_nDocPos
> getFrameArea().Top())
2225 TextFrameIndex
const nIndex(MapModelToView(
2226 &pDocPosAt
->m_rNode
,
2227 pDocPosAt
->m_nIndex
));
2228 InvalidateRange(SwCharRange(nIndex
, TextFrameIndex(1)));
2231 else if (rHint
.GetId() == SfxHintId::SwVirtPageNumHint
)
2233 auto& rVirtPageNumHint
= const_cast<sw::VirtPageNumHint
&>(static_cast<const sw::VirtPageNumHint
&>(rHint
));
2234 if(!IsInDocBody() || IsFollow() || rVirtPageNumHint
.IsFound())
2236 if(const SwPageFrame
* pPage
= FindPageFrame())
2237 pPage
->UpdateVirtPageNumInfo(rVirtPageNumHint
, this);
2240 else if (rHint
.GetId() == SfxHintId::SwMoveText
)
2242 pMoveText
= static_cast<sw::MoveText
const*>(&rHint
);
2244 else if (rHint
.GetId() == SfxHintId::SwRedlineDelText
)
2246 pRedlineDelText
= static_cast<sw::RedlineDelText
const*>(&rHint
);
2248 else if (rHint
.GetId() == SfxHintId::SwRedlineUnDelText
)
2250 pRedlineUnDelText
= static_cast<sw::RedlineUnDelText
const*>(&rHint
);
2252 else if (rHint
.GetId() == SfxHintId::SwFormatChange
)
2254 pFormatChangedHint
= static_cast<const SwFormatChangeHint
*>(&rHint
);
2256 else if (rHint
.GetId() == SfxHintId::SwAttrSetChange
)
2258 pAttrSetChangeHint
= static_cast<const sw::AttrSetChangeHint
*>(&rHint
);
2262 assert(!"unexpected hint");
2267 assert(m_pMergedPara
->listener
.IsListeningTo(&rModify
));
2270 SwTextNode
const& rNode(static_cast<SwTextNode
const&>(rModify
));
2272 // modifications concerning frame attributes are processed by the base class
2273 if( IsInRange( aFrameFormatSetRange
, nWhich
) || pFormatChangedHint
)
2276 { // ignore item set changes that don't apply
2277 SwTextNode
const*const pAttrNode(
2278 (nWhich
== RES_PAGEDESC
|| nWhich
== RES_BREAK
)
2279 ? m_pMergedPara
->pFirstNode
2280 : m_pMergedPara
->pParaPropsNode
);
2281 if (pAttrNode
!= &rModify
)
2286 SwContentFrame::SwClientNotify(rModify
, rHint
);
2287 if( pFormatChangedHint
&& getRootFrame()->GetCurrShell() )
2289 // collection has changed
2292 lcl_SetWrong( *this, rNode
, 0, COMPLETE_STRING
, false );
2293 SetDerivedR2L( false );
2295 // Force complete paint due to existing indents.
2297 InvalidateLineNum();
2302 if (m_pMergedPara
&& m_pMergedPara
->pParaPropsNode
!= &rModify
)
2304 if (isPARATR(nWhich
) || isPARATR_LIST(nWhich
)) // FRMATR handled above
2306 return; // ignore it
2310 Broadcast(SfxHint()); // notify SwAccessibleParagraph
2312 // while locked ignore all modifications
2317 // warning: one has to ensure that all variables are set
2318 TextFrameIndex nPos
;
2319 TextFrameIndex nLen
;
2320 bool bSetFieldsDirty
= false;
2321 bool bRecalcFootnoteFlag
= false;
2323 if (pRedlineDelText
)
2327 sal_Int32
const nNPos
= pRedlineDelText
->nStart
;
2328 sal_Int32
const nNLen
= pRedlineDelText
->nLen
;
2329 nPos
= MapModelToView(&rNode
, nNPos
);
2330 // update merged before doing anything else
2331 nLen
= UpdateMergedParaForDelete(*m_pMergedPara
, false, rNode
, nNPos
, nNLen
);
2332 const sal_Int32 m
= -nNLen
;
2333 if (nLen
&& IsIdxInside(nPos
, nLen
))
2335 InvalidateRange( SwCharRange(nPos
, TextFrameIndex(1)), m
);
2337 lcl_SetWrong( *this, rNode
, nNPos
, m
, false );
2340 lcl_SetScriptInval( *this, nPos
);
2341 bSetFieldsDirty
= bRecalcFootnoteFlag
= true;
2342 lcl_ModifyOfst(*this, nPos
, nLen
, &o3tl::operator-<sal_Int32
, Tag_TextFrameIndex
>);
2346 else if (pRedlineUnDelText
)
2350 sal_Int32
const nNPos
= pRedlineUnDelText
->nStart
;
2351 sal_Int32
const nNLen
= pRedlineUnDelText
->nLen
;
2352 nPos
= MapModelToView(&rNode
, nNPos
);
2353 nLen
= UpdateMergedParaForInsert(*m_pMergedPara
, false, rNode
, nNPos
, nNLen
);
2354 if (IsIdxInside(nPos
, nLen
))
2358 // Refresh NumPortions even when line is empty!
2365 InvalidateRange_( SwCharRange( nPos
, nLen
), nNLen
);
2367 lcl_SetWrong( *this, rNode
, nNPos
, nNLen
, false );
2368 lcl_SetScriptInval( *this, nPos
);
2369 bSetFieldsDirty
= true;
2370 lcl_ModifyOfst(*this, nPos
, nLen
, &o3tl::operator+<sal_Int32
, Tag_TextFrameIndex
>);
2376 && m_pMergedPara
->pFirstNode
->GetIndex() <= pMoveText
->pDestNode
->GetIndex()
2377 && pMoveText
->pDestNode
->GetIndex() <= m_pMergedPara
->pLastNode
->GetIndex())
2378 { // if it's not 2 nodes in merged frame, assume the target node doesn't have frames at all
2379 assert(abs(rNode
.GetIndex() - pMoveText
->pDestNode
->GetIndex()) == SwNodeOffset(1));
2380 UpdateMergedParaForMove(*m_pMergedPara
,
2382 bRecalcFootnoteFlag
,
2383 *pMoveText
->pDestNode
, rNode
,
2384 pMoveText
->nDestStart
,
2385 pMoveText
->nSourceStart
,
2390 // 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
2391 // assert(!m_pMergedPara || !getRootFrame()->IsHideRedlines() || !pMoveText->pDestNode->getLayoutFrame(getRootFrame()));
2394 else if (pInsertText
)
2396 nPos
= MapModelToView(&rNode
, pInsertText
->nPos
);
2397 // unlike redlines, inserting into fieldmark must be explicitly handled
2398 bool isHidden(false);
2399 switch (getRootFrame()->GetFieldmarkMode())
2401 case sw::FieldmarkMode::ShowCommand
:
2402 isHidden
= pInsertText
->isInsideFieldmarkResult
;
2404 case sw::FieldmarkMode::ShowResult
:
2405 isHidden
= pInsertText
->isInsideFieldmarkCommand
;
2407 case sw::FieldmarkMode::ShowBoth
: // just to avoid the warning
2412 nLen
= TextFrameIndex(pInsertText
->nLen
);
2415 UpdateMergedParaForInsert(*m_pMergedPara
, true, rNode
, pInsertText
->nPos
, pInsertText
->nLen
);
2417 if( IsIdxInside( nPos
, nLen
) )
2421 // Refresh NumPortions even when line is empty!
2428 InvalidateRange_( SwCharRange( nPos
, nLen
), pInsertText
->nLen
);
2430 lcl_SetScriptInval( *this, nPos
);
2431 bSetFieldsDirty
= true;
2432 lcl_ModifyOfst(*this, nPos
, nLen
, &o3tl::operator+<sal_Int32
, Tag_TextFrameIndex
>);
2434 lcl_SetWrong( *this, rNode
, pInsertText
->nPos
, pInsertText
->nLen
, true );
2436 else if (pDeleteText
)
2438 nPos
= MapModelToView(&rNode
, pDeleteText
->nStart
);
2440 { // update merged before doing anything else
2441 nLen
= UpdateMergedParaForDelete(*m_pMergedPara
, true, rNode
, pDeleteText
->nStart
, pDeleteText
->nLen
);
2445 nLen
= TextFrameIndex(pDeleteText
->nLen
);
2447 const sal_Int32 m
= -pDeleteText
->nLen
;
2448 if ((!m_pMergedPara
|| nLen
) && IsIdxInside(nPos
, nLen
))
2453 InvalidateRange( SwCharRange(nPos
, TextFrameIndex(1)), m
);
2455 lcl_SetWrong( *this, rNode
, pDeleteText
->nStart
, m
, true );
2458 lcl_SetScriptInval( *this, nPos
);
2459 bSetFieldsDirty
= bRecalcFootnoteFlag
= true;
2460 lcl_ModifyOfst(*this, nPos
, nLen
, &o3tl::operator-<sal_Int32
, Tag_TextFrameIndex
>);
2463 else if (pDeleteChar
)
2465 nPos
= MapModelToView(&rNode
, pDeleteChar
->m_nPos
);
2468 nLen
= UpdateMergedParaForDelete(*m_pMergedPara
, true, rNode
, pDeleteChar
->m_nPos
, 1);
2472 nLen
= TextFrameIndex(1);
2474 lcl_SetWrong( *this, rNode
, pDeleteChar
->m_nPos
, -1, true );
2477 InvalidateRange( SwCharRange(nPos
, nLen
), -1 );
2478 lcl_SetScriptInval( *this, nPos
);
2479 bSetFieldsDirty
= bRecalcFootnoteFlag
= true;
2480 lcl_ModifyOfst(*this, nPos
, nLen
, &o3tl::operator-<sal_Int32
, Tag_TextFrameIndex
>);
2483 else if (pAttrSetChangeHint
)
2485 InvalidateLineNum();
2487 const SwAttrSet
& rNewSet
= *pAttrSetChangeHint
->m_pNew
->GetChgSet();
2489 sal_uInt16 nCount
= rNewSet
.Count();
2491 if( const SwFormatFootnote
* pItem
= rNewSet
.GetItemIfSet( RES_TXTATR_FTN
, false ) )
2493 nPos
= MapModelToView(&rNode
, pItem
->GetTextFootnote()->GetStart());
2494 if (IsIdxInside(nPos
, TextFrameIndex(1)))
2495 Prepare( PrepareHint::FootnoteInvalidation
, pAttrSetChangeHint
->m_pNew
);
2500 if( const SwFormatField
* pItem
= rNewSet
.GetItemIfSet( RES_TXTATR_FIELD
, false ) )
2502 nPos
= MapModelToView(&rNode
, pItem
->GetTextField()->GetStart());
2503 if (IsIdxInside(nPos
, TextFrameIndex(1)))
2505 const SwFormatField
* pOldItem
= pAttrSetChangeHint
->m_pOld
?
2506 &(pAttrSetChangeHint
->m_pOld
->GetChgSet()->Get(RES_TXTATR_FIELD
)) : nullptr;
2507 if (SfxPoolItem::areSame( pItem
, pOldItem
))
2513 InvalidateRange_(SwCharRange(nPos
, TextFrameIndex(1)));
2518 bool bLineSpace
= SfxItemState::SET
== rNewSet
.GetItemState(
2519 RES_PARATR_LINESPACING
, false ),
2520 bRegister
= SfxItemState::SET
== rNewSet
.GetItemState(
2521 RES_PARATR_REGISTER
, false );
2522 if ( bLineSpace
|| bRegister
)
2524 if (!m_pMergedPara
|| m_pMergedPara
->pParaPropsNode
== &rModify
)
2526 Prepare( bRegister
? PrepareHint::Register
: PrepareHint::AdjustSizeWithoutFormatting
);
2532 // (1) Also invalidate next frame on next page/column.
2533 // (2) Skip empty sections and hidden paragraphs
2534 // Thus, use method <InvalidateNextPrtArea()>
2535 InvalidateNextPrtArea();
2543 if ((!m_pMergedPara
|| m_pMergedPara
->pParaPropsNode
== &rModify
)
2544 && IsInSct() && !GetPrev())
2546 SwSectionFrame
*pSect
= FindSctFrame();
2547 if( pSect
->ContainsAny() == this )
2548 pSect
->InvalidatePrt();
2554 if ( SfxItemState::SET
== rNewSet
.GetItemState( RES_PARATR_SPLIT
,
2557 if (!m_pMergedPara
|| m_pMergedPara
->pParaPropsNode
== &rModify
)
2568 if( SfxItemState::SET
== rNewSet
.GetItemState( RES_BACKGROUND
, false)
2569 && (!m_pMergedPara
|| m_pMergedPara
->pParaPropsNode
== &rModify
)
2570 && !IsFollow() && GetDrawObjs() )
2572 SwSortedObjs
*pObjs
= GetDrawObjs();
2573 for ( size_t i
= 0; GetDrawObjs() && i
< pObjs
->size(); ++i
)
2575 SwAnchoredObject
* pAnchoredObj
= (*pObjs
)[i
];
2576 if ( auto pFly
= pAnchoredObj
->DynCastFlyFrame() )
2578 if( !pFly
->IsFlyInContentFrame() )
2580 const SvxBrushItem
&rBack
=
2581 pFly
->GetAttrSet()->GetBackground();
2583 // following condition determines, if the fly frame
2584 // "inherites" the background color of text frame.
2585 // This is the case, if fly frame background
2586 // color is "no fill"/"auto fill" and if the fly frame
2587 // has no background graphic.
2588 // Thus, check complete fly frame background
2589 // color and *not* only its transparency value
2590 if ( (rBack
.GetColor() == COL_TRANSPARENT
) &&
2591 rBack
.GetGraphicPos() == GPOS_NONE
)
2593 pFly
->SetCompletePaint();
2594 pFly
->InvalidatePage();
2601 if ( SfxItemState::SET
==
2602 rNewSet
.GetItemState( RES_TXTATR_CHARFMT
, false ) )
2604 lcl_SetWrong( *this, rNode
, 0, COMPLETE_STRING
, false );
2605 lcl_SetScriptInval( *this, TextFrameIndex(0) );
2607 else if ( SfxItemState::SET
==
2608 rNewSet
.GetItemState( RES_CHRATR_LANGUAGE
, false ) ||
2609 SfxItemState::SET
==
2610 rNewSet
.GetItemState( RES_CHRATR_CJK_LANGUAGE
, false ) ||
2611 SfxItemState::SET
==
2612 rNewSet
.GetItemState( RES_CHRATR_CTL_LANGUAGE
, false ) )
2613 lcl_SetWrong( *this, rNode
, 0, COMPLETE_STRING
, false );
2614 else if ( SfxItemState::SET
==
2615 rNewSet
.GetItemState( RES_CHRATR_FONT
, false ) ||
2616 SfxItemState::SET
==
2617 rNewSet
.GetItemState( RES_CHRATR_CJK_FONT
, false ) ||
2618 SfxItemState::SET
==
2619 rNewSet
.GetItemState( RES_CHRATR_CTL_FONT
, false ) )
2620 lcl_SetScriptInval( *this, TextFrameIndex(0) );
2621 else if ( SfxItemState::SET
==
2622 rNewSet
.GetItemState( RES_FRAMEDIR
, false )
2623 && (!m_pMergedPara
|| m_pMergedPara
->pParaPropsNode
== &rModify
))
2625 SetDerivedR2L( false );
2627 // Force complete paint due to existing indents.
2633 if( getRootFrame()->GetCurrShell() )
2639 if (nClear
|| (m_pMergedPara
&&
2640 (m_pMergedPara
->pParaPropsNode
!= &rModify
||
2641 m_pMergedPara
->pFirstNode
!= &rModify
)))
2643 assert(pAttrSetChangeHint
->m_pOld
);
2644 SwAttrSetChg
aOldSet( *pAttrSetChangeHint
->m_pOld
);
2645 SwAttrSetChg
aNewSet( *pAttrSetChangeHint
->m_pNew
);
2647 if (m_pMergedPara
&& m_pMergedPara
->pParaPropsNode
!= &rModify
)
2649 for (sal_uInt16 i
= RES_PARATR_BEGIN
; i
!= RES_FRMATR_END
; ++i
)
2651 if (i
!= RES_BREAK
&& i
!= RES_PAGEDESC
)
2653 aOldSet
.ClearItem(i
);
2654 aNewSet
.ClearItem(i
);
2657 for (sal_uInt16 i
= XATTR_FILL_FIRST
; i
<= XATTR_FILL_LAST
; ++i
)
2659 aOldSet
.ClearItem(i
);
2660 aNewSet
.ClearItem(i
);
2663 if (m_pMergedPara
&& m_pMergedPara
->pFirstNode
!= &rModify
)
2665 aOldSet
.ClearItem(RES_BREAK
);
2666 aNewSet
.ClearItem(RES_BREAK
);
2667 aOldSet
.ClearItem(RES_PAGEDESC
);
2668 aNewSet
.ClearItem(RES_PAGEDESC
);
2673 aOldSet
.ClearItem( RES_TXTATR_FTN
);
2674 aNewSet
.ClearItem( RES_TXTATR_FTN
);
2678 aOldSet
.ClearItem( RES_TXTATR_FIELD
);
2679 aNewSet
.ClearItem( RES_TXTATR_FIELD
);
2681 if ( 0x04 & nClear
)
2685 aOldSet
.ClearItem( RES_PARATR_LINESPACING
);
2686 aNewSet
.ClearItem( RES_PARATR_LINESPACING
);
2690 aOldSet
.ClearItem( RES_PARATR_REGISTER
);
2691 aNewSet
.ClearItem( RES_PARATR_REGISTER
);
2694 if ( 0x08 & nClear
)
2696 aOldSet
.ClearItem( RES_PARATR_SPLIT
);
2697 aNewSet
.ClearItem( RES_PARATR_SPLIT
);
2699 if (aOldSet
.Count() || aNewSet
.Count())
2701 SwContentFrame::SwClientNotify(rModify
, sw::AttrSetChangeHint(&aOldSet
, &aNewSet
));
2705 SwContentFrame::SwClientNotify(rModify
, rHint
);
2708 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2709 if (isA11yRelevantAttribute(nWhich
))
2711 SwViewShell
* pViewSh
= getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
2714 pViewSh
->InvalidateAccessibleParaAttrs( *this );
2719 else if (rHint
.GetId() == SfxHintId::SwObjectDying
)
2721 else if (pUpdateAttrHint
)
2723 const SwUpdateAttr
* pNewUpdate
= pUpdateAttrHint
->m_pNew
;
2725 sal_Int32
const nNPos
= pNewUpdate
->getStart();
2726 sal_Int32
const nNLen
= pNewUpdate
->getEnd() - nNPos
;
2727 nPos
= MapModelToView(&rNode
, nNPos
);
2728 nLen
= MapModelToView(&rNode
, nNPos
+ nNLen
) - nPos
;
2729 if( IsIdxInside( nPos
, nLen
) )
2731 // We need to reformat anyways, even if the invalidated
2733 // E.g.: empty line, set 14 pt!
2735 // FootnoteNumbers need to be formatted
2737 nLen
= TextFrameIndex(1);
2739 InvalidateRange_( SwCharRange( nPos
, nLen
) );
2740 const sal_uInt16 nTmp
= pNewUpdate
->getWhichAttr();
2742 if( ! nTmp
|| RES_TXTATR_CHARFMT
== nTmp
|| RES_TXTATR_INETFMT
== nTmp
|| RES_TXTATR_AUTOFMT
== nTmp
||
2743 RES_UPDATEATTR_FMT_CHG
== nTmp
|| RES_UPDATEATTR_ATTRSET_CHG
== nTmp
)
2745 lcl_SetWrong( *this, rNode
, nNPos
, nNPos
+ nNLen
, false );
2746 lcl_SetScriptInval( *this, nPos
);
2750 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2751 if( isA11yRelevantAttribute( pNewUpdate
->getWhichAttr() ) &&
2752 hasA11yRelevantAttribute( pNewUpdate
->getFmtAttrs() ) )
2754 SwViewShell
* pViewSh
= getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
2757 pViewSh
->InvalidateAccessibleParaAttrs( *this );
2762 else switch (nWhich
)
2764 case RES_LINENUMBER
:
2766 assert(false); // should have been forwarded to SwContentFrame
2767 InvalidateLineNum();
2771 case RES_PARATR_LINESPACING
:
2776 if( IsInSct() && !GetPrev() )
2778 SwSectionFrame
*pSect
= FindSctFrame();
2779 if( pSect
->ContainsAny() == this )
2780 pSect
->InvalidatePrt();
2784 // (1) Also invalidate next frame on next page/column.
2785 // (2) Skip empty sections and hidden paragraphs
2786 // Thus, use method <InvalidateNextPrtArea()>
2787 InvalidateNextPrtArea();
2793 case RES_TXTATR_FIELD
:
2794 case RES_TXTATR_ANNOTATION
:
2796 sal_Int32
const nNPos
= static_cast<const SwFormatField
*>(pNew
)->GetTextField()->GetStart();
2797 nPos
= MapModelToView(&rNode
, nNPos
);
2798 if (IsIdxInside(nPos
, TextFrameIndex(1)))
2800 if (SfxPoolItem::areSame( pNew
, pOld
))
2803 // opt: invalidate window?
2808 InvalidateRange_(SwCharRange(nPos
, TextFrameIndex(1)));
2810 bSetFieldsDirty
= true;
2812 if ( SwSmartTagMgr::Get().IsSmartTagsEnabled() )
2813 lcl_SetWrong( *this, rNode
, nNPos
, nNPos
+ 1, false );
2817 case RES_TXTATR_FTN
:
2819 if (!IsInFootnote())
2820 { // the hint may be sent from the anchor node, or from a
2821 // node in the footnote; the anchor index is only valid in the
2823 assert(rNode
== static_cast<const SwFormatFootnote
*>(pNew
)->GetTextFootnote()->GetTextNode());
2824 nPos
= MapModelToView(&rNode
,
2825 static_cast<const SwFormatFootnote
*>(pNew
)->GetTextFootnote()->GetStart());
2828 else nPos
= TextFrameIndex(42); // shut up MSVC 2017 spurious warning C4701
2830 if (IsInFootnote() || IsIdxInside(nPos
, TextFrameIndex(1)))
2831 Prepare( PrepareHint::FootnoteInvalidation
, static_cast<const SwFormatFootnote
*>(pNew
)->GetTextFootnote() );
2835 case RES_PARATR_SPLIT
:
2839 bSetFieldsDirty
= true;
2842 assert(false); // should have been forwarded to SwContentFrame
2843 SetDerivedR2L( false );
2852 // is called by e. g. HiddenPara with 0
2853 SwFrame
*pNxt
= FindNext();
2854 if ( nullptr != pNxt
)
2855 pNxt
->InvalidatePrt();
2860 if( bSetFieldsDirty
)
2861 GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, &rNode
, SwNodeOffset(1) );
2863 if ( bRecalcFootnoteFlag
)
2867 void SwTextFrame::PrepWidows( const sal_uInt16 nNeed
, bool bNotify
)
2869 OSL_ENSURE(GetFollow() && nNeed
, "+SwTextFrame::Prepare: lost all friends");
2871 SwParaPortion
*pPara
= GetPara();
2874 pPara
->SetPrepWidows();
2876 sal_uInt16 nHave
= nNeed
;
2878 // We yield a few lines and shrink in CalcPreps()
2879 SwSwapIfNotSwapped
swap( this );
2881 SwTextSizeInfo
aInf( this );
2882 SwTextMargin
aLine( this, &aInf
);
2884 TextFrameIndex nTmpLen
= aLine
.GetCurr()->GetLen();
2885 while( nHave
&& aLine
.PrevLine() )
2889 nTmpLen
= aLine
.GetCurr()->GetLen();
2892 // If it's certain that we can yield lines, the Master needs
2893 // to check the widow rule
2897 if( !IsFollow() ) // only a master decides about orphans
2899 const WidowsAndOrphans
aWidOrp( this );
2900 bSplit
= ( aLine
.GetLineNr() >= aWidOrp
.GetOrphansLines() &&
2901 aLine
.GetLineNr() >= aLine
.GetDropLines() );
2906 GetFollow()->SetOffset( aLine
.GetEnd() );
2907 aLine
.TruncLines( true );
2908 if( pPara
->IsFollowField() )
2909 GetFollow()->SetFieldFollow( true );
2919 static bool lcl_ErgoVadis(SwTextFrame
* pFrame
, TextFrameIndex
& rPos
, const PrepareHint ePrep
)
2921 const SwFootnoteInfo
&rFootnoteInfo
= pFrame
->GetDoc().GetFootnoteInfo();
2922 if( ePrep
== PrepareHint::ErgoSum
)
2924 if( rFootnoteInfo
.m_aErgoSum
.isEmpty() )
2926 rPos
= pFrame
->GetOffset();
2930 if( rFootnoteInfo
.m_aQuoVadis
.isEmpty() )
2932 if( pFrame
->HasFollow() )
2933 rPos
= pFrame
->GetFollow()->GetOffset();
2935 rPos
= TextFrameIndex(pFrame
->GetText().getLength());
2937 --rPos
; // our last character
2942 // Silence over-eager warning emitted at least by GCC 5.3.1
2943 #if defined __GNUC__ && !defined __clang__
2944 # pragma GCC diagnostic push
2945 # pragma GCC diagnostic ignored "-Wstrict-overflow"
2947 bool SwTextFrame::Prepare( const PrepareHint ePrep
, const void* pVoid
,
2950 bool bParaPossiblyInvalid
= false;
2952 SwFrameSwapper
aSwapper( this, false );
2958 case PrepareHint::BossChanged
:
2959 SetInvalidVert( true ); // Test
2961 case PrepareHint::WidowsOrphans
:
2962 case PrepareHint::Widows
:
2963 case PrepareHint::FootnoteInvalidationGone
: return bParaPossiblyInvalid
;
2965 case PrepareHint::FramePositionChanged
:
2967 // We also need an InvalidateSize for Areas (with and without columns),
2968 // so that we format and bUndersized is set (if needed)
2969 if( IsInFly() || IsInSct() )
2971 SwTwips nTmpBottom
= GetUpper()->getFrameArea().Top() +
2972 GetUpper()->getFramePrintArea().Bottom();
2973 if( nTmpBottom
< getFrameArea().Bottom() )
2976 // Are there any free-flying frames on this page?
2977 SwTextFly
aTextFly( this );
2978 if( aTextFly
.IsOn() )
2980 // Does any free-flying frame overlap?
2981 if ( aTextFly
.Relax() || IsUndersized() )
2984 if (GetTextNodeForParaProps()->GetSwAttrSet().GetRegister().GetValue())
2987 SwTextGridItem
const*const pGrid(GetGridItem(FindPageFrame()));
2988 if (pGrid
&& GetTextNodeForParaProps()->GetSwAttrSet().GetParaGrid().GetValue())
2991 // i#28701 - consider anchored objects
2992 if ( GetDrawObjs() )
2995 return bParaPossiblyInvalid
;
3002 // Split fly anchors are technically empty (have no SwParaPortion), but otherwise behave like
3003 // other split text frames, which are non-empty.
3004 bool bSplitFlyAnchor
= GetOffset() == TextFrameIndex(0) && HasFollow()
3005 && GetFollow()->GetOffset() == TextFrameIndex(0);
3007 if( !HasPara() && !bSplitFlyAnchor
&& PrepareHint::MustFit
!= ePrep
)
3009 OSL_ENSURE( !IsLocked(), "SwTextFrame::Prepare: three of a perfect pair" );
3010 // check while ignoring frame width (testParagraphMarkInCell)
3011 // because it's called from InvalidateAllContent()
3012 if (!IsHiddenNowImpl())
3014 SetInvalidVert( true ); // Test
3020 return bParaPossiblyInvalid
;
3023 // Get object from cache while locking
3024 SwTextLineAccess
aAccess( this );
3025 SwParaPortion
*pPara
= aAccess
.GetPara();
3029 case PrepareHint::FootnoteMove
:
3031 SwFrameAreaDefinition::FrameAreaWriteAccess
aFrm(*this);
3036 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*this);
3043 case PrepareHint::AdjustSizeWithoutFormatting
:
3044 pPara
->SetPrepAdjust();
3045 if( IsFootnoteNumFrame() != pPara
->IsFootnoteNum() ||
3048 InvalidateRange(SwCharRange(TextFrameIndex(0), TextFrameIndex(1)), 1);
3049 if( GetOffset() && !IsFollow() )
3050 SetOffset_(TextFrameIndex(0));
3053 case PrepareHint::MustFit
:
3054 pPara
->SetPrepMustFit(true);
3056 case PrepareHint::WidowsOrphans
:
3057 pPara
->SetPrepAdjust();
3059 case PrepareHint::Widows
:
3060 // MustFit is stronger than anything else
3061 if( pPara
->IsPrepMustFit() )
3062 return bParaPossiblyInvalid
;
3063 // see comment in WidowsAndOrphans::FindOrphans and CalcPreps()
3064 PrepWidows( *static_cast<const sal_uInt16
*>(pVoid
), bNotify
);
3067 case PrepareHint::FootnoteInvalidation
:
3069 SwTextFootnote
const *pFootnote
= static_cast<SwTextFootnote
const *>(pVoid
);
3070 if( IsInFootnote() )
3072 // Am I the first TextFrame of a footnote?
3074 // So we're a TextFrame of the footnote, which has
3075 // to display the footnote number or the ErgoSum text
3076 InvalidateRange(SwCharRange(TextFrameIndex(0), TextFrameIndex(1)), 1);
3080 // We're the last Footnote; we need to update the
3081 // QuoVadis texts now
3082 const SwFootnoteInfo
&rFootnoteInfo
= GetDoc().GetFootnoteInfo();
3083 if( !pPara
->UpdateQuoVadis( rFootnoteInfo
.m_aQuoVadis
) )
3085 TextFrameIndex nPos
= pPara
->GetParLen();
3088 InvalidateRange( SwCharRange(nPos
, TextFrameIndex(1)), 1);
3094 // We are the TextFrame _with_ the footnote
3095 TextFrameIndex
const nPos
= MapModelToView(
3096 &pFootnote
->GetTextNode(), pFootnote
->GetStart());
3097 InvalidateRange(SwCharRange(nPos
, TextFrameIndex(1)), 1);
3101 case PrepareHint::BossChanged
:
3105 SetInvalidVert( false );
3106 bool bOld
= IsVertical();
3107 SetInvalidVert( true );
3108 if( bOld
!= IsVertical() )
3109 InvalidateRange(SwCharRange(GetOffset(), TextFrameIndex(COMPLETE_STRING
)));
3114 TextFrameIndex nNxtOfst
= GetFollow()->GetOffset();
3117 InvalidateRange(SwCharRange( nNxtOfst
, TextFrameIndex(1)), 1);
3119 if( IsInFootnote() )
3121 TextFrameIndex nPos
;
3122 if( lcl_ErgoVadis( this, nPos
, PrepareHint::QuoVadis
) )
3123 InvalidateRange( SwCharRange( nPos
, TextFrameIndex(1)) );
3124 if( lcl_ErgoVadis( this, nPos
, PrepareHint::ErgoSum
) )
3125 InvalidateRange( SwCharRange( nPos
, TextFrameIndex(1)) );
3127 // If we have a page number field, we must invalidate those spots
3128 SwTextNode
const* pNode(nullptr);
3129 sw::MergedAttrIter
iter(*this);
3130 TextFrameIndex
const nEnd
= GetFollow()
3131 ? GetFollow()->GetOffset() : TextFrameIndex(COMPLETE_STRING
);
3132 for (SwTextAttr
const* pHt
= iter
.NextAttr(&pNode
); pHt
; pHt
= iter
.NextAttr(&pNode
))
3134 TextFrameIndex
const nStart(MapModelToView(pNode
, pHt
->GetStart()));
3135 if (nStart
>= GetOffset())
3140 // If we're flowing back and own a Footnote, the Footnote also flows
3141 // with us. So that it doesn't obstruct us, we send ourselves
3143 // pVoid != 0 means MoveBwd()
3144 const sal_uInt16 nWhich
= pHt
->Which();
3145 if (RES_TXTATR_FIELD
== nWhich
||
3146 (HasFootnote() && pVoid
&& RES_TXTATR_FTN
== nWhich
))
3147 InvalidateRange(SwCharRange(nStart
, TextFrameIndex(1)), 1);
3150 // A new boss, a new chance for growing
3151 if( IsUndersized() )
3154 InvalidateRange(SwCharRange(GetOffset(), TextFrameIndex(1)), 1);
3159 case PrepareHint::FramePositionChanged
:
3161 if ( isFramePrintAreaValid() )
3163 SwTextGridItem
const*const pGrid(GetGridItem(FindPageFrame()));
3164 if (pGrid
&& GetTextNodeForParaProps()->GetSwAttrSet().GetParaGrid().GetValue())
3168 // If we don't overlap with anybody:
3169 // did any free-flying frame overlapped _before_ the position change?
3170 bool bFormat
= pPara
->HasFly();
3175 SwTwips nTmpBottom
= GetUpper()->getFrameArea().Top() +
3176 GetUpper()->getFramePrintArea().Bottom();
3177 if( nTmpBottom
< getFrameArea().Bottom() )
3182 if ( GetDrawObjs() )
3184 const size_t nCnt
= GetDrawObjs()->size();
3185 for ( size_t i
= 0; i
< nCnt
; ++i
)
3187 SwAnchoredObject
* pAnchoredObj
= (*GetDrawObjs())[i
];
3188 // i#28701 - consider all
3189 // to-character anchored objects
3190 if ( pAnchoredObj
->GetFrameFormat()->GetAnchor().GetAnchorId()
3191 == RndStdIds::FLY_AT_CHAR
)
3200 // Are there any free-flying frames on this page?
3201 SwTextFly
aTextFly( this );
3202 if( aTextFly
.IsOn() )
3204 // Does any free-flying frame overlap?
3205 const bool bRelaxed
= aTextFly
.Relax();
3206 bFormat
= bRelaxed
|| IsUndersized();
3209 // It's possible that pPara was deleted above; retrieve it again
3210 pPara
= aAccess
.GetPara();
3221 if( pPara
->GetRepaint().HasArea() )
3230 if (GetTextNodeForParaProps()->GetSwAttrSet().GetRegister().GetValue())
3231 bParaPossiblyInvalid
= Prepare( PrepareHint::Register
, nullptr, bNotify
);
3232 // The Frames need to be readjusted, which caused by changes
3234 else if( HasFootnote() )
3236 bParaPossiblyInvalid
= Prepare( PrepareHint::AdjustSizeWithoutFormatting
, nullptr, bNotify
);
3240 return bParaPossiblyInvalid
; // So that there's no SetPrep()
3242 if (bParaPossiblyInvalid
)
3244 // It's possible that pPara was deleted above; retrieve it again
3245 pPara
= aAccess
.GetPara();
3251 case PrepareHint::Register
:
3252 if (GetTextNodeForParaProps()->GetSwAttrSet().GetRegister().GetValue())
3254 pPara
->SetPrepAdjust();
3257 // It's possible that pPara was deleted above; retrieve it again
3258 bParaPossiblyInvalid
= true;
3259 pPara
= aAccess
.GetPara();
3263 SwFrame
* pNxt
= GetIndNext();
3264 if ( nullptr != pNxt
)
3266 pNxt
->InvalidatePrt_();
3267 if ( pNxt
->IsLayoutFrame() )
3268 pNxt
->InvalidatePage();
3273 case PrepareHint::FootnoteInvalidationGone
:
3275 // If a Follow is calling us, because a footnote is being deleted, our last
3276 // line has to be formatted, so that the first line of the Follow can flow up.
3277 // Which had flowed to the next page to be together with the footnote (this is
3278 // especially true for areas with columns)
3279 OSL_ENSURE( GetFollow(), "PrepareHint::FootnoteInvalidationGone may only be called by Follow" );
3280 TextFrameIndex nPos
= GetFollow()->GetOffset();
3281 if( IsFollow() && GetOffset() == nPos
) // If we don't have a mass of text, we call our
3282 FindMaster()->Prepare( PrepareHint::FootnoteInvalidationGone
); // Master's Prepare
3284 --nPos
; // The char preceding our Follow
3285 InvalidateRange(SwCharRange(nPos
, TextFrameIndex(1)));
3286 return bParaPossiblyInvalid
;
3288 case PrepareHint::ErgoSum
:
3289 case PrepareHint::QuoVadis
:
3291 TextFrameIndex nPos
;
3292 if( lcl_ErgoVadis( this, nPos
, ePrep
) )
3293 InvalidateRange(SwCharRange(nPos
, TextFrameIndex(1)));
3296 case PrepareHint::FlyFrameAttributesChanged
:
3300 TextFrameIndex
const nWhere
= CalcFlyPos( static_cast<SwFrameFormat
const *>(pVoid
) );
3301 OSL_ENSURE( TextFrameIndex(COMPLETE_STRING
) != nWhere
, "Prepare: Why me?" );
3302 InvalidateRange(SwCharRange(nWhere
, TextFrameIndex(1)));
3303 return bParaPossiblyInvalid
;
3305 [[fallthrough
]]; // else: continue with default case block
3307 case PrepareHint::Clear
:
3312 if( PrepareHint::FlyFrameArrive
== ePrep
|| PrepareHint::FlyFrameLeave
== ePrep
)
3314 TextFrameIndex
const nLen
= (GetFollow()
3315 ? GetFollow()->GetOffset()
3316 : TextFrameIndex(COMPLETE_STRING
))
3318 InvalidateRange( SwCharRange( GetOffset(), nLen
) );
3323 if( pPara
->GetRepaint().HasArea() )
3327 if( GetOffset() && !IsFollow() )
3328 SetOffset_( TextFrameIndex(0) );
3334 return bParaPossiblyInvalid
; // no SetPrep() happened
3342 return bParaPossiblyInvalid
;
3344 #if defined __GNUC__ && !defined __clang__
3345 # pragma GCC diagnostic pop
3349 * Small Helper class:
3350 * Prepares a test format.
3351 * The frame is changed in size and position, its SwParaPortion is moved aside
3352 * and a new one is created.
3353 * To achieve this, run formatting with bTestFormat flag set.
3354 * In the destructor the TextFrame is reset to its original state.
3358 SwTextFrame
*pFrame
;
3359 SwParaPortion
*pOldPara
;
3360 SwRect aOldFrame
, aOldPrt
;
3362 SwTestFormat( SwTextFrame
* pTextFrame
, const SwFrame
* pPrv
, SwTwips nMaxHeight
);
3366 SwTestFormat::SwTestFormat( SwTextFrame
* pTextFrame
, const SwFrame
* pPre
, SwTwips nMaxHeight
)
3367 : pFrame( pTextFrame
)
3369 aOldFrame
= pFrame
->getFrameArea();
3370 aOldPrt
= pFrame
->getFramePrintArea();
3372 SwRectFnSet
aRectFnSet(pFrame
);
3373 SwTwips nLower
= aRectFnSet
.GetBottomMargin(*pFrame
);
3376 // indeed, here the GetUpper()->getFramePrintArea() gets copied and manipulated
3377 SwFrameAreaDefinition::FrameAreaWriteAccess
aFrm(*pFrame
);
3378 aFrm
.setSwRect(pFrame
->GetUpper()->getFramePrintArea());
3379 aFrm
+= pFrame
->GetUpper()->getFrameArea().Pos();
3380 aRectFnSet
.SetHeight( aFrm
, nMaxHeight
);
3382 if( pFrame
->GetPrev() )
3386 aRectFnSet
.GetBottom(pFrame
->GetPrev()->getFrameArea()) - ( aRectFnSet
.IsVert() ? nMaxHeight
+ 1 : 0 ) );
3390 SwBorderAttrAccess
aAccess( SwFrame::GetCache(), pFrame
);
3391 const SwBorderAttrs
&rAttrs
= *aAccess
.Get();
3394 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*pFrame
);
3395 aRectFnSet
.SetPosX(aPrt
, rAttrs
.CalcLeft( pFrame
) );
3400 SwTwips nUpper
= pFrame
->CalcUpperSpace( &rAttrs
, pPre
);
3401 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*pFrame
);
3402 aRectFnSet
.SetPosY(aPrt
, nUpper
);
3406 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*pFrame
);
3407 aRectFnSet
.SetHeight( aPrt
, std::max( tools::Long(0) , aRectFnSet
.GetHeight(pFrame
->getFrameArea()) - aRectFnSet
.GetTop(aPrt
) - nLower
) );
3408 aRectFnSet
.SetWidth( aPrt
, aRectFnSet
.GetWidth(pFrame
->getFrameArea()) - ( rAttrs
.CalcLeft( pFrame
) + rAttrs
.CalcRight( pFrame
) ) );
3411 pOldPara
= pFrame
->HasPara() ? pFrame
->GetPara() : nullptr;
3412 pFrame
->SetPara( new SwParaPortion(), false );
3413 OSL_ENSURE( ! pFrame
->IsSwapped(), "A frame is swapped before Format_" );
3415 if ( pFrame
->IsVertical() )
3416 pFrame
->SwapWidthAndHeight();
3418 SwTextFormatInfo
aInf( pFrame
->getRootFrame()->GetCurrShell()->GetOut(), pFrame
, false, true, true );
3419 SwTextFormatter
aLine( pFrame
, &aInf
);
3421 pFrame
->Format_( aLine
, aInf
);
3423 if ( pFrame
->IsVertical() )
3424 pFrame
->SwapWidthAndHeight();
3426 OSL_ENSURE( ! pFrame
->IsSwapped(), "A frame is swapped after Format_" );
3429 SwTestFormat::~SwTestFormat()
3432 SwFrameAreaDefinition::FrameAreaWriteAccess
aFrm(*pFrame
);
3433 aFrm
.setSwRect(aOldFrame
);
3437 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*pFrame
);
3438 aPrt
.setSwRect(aOldPrt
);
3441 pFrame
->SetPara( pOldPara
);
3444 bool SwTextFrame::TestFormat( const SwFrame
* pPrv
, SwTwips
&rMaxHeight
, bool &bSplit
)
3446 PROTOCOL_ENTER( this, PROT::TestFormat
, DbgAction::NONE
, nullptr )
3448 if( IsLocked() && GetUpper()->getFramePrintArea().Width() <= 0 )
3451 SwTestFormat
aSave( this, pPrv
, rMaxHeight
);
3453 return SwTextFrame::WouldFit(rMaxHeight
, bSplit
, true, false);
3457 * We should not and don't need to reformat.
3458 * We assume that we already formatted and that the formatting
3459 * data is still current.
3461 * We also assume that the frame width of the Master and Follow
3462 * are the same. That's why we're not calling FindBreak() for
3464 * The required height is coming from nMaxHeight.
3466 * @returns true if I can split
3468 bool SwTextFrame::WouldFit(SwTwips
&rMaxHeight
, bool &bSplit
, bool bTst
, bool bMoveBwd
)
3470 OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
3471 "SwTextFrame::WouldFit with swapped frame" );
3472 SwRectFnSet
aRectFnSet(this);
3477 // it can happen that the IdleCollector removed the cached information
3481 // i#27801 - correction: 'short cut' for empty paragraph
3482 // can *not* be applied, if test format is in progress. The test format doesn't
3483 // adjust the frame and the printing area - see method <SwTextFrame::Format_(..)>,
3484 // which is called in <SwTextFrame::TestFormat(..)>
3485 if ( IsEmpty() && !bTst
)
3488 SwTwips nHeight
= aRectFnSet
.IsVert() ? getFramePrintArea().SSize().Width() : getFramePrintArea().SSize().Height();
3489 if( rMaxHeight
< nHeight
)
3493 rMaxHeight
-= nHeight
;
3498 // GetPara can still be 0 in edge cases
3499 // We return true in order to be reformatted on the new Page
3500 OSL_ENSURE( HasPara() || IsHiddenNow(), "WouldFit: GetFormatted() and then !HasPara()" );
3501 if( !HasPara() || ( !aRectFnSet
.GetHeight(getFrameArea()) && IsHiddenNow() ) )
3504 // Because the Orphan flag only exists for a short moment, we also check
3505 // whether the Framesize is set to very huge by CalcPreps, in order to
3507 if (IsWidow() || (aRectFnSet
.IsVert()
3508 ? (0 == getFrameArea().Left())
3509 : (sw::WIDOW_MAGIC
- 20000 < getFrameArea().Bottom())))
3514 // If we've ended up here due to a Widow request by our Follow, we check
3515 // whether there's a Follow with a real height at all.
3516 // Else (e.g. for newly created SctFrames) we ignore the IsWidow() and
3517 // still check if we can find enough room
3518 if (((!aRectFnSet
.IsVert() && getFrameArea().Bottom() <= sw::WIDOW_MAGIC
- 20000) ||
3519 ( aRectFnSet
.IsVert() && 0 < getFrameArea().Left() ) ) &&
3520 ( GetFollow()->IsVertical() ?
3521 !GetFollow()->getFrameArea().Width() :
3522 !GetFollow()->getFrameArea().Height() ) )
3524 SwTextFrame
* pFoll
= GetFollow()->GetFollow();
3526 ( pFoll
->IsVertical() ?
3527 !pFoll
->getFrameArea().Width() :
3528 !pFoll
->getFrameArea().Height() ) )
3529 pFoll
= pFoll
->GetFollow();
3538 SwSwapIfNotSwapped
swap( this );
3540 SwTextSizeInfo
aInf( this );
3541 SwTextMargin
aLine( this, &aInf
);
3543 WidowsAndOrphans
aFrameBreak( this, rMaxHeight
, bSplit
);
3548 // is breaking necessary?
3549 bSplit
= !aFrameBreak
.IsInside( aLine
);
3551 bRet
= !aFrameBreak
.IsKeepAlways() && aFrameBreak
.WouldFit(aLine
, rMaxHeight
, bTst
, bMoveBwd
);
3554 // we need the total height including the current line
3558 rMaxHeight
-= aLine
.GetLineHeight();
3559 } while ( aLine
.Next() );
3565 SwTwips
SwTextFrame::GetParHeight() const
3567 OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
3568 "SwTextFrame::GetParHeight with swapped frame" );
3571 { // For non-empty paragraphs this is a special case
3572 // For UnderSized we can simply just ask 1 Twip more
3573 sal_uInt16 nRet
= o3tl::narrowing
<sal_uInt16
>(getFramePrintArea().SSize().Height());
3574 if( IsUndersized() )
3576 if( IsEmpty() || GetText().isEmpty() )
3577 nRet
= o3tl::narrowing
<sal_uInt16
>(EmptyHeight());
3584 // TODO: Refactor and improve code
3585 const SwLineLayout
* pLineLayout
= GetPara();
3586 SwTwips nHeight
= pLineLayout
? pLineLayout
->GetRealHeight() : 0;
3588 // Is this paragraph scrolled? Our height until now is at least
3589 // one line height too low then
3590 if( GetOffset() && !IsFollow() )
3593 while ( pLineLayout
&& pLineLayout
->GetNext() )
3595 pLineLayout
= pLineLayout
->GetNext();
3596 nHeight
= nHeight
+ pLineLayout
->GetRealHeight();
3603 * @returns this _always_ in the formatted state!
3605 SwTextFrame
* SwTextFrame::GetFormatted( bool bForceQuickFormat
)
3607 vcl::RenderContext
* pRenderContext
= getRootFrame()->GetCurrShell()->GetOut();
3608 SwSwapIfSwapped
swap( this );
3610 // In case the SwLineLayout was cleared out of the s_pTextCache, recreate it
3611 // Not for empty paragraphs
3612 if( !HasPara() && !(isFrameAreaDefinitionValid() && IsEmpty()) )
3614 // Calc() must be called, because frame position can be wrong
3615 const bool bFormat
= isFrameAreaSizeValid();
3616 Calc(pRenderContext
); // calls Format() if invalid
3618 // If the flags were valid (hence bFormat=true), Calc did nothing,
3619 // so Format() must be called manually in order to recreate
3620 // the SwLineLayout that has been deleted from the
3621 // SwTextFrame::s_pTextCache (hence !HasPara() above).
3622 // Optimization with FormatQuick()
3623 if( bFormat
&& !FormatQuick( bForceQuickFormat
) )
3624 Format(getRootFrame()->GetCurrShell()->GetOut());
3630 SwTwips
SwTextFrame::CalcFitToContent()
3633 // If we are currently locked, we better return with a
3634 // fairly reasonable value:
3636 return getFramePrintArea().Width();
3638 SwParaPortion
* pOldPara
= GetPara();
3639 SwParaPortion
*pDummy
= new SwParaPortion();
3640 SetPara( pDummy
, false );
3641 const SwPageFrame
* pPage
= FindPageFrame();
3643 const Point aOldFramePos
= getFrameArea().Pos();
3644 const SwTwips nOldFrameWidth
= getFrameArea().Width();
3645 const SwTwips nOldPrtWidth
= getFramePrintArea().Width();
3646 const SwTwips nPageWidth
= GetUpper()->IsVertical() ?
3647 pPage
->getFramePrintArea().Height() :
3648 pPage
->getFramePrintArea().Width();
3651 SwFrameAreaDefinition::FrameAreaWriteAccess
aFrm(*this);
3652 aFrm
.Width( nPageWidth
);
3656 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*this);
3657 aPrt
.Width( nPageWidth
);
3660 // i#25422 objects anchored as character in RTL
3661 if ( IsRightToLeft() )
3663 SwFrameAreaDefinition::FrameAreaWriteAccess
aFrm(*this);
3664 aFrm
.Pos().AdjustX(nOldFrameWidth
- nPageWidth
);
3667 TextFrameLockGuard
aLock( this );
3669 SwTextFormatInfo
aInf( getRootFrame()->GetCurrShell()->GetOut(), this, false, true, true );
3670 aInf
.SetIgnoreFly( true );
3671 SwTextFormatter
aLine( this, &aInf
);
3672 SwHookOut
aHook( aInf
);
3674 // i#54031 - assure minimum of MINLAY twips.
3675 const SwTwips nMax
= std::max( SwTwips(MINLAY
), aLine
.CalcFitToContent_() + 1 );
3678 SwFrameAreaDefinition::FrameAreaWriteAccess
aFrm(*this);
3679 aFrm
.Width( nOldFrameWidth
);
3681 // i#25422 objects anchored as character in RTL
3682 if ( IsRightToLeft() )
3684 aFrm
.Pos() = aOldFramePos
;
3689 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*this);
3690 aPrt
.Width( nOldPrtWidth
);
3693 SetPara( pOldPara
);
3699 * Simulate format for a list item paragraph, whose list level attributes
3700 * are in LABEL_ALIGNMENT mode, in order to determine additional first
3701 * line offset for the real text formatting due to the value of label
3702 * adjustment attribute of the list level.
3704 void SwTextFrame::CalcAdditionalFirstLineOffset()
3709 // reset additional first line offset
3710 mnAdditionalFirstLineOffset
= 0;
3712 const SwTextNode
* pTextNode( GetTextNodeForParaProps() );
3713 // sw_redlinehide: check that pParaPropsNode is the correct one
3714 assert(pTextNode
->IsNumbered(getRootFrame()) == pTextNode
->IsNumbered(nullptr));
3715 if (!(pTextNode
->IsNumbered(getRootFrame()) &&
3716 pTextNode
->IsCountedInList() && pTextNode
->GetNumRule()))
3719 int nListLevel
= pTextNode
->GetActualListLevel();
3724 if (nListLevel
>= MAXLEVEL
)
3725 nListLevel
= MAXLEVEL
- 1;
3727 const SwNumFormat
& rNumFormat
=
3728 pTextNode
->GetNumRule()->Get( o3tl::narrowing
<sal_uInt16
>(nListLevel
) );
3729 if ( rNumFormat
.GetPositionAndSpaceMode() != SvxNumberFormat::LABEL_ALIGNMENT
)
3732 // keep current paragraph portion and apply dummy paragraph portion
3733 SwParaPortion
* pOldPara
= GetPara();
3734 SwParaPortion
*pDummy
= new SwParaPortion();
3735 SetPara( pDummy
, false );
3738 TextFrameLockGuard
aLock( this );
3740 // simulate text formatting
3741 SwTextFormatInfo
aInf( getRootFrame()->GetCurrShell()->GetOut(), this, false, true, true );
3742 aInf
.SetIgnoreFly( true );
3743 SwTextFormatter
aLine( this, &aInf
);
3744 SwHookOut
aHook( aInf
);
3745 aLine
.CalcFitToContent_();
3747 // determine additional first line offset
3748 const SwLinePortion
* pFirstPortion
= aLine
.GetCurr()->GetFirstPortion();
3749 if ( pFirstPortion
->InNumberGrp() && !pFirstPortion
->IsFootnoteNumPortion() )
3751 SwTwips
nNumberPortionWidth( pFirstPortion
->Width() );
3753 const SwLinePortion
* pPortion
= pFirstPortion
->GetNextPortion();
3755 pPortion
->InNumberGrp() && !pPortion
->IsFootnoteNumPortion())
3757 nNumberPortionWidth
+= pPortion
->Width();
3758 pPortion
= pPortion
->GetNextPortion();
3761 if ( ( IsRightToLeft() &&
3762 rNumFormat
.GetNumAdjust() == SvxAdjust::Left
) ||
3763 ( !IsRightToLeft() &&
3764 rNumFormat
.GetNumAdjust() == SvxAdjust::Right
) )
3766 mnAdditionalFirstLineOffset
= -nNumberPortionWidth
;
3768 else if ( rNumFormat
.GetNumAdjust() == SvxAdjust::Center
)
3770 mnAdditionalFirstLineOffset
= -(nNumberPortionWidth
/2);
3774 // restore paragraph portion
3775 SetPara( pOldPara
);
3779 * Determine the height of the last line for the calculation of
3780 * the proportional line spacing
3782 * Height of last line will be stored in new member
3783 * mnHeightOfLastLine and can be accessed via method
3784 * GetHeightOfLastLine()
3786 * @param _bUseFont force the usage of the former algorithm to
3787 * determine the height of the last line, which
3790 void SwTextFrame::CalcHeightOfLastLine( const bool _bUseFont
)
3793 // Invalidate printing area, if height of last line changes
3794 const SwTwips
nOldHeightOfLastLine( mnHeightOfLastLine
);
3796 // determine output device
3797 SwViewShell
* pVsh
= getRootFrame()->GetCurrShell();
3798 OSL_ENSURE( pVsh
, "<SwTextFrame::_GetHeightOfLastLineForPropLineSpacing()> - no SwViewShell" );
3801 // There could be no <SwViewShell> instance in the case of loading a binary
3802 // StarOffice file format containing an embedded Writer document.
3807 OutputDevice
* pOut
= pVsh
->GetOut();
3808 const IDocumentSettingAccess
*const pIDSA
= &GetDoc().getIDocumentSettingAccess();
3809 if ( !pVsh
->GetViewOptions()->getBrowseMode() ||
3810 pVsh
->GetViewOptions()->IsPrtFormat() )
3812 pOut
= GetDoc().getIDocumentDeviceAccess().getReferenceDevice( true );
3814 OSL_ENSURE( pOut
, "<SwTextFrame::_GetHeightOfLastLineForPropLineSpacing()> - no OutputDevice" );
3821 // determine height of last line
3822 if ( _bUseFont
|| pIDSA
->get(DocumentSettingId::OLD_LINE_SPACING
) )
3824 // former determination of last line height for proportional line
3825 // spacing - take height of font set at the paragraph
3826 // FIXME actually... must the font match across all nodes?
3827 SwFont
aFont( &GetTextNodeForParaProps()->GetSwAttrSet(), pIDSA
);
3829 // we must ensure that the font is restored correctly on the OutputDevice
3830 // otherwise Last!=Owner could occur
3833 SwFntObj
*pOldFont
= pLastFont
;
3834 pLastFont
= nullptr;
3835 aFont
.SetFntChg( true );
3836 aFont
.ChgPhysFnt( pVsh
, *pOut
);
3837 mnHeightOfLastLine
= aFont
.GetHeight( pVsh
, *pOut
);
3838 assert(pLastFont
&& "coverity[var_deref_model] - pLastFont should be set in SwSubFont::ChgFnt");
3839 pLastFont
->Unlock();
3840 pLastFont
= pOldFont
;
3841 pLastFont
->SetDevFont( pVsh
, *pOut
);
3845 vcl::Font aOldFont
= pOut
->GetFont();
3846 aFont
.SetFntChg( true );
3847 aFont
.ChgPhysFnt( pVsh
, *pOut
);
3848 mnHeightOfLastLine
= aFont
.GetHeight( pVsh
, *pOut
);
3849 assert(pLastFont
&& "coverity[var_deref_model] - pLastFont should be set in SwSubFont::ChgFnt");
3850 pLastFont
->Unlock();
3851 pLastFont
= nullptr;
3852 pOut
->SetFont( aOldFont
);
3857 // new determination of last line height - take actually height of last line
3859 // assure same results, if paragraph is undersized
3860 if ( IsUndersized() )
3862 mnHeightOfLastLine
= 0;
3866 bool bCalcHeightOfLastLine
= true;
3867 if ( ( !HasPara() && IsEmpty( ) ) || GetText().isEmpty() )
3869 mnHeightOfLastLine
= EmptyHeight();
3870 bCalcHeightOfLastLine
= false;
3873 if ( bCalcHeightOfLastLine
)
3875 OSL_ENSURE( HasPara(),
3876 "<SwTextFrame::CalcHeightOfLastLine()> - missing paragraph portions." );
3877 const SwLineLayout
* pLineLayout
= GetPara();
3878 while ( pLineLayout
&& pLineLayout
->GetNext() )
3880 // iteration to last line
3881 pLineLayout
= pLineLayout
->GetNext();
3885 SwTwips nAscent
, nDescent
, nDummy1
, nDummy2
;
3886 // i#47162 - suppress consideration of
3887 // fly content portions and the line portion.
3888 pLineLayout
->MaxAscentDescent( nAscent
, nDescent
,
3892 // Suppress wrong invalidation of printing area, if method is
3893 // called recursive.
3894 // Thus, member <mnHeightOfLastLine> is only set directly, if
3895 // no recursive call is needed.
3896 const SwTwips nNewHeightOfLastLine
= nAscent
+ nDescent
;
3897 // i#47162 - if last line only contains
3898 // fly content portions, <mnHeightOfLastLine> is zero.
3899 // In this case determine height of last line by the font
3900 if ( nNewHeightOfLastLine
== 0 )
3902 CalcHeightOfLastLine( true );
3906 mnHeightOfLastLine
= nNewHeightOfLastLine
;
3913 // invalidate printing area, if height of last line changes
3914 if ( mnHeightOfLastLine
!= nOldHeightOfLastLine
)
3921 * Method returns the value of the inter line spacing for a text frame.
3922 * Such a value exists for proportional line spacings ("1,5 Lines",
3923 * "Double", "Proportional" and for leading line spacing ("Leading").
3925 * @param _bNoPropLineSpacing (default = false) control whether the
3926 * value of a proportional line spacing is
3929 tools::Long
SwTextFrame::GetLineSpace( const bool _bNoPropLineSpace
) const
3931 tools::Long nRet
= 0;
3933 const SvxLineSpacingItem
&rSpace
= GetTextNodeForParaProps()->GetSwAttrSet().GetLineSpacing();
3935 switch( rSpace
.GetInterLineSpaceRule() )
3937 case SvxInterLineSpaceRule::Prop
:
3939 if ( _bNoPropLineSpace
)
3944 // i#11860 - adjust spacing implementation for object positioning
3945 // - compatibility to MS Word
3946 nRet
= GetHeightOfLastLine();
3948 tools::Long nTmp
= nRet
;
3949 nTmp
*= rSpace
.GetPropLineSpace();
3958 case SvxInterLineSpaceRule::Fix
:
3960 if ( rSpace
.GetInterLineSpace() > 0 )
3961 nRet
= rSpace
.GetInterLineSpace();
3970 SwTwips
SwTextFrame::FirstLineHeight() const
3974 if( IsEmpty() && isFrameAreaDefinitionValid() )
3975 return IsVertical() ? getFramePrintArea().Width() : getFramePrintArea().Height();
3976 return std::numeric_limits
<SwTwips
>::max();
3978 const SwParaPortion
*pPara
= GetPara();
3980 return std::numeric_limits
<SwTwips
>::max();
3982 // tdf#146500 Lines with only fly overlap cannot be "moved", so the idea
3983 // here is to continue until there's some text.
3984 // FIXME ideally we want to count a fly to the line in which it is anchored
3985 // - it may even be anchored in some other paragraph! SwFlyPortion doesn't
3986 // have a pointer sadly so no way to find out.
3988 for (SwLineLayout
const* pLine
= pPara
; pLine
; pLine
= pLine
->GetNext())
3990 nHeight
+= pLine
->Height();
3991 if (::sw::FindNonFlyPortion(*pLine
))
3999 sal_Int32
SwTextFrame::GetLineCount(TextFrameIndex
const nPos
)
4002 SwTextFrame
*pFrame
= this;
4005 pFrame
->GetFormatted();
4006 if( !pFrame
->HasPara() )
4008 SwTextSizeInfo
aInf( pFrame
);
4009 SwTextMargin
aLine( pFrame
, &aInf
);
4010 if (TextFrameIndex(COMPLETE_STRING
) == nPos
)
4013 aLine
.CharToLine( nPos
);
4014 nRet
= nRet
+ aLine
.GetLineNr();
4015 pFrame
= pFrame
->GetFollow();
4016 } while ( pFrame
&& pFrame
->GetOffset() <= nPos
);
4020 void SwTextFrame::ChgThisLines()
4022 // not necessary to format here (GetFormatted etc.), because we have to come from there!
4024 const SwLineNumberInfo
&rInf
= GetDoc().GetLineNumberInfo();
4025 if ( !GetText().isEmpty() && HasPara() )
4027 SwTextSizeInfo
aInf( this );
4028 SwTextMargin
aLine( this, &aInf
);
4029 if ( rInf
.IsCountBlankLines() )
4032 nNew
= aLine
.GetLineNr();
4038 if( aLine
.GetCurr()->HasContent() )
4040 } while ( aLine
.NextLine() );
4043 else if ( rInf
.IsCountBlankLines() )
4046 if ( nNew
== mnThisLines
)
4049 if (!IsInTab() && GetTextNodeForParaProps()->GetSwAttrSet().GetLineNumber().IsCount())
4051 mnAllLines
-= mnThisLines
;
4053 mnAllLines
+= mnThisLines
;
4054 SwFrame
*pNxt
= GetNextContentFrame();
4055 while( pNxt
&& pNxt
->IsInTab() )
4057 pNxt
= pNxt
->FindTabFrame();
4058 if( nullptr != pNxt
)
4059 pNxt
= pNxt
->FindNextCnt();
4062 pNxt
->InvalidateLineNum();
4064 // Extend repaint to the bottom.
4067 SwRepaint
& rRepaint
= GetPara()->GetRepaint();
4068 rRepaint
.Bottom( std::max( rRepaint
.Bottom(),
4069 getFrameArea().Top()+getFramePrintArea().Bottom()));
4072 else // Paragraphs which are not counted should not manipulate the AllLines.
4076 void SwTextFrame::RecalcAllLines()
4083 const sal_Int32 nOld
= GetAllLines();
4084 const SwFormatLineNumber
&rLineNum
= GetTextNodeForParaProps()->GetSwAttrSet().GetLineNumber();
4086 const bool bRestart
= GetDoc().GetLineNumberInfo().IsRestartEachPage();
4088 if ( !IsFollow() && rLineNum
.GetStartValue() && rLineNum
.IsCount() )
4089 nNewNum
= rLineNum
.GetStartValue() - 1;
4090 // If it is a follow or not has not be considered if it is a restart at each page; the
4091 // restart should also take effect at follows.
4092 else if ( bRestart
&& FindPageFrame()->FindFirstBodyContent() == this )
4098 SwContentFrame
*pPrv
= GetPrevContentFrame();
4100 (pPrv
->IsInTab() || pPrv
->IsInDocBody() != IsInDocBody()) )
4101 pPrv
= pPrv
->GetPrevContentFrame();
4103 // i#78254 Restart line numbering at page change
4104 // First body content may be in table!
4105 if ( bRestart
&& pPrv
&& pPrv
->FindPageFrame() != FindPageFrame() )
4108 nNewNum
= pPrv
? static_cast<SwTextFrame
*>(pPrv
)->GetAllLines() : 0;
4110 if ( rLineNum
.IsCount() )
4111 nNewNum
+= GetThisLines();
4113 if ( nOld
== nNewNum
)
4116 mnAllLines
= nNewNum
;
4117 SwContentFrame
*pNxt
= GetNextContentFrame();
4119 (pNxt
->IsInTab() || pNxt
->IsInDocBody() != IsInDocBody()) )
4120 pNxt
= pNxt
->GetNextContentFrame();
4123 if ( pNxt
->GetUpper() != GetUpper() )
4124 pNxt
->InvalidateLineNum();
4126 pNxt
->InvalidateLineNum_();
4130 void SwTextFrame::VisitPortions( SwPortionHandler
& rPH
) const
4132 const SwParaPortion
* pPara
= isFrameAreaDefinitionValid() ? GetPara() : nullptr;
4137 rPH
.Skip( GetOffset() );
4139 const SwLineLayout
* pLine
= pPara
;
4142 const SwLinePortion
* pPor
= pLine
->GetFirstPortion();
4145 pPor
->HandlePortion( rPH
);
4146 pPor
= pPor
->GetNextPortion();
4150 pLine
= pLine
->GetNext();
4157 const SwScriptInfo
* SwTextFrame::GetScriptInfo() const
4159 const SwParaPortion
* pPara
= GetPara();
4160 return pPara
? &pPara
->GetScriptInfo() : nullptr;
4164 * Helper function for SwTextFrame::CalcBasePosForFly()
4166 static SwTwips
lcl_CalcFlyBasePos( const SwTextFrame
& rFrame
, SwRect aFlyRect
,
4167 SwTextFly
const & rTextFly
)
4169 SwRectFnSet
aRectFnSet(&rFrame
);
4170 SwTwips nRet
= rFrame
.IsRightToLeft() ?
4171 aRectFnSet
.GetRight(rFrame
.getFrameArea()) :
4172 aRectFnSet
.GetLeft(rFrame
.getFrameArea());
4176 SwRect aRect
= rTextFly
.GetFrame( aFlyRect
);
4177 if ( 0 != aRectFnSet
.GetWidth(aRect
) )
4179 if ( rFrame
.IsRightToLeft() )
4181 if ( aRectFnSet
.GetRight(aRect
) -
4182 aRectFnSet
.GetRight(aFlyRect
) >= 0 )
4184 aRectFnSet
.SetRight(
4185 aFlyRect
, aRectFnSet
.GetLeft(aRect
) );
4186 nRet
= aRectFnSet
.GetLeft(aRect
);
4193 if ( aRectFnSet
.GetLeft(aFlyRect
) -
4194 aRectFnSet
.GetLeft(aRect
) >= 0 )
4197 aFlyRect
, aRectFnSet
.GetRight(aRect
) + 1 );
4198 nRet
= aRectFnSet
.GetRight(aRect
);
4207 while ( aRectFnSet
.GetWidth(aFlyRect
) > 0 );
4212 void SwTextFrame::CalcBaseOfstForFly()
4214 OSL_ENSURE( !IsVertical() || !IsSwapped(),
4215 "SwTextFrame::CalcBasePosForFly with swapped frame!" );
4217 if (!GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::ADD_FLY_OFFSETS
))
4220 SwRectFnSet
aRectFnSet(this);
4222 SwRect
aFlyRect( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() );
4224 // Get first 'real' line and adjust position and height of line rectangle.
4225 // Correct behaviour if no 'real' line exists
4226 // (empty paragraph with and without a dummy portion)
4227 SwTwips nFlyAnchorVertOfstNoWrap
= 0;
4229 SwTwips nTop
= aRectFnSet
.GetTop(aFlyRect
);
4230 const SwLineLayout
* pLay
= GetPara();
4231 SwTwips nLineHeight
= 200;
4232 while( pLay
&& pLay
->IsDummy() && pLay
->GetNext() )
4234 nTop
+= pLay
->Height();
4235 nFlyAnchorVertOfstNoWrap
+= pLay
->Height();
4236 pLay
= pLay
->GetNext();
4240 nLineHeight
= pLay
->Height();
4242 aRectFnSet
.SetTopAndHeight( aFlyRect
, nTop
, nLineHeight
);
4245 SwTextFly
aTextFly( this );
4246 aTextFly
.SetIgnoreCurrentFrame( true );
4247 aTextFly
.SetIgnoreContour( true );
4248 // ignore objects in page header|footer for
4249 // text frames not in page header|footer
4250 aTextFly
.SetIgnoreObjsInHeaderFooter( true );
4251 SwTwips nRet1
= lcl_CalcFlyBasePos( *this, aFlyRect
, aTextFly
);
4252 aTextFly
.SetIgnoreCurrentFrame( false );
4253 SwTwips nRet2
= lcl_CalcFlyBasePos( *this, aFlyRect
, aTextFly
);
4255 // make values relative to frame start position
4256 SwTwips nLeft
= IsRightToLeft() ?
4257 aRectFnSet
.GetRight(getFrameArea()) :
4258 aRectFnSet
.GetLeft(getFrameArea());
4260 mnFlyAnchorOfst
= nRet1
- nLeft
;
4261 mnFlyAnchorOfstNoWrap
= nRet2
- nLeft
;
4263 if (!GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS
))
4266 if (mnFlyAnchorOfstNoWrap
> 0)
4267 mnFlyAnchorVertOfstNoWrap
= nFlyAnchorVertOfstNoWrap
;
4270 SwTwips
SwTextFrame::GetBaseVertOffsetForFly(bool bIgnoreFlysAnchoredAtThisFrame
) const
4272 return bIgnoreFlysAnchoredAtThisFrame
? 0 : mnFlyAnchorVertOfstNoWrap
;
4276 * Repaint all text frames of the given text node
4278 void SwTextFrame::repaintTextFrames( const SwTextNode
& rNode
)
4280 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(rNode
);
4281 for( const SwTextFrame
*pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next() )
4283 SwRect
aRec( pFrame
->GetPaintArea() );
4284 const SwRootFrame
*pRootFrame
= pFrame
->getRootFrame();
4285 SwViewShell
*pCurShell
= pRootFrame
? pRootFrame
->GetCurrShell() : nullptr;
4287 pCurShell
->InvalidateWindows( aRec
);
4291 void SwTextFrame::UpdateOutlineContentVisibilityButton(SwWrtShell
* pWrtSh
) const
4293 if (pWrtSh
&& pWrtSh
->GetViewOptions()->IsShowOutlineContentVisibilityButton() &&
4294 GetTextNodeFirst()->IsOutline())
4296 SwEditWin
& rEditWin
= pWrtSh
->GetView().GetEditWin();
4297 SwFrameControlsManager
& rMngr
= rEditWin
.GetFrameControlsManager();
4298 rMngr
.SetOutlineContentVisibilityButton(this);
4302 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */