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.

master
Denes Matetelki 14 years ago
parent f7f2d3145a
commit c16240f8e4

2
.gitignore vendored

@ -5,5 +5,5 @@ Makefile
moc_* moc_*
qrc_* qrc_*
qtmindmap qtmindmap
*.pro.user *.pro.user*
html/ html/

@ -0,0 +1,94 @@
#ifndef GRAPHLOGIC_H
#define GRAPHLOGIC_H
#include <QObject>
#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<Edge *> 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<Node *> 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

@ -6,9 +6,10 @@
#include <QKeyEvent> #include <QKeyEvent>
#include <QGraphicsSceneMouseEvent> #include <QGraphicsSceneMouseEvent>
#include "node.h" #include "graphlogic.h"
class MainWindow; class MainWindow;
class GraphLogic;
class GraphWidget : public QGraphicsView class GraphWidget : public QGraphicsView
{ {
@ -24,11 +25,14 @@ public:
void writeContentToXmlFile(const QString &fileName); void writeContentToXmlFile(const QString &fileName);
void writeContentToPngFile(const QString &fileName); void writeContentToPngFile(const QString &fileName);
static const QColor m_paper;
public slots: public slots:
// commands from MainWindow's MainToolBar's actions // commands from MainWindow's MainToolBar's actions
void insertNode(QKeyEvent *event = 0); void insertNode(QKeyEvent *event = 0);
void removeNode(QKeyEvent *event = 0); void removeNode(QKeyEvent *event = 0);
void editNode(QKeyEvent *event = 0);
void zoomIn(QKeyEvent *event = 0); void zoomIn(QKeyEvent *event = 0);
void zoomOut(QKeyEvent *event = 0); void zoomOut(QKeyEvent *event = 0);
void scaleUp(QKeyEvent *event = 0); void scaleUp(QKeyEvent *event = 0);
@ -37,17 +41,14 @@ public slots:
void nodeTextColor(QKeyEvent *event = 0); void nodeTextColor(QKeyEvent *event = 0);
void addEdge(QKeyEvent *event = 0); void addEdge(QKeyEvent *event = 0);
void removeEdge(QKeyEvent *event = 0); void removeEdge(QKeyEvent *event = 0);
void nodeLoseFocus(QKeyEvent *event = 0);
void hintMode(QKeyEvent *event = 0); void hintMode(QKeyEvent *event = 0);
// bundled signals from statusIconsToolBar // bundled signals from statusIconsToolBar
void insertPicture(const QString &picture); void insertPicture(const QString &picture);
// node reports back it's state change void contentChangedFromLogic();
void nodeChanged(); void notificationFromLogic(const QString &msg);
void nodeSelected();
void nodeEdited(QKeyEvent *event = 0);
void nodeMoved(QGraphicsSceneMouseEvent *event);
void nodeLostFocus();
signals: signals:
@ -63,10 +64,6 @@ protected:
private: private:
Node * nodeFactory();
void selectNode(Node *node);
// keymap commands // keymap commands
void moveUp(QKeyEvent *event); void moveUp(QKeyEvent *event);
void moveDown(QKeyEvent *event); void moveDown(QKeyEvent *event);
@ -78,40 +75,15 @@ private:
void delNumber(QKeyEvent *event); void delNumber(QKeyEvent *event);
void applyNumber(QKeyEvent *event); void applyNumber(QKeyEvent *event);
void move(const int &x, const int &y, QKeyEvent *event);
void scaleView(qreal scaleFactor); void scaleView(qreal scaleFactor);
// functions on the edges
QList<Edge *> 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<Node *> m_nodeList;
MainWindow *m_parent; MainWindow *m_parent;
Node *m_activeNode;
QGraphicsScene *m_scene; QGraphicsScene *m_scene;
bool m_showingNodeNumbers; GraphLogic *m_graphlogic;
QString m_hintNumber;
Node *m_hintNode;
bool m_editingNode;
bool m_edgeAdding;
bool m_edgeDeleting;
std::map<int, void(GraphWidget::*)(QKeyEvent *)> m_memberMap; std::map<int, void(GraphWidget::*)(QKeyEvent *)> m_memberMap;
static const QColor m_paper;
}; };
#endif // GRAPHWIDGET_H #endif // GRAPHWIDGET_H

