/* -*- 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 <dcontact.hxx>
#include <dflyobj.hxx>
#include <pam.hxx>
#include "portab.hxx"
#include <flyfrm.hxx>
#include <rootfrm.hxx>
#include <frmfmt.hxx>
#include <viewsh.hxx>
#include <textboxhelper.hxx>
#include <IDocumentState.hxx>
#include <sal/log.hxx>
#include <fmtanchr.hxx>
#include <fmtflcnt.hxx>
#include <flyfrms.hxx>
#include <txatbase.hxx>
#include "porfly.hxx"
#include "porlay.hxx"
#include "inftxt.hxx"
#include <sortedobjs.hxx>
#include <officecfg/Office/Common.hxx>
#include <PostItMgr.hxx>
/**
* class SwFlyPortion => we expect a frame-locale SwRect!
*/
void SwFlyPortion::Paint( const SwTextPaintInfo& ) const
{
}
bool SwFlyPortion::Format( SwTextFormatInfo &rInf )
{
OSL_ENSURE( GetFix() >= rInf.X(), "SwFlyPortion::Format" );
// tabs must be expanded
if( rInf.GetLastTab() )
rInf.GetLastTab()->FormatEOL( rInf );
rInf.GetLast()->FormatEOL( rInf );
SetBlankWidth(0);
if (auto blankWidth = rInf.GetLast()->ExtraBlankWidth())
{
// Swallow previous blank width
SetBlankWidth(blankWidth);
rInf.GetLast()->ExtraBlankWidth(0);
rInf.X(rInf.X() - blankWidth); // Step back
}
PrtWidth(GetFix() - rInf.X() + PrtWidth());
if( !Width() )
{
OSL_ENSURE( Width(), "+SwFlyPortion::Format: a fly is a fly is a fly" );
Width(1);
}
// resetting
rInf.SetFly( nullptr );
rInf.Width( rInf.RealWidth() );
rInf.GetParaPortion()->SetFly();
// trailing blank:
if( rInf.GetIdx() < TextFrameIndex(rInf.GetText().getLength())
&& TextFrameIndex(1) < rInf.GetIdx()
&& !rInf.GetRest()
&& ' ' == rInf.GetChar( rInf.GetIdx() )
&& ' ' != rInf.GetChar(rInf.GetIdx() - TextFrameIndex(1))
&& ( !rInf.GetLast() || !rInf.GetLast()->IsBreakPortion() ) )
{
SetBlankWidth(GetBlankWidth() + rInf.GetTextSize(OUString(' ')).Width());
SetLen(TextFrameIndex(1));
}
const SwTwips nNewWidth = rInf.X() + PrtWidth();
if( rInf.Width() <= nNewWidth )
{
Truncate();
if( nNewWidth > rInf.Width() )
{
PrtWidth( nNewWidth - rInf.Width() );
SetFixWidth( PrtWidth() );
}
return true;
}
return false;
}
bool SwFlyCntPortion::Format( SwTextFormatInfo &rInf )
{
bool bFull = rInf.Width() < rInf.X() + PrtWidth();
if( bFull )
{
// If the line is full, and the character-bound frame is at
// the beginning of a line
// If it is not possible to side step into a Fly
// "Begin of line" criteria ( ! rInf.X() ) has to be extended.
// KerningPortions at beginning of line, e.g., for grid layout
// must be considered.
const SwLinePortion* pLastPor = rInf.GetLast();
const auto nLeft = ( pLastPor &&
( pLastPor->IsKernPortion() ||
pLastPor->IsErgoSumPortion() ) ) ?
pLastPor->Width() :
0;
if( nLeft == rInf.X() && ! rInf.GetFly() )
{
Width( rInf.Width() );
bFull = false; // so that notes can still be placed in this line
}
else
{
if( !rInf.GetFly() )
rInf.SetNewLine( true );
Width(0);
SetAscent(0);
SetLen(TextFrameIndex(0));
if( rInf.GetLast() )
rInf.GetLast()->FormatEOL( rInf );
return bFull;
}
}
rInf.GetParaPortion()->SetFly();
return bFull;
}
//TODO: improve documentation
/** move character-bound objects inside the given area
*
* This allows moving those objects from Master to Follow, or vice versa.
*
* @param pNew
* @param nStart
* @param nEnd
*/
void SwTextFrame::MoveFlyInCnt(SwTextFrame *pNew,
TextFrameIndex const nStart, TextFrameIndex const nEnd)
{
SwSortedObjs *pObjs = GetDrawObjs();
if ( nullptr == pObjs )
return;
size_t i = 0;
while (GetDrawObjs() && i < pObjs->size())
{
// Consider changed type of <SwSortedList> entries
SwAnchoredObject* pAnchoredObj = (*pObjs)[i];
const SwFormatAnchor& rAnch = pAnchoredObj->GetFrameFormat()->GetAnchor();
if (rAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
{
const SwPosition* pPos = rAnch.GetContentAnchor();
TextFrameIndex const nIndex(MapModelToViewPos(*pPos));
if (nStart <= nIndex && nIndex < nEnd)
{
if ( auto pFlyFrame = pAnchoredObj->DynCastFlyFrame() )
{
RemoveFly( pFlyFrame );
pNew->AppendFly( pFlyFrame );
}
else
{
RemoveDrawObj( *pAnchoredObj );
pNew->AppendDrawObj( *pAnchoredObj );
}
continue; // pObjs shrunk
}
}
++i;
}
}
TextFrameIndex SwTextFrame::CalcFlyPos( SwFrameFormat const * pSearch )
{
sw::MergedAttrIter iter(*this);
for (SwTextAttr const* pHt = iter.NextAttr(); pHt; pHt = iter.NextAttr())
{
if( RES_TXTATR_FLYCNT == pHt->Which() )
{
SwFrameFormat* pFrameFormat = pHt->GetFlyCnt().GetFrameFormat();
if( pFrameFormat == pSearch )
{
return TextFrameIndex(pHt->GetStart());
}
}
}
OSL_ENSURE(false, "CalcFlyPos: Not Found!");
return TextFrameIndex(COMPLETE_STRING);
}
void sw::FlyContentPortion::Paint(const SwTextPaintInfo& rInf) const
{
// Baseline output
// Re-paint everything at a CompletePaint call
SwRect aRepaintRect(rInf.GetPaintRect());
if(rInf.GetTextFrame()->IsRightToLeft())
rInf.GetTextFrame()->SwitchLTRtoRTL(aRepaintRect);
if(rInf.GetTextFrame()->IsVertical())
rInf.GetTextFrame()->SwitchHorizontalToVertical(aRepaintRect);
if(!((m_pFly->IsCompletePaint() ||
m_pFly->getFrameArea().Overlaps(aRepaintRect)) &&
SwFlyFrame::IsPaint(m_pFly->GetVirtDrawObj(), m_pFly->getRootFrame()->GetCurrShell())))
return;
SwRect aRect(m_pFly->getFrameArea());
if(!m_pFly->IsCompletePaint())
aRect.Intersection_(aRepaintRect);
// GetFlyFrame() may change the layout mode at the output device.
{
SwLayoutModeModifier aLayoutModeModifier(*rInf.GetOut());
m_pFly->PaintSwFrame(const_cast<vcl::RenderContext&>(*rInf.GetOut()), aRect);
// track changes: cross out the image, if it is deleted
const SwFrame *pFrame = m_pFly->Lower();
if ( GetAuthor() != std::string::npos && IsDeleted() && pFrame )
{
SwRect aPaintRect( pFrame->GetPaintArea() );
const AntialiasingFlags nFormerAntialiasing( rInf.GetOut()->GetAntialiasing() );
const bool bIsAntiAliasing = officecfg::Office::Common::Drawinglayer::AntiAliasing::get();
if ( bIsAntiAliasing )
const_cast<vcl::RenderContext&>(*rInf.GetOut()).SetAntialiasing(AntialiasingFlags::Enable);
tools::Long startX = aPaintRect.Left( ), endX = aPaintRect.Right();
tools::Long startY = aPaintRect.Top( ), endY = aPaintRect.Bottom();
const_cast<vcl::RenderContext&>(*rInf.GetOut()).SetLineColor(
SwPostItMgr::GetColorAnchor(GetAuthor()) );
const_cast<vcl::RenderContext&>(*rInf.GetOut()).DrawLine(Point(startX, startY), Point(endX, endY));
const_cast<vcl::RenderContext&>(*rInf.GetOut()).DrawLine(Point(startX, endY), Point(endX, startY));
if ( bIsAntiAliasing )
const_cast<vcl::RenderContext&>(*rInf.GetOut()).SetAntialiasing(nFormerAntialiasing);
}
}
const_cast<SwTextPaintInfo&>(rInf).GetRefDev()->SetLayoutMode(rInf.GetOut()->GetLayoutMode());
// As the OutputDevice might be anything, the font must be re-selected.
// Being in const method should not be a problem.
const_cast<SwTextPaintInfo&>(rInf).SelectFont();
assert(rInf.GetVsh());
SAL_WARN_IF(rInf.GetVsh()->GetOut() != rInf.GetOut(), "sw.core", "SwFlyCntPortion::Paint: Outdev has changed");
if(rInf.GetVsh())
const_cast<SwTextPaintInfo&>(rInf).SetOut(rInf.GetVsh()->GetOut());
}
void sw::DrawFlyCntPortion::Paint(const SwTextPaintInfo&) const
{
if(!m_pContact->GetAnchorFrame())
{
// No direct positioning of the drawing object is needed
m_pContact->ConnectToLayout();
}
}
/**
* Use the dimensions of pFly->OutRect()
*/
SwFlyCntPortion::SwFlyCntPortion()
: m_bMax(false)
, m_bDeleted(false)
, m_nAuthor(std::string::npos)
, m_eAlign(sw::LineAlign::NONE)
{
mnLineLength = TextFrameIndex(1);
SetWhichPor(PortionType::FlyCnt);
}
sw::FlyContentPortion::FlyContentPortion(SwFlyInContentFrame* pFly)
: m_pFly(pFly)
{
SAL_WARN_IF(!pFly, "sw.core", "SwFlyCntPortion::SwFlyCntPortion: no SwFlyInContentFrame!");
}
sw::DrawFlyCntPortion::DrawFlyCntPortion(SwFrameFormat const & rFormat)
: m_pContact(nullptr)
{
rFormat.CallSwClientNotify(sw::CreatePortionHint(&m_pContact));
assert(m_pContact);
}
sw::FlyContentPortion* sw::FlyContentPortion::Create(const SwTextFrame& rFrame, SwFlyInContentFrame* pFly, const Point& rBase, tools::Long nLnAscent, tools::Long nLnDescent, tools::Long nFlyAsc, tools::Long nFlyDesc, AsCharFlags nFlags)
{
auto pNew(new sw::FlyContentPortion(pFly));
pNew->SetBase(rFrame, rBase, nLnAscent, nLnDescent, nFlyAsc, nFlyDesc, nFlags | AsCharFlags::UlSpace | AsCharFlags::Init);
return pNew;
}
sw::DrawFlyCntPortion* sw::DrawFlyCntPortion::Create(const SwTextFrame& rFrame, SwFrameFormat const & rFormat, const Point& rBase, tools::Long nLnAscent, tools::Long nLnDescent, tools::Long nFlyAsc, tools::Long nFlyDesc, AsCharFlags nFlags)
{
auto pNew(new DrawFlyCntPortion(rFormat));
pNew->SetBase(rFrame, rBase, nLnAscent, nLnDescent, nFlyAsc, nFlyDesc, nFlags | AsCharFlags::UlSpace | AsCharFlags::Init);
return pNew;
}
sw::DrawFlyCntPortion::~DrawFlyCntPortion() {};
sw::FlyContentPortion::~FlyContentPortion() {};
SdrObject* sw::FlyContentPortion::GetSdrObj(const SwTextFrame&)
{
return m_pFly->GetVirtDrawObj();
}
SdrObject* sw::DrawFlyCntPortion::GetSdrObj(const SwTextFrame& rFrame)
{
SdrObject* pSdrObj;
// Determine drawing object ('master' or 'virtual') by frame
pSdrObj = m_pContact->GetDrawObjectByAnchorFrame(rFrame);
if(!pSdrObj)
{
SAL_WARN("sw.core", "SwFlyCntPortion::SetBase(..) - No drawing object found by <GetDrawContact()->GetDrawObjectByAnchorFrame( rFrame )>");
pSdrObj = m_pContact->GetMaster();
}
// Call <SwAnchoredDrawObject::MakeObjPos()> to assure that flag at
// the <DrawFrameFormat> and at the <SwAnchoredDrawObject> instance are
// correctly set
if(pSdrObj)
m_pContact->GetAnchoredObj(pSdrObj)->MakeObjPos();
return pSdrObj;
}
/**
* After setting the RefPoints, the ascent needs to be recalculated
* because it is dependent on RelPos
*
* @param rBase CAUTION: needs to be an absolute value!
*/
void SwFlyCntPortion::SetBase( const SwTextFrame& rFrame, const Point &rBase,
tools::Long nLnAscent, tools::Long nLnDescent,
tools::Long nFlyAsc, tools::Long nFlyDesc,
AsCharFlags nFlags )
{
// Use new class to position object
// Determine drawing object
SdrObject* pSdrObj = GetSdrObj(rFrame);
if (!pSdrObj)
return;
// position object
objectpositioning::SwAsCharAnchoredObjectPosition aObjPositioning(
*pSdrObj,
rBase, nFlags,
nLnAscent, nLnDescent, nFlyAsc, nFlyDesc );
// Scope of local variable <aObjPosInProgress>
{
SwObjPositioningInProgress aObjPosInProgress( *pSdrObj );
aObjPositioning.CalcPosition();
}
if (auto pFormat = FindFrameFormat(pSdrObj))
{
if (pFormat->GetOtherTextBoxFormats()
&& pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
{
// TODO: Improve security with moving this sync call to other place,
// where it works for typing but not during layout calc.
const bool bModified = pFormat->GetDoc()->getIDocumentState().IsEnableSetModified();
pFormat->GetDoc()->getIDocumentState().SetEnableSetModified(false);
SwTextBoxHelper::synchronizeGroupTextBoxProperty(SwTextBoxHelper::changeAnchor, pFormat,
pFormat->FindRealSdrObject());
SwTextBoxHelper::synchronizeGroupTextBoxProperty(SwTextBoxHelper::syncTextBoxSize,
pFormat, pFormat->FindRealSdrObject());
pFormat->GetDoc()->getIDocumentState().SetEnableSetModified(bModified);
}
}
SetAlign( aObjPositioning.GetLineAlignment() );
m_aRef = aObjPositioning.GetAnchorPos();
if( nFlags & AsCharFlags::Rotate )
SvXSize( aObjPositioning.GetObjBoundRectInclSpacing().SSize() );
else
SvLSize( aObjPositioning.GetObjBoundRectInclSpacing().SSize() );
if( Height() )
{
// GetRelPosY returns the relative position to baseline (if 0, the
// upper border of the FlyCnt if on the baseline of a line)
SwTwips nRelPos = aObjPositioning.GetRelPosY();
if ( nRelPos < 0 )
{
mnAscent = -nRelPos;
if( mnAscent > Height() )
Height( mnAscent );
}
else
{
mnAscent = 0;
Height(Height() + nRelPos);
}
}
else
{
Height( 1 );
mnAscent = 0;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'Intersection_' is required to be utilized.
↑ V595 The 'rInf.GetVsh()' pointer was utilized before it was verified against nullptr. Check lines: 267, 268.