[Uml-devel] branches/work/soc-umbrello/umbrello
Gopala Krishna A
krishna.ggk at gmail.com
Tue Sep 23 17:09:49 UTC 2008
SVN commit 863982 by gopala:
* Implemented "segment" moving and "point" moving mechanism to adjust
the Linepath.
* Hovering mechanism is implemented to highlight the line and point
being targetted for adjustment.
M +165 -14 newlinepath.cpp
M +14 -1 newlinepath.h
--- branches/work/soc-umbrello/umbrello/newlinepath.cpp #863981:863982
@@ -12,7 +12,10 @@
#include "newlinepath.h"
// qt includes
+#include <QtGui/QGraphicsSceneMouseEvent>
#include <QtGui/QPainter>
+#include <QtGui/QStyleOptionGraphicsItem>
+
#include <QtXml/QDomDocument>
#include <QtXml/QDomNode>
#include <QtXml/QDomNodeList>
@@ -37,7 +40,7 @@
// Initialize static variables.
const qreal LinePath::Delta = 5;
- const qreal LinePath::SelectedPointRadius = 5;
+ const qreal LinePath::SelectedPointDiameter = 8;
/**
@@ -45,6 +48,9 @@
*/
LinePath::LinePath(QGraphicsItem *parent) : QGraphicsItem(parent)
{
+ m_activePointIndex = m_activeSegmentIndex = -1;
+ m_hasSegmentMoved = false;
+ setFlags(ItemIsSelectable | ItemIsFocusable);
}
/// Destructor
@@ -152,8 +158,9 @@
* passed.
* @rertval -1 If no linepoint is closer to passed in \a point.
*
- * The closeness is measured by "delta" which indicates radius
- * around the linepoint to be regarded as closer.
+ * @param point The point which is to be tested for closeness.
+ * @param delta The closeness is measured by "delta" which indicates radius
+ * around the linepoint to be regarded as closer.
*/
int LinePath::closestPointIndex(const QPointF& point, qreal delta) const
{
@@ -175,6 +182,36 @@
}
/**
+ * @retval "Index" of the segment on which the point lies (fuzzy)
+ * @retval -1 If no segment contains the point
+ *
+ * @param point The point to be tested for line-segment closure.
+ * @param delta Represents extent of fuzzy region around the line
+ * to be tested for point closure.
+ */
+ int LinePath::segmentIndex(const QPointF& point, qreal delta) const
+ {
+ QPainterPathStroker stroker;
+ stroker.setWidth(delta);
+
+ for(int i = 1; i < m_points.size(); ++i) {
+ QLineF segment(m_points[i-1], m_points[i]);
+
+ QPainterPath path;
+ path.moveTo(segment.p1());
+ path.lineTo(segment.p2());
+
+ path = stroker.createStroke(path);
+
+ if (path.contains(point)) {
+ return i-1;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
* @retval True If point is either the start or end of linepath.
*/
bool LinePath::isEndPoint(const QPointF& point) const
@@ -235,10 +272,19 @@
*/
int LinePath::insertableLinePathIndex(const QPointF &pos) const
{
- QLineF posLine(pos, pos);
+ QPainterPathStroker stroker;
+ stroker.setWidth(LinePath::Delta);
+
for(int i = 1; i < m_points.size(); ++i) {
QLineF segment(m_points[i-1], m_points[i]);
- if (segment.intersect(posLine, 0) != QLineF::NoIntersection) {
+
+ QPainterPath path;
+ path.moveTo(segment.p1());
+ path.lineTo(segment.p2());
+
+ path = stroker.createStroke(path);
+
+ if (path.contains(pos)) {
return i;
}
}
@@ -299,22 +345,26 @@
calculateBoundingRect();
}
+ /// @return Color of line being drawn.
QColor LinePath::lineColor() const
{
return m_pen.color();
}
+ /// Sets the color for the lines being drawn.
void LinePath::setLineColor(const QColor& color)
{
m_pen.setColor(color);
update();
}
+ /// @return The width of the line being drawn.
uint LinePath::lineWidth() const
{
return m_pen.width();
}
+ /// Sets the width of line being drawn to \a width.
void LinePath::setLineWidth(uint width)
{
prepareGeometryChange();
@@ -322,65 +372,159 @@
calculateBoundingRect();
}
+ /// @return The bounding rectangle for the linepath.
QRectF LinePath::boundingRect() const
{
return m_boundingRect;
}
+ /// @return The shape of the linepath.
QPainterPath LinePath::shape() const
{
return m_shape;
}
+ /// Draws the line path and also takes care of highlighting active point or line.
void LinePath::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *)
{
painter->setPen(m_pen);
painter->setBrush(Qt::NoBrush);
painter->drawPolyline(m_points.constData(), m_points.size());
+
+ if (opt->state & QStyle::State_Selected) {
+ QRectF ellipse(0, 0, SelectedPointDiameter, SelectedPointDiameter);
+ painter->setBrush(Qt::blue);
+ foreach (QPointF point, m_points) {
+ ellipse.moveCenter(point);
+ painter->drawEllipse(ellipse);
+ }
+
+ if (m_activePointIndex != -1) {
+ ellipse.moveCenter(m_points.at(m_activePointIndex));
+ painter->setBrush(Qt::darkBlue);
+ painter->drawEllipse(ellipse);
+ }
+ else if (m_activeSegmentIndex != -1) {
+ painter->setPen(Qt::yellow);
+ painter->drawLine(QLineF(m_points[m_activeSegmentIndex], m_points[m_activeSegmentIndex+1]));
+ }
+ }
}
+ /// Determines the active point or segment, the latter being given more priority.
void LinePath::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
-
+ if (event->buttons() & Qt::LeftButton) {
+ m_activePointIndex = closestPointIndex(event->pos());
+ // calculate only if active point index is -1
+ m_activeSegmentIndex = (m_activePointIndex != -1) ? -1 : segmentIndex(event->pos());
+ m_hasSegmentMoved = false;
+ }
+ else {
+ m_activePointIndex = m_activeSegmentIndex = -1;
+ }
+ QGraphicsItem::mousePressEvent(event);
}
+ /// Moves the point or line if active.
void LinePath::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
-
+ if (m_activePointIndex != -1) {
+ setPoint(m_activePointIndex, event->pos());
+ }
+ else if (m_activeSegmentIndex != -1) {
+ if (m_hasSegmentMoved == false) {
+ insertPoint(m_activeSegmentIndex, m_points[m_activeSegmentIndex]);
+ ++m_activeSegmentIndex;
+ insertPoint(m_activeSegmentIndex + 1, m_points[m_activeSegmentIndex + 1]);
+ m_hasSegmentMoved = true;
+ }
+ QPointF delta = event->scenePos() - event->lastScenePos();
+ setPoint(m_activeSegmentIndex, m_points[m_activeSegmentIndex] + delta);
+ setPoint(m_activeSegmentIndex + 1, m_points[m_activeSegmentIndex + 1] + delta);
+ }
}
+ /// Reset active indices and also push undo command.
void LinePath::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
-
+ if (event->buttons() & Qt::LeftButton) {
+ m_activeSegmentIndex = m_activePointIndex = -1;
+ m_hasSegmentMoved = false;
+ }
+ QGraphicsItem::mouseReleaseEvent(event);
}
+ /// Inserts a new point at double click position.
void LinePath::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
-
+ int index = insertableLinePathIndex(event->pos());
+ if (index != -1) {
+ insertPoint(index, event->pos());
+ }
}
-
+ /**
+ * Calculates the "to be highlighted" point and segment indicies
+ * and updates if necessary.
+ */
void LinePath::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
+ int oldPointIndex = m_activePointIndex;
+ int oldSegmentIndex = m_activeSegmentIndex;
+ m_activePointIndex = closestPointIndex(event->pos());
+ // Activate segment index only if point index is -1
+ m_activeSegmentIndex = (m_activePointIndex != -1) ? -1 : segmentIndex(event->pos());
+
+ bool isChanged = (oldSegmentIndex != m_activeSegmentIndex || oldPointIndex != m_activePointIndex);
+ if (isChanged) {
+ update();
+ }
}
+ /**
+ * Calculates the "to be highlighted" point and segment indicies
+ * and updates if necessary.
+ */
void LinePath::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
+ int oldPointIndex = m_activePointIndex;
+ int oldSegmentIndex = m_activeSegmentIndex;
+ m_activePointIndex = closestPointIndex(event->pos());
+ // Activate segment index only if point index is -1
+ m_activeSegmentIndex = (m_activePointIndex != -1) ? -1 : segmentIndex(event->pos());
+
+ bool isChanged = (oldSegmentIndex != m_activeSegmentIndex || oldPointIndex != m_activePointIndex);
+ if (isChanged) {
+ update();
+ }
}
+ /// Reset active indicies and updates.
void LinePath::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
-
+ m_activePointIndex = m_activeSegmentIndex = -1;
+ update();
}
-
+ /**
+ * Enables hover events on selection and disables the same on
+ * deselection.
+ */
QVariant LinePath::itemChange(GraphicsItemChange change, const QVariant& value)
{
+ if (change == ItemSelectedHasChanged) {
+ setAcceptHoverEvents(value.toBool());
+ }
return QGraphicsItem::itemChange(change, value);
}
+ /**
+ * Calculates the "shape" and also the "bounding rectangle"
+ * required by GraphicsView framework.
+ */
void LinePath::calculateBoundingRect()
{
if (m_points.isEmpty()) {
@@ -388,6 +532,7 @@
m_boundingRect = QRectF();
return;
}
+
QPainterPath path;
path.moveTo(m_points.first());
for(int i = 1; i < m_points.size(); ++i) {
@@ -395,10 +540,16 @@
path.lineTo(pt);
}
+ QRectF ellipse(0, 0, SelectedPointDiameter, SelectedPointDiameter);
+ foreach(QPointF point, m_points) {
+ ellipse.moveCenter(point);
+ path.addEllipse(ellipse);
+ }
+
QPainterPathStroker stroker;
- stroker.setWidth(lineWidth());
+ stroker.setWidth(LinePath::Delta); // allow delta region
m_shape = stroker.createStroke(path);
- m_boundingRect = m_shape.controlPointRect();
+ m_boundingRect = m_shape.boundingRect();
}
}
--- branches/work/soc-umbrello/umbrello/newlinepath.h #863981:863982
@@ -41,6 +41,7 @@
void optimizeLinePoints();
int closestPointIndex(const QPointF& point, qreal delta = LinePath::Delta) const;
+ int segmentIndex(const QPointF& point, qreal delta = LinePath::Delta) const;
bool isEndPoint(const QPointF& point) const;
bool isEndPointIndex(int index) const;
@@ -87,15 +88,27 @@
/// The pen used to draw lines
QPen m_pen;
+ /// Index of active point which can be dragged to modify linepath.
+ int m_activePointIndex;
+ /**
+ * Index of active segment index.
+ * @note m_activePointIndex and m_activePointIndex can't be
+ * active at same time!
+ */
+ int m_activeSegmentIndex;
+
/// The bounding rectangle of this linepath
QRectF m_boundingRect;
/// The shape of this linepath.
QPainterPath m_shape;
+ /// State variable to keep track of first move of segment.
+ bool m_hasSegmentMoved;
+
/// The default delta for fuzzy recognition of points closer to point.
static const qreal Delta;
/// The radius of circles drawn to show "selection".
- static const qreal SelectedPointRadius;
+ static const qreal SelectedPointDiameter;
};
}
More information about the umbrello-devel
mailing list