|
|
|
@ -11,11 +11,9 @@ const double Node::m_oneAndHalfPi = Node::m_pi * 1.5;
|
|
|
|
|
const double Node::m_twoPi = Node::m_pi * 2.0;
|
|
|
|
|
|
|
|
|
|
const QColor Node::m_gold(255,215,0);
|
|
|
|
|
const QColor Node::m_orange(255,102,0);
|
|
|
|
|
|
|
|
|
|
Node::Node(GraphWidget *parent) :
|
|
|
|
|
m_graph(parent),
|
|
|
|
|
m_isActive(false),
|
|
|
|
|
m_number(-1),
|
|
|
|
|
m_hasBorder(false),
|
|
|
|
|
m_numberIsSpecial(false),
|
|
|
|
@ -35,11 +33,6 @@ Node::~Node()
|
|
|
|
|
foreach (EdgeElement element, m_edgeList) delete element.edge;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Node::moveNode(QGraphicsSceneMouseEvent *event)
|
|
|
|
|
{
|
|
|
|
|
QGraphicsItem::mouseMoveEvent(event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Node::addEdge(Edge *edge, bool startsFromThisNode)
|
|
|
|
|
{
|
|
|
|
|
m_edgeList.push_back(EdgeElement(edge, startsFromThisNode));
|
|
|
|
@ -75,26 +68,83 @@ void Node::removeEdgeFromList(Edge *edge)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//void Node::adjustEdges()
|
|
|
|
|
//{
|
|
|
|
|
// foreach (EdgeElement element, m_edgeList) element.edge->adjust();
|
|
|
|
|
//}
|
|
|
|
|
// edges from this Node. Exclude secondaries if needed (calc subtree)
|
|
|
|
|
QList<Edge *> Node::edgesFrom(const bool &excludeSecondaries) const
|
|
|
|
|
{
|
|
|
|
|
QList<Edge *> list;
|
|
|
|
|
|
|
|
|
|
void Node::setBorder(const bool &hasBorder)
|
|
|
|
|
foreach(EdgeElement element, m_edgeList)
|
|
|
|
|
if (element.startsFromThisNode &&
|
|
|
|
|
(!element.edge->secondary() || !excludeSecondaries))
|
|
|
|
|
list.push_back(element.edge);
|
|
|
|
|
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// edges to this node (max 1 primary and any number of secondaries)
|
|
|
|
|
QList<Edge *> Node::edgesToThis(const bool &excludeSecondaries) const
|
|
|
|
|
{
|
|
|
|
|
m_hasBorder = hasBorder;
|
|
|
|
|
update();
|
|
|
|
|
QList<Edge *> list;
|
|
|
|
|
|
|
|
|
|
foreach(EdgeElement element, m_edgeList)
|
|
|
|
|
if (!element.startsFromThisNode &&
|
|
|
|
|
(!element.edge->secondary() || !excludeSecondaries))
|
|
|
|
|
list.push_back(element.edge);
|
|
|
|
|
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @note Used to have some disorder here, how does an active node looks like?
|
|
|
|
|
* Let is have border but the color shall not change, shadow is messy too.
|
|
|
|
|
*/
|
|
|
|
|
// the edge from this Node to the parameter Node
|
|
|
|
|
Edge * Node::edgeTo(const Node *node) const
|
|
|
|
|
{
|
|
|
|
|
foreach(EdgeElement element, m_edgeList)
|
|
|
|
|
if ((element.edge->sourceNode() == node ||
|
|
|
|
|
element.edge->destNode() == node))
|
|
|
|
|
return element.edge;
|
|
|
|
|
|
|
|
|
|
void Node::setActive(const bool &active)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<Node *> Node::subtree() const
|
|
|
|
|
{
|
|
|
|
|
m_isActive = active;
|
|
|
|
|
setBorder(active);
|
|
|
|
|
update();
|
|
|
|
|
/** @note QList crashes if modified while traversal,
|
|
|
|
|
* QMutableListIterator lacks push_back, using good old std::list
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
std::list<Node *> list;
|
|
|
|
|
list.push_back(const_cast<Node *>(this));
|
|
|
|
|
|
|
|
|
|
// inorder: push_back the list of children Nodes of iterator
|
|
|
|
|
for(std::list<Node *>::const_iterator it = list.begin();
|
|
|
|
|
it != list.end();
|
|
|
|
|
it++)
|
|
|
|
|
{
|
|
|
|
|
QList<Edge *> edges = (*it)->edgesFrom();
|
|
|
|
|
foreach(Edge *edge, edges)
|
|
|
|
|
if (!edge->secondary())
|
|
|
|
|
list.push_back( edge->destNode() != this ?
|
|
|
|
|
edge->destNode():
|
|
|
|
|
edge->sourceNode());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return QList<Node *>::fromStdList(list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// return thue if this and the parameter Node is connected with an edge
|
|
|
|
|
bool Node::isConnected(const Node *node) const
|
|
|
|
|
{
|
|
|
|
|
foreach (EdgeElement element, m_edgeList)
|
|
|
|
|
if (element.edge->sourceNode() == node ||
|
|
|
|
|
element.edge->destNode() == node)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Node::setBorder(const bool &hasBorder)
|
|
|
|
|
{
|
|
|
|
|
m_hasBorder = hasBorder;
|
|
|
|
|
update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Node::setEditable(const bool &editable)
|
|
|
|
@ -106,6 +156,8 @@ void Node::setEditable(const bool &editable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setTextInteractionFlags(Qt::TextEditable);
|
|
|
|
|
|
|
|
|
|
// set cursor to the end
|
|
|
|
|
QTextCursor c = textCursor();
|
|
|
|
|
c.setPosition(c.document()->toPlainText().length());
|
|
|
|
|
setTextCursor(c);
|
|
|
|
@ -125,17 +177,20 @@ void Node::setTextColor(const QColor &color)
|
|
|
|
|
|
|
|
|
|
void Node::setScale(const qreal &factor,const QRectF &sceneRect)
|
|
|
|
|
{
|
|
|
|
|
// limit scale to a reasonable size
|
|
|
|
|
if (factor * scale() < 0.4 ||
|
|
|
|
|
factor * scale() > 4 )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// cannot scale out the Node from the scene
|
|
|
|
|
if (!sceneRect.contains(pos() +
|
|
|
|
|
boundingRect().bottomRight() * scale() * factor))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
prepareGeometryChange();
|
|
|
|
|
|
|
|
|
|
QGraphicsTextItem::setScale(factor * scale());
|
|
|
|
|
|
|
|
|
|
// scale edges to this Node too
|
|
|
|
|
foreach(EdgeElement element, m_edgeList)
|
|
|
|
|
{
|
|
|
|
|
if (!element.startsFromThisNode)
|
|
|
|
@ -154,15 +209,11 @@ void Node::showNumber(const int &number,
|
|
|
|
|
update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Node::insertPicture(const QString &picture, const int &pos)
|
|
|
|
|
void Node::insertPicture(const QString &picture)
|
|
|
|
|
{
|
|
|
|
|
QTextCursor c = textCursor();
|
|
|
|
|
|
|
|
|
|
if (pos)
|
|
|
|
|
{
|
|
|
|
|
c.setPosition(pos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// strange, picture looks bad when node is scaled up
|
|
|
|
|
c.insertHtml(QString("<img src=").append(picture).
|
|
|
|
|
append(" width=15 height=15></img>"));
|
|
|
|
|
|
|
|
|
@ -170,18 +221,56 @@ void Node::insertPicture(const QString &picture, const int &pos)
|
|
|
|
|
foreach (EdgeElement element, m_edgeList) element.edge->adjust();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double Node::calculateBiggestAngle()
|
|
|
|
|
QPointF Node::intersection(const QLineF &line, const bool &reverse) const
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/// @note What a shame, the following does not work,
|
|
|
|
|
/// doing it with brute (unaccurate) force
|
|
|
|
|
|
|
|
|
|
// QPainterPath nodeShape(shape());
|
|
|
|
|
// nodeShape.translate(pos());
|
|
|
|
|
|
|
|
|
|
// QPainterPath l;
|
|
|
|
|
// l.moveTo(line.p1());
|
|
|
|
|
// l.lineTo(line.p2());
|
|
|
|
|
|
|
|
|
|
// return nodeShape.intersected(l);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QPainterPath path;
|
|
|
|
|
path.addRoundedRect(sceneBoundingRect(), 28.0, 28.0);
|
|
|
|
|
|
|
|
|
|
if (reverse)
|
|
|
|
|
{
|
|
|
|
|
for (qreal t = 1; t!=0; t-=0.01)
|
|
|
|
|
if (!path.contains(line.pointAt(t)))
|
|
|
|
|
return line.pointAt(t);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (qreal t = 0; t!=1; t+=0.01)
|
|
|
|
|
if (!path.contains(line.pointAt(t)))
|
|
|
|
|
return line.pointAt(t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return QPointF(0,0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double Node::calculateBiggestAngle() const
|
|
|
|
|
{
|
|
|
|
|
// in no edge, return with 12 o'clock
|
|
|
|
|
if (m_edgeList.empty())
|
|
|
|
|
return Node::m_oneAndHalfPi;
|
|
|
|
|
|
|
|
|
|
// if there is only one edge, return with it's extension
|
|
|
|
|
if (m_edgeList.size()==1)
|
|
|
|
|
return m_edgeList.first().startsFromThisNode ?
|
|
|
|
|
Node::m_pi - m_edgeList.first().edge->angle() :
|
|
|
|
|
Node::m_twoPi - m_edgeList.first().edge->angle();
|
|
|
|
|
|
|
|
|
|
// put angles of every edges from this node to a list
|
|
|
|
|
QList<double> tmp;
|
|
|
|
|
for(QList<EdgeElement>::iterator it = m_edgeList.begin();
|
|
|
|
|
for(QList<EdgeElement>::const_iterator it = m_edgeList.begin();
|
|
|
|
|
it != m_edgeList.end(); it++)
|
|
|
|
|
{
|
|
|
|
|
tmp.push_back(it->startsFromThisNode ?
|
|
|
|
@ -190,6 +279,7 @@ double Node::calculateBiggestAngle()
|
|
|
|
|
}
|
|
|
|
|
qSort(tmp.begin(), tmp.end());
|
|
|
|
|
|
|
|
|
|
// find the biggest diffrence, store prev angle
|
|
|
|
|
double prev(tmp.first());
|
|
|
|
|
double max_prev(tmp.last());
|
|
|
|
|
double max(Node::m_twoPi - tmp.last() + tmp.first());
|
|
|
|
@ -204,6 +294,7 @@ double Node::calculateBiggestAngle()
|
|
|
|
|
prev = *it;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// return with prev angle + max diff / 2
|
|
|
|
|
return Node::m_twoPi - doubleModulo(max_prev + max / 2, Node::m_twoPi);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -258,117 +349,6 @@ void Node::keyPressEvent(QKeyEvent *event)
|
|
|
|
|
///@note leaving editing mode is done with esc, handled by graphwidget
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Node::isConnected(const Node *node) const
|
|
|
|
|
{
|
|
|
|
|
foreach (EdgeElement element, m_edgeList)
|
|
|
|
|
if (element.edge->sourceNode() == node ||
|
|
|
|
|
element.edge->destNode() == node)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPointF Node::intersection(const QLineF &line, const bool &reverse) const
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/// @note What a shame, the following does not work,
|
|
|
|
|
/// doing it with brute (unaccurate) force
|
|
|
|
|
|
|
|
|
|
// QPainterPath shape;
|
|
|
|
|
// shape.addRoundedRect(sceneBoundingRect(), 20.0, 15.0);
|
|
|
|
|
|
|
|
|
|
// QPainterPath l;
|
|
|
|
|
// l.moveTo(sceneBoundingRect().center());
|
|
|
|
|
// l.lineTo(line.p2());
|
|
|
|
|
|
|
|
|
|
// return shape.intersected(l).pointAtPercent(0.5);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QPainterPath path;
|
|
|
|
|
path.addRoundedRect(sceneBoundingRect(), 28.0, 28.0);
|
|
|
|
|
|
|
|
|
|
if (reverse)
|
|
|
|
|
{
|
|
|
|
|
for (qreal t = 1; t!=0; t-=0.01)
|
|
|
|
|
if (!path.contains(line.pointAt(t)))
|
|
|
|
|
return line.pointAt(t);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (qreal t = 0; t!=1; t+=0.01)
|
|
|
|
|
if (!path.contains(line.pointAt(t)))
|
|
|
|
|
return line.pointAt(t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return QPointF(0,0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<Edge *> Node::edgesFrom(const bool &excludeSecondaries) const
|
|
|
|
|
{
|
|
|
|
|
QList<Edge *> list;
|
|
|
|
|
|
|
|
|
|
foreach(EdgeElement element, m_edgeList)
|
|
|
|
|
if (element.startsFromThisNode &&
|
|
|
|
|
(!element.edge->secondary() || !excludeSecondaries))
|
|
|
|
|
list.push_back(element.edge);
|
|
|
|
|
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<Edge *> Node::edgesToThis(const bool &excludeSecondaries) const
|
|
|
|
|
{
|
|
|
|
|
QList<Edge *> list;
|
|
|
|
|
|
|
|
|
|
foreach(EdgeElement element, m_edgeList)
|
|
|
|
|
if (!element.startsFromThisNode &&
|
|
|
|
|
(!element.edge->secondary() || !excludeSecondaries))
|
|
|
|
|
list.push_back(element.edge);
|
|
|
|
|
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Edge * Node::edgeTo(const Node *node) const
|
|
|
|
|
{
|
|
|
|
|
foreach(EdgeElement element, m_edgeList)
|
|
|
|
|
if ((element.edge->sourceNode() == node ||
|
|
|
|
|
element.edge->destNode() == node))
|
|
|
|
|
return element.edge;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QList<Node *> Node::subtree() const
|
|
|
|
|
{
|
|
|
|
|
/** @note QList crashes if modified while traversal,
|
|
|
|
|
* QMutableListIterator lacks push_back
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
std::list<Node *> list;
|
|
|
|
|
list.push_back(const_cast<Node *>(this));
|
|
|
|
|
|
|
|
|
|
// inorder
|
|
|
|
|
for(std::list<Node *>::const_iterator it = list.begin();
|
|
|
|
|
it != list.end();
|
|
|
|
|
it++)
|
|
|
|
|
{
|
|
|
|
|
QList<Edge *> edges = (*it)->edgesFrom();
|
|
|
|
|
if (!edges.empty())
|
|
|
|
|
{
|
|
|
|
|
foreach(Edge *edge, edges)
|
|
|
|
|
{
|
|
|
|
|
if (!edge->secondary())
|
|
|
|
|
{
|
|
|
|
|
list.push_back( edge->destNode() != this ?
|
|
|
|
|
edge->destNode():
|
|
|
|
|
edge->sourceNode());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return QList<Node *>::fromStdList(list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Node::paint(QPainter *painter,
|
|
|
|
|
const QStyleOptionGraphicsItem *option,
|
|
|
|
|
QWidget *w)
|
|
|
|
@ -379,7 +359,6 @@ void Node::paint(QPainter *painter,
|
|
|
|
|
{
|
|
|
|
|
painter->setPen(Qt::transparent);
|
|
|
|
|
painter->setBrush(m_numberIsSpecial ? Qt::green : Qt::yellow);
|
|
|
|
|
|
|
|
|
|
painter->drawRoundedRect(boundingRect(), 20.0, 15.0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
@ -415,29 +394,29 @@ QVariant Node::itemChange(GraphicsItemChange change, const QVariant &value)
|
|
|
|
|
switch (change) {
|
|
|
|
|
|
|
|
|
|
case ItemPositionChange:
|
|
|
|
|
{
|
|
|
|
|
// Node is about to move, check borders
|
|
|
|
|
|
|
|
|
|
if (change == ItemPositionChange && scene())
|
|
|
|
|
QPointF newPos = value.toPointF();
|
|
|
|
|
|
|
|
|
|
// the fence is reduced with the size of the node
|
|
|
|
|
QRectF rect (scene()->sceneRect().topLeft(),
|
|
|
|
|
scene()->sceneRect().bottomRight() -
|
|
|
|
|
boundingRect().bottomRight() * scale() );
|
|
|
|
|
|
|
|
|
|
if (!rect.contains(newPos))
|
|
|
|
|
{
|
|
|
|
|
// value is the new position.
|
|
|
|
|
QPointF newPos = value.toPointF();
|
|
|
|
|
|
|
|
|
|
// the fence is reduced with the size of the node
|
|
|
|
|
QRectF rect (scene()->sceneRect().topLeft(),
|
|
|
|
|
scene()->sceneRect().bottomRight() -
|
|
|
|
|
boundingRect().bottomRight() * scale() );
|
|
|
|
|
|
|
|
|
|
if (!rect.contains(newPos))
|
|
|
|
|
{
|
|
|
|
|
// Keep the item inside the scene rect.
|
|
|
|
|
newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left())));
|
|
|
|
|
newPos.setY(qMin(rect.bottom(), qMax(newPos.y(), rect.top())));
|
|
|
|
|
return newPos;
|
|
|
|
|
}
|
|
|
|
|
// Keep the item inside the scene rect.
|
|
|
|
|
newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left())));
|
|
|
|
|
newPos.setY(qMin(rect.bottom(), qMax(newPos.y(), rect.top())));
|
|
|
|
|
return newPos;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case ItemPositionHasChanged:
|
|
|
|
|
|
|
|
|
|
// Notify parent, adjust edges that a move has happended.
|
|
|
|
|
m_graph->contentChanged();
|
|
|
|
|
foreach (EdgeElement element, m_edgeList) element.edge->adjust();
|
|
|
|
|
break;
|
|
|
|
@ -467,6 +446,7 @@ void Node::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|
|
|
|
QGraphicsItem::mouseReleaseEvent(event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// notify parent so subtree can be moved too if necessary
|
|
|
|
|
void Node::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|
|
|
|
{
|
|
|
|
|
m_graph->nodeMoved(event);
|
|
|
|
@ -479,16 +459,16 @@ QPainterPath Node::shape () const
|
|
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// leave editing mode when user clicks on the view elsewhere for example
|
|
|
|
|
void Node::focusOutEvent(QFocusEvent *event)
|
|
|
|
|
{
|
|
|
|
|
qDebug() << __PRETTY_FUNCTION__;
|
|
|
|
|
|
|
|
|
|
Q_UNUSED(event);
|
|
|
|
|
setEditable(false);
|
|
|
|
|
m_graph->nodeLostFocus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double Node::doubleModulo(const double &devided, const double &devisor)
|
|
|
|
|
// there is no such thing as modulo operator for double :P
|
|
|
|
|
double Node::doubleModulo(const double &devided, const double &devisor) const
|
|
|
|
|
{
|
|
|
|
|
return devided - static_cast<double>(devisor * static_cast<int>(devided
|
|
|
|
|
/ devisor));
|
|
|
|
|