if(nullptr != GetConnection(true).GetSdrObject() || nullptr != GetConnection(false).GetSdrObject())
{ // check broadcasters; when we are not inserted we do not need broadcasters // TTTT not yet added, but keep hint to do this here // mpCon1->ownerPageChange(); // mpCon2->ownerPageChange();
}
}
// Do not overwrite existing value with default. ImpSetAttrToEdgeInfo() is called several // times with a set, that does not have SDRATTR_EDGEOOXMLCURVE item. if (rSet.HasItem(SDRATTR_EDGEOOXMLCURVE))
m_aEdgeInfo.m_bUseOOXMLCurve = rSet.Get(SDRATTR_EDGEOOXMLCURVE).GetValue();
} elseif(eKind == SdrEdgeKind::ThreeLines)
{ bool bHor1 = m_aEdgeInfo.m_nAngle1 == 0 || m_aEdgeInfo.m_nAngle1 == 18000; bool bHor2 = m_aEdgeInfo.m_nAngle2 == 0 || m_aEdgeInfo.m_nAngle2 == 18000;
// Here no more notifying is necessary, just local changes are OK. if(n != nValCnt)
{
GetProperties().SetObjectItemDirect(SdrEdgeLineDeltaCountItem(n));
}
void SdrEdgeObj::ImpRecalcEdgeTrack()
{ // #i120437# if bEdgeTrackUserDefined, do not recalculate if(m_bEdgeTrackUserDefined)
{ return;
}
// #i120437# also not when model locked during import, but remember if(getSdrModelFromSdrObject().isLocked())
{
mbSuppressed = true; return;
}
// #i110649# if(mbBoundRectCalculationRunning)
{ // This object is involved into another ImpRecalcEdgeTrack() call // from another SdrEdgeObj. Do not calculate again to avoid loop. // Also, do not change bEdgeTrackDirty so that it gets recalculated // later at the first non-looping call.
} else
{ // To not run in a depth loop, use a coloring algorithm on // SdrEdgeObj BoundRect calculations
mbBoundRectCalculationRunning = true;
if(mbSuppressed)
{ // #i123048# If layouting was ever suppressed, it needs to be done once // and the attr need to be set at EdgeInfo, else these attr *will be lost* // in the following call to ImpSetEdgeInfoToAttr() since they were never // set before (!)
*m_pEdgeTrack=ImpCalcEdgeTrack(*m_pEdgeTrack,m_aCon1,m_aCon2,&m_aEdgeInfo);
ImpSetAttrToEdgeInfo();
mbSuppressed = false;
}
tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetCurrentBoundRect();
SetBoundAndSnapRectsDirty();
*m_pEdgeTrack=ImpCalcEdgeTrack(*m_pEdgeTrack,m_aCon1,m_aCon2,&m_aEdgeInfo);
ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
m_bEdgeTrackDirty=false;
// Only redraw here, no object change
ActionChanged();
Point aP1(rStPt); // mandatory difference first,... if (bLks) aP1.setX(rRect.Left() ); if (bRts) aP1.setX(rRect.Right() ); if (bObn) aP1.setY(rRect.Top() ); if (bUnt) aP1.setY(rRect.Bottom() );
Point aP2(aP1); // ...now increase to Meeting height, if necessary if (bLks && rMeeting.X()<=aP2.X()) aP2.setX(rMeeting.X() ); if (bRts && rMeeting.X()>=aP2.X()) aP2.setX(rMeeting.X() ); if (bObn && rMeeting.Y()<=aP2.Y()) aP2.setY(rMeeting.Y() ); if (bUnt && rMeeting.Y()>=aP2.Y()) aP2.setY(rMeeting.Y() );
aXP.Insert(XPOLY_APPEND,aP2,PolyFlags::Normal);
Point aP3(aP2); if ((bLks && rMeeting.X()>aP2.X()) || (bRts && rMeeting.X()<aP2.X())) { // around if (rMeeting.Y()<aP2.Y()) {
aP3.setY(rRect.Top() ); if (rMeeting.Y()<aP3.Y()) aP3.setY(rMeeting.Y() );
} else {
aP3.setY(rRect.Bottom() ); if (rMeeting.Y()>aP3.Y()) aP3.setY(rMeeting.Y() );
}
aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal); if (aP3.Y()!=rMeeting.Y()) {
aP3.setX(rMeeting.X() );
aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
}
} if ((bObn && rMeeting.Y()>aP2.Y()) || (bUnt && rMeeting.Y()<aP2.Y())) { // around if (rMeeting.X()<aP2.X()) {
aP3.setX(rRect.Left() ); if (rMeeting.X()<aP3.X()) aP3.setX(rMeeting.X() );
} else {
aP3.setX(rRect.Right() ); if (rMeeting.X()>aP3.X()) aP3.setX(rMeeting.X() );
}
aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal); if (aP3.X()!=rMeeting.X()) {
aP3.setY(rMeeting.Y() );
aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
}
} #ifdef DBG_UTIL if (aXP.GetPointCount()>4) {
OSL_FAIL("SdrEdgeObj::ImpCalcObjToCenter(): Polygon has more than 4 points!");
} #endif return aXP;
}
XPolygon SdrEdgeObj::ImpCalcEdgeTrack(const XPolygon& rTrack0, SdrObjConnection& rCon1, SdrObjConnection& rCon2, SdrEdgeInfoRec* pInfo) const
{
Point aPt1,aPt2;
SdrGluePoint aGP1,aGP2;
SdrEscapeDirection nEsc1=SdrEscapeDirection::ALL,nEsc2=SdrEscapeDirection::ALL;
tools::Rectangle aBoundRect1;
tools::Rectangle aBoundRect2;
tools::Rectangle aBewareRect1;
tools::Rectangle aBewareRect2; // first, get the old corner points if (rTrack0.GetPointCount()!=0) {
aPt1=rTrack0[0];
sal_uInt16 nSiz=rTrack0.GetPointCount();
nSiz--;
aPt2=rTrack0[nSiz];
} else
{ const tools::Rectangle& rRectangle = getOutRectangle(); if (!rRectangle.IsEmpty()) {
aPt1 = rRectangle.TopLeft();
aPt2 = rRectangle.BottomRight();
}
}
// #i54102# To allow interactive preview, do also if not inserted constbool bCon1(nullptr != rCon1.m_pSdrObj && rCon1.m_pSdrObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject()); constbool bCon2(nullptr != rCon2.m_pSdrObj && rCon2.m_pSdrObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject()); const SfxItemSet& rSet = GetObjectItemSet();
if (bCon1)
{ if (rCon1.m_pSdrObj==static_cast<SdrObject const *>(this))
{ // check, just in case
aBoundRect1 = getOutRectangle();
} else
{ if (getSdrModelFromSdrObject().GetCompatibilityFlag(SdrCompatibilityFlag::ConnectorUseSnapRect))
aBoundRect1 = rCon1.m_pSdrObj->GetSnapRect(); else
aBoundRect1 = rCon1.m_pSdrObj->GetCurrentBoundRect();
}
if (bCon2)
{ if (rCon2.m_pSdrObj==static_cast<SdrObject const *>(this))
{ // check, just in case
aBoundRect2 = getOutRectangle();
} else
{ if (getSdrModelFromSdrObject().GetCompatibilityFlag(SdrCompatibilityFlag::ConnectorUseSnapRect))
aBoundRect2 = rCon2.m_pSdrObj->GetSnapRect(); else
aBoundRect2 = rCon2.m_pSdrObj->GetCurrentBoundRect();
}
// normalization; be aR1 the one exiting to the right, // be aR2 the one exiting to the left
tools::Rectangle aBewR1(bRts1 ? aBewareRect1 : aBewareRect2);
tools::Rectangle aBewR2(bRts1 ? aBewareRect2 : aBewareRect1);
tools::Rectangle aBndR1(bRts1 ? aBoundRect1 : aBoundRect2);
tools::Rectangle aBndR2(bRts1 ? aBoundRect2 : aBoundRect1); if (aBewR1.Bottom()>aBewR2.Top() && aBewR1.Top()<aBewR2.Bottom()) { // overlap on y axis; cases 2.1, 2.8, 2.9 if (aBewR1.Right()>aBewR2.Left()) { /* Cases 2.8, 2.9: Case2.8:alwaysgoingaroundontheoutside (bDirect=false).
Case2.9couldalsobeadirectconnection(inthe casethattheBewareRectsoverlaponlyslightlyand theBoundRectsdon'toverlapatallandifthe lineexitswouldotherwiseviolatetherespective otherobject'sBewareRect).
*/ bool bCase29Direct = false; bool bCase29=aBewR1.Right()>aBewR2.Left(); if (aBndR1.Right()<=aBndR2.Left()) { // case 2.9 without BoundRect overlap if ((aPt1.Y()>aBewareRect2.Top() && aPt1.Y()<aBewareRect2.Bottom()) ||
(aPt2.Y()>aBewareRect1.Top() && aPt2.Y()<aBewareRect1.Bottom())) {
bCase29Direct = true;
}
} if (!bCase29Direct) { bool bObenLang=std::abs(nYMin-aMeeting.Y())<=std::abs(nYMax-aMeeting.Y()); if (bObenLang) {
aMeeting.setY(nYMin );
} else {
aMeeting.setY(nYMax );
} if (bCase29) { // now make sure that the surrounded object // isn't traversed if ((aBewR1.Center().Y()<aBewR2.Center().Y()) != bObenLang) {
aMeeting.setX(aBewR2.Right() );
} else {
aMeeting.setX(aBewR1.Left() );
}
}
} else { // We need a direct connection (3-line Z connection), // because we have to violate the BewareRects. // Use rule of three to scale down the BewareRects.
tools::Long nWant1=aBewR1.Right()-aBndR1.Right(); // distance at Obj1
tools::Long nWant2=aBndR2.Left()-aBewR2.Left(); // distance at Obj2
tools::Long nSpace=aBndR2.Left()-aBndR1.Right(); // available space
tools::Long nGet1=BigMulDiv(nWant1,nSpace,nWant1+nWant2);
tools::Long nGet2=nSpace-nGet1; if (bRts1) { // revert normalization
aBewareRect1.AdjustRight(nGet1-nWant1 );
aBewareRect2.AdjustLeft( -(nGet2-nWant2) );
} else {
aBewareRect2.AdjustRight(nGet1-nWant1 );
aBewareRect1.AdjustLeft( -(nGet2-nWant2) );
}
nIntersections++; // lower quality
}
}
}
} elseif (bVer1) { // both horizontal
tools::Rectangle aBewR1(bUnt1 ? aBewareRect1 : aBewareRect2);
tools::Rectangle aBewR2(bUnt1 ? aBewareRect2 : aBewareRect1);
tools::Rectangle aBndR1(bUnt1 ? aBoundRect1 : aBoundRect2);
tools::Rectangle aBndR2(bUnt1 ? aBoundRect2 : aBoundRect1); if (aBewR1.Right()>aBewR2.Left() && aBewR1.Left()<aBewR2.Right()) { // overlap on y axis; cases 2.1, 2.8, 2.9 if (aBewR1.Bottom()>aBewR2.Top()) { /* Cases 2.8, 2.9 Case2.8alwaysgoingaroundontheoutside(bDirect=false).
Case2.9couldalsobeadirectconnection(inthe casethattheBewareRectsoverlaponlyslightlyand theBoundRectsdon'toverlapatallandifthe lineexitswouldotherwiseviolatetherespective otherobject'sBewareRect).
*/ bool bCase29Direct = false; bool bCase29=aBewR1.Bottom()>aBewR2.Top(); if (aBndR1.Bottom()<=aBndR2.Top()) { // case 2.9 without BoundRect overlap if ((aPt1.X()>aBewareRect2.Left() && aPt1.X()<aBewareRect2.Right()) ||
(aPt2.X()>aBewareRect1.Left() && aPt2.X()<aBewareRect1.Right())) {
bCase29Direct = true;
}
} if (!bCase29Direct) { bool bLinksLang=std::abs(nXMin-aMeeting.X())<=std::abs(nXMax-aMeeting.X()); if (bLinksLang) {
aMeeting.setX(nXMin );
} else {
aMeeting.setX(nXMax );
} if (bCase29) { // now make sure that the surrounded object // isn't traversed if ((aBewR1.Center().X()<aBewR2.Center().X()) != bLinksLang) {
aMeeting.setY(aBewR2.Bottom() );
} else {
aMeeting.setY(aBewR1.Top() );
}
}
} else { // We need a direct connection (3-line Z connection), // because we have to violate the BewareRects. // Use rule of three to scale down the BewareRects.
tools::Long nWant1=aBewR1.Bottom()-aBndR1.Bottom(); // difference at Obj1
tools::Long nWant2=aBndR2.Top()-aBewR2.Top(); // difference at Obj2
tools::Long nSpace=aBndR2.Top()-aBndR1.Bottom(); // available space
tools::Long nGet1=BigMulDiv(nWant1,nSpace,nWant1+nWant2);
tools::Long nGet2=nSpace-nGet1; if (bUnt1) { // revert normalization
aBewareRect1.AdjustBottom(nGet1-nWant1 );
aBewareRect2.AdjustTop( -(nGet2-nWant2) );
} else {
aBewareRect2.AdjustBottom(nGet1-nWant1 );
aBewareRect1.AdjustTop( -(nGet2-nWant2) );
}
nIntersections++; // lower quality
}
}
}
}
} elseif (nMainCase==3) { // case 3: one horizontal, the other vertical /* legend: Thelineexitstothe: -|left
// case 3.2
tools::Rectangle aTmpR1(aBewareRect1);
tools::Rectangle aTmpR2(aBewareRect2); if (bBewareOverlap) { // overlapping BewareRects: use BoundRects for checking for case 3.2
aTmpR1=aBoundRect1;
aTmpR2=aBoundRect2;
} if ((((bRts1 && aTmpR1.Right ()<=aPt2.X()) || (bLks1 && aTmpR1.Left()>=aPt2.X())) &&
((bUnt2 && aTmpR2.Bottom()<=aPt1.Y()) || (bObn2 && aTmpR2.Top ()>=aPt1.Y()))) ||
(((bRts2 && aTmpR2.Right ()<=aPt1.X()) || (bLks2 && aTmpR2.Left()>=aPt1.X())) &&
((bUnt1 && aTmpR1.Bottom()<=aPt2.Y()) || (bObn1 && aTmpR1.Top ()>=aPt2.Y())))) { // case 3.2 applies: connector with only 2 lines if (bHor1) {
aMeeting.setX(aPt2.X() );
aMeeting.setY(aPt1.Y() );
} else {
aMeeting.setX(aPt1.X() );
aMeeting.setY(aPt2.Y() );
} // in the case of overlapping BewareRects:
aBewareRect1=aTmpR1;
aBewareRect2=aTmpR2;
} elseif ((((bRts1 && aBewareRect1.Right ()>aBewareRect2.Left ()) ||
(bLks1 && aBewareRect1.Left ()<aBewareRect2.Right ())) &&
((bUnt2 && aBewareRect2.Bottom()>aBewareRect1.Top ()) ||
(bObn2 && aBewareRect2.Top ()<aBewareRect1.Bottom()))) ||
(((bRts2 && aBewareRect2.Right ()>aBewareRect1.Left ()) ||
(bLks2 && aBewareRect2.Left ()<aBewareRect1.Right ())) &&
((bUnt1 && aBewareRect1.Bottom()>aBewareRect2.Top ()) ||
(bObn1 && aBewareRect1.Top ()<aBewareRect2.Bottom())))) { // case 3.3 if (bRts1 || bRts2) { aMeeting.setX(nXMax ); } if (bLks1 || bLks2) { aMeeting.setX(nXMin ); } if (bUnt1 || bUnt2) { aMeeting.setY(nYMax ); } if (bObn1 || bObn2) { aMeeting.setY(nYMin ); }
}
}
}
XPolygon aXP1(ImpCalcObjToCenter(aPt1,nAngle1,aBewareRect1,aMeeting));
XPolygon aXP2(ImpCalcObjToCenter(aPt2,nAngle2,aBewareRect2,aMeeting));
sal_uInt16 nXP1Cnt=aXP1.GetPointCount();
sal_uInt16 nXP2Cnt=aXP2.GetPointCount();
assert(nXP1Cnt >= 2 && nXP2Cnt >= 2 && "ImpCalcObjToCenter inserts a min of 2 points"); if (pInfo) {
pInfo->m_nObj1Lines=nXP1Cnt; pInfo->m_nObj1Lines--;
pInfo->m_nObj2Lines=nXP2Cnt; pInfo->m_nObj2Lines--;
}
Point aEP1(aXP1[nXP1Cnt-1]);
Point aEP2(aXP2[nXP2Cnt-1]); bool bInsMeetingPoint=aEP1.X()!=aEP2.X() && aEP1.Y()!=aEP2.Y(); bool bHorzE1=aEP1.Y()==aXP1[nXP1Cnt-2].Y(); // is last line of XP1 horizontal? bool bHorzE2=aEP2.Y()==aXP2[nXP2Cnt-2].Y(); // is last line of XP2 horizontal? if (aEP1==aEP2 && ((bHorzE1 && bHorzE2 && aEP1.Y()==aEP2.Y()) || (!bHorzE1 && !bHorzE2 && aEP1.X()==aEP2.X()))) { // special casing 'I' connectors
nXP1Cnt--; aXP1.Remove(nXP1Cnt,1);
nXP2Cnt--; aXP2.Remove(nXP2Cnt,1);
} if (bInsMeetingPoint) {
aXP1.Insert(XPOLY_APPEND,aMeeting,PolyFlags::Normal); if (pInfo) { // Inserting a MeetingPoint adds 2 new lines, // either might become the center line. if (pInfo->m_nObj1Lines==pInfo->m_nObj2Lines) {
pInfo->m_nObj1Lines++;
pInfo->m_nObj2Lines++;
} else { if (pInfo->m_nObj1Lines>pInfo->m_nObj2Lines) {
pInfo->m_nObj2Lines++;
pInfo->m_nMiddleLine=nXP1Cnt-1;
} else {
pInfo->m_nObj1Lines++;
pInfo->m_nMiddleLine=nXP1Cnt;
}
}
}
} elseif (pInfo && aEP1!=aEP2 && nXP1Cnt+nXP2Cnt>=4) { // By connecting both ends, another line is added, this becomes the center line.
pInfo->m_nMiddleLine=nXP1Cnt-1;
}
sal_uInt16 nNum=aXP2.GetPointCount(); if (nXP1Cnt > 1 && nXP2Cnt > 1 && aXP1[nXP1Cnt-1] == aXP2[nXP2Cnt-1]) nNum--; while (nNum>0) {
nNum--;
aXP1.Insert(XPOLY_APPEND,aXP2[nNum],PolyFlags::Normal);
}
sal_uInt16 nPointCount=aXP1.GetPointCount(); char cForm; if (pInfo || pnQuality!=nullptr) { if (nPointCount==2) cForm='I'; elseif (nPointCount==3) cForm='L'; elseif (nPointCount==4) { // Z or U if (nAngle1==nAngle2) cForm='U'; else cForm='Z';
} elseif (nPointCount==6) { // S or C or ... if (nAngle1!=nAngle2) { // For type S, line 2 has the same direction as line 4. // For type C, the opposite is true.
Point aP1(aXP1[1]);
Point aP2(aXP1[2]);
Point aP3(aXP1[3]);
Point aP4(aXP1[4]); if (aP1.Y()==aP2.Y()) { // else both lines are horizontal if ((aP1.X()<aP2.X())==(aP3.X()<aP4.X())) cForm='S'; else cForm='C';
} else { // else both lines are vertical if ((aP1.Y()<aP2.Y())==(aP3.Y()<aP4.Y())) cForm='S'; else cForm='C';
}
} else cForm='4'; // else is case 3 with 5 lines
} else cForm='?'; // more shapes: if (pInfo) { if (cForm=='I' || cForm=='L' || cForm=='Z' || cForm=='U') {
pInfo->m_nObj1Lines=1;
pInfo->m_nObj2Lines=1; if (cForm=='Z' || cForm=='U') {
pInfo->m_nMiddleLine=1;
} else {
pInfo->m_nMiddleLine=0xFFFF;
}
} elseif (cForm=='S' || cForm=='C') {
pInfo->m_nObj1Lines=2;
pInfo->m_nObj2Lines=2;
pInfo->m_nMiddleLine=2;
}
}
} else
{
cForm = 0;
} if (pnQuality!=nullptr) {
sal_uIntPtr nQual=0;
sal_uIntPtr nQual0=nQual; // prevent overruns bool bOverflow = false;
Point aPt0(aXP1[0]); for (sal_uInt16 nPntNum=1; nPntNum<nPointCount; nPntNum++) {
Point aPt1b(aXP1[nPntNum]);
nQual+=std::abs(aPt1b.X()-aPt0.X())+std::abs(aPt1b.Y()-aPt0.Y()); if (nQual<nQual0) bOverflow = true;
nQual0=nQual;
aPt0=aPt1b;
}
sal_uInt16 nTmp=nPointCount; if (cForm=='Z') {
nTmp=2; // Z shape with good quality (nTmp=2 instead of 4)
sal_uIntPtr n1=std::abs(aXP1[1].X()-aXP1[0].X())+std::abs(aXP1[1].Y()-aXP1[0].Y());
sal_uIntPtr n2=std::abs(aXP1[2].X()-aXP1[1].X())+std::abs(aXP1[2].Y()-aXP1[1].Y());
sal_uIntPtr n3=std::abs(aXP1[3].X()-aXP1[2].X())+std::abs(aXP1[3].Y()-aXP1[2].Y()); // try to make lines lengths similar
sal_uIntPtr nBesser=0;
n1+=n3;
n3=n2/4; if (n1>=n2) nBesser=6; elseif (n1>=3*n3) nBesser=4; elseif (n1>=2*n3) nBesser=2; if (aXP1[0].Y()!=aXP1[1].Y()) nBesser++; // vertical starting line gets a plus (for H/V-Prio) if (nQual>nBesser) nQual-=nBesser; else nQual=0;
} if (nTmp>=3) {
nQual0=nQual;
nQual+=static_cast<sal_uIntPtr>(nTmp)*0x01000000; if (nQual<nQual0 || nTmp>15) bOverflow = true;
} if (nPointCount>=2) { // check exit angle again
Point aP1(aXP1[1]); aP1-=aXP1[0];
Point aP2(aXP1[nPointCount-2]); aP2-=aXP1[nPointCount-1];
tools::Long nAng1=0; if (aP1.X()<0) nAng1=18000; if (aP1.Y()>0) nAng1=27000; if (aP1.Y()<0) nAng1=9000; if (aP1.X()!=0 && aP1.Y()!=0) nAng1=1; // slant?!
tools::Long nAng2=0; if (aP2.X()<0) nAng2=18000; if (aP2.Y()>0) nAng2=27000; if (aP2.Y()<0) nAng2=9000; if (aP2.X()!=0 && aP2.Y()!=0) nAng2=1; // slant?! if (nAng1!=nAngle1) nIntersections++; if (nAng2!=nAngle2) nIntersections++;
}
// For the quality check, use the original Rects and at the same time // check whether one them was scaled down for the calculation of the // Edges (e. g. case 2.9)
aBewareRect1=rBewareRect1;
aBewareRect2=rBewareRect2;
for (sal_uInt16 i=0; i<nPointCount; i++) {
Point aPt1b(aXP1[i]); bool b1=aPt1b.X()>aBewareRect1.Left() && aPt1b.X()<aBewareRect1.Right() &&
aPt1b.Y()>aBewareRect1.Top() && aPt1b.Y()<aBewareRect1.Bottom(); bool b2=aPt1b.X()>aBewareRect2.Left() && aPt1b.X()<aBewareRect2.Right() &&
aPt1b.Y()>aBewareRect2.Top() && aPt1b.Y()<aBewareRect2.Bottom();
sal_uInt16 nInt0=nIntersections; if (i==0 || i==nPointCount-1) { if (b1 && b2) nIntersections++;
} else { if (b1) nIntersections++; if (b2) nIntersections++;
} // check for overlaps if (i>0 && nInt0==nIntersections) { if (aPt0.Y()==aPt1b.Y()) { // horizontal line if (aPt0.Y()>aBewareRect1.Top() && aPt0.Y()<aBewareRect1.Bottom() &&
((aPt0.X()<=aBewareRect1.Left() && aPt1b.X()>=aBewareRect1.Right()) ||
(aPt1b.X()<=aBewareRect1.Left() && aPt0.X()>=aBewareRect1.Right()))) nIntersections++; if (aPt0.Y()>aBewareRect2.Top() && aPt0.Y()<aBewareRect2.Bottom() &&
((aPt0.X()<=aBewareRect2.Left() && aPt1b.X()>=aBewareRect2.Right()) ||
(aPt1b.X()<=aBewareRect2.Left() && aPt0.X()>=aBewareRect2.Right()))) nIntersections++;
} else { // vertical line if (aPt0.X()>aBewareRect1.Left() && aPt0.X()<aBewareRect1.Right() &&
((aPt0.Y()<=aBewareRect1.Top() && aPt1b.Y()>=aBewareRect1.Bottom()) ||
(aPt1b.Y()<=aBewareRect1.Top() && aPt0.Y()>=aBewareRect1.Bottom()))) nIntersections++; if (aPt0.X()>aBewareRect2.Left() && aPt0.X()<aBewareRect2.Right() &&
((aPt0.Y()<=aBewareRect2.Top() && aPt1b.Y()>=aBewareRect2.Bottom()) ||
(aPt1b.Y()<=aBewareRect2.Top() && aPt0.Y()>=aBewareRect2.Bottom()))) nIntersections++;
}
}
aPt0=aPt1b;
} if (nPointCount<=1) nIntersections++;
nQual0=nQual;
nQual+=static_cast<sal_uIntPtr>(nIntersections)*0x10000000; if (nQual<nQual0 || nIntersections>15) bOverflow = true;
if (bOverflow || nQual==0xFFFFFFFF) nQual=0xFFFFFFFE;
*pnQuality=nQual;
} if (pInfo) { // now apply line offsets to aXP1 if (pInfo->m_nMiddleLine!=0xFFFF) {
sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::MiddleLine,aXP1); if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::MiddleLine,aXP1)) {
aXP1[nIdx].AdjustY(pInfo->m_aMiddleLine.Y() );
aXP1[nIdx+1].AdjustY(pInfo->m_aMiddleLine.Y() );
} else {
aXP1[nIdx].AdjustX(pInfo->m_aMiddleLine.X() );
aXP1[nIdx+1].AdjustX(pInfo->m_aMiddleLine.X() );
}
} if (pInfo->m_nObj1Lines>=2) {
sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj1Line2,aXP1); if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj1Line2,aXP1)) {
aXP1[nIdx].AdjustY(pInfo->m_aObj1Line2.Y() );
aXP1[nIdx+1].AdjustY(pInfo->m_aObj1Line2.Y() );
} else {
aXP1[nIdx].AdjustX(pInfo->m_aObj1Line2.X() );
aXP1[nIdx+1].AdjustX(pInfo->m_aObj1Line2.X() );
}
} if (pInfo->m_nObj1Lines>=3) {
sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj1Line3,aXP1); if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj1Line3,aXP1)) {
aXP1[nIdx].AdjustY(pInfo->m_aObj1Line3.Y() );
aXP1[nIdx+1].AdjustY(pInfo->m_aObj1Line3.Y() );
} else {
aXP1[nIdx].AdjustX(pInfo->m_aObj1Line3.X() );
aXP1[nIdx+1].AdjustX(pInfo->m_aObj1Line3.X() );
}
} if (pInfo->m_nObj2Lines>=2) {
sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj2Line2,aXP1); if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj2Line2,aXP1)) {
aXP1[nIdx].AdjustY(pInfo->m_aObj2Line2.Y() );
aXP1[nIdx+1].AdjustY(pInfo->m_aObj2Line2.Y() );
} else {
aXP1[nIdx].AdjustX(pInfo->m_aObj2Line2.X() );
aXP1[nIdx+1].AdjustX(pInfo->m_aObj2Line2.X() );
}
} if (pInfo->m_nObj2Lines>=3) {
sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj2Line3,aXP1); if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj2Line3,aXP1)) {
aXP1[nIdx].AdjustY(pInfo->m_aObj2Line3.Y() );
aXP1[nIdx+1].AdjustY(pInfo->m_aObj2Line3.Y() );
} else {
aXP1[nIdx].AdjustX(pInfo->m_aObj2Line3.X() );
aXP1[nIdx+1].AdjustX(pInfo->m_aObj2Line3.X() );
}
}
} // make the connector a bezier curve, if appropriate if (eKind != SdrEdgeKind::Bezier || nPointCount <= 2) return aXP1;
if (pInfo && pInfo->m_bUseOOXMLCurve) // Routing method OOXML
{ // The additional points needed are located on the segments of the path of the // corresponding bentConnector as calculated above. auto SegmentPoint = [&aXP1](const sal_uInt16& nEnd, constdouble& fFactor) { return Point(
aXP1[nEnd - 1].X() + basegfx::fround<tools::Long>(fFactor * (aXP1[nEnd].X() - aXP1[nEnd - 1].X())),
aXP1[nEnd - 1].Y() + basegfx::fround<tools::Long>(fFactor * (aXP1[nEnd].Y() - aXP1[nEnd - 1].Y())));
};
// We change the path going from end to start. Thus inserting points does not affect the index // of the preceding points. // The end point has index nPointCount-1 and is a normal point and kept. // Insert new control point in the middle of last segments.
Point aControl = SegmentPoint(nPointCount - 1, 0.5); // Insert happens before specified index.
aXP1.Insert(nPointCount - 1, aControl, PolyFlags::Control); for (sal_uInt16 nSegment = nPointCount - 2; nSegment > 1; --nSegment)
{ // We need a normal point at center of segment and control points at 1/4 and 3/4 of // segment. At center and 1/4 are new points, at 3/4 will be replacement for the end // point of the segment.
aControl = SegmentPoint(nSegment, 0.25);
Point aNormal = SegmentPoint(nSegment, 0.5);
aXP1.SetFlags(nSegment, PolyFlags::Control);
aXP1[nSegment] = SegmentPoint(nSegment, 0.75);
aXP1.Insert(nSegment, aNormal, PolyFlags::Normal);
aXP1.Insert(nSegment, aControl, PolyFlags::Control);
} // The first segments needs a control point in the middle. It is replacement for the // second point.
aXP1.SetFlags(1, PolyFlags::Control);
aXP1[1] = SegmentPoint(1, 0.5);
} else// Routing method LO
{
Point* pPt1=&aXP1[0];
Point* pPt2=&aXP1[1];
Point* pPt3=&aXP1[nPointCount-2];
Point* pPt4=&aXP1[nPointCount-1];
tools::Long dx1=pPt2->X()-pPt1->X();
tools::Long dy1=pPt2->Y()-pPt1->Y();
tools::Long dx2=pPt3->X()-pPt4->X();
tools::Long dy2=pPt3->Y()-pPt4->Y(); if (cForm=='L') { // nPointCount==3
aXP1.SetFlags(1,PolyFlags::Control);
Point aPt3(*pPt2);
aXP1.Insert(2,aPt3,PolyFlags::Control);
nPointCount=aXP1.GetPointCount();
pPt2=&aXP1[1];
pPt3=&aXP1[nPointCount-2];
pPt2->AdjustX( -(dx1/3) );
pPt2->AdjustY( -(dy1/3) );
pPt3->AdjustX( -(dx2/3) );
pPt3->AdjustY( -(dy2/3) );
} elseif (nPointCount>=4 && nPointCount<=6) { // Z or U or ... // To all others, the end points of the original lines become control // points for now. Thus, we need to do some more work for nPointCount>4!
aXP1.SetFlags(1,PolyFlags::Control);
aXP1.SetFlags(nPointCount-2,PolyFlags::Control); // distance x1.5
pPt2->AdjustX(dx1/2 );
pPt2->AdjustY(dy1/2 );
pPt3->AdjustX(dx2/2 );
pPt3->AdjustY(dy2/2 ); if (nPointCount==5) { // add a control point before and after center
Point aCenter(aXP1[2]);
tools::Long dx1b=aCenter.X()-aXP1[1].X();
tools::Long dy1b=aCenter.Y()-aXP1[1].Y();
tools::Long dx2b=aCenter.X()-aXP1[3].X();
tools::Long dy2b=aCenter.Y()-aXP1[3].Y();
aXP1.Insert(2,aCenter,PolyFlags::Control);
aXP1.SetFlags(3,PolyFlags::Symmetric);
aXP1.Insert(4,aCenter,PolyFlags::Control);
aXP1[2].AdjustX( -(dx1b/2) );
aXP1[2].AdjustY( -(dy1b/2) );
aXP1[3].AdjustX( -((dx1b+dx2b)/4) );
aXP1[3].AdjustY( -((dy1b+dy2b)/4) );
aXP1[4].AdjustX( -(dx2b/2) );
aXP1[4].AdjustY( -(dy2b/2) );
} if (nPointCount==6) {
Point aPt1b(aXP1[2]);
Point aPt2b(aXP1[3]);
aXP1.Insert(2,aPt1b,PolyFlags::Control);
aXP1.Insert(5,aPt2b,PolyFlags::Control);
tools::Long dx=aPt1b.X()-aPt2b.X();
tools::Long dy=aPt1b.Y()-aPt2b.Y();
aXP1[3].AdjustX( -(dx/2) );
aXP1[3].AdjustY( -(dy/2) );
aXP1.SetFlags(3,PolyFlags::Symmetric);
aXP1.Remove(4,1); // because it's identical with aXP1[3]
}
}
} return aXP1;
}
void SdrEdgeObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
{ const SfxHintId nId = rHint.GetId(); bool bDataChg=nId==SfxHintId::DataChanged; bool bDying=nId==SfxHintId::Dying; bool bObj1=m_aCon1.m_pSdrObj!=nullptr && m_aCon1.m_pSdrObj->GetBroadcaster()==&rBC; bool bObj2=m_aCon2.m_pSdrObj!=nullptr && m_aCon2.m_pSdrObj->GetBroadcaster()==&rBC; if (bDying && (bObj1 || bObj2)) { // catch Dying, so AttrObj doesn't start broadcasting // about an alleged change of template if (bObj1) m_aCon1.m_pSdrObj=nullptr; if (bObj2) m_aCon2.m_pSdrObj=nullptr; return;
} if ( bObj1 || bObj2 )
{
m_bEdgeTrackUserDefined = false;
}
SdrTextObj::Notify(rBC,rHint); if (m_nNotifyingCount!=0)return;
// a locking flag
m_nNotifyingCount++; const SdrHint* pSdrHint = ( rHint.GetId() == SfxHintId::ThisIsAnSdrHint ? static_cast<const SdrHint*>(&rHint) : nullptr );
if (bDataChg) { // StyleSheet changed
ImpSetAttrToEdgeInfo(); // when changing templates, copy values from Pool to aEdgeInfo
} if (bDataChg ||
(bObj1 && m_aCon1.m_pSdrObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject()) ||
(bObj2 && m_aCon2.m_pSdrObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject()) ||
(pSdrHint && pSdrHint->GetKind()==SdrHintKind::ObjectRemoved))
{ // broadcasting only, if on the same page
tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetCurrentBoundRect();
ImpDirtyEdgeTrack();
// only redraw here, object hasn't actually changed
ActionChanged();
// #i110629# also set aRect and maSnapeRect depending on pEdgeTrack const tools::Rectangle aPolygonBounds(m_pEdgeTrack->GetBoundRect());
setRectangle(aPolygonBounds);
maSnapRect = aPolygonBounds;
}
}
// copy connections for clone, SdrEdgeObj::operator= does not do this
pRetval->ConnectToNode(true, GetConnectedNode(true));
pRetval->ConnectToNode(false, GetConnectedNode(false));
if(!bOriginalEdgeModified && pOriginalEdge)
{ // copy connections when clone is modified. This is needed because // as preparation to this modification the data from the original object // was copied to the clone using the operator=. As can be seen there, // that operator does not copy the connections (for good reason)
ConnectToNode(true, pOriginalEdge->GetConnection(true).GetSdrObject());
ConnectToNode(false, pOriginalEdge->GetConnection(false).GetSdrObject());
}
if(rDragStat.GetHdl()->GetPointNum() < 2)
{ // start or end point connector drag constbool bDragA(0 == rDragStat.GetHdl()->GetPointNum()); const Point aPointNow(rDragStat.GetNow());
// look for new connection
ImpFindConnector(aPointNow, *rDragStat.GetPageView(), *pDraggedOne, pOriginalEdge, nullptr, &rDragStat);
if(pDraggedOne->m_pSdrObj)
{ // if found, officially connect to it; ImpFindConnector only // sets pObj hard
SdrObject* pNewConnection = pDraggedOne->m_pSdrObj;
pDraggedOne->m_pSdrObj = nullptr;
ConnectToNode(bDragA, pNewConnection);
}
if(rDragStat.GetView() && !bOriginalEdgeModified)
{ // show IA helper, but only do this during IA, so not when the original // Edge gets modified in the last call
rDragStat.GetView()->SetConnectMarker(*pDraggedOne);
}
}
// force recalculation of EdgeTrack
*m_pEdgeTrack = ImpCalcEdgeTrack(*m_pEdgeTrack, m_aCon1, m_aCon2, &m_aEdgeInfo);
m_bEdgeTrackDirty=false;
// save EdgeInfos and mark object as user modified
ImpSetEdgeInfoToAttr();
m_bEdgeTrackUserDefined = false;
SetBoundAndSnapRectsDirty();
if(bOriginalEdgeModified && rDragStat.GetView())
{ // hide connect marker helper again when original gets changed. // This happens at the end of the interaction
rDragStat.GetView()->HideConnectMarker();
}
// if resize is not from paste, forget user distances if (!getSdrModelFromSdrObject().IsPasteResize())
{
m_aEdgeInfo.m_aObj1Line2 = Point();
m_aEdgeInfo.m_aObj1Line3 = Point();
m_aEdgeInfo.m_aObj2Line2 = Point();
m_aEdgeInfo.m_aObj2Line3 = Point();
m_aEdgeInfo.m_aMiddleLine = Point();
}
}
// #i54102# added rotation support void SdrEdgeObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
{ if(m_bEdgeTrackUserDefined)
{ // #i120437# special handling when track is imported, apply // transformation directly to imported track.
SdrTextObj::NbcRotate(rRef, nAngle, sn, cs);
RotateXPoly(*m_pEdgeTrack, rRef, sn, cs);
} else
{ // handle start and end point if not connected constbool bCon1(nullptr != m_aCon1.m_pSdrObj && m_aCon1.m_pSdrObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject()); constbool bCon2(nullptr != m_aCon2.m_pSdrObj && m_aCon2.m_pSdrObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
void SdrEdgeObj::RestoreGeoData(const SdrObjGeoData& rGeo)
{
SdrTextObj::RestoreGeoData(rGeo); const SdrEdgeObjGeoData& rEGeo=static_cast<const SdrEdgeObjGeoData&>(rGeo); if (m_aCon1.m_pSdrObj!=rEGeo.m_aCon1.m_pSdrObj) { if (m_aCon1.m_pSdrObj!=nullptr) m_aCon1.m_pSdrObj->RemoveListener(*this);
m_aCon1=rEGeo.m_aCon1; if (m_aCon1.m_pSdrObj!=nullptr) m_aCon1.m_pSdrObj->AddListener(*this);
} else
m_aCon1=rEGeo.m_aCon1;
if (m_aCon2.m_pSdrObj!=rEGeo.m_aCon2.m_pSdrObj) { if (m_aCon2.m_pSdrObj!=nullptr) m_aCon2.m_pSdrObj->RemoveListener(*this);
m_aCon2=rEGeo.m_aCon2; if (m_aCon2.m_pSdrObj!=nullptr) m_aCon2.m_pSdrObj->AddListener(*this);
} else
m_aCon2=rEGeo.m_aCon2;
/** this method is used by the api to set a gluepoint for a connection nId==-1:Thebestdefaultpointisautomaticallychosen 0<=nId<=3:Oneofthedefaultpointsischosen nId>=4:Auserdefinedgluepointischosen
*/ void SdrEdgeObj::setGluePointIndex( bool bTail, sal_Int32 nIndex /* = -1 */ )
{
SdrObjConnection& rConn1 = GetConnection( bTail );
if( nIndex > 3 )
{
nIndex -= 3; // the start api index is 0, whereas the implementation in svx starts from 1
// for user defined gluepoints we have // to get the id for this index first const SdrGluePointList* pList = rConn1.GetSdrObject() ? rConn1.GetSdrObject()->GetGluePointList() : nullptr; if( pList == nullptr || SDRGLUEPOINT_NOTFOUND == pList->FindGluePoint(static_cast<sal_uInt16>(nIndex)) ) return;
} elseif( nIndex < 0 )
{
nIndex = 0;
}
/** this method is used by the api to return a gluepoint id for a connection.
See setGluePointId for possible return values */
sal_Int32 SdrEdgeObj::getGluePointIndex( bool bTail )
{
SdrObjConnection& rConn1 = GetConnection( bTail );
sal_Int32 nId = -1; if( !rConn1.IsBestConnection() )
{
nId = rConn1.GetConnectorId(); if( !rConn1.IsAutoVertex() )
nId += 3; // the start api index is 0, whereas the implementation in svx starts from 1
} return nId;
}
// Implementation was missing; edge track needs to be invalidated additionally. void SdrEdgeObj::NbcSetAnchorPos(const Point& rPnt)
{ // call parent functionality
SdrTextObj::NbcSetAnchorPos(rPnt);
bool SdrEdgeObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& rPolyPolygon) const
{ // use base method from SdrObject, it's not rotatable and // a call to GetSnapRect() is used. That's what we need for Connector. return SdrObject::TRGetBaseGeometry(rMatrix, rPolyPolygon);
}
void SdrEdgeObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& rPolyPolygon)
{ // where appropriate take care for existing connections. For now, just use the // implementation from SdrObject.
SdrObject::TRSetBaseGeometry(rMatrix, rPolyPolygon);
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.56Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-01)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.