mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-10 22:24:13 +02:00
Merge remote-tracking branch 'upstream/master' into fix/report-arrow-without-terminal
# Conflicts: # tests/qttest/tst_resaveunchanged.cpp
This commit is contained in:
+6
-6
@@ -826,7 +826,7 @@
|
||||
<message>
|
||||
<location filename="../sources/autoNum/ui/autonumberingdockwidget.cpp" line="294"/>
|
||||
<source>Auto Numbering</source>
|
||||
<translation>Automatische Nummerierung</translation>
|
||||
<translation>Autom. Nummerierung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../sources/autoNum/ui/autonumberingdockwidget.cpp" line="386"/>
|
||||
@@ -1512,7 +1512,7 @@ Bemerkung: diese Optionen verhindern NICHT das automatische Nummerieren.</transl
|
||||
<message>
|
||||
<location filename="../sources/ui/conductorpropertieswidget.ui" line="92"/>
|
||||
<source>Auto Numbering</source>
|
||||
<translation>Automatische Nummerierung:</translation>
|
||||
<translation>Autom. Nummerierung:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../sources/ui/conductorpropertieswidget.ui" line="230"/>
|
||||
@@ -3468,7 +3468,7 @@ Der angezeigte Name des Elements lässt sich separat in den Eigenschaften des El
|
||||
<message>
|
||||
<location filename="../sources/ui/elementinfowidget.cpp" line="321"/>
|
||||
<source>Exclude from the bill of materials</source>
|
||||
<translation>Aus der Materialliste ausschließen</translation>
|
||||
<translation>Aus Materialliste ausschließen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../sources/ui/elementinfowidget.cpp" line="486"/>
|
||||
@@ -3504,7 +3504,7 @@ Soll sie erstellt werden?</translation>
|
||||
<message>
|
||||
<location filename="../sources/ui/elementinfowidget.cpp" line="777"/>
|
||||
<source>Automatic numbering</source>
|
||||
<translation>Automatische Nummerierung</translation>
|
||||
<translation>Autom. Nummerierung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../sources/ui/elementinfowidget.cpp" line="784"/>
|
||||
@@ -3565,7 +3565,7 @@ Soll sie erstellt werden?</translation>
|
||||
<message>
|
||||
<location filename="../sources/ui/elementinfowidget.cpp" line="982"/>
|
||||
<source>None (name entered by hand)</source>
|
||||
<translation>Keine (von Hand eingegebener Name)</translation>
|
||||
<translation>Keine (von Hand vergeben)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../sources/ui/elementinfowidget.cpp" line="1001"/>
|
||||
@@ -3702,7 +3702,7 @@ Fortfahren?</translation>
|
||||
<message>
|
||||
<location filename="../sources/editor/ui/elementpropertieseditorwidget.ui" line="63"/>
|
||||
<source>Exclude from the bill of materials</source>
|
||||
<translation>Aus der Materialliste ausschließen</translation>
|
||||
<translation>Aus Materialliste ausschließen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../sources/editor/ui/elementpropertieseditorwidget.ui" line="70"/>
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include "terminalstriplayoutpattern.h"
|
||||
#include "../../../utils/qetutils.h"
|
||||
|
||||
#include <QDomDocument>
|
||||
|
||||
TerminalStripLayoutPattern::TerminalStripLayoutPattern()
|
||||
{
|
||||
m_font.setPixelSize(15);
|
||||
@@ -116,3 +118,176 @@ void TerminalStripLayoutPattern::updateTerminalsTextOption()
|
||||
m_xref_text_option.setAlignment(m_xref_text_alignment);
|
||||
m_xref_text_option.setWrapMode(QTextOption::WordWrap);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void rectToXml(QDomElement &element, const QString &name, const QRectF &rect)
|
||||
{
|
||||
element.setAttribute(name + QStringLiteral("_x"), rect.x());
|
||||
element.setAttribute(name + QStringLiteral("_y"), rect.y());
|
||||
element.setAttribute(name + QStringLiteral("_w"), rect.width());
|
||||
element.setAttribute(name + QStringLiteral("_h"), rect.height());
|
||||
}
|
||||
|
||||
QRectF rectFromXml(const QDomElement &element, const QString &name, const QRectF &default_rect)
|
||||
{
|
||||
auto value = [&element, &name](const QString &suffix, qreal default_value)
|
||||
{
|
||||
bool ok = false;
|
||||
const auto v = element.attribute(name + suffix).toDouble(&ok);
|
||||
return ok ? v : default_value;
|
||||
};
|
||||
|
||||
return QRectF(value(QStringLiteral("_x"), default_rect.x()),
|
||||
value(QStringLiteral("_y"), default_rect.y()),
|
||||
value(QStringLiteral("_w"), default_rect.width()),
|
||||
value(QStringLiteral("_h"), default_rect.height()));
|
||||
}
|
||||
|
||||
qreal realFromXml(const QDomElement &element, const QString &name, qreal default_value)
|
||||
{
|
||||
bool ok = false;
|
||||
const auto v = element.attribute(name).toDouble(&ok);
|
||||
return ok ? v : default_value;
|
||||
}
|
||||
|
||||
QString orientationToString(Qt::Orientation orientation) {
|
||||
return orientation == Qt::Horizontal ? QStringLiteral("horizontal") : QStringLiteral("vertical");
|
||||
}
|
||||
|
||||
Qt::Orientation orientationFromXml(const QDomElement &element, const QString &name, Qt::Orientation default_value)
|
||||
{
|
||||
const auto v = element.attribute(name);
|
||||
if (v == QLatin1String("horizontal")) return Qt::Horizontal;
|
||||
if (v == QLatin1String("vertical")) return Qt::Vertical;
|
||||
return default_value;
|
||||
}
|
||||
|
||||
Qt::Alignment alignmentFromXml(const QDomElement &element, const QString &name, Qt::Alignment default_value)
|
||||
{
|
||||
bool ok = false;
|
||||
const auto v = element.attribute(name).toInt(&ok);
|
||||
return ok && v ? Qt::Alignment(static_cast<Qt::AlignmentFlag>(v)) : default_value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TerminalStripLayoutPattern::xmlTagName
|
||||
* @return the tag name of the xml element used by toXml / fromXml
|
||||
*/
|
||||
QString TerminalStripLayoutPattern::xmlTagName() {
|
||||
return QStringLiteral("terminal_strip_layout");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TerminalStripLayoutPattern::toXml
|
||||
* Save all the values of this layout, including the font.
|
||||
* The font is saved by hand (family, pixel size, bold, italic) and not with
|
||||
* QFont::toString() because the format of this string is different between Qt5 and Qt6.
|
||||
* @param document
|
||||
* @return the xml element
|
||||
*/
|
||||
QDomElement TerminalStripLayoutPattern::toXml(QDomDocument &document) const
|
||||
{
|
||||
auto element = document.createElement(xmlTagName());
|
||||
|
||||
rectToXml(element, QStringLiteral("header"), m_header_rect);
|
||||
element.setAttribute(QStringLiteral("header_text_orientation"), orientationToString(m_header_text_orientation));
|
||||
element.setAttribute(QStringLiteral("header_text_alignment"), static_cast<int>(m_header_text_alignment));
|
||||
|
||||
rectToXml(element, QStringLiteral("spacer"), m_spacer_rect);
|
||||
|
||||
element.setAttribute(QStringLiteral("font_family"), m_font.family());
|
||||
element.setAttribute(QStringLiteral("font_pixel_size"), m_font.pixelSize());
|
||||
element.setAttribute(QStringLiteral("font_bold"), m_font.bold() ? 1 : 0);
|
||||
element.setAttribute(QStringLiteral("font_italic"), m_font.italic() ? 1 : 0);
|
||||
|
||||
for (auto i = 0; i < m_terminal_rect.size(); ++i) {
|
||||
rectToXml(element, QStringLiteral("terminal_%1").arg(i), m_terminal_rect.at(i));
|
||||
}
|
||||
|
||||
element.setAttribute(QStringLiteral("terminals_text_height"), m_terminals_text_height);
|
||||
element.setAttribute(QStringLiteral("terminals_text_y"), m_terminals_text_y);
|
||||
element.setAttribute(QStringLiteral("terminals_text_orientation"), orientationToString(m_terminals_text_orientation));
|
||||
element.setAttribute(QStringLiteral("terminals_text_alignment"), static_cast<int>(m_terminals_text_alignment));
|
||||
|
||||
element.setAttribute(QStringLiteral("xref_text_height"), m_xref_text_height);
|
||||
element.setAttribute(QStringLiteral("xref_text_y"), m_xref_text_y);
|
||||
element.setAttribute(QStringLiteral("xref_text_orientation"), orientationToString(m_xref_text_orientation));
|
||||
element.setAttribute(QStringLiteral("xref_text_alignment"), static_cast<int>(m_xref_text_alignment));
|
||||
|
||||
element.setAttribute(QStringLiteral("type_symbol_y"), m_type_symbol_y);
|
||||
element.setAttribute(QStringLiteral("type_symbol_height"), m_type_symbol_height);
|
||||
element.setAttribute(QStringLiteral("connection_length"), m_connection_length);
|
||||
element.setAttribute(QStringLiteral("cable_wire_length"), m_cable_wire_length);
|
||||
element.setAttribute(QStringLiteral("cable_length"), m_cable_length);
|
||||
element.setAttribute(QStringLiteral("cable_end_length"), m_cable_end_length);
|
||||
|
||||
element.setAttribute(QStringLiteral("bridge_point_d"), m_bridge_point_d);
|
||||
for (auto i = 0; i < m_bridge_point_y_offset.size(); ++i) {
|
||||
element.setAttribute(QStringLiteral("bridge_point_y_offset_%1").arg(i), m_bridge_point_y_offset.at(i));
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TerminalStripLayoutPattern::fromXml
|
||||
* Load the values saved by toXml. A value missing in @a layout_element
|
||||
* (project saved with an older version) keep the current value of this layout.
|
||||
* @param layout_element
|
||||
*/
|
||||
void TerminalStripLayoutPattern::fromXml(const QDomElement &layout_element)
|
||||
{
|
||||
if (layout_element.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_header_rect = rectFromXml(layout_element, QStringLiteral("header"), m_header_rect);
|
||||
m_header_text_orientation = orientationFromXml(layout_element, QStringLiteral("header_text_orientation"), m_header_text_orientation);
|
||||
setHeaderTextAlignment(alignmentFromXml(layout_element, QStringLiteral("header_text_alignment"), m_header_text_alignment));
|
||||
|
||||
m_spacer_rect = rectFromXml(layout_element, QStringLiteral("spacer"), m_spacer_rect);
|
||||
|
||||
auto font_ = m_font;
|
||||
if (layout_element.hasAttribute(QStringLiteral("font_family"))) {
|
||||
font_.setFamily(layout_element.attribute(QStringLiteral("font_family")));
|
||||
}
|
||||
const auto pixel_size = layout_element.attribute(QStringLiteral("font_pixel_size")).toInt();
|
||||
if (pixel_size > 0) {
|
||||
font_.setPixelSize(pixel_size);
|
||||
}
|
||||
if (layout_element.hasAttribute(QStringLiteral("font_bold"))) {
|
||||
font_.setBold(layout_element.attribute(QStringLiteral("font_bold")).toInt() != 0);
|
||||
}
|
||||
if (layout_element.hasAttribute(QStringLiteral("font_italic"))) {
|
||||
font_.setItalic(layout_element.attribute(QStringLiteral("font_italic")).toInt() != 0);
|
||||
}
|
||||
setFont(font_);
|
||||
|
||||
for (auto i = 0; i < m_terminal_rect.size(); ++i) {
|
||||
m_terminal_rect[i] = rectFromXml(layout_element, QStringLiteral("terminal_%1").arg(i), m_terminal_rect.at(i));
|
||||
}
|
||||
|
||||
m_terminals_text_height = realFromXml(layout_element, QStringLiteral("terminals_text_height"), m_terminals_text_height);
|
||||
m_terminals_text_y = realFromXml(layout_element, QStringLiteral("terminals_text_y"), m_terminals_text_y);
|
||||
m_terminals_text_orientation = orientationFromXml(layout_element, QStringLiteral("terminals_text_orientation"), m_terminals_text_orientation);
|
||||
setTerminalsTextAlignment(alignmentFromXml(layout_element, QStringLiteral("terminals_text_alignment"), m_terminals_text_alignment));
|
||||
|
||||
m_xref_text_height = realFromXml(layout_element, QStringLiteral("xref_text_height"), m_xref_text_height);
|
||||
m_xref_text_y = realFromXml(layout_element, QStringLiteral("xref_text_y"), m_xref_text_y);
|
||||
m_xref_text_orientation = orientationFromXml(layout_element, QStringLiteral("xref_text_orientation"), m_xref_text_orientation);
|
||||
setXrefTextAlignment(alignmentFromXml(layout_element, QStringLiteral("xref_text_alignment"), m_xref_text_alignment));
|
||||
|
||||
m_type_symbol_y = realFromXml(layout_element, QStringLiteral("type_symbol_y"), m_type_symbol_y);
|
||||
m_type_symbol_height = realFromXml(layout_element, QStringLiteral("type_symbol_height"), m_type_symbol_height);
|
||||
m_connection_length = realFromXml(layout_element, QStringLiteral("connection_length"), m_connection_length);
|
||||
m_cable_wire_length = realFromXml(layout_element, QStringLiteral("cable_wire_length"), m_cable_wire_length);
|
||||
m_cable_length = realFromXml(layout_element, QStringLiteral("cable_length"), m_cable_length);
|
||||
m_cable_end_length = realFromXml(layout_element, QStringLiteral("cable_end_length"), m_cable_end_length);
|
||||
|
||||
m_bridge_point_d = realFromXml(layout_element, QStringLiteral("bridge_point_d"), m_bridge_point_d);
|
||||
for (auto i = 0; i < m_bridge_point_y_offset.size(); ++i) {
|
||||
m_bridge_point_y_offset[i] = realFromXml(layout_element, QStringLiteral("bridge_point_y_offset_%1").arg(i), m_bridge_point_y_offset.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#ifndef TERMINALSTRIPLAYOUTPATTERN_H
|
||||
#define TERMINALSTRIPLAYOUTPATTERN_H
|
||||
|
||||
#include <QDomElement>
|
||||
#include <QFont>
|
||||
#include <QRect>
|
||||
#include <QSize>
|
||||
@@ -103,6 +104,11 @@ class TerminalStripLayoutPattern
|
||||
QUuid m_uuid{QUuid::createUuid()};
|
||||
QString m_name;
|
||||
|
||||
//Save / load of the layout in the project xml
|
||||
QDomElement toXml(QDomDocument &document) const;
|
||||
void fromXml(const QDomElement &layout_element);
|
||||
static QString xmlTagName();
|
||||
|
||||
private:
|
||||
void updateHeaderTextOption();
|
||||
void updateTerminalsTextOption();
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#include "terminalstriplayoutshandler.h"
|
||||
|
||||
#include <QDomDocument>
|
||||
#include <QObject>
|
||||
|
||||
TerminalStripLayoutsHandler::TerminalStripLayoutsHandler()
|
||||
@@ -30,3 +31,27 @@ QSharedPointer<TerminalStripLayoutPattern> TerminalStripLayoutsHandler::defaultL
|
||||
{
|
||||
return m_default_layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TerminalStripLayoutsHandler::toXml
|
||||
* Append the default layout (font included) as a child of @a parent_element
|
||||
* @param parent_element
|
||||
*/
|
||||
void TerminalStripLayoutsHandler::toXml(QDomElement &parent_element) const
|
||||
{
|
||||
auto document = parent_element.ownerDocument();
|
||||
parent_element.appendChild(m_default_layout->toXml(document));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TerminalStripLayoutsHandler::fromXml
|
||||
* Load the default layout from the child of @a parent_element, if any.
|
||||
* The existing layout object is modified in place because the terminal
|
||||
* strip items share it.
|
||||
* @param parent_element
|
||||
*/
|
||||
void TerminalStripLayoutsHandler::fromXml(const QDomElement &parent_element)
|
||||
{
|
||||
m_default_layout->fromXml(
|
||||
parent_element.firstChildElement(TerminalStripLayoutPattern::xmlTagName()));
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#ifndef TERMINALSTRIPLAYOUTSHANDLER_H
|
||||
#define TERMINALSTRIPLAYOUTSHANDLER_H
|
||||
|
||||
#include <QDomElement>
|
||||
#include <QSet>
|
||||
#include <QSharedPointer>
|
||||
|
||||
@@ -33,6 +34,9 @@ class TerminalStripLayoutsHandler
|
||||
TerminalStripLayoutsHandler();
|
||||
QSharedPointer<TerminalStripLayoutPattern> defaultLayout();
|
||||
|
||||
void toXml(QDomElement &parent_element) const;
|
||||
void fromXml(const QDomElement &parent_element);
|
||||
|
||||
private:
|
||||
QSet<QSharedPointer<TerminalStripLayoutPattern>> m_layout_set;
|
||||
QSharedPointer<TerminalStripLayoutPattern> m_default_layout;
|
||||
|
||||
@@ -90,6 +90,31 @@ void drawGroundSymbol(QPainter *painter)
|
||||
painter->drawLine(QPointF{-1, 4}, QPointF{1, 4});
|
||||
}
|
||||
|
||||
//Two diodes side by side, wired in anti-parallel between the top and
|
||||
//the bottom of the terminal (current flows through the left one from
|
||||
//the top to the bottom and through the right one the other way)
|
||||
void drawAntiParallelDiodesSymbol(QPainter *painter)
|
||||
{
|
||||
painter->drawLine(QPointF{0, -7}, QPointF{0, -5});
|
||||
painter->drawLine(QPointF{-5, -5}, QPointF{5, -5});
|
||||
painter->drawLine(QPointF{0, 5}, QPointF{0, 7});
|
||||
painter->drawLine(QPointF{-5, 5}, QPointF{5, 5});
|
||||
|
||||
//Left diode, anode on top
|
||||
const QPointF left_diode[3] { {-8, -3}, {-2, -3}, {-5, 2} };
|
||||
painter->drawLine(QPointF{-5, -5}, QPointF{-5, -3});
|
||||
painter->drawPolygon(left_diode, 3);
|
||||
painter->drawLine(QPointF{-8, 2}, QPointF{-2, 2});
|
||||
painter->drawLine(QPointF{-5, 2}, QPointF{-5, 5});
|
||||
|
||||
//Right diode, anode at the bottom
|
||||
const QPointF right_diode[3] { {2, 2}, {8, 2}, {5, -3} };
|
||||
painter->drawLine(QPointF{5, -5}, QPointF{5, -3});
|
||||
painter->drawPolygon(right_diode, 3);
|
||||
painter->drawLine(QPointF{2, -3}, QPointF{8, -3});
|
||||
painter->drawLine(QPointF{5, 2}, QPointF{5, 5});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief drawScaled
|
||||
* Call @a draw with the painter moved by @a dx and scaled by @a factor.
|
||||
@@ -147,6 +172,7 @@ void drawTypeSymbol(QPainter *painter, const QRectF &box, ElementData::TerminalT
|
||||
case ElementData::TTSectional : drawSectionalSymbol(p); break;
|
||||
case ElementData::TTDiode : drawDiodeSymbol(p); break;
|
||||
case ElementData::TTGround : drawGroundSymbol(p); break;
|
||||
case ElementData::TTAntiParallelDiodes : drawAntiParallelDiodesSymbol(p); break;
|
||||
default: break;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -191,6 +191,8 @@ void FreeTerminalEditor::on_m_type_cb_activated(int index)
|
||||
override_type = ElementData::TTDiode; break;
|
||||
case 4:
|
||||
override_type = ElementData::TTGround; break;
|
||||
case 5:
|
||||
override_type = ElementData::TTAntiParallelDiodes; break;
|
||||
default:
|
||||
override_type = ElementData::TTGeneric; break;
|
||||
}
|
||||
|
||||
@@ -122,6 +122,11 @@
|
||||
<string>Ground</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Anti-parallel diodes</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2" colspan="2">
|
||||
|
||||
@@ -429,6 +429,7 @@ QWidget *FreeTerminalModelDelegate::createEditor(QWidget *parent, const QStyleOp
|
||||
qcb->addItem(ElementData::translatedTerminalType(ElementData::TTSectional), ElementData::TTSectional);
|
||||
qcb->addItem(ElementData::translatedTerminalType(ElementData::TTDiode), ElementData::TTDiode);
|
||||
qcb->addItem(ElementData::translatedTerminalType(ElementData::TTGround), ElementData::TTGround);
|
||||
qcb->addItem(ElementData::translatedTerminalType(ElementData::TTAntiParallelDiodes), ElementData::TTAntiParallelDiodes);
|
||||
|
||||
return qcb;
|
||||
}
|
||||
|
||||
@@ -569,6 +569,8 @@ void TerminalStripEditor::on_m_type_cb_activated(int index)
|
||||
override_type = ElementData::TTDiode; break;
|
||||
case 4:
|
||||
override_type = ElementData::TTGround; break;
|
||||
case 5:
|
||||
override_type = ElementData::TTAntiParallelDiodes; break;
|
||||
default:
|
||||
override_type = ElementData::TTGeneric; break;
|
||||
}
|
||||
|
||||
@@ -127,6 +127,11 @@
|
||||
<string>Ground</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Anti-parallel diodes</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
|
||||
@@ -833,6 +833,7 @@ QWidget *TerminalStripModelDelegate::createEditor(QWidget *parent, const QStyleO
|
||||
qcb->addItem(ElementData::translatedTerminalType(ElementData::TTSectional), ElementData::TTSectional);
|
||||
qcb->addItem(ElementData::translatedTerminalType(ElementData::TTDiode), ElementData::TTDiode);
|
||||
qcb->addItem(ElementData::translatedTerminalType(ElementData::TTGround), ElementData::TTGround);
|
||||
qcb->addItem(ElementData::translatedTerminalType(ElementData::TTAntiParallelDiodes), ElementData::TTAntiParallelDiodes);
|
||||
|
||||
return qcb;
|
||||
}
|
||||
|
||||
@@ -75,18 +75,7 @@ projectDataBase::projectDataBase(QETProject *project, QObject *parent) :
|
||||
connect(m_project, &QETProject::projectDiagramsOrderChanged, [this]()
|
||||
{
|
||||
m_content_changed = true;
|
||||
for (auto diagram : m_project->diagrams())
|
||||
{
|
||||
m_diagram_order_changed.bindValue(":pos", m_project->folioIndex(diagram)+1);
|
||||
m_diagram_order_changed.bindValue(":uuid", diagram->uuid());
|
||||
m_diagram_order_changed.exec();
|
||||
|
||||
|
||||
m_diagram_info_order_changed.bindValue(":folio", diagram->border_and_titleblock.titleblockInformation().value("folio"));
|
||||
m_diagram_info_order_changed.bindValue(":uuid", diagram->uuid());
|
||||
m_diagram_info_order_changed.exec();
|
||||
|
||||
}
|
||||
updateFolioPositions();
|
||||
emit dataBaseUpdated();
|
||||
});
|
||||
}
|
||||
@@ -122,6 +111,7 @@ void projectDataBase::updateDB()
|
||||
{
|
||||
flushDrawingItems();
|
||||
flushLinks();
|
||||
flushElementPositions();
|
||||
emit dataBaseUpdated();
|
||||
return;
|
||||
}
|
||||
@@ -649,6 +639,7 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString
|
||||
query.exec(QStringLiteral("DELETE FROM ") + table);
|
||||
}
|
||||
m_dirty_link_elements.clear();
|
||||
m_moved_elements.clear();
|
||||
|
||||
for (int i = 0 ; i < diagram_uuids.size() ; ++i)
|
||||
{
|
||||
@@ -824,9 +815,11 @@ QSqlQuery projectDataBase::newQuery(const QString &query, QString *error) {
|
||||
//Drawing-item rows are rewritten lazily, see drawingItemChanged().
|
||||
//Every read from outside comes through here, so this is the one
|
||||
//place the queue has to be emptied for a reader to see current rows.
|
||||
//The same goes for link rows, see linksChanged().
|
||||
//The same goes for link rows and elements' folio cells, see
|
||||
//linksChanged() and elementMoved().
|
||||
flushDrawingItems();
|
||||
flushLinks();
|
||||
flushElementPositions();
|
||||
|
||||
// First gate: which kind of statement is acceptable here at all. A
|
||||
// textual check is the right tool for that and the wrong tool for
|
||||
@@ -920,6 +913,8 @@ void projectDataBase::addElement(Element *element)
|
||||
}
|
||||
connect(element, &Element::linkedElementChanged,
|
||||
this, &projectDataBase::linksChanged, Qt::UniqueConnection);
|
||||
connect(element, &QGraphicsObject::xChanged, this, &projectDataBase::elementMoved, Qt::UniqueConnection);
|
||||
connect(element, &QGraphicsObject::yChanged, this, &projectDataBase::elementMoved, Qt::UniqueConnection);
|
||||
|
||||
bindElementInfoValues(m_insert_element_info_query, element);
|
||||
if (!m_insert_element_info_query.exec()) {
|
||||
@@ -1022,18 +1017,78 @@ void projectDataBase::addDiagram(Diagram *diagram)
|
||||
addDrawingItem(item);
|
||||
}
|
||||
|
||||
//The information "folio" of other existing diagram can have the variable %total,
|
||||
//so when a new diagram is added this variable change.
|
||||
//We need to update this information in the database.
|
||||
for (auto diagram : project()->diagrams())
|
||||
//The folios after the new one moved down, and a folio number made
|
||||
//from %id or %total changed on every folio.
|
||||
updateFolioPositions();
|
||||
emit dataBaseUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::elementMoved
|
||||
The sender() element moved: its folio cell is written again by the
|
||||
next flushElementPositions().
|
||||
*/
|
||||
void projectDataBase::elementMoved()
|
||||
{
|
||||
auto *element = qobject_cast<Element *>(sender());
|
||||
if (element && !m_moved_elements.contains(element)) {
|
||||
m_moved_elements << element;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::flushElementPositions
|
||||
Write the folio cell of every element queued by elementMoved()
|
||||
*/
|
||||
void projectDataBase::flushElementPositions()
|
||||
{
|
||||
if (m_moved_elements.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto moved = m_moved_elements;
|
||||
m_moved_elements.clear();
|
||||
const bool own_transaction = m_data_base.transaction();
|
||||
QSqlQuery update(m_data_base);
|
||||
update.prepare(QStringLiteral("UPDATE element SET pos = :pos WHERE uuid = :uuid"));
|
||||
for (const QPointer<Element> &element : moved)
|
||||
{
|
||||
m_diagram_info_order_changed.bindValue(":folio", diagram->border_and_titleblock.titleblockInformation().value("folio"));
|
||||
m_diagram_info_order_changed.bindValue(":uuid", diagram->uuid());
|
||||
if (!m_diagram_info_order_changed.exec()) {
|
||||
qDebug() << "projectDataBase::addDiagram update diagram infp order error : " << m_diagram_info_order_changed.lastError();
|
||||
if (!element || !element->diagram()) {
|
||||
continue;
|
||||
}
|
||||
update.bindValue(QStringLiteral(":pos"),
|
||||
element->diagram()->convertPosition(element->scenePos()).toString());
|
||||
update.bindValue(QStringLiteral(":uuid"), element->uuid().toString());
|
||||
if (!update.exec()) {
|
||||
qDebug() << "projectDataBase::flushElementPositions error : " << update.lastError();
|
||||
}
|
||||
}
|
||||
if (own_transaction) {
|
||||
m_data_base.commit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::updateFolioPositions
|
||||
Write every folio's position and folio number again, after a folio was
|
||||
added, removed or moved.
|
||||
*/
|
||||
void projectDataBase::updateFolioPositions()
|
||||
{
|
||||
for (auto diagram : m_project->diagrams())
|
||||
{
|
||||
m_diagram_order_changed.bindValue(":pos", m_project->folioIndex(diagram)+1);
|
||||
m_diagram_order_changed.bindValue(":uuid", diagram->uuid().toString());
|
||||
if (!m_diagram_order_changed.exec()) {
|
||||
qDebug() << "projectDataBase::updateFolioPositions position error : " << m_diagram_order_changed.lastError();
|
||||
}
|
||||
|
||||
m_diagram_info_order_changed.bindValue(":folio", diagram->border_and_titleblock.titleblockInformation().value("folio"));
|
||||
m_diagram_info_order_changed.bindValue(":uuid", diagram->uuid().toString());
|
||||
if (!m_diagram_info_order_changed.exec()) {
|
||||
qDebug() << "projectDataBase::updateFolioPositions folio error : " << m_diagram_info_order_changed.lastError();
|
||||
}
|
||||
}
|
||||
emit dataBaseUpdated();
|
||||
}
|
||||
|
||||
void projectDataBase::removeDiagram(Diagram *diagram)
|
||||
@@ -1118,6 +1173,15 @@ void projectDataBase::removeDiagram(Diagram *diagram)
|
||||
}
|
||||
}
|
||||
|
||||
QSqlQuery remove_info(m_data_base);
|
||||
remove_info.prepare(QStringLiteral("DELETE FROM diagram_info WHERE diagram_uuid = :uuid"));
|
||||
remove_info.bindValue(QStringLiteral(":uuid"), uuid_str);
|
||||
if (!remove_info.exec()) {
|
||||
qDebug() << "projectDataBase::removeDiagram diagram_info delete error : " << remove_info.lastError();
|
||||
m_data_base.rollback();
|
||||
return;
|
||||
}
|
||||
|
||||
m_remove_diagram_query.bindValue(":uuid", uuid_str);
|
||||
if (!m_remove_diagram_query.exec()) {
|
||||
qDebug() << "projectDataBase::removeDiagram delete error : " << m_remove_diagram_query.lastError();
|
||||
@@ -1126,6 +1190,8 @@ void projectDataBase::removeDiagram(Diagram *diagram)
|
||||
}
|
||||
|
||||
m_data_base.commit();
|
||||
//The folios after it moved up, and %id / %total changed.
|
||||
updateFolioPositions();
|
||||
emit dataBaseUpdated();
|
||||
}
|
||||
|
||||
@@ -1187,12 +1253,39 @@ void projectDataBase::addConductor(Conductor *conductor)
|
||||
void projectDataBase::removeConductor(Conductor *conductor)
|
||||
{
|
||||
m_content_changed = true;
|
||||
//The terminal table lists the terminals a conductor ends on: the
|
||||
//conductor's two ends go with it unless another conductor ends there.
|
||||
QList<QPair<QString, QString>> ends;
|
||||
QSqlQuery read_ends(m_data_base);
|
||||
read_ends.prepare(QStringLiteral("SELECT terminal1_uuid, terminal1_element_uuid, "
|
||||
"terminal2_uuid, terminal2_element_uuid "
|
||||
"FROM conductor WHERE uuid = :uuid"));
|
||||
read_ends.bindValue(QStringLiteral(":uuid"), conductor->uuid().toString());
|
||||
if (read_ends.exec() && read_ends.next()) {
|
||||
ends << qMakePair(read_ends.value(0).toString(), read_ends.value(1).toString())
|
||||
<< qMakePair(read_ends.value(2).toString(), read_ends.value(3).toString());
|
||||
}
|
||||
|
||||
m_remove_conductor_query.bindValue(":uuid", conductor->uuid().toString());
|
||||
if (!m_remove_conductor_query.exec()) {
|
||||
qDebug() << "projectDataBase::removeConductor delete error : " << m_remove_conductor_query.lastError();
|
||||
} else {
|
||||
emit dataBaseUpdated();
|
||||
return;
|
||||
}
|
||||
|
||||
QSqlQuery remove_end(m_data_base);
|
||||
remove_end.prepare(QStringLiteral(
|
||||
"DELETE FROM terminal WHERE uuid = :uuid AND element_uuid = :element_uuid "
|
||||
"AND NOT EXISTS (SELECT 1 FROM conductor WHERE "
|
||||
"(terminal1_uuid = :uuid AND terminal1_element_uuid = :element_uuid) OR "
|
||||
"(terminal2_uuid = :uuid AND terminal2_element_uuid = :element_uuid))"));
|
||||
for (const auto &end : std::as_const(ends)) {
|
||||
remove_end.bindValue(QStringLiteral(":uuid"), end.first);
|
||||
remove_end.bindValue(QStringLiteral(":element_uuid"), end.second);
|
||||
if (!remove_end.exec()) {
|
||||
qDebug() << "projectDataBase::removeConductor terminal delete error : " << remove_end.lastError();
|
||||
}
|
||||
}
|
||||
emit dataBaseUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2061,6 +2154,7 @@ static ElementData::Types allElementTypes()
|
||||
*/
|
||||
void projectDataBase::populateElementTable()
|
||||
{
|
||||
m_moved_elements.clear();
|
||||
QSqlQuery query_(m_data_base);
|
||||
query_.exec("DELETE FROM element");
|
||||
|
||||
@@ -2597,6 +2691,7 @@ void projectDataBase::exportDb(projectDataBase *db,
|
||||
const auto escaped_path = path_.replace("'", "''");
|
||||
db->flushDrawingItems();
|
||||
db->flushLinks();
|
||||
db->flushElementPositions();
|
||||
QSqlQuery query(db->m_data_base);
|
||||
if (!query.exec("VACUUM INTO '" % escaped_path % "'")) {
|
||||
qWarning() << "Unable to export project database:" << query.lastError().text();
|
||||
|
||||
@@ -126,6 +126,8 @@ class projectDataBase : public QObject
|
||||
void drawingItemDestroyed(QObject *object);
|
||||
//Queue the sender()'s link rows for rewriting.
|
||||
void linksChanged();
|
||||
//Queue the sender() element's folio cell for rewriting.
|
||||
void elementMoved();
|
||||
|
||||
public:
|
||||
|
||||
@@ -139,6 +141,8 @@ class projectDataBase : public QObject
|
||||
void createWiringListView();
|
||||
void createDrawingItemView();
|
||||
void populateDiagramTable();
|
||||
void updateFolioPositions();
|
||||
void flushElementPositions();
|
||||
void populateElementTable();
|
||||
void populateElementInfoTable();
|
||||
void populateDiagramInfoTable();
|
||||
@@ -214,6 +218,10 @@ class projectDataBase : public QObject
|
||||
//written: a link is made first and its contact group set after,
|
||||
//so the rows are written when next read, as drawing items are.
|
||||
QList<QPointer<Element>> m_dirty_link_elements;
|
||||
//Elements moved since their folio cell was last written: a move
|
||||
//sends xChanged and yChanged for every step of a drag, so the
|
||||
//cell is written when next read, as drawing items are.
|
||||
QList<QPointer<Element>> m_moved_elements;
|
||||
|
||||
#ifdef QET_EXPORT_PROJECT_DB
|
||||
public:
|
||||
|
||||
@@ -225,6 +225,7 @@ void ElementPropertiesEditorWidget::setUpInterface()
|
||||
ui->m_terminal_type_cb->addItem(tr("Sectionable"), ElementData::TTSectional);
|
||||
ui->m_terminal_type_cb->addItem(tr("Diode"), ElementData::TTDiode);
|
||||
ui->m_terminal_type_cb->addItem(tr("Ground"), ElementData::TTGround);
|
||||
ui->m_terminal_type_cb->addItem(tr("Anti-parallel diodes"), ElementData::TTAntiParallelDiodes);
|
||||
|
||||
ui->m_terminal_func_cb->addItem(tr("Generic"), ElementData::TFGeneric);
|
||||
ui->m_terminal_func_cb->addItem(tr("Phase"), ElementData::TFPhase);
|
||||
|
||||
@@ -35,11 +35,6 @@ namespace
|
||||
const QString suffix = QFileInfo(url.toLocalFile()).suffix().toLower();
|
||||
return ImageDrop::supportedSuffixes().contains(suffix);
|
||||
}
|
||||
|
||||
QString tr(const char *text)
|
||||
{
|
||||
return QCoreApplication::translate("ImageDrop", text);
|
||||
}
|
||||
}
|
||||
|
||||
QStringList ImageDrop::supportedSuffixes()
|
||||
@@ -74,18 +69,18 @@ QImage ImageDrop::load(const QString &path, QString *error)
|
||||
|
||||
const QFileInfo info(path);
|
||||
if (!info.isFile())
|
||||
return fail(tr("not a file"));
|
||||
return fail(QCoreApplication::translate("ImageDrop", "not a file"));
|
||||
if (info.size() > maxFileBytes)
|
||||
return fail(tr("the file is larger than 10 MB"));
|
||||
return fail(QCoreApplication::translate("ImageDrop", "the file is larger than 10 MB"));
|
||||
|
||||
QImageReader reader(path);
|
||||
const QSize size = reader.size();
|
||||
if (size.isValid() && qint64(size.width()) * size.height() > maxPixels)
|
||||
return fail(tr("the image has too many pixels"));
|
||||
return fail(QCoreApplication::translate("ImageDrop", "the image has too many pixels"));
|
||||
|
||||
const QImage image = reader.read();
|
||||
if (image.isNull())
|
||||
return fail(tr("unable to read the image"));
|
||||
return fail(QCoreApplication::translate("ImageDrop", "unable to read the image"));
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
@@ -840,6 +840,8 @@ QString ElementData::terminalTypeToString(ElementData::TerminalType type)
|
||||
return QStringLiteral("diode");
|
||||
case ElementData::TTGround:
|
||||
return QStringLiteral("ground");
|
||||
case ElementData::TTAntiParallelDiodes:
|
||||
return QStringLiteral("antiparallel_diodes");
|
||||
}
|
||||
return QStringLiteral("generic");
|
||||
}
|
||||
@@ -856,6 +858,8 @@ ElementData::TerminalType ElementData::terminalTypeFromString(const QString &str
|
||||
return ElementData::TTDiode;
|
||||
} else if (string == QLatin1String("ground")) {
|
||||
return ElementData::TTGround;
|
||||
} else if (string == QLatin1String("antiparallel_diodes")) {
|
||||
return ElementData::TTAntiParallelDiodes;
|
||||
}
|
||||
|
||||
qDebug() << "ElementData::terminalTypeFromString : string : "
|
||||
@@ -877,6 +881,8 @@ QString ElementData::translatedTerminalType(ElementData::TerminalType type)
|
||||
return QObject::tr("Diode", "diode terminal element type");
|
||||
case ElementData::TTGround:
|
||||
return QObject::tr("Ground", "ground terminal element type");
|
||||
case ElementData::TTAntiParallelDiodes:
|
||||
return QObject::tr("Anti-parallel diodes", "anti-parallel diodes terminal element type");
|
||||
}
|
||||
return QObject::tr("Generic", "generic terminal element type");
|
||||
}
|
||||
|
||||
@@ -175,7 +175,8 @@ class ElementData : public PropertiesInterface
|
||||
TTFuse,
|
||||
TTSectional,
|
||||
TTDiode,
|
||||
TTGround
|
||||
TTGround,
|
||||
TTAntiParallelDiodes
|
||||
};
|
||||
Q_ENUM(TerminalType)
|
||||
|
||||
|
||||
@@ -448,7 +448,7 @@ void QetGraphicsTableItem::setPreviousTable(QetGraphicsTableItem *table)
|
||||
m_previous_table->setNextTable(this);
|
||||
setModel(m_previous_table->m_model);
|
||||
}
|
||||
else //Copie the model of old previous table
|
||||
else if (old_previous_table->model()) //Copie the model of old previous table
|
||||
{
|
||||
setModel(new ProjectDBModel(*static_cast<ProjectDBModel *>(old_previous_table->model())));
|
||||
}
|
||||
@@ -596,11 +596,23 @@ QDomElement QetGraphicsTableItem::toXml(QDomDocument &dom_document) const
|
||||
//Add the header xml
|
||||
dom_table.appendChild(m_header_item->toXml(dom_document));
|
||||
|
||||
//Add previous table, the model is save by the previous table
|
||||
if (m_previous_table)
|
||||
//Add previous table, the model is save by the previous table.
|
||||
//A folio deleted with undo still possible keeps its tables in the
|
||||
//chain, but it is not saved: name the nearest previous table that
|
||||
//is, or save the model if none is.
|
||||
const auto project_ = diagram() ? diagram()->project() : nullptr;
|
||||
auto in_project = [project_](const QetGraphicsTableItem *table) {
|
||||
return project_ && table->diagram()
|
||||
&& project_->diagrams().contains(table->diagram());
|
||||
};
|
||||
QetGraphicsTableItem *previous_ = m_previous_table;
|
||||
while (previous_ && !in_project(previous_)) {
|
||||
previous_ = previous_->previousTable();
|
||||
}
|
||||
if (previous_)
|
||||
{
|
||||
auto dom_previous_table = dom_document.createElement("previous_table");
|
||||
dom_previous_table.setAttribute("uuid", m_previous_table->m_uuid.toString());
|
||||
dom_previous_table.setAttribute("uuid", previous_->m_uuid.toString());
|
||||
dom_table.appendChild(dom_previous_table);
|
||||
}
|
||||
else if (m_model) //There is not a previous table, we need to save the model
|
||||
|
||||
@@ -1587,6 +1587,8 @@ QDomDocument QETProject::toXml()
|
||||
//Write terminal strip to xml
|
||||
if (m_terminal_strip_vector.count())
|
||||
{
|
||||
m_project_properties_handler.terminalStripLayoutHandler().toXml(project_root);
|
||||
|
||||
auto xml_strip = xml_doc.createElement(QStringLiteral("terminal_strips"));
|
||||
for (auto &strip : m_terminal_strip_vector) {
|
||||
xml_strip.appendChild(strip->toXml(xml_doc));
|
||||
@@ -2092,6 +2094,10 @@ void QETProject::readProjectXml(QDomDocument &xml_project)
|
||||
readElementsCollectionXml(xml_project);
|
||||
const qint64 elements_ms = phase_timer.restart();
|
||||
|
||||
//Load the layout of the terminal strip (font, size of cells...),
|
||||
//before the diagrams because the terminal strip items use it
|
||||
m_project_properties_handler.terminalStripLayoutHandler().fromXml(xml_project.documentElement());
|
||||
|
||||
//Load the diagrams
|
||||
readDiagramsXml(xml_project);
|
||||
const qint64 diagrams_ms = phase_timer.restart();
|
||||
|
||||
@@ -56,6 +56,12 @@ class tst_databasefromdocument : public QObject
|
||||
{
|
||||
const QString out = run({QStringLiteral("--run"), m_dir.filePath(QStringLiteral("dump.js")),
|
||||
project}, from_folios);
|
||||
return tablesIn(out, how);
|
||||
}
|
||||
|
||||
// The DUMP line of a --run output, each table as a sorted list of rows
|
||||
static QJsonObject tablesIn(const QString &out, QString *how)
|
||||
{
|
||||
QJsonObject tables;
|
||||
for (const QString &line : out.split(QLatin1Char('\n'))) {
|
||||
if (line.contains(QStringLiteral("Project database filled")))
|
||||
@@ -345,6 +351,105 @@ private slots:
|
||||
"a link is not one the folios would make as saved"));
|
||||
}
|
||||
|
||||
// After a folio is added and another removed, the folio tables hold
|
||||
// what a fresh open of the saved project gives: every folio's position
|
||||
// and folio number, and no row for the removed folio. Positions only
|
||||
// changed when folios were reordered, and the removed folio's
|
||||
// diagram_info row stayed.
|
||||
void folioAddedAndRemoved()
|
||||
{
|
||||
const QString saved = m_dir.filePath(QStringLiteral("folios.qet"));
|
||||
const QString edited = m_dir.filePath(QStringLiteral("folios-edited.qet"));
|
||||
run({QStringLiteral("--resave"), QStringLiteral(QET_EXAMPLES_DIR "/tremie_vibrante.qet"), saved});
|
||||
QVERIFY(QFile::exists(saved));
|
||||
QFile js(m_dir.filePath(QStringLiteral("folios.js")));
|
||||
QVERIFY(js.open(QIODevice::WriteOnly));
|
||||
js.write(QStringLiteral(
|
||||
"qet.log('ADDED ' + qet.insertFolio(0));\n"
|
||||
"qet.log('REMOVED ' + qet.removeFolio(2));\n"
|
||||
"var out = {};\n"
|
||||
"['diagram', 'diagram_info'].forEach(function (t) { out[t] = qet.query('SELECT * FROM ' + t); });\n"
|
||||
"qet.log('DUMP ' + JSON.stringify(out));\n"
|
||||
"qet.log('SAVED ' + qet.save('%1'));\n").arg(edited).toUtf8());
|
||||
js.close();
|
||||
const QString log = run({QStringLiteral("--run"), js.fileName(), saved});
|
||||
QVERIFY2(log.contains(QStringLiteral("ADDED 0")) && log.contains(QStringLiteral("REMOVED true"))
|
||||
&& log.contains(QStringLiteral("SAVED true")), qPrintable(log.right(400)));
|
||||
QString unused;
|
||||
const QJsonObject after_edits = tablesIn(log, &unused);
|
||||
const QJsonObject reopened = dump(edited, false, &unused);
|
||||
for (const QString &table : {QStringLiteral("diagram"), QStringLiteral("diagram_info")}) {
|
||||
const QJsonArray a = after_edits.value(table).toArray(), b = reopened.value(table).toArray();
|
||||
QVERIFY2(!a.isEmpty(), qPrintable(table));
|
||||
QVERIFY2(a == b, qPrintable(QStringLiteral("%1: %2 rows after the edits, %3 on reopening, or different")
|
||||
.arg(table).arg(a.size()).arg(b.size())));
|
||||
}
|
||||
}
|
||||
|
||||
// After an element is moved several columns, its folio cell in the
|
||||
// element table is the one a fresh open of the saved project gives.
|
||||
// It used to stay where the element was added.
|
||||
void movedElementCell()
|
||||
{
|
||||
const QString saved = m_dir.filePath(QStringLiteral("moved.qet"));
|
||||
const QString edited = m_dir.filePath(QStringLiteral("moved-edited.qet"));
|
||||
run({QStringLiteral("--resave"), QStringLiteral(QET_EXAMPLES_DIR "/tremie_vibrante.qet"), saved});
|
||||
QVERIFY(QFile::exists(saved));
|
||||
QFile js(m_dir.filePath(QStringLiteral("moved.js")));
|
||||
QVERIFY(js.open(QIODevice::WriteOnly));
|
||||
js.write(QStringLiteral(
|
||||
"var e = qet.elementUuids(0)[0];\n"
|
||||
"var before = qet.query(\"SELECT pos FROM element WHERE uuid = '\" + e + \"'\")[0].pos;\n"
|
||||
"qet.log('MOVED ' + qet.moveElement(0, e, 300, 0));\n"
|
||||
"var after = qet.query(\"SELECT pos FROM element WHERE uuid = '\" + e + \"'\")[0].pos;\n"
|
||||
"qet.log('CELL ' + before + ' ' + after);\n"
|
||||
"qet.log('DUMP ' + JSON.stringify({element: qet.query('SELECT * FROM element')}));\n"
|
||||
"qet.log('SAVED ' + qet.save('%1'));\n").arg(edited).toUtf8());
|
||||
js.close();
|
||||
const QString log = run({QStringLiteral("--run"), js.fileName(), saved});
|
||||
QVERIFY2(log.contains(QStringLiteral("MOVED true")) && log.contains(QStringLiteral("SAVED true")),
|
||||
qPrintable(log.right(400)));
|
||||
QString unused;
|
||||
const QJsonArray a = tablesIn(log, &unused).value(QStringLiteral("element")).toArray();
|
||||
const QJsonArray b = dump(edited, false, &unused).value(QStringLiteral("element")).toArray();
|
||||
QVERIFY(!a.isEmpty());
|
||||
QVERIFY2(a == b, qPrintable(log.mid(log.indexOf(QStringLiteral("CELL")), 40)));
|
||||
}
|
||||
|
||||
// After every conductor on a folio is deleted, the terminal table
|
||||
// holds what a fresh open of the saved project gives: a terminal is
|
||||
// listed while a conductor ends on it. The ends of a deleted conductor
|
||||
// used to stay.
|
||||
void deletedWireTerminals()
|
||||
{
|
||||
const QString saved = m_dir.filePath(QStringLiteral("unwired.qet"));
|
||||
const QString edited = m_dir.filePath(QStringLiteral("unwired-edited.qet"));
|
||||
run({QStringLiteral("--resave"), QStringLiteral(QET_EXAMPLES_DIR "/tremie_vibrante.qet"), saved});
|
||||
QVERIFY(QFile::exists(saved));
|
||||
QFile js(m_dir.filePath(QStringLiteral("unwired.js")));
|
||||
QVERIFY(js.open(QIODevice::WriteOnly));
|
||||
js.write(QStringLiteral(
|
||||
"var n = 0;\n"
|
||||
"qet.conductorUuids(0).forEach(function (c) {\n"
|
||||
" var end = qet.conductorEnds(0, c)[0];\n"
|
||||
" if (!end) return;\n"
|
||||
" var m = end.match(/^(\\{[^}]+\\}) terminal (\\d+)$/);\n"
|
||||
" if (m && qet.deleteConductor(0, m[1], parseInt(m[2]))) ++n;\n"
|
||||
"});\n"
|
||||
"qet.log('DELETED ' + n);\n"
|
||||
"qet.log('DUMP ' + JSON.stringify({terminal: qet.query('SELECT * FROM terminal')}));\n"
|
||||
"qet.log('SAVED ' + qet.save('%1'));\n").arg(edited).toUtf8());
|
||||
js.close();
|
||||
const QString log = run({QStringLiteral("--run"), js.fileName(), saved});
|
||||
QVERIFY2(!log.contains(QStringLiteral("DELETED 0")) && log.contains(QStringLiteral("SAVED true")),
|
||||
qPrintable(log.right(400)));
|
||||
QString unused;
|
||||
const QJsonArray a = tablesIn(log, &unused).value(QStringLiteral("terminal")).toArray();
|
||||
const QJsonArray b = dump(edited, false, &unused).value(QStringLiteral("terminal")).toArray();
|
||||
QVERIFY2(a == b, qPrintable(QStringLiteral("%1 terminal rows after the edits, %2 on reopening")
|
||||
.arg(a.size()).arg(b.size())));
|
||||
}
|
||||
|
||||
// A file whose items carry no saved uuid is filled from the folios,
|
||||
// which work those uuids out as they are built, and says why.
|
||||
void olderFileFallsBack()
|
||||
|
||||
@@ -160,6 +160,73 @@ private slots:
|
||||
QVERIFY2(sb < sa, "the coil's contacts were saved in another order than the file's");
|
||||
}
|
||||
|
||||
// A table split over several folios whose part names a previous part
|
||||
// that is not in the file: opening and closing it crashed (the part
|
||||
// has no data of its own and handed its missing data on when it was
|
||||
// destroyed). industrial.qet's third <previous_table> is changed.
|
||||
void missingPreviousTableOpens()
|
||||
{
|
||||
QByteArray xml = read(QStringLiteral(QET_EXAMPLES_DIR "/industrial.qet"));
|
||||
int at = -1;
|
||||
for (int n = 0 ; n < 3 ; ++n) {
|
||||
at = xml.indexOf("<previous_table uuid=\"", at + 1);
|
||||
QVERIFY(at > 0);
|
||||
}
|
||||
at += int(qstrlen("<previous_table uuid=\""));
|
||||
xml.replace(at, 38, "{00000000-0000-4000-8000-000000000000}");
|
||||
const QString in = m_dir.filePath(QStringLiteral("missing_previous.qet"));
|
||||
QFile f(in);
|
||||
QVERIFY(f.open(QIODevice::WriteOnly));
|
||||
f.write(xml);
|
||||
f.close();
|
||||
QVERIFY2(!resave(in).isEmpty(), "--resave failed: QElectroTech crashed closing the project");
|
||||
}
|
||||
|
||||
// Deleting the folio that holds a middle part of a split table, then
|
||||
// saving: every <previous_table> in the file names a table that is in
|
||||
// it. It used to name the deleted part, so every part after it opened
|
||||
// with no data. industrial.qet's folio 46 (index 45) holds such a part.
|
||||
void deletedFolioKeepsTableChain()
|
||||
{
|
||||
#ifndef QET_HAS_SCRIPTING
|
||||
QSKIP("needs --run: this QElectroTech is built without Qt Qml");
|
||||
#endif
|
||||
const QString out = m_dir.filePath(QStringLiteral("deleted_folio.qet"));
|
||||
const QString script = m_dir.filePath(QStringLiteral("deleted_folio.js"));
|
||||
QFile js(script);
|
||||
QVERIFY(js.open(QIODevice::WriteOnly));
|
||||
js.write(QStringLiteral("qet.log('REMOVED ' + qet.removeFolio(45));\n"
|
||||
"qet.log('SAVED ' + qet.save('%1'));\n").arg(out).toUtf8());
|
||||
js.close();
|
||||
const QString home = m_dir.filePath(QStringLiteral("home%1").arg(m_run++));
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
env.insert(QStringLiteral("QET_ENABLE_SCRIPTING"), QStringLiteral("1"));
|
||||
env.insert(QStringLiteral("HOME"), home);
|
||||
env.insert(QStringLiteral("XDG_CONFIG_HOME"), home + QStringLiteral("/config"));
|
||||
env.insert(QStringLiteral("XDG_DATA_HOME"), home + QStringLiteral("/data"));
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH),
|
||||
{QStringLiteral("--run"), script, QStringLiteral(QET_EXAMPLES_DIR "/industrial.qet")});
|
||||
QVERIFY(proc.waitForFinished(180000));
|
||||
const QString log = QString::fromUtf8(proc.readAllStandardOutput() + proc.readAllStandardError());
|
||||
QVERIFY2(log.contains(QStringLiteral("REMOVED true")) && log.contains(QStringLiteral("SAVED true")),
|
||||
qPrintable(log.right(400)));
|
||||
|
||||
const QString saved = QString::fromUtf8(read(out));
|
||||
QSet<QString> tables;
|
||||
for (const auto &m : QRegularExpression(QStringLiteral("<graphics_table [^>]*uuid=\"([^\"]+)\"")).globalMatch(saved))
|
||||
tables.insert(m.captured(1));
|
||||
int references = 0;
|
||||
for (const auto &m : QRegularExpression(QStringLiteral("<previous_table uuid=\"([^\"]+)\"")).globalMatch(saved)) {
|
||||
++references;
|
||||
QVERIFY2(tables.contains(m.captured(1)),
|
||||
qPrintable(QStringLiteral("a table names %1, which is not in the file").arg(m.captured(1))));
|
||||
}
|
||||
QVERIFY(references > 0);
|
||||
}
|
||||
|
||||
// A folio report arrow linked to one that has no terminal -- such as
|
||||
// ref_voyant_2_h.elmt from the collection -- crashed QElectroTech on
|
||||
// opening: the arrow's text looked for a wire on the other arrow's
|
||||
|
||||
Reference in New Issue
Block a user