@ -16,7 +16,7 @@ class Node : public QGraphicsTextItem
public: public:
Node(GraphWidget *graphWidget = 0); Node();
~Node(); ~Node();
// add/remove edges // add/remove edges
@ -91,7 +91,7 @@ private:
}; };
QList<EdgeElement> m_edgeList; QList<EdgeElement> m_edgeList;
GraphWidget *m_graph; // GraphWidget *m_graph;
int m_number; int m_number;
bool m_hasBorder; bool m_hasBorder;
bool m_numberIsSpecial; bool m_numberIsSpecial;

@ -17,18 +17,22 @@ TEMPLATE = app
SOURCES += src/main.cpp \ SOURCES += src/main.cpp \
src/mainwindow.cpp \ src/mainwindow.cpp \
src/graphwidget.cpp \ src/graphwidget.cpp \
src/graphlogic.cpp \
src/node.cpp \ src/node.cpp \
src/edge.cpp \ src/edge.cpp \
src/systemtray.cpp \ src/systemtray.cpp \
src/argumentparser.cpp src/argumentparser.cpp
HEADERS += include/mainwindow.h \ HEADERS += include/mainwindow.h \
include/graphwidget.h \ include/graphwidget.h \
include/graphlogic.h \
include/node.h \ include/node.h \
include/edge.h \ include/edge.h \
include/systemtray.h \ include/systemtray.h \
include/argumentparser.h include/argumentparser.h
FORMS += ui/mainwindow.ui FORMS += ui/mainwindow.ui
RESOURCES += images/qtmindmap.qrc RESOURCES += images/qtmindmap.qrc

@ -0,0 +1,784 @@
#include "include/graphwidget.h"
#include <QtXml>
#include <QColorDialog>
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("<img src=:/qtmindmap.svg width=50 height=50></img>"));
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 <Node *> 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 <Node *> 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 <Node *> 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 <Node *> 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 <Node *> 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 <Node *> 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<Node*>(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 <Node *> 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<Edge *> GraphLogic::allEdges() const
{
QList<Edge *> 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<Node *>::const_iterator it = m_nodeList.begin();
it != m_nodeList.end(); it++,
i++)
{
dynamic_cast<Node*>(*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<Node *>::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<Node*>(*it)->showNumber(i,show,true);
m_hintNode = dynamic_cast<Node*>(*it);
continue;
}
// if 'i' starts with 'prefix'
if ((QString::number(i)).startsWith(QString::number(prefix)))
{
hit++;
dynamic_cast<Node*>(*it)->showNumber(i,show);
}
}
if (hit==1)
{
selectNode(m_hintNode);
}
else if (hit == 0)
{
m_showingNodeNumbers = false;
showingAllNodeNumbers(false);
}
}

