From c16240f8e4fe3a0edd68a9cfc597304d0bc53f60 Mon Sep 17 00:00:00 2001 From: Denes Matetelki Date: Sat, 2 Jul 2011 14:43:23 +0200 Subject: [PATCH] Huge refactor, graph logic has been taken out from GraphWidget to the new GraphLogic class. Refactor is not over: GraphWidget has too many functions still. --- .gitignore | 2 +- include/graphlogic.h | 94 +++++ include/graphwidget.h | 48 +-- include/node.h | 4 +- qtmindmap.pro | 4 + src/graphlogic.cpp | 784 ++++++++++++++++++++++++++++++++++++++++++ src/graphwidget.cpp | 784 ++++-------------------------------------- src/mainwindow.cpp | 4 +- src/node.cpp | 17 +- 9 files changed, 978 insertions(+), 763 deletions(-) create mode 100644 include/graphlogic.h create mode 100644 src/graphlogic.cpp diff --git a/.gitignore b/.gitignore index 96a56f9..5ef222e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,5 @@ Makefile moc_* qrc_* qtmindmap -*.pro.user +*.pro.user* html/ diff --git a/include/graphlogic.h b/include/graphlogic.h new file mode 100644 index 0000000..96bdcbd --- /dev/null +++ b/include/graphlogic.h @@ -0,0 +1,94 @@ +#ifndef GRAPHLOGIC_H +#define GRAPHLOGIC_H + +#include + +#include "node.h" +#include "graphwidget.h" + + +class GraphWidget; + +class GraphLogic : public QObject +{ + Q_OBJECT + +public: + + explicit GraphLogic(GraphWidget *parent = 0); + + // functions on nodes + void addFirstNode(); + void removeAllNodes(); + void setActiveNode(Node *node); + + bool readContentFromXmlFile(const QString &fileName); + void writeContentToXmlFile(const QString &fileName); + void writeContentToPngFile(const QString &fileName); + + bool editing() const; + void passKey(QKeyEvent *event = 0); + + // undo-able commands: + void insertNode(); + void removeNode(const bool &subtree = false); + void scaleUp(const bool &subtree = false); + void scaleDown(const bool &subtree = false); + void setNodeColor(const QColor &color, const bool &subtree = false); + void setNodeTextColor(const QColor &color, const bool &subtree = false); + void addEdge(); + void removeEdge(); + void hintMode(); + void insertPicture(const QString &picture); + void move(const int &x, const int &y, const bool &subtree = false); + + void appendNumber(const int &unm); + void delNumber(); + void applyNumber(); + + void nodeColor(const bool &subtree = false); + void nodeTextColor(const bool &subtree = false); + +public slots: + + void nodeChanged(); + void nodeSelected(); + void nodeEdited(QKeyEvent *event = 0); + void nodeMoved(QGraphicsSceneMouseEvent *event); + void nodeLostFocus(); + +signals: + + void contentChanged(); + void notification(const QString &msg); + +private: + + Node *nodeFactory(); + void selectNode(Node *node); + + + // functions on the edges + QList allEdges() const; + void addEdge(Node *source, Node *destination); + void removeEdge(Node* source, Node *destination); + + // hint mode's nodenumber handling functions + void showNodeNumbers(); + void showingAllNodeNumbers(const bool &show = true); + void showingNodeNumbersBeginWithNumber(const int &prefix, + const bool &show = true); + + GraphWidget *m_graphWidget; + + QList m_nodeList; + Node *m_activeNode; + bool m_showingNodeNumbers; + QString m_hintNumber; + Node *m_hintNode; + bool m_editingNode; + bool m_edgeAdding; + bool m_edgeDeleting; +}; + +#endif // GRAPHLOGIC_H diff --git a/include/graphwidget.h b/include/graphwidget.h index c8b44f6..cffab7b 100644 --- a/include/graphwidget.h +++ b/include/graphwidget.h @@ -6,9 +6,10 @@ #include #include -#include "node.h" +#include "graphlogic.h" class MainWindow; +class GraphLogic; class GraphWidget : public QGraphicsView { @@ -24,11 +25,14 @@ public: void writeContentToXmlFile(const QString &fileName); void writeContentToPngFile(const QString &fileName); + static const QColor m_paper; + public slots: // commands from MainWindow's MainToolBar's actions void insertNode(QKeyEvent *event = 0); void removeNode(QKeyEvent *event = 0); + void editNode(QKeyEvent *event = 0); void zoomIn(QKeyEvent *event = 0); void zoomOut(QKeyEvent *event = 0); void scaleUp(QKeyEvent *event = 0); @@ -37,17 +41,14 @@ public slots: void nodeTextColor(QKeyEvent *event = 0); void addEdge(QKeyEvent *event = 0); void removeEdge(QKeyEvent *event = 0); + void nodeLoseFocus(QKeyEvent *event = 0); void hintMode(QKeyEvent *event = 0); // bundled signals from statusIconsToolBar void insertPicture(const QString &picture); - // node reports back it's state change - void nodeChanged(); - void nodeSelected(); - void nodeEdited(QKeyEvent *event = 0); - void nodeMoved(QGraphicsSceneMouseEvent *event); - void nodeLostFocus(); + void contentChangedFromLogic(); + void notificationFromLogic(const QString &msg); signals: @@ -63,10 +64,6 @@ protected: private: - Node * nodeFactory(); - - void selectNode(Node *node); - // keymap commands void moveUp(QKeyEvent *event); void moveDown(QKeyEvent *event); @@ -78,40 +75,15 @@ private: void delNumber(QKeyEvent *event); void applyNumber(QKeyEvent *event); - void move(const int &x, const int &y, QKeyEvent *event); void scaleView(qreal scaleFactor); - // functions on the edges - QList allEdges() const; - void addEdge(Node *source, Node *destination); - void removeEdge(Node* source, Node *destination); - - // functions on nodes - void addFirstNode(); - void removeAllNodes(); - void setActiveNode(Node *node); - // hint mode's nodenumber handling functions - void showNodeNumbers(); - void showingAllNodeNumbers(const bool &show = true); - void showingNodeNumbersBeginWithNumber(const int &prefix, - const bool &show = true); - - - QList m_nodeList; MainWindow *m_parent; - Node *m_activeNode; QGraphicsScene *m_scene; - bool m_showingNodeNumbers; - QString m_hintNumber; - Node *m_hintNode; - bool m_editingNode; - bool m_edgeAdding; - bool m_edgeDeleting; - + GraphLogic *m_graphlogic; std::map m_memberMap; - static const QColor m_paper; + }; #endif // GRAPHWIDGET_H diff --git a/include/node.h b/include/node.h index dd2f10d..d363a7a 100644 --- a/include/node.h +++ b/include/node.h @@ -16,7 +16,7 @@ class Node : public QGraphicsTextItem public: - Node(GraphWidget *graphWidget = 0); + Node(); ~Node(); // add/remove edges @@ -91,7 +91,7 @@ private: }; QList m_edgeList; - GraphWidget *m_graph; +// GraphWidget *m_graph; int m_number; bool m_hasBorder; bool m_numberIsSpecial; diff --git a/qtmindmap.pro b/qtmindmap.pro index 9036105..b309013 100644 --- a/qtmindmap.pro +++ b/qtmindmap.pro @@ -17,18 +17,22 @@ TEMPLATE = app SOURCES += src/main.cpp \ src/mainwindow.cpp \ src/graphwidget.cpp \ + src/graphlogic.cpp \ src/node.cpp \ src/edge.cpp \ src/systemtray.cpp \ src/argumentparser.cpp + HEADERS += include/mainwindow.h \ include/graphwidget.h \ + include/graphlogic.h \ include/node.h \ include/edge.h \ include/systemtray.h \ include/argumentparser.h + FORMS += ui/mainwindow.ui RESOURCES += images/qtmindmap.qrc diff --git a/src/graphlogic.cpp b/src/graphlogic.cpp new file mode 100644 index 0000000..37b4e65 --- /dev/null +++ b/src/graphlogic.cpp @@ -0,0 +1,784 @@ +#include "include/graphwidget.h" + +#include +#include + +GraphLogic::GraphLogic(GraphWidget *parent) + : QObject(parent) + , m_graphWidget(parent) + , m_activeNode(0) + , m_showingNodeNumbers(false) + , m_hintNumber("") + , m_hintNode(0) + , m_editingNode(false) + , m_edgeAdding(false) + , m_edgeDeleting(false) +{ + +} + +void GraphLogic::addFirstNode() +{ + Node *node = nodeFactory(); + node->setHtml( + QString("")); + + m_activeNode = m_nodeList.first(); + m_activeNode->setBorder(); +} + +void GraphLogic::removeAllNodes() +{ + foreach(Node *node, m_nodeList) + delete node; + + m_nodeList.clear(); + m_activeNode = 0; + m_hintNode = 0; +} + +void GraphLogic::setActiveNode(Node *node) +{ + if (m_activeNode!=0) + m_activeNode->setBorder(false); + + m_activeNode = node; + if (m_activeNode) + m_activeNode->setBorder(); +} + + +bool GraphLogic::readContentFromXmlFile(const QString &fileName) +{ + // open & parse XML file + QDomDocument doc("QtMindMap"); + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) + { + emit notification(tr("Couldn't read file.")); + return false; + } + + if (!doc.setContent(&file)) + { + emit notification(tr("Couldn't parse XML file.")); + file.close(); + return false; + } + file.close(); + + QDomElement docElem = doc.documentElement(); + + // add nodes + QDomNodeList nodes = docElem.childNodes().item(0).childNodes(); + for (unsigned int i = 0; i < nodes.length(); i++) + { + QDomElement e = nodes.item(i).toElement(); + if(!e.isNull()) + { + Node *node = nodeFactory(); + node->setHtml(e.attribute("htmlContent")); + node->setPos(e.attribute("x").toFloat(), + e.attribute("y").toFloat()); + node->setScale(e.attribute("scale").toFloat(), + m_graphWidget->sceneRect()); + node->setColor(QColor(e.attribute("bg_red").toFloat(), + e.attribute("bg_green").toFloat(), + e.attribute("bg_blue").toFloat())); + node->setTextColor(QColor(e.attribute("text_red").toFloat(), + e.attribute("text_green").toFloat(), + e.attribute("text_blue").toFloat())); + } + } + + // add edges + QDomNodeList edges = docElem.childNodes().item(1).childNodes(); + for (unsigned int i = 0; i < edges.length(); i++) + { + QDomElement e = edges.item(i).toElement(); + if(!e.isNull()) + { + Node *source = m_nodeList[e.attribute("source").toInt()]; + Node *destination = m_nodeList[e.attribute("destination").toInt()]; + + Edge *edge = new Edge(source, destination); + source->addEdge(edge, true); + destination->addEdge(edge, false); + + edge->setColor(QColor(e.attribute("red").toFloat(), + e.attribute("green").toFloat(), + e.attribute("blue").toFloat())); + edge->setWidth(e.attribute("width").toFloat()); + edge->setSecondary(e.attribute("secondary").toInt() ); + + m_graphWidget->scene()->addItem(edge); + } + } + + // test the first node the active one + m_activeNode = m_nodeList.first(); + m_activeNode->setBorder(); + m_activeNode->setFocus(); + + m_graphWidget->show(); + return true; +} + +void GraphLogic::writeContentToXmlFile(const QString &fileName) +{ + // create XML doc object + QDomDocument doc("QtMindMap"); + + QDomElement root = doc.createElement("qtmindmap"); + doc.appendChild( root ); + + // nodes + QDomElement nodes_root = doc.createElement("nodes"); + root.appendChild(nodes_root); + foreach(Node *node, m_nodeList) + { + QDomElement cn = doc.createElement("node"); + + // no need to store ID: parsing order is preorder. + // cn.setAttribute( "id", QString::number(m_nodeList.indexOf(node))); + cn.setAttribute( "x", QString::number(node->pos().x())); + cn.setAttribute( "y", QString::number(node->pos().y())); + cn.setAttribute( "htmlContent", node->toHtml()); + cn.setAttribute( "scale", QString::number(node->scale())); + cn.setAttribute( "bg_red", QString::number(node->color().red())); + cn.setAttribute( "bg_green", QString::number(node->color().green())); + cn.setAttribute( "bg_blue", QString::number(node->color().blue())); + cn.setAttribute( "text_red", QString::number(node->textColor().red())); + cn.setAttribute( "text_green", + QString::number(node->textColor().green())); + cn.setAttribute( "text_blue", + QString::number(node->textColor().blue())); + nodes_root.appendChild(cn); + } + + //edges + QDomElement edges_root = doc.createElement("edges"); + root.appendChild(edges_root); + foreach(Edge *edge, allEdges()) + { + QDomElement cn = doc.createElement("edge"); + cn.setAttribute( "source", + QString::number(m_nodeList.indexOf(edge->sourceNode()))); + cn.setAttribute( "destination", + QString::number(m_nodeList.indexOf(edge->destNode()))); + cn.setAttribute( "red", QString::number(edge->color().red())); + cn.setAttribute( "green", QString::number(edge->color().green())); + cn.setAttribute( "blue", QString::number(edge->color().blue())); + cn.setAttribute( "width", QString::number(edge->width())); + cn.setAttribute( "secondary", QString::number(edge->secondary())); + + edges_root.appendChild(cn); + } + + // write XML doc object to file + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly)) + { + emit notification(tr("Couldn't open file to write.")); + return; + } + QTextStream ts( &file ); + ts << doc.toString(); + file.close(); + + // show a statusBar message to the user + emit notification(tr("Saved.")); +} + +void GraphLogic::writeContentToPngFile(const QString &fileName) +{ + QImage img(m_graphWidget->scene()->sceneRect().width(), + m_graphWidget->scene()->sceneRect().height(), + QImage::Format_ARGB32_Premultiplied); + QPainter painter(&img); + + painter.setRenderHint(QPainter::Antialiasing); + + // Strange that I have to set this, and scene->render() does not do this + m_graphWidget->scene()->setBackgroundBrush(GraphWidget::m_paper); + + m_graphWidget->scene()->render(&painter); + painter.setBackground(GraphWidget::m_paper); + painter.end(); + + img.save(fileName); + + // show a statusBar message to the user + emit notification(tr("MindMap exported as ") + fileName); +} + +bool GraphLogic::editing() const +{ + return m_editingNode; +} + +void GraphLogic::passKey(QKeyEvent *event) +{ + m_activeNode->keyPressEvent(event); +} + +void GraphLogic::insertNode() +{ + nodeLostFocus(); + + if (!m_activeNode) + { + emit notification(tr("No active node.")); + return; + } + + // get the biggest angle between the edges of the Node. + double angle(m_activeNode->calculateBiggestAngle()); + + // let the distance between the current and new Node be 100 pixels + qreal length(100); + + QPointF pos(length * cos(angle), length * sin(angle)); + + QPointF newPos(m_activeNode->sceneBoundingRect().center() + + pos - Node::newNodeCenter); + QRectF rect (m_graphWidget->scene()->sceneRect().topLeft(), + m_graphWidget->scene()->sceneRect().bottomRight() + - Node::newNodeBottomRigth); + + if (!rect.contains(newPos)) + { + emit notification(tr("New node would be placed outside of the scene")); + return; + } + + // add a new node which inherits the color and textColor + Node *node = nodeFactory(); + node->setColor(m_activeNode->color()); + node->setTextColor(m_activeNode->textColor()); + node->setHtml(QString("")); + node->setPos(newPos); + + addEdge(m_activeNode, node); + + // set it the active Node and editable, so the user can edit it at once + setActiveNode(node); + nodeEdited(); + + emit contentChanged(); + + // it we are in hint mode, the numbers shall be re-calculated + if (m_showingNodeNumbers) + showNodeNumbers(); +} + +void GraphLogic::removeNode(const bool &subtree) +{ + if (!m_activeNode) + { + emit notification(tr("No active node.")); + return; + } + + if (m_activeNode == m_nodeList.first()) + { + emit notification(tr("Base node cannot be deleted.")); + return; + } + + // remove just the active Node or it's subtree too? + QList nodeList; + if (subtree) + { + nodeList = m_activeNode->subtree(); + } + else + { + nodeList.push_back(m_activeNode); + } + + foreach(Node *node, nodeList) + { + if (m_hintNode==node) + m_hintNode=0; + + m_nodeList.removeAll(node); + delete node; + } + + m_activeNode = 0; + emit contentChanged(); + + // it we are in hint mode, the numbers shall be re-calculated + if (m_showingNodeNumbers) + showNodeNumbers(); +} + +void GraphLogic::scaleUp(const bool &subtree) +{ + if (!m_activeNode) + { + emit notification(tr("No active node.")); + return; + } + + if (subtree) + { + QList nodeList = m_activeNode->subtree(); + foreach(Node *node, nodeList) + node->setScale(qreal(1.2), m_graphWidget->sceneRect()); + } + else + { + m_activeNode->setScale(qreal(1.2),m_graphWidget->sceneRect()); + } +} + +void GraphLogic::scaleDown(const bool &subtree) +{ + if (!m_activeNode) + { + emit notification(tr("No active node.")); + return; + } + + if (subtree) + { + QList nodeList = m_activeNode->subtree(); + foreach(Node *node, nodeList) + node->setScale(qreal(1 / 1.2),m_graphWidget->sceneRect()); + } + else + { + m_activeNode->setScale(qreal(1 / 1.2),m_graphWidget->sceneRect()); + } +} + +void GraphLogic::setNodeColor(const QColor &color, const bool &subtree) +{ + QList nodeList; + if (subtree) + { + nodeList = m_activeNode->subtree(); + } + else + { + nodeList.push_back(m_activeNode); + } + + foreach(Node *node, nodeList) + { + node->setColor(color); + foreach (Edge * edge, node->edgesToThis(false)) + edge->setColor(color); + } +} + +void GraphLogic::setNodeTextColor(const QColor &color, const bool &subtree) +{ + QList nodeList; + if (subtree) + { + nodeList = m_activeNode->subtree(); + } + else + { + nodeList.push_back(m_activeNode); + } + + foreach(Node *node, nodeList) + node->setTextColor(color); +} + +void GraphLogic::addEdge() +{ + emit notification(tr("Add edge: select destination node.")); + m_edgeAdding = true; +} + +void GraphLogic::removeEdge() +{ + emit notification(tr("Delete edge: select other end-node.")); + m_edgeDeleting = true; +} + +void GraphLogic::hintMode() +{ + // vimperator-style node selection with keys. + // show or hide the numbers if we enter/leave this mode. + m_showingNodeNumbers = !m_showingNodeNumbers; + if (!m_showingNodeNumbers) + { + showingAllNodeNumbers(false); + return; + } + + m_hintNumber.clear(); + showNodeNumbers(); +} + + +void GraphLogic::insertPicture(const QString &picture) +{ + if (!m_activeNode) + { + emit notification(tr("No active node.")); + return; + } + + m_activeNode->insertPicture(picture); +} + +void GraphLogic::move(const int &x, const int &y, const bool &subtree) +{ + if (!m_activeNode) + { + emit notification(tr("No active node.")); + return; + } + + if (subtree) + { + QList nodeList = m_activeNode->subtree(); + foreach(Node *node, nodeList) + node->moveBy(x, y); + + emit contentChanged(); + } + else // Move just the active Node. + { + m_activeNode->moveBy(x, y); + emit contentChanged(); + } +} + +void GraphLogic::appendNumber(const int &num) +{ + if (!m_showingNodeNumbers) + return; + + m_hintNumber.append(QString::number(num)); + + showingAllNodeNumbers(false); + showingNodeNumbersBeginWithNumber(m_hintNumber.toInt(), true); +} + +void GraphLogic::delNumber() +{ + if (!m_showingNodeNumbers && m_hintNumber.isEmpty()) + return; + + m_hintNumber.remove(m_hintNumber.length()-1,1); + showNodeNumbers(); +} + +void GraphLogic::applyNumber() +{ + if (m_hintNode && m_showingNodeNumbers) + selectNode(m_hintNode); +} + +void GraphLogic::nodeColor(const bool &subtree) +{ + if (!m_activeNode) + { + emit notification(tr("No active node.")); + return; + } + + // popup a color selector dialogm def color is the curr one. + QColorDialog dialog(m_graphWidget); + dialog.setWindowTitle(tr("Select node color")); + dialog.setCurrentColor(m_activeNode->color()); + if (!dialog.exec()) + return; + + setNodeColor(dialog.selectedColor(), subtree); +} + +void GraphLogic::nodeTextColor(const bool &subtree) +{ + if (!m_activeNode) + { + emit notification(tr("No active node.")); + return; + } + + // popup a color selector dialogm def color is the curr one. + QColorDialog dialog(m_graphWidget); + dialog.setWindowTitle(tr("Select text color")); + dialog.setCurrentColor(m_activeNode->textColor()); + if (!dialog.exec()) + return; + + setNodeTextColor(dialog.selectedColor(), subtree); +} + +void GraphLogic::nodeChanged() +{ + emit contentChanged(); +} + +void GraphLogic::nodeSelected() +{ + // if node == 0 then nodeSelected invoked after a signal from a Node + selectNode(dynamic_cast(QObject::sender())); +} + +void GraphLogic::nodeEdited(QKeyEvent *event) +{ + Q_UNUSED(event) + if (!m_activeNode) + { + emit notification(tr("No active node.")); + return; + } + + m_editingNode = true; + m_activeNode->setEditable(); + m_graphWidget->scene()->setFocusItem(m_activeNode); +} + +void GraphLogic::nodeMoved(QGraphicsSceneMouseEvent *event) +{ + // move just the active Node, or it's subtree too? + QList nodeList; + if (event->modifiers() & Qt::ControlModifier && + event->modifiers() & Qt::ShiftModifier) + { + nodeList = m_activeNode->subtree(); + } + else + { + nodeList.push_back(m_activeNode); + } + + foreach(Node *node, nodeList) + node->setPos(node->pos() + event->scenePos() - event->lastScenePos()); +} + + +void GraphLogic::nodeLostFocus() +{ + if (m_editingNode) + { + m_editingNode = false; + if (m_activeNode) + { + m_activeNode->setEditable(false); + m_activeNode->update(); + } + return; + } + + if (m_edgeAdding) + { + m_edgeAdding = false; + emit notification(tr("Edge adding cancelled.")); + return; + } + + if (m_edgeDeleting) + { + m_edgeDeleting = false; + emit notification(tr("Edge deleting cancelled.")); + return; + } + + if(m_showingNodeNumbers) + { + m_hintNumber.clear(); + showingAllNodeNumbers(false); + m_showingNodeNumbers = false; + return; + } +} + +Node * GraphLogic::nodeFactory() +{ + Node *node = new Node(); + + connect(node, SIGNAL(nodeChanged()), this, SLOT(nodeChanged())); + connect(node, SIGNAL(nodeSelected()), this, SLOT(nodeSelected())); + connect(node, SIGNAL(nodeEdited()), this, SLOT(nodeEdited())); + connect(node, SIGNAL(nodeMoved(QGraphicsSceneMouseEvent*)), + this, SLOT(nodeMoved(QGraphicsSceneMouseEvent*))); + connect(node, SIGNAL(nodeLostFocus()), this, SLOT(nodeLostFocus())); + + m_graphWidget->scene()->addItem(node); + m_nodeList.append(node); + + return node; +} + +void GraphLogic::selectNode(Node *node) +{ + // leave hint mode + showingAllNodeNumbers(false); + m_showingNodeNumbers = false; + + if (m_edgeAdding) + { + addEdge(m_activeNode, node); + m_edgeAdding = false; + } + else if (m_edgeDeleting) + { + removeEdge(m_activeNode, node); + m_edgeDeleting = false; + } + else + { + setActiveNode(node); + } +} + +QList GraphLogic::allEdges() const +{ + QList list; + + // GraphWidget has a list of Nodes only. + // Each Node maintains a list of it's own Edges. + // We iterate through the list of Nodes and call Node::edgesFrom() on them. + // edgesFrom(exludeSecundaries=false) return a list of edges (including + // secondary edges) which starts from this Node. + foreach(Node * node, m_nodeList) + list.append(node->edgesFrom(false)); + + return list; +} + + +void GraphLogic::addEdge(Node *source, Node *destination) +{ + if (!m_activeNode) + { + emit notification(tr("No active node.")); + return; + } + + if (destination == m_nodeList.first()) + { + setActiveNode(destination); + emit notification( + tr("Root element cannot be an edge target.")); + return; + } + + if (source->isConnected(destination)) + { + setActiveNode(destination); + emit notification( + tr("There is already an edge between these two nodes.")); + } + else + { + // aviod the graph beeing acyclic. (ok, Nodes having multiple parents) + bool sec(false); + if (!destination->edgesToThis().empty()) + { + emit notification( + tr("The graph is acyclic, edge added as secondary edge.")); + sec = true; + } + Edge *edge = new Edge(source, destination); + source->addEdge(edge, true); + destination->addEdge(edge, false); + + edge->setColor(destination->color()); + edge->setWidth(destination->scale()*2 + 1); + + // The Edge is secondary, because the Node already has a parent + // (it is already a destination of another Edge) + edge->setSecondary(sec); + m_graphWidget->scene()->addItem(edge); + + setActiveNode(destination); + emit contentChanged(); + } +} + +void GraphLogic::removeEdge(Node *source, Node *destination) +{ + if (!m_activeNode) + { + emit notification(tr("No active node.")); + return; + } + + if (!source->isConnected(destination)) + { + setActiveNode(destination); + emit notification(tr("There no edge between these two nodes.")); + } + else + { + source->deleteEdge(destination); + setActiveNode(destination); + emit contentChanged(); + } +} + +// re-draw numbers +void GraphLogic::showNodeNumbers() +{ + if (m_hintNumber.isEmpty()) + { + showingAllNodeNumbers(true); + m_nodeList.first()->showNumber(0,true,true); + m_hintNode = m_nodeList.first(); + } + else + { + showingAllNodeNumbers(false); + showingNodeNumbersBeginWithNumber(m_hintNumber.toInt(), true); + } +} + +// show/hide numbers on all nodes +void GraphLogic::showingAllNodeNumbers(const bool &show) +{ + int i(0); + for (QList::const_iterator it = m_nodeList.begin(); + it != m_nodeList.end(); it++, + i++) + { + dynamic_cast(*it)->showNumber(i,show); + } +} + +// show nodes numbers where number begins with 'prefix' +void GraphLogic::showingNodeNumbersBeginWithNumber(const int &prefix, + const bool &show) +{ + int i(0); + int hit(0); + for (QList::const_iterator it = m_nodeList.begin(); + it != m_nodeList.end(); it++, i++) + { + // if nodenumber == 'prefix' the node is selected + if (i == prefix) + { + hit++; + dynamic_cast(*it)->showNumber(i,show,true); + m_hintNode = dynamic_cast(*it); + continue; + } + + // if 'i' starts with 'prefix' + if ((QString::number(i)).startsWith(QString::number(prefix))) + { + hit++; + dynamic_cast(*it)->showNumber(i,show); + } + } + if (hit==1) + { + selectNode(m_hintNode); + } + else if (hit == 0) + { + m_showingNodeNumbers = false; + showingAllNodeNumbers(false); + } +} diff --git a/src/graphwidget.cpp b/src/graphwidget.cpp index 1dd74d3..815e518 100644 --- a/src/graphwidget.cpp +++ b/src/graphwidget.cpp @@ -4,8 +4,6 @@ #include #include #include -#include -#include #include #include "include/node.h" @@ -19,12 +17,6 @@ const QColor GraphWidget::m_paper(255,255,153); GraphWidget::GraphWidget(MainWindow *parent) : QGraphicsView(parent) , m_parent(parent) - , m_activeNode(0) - , m_showingNodeNumbers(false) - , m_hintNode(0) - , m_editingNode(false) - , m_edgeAdding(false) - , m_edgeDeleting(false) { m_scene = new QGraphicsScene(this); m_scene->setItemIndexMethod(QGraphicsScene::NoIndex); @@ -81,12 +73,12 @@ GraphWidget::GraphWidget(MainWindow *parent) (Qt::Key_0, &GraphWidget::appendNumber)); m_memberMap.insert(std::pair - (Qt::Key_F2, &GraphWidget::nodeEdited)); + (Qt::Key_F2, &GraphWidget::editNode)); m_memberMap.insert(std::pair (Qt::Key_Backspace, &GraphWidget::delNumber)); m_memberMap.insert(std::pair - (Qt::Key_Return, &GraphWidget::delNumber)); + (Qt::Key_Return, &GraphWidget::applyNumber)); m_memberMap.insert(std::pair (Qt::Key_Enter, &GraphWidget::applyNumber)); m_memberMap.insert(std::pair @@ -100,320 +92,62 @@ GraphWidget::GraphWidget(MainWindow *parent) (Qt::Key_C, &GraphWidget::nodeColor)); m_memberMap.insert(std::pair (Qt::Key_T, &GraphWidget::nodeTextColor)); -} -void GraphWidget::nodeSelected() -{ - // if node == 0 then nodeSelected invoked after a signal from a Node - selectNode(dynamic_cast(QObject::sender())); -} - -void GraphWidget::nodeMoved(QGraphicsSceneMouseEvent *event) -{ - // move just the active Node, or it's subtree too? - QList nodeList; - if (event->modifiers() & Qt::ControlModifier && - event->modifiers() & Qt::ShiftModifier) - { - nodeList = m_activeNode->subtree(); - } - else - { - nodeList.push_back(m_activeNode); - } + m_graphlogic = new GraphLogic(this); + connect(m_graphlogic, SIGNAL(contentChanged()), + this, SLOT(contentChangedFromLogic())); - foreach(Node *node, nodeList) - node->setPos(node->pos() + event->scenePos() - event->lastScenePos()); -} - -void GraphWidget::nodeChanged() -{ - emit contentChanged(); + connect(m_graphlogic, SIGNAL(notification(QString)), + this, SLOT(notificationFromLogic(QString))); } void GraphWidget::newScene() { - removeAllNodes(); - addFirstNode(); + m_graphlogic->removeAllNodes(); + m_graphlogic->addFirstNode(); + this->show(); } void GraphWidget::closeScene() { - removeAllNodes(); + m_graphlogic->removeAllNodes(); this->hide(); } bool GraphWidget::readContentFromXmlFile(const QString &fileName) { - // open & parse XML file - QDomDocument doc("QtMindMap"); - QFile file(fileName); - if (!file.open(QIODevice::ReadOnly)) - { - emit notification(tr("Couldn't read file.")); - return false; - } - - if (!doc.setContent(&file)) - { - emit notification(tr("Couldn't parse XML file.")); - file.close(); - return false; - } - file.close(); - - QDomElement docElem = doc.documentElement(); - - // add nodes - QDomNodeList nodes = docElem.childNodes().item(0).childNodes(); - for (unsigned int i = 0; i < nodes.length(); i++) - { - QDomElement e = nodes.item(i).toElement(); - if(!e.isNull()) - { - Node *node = nodeFactory(); - node->setHtml(e.attribute("htmlContent")); - node->setPos(e.attribute("x").toFloat(), - e.attribute("y").toFloat()); - node->setScale(e.attribute("scale").toFloat(),sceneRect()); - node->setColor(QColor(e.attribute("bg_red").toFloat(), - e.attribute("bg_green").toFloat(), - e.attribute("bg_blue").toFloat())); - node->setTextColor(QColor(e.attribute("text_red").toFloat(), - e.attribute("text_green").toFloat(), - e.attribute("text_blue").toFloat())); - } - } - - // add edges - QDomNodeList edges = docElem.childNodes().item(1).childNodes(); - for (unsigned int i = 0; i < edges.length(); i++) - { - QDomElement e = edges.item(i).toElement(); - if(!e.isNull()) - { - Node *source = m_nodeList[e.attribute("source").toInt()]; - Node *destination = m_nodeList[e.attribute("destination").toInt()]; - - Edge *edge = new Edge(source, destination); - source->addEdge(edge, true); - destination->addEdge(edge, false); - - edge->setColor(QColor(e.attribute("red").toFloat(), - e.attribute("green").toFloat(), - e.attribute("blue").toFloat())); - edge->setWidth(e.attribute("width").toFloat()); - edge->setSecondary(e.attribute("secondary").toInt() ); - - m_scene->addItem(edge); - } - } - - // test the first node the active one - m_activeNode = m_nodeList.first(); - m_activeNode->setBorder(); - m_activeNode->setFocus(); - - this->show(); - return true; + return m_graphlogic->readContentFromXmlFile(fileName); } void GraphWidget::writeContentToXmlFile(const QString &fileName) { - // create XML doc object - QDomDocument doc("QtMindMap"); - - QDomElement root = doc.createElement("qtmindmap"); - doc.appendChild( root ); - - // nodes - QDomElement nodes_root = doc.createElement("nodes"); - root.appendChild(nodes_root); - foreach(Node *node, m_nodeList) - { - QDomElement cn = doc.createElement("node"); - - // no need to store ID: parsing order is preorder. - // cn.setAttribute( "id", QString::number(m_nodeList.indexOf(node))); - cn.setAttribute( "x", QString::number(node->pos().x())); - cn.setAttribute( "y", QString::number(node->pos().y())); - cn.setAttribute( "htmlContent", node->toHtml()); - cn.setAttribute( "scale", QString::number(node->scale())); - cn.setAttribute( "bg_red", QString::number(node->color().red())); - cn.setAttribute( "bg_green", QString::number(node->color().green())); - cn.setAttribute( "bg_blue", QString::number(node->color().blue())); - cn.setAttribute( "text_red", QString::number(node->textColor().red())); - cn.setAttribute( "text_green", - QString::number(node->textColor().green())); - cn.setAttribute( "text_blue", - QString::number(node->textColor().blue())); - nodes_root.appendChild(cn); - } - - //edges - QDomElement edges_root = doc.createElement("edges"); - root.appendChild(edges_root); - foreach(Edge *edge, allEdges()) - { - QDomElement cn = doc.createElement("edge"); - cn.setAttribute( "source", - QString::number(m_nodeList.indexOf(edge->sourceNode()))); - cn.setAttribute( "destination", - QString::number(m_nodeList.indexOf(edge->destNode()))); - cn.setAttribute( "red", QString::number(edge->color().red())); - cn.setAttribute( "green", QString::number(edge->color().green())); - cn.setAttribute( "blue", QString::number(edge->color().blue())); - cn.setAttribute( "width", QString::number(edge->width())); - cn.setAttribute( "secondary", QString::number(edge->secondary())); - - edges_root.appendChild(cn); - } - - // write XML doc object to file - QFile file(fileName); - if (!file.open(QIODevice::WriteOnly)) - { - emit notification(tr("Couldn't open file to write.")); - return; - } - QTextStream ts( &file ); - ts << doc.toString(); - file.close(); - - // show a statusBar message to the user - emit notification(tr("Saved.")); + m_graphlogic->writeContentToXmlFile(fileName); } void GraphWidget::writeContentToPngFile(const QString &fileName) { - QImage img(m_scene->sceneRect().width(), - m_scene->sceneRect().height(), - QImage::Format_ARGB32_Premultiplied); - QPainter painter(&img); - - painter.setRenderHint(QPainter::Antialiasing); - - // Strange that I have to set this, and scene->render() does not do this - m_scene->setBackgroundBrush(GraphWidget::m_paper); - - m_scene->render(&painter); - painter.setBackground(GraphWidget::m_paper); - painter.end(); - - img.save(fileName); - - // show a statusBar message to the user - emit notification(tr("MindMap exported as ") + fileName); + m_graphlogic->writeContentToPngFile(fileName); } void GraphWidget::insertNode(QKeyEvent *event) { Q_UNUSED(event) - nodeLostFocus(); - - if (!m_activeNode) - { - emit notification(tr("No active node.")); - return; - } - - // get the biggest angle between the edges of the Node. - double angle(m_activeNode->calculateBiggestAngle()); - - // let the distance between the current and new Node be 100 pixels - qreal length(100); - - QPointF pos(length * cos(angle), length * sin(angle)); - - QPointF newPos(m_activeNode->sceneBoundingRect().center() + - pos - Node::newNodeCenter); - QRectF rect (scene()->sceneRect().topLeft(), - scene()->sceneRect().bottomRight() - - Node::newNodeBottomRigth); - - if (!rect.contains(newPos)) - { - emit notification(tr("New node would be placed outside of the scene")); - return; - } - - // add a new node which inherits the color and textColor - Node *node = nodeFactory(); - node->setColor(m_activeNode->color()); - node->setTextColor(m_activeNode->textColor()); - node->setHtml(QString("")); - node->setPos(newPos); - - addEdge(m_activeNode, node); - - // set it the active Node and editable, so the user can edit it at once - setActiveNode(node); - nodeEdited(); - - emit contentChanged(); - - // it we are in hint mode, the numbers shall be re-calculated - if (m_showingNodeNumbers) - showNodeNumbers(); + m_graphlogic->insertNode(); } void GraphWidget::removeNode(QKeyEvent *event) { + m_graphlogic->removeNode( + QApplication::keyboardModifiers() & Qt::ControlModifier && + QApplication::keyboardModifiers() & Qt::ShiftModifier); Q_UNUSED(event) - if (!m_activeNode) - { - emit notification(tr("No active node.")); - return; - } - - if (m_activeNode == m_nodeList.first()) - { - emit notification(tr("Base node cannot be deleted.")); - return; - } - - // remove just the active Node or it's subtree too? - QList nodeList; - if (QApplication::keyboardModifiers() & Qt::ControlModifier && - QApplication::keyboardModifiers() & Qt::ShiftModifier) - { - nodeList = m_activeNode->subtree(); - } - else - { - nodeList.push_back(m_activeNode); - } - - foreach(Node *node, nodeList) - { - if (m_hintNode==node) - m_hintNode=0; - - m_nodeList.removeAll(node); - delete node; - } - - m_activeNode = 0; - emit contentChanged(); - - // it we are in hint mode, the numbers shall be re-calculated - if (m_showingNodeNumbers) - showNodeNumbers(); } -void GraphWidget::nodeEdited(QKeyEvent *event) +void GraphWidget::editNode(QKeyEvent *event) { Q_UNUSED(event) - if (!m_activeNode) - { - emit notification(tr("No active node.")); - return; - } - - m_editingNode = true; - m_activeNode->setEditable(); - m_scene->setFocusItem(m_activeNode); + m_graphlogic->nodeEdited(); } void GraphWidget::zoomIn(QKeyEvent *event) @@ -431,194 +165,74 @@ void GraphWidget::zoomOut(QKeyEvent *event) void GraphWidget::scaleUp(QKeyEvent *event) { Q_UNUSED(event) - if (!m_activeNode) - { - emit notification(tr("No active node.")); - return; - } - - // Scale up just the active Node or it's subtree too? - if (QApplication::keyboardModifiers() & Qt::ShiftModifier) - { - QList nodeList = m_activeNode->subtree(); - foreach(Node *node, nodeList) - node->setScale(qreal(1.2),sceneRect()); - } - else - { - m_activeNode->setScale(qreal(1.2),sceneRect()); - } + m_graphlogic->scaleUp( + QApplication::keyboardModifiers() & Qt::ControlModifier && + QApplication::keyboardModifiers() & Qt::ShiftModifier); } void GraphWidget::scaleDown(QKeyEvent *event) { Q_UNUSED(event) - if (!m_activeNode) - { - emit notification(tr("No active node.")); - return; - } - - // Scale down just the active Node or it's subtree too? - if (QApplication::keyboardModifiers() & Qt::ShiftModifier) - { - QList nodeList = m_activeNode->subtree(); - foreach(Node *node, nodeList) - node->setScale(qreal(1 / 1.2),sceneRect()); - } - else - { - m_activeNode->setScale(qreal(1 / 1.2),sceneRect()); - } + m_graphlogic->scaleDown( + QApplication::keyboardModifiers() & Qt::ControlModifier && + QApplication::keyboardModifiers() & Qt::ShiftModifier); } void GraphWidget::nodeColor(QKeyEvent *event) { Q_UNUSED(event) - if (!m_activeNode) - { - emit notification(tr("No active node.")); - return; - } - - // Set color of the active Node or of it's subtree too? - QList nodeList; - if (QApplication::keyboardModifiers() & Qt::ControlModifier && - QApplication::keyboardModifiers() & Qt::ShiftModifier) - { - nodeList = m_activeNode->subtree(); - } - else - { - nodeList.push_back(m_activeNode); - } - - // popup a color selector dialogm def color is the curr one. - QColorDialog dialog(this); - dialog.setWindowTitle(tr("Select node color")); - dialog.setCurrentColor(m_activeNode->color()); - if (!dialog.exec()) - return; - - QColor color = dialog.selectedColor(); - foreach(Node *node, nodeList) - { - node->setColor(color); - foreach (Edge * edge, node->edgesToThis(false)) - edge->setColor(color); - } + m_graphlogic->nodeColor( + QApplication::keyboardModifiers() & Qt::ControlModifier && + QApplication::keyboardModifiers() & Qt::ShiftModifier); } void GraphWidget::nodeTextColor(QKeyEvent *event) { Q_UNUSED(event) - if (!m_activeNode) - { - emit notification(tr("No active node.")); - return; - } - - // Set textcolor of the active Node or of it's subtree too? - QList nodeList; - if (QApplication::keyboardModifiers() & Qt::ControlModifier && - QApplication::keyboardModifiers() & Qt::ShiftModifier) - { - nodeList = m_activeNode->subtree(); - } - else - { - nodeList.push_back(m_activeNode); - } - - // popup a color selector dialogm def color is the curr one. - QColorDialog dialog(this); - dialog.setWindowTitle(tr("Select text color")); - dialog.setCurrentColor(m_activeNode->textColor()); - if (!dialog.exec()) - return; - - QColor color = dialog.selectedColor(); - foreach(Node *node, nodeList) - node->setTextColor(color); + m_graphlogic->nodeTextColor( + QApplication::keyboardModifiers() & Qt::ControlModifier && + QApplication::keyboardModifiers() & Qt::ShiftModifier); } void GraphWidget::addEdge(QKeyEvent *event) { Q_UNUSED(event) - emit notification(tr("Add edge: select destination node.")); - m_edgeAdding = true; + m_graphlogic->addEdge(); } void GraphWidget::removeEdge(QKeyEvent *event) { Q_UNUSED(event) - emit notification(tr("Delete edge: select other end-node.")); - m_edgeDeleting = true; + m_graphlogic->removeEdge(); } -void GraphWidget::nodeLostFocus() +void GraphWidget::nodeLoseFocus(QKeyEvent *event) { - if (m_editingNode) - { - m_editingNode = false; - if (m_activeNode) - { - m_activeNode->setEditable(false); - m_activeNode->update(); - } - return; - } - - if (m_edgeAdding) - { - m_edgeAdding = false; - emit notification(tr("Edge adding cancelled.")); - return; - } - - if (m_edgeDeleting) - { - m_edgeDeleting = false; - emit notification(tr("Edge deleting cancelled.")); - return; - } - - if(m_showingNodeNumbers) - { - m_hintNumber.clear(); - showingAllNodeNumbers(false); - m_showingNodeNumbers = false; - return; - } + Q_UNUSED(event) + m_graphlogic->nodeLostFocus(); } void GraphWidget::hintMode(QKeyEvent *event) { Q_UNUSED(event) - - // vimperator-style node selection with keys. - // show or hide the numbers if we enter/leave this mode. - m_showingNodeNumbers = !m_showingNodeNumbers; - if (!m_showingNodeNumbers) - { - showingAllNodeNumbers(false); - return; - } - - m_hintNumber.clear(); - showNodeNumbers(); + m_graphlogic->hintMode(); } void GraphWidget::insertPicture(const QString &picture) { - if (!m_activeNode) - { - emit notification(tr("No active node.")); - return; - } + Q_UNUSED(picture) + m_graphlogic->insertPicture(picture); +} + +void GraphWidget::contentChangedFromLogic() +{ + emit contentChanged(); +} - m_activeNode->insertPicture(picture); +void GraphWidget::notificationFromLogic(const QString &msg) +{ + emit notification(msg); } // All key event arrives here. @@ -629,14 +243,14 @@ void GraphWidget::keyPressEvent(QKeyEvent *event) // Node lost focus: leaving edge adding/deleting or Node editing. if (event->key() == Qt::Key_Escape) { - nodeLostFocus(); + nodeLoseFocus(); return; } // During Node editing mode, pass every key to the Node's keyPressEvent. - if (m_editingNode) + if (m_graphlogic->editing()) { - m_activeNode->keyPressEvent(event); + m_graphlogic->passKey(event); return; } @@ -665,137 +279,64 @@ void GraphWidget::drawBackground(QPainter *painter, const QRectF &rect) painter->drawRect(m_scene->sceneRect()); } -Node * GraphWidget::nodeFactory() -{ - Node *node = new Node(this); - - connect(node, SIGNAL(nodeChanged()), this, SLOT(nodeChanged())); - connect(node, SIGNAL(nodeSelected()), this, SLOT(nodeSelected())); - connect(node, SIGNAL(nodeEdited()), this, SLOT(nodeEdited())); - connect(node, SIGNAL(nodeMoved(QGraphicsSceneMouseEvent*)), - this, SLOT(nodeMoved(QGraphicsSceneMouseEvent*))); - connect(node, SIGNAL(nodeLostFocus()), this, SLOT(nodeLostFocus())); - - m_scene->addItem(node); - m_nodeList.append(node); - - return node; -} - -void GraphWidget::selectNode(Node *node) -{ - // leave hint mode - showingAllNodeNumbers(false); - m_showingNodeNumbers = false; - - if (m_edgeAdding) - { - addEdge(m_activeNode, node); - m_edgeAdding = false; - } - else if (m_edgeDeleting) - { - removeEdge(m_activeNode, node); - m_edgeDeleting = false; - } - else - { - setActiveNode(node); - } -} void GraphWidget::moveUp(QKeyEvent *event) { - move(-20,0,event); + event->modifiers() & Qt::ControlModifier ? + m_graphlogic->move(0,-20, event->modifiers() & Qt::ShiftModifier) : + QGraphicsView::keyPressEvent(event); } void GraphWidget::moveDown(QKeyEvent *event) { - move(20,0,event); + event->modifiers() & Qt::ControlModifier ? + m_graphlogic->move(0,20, event->modifiers() & Qt::ShiftModifier) : + QGraphicsView::keyPressEvent(event); } void GraphWidget::moveLeft(QKeyEvent *event) { - move(0,-20,event); + event->modifiers() & Qt::ControlModifier ? + m_graphlogic->move(-20,0, event->modifiers() & Qt::ShiftModifier) : + QGraphicsView::keyPressEvent(event); } void GraphWidget::moveRight(QKeyEvent *event) { - move(0,20,event); + event->modifiers() & Qt::ControlModifier ? + m_graphlogic->move(20,0, event->modifiers() & Qt::ShiftModifier) : + QGraphicsView::keyPressEvent(event); } void GraphWidget::increment(QKeyEvent *event) { - Q_UNUSED(event) - QApplication::keyboardModifiers() & Qt::ControlModifier ? - scaleDown() : - zoomOut(); + event->modifiers() & Qt::ControlModifier ? + scaleUp(event) : + zoomIn(); } void GraphWidget::decrement(QKeyEvent *event) { - Q_UNUSED(event) - QApplication::keyboardModifiers() & Qt::ControlModifier ? - scaleDown() : + event->modifiers() & Qt::ControlModifier ? + scaleDown(event) : zoomOut(); } void GraphWidget::appendNumber(QKeyEvent *event) { - if (!m_showingNodeNumbers) - return; - - m_hintNumber.append(QString::number(event->key()-48)); - showingAllNodeNumbers(false); - showingNodeNumbersBeginWithNumber(m_hintNumber.toInt(), true); + m_graphlogic->appendNumber(event->key()-48); } void GraphWidget::delNumber(QKeyEvent *event) { Q_UNUSED(event) - if (!m_showingNodeNumbers && m_hintNumber.isEmpty()) - return; - - m_hintNumber.remove(m_hintNumber.length()-1,1); - showNodeNumbers(); + m_graphlogic->delNumber(); } void GraphWidget::applyNumber(QKeyEvent *event) { Q_UNUSED(event) - if (m_hintNode && m_showingNodeNumbers) - selectNode(m_hintNode); -} - -void GraphWidget::move(const int &x, const int &y, QKeyEvent *event) -{ - if (!m_activeNode) - { - emit notification(tr("No active node.")); - return; - } - - if (QApplication::keyboardModifiers() & Qt::ControlModifier) - { - // Move whole subtree of active Node. - if (QApplication::keyboardModifiers() & Qt::ShiftModifier) - { - QList nodeList = m_activeNode->subtree(); - foreach(Node *node, nodeList) - node->moveBy(x, y); - - emit contentChanged(); - } - else // Move just the active Node. - { - m_activeNode->moveBy(x, y); - emit contentChanged(); - } - } - else // Move scene. - { - QGraphicsView::keyPressEvent(event); - } + m_graphlogic->applyNumber(); } void GraphWidget::scaleView(qreal scaleFactor) @@ -809,184 +350,3 @@ void GraphWidget::scaleView(qreal scaleFactor) scale(scaleFactor, scaleFactor); } - -QList GraphWidget::allEdges() const -{ - QList list; - - // GraphWidget has a list of Nodes only. - // Each Node maintains a list of it's own Edges. - // We iterate through the list of Nodes and call Node::edgesFrom() on them. - // edgesFrom(exludeSecundaries=false) return a list of edges (including - // secondary edges) which starts from this Node. - foreach(Node * node, m_nodeList) - list.append(node->edgesFrom(false)); - - return list; -} - - -void GraphWidget::addEdge(Node *source, Node *destination) -{ - if (!m_activeNode) - { - emit notification(tr("No active node.")); - return; - } - - if (destination == m_nodeList.first()) - { - setActiveNode(destination); - emit notification( - tr("Root element cannot be an edge target.")); - return; - } - - if (source->isConnected(destination)) - { - setActiveNode(destination); - emit notification( - tr("There is already an edge between these two nodes.")); - } - else - { - // aviod the graph beeing acyclic. (ok, Nodes having multiple parents) - bool sec(false); - if (!destination->edgesToThis().empty()) - { - emit notification( - tr("The graph is acyclic, edge added as secondary edge.")); - sec = true; - } - Edge *edge = new Edge(source, destination); - source->addEdge(edge, true); - destination->addEdge(edge, false); - - edge->setColor(destination->color()); - edge->setWidth(destination->scale()*2 + 1); - - // The Edge is secondary, because the Node already has a parent - // (it is already a destination of another Edge) - edge->setSecondary(sec); - m_scene->addItem(edge); - - setActiveNode(destination); - emit contentChanged(); - } -} - -void GraphWidget::removeEdge(Node *source, Node *destination) -{ - if (!m_activeNode) - { - emit notification(tr("No active node.")); - return; - } - - if (!source->isConnected(destination)) - { - setActiveNode(destination); - emit notification(tr("There no edge between these two nodes.")); - } - else - { - source->deleteEdge(destination); - setActiveNode(destination); - emit contentChanged(); - } -} - -void GraphWidget::addFirstNode() -{ - Node *node = nodeFactory(); - node->setHtml( - QString("")); - - m_activeNode = m_nodeList.first(); - m_activeNode->setBorder(); -} - -void GraphWidget::removeAllNodes() -{ - foreach(Node *node, m_nodeList) - delete node; - - m_nodeList.clear(); - m_activeNode = 0; - m_hintNode = 0; -} - -void GraphWidget::setActiveNode(Node *node) -{ - if (m_activeNode!=0) - m_activeNode->setBorder(false); - - m_activeNode = node; - if (m_activeNode) - m_activeNode->setBorder(); -} - -// re-draw numbers -void GraphWidget::showNodeNumbers() -{ - if (m_hintNumber.isEmpty()) - { - showingAllNodeNumbers(true); - m_nodeList.first()->showNumber(0,true,true); - m_hintNode = m_nodeList.first(); - } - else - { - showingAllNodeNumbers(false); - showingNodeNumbersBeginWithNumber(m_hintNumber.toInt(), true); - } -} - -// show/hide numbers on all nodes -void GraphWidget::showingAllNodeNumbers(const bool &show) -{ - int i(0); - for (QList::const_iterator it = m_nodeList.begin(); - it != m_nodeList.end(); it++, - i++) - { - dynamic_cast(*it)->showNumber(i,show); - } -} - -// show nodes numbers where number begins with 'prefix' -void GraphWidget::showingNodeNumbersBeginWithNumber(const int &prefix, - const bool &show) -{ - int i(0); - int hit(0); - for (QList::const_iterator it = m_nodeList.begin(); - it != m_nodeList.end(); it++, i++) - { - // if nodenumber == 'prefix' the node is selected - if (i == prefix) - { - hit++; - dynamic_cast(*it)->showNumber(i,show,true); - m_hintNode = dynamic_cast(*it); - continue; - } - - // if 'i' starts with 'prefix' - if ((QString::number(i)).startsWith(QString::number(prefix))) - { - hit++; - dynamic_cast(*it)->showNumber(i,show); - } - } - if (hit==1) - { - selectNode(m_hintNode); - } - else if (hit == 0) - { - m_showingNodeNumbers = false; - showingAllNodeNumbers(false); - } -} - diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index d5549a2..aa8069f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -317,7 +317,7 @@ void MainWindow::setUpMainToolbar() m_editNode = new QAction(tr("Edit node (F2, dubclick)"), this); connect(m_editNode, SIGNAL(activated()), m_graphicsView, - SLOT(nodeEdited())); + SLOT(editNode())); m_scaleUpNode = new QAction(tr("ScaleUp Node (Ctrl +)"), this); connect(m_scaleUpNode, SIGNAL(activated()), m_graphicsView, @@ -358,7 +358,7 @@ void MainWindow::setUpMainToolbar() m_esc = new QAction(tr("Leave editing,\nedge eadd/remove (esc)"), this); connect(m_esc, SIGNAL(activated()), m_graphicsView, - SLOT(nodeLostFocus())); + SLOT(nodeLoseFocus())); m_hintMode = new QAction(tr("Hint mode (f)"), this); connect(m_hintMode, SIGNAL(activated()), m_graphicsView, diff --git a/src/node.cpp b/src/node.cpp index f872aa5..a21bbd0 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -15,14 +15,13 @@ const double Node::m_twoPi = Node::m_pi * 2.0; const QColor Node::m_gold(255,215,0); -Node::Node(GraphWidget *parent) : - m_graph(parent), - m_number(-1), - m_hasBorder(false), - m_numberIsSpecial(false), - m_color(m_gold), - m_textColor(0,0,0), - m_effect(new QGraphicsDropShadowEffect(this)) +Node::Node() + : m_number(-1) + , m_hasBorder(false) + , m_numberIsSpecial(false) + , m_color(m_gold) + , m_textColor(0,0,0) + , m_effect(new QGraphicsDropShadowEffect(this)) { setFlag(ItemIsMovable); setFlag(ItemSendsGeometryChanges); @@ -42,6 +41,8 @@ Node::~Node() Edge *tmp = element.edge; tmp->sourceNode()->removeEdgeFromList(tmp); tmp->destNode()->removeEdgeFromList(tmp); + + /// @bug crashes sometimes delete tmp; } }