mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-21 22:04:13 +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,
|
||||
|
||||
Reference in New Issue
Block a user