LibreOffice Module svx (master)  1
svdoedge.cxx
Go to the documentation of this file.
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 #include <svx/dialmgr.hxx>
21 #include <svx/strings.hrc>
22 #include <osl/diagnose.h>
23 
26 #include <svl/hint.hxx>
27 
30 #include <svx/sdrhittesthelper.hxx>
31 #include <svx/svddrag.hxx>
32 #include <svx/svddrgmt.hxx>
33 #include <svx/svdhdl.hxx>
34 #include <svx/svdmodel.hxx>
35 #include <svx/svdoedge.hxx>
36 #include <svx/svdopath.hxx>
37 #include <svx/svdpage.hxx>
38 #include <svx/svdpagv.hxx>
39 #include <svx/svdtrans.hxx>
40 #include <svx/svdview.hxx>
41 #include <svx/sxekitm.hxx>
42 #include <svx/sxelditm.hxx>
43 #include <svx/sxenditm.hxx>
44 #include <svx/xpoly.hxx>
45 #include <vcl/ptrstyle.hxx>
46 #include <comphelper/lok.hxx>
47 
49 {
50  pObj=nullptr;
51  nConId=0;
52  bBestConn=true;
53  bBestVertex=true;
54  bAutoVertex=false;
55  bAutoCorner=false;
56 }
57 
59 {
60  bool bRet = false;
61  if (pObj!=nullptr) { // one object has to be docked already!
62  if (bAutoVertex) {
64  bRet = true;
65  } else if (bAutoCorner) {
67  bRet = true;
68  } else {
69  const SdrGluePointList* pGPL=pObj->GetGluePointList();
70  if (pGPL!=nullptr) {
71  sal_uInt16 nNum=pGPL->FindGluePoint(nConId);
72  if (nNum!=SDRGLUEPOINT_NOTFOUND) {
73  rGP=(*pGPL)[nNum];
74  bRet = true;
75  }
76  }
77  }
78  }
79  if (bRet) {
80  Point aPt(rGP.GetAbsolutePos(*pObj));
81  aPt+=aObjOfs;
82  rGP.SetPos(aPt);
83  }
84  return bRet;
85 }
86 
88 {
89  switch (eLineCode) {
95  } // switch
96  return aMiddleLine;
97 }
98 
99 sal_uInt16 SdrEdgeInfoRec::ImpGetPolyIdx(SdrEdgeLineCode eLineCode, const XPolygon& rXP) const
100 {
101  switch (eLineCode) {
102  case SdrEdgeLineCode::Obj1Line2 : return 1;
103  case SdrEdgeLineCode::Obj1Line3 : return 2;
104  case SdrEdgeLineCode::Obj2Line2 : return rXP.GetPointCount()-3;
105  case SdrEdgeLineCode::Obj2Line3 : return rXP.GetPointCount()-4;
107  } // switch
108  return 0;
109 }
110 
111 bool SdrEdgeInfoRec::ImpIsHorzLine(SdrEdgeLineCode eLineCode, const XPolygon& rXP) const
112 {
113  sal_uInt16 nIdx=ImpGetPolyIdx(eLineCode,rXP);
114  bool bHorz=nAngle1==0 || nAngle1==18000;
115  if (eLineCode==SdrEdgeLineCode::Obj2Line2 || eLineCode==SdrEdgeLineCode::Obj2Line3) {
116  nIdx=rXP.GetPointCount()-nIdx;
117  bHorz=nAngle2==0 || nAngle2==18000;
118  }
119  if ((nIdx & 1)==1) bHorz=!bHorz;
120  return bHorz;
121 }
122 
124 {
125  Point& rPt=ImpGetLineOffsetPoint(eLineCode);
126  if (ImpIsHorzLine(eLineCode,rXP)) rPt.setY(nVal );
127  else rPt.setX(nVal );
128 }
129 
131 {
132  const Point& rPt = const_cast<SdrEdgeInfoRec*>(this)->ImpGetLineOffsetPoint(eLineCode);
133  if (ImpIsHorzLine(eLineCode,rXP))
134  return rPt.Y();
135  else
136  return rPt.X();
137 }
138 
139 
140 // BaseProperties section
141 
142 std::unique_ptr<sdr::properties::BaseProperties> SdrEdgeObj::CreateObjectSpecificProperties()
143 {
144  return std::make_unique<sdr::properties::ConnectorProperties>(*this);
145 }
146 
147 
148 // DrawContact section
149 
150 std::unique_ptr<sdr::contact::ViewContact> SdrEdgeObj::CreateObjectSpecificViewContact()
151 {
152  return std::make_unique<sdr::contact::ViewContactOfSdrEdgeObj>(*this);
153 }
154 
155 
157 : SdrTextObj(rSdrModel),
158  nNotifyingCount(0),
159  bEdgeTrackDirty(false),
160  bEdgeTrackUserDefined(false),
161  // Default is to allow default connects
162  mbSuppressDefaultConnect(false),
163  mbBoundRectCalculationRunning(false),
164  mbSuppressed(false)
165 {
166  m_bClosedObj=false;
167  m_bIsEdge=true;
168  pEdgeTrack = XPolygon();
169 }
170 
171 SdrEdgeObj::SdrEdgeObj(SdrModel& rSdrModel, SdrEdgeObj const & rSource)
172 : SdrTextObj(rSdrModel, rSource),
173  nNotifyingCount(0),
174  bEdgeTrackDirty(false),
175  bEdgeTrackUserDefined(false),
176  // Default is to allow default connects
177  mbSuppressDefaultConnect(false),
178  mbBoundRectCalculationRunning(false),
179  mbSuppressed(false)
180 {
181  m_bClosedObj = false;
182  m_bIsEdge = true;
183  pEdgeTrack = rSource.pEdgeTrack;
185  aCon1 =rSource.aCon1;
186  aCon2 =rSource.aCon2;
187  aCon1.pObj=nullptr;
188  aCon2.pObj=nullptr;
189  aEdgeInfo=rSource.aEdgeInfo;
190 }
191 
193 {
196 }
197 
198 void SdrEdgeObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
199 {
200  // call parent
201  SdrTextObj::handlePageChange(pOldPage, pNewPage);
202 
203  if(nullptr != GetConnection(true).GetObject() || nullptr != GetConnection(false).GetObject())
204  {
205  // check broadcasters; when we are not inserted we do not need broadcasters
206  // TTTT not yet added, but keep hint to do this here
207  // mpCon1->ownerPageChange();
208  // mpCon2->ownerPageChange();
209  }
210 }
211 
213 {
214  const SfxItemSet& rSet = GetObjectItemSet();
215  SdrEdgeKind eKind = rSet.Get(SDRATTR_EDGEKIND).GetValue();
216  sal_Int32 nVal1 = rSet.Get(SDRATTR_EDGELINE1DELTA).GetValue();
217  sal_Int32 nVal2 = rSet.Get(SDRATTR_EDGELINE2DELTA).GetValue();
218  sal_Int32 nVal3 = rSet.Get(SDRATTR_EDGELINE3DELTA).GetValue();
219 
220  if(eKind == SdrEdgeKind::OrthoLines || eKind == SdrEdgeKind::Bezier)
221  {
222  sal_Int32 nVals[3] = { nVal1, nVal2, nVal3 };
223  sal_uInt16 n = 0;
224 
225  if(aEdgeInfo.nObj1Lines >= 2 && n < 3)
226  {
228  n++;
229  }
230 
231  if(aEdgeInfo.nObj1Lines >= 3 && n < 3)
232  {
234  n++;
235  }
236 
237  if(aEdgeInfo.nMiddleLine != 0xFFFF && n < 3)
238  {
240  n++;
241  }
242 
243  if(aEdgeInfo.nObj2Lines >= 3 && n < 3)
244  {
246  n++;
247  }
248 
249  if(aEdgeInfo.nObj2Lines >= 2 && n < 3)
250  {
252  n++;
253  }
254  }
255  else if(eKind == SdrEdgeKind::ThreeLines)
256  {
257  bool bHor1 = aEdgeInfo.nAngle1 == 0 || aEdgeInfo.nAngle1 == 18000;
258  bool bHor2 = aEdgeInfo.nAngle2 == 0 || aEdgeInfo.nAngle2 == 18000;
259 
260  if(bHor1)
261  {
262  aEdgeInfo.aObj1Line2.setX( nVal1 );
263  }
264  else
265  {
266  aEdgeInfo.aObj1Line2.setY( nVal1 );
267  }
268 
269  if(bHor2)
270  {
271  aEdgeInfo.aObj2Line2.setX( nVal2 );
272  }
273  else
274  {
275  aEdgeInfo.aObj2Line2.setY( nVal2 );
276  }
277  }
278 
280 }
281 
283 {
284  const SfxItemSet& rSet = GetObjectItemSet();
285  SdrEdgeKind eKind = rSet.Get(SDRATTR_EDGEKIND).GetValue();
286  sal_Int32 nValCnt = rSet.Get(SDRATTR_EDGELINEDELTACOUNT).GetValue();
287  sal_Int32 nVal1 = rSet.Get(SDRATTR_EDGELINE1DELTA).GetValue();
288  sal_Int32 nVal2 = rSet.Get(SDRATTR_EDGELINE2DELTA).GetValue();
289  sal_Int32 nVal3 = rSet.Get(SDRATTR_EDGELINE3DELTA).GetValue();
290  sal_Int32 nVals[3] = { nVal1, nVal2, nVal3 };
291  sal_uInt16 n = 0;
292 
293  if(eKind == SdrEdgeKind::OrthoLines || eKind == SdrEdgeKind::Bezier)
294  {
295  if(aEdgeInfo.nObj1Lines >= 2 && n < 3)
296  {
298  n++;
299  }
300 
301  if(aEdgeInfo.nObj1Lines >= 3 && n < 3)
302  {
304  n++;
305  }
306 
307  if(aEdgeInfo.nMiddleLine != 0xFFFF && n < 3)
308  {
310  n++;
311  }
312 
313  if(aEdgeInfo.nObj2Lines >= 3 && n < 3)
314  {
316  n++;
317  }
318 
319  if(aEdgeInfo.nObj2Lines >= 2 && n < 3)
320  {
322  n++;
323  }
324  }
325  else if(eKind == SdrEdgeKind::ThreeLines)
326  {
327  bool bHor1 = aEdgeInfo.nAngle1 == 0 || aEdgeInfo.nAngle1 == 18000;
328  bool bHor2 = aEdgeInfo.nAngle2 == 0 || aEdgeInfo.nAngle2 == 18000;
329 
330  n = 2;
331  nVals[0] = bHor1 ? aEdgeInfo.aObj1Line2.X() : aEdgeInfo.aObj1Line2.Y();
332  nVals[1] = bHor2 ? aEdgeInfo.aObj2Line2.X() : aEdgeInfo.aObj2Line2.Y();
333  }
334 
335  if(!(n != nValCnt || nVals[0] != nVal1 || nVals[1] != nVal2 || nVals[2] != nVal3))
336  return;
337 
338  // Here no more notifying is necessary, just local changes are OK.
339  if(n != nValCnt)
340  {
342  }
343 
344  if(nVals[0] != nVal1)
345  {
347  }
348 
349  if(nVals[1] != nVal2)
350  {
352  }
353 
354  if(nVals[2] != nVal3)
355  {
357  }
358 
359  if(n < 3)
360  {
362  }
363 
364  if(n < 2)
365  {
367  }
368 
369  if(n < 1)
370  {
372  }
373 }
374 
376 {
377  // #i54102# allow rotation, mirror and shear
378  rInfo.bRotateFreeAllowed = true;
379  rInfo.bRotate90Allowed = true;
380  rInfo.bMirrorFreeAllowed = true;
381  rInfo.bMirror45Allowed = true;
382  rInfo.bMirror90Allowed = true;
383  rInfo.bTransparenceAllowed = false;
384  rInfo.bShearAllowed = true;
385  rInfo.bEdgeRadiusAllowed = false;
386  bool bCanConv=!HasText() || ImpCanConvTextToCurve();
387  rInfo.bCanConvToPath=bCanConv;
388  rInfo.bCanConvToPoly=bCanConv;
390 }
391 
393 {
394  return SdrObjKind::Edge;
395 }
396 
398 {
399  if(bEdgeTrackDirty)
400  {
401  const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
402  }
403 
405 }
406 
408 {
409  if(bEdgeTrackDirty)
410  {
411  const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
412  }
413 
414  return SdrTextObj::GetSnapRect();
415 }
416 
418 {
419  maSnapRect=pEdgeTrack->GetBoundRect();
420 }
421 
423 {
424  rRect=GetSnapRect();
425 }
426 
428 {
429  Point aPt;
430  sal_uInt16 nPointCount=pEdgeTrack->GetPointCount();
431  if (nPointCount>0)
432  {
433  Point aOfs = GetSnapRect().Center();
434  if (nNum==2 && GetConnectedNode(true)==nullptr) aPt=(*pEdgeTrack)[0];
435  else if (nNum==3 && GetConnectedNode(false)==nullptr) aPt=(*pEdgeTrack)[nPointCount-1];
436  else {
437  if ((nPointCount & 1) ==1) {
438  aPt=(*pEdgeTrack)[nPointCount/2];
439  } else {
440  Point aPt1((*pEdgeTrack)[nPointCount/2-1]);
441  Point aPt2((*pEdgeTrack)[nPointCount/2]);
442  aPt1+=aPt2;
443  aPt1.setX( aPt1.X() / 2 );
444  aPt1.setY( aPt1.Y() / 2 );
445  aPt=aPt1;
446  }
447  }
448  aPt-=aOfs;
449  }
450  SdrGluePoint aGP(aPt);
451  aGP.SetPercent(false);
452  return aGP;
453 }
454 
456 {
457  return GetVertexGluePoint(nNum);
458 }
459 
461 {
462  return nullptr; // no user defined gluepoints for connectors
463 }
464 
466 {
467  return nullptr; // no user defined gluepoints for connectors
468 }
469 
470 void SdrEdgeObj::ConnectToNode(bool bTail1, SdrObject* pObj)
471 {
472  SdrObjConnection& rCon=GetConnection(bTail1);
473  DisconnectFromNode(bTail1);
474  if (pObj!=nullptr) {
475  pObj->AddListener(*this);
476  rCon.pObj=pObj;
477 
478  // #i120437# If connection is set, reset bEdgeTrackUserDefined
479  bEdgeTrackUserDefined = false;
480 
482  }
483 }
484 
486 {
487  SdrObjConnection& rCon=GetConnection(bTail1);
488  if (rCon.pObj!=nullptr) {
489  rCon.pObj->RemoveListener(*this);
490  rCon.pObj=nullptr;
491  }
492 }
493 
495 {
496  SdrObject* pObj(GetConnection(bTail1).pObj);
497 
498  if(nullptr != pObj
499  && (pObj->getSdrPageFromSdrObject() != getSdrPageFromSdrObject() || !pObj->IsInserted()))
500  {
501  pObj = nullptr;
502  }
503 
504  return pObj;
505 }
506 
507 bool SdrEdgeObj::CheckNodeConnection(bool bTail1) const
508 {
509  bool bRet = false;
510  const SdrObjConnection& rCon=GetConnection(bTail1);
511  sal_uInt16 nPointCount=pEdgeTrack->GetPointCount();
512 
513  if(nullptr != rCon.pObj && rCon.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject() && 0 != nPointCount)
514  {
515  const SdrGluePointList* pGPL=rCon.pObj->GetGluePointList();
516  sal_uInt16 nGluePointCnt=pGPL==nullptr ? 0 : pGPL->GetCount();
517  sal_uInt16 nGesAnz=nGluePointCnt+8;
518  Point aTail(bTail1 ? (*pEdgeTrack)[0] : (*pEdgeTrack)[sal_uInt16(nPointCount-1)]);
519  for (sal_uInt16 i=0; i<nGesAnz && !bRet; i++) {
520  if (i<nGluePointCnt) { // UserDefined
521  bRet=aTail==(*pGPL)[i].GetAbsolutePos(*rCon.pObj);
522  } else if (i<nGluePointCnt+4) { // Vertex
523  SdrGluePoint aPt(rCon.pObj->GetVertexGluePoint(i-nGluePointCnt));
524  bRet=aTail==aPt.GetAbsolutePos(*rCon.pObj);
525  } else { // Corner
526  SdrGluePoint aPt(rCon.pObj->GetCornerGluePoint(i-nGluePointCnt-4));
527  bRet=aTail==aPt.GetAbsolutePos(*rCon.pObj);
528  }
529  }
530  }
531  return bRet;
532 }
533 
534 void SdrEdgeObj::ImpSetTailPoint(bool bTail1, const Point& rPt)
535 {
536  sal_uInt16 nPointCount=pEdgeTrack->GetPointCount();
537  if (nPointCount==0) {
538  (*pEdgeTrack)[0]=rPt;
539  (*pEdgeTrack)[1]=rPt;
540  } else if (nPointCount==1) {
541  if (!bTail1) (*pEdgeTrack)[1]=rPt;
542  else { (*pEdgeTrack)[1]=(*pEdgeTrack)[0]; (*pEdgeTrack)[0]=rPt; }
543  } else {
544  if (!bTail1) (*pEdgeTrack)[sal_uInt16(nPointCount-1)]=rPt;
545  else (*pEdgeTrack)[0]=rPt;
546  }
549 }
550 
552 {
553  if ( !bEdgeTrackUserDefined || !getSdrModelFromSdrObject().isLocked() )
554  bEdgeTrackDirty = true;
555 }
556 
558 {
559  if (bEdgeTrackDirty && getSdrModelFromSdrObject().isLocked())
560  {
562  }
563 }
564 
566 {
567  // #i120437# if bEdgeTrackUserDefined, do not recalculate
569  {
570  return;
571  }
572 
573  // #i120437# also not when model locked during import, but remember
574  if(getSdrModelFromSdrObject().isLocked())
575  {
576  mbSuppressed = true;
577  return;
578  }
579 
580  // #i110649#
582  {
583  // This object is involved into another ImpRecalcEdgeTrack() call
584  // from another SdrEdgeObj. Do not calculate again to avoid loop.
585  // Also, do not change bEdgeTrackDirty so that it gets recalculated
586  // later at the first non-looping call.
587  }
588  else
589  {
590  // To not run in a depth loop, use a coloring algorithm on
591  // SdrEdgeObj BoundRect calculations
593 
594  if(mbSuppressed)
595  {
596  // #i123048# If layouting was ever suppressed, it needs to be done once
597  // and the attr need to be set at EdgeInfo, else these attr *will be lost*
598  // in the following call to ImpSetEdgeInfoToAttr() since they were never
599  // set before (!)
602  mbSuppressed = false;
603  }
604 
605  tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetCurrentBoundRect();
608  ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
609  bEdgeTrackDirty=false;
610 
611  // Only redraw here, no object change
612  ActionChanged();
613 
615 
617  }
618 }
619 
621 {
622  if (pObj==nullptr) return SdrEscapeDirection::ALL;
623  tools::Rectangle aR(pObj->GetSnapRect());
624  tools::Long dxl=rPt.X()-aR.Left();
625  tools::Long dyo=rPt.Y()-aR.Top();
626  tools::Long dxr=aR.Right()-rPt.X();
627  tools::Long dyu=aR.Bottom()-rPt.Y();
628  bool bxMitt=std::abs(dxl-dxr)<2;
629  bool byMitt=std::abs(dyo-dyu)<2;
630  tools::Long dx=std::min(dxl,dxr);
631  tools::Long dy=std::min(dyo,dyu);
632  bool bDiag=std::abs(dx-dy)<2;
633  if (bxMitt && byMitt) return SdrEscapeDirection::ALL; // in the center
634  if (bDiag) { // diagonally
636  if (byMitt) nRet|=SdrEscapeDirection::VERT;
637  if (bxMitt) nRet|=SdrEscapeDirection::HORZ;
638  if (dxl<dxr) { // left
641  } else { // right
644  }
645  return nRet;
646  }
647  if (dx<dy) { // horizontal
648  if (bxMitt) return SdrEscapeDirection::HORZ;
649  if (dxl<dxr) return SdrEscapeDirection::LEFT;
650  else return SdrEscapeDirection::RIGHT;
651  } else { // vertical
652  if (byMitt) return SdrEscapeDirection::VERT;
653  if (dyo<dyu) return SdrEscapeDirection::TOP;
654  else return SdrEscapeDirection::BOTTOM;
655  }
656 }
657 
658 XPolygon SdrEdgeObj::ImpCalcObjToCenter(const Point& rStPt, tools::Long nEscAngle, const tools::Rectangle& rRect, const Point& rMeeting)
659 {
660  XPolygon aXP;
661  aXP.Insert(XPOLY_APPEND,rStPt,PolyFlags::Normal);
662  bool bRts=nEscAngle==0;
663  bool bObn=nEscAngle==9000;
664  bool bLks=nEscAngle==18000;
665  bool bUnt=nEscAngle==27000;
666 
667  Point aP1(rStPt); // mandatory difference first,...
668  if (bLks) aP1.setX(rRect.Left() );
669  if (bRts) aP1.setX(rRect.Right() );
670  if (bObn) aP1.setY(rRect.Top() );
671  if (bUnt) aP1.setY(rRect.Bottom() );
672 
673  Point aP2(aP1); // ...now increase to Meeting height, if necessary
674  if (bLks && rMeeting.X()<=aP2.X()) aP2.setX(rMeeting.X() );
675  if (bRts && rMeeting.X()>=aP2.X()) aP2.setX(rMeeting.X() );
676  if (bObn && rMeeting.Y()<=aP2.Y()) aP2.setY(rMeeting.Y() );
677  if (bUnt && rMeeting.Y()>=aP2.Y()) aP2.setY(rMeeting.Y() );
678  aXP.Insert(XPOLY_APPEND,aP2,PolyFlags::Normal);
679 
680  Point aP3(aP2);
681  if ((bLks && rMeeting.X()>aP2.X()) || (bRts && rMeeting.X()<aP2.X())) { // around
682  if (rMeeting.Y()<aP2.Y()) {
683  aP3.setY(rRect.Top() );
684  if (rMeeting.Y()<aP3.Y()) aP3.setY(rMeeting.Y() );
685  } else {
686  aP3.setY(rRect.Bottom() );
687  if (rMeeting.Y()>aP3.Y()) aP3.setY(rMeeting.Y() );
688  }
689  aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
690  if (aP3.Y()!=rMeeting.Y()) {
691  aP3.setX(rMeeting.X() );
692  aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
693  }
694  }
695  if ((bObn && rMeeting.Y()>aP2.Y()) || (bUnt && rMeeting.Y()<aP2.Y())) { // around
696  if (rMeeting.X()<aP2.X()) {
697  aP3.setX(rRect.Left() );
698  if (rMeeting.X()<aP3.X()) aP3.setX(rMeeting.X() );
699  } else {
700  aP3.setX(rRect.Right() );
701  if (rMeeting.X()>aP3.X()) aP3.setX(rMeeting.X() );
702  }
703  aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
704  if (aP3.X()!=rMeeting.X()) {
705  aP3.setY(rMeeting.Y() );
706  aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
707  }
708  }
709 #ifdef DBG_UTIL
710  if (aXP.GetPointCount()>4) {
711  OSL_FAIL("SdrEdgeObj::ImpCalcObjToCenter(): Polygon has more than 4 points!");
712  }
713 #endif
714  return aXP;
715 }
716 
718 {
719  Point aPt1,aPt2;
720  SdrGluePoint aGP1,aGP2;
722  tools::Rectangle aBoundRect1;
723  tools::Rectangle aBoundRect2;
724  tools::Rectangle aBewareRect1;
725  tools::Rectangle aBewareRect2;
726  // first, get the old corner points
727  if (rTrack0.GetPointCount()!=0) {
728  aPt1=rTrack0[0];
729  sal_uInt16 nSiz=rTrack0.GetPointCount();
730  nSiz--;
731  aPt2=rTrack0[nSiz];
732  } else {
733  if (!m_aOutRect.IsEmpty()) {
734  aPt1=m_aOutRect.TopLeft();
735  aPt2=m_aOutRect.BottomRight();
736  }
737  }
738 
739  // #i54102# To allow interactive preview, do also if not inserted
740  const bool bCon1(nullptr != rCon1.pObj && rCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
741  const bool bCon2(nullptr != rCon2.pObj && rCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
742  const SfxItemSet& rSet = GetObjectItemSet();
743 
744  if (bCon1)
745  {
746  if (rCon1.pObj==static_cast<SdrObject const *>(this))
747  {
748  // check, just in case
749  aBoundRect1=m_aOutRect;
750  }
751  else
752  {
753  aBoundRect1 = rCon1.pObj->GetCurrentBoundRect();
754  }
755 
756  aBoundRect1.Move(rCon1.aObjOfs.X(),rCon1.aObjOfs.Y());
757  aBewareRect1=aBoundRect1;
758  sal_Int32 nH = rSet.Get(SDRATTR_EDGENODE1HORZDIST).GetValue();
759  sal_Int32 nV = rSet.Get(SDRATTR_EDGENODE1VERTDIST).GetValue();
760  aBewareRect1.AdjustLeft( -nH );
761  aBewareRect1.AdjustRight(nH );
762  aBewareRect1.AdjustTop( -nV );
763  aBewareRect1.AdjustBottom(nV );
764  }
765  else
766  {
767  aBoundRect1=tools::Rectangle(aPt1,aPt1);
768  aBoundRect1.Move(rCon1.aObjOfs.X(),rCon1.aObjOfs.Y());
769  aBewareRect1=aBoundRect1;
770  }
771 
772  if (bCon2)
773  {
774  if (rCon2.pObj==static_cast<SdrObject const *>(this))
775  { // check, just in case
776  aBoundRect2=m_aOutRect;
777  }
778  else
779  {
780  aBoundRect2 = rCon2.pObj->GetCurrentBoundRect();
781  }
782 
783  aBoundRect2.Move(rCon2.aObjOfs.X(),rCon2.aObjOfs.Y());
784  aBewareRect2=aBoundRect2;
785  sal_Int32 nH = rSet.Get(SDRATTR_EDGENODE2HORZDIST).GetValue();
786  sal_Int32 nV = rSet.Get(SDRATTR_EDGENODE2VERTDIST).GetValue();
787  aBewareRect2.AdjustLeft( -nH );
788  aBewareRect2.AdjustRight(nH );
789  aBewareRect2.AdjustTop( -nV );
790  aBewareRect2.AdjustBottom(nV );
791  }
792  else
793  {
794  aBoundRect2=tools::Rectangle(aPt2,aPt2);
795  aBoundRect2.Move(rCon2.aObjOfs.X(),rCon2.aObjOfs.Y());
796  aBewareRect2=aBoundRect2;
797  }
798 
799  XPolygon aBestXP;
800  sal_uIntPtr nBestQual=0xFFFFFFFF;
801  SdrEdgeInfoRec aBestInfo;
802  bool bAuto1=bCon1 && rCon1.bBestVertex;
803  bool bAuto2=bCon2 && rCon2.bBestVertex;
804  if (bAuto1) rCon1.bAutoVertex=true;
805  if (bAuto2) rCon2.bAutoVertex=true;
806  sal_uInt16 nBestAuto1=0;
807  sal_uInt16 nBestAuto2=0;
808  sal_uInt16 nCount1=bAuto1 ? 4 : 1;
809  sal_uInt16 nCount2=bAuto2 ? 4 : 1;
810 
811  for (sal_uInt16 nNum1=0; nNum1<nCount1; nNum1++)
812  {
813  if (bAuto1) rCon1.nConId=nNum1;
814  if (bCon1 && rCon1.TakeGluePoint(aGP1))
815  {
816  aPt1=aGP1.GetPos();
817  nEsc1=aGP1.GetEscDir();
818  if (nEsc1==SdrEscapeDirection::SMART) nEsc1=ImpCalcEscAngle(rCon1.pObj,aPt1-rCon1.aObjOfs);
819  }
820  for (sal_uInt16 nNum2=0; nNum2<nCount2; nNum2++)
821  {
822  if (bAuto2) rCon2.nConId=nNum2;
823  if (bCon2 && rCon2.TakeGluePoint(aGP2))
824  {
825  aPt2=aGP2.GetPos();
826  nEsc2=aGP2.GetEscDir();
827  if (nEsc2==SdrEscapeDirection::SMART) nEsc2=ImpCalcEscAngle(rCon2.pObj,aPt2-rCon2.aObjOfs);
828  }
829  for (tools::Long nA1=0; nA1<36000; nA1+=9000)
830  {
832  for (tools::Long nA2=0; nA2<36000; nA2+=9000)
833  {
835  if ((nEsc1&nE1) && (nEsc2&nE2))
836  {
837  sal_uIntPtr nQual=0;
838  SdrEdgeInfoRec aInfo;
839  if (pInfo!=nullptr) aInfo=*pInfo;
840  XPolygon aXP(ImpCalcEdgeTrack(aPt1,nA1,aBoundRect1,aBewareRect1,aPt2,nA2,aBoundRect2,aBewareRect2,&nQual,&aInfo));
841  if (nQual<nBestQual)
842  {
843  aBestXP=std::move(aXP);
844  nBestQual=nQual;
845  aBestInfo=aInfo;
846  nBestAuto1=nNum1;
847  nBestAuto2=nNum2;
848  }
849  }
850  }
851  }
852  }
853  }
854  if (bAuto1) rCon1.nConId=nBestAuto1;
855  if (bAuto2) rCon2.nConId=nBestAuto2;
856  if (pInfo!=nullptr) *pInfo=aBestInfo;
857  return aBestXP;
858 }
859 
860 XPolygon SdrEdgeObj::ImpCalcEdgeTrack(const Point& rPt1, tools::Long nAngle1, const tools::Rectangle& rBoundRect1, const tools::Rectangle& rBewareRect1,
861  const Point& rPt2, tools::Long nAngle2, const tools::Rectangle& rBoundRect2, const tools::Rectangle& rBewareRect2,
862  sal_uIntPtr* pnQuality, SdrEdgeInfoRec* pInfo) const
863 {
864  SdrEdgeKind eKind=GetObjectItem(SDRATTR_EDGEKIND).GetValue();
865  bool bRts1=nAngle1==0;
866  bool bObn1=nAngle1==9000;
867  bool bLks1=nAngle1==18000;
868  bool bUnt1=nAngle1==27000;
869  bool bHor1=bLks1 || bRts1;
870  bool bVer1=bObn1 || bUnt1;
871  bool bRts2=nAngle2==0;
872  bool bObn2=nAngle2==9000;
873  bool bLks2=nAngle2==18000;
874  bool bUnt2=nAngle2==27000;
875  bool bHor2=bLks2 || bRts2;
876  bool bVer2=bObn2 || bUnt2;
877  bool bInfo=pInfo!=nullptr;
878  if (bInfo) {
879  pInfo->nAngle1=nAngle1;
880  pInfo->nAngle2=nAngle2;
881  pInfo->nObj1Lines=1;
882  pInfo->nObj2Lines=1;
883  pInfo->nMiddleLine=0xFFFF;
884  }
885  Point aPt1(rPt1);
886  Point aPt2(rPt2);
887  tools::Rectangle aBoundRect1 (rBoundRect1 );
888  tools::Rectangle aBoundRect2 (rBoundRect2 );
889  tools::Rectangle aBewareRect1(rBewareRect1);
890  tools::Rectangle aBewareRect2(rBewareRect2);
891  Point aMeeting((aPt1.X()+aPt2.X()+1)/2,(aPt1.Y()+aPt2.Y()+1)/2);
892  if (eKind==SdrEdgeKind::OneLine) {
893  XPolygon aXP(2);
894  aXP[0]=rPt1;
895  aXP[1]=rPt2;
896  if (pnQuality!=nullptr) {
897  *pnQuality=std::abs(rPt1.X()-rPt2.X())+std::abs(rPt1.Y()-rPt2.Y());
898  }
899  return aXP;
900  } else if (eKind==SdrEdgeKind::ThreeLines) {
901  XPolygon aXP(4);
902  aXP[0]=rPt1;
903  aXP[1]=rPt1;
904  aXP[2]=rPt2;
905  aXP[3]=rPt2;
906  if (bRts1) aXP[1].setX(aBewareRect1.Right() ); //+=500;
907  if (bObn1) aXP[1].setY(aBewareRect1.Top() ); //-=500;
908  if (bLks1) aXP[1].setX(aBewareRect1.Left() ); //-=500;
909  if (bUnt1) aXP[1].setY(aBewareRect1.Bottom() ); //+=500;
910  if (bRts2) aXP[2].setX(aBewareRect2.Right() ); //+=500;
911  if (bObn2) aXP[2].setY(aBewareRect2.Top() ); //-=500;
912  if (bLks2) aXP[2].setX(aBewareRect2.Left() ); //-=500;
913  if (bUnt2) aXP[2].setY(aBewareRect2.Bottom() ); //+=500;
914  if (pnQuality!=nullptr) {
915  tools::Long nQ=std::abs(aXP[1].X()-aXP[0].X())+std::abs(aXP[1].Y()-aXP[0].Y());
916  nQ+=std::abs(aXP[2].X()-aXP[1].X())+std::abs(aXP[2].Y()-aXP[1].Y());
917  nQ+=std::abs(aXP[3].X()-aXP[2].X())+std::abs(aXP[3].Y()-aXP[2].Y());
918  *pnQuality=nQ;
919  }
920  if (bInfo) {
921  pInfo->nObj1Lines=2;
922  pInfo->nObj2Lines=2;
923  if (bHor1) {
924  aXP[1].AdjustX(pInfo->aObj1Line2.X() );
925  } else {
926  aXP[1].AdjustY(pInfo->aObj1Line2.Y() );
927  }
928  if (bHor2) {
929  aXP[2].AdjustX(pInfo->aObj2Line2.X() );
930  } else {
931  aXP[2].AdjustY(pInfo->aObj2Line2.Y() );
932  }
933  }
934  return aXP;
935  }
936  sal_uInt16 nIntersections=0;
937  {
938  Point aC1(aBewareRect1.Center());
939  Point aC2(aBewareRect2.Center());
940  if (aBewareRect1.Left()<=aBewareRect2.Right() && aBewareRect1.Right()>=aBewareRect2.Left()) {
941  // overlapping on the x axis
942  tools::Long n1=std::max(aBewareRect1.Left(),aBewareRect2.Left());
943  tools::Long n2=std::min(aBewareRect1.Right(),aBewareRect2.Right());
944  aMeeting.setX((n1+n2+1)/2 );
945  } else {
946  // otherwise the center point of the empty space
947  if (aC1.X()<aC2.X()) {
948  aMeeting.setX((aBewareRect1.Right()+aBewareRect2.Left()+1)/2 );
949  } else {
950  aMeeting.setX((aBewareRect1.Left()+aBewareRect2.Right()+1)/2 );
951  }
952  }
953  if (aBewareRect1.Top()<=aBewareRect2.Bottom() && aBewareRect1.Bottom()>=aBewareRect2.Top()) {
954  // overlapping on the x axis
955  tools::Long n1=std::max(aBewareRect1.Top(),aBewareRect2.Top());
956  tools::Long n2=std::min(aBewareRect1.Bottom(),aBewareRect2.Bottom());
957  aMeeting.setY((n1+n2+1)/2 );
958  } else {
959  // otherwise the center point of the empty space
960  if (aC1.Y()<aC2.Y()) {
961  aMeeting.setY((aBewareRect1.Bottom()+aBewareRect2.Top()+1)/2 );
962  } else {
963  aMeeting.setY((aBewareRect1.Top()+aBewareRect2.Bottom()+1)/2 );
964  }
965  }
966  // Here, there are three cases:
967  // 1. both go into the same direction
968  // 2. both go into opposite directions
969  // 3. one is vertical, the other is horizontal
970  tools::Long nXMin=std::min(aBewareRect1.Left(),aBewareRect2.Left());
971  tools::Long nXMax=std::max(aBewareRect1.Right(),aBewareRect2.Right());
972  tools::Long nYMin=std::min(aBewareRect1.Top(),aBewareRect2.Top());
973  tools::Long nYMax=std::max(aBewareRect1.Bottom(),aBewareRect2.Bottom());
974  bool bBewareOverlap=aBewareRect1.Right()>aBewareRect2.Left() && aBewareRect1.Left()<aBewareRect2.Right() &&
975  aBewareRect1.Bottom()>aBewareRect2.Top() && aBewareRect1.Top()<aBewareRect2.Bottom();
976  unsigned nMainCase=3;
977  if (nAngle1==nAngle2) nMainCase=1;
978  else if ((bHor1 && bHor2) || (bVer1 && bVer2)) nMainCase=2;
979  if (nMainCase==1) { // case 1 (both go in one direction) is possible
980  if (bVer1) aMeeting.setX((aPt1.X()+aPt2.X()+1)/2 ); // Here, this is better than
981  if (bHor1) aMeeting.setY((aPt1.Y()+aPt2.Y()+1)/2 ); // using center point of empty space
982  // bX1Ok means that the vertical exiting Obj1 doesn't conflict with Obj2, ...
983  bool bX1Ok=aPt1.X()<=aBewareRect2.Left() || aPt1.X()>=aBewareRect2.Right();
984  bool bX2Ok=aPt2.X()<=aBewareRect1.Left() || aPt2.X()>=aBewareRect1.Right();
985  bool bY1Ok=aPt1.Y()<=aBewareRect2.Top() || aPt1.Y()>=aBewareRect2.Bottom();
986  bool bY2Ok=aPt2.Y()<=aBewareRect1.Top() || aPt2.Y()>=aBewareRect1.Bottom();
987  if (bLks1 && (bY1Ok || aBewareRect1.Left()<aBewareRect2.Right()) && (bY2Ok || aBewareRect2.Left()<aBewareRect1.Right())) {
988  aMeeting.setX(nXMin );
989  }
990  if (bRts1 && (bY1Ok || aBewareRect1.Right()>aBewareRect2.Left()) && (bY2Ok || aBewareRect2.Right()>aBewareRect1.Left())) {
991  aMeeting.setX(nXMax );
992  }
993  if (bObn1 && (bX1Ok || aBewareRect1.Top()<aBewareRect2.Bottom()) && (bX2Ok || aBewareRect2.Top()<aBewareRect1.Bottom())) {
994  aMeeting.setY(nYMin );
995  }
996  if (bUnt1 && (bX1Ok || aBewareRect1.Bottom()>aBewareRect2.Top()) && (bX2Ok || aBewareRect2.Bottom()>aBewareRect1.Top())) {
997  aMeeting.setY(nYMax );
998  }
999  } else if (nMainCase==2) {
1000  // case 2:
1001  if (bHor1) { // both horizontal
1002  /* 9 sub-cases:
1003  (legend: line exits to the left (-|), right (|-))
1004 
1005  2.1: Facing; overlap only on y axis
1006  * * *
1007  |--| *
1008  * * *
1009 
1010  2.2, 2.3: Facing, offset vertically; no overlap on either
1011  axis
1012  |- * * * * *
1013  * -| * * -| *
1014  * * * , * * *
1015 
1016  2.4, 2.5: One below the other; overlap only on y axis
1017  * |- * * * *
1018  * -| * * -| *
1019  * * * , * |- *
1020 
1021  2.6, 2.7: Not facing, offset vertically; no overlap on either
1022  axis
1023  * * |- * * *
1024  * -| * * -| *
1025  * * * , * * |-
1026 
1027  2.8: Not facing; overlap only on y axis
1028  * * *
1029  * -| |-
1030  * * *
1031 
1032  2.9: The objects's BewareRects overlap on x and y axis
1033 
1034  These cases, with some modifications are also valid for
1035  horizontal line exits.
1036  Cases 2.1 through 2.7 are covered well enough with the
1037  default meetings. Only for cases 2.8 and 2.9 do we determine
1038  special meeting points here.
1039  */
1040 
1041  // normalization; be aR1 the one exiting to the right,
1042  // be aR2 the one exiting to the left
1043  tools::Rectangle aBewR1(bRts1 ? aBewareRect1 : aBewareRect2);
1044  tools::Rectangle aBewR2(bRts1 ? aBewareRect2 : aBewareRect1);
1045  tools::Rectangle aBndR1(bRts1 ? aBoundRect1 : aBoundRect2);
1046  tools::Rectangle aBndR2(bRts1 ? aBoundRect2 : aBoundRect1);
1047  if (aBewR1.Bottom()>aBewR2.Top() && aBewR1.Top()<aBewR2.Bottom()) {
1048  // overlap on y axis; cases 2.1, 2.8, 2.9
1049  if (aBewR1.Right()>aBewR2.Left()) {
1050  /* Cases 2.8, 2.9:
1051  Case 2.8: always going around on the outside
1052  (bDirect=false).
1053 
1054  Case 2.9 could also be a direct connection (in the
1055  case that the BewareRects overlap only slightly and
1056  the BoundRects don't overlap at all and if the
1057  line exits would otherwise violate the respective
1058  other object's BewareRect).
1059  */
1060  bool bCase29Direct = false;
1061  bool bCase29=aBewR1.Right()>aBewR2.Left();
1062  if (aBndR1.Right()<=aBndR2.Left()) { // case 2.9 without BoundRect overlap
1063  if ((aPt1.Y()>aBewareRect2.Top() && aPt1.Y()<aBewareRect2.Bottom()) ||
1064  (aPt2.Y()>aBewareRect1.Top() && aPt2.Y()<aBewareRect1.Bottom())) {
1065  bCase29Direct = true;
1066  }
1067  }
1068  if (!bCase29Direct) {
1069  bool bObenLang=std::abs(nYMin-aMeeting.Y())<=std::abs(nYMax-aMeeting.Y());
1070  if (bObenLang) {
1071  aMeeting.setY(nYMin );
1072  } else {
1073  aMeeting.setY(nYMax );
1074  }
1075  if (bCase29) {
1076  // now make sure that the surrounded object
1077  // isn't traversed
1078  if ((aBewR1.Center().Y()<aBewR2.Center().Y()) != bObenLang) {
1079  aMeeting.setX(aBewR2.Right() );
1080  } else {
1081  aMeeting.setX(aBewR1.Left() );
1082  }
1083  }
1084  } else {
1085  // We need a direct connection (3-line Z connection),
1086  // because we have to violate the BewareRects.
1087  // Use rule of three to scale down the BewareRects.
1088  tools::Long nWant1=aBewR1.Right()-aBndR1.Right(); // distance at Obj1
1089  tools::Long nWant2=aBndR2.Left()-aBewR2.Left(); // distance at Obj2
1090  tools::Long nSpace=aBndR2.Left()-aBndR1.Right(); // available space
1091  tools::Long nGet1=BigMulDiv(nWant1,nSpace,nWant1+nWant2);
1092  tools::Long nGet2=nSpace-nGet1;
1093  if (bRts1) { // revert normalization
1094  aBewareRect1.AdjustRight(nGet1-nWant1 );
1095  aBewareRect2.AdjustLeft( -(nGet2-nWant2) );
1096  } else {
1097  aBewareRect2.AdjustRight(nGet1-nWant1 );
1098  aBewareRect1.AdjustLeft( -(nGet2-nWant2) );
1099  }
1100  nIntersections++; // lower quality
1101  }
1102  }
1103  }
1104  } else if (bVer1) { // both horizontal
1105  tools::Rectangle aBewR1(bUnt1 ? aBewareRect1 : aBewareRect2);
1106  tools::Rectangle aBewR2(bUnt1 ? aBewareRect2 : aBewareRect1);
1107  tools::Rectangle aBndR1(bUnt1 ? aBoundRect1 : aBoundRect2);
1108  tools::Rectangle aBndR2(bUnt1 ? aBoundRect2 : aBoundRect1);
1109  if (aBewR1.Right()>aBewR2.Left() && aBewR1.Left()<aBewR2.Right()) {
1110  // overlap on y axis; cases 2.1, 2.8, 2.9
1111  if (aBewR1.Bottom()>aBewR2.Top()) {
1112  /* Cases 2.8, 2.9
1113  Case 2.8 always going around on the outside (bDirect=false).
1114 
1115  Case 2.9 could also be a direct connection (in the
1116  case that the BewareRects overlap only slightly and
1117  the BoundRects don't overlap at all and if the
1118  line exits would otherwise violate the respective
1119  other object's BewareRect).
1120  */
1121  bool bCase29Direct = false;
1122  bool bCase29=aBewR1.Bottom()>aBewR2.Top();
1123  if (aBndR1.Bottom()<=aBndR2.Top()) { // case 2.9 without BoundRect overlap
1124  if ((aPt1.X()>aBewareRect2.Left() && aPt1.X()<aBewareRect2.Right()) ||
1125  (aPt2.X()>aBewareRect1.Left() && aPt2.X()<aBewareRect1.Right())) {
1126  bCase29Direct = true;
1127  }
1128  }
1129  if (!bCase29Direct) {
1130  bool bLinksLang=std::abs(nXMin-aMeeting.X())<=std::abs(nXMax-aMeeting.X());
1131  if (bLinksLang) {
1132  aMeeting.setX(nXMin );
1133  } else {
1134  aMeeting.setX(nXMax );
1135  }
1136  if (bCase29) {
1137  // now make sure that the surrounded object
1138  // isn't traversed
1139  if ((aBewR1.Center().X()<aBewR2.Center().X()) != bLinksLang) {
1140  aMeeting.setY(aBewR2.Bottom() );
1141  } else {
1142  aMeeting.setY(aBewR1.Top() );
1143  }
1144  }
1145  } else {
1146  // We need a direct connection (3-line Z connection),
1147  // because we have to violate the BewareRects.
1148  // Use rule of three to scale down the BewareRects.
1149  tools::Long nWant1=aBewR1.Bottom()-aBndR1.Bottom(); // difference at Obj1
1150  tools::Long nWant2=aBndR2.Top()-aBewR2.Top(); // difference at Obj2
1151  tools::Long nSpace=aBndR2.Top()-aBndR1.Bottom(); // available space
1152  tools::Long nGet1=BigMulDiv(nWant1,nSpace,nWant1+nWant2);
1153  tools::Long nGet2=nSpace-nGet1;
1154  if (bUnt1) { // revert normalization
1155  aBewareRect1.AdjustBottom(nGet1-nWant1 );
1156  aBewareRect2.AdjustTop( -(nGet2-nWant2) );
1157  } else {
1158  aBewareRect2.AdjustBottom(nGet1-nWant1 );
1159  aBewareRect1.AdjustTop( -(nGet2-nWant2) );
1160  }
1161  nIntersections++; // lower quality
1162  }
1163  }
1164  }
1165  }
1166  } else if (nMainCase==3) { // case 3: one horizontal, the other vertical
1167  /* legend:
1168  The line exits to the:
1169  -| left
1170 
1171  |- right
1172 
1173  _|_ top
1174 
1175  T bottom
1176 
1177  * . * . * -- no overlap, at most might touch
1178  . . . . . -- overlap
1179  * . |- . * -- same height
1180  . . . . . -- overlap
1181  * . * . * -- no overlap, at most might touch
1182 
1183  Overall, there are 96 possible constellations, some of these can't even
1184  be unambiguously assigned to a certain case/method of handling.
1185 
1186 
1187  3.1: All those constellations that are covered reasonably well
1188  by the default MeetingPoint (20+12).
1189 
1190  T T T . _|_ _|_ . T T T these 12 * . * T * * . * . * * T * . * * . * . *
1191  . . . . _|_ _|_ . . . . constellations . . . . . . . . . T . . . . . T . . . .
1192  * . |- . * * . -| . * are covered * . |- . _|_ * . |- . T _|_ . -| . * T . -| . *
1193  . . . . T T . . . . only in . . . . _|_ . . . . . _|_ . . . . . . . . .
1194  _|__|__|_ . T T . _|__|__|_ part: * . * _|_ * * . * . * * _|_ * . * * . * . *
1195 
1196  The last 16 of these cases can be excluded, if the objects face each other openly.
1197 
1198 
1199  3.2: The objects face each other openly, thus a connection using only two lines is possible (4+20);
1200  This case is priority #1.
1201  * . * . T T . * . * these 20 * . * T * * T * . * * . * . * * . * . *
1202  . . . . . . . . . . constellations . . . T T T T . . . . . . . . . . . . .
1203  * . |- . * * . -| . * are covered * . |-_|__|_ _|__|_-| . * * . |- T T T T -| . *
1204  . . . . . . . . . . only in . . . _|__|_ _|__|_ . . . . . . . . . . . . .
1205  * . * . _|_ _|_ . * . * part: * . * _|_ * * _|_ * . * * . * . * * . * . *
1206 
1207  3.3: The line exits point away from the other object or miss its back (52+4).
1208  _|__|__|__|_ * * _|__|__|__|_ * . . . * * . * . * these 4 * . * . * * . * . *
1209  _|__|__|__|_ . . _|__|__|__|_ T T T . . . . T T T constellations . . . T . . T . . .
1210  _|__|_ |- . * * . -| _|__|_ T T |- . * * . -| T T are covered * . |- . * * . -| . *
1211  _|__|__|_ . . . . _|__|__|_ T T T T . . T T T T only in . . . _|_ . . _|_ . . .
1212  * . * . * * . * . * T T T T * * T T T T part: * . * . * * . * . *
1213  */
1214 
1215  // case 3.2
1216  tools::Rectangle aTmpR1(aBewareRect1);
1217  tools::Rectangle aTmpR2(aBewareRect2);
1218  if (bBewareOverlap) {
1219  // overlapping BewareRects: use BoundRects for checking for case 3.2
1220  aTmpR1=aBoundRect1;
1221  aTmpR2=aBoundRect2;
1222  }
1223  if ((((bRts1 && aTmpR1.Right ()<=aPt2.X()) || (bLks1 && aTmpR1.Left()>=aPt2.X())) &&
1224  ((bUnt2 && aTmpR2.Bottom()<=aPt1.Y()) || (bObn2 && aTmpR2.Top ()>=aPt1.Y()))) ||
1225  (((bRts2 && aTmpR2.Right ()<=aPt1.X()) || (bLks2 && aTmpR2.Left()>=aPt1.X())) &&
1226  ((bUnt1 && aTmpR1.Bottom()<=aPt2.Y()) || (bObn1 && aTmpR1.Top ()>=aPt2.Y())))) {
1227  // case 3.2 applies: connector with only 2 lines
1228  if (bHor1) {
1229  aMeeting.setX(aPt2.X() );
1230  aMeeting.setY(aPt1.Y() );
1231  } else {
1232  aMeeting.setX(aPt1.X() );
1233  aMeeting.setY(aPt2.Y() );
1234  }
1235  // in the case of overlapping BewareRects:
1236  aBewareRect1=aTmpR1;
1237  aBewareRect2=aTmpR2;
1238  } else if ((((bRts1 && aBewareRect1.Right ()>aBewareRect2.Left ()) ||
1239  (bLks1 && aBewareRect1.Left ()<aBewareRect2.Right ())) &&
1240  ((bUnt2 && aBewareRect2.Bottom()>aBewareRect1.Top ()) ||
1241  (bObn2 && aBewareRect2.Top ()<aBewareRect1.Bottom()))) ||
1242  (((bRts2 && aBewareRect2.Right ()>aBewareRect1.Left ()) ||
1243  (bLks2 && aBewareRect2.Left ()<aBewareRect1.Right ())) &&
1244  ((bUnt1 && aBewareRect1.Bottom()>aBewareRect2.Top ()) ||
1245  (bObn1 && aBewareRect1.Top ()<aBewareRect2.Bottom())))) {
1246  // case 3.3
1247  if (bRts1 || bRts2) { aMeeting.setX(nXMax ); }
1248  if (bLks1 || bLks2) { aMeeting.setX(nXMin ); }
1249  if (bUnt1 || bUnt2) { aMeeting.setY(nYMax ); }
1250  if (bObn1 || bObn2) { aMeeting.setY(nYMin ); }
1251  }
1252  }
1253  }
1254 
1255  XPolygon aXP1(ImpCalcObjToCenter(aPt1,nAngle1,aBewareRect1,aMeeting));
1256  XPolygon aXP2(ImpCalcObjToCenter(aPt2,nAngle2,aBewareRect2,aMeeting));
1257  sal_uInt16 nXP1Cnt=aXP1.GetPointCount();
1258  sal_uInt16 nXP2Cnt=aXP2.GetPointCount();
1259  if (bInfo) {
1260  pInfo->nObj1Lines=nXP1Cnt; if (nXP1Cnt>1) pInfo->nObj1Lines--;
1261  pInfo->nObj2Lines=nXP2Cnt; if (nXP2Cnt>1) pInfo->nObj2Lines--;
1262  }
1263  Point aEP1(aXP1[nXP1Cnt-1]);
1264  Point aEP2(aXP2[nXP2Cnt-1]);
1265  bool bInsMeetingPoint=aEP1.X()!=aEP2.X() && aEP1.Y()!=aEP2.Y();
1266  bool bHorzE1=aEP1.Y()==aXP1[nXP1Cnt-2].Y(); // is last line of XP1 horizontal?
1267  bool bHorzE2=aEP2.Y()==aXP2[nXP2Cnt-2].Y(); // is last line of XP2 horizontal?
1268  if (aEP1==aEP2 && ((bHorzE1 && bHorzE2 && aEP1.Y()==aEP2.Y()) || (!bHorzE1 && !bHorzE2 && aEP1.X()==aEP2.X()))) {
1269  // special casing 'I' connectors
1270  nXP1Cnt--; aXP1.Remove(nXP1Cnt,1);
1271  nXP2Cnt--; aXP2.Remove(nXP2Cnt,1);
1272  }
1273  if (bInsMeetingPoint) {
1274  aXP1.Insert(XPOLY_APPEND,aMeeting,PolyFlags::Normal);
1275  if (bInfo) {
1276  // Inserting a MeetingPoint adds 2 new lines,
1277  // either might become the center line.
1278  if (pInfo->nObj1Lines==pInfo->nObj2Lines) {
1279  pInfo->nObj1Lines++;
1280  pInfo->nObj2Lines++;
1281  } else {
1282  if (pInfo->nObj1Lines>pInfo->nObj2Lines) {
1283  pInfo->nObj2Lines++;
1284  pInfo->nMiddleLine=nXP1Cnt-1;
1285  } else {
1286  pInfo->nObj1Lines++;
1287  pInfo->nMiddleLine=nXP1Cnt;
1288  }
1289  }
1290  }
1291  } else if (bInfo && aEP1!=aEP2 && nXP1Cnt+nXP2Cnt>=4) {
1292  // By connecting both ends, another line is added, this becomes the center line.
1293  pInfo->nMiddleLine=nXP1Cnt-1;
1294  }
1295  sal_uInt16 nNum=aXP2.GetPointCount();
1296  if (aXP1[nXP1Cnt-1]==aXP2[nXP2Cnt-1] && nXP1Cnt>1 && nXP2Cnt>1) nNum--;
1297  while (nNum>0) {
1298  nNum--;
1299  aXP1.Insert(XPOLY_APPEND,aXP2[nNum],PolyFlags::Normal);
1300  }
1301  sal_uInt16 nPointCount=aXP1.GetPointCount();
1302  char cForm;
1303  if (bInfo || pnQuality!=nullptr) {
1304  if (nPointCount==2) cForm='I';
1305  else if (nPointCount==3) cForm='L';
1306  else if (nPointCount==4) { // Z or U
1307  if (nAngle1==nAngle2) cForm='U';
1308  else cForm='Z';
1309  } else if (nPointCount==6) { // S or C or ...
1310  if (nAngle1!=nAngle2) {
1311  // For type S, line 2 has the same direction as line 4.
1312  // For type C, the opposite is true.
1313  Point aP1(aXP1[1]);
1314  Point aP2(aXP1[2]);
1315  Point aP3(aXP1[3]);
1316  Point aP4(aXP1[4]);
1317  if (aP1.Y()==aP2.Y()) { // else both lines are horizontal
1318  if ((aP1.X()<aP2.X())==(aP3.X()<aP4.X())) cForm='S';
1319  else cForm='C';
1320  } else { // else both lines are vertical
1321  if ((aP1.Y()<aP2.Y())==(aP3.Y()<aP4.Y())) cForm='S';
1322  else cForm='C';
1323  }
1324  } else cForm='4'; // else is case 3 with 5 lines
1325  } else cForm='?';
1326  // more shapes:
1327  if (bInfo) {
1328  if (cForm=='I' || cForm=='L' || cForm=='Z' || cForm=='U') {
1329  pInfo->nObj1Lines=1;
1330  pInfo->nObj2Lines=1;
1331  if (cForm=='Z' || cForm=='U') {
1332  pInfo->nMiddleLine=1;
1333  } else {
1334  pInfo->nMiddleLine=0xFFFF;
1335  }
1336  } else if (cForm=='S' || cForm=='C') {
1337  pInfo->nObj1Lines=2;
1338  pInfo->nObj2Lines=2;
1339  pInfo->nMiddleLine=2;
1340  }
1341  }
1342  }
1343  else
1344  {
1345  cForm = 0;
1346  }
1347  if (pnQuality!=nullptr) {
1348  sal_uIntPtr nQual=0;
1349  sal_uIntPtr nQual0=nQual; // prevent overruns
1350  bool bOverflow = false;
1351  Point aPt0(aXP1[0]);
1352  for (sal_uInt16 nPntNum=1; nPntNum<nPointCount; nPntNum++) {
1353  Point aPt1b(aXP1[nPntNum]);
1354  nQual+=std::abs(aPt1b.X()-aPt0.X())+std::abs(aPt1b.Y()-aPt0.Y());
1355  if (nQual<nQual0) bOverflow = true;
1356  nQual0=nQual;
1357  aPt0=aPt1b;
1358  }
1359 
1360  sal_uInt16 nTmp=nPointCount;
1361  if (cForm=='Z') {
1362  nTmp=2; // Z shape with good quality (nTmp=2 instead of 4)
1363  sal_uIntPtr n1=std::abs(aXP1[1].X()-aXP1[0].X())+std::abs(aXP1[1].Y()-aXP1[0].Y());
1364  sal_uIntPtr n2=std::abs(aXP1[2].X()-aXP1[1].X())+std::abs(aXP1[2].Y()-aXP1[1].Y());
1365  sal_uIntPtr n3=std::abs(aXP1[3].X()-aXP1[2].X())+std::abs(aXP1[3].Y()-aXP1[2].Y());
1366  // try to make lines lengths similar
1367  sal_uIntPtr nBesser=0;
1368  n1+=n3;
1369  n3=n2/4;
1370  if (n1>=n2) nBesser=6;
1371  else if (n1>=3*n3) nBesser=4;
1372  else if (n1>=2*n3) nBesser=2;
1373  if (aXP1[0].Y()!=aXP1[1].Y()) nBesser++; // vertical starting line gets a plus (for H/V-Prio)
1374  if (nQual>nBesser) nQual-=nBesser; else nQual=0;
1375  }
1376  if (nTmp>=3) {
1377  nQual0=nQual;
1378  nQual+=static_cast<sal_uIntPtr>(nTmp)*0x01000000;
1379  if (nQual<nQual0 || nTmp>15) bOverflow = true;
1380  }
1381  if (nPointCount>=2) { // check exit angle again
1382  Point aP1(aXP1[1]); aP1-=aXP1[0];
1383  Point aP2(aXP1[nPointCount-2]); aP2-=aXP1[nPointCount-1];
1384  tools::Long nAng1=0; if (aP1.X()<0) nAng1=18000; if (aP1.Y()>0) nAng1=27000;
1385  if (aP1.Y()<0) nAng1=9000;
1386  if (aP1.X()!=0 && aP1.Y()!=0) nAng1=1; // slant?!
1387  tools::Long nAng2=0; if (aP2.X()<0) nAng2=18000; if (aP2.Y()>0) nAng2=27000;
1388  if (aP2.Y()<0) nAng2=9000;
1389  if (aP2.X()!=0 && aP2.Y()!=0) nAng2=1; // slant?!
1390  if (nAng1!=nAngle1) nIntersections++;
1391  if (nAng2!=nAngle2) nIntersections++;
1392  }
1393 
1394  // For the quality check, use the original Rects and at the same time
1395  // check whether one them was scaled down for the calculation of the
1396  // Edges (e. g. case 2.9)
1397  aBewareRect1=rBewareRect1;
1398  aBewareRect2=rBewareRect2;
1399 
1400  for (sal_uInt16 i=0; i<nPointCount; i++) {
1401  Point aPt1b(aXP1[i]);
1402  bool b1=aPt1b.X()>aBewareRect1.Left() && aPt1b.X()<aBewareRect1.Right() &&
1403  aPt1b.Y()>aBewareRect1.Top() && aPt1b.Y()<aBewareRect1.Bottom();
1404  bool b2=aPt1b.X()>aBewareRect2.Left() && aPt1b.X()<aBewareRect2.Right() &&
1405  aPt1b.Y()>aBewareRect2.Top() && aPt1b.Y()<aBewareRect2.Bottom();
1406  sal_uInt16 nInt0=nIntersections;
1407  if (i==0 || i==nPointCount-1) {
1408  if (b1 && b2) nIntersections++;
1409  } else {
1410  if (b1) nIntersections++;
1411  if (b2) nIntersections++;
1412  }
1413  // check for overlaps
1414  if (i>0 && nInt0==nIntersections) {
1415  if (aPt0.Y()==aPt1b.Y()) { // horizontal line
1416  if (aPt0.Y()>aBewareRect1.Top() && aPt0.Y()<aBewareRect1.Bottom() &&
1417  ((aPt0.X()<=aBewareRect1.Left() && aPt1b.X()>=aBewareRect1.Right()) ||
1418  (aPt1b.X()<=aBewareRect1.Left() && aPt0.X()>=aBewareRect1.Right()))) nIntersections++;
1419  if (aPt0.Y()>aBewareRect2.Top() && aPt0.Y()<aBewareRect2.Bottom() &&
1420  ((aPt0.X()<=aBewareRect2.Left() && aPt1b.X()>=aBewareRect2.Right()) ||
1421  (aPt1b.X()<=aBewareRect2.Left() && aPt0.X()>=aBewareRect2.Right()))) nIntersections++;
1422  } else { // vertical line
1423  if (aPt0.X()>aBewareRect1.Left() && aPt0.X()<aBewareRect1.Right() &&
1424  ((aPt0.Y()<=aBewareRect1.Top() && aPt1b.Y()>=aBewareRect1.Bottom()) ||
1425  (aPt1b.Y()<=aBewareRect1.Top() && aPt0.Y()>=aBewareRect1.Bottom()))) nIntersections++;
1426  if (aPt0.X()>aBewareRect2.Left() && aPt0.X()<aBewareRect2.Right() &&
1427  ((aPt0.Y()<=aBewareRect2.Top() && aPt1b.Y()>=aBewareRect2.Bottom()) ||
1428  (aPt1b.Y()<=aBewareRect2.Top() && aPt0.Y()>=aBewareRect2.Bottom()))) nIntersections++;
1429  }
1430  }
1431  aPt0=aPt1b;
1432  }
1433  if (nPointCount<=1) nIntersections++;
1434  nQual0=nQual;
1435  nQual+=static_cast<sal_uIntPtr>(nIntersections)*0x10000000;
1436  if (nQual<nQual0 || nIntersections>15) bOverflow = true;
1437 
1438  if (bOverflow || nQual==0xFFFFFFFF) nQual=0xFFFFFFFE;
1439  *pnQuality=nQual;
1440  }
1441  if (bInfo) { // now apply line offsets to aXP1
1442  if (pInfo->nMiddleLine!=0xFFFF) {
1443  sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::MiddleLine,aXP1);
1444  if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::MiddleLine,aXP1)) {
1445  aXP1[nIdx].AdjustY(pInfo->aMiddleLine.Y() );
1446  aXP1[nIdx+1].AdjustY(pInfo->aMiddleLine.Y() );
1447  } else {
1448  aXP1[nIdx].AdjustX(pInfo->aMiddleLine.X() );
1449  aXP1[nIdx+1].AdjustX(pInfo->aMiddleLine.X() );
1450  }
1451  }
1452  if (pInfo->nObj1Lines>=2) {
1453  sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj1Line2,aXP1);
1454  if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj1Line2,aXP1)) {
1455  aXP1[nIdx].AdjustY(pInfo->aObj1Line2.Y() );
1456  aXP1[nIdx+1].AdjustY(pInfo->aObj1Line2.Y() );
1457  } else {
1458  aXP1[nIdx].AdjustX(pInfo->aObj1Line2.X() );
1459  aXP1[nIdx+1].AdjustX(pInfo->aObj1Line2.X() );
1460  }
1461  }
1462  if (pInfo->nObj1Lines>=3) {
1463  sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj1Line3,aXP1);
1464  if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj1Line3,aXP1)) {
1465  aXP1[nIdx].AdjustY(pInfo->aObj1Line3.Y() );
1466  aXP1[nIdx+1].AdjustY(pInfo->aObj1Line3.Y() );
1467  } else {
1468  aXP1[nIdx].AdjustX(pInfo->aObj1Line3.X() );
1469  aXP1[nIdx+1].AdjustX(pInfo->aObj1Line3.X() );
1470  }
1471  }
1472  if (pInfo->nObj2Lines>=2) {
1473  sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj2Line2,aXP1);
1474  if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj2Line2,aXP1)) {
1475  aXP1[nIdx].AdjustY(pInfo->aObj2Line2.Y() );
1476  aXP1[nIdx+1].AdjustY(pInfo->aObj2Line2.Y() );
1477  } else {
1478  aXP1[nIdx].AdjustX(pInfo->aObj2Line2.X() );
1479  aXP1[nIdx+1].AdjustX(pInfo->aObj2Line2.X() );
1480  }
1481  }
1482  if (pInfo->nObj2Lines>=3) {
1483  sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj2Line3,aXP1);
1484  if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj2Line3,aXP1)) {
1485  aXP1[nIdx].AdjustY(pInfo->aObj2Line3.Y() );
1486  aXP1[nIdx+1].AdjustY(pInfo->aObj2Line3.Y() );
1487  } else {
1488  aXP1[nIdx].AdjustX(pInfo->aObj2Line3.X() );
1489  aXP1[nIdx+1].AdjustX(pInfo->aObj2Line3.X() );
1490  }
1491  }
1492  }
1493  // make the connector a bezier curve, if appropriate
1494  if (eKind==SdrEdgeKind::Bezier && nPointCount>2) {
1495  Point* pPt1=&aXP1[0];
1496  Point* pPt2=&aXP1[1];
1497  Point* pPt3=&aXP1[nPointCount-2];
1498  Point* pPt4=&aXP1[nPointCount-1];
1499  tools::Long dx1=pPt2->X()-pPt1->X();
1500  tools::Long dy1=pPt2->Y()-pPt1->Y();
1501  tools::Long dx2=pPt3->X()-pPt4->X();
1502  tools::Long dy2=pPt3->Y()-pPt4->Y();
1503  if (cForm=='L') { // nPointCount==3
1504  aXP1.SetFlags(1,PolyFlags::Control);
1505  Point aPt3(*pPt2);
1506  aXP1.Insert(2,aPt3,PolyFlags::Control);
1507  nPointCount=aXP1.GetPointCount();
1508  pPt2=&aXP1[1];
1509  pPt3=&aXP1[nPointCount-2];
1510  pPt2->AdjustX( -(dx1/3) );
1511  pPt2->AdjustY( -(dy1/3) );
1512  pPt3->AdjustX( -(dx2/3) );
1513  pPt3->AdjustY( -(dy2/3) );
1514  } else if (nPointCount>=4 && nPointCount<=6) { // Z or U or ...
1515  // To all others, the end points of the original lines become control
1516  // points for now. Thus, we need to do some more work for nPointCount>4!
1517  aXP1.SetFlags(1,PolyFlags::Control);
1518  aXP1.SetFlags(nPointCount-2,PolyFlags::Control);
1519  // distance x1.5
1520  pPt2->AdjustX(dx1/2 );
1521  pPt2->AdjustY(dy1/2 );
1522  pPt3->AdjustX(dx2/2 );
1523  pPt3->AdjustY(dy2/2 );
1524  if (nPointCount==5) {
1525  // add a control point before and after center
1526  Point aCenter(aXP1[2]);
1527  tools::Long dx1b=aCenter.X()-aXP1[1].X();
1528  tools::Long dy1b=aCenter.Y()-aXP1[1].Y();
1529  tools::Long dx2b=aCenter.X()-aXP1[3].X();
1530  tools::Long dy2b=aCenter.Y()-aXP1[3].Y();
1531  aXP1.Insert(2,aCenter,PolyFlags::Control);
1532  aXP1.SetFlags(3,PolyFlags::Symmetric);
1533  aXP1.Insert(4,aCenter,PolyFlags::Control);
1534  aXP1[2].AdjustX( -(dx1b/2) );
1535  aXP1[2].AdjustY( -(dy1b/2) );
1536  aXP1[3].AdjustX( -((dx1b+dx2b)/4) );
1537  aXP1[3].AdjustY( -((dy1b+dy2b)/4) );
1538  aXP1[4].AdjustX( -(dx2b/2) );
1539  aXP1[4].AdjustY( -(dy2b/2) );
1540  }
1541  if (nPointCount==6) {
1542  Point aPt1b(aXP1[2]);
1543  Point aPt2b(aXP1[3]);
1544  aXP1.Insert(2,aPt1b,PolyFlags::Control);
1545  aXP1.Insert(5,aPt2b,PolyFlags::Control);
1546  tools::Long dx=aPt1b.X()-aPt2b.X();
1547  tools::Long dy=aPt1b.Y()-aPt2b.Y();
1548  aXP1[3].AdjustX( -(dx/2) );
1549  aXP1[3].AdjustY( -(dy/2) );
1550  aXP1.SetFlags(3,PolyFlags::Symmetric);
1551  aXP1.Remove(4,1); // because it's identical with aXP1[3]
1552  }
1553  }
1554  }
1555  return aXP1;
1556 }
1557 
1558 /*
1559 There could be a maximum of 64 different developments with 5 lines, a
1560 maximum of 32 developments with 4 lines, a maximum of 16 developments with
1561 3 lines, a maximum of 8 developments with 2 lines.
1562 This gives us a total of 124 possibilities.
1563 Normalized for the 1st exit angle to the right, there remain 31 possibilities.
1564 Now, normalizing away the vertical mirroring, we get to a total of 16
1565 characteristic developments with 1 through 5 lines:
1566 
1567 1 line (type "I") --
1568 
1569 2 lines (type "L") __|
1570 
1571 3 lines (type "U") __ (type "Z") _
1572  __| _|
1573  _ _
1574 4 lines #1 _| #2 | | #3 |_ #4 | |
1575  _| _| _| _|
1576  Of these, #1 is implausible, #2 is a rotated version of #3. This leaves
1577  #2 (from now on referred to as 4.1) and #4 (from now on referred to as 4.2).
1578  _ _
1579 5 lines #1 _| #2 _| #3 ___ #4 _
1580  _| _| _| _| _| |_
1581  _ _ _
1582  #5 |_ #6 |_ #7 _| | #8 ____
1583  _| _| _| |_ _|
1584  Of these, 5.1, 5.2, 5.4 and 5.5 are implausible, 5.7 is a reversed version
1585  of 5.3. This leaves 5.3 (type "4"), 5.6 (type "S") and 5.8 (type "C").
1586 
1587 We now have discerned the 9 basic types to cover all 400 possible constellations
1588 of object positions and exit angles. 4 of the 9 types have got a center
1589 line (CL). The number of object margins per object varies between 0 and 3:
1590 
1591  CL O1 O2 Note
1592 "I": n 0 0
1593 "L": n 0 0
1594 "U": n 0-1 0-1
1595 "Z": y 0 0
1596 4.2: y 0 1 = U+1, respectively 1+U
1597 4.4: n 0-2 0-2 = Z+1
1598 "4": y 0 2 = Z+2
1599 "S": y 1 1 = 1+Z+1
1600 "C": n 0-3 0-3 = 1+U+1
1601 */
1602 
1604 {
1605  const SfxHintId nId = rHint.GetId();
1606  bool bDataChg=nId==SfxHintId::DataChanged;
1607  bool bDying=nId==SfxHintId::Dying;
1608  bool bObj1=aCon1.pObj!=nullptr && aCon1.pObj->GetBroadcaster()==&rBC;
1609  bool bObj2=aCon2.pObj!=nullptr && aCon2.pObj->GetBroadcaster()==&rBC;
1610  if (bDying && (bObj1 || bObj2)) {
1611  // catch Dying, so AttrObj doesn't start broadcasting
1612  // about an alleged change of template
1613  if (bObj1) aCon1.pObj=nullptr;
1614  if (bObj2) aCon2.pObj=nullptr;
1615  return;
1616  }
1617  if ( bObj1 || bObj2 )
1618  {
1619  bEdgeTrackUserDefined = false;
1620  }
1621  SdrTextObj::Notify(rBC,rHint);
1622  if (nNotifyingCount!=0)return;
1623 
1624 // a locking flag
1625  nNotifyingCount++;
1626  const SdrHint* pSdrHint = ( rHint.GetId() == SfxHintId::ThisIsAnSdrHint ? static_cast<const SdrHint*>(&rHint) : nullptr );
1627 
1628  if (bDataChg) { // StyleSheet changed
1629  ImpSetAttrToEdgeInfo(); // when changing templates, copy values from Pool to aEdgeInfo
1630  }
1631  if (bDataChg ||
1634  (pSdrHint && pSdrHint->GetKind()==SdrHintKind::ObjectRemoved))
1635  {
1636  // broadcasting only, if on the same page
1637  tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetCurrentBoundRect();
1639 
1640  // only redraw here, object hasn't actually changed
1641  ActionChanged();
1642 
1644  }
1645  nNotifyingCount--;
1646 }
1647 
1652 {
1653  if( nullptr != aCon1.pObj )
1654  {
1655  SfxHint aHint( SfxHintId::DataChanged );
1656  Notify( *const_cast<SfxBroadcaster*>(aCon1.pObj->GetBroadcaster()), aHint );
1657  }
1658 
1659  if( nullptr != aCon2.pObj )
1660  {
1661  SfxHint aHint( SfxHintId::DataChanged );
1662  Notify( *const_cast<SfxBroadcaster*>(aCon2.pObj->GetBroadcaster()), aHint );
1663  }
1664 }
1665 
1667 {
1668  return new SdrEdgeObj(rTargetModel, *this);
1669 }
1670 
1672 {
1673  OUString sName(SvxResId(STR_ObjNameSingulEDGE));
1674 
1675  OUString aName(GetName());
1676  if (!aName.isEmpty())
1677  sName += " '" + aName + "'";
1678  return sName;
1679 }
1680 
1682 {
1683  return SvxResId(STR_ObjNamePluralEDGE);
1684 }
1685 
1687 {
1688  basegfx::B2DPolyPolygon aPolyPolygon;
1689 
1690  if (bEdgeTrackDirty)
1691  {
1692  const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
1693  }
1694 
1695  if(pEdgeTrack)
1696  {
1697  aPolyPolygon.append(pEdgeTrack->getB2DPolygon());
1698  }
1699 
1700  return aPolyPolygon;
1701 }
1702 
1704 {
1705  if ( !rPoly.count() )
1706  {
1707  bEdgeTrackDirty = true;
1708  bEdgeTrackUserDefined = false;
1709  }
1710  else
1711  {
1712  *pEdgeTrack = XPolygon( rPoly.getB2DPolygon( 0 ) );
1713  bEdgeTrackDirty = false;
1714  bEdgeTrackUserDefined = true;
1715 
1716  // #i110629# also set aRect and maSnapeRect depending on pEdgeTrack
1717  const tools::Rectangle aPolygonBounds(pEdgeTrack->GetBoundRect());
1718  maRect = aPolygonBounds;
1719  maSnapRect = aPolygonBounds;
1720  }
1721 }
1722 
1724 {
1725  basegfx::B2DPolyPolygon aPolyPolygon;
1726 
1727  if (bEdgeTrackDirty)
1728  const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
1729 
1730  aPolyPolygon.append( pEdgeTrack->getB2DPolygon() );
1731 
1732  return aPolyPolygon;
1733 }
1734 
1735 sal_uInt32 SdrEdgeObj::GetHdlCount() const
1736 {
1737  SdrEdgeKind eKind=GetObjectItem(SDRATTR_EDGEKIND).GetValue();
1738  sal_uInt32 nHdlCnt(0);
1739  sal_uInt32 nPointCount(pEdgeTrack->GetPointCount());
1740 
1741  if(nPointCount)
1742  {
1743  nHdlCnt = 2;
1744  if ((eKind==SdrEdgeKind::OrthoLines || eKind==SdrEdgeKind::Bezier) && nPointCount >= 4)
1745  {
1746  sal_uInt32 nO1(aEdgeInfo.nObj1Lines > 0 ? aEdgeInfo.nObj1Lines - 1 : 0);
1747  sal_uInt32 nO2(aEdgeInfo.nObj2Lines > 0 ? aEdgeInfo.nObj2Lines - 1 : 0);
1748  sal_uInt32 nM(aEdgeInfo.nMiddleLine != 0xFFFF ? 1 : 0);
1749  nHdlCnt += nO1 + nO2 + nM;
1750  }
1751  else if (eKind==SdrEdgeKind::ThreeLines && nPointCount == 4)
1752  {
1753  if(GetConnectedNode(true))
1754  nHdlCnt++;
1755 
1756  if(GetConnectedNode(false))
1757  nHdlCnt++;
1758  }
1759  }
1760 
1761  return nHdlCnt;
1762 }
1763 
1765 {
1766  sal_uInt32 nPointCount(pEdgeTrack->GetPointCount());
1767  if (nPointCount==0)
1768  return;
1769 
1770  {
1771  std::unique_ptr<SdrHdl> pHdl(new ImpEdgeHdl((*pEdgeTrack)[0],SdrHdlKind::Poly));
1772  if (aCon1.pObj!=nullptr && aCon1.bBestVertex) pHdl->Set1PixMore();
1773  pHdl->SetPointNum(0);
1774  rHdlList.AddHdl(std::move(pHdl));
1775  }
1776  {
1777  std::unique_ptr<SdrHdl> pHdl(new ImpEdgeHdl((*pEdgeTrack)[sal_uInt16(nPointCount-1)],SdrHdlKind::Poly));
1778  if (aCon2.pObj!=nullptr && aCon2.bBestVertex) pHdl->Set1PixMore();
1779  pHdl->SetPointNum(1);
1780  rHdlList.AddHdl(std::move(pHdl));
1781  }
1782  {
1783  SdrEdgeKind eKind=GetObjectItem(SDRATTR_EDGEKIND).GetValue();
1784  if ((eKind==SdrEdgeKind::OrthoLines || eKind==SdrEdgeKind::Bezier) && nPointCount >= 4)
1785  {
1786  sal_uInt32 nO1(aEdgeInfo.nObj1Lines > 0 ? aEdgeInfo.nObj1Lines - 1 : 0);
1787  sal_uInt32 nO2(aEdgeInfo.nObj2Lines > 0 ? aEdgeInfo.nObj2Lines - 1 : 0);
1788  sal_uInt32 nM(aEdgeInfo.nMiddleLine != 0xFFFF ? 1 : 0);
1789  for(sal_uInt32 i = 0; i < (nO1 + nO2 + nM); ++i)
1790  {
1791  sal_Int32 nPt(0);
1792  sal_uInt32 nNum = i;
1793  std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(Point(),SdrHdlKind::Poly));
1794  if (nNum<nO1) {
1795  nPt=nNum+1;
1796  if (nNum==0) pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line2);
1797  if (nNum==1) pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line3);
1798  } else {
1799  nNum=nNum-nO1;
1800  if (nNum<nO2) {
1801  nPt=nPointCount-3-nNum;
1802  if (nNum==0) pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line2);
1803  if (nNum==1) pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line3);
1804  } else {
1805  nNum=nNum-nO2;
1806  if (nNum<nM) {
1807  nPt=aEdgeInfo.nMiddleLine;
1808  pHdl->SetLineCode(SdrEdgeLineCode::MiddleLine);
1809  }
1810  }
1811  }
1812  if (nPt>0) {
1813  Point aPos((*pEdgeTrack)[static_cast<sal_uInt16>(nPt)]);
1814  aPos+=(*pEdgeTrack)[static_cast<sal_uInt16>(nPt)+1];
1815  aPos.setX( aPos.X() / 2 );
1816  aPos.setY( aPos.Y() / 2 );
1817  pHdl->SetPos(aPos);
1818  pHdl->SetPointNum(i + 2);
1819  rHdlList.AddHdl(std::move(pHdl));
1820  }
1821  }
1822  }
1823  else if (eKind==SdrEdgeKind::ThreeLines && nPointCount == 4)
1824  {
1825  if(GetConnectedNode(true))
1826  {
1827  Point aPos((*pEdgeTrack)[1]);
1828  std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(aPos,SdrHdlKind::Poly));
1829  pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line2);
1830  pHdl->SetPointNum(2);
1831  rHdlList.AddHdl(std::move(pHdl));
1832  }
1833  if(GetConnectedNode(false))
1834  {
1835  Point aPos((*pEdgeTrack)[2]);
1836  std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(aPos,SdrHdlKind::Poly));
1837  pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line2);
1838  pHdl->SetPointNum(3);
1839  rHdlList.AddHdl(std::move(pHdl));
1840  }
1841  }
1842  }
1843 }
1844 
1846 {
1847  return true;
1848 }
1849 
1851 {
1852  // use Clone operator
1854 
1855  // copy connections for clone, SdrEdgeObj::operator= does not do this
1856  pRetval->ConnectToNode(true, GetConnectedNode(true));
1857  pRetval->ConnectToNode(false, GetConnectedNode(false));
1858 
1859  return SdrObjectUniquePtr(pRetval);
1860 }
1861 
1863 {
1864  if(!rDrag.GetHdl())
1865  return false;
1866 
1867  rDrag.SetEndDragChangesAttributes(true);
1868 
1869  if(rDrag.GetHdl()->GetPointNum() < 2)
1870  {
1871  rDrag.SetNoSnap();
1872  }
1873 
1874  return true;
1875 }
1876 
1878 {
1879  SdrEdgeObj* pOriginalEdge = dynamic_cast< SdrEdgeObj* >(rDragStat.GetHdl()->GetObj());
1880  const bool bOriginalEdgeModified(pOriginalEdge == this);
1881 
1882  if(!bOriginalEdgeModified && pOriginalEdge)
1883  {
1884  // copy connections when clone is modified. This is needed because
1885  // as preparation to this modification the data from the original object
1886  // was copied to the clone using the operator=. As can be seen there,
1887  // that operator does not copy the connections (for good reason)
1888  ConnectToNode(true, pOriginalEdge->GetConnection(true).GetObject());
1889  ConnectToNode(false, pOriginalEdge->GetConnection(false).GetObject());
1890  }
1891 
1892  if(rDragStat.GetHdl()->GetPointNum() < 2)
1893  {
1894  // start or end point connector drag
1895  const bool bDragA(0 == rDragStat.GetHdl()->GetPointNum());
1896  const Point aPointNow(rDragStat.GetNow());
1897 
1898  rDragStat.SetEndDragChangesGeoAndAttributes(true);
1899 
1900  if(rDragStat.GetPageView())
1901  {
1902  SdrObjConnection* pDraggedOne(bDragA ? &aCon1 : &aCon2);
1903 
1904  // clear connection
1905  DisconnectFromNode(bDragA);
1906 
1907  // look for new connection
1908  ImpFindConnector(aPointNow, *rDragStat.GetPageView(), *pDraggedOne, pOriginalEdge, nullptr, &rDragStat);
1909 
1910  if(pDraggedOne->pObj)
1911  {
1912  // if found, officially connect to it; ImpFindConnector only
1913  // sets pObj hard
1914  SdrObject* pNewConnection = pDraggedOne->pObj;
1915  pDraggedOne->pObj = nullptr;
1916  ConnectToNode(bDragA, pNewConnection);
1917  }
1918 
1919  if(rDragStat.GetView() && !bOriginalEdgeModified)
1920  {
1921  // show IA helper, but only do this during IA, so not when the original
1922  // Edge gets modified in the last call
1923  rDragStat.GetView()->SetConnectMarker(*pDraggedOne);
1924  }
1925  }
1926 
1927  if(pEdgeTrack)
1928  {
1929  // change pEdgeTrack to modified position
1930  if(bDragA)
1931  {
1932  (*pEdgeTrack)[0] = aPointNow;
1933  }
1934  else
1935  {
1936  (*pEdgeTrack)[sal_uInt16(pEdgeTrack->GetPointCount()-1)] = aPointNow;
1937  }
1938  }
1939 
1940  // reset edge info's offsets, this is an end point drag
1946  }
1947  else
1948  {
1949  // control point connector drag
1950  const ImpEdgeHdl* pEdgeHdl = static_cast<const ImpEdgeHdl*>(rDragStat.GetHdl());
1951  const SdrEdgeLineCode eLineCode = pEdgeHdl->GetLineCode();
1952  const Point aDist(rDragStat.GetNow() - rDragStat.GetStart());
1953  sal_Int32 nDist(pEdgeHdl->IsHorzDrag() ? aDist.X() : aDist.Y());
1954 
1955  nDist += aEdgeInfo.ImpGetLineOffset(eLineCode, *pEdgeTrack);
1956  aEdgeInfo.ImpSetLineOffset(eLineCode, *pEdgeTrack, nDist);
1957  }
1958 
1959  // force recalculation of EdgeTrack
1961  bEdgeTrackDirty=false;
1962 
1963  // save EdgeInfos and mark object as user modified
1965  bEdgeTrackUserDefined = false;
1966 
1968 
1969  if(bOriginalEdgeModified && rDragStat.GetView())
1970  {
1971  // hide connect marker helper again when original gets changed.
1972  // This happens at the end of the interaction
1973  rDragStat.GetView()->HideConnectMarker();
1974  }
1975 
1976  return true;
1977 }
1978 
1980 {
1981  const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
1982 
1983  if(bCreateComment)
1984  {
1985  return OUString();
1986  }
1987  else
1988  {
1989  return ImpGetDescriptionStr(STR_DragEdgeTail);
1990  }
1991 }
1992 
1993 
1994 basegfx::B2DPolygon SdrEdgeObj::ImplAddConnectorOverlay(const SdrDragMethod& rDragMethod, bool bTail1, bool bTail2, bool bDetail) const
1995 {
1996  basegfx::B2DPolygon aResult;
1997 
1998  if(bDetail)
1999  {
2000  SdrObjConnection aMyCon1(aCon1);
2001  SdrObjConnection aMyCon2(aCon2);
2002 
2003  if (bTail1)
2004  {
2005  const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aMyCon1.aObjOfs.X(), aMyCon1.aObjOfs.Y()));
2006  aMyCon1.aObjOfs.setX( basegfx::fround(aTemp.getX()) );
2007  aMyCon1.aObjOfs.setY( basegfx::fround(aTemp.getY()) );
2008  }
2009 
2010  if (bTail2)
2011  {
2012  const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aMyCon2.aObjOfs.X(), aMyCon2.aObjOfs.Y()));
2013  aMyCon2.aObjOfs.setX( basegfx::fround(aTemp.getX()) );
2014  aMyCon2.aObjOfs.setY( basegfx::fround(aTemp.getY()) );
2015  }
2016 
2017  SdrEdgeInfoRec aInfo(aEdgeInfo);
2018  XPolygon aXP(ImpCalcEdgeTrack(*pEdgeTrack, aMyCon1, aMyCon2, &aInfo));
2019 
2020  if(aXP.GetPointCount())
2021  {
2022  aResult = aXP.getB2DPolygon();
2023  }
2024  }
2025  else
2026  {
2027  Point aPt1((*pEdgeTrack)[0]);
2028  Point aPt2((*pEdgeTrack)[sal_uInt16(pEdgeTrack->GetPointCount() - 1)]);
2029 
2030  if (aCon1.pObj && (aCon1.bBestConn || aCon1.bBestVertex))
2031  aPt1 = aCon1.pObj->GetSnapRect().Center();
2032 
2033  if (aCon2.pObj && (aCon2.bBestConn || aCon2.bBestVertex))
2034  aPt2 = aCon2.pObj->GetSnapRect().Center();
2035 
2036  if (bTail1)
2037  {
2038  const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aPt1.X(), aPt1.Y()));
2039  aPt1.setX( basegfx::fround(aTemp.getX()) );
2040  aPt1.setY( basegfx::fround(aTemp.getY()) );
2041  }
2042 
2043  if (bTail2)
2044  {
2045  const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aPt2.X(), aPt2.Y()));
2046  aPt2.setX( basegfx::fround(aTemp.getX()) );
2047  aPt2.setY( basegfx::fround(aTemp.getY()) );
2048  }
2049 
2050  aResult.append(basegfx::B2DPoint(aPt1.X(), aPt1.Y()));
2051  aResult.append(basegfx::B2DPoint(aPt2.X(), aPt2.Y()));
2052  }
2053 
2054  return aResult;
2055 }
2056 
2058 {
2059  rDragStat.SetNoSnap();
2060  pEdgeTrack->SetPointCount(2);
2061  (*pEdgeTrack)[0]=rDragStat.GetStart();
2062  (*pEdgeTrack)[1]=rDragStat.GetNow();
2063  if (rDragStat.GetPageView()!=nullptr) {
2064  ImpFindConnector(rDragStat.GetStart(),*rDragStat.GetPageView(),aCon1,this);
2065  ConnectToNode(true,aCon1.pObj);
2066  }
2068  return true;
2069 }
2070 
2072 {
2073  sal_uInt16 nMax=pEdgeTrack->GetPointCount();
2074  (*pEdgeTrack)[nMax-1]=rDragStat.GetNow();
2075  if (rDragStat.GetPageView()!=nullptr) {
2076  ImpFindConnector(rDragStat.GetNow(),*rDragStat.GetPageView(),aCon2,this);
2077  rDragStat.GetView()->SetConnectMarker(aCon2);
2078  }
2080  m_bSnapRectDirty=true;
2081  ConnectToNode(false,aCon2.pObj);
2083  bEdgeTrackDirty=false;
2084  return true;
2085 }
2086 
2088 {
2089  bool bOk=(eCmd==SdrCreateCmd::ForceEnd || rDragStat.GetPointCount()>=2);
2090  if (bOk) {
2091  ConnectToNode(true,aCon1.pObj);
2092  ConnectToNode(false,aCon2.pObj);
2093  if (rDragStat.GetView()!=nullptr) {
2094  rDragStat.GetView()->HideConnectMarker();
2095  }
2096  ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
2097  }
2099  return bOk;
2100 }
2101 
2103 {
2104  if (rDragStat.GetView()!=nullptr) {
2105  rDragStat.GetView()->HideConnectMarker();
2106  }
2107  return false;
2108 }
2109 
2111 {
2112  if (rDragStat.GetView()!=nullptr) {
2113  rDragStat.GetView()->HideConnectMarker();
2114  }
2115 }
2116 
2118 {
2119  basegfx::B2DPolyPolygon aRetval;
2120  aRetval.append(pEdgeTrack->getB2DPolygon());
2121  return aRetval;
2122 }
2123 
2125 {
2126  return PointerStyle::DrawConnect;
2127 }
2128 
2129 bool SdrEdgeObj::ImpFindConnector(const Point& rPt, const SdrPageView& rPV, SdrObjConnection& rCon, const SdrEdgeObj* pThis, OutputDevice* pOut, SdrDragStat* pDragStat)
2130 {
2131  rCon.ResetVars();
2132  if (pOut==nullptr) pOut=rPV.GetView().GetFirstOutputDevice();
2133  if (pOut==nullptr) return false;
2134  SdrObjList* pOL=rPV.GetObjList();
2135  const SdrLayerIDSet& rVisLayer=rPV.GetVisibleLayers();
2136  // sensitive area of connectors is twice as large as the one of the handles
2137  sal_uInt16 nMarkHdSiz=rPV.GetView().GetMarkHdlSizePixel();
2138  Size aHalfConSiz(nMarkHdSiz,nMarkHdSiz);
2139  if (comphelper::LibreOfficeKit::isActive() && pOut->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
2140  aHalfConSiz=pOut->PixelToLogic(aHalfConSiz, MapMode(MapUnit::Map100thMM));
2141  else
2142  aHalfConSiz=pOut->PixelToLogic(aHalfConSiz);
2143  tools::Rectangle aMouseRect(rPt,rPt);
2144  aMouseRect.AdjustLeft( -(aHalfConSiz.Width()) );
2145  aMouseRect.AdjustTop( -(aHalfConSiz.Height()) );
2146  aMouseRect.AdjustRight(aHalfConSiz.Width() );
2147  aMouseRect.AdjustBottom(aHalfConSiz.Height() );
2148  sal_uInt16 nBoundHitTol=static_cast<sal_uInt16>(aHalfConSiz.Width())/2; if (nBoundHitTol==0) nBoundHitTol=1;
2149  size_t no=pOL->GetObjCount();
2150  bool bFnd = false;
2151  SdrObjConnection aTestCon;
2152  bool bTiledRendering = comphelper::LibreOfficeKit::isActive();
2153  bool bHasRequestedOrdNum = false;
2154  sal_Int32 requestedOrdNum = -1;
2155 
2156  if (bTiledRendering && pDragStat)
2157  {
2158  auto& glueOptions = pDragStat->GetGlueOptions();
2159  if (glueOptions.objectOrdNum != -1)
2160  {
2161  requestedOrdNum = glueOptions.objectOrdNum;
2162  bHasRequestedOrdNum = true;
2163  }
2164  }
2165 
2166  while (no>0 && !bFnd) {
2167  // issue: group objects on different layers return LayerID=0!
2168  no--;
2169  SdrObject* pObj=pOL->GetObj(no);
2170  if (bHasRequestedOrdNum)
2171  {
2172  if (pObj->GetOrdNumDirect() != static_cast<sal_uInt32>(requestedOrdNum))
2173  continue;
2174  }
2175  if (rVisLayer.IsSet(pObj->GetLayer()) && pObj->IsVisible() && // only visible objects
2176  (pThis==nullptr || pObj!=static_cast<SdrObject const *>(pThis))) // don't connect it to itself
2177  {
2178  tools::Rectangle aObjBound(pObj->GetCurrentBoundRect());
2179  if (aObjBound.Overlaps(aMouseRect)) {
2180  aTestCon.ResetVars();
2181  bool bEdge=dynamic_cast<const SdrEdgeObj *>(pObj) != nullptr; // no BestCon for Edge
2182  // User-defined connectors have absolute priority.
2183  // After those come Vertex, Corner and center (Best), all prioritized equally.
2184  // Finally, a HitTest for the object.
2185  const SdrGluePointList* pGPL=pObj->GetGluePointList();
2186  sal_uInt16 nGluePointCnt=pGPL==nullptr ? 0 : pGPL->GetCount();
2187  sal_uInt16 nGesAnz=nGluePointCnt+9;
2188  bool bUserFnd = false;
2189  sal_uIntPtr nBestDist=0xFFFFFFFF;
2190  for (sal_uInt16 i=0; i<nGesAnz; i++)
2191  {
2192  bool bUser=i<nGluePointCnt;
2193  bool bVertex=i>=nGluePointCnt+0 && i<nGluePointCnt+4;
2194  bool bCorner=i>=nGluePointCnt+4 && i<nGluePointCnt+8;
2195  bool bCenter=i==nGluePointCnt+8;
2196  bool bOk = false;
2197  Point aConPos;
2198  sal_uInt16 nConNum=i;
2199  if (bUser) {
2200  const SdrGluePoint& rGP=(*pGPL)[nConNum];
2201  aConPos=rGP.GetAbsolutePos(*pObj);
2202  nConNum=rGP.GetId();
2203  bOk = true;
2204  } else if (bVertex && !bUserFnd) {
2205  nConNum=nConNum-nGluePointCnt;
2206  SdrGluePoint aPt(pObj->GetVertexGluePoint(nConNum));
2207  aConPos=aPt.GetAbsolutePos(*pObj);
2208  bOk = true;
2209  } else if (bCorner && !bUserFnd) {
2210  nConNum-=nGluePointCnt+4;
2211  i+=3;
2212  }
2213  else if (bCenter && !bUserFnd && !bEdge)
2214  {
2215  // Suppress default connect at object center
2216  if(!pThis || !pThis->GetSuppressDefaultConnect())
2217  {
2218  // not the edges!
2219  nConNum=0;
2220  aConPos=aObjBound.Center();
2221  bOk = true;
2222  }
2223  }
2224  if (bOk && aMouseRect.Contains(aConPos)) {
2225  if (bUser) bUserFnd = true;
2226  bFnd = true;
2227  sal_uIntPtr nDist=static_cast<sal_uIntPtr>(std::abs(aConPos.X()-rPt.X()))+static_cast<sal_uIntPtr>(std::abs(aConPos.Y()-rPt.Y()));
2228  if (nDist<nBestDist) {
2229  nBestDist=nDist;
2230  aTestCon.pObj=pObj;
2231  aTestCon.nConId=nConNum;
2232  aTestCon.bAutoCorner=bCorner;
2233  aTestCon.bAutoVertex=bVertex;
2234  aTestCon.bBestConn=false; // bCenter;
2235  aTestCon.bBestVertex=bCenter;
2236  }
2237  }
2238  }
2239  // if no connector is hit, try HitTest again, for BestConnector (=bCenter)
2240  if(!bFnd &&
2241  !bEdge &&
2242  SdrObjectPrimitiveHit(*pObj, rPt, nBoundHitTol, rPV, &rVisLayer, false))
2243  {
2244  // Suppress default connect at object inside bound
2245  if(!pThis || !pThis->GetSuppressDefaultConnect())
2246  {
2247  bFnd = true;
2248  aTestCon.pObj=pObj;
2249  aTestCon.bBestConn=true;
2250  }
2251  }
2252  if (bFnd) {
2253  aMouseRect.AdjustLeft( -nBoundHitTol );
2254  aMouseRect.AdjustTop( -nBoundHitTol );
2255  aMouseRect.AdjustRight(nBoundHitTol );
2256  aMouseRect.AdjustBottom(nBoundHitTol );
2257  }
2258 
2259  }
2260  }
2261  }
2262  rCon=aTestCon;
2263  return bFnd;
2264 }
2265 
2267 {
2268  const tools::Rectangle aOld(GetSnapRect());
2269 
2270  if(aOld == rRect)
2271  return;
2272 
2273  if (maRect.IsEmpty() && 0 == pEdgeTrack->GetPointCount())
2274  {
2275  // #i110629# When initializing, do not scale on empty Rectangle; this
2276  // will mirror the underlying text object (!)
2277  maRect = rRect;
2278  maSnapRect = rRect;
2279  }
2280  else
2281  {
2282  tools::Long nMulX = rRect.Right() - rRect.Left();
2283  tools::Long nDivX = aOld.Right() - aOld.Left();
2284  tools::Long nMulY = rRect.Bottom() - rRect.Top();
2285  tools::Long nDivY = aOld.Bottom() - aOld.Top();
2286  if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
2287  if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
2288  Fraction aX(nMulX, nDivX);
2289  Fraction aY(nMulY, nDivY);
2290  NbcResize(aOld.TopLeft(), aX, aY);
2291  NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
2292  }
2293 }
2294 
2295 void SdrEdgeObj::NbcMove(const Size& rSiz)
2296 {
2297  SdrTextObj::NbcMove(rSiz);
2298  MoveXPoly(*pEdgeTrack,rSiz);
2299 }
2300 
2301 void SdrEdgeObj::NbcResize(const Point& rRefPnt, const Fraction& aXFact, const Fraction& aYFact)
2302 {
2303  SdrTextObj::NbcResize(rRefPnt,aXFact,aXFact);
2304  ResizeXPoly(*pEdgeTrack,rRefPnt,aXFact,aYFact);
2305 
2306  // if resize is not from paste, forget user distances
2307  if (!getSdrModelFromSdrObject().IsPasteResize())
2308  {
2314  }
2315 }
2316 
2317 // #i54102# added rotation support
2318 void SdrEdgeObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
2319 {
2321  {
2322  // #i120437# special handling when track is imported, apply
2323  // transformation directly to imported track.
2324  SdrTextObj::NbcRotate(rRef, nAngle, sn, cs);
2325  RotateXPoly(*pEdgeTrack, rRef, sn, cs);
2326  }
2327  else
2328  {
2329  // handle start and end point if not connected
2330  const bool bCon1(nullptr != aCon1.pObj && aCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2331  const bool bCon2(nullptr != aCon2.pObj && aCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2332 
2333  if(!bCon1 && pEdgeTrack)
2334  {
2335  RotatePoint((*pEdgeTrack)[0],rRef,sn,cs);
2337  }
2338 
2339  if(!bCon2 && pEdgeTrack)
2340  {
2341  sal_uInt16 nPointCount = pEdgeTrack->GetPointCount();
2342  RotatePoint((*pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef,sn,cs);
2344  }
2345  }
2346 }
2347 
2348 // #i54102# added mirror support
2349 void SdrEdgeObj::NbcMirror(const Point& rRef1, const Point& rRef2)
2350 {
2352  {
2353  // #i120437# special handling when track is imported, apply
2354  // transformation directly to imported track.
2355  SdrTextObj::NbcMirror(rRef1, rRef2);
2356  MirrorXPoly(*pEdgeTrack, rRef1, rRef2);
2357  }
2358  else
2359  {
2360  // handle start and end point if not connected
2361  const bool bCon1(nullptr != aCon1.pObj && aCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2362  const bool bCon2(nullptr != aCon2.pObj && aCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2363 
2364  if(!bCon1 && pEdgeTrack)
2365  {
2366  MirrorPoint((*pEdgeTrack)[0],rRef1,rRef2);
2368  }
2369 
2370  if(!bCon2 && pEdgeTrack)
2371  {
2372  sal_uInt16 nPointCount = pEdgeTrack->GetPointCount();
2373  MirrorPoint((*pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef1,rRef2);
2375  }
2376  }
2377 }
2378 
2379 // #i54102# added shear support
2380 void SdrEdgeObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
2381 {
2383  {
2384  // #i120437# special handling when track is imported, apply
2385  // transformation directly to imported track.
2386  SdrTextObj::NbcShear(rRef, nAngle, tn, bVShear);
2387  ShearXPoly(*pEdgeTrack, rRef, tn, bVShear);
2388  }
2389  else
2390  {
2391  // handle start and end point if not connected
2392  const bool bCon1(nullptr != aCon1.pObj && aCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2393  const bool bCon2(nullptr != aCon2.pObj && aCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2394 
2395  if(!bCon1 && pEdgeTrack)
2396  {
2397  ShearPoint((*pEdgeTrack)[0],rRef,tn,bVShear);
2399  }
2400 
2401  if(!bCon2 && pEdgeTrack)
2402  {
2403  sal_uInt16 nPointCount = pEdgeTrack->GetPointCount();
2404  ShearPoint((*pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef,tn,bVShear);
2406  }
2407  }
2408 }
2409 
2410 SdrObjectUniquePtr SdrEdgeObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
2411 {
2412  basegfx::B2DPolyPolygon aPolyPolygon;
2413  aPolyPolygon.append(pEdgeTrack->getB2DPolygon());
2414  SdrObjectUniquePtr pRet = ImpConvertMakeObj(aPolyPolygon, false, bBezier);
2415 
2416  if(bAddText)
2417  {
2418  pRet = ImpConvertAddText(std::move(pRet), bBezier);
2419  }
2420 
2421  return pRet;
2422 }
2423 
2425 {
2426  return 2;
2427 }
2428 
2429 Point SdrEdgeObj::GetSnapPoint(sal_uInt32 i) const
2430 {
2431  const_cast<SdrEdgeObj*>(this)->ImpUndirtyEdgeTrack();
2432  sal_uInt16 nCount=pEdgeTrack->GetPointCount();
2433  if (i==0) return (*pEdgeTrack)[0];
2434  else return (*pEdgeTrack)[nCount-1];
2435 }
2436 
2438 {
2439  return false;
2440 }
2441 
2442 sal_uInt32 SdrEdgeObj::GetPointCount() const
2443 {
2444  return 0;
2445 }
2446 
2447 Point SdrEdgeObj::GetPoint(sal_uInt32 i) const
2448 {
2449  const_cast<SdrEdgeObj*>(this)->ImpUndirtyEdgeTrack();
2450  sal_uInt16 nCount=pEdgeTrack->GetPointCount();
2451  if (0 == i)
2452  return (*pEdgeTrack)[0];
2453  else
2454  return (*pEdgeTrack)[nCount-1];
2455 }
2456 
2457 void SdrEdgeObj::NbcSetPoint(const Point& rPnt, sal_uInt32 i)
2458 {
2459  // TODO: Need an implementation to connect differently.
2461  sal_uInt16 nCount=pEdgeTrack->GetPointCount();
2462  if (0 == i)
2463  (*pEdgeTrack)[0]=rPnt;
2464  if (1 == i)
2465  (*pEdgeTrack)[nCount-1]=rPnt;
2468 }
2469 
2471  : pEdgeTrack(std::in_place)
2472  , bEdgeTrackDirty(false)
2473  , bEdgeTrackUserDefined(false)
2474 {
2475 }
2476 
2478 {
2479 }
2480 
2481 std::unique_ptr<SdrObjGeoData> SdrEdgeObj::NewGeoData() const
2482 {
2483  return std::make_unique<SdrEdgeObjGeoData>();
2484 }
2485 
2487 {
2489  SdrEdgeObjGeoData& rEGeo=static_cast<SdrEdgeObjGeoData&>(rGeo);
2490  rEGeo.aCon1 =aCon1;
2491  rEGeo.aCon2 =aCon2;
2492  *rEGeo.pEdgeTrack =*pEdgeTrack;
2495  rEGeo.aEdgeInfo =aEdgeInfo;
2496 }
2497 
2499 {
2501  const SdrEdgeObjGeoData& rEGeo=static_cast<const SdrEdgeObjGeoData&>(rGeo);
2502  if (aCon1.pObj!=rEGeo.aCon1.pObj) {
2503  if (aCon1.pObj!=nullptr) aCon1.pObj->RemoveListener(*this);
2504  aCon1=rEGeo.aCon1;
2505  if (aCon1.pObj!=nullptr) aCon1.pObj->AddListener(*this);
2506  }
2507  else
2508  aCon1=rEGeo.aCon1;
2509 
2510  if (aCon2.pObj!=rEGeo.aCon2.pObj) {
2511  if (aCon2.pObj!=nullptr) aCon2.pObj->RemoveListener(*this);
2512  aCon2=rEGeo.aCon2;
2513  if (aCon2.pObj!=nullptr) aCon2.pObj->AddListener(*this);
2514  }
2515  else
2516  aCon2=rEGeo.aCon2;
2517 
2518  *pEdgeTrack =*rEGeo.pEdgeTrack;
2521  aEdgeInfo =rEGeo.aEdgeInfo;
2522 }
2523 
2524 Point SdrEdgeObj::GetTailPoint( bool bTail ) const
2525 {
2526  if( pEdgeTrack && pEdgeTrack->GetPointCount()!=0)
2527  {
2528  const XPolygon& rTrack0 = *pEdgeTrack;
2529  if(bTail)
2530  {
2531  return rTrack0[0];
2532  }
2533  else
2534  {
2535  const sal_uInt16 nSiz = rTrack0.GetPointCount() - 1;
2536  return rTrack0[nSiz];
2537  }
2538  }
2539  else
2540  {
2541  if(bTail)
2542  return m_aOutRect.TopLeft();
2543  else
2544  return m_aOutRect.BottomRight();
2545  }
2546 
2547 }
2548 
2549 void SdrEdgeObj::SetTailPoint( bool bTail, const Point& rPt )
2550 {
2551  ImpSetTailPoint( bTail, rPt );
2552  SetChanged();
2553 }
2554 
2560 void SdrEdgeObj::setGluePointIndex( bool bTail, sal_Int32 nIndex /* = -1 */ )
2561 {
2562  SdrObjConnection& rConn1 = GetConnection( bTail );
2563 
2564  rConn1.SetAutoVertex( nIndex >= 0 && nIndex <= 3 );
2565  rConn1.SetBestConnection( nIndex < 0 );
2566  rConn1.SetBestVertex( nIndex < 0 );
2567 
2568  if( nIndex > 3 )
2569  {
2570  nIndex -= 3; // the start api index is 0, whereas the implementation in svx starts from 1
2571 
2572  // for user defined gluepoints we have
2573  // to get the id for this index first
2574  const SdrGluePointList* pList = rConn1.GetObject() ? rConn1.GetObject()->GetGluePointList() : nullptr;
2575  if( pList == nullptr || SDRGLUEPOINT_NOTFOUND == pList->FindGluePoint(static_cast<sal_uInt16>(nIndex)) )
2576  return;
2577  }
2578  else if( nIndex < 0 )
2579  {
2580  nIndex = 0;
2581  }
2582 
2583  rConn1.SetConnectorId( static_cast<sal_uInt16>(nIndex) );
2584 
2585  SetChanged();
2588 }
2589 
2592 sal_Int32 SdrEdgeObj::getGluePointIndex( bool bTail )
2593 {
2594  SdrObjConnection& rConn1 = GetConnection( bTail );
2595  sal_Int32 nId = -1;
2596  if( !rConn1.IsBestConnection() )
2597  {
2598  nId = rConn1.GetConnectorId();
2599  if( !rConn1.IsAutoVertex() )
2600  nId += 3; // the start api index is 0, whereas the implementation in svx starts from 1
2601  }
2602  return nId;
2603 }
2604 
2605 // Implementation was missing; edge track needs to be invalidated additionally.
2606 void SdrEdgeObj::NbcSetAnchorPos(const Point& rPnt)
2607 {
2608  // call parent functionality
2610 
2611  // Additionally, invalidate edge track
2613 }
2614 
2616 {
2617  // use base method from SdrObject, it's not rotatable and
2618  // a call to GetSnapRect() is used. That's what we need for Connector.
2619  return SdrObject::TRGetBaseGeometry(rMatrix, rPolyPolygon);
2620 }
2621 
2623 {
2624  // where appropriate take care for existing connections. For now, just use the
2625  // implementation from SdrObject.
2626  SdrObject::TRSetBaseGeometry(rMatrix, rPolyPolygon);
2627 }
2628 
2629 // for geometry access
2631 {
2632  if(bEdgeTrackDirty)
2633  {
2634  const_cast< SdrEdgeObj* >(this)->ImpRecalcEdgeTrack();
2635  }
2636 
2637  if(pEdgeTrack)
2638  {
2639  return pEdgeTrack->getB2DPolygon();
2640  }
2641  else
2642  {
2643  return ::basegfx::B2DPolygon();
2644  }
2645 }
2646 
2647 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::optional< XPolygon > pEdgeTrack
Definition: svdoedge.hxx:144
virtual void ClearObjectItemDirect(const sal_uInt16 nWhich)=0
double getY() const
virtual void NbcSetAnchorPos(const Point &rPnt) override
Definition: svdoedge.cxx:2606
virtual OUString TakeObjNameSingul() const override
Definition: svdoedge.cxx:1671
Point aMiddleLine
Definition: svdoedge.hxx:85
Point aObj2Line2
Definition: svdoedge.hxx:83
virtual bool BegCreate(SdrDragStat &rStat) override
Every object must be able to create itself interactively.
Definition: svdoedge.cxx:2057
SfxHintId
virtual void NbcResize(const Point &rRef, const Fraction &xFact, const Fraction &yFact) override
Definition: svdotxtr.cxx:102
void ShearXPoly(XPolygon &rPoly, const Point &rRef, double tn, bool bVShear)
Definition: svdtrans.cxx:155
void append(const basegfx::B2DPoint &rPoint, sal_uInt32 nCount)
OLE object.
Utility class SdrObjConnection.
Definition: svdoedge.hxx:39
SdrPageView * GetPageView() const
Definition: svddrag.hxx:98
SdrHintKind GetKind() const
Definition: svdmodel.hxx:132
virtual SdrGluePoint GetCornerGluePoint(sal_uInt16 nNum) const override
Definition: svdoedge.cxx:455
virtual const tools::Rectangle & GetCurrentBoundRect() const
Definition: svdobj.cxx:949
void ImpRecalcEdgeTrack()
Definition: svdoedge.cxx:565
virtual void SetObjectItemDirect(const SfxPoolItem &rItem)=0
virtual ~SdrEdgeObj() override
Definition: svdoedge.cxx:192
void ShearPoint(Point &rPnt, const Point &rRef, double tn, bool bVShear=false)
Definition: svdtrans.hxx:109
tools::Long ImpGetLineOffset(SdrEdgeLineCode eLineCode, const XPolygon &rXP) const
Definition: svdoedge.cxx:130
void SetAutoVertex(bool rB)
Definition: svdoedge.hxx:62
tools::Long AdjustRight(tools::Long nHorzMoveDelta)
virtual const SdrGluePointList * GetGluePointList() const override
Definition: svdoedge.cxx:460
virtual bool IsPolyObj() const override
Definition: svdoedge.cxx:2437
SdrObjConnection aCon2
Definition: svdoedge.hxx:116
bool m_bClosedObj
Definition: svdobj.hxx:902
::basegfx::B2DPolygon getEdgeTrack() const
Definition: svdoedge.cxx:2630
constexpr tools::Long Left() const
const Point & GetStart() const
Definition: svddrag.hxx:102
SdrEscapeDirection
Definition: svdglue.hxx:34
SdrView & GetView()
Definition: svdpagv.hxx:130
bool Contains(const Point &rPOINT) const
int n1
virtual SdrObjectUniquePtr DoConvertToPolyObj(bool bBezier, bool bAddText) const override
Definition: svdoedge.cxx:2410
sal_uInt16 nNotifyingCount
Definition: svdoedge.hxx:145
void ResizeXPoly(XPolygon &rPoly, const Point &rRef, const Fraction &xFact, const Fraction &yFact)
Definition: svdtrans.cxx:73
static XPolygon ImpCalcObjToCenter(const Point &rStPt, tools::Long nEscAngle, const tools::Rectangle &rRect, const Point &rCenter)
Definition: svdoedge.cxx:658
sal_uInt16 nMiddleLine
Definition: svdoedge.hxx:92
Utility class SdrEdgeInfoRec.
Definition: svdoedge.hxx:75
long Long
sal_uInt16 nObj2Lines
Definition: svdoedge.hxx:91
virtual basegfx::B2DPolyPolygon TakeCreatePoly(const SdrDragStat &rDrag) const override
Polygon dragged by the user when creating the object.
Definition: svdoedge.cxx:2117
bool ImpCanConvTextToCurve() const
Definition: svdotxtr.cxx:418
constexpr TypedWhichId< SdrEdgeNode1HorzDistItem > SDRATTR_EDGENODE1HORZDIST(SDRATTR_EDGE_FIRST+1)
bool IsHorzDrag() const
Definition: svdhdl.cxx:1694
sal_Int64 n
SdrObject * GetObj(size_t nNum) const
Definition: svdpage.cxx:807
bool IsInserted() const
Definition: svdobj.hxx:739
size_t GetObjCount() const
Definition: svdpage.cxx:801
sal_uInt32 GetPointNum() const
Definition: svdhdl.hxx:222
tools::Long nAngle2
Definition: svdoedge.hxx:89
virtual void SetBoundRectDirty()
Definition: svdobj.cxx:316
bool bEdgeTrackUserDefined
Definition: svdoedge.hxx:119
virtual bool MovCreate(SdrDragStat &rStat) override
Definition: svdoedge.cxx:2071
sal_Int16 nId
void ImpSetEdgeInfoToAttr()
Definition: svdoedge.cxx:282
virtual bool TRGetBaseGeometry(basegfx::B2DHomMatrix &rMatrix, basegfx::B2DPolyPolygon &rPolyPolygon) const override
Definition: svdoedge.cxx:2615
const MapMode & GetMapMode() const
SdrEdgeInfoRec aEdgeInfo
Definition: svdoedge.hxx:146
SdrObject * GetObj() const
Definition: svdhdl.hxx:203
constexpr TypedWhichId< SdrEdgeNode2VertDistItem > SDRATTR_EDGENODE2VERTDIST(SDRATTR_EDGE_FIRST+4)
SdrObjectUniquePtr ImpConvertAddText(SdrObjectUniquePtr pObj, bool bBezier) const
Definition: svdotxtr.cxx:457
virtual void NbcSetSnapRect(const tools::Rectangle &rRect) override
Definition: svdoedge.cxx:2266
const SfxBroadcaster * GetBroadcaster() const
Definition: svdobj.cxx:718
void ResetVars()
Definition: svdoedge.cxx:48
tools::Rectangle m_aOutRect
Definition: svdobj.hxx:882
virtual OUString TakeObjNamePlural() const override
Definition: svdoedge.cxx:1681
virtual std::unique_ptr< sdr::properties::BaseProperties > CreateObjectSpecificProperties() override
Definition: svdoedge.cxx:142
sal_uInt16 GetCount() const
Definition: svdglue.hxx:194
virtual sdr::properties::BaseProperties & GetProperties() const
Definition: svdobj.cxx:210
virtual void NbcRotate(const Point &rRef, Degree100 nAngle, double sn, double cs) override
Definition: svdoedge.cxx:2318
static bool ImpFindConnector(const Point &rPt, const SdrPageView &rPV, SdrObjConnection &rCon, const SdrEdgeObj *pThis, OutputDevice *pOut=nullptr, SdrDragStat *pDragStat=nullptr)
Definition: svdoedge.cxx:2129
virtual const tools::Rectangle & GetSnapRect() const override
Definition: svdoattr.cxx:49
All geometrical data of an arbitrary object for use in undo/redo.
Definition: svdobj.hxx:173
bool bEdgeTrackUserDefined
Definition: svdoedge.hxx:149
void AddListener(SfxListener &rListener)
Definition: svdobj.cxx:697
SfxHintId GetId() const
constexpr TypedWhichId< SdrEdgeLineDeltaCountItem > SDRATTR_EDGELINEDELTACOUNT(SDRATTR_EDGE_FIRST+7)
virtual bool HasText() const override
Definition: svdotxat.cxx:418
void ConnectToNode(bool bTail1, SdrObject *pObj) override
Definition: svdoedge.cxx:470
virtual std::unique_ptr< sdr::contact::ViewContact > CreateObjectSpecificViewContact() override
Definition: svdoedge.cxx:150
virtual void BrkCreate(SdrDragStat &rStat) override
Definition: svdoedge.cxx:2110
bool bEdgeTrackDirty
Definition: svdoedge.hxx:148
virtual void NbcMirror(const Point &rRef1, const Point &rRef2) override
Definition: svdotxtr.cxx:232
virtual const tools::Rectangle & GetSnapRect() const
Definition: svdobj.cxx:1660
SdrObjKind
Definition: svdobjkind.hxx:24
void ImpUndirtyEdgeTrack()
Definition: svdoedge.cxx:557
bool IsVisible() const
Definition: svdobj.hxx:751
bool m_bIsEdge
Definition: svdobj.hxx:903
sal_uInt16 GetConnectorId() const
Definition: svdoedge.hxx:67
constexpr tools::Long Width() const
sal_uInt16 GetId() const
Definition: svdglue.hxx:112
Provides information about various ZObject properties.
Definition: svdobj.hxx:195
const SdrHdl * GetHdl() const
Definition: svddrag.hxx:111
B2DPolygon const & getB2DPolygon(sal_uInt32 nIndex) const
constexpr TypedWhichId< SdrEdgeKindItem > SDRATTR_EDGEKIND(SDRATTR_EDGE_FIRST+0)
basegfx::B2DPolyPolygon GetEdgeTrackPath() const
Definition: svdoedge.cxx:1723
void ImpSetTailPoint(bool bTail1, const Point &rPt)
Definition: svdoedge.cxx:534
tools::Long nAngle1
Definition: svdoedge.hxx:88
int n2
Point aObj2Line3
Definition: svdoedge.hxx:84
SdrPage * getSdrPageFromSdrObject() const
Definition: svdobj.cxx:269
virtual SdrGluePoint GetVertexGluePoint(sal_uInt16 nNum) const
Definition: svdobj.cxx:2273
virtual PointerStyle GetCreatePointer() const override
get the cursor/pointer that signals creating this object
Definition: svdoedge.cxx:2124
virtual void SaveGeoData(SdrObjGeoData &rGeo) const override
Definition: svdotext.cxx:1417
int nCount
OUString SvxResId(TranslateId aId)
Definition: dialmgr.cxx:24
SdrMetricItem makeSdrEdgeLine2DeltaItem(tools::Long nVal)
Definition: sxelditm.hxx:43
const SfxItemSet & GetObjectItemSet() const
Definition: svdobj.cxx:1967
virtual void NbcSetPoint(const Point &rPnt, sal_uInt32 i) override
Definition: svdoedge.cxx:2457
SdrObject * SdrObjectPrimitiveHit(const SdrObject &rObject, const Point &rPnt, sal_uInt16 nTol, const SdrPageView &rSdrPageView, const SdrLayerIDSet *pVisiLayer, bool bTextOnly, drawinglayer::primitive2d::Primitive2DContainer *pHitContainer)
tools::Rectangle maRect
Definition: svdotext.hxx:170
auto & GetGlueOptions()
Definition: svddrag.hxx:172
tools::Long BigMulDiv(tools::Long nVal, tools::Long nMul, tools::Long nDiv)
Definition: svdtrans.cxx:552
void setGluePointIndex(bool bTail, sal_Int32 nId=-1)
this method is used by the api to set a gluepoint for a connection nId == -1 : The best default point...
Definition: svdoedge.cxx:2560
void ImpSetAttrToEdgeInfo()
Definition: svdoedge.cxx:212
virtual void NbcShear(const Point &rRef, Degree100 nAngle, double tn, bool bVShear) override
Definition: svdotxtr.cxx:210
SdrObjConnection aCon1
Definition: svdoedge.hxx:115
OutputDevice * GetFirstOutputDevice() const
Definition: svdpntv.cxx:91
bool TakeGluePoint(SdrGluePoint &rGP) const
Definition: svdoedge.cxx:58
Utility class SdrEdgeObjGeoData.
Definition: svdoedge.hxx:112
tools::Rectangle maSnapRect
Definition: svdoattr.hxx:41
SdrObject * GetObject() const
Definition: svdoedge.hxx:68
#define XPOLY_APPEND
Definition: xpoly.hxx:38
SdrEdgeLineCode
Definition: svdoedge.hxx:72
tools::Long AdjustBottom(tools::Long nVertMoveDelta)
B2IRange fround(const B2DRange &rRange)
bool ImpIsHorzLine(SdrEdgeLineCode eLineCode, const XPolygon &rXP) const
Definition: svdoedge.cxx:111
virtual void SetBoundAndSnapRectsDirty(bool bNotMyself=false, bool bRecursive=true)
Definition: svdobj.cxx:510
SdrEdgeObj(SdrModel &rSdrModel)
Definition: svdoedge.cxx:156
sal_uInt32 GetOrdNumDirect() const
Definition: svdobj.hxx:838
SdrObject * GetConnectedNode(bool bTail1) const override
Definition: svdoedge.cxx:494
virtual SdrEdgeObj * CloneSdrObject(SdrModel &rTargetModel) const override
Definition: svdoedge.cxx:1666
virtual const tools::Rectangle & GetCurrentBoundRect() const override
Definition: svdoedge.cxx:397
constexpr bool IsEmpty() const
virtual bool BckCreate(SdrDragStat &rStat) override
Definition: svdoedge.cxx:2102
SdrEscapeDirection GetEscDir() const
Definition: svdglue.hxx:104
void SetBestVertex(bool rB)
Definition: svdoedge.hxx:61
virtual std::unique_ptr< SdrObjGeoData > NewGeoData() const override
A derived class must override these 3 methods if it has own geometric data that must be saved for Und...
Definition: svdoedge.cxx:2481
SdrObjList * GetObjList() const
Return current List.
Definition: svdpagv.hxx:169
virtual void NbcMirror(const Point &rRef1, const Point &rRef2) override
Definition: svdoedge.cxx:2349
constexpr TypedWhichId< SdrMetricItem > SDRATTR_EDGELINE3DELTA(SDRATTR_EDGE_FIRST+10)
const SfxPoolItem & GetObjectItem(const sal_uInt16 nWhich) const
Definition: svdobj.cxx:2002
constexpr TypedWhichId< SdrEdgeNode2HorzDistItem > SDRATTR_EDGENODE2HORZDIST(SDRATTR_EDGE_FIRST+3)
const SdrLayerIDSet & GetVisibleLayers() const
Definition: svdpagv.hxx:210
void SetNoSnap(bool bOn=true)
Definition: svddrag.hxx:131
virtual void handlePageChange(SdrPage *pOldPage, SdrPage *pNewPage) override
Definition: svdotext.cxx:490
void SetEndDragChangesGeoAndAttributes(bool bOn)
Definition: svddrag.hxx:145
virtual const SdrGluePointList * GetGluePointList() const
Definition: svdobj.cxx:2310
bool mbSuppressed
Definition: svdoedge.hxx:164
virtual void TakeUnrotatedSnapRect(tools::Rectangle &rRect) const override
Definition: svdoedge.cxx:422
virtual SdrGluePointList * ForceGluePointList() override
Definition: svdoedge.cxx:465
SdrEdgeLineCode GetLineCode() const
Definition: svdhdl.hxx:397
int i
void MirrorXPoly(XPolygon &rPoly, const Point &rRef1, const Point &rRef2)
Definition: svdtrans.cxx:139
#define SDRGLUEPOINT_NOTFOUND
Definition: svdglue.hxx:181
virtual Point GetPoint(sal_uInt32 i) const override
Definition: svdoedge.cxx:2447
constexpr TypedWhichId< SdrEdgeNode1VertDistItem > SDRATTR_EDGENODE1VERTDIST(SDRATTR_EDGE_FIRST+2)
virtual SdrObjKind GetObjIdentifier() const override
Definition: svdoedge.cxx:392
Point GetTailPoint(bool bTail) const
Definition: svdoedge.cxx:2524
OUString sName
SdrObject * GetCreateObj() const
Definition: svdcrtv.hxx:120
basegfx::B2DPolygon ImplAddConnectorOverlay(const SdrDragMethod &rDragMethod, bool bTail1, bool bTail2, bool bDetail) const
Definition: svdoedge.cxx:1994
sal_uInt16 nObj1Lines
Definition: svdoedge.hxx:90
virtual void handlePageChange(SdrPage *pOldPage, SdrPage *pNewPage) override
Definition: svdoedge.cxx:198
virtual void TRSetBaseGeometry(const basegfx::B2DHomMatrix &rMatrix, const basegfx::B2DPolyPolygon &rPolyPolygon)
Definition: svdobj.cxx:3024
void DisconnectFromNode(bool bTail1) override
Definition: svdoedge.cxx:485
SdrPathObjUniquePtr ImpConvertMakeObj(const basegfx::B2DPolyPolygon &rPolyPolygon, bool bClosed, bool bBezier) const
Definition: svdotxtr.cxx:423
std::optional< XPolygon > pEdgeTrack
Definition: svdoedge.hxx:117
virtual void RestoreGeoData(const SdrObjGeoData &rGeo) override
Definition: svdoedge.cxx:2498
void ImpDirtyEdgeTrack()
Definition: svdoedge.cxx:551
sal_Int32 GetPointCount() const
Definition: svddrag.hxx:101
void MirrorPoint(Point &rPnt, const Point &rRef1, const Point &rRef2)
Definition: svdtrans.cxx:105
OUString ImpGetDescriptionStr(TranslateId pStrCacheID) const
Definition: svdobj.cxx:1090
void ActionChanged() const
Definition: svdobj.cxx:263
SdrView * GetView() const
Definition: svddrag.hxx:96
virtual ~SdrEdgeObjGeoData() override
Definition: svdoedge.cxx:2477
basegfx::B2DPolygon getB2DPolygon() const
Definition: _xpoly.cxx:815
constexpr tools::Long Right() const
virtual bool hasSpecialDrag() const override
The standard transformations (Move,Resize,Rotate,Mirror,Shear) are taken over by the View (TakeXorPol...
Definition: svdoedge.cxx:1845
SdrEdgeKind
Definition: sxekitm.hxx:26
SdrModel & getSdrModelFromSdrObject() const
Definition: svdobj.cxx:279
SdrObjUserCall * m_pUserCall
Definition: svdobj.hxx:884
virtual void NbcResize(const Point &rRefPnt, const Fraction &aXFact, const Fraction &aYFact) override
Definition: svdoedge.cxx:2301
void SetPercent(bool bOn)
Definition: svdglue.hxx:124
constexpr tools::Long Top() const
SdrObjConnection aCon2
Definition: svdoedge.hxx:142
virtual css::uno::Reference< css::embed::XEmbeddedObject > GetObject() override
Point aObj1Line3
Definition: svdoedge.hxx:82
void ImpSetLineOffset(SdrEdgeLineCode eLineCode, const XPolygon &rXP, tools::Long nVal)
Definition: svdoedge.cxx:123
void SetConnectorId(sal_uInt16 nId)
Definition: svdoedge.hxx:63
virtual basegfx::B2DPolyPolygon TakeXorPoly() const override
The Xor-Polygon is required by the View to drag the object.
Definition: svdoedge.cxx:1686
virtual void NbcMove(const Size &rSiz) override
The methods Move, Resize, Rotate, Mirror, Shear, SetSnapRect and SetLogicRect call the corresponding ...
Definition: svdotxtr.cxx:94
SdrMetricItem makeSdrEdgeLine1DeltaItem(tools::Long nVal)
Definition: sxelditm.hxx:38
void Reformat()
updates edges that are connected to the edges of this object as if the connected objects send a repai...
Definition: svdoedge.cxx:1651
bool IsAutoVertex() const
Definition: svdoedge.hxx:66
void SetEndDragChangesAttributes(bool bOn)
Definition: svddrag.hxx:143
virtual void NbcRotate(const Point &rRef, Degree100 nAngle, double sn, double cs) override
Definition: svdotxtr.cxx:186
void SetTailPoint(bool bTail, const Point &rPt)
Definition: svdoedge.cxx:2549
virtual bool beginSpecialDrag(SdrDragStat &rDrag) const override
Definition: svdoedge.cxx:1862
MapUnit GetMapUnit() const
virtual sal_uInt32 GetSnapPointCount() const override
snap to special points of an Object (polygon points, center of circle)
Definition: svdoedge.cxx:2424
bool CheckNodeConnection(bool bTail1) const
Definition: svdoedge.cxx:507
Abstract DrawObject.
Definition: svdobj.hxx:259
void SetBestConnection(bool rB)
Definition: svdoedge.hxx:60
virtual void NbcMove(const Size &aSize) override
The methods Move, Resize, Rotate, Mirror, Shear, SetSnapRect and SetLogicRect call the corresponding ...
Definition: svdoedge.cxx:2295
SdrObjConnection & GetConnection(bool bTail1)
Definition: svdoedge.hxx:199
virtual void TRSetBaseGeometry(const basegfx::B2DHomMatrix &rMatrix, const basegfx::B2DPolyPolygon &rPolyPolygon) override
Definition: svdoedge.cxx:2622
Point & ImpGetLineOffsetPoint(SdrEdgeLineCode eLineCode)
Definition: svdoedge.cxx:87
Point GetAbsolutePos(const SdrObject &rObj) const
Definition: svdglue.cxx:48
#define Y
virtual sal_uInt32 GetHdlCount() const override
Via GetHdlCount the number of Handles can be retrieved.
Definition: svdoedge.cxx:1735
virtual void NbcShear(const Point &rRef, Degree100 nAngle, double tn, bool bVShear) override
Definition: svdoedge.cxx:2380
virtual SdrObjectUniquePtr getFullDragClone() const override
Definition: svdoedge.cxx:1850
constexpr Point Center() const
sal_uInt16 FindGluePoint(sal_uInt16 nId) const
Definition: svdglue.cxx:336
constexpr Point TopLeft() const
virtual SdrLayerID GetLayer() const
Definition: svdobj.cxx:668
tools::Long AdjustTop(tools::Long nVertMoveDelta)
constexpr tools::Long Bottom() const
SAL_WARN_UNUSED_RESULT Point PixelToLogic(const Point &rDevicePt) const
SdrEdgeInfoRec aEdgeInfo
Definition: svdoedge.hxx:120
SdrMetricItem makeSdrEdgeLine3DeltaItem(tools::Long nVal)
Definition: sxelditm.hxx:48
virtual const tools::Rectangle & GetSnapRect() const override
Definition: svdoedge.cxx:407
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
friend class ImpEdgeHdl
Definition: svdoedge.hxx:136
bool IsSet(SdrLayerID a) const
Definition: svdsob.hxx:69
void Insert(sal_uInt16 nPos, const Point &rPt, PolyFlags eFlags)
Definition: _xpoly.cxx:352
sal_uInt16 nConId
Definition: svdoedge.hxx:47
virtual SdrGluePoint GetCornerGluePoint(sal_uInt16 nNum) const
Definition: svdobj.cxx:2294
void RotatePoint(Point &rPnt, const Point &rRef, double sn, double cs)
Definition: svdtrans.hxx:101
sal_uInt32 count() const
void Remove(sal_uInt16 nPos, sal_uInt16 nCount)
Definition: _xpoly.cxx:376
XPolygon ImpCalcEdgeTrack(const XPolygon &rTrack0, SdrObjConnection &rCon1, SdrObjConnection &rCon2, SdrEdgeInfoRec *pInfo) const
Definition: svdoedge.cxx:717
const SfxPoolItem & Get(sal_uInt16 nWhich, bool bSrchInParent=true) const
constexpr tools::Long Height() const
virtual void Notify(SfxBroadcaster &rBC, const SfxHint &rHint) override
Detects when a stylesheet is changed.
Definition: svdoattr.cxx:61
virtual void TakeObjInfo(SdrObjTransformInfoRec &rInfo) const override
Definition: svdoedge.cxx:375
virtual sal_uInt32 GetPointCount() const override
Definition: svdoedge.cxx:2442
virtual void AddToHdlList(SdrHdlList &rHdlList) const override
Definition: svdoedge.cxx:1764
virtual void SaveGeoData(SdrObjGeoData &rGeo) const override
Definition: svdoedge.cxx:2486
virtual bool applySpecialDrag(SdrDragStat &rDrag) override
Definition: svdoedge.cxx:1877
void SetFlags(sal_uInt16 nPos, PolyFlags eFlags)
set the flags for the point at the given position
Definition: _xpoly.cxx:459
OUString aName
virtual basegfx::B2DHomMatrix getCurrentTransformation() const
Definition: svddrgmt.cxx:657
virtual OUString getSpecialDragComment(const SdrDragStat &rDrag) const override
Definition: svdoedge.cxx:1979
bool IsBestConnection() const
Definition: svdoedge.hxx:65
void RemoveListener(SfxListener &rListener)
Definition: svdobj.cxx:708
SdrObjConnection aCon1
Definition: svdoedge.hxx:141
virtual void RestoreGeoData(const SdrObjGeoData &rGeo) override
Definition: svdotext.cxx:1425
std::unique_ptr< SdrObject, SdrObjectFreeOp > SdrObjectUniquePtr
Definition: svdobj.hxx:97
constexpr TypedWhichId< SdrMetricItem > SDRATTR_EDGELINE1DELTA(SDRATTR_EDGE_FIRST+8)
const Point & GetPos() const
Definition: svdglue.hxx:96
const Point & GetNow() const
Definition: svddrag.hxx:105
PointerStyle
bool mbBoundRectCalculationRunning
Definition: svdoedge.hxx:160
void SetConnectMarker(const SdrObjConnection &rCon)
Definition: svdcrtv.cxx:270
constexpr Point BottomRight() const
void SetEdgeTrackDirty()
Definition: svdoedge.hxx:212
sal_uInt16 ImpGetPolyIdx(SdrEdgeLineCode eLineCode, const XPolygon &rXP) const
Definition: svdoedge.cxx:99
sal_uInt16 GetMarkHdlSizePixel() const
Definition: svdmrkv.cxx:2192
void Move(tools::Long nHorzMoveDelta, tools::Long nVertMoveDelta)
constexpr TypedWhichId< SdrMetricItem > SDRATTR_EDGELINE2DELTA(SDRATTR_EDGE_FIRST+9)
void SetEdgeTrackPath(const basegfx::B2DPolyPolygon &rPoly)
Definition: svdoedge.cxx:1703
virtual void Notify(SfxBroadcaster &rBC, const SfxHint &rHint) override
Detects when a stylesheet is changed.
Definition: svdoedge.cxx:1603
void RotateXPoly(XPolygon &rPoly, const Point &rRef, double sn, double cs)
Definition: svdtrans.cxx:89
sal_Int32 getGluePointIndex(bool bTail)
this method is used by the api to return a gluepoint id for a connection.
Definition: svdoedge.cxx:2592
tools::Long AdjustLeft(tools::Long nHorzMoveDelta)
static SdrEscapeDirection ImpCalcEscAngle(SdrObject const *pObj, const Point &aPt2)
Definition: svdoedge.cxx:620
double getX() const
virtual void NbcSetAnchorPos(const Point &rPnt)
Definition: svdobj.cxx:1633
bool LineGeometryUsageIsNecessary() const
Definition: svdobj.cxx:1058
bool m_bSnapRectDirty
Definition: svdobj.hxx:889
A SdrPage contains exactly one SdrObjList and a description of the physical page dimensions (size / m...
Definition: svdpage.hxx:373
virtual SdrGluePoint GetVertexGluePoint(sal_uInt16 nNum) const override
Definition: svdoedge.cxx:427
virtual bool TRGetBaseGeometry(basegfx::B2DHomMatrix &rMatrix, basegfx::B2DPolyPolygon &rPolyPolygon) const
Definition: svdobj.cxx:2997
SdrCreateCmd
Definition: svdtypes.hxx:26
sal_uInt16 GetPointCount() const
Definition: _xpoly.cxx:346
virtual void SetChanged()
Definition: svdobj.cxx:1025
void MoveXPoly(XPolygon &rPoly, const Size &S)
Definition: svdtrans.cxx:33
Point aObj1Line2
Definition: svdoedge.hxx:81
const OUString & GetName() const
Definition: svdobj.cxx:792
void SendUserCall(SdrUserCallType eUserCall, const tools::Rectangle &rBoundRect) const
Definition: svdobj.cxx:2762
void SetPos(const Point &rNewPos)
Definition: svdglue.hxx:100
bool GetSuppressDefaultConnect() const
Definition: svdoedge.hxx:169
void AddHdl(std::unique_ptr< SdrHdl > pHdl)
Definition: svdhdl.cxx:2290
virtual bool EndCreate(SdrDragStat &rStat, SdrCreateCmd eCmd) override
Definition: svdoedge.cxx:2087
void HideConnectMarker()
Definition: svdcrtv.cxx:293
Utility class SdrEdgeObj.
Definition: svdoedge.hxx:129
SdrObject * pObj
Definition: svdoedge.hxx:46
virtual void RecalcSnapRect() override
Snap is not done on the BoundRect but if possible on logic coordinates (i.e.
Definition: svdoedge.cxx:417
bool m_bDetectedRangeSegmentation false
virtual Point GetSnapPoint(sal_uInt32 i) const override
Definition: svdoedge.cxx:2429