Bump version to 24.04.3.4
[LibreOffice.git] / svx / source / svdraw / svdotext.cxx
blob986e2c7a4126ae66d5364d126c7459e11da378af
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 <svl/stritem.hxx>
23 #include <svx/svdotext.hxx>
24 #include <svx/svdpage.hxx>
25 #include <svx/svdoutl.hxx>
26 #include <svx/svdmodel.hxx>
27 #include <svx/dialmgr.hxx>
28 #include <svx/strings.hrc>
29 #include <editeng/writingmodeitem.hxx>
30 #include <svx/sdtfchim.hxx>
31 #include <editeng/editdata.hxx>
32 #include <editeng/editstat.hxx>
33 #include <editeng/outlobj.hxx>
34 #include <editeng/editobj.hxx>
35 #include <editeng/outliner.hxx>
36 #include <textchain.hxx>
37 #include <textchainflow.hxx>
38 #include <tools/helpers.hxx>
39 #include <svx/sderitm.hxx>
40 #include <svx/sdooitm.hxx>
41 #include <svx/sdshitm.hxx>
42 #include <svx/sdtagitm.hxx>
43 #include <svx/sdtfsitm.hxx>
44 #include <svx/sdtmfitm.hxx>
45 #include <svx/xtextit0.hxx>
46 #include <svx/compatflags.hxx>
47 #include <sdr/properties/textproperties.hxx>
48 #include <sdr/contact/viewcontactoftextobj.hxx>
49 #include <basegfx/tuple/b2dtuple.hxx>
50 #include <basegfx/matrix/b2dhommatrix.hxx>
51 #include <vcl/gdimtf.hxx>
52 #include <vcl/virdev.hxx>
53 #include <basegfx/matrix/b2dhommatrixtools.hxx>
54 #include <sal/log.hxx>
55 #include <o3tl/unit_conversion.hxx>
56 #include <o3tl/temporary.hxx>
57 #include <unotools/configmgr.hxx>
58 #include <editeng/eeitem.hxx>
59 #include <editeng/fhgtitem.hxx>
61 using namespace com::sun::star;
63 // BaseProperties section
64 std::unique_ptr<sdr::properties::BaseProperties> SdrTextObj::CreateObjectSpecificProperties()
66 return std::make_unique<sdr::properties::TextProperties>(*this);
69 // DrawContact section
70 std::unique_ptr<sdr::contact::ViewContact> SdrTextObj::CreateObjectSpecificViewContact()
72 return std::make_unique<sdr::contact::ViewContactOfTextObj>(*this);
75 SdrTextObj::SdrTextObj(SdrModel& rSdrModel)
76 : SdrAttrObj(rSdrModel)
77 , mpEditingOutliner(nullptr)
78 , meTextKind(SdrObjKind::Text)
79 , maTextEditOffset(Point(0, 0))
80 , mbTextFrame(false)
81 , mbNoShear(false)
82 , mbTextSizeDirty(false)
83 , mbInEditMode(false)
84 , mbDisableAutoWidthOnDragging(false)
85 , mbTextAnimationAllowed(true)
86 , mbInDownScale(false)
88 // #i25616#
89 mbSupportTextIndentingOnLineWidthChange = true;
92 SdrTextObj::SdrTextObj(SdrModel& rSdrModel, SdrTextObj const & rSource)
93 : SdrAttrObj(rSdrModel, rSource)
94 , mpEditingOutliner(nullptr)
95 , meTextKind(rSource.meTextKind)
96 , maTextEditOffset(Point(0, 0))
97 , mbTextFrame(rSource.mbTextFrame)
98 , mbNoShear(rSource.mbNoShear)
99 , mbTextSizeDirty(rSource.mbTextSizeDirty)
100 , mbInEditMode(false)
101 , mbDisableAutoWidthOnDragging(rSource.mbDisableAutoWidthOnDragging)
102 , mbTextAnimationAllowed(true)
103 , mbInDownScale(false)
105 // #i25616#
106 mbSupportTextIndentingOnLineWidthChange = true;
108 maRectangle = rSource.maRectangle;
109 maGeo = rSource.maGeo;
110 maTextSize = rSource.maTextSize;
112 // Not all of the necessary parameters were copied yet.
113 SdrText* pText = getActiveText();
115 if( pText && rSource.HasText() )
117 // before pNewOutlinerParaObject was created the same, but
118 // set at mpText (outside this scope), but mpText might be
119 // empty (this operator== seems not prepared for MultiText
120 // objects). In the current form it makes only sense to
121 // create locally and use locally on a known existing SdrText
122 const Outliner* pEO = rSource.mpEditingOutliner;
123 std::optional<OutlinerParaObject> pNewOutlinerParaObject;
125 if (pEO!=nullptr)
127 pNewOutlinerParaObject = pEO->CreateParaObject();
129 else if (nullptr != rSource.getActiveText()->GetOutlinerParaObject())
131 pNewOutlinerParaObject = *rSource.getActiveText()->GetOutlinerParaObject();
134 pText->SetOutlinerParaObject( std::move(pNewOutlinerParaObject) );
137 ImpSetTextStyleSheetListeners();
140 SdrTextObj::SdrTextObj(SdrModel& rSdrModel, const tools::Rectangle& rNewRect)
141 : SdrAttrObj(rSdrModel)
142 , mpEditingOutliner(nullptr)
143 , meTextKind(SdrObjKind::Text)
144 , maTextEditOffset(Point(0, 0))
145 , mbTextFrame(false)
146 , mbNoShear(false)
147 , mbTextSizeDirty(false)
148 , mbInEditMode(false)
149 , mbDisableAutoWidthOnDragging(false)
150 , mbTextAnimationAllowed(true)
151 , mbInDownScale(false)
153 tools::Rectangle aRectangle(rNewRect);
154 ImpJustifyRect(aRectangle);
155 setRectangle(aRectangle);
157 // #i25616#
158 mbSupportTextIndentingOnLineWidthChange = true;
161 SdrTextObj::SdrTextObj(SdrModel& rSdrModel, SdrObjKind eNewTextKind)
162 : SdrAttrObj(rSdrModel)
163 , mpEditingOutliner(nullptr)
164 , meTextKind(eNewTextKind)
165 , maTextEditOffset(Point(0, 0))
166 , mbTextFrame(true)
167 , mbNoShear(true)
168 , mbTextSizeDirty(false)
169 , mbInEditMode(false)
170 , mbDisableAutoWidthOnDragging(false)
171 , mbTextAnimationAllowed(true)
172 , mbInDownScale(false)
174 // #i25616#
175 mbSupportTextIndentingOnLineWidthChange = true;
178 SdrTextObj::SdrTextObj(SdrModel& rSdrModel, SdrObjKind eNewTextKind,
179 const tools::Rectangle& rNewRect)
180 : SdrAttrObj(rSdrModel)
181 , mpEditingOutliner(nullptr)
182 , meTextKind(eNewTextKind)
183 , maTextEditOffset(Point(0, 0))
184 , mbTextFrame(true)
185 , mbNoShear(true)
186 , mbTextSizeDirty(false)
187 , mbInEditMode(false)
188 , mbDisableAutoWidthOnDragging(false)
189 , mbTextAnimationAllowed(true)
190 , mbInDownScale(false)
192 tools::Rectangle aRectangle(rNewRect);
193 ImpJustifyRect(aRectangle);
194 setRectangle(aRectangle);
196 // #i25616#
197 mbSupportTextIndentingOnLineWidthChange = true;
200 SdrTextObj::~SdrTextObj()
202 mxText.clear();
203 ImpDeregisterLink();
206 void SdrTextObj::FitFrameToTextSize()
208 ImpJustifyRect(maRectangle);
210 SdrText* pText = getActiveText();
211 if(pText==nullptr || !pText->GetOutlinerParaObject())
212 return;
214 SdrOutliner& rOutliner=ImpGetDrawOutliner();
215 rOutliner.SetPaperSize(Size(getRectangle().Right() - getRectangle().Left(), getRectangle().Bottom() - getRectangle().Top()));
216 rOutliner.SetUpdateLayout(true);
217 rOutliner.SetText(*pText->GetOutlinerParaObject());
218 Size aNewSize(rOutliner.CalcTextSize());
219 rOutliner.Clear();
220 aNewSize.AdjustWidth( 1 ); // because of possible rounding errors
221 aNewSize.AdjustWidth(GetTextLeftDistance()+GetTextRightDistance() );
222 aNewSize.AdjustHeight(GetTextUpperDistance()+GetTextLowerDistance() );
223 tools::Rectangle aNewRect(getRectangle());
224 aNewRect.SetSize(aNewSize);
225 ImpJustifyRect(aNewRect);
227 if (aNewRect != getRectangle())
228 SetLogicRect(aNewRect);
231 void SdrTextObj::NbcSetText(const OUString& rStr)
233 SdrOutliner& rOutliner=ImpGetDrawOutliner();
234 rOutliner.SetStyleSheet( 0, GetStyleSheet());
235 rOutliner.SetText(rStr,rOutliner.GetParagraph( 0 ));
236 std::optional<OutlinerParaObject> pNewText=rOutliner.CreateParaObject();
237 NbcSetOutlinerParaObject(std::move(pNewText));
238 mbTextSizeDirty=true;
241 void SdrTextObj::SetText(const OUString& rStr)
243 tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
244 NbcSetText(rStr);
245 SetChanged();
246 BroadcastObjectChange();
247 SendUserCall(SdrUserCallType::Resize,aBoundRect0);
250 void SdrTextObj::NbcSetText(SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat)
252 SdrOutliner& rOutliner=ImpGetDrawOutliner();
253 rOutliner.SetStyleSheet( 0, GetStyleSheet());
254 rOutliner.Read(rInput,rBaseURL,eFormat);
255 std::optional<OutlinerParaObject> pNewText=rOutliner.CreateParaObject();
256 rOutliner.SetUpdateLayout(true);
257 Size aSize(rOutliner.CalcTextSize());
258 rOutliner.Clear();
259 NbcSetOutlinerParaObject(std::move(pNewText));
260 maTextSize=aSize;
261 mbTextSizeDirty=false;
264 void SdrTextObj::SetText(SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat)
266 tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
267 NbcSetText(rInput,rBaseURL,eFormat);
268 SetChanged();
269 BroadcastObjectChange();
270 SendUserCall(SdrUserCallType::Resize,aBoundRect0);
273 const Size& SdrTextObj::GetTextSize() const
275 if (mbTextSizeDirty)
277 Size aSiz;
278 SdrText* pText = getActiveText();
279 if( pText && pText->GetOutlinerParaObject ())
281 SdrOutliner& rOutliner=ImpGetDrawOutliner();
282 rOutliner.SetText(*pText->GetOutlinerParaObject());
283 rOutliner.SetUpdateLayout(true);
284 aSiz=rOutliner.CalcTextSize();
285 rOutliner.Clear();
287 // casting to nonconst twice
288 const_cast<SdrTextObj*>(this)->maTextSize = aSiz;
289 const_cast<SdrTextObj*>(this)->mbTextSizeDirty = false;
291 return maTextSize;
294 bool SdrTextObj::IsAutoGrowHeight() const
296 if(!mbTextFrame)
297 return false; // AutoGrow only together with TextFrames
299 const SfxItemSet& rSet = GetObjectItemSet();
300 bool bRet = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
302 if(bRet)
304 SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
306 if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
308 SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
310 if(eDirection == SdrTextAniDirection::Up || eDirection == SdrTextAniDirection::Down)
312 bRet = false;
316 return bRet;
319 bool SdrTextObj::IsAutoGrowWidth() const
321 if (!mbTextFrame)
322 return false; // AutoGrow only together with TextFrames
324 const SfxItemSet& rSet = GetObjectItemSet();
325 bool bRet = rSet.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue();
327 bool bInEditMOde = IsInEditMode();
329 if(!bInEditMOde && bRet)
331 SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
333 if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
335 SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
337 if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
339 bRet = false;
343 return bRet;
346 SdrTextHorzAdjust SdrTextObj::GetTextHorizontalAdjust() const
348 return GetTextHorizontalAdjust(GetObjectItemSet());
351 SdrTextHorzAdjust SdrTextObj::GetTextHorizontalAdjust(const SfxItemSet& rSet) const
353 if(IsContourTextFrame())
354 return SDRTEXTHORZADJUST_BLOCK;
356 SdrTextHorzAdjust eRet = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue();
358 bool bInEditMode = IsInEditMode();
360 if(!bInEditMode && eRet == SDRTEXTHORZADJUST_BLOCK)
362 SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
364 if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
366 SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
368 if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
370 eRet = SDRTEXTHORZADJUST_LEFT;
375 return eRet;
376 } // defaults: BLOCK (justify) for text frame, CENTER for captions of drawing objects
378 SdrTextVertAdjust SdrTextObj::GetTextVerticalAdjust() const
380 return GetTextVerticalAdjust(GetObjectItemSet());
383 SdrTextVertAdjust SdrTextObj::GetTextVerticalAdjust(const SfxItemSet& rSet) const
385 if(IsContourTextFrame())
386 return SDRTEXTVERTADJUST_TOP;
388 // Take care for vertical text animation here
389 SdrTextVertAdjust eRet = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
390 bool bInEditMode = IsInEditMode();
392 // Take care for vertical text animation here
393 if(!bInEditMode && eRet == SDRTEXTVERTADJUST_BLOCK)
395 SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
397 if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
399 SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
401 if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
403 eRet = SDRTEXTVERTADJUST_TOP;
408 return eRet;
409 } // defaults: TOP for text frame, CENTER for captions of drawing objects
411 void SdrTextObj::ImpJustifyRect(tools::Rectangle& rRect)
413 if (!rRect.IsEmpty()) {
414 rRect.Normalize();
415 if (rRect.Left()==rRect.Right()) rRect.AdjustRight( 1 );
416 if (rRect.Top()==rRect.Bottom()) rRect.AdjustBottom( 1 );
420 void SdrTextObj::ImpCheckShear()
422 if (mbNoShear && maGeo.m_nShearAngle)
424 maGeo.m_nShearAngle = 0_deg100;
425 maGeo.mfTanShearAngle = 0;
429 void SdrTextObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
431 bool bNoTextFrame=!IsTextFrame();
432 rInfo.bResizeFreeAllowed=bNoTextFrame || ((maGeo.m_nRotationAngle.get() % 9000) == 0);
433 rInfo.bResizePropAllowed=true;
434 rInfo.bRotateFreeAllowed=true;
435 rInfo.bRotate90Allowed =true;
436 rInfo.bMirrorFreeAllowed=bNoTextFrame;
437 rInfo.bMirror45Allowed =bNoTextFrame;
438 rInfo.bMirror90Allowed =bNoTextFrame;
440 // allow transparency
441 rInfo.bTransparenceAllowed = true;
443 rInfo.bShearAllowed =bNoTextFrame;
444 rInfo.bEdgeRadiusAllowed=true;
445 bool bCanConv=ImpCanConvTextToCurve();
446 rInfo.bCanConvToPath =bCanConv;
447 rInfo.bCanConvToPoly =bCanConv;
448 rInfo.bCanConvToPathLineToArea=bCanConv;
449 rInfo.bCanConvToPolyLineToArea=bCanConv;
450 rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
453 SdrObjKind SdrTextObj::GetObjIdentifier() const
455 return meTextKind;
458 bool SdrTextObj::HasTextImpl( SdrOutliner const * pOutliner )
460 bool bRet=false;
461 if(pOutliner)
463 Paragraph* p1stPara=pOutliner->GetParagraph( 0 );
464 sal_Int32 nParaCount=pOutliner->GetParagraphCount();
465 if(p1stPara==nullptr)
466 nParaCount=0;
468 if(nParaCount==1)
470 // if it is only one paragraph, check if that paragraph is empty
471 if( pOutliner->GetText(p1stPara).isEmpty() )
472 nParaCount = 0;
475 bRet= nParaCount!=0;
477 return bRet;
480 void SdrTextObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
482 const bool bRemove(pNewPage == nullptr && pOldPage != nullptr);
483 const bool bInsert(pNewPage != nullptr && pOldPage == nullptr);
484 const bool bLinked(IsLinkedText());
486 if (bLinked && bRemove)
488 ImpDeregisterLink();
491 // call parent
492 SdrAttrObj::handlePageChange(pOldPage, pNewPage);
494 if (bLinked && bInsert)
496 ImpRegisterLink();
500 void SdrTextObj::NbcSetEckenradius(tools::Long nRad)
502 SetObjectItem(makeSdrEckenradiusItem(nRad));
505 // #115391# This implementation is based on the object size (aRect) and the
506 // states of IsAutoGrowWidth/Height to correctly set TextMinFrameWidth/Height
507 void SdrTextObj::AdaptTextMinSize()
509 if (!mbTextFrame)
510 // Only do this for text frame.
511 return;
513 if (getSdrModelFromSdrObject().IsPasteResize())
514 // Don't do this during paste resize.
515 return;
517 const bool bW = IsAutoGrowWidth();
518 const bool bH = IsAutoGrowHeight();
520 if (!bW && !bH)
521 // No auto grow requested. Bail out.
522 return;
524 SfxItemSetFixed<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
525 SDRATTR_TEXT_MINFRAMEWIDTH, SDRATTR_TEXT_AUTOGROWWIDTH> // contains SDRATTR_TEXT_MAXFRAMEWIDTH
526 aSet(*GetObjectItemSet().GetPool());
528 if(bW)
530 // Set minimum width.
531 const tools::Long nDist = GetTextLeftDistance() + GetTextRightDistance();
532 const tools::Long nW = std::max<tools::Long>(0, getRectangle().GetWidth() - 1 - nDist); // text width without margins
534 aSet.Put(makeSdrTextMinFrameWidthItem(nW));
536 if(!IsVerticalWriting() && mbDisableAutoWidthOnDragging)
538 mbDisableAutoWidthOnDragging = true;
539 aSet.Put(makeSdrTextAutoGrowWidthItem(false));
543 if(bH)
545 // Set Minimum height.
546 const tools::Long nDist = GetTextUpperDistance() + GetTextLowerDistance();
547 const tools::Long nH = std::max<tools::Long>(0, getRectangle().GetHeight() - 1 - nDist); // text height without margins
549 aSet.Put(makeSdrTextMinFrameHeightItem(nH));
551 if(IsVerticalWriting() && mbDisableAutoWidthOnDragging)
553 mbDisableAutoWidthOnDragging = false;
554 aSet.Put(makeSdrTextAutoGrowHeightItem(false));
558 SetObjectItemSet(aSet);
561 void SdrTextObj::ImpSetContourPolygon( SdrOutliner& rOutliner, tools::Rectangle const & rAnchorRect, bool bLineWidth ) const
563 basegfx::B2DPolyPolygon aXorPolyPolygon(TakeXorPoly());
564 std::optional<basegfx::B2DPolyPolygon> pContourPolyPolygon;
565 basegfx::B2DHomMatrix aMatrix(basegfx::utils::createTranslateB2DHomMatrix(
566 -rAnchorRect.Left(), -rAnchorRect.Top()));
568 if(maGeo.m_nRotationAngle)
570 // Unrotate!
571 aMatrix.rotate(-toRadians(maGeo.m_nRotationAngle));
574 aXorPolyPolygon.transform(aMatrix);
576 if( bLineWidth )
578 // Take line width into account.
579 // When doing the hit test, avoid this. (Performance!)
580 pContourPolyPolygon.emplace();
582 // test if shadow needs to be avoided for TakeContour()
583 const SfxItemSet& rSet = GetObjectItemSet();
584 bool bShadowOn = rSet.Get(SDRATTR_SHADOW).GetValue();
586 // #i33696#
587 // Remember TextObject currently set at the DrawOutliner, it WILL be
588 // replaced during calculating the outline since it uses an own paint
589 // and that one uses the DrawOutliner, too.
590 const SdrTextObj* pLastTextObject = rOutliner.GetTextObj();
592 if(bShadowOn)
594 // force shadow off
595 rtl::Reference<SdrTextObj> pCopy = SdrObject::Clone(*this, getSdrModelFromSdrObject());
596 pCopy->SetMergedItem(makeSdrShadowItem(false));
597 *pContourPolyPolygon = pCopy->TakeContour();
599 else
601 *pContourPolyPolygon = TakeContour();
604 // #i33696#
605 // restore remembered text object
606 if(pLastTextObject != rOutliner.GetTextObj())
608 rOutliner.SetTextObj(pLastTextObject);
611 pContourPolyPolygon->transform(aMatrix);
614 rOutliner.SetPolygon(aXorPolyPolygon, pContourPolyPolygon ? &*pContourPolyPolygon : nullptr);
617 void SdrTextObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
619 rRect = getRectangle();
622 // See also: <unnamed>::getTextAnchorRange in svx/source/sdr/primitive2d/sdrdecompositiontools.cxx
623 void SdrTextObj::AdjustRectToTextDistance(tools::Rectangle& rAnchorRect) const
625 const tools::Long nLeftDist = GetTextLeftDistance();
626 const tools::Long nRightDist = GetTextRightDistance();
627 const tools::Long nUpperDist = GetTextUpperDistance();
628 const tools::Long nLowerDist = GetTextLowerDistance();
629 if (!IsVerticalWriting())
631 rAnchorRect.AdjustLeft(nLeftDist);
632 rAnchorRect.AdjustTop(nUpperDist);
633 rAnchorRect.AdjustRight(-nRightDist);
634 rAnchorRect.AdjustBottom(-nLowerDist);
636 else if (IsTopToBottom())
638 rAnchorRect.AdjustLeft(nLowerDist);
639 rAnchorRect.AdjustTop(nLeftDist);
640 rAnchorRect.AdjustRight(-nUpperDist);
641 rAnchorRect.AdjustBottom(-nRightDist);
643 else
645 rAnchorRect.AdjustLeft(nUpperDist);
646 rAnchorRect.AdjustTop(nRightDist);
647 rAnchorRect.AdjustRight(-nLowerDist);
648 rAnchorRect.AdjustBottom(-nLeftDist);
651 // Since sizes may be bigger than the object bounds it is necessary to
652 // justify the rect now.
653 ImpJustifyRect(rAnchorRect);
656 void SdrTextObj::TakeTextAnchorRect(tools::Rectangle& rAnchorRect) const
658 tools::Rectangle aAnkRect(getRectangle()); // the rectangle in which we anchor
659 bool bFrame=IsTextFrame();
660 if (!bFrame) {
661 TakeUnrotatedSnapRect(aAnkRect);
663 Point aRotateRef(aAnkRect.TopLeft());
664 AdjustRectToTextDistance(aAnkRect);
666 if (bFrame) {
667 // TODO: Optimize this.
668 if (aAnkRect.GetWidth()<2) aAnkRect.SetRight(aAnkRect.Left()+1 ); // minimum size h and v: 2 px
669 if (aAnkRect.GetHeight()<2) aAnkRect.SetBottom(aAnkRect.Top()+1 );
671 if (maGeo.m_nRotationAngle) {
672 Point aTmpPt(aAnkRect.TopLeft());
673 RotatePoint(aTmpPt,aRotateRef,maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
674 aTmpPt-=aAnkRect.TopLeft();
675 aAnkRect.Move(aTmpPt.X(),aTmpPt.Y());
677 rAnchorRect=aAnkRect;
680 void SdrTextObj::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText,
681 tools::Rectangle* pAnchorRect, bool bLineWidth ) const
683 tools::Rectangle aAnkRect; // the rectangle in which we anchor
684 TakeTextAnchorRect(aAnkRect);
685 SdrTextVertAdjust eVAdj=GetTextVerticalAdjust();
686 SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust();
687 SdrTextAniKind eAniKind=GetTextAniKind();
688 SdrTextAniDirection eAniDirection=GetTextAniDirection();
690 bool bFitToSize(IsFitToSize());
691 bool bContourFrame=IsContourTextFrame();
693 bool bFrame=IsTextFrame();
694 EEControlBits nStat0=rOutliner.GetControlWord();
695 Size aNullSize;
696 if (!bContourFrame)
698 rOutliner.SetControlWord(nStat0|EEControlBits::AUTOPAGESIZE);
699 rOutliner.SetMinAutoPaperSize(aNullSize);
700 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
703 if (!bFitToSize && !bContourFrame)
705 tools::Long nAnkWdt=aAnkRect.GetWidth();
706 tools::Long nAnkHgt=aAnkRect.GetHeight();
707 if (bFrame)
709 tools::Long nWdt=nAnkWdt;
710 tools::Long nHgt=nAnkHgt;
712 bool bInEditMode = IsInEditMode();
714 if (!bInEditMode && (eAniKind==SdrTextAniKind::Scroll || eAniKind==SdrTextAniKind::Alternate || eAniKind==SdrTextAniKind::Slide))
716 // unlimited paper size for ticker text
717 if (eAniDirection==SdrTextAniDirection::Left || eAniDirection==SdrTextAniDirection::Right) nWdt=1000000;
718 if (eAniDirection==SdrTextAniDirection::Up || eAniDirection==SdrTextAniDirection::Down) nHgt=1000000;
721 bool bChainedFrame = IsChainable();
722 // Might be required for overflow check working: do limit height to frame if box is chainable.
723 if (!bChainedFrame) {
724 // #i119885# Do not limit/force height to geometrical frame (vice versa for vertical writing)
726 if(IsVerticalWriting())
728 nWdt = 1000000;
730 else
732 nHgt = 1000000;
736 rOutliner.SetMaxAutoPaperSize(Size(nWdt,nHgt));
739 // New try with _BLOCK for hor and ver after completely
740 // supporting full width for vertical text.
741 if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting())
743 rOutliner.SetMinAutoPaperSize(Size(nAnkWdt, 0));
744 rOutliner.SetMinColumnWrapHeight(nAnkHgt);
747 if(SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting())
749 rOutliner.SetMinAutoPaperSize(Size(0, nAnkHgt));
750 rOutliner.SetMinColumnWrapHeight(nAnkWdt);
754 rOutliner.SetPaperSize(aNullSize);
755 if (bContourFrame)
756 ImpSetContourPolygon( rOutliner, aAnkRect, bLineWidth );
758 // put text into the outliner, if available from the edit outliner
759 SdrText* pText = getActiveText();
760 OutlinerParaObject* pOutlinerParaObject = pText ? pText->GetOutlinerParaObject() : nullptr;
761 std::optional<OutlinerParaObject> pPara;
762 if (mpEditingOutliner && !bNoEditText)
763 pPara = mpEditingOutliner->CreateParaObject();
764 else if (pOutlinerParaObject)
765 pPara = *pOutlinerParaObject;
767 if (pPara)
769 const bool bHitTest(&getSdrModelFromSdrObject().GetHitTestOutliner() == &rOutliner);
770 const SdrTextObj* pTestObj = rOutliner.GetTextObj();
772 if( !pTestObj || !bHitTest || pTestObj != this ||
773 pTestObj->GetOutlinerParaObject() != pOutlinerParaObject )
775 if( bHitTest ) // #i33696# take back fix #i27510#
777 rOutliner.SetTextObj( this );
778 rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
781 rOutliner.SetUpdateLayout(true);
782 rOutliner.SetText(*pPara);
785 else
787 rOutliner.SetTextObj( nullptr );
790 rOutliner.SetUpdateLayout(true);
791 rOutliner.SetControlWord(nStat0);
793 if( pText )
794 pText->CheckPortionInfo(rOutliner);
796 Point aTextPos(aAnkRect.TopLeft());
797 Size aTextSiz(rOutliner.GetPaperSize()); // GetPaperSize() adds a little tolerance, right?
799 // For draw objects containing text correct hor/ver alignment if text is bigger
800 // than the object itself. Without that correction, the text would always be
801 // formatted to the left edge (or top edge when vertical) of the draw object.
802 if(!IsTextFrame())
804 if(aAnkRect.GetWidth() < aTextSiz.Width() && !IsVerticalWriting())
806 // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
807 // else the alignment is wanted.
808 if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
810 eHAdj = SDRTEXTHORZADJUST_CENTER;
814 if(aAnkRect.GetHeight() < aTextSiz.Height() && IsVerticalWriting())
816 // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
817 // else the alignment is wanted.
818 if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
820 eVAdj = SDRTEXTVERTADJUST_CENTER;
825 if (eHAdj==SDRTEXTHORZADJUST_CENTER || eHAdj==SDRTEXTHORZADJUST_RIGHT)
827 tools::Long nFreeWdt=aAnkRect.GetWidth()-aTextSiz.Width();
828 if (eHAdj==SDRTEXTHORZADJUST_CENTER)
829 aTextPos.AdjustX(nFreeWdt/2 );
830 if (eHAdj==SDRTEXTHORZADJUST_RIGHT)
831 aTextPos.AdjustX(nFreeWdt );
833 if (eVAdj==SDRTEXTVERTADJUST_CENTER || eVAdj==SDRTEXTVERTADJUST_BOTTOM)
835 tools::Long nFreeHgt=aAnkRect.GetHeight()-aTextSiz.Height();
836 if (eVAdj==SDRTEXTVERTADJUST_CENTER)
837 aTextPos.AdjustY(nFreeHgt/2 );
838 if (eVAdj==SDRTEXTVERTADJUST_BOTTOM)
839 aTextPos.AdjustY(nFreeHgt );
841 if (maGeo.m_nRotationAngle)
842 RotatePoint(aTextPos,aAnkRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
844 if (pAnchorRect)
845 *pAnchorRect=aAnkRect;
847 // rTextRect might not be correct in some cases at ContourFrame
848 rTextRect=tools::Rectangle(aTextPos,aTextSiz);
849 if (bContourFrame)
850 rTextRect=aAnkRect;
853 bool SdrTextObj::CanCreateEditOutlinerParaObject() const
855 if( HasTextImpl( mpEditingOutliner ) )
857 return mpEditingOutliner->GetParagraphCount() > 0;
859 return false;
862 std::optional<OutlinerParaObject> SdrTextObj::CreateEditOutlinerParaObject() const
864 std::optional<OutlinerParaObject> pPara;
865 if( HasTextImpl( mpEditingOutliner ) )
867 sal_Int32 nParaCount = mpEditingOutliner->GetParagraphCount();
868 pPara = mpEditingOutliner->CreateParaObject(0, nParaCount);
870 return pPara;
873 void SdrTextObj::ImpSetCharStretching(SdrOutliner& rOutliner, const Size& rTextSize, const Size& rShapeSize, Fraction& rFitXCorrection)
875 OutputDevice* pOut = rOutliner.GetRefDevice();
876 bool bNoStretching(false);
878 if(pOut && pOut->GetOutDevType() == OUTDEV_PRINTER)
880 // check whether CharStretching is possible at all
881 GDIMetaFile* pMtf = pOut->GetConnectMetaFile();
882 OUString aTestString(u'J');
884 if(pMtf && (!pMtf->IsRecord() || pMtf->IsPause()))
885 pMtf = nullptr;
887 if(pMtf)
888 pMtf->Pause(true);
890 vcl::Font aOriginalFont(pOut->GetFont());
891 vcl::Font aTmpFont( OutputDevice::GetDefaultFont( DefaultFontType::SERIF, LANGUAGE_SYSTEM, GetDefaultFontFlags::OnlyOne ) );
893 aTmpFont.SetFontSize(Size(0,100));
894 pOut->SetFont(aTmpFont);
895 Size aSize1(pOut->GetTextWidth(aTestString), pOut->GetTextHeight());
896 aTmpFont.SetFontSize(Size(800,100));
897 pOut->SetFont(aTmpFont);
898 Size aSize2(pOut->GetTextWidth(aTestString), pOut->GetTextHeight());
899 pOut->SetFont(aOriginalFont);
901 if(pMtf)
902 pMtf->Pause(false);
904 bNoStretching = (aSize1 == aSize2);
906 #ifdef _WIN32
907 // Windows zooms the font proportionally when using Size(100,500),
908 // we don't like that.
909 if(aSize2.Height() >= aSize1.Height() * 2)
911 bNoStretching = true;
913 #endif
916 rOutliner.setRoundFontSizeToPt(false);
918 unsigned nLoopCount=0;
919 bool bNoMoreLoop = false;
920 tools::Long nXDiff0=0x7FFFFFFF;
921 tools::Long nWantWdt=rShapeSize.Width();
922 tools::Long nIsWdt=rTextSize.Width();
923 if (nIsWdt==0) nIsWdt=1;
925 tools::Long nWantHgt=rShapeSize.Height();
926 tools::Long nIsHgt=rTextSize.Height();
927 if (nIsHgt==0) nIsHgt=1;
929 tools::Long nXTolPl=nWantWdt/100; // tolerance: +1%
930 tools::Long nXTolMi=nWantWdt/25; // tolerance: -4%
931 tools::Long nXCorr =nWantWdt/20; // correction scale: 5%
933 double nX = (nWantWdt * 100.0) / double(nIsWdt); // calculate X stretching
934 double nY = (nWantHgt * 100.0) / double(nIsHgt); // calculate Y stretching
935 bool bChkX = true;
936 if (bNoStretching)
937 { // might only be possible proportionally
938 if (nX > nY)
940 nX = nY;
941 bChkX = false;
943 else
945 nY = nX;
949 while (nLoopCount<5 && !bNoMoreLoop)
951 if (nX < 0.0)
952 nX = -nX;
953 if (nX < 1.0)
955 nX = 1.0;
956 bNoMoreLoop = true;
958 if (nX > 65535.0)
960 nX = 65535.0;
961 bNoMoreLoop = true;
964 if (nY < 0.0)
966 nY = -nY;
968 if (nY < 1.0)
970 nY = 1.0;
971 bNoMoreLoop = true;
973 if (nY > 65535.0)
975 nY = 65535.0;
976 bNoMoreLoop = true;
979 // exception, there is no text yet (horizontal case)
980 if (nIsWdt <= 1)
982 nX = nY;
983 bNoMoreLoop = true;
986 // exception, there is no text yet (vertical case)
987 if (nIsHgt <= 1)
989 nY = nX;
990 bNoMoreLoop = true;
992 rOutliner.setScalingParameters({nX, nY});
993 nLoopCount++;
994 Size aSiz(rOutliner.CalcTextSize());
995 tools::Long nXDiff = aSiz.Width() - nWantWdt;
996 rFitXCorrection=Fraction(nWantWdt,aSiz.Width());
997 if (((nXDiff>=nXTolMi || !bChkX) && nXDiff<=nXTolPl) || nXDiff==nXDiff0) {
998 bNoMoreLoop = true;
999 } else {
1000 // correct stretching factors
1001 tools::Long nMul = nWantWdt;
1002 tools::Long nDiv = aSiz.Width();
1003 if (std::abs(nXDiff) <= 2 * nXCorr)
1005 if (nMul > nDiv)
1006 nDiv += (nMul - nDiv) / 2.0; // but only add half of what we calculated,
1007 else
1008 nMul += (nDiv - nMul) / 2.0;// because the EditEngine calculates wrongly later on
1010 nX = nX * double(nMul) / double(nDiv);
1011 if (bNoStretching)
1012 nY = nX;
1014 nXDiff0 = nXDiff;
1018 OUString SdrTextObj::TakeObjNameSingul() const
1020 OUString aStr;
1022 switch(meTextKind)
1024 case SdrObjKind::OutlineText:
1026 aStr = SvxResId(STR_ObjNameSingulOUTLINETEXT);
1027 break;
1030 case SdrObjKind::TitleText :
1032 aStr = SvxResId(STR_ObjNameSingulTITLETEXT);
1033 break;
1036 default:
1038 if(IsLinkedText())
1039 aStr = SvxResId(STR_ObjNameSingulTEXTLNK);
1040 else
1041 aStr = SvxResId(STR_ObjNameSingulTEXT);
1042 break;
1046 OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
1047 if(pOutlinerParaObject && meTextKind != SdrObjKind::OutlineText)
1049 // shouldn't currently cause any problems at OUTLINETEXT
1050 OUString aStr2(comphelper::string::stripStart(pOutlinerParaObject->GetTextObject().GetText(0), ' '));
1052 // avoid non expanded text portions in object name
1053 // (second condition is new)
1054 if(!aStr2.isEmpty() && aStr2.indexOf(u'\x00FF') == -1)
1056 // space between ResStr and content text
1057 aStr += " \'";
1059 if(aStr2.getLength() > 10)
1061 aStr2 = OUString::Concat(aStr2.subView(0, 8)) + "...";
1064 aStr += aStr2 + "\'";
1068 OUString sName(aStr);
1070 OUString aName(GetName());
1071 if (!aName.isEmpty())
1072 sName += " '" + aName + "'";
1074 return sName;
1077 OUString SdrTextObj::TakeObjNamePlural() const
1079 OUString sName;
1080 switch (meTextKind)
1082 case SdrObjKind::OutlineText: sName=SvxResId(STR_ObjNamePluralOUTLINETEXT); break;
1083 case SdrObjKind::TitleText : sName=SvxResId(STR_ObjNamePluralTITLETEXT); break;
1084 default: {
1085 if (IsLinkedText()) {
1086 sName=SvxResId(STR_ObjNamePluralTEXTLNK);
1087 } else {
1088 sName=SvxResId(STR_ObjNamePluralTEXT);
1090 } break;
1091 } // switch
1092 return sName;
1095 rtl::Reference<SdrObject> SdrTextObj::CloneSdrObject(SdrModel& rTargetModel) const
1097 return new SdrTextObj(rTargetModel, *this);
1100 basegfx::B2DPolyPolygon SdrTextObj::TakeXorPoly() const
1102 tools::Polygon aPol(getRectangle());
1103 if (maGeo.m_nShearAngle)
1104 ShearPoly(aPol, getRectangle().TopLeft(), maGeo.mfTanShearAngle);
1105 if (maGeo.m_nRotationAngle)
1106 RotatePoly(aPol, getRectangle().TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
1108 basegfx::B2DPolyPolygon aRetval;
1109 aRetval.append(aPol.getB2DPolygon());
1110 return aRetval;
1113 basegfx::B2DPolyPolygon SdrTextObj::TakeContour() const
1115 basegfx::B2DPolyPolygon aRetval(SdrAttrObj::TakeContour());
1117 // and now add the BoundRect of the text, if necessary
1118 if ( GetOutlinerParaObject() && !IsFontwork() && !IsContourTextFrame() )
1120 // using Clone()-Paint() strategy inside TakeContour() leaves a destroyed
1121 // SdrObject as pointer in DrawOutliner. Set *this again in fetching the outliner
1122 // in every case
1123 SdrOutliner& rOutliner=ImpGetDrawOutliner();
1125 tools::Rectangle aAnchor2;
1126 tools::Rectangle aR;
1127 TakeTextRect(rOutliner,aR,false,&aAnchor2);
1128 rOutliner.Clear();
1129 bool bFitToSize(IsFitToSize());
1130 if (bFitToSize) aR=aAnchor2;
1131 tools::Polygon aPol(aR);
1132 if (maGeo.m_nRotationAngle) RotatePoly(aPol,aR.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
1134 aRetval.append(aPol.getB2DPolygon());
1137 return aRetval;
1140 void SdrTextObj::RecalcSnapRect()
1142 if (maGeo.m_nRotationAngle || maGeo.m_nShearAngle)
1144 maSnapRect = Rect2Poly(getRectangle(), maGeo).GetBoundRect();
1145 } else {
1146 maSnapRect = getRectangle();
1150 sal_uInt32 SdrTextObj::GetSnapPointCount() const
1152 return 4;
1155 Point SdrTextObj::GetSnapPoint(sal_uInt32 i) const
1157 Point aP;
1158 auto aRectangle = getRectangle();
1159 switch (i) {
1160 case 0: aP = aRectangle.TopLeft(); break;
1161 case 1: aP = aRectangle.TopRight(); break;
1162 case 2: aP = aRectangle.BottomLeft(); break;
1163 case 3: aP = aRectangle.BottomRight(); break;
1164 default: aP = aRectangle.Center(); break;
1166 if (maGeo.m_nShearAngle)
1167 ShearPoint(aP, aRectangle.TopLeft(), maGeo.mfTanShearAngle);
1168 if (maGeo.m_nRotationAngle)
1169 RotatePoint(aP, aRectangle.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
1170 return aP;
1173 // Extracted from ImpGetDrawOutliner()
1174 void SdrTextObj::ImpInitDrawOutliner( SdrOutliner& rOutl ) const
1176 rOutl.SetUpdateLayout(false);
1177 OutlinerMode nOutlinerMode = OutlinerMode::OutlineObject;
1178 if ( !IsOutlText() )
1179 nOutlinerMode = OutlinerMode::TextObject;
1180 rOutl.Init( nOutlinerMode );
1182 rOutl.resetScalingParameters();
1184 EEControlBits nStat=rOutl.GetControlWord();
1185 nStat &= ~EEControlBits(EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE);
1186 rOutl.SetControlWord(nStat);
1187 Size aMaxSize(100000,100000);
1188 rOutl.SetMinAutoPaperSize(Size());
1189 rOutl.SetMaxAutoPaperSize(aMaxSize);
1190 rOutl.SetPaperSize(aMaxSize);
1191 rOutl.ClearPolygon();
1194 SdrOutliner& SdrTextObj::ImpGetDrawOutliner() const
1196 SdrOutliner& rOutl(getSdrModelFromSdrObject().GetDrawOutliner(this));
1198 // Code extracted to ImpInitDrawOutliner()
1199 ImpInitDrawOutliner( rOutl );
1201 return rOutl;
1204 // Extracted from Paint()
1205 void SdrTextObj::ImpSetupDrawOutlinerForPaint( bool bContourFrame,
1206 SdrOutliner& rOutliner,
1207 tools::Rectangle& rTextRect,
1208 tools::Rectangle& rAnchorRect,
1209 tools::Rectangle& rPaintRect,
1210 Fraction& rFitXCorrection ) const
1212 if (!bContourFrame)
1214 // FitToSize can't be used together with ContourFrame for now
1215 if (IsFitToSize() || IsAutoFit())
1217 EEControlBits nStat=rOutliner.GetControlWord();
1218 nStat|=EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE;
1219 rOutliner.SetControlWord(nStat);
1222 rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
1223 TakeTextRect(rOutliner, rTextRect, false, &rAnchorRect);
1224 rPaintRect = rTextRect;
1226 if (bContourFrame)
1227 return;
1229 // FitToSize can't be used together with ContourFrame for now
1230 if (IsFitToSize())
1232 ImpSetCharStretching(rOutliner,rTextRect.GetSize(),rAnchorRect.GetSize(),rFitXCorrection);
1233 rPaintRect=rAnchorRect;
1235 else if (IsAutoFit())
1237 setupAutoFitText(rOutliner);
1241 double SdrTextObj::GetFontScale() const
1243 SdrOutliner& rOutliner = ImpGetDrawOutliner();
1244 // This eventually calls setupAutoFitText
1245 UpdateOutlinerFormatting(rOutliner, o3tl::temporary(tools::Rectangle()));
1247 return rOutliner.getScalingParameters().fFontY;
1250 double SdrTextObj::GetSpacingScale() const
1252 SdrOutliner& rOutliner = ImpGetDrawOutliner();
1253 // This eventually calls setupAutoFitText
1254 UpdateOutlinerFormatting(rOutliner, o3tl::temporary(tools::Rectangle()));
1256 return rOutliner.getScalingParameters().fSpacingY;
1259 void SdrTextObj::setupAutoFitText(SdrOutliner& rOutliner) const
1261 const Size aShapeSize = GetSnapRect().GetSize();
1262 Size aSize(aShapeSize.Width() - GetTextLeftDistance() - GetTextRightDistance(),
1263 aShapeSize.Height() - GetTextUpperDistance() - GetTextLowerDistance());
1265 setupAutoFitText(rOutliner, aSize);
1267 void SdrTextObj::setupAutoFitText(SdrOutliner& rOutliner, const Size& rTextBoxSize) const
1269 rOutliner.setRoundFontSizeToPt(true); // We need to round the font size nearest integer pt size
1270 rOutliner.SetMaxAutoPaperSize(rTextBoxSize);
1271 rOutliner.SetPaperSize(rTextBoxSize);
1273 const SdrTextFitToSizeTypeItem& rItem = GetObjectItem(SDRATTR_TEXT_FITTOSIZE);
1275 double fFontScale = rItem.getFontScale();
1276 double fSpacingScale = rItem.getSpacingScale();
1278 if (fFontScale > 0.0 && fSpacingScale > 0.0 && !mbInEditMode)
1280 rOutliner.setScalingParameters({ fFontScale, fFontScale, 100.0, fSpacingScale });
1282 else
1284 rOutliner.resetScalingParameters();
1287 rOutliner.QuickFormatDoc();
1290 void SdrTextObj::SetupOutlinerFormatting( SdrOutliner& rOutl, tools::Rectangle& rPaintRect ) const
1292 ImpInitDrawOutliner( rOutl );
1293 UpdateOutlinerFormatting( rOutl, rPaintRect );
1296 void SdrTextObj::UpdateOutlinerFormatting( SdrOutliner& rOutl, tools::Rectangle& rPaintRect ) const
1298 tools::Rectangle aTextRect;
1299 tools::Rectangle aAnchorRect;
1300 Fraction aFitXCorrection(1,1);
1302 const bool bContourFrame(IsContourTextFrame());
1303 const MapMode aMapMode(getSdrModelFromSdrObject().GetScaleUnit());
1305 rOutl.SetRefMapMode(aMapMode);
1306 ImpSetupDrawOutlinerForPaint(
1307 bContourFrame,
1308 rOutl,
1309 aTextRect,
1310 aAnchorRect,
1311 rPaintRect,
1312 aFitXCorrection);
1316 OutlinerParaObject* SdrTextObj::GetOutlinerParaObject() const
1318 SdrText* pText = getActiveText();
1319 if( pText )
1320 return pText->GetOutlinerParaObject();
1321 else
1322 return nullptr;
1325 void SdrTextObj::NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject)
1327 NbcSetOutlinerParaObjectForText( std::move(pTextObject), getActiveText() );
1330 namespace
1332 bool IsAutoGrow(const SdrTextObj& rObj)
1334 bool bAutoGrow = rObj.IsAutoGrowHeight() || rObj.IsAutoGrowWidth();
1335 return bAutoGrow && !utl::ConfigManager::IsFuzzing();
1339 void SdrTextObj::NbcSetOutlinerParaObjectForText( std::optional<OutlinerParaObject> pTextObject, SdrText* pText )
1341 if( pText )
1342 pText->SetOutlinerParaObject( std::move(pTextObject) );
1344 if (pText && pText->GetOutlinerParaObject())
1346 SvxWritingModeItem aWritingMode(pText->GetOutlinerParaObject()->IsEffectivelyVertical() && pText->GetOutlinerParaObject()->IsTopToBottom()
1347 ? css::text::WritingMode_TB_RL
1348 : css::text::WritingMode_LR_TB,
1349 SDRATTR_TEXTDIRECTION);
1350 GetProperties().SetObjectItemDirect(aWritingMode);
1353 SetTextSizeDirty();
1354 if (IsTextFrame() && IsAutoGrow(*this))
1355 { // adapt text frame!
1356 NbcAdjustTextFrameWidthAndHeight();
1358 if (!IsTextFrame())
1360 // the SnapRect keeps its size
1361 SetBoundAndSnapRectsDirty(true);
1364 // always invalidate BoundRect on change
1365 SetBoundRectDirty();
1366 ActionChanged();
1368 ImpSetTextStyleSheetListeners();
1371 void SdrTextObj::NbcReformatText()
1373 SdrText* pText = getActiveText();
1374 if( !(pText && pText->GetOutlinerParaObject()) )
1375 return;
1377 pText->ReformatText();
1378 if (mbTextFrame)
1380 NbcAdjustTextFrameWidthAndHeight();
1382 else
1384 // the SnapRect keeps its size
1385 SetBoundRectDirty();
1386 SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
1388 SetTextSizeDirty();
1389 ActionChanged();
1390 // i22396
1391 // Necessary here since we have no compare operator at the outliner
1392 // para object which may detect changes regarding the combination
1393 // of outliner para data and configuration (e.g., change of
1394 // formatting of text numerals)
1395 GetViewContact().flushViewObjectContacts(false);
1398 std::unique_ptr<SdrObjGeoData> SdrTextObj::NewGeoData() const
1400 return std::make_unique<SdrTextObjGeoData>();
1403 void SdrTextObj::SaveGeoData(SdrObjGeoData& rGeo) const
1405 SdrAttrObj::SaveGeoData(rGeo);
1406 SdrTextObjGeoData& rTGeo=static_cast<SdrTextObjGeoData&>(rGeo);
1407 rTGeo.maRect = getRectangle();
1408 rTGeo.maGeo = maGeo;
1411 void SdrTextObj::RestoreGeoData(const SdrObjGeoData& rGeo)
1412 { // RectsDirty is called by SdrObject
1413 SdrAttrObj::RestoreGeoData(rGeo);
1414 const SdrTextObjGeoData& rTGeo=static_cast<const SdrTextObjGeoData&>(rGeo);
1415 NbcSetLogicRect(rTGeo.maRect);
1416 maGeo = rTGeo.maGeo;
1417 SetTextSizeDirty();
1420 drawing::TextFitToSizeType SdrTextObj::GetFitToSize() const
1422 drawing::TextFitToSizeType eType = drawing::TextFitToSizeType_NONE;
1424 if(!IsAutoGrowWidth())
1425 eType = GetObjectItem(SDRATTR_TEXT_FITTOSIZE).GetValue();
1427 return eType;
1430 const tools::Rectangle& SdrTextObj::GetGeoRect() const
1432 return getRectangle();
1435 void SdrTextObj::ForceOutlinerParaObject()
1437 SdrText* pText = getActiveText();
1438 if( pText && (pText->GetOutlinerParaObject() == nullptr) )
1440 OutlinerMode nOutlMode = OutlinerMode::TextObject;
1441 if( IsTextFrame() && meTextKind == SdrObjKind::OutlineText )
1442 nOutlMode = OutlinerMode::OutlineObject;
1444 pText->ForceOutlinerParaObject( nOutlMode );
1448 TextChain *SdrTextObj::GetTextChain() const
1450 //if (!IsChainable())
1451 // return NULL;
1453 return getSdrModelFromSdrObject().GetTextChain();
1456 bool SdrTextObj::IsVerticalWriting() const
1458 if(mpEditingOutliner)
1460 return mpEditingOutliner->IsVertical();
1463 OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
1464 if(pOutlinerParaObject)
1466 return pOutlinerParaObject->IsEffectivelyVertical();
1469 return false;
1472 void SdrTextObj::SetVerticalWriting(bool bVertical)
1474 OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
1476 if( !pOutlinerParaObject && bVertical )
1478 // we only need to force an outliner para object if the default of
1479 // horizontal text is changed
1480 ForceOutlinerParaObject();
1481 pOutlinerParaObject = GetOutlinerParaObject();
1484 if (!pOutlinerParaObject ||
1485 (pOutlinerParaObject->IsEffectivelyVertical() == bVertical))
1486 return;
1488 // get item settings
1489 const SfxItemSet& rSet = GetObjectItemSet();
1490 bool bAutoGrowWidth = rSet.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue();
1491 bool bAutoGrowHeight = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
1493 // Also exchange hor/ver adjust items
1494 SdrTextHorzAdjust eHorz = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue();
1495 SdrTextVertAdjust eVert = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
1497 // rescue object size
1498 tools::Rectangle aObjectRect = GetSnapRect();
1500 // prepare ItemSet to set exchanged width and height items
1501 SfxItemSetFixed<SDRATTR_TEXT_AUTOGROWHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
1502 // Expanded item ranges to also support hor and ver adjust.
1503 SDRATTR_TEXT_VERTADJUST, SDRATTR_TEXT_VERTADJUST,
1504 SDRATTR_TEXT_AUTOGROWWIDTH, SDRATTR_TEXT_HORZADJUST> aNewSet(*rSet.GetPool());
1506 aNewSet.Put(rSet);
1507 aNewSet.Put(makeSdrTextAutoGrowWidthItem(bAutoGrowHeight));
1508 aNewSet.Put(makeSdrTextAutoGrowHeightItem(bAutoGrowWidth));
1510 // Exchange horz and vert adjusts
1511 switch (eVert)
1513 case SDRTEXTVERTADJUST_TOP: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); break;
1514 case SDRTEXTVERTADJUST_CENTER: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_CENTER)); break;
1515 case SDRTEXTVERTADJUST_BOTTOM: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT)); break;
1516 case SDRTEXTVERTADJUST_BLOCK: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_BLOCK)); break;
1518 switch (eHorz)
1520 case SDRTEXTHORZADJUST_LEFT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BOTTOM)); break;
1521 case SDRTEXTHORZADJUST_CENTER: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); break;
1522 case SDRTEXTHORZADJUST_RIGHT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP)); break;
1523 case SDRTEXTHORZADJUST_BLOCK: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BLOCK)); break;
1526 SetObjectItemSet(aNewSet);
1528 pOutlinerParaObject = GetOutlinerParaObject();
1529 if (pOutlinerParaObject)
1531 // set ParaObject orientation accordingly
1532 pOutlinerParaObject->SetVertical(bVertical);
1535 // restore object size
1536 SetSnapRect(aObjectRect);
1539 bool SdrTextObj::IsTopToBottom() const
1541 if (mpEditingOutliner)
1542 return mpEditingOutliner->IsTopToBottom();
1544 if (OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject())
1545 return pOutlinerParaObject->IsTopToBottom();
1547 return false;
1550 // transformation interface for StarOfficeAPI. This implements support for
1551 // homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
1552 // moment it contains a shearX, rotation and translation, but for setting all linear
1553 // transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
1556 // gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
1557 // with the base geometry and returns TRUE. Otherwise it returns FALSE.
1558 bool SdrTextObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
1560 // get turn and shear
1561 double fRotate = toRadians(maGeo.m_nRotationAngle);
1562 double fShearX = toRadians(maGeo.m_nShearAngle);
1564 // get aRect, this is the unrotated snaprect
1565 tools::Rectangle aRectangle(getRectangle());
1567 // fill other values
1568 basegfx::B2DTuple aScale(aRectangle.GetWidth(), aRectangle.GetHeight());
1569 basegfx::B2DTuple aTranslate(aRectangle.Left(), aRectangle.Top());
1571 // position maybe relative to anchorpos, convert
1572 if( getSdrModelFromSdrObject().IsWriter() )
1574 if(GetAnchorPos().X() || GetAnchorPos().Y())
1576 aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
1580 // build matrix
1581 rMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
1582 aScale,
1583 basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
1584 basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate,
1585 aTranslate);
1587 return false;
1590 // sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
1591 // If it's an SdrPathObj it will use the provided geometry information. The Polygon has
1592 // to use (0,0) as upper left and will be scaled to the given size in the matrix.
1593 void SdrTextObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
1595 // break up matrix
1596 basegfx::B2DTuple aScale;
1597 basegfx::B2DTuple aTranslate;
1598 double fRotate(0.0);
1599 double fShearX(0.0);
1600 rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
1602 // flip?
1603 bool bFlipX = aScale.getX() < 0.0,
1604 bFlipY = aScale.getY() < 0.0;
1605 if (bFlipX)
1607 aScale.setX(fabs(aScale.getX()));
1609 if (bFlipY)
1611 aScale.setY(fabs(aScale.getY()));
1614 // reset object shear and rotations
1615 maGeo.m_nRotationAngle = 0_deg100;
1616 maGeo.RecalcSinCos();
1617 maGeo.m_nShearAngle = 0_deg100;
1618 maGeo.RecalcTan();
1620 // if anchor is used, make position relative to it
1621 if( getSdrModelFromSdrObject().IsWriter() )
1623 if(GetAnchorPos().X() || GetAnchorPos().Y())
1625 aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
1629 // build and set BaseRect (use scale)
1630 Size aSize(FRound(aScale.getX()), FRound(aScale.getY()));
1631 tools::Rectangle aBaseRect(Point(), aSize);
1632 SetSnapRect(aBaseRect);
1634 // flip?
1635 if (bFlipX)
1637 Mirror(Point(), Point(0, 1));
1639 if (bFlipY)
1641 Mirror(Point(), Point(1, 0));
1644 // shear?
1645 if(!basegfx::fTools::equalZero(fShearX))
1647 GeoStat aGeoStat;
1648 aGeoStat.m_nShearAngle = Degree100(FRound(basegfx::rad2deg<100>(atan(fShearX))));
1649 aGeoStat.RecalcTan();
1650 Shear(Point(), aGeoStat.m_nShearAngle, aGeoStat.mfTanShearAngle, false);
1653 // rotation?
1654 if(!basegfx::fTools::equalZero(fRotate))
1656 GeoStat aGeoStat;
1658 // #i78696#
1659 // fRotate is matematically correct, but aGeoStat.nRotationAngle is
1660 // mirrored -> mirror value here
1661 aGeoStat.m_nRotationAngle = NormAngle36000(Degree100(FRound(-basegfx::rad2deg<100>(fRotate))));
1662 aGeoStat.RecalcSinCos();
1663 Rotate(Point(), aGeoStat.m_nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
1666 // translate?
1667 if(!aTranslate.equalZero())
1669 Move(Size(FRound(aTranslate.getX()), FRound(aTranslate.getY())));
1673 bool SdrTextObj::IsReallyEdited() const
1675 return mpEditingOutliner && mpEditingOutliner->IsModified();
1678 // moved inlines here form hxx
1680 tools::Long SdrTextObj::GetEckenradius() const
1682 return GetObjectItemSet().Get(SDRATTR_CORNER_RADIUS).GetValue();
1685 tools::Long SdrTextObj::GetMinTextFrameHeight() const
1687 return GetObjectItemSet().Get(SDRATTR_TEXT_MINFRAMEHEIGHT).GetValue();
1690 tools::Long SdrTextObj::GetMaxTextFrameHeight() const
1692 return GetObjectItemSet().Get(SDRATTR_TEXT_MAXFRAMEHEIGHT).GetValue();
1695 tools::Long SdrTextObj::GetMinTextFrameWidth() const
1697 return GetObjectItemSet().Get(SDRATTR_TEXT_MINFRAMEWIDTH).GetValue();
1700 tools::Long SdrTextObj::GetMaxTextFrameWidth() const
1702 return GetObjectItemSet().Get(SDRATTR_TEXT_MAXFRAMEWIDTH).GetValue();
1705 bool SdrTextObj::IsFontwork() const
1707 return !mbTextFrame // Default is FALSE
1708 && GetObjectItemSet().Get(XATTR_FORMTXTSTYLE).GetValue() != XFormTextStyle::NONE;
1711 bool SdrTextObj::IsHideContour() const
1713 return !mbTextFrame // Default is: no, don't HideContour; HideContour not together with TextFrames
1714 && GetObjectItemSet().Get(XATTR_FORMTXTHIDEFORM).GetValue();
1717 bool SdrTextObj::IsContourTextFrame() const
1719 return !mbTextFrame // ContourFrame not together with normal TextFrames
1720 && GetObjectItemSet().Get(SDRATTR_TEXT_CONTOURFRAME).GetValue();
1723 tools::Long SdrTextObj::GetTextLeftDistance() const
1725 return GetObjectItemSet().Get(SDRATTR_TEXT_LEFTDIST).GetValue();
1728 tools::Long SdrTextObj::GetTextRightDistance() const
1730 return GetObjectItemSet().Get(SDRATTR_TEXT_RIGHTDIST).GetValue();
1733 tools::Long SdrTextObj::GetTextUpperDistance() const
1735 return GetObjectItemSet().Get(SDRATTR_TEXT_UPPERDIST).GetValue();
1738 tools::Long SdrTextObj::GetTextLowerDistance() const
1740 return GetObjectItemSet().Get(SDRATTR_TEXT_LOWERDIST).GetValue();
1743 SdrTextAniKind SdrTextObj::GetTextAniKind() const
1745 return GetObjectItemSet().Get(SDRATTR_TEXT_ANIKIND).GetValue();
1748 SdrTextAniDirection SdrTextObj::GetTextAniDirection() const
1750 return GetObjectItemSet().Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
1753 bool SdrTextObj::HasTextColumnsNumber() const
1755 return GetObjectItemSet().HasItem(SDRATTR_TEXTCOLUMNS_NUMBER);
1758 sal_Int16 SdrTextObj::GetTextColumnsNumber() const
1760 return GetObjectItemSet().Get(SDRATTR_TEXTCOLUMNS_NUMBER).GetValue();
1763 void SdrTextObj::SetTextColumnsNumber(sal_Int16 nColumns)
1765 SetObjectItem(SfxInt16Item(SDRATTR_TEXTCOLUMNS_NUMBER, nColumns));
1768 bool SdrTextObj::HasTextColumnsSpacing() const
1770 return GetObjectItemSet().HasItem(SDRATTR_TEXTCOLUMNS_SPACING);
1773 sal_Int32 SdrTextObj::GetTextColumnsSpacing() const
1775 return GetObjectItemSet().Get(SDRATTR_TEXTCOLUMNS_SPACING).GetValue();
1778 void SdrTextObj::SetTextColumnsSpacing(sal_Int32 nSpacing)
1780 SetObjectItem(SdrMetricItem(SDRATTR_TEXTCOLUMNS_SPACING, nSpacing));
1783 // Get necessary data for text scroll animation. ATM base it on a Text-Metafile and a
1784 // painting rectangle. Rotation is excluded from the returned values.
1785 GDIMetaFile* SdrTextObj::GetTextScrollMetaFileAndRectangle(
1786 tools::Rectangle& rScrollRectangle, tools::Rectangle& rPaintRectangle)
1788 GDIMetaFile* pRetval = nullptr;
1789 SdrOutliner& rOutliner = ImpGetDrawOutliner();
1790 tools::Rectangle aTextRect;
1791 tools::Rectangle aAnchorRect;
1792 tools::Rectangle aPaintRect;
1793 Fraction aFitXCorrection(1,1);
1794 bool bContourFrame(IsContourTextFrame());
1796 // get outliner set up. To avoid getting a somehow rotated MetaFile,
1797 // temporarily disable object rotation.
1798 Degree100 nAngle(maGeo.m_nRotationAngle);
1799 maGeo.m_nRotationAngle = 0_deg100;
1800 ImpSetupDrawOutlinerForPaint( bContourFrame, rOutliner, aTextRect, aAnchorRect, aPaintRect, aFitXCorrection );
1801 maGeo.m_nRotationAngle = nAngle;
1803 tools::Rectangle aScrollFrameRect(aPaintRect);
1804 const SfxItemSet& rSet = GetObjectItemSet();
1805 SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
1807 if(SdrTextAniDirection::Left == eDirection || SdrTextAniDirection::Right == eDirection)
1809 aScrollFrameRect.SetLeft( aAnchorRect.Left() );
1810 aScrollFrameRect.SetRight( aAnchorRect.Right() );
1813 if(SdrTextAniDirection::Up == eDirection || SdrTextAniDirection::Down == eDirection)
1815 aScrollFrameRect.SetTop( aAnchorRect.Top() );
1816 aScrollFrameRect.SetBottom( aAnchorRect.Bottom() );
1819 // create the MetaFile
1820 pRetval = new GDIMetaFile;
1821 ScopedVclPtrInstance< VirtualDevice > pBlackHole;
1822 pBlackHole->EnableOutput(false);
1823 pRetval->Record(pBlackHole);
1824 Point aPaintPos = aPaintRect.TopLeft();
1826 rOutliner.Draw(*pBlackHole, aPaintPos);
1828 pRetval->Stop();
1829 pRetval->WindStart();
1831 // return PaintRectanglePixel and pRetval;
1832 rScrollRectangle = aScrollFrameRect;
1833 rPaintRectangle = aPaintRect;
1835 return pRetval;
1838 // Access to TextAnimationAllowed flag
1839 bool SdrTextObj::IsAutoFit() const
1841 return GetFitToSize() == drawing::TextFitToSizeType_AUTOFIT;
1844 bool SdrTextObj::IsFitToSize() const
1846 const drawing::TextFitToSizeType eFit = GetFitToSize();
1847 return (eFit == drawing::TextFitToSizeType_PROPORTIONAL
1848 || eFit == drawing::TextFitToSizeType_ALLLINES);
1851 void SdrTextObj::SetTextAnimationAllowed(bool bNew)
1853 if(mbTextAnimationAllowed != bNew)
1855 mbTextAnimationAllowed = bNew;
1856 ActionChanged();
1860 /** called from the SdrObjEditView during text edit when the status of the edit outliner changes */
1861 void SdrTextObj::onEditOutlinerStatusEvent( EditStatus* pEditStatus )
1863 const EditStatusFlags nStat = pEditStatus->GetStatusWord();
1864 const bool bGrowX = bool(nStat & EditStatusFlags::TEXTWIDTHCHANGED);
1865 const bool bGrowY = bool(nStat & EditStatusFlags::TextHeightChanged);
1866 if(!(mbTextFrame && (bGrowX || bGrowY)))
1867 return;
1869 if ((bGrowX && IsAutoGrowWidth()) || (bGrowY && IsAutoGrowHeight()))
1871 AdjustTextFrameWidthAndHeight();
1873 else if ( (IsAutoFit() || IsFitToSize()) && !mbInDownScale)
1875 assert(mpEditingOutliner);
1876 mbInDownScale = true;
1878 // Need to reset scaling so it searches for the fitting size again
1879 mpEditingOutliner->resetScalingParameters();
1881 // sucks that we cannot disable paints via
1882 // mpEditingOutliner->SetUpdateMode(FALSE) - but EditEngine skips
1883 // formatting as well, then.
1884 setupAutoFitText(*mpEditingOutliner);
1885 mbInDownScale = false;
1889 /* Begin chaining code */
1891 // XXX: Make it a method somewhere?
1892 static SdrObject *ImpGetObjByName(SdrObjList const *pObjList, std::u16string_view aObjName)
1894 // scan the whole list
1895 for (const rtl::Reference<SdrObject>& pCurObj : *pObjList)
1896 if (pCurObj->GetName() == aObjName)
1897 return pCurObj.get();
1898 // not found
1899 return nullptr;
1902 // XXX: Make it a (private) method of SdrTextObj
1903 static void ImpUpdateChainLinks(SdrTextObj *pTextObj, std::u16string_view aNextLinkName)
1905 // XXX: Current implementation constraints text boxes to be on the same page
1907 // No next link
1908 if (aNextLinkName.empty()) {
1909 pTextObj->SetNextLinkInChain(nullptr);
1910 return;
1913 SdrPage *pPage(pTextObj->getSdrPageFromSdrObject());
1914 assert(pPage);
1915 SdrTextObj *pNextTextObj = DynCastSdrTextObj
1916 (ImpGetObjByName(pPage, aNextLinkName));
1917 if (!pNextTextObj) {
1918 SAL_INFO("svx.chaining", "[CHAINING] Can't find object as next link.");
1919 return;
1922 pTextObj->SetNextLinkInChain(pNextTextObj);
1925 bool SdrTextObj::IsChainable() const
1927 // Read it as item
1928 const SfxItemSet& rSet = GetObjectItemSet();
1929 OUString aNextLinkName = rSet.Get(SDRATTR_TEXT_CHAINNEXTNAME).GetValue();
1931 // Update links if any inconsistency is found
1932 bool bNextLinkUnsetYet = !aNextLinkName.isEmpty() && !mpNextInChain;
1933 bool bInconsistentNextLink = mpNextInChain && mpNextInChain->GetName() != aNextLinkName;
1934 // if the link is not set despite there should be one OR if it has changed
1935 if (bNextLinkUnsetYet || bInconsistentNextLink) {
1936 ImpUpdateChainLinks(const_cast<SdrTextObj *>(this), aNextLinkName);
1939 return !aNextLinkName.isEmpty(); // XXX: Should we also check for GetNilChainingEvent? (see old code below)
1942 // Check that no overflow is going on
1943 if (!GetTextChain() || GetTextChain()->GetNilChainingEvent(this))
1944 return false;
1948 void SdrTextObj::onChainingEvent()
1950 if (!mpEditingOutliner)
1951 return;
1953 // Outliner for text transfer
1954 SdrOutliner &aDrawOutliner = ImpGetDrawOutliner();
1956 EditingTextChainFlow aTxtChainFlow(this);
1957 aTxtChainFlow.CheckForFlowEvents(mpEditingOutliner);
1959 if (aTxtChainFlow.IsOverflow()) {
1960 SAL_INFO("svx.chaining", "[CHAINING] Overflow going on");
1961 // One outliner is for non-overflowing text, the other for overflowing text
1962 // We remove text directly from the editing outliner
1963 aTxtChainFlow.ExecuteOverflow(mpEditingOutliner, &aDrawOutliner);
1964 } else if (aTxtChainFlow.IsUnderflow()) {
1965 SAL_INFO("svx.chaining", "[CHAINING] Underflow going on");
1966 // underflow-induced overflow
1967 aTxtChainFlow.ExecuteUnderflow(&aDrawOutliner);
1968 bool bIsOverflowFromUnderflow = aTxtChainFlow.IsOverflow();
1969 // handle overflow
1970 if (bIsOverflowFromUnderflow) {
1971 SAL_INFO("svx.chaining", "[CHAINING] Overflow going on (underflow induced)");
1972 // prevents infinite loops when setting text for editing outliner
1973 aTxtChainFlow.ExecuteOverflow(&aDrawOutliner, &aDrawOutliner);
1978 SdrTextObj* SdrTextObj::GetNextLinkInChain() const
1981 if (GetTextChain())
1982 return GetTextChain()->GetNextLink(this);
1984 return NULL;
1987 return mpNextInChain;
1990 void SdrTextObj::SetNextLinkInChain(SdrTextObj *pNextObj)
1992 // Basically a doubly linked list implementation
1994 SdrTextObj *pOldNextObj = mpNextInChain;
1996 // Replace next link
1997 mpNextInChain = pNextObj;
1998 // Deal with old next link's prev link
1999 if (pOldNextObj) {
2000 pOldNextObj->mpPrevInChain = nullptr;
2003 // Deal with new next link's prev link
2004 if (mpNextInChain) {
2005 // If there is a prev already at all and this is not already the current object
2006 if (mpNextInChain->mpPrevInChain &&
2007 mpNextInChain->mpPrevInChain != this)
2008 mpNextInChain->mpPrevInChain->mpNextInChain = nullptr;
2009 mpNextInChain->mpPrevInChain = this;
2012 // TODO: Introduce check for circular chains
2016 SdrTextObj* SdrTextObj::GetPrevLinkInChain() const
2019 if (GetTextChain())
2020 return GetTextChain()->GetPrevLink(this);
2022 return NULL;
2025 return mpPrevInChain;
2028 bool SdrTextObj::GetPreventChainable() const
2030 // Prevent chaining it 1) during dragging && 2) when we are editing next link
2031 return mbIsUnchainableClone || (GetNextLinkInChain() && GetNextLinkInChain()->IsInEditMode());
2034 rtl::Reference<SdrObject> SdrTextObj::getFullDragClone() const
2036 rtl::Reference<SdrObject> pClone = SdrAttrObj::getFullDragClone();
2037 SdrTextObj *pTextObjClone = DynCastSdrTextObj(pClone.get());
2038 if (pTextObjClone != nullptr) {
2039 // Avoid transferring of text for chainable object during dragging
2040 pTextObjClone->mbIsUnchainableClone = true;
2043 return pClone;
2046 /* End chaining code */
2048 /** returns the currently active text. */
2049 SdrText* SdrTextObj::getActiveText() const
2051 if( !mxText )
2052 return getText( 0 );
2053 else
2054 return mxText.get();
2057 /** returns the nth available text. */
2058 SdrText* SdrTextObj::getText( sal_Int32 nIndex ) const
2060 if( nIndex == 0 )
2062 if( !mxText )
2063 const_cast< SdrTextObj* >(this)->mxText = new SdrText( *const_cast< SdrTextObj* >(this) );
2064 return mxText.get();
2066 else
2068 return nullptr;
2072 /** returns the number of texts available for this object. */
2073 sal_Int32 SdrTextObj::getTextCount() const
2075 return 1;
2078 /** changes the current active text */
2079 void SdrTextObj::setActiveText( sal_Int32 /*nIndex*/ )
2083 /** returns the index of the text that contains the given point or -1 */
2084 sal_Int32 SdrTextObj::CheckTextHit(const Point& /*rPnt*/) const
2086 return 0;
2089 void SdrTextObj::SetObjectItemNoBroadcast(const SfxPoolItem& rItem)
2091 static_cast< sdr::properties::TextProperties& >(GetProperties()).SetObjectItemNoBroadcast(rItem);
2095 // The concept of the text object:
2096 // ~~~~~~~~~~~~~~~~~~~~~~~~
2097 // Attributes/Variations:
2098 // - bool text frame / graphics object with caption
2099 // - bool FontWork (if it is not a text frame and not a ContourTextFrame)
2100 // - bool ContourTextFrame (if it is not a text frame and not Fontwork)
2101 // - long rotation angle (if it is not FontWork)
2102 // - long text frame margins (if it is not FontWork)
2103 // - bool FitToSize (if it is not FontWork)
2104 // - bool AutoGrowingWidth/Height (if it is not FitToSize and not FontWork)
2105 // - long Min/MaxFrameWidth/Height (if AutoGrowingWidth/Height)
2106 // - enum horizontal text anchoring left,center,right,justify/block,Stretch(ni)
2107 // - enum vertical text anchoring top, middle, bottom, block, stretch(ni)
2108 // - enum ticker text (if it is not FontWork)
2110 // Every derived object is either a text frame (mbTextFrame=true)
2111 // or a drawing object with a caption (mbTextFrame=false).
2113 // Default anchoring for text frames:
2114 // SDRTEXTHORZADJUST_BLOCK, SDRTEXTVERTADJUST_TOP
2115 // = static Pool defaults
2116 // Default anchoring for drawing objects with a caption:
2117 // SDRTEXTHORZADJUST_CENTER, SDRTEXTVERTADJUST_CENTER
2118 // via "hard" attribution of SdrAttrObj
2120 // Every object derived from SdrTextObj must return an "UnrotatedSnapRect"
2121 // (->TakeUnrotatedSnapRect()) (the reference point for the rotation is the top
2122 // left of the rectangle (maGeo.nRotationAngle)) which is the basis for anchoring
2123 // text. We then subtract the text frame margins from this rectangle, as a re-
2124 // sult we get the anchoring area (->TakeTextAnchorRect()). Within this area, we
2125 // calculate the anchoring point and the painting area, depending on the hori-
2126 // zontal and vertical adjustment of the text (SdrTextVertAdjust,
2127 // SdrTextHorzAdjust).
2128 // In the case of drawing objects with a caption the painting area might well
2129 // be larger than the anchoring area, for text frames on the other hand, it is
2130 // always of the same or a smaller size (except when there are negative text
2131 // frame margins).
2133 // FitToSize takes priority over text anchoring and AutoGrowHeight/Width. When
2134 // FitToSize is turned on, the painting area is always equal to the anchoring
2135 // area. Additionally, FitToSize doesn't allow automatic line breaks.
2137 // ContourTextFrame:
2138 // - long rotation angle
2139 // - long text frame margins (maybe later)
2140 // - bool FitToSize (maybe later)
2141 // - bool AutoGrowingWidth/Height (maybe much later)
2142 // - long Min/MaxFrameWidth/Height (maybe much later)
2143 // - enum horizontal text anchoring (maybe later, for now: left, centered)
2144 // - enum vertical text anchoring (maybe later, for now: top)
2145 // - enum ticker text (maybe later, maybe even with correct clipping)
2147 // When making changes, check these:
2148 // - Paint
2149 // - HitTest
2150 // - ConvertToPoly
2151 // - Edit
2152 // - Printing, Saving, Painting in neighboring View while editing
2153 // - ModelChanged (e. g. through a neighboring View or rulers) while editing
2154 // - FillColorChanged while editing
2155 // - and many more...
2158 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */