mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-19 12:34:15 +02:00
Merge pull request #1552 from ispyisail/fix/keep-unloadable-items
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m43s
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m43s
Fix symbols and wires lost when their definition cannot be found
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
#include "qetcontainerdb.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <functional>
|
||||
#include <QHash>
|
||||
#include <QMap>
|
||||
#include <QSet>
|
||||
@@ -242,6 +243,28 @@ bool QetContainer::ownUuids(const QDomElement &project)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QetContainer::embeddedDefinitions
|
||||
@return the paths of the symbol definitions the project @p project
|
||||
holds in its collection, as a symbol's type names them after embed://
|
||||
*/
|
||||
QSet<QString> QetContainer::embeddedDefinitions(const QDomElement &project)
|
||||
{
|
||||
QSet<QString> paths;
|
||||
std::function<void(const QDomElement &, const QString &)> walk =
|
||||
[&](const QDomElement &category, const QString &path) {
|
||||
for (QDomElement child = category.firstChildElement() ; !child.isNull() ;
|
||||
child = child.nextSiblingElement()) {
|
||||
const QString name = path + child.attribute(QStringLiteral("name"));
|
||||
if (child.tagName() == QLatin1String("category")) walk(child, name + QLatin1Char('/'));
|
||||
else if (child.tagName() == QLatin1String("element")) paths.insert(name);
|
||||
}
|
||||
};
|
||||
for (const QDomElement &collection : childElements(project, QStringLiteral("collection")))
|
||||
walk(collection, QString());
|
||||
return paths;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QetContainer::dropComputed
|
||||
Take out of the folio @p diagram what QElectroTech computes when it
|
||||
@@ -253,19 +276,39 @@ bool QetContainer::ownUuids(const QDomElement &project)
|
||||
list of its terminals. Checked on 342 projects: opened and saved, each
|
||||
comes back as it was, but the terminal lists of a file whose symbols
|
||||
share a uuid (F100), where wires find their ends by them.
|
||||
Nothing is taken from a symbol whose definition is not among
|
||||
@p defined (embeddedDefinitions()), nor from a wire ending on one:
|
||||
QElectroTech cannot build that symbol, keeps it as read and saves it
|
||||
back (F127), so nothing of it would be computed again.
|
||||
*/
|
||||
void QetContainer::dropComputed(QDomElement diagram, bool terminal_lists)
|
||||
void QetContainer::dropComputed(QDomElement diagram, bool terminal_lists,
|
||||
const QSet<QString> &defined)
|
||||
{
|
||||
QSet<QString> undefined;
|
||||
for (const QDomElement &list : childElements(diagram, QStringLiteral("elements")))
|
||||
for (const QDomElement &element : childElements(list, QStringLiteral("element"))) {
|
||||
const QString type = element.attribute(QStringLiteral("type"));
|
||||
if (!type.startsWith(QLatin1String("embed://"))
|
||||
|| !defined.contains(type.mid(int(qstrlen("embed://")))))
|
||||
undefined.insert(element.attribute(QStringLiteral("uuid")));
|
||||
}
|
||||
for (QDomElement list : childElements(diagram, QStringLiteral("conductors")))
|
||||
for (QDomElement wire : childElements(list, QStringLiteral("conductor")))
|
||||
{
|
||||
if (undefined.contains(wire.attribute(QStringLiteral("element1")))
|
||||
|| undefined.contains(wire.attribute(QStringLiteral("element2"))))
|
||||
continue;
|
||||
for (const char *end : {"1", "2"})
|
||||
for (const char *name : {"element%1_label", "element%1_name", "element%1_linked",
|
||||
"terminalname%1"})
|
||||
wire.removeAttribute(QString::fromLatin1(name).arg(QLatin1String(end)));
|
||||
}
|
||||
for (QDomElement list : childElements(diagram, QStringLiteral("elements")))
|
||||
{
|
||||
for (QDomElement element : childElements(list, QStringLiteral("element")))
|
||||
{
|
||||
if (undefined.contains(element.attribute(QStringLiteral("uuid"))))
|
||||
continue;
|
||||
if (terminal_lists)
|
||||
for (QDomElement terminals : childElements(element, QStringLiteral("terminals")))
|
||||
removeElement(terminals);
|
||||
@@ -341,8 +384,9 @@ QList<QetZip::Entry> QetContainer::split(const QDomDocument &project, const QStr
|
||||
int folio = 0;
|
||||
//Nothing QElectroTech computes is kept (format 4)
|
||||
const bool own_uuids = ownUuids(root);
|
||||
const QSet<QString> defined = embeddedDefinitions(root);
|
||||
for (QDomElement diagram : childElements(root, QStringLiteral("diagram")))
|
||||
dropComputed(diagram, own_uuids);
|
||||
dropComputed(diagram, own_uuids, defined);
|
||||
|
||||
//The parts of the folios kept as they were, and their pictures
|
||||
QMap<QString, QByteArray> pictures; //name -> png, once each
|
||||
|
||||
@@ -89,7 +89,9 @@ namespace QetContainer
|
||||
void inlinePicture(QDomElement image, const QByteArray &bytes);
|
||||
|
||||
/// Take what QElectroTech computes out of a folio (format 4)
|
||||
void dropComputed(QDomElement diagram, bool terminal_lists);
|
||||
void dropComputed(QDomElement diagram, bool terminal_lists, const QSet<QString> &defined);
|
||||
/// The symbol definitions a project holds, by path after embed://
|
||||
QSet<QString> embeddedDefinitions(const QDomElement &project);
|
||||
/// true if every folio, symbol and wire has a uuid of its own
|
||||
bool ownUuids(const QDomElement &project);
|
||||
/// Parse @p xml as a .qet is read (QETProject::openFile)
|
||||
|
||||
@@ -4119,9 +4119,24 @@ bool projectDataBase::savedLinks(const Element *element, QList<std::pair<QUuid,
|
||||
@brief projectDataBase::waitingSymbols
|
||||
@return the symbols on folios not built yet (lazy folios) and not
|
||||
built alone as partners either (Diagram::buildSymbols()): partners a
|
||||
symbol keeps its rows to until they are built
|
||||
symbol keeps its rows to until they are built; and the symbols a built
|
||||
folio could not build and keeps as read (Diagram::keptSymbolUuids())
|
||||
*/
|
||||
QSet<QUuid> projectDataBase::waitingSymbols() const
|
||||
{
|
||||
QSet<QUuid> kept;
|
||||
if (m_project)
|
||||
for (Diagram *diagram : m_project->folios())
|
||||
if (diagram->isLoaded()) kept.unite(diagram->keptSymbolUuids());
|
||||
return unbuiltSymbols().unite(kept);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::unbuiltSymbols
|
||||
@return the symbols on folios not built yet and not built alone
|
||||
(waitingSymbols())
|
||||
*/
|
||||
QSet<QUuid> projectDataBase::unbuiltSymbols() const
|
||||
{
|
||||
if (!m_project || !m_project->unloadedFolioCount()) {
|
||||
return QSet<QUuid>();
|
||||
|
||||
@@ -165,6 +165,7 @@ class projectDataBase : public QObject
|
||||
QList<std::pair<QUuid, int>> linkRowsOf(const QUuid &element) const;
|
||||
QSet<QUuid> waitingPartners(const QUuid &element, const QSet<QUuid> &waiting);
|
||||
QSet<QUuid> waitingSymbols() const;
|
||||
QSet<QUuid> unbuiltSymbols() const;
|
||||
|
||||
//Fill the three stores from a project's document before its
|
||||
//folios are built, then, once
|
||||
|
||||
+197
-13
@@ -944,6 +944,132 @@ QStringList Diagram::wiresNotReconnected() const
|
||||
return m_wires_not_reconnected;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Diagram::symbolsNotLoaded
|
||||
@return one line per symbol of the loaded file that could not be built,
|
||||
its definition missing from the project and the collections: kept as
|
||||
read and saved back (F127). Empty for a folio that built every symbol.
|
||||
*/
|
||||
QStringList Diagram::symbolsNotLoaded() const
|
||||
{
|
||||
return m_symbols_not_loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Diagram::keptSymbolUuids
|
||||
@return the uuids of the symbols this folio could not build and keeps
|
||||
as read: a partner linked to one of them keeps its link
|
||||
*/
|
||||
QSet<QUuid> Diagram::keptSymbolUuids() const
|
||||
{
|
||||
QSet<QUuid> uuids;
|
||||
for (const QDomElement &symbol : m_kept_symbols)
|
||||
uuids.insert(QUuid(symbol.attribute(QStringLiteral("uuid"))));
|
||||
return uuids;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Diagram::keepWire
|
||||
Keep @p wire, which could not be joined to its terminals, to save it
|
||||
back (F127, F128). An end found (@p found1, @p found2) is written from
|
||||
that terminal at save; an end not found keeps the symbol and terminal
|
||||
it names, as uuids: for a wire saved by legacy terminal ids, the
|
||||
symbol among @p symbols (uuid and XML of each symbol read) holding that
|
||||
id and the uuid derived for that terminal where it was saved
|
||||
(Element::savedTerminalUuid()), so the wire joins it again once the
|
||||
symbol's definition has it there. An end naming no terminal of the
|
||||
file at all pointed nowhere when it was saved: written as the null
|
||||
uuid, so it names no symbol and is read back as a wire whose ends
|
||||
cannot be read, kept unchanged and joining nothing (never a legacy id,
|
||||
which the next save gives to another terminal).
|
||||
*/
|
||||
void Diagram::keepWire(const QDomElement &wire, Terminal *found1, Terminal *found2,
|
||||
const QList<QPair<QUuid, QDomElement>> &symbols)
|
||||
{
|
||||
KeptWire kept;
|
||||
Terminal *found[2] = {found1, found2};
|
||||
for (int i = 0 ; i < 2 ; ++i) {
|
||||
const QString index = QString::number(i + 1);
|
||||
if (found[i]) {
|
||||
kept.end[i] = found[i];
|
||||
kept.found[i] = true;
|
||||
continue;
|
||||
}
|
||||
if (wire.hasAttribute(QStringLiteral("element") + index)) {
|
||||
kept.element[i] = QUuid(wire.attribute(QStringLiteral("element") + index));
|
||||
kept.terminal[i] = QUuid(wire.attribute(QStringLiteral("terminal") + index));
|
||||
} else {
|
||||
const QString id = wire.attribute(QStringLiteral("terminal") + index);
|
||||
for (const auto &symbol : symbols) {
|
||||
const QList<QDomElement> records = QET::findInDomElement(
|
||||
symbol.second, QStringLiteral("terminals"), QStringLiteral("terminal"));
|
||||
for (int r = 0 ; r < records.size() && kept.element[i].isNull() ; ++r)
|
||||
if (records.at(r).attribute(QStringLiteral("id")) == id) {
|
||||
kept.element[i] = symbol.first;
|
||||
kept.terminal[i] = Element::savedTerminalUuid(records, r);
|
||||
}
|
||||
if (!kept.element[i].isNull()) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
kept.xml = m_kept_document.importNode(wire, true).toElement();
|
||||
if (QUuid(kept.xml.attribute(QStringLiteral("uuid"))).isNull() && m_project) {
|
||||
QStringList ends;
|
||||
for (int i = 0 ; i < 2 ; ++i)
|
||||
ends << (kept.found[i]
|
||||
? kept.end[i]->parentElement()->uuid().toString() + QLatin1Char('/')
|
||||
+ kept.end[i]->stableUuid().toString()
|
||||
: kept.element[i].toString() + QLatin1Char('/') + kept.terminal[i].toString());
|
||||
ends.sort();
|
||||
kept.xml.setAttribute(QStringLiteral("uuid"),
|
||||
m_project->derivedItemUuid(QStringLiteral("conductor"),
|
||||
ends.join(QLatin1Char('\n'))).toString());
|
||||
}
|
||||
m_kept_wires << kept;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Diagram::writeKept
|
||||
Write what this folio could not load, as it was read, into @p elements
|
||||
and @p conductors of @p document: the symbols, their terminals given ids
|
||||
after those of @p table_adr_id so none is read twice, and the wires, an
|
||||
end found written from its terminal as Conductor::toXml() does. A wire
|
||||
whose found end was deleted since goes with it.
|
||||
*/
|
||||
void Diagram::writeKept(QDomDocument &document, QDomElement &elements,
|
||||
QDomElement &conductors, const QHash<Terminal *, int> &table_adr_id) const
|
||||
{
|
||||
int next_id = 0;
|
||||
for (int id : table_adr_id) next_id = qMax(next_id, id + 1);
|
||||
for (const QDomElement &symbol : m_kept_symbols) {
|
||||
QDomElement copy = document.importNode(symbol, true).toElement();
|
||||
for (QDomElement record : QET::findInDomElement(copy, QStringLiteral("terminals"),
|
||||
QStringLiteral("terminal")))
|
||||
record.setAttribute(QStringLiteral("id"), next_id++);
|
||||
elements.appendChild(copy);
|
||||
}
|
||||
for (const KeptWire &kept : m_kept_wires) {
|
||||
if ((kept.found[0] && !kept.end[0]) || (kept.found[1] && !kept.end[1])) continue;
|
||||
QDomElement copy = document.importNode(kept.xml, true).toElement();
|
||||
for (int i = 0 ; i < 2 && !kept.as_read ; ++i) {
|
||||
const QString index = QString::number(i + 1);
|
||||
const QString element = QStringLiteral("element") + index;
|
||||
const QString terminal = QStringLiteral("terminal") + index;
|
||||
if (!kept.found[i]) {
|
||||
copy.setAttribute(element, kept.element[i].toString());
|
||||
copy.setAttribute(terminal, kept.terminal[i].toString());
|
||||
} else if (kept.end[i]->uuid().isNull()) {
|
||||
copy.removeAttribute(element);
|
||||
copy.setAttribute(terminal, table_adr_id.value(kept.end[i]));
|
||||
} else {
|
||||
copy.setAttribute(element, kept.end[i]->parentElement()->uuid().toString());
|
||||
copy.setAttribute(terminal, kept.end[i]->uuid().toString());
|
||||
}
|
||||
}
|
||||
conductors.appendChild(copy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Diagram::uuidUsedByOtherDiagram
|
||||
A hand-edited or merged project file can contain two folios with the same
|
||||
@@ -1268,7 +1394,10 @@ QDomDocument Diagram::toXml(bool whole_content, bool is_copy_command) {
|
||||
}
|
||||
document.appendChild(dom_root);
|
||||
|
||||
if (items().isEmpty())
|
||||
//What the folio could not load is written back with a whole folio
|
||||
const bool write_kept = whole_content && !is_copy_command
|
||||
&& (!m_kept_symbols.isEmpty() || !m_kept_wires.isEmpty());
|
||||
if (items().isEmpty() && !write_kept)
|
||||
return(document);
|
||||
|
||||
QVector<Element *> list_elements;
|
||||
@@ -1375,8 +1504,8 @@ QDomDocument Diagram::toXml(bool whole_content, bool is_copy_command) {
|
||||
// table de correspondance entre les adresses des bornes et leurs ids
|
||||
QHash<Terminal *, int> table_adr_id;
|
||||
|
||||
if (!list_elements.isEmpty()) {
|
||||
auto dom_elements = document.createElement(QStringLiteral("elements"));
|
||||
auto dom_elements = document.createElement(QStringLiteral("elements"));
|
||||
if (!list_elements.isEmpty() || write_kept) {
|
||||
for (auto elmt : list_elements) {
|
||||
QDomElement dom_element = elmt->toXml(document, table_adr_id);
|
||||
ItemGroups::write(dom_element, elmt);
|
||||
@@ -1388,6 +1517,7 @@ QDomDocument Diagram::toXml(bool whole_content, bool is_copy_command) {
|
||||
dom_root.appendChild(dom_elements);
|
||||
}
|
||||
|
||||
auto dom_conductors = document.createElement(QStringLiteral("conductors"));
|
||||
if (!list_conductors.isEmpty()) {
|
||||
//Symbols copied in old versions can share one uuid. A wire
|
||||
//saved by that uuid would reopen on the first of them (#1408).
|
||||
@@ -1397,7 +1527,6 @@ QDomDocument Diagram::toXml(bool whole_content, bool is_copy_command) {
|
||||
shared_uuids.insert(elmt->uuid());
|
||||
seen_uuids.insert(elmt->uuid());
|
||||
}
|
||||
auto dom_conductors = document.createElement(QStringLiteral("conductors"));
|
||||
for (auto cond : list_conductors) {
|
||||
dom_conductors.appendChild(cond->toXml(document,
|
||||
table_adr_id,
|
||||
@@ -1405,6 +1534,11 @@ QDomDocument Diagram::toXml(bool whole_content, bool is_copy_command) {
|
||||
}
|
||||
dom_root.appendChild(dom_conductors);
|
||||
}
|
||||
if (write_kept) {
|
||||
writeKept(document, dom_elements, dom_conductors, table_adr_id);
|
||||
if (dom_conductors.hasChildNodes() && dom_conductors.parentNode().isNull())
|
||||
dom_root.insertAfter(dom_conductors, dom_elements);
|
||||
}
|
||||
|
||||
// A rect query, unlike items(), returns true stacking order (z, then
|
||||
// insertion order) even with NoIndex.
|
||||
@@ -1603,6 +1737,23 @@ void Diagram::folioSequentialsToXml(QHash<QString,
|
||||
fromXml() does for each symbol it reads.
|
||||
@return the symbol, or nullptr if it could not be made (said in the log)
|
||||
*/
|
||||
/**
|
||||
@brief Diagram::derivedSymbolUuid
|
||||
@return the uuid of the symbol @p element_xml, saved without one:
|
||||
derived from what it is and where it sits on its folio, never from the
|
||||
folio's index (see buildElement())
|
||||
*/
|
||||
QUuid Diagram::derivedSymbolUuid(const QDomElement &element_xml) const
|
||||
{
|
||||
return m_project->derivedItemUuid(
|
||||
QStringLiteral("element"),
|
||||
QStringList{element_xml.attribute(QStringLiteral("type")),
|
||||
element_xml.attribute(QStringLiteral("x")),
|
||||
element_xml.attribute(QStringLiteral("y")),
|
||||
element_xml.attribute(QStringLiteral("orientation"))}
|
||||
.join(QLatin1Char('\n')));
|
||||
}
|
||||
|
||||
Element *Diagram::buildElement(QDomElement element_xml, QHash<int, Terminal *> &table_adr_id,
|
||||
bool consider_informations)
|
||||
{
|
||||
@@ -1650,13 +1801,7 @@ Element *Diagram::buildElement(QDomElement element_xml, QHash<int, Terminal *> &
|
||||
//uuids anyway.
|
||||
if (consider_informations && m_project
|
||||
&& QUuid(element_xml.attribute(QStringLiteral("uuid"))).isNull()) {
|
||||
nvel_elmt->setUuid(m_project->derivedItemUuid(
|
||||
QStringLiteral("element"),
|
||||
QStringList{type_id,
|
||||
element_xml.attribute(QStringLiteral("x")),
|
||||
element_xml.attribute(QStringLiteral("y")),
|
||||
element_xml.attribute(QStringLiteral("orientation"))}
|
||||
.join(QLatin1Char('\n'))));
|
||||
nvel_elmt->setUuid(derivedSymbolUuid(element_xml));
|
||||
}
|
||||
ItemGroups::setGroup(nvel_elmt, ItemGroups::read(element_xml));
|
||||
return nvel_elmt;
|
||||
@@ -2092,6 +2237,14 @@ bool Diagram::fromXml(QDomElement &document,
|
||||
QHash<int, Terminal *> table_adr_id;
|
||||
if (m_loading_deferred) {
|
||||
table_adr_id = m_partial_table; //the terminals of symbols built already
|
||||
}
|
||||
//Each symbol read, by uuid: the ends of a wire kept (keepWire())
|
||||
QList<QPair<QUuid, QDomElement>> read_symbols;
|
||||
if (consider_informations) {
|
||||
m_kept_document = QDomDocument();
|
||||
m_kept_symbols.clear();
|
||||
m_kept_wires.clear();
|
||||
m_symbols_not_loaded.clear();
|
||||
}
|
||||
for (auto element_xml :
|
||||
QET::findInDomElement(root, QStringLiteral("elements"), QStringLiteral("element")))
|
||||
@@ -2102,11 +2255,30 @@ bool Diagram::fromXml(QDomElement &document,
|
||||
Element *built = m_partial_elements.value(QUuid(element_xml.attribute(QStringLiteral("uuid"))));
|
||||
if (built) {
|
||||
added_elements << built;
|
||||
read_symbols.append({built->uuid(), element_xml});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (Element *nvel_elmt = buildElement(element_xml, table_adr_id, consider_informations))
|
||||
if (Element *nvel_elmt = buildElement(element_xml, table_adr_id, consider_informations)) {
|
||||
added_elements << nvel_elmt;
|
||||
if (consider_informations) read_symbols.append({nvel_elmt->uuid(), element_xml});
|
||||
} else if (consider_informations) {
|
||||
//Its definition is missing: kept as read, saved back, so a
|
||||
//save loses nothing (F127)
|
||||
QDomElement kept = m_kept_document.importNode(element_xml, true).toElement();
|
||||
if (QUuid(kept.attribute(QStringLiteral("uuid"))).isNull() && m_project)
|
||||
kept.setAttribute(QStringLiteral("uuid"), derivedSymbolUuid(element_xml).toString());
|
||||
m_kept_symbols << kept;
|
||||
read_symbols.append({QUuid(kept.attribute(QStringLiteral("uuid"))), element_xml});
|
||||
QString label;
|
||||
for (const QDomElement &info : QET::findInDomElement(
|
||||
element_xml, QStringLiteral("elementInformations"), QStringLiteral("elementInformation")))
|
||||
if (info.attribute(QStringLiteral("name")) == QLatin1String("label")) label = info.text();
|
||||
const QString type = element_xml.attribute(QStringLiteral("type"));
|
||||
m_symbols_not_loaded << (label.isEmpty()
|
||||
? type.section(QLatin1Char('/'), -1)
|
||||
: QStringLiteral("%1 (%2)").arg(label, type.section(QLatin1Char('/'), -1)));
|
||||
}
|
||||
}
|
||||
|
||||
//Texts, images and shapes written before they carried a uuid (or
|
||||
@@ -2262,7 +2434,17 @@ bool Diagram::fromXml(QDomElement &document,
|
||||
QStringLiteral("conductors"),
|
||||
QStringLiteral("conductor")))
|
||||
{
|
||||
if (!Conductor::valideXml(f)) continue;
|
||||
if (!Conductor::valideXml(f)) {
|
||||
//Its ends cannot be read: kept and written back unchanged
|
||||
//(F128), it can join nothing
|
||||
if (consider_informations) {
|
||||
KeptWire kept;
|
||||
kept.xml = m_kept_document.importNode(f, true).toElement();
|
||||
kept.as_read = true;
|
||||
m_kept_wires << kept;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
//Check if terminal that conductor must be linked is know
|
||||
|
||||
@@ -2297,6 +2479,8 @@ bool Diagram::fromXml(QDomElement &document,
|
||||
if (!num.isEmpty() && num != QLatin1String("_"))
|
||||
wire += QStringLiteral(" (%1)").arg(num);
|
||||
m_wires_not_reconnected << wire;
|
||||
//Kept as read, saved back (F128)
|
||||
keepWire(f, p1, p2, read_symbols);
|
||||
}
|
||||
|
||||
if (p1 && p2 && p1 != p2)
|
||||
|
||||
@@ -39,6 +39,7 @@ class DiagramContent;
|
||||
class DiagramPosition;
|
||||
class DiagramTextItem;
|
||||
class Element;
|
||||
class Terminal;
|
||||
class ElementsLocation;
|
||||
class DiagramImageItem;
|
||||
class DiagramEventInterface;
|
||||
@@ -165,6 +166,28 @@ class Diagram : public QGraphicsScene
|
||||
|
||||
//Wires of the loaded file whose ends could not be found
|
||||
QStringList m_wires_not_reconnected;
|
||||
//Symbols of the loaded file that could not be built
|
||||
QStringList m_symbols_not_loaded;
|
||||
|
||||
//What the folio could not load, written back as it was read
|
||||
//(F127, F128): symbols whose definition is missing, and wires
|
||||
//to a terminal not found
|
||||
struct KeptWire {
|
||||
QDomElement xml;
|
||||
QPointer<Terminal> end[2]; ///< an end found, null if not
|
||||
bool found[2] = {false, false};
|
||||
QUuid element[2]; ///< an end not found: its symbol
|
||||
QUuid terminal[2]; ///< and its terminal
|
||||
bool as_read = false; ///< no end can be read: written unchanged
|
||||
};
|
||||
QDomDocument m_kept_document;
|
||||
QList<QDomElement> m_kept_symbols;
|
||||
QList<KeptWire> m_kept_wires;
|
||||
QUuid derivedSymbolUuid(const QDomElement &element_xml) const;
|
||||
void keepWire(const QDomElement &wire, Terminal *found1, Terminal *found2,
|
||||
const QList<QPair<QUuid, QDomElement>> &symbols);
|
||||
void writeKept(QDomDocument &document, QDomElement &elements,
|
||||
QDomElement &conductors, const QHash<Terminal *, int> &table_adr_id) const;
|
||||
|
||||
// METHODS
|
||||
protected:
|
||||
@@ -189,6 +212,8 @@ class Diagram : public QGraphicsScene
|
||||
void restoreText(Element* elmt);
|
||||
QUuid uuid();
|
||||
QStringList wiresNotReconnected() const;
|
||||
QStringList symbolsNotLoaded() const;
|
||||
QSet<QUuid> keptSymbolUuids() const;
|
||||
void setEventInterface (DiagramEventInterface *event_interface);
|
||||
void clearEventInterface();
|
||||
|
||||
|
||||
@@ -2262,27 +2262,49 @@ bool QETDiagramEditor::openAndAddProject(
|
||||
);
|
||||
}
|
||||
|
||||
//Report wires left out of the load because a terminal they join
|
||||
//was not found: they would otherwise vanish on the next save
|
||||
//without the user ever being told.
|
||||
QStringList lost_wires;
|
||||
//Report the elements and wires left out of the load: an element
|
||||
//whose definition is missing, a wire whose terminal was not found.
|
||||
//They are kept in the file as they were (F127, F128), but are not
|
||||
//shown.
|
||||
QStringList lost_elements, lost_wires;
|
||||
for (Diagram *diagram : project->folios()) {
|
||||
if (!diagram->isLoaded()) continue;
|
||||
for (const QString &element : diagram->symbolsNotLoaded())
|
||||
lost_elements << tr("Sheet %1: %2").arg(diagram->folioIndex() + 1).arg(element);
|
||||
}
|
||||
for (Diagram *diagram : project->foliosWithWiresNotReconnected()) {
|
||||
for (const QString &wire : diagram->wiresNotReconnected()) {
|
||||
lost_wires << tr("Sheet %1: %2").arg(diagram->folioIndex() + 1).arg(wire);
|
||||
}
|
||||
}
|
||||
if (interactive && !lost_elements.isEmpty())
|
||||
{
|
||||
QMessageBox box(QMessageBox::Warning,
|
||||
tr("Elements not loaded", "message box title"),
|
||||
tr("%n elements could not be loaded: their definition is "
|
||||
"missing from the project and from the collections.\n"
|
||||
"\n"
|
||||
"They are not shown, but they are kept in the file as they "
|
||||
"are, with their conductors, and come back once their "
|
||||
"definition is available again.",
|
||||
"message box content",
|
||||
lost_elements.size()),
|
||||
QMessageBox::Ok,
|
||||
this);
|
||||
box.setDetailedText(lost_elements.join(QLatin1Char('\n')));
|
||||
box.exec();
|
||||
}
|
||||
if (interactive && !lost_wires.isEmpty())
|
||||
{
|
||||
QMessageBox box(QMessageBox::Warning,
|
||||
tr("Conductors not loaded", "message box title"),
|
||||
tr("%n conductors could not be joined to their terminals and "
|
||||
"were not loaded. The element's definition in the project "
|
||||
"was probably replaced by another one with different "
|
||||
"terminals.\n"
|
||||
"were not loaded. The element's definition is missing, or "
|
||||
"was replaced by another one with different terminals.\n"
|
||||
"\n"
|
||||
"If you save the project, these conductors will disappear "
|
||||
"from the file. Close it without saving to keep the file as "
|
||||
"it is.",
|
||||
"They are not shown, but they are kept in the file as they "
|
||||
"are, and join their terminals again once the definition "
|
||||
"has them where they were.",
|
||||
"message box content",
|
||||
lost_wires.size()),
|
||||
QMessageBox::Ok,
|
||||
|
||||
@@ -975,6 +975,41 @@ bool Element::valideXml(QDomElement &e)
|
||||
return(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Element::savedTerminalUuid
|
||||
@return the uuid TerminalUuids derives for the terminal saved as
|
||||
@p records [@p index] (a symbol's saved terminals, in file order) at the
|
||||
place it was saved: its docking point less the stub Terminal::init()
|
||||
adds, and its rank among the records at that place, as the project
|
||||
counted it when it gave the terminals their uuids
|
||||
*/
|
||||
QUuid Element::savedTerminalUuid(const QList<QDomElement> &records, int index)
|
||||
{
|
||||
const auto dock_of = [](const QDomElement &r) {
|
||||
return QPointF(r.attribute(QStringLiteral("x")).toDouble(),
|
||||
r.attribute(QStringLiteral("y")).toDouble());
|
||||
};
|
||||
const QDomElement &record = records.at(index);
|
||||
const int orientation = record.attribute(QStringLiteral("orientation")).toInt();
|
||||
const QPointF dock = dock_of(record);
|
||||
QPointF old_pos = dock;
|
||||
switch (orientation) {
|
||||
case Qet::North: old_pos -= QPointF(0, Terminal::terminalSize); break;
|
||||
case Qet::East : old_pos -= QPointF(-Terminal::terminalSize, 0); break;
|
||||
case Qet::West : old_pos -= QPointF(Terminal::terminalSize, 0); break;
|
||||
case Qet::South: old_pos -= QPointF(0, -Terminal::terminalSize); break;
|
||||
}
|
||||
int rank = 0;
|
||||
for (int j = 0; j < index; ++j) {
|
||||
const QPointF other = dock_of(records.at(j));
|
||||
if (records.at(j).attribute(QStringLiteral("orientation")).toInt() == orientation
|
||||
&& qAbs(other.x() - dock.x()) < 1e-6
|
||||
&& qAbs(other.y() - dock.y()) < 1e-6)
|
||||
++rank;
|
||||
}
|
||||
return TerminalUuids::derived(old_pos.x(), old_pos.y(), orientation, rank);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Element::matchMovedTerminals
|
||||
Pair the saved terminals that no live terminal matched exactly with the
|
||||
@@ -1033,32 +1068,8 @@ void Element::matchMovedTerminals(const QList<QDomElement> &records,
|
||||
//Pair a record with a live terminal: register its legacy id, and the
|
||||
//uuid derived for the terminal at its old place
|
||||
const auto pair_up = [&](int index, Terminal *t) {
|
||||
const QDomElement &record = records.at(index);
|
||||
const QPointF dock = dock_of(record);
|
||||
|
||||
//The old place of the terminal: its docking point less the
|
||||
//stub that Terminal::init() added
|
||||
QPointF old_pos = dock;
|
||||
switch (orientation_of(record)) {
|
||||
case Qet::North: old_pos -= QPointF(0, Terminal::terminalSize); break;
|
||||
case Qet::East : old_pos -= QPointF(-Terminal::terminalSize, 0); break;
|
||||
case Qet::West : old_pos -= QPointF(Terminal::terminalSize, 0); break;
|
||||
case Qet::South: old_pos -= QPointF(0, -Terminal::terminalSize); break;
|
||||
}
|
||||
//The rank among the records at the same place, as the project
|
||||
//counted it when it gave the terminals their uuids
|
||||
int rank = 0;
|
||||
for (int j = 0; j < index; ++j) {
|
||||
const QPointF other = dock_of(records.at(j));
|
||||
if (orientation_of(records.at(j)) == orientation_of(record)
|
||||
&& qAbs(other.x() - dock.x()) < 1e-6
|
||||
&& qAbs(other.y() - dock.y()) < 1e-6)
|
||||
++rank;
|
||||
}
|
||||
|
||||
id_adr.insert(record.attribute(QStringLiteral("id")).toInt(), t);
|
||||
m_old_place_terminals.insert(TerminalUuids::derived(old_pos.x(), old_pos.y(),
|
||||
orientation_of(record), rank), t);
|
||||
id_adr.insert(records.at(index).attribute(QStringLiteral("id")).toInt(), t);
|
||||
m_old_place_terminals.insert(savedTerminalUuid(records, index), t);
|
||||
m_moved_terminals << t;
|
||||
};
|
||||
const auto same_place = [](const QPointF &a, const QPointF &b) {
|
||||
|
||||
@@ -183,6 +183,7 @@ class Element : public QetGraphicsItem
|
||||
QPoint hotspot() const;
|
||||
void editProperty() override;
|
||||
static bool valideXml(QDomElement &);
|
||||
static QUuid savedTerminalUuid(const QList<QDomElement> &records, int index);
|
||||
virtual bool fromXml(
|
||||
QDomElement &,
|
||||
QHash<int,
|
||||
|
||||
@@ -0,0 +1,421 @@
|
||||
<project title="Free text widths" uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7a00}" version="0.200.1">
|
||||
<properties>
|
||||
<property name="saveddate" show="1">10/2/26</property>
|
||||
<property name="saveddate-eu" show="1">02-10-2026</property>
|
||||
<property name="saveddate-us" show="1">2026-10-02</property>
|
||||
<property name="savedfilename" show="1">free_text_width</property>
|
||||
<property name="savedtime" show="1">18:39</property>
|
||||
</properties>
|
||||
<usage enabled="true" time_spent="0"/>
|
||||
<newdiagrams>
|
||||
<border cols="17" colsize="60" displaycols="true" displayrows="true" rows="8" rowsize="80"/>
|
||||
<inset author="" auto_page_num="" date="null" displayAt="bottom" filename="" folio="" indexrev="" locmach="" plant="" title="" version=""/>
|
||||
<conductors bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
<report label="%f-%l%c"/>
|
||||
<xrefs>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" stackoverlapping="true" switchprefix="" type="cable" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" stackoverlapping="true" switchprefix="" type="coil" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" stackoverlapping="true" switchprefix="" type="commutator" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" stackoverlapping="true" switchprefix="" type="plc" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" stackoverlapping="true" switchprefix="" type="protection" xrefpos="AlignBottom"/>
|
||||
</xrefs>
|
||||
<conductors_autonums auto_break_conductors="false" current_autonum="" freeze_new_conductors="false"/>
|
||||
<folio_autonums/>
|
||||
<element_autonums current_autonum="" freeze_new_elements="false"/>
|
||||
<guides/>
|
||||
</newdiagrams>
|
||||
<diagram author="" auto_page_num="" cols="17" colsize="60" date="null" displayAt="bottom" displaycols="true" displayrows="true" filename="" folio="" freezeNewConductor="false" freezeNewElement="false" height="660" indexrev="" locmach="" order="1" plant="" rows="8" rowsize="80" title="" uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7a01}" version="0.200.1-dev">
|
||||
<defaultconductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
<elements>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="K" type="embed://import/10_electric/10_allpole/310_relays_contactors_contacts/01_coils/bobine3.elmt" uuid="{ee860e7c-eaf3-404a-9c71-5ae4b296bdde}" x="200" y="200" z="10">
|
||||
<terminals>
|
||||
<terminal id="0" orientation="0" x="0" y="-16"/>
|
||||
<terminal id="1" orientation="2" x="0" y="16"/>
|
||||
</terminals>
|
||||
<inputs/>
|
||||
<links_uuids>
|
||||
<link_uuid uuid="{8f8831dc-ebbe-48e0-8916-c014c9dd31eb}"/>
|
||||
</links_uuids>
|
||||
<elementInformations>
|
||||
<elementInformation name="label" show="1">K1</elementInformation>
|
||||
</elementInformations>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{28faae8b-3f47-4578-9708-eee8500d3507}" x="2" y="11.33">
|
||||
<text>A2</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{bcabc653-1e9a-43d2-addd-722e2b0bff35}" x="2" y="-24.67">
|
||||
<text>A1</text>
|
||||
</dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{93524f29-abab-4d7a-950c-fe6bfa385fa9}" x="25" y="-9.17">
|
||||
<text>K1</text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups/>
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="S" type="embed://import/10_electric/10_allpole/310_relays_contactors_contacts/02_contacts_cross_referencing/01_auxiliary_contacts/con_simple.elmt" uuid="{8f8831dc-ebbe-48e0-8916-c014c9dd31eb}" x="400" y="200" z="10">
|
||||
<terminals>
|
||||
<terminal id="2" orientation="0" x="0" y="-16"/>
|
||||
<terminal id="3" orientation="2" x="0" y="16"/>
|
||||
</terminals>
|
||||
<inputs/>
|
||||
<links_uuids>
|
||||
<link_uuid uuid="{ee860e7c-eaf3-404a-9c71-5ae4b296bdde}"/>
|
||||
</links_uuids>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{e6230b67-df36-4e2b-96f9-5e1b6ff44cf9}" x="2.75" y="-9.5">
|
||||
<text>K1</text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups/>
|
||||
</element>
|
||||
</elements>
|
||||
<conductors>
|
||||
<conductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{ee860e7c-eaf3-404a-9c71-5ae4b296bdde}" element1_label="K1" element1_linked="" element1_name="Coil" element2="{8f8831dc-ebbe-48e0-8916-c014c9dd31eb}" element2_label="" element2_linked="K1" element2_name="Simple contact" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" terminal1="{8d0fa333-2d98-4a75-8a4e-21c81cce7ec3}" terminal2="{b3b3dc63-1c98-409b-ab79-3dbaea3556cb}" terminalname1="A1" terminalname2="" text_color="#000000" type="multi" uuid="{965e3155-9b9d-41f3-af7d-03ecf52ddf9a}" vertical-alignment="AlignRight" vertirotatetext="270" x="0" y="0">
|
||||
<sequentialNumbers/>
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{ee860e7c-eaf3-404a-9c71-5ae4b296bdde}" element1_label="K1" element1_linked="" element1_name="Coil" element2="{8f8831dc-ebbe-48e0-8916-c014c9dd31eb}" element2_label="" element2_linked="K1" element2_name="Simple contact" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" terminal1="{c5376fd7-bdf1-4c10-985a-0d7d5f52c8f9}" terminal2="{b9683546-f0a2-4386-b114-ef1d46b2df3d}" terminalname1="A2" terminalname2="" text_color="#000000" type="multi" uuid="{5ccf65d5-ed6a-45f3-b0bc-d041c36cebca}" vertical-alignment="AlignRight" vertirotatetext="270" x="0" y="0">
|
||||
<sequentialNumbers/>
|
||||
</conductor>
|
||||
</conductors>
|
||||
<inputs>
|
||||
<input font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" rotation="0" text="<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css">
p, li { white-space: pre-wrap; }
hr { height: 1px; border-width: 0; }
li.unchecked::marker { content: "\2610"; }
li.checked::marker { content: "\2612"; }
</style></head><body style=" font-family:'Liberation Sans'; font-size:9pt; font-weight:400; font-style:normal;">
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">FreeAlpha FreeBeta FreeGamma FreeDelta FreeEpsilon</p></body></html>" text_width="70" uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7a03}" x="100" y="100"/>
|
||||
<input font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" rotation="0" text="<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css">
p, li { white-space: pre-wrap; }
hr { height: 1px; border-width: 0; }
li.unchecked::marker { content: "\2610"; }
li.checked::marker { content: "\2612"; }
</style></head><body style=" font-family:'Liberation Sans'; font-size:9pt; font-weight:400; font-style:normal;">
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">OpenAlpha OpenBeta OpenGamma OpenDelta OpenEpsilon</p></body></html>" uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7a04}" x="100" y="400"/>
|
||||
<input font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" rotation="0" text="<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css">
p, li { white-space: pre-wrap; }
hr { height: 1px; border-width: 0; }
li.unchecked::marker { content: "\2610"; }
li.checked::marker { content: "\2612"; }
</style></head><body style=" font-family:'Liberation Sans'; font-size:9pt; font-weight:400; font-style:normal;">
<p align="center" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">CentAlpha CentBeta CentGamma</p></body></html>" text_width="120" uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7a08}" x="400" y="100"/>
|
||||
</inputs>
|
||||
</diagram>
|
||||
<collection>
|
||||
<category name="import">
|
||||
<names>
|
||||
<name lang="en">Imported elements</name>
|
||||
</names>
|
||||
<category name="10_electric">
|
||||
<names>
|
||||
<name lang="ar">كهربائي</name>
|
||||
<name lang="ca">Elèctric</name>
|
||||
<name lang="cs">Elektrotechnika</name>
|
||||
<name lang="da">Elektrisk</name>
|
||||
<name lang="de">Elektrik</name>
|
||||
<name lang="el">Ηλεκτρικά</name>
|
||||
<name lang="en">Electric</name>
|
||||
<name lang="es">Eléctrica</name>
|
||||
<name lang="fi">Sähkö</name>
|
||||
<name lang="fr">Electrique</name>
|
||||
<name lang="hr">Električno</name>
|
||||
<name lang="hu">Elektromos</name>
|
||||
<name lang="it">Elettrica</name>
|
||||
<name lang="ja">電気</name>
|
||||
<name lang="ko">전기</name>
|
||||
<name lang="mn">Цахилгаан</name>
|
||||
<name lang="nb">Elektrisk</name>
|
||||
<name lang="nl">Elektrotechniek</name>
|
||||
<name lang="nl_BE">Elektrisch</name>
|
||||
<name lang="pl">Elektrotechnika</name>
|
||||
<name lang="pt">Elétrico</name>
|
||||
<name lang="pt_BR">Elétrica</name>
|
||||
<name lang="ro">Electric</name>
|
||||
<name lang="ru">Электротехника</name>
|
||||
<name lang="sk">Elektrické</name>
|
||||
<name lang="sl">Elektrika</name>
|
||||
<name lang="sr">Електрика</name>
|
||||
<name lang="sv">El</name>
|
||||
<name lang="tr">Elektrik</name>
|
||||
<name lang="uk">Електрика</name>
|
||||
<name lang="zh">10_电气</name>
|
||||
<name lang="zn">电气工程</name>
|
||||
</names>
|
||||
<category name="10_allpole">
|
||||
<names>
|
||||
<name lang="ar">متعدد الأقطاب</name>
|
||||
<name lang="ca">Multifilar</name>
|
||||
<name lang="cs">Vícežilový</name>
|
||||
<name lang="da">Flere ledere</name>
|
||||
<name lang="de">Allpolig</name>
|
||||
<name lang="el">Πολυγραμμικό</name>
|
||||
<name lang="en">All-pole</name>
|
||||
<name lang="es">Multifilar</name>
|
||||
<name lang="fi">Yleisnapa</name>
|
||||
<name lang="fr">Multifilaire</name>
|
||||
<name lang="hr">Univerzalni</name>
|
||||
<name lang="hu">Többvonalas szimbólumok</name>
|
||||
<name lang="it">Multifilare</name>
|
||||
<name lang="ja">複線</name>
|
||||
<name lang="ko">다-극</name>
|
||||
<name lang="mn">Бүх туйл</name>
|
||||
<name lang="nb">Alle poler</name>
|
||||
<name lang="nl">Veel polig</name>
|
||||
<name lang="nl_BE">All-pole</name>
|
||||
<name lang="pl">Schematy wieloliniowe</name>
|
||||
<name lang="pt">Multipolar</name>
|
||||
<name lang="pt_BR">Multifilar</name>
|
||||
<name lang="ro">Bipolar</name>
|
||||
<name lang="ru">Многополюсные</name>
|
||||
<name lang="sk">Všepólové</name>
|
||||
<name lang="sl">Vsi poli</name>
|
||||
<name lang="sr">Свe-полни</name>
|
||||
<name lang="sv">Allpolig</name>
|
||||
<name lang="tr">Çok kutuplu</name>
|
||||
<name lang="uk">Багатополюсні</name>
|
||||
<name lang="zh">10_全极</name>
|
||||
</names>
|
||||
<category name="310_relays_contactors_contacts">
|
||||
<names>
|
||||
<name lang="ar">المرحّلات والموصّلات وجهات الاتصال</name>
|
||||
<name lang="ca">Relés, contactors i contactes</name>
|
||||
<name lang="cs">Relé, stykače a kontakty</name>
|
||||
<name lang="da">Relæer, kontaktorer og kontakter</name>
|
||||
<name lang="de">Relais, Schütze und Kontakte</name>
|
||||
<name lang="el">Ηλεκτρονόμοι και επαφές</name>
|
||||
<name lang="en">Relays, contactors and contacts</name>
|
||||
<name lang="es">Relevadores, contactores y contactos</name>
|
||||
<name lang="fi">Releet, kontaktorit ja koskettimet</name>
|
||||
<name lang="fr">Relais, contacteurs et contacts</name>
|
||||
<name lang="hr">Releji, kontaktori i kontakti</name>
|
||||
<name lang="hu">Relék, kontaktorok és érintkezők</name>
|
||||
<name lang="it">Relè, contattori e contatti</name>
|
||||
<name lang="ja">継電器、接触器、接点</name>
|
||||
<name lang="ko">릴레이, 커넥터및 접점</name>
|
||||
<name lang="mn">Реле, контактор болон контакт</name>
|
||||
<name lang="nb">Reléer, kontaktorere og kontakter</name>
|
||||
<name lang="nl">Relais, contactoren en contacten</name>
|
||||
<name lang="nl_BE">Relais, contacteurs en contacten</name>
|
||||
<name lang="pl">Przekaźniki i styczniki</name>
|
||||
<name lang="pt">Relés, contatores e contactos</name>
|
||||
<name lang="pt_BR">Relés, Contatores e contatos</name>
|
||||
<name lang="ro">Relee, contactoare și contacte</name>
|
||||
<name lang="ru">Реле, пускатели и контакты</name>
|
||||
<name lang="sk">Relé, stykače a kontakty</name>
|
||||
<name lang="sl">Releji, kontaktorji in kontakti</name>
|
||||
<name lang="sr">Релеји, контактори и контакти</name>
|
||||
<name lang="sv">Reläer, kontaktorer och kontakter</name>
|
||||
<name lang="tr">Röleler, kontaktörler ve kontaklar</name>
|
||||
<name lang="uk">Реле, контактори та контакти</name>
|
||||
<name lang="zh">继电器、接触器与触点</name>
|
||||
</names>
|
||||
<category name="01_coils">
|
||||
<names>
|
||||
<name lang="ar">لفه</name>
|
||||
<name lang="ca">Bobines</name>
|
||||
<name lang="cs">cívka</name>
|
||||
<name lang="da">spole</name>
|
||||
<name lang="de">Spule</name>
|
||||
<name lang="el">σπείρα</name>
|
||||
<name lang="en">coil</name>
|
||||
<name lang="es">bobina</name>
|
||||
<name lang="fi">Käämi</name>
|
||||
<name lang="fr">bobine</name>
|
||||
<name lang="hr">zavojnica</name>
|
||||
<name lang="hu">tekercs</name>
|
||||
<name lang="it">bobina</name>
|
||||
<name lang="ja">コイル</name>
|
||||
<name lang="ko">코일</name>
|
||||
<name lang="mn">ороомог</name>
|
||||
<name lang="nb">Spole</name>
|
||||
<name lang="nl">spoel</name>
|
||||
<name lang="nl_BE">Spoel</name>
|
||||
<name lang="pl">cewka</name>
|
||||
<name lang="pt">bobina</name>
|
||||
<name lang="pt_BR">bobina</name>
|
||||
<name lang="ro">bobina</name>
|
||||
<name lang="ru">катушка</name>
|
||||
<name lang="sk">Cievka</name>
|
||||
<name lang="sl">tuljava</name>
|
||||
<name lang="sr">калем</name>
|
||||
<name lang="sv">spole</name>
|
||||
<name lang="tr">bobin</name>
|
||||
<name lang="uk">котушка</name>
|
||||
<name lang="zh">线圈</name>
|
||||
</names>
|
||||
<element name="bobine3.elmt">
|
||||
<definition height="60" hotspot_x="20" hotspot_y="32" link_type="master" type="element" version="0.100.0" width="40">
|
||||
<uuid uuid="{793302b1-e96a-f7f8-70bc-dec53eeaab5b}"/>
|
||||
<names>
|
||||
<name lang="ar">ملف</name>
|
||||
<name lang="ca">Bobina</name>
|
||||
<name lang="cs">Cívka relé</name>
|
||||
<name lang="da">Relæ</name>
|
||||
<name lang="de">Spule</name>
|
||||
<name lang="el">Πηνίο</name>
|
||||
<name lang="en">Coil</name>
|
||||
<name lang="es">Bobina</name>
|
||||
<name lang="fi">Käämi</name>
|
||||
<name lang="fr">Bobine</name>
|
||||
<name lang="hr">Zavojnica</name>
|
||||
<name lang="hu">Tekercs</name>
|
||||
<name lang="it">Bobina</name>
|
||||
<name lang="ja">コイル</name>
|
||||
<name lang="ko">코일</name>
|
||||
<name lang="mn">Ороомог</name>
|
||||
<name lang="nb">Spole</name>
|
||||
<name lang="nl">Spoel</name>
|
||||
<name lang="nl_BE">Relais spoel (algemeen)</name>
|
||||
<name lang="pl">Cewka</name>
|
||||
<name lang="pt">Bobina</name>
|
||||
<name lang="pt_BR">Bobina</name>
|
||||
<name lang="ro">Bobina</name>
|
||||
<name lang="ru">Обмотка</name>
|
||||
<name lang="sk">Cievka</name>
|
||||
<name lang="sl">Tuljava</name>
|
||||
<name lang="sr">Каlem</name>
|
||||
<name lang="sv">Spole</name>
|
||||
<name lang="tr">Bobin</name>
|
||||
<name lang="uk">Котушка</name>
|
||||
<name lang="zh">三线圈</name>
|
||||
</names>
|
||||
<kindInformations>
|
||||
<kindInformation name="type">coil</kindInformation>
|
||||
</kindInformations>
|
||||
<informations>Author: The QElectroTech team
|
||||
License: see http://qelectrotech.org/wiki/doc/elements_license</informations>
|
||||
<description>
|
||||
<rect antialias="false" height="16" rx="0" ry="0" style="line-style:normal;line-weight:normal;filling:none;color:black" width="28" x="-14" y="-8"/>
|
||||
<line antialias="false" end1="none" end2="none" length1="1.5" length2="1.5" style="line-style:normal;line-weight:normal;filling:none;color:black" x1="0" x2="0" y1="-20" y2="-8"/>
|
||||
<dynamic_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" text_from="UserText" text_width="-1" uuid="{0a58bdda-9173-48ba-acaa-35f2dc00c4bb}" x="2" y="11.33" z="3">
|
||||
<text>A2</text>
|
||||
</dynamic_text>
|
||||
<dynamic_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" text_from="UserText" text_width="-1" uuid="{7e98ddfe-fde0-4715-a926-9f60cf750078}" x="2" y="-24.67" z="4">
|
||||
<text>A1</text>
|
||||
</dynamic_text>
|
||||
<line antialias="false" end1="none" end2="none" length1="1.5" length2="1.5" style="line-style:normal;line-weight:normal;filling:none;color:black" x1="0" x2="0" y1="8" y2="20"/>
|
||||
<dynamic_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" text_from="ElementInfo" text_width="-1" uuid="{3ef105d2-add6-4c4b-a4cd-120c40096202}" x="25" y="-9.17" z="6">
|
||||
<text/>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_text>
|
||||
<terminal name="A1" orientation="n" type="Generic" uuid="{8d0fa333-2d98-4a75-8a4e-21c81cce7ec3}" x="0" y="-20"/>
|
||||
<terminal name="A2" orientation="s" type="Generic" uuid="{c5376fd7-bdf1-4c10-985a-0d7d5f52c8f9}" x="0" y="20"/>
|
||||
</description>
|
||||
</definition>
|
||||
</element>
|
||||
</category>
|
||||
<category name="02_contacts_cross_referencing">
|
||||
<names>
|
||||
<name lang="ar">تماسات مع إحالات</name>
|
||||
<name lang="ca">Contactes amb referències creuades</name>
|
||||
<name lang="cs">Kontakty s křížovými odkazy</name>
|
||||
<name lang="da">Kontaker med krydsreference</name>
|
||||
<name lang="de">Kontakte mit Querverweis</name>
|
||||
<name lang="el">Επαφές με παραπομπές</name>
|
||||
<name lang="en">Contacts with cross referencing</name>
|
||||
<name lang="es">Contactos con referencia cruzada</name>
|
||||
<name lang="fi">Kosketimet lehtiviittauksella</name>
|
||||
<name lang="fr">Contacts avec référence croisée</name>
|
||||
<name lang="hr">Kontakti s unakrsnim referenciranjem</name>
|
||||
<name lang="hu">Érintkezők kereszthivatkozásokkal</name>
|
||||
<name lang="it">Contatti con riferimenti incrociati</name>
|
||||
<name lang="ja">相互参照付き接点</name>
|
||||
<name lang="ko">교차 참조 접점</name>
|
||||
<name lang="mn">Харилцан холбоос бүхий контакт</name>
|
||||
<name lang="nb">Kontakter med kryssreferanse</name>
|
||||
<name lang="nl">Contacten met kruisreferentie</name>
|
||||
<name lang="nl_BE">Contacten met kruisverwijzing</name>
|
||||
<name lang="pl">Zestyki (automatyczne oznaczenia)</name>
|
||||
<name lang="pt">Contactos com renvoi de folio</name>
|
||||
<name lang="pt_BR">Contatos com referencia cruzada</name>
|
||||
<name lang="ro">Contacte cu renvoi de folio</name>
|
||||
<name lang="ru">Контакты с перекрестными ссылками</name>
|
||||
<name lang="sk">Kontakty s krížovým odkazom</name>
|
||||
<name lang="sl">Kontakti s sklici</name>
|
||||
<name lang="sr">Контакти са унакрсним референцирањем</name>
|
||||
<name lang="sv">Kontakter med bladhänvisning</name>
|
||||
<name lang="tr">Çapraz referanslı kontaklar</name>
|
||||
<name lang="uk">Контакти з перехресними посиланнями</name>
|
||||
<name lang="zh">触点交叉引用</name>
|
||||
</names>
|
||||
<category name="01_auxiliary_contacts">
|
||||
<names>
|
||||
<name lang="ar">مساعد</name>
|
||||
<name lang="ca">Auxiliars</name>
|
||||
<name lang="cs">Pomocné kontakty</name>
|
||||
<name lang="da">Hjælpe kontakter</name>
|
||||
<name lang="de">Hilfsglieder</name>
|
||||
<name lang="el">Βοηθητικές επαφές</name>
|
||||
<name lang="en">Auxiliary</name>
|
||||
<name lang="es">Contactos auxiliares</name>
|
||||
<name lang="fi">Apupiiri</name>
|
||||
<name lang="fr">Auxilliaires</name>
|
||||
<name lang="hr">Pomoćni</name>
|
||||
<name lang="hu">Segéd</name>
|
||||
<name lang="it">Contatti ausiliari</name>
|
||||
<name lang="ja">補助</name>
|
||||
<name lang="ko">보조 접점</name>
|
||||
<name lang="mn">Туслах</name>
|
||||
<name lang="nb">Hjelpe</name>
|
||||
<name lang="nl">Hulpcontacten</name>
|
||||
<name lang="nl_BE">Hulpcontacten</name>
|
||||
<name lang="pl">Zestyki pomocnicze</name>
|
||||
<name lang="pt">Auxiliar</name>
|
||||
<name lang="pt_BR">Auxiliares</name>
|
||||
<name lang="ro">Auxiliare</name>
|
||||
<name lang="ru">Внешние</name>
|
||||
<name lang="sk">Pomocné</name>
|
||||
<name lang="sl">Pomožni</name>
|
||||
<name lang="sr">Помоћни</name>
|
||||
<name lang="sv">Hjälp</name>
|
||||
<name lang="tr">Yardımcı</name>
|
||||
<name lang="uk">Допоміжні</name>
|
||||
<name lang="zh">辅助触点</name>
|
||||
</names>
|
||||
<element name="con_simple.elmt">
|
||||
<definition height="60" hotspot_x="10" hotspot_y="30" link_type="slave" type="element" version="0.100.0" width="20">
|
||||
<uuid uuid="{9d34fad7-8a48-2a3e-df68-74d26a8f1012}"/>
|
||||
<names>
|
||||
<name lang="ar">مُلامس بسيط (NO)</name>
|
||||
<name lang="ca">Contacte senzill</name>
|
||||
<name lang="cs">Spínací kontakt</name>
|
||||
<name lang="da">Enkel kontakt</name>
|
||||
<name lang="de">Schliesser</name>
|
||||
<name lang="el">Απλή επαφή, ανοικτή</name>
|
||||
<name lang="en">Simple contact</name>
|
||||
<name lang="es">Contactor simple</name>
|
||||
<name lang="fi">Yksinkertainen kosketin</name>
|
||||
<name lang="fr">Contact simple</name>
|
||||
<name lang="hr">Jednostavni kontakt</name>
|
||||
<name lang="hu">Egyszerű érintkező</name>
|
||||
<name lang="it">Contatto semplice (NO)</name>
|
||||
<name lang="ja">単純接点</name>
|
||||
<name lang="ko">단순 접점</name>
|
||||
<name lang="mn">Энгийн контакт</name>
|
||||
<name lang="nb">Enkel kontakt</name>
|
||||
<name lang="nl">contact enkel</name>
|
||||
<name lang="nl_BE">Eenvoudig contact</name>
|
||||
<name lang="pl">Zestyk zwierny</name>
|
||||
<name lang="pt">Contacto simples (NO)</name>
|
||||
<name lang="pt_BR">Contato simples</name>
|
||||
<name lang="ro">Contact simplu</name>
|
||||
<name lang="ru">Проктой контакт (НР)</name>
|
||||
<name lang="sk">Jednoduchý kontakt</name>
|
||||
<name lang="sl">Enostavni kontakt</name>
|
||||
<name lang="sr">Прости контакт</name>
|
||||
<name lang="sv">Enkelkontakt</name>
|
||||
<name lang="tr">Basit kontak</name>
|
||||
<name lang="uk">Простий контакт</name>
|
||||
<name lang="zh">简单连接器</name>
|
||||
</names>
|
||||
<kindInformations>
|
||||
<kindInformation name="type">simple</kindInformation>
|
||||
<kindInformation name="state">NO</kindInformation>
|
||||
<kindInformation name="number">1</kindInformation>
|
||||
</kindInformations>
|
||||
<informations>Author: The QElectroTech team
|
||||
License: see http://qelectrotech.org/wiki/doc/elements_license</informations>
|
||||
<description>
|
||||
<polygon antialias="true" closed="false" style="line-style:normal;line-weight:normal;filling:none;color:black" x1="-5" x2="0" x3="0" y1="-10" y2="10" y3="20"/>
|
||||
<dynamic_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" text_from="ElementInfo" text_width="-1" uuid="{a4a5addc-2580-413c-828c-6e93cfe494ef}" x="2.75" y="-9.5" z="2">
|
||||
<text/>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_text>
|
||||
<line antialias="false" end1="none" end2="none" length1="1.5" length2="1.5" style="line-style:normal;line-weight:normal;filling:none;color:black" x1="0" x2="0" y1="-20" y2="-10"/>
|
||||
<terminal name="" orientation="n" type="Generic" uuid="{b3b3dc63-1c98-409b-ab79-3dbaea3556cb}" x="0" y="-20"/>
|
||||
<terminal name="" orientation="s" type="Generic" uuid="{b9683546-f0a2-4386-b114-ef1d46b2df3d}" x="0" y="20"/>
|
||||
</description>
|
||||
</definition>
|
||||
</element>
|
||||
</category>
|
||||
</category>
|
||||
</category>
|
||||
</category>
|
||||
</category>
|
||||
</category>
|
||||
</collection>
|
||||
</project>
|
||||
@@ -155,10 +155,12 @@ class tst_movedterminals : public QObject
|
||||
QTemporaryDir m_dir;
|
||||
int m_run = 0;
|
||||
|
||||
struct Result { int wires = -1; QString log; QStringList pairs; };
|
||||
// wires: those joined to their terminals; saved: those in the saved
|
||||
// file, which keeps a wire it could not join as it was read (F128)
|
||||
struct Result { int wires = -1; int saved = -1; QString log; QStringList pairs; };
|
||||
|
||||
// Open and save the project in a sandbox of its own (so a running
|
||||
// QElectroTech cannot answer instead): the wires that survive.
|
||||
// QElectroTech cannot answer instead): the wires that are joined.
|
||||
Result resave(const QString &xml)
|
||||
{
|
||||
const QString in = m_dir.filePath(QStringLiteral("in%1.qet").arg(m_run));
|
||||
@@ -184,8 +186,13 @@ class tst_movedterminals : public QObject
|
||||
QDomDocument doc;
|
||||
QFile saved(out);
|
||||
if (!saved.open(QIODevice::ReadOnly) || !doc.setContent(&saved)) return {};
|
||||
r.wires = doc.elementsByTagName(QStringLiteral("conductor")).size();
|
||||
for (int i = 0; i < r.wires; ++i) {
|
||||
r.saved = doc.elementsByTagName(QStringLiteral("conductor")).size();
|
||||
int not_joined = 0;
|
||||
static const QRegularExpression not_loaded(QStringLiteral("(\\d+) wire\\(s\\) not loaded"));
|
||||
for (auto it = not_loaded.globalMatch(r.log); it.hasNext() ; )
|
||||
not_joined += it.next().captured(1).toInt();
|
||||
r.wires = r.saved - not_joined;
|
||||
for (int i = 0; i < r.saved; ++i) {
|
||||
const QDomElement c = doc.elementsByTagName(QStringLiteral("conductor")).at(i).toElement();
|
||||
r.pairs << c.attribute(QStringLiteral("terminal1")) + QLatin1Char('>') + c.attribute(QStringLiteral("terminal2"));
|
||||
}
|
||||
@@ -236,8 +243,12 @@ private slots:
|
||||
|
||||
void movedSixUnitsDoesNot()
|
||||
{
|
||||
QCOMPARE(resave(project({16}, byId())).wires, 0);
|
||||
QCOMPARE(resave(project({16}, byUuid(10))).wires, 0);
|
||||
const Result legacy = resave(project({16}, byId()));
|
||||
QCOMPARE(legacy.wires, 0);
|
||||
QCOMPARE(legacy.saved, 1);
|
||||
const Result uuid = resave(project({16}, byUuid(10)));
|
||||
QCOMPARE(uuid.wires, 0);
|
||||
QCOMPARE(uuid.saved, 1);
|
||||
}
|
||||
|
||||
// Two live terminals in reach of one saved one: no way to tell which.
|
||||
@@ -245,6 +256,7 @@ private slots:
|
||||
{
|
||||
const Result legacy = resave(project({13, 14}, byId()));
|
||||
QCOMPARE(legacy.wires, 0);
|
||||
QCOMPARE(legacy.saved, 1);
|
||||
QVERIFY(!said(legacy));
|
||||
QCOMPARE(resave(project({13, 14}, byUuid(10))).wires, 0);
|
||||
}
|
||||
@@ -296,8 +308,10 @@ private slots:
|
||||
// one, and no single shift maps both.
|
||||
void ambiguousStillRefuses()
|
||||
{
|
||||
QCOMPARE(resave(multiProject({{'n', 0, -10}, {'n', 4, -10}},
|
||||
{{'n', 2, -10}, {'n', 6, -10}, {'n', -2, -10}})).wires, 0);
|
||||
const Result r = resave(multiProject({{'n', 0, -10}, {'n', 4, -10}},
|
||||
{{'n', 2, -10}, {'n', 6, -10}, {'n', -2, -10}}));
|
||||
QCOMPARE(r.wires, 0);
|
||||
QCOMPARE(r.saved, 2);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -77,9 +77,10 @@ class tst_qetcontainer : public QObject
|
||||
{
|
||||
QDomDocument copy = document.cloneNode(true).toDocument();
|
||||
const bool own_uuids = QetContainer::ownUuids(copy.documentElement());
|
||||
const QSet<QString> defined = QetContainer::embeddedDefinitions(copy.documentElement());
|
||||
for (QDomElement diagram = copy.documentElement().firstChildElement(QStringLiteral("diagram"));
|
||||
!diagram.isNull() ; diagram = diagram.nextSiblingElement(QStringLiteral("diagram")))
|
||||
QetContainer::dropComputed(diagram, own_uuids);
|
||||
QetContainer::dropComputed(diagram, own_uuids, defined);
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
@@ -802,6 +802,101 @@ private slots:
|
||||
QStringLiteral("<property [^>]*name=\"accents\"[^>]*>Armoire façade été</property>"))),
|
||||
"the accented value changed");
|
||||
}
|
||||
|
||||
// What a folio cannot load is saved back as it was read (F127, F128).
|
||||
// fixtures/coil_contact_wired.qet: a coil K1 and its contact, linked,
|
||||
// two wires between them. Each row damages it as an uploaded project
|
||||
// can be: the coil's definition missing from the file, or the
|
||||
// contact's definition redrawn (terminals moved, other uuids). A save
|
||||
// keeps both symbols, both wires and both links, a second save changes
|
||||
// nothing, and once the coil's definition is back the save is the
|
||||
// undamaged project's again.
|
||||
void unloadableItemsKept_data()
|
||||
{
|
||||
QTest::addColumn<QString>("damage");
|
||||
QTest::newRow("definition missing") << QStringLiteral("coil");
|
||||
QTest::newRow("terminals redrawn") << QStringLiteral("contact");
|
||||
}
|
||||
void unloadableItemsKept()
|
||||
{
|
||||
QFETCH(QString, damage);
|
||||
const QString fixture = QFINDTESTDATA("fixtures/coil_contact_wired.qet");
|
||||
const QString whole = resave(fixture);
|
||||
QVERIFY2(!whole.isEmpty(), "--resave of the fixture failed");
|
||||
const QString xml = QString::fromUtf8(read(whole));
|
||||
static const QRegularExpression coil_definition(QStringLiteral(
|
||||
"<element name=\"bobine3\\.elmt\">.*?</element>\\s*"),
|
||||
QRegularExpression::DotMatchesEverythingOption);
|
||||
const QString coil = coil_definition.match(xml).captured(0);
|
||||
QVERIFY(!coil.isEmpty());
|
||||
QString damaged = xml;
|
||||
if (damage == QLatin1String("coil")) {
|
||||
damaged.remove(coil);
|
||||
} else {
|
||||
//The contact's terminals 40 further right, with new uuids
|
||||
static const QRegularExpression contact_definition(QStringLiteral(
|
||||
"<element name=\"con_simple\\.elmt\">.*?</element>"),
|
||||
QRegularExpression::DotMatchesEverythingOption);
|
||||
QString contact = contact_definition.match(xml).captured(0);
|
||||
QVERIFY(!contact.isEmpty());
|
||||
const QString before = contact;
|
||||
static const QRegularExpression terminal(QStringLiteral(
|
||||
"(<terminal [^>]*uuid=\"\\{)[0-9a-f]{8}([^>]*\\bx=\")(-?[0-9.]+)"));
|
||||
int n = 0;
|
||||
for (auto it = terminal.globalMatch(before); it.hasNext() ; ++n) {
|
||||
const auto m = it.next();
|
||||
contact.replace(m.captured(0), m.captured(1) + QStringLiteral("%1").arg(n, 8, 10, QLatin1Char('0'))
|
||||
+ m.captured(2) + QString::number(m.captured(3).toDouble() + 40));
|
||||
}
|
||||
QCOMPARE(n, 2);
|
||||
damaged.replace(before, contact);
|
||||
}
|
||||
const QString damaged_path = m_dir.filePath(QStringLiteral("damaged%1.qet").arg(m_run));
|
||||
QFile out(damaged_path);
|
||||
QVERIFY(out.open(QIODevice::WriteOnly));
|
||||
out.write(damaged.toUtf8());
|
||||
out.close();
|
||||
|
||||
auto count = [](const QString &text, const char *pattern) {
|
||||
return int(text.count(QRegularExpression(QString::fromLatin1(pattern))));
|
||||
};
|
||||
const QString first = resave(damaged_path);
|
||||
QVERIFY2(!first.isEmpty(), "--resave of the damaged project failed");
|
||||
const QString saved = QString::fromUtf8(read(first));
|
||||
QCOMPARE(count(saved, "<element [^>]*\\btype="), 2);
|
||||
QCOMPARE(count(saved, "<conductor "), 2);
|
||||
QCOMPARE(count(saved, "<link_uuid "), 2);
|
||||
const QString second = resave(first);
|
||||
QVERIFY2(!second.isEmpty(), "second --resave failed");
|
||||
QVERIFY2(read(second) == read(first), "the second save changed the file");
|
||||
|
||||
if (damage != QLatin1String("coil")) return;
|
||||
//The definition back: the coil and its wires are built again
|
||||
static const QRegularExpression collection_category(QStringLiteral(
|
||||
"(<category name=\"01_coils\">\\s*<names>.*?</names>\\s*)"),
|
||||
QRegularExpression::DotMatchesEverythingOption);
|
||||
QString mended = saved;
|
||||
QVERIFY(collection_category.match(mended).hasMatch());
|
||||
mended.replace(collection_category, QStringLiteral("\\1") + coil);
|
||||
const QString mended_path = m_dir.filePath(QStringLiteral("mended%1.qet").arg(m_run));
|
||||
QFile mend(mended_path);
|
||||
QVERIFY(mend.open(QIODevice::WriteOnly));
|
||||
mend.write(mended.toUtf8());
|
||||
mend.close();
|
||||
const QString back = resave(mended_path);
|
||||
QVERIFY2(!back.isEmpty(), "--resave of the mended project failed");
|
||||
auto items = [](const QString &text, const char *tag) {
|
||||
QStringList found;
|
||||
static const QRegularExpression noise(QStringLiteral(" (element[12]_(label|linked|name)|terminalname[12])=\"[^\"]*\""));
|
||||
auto it = QRegularExpression(QStringLiteral("<%1 [^>]*>").arg(QLatin1String(tag))).globalMatch(text);
|
||||
while (it.hasNext()) found << it.next().captured(0).remove(noise);
|
||||
found.sort();
|
||||
return found;
|
||||
};
|
||||
const QString again = QString::fromUtf8(read(back));
|
||||
QCOMPARE(items(again, "element"), items(xml, "element"));
|
||||
QCOMPARE(items(again, "conductor"), items(xml, "conductor"));
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_resaveunchanged)
|
||||
|
||||
Reference in New Issue
Block a user