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