Until now the 'local' StyleSheet got set, but that may not be the same as the original. It was/gets identified just by searching by name and type, not comparing content. What to do? We could... (a) the Style-Friendly way: - check if StyleSheet is identical - if not, create a new in-between StyleSheet hosting missing Items - if yes, use, done. (b) the pragmatic/practical way: - Just hard-attribute the Items that are detected as different similar to what BurnInStyleSheetAttributes does/did, but only for a view differing Items (b) is the most simple for now, but will lead to more hard attributes. The other possibilities will be better in the long term. For now, I will use (b). Note that there are more cases in that VERY complex and grown stuff emerging from SdDrawDocument::PasteBookmarkAsPage. More things will need to be fixed, see this as a 1st step. Needed to adapt postProcessAfterCloneToDifferentModel due to UT testTdf96206 failing, see comment there for details. That 1st implementation showed weaknesses with those Items critical and only on MasterPages, so decided to give up on that and implement ContentCompare to SdPage::Equals. That was easier than thought: No parameter needed, only used by InsertBookmarkAsPage_FindDuplicateLayouts::operator(). Thus, SdPage::Equals needs no change, just SdrObject::Equals. Due to still problems with Items corrected in Objects on MasterPages I will for now exclude this. Could now isolate remaining problems, these are defaults and/or PresObj things on MasterPages. Important is that I can completely correct all Items for all Objects in postProcessAfterCloneToDifferentModel. I can react to these false-positives in the ContentCompare in SdrObject::Equals. Signed-off-by: Armin Le Grand (collabora) <Armin.LeGrand@collabora.com> Change-Id: I10e17c8d6a893dbe92ab716182f07bafce1f3284 Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/6595 Tested-by: Jenkins CPCI <releng@collaboraoffice.com> Reviewed-by: Armin Le Grand <armin.legrand@collabora.com> (cherry picked from commit 92ffaeac04c5c8586006b026f29ce12fba3674f1) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211729 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
2129 lines
67 KiB
C++
2129 lines
67 KiB
C++
/* -*- 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/svdetc.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/rendercontext/GetDefaultFontFlags.hxx>
|
|
#include <vcl/virdev.hxx>
|
|
#include <basegfx/matrix/b2dhommatrixtools.hxx>
|
|
#include <sal/log.hxx>
|
|
#include <o3tl/unit_conversion.hxx>
|
|
#include <o3tl/temporary.hxx>
|
|
#include <comphelper/configuration.hxx>
|
|
#include <editeng/eeitem.hxx>
|
|
#include <editeng/fhgtitem.hxx>
|
|
#include <unotools/fontdefs.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, const tools::Rectangle& rRectangle, std::optional<SdrObjKind> oeTextKind)
|
|
: SdrAttrObj(rSdrModel)
|
|
, mpEditingOutliner(nullptr)
|
|
, meTextKind(oeTextKind ? *oeTextKind : SdrObjKind::Text)
|
|
, maTextEditOffset(Point())
|
|
, mbTextFrame(false)
|
|
, mbNoShear(false)
|
|
, mbTextSizeDirty(false)
|
|
, mbInEditMode(false)
|
|
, mbDisableAutoWidthOnDragging(false)
|
|
, mbTextAnimationAllowed(true)
|
|
, mbInDownScale(false)
|
|
{
|
|
if (!rRectangle.IsEmpty())
|
|
{
|
|
tools::Rectangle aRectangle(rRectangle);
|
|
ImpJustifyRect(aRectangle);
|
|
setRectangle(aRectangle);
|
|
}
|
|
|
|
if (oeTextKind)
|
|
{
|
|
mbTextFrame = true;
|
|
mbNoShear = true;
|
|
}
|
|
|
|
mbSupportTextIndentingOnLineWidthChange = true;
|
|
}
|
|
|
|
SdrTextObj::SdrTextObj(SdrModel& rSdrModel, SdrTextObj const & rSource)
|
|
: SdrAttrObj(rSdrModel, rSource)
|
|
, mpEditingOutliner(nullptr)
|
|
, meTextKind(rSource.meTextKind)
|
|
, maTextEditOffset(Point(0, 0))
|
|
, mbTextFrame(rSource.mbTextFrame)
|
|
, mbNoShear(rSource.mbNoShear)
|
|
, mbTextSizeDirty(rSource.mbTextSizeDirty)
|
|
, mbInEditMode(false)
|
|
, mbDisableAutoWidthOnDragging(rSource.mbDisableAutoWidthOnDragging)
|
|
, mbTextAnimationAllowed(true)
|
|
, mbInDownScale(false)
|
|
{
|
|
// #i25616#
|
|
mbSupportTextIndentingOnLineWidthChange = true;
|
|
|
|
maRectangle = rSource.maRectangle;
|
|
maGeo = rSource.maGeo;
|
|
maTextSize = rSource.maTextSize;
|
|
|
|
// Not all of the necessary parameters were copied yet.
|
|
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()
|
|
{
|
|
mxText.clear();
|
|
ImpDeregisterLink();
|
|
}
|
|
|
|
void SdrTextObj::FitFrameToTextSize()
|
|
{
|
|
ImpJustifyRect(maRectangle);
|
|
|
|
SdrText* pText = getActiveText();
|
|
if(pText==nullptr || !pText->GetOutlinerParaObject())
|
|
return;
|
|
|
|
SdrOutliner& rOutliner=ImpGetDrawOutliner();
|
|
rOutliner.SetPaperSize(Size(getRectangle().Right() - getRectangle().Left(), getRectangle().Bottom() - getRectangle().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(getRectangle());
|
|
aNewRect.SetSize(aNewSize);
|
|
ImpJustifyRect(aNewRect);
|
|
|
|
if (aNewRect != getRectangle())
|
|
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.Normalize();
|
|
if (rRect.Left()==rRect.Right()) rRect.AdjustRight( 1 );
|
|
if (rRect.Top()==rRect.Bottom()) rRect.AdjustBottom( 1 );
|
|
}
|
|
}
|
|
|
|
void SdrTextObj::ImpCheckShear()
|
|
{
|
|
if (mbNoShear && maGeo.m_nShearAngle)
|
|
{
|
|
maGeo.m_nShearAngle = 0_deg100;
|
|
maGeo.mfTanShearAngle = 0;
|
|
}
|
|
}
|
|
|
|
void SdrTextObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
|
|
{
|
|
bool bNoTextFrame=!IsTextFrame();
|
|
rInfo.bResizeFreeAllowed=bNoTextFrame || ((maGeo.m_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;
|
|
|
|
SfxItemSet aSet(SfxItemSet::makeFixedSfxItemSet<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
|
|
// contains SDRATTR_TEXT_MAXFRAMEWIDTH
|
|
SDRATTR_TEXT_MINFRAMEWIDTH, SDRATTR_TEXT_AUTOGROWWIDTH>(*GetObjectItemSet().GetPool()));
|
|
|
|
if(bW)
|
|
{
|
|
// Set minimum width.
|
|
const tools::Long nDist = GetTextLeftDistance() + GetTextRightDistance();
|
|
const tools::Long nW = std::max<tools::Long>(0, getRectangle().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, getRectangle().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.m_nRotationAngle)
|
|
{
|
|
// Unrotate!
|
|
aMatrix.rotate(-toRadians(maGeo.m_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
|
|
rtl::Reference<SdrTextObj> pCopy = SdrObject::Clone(*this, getSdrModelFromSdrObject());
|
|
pCopy->SetMergedItem(makeSdrShadowItem(false));
|
|
*pContourPolyPolygon = pCopy->TakeContour();
|
|
}
|
|
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 = getRectangle();
|
|
}
|
|
|
|
// See also: <unnamed>::getTextAnchorRange in svx/source/sdr/primitive2d/sdrdecompositiontools.cxx
|
|
void SdrTextObj::AdjustRectToTextDistance(tools::Rectangle& rAnchorRect, double fExtraRot) const
|
|
{
|
|
const tools::Long nLeftDist = GetTextLeftDistance();
|
|
const tools::Long nRightDist = GetTextRightDistance();
|
|
const tools::Long nUpperDist = GetTextUpperDistance();
|
|
const tools::Long nLowerDist = GetTextLowerDistance();
|
|
if (!IsVerticalWriting())
|
|
{
|
|
if (fExtraRot == 180.0)
|
|
{
|
|
rAnchorRect.AdjustLeft(nLeftDist);
|
|
rAnchorRect.AdjustTop(-nUpperDist);
|
|
rAnchorRect.AdjustRight(-nRightDist);
|
|
rAnchorRect.AdjustBottom(nLowerDist);
|
|
}
|
|
else
|
|
{
|
|
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(getRectangle()); // 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.m_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.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
|
|
rOutliner.SetTextObj( this );
|
|
}
|
|
|
|
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.m_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, double& 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
|
|
}
|
|
|
|
rOutliner.setRoundFontSizeToPt(false);
|
|
|
|
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%
|
|
|
|
double nX = nWantWdt / double(nIsWdt); // calculate X stretching
|
|
double nY = nWantHgt / double(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.0)
|
|
nX = -nX;
|
|
if (nX < 0.01)
|
|
{
|
|
nX = 0.01;
|
|
bNoMoreLoop = true;
|
|
}
|
|
if (nX > 655.35)
|
|
{
|
|
nX = 655.35;
|
|
bNoMoreLoop = true;
|
|
}
|
|
|
|
if (nY < 0.0)
|
|
{
|
|
nY = -nY;
|
|
}
|
|
if (nY < 0.01)
|
|
{
|
|
nY = 0.01;
|
|
bNoMoreLoop = true;
|
|
}
|
|
if (nY > 655.35)
|
|
{
|
|
nY = 655.35;
|
|
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.setScalingParameters({nX, nY});
|
|
nLoopCount++;
|
|
Size aSiz(rOutliner.CalcTextSize());
|
|
tools::Long nXDiff = aSiz.Width() - nWantWdt;
|
|
rFitXCorrection = double(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.0; // but only add half of what we calculated,
|
|
else
|
|
nMul += (nDiv - nMul) / 2.0;// because the EditEngine calculates wrongly later on
|
|
}
|
|
nX = nX * double(nMul) / double(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;
|
|
}
|
|
|
|
rtl::Reference<SdrObject> SdrTextObj::implCloneSdrObject(SdrModel& rTargetModel) const
|
|
{
|
|
return new SdrTextObj(rTargetModel, *this);
|
|
}
|
|
|
|
basegfx::B2DPolyPolygon SdrTextObj::TakeXorPoly() const
|
|
{
|
|
tools::Polygon aPol(getRectangle());
|
|
if (maGeo.m_nShearAngle)
|
|
ShearPoly(aPol, getRectangle().TopLeft(), maGeo.mfTanShearAngle);
|
|
if (maGeo.m_nRotationAngle)
|
|
RotatePoly(aPol, getRectangle().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.m_nRotationAngle) RotatePoly(aPol,aR.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
|
|
|
|
aRetval.append(aPol.getB2DPolygon());
|
|
}
|
|
|
|
return aRetval;
|
|
}
|
|
|
|
void SdrTextObj::RecalcSnapRect()
|
|
{
|
|
if (maGeo.m_nRotationAngle || maGeo.m_nShearAngle)
|
|
{
|
|
maSnapRect = Rect2Poly(getRectangle(), maGeo).GetBoundRect();
|
|
} else {
|
|
maSnapRect = getRectangle();
|
|
}
|
|
}
|
|
|
|
sal_uInt32 SdrTextObj::GetSnapPointCount() const
|
|
{
|
|
return 4;
|
|
}
|
|
|
|
Point SdrTextObj::GetSnapPoint(sal_uInt32 i) const
|
|
{
|
|
Point aP;
|
|
const auto& rRectangle = getRectangle();
|
|
switch (i) {
|
|
case 0: aP = rRectangle.TopLeft(); break;
|
|
case 1: aP = rRectangle.TopRight(); break;
|
|
case 2: aP = rRectangle.BottomLeft(); break;
|
|
case 3: aP = rRectangle.BottomRight(); break;
|
|
default: aP = rRectangle.Center(); break;
|
|
}
|
|
if (maGeo.m_nShearAngle)
|
|
ShearPoint(aP, rRectangle.TopLeft(), maGeo.mfTanShearAngle);
|
|
if (maGeo.m_nRotationAngle)
|
|
RotatePoint(aP, rRectangle.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.resetScalingParameters();
|
|
|
|
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,
|
|
double& 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())
|
|
{
|
|
setupAutoFitText(rOutliner);
|
|
}
|
|
}
|
|
|
|
double SdrTextObj::GetFontScale() const
|
|
{
|
|
SdrOutliner& rOutliner = ImpGetDrawOutliner();
|
|
// This eventually calls setupAutoFitText
|
|
UpdateOutlinerFormatting(rOutliner, o3tl::temporary(tools::Rectangle()));
|
|
|
|
return rOutliner.getScalingParameters().fFontY;
|
|
}
|
|
|
|
double SdrTextObj::GetSpacingScale() const
|
|
{
|
|
SdrOutliner& rOutliner = ImpGetDrawOutliner();
|
|
// This eventually calls setupAutoFitText
|
|
UpdateOutlinerFormatting(rOutliner, o3tl::temporary(tools::Rectangle()));
|
|
|
|
return rOutliner.getScalingParameters().fSpacingY;
|
|
}
|
|
|
|
void SdrTextObj::setupAutoFitText(SdrOutliner& rOutliner) const
|
|
{
|
|
const Size aShapeSize = GetSnapRect().GetSize();
|
|
Size aSize(aShapeSize.Width() - GetTextLeftDistance() - GetTextRightDistance(),
|
|
aShapeSize.Height() - GetTextUpperDistance() - GetTextLowerDistance());
|
|
|
|
setupAutoFitText(rOutliner, aSize);
|
|
}
|
|
void SdrTextObj::setupAutoFitText(SdrOutliner& rOutliner, const Size& rTextBoxSize) const
|
|
{
|
|
rOutliner.setRoundFontSizeToPt(true); // We need to round the font size nearest integer pt size
|
|
rOutliner.SetMaxAutoPaperSize(rTextBoxSize);
|
|
rOutliner.SetPaperSize(rTextBoxSize);
|
|
|
|
const SdrTextFitToSizeTypeItem& rItem = GetObjectItem(SDRATTR_TEXT_FITTOSIZE);
|
|
|
|
double fFontScale = rItem.getFontScale();
|
|
double fSpacingScale = rItem.getSpacingScale();
|
|
|
|
if (fFontScale > 0.0 && fSpacingScale > 0.0 && !mbInEditMode)
|
|
{
|
|
rOutliner.setScalingParameters({ fFontScale, fFontScale, 1.0, fSpacingScale });
|
|
}
|
|
else
|
|
{
|
|
rOutliner.resetScalingParameters();
|
|
}
|
|
|
|
rOutliner.QuickFormatDoc();
|
|
}
|
|
|
|
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;
|
|
double aFitXCorrection(1.0);
|
|
|
|
const bool bContourFrame(IsContourTextFrame());
|
|
const MapMode aMapMode(getSdrModelFromSdrObject().GetScaleUnit());
|
|
|
|
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, bool bAdjustTextFrameWidthAndHeight)
|
|
{
|
|
NbcSetOutlinerParaObjectForText( std::move(pTextObject), getActiveText(), bAdjustTextFrameWidthAndHeight );
|
|
}
|
|
|
|
namespace
|
|
{
|
|
bool IsAutoGrow(const SdrTextObj& rObj)
|
|
{
|
|
bool bAutoGrow = rObj.IsAutoGrowHeight() || rObj.IsAutoGrowWidth();
|
|
return bAutoGrow && !comphelper::IsFuzzing();
|
|
}
|
|
}
|
|
|
|
void SdrTextObj::NbcSetOutlinerParaObjectForText( std::optional<OutlinerParaObject> pTextObject, SdrText* pText, bool bAdjustTextFrameWidthAndHeight )
|
|
{
|
|
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) && bAdjustTextFrameWidthAndHeight)
|
|
{ // 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 = getRectangle();
|
|
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 getRectangle();
|
|
}
|
|
|
|
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
|
|
SfxItemSet aNewSet(SfxItemSet::makeFixedSfxItemSet<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>(*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.m_nRotationAngle);
|
|
double fShearX = toRadians(maGeo.m_nShearAngle);
|
|
|
|
// get aRect, this is the unrotated snaprect
|
|
tools::Rectangle aRectangle(getRectangle());
|
|
|
|
// 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),
|
|
-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.m_nRotationAngle = 0_deg100;
|
|
maGeo.RecalcSinCos();
|
|
maGeo.m_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(basegfx::fround<tools::Long>(aScale.getX()),
|
|
basegfx::fround<tools::Long>(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.m_nShearAngle = Degree100(basegfx::fround(basegfx::rad2deg<100>(atan(fShearX))));
|
|
aGeoStat.RecalcTan();
|
|
Shear(Point(), aGeoStat.m_nShearAngle, aGeoStat.mfTanShearAngle, false);
|
|
}
|
|
|
|
// rotation?
|
|
if(!basegfx::fTools::equalZero(fRotate))
|
|
{
|
|
GeoStat aGeoStat;
|
|
|
|
// #i78696#
|
|
// fRotate is mathematically correct, but aGeoStat.nRotationAngle is
|
|
// mirrored -> mirror value here
|
|
aGeoStat.m_nRotationAngle = NormAngle36000(Degree100(basegfx::fround(-basegfx::rad2deg<100>(fRotate))));
|
|
aGeoStat.RecalcSinCos();
|
|
Rotate(Point(), aGeoStat.m_nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
|
|
}
|
|
|
|
// translate?
|
|
if(!aTranslate.equalZero())
|
|
{
|
|
Move(Size(basegfx::fround<tools::Long>(aTranslate.getX()),
|
|
basegfx::fround<tools::Long>(aTranslate.getY())));
|
|
}
|
|
}
|
|
|
|
bool SdrTextObj::IsReallyEdited() const
|
|
{
|
|
return mpEditingOutliner && mpEditingOutliner->IsModified();
|
|
}
|
|
|
|
// moved inlines here from 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;
|
|
double aFitXCorrection(1.0);
|
|
bool bContourFrame(IsContourTextFrame());
|
|
|
|
// get outliner set up. To avoid getting a somehow rotated MetaFile,
|
|
// temporarily disable object rotation.
|
|
Degree100 nAngle(maGeo.m_nRotationAngle);
|
|
maGeo.m_nRotationAngle = 0_deg100;
|
|
ImpSetupDrawOutlinerForPaint( bContourFrame, rOutliner, aTextRect, aAnchorRect, aPaintRect, aFitXCorrection );
|
|
maGeo.m_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.DrawText_ToPosition(*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;
|
|
|
|
// Need to reset scaling so it searches for the fitting size again
|
|
mpEditingOutliner->resetScalingParameters();
|
|
|
|
// sucks that we cannot disable paints via
|
|
// mpEditingOutliner->SetUpdateMode(FALSE) - but EditEngine skips
|
|
// formatting as well, then.
|
|
setupAutoFitText(*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
|
|
for (const rtl::Reference<SdrObject>& pCurObj : *pObjList)
|
|
if (pCurObj->GetName() == aObjName)
|
|
return pCurObj.get();
|
|
// 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 = DynCastSdrTextObj
|
|
(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());
|
|
}
|
|
|
|
rtl::Reference<SdrObject> SdrTextObj::getFullDragClone() const
|
|
{
|
|
rtl::Reference<SdrObject> pClone = SdrAttrObj::getFullDragClone();
|
|
SdrTextObj *pTextObjClone = DynCastSdrTextObj(pClone.get());
|
|
if (pTextObjClone != nullptr) {
|
|
// Avoid transferring of text for chainable object during dragging
|
|
pTextObjClone->mbIsUnchainableClone = true;
|
|
}
|
|
|
|
return pClone;
|
|
}
|
|
|
|
/* End chaining code */
|
|
|
|
std::optional<Color> SdrTextObj::GetActiveTextBackgroundColor(const SdrText* /*pSdrText*/) const
|
|
{
|
|
return GetDraftFillColor(GetMergedItemSet());
|
|
}
|
|
|
|
/** returns the currently active text. */
|
|
SdrText* SdrTextObj::getActiveText() const
|
|
{
|
|
if( !mxText )
|
|
return getText( 0 );
|
|
else
|
|
return mxText.get();
|
|
}
|
|
|
|
/** returns the nth available text. */
|
|
SdrText* SdrTextObj::getText( sal_Int32 nIndex ) const
|
|
{
|
|
if( nIndex == 0 )
|
|
{
|
|
if( !mxText )
|
|
const_cast< SdrTextObj* >(this)->mxText = new SdrText( *const_cast< SdrTextObj* >(this) );
|
|
return mxText.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: */
|