@ -4,8 +4,6 @@
#include <QStatusBar> #include <QStatusBar>
#include <QMessageBox> #include <QMessageBox>
#include <QFileDialog> #include <QFileDialog>
#include <QtXml>
#include <QColorDialog>
#include <QApplication> #include <QApplication>
#include "include/node.h" #include "include/node.h"
@ -19,12 +17,6 @@ const QColor GraphWidget::m_paper(255,255,153);
GraphWidget::GraphWidget(MainWindow *parent) GraphWidget::GraphWidget(MainWindow *parent)
: QGraphicsView(parent) : QGraphicsView(parent)
, m_parent(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 = new QGraphicsScene(this);
m_scene->setItemIndexMethod(QGraphicsScene::NoIndex); m_scene->setItemIndexMethod(QGraphicsScene::NoIndex);
@ -81,12 +73,12 @@ GraphWidget::GraphWidget(MainWindow *parent)
(Qt::Key_0, &GraphWidget::appendNumber)); (Qt::Key_0, &GraphWidget::appendNumber));
m_memberMap.insert(std::pair<int, void(GraphWidget::*)(QKeyEvent *)> m_memberMap.insert(std::pair<int, void(GraphWidget::*)(QKeyEvent *)>
(Qt::Key_F2, &GraphWidget::nodeEdited)); (Qt::Key_F2, &GraphWidget::editNode));
m_memberMap.insert(std::pair<int, void(GraphWidget::*)(QKeyEvent *)> m_memberMap.insert(std::pair<int, void(GraphWidget::*)(QKeyEvent *)>
(Qt::Key_Backspace, &GraphWidget::delNumber)); (Qt::Key_Backspace, &GraphWidget::delNumber));
m_memberMap.insert(std::pair<int, void(GraphWidget::*)(QKeyEvent *)> m_memberMap.insert(std::pair<int, void(GraphWidget::*)(QKeyEvent *)>
(Qt::Key_Return, &GraphWidget::delNumber)); (Qt::Key_Return, &GraphWidget::applyNumber));
m_memberMap.insert(std::pair<int, void(GraphWidget::*)(QKeyEvent *)> m_memberMap.insert(std::pair<int, void(GraphWidget::*)(QKeyEvent *)>
(Qt::Key_Enter, &GraphWidget::applyNumber)); (Qt::Key_Enter, &GraphWidget::applyNumber));
m_memberMap.insert(std::pair<int, void(GraphWidget::*)(QKeyEvent *)> m_memberMap.insert(std::pair<int, void(GraphWidget::*)(QKeyEvent *)>
@ -100,320 +92,62 @@ GraphWidget::GraphWidget(MainWindow *parent)
(Qt::Key_C, &GraphWidget::nodeColor)); (Qt::Key_C, &GraphWidget::nodeColor));
m_memberMap.insert(std::pair<int, void(GraphWidget::*)(QKeyEvent *)> m_memberMap.insert(std::pair<int, void(GraphWidget::*)(QKeyEvent *)>
(Qt::Key_T, &GraphWidget::nodeTextColor)); (Qt::Key_T, &GraphWidget::nodeTextColor));
}
void GraphWidget::nodeSelected() m_graphlogic = new GraphLogic(this);
{ connect(m_graphlogic, SIGNAL(contentChanged()),
// if node == 0 then nodeSelected invoked after a signal from a Node this, SLOT(contentChangedFromLogic()));
selectNode(dynamic_cast<Node*>(QObject::sender()));
}
void GraphWidget::nodeMoved(QGraphicsSceneMouseEvent *event)
{
// move just the active Node, or it's subtree too?
QList <Node *> nodeList;
if (event->modifiers() & Qt::ControlModifier &&
event->modifiers() & Qt::ShiftModifier)
{
nodeList = m_activeNode->subtree();
}
else
{
nodeList.push_back(m_activeNode);
}
foreach(Node *node, nodeList) connect(m_graphlogic, SIGNAL(notification(QString)),
node->setPos(node->pos() + event->scenePos() - event->lastScenePos()); this, SLOT(notificationFromLogic(QString)));
}
void GraphWidget::nodeChanged()
{
emit contentChanged();
} }
void GraphWidget::newScene() void GraphWidget::newScene()
{ {
removeAllNodes(); m_graphlogic->removeAllNodes();
addFirstNode(); m_graphlogic->addFirstNode();
this->show(); this->show();
} }
void GraphWidget::closeScene() void GraphWidget::closeScene()
{ {
removeAllNodes(); m_graphlogic->removeAllNodes();
this->hide(); this->hide();
} }
bool GraphWidget::readContentFromXmlFile(const QString &fileName) bool GraphWidget::readContentFromXmlFile(const QString &fileName)
{ {
// open & parse XML file return m_graphlogic->readContentFromXmlFile(fileName);
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;
} }
void GraphWidget::writeContentToXmlFile(const QString &fileName) void GraphWidget::writeContentToXmlFile(const QString &fileName)
{ {
// create XML doc object m_graphlogic->writeContentToXmlFile(fileName);
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 GraphWidget::writeContentToPngFile(const QString &fileName) void GraphWidget::writeContentToPngFile(const QString &fileName)
{ {
QImage img(m_scene->sceneRect().width(), m_graphlogic->writeContentToPngFile(fileName);
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);
} }
void GraphWidget::insertNode(QKeyEvent *event) void GraphWidget::insertNode(QKeyEvent *event)
{ {
Q_UNUSED(event) Q_UNUSED(event)
nodeLostFocus(); m_graphlogic->insertNode();
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();
} }
void GraphWidget::removeNode(QKeyEvent *event) void GraphWidget::removeNode(QKeyEvent *event)
{ {
m_graphlogic->removeNode(
QApplication::keyboardModifiers() & Qt::ControlModifier &&
QApplication::keyboardModifiers() & Qt::ShiftModifier);
Q_UNUSED(event) 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 <Node *> 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) Q_UNUSED(event)
if (!m_activeNode) m_graphlogic->nodeEdited();
{
emit notification(tr("No active node."));
return;
}
m_editingNode = true;
m_activeNode->setEditable();
m_scene->setFocusItem(m_activeNode);
} }
void GraphWidget::zoomIn(QKeyEvent *event) void GraphWidget::zoomIn(QKeyEvent *event)
@ -431,194 +165,74 @@ void GraphWidget::zoomOut(QKeyEvent *event)
void GraphWidget::scaleUp(QKeyEvent *event) void GraphWidget::scaleUp(QKeyEvent *event)
{ {
Q_UNUSED(event) Q_UNUSED(event)
if (!m_activeNode) m_graphlogic->scaleUp(
{ QApplication::keyboardModifiers() & Qt::ControlModifier &&
emit notification(tr("No active node.")); QApplication::keyboardModifiers() & Qt::ShiftModifier);
return;
}
// Scale up just the active Node or it's subtree too?
if (QApplication::keyboardModifiers() & Qt::ShiftModifier)
{
QList <Node *> nodeList = m_activeNode->subtree();
foreach(Node *node, nodeList)
node->setScale(qreal(1.2),sceneRect());
}
else
{
m_activeNode->setScale(qreal(1.2),sceneRect());
}
} }
void GraphWidget::scaleDown(QKeyEvent *event) void GraphWidget::scaleDown(QKeyEvent *event)
{ {
Q_UNUSED(event) Q_UNUSED(event)
if (!m_activeNode) m_graphlogic->scaleDown(
{ QApplication::keyboardModifiers() & Qt::ControlModifier &&
emit notification(tr("No active node.")); QApplication::keyboardModifiers() & Qt::ShiftModifier);
return;
}
// Scale down just the active Node or it's subtree too?
if (QApplication::keyboardModifiers() & Qt::ShiftModifier)
{
QList <Node *> nodeList = m_activeNode->subtree();
foreach(Node *node, nodeList)
node->setScale(qreal(1 / 1.2),sceneRect());
}
else
{
m_activeNode->setScale(qreal(1 / 1.2),sceneRect());
}
} }
void GraphWidget::nodeColor(QKeyEvent *event) void GraphWidget::nodeColor(QKeyEvent *event)
{ {
Q_UNUSED(event) Q_UNUSED(event)
if (!m_activeNode) m_graphlogic->nodeColor(
{ QApplication::keyboardModifiers() & Qt::ControlModifier &&
emit notification(tr("No active node.")); QApplication::keyboardModifiers() & Qt::ShiftModifier);
return;
}
// Set color of the active Node or of it's subtree too?
QList <Node *> 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);
}
} }
void GraphWidget::nodeTextColor(QKeyEvent *event) void GraphWidget::nodeTextColor(QKeyEvent *event)
{ {
Q_UNUSED(event) Q_UNUSED(event)
if (!m_activeNode) m_graphlogic->nodeTextColor(
{ QApplication::keyboardModifiers() & Qt::ControlModifier &&
emit notification(tr("No active node.")); QApplication::keyboardModifiers() & Qt::ShiftModifier);
return;
}
// Set textcolor of the active Node or of it's subtree too?
QList <Node *> 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);
} }
void GraphWidget::addEdge(QKeyEvent *event) void GraphWidget::addEdge(QKeyEvent *event)
{ {
Q_UNUSED(event) Q_UNUSED(event)
emit notification(tr("Add edge: select destination node.")); m_graphlogic->addEdge();
m_edgeAdding = true;
} }
void GraphWidget::removeEdge(QKeyEvent *event) void GraphWidget::removeEdge(QKeyEvent *event)
{ {
Q_UNUSED(event) Q_UNUSED(event)
emit notification(tr("Delete edge: select other end-node.")); m_graphlogic->removeEdge();
m_edgeDeleting = true;
} }
void GraphWidget::nodeLostFocus() void GraphWidget::nodeLoseFocus(QKeyEvent *event)
{ {
if (m_editingNode) Q_UNUSED(event)
{ m_graphlogic->nodeLostFocus();
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;
}
} }
void GraphWidget::hintMode(QKeyEvent *event) void GraphWidget::hintMode(QKeyEvent *event)
{ {
Q_UNUSED(event) Q_UNUSED(event)
m_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 GraphWidget::insertPicture(const QString &picture) void GraphWidget::insertPicture(const QString &picture)
{ {
if (!m_activeNode) Q_UNUSED(picture)
{ m_graphlogic->insertPicture(picture);
emit notification(tr("No active node.")); }
return;
} void GraphWidget::contentChangedFromLogic()
{
emit contentChanged();
}
m_activeNode->insertPicture(picture); void GraphWidget::notificationFromLogic(const QString &msg)
{
emit notification(msg);
} }
// All key event arrives here. // All key event arrives here.
@ -629,14 +243,14 @@ void GraphWidget::keyPressEvent(QKeyEvent *event)
// Node lost focus: leaving edge adding/deleting or Node editing. // Node lost focus: leaving edge adding/deleting or Node editing.
if (event->key() == Qt::Key_Escape) if (event->key() == Qt::Key_Escape)
{ {
nodeLostFocus(); nodeLoseFocus();
return; return;
} }
// During Node editing mode, pass every key to the Node's keyPressEvent. // 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; return;
} }
@ -665,137 +279,64 @@ void GraphWidget::drawBackground(QPainter *painter, const QRectF &rect)
painter->drawRect(m_scene->sceneRect()); 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) 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) 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) 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) 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) void GraphWidget::increment(QKeyEvent *event)
{ {
Q_UNUSED(event) event->modifiers() & Qt::ControlModifier ?
QApplication::keyboardModifiers() & Qt::ControlModifier ? scaleUp(event) :
scaleDown() : zoomIn();
zoomOut();
} }
void GraphWidget::decrement(QKeyEvent *event) void GraphWidget::decrement(QKeyEvent *event)
{ {
Q_UNUSED(event) event->modifiers() & Qt::ControlModifier ?
QApplication::keyboardModifiers() & Qt::ControlModifier ? scaleDown(event) :
scaleDown() :
zoomOut(); zoomOut();
} }
void GraphWidget::appendNumber(QKeyEvent *event) void GraphWidget::appendNumber(QKeyEvent *event)
{ {
if (!m_showingNodeNumbers) m_graphlogic->appendNumber(event->key()-48);
return;
m_hintNumber.append(QString::number(event->key()-48));
showingAllNodeNumbers(false);
showingNodeNumbersBeginWithNumber(m_hintNumber.toInt(), true);
} }
void GraphWidget::delNumber(QKeyEvent *event) void GraphWidget::delNumber(QKeyEvent *event)
{ {
Q_UNUSED(event) Q_UNUSED(event)
if (!m_showingNodeNumbers && m_hintNumber.isEmpty()) m_graphlogic->delNumber();
return;
m_hintNumber.remove(m_hintNumber.length()-1,1);
showNodeNumbers();
} }
void GraphWidget::applyNumber(QKeyEvent *event) void GraphWidget::applyNumber(QKeyEvent *event)
{ {
Q_UNUSED(event) Q_UNUSED(event)
if (m_hintNode && m_showingNodeNumbers) m_graphlogic->applyNumber();
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 <Node *> 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);
}
} }
void GraphWidget::scaleView(qreal scaleFactor) void GraphWidget::scaleView(qreal scaleFactor)
@ -809,184 +350,3 @@ void GraphWidget::scaleView(qreal scaleFactor)
scale(scaleFactor, scaleFactor); scale(scaleFactor, scaleFactor);
} }
QList<Edge *> GraphWidget::allEdges() const
{
QList<Edge *> 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("<img src=:/qtmindmap.svg width=50 height=50></img>"));
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<Node *>::const_iterator it = m_nodeList.begin();
it != m_nodeList.end(); it++,
i++)
{
dynamic_cast<Node*>(*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<Node *>::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<Node*>(*it)->showNumber(i,show,true);
m_hintNode = dynamic_cast<Node*>(*it);
continue;
}
// if 'i' starts with 'prefix'
if ((QString::number(i)).startsWith(QString::number(prefix)))
{
hit++;
dynamic_cast<Node*>(*it)->showNumber(i,show);
}
}
if (hit==1)
{
selectNode(m_hintNode);
}
else if (hit == 0)
{
m_showingNodeNumbers = false;
showingAllNodeNumbers(false);
}
}

