1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
20 #include <sal/config.h>
24 #include <basegfx/matrix/b2dhommatrix.hxx>
25 #include <basegfx/polygon/b2dpolygon.hxx>
26 #include <basegfx/polygon/b2dpolygontools.hxx>
27 #include <basegfx/range/b2drange.hxx>
28 #include <basegfx/tuple/b2dtuple.hxx>
29 #include <tools/bigint.hxx>
30 #include <tools/helpers.hxx>
31 #include <rtl/ustrbuf.hxx>
33 #include <svx/dialmgr.hxx>
34 #include <svx/strings.hrc>
36 #include <sdr/contact/viewcontactofsdrcaptionobj.hxx>
37 #include <sdr/properties/captionproperties.hxx>
38 #include <svx/sdrhittesthelper.hxx>
39 #include <svx/sdooitm.hxx>
40 #include <svx/svddrag.hxx>
41 #include <svx/svdhdl.hxx>
42 #include <svx/svdmodel.hxx>
43 #include <svx/svdocapt.hxx>
44 #include <svx/svdopath.hxx>
45 #include <svx/svdogrp.hxx>
46 #include <svx/svdpage.hxx>
47 #include <svx/svdtrans.hxx>
48 #include <svx/svdview.hxx>
49 #include <svx/sxcecitm.hxx>
50 #include <svx/sxcgitm.hxx>
51 #include <svx/sxcllitm.hxx>
52 #include <svx/sxctitm.hxx>
53 #include <vcl/canvastools.hxx>
54 #include <vcl/ptrstyle.hxx>
58 enum EscDir
{LKS
,RTS
,OBN
,UNT
};
70 SdrCaptionEscDir eEscDir
;
77 : eType(SdrCaptionType::Type3
),
78 nGap(0), nEscRel(5000), nEscAbs(0),
79 nLineLen(0), eEscDir(SdrCaptionEscDir::Horizontal
),
80 bFitLineLen(true), bEscRel(true), bFixedAngle(false)
83 void CalcEscPos(const Point
& rTail
, const tools::Rectangle
& rRect
, Point
& rPt
, EscDir
& rDir
) const;
86 void ImpCaptParams::CalcEscPos(const Point
& rTailPt
, const tools::Rectangle
& rRect
, Point
& rPt
, EscDir
& rDir
) const
88 Point
aTl(rTailPt
); // copy locally for performance reasons
91 nX
=rRect
.Right()-rRect
.Left();
92 nX
=BigMulDiv(nX
,nEscRel
,10000);
93 nY
=rRect
.Bottom()-rRect
.Top();
94 nY
=BigMulDiv(nY
,nEscRel
,10000);
103 bool bTryH
=eEscDir
==SdrCaptionEscDir::BestFit
;
105 if (eType
!=SdrCaptionType::Type1
) {
106 bTryH
=eEscDir
==SdrCaptionEscDir::Horizontal
;
108 bTryH
=eEscDir
==SdrCaptionEscDir::Vertical
;
111 bool bTryV
=eEscDir
==SdrCaptionEscDir::BestFit
;
113 if (eType
!=SdrCaptionType::Type1
) {
114 bTryV
=eEscDir
==SdrCaptionEscDir::Vertical
;
116 bTryV
=eEscDir
==SdrCaptionEscDir::Horizontal
;
121 Point
aLft(rRect
.Left()-nGap
,nY
);
122 Point
aRgt(rRect
.Right()+nGap
,nY
);
123 bool bLft
=(aTl
.X()-aLft
.X()<aRgt
.X()-aTl
.X());
133 Point
aTop(nX
,rRect
.Top()-nGap
);
134 Point
aBtm(nX
,rRect
.Bottom()+nGap
);
135 bool bTop
=(aTl
.Y()-aTop
.Y()<aBtm
.Y()-aTl
.Y());
145 bool bTakeIt
=eEscDir
!=SdrCaptionEscDir::BestFit
;
147 BigInt
aHorX(aBestPt
.X()-aTl
.X()); aHorX
*=aHorX
;
148 BigInt
aHorY(aBestPt
.Y()-aTl
.Y()); aHorY
*=aHorY
;
149 BigInt
aVerX(aBest2
.X()-aTl
.X()); aVerX
*=aVerX
;
150 BigInt
aVerY(aBest2
.Y()-aTl
.Y()); aVerY
*=aVerY
;
151 if (eType
!=SdrCaptionType::Type1
) {
152 bTakeIt
=aVerX
+aVerY
<aHorX
+aHorY
;
154 bTakeIt
=aVerX
+aVerY
>=aHorX
+aHorY
;
167 // BaseProperties section
169 std::unique_ptr
<sdr::properties::BaseProperties
> SdrCaptionObj::CreateObjectSpecificProperties()
171 return std::make_unique
<sdr::properties::CaptionProperties
>(*this);
175 // DrawContact section
177 std::unique_ptr
<sdr::contact::ViewContact
> SdrCaptionObj::CreateObjectSpecificViewContact()
179 return std::make_unique
<sdr::contact::ViewContactOfSdrCaptionObj
>(*this);
183 SdrCaptionObj::SdrCaptionObj(SdrModel
& rSdrModel
)
184 : SdrRectObj(rSdrModel
, OBJ_TEXT
),
185 aTailPoly(3), // default size: 3 points = 2 lines
186 mbSpecialTextBoxShadow(false),
188 mbSuppressGetBitmap(false),
193 SdrCaptionObj::SdrCaptionObj(
195 const tools::Rectangle
& rRect
,
197 : SdrRectObj(rSdrModel
, OBJ_TEXT
,rRect
),
198 aTailPoly(3), // default size: 3 points = 2 lines
199 mbSpecialTextBoxShadow(false),
201 mbSuppressGetBitmap(false),
204 aTailPoly
[0]=maFixedTailPos
=rTail
;
207 SdrCaptionObj::~SdrCaptionObj()
211 void SdrCaptionObj::TakeObjInfo(SdrObjTransformInfoRec
& rInfo
) const
213 rInfo
.bRotateFreeAllowed
=false;
214 rInfo
.bRotate90Allowed
=false;
215 rInfo
.bMirrorFreeAllowed
=false;
216 rInfo
.bMirror45Allowed
=false;
217 rInfo
.bMirror90Allowed
=false;
218 rInfo
.bTransparenceAllowed
= false;
219 rInfo
.bShearAllowed
=false;
220 rInfo
.bEdgeRadiusAllowed
=false;
221 rInfo
.bCanConvToPath
=true;
222 rInfo
.bCanConvToPoly
=true;
223 rInfo
.bCanConvToPathLineToArea
=false;
224 rInfo
.bCanConvToPolyLineToArea
=false;
225 rInfo
.bCanConvToContour
= (rInfo
.bCanConvToPoly
|| LineGeometryUsageIsNecessary());
228 SdrObjKind
SdrCaptionObj::GetObjIdentifier() const
233 SdrCaptionObj
* SdrCaptionObj::CloneSdrObject(SdrModel
& rTargetModel
) const
235 return CloneHelper
< SdrCaptionObj
>(rTargetModel
);
238 SdrCaptionObj
& SdrCaptionObj::operator=(const SdrCaptionObj
& rObj
)
242 SdrRectObj::operator=(rObj
);
244 aTailPoly
= rObj
.aTailPoly
;
245 mbSpecialTextBoxShadow
= rObj
.mbSpecialTextBoxShadow
;
246 mbFixedTail
= rObj
.mbFixedTail
;
247 maFixedTailPos
= rObj
.maFixedTailPos
;
252 OUString
SdrCaptionObj::TakeObjNameSingul() const
254 OUStringBuffer
sName(SvxResId(STR_ObjNameSingulCAPTION
));
256 OUString
aName(GetName());
257 if (!aName
.isEmpty())
265 return sName
.makeStringAndClear();
268 OUString
SdrCaptionObj::TakeObjNamePlural() const
270 return SvxResId(STR_ObjNamePluralCAPTION
);
273 basegfx::B2DPolyPolygon
SdrCaptionObj::TakeXorPoly() const
275 basegfx::B2DPolyPolygon
aPolyPoly(SdrRectObj::TakeXorPoly());
276 aPolyPoly
.append(aTailPoly
.getB2DPolygon());
281 sal_uInt32
SdrCaptionObj::GetHdlCount() const
283 sal_uInt32
nCount1(SdrRectObj::GetHdlCount());
284 // Currently only dragging the tail's end is implemented.
288 void SdrCaptionObj::AddToHdlList(SdrHdlList
& rHdlList
) const
290 SdrRectObj::AddToHdlList(rHdlList
);
291 // Currently only dragging the tail's end is implemented.
292 std::unique_ptr
<SdrHdl
> pHdl(new SdrHdl(aTailPoly
.GetPoint(0), SdrHdlKind::Poly
));
294 pHdl
->SetPointNum(0);
295 rHdlList
.AddHdl(std::move(pHdl
));
298 bool SdrCaptionObj::hasSpecialDrag() const
303 bool SdrCaptionObj::beginSpecialDrag(SdrDragStat
& rDrag
) const
305 const SdrHdl
* pHdl
= rDrag
.GetHdl();
306 rDrag
.SetEndDragChangesAttributes(true);
307 rDrag
.SetEndDragChangesGeoAndAttributes(true);
309 if(pHdl
&& 0 == pHdl
->GetPolyNum())
311 return SdrRectObj::beginSpecialDrag(rDrag
);
315 rDrag
.SetOrtho8Possible();
323 rDrag
.SetActionRect(maRect
);
325 Point
aHit(rDrag
.GetStart());
327 if(rDrag
.GetPageView() && SdrObjectPrimitiveHit(*this, aHit
, 0, *rDrag
.GetPageView(), nullptr, false))
334 if((1 == pHdl
->GetPolyNum()) && (0 == pHdl
->GetPointNum()))
342 bool SdrCaptionObj::applySpecialDrag(SdrDragStat
& rDrag
)
344 const SdrHdl
* pHdl
= rDrag
.GetHdl();
346 if(pHdl
&& 0 == pHdl
->GetPolyNum())
348 const bool bRet(SdrRectObj::applySpecialDrag(rDrag
));
356 Point
aDelt(rDrag
.GetNow()-rDrag
.GetStart());
360 maRect
.Move(aDelt
.X(),aDelt
.Y());
364 aTailPoly
[0] += aDelt
;
374 OUString
SdrCaptionObj::getSpecialDragComment(const SdrDragStat
& rDrag
) const
376 const bool bCreateComment(rDrag
.GetView() && this == rDrag
.GetView()->GetCreateObj());
384 const SdrHdl
* pHdl
= rDrag
.GetHdl();
386 if(pHdl
&& 0 == pHdl
->GetPolyNum())
388 return SdrRectObj::getSpecialDragComment(rDrag
);
394 return ImpGetDescriptionStr(STR_DragCaptFram
);
398 return ImpGetDescriptionStr(STR_DragCaptTail
);
405 void SdrCaptionObj::ImpGetCaptParams(ImpCaptParams
& rPara
) const
407 const SfxItemSet
& rSet
= GetObjectItemSet();
408 rPara
.eType
=rSet
.Get(SDRATTR_CAPTIONTYPE
).GetValue();
409 rPara
.bFixedAngle
=rSet
.Get(SDRATTR_CAPTIONFIXEDANGLE
).GetValue();
410 rPara
.nGap
=static_cast<const SdrCaptionGapItem
&> (rSet
.Get(SDRATTR_CAPTIONGAP
)).GetValue();
411 rPara
.eEscDir
=rSet
.Get(SDRATTR_CAPTIONESCDIR
).GetValue();
412 rPara
.bEscRel
=rSet
.Get(SDRATTR_CAPTIONESCISREL
).GetValue();
413 rPara
.nEscRel
=rSet
.Get(SDRATTR_CAPTIONESCREL
).GetValue();
414 rPara
.nEscAbs
=rSet
.Get(SDRATTR_CAPTIONESCABS
).GetValue();
415 rPara
.nLineLen
=rSet
.Get(SDRATTR_CAPTIONLINELEN
).GetValue();
416 rPara
.bFitLineLen
=rSet
.Get(SDRATTR_CAPTIONFITLINELEN
).GetValue();
419 void SdrCaptionObj::ImpRecalcTail()
422 ImpGetCaptParams(aPara
);
423 ImpCalcTail(aPara
, aTailPoly
, maRect
);
429 // SdrCaptionObj::ImpCalcTail1 does move the object(!). What a hack.
430 // I really wonder why this had not triggered problems before. I am
431 // sure there are some places where SetTailPos() is called at least
432 // twice or SetSnapRect after it again just to work around this.
433 // Changed this method to not do that.
434 // Also found why this has been done: For interactive dragging of the
435 // tail end pos for SdrCaptionType::Type1. This sure was the simplest method
436 // to achieve this, at the cost of making a whole group of const methods
437 // of this object implicitly change the object's position.
438 void SdrCaptionObj::ImpCalcTail1(const ImpCaptParams
& rPara
, tools::Polygon
& rPoly
, tools::Rectangle
const & rRect
)
440 tools::Polygon
aPol(2);
449 rPara
.CalcEscPos(aTl
, rRect
, aEscPos
, eEscDir
);
452 if(eEscDir
==LKS
|| eEscDir
==RTS
)
454 aPol
[0].setX( aEscPos
.X() );
458 aPol
[0].setY( aEscPos
.Y() );
464 void SdrCaptionObj::ImpCalcTail2(const ImpCaptParams
& rPara
, tools::Polygon
& rPoly
, tools::Rectangle
const & rRect
)
465 { // Gap/EscDir/EscPos/Angle
466 tools::Polygon
aPol(2);
472 rPara
.CalcEscPos(aTl
,rRect
,aEscPos
,eEscDir
);
475 if (!rPara
.bFixedAngle
) {
476 // TODO: Implementation missing.
481 void SdrCaptionObj::ImpCalcTail3(const ImpCaptParams
& rPara
, tools::Polygon
& rPoly
, tools::Rectangle
const & rRect
)
482 { // Gap/EscDir/EscPos/Angle/LineLen
483 tools::Polygon
aPol(3);
489 rPara
.CalcEscPos(aTl
,rRect
,aEscPos
,eEscDir
);
493 if (eEscDir
==LKS
|| eEscDir
==RTS
) {
494 if (rPara
.bFitLineLen
) {
495 aPol
[1].setX((aTl
.X()+aEscPos
.X())/2 );
497 if (eEscDir
==LKS
) aPol
[1].AdjustX( -(rPara
.nLineLen
) );
498 else aPol
[1].AdjustX(rPara
.nLineLen
);
501 if (rPara
.bFitLineLen
) {
502 aPol
[1].setY((aTl
.Y()+aEscPos
.Y())/2 );
504 if (eEscDir
==OBN
) aPol
[1].AdjustY( -(rPara
.nLineLen
) );
505 else aPol
[1].AdjustY(rPara
.nLineLen
);
508 if (!rPara
.bFixedAngle
) {
509 // TODO: Implementation missing.
514 void SdrCaptionObj::ImpCalcTail(const ImpCaptParams
& rPara
, tools::Polygon
& rPoly
, tools::Rectangle
const & rRect
)
516 switch (rPara
.eType
) {
517 case SdrCaptionType::Type1
: ImpCalcTail1(rPara
,rPoly
,rRect
); break;
518 case SdrCaptionType::Type2
: ImpCalcTail2(rPara
,rPoly
,rRect
); break;
519 case SdrCaptionType::Type3
: ImpCalcTail3(rPara
,rPoly
,rRect
); break;
520 case SdrCaptionType::Type4
: ImpCalcTail3(rPara
,rPoly
,rRect
); break;
524 bool SdrCaptionObj::BegCreate(SdrDragStat
& rStat
)
526 if (maRect
.IsEmpty()) return false; // Create currently only works with the given Rect
529 ImpGetCaptParams(aPara
);
530 maRect
.SetPos(rStat
.GetNow());
531 aTailPoly
[0]=rStat
.GetStart();
532 ImpCalcTail(aPara
,aTailPoly
,maRect
);
533 rStat
.SetActionRect(maRect
);
537 bool SdrCaptionObj::MovCreate(SdrDragStat
& rStat
)
540 ImpGetCaptParams(aPara
);
541 maRect
.SetPos(rStat
.GetNow());
542 ImpCalcTail(aPara
,aTailPoly
,maRect
);
543 rStat
.SetActionRect(maRect
);
549 bool SdrCaptionObj::EndCreate(SdrDragStat
& rStat
, SdrCreateCmd eCmd
)
552 ImpGetCaptParams(aPara
);
553 maRect
.SetPos(rStat
.GetNow());
554 ImpCalcTail(aPara
,aTailPoly
,maRect
);
556 return (eCmd
==SdrCreateCmd::ForceEnd
|| rStat
.GetPointCount()>=2);
559 bool SdrCaptionObj::BckCreate(SdrDragStat
& /*rStat*/)
564 void SdrCaptionObj::BrkCreate(SdrDragStat
& /*rStat*/)
568 basegfx::B2DPolyPolygon
SdrCaptionObj::TakeCreatePoly(const SdrDragStat
& /*rDrag*/) const
570 basegfx::B2DPolyPolygon aRetval
;
571 const basegfx::B2DRange aRange
=vcl::unotools::b2DRectangleFromRectangle(maRect
);
572 aRetval
.append(basegfx::utils::createPolygonFromRect(aRange
));
573 aRetval
.append(aTailPoly
.getB2DPolygon());
577 PointerStyle
SdrCaptionObj::GetCreatePointer() const
579 return PointerStyle::DrawCaption
;
582 void SdrCaptionObj::NbcMove(const Size
& rSiz
)
584 SdrRectObj::NbcMove(rSiz
);
585 MovePoly(aTailPoly
,rSiz
);
587 SetTailPos(GetFixedTailPos());
590 void SdrCaptionObj::NbcResize(const Point
& rRef
, const Fraction
& xFact
, const Fraction
& yFact
)
592 SdrRectObj::NbcResize(rRef
,xFact
,yFact
);
593 ResizePoly(aTailPoly
,rRef
,xFact
,yFact
);
596 SetTailPos(GetFixedTailPos());
599 void SdrCaptionObj::NbcSetRelativePos(const Point
& rPnt
)
601 Point
aRelPos0(aTailPoly
.GetPoint(0)-aAnchor
);
602 Size
aSiz(rPnt
.X()-aRelPos0
.X(),rPnt
.Y()-aRelPos0
.Y());
603 NbcMove(aSiz
); // This also calls SetRectsDirty()
606 Point
SdrCaptionObj::GetRelativePos() const
608 return aTailPoly
.GetPoint(0)-aAnchor
;
611 const tools::Rectangle
& SdrCaptionObj::GetLogicRect() const
616 void SdrCaptionObj::NbcSetLogicRect(const tools::Rectangle
& rRect
)
618 SdrRectObj::NbcSetLogicRect(rRect
);
622 const Point
& SdrCaptionObj::GetTailPos() const
627 void SdrCaptionObj::SetTailPos(const Point
& rPos
)
629 if (aTailPoly
.GetSize()==0 || aTailPoly
[0]!=rPos
) {
630 tools::Rectangle aBoundRect0
; if (pUserCall
!=nullptr) aBoundRect0
=GetLastBoundRect();
633 BroadcastObjectChange();
634 SendUserCall(SdrUserCallType::Resize
,aBoundRect0
);
638 void SdrCaptionObj::NbcSetTailPos(const Point
& rPos
)
644 sal_uInt32
SdrCaptionObj::GetSnapPointCount() const
646 // TODO: Implementation missing.
650 Point
SdrCaptionObj::GetSnapPoint(sal_uInt32
/*i*/) const
652 // TODO: Implementation missing.
656 void SdrCaptionObj::Notify(SfxBroadcaster
& rBC
, const SfxHint
& rHint
)
658 SdrRectObj::Notify(rBC
,rHint
);
662 SdrObjGeoData
* SdrCaptionObj::NewGeoData() const
664 return new SdrCaptObjGeoData
;
667 void SdrCaptionObj::SaveGeoData(SdrObjGeoData
& rGeo
) const
669 SdrRectObj::SaveGeoData(rGeo
);
670 SdrCaptObjGeoData
& rCGeo
=static_cast<SdrCaptObjGeoData
&>(rGeo
);
671 rCGeo
.aTailPoly
=aTailPoly
;
674 void SdrCaptionObj::RestGeoData(const SdrObjGeoData
& rGeo
)
676 SdrRectObj::RestGeoData(rGeo
);
677 const SdrCaptObjGeoData
& rCGeo
=static_cast<const SdrCaptObjGeoData
&>(rGeo
);
678 aTailPoly
=rCGeo
.aTailPoly
;
681 SdrObjectUniquePtr
SdrCaptionObj::DoConvertToPolyObj(bool bBezier
, bool bAddText
) const
683 SdrObjectUniquePtr pRect
= SdrRectObj::DoConvertToPolyObj(bBezier
, bAddText
);
684 SdrObjectUniquePtr pTail
= ImpConvertMakeObj(basegfx::B2DPolyPolygon(aTailPoly
.getB2DPolygon()), false, bBezier
);
685 SdrObjectUniquePtr pRet
;
687 pRet
= std::move(pTail
);
688 else if (pRect
&& !pTail
)
689 pRet
= std::move(pRect
);
690 else if (pTail
&& pRect
)
692 if (pTail
->GetSubList())
694 pTail
->GetSubList()->NbcInsertObject(pRect
.release());
695 pRet
= std::move(pTail
);
697 else if (pRect
->GetSubList())
699 pRect
->GetSubList()->NbcInsertObject(pTail
.release(),0);
700 pRet
= std::move(pRect
);
704 SdrObjGroup
* pGrp
= new SdrObjGroup(getSdrModelFromSdrObject());
705 pGrp
->GetSubList()->NbcInsertObject(pRect
.release());
706 pGrp
->GetSubList()->NbcInsertObject(pTail
.release(),0);
715 void handleNegativeScale(basegfx::B2DTuple
& scale
, double * rotate
) {
716 assert(rotate
!= nullptr);
718 // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
719 // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
720 if(basegfx::fTools::less(scale
.getX(), 0.0) && basegfx::fTools::less(scale
.getY(), 0.0))
722 scale
.setX(fabs(scale
.getX()));
723 scale
.setY(fabs(scale
.getY()));
724 *rotate
= fmod(*rotate
+ F_PI
, F_2PI
);
731 // Add own implementation for TRSetBaseGeometry to handle TailPos over changes.
732 void SdrCaptionObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::B2DPolyPolygon
& /*rPolyPolygon*/)
735 basegfx::B2DTuple aScale
;
736 basegfx::B2DTuple aTranslate
;
737 double fRotate
, fShearX
;
738 rMatrix
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
740 handleNegativeScale(aScale
, &fRotate
);
742 // if anchor is used, make position relative to it
743 if(getSdrModelFromSdrObject().IsWriter())
745 if(GetAnchorPos().X() || GetAnchorPos().Y())
747 aTranslate
+= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
752 Point
aPoint(FRound(aTranslate
.getX()), FRound(aTranslate
.getY()));
753 tools::Rectangle
aBaseRect(aPoint
, Size(FRound(aScale
.getX()), FRound(aScale
.getY())));
755 // set BaseRect, but rescue TailPos over this call
756 const Point aTailPoint
= GetTailPos();
757 SetSnapRect(aBaseRect
);
758 SetTailPos(aTailPoint
);
763 basegfx::B2DPolygon
SdrCaptionObj::getTailPolygon() const
765 return aTailPoly
.getB2DPolygon();
768 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */