Node refactor

master
Denes Matetelki 14 years ago
parent 8843d20ac2
commit 6a076d2330

@ -16,14 +16,20 @@ public:
Node(GraphWidget *graphWidget = 0); Node(GraphWidget *graphWidget = 0);
~Node(); ~Node();
void moveNode(QGraphicsSceneMouseEvent *event); // add/remove edges
void addEdge(Edge *edge, bool startsFromThisNode); void addEdge(Edge *edge, bool startsFromThisNode);
void deleteEdge(Node *otherEnd); void deleteEdge(Node *otherEnd);
void removeEdgeFromList(Edge *edge); void removeEdgeFromList(Edge *edge);
// void adjustEdges();
void setBorder(const bool &hasBorder); // graph traversal
void setActive(const bool &active = true); QList<Edge *> edgesFrom(const bool &excludeSecondaries = true) const;
QList<Edge *> edgesToThis(const bool &excludeSecondaries = true) const;
Edge * edgeTo(const Node* node) const;
QList<Node *> subtree() const;
bool isConnected(const Node *node) const;
// prop set/get
void setBorder(const bool &hasBorder = true);
void setEditable(const bool &editable = true); void setEditable(const bool &editable = true);
void setColor(const QColor &color); void setColor(const QColor &color);
QColor color() const { return m_color; } QColor color() const { return m_color; }
@ -31,20 +37,21 @@ public:
QColor textColor() const { return m_textColor; } QColor textColor() const { return m_textColor; }
void setScale(const qreal &factor, const QRectF &sceneRect); void setScale(const qreal &factor, const QRectF &sceneRect);
// show numbers in hint mode
void showNumber(const int &number, const bool& show = true, void showNumber(const int &number, const bool& show = true,
const bool &numberIsSpecial = false); const bool &numberIsSpecial = false);
void insertPicture(const QString &picture, const int &pos = 0); // insert picture to the cursor's current position
double calculateBiggestAngle(); void insertPicture(const QString &picture);
// changing visibility from prot to pub // changing visibility from prot to pub
// so GraphWidget::keyPressEvent can call it edit during editing
void keyPressEvent(QKeyEvent *event); void keyPressEvent(QKeyEvent *event);
bool isConnected(const Node *node) const;
// calculetes the intersection of line and shape of this Node
QPointF intersection(const QLineF &line, const bool &reverse = false) const; QPointF intersection(const QLineF &line, const bool &reverse = false) const;
QList<Edge *> edgesFrom(const bool &excludeSecondaries = true) const; // returns with the biggest angle between the edges
QList<Edge *> edgesToThis(const bool &excludeSecondaries = true) const; double calculateBiggestAngle() const;
Edge * edgeTo(const Node* node) const;
QList<Node *> subtree() const;
protected: protected:
@ -60,7 +67,7 @@ protected:
private: private:
double doubleModulo(const double &devided, const double &devisor); double doubleModulo(const double &devided, const double &devisor) const;
struct EdgeElement struct EdgeElement
{ {
@ -71,7 +78,6 @@ private:
QList<EdgeElement> m_edgeList; QList<EdgeElement> m_edgeList;
GraphWidget *m_graph; GraphWidget *m_graph;
bool m_isActive;
int m_number; int m_number;
bool m_hasBorder; bool m_hasBorder;
bool m_numberIsSpecial; bool m_numberIsSpecial;
@ -82,7 +88,6 @@ private:
static const double m_oneAndHalfPi; static const double m_oneAndHalfPi;
static const double m_twoPi; static const double m_twoPi;
static const QColor m_orange;
static const QColor m_gold; static const QColor m_gold;
}; };

