diff options
Diffstat (limited to 'svx/source/svdraw/svdotext.cxx')
-rw-r--r-- | svx/source/svdraw/svdotext.cxx | 2174 |
1 files changed, 2174 insertions, 0 deletions
diff --git a/svx/source/svdraw/svdotext.cxx b/svx/source/svdraw/svdotext.cxx new file mode 100644 index 000000000..fe5e13446 --- /dev/null +++ b/svx/source/svdraw/svdotext.cxx @@ -0,0 +1,2174 @@ +/* -*- 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 <comphelper/string.hxx> +#include <svl/stritem.hxx> +#include <svx/svdotext.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdoutl.hxx> +#include <svx/svdmodel.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <editeng/writingmodeitem.hxx> +#include <svx/sdtfchim.hxx> +#include <editeng/editdata.hxx> +#include <editeng/editstat.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/editobj.hxx> +#include <editeng/outliner.hxx> +#include <textchain.hxx> +#include <textchainflow.hxx> +#include <tools/helpers.hxx> +#include <svx/sderitm.hxx> +#include <svx/sdooitm.hxx> +#include <svx/sdshitm.hxx> +#include <svx/sdtagitm.hxx> +#include <svx/sdtfsitm.hxx> +#include <svx/sdtmfitm.hxx> +#include <svx/xtextit0.hxx> +#include <sdr/properties/textproperties.hxx> +#include <sdr/contact/viewcontactoftextobj.hxx> +#include <basegfx/tuple/b2dtuple.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/virdev.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <sal/log.hxx> +#include <o3tl/temporary.hxx> +#include <unotools/configmgr.hxx> + +using namespace com::sun::star; + +// BaseProperties section +std::unique_ptr<sdr::properties::BaseProperties> SdrTextObj::CreateObjectSpecificProperties() +{ + return std::make_unique<sdr::properties::TextProperties>(*this); +} + +// DrawContact section +std::unique_ptr<sdr::contact::ViewContact> SdrTextObj::CreateObjectSpecificViewContact() +{ + return std::make_unique<sdr::contact::ViewContactOfTextObj>(*this); +} + +SdrTextObj::SdrTextObj(SdrModel& rSdrModel) +: SdrAttrObj(rSdrModel), + mpEditingOutliner(nullptr), + meTextKind(SdrObjKind::Text) +{ + mbTextSizeDirty = false; + mbTextFrame = false; + mbNoShear = false; + mbDisableAutoWidthOnDragging = false; + + mbInEditMode = false; + mbTextAnimationAllowed = true; + maTextEditOffset = Point(0, 0); + + // #i25616# + mbSupportTextIndentingOnLineWidthChange = true; + mbInDownScale = false; +} + +SdrTextObj::SdrTextObj(SdrModel& rSdrModel, SdrTextObj const & rSource) +: SdrAttrObj(rSdrModel, rSource), + mpEditingOutliner(nullptr) +{ + mbInEditMode = false; + mbTextAnimationAllowed = true; + maTextEditOffset = Point(0, 0); + + // #i25616# + mbSupportTextIndentingOnLineWidthChange = true; + mbInDownScale = false; + + maRect = rSource.maRect; + maGeo = rSource.maGeo; + meTextKind = rSource.meTextKind; + mbTextFrame = rSource.mbTextFrame; + maTextSize = rSource.maTextSize; + mbTextSizeDirty = rSource.mbTextSizeDirty; + + // Not all of the necessary parameters were copied yet. + mbNoShear = rSource.mbNoShear; + mbDisableAutoWidthOnDragging = rSource.mbDisableAutoWidthOnDragging; + SdrText* pText = getActiveText(); + + if( pText && rSource.HasText() ) + { + // before pNewOutlinerParaObject was created the same, but + // set at mpText (outside this scope), but mpText might be + // empty (this operator== seems not prepared for MultiText + // objects). In the current form it makes only sense to + // create locally and use locally on a known existing SdrText + const Outliner* pEO = rSource.mpEditingOutliner; + std::optional<OutlinerParaObject> pNewOutlinerParaObject; + + if (pEO!=nullptr) + { + pNewOutlinerParaObject = pEO->CreateParaObject(); + } + else if (nullptr != rSource.getActiveText()->GetOutlinerParaObject()) + { + pNewOutlinerParaObject = *rSource.getActiveText()->GetOutlinerParaObject(); + } + + pText->SetOutlinerParaObject( std::move(pNewOutlinerParaObject) ); + } + + ImpSetTextStyleSheetListeners(); +} + +SdrTextObj::SdrTextObj( + SdrModel& rSdrModel, + const tools::Rectangle& rNewRect) +: SdrAttrObj(rSdrModel), + maRect(rNewRect), + mpEditingOutliner(nullptr), + meTextKind(SdrObjKind::Text) +{ + mbTextSizeDirty = false; + mbTextFrame = false; + mbNoShear = false; + mbDisableAutoWidthOnDragging = false; + ImpJustifyRect(maRect); + + mbInEditMode = false; + mbTextAnimationAllowed = true; + mbInDownScale = false; + maTextEditOffset = Point(0, 0); + + // #i25616# + mbSupportTextIndentingOnLineWidthChange = true; +} + +SdrTextObj::SdrTextObj( + SdrModel& rSdrModel, + SdrObjKind eNewTextKind) +: SdrAttrObj(rSdrModel), + mpEditingOutliner(nullptr), + meTextKind(eNewTextKind) +{ + mbTextSizeDirty = false; + mbTextFrame = true; + mbNoShear = true; + mbDisableAutoWidthOnDragging = false; + + mbInEditMode = false; + mbTextAnimationAllowed = true; + mbInDownScale = false; + maTextEditOffset = Point(0, 0); + + // #i25616# + mbSupportTextIndentingOnLineWidthChange = true; +} + +SdrTextObj::SdrTextObj( + SdrModel& rSdrModel, + SdrObjKind eNewTextKind, + const tools::Rectangle& rNewRect) +: SdrAttrObj(rSdrModel), + maRect(rNewRect), + mpEditingOutliner(nullptr), + meTextKind(eNewTextKind) +{ + mbTextSizeDirty = false; + mbTextFrame = true; + mbNoShear = true; + mbDisableAutoWidthOnDragging = false; + ImpJustifyRect(maRect); + + mbInEditMode = false; + mbTextAnimationAllowed = true; + mbInDownScale = false; + maTextEditOffset = Point(0, 0); + + // #i25616# + mbSupportTextIndentingOnLineWidthChange = true; +} + +SdrTextObj::~SdrTextObj() +{ + SdrOutliner& rOutl(getSdrModelFromSdrObject().GetHitTestOutliner()); + if( rOutl.GetTextObj() == this ) + rOutl.SetTextObj( nullptr ); + mpText.reset(); + ImpDeregisterLink(); +} + +void SdrTextObj::FitFrameToTextSize() +{ + ImpJustifyRect(maRect); + + SdrText* pText = getActiveText(); + if(pText==nullptr || !pText->GetOutlinerParaObject()) + return; + + SdrOutliner& rOutliner=ImpGetDrawOutliner(); + rOutliner.SetPaperSize(Size(maRect.Right()-maRect.Left(),maRect.Bottom()-maRect.Top())); + rOutliner.SetUpdateLayout(true); + rOutliner.SetText(*pText->GetOutlinerParaObject()); + Size aNewSize(rOutliner.CalcTextSize()); + rOutliner.Clear(); + aNewSize.AdjustWidth( 1 ); // because of possible rounding errors + aNewSize.AdjustWidth(GetTextLeftDistance()+GetTextRightDistance() ); + aNewSize.AdjustHeight(GetTextUpperDistance()+GetTextLowerDistance() ); + tools::Rectangle aNewRect(maRect); + aNewRect.SetSize(aNewSize); + ImpJustifyRect(aNewRect); + if (aNewRect!=maRect) { + SetLogicRect(aNewRect); + } +} + +void SdrTextObj::NbcSetText(const OUString& rStr) +{ + SdrOutliner& rOutliner=ImpGetDrawOutliner(); + rOutliner.SetStyleSheet( 0, GetStyleSheet()); + rOutliner.SetText(rStr,rOutliner.GetParagraph( 0 )); + std::optional<OutlinerParaObject> pNewText=rOutliner.CreateParaObject(); + NbcSetOutlinerParaObject(std::move(pNewText)); + mbTextSizeDirty=true; +} + +void SdrTextObj::SetText(const OUString& rStr) +{ + tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect(); + NbcSetText(rStr); + SetChanged(); + BroadcastObjectChange(); + SendUserCall(SdrUserCallType::Resize,aBoundRect0); +} + +void SdrTextObj::NbcSetText(SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat) +{ + SdrOutliner& rOutliner=ImpGetDrawOutliner(); + rOutliner.SetStyleSheet( 0, GetStyleSheet()); + rOutliner.Read(rInput,rBaseURL,eFormat); + std::optional<OutlinerParaObject> pNewText=rOutliner.CreateParaObject(); + rOutliner.SetUpdateLayout(true); + Size aSize(rOutliner.CalcTextSize()); + rOutliner.Clear(); + NbcSetOutlinerParaObject(std::move(pNewText)); + maTextSize=aSize; + mbTextSizeDirty=false; +} + +void SdrTextObj::SetText(SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat) +{ + tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect(); + NbcSetText(rInput,rBaseURL,eFormat); + SetChanged(); + BroadcastObjectChange(); + SendUserCall(SdrUserCallType::Resize,aBoundRect0); +} + +const Size& SdrTextObj::GetTextSize() const +{ + if (mbTextSizeDirty) + { + Size aSiz; + SdrText* pText = getActiveText(); + if( pText && pText->GetOutlinerParaObject ()) + { + SdrOutliner& rOutliner=ImpGetDrawOutliner(); + rOutliner.SetText(*pText->GetOutlinerParaObject()); + rOutliner.SetUpdateLayout(true); + aSiz=rOutliner.CalcTextSize(); + rOutliner.Clear(); + } + // casting to nonconst twice + const_cast<SdrTextObj*>(this)->maTextSize = aSiz; + const_cast<SdrTextObj*>(this)->mbTextSizeDirty = false; + } + return maTextSize; +} + +bool SdrTextObj::IsAutoGrowHeight() const +{ + if(!mbTextFrame) + return false; // AutoGrow only together with TextFrames + + const SfxItemSet& rSet = GetObjectItemSet(); + bool bRet = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue(); + + if(bRet) + { + SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue(); + + if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide) + { + SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue(); + + if(eDirection == SdrTextAniDirection::Up || eDirection == SdrTextAniDirection::Down) + { + bRet = false; + } + } + } + return bRet; +} + +bool SdrTextObj::IsAutoGrowWidth() const +{ + if (!mbTextFrame) + return false; // AutoGrow only together with TextFrames + + const SfxItemSet& rSet = GetObjectItemSet(); + bool bRet = rSet.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue(); + + bool bInEditMOde = IsInEditMode(); + + if(!bInEditMOde && bRet) + { + SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue(); + + if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide) + { + SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue(); + + if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right) + { + bRet = false; + } + } + } + return bRet; +} + +SdrTextHorzAdjust SdrTextObj::GetTextHorizontalAdjust() const +{ + return GetTextHorizontalAdjust(GetObjectItemSet()); +} + +SdrTextHorzAdjust SdrTextObj::GetTextHorizontalAdjust(const SfxItemSet& rSet) const +{ + if(IsContourTextFrame()) + return SDRTEXTHORZADJUST_BLOCK; + + SdrTextHorzAdjust eRet = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue(); + + bool bInEditMode = IsInEditMode(); + + if(!bInEditMode && eRet == SDRTEXTHORZADJUST_BLOCK) + { + SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue(); + + if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide) + { + SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue(); + + if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right) + { + eRet = SDRTEXTHORZADJUST_LEFT; + } + } + } + + return eRet; +} // defaults: BLOCK (justify) for text frame, CENTER for captions of drawing objects + +SdrTextVertAdjust SdrTextObj::GetTextVerticalAdjust() const +{ + return GetTextVerticalAdjust(GetObjectItemSet()); +} + +SdrTextVertAdjust SdrTextObj::GetTextVerticalAdjust(const SfxItemSet& rSet) const +{ + if(IsContourTextFrame()) + return SDRTEXTVERTADJUST_TOP; + + // Take care for vertical text animation here + SdrTextVertAdjust eRet = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue(); + bool bInEditMode = IsInEditMode(); + + // Take care for vertical text animation here + if(!bInEditMode && eRet == SDRTEXTVERTADJUST_BLOCK) + { + SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue(); + + if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide) + { + SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue(); + + if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right) + { + eRet = SDRTEXTVERTADJUST_TOP; + } + } + } + + return eRet; +} // defaults: TOP for text frame, CENTER for captions of drawing objects + +void SdrTextObj::ImpJustifyRect(tools::Rectangle& rRect) +{ + if (!rRect.IsEmpty()) { + rRect.Justify(); + if (rRect.Left()==rRect.Right()) rRect.AdjustRight( 1 ); + if (rRect.Top()==rRect.Bottom()) rRect.AdjustBottom( 1 ); + } +} + +void SdrTextObj::ImpCheckShear() +{ + if (mbNoShear && maGeo.nShearAngle) + { + maGeo.nShearAngle = 0_deg100; + maGeo.mfTanShearAngle = 0; + } +} + +void SdrTextObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const +{ + bool bNoTextFrame=!IsTextFrame(); + rInfo.bResizeFreeAllowed=bNoTextFrame || ((maGeo.nRotationAngle.get() % 9000) == 0); + rInfo.bResizePropAllowed=true; + rInfo.bRotateFreeAllowed=true; + rInfo.bRotate90Allowed =true; + rInfo.bMirrorFreeAllowed=bNoTextFrame; + rInfo.bMirror45Allowed =bNoTextFrame; + rInfo.bMirror90Allowed =bNoTextFrame; + + // allow transparency + rInfo.bTransparenceAllowed = true; + + rInfo.bShearAllowed =bNoTextFrame; + rInfo.bEdgeRadiusAllowed=true; + bool bCanConv=ImpCanConvTextToCurve(); + rInfo.bCanConvToPath =bCanConv; + rInfo.bCanConvToPoly =bCanConv; + rInfo.bCanConvToPathLineToArea=bCanConv; + rInfo.bCanConvToPolyLineToArea=bCanConv; + rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary()); +} + +SdrObjKind SdrTextObj::GetObjIdentifier() const +{ + return meTextKind; +} + +bool SdrTextObj::HasTextImpl( SdrOutliner const * pOutliner ) +{ + bool bRet=false; + if(pOutliner) + { + Paragraph* p1stPara=pOutliner->GetParagraph( 0 ); + sal_Int32 nParaCount=pOutliner->GetParagraphCount(); + if(p1stPara==nullptr) + nParaCount=0; + + if(nParaCount==1) + { + // if it is only one paragraph, check if that paragraph is empty + if( pOutliner->GetText(p1stPara).isEmpty() ) + nParaCount = 0; + } + + bRet= nParaCount!=0; + } + return bRet; +} + +void SdrTextObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage) +{ + const bool bRemove(pNewPage == nullptr && pOldPage != nullptr); + const bool bInsert(pNewPage != nullptr && pOldPage == nullptr); + const bool bLinked(IsLinkedText()); + + if (bLinked && bRemove) + { + ImpDeregisterLink(); + } + + // call parent + SdrAttrObj::handlePageChange(pOldPage, pNewPage); + + if (bLinked && bInsert) + { + ImpRegisterLink(); + } +} + +void SdrTextObj::NbcSetEckenradius(tools::Long nRad) +{ + SetObjectItem(makeSdrEckenradiusItem(nRad)); +} + +// #115391# This implementation is based on the object size (aRect) and the +// states of IsAutoGrowWidth/Height to correctly set TextMinFrameWidth/Height +void SdrTextObj::AdaptTextMinSize() +{ + if (!mbTextFrame) + // Only do this for text frame. + return; + + if (getSdrModelFromSdrObject().IsPasteResize()) + // Don't do this during paste resize. + return; + + const bool bW = IsAutoGrowWidth(); + const bool bH = IsAutoGrowHeight(); + + if (!bW && !bH) + // No auto grow requested. Bail out. + return; + + SfxItemSetFixed<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT, + SDRATTR_TEXT_MINFRAMEWIDTH, SDRATTR_TEXT_AUTOGROWWIDTH> // contains SDRATTR_TEXT_MAXFRAMEWIDTH + aSet(*GetObjectItemSet().GetPool()); + + if(bW) + { + // Set minimum width. + const tools::Long nDist = GetTextLeftDistance() + GetTextRightDistance(); + const tools::Long nW = std::max<tools::Long>(0, maRect.GetWidth() - 1 - nDist); // text width without margins + + aSet.Put(makeSdrTextMinFrameWidthItem(nW)); + + if(!IsVerticalWriting() && mbDisableAutoWidthOnDragging) + { + mbDisableAutoWidthOnDragging = true; + aSet.Put(makeSdrTextAutoGrowWidthItem(false)); + } + } + + if(bH) + { + // Set Minimum height. + const tools::Long nDist = GetTextUpperDistance() + GetTextLowerDistance(); + const tools::Long nH = std::max<tools::Long>(0, maRect.GetHeight() - 1 - nDist); // text height without margins + + aSet.Put(makeSdrTextMinFrameHeightItem(nH)); + + if(IsVerticalWriting() && mbDisableAutoWidthOnDragging) + { + mbDisableAutoWidthOnDragging = false; + aSet.Put(makeSdrTextAutoGrowHeightItem(false)); + } + } + + SetObjectItemSet(aSet); +} + +void SdrTextObj::ImpSetContourPolygon( SdrOutliner& rOutliner, tools::Rectangle const & rAnchorRect, bool bLineWidth ) const +{ + basegfx::B2DPolyPolygon aXorPolyPolygon(TakeXorPoly()); + std::optional<basegfx::B2DPolyPolygon> pContourPolyPolygon; + basegfx::B2DHomMatrix aMatrix(basegfx::utils::createTranslateB2DHomMatrix( + -rAnchorRect.Left(), -rAnchorRect.Top())); + + if(maGeo.nRotationAngle) + { + // Unrotate! + aMatrix.rotate(-toRadians(maGeo.nRotationAngle)); + } + + aXorPolyPolygon.transform(aMatrix); + + if( bLineWidth ) + { + // Take line width into account. + // When doing the hit test, avoid this. (Performance!) + pContourPolyPolygon.emplace(); + + // test if shadow needs to be avoided for TakeContour() + const SfxItemSet& rSet = GetObjectItemSet(); + bool bShadowOn = rSet.Get(SDRATTR_SHADOW).GetValue(); + + // #i33696# + // Remember TextObject currently set at the DrawOutliner, it WILL be + // replaced during calculating the outline since it uses an own paint + // and that one uses the DrawOutliner, too. + const SdrTextObj* pLastTextObject = rOutliner.GetTextObj(); + + if(bShadowOn) + { + // force shadow off + SdrObject* pCopy(CloneSdrObject(getSdrModelFromSdrObject())); + pCopy->SetMergedItem(makeSdrShadowItem(false)); + *pContourPolyPolygon = pCopy->TakeContour(); + SdrObject::Free( pCopy ); + } + else + { + *pContourPolyPolygon = TakeContour(); + } + + // #i33696# + // restore remembered text object + if(pLastTextObject != rOutliner.GetTextObj()) + { + rOutliner.SetTextObj(pLastTextObject); + } + + pContourPolyPolygon->transform(aMatrix); + } + + rOutliner.SetPolygon(aXorPolyPolygon, pContourPolyPolygon ? &*pContourPolyPolygon : nullptr); +} + +void SdrTextObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const +{ + rRect=maRect; +} + +// See also: <unnamed>::getTextAnchorRange in svx/source/sdr/primitive2d/sdrdecompositiontools.cxx +void SdrTextObj::AdjustRectToTextDistance(tools::Rectangle& rAnchorRect) const +{ + const tools::Long nLeftDist = GetTextLeftDistance(); + const tools::Long nRightDist = GetTextRightDistance(); + const tools::Long nUpperDist = GetTextUpperDistance(); + const tools::Long nLowerDist = GetTextLowerDistance(); + if (!IsVerticalWriting()) + { + rAnchorRect.AdjustLeft(nLeftDist); + rAnchorRect.AdjustTop(nUpperDist); + rAnchorRect.AdjustRight(-nRightDist); + rAnchorRect.AdjustBottom(-nLowerDist); + } + else if (IsTopToBottom()) + { + rAnchorRect.AdjustLeft(nLowerDist); + rAnchorRect.AdjustTop(nLeftDist); + rAnchorRect.AdjustRight(-nUpperDist); + rAnchorRect.AdjustBottom(-nRightDist); + } + else + { + rAnchorRect.AdjustLeft(nUpperDist); + rAnchorRect.AdjustTop(nRightDist); + rAnchorRect.AdjustRight(-nLowerDist); + rAnchorRect.AdjustBottom(-nLeftDist); + } + + // Since sizes may be bigger than the object bounds it is necessary to + // justify the rect now. + ImpJustifyRect(rAnchorRect); +} + +void SdrTextObj::TakeTextAnchorRect(tools::Rectangle& rAnchorRect) const +{ + tools::Rectangle aAnkRect(maRect); // the rectangle in which we anchor + bool bFrame=IsTextFrame(); + if (!bFrame) { + TakeUnrotatedSnapRect(aAnkRect); + } + Point aRotateRef(aAnkRect.TopLeft()); + AdjustRectToTextDistance(aAnkRect); + + if (bFrame) { + // TODO: Optimize this. + if (aAnkRect.GetWidth()<2) aAnkRect.SetRight(aAnkRect.Left()+1 ); // minimum size h and v: 2 px + if (aAnkRect.GetHeight()<2) aAnkRect.SetBottom(aAnkRect.Top()+1 ); + } + if (maGeo.nRotationAngle) { + Point aTmpPt(aAnkRect.TopLeft()); + RotatePoint(aTmpPt,aRotateRef,maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle); + aTmpPt-=aAnkRect.TopLeft(); + aAnkRect.Move(aTmpPt.X(),aTmpPt.Y()); + } + rAnchorRect=aAnkRect; +} + +void SdrTextObj::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText, + tools::Rectangle* pAnchorRect, bool bLineWidth ) const +{ + tools::Rectangle aAnkRect; // the rectangle in which we anchor + TakeTextAnchorRect(aAnkRect); + SdrTextVertAdjust eVAdj=GetTextVerticalAdjust(); + SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust(); + SdrTextAniKind eAniKind=GetTextAniKind(); + SdrTextAniDirection eAniDirection=GetTextAniDirection(); + + bool bFitToSize(IsFitToSize()); + bool bContourFrame=IsContourTextFrame(); + + bool bFrame=IsTextFrame(); + EEControlBits nStat0=rOutliner.GetControlWord(); + Size aNullSize; + if (!bContourFrame) + { + rOutliner.SetControlWord(nStat0|EEControlBits::AUTOPAGESIZE); + rOutliner.SetMinAutoPaperSize(aNullSize); + rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000)); + } + + if (!bFitToSize && !bContourFrame) + { + tools::Long nAnkWdt=aAnkRect.GetWidth(); + tools::Long nAnkHgt=aAnkRect.GetHeight(); + if (bFrame) + { + tools::Long nWdt=nAnkWdt; + tools::Long nHgt=nAnkHgt; + + bool bInEditMode = IsInEditMode(); + + if (!bInEditMode && (eAniKind==SdrTextAniKind::Scroll || eAniKind==SdrTextAniKind::Alternate || eAniKind==SdrTextAniKind::Slide)) + { + // unlimited paper size for ticker text + if (eAniDirection==SdrTextAniDirection::Left || eAniDirection==SdrTextAniDirection::Right) nWdt=1000000; + if (eAniDirection==SdrTextAniDirection::Up || eAniDirection==SdrTextAniDirection::Down) nHgt=1000000; + } + + bool bChainedFrame = IsChainable(); + // Might be required for overflow check working: do limit height to frame if box is chainable. + if (!bChainedFrame) { + // #i119885# Do not limit/force height to geometrical frame (vice versa for vertical writing) + + if(IsVerticalWriting()) + { + nWdt = 1000000; + } + else + { + nHgt = 1000000; + } + } + + rOutliner.SetMaxAutoPaperSize(Size(nWdt,nHgt)); + } + + // New try with _BLOCK for hor and ver after completely + // supporting full width for vertical text. + if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting()) + { + rOutliner.SetMinAutoPaperSize(Size(nAnkWdt, 0)); + rOutliner.SetMinColumnWrapHeight(nAnkHgt); + } + + if(SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting()) + { + rOutliner.SetMinAutoPaperSize(Size(0, nAnkHgt)); + rOutliner.SetMinColumnWrapHeight(nAnkWdt); + } + } + + rOutliner.SetPaperSize(aNullSize); + if (bContourFrame) + ImpSetContourPolygon( rOutliner, aAnkRect, bLineWidth ); + + // put text into the outliner, if available from the edit outliner + SdrText* pText = getActiveText(); + OutlinerParaObject* pOutlinerParaObject = pText ? pText->GetOutlinerParaObject() : nullptr; + std::optional<OutlinerParaObject> pPara; + if (mpEditingOutliner && !bNoEditText) + pPara = mpEditingOutliner->CreateParaObject(); + else if (pOutlinerParaObject) + pPara = *pOutlinerParaObject; + + if (pPara) + { + const bool bHitTest(&getSdrModelFromSdrObject().GetHitTestOutliner() == &rOutliner); + const SdrTextObj* pTestObj = rOutliner.GetTextObj(); + + if( !pTestObj || !bHitTest || pTestObj != this || + pTestObj->GetOutlinerParaObject() != pOutlinerParaObject ) + { + if( bHitTest ) // #i33696# take back fix #i27510# + { + rOutliner.SetTextObj( this ); + rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue()); + } + + rOutliner.SetUpdateLayout(true); + rOutliner.SetText(*pPara); + } + } + else + { + rOutliner.SetTextObj( nullptr ); + } + + rOutliner.SetUpdateLayout(true); + rOutliner.SetControlWord(nStat0); + + if( pText ) + pText->CheckPortionInfo(rOutliner); + + Point aTextPos(aAnkRect.TopLeft()); + Size aTextSiz(rOutliner.GetPaperSize()); // GetPaperSize() adds a little tolerance, right? + + // For draw objects containing text correct hor/ver alignment if text is bigger + // than the object itself. Without that correction, the text would always be + // formatted to the left edge (or top edge when vertical) of the draw object. + if(!IsTextFrame()) + { + if(aAnkRect.GetWidth() < aTextSiz.Width() && !IsVerticalWriting()) + { + // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK, + // else the alignment is wanted. + if(SDRTEXTHORZADJUST_BLOCK == eHAdj) + { + eHAdj = SDRTEXTHORZADJUST_CENTER; + } + } + + if(aAnkRect.GetHeight() < aTextSiz.Height() && IsVerticalWriting()) + { + // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK, + // else the alignment is wanted. + if(SDRTEXTVERTADJUST_BLOCK == eVAdj) + { + eVAdj = SDRTEXTVERTADJUST_CENTER; + } + } + } + + if (eHAdj==SDRTEXTHORZADJUST_CENTER || eHAdj==SDRTEXTHORZADJUST_RIGHT) + { + tools::Long nFreeWdt=aAnkRect.GetWidth()-aTextSiz.Width(); + if (eHAdj==SDRTEXTHORZADJUST_CENTER) + aTextPos.AdjustX(nFreeWdt/2 ); + if (eHAdj==SDRTEXTHORZADJUST_RIGHT) + aTextPos.AdjustX(nFreeWdt ); + } + if (eVAdj==SDRTEXTVERTADJUST_CENTER || eVAdj==SDRTEXTVERTADJUST_BOTTOM) + { + tools::Long nFreeHgt=aAnkRect.GetHeight()-aTextSiz.Height(); + if (eVAdj==SDRTEXTVERTADJUST_CENTER) + aTextPos.AdjustY(nFreeHgt/2 ); + if (eVAdj==SDRTEXTVERTADJUST_BOTTOM) + aTextPos.AdjustY(nFreeHgt ); + } + if (maGeo.nRotationAngle) + RotatePoint(aTextPos,aAnkRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle); + + if (pAnchorRect) + *pAnchorRect=aAnkRect; + + // rTextRect might not be correct in some cases at ContourFrame + rTextRect=tools::Rectangle(aTextPos,aTextSiz); + if (bContourFrame) + rTextRect=aAnkRect; +} + +bool SdrTextObj::CanCreateEditOutlinerParaObject() const +{ + if( HasTextImpl( mpEditingOutliner ) ) + { + return mpEditingOutliner->GetParagraphCount() > 0; + } + return false; +} + +std::optional<OutlinerParaObject> SdrTextObj::CreateEditOutlinerParaObject() const +{ + std::optional<OutlinerParaObject> pPara; + if( HasTextImpl( mpEditingOutliner ) ) + { + sal_Int32 nParaCount = mpEditingOutliner->GetParagraphCount(); + pPara = mpEditingOutliner->CreateParaObject(0, nParaCount); + } + return pPara; +} + +void SdrTextObj::ImpSetCharStretching(SdrOutliner& rOutliner, const Size& rTextSize, const Size& rShapeSize, Fraction& rFitXCorrection) +{ + OutputDevice* pOut = rOutliner.GetRefDevice(); + bool bNoStretching(false); + + if(pOut && pOut->GetOutDevType() == OUTDEV_PRINTER) + { + // check whether CharStretching is possible at all + GDIMetaFile* pMtf = pOut->GetConnectMetaFile(); + OUString aTestString(u'J'); + + if(pMtf && (!pMtf->IsRecord() || pMtf->IsPause())) + pMtf = nullptr; + + if(pMtf) + pMtf->Pause(true); + + vcl::Font aOriginalFont(pOut->GetFont()); + vcl::Font aTmpFont( OutputDevice::GetDefaultFont( DefaultFontType::SERIF, LANGUAGE_SYSTEM, GetDefaultFontFlags::OnlyOne ) ); + + aTmpFont.SetFontSize(Size(0,100)); + pOut->SetFont(aTmpFont); + Size aSize1(pOut->GetTextWidth(aTestString), pOut->GetTextHeight()); + aTmpFont.SetFontSize(Size(800,100)); + pOut->SetFont(aTmpFont); + Size aSize2(pOut->GetTextWidth(aTestString), pOut->GetTextHeight()); + pOut->SetFont(aOriginalFont); + + if(pMtf) + pMtf->Pause(false); + + bNoStretching = (aSize1 == aSize2); + +#ifdef _WIN32 + // Windows zooms the font proportionally when using Size(100,500), + // we don't like that. + if(aSize2.Height() >= aSize1.Height() * 2) + { + bNoStretching = true; + } +#endif + } + unsigned nLoopCount=0; + bool bNoMoreLoop = false; + tools::Long nXDiff0=0x7FFFFFFF; + tools::Long nWantWdt=rShapeSize.Width(); + tools::Long nIsWdt=rTextSize.Width(); + if (nIsWdt==0) nIsWdt=1; + + tools::Long nWantHgt=rShapeSize.Height(); + tools::Long nIsHgt=rTextSize.Height(); + if (nIsHgt==0) nIsHgt=1; + + tools::Long nXTolPl=nWantWdt/100; // tolerance: +1% + tools::Long nXTolMi=nWantWdt/25; // tolerance: -4% + tools::Long nXCorr =nWantWdt/20; // correction scale: 5% + + tools::Long nX=(nWantWdt*100) /nIsWdt; // calculate X stretching + tools::Long nY=(nWantHgt*100) /nIsHgt; // calculate Y stretching + bool bChkX = true; + if (bNoStretching) { // might only be possible proportionally + if (nX>nY) { nX=nY; bChkX=false; } + else { nY=nX; } + } + + while (nLoopCount<5 && !bNoMoreLoop) { + if (nX<0) nX=-nX; + if (nX<1) { nX=1; bNoMoreLoop = true; } + if (nX>65535) { nX=65535; bNoMoreLoop = true; } + + if (nY<0) nY=-nY; + if (nY<1) { nY=1; bNoMoreLoop = true; } + if (nY>65535) { nY=65535; bNoMoreLoop = true; } + + // exception, there is no text yet (horizontal case) + if(nIsWdt <= 1) + { + nX = nY; + bNoMoreLoop = true; + } + + // exception, there is no text yet (vertical case) + if(nIsHgt <= 1) + { + nY = nX; + bNoMoreLoop = true; + } + + rOutliner.SetGlobalCharStretching(static_cast<sal_uInt16>(nX),static_cast<sal_uInt16>(nY)); + nLoopCount++; + Size aSiz(rOutliner.CalcTextSize()); + tools::Long nXDiff=aSiz.Width()-nWantWdt; + rFitXCorrection=Fraction(nWantWdt,aSiz.Width()); + if (((nXDiff>=nXTolMi || !bChkX) && nXDiff<=nXTolPl) || nXDiff==nXDiff0) { + bNoMoreLoop = true; + } else { + // correct stretching factors + tools::Long nMul=nWantWdt; + tools::Long nDiv=aSiz.Width(); + if (std::abs(nXDiff)<=2*nXCorr) { + if (nMul>nDiv) nDiv+=(nMul-nDiv)/2; // but only add half of what we calculated, + else nMul+=(nDiv-nMul)/2; // because the EditEngine calculates wrongly later on + } + nX=nX*nMul/nDiv; + if (bNoStretching) nY=nX; + } + nXDiff0=nXDiff; + } +} + +OUString SdrTextObj::TakeObjNameSingul() const +{ + OUString aStr; + + switch(meTextKind) + { + case SdrObjKind::OutlineText: + { + aStr = SvxResId(STR_ObjNameSingulOUTLINETEXT); + break; + } + + case SdrObjKind::TitleText : + { + aStr = SvxResId(STR_ObjNameSingulTITLETEXT); + break; + } + + default: + { + if(IsLinkedText()) + aStr = SvxResId(STR_ObjNameSingulTEXTLNK); + else + aStr = SvxResId(STR_ObjNameSingulTEXT); + break; + } + } + + OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject(); + if(pOutlinerParaObject && meTextKind != SdrObjKind::OutlineText) + { + // shouldn't currently cause any problems at OUTLINETEXT + OUString aStr2(comphelper::string::stripStart(pOutlinerParaObject->GetTextObject().GetText(0), ' ')); + + // avoid non expanded text portions in object name + // (second condition is new) + if(!aStr2.isEmpty() && aStr2.indexOf(u'\x00FF') == -1) + { + // space between ResStr and content text + aStr += " \'"; + + if(aStr2.getLength() > 10) + { + aStr2 = OUString::Concat(aStr2.subView(0, 8)) + "..."; + } + + aStr += aStr2 + "\'"; + } + } + + OUString sName(aStr); + + OUString aName(GetName()); + if (!aName.isEmpty()) + sName += " '" + aName + "'"; + + return sName; +} + +OUString SdrTextObj::TakeObjNamePlural() const +{ + OUString sName; + switch (meTextKind) + { + case SdrObjKind::OutlineText: sName=SvxResId(STR_ObjNamePluralOUTLINETEXT); break; + case SdrObjKind::TitleText : sName=SvxResId(STR_ObjNamePluralTITLETEXT); break; + default: { + if (IsLinkedText()) { + sName=SvxResId(STR_ObjNamePluralTEXTLNK); + } else { + sName=SvxResId(STR_ObjNamePluralTEXT); + } + } break; + } // switch + return sName; +} + +SdrTextObj* SdrTextObj::CloneSdrObject(SdrModel& rTargetModel) const +{ + return new SdrTextObj(rTargetModel, *this); +} + +basegfx::B2DPolyPolygon SdrTextObj::TakeXorPoly() const +{ + tools::Polygon aPol(maRect); + if (maGeo.nShearAngle) ShearPoly(aPol,maRect.TopLeft(),maGeo.mfTanShearAngle); + if (maGeo.nRotationAngle) RotatePoly(aPol,maRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle); + + basegfx::B2DPolyPolygon aRetval; + aRetval.append(aPol.getB2DPolygon()); + return aRetval; +} + +basegfx::B2DPolyPolygon SdrTextObj::TakeContour() const +{ + basegfx::B2DPolyPolygon aRetval(SdrAttrObj::TakeContour()); + + // and now add the BoundRect of the text, if necessary + if ( GetOutlinerParaObject() && !IsFontwork() && !IsContourTextFrame() ) + { + // using Clone()-Paint() strategy inside TakeContour() leaves a destroyed + // SdrObject as pointer in DrawOutliner. Set *this again in fetching the outliner + // in every case + SdrOutliner& rOutliner=ImpGetDrawOutliner(); + + tools::Rectangle aAnchor2; + tools::Rectangle aR; + TakeTextRect(rOutliner,aR,false,&aAnchor2); + rOutliner.Clear(); + bool bFitToSize(IsFitToSize()); + if (bFitToSize) aR=aAnchor2; + tools::Polygon aPol(aR); + if (maGeo.nRotationAngle) RotatePoly(aPol,aR.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); + + aRetval.append(aPol.getB2DPolygon()); + } + + return aRetval; +} + +void SdrTextObj::RecalcSnapRect() +{ + if (maGeo.nRotationAngle || maGeo.nShearAngle) + { + maSnapRect = Rect2Poly(maRect, maGeo).GetBoundRect(); + } else { + maSnapRect = maRect; + } +} + +sal_uInt32 SdrTextObj::GetSnapPointCount() const +{ + return 4; +} + +Point SdrTextObj::GetSnapPoint(sal_uInt32 i) const +{ + Point aP; + switch (i) { + case 0: aP=maRect.TopLeft(); break; + case 1: aP=maRect.TopRight(); break; + case 2: aP=maRect.BottomLeft(); break; + case 3: aP=maRect.BottomRight(); break; + default: aP=maRect.Center(); break; + } + if (maGeo.nShearAngle) ShearPoint(aP,maRect.TopLeft(),maGeo.mfTanShearAngle); + if (maGeo.nRotationAngle) RotatePoint(aP,maRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle); + return aP; +} + +// Extracted from ImpGetDrawOutliner() +void SdrTextObj::ImpInitDrawOutliner( SdrOutliner& rOutl ) const +{ + rOutl.SetUpdateLayout(false); + OutlinerMode nOutlinerMode = OutlinerMode::OutlineObject; + if ( !IsOutlText() ) + nOutlinerMode = OutlinerMode::TextObject; + rOutl.Init( nOutlinerMode ); + + rOutl.SetGlobalCharStretching(); + EEControlBits nStat=rOutl.GetControlWord(); + nStat &= ~EEControlBits(EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE); + rOutl.SetControlWord(nStat); + Size aMaxSize(100000,100000); + rOutl.SetMinAutoPaperSize(Size()); + rOutl.SetMaxAutoPaperSize(aMaxSize); + rOutl.SetPaperSize(aMaxSize); + rOutl.ClearPolygon(); +} + +SdrOutliner& SdrTextObj::ImpGetDrawOutliner() const +{ + SdrOutliner& rOutl(getSdrModelFromSdrObject().GetDrawOutliner(this)); + + // Code extracted to ImpInitDrawOutliner() + ImpInitDrawOutliner( rOutl ); + + return rOutl; +} + +// Extracted from Paint() +void SdrTextObj::ImpSetupDrawOutlinerForPaint( bool bContourFrame, + SdrOutliner& rOutliner, + tools::Rectangle& rTextRect, + tools::Rectangle& rAnchorRect, + tools::Rectangle& rPaintRect, + Fraction& rFitXCorrection ) const +{ + if (!bContourFrame) + { + // FitToSize can't be used together with ContourFrame for now + if (IsFitToSize() || IsAutoFit()) + { + EEControlBits nStat=rOutliner.GetControlWord(); + nStat|=EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE; + rOutliner.SetControlWord(nStat); + } + } + rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue()); + TakeTextRect(rOutliner, rTextRect, false, &rAnchorRect); + rPaintRect = rTextRect; + + if (bContourFrame) + return; + + // FitToSize can't be used together with ContourFrame for now + if (IsFitToSize()) + { + ImpSetCharStretching(rOutliner,rTextRect.GetSize(),rAnchorRect.GetSize(),rFitXCorrection); + rPaintRect=rAnchorRect; + } + else if (IsAutoFit()) + { + ImpAutoFitText(rOutliner); + } +} + +sal_uInt16 SdrTextObj::GetFontScaleY() const +{ + SdrOutliner& rOutliner = ImpGetDrawOutliner(); + // This eventually calls ImpAutoFitText + UpdateOutlinerFormatting(rOutliner, o3tl::temporary(tools::Rectangle())); + + sal_uInt16 nStretchY; + rOutliner.GetGlobalCharStretching(o3tl::temporary(sal_uInt16()), nStretchY); + return nStretchY; +} + +void SdrTextObj::ImpAutoFitText( SdrOutliner& rOutliner ) const +{ + const Size aShapeSize=GetSnapRect().GetSize(); + ImpAutoFitText( rOutliner, + Size(aShapeSize.Width()-GetTextLeftDistance()-GetTextRightDistance(), + aShapeSize.Height()-GetTextUpperDistance()-GetTextLowerDistance()), + IsVerticalWriting() ); +} + +void SdrTextObj::ImpAutoFitText(SdrOutliner& rOutliner, const Size& rTextSize, + bool bIsVerticalWriting) const +{ + // EditEngine formatting is unstable enough for + // line-breaking text that we need some more samples + + // loop early-exits if we detect an already attained value + sal_uInt16 nMinStretchX=0, nMinStretchY=0; + sal_uInt16 aOldStretchXVals[]={0,0,0,0,0,0,0,0,0,0}; + const size_t aStretchArySize=SAL_N_ELEMENTS(aOldStretchXVals); + for(unsigned int i=0; i<aStretchArySize; ++i) + { + const Size aCurrTextSize = rOutliner.CalcTextSizeNTP(); + double fFactor(1.0); + if( bIsVerticalWriting ) + { + if (aCurrTextSize.Width() != 0) + { + fFactor = double(rTextSize.Width())/aCurrTextSize.Width(); + } + } + else if (aCurrTextSize.Height() != 0) + { + fFactor = double(rTextSize.Height())/aCurrTextSize.Height(); + } + // fFactor scales in both x and y directions + // - this is fine for bulleted words + // - but it scales too much for a long paragraph + // - taking sqrt scales long paragraphs the best + // - bulleted words will have to go through more iterations + fFactor = std::sqrt(fFactor); + + sal_uInt16 nCurrStretchX, nCurrStretchY; + rOutliner.GetGlobalCharStretching(nCurrStretchX, nCurrStretchY); + + if (fFactor >= 1.0 ) + { + // resulting text area fits into available shape rect - + // err on the larger stretching, to optimally fill area + nMinStretchX = std::max(nMinStretchX,nCurrStretchX); + nMinStretchY = std::max(nMinStretchY,nCurrStretchY); + } + + aOldStretchXVals[i] = nCurrStretchX; + if( std::find(aOldStretchXVals, aOldStretchXVals+i, nCurrStretchX) != aOldStretchXVals+i ) + break; // same value already attained once; algo is looping, exit + + if (fFactor < 1.0 || nCurrStretchX != 100) + { + nCurrStretchX = sal::static_int_cast<sal_uInt16>(nCurrStretchX*fFactor); + nCurrStretchY = sal::static_int_cast<sal_uInt16>(nCurrStretchY*fFactor); + rOutliner.SetGlobalCharStretching(std::min(sal_uInt16(100),nCurrStretchX), + std::min(sal_uInt16(100),nCurrStretchY)); + SAL_INFO("svx", "zoom is " << nCurrStretchX); + } + } + + const SdrTextFitToSizeTypeItem& rItem = GetObjectItem(SDRATTR_TEXT_FITTOSIZE); + if (rItem.GetMaxScale() > 0) + { + nMinStretchX = std::min<sal_uInt16>(rItem.GetMaxScale(), nMinStretchX); + nMinStretchY = std::min<sal_uInt16>(rItem.GetMaxScale(), nMinStretchY); + } + + SAL_INFO("svx", "final zoom is " << nMinStretchX); + rOutliner.SetGlobalCharStretching(std::min(sal_uInt16(100),nMinStretchX), + std::min(sal_uInt16(100),nMinStretchY)); +} + +void SdrTextObj::SetupOutlinerFormatting( SdrOutliner& rOutl, tools::Rectangle& rPaintRect ) const +{ + ImpInitDrawOutliner( rOutl ); + UpdateOutlinerFormatting( rOutl, rPaintRect ); +} + +void SdrTextObj::UpdateOutlinerFormatting( SdrOutliner& rOutl, tools::Rectangle& rPaintRect ) const +{ + tools::Rectangle aTextRect; + tools::Rectangle aAnchorRect; + Fraction aFitXCorrection(1,1); + + const bool bContourFrame(IsContourTextFrame()); + const MapMode aMapMode( + getSdrModelFromSdrObject().GetScaleUnit(), + Point(0,0), + getSdrModelFromSdrObject().GetScaleFraction(), + getSdrModelFromSdrObject().GetScaleFraction()); + + rOutl.SetRefMapMode(aMapMode); + ImpSetupDrawOutlinerForPaint( + bContourFrame, + rOutl, + aTextRect, + aAnchorRect, + rPaintRect, + aFitXCorrection); +} + + +OutlinerParaObject* SdrTextObj::GetOutlinerParaObject() const +{ + SdrText* pText = getActiveText(); + if( pText ) + return pText->GetOutlinerParaObject(); + else + return nullptr; +} + +void SdrTextObj::NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject) +{ + NbcSetOutlinerParaObjectForText( std::move(pTextObject), getActiveText() ); +} + +namespace +{ + bool IsAutoGrow(const SdrTextObj& rObj) + { + bool bAutoGrow = rObj.IsAutoGrowHeight() || rObj.IsAutoGrowWidth(); + return bAutoGrow && !utl::ConfigManager::IsFuzzing(); + } +} + +void SdrTextObj::NbcSetOutlinerParaObjectForText( std::optional<OutlinerParaObject> pTextObject, SdrText* pText ) +{ + if( pText ) + pText->SetOutlinerParaObject( std::move(pTextObject) ); + + if (pText && pText->GetOutlinerParaObject()) + { + SvxWritingModeItem aWritingMode(pText->GetOutlinerParaObject()->IsEffectivelyVertical() && pText->GetOutlinerParaObject()->IsTopToBottom() + ? css::text::WritingMode_TB_RL + : css::text::WritingMode_LR_TB, + SDRATTR_TEXTDIRECTION); + GetProperties().SetObjectItemDirect(aWritingMode); + } + + SetTextSizeDirty(); + if (IsTextFrame() && IsAutoGrow(*this)) + { // adapt text frame! + NbcAdjustTextFrameWidthAndHeight(); + } + if (!IsTextFrame()) + { + // the SnapRect keeps its size + SetBoundAndSnapRectsDirty(true); + } + + // always invalidate BoundRect on change + SetBoundRectDirty(); + ActionChanged(); + + ImpSetTextStyleSheetListeners(); +} + +void SdrTextObj::NbcReformatText() +{ + SdrText* pText = getActiveText(); + if( !(pText && pText->GetOutlinerParaObject()) ) + return; + + pText->ReformatText(); + if (mbTextFrame) + { + NbcAdjustTextFrameWidthAndHeight(); + } + else + { + // the SnapRect keeps its size + SetBoundRectDirty(); + SetBoundAndSnapRectsDirty(/*bNotMyself*/true); + } + SetTextSizeDirty(); + ActionChanged(); + // i22396 + // Necessary here since we have no compare operator at the outliner + // para object which may detect changes regarding the combination + // of outliner para data and configuration (e.g., change of + // formatting of text numerals) + GetViewContact().flushViewObjectContacts(false); +} + +std::unique_ptr<SdrObjGeoData> SdrTextObj::NewGeoData() const +{ + return std::make_unique<SdrTextObjGeoData>(); +} + +void SdrTextObj::SaveGeoData(SdrObjGeoData& rGeo) const +{ + SdrAttrObj::SaveGeoData(rGeo); + SdrTextObjGeoData& rTGeo=static_cast<SdrTextObjGeoData&>(rGeo); + rTGeo.maRect = maRect; + rTGeo.maGeo = maGeo; +} + +void SdrTextObj::RestoreGeoData(const SdrObjGeoData& rGeo) +{ // RectsDirty is called by SdrObject + SdrAttrObj::RestoreGeoData(rGeo); + const SdrTextObjGeoData& rTGeo=static_cast<const SdrTextObjGeoData&>(rGeo); + NbcSetLogicRect(rTGeo.maRect); + maGeo = rTGeo.maGeo; + SetTextSizeDirty(); +} + +drawing::TextFitToSizeType SdrTextObj::GetFitToSize() const +{ + drawing::TextFitToSizeType eType = drawing::TextFitToSizeType_NONE; + + if(!IsAutoGrowWidth()) + eType = GetObjectItem(SDRATTR_TEXT_FITTOSIZE).GetValue(); + + return eType; +} + +const tools::Rectangle& SdrTextObj::GetGeoRect() const +{ + return maRect; +} + +void SdrTextObj::ForceOutlinerParaObject() +{ + SdrText* pText = getActiveText(); + if( pText && (pText->GetOutlinerParaObject() == nullptr) ) + { + OutlinerMode nOutlMode = OutlinerMode::TextObject; + if( IsTextFrame() && meTextKind == SdrObjKind::OutlineText ) + nOutlMode = OutlinerMode::OutlineObject; + + pText->ForceOutlinerParaObject( nOutlMode ); + } +} + +TextChain *SdrTextObj::GetTextChain() const +{ + //if (!IsChainable()) + // return NULL; + + return getSdrModelFromSdrObject().GetTextChain(); +} + +bool SdrTextObj::IsVerticalWriting() const +{ + if(mpEditingOutliner) + { + return mpEditingOutliner->IsVertical(); + } + + OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject(); + if(pOutlinerParaObject) + { + return pOutlinerParaObject->IsEffectivelyVertical(); + } + + return false; +} + +void SdrTextObj::SetVerticalWriting(bool bVertical) +{ + OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject(); + + if( !pOutlinerParaObject && bVertical ) + { + // we only need to force an outliner para object if the default of + // horizontal text is changed + ForceOutlinerParaObject(); + pOutlinerParaObject = GetOutlinerParaObject(); + } + + if (!pOutlinerParaObject || + (pOutlinerParaObject->IsEffectivelyVertical() == bVertical)) + return; + + // get item settings + const SfxItemSet& rSet = GetObjectItemSet(); + bool bAutoGrowWidth = rSet.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue(); + bool bAutoGrowHeight = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue(); + + // Also exchange hor/ver adjust items + SdrTextHorzAdjust eHorz = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue(); + SdrTextVertAdjust eVert = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue(); + + // rescue object size + tools::Rectangle aObjectRect = GetSnapRect(); + + // prepare ItemSet to set exchanged width and height items + SfxItemSetFixed<SDRATTR_TEXT_AUTOGROWHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT, + // Expanded item ranges to also support hor and ver adjust. + SDRATTR_TEXT_VERTADJUST, SDRATTR_TEXT_VERTADJUST, + SDRATTR_TEXT_AUTOGROWWIDTH, SDRATTR_TEXT_HORZADJUST> aNewSet(*rSet.GetPool()); + + aNewSet.Put(rSet); + aNewSet.Put(makeSdrTextAutoGrowWidthItem(bAutoGrowHeight)); + aNewSet.Put(makeSdrTextAutoGrowHeightItem(bAutoGrowWidth)); + + // Exchange horz and vert adjusts + switch (eVert) + { + case SDRTEXTVERTADJUST_TOP: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); break; + case SDRTEXTVERTADJUST_CENTER: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_CENTER)); break; + case SDRTEXTVERTADJUST_BOTTOM: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT)); break; + case SDRTEXTVERTADJUST_BLOCK: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_BLOCK)); break; + } + switch (eHorz) + { + case SDRTEXTHORZADJUST_LEFT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BOTTOM)); break; + case SDRTEXTHORZADJUST_CENTER: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); break; + case SDRTEXTHORZADJUST_RIGHT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP)); break; + case SDRTEXTHORZADJUST_BLOCK: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BLOCK)); break; + } + + SetObjectItemSet(aNewSet); + + pOutlinerParaObject = GetOutlinerParaObject(); + if (pOutlinerParaObject) + { + // set ParaObject orientation accordingly + pOutlinerParaObject->SetVertical(bVertical); + } + + // restore object size + SetSnapRect(aObjectRect); +} + +bool SdrTextObj::IsTopToBottom() const +{ + if (mpEditingOutliner) + return mpEditingOutliner->IsTopToBottom(); + + if (OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject()) + return pOutlinerParaObject->IsTopToBottom(); + + return false; +} + +// transformation interface for StarOfficeAPI. This implements support for +// homogeneous 3x3 matrices containing the transformation of the SdrObject. At the +// moment it contains a shearX, rotation and translation, but for setting all linear +// transforms like Scale, ShearX, ShearY, Rotate and Translate are supported. + + +// gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon +// with the base geometry and returns TRUE. Otherwise it returns FALSE. +bool SdrTextObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const +{ + // get turn and shear + double fRotate = toRadians(maGeo.nRotationAngle); + double fShearX = toRadians(maGeo.nShearAngle); + + // get aRect, this is the unrotated snaprect + tools::Rectangle aRectangle(maRect); + + // fill other values + basegfx::B2DTuple aScale(aRectangle.GetWidth(), aRectangle.GetHeight()); + basegfx::B2DTuple aTranslate(aRectangle.Left(), aRectangle.Top()); + + // position maybe relative to anchorpos, convert + if( getSdrModelFromSdrObject().IsWriter() ) + { + if(GetAnchorPos().X() || GetAnchorPos().Y()) + { + aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y()); + } + } + + // build matrix + rMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + aScale, + basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX), + basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate, + aTranslate); + + return false; +} + +// sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix. +// If it's an SdrPathObj it will use the provided geometry information. The Polygon has +// to use (0,0) as upper left and will be scaled to the given size in the matrix. +void SdrTextObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/) +{ + // break up matrix + basegfx::B2DTuple aScale; + basegfx::B2DTuple aTranslate; + double fRotate(0.0); + double fShearX(0.0); + rMatrix.decompose(aScale, aTranslate, fRotate, fShearX); + + // flip? + bool bFlipX = aScale.getX() < 0.0, + bFlipY = aScale.getY() < 0.0; + if (bFlipX) + { + aScale.setX(fabs(aScale.getX())); + } + if (bFlipY) + { + aScale.setY(fabs(aScale.getY())); + } + + // reset object shear and rotations + maGeo.nRotationAngle = 0_deg100; + maGeo.RecalcSinCos(); + maGeo.nShearAngle = 0_deg100; + maGeo.RecalcTan(); + + // if anchor is used, make position relative to it + if( getSdrModelFromSdrObject().IsWriter() ) + { + if(GetAnchorPos().X() || GetAnchorPos().Y()) + { + aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y()); + } + } + + // build and set BaseRect (use scale) + Size aSize(FRound(aScale.getX()), FRound(aScale.getY())); + tools::Rectangle aBaseRect(Point(), aSize); + SetSnapRect(aBaseRect); + + // flip? + if (bFlipX) + { + Mirror(Point(), Point(0, 1)); + } + if (bFlipY) + { + Mirror(Point(), Point(1, 0)); + } + + // shear? + if(!basegfx::fTools::equalZero(fShearX)) + { + GeoStat aGeoStat; + aGeoStat.nShearAngle = Degree100(FRound(basegfx::rad2deg<100>(atan(fShearX)))); + aGeoStat.RecalcTan(); + Shear(Point(), aGeoStat.nShearAngle, aGeoStat.mfTanShearAngle, false); + } + + // rotation? + if(!basegfx::fTools::equalZero(fRotate)) + { + GeoStat aGeoStat; + + // #i78696# + // fRotate is matematically correct, but aGeoStat.nRotationAngle is + // mirrored -> mirror value here + aGeoStat.nRotationAngle = NormAngle36000(Degree100(FRound(-basegfx::rad2deg<100>(fRotate)))); + aGeoStat.RecalcSinCos(); + Rotate(Point(), aGeoStat.nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle); + } + + // translate? + if(!aTranslate.equalZero()) + { + Move(Size(FRound(aTranslate.getX()), FRound(aTranslate.getY()))); + } +} + +bool SdrTextObj::IsReallyEdited() const +{ + return mpEditingOutliner && mpEditingOutliner->IsModified(); +} + +// moved inlines here form hxx + +tools::Long SdrTextObj::GetEckenradius() const +{ + return GetObjectItemSet().Get(SDRATTR_CORNER_RADIUS).GetValue(); +} + +tools::Long SdrTextObj::GetMinTextFrameHeight() const +{ + return GetObjectItemSet().Get(SDRATTR_TEXT_MINFRAMEHEIGHT).GetValue(); +} + +tools::Long SdrTextObj::GetMaxTextFrameHeight() const +{ + return GetObjectItemSet().Get(SDRATTR_TEXT_MAXFRAMEHEIGHT).GetValue(); +} + +tools::Long SdrTextObj::GetMinTextFrameWidth() const +{ + return GetObjectItemSet().Get(SDRATTR_TEXT_MINFRAMEWIDTH).GetValue(); +} + +tools::Long SdrTextObj::GetMaxTextFrameWidth() const +{ + return GetObjectItemSet().Get(SDRATTR_TEXT_MAXFRAMEWIDTH).GetValue(); +} + +bool SdrTextObj::IsFontwork() const +{ + return !mbTextFrame // Default is FALSE + && GetObjectItemSet().Get(XATTR_FORMTXTSTYLE).GetValue() != XFormTextStyle::NONE; +} + +bool SdrTextObj::IsHideContour() const +{ + return !mbTextFrame // Default is: no, don't HideContour; HideContour not together with TextFrames + && GetObjectItemSet().Get(XATTR_FORMTXTHIDEFORM).GetValue(); +} + +bool SdrTextObj::IsContourTextFrame() const +{ + return !mbTextFrame // ContourFrame not together with normal TextFrames + && GetObjectItemSet().Get(SDRATTR_TEXT_CONTOURFRAME).GetValue(); +} + +tools::Long SdrTextObj::GetTextLeftDistance() const +{ + return GetObjectItemSet().Get(SDRATTR_TEXT_LEFTDIST).GetValue(); +} + +tools::Long SdrTextObj::GetTextRightDistance() const +{ + return GetObjectItemSet().Get(SDRATTR_TEXT_RIGHTDIST).GetValue(); +} + +tools::Long SdrTextObj::GetTextUpperDistance() const +{ + return GetObjectItemSet().Get(SDRATTR_TEXT_UPPERDIST).GetValue(); +} + +tools::Long SdrTextObj::GetTextLowerDistance() const +{ + return GetObjectItemSet().Get(SDRATTR_TEXT_LOWERDIST).GetValue(); +} + +SdrTextAniKind SdrTextObj::GetTextAniKind() const +{ + return GetObjectItemSet().Get(SDRATTR_TEXT_ANIKIND).GetValue(); +} + +SdrTextAniDirection SdrTextObj::GetTextAniDirection() const +{ + return GetObjectItemSet().Get(SDRATTR_TEXT_ANIDIRECTION).GetValue(); +} + +bool SdrTextObj::HasTextColumnsNumber() const +{ + return GetObjectItemSet().HasItem(SDRATTR_TEXTCOLUMNS_NUMBER); +} + +sal_Int16 SdrTextObj::GetTextColumnsNumber() const +{ + return GetObjectItemSet().Get(SDRATTR_TEXTCOLUMNS_NUMBER).GetValue(); +} + +void SdrTextObj::SetTextColumnsNumber(sal_Int16 nColumns) +{ + SetObjectItem(SfxInt16Item(SDRATTR_TEXTCOLUMNS_NUMBER, nColumns)); +} + +bool SdrTextObj::HasTextColumnsSpacing() const +{ + return GetObjectItemSet().HasItem(SDRATTR_TEXTCOLUMNS_SPACING); +} + +sal_Int32 SdrTextObj::GetTextColumnsSpacing() const +{ + return GetObjectItemSet().Get(SDRATTR_TEXTCOLUMNS_SPACING).GetValue(); +} + +void SdrTextObj::SetTextColumnsSpacing(sal_Int32 nSpacing) +{ + SetObjectItem(SdrMetricItem(SDRATTR_TEXTCOLUMNS_SPACING, nSpacing)); +} + +// Get necessary data for text scroll animation. ATM base it on a Text-Metafile and a +// painting rectangle. Rotation is excluded from the returned values. +GDIMetaFile* SdrTextObj::GetTextScrollMetaFileAndRectangle( + tools::Rectangle& rScrollRectangle, tools::Rectangle& rPaintRectangle) +{ + GDIMetaFile* pRetval = nullptr; + SdrOutliner& rOutliner = ImpGetDrawOutliner(); + tools::Rectangle aTextRect; + tools::Rectangle aAnchorRect; + tools::Rectangle aPaintRect; + Fraction aFitXCorrection(1,1); + bool bContourFrame(IsContourTextFrame()); + + // get outliner set up. To avoid getting a somehow rotated MetaFile, + // temporarily disable object rotation. + Degree100 nAngle(maGeo.nRotationAngle); + maGeo.nRotationAngle = 0_deg100; + ImpSetupDrawOutlinerForPaint( bContourFrame, rOutliner, aTextRect, aAnchorRect, aPaintRect, aFitXCorrection ); + maGeo.nRotationAngle = nAngle; + + tools::Rectangle aScrollFrameRect(aPaintRect); + const SfxItemSet& rSet = GetObjectItemSet(); + SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue(); + + if(SdrTextAniDirection::Left == eDirection || SdrTextAniDirection::Right == eDirection) + { + aScrollFrameRect.SetLeft( aAnchorRect.Left() ); + aScrollFrameRect.SetRight( aAnchorRect.Right() ); + } + + if(SdrTextAniDirection::Up == eDirection || SdrTextAniDirection::Down == eDirection) + { + aScrollFrameRect.SetTop( aAnchorRect.Top() ); + aScrollFrameRect.SetBottom( aAnchorRect.Bottom() ); + } + + // create the MetaFile + pRetval = new GDIMetaFile; + ScopedVclPtrInstance< VirtualDevice > pBlackHole; + pBlackHole->EnableOutput(false); + pRetval->Record(pBlackHole); + Point aPaintPos = aPaintRect.TopLeft(); + + rOutliner.Draw(*pBlackHole, aPaintPos); + + pRetval->Stop(); + pRetval->WindStart(); + + // return PaintRectanglePixel and pRetval; + rScrollRectangle = aScrollFrameRect; + rPaintRectangle = aPaintRect; + + return pRetval; +} + +// Access to TextAnimationAllowed flag +bool SdrTextObj::IsAutoFit() const +{ + return GetFitToSize() == drawing::TextFitToSizeType_AUTOFIT; +} + +bool SdrTextObj::IsFitToSize() const +{ + const drawing::TextFitToSizeType eFit = GetFitToSize(); + return (eFit == drawing::TextFitToSizeType_PROPORTIONAL + || eFit == drawing::TextFitToSizeType_ALLLINES); +} + +void SdrTextObj::SetTextAnimationAllowed(bool bNew) +{ + if(mbTextAnimationAllowed != bNew) + { + mbTextAnimationAllowed = bNew; + ActionChanged(); + } +} + +/** called from the SdrObjEditView during text edit when the status of the edit outliner changes */ +void SdrTextObj::onEditOutlinerStatusEvent( EditStatus* pEditStatus ) +{ + const EditStatusFlags nStat = pEditStatus->GetStatusWord(); + const bool bGrowX = bool(nStat & EditStatusFlags::TEXTWIDTHCHANGED); + const bool bGrowY = bool(nStat & EditStatusFlags::TextHeightChanged); + if(!(mbTextFrame && (bGrowX || bGrowY))) + return; + + if ((bGrowX && IsAutoGrowWidth()) || (bGrowY && IsAutoGrowHeight())) + { + AdjustTextFrameWidthAndHeight(); + } + else if ( (IsAutoFit() || IsFitToSize()) && !mbInDownScale) + { + assert(mpEditingOutliner); + mbInDownScale = true; + + // sucks that we cannot disable paints via + // mpEditingOutliner->SetUpdateMode(FALSE) - but EditEngine skips + // formatting as well, then. + ImpAutoFitText(*mpEditingOutliner); + mbInDownScale = false; + } +} + +/* Begin chaining code */ + +// XXX: Make it a method somewhere? +static SdrObject *ImpGetObjByName(SdrObjList const *pObjList, std::u16string_view aObjName) +{ + // scan the whole list + size_t nObjCount = pObjList->GetObjCount(); + for (size_t i = 0; i < nObjCount; i++) { + SdrObject *pCurObj = pObjList->GetObj(i); + + if (pCurObj->GetName() == aObjName) { + return pCurObj; + } + } + // not found + return nullptr; +} + +// XXX: Make it a (private) method of SdrTextObj +static void ImpUpdateChainLinks(SdrTextObj *pTextObj, std::u16string_view aNextLinkName) +{ + // XXX: Current implementation constraints text boxes to be on the same page + + // No next link + if (aNextLinkName.empty()) { + pTextObj->SetNextLinkInChain(nullptr); + return; + } + + SdrPage *pPage(pTextObj->getSdrPageFromSdrObject()); + assert(pPage); + SdrTextObj *pNextTextObj = dynamic_cast< SdrTextObj * > + (ImpGetObjByName(pPage, aNextLinkName)); + if (!pNextTextObj) { + SAL_INFO("svx.chaining", "[CHAINING] Can't find object as next link."); + return; + } + + pTextObj->SetNextLinkInChain(pNextTextObj); +} + +bool SdrTextObj::IsChainable() const +{ + // Read it as item + const SfxItemSet& rSet = GetObjectItemSet(); + OUString aNextLinkName = rSet.Get(SDRATTR_TEXT_CHAINNEXTNAME).GetValue(); + + // Update links if any inconsistency is found + bool bNextLinkUnsetYet = !aNextLinkName.isEmpty() && !mpNextInChain; + bool bInconsistentNextLink = mpNextInChain && mpNextInChain->GetName() != aNextLinkName; + // if the link is not set despite there should be one OR if it has changed + if (bNextLinkUnsetYet || bInconsistentNextLink) { + ImpUpdateChainLinks(const_cast<SdrTextObj *>(this), aNextLinkName); + } + + return !aNextLinkName.isEmpty(); // XXX: Should we also check for GetNilChainingEvent? (see old code below) + +/* + // Check that no overflow is going on + if (!GetTextChain() || GetTextChain()->GetNilChainingEvent(this)) + return false; +*/ +} + +void SdrTextObj::onChainingEvent() +{ + if (!mpEditingOutliner) + return; + + // Outliner for text transfer + SdrOutliner &aDrawOutliner = ImpGetDrawOutliner(); + + EditingTextChainFlow aTxtChainFlow(this); + aTxtChainFlow.CheckForFlowEvents(mpEditingOutliner); + + if (aTxtChainFlow.IsOverflow()) { + SAL_INFO("svx.chaining", "[CHAINING] Overflow going on"); + // One outliner is for non-overflowing text, the other for overflowing text + // We remove text directly from the editing outliner + aTxtChainFlow.ExecuteOverflow(mpEditingOutliner, &aDrawOutliner); + } else if (aTxtChainFlow.IsUnderflow()) { + SAL_INFO("svx.chaining", "[CHAINING] Underflow going on"); + // underflow-induced overflow + aTxtChainFlow.ExecuteUnderflow(&aDrawOutliner); + bool bIsOverflowFromUnderflow = aTxtChainFlow.IsOverflow(); + // handle overflow + if (bIsOverflowFromUnderflow) { + SAL_INFO("svx.chaining", "[CHAINING] Overflow going on (underflow induced)"); + // prevents infinite loops when setting text for editing outliner + aTxtChainFlow.ExecuteOverflow(&aDrawOutliner, &aDrawOutliner); + } + } +} + +SdrTextObj* SdrTextObj::GetNextLinkInChain() const +{ + /* + if (GetTextChain()) + return GetTextChain()->GetNextLink(this); + + return NULL; + */ + + return mpNextInChain; +} + +void SdrTextObj::SetNextLinkInChain(SdrTextObj *pNextObj) +{ + // Basically a doubly linked list implementation + + SdrTextObj *pOldNextObj = mpNextInChain; + + // Replace next link + mpNextInChain = pNextObj; + // Deal with old next link's prev link + if (pOldNextObj) { + pOldNextObj->mpPrevInChain = nullptr; + } + + // Deal with new next link's prev link + if (mpNextInChain) { + // If there is a prev already at all and this is not already the current object + if (mpNextInChain->mpPrevInChain && + mpNextInChain->mpPrevInChain != this) + mpNextInChain->mpPrevInChain->mpNextInChain = nullptr; + mpNextInChain->mpPrevInChain = this; + } + + // TODO: Introduce check for circular chains + +} + +SdrTextObj* SdrTextObj::GetPrevLinkInChain() const +{ + /* + if (GetTextChain()) + return GetTextChain()->GetPrevLink(this); + + return NULL; + */ + + return mpPrevInChain; +} + +bool SdrTextObj::GetPreventChainable() const +{ + // Prevent chaining it 1) during dragging && 2) when we are editing next link + return mbIsUnchainableClone || (GetNextLinkInChain() && GetNextLinkInChain()->IsInEditMode()); +} + +SdrObjectUniquePtr SdrTextObj::getFullDragClone() const +{ + SdrObjectUniquePtr pClone = SdrAttrObj::getFullDragClone(); + SdrTextObj *pTextObjClone = dynamic_cast<SdrTextObj *>(pClone.get()); + if (pTextObjClone != nullptr) { + // Avoid transferring of text for chainable object during dragging + pTextObjClone->mbIsUnchainableClone = true; + } + + return pClone; + } + +/* End chaining code */ + +/** returns the currently active text. */ +SdrText* SdrTextObj::getActiveText() const +{ + if( !mpText ) + return getText( 0 ); + else + return mpText.get(); +} + +/** returns the nth available text. */ +SdrText* SdrTextObj::getText( sal_Int32 nIndex ) const +{ + if( nIndex == 0 ) + { + if( !mpText ) + const_cast< SdrTextObj* >(this)->mpText.reset( new SdrText( *const_cast< SdrTextObj* >(this) ) ); + return mpText.get(); + } + else + { + return nullptr; + } +} + +/** returns the number of texts available for this object. */ +sal_Int32 SdrTextObj::getTextCount() const +{ + return 1; +} + +/** changes the current active text */ +void SdrTextObj::setActiveText( sal_Int32 /*nIndex*/ ) +{ +} + +/** returns the index of the text that contains the given point or -1 */ +sal_Int32 SdrTextObj::CheckTextHit(const Point& /*rPnt*/) const +{ + return 0; +} + +void SdrTextObj::SetObjectItemNoBroadcast(const SfxPoolItem& rItem) +{ + static_cast< sdr::properties::TextProperties& >(GetProperties()).SetObjectItemNoBroadcast(rItem); +} + + +// The concept of the text object: +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// Attributes/Variations: +// - bool text frame / graphics object with caption +// - bool FontWork (if it is not a text frame and not a ContourTextFrame) +// - bool ContourTextFrame (if it is not a text frame and not Fontwork) +// - long rotation angle (if it is not FontWork) +// - long text frame margins (if it is not FontWork) +// - bool FitToSize (if it is not FontWork) +// - bool AutoGrowingWidth/Height (if it is not FitToSize and not FontWork) +// - long Min/MaxFrameWidth/Height (if AutoGrowingWidth/Height) +// - enum horizontal text anchoring left,center,right,justify/block,Stretch(ni) +// - enum vertical text anchoring top, middle, bottom, block, stretch(ni) +// - enum ticker text (if it is not FontWork) + +// Every derived object is either a text frame (mbTextFrame=true) +// or a drawing object with a caption (mbTextFrame=false). + +// Default anchoring for text frames: +// SDRTEXTHORZADJUST_BLOCK, SDRTEXTVERTADJUST_TOP +// = static Pool defaults +// Default anchoring for drawing objects with a caption: +// SDRTEXTHORZADJUST_CENTER, SDRTEXTVERTADJUST_CENTER +// via "hard" attribution of SdrAttrObj + +// Every object derived from SdrTextObj must return an "UnrotatedSnapRect" +// (->TakeUnrotatedSnapRect()) (the reference point for the rotation is the top +// left of the rectangle (maGeo.nRotationAngle)) which is the basis for anchoring +// text. We then subtract the text frame margins from this rectangle, as a re- +// sult we get the anchoring area (->TakeTextAnchorRect()). Within this area, we +// calculate the anchoring point and the painting area, depending on the hori- +// zontal and vertical adjustment of the text (SdrTextVertAdjust, +// SdrTextHorzAdjust). +// In the case of drawing objects with a caption the painting area might well +// be larger than the anchoring area, for text frames on the other hand, it is +// always of the same or a smaller size (except when there are negative text +// frame margins). + +// FitToSize takes priority over text anchoring and AutoGrowHeight/Width. When +// FitToSize is turned on, the painting area is always equal to the anchoring +// area. Additionally, FitToSize doesn't allow automatic line breaks. + +// ContourTextFrame: +// - long rotation angle +// - long text frame margins (maybe later) +// - bool FitToSize (maybe later) +// - bool AutoGrowingWidth/Height (maybe much later) +// - long Min/MaxFrameWidth/Height (maybe much later) +// - enum horizontal text anchoring (maybe later, for now: left, centered) +// - enum vertical text anchoring (maybe later, for now: top) +// - enum ticker text (maybe later, maybe even with correct clipping) + +// When making changes, check these: +// - Paint +// - HitTest +// - ConvertToPoly +// - Edit +// - Printing, Saving, Painting in neighboring View while editing +// - ModelChanged (e. g. through a neighboring View or rulers) while editing +// - FillColorChanged while editing +// - and many more... + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |