diff --git a/sources/editor/graphicspart/customelementgraphicpart.cpp b/sources/editor/graphicspart/customelementgraphicpart.cpp index e19280ecc..1ba064e3b 100644 --- a/sources/editor/graphicspart/customelementgraphicpart.cpp +++ b/sources/editor/graphicspart/customelementgraphicpart.cpp @@ -144,7 +144,7 @@ void CustomElementGraphicPart::setAntialiased(const bool b) * Each style separate by ; and name-style/value are separate by : * @param qde : QDOmElement used to write the style. */ -void CustomElementGraphicPart::stylesToXml(QDomElement &qde) const +void CustomElementGraphicPart::stylesToXml(QDomDocument &xml_document, QDomElement &qde) const { QString css_like_styles; @@ -479,9 +479,8 @@ void CustomElementGraphicPart::stylesToXml(QDomElement &qde) const else if (_color == HTMLGrayBlackColor) css_like_styles += "HTMLGrayBlack"; else if (_color == NoneColor) css_like_styles += "none"; - - qde.setAttribute("style", css_like_styles); - qde.setAttribute("antialias", _antialiased ? "true" : "false"); + qde.appendChild(createXmlProperty(xml_document, "style", css_like_styles)); + qde.appendChild(createXmlProperty(xml_document, "antialias", _antialiased ? "true" : "false")); } @@ -493,13 +492,18 @@ void CustomElementGraphicPart::stylesToXml(QDomElement &qde) const void CustomElementGraphicPart::stylesFromXml(const QDomElement &qde) { resetStyles(); + + QString style_string; + propertyString(qde, "style", &style_string); //Get the list of pair style/value #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) // ### Qt 6: remove - QStringList styles = qde.attribute("style").split(";", QString::SkipEmptyParts); + QStringList styles = style_string.split(";", QString::SkipEmptyParts); #else - QStringList styles = qde.attribute("style").split(";", Qt::SkipEmptyParts); + QStringList styles = style_string.split(";", Qt::SkipEmptyParts); #endif + + //Check each pair of style QRegExp rx("^\\s*([a-z-]+)\\s*:\\s*([a-zA-Z-]+)\\s*$"); @@ -843,7 +847,9 @@ void CustomElementGraphicPart::stylesFromXml(const QDomElement &qde) } } //Get antialiasing - _antialiased = qde.attribute("antialias") == "true"; + QString a; + propertyString(qde, "antialias", &a); + _antialiased = a == "true"; } diff --git a/sources/editor/graphicspart/customelementgraphicpart.h b/sources/editor/graphicspart/customelementgraphicpart.h index 341c245c2..0fdf5979d 100644 --- a/sources/editor/graphicspart/customelementgraphicpart.h +++ b/sources/editor/graphicspart/customelementgraphicpart.h @@ -95,7 +95,7 @@ class CustomElementGraphicPart : public QGraphicsObject, public CustomElementPar virtual void resetAllHandlerColor() {} protected: - void stylesToXml (QDomElement &) const; + void stylesToXml (QDomDocument &xml_document, QDomElement &) const; void stylesFromXml(const QDomElement &); void resetStyles (); void applyStylesToQPainter(QPainter &) const; diff --git a/sources/editor/graphicspart/customelementpart.h b/sources/editor/graphicspart/customelementpart.h index 3b58a5698..f36658aa0 100644 --- a/sources/editor/graphicspart/customelementpart.h +++ b/sources/editor/graphicspart/customelementpart.h @@ -19,6 +19,7 @@ #define CUSTOM_ELEMENT_PART_H #include "qet.h" +#include "propertiesinterface.h" class CustomElement; class ElementPrimitiveDecorator; @@ -35,7 +36,7 @@ class QGraphicsSceneMouseEvent; is no point for those classes to store their visual representation with anything more complex than a QImage. */ -class CustomElementPart { +class CustomElementPart: public PropertiesInterface { // constructors, destructor public: /** @@ -55,14 +56,6 @@ class CustomElementPart { // methods public: - /** - Load the primitive from an XML element that describes it - */ - virtual void fromXml(const QDomElement &) = 0; - /** - Export the primitive as an XML element - */ - virtual const QDomElement toXml(QDomDocument &) const = 0; /** Set a specific property of the primitive */ diff --git a/sources/editor/graphicspart/partarc.cpp b/sources/editor/graphicspart/partarc.cpp index a42bff805..c9e526087 100644 --- a/sources/editor/graphicspart/partarc.cpp +++ b/sources/editor/graphicspart/partarc.cpp @@ -98,14 +98,18 @@ void PartArc::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, const QDomElement PartArc::toXml(QDomDocument &xml_document) const { QDomElement xml_element = xml_document.createElement("arc"); 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())); - //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); + + xml_element.appendChild(createXmlProperty(xml_document, "x", top_left.x())); + xml_element.appendChild(createXmlProperty(xml_document, "y", top_left.y())); + xml_element.appendChild(createXmlProperty(xml_document, "width", rect().width())); + xml_element.appendChild(createXmlProperty(xml_document, "height", rect().height())); + + //to maintain compatibility with the previous version, we write the angle in degrees. + xml_element.appendChild(createXmlProperty(xml_document, "start", m_start_angle / 16)); + xml_element.appendChild(createXmlProperty(xml_document, "angle", m_span_angle / 16)); + + + stylesToXml(xml_document, xml_element); return(xml_element); } @@ -115,14 +119,23 @@ const QDomElement PartArc::toXml(QDomDocument &xml_document) const { * @param qde : Xml document to use. */ void PartArc::fromXml(const QDomElement &qde) { - stylesFromXml(qde); - 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()) ); + stylesFromXml(qde); - m_start_angle = qde.attribute("start", "0").toDouble() * 16; - m_span_angle = qde.attribute("angle", "-1440").toDouble() * 16; + double x = 0, y = 0, w = 0, h = 0; // default values + propertyDouble(qde, "x", &x); + propertyDouble(qde, "y", &y); + propertyDouble(qde, "width", &w); + propertyDouble(qde, "height", &h); + + m_rect = QRectF(mapFromScene(x, y), QSizeF(w, h) ); + + m_start_angle = 0; + propertyDouble(qde, "start", &m_start_angle); + m_start_angle *= 16; + + m_span_angle = -1440; + propertyDouble(qde, "angle", &m_span_angle); + m_span_angle *= 16; } /** diff --git a/sources/editor/graphicspart/partellipse.cpp b/sources/editor/graphicspart/partellipse.cpp index c6fbdcba4..226719010 100644 --- a/sources/editor/graphicspart/partellipse.cpp +++ b/sources/editor/graphicspart/partellipse.cpp @@ -82,20 +82,20 @@ const QDomElement PartEllipse::toXml(QDomDocument &xml_document) const if (qFuzzyCompare(rect().width(), rect().height())) { xml_element = xml_document.createElement("circle"); - xml_element.setAttribute("diameter", QString("%1").arg(rect().width())); + xml_element.appendChild(createXmlProperty(xml_document, "diameter", rect().width())); } 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())); + xml_element.appendChild(createXmlProperty(xml_document, "width", rect().width())); + xml_element.appendChild(createXmlProperty(xml_document, "height", 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())); + xml_element.appendChild(createXmlProperty(xml_document, "x", top_left.x())); + xml_element.appendChild(createXmlProperty(xml_document, "y", top_left.y())); - stylesToXml(xml_element); + stylesToXml(xml_document, xml_element); return(xml_element); } @@ -108,19 +108,23 @@ const QDomElement PartEllipse::toXml(QDomDocument &xml_document) const void PartEllipse::fromXml(const QDomElement &qde) { stylesFromXml(qde); - qreal width, height; + double x = 0, y = 0, width = 0, height = 0; if (qde.tagName() == "ellipse") { - width = qde.attribute("width", "0").toDouble(); - height = qde.attribute("height", "0").toDouble(); + propertyDouble(qde, "width", &width); + propertyDouble(qde, "height", &height); } - else - width = height = qde.attribute("diameter", "0").toDouble(); + else { + propertyDouble(qde, "diameter", &width); + height = width; + } - m_rect = QRectF(mapFromScene(qde.attribute("x", "0").toDouble(), - qde.attribute("y", "0").toDouble()), - QSizeF(width, height)); + + propertyDouble(qde, "x", &x); + propertyDouble(qde, "y", &y); + + m_rect = QRectF(mapFromScene(x, y), QSizeF(width, height)); } /** diff --git a/sources/editor/graphicspart/partline.cpp b/sources/editor/graphicspart/partline.cpp index fc6d0746e..57d75900d 100644 --- a/sources/editor/graphicspart/partline.cpp +++ b/sources/editor/graphicspart/partline.cpp @@ -108,16 +108,18 @@ const QDomElement PartLine::toXml(QDomDocument &xml_document) const QPointF p2(sceneP2()); QDomElement xml_element = xml_document.createElement("line"); - xml_element.setAttribute("x1", QString("%1").arg(p1.x())); - xml_element.setAttribute("y1", QString("%1").arg(p1.y())); - xml_element.setAttribute("x2", QString("%1").arg(p2.x())); - xml_element.setAttribute("y2", QString("%1").arg(p2.y())); - xml_element.setAttribute("end1", Qet::endTypeToString(first_end)); - xml_element.setAttribute("length1", QString("%1").arg(first_length)); - xml_element.setAttribute("end2", Qet::endTypeToString(second_end)); - xml_element.setAttribute("length2", QString("%1").arg(second_length)); + + xml_element.appendChild(createXmlProperty(xml_document, "x1", p1.x())); + xml_element.appendChild(createXmlProperty(xml_document, "y1", p1.y())); + xml_element.appendChild(createXmlProperty(xml_document, "x2", p2.x())); + xml_element.appendChild(createXmlProperty(xml_document, "y2", p2.y())); + + xml_element.appendChild(createXmlProperty(xml_document, "end1", Qet::endTypeToString(first_end))); + xml_element.appendChild(createXmlProperty(xml_document, "length1", first_length)); + xml_element.appendChild(createXmlProperty(xml_document, "end2", Qet::endTypeToString(second_end))); + xml_element.appendChild(createXmlProperty(xml_document, "length2", second_length)); - stylesToXml(xml_element); + stylesToXml(xml_document, xml_element); return(xml_element); } @@ -128,15 +130,28 @@ const QDomElement PartLine::toXml(QDomDocument &xml_document) const */ void PartLine::fromXml(const QDomElement &qde) { stylesFromXml(qde); - 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(); + double x1 = 0, y1 = 0, x2 = 0, y2 = 0; + propertyDouble(qde, "x1", &x1); + propertyDouble(qde, "y1", &y1); + propertyDouble(qde, "x2", &x2); + propertyDouble(qde, "y2", &y2); + + m_line = QLineF(mapFromScene(x1, y1), + mapFromScene(x2, y2)); + + QString s; + propertyString(qde, "end1", &s); + first_end = Qet::endTypeFromString(s); + + propertyString(qde, "end2", &s); + first_end = Qet::endTypeFromString(s); + + first_length = 1.5; + second_length = 1.5; + + propertyDouble(qde, "length1", &first_length); + propertyDouble(qde, "length2", &second_length); } /** diff --git a/sources/editor/graphicspart/partpolygon.cpp b/sources/editor/graphicspart/partpolygon.cpp index 4767337ca..2e518bf65 100644 --- a/sources/editor/graphicspart/partpolygon.cpp +++ b/sources/editor/graphicspart/partpolygon.cpp @@ -89,22 +89,24 @@ void PartPolygon::fromXml(const QDomElement &qde) int i = 1; while(true) { - if (QET::attributeIsAReal(qde, QString("x%1").arg(i)) &&\ - QET::attributeIsAReal(qde, QString("y%1").arg(i))) - ++ i; + if (propertyDouble(qde, QString("x%1").arg(i)) && + propertyDouble(qde, QString("y%1").arg(i))) + i++; else break; } QPolygonF temp_polygon; + double x, y; 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()); + propertyDouble(qde, QString("x%1").arg(j), &x); + propertyDouble(qde, QString("y%1").arg(j), &y); + temp_polygon << QPointF(x, y); } m_polygon = temp_polygon; - m_closed = qde.attribute("closed") != "false"; + propertyBool(qde, "closed", &m_closed); } /** @@ -119,12 +121,14 @@ const QDomElement PartPolygon::toXml(QDomDocument &xml_document) const int i = 1; 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())); + xml_element.appendChild(createXmlProperty(xml_document, QString("x%1").arg(i), point.x())); + xml_element.appendChild(createXmlProperty(xml_document, QString("y%1").arg(i), point.y())); ++ i; } - if (!m_closed) xml_element.setAttribute("closed", "false"); - stylesToXml(xml_element); + + xml_element.appendChild(createXmlProperty(xml_document, "closed", m_closed)); + + stylesToXml(xml_document, xml_element); return(xml_element); } diff --git a/sources/editor/graphicspart/partrectangle.cpp b/sources/editor/graphicspart/partrectangle.cpp index 5f17b7252..96ad18a1f 100644 --- a/sources/editor/graphicspart/partrectangle.cpp +++ b/sources/editor/graphicspart/partrectangle.cpp @@ -81,10 +81,11 @@ 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(m_rect.width())); - xml_element.setAttribute("height", QString("%1").arg(m_rect.height())); + + xml_element.appendChild(createXmlProperty(xml_document, "x", top_left.x())); + xml_element.appendChild(createXmlProperty(xml_document, "y", top_left.y())); + xml_element.appendChild(createXmlProperty(xml_document, "width", m_rect.width())); + xml_element.appendChild(createXmlProperty(xml_document, "height", m_rect.height())); QRectF rect = m_rect.normalized(); qreal x = m_xRadius; @@ -98,8 +99,11 @@ const QDomElement PartRectangle::toXml(QDomDocument &xml_document) const xml_element.setAttribute("rx", QString::number(m_xRadius)); xml_element.setAttribute("ry", QString::number(m_yRadius)); + + xml_element.appendChild(createXmlProperty(xml_document, "rx", m_xRadius)); + xml_element.appendChild(createXmlProperty(xml_document, "ry", m_yRadius)); - stylesToXml(xml_element); + stylesToXml(xml_document, xml_element); return(xml_element); } diff --git a/sources/editor/graphicspart/parttext.cpp b/sources/editor/graphicspart/parttext.cpp index 9e1b9b5c6..e3dbf794a 100644 --- a/sources/editor/graphicspart/parttext.cpp +++ b/sources/editor/graphicspart/parttext.cpp @@ -59,28 +59,41 @@ void PartText::fromXml(const QDomElement &xml_element) { bool ok; - if (xml_element.hasAttribute("size")) + int size; + QString font; + + if (propertyInteger(xml_element, "size", &size) != PropertyFlags::NotFound) { - int font_size = xml_element.attribute("size").toInt(&ok); - if (!ok || font_size < 1) { - font_size = 20; + if (size < 1) { + size = 20; } QFont font_ = this->font(); - font_.setPointSize(font_size); + font_.setPointSize(size); setFont(font_); } - else if (xml_element.hasAttribute("font")) + else if (propertyString(xml_element, "font", &font) != PropertyFlags::NotFound) { QFont font_; - font_.fromString(xml_element.attribute("font")); + font_.fromString(font); setFont(font_); - } + } - setDefaultTextColor(QColor(xml_element.attribute("color", "#000000"))); - setPlainText(xml_element.attribute("text")); - setPos(xml_element.attribute("x").toDouble(), - xml_element.attribute("y").toDouble()); - setRotation(xml_element.attribute("rotation", QString::number(0)).toDouble()); + QString color; + QString text; + propertyString(xml_element, "color", &color, "#000000"); + setDefaultTextColor(QColor(color)); + + + propertyString(xml_element, "text", &text); + setPlainText(text); + + double x, y, rot; + propertyDouble(xml_element, "x", &x, 0); + propertyDouble(xml_element, "y", &y, 0); + setPos(x, y); + + propertyDouble(xml_element, "rotation", &rot, 0); + setRotation(rot); } /** @@ -92,12 +105,12 @@ const QDomElement PartText::toXml(QDomDocument &xml_document) const { QDomElement xml_element = xml_document.createElement(xmlName()); - xml_element.setAttribute("x", QString::number(pos().x())); - xml_element.setAttribute("y", QString::number(pos().y())); - xml_element.setAttribute("text", toPlainText()); - xml_element.setAttribute("font", font().toString()); - xml_element.setAttribute("rotation", QString::number(rotation())); - xml_element.setAttribute("color", defaultTextColor().name()); + xml_element.appendChild(createXmlProperty(xml_document, "x", pos().x())); + xml_element.appendChild(createXmlProperty(xml_document, "y", pos().y())); + xml_element.appendChild(createXmlProperty(xml_document, "text", toPlainText())); + xml_element.appendChild(createXmlProperty(xml_document, "font", font().toString())); + xml_element.appendChild(createXmlProperty(xml_document, "rotation", rotation())); + xml_element.appendChild(createXmlProperty(xml_document, "color", defaultTextColor().name())); return(xml_element); } diff --git a/sources/properties/propertiesinterface.cpp b/sources/properties/propertiesinterface.cpp index da7b042d4..9ffd197a5 100644 --- a/sources/properties/propertiesinterface.cpp +++ b/sources/properties/propertiesinterface.cpp @@ -17,6 +17,224 @@ */ #include "propertiesinterface.h" +/*! + * Available property types + */ +namespace { + const QString integerS = "int"; + const QString doubleS = "double"; + const QString boolS = "bool"; + const QString stringS = "string"; +} + PropertiesInterface::PropertiesInterface() { } + +QDomElement PropertiesInterface::createXmlProperty(QDomDocument &doc, const QString& name, const QString value) const { + QDomElement p = doc.createElement("property"); + p.setAttribute("name", name); + p.setAttribute("type", stringS); + p.setAttribute("value", value); + return p; +} + +QDomElement PropertiesInterface::createXmlProperty(QDomDocument& doc, const QString& name, const int value) const { + QDomElement p = doc.createElement("property"); + p.setAttribute("name", name); + p.setAttribute("type", integerS); + p.setAttribute("value", value); + return p; +} + +QDomElement PropertiesInterface::createXmlProperty(QDomDocument& doc, const QString& name, const double value) const { + QDomElement p = doc.createElement("property"); + p.setAttribute("name", name); + p.setAttribute("type", doubleS); + p.setAttribute("value", value); + return p; +} + +QDomElement PropertiesInterface::createXmlProperty(QDomDocument& doc, const QString& name, const bool value) const { + QDomElement p = doc.createElement("property"); + p.setAttribute("name", name); + p.setAttribute("type", boolS); + p.setAttribute("value", value); + return p; +} + +QDomElement PropertiesInterface::property(const QDomElement& e, const QString& name) { + for (int i=0; i < e.childNodes().count(); i++) { + QDomElement child = e.childNodes().at(i).toElement(); + if (!validXmlProperty(child)) + return QDomElement(); + + if (child.attribute("name") == name) + return child; + } + return QDomElement(); +} + +/*! + * \brief PropertiesInterface::attribute + * Returns the property with the name \p attribute_name + * \param e Xml element which contains the property + * \param attribute_name + * \param type Type of the property + * \param attr + * \return + */ +bool PropertiesInterface::attribute(const QDomElement& e, const QString& attribute_name, const QString& type, QString* attr) { + QDomElement p = property(e, attribute_name); + if (p.isNull()) { + // check if legacy property is available, + // where the property is inside the element as attribute + if (!e.hasAttribute(attribute_name)) + return false; + + *attr = e.attribute(attribute_name); + + } else { + if (p.attribute("type") != type) + return false; + + *attr = p.attribute("value"); + + } + return true; +} + +/*! + * \brief PropertiesInterface::propertyInteger + * Reads an interger from the XML element. + * \param e DomElement which contains the property attribute + * \param attribute_name Name of the attribute + * \param entier Return value if success + * \return True if reading an integer was successful, else False. If the attribute was not found, + * \p entier is not valid and the return value is False + */ +PropertiesInterface::PropertyFlags PropertiesInterface::propertyInteger(const QDomElement &e, const QString& attribute_name, int* entier, int defaultValue) { + + QString attr; + + if (!attribute(e, attribute_name, integerS, &attr)) { + *entier = defaultValue; + return PropertyFlags::NotFound; + } + + // verifie la validite de l'attribut + bool ok; + int tmp = attr.toInt(&ok); + if (!ok) + return PropertyFlags::NoValidConversion; + + if (entier != nullptr) + *entier = tmp; + + return PropertyFlags::Success; +} + +PropertiesInterface::PropertyFlags PropertiesInterface::propertyDouble(const QDomElement &e, const QString& attribute_name, double* reel, double defaultValue) { + + QString attr; + + if (!attribute(e, attribute_name, doubleS, &attr)) { + *reel = defaultValue; + return PropertyFlags::NotFound; + } + + // verifie la validite de l'attribut + bool ok; + double tmp = attr.toDouble(&ok); + if (!ok) + return PropertyFlags::NoValidConversion; + + if (reel != nullptr) + *reel = tmp; + + return PropertyFlags::Success; +} + +PropertiesInterface::PropertyFlags PropertiesInterface::propertyBool(const QDomElement &e, const QString& attribute_name, bool* boolean, bool defaulValue) { + + QString attr; + + if (!attribute(e, attribute_name, integerS, &attr)) { + *boolean = defaulValue; + return PropertyFlags::NotFound; + } + + // verifie la validite de l'attribut + bool ok; + bool tmp = attr.toInt(&ok); + if (!ok) + return PropertyFlags::NoValidConversion; + + if (boolean != nullptr) + *boolean = tmp; + + return PropertyFlags::Success; +} + +PropertiesInterface::PropertyFlags PropertiesInterface::propertyString(const QDomElement& e, const QString& attribute_name, QString* string, QString defaultValue) { + + QString attr; + if (!attribute(e, attribute_name, stringS, &attr)) { + *string = defaultValue; + return PropertyFlags::NotFound; + } + + // verifie la validite de l'attribut + if (string != nullptr) + *string = attr; + + return PropertyFlags::Success; +} + +/*! + * \brief PropertiesInterface::validXmlProperty + * Check if the Xml element contains the needed fields + * \param e Xml Property + * \return True if name, type, value attribute are available, else false + */ +bool PropertiesInterface::validXmlProperty(const QDomElement& e) { + if (!e.hasAttribute("name")) + return false; + + if (!e.hasAttribute("type")) + return false; + + if (!e.hasAttribute("value")) + return false; +} + +/** + Permet de convertir une chaine de caracteres ("n", "s", "e" ou "w") + en orientation. Si la chaine fait plusieurs caracteres, seul le + premier est pris en compte. En cas d'incoherence, Qet::North est + retourne. + @param s Chaine de caractere cense representer une orientation + @return l'orientation designee par la chaine de caractere +*/ +Qet::Orientation PropertiesInterface::orientationFromString(const QString &s) { + QChar c = s[0]; + if (c == 'e') return(Qet::East); + else if (c == 's') return(Qet::South); + else if (c == 'w') return (Qet::West); + else return(Qet::North); +} + +/** + @param o une orientation + @return une chaine de caractere representant l'orientation +*/ +QString PropertiesInterface::orientationToString(Qet::Orientation o) { + QString ret; + switch(o) { + case Qet::North: ret = "n"; break; + case Qet::East : ret = "e"; break; + case Qet::South: ret = "s"; break; + case Qet::West : ret = "w"; break; + } + return(ret); +} diff --git a/sources/properties/propertiesinterface.h b/sources/properties/propertiesinterface.h index 0799ab69b..0c4bc5793 100644 --- a/sources/properties/propertiesinterface.h +++ b/sources/properties/propertiesinterface.h @@ -21,6 +21,8 @@ #include #include #include +#include +#include "qet.h" /** * @brief The PropertiesInterface class @@ -36,6 +38,49 @@ class PropertiesInterface // Save/load properties to xml element virtual QDomElement toXml (QDomDocument &xml_document) const =0; virtual bool fromXml (const QDomElement &xml_element) =0; + virtual bool valideXml(QDomElement& element) const = 0; + + /*! + * Use this functions to add properties to the xml document + */ + QDomElement createXmlProperty(QDomDocument& doc, const QString& name, const QString value) const; + QDomElement createXmlProperty(QDomDocument& doc, const QString& name, const int value) const; + QDomElement createXmlProperty(QDomDocument& doc, const QString& name, const double value) const; + QDomElement createXmlProperty(QDomDocument& doc, const QString& name, const bool value) const; + + static QDomElement property(const QDomElement& e, const QString& name); + static bool attribute(const QDomElement& e, const QString& attribute_name, const QString& type, QString* attr); + + typedef enum PropertyFlags { + Success, + NotFound, + NoValidConversion, + }; + + static PropertyFlags propertyInteger(const QDomElement &e, const QString& attribute_name, int *entier = nullptr, int defaultValue = std::numeric_limits::quiet_NaN()); + static PropertyFlags propertyDouble(const QDomElement &e, const QString& attribute_name, double *reel = nullptr, double defaultValue = std::numeric_limits::quiet_NaN()); + static PropertyFlags propertyString(const QDomElement& e, const QString& attribute_name, QString* string = nullptr, QString defaultValue = QString()); + static PropertyFlags propertyBool(const QDomElement &e, const QString& attribute_name, bool* boolean = nullptr, bool defaulValue = false); + + static bool validXmlProperty(const QDomElement& e); + + QVariant XmlProperty(const QDomElement& element); + + /** + Permet de convertir une chaine de caracteres ("n", "s", "e" ou "w") + en orientation. Si la chaine fait plusieurs caracteres, seul le + premier est pris en compte. En cas d'incoherence, Qet::North est + retourne. + @param s Chaine de caractere cense representer une orientation + @return l'orientation designee par la chaine de caractere + */ + static Qet::Orientation orientationFromString(const QString &s); + + /** + @param o une orientation + @return une chaine de caractere representant l'orientation + */ + static QString orientationToString(Qet::Orientation o); }; #endif // PROPERTIESINTERFACE_H diff --git a/sources/properties/terminaldata.cpp b/sources/properties/terminaldata.cpp index 27bf607f2..2aefb0710 100644 --- a/sources/properties/terminaldata.cpp +++ b/sources/properties/terminaldata.cpp @@ -43,17 +43,11 @@ 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 + xml_element.appendChild(createXmlProperty(xml_document, "x", q->scenePos().x())); + xml_element.appendChild(createXmlProperty(xml_document, "y", q->scenePos().y())); + xml_element.appendChild(createXmlProperty(xml_document, "uuid", m_uuid.toString())); + xml_element.appendChild(createXmlProperty(xml_document, "name", m_name)); + xml_element.appendChild(createXmlProperty(xml_document, "orientation", orientationToString(m_orientation))); return(xml_element); } @@ -61,27 +55,40 @@ 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)) + + if (!propertyDouble(xml_element, "x", &term_x)) return false; - if (!QET::attributeIsAReal(xml_element, "y", &term_y)) + if (!propertyDouble(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"); + QString uuid; + if (!propertyString(xml_element, "uuid", &uuid)) + return false; + // update part and add uuid, which is used in the new version to connect terminals together // if the attribute not exists, means, the element is created with an older version of qet. So use the legacy approach // to identify terminals if (!uuid.isEmpty()) m_uuid = QUuid(uuid); - m_name = xml_element.attribute("name"); + if (!propertyString(xml_element, "name", &m_name)) + return false; + + QString o; + if (!propertyString(xml_element, "orientation", &o)) + return false; // lit l'orientation de la borne - m_orientation = Qet::orientationFromString(xml_element.attribute("orientation")); + m_orientation = orientationFromString(o); return true; } + +bool TerminalData::valideXml(QDomElement& element) const { + return true; +} diff --git a/sources/properties/terminaldata.h b/sources/properties/terminaldata.h index c31e73929..4d5301cda 100644 --- a/sources/properties/terminaldata.h +++ b/sources/properties/terminaldata.h @@ -35,6 +35,24 @@ public: QDomElement toXml(QDomDocument &xml_element) const override; bool fromXml(const QDomElement &xml_element) override; + bool valideXml(QDomElement &element) const override; + + /** + Permet de convertir une chaine de caracteres ("n", "s", "e" ou "w") + en orientation. Si la chaine fait plusieurs caracteres, seul le + premier est pris en compte. En cas d'incoherence, Qet::North est + retourne. + @param s Chaine de caractere cense representer une orientation + @return l'orientation designee par la chaine de caractere + */ + static Qet::Orientation orientationFromString(const QString &s); + + /** + @param o une orientation + @return une chaine de caractere representant l'orientation + */ + static QString orientationToString(Qet::Orientation o); + // must be public, because this class is a private member of PartTerminal/Terminal and they must // access this data public: diff --git a/sources/properties/xrefproperties.cpp b/sources/properties/xrefproperties.cpp index 538549761..360ad9b5f 100644 --- a/sources/properties/xrefproperties.cpp +++ b/sources/properties/xrefproperties.cpp @@ -96,27 +96,21 @@ void XRefProperties::fromSettings(const QSettings &settings, const QString prefi QDomElement XRefProperties::toXml(QDomDocument &xml_document) const { QDomElement xml_element = xml_document.createElement("xref"); - xml_element.setAttribute("type", m_key); - xml_element.setAttribute("showpowerctc", m_show_power_ctc? "true" : "false"); - QString display = m_display == Cross? "cross" : "contacts"; - xml_element.setAttribute("displayhas", display); - QString snap = m_snap_to == Bottom? "bottom" : "label"; - xml_element.setAttribute("snapto", snap); + xml_element.appendChild(createXmlProperty(xml_document, "type", m_key)); + xml_element.appendChild(createXmlProperty(xml_document, "showpowerctc", m_show_power_ctc)); + xml_element.appendChild(createXmlProperty(xml_document, "displayhas", m_display == Cross? "cross" : "contacts")); + xml_element.appendChild(createXmlProperty(xml_document, "snapto", m_snap_to == Bottom? "bottom" : "label")); - QString xrefpos; - - QMetaEnum var = QMetaEnum::fromType(); - xml_element.setAttribute("xrefpos", var.valueToKey(m_xref_pos)); - int offset = m_offset; - xml_element.setAttribute("offset", QString::number(offset)); - QString master_label = m_master_label; - xml_element.setAttribute("master_label", master_label); - QString slave_label = m_slave_label; - xml_element.setAttribute("slave_label", slave_label); + QMetaEnum var = QMetaEnum::fromType(); + xml_element.appendChild(createXmlProperty(xml_document, "xrefpos", var.valueToKey(m_xref_pos))); + xml_element.appendChild(createXmlProperty(xml_document, "offset", m_offset)); + xml_element.appendChild(createXmlProperty(xml_document, "master_label", m_master_label)); + xml_element.appendChild(createXmlProperty(xml_document, "slave_label", m_slave_label)); + foreach (QString key, m_prefix.keys()) { - xml_element.setAttribute(key + "prefix", m_prefix.value(key)); + xml_element.appendChild(createXmlProperty(xml_document, key + "prefix", m_prefix.value(key))); } return xml_element; @@ -128,30 +122,42 @@ QDomElement XRefProperties::toXml(QDomDocument &xml_document) const { * @param xml_element: QDomElement to use for load */ bool XRefProperties::fromXml(const QDomElement &xml_element) { - m_show_power_ctc = xml_element.attribute("showpowerctc") == "true"; - QString display = xml_element.attribute("displayhas", "cross"); + + if (!propertyBool(xml_element, "showpowerctc", &m_show_power_ctc)) + return false; + + QString display; + propertyString(xml_element, "displayhas", &display, "cross"); display == "cross"? m_display = Cross : m_display = Contacts; - QString snap = xml_element.attribute("snapto", "label"); + + + QString snap; + propertyString(xml_element, "snapto", &snap, "label"); snap == "bottom"? m_snap_to = Bottom : m_snap_to = Label; - QString xrefpos = xml_element.attribute("xrefpos","Left"); + QString xrefpos; + if (propertyString(xml_element, "xrefpos", &xrefpos, "Left") == PropertyFlags::NotFound) { + QMetaEnum var = QMetaEnum::fromType(); + m_xref_pos = Qt::AlignmentFlag(var.keyToValue(xrefpos.toStdString().data())); + } else + m_xref_pos = Qt::AlignBottom; - QMetaEnum var = QMetaEnum::fromType(); - - if(xml_element.hasAttribute("xrefpos")) - m_xref_pos = Qt::AlignmentFlag(var.keyToValue(xml_element.attribute("xrefpos").toStdString().data())); - else - m_xref_pos = Qt::AlignBottom; - - m_offset = xml_element.attribute("offset", "0").toInt(); - m_master_label = xml_element.attribute("master_label", "%f-%l%c"); - m_slave_label = xml_element.attribute("slave_label","(%f-%l%c)"); + propertyInteger(xml_element, "offset", &m_offset, 0); + propertyString(xml_element, "master_label", &m_master_label, "%f-%l%c"); + propertyString(xml_element, "slave_label", &m_slave_label, "(%f-%l%c)"); + QString value; foreach (QString key, m_prefix_keys) { - m_prefix.insert(key, xml_element.attribute(key + "prefix")); + propertyString(xml_element, key + "prefix", &value); + m_prefix.insert(key, value); } return true; } +bool XRefProperties::valideXml(QDomElement& element) const { + // TODO: implement + return true; +} + /** * @brief XRefProperties::defaultProperties * @return the default properties stored in the setting file diff --git a/sources/properties/xrefproperties.h b/sources/properties/xrefproperties.h index dc03196cf..e10ee872a 100644 --- a/sources/properties/xrefproperties.h +++ b/sources/properties/xrefproperties.h @@ -44,6 +44,7 @@ class XRefProperties : public PropertiesInterface void fromSettings (const QSettings &settings, const QString = QString()) override; QDomElement toXml (QDomDocument &xml_document) const override; bool fromXml(const QDomElement &xml_element) override; + bool valideXml(QDomElement& element) const override; static QHash defaultProperties(); diff --git a/sources/qet.cpp b/sources/qet.cpp index 928ccda86..c2702e7bf 100644 --- a/sources/qet.cpp +++ b/sources/qet.cpp @@ -25,37 +25,6 @@ #include #include -/** - Permet de convertir une chaine de caracteres ("n", "s", "e" ou "w") - en orientation. Si la chaine fait plusieurs caracteres, seul le - premier est pris en compte. En cas d'incoherence, Qet::North est - retourne. - @param s Chaine de caractere cense representer une orientation - @return l'orientation designee par la chaine de caractere -*/ -Qet::Orientation Qet::orientationFromString(const QString &s) { - QChar c = s[0]; - if (c == 'e') return(Qet::East); - else if (c == 's') return(Qet::South); - else if (c == 'w') return (Qet::West); - else return(Qet::North); -} - -/** - @param o une orientation - @return une chaine de caractere representant l'orientation -*/ -QString Qet::orientationToString(Qet::Orientation o) { - QString ret; - switch(o) { - case Qet::North: ret = "n"; break; - case Qet::East : ret = "e"; break; - case Qet::South: ret = "s"; break; - case Qet::West : ret = "w"; break; - } - return(ret); -} - /** Indique si deux orientations de Borne sont sur le meme axe (Vertical / Horizontal). @param a La premiere orientation de Borne diff --git a/sources/qetgraphicsitem/terminal.cpp b/sources/qetgraphicsitem/terminal.cpp index 1f32441b6..f2f13dfe6 100644 --- a/sources/qetgraphicsitem/terminal.cpp +++ b/sources/qetgraphicsitem/terminal.cpp @@ -719,15 +719,13 @@ QList Terminal::conductors() const { QDomElement Terminal::toXml(QDomDocument &doc) const { QDomElement qdo = doc.createElement("terminal"); - // for backward compatibility - qdo.setAttribute("x", QString("%1").arg(dock_elmt_.x())); - qdo.setAttribute("y", QString("%1").arg(dock_elmt_.y())); - // end for backward compatibility + qdo.appendChild(createXmlProperty(doc, "x", dock_elmt_.x())); + qdo.appendChild(createXmlProperty(doc, "y", dock_elmt_.y())); + qdo.appendChild(createXmlProperty(doc, "orientation", orientationToString(d->m_orientation))); + qdo.appendChild(createXmlProperty(doc, "number", number_terminal_)); + qdo.appendChild(createXmlProperty(doc, "name", name_terminal_)); + qdo.appendChild(createXmlProperty(doc, "nameHidden", name_terminal_hidden)); - qdo.setAttribute("orientation", d->m_orientation); - qdo.setAttribute("number", number_terminal_); - qdo.setAttribute("name", name_terminal_); - qdo.setAttribute("nameHidden", name_terminal_hidden); return(qdo); } @@ -737,38 +735,36 @@ QDomElement Terminal::toXml(QDomDocument &doc) const { @param terminal Le QDomElement a analyser @return true si le QDomElement passe en parametre est une borne, false sinon */ -bool Terminal::valideXml(QDomElement &terminal) { - // verifie le nom du tag +bool Terminal::valideXml(QDomElement &terminal) const { if (terminal.tagName() != "terminal") return(false); + + if (!propertyString(terminal, "number", nullptr)) + return false; + + if (!propertyString(terminal, "name", nullptr)) + return false; + + if (!propertyBool(terminal, "nameHidden", nullptr)) + return false; + + if (!propertyDouble(terminal, "x", nullptr)) + return false; + if (!propertyDouble(terminal, "y", nullptr)) + return false; + + QString o; + if (!propertyString(terminal, "orientation", &o)) + return false; + + Qet::Orientation terminal_or = orientationFromString(o); + if (terminal_or != Qet::North + && terminal_or != Qet::South + && terminal_or != Qet::East + && terminal_or != Qet::West) + return false; - // verifie la presence des attributs minimaux - if (!terminal.hasAttribute("x")) return(false); - if (!terminal.hasAttribute("y")) return(false); - if (!terminal.hasAttribute("orientation")) return(false); - - bool conv_ok; - // parse l'abscisse - terminal.attribute("x").toDouble(&conv_ok); - if (!conv_ok) return(false); - - // parse l'ordonnee - terminal.attribute("y").toDouble(&conv_ok); - if (!conv_ok) return(false); - - // parse l'id - terminal.attribute("id").toInt(&conv_ok); - if (!conv_ok) return(false); - - // parse l'orientation - int terminal_or = terminal.attribute("orientation").toInt(&conv_ok); - if (!conv_ok) return(false); - if (terminal_or != Qet::North - && terminal_or != Qet::South - && terminal_or != Qet::East - && terminal_or != Qet::West) return(false); - - // a ce stade, la borne est syntaxiquement correcte - return(true); + // a ce stade, la borne est syntaxiquement correcte + return true; } /** @@ -779,15 +775,30 @@ bool Terminal::valideXml(QDomElement &terminal) { @return true si la borne "se reconnait" (memes coordonnes, meme orientation), false sinon */ -bool Terminal::fromXml(QDomElement &terminal) { - number_terminal_ = terminal.attribute("number"); - name_terminal_ = terminal.attribute("name"); - name_terminal_hidden = terminal.attribute("nameHidden").toInt(); +bool Terminal::fromXml(const QDomElement &terminal) { + if (!propertyString(terminal, "number", &number_terminal_)) + return false; - return ( - qFuzzyCompare(terminal.attribute("x").toDouble(), dock_elmt_.x()) && - qFuzzyCompare(terminal.attribute("y").toDouble(), dock_elmt_.y()) && - (terminal.attribute("orientation").toInt() == d->m_orientation) + if (!propertyString(terminal, "name", &name_terminal_)) + return false; + + if (!propertyBool(terminal, "nameHidden", &name_terminal_hidden)) + return false; + + double x, y; + if (!propertyDouble(terminal, "x", &x)) + return false; + if (!propertyDouble(terminal, "y", &y)) + return false; + + QString o; + if (!propertyString(terminal, "orientation", &o)) + return false; + + return ( + qFuzzyCompare(x, dock_elmt_.x()) && + qFuzzyCompare(y, dock_elmt_.y()) && + (orientationFromString(o) == d->m_orientation) ); } diff --git a/sources/qetgraphicsitem/terminal.h b/sources/qetgraphicsitem/terminal.h index 17e9d7833..b33fdcae9 100644 --- a/sources/qetgraphicsitem/terminal.h +++ b/sources/qetgraphicsitem/terminal.h @@ -20,6 +20,8 @@ #include #include #include "qet.h" +#include "propertiesinterface.h" + class Conductor; class Diagram; class Element; @@ -31,7 +33,7 @@ class TerminalData; plug point for conductors. This class handles all mouse events for connecting conductors */ -class Terminal : public QGraphicsObject +class Terminal : public QGraphicsObject, public PropertiesInterface { Q_OBJECT @@ -88,9 +90,9 @@ class Terminal : public QGraphicsObject bool canBeLinkedTo(Terminal *); // methods related to XML import/export - static bool valideXml(QDomElement &); - bool fromXml (QDomElement &); - QDomElement toXml (QDomDocument &) const; + bool valideXml(QDomElement &) const override; + bool fromXml (const QDomElement &) override; + QDomElement toXml (QDomDocument &) const override; protected: // methods related to events management