@ -317,7 +317,7 @@ void MainWindow::setUpMainToolbar()
m_editNode = new QAction(tr("Edit node (F2, dubclick)"), this); m_editNode = new QAction(tr("Edit node (F2, dubclick)"), this);
connect(m_editNode, SIGNAL(activated()), m_graphicsView, connect(m_editNode, SIGNAL(activated()), m_graphicsView,
SLOT(nodeEdited())); SLOT(editNode()));
m_scaleUpNode = new QAction(tr("ScaleUp Node (Ctrl +)"), this); m_scaleUpNode = new QAction(tr("ScaleUp Node (Ctrl +)"), this);
connect(m_scaleUpNode, SIGNAL(activated()), m_graphicsView, 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); m_esc = new QAction(tr("Leave editing,\nedge eadd/remove (esc)"), this);
connect(m_esc, SIGNAL(activated()), m_graphicsView, connect(m_esc, SIGNAL(activated()), m_graphicsView,
SLOT(nodeLostFocus())); SLOT(nodeLoseFocus()));
m_hintMode = new QAction(tr("Hint mode (f)"), this); m_hintMode = new QAction(tr("Hint mode (f)"), this);
connect(m_hintMode, SIGNAL(activated()), m_graphicsView, connect(m_hintMode, SIGNAL(activated()), m_graphicsView,

@ -15,14 +15,13 @@ 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);
Node::Node(GraphWidget *parent) : Node::Node()
m_graph(parent), : m_number(-1)
m_number(-1), , m_hasBorder(false)
m_hasBorder(false), , m_numberIsSpecial(false)
m_numberIsSpecial(false), , m_color(m_gold)
m_color(m_gold), , m_textColor(0,0,0)
m_textColor(0,0,0), , m_effect(new QGraphicsDropShadowEffect(this))
m_effect(new QGraphicsDropShadowEffect(this))
{ {
setFlag(ItemIsMovable); setFlag(ItemIsMovable);
setFlag(ItemSendsGeometryChanges); setFlag(ItemSendsGeometryChanges);
@ -42,6 +41,8 @@ Node::~Node()
Edge *tmp = element.edge; Edge *tmp = element.edge;
tmp->sourceNode()->removeEdgeFromList(tmp); tmp->sourceNode()->removeEdgeFromList(tmp);
tmp->destNode()->removeEdgeFromList(tmp); tmp->destNode()->removeEdgeFromList(tmp);
/// @bug crashes sometimes
delete tmp; delete tmp;
} }
} }

Loading…
Cancel
Save