@ -165,7 +165,7 @@ bool GraphWidget::readContentFromXmlFile(const QString &fileName)
// test the first node the active one // test the first node the active one
m_activeNode = m_nodeList.first(); m_activeNode = m_nodeList.first();
m_activeNode->setActive(); m_activeNode->setBorder();
m_activeNode->setFocus(); m_activeNode->setFocus();
this->show(); this->show();
@ -197,8 +197,10 @@ void GraphWidget::writeContentToXmlFile(const QString &fileName)
cn.setAttribute( "bg_green", QString::number(node->color().green())); cn.setAttribute( "bg_green", QString::number(node->color().green()));
cn.setAttribute( "bg_blue", QString::number(node->color().blue())); cn.setAttribute( "bg_blue", QString::number(node->color().blue()));
cn.setAttribute( "text_red", QString::number(node->textColor().red())); cn.setAttribute( "text_red", QString::number(node->textColor().red()));
cn.setAttribute( "text_green", QString::number(node->textColor().green())); cn.setAttribute( "text_green",
cn.setAttribute( "text_blue", QString::number(node->textColor().blue())); QString::number(node->textColor().green()));
cn.setAttribute( "text_blue",
QString::number(node->textColor().blue()));
nodes_root.appendChild(cn); nodes_root.appendChild(cn);
} }
@ -260,10 +262,6 @@ void GraphWidget::writeContentToPngFile(const QString &fileName)
void GraphWidget::insertNode() void GraphWidget::insertNode()
{ {
/// @note this is TERRIBLE!
// basically when insertNode() is called from mainToolBar, it needs to
// wait for a paralell nodeLostFocus() to finish...
// so I call ANOTHER one which takes the same amount of time...just kill me
nodeLostFocus(); nodeLostFocus();
if (!m_activeNode) if (!m_activeNode)
@ -595,15 +593,21 @@ void GraphWidget::keyPressEvent(QKeyEvent *event)
else if (event->key() == Qt::Key_Down) node->moveBy(0, 20); else if (event->key() == Qt::Key_Down) node->moveBy(0, 20);
else if (event->key() == Qt::Key_Left) node->moveBy(-20, 0); else if (event->key() == Qt::Key_Left) node->moveBy(-20, 0);
else if (event->key() == Qt::Key_Right) node->moveBy(20, 0); else if (event->key() == Qt::Key_Right) node->moveBy(20, 0);
contentChanged(); contentChanged();
} }
} }
else // Move just the active Node. else // Move just the active Node.
{ {
if (event->key() == Qt::Key_Up) m_activeNode->moveBy(0, -20); if (event->key() == Qt::Key_Up)
else if (event->key() == Qt::Key_Down) m_activeNode->moveBy(0, 20); m_activeNode->moveBy(0, -20);
else if (event->key() == Qt::Key_Left) m_activeNode->moveBy(-20, 0); else if (event->key() == Qt::Key_Down)
else if (event->key() == Qt::Key_Right) m_activeNode->moveBy(20, 0); m_activeNode->moveBy(0, 20);
else if (event->key() == Qt::Key_Left)
m_activeNode->moveBy(-20, 0);
else if (event->key() == Qt::Key_Right)
m_activeNode->moveBy(20, 0);
contentChanged(); contentChanged();
} }
} }
@ -832,7 +836,7 @@ void GraphWidget::addFirstNode()
m_nodeList.append(node); m_nodeList.append(node);
m_activeNode = m_nodeList.first(); m_activeNode = m_nodeList.first();
m_activeNode->setActive(); m_activeNode->setBorder();
} }
void GraphWidget::removeAllNodes() void GraphWidget::removeAllNodes()
@ -848,10 +852,10 @@ void GraphWidget::removeAllNodes()
void GraphWidget::setActiveNode(Node *node) void GraphWidget::setActiveNode(Node *node)
{ {
if (m_activeNode!=0) if (m_activeNode!=0)
m_activeNode->setActive(false); m_activeNode->setBorder(false);
m_activeNode = node; m_activeNode = node;
m_activeNode->setActive(); m_activeNode->setBorder();
} }
// re-draw numbers // re-draw numbers

