svx UNO API for shapes: allow setting a max factor for autofit text scale
[LibreOffice.git] / svx / source / svdraw / svdotext.cxx
blob3df06ba3bfb28e6f1d771171c44ceb779a45a732
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <comphelper/string.hxx>
22 #include <svx/svdotext.hxx>
23 #include <svx/svdpagv.hxx>
24 #include <svx/svdview.hxx>
25 #include <svx/svdpage.hxx>
26 #include <svx/svdetc.hxx>
27 #include <svx/svdoutl.hxx>
28 #include <svx/svdmodel.hxx>
29 #include <svx/dialmgr.hxx>
30 #include <svx/strings.hrc>
31 #include <editeng/writingmodeitem.hxx>
32 #include <svx/sdtfchim.hxx>
33 #include <svtools/colorcfg.hxx>
34 #include <editeng/editdata.hxx>
35 #include <editeng/eeitem.hxx>
36 #include <editeng/editstat.hxx>
37 #include <editeng/outlobj.hxx>
38 #include <editeng/editobj.hxx>
39 #include <editeng/outliner.hxx>
40 #include <editeng/fhgtitem.hxx>
41 #include <svx/textchain.hxx>
42 #include <svx/textchainflow.hxx>
43 #include <svl/itempool.hxx>
44 #include <editeng/adjustitem.hxx>
45 #include <editeng/flditem.hxx>
46 #include <svx/xftouit.hxx>
47 #include <tools/helpers.hxx>
48 #include <svx/xflgrit.hxx>
49 #include <svx/svdpool.hxx>
50 #include <svx/xflclit.hxx>
51 #include <svl/style.hxx>
52 #include <svx/sderitm.hxx>
53 #include <svx/sdooitm.hxx>
54 #include <svx/sdshitm.hxx>
55 #include <svx/sdtagitm.hxx>
56 #include <svx/sdtfsitm.hxx>
57 #include <svx/sdtmfitm.hxx>
58 #include <svx/xtextit0.hxx>
59 #include <editeng/editeng.hxx>
60 #include <svl/itemiter.hxx>
61 #include <sdr/properties/textproperties.hxx>
62 #include <vcl/metaact.hxx>
63 #include <svx/sdr/contact/viewcontactoftextobj.hxx>
64 #include <basegfx/tuple/b2dtuple.hxx>
65 #include <basegfx/matrix/b2dhommatrix.hxx>
66 #include <basegfx/polygon/b2dpolygon.hxx>
67 #include <drawinglayer/geometry/viewinformation2d.hxx>
68 #include <vcl/virdev.hxx>
69 #include <basegfx/matrix/b2dhommatrixtools.hxx>
70 #include <sal/log.hxx>
72 using namespace com::sun::star;
74 // BaseProperties section
75 std::unique_ptr<sdr::properties::BaseProperties> SdrTextObj::CreateObjectSpecificProperties()
77 return std::make_unique<sdr::properties::TextProperties>(*this);
80 // DrawContact section
81 std::unique_ptr<sdr::contact::ViewContact> SdrTextObj::CreateObjectSpecificViewContact()
83 return std::make_unique<sdr::contact::ViewContactOfTextObj>(*this);
86 SdrTextObj::SdrTextObj(SdrModel& rSdrModel)
87 : SdrAttrObj(rSdrModel),
88 pEdtOutl(nullptr),
89 eTextKind(OBJ_TEXT)
91 bTextSizeDirty=false;
92 bTextFrame=false;
93 bNoShear=false;
94 bDisableAutoWidthOnDragging=false;
96 mbInEditMode = false;
97 mbTextAnimationAllowed = true;
98 maTextEditOffset = Point(0, 0);
100 // #i25616#
101 mbSupportTextIndentingOnLineWidthChange = true;
102 mbInDownScale = false;
105 SdrTextObj::SdrTextObj(
106 SdrModel& rSdrModel,
107 const tools::Rectangle& rNewRect)
108 : SdrAttrObj(rSdrModel),
109 maRect(rNewRect),
110 pEdtOutl(nullptr),
111 eTextKind(OBJ_TEXT)
113 bTextSizeDirty=false;
114 bTextFrame=false;
115 bNoShear=false;
116 bDisableAutoWidthOnDragging=false;
117 ImpJustifyRect(maRect);
119 mbInEditMode = false;
120 mbTextAnimationAllowed = true;
121 mbInDownScale = false;
122 maTextEditOffset = Point(0, 0);
124 // #i25616#
125 mbSupportTextIndentingOnLineWidthChange = true;
128 SdrTextObj::SdrTextObj(
129 SdrModel& rSdrModel,
130 SdrObjKind eNewTextKind)
131 : SdrAttrObj(rSdrModel),
132 pEdtOutl(nullptr),
133 eTextKind(eNewTextKind)
135 bTextSizeDirty=false;
136 bTextFrame=true;
137 bNoShear=true;
138 bDisableAutoWidthOnDragging=false;
140 mbInEditMode = false;
141 mbTextAnimationAllowed = true;
142 mbInDownScale = false;
143 maTextEditOffset = Point(0, 0);
145 // #i25616#
146 mbSupportTextIndentingOnLineWidthChange = true;
149 SdrTextObj::SdrTextObj(
150 SdrModel& rSdrModel,
151 SdrObjKind eNewTextKind,
152 const tools::Rectangle& rNewRect)
153 : SdrAttrObj(rSdrModel),
154 maRect(rNewRect),
155 pEdtOutl(nullptr),
156 eTextKind(eNewTextKind)
158 bTextSizeDirty=false;
159 bTextFrame=true;
160 bNoShear=true;
161 bDisableAutoWidthOnDragging=false;
162 ImpJustifyRect(maRect);
164 mbInEditMode = false;
165 mbTextAnimationAllowed = true;
166 mbInDownScale = false;
167 maTextEditOffset = Point(0, 0);
169 // #i25616#
170 mbSupportTextIndentingOnLineWidthChange = true;
173 SdrTextObj::~SdrTextObj()
175 SdrOutliner& rOutl(getSdrModelFromSdrObject().GetHitTestOutliner());
176 if( rOutl.GetTextObj() == this )
177 rOutl.SetTextObj( nullptr );
178 mpText.reset();
179 ImpDeregisterLink();
182 void SdrTextObj::FitFrameToTextSize()
184 ImpJustifyRect(maRect);
186 SdrText* pText = getActiveText();
187 if(pText==nullptr || !pText->GetOutlinerParaObject())
188 return;
190 SdrOutliner& rOutliner=ImpGetDrawOutliner();
191 rOutliner.SetPaperSize(Size(maRect.Right()-maRect.Left(),maRect.Bottom()-maRect.Top()));
192 rOutliner.SetUpdateMode(true);
193 rOutliner.SetText(*pText->GetOutlinerParaObject());
194 Size aNewSize(rOutliner.CalcTextSize());
195 rOutliner.Clear();
196 aNewSize.AdjustWidth( 1 ); // because of possible rounding errors
197 aNewSize.AdjustWidth(GetTextLeftDistance()+GetTextRightDistance() );
198 aNewSize.AdjustHeight(GetTextUpperDistance()+GetTextLowerDistance() );
199 tools::Rectangle aNewRect(maRect);
200 aNewRect.SetSize(aNewSize);
201 ImpJustifyRect(aNewRect);
202 if (aNewRect!=maRect) {
203 SetLogicRect(aNewRect);
207 void SdrTextObj::NbcSetText(const OUString& rStr)
209 SdrOutliner& rOutliner=ImpGetDrawOutliner();
210 rOutliner.SetStyleSheet( 0, GetStyleSheet());
211 rOutliner.SetUpdateMode(true);
212 rOutliner.SetText(rStr,rOutliner.GetParagraph( 0 ));
213 std::unique_ptr<OutlinerParaObject> pNewText=rOutliner.CreateParaObject();
214 Size aSiz(rOutliner.CalcTextSize());
215 rOutliner.Clear();
216 NbcSetOutlinerParaObject(std::move(pNewText));
217 aTextSize=aSiz;
218 bTextSizeDirty=false;
221 void SdrTextObj::SetText(const OUString& rStr)
223 tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
224 NbcSetText(rStr);
225 SetChanged();
226 BroadcastObjectChange();
227 SendUserCall(SdrUserCallType::Resize,aBoundRect0);
230 void SdrTextObj::NbcSetText(SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat)
232 SdrOutliner& rOutliner=ImpGetDrawOutliner();
233 rOutliner.SetStyleSheet( 0, GetStyleSheet());
234 rOutliner.Read(rInput,rBaseURL,eFormat);
235 std::unique_ptr<OutlinerParaObject> pNewText=rOutliner.CreateParaObject();
236 rOutliner.SetUpdateMode(true);
237 Size aSiz(rOutliner.CalcTextSize());
238 rOutliner.Clear();
239 NbcSetOutlinerParaObject(std::move(pNewText));
240 aTextSize=aSiz;
241 bTextSizeDirty=false;
244 void SdrTextObj::SetText(SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat)
246 tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
247 NbcSetText(rInput,rBaseURL,eFormat);
248 SetChanged();
249 BroadcastObjectChange();
250 SendUserCall(SdrUserCallType::Resize,aBoundRect0);
253 const Size& SdrTextObj::GetTextSize() const
255 if (bTextSizeDirty)
257 Size aSiz;
258 SdrText* pText = getActiveText();
259 if( pText && pText->GetOutlinerParaObject ())
261 SdrOutliner& rOutliner=ImpGetDrawOutliner();
262 rOutliner.SetText(*pText->GetOutlinerParaObject());
263 rOutliner.SetUpdateMode(true);
264 aSiz=rOutliner.CalcTextSize();
265 rOutliner.Clear();
267 // casting to nonconst twice
268 const_cast<SdrTextObj*>(this)->aTextSize=aSiz;
269 const_cast<SdrTextObj*>(this)->bTextSizeDirty=false;
271 return aTextSize;
274 bool SdrTextObj::IsAutoGrowHeight() const
276 if(!bTextFrame)
277 return false; // AutoGrow only together with TextFrames
279 const SfxItemSet& rSet = GetObjectItemSet();
280 bool bRet = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
282 if(bRet)
284 SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
286 if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
288 SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
290 if(eDirection == SdrTextAniDirection::Up || eDirection == SdrTextAniDirection::Down)
292 bRet = false;
296 return bRet;
299 bool SdrTextObj::IsAutoGrowWidth() const
301 if(!bTextFrame)
302 return false; // AutoGrow only together with TextFrames
304 const SfxItemSet& rSet = GetObjectItemSet();
305 bool bRet = rSet.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue();
307 bool bInEditMOde = IsInEditMode();
309 if(!bInEditMOde && bRet)
311 SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
313 if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
315 SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
317 if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
319 bRet = false;
323 return bRet;
326 SdrTextHorzAdjust SdrTextObj::GetTextHorizontalAdjust() const
328 return GetTextHorizontalAdjust(GetObjectItemSet());
331 SdrTextHorzAdjust SdrTextObj::GetTextHorizontalAdjust(const SfxItemSet& rSet) const
333 if(IsContourTextFrame())
334 return SDRTEXTHORZADJUST_BLOCK;
336 SdrTextHorzAdjust eRet = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue();
338 bool bInEditMode = IsInEditMode();
340 if(!bInEditMode && eRet == SDRTEXTHORZADJUST_BLOCK)
342 SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
344 if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
346 SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
348 if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
350 eRet = SDRTEXTHORZADJUST_LEFT;
355 return eRet;
356 } // defaults: BLOCK (justify) for text frame, CENTER for captions of drawing objects
358 SdrTextVertAdjust SdrTextObj::GetTextVerticalAdjust() const
360 return GetTextVerticalAdjust(GetObjectItemSet());
363 SdrTextVertAdjust SdrTextObj::GetTextVerticalAdjust(const SfxItemSet& rSet) const
365 if(IsContourTextFrame())
366 return SDRTEXTVERTADJUST_TOP;
368 // Take care for vertical text animation here
369 SdrTextVertAdjust eRet = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
370 bool bInEditMode = IsInEditMode();
372 // Take care for vertical text animation here
373 if(!bInEditMode && eRet == SDRTEXTVERTADJUST_BLOCK)
375 SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
377 if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
379 SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
381 if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
383 eRet = SDRTEXTVERTADJUST_TOP;
388 return eRet;
389 } // defaults: TOP for text frame, CENTER for captions of drawing objects
391 void SdrTextObj::ImpJustifyRect(tools::Rectangle& rRect)
393 if (!rRect.IsEmpty()) {
394 rRect.Justify();
395 if (rRect.Left()==rRect.Right()) rRect.AdjustRight( 1 );
396 if (rRect.Top()==rRect.Bottom()) rRect.AdjustBottom( 1 );
400 void SdrTextObj::ImpCheckShear()
402 if (bNoShear && aGeo.nShearAngle!=0) {
403 aGeo.nShearAngle=0;
404 aGeo.nTan=0;
408 void SdrTextObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
410 bool bNoTextFrame=!IsTextFrame();
411 rInfo.bResizeFreeAllowed=bNoTextFrame || aGeo.nRotationAngle%9000==0;
412 rInfo.bResizePropAllowed=true;
413 rInfo.bRotateFreeAllowed=true;
414 rInfo.bRotate90Allowed =true;
415 rInfo.bMirrorFreeAllowed=bNoTextFrame;
416 rInfo.bMirror45Allowed =bNoTextFrame;
417 rInfo.bMirror90Allowed =bNoTextFrame;
419 // allow transparency
420 rInfo.bTransparenceAllowed = true;
422 rInfo.bShearAllowed =bNoTextFrame;
423 rInfo.bEdgeRadiusAllowed=true;
424 bool bCanConv=ImpCanConvTextToCurve();
425 rInfo.bCanConvToPath =bCanConv;
426 rInfo.bCanConvToPoly =bCanConv;
427 rInfo.bCanConvToPathLineToArea=bCanConv;
428 rInfo.bCanConvToPolyLineToArea=bCanConv;
429 rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
432 sal_uInt16 SdrTextObj::GetObjIdentifier() const
434 return sal_uInt16(eTextKind);
437 bool SdrTextObj::HasTextImpl( SdrOutliner const * pOutliner )
439 bool bRet=false;
440 if(pOutliner)
442 Paragraph* p1stPara=pOutliner->GetParagraph( 0 );
443 sal_Int32 nParaCount=pOutliner->GetParagraphCount();
444 if(p1stPara==nullptr)
445 nParaCount=0;
447 if(nParaCount==1)
449 // if it is only one paragraph, check if that paragraph is empty
450 if( pOutliner->GetText(p1stPara).isEmpty() )
451 nParaCount = 0;
454 bRet= nParaCount!=0;
456 return bRet;
459 void SdrTextObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
461 const bool bRemove(pNewPage == nullptr && pOldPage != nullptr);
462 const bool bInsert(pNewPage != nullptr && pOldPage == nullptr);
463 const bool bLinked(IsLinkedText());
465 if (bLinked && bRemove)
467 ImpDeregisterLink();
470 // call parent
471 SdrAttrObj::handlePageChange(pOldPage, pNewPage);
473 if (bLinked && bInsert)
475 ImpRegisterLink();
479 void SdrTextObj::NbcSetEckenradius(long nRad)
481 SetObjectItem(makeSdrEckenradiusItem(nRad));
484 // #115391# This implementation is based on the object size (aRect) and the
485 // states of IsAutoGrowWidth/Height to correctly set TextMinFrameWidth/Height
486 void SdrTextObj::AdaptTextMinSize()
488 if (!bTextFrame)
489 // Only do this for text frame.
490 return;
492 if (getSdrModelFromSdrObject().IsPasteResize())
493 // Don't do this during paste resize.
494 return;
496 const bool bW = IsAutoGrowWidth();
497 const bool bH = IsAutoGrowHeight();
499 if (!bW && !bH)
500 // No auto grow requested. Bail out.
501 return;
503 SfxItemSet aSet(
504 *GetObjectItemSet().GetPool(),
505 svl::Items<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
506 SDRATTR_TEXT_MINFRAMEWIDTH, SDRATTR_TEXT_AUTOGROWWIDTH>{}); // contains SDRATTR_TEXT_MAXFRAMEWIDTH
508 if(bW)
510 // Set minimum width.
511 const long nDist = GetTextLeftDistance() + GetTextRightDistance();
512 const long nW = std::max<long>(0, maRect.GetWidth() - 1 - nDist); // text width without margins
514 aSet.Put(makeSdrTextMinFrameWidthItem(nW));
516 if(!IsVerticalWriting() && bDisableAutoWidthOnDragging)
518 bDisableAutoWidthOnDragging = true;
519 aSet.Put(makeSdrTextAutoGrowWidthItem(false));
523 if(bH)
525 // Set Minimum height.
526 const long nDist = GetTextUpperDistance() + GetTextLowerDistance();
527 const long nH = std::max<long>(0, maRect.GetHeight() - 1 - nDist); // text height without margins
529 aSet.Put(makeSdrTextMinFrameHeightItem(nH));
531 if(IsVerticalWriting() && bDisableAutoWidthOnDragging)
533 bDisableAutoWidthOnDragging = false;
534 aSet.Put(makeSdrTextAutoGrowHeightItem(false));
538 SetObjectItemSet(aSet);
541 void SdrTextObj::ImpSetContourPolygon( SdrOutliner& rOutliner, tools::Rectangle const & rAnchorRect, bool bLineWidth ) const
543 basegfx::B2DPolyPolygon aXorPolyPolygon(TakeXorPoly());
544 std::unique_ptr<basegfx::B2DPolyPolygon> pContourPolyPolygon;
545 basegfx::B2DHomMatrix aMatrix(basegfx::utils::createTranslateB2DHomMatrix(
546 -rAnchorRect.Left(), -rAnchorRect.Top()));
548 if(aGeo.nRotationAngle)
550 // Unrotate!
551 aMatrix.rotate(-aGeo.nRotationAngle * F_PI18000);
554 aXorPolyPolygon.transform(aMatrix);
556 if( bLineWidth )
558 // Take line width into account.
559 // When doing the hit test, avoid this. (Performance!)
560 pContourPolyPolygon.reset(new basegfx::B2DPolyPolygon());
562 // test if shadow needs to be avoided for TakeContour()
563 const SfxItemSet& rSet = GetObjectItemSet();
564 bool bShadowOn = rSet.Get(SDRATTR_SHADOW).GetValue();
566 // #i33696#
567 // Remember TextObject currently set at the DrawOutliner, it WILL be
568 // replaced during calculating the outline since it uses an own paint
569 // and that one uses the DrawOutliner, too.
570 const SdrTextObj* pLastTextObject = rOutliner.GetTextObj();
572 if(bShadowOn)
574 // force shadow off
575 SdrObject* pCopy(CloneSdrObject(getSdrModelFromSdrObject()));
576 pCopy->SetMergedItem(makeSdrShadowItem(false));
577 *pContourPolyPolygon = pCopy->TakeContour();
578 SdrObject::Free( pCopy );
580 else
582 *pContourPolyPolygon = TakeContour();
585 // #i33696#
586 // restore remembered text object
587 if(pLastTextObject != rOutliner.GetTextObj())
589 rOutliner.SetTextObj(pLastTextObject);
592 pContourPolyPolygon->transform(aMatrix);
595 rOutliner.SetPolygon(aXorPolyPolygon, pContourPolyPolygon.get());
598 void SdrTextObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
600 rRect=maRect;
603 void SdrTextObj::TakeTextAnchorRect(tools::Rectangle& rAnchorRect) const
605 long nLeftDist=GetTextLeftDistance();
606 long nRightDist=GetTextRightDistance();
607 long nUpperDist=GetTextUpperDistance();
608 long nLowerDist=GetTextLowerDistance();
609 tools::Rectangle aAnkRect(maRect); // the rectangle in which we anchor
610 bool bFrame=IsTextFrame();
611 if (!bFrame) {
612 TakeUnrotatedSnapRect(aAnkRect);
614 Point aRotateRef(aAnkRect.TopLeft());
615 aAnkRect.AdjustLeft(nLeftDist );
616 aAnkRect.AdjustTop(nUpperDist );
617 aAnkRect.AdjustRight( -nRightDist );
618 aAnkRect.AdjustBottom( -nLowerDist );
620 // Since sizes may be bigger than the object bounds it is necessary to
621 // justify the rect now.
622 ImpJustifyRect(aAnkRect);
624 if (bFrame) {
625 // TODO: Optimize this.
626 if (aAnkRect.GetWidth()<2) aAnkRect.SetRight(aAnkRect.Left()+1 ); // minimum size h and v: 2 px
627 if (aAnkRect.GetHeight()<2) aAnkRect.SetBottom(aAnkRect.Top()+1 );
629 if (aGeo.nRotationAngle!=0) {
630 Point aTmpPt(aAnkRect.TopLeft());
631 RotatePoint(aTmpPt,aRotateRef,aGeo.nSin,aGeo.nCos);
632 aTmpPt-=aAnkRect.TopLeft();
633 aAnkRect.Move(aTmpPt.X(),aTmpPt.Y());
635 rAnchorRect=aAnkRect;
638 void SdrTextObj::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText,
639 tools::Rectangle* pAnchorRect, bool bLineWidth ) const
641 tools::Rectangle aAnkRect; // the rectangle in which we anchor
642 TakeTextAnchorRect(aAnkRect);
643 SdrTextVertAdjust eVAdj=GetTextVerticalAdjust();
644 SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust();
645 SdrTextAniKind eAniKind=GetTextAniKind();
646 SdrTextAniDirection eAniDirection=GetTextAniDirection();
648 bool bFitToSize(IsFitToSize());
649 bool bContourFrame=IsContourTextFrame();
651 bool bFrame=IsTextFrame();
652 EEControlBits nStat0=rOutliner.GetControlWord();
653 Size aNullSize;
654 if (!bContourFrame)
656 rOutliner.SetControlWord(nStat0|EEControlBits::AUTOPAGESIZE);
657 rOutliner.SetMinAutoPaperSize(aNullSize);
658 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
661 if (!bFitToSize && !bContourFrame)
663 long nAnkWdt=aAnkRect.GetWidth();
664 long nAnkHgt=aAnkRect.GetHeight();
665 if (bFrame)
667 long nWdt=nAnkWdt;
668 long nHgt=nAnkHgt;
670 bool bInEditMode = IsInEditMode();
672 if (!bInEditMode && (eAniKind==SdrTextAniKind::Scroll || eAniKind==SdrTextAniKind::Alternate || eAniKind==SdrTextAniKind::Slide))
674 // unlimited paper size for ticker text
675 if (eAniDirection==SdrTextAniDirection::Left || eAniDirection==SdrTextAniDirection::Right) nWdt=1000000;
676 if (eAniDirection==SdrTextAniDirection::Up || eAniDirection==SdrTextAniDirection::Down) nHgt=1000000;
679 bool bChainedFrame = IsChainable();
680 // Might be required for overflow check working: do limit height to frame if box is chainable.
681 if (!bChainedFrame) {
682 // #i119885# Do not limit/force height to geometrical frame (vice versa for vertical writing)
684 if(IsVerticalWriting())
686 nWdt = 1000000;
688 else
690 nHgt = 1000000;
694 rOutliner.SetMaxAutoPaperSize(Size(nWdt,nHgt));
697 // New try with _BLOCK for hor and ver after completely
698 // supporting full width for vertical text.
699 if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting())
701 rOutliner.SetMinAutoPaperSize(Size(nAnkWdt, 0));
704 if(SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting())
706 rOutliner.SetMinAutoPaperSize(Size(0, nAnkHgt));
710 rOutliner.SetPaperSize(aNullSize);
711 if (bContourFrame)
712 ImpSetContourPolygon( rOutliner, aAnkRect, bLineWidth );
714 // put text into the outliner, if available from the edit outliner
715 SdrText* pText = getActiveText();
716 OutlinerParaObject* pOutlinerParaObject = pText ? pText->GetOutlinerParaObject() : nullptr;
717 OutlinerParaObject* pPara = (pEdtOutl && !bNoEditText) ? pEdtOutl->CreateParaObject().release() : pOutlinerParaObject;
719 if (pPara)
721 const bool bHitTest(&getSdrModelFromSdrObject().GetHitTestOutliner() == &rOutliner);
722 const SdrTextObj* pTestObj = rOutliner.GetTextObj();
724 if( !pTestObj || !bHitTest || pTestObj != this ||
725 pTestObj->GetOutlinerParaObject() != pOutlinerParaObject )
727 if( bHitTest ) // #i33696# take back fix #i27510#
729 rOutliner.SetTextObj( this );
730 rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
733 rOutliner.SetUpdateMode(true);
734 rOutliner.SetText(*pPara);
737 else
739 rOutliner.SetTextObj( nullptr );
742 if (pEdtOutl && !bNoEditText && pPara)
743 delete pPara;
745 rOutliner.SetUpdateMode(true);
746 rOutliner.SetControlWord(nStat0);
748 if( pText )
749 pText->CheckPortionInfo(rOutliner);
751 Point aTextPos(aAnkRect.TopLeft());
752 Size aTextSiz(rOutliner.GetPaperSize()); // GetPaperSize() adds a little tolerance, right?
754 // For draw objects containing text correct hor/ver alignment if text is bigger
755 // than the object itself. Without that correction, the text would always be
756 // formatted to the left edge (or top edge when vertical) of the draw object.
757 if(!IsTextFrame())
759 if(aAnkRect.GetWidth() < aTextSiz.Width() && !IsVerticalWriting())
761 // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
762 // else the alignment is wanted.
763 if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
765 eHAdj = SDRTEXTHORZADJUST_CENTER;
769 if(aAnkRect.GetHeight() < aTextSiz.Height() && IsVerticalWriting())
771 // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
772 // else the alignment is wanted.
773 if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
775 eVAdj = SDRTEXTVERTADJUST_CENTER;
780 if (eHAdj==SDRTEXTHORZADJUST_CENTER || eHAdj==SDRTEXTHORZADJUST_RIGHT)
782 long nFreeWdt=aAnkRect.GetWidth()-aTextSiz.Width();
783 if (eHAdj==SDRTEXTHORZADJUST_CENTER)
784 aTextPos.AdjustX(nFreeWdt/2 );
785 if (eHAdj==SDRTEXTHORZADJUST_RIGHT)
786 aTextPos.AdjustX(nFreeWdt );
788 if (eVAdj==SDRTEXTVERTADJUST_CENTER || eVAdj==SDRTEXTVERTADJUST_BOTTOM)
790 long nFreeHgt=aAnkRect.GetHeight()-aTextSiz.Height();
791 if (eVAdj==SDRTEXTVERTADJUST_CENTER)
792 aTextPos.AdjustY(nFreeHgt/2 );
793 if (eVAdj==SDRTEXTVERTADJUST_BOTTOM)
794 aTextPos.AdjustY(nFreeHgt );
796 if (aGeo.nRotationAngle!=0)
797 RotatePoint(aTextPos,aAnkRect.TopLeft(),aGeo.nSin,aGeo.nCos);
799 if (pAnchorRect)
800 *pAnchorRect=aAnkRect;
802 // rTextRect might not be correct in some cases at ContourFrame
803 rTextRect=tools::Rectangle(aTextPos,aTextSiz);
804 if (bContourFrame)
805 rTextRect=aAnkRect;
808 bool SdrTextObj::CanCreateEditOutlinerParaObject() const
810 if( HasTextImpl( pEdtOutl ) )
812 return pEdtOutl->GetParagraphCount() > 0;
814 return false;
817 std::unique_ptr<OutlinerParaObject> SdrTextObj::CreateEditOutlinerParaObject() const
819 std::unique_ptr<OutlinerParaObject> pPara;
820 if( HasTextImpl( pEdtOutl ) )
822 sal_Int32 nParaCount = pEdtOutl->GetParagraphCount();
823 pPara = pEdtOutl->CreateParaObject(0, nParaCount);
825 return pPara;
828 void SdrTextObj::ImpSetCharStretching(SdrOutliner& rOutliner, const Size& rTextSize, const Size& rShapeSize, Fraction& rFitXCorrection)
830 OutputDevice* pOut = rOutliner.GetRefDevice();
831 bool bNoStretching(false);
833 if(pOut && pOut->GetOutDevType() == OUTDEV_PRINTER)
835 // check whether CharStretching is possible at all
836 GDIMetaFile* pMtf = pOut->GetConnectMetaFile();
837 OUString aTestString(u'J');
839 if(pMtf && (!pMtf->IsRecord() || pMtf->IsPause()))
840 pMtf = nullptr;
842 if(pMtf)
843 pMtf->Pause(true);
845 vcl::Font aOriginalFont(pOut->GetFont());
846 vcl::Font aTmpFont( OutputDevice::GetDefaultFont( DefaultFontType::SERIF, LANGUAGE_SYSTEM, GetDefaultFontFlags::OnlyOne ) );
848 aTmpFont.SetFontSize(Size(0,100));
849 pOut->SetFont(aTmpFont);
850 Size aSize1(pOut->GetTextWidth(aTestString), pOut->GetTextHeight());
851 aTmpFont.SetFontSize(Size(800,100));
852 pOut->SetFont(aTmpFont);
853 Size aSize2(pOut->GetTextWidth(aTestString), pOut->GetTextHeight());
854 pOut->SetFont(aOriginalFont);
856 if(pMtf)
857 pMtf->Pause(false);
859 bNoStretching = (aSize1 == aSize2);
861 #ifdef _WIN32
862 // Windows zooms the font proportionally when using Size(100,500),
863 // we don't like that.
864 if(aSize2.Height() >= aSize1.Height() * 2)
866 bNoStretching = true;
868 #endif
870 unsigned nLoopCount=0;
871 bool bNoMoreLoop = false;
872 long nXDiff0=0x7FFFFFFF;
873 long nWantWdt=rShapeSize.Width();
874 long nIsWdt=rTextSize.Width();
875 if (nIsWdt==0) nIsWdt=1;
877 long nWantHgt=rShapeSize.Height();
878 long nIsHgt=rTextSize.Height();
879 if (nIsHgt==0) nIsHgt=1;
881 long nXTolPl=nWantWdt/100; // tolerance: +1%
882 long nXTolMi=nWantWdt/25; // tolerance: -4%
883 long nXCorr =nWantWdt/20; // correction scale: 5%
885 long nX=(nWantWdt*100) /nIsWdt; // calculate X stretching
886 long nY=(nWantHgt*100) /nIsHgt; // calculate Y stretching
887 bool bChkX = true;
888 if (bNoStretching) { // might only be possible proportionally
889 if (nX>nY) { nX=nY; bChkX=false; }
890 else { nY=nX; }
893 while (nLoopCount<5 && !bNoMoreLoop) {
894 if (nX<0) nX=-nX;
895 if (nX<1) { nX=1; bNoMoreLoop = true; }
896 if (nX>65535) { nX=65535; bNoMoreLoop = true; }
898 if (nY<0) nY=-nY;
899 if (nY<1) { nY=1; bNoMoreLoop = true; }
900 if (nY>65535) { nY=65535; bNoMoreLoop = true; }
902 // exception, there is no text yet (horizontal case)
903 if(nIsWdt <= 1)
905 nX = nY;
906 bNoMoreLoop = true;
909 // exception, there is no text yet (vertical case)
910 if(nIsHgt <= 1)
912 nY = nX;
913 bNoMoreLoop = true;
916 rOutliner.SetGlobalCharStretching(static_cast<sal_uInt16>(nX),static_cast<sal_uInt16>(nY));
917 nLoopCount++;
918 Size aSiz(rOutliner.CalcTextSize());
919 long nXDiff=aSiz.Width()-nWantWdt;
920 rFitXCorrection=Fraction(nWantWdt,aSiz.Width());
921 if (((nXDiff>=nXTolMi || !bChkX) && nXDiff<=nXTolPl) || nXDiff==nXDiff0) {
922 bNoMoreLoop = true;
923 } else {
924 // correct stretching factors
925 long nMul=nWantWdt;
926 long nDiv=aSiz.Width();
927 if (std::abs(nXDiff)<=2*nXCorr) {
928 if (nMul>nDiv) nDiv+=(nMul-nDiv)/2; // but only add half of what we calculated,
929 else nMul+=(nDiv-nMul)/2; // because the EditEngine calculates wrongly later on
931 nX=nX*nMul/nDiv;
932 if (bNoStretching) nY=nX;
934 nXDiff0=nXDiff;
938 OUString SdrTextObj::TakeObjNameSingul() const
940 OUString aStr;
942 switch(eTextKind)
944 case OBJ_OUTLINETEXT:
946 aStr = SvxResId(STR_ObjNameSingulOUTLINETEXT);
947 break;
950 case OBJ_TITLETEXT :
952 aStr = SvxResId(STR_ObjNameSingulTITLETEXT);
953 break;
956 default:
958 if(IsLinkedText())
959 aStr = SvxResId(STR_ObjNameSingulTEXTLNK);
960 else
961 aStr = SvxResId(STR_ObjNameSingulTEXT);
962 break;
966 OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
967 if(pOutlinerParaObject && eTextKind != OBJ_OUTLINETEXT)
969 // shouldn't currently cause any problems at OUTLINETEXT
970 OUString aStr2(comphelper::string::stripStart(pOutlinerParaObject->GetTextObject().GetText(0), ' '));
972 // avoid non expanded text portions in object name
973 // (second condition is new)
974 if(!aStr2.isEmpty() && aStr2.indexOf(u'\x00FF') == -1)
976 // space between ResStr and content text
977 aStr += " \'";
979 if(aStr2.getLength() > 10)
981 aStr2 = aStr2.copy(0, 8) + "...";
984 aStr += aStr2 + "\'";
988 OUStringBuffer sName(aStr);
990 OUString aName(GetName());
991 if (!aName.isEmpty())
993 sName.append(' ');
994 sName.append('\'');
995 sName.append(aName);
996 sName.append('\'');
999 return sName.makeStringAndClear();
1002 OUString SdrTextObj::TakeObjNamePlural() const
1004 OUString sName;
1005 switch (eTextKind) {
1006 case OBJ_OUTLINETEXT: sName=SvxResId(STR_ObjNamePluralOUTLINETEXT); break;
1007 case OBJ_TITLETEXT : sName=SvxResId(STR_ObjNamePluralTITLETEXT); break;
1008 default: {
1009 if (IsLinkedText()) {
1010 sName=SvxResId(STR_ObjNamePluralTEXTLNK);
1011 } else {
1012 sName=SvxResId(STR_ObjNamePluralTEXT);
1014 } break;
1015 } // switch
1016 return sName;
1019 SdrTextObj* SdrTextObj::CloneSdrObject(SdrModel& rTargetModel) const
1021 return CloneHelper< SdrTextObj >(rTargetModel);
1024 SdrTextObj& SdrTextObj::operator=(const SdrTextObj& rObj)
1026 if( this == &rObj )
1027 return *this;
1029 // call parent. tdf#116979: use the correct parent class
1030 SdrAttrObj::operator=(rObj);
1032 maRect = rObj.maRect;
1033 aGeo =rObj.aGeo;
1034 eTextKind =rObj.eTextKind;
1035 bTextFrame=rObj.bTextFrame;
1036 aTextSize=rObj.aTextSize;
1037 bTextSizeDirty=rObj.bTextSizeDirty;
1039 // Not all of the necessary parameters were copied yet.
1040 bNoShear = rObj.bNoShear;
1041 bDisableAutoWidthOnDragging = rObj.bDisableAutoWidthOnDragging;
1042 SdrText* pText = getActiveText();
1044 if( pText && rObj.HasText() )
1046 // before pNewOutlinerParaObject was created the same, but
1047 // set at mpText (outside this scope), but mpText might be
1048 // empty (this operator== seems not prepared for MultiText
1049 // objects). In the current form it makes only sense to
1050 // create locally and use locally on a known existing SdrText
1051 const Outliner* pEO=rObj.pEdtOutl;
1052 std::unique_ptr<OutlinerParaObject> pNewOutlinerParaObject;
1054 if (pEO!=nullptr)
1056 pNewOutlinerParaObject = pEO->CreateParaObject();
1058 else if (nullptr != rObj.getActiveText()->GetOutlinerParaObject())
1060 pNewOutlinerParaObject.reset( new OutlinerParaObject(*rObj.getActiveText()->GetOutlinerParaObject()) );
1063 pText->SetOutlinerParaObject( std::move(pNewOutlinerParaObject) );
1066 ImpSetTextStyleSheetListeners();
1067 return *this;
1070 basegfx::B2DPolyPolygon SdrTextObj::TakeXorPoly() const
1072 tools::Polygon aPol(maRect);
1073 if (aGeo.nShearAngle!=0) ShearPoly(aPol,maRect.TopLeft(),aGeo.nTan);
1074 if (aGeo.nRotationAngle!=0) RotatePoly(aPol,maRect.TopLeft(),aGeo.nSin,aGeo.nCos);
1076 basegfx::B2DPolyPolygon aRetval;
1077 aRetval.append(aPol.getB2DPolygon());
1078 return aRetval;
1081 basegfx::B2DPolyPolygon SdrTextObj::TakeContour() const
1083 basegfx::B2DPolyPolygon aRetval(SdrAttrObj::TakeContour());
1085 // and now add the BoundRect of the text, if necessary
1086 if ( GetOutlinerParaObject() && !IsFontwork() && !IsContourTextFrame() )
1088 // using Clone()-Paint() strategy inside TakeContour() leaves a destroyed
1089 // SdrObject as pointer in DrawOutliner. Set *this again in fetching the outliner
1090 // in every case
1091 SdrOutliner& rOutliner=ImpGetDrawOutliner();
1093 tools::Rectangle aAnchor2;
1094 tools::Rectangle aR;
1095 TakeTextRect(rOutliner,aR,false,&aAnchor2);
1096 rOutliner.Clear();
1097 bool bFitToSize(IsFitToSize());
1098 if (bFitToSize) aR=aAnchor2;
1099 tools::Polygon aPol(aR);
1100 if (aGeo.nRotationAngle!=0) RotatePoly(aPol,aR.TopLeft(),aGeo.nSin,aGeo.nCos);
1102 aRetval.append(aPol.getB2DPolygon());
1105 return aRetval;
1108 void SdrTextObj::RecalcSnapRect()
1110 if (aGeo.nRotationAngle!=0 || aGeo.nShearAngle!=0)
1112 tools::Polygon aPol(maRect);
1113 if (aGeo.nShearAngle!=0) ShearPoly(aPol,maRect.TopLeft(),aGeo.nTan);
1114 if (aGeo.nRotationAngle!=0) RotatePoly(aPol,maRect.TopLeft(),aGeo.nSin,aGeo.nCos);
1115 maSnapRect=aPol.GetBoundRect();
1116 } else {
1117 maSnapRect = maRect;
1121 sal_uInt32 SdrTextObj::GetSnapPointCount() const
1123 return 4L;
1126 Point SdrTextObj::GetSnapPoint(sal_uInt32 i) const
1128 Point aP;
1129 switch (i) {
1130 case 0: aP=maRect.TopLeft(); break;
1131 case 1: aP=maRect.TopRight(); break;
1132 case 2: aP=maRect.BottomLeft(); break;
1133 case 3: aP=maRect.BottomRight(); break;
1134 default: aP=maRect.Center(); break;
1136 if (aGeo.nShearAngle!=0) ShearPoint(aP,maRect.TopLeft(),aGeo.nTan);
1137 if (aGeo.nRotationAngle!=0) RotatePoint(aP,maRect.TopLeft(),aGeo.nSin,aGeo.nCos);
1138 return aP;
1141 // Extracted from ImpGetDrawOutliner()
1142 void SdrTextObj::ImpInitDrawOutliner( SdrOutliner& rOutl ) const
1144 rOutl.SetUpdateMode(false);
1145 OutlinerMode nOutlinerMode = OutlinerMode::OutlineObject;
1146 if ( !IsOutlText() )
1147 nOutlinerMode = OutlinerMode::TextObject;
1148 rOutl.Init( nOutlinerMode );
1150 rOutl.SetGlobalCharStretching();
1151 EEControlBits nStat=rOutl.GetControlWord();
1152 nStat &= ~EEControlBits(EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE);
1153 rOutl.SetControlWord(nStat);
1154 Size aMaxSize(100000,100000);
1155 rOutl.SetMinAutoPaperSize(Size());
1156 rOutl.SetMaxAutoPaperSize(aMaxSize);
1157 rOutl.SetPaperSize(aMaxSize);
1158 rOutl.ClearPolygon();
1161 SdrOutliner& SdrTextObj::ImpGetDrawOutliner() const
1163 SdrOutliner& rOutl(getSdrModelFromSdrObject().GetDrawOutliner(this));
1165 // Code extracted to ImpInitDrawOutliner()
1166 ImpInitDrawOutliner( rOutl );
1168 return rOutl;
1171 // Extracted from Paint()
1172 void SdrTextObj::ImpSetupDrawOutlinerForPaint( bool bContourFrame,
1173 SdrOutliner& rOutliner,
1174 tools::Rectangle& rTextRect,
1175 tools::Rectangle& rAnchorRect,
1176 tools::Rectangle& rPaintRect,
1177 Fraction& rFitXCorrection ) const
1179 if (!bContourFrame)
1181 // FitToSize can't be used together with ContourFrame for now
1182 if (IsFitToSize() || IsAutoFit())
1184 EEControlBits nStat=rOutliner.GetControlWord();
1185 nStat|=EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE;
1186 rOutliner.SetControlWord(nStat);
1189 rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
1190 TakeTextRect(rOutliner, rTextRect, false, &rAnchorRect);
1191 rPaintRect = rTextRect;
1193 if (!bContourFrame)
1195 // FitToSize can't be used together with ContourFrame for now
1196 if (IsFitToSize())
1198 ImpSetCharStretching(rOutliner,rTextRect.GetSize(),rAnchorRect.GetSize(),rFitXCorrection);
1199 rPaintRect=rAnchorRect;
1201 else if (IsAutoFit())
1203 ImpAutoFitText(rOutliner);
1208 double SdrTextObj::GetFontScaleY() const
1210 SdrText* pText = getActiveText();
1211 if (pText == nullptr || !pText->GetOutlinerParaObject())
1212 return 1.0;
1214 SdrOutliner& rOutliner = ImpGetDrawOutliner();
1215 const Size aShapeSize = GetSnapRect().GetSize();
1216 const Size aSize(aShapeSize.Width() - GetTextLeftDistance() - GetTextRightDistance(),
1217 aShapeSize.Height() - GetTextUpperDistance() - GetTextLowerDistance());
1219 rOutliner.SetPaperSize(aSize);
1220 rOutliner.SetUpdateMode(true);
1221 rOutliner.SetText(*pText->GetOutlinerParaObject());
1222 bool bIsVerticalWriting = IsVerticalWriting();
1224 // Algorithm from SdrTextObj::ImpAutoFitText
1226 sal_uInt16 nMinStretchX = 0, nMinStretchY = 0;
1227 sal_uInt16 nCurrStretchX = 100, nCurrStretchY = 100;
1228 sal_uInt16 aOldStretchXVals[] = { 0,0,0 };
1229 const size_t aStretchArySize = SAL_N_ELEMENTS(aOldStretchXVals);
1230 for (unsigned int i = 0; i<aStretchArySize; ++i)
1232 const Size aCurrTextSize = rOutliner.CalcTextSizeNTP();
1233 double fFactor(1.0);
1234 if (bIsVerticalWriting)
1236 if (aCurrTextSize.Width() != 0)
1238 fFactor = double(aSize.Width()) / aCurrTextSize.Width();
1241 else if (aCurrTextSize.Height() != 0)
1243 fFactor = double(aSize.Height()) / aCurrTextSize.Height();
1245 fFactor = std::sqrt(fFactor);
1247 rOutliner.GetGlobalCharStretching(nCurrStretchX, nCurrStretchY);
1249 if (fFactor >= 1.0)
1251 nMinStretchX = std::max(nMinStretchX, nCurrStretchX);
1252 nMinStretchY = std::max(nMinStretchY, nCurrStretchY);
1255 aOldStretchXVals[i] = nCurrStretchX;
1256 if (std::find(aOldStretchXVals, aOldStretchXVals + i, nCurrStretchX) != aOldStretchXVals + i)
1257 break; // same value already attained once; algo is looping, exit
1259 if (fFactor < 1.0 || nCurrStretchX != 100)
1261 nCurrStretchX = sal::static_int_cast<sal_uInt16>(nCurrStretchX*fFactor);
1262 nCurrStretchY = sal::static_int_cast<sal_uInt16>(nCurrStretchY*fFactor);
1263 rOutliner.SetGlobalCharStretching(std::min(sal_uInt16(100), nCurrStretchX),
1264 std::min(sal_uInt16(100), nCurrStretchY));
1268 return std::min(sal_uInt16(100), nCurrStretchY) / 100.0;
1271 void SdrTextObj::ImpAutoFitText( SdrOutliner& rOutliner ) const
1273 const Size aShapeSize=GetSnapRect().GetSize();
1274 ImpAutoFitText( rOutliner,
1275 Size(aShapeSize.Width()-GetTextLeftDistance()-GetTextRightDistance(),
1276 aShapeSize.Height()-GetTextUpperDistance()-GetTextLowerDistance()),
1277 IsVerticalWriting() );
1280 void SdrTextObj::ImpAutoFitText(SdrOutliner& rOutliner, const Size& rTextSize,
1281 bool bIsVerticalWriting) const
1283 // EditEngine formatting is unstable enough for
1284 // line-breaking text that we need some more samples
1286 // loop early-exits if we detect an already attained value
1287 sal_uInt16 nMinStretchX=0, nMinStretchY=0;
1288 sal_uInt16 aOldStretchXVals[]={0,0,0,0,0,0,0,0,0,0};
1289 const size_t aStretchArySize=SAL_N_ELEMENTS(aOldStretchXVals);
1290 for(unsigned int i=0; i<aStretchArySize; ++i)
1292 const Size aCurrTextSize = rOutliner.CalcTextSizeNTP();
1293 double fFactor(1.0);
1294 if( bIsVerticalWriting )
1296 if (aCurrTextSize.Width() != 0)
1298 fFactor = double(rTextSize.Width())/aCurrTextSize.Width();
1301 else if (aCurrTextSize.Height() != 0)
1303 fFactor = double(rTextSize.Height())/aCurrTextSize.Height();
1305 // fFactor scales in both x and y directions
1306 // - this is fine for bulleted words
1307 // - but it scales too much for a long paragraph
1308 // - taking sqrt scales long paragraphs the best
1309 // - bulleted words will have to go through more iterations
1310 fFactor = std::sqrt(fFactor);
1312 sal_uInt16 nCurrStretchX, nCurrStretchY;
1313 rOutliner.GetGlobalCharStretching(nCurrStretchX, nCurrStretchY);
1315 if (fFactor >= 1.0 )
1317 // resulting text area fits into available shape rect -
1318 // err on the larger stretching, to optimally fill area
1319 nMinStretchX = std::max(nMinStretchX,nCurrStretchX);
1320 nMinStretchY = std::max(nMinStretchY,nCurrStretchY);
1323 aOldStretchXVals[i] = nCurrStretchX;
1324 if( std::find(aOldStretchXVals, aOldStretchXVals+i, nCurrStretchX) != aOldStretchXVals+i )
1325 break; // same value already attained once; algo is looping, exit
1327 if (fFactor < 1.0 || nCurrStretchX != 100)
1329 nCurrStretchX = sal::static_int_cast<sal_uInt16>(nCurrStretchX*fFactor);
1330 nCurrStretchY = sal::static_int_cast<sal_uInt16>(nCurrStretchY*fFactor);
1331 rOutliner.SetGlobalCharStretching(std::min(sal_uInt16(100),nCurrStretchX),
1332 std::min(sal_uInt16(100),nCurrStretchY));
1333 SAL_INFO("svx", "zoom is " << nCurrStretchX);
1337 const SdrTextFitToSizeTypeItem& rItem = GetObjectItem(SDRATTR_TEXT_FITTOSIZE);
1338 if (rItem.GetMaxScale() > 0)
1340 nMinStretchX = std::min<sal_uInt16>(rItem.GetMaxScale(), nMinStretchX);
1341 nMinStretchY = std::min<sal_uInt16>(rItem.GetMaxScale(), nMinStretchY);
1344 SAL_INFO("svx", "final zoom is " << nMinStretchX);
1345 rOutliner.SetGlobalCharStretching(std::min(sal_uInt16(100),nMinStretchX),
1346 std::min(sal_uInt16(100),nMinStretchY));
1349 void SdrTextObj::SetupOutlinerFormatting( SdrOutliner& rOutl, tools::Rectangle& rPaintRect ) const
1351 ImpInitDrawOutliner( rOutl );
1352 UpdateOutlinerFormatting( rOutl, rPaintRect );
1355 void SdrTextObj::UpdateOutlinerFormatting( SdrOutliner& rOutl, tools::Rectangle& rPaintRect ) const
1357 tools::Rectangle aTextRect;
1358 tools::Rectangle aAnchorRect;
1359 Fraction aFitXCorrection(1,1);
1361 const bool bContourFrame(IsContourTextFrame());
1362 const MapMode aMapMode(
1363 getSdrModelFromSdrObject().GetScaleUnit(),
1364 Point(0,0),
1365 getSdrModelFromSdrObject().GetScaleFraction(),
1366 getSdrModelFromSdrObject().GetScaleFraction());
1368 rOutl.SetRefMapMode(aMapMode);
1369 ImpSetupDrawOutlinerForPaint(
1370 bContourFrame,
1371 rOutl,
1372 aTextRect,
1373 aAnchorRect,
1374 rPaintRect,
1375 aFitXCorrection);
1379 OutlinerParaObject* SdrTextObj::GetOutlinerParaObject() const
1381 SdrText* pText = getActiveText();
1382 if( pText )
1383 return pText->GetOutlinerParaObject();
1384 else
1385 return nullptr;
1388 void SdrTextObj::NbcSetOutlinerParaObject(std::unique_ptr<OutlinerParaObject> pTextObject)
1390 NbcSetOutlinerParaObjectForText( std::move(pTextObject), getActiveText() );
1393 void SdrTextObj::NbcSetOutlinerParaObjectForText( std::unique_ptr<OutlinerParaObject> pTextObject, SdrText* pText )
1395 if( pText )
1396 pText->SetOutlinerParaObject( std::move(pTextObject) );
1398 if (pText && pText->GetOutlinerParaObject())
1400 SvxWritingModeItem aWritingMode(pText->GetOutlinerParaObject()->IsVertical() && pText->GetOutlinerParaObject()->IsTopToBottom()
1401 ? css::text::WritingMode_TB_RL
1402 : css::text::WritingMode_LR_TB,
1403 SDRATTR_TEXTDIRECTION);
1404 GetProperties().SetObjectItemDirect(aWritingMode);
1407 SetTextSizeDirty();
1408 if (IsTextFrame() && (IsAutoGrowHeight() || IsAutoGrowWidth()))
1409 { // adapt text frame!
1410 NbcAdjustTextFrameWidthAndHeight();
1412 if (!IsTextFrame())
1414 // the SnapRect keeps its size
1415 SetRectsDirty(true);
1418 // always invalidate BoundRect on change
1419 SetBoundRectDirty();
1420 ActionChanged();
1422 ImpSetTextStyleSheetListeners();
1425 void SdrTextObj::NbcReformatText()
1427 SdrText* pText = getActiveText();
1428 if( pText && pText->GetOutlinerParaObject() )
1430 pText->ReformatText();
1431 if (bTextFrame)
1433 NbcAdjustTextFrameWidthAndHeight();
1435 else
1437 // the SnapRect keeps its size
1438 SetBoundRectDirty();
1439 SetRectsDirty(true);
1441 SetTextSizeDirty();
1442 ActionChanged();
1443 // i22396
1444 // Necessary here since we have no compare operator at the outliner
1445 // para object which may detect changes regarding the combination
1446 // of outliner para data and configuration (e.g., change of
1447 // formatting of text numerals)
1448 GetViewContact().flushViewObjectContacts(false);
1452 SdrObjGeoData* SdrTextObj::NewGeoData() const
1454 return new SdrTextObjGeoData;
1457 void SdrTextObj::SaveGeoData(SdrObjGeoData& rGeo) const
1459 SdrAttrObj::SaveGeoData(rGeo);
1460 SdrTextObjGeoData& rTGeo=static_cast<SdrTextObjGeoData&>(rGeo);
1461 rTGeo.aRect = maRect;
1462 rTGeo.aGeo =aGeo;
1465 void SdrTextObj::RestGeoData(const SdrObjGeoData& rGeo)
1466 { // RectsDirty is called by SdrObject
1467 SdrAttrObj::RestGeoData(rGeo);
1468 const SdrTextObjGeoData& rTGeo=static_cast<const SdrTextObjGeoData&>(rGeo);
1469 NbcSetLogicRect(rTGeo.aRect);
1470 aGeo =rTGeo.aGeo;
1471 SetTextSizeDirty();
1474 drawing::TextFitToSizeType SdrTextObj::GetFitToSize() const
1476 drawing::TextFitToSizeType eType = drawing::TextFitToSizeType_NONE;
1478 if(!IsAutoGrowWidth())
1479 eType = GetObjectItem(SDRATTR_TEXT_FITTOSIZE).GetValue();
1481 return eType;
1484 const tools::Rectangle& SdrTextObj::GetGeoRect() const
1486 return maRect;
1489 void SdrTextObj::ForceOutlinerParaObject()
1491 SdrText* pText = getActiveText();
1492 if( pText && (pText->GetOutlinerParaObject() == nullptr) )
1494 OutlinerMode nOutlMode = OutlinerMode::TextObject;
1495 if( IsTextFrame() && eTextKind == OBJ_OUTLINETEXT )
1496 nOutlMode = OutlinerMode::OutlineObject;
1498 pText->ForceOutlinerParaObject( nOutlMode );
1502 TextChain *SdrTextObj::GetTextChain() const
1504 //if (!IsChainable())
1505 // return NULL;
1507 return getSdrModelFromSdrObject().GetTextChain();
1510 bool SdrTextObj::IsVerticalWriting() const
1512 if(pEdtOutl)
1514 return pEdtOutl->IsVertical();
1517 OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
1518 if(pOutlinerParaObject)
1520 return pOutlinerParaObject->IsVertical();
1523 return false;
1526 void SdrTextObj::SetVerticalWriting(bool bVertical)
1528 OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
1530 if( !pOutlinerParaObject && bVertical )
1532 // we only need to force an outliner para object if the default of
1533 // horizontal text is changed
1534 ForceOutlinerParaObject();
1535 pOutlinerParaObject = GetOutlinerParaObject();
1538 if (!pOutlinerParaObject ||
1539 (pOutlinerParaObject->IsVertical() == bVertical))
1540 return;
1542 // get item settings
1543 const SfxItemSet& rSet = GetObjectItemSet();
1544 bool bAutoGrowWidth = rSet.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue();
1545 bool bAutoGrowHeight = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
1547 // Also exchange hor/ver adjust items
1548 SdrTextHorzAdjust eHorz = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue();
1549 SdrTextVertAdjust eVert = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
1551 // rescue object size
1552 tools::Rectangle aObjectRect = GetSnapRect();
1554 // prepare ItemSet to set exchanged width and height items
1555 SfxItemSet aNewSet(*rSet.GetPool(),
1556 svl::Items<SDRATTR_TEXT_AUTOGROWHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
1557 // Expanded item ranges to also support hor and ver adjust.
1558 SDRATTR_TEXT_VERTADJUST, SDRATTR_TEXT_VERTADJUST,
1559 SDRATTR_TEXT_AUTOGROWWIDTH, SDRATTR_TEXT_HORZADJUST>{});
1561 aNewSet.Put(rSet);
1562 aNewSet.Put(makeSdrTextAutoGrowWidthItem(bAutoGrowHeight));
1563 aNewSet.Put(makeSdrTextAutoGrowHeightItem(bAutoGrowWidth));
1565 // Exchange horz and vert adjusts
1566 switch (eVert)
1568 case SDRTEXTVERTADJUST_TOP: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); break;
1569 case SDRTEXTVERTADJUST_CENTER: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_CENTER)); break;
1570 case SDRTEXTVERTADJUST_BOTTOM: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT)); break;
1571 case SDRTEXTVERTADJUST_BLOCK: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_BLOCK)); break;
1573 switch (eHorz)
1575 case SDRTEXTHORZADJUST_LEFT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BOTTOM)); break;
1576 case SDRTEXTHORZADJUST_CENTER: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); break;
1577 case SDRTEXTHORZADJUST_RIGHT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP)); break;
1578 case SDRTEXTHORZADJUST_BLOCK: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BLOCK)); break;
1581 SetObjectItemSet(aNewSet);
1583 pOutlinerParaObject = GetOutlinerParaObject();
1584 if (pOutlinerParaObject)
1586 // set ParaObject orientation accordingly
1587 pOutlinerParaObject->SetVertical(bVertical);
1590 // restore object size
1591 SetSnapRect(aObjectRect);
1594 // transformation interface for StarOfficeAPI. This implements support for
1595 // homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
1596 // moment it contains a shearX, rotation and translation, but for setting all linear
1597 // transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
1600 // gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
1601 // with the base geometry and returns TRUE. Otherwise it returns FALSE.
1602 bool SdrTextObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
1604 // get turn and shear
1605 double fRotate = basegfx::deg2rad(aGeo.nRotationAngle / 100.0);
1606 double fShearX = basegfx::deg2rad(aGeo.nShearAngle / 100.0);
1608 // get aRect, this is the unrotated snaprect
1609 tools::Rectangle aRectangle(maRect);
1611 // fill other values
1612 basegfx::B2DTuple aScale(aRectangle.GetWidth(), aRectangle.GetHeight());
1613 basegfx::B2DTuple aTranslate(aRectangle.Left(), aRectangle.Top());
1615 // position maybe relative to anchorpos, convert
1616 if( getSdrModelFromSdrObject().IsWriter() )
1618 if(GetAnchorPos().X() || GetAnchorPos().Y())
1620 aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
1624 // build matrix
1625 rMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
1626 aScale,
1627 basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
1628 basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate,
1629 aTranslate);
1631 return false;
1634 // sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
1635 // If it's an SdrPathObj it will use the provided geometry information. The Polygon has
1636 // to use (0,0) as upper left and will be scaled to the given size in the matrix.
1637 void SdrTextObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
1639 // break up matrix
1640 basegfx::B2DTuple aScale;
1641 basegfx::B2DTuple aTranslate;
1642 double fRotate(0.0);
1643 double fShearX(0.0);
1644 rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
1646 // flip?
1647 bool bFlipX = aScale.getX() < 0.0,
1648 bFlipY = aScale.getY() < 0.0;
1649 if (bFlipX)
1651 aScale.setX(fabs(aScale.getX()));
1653 if (bFlipY)
1655 aScale.setY(fabs(aScale.getY()));
1658 // reset object shear and rotations
1659 aGeo.nRotationAngle = 0;
1660 aGeo.RecalcSinCos();
1661 aGeo.nShearAngle = 0;
1662 aGeo.RecalcTan();
1664 // if anchor is used, make position relative to it
1665 if( getSdrModelFromSdrObject().IsWriter() )
1667 if(GetAnchorPos().X() || GetAnchorPos().Y())
1669 aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
1673 // build and set BaseRect (use scale)
1674 Size aSize(FRound(aScale.getX()), FRound(aScale.getY()));
1675 tools::Rectangle aBaseRect(Point(), aSize);
1676 SetSnapRect(aBaseRect);
1678 // flip?
1679 if (bFlipX)
1681 Mirror(Point(), Point(0, 1));
1683 if (bFlipY)
1685 Mirror(Point(), Point(1, 0));
1688 // shear?
1689 if(!basegfx::fTools::equalZero(fShearX))
1691 GeoStat aGeoStat;
1692 aGeoStat.nShearAngle = FRound(basegfx::rad2deg(atan(fShearX)) * 100.0);
1693 aGeoStat.RecalcTan();
1694 Shear(Point(), aGeoStat.nShearAngle, aGeoStat.nTan, false);
1697 // rotation?
1698 if(!basegfx::fTools::equalZero(fRotate))
1700 GeoStat aGeoStat;
1702 // #i78696#
1703 // fRotate is matematically correct, but aGeoStat.nRotationAngle is
1704 // mirrored -> mirror value here
1705 aGeoStat.nRotationAngle = NormAngle36000(FRound(-fRotate / F_PI18000));
1706 aGeoStat.RecalcSinCos();
1707 Rotate(Point(), aGeoStat.nRotationAngle, aGeoStat.nSin, aGeoStat.nCos);
1710 // translate?
1711 if(!aTranslate.equalZero())
1713 Move(Size(FRound(aTranslate.getX()), FRound(aTranslate.getY())));
1717 bool SdrTextObj::IsReallyEdited() const
1719 return pEdtOutl && pEdtOutl->IsModified();
1722 // moved inlines here form hxx
1724 long SdrTextObj::GetEckenradius() const
1726 return GetObjectItemSet().Get(SDRATTR_ECKENRADIUS).GetValue();
1729 long SdrTextObj::GetMinTextFrameHeight() const
1731 return GetObjectItemSet().Get(SDRATTR_TEXT_MINFRAMEHEIGHT).GetValue();
1734 long SdrTextObj::GetMaxTextFrameHeight() const
1736 return GetObjectItemSet().Get(SDRATTR_TEXT_MAXFRAMEHEIGHT).GetValue();
1739 long SdrTextObj::GetMinTextFrameWidth() const
1741 return GetObjectItemSet().Get(SDRATTR_TEXT_MINFRAMEWIDTH).GetValue();
1744 long SdrTextObj::GetMaxTextFrameWidth() const
1746 return GetObjectItemSet().Get(SDRATTR_TEXT_MAXFRAMEWIDTH).GetValue();
1749 bool SdrTextObj::IsFontwork() const
1751 return !bTextFrame // Default is FALSE
1752 && GetObjectItemSet().Get(XATTR_FORMTXTSTYLE).GetValue() != XFormTextStyle::NONE;
1755 bool SdrTextObj::IsHideContour() const
1757 return !bTextFrame // Default is: no, don't HideContour; HideContour not together with TextFrames
1758 && GetObjectItemSet().Get(XATTR_FORMTXTHIDEFORM).GetValue();
1761 bool SdrTextObj::IsContourTextFrame() const
1763 return !bTextFrame // ContourFrame not together with normal TextFrames
1764 && GetObjectItemSet().Get(SDRATTR_TEXT_CONTOURFRAME).GetValue();
1767 long SdrTextObj::GetTextLeftDistance() const
1769 return GetObjectItemSet().Get(SDRATTR_TEXT_LEFTDIST).GetValue();
1772 long SdrTextObj::GetTextRightDistance() const
1774 return GetObjectItemSet().Get(SDRATTR_TEXT_RIGHTDIST).GetValue();
1777 long SdrTextObj::GetTextUpperDistance() const
1779 return GetObjectItemSet().Get(SDRATTR_TEXT_UPPERDIST).GetValue();
1782 long SdrTextObj::GetTextLowerDistance() const
1784 return GetObjectItemSet().Get(SDRATTR_TEXT_LOWERDIST).GetValue();
1787 SdrTextAniKind SdrTextObj::GetTextAniKind() const
1789 return GetObjectItemSet().Get(SDRATTR_TEXT_ANIKIND).GetValue();
1792 SdrTextAniDirection SdrTextObj::GetTextAniDirection() const
1794 return GetObjectItemSet().Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
1797 // Get necessary data for text scroll animation. ATM base it on a Text-Metafile and a
1798 // painting rectangle. Rotation is excluded from the returned values.
1799 GDIMetaFile* SdrTextObj::GetTextScrollMetaFileAndRectangle(
1800 tools::Rectangle& rScrollRectangle, tools::Rectangle& rPaintRectangle)
1802 GDIMetaFile* pRetval = nullptr;
1803 SdrOutliner& rOutliner = ImpGetDrawOutliner();
1804 tools::Rectangle aTextRect;
1805 tools::Rectangle aAnchorRect;
1806 tools::Rectangle aPaintRect;
1807 Fraction aFitXCorrection(1,1);
1808 bool bContourFrame(IsContourTextFrame());
1810 // get outliner set up. To avoid getting a somehow rotated MetaFile,
1811 // temporarily disable object rotation.
1812 sal_Int32 nAngle(aGeo.nRotationAngle);
1813 aGeo.nRotationAngle = 0;
1814 ImpSetupDrawOutlinerForPaint( bContourFrame, rOutliner, aTextRect, aAnchorRect, aPaintRect, aFitXCorrection );
1815 aGeo.nRotationAngle = nAngle;
1817 tools::Rectangle aScrollFrameRect(aPaintRect);
1818 const SfxItemSet& rSet = GetObjectItemSet();
1819 SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
1821 if(SdrTextAniDirection::Left == eDirection || SdrTextAniDirection::Right == eDirection)
1823 aScrollFrameRect.SetLeft( aAnchorRect.Left() );
1824 aScrollFrameRect.SetRight( aAnchorRect.Right() );
1827 if(SdrTextAniDirection::Up == eDirection || SdrTextAniDirection::Down == eDirection)
1829 aScrollFrameRect.SetTop( aAnchorRect.Top() );
1830 aScrollFrameRect.SetBottom( aAnchorRect.Bottom() );
1833 // create the MetaFile
1834 pRetval = new GDIMetaFile;
1835 ScopedVclPtrInstance< VirtualDevice > pBlackHole;
1836 pBlackHole->EnableOutput(false);
1837 pRetval->Record(pBlackHole);
1838 Point aPaintPos = aPaintRect.TopLeft();
1840 rOutliner.Draw(pBlackHole, aPaintPos);
1842 pRetval->Stop();
1843 pRetval->WindStart();
1845 // return PaintRectanglePixel and pRetval;
1846 rScrollRectangle = aScrollFrameRect;
1847 rPaintRectangle = aPaintRect;
1849 return pRetval;
1852 // Access to TextAnimationAllowed flag
1853 bool SdrTextObj::IsAutoFit() const
1855 return GetFitToSize() == drawing::TextFitToSizeType_AUTOFIT;
1858 bool SdrTextObj::IsFitToSize() const
1860 const drawing::TextFitToSizeType eFit = GetFitToSize();
1861 return (eFit == drawing::TextFitToSizeType_PROPORTIONAL
1862 || eFit == drawing::TextFitToSizeType_ALLLINES);
1865 void SdrTextObj::SetTextAnimationAllowed(bool bNew)
1867 if(mbTextAnimationAllowed != bNew)
1869 mbTextAnimationAllowed = bNew;
1870 ActionChanged();
1874 /** called from the SdrObjEditView during text edit when the status of the edit outliner changes */
1875 void SdrTextObj::onEditOutlinerStatusEvent( EditStatus* pEditStatus )
1877 const EditStatusFlags nStat = pEditStatus->GetStatusWord();
1878 const bool bGrowX = bool(nStat & EditStatusFlags::TEXTWIDTHCHANGED);
1879 const bool bGrowY = bool(nStat & EditStatusFlags::TextHeightChanged);
1880 if(bTextFrame && (bGrowX || bGrowY))
1882 if ((bGrowX && IsAutoGrowWidth()) || (bGrowY && IsAutoGrowHeight()))
1884 AdjustTextFrameWidthAndHeight();
1886 else if ( (IsAutoFit() || IsFitToSize()) && !mbInDownScale)
1888 assert(pEdtOutl);
1889 mbInDownScale = true;
1891 // sucks that we cannot disable paints via
1892 // pEdtOutl->SetUpdateMode(FALSE) - but EditEngine skips
1893 // formatting as well, then.
1894 ImpAutoFitText(*pEdtOutl);
1895 mbInDownScale = false;
1900 /* Begin chaining code */
1902 // XXX: Make it a method somewhere?
1903 static SdrObject *ImpGetObjByName(SdrObjList const *pObjList, OUString const& aObjName)
1905 // scan the whole list
1906 size_t nObjCount = pObjList->GetObjCount();
1907 for (size_t i = 0; i < nObjCount; i++) {
1908 SdrObject *pCurObj = pObjList->GetObj(i);
1910 if (pCurObj->GetName() == aObjName) {
1911 return pCurObj;
1914 // not found
1915 return nullptr;
1918 // XXX: Make it a (private) method of SdrTextObj
1919 static void ImpUpdateChainLinks(SdrTextObj *pTextObj, OUString const& aNextLinkName)
1921 // XXX: Current implementation constraints text boxes to be on the same page
1923 // No next link
1924 if (aNextLinkName.isEmpty()) {
1925 pTextObj->SetNextLinkInChain(nullptr);
1926 return;
1929 SdrPage *pPage(pTextObj->getSdrPageFromSdrObject());
1930 assert(pPage);
1931 SdrTextObj *pNextTextObj = dynamic_cast< SdrTextObj * >
1932 (ImpGetObjByName(pPage, aNextLinkName));
1933 if (!pNextTextObj) {
1934 SAL_INFO("svx.chaining", "[CHAINING] Can't find object as next link.");
1935 return;
1938 pTextObj->SetNextLinkInChain(pNextTextObj);
1941 bool SdrTextObj::IsChainable() const
1943 // Read it as item
1944 const SfxItemSet& rSet = GetObjectItemSet();
1945 OUString aNextLinkName = rSet.Get(SDRATTR_TEXT_CHAINNEXTNAME).GetValue();
1947 // Update links if any inconsistency is found
1948 bool bNextLinkUnsetYet = !aNextLinkName.isEmpty() && !mpNextInChain;
1949 bool bInconsistentNextLink = mpNextInChain && mpNextInChain->GetName() != aNextLinkName;
1950 // if the link is not set despite there should be one OR if it has changed
1951 if (bNextLinkUnsetYet || bInconsistentNextLink) {
1952 ImpUpdateChainLinks(const_cast<SdrTextObj *>(this), aNextLinkName);
1955 return !aNextLinkName.isEmpty(); // XXX: Should we also check for GetNilChainingEvent? (see old code below)
1958 // Check that no overflow is going on
1959 if (!GetTextChain() || GetTextChain()->GetNilChainingEvent(this))
1960 return false;
1964 void SdrTextObj::onChainingEvent()
1966 if (!pEdtOutl)
1967 return;
1969 // Outliner for text transfer
1970 SdrOutliner &aDrawOutliner = ImpGetDrawOutliner();
1972 EditingTextChainFlow aTxtChainFlow(this);
1973 aTxtChainFlow.CheckForFlowEvents(pEdtOutl);
1975 if (aTxtChainFlow.IsOverflow()) {
1976 SAL_INFO("svx.chaining", "[CHAINING] Overflow going on");
1977 // One outliner is for non-overflowing text, the other for overflowing text
1978 // We remove text directly from the editing outliner
1979 aTxtChainFlow.ExecuteOverflow(pEdtOutl, &aDrawOutliner);
1980 } else if (aTxtChainFlow.IsUnderflow()) {
1981 SAL_INFO("svx.chaining", "[CHAINING] Underflow going on");
1982 // underflow-induced overflow
1983 aTxtChainFlow.ExecuteUnderflow(&aDrawOutliner);
1984 bool bIsOverflowFromUnderflow = aTxtChainFlow.IsOverflow();
1985 // handle overflow
1986 if (bIsOverflowFromUnderflow) {
1987 SAL_INFO("svx.chaining", "[CHAINING] Overflow going on (underflow induced)");
1988 // prevents infinite loops when setting text for editing outliner
1989 aTxtChainFlow.ExecuteOverflow(&aDrawOutliner, &aDrawOutliner);
1994 SdrTextObj* SdrTextObj::GetNextLinkInChain() const
1997 if (GetTextChain())
1998 return GetTextChain()->GetNextLink(this);
2000 return NULL;
2003 return mpNextInChain;
2006 void SdrTextObj::SetNextLinkInChain(SdrTextObj *pNextObj)
2008 // Basically a doubly linked list implementation
2010 SdrTextObj *pOldNextObj = mpNextInChain;
2012 // Replace next link
2013 mpNextInChain = pNextObj;
2014 // Deal with old next link's prev link
2015 if (pOldNextObj) {
2016 pOldNextObj->mpPrevInChain = nullptr;
2019 // Deal with new next link's prev link
2020 if (mpNextInChain) {
2021 // If there is a prev already at all and this is not already the current object
2022 if (mpNextInChain->mpPrevInChain &&
2023 mpNextInChain->mpPrevInChain != this)
2024 mpNextInChain->mpPrevInChain->mpNextInChain = nullptr;
2025 mpNextInChain->mpPrevInChain = this;
2028 // TODO: Introduce check for circular chains
2032 SdrTextObj* SdrTextObj::GetPrevLinkInChain() const
2035 if (GetTextChain())
2036 return GetTextChain()->GetPrevLink(this);
2038 return NULL;
2041 return mpPrevInChain;
2044 bool SdrTextObj::GetPreventChainable() const
2046 // Prevent chaining it 1) during dragging && 2) when we are editing next link
2047 return mbIsUnchainableClone || (GetNextLinkInChain() && GetNextLinkInChain()->IsInEditMode());
2050 SdrObjectUniquePtr SdrTextObj::getFullDragClone() const
2052 SdrObjectUniquePtr pClone = SdrAttrObj::getFullDragClone();
2053 SdrTextObj *pTextObjClone = dynamic_cast<SdrTextObj *>(pClone.get());
2054 if (pTextObjClone != nullptr) {
2055 // Avoid transferring of text for chainable object during dragging
2056 pTextObjClone->mbIsUnchainableClone = true;
2059 return pClone;
2062 /* End chaining code */
2064 /** returns the currently active text. */
2065 SdrText* SdrTextObj::getActiveText() const
2067 if( !mpText )
2068 return getText( 0 );
2069 else
2070 return mpText.get();
2073 /** returns the nth available text. */
2074 SdrText* SdrTextObj::getText( sal_Int32 nIndex ) const
2076 if( nIndex == 0 )
2078 if( !mpText )
2079 const_cast< SdrTextObj* >(this)->mpText.reset( new SdrText( *const_cast< SdrTextObj* >(this) ) );
2080 return mpText.get();
2082 else
2084 return nullptr;
2088 /** returns the number of texts available for this object. */
2089 sal_Int32 SdrTextObj::getTextCount() const
2091 return 1;
2094 /** changes the current active text */
2095 void SdrTextObj::setActiveText( sal_Int32 /*nIndex*/ )
2099 /** returns the index of the text that contains the given point or -1 */
2100 sal_Int32 SdrTextObj::CheckTextHit(const Point& /*rPnt*/) const
2102 return 0;
2105 void SdrTextObj::SetObjectItemNoBroadcast(const SfxPoolItem& rItem)
2107 static_cast< sdr::properties::TextProperties& >(GetProperties()).SetObjectItemNoBroadcast(rItem);
2111 // The concept of the text object:
2112 // ~~~~~~~~~~~~~~~~~~~~~~~~
2113 // Attributes/Variations:
2114 // - bool text frame / graphics object with caption
2115 // - bool FontWork (if it is not a text frame and not a ContourTextFrame)
2116 // - bool ContourTextFrame (if it is not a text frame and not Fontwork)
2117 // - long rotation angle (if it is not FontWork)
2118 // - long text frame margins (if it is not FontWork)
2119 // - bool FitToSize (if it is not FontWork)
2120 // - bool AutoGrowingWidth/Height (if it is not FitToSize and not FontWork)
2121 // - long Min/MaxFrameWidth/Height (if AutoGrowingWidth/Height)
2122 // - enum horizontal text anchoring left,center,right,justify/block,Stretch(ni)
2123 // - enum vertical text anchoring top, middle, bottom, block, stretch(ni)
2124 // - enum ticker text (if it is not FontWork)
2126 // Every derived object is either a text frame (bTextFrame=true)
2127 // or a drawing object with a caption (bTextFrame=false).
2129 // Default anchoring for text frames:
2130 // SDRTEXTHORZADJUST_BLOCK, SDRTEXTVERTADJUST_TOP
2131 // = static Pool defaults
2132 // Default anchoring for drawing objects with a caption:
2133 // SDRTEXTHORZADJUST_CENTER, SDRTEXTVERTADJUST_CENTER
2134 // via "hard" attribution of SdrAttrObj
2136 // Every object derived from SdrTextObj must return an "UnrotatedSnapRect"
2137 // (->TakeUnrotatedSnapRect()) (the reference point for the rotation is the top
2138 // left of the rectangle (aGeo.nRotationAngle)) which is the basis for anchoring
2139 // text. We then subtract the text frame margins from this rectangle, as a re-
2140 // sult we get the anchoring area (->TakeTextAnchorRect()). Within this area, we
2141 // calculate the anchoring point and the painting area, depending on the hori-
2142 // zontal and vertical adjustment of the text (SdrTextVertAdjust,
2143 // SdrTextHorzAdjust).
2144 // In the case of drawing objects with a caption the painting area might well
2145 // be larger than the anchoring area, for text frames on the other hand, it is
2146 // always of the same or a smaller size (except when there are negative text
2147 // frame margins).
2149 // FitToSize takes priority over text anchoring and AutoGrowHeight/Width. When
2150 // FitToSize is turned on, the painting area is always equal to the anchoring
2151 // area. Additionally, FitToSize doesn't allow automatic line breaks.
2153 // ContourTextFrame:
2154 // - long rotation angle
2155 // - long text frame margins (maybe later)
2156 // - bool FitToSize (maybe later)
2157 // - bool AutoGrowingWidth/Height (maybe much later)
2158 // - long Min/MaxFrameWidth/Height (maybe much later)
2159 // - enum horizontal text anchoring (maybe later, for now: left, centered)
2160 // - enum vertical text anchoring (maybe later, for now: top)
2161 // - enum ticker text (maybe later, maybe even with correct clipping)
2163 // When making changes, check these:
2164 // - Paint
2165 // - HitTest
2166 // - ConvertToPoly
2167 // - Edit
2168 // - Printing, Saving, Painting in neighboring View while editing
2169 // - ModelChanged (e. g. through a neighboring View or rulers) while editing
2170 // - FillColorChanged while editing
2171 // - and many more...
2174 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */