/* -*- 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 <o3tl/sorted_vector.hxx>
#include <o3tl/string_view.hxx>
#include <svl/style.hxx>
#include <svx/svdotext.hxx>
#include <svx/svdmodel.hxx>
#include <svx/svdoutl.hxx>
#include <svx/svdorect.hxx>
#include <svx/svdocapt.hxx>
#include <editeng/editdata.hxx>
#include <svx/sdtfchim.hxx>
#include <editeng/outlobj.hxx>
#include <editeng/outliner.hxx>
#include <editeng/editobj.hxx>
namespace {
// The style family which is appended to the style names is padded to this many characters.
const short PADDING_LENGTH_FOR_STYLE_FAMILY = 5;
// this character will be used to pad the style families when they are appended to the style names
const char PADDING_CHARACTER_FOR_STYLE_FAMILY = ' ';
}
bool SdrTextObj::AdjustTextFrameWidthAndHeight( tools::Rectangle& rR, bool bHgt, bool bWdt ) const
{
if (!mbTextFrame)
// Not a text frame. Bail out.
return false;
if (rR.IsEmpty())
// Empty rectangle.
return false;
bool bFitToSize = IsFitToSize();
if (bFitToSize)
return false;
bool bWdtGrow = bWdt && IsAutoGrowWidth();
bool bHgtGrow = bHgt && IsAutoGrowHeight();
if (!bWdtGrow && !bHgtGrow)
// Not supposed to auto-adjust width or height.
return false;
SdrTextAniKind eAniKind = GetTextAniKind();
SdrTextAniDirection eAniDir = GetTextAniDirection();
bool bScroll = eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide;
bool bHScroll = bScroll && (eAniDir == SdrTextAniDirection::Left || eAniDir == SdrTextAniDirection::Right);
bool bVScroll = bScroll && (eAniDir == SdrTextAniDirection::Up || eAniDir == SdrTextAniDirection::Down);
tools::Rectangle aOldRect = rR;
tools::Long nHgt = 0, nMinHgt = 0, nMaxHgt = 0;
tools::Long nWdt = 0, nMinWdt = 0, nMaxWdt = 0;
Size aNewSize = rR.GetSize();
aNewSize.AdjustWidth( -1 ); aNewSize.AdjustHeight( -1 );
Size aMaxSiz(100000, 100000);
Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize());
if (aTmpSiz.Width())
aMaxSiz.setWidth( aTmpSiz.Width() );
if (aTmpSiz.Height())
aMaxSiz.setHeight( aTmpSiz.Height() );
if (bWdtGrow)
{
nMinWdt = GetMinTextFrameWidth();
nMaxWdt = GetMaxTextFrameWidth();
if (nMaxWdt == 0 || nMaxWdt > aMaxSiz.Width())
nMaxWdt = aMaxSiz.Width();
if (nMinWdt <= 0)
nMinWdt = 1;
aNewSize.setWidth( nMaxWdt );
}
if (bHgtGrow)
{
nMinHgt = GetMinTextFrameHeight();
nMaxHgt = GetMaxTextFrameHeight();
if (nMaxHgt == 0 || nMaxHgt > aMaxSiz.Height())
nMaxHgt = aMaxSiz.Height();
if (nMinHgt <= 0)
nMinHgt = 1;
aNewSize.setHeight( nMaxHgt );
}
tools::Long nHDist = GetTextLeftDistance() + GetTextRightDistance();
tools::Long nVDist = GetTextUpperDistance() + GetTextLowerDistance();
aNewSize.AdjustWidth( -nHDist );
aNewSize.AdjustHeight( -nVDist );
if (aNewSize.Width() < 2)
aNewSize.setWidth( 2 );
if (aNewSize.Height() < 2)
aNewSize.setHeight( 2 );
if (!IsInEditMode())
{
if (bHScroll)
aNewSize.setWidth( 0x0FFFFFFF ); // don't break ticker text
if (bVScroll)
aNewSize.setHeight( 0x0FFFFFFF );
}
if (mpEditingOutliner)
{
mpEditingOutliner->SetMaxAutoPaperSize(aNewSize);
if (bWdtGrow)
{
Size aSiz2(mpEditingOutliner->CalcTextSize());
nWdt = aSiz2.Width() + 1; // a little tolerance
if (bHgtGrow)
nHgt = aSiz2.Height() + 1; // a little tolerance
}
else
{
nHgt = mpEditingOutliner->GetTextHeight() + 1; // a little tolerance
}
}
else
{
Outliner& rOutliner = ImpGetDrawOutliner();
rOutliner.SetPaperSize(aNewSize);
rOutliner.SetUpdateLayout(true);
// TODO: add the optimization with bPortionInfoChecked etc. here
OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
if (pOutlinerParaObject)
{
rOutliner.SetText(*pOutlinerParaObject);
rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
}
if (bWdtGrow)
{
Size aSiz2(rOutliner.CalcTextSize());
nWdt = aSiz2.Width() + 1; // a little tolerance
if (bHgtGrow)
nHgt = aSiz2.Height() + 1; // a little tolerance
}
else
{
nHgt = rOutliner.GetTextHeight() + 1; // a little tolerance
}
rOutliner.Clear();
}
if (nWdt < nMinWdt)
nWdt = nMinWdt;
if (nWdt > nMaxWdt)
nWdt = nMaxWdt;
nWdt += nHDist;
if (nWdt < 1)
nWdt = 1; // nHDist may be negative
if (nHgt < nMinHgt)
nHgt = nMinHgt;
if (nHgt > nMaxHgt)
nHgt = nMaxHgt;
nHgt += nVDist;
if (nHgt < 1)
nHgt = 1; // nVDist may be negative
tools::Long nWdtGrow = nWdt - (rR.Right() - rR.Left());
tools::Long nHgtGrow = nHgt - (rR.Bottom() - rR.Top());
if (nWdtGrow == 0)
bWdtGrow = false;
if (nHgtGrow == 0)
bHgtGrow = false;
if (!bWdtGrow && !bHgtGrow)
return false;
if (bWdtGrow)
{
SdrTextHorzAdjust eHAdj = GetTextHorizontalAdjust();
if (eHAdj == SDRTEXTHORZADJUST_LEFT)
rR.AdjustRight(nWdtGrow );
else if (eHAdj == SDRTEXTHORZADJUST_RIGHT)
rR.AdjustLeft( -nWdtGrow );
else
{
tools::Long nWdtGrow2 = nWdtGrow / 2;
rR.AdjustLeft( -nWdtGrow2 );
rR.SetRight( rR.Left() + nWdt );
}
}
if (bHgtGrow)
{
SdrTextVertAdjust eVAdj = GetTextVerticalAdjust();
if (eVAdj == SDRTEXTVERTADJUST_TOP)
rR.AdjustBottom(nHgtGrow );
else if (eVAdj == SDRTEXTVERTADJUST_BOTTOM)
rR.AdjustTop( -nHgtGrow );
else
{
tools::Long nHgtGrow2 = nHgtGrow / 2;
rR.AdjustTop( -nHgtGrow2 );
rR.SetBottom( rR.Top() + nHgt );
}
}
if (maGeo.m_nRotationAngle)
{
// Object is rotated.
Point aD1(rR.TopLeft());
aD1 -= aOldRect.TopLeft();
Point aD2(aD1);
RotatePoint(aD2, Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
aD2 -= aD1;
rR.Move(aD2.X(), aD2.Y());
}
return true;
}
bool SdrTextObj::NbcAdjustTextFrameWidthAndHeight(bool bHgt, bool bWdt)
{
tools::Rectangle aRectangle(getRectangle());
bool bRet = AdjustTextFrameWidthAndHeight(aRectangle, bHgt, bWdt);
setRectangle(aRectangle);
if (bRet)
{
SetBoundAndSnapRectsDirty();
if (auto pRectObj = dynamic_cast<SdrRectObj *>(this)) { // this is a hack
pRectObj->SetXPolyDirty();
}
if (auto pCaptionObj = dynamic_cast<SdrCaptionObj *>(this)) { // this is a hack
pCaptionObj->ImpRecalcTail();
}
}
return bRet;
}
bool SdrTextObj::AdjustTextFrameWidthAndHeight()
{
tools::Rectangle aNewRect(getRectangle());
bool bRet = AdjustTextFrameWidthAndHeight(aNewRect);
if (bRet) {
tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
setRectangle(aNewRect);
SetBoundAndSnapRectsDirty();
if (auto pRectObj = dynamic_cast<SdrRectObj *>(this)) { // this is a hack
pRectObj->SetXPolyDirty();
}
bool bScPostIt = false;
if (auto pCaptionObj = dynamic_cast<SdrCaptionObj *>(this)) { // this is a hack
pCaptionObj->ImpRecalcTail();
// tdf#114956, tdf#138549 use GetSpecialTextBoxShadow to recognize
// that this SdrCaption is for a ScPostit
bScPostIt = pCaptionObj->GetSpecialTextBoxShadow();
}
// to not slow down EditView visualization on Overlay (see
// TextEditOverlayObject) it is necessary to suppress the
// Invalidates for the deep repaint when the size of the
// TextFrame changed (AdjustTextFrameWidthAndHeight returned
// true). The ObjectChanges are valid, invalidate will be
// done on EndTextEdit anyways
const bool bSuppressChangeWhenEditOnOverlay(
IsInEditMode() &&
GetTextEditOutliner() &&
GetTextEditOutliner()->hasEditViewCallbacks());
if (!bSuppressChangeWhenEditOnOverlay || bScPostIt)
{
SetChanged();
BroadcastObjectChange();
}
SendUserCall(SdrUserCallType::Resize,aBoundRect0);
}
return bRet;
}
void SdrTextObj::ImpSetTextStyleSheetListeners()
{
SfxStyleSheetBasePool* pStylePool(getSdrModelFromSdrObject().GetStyleSheetPool());
if (pStylePool==nullptr)
return;
std::vector<OUString> aStyleNames;
OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
if (pOutlinerParaObject!=nullptr)
{
// First, we collect all stylesheets contained in the ParaObject in
// the container aStyles. The Family is always appended to the name
// of the stylesheet.
const EditTextObject& rTextObj=pOutlinerParaObject->GetTextObject();
OUString aStyleName;
SfxStyleFamily eStyleFam;
sal_Int32 nParaCnt=rTextObj.GetParagraphCount();
for(sal_Int32 nParaNum(0); nParaNum < nParaCnt; nParaNum++)
{
rTextObj.GetStyleSheet(nParaNum, aStyleName, eStyleFam);
if (!aStyleName.isEmpty())
{
AppendFamilyToStyleName(aStyleName, eStyleFam);
bool bFnd(false);
sal_uInt32 nNum(aStyleNames.size());
while(!bFnd && nNum > 0)
{
// we don't want duplicate stylesheets
nNum--;
bFnd = aStyleName == aStyleNames[nNum];
}
if(!bFnd)
{
aStyleNames.push_back(aStyleName);
}
}
}
}
// now convert the strings in the vector from names to StyleSheet*
o3tl::sorted_vector<SfxStyleSheet*> aStyleSheets;
while (!aStyleNames.empty()) {
OUString aName = aStyleNames.back();
aStyleNames.pop_back();
SfxStyleFamily eFam = ReadFamilyFromStyleName(aName);
SfxStyleSheetBase* pStyleBase = pStylePool->Find(aName,eFam);
SfxStyleSheet* pStyle = dynamic_cast<SfxStyleSheet*>( pStyleBase );
if (pStyle!=nullptr && pStyle!=GetStyleSheet()) {
aStyleSheets.insert(pStyle);
}
}
// now remove all superfluous stylesheets
sal_uInt16 nNum=GetBroadcasterCount();
while (nNum>0) {
nNum--;
SfxBroadcaster* pBroadcast=GetBroadcasterJOE(nNum);
if (pBroadcast->IsSfxStyleSheet())
{
SfxStyleSheet* pStyle = static_cast<SfxStyleSheet*>( pBroadcast );
if (pStyle!=GetStyleSheet()) { // special case for stylesheet of the object
if (aStyleSheets.find(pStyle)==aStyleSheets.end()) {
EndListening(*pStyle);
}
}
}
}
// and finally, merge all stylesheets that are contained in aStyles with previous broadcasters
for(SfxStyleSheet* pStyle : aStyleSheets) {
// let StartListening see for itself if there's already a listener registered
StartListening(*pStyle, DuplicateHandling::Prevent);
}
}
/** iterates over the paragraphs of a given SdrObject and removes all
hard set character attributes with the which ids contained in the
given vector
*/
void SdrTextObj::RemoveOutlinerCharacterAttribs( const std::vector<sal_uInt16>& rCharWhichIds )
{
sal_Int32 nText = getTextCount();
while( --nText >= 0 )
{
SdrText* pText = getText( nText );
OutlinerParaObject* pOutlinerParaObject = pText ? pText->GetOutlinerParaObject() : nullptr;
if(pOutlinerParaObject)
{
Outliner* pOutliner = nullptr;
if( mpEditingOutliner || (pText == getActiveText()) )
pOutliner = mpEditingOutliner;
if(!pOutliner)
{
pOutliner = &ImpGetDrawOutliner();
pOutliner->SetText(*pOutlinerParaObject);
}
auto aSelAll = ESelection::All();
for( const auto& rWhichId : rCharWhichIds )
{
pOutliner->RemoveAttribs( aSelAll, false, rWhichId );
}
if(!mpEditingOutliner || (pText != getActiveText()) )
{
const sal_Int32 nParaCount = pOutliner->GetParagraphCount();
std::optional<OutlinerParaObject> pTemp = pOutliner->CreateParaObject(0, nParaCount);
pOutliner->Clear();
NbcSetOutlinerParaObjectForText(std::move(pTemp), pText);
}
}
}
}
bool SdrTextObj::HasText() const
{
if (mpEditingOutliner)
return HasTextImpl(mpEditingOutliner);
OutlinerParaObject* pOPO = GetOutlinerParaObject();
if( !pOPO )
return false;
const EditTextObject& rETO = pOPO->GetTextObject();
sal_Int32 nParaCount = rETO.GetParagraphCount();
if( nParaCount == 0 )
return false;
if( nParaCount > 1 )
return true;
return rETO.HasText( 0 );
}
void SdrTextObj::AppendFamilyToStyleName(OUString& styleName, SfxStyleFamily family)
{
OUStringBuffer aFam = OUString::number(static_cast<sal_Int32>(family));
comphelper::string::padToLength(aFam, PADDING_LENGTH_FOR_STYLE_FAMILY , PADDING_CHARACTER_FOR_STYLE_FAMILY);
styleName += "|" + aFam;
}
SfxStyleFamily SdrTextObj::ReadFamilyFromStyleName(std::u16string_view styleName)
{
std::u16string_view familyString = styleName.substr(styleName.size() - PADDING_LENGTH_FOR_STYLE_FAMILY);
familyString = comphelper::string::stripEnd(familyString, PADDING_CHARACTER_FOR_STYLE_FAMILY);
sal_uInt16 nFam = static_cast<sal_uInt16>(o3tl::toInt32(familyString));
assert(nFam != 0);
return static_cast<SfxStyleFamily>(nFam);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'padToLength' is required to be utilized.