mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-18 03:54:14 +02:00
Merge pull request #1514 from ispyisail/fix/lazy-table-chain-folio-removed
Fix a table losing its query when a folio is removed with lazy folios
This commit is contained in:
@@ -752,11 +752,24 @@ void QETProject::buildFolios(const QList<Diagram *> &folios) const
|
||||
auto *self = const_cast<QETProject *>(this);
|
||||
const ProjectState state = m_state;
|
||||
self->m_state = ProjectParsingRunning;
|
||||
//Batch the database's signals as a paste does
|
||||
//(DiagramEventAddPaste): every symbol and wire built would emit
|
||||
//dataBaseUpdated(), and every table already built would measure all
|
||||
//its rows again each time -- minutes for a save building 40 folios
|
||||
//with 10 parts-list folios built. The rows are still written as each
|
||||
//item is built: only the signal waits, and is sent once at the end
|
||||
//(updateDB() would fill the whole database again, every time).
|
||||
const bool signals_were_blocked = self->m_data_base.signalsBlocked();
|
||||
self->m_data_base.blockSignals(true);
|
||||
QList<Diagram *> built;
|
||||
for (Diagram *d : folios)
|
||||
if (d->buildDeferred()) built << d;
|
||||
for (Diagram *d : std::as_const(built))
|
||||
self->folioLoaded(d);
|
||||
self->m_data_base.blockSignals(signals_were_blocked);
|
||||
if (!signals_were_blocked && !built.isEmpty()) {
|
||||
emit self->m_data_base.dataBaseUpdated();
|
||||
}
|
||||
//A symbol built before still keeping the links it was saved with
|
||||
//(a partner on a folio not built): its initLink() links those just
|
||||
//built, puts its partners back in their saved order and drops what
|
||||
@@ -3175,6 +3188,23 @@ void QETProject::buildFoliosHoldingTables()
|
||||
buildFolios(to_build);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETProject::folioHoldsTablesOrSymbols
|
||||
@return true if @p folio, built or not, has a drawn table or a symbol
|
||||
*/
|
||||
bool QETProject::folioHoldsTablesOrSymbols(Diagram *folio) const
|
||||
{
|
||||
if (!folio->isLoaded()) {
|
||||
const QDomElement xml = folio->deferredXml();
|
||||
return !xml.elementsByTagName(QetGraphicsTableItem::xmlTagName()).isEmpty()
|
||||
|| !xml.elementsByTagName(QStringLiteral("element")).isEmpty();
|
||||
}
|
||||
for (QGraphicsItem *item : folio->items())
|
||||
if (item->type() == QetGraphicsTableItem::Type || item->type() == Element::Type)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETProject::unbuiltFolioHasTables
|
||||
@return true if @p folio, not built yet (lazy folios), has a drawn
|
||||
@@ -4274,6 +4304,20 @@ void QETProject::detachDiagram(Diagram *diagram)
|
||||
disconnect(diagram, &Diagram::usedTitleBlockTemplateChanged,
|
||||
this, &QETProject::usedTitleBlockTemplateChanged);
|
||||
|
||||
//A folio that holds a table or a symbol is removed: tables on folios
|
||||
//not built yet (lazy folios) are built while it is still in the
|
||||
//project, as they are with every folio built (F121). A table split
|
||||
//over several folios keeps its query in its first part, and the next
|
||||
//part takes it over only if that part was built
|
||||
//(QetGraphicsTableItem::toXml()); built later, from the file, it
|
||||
//names a part that is gone and saves no query. A built table also
|
||||
//follows the project's database, so a parts list that showed this
|
||||
//folio's symbols is resized like every other. A folio with neither
|
||||
//builds no other.
|
||||
if (folioHoldsTablesOrSymbols(diagram)) {
|
||||
buildFoliosHoldingTables();
|
||||
}
|
||||
|
||||
m_diagrams_list.removeAll(diagram);
|
||||
updateDiagramsFolioData();
|
||||
emit diagramRemoved(this, diagram);
|
||||
|
||||
@@ -354,6 +354,7 @@ class QETProject : public QObject
|
||||
QList <ElementsLocation> unusedElements() const;
|
||||
QList<ElementsLocation> unbuiltFolioElements() const;
|
||||
bool unbuiltFolioHasTables(Diagram *folio) const;
|
||||
bool folioHoldsTablesOrSymbols(Diagram *folio) const;
|
||||
bool usesTitleBlockTemplate(const TitleBlockTemplateLocation &);
|
||||
bool projectWasModified();
|
||||
bool projectOptionsWereModified();
|
||||
|
||||
@@ -234,6 +234,65 @@ private slots:
|
||||
QVERIFY(references > 0);
|
||||
}
|
||||
|
||||
// Deleting the folio that holds the first part of a split table, with
|
||||
// folios opened lazily (the default), then saving: the next part keeps
|
||||
// the table's query (F121). The first part holds it; the next part takes
|
||||
// it over only if it was built when the folio went, and it used to be
|
||||
// built later, from the file, naming a part that was gone and saving no
|
||||
// query. industrial.qet's folio 41 (index 40) holds the first part of its
|
||||
// 10-part parts list. It is resaved first: its folios have no saved uuid,
|
||||
// and such a project opens with every folio built. Every table in the
|
||||
// file must name a table that is in it, or have a query.
|
||||
void deletedFirstPartKeepsTableQuery()
|
||||
{
|
||||
#ifndef QET_HAS_SCRIPTING
|
||||
QSKIP("needs --run: this QElectroTech is built without Qt Qml");
|
||||
#endif
|
||||
const QString in = resave(QStringLiteral(QET_EXAMPLES_DIR "/industrial.qet"));
|
||||
QVERIFY(!in.isEmpty());
|
||||
const QString out = m_dir.filePath(QStringLiteral("deleted_first_part.qet"));
|
||||
const QString script = m_dir.filePath(QStringLiteral("deleted_first_part.js"));
|
||||
QFile js(script);
|
||||
QVERIFY(js.open(QIODevice::WriteOnly));
|
||||
js.write(QStringLiteral("qet.log('REMOVED ' + qet.removeFolio(40));\n"
|
||||
"qet.log('SAVED ' + qet.save('%1'));\n").arg(out).toUtf8());
|
||||
js.close();
|
||||
const QString home = m_dir.filePath(QStringLiteral("home%1").arg(m_run++));
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
env.insert(QStringLiteral("QET_ENABLE_SCRIPTING"), QStringLiteral("1"));
|
||||
env.remove(QStringLiteral("QET_EAGER_FOLIOS"));
|
||||
env.insert(QStringLiteral("HOME"), home);
|
||||
env.insert(QStringLiteral("XDG_CONFIG_HOME"), home + QStringLiteral("/config"));
|
||||
env.insert(QStringLiteral("XDG_DATA_HOME"), home + QStringLiteral("/data"));
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH),
|
||||
{QStringLiteral("--run"), script, in});
|
||||
QVERIFY(proc.waitForFinished(180000));
|
||||
const QString log = QString::fromUtf8(proc.readAllStandardOutput() + proc.readAllStandardError());
|
||||
QVERIFY2(log.contains(QStringLiteral("REMOVED true")) && log.contains(QStringLiteral("SAVED true")),
|
||||
qPrintable(log.right(400)));
|
||||
|
||||
const QString saved = QString::fromUtf8(read(out));
|
||||
const QRegularExpression table_re(
|
||||
QStringLiteral("<graphics_table [^>]*uuid=\"([^\"]+)\"[^>]*>(.*?)</graphics_table>"),
|
||||
QRegularExpression::DotMatchesEverythingOption);
|
||||
QHash<QString, QString> tables; // uuid -> what is inside the table
|
||||
for (const auto &m : table_re.globalMatch(saved))
|
||||
tables.insert(m.captured(1), m.captured(2));
|
||||
QVERIFY(tables.size() >= 9);
|
||||
const QRegularExpression previous_re(QStringLiteral("<previous_table uuid=\"([^\"]+)\""));
|
||||
const QRegularExpression query_re(QStringLiteral("<query>\\s*\\S"));
|
||||
for (auto it = tables.cbegin() ; it != tables.cend() ; ++it) {
|
||||
const auto previous = previous_re.match(it.value());
|
||||
QVERIFY2(query_re.match(it.value()).hasMatch()
|
||||
|| (previous.hasMatch() && tables.contains(previous.captured(1))),
|
||||
qPrintable(QStringLiteral("table %1 has no query and names no table in the file")
|
||||
.arg(it.key())));
|
||||
}
|
||||
}
|
||||
|
||||
// A folio report arrow linked to one that has no terminal -- such as
|
||||
// ref_voyant_2_h.elmt from the collection -- crashed QElectroTech on
|
||||
// opening: the arrow's text looked for a wire on the other arrow's
|
||||
|
||||
Reference in New Issue
Block a user