1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20
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 <sdr/properties/textproperties.hxx>
47 #include <sdr/contact/viewcontactoftextobj.hxx>
48 #include <basegfx/tuple/b2dtuple.hxx>
49 #include <basegfx/matrix/b2dhommatrix.hxx>
50 #include <vcl/gdimtf.hxx>
51 #include <vcl/virdev.hxx>
52 #include <basegfx/matrix/b2dhommatrixtools.hxx>
53 #include <sal/log.hxx>
54 #include <o3tl/unit_conversion.hxx>
55 #include <o3tl/temporary.hxx>
56 #include <comphelper/configuration.hxx>
57 #include <editeng/eeitem.hxx>
58 #include <editeng/fhgtitem.hxx>
59
60 using namespace com::sun::star;
61
62 // BaseProperties section
CreateObjectSpecificProperties()63 std::unique_ptr<sdr::properties::BaseProperties> SdrTextObj::CreateObjectSpecificProperties()
64 {
65 return std::make_unique<sdr::properties::TextProperties>(*this);
66 }
67
68 // DrawContact section
CreateObjectSpecificViewContact()69 std::unique_ptr<sdr::contact::ViewContact> SdrTextObj::CreateObjectSpecificViewContact()
70 {
71 return std::make_unique<sdr::contact::ViewContactOfTextObj>(*this);
72 }
73
SdrTextObj(SdrModel & rSdrModel,const tools::Rectangle & rRectangle,std::optional<SdrObjKind> oeTextKind)74 SdrTextObj::SdrTextObj(SdrModel& rSdrModel, const tools::Rectangle& rRectangle, std::optional<SdrObjKind> oeTextKind)
75 : SdrAttrObj(rSdrModel)
76 , mpEditingOutliner(nullptr)
77 , meTextKind(oeTextKind ? *oeTextKind : SdrObjKind::Text)
78 , maTextEditOffset(Point())
79 , mbTextFrame(false)
80 , mbNoShear(false)
81 , mbTextSizeDirty(false)
82 , mbInEditMode(false)
83 , mbDisableAutoWidthOnDragging(false)
84 , mbTextAnimationAllowed(true)
85 , mbInDownScale(false)
86 {
87 if (!rRectangle.IsEmpty())
88 {
89 tools::Rectangle aRectangle(rRectangle);
90 ImpJustifyRect(aRectangle);
91 setRectangle(aRectangle);
92 }
93
94 if (oeTextKind)
95 {
96 mbTextFrame = true;
97 mbNoShear = true;
98 }
99
100 mbSupportTextIndentingOnLineWidthChange = true;
101 }
102
SdrTextObj(SdrModel & rSdrModel,SdrTextObj const & rSource)103 SdrTextObj::SdrTextObj(SdrModel& rSdrModel, SdrTextObj const & rSource)
104 : SdrAttrObj(rSdrModel, rSource)
105 , mpEditingOutliner(nullptr)
106 , meTextKind(rSource.meTextKind)
107 , maTextEditOffset(Point(0, 0))
108 , mbTextFrame(rSource.mbTextFrame)
109 , mbNoShear(rSource.mbNoShear)
110 , mbTextSizeDirty(rSource.mbTextSizeDirty)
111 , mbInEditMode(false)
112 , mbDisableAutoWidthOnDragging(rSource.mbDisableAutoWidthOnDragging)
113 , mbTextAnimationAllowed(true)
114 , mbInDownScale(false)
115 {
116 // #i25616#
117 mbSupportTextIndentingOnLineWidthChange = true;
118
119 maRectangle = rSource.maRectangle;
120 maGeo = rSource.maGeo;
121 maTextSize = rSource.maTextSize;
122
123 // Not all of the necessary parameters were copied yet.
124 SdrText* pText = getActiveText();
125
126 if( pText && rSource.HasText() )
127 {
128 // before pNewOutlinerParaObject was created the same, but
129 // set at mpText (outside this scope), but mpText might be
130 // empty (this operator== seems not prepared for MultiText
131 // objects). In the current form it makes only sense to
132 // create locally and use locally on a known existing SdrText
133 const Outliner* pEO = rSource.mpEditingOutliner;
134 std::optional<OutlinerParaObject> pNewOutlinerParaObject;
135
136 if (pEO!=nullptr)
137 {
138 pNewOutlinerParaObject = pEO->CreateParaObject();
139 }
140 else if (nullptr != rSource.getActiveText()->GetOutlinerParaObject())
141 {
142 pNewOutlinerParaObject = *rSource.getActiveText()->GetOutlinerParaObject();
143 }
144
145 pText->SetOutlinerParaObject( std::move(pNewOutlinerParaObject) );
146 }
147
148 ImpSetTextStyleSheetListeners();
149 }
150
~SdrTextObj()151 SdrTextObj::~SdrTextObj()
152 {
153 mxText.clear();
154 ImpDeregisterLink();
155 }
156
FitFrameToTextSize()157 void SdrTextObj::FitFrameToTextSize()
158 {
159 ImpJustifyRect(maRectangle);
160
161 SdrText* pText = getActiveText();
162 if(pText==nullptr || !pText->GetOutlinerParaObject())
163 return;
164
165 SdrOutliner& rOutliner=ImpGetDrawOutliner();
166 rOutliner.SetPaperSize(Size(getRectangle().Right() - getRectangle().Left(), getRectangle().Bottom() - getRectangle().Top()));
167 rOutliner.SetUpdateLayout(true);
168 rOutliner.SetText(*pText->GetOutlinerParaObject());
169 Size aNewSize(rOutliner.CalcTextSize());
170 rOutliner.Clear();
171 aNewSize.AdjustWidth( 1 ); // because of possible rounding errors
172 aNewSize.AdjustWidth(GetTextLeftDistance()+GetTextRightDistance() );
173 aNewSize.AdjustHeight(GetTextUpperDistance()+GetTextLowerDistance() );
174 tools::Rectangle aNewRect(getRectangle());
175 aNewRect.SetSize(aNewSize);
176 ImpJustifyRect(aNewRect);
177
178 if (aNewRect != getRectangle())
179 SetLogicRect(aNewRect);
180 }
181
NbcSetText(const OUString & rStr)182 void SdrTextObj::NbcSetText(const OUString& rStr)
183 {
184 SdrOutliner& rOutliner=ImpGetDrawOutliner();
185 rOutliner.SetStyleSheet( 0, GetStyleSheet());
186 rOutliner.SetText(rStr,rOutliner.GetParagraph( 0 ));
187 std::optional<OutlinerParaObject> pNewText=rOutliner.CreateParaObject();
188 NbcSetOutlinerParaObject(std::move(pNewText));
189 mbTextSizeDirty=true;
190 }
191
SetText(const OUString & rStr)192 void SdrTextObj::SetText(const OUString& rStr)
193 {
194 tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
195 NbcSetText(rStr);
196 SetChanged();
197 BroadcastObjectChange();
198 SendUserCall(SdrUserCallType::Resize,aBoundRect0);
199 }
200
NbcSetText(SvStream & rInput,const OUString & rBaseURL,EETextFormat eFormat)201 void SdrTextObj::NbcSetText(SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat)
202 {
203 SdrOutliner& rOutliner=ImpGetDrawOutliner();
204 rOutliner.SetStyleSheet( 0, GetStyleSheet());
205 rOutliner.Read(rInput,rBaseURL,eFormat);
206 std::optional<OutlinerParaObject> pNewText=rOutliner.CreateParaObject();
207 rOutliner.SetUpdateLayout(true);
208 Size aSize(rOutliner.CalcTextSize());
209 rOutliner.Clear();
210 NbcSetOutlinerParaObject(std::move(pNewText));
211 maTextSize=aSize;
212 mbTextSizeDirty=false;
213 }
214
SetText(SvStream & rInput,const OUString & rBaseURL,EETextFormat eFormat)215 void SdrTextObj::SetText(SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat)
216 {
217 tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
218 NbcSetText(rInput,rBaseURL,eFormat);
219 SetChanged();
220 BroadcastObjectChange();
221 SendUserCall(SdrUserCallType::Resize,aBoundRect0);
222 }
223
GetTextSize() const224 const Size& SdrTextObj::GetTextSize() const
225 {
226 if (mbTextSizeDirty)
227 {
228 Size aSiz;
229 SdrText* pText = getActiveText();
230 if( pText && pText->GetOutlinerParaObject ())
231 {
232 SdrOutliner& rOutliner=ImpGetDrawOutliner();
233 rOutliner.SetText(*pText->GetOutlinerParaObject());
234 rOutliner.SetUpdateLayout(true);
235 aSiz=rOutliner.CalcTextSize();
236 rOutliner.Clear();
237 }
238 // casting to nonconst twice
239 const_cast<SdrTextObj*>(this)->maTextSize = aSiz;
240 const_cast<SdrTextObj*>(this)->mbTextSizeDirty = false;
241 }
242 return maTextSize;
243 }
244
IsAutoGrowHeight() const245 bool SdrTextObj::IsAutoGrowHeight() const
246 {
247 if(!mbTextFrame)
248 return false; // AutoGrow only together with TextFrames
249
250 const SfxItemSet& rSet = GetObjectItemSet();
251 bool bRet = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
252
253 if(bRet)
254 {
255 SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
256
257 if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
258 {
259 SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
260
261 if(eDirection == SdrTextAniDirection::Up || eDirection == SdrTextAniDirection::Down)
262 {
263 bRet = false;
264 }
265 }
266 }
267 return bRet;
268 }
269
IsAutoGrowWidth() const270 bool SdrTextObj::IsAutoGrowWidth() const
271 {
272 if (!mbTextFrame)
273 return false; // AutoGrow only together with TextFrames
274
275 const SfxItemSet& rSet = GetObjectItemSet();
276 bool bRet = rSet.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue();
277
278 bool bInEditMOde = IsInEditMode();
279
280 if(!bInEditMOde && bRet)
281 {
282 SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
283
284 if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
285 {
286 SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
287
288 if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
289 {
290 bRet = false;
291 }
292 }
293 }
294 return bRet;
295 }
296
GetTextHorizontalAdjust() const297 SdrTextHorzAdjust SdrTextObj::GetTextHorizontalAdjust() const
298 {
299 return GetTextHorizontalAdjust(GetObjectItemSet());
300 }
301
GetTextHorizontalAdjust(const SfxItemSet & rSet) const302 SdrTextHorzAdjust SdrTextObj::GetTextHorizontalAdjust(const SfxItemSet& rSet) const
303 {
304 if(IsContourTextFrame())
305 return SDRTEXTHORZADJUST_BLOCK;
306
307 SdrTextHorzAdjust eRet = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue();
308
309 bool bInEditMode = IsInEditMode();
310
311 if(!bInEditMode && eRet == SDRTEXTHORZADJUST_BLOCK)
312 {
313 SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
314
315 if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
316 {
317 SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
318
319 if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
320 {
321 eRet = SDRTEXTHORZADJUST_LEFT;
322 }
323 }
324 }
325
326 return eRet;
327 } // defaults: BLOCK (justify) for text frame, CENTER for captions of drawing objects
328
GetTextVerticalAdjust() const329 SdrTextVertAdjust SdrTextObj::GetTextVerticalAdjust() const
330 {
331 return GetTextVerticalAdjust(GetObjectItemSet());
332 }
333
GetTextVerticalAdjust(const SfxItemSet & rSet) const334 SdrTextVertAdjust SdrTextObj::GetTextVerticalAdjust(const SfxItemSet& rSet) const
335 {
336 if(IsContourTextFrame())
337 return SDRTEXTVERTADJUST_TOP;
338
339 // Take care for vertical text animation here
340 SdrTextVertAdjust eRet = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
341 bool bInEditMode = IsInEditMode();
342
343 // Take care for vertical text animation here
344 if(!bInEditMode && eRet == SDRTEXTVERTADJUST_BLOCK)
345 {
346 SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
347
348 if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
349 {
350 SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
351
352 if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
353 {
354 eRet = SDRTEXTVERTADJUST_TOP;
355 }
356 }
357 }
358
359 return eRet;
360 } // defaults: TOP for text frame, CENTER for captions of drawing objects
361
ImpJustifyRect(tools::Rectangle & rRect)362 void SdrTextObj::ImpJustifyRect(tools::Rectangle& rRect)
363 {
364 if (!rRect.IsEmpty()) {
365 rRect.Normalize();
366 if (rRect.Left()==rRect.Right()) rRect.AdjustRight( 1 );
367 if (rRect.Top()==rRect.Bottom()) rRect.AdjustBottom( 1 );
368 }
369 }
370
ImpCheckShear()371 void SdrTextObj::ImpCheckShear()
372 {
373 if (mbNoShear && maGeo.m_nShearAngle)
374 {
375 maGeo.m_nShearAngle = 0_deg100;
376 maGeo.mfTanShearAngle = 0;
377 }
378 }
379
TakeObjInfo(SdrObjTransformInfoRec & rInfo) const380 void SdrTextObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
381 {
382 bool bNoTextFrame=!IsTextFrame();
383 rInfo.bResizeFreeAllowed=bNoTextFrame || ((maGeo.m_nRotationAngle.get() % 9000) == 0);
384 rInfo.bResizePropAllowed=true;
385 rInfo.bRotateFreeAllowed=true;
386 rInfo.bRotate90Allowed =true;
387 rInfo.bMirrorFreeAllowed=bNoTextFrame;
388 rInfo.bMirror45Allowed =bNoTextFrame;
389 rInfo.bMirror90Allowed =bNoTextFrame;
390
391 // allow transparency
392 rInfo.bTransparenceAllowed = true;
393
394 rInfo.bShearAllowed =bNoTextFrame;
395 rInfo.bEdgeRadiusAllowed=true;
396 bool bCanConv=ImpCanConvTextToCurve();
397 rInfo.bCanConvToPath =bCanConv;
398 rInfo.bCanConvToPoly =bCanConv;
399 rInfo.bCanConvToPathLineToArea=bCanConv;
400 rInfo.bCanConvToPolyLineToArea=bCanConv;
401 rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
402 }
403
GetObjIdentifier() const404 SdrObjKind SdrTextObj::GetObjIdentifier() const
405 {
406 return meTextKind;
407 }
408
HasTextImpl(SdrOutliner const * pOutliner)409 bool SdrTextObj::HasTextImpl( SdrOutliner const * pOutliner )
410 {
411 bool bRet=false;
412 if(pOutliner)
413 {
414 Paragraph* p1stPara=pOutliner->GetParagraph( 0 );
415 sal_Int32 nParaCount=pOutliner->GetParagraphCount();
416 if(p1stPara==nullptr)
417 nParaCount=0;
418
419 if(nParaCount==1)
420 {
421 // if it is only one paragraph, check if that paragraph is empty
422 if( pOutliner->GetText(p1stPara).isEmpty() )
423 nParaCount = 0;
424 }
425
426 bRet= nParaCount!=0;
427 }
428 return bRet;
429 }
430
handlePageChange(SdrPage * pOldPage,SdrPage * pNewPage)431 void SdrTextObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
432 {
433 const bool bRemove(pNewPage == nullptr && pOldPage != nullptr);
434 const bool bInsert(pNewPage != nullptr && pOldPage == nullptr);
435 const bool bLinked(IsLinkedText());
436
437 if (bLinked && bRemove)
438 {
439 ImpDeregisterLink();
440 }
441
442 // call parent
443 SdrAttrObj::handlePageChange(pOldPage, pNewPage);
444
445 if (bLinked && bInsert)
446 {
447 ImpRegisterLink();
448 }
449 }
450
NbcSetEckenradius(tools::Long nRad)451 void SdrTextObj::NbcSetEckenradius(tools::Long nRad)
452 {
453 SetObjectItem(makeSdrEckenradiusItem(nRad));
454 }
455
456 // #115391# This implementation is based on the object size (aRect) and the
457 // states of IsAutoGrowWidth/Height to correctly set TextMinFrameWidth/Height
AdaptTextMinSize()458 void SdrTextObj::AdaptTextMinSize()
459 {
460 if (!mbTextFrame)
461 // Only do this for text frame.
462 return;
463
464 if (getSdrModelFromSdrObject().IsPasteResize())
465 // Don't do this during paste resize.
466 return;
467
468 const bool bW = IsAutoGrowWidth();
469 const bool bH = IsAutoGrowHeight();
470
471 if (!bW && !bH)
472 // No auto grow requested. Bail out.
473 return;
474
475 SfxItemSet aSet(SfxItemSet::makeFixedSfxItemSet<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
476 // contains SDRATTR_TEXT_MAXFRAMEWIDTH
477 SDRATTR_TEXT_MINFRAMEWIDTH, SDRATTR_TEXT_AUTOGROWWIDTH>(*GetObjectItemSet().GetPool()));
478
479 if(bW)
480 {
481 // Set minimum width.
482 const tools::Long nDist = GetTextLeftDistance() + GetTextRightDistance();
483 const tools::Long nW = std::max<tools::Long>(0, getRectangle().GetWidth() - 1 - nDist); // text width without margins
484
485 aSet.Put(makeSdrTextMinFrameWidthItem(nW));
486
487 if(!IsVerticalWriting() && mbDisableAutoWidthOnDragging)
488 {
489 mbDisableAutoWidthOnDragging = true;
490 aSet.Put(makeSdrTextAutoGrowWidthItem(false));
491 }
492 }
493
494 if(bH)
495 {
496 // Set Minimum height.
497 const tools::Long nDist = GetTextUpperDistance() + GetTextLowerDistance();
498 const tools::Long nH = std::max<tools::Long>(0, getRectangle().GetHeight() - 1 - nDist); // text height without margins
499
500 aSet.Put(makeSdrTextMinFrameHeightItem(nH));
501
502 if(IsVerticalWriting() && mbDisableAutoWidthOnDragging)
503 {
504 mbDisableAutoWidthOnDragging = false;
505 aSet.Put(makeSdrTextAutoGrowHeightItem(false));
506 }
507 }
508
509 SetObjectItemSet(aSet);
510 }
511
ImpSetContourPolygon(SdrOutliner & rOutliner,tools::Rectangle const & rAnchorRect,bool bLineWidth) const512 void SdrTextObj::ImpSetContourPolygon( SdrOutliner& rOutliner, tools::Rectangle const & rAnchorRect, bool bLineWidth ) const
513 {
514 basegfx::B2DPolyPolygon aXorPolyPolygon(TakeXorPoly());
515 std::optional<basegfx::B2DPolyPolygon> pContourPolyPolygon;
516 basegfx::B2DHomMatrix aMatrix(basegfx::utils::createTranslateB2DHomMatrix(
517 -rAnchorRect.Left(), -rAnchorRect.Top()));
518
519 if(maGeo.m_nRotationAngle)
520 {
521 // Unrotate!
522 aMatrix.rotate(-toRadians(maGeo.m_nRotationAngle));
523 }
524
525 aXorPolyPolygon.transform(aMatrix);
526
527 if( bLineWidth )
528 {
529 // Take line width into account.
530 // When doing the hit test, avoid this. (Performance!)
531 pContourPolyPolygon.emplace();
532
533 // test if shadow needs to be avoided for TakeContour()
534 const SfxItemSet& rSet = GetObjectItemSet();
535 bool bShadowOn = rSet.Get(SDRATTR_SHADOW).GetValue();
536
537 // #i33696#
538 // Remember TextObject currently set at the DrawOutliner, it WILL be
539 // replaced during calculating the outline since it uses an own paint
540 // and that one uses the DrawOutliner, too.
541 const SdrTextObj* pLastTextObject = rOutliner.GetTextObj();
542
543 if(bShadowOn)
544 {
545 // force shadow off
546 rtl::Reference<SdrTextObj> pCopy = SdrObject::Clone(*this, getSdrModelFromSdrObject());
547 pCopy->SetMergedItem(makeSdrShadowItem(false));
548 *pContourPolyPolygon = pCopy->TakeContour();
549 }
550 else
551 {
552 *pContourPolyPolygon = TakeContour();
553 }
554
555 // #i33696#
556 // restore remembered text object
557 if(pLastTextObject != rOutliner.GetTextObj())
558 {
559 rOutliner.SetTextObj(pLastTextObject);
560 }
561
562 pContourPolyPolygon->transform(aMatrix);
563 }
564
565 rOutliner.SetPolygon(aXorPolyPolygon, pContourPolyPolygon ? &*pContourPolyPolygon : nullptr);
566 }
567
TakeUnrotatedSnapRect(tools::Rectangle & rRect) const568 void SdrTextObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
569 {
570 rRect = getRectangle();
571 }
572
573 // See also: <unnamed>::getTextAnchorRange in svx/source/sdr/primitive2d/sdrdecompositiontools.cxx
AdjustRectToTextDistance(tools::Rectangle & rAnchorRect,double fExtraRot) const574 void SdrTextObj::AdjustRectToTextDistance(tools::Rectangle& rAnchorRect, double fExtraRot) const
575 {
576 const tools::Long nLeftDist = GetTextLeftDistance();
577 const tools::Long nRightDist = GetTextRightDistance();
578 const tools::Long nUpperDist = GetTextUpperDistance();
579 const tools::Long nLowerDist = GetTextLowerDistance();
580 if (!IsVerticalWriting())
581 {
582 if (fExtraRot == 180.0)
583 {
584 rAnchorRect.AdjustLeft(nLeftDist);
585 rAnchorRect.AdjustTop(-nUpperDist);
586 rAnchorRect.AdjustRight(-nRightDist);
587 rAnchorRect.AdjustBottom(nLowerDist);
588 }
589 else
590 {
591 rAnchorRect.AdjustLeft(nLeftDist);
592 rAnchorRect.AdjustTop(nUpperDist);
593 rAnchorRect.AdjustRight(-nRightDist);
594 rAnchorRect.AdjustBottom(-nLowerDist);
595 }
596 }
597 else if (IsTopToBottom())
598 {
599 rAnchorRect.AdjustLeft(nLowerDist);
600 rAnchorRect.AdjustTop(nLeftDist);
601 rAnchorRect.AdjustRight(-nUpperDist);
602 rAnchorRect.AdjustBottom(-nRightDist);
603 }
604 else
605 {
606 rAnchorRect.AdjustLeft(nUpperDist);
607 rAnchorRect.AdjustTop(nRightDist);
608 rAnchorRect.AdjustRight(-nLowerDist);
609 rAnchorRect.AdjustBottom(-nLeftDist);
610 }
611
612 // Since sizes may be bigger than the object bounds it is necessary to
613 // justify the rect now.
614 ImpJustifyRect(rAnchorRect);
615 }
616
TakeTextAnchorRect(tools::Rectangle & rAnchorRect) const617 void SdrTextObj::TakeTextAnchorRect(tools::Rectangle& rAnchorRect) const
618 {
619 tools::Rectangle aAnkRect(getRectangle()); // the rectangle in which we anchor
620 bool bFrame=IsTextFrame();
621 if (!bFrame) {
622 TakeUnrotatedSnapRect(aAnkRect);
623 }
624 Point aRotateRef(aAnkRect.TopLeft());
625 AdjustRectToTextDistance(aAnkRect);
626
627 if (bFrame) {
628 // TODO: Optimize this.
629 if (aAnkRect.GetWidth()<2) aAnkRect.SetRight(aAnkRect.Left()+1 ); // minimum size h and v: 2 px
630 if (aAnkRect.GetHeight()<2) aAnkRect.SetBottom(aAnkRect.Top()+1 );
631 }
632 if (maGeo.m_nRotationAngle) {
633 Point aTmpPt(aAnkRect.TopLeft());
634 RotatePoint(aTmpPt,aRotateRef,maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
635 aTmpPt-=aAnkRect.TopLeft();
636 aAnkRect.Move(aTmpPt.X(),aTmpPt.Y());
637 }
638 rAnchorRect=aAnkRect;
639 }
640
TakeTextRect(SdrOutliner & rOutliner,tools::Rectangle & rTextRect,bool bNoEditText,tools::Rectangle * pAnchorRect,bool bLineWidth) const641 void SdrTextObj::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText,
642 tools::Rectangle* pAnchorRect, bool bLineWidth ) const
643 {
644 tools::Rectangle aAnkRect; // the rectangle in which we anchor
645 TakeTextAnchorRect(aAnkRect);
646 SdrTextVertAdjust eVAdj=GetTextVerticalAdjust();
647 SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust();
648 SdrTextAniKind eAniKind=GetTextAniKind();
649 SdrTextAniDirection eAniDirection=GetTextAniDirection();
650
651 bool bFitToSize(IsFitToSize());
652 bool bContourFrame=IsContourTextFrame();
653
654 bool bFrame=IsTextFrame();
655 EEControlBits nStat0=rOutliner.GetControlWord();
656 Size aNullSize;
657 if (!bContourFrame)
658 {
659 rOutliner.SetControlWord(nStat0|EEControlBits::AUTOPAGESIZE);
660 rOutliner.SetMinAutoPaperSize(aNullSize);
661 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
662 }
663
664 if (!bFitToSize && !bContourFrame)
665 {
666 tools::Long nAnkWdt=aAnkRect.GetWidth();
667 tools::Long nAnkHgt=aAnkRect.GetHeight();
668 if (bFrame)
669 {
670 tools::Long nWdt=nAnkWdt;
671 tools::Long nHgt=nAnkHgt;
672
673 bool bInEditMode = IsInEditMode();
674
675 if (!bInEditMode && (eAniKind==SdrTextAniKind::Scroll || eAniKind==SdrTextAniKind::Alternate || eAniKind==SdrTextAniKind::Slide))
676 {
677 // unlimited paper size for ticker text
678 if (eAniDirection==SdrTextAniDirection::Left || eAniDirection==SdrTextAniDirection::Right) nWdt=1000000;
679 if (eAniDirection==SdrTextAniDirection::Up || eAniDirection==SdrTextAniDirection::Down) nHgt=1000000;
680 }
681
682 bool bChainedFrame = IsChainable();
683 // Might be required for overflow check working: do limit height to frame if box is chainable.
684 if (!bChainedFrame) {
685 // #i119885# Do not limit/force height to geometrical frame (vice versa for vertical writing)
686
687 if(IsVerticalWriting())
688 {
689 nWdt = 1000000;
690 }
691 else
692 {
693 nHgt = 1000000;
694 }
695 }
696
697 rOutliner.SetMaxAutoPaperSize(Size(nWdt,nHgt));
698 }
699
700 // New try with _BLOCK for hor and ver after completely
701 // supporting full width for vertical text.
702 if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting())
703 {
704 rOutliner.SetMinAutoPaperSize(Size(nAnkWdt, 0));
705 rOutliner.SetMinColumnWrapHeight(nAnkHgt);
706 }
707
708 if(SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting())
709 {
710 rOutliner.SetMinAutoPaperSize(Size(0, nAnkHgt));
711 rOutliner.SetMinColumnWrapHeight(nAnkWdt);
712 }
713 }
714
715 rOutliner.SetPaperSize(aNullSize);
716 if (bContourFrame)
717 ImpSetContourPolygon( rOutliner, aAnkRect, bLineWidth );
718
719 // put text into the outliner, if available from the edit outliner
720 SdrText* pText = getActiveText();
721 OutlinerParaObject* pOutlinerParaObject = pText ? pText->GetOutlinerParaObject() : nullptr;
722 std::optional<OutlinerParaObject> pPara;
723 if (mpEditingOutliner && !bNoEditText)
724 pPara = mpEditingOutliner->CreateParaObject();
725 else if (pOutlinerParaObject)
726 pPara = *pOutlinerParaObject;
727
728 if (pPara)
729 {
730 const bool bHitTest(&getSdrModelFromSdrObject().GetHitTestOutliner() == &rOutliner);
731 const SdrTextObj* pTestObj = rOutliner.GetTextObj();
732
733 if( !pTestObj || !bHitTest || pTestObj != this ||
734 pTestObj->GetOutlinerParaObject() != pOutlinerParaObject )
735 {
736 if( bHitTest ) // #i33696# take back fix #i27510#
737 {
738 rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
739 rOutliner.SetTextObj( this );
740 }
741
742 rOutliner.SetText(*pPara);
743 }
744 }
745 else
746 {
747 rOutliner.SetTextObj( nullptr );
748 }
749
750 rOutliner.SetUpdateLayout(true);
751 rOutliner.SetControlWord(nStat0);
752
753 if( pText )
754 pText->CheckPortionInfo(rOutliner);
755
756 Point aTextPos(aAnkRect.TopLeft());
757 Size aTextSiz(rOutliner.GetPaperSize()); // GetPaperSize() adds a little tolerance, right?
758
759 // For draw objects containing text correct hor/ver alignment if text is bigger
760 // than the object itself. Without that correction, the text would always be
761 // formatted to the left edge (or top edge when vertical) of the draw object.
762 if(!IsTextFrame())
763 {
764 if(aAnkRect.GetWidth() < aTextSiz.Width() && !IsVerticalWriting())
765 {
766 // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
767 // else the alignment is wanted.
768 if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
769 {
770 eHAdj = SDRTEXTHORZADJUST_CENTER;
771 }
772 }
773
774 if(aAnkRect.GetHeight() < aTextSiz.Height() && IsVerticalWriting())
775 {
776 // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
777 // else the alignment is wanted.
778 if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
779 {
780 eVAdj = SDRTEXTVERTADJUST_CENTER;
781 }
782 }
783 }
784
785 if (eHAdj==SDRTEXTHORZADJUST_CENTER || eHAdj==SDRTEXTHORZADJUST_RIGHT)
786 {
787 tools::Long nFreeWdt=aAnkRect.GetWidth()-aTextSiz.Width();
788 if (eHAdj==SDRTEXTHORZADJUST_CENTER)
789 aTextPos.AdjustX(nFreeWdt/2 );
790 if (eHAdj==SDRTEXTHORZADJUST_RIGHT)
791 aTextPos.AdjustX(nFreeWdt );
792 }
793 if (eVAdj==SDRTEXTVERTADJUST_CENTER || eVAdj==SDRTEXTVERTADJUST_BOTTOM)
794 {
795 tools::Long nFreeHgt=aAnkRect.GetHeight()-aTextSiz.Height();
796 if (eVAdj==SDRTEXTVERTADJUST_CENTER)
797 aTextPos.AdjustY(nFreeHgt/2 );
798 if (eVAdj==SDRTEXTVERTADJUST_BOTTOM)
799 aTextPos.AdjustY(nFreeHgt );
800 }
801 if (maGeo.m_nRotationAngle)
802 RotatePoint(aTextPos,aAnkRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
803
804 if (pAnchorRect)
805 *pAnchorRect=aAnkRect;
806
807 // rTextRect might not be correct in some cases at ContourFrame
808 rTextRect=tools::Rectangle(aTextPos,aTextSiz);
809 if (bContourFrame)
810 rTextRect=aAnkRect;
811 }
812
CanCreateEditOutlinerParaObject() const813 bool SdrTextObj::CanCreateEditOutlinerParaObject() const
814 {
815 if( HasTextImpl( mpEditingOutliner ) )
816 {
817 return mpEditingOutliner->GetParagraphCount() > 0;
818 }
819 return false;
820 }
821
CreateEditOutlinerParaObject() const822 std::optional<OutlinerParaObject> SdrTextObj::CreateEditOutlinerParaObject() const
823 {
824 std::optional<OutlinerParaObject> pPara;
825 if( HasTextImpl( mpEditingOutliner ) )
826 {
827 sal_Int32 nParaCount = mpEditingOutliner->GetParagraphCount();
828 pPara = mpEditingOutliner->CreateParaObject(0, nParaCount);
829 }
830 return pPara;
831 }
832
ImpSetCharStretching(SdrOutliner & rOutliner,const Size & rTextSize,const Size & rShapeSize,Fraction & rFitXCorrection)833 void SdrTextObj::ImpSetCharStretching(SdrOutliner& rOutliner, const Size& rTextSize, const Size& rShapeSize, Fraction& rFitXCorrection)
834 {
835 OutputDevice* pOut = rOutliner.GetRefDevice();
836 bool bNoStretching(false);
837
838 if(pOut && pOut->GetOutDevType() == OUTDEV_PRINTER)
839 {
840 // check whether CharStretching is possible at all
841 GDIMetaFile* pMtf = pOut->GetConnectMetaFile();
842 OUString aTestString(u'J');
843
844 if(pMtf && (!pMtf->IsRecord() || pMtf->IsPause()))
845 pMtf = nullptr;
846
847 if(pMtf)
848 pMtf->Pause(true);
849
850 vcl::Font aOriginalFont(pOut->GetFont());
851 vcl::Font aTmpFont( OutputDevice::GetDefaultFont( DefaultFontType::SERIF, LANGUAGE_SYSTEM, GetDefaultFontFlags::OnlyOne ) );
852
853 aTmpFont.SetFontSize(Size(0,100));
854 pOut->SetFont(aTmpFont);
855 Size aSize1(pOut->GetTextWidth(aTestString), pOut->GetTextHeight());
856 aTmpFont.SetFontSize(Size(800,100));
857 pOut->SetFont(aTmpFont);
858 Size aSize2(pOut->GetTextWidth(aTestString), pOut->GetTextHeight());
859 pOut->SetFont(aOriginalFont);
860
861 if(pMtf)
862 pMtf->Pause(false);
863
864 bNoStretching = (aSize1 == aSize2);
865
866 #ifdef _WIN32
867 // Windows zooms the font proportionally when using Size(100,500),
868 // we don't like that.
869 if(aSize2.Height() >= aSize1.Height() * 2)
870 {
871 bNoStretching = true;
872 }
873 #endif
874 }
875
876 rOutliner.setRoundFontSizeToPt(false);
877
878 unsigned nLoopCount=0;
879 bool bNoMoreLoop = false;
880 tools::Long nXDiff0=0x7FFFFFFF;
881 tools::Long nWantWdt=rShapeSize.Width();
882 tools::Long nIsWdt=rTextSize.Width();
883 if (nIsWdt==0) nIsWdt=1;
884
885 tools::Long nWantHgt=rShapeSize.Height();
886 tools::Long nIsHgt=rTextSize.Height();
887 if (nIsHgt==0) nIsHgt=1;
888
889 tools::Long nXTolPl=nWantWdt/100; // tolerance: +1%
890 tools::Long nXTolMi=nWantWdt/25; // tolerance: -4%
891 tools::Long nXCorr =nWantWdt/20; // correction scale: 5%
892
893 double nX = nWantWdt / double(nIsWdt); // calculate X stretching
894 double nY = nWantHgt / double(nIsHgt); // calculate Y stretching
895 bool bChkX = true;
896 if (bNoStretching)
897 { // might only be possible proportionally
898 if (nX > nY)
899 {
900 nX = nY;
901 bChkX = false;
902 }
903 else
904 {
905 nY = nX;
906 }
907 }
908
909 while (nLoopCount<5 && !bNoMoreLoop)
910 {
911 if (nX < 0.0)
912 nX = -nX;
913 if (nX < 0.01)
914 {
915 nX = 0.01;
916 bNoMoreLoop = true;
917 }
918 if (nX > 655.35)
919 {
920 nX = 655.35;
921 bNoMoreLoop = true;
922 }
923
924 if (nY < 0.0)
925 {
926 nY = -nY;
927 }
928 if (nY < 0.01)
929 {
930 nY = 0.01;
931 bNoMoreLoop = true;
932 }
933 if (nY > 655.35)
934 {
935 nY = 655.35;
936 bNoMoreLoop = true;
937 }
938
939 // exception, there is no text yet (horizontal case)
940 if (nIsWdt <= 1)
941 {
942 nX = nY;
943 bNoMoreLoop = true;
944 }
945
946 // exception, there is no text yet (vertical case)
947 if (nIsHgt <= 1)
948 {
949 nY = nX;
950 bNoMoreLoop = true;
951 }
952 rOutliner.setScalingParameters({nX, nY});
953 nLoopCount++;
954 Size aSiz(rOutliner.CalcTextSize());
955 tools::Long nXDiff = aSiz.Width() - nWantWdt;
956 rFitXCorrection=Fraction(nWantWdt,aSiz.Width());
957 if (((nXDiff>=nXTolMi || !bChkX) && nXDiff<=nXTolPl) || nXDiff==nXDiff0) {
958 bNoMoreLoop = true;
959 } else {
960 // correct stretching factors
961 tools::Long nMul = nWantWdt;
962 tools::Long nDiv = aSiz.Width();
963 if (std::abs(nXDiff) <= 2 * nXCorr)
964 {
965 if (nMul > nDiv)
966 nDiv += (nMul - nDiv) / 2.0; // but only add half of what we calculated,
967 else
968 nMul += (nDiv - nMul) / 2.0;// because the EditEngine calculates wrongly later on
969 }
970 nX = nX * double(nMul) / double(nDiv);
971 if (bNoStretching)
972 nY = nX;
973 }
974 nXDiff0 = nXDiff;
975 }
976 }
977
TakeObjNameSingul() const978 OUString SdrTextObj::TakeObjNameSingul() const
979 {
980 OUString aStr;
981
982 switch(meTextKind)
983 {
984 case SdrObjKind::OutlineText:
985 {
986 aStr = SvxResId(STR_ObjNameSingulOUTLINETEXT);
987 break;
988 }
989
990 case SdrObjKind::TitleText :
991 {
992 aStr = SvxResId(STR_ObjNameSingulTITLETEXT);
993 break;
994 }
995
996 default:
997 {
998 if(IsLinkedText())
999 aStr = SvxResId(STR_ObjNameSingulTEXTLNK);
1000 else
1001 aStr = SvxResId(STR_ObjNameSingulTEXT);
1002 break;
1003 }
1004 }
1005
1006 OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
1007 if(pOutlinerParaObject && meTextKind != SdrObjKind::OutlineText)
1008 {
1009 // shouldn't currently cause any problems at OUTLINETEXT
1010 OUString aStr2(comphelper::string::stripStart(pOutlinerParaObject->GetTextObject().GetText(0), ' '));
1011
1012 // avoid non expanded text portions in object name
1013 // (second condition is new)
1014 if(!aStr2.isEmpty() && aStr2.indexOf(u'\x00FF') == -1)
1015 {
1016 // space between ResStr and content text
1017 aStr += " \'";
1018
1019 if(aStr2.getLength() > 10)
1020 {
1021 aStr2 = OUString::Concat(aStr2.subView(0, 8)) + "...";
1022 }
1023
1024 aStr += aStr2 + "\'";
1025 }
1026 }
1027
1028 OUString sName(aStr);
1029
1030 OUString aName(GetName());
1031 if (!aName.isEmpty())
1032 sName += " '" + aName + "'";
1033
1034 return sName;
1035 }
1036
TakeObjNamePlural() const1037 OUString SdrTextObj::TakeObjNamePlural() const
1038 {
1039 OUString sName;
1040 switch (meTextKind)
1041 {
1042 case SdrObjKind::OutlineText: sName=SvxResId(STR_ObjNamePluralOUTLINETEXT); break;
1043 case SdrObjKind::TitleText : sName=SvxResId(STR_ObjNamePluralTITLETEXT); break;
1044 default: {
1045 if (IsLinkedText()) {
1046 sName=SvxResId(STR_ObjNamePluralTEXTLNK);
1047 } else {
1048 sName=SvxResId(STR_ObjNamePluralTEXT);
1049 }
1050 } break;
1051 } // switch
1052 return sName;
1053 }
1054
CloneSdrObject(SdrModel & rTargetModel) const1055 rtl::Reference<SdrObject> SdrTextObj::CloneSdrObject(SdrModel& rTargetModel) const
1056 {
1057 return new SdrTextObj(rTargetModel, *this);
1058 }
1059
TakeXorPoly() const1060 basegfx::B2DPolyPolygon SdrTextObj::TakeXorPoly() const
1061 {
1062 tools::Polygon aPol(getRectangle());
1063 if (maGeo.m_nShearAngle)
1064 ShearPoly(aPol, getRectangle().TopLeft(), maGeo.mfTanShearAngle);
1065 if (maGeo.m_nRotationAngle)
1066 RotatePoly(aPol, getRectangle().TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
1067
1068 basegfx::B2DPolyPolygon aRetval;
1069 aRetval.append(aPol.getB2DPolygon());
1070 return aRetval;
1071 }
1072
TakeContour() const1073 basegfx::B2DPolyPolygon SdrTextObj::TakeContour() const
1074 {
1075 basegfx::B2DPolyPolygon aRetval(SdrAttrObj::TakeContour());
1076
1077 // and now add the BoundRect of the text, if necessary
1078 if ( GetOutlinerParaObject() && !IsFontwork() && !IsContourTextFrame() )
1079 {
1080 // using Clone()-Paint() strategy inside TakeContour() leaves a destroyed
1081 // SdrObject as pointer in DrawOutliner. Set *this again in fetching the outliner
1082 // in every case
1083 SdrOutliner& rOutliner=ImpGetDrawOutliner();
1084
1085 tools::Rectangle aAnchor2;
1086 tools::Rectangle aR;
1087 TakeTextRect(rOutliner,aR,false,&aAnchor2);
1088 rOutliner.Clear();
1089 bool bFitToSize(IsFitToSize());
1090 if (bFitToSize) aR=aAnchor2;
1091 tools::Polygon aPol(aR);
1092 if (maGeo.m_nRotationAngle) RotatePoly(aPol,aR.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
1093
1094 aRetval.append(aPol.getB2DPolygon());
1095 }
1096
1097 return aRetval;
1098 }
1099
RecalcSnapRect()1100 void SdrTextObj::RecalcSnapRect()
1101 {
1102 if (maGeo.m_nRotationAngle || maGeo.m_nShearAngle)
1103 {
1104 maSnapRect = Rect2Poly(getRectangle(), maGeo).GetBoundRect();
1105 } else {
1106 maSnapRect = getRectangle();
1107 }
1108 }
1109
GetSnapPointCount() const1110 sal_uInt32 SdrTextObj::GetSnapPointCount() const
1111 {
1112 return 4;
1113 }
1114
GetSnapPoint(sal_uInt32 i) const1115 Point SdrTextObj::GetSnapPoint(sal_uInt32 i) const
1116 {
1117 Point aP;
1118 const auto& rRectangle = getRectangle();
1119 switch (i) {
1120 case 0: aP = rRectangle.TopLeft(); break;
1121 case 1: aP = rRectangle.TopRight(); break;
1122 case 2: aP = rRectangle.BottomLeft(); break;
1123 case 3: aP = rRectangle.BottomRight(); break;
1124 default: aP = rRectangle.Center(); break;
1125 }
1126 if (maGeo.m_nShearAngle)
1127 ShearPoint(aP, rRectangle.TopLeft(), maGeo.mfTanShearAngle);
1128 if (maGeo.m_nRotationAngle)
1129 RotatePoint(aP, rRectangle.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
1130 return aP;
1131 }
1132
1133 // Extracted from ImpGetDrawOutliner()
ImpInitDrawOutliner(SdrOutliner & rOutl) const1134 void SdrTextObj::ImpInitDrawOutliner( SdrOutliner& rOutl ) const
1135 {
1136 rOutl.SetUpdateLayout(false);
1137 OutlinerMode nOutlinerMode = OutlinerMode::OutlineObject;
1138 if ( !IsOutlText() )
1139 nOutlinerMode = OutlinerMode::TextObject;
1140 rOutl.Init( nOutlinerMode );
1141
1142 rOutl.resetScalingParameters();
1143
1144 EEControlBits nStat=rOutl.GetControlWord();
1145 nStat &= ~EEControlBits(EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE);
1146 rOutl.SetControlWord(nStat);
1147 Size aMaxSize(100000,100000);
1148 rOutl.SetMinAutoPaperSize(Size());
1149 rOutl.SetMaxAutoPaperSize(aMaxSize);
1150 rOutl.SetPaperSize(aMaxSize);
1151 rOutl.ClearPolygon();
1152 }
1153
ImpGetDrawOutliner() const1154 SdrOutliner& SdrTextObj::ImpGetDrawOutliner() const
1155 {
1156 SdrOutliner& rOutl(getSdrModelFromSdrObject().GetDrawOutliner(this));
1157
1158 // Code extracted to ImpInitDrawOutliner()
1159 ImpInitDrawOutliner( rOutl );
1160
1161 return rOutl;
1162 }
1163
1164 // Extracted from Paint()
ImpSetupDrawOutlinerForPaint(bool bContourFrame,SdrOutliner & rOutliner,tools::Rectangle & rTextRect,tools::Rectangle & rAnchorRect,tools::Rectangle & rPaintRect,Fraction & rFitXCorrection) const1165 void SdrTextObj::ImpSetupDrawOutlinerForPaint( bool bContourFrame,
1166 SdrOutliner& rOutliner,
1167 tools::Rectangle& rTextRect,
1168 tools::Rectangle& rAnchorRect,
1169 tools::Rectangle& rPaintRect,
1170 Fraction& rFitXCorrection ) const
1171 {
1172 if (!bContourFrame)
1173 {
1174 // FitToSize can't be used together with ContourFrame for now
1175 if (IsFitToSize() || IsAutoFit())
1176 {
1177 EEControlBits nStat=rOutliner.GetControlWord();
1178 nStat|=EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE;
1179 rOutliner.SetControlWord(nStat);
1180 }
1181 }
1182 rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
1183 TakeTextRect(rOutliner, rTextRect, false, &rAnchorRect);
1184 rPaintRect = rTextRect;
1185
1186 if (bContourFrame)
1187 return;
1188
1189 // FitToSize can't be used together with ContourFrame for now
1190 if (IsFitToSize())
1191 {
1192 ImpSetCharStretching(rOutliner,rTextRect.GetSize(),rAnchorRect.GetSize(),rFitXCorrection);
1193 rPaintRect=rAnchorRect;
1194 }
1195 else if (IsAutoFit())
1196 {
1197 setupAutoFitText(rOutliner);
1198 }
1199 }
1200
GetFontScale() const1201 double SdrTextObj::GetFontScale() const
1202 {
1203 SdrOutliner& rOutliner = ImpGetDrawOutliner();
1204 // This eventually calls setupAutoFitText
1205 UpdateOutlinerFormatting(rOutliner, o3tl::temporary(tools::Rectangle()));
1206
1207 return rOutliner.getScalingParameters().fFontY;
1208 }
1209
GetSpacingScale() const1210 double SdrTextObj::GetSpacingScale() const
1211 {
1212 SdrOutliner& rOutliner = ImpGetDrawOutliner();
1213 // This eventually calls setupAutoFitText
1214 UpdateOutlinerFormatting(rOutliner, o3tl::temporary(tools::Rectangle()));
1215
1216 return rOutliner.getScalingParameters().fSpacingY;
1217 }
1218
setupAutoFitText(SdrOutliner & rOutliner) const1219 void SdrTextObj::setupAutoFitText(SdrOutliner& rOutliner) const
1220 {
1221 const Size aShapeSize = GetSnapRect().GetSize();
1222 Size aSize(aShapeSize.Width() - GetTextLeftDistance() - GetTextRightDistance(),
1223 aShapeSize.Height() - GetTextUpperDistance() - GetTextLowerDistance());
1224
1225 setupAutoFitText(rOutliner, aSize);
1226 }
setupAutoFitText(SdrOutliner & rOutliner,const Size & rTextBoxSize) const1227 void SdrTextObj::setupAutoFitText(SdrOutliner& rOutliner, const Size& rTextBoxSize) const
1228 {
1229 rOutliner.setRoundFontSizeToPt(true); // We need to round the font size nearest integer pt size
1230 rOutliner.SetMaxAutoPaperSize(rTextBoxSize);
1231 rOutliner.SetPaperSize(rTextBoxSize);
1232
1233 const SdrTextFitToSizeTypeItem& rItem = GetObjectItem(SDRATTR_TEXT_FITTOSIZE);
1234
1235 double fFontScale = rItem.getFontScale();
1236 double fSpacingScale = rItem.getSpacingScale();
1237
1238 if (fFontScale > 0.0 && fSpacingScale > 0.0 && !mbInEditMode)
1239 {
1240 rOutliner.setScalingParameters({ fFontScale, fFontScale, 1.0, fSpacingScale });
1241 }
1242 else
1243 {
1244 rOutliner.resetScalingParameters();
1245 }
1246
1247 rOutliner.QuickFormatDoc();
1248 }
1249
SetupOutlinerFormatting(SdrOutliner & rOutl,tools::Rectangle & rPaintRect) const1250 void SdrTextObj::SetupOutlinerFormatting( SdrOutliner& rOutl, tools::Rectangle& rPaintRect ) const
1251 {
1252 ImpInitDrawOutliner( rOutl );
1253 UpdateOutlinerFormatting( rOutl, rPaintRect );
1254 }
1255
UpdateOutlinerFormatting(SdrOutliner & rOutl,tools::Rectangle & rPaintRect) const1256 void SdrTextObj::UpdateOutlinerFormatting( SdrOutliner& rOutl, tools::Rectangle& rPaintRect ) const
1257 {
1258 tools::Rectangle aTextRect;
1259 tools::Rectangle aAnchorRect;
1260 Fraction aFitXCorrection(1,1);
1261
1262 const bool bContourFrame(IsContourTextFrame());
1263 const MapMode aMapMode(getSdrModelFromSdrObject().GetScaleUnit());
1264
1265 rOutl.SetRefMapMode(aMapMode);
1266 ImpSetupDrawOutlinerForPaint(
1267 bContourFrame,
1268 rOutl,
1269 aTextRect,
1270 aAnchorRect,
1271 rPaintRect,
1272 aFitXCorrection);
1273 }
1274
1275
GetOutlinerParaObject() const1276 OutlinerParaObject* SdrTextObj::GetOutlinerParaObject() const
1277 {
1278 SdrText* pText = getActiveText();
1279 if( pText )
1280 return pText->GetOutlinerParaObject();
1281 else
1282 return nullptr;
1283 }
1284
NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject,bool bAdjustTextFrameWidthAndHeight)1285 void SdrTextObj::NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject, bool bAdjustTextFrameWidthAndHeight)
1286 {
1287 NbcSetOutlinerParaObjectForText( std::move(pTextObject), getActiveText(), bAdjustTextFrameWidthAndHeight );
1288 }
1289
1290 namespace
1291 {
IsAutoGrow(const SdrTextObj & rObj)1292 bool IsAutoGrow(const SdrTextObj& rObj)
1293 {
1294 bool bAutoGrow = rObj.IsAutoGrowHeight() || rObj.IsAutoGrowWidth();
1295 return bAutoGrow && !comphelper::IsFuzzing();
1296 }
1297 }
1298
NbcSetOutlinerParaObjectForText(std::optional<OutlinerParaObject> pTextObject,SdrText * pText,bool bAdjustTextFrameWidthAndHeight)1299 void SdrTextObj::NbcSetOutlinerParaObjectForText( std::optional<OutlinerParaObject> pTextObject, SdrText* pText, bool bAdjustTextFrameWidthAndHeight )
1300 {
1301 if( pText )
1302 pText->SetOutlinerParaObject( std::move(pTextObject) );
1303
1304 if (pText && pText->GetOutlinerParaObject())
1305 {
1306 SvxWritingModeItem aWritingMode(pText->GetOutlinerParaObject()->IsEffectivelyVertical() && pText->GetOutlinerParaObject()->IsTopToBottom()
1307 ? css::text::WritingMode_TB_RL
1308 : css::text::WritingMode_LR_TB,
1309 SDRATTR_TEXTDIRECTION);
1310 GetProperties().SetObjectItemDirect(aWritingMode);
1311 }
1312
1313 SetTextSizeDirty();
1314 if (IsTextFrame() && IsAutoGrow(*this) && bAdjustTextFrameWidthAndHeight)
1315 { // adapt text frame!
1316 NbcAdjustTextFrameWidthAndHeight();
1317 }
1318 if (!IsTextFrame())
1319 {
1320 // the SnapRect keeps its size
1321 SetBoundAndSnapRectsDirty(true);
1322 }
1323
1324 // always invalidate BoundRect on change
1325 SetBoundRectDirty();
1326 ActionChanged();
1327
1328 ImpSetTextStyleSheetListeners();
1329 }
1330
NbcReformatText()1331 void SdrTextObj::NbcReformatText()
1332 {
1333 SdrText* pText = getActiveText();
1334 if( !(pText && pText->GetOutlinerParaObject()) )
1335 return;
1336
1337 pText->ReformatText();
1338 if (mbTextFrame)
1339 {
1340 NbcAdjustTextFrameWidthAndHeight();
1341 }
1342 else
1343 {
1344 // the SnapRect keeps its size
1345 SetBoundRectDirty();
1346 SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
1347 }
1348 SetTextSizeDirty();
1349 ActionChanged();
1350 // i22396
1351 // Necessary here since we have no compare operator at the outliner
1352 // para object which may detect changes regarding the combination
1353 // of outliner para data and configuration (e.g., change of
1354 // formatting of text numerals)
1355 GetViewContact().flushViewObjectContacts(false);
1356 }
1357
NewGeoData() const1358 std::unique_ptr<SdrObjGeoData> SdrTextObj::NewGeoData() const
1359 {
1360 return std::make_unique<SdrTextObjGeoData>();
1361 }
1362
SaveGeoData(SdrObjGeoData & rGeo) const1363 void SdrTextObj::SaveGeoData(SdrObjGeoData& rGeo) const
1364 {
1365 SdrAttrObj::SaveGeoData(rGeo);
1366 SdrTextObjGeoData& rTGeo=static_cast<SdrTextObjGeoData&>(rGeo);
1367 rTGeo.maRect = getRectangle();
1368 rTGeo.maGeo = maGeo;
1369 }
1370
RestoreGeoData(const SdrObjGeoData & rGeo)1371 void SdrTextObj::RestoreGeoData(const SdrObjGeoData& rGeo)
1372 { // RectsDirty is called by SdrObject
1373 SdrAttrObj::RestoreGeoData(rGeo);
1374 const SdrTextObjGeoData& rTGeo=static_cast<const SdrTextObjGeoData&>(rGeo);
1375 NbcSetLogicRect(rTGeo.maRect);
1376 maGeo = rTGeo.maGeo;
1377 SetTextSizeDirty();
1378 }
1379
GetFitToSize() const1380 drawing::TextFitToSizeType SdrTextObj::GetFitToSize() const
1381 {
1382 drawing::TextFitToSizeType eType = drawing::TextFitToSizeType_NONE;
1383
1384 if(!IsAutoGrowWidth())
1385 eType = GetObjectItem(SDRATTR_TEXT_FITTOSIZE).GetValue();
1386
1387 return eType;
1388 }
1389
GetGeoRect() const1390 const tools::Rectangle& SdrTextObj::GetGeoRect() const
1391 {
1392 return getRectangle();
1393 }
1394
ForceOutlinerParaObject()1395 void SdrTextObj::ForceOutlinerParaObject()
1396 {
1397 SdrText* pText = getActiveText();
1398 if( pText && (pText->GetOutlinerParaObject() == nullptr) )
1399 {
1400 OutlinerMode nOutlMode = OutlinerMode::TextObject;
1401 if( IsTextFrame() && meTextKind == SdrObjKind::OutlineText )
1402 nOutlMode = OutlinerMode::OutlineObject;
1403
1404 pText->ForceOutlinerParaObject( nOutlMode );
1405 }
1406 }
1407
GetTextChain() const1408 TextChain *SdrTextObj::GetTextChain() const
1409 {
1410 //if (!IsChainable())
1411 // return NULL;
1412
1413 return getSdrModelFromSdrObject().GetTextChain();
1414 }
1415
IsVerticalWriting() const1416 bool SdrTextObj::IsVerticalWriting() const
1417 {
1418 if(mpEditingOutliner)
1419 {
1420 return mpEditingOutliner->IsVertical();
1421 }
1422
1423 OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
1424 if(pOutlinerParaObject)
1425 {
1426 return pOutlinerParaObject->IsEffectivelyVertical();
1427 }
1428
1429 return false;
1430 }
1431
SetVerticalWriting(bool bVertical)1432 void SdrTextObj::SetVerticalWriting(bool bVertical)
1433 {
1434 OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
1435
1436 if( !pOutlinerParaObject && bVertical )
1437 {
1438 // we only need to force an outliner para object if the default of
1439 // horizontal text is changed
1440 ForceOutlinerParaObject();
1441 pOutlinerParaObject = GetOutlinerParaObject();
1442 }
1443
1444 if (!pOutlinerParaObject ||
1445 (pOutlinerParaObject->IsEffectivelyVertical() == bVertical))
1446 return;
1447
1448 // get item settings
1449 const SfxItemSet& rSet = GetObjectItemSet();
1450 bool bAutoGrowWidth = rSet.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue();
1451 bool bAutoGrowHeight = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
1452
1453 // Also exchange hor/ver adjust items
1454 SdrTextHorzAdjust eHorz = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue();
1455 SdrTextVertAdjust eVert = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
1456
1457 // rescue object size
1458 tools::Rectangle aObjectRect = GetSnapRect();
1459
1460 // prepare ItemSet to set exchanged width and height items
1461 SfxItemSet aNewSet(SfxItemSet::makeFixedSfxItemSet<SDRATTR_TEXT_AUTOGROWHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
1462 // Expanded item ranges to also support hor and ver adjust.
1463 SDRATTR_TEXT_VERTADJUST, SDRATTR_TEXT_VERTADJUST,
1464 SDRATTR_TEXT_AUTOGROWWIDTH, SDRATTR_TEXT_HORZADJUST>(*rSet.GetPool()));
1465
1466 aNewSet.Put(rSet);
1467 aNewSet.Put(makeSdrTextAutoGrowWidthItem(bAutoGrowHeight));
1468 aNewSet.Put(makeSdrTextAutoGrowHeightItem(bAutoGrowWidth));
1469
1470 // Exchange horz and vert adjusts
1471 switch (eVert)
1472 {
1473 case SDRTEXTVERTADJUST_TOP: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); break;
1474 case SDRTEXTVERTADJUST_CENTER: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_CENTER)); break;
1475 case SDRTEXTVERTADJUST_BOTTOM: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT)); break;
1476 case SDRTEXTVERTADJUST_BLOCK: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_BLOCK)); break;
1477 }
1478 switch (eHorz)
1479 {
1480 case SDRTEXTHORZADJUST_LEFT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BOTTOM)); break;
1481 case SDRTEXTHORZADJUST_CENTER: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); break;
1482 case SDRTEXTHORZADJUST_RIGHT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP)); break;
1483 case SDRTEXTHORZADJUST_BLOCK: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BLOCK)); break;
1484 }
1485
1486 SetObjectItemSet(aNewSet);
1487
1488 pOutlinerParaObject = GetOutlinerParaObject();
1489 if (pOutlinerParaObject)
1490 {
1491 // set ParaObject orientation accordingly
1492 pOutlinerParaObject->SetVertical(bVertical);
1493 }
1494
1495 // restore object size
1496 SetSnapRect(aObjectRect);
1497 }
1498
IsTopToBottom() const1499 bool SdrTextObj::IsTopToBottom() const
1500 {
1501 if (mpEditingOutliner)
1502 return mpEditingOutliner->IsTopToBottom();
1503
1504 if (OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject())
1505 return pOutlinerParaObject->IsTopToBottom();
1506
1507 return false;
1508 }
1509
1510 // transformation interface for StarOfficeAPI. This implements support for
1511 // homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
1512 // moment it contains a shearX, rotation and translation, but for setting all linear
1513 // transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
1514
1515
1516 // gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
1517 // with the base geometry and returns TRUE. Otherwise it returns FALSE.
TRGetBaseGeometry(basegfx::B2DHomMatrix & rMatrix,basegfx::B2DPolyPolygon &) const1518 bool SdrTextObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
1519 {
1520 // get turn and shear
1521 double fRotate = toRadians(maGeo.m_nRotationAngle);
1522 double fShearX = toRadians(maGeo.m_nShearAngle);
1523
1524 // get aRect, this is the unrotated snaprect
1525 tools::Rectangle aRectangle(getRectangle());
1526
1527 // fill other values
1528 basegfx::B2DTuple aScale(aRectangle.GetWidth(), aRectangle.GetHeight());
1529 basegfx::B2DTuple aTranslate(aRectangle.Left(), aRectangle.Top());
1530
1531 // position maybe relative to anchorpos, convert
1532 if( getSdrModelFromSdrObject().IsWriter() )
1533 {
1534 if(GetAnchorPos().X() || GetAnchorPos().Y())
1535 {
1536 aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
1537 }
1538 }
1539
1540 // build matrix
1541 rMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
1542 aScale,
1543 basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
1544 -fRotate,
1545 aTranslate);
1546
1547 return false;
1548 }
1549
1550 // sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
1551 // If it's an SdrPathObj it will use the provided geometry information. The Polygon has
1552 // to use (0,0) as upper left and will be scaled to the given size in the matrix.
TRSetBaseGeometry(const basegfx::B2DHomMatrix & rMatrix,const basegfx::B2DPolyPolygon &)1553 void SdrTextObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
1554 {
1555 // break up matrix
1556 basegfx::B2DTuple aScale;
1557 basegfx::B2DTuple aTranslate;
1558 double fRotate(0.0);
1559 double fShearX(0.0);
1560 rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
1561
1562 // flip?
1563 bool bFlipX = aScale.getX() < 0.0,
1564 bFlipY = aScale.getY() < 0.0;
1565 if (bFlipX)
1566 {
1567 aScale.setX(fabs(aScale.getX()));
1568 }
1569 if (bFlipY)
1570 {
1571 aScale.setY(fabs(aScale.getY()));
1572 }
1573
1574 // reset object shear and rotations
1575 maGeo.m_nRotationAngle = 0_deg100;
1576 maGeo.RecalcSinCos();
1577 maGeo.m_nShearAngle = 0_deg100;
1578 maGeo.RecalcTan();
1579
1580 // if anchor is used, make position relative to it
1581 if( getSdrModelFromSdrObject().IsWriter() )
1582 {
1583 if(GetAnchorPos().X() || GetAnchorPos().Y())
1584 {
1585 aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
1586 }
1587 }
1588
1589 // build and set BaseRect (use scale)
1590 Size aSize(basegfx::fround<tools::Long>(aScale.getX()),
1591 basegfx::fround<tools::Long>(aScale.getY()));
1592 tools::Rectangle aBaseRect(Point(), aSize);
1593 SetSnapRect(aBaseRect);
1594
1595 // flip?
1596 if (bFlipX)
1597 {
1598 Mirror(Point(), Point(0, 1));
1599 }
1600 if (bFlipY)
1601 {
1602 Mirror(Point(), Point(1, 0));
1603 }
1604
1605 // shear?
1606 if(!basegfx::fTools::equalZero(fShearX))
1607 {
1608 GeoStat aGeoStat;
1609 aGeoStat.m_nShearAngle = Degree100(basegfx::fround(basegfx::rad2deg<100>(atan(fShearX))));
1610 aGeoStat.RecalcTan();
1611 Shear(Point(), aGeoStat.m_nShearAngle, aGeoStat.mfTanShearAngle, false);
1612 }
1613
1614 // rotation?
1615 if(!basegfx::fTools::equalZero(fRotate))
1616 {
1617 GeoStat aGeoStat;
1618
1619 // #i78696#
1620 // fRotate is matematically correct, but aGeoStat.nRotationAngle is
1621 // mirrored -> mirror value here
1622 aGeoStat.m_nRotationAngle = NormAngle36000(Degree100(basegfx::fround(-basegfx::rad2deg<100>(fRotate))));
1623 aGeoStat.RecalcSinCos();
1624 Rotate(Point(), aGeoStat.m_nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
1625 }
1626
1627 // translate?
1628 if(!aTranslate.equalZero())
1629 {
1630 Move(Size(basegfx::fround<tools::Long>(aTranslate.getX()),
1631 basegfx::fround<tools::Long>(aTranslate.getY())));
1632 }
1633 }
1634
IsReallyEdited() const1635 bool SdrTextObj::IsReallyEdited() const
1636 {
1637 return mpEditingOutliner && mpEditingOutliner->IsModified();
1638 }
1639
1640 // moved inlines here form hxx
1641
GetEckenradius() const1642 tools::Long SdrTextObj::GetEckenradius() const
1643 {
1644 return GetObjectItemSet().Get(SDRATTR_CORNER_RADIUS).GetValue();
1645 }
1646
GetMinTextFrameHeight() const1647 tools::Long SdrTextObj::GetMinTextFrameHeight() const
1648 {
1649 return GetObjectItemSet().Get(SDRATTR_TEXT_MINFRAMEHEIGHT).GetValue();
1650 }
1651
GetMaxTextFrameHeight() const1652 tools::Long SdrTextObj::GetMaxTextFrameHeight() const
1653 {
1654 return GetObjectItemSet().Get(SDRATTR_TEXT_MAXFRAMEHEIGHT).GetValue();
1655 }
1656
GetMinTextFrameWidth() const1657 tools::Long SdrTextObj::GetMinTextFrameWidth() const
1658 {
1659 return GetObjectItemSet().Get(SDRATTR_TEXT_MINFRAMEWIDTH).GetValue();
1660 }
1661
GetMaxTextFrameWidth() const1662 tools::Long SdrTextObj::GetMaxTextFrameWidth() const
1663 {
1664 return GetObjectItemSet().Get(SDRATTR_TEXT_MAXFRAMEWIDTH).GetValue();
1665 }
1666
IsFontwork() const1667 bool SdrTextObj::IsFontwork() const
1668 {
1669 return !mbTextFrame // Default is FALSE
1670 && GetObjectItemSet().Get(XATTR_FORMTXTSTYLE).GetValue() != XFormTextStyle::NONE;
1671 }
1672
IsHideContour() const1673 bool SdrTextObj::IsHideContour() const
1674 {
1675 return !mbTextFrame // Default is: no, don't HideContour; HideContour not together with TextFrames
1676 && GetObjectItemSet().Get(XATTR_FORMTXTHIDEFORM).GetValue();
1677 }
1678
IsContourTextFrame() const1679 bool SdrTextObj::IsContourTextFrame() const
1680 {
1681 return !mbTextFrame // ContourFrame not together with normal TextFrames
1682 && GetObjectItemSet().Get(SDRATTR_TEXT_CONTOURFRAME).GetValue();
1683 }
1684
GetTextLeftDistance() const1685 tools::Long SdrTextObj::GetTextLeftDistance() const
1686 {
1687 return GetObjectItemSet().Get(SDRATTR_TEXT_LEFTDIST).GetValue();
1688 }
1689
GetTextRightDistance() const1690 tools::Long SdrTextObj::GetTextRightDistance() const
1691 {
1692 return GetObjectItemSet().Get(SDRATTR_TEXT_RIGHTDIST).GetValue();
1693 }
1694
GetTextUpperDistance() const1695 tools::Long SdrTextObj::GetTextUpperDistance() const
1696 {
1697 return GetObjectItemSet().Get(SDRATTR_TEXT_UPPERDIST).GetValue();
1698 }
1699
GetTextLowerDistance() const1700 tools::Long SdrTextObj::GetTextLowerDistance() const
1701 {
1702 return GetObjectItemSet().Get(SDRATTR_TEXT_LOWERDIST).GetValue();
1703 }
1704
GetTextAniKind() const1705 SdrTextAniKind SdrTextObj::GetTextAniKind() const
1706 {
1707 return GetObjectItemSet().Get(SDRATTR_TEXT_ANIKIND).GetValue();
1708 }
1709
GetTextAniDirection() const1710 SdrTextAniDirection SdrTextObj::GetTextAniDirection() const
1711 {
1712 return GetObjectItemSet().Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
1713 }
1714
HasTextColumnsNumber() const1715 bool SdrTextObj::HasTextColumnsNumber() const
1716 {
1717 return GetObjectItemSet().HasItem(SDRATTR_TEXTCOLUMNS_NUMBER);
1718 }
1719
GetTextColumnsNumber() const1720 sal_Int16 SdrTextObj::GetTextColumnsNumber() const
1721 {
1722 return GetObjectItemSet().Get(SDRATTR_TEXTCOLUMNS_NUMBER).GetValue();
1723 }
1724
SetTextColumnsNumber(sal_Int16 nColumns)1725 void SdrTextObj::SetTextColumnsNumber(sal_Int16 nColumns)
1726 {
1727 SetObjectItem(SfxInt16Item(SDRATTR_TEXTCOLUMNS_NUMBER, nColumns));
1728 }
1729
HasTextColumnsSpacing() const1730 bool SdrTextObj::HasTextColumnsSpacing() const
1731 {
1732 return GetObjectItemSet().HasItem(SDRATTR_TEXTCOLUMNS_SPACING);
1733 }
1734
GetTextColumnsSpacing() const1735 sal_Int32 SdrTextObj::GetTextColumnsSpacing() const
1736 {
1737 return GetObjectItemSet().Get(SDRATTR_TEXTCOLUMNS_SPACING).GetValue();
1738 }
1739
SetTextColumnsSpacing(sal_Int32 nSpacing)1740 void SdrTextObj::SetTextColumnsSpacing(sal_Int32 nSpacing)
1741 {
1742 SetObjectItem(SdrMetricItem(SDRATTR_TEXTCOLUMNS_SPACING, nSpacing));
1743 }
1744
1745 // Get necessary data for text scroll animation. ATM base it on a Text-Metafile and a
1746 // painting rectangle. Rotation is excluded from the returned values.
GetTextScrollMetaFileAndRectangle(tools::Rectangle & rScrollRectangle,tools::Rectangle & rPaintRectangle)1747 GDIMetaFile* SdrTextObj::GetTextScrollMetaFileAndRectangle(
1748 tools::Rectangle& rScrollRectangle, tools::Rectangle& rPaintRectangle)
1749 {
1750 GDIMetaFile* pRetval = nullptr;
1751 SdrOutliner& rOutliner = ImpGetDrawOutliner();
1752 tools::Rectangle aTextRect;
1753 tools::Rectangle aAnchorRect;
1754 tools::Rectangle aPaintRect;
1755 Fraction aFitXCorrection(1,1);
1756 bool bContourFrame(IsContourTextFrame());
1757
1758 // get outliner set up. To avoid getting a somehow rotated MetaFile,
1759 // temporarily disable object rotation.
1760 Degree100 nAngle(maGeo.m_nRotationAngle);
1761 maGeo.m_nRotationAngle = 0_deg100;
1762 ImpSetupDrawOutlinerForPaint( bContourFrame, rOutliner, aTextRect, aAnchorRect, aPaintRect, aFitXCorrection );
1763 maGeo.m_nRotationAngle = nAngle;
1764
1765 tools::Rectangle aScrollFrameRect(aPaintRect);
1766 const SfxItemSet& rSet = GetObjectItemSet();
1767 SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
1768
1769 if(SdrTextAniDirection::Left == eDirection || SdrTextAniDirection::Right == eDirection)
1770 {
1771 aScrollFrameRect.SetLeft( aAnchorRect.Left() );
1772 aScrollFrameRect.SetRight( aAnchorRect.Right() );
1773 }
1774
1775 if(SdrTextAniDirection::Up == eDirection || SdrTextAniDirection::Down == eDirection)
1776 {
1777 aScrollFrameRect.SetTop( aAnchorRect.Top() );
1778 aScrollFrameRect.SetBottom( aAnchorRect.Bottom() );
1779 }
1780
1781 // create the MetaFile
1782 pRetval = new GDIMetaFile;
1783 ScopedVclPtrInstance< VirtualDevice > pBlackHole;
1784 pBlackHole->EnableOutput(false);
1785 pRetval->Record(pBlackHole);
1786 Point aPaintPos = aPaintRect.TopLeft();
1787
1788 rOutliner.DrawText_ToPosition(*pBlackHole, aPaintPos);
1789
1790 pRetval->Stop();
1791 pRetval->WindStart();
1792
1793 // return PaintRectanglePixel and pRetval;
1794 rScrollRectangle = aScrollFrameRect;
1795 rPaintRectangle = aPaintRect;
1796
1797 return pRetval;
1798 }
1799
1800 // Access to TextAnimationAllowed flag
IsAutoFit() const1801 bool SdrTextObj::IsAutoFit() const
1802 {
1803 return GetFitToSize() == drawing::TextFitToSizeType_AUTOFIT;
1804 }
1805
IsFitToSize() const1806 bool SdrTextObj::IsFitToSize() const
1807 {
1808 const drawing::TextFitToSizeType eFit = GetFitToSize();
1809 return (eFit == drawing::TextFitToSizeType_PROPORTIONAL
1810 || eFit == drawing::TextFitToSizeType_ALLLINES);
1811 }
1812
SetTextAnimationAllowed(bool bNew)1813 void SdrTextObj::SetTextAnimationAllowed(bool bNew)
1814 {
1815 if(mbTextAnimationAllowed != bNew)
1816 {
1817 mbTextAnimationAllowed = bNew;
1818 ActionChanged();
1819 }
1820 }
1821
1822 /** called from the SdrObjEditView during text edit when the status of the edit outliner changes */
onEditOutlinerStatusEvent(EditStatus * pEditStatus)1823 void SdrTextObj::onEditOutlinerStatusEvent( EditStatus* pEditStatus )
1824 {
1825 const EditStatusFlags nStat = pEditStatus->GetStatusWord();
1826 const bool bGrowX = bool(nStat & EditStatusFlags::TEXTWIDTHCHANGED);
1827 const bool bGrowY = bool(nStat & EditStatusFlags::TextHeightChanged);
1828 if(!(mbTextFrame && (bGrowX || bGrowY)))
1829 return;
1830
1831 if ((bGrowX && IsAutoGrowWidth()) || (bGrowY && IsAutoGrowHeight()))
1832 {
1833 AdjustTextFrameWidthAndHeight();
1834 }
1835 else if ( (IsAutoFit() || IsFitToSize()) && !mbInDownScale)
1836 {
1837 assert(mpEditingOutliner);
1838 mbInDownScale = true;
1839
1840 // Need to reset scaling so it searches for the fitting size again
1841 mpEditingOutliner->resetScalingParameters();
1842
1843 // sucks that we cannot disable paints via
1844 // mpEditingOutliner->SetUpdateMode(FALSE) - but EditEngine skips
1845 // formatting as well, then.
1846 setupAutoFitText(*mpEditingOutliner);
1847 mbInDownScale = false;
1848 }
1849 }
1850
1851 /* Begin chaining code */
1852
1853 // XXX: Make it a method somewhere?
ImpGetObjByName(SdrObjList const * pObjList,std::u16string_view aObjName)1854 static SdrObject *ImpGetObjByName(SdrObjList const *pObjList, std::u16string_view aObjName)
1855 {
1856 // scan the whole list
1857 for (const rtl::Reference<SdrObject>& pCurObj : *pObjList)
1858 if (pCurObj->GetName() == aObjName)
1859 return pCurObj.get();
1860 // not found
1861 return nullptr;
1862 }
1863
1864 // XXX: Make it a (private) method of SdrTextObj
ImpUpdateChainLinks(SdrTextObj * pTextObj,std::u16string_view aNextLinkName)1865 static void ImpUpdateChainLinks(SdrTextObj *pTextObj, std::u16string_view aNextLinkName)
1866 {
1867 // XXX: Current implementation constraints text boxes to be on the same page
1868
1869 // No next link
1870 if (aNextLinkName.empty()) {
1871 pTextObj->SetNextLinkInChain(nullptr);
1872 return;
1873 }
1874
1875 SdrPage *pPage(pTextObj->getSdrPageFromSdrObject());
1876 assert(pPage);
1877 SdrTextObj *pNextTextObj = DynCastSdrTextObj
1878 (ImpGetObjByName(pPage, aNextLinkName));
1879 if (!pNextTextObj) {
1880 SAL_INFO("svx.chaining", "[CHAINING] Can't find object as next link.");
1881 return;
1882 }
1883
1884 pTextObj->SetNextLinkInChain(pNextTextObj);
1885 }
1886
IsChainable() const1887 bool SdrTextObj::IsChainable() const
1888 {
1889 // Read it as item
1890 const SfxItemSet& rSet = GetObjectItemSet();
1891 OUString aNextLinkName = rSet.Get(SDRATTR_TEXT_CHAINNEXTNAME).GetValue();
1892
1893 // Update links if any inconsistency is found
1894 bool bNextLinkUnsetYet = !aNextLinkName.isEmpty() && !mpNextInChain;
1895 bool bInconsistentNextLink = mpNextInChain && mpNextInChain->GetName() != aNextLinkName;
1896 // if the link is not set despite there should be one OR if it has changed
1897 if (bNextLinkUnsetYet || bInconsistentNextLink) {
1898 ImpUpdateChainLinks(const_cast<SdrTextObj *>(this), aNextLinkName);
1899 }
1900
1901 return !aNextLinkName.isEmpty(); // XXX: Should we also check for GetNilChainingEvent? (see old code below)
1902
1903 /*
1904 // Check that no overflow is going on
1905 if (!GetTextChain() || GetTextChain()->GetNilChainingEvent(this))
1906 return false;
1907 */
1908 }
1909
onChainingEvent()1910 void SdrTextObj::onChainingEvent()
1911 {
1912 if (!mpEditingOutliner)
1913 return;
1914
1915 // Outliner for text transfer
1916 SdrOutliner &aDrawOutliner = ImpGetDrawOutliner();
1917
1918 EditingTextChainFlow aTxtChainFlow(this);
1919 aTxtChainFlow.CheckForFlowEvents(mpEditingOutliner);
1920
1921 if (aTxtChainFlow.IsOverflow()) {
1922 SAL_INFO("svx.chaining", "[CHAINING] Overflow going on");
1923 // One outliner is for non-overflowing text, the other for overflowing text
1924 // We remove text directly from the editing outliner
1925 aTxtChainFlow.ExecuteOverflow(mpEditingOutliner, &aDrawOutliner);
1926 } else if (aTxtChainFlow.IsUnderflow()) {
1927 SAL_INFO("svx.chaining", "[CHAINING] Underflow going on");
1928 // underflow-induced overflow
1929 aTxtChainFlow.ExecuteUnderflow(&aDrawOutliner);
1930 bool bIsOverflowFromUnderflow = aTxtChainFlow.IsOverflow();
1931 // handle overflow
1932 if (bIsOverflowFromUnderflow) {
1933 SAL_INFO("svx.chaining", "[CHAINING] Overflow going on (underflow induced)");
1934 // prevents infinite loops when setting text for editing outliner
1935 aTxtChainFlow.ExecuteOverflow(&aDrawOutliner, &aDrawOutliner);
1936 }
1937 }
1938 }
1939
GetNextLinkInChain() const1940 SdrTextObj* SdrTextObj::GetNextLinkInChain() const
1941 {
1942 /*
1943 if (GetTextChain())
1944 return GetTextChain()->GetNextLink(this);
1945
1946 return NULL;
1947 */
1948
1949 return mpNextInChain;
1950 }
1951
SetNextLinkInChain(SdrTextObj * pNextObj)1952 void SdrTextObj::SetNextLinkInChain(SdrTextObj *pNextObj)
1953 {
1954 // Basically a doubly linked list implementation
1955
1956 SdrTextObj *pOldNextObj = mpNextInChain;
1957
1958 // Replace next link
1959 mpNextInChain = pNextObj;
1960 // Deal with old next link's prev link
1961 if (pOldNextObj) {
1962 pOldNextObj->mpPrevInChain = nullptr;
1963 }
1964
1965 // Deal with new next link's prev link
1966 if (mpNextInChain) {
1967 // If there is a prev already at all and this is not already the current object
1968 if (mpNextInChain->mpPrevInChain &&
1969 mpNextInChain->mpPrevInChain != this)
1970 mpNextInChain->mpPrevInChain->mpNextInChain = nullptr;
1971 mpNextInChain->mpPrevInChain = this;
1972 }
1973
1974 // TODO: Introduce check for circular chains
1975
1976 }
1977
GetPrevLinkInChain() const1978 SdrTextObj* SdrTextObj::GetPrevLinkInChain() const
1979 {
1980 /*
1981 if (GetTextChain())
1982 return GetTextChain()->GetPrevLink(this);
1983
1984 return NULL;
1985 */
1986
1987 return mpPrevInChain;
1988 }
1989
GetPreventChainable() const1990 bool SdrTextObj::GetPreventChainable() const
1991 {
1992 // Prevent chaining it 1) during dragging && 2) when we are editing next link
1993 return mbIsUnchainableClone || (GetNextLinkInChain() && GetNextLinkInChain()->IsInEditMode());
1994 }
1995
getFullDragClone() const1996 rtl::Reference<SdrObject> SdrTextObj::getFullDragClone() const
1997 {
1998 rtl::Reference<SdrObject> pClone = SdrAttrObj::getFullDragClone();
1999 SdrTextObj *pTextObjClone = DynCastSdrTextObj(pClone.get());
2000 if (pTextObjClone != nullptr) {
2001 // Avoid transferring of text for chainable object during dragging
2002 pTextObjClone->mbIsUnchainableClone = true;
2003 }
2004
2005 return pClone;
2006 }
2007
2008 /* End chaining code */
2009
2010 /** returns the currently active text. */
getActiveText() const2011 SdrText* SdrTextObj::getActiveText() const
2012 {
2013 if( !mxText )
2014 return getText( 0 );
2015 else
2016 return mxText.get();
2017 }
2018
2019 /** returns the nth available text. */
getText(sal_Int32 nIndex) const2020 SdrText* SdrTextObj::getText( sal_Int32 nIndex ) const
2021 {
2022 if( nIndex == 0 )
2023 {
2024 if( !mxText )
2025 const_cast< SdrTextObj* >(this)->mxText = new SdrText( *const_cast< SdrTextObj* >(this) );
2026 return mxText.get();
2027 }
2028 else
2029 {
2030 return nullptr;
2031 }
2032 }
2033
2034 /** returns the number of texts available for this object. */
getTextCount() const2035 sal_Int32 SdrTextObj::getTextCount() const
2036 {
2037 return 1;
2038 }
2039
2040 /** changes the current active text */
setActiveText(sal_Int32)2041 void SdrTextObj::setActiveText( sal_Int32 /*nIndex*/ )
2042 {
2043 }
2044
2045 /** returns the index of the text that contains the given point or -1 */
CheckTextHit(const Point &) const2046 sal_Int32 SdrTextObj::CheckTextHit(const Point& /*rPnt*/) const
2047 {
2048 return 0;
2049 }
2050
SetObjectItemNoBroadcast(const SfxPoolItem & rItem)2051 void SdrTextObj::SetObjectItemNoBroadcast(const SfxPoolItem& rItem)
2052 {
2053 static_cast< sdr::properties::TextProperties& >(GetProperties()).SetObjectItemNoBroadcast(rItem);
2054 }
2055
2056
2057 // The concept of the text object:
2058 // ~~~~~~~~~~~~~~~~~~~~~~~~
2059 // Attributes/Variations:
2060 // - bool text frame / graphics object with caption
2061 // - bool FontWork (if it is not a text frame and not a ContourTextFrame)
2062 // - bool ContourTextFrame (if it is not a text frame and not Fontwork)
2063 // - long rotation angle (if it is not FontWork)
2064 // - long text frame margins (if it is not FontWork)
2065 // - bool FitToSize (if it is not FontWork)
2066 // - bool AutoGrowingWidth/Height (if it is not FitToSize and not FontWork)
2067 // - long Min/MaxFrameWidth/Height (if AutoGrowingWidth/Height)
2068 // - enum horizontal text anchoring left,center,right,justify/block,Stretch(ni)
2069 // - enum vertical text anchoring top, middle, bottom, block, stretch(ni)
2070 // - enum ticker text (if it is not FontWork)
2071
2072 // Every derived object is either a text frame (mbTextFrame=true)
2073 // or a drawing object with a caption (mbTextFrame=false).
2074
2075 // Default anchoring for text frames:
2076 // SDRTEXTHORZADJUST_BLOCK, SDRTEXTVERTADJUST_TOP
2077 // = static Pool defaults
2078 // Default anchoring for drawing objects with a caption:
2079 // SDRTEXTHORZADJUST_CENTER, SDRTEXTVERTADJUST_CENTER
2080 // via "hard" attribution of SdrAttrObj
2081
2082 // Every object derived from SdrTextObj must return an "UnrotatedSnapRect"
2083 // (->TakeUnrotatedSnapRect()) (the reference point for the rotation is the top
2084 // left of the rectangle (maGeo.nRotationAngle)) which is the basis for anchoring
2085 // text. We then subtract the text frame margins from this rectangle, as a re-
2086 // sult we get the anchoring area (->TakeTextAnchorRect()). Within this area, we
2087 // calculate the anchoring point and the painting area, depending on the hori-
2088 // zontal and vertical adjustment of the text (SdrTextVertAdjust,
2089 // SdrTextHorzAdjust).
2090 // In the case of drawing objects with a caption the painting area might well
2091 // be larger than the anchoring area, for text frames on the other hand, it is
2092 // always of the same or a smaller size (except when there are negative text
2093 // frame margins).
2094
2095 // FitToSize takes priority over text anchoring and AutoGrowHeight/Width. When
2096 // FitToSize is turned on, the painting area is always equal to the anchoring
2097 // area. Additionally, FitToSize doesn't allow automatic line breaks.
2098
2099 // ContourTextFrame:
2100 // - long rotation angle
2101 // - long text frame margins (maybe later)
2102 // - bool FitToSize (maybe later)
2103 // - bool AutoGrowingWidth/Height (maybe much later)
2104 // - long Min/MaxFrameWidth/Height (maybe much later)
2105 // - enum horizontal text anchoring (maybe later, for now: left, centered)
2106 // - enum vertical text anchoring (maybe later, for now: top)
2107 // - enum ticker text (maybe later, maybe even with correct clipping)
2108
2109 // When making changes, check these:
2110 // - Paint
2111 // - HitTest
2112 // - ConvertToPoly
2113 // - Edit
2114 // - Printing, Saving, Painting in neighboring View while editing
2115 // - ModelChanged (e. g. through a neighboring View or rulers) while editing
2116 // - FillColorChanged while editing
2117 // - and many more...
2118
2119
2120 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2121