#include <QDebug>
#include "xlinkobj.h"
#include "branchobj.h"
#include "branchitem.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 || 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;
}
}