diff --git a/include/graphwidget.h b/include/graphwidget.h index 2e59877..244710f 100644 --- a/include/graphwidget.h +++ b/include/graphwidget.h @@ -17,54 +17,64 @@ class GraphWidget : public QGraphicsView public: GraphWidget(MainWindow *parent = 0); - void setActiveNode(Node *node); + // node reports back it's state change void nodeSelected(Node *node); void nodeMoved(QGraphicsSceneMouseEvent *event); - QList edges() const; + // notify MainWindow: a node/edge has changed void contentChanged(const bool &changed = true); + // commands from MainWindow void newScene(); void closeScene(); bool readContentFromXmlFile(const QString &fileName); void writeContentToXmlFile(const QString &fileName); void writeContentToPngFile(const QString &fileName); - void insertPicture(const QString &picture); public slots: - void zoomIn(); - void zoomOut(); + // commands from MainWindow's MainToolBar's actions void insertNode(); void removeNode(); void editNode(); + void zoomIn(); + void zoomOut(); void nodeColor(); void nodeTextColor(); void addEdge(); void removeEdge(); - void hintMode(); void nodeLostFocus(); + void hintMode(); protected: + // key dispathcer of the whole program: long and pedant void keyPressEvent(QKeyEvent *event); void wheelEvent(QWheelEvent *event); void drawBackground(QPainter *painter, const QRectF &rect); private: + // zoom in/out of the view void scaleView(qreal scaleFactor); - void showNodeNumbers(); - void showingAllNodeNumbers(const bool &show = true); - void showingNodeNumbersBeginWithNumber(const int &number, - const bool &show = true); - bool numberStartsWithNumber(const int &number, const int &prefix); - qreal calculateBiggestAngle(Node *node); + + // functions on the edges + QList allEdges() const; void addEdge(Node *source, Node *destination); void removeEdge(Node* source, Node *destination); - void removeAllNodes(); + + // 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; @@ -80,7 +90,6 @@ private: QString m_fileName; static const QColor m_paper; - static const QColor m_gold; }; #endif // GRAPHWIDGET_H diff --git a/src/graphwidget.cpp b/src/graphwidget.cpp index 5c9b619..1e9935f 100644 --- a/src/graphwidget.cpp +++ b/src/graphwidget.cpp @@ -16,17 +16,16 @@ 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_contentChanged(false) +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_contentChanged(false) { m_scene = new QGraphicsScene(this); m_scene->setItemIndexMethod(QGraphicsScene::NoIndex); @@ -40,6 +39,51 @@ GraphWidget::GraphWidget(MainWindow *parent) : setMinimumSize(400, 400); } +void GraphWidget::nodeSelected(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::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 GraphWidget::contentChanged(const bool &changed) +{ + m_parent->contentChanged(changed); +} + void GraphWidget::newScene() { removeAllNodes(); @@ -55,6 +99,7 @@ void GraphWidget::closeScene() bool GraphWidget::readContentFromXmlFile(const QString &fileName) { + // open & parse XML file QDomDocument doc("QtMindMap"); QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) @@ -71,10 +116,9 @@ bool GraphWidget::readContentFromXmlFile(const QString &fileName) } file.close(); - removeAllNodes(); - QDomElement docElem = doc.documentElement(); + // add nodes QDomNodeList nodes = docElem.childNodes().item(0).childNodes(); for (unsigned int i = 0; i < nodes.length(); i++) { @@ -98,6 +142,7 @@ bool GraphWidget::readContentFromXmlFile(const QString &fileName) } } + // add edges QDomNodeList edges = docElem.childNodes().item(1).childNodes(); for (unsigned int i = 0; i < edges.length(); i++) { @@ -117,6 +162,7 @@ bool GraphWidget::readContentFromXmlFile(const QString &fileName) } } + // test the first node the active one m_activeNode = m_nodeList.first(); m_activeNode->setActive(); m_activeNode->setFocus(); @@ -127,6 +173,7 @@ bool GraphWidget::readContentFromXmlFile(const QString &fileName) void GraphWidget::writeContentToXmlFile(const QString &fileName) { + // create XML doc object QDomDocument doc("QtMindMap"); QDomElement root = doc.createElement("qtmindmap"); @@ -157,7 +204,7 @@ void GraphWidget::writeContentToXmlFile(const QString &fileName) //edges QDomElement edges_root = doc.createElement("edges"); root.appendChild(edges_root); - foreach(Edge *edge, edges()) + foreach(Edge *edge, allEdges()) { QDomElement cn = doc.createElement("edge"); cn.setAttribute( "source", @@ -173,17 +220,18 @@ void GraphWidget::writeContentToXmlFile(const QString &fileName) edges_root.appendChild(cn); } + // write XML doc object to file QFile file(fileName); if (!file.open(QIODevice::WriteOnly)) { m_parent->statusBarMsg(tr("Couldn't open file to write.")); return; } - QTextStream ts( &file ); ts << doc.toString(); file.close(); + // show a statusBar message to the user m_parent->statusBarMsg(tr("Saved.")); } @@ -196,6 +244,7 @@ void GraphWidget::writeContentToPngFile(const QString &fileName) 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); @@ -204,6 +253,7 @@ void GraphWidget::writeContentToPngFile(const QString &fileName) img.save(fileName); + // show a statusBar message to the user m_parent->statusBarMsg(tr("MindMap exported as ") + fileName); } @@ -218,193 +268,105 @@ void GraphWidget::insertPicture(const QString &picture) m_activeNode->insertPicture(picture); } -void GraphWidget::keyPressEvent(QKeyEvent *event) +void GraphWidget::insertNode() { - if (event->key() == Qt::Key_Escape) - { - nodeLostFocus(); - return; - } - - if (m_editingNode) + if (!m_activeNode) { - m_activeNode->keyPressEvent(event); + m_parent->statusBarMsg(tr("No active node.")); return; } - switch (event->key()) - { - case Qt::Key_Up: - case Qt::Key_Down: - case Qt::Key_Left: - case Qt::Key_Right: - - if (!m_activeNode) - { - m_parent->statusBarMsg(tr("No active node.")); - return; - } - - if (event->modifiers() & Qt::ControlModifier) - { - if (event->modifiers() & Qt::ShiftModifier) - { - QList nodeList = m_activeNode->subtree(); - foreach(Node *node, nodeList) - { - if (event->key() == Qt::Key_Up) 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_Right) node->moveBy(20, 0); - contentChanged(); - } - } - else - { - if (event->key() == Qt::Key_Up) m_activeNode->moveBy(0, -20); - else if (event->key() == Qt::Key_Down) 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(); - } - } - else // move scene - { - QGraphicsView::keyPressEvent(event); - } - break; - - case Qt::Key_Plus: - - zoomIn(); - break; - - case Qt::Key_Minus: - - zoomOut(); - break; - - // Hint mode: select a node vimperator style - case Qt::Key_F: - - hintMode(); - break; + // get the biggest angle between the edges of the Node. + double angle(m_activeNode->calculateBiggestAngle()); - case Qt::Key_Insert: - insertNode(); + // let the distance between the current and new Node be 100 pixels + qreal length(100); - break; + QPointF pos(length * cos(angle), length * sin(angle)); - // used in node selection mode, to select node with numbers/enter - case Qt::Key_0: - case Qt::Key_1: - case Qt::Key_2: - case Qt::Key_3: - case Qt::Key_4: - case Qt::Key_5: - case Qt::Key_6: - case Qt::Key_7: - case Qt::Key_8: - case Qt::Key_9: - if (!m_showingNodeNumbers) - break; + // add a new node which inherits the color and textColor + Node *node = new Node(this); + node->setColor(m_activeNode->color()); + node->setTextColor(m_activeNode->textColor()); + node->setHtml(QString("")); + m_scene->addItem(node); + node->setPos(m_activeNode->sceneBoundingRect().center() + + pos - + node->boundingRect().center()); + m_nodeList.append(node); - m_hintNumber.append(QString::number(event->key()-48)); - showingAllNodeNumbers(false); - showingNodeNumbersBeginWithNumber(m_hintNumber.toInt(), true); + addEdge(m_activeNode, node); - break; + // set it the active Node and editable, so the user can edit it at once + setActiveNode(node); + editNode(); - // delete one letter back in node selection - case Qt::Key_Backspace: - if (!m_showingNodeNumbers && m_hintNumber.isEmpty()) - break; + contentChanged(); - m_hintNumber.remove(m_hintNumber.length()-1,1); + // it we are in hint mode, the numbers shall be re-calculated + if (m_showingNodeNumbers) showNodeNumbers(); - break; - - // in node selection select node if nudenum = enterednum - case Qt::Key_Return: - case Qt::Key_Enter: - - if (m_hintNode && m_showingNodeNumbers) - nodeSelected(m_hintNode); - - break; - - case Qt::Key_F2: - - editNode(); - break; - - case Qt::Key_Delete: - - removeNode(); - break; - - case Qt::Key_A: - - addEdge(); - break; - - case Qt::Key_D: - - removeEdge(); - break; +} - case Qt::Key_C: +void GraphWidget::removeNode() +{ + if (!m_activeNode) + { + m_parent->statusBarMsg(tr("No active node.")); + return; + } - nodeColor(); - break; + if (m_activeNode == m_nodeList.first()) + { + m_parent->statusBarMsg(tr("Base node cannot be deleted.")); + return; + } - case Qt::Key_T: + // 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); + } - nodeTextColor(); - break; + foreach(Node *node, nodeList) + { + if (m_hintNode==node) + m_hintNode=0; - default: - QGraphicsView::keyPressEvent(event); + m_nodeList.removeAll(node); + delete node; } -} - + m_activeNode = 0; + contentChanged(); -void GraphWidget::wheelEvent(QWheelEvent *event) -{ - event->delta() > 0 ? - zoomIn() : - zoomOut(); + // it we are in hint mode, the numbers shall be re-calculated + if (m_showingNodeNumbers) + showNodeNumbers(); } -void GraphWidget::drawBackground(QPainter *painter, const QRectF &rect) +void GraphWidget::editNode() { - Q_UNUSED(rect); - - painter->fillRect(m_scene->sceneRect(), GraphWidget::m_paper); - painter->setBrush(Qt::NoBrush); - painter->drawRect(m_scene->sceneRect()); -} + qDebug() << __PRETTY_FUNCTION__; -void GraphWidget::scaleView(qreal scaleFactor) -{ - qreal factor = transform().scale(scaleFactor, scaleFactor). - mapRect(QRectF(0, 0, 1, 1)).width(); - if (factor < 0.2 || factor > 10) return; + if (!m_activeNode) + { + m_parent->statusBarMsg(tr("No active node.")); + return; + } - scale(scaleFactor, scaleFactor); + m_editingNode = true; + m_activeNode->setEditable(); + m_scene->setFocusItem(m_activeNode); } -void GraphWidget::setActiveNode(Node *node) -{ - if (m_activeNode!=0) - m_activeNode->setActive(false); - - m_activeNode = node; - m_activeNode->setActive(); -} - -void GraphWidget::zoomIn() +void GraphWidget::zoomIn() { if (QApplication::keyboardModifiers() & Qt::ControlModifier) { @@ -414,6 +376,7 @@ void GraphWidget::zoomIn() return; } + // Scale up just the active Node or it's subtree too? if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { QList nodeList = m_activeNode->subtree(); @@ -425,7 +388,7 @@ void GraphWidget::zoomIn() m_activeNode->setScale(qreal(1.2),sceneRect()); } } - else + else // zoom in the view { scaleView(qreal(1.2)); } @@ -441,6 +404,7 @@ void GraphWidget::zoomOut() return; } + // Scale down just the active Node or it's subtree too? if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { QList nodeList = m_activeNode->subtree(); @@ -452,100 +416,12 @@ void GraphWidget::zoomOut() m_activeNode->setScale(qreal(1 / 1.2),sceneRect()); } } - else + else // zoom out of the view { scaleView(qreal(1 / 1.2)); } } -void GraphWidget::insertNode() -{ - if (!m_activeNode) - { - m_parent->statusBarMsg(tr("No active node.")); - return; - } - - double angle(m_activeNode->calculateBiggestAngle()); - - qreal length(100); - - QPointF pos(length * cos(angle), length * sin(angle)); - - Node *node = new Node(this); - node->setColor(m_activeNode->color()); - node->setTextColor(m_activeNode->textColor()); - node->setHtml(QString("")); - m_scene->addItem(node); - node->setPos(m_activeNode->sceneBoundingRect().center() + - pos - - node->boundingRect().center()); - m_nodeList.append(node); - - addEdge(m_activeNode, node); - - setActiveNode(node); - editNode(); - - contentChanged(); - if (m_showingNodeNumbers) - showNodeNumbers(); -} - -void GraphWidget::removeNode() -{ - if (!m_activeNode) - { - m_parent->statusBarMsg(tr("No active node.")); - return; - } - - if (m_activeNode == m_nodeList.first()) - { - m_parent->statusBarMsg(tr("Base node cannot be deleted.")); - return; - } - - 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; - contentChanged(); - - if (m_showingNodeNumbers) - showNodeNumbers(); -} - -void GraphWidget::editNode() -{ - if (!m_activeNode) - { - m_parent->statusBarMsg(tr("No active node.")); - return; - } - - m_editingNode = true; - m_activeNode->setEditable(); - m_scene->setFocusItem(m_activeNode); -} - void GraphWidget::nodeColor() { if (!m_activeNode) @@ -554,6 +430,7 @@ void GraphWidget::nodeColor() return; } + // Set color of the active Node or of it's subtree too? QList nodeList; if (QApplication::keyboardModifiers() & Qt::ControlModifier && QApplication::keyboardModifiers() & Qt::ShiftModifier) @@ -565,6 +442,7 @@ void GraphWidget::nodeColor() 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()); @@ -588,6 +466,7 @@ void GraphWidget::nodeTextColor() return; } + // Set textcolor of the active Node or of it's subtree too? QList nodeList; if (QApplication::keyboardModifiers() & Qt::ControlModifier && QApplication::keyboardModifiers() & Qt::ShiftModifier) @@ -599,6 +478,7 @@ void GraphWidget::nodeTextColor() 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()); @@ -622,8 +502,38 @@ void GraphWidget::removeEdge() m_edgeDeleting = true; } +void GraphWidget::nodeLostFocus() +{ + qDebug() << __PRETTY_FUNCTION__; + + if (m_editingNode) + { + m_editingNode = false; + m_activeNode->setEditable(false); + m_activeNode->update(); + } + else if (m_edgeAdding) + { + m_edgeAdding = false; + m_parent->statusBarMsg(tr("Edge adding cancelled.")); + } + else if (m_edgeDeleting) + { + m_edgeDeleting = false; + m_parent->statusBarMsg(tr("Edge deleting cancelled.")); + } + else if(m_showingNodeNumbers) + { + m_hintNumber.clear(); + showingAllNodeNumbers(false); + m_showingNodeNumbers = false; + } +} + void GraphWidget::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) { @@ -635,120 +545,216 @@ void GraphWidget::hintMode() showNodeNumbers(); } -void GraphWidget::showingAllNodeNumbers(const bool &show) +// All key event arrives here. +// MainWindow::keyPressEvent passes all of them here, except +// Ctrl + m (show/hide mainToolBar) and Ctrl + i (show/hide statusIconsToolbar) +void GraphWidget::keyPressEvent(QKeyEvent *event) { - int i =0; - for (QList::const_iterator it = m_nodeList.begin(); - it != m_nodeList.end(); it++, i++) - dynamic_cast(*it)->showNumber(i,show); -} + // Node lost focus: leaving edge adding/deleting or Node editing. + if (event->key() == Qt::Key_Escape) + { + nodeLostFocus(); + return; + } -void GraphWidget::showingNodeNumbersBeginWithNumber(const int &number, - const bool &show) -{ - int i(0); - int hit(0); - for (QList::const_iterator it = m_nodeList.begin(); - it != m_nodeList.end(); it++, i++) + // During Node editing mode, pass every key to the Node's keyPressEvent. + if (m_editingNode) + { + m_activeNode->keyPressEvent(event); + return; + } + + switch (event->key()) { - if (i == number) + + // move + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_Left: + case Qt::Key_Right: + + if (!m_activeNode) { - hit++; - dynamic_cast(*it)->showNumber(i,show,true); - m_hintNode = dynamic_cast(*it); - continue; + m_parent->statusBarMsg(tr("No active node.")); + return; } - if (numberStartsWithNumber(i, number)) + + if (event->modifiers() & Qt::ControlModifier) { - hit++; - dynamic_cast(*it)->showNumber(i,show); + // Move whole subtree of active Node. + if (event->modifiers() & Qt::ShiftModifier) + { + QList nodeList = m_activeNode->subtree(); + foreach(Node *node, nodeList) + { + if (event->key() == Qt::Key_Up) 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_Right) node->moveBy(20, 0); + contentChanged(); + } + } + else // Move just the active Node. + { + if (event->key() == Qt::Key_Up) m_activeNode->moveBy(0, -20); + else if (event->key() == Qt::Key_Down) 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(); + } } - } - if (hit==1) - { - nodeSelected(m_hintNode); - } - else if (hit == 0) - { - m_showingNodeNumbers = false; + else // Move scene. + { + QGraphicsView::keyPressEvent(event); + } + break; + + case Qt::Key_Plus: + + zoomIn(); + break; + + case Qt::Key_Minus: + + zoomOut(); + break; + + // Hint mode: select a Node vimperator-style. + case Qt::Key_F: + + hintMode(); + break; + + case Qt::Key_Insert: + insertNode(); + + break; + + // Used in hint mode, to select node with numbers/backspace/enter. + case Qt::Key_0: + case Qt::Key_1: + case Qt::Key_2: + case Qt::Key_3: + case Qt::Key_4: + case Qt::Key_5: + case Qt::Key_6: + case Qt::Key_7: + case Qt::Key_8: + case Qt::Key_9: + if (!m_showingNodeNumbers) + break; + + m_hintNumber.append(QString::number(event->key()-48)); showingAllNodeNumbers(false); + showingNodeNumbersBeginWithNumber(m_hintNumber.toInt(), true); + + break; + + // Delete one letter back in hint mode. + case Qt::Key_Backspace: + if (!m_showingNodeNumbers && m_hintNumber.isEmpty()) + break; + + m_hintNumber.remove(m_hintNumber.length()-1,1); + showNodeNumbers(); + break; + + // In hint mode select the suggested Node. + case Qt::Key_Return: + case Qt::Key_Enter: + + if (m_hintNode && m_showingNodeNumbers) + nodeSelected(m_hintNode); + + break; + + case Qt::Key_F2: + + editNode(); + break; + + case Qt::Key_Delete: + + removeNode(); + break; + + case Qt::Key_A: + + addEdge(); + break; + + case Qt::Key_D: + + removeEdge(); + break; + + case Qt::Key_C: + + nodeColor(); + break; + + case Qt::Key_T: + + nodeTextColor(); + break; + + default: + QGraphicsView::keyPressEvent(event); } } -bool GraphWidget::numberStartsWithNumber(const int &number, const int &prefix) +void GraphWidget::wheelEvent(QWheelEvent *event) { - return (QString::number(number)).startsWith(QString::number(prefix)); + event->delta() > 0 ? + zoomIn() : + zoomOut(); } -void GraphWidget::nodeSelected(Node *node) +void GraphWidget::drawBackground(QPainter *painter, const QRectF &rect) { - showingAllNodeNumbers(false); - m_showingNodeNumbers = false; - - node->setEditable(false); - m_editingNode = false; + Q_UNUSED(rect); - if (m_edgeAdding) - { - addEdge(m_activeNode, node); - m_edgeAdding = false; - } - else if (m_edgeDeleting) - { - removeEdge(m_activeNode, node); - m_edgeDeleting = false; - } - else - { - setActiveNode(node); - } + painter->fillRect(m_scene->sceneRect(), GraphWidget::m_paper); + painter->setBrush(Qt::NoBrush); + painter->drawRect(m_scene->sceneRect()); } -void GraphWidget::nodeMoved(QGraphicsSceneMouseEvent *event) +void GraphWidget::scaleView(qreal scaleFactor) { - QList nodeList; - if (event->modifiers() & Qt::ControlModifier && - event->modifiers() & Qt::ShiftModifier) - { - nodeList = m_activeNode->subtree(); - } - else - { - nodeList.push_back(m_activeNode); - } + qreal factor = transform().scale(scaleFactor, scaleFactor). + mapRect(QRectF(0, 0, 1, 1)).width(); - foreach(Node *node, nodeList) - node->setPos(node->pos() + event->scenePos() - event->lastScenePos()); + // don't allow to scale up/down too much + if (factor < 0.2 || factor > 10) + return; + + scale(scaleFactor, scaleFactor); } -void GraphWidget::nodeLostFocus() +QList GraphWidget::allEdges() const { - if (m_editingNode) - { - m_editingNode = false; - m_activeNode->setEditable(false); - m_activeNode->update(); - } - else if (m_edgeAdding) - { - m_edgeAdding = false; - m_parent->statusBarMsg(tr("Edge adding cancelled.")); - } - else if (m_edgeDeleting) - { - m_edgeDeleting = false; - m_parent->statusBarMsg(tr("Edge deleting cancelled.")); - } - else if(m_showingNodeNumbers) - { - m_hintNumber.clear(); - showingAllNodeNumbers(false); - m_showingNodeNumbers = false; - } + 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) + { + m_parent->statusBarMsg(tr("No active node.")); + return; + } + if (destination == m_nodeList.first()) { m_parent->statusBarMsg( @@ -763,6 +769,7 @@ void GraphWidget::addEdge(Node *source, Node *destination) } else { + // aviod the graph beeing acyclic. (ok, Nodes having multiple parents) bool sec(false); if (!destination->edgesToThis().empty()) { @@ -773,6 +780,9 @@ void GraphWidget::addEdge(Node *source, Node *destination) Edge *edge = new Edge(source, destination); 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); @@ -782,6 +792,12 @@ void GraphWidget::addEdge(Node *source, Node *destination) void GraphWidget::removeEdge(Node *source, Node *destination) { + if (!m_activeNode) + { + m_parent->statusBarMsg(tr("No active node.")); + return; + } + if (!source->isConnected(destination)) { m_parent->statusBarMsg(tr("There no edge between these two nodes.")); @@ -793,6 +809,39 @@ void GraphWidget::removeEdge(Node *source, Node *destination) } } +void GraphWidget::addFirstNode() +{ + Node *node = new Node(this); + node->setHtml( + QString("")); + m_scene->addItem(node); + + m_nodeList.append(node); + + m_activeNode = m_nodeList.first(); + m_activeNode->setActive(); +} + +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->setActive(false); + + m_activeNode = node; + m_activeNode->setActive(); +} + +// re-draw numbers void GraphWidget::showNodeNumbers() { if (m_hintNumber.isEmpty()) @@ -808,38 +857,51 @@ void GraphWidget::showNodeNumbers() } } -void GraphWidget::removeAllNodes() -{ - foreach(Node *node, m_nodeList) delete node; - m_nodeList.clear(); - m_activeNode = 0; - m_hintNode = 0; -} - -void GraphWidget::addFirstNode() +// show/hide numbers on all nodes +void GraphWidget::showingAllNodeNumbers(const bool &show) { - Node *node = new Node(this); - node->setHtml( - QString("")); - m_scene->addItem(node); - - m_nodeList.append(node); - - m_activeNode = m_nodeList.first(); - m_activeNode->setActive(); + int i(0); + for (QList::const_iterator it = m_nodeList.begin(); + it != m_nodeList.end(); it++, + i++) + { + dynamic_cast(*it)->showNumber(i,show); + } } -QList GraphWidget::edges() const +// show nodes numbers where number begins with 'prefix' +void GraphWidget::showingNodeNumbersBeginWithNumber(const int &prefix, + const bool &show) { - QList list; - - foreach(Node * node, m_nodeList) - list.append(node->edgesFrom(false)); + 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; + } - return list; + // if 'i' starts with 'prefix' + if ((QString::number(i)).startsWith(QString::number(prefix))) + { + hit++; + dynamic_cast(*it)->showNumber(i,show); + } + } + if (hit==1) + { + nodeSelected(m_hintNode); + } + else if (hit == 0) + { + m_showingNodeNumbers = false; + showingAllNodeNumbers(false); + } } -void GraphWidget::contentChanged(const bool &changed) -{ - m_parent->contentChanged(changed); -} diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 42666ad..be56e75 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -352,41 +352,68 @@ void MainWindow::setUpMainToolbar() /// @bug or a feature? no underline here m_addNode = new QAction(tr("Add node (ins)"), this); - connect(m_addNode, SIGNAL(activated()), m_graphicsView, SLOT(insertNode())); + connect(m_addNode, SIGNAL(activated()), m_graphicsView, + SLOT(insertNode())); + m_delNode = new QAction(tr("Del node (del)"), this); - connect(m_delNode, SIGNAL(activated()), m_graphicsView, SLOT(removeNode())); + connect(m_delNode, SIGNAL(activated()), m_graphicsView, + SLOT(removeNode())); + m_editNode = new QAction(tr("Edit node (F2, dubclick)"), this); - connect(m_editNode, SIGNAL(activated()), m_graphicsView, SLOT(editNode())); + connect(m_editNode, SIGNAL(activated()), m_graphicsView, + SLOT(editNode())); /// @todo pass ctrl m_scaleUpNode = new QAction(tr("ScaleUp Node (Ctrl +)"), this); m_scaleDownNode = new QAction(tr("ScaleDown Node (Ctrl -)"), this); m_nodeColor = new QAction(tr("Node color (c)"), this); - connect(m_nodeColor, SIGNAL(activated()), m_graphicsView, SLOT(nodeColor())); + connect(m_nodeColor, SIGNAL(activated()), m_graphicsView, + SLOT(nodeColor())); + m_nodeTextColor = new QAction(tr("Node textcolor (t)"), this); - connect(m_nodeTextColor, SIGNAL(activated()), m_graphicsView, SLOT(nodeTextColor())); + connect(m_nodeTextColor, SIGNAL(activated()), m_graphicsView, + SLOT(nodeTextColor())); + m_addEdge = new QAction(tr("Add edge (a)"), this); - connect(m_addEdge, SIGNAL(activated()), m_graphicsView, SLOT(addEdge())); + connect(m_addEdge, SIGNAL(activated()), m_graphicsView, + SLOT(addEdge())); + m_delEdge = new QAction(tr("Del edge (d)"), this); - connect(m_delEdge, SIGNAL(activated()), m_graphicsView, SLOT(removeEdge())); + connect(m_delEdge, SIGNAL(activated()), m_graphicsView, + SLOT(removeEdge())); + m_moveNode = new QAction(tr("Move node\n(Ctrl cursor, drag)"), this); m_moveNode->setDisabled(true); + m_subtree = new QAction(tr("Change on wholesubtree\n(Ctrl shift)"), this); m_subtree->setDisabled(true); + m_zoomIn = new QAction(tr("Zoom in (+, scrollup)"), this); - connect(m_zoomIn, SIGNAL(activated()), m_graphicsView, SLOT(zoomIn())); + connect(m_zoomIn, SIGNAL(activated()), m_graphicsView, + SLOT(zoomIn())); + m_zoomOut = new QAction(tr("Zoom out (-, scrolldown)"), this); - connect(m_zoomOut, SIGNAL(activated()), m_graphicsView, SLOT(zoomOut())); + connect(m_zoomOut, SIGNAL(activated()), m_graphicsView, + SLOT(zoomOut())); + m_esc = new QAction(tr("Leave editing,\nedge eadd/remove (esc)"), this); - connect(m_esc, SIGNAL(activated()), m_graphicsView, SLOT(nodeLostFocus())); + connect(m_esc, SIGNAL(activated()), m_graphicsView, + SLOT(nodeLostFocus())); + m_hintMode = new QAction(tr("Hint mode (f)"), this); - connect(m_hintMode, SIGNAL(activated()), m_graphicsView, SLOT(hintMode())); + connect(m_hintMode, SIGNAL(activated()), m_graphicsView, + SLOT(hintMode())); + m_showMainToolbar = new QAction(tr("Show main toolbar\n(Ctrl m)"), this); m_showMainToolbar->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_M)); - connect(m_showMainToolbar, SIGNAL(activated()), this, SLOT(showMainToolbar())); - m_showStatusIconToolbar = new QAction(tr("Insert status icons\n(Ctrl i)"), this); - connect(m_showStatusIconToolbar, SIGNAL(activated()), this, SLOT(showStatusIconToolbar())); + connect(m_showMainToolbar, SIGNAL(activated()), this, + SLOT(showMainToolbar())); + + m_showStatusIconToolbar = new QAction(tr("Insert status icons\n(Ctrl i)"), + this); + connect(m_showStatusIconToolbar, SIGNAL(activated()), this, + SLOT(showStatusIconToolbar())); m_ui->mainToolBar->addAction(m_addNode); m_ui->mainToolBar->addAction(m_delNode); diff --git a/src/node.cpp b/src/node.cpp index a0dbf60..080a0ef 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -99,11 +99,13 @@ void Node::setActive(const bool &active) void Node::setEditable(const bool &editable) { - setTextInteractionFlags( - editable ? - Qt::TextEditable : - Qt::NoTextInteraction); + if (!editable) + { + setTextInteractionFlags(Qt::NoTextInteraction); + return; + } + setTextInteractionFlags(Qt::TextEditable); QTextCursor c = textCursor(); c.setPosition(c.document()->toPlainText().length()); setTextCursor(c); @@ -479,7 +481,10 @@ QPainterPath Node::shape () const void Node::focusOutEvent(QFocusEvent *event) { + qDebug() << __PRETTY_FUNCTION__; + Q_UNUSED(event); + setEditable(false); m_graph->nodeLostFocus(); }