/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "itrpaint.hxx" #include "txtpaint.hxx" #include "txtcache.hxx" #include #include "redlnitr.hxx" #include #include #include #include #include #include #include #include #define REDLINE_DISTANCE 567/4 #define REDLINE_MINDIST 567/10 using namespace ::com::sun::star; static bool bInitFont = true; namespace { class SwExtraPainter { SwSaveClip m_aClip; SwRect m_aRect; const SwTextFrame* m_pTextFrame; SwViewShell *m_pSh; std::unique_ptr m_pFnt; const SwLineNumberInfo &m_rLineInf; SwTwips m_nX; SwTwips m_nRedX; sal_uLong m_nLineNr; sal_uInt16 m_nDivider; bool m_bGoLeft; bool IsClipChg() const { return m_aClip.IsChg(); } SwExtraPainter(const SwExtraPainter&) = delete; SwExtraPainter& operator=(const SwExtraPainter&) = delete; public: SwExtraPainter( const SwTextFrame *pFrame, SwViewShell *pVwSh, const SwLineNumberInfo &rLnInf, const SwRect &rRct, sal_Int16 eHor, bool bLnNm ); SwFont* GetFont() const { return m_pFnt.get(); } void IncLineNr() { ++m_nLineNr; } bool HasNumber() const { assert( m_rLineInf.GetCountBy() != 0 ); if( m_rLineInf.GetCountBy() == 0 ) return false; return !( m_nLineNr % m_rLineInf.GetCountBy() ); } bool HasDivider() const { assert( m_rLineInf.GetDividerCountBy() != 0 ); if( !m_nDivider || m_rLineInf.GetDividerCountBy() == 0 ) return false; return !(m_nLineNr % m_rLineInf.GetDividerCountBy()); } void PaintExtra( SwTwips nY, tools::Long nAsc, tools::Long nMax, bool bRed, const OUString* pRedlineText = nullptr ); void PaintRedline( SwTwips nY, tools::Long nMax ); }; } SwExtraPainter::SwExtraPainter( const SwTextFrame *pFrame, SwViewShell *pVwSh, const SwLineNumberInfo &rLnInf, const SwRect &rRct, sal_Int16 eHor, bool bLineNum ) : m_aClip( pVwSh->GetWin() || pFrame->IsUndersized() ? pVwSh->GetOut() : nullptr ) , m_aRect( rRct ) , m_pTextFrame( pFrame ) , m_pSh( pVwSh ) , m_rLineInf( rLnInf ) , m_nX(0) , m_nRedX(0) , m_nLineNr( 1 ) , m_nDivider(0) , m_bGoLeft(false) { if( pFrame->IsUndersized() ) { SwTwips nBottom = pFrame->getFrameArea().Bottom(); if( m_aRect.Bottom() > nBottom ) m_aRect.Bottom( nBottom ); } std::optional oIsRightPage; { /* Initializes the Members necessary for line numbering: nDivider, how often do we want a substring; 0 == never nX, line number's x position pFnt, line number's font nLineNr, the first line number bLineNum is set back to false if the numbering is completely outside of the paint rect */ m_nDivider = !m_rLineInf.GetDivider().isEmpty() ? m_rLineInf.GetDividerCountBy() : 0; m_nX = pFrame->getFrameArea().Left(); SwCharFormat* pFormat = m_rLineInf.GetCharFormat( const_cast(pFrame->GetDoc().getIDocumentStylePoolAccess()) ); OSL_ENSURE( pFormat, "PaintExtraData without CharFormat" ); m_pFnt.reset( new SwFont(&pFormat->GetAttrSet(), &pFrame->GetDoc().getIDocumentSettingAccess()) ); m_pFnt->Invalidate(); m_pFnt->ChgPhysFnt( m_pSh, *m_pSh->GetOut() ); m_pFnt->SetVertical( 0_deg10, pFrame->IsVertical() ); } if( bLineNum ) { m_nLineNr += pFrame->GetAllLines() - pFrame->GetThisLines(); LineNumberPosition ePos = m_rLineInf.GetPos(); if( ePos != LINENUMBER_POS_LEFT && ePos != LINENUMBER_POS_RIGHT ) { if( pFrame->FindPageFrame()->OnRightPage() ) { oIsRightPage = true; ePos = ePos == LINENUMBER_POS_INSIDE ? LINENUMBER_POS_LEFT : LINENUMBER_POS_RIGHT; } else { oIsRightPage = false; ePos = ePos == LINENUMBER_POS_OUTSIDE ? LINENUMBER_POS_LEFT : LINENUMBER_POS_RIGHT; } } if( LINENUMBER_POS_LEFT == ePos ) { m_bGoLeft = true; m_nX -= m_rLineInf.GetPosFromLeft(); } else { m_bGoLeft = false; m_nX += pFrame->getFrameArea().Width() + m_rLineInf.GetPosFromLeft(); } } if( eHor == text::HoriOrientation::NONE ) return; if( text::HoriOrientation::INSIDE == eHor || text::HoriOrientation::OUTSIDE == eHor ) { if (!oIsRightPage) oIsRightPage = pFrame->FindPageFrame()->OnRightPage(); if (*oIsRightPage) eHor = eHor == text::HoriOrientation::INSIDE ? text::HoriOrientation::LEFT : text::HoriOrientation::RIGHT; else eHor = eHor == text::HoriOrientation::OUTSIDE ? text::HoriOrientation::LEFT : text::HoriOrientation::RIGHT; } const SwFrame* pTmpFrame = pFrame->FindTabFrame(); if( !pTmpFrame ) pTmpFrame = pFrame; m_nRedX = text::HoriOrientation::LEFT == eHor ? pTmpFrame->getFrameArea().Left() - REDLINE_DISTANCE : pTmpFrame->getFrameArea().Right() + REDLINE_DISTANCE; } void SwExtraPainter::PaintExtra( SwTwips nY, tools::Long nAsc, tools::Long nMax, bool bRed, const OUString* pRedlineText ) { const OUString aTmp( pRedlineText // Tracked change is stronger than the line number ? *pRedlineText : ( HasNumber() // Line number is stronger than the divider ? m_rLineInf.GetNumType().GetNumStr( m_nLineNr ) : m_rLineInf.GetDivider() ) ); // Get script type of line numbering: m_pFnt->SetActual( SwScriptInfo::WhichFont(0, aTmp) ); if ( pRedlineText ) { m_pFnt->SetColor(NON_PRINTING_CHARACTER_COLOR); // don't strike out text in Insertions In Margin mode if ( !m_pSh->GetViewOptions()->IsShowChangesInMargin2() ) m_pFnt->SetStrikeout( STRIKEOUT_SINGLE ); m_pFnt->SetSize( Size( 0, 200), m_pFnt->GetActual() ); } SwDrawTextInfo aDrawInf( m_pSh, *m_pSh->GetOut(), aTmp, 0, aTmp.getLength() ); aDrawInf.SetSpace( 0 ); aDrawInf.SetWrong( nullptr ); aDrawInf.SetGrammarCheck( nullptr ); aDrawInf.SetSmartTags( nullptr ); aDrawInf.SetFrame( m_pTextFrame ); aDrawInf.SetFont( m_pFnt.get() ); aDrawInf.SetSnapToGrid( false ); aDrawInf.SetIgnoreFrameRTL( true ); bool bTooBig = m_pFnt->GetSize( m_pFnt->GetActual() ).Height() > nMax && m_pFnt->GetHeight( m_pSh, *m_pSh->GetOut() ) > nMax; SwFont* pTmpFnt; if( bTooBig ) { pTmpFnt = new SwFont( *GetFont() ); if( nMax >= 20 ) { nMax *= 17; nMax /= 20; } pTmpFnt->SetSize( Size( 0, nMax ), pTmpFnt->GetActual() ); } else pTmpFnt = GetFont(); Point aTmpPos( m_nX, nY ); aTmpPos.AdjustY(nAsc ); if ( pRedlineText ) { Size aSize = pTmpFnt->GetTextSize_( aDrawInf ); aTmpPos.AdjustX( -(aSize.Width()) - 200 ); } bool bPaint = true; if( !IsClipChg() ) { Size aSize = pTmpFnt->GetTextSize_( aDrawInf ); if( m_bGoLeft ) aTmpPos.AdjustX( -(aSize.Width()) ); // calculate rectangle containing the line number SwRect aRct( Point( aTmpPos.X(), aTmpPos.Y() - pTmpFnt->GetAscent( m_pSh, *m_pSh->GetOut() ) ), aSize ); if( !m_aRect.Contains( aRct ) ) { if( aRct.Intersection( m_aRect ).IsEmpty() ) bPaint = false; else m_aClip.ChgClip( m_aRect, m_pTextFrame ); } } else if( m_bGoLeft ) aTmpPos.AdjustX( -(pTmpFnt->GetTextSize_( aDrawInf ).Width()) ); aDrawInf.SetPos( aTmpPos ); if( bPaint ) pTmpFnt->DrawText_( aDrawInf ); if( bTooBig ) delete pTmpFnt; if( bRed ) { tools::Long nDiff = m_bGoLeft ? m_nRedX - m_nX : m_nX - m_nRedX; if( nDiff > REDLINE_MINDIST ) PaintRedline( nY, nMax ); } } void SwExtraPainter::PaintRedline( SwTwips nY, tools::Long nMax ) { Point aStart( m_nRedX, nY ); Point aEnd( m_nRedX, nY + nMax ); if( !IsClipChg() ) { SwRect aRct( aStart, aEnd ); if( !m_aRect.Contains( aRct ) ) { if( aRct.Intersection( m_aRect ).IsEmpty() ) return; m_aClip.ChgClip( m_aRect, m_pTextFrame ); } } const Color aOldCol( m_pSh->GetOut()->GetLineColor() ); m_pSh->GetOut()->SetLineColor( SW_MOD()->GetRedlineMarkColor() ); if ( m_pTextFrame->IsVertical() ) { m_pTextFrame->SwitchHorizontalToVertical( aStart ); m_pTextFrame->SwitchHorizontalToVertical( aEnd ); } m_pSh->GetOut()->DrawLine( aStart, aEnd ); m_pSh->GetOut()->SetLineColor( aOldCol ); } void SwTextFrame::PaintExtraData( const SwRect &rRect ) const { if( getFrameArea().Top() > rRect.Bottom() || getFrameArea().Bottom() < rRect.Top() ) return; PaintOutlineContentVisibilityButton(); SwDoc const& rDoc(GetDoc()); const IDocumentRedlineAccess& rIDRA = rDoc.getIDocumentRedlineAccess(); const SwLineNumberInfo &rLineInf = rDoc.GetLineNumberInfo(); const SwFormatLineNumber &rLineNum = GetAttrSet()->GetLineNumber(); bool bLineNum = !IsInTab() && rLineInf.IsPaintLineNumbers() && ( !IsInFly() || rLineInf.IsCountInFlys() ) && rLineNum.IsCount(); sal_Int16 eHor = static_cast(SW_MOD()->GetRedlineMarkPos()); if (eHor != text::HoriOrientation::NONE && (!IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags()) || getRootFrame()->IsHideRedlines())) { eHor = text::HoriOrientation::NONE; } bool bRedLine = eHor != text::HoriOrientation::NONE; if ( !bLineNum && !bRedLine ) return; if( IsLocked() || IsHiddenNow() || !getFramePrintArea().Height() ) return; SwViewShell *pSh = getRootFrame()->GetCurrShell(); SwSwapIfNotSwapped swap(const_cast(this)); SwRect rOldRect( rRect ); if ( IsVertical() ) SwitchVerticalToHorizontal( const_cast(rRect) ); SwLayoutModeModifier aLayoutModeModifier( *pSh->GetOut() ); aLayoutModeModifier.Modify( false ); // #i16816# tagged pdf support SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pSh->GetOut() ); SwExtraPainter aExtra( this, pSh, rLineInf, rRect, eHor, bLineNum ); if( HasPara() ) { TextFrameLockGuard aLock(const_cast(this)); SwTextLineAccess aAccess( this ); aAccess.GetPara(); SwTextPaintInfo aInf( const_cast(this), rRect ); aLayoutModeModifier.Modify( false ); SwTextPainter aLine( const_cast(this), &aInf ); bool bNoDummy = !aLine.GetNext(); // Only one empty line! while( aLine.Y() + aLine.GetLineHeight() <= rRect.Top() ) { if( !aLine.GetCurr()->IsDummy() && ( rLineInf.IsCountBlankLines() || aLine.GetCurr()->HasContent() ) ) aExtra.IncLineNr(); if( !aLine.Next() ) { const_cast(rRect) = rOldRect; return; } } tools::Long nBottom = rRect.Bottom(); bool bNoPrtLine = 0 == GetMinPrtLine(); if( !bNoPrtLine ) { while ( aLine.Y() < GetMinPrtLine() ) { if( ( rLineInf.IsCountBlankLines() || aLine.GetCurr()->HasContent() ) && !aLine.GetCurr()->IsDummy() ) aExtra.IncLineNr(); if( !aLine.Next() ) break; } bNoPrtLine = aLine.Y() >= GetMinPrtLine(); } const bool bIsShowChangesInMargin = pSh->GetViewOptions()->IsShowChangesInMargin(); if( bNoPrtLine ) { do { if( bNoDummy || !aLine.GetCurr()->IsDummy() ) { bool bRed = bRedLine && aLine.GetCurr()->HasRedline(); if( rLineInf.IsCountBlankLines() || aLine.GetCurr()->HasContent() ) { bool bRedInMargin = bIsShowChangesInMargin && bRed; bool bNum = bLineNum && ( aExtra.HasNumber() || aExtra.HasDivider() ); if( bRedInMargin || bNum ) { SwTwips nTmpHeight, nTmpAscent; aLine.CalcAscentAndHeight( nTmpAscent, nTmpHeight ); if ( bRedInMargin ) { const OUString* pRedlineText = aLine.GetCurr()->GetRedlineText(); if( !pRedlineText->isEmpty() ) { aExtra.PaintExtra( aLine.Y(), nTmpAscent, nTmpHeight, bRed, pRedlineText ); bRed = false; bNum = false; } } if ( bNum ) { aExtra.PaintExtra( aLine.Y(), nTmpAscent, nTmpHeight, bRed ); bRed = false; } } aExtra.IncLineNr(); } if( bRed ) aExtra.PaintRedline( aLine.Y(), aLine.GetLineHeight() ); } } while( aLine.Next() && aLine.Y() <= nBottom ); } } else { if (SwRedlineTable::npos == rIDRA.GetRedlinePos(*GetTextNodeFirst(), RedlineType::Any)) { bRedLine = false; } if( bLineNum && rLineInf.IsCountBlankLines() && ( aExtra.HasNumber() || aExtra.HasDivider() ) ) { aExtra.PaintExtra( getFrameArea().Top()+getFramePrintArea().Top(), aExtra.GetFont() ->GetAscent( pSh, *pSh->GetOut() ), getFramePrintArea().Height(), bRedLine ); } else if( bRedLine ) aExtra.PaintRedline( getFrameArea().Top()+getFramePrintArea().Top(), getFramePrintArea().Height() ); } const_cast(rRect) = rOldRect; } SwRect SwTextFrame::GetPaintSwRect() { // finger layout OSL_ENSURE( isFrameAreaPositionValid(), "+SwTextFrame::GetPaintSwRect: no Calc()" ); SwRect aRet( getFramePrintArea() ); if ( IsEmpty() || !HasPara() ) aRet += getFrameArea().Pos(); else { // We return the right paint rect. Use the calculated PaintOfst as the // left margin SwRepaint& rRepaint = GetPara()->GetRepaint(); tools::Long l; if ( IsVertLR() && !IsVertLRBT()) // mba: the following line was added, but we don't need it for the existing directions; kept for IsVertLR(), but should be checked rRepaint.Chg( GetUpper()->getFrameArea().Pos() + GetUpper()->getFramePrintArea().Pos(), GetUpper()->getFramePrintArea().SSize() ); if( rRepaint.GetOffset() ) rRepaint.Left( rRepaint.GetOffset() ); l = rRepaint.GetRightOfst(); if( l && l > rRepaint.Right() ) rRepaint.Right( l ); rRepaint.SetOffset( 0 ); aRet = rRepaint; // In case our left edge is the same as the body frame's left edge, // then extend the rectangle to include the page margin as well, // otherwise some font will be clipped. SwLayoutFrame* pBodyFrame = GetUpper(); if (pBodyFrame->IsBodyFrame() && aRet.Left() == (pBodyFrame->getFrameArea().Left() + pBodyFrame->getFramePrintArea().Left())) if (SwLayoutFrame* pPageFrame = pBodyFrame->GetUpper()) aRet.Left(pPageFrame->getFrameArea().Left()); if ( IsRightToLeft() ) SwitchLTRtoRTL( aRet ); if ( IsVertical() ) SwitchHorizontalToVertical( aRet ); } ResetRepaint(); return aRet; } bool SwTextFrame::PaintEmpty( const SwRect &rRect, bool bCheck ) const { SwViewShell *pSh = getRootFrame()->GetCurrShell(); if( pSh && ( pSh->GetViewOptions()->IsParagraph() || bInitFont ) ) { bInitFont = false; SwTextFly aTextFly( this ); aTextFly.SetTopRule(); SwRect aRect; if( bCheck && aTextFly.IsOn() && aTextFly.IsAnyObj( aRect ) ) return false; else if( pSh->GetWin() ) { std::unique_ptr pFnt; RedlineType eRedline = RedlineType::None; const SwTextNode& rTextNode = *GetTextNodeForParaProps(); if ( rTextNode.HasSwAttrSet() ) { const SwAttrSet *pAttrSet = &( rTextNode.GetSwAttrSet() ); pFnt.reset(new SwFont( pAttrSet, rTextNode.getIDocumentSettingAccess() )); } else { SwFontAccess aFontAccess( &rTextNode.GetAnyFormatColl(), pSh ); pFnt.reset(new SwFont( aFontAccess.Get()->GetFont() )); } const IDocumentRedlineAccess& rIDRA = rTextNode.getIDocumentRedlineAccess(); if (IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags()) && !getRootFrame()->IsHideRedlines()) { const SwRedlineTable::size_type nRedlPos = rIDRA.GetRedlinePos( rTextNode, RedlineType::Any ); if( SwRedlineTable::npos != nRedlPos ) { SwAttrHandler aAttrHandler; aAttrHandler.Init( rTextNode.GetSwAttrSet(), *rTextNode.getIDocumentSettingAccess() ); SwRedlineItr aRedln(rTextNode, *pFnt, aAttrHandler, nRedlPos, SwRedlineItr::Mode::Show); const SwRangeRedline* pRedline = rIDRA.GetRedlineTable()[nRedlPos]; // show redlining only on the inserted/deleted empty paragraph, but not on the next one if ( rTextNode.GetIndex() != pRedline->End()->GetNodeIndex() ) eRedline = pRedline->GetType(); // except if the next empty paragraph starts a new redline (e.g. deletion after insertion) else if ( nRedlPos + 1 < rIDRA.GetRedlineTable().size() ) { const SwRangeRedline* pNextRedline = rIDRA.GetRedlineTable()[nRedlPos + 1]; if ( rTextNode.GetIndex() == pNextRedline->Start()->GetNodeIndex() ) eRedline = pNextRedline->GetType(); } } } if( pSh->GetViewOptions()->IsParagraph() && getFramePrintArea().Height() ) { if( RTL_TEXTENCODING_SYMBOL == pFnt->GetCharSet( SwFontScript::Latin ) && pFnt->GetName( SwFontScript::Latin ) != numfunc::GetDefBulletFontname() ) { pFnt->SetFamily( FAMILY_DONTKNOW, SwFontScript::Latin ); pFnt->SetName( numfunc::GetDefBulletFontname(), SwFontScript::Latin ); pFnt->SetStyleName(OUString(), SwFontScript::Latin); pFnt->SetCharSet( RTL_TEXTENCODING_SYMBOL, SwFontScript::Latin ); } pFnt->SetVertical( 0_deg10, IsVertical() ); SwFrameSwapper aSwapper( this, true ); SwLayoutModeModifier aLayoutModeModifier( *pSh->GetOut() ); aLayoutModeModifier.Modify( IsRightToLeft() ); pFnt->Invalidate(); pFnt->ChgPhysFnt( pSh, *pSh->GetOut() ); Point aPos = getFrameArea().Pos() + getFramePrintArea().Pos(); const SvxLRSpaceItem &rSpace = GetTextNodeForParaProps()->GetSwAttrSet().GetLRSpace(); if ( rSpace.GetTextFirstLineOffset() > 0 ) aPos.AdjustX(rSpace.GetTextFirstLineOffset() ); std::unique_ptr> xClip; if( IsUndersized() ) { xClip.reset(new SwSaveClip( pSh->GetOut() )); xClip->ChgClip( rRect ); } aPos.AdjustY(pFnt->GetAscent( pSh, *pSh->GetOut() ) ); if (GetTextNodeForParaProps()->GetSwAttrSet().GetParaGrid().GetValue() && IsInDocBody() ) { SwTextGridItem const*const pGrid(GetGridItem(FindPageFrame())); if ( pGrid ) { // center character in grid line aPos.AdjustY(( pGrid->GetBaseHeight() - pFnt->GetHeight( pSh, *pSh->GetOut() ) ) / 2 ); if ( ! pGrid->GetRubyTextBelow() ) aPos.AdjustY(pGrid->GetRubyHeight() ); } } // Don't show the paragraph mark for collapsed paragraphs, when they are hidden if ( EmptyHeight( ) > 1 ) { SwDrawTextInfo aDrawInf( pSh, *pSh->GetOut(), CH_PAR, 0, 1 ); aDrawInf.SetPos( aPos ); aDrawInf.SetSpace( 0 ); aDrawInf.SetKanaComp( 0 ); aDrawInf.SetWrong( nullptr ); aDrawInf.SetGrammarCheck( nullptr ); aDrawInf.SetSmartTags( nullptr ); aDrawInf.SetFrame( this ); aDrawInf.SetFont( pFnt.get() ); aDrawInf.SetSnapToGrid( false ); // show redline color and settings drawing a background pilcrow, // but keep also other formattings (with neutral pilcrow color) if ( eRedline != RedlineType::None ) { pFnt->DrawText_( aDrawInf ); if ( eRedline == RedlineType::Delete ) pFnt->SetStrikeout( STRIKEOUT_NONE ); else pFnt->SetUnderline( LINESTYLE_NONE ); } pFnt->SetColor(NON_PRINTING_CHARACTER_COLOR); pFnt->DrawText_( aDrawInf ); } } return true; } } else return true; return false; } void SwTextFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const { ResetRepaint(); // #i16816# tagged pdf support SwViewShell *pSh = getRootFrame()->GetCurrShell(); Num_Info aNumInfo( *this ); SwTaggedPDFHelper aTaggedPDFHelperNumbering( &aNumInfo, nullptr, nullptr, rRenderContext ); Frame_Info aFrameInfo( *this ); SwTaggedPDFHelper aTaggedPDFHelperParagraph( nullptr, &aFrameInfo, nullptr, rRenderContext ); if( IsEmpty() && PaintEmpty( rRect, true ) ) return; if( IsLocked() || IsHiddenNow() || ! getFramePrintArea().HasArea() ) return; // It can happen that the IdleCollector withdrew my cached information if( !HasPara() ) { OSL_ENSURE( isFrameAreaPositionValid(), "+SwTextFrame::PaintSwFrame: no Calc()" ); // #i29062# pass info that we are currently // painting. const_cast(this)->GetFormatted( true ); if( IsEmpty() ) { PaintEmpty( rRect, false ); return; } if( !HasPara() ) { OSL_ENSURE( false, "+SwTextFrame::PaintSwFrame: missing format information" ); return; } } // We don't want to be interrupted while painting. // Do that after thr Format()! TextFrameLockGuard aLock(const_cast(this)); // We only paint the part of the TextFrame which changed, is within the // range and was requested to paint. // One could think that the area rRect _needs_ to be painted, although // rRepaint is set. Indeed, we cannot avoid this problem from a formal // perspective. Luckily we can assume rRepaint to be empty when we need // paint the while Frame. SwTextLineAccess aAccess( this ); SwParaPortion *pPara = aAccess.GetPara(); SwRepaint &rRepaint = pPara->GetRepaint(); // Switch off recycling when in the FlyContentFrame. // A DrawRect is called for repainting the line anyways. if( rRepaint.GetOffset() ) { const SwFlyFrame *pFly = FindFlyFrame(); if( pFly && pFly->IsFlyInContentFrame() ) rRepaint.SetOffset( 0 ); } // Ge the String for painting. The length is of special interest. // Rectangle OSL_ENSURE( ! IsSwapped(), "A frame is swapped before Paint" ); SwRect aOldRect( rRect ); { SwSwapIfNotSwapped swap(const_cast(this)); if ( IsVertical() ) SwitchVerticalToHorizontal( const_cast(rRect) ); if ( IsRightToLeft() ) SwitchRTLtoLTR( const_cast(rRect) ); SwTextPaintInfo aInf( const_cast(this), rRect ); sw::WrongListIterator iterWrong(*this, &SwTextNode::GetWrong); sw::WrongListIterator iterGrammar(*this, &SwTextNode::GetGrammarCheck); sw::WrongListIterator iterSmartTags(*this, &SwTextNode::GetSmartTags); if (iterWrong.LooksUseful()) { aInf.SetWrongList( &iterWrong ); } if (iterGrammar.LooksUseful()) { aInf.SetGrammarCheckList( &iterGrammar ); } if (iterSmartTags.LooksUseful()) { aInf.SetSmartTags( &iterSmartTags ); } aInf.GetTextFly().SetTopRule(); SwTextPainter aLine( const_cast(this), &aInf ); // Optimization: if no free flying Frame overlaps into our line, the // SwTextFly just switches off aInf.GetTextFly().Relax(); OutputDevice* pOut = aInf.GetOut(); const bool bOnWin = pSh->GetWin() != nullptr; SwSaveClip aClip( bOnWin || IsUndersized() ? pOut : nullptr ); // Output loop: For each Line ... (which is still visible) ... // adapt rRect (Top + 1, Bottom - 1) // Because the Iterator attaches the Lines without a gap to each other aLine.TwipsToLine( rRect.Top() + 1 ); tools::Long nBottom = rRect.Bottom(); bool bNoPrtLine = 0 == GetMinPrtLine(); if( !bNoPrtLine ) { while ( aLine.Y() < GetMinPrtLine() && aLine.Next() ) ; bNoPrtLine = aLine.Y() >= GetMinPrtLine(); } if( bNoPrtLine ) { do { aLine.DrawTextLine( rRect, aClip, IsUndersized() ); } while( aLine.Next() && aLine.Y() <= nBottom ); } // Once is enough: if( aLine.IsPaintDrop() ) aLine.PaintDropPortion(); if( rRepaint.HasArea() ) rRepaint.Clear(); } const_cast(rRect) = aOldRect; OSL_ENSURE( ! IsSwapped(), "A frame is swapped after Paint" ); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */