From ecc06f131cb73bf9920b52982494846aeeeed345 Mon Sep 17 00:00:00 2001 From: Lukáš Lalinský Date: Thu, 8 Jan 2009 23:21:00 +0100 Subject: Move line drawing functionality into the Line class --- src/diagram/diagram.pri | 7 + src/diagram/linelayouter.cpp | 36 +++++ src/diagram/linelayouter.h | 47 +++++++ src/diagramdocument.cpp | 2 + src/items/database/databaserelationship.cpp | 115 ++++------------ src/items/database/databaserelationship.h | 4 - src/line.cpp | 197 +++++++++++++++++++++++++++- src/line.h | 34 +++++ src/src.pro | 1 + 9 files changed, 345 insertions(+), 98 deletions(-) create mode 100644 src/diagram/diagram.pri create mode 100644 src/diagram/linelayouter.cpp create mode 100644 src/diagram/linelayouter.h diff --git a/src/diagram/diagram.pri b/src/diagram/diagram.pri new file mode 100644 index 0000000..b98b4dd --- /dev/null +++ b/src/diagram/diagram.pri @@ -0,0 +1,7 @@ +DEPENDPATH += $$PWD + +SOURCES += \ + linelayouter.cpp + +HEADERS += \ + linelayouter.h diff --git a/src/diagram/linelayouter.cpp b/src/diagram/linelayouter.cpp new file mode 100644 index 0000000..33293a4 --- /dev/null +++ b/src/diagram/linelayouter.cpp @@ -0,0 +1,36 @@ +// Copyright (C) 2008 Lukas Lalinsky +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#include "linelayouter.h" + +using namespace Diagram; + +LineLayouter::LineLayouter() +{ + m_updateTimer = new QTimer(this); + m_updateTimer->setSingleShot(true); + m_updateTimer->setInterval(5); + connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(doUpdate())); +} + +LineLayouter::~LineLayouter() +{ +} + +void +LineLayouter::doUpdate() +{ +} diff --git a/src/diagram/linelayouter.h b/src/diagram/linelayouter.h new file mode 100644 index 0000000..88fcbef --- /dev/null +++ b/src/diagram/linelayouter.h @@ -0,0 +1,47 @@ +// Copyright (C) 2009 Lukas Lalinsky +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#ifndef DIAGRAM_LINELAYOUTER_H +#define DIAGRAM_LINELAYOUTER_H + +#include +#include + +class Line; + +namespace Diagram { + +class LineLayouter : public QObject +{ + Q_OBJECT + +public: + LineLayouter(); + ~LineLayouter(); + + void updateLine(Line *line); + void addLine(Line *line); + +protected slots: + void doUpdate(); + +private: + QTimer *m_updateTimer; +}; + +} + +#endif diff --git a/src/diagramdocument.cpp b/src/diagramdocument.cpp index 3c73df6..9e22052 100644 --- a/src/diagramdocument.cpp +++ b/src/diagramdocument.cpp @@ -30,6 +30,7 @@ #include #include #include +#include "diagram/linelayouter.h" using namespace std; @@ -57,6 +58,7 @@ public: QSet linesToUpdate; QSet objectsToUpdate; QMap counters; + Diagram::LineLayouter *lineLayouter; }; DiagramDocument::DiagramDocument(QObject *parent) diff --git a/src/items/database/databaserelationship.cpp b/src/items/database/databaserelationship.cpp index aacacf6..c1cddce 100644 --- a/src/items/database/databaserelationship.cpp +++ b/src/items/database/databaserelationship.cpp @@ -62,9 +62,10 @@ public: paths[0].lineTo(5, 0); paths[0].addEllipse(-3.5, 8, 7, 7); - paths[4].moveTo(-4.5, 10); + paths[4].moveTo(-4.5, 9); paths[4].lineTo(0, 0); - paths[4].lineTo(4.5, 10); + paths[4].lineTo(4.5, 9); + paths[4].lineTo(-4.5, 9); } } @@ -76,12 +77,6 @@ public: Column *childColumn; Column *parentColumn; - bool fillEnds; - QPainterPath sourceEnd; - QPainterPath targetEnd; - QPolygonF line; - Qt::PenStyle lineStyle; - QPainterPath crowsFootPath(bool toMany, bool optional) { if (toMany) { @@ -220,59 +215,6 @@ DatabaseRelationship::isIdentifying() const return false; } -QRectF -DatabaseRelationship::boundingRect() const -{ - return shape().controlPointRect(); -} - -QPainterPath -DatabaseRelationship::shape() const -{ - QPainterPath path; - if (d->line.isEmpty()) - return path; - path.moveTo(d->line[0]); - for (int i = 1; i < d->line.size(); i++) - path.lineTo(d->line[i]); - path.addPath(d->targetEnd); - path.addPath(d->sourceEnd); - QPen pen(QPen(QColor(0, 0, 0), 1.33)); - pen.setJoinStyle(Qt::MiterJoin); - QPainterPathStroker ps; - ps.setCapStyle(pen.capStyle()); - ps.setWidth(pen.widthF() + 2); - ps.setJoinStyle(pen.joinStyle()); - ps.setMiterLimit(pen.miterLimit()); - return ps.createStroke(path); -} - -void -DatabaseRelationship::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) -{ - Q_UNUSED(option); - Q_UNUSED(widget); - - QPen pen(QPen(QColor(0, 0, 0), 1.5)); - pen.setJoinStyle(Qt::MiterJoin); - pen.setStyle(d->lineStyle); - - if (!document()->isPrinting() && isSelected()) { - pen.setColor(QColor(0, 96, 255)); -// pen.setWidth(2); - } - - painter->setPen(pen); - painter->drawPolyline(d->line); - if (d->fillEnds) - painter->setBrush(pen.color()); - - pen.setStyle(Qt::SolidLine); - painter->setPen(pen); - painter->drawPath(d->sourceEnd); - painter->drawPath(d->targetEnd); -} - QVariant DatabaseRelationship::itemChange(GraphicsItemChange change, const QVariant &value) { @@ -290,35 +232,20 @@ DatabaseRelationship::updateEnds() switch (document()->notation()) { case DiagramDocument::Relational: - d->fillEnds = true; - d->targetEnd = d->arrowHeadPath(); - d->sourceEnd = QPainterPath(); - d->lineStyle = Qt::SolidLine; + setFillStartArrow(false); + setFillEndArrow(true); + setStartArrow(QPainterPath()); + setEndArrow(d->arrowHeadPath()); + setLineStyle(Qt::SolidLine); break; case DiagramDocument::CrowsFoot: - d->fillEnds = false; - d->targetEnd = d->crowsFootPath(d->cardinality == ManyToMany, isParentOptional()); - d->sourceEnd = d->crowsFootPath(d->cardinality != OneToOne, isChildOptional()); - d->lineStyle = isIdentifying() ? Qt::SolidLine : Qt::DashLine; + setFillStartArrow(false); + setFillEndArrow(false); + setStartArrow(d->crowsFootPath(d->cardinality != OneToOne, isChildOptional())); + setEndArrow(d->crowsFootPath(d->cardinality == ManyToMany, isParentOptional())); + setLineStyle(isIdentifying() ? Qt::SolidLine : Qt::DashLine); break; } - - if (!d->sourceEnd.isEmpty()) { - QLineF firstSegment(d->line[0], d->line[1]); - QMatrix matrix; - matrix.translate(firstSegment.p1().x(), firstSegment.p1().y()); - matrix.rotate(360 - firstSegment.angle() + 90 + 180); - d->sourceEnd = matrix.map(d->sourceEnd); - } - - if (!d->targetEnd.isEmpty()) { - int size = d->line.size(); - QLineF lastSegment(d->line[size-2], d->line[size-1]); - QMatrix matrix; - matrix.translate(lastSegment.p2().x(), lastSegment.p2().y()); - matrix.rotate(-lastSegment.angle() + 90); - d->targetEnd = matrix.map(d->targetEnd); - } } void @@ -331,6 +258,8 @@ DatabaseRelationship::updateLayout() bool haveLine = false; + QPolygonF line; + // Orthogonal line if (!haveLine) { QPointF p1 = connector1->pos(); @@ -341,24 +270,23 @@ DatabaseRelationship::updateLayout() QLineF line1 = QLineF::fromPolar(1, a1).translated(p1); QLineF line2 = QLineF::fromPolar(1, a2).translated(p2); QPointF intersection; - d->line.clear(); - d->line << p1; + line << p1; if (line1.intersect(line2, &intersection) != QLineF::NoIntersection) { // 2-segment line - d->line << intersection; + line << intersection; } else { if (line1.intersect(line2.normalVector(), &intersection) != QLineF::NoIntersection) { // 3-segment line qreal len = QLineF(p1, intersection).length() * 0.5; - d->line << QLineF::fromPolar(len, a1).translated(p1).p2(); - d->line << QLineF::fromPolar(len, a2).translated(p2).p2(); + line << QLineF::fromPolar(len, a1).translated(p1).p2(); + line << QLineF::fromPolar(len, a2).translated(p2).p2(); } else { qFatal("No line?"); } } - d->line << p2; + line << p2; haveLine = true; } @@ -366,10 +294,11 @@ DatabaseRelationship::updateLayout() if (!haveLine) { QPointF p1 = connector1->pos(); QPointF p2 = connector2->pos(); - d->line = QPolygonF() << p1 << p2; + line << p1 << p2; haveLine = true; } + setLinePoints(line); updateEnds(); } diff --git a/src/items/database/databaserelationship.h b/src/items/database/databaserelationship.h index 044a891..7fe6f61 100644 --- a/src/items/database/databaserelationship.h +++ b/src/items/database/databaserelationship.h @@ -38,10 +38,6 @@ public: DatabaseRelationship(DiagramItem *parent = 0); ~DatabaseRelationship(); - QRectF boundingRect() const; - QPainterPath shape() const; - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); - DatabaseTable *childTable() const { return qobject_cast(connector(0)->connectedObject()); } DatabaseTable *parentTable() const { return qobject_cast(connector(1)->connectedObject()); } diff --git a/src/line.cpp b/src/line.cpp index 133c02f..527fab3 100644 --- a/src/line.cpp +++ b/src/line.cpp @@ -14,6 +14,7 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +#include #include "line.h" #include "hub.h" #include "diagramdocument.h" @@ -21,7 +22,7 @@ #include "connector.h" Line::Line(DiagramItem *parent) - : DiagramItem(parent) + : DiagramItem(parent), m_fillStartArrow(false), m_fillEndArrow(false), m_dirty(false) { m_connectors[0] = new Connector(this); m_connectors[1] = new Connector(this); @@ -97,3 +98,197 @@ Line::saveToXml(QDomDocument doc, QDomElement element) } } } + +QPolygonF +Line::linePoints() const +{ + return m_linePoints; +} + +void +Line::setLinePoints(const QPolygonF& points) +{ + if (m_linePoints != points) { + prepareGeometryChange(); + m_linePoints = points; + m_dirty = true; + } +} + +Qt::PenStyle +Line::lineStyle() const +{ + return m_lineStyle; +} + +void +Line::setLineStyle(Qt::PenStyle style) +{ + if (m_lineStyle != style) { + // prepareGeometryChange(); + m_lineStyle = style; + // m_dirty = true; + } +} + +QPainterPath +Line::startArrow() const +{ + return m_startArrow; +} + +void +Line::setStartArrow(const QPainterPath& path) +{ + if (m_startArrow != path) { + prepareGeometryChange(); + m_startArrow = path; + m_dirty = true; + } +} + +bool +Line::fillStartArrow() const +{ + return m_fillStartArrow; +} + +void +Line::setFillStartArrow(bool fill) +{ + if (m_fillStartArrow != fill) { + m_fillStartArrow = fill; + m_dirty = true; + } +} + +QPainterPath +Line::endArrow() const +{ + return m_endArrow; +} + +void +Line::setEndArrow(const QPainterPath& path) +{ + if (m_endArrow != path) { + prepareGeometryChange(); + m_endArrow = path; + m_dirty = true; + } +} + +bool +Line::fillEndArrow() const +{ + return m_fillEndArrow; +} + +void +Line::setFillEndArrow(bool fill) +{ + if (m_fillEndArrow != fill) { + m_fillEndArrow = fill; + m_dirty = true; + } +} + +void +Line::updateCache() +{ + m_dirty = false; + + QPainterPath path; + if (m_linePoints.isEmpty()) { + // Nothing to process + m_shape = path; + return; + } + + if (!m_startArrow.isEmpty()) { + QLineF firstSegment(m_linePoints.at(0), m_linePoints.at(1)); + QMatrix matrix; + matrix.translate(firstSegment.p1().x(), firstSegment.p1().y()); + matrix.rotate(360 - firstSegment.angle() + 90 + 180); + m_transformedStartArrow = matrix.map(m_startArrow); + } + else { + m_transformedStartArrow = QPainterPath(); + } + + if (!m_endArrow.isEmpty()) { + int size = m_linePoints.size(); + QLineF lastSegment(m_linePoints.at(size-2), m_linePoints.at(size-1)); + QMatrix matrix; + matrix.translate(lastSegment.p2().x(), lastSegment.p2().y()); + matrix.rotate(-lastSegment.angle() + 90); + m_transformedEndArrow = matrix.map(m_endArrow); + } + else { + m_transformedEndArrow = QPainterPath(); + } + + // Add the main line + path.moveTo(m_linePoints.at(0)); + for (int i = 1; i < m_linePoints.size(); i++) + path.lineTo(m_linePoints.at(i)); + + path.addPath(m_transformedStartArrow); + path.addPath(m_transformedEndArrow); + + // Add an outline around the path + QPainterPathStroker ps; + ps.setWidth(4.33); + ps.setJoinStyle(Qt::MiterJoin); + m_shape = ps.createStroke(path); +} + +QRectF +Line::boundingRect() const +{ + return shape().controlPointRect(); +} + +QPainterPath +Line::shape() const +{ + if (m_dirty) + const_cast(this)->updateCache(); + + return m_shape; +} + +void +Line::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + if (m_dirty) + updateCache(); + + QPen pen(QPen(QColor(0, 0, 0), 1.33)); + pen.setJoinStyle(Qt::MiterJoin); + pen.setStyle(m_lineStyle); + if (!document()->isPrinting() && isSelected()) { + pen.setColor(QColor(0, 96, 255)); + } + + painter->setPen(pen); + painter->drawPolyline(m_linePoints); + + pen.setStyle(Qt::SolidLine); + painter->setPen(pen); + if (m_fillStartArrow) { + painter->setBrush(pen.color()); + } + painter->drawPath(m_transformedStartArrow); + if (m_fillEndArrow) { + if (!m_fillStartArrow) { + painter->setBrush(pen.color()); + } + } + else { + if (m_fillStartArrow) { + painter->setBrush(QBrush()); + } + } + painter->drawPath(m_transformedEndArrow); +} diff --git a/src/line.h b/src/line.h index 79de959..a79abe4 100644 --- a/src/line.h +++ b/src/line.h @@ -38,13 +38,47 @@ public: Connector *connector(int index) const; + QRectF boundingRect() const; + QPainterPath shape() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + virtual void updateLayout(); virtual void loadFromXml(QDomElement element, DiagramDocument *document = 0); void saveToXml(QDomDocument doc, QDomElement element); + QPolygonF linePoints() const; + void setLinePoints(const QPolygonF &points); + + Qt::PenStyle lineStyle() const; + void setLineStyle(Qt::PenStyle style); + + QPainterPath startArrow() const; + void setStartArrow(const QPainterPath &path); + + bool fillStartArrow() const; + void setFillStartArrow(bool fill); + + QPainterPath endArrow() const; + void setEndArrow(const QPainterPath &path); + + bool fillEndArrow() const; + void setFillEndArrow(bool fill); + private: + void updateCache(); + Connector *m_connectors[2]; + QPolygonF m_linePoints; + Qt::PenStyle m_lineStyle; + QPainterPath m_shape; + QPainterPath m_startArrow; + QPainterPath m_transformedStartArrow; + bool m_fillStartArrow; + QPainterPath m_endArrow; + QPainterPath m_transformedEndArrow; + bool m_fillEndArrow; + bool m_dirty; }; #endif diff --git a/src/src.pro b/src/src.pro index 526656d..91cedb2 100644 --- a/src/src.pro +++ b/src/src.pro @@ -5,6 +5,7 @@ QT += xml svg CONFIG += debug include(src.pri) +include(diagram/diagram.pri) include(items/items.pri) include(utils/utils.pri) -- cgit v1.2.3-54-g00ecf