@ -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 double Node::m_twoPi = Node::m_pi * 2.0;
const QColor Node::m_gold(255,215,0); const QColor Node::m_gold(255,215,0);
const QColor Node::m_orange(255,102,0);
Node::Node(GraphWidget *parent) : Node::Node(GraphWidget *parent) :
m_graph(parent), m_graph(parent),
m_isActive(false),
m_number(-1), m_number(-1),
m_hasBorder(false), m_hasBorder(false),
m_numberIsSpecial(false), m_numberIsSpecial(false),
@ -35,11 +33,6 @@ Node::~Node()
foreach (EdgeElement element, m_edgeList) delete element.edge; foreach (EdgeElement element, m_edgeList) delete element.edge;
} }
void Node::moveNode(QGraphicsSceneMouseEvent *event)
{
QGraphicsItem::mouseMoveEvent(event);
}
void Node::addEdge(Edge *edge, bool startsFromThisNode) void Node::addEdge(Edge *edge, bool startsFromThisNode)
{ {
m_edgeList.push_back(EdgeElement(edge, startsFromThisNode)); m_edgeList.push_back(EdgeElement(edge, startsFromThisNode));
@ -75,26 +68,83 @@ void Node::removeEdgeFromList(Edge *edge)
} }
} }
//void Node::adjustEdges() // edges from this Node. Exclude secondaries if needed (calc subtree)
//{ QList<Edge *> Node::edgesFrom(const bool &excludeSecondaries) const
// foreach (EdgeElement element, m_edgeList) element.edge->adjust(); {
//} 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; QList<Edge *> list;
update();
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? // the edge from this Node to the parameter Node
* Let is have border but the color shall not change, shadow is messy too. 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; /** @note QList crashes if modified while traversal,
setBorder(active); * QMutableListIterator lacks push_back, using good old std::list
update(); */
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) void Node::setEditable(const bool &editable)
@ -106,6 +156,8 @@ void Node::setEditable(const bool &editable)
} }
setTextInteractionFlags(Qt::TextEditable); setTextInteractionFlags(Qt::TextEditable);
// set cursor to the end
QTextCursor c = textCursor(); QTextCursor c = textCursor();
c.setPosition(c.document()->toPlainText().length()); c.setPosition(c.document()->toPlainText().length());
setTextCursor(c); setTextCursor(c);
@ -125,17 +177,20 @@ void Node::setTextColor(const QColor &color)
void Node::setScale(const qreal &factor,const QRectF &sceneRect) void Node::setScale(const qreal &factor,const QRectF &sceneRect)
{ {
// limit scale to a reasonable size
if (factor * scale() < 0.4 || if (factor * scale() < 0.4 ||
factor * scale() > 4 ) factor * scale() > 4 )
return; return;
// cannot scale out the Node from the scene
if (!sceneRect.contains(pos() + if (!sceneRect.contains(pos() +
boundingRect().bottomRight() * scale() * factor)) boundingRect().bottomRight() * scale() * factor))
return; return;
prepareGeometryChange(); prepareGeometryChange();
QGraphicsTextItem::setScale(factor * scale()); QGraphicsTextItem::setScale(factor * scale());
// scale edges to this Node too
foreach(EdgeElement element, m_edgeList) foreach(EdgeElement element, m_edgeList)
{ {
if (!element.startsFromThisNode) if (!element.startsFromThisNode)
@ -154,15 +209,11 @@ void Node::showNumber(const int &number,
update(); update();
} }
void Node::insertPicture(const QString &picture, const int &pos) void Node::insertPicture(const QString &picture)
{ {
QTextCursor c = textCursor(); QTextCursor c = textCursor();
if (pos) // strange, picture looks bad when node is scaled up
{
c.setPosition(pos);
}
c.insertHtml(QString("<img src=").append(picture). c.insertHtml(QString("<img src=").append(picture).
append(" width=15 height=15></img>")); 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(); 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()) if (m_edgeList.empty())
return Node::m_oneAndHalfPi; return Node::m_oneAndHalfPi;
// if there is only one edge, return with it's extension
if (m_edgeList.size()==1) if (m_edgeList.size()==1)
return m_edgeList.first().startsFromThisNode ? return m_edgeList.first().startsFromThisNode ?
Node::m_pi - m_edgeList.first().edge->angle() : Node::m_pi - m_edgeList.first().edge->angle() :
Node::m_twoPi - 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; 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++) it != m_edgeList.end(); it++)
{ {
tmp.push_back(it->startsFromThisNode ? tmp.push_back(it->startsFromThisNode ?
@ -190,6 +279,7 @@ double Node::calculateBiggestAngle()
} }
qSort(tmp.begin(), tmp.end()); qSort(tmp.begin(), tmp.end());
// find the biggest diffrence, store prev angle
double prev(tmp.first()); double prev(tmp.first());
double max_prev(tmp.last()); double max_prev(tmp.last());
double max(Node::m_twoPi - tmp.last() + tmp.first()); double max(Node::m_twoPi - tmp.last() + tmp.first());
@ -204,6 +294,7 @@ double Node::calculateBiggestAngle()
prev = *it; prev = *it;
} }
// return with prev angle + max diff / 2
return Node::m_twoPi - doubleModulo(max_prev + max / 2, Node::m_twoPi); 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 ///@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, void Node::paint(QPainter *painter,
const QStyleOptionGraphicsItem *option, const QStyleOptionGraphicsItem *option,
QWidget *w) QWidget *w)
@ -379,7 +359,6 @@ void Node::paint(QPainter *painter,
{ {
painter->setPen(Qt::transparent); painter->setPen(Qt::transparent);
painter->setBrush(m_numberIsSpecial ? Qt::green : Qt::yellow); painter->setBrush(m_numberIsSpecial ? Qt::green : Qt::yellow);
painter->drawRoundedRect(boundingRect(), 20.0, 15.0); painter->drawRoundedRect(boundingRect(), 20.0, 15.0);
} }
else else
@ -415,29 +394,29 @@ QVariant Node::itemChange(GraphicsItemChange change, const QVariant &value)
switch (change) { switch (change) {
case ItemPositionChange: 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. // Keep the item inside the scene rect.
QPointF newPos = value.toPointF(); newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left())));
newPos.setY(qMin(rect.bottom(), qMax(newPos.y(), rect.top())));
// the fence is reduced with the size of the node return newPos;
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;
}
} }
break;
break;
}
case ItemPositionHasChanged: case ItemPositionHasChanged:
// Notify parent, adjust edges that a move has happended.
m_graph->contentChanged(); m_graph->contentChanged();
foreach (EdgeElement element, m_edgeList) element.edge->adjust(); foreach (EdgeElement element, m_edgeList) element.edge->adjust();
break; break;
@ -467,6 +446,7 @@ void Node::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
QGraphicsItem::mouseReleaseEvent(event); QGraphicsItem::mouseReleaseEvent(event);
} }
// notify parent so subtree can be moved too if necessary
void Node::mouseMoveEvent(QGraphicsSceneMouseEvent *event) void Node::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{ {
m_graph->nodeMoved(event); m_graph->nodeMoved(event);
@ -479,16 +459,16 @@ QPainterPath Node::shape () const
return path; return path;
} }
// leave editing mode when user clicks on the view elsewhere for example
void Node::focusOutEvent(QFocusEvent *event) void Node::focusOutEvent(QFocusEvent *event)
{ {
qDebug() << __PRETTY_FUNCTION__;
Q_UNUSED(event); Q_UNUSED(event);
setEditable(false); setEditable(false);
m_graph->nodeLostFocus(); 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 return devided - static_cast<double>(devisor * static_cast<int>(devided
/ devisor)); / devisor));

Loading…
Cancel
Save