diff --git a/sources/editor/graphicspart/partterminal.cpp b/sources/editor/graphicspart/partterminal.cpp index a1b557d79..88cb59ba3 100644 --- a/sources/editor/graphicspart/partterminal.cpp +++ b/sources/editor/graphicspart/partterminal.cpp @@ -25,9 +25,11 @@ @param scene La scene sur laquelle figure cette borne */ PartTerminal::PartTerminal(QETElementEditor *editor, QGraphicsItem *parent) : - CustomElementGraphicPart(editor, parent), - m_orientation(Qet::North) + CustomElementGraphicPart(editor, parent) { + d = new TerminalData(this); + d->m_orientation = Qet::North; + d->m_uuid = QUuid::createUuid(); // if part is loaded this uuid will be overwritten, but being sure that terminal has a uuid updateSecondPoint(); setZValue(100000); } @@ -41,15 +43,8 @@ PartTerminal::~PartTerminal() { @param xml_elmt Element XML a lire */ void PartTerminal::fromXml(const QDomElement &xml_elmt) { - // lit la position de la borne - qreal term_x = 0.0, term_y = 0.0; - QET::attributeIsAReal(xml_elmt, "x", &term_x); - QET::attributeIsAReal(xml_elmt, "y", &term_y); - setPos(QPointF(term_x, term_y)); - - // lit l'orientation de la borne - m_orientation = Qet::orientationFromString(xml_elmt.attribute("orientation")); - + d->fromXml(xml_elmt); + setPos(d->m_pos); updateSecondPoint(); } @@ -59,18 +54,7 @@ void PartTerminal::fromXml(const QDomElement &xml_elmt) { @return un element XML decrivant la borne */ const QDomElement PartTerminal::toXml(QDomDocument &xml_document) const { - QDomElement xml_element = xml_document.createElement("terminal"); - - // ecrit la position de la borne - xml_element.setAttribute("x", QString("%1").arg(scenePos().x())); - xml_element.setAttribute("y", QString("%1").arg(scenePos().y())); - - - // ecrit l'orientation de la borne - xml_element.setAttribute("orientation", Qet::orientationToString(m_orientation)); - // Write name and number to XML - - return(xml_element); + return d->toXml(xml_document); } /** @@ -95,7 +79,7 @@ void PartTerminal::paint(QPainter *p, const QStyleOptionGraphicsItem *options, Q // dessin de la borne en rouge t.setColor(isSelected() ? Terminal::neutralColor : Qt::red); p -> setPen(t); - p -> drawLine(QPointF(0.0, 0.0), second_point); + p -> drawLine(QPointF(0.0, 0.0), d->second_point); // dessin du point d'amarrage au conducteur en bleu t.setColor(isSelected() ? Qt::red : Terminal::neutralColor); @@ -115,7 +99,7 @@ void PartTerminal::paint(QPainter *p, const QStyleOptionGraphicsItem *options, Q QPainterPath PartTerminal::shape() const { QPainterPath shape; - shape.lineTo(second_point); + shape.lineTo(d->second_point); QPainterPathStroker pps; pps.setWidth(1); @@ -129,7 +113,7 @@ QPainterPath PartTerminal::shape() const */ QRectF PartTerminal::boundingRect() const { - QRectF br(QPointF(0, 0), second_point); + QRectF br(QPointF(0, 0), d->second_point); br = br.normalized(); qreal adjust = (SHADOWS_HEIGHT + 1) / 2; @@ -143,24 +127,31 @@ QRectF PartTerminal::boundingRect() const */ void PartTerminal::setOrientation(Qet::Orientation ori) { - if (m_orientation == ori) return; + if (d->m_orientation == ori) return; prepareGeometryChange(); - m_orientation = ori; + d->m_orientation = ori; updateSecondPoint(); emit orientationChanged(); } +void PartTerminal::setName(QString& name) +{ + if (d->m_name == name) return; + d->m_name = name; + emit nameChanged(); +} + /** Met a jour la position du second point en fonction de la position et de l'orientation de la borne. */ void PartTerminal::updateSecondPoint() { qreal ts = 4.0; // terminal size - switch(m_orientation) { - case Qet::North: second_point = QPointF(0.0, ts); break; - case Qet::East : second_point = QPointF(-ts, 0.0); break; - case Qet::South: second_point = QPointF(0.0, -ts); break; - case Qet::West : second_point = QPointF(ts, 0.0); break; + switch(d->m_orientation) { + case Qet::North: d->second_point = QPointF(0.0, ts); break; + case Qet::East : d->second_point = QPointF(-ts, 0.0); break; + case Qet::South: d->second_point = QPointF(0.0, -ts); break; + case Qet::West : d->second_point = QPointF(ts, 0.0); break; } } diff --git a/sources/editor/graphicspart/partterminal.h b/sources/editor/graphicspart/partterminal.h index 4565468bf..26c624bc9 100644 --- a/sources/editor/graphicspart/partterminal.h +++ b/sources/editor/graphicspart/partterminal.h @@ -19,6 +19,8 @@ #define PART_TERMINAL_H #include "customelementgraphicpart.h" +#include "QUuid" +#include "terminaldata.h" @@ -31,6 +33,7 @@ class PartTerminal : public CustomElementGraphicPart Q_OBJECT Q_PROPERTY(Qet::Orientation orientation READ orientation WRITE setOrientation) + Q_PROPERTY(QString name READ name WRITE setName) public: @@ -42,12 +45,7 @@ class PartTerminal : public CustomElementGraphicPart signals: void orientationChanged(); - - // attributes - private: - Qet::Orientation m_orientation; - QPointF second_point; - + void nameChanged(); // methods public: @@ -57,7 +55,7 @@ class PartTerminal : public CustomElementGraphicPart * @return the QGraphicsItem type */ int type() const override { return Type; } - QString name() const override { return(QObject::tr("borne", "element part name")); } + QString name() const override { return d->m_name; } QString xmlName() const override { return(QString("terminal")); } void fromXml(const QDomElement &) override; const QDomElement toXml(QDomDocument &) const override; @@ -71,11 +69,14 @@ class PartTerminal : public CustomElementGraphicPart void startUserTransformation(const QRectF &) override; void handleUserTransformation(const QRectF &, const QRectF &) override; - Qet::Orientation orientation() const {return m_orientation;} + Qet::Orientation orientation() const {return d->m_orientation;} void setOrientation(Qet::Orientation ori); + + void setName(QString& name); private: void updateSecondPoint(); + TerminalData* d; // pointer to the terminal data private: QPointF saved_position_; diff --git a/sources/properties/terminaldata.cpp b/sources/properties/terminaldata.cpp new file mode 100644 index 000000000..6e4c9af8c --- /dev/null +++ b/sources/properties/terminaldata.cpp @@ -0,0 +1,82 @@ +#include "terminaldata.h" + +#include + +TerminalData::TerminalData(): + PropertiesInterface() +{ + +} + +TerminalData::TerminalData(QGraphicsObject *parent): + PropertiesInterface(), + q(parent) +{ + +} + +TerminalData::~TerminalData() +{ + +} + +void TerminalData::setParent(QGraphicsObject* parent) +{ + q = parent; +} + +void TerminalData::toSettings(QSettings &settings, const QString) const +{ + +} + +void TerminalData::fromSettings(const QSettings &settings, const QString) +{ + +} + +QDomElement TerminalData::toXml(QDomDocument &xml_document) const +{ + QDomElement xml_element = xml_document.createElement("terminal"); + + // ecrit la position de la borne + xml_element.setAttribute("x", QString("%1").arg(q->scenePos().x())); + xml_element.setAttribute("y", QString("%1").arg(q->scenePos().y())); + + + xml_element.setAttribute("uuid", m_uuid.toString()); + xml_element.setAttribute("name", m_name); + + // ecrit l'orientation de la borne + xml_element.setAttribute("orientation", Qet::orientationToString(m_orientation)); + // Write name and number to XML + + return(xml_element); +} +bool TerminalData::fromXml (const QDomElement &xml_element) +{ + // lit la position de la borne + qreal term_x = 0.0, term_y = 0.0; + if (!QET::attributeIsAReal(xml_element, "x", &term_x)) + return false; + + if (!QET::attributeIsAReal(xml_element, "y", &term_y)) + return false; + + m_pos = QPointF(term_x, term_y); + + //emit posFromXML(QPointF(term_x, term_y)); + + QString uuid = xml_element.attribute("uuid"); + // update part and add uuid, which is used in the new version to connect terminals together + // if the attribute not exists, in the constructor of PartTerminal already one is created + if (!uuid.isEmpty()) + m_uuid = QUuid(uuid); + + m_name = xml_element.attribute("name"); + + // lit l'orientation de la borne + m_orientation = Qet::orientationFromString(xml_element.attribute("orientation")); + + return true; +} diff --git a/sources/properties/terminaldata.h b/sources/properties/terminaldata.h new file mode 100644 index 000000000..fd1990f5e --- /dev/null +++ b/sources/properties/terminaldata.h @@ -0,0 +1,67 @@ +#ifndef TERMINALDATA_H +#define TERMINALDATA_H + +#include "propertiesinterface.h" +#include "qet.h" + +#include +#include + +class QGraphicsObject; + +/*! + * \brief The TerminalData class + * Data of the terminal. Stored in extra class so it can be used by PartTerminal and Terminal without + * defining everything again. + */ +class TerminalData : public PropertiesInterface +{ +public: + TerminalData(); + TerminalData(QGraphicsObject* parent); + ~TerminalData(); + + void setParent(QGraphicsObject* parent); + + // Save/load properties to setting file. QString is use for prefix a word befor the name of each paramètre + void toSettings(QSettings &settings, const QString = QString()) const override; + void fromSettings(const QSettings &settings, const QString = QString()) override; + // Save/load properties to xml element + // This method is only called from the PartTerminal and should never called from the Terminal class + QDomElement toXml(QDomDocument &xml_element) const override; + bool fromXml(const QDomElement &xml_element) override; + + // must be public, because this class is a private member of PartTerminal/Terminal and they must + // access this data +public: + Qet::Orientation m_orientation; + QPointF second_point; + /*! + * \brief m_uuid + * Uuid of the terminal. + * + * In elementscene.cpp an element gets a new uuid when saving the element. In the current state + * each connection is made by using the local position of the terminal and a dynamic id. In the new + * case, each terminal should have it's own uuid to identify it uniquely. When changing each time this + * uuid, the conductor after updating the part is anymore valid. So if in the loaded document a uuid exists, + * use this one and don't create a new one. + */ + QUuid m_uuid; + /*! + * \brief m_name + * Name of the element. It can be used to create wiring harness tables + */ + QString m_name; + + /*! + * \brief m_pos + * Position of the terminal. The second point is calculated from this position and the orientation + * Important: this variable is only updated during read from xml and not during mouse move! + * It is used to store the initial position so that PartTerminal and Terminal have access to it. + */ + QPointF m_pos; +private: + QGraphicsObject* q{nullptr}; +}; + +#endif // TERMINALDATA_H diff --git a/sources/qetgraphicsitem/element.cpp b/sources/qetgraphicsitem/element.cpp index 2eec4f07f..9a25d0fa0 100644 --- a/sources/qetgraphicsitem/element.cpp +++ b/sources/qetgraphicsitem/element.cpp @@ -23,6 +23,7 @@ #include "elementprovider.h" #include "diagramposition.h" #include "terminal.h" +#include "terminaldata.h" #include "PropertiesEditor/propertieseditordialog.h" #include "elementpropertieswidget.h" #include "numerotationcontextcommands.h" @@ -560,36 +561,22 @@ DynamicElementTextItem *Element::parseDynamicText(const QDomElement &dom_element return deti; } +/*! + * \brief Element::parseTerminal + * Parse partTerminal from xml structure + * \param dom_element + * \return + */ Terminal *Element::parseTerminal(const QDomElement &dom_element) { - qreal terminalx, terminaly; - Qet::Orientation terminalo; - if (!QET::attributeIsAReal(dom_element, QString("x"), &terminalx)) { - return(nullptr); - } - if (!QET::attributeIsAReal(dom_element, QString("y"), &terminaly)) { - return(nullptr); - } - if (!dom_element.hasAttribute("orientation")) { - return(nullptr); - } - if (dom_element.attribute("orientation") == "n") { - terminalo = Qet::North; - } - else if (dom_element.attribute("orientation") == "s") { - terminalo = Qet::South; - } - else if (dom_element.attribute("orientation") == "e") { - terminalo = Qet::East; - } - else if (dom_element.attribute("orientation") == "w") { - terminalo = Qet::West; - } - else { - return(nullptr); - } + + TerminalData* data = new TerminalData(); + if (!data->fromXml(dom_element)) { + delete data; + return nullptr; + } - Terminal *new_terminal = new Terminal(terminalx, terminaly, terminalo, this); + Terminal *new_terminal = new Terminal(data, this); m_terminals << new_terminal; //Sort from top to bottom and left to rigth diff --git a/sources/qetgraphicsitem/terminal.cpp b/sources/qetgraphicsitem/terminal.cpp index 090c7f03b..3d64ec2eb 100644 --- a/sources/qetgraphicsitem/terminal.cpp +++ b/sources/qetgraphicsitem/terminal.cpp @@ -24,6 +24,7 @@ #include "diagramcommands.h" #include "conductorautonumerotation.h" #include "conductortextitem.h" +#include "terminaldata.h" QColor Terminal::neutralColor = QColor(Qt::blue); QColor Terminal::allowedColor = QColor(Qt::darkGreen); @@ -39,17 +40,13 @@ const qreal Terminal::Z = 1000; @param number of terminal @param name of terminal */ -void Terminal::init(QPointF pf, Qet::Orientation o, QString number, QString name, bool hiddenName) { - // definition du pount d'amarrage pour un conducteur - dock_conductor_ = pf; - - // definition de l'orientation de la borne (par defaut : sud) - if (o < Qet::North || o > Qet::West) ori_ = Qet::South; - else ori_ = o; +void Terminal::init(QString number, QString name, bool hiddenName) { + + m_uuid = QUuid::createUuid(); // calcul de la position du point d'amarrage a l'element - dock_elmt_ = dock_conductor_; - switch(ori_) { + dock_elmt_ = d->m_pos; + switch(d->m_orientation) { case Qet::North: dock_elmt_ += QPointF(0, Terminal::terminalSize); break; case Qet::East : dock_elmt_ += QPointF(-Terminal::terminalSize, 0); break; case Qet::West : dock_elmt_ += QPointF(Terminal::terminalSize, 0); break; @@ -74,6 +71,27 @@ void Terminal::init(QPointF pf, Qet::Orientation o, QString number, QString name setZValue(Z); } +/*! + * \brief Terminal::init + * Additionaly to the init above, this method stores position and orientation into the data class + * \param pf + * \param o + * \param number + * \param name + * \param hiddenName + */ +void Terminal::init(QPointF pf, Qet::Orientation o, QString number, QString name, bool hiddenName) +{ + // definition du pount d'amarrage pour un conducteur + d->m_pos = pf; + + // definition de l'orientation de la borne (par defaut : sud) + if (o < Qet::North || o > Qet::West) d->m_orientation = Qet::South; + else d->m_orientation = o; + + init(number, name, hiddenName); +} + /** initialise une borne @param pf position du point d'amarrage pour un conducteur @@ -83,11 +101,12 @@ void Terminal::init(QPointF pf, Qet::Orientation o, QString number, QString name */ Terminal::Terminal(QPointF pf, Qet::Orientation o, Element *e) : QGraphicsObject(e), + d(new TerminalData(this)), m_draw_help_line(false), m_help_line (nullptr), m_help_line_a (nullptr), parent_element_ (e), - hovered_color_ (Terminal::neutralColor) + hovered_color_ (Terminal::neutralColor) { init(pf, o, "_", "_", false); } @@ -102,13 +121,14 @@ Terminal::Terminal(QPointF pf, Qet::Orientation o, Element *e) : */ Terminal::Terminal(qreal pf_x, qreal pf_y, Qet::Orientation o, Element *e) : QGraphicsObject(e), + d(new TerminalData(this)), m_draw_help_line (false), m_help_line (nullptr), m_help_line_a (nullptr), parent_element_ (e), hovered_color_ (Terminal::neutralColor) { - init(QPointF(pf_x, pf_y), o, "_", "_", false); + init(QPointF(pf_x, pf_y), o, "_", "_", false); } /** @@ -123,6 +143,7 @@ Terminal::Terminal(qreal pf_x, qreal pf_y, Qet::Orientation o, Element *e) : */ Terminal::Terminal(QPointF pf, Qet::Orientation o, QString num, QString name, bool hiddenName, Element *e) : QGraphicsObject (e), + d(new TerminalData(this)), m_draw_help_line (false), m_help_line (nullptr), m_help_line_a (nullptr), @@ -132,6 +153,15 @@ Terminal::Terminal(QPointF pf, Qet::Orientation o, QString num, QString name, bo init(pf, o, std::move(num), std::move(name), hiddenName); } +Terminal::Terminal(TerminalData* data, Element* e) : + QGraphicsObject(e), + d(data) +{ + // TODO: what is when multiple parents exist. So the other relation is lost. + d->setParent(this); + init("_", "_", false); +} + /** Destructeur La destruction de la borne entraine la destruction des conducteurs @@ -153,15 +183,15 @@ Qet::Orientation Terminal::orientation() const { if (Element *elt = qgraphicsitem_cast(parentItem())) { // orientations actuelle et par defaut de l'element int ori_cur = elt -> orientation(); - if (ori_cur == 0) return(ori_); + if (ori_cur == 0) return(d->m_orientation); else { // calcul l'angle de rotation implique par l'orientation de l'element parent // angle de rotation de la borne sur la scene, divise par 90 - int angle = ori_cur + ori_; + int angle = ori_cur + d->m_orientation; while (angle >= 4) angle -= 4; return((Qet::Orientation)angle); } - } else return(ori_); + } else return(d->m_orientation); } @@ -238,7 +268,7 @@ void Terminal::paint(QPainter *p, const QStyleOptionGraphicsItem *options, QWidg p -> setRenderHint(QPainter::SmoothPixmapTransform, false); // on travaille avec les coordonnees de l'element parent - QPointF c = mapFromParent(dock_conductor_); + QPointF c = mapFromParent(d->m_pos); QPointF e = mapFromParent(dock_elmt_); QPen t; @@ -390,11 +420,11 @@ QLineF Terminal::HelpLine() const */ QRectF Terminal::boundingRect() const { if (br_ -> isNull()) { - qreal dcx = dock_conductor_.x(); - qreal dcy = dock_conductor_.y(); + qreal dcx = d->m_pos.x(); + qreal dcy = d->m_pos.y(); qreal dex = dock_elmt_.x(); qreal dey = dock_elmt_.y(); - QPointF origin = (dcx <= dex && dcy <= dey ? dock_conductor_ : dock_elmt_); + QPointF origin = (dcx <= dex && dcy <= dey ? d->m_pos : dock_elmt_); origin += QPointF(-3.0, -3.0); qreal w = qAbs((int)(dcx - dex)) + 7; qreal h = qAbs((int)(dcy - dey)) + 7; @@ -492,10 +522,10 @@ void Terminal::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { @param e L'evenement souris correspondant */ void Terminal::mousePressEvent(QGraphicsSceneMouseEvent *e) { - if (Diagram *d = diagram()) { - d -> setConductorStart(mapToScene(QPointF(dock_conductor_))); - d -> setConductorStop(e -> scenePos()); - d -> setConductor(true); + if (Diagram *diag = diagram()) { + diag -> setConductorStart(mapToScene(QPointF(d->m_pos))); + diag -> setConductorStop(e -> scenePos()); + diag -> setConductor(true); //setCursor(Qt::CrossCursor); } } @@ -517,13 +547,13 @@ void Terminal::mouseMoveEvent(QGraphicsSceneMouseEvent *e) { } - Diagram *d = diagram(); - if (!d) return; + Diagram *diag = diagram(); + if (!diag) return; // si la scene est un Diagram, on actualise le poseur de conducteur - d -> setConductorStop(e -> scenePos()); + diag -> setConductorStop(e -> scenePos()); // on recupere la liste des qgi sous le pointeur - QList qgis = d -> items(e -> scenePos()); + QList qgis = diag -> items(e -> scenePos()); /* le qgi le plus haut = le poseur de conductor @@ -659,6 +689,10 @@ bool Terminal::isLinkedTo(Terminal *other_terminal) { /** * @brief Terminal::canBeLinkedTo + * Checking if the terminal can be linked to \p other_terminal or not + * Reasons for not linable: + * - \p other_terminal is this terminal + * - this terminal is already connected to \p other_terminal * @param other_terminal * @return true if this terminal can be linked to @other_terminal, * otherwise false @@ -687,10 +721,11 @@ QDomElement Terminal::toXml(QDomDocument &doc) const { QDomElement qdo = doc.createElement("terminal"); qdo.setAttribute("x", QString("%1").arg(dock_elmt_.x())); qdo.setAttribute("y", QString("%1").arg(dock_elmt_.y())); - qdo.setAttribute("orientation", ori_); + qdo.setAttribute("orientation", d->m_orientation); qdo.setAttribute("number", number_terminal_); qdo.setAttribute("name", name_terminal_); qdo.setAttribute("nameHidden", name_terminal_hidden); + qdo.setAttribute("uuid", m_uuid.toString()); return(qdo); } @@ -739,13 +774,25 @@ bool Terminal::fromXml(QDomElement &terminal) { number_terminal_ = terminal.attribute("number"); name_terminal_ = terminal.attribute("name"); name_terminal_hidden = terminal.attribute("nameHidden").toInt(); + QString uuid = terminal.attribute("uuid"); + if (!uuid.isEmpty()) + m_uuid = uuid; + return ( qFuzzyCompare(terminal.attribute("x").toDouble(), dock_elmt_.x()) && qFuzzyCompare(terminal.attribute("y").toDouble(), dock_elmt_.y()) && - (terminal.attribute("orientation").toInt() == ori_) + (terminal.attribute("orientation").toInt() == d->m_orientation) ); } +/** + @return the position, relative to the scene, of the docking point for + conductors. +*/ +inline QPointF Terminal::dockConductor() const { + return(mapToScene(d->m_pos)); +} + /** @return le Diagram auquel cette borne appartient, ou 0 si cette borne est independant */ diff --git a/sources/qetgraphicsitem/terminal.h b/sources/qetgraphicsitem/terminal.h index 7b9a488cc..a1a62cd49 100644 --- a/sources/qetgraphicsitem/terminal.h +++ b/sources/qetgraphicsitem/terminal.h @@ -23,9 +23,12 @@ class Conductor; class Diagram; class Element; +class TerminalData; + /** This class represents a terminal of an electrical element, i.e. a possible plug point for conductors. + This class handles all mouse events for connecting conductors */ class Terminal : public QGraphicsObject { @@ -39,6 +42,7 @@ class Terminal : public QGraphicsObject public: Terminal(QPointF, Qet::Orientation, Element * = nullptr); Terminal(qreal, qreal, Qet::Orientation, Element * = nullptr); + Terminal(TerminalData* data, Element *e = nullptr); Terminal(QPointF, Qet::Orientation, QString number, QString name, bool hiddenName, Element * = nullptr); ~Terminal() override; @@ -110,14 +114,13 @@ class Terminal : public QGraphicsObject QGraphicsLineItem *m_help_line; QGraphicsLineItem *m_help_line_a; + + TerminalData* d; + /// Parent electrical element Element *parent_element_; - /// docking point for conductors - QPointF dock_conductor_; /// docking point for parent element QPointF dock_elmt_; - /// terminal orientation - Qet::Orientation ori_; /// List of conductors attached to the terminal QList conductors_; /// Pointer to a rectangle representing the terminal bounding rect; @@ -135,9 +138,14 @@ class Terminal : public QGraphicsObject /// Name of Terminal QString name_terminal_; bool name_terminal_hidden; + + /// Unique identifier of the terminal + /// This uuid is different to the uuid of the part terminal it represents + QUuid m_uuid; private: - void init(QPointF, Qet::Orientation, QString number, QString name, bool hiddenName); + void init(QString number, QString name, bool hiddenName); + void init(QPointF pf, Qet::Orientation o, QString number, QString name, bool hiddenName); }; /** @@ -147,14 +155,6 @@ inline int Terminal::conductorsCount() const { return(conductors_.size()); } -/** - @return the position, relative to the scene, of the docking point for - conductors. -*/ -inline QPointF Terminal::dockConductor() const { - return(mapToScene(dock_conductor_)); -} - /** @return the number of terminal. */