+#include <QDebug>
+
+#include "xlinkobj.h"
+
+#include "branchitem.h"
+#include "branchobj.h"
+#include "math.h" // atan
+#include "misc.h" // max
+
+/////////////////////////////////////////////////////////////////
+// XLinkObj
+/////////////////////////////////////////////////////////////////
+
+int XLinkObj::arrowSize = 6; // make instances
+int XLinkObj::clickBorder = 8;
+int XLinkObj::pointRadius = 10;
+int XLinkObj::d_control = 300;
+
+XLinkObj::XLinkObj(QGraphicsItem *parent, Link *l) : MapObj(parent)
+{
+ // qDebug()<< "Const XLinkObj (parent,Link)";
+ link = l;
+ init();
+}
+
+XLinkObj::~XLinkObj()
+{
+ // qDebug() << "Destr XLinkObj";
+ delete (poly);
+ delete (path);
+ delete (ctrl_p0);
+ delete (ctrl_p1);
+ delete (pointerEnd);
+ delete (pointerBegin);
+}
+
+void XLinkObj::init()
+{
+ visBranch = NULL;
+
+ stateVis = Hidden;
+
+ QPen pen = link->getPen();
+
+ path = scene()->addPath(QPainterPath(), pen, Qt::NoBrush);
+ path->setZValue(dZ_XLINK);
+
+ pointerBegin = new ArrowObj(this);
+ pointerBegin->setPen(pen);
+ pointerBegin->setUseFixedLength(true);
+ pointerBegin->setFixedLength(0);
+
+ pointerEnd = new ArrowObj(this);
+ pointerEnd->setPen(pen);
+ pointerEnd->setUseFixedLength(true);
+ pointerEnd->setFixedLength(0);
+
+ pen.setStyle(Qt::SolidLine);
+ poly = scene()->addPolygon(QPolygonF(), pen, pen.color());
+ poly->setZValue(dZ_XLINK);
+
+ // Control points for bezier path
+ // (We have at least a begin branch, consider its orientation)
+ initC0();
+ initC1();
+
+ ctrl_p0 = scene()->addEllipse(c0.x(), c0.y(), clickBorder * 2,
+ clickBorder * 2, pen, pen.color());
+ ctrl_p1 = scene()->addEllipse(c1.x(), c1.y(), clickBorder * 2,
+ clickBorder * 2, pen, pen.color());
+
+ beginOrient = endOrient = LinkableMapObj::UndefinedOrientation;
+ pen.setWidth(1);
+ pen.setStyle(Qt::DashLine);
+
+ curSelection = Unselected;
+
+ setVisibility(true);
+}
+
+QPointF XLinkObj::getAbsPos()
+{
+ switch (curSelection) {
+ case C0:
+ return c0;
+ break;
+ case C1:
+ return c1;
+ break;
+ default:
+ return QPointF();
+ break;
+ }
+}
+
+void XLinkObj::setStyleBegin(const QString &s) { pointerBegin->setStyleEnd(s); }
+
+void XLinkObj::setStyleBegin(ArrowObj::OrnamentStyle os)
+{
+ pointerBegin->setStyleEnd(os);
+}
+
+ArrowObj::OrnamentStyle XLinkObj::getStyleBegin()
+{
+ return pointerBegin->getStyleEnd();
+}
+
+void XLinkObj::setStyleEnd(const QString &s) { pointerEnd->setStyleEnd(s); }
+
+void XLinkObj::setStyleEnd(ArrowObj::OrnamentStyle os)
+{
+ pointerEnd->setStyleEnd(os);
+}
+
+ArrowObj::OrnamentStyle XLinkObj::getStyleEnd()
+{
+ return pointerEnd->getStyleEnd();
+}
+
+QPointF XLinkObj::getBeginPos() { return beginPos; }
+
+QPointF XLinkObj::getEndPos() { return endPos; }
+
+void XLinkObj::move(QPointF p)
+{
+ switch (curSelection) {
+ case C0:
+ c0 = p;
+ break;
+ case C1:
+ c1 = p;
+ break;
+ default:
+ break;
+ }
+ updateXLink();
+}
+
+void XLinkObj::setEnd(QPointF p) { endPos = p; }
+
+void XLinkObj::setSelection(CurrentSelection s)
+{
+ curSelection = s;
+ setVisibility();
+}
+
+void XLinkObj::setSelection(int cp)
+{
+ if (cp == 0)
+ setSelection(C0);
+ else if (cp == 1)
+ setSelection(C1);
+ else
+ qWarning() << "XLO::setSelection cp=" << cp;
+}
+
+void XLinkObj::updateXLink()
+{
+ QPointF a, b;
+ QPolygonF pa;
+
+ BranchObj *beginBO = NULL;
+ BranchObj *endBO = NULL;
+ BranchItem *bi = link->getBeginBranch();
+ if (bi)
+ beginBO = (BranchObj *)(bi->getLMO());
+ bi = link->getEndBranch();
+ if (bi)
+ endBO = (BranchObj *)(bi->getLMO());
+
+ if (beginBO) {
+ if (beginOrient != LinkableMapObj::UndefinedOrientation &&
+ beginOrient != beginBO->getOrientation())
+ c0.setX(-c0.x());
+ beginOrient = beginBO->getOrientation();
+ }
+ if (endBO) {
+ if (endOrient != LinkableMapObj::UndefinedOrientation &&
+ endOrient != endBO->getOrientation())
+ c1.setX(-c1.x());
+ endOrient = endBO->getOrientation();
+ }
+
+ if (visBranch) {
+ // Only one of the linked branches is visible
+ // Draw arrowhead //FIXME-3 missing shaft of arrow
+ BranchObj *bo = (BranchObj *)(visBranch->getLMO());
+ if (!bo)
+ return;
+
+ a = b = bo->getChildRefPos();
+
+ if (bo->getOrientation() == LinkableMapObj::RightOfCenter) {
+ b.setX(b.x() + 2 * arrowSize);
+ pa.clear();
+ pa << a << b << QPointF(b.x(), b.y() - arrowSize)
+ << QPointF(b.x() + arrowSize, b.y())
+ << QPointF(b.x(), b.y() + arrowSize) << b << a;
+ poly->setPolygon(pa);
+ }
+ else {
+ b.setX(b.x() - 2 * arrowSize);
+ pa.clear();
+ pa << a << b << QPointF(b.x(), b.y() - arrowSize)
+ << QPointF(b.x() - arrowSize, b.y())
+ << QPointF(b.x(), b.y() + arrowSize) << b << a;
+ poly->setPolygon(pa);
+ }
+ }
+ else {
+ // Both linked branches are visible
+
+ // If a link is just drawn in the editor,
+ // we have already a beginBranch
+ if (beginBO)
+ beginPos = beginBO->getChildRefPos();
+ if (endBO)
+ endPos = endBO->getChildRefPos();
+
+ if (beginBO && endBO) {
+ pointerBegin->move(beginPos + c0);
+ pointerBegin->setEndPoint(beginPos);
+
+ pointerEnd->move(endPos + c1);
+ pointerEnd->setEndPoint(endPos);
+ }
+ }
+
+ // Update control points for bezier
+ QPainterPath p(beginPos);
+ p.cubicTo(beginPos + c0, endPos + c1, endPos);
+
+ clickPath = p;
+ path->setPath(p);
+
+ // Go back to create closed curve,
+ // needed for intersection check:
+ clickPath.cubicTo(endPos + c1, beginPos + c0, beginPos);
+
+ QPen pen = link->getPen();
+ path->setPen(pen);
+ poly->setBrush(pen.color());
+
+ pointerBegin->setPen(pen);
+ pointerEnd->setPen(pen);
+
+ pen.setStyle(Qt::SolidLine);
+
+ ctrl_p0->setRect(beginPos.x() + c0.x() - pointRadius / 2,
+ beginPos.y() + c0.y() - pointRadius / 2, pointRadius,
+ pointRadius);
+ ctrl_p0->setPen(pen);
+ ctrl_p0->setBrush(pen.color());
+
+ ctrl_p1->setRect(endPos.x() + c1.x() - pointRadius / 2,
+ endPos.y() + c1.y() - pointRadius / 2, pointRadius,
+ pointRadius);
+ ctrl_p1->setPen(pen);
+ ctrl_p1->setBrush(pen.color());
+
+ BranchItem *bi_begin = link->getBeginBranch();
+ BranchItem *bi_end = link->getEndBranch();
+ if (bi_begin && bi_end && link->getState() == Link::activeXLink)
+ // Note: with MapObj being a GraphicsItem now, maybe better reparent the
+ // xlinkobj line->setZValue (dZ_DEPTH *
+ // max(bi_begin->depth(),bi_end->depth()) + dZ_XLINK);
+ path->setZValue(dZ_XLINK);
+ else
+ path->setZValue(dZ_XLINK);
+
+ setVisibility();
+}
+
+void XLinkObj::positionBBox() {}
+
+void XLinkObj::calcBBoxSize() {}
+
+void XLinkObj::setVisibility(bool b)
+{
+ if (stateVis == FullShowControls) {
+ ctrl_p0->show();
+ ctrl_p1->show();
+ pointerBegin->setUseFixedLength(false);
+ pointerEnd->setUseFixedLength(false);
+ }
+ else {
+ ctrl_p0->hide();
+ ctrl_p1->hide();
+ pointerBegin->setUseFixedLength(true);
+ pointerBegin->setFixedLength(0);
+ pointerEnd->setUseFixedLength(true);
+ pointerEnd->setFixedLength(0);
+ }
+
+ MapObj::setVisibility(b);
+ if (b) {
+ if (stateVis == OnlyBegin) {
+ path->hide();
+ poly->show();
+ pointerBegin->hide();
+ pointerEnd->hide();
+ }
+ else if (stateVis == OnlyEnd) {
+ path->hide();
+ poly->show();
+ pointerBegin->hide();
+ pointerEnd->hide();
+ }
+ else {
+ path->show();
+ poly->hide();
+ pointerBegin->show();
+ pointerEnd->show();
+ }
+ }
+ else {
+ poly->hide();
+ path->hide();
+ pointerBegin->hide();
+ pointerEnd->hide();
+ }
+}
+
+void XLinkObj::setVisibility()
+{
+ BranchItem *beginBI = link->getBeginBranch();
+ BranchObj *beginBO = NULL;
+ if (beginBI)
+ beginBO = (BranchObj *)(beginBI->getLMO());
+
+ BranchObj *endBO = NULL;
+ BranchItem *endBI = link->getEndBranch();
+ if (endBI)
+ endBO = (BranchObj *)(endBI->getLMO());
+ if (beginBO && endBO) {
+ if (beginBO->isVisibleObj() &&
+ endBO->isVisibleObj()) { // Both ends are visible
+ visBranch = NULL;
+ if (curSelection != Unselected)
+ stateVis = FullShowControls;
+ else
+ stateVis = Full;
+ setVisibility(true);
+ }
+ else {
+ if (!beginBO->isVisibleObj() &&
+ !endBO->isVisibleObj()) { // None of the ends is visible
+ visBranch = NULL;
+ stateVis = Hidden;
+ setVisibility(false);
+ }
+ else { // Just one end is visible, draw a symbol that shows
+ // that there is a link to a scrolled branch
+ if (beginBO->isVisibleObj()) {
+ stateVis = OnlyBegin;
+ visBranch = beginBI;
+ }
+ else {
+ visBranch = endBI;
+ stateVis = OnlyEnd;
+ }
+ setVisibility(true);
+ }
+ }
+ }
+}
+
+void XLinkObj::initC0()
+{
+ if (!link)
+ return;
+ BranchItem *beginBranch = link->getBeginBranch();
+ if (!beginBranch)
+ return;
+ BranchObj *bo = beginBranch->getBranchObj();
+ if (!bo)
+ return;
+ if (bo->getOrientation() == LinkableMapObj::RightOfCenter)
+ c0 = QPointF(d_control, 0);
+ else
+ c0 = QPointF(-d_control, 0);
+}
+
+void XLinkObj::initC1()
+{
+ if (!link)
+ return;
+ BranchItem *endBranch = link->getEndBranch();
+ if (!endBranch)
+ return;
+ BranchObj *bo = endBranch->getBranchObj();
+ if (!bo)
+ return;
+ if (bo->getOrientation() == LinkableMapObj::RightOfCenter)
+ c1 = QPointF(d_control, 0);
+ else
+ c1 = QPointF(-d_control, 0);
+}
+
+void XLinkObj::setC0(const QPointF &p) { c0 = p; }
+
+QPointF XLinkObj::getC0() { return c0; }
+
+void XLinkObj::setC1(const QPointF &p) { c1 = p; }
+
+QPointF XLinkObj::getC1() { return c1; }
+
+int XLinkObj::ctrlPointInClickBox(const QPointF &p)
+{
+ CurrentSelection oldSel = curSelection;
+ int ret = -1;
+
+ QRectF r(p.x() - clickBorder, p.y() - clickBorder, clickBorder * 2,
+ clickBorder * 2);
+
+ if (curSelection == C0 || curSelection == C1) {
+ // If Cx selected, check both ctrl points
+ curSelection = C0;
+ if (getClickPath().intersects(r))
+ ret = 0;
+ curSelection = C1;
+ if (getClickPath().intersects(r))
+ ret = 1;
+ }
+ curSelection = oldSel;
+ return ret;
+}
+
+bool XLinkObj::isInClickBox(const QPointF &p)
+{
+ // Return, if not visible at all...
+ if (stateVis == Hidden)
+ return false;
+
+ CurrentSelection oldSel = curSelection;
+ bool b = false;
+
+ QRectF r(p.x() - clickBorder, p.y() - clickBorder, clickBorder * 2,
+ clickBorder * 2);
+
+ switch (stateVis) {
+ case FullShowControls:
+ // If Cx selected, check both ctrl points
+ if (ctrlPointInClickBox(p) > -1)
+ b = true;
+
+ // Enable selecting the path, when a ctrl point is already selected
+ if (!b && curSelection != Unselected && clickPath.intersects(r))
+ b = true;
+ break;
+ case OnlyBegin:
+ case OnlyEnd:
+ // not selected, only partially visible
+ if (poly->boundingRect().contains(p))
+ b = true;
+ break;
+ default:
+ // not selected, but path is fully visible
+ curSelection = Path;
+ if (getClickPath().intersects(r))
+ b = true;
+ break;
+ }
+ curSelection = oldSel;
+ return b;
+}
+
+QPainterPath
+XLinkObj::getClickPath() // also needs mirroring if oriented left. Create method
+ // to generate the coordinates
+{
+ QPainterPath p;
+ switch (curSelection) {
+ case C0:
+ p.addEllipse(beginPos + c0, 15, 15);
+ return p;
+ break;
+ case C1:
+ p.addEllipse(endPos + c1, 15, 15);
+ return p;
+ break;
+ default:
+ return clickPath;
+ break;
+ }
+}
+
+QPainterPath XLinkObj::getSelectionPath()
+{
+ return getClickPath();
+}