diff --git a/sources/editor/arceditor.cpp b/sources/editor/arceditor.cpp index 7130b2f87..36c2346b4 100644 --- a/sources/editor/arceditor.cpp +++ b/sources/editor/arceditor.cpp @@ -110,26 +110,26 @@ CustomElementPart *ArcEditor::currentPart() const { */ void ArcEditor::updateArc() { if (!part) return; - part -> setProperty("x", x -> value()); - part -> setProperty("y", y -> value()); - part -> setProperty("diameter_h", h -> value()); - part -> setProperty("diameter_v", v -> value()); - part -> setProperty("start_angle", -start_angle -> value() + 90); - part -> setProperty("angle", -angle -> value()); + part -> setProperty("centerX", x -> value()); + part -> setProperty("centerY", y -> value()); + part -> setProperty("diameter_h", h -> value()); + part -> setProperty("diameter_v", v -> value()); + part -> setProperty("startAngle", ((start_angle -> value() * -1) + 90) * 16); + part -> setProperty("spanAngle", angle -> value() * -16); } /// Met a jour l'abscisse du centre de l'arc et cree un objet d'annulation -void ArcEditor::updateArcX() { addChangePartCommand(tr("abscisse"), part, "x", x -> value()); } +void ArcEditor::updateArcX() { addChangePartCommand(tr("abscisse"), part, "centerX", x -> value()); } /// Met a jour l'ordonnee du centre de l'arc et cree un objet d'annulation -void ArcEditor::updateArcY() { addChangePartCommand(tr("ordonn\351e"), part, "y", y -> value()); } +void ArcEditor::updateArcY() { addChangePartCommand(tr("ordonn\351e"), part, "centerY", y -> value()); } /// Met a jour le diametre horizontal de l'arc et cree un objet d'annulation -void ArcEditor::updateArcH() { addChangePartCommand(tr("diam\350tre horizontal"), part, "diameter_h", h -> value()); } +void ArcEditor::updateArcH() { addChangePartCommand(tr("diam\350tre horizontal"), part, "diameter_h", h -> value()); } /// Met a jour le diametre vertical de l'arc et cree un objet d'annulation -void ArcEditor::updateArcV() { addChangePartCommand(tr("diam\350tre vertical"), part, "diameter_v", v -> value()); } +void ArcEditor::updateArcV() { addChangePartCommand(tr("diam\350tre vertical"), part, "diameter_v", v -> value()); } /// Met a jour l'angle de depart de l'arc et cree un objet d'annulation -void ArcEditor::updateArcS() { addChangePartCommand(tr("angle de d\351part"), part, "start_angle", -start_angle -> value() + 90); } +void ArcEditor::updateArcS() { addChangePartCommand(tr("angle de d\351part"), part, "startAngle", ((start_angle -> value() * -1) + 90) * 16); } /// Met a jour l'etendue de l'arc et cree un objet d'annulation -void ArcEditor::updateArcA() { addChangePartCommand(tr("angle"), part, "angle", -angle -> value()); } +void ArcEditor::updateArcA() { addChangePartCommand(tr("angle"), part, "spanAngle", angle -> value() * -16); } /** Met a jour le formulaire d'edition @@ -141,8 +141,8 @@ void ArcEditor::updateForm() { y->setValue(part->property("y").toReal()); h->setValue(part->property("diameter_h").toReal()); v->setValue(part->property("diameter_v").toReal()); - start_angle -> setValue(-part -> startAngle() + 90); - angle -> setValue(-part -> angle()); + start_angle -> setValue(((part->property("startAngle").toInt() / 16) - 90) * -1); + angle -> setValue(part->property("spanAngle").toInt() / -16); activeConnections(true); } diff --git a/sources/editor/editorcommands.cpp b/sources/editor/editorcommands.cpp index 99567691c..e1c64cbc8 100644 --- a/sources/editor/editorcommands.cpp +++ b/sources/editor/editorcommands.cpp @@ -567,8 +567,7 @@ void ChangeInformationsCommand::redo() { ScalePartsCommand::ScalePartsCommand(ElementScene *scene, QUndoCommand * parent) : ElementEditionCommand(scene, 0, parent), first_redo(true) -{ -} +{} /** Destructor diff --git a/sources/editor/elementscene.cpp b/sources/editor/elementscene.cpp index 150b721cb..947c0e15f 100644 --- a/sources/editor/elementscene.cpp +++ b/sources/editor/elementscene.cpp @@ -849,15 +849,15 @@ ElementContent ElementScene::loadContent(const QDomDocument &xml_document, QStri QDomElement qde = n.toElement(); if (qde.isNull()) continue; CustomElementPart *cep; - if (qde.tagName() == "line") cep = new PartLine (element_editor, 0, 0); - else if (qde.tagName() == "rect") cep = new PartRectangle(element_editor, 0, 0); - else if (qde.tagName() == "ellipse") cep = new PartEllipse (element_editor, 0, 0); - else if (qde.tagName() == "circle") cep = new PartEllipse (element_editor, 0, 0); - else if (qde.tagName() == "polygon") cep = new PartPolygon (element_editor, 0, 0); - else if (qde.tagName() == "terminal") cep = new PartTerminal (element_editor, 0, 0); - else if (qde.tagName() == "text") cep = new PartText (element_editor, 0, 0); - else if (qde.tagName() == "input") cep = new PartTextField(element_editor, 0, 0); - else if (qde.tagName() == "arc") cep = new PartArc (element_editor, 0, 0); + if (qde.tagName() == "line") cep = new PartLine (element_editor); + else if (qde.tagName() == "rect") cep = new PartRectangle(element_editor); + else if (qde.tagName() == "ellipse") cep = new PartEllipse (element_editor); + else if (qde.tagName() == "circle") cep = new PartEllipse (element_editor); + else if (qde.tagName() == "polygon") cep = new PartPolygon (element_editor); + else if (qde.tagName() == "terminal") cep = new PartTerminal (element_editor); + else if (qde.tagName() == "text") cep = new PartText (element_editor); + else if (qde.tagName() == "input") cep = new PartTextField(element_editor); + else if (qde.tagName() == "arc") cep = new PartArc (element_editor); else continue; if (QGraphicsItem *qgi = dynamic_cast(cep)) { if (!qgi -> zValue()) qgi -> setZValue(z++); diff --git a/sources/editor/ellipseeditor.cpp b/sources/editor/ellipseeditor.cpp index bcd7adda5..ed093941d 100644 --- a/sources/editor/ellipseeditor.cpp +++ b/sources/editor/ellipseeditor.cpp @@ -102,16 +102,16 @@ CustomElementPart *EllipseEditor::currentPart() const { */ void EllipseEditor::updateEllipse() { if (!part) return; - part -> setProperty("x", x -> value()); - part -> setProperty("y", y -> value()); + part -> setProperty("centerX", x -> value()); + part -> setProperty("centerY", y -> value()); part -> setProperty("diameter_h", h -> value()); part -> setProperty("diameter_v", v -> value()); } /// Met a jour l'abscisse du centre de l'ellipse et cree un objet d'annulation -void EllipseEditor::updateEllipseX() { addChangePartCommand(tr("abscisse"), part, "x", x -> value()); } +void EllipseEditor::updateEllipseX() { addChangePartCommand(tr("abscisse"), part, "centerX", x -> value()); } /// Met a jour l'ordonnee du centre de l'ellipse et cree un objet d'annulation -void EllipseEditor::updateEllipseY() { addChangePartCommand(tr("ordonn\351e"), part, "y", y -> value()); } +void EllipseEditor::updateEllipseY() { addChangePartCommand(tr("ordonn\351e"), part, "centerY", y -> value()); } /// Met a jour le diametre horizontal de l'ellipse et cree un objet d'annulation void EllipseEditor::updateEllipseH() { addChangePartCommand(tr("diam\350tre horizontal"), part, "diameter_h", h -> value()); } /// Met a jour le diametre vertical de l'ellipse et cree un objet d'annulation @@ -123,8 +123,8 @@ void EllipseEditor::updateEllipseV() { addChangePartCommand(tr("diam\350tre vert void EllipseEditor::updateForm() { if (!part) return; activeConnections(false); - x->setValue(part->property("x").toReal()); - y->setValue(part->property("y").toReal()); + x->setValue(part->property("centerX").toReal()); + y->setValue(part->property("centerY").toReal()); h->setValue(part->property("diameter_h").toReal()); v->setValue(part->property("diameter_v").toReal()); activeConnections(true); diff --git a/sources/editor/esevent/eseventaddarc.cpp b/sources/editor/esevent/eseventaddarc.cpp index e801a4872..3854f001a 100644 --- a/sources/editor/esevent/eseventaddarc.cpp +++ b/sources/editor/esevent/eseventaddarc.cpp @@ -45,30 +45,36 @@ ESEventAddArc::~ESEventAddArc() { * @param event * @return */ -bool ESEventAddArc::mousePressEvent(QGraphicsSceneMouseEvent *event) { - if (event -> button() == Qt::LeftButton) { +bool ESEventAddArc::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + if (event -> button() == Qt::LeftButton) + { if(!m_running) m_running = true; QPointF pos = m_scene->snapToGrid(event -> scenePos()); - //create new arc - if (!m_arc) { - m_arc = new PartArc(m_editor, 0, m_scene); - m_arc -> setRect(QRectF(pos, pos)); - m_arc -> setAngle(90); + //create new arc + if (!m_arc) + { + m_arc = new PartArc(m_editor); + m_scene -> addItem(m_arc); + m_arc -> setPos(pos); + m_arc -> setProperty("startAngle", 0); + m_arc -> setProperty("spanAngle", 1440); m_arc -> setProperty("antialias", true); m_origin = pos; return true; } - //Add arc to scene + //At this point, m_arc is finish, we add it with an undo command m_arc -> setRect(m_arc->rect().normalized()); m_scene -> undoStack().push(new AddPartCommand(QObject::tr("Arc"), m_scene, m_arc)); - //Set m_arc to nullptr for create new ellipse at next mouse press + //Set m_arc to nullptr for create new ellipse at next mouse press m_arc = nullptr; return true; } + return false; } @@ -120,67 +126,78 @@ bool ESEventAddArc::keyPressEvent(QKeyEvent *event) { * @brief ESEventAddArc::updateArc * Redraw the arc with curent value */ -void ESEventAddArc::updateArc() { - +void ESEventAddArc::updateArc() +{ qreal width = (m_mouse_pos.x() - m_origin.x())*2; if (width < 0) width *= -1; qreal height = (m_mouse_pos.y() - m_origin.y())*2; if (height < 0) height *= -1; - QPointF pos_ = m_origin; + QPointF pos_ = m_arc -> mapFromScene(m_origin); //Draw arc inverted - if (m_inverted) { + if (m_inverted) + { //Adjust the start angle to be snapped at the origin point of draw - if (m_mouse_pos.y() > m_origin.y()) { - - if (m_mouse_pos.x() > m_origin.x()) { + if (m_mouse_pos.y() > m_origin.y()) + { + if (m_mouse_pos.x() > m_origin.x()) + { pos_.ry() -= height/2; - m_arc->setStartAngle(180); + m_arc->setStartAngle(2880); } - else { + else + { pos_.rx() -= width/2; - m_arc->setStartAngle(90); + m_arc->setStartAngle(1440); } } - else { - if (m_mouse_pos.x() > m_origin.x()) { + else + { + if (m_mouse_pos.x() > m_origin.x()) + { pos_.ry() -= height; pos_.rx() -= width/2; - m_arc->setStartAngle(270); + m_arc->setStartAngle(4320); } - else { + else + { pos_.rx() -= width; pos_.ry() -= height/2; m_arc->setStartAngle(0); } } } - - //Draw arc non inverted - else { - //Adjust the start angle to be snapped at the origin point of draw - if (m_mouse_pos.y() > m_origin.y()) { - - if (m_mouse_pos.x() > m_origin.x()) { + //Draw arc non inverted + else + { + //Adjust the start angle to be snapped at the origin point of draw + if (m_mouse_pos.y() > m_origin.y()) + { + if (m_mouse_pos.x() > m_origin.x()) + { pos_.rx() -= width/2; m_arc->setStartAngle(0); } - else { + else + { pos_.rx() -= width; pos_.ry() -= height/2; - m_arc->setStartAngle(270); + m_arc->setStartAngle(4320); } } - else { - if (m_mouse_pos.x() > m_origin.x()) { + else + { + if (m_mouse_pos.x() > m_origin.x()) + { pos_.ry() -= height/2; - m_arc->setStartAngle(90); + m_arc->setStartAngle(1440); } - else { + else + { pos_.rx() -= width/2; pos_.ry() -= height; - m_arc->setStartAngle(180); + m_arc->setStartAngle(2880); } } } diff --git a/sources/editor/esevent/eseventaddellipse.cpp b/sources/editor/esevent/eseventaddellipse.cpp index 7b807c883..cae8c326e 100644 --- a/sources/editor/esevent/eseventaddellipse.cpp +++ b/sources/editor/esevent/eseventaddellipse.cpp @@ -50,19 +50,20 @@ bool ESEventAddEllipse::mousePressEvent(QGraphicsSceneMouseEvent *event) { if(!m_running) m_running = true; QPointF pos = m_scene->snapToGrid(event -> scenePos()); - //create new ellpise + //create new ellpise if (!m_ellipse) { - m_ellipse = new PartEllipse(m_editor, 0, m_scene); - m_ellipse -> setRect(QRectF(pos, pos)); - m_origin = pos; + m_ellipse = new PartEllipse(m_editor); + m_scene -> addItem(m_ellipse); + m_ellipse -> setPos(pos); + m_origin = m_new_pos = pos; return true; } - //Add ellipse to scene - m_ellipse -> setRect(m_ellipse -> rect().normalized()); + //Add ellipse to scene + m_ellipse -> setRect(m_ellipse -> rect().normalized()); m_scene -> undoStack().push(new AddPartCommand(QObject::tr("Ellipse"), m_scene, m_ellipse)); - //Set m_ellipse to nullptr for create new ellipse at next mouse press + //Set m_ellipse to nullptr for create new ellipse at next mouse press m_ellipse = nullptr; return true; @@ -79,15 +80,16 @@ bool ESEventAddEllipse::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { updateHelpCross(event -> scenePos()); if (!m_ellipse) return false; - QPointF mouse_pos = m_scene -> snapToGrid(event -> scenePos()); + QPointF pos = m_scene -> snapToGrid(event -> scenePos()); + if (pos == m_new_pos) return true; + m_new_pos = pos; - qreal width = (mouse_pos.x() - m_origin.x())*2; - qreal height = (mouse_pos.y() - m_origin.y())*2; + qreal width = (m_new_pos.x() - m_origin.x())*2; + qreal height = (m_new_pos.y() - m_origin.y())*2; + //calculates the position of the rectangle so that its center is at position (0,0) of m_ellipse + QPointF center(-width/2, -height/2); - QPointF pos(m_origin.x() - width/2, - m_origin.y() - height/2); - - m_ellipse -> setRect(QRectF(pos, QSizeF(width, height))); + m_ellipse -> setRect(QRectF(center, QSizeF(width, height))); return true; } diff --git a/sources/editor/esevent/eseventaddellipse.h b/sources/editor/esevent/eseventaddellipse.h index d31bf9b0c..4dedf3080 100644 --- a/sources/editor/esevent/eseventaddellipse.h +++ b/sources/editor/esevent/eseventaddellipse.h @@ -41,7 +41,7 @@ class ESEventAddEllipse : public ESEventInterface private: PartEllipse *m_ellipse; - QPointF m_origin; + QPointF m_origin, m_new_pos; }; #endif // ESEVENTADDELLIPSE_H diff --git a/sources/editor/esevent/eseventaddline.cpp b/sources/editor/esevent/eseventaddline.cpp index 7f201ff00..24e6a895e 100644 --- a/sources/editor/esevent/eseventaddline.cpp +++ b/sources/editor/esevent/eseventaddline.cpp @@ -57,8 +57,10 @@ bool ESEventAddLine::mousePressEvent(QGraphicsSceneMouseEvent *event) { //Create new line if (!m_line) { - m_line = new PartLine(m_editor, 0, m_scene); - m_line -> setLine(QLineF(pos, pos)); + m_line = new PartLine(m_editor); + m_scene -> addItem(m_line); + m_line -> setP1(pos); + m_line -> setP2(pos); return true; } diff --git a/sources/editor/esevent/eseventaddpolygon.cpp b/sources/editor/esevent/eseventaddpolygon.cpp index 0e6870226..81e8e636d 100644 --- a/sources/editor/esevent/eseventaddpolygon.cpp +++ b/sources/editor/esevent/eseventaddpolygon.cpp @@ -51,7 +51,8 @@ bool ESEventAddPolygon::mousePressEvent(QGraphicsSceneMouseEvent *event) { //create new polygon if (!m_polygon) { - m_polygon = new PartPolygon(m_editor, 0, m_scene); + m_polygon = new PartPolygon(m_editor); + m_scene -> addItem(m_polygon); m_polygon -> addPoint(pos); } diff --git a/sources/editor/esevent/eseventaddrect.cpp b/sources/editor/esevent/eseventaddrect.cpp index 6d194f289..d113324a7 100644 --- a/sources/editor/esevent/eseventaddrect.cpp +++ b/sources/editor/esevent/eseventaddrect.cpp @@ -44,23 +44,29 @@ ESEventAddRect::~ESEventAddRect() { * @param event * @return */ -bool ESEventAddRect::mousePressEvent(QGraphicsSceneMouseEvent *event) { - if (event -> button() == Qt::LeftButton) { +bool ESEventAddRect::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + if (event -> button() == Qt::LeftButton) + { if(!m_running) m_running = true; QPointF pos = m_scene->snapToGrid(event -> scenePos()); - //create new rectangle - if (!m_rect) { - m_rect = new PartRectangle(m_editor, 0, m_scene); + //create new rectangle, pos isn't define, + //so m_rect.pos = 0,0 , that mean event.scenePos is in same coordinate of item + //we don't need to map point for m_rect + if (!m_rect) + { + m_rect = new PartRectangle(m_editor); + m_scene -> addItem(m_rect); m_rect -> setRect(QRectF(pos, pos)); return true; } - //Add rectangle to scene + //Add rectangle to scene m_rect -> setRect(m_rect -> rect().normalized()); m_scene -> undoStack().push(new AddPartCommand(QObject::tr("Rectangle"), m_scene, m_rect)); - //Set m_rect to nullptr for create new rectangle at next mouse press + //Set m_rect to nullptr for create new rectangle at next mouse press m_rect = nullptr; return true; @@ -77,7 +83,7 @@ bool ESEventAddRect::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { updateHelpCross(event -> scenePos()); if (!m_rect) return false; - QRectF rect(m_rect -> rect().topLeft(), m_scene->snapToGrid(event -> scenePos())); + QRectF rect(m_rect->rectTopLeft(), m_scene->snapToGrid(event -> scenePos())); m_rect -> setRect(rect); return true; } diff --git a/sources/editor/esevent/eseventaddrect.h b/sources/editor/esevent/eseventaddrect.h index 9b26f68bc..db1a6fc29 100644 --- a/sources/editor/esevent/eseventaddrect.h +++ b/sources/editor/esevent/eseventaddrect.h @@ -19,6 +19,7 @@ #define ESEVENTADDRECT_H #include "eseventinterface.h" +#include class ElementScene; class PartRectangle; diff --git a/sources/editor/esevent/eseventaddterminal.cpp b/sources/editor/esevent/eseventaddterminal.cpp index 49bc4b442..7f701d821 100644 --- a/sources/editor/esevent/eseventaddterminal.cpp +++ b/sources/editor/esevent/eseventaddterminal.cpp @@ -29,7 +29,8 @@ ESEventAddTerminal::ESEventAddTerminal(ElementScene *scene) : ESEventInterface(scene) { - m_terminal = new PartTerminal(m_editor, 0, m_scene); + m_terminal = new PartTerminal(m_editor); + m_scene -> addItem(m_terminal); m_running = true; } @@ -63,7 +64,8 @@ bool ESEventAddTerminal::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { //Set new terminal with same rotation Qet::Orientation ori = m_terminal -> orientation(); - m_terminal = new PartTerminal(m_editor, 0, m_scene); + m_terminal = new PartTerminal(m_editor); + m_scene -> addItem(m_terminal); m_terminal -> setOrientation(ori); m_terminal -> setPos(m_scene -> snapToGrid(event -> scenePos())); diff --git a/sources/editor/graphicspart/abstractpartellipse.cpp b/sources/editor/graphicspart/abstractpartellipse.cpp new file mode 100644 index 000000000..0700fd5e1 --- /dev/null +++ b/sources/editor/graphicspart/abstractpartellipse.cpp @@ -0,0 +1,229 @@ +/* + Copyright 2006-2015 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech 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. + + QElectroTech 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 QElectroTech. If not, see . +*/ +#include "abstractpartellipse.h" + +/** + * @brief AbstractPartEllipse::AbstractPartEllipse + * Constructor + * @param editor : QETElementEditor of this part + * @param parent : parent item + */ +AbstractPartEllipse::AbstractPartEllipse(QETElementEditor *editor, QGraphicsItem *parent) : + CustomElementGraphicPart(editor, parent), + m_rect (QRectF(0, 0, 0, 0)), + m_start_angle (0), + m_span_angle (5760) +{} + +/** + * @brief AbstractPartEllipse::~AbstractPartEllipse + * Destructor + */ +AbstractPartEllipse::~AbstractPartEllipse() {} + +/** + * @brief AbstractPartEllipse::startUserTransformation + * Start the user-induced transformation, provided this primitive is contained + * within the initial_selection_rect bounding rectangle. + * @param initial_selection_rect + */ +void AbstractPartEllipse::startUserTransformation(const QRectF &initial_selection_rect) +{ + Q_UNUSED(initial_selection_rect) + // we keep track of our own rectangle at the moment in scene coordinates too + saved_points_.clear(); + saved_points_ << mapToScene(rect().topLeft()) << mapToScene(rect().bottomRight()); +} + +/** + * @brief AbstractPartEllipse::handleUserTransformation + * Handle the user-induced transformation from \a initial_selection_rect to \a new_selection_rect + * @param initial_selection_rect + * @param new_selection_rect + */ +void AbstractPartEllipse::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) +{ + QList mapped_points = mapPoints(initial_selection_rect, new_selection_rect, saved_points_); + setRect(QRectF(mapFromScene(mapped_points.at(0)), mapFromScene(mapped_points.at(1)))); +} + +/** + * @brief AbstractPartEllipse::boundingRect + * Bounding rectangle this part can fit into + * @return + */ +QRectF AbstractPartEllipse::boundingRect() const +{ + qreal adjust = (SHADOWS_HEIGHT + penWeight()) / 2; + //We add 0.5 because CustomElementGraphicPart::drawShadowShape + //draw a shape bigger of 0.5 when pen weight is to 0. + if (penWeight() == 0) adjust += 0.5; + QRectF r(m_rect.normalized()); + r.adjust(-adjust, -adjust, adjust, adjust); + return(r); +} + +/** + * @brief AbstractPartEllipse::sceneGeometricRect + * @return the minimum, margin-less rectangle this part can fit into in scene coordinates. + * It is different from boundingRect() because it is not supposed + * to imply any margin, and it is different from shape because it is a regular + * rectangle, not a complex shape. + */ +QRectF AbstractPartEllipse::sceneGeometricRect() const { + return(mapToScene(rect()).boundingRect()); +} + +/** + * @brief AbstractPartEllipse::sceneTopLeft + * @return return the top left of rectangle, in scene coordinate + */ +QPointF AbstractPartEllipse::sceneTopLeft() const { + return(mapToScene(rect().topLeft())); +} + +/** + * @brief AbstractPartEllipse::rect + * Returns the item's ellipse geometry as a QRectF. + */ +QRectF AbstractPartEllipse::rect() const { + return m_rect; +} + +/** + * @brief AbstractPartEllipse::setRect + * Sets the item's ellipse geometry to rect. + * The rectangle's left edge defines the left edge of the ellipse, + * and the rectangle's top edge describes the top of the ellipse + * The height and width of the rectangle describe the height and width of the ellipse. + * @param rect + */ +void AbstractPartEllipse::setRect(const QRectF &rect) +{ + if (rect == m_rect) return; + prepareGeometryChange(); + m_rect = rect; +} + +/** + * @brief AbstractPartEllipse::isUseless + * @return true if this part is irrelevant and does not deserve to be Retained / registered. + * An ellipse is relevant when is rect is not null. + */ +bool AbstractPartEllipse::isUseless() const { + return(rect().isNull()); +} + +/** + * @brief AbstractPartEllipse::setStartAngle + * Sets the start angle for an ellipse segment to angle, which is in 16ths of a degree. + * This angle is used together with spanAngle() for representing an ellipse segment (a pie). + * By default, the start angle is 0. + * @param start_angle + */ +void AbstractPartEllipse::setStartAngle(const int &start_angle) +{ + if (m_start_angle == start_angle) return; + m_start_angle = start_angle; + update(); +} + +/** + * @brief AbstractPartEllipse::setSpanAngle + * Returns the span angle of an ellipse segment in 16ths of a degree. + * This angle is used together with startAngle() for representing an ellipse segment (a pie). + * By default, this function returns 5760 (360 * 16, a full ellipse). + * @param span_angle + */ +void AbstractPartEllipse::setSpanAngle(const int &span_angle) +{ + if (m_span_angle == span_angle) return; + m_span_angle = span_angle; + update(); +} + +/** + * @brief AbstractPartEllipse::setCenterX + * Like setCenter but Y keep unchanged + * See setCenter(const QPointF ¢er) + * @param x + */ +void AbstractPartEllipse::setCenterX(const qreal x) +{ + QPointF pos = mapToParent(m_rect.center()); + pos.setX(x); + setCenter(pos); +} + +/** + * @brief AbstractPartEllipse::setCenterY + * Like setCenter but X keep unchanged + * See setCenter(const QPointF ¢er) + * @param y + */ +void AbstractPartEllipse::setCenterY(const qreal y) +{ + QPointF pos = mapToParent(m_rect.center()); + pos.setY(y); + setCenter(pos); +} + +/** + * @brief AbstractPartEllipse::setCenter + * This is a convenience method to setPos(). + * Adjust the position of this item, + * so that the center of the rectangle is at the given position(position in parent coordinates). + * @param center + */ +void AbstractPartEllipse::setCenter(const QPointF ¢er) +{ + QPointF pos = center - m_rect.center(); + setPos(pos); +} + +/** + * @brief AbstractPartEllipse::setWidth + * Set new width for rectangle. + * The center of rectangle is unchanged, + * The coordinates of the left side and right side of the rectangle change + * @param w + */ +void AbstractPartEllipse::setWidth(const qreal w) +{ + qreal new_width = qAbs(w); + QRectF current_rect = rect(); + current_rect.translate((new_width - current_rect.width()) / -2.0, 0.0); + current_rect.setWidth(new_width); + setRect(current_rect); +} + +/** + * @brief AbstractPartEllipse::setHeight + * Set new height for rectangle + * The center of rectangle is unchanged + * The coordinates of the top side and bottom side of the rectangle change + * @param h + */ +void AbstractPartEllipse::setHeight(const qreal h) +{ + qreal new_height = qAbs(h); + QRectF current_rect = rect(); + current_rect.translate(0.0, (new_height - current_rect.height()) / -2.0); + current_rect.setHeight(new_height); + setRect(current_rect); +} diff --git a/sources/editor/graphicspart/abstractpartellipse.h b/sources/editor/graphicspart/abstractpartellipse.h new file mode 100644 index 000000000..f89b67177 --- /dev/null +++ b/sources/editor/graphicspart/abstractpartellipse.h @@ -0,0 +1,92 @@ +/* + Copyright 2006-2015 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech 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. + + QElectroTech 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 QElectroTech. If not, see . +*/ +#ifndef ABSTRACTPARTELLIPSE_H +#define ABSTRACTPARTELLIPSE_H + +#include "customelementgraphicpart.h" + +/** + * @brief The AbstractPartEllipse class + * This is the base class for all ellipse based item like ellipse, circle, arc. + * This class only provide common method for edit the ellipse like rect that contain the ellipse. + * All coordinates is in item coordinate, except pos(), center(), centerX() and centerY() + * which are in parent coordinate (or scene if no parent). + * + * In several points, this class is a copy of QGraphicsEllipseItem with some change, + * (the use of Q_PROPERTY) to be easily used with Element editor. + */ +class AbstractPartEllipse : public CustomElementGraphicPart +{ + Q_OBJECT + Q_PROPERTY(int startAngle READ startAngle WRITE setStartAngle) + Q_PROPERTY(int spanAngle READ spanAngle WRITE setSpanAngle) + Q_PROPERTY(qreal centerX READ centerX WRITE setCenterX) + Q_PROPERTY(qreal centerY READ centerY WRITE setCenterY) + Q_PROPERTY(qreal diameter_h READ width WRITE setWidth) + Q_PROPERTY(qreal diameter_v READ height WRITE setHeight) + + // constructors, destructor + public: + AbstractPartEllipse(QETElementEditor *editor, QGraphicsItem * parent = 0); + virtual ~AbstractPartEllipse(); + + private: + AbstractPartEllipse(const AbstractPartEllipse &); + + // methods + public: + virtual void startUserTransformation (const QRectF &); + virtual void handleUserTransformation (const QRectF &, const QRectF &); + + //Coordinates + virtual QRectF boundingRect() const; + virtual QRectF sceneGeometricRect() const; + virtual QPointF sceneTopLeft() const; + + QRectF rect() const; + void setRect (const QRectF &rect); + virtual bool isUseless() const; + + int startAngle() const {return m_start_angle;} + void setStartAngle (const int &start_angle); + + int spanAngle () const {return m_span_angle;} + void setSpanAngle (const int &span_angle); + + qreal centerX() const {return mapToScene(rect().center()).x() ;} + void setCenterX(const qreal x); + + qreal centerY() const {return mapToScene(rect().center()).y();} + void setCenterY(const qreal y); + + void setCenter (const QPointF ¢er); + + qreal width() const {return rect().width();} + void setWidth(const qreal w); + + qreal height() const {return rect().height();} + void setHeight (const qreal h); + + protected: + QList saved_points_; + QRectF m_rect; + int m_start_angle; + int m_span_angle; +}; + +#endif // ABSTRACTPARTELLIPSE_H diff --git a/sources/editor/graphicspart/customelementgraphicpart.cpp b/sources/editor/graphicspart/customelementgraphicpart.cpp index 72820e776..b59405fa7 100644 --- a/sources/editor/graphicspart/customelementgraphicpart.cpp +++ b/sources/editor/graphicspart/customelementgraphicpart.cpp @@ -20,16 +20,27 @@ /** * @brief CustomElementGraphicPart::CustomElementGraphicPart * Default constructor. + * By default, item is selectable, send geometry change (Qt > 4.6), + * accept mouse left button and accept hover event * @param editor QETElement editor that belong this. */ -CustomElementGraphicPart::CustomElementGraphicPart(QETElementEditor *editor) : +CustomElementGraphicPart::CustomElementGraphicPart(QETElementEditor *editor, QGraphicsItem *parent) : + QGraphicsObject (parent), CustomElementPart(editor), + m_hovered (false), _linestyle(NormalStyle), _lineweight(NormalWeight), _filling(NoneFilling), _color(BlackColor), _antialiased(false) -{} +{ + setFlags(QGraphicsItem::ItemIsSelectable); +#if QT_VERSION >= 0x040600 + setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); +#endif + setAcceptedMouseButtons(Qt::LeftButton); + setAcceptHoverEvents(true); +} /** * @brief CustomElementGraphicPart::~CustomElementGraphicPart @@ -37,6 +48,36 @@ CustomElementGraphicPart::CustomElementGraphicPart(QETElementEditor *editor) : */ CustomElementGraphicPart::~CustomElementGraphicPart() {} +/** + * @brief CustomElementGraphicPart::drawCross + * Draw a cross at pos center + * @param center : center of cross + * @param painter : painter to use for draw cross, + * the painter state is restored at end of this method. + */ +void CustomElementGraphicPart::drawCross(const QPointF ¢er, QPainter *painter) +{ + painter -> save(); + painter -> setRenderHint(QPainter::Antialiasing, false); + painter -> setPen((painter -> brush().color() == QColor(Qt::black) && painter -> brush().isOpaque()) ? Qt::yellow : Qt::blue); + painter -> drawLine(QLineF(center.x() - 2.0, center.y(), center.x() + 2.0, center.y())); + painter -> drawLine(QLineF(center.x(), center.y() - 2.0, center.x(), center.y() + 2.0)); + painter -> restore(); +} + +/** + * @brief CustomElementGraphicPart::penWeight + * @return the weight of pen + */ +qreal CustomElementGraphicPart::penWeight() const +{ + if (_lineweight == NoneWeight || _lineweight == ThinWeight) return 0; + else if (_lineweight == NormalWeight) return 1; + else if (_lineweight == UltraWeight) return 2; + else if (_lineweight == BigWeight) return 5; + return 1; +} + /** * @brief CustomElementGraphicPart::stylesToXml * Write the curent style to xml element. @@ -206,3 +247,69 @@ void CustomElementGraphicPart::applyStylesToQPainter(QPainter &painter) const painter.setPen(pen); painter.setBrush(brush); } + +/** + * @brief CustomElementGraphicPart::drawShadowShape + * Draw a transparent blue shadow arround the shape of this item. + * The QPainterPathStroker used to draw shadows have a width of SHADOWS_HEIGHT + * Be carefull if penWeight of this item is to 0 the outline of strock is bigger of 0.5 + * @param painter : painter to use for draw this shadows + */ +void CustomElementGraphicPart::drawShadowShape(QPainter *painter) +{ + //@FIXME if pen weight is 0, the strock outline is SHADOWS_HEIGHT/2 + 0.5 + //may be because shape have no line weight + QPainterPathStroker strock; + strock.setWidth(SHADOWS_HEIGHT); + strock.setJoinStyle(Qt::RoundJoin); + + painter->save(); + QColor color(Qt::darkBlue); + color.setAlpha(50); + painter -> setBrush (QBrush (color)); + painter -> setPen (Qt::NoPen); + painter -> drawPath (strock.createStroke(shape())); + painter -> restore (); +} + +/** + * @brief CustomElementGraphicPart::itemChange + * Reimplemented from QGraphicsObject. + * If the item position change call updateCurrentPartEditor() + * the change is always send to QGraphicsObject + * @param change + * @param value + * @return the returned value of QGraphicsObject::itemChange + */ +QVariant CustomElementGraphicPart::itemChange(GraphicsItemChange change, const QVariant &value) +{ + if (scene()) + if (change == QGraphicsItem::ItemPositionChange || change == QGraphicsItem::ItemPositionHasChanged) + updateCurrentPartEditor(); + + return(QGraphicsObject::itemChange(change, value)); +} + +/** + * @brief CustomElementGraphicPart::hoverEnterEvent + * Reimplemented from QGraphicsObject. + * Set m_hovered to true + * @param event + */ +void CustomElementGraphicPart::hoverEnterEvent(QGraphicsSceneHoverEvent *event) +{ + m_hovered = true; + QGraphicsObject::hoverEnterEvent(event); +} + +/** + * @brief CustomElementGraphicPart::hoverLeaveEvent + * Reimplemented from QGraphicsObject. + * Set m_hovered to false + * @param event + */ +void CustomElementGraphicPart::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) +{ + m_hovered = false; + QGraphicsObject::hoverLeaveEvent(event); +} diff --git a/sources/editor/graphicspart/customelementgraphicpart.h b/sources/editor/graphicspart/customelementgraphicpart.h index 390734973..d3cb8e722 100644 --- a/sources/editor/graphicspart/customelementgraphicpart.h +++ b/sources/editor/graphicspart/customelementgraphicpart.h @@ -18,19 +18,22 @@ #ifndef CUSTOM_ELEMENT_GRAPHIC_PART_H #define CUSTOM_ELEMENT_GRAPHIC_PART_H -#include +#include #include "customelementpart.h" class QETElementEditor; class QPainter; + /** * @brief The CustomElementGraphicPart class * This class is the base for all home-made primitive like line, rectangle, ellipse etc.... * It provides methods and enums to manage style attributes available for primitive (color, pen style, etc...) */ -class CustomElementGraphicPart : public QObject, public CustomElementPart +class CustomElementGraphicPart : public QGraphicsObject, public CustomElementPart { + #define SHADOWS_HEIGHT 4.0 + Q_OBJECT //Made this Q_ENUMS to be used by the Q_PROPERTY system. @@ -62,15 +65,18 @@ class CustomElementGraphicPart : public QObject, public CustomElementPart // constructors, destructor public: - CustomElementGraphicPart(QETElementEditor *editor); + CustomElementGraphicPart(QETElementEditor *editor, QGraphicsItem *parent = 0); virtual ~CustomElementGraphicPart(); + static void drawCross (const QPointF ¢er, QPainter *painter); + //Getter and setter LineStyle lineStyle () const {return _linestyle;} void setLineStyle (const LineStyle ls) {_linestyle = ls;} LineWeight lineWeight () const {return _lineweight;} void setLineWeight (const LineWeight lw) {_lineweight = lw;} + qreal penWeight () const; Filling filling () const {return _filling;} void setFilling(const Filling f) {_filling = f;} @@ -92,8 +98,14 @@ class CustomElementGraphicPart : public QObject, public CustomElementPart void stylesFromXml(const QDomElement &); void resetStyles (); void applyStylesToQPainter(QPainter &) const; - + void drawShadowShape (QPainter *painter); + + QVariant itemChange(GraphicsItemChange change, const QVariant &value); + void hoverEnterEvent(QGraphicsSceneHoverEvent *event); + void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); + // attributes + bool m_hovered; private: LineStyle _linestyle; LineWeight _lineweight; diff --git a/sources/editor/graphicspart/partarc.cpp b/sources/editor/graphicspart/partarc.cpp index 9338b57a9..312c36bde 100644 --- a/sources/editor/graphicspart/partarc.cpp +++ b/sources/editor/graphicspart/partarc.cpp @@ -18,68 +18,68 @@ #include "partarc.h" /** - Constructeur - @param editor L'editeur d'element concerne - @param parent Le QGraphicsItem parent de cet arc - @param scene La scene sur laquelle figure cet arc -*/ -PartArc::PartArc(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) : - CustomElementGraphicPart(editor), - QGraphicsEllipseItem(parent, scene), - _angle(-90), - start_angle(0) + * @brief PartArc::PartArc + * Constructor + * @param editor : QETElementEditor of this part + * @param parent : parent item + */ +PartArc::PartArc(QETElementEditor *editor, QGraphicsItem *parent) : + AbstractPartEllipse(editor, parent) { - setFlags(QGraphicsItem::ItemIsSelectable); -#if QT_VERSION >= 0x040600 - setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); -#endif - setAcceptedMouseButtons(Qt::LeftButton); -} - -/// Destructeur -PartArc::~PartArc() { + m_start_angle = 0; + m_span_angle = -1440; } /** - Dessine l'arc de cercle - @param painter QPainter a utiliser pour rendre le dessin - @param options Options pour affiner le rendu - @param widget Widget sur lequel le rendu est effectue -*/ -void PartArc::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget) { + * @brief PartArc::~PartArc + * Destructor + */ +PartArc::~PartArc() {} + +/** + * @brief PartArc::paint + * Draw this arc + * @param painter + * @param options + * @param widget + */ +void PartArc::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget) +{ Q_UNUSED(widget); + applyStylesToQPainter(*painter); - // enleve systematiquement la couleur de fond + + //Always remove the brush painter -> setBrush(Qt::NoBrush); QPen t = painter -> pen(); t.setCosmetic(options && options -> levelOfDetail < 1.0); painter -> setPen(t); - if (isSelected()) { - // dessine l'ellipse en noir + if (isSelected()) + { + //Draw the ellipse in black painter -> drawEllipse(rect()); - - // dessine l'arc en rouge + + //Draw the arc in red t.setColor(Qt::red); painter -> setPen(t); } - painter -> drawArc(rect(), start_angle * 16, _angle * 16); - if (isSelected()) { - // dessine la croix au centre de l'ellipse - painter -> setRenderHint(QPainter::Antialiasing, false); - painter -> setPen((painter -> brush().color() == QColor(Qt::black) && painter -> brush().isOpaque()) ? Qt::yellow : Qt::blue); - QPointF center = rect().center(); - painter -> drawLine(QLineF(center.x() - 2.0, center.y(), center.x() + 2.0, center.y())); - painter -> drawLine(QLineF(center.x(), center.y() - 2.0, center.x(), center.y() + 2.0)); - } + painter -> drawArc(m_rect, m_start_angle, m_span_angle); + + if (m_hovered) + drawShadowShape(painter); + + if (isSelected()) + drawCross(m_rect.center(), painter); } /** - Exporte l'arc de cercle en XML - @param xml_document Document XML a utiliser pour creer l'element XML - @return un element XML decrivant l'arc de cercle -*/ + * @brief PartArc::toXml + * Export this arc in xml + * @param xml_document : Xml document to use for create the xml element. + * @return : an xml element that describe this arc + */ const QDomElement PartArc::toXml(QDomDocument &xml_document) const { QDomElement xml_element = xml_document.createElement("arc"); QPointF top_left(sceneTopLeft()); @@ -87,144 +87,41 @@ const QDomElement PartArc::toXml(QDomDocument &xml_document) const { xml_element.setAttribute("y", QString("%1").arg(top_left.y())); xml_element.setAttribute("width", QString("%1").arg(rect().width())); xml_element.setAttribute("height", QString("%1").arg(rect().height())); - xml_element.setAttribute("start", QString("%1").arg(start_angle)); - xml_element.setAttribute("angle", QString("%1").arg(_angle)); + //to maintain compatibility with the previous version, we write the angle in degrees. + xml_element.setAttribute("start", QString("%1").arg(m_start_angle / 16)); + xml_element.setAttribute("angle", QString("%1").arg(m_span_angle / 16)); stylesToXml(xml_element); return(xml_element); } /** - Importe les proprietes d'un arc de cercle depuis un element XML - @param qde Element XML a lire -*/ + * @brief PartArc::fromXml + * Import the properties of this arc from a xml element. + * @param qde : Xml document to use. + */ void PartArc::fromXml(const QDomElement &qde) { stylesFromXml(qde); - setRect( - QRectF( - mapFromScene( - qde.attribute("x", "0").toDouble(), - qde.attribute("y", "0").toDouble() - ), - QSizeF( - qde.attribute("width", "0").toDouble(), - qde.attribute("height", "0").toDouble() - ) - ) - ); - setStartAngle(qde.attribute("start", "0").toInt()); - setAngle(qde.attribute("angle", "-90").toInt()); + m_rect = QRectF(mapFromScene(qde.attribute("x", "0").toDouble(), + qde.attribute("y", "0").toDouble()), + QSizeF(qde.attribute("width", "0").toDouble(), + qde.attribute("height", "0").toDouble()) ); + + m_start_angle = qde.attribute("start", "0").toInt() * 16; + m_span_angle = qde.attribute("angle", "-1440").toInt() * 16; } /** - @return le coin superieur gauche du rectangle dans lequel s'inscrit - l'ellipse dont fait partie cet arc, dans les coordonnees de la scene. -*/ -QPointF PartArc::sceneTopLeft() const { - return(mapToScene(rect().topLeft())); -} - -/** - * @brief PartArc::setX - * @param x is the center of the rect bounding this ellipse + * @brief PartArc::shape + * @return the shape of this item */ -void PartArc::setX(const qreal x) { - QRectF current_rect = rect(); - QPointF current_pos = mapToScene(current_rect.center()); - setRect(current_rect.translated(x - current_pos.x(), 0.0)); -} +QPainterPath PartArc::shape() const +{ + QPainterPath shape; + shape.arcMoveTo(m_rect, m_start_angle/16); + shape.arcTo(m_rect, m_start_angle/16, m_span_angle/16); -/** - * @brief PartArc::setY - * @param y is the center of the rect bounding this ellipse - */ -void PartArc::setY(const qreal y) { - QRectF current_rect = rect(); - QPointF current_pos = mapToScene(current_rect.center()); - setRect(current_rect.translated(0.0, y - current_pos.y())); -} + QPainterPathStroker pps; + pps.setWidth(penWeight()); -/** - * @brief PartArc::setWidth - * @param w is the width of the rect bounding this ellipse - */ -void PartArc::setWidth(const qreal w) { - qreal new_width = qAbs(w); - QRectF current_rect = rect(); - current_rect.translate((new_width - current_rect.width()) / -2.0, 0.0); - current_rect.setWidth(new_width); - setRect(current_rect); -} - -/** - * @brief PartArc::setHeight - * @param h is the heigth of the rect bounding this ellipse - */ -void PartArc::setHeight(const qreal h) { - qreal new_height = qAbs(h); - QRectF current_rect = rect(); - current_rect.translate(0.0, (new_height - current_rect.height()) / -2.0); - current_rect.setHeight(new_height); - setRect(current_rect); -} - -/** - Gere les changements intervenant sur cette partie - @param change Type de changement - @param value Valeur numerique relative au changement -*/ -QVariant PartArc::itemChange(GraphicsItemChange change, const QVariant &value) { - if (scene()) { - if (change == QGraphicsItem::ItemPositionChange || change == QGraphicsItem::ItemPositionHasChanged) { - updateCurrentPartEditor(); - } - } - return(QGraphicsEllipseItem::itemChange(change, value)); -} - -/** - @return true si cette partie n'est pas pertinente et ne merite pas d'etre - conservee / enregistree. - Un arc est pertinent des lors que ses dimensions et son etendue ne sont - pas nulles. -*/ -bool PartArc::isUseless() const { - return(rect().isNull() || !angle()); -} - -/** - @return the minimum, margin-less rectangle this part can fit into, in scene - coordinates. It is different from boundingRect() because it is not supposed - to imply any margin, and it is different from shape because it is a regular - rectangle, not a complex shape. -*/ -QRectF PartArc::sceneGeometricRect() const { - return(mapToScene(rect()).boundingRect()); -} - -/** - Start the user-induced transformation, provided this primitive is contained - within the \a initial_selection_rect bounding rectangle. -*/ -void PartArc::startUserTransformation(const QRectF &initial_selection_rect) { - Q_UNUSED(initial_selection_rect) - saved_points_.clear(); - saved_points_ << mapToScene(rect().topLeft()) << mapToScene(rect().bottomRight()); -} - -/** - Handle the user-induced transformation from \a initial_selection_rect to \a new_selection_rect -*/ -void PartArc::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) { - QList mapped_points = mapPoints(initial_selection_rect, new_selection_rect, saved_points_); - setRect(QRectF(mapFromScene(mapped_points.at(0)), mapFromScene(mapped_points.at(1)))); -} - -/** - @return le rectangle delimitant cette partie. -*/ -QRectF PartArc::boundingRect() const { - qreal adjust = 1.5; - QRectF r(QGraphicsEllipseItem::boundingRect().normalized()); - r.adjust(-adjust, -adjust, adjust, adjust); - return(r); + return (pps.createStroke(shape)); } diff --git a/sources/editor/graphicspart/partarc.h b/sources/editor/graphicspart/partarc.h index 4507d50d2..048bb389e 100644 --- a/sources/editor/graphicspart/partarc.h +++ b/sources/editor/graphicspart/partarc.h @@ -17,79 +17,41 @@ */ #ifndef PART_ARC_H #define PART_ARC_H -#include -#include "customelementgraphicpart.h" + +#include "abstractpartellipse.h" + /** - This class represents an elliptical arc primitive which may be used to - compose the drawing of an electrical element within the element editor. -*/ -class PartArc : public CustomElementGraphicPart, public QGraphicsEllipseItem { - Q_OBJECT - // constructors, destructor - public: - PartArc(QETElementEditor *, QGraphicsItem * = 0, QGraphicsScene * = 0); - virtual ~PartArc(); - - private: - PartArc(const PartArc &); - - // attributes - private: - int _angle; - int start_angle; - - // methods - public: - enum { Type = UserType + 1101 }; - /** - Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a - PartArc. - @return the QGraphicsItem type - */ - virtual int type() const { return Type; } - virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0); - virtual QString name() const { return(QObject::tr("arc", "element part name")); } - virtual QString xmlName() const { return(QString("arc")); } - virtual const QDomElement toXml(QDomDocument &) const; - virtual void fromXml(const QDomElement &); - virtual QPointF sceneTopLeft() const; - virtual QRectF boundingRect() const; - virtual bool isUseless() const; - virtual QRectF sceneGeometricRect() const; - virtual void startUserTransformation(const QRectF &); - virtual void handleUserTransformation(const QRectF &, const QRectF &); + * @brief The PartArc class + * This class represents an elliptical arc primitive which may be used to + * compose the drawing of an electrical element within the element editor. + */ +class PartArc : public AbstractPartEllipse +{ + Q_OBJECT - ///PROPERT - // X value - Q_PROPERTY(qreal x READ x WRITE setX) - qreal x() const {return mapToScene(rect().center()).x() ;} - void setX(const qreal x); - //Y value - Q_PROPERTY(qreal y READ y WRITE setY) - qreal y() const {return mapToScene(rect().center()).y();} - void setY(const qreal y); - // horizontal diameter - Q_PROPERTY(qreal diameter_h READ width WRITE setWidth) - qreal width() const {return rect().width();} - void setWidth(const qreal w); - // vertical diameter - Q_PROPERTY(qreal diameter_v READ height WRITE setHeight) - qreal height() const {return rect().height();} - void setHeight (const qreal h); - // start angle - Q_PROPERTY(int start_angle READ startAngle WRITE setStartAngle) - int startAngle() const {return start_angle;} - void setStartAngle(const int sa){start_angle = sa;} - // angle value - Q_PROPERTY(int angle READ angle WRITE setAngle) - int angle() const {return _angle;} - void setAngle(const int a) {_angle = a;} - - - protected: - QVariant itemChange(GraphicsItemChange, const QVariant &); + public: + PartArc(QETElementEditor *editor, QGraphicsItem *parent = 0); + virtual ~PartArc(); private: - QList saved_points_; + PartArc(const PartArc &); + + // methods + public: + enum { Type = UserType + 1101 }; + /** + * Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a PartArc. + * @return the QGraphicsItem type + */ + virtual int type() const { return Type; } + virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0); + + //Name and XML + virtual QString name() const { return(QObject::tr("arc", "element part name")); } + virtual QString xmlName() const { return(QString("arc")); } + virtual const QDomElement toXml (QDomDocument &) const; + virtual void fromXml (const QDomElement &); + + virtual QPainterPath shape() const; }; #endif diff --git a/sources/editor/graphicspart/partellipse.cpp b/sources/editor/graphicspart/partellipse.cpp index e5d7a67a2..62a503a2a 100644 --- a/sources/editor/graphicspart/partellipse.cpp +++ b/sources/editor/graphicspart/partellipse.cpp @@ -18,188 +18,113 @@ #include "partellipse.h" /** - Constructeur - @param editor L'editeur d'element concerne - @param parent Le QGraphicsItem parent de cette ellipse - @param scene La scene sur laquelle figure cette ellipse -*/ -PartEllipse::PartEllipse(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) : CustomElementGraphicPart(editor), QGraphicsEllipseItem(parent, scene) { - setFlags(QGraphicsItem::ItemIsSelectable); -#if QT_VERSION >= 0x040600 - setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); -#endif - setAcceptedMouseButtons(Qt::LeftButton); -} - -/// Destructeur -PartEllipse::~PartEllipse() { -} + * @brief PartEllipse::PartEllipse + * Constructor + * @param editor : QETElementEditor of this part + * @param parent : parent item + */ +PartEllipse::PartEllipse(QETElementEditor *editor, QGraphicsItem *parent) : + AbstractPartEllipse(editor, parent) +{} /** - Dessine l'ellipse - @param painter QPainter a utiliser pour rendre le dessin - @param options Options pour affiner le rendu - @param widget Widget sur lequel le rendu est effectue -*/ -void PartEllipse::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget) { + * @brief PartEllipse::~PartEllipse + * Destructor + */ +PartEllipse::~PartEllipse() {} + +/** + * @brief PartEllipse::paint + * Draw this ellpise + * @param painter + * @param options + * @param widget + */ +void PartEllipse::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget) +{ Q_UNUSED(widget); applyStylesToQPainter(*painter); + QPen t = painter -> pen(); t.setCosmetic(options && options -> levelOfDetail < 1.0); - if (isSelected()) { + + if (isSelected()) t.setColor(Qt::red); - } + painter -> setPen(t); painter -> drawEllipse(rect()); - if (isSelected()) { - painter -> setRenderHint(QPainter::Antialiasing, false); - painter -> setPen((painter -> brush().color() == QColor(Qt::black) && painter -> brush().isOpaque()) ? Qt::yellow : Qt::blue); - QPointF center = rect().center(); - painter -> drawLine(QLineF(center.x() - 2.0, center.y(), center.x() + 2.0, center.y())); - painter -> drawLine(QLineF(center.x(), center.y() - 2.0, center.x(), center.y() + 2.0)); - } + + if (m_hovered) + drawShadowShape(painter); + + if (isSelected()) + drawCross(m_rect.center(), painter); } /** - Exporte l'ellipse en XML - @param xml_document Document XML a utiliser pour creer l'element XML - @return un element XML decrivant l'ellipse -*/ -const QDomElement PartEllipse::toXml(QDomDocument &xml_document) const { + * @brief PartEllipse::toXml + * Export this ellipse in xml + * @param xml_document : Xml document to use for create the xml element. + * @return : an xml element that describe this ellipse + */ +const QDomElement PartEllipse::toXml(QDomDocument &xml_document) const +{ QDomElement xml_element; - if (qFuzzyCompare(rect().width(), rect().height())) { + if (qFuzzyCompare(rect().width(), rect().height())) + { xml_element = xml_document.createElement("circle"); xml_element.setAttribute("diameter", QString("%1").arg(rect().width())); - } else { + } + else + { xml_element = xml_document.createElement("ellipse"); xml_element.setAttribute("width", QString("%1").arg(rect().width())); xml_element.setAttribute("height", QString("%1").arg(rect().height())); } + QPointF top_left(sceneTopLeft()); xml_element.setAttribute("x", QString("%1").arg(top_left.x())); xml_element.setAttribute("y", QString("%1").arg(top_left.y())); + stylesToXml(xml_element); + return(xml_element); } /** - Importe les proprietes d'une ellipse depuis un element XML - @param qde Element XML a lire -*/ -void PartEllipse::fromXml(const QDomElement &qde) { + * @brief PartEllipse::fromXml + * Import the properties of this ellipse from a xml element. + * @param qde : Xml document to use. + */ +void PartEllipse::fromXml(const QDomElement &qde) +{ stylesFromXml(qde); qreal width, height; - if (qde.tagName() == "ellipse") { + + if (qde.tagName() == "ellipse") + { width = qde.attribute("width", "0").toDouble(); height = qde.attribute("height", "0").toDouble(); - } else { + } + else width = height = qde.attribute("diameter", "0").toDouble(); - } - setRect( - QRectF( - mapFromScene( - qde.attribute("x", "0").toDouble(), - qde.attribute("y", "0").toDouble() - ), - QSizeF(width, height) - ) - ); -} -void PartEllipse::setX(const qreal x) { - QRectF current_rect = rect(); - QPointF current_pos = mapToScene(current_rect.center()); - setRect(current_rect.translated(x - current_pos.x(), 0.0)); -} - -void PartEllipse::setY(const qreal y) { - QRectF current_rect = rect(); - QPointF current_pos = mapToScene(current_rect.center()); - setRect(current_rect.translated(0.0, y - current_pos.y())); -} - -void PartEllipse::setWidth(const qreal w) { - qreal new_width = qAbs(w); - QRectF current_rect = rect(); - current_rect.translate((new_width - current_rect.width()) / -2.0, 0.0); - current_rect.setWidth(new_width); - setRect(current_rect); -} - -void PartEllipse::setHeight(const qreal h) { - qreal new_height = qAbs(h); - QRectF current_rect = rect(); - current_rect.translate(0.0, (new_height - current_rect.height()) / -2.0); - current_rect.setHeight(new_height); - setRect(current_rect); + m_rect = QRectF(mapFromScene(qde.attribute("x", "0").toDouble(), + qde.attribute("y", "0").toDouble()), + QSizeF(width, height)); } /** - Gere les changements intervenant sur cette partie - @param change Type de changement - @param value Valeur numerique relative au changement -*/ -QVariant PartEllipse::itemChange(GraphicsItemChange change, const QVariant &value) { - if (scene()) { - if (change == QGraphicsItem::ItemPositionChange || change == QGraphicsItem::ItemPositionHasChanged) { - updateCurrentPartEditor(); - } - } - return(QGraphicsEllipseItem::itemChange(change, value)); -} + * @brief PartEllipse::shape + * @return the shape of this item + */ +QPainterPath PartEllipse::shape() const +{ + QPainterPath shape; + shape.addEllipse(m_rect); -/** - @return le coin superieur gauche du rectangle dans lequel s'inscrit - l'ellipse, dans les coordonnees de la scene. -*/ -QPointF PartEllipse::sceneTopLeft() const { - return(mapToScene(rect().topLeft())); -} + QPainterPathStroker pps; + pps.setWidth(penWeight()); -/** - @return true si cette partie n'est pas pertinente et ne merite pas d'etre - conservee / enregistree. - Une ellipse est pertinente des lors que ses dimensions ne sont pas nulles -*/ -bool PartEllipse::isUseless() const { - return(rect().isNull()); -} - -/** - @return the minimum, margin-less rectangle this part can fit into, in scene - coordinates. It is different from boundingRect() because it is not supposed - to imply any margin, and it is different from shape because it is a regular - rectangle, not a complex shape. -*/ -QRectF PartEllipse::sceneGeometricRect() const { - return(mapToScene(rect()).boundingRect()); -} - -/** - Start the user-induced transformation, provided this primitive is contained - within the \a initial_selection_rect bounding rectangle. -*/ -void PartEllipse::startUserTransformation(const QRectF &initial_selection_rect) { - Q_UNUSED(initial_selection_rect) - // we keep track of our own rectangle at the moment in scene coordinates too - saved_points_.clear(); - saved_points_ << mapToScene(rect().topLeft()) << mapToScene(rect().bottomRight()); -} - -/** - Handle the user-induced transformation from \a initial_selection_rect to \a new_selection_rect -*/ -void PartEllipse::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) { - QList mapped_points = mapPoints(initial_selection_rect, new_selection_rect, saved_points_); - setRect(QRectF(mapFromScene(mapped_points.at(0)), mapFromScene(mapped_points.at(1)))); -} - -/** - @return le rectangle delimitant cette partie. -*/ -QRectF PartEllipse::boundingRect() const { - qreal adjust = 1.5; - QRectF r(QGraphicsEllipseItem::boundingRect().normalized()); - r.adjust(-adjust, -adjust, adjust, adjust); - return(r); + return (pps.createStroke(shape)); } diff --git a/sources/editor/graphicspart/partellipse.h b/sources/editor/graphicspart/partellipse.h index 499a132d5..81093d6bd 100644 --- a/sources/editor/graphicspart/partellipse.h +++ b/sources/editor/graphicspart/partellipse.h @@ -17,65 +17,42 @@ */ #ifndef PART_ELLIPSE_H #define PART_ELLIPSE_H -#include -#include "customelementgraphicpart.h" -/** - This class represents an ellipse primitive which may be used to compose the - drawing of an electrical element within the element editor. -*/ -class PartEllipse : public CustomElementGraphicPart, public QGraphicsEllipseItem { - Q_OBJECT - // constructors, destructor - public: - PartEllipse(QETElementEditor *, QGraphicsItem * = 0, QGraphicsScene * = 0); - virtual ~PartEllipse(); - - private: - PartEllipse(const PartEllipse &); - - // methods - public: - enum { Type = UserType + 1103 }; - /** - Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a - PartEllipse. - @return the QGraphicsItem type - */ - virtual int type() const { return Type; } - virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0); - virtual QString name() const { return(QObject::tr("ellipse", "element part name")); } - virtual QString xmlName() const { return(QString("ellipse")); } - virtual const QDomElement toXml(QDomDocument &) const; - virtual void fromXml(const QDomElement &); - virtual QPointF sceneTopLeft() const; - virtual QRectF boundingRect() const; - virtual bool isUseless() const; - virtual QRectF sceneGeometricRect() const; - virtual void startUserTransformation(const QRectF &); - virtual void handleUserTransformation(const QRectF &, const QRectF &); - ///PROPERTY - // X value - Q_PROPERTY(qreal x READ x WRITE setX) - qreal x() const {return mapToScene(rect().center()).x() ;} - void setX(const qreal x); - // Y value - Q_PROPERTY(qreal y READ y WRITE setY) - qreal y() const {return mapToScene(rect().center()).y();} - void setY(const qreal y); - // horizontal diameter - Q_PROPERTY(qreal diameter_h READ width WRITE setWidth) - qreal width() const {return rect().width();} - void setWidth(const qreal w); - // vertical diameter - Q_PROPERTY(qreal diameter_v READ height WRITE setHeight) - qreal height() const {return rect().height();} - void setHeight (const qreal h); - - protected: - QVariant itemChange(GraphicsItemChange, const QVariant &); +#include "abstractpartellipse.h" + +/** + * @brief The PartEllipse class + * This class represents an ellipse primitive which may be used to compose the + * drawing of an electrical element within the element editor. + */ +class PartEllipse : public AbstractPartEllipse +{ + Q_OBJECT + + // constructors, destructor + public: + PartEllipse(QETElementEditor *editor, QGraphicsItem * parent = 0); + virtual ~PartEllipse(); private: - QList saved_points_; + PartEllipse(const PartEllipse &); + + // methods + public: + enum { Type = UserType + 1103 }; + /** + * Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a PartEllipse. + * @return the QGraphicsItem type + */ + virtual int type() const { return Type; } + virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0); + + //Name and XML + virtual QString name() const { return(QObject::tr("ellipse", "element part name")); } + virtual QString xmlName() const { return(QString("ellipse")); } + virtual const QDomElement toXml (QDomDocument &) const; + virtual void fromXml (const QDomElement &); + + virtual QPainterPath shape() const; }; #endif diff --git a/sources/editor/graphicspart/partline.cpp b/sources/editor/graphicspart/partline.cpp index 0b9637d5b..e3a87a747 100644 --- a/sources/editor/graphicspart/partline.cpp +++ b/sources/editor/graphicspart/partline.cpp @@ -18,148 +18,80 @@ #include "partline.h" #include + /** - Constructeur - @param editor L'editeur d'element concerne - @param parent Le QGraphicsItem parent de cette ligne - @param scene La scene sur laquelle figure cette ligne -*/ -PartLine::PartLine(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) : - CustomElementGraphicPart(editor), - QGraphicsLineItem(parent, scene), + * @brief PartLine::PartLine + * Constructor + * @param editor : QETElementEditor of this part + * @param parent : parent item + */ +PartLine::PartLine(QETElementEditor *editor, QGraphicsItem *parent) : + CustomElementGraphicPart(editor, parent), first_end(Qet::None), first_length(1.5), second_end(Qet::None), second_length(1.5) -{ - setFlags(QGraphicsItem::ItemIsSelectable); -#if QT_VERSION >= 0x040600 - setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); -#endif - setAcceptedMouseButtons(Qt::LeftButton); -} +{} /// Destructeur -PartLine::~PartLine() { -} +PartLine::~PartLine() {} /** - @param end_type Type d'extremite - @return Le nombre de "longueurs" requises pour dessiner une extremite de type end_type -*/ -uint PartLine::requiredLengthForEndType(const Qet::EndType &end_type) { + * @brief PartLine::requiredLengthForEndType + * @param end_type + * @return the number of "length" needed to draw a extremity of type Qet::EndType. + */ +uint PartLine::requiredLengthForEndType(const Qet::EndType &end_type) +{ uint length_count_required = 0; - if (end_type == Qet::Circle || end_type == Qet::Diamond) { + + if (end_type == Qet::Circle || end_type == Qet::Diamond) length_count_required = 2; - } else if (end_type == Qet::Simple || end_type == Qet::Triangle) { + else if (end_type == Qet::Simple || end_type == Qet::Triangle) length_count_required = 1; - } + return(length_count_required); } /** - Dessine la ligne - @param painter QPainter a utiliser pour rendre le dessin - @param options Options pour affiner le rendu - @param widget Widget sur lequel le rendu est effectue -*/ -void PartLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget) { + * @brief PartLine::paint + * Draw this line + * @param painter + * @param options + * @param widget + */ +void PartLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget) +{ Q_UNUSED(widget); - // inutile de dessiner une ligne nulle - if (line().p1() == line().p2()) return; + if (isUseless()) return; + applyStylesToQPainter(*painter); QPen t = painter -> pen(); t.setJoinStyle(Qt::MiterJoin); t.setCosmetic(options && options -> levelOfDetail < 1.0); - if (isSelected()) { + + if (isSelected()) t.setColor(Qt::red); - } + painter -> setPen(t); - QPointF point1(line().p1()); - QPointF point2(line().p2()); - - qreal line_length(line().length()); - qreal pen_width = painter -> pen().widthF(); - - qreal length1 = first_length; - qreal length2 = second_length; - - //debugPaint(painter); - - // determine s'il faut dessiner les extremites - bool draw_1st_end, draw_2nd_end; - qreal reduced_line_length = line_length - (length1 * requiredLengthForEndType(first_end)); - draw_1st_end = first_end && reduced_line_length >= 0; - if (draw_1st_end) { - reduced_line_length -= (length2 * requiredLengthForEndType(second_end)); - } else { - reduced_line_length = line_length - (length2 * requiredLengthForEndType(second_end)); - } - draw_2nd_end = second_end && reduced_line_length >= 0; - - // dessine la premiere extremite - QPointF start_point, stop_point; - if (draw_1st_end) { - QList four_points1(fourEndPoints(point1, point2, length1)); - if (first_end == Qet::Circle) { - painter -> drawEllipse(QRectF(four_points1[0] - QPointF(length1, length1), QSizeF(length1 * 2.0, length1 * 2.0))); - start_point = four_points1[1]; - } else if (first_end == Qet::Diamond) { - painter -> drawPolygon(QPolygonF() << four_points1[1] << four_points1[2] << point1 << four_points1[3]); - start_point = four_points1[1]; - } else if (first_end == Qet::Simple) { - painter -> drawPolyline(QPolygonF() << four_points1[3] << point1 << four_points1[2]); - start_point = point1; - - } else if (first_end == Qet::Triangle) { - painter -> drawPolygon(QPolygonF() << four_points1[0] << four_points1[2] << point1 << four_points1[3]); - start_point = four_points1[0]; - } - - // ajuste le depart selon l'epaisseur du trait - if (pen_width && (first_end == Qet::Simple || first_end == Qet::Circle)) { - start_point = QLineF(start_point, point2).pointAt(pen_width / 2.0 / line_length); - } - } else { - start_point = point1; - } - - // dessine la seconde extremite - if (draw_2nd_end) { - QList four_points2(fourEndPoints(point2, point1, length2)); - if (second_end == Qet::Circle) { - painter -> drawEllipse(QRectF(four_points2[0] - QPointF(length2, length2), QSizeF(length2 * 2.0, length2 * 2.0))); - stop_point = four_points2[1]; - } else if (second_end == Qet::Diamond) { - painter -> drawPolygon(QPolygonF() << four_points2[2] << point2 << four_points2[3] << four_points2[1]); - stop_point = four_points2[1]; - } else if (second_end == Qet::Simple) { - painter -> drawPolyline(QPolygonF() << four_points2[3] << point2 << four_points2[2]); - stop_point = point2; - } else if (second_end == Qet::Triangle) { - painter -> drawPolygon(QPolygonF() << four_points2[0] << four_points2[2] << point2 << four_points2[3] << four_points2[0]); - stop_point = four_points2[0]; - } - - // ajuste l'arrivee selon l'epaisseur du trait - if (pen_width && (second_end == Qet::Simple || second_end == Qet::Circle)) { - stop_point = QLineF(point1, stop_point).pointAt((line_length - (pen_width / 2.0)) / line_length); - } - } else { - stop_point = point2; - } - - painter -> drawLine(start_point, stop_point); + if (first_end || second_end) + painter -> drawPath(path()); + else + painter -> drawLine(m_line); + + if (m_hovered) + drawShadowShape(painter); } /** - Exporte la ligne en XML - @param xml_document Document XML a utiliser pour creer l'element XML - @return un element XML decrivant la ligne -*/ -const QDomElement PartLine::toXml(QDomDocument &xml_document) const { - + * @brief PartLine::toXml + * Export this line in xml + * @param xml_document : Xml document to use for create the xml element. + * @return an xml element that describe this line + */ +const QDomElement PartLine::toXml(QDomDocument &xml_document) const +{ QPointF p1(sceneP1()); QPointF p2(sceneP2()); @@ -178,49 +110,29 @@ const QDomElement PartLine::toXml(QDomDocument &xml_document) const { } /** - Importe les proprietes d'une ligne depuis un element XML - @param qde Element XML a lire -*/ + * @brief PartLine::fromXml + * Import the properties of this line from a xml element. + * @param qde : Xml document to use + */ void PartLine::fromXml(const QDomElement &qde) { stylesFromXml(qde); - setLine( - QLineF( - mapFromScene( - qde.attribute("x1", "0").toDouble(), - qde.attribute("y1", "0").toDouble() - ), - mapFromScene( - qde.attribute("x2", "0").toDouble(), - qde.attribute("y2", "0").toDouble() - ) - ) - ); - first_end = Qet::endTypeFromString(qde.attribute("end1")); - first_length = qde.attribute("length1", "1.5").toDouble(); - second_end = Qet::endTypeFromString(qde.attribute("end2")); + m_line = QLineF(mapFromScene(qde.attribute("x1", "0").toDouble(), + qde.attribute("y1", "0").toDouble()), + mapFromScene(qde.attribute("x2", "0").toDouble(), + qde.attribute("y2", "0").toDouble())); + + first_end = Qet::endTypeFromString(qde.attribute("end1")); + first_length = qde.attribute("length1", "1.5").toDouble(); + second_end = Qet::endTypeFromString(qde.attribute("end2")); second_length = qde.attribute("length2", "1.5").toDouble(); } /** - * @brief PartLine::setX1 - * set X of P1 - * @param x1 + * @brief PartLine::p1 + * @return the point p1 of line. */ -void PartLine::setX1(qreal x1) { - QPointF p = line().p1(); - p.setX(x1); - setLine(QLineF(p, line().p2())); -} - -/** - * @brief PartLine::setY1 - * set y of P1 - * @param y1 - */ -void PartLine::setY1(qreal y1) { - QPointF p = line().p1(); - p.setY(y1); - setLine(QLineF(p, line().p2())); +QPointF PartLine::p1() const { + return m_line.p1(); } /** @@ -228,30 +140,19 @@ void PartLine::setY1(qreal y1) { * set first point to P1 * @param p1 */ -void PartLine::setP1(QPointF p1) { - setLine(QLineF(p1, line().p2())); +void PartLine::setP1(const QPointF &p1) +{ + if (p1 == m_line.p1()) return; + prepareGeometryChange(); + m_line.setP1(p1); } /** - * @brief PartLine::setX2 - * set x of P2 - * @param x2 + * @brief PartLine::p2 + * @return the point p2 of line */ -void PartLine::setX2(qreal x2) { - QPointF p = line().p2(); - p.setX(x2); - setLine(QLineF(line().p1(), p)); -} - -/** - * @brief PartLine::setY2 - * set y of P2 - * @param y2 - */ -void PartLine::setY2(qreal y2) { - QPointF p = line().p2(); - p.setY(y2); - setLine(QLineF(line().p1(), p)); +QPointF PartLine::p2() const { + return m_line.p2(); } /** @@ -259,105 +160,91 @@ void PartLine::setY2(qreal y2) { * set second point to P2 * @param p2 */ -void PartLine::setP2(QPointF p2) { - setLine(QLineF(line().p1(), p2)); +void PartLine::setP2(const QPointF &p2) +{ + if (p2 == m_line.p2()) return; + prepareGeometryChange(); + m_line.setP2(p2); } /** - Gere les changements intervenant sur cette partie - @param change Type de changement - @param value Valeur numerique relative au changement -*/ -QVariant PartLine::itemChange(GraphicsItemChange change, const QVariant &value) { - if (scene()) { - if (change == QGraphicsItem::ItemPositionChange || change == QGraphicsItem::ItemPositionHasChanged) { - updateCurrentPartEditor(); - } - } - return(QGraphicsLineItem::itemChange(change, value)); -} - -/** - @return le premier point, dans les coordonnees de la scene. -*/ + * @brief PartLine::sceneP1 + * @return the point p1 in scene coordinate + */ QPointF PartLine::sceneP1() const { - return(mapToScene(line().p1())); + return(mapToScene(p1())); } /** - @return le second point, dans les coordonnees de la scene. -*/ + * @brief PartLine::sceneP2 + * @return the point p2 in scen coordinate + */ QPointF PartLine::sceneP2() const { - return(mapToScene(line().p2())); + return(mapToScene(p2())); } /** - @return la forme selectionnable de la ligne -*/ -QPainterPath PartLine::shape() const { - QList points = fourShapePoints(); - QPainterPath t; - t.setFillRule(Qt::WindingFill); - t.moveTo(points.at(0)); - t.lineTo(points.at(1)); - t.lineTo(points.at(2)); - t.lineTo(points.at(3)); - t.lineTo(points.at(0)); - - // n'en fait pas plus si la ligne se ramene a un point - if (line().p1() == line().p2()) return(t); - - // ajoute un cercle pour l'extremite 1 si besoin - if (first_end) { - QPainterPath t2; - t2.addEllipse(firstEndCircleRect()); - t.addPath(t2.subtracted(t)); + * @brief PartLine::shape + * @return the shape of this item + */ +QPainterPath PartLine::shape() const +{ + QPainterPath shape; + + //We calcul path only if there is an end type + //Else we just draw a line + if (first_end || second_end) + shape.addPath(path()); + else + { + shape.moveTo(m_line.p1()); + shape.lineTo(m_line.p2()); } - - // ajoute un cercle pour l'extremite 2 si besoin - if (second_end) { - QPainterPath t2; - t2.addEllipse(secondEndCircleRect()); - t.addPath(t2.subtracted(t)); - } - - return(t); + + QPainterPathStroker pps; + pps.setWidth(penWeight()); + + return (pps.createStroke(shape)); } /** - @return une liste contenant les deux points de la droite + les 4 points entourant ces deux points -*/ -QList PartLine::fourShapePoints() const { + * @brief PartLine::fourShapePoints + * @return a list with the two points that delimite the line + * + the four points surrounding these two points + */ +QList PartLine::fourShapePoints() const +{ const qreal marge = 2.0; - // on a donc A(xa , ya) et B(xb, yb) - QPointF a = line().p1(); - QPointF b = line().p2(); + + QPointF a = m_line.p1(); + QPointF b = m_line.p2(); QList result; - // cas particulier : la droite se ramene a un point - if (a == b) { + //Special case, the line is defined by one point + if (a == b) + { result << QPointF(a.x() - marge, a.y() - marge); result << QPointF(a.x() - marge, a.y() + marge); result << QPointF(a.x() + marge, a.y() + marge); result << QPointF(a.x() + marge, a.y() - marge); - } else { - - // on calcule le vecteur AB : (xb-xa, yb-ya) + } + else + { + //We calcule the vector AB : (xb-xa, yb-ya) QPointF v_ab = b - a; - // et la distance AB : racine des coordonnees du vecteur au carre + //And the distance AB: root of the coordinates of the vector squared qreal ab = sqrt(pow(v_ab.x(), 2) + pow(v_ab.y(), 2)); - // ensuite on definit le vecteur u(a, b) qui est egal au vecteur AB divise - // par sa longueur et multiplie par la longueur de la marge que tu veux - // laisser + //Next, we define the vector u(a, b) wich is equal to the vector AB divided + //by is length and multiplied by the length of marge. QPointF u = v_ab / ab * marge; - // on definit le vecteur v(-b , a) qui est perpendiculaire a AB + //We define the vector v(-b, a) wich is perpendicular to AB QPointF v(-u.y(), u.x()); - QPointF m = -u + v; // on a le vecteur M = -u + v - QPointF n = -u - v; // et le vecteur N=-u-v + QPointF m = -u + v; // we have vector M = -u + v + QPointF n = -u - v; // and vector N=-u-v QPointF h = a + m; // H = A + M QPointF k = a + n; // K = A + N QPointF i = b - n; // I = B - N @@ -369,14 +256,14 @@ QList PartLine::fourShapePoints() const { } /** - @return le rectangle encadrant l'integralite de la premiere extremite -*/ -QRectF PartLine::firstEndCircleRect() const { - QList interesting_points = fourEndPoints( - line().p1(), - line().p2(), - first_length - ); + * @brief PartLine::firstEndCircleRect + * @return the rectangle bordering the entirety of the first extremity + */ +QRectF PartLine::firstEndCircleRect() const +{ + QList interesting_points = fourEndPoints(m_line.p1(), + m_line.p2(), + first_length); QRectF end_rect( interesting_points[0] - QPointF(first_length, first_length), @@ -387,14 +274,13 @@ QRectF PartLine::firstEndCircleRect() const { } /** - @return le rectangle encadrant l'integralite de la seconde extremite -*/ + * @brief PartLine::secondEndCircleRect + * @return the rectangle bordering the entirety of the second extremity + */ QRectF PartLine::secondEndCircleRect() const { - QList interesting_points = fourEndPoints( - line().p2(), - line().p1(), - second_length - ); + QList interesting_points = fourEndPoints(m_line.p2(), + m_line.p1(), + second_length); QRectF end_rect( interesting_points[0] - QPointF(second_length, second_length), @@ -405,13 +291,15 @@ QRectF PartLine::secondEndCircleRect() const { } /** - Affiche differentes composantes du dessin : - - le boundingRect - - les point speciaux a chaque extremite - - la quadrature du cercle a chaque extremite, meme si celle-ci est d'un - autre type -*/ -void PartLine::debugPaint(QPainter *painter) { + * @brief PartLine::debugPaint + * Display several composante of the drawing + * -the bounding rect + * -special points at each extremity + * -the quadrature of the circle at each extremity, even if itself is an other type + * @param painter + */ +void PartLine::debugPaint(QPainter *painter) +{ painter -> save(); painter -> setPen(Qt::gray); painter -> drawRect(boundingRect()); @@ -421,99 +309,109 @@ void PartLine::debugPaint(QPainter *painter) { painter -> drawRect(secondEndCircleRect()); painter -> setPen(Qt::red); - foreach(QPointF pointy, fourEndPoints(line().p1(), line().p2(), first_length)) { + + foreach(QPointF pointy, fourEndPoints(p1(), p2(), first_length)) painter -> drawEllipse(pointy, 0.1, 0.1); - } - foreach(QPointF pointy, fourEndPoints(line().p2(), line().p1(), second_length)) { + + foreach(QPointF pointy, fourEndPoints(p2(), p1(), second_length)) painter -> drawEllipse(pointy, 0.1, 0.1); - } painter -> restore(); } /** - @return le rectangle delimitant cette partie. -*/ -QRectF PartLine::boundingRect() const { - QRectF r(QGraphicsLineItem::boundingRect()); - - // le rectangle ainsi obtenu ne doit pas avoir une dimension nulle - r.adjust(0.0, 0.0, 0.1, 0.1); - - // cas special : les embouts sortent largement du bounding rect originel - if (first_end != Qet::None) { - r = r.united(firstEndCircleRect()); - } - - if (second_end != Qet::None) { - r = r.united(secondEndCircleRect()); - } - - // la taille du bounding rect est ajustee avec une certaine marge - qreal adjust = 1.2; - r.adjust(-adjust, -adjust, adjust, adjust); - return(r); + * @brief PartLine::boundingRect + * @return the bounding rect of this part + */ +QRectF PartLine::boundingRect() const +{ + QRectF bound; + if (first_end || second_end) + bound = path().boundingRect(); + else + bound = QRectF (m_line.p1(), m_line.p2()); + + qreal adjust = (SHADOWS_HEIGHT + penWeight()) / 2; + //We add 0.5 because CustomElementGraphicPart::drawShadowShape + //draw a shape bigger of 0.5 when pen weight is to 0. + if (penWeight() == 0) adjust += 0.5; + + bound = bound.normalized(); + bound.adjust(-adjust, -adjust, adjust, adjust); + return bound; } /** - @return true si cette partie n'est pas pertinente et ne merite pas d'etre - conservee / enregistree. - Une ligne est pertinente des lors que ses deux points sont differents -*/ + * @brief PartLine::isUseless + * @return true if this part is irrelevant and does not deserve to be Retained / registered. + * A line is relevant when is two point is different + */ bool PartLine::isUseless() const { - return(sceneP1() == sceneP2()); + return(m_line.p1() == m_line.p2()); } /** - @return the minimum, margin-less rectangle this part can fit into, in scene - coordinates. It is different from boundingRect() because it is not supposed - to imply any margin, and it is different from shape because it is a regular - rectangle, not a complex shape. -*/ + * @brief PartLine::sceneGeometricRect + * @return the minimum, margin-less rectangle this part can fit into, in scene + * coordinates. It is different from boundingRect() because it is not supposed + * to imply any margin, and it is different from shape because it is a regular + * rectangle, not a complex shape. + */ QRectF PartLine::sceneGeometricRect() const { return(QRectF(sceneP1(), sceneP2())); } /** - Start the user-induced transformation, provided this primitive is contained - within the \a initial_selection_rect bounding rectangle. -*/ -void PartLine::startUserTransformation(const QRectF &initial_selection_rect) { + * @brief PartLine::startUserTransformation + * Start the user-induced transformation, provided this primitive is contained + * within the \a initial_selection_rect bounding rectangle. + * @param initial_selection_rect + */ +void PartLine::startUserTransformation(const QRectF &initial_selection_rect) +{ Q_UNUSED(initial_selection_rect) saved_points_.clear(); saved_points_ << sceneP1() << sceneP2(); } /** - Handle the user-induced transformation from \a initial_selection_rect to \a new_selection_rect -*/ -void PartLine::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) { + * @brief PartLine::handleUserTransformation + * Handle the user-induced transformation from \a initial_selection_rect to \a new_selection_rect + * @param initial_selection_rect + * @param new_selection_rect + */ +void PartLine::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) +{ QList mapped_points = mapPoints(initial_selection_rect, new_selection_rect, saved_points_); - setLine(QLineF(mapFromScene(mapped_points.at(0)), mapFromScene(mapped_points.at(1)))); + prepareGeometryChange(); + m_line = QLineF(mapFromScene(mapped_points.at(0)), mapFromScene(mapped_points.at(1))); } /** - @return Les quatre points interessants a l'extremite d'une droite - Ces points sont, dans l'ordre : - * O : point sur la ligne, a une distance length de l'extremite - * A : point sur la ligne a une distance 2 x length de l'extremite - * B : point a une distance length de O - O est le projete de B sur la droite - * C : point a une distance length de O - O est le projete de C sur la droite - B et C sont situes de part et d'autre de la ligne - @param end_point Extremite concernee - @param other_point Autre point permettant de definir une ligne - @param length Longueur a utiliser entre l'extremite et le point O -*/ -QList PartLine::fourEndPoints(const QPointF &end_point, const QPointF &other_point, const qreal &length) { - // vecteur et longueur de la ligne + * @brief PartLine::fourEndPoints + * Return the four interesting point needed to draw the shape + * at extremity of line (circle, diamond, arrow, triangle) + * This points are in order : + * O : point on the line, at a distance 'length' of the extremity + * A : point on the line at a 'length' of 2x the extremity length + * B : point at a distance of length O - O is the projection of B on the line + * C : point at a distance of length O - O is the projection of C on the line + * @param end_point : The concerned extremity + * @param other_point : other needed point to define the line + * @param length : length to use between the extremity and the point O + * @return + */ +QList PartLine::fourEndPoints(const QPointF &end_point, const QPointF &other_point, const qreal &length) +{ + //Vector and length of the line QPointF line_vector = end_point - other_point; qreal line_length = sqrt(pow(line_vector.x(), 2) + pow(line_vector.y(), 2)); - - // vecteur unitaire et vecteur perpendiculaire + + //Unitary vector and perpendicular vector QPointF u(line_vector / line_length * length); QPointF v(-u.y(), u.x()); - // points O, A, B et C + // points O, A, B, C QPointF o(end_point - u); QPointF a(o - u); QPointF b(o + v); @@ -521,3 +419,119 @@ QList PartLine::fourEndPoints(const QPointF &end_point, const QPointF & return(QList() << o << a << b << c); } + +/** + * @brief PartLine::path + * @return this line has a QPainterPath. + * It's notably use when this line have an end type (circle, triangle etc....), + * because return a QPainterPath with end already draw. + * Else if there isn't an end type get P1 and P2 of line is better (faster). + */ +QPainterPath PartLine::path() const +{ + QPainterPath path; + + QPointF point1(m_line.p1()); + QPointF point2(m_line.p2()); + + qreal line_length(m_line.length()); + qreal pen_width = penWeight(); + + qreal length1 = first_length; + qreal length2 = second_length; + + //debugPaint(painter); + + //Determine if we must to draw extremity + qreal reduced_line_length = line_length - (length1 * requiredLengthForEndType(first_end)); + bool draw_1st_end = first_end && reduced_line_length >= 0; + + if (draw_1st_end) + reduced_line_length -= (length2 * requiredLengthForEndType(second_end)); + else + reduced_line_length = line_length - (length2 * requiredLengthForEndType(second_end)); + + + //Draw the first extremity + QPointF start_point; + if (draw_1st_end) + { + QList four_points1(fourEndPoints(point1, point2, length1)); + + if (first_end == Qet::Circle) + { + path.addEllipse(QRectF(four_points1[0] - QPointF(length1, length1), QSizeF(length1 * 2.0, length1 * 2.0))); + start_point = four_points1[1]; + } + else if (first_end == Qet::Diamond) + { + path.addPolygon(QPolygonF() << four_points1[1] << four_points1[2] << point1 << four_points1[3] << four_points1[1]); + start_point = four_points1[1]; + } + else if (first_end == Qet::Simple) + { + path.addPolygon(QPolygonF() << four_points1[3] << point1 << four_points1[2]); + start_point = point1; + + } + else if (first_end == Qet::Triangle) + { + path.addPolygon(QPolygonF() << four_points1[0] << four_points1[2] << point1 << four_points1[3] << four_points1[0]); + start_point = four_points1[0]; + } + + //Adjust the start point according to the pen width + if (pen_width && (first_end == Qet::Simple || first_end == Qet::Circle)) + start_point = QLineF(start_point, point2).pointAt(pen_width / 2.0 / line_length); + } + else + { + start_point = point1; + } + + //Draw the second extremity + QPointF stop_point; + bool draw_2nd_end = second_end && reduced_line_length >= 0; + if (draw_2nd_end) + { + QList four_points2(fourEndPoints(point2, point1, length2)); + + if (second_end == Qet::Circle) + { + path.addEllipse(QRectF(four_points2[0] - QPointF(length2, length2), QSizeF(length2 * 2.0, length2 * 2.0))); + stop_point = four_points2[1]; + } + else if (second_end == Qet::Diamond) + { + path.addPolygon(QPolygonF() << four_points2[2] << point2 << four_points2[3] << four_points2[1] << four_points2[2]); + stop_point = four_points2[1]; + } + else if (second_end == Qet::Simple) + { + path.addPolygon(QPolygonF() << four_points2[3] << point2 << four_points2[2]); + stop_point = point2; + } + else if (second_end == Qet::Triangle) + {/** + @return true si cette partie n'est pas pertinente et ne merite pas d'etre + conservee / enregistree. + Une ligne est pertinente des lors que ses deux points sont differents +*/ + path.addPolygon(QPolygonF() << four_points2[0] << four_points2[2] << point2 << four_points2[3] << four_points2[0]); + stop_point = four_points2[0]; + } + + //Adjust the end point accordint to the pen width + if (pen_width && (second_end == Qet::Simple || second_end == Qet::Circle)) + stop_point = QLineF(point1, stop_point).pointAt((line_length - (pen_width / 2.0)) / line_length); + } + else + { + stop_point = point2; + } + + path.moveTo(start_point); + path.lineTo(stop_point); + + return path; +} diff --git a/sources/editor/graphicspart/partline.h b/sources/editor/graphicspart/partline.h index 1133226eb..0c3698981 100644 --- a/sources/editor/graphicspart/partline.h +++ b/sources/editor/graphicspart/partline.h @@ -17,7 +17,7 @@ */ #ifndef PART_LINE_H #define PART_LINE_H -#include + #include "customelementgraphicpart.h" #include "qet.h" /** @@ -29,11 +29,20 @@ drawn if the required length for their drawing is longer than the line itself. In case there is room for a single end only, the first one get priority. */ -class PartLine : public CustomElementGraphicPart, public QGraphicsLineItem { - Q_OBJECT +class PartLine : public CustomElementGraphicPart +{ + Q_OBJECT + + Q_PROPERTY(QPointF p1 READ p1 WRITE setP1) + Q_PROPERTY(QPointF p2 READ p2 WRITE setP2) + Q_PROPERTY(Qet::EndType end1 READ firstEndType WRITE setFirstEndType) + Q_PROPERTY(Qet::EndType end2 READ secondEndType WRITE setSecondEndType) + Q_PROPERTY(qreal length1 READ firstEndLength WRITE setFirstEndLength) + Q_PROPERTY(qreal length2 READ secondEndLength WRITE setSecondEndLength) + // constructors, destructor public: - PartLine(QETElementEditor *, QGraphicsItem * = 0, QGraphicsScene * = 0); + PartLine(QETElementEditor *, QGraphicsItem * = 0); virtual ~PartLine(); private: @@ -41,95 +50,60 @@ class PartLine : public CustomElementGraphicPart, public QGraphicsLineItem { // attributes private: - Qet::EndType first_end; - qreal first_length; - Qet::EndType second_end; - qreal second_length; - QList saved_points_; + // methods public: - enum { Type = UserType + 1104 }; + enum { Type = UserType + 1104 }; - /** - Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a - PartLine. - @return the QGraphicsItem type - */ - virtual int type() const { return Type; } - virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0); - virtual QString name() const { return(QObject::tr("ligne", "element part name")); } - virtual QString xmlName() const { return(QString("line")); } - virtual const QDomElement toXml(QDomDocument &) const; - virtual void fromXml(const QDomElement &); - virtual QPointF sceneP1() const; - virtual QPointF sceneP2() const; - virtual QPainterPath shape() const; - virtual QRectF boundingRect() const; - virtual bool isUseless() const; - virtual QRectF sceneGeometricRect() const; - virtual void startUserTransformation(const QRectF &); - virtual void handleUserTransformation(const QRectF &, const QRectF &); - static uint requiredLengthForEndType(const Qet::EndType &); - static QList fourEndPoints(const QPointF &, const QPointF &, const qreal &); + /** + * Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a PartLine. + * @return the QGraphicsItem type + */ + virtual int type() const { return Type; } + virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0); + virtual QString name() const { return(QObject::tr("ligne", "element part name")); } + virtual QString xmlName() const { return(QString("line")); } + virtual const QDomElement toXml(QDomDocument &) const; + virtual void fromXml(const QDomElement &); + virtual QPointF sceneP1() const; + virtual QPointF sceneP2() const; + virtual QPainterPath shape() const; + virtual QRectF boundingRect() const; + virtual bool isUseless() const; + virtual QRectF sceneGeometricRect() const; + virtual void startUserTransformation(const QRectF &); + virtual void handleUserTransformation(const QRectF &, const QRectF &); + static uint requiredLengthForEndType(const Qet::EndType &); + static QList fourEndPoints(const QPointF &, const QPointF &, const qreal &); - ///PROPERTY - // X value of the first point - Q_PROPERTY(qreal x1 READ x1 WRITE setX1) - qreal x1() const {return sceneP1().x();} - void setX1(qreal x1); - - // Y value of the first point - Q_PROPERTY(qreal y1 READ y1 WRITE setY1) - qreal y1() const {return sceneP1().y();} - void setY1(qreal y1); - - //pos of firts point - Q_PROPERTY(QPointF p1 READ sceneP1 WRITE setP1) - void setP1 (QPointF p1); - - // X value of the second point - Q_PROPERTY(qreal x2 READ x2 WRITE setX2) - qreal x2() const {return sceneP2().x();} - void setX2(qreal x2); - - // Y value of the second point - Q_PROPERTY(qreal y2 READ y2 WRITE setY2) - qreal y2() const {return sceneP2().y();} - void setY2(qreal y2); - - //pos of second point - Q_PROPERTY(QPointF p2 READ sceneP2 WRITE setP2) - void setP2 (QPointF p2); - - // End type of the first point - Q_PROPERTY(Qet::EndType end1 READ firstEndType WRITE setFirstEndType) + QPointF p1() const; + void setP1 (const QPointF &p1); + QPointF p2 () const; + void setP2 (const QPointF &p2); Qet::EndType firstEndType() const {return first_end;} void setFirstEndType(const Qet::EndType &et) {first_end = et;} - - // End type of the second point - Q_PROPERTY(Qet::EndType end2 READ secondEndType WRITE setSecondEndType) Qet::EndType secondEndType() const {return second_end;} void setSecondEndType(const Qet::EndType &et) {second_end = et;} - - // Size of end type of first point - Q_PROPERTY(qreal length1 READ firstEndLength WRITE setFirstEndLength) qreal firstEndLength() const {return first_length;} - void setFirstEndLength(const qreal &l) {first_length = qMin(qAbs(l), line().length());} - - // Size of end type of the second point - Q_PROPERTY(qreal length2 READ secondEndLength WRITE setSecondEndLength) + void setFirstEndLength(const qreal &l) {first_length = qMin(qAbs(l), m_line.length());} qreal secondEndLength() const {return second_length;} - void setSecondEndLength(const qreal &l) {second_length = qMin(qAbs(l), line().length());} - - - protected: - QVariant itemChange(GraphicsItemChange, const QVariant &); + void setSecondEndLength(const qreal &l) {second_length = qMin(qAbs(l), m_line.length());} private: - QList fourShapePoints() const; - QRectF firstEndCircleRect() const; - QRectF secondEndCircleRect() const; - void debugPaint(QPainter *); + QPainterPath path() const; + + QList fourShapePoints() const; + QRectF firstEndCircleRect() const; + QRectF secondEndCircleRect() const; + void debugPaint(QPainter *); + + Qet::EndType first_end; + qreal first_length; + + Qet::EndType second_end; + qreal second_length; + QList saved_points_; + QLineF m_line; }; #endif diff --git a/sources/editor/graphicspart/partpolygon.cpp b/sources/editor/graphicspart/partpolygon.cpp index 1c3060388..3f639b481 100644 --- a/sources/editor/graphicspart/partpolygon.cpp +++ b/sources/editor/graphicspart/partpolygon.cpp @@ -16,66 +16,89 @@ along with QElectroTech. If not, see . */ #include "partpolygon.h" -#include "qet.h" /** - Constructeur - @param editor L'editeur d'element concerne - @param parent Le QGraphicsItem parent de ce polygone - @param scene La scene sur laquelle figure ce polygone -*/ -PartPolygon::PartPolygon(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) : - CustomElementGraphicPart(editor), - QGraphicsPolygonItem(parent, scene), + * @brief PartPolygon::PartPolygon + * Constructor + * @param editor : editor of this item + * @param parent : parent item + */ +PartPolygon::PartPolygon(QETElementEditor *editor, QGraphicsItem *parent) : + CustomElementGraphicPart(editor, parent), m_closed(false) -{ - setFlags(QGraphicsItem::ItemIsSelectable); -#if QT_VERSION >= 0x040600 - setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); -#endif - setAcceptedMouseButtons(Qt::LeftButton); -} +{} -/// Destructeur -PartPolygon::~PartPolygon() { +/** + * @brief PartPolygon::~PartPolygon + */ +PartPolygon::~PartPolygon() {} + +/** + * @brief PartPolygon::paint + * Draw this polygon + * @param painter + * @param options + * @param widget + */ +void PartPolygon::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget) +{ + Q_UNUSED(widget); + + applyStylesToQPainter(*painter); + + QPen t = painter -> pen(); + t.setCosmetic(options && options -> levelOfDetail < 1.0); + if (isSelected()) t.setColor(Qt::red); + painter -> setPen(t); + + m_closed ? painter -> drawPolygon (m_polygon) : + painter -> drawPolyline(m_polygon); + + if (m_hovered) + drawShadowShape(painter); } /** - Importe les proprietes d'un polygone depuis un element XML - @param qde Element XML a lire -*/ -void PartPolygon::fromXml(const QDomElement &qde) { + * @brief PartPolygon::fromXml + * Import the properties of this polygon from a xml element + * @param qde : Xml document to use + */ +void PartPolygon::fromXml(const QDomElement &qde) +{ stylesFromXml(qde); + int i = 1; - while(true) { - if ( - QET::attributeIsAReal(qde, QString("x%1").arg(i)) &&\ - QET::attributeIsAReal(qde, QString("y%1").arg(i)) - ) ++ i; + while(true) + { + if (QET::attributeIsAReal(qde, QString("x%1").arg(i)) &&\ + QET::attributeIsAReal(qde, QString("y%1").arg(i))) + ++ i; + else break; } QPolygonF temp_polygon; - for (int j = 1 ; j < i ; ++ j) { - temp_polygon << QPointF( - qde.attribute(QString("x%1").arg(j)).toDouble(), - qde.attribute(QString("y%1").arg(j)).toDouble() - ); + for (int j = 1 ; j < i ; ++ j) + { + temp_polygon << QPointF(qde.attribute(QString("x%1").arg(j)).toDouble(), + qde.attribute(QString("y%1").arg(j)).toDouble()); } - setPolygon(temp_polygon); + m_polygon = temp_polygon; m_closed = qde.attribute("closed") != "false"; } /** - Exporte le polygone en XML - @param xml_document Document XML a utiliser pour creer l'element XML - @return un element XML decrivant le polygone -*/ -const QDomElement PartPolygon::toXml(QDomDocument &xml_document) const { + * @brief PartPolygon::toXml + * Export this polygin in xml + * @param xml_document : Xml document to use for create the xml element + * @return an xml element that describe this polygon + */ +const QDomElement PartPolygon::toXml(QDomDocument &xml_document) const +{ QDomElement xml_element = xml_document.createElement("polygon"); int i = 1; - foreach(QPointF point, polygon()) { + foreach(QPointF point, m_polygon) { point = mapToScene(point); xml_element.setAttribute(QString("x%1").arg(i), QString("%1").arg(point.x())); xml_element.setAttribute(QString("y%1").arg(i), QString("%1").arg(point.y())); @@ -87,101 +110,95 @@ const QDomElement PartPolygon::toXml(QDomDocument &xml_document) const { } /** - Dessine le polygone - @param painter QPainter a utiliser pour rendre le dessin - @param options Options pour affiner le rendu - @param widget Widget sur lequel le rendu est effectue -*/ -void PartPolygon::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget) { - Q_UNUSED(widget); - applyStylesToQPainter(*painter); - QPen t = painter -> pen(); - t.setCosmetic(options && options -> levelOfDetail < 1.0); - if (isSelected()) t.setColor(Qt::red); - painter -> setPen(t); - if (m_closed) painter -> drawPolygon(polygon()); - else painter -> drawPolyline(polygon()); -} + * @brief PartPolygon::isUseless + * @return true if this part is irrelevant and does not deserve to be Retained / registered. + * A polygon is relevant when he have 2 differents points + */ +bool PartPolygon::isUseless() const +{ + if (m_polygon.count() < 2) return(true); -/** - Gere les changements intervenant sur cette partie - @param change Type de changement - @param value Valeur numerique relative au changement -*/ -QVariant PartPolygon::itemChange(GraphicsItemChange change, const QVariant &value) { - if (scene()) { - if (change == QGraphicsItem::ItemPositionChange || change == QGraphicsItem::ItemPositionHasChanged) { - updateCurrentPartEditor(); - } - } - return(QGraphicsPolygonItem::itemChange(change, value)); -} + for (int i = 1 ; i < m_polygon.count() ; ++ i) + if (m_polygon[i] != m_polygon[i-1]) return(false); -/** - @return true si cette partie n'est pas pertinente et ne merite pas d'etre - conservee / enregistree. - Un polygone est pertinent des lors qu'il possede deux points differents. -*/ -bool PartPolygon::isUseless() const { - QPolygonF poly(polygon()); - - if (polygon().count() < 2) return(true); - - QPointF previous_point; - for (int i = 1 ; i < poly.count() ; ++ i) { - if (poly[i] != poly[i-1]) return(false); - } - return(true); } /** - @return the minimum, margin-less rectangle this part can fit into, in scene - coordinates. It is different from boundingRect() because it is not supposed - to imply any margin, and it is different from shape because it is a regular - rectangle, not a complex shape. -*/ + * @brief PartPolygon::sceneGeometricRect + * @return the minimum, margin-less rectangle this part can fit into, in scene + * coordinates. It is different from boundingRect() because it is not supposed + * to imply any margin, and it is different from shape because it is a regular + * rectangle, not a complex shape. + */ QRectF PartPolygon::sceneGeometricRect() const { - return(mapToScene(polygon().boundingRect()).boundingRect()); + return(mapToScene(m_polygon.boundingRect()).boundingRect()); } /** - Start the user-induced transformation, provided this primitive is contained - within the \a initial_selection_rect bounding rectangle. -*/ -void PartPolygon::startUserTransformation(const QRectF &initial_selection_rect) { + * @brief PartPolygon::startUserTransformation + * Start the user-induced transformation, provided this primitive is contained + * within the initial_selection_rect bounding rectangle. + * @param initial_selection_rect + */ +void PartPolygon::startUserTransformation(const QRectF &initial_selection_rect) +{ Q_UNUSED(initial_selection_rect) - saved_points_ = mapToScene(polygon()).toList(); + saved_points_ = mapToScene(m_polygon).toList(); } /** - Handle the user-induced transformation from \a initial_selection_rect to \a new_selection_rect -*/ -void PartPolygon::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) { + * @brief PartPolygon::handleUserTransformation + * Handle the user-induced transformation from initial_selection_rect to new_selection_rect + * @param initial_selection_rect + * @param new_selection_rect + */ +void PartPolygon::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) +{ QList mapped_points = mapPoints(initial_selection_rect, new_selection_rect, saved_points_); - setPolygon(mapFromScene(QPolygonF(mapped_points.toVector()))); + m_polygon = (mapFromScene(QPolygonF(mapped_points.toVector()))); } /** - @reimp CustomElementPart::preferredScalingMethod - This method is called by the decorator when it needs to determine the best - way to interactively scale a primitive. It is typically called when only a - single primitive is being scaled. - This reimplementation systematically returns QET::RoundScaleRatios. -*/ + * @brief PartPolygon::preferredScalingMethod + * This method is called by the decorator when it needs to determine the best + * way to interactively scale a primitive. It is typically called when only a + * single primitive is being scaled. + * @return : This reimplementation systematically returns QET::RoundScaleRatios. + */ QET::ScalingMethod PartPolygon::preferredScalingMethod() const { return(QET::RoundScaleRatios); } +/** + * @brief PartPolygon::polygon + * @return the item's polygon, or an empty polygon if no polygon has been set. + */ +QPolygonF PartPolygon::polygon() const { + return m_polygon; +} + +/** + * @brief PartPolygon::setPolygon + * Sets the item's polygon to be the given polygon. + * @param polygon + */ +void PartPolygon::setPolygon(const QPolygonF &polygon) +{ + if (m_polygon == polygon) return; + prepareGeometryChange(); + m_polygon = polygon; +} + /** * @brief PartPolygon::addPoint * Add new point to polygon * @param point */ -void PartPolygon::addPoint(const QPointF &point) { - QPolygonF poly = polygon(); - poly << point; - setPolygon(poly); +void PartPolygon::addPoint(const QPointF &point) +{ + prepareGeometryChange(); + m_polygon << point; } /** @@ -189,35 +206,59 @@ void PartPolygon::addPoint(const QPointF &point) { * Set the last point of polygon to @point * @param point */ -void PartPolygon::setLastPoint(const QPointF &point) { - QPolygonF poly = polygon(); +void PartPolygon::setLastPoint(const QPointF &point) +{ + if (m_polygon.size()) + m_polygon.pop_back(); - if (poly.size()) - poly.pop_back(); - - poly << point; - setPolygon(poly); + prepareGeometryChange(); + m_polygon << point; } /** * @brief PartPolygon::removeLastPoint * Remove the last point of polygon */ -void PartPolygon::removeLastPoint() { - QPolygonF poly = polygon(); - - if (poly.size()) - poly.pop_back(); - - setPolygon(poly); +void PartPolygon::removeLastPoint() +{ + if (m_polygon.size()) + { + prepareGeometryChange(); + m_polygon.pop_back(); + } } /** - @return le rectangle delimitant cette partie. -*/ -QRectF PartPolygon::boundingRect() const { - qreal adjust = 1.5; - QRectF r(QGraphicsPolygonItem::boundingRect()); + * @brief PartPolygon::shape + * @return the shape of this item + */ +QPainterPath PartPolygon::shape() const +{ + QPainterPath shape; + shape.addPolygon(m_polygon); + + if (m_closed) + shape.lineTo(m_polygon.first()); + + QPainterPathStroker pps; + pps.setWidth(penWeight()); + + return (pps.createStroke(shape)); +} + +/** + * @brief PartPolygon::boundingRect + * @return the bounding rect of this item + */ +QRectF PartPolygon::boundingRect() const +{ + QRectF r = m_polygon.boundingRect(); + + qreal adjust = (SHADOWS_HEIGHT + penWeight()) / 2; + //We add 0.5 because CustomElementGraphicPart::drawShadowShape + //draw a shape bigger of 0.5 when pen weight is to 0. + if (penWeight() == 0) adjust += 0.5; + r.adjust(-adjust, -adjust, adjust, adjust); return(r); } diff --git a/sources/editor/graphicspart/partpolygon.h b/sources/editor/graphicspart/partpolygon.h index d52556c20..3f098d664 100644 --- a/sources/editor/graphicspart/partpolygon.h +++ b/sources/editor/graphicspart/partpolygon.h @@ -17,63 +17,66 @@ */ #ifndef PART_POLYGON_H #define PART_POLYGON_H -#include + +#include #include "customelementgraphicpart.h" + /** - This class represents a polygon primitive which may be used to compose the - drawing of an electrical element within the element editor. -*/ -class PartPolygon : public CustomElementGraphicPart, public QGraphicsPolygonItem { - Q_OBJECT - // constructors, destructor + * @brief The PartPolygon class + * This class represents a polygon primitive which may be used to compose the + * drawing of an electrical element within the element editor. + */ +class PartPolygon : public CustomElementGraphicPart +{ + Q_OBJECT + + Q_PROPERTY(bool closed READ isClosed WRITE setClosed) + + // constructors, destructor public: - PartPolygon(QETElementEditor *, QGraphicsItem * = 0, QGraphicsScene * = 0); - virtual ~PartPolygon(); + PartPolygon(QETElementEditor *editor, QGraphicsItem *parent = 0); + virtual ~PartPolygon(); private: - PartPolygon(const PartPolygon &); - - // attributes - private: - bool m_closed; - - // methods + PartPolygon(const PartPolygon &); + + // methods public: - enum { Type = UserType + 1105 }; - /** - Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a - PartPolygon. - @return the QGraphicsItem type - */ - virtual int type() const { return Type; } - virtual QString name() const { return(QObject::tr("polygone", "element part name")); } - virtual QString xmlName() const { return(QString("polygon")); } - void fromXml(const QDomElement &); - const QDomElement toXml(QDomDocument &) const; - virtual QRectF boundingRect() const; - void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); - virtual bool isUseless() const; - virtual QRectF sceneGeometricRect() const; - virtual void startUserTransformation(const QRectF &); - virtual void handleUserTransformation(const QRectF &, const QRectF &); - virtual QET::ScalingMethod preferredScalingMethod() const; + enum { Type = UserType + 1105 }; + /** + * Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a PartPolygon. + * @return the QGraphicsItem type + */ + virtual int type() const { return Type; } + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + + virtual QString name() const { return(QObject::tr("polygone", "element part name")); } + virtual QString xmlName() const { return(QString("polygon")); } + void fromXml(const QDomElement &); + const QDomElement toXml(QDomDocument &) const; + + virtual QPainterPath shape () const; + virtual QRectF boundingRect() const; + virtual bool isUseless() const; + virtual QRectF sceneGeometricRect() const; + + virtual void startUserTransformation(const QRectF &); + virtual void handleUserTransformation(const QRectF &, const QRectF &); + virtual QET::ScalingMethod preferredScalingMethod() const; + + QPolygonF polygon () const; + void setPolygon (const QPolygonF &polygon); void addPoint (const QPointF &point); void setLastPoint (const QPointF &point); - void removeLastPoint (); + void removeLastPoint (); - ///PROPERTY - // Closed (join the first and last point by a line) - Q_PROPERTY(bool closed READ isClosed WRITE setClosed) - bool isClosed() const {return m_closed;} - void setClosed(bool c) {m_closed = c;} - - - - protected: - QVariant itemChange(GraphicsItemChange, const QVariant &); + bool isClosed () const {return m_closed;} + void setClosed (bool c) {m_closed = c;} private: - QList saved_points_; + bool m_closed; + QList saved_points_; + QPolygonF m_polygon; }; #endif diff --git a/sources/editor/graphicspart/partrectangle.cpp b/sources/editor/graphicspart/partrectangle.cpp index fe1daee08..b0b426e72 100644 --- a/sources/editor/graphicspart/partrectangle.cpp +++ b/sources/editor/graphicspart/partrectangle.cpp @@ -18,201 +18,227 @@ #include "partrectangle.h" /** - Constructeur - @param editor L'editeur d'element concerne - @param parent Le QGraphicsItem parent de ce rectangle - @param scene La scene sur laquelle figure ce rectangle -*/ -PartRectangle::PartRectangle(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) : CustomElementGraphicPart(editor), QGraphicsRectItem(parent, scene) { - setFlags(QGraphicsItem::ItemIsSelectable); -#if QT_VERSION >= 0x040600 - setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); -#endif - setAcceptedMouseButtons(Qt::LeftButton); -} - -/// Destructeur -PartRectangle::~PartRectangle() { -} + * @brief PartRectangle::PartRectangle + * Constructor + * @param editor the QETElementEditor of this item + * @param parent parent item + */ +PartRectangle::PartRectangle(QETElementEditor *editor, QGraphicsItem *parent) : + CustomElementGraphicPart(editor, parent) +{} /** - Dessine le rectangle - @param painter QPainter a utiliser pour rendre le dessin - @param options Options pour affiner le rendu - @param widget Widget sur lequel le rendu est effectue -*/ -void PartRectangle::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget) { + * @brief PartRectangle::~PartRectangle + */ +PartRectangle::~PartRectangle() {} + +/** + * @brief PartRectangle::paint + * Draw this Rectangle + * @param painter + * @param options + * @param widget + */ +void PartRectangle::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget) +{ Q_UNUSED(widget); applyStylesToQPainter(*painter); QPen t = painter -> pen(); t.setCosmetic(options && options -> levelOfDetail < 1.0); - if (isSelected()) { + + if (isSelected()) t.setColor(Qt::red); - } - - // force le type de jointures pour les rectangles + t.setJoinStyle(Qt::MiterJoin); - // force le dessin avec un trait fin si l'une des dimensions du rectangle est nulle - if (!rect().width() || !rect().height()) { + //Force the pen to width 0 if one of dimension is null + if (!rect().width() || !rect().height()) t.setWidth(0); - } painter -> setPen(t); painter -> drawRect(rect()); - if (isSelected()) { - painter -> setRenderHint(QPainter::Antialiasing, false); - painter -> setPen((painter -> brush().color() == QColor(Qt::black) && painter -> brush().isOpaque()) ? Qt::yellow : Qt::blue); - QPointF center = rect().center(); - painter -> drawLine(QLineF(center.x() - 2.0, center.y(), center.x() + 2.0, center.y())); - painter -> drawLine(QLineF(center.x(), center.y() - 2.0, center.x(), center.y() + 2.0)); - } + + if (m_hovered) + drawShadowShape(painter); + + if (isSelected()) + drawCross(m_rect.center(), painter); } /** - Exporte le rectangle en XML - @param xml_document Document XML a utiliser pour creer l'element XML - @return un element XML decrivant le rectangle -*/ -const QDomElement PartRectangle::toXml(QDomDocument &xml_document) const { + * @brief PartRectangle::toXml + * Export this rectangle in xml + * @param xml_document : Xml document to use for create the xml element. + * @return an xml element that describe this ellipse + */ +const QDomElement PartRectangle::toXml(QDomDocument &xml_document) const +{ QDomElement xml_element = xml_document.createElement("rect"); QPointF top_left(sceneTopLeft()); xml_element.setAttribute("x", QString("%1").arg(top_left.x())); xml_element.setAttribute("y", QString("%1").arg(top_left.y())); - xml_element.setAttribute("width", QString("%1").arg(rect().width())); - xml_element.setAttribute("height", QString("%1").arg(rect().height())); + xml_element.setAttribute("width", QString("%1").arg(m_rect.width())); + xml_element.setAttribute("height", QString("%1").arg(m_rect.height())); stylesToXml(xml_element); return(xml_element); } /** - Importe les proprietes d'une rectangle depuis un element XML - @param qde Element XML a lire -*/ -void PartRectangle::fromXml(const QDomElement &qde) { + * @brief PartRectangle::fromXml + * Import the properties of this rectangle from a xml element. + * @param qde : Xml document to use. + */ +void PartRectangle::fromXml(const QDomElement &qde) +{ stylesFromXml(qde); - setRect( - QRectF( - mapFromScene( - qde.attribute("x", "0").toDouble(), - qde.attribute("y", "0").toDouble() - ), - QSizeF( - qde.attribute("width", "0").toDouble(), - qde.attribute("height", "0").toDouble() - ) - ) - ); + setRect(QRectF(mapFromScene(qde.attribute("x", "0").toDouble(), + qde.attribute("y", "0").toDouble()), + QSizeF(qde.attribute("width", "0").toDouble(), + qde.attribute("height", "0").toDouble()))); } /** - * @brief PartRectangle::setX - * @param x new value + * @brief PartRectangle::rect + * @return : Returns the item's rectangle. */ -void PartRectangle::setX(qreal x) { - QRectF current_rect = rect(); - QPointF current_pos = mapToScene(current_rect.topLeft()); - setRect(current_rect.translated(x - current_pos.x(), 0.0)); +QRectF PartRectangle::rect() const { + return m_rect; } /** - * @brief PartRectangle::setY - * @param y new value + * @brief PartRectangle::setRect + * Sets the item's rectangle to be the given rectangle. + * @param rect */ -void PartRectangle::setY(qreal y) { - QRectF current_rect = rect(); - QPointF current_pos = mapToScene(current_rect.topLeft()); - setRect(current_rect.translated(0.0, y - current_pos.y())); +void PartRectangle::setRect(const QRectF &rect) +{ + if (rect == m_rect) return; + prepareGeometryChange(); + m_rect = rect; +} + +/** + * @brief PartRectangle::rectTopLeft + * @return the rectangle top left in item coordinate + */ +QPointF PartRectangle::rectTopLeft() const { + return m_rect.topLeft(); +} + +/** + * @brief PartRectangle::setRectTopLeft + * @param point, set the rectangle top left in item coordinate. + * The rectangle size is unchanged + */ +void PartRectangle::setRectTopLeft(const QPointF &point) { + m_rect.moveTopLeft(point); } /** * @brief PartRectangle::setWidth + * Sets the width of the rectangle to the given width. + * The right edge is changed, but not the left one. * @param w new value */ -void PartRectangle::setWidth(qreal w) { - qreal new_width = qAbs(w); - QRectF current_rect = rect(); - current_rect.setWidth(new_width); - setRect(current_rect); +void PartRectangle::setWidth(qreal w) +{ + prepareGeometryChange(); + m_rect.setWidth(qAbs(w)); } /** * @brief PartRectangle::setHeight + * Sets the height of the rectangle to the given height. + * The bottom edge is changed, but not the top one. * @param h new value */ -void PartRectangle::setHeight(qreal h) { - qreal new_height = qAbs(h); - QRectF current_rect = rect(); - current_rect.setHeight(new_height); - setRect(current_rect); +void PartRectangle::setHeight(qreal h) +{ + prepareGeometryChange(); + m_rect.setHeight(qAbs(h)); } /** - Gere les changements intervenant sur cette partie - @param change Type de changement - @param value Valeur numerique relative au changement -*/ -QVariant PartRectangle::itemChange(GraphicsItemChange change, const QVariant &value) { - if (scene()) { - if (change == QGraphicsItem::ItemPositionChange || change == QGraphicsItem::ItemPositionHasChanged) { - updateCurrentPartEditor(); - } - } - return(QGraphicsRectItem::itemChange(change, value)); -} - -/** - @return le coin superieur gauche du rectangle, dans les coordonnees de la - scene. -*/ -QPointF PartRectangle::sceneTopLeft() const { - return(mapToScene(rect().topLeft())); -} - -/** - @return true si cette partie n'est pas pertinente et ne merite pas d'etre - conservee / enregistree. - Un rectangle est pertinent des lors que ses dimensions ne sont pas nulles. -*/ -bool PartRectangle::isUseless() const { - return(rect().isNull()); -} - -/** - @return the minimum, margin-less rectangle this part can fit into, in scene - coordinates. It is different from boundingRect() because it is not supposed - to imply any margin, and it is different from shape because it is a regular - rectangle, not a complex shape. -*/ + * @brief PartRectangle::sceneGeometricRect + * @return the minimum, margin-less rectangle this part can fit into, in scene + * coordinates. It is different from boundingRect() because it is not supposed + * to imply any margin, and it is different from shape because it is a regular + * rectangle, not a complex shape. + */ QRectF PartRectangle::sceneGeometricRect() const { return(mapToScene(rect()).boundingRect()); } /** - Start the user-induced transformation, provided this primitive is contained - within the \a initial_selection_rect bounding rectangle. -*/ -void PartRectangle::startUserTransformation(const QRectF &initial_selection_rect) { + * @brief PartRectangle::sceneTopLeft + * @return the top left of rectangle, in scene coordinate + */ +QPointF PartRectangle::sceneTopLeft() const { + return(mapToScene(rect().topLeft())); +} + +/** + * @brief PartRectangle::shape + * @return the shape of this item + */ +QPainterPath PartRectangle::shape() const +{ + QPainterPath shape; + shape.addRect(m_rect); + + QPainterPathStroker pps; + pps.setWidth(penWeight()); + + return (pps.createStroke(shape)); +} + +/** + * @brief PartRectangle::boundingRect + * @return Bounding rectangle this part can fit into + */ +QRectF PartRectangle::boundingRect() const +{ + qreal adjust = (SHADOWS_HEIGHT + penWeight()) / 2; + //We add 0.5 because CustomElementGraphicPart::drawShadowShape + //draw a shape bigger of 0.5 when pen weight is to 0. + if (penWeight() == 0) adjust += 0.5; + + QRectF r = m_rect.normalized(); + r.adjust(-adjust, -adjust, adjust, adjust); + return(r); +} + +/** + * @brief PartRectangle::isUseless + * @return true if this part is irrelevant and does not deserve to be Retained / registered. + * An rectangle is relevant when he's not null. + */ +bool PartRectangle::isUseless() const { + return(rect().isNull()); +} + +/** + * @brief PartRectangle::startUserTransformation + * Start the user-induced transformation, provided this primitive is contained + * within the initial_selection_rect bounding rectangle. + * @param initial_selection_rect + */ +void PartRectangle::startUserTransformation(const QRectF &initial_selection_rect) +{ Q_UNUSED(initial_selection_rect) - // we keep track of our own rectangle at the moment in scene coordinates too + // we keep track of our own rectangle at the moment in scene coordinates too saved_points_.clear(); saved_points_ << mapToScene(rect().topLeft()) << mapToScene(rect().bottomRight()); } /** - Handle the user-induced transformation from \a initial_selection_rect to \a new_selection_rect -*/ -void PartRectangle::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) { + * @brief PartRectangle::handleUserTransformation + * Handle the user-induced transformation from \a initial_selection_rect to \a new_selection_rect + * @param initial_selection_rect + * @param new_selection_rect + */ +void PartRectangle::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) +{ QList mapped_points = mapPoints(initial_selection_rect, new_selection_rect, saved_points_); setRect(QRectF(mapFromScene(mapped_points.at(0)), mapFromScene(mapped_points.at(1)))); } - -/** - @return le rectangle delimitant cette partie. -*/ -QRectF PartRectangle::boundingRect() const { - qreal adjust = 1.5; - QRectF r(QGraphicsRectItem::boundingRect().normalized()); - r.adjust(-adjust, -adjust, adjust, adjust); - return(r); -} diff --git a/sources/editor/graphicspart/partrectangle.h b/sources/editor/graphicspart/partrectangle.h index 07da339a1..a95c38d8c 100644 --- a/sources/editor/graphicspart/partrectangle.h +++ b/sources/editor/graphicspart/partrectangle.h @@ -17,65 +17,69 @@ */ #ifndef PART_RECTANGLE_H #define PART_RECTANGLE_H -#include -#include "customelementgraphicpart.h" -/** - This class represents a rectangle primitive which may be used to compose the - drawing of an electrical element within the element editor. -*/ -class PartRectangle : public CustomElementGraphicPart, public QGraphicsRectItem { - Q_OBJECT - // constructors, destructor - public: - PartRectangle(QETElementEditor *, QGraphicsItem * = 0, QGraphicsScene * = 0); - virtual ~PartRectangle(); - - private: - PartRectangle(const PartRectangle &); - - // methods - public: - enum { Type = UserType + 1109 }; - /** - Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a - PartRectangle. - @return the QGraphicsItem type - */ - virtual int type() const { return Type; } - virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0); - virtual QString name() const { return(QObject::tr("rectangle", "element part name")); } - virtual QString xmlName() const { return(QString("rect")); } - virtual const QDomElement toXml(QDomDocument &) const; - virtual void fromXml(const QDomElement &); - virtual QPointF sceneTopLeft() const; - virtual QRectF boundingRect() const; - virtual bool isUseless() const; - virtual QRectF sceneGeometricRect() const; - virtual void startUserTransformation(const QRectF &); - virtual void handleUserTransformation(const QRectF &, const QRectF &); - ///PROPERTY - // X value - Q_PROPERTY(qreal x READ x WRITE setX) - qreal x() const {return mapToScene(rect().topLeft()).x();} - void setX(qreal x); - // Y value - Q_PROPERTY(qreal y READ y WRITE setY) - qreal y() const {return mapToScene(rect().topLeft()).y();} - void setY(qreal y); - // Width value - Q_PROPERTY(qreal width READ width WRITE setWidth) - qreal width() const {return rect().width();} - void setWidth(qreal w); - // Height value - Q_PROPERTY(qreal height READ height WRITE setHeight) - qreal height() const { return rect().height();} - void setHeight(qreal h); - - protected: - QVariant itemChange(GraphicsItemChange, const QVariant &); +#include "customelementgraphicpart.h" + +/** + * This class represents a rectangle primitive which may be used to compose the + * drawing of an electrical element within the element editor. + * All coordinates is in item coordinate, except pos() +*/ +class PartRectangle : public CustomElementGraphicPart +{ + Q_OBJECT + + Q_PROPERTY(QPointF rectTopLeft READ rectTopLeft WRITE setRectTopLeft) + Q_PROPERTY(qreal width READ width WRITE setWidth) + Q_PROPERTY(qreal height READ height WRITE setHeight) + + // constructors, destructor + public: + PartRectangle(QETElementEditor *, QGraphicsItem *parent = 0); + virtual ~PartRectangle(); private: - QList saved_points_; + PartRectangle(const PartRectangle &); + + // methods + public: + enum { Type = UserType + 1109 }; + /** + * Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a PartRectangle. + * @return the QGraphicsItem type + */ + virtual int type () const { return Type; } + virtual void paint (QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0); + virtual QString name () const { return(QObject::tr("rectangle", "element part name")); } + + virtual QString xmlName () const { return(QString("rect")); } + virtual const QDomElement toXml (QDomDocument &) const; + virtual void fromXml (const QDomElement &); + + QRectF rect() const; + void setRect(const QRectF &rect); + + QPointF rectTopLeft () const; + void setRectTopLeft (const QPointF &point); + + qreal width () const {return rect().width();} + void setWidth (qreal w); + + qreal height () const { return rect().height();} + void setHeight (qreal h); + + virtual QRectF sceneGeometricRect() const; + virtual QPointF sceneTopLeft() const; + + virtual QPainterPath shape () const; + virtual QRectF boundingRect() const; + virtual bool isUseless() const; + + virtual void startUserTransformation(const QRectF &); + virtual void handleUserTransformation(const QRectF &, const QRectF &); + + private: + QRectF m_rect; + QList saved_points_; }; #endif diff --git a/sources/editor/graphicspart/partterminal.cpp b/sources/editor/graphicspart/partterminal.cpp index 674065a4f..eedd7e10c 100644 --- a/sources/editor/graphicspart/partterminal.cpp +++ b/sources/editor/graphicspart/partterminal.cpp @@ -24,16 +24,11 @@ @param parent Le QGraphicsItem parent de cette borne @param scene La scene sur laquelle figure cette borne */ -PartTerminal::PartTerminal(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) : - CustomElementGraphicPart(editor), - QGraphicsItem(parent, scene), +PartTerminal::PartTerminal(QETElementEditor *editor, QGraphicsItem *parent) : + CustomElementGraphicPart(editor, parent), m_orientation(Qet::North) { updateSecondPoint(); - setFlags(QGraphicsItem::ItemIsSelectable); -#if QT_VERSION >= 0x040600 - setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); -#endif setZValue(100000); } @@ -107,23 +102,37 @@ void PartTerminal::paint(QPainter *p, const QStyleOptionGraphicsItem *options, Q p -> setBrush(Terminal::neutralColor); p -> drawPoint(QPointF(0.0, 0.0)); p -> restore(); + + if (m_hovered) + drawShadowShape(p); } /** - @return le rectangle delimitant cette partie. -*/ -QRectF PartTerminal::boundingRect() const { - QPointF p1, p2; - if (second_point.x() <= 0.0 && second_point.y() <= 0.0) { - p1 = second_point; - p2 = QPointF(0.0, 0.0); - } else { - p1 = QPointF(0.0, 0.0); - p2 = second_point; - } - QRectF br; - br.setTopLeft (p1 - QPointF(2.0, 2.0)); - br.setBottomRight(p2 + QPointF(2.0, 2.0)); + * @brief PartTerminal::shape + * @return the shape of this item + */ +QPainterPath PartTerminal::shape() const +{ + QPainterPath shape; + shape.lineTo(second_point); + + QPainterPathStroker pps; + pps.setWidth(1); + + return (pps.createStroke(shape)); +} + +/** + * @brief PartTerminal::boundingRect + * @return the bounding rect of this item + */ +QRectF PartTerminal::boundingRect() const +{ + QRectF br(QPointF(0, 0), second_point); + br = br.normalized(); + + qreal adjust = (SHADOWS_HEIGHT + 1) / 2; + br.adjust(-adjust, -adjust, adjust, adjust); return(br); } @@ -137,20 +146,6 @@ void PartTerminal::setOrientation(Qet::Orientation ori) { updateSecondPoint(); } -/** - Gere les changements intervenant sur cette partie - @param change Type de changement - @param value Valeur numerique relative au changement -*/ -QVariant PartTerminal::itemChange(GraphicsItemChange change, const QVariant &value) { - if (scene()) { - if (change == QGraphicsItem::ItemPositionChange || change == QGraphicsItem::ItemPositionHasChanged) { - updateCurrentPartEditor(); - } - } - return(QGraphicsItem::itemChange(change, value)); -} - /** Met a jour la position du second point en fonction de la position et de l'orientation de la borne. diff --git a/sources/editor/graphicspart/partterminal.h b/sources/editor/graphicspart/partterminal.h index d774e9e36..25f4c6c28 100644 --- a/sources/editor/graphicspart/partterminal.h +++ b/sources/editor/graphicspart/partterminal.h @@ -17,67 +17,59 @@ */ #ifndef PART_TERMINAL_H #define PART_TERMINAL_H + #include "customelementgraphicpart.h" -#include "qet.h" -#include + /** This class represents a terminal which may be used to compose the drawing of an electrical element within the element editor. */ -class PartTerminal : public CustomElementGraphicPart, public QGraphicsItem { - Q_OBJECT - public: - // constructors, destructor - PartTerminal(QETElementEditor *, QGraphicsItem * = 0, QGraphicsScene * = 0); - virtual ~PartTerminal(); - private: - PartTerminal(const PartTerminal &); - - // attributes - private: - Qet::Orientation m_orientation; - QPointF second_point; - - // methods - public: - enum { Type = UserType + 1106 }; - /** - Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a - PartTerminal. - @return the QGraphicsItem type - */ - virtual int type() const { return Type; } - virtual QString name() const { return(QObject::tr("borne", "element part name")); } - virtual QString xmlName() const { return(QString("terminal")); } - virtual void fromXml(const QDomElement &); - virtual const QDomElement toXml(QDomDocument &) const; - virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); - virtual QRectF boundingRect() const; - - /*virtual void setProperty(const QString &, const QVariant &); - virtual QVariant property(const QString &);*/ - virtual bool isUseless() const; - virtual QRectF sceneGeometricRect() const; - virtual void startUserTransformation(const QRectF &); - virtual void handleUserTransformation(const QRectF &, const QRectF &); +class PartTerminal : public CustomElementGraphicPart +{ + Q_OBJECT + + Q_PROPERTY(Qet::Orientation orientation READ orientation WRITE setOrientation) + + public: + // constructors, destructor + PartTerminal(QETElementEditor *editor, QGraphicsItem *parent = 0); + virtual ~PartTerminal(); + private: + PartTerminal(const PartTerminal &); + + // attributes + private: + Qet::Orientation m_orientation; + QPointF second_point; + + // methods + public: + enum { Type = UserType + 1106 }; + /** + * Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a PartTerminal. + * @return the QGraphicsItem type + */ + virtual int type() const { return Type; } + virtual QString name() const { return(QObject::tr("borne", "element part name")); } + virtual QString xmlName() const { return(QString("terminal")); } + virtual void fromXml(const QDomElement &); + virtual const QDomElement toXml(QDomDocument &) const; + virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + + virtual QPainterPath shape() const; + virtual QRectF boundingRect() const; + virtual bool isUseless() const; + virtual QRectF sceneGeometricRect() const; + virtual void startUserTransformation(const QRectF &); + virtual void handleUserTransformation(const QRectF &, const QRectF &); - ///PROPERTY - // X value - Q_PROPERTY(qreal x READ x WRITE setX) - // Y value - Q_PROPERTY(qreal y READ y WRITE setY) - // Horientation value - Q_PROPERTY(Qet::Orientation orientation READ orientation WRITE setOrientation) Qet::Orientation orientation() const {return m_orientation;} void setOrientation(Qet::Orientation ori); - protected: - QVariant itemChange(GraphicsItemChange, const QVariant &); + private: + void updateSecondPoint(); private: - void updateSecondPoint(); - - private: - QPointF saved_position_; + QPointF saved_position_; }; #endif diff --git a/sources/editor/lineeditor.cpp b/sources/editor/lineeditor.cpp index b8af7312f..52e507787 100644 --- a/sources/editor/lineeditor.cpp +++ b/sources/editor/lineeditor.cpp @@ -119,6 +119,22 @@ CustomElementPart *LineEditor::currentPart() const { return(part); } +/** + * @brief LineEditor::editedP1 + * @return The edited P1 in item coordinate + */ +QPointF LineEditor::editedP1() const { + return part -> mapFromScene(x1->value(), y1->value()); +} + +/** + * @brief LineEditor::editedP2 + * @return The edited P2 in item coordinate + */ +QPointF LineEditor::editedP2() const { + return part -> mapFromScene(x2->value(), y2->value()); +} + /** Met a jour la ligne a partir des donnees du formulaire */ @@ -128,28 +144,18 @@ void LineEditor::updateLine() { part -> setProperty("length1", end1_length -> value()); part -> setProperty("end2", end2_type -> itemData(end2_type->currentIndex())); part -> setProperty("length2", end2_length -> value()); - part -> setLine( - QLineF( - part -> mapFromScene( - x1 -> value(), - y1 -> value() - ), - part -> mapFromScene( - x2 -> value(), - y2 -> value() - ) - ) - ); + part -> setProperty("p1", editedP1()); + part -> setProperty("p2", editedP2()); } /// Met a jour l'abscisse du premier point de la ligne et cree un objet d'annulation -void LineEditor::updateLineX1() { addChangePartCommand(tr("abscisse point 1"), part, "x1", x1 -> value()); } +void LineEditor::updateLineX1() { addChangePartCommand(tr("abscisse point 1"), part, "p1", editedP1()); } /// Met a jour l'ordonnee du premier point de la ligne et cree un objet d'annulation -void LineEditor::updateLineY1() { addChangePartCommand(tr("ordonn\351e point 1"), part, "y1", y1 -> value()); } +void LineEditor::updateLineY1() { addChangePartCommand(tr("ordonn\351e point 1"), part, "p1", editedP1()); } /// Met a jour l'abscisse du second point de la ligne et cree un objet d'annulation -void LineEditor::updateLineX2() { addChangePartCommand(tr("abscisse point 2"), part, "x2", x2 -> value()); } +void LineEditor::updateLineX2() { addChangePartCommand(tr("abscisse point 2"), part, "p2", editedP2()); } /// Met a jour l'ordonnee du second point de la ligne et cree un objet d'annulation -void LineEditor::updateLineY2() { addChangePartCommand(tr("ordonn\351e point 2"), part, "y2", y2 -> value()); } +void LineEditor::updateLineY2() { addChangePartCommand(tr("ordonn\351e point 2"), part, "p2", editedP2()); } /// Met a jour le type de la premiere extremite void LineEditor::updateLineEndType1() { addChangePartCommand(tr("type fin 1"), part, "end1", end1_type -> itemData(end1_type->currentIndex())); } /// Met a jour la longueur de la premiere extremite diff --git a/sources/editor/lineeditor.h b/sources/editor/lineeditor.h index bfc52ae52..507e792a5 100644 --- a/sources/editor/lineeditor.h +++ b/sources/editor/lineeditor.h @@ -24,41 +24,44 @@ class StyleEditor; /** This class provides a widget to edit lines within the element editor. */ -class LineEditor : public ElementItemEditor { +class LineEditor : public ElementItemEditor +{ Q_OBJECT // constructors, destructor public: - LineEditor(QETElementEditor *, PartLine * = 0, QWidget * = 0); - virtual ~LineEditor(); + LineEditor(QETElementEditor *, PartLine * = 0, QWidget * = 0); + virtual ~LineEditor(); private: - LineEditor(const LineEditor &); + LineEditor(const LineEditor &); // attributes private: - PartLine *part; - StyleEditor *style_; - QDoubleSpinBox *x1, *y1, *x2, *y2; - QComboBox *end1_type, *end2_type; - QDoubleSpinBox*end1_length, *end2_length; + PartLine *part; + StyleEditor *style_; + QDoubleSpinBox *x1, *y1, *x2, *y2; + QComboBox *end1_type, *end2_type; + QDoubleSpinBox*end1_length, *end2_length; // methods public: - virtual bool setPart(CustomElementPart *); - virtual CustomElementPart *currentPart() const; + virtual bool setPart(CustomElementPart *); + virtual CustomElementPart *currentPart() const; + QPointF editedP1() const; + QPointF editedP2() const; public slots: - void updateLine(); - void updateLineX1(); - void updateLineY1(); - void updateLineX2(); - void updateLineY2(); - void updateLineEndType1(); - void updateLineEndLength1(); - void updateLineEndType2(); - void updateLineEndLength2(); - void updateForm(); + void updateLine(); + void updateLineX1(); + void updateLineY1(); + void updateLineX2(); + void updateLineY2(); + void updateLineEndType1(); + void updateLineEndLength1(); + void updateLineEndType2(); + void updateLineEndLength2(); + void updateForm(); private: - void activeConnections(bool); + void activeConnections(bool); }; #endif diff --git a/sources/editor/rectangleeditor.cpp b/sources/editor/rectangleeditor.cpp index f020ec638..58febb0ea 100644 --- a/sources/editor/rectangleeditor.cpp +++ b/sources/editor/rectangleeditor.cpp @@ -97,25 +97,32 @@ CustomElementPart *RectangleEditor::currentPart() const { return(part); } +/** + * @brief RectangleEditor::topLeft + * @return The edited topLeft already mapped to part coordinate + */ +QPointF RectangleEditor::editedTopLeft() const { + return part -> mapFromScene(x->value(), y->value()); +} + /** Met a jour le rectangle a partir des donnees du formulaire */ void RectangleEditor::updateRectangle() { if (!part) return; - part -> setProperty("x", x -> value()); - part -> setProperty("y", y -> value()); + part -> setProperty("rectTopLeft", editedTopLeft()); part -> setProperty("width", w -> value()); part -> setProperty("height", h -> value()); } /// Met a jour l'abscisse du coin superieur gauche du rectangle et cree un objet d'annulation -void RectangleEditor::updateRectangleX() { addChangePartCommand(tr("abscisse"), part, "x", x -> value()); } +void RectangleEditor::updateRectangleX() { addChangePartCommand(tr("abscisse"), part, "rectTopLeft", editedTopLeft());} /// Met a jour l'ordonnee du coin superieur gauche du rectangle et cree un objet d'annulation -void RectangleEditor::updateRectangleY() { addChangePartCommand(tr("ordonn\351e"), part, "y", y -> value()); } +void RectangleEditor::updateRectangleY() { addChangePartCommand(tr("ordonn\351e"), part, "rectTopLeft", editedTopLeft());} /// Met a jour la largeur du rectangle et cree un objet d'annulation -void RectangleEditor::updateRectangleW() { addChangePartCommand(tr("largeur"), part, "width", w -> value()); } +void RectangleEditor::updateRectangleW() { addChangePartCommand(tr("largeur"), part, "width", w -> value());} /// Met a jour la hauteur du rectangle et cree un objet d'annulation -void RectangleEditor::updateRectangleH() { addChangePartCommand(tr("hauteur"), part, "height", h -> value()); } +void RectangleEditor::updateRectangleH() { addChangePartCommand(tr("hauteur"), part, "height", h -> value());} /** Met a jour le formulaire d'edition @@ -123,8 +130,9 @@ void RectangleEditor::updateRectangleH() { addChangePartCommand(tr("hauteur"), void RectangleEditor::updateForm() { if (!part) return; activeConnections(false); - x->setValue(part->property("x").toReal()); - y->setValue(part->property("y").toReal()); + QPointF p = part->mapToScene(part->property("rectTopLeft").toPointF()); + x->setValue(p.x()); + y->setValue(p.y()); w->setValue(part->property("width").toReal()); h->setValue(part->property("height").toReal()); activeConnections(true); diff --git a/sources/editor/rectangleeditor.h b/sources/editor/rectangleeditor.h index 37e051d37..8ce4bf1ae 100644 --- a/sources/editor/rectangleeditor.h +++ b/sources/editor/rectangleeditor.h @@ -43,6 +43,7 @@ class RectangleEditor : public ElementItemEditor { public: virtual bool setPart(CustomElementPart *); virtual CustomElementPart *currentPart() const; + QPointF editedTopLeft () const; public slots: void updateRectangle();